[
  {
    "path": ".gitignore",
    "content": ".DS_Store\nChapter01/.DS_Store\nChapter02/.DS_Store\nChapter03/.DS_Store\nChapter04/.DS_Store\nChapter06/.DS_Store\nChapter07/.DS_Store\nChapter09/.DS_Store\n图书勘误/图书勘误.docx\n图书勘误/~$图书勘误.docx\n"
  },
  {
    "path": "Chapter01/codes/TestRAII1/ReadMe.txt",
    "content": "========================================================================\r\n    CONSOLE APPLICATION : TestRAII1 Project Overview\r\n========================================================================\r\n\r\nAppWizard has created this TestRAII1 application for you.\r\n\r\nThis file contains a summary of what you will find in each of the files that\r\nmake up your TestRAII1 application.\r\n\r\n\r\nTestRAII1.vcxproj\r\n    This is the main project file for VC++ projects generated using an Application Wizard.\r\n    It contains information about the version of Visual C++ that generated the file, and\r\n    information about the platforms, configurations, and project features selected with the\r\n    Application Wizard.\r\n\r\nTestRAII1.vcxproj.filters\r\n    This is the filters file for VC++ projects generated using an Application Wizard. \r\n    It contains information about the association between the files in your project \r\n    and the filters. This association is used in the IDE to show grouping of files with\r\n    similar extensions under a specific node (for e.g. \".cpp\" files are associated with the\r\n    \"Source Files\" filter).\r\n\r\nTestRAII1.cpp\r\n    This is the main application source file.\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\nOther standard files:\r\n\r\nStdAfx.h, StdAfx.cpp\r\n    These files are used to build a precompiled header (PCH) file\r\n    named TestRAII1.pch and a precompiled types file named StdAfx.obj.\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\nOther notes:\r\n\r\nAppWizard uses \"TODO:\" comments to indicate parts of the source code you\r\nshould add to or customize.\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII1.cpp",
    "content": "// #include <winsock2.h>\r\n// #include <stdio.h>\r\n\r\n// //链接Windows的socket库\r\n// #pragma comment(lib, \"ws2_32.lib\")\r\n\r\n// int main(int argc, char* argv[])\r\n// {\r\n//     //初始化socket库\r\n//     WORD wVersionRequested = MAKEWORD(2, 2);\r\n//     WSADATA wsaData;\r\n//     int err = WSAStartup(wVersionRequested, &wsaData);\r\n//     if (err != 0)\r\n//         return 1;\r\n\r\n//     if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)\r\n//     {\r\n//         WSACleanup();\r\n//         return 1;\r\n//     }\r\n\r\n//     //创建用于监听的套接字\r\n//     SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);\r\n//     if (sockSrv == -1)\r\n//     {\r\n//         WSACleanup();\r\n//         return 1;\r\n//     }\r\n\r\n//     SOCKADDR_IN addrSrv;\r\n//     addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);\r\n//     addrSrv.sin_family = AF_INET;\r\n//     addrSrv.sin_port = htons(6000);\r\n//     //绑定套接字，监听6000端口\r\n//     if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == -1)\r\n//     {\r\n//         closesocket(sockSrv);\r\n//         WSACleanup();\r\n//         return 1;\r\n//     }\r\n\r\n//     //启动监听，准备接受客户请求\r\n//     if (listen(sockSrv, 15) == -1)\r\n//     {\r\n//         closesocket(sockSrv);\r\n//         WSACleanup();\r\n//         return 1;\r\n//     }\r\n\r\n//     SOCKADDR_IN addrClient;\r\n//     int len = sizeof(SOCKADDR);\r\n//     char msg[] = \"HelloWorld\";\r\n//     while (true)\r\n//     {\r\n//         //等待客户请求到来，如果有客户端连接，则接受连接\r\n//         SOCKET sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);\r\n//         if (sockClient == -1)\r\n//         \tbreak;\r\n        \t\r\n//         //给客户端发送”HelloWorld“消息\r\n//         send(sockClient, msg, strlen(msg), 0);\r\n//         closesocket(sockClient);\r\n//     }// end while-loop\r\n\r\n//     closesocket(sockSrv);\r\n//     WSACleanup();\r\n\r\n//     return 0;\r\n// }\r\n//\r\n// char* p = new char[1024];\r\n\r\n// if (操作1不成功)\r\n// {\r\n//     delete[] p;\r\n//     p = NULL;\r\n//     return;\r\n// }\r\n\r\n// if (操作2不成功)\r\n// {\r\n//     delete[] p;\r\n//     p = NULL;\r\n//     return;\r\n// }\r\n\r\n// if (操作3不成功)\r\n// {\r\n//     delete[] p;\r\n//     p = NULL;\r\n//     return;\r\n// }\r\n\r\n// delete[] p;\r\n// p = NULL;"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII1.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.30501.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"TestRAII1\", \"TestRAII1.vcxproj\", \"{3EC68362-A282-4B6E-A541-53C23746CAFA}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{3EC68362-A282-4B6E-A541-53C23746CAFA}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{3EC68362-A282-4B6E-A541-53C23746CAFA}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{3EC68362-A282-4B6E-A541-53C23746CAFA}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{3EC68362-A282-4B6E-A541-53C23746CAFA}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII1.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{3EC68362-A282-4B6E-A541-53C23746CAFA}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>TestRAII1</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n    <ClCompile Include=\"TestRAII1.cpp\" />\r\n    <ClCompile Include=\"TestRAII2.cpp\" />\r\n    <ClCompile Include=\"TestRAII3.cpp\" />\r\n    <ClCompile Include=\"TestRAII4.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII1.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"TestRAII1.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"TestRAII2.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"TestRAII3.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"TestRAII4.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII2.cpp",
    "content": "// #include \"stdafx.h\"\r\n// #include <winsock2.h>\r\n// #include <stdio.h>\r\n\r\n// //链接Windows的socket库\r\n// #pragma comment(lib, \"ws2_32.lib\")\r\n\r\n// int main(int argc, char* argv[])\r\n// {\r\n//     //由于goto语句不能跳过变量声明，\r\n//     //所以提前定义下文需要用到的变量\r\n//     SOCKET sockSrv;\r\n//     SOCKADDR_IN addrSrv;\r\n//     SOCKADDR_IN addrClient;\r\n//     int len = sizeof(SOCKADDR);\r\n//     char msg[] = \"HelloWorld\";\r\n    \r\n//     //初始化socket库\r\n//     WORD wVersionRequested = MAKEWORD(2, 2);\r\n//     WSADATA wsaData;\r\n//     int err = WSAStartup(wVersionRequested, &wsaData);\r\n//     if (err != 0)\r\n//         return 1;\r\n\r\n//     if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)\r\n//     {\r\n//         goto cleanup2;\r\n//         return 1;\r\n//     }\r\n\r\n//     //创建用于监听的套接字\r\n//     sockSrv = socket(AF_INET, SOCK_STREAM, 0);\r\n//     if (sockSrv == -1)\r\n//     {\r\n//         goto cleanup2;\r\n//         return 1;\r\n//     }\r\n\r\n//     addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);\r\n//     addrSrv.sin_family = AF_INET;\r\n//     addrSrv.sin_port = htons(6000);\r\n//     //绑定套接字，监听6000端口\r\n//     if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == -1)\r\n//     {\r\n//         goto cleanup1;\r\n//         return 1;\r\n//     }\r\n\r\n//     //启动监听，准备接受客户请求\r\n//     if (listen(sockSrv, 15) == -1)\r\n//     {\r\n//         goto cleanup1;\r\n//         return 1;\r\n//     }\r\n\r\n//     while (true)\r\n//     {\r\n//         //等待客户请求到来，如果有客户端连接，则接受连接\r\n//         SOCKET sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);\r\n//         if (sockClient == -1)\r\n//             break;\r\n\r\n//         //给客户端发送”HelloWorld“消息\r\n//         send(sockClient, msg, strlen(msg), 0);\r\n//         closesocket(sockClient);\r\n//     }// end while-loop\r\n\r\n// cleanup1:\r\n//     closesocket(sockSrv);\r\n\r\n// cleanup2:\r\n//     WSACleanup();\r\n\r\n//     return 0;\r\n// }\r\n\r\n//\r\n////char* p = new char[1024];\r\n////\r\n////if (²Ù×÷1²»³É¹¦)\r\n////{\r\n////    delete[] p;\r\n////    p = NULL;\r\n////    return;\r\n////}\r\n////\r\n////if (²Ù×÷2²»³É¹¦)\r\n////{\r\n////    delete[] p;\r\n////    p = NULL;\r\n////    return;\r\n////}\r\n////\r\n////if (²Ù×÷3²»³É¹¦)\r\n////{\r\n////    delete[] p;\r\n////    p = NULL;\r\n////    return;\r\n////}\r\n////\r\n////delete[] p;\r\n////p = NULL;"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII3.cpp",
    "content": "#include \"stdafx.h\"\r\n//#include <winsock2.h>\r\n//#include <stdio.h>\r\n//\r\n////Windowssocket\r\n//#pragma comment(lib, \"ws2_32.lib\")\r\n//\r\n//int main(int argc, char* argv[])\r\n//{\r\n//    //׽ֿ\r\n//    WORD wVersionRequested = MAKEWORD(2, 2);\r\n//    WSADATA wsaData;\r\n//    int err = WSAStartup(wVersionRequested, &wsaData);\r\n//    if (err != 0)\r\n//        return 1;\r\n//\r\n//    SOCKET sockSrv = -1;\r\n//    do \r\n//    {\r\n//        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)\r\n//            break;\r\n//\r\n//        //ڼ׽        \r\n//        sockSrv = socket(AF_INET, SOCK_STREAM, 0);\r\n//        if (sockSrv == -1)\r\n//            break;\r\n//\r\n//        SOCKADDR_IN addrSrv;\r\n//        addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);\r\n//        addrSrv.sin_family = AF_INET;\r\n//        addrSrv.sin_port = htons(6000);\r\n//        //׽֣6000˿ϼ\r\n//        if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == -1)\r\n//            break;\r\n//\r\n//        //׽Ϊģʽ׼ܿͻ\r\n//        if (listen(sockSrv, 15) == -1)\r\n//            break;\r\n//\r\n//        SOCKADDR_IN addrClient;\r\n//        int len = sizeof(SOCKADDR);\r\n//        char msg[] = \"HelloWorld\";\r\n//        while (true)\r\n//        {\r\n//            //ȴͻпͻӣ\r\n//            SOCKET sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);\r\n//            //ͻ˷HelloWorld\r\n//            send(sockClient, msg, strlen(msg), 0);\r\n//            closesocket(sockClient);\r\n//        }// end inner-while-loop\r\n//    } while (0); //end outer-while-loop\r\n//    \r\n//\r\n//    if (sockSrv != -1)\r\n//        closesocket(sockSrv);\r\n//    \r\n//    WSACleanup();\r\n//\r\n//    return 0;\r\n//}\r\n//\r\n////char* p = new char[1024];\r\n////\r\n////if (1ɹ)\r\n////{\r\n////    delete[] p;\r\n////    p = NULL;\r\n////    return;\r\n////}\r\n////\r\n////if (2ɹ)\r\n////{\r\n////    delete[] p;\r\n////    p = NULL;\r\n////    return;\r\n////}\r\n////\r\n////if (3ɹ)\r\n////{\r\n////    delete[] p;\r\n////    p = NULL;\r\n////    return;\r\n////}\r\n////\r\n////delete[] p;\r\n////p = NULL;\r\n//\r\n//\r\n//#include \"stdafx.h\"\r\n//#include <winsock2.h>\r\n//#include <stdio.h>\r\n//\r\n////Windowssocket\r\n//#pragma comment(lib, \"ws2_32.lib\")\r\n//\r\n//int main(int argc, char* argv[])\r\n//{\r\n//    //׽ֿ\r\n//    WORD wVersionRequested = MAKEWORD(1, 1);\r\n//    WSADATA wsaData;\r\n//    int err = WSAStartup(wVersionRequested, &wsaData);\r\n//    if (err != 0)\r\n//        return 1;\r\n//\r\n//    SOCKET sockSrv = -1;\r\n//    do\r\n//    {\r\n//        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)\r\n//            break;\r\n//\r\n//        //ڼ׽        \r\n//        sockSrv = socket(AF_INET, SOCK_STREAM, 0);\r\n//        if (sockSrv == -1)\r\n//            break;\r\n//\r\n//        SOCKADDR_IN addrSrv;\r\n//        addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);\r\n//        addrSrv.sin_family = AF_INET;\r\n//        addrSrv.sin_port = htons(6000);\r\n//        //׽֣6000˿ϼ\r\n//        if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == -1)\r\n//            break;\r\n//\r\n//        //׽Ϊģʽ׼ܿͻ\r\n//        if (listen(sockSrv, 15) == -1)\r\n//            break;\r\n//\r\n//        SOCKADDR_IN addrClient;\r\n//        int len = sizeof(SOCKADDR);\r\n//        char msg[] = \"HelloWorld\";\r\n//        while (true)\r\n//        {\r\n//            //ȴͻпͻӣ\r\n//            SOCKET sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);\r\n//            //ͻ˷HelloWorld\r\n//            send(sockClient, msg, strlen(msg), 0);\r\n//            closesocket(sockClient);\r\n//        }// end inner-while-loop\r\n//    } while (0); //end outer-while-loop\r\n//\r\n//\r\n//    if (sockSrv != -1)\r\n//        closesocket(sockSrv);\r\n//\r\n//    WSACleanup();\r\n//\r\n//    return 0;\r\n//}\r\n//\r\n//char* p = NULL;\r\n//do \r\n//{\r\n//   p = new char[1024];\r\n//   if (1ɹ)\r\n//       break;\r\n//\r\n//   if (2ɹ)\r\n//       break;\r\n//\r\n//   if (3ɹ)\r\n//       break;\r\n//} while (0);\r\n//\r\n//delete[] p;\r\n//p = NULL;"
  },
  {
    "path": "Chapter01/codes/TestRAII1/TestRAII4.cpp",
    "content": "#include <winsock2.h>\r\n#include <stdio.h>\r\n\r\n//链接Windows的socket库\r\n#pragma comment(lib, \"ws2_32.lib\")\r\n\r\nclass ServerSocket\r\n{\r\npublic:\r\n    ServerSocket()\r\n    {\r\n        m_bInit = false;\r\n        m_ListenSocket = -1;\r\n    }\r\n\r\n    ~ServerSocket()\r\n    {\r\n        if (m_ListenSocket != -1)\r\n            ::closesocket(m_ListenSocket);\r\n\r\n        if (m_bInit)\r\n            ::WSACleanup();\r\n    }\r\n\r\n    bool DoInit()\r\n    {\r\n        //初始化socket库\r\n        WORD wVersionRequested = MAKEWORD(2, 2);\r\n        WSADATA wsaData;\r\n        int err = WSAStartup(wVersionRequested, &wsaData);\r\n        if (err != 0)\r\n            return false;\r\n\r\n        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)\r\n            return false;\r\n            \r\n        m_bInit = true;\r\n\r\n        //创建用于监听的套接字\r\n        m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0);\r\n        if (m_ListenSocket == -1)\r\n            return false;\r\n\r\n        return true;\r\n    }\r\n\r\n    bool DoBind(const char* ip, short port = 6000)\r\n    {\r\n        SOCKADDR_IN addrSrv;\r\n        addrSrv.sin_addr.S_un.S_addr = inet_addr(ip);\r\n        addrSrv.sin_family = AF_INET;\r\n        addrSrv.sin_port = htons(port);\r\n        if (::bind(m_ListenSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == -1)\r\n            return false;\r\n\r\n        return true;\r\n    }\r\n\r\n    bool DoListen(int backlog = 15)\r\n    {\r\n        if (listen(m_ListenSocket, backlog) == -1)\r\n            return false;\r\n\r\n        return true;\r\n    }\r\n\r\n    bool DoAccept()\r\n    {\r\n        SOCKADDR_IN addrClient;\r\n        int len = sizeof(SOCKADDR);\r\n        char msg[] = \"HelloWorld\";\r\n        while (true)\r\n        {\r\n            //等待客户请求到来，如果有客户端连接，则接受连接\r\n            SOCKET sockClient = accept(m_ListenSocket, (SOCKADDR*)&addrClient, &len);\r\n            if (sockClient == -1)\r\n                break;\r\n\r\n            //给客户端发送”HelloWorld“消息\r\n            send(sockClient, msg, strlen(msg), 0);\r\n            closesocket(sockClient);\r\n        }// end inner-while-loop\r\n\r\n        return false;\r\n    }\r\n\r\nprivate:\r\n    bool    m_bInit;\r\n    SOCKET  m_ListenSocket;\r\n};\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    ServerSocket serverSocket;\r\n    if (!serverSocket.DoInit())\r\n        return false;\r\n\r\n    if (!serverSocket.DoBind(\"0.0.0.0\", 6000))\r\n        return false;\r\n\r\n    if (!serverSocket.DoListen(15))\r\n        return false;\r\n\r\n    if (!serverSocket.DoAccept())\r\n        return false;\r\n\r\n    return 0;\r\n}\r\n\r\n\r\n// void SomeFunction()\r\n// {\r\n//     得到某把锁；\r\n//     if (条件1)\r\n//     {\r\n//         if (条件2)\r\n//         {\r\n//             某些操作1\r\n//             释放锁;\r\n//             return;\r\n//         }\r\n//         else (条件3)\r\n//         {\r\n//             某些操作2\r\n//             释放锁;\r\n//             return;\r\n//         }\r\n//     }\r\n\r\n//     if (条件3)\r\n//     {\r\n//         某些操作3\r\n//         释放锁;\r\n//         return;\r\n//     }\r\n\r\n//     某些操作4\r\n//     释放锁;\r\n// }\r\n\r\n// class SomeLockGuard\r\n// {\r\n// public:\r\n//     SomeLockGuard()\r\n//     {\r\n//         //加锁\r\n//         m_lock.lock();\r\n//     }\r\n\r\n//     ~SomeLockGuard()\r\n//     {\r\n//         //解锁\r\n//         m_lock.unlock();\r\n//     }\r\n\r\n// private:\r\n//     SomeLock  m_lock;\r\n// };\r\n\r\n// void SomeFunction()\r\n// {\r\n//     SomeLockGuard lockWrapper;\r\n//     if (条件1)\r\n//     {\r\n//         if (条件2)\r\n//         {\r\n//             某些操作1\r\n//             return;\r\n//         }\r\n//         else (条件3)\r\n//         {\r\n//             某些操作2\r\n//             return;\r\n//         }\r\n//     }\r\n\r\n//     if (条件3)\r\n//     {\r\n//         某些操作3\r\n//         return;\r\n//     }\r\n\r\n//     某些操作4\r\n// }"
  },
  {
    "path": "Chapter01/codes/TestRAII1/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// TestRAII1.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter01/codes/TestRAII1/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#include <stdio.h>\r\n#include <tchar.h>\r\n\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter01/codes/TestRAII1/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "Chapter01/codes/construct_complex_objects.cpp",
    "content": "﻿// construct_complex_objects.cpp : 此文件包含 \"main\" 函数。程序执行将在此处开始并结束。\r\n//\r\n\r\n#include <iostream>\r\n#include <string>\r\n#include <initializer_list>\r\n#include <vector>\r\n#include <sstream>\r\n\r\n//简单地模拟 json 支持的几种数据类型\r\nenum class jsonType\r\n{\r\n    jsonTypeNull,\r\n    jsonTypeInt,\r\n    jsonTypeLong,\r\n    jsonTypeDouble,\r\n    jsonTypeBool,\r\n    jsonTypeString,\r\n    jsonTypeArray,\r\n    jsonTypeObject\r\n};\r\n\r\nstruct jsonNode\r\n{\r\n    jsonNode(const char* key, const char* value) : \r\n        m_type(jsonType::jsonTypeString),\r\n        m_key(key),\r\n        m_value(value)\r\n    { \r\n        std::cout << \"jsonNode contructor1 called.\" << std::endl;\r\n    }\r\n\r\n    jsonNode(const char* key, double value) :\r\n        m_type(jsonType::jsonTypeString),\r\n        m_key(key),\r\n        m_value(std::to_string(value))\r\n    {\r\n        std::cout << \"jsonNode contructor2 called.\" << std::endl;\r\n    }\r\n\r\n    jsonType    m_type;\r\n    std::string m_key;\r\n    std::string m_value;\r\n};\r\n\r\nclass json\r\n{\r\npublic:\r\n    static json& array(std::initializer_list<jsonNode> nodes)\r\n    {            \r\n        m_json.m_nodes.clear();\r\n        m_json.m_nodes.insert(m_json.m_nodes.end(), nodes.begin(), nodes.end());\r\n\r\n        std::cout << \"json::array() called.\" << std::endl;\r\n\r\n        return m_json;\r\n    }\r\n\r\n    json()\r\n    {\r\n\r\n    }\r\n\r\n    ~json()\r\n    {\r\n\r\n    }\r\n\r\n    std::string toString()\r\n    {\r\n        std::ostringstream os;\r\n        size_t size = m_nodes.size();\r\n        for (size_t i = 0; i < size; ++i)\r\n        {\r\n            switch (m_nodes[i].m_type)\r\n            {\r\n            //根据类型，组装成一个json字符串，代码省略...\r\n            case jsonType::jsonTypeDouble:\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\nprivate:\r\n    std::vector<jsonNode> m_nodes;\r\n\r\n    static json           m_json;\r\n};\r\n\r\njson json::m_json;\r\n\r\nint main()\r\n{\r\n    json array_not_object = json::array({ {\"currency\", \"USD\"}, {\"value\", 42.99} });\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_auto_ptr.cpp",
    "content": "// test_auto_ptr.cpp : This file contains the 'main' function. Program execution begins and ends there.\r\n//\r\n\r\n#include <iostream>\r\n#include <memory>\r\n\r\nint main()\r\n{\r\n    //Կ\r\n    std::auto_ptr<int> sp1(new int(8));\r\n    std::auto_ptr<int> sp2(sp1);\r\n    if (sp1.get() != NULL)\r\n    {\r\n        std::cout << \"sp1 is not empty.\" << std::endl;\r\n    }\r\n    else\r\n    {\r\n        std::cout << \"sp1 is empty.\" << std::endl;\r\n    }\r\n\r\n    if (sp2.get() != NULL)\r\n    {\r\n        std::cout << \"sp2 is not empty.\" << std::endl;\r\n    }\r\n    else\r\n    {\r\n        std::cout << \"sp2 is empty.\" << std::endl;\r\n    }\r\n\r\n    // operator= \r\n    std::auto_ptr<int> sp3(new int(8));\r\n    std::auto_ptr<int> sp4;\r\n    sp4 = sp3;\r\n    if (sp3.get() != NULL)\r\n    {\r\n        std::cout << \"sp3 is not empty.\" << std::endl;\r\n    }\r\n    else\r\n    {\r\n        std::cout << \"sp3 is empty.\" << std::endl;\r\n    }\r\n\r\n    if (sp4.get() != NULL)\r\n    {\r\n        std::cout << \"sp4 is not empty.\" << std::endl;\r\n    }\r\n    else\r\n    {\r\n        std::cout << \"sp4 is empty.\" << std::endl;\r\n    }\r\n\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter01/codes/test_custom_foreach_loop.cpp",
    "content": "#include <iostream>\r\n#include <string>\r\n\r\ntemplate<typename T, size_t N>\r\nclass A\r\n{\r\npublic:\r\n    A()\r\n    {\r\n        for (size_t i = 0; i < N; ++i)\r\n        {\r\n            m_elements[i] = i;\r\n        }\r\n    }\r\n\r\n    ~A()\r\n    {\r\n\r\n    }\r\n\r\n    T* begin()\r\n    {\r\n        return m_elements + 0;\r\n    }\r\n\r\n    T* end()\r\n    {\r\n        return m_elements + N;\r\n    }\r\n\r\nprivate:\r\n    T       m_elements[N];\r\n};\r\n\r\nint main()\r\n{\r\n    A<int, 10> a;\r\n    for (auto iter : a)\r\n    {\r\n        std::cout << iter << std::endl;\r\n    }\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_initializer_list.cpp",
    "content": "#include <iostream>\r\n#include <initializer_list>\r\n#include <vector>\r\n\r\nclass A\r\n{\r\npublic:\r\n    A(std::initializer_list<int> integers)\r\n    {\r\n        m_vecIntegers.insert(m_vecIntegers.end(), integers.begin(), integers.end());\r\n    }\r\n\r\n    ~A()\r\n    {\r\n\r\n    }\r\n\r\n    void append(std::initializer_list<int> integers)\r\n    {\r\n        m_vecIntegers.insert(m_vecIntegers.end(), integers.begin(), integers.end());\r\n    }\r\n\r\n    void print()\r\n    {\r\n        size_t size = m_vecIntegers.size();\r\n        for (size_t i = 0; i < size; ++i)\r\n        {\r\n            std::cout << m_vecIntegers[i] << std::endl;\r\n        }\r\n    }\r\n\r\nprivate:\r\n    std::vector<int> m_vecIntegers;\r\n};\r\n\r\nint main()\r\n{\r\n    A a{ 1, 2, 3 };\r\n    a.print();\r\n\r\n    std::cout << \"After appending...\" << std::endl;\r\n\r\n    a.append({ 4, 5, 6 });\r\n    a.print();\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_map_insert_or_assign.cpp",
    "content": "/** \r\n * std::map::try_emplace÷ʾ\r\n * zhangyl 2019.10.06\r\n */\r\n\r\n#include <iostream>\r\n#include <map>\r\n\r\nclass ChatDialog\r\n{\r\n//ʵʡ...\r\npublic:\r\n    ChatDialog(int64_t userid) : m_userid(userid)\r\n    {\r\n        std::cout << \"ChatDialog constructor\" << std::endl;\r\n    }\r\n\r\n    ~ChatDialog()\r\n    {\r\n        std::cout << \"ChatDialog destructor\" << std::endl;\r\n    }\r\n\r\n    void activate()\r\n    {\r\n        //ʵʡ\r\n    }\r\n\r\nprivate:\r\n    int64_t     m_userid;\r\n};\r\n\r\n//ڹԻmapkeyǺidvalueChatDialogԻָ\r\nstd::map<int64_t, ChatDialog>   m_ChatDialogs;\r\n\r\n//C++ 17汾3\r\nvoid onDoubleClickFriendItem3(int64_t userid)\r\n{   \r\n    //ṹ󶨺try_emplace C++17﷨    \r\n    auto [iter, inserted] = m_ChatDialogs.try_emplace(userid, userid);   \r\n    iter->second.activate();\r\n}\r\n\r\n//int main()\r\n//{\r\n//    //\r\n//    //906106643 userid\r\n//    onDoubleClickFriendItem3(906106643L);\r\n//    //906106644 userid\r\n//    onDoubleClickFriendItem3(906106644L);\r\n//    //906106643 userid\r\n//    onDoubleClickFriendItem3(906106643L);\r\n//\r\n//    return 0;\r\n//}\r\n\r\nint main()\r\n{\r\n    std::map<std::string, int> mapUsersAge{ { \"Alex\", 45 }, { \"John\", 25 } };\r\n    mapUsersAge.insert_or_assign(\"Tom\", 26);\r\n    mapUsersAge.insert_or_assign(\"Alex\", 27);\r\n\r\n    for (const auto& [userName, userAge] : mapUsersAge)\r\n    {\r\n        std::cout << \"userName: \" << userName << \", userAge: \" << userAge << std::endl;\r\n    }\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_map_try_emplace.cpp",
    "content": "// test_map_try_emplace.cpp : This file contains the 'main' function. Program execution begins and ends there.\r\n//\r\n\r\n#include <iostream>\r\n#include <map>\r\n\r\nclass ChatDialog\r\n{\r\n//ʵʡ...\r\npublic:\r\n    void activate()\r\n    {\r\n        //ʵʡ\r\n    }\r\n};\r\n\r\n//ڹԻmapkeyǺidChatDialogԻָ\r\nstd::map<int64_t, ChatDialog*> m_ChatDialogs;\r\n\r\n//ͨ汾\r\nvoid onDoubleClickFriendItem(int64_t userid)\r\n{\r\n    auto targetChatDialog = m_ChatDialogs.find(userid);\r\n    //ѶԻ򲻴ڣ򴴽֮\r\n    if (targetChatDialog == m_ChatDialogs.end())\r\n    {\r\n        ChatDialog* pChatDialog = new ChatDialog();\r\n        m_ChatDialogs.insert(std::pair<int64_t, ChatDialog*>(userid, pChatDialog));\r\n        pChatDialog->activate();\r\n    }\r\n    //ѶԻڣֱӼ\r\n    else\r\n    {\r\n        targetChatDialog->second->activate();\r\n    }\r\n}\r\n\r\n//C++ 17汾1\r\nvoid onDoubleClickFriendItem2(int64_t userid)\r\n{   \r\n    //ṹ󶨺try_emplace C++17﷨\r\n    auto [iter, inserted] = m_ChatDialogs.try_emplace(userid);\r\n    if (inserted)\r\n        iter->second = new ChatDialog();   \r\n\r\n    iter->second->activate();\r\n}\r\n\r\nint main()\r\n{\r\n    //\r\n    //906106643 userid\r\n    onDoubleClickFriendItem2(906106643L);\r\n    //906106644 userid\r\n    onDoubleClickFriendItem2(906106644L);\r\n    //906106643 userid\r\n    onDoubleClickFriendItem2(906106643L);\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_map_try_emplace_with_directobject.cpp",
    "content": "/** \r\n * std::map::try_emplace÷ʾ\r\n * zhangyl 2019.10.06\r\n */\r\n\r\n#include <iostream>\r\n#include <map>\r\n\r\nclass ChatDialog\r\n{\r\n//ʵʡ...\r\npublic:\r\n    ChatDialog(int64_t userid) : m_userid(userid)\r\n    {\r\n        std::cout << \"ChatDialog constructor\" << std::endl;\r\n    }\r\n\r\n    ~ChatDialog()\r\n    {\r\n        std::cout << \"ChatDialog destructor\" << std::endl;\r\n    }\r\n\r\n    void activate()\r\n    {\r\n        //ʵʡ\r\n    }\r\n\r\nprivate:\r\n    int64_t     m_userid;\r\n};\r\n\r\n//ڹԻmapkeyǺidvalueChatDialogԻָ\r\nstd::map<int64_t, ChatDialog>   m_ChatDialogs;\r\n\r\n//C++ 17汾3\r\nvoid onDoubleClickFriendItem3(int64_t userid)\r\n{   \r\n    //ṹ󶨺try_emplace C++17﷨    \r\n    auto [iter, inserted] = m_ChatDialogs.try_emplace(userid, userid);   \r\n    iter->second.activate();\r\n}\r\n\r\nint main()\r\n{\r\n    //\r\n    //906106643 userid\r\n    onDoubleClickFriendItem3(906106643L);\r\n    //906106644 userid\r\n    onDoubleClickFriendItem3(906106644L);\r\n    //906106643 userid\r\n    onDoubleClickFriendItem3(906106643L);\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_map_try_emplace_with_smartpointer.cpp",
    "content": "/** \r\n * std::map::try_emplace÷ʾ\r\n * zhangyl 2019.10.06\r\n */\r\n\r\n#include <iostream>\r\n#include <map>\r\n#include <memory>\r\n\r\nclass ChatDialog\r\n{\r\n//ʵʡ...\r\npublic:\r\n    ChatDialog()\r\n    {\r\n        std::cout << \"ChatDialog constructor\" << std::endl;\r\n    }\r\n\r\n    ~ChatDialog()\r\n    {\r\n        std::cout << \"ChatDialog destructor\" << std::endl;\r\n    }\r\n\r\n    void activate()\r\n    {\r\n        //ʵʡ\r\n    }\r\n};\r\n\r\n//ڹԻmapkeyǺidvalueChatDialogԻָ\r\nstd::map<int64_t, std::unique_ptr<ChatDialog>> m_ChatDialogs;\r\n\r\n//C++ 17汾2\r\nvoid onDoubleClickFriendItem3(int64_t userid)\r\n{   \r\n    //ṹ󶨺try_emplace C++17﷨\r\n    auto spChatDialog = std::make_unique<ChatDialog>();\r\n    auto [iter, inserted] = m_ChatDialogs.try_emplace(userid, std::move(spChatDialog));\r\n    iter->second->activate();\r\n}\r\n\r\nint main()\r\n{\r\n    //\r\n    //906106643 userid\r\n    onDoubleClickFriendItem3(906106643L);\r\n    //906106644 userid\r\n    onDoubleClickFriendItem3(906106644L);\r\n    //906106643 userid\r\n    onDoubleClickFriendItem3(906106643L);\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_map_try_emplace_with_smartpointer2.cpp",
    "content": "/** \r\n * std::map::try_emplace÷ʾ\r\n * zhangyl 2019.10.06\r\n */\r\n\r\n#include <iostream>\r\n#include <map>\r\n#include <memory>\r\n\r\nclass ChatDialog\r\n{\r\n//ʵʡ...\r\npublic:\r\n    ChatDialog()\r\n    {\r\n        std::cout << \"ChatDialog constructor\" << std::endl;\r\n    }\r\n\r\n    ~ChatDialog()\r\n    {\r\n        std::cout << \"ChatDialog destructor\" << std::endl;\r\n    }\r\n\r\n    void activate()\r\n    {\r\n        //ʵʡ\r\n    }\r\n};\r\n\r\n//ڹԻmapkeyǺidvalueChatDialogԻָ\r\nstd::map<int64_t, std::unique_ptr<ChatDialog>> m_ChatDialogs;\r\n\r\n//C++ 17汾3\r\nvoid onDoubleClickFriendItem3(int64_t userid)\r\n{   \r\n    //ṹ󶨺try_emplace C++17﷨    \r\n    auto [iter, inserted] = m_ChatDialogs.try_emplace(userid, nullptr);\r\n    if (inserted)\r\n    {\r\n        //Ͱ贴\r\n        auto spChatDialog = std::make_unique<ChatDialog>();\r\n        iter->second = std::move(spChatDialog);\r\n    }\r\n\r\n    iter->second->activate();\r\n}\r\n\r\nint main()\r\n{\r\n    //\r\n    //906106643 userid\r\n    onDoubleClickFriendItem3(906106643L);\r\n    //906106644 userid\r\n    onDoubleClickFriendItem3(906106644L);\r\n    //906106643 userid\r\n    onDoubleClickFriendItem3(906106643L);\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_shared_ptr_use_count.cpp",
    "content": "// test_shared_ptr.cpp : This file contains the 'main' function. Program execution begins and ends there.\r\n//\r\n\r\n#include <iostream>\r\n#include <memory>\r\n\r\nclass A\r\n{\r\npublic:\r\n    A()\r\n    {\r\n        std::cout << \"A constructor\" << std::endl;\r\n    }\r\n\r\n    ~A()\r\n    {\r\n        std::cout << \"A destructor\" << std::endl;\r\n    }\r\n};\r\n\r\nint main()\r\n{\r\n    {\r\n        //ʼʽ1\r\n        std::shared_ptr<A> sp1(new A());\r\n\r\n        std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n        //ʼʽ2\r\n        std::shared_ptr<A> sp2(sp1);\r\n        std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n        sp2.reset();\r\n        std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n        {\r\n            std::shared_ptr<A> sp3 = sp1;\r\n            std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n        }\r\n\r\n        std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n    }\r\n       \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter01/codes/test_std_enable_shared_from_this.cpp",
    "content": "// test_std_enable_shared_from_this.cpp\r\n#include <iostream>\r\n#include <memory>\r\n\r\nclass A : public std::enable_shared_from_this<A>\r\n{\r\npublic:\r\n    A()\r\n    {\r\n        std::cout << \"A constructor\" << std::endl;\r\n    }\r\n\r\n    ~A()\r\n    {\r\n        std::cout << \"A destructor\" << std::endl;\r\n    }\r\n\r\n    std::shared_ptr<A> getSelf()\r\n    {\r\n        return shared_from_this();\r\n    }\r\n};\r\n\r\n\r\nint main()\r\n{\r\n    std::shared_ptr<A> sp1(new A());\r\n\r\n    std::shared_ptr<A> sp2 = sp1->getSelf();\r\n\r\n    std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter01/codes/test_std_enable_shared_from_this_problem.cpp",
    "content": "// test_std_enable_shared_from_this.cpp : This file contains the 'main' function. Program execution begins and ends there.\r\n//\r\n#include <iostream>\r\n#include <memory>\r\n\r\nclass A : public std::enable_shared_from_this<A>\n{\npublic:\n    A()\n    {\n        m_i = 9;\n        //ע:\n        //ȽϺõڹ캯shared_from_this()m_SelfPtrֵ\n        //Ǻźô,дڹ캯ֱӱ\n\n        std::cout << \"A constructor\" << std::endl;\n    }\n\n    ~A()\n    {\n        m_i = 0;\n\n        std::cout << \"A destructor\" << std::endl;\n    }\n\n    void func()\n    {\n        m_SelfPtr = shared_from_this();\n    }\n\npublic:\n    int                 m_i;\n    std::shared_ptr<A>  m_SelfPtr;\n\n};\n\nint main()\n{\n    {\n        std::shared_ptr<A> spa(new A());\n        spa->func();\n    }\n\n    return 0;\r\n}"
  },
  {
    "path": "Chapter01/codes/test_unique_ptr.cpp",
    "content": "// test_unique_ptr.cpp : This file contains the 'main' function. Program execution begins and ends there.\r\n//\r\n\r\n#include <iostream>\r\n#include <memory>\r\n\r\nint main()\r\n{\r\n    //10int͵ĶѶ\r\n    //ʽ1\r\n    std::unique_ptr<int[]> sp1(new int[10]);\r\n    \r\n    //ʽ2\r\n    std::unique_ptr<int[]> sp2;\r\n    sp2.reset(new int[10]);\r\n    //ʽ3\r\n    std::unique_ptr<int[]> sp3(std::make_unique<int[]>(10));\r\n\r\n    for (int i = 0; i < 10; ++i)\r\n    {\r\n        sp1[i] = i;\r\n        sp2[i] = i;\r\n        sp3[i] = i;\r\n    }\r\n\r\n    for (int i = 0; i < 10; ++i)\r\n    {\r\n        std::cout << sp1[i] << \", \" << sp2[i] << \", \" << sp3[i] << std::endl;\r\n    }\r\n\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter01/codes/test_unique_ptr_deletor.cpp",
    "content": "#include <iostream>\r\n#include <memory>\r\n\r\nclass Socket\r\n{\r\npublic:\r\n    Socket()\r\n    {\r\n\r\n    }\r\n\r\n    ~Socket()\r\n    {\r\n\r\n    }\r\n\r\n    //رԴ\r\n    void close()\r\n    {\r\n\r\n    }\r\n};\r\n\r\nint main()\r\n{\r\n    auto deletor = [](Socket* pSocket) {\r\n        //رվ\r\n        pSocket->close();\r\n        //TODO: ӡһ־...\r\n        delete pSocket;\r\n    };\r\n\r\n    //std::unique_ptr<Socket, void(*)(Socket * pSocket)> spSocket(new Socket(), deletor);\r\n    std::unique_ptr<Socket, decltype(deletor)> spSocket(new Socket(), deletor);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter01/codes/test_unique_ptr_with_array.cpp",
    "content": "#include <iostream>\n#include <memory>\n\nint main()\n{\n    //创建10个int类型的堆对象\n    //形式1\n    std::unique_ptr<int[]> sp1(new int[10]);\n    \n    //形式2\n    std::unique_ptr<int[]> sp2;\n    sp2.reset(new int[10]);\n    //形式3\n    std::unique_ptr<int[]> sp3(std::make_unique<int[]>(10));\n\n    for (int i = 0; i < 10; ++i)\n    {\n        sp1[i] = i;\n        sp2[i] = i;\n        sp3[i] = i;\n    }\n\n    for (int i = 0; i < 10; ++i)\n    {\n        std::cout << sp1[i] << \", \" << sp2[i] << \", \" << sp3[i] << std::endl;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter01/codes/test_weak_ptr.cpp",
    "content": "// test_weak_ptr.cpp : This file contains the 'main' function. Program execution begins and ends there.\r\n//\r\n\r\n#include <iostream>\r\n#include <memory>\r\n\r\nint main()\r\n{\r\n    //һstd::shared_ptr\r\n    std::shared_ptr<int> sp1(new int(123));\r\n    std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n    //ͨ캯õһstd::weak_ptr\r\n    std::weak_ptr<int> sp2(sp1);\r\n    std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n    //ֵͨõһstd::weak_ptr\r\n    std::weak_ptr<int> sp3 = sp1;\r\n    std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n    //ͨһstd::weak_ptrõһstd::weak_ptr\r\n    std::weak_ptr<int> sp4 = sp2;\r\n    std::cout << \"use count: \" << sp1.use_count() << std::endl;\r\n\r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter02/VisualGDB 下载与安装.md",
    "content": "# VisualGDB 下载与安装\n\n## VisualGDB 官网\n\n```\nhttps://visualgdb.com/\n```\n\n\n\n## 网络上流传的破解版\n\n> 链接: https://pan.baidu.com/s/1RlefT8_bXIpqlKrWTNP4bg  密码: ojto\n\n"
  },
  {
    "path": "Chapter02/cgdb下载与安装.md",
    "content": "# cgdb 下载与安装\n\n**下载地址**\n\n```\nhttp://cgdb.github.io/\n```\n\n\n\n下载完 cgdb 之后，进入 cgdb 目录，执行：\n\n```\n$ ./configure --prefix=/usr/local\n$ make\n$ sudo make install\n```\n\n\n\n**出现错误：**\n\n```\nconfigure: error: CGDB requires curses.h or ncurses/curses.h to build.\n```\n\n解决方案：\n\n```\nyum install ncurses-devel\n```\n\n\n\n**出现错误：**\n\n```\nconfigure: error: Please install makeinfo before installing\n```\n\n解决方案：\n\n```\nyum install texinfo\n```\n\n如果是 CentOS 8.0 及以上系统，需要先执行：\n\n```\nyum config-manager --set-enabled PowerTools \n```\n\n\n\n**出现错误：**\n\n```\nconfigure: error: Please install help2man\n```\n\n解决方案：\n\n```\nyum install help2man\n```\n\n\n\n**出现错误：**\n\n```\nconfigure: error: CGDB requires GNU readline 5.1 or greater to link.\n  If you used --with-readline instead of using the system readline library,\n  make sure to set the correct readline library on the linker search path\n  via LD_LIBRARY_PATH or some other facility.\n```\n\n解决方案：\n\n```\nyum install readline-devel\n```\n\n\n\n**出现错误：**\n\n```\nconfigure: error: Please install flex before installing\n```\n\n解决方案：\n\n```\nyum install flex\n```\n\n"
  },
  {
    "path": "Chapter02/codes/main.cpp",
    "content": "#include <stdio.h>\n#include <pthread.h>\n#include <unistd.h>\n\nlong g = 0;\n\nvoid* worker_thread_1(void* p)\n{\n\twhile (true)\n\t{\n\t\tg = 100;\n\t\tprintf(\"worker_thread_1\\n\");\n\t\tusleep(300000);\n\t}\n\n\treturn NULL;\n}\n\nvoid* worker_thread_2(void* p)\n{\n\twhile (true)\n\t{\n\t\tg = -100;\n\t\tprintf(\"worker_thread_2\\n\");\n\t\tusleep(500000);\n\n\t}\n\n\treturn NULL;\n}\n\nint main()\n{\n\tpthread_t thread_id_1;\n\tpthread_create(&thread_id_1, NULL, worker_thread_1, NULL); \n\tpthread_t thread_id_2;\n\tpthread_create(&thread_id_2, NULL, worker_thread_2, NULL);  \n\n\twhile (true)\n\t{\n\t\tg = -1;\n\t\tprintf(\"g=%d\\n\", g);\n\t\tg = -2;\n\t\tprintf(\"g=%d\\n\", g);\n\t\tg = -3;\n\t\tprintf(\"g=%d\\n\", g);\n\t\tg = -4;\n\t\tprintf(\"g=%d\\n\", g);\n\n\t\tusleep(1000000);\n\t}\n\n\treturn 0;\n}"
  },
  {
    "path": "Chapter02/redis-6.0.3/.github/workflows/ci.yml",
    "content": "name: CI\n\non: [push, pull_request]\n\njobs:\n  test-ubuntu-latest:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5\n        ./runtest --verbose\n    - name: module api test\n      run: ./runtest-moduleapi --verbose\n\n  build-ubuntu-old:\n    runs-on: ubuntu-16.04\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n\n  build-macos-latest:\n    runs-on: macos-latest\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/.github/workflows/daily.yml",
    "content": "name: Daily\n\non:\n  schedule:\n    - cron: '0 7 * * *'\n\njobs:\n  test-jemalloc:\n    runs-on: ubuntu-latest\n    timeout-minutes: 1200\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5\n        ./runtest --accurate --verbose\n    - name: module api test\n      run: ./runtest-moduleapi --verbose\n\n  test-libc-malloc:\n    runs-on: ubuntu-latest\n    timeout-minutes: 1200\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make MALLOC=libc\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5\n        ./runtest --accurate --verbose\n    - name: module api test\n      run: ./runtest-moduleapi --verbose\n\n  test-valgrind:\n    runs-on: ubuntu-latest\n    timeout-minutes: 14400\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make valgrind\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5 valgrind -y\n        ./runtest --valgrind --verbose --clients 1\n    - name: module api test\n      run: ./runtest-moduleapi --valgrind --verbose --clients 1\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/.gitignore",
    "content": ".*.swp\n*.o\n*.xo\n*.so\n*.d\n*.log\ndump.rdb\nredis-benchmark\nredis-check-aof\nredis-check-rdb\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\nsrc/nodes.conf\ndeps/lua/src/lua\ndeps/lua/src/luac\ndeps/lua/src/liblua.a\n.make-*\n.prerequisites\n*.dSYM\nMakefile.dep\n.vscode/*\n.idea/*\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/00-RELEASENOTES",
    "content": "Redis 6.0 release notes\n=======================\n\n--------------------------------------------------------------------------------\nUpgrade urgency levels:\n\nLOW:      No need to upgrade unless there are new features you want to use.\nMODERATE: Program an upgrade of the server, but it's not urgent.\nHIGH:     There is a critical bug that may affect a subset of users. Upgrade!\nCRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.\nSECURITY: There are security fixes in the release.\n--------------------------------------------------------------------------------\n\n================================================================================\nRedis 6.0.3     Released Sat May 16 18:10:21 CEST 2020\n================================================================================\n\nUpgrade urgency CRITICAL: a crash introduced in 6.0.2 is now fixed.\n\n1eab62f7e Remove the client from CLOSE_ASAP list before caching the master.\n\n================================================================================\nRedis 6.0.2     Released Fri May 15 22:24:36 CEST 2020\n================================================================================\n\nUpgrade urgency MODERATE: many not critical bugfixes in different areas.\n                          Critical fix to client side caching when\n                          keys are evicted from the tracking table but\n                          no notifications are sent.\n\nThe following are the most serious fix:\n\n* XPENDING should not update consumer's seen-time\n* optimize memory usage of deferred replies - fixed\n* Fix CRC64 initialization outside the Redis server itself.\n* stringmatchlen() should not expect null terminated strings.\n* Cluster nodes availability checks improved when there is\n  high Pub/Sub load on the cluster bus.\n* Redis Benchmark: Fix coredump because of double free\n* Tracking: send eviction messages when evicting entries.\n* rax.c updated from upstream antirez/rax.\n* fix redis 6.0 not freeing closed connections during loading.\n\nNew features:\n\n* Support setcpuaffinity on linux/bsd\n* Client Side Caching: Add Tracking Prefix Number Stats in Server Info\n* Add --user argument to redis-benchmark.c (ACL)\n\nFull list of commits:\n\nYossi Gottlieb in commit 16ba33c05:\n TLS: Fix test failures on recent Debian/Ubuntu.\n 1 file changed, 20 deletions(-)\n\nYossi Gottlieb in commit 77ae66930:\n TLS: Add crypto locks for older OpenSSL support.\n 1 file changed, 45 insertions(+)\n\nDavid Carlier in commit 389697988:\n NetBSD build update.\n 3 files changed, 30 insertions(+), 1 deletion(-)\n\nMadelyn Olson in commit 2435341d7:\n Added a refcount on timer events to prevent deletion of recursive timer calls\n 2 files changed, 12 insertions(+)\n\nantirez in commit 80c906bd3:\n Cache master without checking of deferred close flags.\n 3 files changed, 11 insertions(+), 8 deletions(-)\n\nantirez in commit 74249be4a:\n Track events processed while blocked globally.\n 5 files changed, 32 insertions(+), 17 deletions(-)\n\nantirez in commit 8bf660af9:\n Some rework of #7234.\n 4 files changed, 77 insertions(+), 65 deletions(-)\n\nOran Agra in commit 9da134cd8:\n fix redis 6.0 not freeing closed connections during loading.\n 3 files changed, 133 insertions(+), 58 deletions(-)\n\nantirez in commit f7f219a13:\n Regression test for #7249.\n 1 file changed, 22 insertions(+)\n\nantirez in commit 693629585:\n rax.c updated from upstream antirez/rax.\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\nantirez in commit e3b5648df:\n Tracking: send eviction messages when evicting entries.\n 2 files changed, 29 insertions(+), 12 deletions(-)\n\nOran Agra in commit 5c41802d5:\n fix unstable replication test\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nShooterIT in commit a23cdbb94:\n Redis Benchmark: Fix coredump because of double free\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 1276058ea:\n Cluster: clarify we always resolve the sender.\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nantirez in commit 002fcde3d:\n Cluster: refactor ping/data delay handling.\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\nantirez in commit 960186a71:\n Cluster: introduce data_received field.\n 2 files changed, 27 insertions(+), 10 deletions(-)\n\nantirez in commit 3672875b4:\n stringmatchlen() should not expect null terminated strings.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nBrad Dunbar in commit 24e12641d:\n Remove unreachable branch.\n 1 file changed, 2 deletions(-)\n\nhwware in commit c7edffbd5:\n add jemalloc-bg-thread config in redis conf\n 1 file changed, 3 insertions(+)\n\nhwware in commit 8a9c84f4a:\n add include guard for lolwut.h\n 1 file changed, 6 insertions(+)\n\nantirez in commit cb683a84f:\n Don't propagate spurious MULTI on DEBUG LOADAOF.\n 2 files changed, 6 insertions(+), 3 deletions(-)\n\nantirez in commit 84d9766d6:\n Dump recent backlog on master query generating errors.\n 1 file changed, 29 insertions(+)\n\nTitouan Christophe in commit ec1e106ec:\n make struct user anonymous (only typedefed)\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit e48c37316:\n Test: --dont-clean should do first cleanup.\n 1 file changed, 2 insertions(+), 5 deletions(-)\n\nBenjamin Sergeant in commit 1e561cfaa:\n Add --user argument to redis-benchmark.c (ACL)\n 1 file changed, 15 insertions(+), 2 deletions(-)\n\nantirez in commit d1af82a88:\n Drop not needed part from #7194.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nMuhammad Zahalqa in commit 897a360d0:\n Fix compiler warnings on function rev(unsigned long)\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nantirez in commit ac316d8cc:\n Move CRC64 initialization in main().\n 2 files changed, 1 insertion(+), 4 deletions(-)\n\nantirez in commit fc7bc3204:\n Fix CRC64 initialization outside the Redis server itself.\n 1 file changed, 3 insertions(+)\n\nhwware in commit a6e55c096:\n Client Side Caching: Add Tracking Prefix Number Stats in Server Info\n 3 files changed, 8 insertions(+)\n\nantirez in commit b062fd523:\n Fix NetBSD build by fixing redis_set_thread_title() support.\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nantirez in commit 4efb25d9c:\n Rework a bit the documentation for CPU pinning.\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\nzhenwei pi in commit d6436eb7c:\n Support setcpuaffinity on linux/bsd\n 12 files changed, 180 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit 3a441c7d9:\n XPENDING should not update consumer's seen-time\n 4 files changed, 33 insertions(+), 20 deletions(-)\n\nOran Agra in commit 75addb4fe:\n optimize memory usage of deferred replies - fixed\n 1 file changed, 29 insertions(+)\n\nDeliang Yang in commit c57d9146f:\n reformat code\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nOran Agra in commit 3d3861dd8:\n add daily github actions with libc malloc and valgrind\n 5 files changed, 106 insertions(+), 18 deletions(-)\n\n\n================================================================================\nRedis 6.0.1     Released Sat May 02 00:06:07 CEST 2020\n================================================================================\n\nUpgrade urgency HIGH: This release fixes a crash when builiding against\n                      Libc malloc.\n\nHere we revert 8110ba888, an optimization that causes a crash due to a\nbug in the code. It does not happen with the default allocator because of\ndifferences between Jemalloc and libc malloc, so this escaped all our\ntesting but was reported by a user. We'll add back the original optimization\nthat was reverted here later, after checking what happens: it is not a\ncritical optimization.\n\nThe other commits are minor stuff:\n\nantirez in commit db73d0998:\n Cast printf() argument to the format specifier.\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nantirez in commit 7c0fe7271:\n Revert \"optimize memory usage of deferred replies\"\n 1 file changed, 31 deletions(-)\n\nantirez in commit 8fe25edc7:\n Save a call to stopThreadedIOIfNeeded() for the base case.\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\n================================================================================\nRedis 6.0.0 GA  Released Thu Apr 30 14:55:02 CEST 2020\n================================================================================\n\nUpgrade urgency CRITICAL: many bugs fixed compared to the last release\n                          candidate. Better to upgrade if you see things\n                          affecting your environment in the changelog.\n\nHi all, finally we have Redis 6.0.0 GA! Enjoy this new Redis release.\nMost of the documentation was updated today so that you can likely\nfind what you are looking for about the new features at redis.io.\nThis is the list of what changed compared to the previoius release candidate:\n\n* XCLAIM AOF/replicas propagation fixed.\n* Client side caching: new NOLOOP option to avoid getting notified about\n  changes performed by ourselves.\n* ACL GENPASS now uses HMAC-SHA256 and have an optional \"bits\" argument.\n  It means you can use it as a general purpose \"secure random strings\"\n  primitive!\n* Cluster \"SLOTS\" subcommand memory optimization.\n* The LCS command is now a subcommand of STRALGO.\n* Meaningful offset for replicas as well. More successful partial\n  resynchronizations.\n* Optimize memory usage of deferred replies.\n* Faster CRC64 algorithm for faster RDB loading.\n* XINFO STREAM FULL, a new subcommand to get the whole stream state.\n* CLIENT KILL USER <username>.\n* MIGRATE AUTH2 option, for ACL style authentication support.\n* Other random bugfixes.\n\nEnjoy Redis 6! :-)\nGoodbye antirez\n\nList of commits in this release:\n\nantirez in commit 1f9b82bd5:\n Update help.h again before Redis 6 GA.\n 1 file changed, 17 insertions(+), 12 deletions(-)\n\nantirez in commit 3fcffe7d0:\n redis-cli: fix hints with subcommands.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 455d8a05c:\n redis-cli command help updated.\n 1 file changed, 165 insertions(+), 25 deletions(-)\n\nzhaozhao.zz in commit 70287bbc9:\n lazyfree & eviction: record latency generated by lazyfree eviction\n 1 file changed, 18 insertions(+), 13 deletions(-)\n\nantirez in commit 7be21139a:\n MIGRATE AUTH2 for ACL support.\n 1 file changed, 19 insertions(+), 5 deletions(-)\n\nantirez in commit e1ee1a49d:\n CLIENT KILL USER <username>.\n 1 file changed, 11 insertions(+)\n\nantirez in commit d56f058c0:\n Fix tracking table max keys option in redis.conf.\n 1 file changed, 12 insertions(+), 9 deletions(-)\n\nantirez in commit 96dd5fc93:\n redis-cli: safer cluster fix with unreachalbe masters.\n 1 file changed, 26 insertions(+), 1 deletion(-)\n\nantirez in commit 5b59d9c5d:\n redis-cli: simplify cluster nodes coverage display.\n 1 file changed, 10 insertions(+), 17 deletions(-)\n\nantirez in commit c163d4add:\n redis-cli: try to make clusterManagerFixOpenSlot() more readable.\n 1 file changed, 25 insertions(+), 6 deletions(-)\n\nGuy Benoish in commit aab74b715:\n XINFO STREAM FULL should have a default COUNT of 10\n 1 file changed, 8 insertions(+), 4 deletions(-)\n\nantirez in commit 606134f9d:\n Comment clearly why we moved some code in #6623.\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nsrzhao in commit ee627bb66:\n fix pipelined WAIT performance issue.\n 1 file changed, 13 insertions(+), 13 deletions(-)\n\nantirez in commit 47b8a7f9b:\n Fix create-cluster BIN_PATH.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nGuy Benoish in commit 6c0bc608a:\n Extend XINFO STREAM output\n 2 files changed, 226 insertions(+), 34 deletions(-)\n\nhwware in commit 5bfc18950:\n Fix not used marco in cluster.c\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nItamar Haber in commit 56d628f85:\n Update create-cluster\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nItamar Haber in commit cac9d7cf7:\n Adds `BIN_PATH` to create-cluster\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\nOran Agra in commit b712fba17:\n hickup, re-fix dictEncObjKeyCompare\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\nOran Agra in commit ea63aea72:\n fix loading race in psync2 tests\n 3 files changed, 15 insertions(+), 1 deletion(-)\n\nantirez in commit 64e588bfa:\n Rework comment in dictEncObjKeyCompare().\n 1 file changed, 8 insertions(+), 9 deletions(-)\n\nOran Agra in commit 0d1e8c93b:\n allow dictFind using static robj\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\nMadelyn Olson in commit a1bed447b:\n Added crcspeed library\n 2 files changed, 341 insertions(+)\n\nMadelyn Olson in commit a75fa3aad:\n Made crc64 test consistent\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\nMadelyn Olson in commit 52c75e9db:\n Implemented CRC64 based on slice by 4\n 5 files changed, 124 insertions(+), 157 deletions(-)\n\nOran Agra in commit 8110ba888:\n optimize memory usage of deferred replies\n 1 file changed, 31 insertions(+)\n\nOran Agra in commit e4d2bb62b:\n Keep track of meaningful replication offset in replicas too\n 5 files changed, 212 insertions(+), 92 deletions(-)\n\nantirez in commit fea9788cc:\n Fix STRALGO command flags.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nDave-in-lafayette in commit 2144047e1:\n fix for unintended crash during panic response\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nGuy Benoish in commit 43329c9b6:\n Add the stream tag to XSETID tests\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nDave-in-lafayette in commit 1e17d3de7:\n fix for crash during panic before all threads are up\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 3722f89f4:\n LCS -> STRALGO LCS.\n 4 files changed, 28 insertions(+), 15 deletions(-)\n\nantirez in commit 373ae6061:\n Also use propagate() in streamPropagateGroupID().\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\nyanhui13 in commit f03f1fad6:\n add tcl test for cluster slots\n 1 file changed, 44 insertions(+)\n\nyanhui13 in commit 374ffdf1c:\n optimize the output of cluster slots\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\nantirez in commit 4db38d2ef:\n Minor aesthetic changes to #7135.\n 1 file changed, 5 insertions(+), 7 deletions(-)\n\nValentino Geron in commit f0a261448:\n XREADGROUP with NOACK should propagate only one XGROUP SETID command\n 1 file changed, 13 insertions(+), 7 deletions(-)\n\nantirez in commit fbdef6a9b:\n ACL: re-enable command execution of disabled users.\n 1 file changed, 4 deletions(-)\n\nantirez in commit 05a41da75:\n getRandomBytes(): use HMAC-SHA256.\n 1 file changed, 30 insertions(+), 10 deletions(-)\n\nantirez in commit 345c3768d:\n ACL GENPASS: take number of bits as argument.\n 1 file changed, 21 insertions(+), 6 deletions(-)\n\nantirez in commit 639c8a1d9:\n ACL GENPASS: emit 256 bits instead of 128.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 321acea03:\n ACL: deny commands execution of disabled users.\n 1 file changed, 4 insertions(+)\n\nTheo Buehler in commit b0920e6e8:\n TLS: Fix build with SSL_OP_NO_CLIENT_RENEGOTIATION\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nYossi Gottlieb in commit 149b658b5:\n TLS: Fix build on older verisons of OpenSSL.\n 1 file changed, 2 insertions(+)\n\nantirez in commit 06917e581:\n Tracking: test expired keys notifications.\n 1 file changed, 13 insertions(+)\n\nantirez in commit e434b2ce4:\n Tracking: NOLOOP tests.\n 1 file changed, 32 insertions(+)\n\nantirez in commit f3a172887:\n Tracking: signal key as modified when evicting.\n 1 file changed, 1 insertion(+)\n\nantirez in commit e63bb7ec8:\n Tracking: NOLOOP further implementation and fixes.\n 2 files changed, 21 insertions(+), 6 deletions(-)\n\nantirez in commit 6791ff052:\n Tracking: NOLOOP internals implementation.\n 17 files changed, 174 insertions(+), 112 deletions(-)\n\nantirez in commit 725b8cc68:\n Implement redis_set_thread_title for MacOS.\n 1 file changed, 6 insertions(+)\n\nzhenwei pi in commit 3575b8706:\n Threaded IO: set thread name for redis-server\n 3 files changed, 28 insertions(+)\n\nantirez in commit a76c67578:\n Sentinel: small refactoring of sentinelCollectTerminatedScripts().\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\nomg-by in commit 3a27064c4:\n fix(sentinel): sentinel.running_scripts will always increase more times and not reset\n 1 file changed, 1 insertion(+)\n\nantirez in commit 5c4c73e2c:\n A few comments and name changes for #7103.\n 1 file changed, 13 insertions(+), 4 deletions(-)\n\nOran Agra in commit 6148f9493:\n testsuite run the defrag latency test solo\n 3 files changed, 42 insertions(+), 2 deletions(-)\n\nJamie Scott in commit 51d3012d4:\n Adding acllog-max-len to Redis.conf\n 1 file changed, 9 insertions(+)\n\nantirez in commit c39f16c42:\n Fix XCLAIM propagation in AOF/replicas for blocking XREADGROUP.\n 2 files changed, 8 insertions(+), 3 deletions(-)\n\n================================================================================\nRedis 6.0-rc4     Released Thu Apr 16 16:10:35 CEST 2020\n================================================================================\n\nUpgrade urgency LOW: If you are using RC3 without issues, don't rush.\n\nHi all, this the latest release candidate of Redis 6. This is likely to\nbe very similar to what you'll see in Redis 6 GA. Please test it and\nreport any issue :-)\n\nMain changes in this release:\n\n    * Big INFO speedup when using a lot of of clients.\n    * Big speedup on all the blocking commands: now blocking\n      on the same key is O(1) instead of being O(N).\n    * Stale replicas now allow MULTI/EXEC.\n    * New command: LCS (Longest Common Subsequence).\n    * Add a new configuration to make DEL like UNLINK.\n    * RDB loading speedup.\n    * Many bugs fixed (see the commit messages at the end of this node)\n\nSee you in 14 days for Redis 6 GA.\n\nList of commits:\n\nantirez in commit 9f594e243:\n Update SDS to latest version.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 48781dd95:\n RESP3: fix HELLO map len in Sentinel mode.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 371ab0cff:\n Don't allow empty spaces in ACL usernames.\n 1 file changed, 36 insertions(+), 8 deletions(-)\n\nantirez in commit b86140ac5:\n Don't allow empty spaces in ACL key patterns.\n 1 file changed, 12 insertions(+), 1 deletion(-)\n\nliumiuyong in commit a7ee3c3e7:\n FIX: truncate max/min longitude,latitude related geo_point (ex:  {180, 85.05112878} )\n 1 file changed, 4 insertions(+)\n\nGuy Benoish in commit e5b9eb817:\n Typo in getTimeoutFromObjectOrReply's error reply\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 0f31bb5c1:\n Fix HELLO reply in Sentinel mode, see #6160.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit b92d9a895:\n fix spelling in acl.c\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nantirez in commit 8f896e57a:\n Fix zsetAdd() top comment spelling.\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nhayleeliu in commit 8f5157058:\n fix spelling mistake in bitops.c\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit ddeda9ceb:\n Fix function names in zslDeleteNode() top comment.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit bde1f0a8e:\n RESP3: change streams items from maps to arrays.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit bec68bff2:\n Use the special static refcount for stack objects.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 0f239e51b:\n RDB: refactor some RDB loading code into dbAddRDBLoad().\n 3 files changed, 22 insertions(+), 4 deletions(-)\n\nantirez in commit f855db61b:\n incrRefCount(): abort on statically allocated object.\n 2 files changed, 12 insertions(+), 2 deletions(-)\n\nantirez in commit 23094ba01:\n More powerful DEBUG RELOAD.\n 3 files changed, 55 insertions(+), 16 deletions(-)\n\nantirez in commit 8161a7a3e:\n RDB: clarify a condition in rdbLoadRio().\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\nantirez in commit 61b153073:\n RDB: load files faster avoiding useless free+realloc.\n 7 files changed, 40 insertions(+), 28 deletions(-)\n\nantirez in commit 414debfd0:\n Speedup: unblock clients on keys in O(1).\n 4 files changed, 50 insertions(+), 23 deletions(-)\n\nantirez in commit cbcd07777:\n Fix ACL HELP table missing comma.\n 1 file changed, 12 insertions(+), 12 deletions(-)\n\nmymilkbottles in commit 2437455f2:\n Judge the log level in advance\n 1 file changed, 1 insertion(+)\n\nantirez in commit 35c64b898:\n Speedup INFO by counting client memory incrementally.\n 4 files changed, 52 insertions(+), 26 deletions(-)\n\nqetu3790 in commit c3ac71748:\n fix comments about RESIZE DB opcode in rdb.c\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\nantirez in commit c8dbcff9d:\n Clarify redis.conf comment about lazyfree-lazy-user-del.\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\nzhaozhao.zz in commit abd5156f2:\n lazyfree: add a new configuration lazyfree-lazy-user-del\n 4 files changed, 7 insertions(+), 2 deletions(-)\n\nantirez in commit 5719b3054:\n LCS: more tests.\n 1 file changed, 8 insertions(+)\n\nantirez in commit c89e1f293:\n LCS: allow KEYS / STRINGS to be anywhere.\n 1 file changed, 6 deletions(-)\n\nantirez in commit 0b16f8d44:\n LCS tests.\n 1 file changed, 22 insertions(+)\n\nantirez in commit 9254a805d:\n LCS: get rid of STOREIDX option. Fix get keys helper.\n 2 files changed, 20 insertions(+), 21 deletions(-)\n\nantirez in commit a4c490703:\n LCS: fix stale comment.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit cb92c23de:\n LCS: output LCS len as well in IDX mode.\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nantirez in commit 56a52e804:\n LCS: MINMATCHLEN and WITHMATCHLEN options.\n 1 file changed, 24 insertions(+), 11 deletions(-)\n\nantirez in commit ebb09a5c3:\n LCS: 7x speedup by accessing the array with better locality.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit a9f8a8cba:\n LCS: implement KEYS option.\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\nantirez in commit 4aa24e62a:\n LCS: other fixes to range emission.\n 1 file changed, 20 insertions(+), 16 deletions(-)\n\nantirez in commit 2b67b6b87:\n LCS: fix emission of last range starting at index 0.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 420aac727:\n LCS: implement range indexes option.\n 1 file changed, 59 insertions(+), 9 deletions(-)\n\nantirez in commit a518a9a76:\n LCS: initial functionality implemented.\n 4 files changed, 156 insertions(+), 1 deletion(-)\n\nsrzhao in commit 026cc11b0:\n Check OOM at script start to get stable lua OOM state.\n 3 files changed, 11 insertions(+), 4 deletions(-)\n\nOran Agra in commit 02b594f6a:\n diffrent fix for runtest --host --port\n 2 files changed, 13 insertions(+), 13 deletions(-)\n\nGuy Benoish in commit f695d1830:\n Try to fix time-sensitive tests in blockonkey.tcl\n 1 file changed, 54 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit 0e42cfc36:\n Use __attribute__ only if __GNUC__ is defined\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\nGuy Benoish in commit 91ed9b3c4:\n Modules: Perform printf-like format checks in variadic API\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nValentino Geron in commit 3e0d20962:\n XREAD and XREADGROUP should not be allowed from scripts when BLOCK option is being used\n 3 files changed, 18 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 240094c9b:\n Stale replica should allow MULTI/EXEC\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nXudong Zhang in commit 209f3a1eb:\n fix integer overflow\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 024c380b9:\n Fix no-negative-zero test\n 1 file changed, 1 insertion(+)\n\nOran Agra in commit a38ff404b:\n modules don't signalModifiedKey in setKey() since that's done (optionally) in RM_CloseKey\n 4 files changed, 8 insertions(+), 8 deletions(-)\n\nOran Agra in commit 814874d68:\n change CI to build and run the module api tests\n 1 file changed, 2 insertions(+)\n\nOran Agra in commit 061616c1b:\n fix possible warning on incomplete struct init\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nGuy Benoish in commit 7764996be:\n Make sure Redis does not reply with negative zero\n 2 files changed, 10 insertions(+)\n\nGuy Benoish in commit eba28e2ce:\n DEBUG OBJECT should pass keyname to module when loading\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\nDavid Carlier in commit 15c9e79a7:\n debug, dump registers on arm too.\n 1 file changed, 55 insertions(+), 27 deletions(-)\n\nhwware in commit cd2b5df97:\n fix spelling in cluster.c\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nValentino Geron in commit 8cdc153f5:\n XACK should be executed in a \"all or nothing\" fashion.\n 2 files changed, 23 insertions(+), 1 deletion(-)\n\nhwware in commit b35407fa7:\n add check for not switching between optin optout mode directly\n 1 file changed, 12 insertions(+), 1 deletion(-)\n\nhwware in commit 4395889c9:\n add check for not providing both optin optout flag\n 1 file changed, 8 insertions(+)\n\nGuy Benoish in commit 1907e0f18:\n PERSIST should notify a keyspace event\n 1 file changed, 1 insertion(+)\n\nGuy Benoish in commit c35a53169:\n streamReplyWithRange: Redundant XSETIDs to replica\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 6fe66e096:\n Simplify comment in moduleTryServeClientBlockedOnKey().\n 1 file changed, 3 insertions(+), 12 deletions(-)\n\nGuy Benoish in commit 193fc241c:\n Fix memory corruption in moduleHandleBlockedClients\n 3 files changed, 149 insertions(+), 46 deletions(-)\n\n================================================================================\nRedis 6.0-rc3     Released Tue Mar 31 17:42:39 CEST 2020\n================================================================================\n\nUpgrade urgency CRITICAL: A connection management bug introduced with the\n                          SSL implementation can crash Redis easily.\n\nDear users, this is a list of the major changes in this release, please check \nthe list of commits for detail:\n\n* Fix crash due to refactoring for SSL, for the connection code.\n* Precise timeouts for blocking commands. Now the timeouts have HZ\n  resolution regardless of the number of connected clinets. New timeouts\n  are stored in a radix tree and sorted by expire time.\n* Fix rare crash when resizing the event loop because of CONFIG maxclients.\n* Fix systemd readiness after successful partial resync.\n* Redis-cli ask password mode to be prompted at startup (for additional safety).\n* Keyspace notifications added to MIGRATE / RESTORE.\n* Threaded I/O bugs fixed.\n* Implement new ACL style AUTH in Sentinel.\n* Make 'requirepass' more backward compatible with Redis <= 5.\n* ACL: Handle default user as disabled if it's off regardless of \"nopass\".\n* Fix a potential inconsistency when upgrading an instance in Redis Cluster\n  and restarting it. The instance will act as a replica but will actually be\n  set as a master immediately. However the choice of what to do with already\n  expired keys, on loading, was made from the POV of replicas.\n* Abort transactions after -READONLY error.\n* Many different fixes to module APIs.\n* BITFIELD_RO added to call the command on read only replicas.\n* PSYNC2: meaningful offset implementation. Allow the disconnected master\n  that is still sending PINGs to replicas, to be able to successfully\n  PSYNC incrementally to new slaves, discarding the last part of the\n  replication backlog consisting only of PINGs.\n* Fix pipelined MULTI/EXEC during Lua scripts are in BUSY state.\n* Re-fix propagation API in modules, broken again after other changes.\n\nantirez in commit ef1b1f01:\n cast raxSize() to avoid warning with format spec.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 9f347fab:\n Minor changes to #7037.\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\nGuy Benoish in commit a509400d:\n Modules: Test MULTI/EXEC replication of RM_Replicate\n 6 files changed, 49 insertions(+), 9 deletions(-)\n\nGuy Benoish in commit 805c8c94:\n RENAME can unblock XREADGROUP\n 3 files changed, 25 insertions(+), 1 deletion(-)\n\nantirez in commit 97b80b57:\n Fix the propagate Tcl test after module changes.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 4f6b6b80:\n Modify the propagate unit test to show more cases.\n 1 file changed, 30 insertions(+), 2 deletions(-)\n\nantirez in commit 616b1cb7:\n Fix module commands propagation double MULTI bug.\n 4 files changed, 25 insertions(+), 8 deletions(-)\n\nantirez in commit 08fdef4b:\n Fix RM_Call() stale comment due to cut&paste.\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\nOMG-By in commit 26b79ca1:\n fix: dict.c->dictResize()->minimal  type\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nzhaozhao.zz in commit fa418637:\n PSYNC2: reset backlog_idx and master_repl_offset correctly\n 1 file changed, 10 insertions(+), 5 deletions(-)\n\nantirez in commit bbbc80ac:\n Precise timeouts: reference client pointer directly.\n 1 file changed, 13 insertions(+), 16 deletions(-)\n\nantirez in commit c3b268a0:\n timeout.c created: move client timeouts code there.\n 5 files changed, 198 insertions(+), 167 deletions(-)\n\nOran Agra in commit 0f7dfc37:\n AOFRW on an empty stream created with MKSTREAM loads badkly\n 2 files changed, 15 insertions(+), 1 deletion(-)\n\nantirez in commit 67643ead:\n Precise timeouts: cleaup the table on unblock.\n 3 files changed, 21 insertions(+), 2 deletions(-)\n\nantirez in commit ad94066e:\n Precise timeouts: fix comments after functional change.\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\nantirez in commit a443ec2e:\n Precise timeouts: use only radix tree for timeouts.\n 3 files changed, 15 insertions(+), 38 deletions(-)\n\nantirez in commit 6862fd70:\n Precise timeouts: fast exit for clientsHandleShortTimeout().\n 1 file changed, 1 insertion(+)\n\nantirez in commit 30f1df8c:\n Precise timeouts: fix bugs in initial implementation.\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\nantirez in commit 7add0f24:\n Precise timeouts: working initial implementation.\n 3 files changed, 110 insertions(+), 28 deletions(-)\n\nantirez in commit 9d6d1779:\n Precise timeouts: refactor unblocking on timeout.\n 2 files changed, 33 insertions(+), 13 deletions(-)\n\nantirez in commit 316a8f15:\n PSYNC2: fix backlog_idx when adjusting for meaningful offset\n 1 file changed, 3 insertions(+)\n\n伯成 in commit 11db53f8:\n Boost up performance for redis PUB-SUB patterns matching\n 3 files changed, 43 insertions(+), 11 deletions(-)\n\nantirez in commit e257f121:\n PSYNC2: meaningful offset test.\n 2 files changed, 62 insertions(+)\n\nantirez in commit 5f72f696:\n PSYNC2: meaningful offset implemented.\n 3 files changed, 40 insertions(+), 1 deletion(-)\n\nantirez in commit 8caa2714:\n Explain why we allow transactions in -BUSY state.\n 1 file changed, 9 insertions(+), 2 deletions(-)\n\nOran Agra in commit e43cd831:\n MULTI/EXEC during LUA script timeout are messed up\n 2 files changed, 73 insertions(+)\n\nantirez in commit 34b89832:\n Improve comments of replicationCacheMasterUsingMyself().\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nantirez in commit 70a98a43:\n Fix BITFIELD_RO test.\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\nantirez in commit 8783304a:\n Abort transactions after -READONLY error. Fix #7014.\n 1 file changed, 1 insertion(+)\n\nantirez in commit ec9cf002:\n Minor changes to BITFIELD_RO PR #6951.\n 1 file changed, 9 insertions(+), 6 deletions(-)\n\nbodong.ybd in commit b3e4abf0:\n Added BITFIELD_RO variants for read-only operations.\n 4 files changed, 54 insertions(+), 1 deletion(-)\n\nantirez in commit 50f8f950:\n Modules: updated function doc after #7003.\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit f2f3dc5e:\n Allow RM_GetContextFlags to work with ctx==NULL\n 1 file changed, 16 insertions(+), 14 deletions(-)\n\nhwware in commit eb808879:\n fix potentical memory leak in redis-cli\n 1 file changed, 2 insertions(+)\n\nYossi Gottlieb in commit cdcab0e8:\n Fix crashes related to failed/rejected accepts.\n 1 file changed, 6 insertions(+), 5 deletions(-)\n\nYossi Gottlieb in commit 50dcd9f9:\n Cluster: fix misleading accept errors.\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\nYossi Gottlieb in commit 87dbd8f5:\n Conns: Fix connClose() / connAccept() behavior.\n 3 files changed, 48 insertions(+), 32 deletions(-)\n\nhwware in commit 81e8686c:\n remove redundant Semicolon\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit c7524a7e:\n clean CLIENT_TRACKING_CACHING flag when disabled caching\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit 2dd1ca6a:\n add missing commands in cluster help\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nartix in commit 95324b81:\n Support Redis Cluster Proxy PROXY INFO command\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\n박승현 in commit 04c53fa1:\n Update redis.conf\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nWuYunlong in commit 0578157d:\n Fix master replica inconsistency for upgrading scenario.\n 3 files changed, 9 insertions(+), 2 deletions(-)\n\nWuYunlong in commit 299f1d02:\n Add 14-consistency-check.tcl to prove there is a data consistency issue.\n 1 file changed, 87 insertions(+)\n\nantirez in commit 61b98f32:\n Regression test for #7011.\n 1 file changed, 7 insertions(+)\n\nantirez in commit 34ea2f4e:\n ACL: default user off should not allow automatic authentication.\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\nantirez in commit cbbf9b39:\n Sentinel: document auth-user directive.\n 1 file changed, 12 insertions(+)\n\nantirez in commit 9c2e42dd:\n ACL: Make Redis 6 more backward compatible with requirepass.\n 4 files changed, 17 insertions(+), 15 deletions(-)\n\nantirez in commit d387f67d:\n Sentinel: implement auth-user directive for ACLs.\n 1 file changed, 38 insertions(+), 7 deletions(-)\n\nzhaozhao.zz in commit 7c078416:\n Threaded IO: bugfix client kill may crash redis\n 1 file changed, 11 insertions(+), 5 deletions(-)\n\nzhaozhao.zz in commit 9cc7038e:\n Threaded IO: handle pending reads clients ASAP after event loop\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nantirez in commit da8c7c49:\n Example sentinel conf: document requirepass.\n 1 file changed, 8 insertions(+)\n\nantirez in commit bdb338cf:\n Aesthetic changes in PR #6989.\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\nzhaozhao.zz in commit b3e03054:\n Threaded IO: bugfix #6988 process events while blocked\n 1 file changed, 5 insertions(+)\n\nantirez in commit e628f944:\n Restore newline at the end of redis-cli.c\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nchendianqiang in commit 5d4c4df3:\n use correct list for moduleUnregisterUsedAPI\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nguodongxiaren in commit da14982d:\n string literal should be const char*\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nItamar Haber in commit dc8885a1:\n Adds keyspace notifications to migrate and restore\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nbodong.ybd in commit bfb18e55:\n Remove duplicate obj files in Makefile\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nbodong.ybd in commit 76d57161:\n Fix bug of tcl test using external server\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\nfengpf in commit 0e5820d8:\n fix comments in latency.c\n 2 files changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 916dd79f:\n Update linenoise.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nlifubang in commit c0c67c9b:\n add askpass mode\n 1 file changed, 19 insertions(+), 1 deletion(-)\n\nlifubang in commit e1c29434:\n update linenoise to https://github.com/antirez/linenoise/tree/fc9667a81d43911a6690fb1e68c16e6e3bb8df05\n 4 files changed, 59 insertions(+), 4 deletions(-)\n\nJamie Scott in commit e5a063bc:\n Remove default guidance in Redis.conf\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\nJamie Scott in commit d28cbaf7:\n Update Redis.conf to improve TLS usability\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nJohannes Truschnigg in commit 23d5e8b8:\n Signal systemd readiness atfer Partial Resync\n 1 file changed, 4 insertions(+)\n\nOran Agra in commit 61738154:\n fix for flaky psync2 test\n 1 file changed, 21 insertions(+)\n\nantirez in commit 70e0e499:\n ae.c: fix crash when resizing the event loop.\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\nantirez in commit b3e4aa67:\n Fix release notes spelling mistake.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\n\n================================================================================\nRedis 6.0 RC2     Released Thu Mar 05 15:40:53 CET 2020\n================================================================================\n\nUpgrade urgency MODERATE: Normal bugfixing release of a non-GA branch.\n\nHi Redis users, Redis 6 is approaching and will be released 30th of April.\nNew release candidates will be released at the end of March, then another\none mid April, to finally reach the GA at the end of April.\n\nRedis 6 RC2 brings many fixes and new things, especially in the area of\nclient side caching. This is the list of big changes in this release. As\nusually you can find the full list of commits at the end:\n\nNew features and improvements:\n\n* ACL LOG: log denied commands, keys accesses and authentications.\n* Client side caching redesigned. Now we use keys not caching slots.\n* Client side caching: Broadcasting mode implemented.\n* Client side caching: OPTIN/OPTOUT modes implemented.\n* Remove RDB files used for replication in persistence-less instances (option).\n\nFixes (only selected ones, see commits for all the fixes):\n\n* Different fixes to streams in edge cases.\n* Fix duplicated CLIENT SETNAME reply because of RESP3 changes.\n* Fix crash due to new active expire division by zero.\n* Avoid sentinel changes promoted_slave to be its own replica.\n* Fix bug on KEYS command where pattern starts with * followed by \\x00.\n* Threaded I/O: now the main thread is used as well to do I/O.\n* Many fixes to modules APIs, and more to come in the next RCs.\n* ld2string should fail if string contains \\0 in the middle.\n* Make the Redis test more reliable.\n* Fix SPOP returning nil (see #4709). WARNING: API change.\n\nqetu3790 in commit 4af0d7fd:\n Fix not used constant in lru_test_mode.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit 6ef01878:\n add missing file marco\n 1 file changed, 5 insertions(+)\n\nShooterIT in commit fe81d5c8:\n Avoid compiler warnings\n 1 file changed, 1 insertion(+)\n\nantirez in commit c2f01d7f:\n RDB deletion: document it in example redis.conf.\n 1 file changed, 13 insertions(+)\n\nantirez in commit 127e09bc:\n Make sync RDB deletion configurable. Default to no.\n 3 files changed, 22 insertions(+), 4 deletions(-)\n\nantirez in commit a20303c6:\n Check that the file exists in removeRDBUsedToSyncReplicas().\n 1 file changed, 8 insertions(+), 4 deletions(-)\n\nantirez in commit 7a23b945:\n Log RDB deletion in persistence-less instances.\n 1 file changed, 15 insertions(+), 2 deletions(-)\n\nantirez in commit baaf869f:\n Introduce bg_unlink().\n 1 file changed, 31 insertions(+), 3 deletions(-)\n\nantirez in commit be4bc1a5:\n Remove RDB files used for replication in persistence-less instances.\n 3 files changed, 56 insertions(+), 1 deletion(-)\n\nantirez in commit 07dc1b42:\n Use a smaller getkeys global buffer.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nOran Agra in commit 10e71b3d:\n Optimize temporary memory allocations for getKeysFromCommand mechanism\n 1 file changed, 31 insertions(+), 10 deletions(-)\n\nantirez in commit edc0ed14:\n Modules: reformat RM_Scan() top comment a bit.\n 1 file changed, 21 insertions(+), 12 deletions(-)\n\nantirez in commit c5319612:\n Modules: more details in RM_Scan API top comment.\n 1 file changed, 22 insertions(+), 6 deletions(-)\n\nOran Agra in commit fff6b26a:\n RM_Scan disable dict rehashing\n 2 files changed, 21 insertions(+), 6 deletions(-)\n\nGuy Benoish in commit 65048460:\n Add RM_CreateStringFromDouble\n 2 files changed, 14 insertions(+)\n\nOran Agra in commit 3144a278:\n add no_auth to COMMAND INFO\n 1 file changed, 1 insertion(+)\n\nOran Agra in commit afe0b16c:\n module api docs for aux_save and aux_load\n 2 files changed, 7 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit df152b0c:\n streamReplyWithRangeFromConsumerPEL: Redundant streamDecodeID\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\nantirez in commit e3c1f439:\n Show Redis version when not understanding a config directive.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 141c0679:\n Changelog: explain Redis 6 SPOP change.\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nbodong.ybd in commit fe902461:\n Fix spop return nil #4709\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 9d4219eb:\n Fix SDS misuse in enumConfigSet(). Related to #6778.\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nantirez in commit 84243064:\n Remove useless comment from enumConfigSet().\n 1 file changed, 1 deletion(-)\n\nPonnuvel Palaniyappan in commit dafb94db:\n Fix a potential overflow with strncpy\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\nantirez in commit ea697b63:\n Improve aeDeleteEventLoop() top comment grammar.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nwangyuan21 in commit dd479880:\n free time event when delete eventloop\n 1 file changed, 7 insertions(+)\n\nsrzhao in commit ecf3b2ef:\n fix impl of aof-child whitelist SIGUSR1 feature.\n 1 file changed, 5 insertions(+), 4 deletions(-)\n\nmeir@redislabs.com in commit 2966132c:\n Changed log level for module fork api from 'notice' to 'verbos'.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nhwware in commit 7277e5d8:\n format fix\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit 1bb5ee9c:\n fix potentical memory leaks\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nHengjian Tang in commit 97329733:\n modify the read buf size according to the write buf size PROTO_IOBUF_LEN defined before\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nAriel in commit 15ea1324:\n fix ThreadSafeContext lock/unlock function names\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 4d12c37c:\n XREADGROUP should propagate XCALIM/SETID in MULTI/EXEC\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nOran Agra in commit 12626ce9:\n fix race in module api test for fork\n 2 files changed, 2 insertions(+), 3 deletions(-)\n\nGuy Benoish in commit 2ecab0b6:\n Modules: Do not auto-unblock clients if not blocked on keys\n 1 file changed, 22 insertions(+), 7 deletions(-)\n\nOran Agra in commit 635321d4:\n fix github actions failing latency test for active defrag - part 2\n 2 files changed, 5 insertions(+), 4 deletions(-)\n\nOran Agra in commit 0b988fa9:\n fix github actions failing latency test for active defrag\n 2 files changed, 14 insertions(+), 13 deletions(-)\n\nOran Agra in commit 60096bc1:\n Fix latency sensitivity of new defrag test\n 1 file changed, 32 insertions(+), 8 deletions(-)\n\nantirez in commit b4395426:\n Tracking: optin/out implemented.\n 3 files changed, 82 insertions(+), 16 deletions(-)\n\nantirez in commit ef3551d1:\n Test engine: experimental change to avoid busy port problems.\n 1 file changed, 84 insertions(+), 49 deletions(-)\n\nantirez in commit 72c05351:\n Test engine: detect timeout when checking for Redis startup.\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\nantirez in commit 294c9af4:\n Test engine: better tracking of what workers are doing.\n 2 files changed, 12 insertions(+), 4 deletions(-)\n\nhwware in commit ba027079:\n add missing subcommand description for debug oom\n 1 file changed, 1 insertion(+)\n\nGuy Benoish in commit 5d0890c0:\n Fix memory leak in test_ld_conv\n 1 file changed, 4 insertions(+)\n\nMadelyn Olson in commit d1f22eac:\n Give an error message if you specify redirect twice\n 1 file changed, 7 insertions(+)\n\nMadelyn Olson in commit 762fbcb6:\n Minor CSC fixes and fixed documentation\n 2 files changed, 16 insertions(+), 17 deletions(-)\n\nOran Agra in commit 349aa245:\n Defrag big lists in portions to avoid latency and freeze\n 4 files changed, 350 insertions(+), 34 deletions(-)\n\nGuy Benoish in commit b4ddc7b7:\n XGROUP DESTROY should unblock XREADGROUP with -NOGROUP\n 2 files changed, 11 insertions(+)\n\nhayashier in commit 73806f74:\n fix typo from fss to rss\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nantirez in commit b6129f86:\n Test is more complex now, increase default timeout.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit f15fb727:\n Tracking: fix max-keys configuration directive.\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\nItamar Haber in commit e374573f:\n Fixes segfault on calling trackingGetTotalKeys\n 1 file changed, 1 insertion(+)\n\nantirez in commit 73d47d57:\n Signal key as modified when expired on-access.\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\nantirez in commit b7cb28d5:\n Tracking: first set of tests for the feature.\n 1 file changed, 66 insertions(+)\n\nantirez in commit 1db72571:\n Tracking: fix operators precedence error in bcast check.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit fe96e29d:\n Tracking: fix behavior when switchinig from normal to BCAST.\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\nantirez in commit f21be1ec:\n Tracking: fix sending messages bug + tracking off bug.\n 2 files changed, 28 insertions(+), 20 deletions(-)\n\nantirez in commit 6fb1aa23:\n Tracking: BCAST: basic feature now works.\n 3 files changed, 55 insertions(+), 40 deletions(-)\n\nantirez in commit d4fe79a1:\n Tracking: BCAST: broadcasting of keys in prefixes implemented.\n 2 files changed, 94 insertions(+), 9 deletions(-)\n\nantirez in commit abb81c63:\n Tracking: BCAST: registration in the prefix table.\n 3 files changed, 67 insertions(+), 20 deletions(-)\n\nantirez in commit 77da9608:\n Tracking: BCAST: parsing of the options + skeleton.\n 4 files changed, 73 insertions(+), 19 deletions(-)\n\nantirez in commit 3e8c69a9:\n Tracking: always reply with an array of keys.\n 2 files changed, 10 insertions(+), 3 deletions(-)\n\nantirez in commit a788c373:\n Tracking: minor change of names and new INFO field.\n 4 files changed, 11 insertions(+), 4 deletions(-)\n\nantirez in commit df838927:\n Rax.c: populate data field after random walk.\n 1 file changed, 1 insertion(+)\n\nantirez in commit 0517da36:\n Tracking: rename INFO field with total items.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 3c16d6b3:\n Tracking: first conversion from hashing to key names.\n 3 files changed, 84 insertions(+), 114 deletions(-)\n\nOran Agra in commit 3b4f1477:\n add no-slowlog option to RM_CreateCommand\n 1 file changed, 3 insertions(+)\n\nKhem Raj in commit 5e762d84:\n Mark extern definition of SDS_NOINIT in sds.h\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nlifubang in commit 54f5499a:\n correct help info for --user and --pass\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nSeunghoon Woo in commit 0c952b13:\n [FIX] revisit CVE-2015-8080 vulnerability\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\nGuy Benoish in commit dd34f703:\n Diskless-load emptyDb-related fixes\n 3 files changed, 44 insertions(+), 28 deletions(-)\n\nlifubang in commit 5e042dbc:\n fix ssl flag check for redis-cli\n 1 file changed, 10 insertions(+), 9 deletions(-)\n\nGuy Benoish in commit dcbe8bfa:\n Exclude \"keymiss\" notification from NOTIFY_ALL\n 5 files changed, 12 insertions(+), 7 deletions(-)\n\nOran Agra in commit 36caf2e4:\n update RM_SignalModifiedKey doc comment\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nOran Agra in commit 3067352a:\n Add handling of short read of module id in rdb\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nYossi Gottlieb in commit 9baaf858:\n TLS: Update documentation.\n 2 files changed, 32 insertions(+), 31 deletions(-)\n\nOran Agra in commit 4440133e:\n A few non-data commands that should be allowed while loading or stale\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\nOran Agra in commit c9577941:\n Memory leak when bind config is provided twice\n 1 file changed, 4 insertions(+)\n\nOran Agra in commit 1333a46b:\n fix maxmemory config warning\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\nOran Agra in commit 8e7282eb:\n Fix client flags to be int64 in module.c\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nOran Agra in commit a678390e:\n moduleRDBLoadError, add key name, and use panic rather than exit\n 1 file changed, 5 insertions(+), 4 deletions(-)\n\nOran Agra in commit 919fbf42:\n reduce repeated calls to use_diskless_load\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\nOran Agra in commit 22e45d46:\n freeClientAsync don't lock mutex if there's just one thread\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nOran Agra in commit ba289244:\n move restartAOFAfterSYNC from replicaofCommand to replicationUnsetMaster\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\nOran Agra in commit f42ce57d:\n stopAppendOnly resets aof_rewrite_scheduled\n 1 file changed, 1 insertion(+)\n\nOran Agra in commit df096bc9:\n add SAVE subcommand to ACL HELP and top comment\n 1 file changed, 2 insertions(+)\n\nOran Agra in commit a55e5847:\n DEBUG HELP - add PROTOCOL\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 5a6cfbf4:\n Some refactroing using getClientType instead of CLIENT_SLAVE\n 2 files changed, 18 insertions(+), 26 deletions(-)\n\nGuy Benoish in commit fae306b3:\n Fix small bugs related to replica and monitor ambiguity\n 2 files changed, 8 insertions(+), 6 deletions(-)\n\nYossi Gottlieb in commit 73630966:\n TLS: Some redis.conf clarifications.\n 1 file changed, 10 insertions(+), 11 deletions(-)\n\nOran Agra in commit 488e1947:\n config.c verbose error replies for CONFIG SET, like config file parsing\n 1 file changed, 31 insertions(+), 97 deletions(-)\n\nOran Agra in commit c82ccf06:\n memoryGetKeys helper function so that ACL can limit access to keys for MEMORY command\n 3 files changed, 18 insertions(+), 1 deletion(-)\n\nantirez in commit 51c1a9f8:\n ACL LOG: make max log entries configurable.\n 4 files changed, 19 insertions(+)\n\nantirez in commit ea1e1b12:\n ACL LOG: test for AUTH reason.\n 1 file changed, 9 insertions(+)\n\nantirez in commit 7379c78a:\n ACL LOG: log failed auth attempts.\n 5 files changed, 34 insertions(+), 12 deletions(-)\n\nantirez in commit 9f6e84f6:\n ACL LOG: implement a few basic tests.\n 1 file changed, 87 insertions(+)\n\nantirez in commit 82790e51:\n ACL LOG: also log ACL errors in the scripting/MULTI ctx.\n 2 files changed, 6 insertions(+), 2 deletions(-)\n\nantirez in commit 943008eb:\n ACL LOG: implement LOG RESET.\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\nantirez in commit e271a611:\n ACL LOG: group similar entries in a given time delta.\n 1 file changed, 58 insertions(+), 3 deletions(-)\n\nantirez in commit f1974d5d:\n ACL LOG: actually emit entries.\n 3 files changed, 34 insertions(+), 5 deletions(-)\n\nantirez in commit d9b153c9:\n ACL LOG: implement ACL LOG subcommadn skeleton.\n 1 file changed, 37 insertions(+)\n\nantirez in commit 577fc438:\n ACL LOG: data structures and initial functions.\n 5 files changed, 54 insertions(+), 5 deletions(-)\n\nLeo Murillo in commit f7a94526:\n Set ZSKIPLIST_MAXLEVEL to optimal value given 2^64 elements and p=0.25\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nWuYunlong in commit eecfa979:\n Fix lua related memory leak.\n 1 file changed, 1 insertion(+)\n\nWuYunlong in commit d2509811:\n Add tcl regression test in scripting.tcl to reproduce memory leak.\n 1 file changed, 5 insertions(+)\n\nYossi Gottlieb in commit 29d4a150:\n TLS: Fix missing initialization in redis-cli.\n 1 file changed, 9 insertions(+)\n\nOran Agra in commit ec0c61da:\n fix uninitialized info_cb var in module.c\n 1 file changed, 1 insertion(+)\n\nGuy Benoish in commit 6fe55c2f:\n ld2string should fail if string contains \\0 in the middle\n 5 files changed, 20 insertions(+), 11 deletions(-)\n\nantirez in commit bbce3ba9:\n Add more info in the unblockClientFromModule() function.\n 1 file changed, 7 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit 40295fb3:\n Modules: Fix blocked-client-related memory leak\n 3 files changed, 51 insertions(+), 6 deletions(-)\n\nantirez in commit 8e9d19bc:\n Change error message for #6775.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nVasyl Melnychuk in commit ba146d4c:\n Make error when submitting command in incorrect context more explicit\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nantirez in commit 721a39dd:\n Document I/O threads in redis.conf.\n 1 file changed, 46 insertions(+)\n\nantirez in commit 5be3a15a:\n Setting N I/O threads should mean N-1 additional + 1 main thread.\n 1 file changed, 25 insertions(+), 22 deletions(-)\n\nantirez in commit cbabf779:\n Simplify #6379 changes.\n 2 files changed, 4 insertions(+), 9 deletions(-)\n\nWuYunlong in commit 658749cc:\n Free allocated sds in pfdebugCommand() to avoid memory leak.\n 1 file changed, 1 insertion(+)\n\nWuYunlong in commit 47988c96:\n Fix potential memory leak of clusterLoadConfig().\n 1 file changed, 20 insertions(+), 5 deletions(-)\n\nWuYunlong in commit cc90f79b:\n Fix potential memory leak of rioWriteBulkStreamID().\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nantirez in commit ecd17e81:\n Jump to right label on AOF parsing error.\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\nantirez in commit 1927932b:\n Port PR #6110 to new connection object code.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nantirez in commit f2df5773:\n A few comments about main thread serving I/O as well.\n 1 file changed, 7 insertions(+), 1 deletion(-)\n\nzhaozhao.zz in commit b3ff8a4b:\n Threaded IO: use main thread to handle read work\n 1 file changed, 8 insertions(+), 1 deletion(-)\n\nzhaozhao.zz in commit b1f2c510:\n Threaded IO: use main thread to handle write work\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\nShooterIT in commit 7bbafc56:\n Rename rdb asynchronously\n 1 file changed, 7 insertions(+)\n\nLeo Murillo in commit c7f75266:\n Fix bug on KEYS command where pattern starts with * followed by \\x00 (null char).\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nJamie Scott in commit ed7ea13a:\n Update to directive in redis.conf (missing s)\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 3be77623:\n Free fakeclient argv on AOF error.\n 1 file changed, 11 insertions(+), 3 deletions(-)\n\nantirez in commit 15f6b748:\n Git ignore: ignore more files.\n 1 file changed, 2 insertions(+)\n\nGuy Benoish in commit 1b5bf40c:\n Blocking XREAD[GROUP] should always reply with valid data (or timeout)\n 3 files changed, 44 insertions(+), 10 deletions(-)\n\nJohn Sully in commit 954c20ed:\n Add support for incremental build with header files\n 2 files changed, 6 insertions(+), 1 deletion(-)\n\nWuYunlong in commit 11c3afd7:\n Fix petential cluster link error.\n 1 file changed, 4 insertions(+)\n\nYossi Gottlieb in commit b752e83d:\n Add REDISMODULE_CTX_FLAGS_MULTI_DIRTY.\n 2 files changed, 8 insertions(+)\n\nhwware in commit e16eb874:\n typo fix in acl.c\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nItamar Haber in commit 35ea9d23:\n Adjusts 'io_threads_num' max to 128\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 38729126:\n XCLAIM: Create the consumer only on successful claims.\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\nyz1509 in commit b9a15303:\n avoid sentinel changes promoted_slave to be its own replica.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 5e7e5e6b:\n Fix active expire division by zero.\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\nantirez in commit e61dde88:\n Fix duplicated CLIENT SETNAME reply.\n 1 file changed, 1 deletion(-)\n\nGuy Benoish in commit cddf1da2:\n Stream: Handle streamID-related edge cases\n 4 files changed, 54 insertions(+), 4 deletions(-)\n\nOran Agra in commit 52ea44e5:\n config.c adjust config limits and mutable\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\nantirez in commit 0f28ea16:\n Inline protocol: handle empty strings well.\n 1 file changed, 2 insertions(+), 6 deletions(-)\n\nantirez in commit 00e5fefe:\n Fix ip and missing mode in RM_GetClusterNodeInfo().\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\n================================================================================\nRedis 6.0 RC1   Released Thu Dec 19 09:58:24 CEST 2019\n================================================================================\n\nUpgrade urgency LOW: This is the first RC of Redis 6.\n\nIntroduction to the Redis 6 release\n===================================\n\nRedis 6 improves Redis in a number of key areas and is one of the largest\nRedis releases in the history of the project, so here we'll list only\nthe biggest features in this release:\n\n* The modules system now has a number of new APIs that allow module authors\n  to make things otherwise not possible in the past. It is possible to\n  store arbitrary module private data in RDB files, to hook on different\n  server events, capture and rewrite commands executions, block clients on\n  keys, and so forth. \n* The Redis active expire cycle was rewritten for much faster eviction of keys\n  that are already expired. Now the effort is tunable.\n* Redis now supports SSL on all channels.\n* ACL support, you can define users that can run only certain commands and/or\n  can only access only certain keys patterns.\n* Redis now supports a new protocol called RESP3, which returns more\n  semantical replies: new clients using this protocol can understand just\n  from the reply what type to return to the calling program.\n* There is server-side support for client-side caching of key values. This\n  feature is still experimental and will get more changes during the next\n  release candidates, but you can already test it and read about it here:\n  https://redis.io/topics/client-side-caching\n* Redis can now optionally use threads to handle I/O, allowing to serve\n  2 times as much operations per second in a single instance when\n  pipelining cannot be used.\n* Diskless replication is now supported even on replicas: a replica is now\n  able, under certain conditions the user can configure, to load the RDB\n  in the first synchronization directly from the socket to the memory.\n* Redis-benchmark now supports a Redis Cluster mode.\n* SRANDMEMBER and similar commands have a better distribution.\n* Redis-cli improvements.\n* Systemd support rewritten.\n* A Redis Cluster proxy was released here:\n  https://github.com/artix75/redis-cluster-proxy\n* A Disque module for Redis was released here:\n  https://github.com/antirez/disque-module\n\nThanks to all the users and developers who made this release possible.\nWe'll follow up with more RC releases, until the code looks production ready\nand we don't get reports of serious issues for a while.\n\nA special thank you for the amount of work put into this release\n(in decreasing number of commits, only listing contributors with more\nthan a single commit) by:\n\n   685  antirez\n    81  zhaozhao.zz\n    76  Oran Agra\n    51  artix\n    28  Madelyn Olson\n    27  Yossi Gottlieb\n    15  David Carlier\n    14  Guy Benoish\n    14  Guy Korland\n    13  Itamar Haber\n     9  Angus Pearson\n     8  WuYunlong\n     8  yongman\n     7  vattezhang\n     7  Chris Lamb\n     5  Dvir Volk\n     5  meir@redislabs.com\n     5  chendianqiang\n     5  John Sully\n     4  dejun.xdj\n     4  Daniel Dai\n     4  Johannes Truschnigg\n     4  swilly22\n     3  Bruce Merry\n     3  filipecosta90\n     3  youjiali1995\n     2  James Rouzier\n     2  Andrey Bugaevskiy\n     2  Brad Solomon\n     2  Hamid Alaei\n     2  Michael Chaten\n     2  Steve Webster\n     2  Wander Hillen\n     2  Weiliang Li\n     2  Yuan Zhou\n     2  charsyam\n     2  hujie\n     2  jem\n     2  shenlongxing\n     2  valentino\n     2  zhudacai 00228490\n     2  喜欢兰花山丘\n\nMigrating from 5.0 to 6.0\n=========================\n\nRedis 6.0 is mostly a strict superset of 5.0, you should not have any problem\nupgrading your application from 5.0 to 6.0. However this is a list of small\nnon-backward compatible changes introduced in the 6.0 release:\n\n* The SPOP <count> command no longer returns null when the set key does not\n  exist. Now it returns the empty set as it should and as happens when it is\n  called with a 0 argument. This is technically a fix, however it changes the\n  old behavior.\n\n--------------------------------------------------------------------------------\n\nCredits: For each release, a list of changes with the relative author is\nprovided. Where not specified the implementation and design is done by\nSalvatore Sanfilippo. Thanks to Redis Labs for making all this possible.\nAlso many thanks to all the other contributors and the amazing community\nwe have.\n\nCommit messages may contain additional credits.\n\nEnjoy,\nSalvatore\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/BUGS",
    "content": "Please check https://github.com/antirez/redis/issues\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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      https://groups.google.com/forum/m/#!forum/Redis-db\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 mailing list.\n\n  There is also an active community of Redis users at Stack Overflow:\n\n      http://stackoverflow.com/questions/tagged/redis\n\n# How to provide a patch for a new feature\n\n1. If it is a major feature or a semantical change, please don't start coding\nstraight away: if your feature is not a conceptual fit you'll lose a lot of\ntime writing the code without any reason. Start by posting in the mailing list\nand creating an issue at Github with the description of, exactly, what you want\nto accomplish and why. Use cases are important for features to be accepted.\nHere you'll see if there is consensus about your idea.\n\n2. If in step 1 you get an acknowledgment 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 ( https://help.github.com/articles/creating-a-pull-request/ )\n    e. Done :)\n\n3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait\nfor a *very* long time. However this is not lack of interest, as the project\ngets more and more users, we find ourselves in a constant need to prioritize\ncertain issues/PRs over others. If you think your issue/PR is very important\ntry to popularize it, have other users commenting and sharing their point of\nview and so forth. This helps.\n\n4. For minor fixes just open a pull request on Github.\n\nThanks!\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/COPYING",
    "content": "Copyright (c) 2006-2015, 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": "Chapter02/redis-6.0.3/INSTALL",
    "content": "See README\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 - We believe in code efficiency. Computers get faster and faster, yet we\n    believe that abusing computing capabilities is not wise: the amount of\n    operations you can do for a given amount of energy remains anyway a\n    significant parameter: it allows to do more with less computers and, at\n    the same time, having a smaller environmental impact. Similarly Redis is\n    able to \"scale down\" to smaller devices. It is perfectly usable in a\n    Raspberry Pi and other small ARM based computers. Faster code having\n    just the layers of abstractions that are really needed will also result,\n    often, in more predictable performances. We think likewise about memory\n    usage, one of the fundamental goals of the Redis project is to\n    incrementally build more and more memory efficient data structures, so that\n    problems that were not approachable in RAM in the past will be perfectly\n    fine to handle in the future.\n\n5 - 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\n6 - 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. Complexity is also a form of lock-in: code that is\n    very hard to understand cannot be modified by users in an independent way\n    regardless of the license. One of the main Redis goals is to remain\n    understandable, enough for a single programmer to have a clear idea of how\n    it works in detail just reading the source code for a couple of weeks.\n\n7 - Threading is not a silver bullet. Instead of making Redis threaded we\n    believe on the idea of an efficient (mostly) single threaded Redis core.\n    Multiple of such cores, that may run in the same computer or may run\n    in multiple computers, are abstracted away as a single big system by\n    higher order protocols and features: Redis Cluster and the upcoming\n    Redis Proxy are our main goals. A shared nothing approach is not just\n    much simpler (see the previous point in this document), is also optimal\n    in NUMA systems. In the specific case of Redis it allows for each instance\n    to have a more limited amount of data, making the Redis persist-by-fork\n    approach more sounding. In the future we may explore parallelism only for\n    I/O, which is the low hanging fruit: minimal complexity could provide an\n    improved single process experience.\n\n8 - 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\n    trade-offs to the user.\n\n9 - 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\n10 - All the above points are put together in what we call opportunistic\n     programming: trying to get the most for the user with minimal increases\n     in complexity (hanging fruits). Solve 95% of the problem with 5% of the\n     code when it is acceptable. Avoid a fixed schedule but follow the flow of\n     user requests, inspiration, Redis internal readiness for certain features\n     (sometimes many past changes reach a critical point making a previously\n     complex feature very easy to obtain).\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/README.md",
    "content": "This README is just a fast *quick start* document. You can find more detailed documentation at [redis.io](https://redis.io).\n\nWhat is Redis?\n--------------\n\nRedis is often referred as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.\n\nData structures implemented into Redis have a few special properties:\n\n* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that is also non-volatile.\n* Implementation of data structures stress on memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modeled using an high level programming language.\n* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, cluster, high availability.\n\nAnother good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations to work with complex data types like Lists, Sets, ordered data structures, and so forth.\n\nIf you want to know more, this is a list of selected starting points:\n\n* Introduction to Redis data types. http://redis.io/topics/data-types-intro\n* Try Redis directly inside your browser. http://try.redis.io\n* The full list of Redis commands. http://redis.io/commands\n* There is much more inside the Redis official documentation. http://redis.io/documentation\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, and both 32 bit\nand 64 bit systems.\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\nTo build with TLS support, you'll need OpenSSL development libraries (e.g.\nlibssl-dev on Debian/Ubuntu) and run:\n\n    % make BUILD_TLS=yes\n\nYou can run a 32 bit Redis binary using:\n\n    % make 32bit\n\nAfter building Redis, it is a good idea to test it using:\n\n    % make test\n\nIf TLS is built, running the tests with TLS enabled (you will need `tcl-tls`\ninstalled):\n\n    % ./utils/gen-test-certs.sh\n    % ./runtest --tls\n\n\nFixing build problems with dependencies or cached build options\n---------\n\nRedis has some dependencies which are included into the `deps` directory.\n`make` does not automatically rebuild dependencies even if something in\nthe source code of dependencies changes.\n\nWhen you update the source code with `git pull` or when code inside the\ndependencies tree is modified in any other way, make sure to use the following\ncommand in order to really clean everything and rebuild from scratch:\n\n    make distclean\n\nThis will clean: jemalloc, lua, hiredis, linenoise.\n\nAlso if you force certain build options like 32bit target, no C compiler\noptimizations (for debugging purposes), and other similar build time options,\nthose options are cached indefinitely until you issue a `make distclean`\ncommand.\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  `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 by passing parameters directly\nas options using the command line. Examples:\n\n    % ./redis-server --port 9999 --replicaof 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\nRunning Redis with TLS:\n------------------\n\nPlease consult the [TLS.md](TLS.md) file for more information on\nhow to use Redis with TLS.\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 at 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\n_Note_: `install_server.sh` will not work on Mac OSX; it is built for Linux only.\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][1] file included in the Redis\nsource distribution.\n\nPlease see the [CONTRIBUTING][2] file in this source distribution for more\ninformation.\n\n[1]: https://github.com/antirez/redis/blob/unstable/COPYING\n[2]: https://github.com/antirez/redis/blob/unstable/CONTRIBUTING\n\nRedis internals\n===\n\nIf you are reading this README you are likely in front of a Github page\nor you just untarred the Redis distribution tar ball. In both the cases\nyou are basically one step away from the source code, so here we explain\nthe Redis source code layout, what is in each file as a general idea, the\nmost important functions and structures inside the Redis server and so forth.\nWe keep all the discussion at a high level without digging into the details\nsince this document would be huge otherwise and our code base changes\ncontinuously, but a general idea should be a good starting point to\nunderstand more. Moreover most of the code is heavily commented and easy\nto follow.\n\nSource code layout\n---\n\nThe Redis root directory just contains this README, the Makefile which\ncalls the real Makefile inside the `src` directory and an example\nconfiguration for Redis and Sentinel. You can find a few shell\nscripts that are used in order to execute the Redis, Redis Cluster and\nRedis Sentinel unit tests, which are implemented inside the `tests`\ndirectory.\n\nInside the root are the following important directories:\n\n* `src`: contains the Redis implementation, written in C.\n* `tests`: contains the unit tests, implemented in Tcl.\n* `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `antirez/redis`.\n\nThere are a few more directories but they are not very important for our goals\nhere. We'll focus mostly on `src`, where the Redis implementation is contained,\nexploring what there is inside each file. The order in which files are\nexposed is the logical one to follow in order to disclose different layers\nof complexity incrementally.\n\nNote: lately Redis was refactored quite a bit. Function names and file\nnames have been changed, so you may find that this documentation reflects the\n`unstable` branch more closely. For instance in Redis 3.0 the `server.c`\nand `server.h` files were named `redis.c` and `redis.h`. However the overall\nstructure is the same. Keep in mind that all the new developments and pull\nrequests should be performed against the `unstable` branch.\n\nserver.h\n---\n\nThe simplest way to understand how a program works is to understand the\ndata structures it uses. So we'll start from the main header file of\nRedis, which is `server.h`.\n\nAll the server configuration and in general all the shared state is\ndefined in a global structure called `server`, of type `struct redisServer`.\nA few important fields in this structure are:\n\n* `server.db` is an array of Redis databases, where data is stored.\n* `server.commands` is the command table.\n* `server.clients` is a linked list of clients connected to the server.\n* `server.master` is a special client, the master, if the instance is a replica.\n\nThere are tons of other fields. Most fields are commented directly inside\nthe structure definition.\n\nAnother important Redis data structure is the one defining a client.\nIn the past it was called `redisClient`, now just `client`. The structure\nhas many fields, here we'll just show the main ones:\n\n    struct client {\n        int fd;\n        sds querybuf;\n        int argc;\n        robj **argv;\n        redisDb *db;\n        int flags;\n        list *reply;\n        char buf[PROTO_REPLY_CHUNK_BYTES];\n        ... many other fields ...\n    }\n\nThe client structure defines a *connected client*:\n\n* The `fd` field is the client socket file descriptor.\n* `argc` and `argv` are populated with the command the client is executing, so that functions implementing a given Redis command can read the arguments.\n* `querybuf` accumulates the requests from the client, which are parsed by the Redis server according to the Redis protocol and executed by calling the implementations of the commands the client is executing.\n* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writable.\n\nAs you can see in the client structure above, arguments in a command\nare described as `robj` structures. The following is the full `robj`\nstructure, which defines a *Redis object*:\n\n    typedef struct redisObject {\n        unsigned type:4;\n        unsigned encoding:4;\n        unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */\n        int refcount;\n        void *ptr;\n    } robj;\n\nBasically this structure can represent all the basic Redis data types like\nstrings, lists, sets, sorted sets and so forth. The interesting thing is that\nit has a `type` field, so that it is possible to know what type a given\nobject has, and a `refcount`, so that the same object can be referenced\nin multiple places without allocating it multiple times. Finally the `ptr`\nfield points to the actual representation of the object, which might vary\neven for the same type, depending on the `encoding` used.\n\nRedis objects are used extensively in the Redis internals, however in order\nto avoid the overhead of indirect accesses, recently in many places\nwe just use plain dynamic strings not wrapped inside a Redis object.\n\nserver.c\n---\n\nThis is the entry point of the Redis server, where the `main()` function\nis defined. The following are the most important steps in order to startup\nthe Redis server.\n\n* `initServerConfig()` setups the default values of the `server` structure.\n* `initServer()` allocates the data structures needed to operate, setup the listening socket, and so forth.\n* `aeMain()` starts the event loop which listens for new connections.\n\nThere are two special functions called periodically by the event loop:\n\n1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timedout clients.\n2. `beforeSleep()` is called every time the event loop fired, Redis served a few requests, and is returning back into the event loop.\n\nInside server.c you can find code that handles other vital things of the Redis server:\n\n* `call()` is used in order to call a given command in the context of a given client.\n* `activeExpireCycle()` handles eviciton of keys with a time to live set via the `EXPIRE` command.\n* `freeMemoryIfNeeded()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.\n* The global variable `redisCommandTable` defines all the Redis commands, specifying the name of the command, the function implementing the command, the number of arguments required, and other properties of each command.\n\nnetworking.c\n---\n\nThis file defines all the I/O functions with clients, masters and replicas\n(which in Redis are just special clients):\n\n* `createClient()` allocates and initializes a new client.\n* the `addReply*()` family of functions are used by commands implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.\n* `writeToClient()` transmits the data pending in the output buffers to the client and is called by the *writable event handler* `sendReplyToClient()`.\n* `readQueryFromClient()` is the *readable event handler* and accumulates data from read from the client into the query buffer.\n* `processInputBuffer()` is the entry point in order to parse the client query buffer according to the Redis protocol. Once commands are ready to be processed, it calls `processCommand()` which is defined inside `server.c` in order to actually execute the command.\n* `freeClient()` deallocates, disconnects and removes a client.\n\naof.c and rdb.c\n---\n\nAs you can guess from the names these files implement the RDB and AOF\npersistence for Redis. Redis uses a persistence model based on the `fork()`\nsystem call in order to create a thread with the same (shared) memory\ncontent of the main Redis thread. This secondary thread dumps the content\nof the memory on disk. This is used by `rdb.c` to create the snapshots\non disk and by `aof.c` in order to perform the AOF rewrite when the\nappend only file gets too big.\n\nThe implementation inside `aof.c` has additional functions in order to\nimplement an API that allows commands to append new commands into the AOF\nfile as clients execute them.\n\nThe `call()` function defined inside `server.c` is responsible to call\nthe functions that in turn will write the commands into the AOF.\n\ndb.c\n---\n\nCertain Redis commands operate on specific data types, others are general.\nExamples of generic commands are `DEL` and `EXPIRE`. They operate on keys\nand not on their values specifically. All those generic commands are\ndefined inside `db.c`.\n\nMoreover `db.c` implements an API in order to perform certain operations\non the Redis dataset without directly accessing the internal data structures.\n\nThe most important functions inside `db.c` which are used in many commands\nimplementations are the following:\n\n* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.\n* `dbAdd()` and its higher level counterpart `setKey()` create a new key in a Redis database.\n* `dbDelete()` removes a key and its associated value.\n* `emptyDb()` removes an entire single database or all the databases defined.\n\nThe rest of the file implements the generic commands exposed to the client.\n\nobject.c\n---\n\nThe `robj` structure defining Redis objects was already described. Inside\n`object.c` there are all the functions that operate with Redis objects at\na basic level, like functions to allocate new objects, handle the reference\ncounting and so forth. Notable functions inside this file:\n\n* `incrRefcount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.\n* `createObject()` allocates a new object. There are also specialized functions to allocate string objects having a specific content, like `createStringObjectFromLongLong()` and similar functions.\n\nThis file also implements the `OBJECT` command.\n\nreplication.c\n---\n\nThis is one of the most complex files inside Redis, it is recommended to\napproach it only after getting a bit familiar with the rest of the code base.\nIn this file there is the implementation of both the master and replica role\nof Redis.\n\nOne of the most important functions inside this file is `replicationFeedSlaves()` that writes commands to the clients representing replica instances connected\nto our master, so that the replicas can get the writes performed by the clients:\nthis way their data set will remain synchronized with the one in the master.\n\nThis file also implements both the `SYNC` and `PSYNC` commands that are\nused in order to perform the first synchronization between masters and\nreplicas, or to continue the replication after a disconnection.\n\nOther C files\n---\n\n* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client commands implementations for these data types.\n* `ae.c` implements the Redis event loop, it's a self contained library which is simple to read and understand.\n* `sds.c` is the Redis string library, check http://github.com/antirez/sds for more information.\n* `anet.c` is a library to use POSIX networking in a simpler way compared to the raw interface exposed by the kernel.\n* `dict.c` is an implementation of a non-blocking hash table which rehashes incrementally.\n* `scripting.c` implements Lua scripting. It is completely self contained from the rest of the Redis implementation and is simple enough to understand if you are familar with the Lua API.\n* `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3].\n\n[3]: http://redis.io/topics/cluster-spec\n\nAnatomy of a Redis command\n---\n\nAll the Redis commands are defined in the following way:\n\n    void foobarCommand(client *c) {\n        printf(\"%s\",c->argv[1]->ptr); /* Do something with the argument. */\n        addReply(c,shared.ok); /* Reply something to the client. */\n    }\n\nThe command is then referenced inside `server.c` in the command table:\n\n    {\"foobar\",foobarCommand,2,\"rtF\",0,NULL,0,0,0,0,0},\n\nIn the above example `2` is the number of arguments the command takes,\nwhile `\"rtF\"` are the command flags, as documented in the command table\ntop comment inside `server.c`.\n\nAfter the command operates in some way, it returns a reply to the client,\nusually using `addReply()` or a similar function defined inside `networking.c`.\n\nThere are tons of commands implementations inside the Redis source code\nthat can serve as examples of actual commands implementations. To write\na few toy commands can be a good exercise to familiarize with the code base.\n\nThere are also many other files not described here, but it is useless to\ncover everything. We want to just help you with the first steps.\nEventually you'll find your way inside the Redis code base :-)\n\nEnjoy!\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/TLS.md",
    "content": "TLS Support\n===========\n\nGetting Started\n---------------\n\n### Building\n\nTo build with TLS support you'll need OpenSSL development libraries (e.g.\nlibssl-dev on Debian/Ubuntu).\n\nRun `make BUILD_TLS=yes`.\n\n### Tests\n\nTo run Redis test suite with TLS, you'll need TLS support for TCL (i.e.\n`tcl-tls` package on Debian/Ubuntu).\n\n1. Run `./utils/gen-test-certs.sh` to generate a root CA and a server\n   certificate.\n\n2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis\n   Cluster tests in TLS mode.\n\n### Running manually\n\nTo manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was\ninvoked so sample certificates/keys are available):\n\n    ./src/redis-server --tls-port 6379 --port 0 \\\n        --tls-cert-file ./tests/tls/redis.crt \\\n        --tls-key-file ./tests/tls/redis.key \\\n        --tls-ca-cert-file ./tests/tls/ca.crt\n\nTo connect to this Redis server with `redis-cli`:\n\n    ./src/redis-cli --tls \\\n        --cert ./tests/tls/redis.crt \\\n        --key ./tests/tls/redis.key \\\n        --cacert ./tests/tls/ca.crt\n\nThis will disable TCP and enable TLS on port 6379. It's also possible to have\nboth TCP and TLS available, but you'll need to assign different ports.\n\nTo make a Replica connect to the master using TLS, use `--tls-replication yes`,\nand to make Redis Cluster use TLS across nodes use `--tls-cluster yes`.\n\nConnections\n-----------\n\nAll socket operations now go through a connection abstraction layer that hides\nI/O and read/write event handling from the caller.\n\n**Multi-threading I/O is not currently supported for TLS**, as a TLS connection\nneeds to do its own manipulation of AE events which is not thread safe. The\nsolution is probably to manage independent AE loops for I/O threads and longer\nterm association of connections with threads. This may potentially improve\noverall performance as well.\n\nSync IO for TLS is currently implemented in a hackish way, i.e. making the\nsocket blocking and configuring socket-level timeout.  This means the timeout\nvalue may not be so accurate, and there would be a lot of syscall overhead.\nHowever I believe that getting rid of syncio completely in favor of pure async\nwork is probably a better move than trying to fix that. For replication it would\nprobably not be so hard. For cluster keys migration it might be more difficult,\nbut there are probably other good reasons to improve that part anyway.\n\nTo-Do List\n----------\n\n- [ ] Add session caching support. Check if/how it's handled by clients to\n  assess how useful/important it is.\n- [ ] redis-benchmark support. The current implementation is a mix of using\n  hiredis for parsing and basic networking (establishing connections), but\n  directly manipulating sockets for most actions. This will need to be cleaned\n  up for proper TLS support. The best approach is probably to migrate to hiredis\n  async mode.\n- [ ] redis-cli `--slave` and `--rdb` support.\n\nMulti-port\n----------\n\nConsider the implications of allowing TLS to be configured on a separate port,\nmaking Redis listening on multiple ports:\n\n1. Startup banner port notification\n2. Proctitle\n3. How slaves announce themselves\n4. Cluster bus port calculation\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\nifeq ($(BUILD_TLS),yes)\n    HIREDIS_MAKE_FLAGS = USE_SSL=1\nendif\n\nhiredis: .make-prerequisites\n\t@printf '%b %b\\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)\n\tcd hiredis && $(MAKE) static $(HIREDIS_MAKE_FLAGS)\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 -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' $(CFLAGS)\nLUA_LDFLAGS+= $(LDFLAGS)\n# lua's Makefile defines AR=\"ar rcu\", which is unusual, and makes it more\n# challenging to cross-compile lua (and redis).  These defines make it easier\n# to fit redis into cross-compilation environments, which typically set AR.\nAR=ar\nARFLAGS=rcu\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)\" AR=\"$(AR) $(ARFLAGS)\"\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-version=5.1.0-0-g0 --with-lg-quantum=3 --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": "Chapter02/redis-6.0.3/deps/README.md",
    "content": "This directory contains all Redis dependencies, except for the libc that\nshould be provided by the operating system.\n\n* **Jemalloc** is our memory allocator, used as replacement for libc malloc on Linux by default. It has good performances and excellent fragmentation behavior. This component is upgraded from time to time.\n* **hiredis** is the official C client library for Redis. It is used by redis-cli, redis-benchmark and Redis Sentinel. It is part of the Redis official ecosystem but is developed externally from the Redis repository, so we just upgrade it as needed.\n* **linenoise** is a readline replacement. It is developed by the same authors of Redis but is managed as a separated project and updated as needed.\n* **lua** is Lua 5.1 with minor changes for security and additional libraries.\n\nHow to upgrade the above dependencies\n===\n\nJemalloc\n---\n\nJemalloc is modified with changes that allow us to implement the Redis\nactive defragmentation logic. However this feature of Redis is not mandatory\nand Redis is able to understand if the Jemalloc version it is compiled\nagainst supports such Redis-specific modifications. So in theory, if you\nare not interested in the active defragmentation, you can replace Jemalloc\njust following tose steps:\n\n1. Remove the jemalloc directory.\n2. Substitute it with the new jemalloc source tree.\n3. Edit the Makefile localted in the same directory as the README you are\n   reading, and change the --with-version in the Jemalloc configure script\n   options with the version you are using. This is required because otherwise\n   Jemalloc configuration script is broken and will not work nested in another\n   git repository.\n\nHowever note that we change Jemalloc settings via the `configure` script of Jemalloc using the `--with-lg-quantum` option, setting it to the value of 3 instead of 4. This provides us with more size classes that better suit the Redis data structures, in order to gain memory efficiency.\n\nIf you want to upgrade Jemalloc while also providing support for\nactive defragmentation, in addition to the above steps you need to perform\nthe following additional steps:\n\n5. In Jemalloc three, file `include/jemalloc/jemalloc_macros.h.in`, make sure\n   to add `#define JEMALLOC_FRAG_HINT`.\n6. Implement the function `je_get_defrag_hint()` inside `src/jemalloc.c`. You\n   can see how it is implemented in the current Jemalloc source tree shipped\n   with Redis, and rewrite it according to the new Jemalloc internals, if they\n   changed, otherwise you could just copy the old implementation if you are\n   upgrading just to a similar version of Jemalloc.\n\nHiredis\n---\n\nHiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:\n\n1. Check with diff if hiredis API changed and what impact it could have in Redis.\n2. Make sure thet the SDS library inside Hiredis and inside Redis are compatible.\n3. After the upgrade, run the Redis Sentinel test.\n4. Check manually that redis-cli and redis-benchmark behave as expecteed, since we have no tests for CLI utilities currently.\n\nLinenoise\n---\n\nLinenoise is rarely upgraded as needed. The upgrade process is trivial since\nRedis uses a non modified version of linenoise, so to upgrade just do the\nfollowing:\n\n1. Remove the linenoise directory.\n2. Substitute it with the new linenoise source tree.\n\nLua\n---\n\nWe use Lua 5.1 and no upgrade is planned currently, since we don't want to break\nLua scripts for new Lua features: in the context of Redis Lua scripts the\ncapabilities of 5.1 are usually more than enough, the release is rock solid,\nand we definitely don't want to break old scripts.\n\nSo upgrading of Lua is up to the Redis project maintainers and should be a\nmanual procedure performed by taking a diff between the different versions.\n\nCurrently we have at least the following differences between official Lua 5.1\nand our version:\n\n1. Makefile is modified to allow a different compiler than GCC.\n2. We have the implementation source code, and directly link to the following external libraries: `lua_cjson.o`, `lua_struct.o`, `lua_cmsgpack.o` and `lua_bit.o`.\n3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order toa void direct bytecode execution.\n\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/.gitignore",
    "content": "/hiredis-test\n/examples/hiredis-example*\n/*.o\n/*.so\n/*.dylib\n/*.a\n/*.pc\n*.dSYM\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/.travis.yml",
    "content": "language: c\nsudo: false\ncompiler:\n  - gcc\n  - clang\n\nos:\n  - linux\n  - osx\n\nbranches:\n  only:\n    - staging\n    - trying\n    - master\n\nbefore_script:\n    - if [ \"$TRAVIS_OS_NAME\" == \"osx\" ] ; then brew update; brew install redis; fi\n\naddons:\n  apt:\n    packages:\n    - libc6-dbg\n    - libc6-dev\n    - libc6:i386\n    - libc6-dev-i386\n    - libc6-dbg:i386\n    - gcc-multilib\n    - g++-multilib\n    - valgrind\n\nenv:\n  - BITS=\"32\"\n  - BITS=\"64\"\n\nscript:\n  - EXTRA_CMAKE_OPTS=\"-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON\";\n    if [ \"$TRAVIS_OS_NAME\" == \"osx\" ]; then\n      if [ \"$BITS\" == \"32\" ]; then\n        CFLAGS=\"-m32 -Werror\";\n        CXXFLAGS=\"-m32 -Werror\";\n        LDFLAGS=\"-m32\";\n        EXTRA_CMAKE_OPTS=;\n      else\n        CFLAGS=\"-Werror\";\n        CXXFLAGS=\"-Werror\";\n      fi;\n    else\n      TEST_PREFIX=\"valgrind --track-origins=yes --leak-check=full\";\n      if [ \"$BITS\" == \"32\" ]; then\n        CFLAGS=\"-m32 -Werror\";\n        CXXFLAGS=\"-m32 -Werror\";\n        LDFLAGS=\"-m32\";\n        EXTRA_CMAKE_OPTS=;\n      else\n        CFLAGS=\"-Werror\";\n        CXXFLAGS=\"-Werror\";\n      fi;\n    fi;\n    export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS\n  - mkdir build/ && cd build/\n  - cmake .. ${EXTRA_CMAKE_OPTS}\n  - make VERBOSE=1\n  - ctest -V\n\nmatrix:\n  include:\n    # Windows MinGW cross compile on Linux\n    - os: linux\n      dist: xenial\n      compiler: mingw\n      addons:\n        apt:\n          packages:\n            - ninja-build\n            - gcc-mingw-w64-x86-64\n            - g++-mingw-w64-x86-64\n      script:\n        - mkdir build && cd build\n        - CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on\n        - ninja -v\n\n    # Windows MSVC 2017\n    - os: windows\n      compiler: msvc\n      env:\n        - MATRIX_EVAL=\"CC=cl.exe && CXX=cl.exe\"\n      before_install:\n        - eval \"${MATRIX_EVAL}\"\n      install:\n        - choco install ninja\n      script:\n        - mkdir build && cd build\n        - cmd.exe /C '\"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Auxiliary\\Build\\vcvarsall.bat\" amd64 &&\n          cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release &&\n          ninja -v'\n        - ctest -V\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/CHANGELOG.md",
    "content": "### 1.0.0 (unreleased)\n\n**BREAKING CHANGES**:\n\n* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now\n  protocol errors. This is consistent with the RESP specification. On 32-bit\n  platforms, the upper bound is lowered to `SIZE_MAX`.\n\n* Change `redisReply.len` to `size_t`, as it denotes the the size of a string\n\n  User code should compare this to `size_t` values as well.  If it was used to\n  compare to other values, casting might be necessary or can be removed, if\n  casting was applied before.\n\n### 0.x.x (unreleased)\n**BREAKING CHANGES**:\n\n* Change `redisReply.len` to `size_t`, as it denotes the the size of a string\n\nUser code should compare this to `size_t` values as well.\nIf it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.\n\n* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.\n\n### 0.14.0 (2018-09-25)\n\n* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])\n* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])\n* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])\n* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])\n* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])\n* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])\n* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])\n* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])\n* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])\n* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])\n* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])\n* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])\n* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])\n* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])\n* Fix libevent leak (zfz [515228])\n* Clean up GCC warning (Ichito Nagata [2ec774])\n* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])\n* Solaris compilation fix (Donald Whyte [41b07d])\n* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])\n* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])\n* libuv use after free fix (Paul Scott [cbb956])\n* Properly close socket fd on reconnect attempt (WSL [64d1ec])\n* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])\n* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])\n* Update libevent (Chris Xin [386802])\n* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])\n* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])\n* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])\n* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])\n* Compatibility fix for strerror_r (Tom Lee [bb1747])\n* Properly detect integer parse/overflow errors (Justin Brewer [93421f])\n* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])\n* Catch a buffer overflow when formatting the error message\n* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13\n* Fix warnings, when compiled with -Wshadow\n* Make hiredis compile in Cygwin on Windows, now CI-tested\n* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now\n  protocol errors. This is consistent with the RESP specification. On 32-bit\n  platforms, the upper bound is lowered to `SIZE_MAX`.\n\n* Remove backwards compatibility macro's\n\nThis removes the following old function aliases, use the new name now:\n\n| Old                         | New                    |\n| --------------------------- | ---------------------- |\n| redisReplyReaderCreate      | redisReaderCreate      |\n| redisReplyReaderCreate      | redisReaderCreate      |\n| redisReplyReaderFree        | redisReaderFree        |\n| redisReplyReaderFeed        | redisReaderFeed        |\n| redisReplyReaderGetReply    | redisReaderGetReply    |\n| redisReplyReaderSetPrivdata | redisReaderSetPrivdata |\n| redisReplyReaderGetObject   | redisReaderGetObject   |\n| redisReplyReaderGetError    | redisReaderGetError    |\n\n* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS`\n\nPreviously it broke some builds for people that had `DEBUG` set to some arbitrary value,\ndue to debugging other software.\nBy renaming we avoid unintentional name clashes.\n\nSimply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again.\n\n### 0.13.3 (2015-09-16)\n\n* Revert \"Clear `REDIS_CONNECTED` flag when connection is closed\".\n* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni)\n\n\nIf the `REDIS_CONNECTED` flag is cleared,\nthe async onDisconnect callback function will never be called.\nThis causes problems as the disconnect is never reported back to the user.\n\n### 0.13.2 (2015-08-25)\n\n* Prevent crash on pending replies in async code (Thanks, @switch-st)\n* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs)\n* Add MacOS X addapter (Thanks, @dizzus)\n* Add Qt adapter (Thanks, Pietro Cerutti)\n* Add Ivykis adapter (Thanks, Gergely Nagy)\n\nAll adapters are provided as is and are only tested where possible.\n\n### 0.13.1 (2015-05-03)\n\nThis is a bug fix release.\nThe new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code.\nAnother commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects.\nOther non-C99 code can now use hiredis as usual again.\nSorry for the inconvenience.\n\n* Fix memory leak in async reply handling (Salvatore Sanfilippo)\n* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa)\n\n### 0.13.0 (2015-04-16)\n\nThis release adds a minimal Windows compatibility layer.\nThe parser, standalone since v0.12.0, can now be compiled on Windows\n(and thus used in other client libraries as well)\n\n* Windows compatibility layer for parser code (tzickel)\n* Properly escape data printed to PKGCONF file (Dan Skorupski)\n* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff)\n* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra)\n\n### 0.12.1 (2015-01-26)\n\n* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location\n* Fix `make test` as 32 bit build on 64 bit platform\n\n### 0.12.0 (2015-01-22)\n\n* Add optional KeepAlive support\n\n* Try again on EINTR errors\n\n* Add libuv adapter\n\n* Add IPv6 support\n\n* Remove possibility of multiple close on same fd\n\n* Add ability to bind source address on connect\n\n* Add redisConnectFd() and redisFreeKeepFd()\n\n* Fix getaddrinfo() memory leak\n\n* Free string if it is unused (fixes memory leak)\n\n* Improve redisAppendCommandArgv performance 2.5x\n\n* Add support for SO_REUSEADDR\n\n* Fix redisvFormatCommand format parsing\n\n* Add GLib 2.0 adapter\n\n* Refactor reading code into read.c\n\n* Fix errno error buffers to not clobber errors\n\n* Generate pkgconf during build\n\n* Silence _BSD_SOURCE warnings\n\n* Improve digit counting for multibulk creation\n\n\n### 0.11.0\n\n* Increase the maximum multi-bulk reply depth to 7.\n\n* Increase the read buffer size from 2k to 16k.\n\n* Use poll(2) instead of select(2) to support large fds (>= 1024).\n\n### 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": "Chapter02/redis-6.0.3/deps/hiredis/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)\nINCLUDE(GNUInstallDirs)\nPROJECT(hiredis)\n\nOPTION(ENABLE_SSL \"Build hiredis_ssl for SSL support\" OFF)\n\nMACRO(getVersionBit name)\n  SET(VERSION_REGEX \"^#define ${name} (.+)$\")\n  FILE(STRINGS \"${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h\"\n    VERSION_BIT REGEX ${VERSION_REGEX})\n  STRING(REGEX REPLACE ${VERSION_REGEX} \"\\\\1\" ${name} \"${VERSION_BIT}\")\nENDMACRO(getVersionBit)\n\ngetVersionBit(HIREDIS_MAJOR)\ngetVersionBit(HIREDIS_MINOR)\ngetVersionBit(HIREDIS_PATCH)\ngetVersionBit(HIREDIS_SONAME)\nSET(VERSION \"${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}\")\nMESSAGE(\"Detected version: ${VERSION}\")\n\nPROJECT(hiredis VERSION \"${VERSION}\")\n\nSET(ENABLE_EXAMPLES OFF CACHE BOOL \"Enable building hiredis examples\")\n\nADD_LIBRARY(hiredis SHARED\n    async.c\n    dict.c\n    hiredis.c\n    net.c\n    read.c\n    sds.c\n    sockcompat.c)\n\nSET_TARGET_PROPERTIES(hiredis\n    PROPERTIES\n    VERSION \"${HIREDIS_SONAME}\")\nIF(WIN32 OR MINGW)\n    TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)\nENDIF()\nTARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)\n\nCONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)\n\nINSTALL(TARGETS hiredis\n    DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n\nINSTALL(FILES hiredis.h read.h sds.h async.h\n    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)\n    \nINSTALL(DIRECTORY adapters\n    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)\n    \nINSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc\n    DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)\n\nIF(ENABLE_SSL)\n    IF (NOT OPENSSL_ROOT_DIR)\n        IF (APPLE)\n            SET(OPENSSL_ROOT_DIR \"/usr/local/opt/openssl\")\n        ENDIF()\n    ENDIF()\n    FIND_PACKAGE(OpenSSL REQUIRED)\n    ADD_LIBRARY(hiredis_ssl SHARED\n        ssl.c)\n    TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE \"${OPENSSL_INCLUDE_DIR}\")\n    TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})\n    CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)\n\n    INSTALL(TARGETS hiredis_ssl\n        DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n\n    INSTALL(FILES hiredis_ssl.h\n        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)\n    \n    INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc\n        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)\nENDIF()\n\nIF(NOT (WIN32 OR MINGW))\n    ENABLE_TESTING()\n    ADD_EXECUTABLE(hiredis-test test.c)\n    TARGET_LINK_LIBRARIES(hiredis-test hiredis)\n    ADD_TEST(NAME hiredis-test\n        COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)\nENDIF()\n\n# Add examples\nIF(ENABLE_EXAMPLES)\n  ADD_SUBDIRECTORY(examples)\nENDIF(ENABLE_EXAMPLES)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 read.o sockcompat.o\nSSL_OBJ=ssl.o\nEXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib\nifeq ($(USE_SSL),1)\nEXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl\nendif\nTESTS=hiredis-test\nLIBNAME=libhiredis\nSSL_LIBNAME=libhiredis_ssl\nPKGCONFNAME=hiredis.pc\nSSL_PKGCONFNAME=hiredis_ssl.pc\n\nHIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')\nHIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')\nHIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}')\nHIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}')\n\n# Installation related variables and target\nPREFIX?=/usr/local\nINCLUDE_PATH?=include/hiredis\nLIBRARY_PATH?=lib\nPKGCONF_PATH?=pkgconfig\nINSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH)\nINSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)\nINSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH)\n\n# redis-server configuration used for testing\nREDIS_PORT=56379\nREDIS_SERVER=redis-server\ndefine REDIS_TEST_CONFIG\n\tdaemonize yes\n\tpidfile /tmp/hiredis-test-redis.pid\n\tport $(REDIS_PORT)\n\tbind 127.0.0.1\n\tunixsocket /tmp/hiredis-test-redis.sock\nendef\nexport REDIS_TEST_CONFIG\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')\nCXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')\nOPTIMIZATION?=-O3\nWARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers\nDEBUG_FLAGS?= -g -ggdb\nREAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)\nREAL_LDFLAGS=$(LDFLAGS)\n\nDYLIBSUFFIX=so\nSTLIBSUFFIX=a\nDYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)\nDYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)\nDYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)\nSSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)\nDYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)\nSTLIBNAME=$(LIBNAME).$(STLIBSUFFIX)\nSSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)\nSTLIB_MAKE_CMD=$(AR) rcs\n\n# Platform-specific overrides\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\nUSE_SSL?=0\n\n# This is required for test.c only\nifeq ($(USE_SSL),1)\n  CFLAGS+=-DHIREDIS_TEST_SSL\nendif\n\nifeq ($(uname_S),Linux)\n  SSL_LDFLAGS=-lssl -lcrypto\nelse\n  OPENSSL_PREFIX?=/usr/local/opt/openssl\n  CFLAGS+=-I$(OPENSSL_PREFIX)/include\n  SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto\nendif\n\nifeq ($(uname_S),SunOS)\n  REAL_LDFLAGS+= -ldl -lnsl -lsocket\n  DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)\nendif\nifeq ($(uname_S),Darwin)\n  DYLIBSUFFIX=dylib\n  DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)\n  DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)\nendif\n\nall: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)\nifeq ($(USE_SSL),1)\nall: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)\nendif\n\n# Deps (use make dep to generate this)\nasync.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h\ndict.o: dict.c fmacros.h dict.h\nhiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h win32.h\nnet.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h\nread.o: read.c fmacros.h read.h sds.h\nsds.o: sds.c sds.h\nsockcompat.o: sockcompat.c sockcompat.h\nssl.o: ssl.c hiredis.h\ntest.o: test.c fmacros.h hiredis.h read.h sds.h\n\n$(DYLIBNAME): $(OBJ)\n\t$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)\n\n$(STLIBNAME): $(OBJ)\n\t$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)\n\n$(SSL_DYLIBNAME): $(SSL_OBJ)\n\t$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)\n\n$(SSL_STLIBNAME): $(SSL_OBJ)\n\t$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)\n\ndynamic: $(DYLIBNAME)\nstatic: $(STLIBNAME)\nifeq ($(USE_SSL),1)\ndynamic: $(SSL_DYLIBNAME)\nstatic: $(SSL_STLIBNAME)\nendif\n\n# Binaries:\nhiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)\n\nhiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)\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: examples/example-ae.c adapters/ae.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)\nendif\n\nifndef LIBUV_DIR\nhiredis-example-libuv:\n\t@echo \"Please specify LIBUV_DIR (e.g. ../libuv/)\"\n\t@false\nelse\nhiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS)\nendif\n\nifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),)\nhiredis-example-qt:\n\t@echo \"Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR\"\n\t@false\nelse\nhiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME)\n\t$(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \\\n\t    $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore\n\t$(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \\\n\t    $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore\n\t$(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore\nendif\n\nhiredis-example: examples/example.c $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)\n\nexamples: $(EXAMPLES)\n\nTEST_LIBS = $(STLIBNAME)\nifeq ($(USE_SSL),1)\n    TEST_LIBS += $(SSL_STLIBNAME) -lssl -lcrypto -lpthread\nendif\nhiredis-test: test.o $(TEST_LIBS)\n\nhiredis-%: %.o $(STLIBNAME)\n\t$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)\n\ntest: hiredis-test\n\t./hiredis-test\n\ncheck: hiredis-test\n\tTEST_SSL=$(USE_SSL) ./test.sh\n\n.c.o:\n\t$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<\n\nclean:\n\trm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov\n\ndep:\n\t$(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c\n\nINSTALL?= cp -pPR\n\n$(PKGCONFNAME): hiredis.h\n\t@echo \"Generating $@ for pkgconfig...\"\n\t@echo prefix=$(PREFIX) > $@\n\t@echo exec_prefix=\\$${prefix} >> $@\n\t@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@\n\t@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@\n\t@echo >> $@\n\t@echo Name: hiredis >> $@\n\t@echo Description: Minimalistic C client library for Redis. >> $@\n\t@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@\n\t@echo Libs: -L\\$${libdir} -lhiredis >> $@\n\t@echo Cflags: -I\\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@\n\n$(SSL_PKGCONFNAME): hiredis.h\n\t@echo \"Generating $@ for pkgconfig...\"\n\t@echo prefix=$(PREFIX) > $@\n\t@echo exec_prefix=\\$${prefix} >> $@\n\t@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@\n\t@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@\n\t@echo >> $@\n\t@echo Name: hiredis_ssl >> $@\n\t@echo Description: SSL Support for hiredis. >> $@\n\t@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@\n\t@echo Requires: hiredis >> $@\n\t@echo Libs: -L\\$${libdir} -lhiredis_ssl >> $@\n\t@echo Libs.private: -lssl -lcrypto >> $@\n\ninstall: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)\n\tmkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)\n\t$(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)\n\t$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters\n\t$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)\n\tcd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)\n\t$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)\n\tmkdir -p $(INSTALL_PKGCONF_PATH)\n\t$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_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\n32bit-vars:\n\t$(eval CFLAGS=-m32)\n\t$(eval 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 32bit-vars gprof gcov noopt\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/README.md",
    "content": "[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)\n\n**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**\n\n# 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 a 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 to `1.0.0`\n\nVersion 1.0.0 marks a stable release of hiredis.\nIt includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.\nIt also bundles the updated `sds` library, to sync up with upstream and Redis.\nFor most applications a recompile against the new hiredis should be enough.\nFor code changes see the [Changelog](CHANGELOG.md).\n\n## Upgrading from `<0.9.0`\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```c\nredisContext *redisConnect(const char *ip, int port);\nvoid *redisCommand(redisContext *c, const char *format, ...);\nvoid freeReplyObject(void *reply);\n```\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 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```c\nredisContext *c = redisConnect(\"127.0.0.1\", 6379);\nif (c == NULL || c->err) {\n    if (c) {\n        printf(\"Error: %s\\n\", c->errstr);\n        // handle error\n    } else {\n        printf(\"Can't allocate redis context\\n\");\n    }\n}\n```\n\n*Note: A `redisContext` is not thread-safe.*\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```c\nreply = redisCommand(context, \"SET foo bar\");\n```\n\nThe specifier `%s` interpolates a string in the command, and uses `strlen` to\ndetermine the length of the string:\n```c\nreply = 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```c\nreply = redisCommand(context, \"SET foo %b\", value, (size_t) 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```c\nreply = redisCommand(context, \"SET key:%s %s\", myid, value);\n```\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-reply 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) frees 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```c\nvoid redisFree(redisContext *c);\n```\nThis function immediately closes the socket and then frees 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```c\nvoid *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```c\nvoid redisAppendCommand(redisContext *c, const char *format, ...);\nvoid 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```c\nredisReply *reply;\nredisAppendCommand(context,\"SET foo bar\");\nredisAppendCommand(context,\"GET foo\");\nredisGetReply(context,&reply); // reply for SET\nfreeReplyObject(reply);\nredisGetReply(context,&reply); // reply for GET\nfreeReplyObject(reply);\n```\nThis API can also be used to implement a blocking subscriber:\n```c\nreply = redisCommand(context,\"SUBSCRIBE foo\");\nfreeReplyObject(reply);\nwhile(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*Note: A `redisAsyncContext` is not thread-safe.*\n\n```c\nredisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\nif (c->err) {\n    printf(\"Error: %s\\n\", c->errstr);\n    // handle error\n}\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```c\nvoid(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 freed 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```c\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\n```\n`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.\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```c\nvoid(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```c\nint redisAsyncCommand(\n  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n  const char *format, ...);\nint 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 freed. When the callback\nfor a command is non-`NULL`, the memory is freed 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```c\nvoid 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 freed.\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```c\nredisReader *redisReaderCreate(void);\nvoid redisReaderFree(redisReader *reader);\nint redisReaderFeed(redisReader *reader, const char *buf, size_t len);\nint 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\nThe parser limits the level of nesting for multi bulk payloads to 7. If the\nmulti bulk nesting level is higher than this, the parser returns an 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\nKiB 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```c\ncontext->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.\nHiredis is currently maintained by Matt Stancliff (matt at genges dot com) and\nJan-Erik Rediger (janerik at fnordig dot com)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/hiredis/adapters/glib.h",
    "content": "#ifndef __HIREDIS_GLIB_H__\n#define __HIREDIS_GLIB_H__\n\n#include <glib.h>\n\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct\n{\n    GSource source;\n    redisAsyncContext *ac;\n    GPollFD poll_fd;\n} RedisSource;\n\nstatic void\nredis_source_add_read (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events |= G_IO_IN;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_del_read (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events &= ~G_IO_IN;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_add_write (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events |= G_IO_OUT;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_del_write (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events &= ~G_IO_OUT;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_cleanup (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n\n    g_return_if_fail(source);\n\n    redis_source_del_read(source);\n    redis_source_del_write(source);\n    /*\n     * It is not our responsibility to remove ourself from the\n     * current main loop. However, we will remove the GPollFD.\n     */\n    if (source->poll_fd.fd >= 0) {\n        g_source_remove_poll((GSource *)data, &source->poll_fd);\n        source->poll_fd.fd = -1;\n    }\n}\n\nstatic gboolean\nredis_source_prepare (GSource *source,\n                      gint    *timeout_)\n{\n    RedisSource *redis = (RedisSource *)source;\n    *timeout_ = -1;\n    return !!(redis->poll_fd.events & redis->poll_fd.revents);\n}\n\nstatic gboolean\nredis_source_check (GSource *source)\n{\n    RedisSource *redis = (RedisSource *)source;\n    return !!(redis->poll_fd.events & redis->poll_fd.revents);\n}\n\nstatic gboolean\nredis_source_dispatch (GSource      *source,\n                       GSourceFunc   callback,\n                       gpointer      user_data)\n{\n    RedisSource *redis = (RedisSource *)source;\n\n    if ((redis->poll_fd.revents & G_IO_OUT)) {\n        redisAsyncHandleWrite(redis->ac);\n        redis->poll_fd.revents &= ~G_IO_OUT;\n    }\n\n    if ((redis->poll_fd.revents & G_IO_IN)) {\n        redisAsyncHandleRead(redis->ac);\n        redis->poll_fd.revents &= ~G_IO_IN;\n    }\n\n    if (callback) {\n        return callback(user_data);\n    }\n\n    return TRUE;\n}\n\nstatic void\nredis_source_finalize (GSource *source)\n{\n    RedisSource *redis = (RedisSource *)source;\n\n    if (redis->poll_fd.fd >= 0) {\n        g_source_remove_poll(source, &redis->poll_fd);\n        redis->poll_fd.fd = -1;\n    }\n}\n\nstatic GSource *\nredis_source_new (redisAsyncContext *ac)\n{\n    static GSourceFuncs source_funcs = {\n        .prepare  = redis_source_prepare,\n        .check     = redis_source_check,\n        .dispatch = redis_source_dispatch,\n        .finalize = redis_source_finalize,\n    };\n    redisContext *c = &ac->c;\n    RedisSource *source;\n\n    g_return_val_if_fail(ac != NULL, NULL);\n\n    source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);\n    source->ac = ac;\n    source->poll_fd.fd = c->fd;\n    source->poll_fd.events = 0;\n    source->poll_fd.revents = 0;\n    g_source_add_poll((GSource *)source, &source->poll_fd);\n\n    ac->ev.addRead = redis_source_add_read;\n    ac->ev.delRead = redis_source_del_read;\n    ac->ev.addWrite = redis_source_add_write;\n    ac->ev.delWrite = redis_source_del_write;\n    ac->ev.cleanup = redis_source_cleanup;\n    ac->ev.data = source;\n\n    return (GSource *)source;\n}\n\n#endif /* __HIREDIS_GLIB_H__ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/adapters/ivykis.h",
    "content": "#ifndef __HIREDIS_IVYKIS_H__\n#define __HIREDIS_IVYKIS_H__\n#include <iv.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct redisIvykisEvents {\n    redisAsyncContext *context;\n    struct iv_fd fd;\n} redisIvykisEvents;\n\nstatic void redisIvykisReadEvent(void *arg) {\n    redisAsyncContext *context = (redisAsyncContext *)arg;\n    redisAsyncHandleRead(context);\n}\n\nstatic void redisIvykisWriteEvent(void *arg) {\n    redisAsyncContext *context = (redisAsyncContext *)arg;\n    redisAsyncHandleWrite(context);\n}\n\nstatic void redisIvykisAddRead(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);\n}\n\nstatic void redisIvykisDelRead(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_in(&e->fd, NULL);\n}\n\nstatic void redisIvykisAddWrite(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);\n}\n\nstatic void redisIvykisDelWrite(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_out(&e->fd, NULL);\n}\n\nstatic void redisIvykisCleanup(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n\n    iv_fd_unregister(&e->fd);\n    free(e);\n}\n\nstatic int redisIvykisAttach(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisIvykisEvents *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 = (redisIvykisEvents*)malloc(sizeof(*e));\n    e->context = ac;\n\n    /* Register functions to start/stop listening for events */\n    ac->ev.addRead = redisIvykisAddRead;\n    ac->ev.delRead = redisIvykisDelRead;\n    ac->ev.addWrite = redisIvykisAddWrite;\n    ac->ev.delWrite = redisIvykisDelWrite;\n    ac->ev.cleanup = redisIvykisCleanup;\n    ac->ev.data = e;\n\n    /* Initialize and install read/write events */\n    IV_FD_INIT(&e->fd);\n    e->fd.fd = c->fd;\n    e->fd.handler_in = redisIvykisReadEvent;\n    e->fd.handler_out = redisIvykisWriteEvent;\n    e->fd.handler_err = NULL;\n    e->fd.cookie = e->context;\n\n    iv_fd_register(&e->fd);\n\n    return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 <event2/event.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\n#define REDIS_LIBEVENT_DELETED 0x01\n#define REDIS_LIBEVENT_ENTERED 0x02\n\ntypedef struct redisLibeventEvents {\n    redisAsyncContext *context;\n    struct event *ev;\n    struct event_base *base;\n    struct timeval tv;\n    short flags;\n    short state;\n} redisLibeventEvents;\n\nstatic void redisLibeventDestroy(redisLibeventEvents *e) {\n    free(e);\n}\n\nstatic void redisLibeventHandler(int fd, short event, void *arg) {\n    ((void)fd);\n    redisLibeventEvents *e = (redisLibeventEvents*)arg;\n    e->state |= REDIS_LIBEVENT_ENTERED;\n\n    #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\\\n        redisLibeventDestroy(e);\\\n        return; \\\n    }\n\n    if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {\n        redisAsyncHandleTimeout(e->context);\n        CHECK_DELETED();\n    }\n\n    if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {\n        redisAsyncHandleRead(e->context);\n        CHECK_DELETED();\n    }\n\n    if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {\n        redisAsyncHandleWrite(e->context);\n        CHECK_DELETED();\n    }\n\n    e->state &= ~REDIS_LIBEVENT_ENTERED;\n    #undef CHECK_DELETED\n}\n\nstatic void redisLibeventUpdate(void *privdata, short flag, int isRemove) {\n    redisLibeventEvents *e = (redisLibeventEvents *)privdata;\n    const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;\n\n    if (isRemove) {\n        if ((e->flags & flag) == 0) {\n            return;\n        } else {\n            e->flags &= ~flag;\n        }\n    } else {\n        if (e->flags & flag) {\n            return;\n        } else {\n            e->flags |= flag;\n        }\n    }\n\n    event_del(e->ev);\n    event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,\n                 redisLibeventHandler, privdata);\n    event_add(e->ev, tv);\n}\n\nstatic void redisLibeventAddRead(void *privdata) {\n    redisLibeventUpdate(privdata, EV_READ, 0);\n}\n\nstatic void redisLibeventDelRead(void *privdata) {\n    redisLibeventUpdate(privdata, EV_READ, 1);\n}\n\nstatic void redisLibeventAddWrite(void *privdata) {\n    redisLibeventUpdate(privdata, EV_WRITE, 0);\n}\n\nstatic void redisLibeventDelWrite(void *privdata) {\n    redisLibeventUpdate(privdata, EV_WRITE, 1);\n}\n\nstatic void redisLibeventCleanup(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    if (!e) {\n        return;\n    }\n    event_del(e->ev);\n    event_free(e->ev);\n    e->ev = NULL;\n\n    if (e->state & REDIS_LIBEVENT_ENTERED) {\n        e->state |= REDIS_LIBEVENT_DELETED;\n    } else {\n        redisLibeventDestroy(e);\n    }\n}\n\nstatic void redisLibeventSetTimeout(void *privdata, struct timeval tv) {\n    redisLibeventEvents *e = (redisLibeventEvents *)privdata;\n    short flags = e->flags;\n    e->flags = 0;\n    e->tv = tv;\n    redisLibeventUpdate(e, flags, 0);\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*)calloc(1, 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.scheduleTimer = redisLibeventSetTimeout;\n    ac->ev.data = e;\n\n    /* Initialize and install read/write events */\n    e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);\n    e->base = base;\n    return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/adapters/libuv.h",
    "content": "#ifndef __HIREDIS_LIBUV_H__\n#define __HIREDIS_LIBUV_H__\n#include <stdlib.h>\n#include <uv.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n#include <string.h>\n\ntypedef struct redisLibuvEvents {\n  redisAsyncContext* context;\n  uv_poll_t          handle;\n  int                events;\n} redisLibuvEvents;\n\n\nstatic void redisLibuvPoll(uv_poll_t* handle, int status, int events) {\n  redisLibuvEvents* p = (redisLibuvEvents*)handle->data;\n  int ev = (status ? p->events : events);\n\n  if (p->context != NULL && (ev & UV_READABLE)) {\n    redisAsyncHandleRead(p->context);\n  }\n  if (p->context != NULL && (ev & UV_WRITABLE)) {\n    redisAsyncHandleWrite(p->context);\n  }\n}\n\n\nstatic void redisLibuvAddRead(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events |= UV_READABLE;\n\n  uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n}\n\n\nstatic void redisLibuvDelRead(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events &= ~UV_READABLE;\n\n  if (p->events) {\n    uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n  } else {\n    uv_poll_stop(&p->handle);\n  }\n}\n\n\nstatic void redisLibuvAddWrite(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events |= UV_WRITABLE;\n\n  uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n}\n\n\nstatic void redisLibuvDelWrite(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events &= ~UV_WRITABLE;\n\n  if (p->events) {\n    uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n  } else {\n    uv_poll_stop(&p->handle);\n  }\n}\n\n\nstatic void on_close(uv_handle_t* handle) {\n  redisLibuvEvents* p = (redisLibuvEvents*)handle->data;\n\n  free(p);\n}\n\n\nstatic void redisLibuvCleanup(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->context = NULL; // indicate that context might no longer exist\n  uv_close((uv_handle_t*)&p->handle, on_close);\n}\n\n\nstatic int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {\n  redisContext *c = &(ac->c);\n\n  if (ac->ev.data != NULL) {\n    return REDIS_ERR;\n  }\n\n  ac->ev.addRead  = redisLibuvAddRead;\n  ac->ev.delRead  = redisLibuvDelRead;\n  ac->ev.addWrite = redisLibuvAddWrite;\n  ac->ev.delWrite = redisLibuvDelWrite;\n  ac->ev.cleanup  = redisLibuvCleanup;\n\n  redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));\n\n  if (!p) {\n    return REDIS_ERR;\n  }\n\n  memset(p, 0, sizeof(*p));\n\n  if (uv_poll_init(loop, &p->handle, c->fd) != 0) {\n    return REDIS_ERR;\n  }\n\n  ac->ev.data    = p;\n  p->handle.data = p;\n  p->context     = ac;\n\n  return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/adapters/macosx.h",
    "content": "//\n//  Created by Дмитрий Бахвалов on 13.07.15.\n//  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.\n//\n\n#ifndef __HIREDIS_MACOSX_H__\n#define __HIREDIS_MACOSX_H__\n\n#include <CoreFoundation/CoreFoundation.h>\n\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct {\n    redisAsyncContext *context;\n    CFSocketRef socketRef;\n    CFRunLoopSourceRef sourceRef;\n} RedisRunLoop;\n\nstatic int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {\n    if( redisRunLoop != NULL ) {\n        if( redisRunLoop->sourceRef != NULL ) {\n            CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);\n            CFRelease(redisRunLoop->sourceRef);\n        }\n        if( redisRunLoop->socketRef != NULL ) {\n            CFSocketInvalidate(redisRunLoop->socketRef);\n            CFRelease(redisRunLoop->socketRef);\n        }\n        free(redisRunLoop);\n    }\n    return REDIS_ERR;\n}\n\nstatic void redisMacOSAddRead(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);\n}\n\nstatic void redisMacOSDelRead(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);\n}\n\nstatic void redisMacOSAddWrite(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);\n}\n\nstatic void redisMacOSDelWrite(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);\n}\n\nstatic void redisMacOSCleanup(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    freeRedisRunLoop(redisRunLoop);\n}\n\nstatic void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {\n    redisAsyncContext* context = (redisAsyncContext*) info;\n\n    switch (callbackType) {\n        case kCFSocketReadCallBack:\n            redisAsyncHandleRead(context);\n            break;\n\n        case kCFSocketWriteCallBack:\n            redisAsyncHandleWrite(context);\n            break;\n\n        default:\n            break;\n    }\n}\n\nstatic int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {\n    redisContext *redisCtx = &(redisAsyncCtx->c);\n\n    /* Nothing should be attached when something is already attached */\n    if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;\n\n    RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));\n    if( !redisRunLoop ) return REDIS_ERR;\n\n    /* Setup redis stuff */\n    redisRunLoop->context = redisAsyncCtx;\n\n    redisAsyncCtx->ev.addRead  = redisMacOSAddRead;\n    redisAsyncCtx->ev.delRead  = redisMacOSDelRead;\n    redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;\n    redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;\n    redisAsyncCtx->ev.cleanup  = redisMacOSCleanup;\n    redisAsyncCtx->ev.data     = redisRunLoop;\n\n    /* Initialize and install read/write events */\n    CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };\n\n    redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,\n                                                       kCFSocketReadCallBack | kCFSocketWriteCallBack,\n                                                       redisMacOSAsyncCallback,\n                                                       &socketCtx);\n    if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);\n\n    redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);\n    if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);\n\n    CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);\n\n    return REDIS_OK;\n}\n\n#endif\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/adapters/qt.h",
    "content": "/*-\n * Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>\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 *\n * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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#ifndef __HIREDIS_QT_H__\n#define __HIREDIS_QT_H__\n#include <QSocketNotifier>\n#include \"../async.h\"\n\nstatic void RedisQtAddRead(void *);\nstatic void RedisQtDelRead(void *);\nstatic void RedisQtAddWrite(void *);\nstatic void RedisQtDelWrite(void *);\nstatic void RedisQtCleanup(void *);\n\nclass RedisQtAdapter : public QObject {\n\n    Q_OBJECT\n\n    friend\n    void RedisQtAddRead(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->addRead();\n    }\n\n    friend\n    void RedisQtDelRead(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->delRead();\n    }\n\n    friend\n    void RedisQtAddWrite(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->addWrite();\n    }\n\n    friend\n    void RedisQtDelWrite(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->delWrite();\n    }\n\n    friend\n    void RedisQtCleanup(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->cleanup();\n    }\n\n    public:\n        RedisQtAdapter(QObject * parent = 0)\n            : QObject(parent), m_ctx(0), m_read(0), m_write(0) { }\n\n        ~RedisQtAdapter() {\n            if (m_ctx != 0) {\n                m_ctx->ev.data = NULL;\n            }\n        }\n\n        int setContext(redisAsyncContext * ac) {\n            if (ac->ev.data != NULL) {\n                return REDIS_ERR;\n            }\n            m_ctx = ac;\n            m_ctx->ev.data = this;\n            m_ctx->ev.addRead = RedisQtAddRead;\n            m_ctx->ev.delRead = RedisQtDelRead;\n            m_ctx->ev.addWrite = RedisQtAddWrite;\n            m_ctx->ev.delWrite = RedisQtDelWrite;\n            m_ctx->ev.cleanup = RedisQtCleanup;\n            return REDIS_OK;\n        }\n\n    private:\n        void addRead() {\n            if (m_read) return;\n            m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);\n            connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));\n        }\n\n        void delRead() {\n            if (!m_read) return;\n            delete m_read;\n            m_read = 0;\n        }\n\n        void addWrite() {\n            if (m_write) return;\n            m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);\n            connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));\n        }\n\n        void delWrite() {\n            if (!m_write) return;\n            delete m_write;\n            m_write = 0;\n        }\n\n        void cleanup() {\n            delRead();\n            delWrite();\n        }\n\n    private slots:\n        void read() { redisAsyncHandleRead(m_ctx); }\n        void write() { redisAsyncHandleWrite(m_ctx); }\n\n    private:\n        redisAsyncContext * m_ctx;\n        QSocketNotifier * m_read;\n        QSocketNotifier * m_write;\n};\n\n#endif /* !__HIREDIS_QT_H__ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/appveyor.yml",
    "content": "# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)\nenvironment:\n    matrix:\n        - CYG_BASH: C:\\cygwin64\\bin\\bash\n          CC: gcc\n        - CYG_BASH: C:\\cygwin\\bin\\bash\n          CC: gcc\n          CFLAGS: -m32\n          CXXFLAGS: -m32\n          LDFLAGS: -m32\n\nclone_depth: 1\n\n# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail\ninit:\n    - git config --global core.autocrlf input\n\n# Install needed build dependencies\ninstall:\n    - '%CYG_BASH% -lc \"cygcheck -dc cygwin\"'\n\nbuild_script:\n    - 'echo building...'\n    - '%CYG_BASH% -lc \"cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; mkdir build && cd build && cmake .. -G \\\"Unix Makefiles\\\" && make VERBOSE=1\"'\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#ifndef _MSC_VER\n#include <strings.h>\n#endif\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#include \"win32.h\"\n\n#include \"async_private.h\"\n\n/* Forward declaration of function in hiredis.c */\nint __redisAppendCommand(redisContext *c, const 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((const unsigned char *)key,\n                               sdslen((const sds)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((const sds)key1);\n    l2 = sdslen((const 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;\n\n    ac = realloc(c,sizeof(redisAsyncContext));\n    if (ac == NULL)\n        return NULL;\n\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    ac->ev.scheduleTimer = 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    if (!ac)\n        return;\n\n    redisContext *c = &(ac->c);\n    ac->err = c->err;\n    ac->errstr = c->errstr;\n}\n\nredisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {\n    redisOptions myOptions = *options;\n    redisContext *c;\n    redisAsyncContext *ac;\n\n    myOptions.options |= REDIS_OPT_NONBLOCK;\n    c = redisConnectWithOptions(&myOptions);\n    if (c == NULL) {\n        return NULL;\n    }\n    ac = redisAsyncInitialize(c);\n    if (ac == NULL) {\n        redisFree(c);\n        return NULL;\n    }\n    __redisAsyncCopyError(ac);\n    return ac;\n}\n\nredisAsyncContext *redisAsyncConnect(const char *ip, int port) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    return redisAsyncConnectWithOptions(&options);\n}\n\nredisAsyncContext *redisAsyncConnectBind(const char *ip, int port,\n                                         const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    return redisAsyncConnectWithOptions(&options);\n}\n\nredisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,\n                                                  const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.options |= REDIS_OPT_REUSEADDR;\n    options.endpoint.tcp.source_addr = source_addr;\n    return redisAsyncConnectWithOptions(&options);\n}\n\nredisAsyncContext *redisAsyncConnectUnix(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    return redisAsyncConnectWithOptions(&options);\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 (cb == NULL)\n        return REDIS_ERR_OOM;\n\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. */\nvoid __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        int ret = __redisShiftCallback(&ac->replies,NULL);\n        assert(ret == 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    /* cleanup event library on disconnect.\n     * this is safe to call multiple times */\n    _EL_CLEANUP(ac);\n\n    /* For non-clean disconnects, __redisAsyncFree() will execute pending\n     * callbacks with a NULL-reply. */\n    if (!(c->flags & REDIS_NO_AUTO_FREE)) {\n      __redisAsyncFree(ac);\n    }\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\n    /** unset the auto-free flag here, because disconnect undoes this */\n    c->flags &= ~REDIS_NO_AUTO_FREE;\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    redisCallback *cb;\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            cb = dictGetEntryVal(de);\n\n            /* If this is an subscribe reply decrease pending counter. */\n            if (strcasecmp(stype+pvariant,\"subscribe\") == 0) {\n                cb->pending_subs -= 1;\n            }\n\n            memcpy(dstcb,cb,sizeof(*dstcb));\n\n            /* If this is an unsubscribe message, remove it. */\n            if (strcasecmp(stype+pvariant,\"unsubscribe\") == 0) {\n                if (cb->pending_subs == 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\n                /* Unset subscribed flag only when no pipelined pending subscribe. */\n                if (reply->element[2]->integer == 0\n                    && dictSize(ac->sub.channels) == 0\n                    && dictSize(ac->sub.patterns) == 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 = {NULL, NULL, 0, NULL};\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                && ac->replies.head == NULL) {\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                c->reader->fn->freeObject(reply);\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 successful, the connect callback\n * is called with a REDIS_ERR status and the context is free'd. */\nstatic int __redisAsyncHandleConnect(redisAsyncContext *ac) {\n    int completed = 0;\n    redisContext *c = &(ac->c);\n    if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {\n        /* Error! */\n        redisCheckSocketError(c);\n        if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);\n        __redisAsyncDisconnect(ac);\n        return REDIS_ERR;\n    } else if (completed == 1) {\n        /* connected! */\n        if (ac->onConnect) ac->onConnect(ac, REDIS_OK);\n        c->flags |= REDIS_CONNECTED;\n        return REDIS_OK;\n    } else {\n        return REDIS_OK;\n    }\n}\n\nvoid redisAsyncRead(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\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\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    c->funcs->async_read(ac);\n}\n\nvoid redisAsyncWrite(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    int done = 0;\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\nvoid redisAsyncHandleWrite(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    c->funcs->async_write(ac);\n}\n\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\nvoid redisAsyncHandleTimeout(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n\n    if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {\n        /* Nothing to do - just an idle timeout */\n        return;\n    }\n\n    if (!c->err) {\n        __redisSetError(c, REDIS_ERR_TIMEOUT, \"Timeout\");\n    }\n\n    if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {\n        ac->onConnect(ac, REDIS_ERR);\n    }\n\n    while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {\n        __redisRunCallback(ac, &cb, NULL);\n    }\n\n    /**\n     * TODO: Don't automatically sever the connection,\n     * rather, allow to ignore <x> responses before the queue is clear\n     */\n    __redisAsyncDisconnect(ac);\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 const char *nextArgument(const char *start, const char **str, size_t *len) {\n    const 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, const char *cmd, size_t len) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n    struct dict *cbdict;\n    dictEntry *de;\n    redisCallback *existcb;\n    int pvariant, hasnext;\n    const char *cstr, *astr;\n    size_t clen, alen;\n    const char *p;\n    sds sname;\n    int ret;\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    cb.pending_subs = 1;\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                cbdict = ac->sub.patterns;\n            else\n                cbdict = ac->sub.channels;\n\n            de = dictFind(cbdict,sname);\n\n            if (de != NULL) {\n                existcb = dictGetEntryVal(de);\n                cb.pending_subs = existcb->pending_subs + 1;\n            }\n\n            ret = dictReplace(cbdict,sname,&cb);\n\n            if (ret == 0) sdsfree(sname);\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\n    /* We don't want to pass -1 or -2 to future functions as a length. */\n    if (len < 0)\n        return REDIS_ERR;\n\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    sds cmd;\n    int len;\n    int status;\n    len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);\n    if (len < 0)\n        return REDIS_ERR;\n    status = __redisAsyncCommand(ac,fn,privdata,cmd,len);\n    sdsfree(cmd);\n    return status;\n}\n\nint redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {\n    int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);\n    return status;\n}\n\nvoid redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {\n    if (!ac->c.timeout) {\n        ac->c.timeout = calloc(1, sizeof(tv));\n    }\n\n    if (tv.tv_sec == ac->c.timeout->tv_sec &&\n        tv.tv_usec == ac->c.timeout->tv_usec) {\n        return;\n    }\n\n    *ac->c.timeout = tv;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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    int pending_subs;\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);\ntypedef void(redisTimerCallback)(void *timer, void *privdata);\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        void (*scheduleTimer)(void *privdata, struct timeval tv);\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    /* Address used for connect() */\n    struct sockaddr *saddr;\n    size_t addrlen;\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 *redisAsyncConnectWithOptions(const redisOptions *options);\nredisAsyncContext *redisAsyncConnect(const char *ip, int port);\nredisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);\nredisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,\n                                                  const char *source_addr);\nredisAsyncContext *redisAsyncConnectUnix(const char *path);\nint redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\n\nvoid redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);\nvoid redisAsyncDisconnect(redisAsyncContext *ac);\nvoid redisAsyncFree(redisAsyncContext *ac);\n\n/* Handle read/write events */\nvoid redisAsyncHandleRead(redisAsyncContext *ac);\nvoid redisAsyncHandleWrite(redisAsyncContext *ac);\nvoid redisAsyncHandleTimeout(redisAsyncContext *ac);\nvoid redisAsyncRead(redisAsyncContext *ac);\nvoid redisAsyncWrite(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);\nint redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/async_private.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_PRIVATE_H\n#define __HIREDIS_ASYNC_PRIVATE_H\n\n#define _EL_ADD_READ(ctx)                                         \\\n    do {                                                          \\\n        refreshTimeout(ctx);                                      \\\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)                                          \\\n    do {                                                            \\\n        refreshTimeout(ctx);                                        \\\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        ctx->ev.cleanup = NULL; \\\n    } while(0);\n\nstatic inline void refreshTimeout(redisAsyncContext *ctx) {\n    if (ctx->c.timeout && ctx->ev.scheduleTimer &&\n        (ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) {\n        ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);\n    // } else {\n    //     printf(\"Not scheduling timer.. (tmo=%p)\\n\", ctx->c.timeout);\n    //     if (ctx->c.timeout){\n    //         printf(\"tv_sec: %u. tv_usec: %u\\n\", ctx->c.timeout->tv_sec,\n    //                ctx->c.timeout->tv_usec);\n    //     }\n    }\n}\n\nvoid __redisAsyncDisconnect(redisAsyncContext *ac);\nvoid redisProcessCallbacks(redisAsyncContext *ac);\n\n#endif  /* __HIREDIS_ASYNC_PRIVATE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 succeed. */\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 initial 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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/hiredis/examples/CMakeLists.txt",
    "content": "INCLUDE(FindPkgConfig)\n# Check for GLib\n\nPKG_CHECK_MODULES(GLIB2 glib-2.0)\nif (GLIB2_FOUND)\n    INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS})\n    LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS})\n    ADD_EXECUTABLE(example-glib example-glib.c)\n    TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES})\nENDIF(GLIB2_FOUND)\n\nFIND_PATH(LIBEV ev.h\n    HINTS /usr/local /usr/opt/local\n    ENV LIBEV_INCLUDE_DIR)\n\nif (LIBEV)\n    # Just compile and link with libev\n    ADD_EXECUTABLE(example-libev example-libev.c)\n    TARGET_LINK_LIBRARIES(example-libev hiredis ev)\nENDIF()\n\nFIND_PATH(LIBEVENT event.h)\nif (LIBEVENT)\n    ADD_EXECUTABLE(example-libevent example-libevent)\n    TARGET_LINK_LIBRARIES(example-libevent hiredis event)\nENDIF()\n\nFIND_PATH(LIBUV uv.h)\nIF (LIBUV)\n    ADD_EXECUTABLE(example-libuv example-libuv.c)\n    TARGET_LINK_LIBRARIES(example-libuv hiredis uv)\nENDIF()\n\nIF (APPLE)\n    FIND_LIBRARY(CF CoreFoundation)\n    ADD_EXECUTABLE(example-macosx example-macosx.c)\n    TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF})\nENDIF()\n\nIF (ENABLE_SSL)\n    ADD_EXECUTABLE(example-ssl example-ssl.c)\n    TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl)\nENDIF()\n\nADD_EXECUTABLE(example example.c)\nTARGET_LINK_LIBRARIES(example hiredis)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-ae.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\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        aeStop(loop);\n        return;\n    }\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        aeStop(loop);\n        return;\n    }\n\n    printf(\"Disconnected...\\n\");\n    aeStop(loop);\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(64);\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": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-glib.c",
    "content": "#include <stdlib.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/glib.h>\n\nstatic GMainLoop *mainloop;\n\nstatic void\nconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,\n            int status)\n{\n    if (status != REDIS_OK) {\n        g_printerr(\"Failed to connect: %s\\n\", ac->errstr);\n        g_main_loop_quit(mainloop);\n    } else {\n        g_printerr(\"Connected...\\n\");\n    }\n}\n\nstatic void\ndisconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,\n               int status)\n{\n    if (status != REDIS_OK) {\n        g_error(\"Failed to disconnect: %s\", ac->errstr);\n    } else {\n        g_printerr(\"Disconnected...\\n\");\n        g_main_loop_quit(mainloop);\n    }\n}\n\nstatic void\ncommand_cb(redisAsyncContext *ac,\n           gpointer r,\n           gpointer user_data G_GNUC_UNUSED)\n{\n    redisReply *reply = r;\n\n    if (reply) {\n        g_print(\"REPLY: %s\\n\", reply->str);\n    }\n\n    redisAsyncDisconnect(ac);\n}\n\ngint\nmain (gint argc     G_GNUC_UNUSED,\n      gchar *argv[] G_GNUC_UNUSED)\n{\n    redisAsyncContext *ac;\n    GMainContext *context = NULL;\n    GSource *source;\n\n    ac = redisAsyncConnect(\"127.0.0.1\", 6379);\n    if (ac->err) {\n        g_printerr(\"%s\\n\", ac->errstr);\n        exit(EXIT_FAILURE);\n    }\n\n    source = redis_source_new(ac);\n    mainloop = g_main_loop_new(context, FALSE);\n    g_source_attach(source, context);\n\n    redisAsyncSetConnectCallback(ac, connect_cb);\n    redisAsyncSetDisconnectCallback(ac, disconnect_cb);\n    redisAsyncCommand(ac, command_cb, NULL, \"SET key 1234\");\n    redisAsyncCommand(ac, command_cb, NULL, \"GET key\");\n\n    g_main_loop_run(mainloop);\n\n    return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-ivykis.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/ivykis.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    iv_init();\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    redisIvykisAttach(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\n    iv_main();\n\n    iv_deinit();\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-libev.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\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": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-libevent-ssl.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis.h>\n#include <hiredis_ssl.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    if (argc < 5) {\n        fprintf(stderr,\n                \"Usage: %s <key> <host> <port> <cert> <certKey> [ca]\\n\", argv[0]);\n        exit(1);\n    }\n\n    const char *value = argv[1];\n    size_t nvalue = strlen(value);\n\n    const char *hostname = argv[2];\n    int port = atoi(argv[3]);\n\n    const char *cert = argv[4];\n    const char *certKey = argv[5];\n    const char *caCert = argc > 5 ? argv[6] : NULL;\n\n    redisAsyncContext *c = redisAsyncConnect(hostname, port);\n    if (c->err) {\n        /* Let *c leak for now... */\n        printf(\"Error: %s\\n\", c->errstr);\n        return 1;\n    }\n    if (redisSecureConnection(&c->c, caCert, cert, certKey, \"sni\") != REDIS_OK) {\n        printf(\"SSL Error!\\n\");\n        exit(1);\n    }\n\n    redisLibeventAttach(c,base);\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n    redisAsyncCommand(c, NULL, NULL, \"SET key %b\", value, nvalue);\n    redisAsyncCommand(c, getCallback, (char*)\"end-1\", \"GET key\");\n    event_base_dispatch(base);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-libevent.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\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) {\n        if (c->errstr) {\n            printf(\"errstr: %s\\n\", c->errstr);\n        }\n        return;\n    }\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    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, \"127.0.0.1\", 6379);\n    struct timeval tv = {0};\n    tv.tv_sec = 1;\n    options.timeout = &tv;\n\n\n    redisAsyncContext *c = redisAsyncConnectWithOptions(&options);\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": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-libuv.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/libuv.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    uv_loop_t* loop = uv_default_loop();\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    redisLibuvAttach(c,loop);\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    uv_run(loop, UV_RUN_DEFAULT);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-macosx.c",
    "content": "//\n//  Created by Дмитрий Бахвалов on 13.07.15.\n//  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.\n//\n\n#include <stdio.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/macosx.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    CFRunLoopStop(CFRunLoopGetCurrent());\n    printf(\"Disconnected...\\n\");\n}\n\nint main (int argc, char **argv) {\n    signal(SIGPIPE, SIG_IGN);\n\n    CFRunLoopRef loop = CFRunLoopGetCurrent();\n    if( !loop ) {\n        printf(\"Error: Cannot get current run loop\\n\");\n        return 1;\n    }\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    redisMacOSAttach(c, loop);\n\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n\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\n    CFRunLoopRun();\n\n    return 0;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-qt.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\n#include <QCoreApplication>\n#include <QTimer>\n\n#include \"example-qt.h\"\n\nvoid getCallback(redisAsyncContext *, void * r, void * privdata) {\n\n    redisReply * reply = static_cast<redisReply *>(r);\n    ExampleQt * ex = static_cast<ExampleQt *>(privdata);\n    if (reply == nullptr || ex == nullptr) return;\n\n    cout << \"key: \" << reply->str << endl;\n\n    ex->finish();\n}\n\nvoid ExampleQt::run() {\n\n    m_ctx = redisAsyncConnect(\"localhost\", 6379);\n\n    if (m_ctx->err) {\n        cerr << \"Error: \" << m_ctx->errstr << endl;\n        redisAsyncFree(m_ctx);\n        emit finished();\n    }\n\n    m_adapter.setContext(m_ctx);\n\n    redisAsyncCommand(m_ctx, NULL, NULL, \"SET key %s\", m_value);\n    redisAsyncCommand(m_ctx, getCallback, this, \"GET key\");\n}\n\nint main (int argc, char **argv) {\n\n    QCoreApplication app(argc, argv);\n\n    ExampleQt example(argv[argc-1]);\n\n    QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit()));\n    QTimer::singleShot(0, &example, SLOT(run()));\n\n    return app.exec();\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-qt.h",
    "content": "#ifndef __HIREDIS_EXAMPLE_QT_H\n#define __HIREDIS_EXAMPLE_QT_H\n\n#include <adapters/qt.h>\n\nclass ExampleQt : public QObject {\n\n    Q_OBJECT\n\n    public:\n        ExampleQt(const char * value, QObject * parent = 0)\n            : QObject(parent), m_value(value) {}\n\n    signals:\n        void finished();\n\n    public slots:\n        void run();\n\n    private:\n        void finish() { emit finished(); }\n\n    private:\n        const char * m_value;\n        redisAsyncContext * m_ctx;\n        RedisQtAdapter m_adapter;\n\n    friend\n    void getCallback(redisAsyncContext *, void *, void *);\n};\n\n#endif /* !__HIREDIS_EXAMPLE_QT_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example-ssl.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <hiredis.h>\n#include <hiredis_ssl.h>\n\nint main(int argc, char **argv) {\n    unsigned int j;\n    redisContext *c;\n    redisReply *reply;\n    if (argc < 4) {\n        printf(\"Usage: %s <host> <port> <cert> <key> [ca]\\n\", argv[0]);\n        exit(1);\n    }\n    const char *hostname = (argc > 1) ? argv[1] : \"127.0.0.1\";\n    int port = atoi(argv[2]);\n    const char *cert = argv[3];\n    const char *key = argv[4];\n    const char *ca = argc > 4 ? argv[5] : NULL;\n\n    struct timeval tv = { 1, 500000 }; // 1.5 seconds\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, hostname, port);\n    options.timeout = &tv;\n    c = redisConnectWithOptions(&options);\n\n    if (c == NULL || c->err) {\n        if (c) {\n            printf(\"Connection error: %s\\n\", c->errstr);\n            redisFree(c);\n        } else {\n            printf(\"Connection error: can't allocate redis context\\n\");\n        }\n        exit(1);\n    }\n\n    if (redisSecureConnection(c, ca, cert, key, \"sni\") != REDIS_OK) {\n        printf(\"Couldn't initialize SSL!\\n\");\n        printf(\"Error: %s\\n\", c->errstr);\n        redisFree(c);\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\", (size_t) 3, \"hello\", (size_t) 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,\"%u\",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    /* Disconnects and frees the context */\n    redisFree(c);\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/examples/example.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <hiredis.h>\n\nint main(int argc, char **argv) {\n    unsigned int j, isunix = 0;\n    redisContext *c;\n    redisReply *reply;\n    const char *hostname = (argc > 1) ? argv[1] : \"127.0.0.1\";\n\n    if (argc > 2) {\n        if (*argv[2] == 'u' || *argv[2] == 'U') {\n            isunix = 1;\n            /* in this case, host is the path to the unix socket */\n            printf(\"Will connect to unix socket @%s\\n\", hostname);\n        }\n    }\n\n    int port = (argc > 2) ? atoi(argv[2]) : 6379;\n\n    struct timeval timeout = { 1, 500000 }; // 1.5 seconds\n    if (isunix) {\n        c = redisConnectUnixWithTimeout(hostname, timeout);\n    } else {\n        c = redisConnectWithTimeout(hostname, port, timeout);\n    }\n    if (c == NULL || c->err) {\n        if (c) {\n            printf(\"Connection error: %s\\n\", c->errstr);\n            redisFree(c);\n        } else {\n            printf(\"Connection error: can't allocate redis context\\n\");\n        }\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\", (size_t) 3, \"hello\", (size_t) 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,\"%u\",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    /* Disconnects and frees the context */\n    redisFree(c);\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/fmacros.h",
    "content": "#ifndef __HIREDIS_FMACRO_H\n#define __HIREDIS_FMACRO_H\n\n#define _XOPEN_SOURCE 600\n#define _POSIX_C_SOURCE 200112L\n\n#if defined(__APPLE__) && defined(__MACH__)\n/* Enable TCP_KEEPALIVE */\n#define _DARWIN_C_SOURCE\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/hiredis.c",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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 <assert.h>\n#include <errno.h>\n#include <ctype.h>\n\n#include \"hiredis.h\"\n#include \"net.h\"\n#include \"sds.h\"\n#include \"async.h\"\n#include \"win32.h\"\n\nstatic redisContextFuncs redisContextDefaultFuncs = {\n    .free_privdata = NULL,\n    .async_read = redisAsyncRead,\n    .async_write = redisAsyncWrite,\n    .read = redisNetRead,\n    .write = redisNetWrite\n};\n\nstatic redisReply *createReplyObject(int type);\nstatic void *createStringObject(const redisReadTask *task, char *str, size_t len);\nstatic void *createArrayObject(const redisReadTask *task, size_t elements);\nstatic void *createIntegerObject(const redisReadTask *task, long long value);\nstatic void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);\nstatic void *createNilObject(const redisReadTask *task);\nstatic void *createBoolObject(const redisReadTask *task, int bval);\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    createDoubleObject,\n    createNilObject,\n    createBoolObject,\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    if (r == NULL)\n        return;\n\n    switch(r->type) {\n    case REDIS_REPLY_INTEGER:\n        break; /* Nothing to free */\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n        if (r->element != NULL) {\n            for (j = 0; j < r->elements; j++)\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    case REDIS_REPLY_DOUBLE:\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    assert(task->type == REDIS_REPLY_ERROR  ||\n           task->type == REDIS_REPLY_STATUS ||\n           task->type == REDIS_REPLY_STRING ||\n           task->type == REDIS_REPLY_VERB);\n\n    /* Copy string value */\n    if (task->type == REDIS_REPLY_VERB) {\n        buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */\n        if (buf == NULL) {\n            freeReplyObject(r);\n            return NULL;\n        }\n        memcpy(r->vtype,str,3);\n        r->vtype[3] = '\\0';\n        memcpy(buf,str+4,len-4);\n        buf[len-4] = '\\0';\n        r->len = len-4;\n    } else {\n        buf = malloc(len+1);\n        if (buf == NULL) {\n            freeReplyObject(r);\n            return NULL;\n        }\n        memcpy(buf,str,len);\n        buf[len] = '\\0';\n        r->len = len;\n    }\n    r->str = buf;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createArrayObject(const redisReadTask *task, size_t elements) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(task->type);\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->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\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->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_DOUBLE);\n    if (r == NULL)\n        return NULL;\n\n    r->dval = value;\n    r->str = malloc(len+1);\n    if (r->str == NULL) {\n        freeReplyObject(r);\n        return NULL;\n    }\n\n    /* The double reply also has the original protocol string representing a\n     * double as a null terminated string. This way the caller does not need\n     * to format back for string conversion, especially since Redis does efforts\n     * to make the string more human readable avoiding the calssical double\n     * decimal string conversion artifacts. */\n    memcpy(r->str, str, len);\n    r->str[len] = '\\0';\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\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->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createBoolObject(const redisReadTask *task, int bval) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_BOOL);\n    if (r == NULL)\n        return NULL;\n\n    r->integer = bval != 0;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * Implementation borrowed from link in redis/src/util.c:string2ll(). */\nstatic uint32_t countDigits(uint64_t v) {\n  uint32_t result = 1;\n  for (;;) {\n    if (v < 10) return result;\n    if (v < 100) return result + 1;\n    if (v < 1000) return result + 2;\n    if (v < 10000) return result + 3;\n    v /= 10000U;\n    result += 4;\n  }\n}\n\n/* Helper that calculates the bulk length given a certain string length. */\nstatic size_t bulklen(size_t len) {\n    return 1+countDigits(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 error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */\n    int j;\n\n    /* Abort if there is not target to set */\n    if (target == NULL)\n        return -1;\n\n    /* Build the command string accordingly to protocol */\n    curarg = sdsempty();\n    if (curarg == NULL)\n        return -1;\n\n    while(*c != '\\0') {\n        if (*c != '%' || c[1] == '\\0') {\n            if (*c == ' ') {\n                if (touched) {\n                    newargv = realloc(curargv,sizeof(char*)*(argc+1));\n                    if (newargv == NULL) goto memory_err;\n                    curargv = newargv;\n                    curargv[argc++] = curarg;\n                    totlen += bulklen(sdslen(curarg));\n\n                    /* curarg is put in argv so it can be overwritten. */\n                    curarg = sdsempty();\n                    if (curarg == NULL) goto memory_err;\n                    touched = 0;\n                }\n            } else {\n                newarg = sdscatlen(curarg,c,1);\n                if (newarg == NULL) goto memory_err;\n                curarg = newarg;\n                touched = 1;\n            }\n        } else {\n            char *arg;\n            size_t size;\n\n            /* Set newarg so it can be checked even if it is not touched. */\n            newarg = curarg;\n\n            switch(c[1]) {\n            case 's':\n                arg = va_arg(ap,char*);\n                size = strlen(arg);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case 'b':\n                arg = va_arg(ap,char*);\n                size = va_arg(ap,size_t);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case '%':\n                newarg = sdscat(curarg,\"%\");\n                break;\n            default:\n                /* Try to detect printf format */\n                {\n                    static const char intfmts[] = \"diouxX\";\n                    static const char flags[] = \"#0-+ \";\n                    char _format[16];\n                    const char *_p = c+1;\n                    size_t _l = 0;\n                    va_list _cpy;\n\n                    /* Flags */\n                    while (*_p != '\\0' && strchr(flags,*_p) != NULL) _p++;\n\n                    /* Field width */\n                    while (*_p != '\\0' && isdigit(*_p)) _p++;\n\n                    /* Precision */\n                    if (*_p == '.') {\n                        _p++;\n                        while (*_p != '\\0' && isdigit(*_p)) _p++;\n                    }\n\n                    /* Copy va_list before consuming with va_arg */\n                    va_copy(_cpy,ap);\n\n                    /* Integer conversion (without modifiers) */\n                    if (strchr(intfmts,*_p) != NULL) {\n                        va_arg(ap,int);\n                        goto fmt_valid;\n                    }\n\n                    /* Double conversion (without modifiers) */\n                    if (strchr(\"eEfFgGaA\",*_p) != NULL) {\n                        va_arg(ap,double);\n                        goto fmt_valid;\n                    }\n\n                    /* Size: char */\n                    if (_p[0] == 'h' && _p[1] == 'h') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* char gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: short */\n                    if (_p[0] == 'h') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* short gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long long */\n                    if (_p[0] == 'l' && _p[1] == 'l') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long */\n                    if (_p[0] == 'l') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                fmt_invalid:\n                    va_end(_cpy);\n                    goto format_err;\n\n                fmt_valid:\n                    _l = (_p+1)-c;\n                    if (_l < sizeof(_format)-2) {\n                        memcpy(_format,c,_l);\n                        _format[_l] = '\\0';\n                        newarg = sdscatvprintf(curarg,_format,_cpy);\n\n                        /* Update current position (note: outer blocks\n                         * increment c twice so compensate here) */\n                        c = _p-1;\n                    }\n\n                    va_end(_cpy);\n                    break;\n                }\n            }\n\n            if (newarg == NULL) goto memory_err;\n            curarg = newarg;\n\n            touched = 1;\n            c++;\n        }\n        c++;\n    }\n\n    /* Add the last argument if needed */\n    if (touched) {\n        newargv = realloc(curargv,sizeof(char*)*(argc+1));\n        if (newargv == NULL) goto memory_err;\n        curargv = newargv;\n        curargv[argc++] = curarg;\n        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+countDigits(argc)+2;\n\n    /* Build the command at protocol level */\n    cmd = malloc(totlen+1);\n    if (cmd == NULL) goto memory_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\nformat_err:\n    error_type = -2;\n    goto cleanup;\n\nmemory_err:\n    error_type = -1;\n    goto cleanup;\n\ncleanup:\n    if (curargv) {\n        while(argc--)\n            sdsfree(curargv[argc]);\n        free(curargv);\n    }\n\n    sdsfree(curarg);\n    free(cmd);\n\n    return error_type;\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 as a size_t. 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\n    /* The API says \"-1\" means bad result, but we now also return \"-2\" in some\n     * cases.  Force the return value to always be -1. */\n    if (len < 0)\n        len = -1;\n\n    return len;\n}\n\n/* Format a command according to the Redis protocol using an sds string and\n * sdscatfmt for the processing of arguments. 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 redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,\n                              const size_t *argvlen)\n{\n    sds cmd;\n    unsigned long long totlen;\n    int j;\n    size_t len;\n\n    /* Abort on a NULL target */\n    if (target == NULL)\n        return -1;\n\n    /* Calculate our total size */\n    totlen = 1+countDigits(argc)+2;\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        totlen += bulklen(len);\n    }\n\n    /* Use an SDS string for command construction */\n    cmd = sdsempty();\n    if (cmd == NULL)\n        return -1;\n\n    /* We already know how much storage we need */\n    cmd = sdsMakeRoomFor(cmd, totlen);\n    if (cmd == NULL)\n        return -1;\n\n    /* Construct command */\n    cmd = sdscatfmt(cmd, \"*%i\\r\\n\", argc);\n    for (j=0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        cmd = sdscatfmt(cmd, \"$%u\\r\\n\", len);\n        cmd = sdscatlen(cmd, argv[j], len);\n        cmd = sdscatlen(cmd, \"\\r\\n\", sizeof(\"\\r\\n\")-1);\n    }\n\n    assert(sdslen(cmd)==totlen);\n\n    *target = cmd;\n    return totlen;\n}\n\nvoid redisFreeSdsCommand(sds cmd) {\n    sdsfree(cmd);\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    /* Abort on a NULL target */\n    if (target == NULL)\n        return -1;\n\n    /* Calculate number of bytes needed for the command */\n    totlen = 1+countDigits(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 redisFreeCommand(char *cmd) {\n    free(cmd);\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\nredisReader *redisReaderCreate(void) {\n    return redisReaderCreateWithFunctions(&defaultFunctions);\n}\n\nstatic redisContext *redisContextInit(const redisOptions *options) {\n    redisContext *c;\n\n    c = calloc(1, sizeof(*c));\n    if (c == NULL)\n        return NULL;\n\n    c->funcs = &redisContextDefaultFuncs;\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n    c->fd = REDIS_INVALID_FD;\n\n    if (c->obuf == NULL || c->reader == NULL) {\n        redisFree(c);\n        return NULL;\n    }\n    (void)options; /* options are used in other functions */\n    return c;\n}\n\nvoid redisFree(redisContext *c) {\n    if (c == NULL)\n        return;\n    redisNetClose(c);\n\n    sdsfree(c->obuf);\n    redisReaderFree(c->reader);\n    free(c->tcp.host);\n    free(c->tcp.source_addr);\n    free(c->unix_sock.path);\n    free(c->timeout);\n    free(c->saddr);\n    if (c->funcs->free_privdata) {\n        c->funcs->free_privdata(c->privdata);\n    }\n    memset(c, 0xff, sizeof(*c));\n    free(c);\n}\n\nredisFD redisFreeKeepFd(redisContext *c) {\n    redisFD fd = c->fd;\n    c->fd = REDIS_INVALID_FD;\n    redisFree(c);\n    return fd;\n}\n\nint redisReconnect(redisContext *c) {\n    c->err = 0;\n    memset(c->errstr, '\\0', strlen(c->errstr));\n\n    if (c->privdata && c->funcs->free_privdata) {\n        c->funcs->free_privdata(c->privdata);\n        c->privdata = NULL;\n    }\n\n    redisNetClose(c);\n\n    sdsfree(c->obuf);\n    redisReaderFree(c->reader);\n\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n\n    if (c->connection_type == REDIS_CONN_TCP) {\n        return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,\n                c->timeout, c->tcp.source_addr);\n    } else if (c->connection_type == REDIS_CONN_UNIX) {\n        return redisContextConnectUnix(c, c->unix_sock.path, c->timeout);\n    } else {\n        /* Something bad happened here and shouldn't have. There isn't\n           enough information in the context to reconnect. */\n        __redisSetError(c,REDIS_ERR_OTHER,\"Not enough information to reconnect\");\n    }\n\n    return REDIS_ERR;\n}\n\nredisContext *redisConnectWithOptions(const redisOptions *options) {\n    redisContext *c = redisContextInit(options);\n    if (c == NULL) {\n        return NULL;\n    }\n    if (!(options->options & REDIS_OPT_NONBLOCK)) {\n        c->flags |= REDIS_BLOCK;\n    }\n    if (options->options & REDIS_OPT_REUSEADDR) {\n        c->flags |= REDIS_REUSEADDR;\n    }\n    if (options->options & REDIS_OPT_NOAUTOFREE) {\n      c->flags |= REDIS_NO_AUTO_FREE;\n    }\n\n    if (options->type == REDIS_CONN_TCP) {\n        redisContextConnectBindTcp(c, options->endpoint.tcp.ip,\n                                   options->endpoint.tcp.port, options->timeout,\n                                   options->endpoint.tcp.source_addr);\n    } else if (options->type == REDIS_CONN_UNIX) {\n        redisContextConnectUnix(c, options->endpoint.unix_socket,\n                                options->timeout);\n    } else if (options->type == REDIS_CONN_USERFD) {\n        c->fd = options->endpoint.fd;\n        c->flags |= REDIS_CONNECTED;\n    } else {\n        // Unknown type - FIXME - FREE\n        return NULL;\n    }\n    if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {\n        redisContextSetTimeout(c, *options->timeout);\n    }\n    return 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    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.timeout = &tv;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectNonBlock(const char *ip, int port) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectBindNonBlock(const char *ip, int port,\n                                       const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,\n                                                const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnix(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    options.timeout = &tv;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnixNonBlock(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectFd(redisFD fd) {\n    redisOptions options = {0};\n    options.type = REDIS_CONN_USERFD;\n    options.endpoint.fd = fd;\n    return redisConnectWithOptions(&options);\n}\n\n/* Set read/write timeout on a blocking socket. */\nint redisSetTimeout(redisContext *c, const struct timeval tv) {\n    if (c->flags & REDIS_BLOCK)\n        return redisContextSetTimeout(c,tv);\n    return REDIS_ERR;\n}\n\n/* Enable connection KeepAlive. */\nint redisEnableKeepAlive(redisContext *c) {\n    if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)\n        return REDIS_ERR;\n    return REDIS_OK;\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 redisGetReplyFromReader 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 = c->funcs->read(c, buf, sizeof(buf));\n    if (nread > 0) {\n        if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) {\n            __redisSetError(c, c->reader->err, c->reader->errstr);\n            return REDIS_ERR;\n        } else {\n        }\n    } else if (nread < 0) {\n        return REDIS_ERR;\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 * successfully 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 occurred trying to write and sets\n * c->errstr to hold the appropriate error string.\n */\nint redisBufferWrite(redisContext *c, int *done) {\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        int nwritten = c->funcs->write(c);\n        if (nwritten < 0) {\n            return REDIS_ERR;\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, const 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 redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {\n\n    if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {\n        return REDIS_ERR;\n    }\n\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    } else if (len == -2) {\n        __redisSetError(c,REDIS_ERR_OTHER,\"Invalid format string\");\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    sds cmd;\n    int len;\n\n    len = redisFormatSdsCommandArgv(&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        sdsfree(cmd);\n        return REDIS_ERR;\n    }\n\n    sdsfree(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 successfully 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    va_start(ap,format);\n    void *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": "Chapter02/redis-6.0.3/deps/hiredis/hiredis.h",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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 \"read.h\"\n#include <stdarg.h> /* for va_list */\n#ifndef _MSC_VER\n#include <sys/time.h> /* for struct timeval */\n#else\nstruct timeval; /* forward declaration */\n#endif\n#include <stdint.h> /* uintXX_t, etc */\n#include \"sds.h\" /* for sds */\n\n#define HIREDIS_MAJOR 0\n#define HIREDIS_MINOR 14\n#define HIREDIS_PATCH 0\n#define HIREDIS_SONAME 0.14\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/* Flag that is set when we should set SO_REUSEADDR before calling bind() */\n#define REDIS_REUSEADDR 0x80\n\n/**\n * Flag that indicates the user does not want the context to\n * be automatically freed upon error\n */\n#define REDIS_NO_AUTO_FREE 0x200\n\n#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */\n\n/* number of times we retry to connect in the case of EADDRNOTAVAIL and\n * SO_REUSEADDR is being used. */\n#define REDIS_CONNECT_RETRIES  10\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    double dval; /* The double when type is REDIS_REPLY_DOUBLE */\n    size_t len; /* Length of string */\n    char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING\n                  and REDIS_REPLY_DOUBLE (in additionl to dval). */\n    char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null\n                      terminated 3 character content type, such as \"txt\". */\n    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */\n    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */\n} redisReply;\n\nredisReader *redisReaderCreate(void);\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);\nint redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);\nvoid redisFreeCommand(char *cmd);\nvoid redisFreeSdsCommand(sds cmd);\n\nenum redisConnectionType {\n    REDIS_CONN_TCP,\n    REDIS_CONN_UNIX,\n    REDIS_CONN_USERFD\n};\n\nstruct redisSsl;\n\n#define REDIS_OPT_NONBLOCK 0x01\n#define REDIS_OPT_REUSEADDR 0x02\n\n/**\n * Don't automatically free the async object on a connection failure,\n * or other implicit conditions. Only free on an explicit call to disconnect() or free()\n */\n#define REDIS_OPT_NOAUTOFREE 0x04\n\n/* In Unix systems a file descriptor is a regular signed int, with -1\n * representing an invalid descriptor. In Windows it is a SOCKET\n * (32- or 64-bit unsigned integer depending on the architecture), where\n * all bits set (~0) is INVALID_SOCKET.  */\n#ifndef _WIN32\ntypedef int redisFD;\n#define REDIS_INVALID_FD -1\n#else\n#ifdef _WIN64\ntypedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */\n#else\ntypedef unsigned long redisFD;      /* SOCKET = 32-bit UINT_PTR */\n#endif\n#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */\n#endif\n\ntypedef struct {\n    /*\n     * the type of connection to use. This also indicates which\n     * `endpoint` member field to use\n     */\n    int type;\n    /* bit field of REDIS_OPT_xxx */\n    int options;\n    /* timeout value. if NULL, no timeout is used */\n    const struct timeval *timeout;\n    union {\n        /** use this field for tcp/ip connections */\n        struct {\n            const char *source_addr;\n            const char *ip;\n            int port;\n        } tcp;\n        /** use this field for unix domain sockets */\n        const char *unix_socket;\n        /**\n         * use this field to have hiredis operate an already-open\n         * file descriptor */\n        redisFD fd;\n    } endpoint;\n} redisOptions;\n\n/**\n * Helper macros to initialize options to their specified fields.\n */\n#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \\\n    (opts)->type = REDIS_CONN_TCP; \\\n    (opts)->endpoint.tcp.ip = ip_; \\\n    (opts)->endpoint.tcp.port = port_;\n\n#define REDIS_OPTIONS_SET_UNIX(opts, path) \\\n    (opts)->type = REDIS_CONN_UNIX;        \\\n    (opts)->endpoint.unix_socket = path;\n\nstruct redisAsyncContext;\nstruct redisContext;\n\ntypedef struct redisContextFuncs {\n    void (*free_privdata)(void *);\n    void (*async_read)(struct redisAsyncContext *);\n    void (*async_write)(struct redisAsyncContext *);\n    int (*read)(struct redisContext *, char *, size_t);\n    int (*write)(struct redisContext *);\n} redisContextFuncs;\n\n/* Context for a connection to Redis */\ntypedef struct redisContext {\n    const redisContextFuncs *funcs;   /* Function table */\n\n    int err; /* Error flags, 0 when there is no error */\n    char errstr[128]; /* String representation of error when applicable */\n    redisFD fd;\n    int flags;\n    char *obuf; /* Write buffer */\n    redisReader *reader; /* Protocol reader */\n\n    enum redisConnectionType connection_type;\n    struct timeval *timeout;\n\n    struct {\n        char *host;\n        char *source_addr;\n        int port;\n    } tcp;\n\n    struct {\n        char *path;\n    } unix_sock;\n\n    /* For non-blocking connect */\n    struct sockadr *saddr;\n    size_t addrlen;\n\n    /* Additional private data for hiredis addons such as SSL */\n    void *privdata;\n} redisContext;\n\nredisContext *redisConnectWithOptions(const redisOptions *options);\nredisContext *redisConnect(const char *ip, int port);\nredisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);\nredisContext *redisConnectNonBlock(const char *ip, int port);\nredisContext *redisConnectBindNonBlock(const char *ip, int port,\n                                       const char *source_addr);\nredisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,\n                                                const char *source_addr);\nredisContext *redisConnectUnix(const char *path);\nredisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);\nredisContext *redisConnectUnixNonBlock(const char *path);\nredisContext *redisConnectFd(redisFD fd);\n\n/**\n * Reconnect the given context using the saved information.\n *\n * This re-uses the exact same connect options as in the initial connection.\n * host, ip (or path), timeout and bind address are reused,\n * flags are used unmodified from the existing context.\n *\n * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.\n */\nint redisReconnect(redisContext *c);\n\nint redisSetTimeout(redisContext *c, const struct timeval tv);\nint redisEnableKeepAlive(redisContext *c);\nvoid redisFree(redisContext *c);\nredisFD redisFreeKeepFd(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 formatted command to the output buffer. Use these functions in blocking mode\n * to get a pipeline of commands. */\nint redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);\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": "Chapter02/redis-6.0.3/deps/hiredis/hiredis.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\nincludedir=${prefix}/include\npkgincludedir=${includedir}/hiredis\n\nName: hiredis\nDescription: Minimalistic C client library for Redis.\nVersion: @PROJECT_VERSION@\nLibs: -L${libdir} -lhiredis\nCflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/hiredis_ssl.h",
    "content": "\n/*\n * Copyright (c) 2019, Redis Labs\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_SSL_H\n#define __HIREDIS_SSL_H\n\n/* This is the underlying struct for SSL in ssl.h, which is not included to\n * keep build dependencies short here.\n */\nstruct ssl_st;\n\n/**\n * Secure the connection using SSL. This should be done before any command is\n * executed on the connection.\n */\nint redisSecureConnection(redisContext *c, const char *capath, const char *certpath,\n                          const char *keypath, const char *servername);\n\n/**\n * Initiate SSL/TLS negotiation on a provided context.\n */\n\nint redisInitiateSSL(redisContext *c, struct ssl_st *ssl);\n\n#endif  /* __HIREDIS_SSL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/hiredis_ssl.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\nincludedir=${prefix}/include\npkgincludedir=${includedir}/hiredis\n\nName: hiredis_ssl\nDescription: SSL Support for hiredis.\nVersion: @PROJECT_VERSION@\nRequires: hiredis\nLibs: -L${libdir} -lhiredis_ssl\nLibs.private: -lssl -lcrypto\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/net.c",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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 <fcntl.h>\n#include <string.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <limits.h>\n#include <stdlib.h>\n\n#include \"net.h\"\n#include \"sds.h\"\n#include \"sockcompat.h\"\n#include \"win32.h\"\n\n/* Defined in hiredis.c */\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\nvoid redisNetClose(redisContext *c) {\n    if (c && c->fd != REDIS_INVALID_FD) {\n        close(c->fd);\n        c->fd = REDIS_INVALID_FD;\n    }\n}\n\nint redisNetRead(redisContext *c, char *buf, size_t bufcap) {\n    int nread = recv(c->fd, buf, bufcap, 0);\n    if (nread == -1) {\n        if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n            return 0;\n        } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {\n            /* especially in windows */\n            __redisSetError(c, REDIS_ERR_TIMEOUT, \"recv timeout\");\n            return -1;\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    } else if (nread == 0) {\n        __redisSetError(c, REDIS_ERR_EOF, \"Server closed the connection\");\n        return -1;\n    } else {\n        return nread;\n    }\n}\n\nint redisNetWrite(redisContext *c) {\n    int nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);\n    if (nwritten < 0) {\n        if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n    return nwritten;\n}\n\nstatic void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {\n    int errorno = errno;  /* snprintf() may change errno */\n    char buf[128] = { 0 };\n    size_t len = 0;\n\n    if (prefix != NULL)\n        len = snprintf(buf,sizeof(buf),\"%s: \",prefix);\n    strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);\n    __redisSetError(c,type,buf);\n}\n\nstatic int redisSetReuseAddr(redisContext *c) {\n    int on = 1;\n    if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int redisCreateSocket(redisContext *c, int type) {\n    redisFD s;\n    if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n    c->fd = s;\n    if (type == AF_INET) {\n        if (redisSetReuseAddr(c) == REDIS_ERR) {\n            return REDIS_ERR;\n        }\n    }\n    return REDIS_OK;\n}\n\nstatic int redisSetBlocking(redisContext *c, int blocking) {\n#ifndef _WIN32\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(c->fd, F_GETFL)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_GETFL)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n\n    if (blocking)\n        flags &= ~O_NONBLOCK;\n    else\n        flags |= O_NONBLOCK;\n\n    if (fcntl(c->fd, F_SETFL, flags) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_SETFL)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n#else\n    u_long mode = blocking ? 0 : 1;\n    if (ioctl(c->fd, FIONBIO, &mode) == -1) {\n        __redisSetErrorFromErrno(c, REDIS_ERR_IO, \"ioctl(FIONBIO)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n#endif /* _WIN32 */\n    return REDIS_OK;\n}\n\nint redisKeepAlive(redisContext *c, int interval) {\n    int val = 1;\n    redisFD fd = c->fd;\n\n    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = interval;\n\n#if defined(__APPLE__) && defined(__MACH__)\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n#else\n#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = interval/3;\n    if (val == 0) val = 1;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = 3;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n#endif\n#endif\n\n    return REDIS_OK;\n}\n\nstatic int redisSetTcpNoDelay(redisContext *c) {\n    int yes = 1;\n    if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(TCP_NODELAY)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\n#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)\n\nstatic int redisContextTimeoutMsec(redisContext *c, long *result)\n{\n    const struct timeval *timeout = c->timeout;\n    long msec = -1;\n\n    /* Only use timeout when not NULL. */\n    if (timeout != NULL) {\n        if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {\n            *result = msec;\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    *result = msec;\n    return REDIS_OK;\n}\n\nstatic int redisContextWaitReady(redisContext *c, long msec) {\n    struct pollfd   wfd[1];\n\n    wfd[0].fd     = c->fd;\n    wfd[0].events = POLLOUT;\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            redisNetClose(c);\n            return REDIS_ERR;\n        } else if (res == 0) {\n            errno = ETIMEDOUT;\n            __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n            redisNetClose(c);\n            return REDIS_ERR;\n        }\n\n        if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {\n            redisCheckSocketError(c);\n            return REDIS_ERR;\n        }\n\n        return REDIS_OK;\n    }\n\n    __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n    redisNetClose(c);\n    return REDIS_ERR;\n}\n\nint redisCheckConnectDone(redisContext *c, int *completed) {\n    int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);\n    if (rc == 0) {\n        *completed = 1;\n        return REDIS_OK;\n    }\n    switch (errno) {\n    case EISCONN:\n        *completed = 1;\n        return REDIS_OK;\n    case EALREADY:\n    case EINPROGRESS:\n    case EWOULDBLOCK:\n        *completed = 0;\n        return REDIS_OK;\n    default:\n        return REDIS_ERR;\n    }\n}\n\nint redisCheckSocketError(redisContext *c) {\n    int err = 0, errno_saved = errno;\n    socklen_t errlen = sizeof(err);\n\n    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"getsockopt(SO_ERROR)\");\n        return REDIS_ERR;\n    }\n\n    if (err == 0) {\n        err = errno_saved;\n    }\n\n    if (err) {\n        errno = err;\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n\n    return REDIS_OK;\n}\n\nint redisContextSetTimeout(redisContext *c, const struct timeval tv) {\n    const void *to_ptr = &tv;\n    size_t to_sz = sizeof(tv);\n#ifdef _WIN32\n    DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;\n    to_ptr = &timeout_msec;\n    to_sz = sizeof(timeout_msec);\n#endif\n    if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_RCVTIMEO)\");\n        return REDIS_ERR;\n    }\n    if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_SNDTIMEO)\");\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int _redisContextConnectTcp(redisContext *c, const char *addr, int port,\n                                   const struct timeval *timeout,\n                                   const char *source_addr) {\n    redisFD s;\n    int rv, n;\n    char _port[6];  /* strlen(\"65535\"); */\n    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;\n    int blocking = (c->flags & REDIS_BLOCK);\n    int reuseaddr = (c->flags & REDIS_REUSEADDR);\n    int reuses = 0;\n    long timeout_msec = -1;\n\n    servinfo = NULL;\n    c->connection_type = REDIS_CONN_TCP;\n    c->tcp.port = port;\n\n    /* We need to take possession of the passed parameters\n     * to make them reusable for a reconnect.\n     * We also carefully check we don't free data we already own,\n     * as in the case of the reconnect method.\n     *\n     * This is a bit ugly, but atleast it works and doesn't leak memory.\n     **/\n    if (c->tcp.host != addr) {\n        free(c->tcp.host);\n\n        c->tcp.host = strdup(addr);\n    }\n\n    if (timeout) {\n        if (c->timeout != timeout) {\n            if (c->timeout == NULL)\n                c->timeout = malloc(sizeof(struct timeval));\n\n            memcpy(c->timeout, timeout, sizeof(struct timeval));\n        }\n    } else {\n        free(c->timeout);\n        c->timeout = NULL;\n    }\n\n    if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {\n        __redisSetError(c, REDIS_ERR_IO, \"Invalid timeout specified\");\n        goto error;\n    }\n\n    if (source_addr == NULL) {\n        free(c->tcp.source_addr);\n        c->tcp.source_addr = NULL;\n    } else if (c->tcp.source_addr != source_addr) {\n        free(c->tcp.source_addr);\n        c->tcp.source_addr = strdup(source_addr);\n    }\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(c->tcp.host,_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) {\naddrretry:\n        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)\n            continue;\n\n        c->fd = s;\n        if (redisSetBlocking(c,0) != REDIS_OK)\n            goto error;\n        if (c->tcp.source_addr) {\n            int bound = 0;\n            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */\n            if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {\n                char buf[128];\n                snprintf(buf,sizeof(buf),\"Can't get addr: %s\",gai_strerror(rv));\n                __redisSetError(c,REDIS_ERR_OTHER,buf);\n                goto error;\n            }\n\n            if (reuseaddr) {\n                n = 1;\n                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,\n                               sizeof(n)) < 0) {\n                    freeaddrinfo(bservinfo);\n                    goto error;\n                }\n            }\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            freeaddrinfo(bservinfo);\n            if (!bound) {\n                char buf[128];\n                snprintf(buf,sizeof(buf),\"Can't bind socket: %s\",strerror(errno));\n                __redisSetError(c,REDIS_ERR_OTHER,buf);\n                goto error;\n            }\n        }\n\n        /* For repeat connection */\n        free(c->saddr);\n        c->saddr = malloc(p->ai_addrlen);\n        memcpy(c->saddr, p->ai_addr, p->ai_addrlen);\n        c->addrlen = p->ai_addrlen;\n\n        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {\n            if (errno == EHOSTUNREACH) {\n                redisNetClose(c);\n                continue;\n            } else if (errno == EINPROGRESS) {\n                if (blocking) {\n                    goto wait_for_ready;\n                }\n                /* This is ok.\n                 * Note that even when it's in blocking mode, we unset blocking\n                 * for `connect()`\n                 */\n            } else if (errno == EADDRNOTAVAIL && reuseaddr) {\n                if (++reuses >= REDIS_CONNECT_RETRIES) {\n                    goto error;\n                } else {\n                    redisNetClose(c);\n                    goto addrretry;\n                }\n            } else {\n                wait_for_ready:\n                if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)\n                    goto error;\n            }\n        }\n        if (blocking && redisSetBlocking(c,1) != REDIS_OK)\n            goto error;\n        if (redisSetTcpNoDelay(c) != REDIS_OK)\n            goto error;\n\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    if(servinfo) {\n        freeaddrinfo(servinfo);\n    }\n\n    return rv;  // Need to return REDIS_OK if alright\n}\n\nint redisContextConnectTcp(redisContext *c, const char *addr, int port,\n                           const struct timeval *timeout) {\n    return _redisContextConnectTcp(c, addr, port, timeout, NULL);\n}\n\nint redisContextConnectBindTcp(redisContext *c, const char *addr, int port,\n                               const struct timeval *timeout,\n                               const char *source_addr) {\n    return _redisContextConnectTcp(c, addr, port, timeout, source_addr);\n}\n\nint redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {\n#ifndef _WIN32\n    int blocking = (c->flags & REDIS_BLOCK);\n    struct sockaddr_un *sa;\n    long timeout_msec = -1;\n\n    if (redisCreateSocket(c,AF_UNIX) < 0)\n        return REDIS_ERR;\n    if (redisSetBlocking(c,0) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->connection_type = REDIS_CONN_UNIX;\n    if (c->unix_sock.path != path)\n        c->unix_sock.path = strdup(path);\n\n    if (timeout) {\n        if (c->timeout != timeout) {\n            if (c->timeout == NULL)\n                c->timeout = malloc(sizeof(struct timeval));\n\n            memcpy(c->timeout, timeout, sizeof(struct timeval));\n        }\n    } else {\n        free(c->timeout);\n        c->timeout = NULL;\n    }\n\n    if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)\n        return REDIS_ERR;\n\n    sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));\n    c->addrlen = sizeof(struct sockaddr_un);\n    sa->sun_family = AF_UNIX;\n    strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);\n    if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {\n        if (errno == EINPROGRESS && !blocking) {\n            /* This is ok. */\n        } else {\n            if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)\n                return REDIS_ERR;\n        }\n    }\n\n    /* Reset socket to be blocking after connect(2). */\n    if (blocking && redisSetBlocking(c,1) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->flags |= REDIS_CONNECTED;\n    return REDIS_OK;\n#else\n    /* We currently do not support Unix sockets for Windows. */\n    /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */\n    errno = EPROTONOSUPPORT;\n    return REDIS_ERR;\n#endif /* _WIN32 */\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/net.h",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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\nvoid redisNetClose(redisContext *c);\nint redisNetRead(redisContext *c, char *buf, size_t bufcap);\nint redisNetWrite(redisContext *c);\n\nint redisCheckSocketError(redisContext *c);\nint redisContextSetTimeout(redisContext *c, const struct timeval tv);\nint redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);\nint redisContextConnectBindTcp(redisContext *c, const char *addr, int port,\n                               const struct timeval *timeout,\n                               const char *source_addr);\nint redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);\nint redisKeepAlive(redisContext *c, int interval);\nint redisCheckConnectDone(redisContext *c, int *completed);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/read.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#ifndef _MSC_VER\n#include <unistd.h>\n#include <strings.h>\n#endif\n#include <assert.h>\n#include <errno.h>\n#include <ctype.h>\n#include <limits.h>\n#include <math.h>\n\n#include \"read.h\"\n#include \"sds.h\"\n#include \"win32.h\"\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    sdsfree(r->buf);\n    r->buf = NULL;\n    r->pos = r->len = 0;\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 (pos==_len) {\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/* Convert a string into a long long. Returns REDIS_OK if the string could be\n * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value\n * will be set to the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a long long: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. */\nstatic int 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 REDIS_ERR;\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 REDIS_OK;\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 REDIS_ERR;\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 REDIS_OK;\n    } else {\n        return REDIS_ERR;\n    }\n\n    while (plen < slen && p[0] >= '0' && p[0] <= '9') {\n        if (v > (ULLONG_MAX / 10)) /* Overflow. */\n            return REDIS_ERR;\n        v *= 10;\n\n        if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */\n            return REDIS_ERR;\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 REDIS_ERR;\n\n    if (negative) {\n        if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */\n            return REDIS_ERR;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > LLONG_MAX) /* Overflow. */\n            return REDIS_ERR;\n        if (value != NULL) *value = v;\n    }\n    return REDIS_OK;\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               prv->type == REDIS_REPLY_MAP ||\n               prv->type == REDIS_REPLY_SET);\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                long long v;\n                if (string2ll(p, len, &v) == REDIS_ERR) {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Bad integer value\");\n                    return REDIS_ERR;\n                }\n                obj = r->fn->createInteger(cur,v);\n            } else {\n                obj = (void*)REDIS_REPLY_INTEGER;\n            }\n        } else if (cur->type == REDIS_REPLY_DOUBLE) {\n            if (r->fn && r->fn->createDouble) {\n                char buf[326], *eptr;\n                double d;\n\n                if ((size_t)len >= sizeof(buf)) {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Double value is too large\");\n                    return REDIS_ERR;\n                }\n\n                memcpy(buf,p,len);\n                buf[len] = '\\0';\n\n                if (strcasecmp(buf,\",inf\") == 0) {\n                    d = INFINITY; /* Positive infinite. */\n                } else if (strcasecmp(buf,\",-inf\") == 0) {\n                    d = -INFINITY; /* Nevative infinite. */\n                } else {\n                    d = strtod((char*)buf,&eptr);\n                    if (buf[0] == '\\0' || eptr[0] != '\\0' || isnan(d)) {\n                        __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                                \"Bad double value\");\n                        return REDIS_ERR;\n                    }\n                }\n                obj = r->fn->createDouble(cur,d,buf,len);\n            } else {\n                obj = (void*)REDIS_REPLY_DOUBLE;\n            }\n        } else if (cur->type == REDIS_REPLY_NIL) {\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n        } else if (cur->type == REDIS_REPLY_BOOL) {\n            int bval = p[0] == 't' || p[0] == 'T';\n            if (r->fn && r->fn->createBool)\n                obj = r->fn->createBool(cur,bval);\n            else\n                obj = (void*)REDIS_REPLY_BOOL;\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 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\n        if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bad bulk string length\");\n            return REDIS_ERR;\n        }\n\n        if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bulk string length out of range\");\n            return REDIS_ERR;\n        }\n\n        if (len == -1) {\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 ((cur->type == REDIS_REPLY_VERB && len < 4) ||\n                    (cur->type == REDIS_REPLY_VERB && s[5] != ':'))\n                {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Verbatim string 4 bytes of content type are \"\n                            \"missing or incorrectly encoded.\");\n                    return REDIS_ERR;\n                }\n                if (r->fn && r->fn->createString)\n                    obj = r->fn->createString(cur,s+2,len);\n                else\n                    obj = (void*)(long)cur->type;\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\n/* Process the array, map and set types. */\nstatic int processAggregateItem(redisReader *r) {\n    redisReadTask *cur = &(r->rstack[r->ridx]);\n    void *obj;\n    char *p;\n    long long elements;\n    int root = 0, len;\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,&len)) != NULL) {\n        if (string2ll(p, len, &elements) == REDIS_ERR) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bad multi-bulk length\");\n            return REDIS_ERR;\n        }\n\n        root = (r->ridx == 0);\n\n        if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Multi-bulk length out of range\");\n            return REDIS_ERR;\n        }\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 (cur->type == REDIS_REPLY_MAP) elements *= 2;\n\n            if (r->fn && r->fn->createArray)\n                obj = r->fn->createArray(cur,elements);\n            else\n                obj = (void*)(long)cur->type;\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_DOUBLE;\n                break;\n            case '_':\n                cur->type = REDIS_REPLY_NIL;\n                break;\n            case '$':\n                cur->type = REDIS_REPLY_STRING;\n                break;\n            case '*':\n                cur->type = REDIS_REPLY_ARRAY;\n                break;\n            case '%':\n                cur->type = REDIS_REPLY_MAP;\n                break;\n            case '~':\n                cur->type = REDIS_REPLY_SET;\n                break;\n            case '#':\n                cur->type = REDIS_REPLY_BOOL;\n                break;\n            case '=':\n                cur->type = REDIS_REPLY_VERB;\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    case REDIS_REPLY_DOUBLE:\n    case REDIS_REPLY_NIL:\n    case REDIS_REPLY_BOOL:\n        return processLineItem(r);\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        return processBulkItem(r);\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n        return processAggregateItem(r);\n    default:\n        assert(NULL);\n        return REDIS_ERR; /* Avoid warning. */\n    }\n}\n\nredisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {\n    redisReader *r;\n\n    r = calloc(1,sizeof(redisReader));\n    if (r == NULL)\n        return NULL;\n\n    r->fn = fn;\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 == NULL)\n        return;\n    if (r->reply != NULL && r->fn && r->fn->freeObject)\n        r->fn->freeObject(r->reply);\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        } else if (r->reply != NULL && r->fn && r->fn->freeObject) {\n            r->fn->freeObject(r->reply);\n        }\n        r->reply = NULL;\n    }\n    return REDIS_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/read.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\n#ifndef __HIREDIS_READ_H\n#define __HIREDIS_READ_H\n#include <stdio.h> /* for size_t */\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 occurred. 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_TIMEOUT 6 /* Timed out */\n#define REDIS_ERR_OTHER 2 /* Everything else... */\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#define REDIS_REPLY_DOUBLE 7\n#define REDIS_REPLY_BOOL 8\n#define REDIS_REPLY_MAP 9\n#define REDIS_REPLY_SET 10\n#define REDIS_REPLY_ATTR 11\n#define REDIS_REPLY_PUSH 12\n#define REDIS_REPLY_BIGNUM 13\n#define REDIS_REPLY_VERB 14\n\n#define REDIS_READER_MAX_BUF (1024*16)  /* Default max unused reader buffer. */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\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*, size_t);\n    void *(*createInteger)(const redisReadTask*, long long);\n    void *(*createDouble)(const redisReadTask*, double, char*, size_t);\n    void *(*createNil)(const redisReadTask*);\n    void *(*createBool)(const redisReadTask*, int);\n    void (*freeObject)(void*);\n} redisReplyObjectFunctions;\n\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 *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);\nvoid redisReaderFree(redisReader *r);\nint redisReaderFeed(redisReader *r, const char *buf, size_t len);\nint redisReaderGetReply(redisReader *r, void **reply);\n\n#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))\n#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)\n#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/sds.c",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include \"sds.h\"\n#include \"sdsalloc.h\"\n\nstatic inline int sdsHdrSize(char type) {\n    switch(type&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return sizeof(struct sdshdr5);\n        case SDS_TYPE_8:\n            return sizeof(struct sdshdr8);\n        case SDS_TYPE_16:\n            return sizeof(struct sdshdr16);\n        case SDS_TYPE_32:\n            return sizeof(struct sdshdr32);\n        case SDS_TYPE_64:\n            return sizeof(struct sdshdr64);\n    }\n    return 0;\n}\n\nstatic inline char sdsReqType(size_t string_size) {\n    if (string_size < 32)\n        return SDS_TYPE_5;\n    if (string_size < 0xff)\n        return SDS_TYPE_8;\n    if (string_size < 0xffff)\n        return SDS_TYPE_16;\n    if (string_size < 0xffffffff)\n        return SDS_TYPE_32;\n    return SDS_TYPE_64;\n}\n\n/* Create a new sds string with the content specified by the 'init' pointer\n * and 'initlen'.\n * If NULL is used for 'init' the string is initialized with zero bytes.\n *\n * The string is always null-termined (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3);\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    void *sh;\n    sds s;\n    char type = sdsReqType(initlen);\n    /* Empty strings are usually created in order to append. Use type 8\n     * since type 5 is not good at this. */\n    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;\n    int hdrlen = sdsHdrSize(type);\n    unsigned char *fp; /* flags pointer. */\n\n    sh = s_malloc(hdrlen+initlen+1);\n    if (sh == NULL) return NULL;\n    if (!init)\n        memset(sh, 0, hdrlen+initlen+1);\n    s = (char*)sh+hdrlen;\n    fp = ((unsigned char*)s)-1;\n    switch(type) {\n        case SDS_TYPE_5: {\n            *fp = type | (initlen << SDS_TYPE_BITS);\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n    }\n    if (initlen && init)\n        memcpy(s, init, initlen);\n    s[initlen] = '\\0';\n    return s;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null terminated C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    s_free((char*)s-sdsHdrSize(s[-1]));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    int reallen = strlen(s);\n    sdssetlen(s, reallen);\n}\n\n/* Modify an sds string in-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    sdssetlen(s, 0);\n    s[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n    void *sh, *newsh;\n    size_t avail = sdsavail(s);\n    size_t len, newlen;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n\n    /* Return ASAP if there is enough space left. */\n    if (avail >= addlen) return s;\n\n    len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n\n    type = sdsReqType(newlen);\n\n    /* Don't use type 5: the user is appending to the string and type 5 is\n     * not able to remember empty space, so sdsMakeRoomFor() must be called\n     * at every appending operation. */\n    if (type == SDS_TYPE_5) type = SDS_TYPE_8;\n\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+newlen+1);\n        if (newsh == NULL) {\n            s_free(sh);\n            return NULL;\n        }\n        s = (char*)newsh+hdrlen;\n    } else {\n        /* Since the header size changes, need to move the string forward,\n         * and can't use realloc */\n        newsh = s_malloc(hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, newlen);\n    return s;\n}\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    void *sh, *newsh;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n    size_t len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n\n    type = sdsReqType(len);\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        newsh = s_malloc(hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, len);\n    return s;\n}\n\n/* Return the total size of the allocation of the specifed sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    size_t alloc = sdsalloc(s);\n    return sdsHdrSize(s[-1])+alloc+1;\n}\n\n/* Return the pointer of the actual SDS allocation (normally SDS strings\n * are referenced by the start of the string buffer). */\nvoid *sdsAllocPtr(sds s) {\n    return (void*) (s-sdsHdrSize(s[-1]));\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, int incr) {\n    unsigned char flags = s[-1];\n    size_t len;\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            unsigned char *fp = ((unsigned char*)s)-1;\n            unsigned char oldlen = SDS_TYPE_5_LEN(flags);\n            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));\n            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);\n            len = oldlen+incr;\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        default: len = 0; /* Just to avoid compilation warnings. */\n    }\n    s[len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    size_t curlen = sdslen(s);\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    memcpy(s+curlen, t, len);\n    sdssetlen(s, curlen+len);\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    if (sdsalloc(s) < len) {\n        s = sdsMakeRoomFor(s,len-sdslen(s));\n        if (s == NULL) return NULL;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-termined string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the length of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[SDS_LLSTR_SIZE];\n    int len = sdsll2str(buf,value);\n\n    return sdsnewlen(buf,len);\n}\n\n/* Like sdscatprintf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char staticbuf[1024], *buf = staticbuf, *t;\n    size_t buflen = strlen(fmt)*2;\n\n    /* We try to start using a static buffer for speed.\n     * If not possible we revert to heap allocation. */\n    if (buflen > sizeof(staticbuf)) {\n        buf = s_malloc(buflen);\n        if (buf == NULL) return NULL;\n    } else {\n        buflen = sizeof(staticbuf);\n    }\n\n    /* Try with buffers two times bigger every time we fail to\n     * fit the string in the current buffer size. */\n    while(1) {\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        va_end(cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) s_free(buf);\n            buflen *= 2;\n            buf = s_malloc(buflen);\n            if (buf == NULL) return NULL;\n            continue;\n        }\n        break;\n    }\n\n    /* Finally concat the obtained string to the SDS string and return it. */\n    t = sdscat(s, buf);\n    if (buf != staticbuf) s_free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b).\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    const char *f = fmt;\n    int i;\n    va_list ap;\n\n    va_start(ap,fmt);\n    i = sdslen(s); /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        size_t l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sdsavail(s)==0) {\n            s = sdsMakeRoomFor(s,1);\n            if (s == NULL) goto fmt_error;\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sdsavail(s) < l) {\n                    s = sdsMakeRoomFor(s,l);\n                    if (s == NULL) goto fmt_error;\n                }\n                memcpy(s+i,str,l);\n                sdsinclen(s,l);\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        if (s == NULL) goto fmt_error;\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else\n                    unum = va_arg(ap,unsigned long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        if (s == NULL) goto fmt_error;\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sdsinclen(s,1);\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sdsinclen(s,1);\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n\nfmt_error:\n    va_end(ap);\n    return NULL;\n}\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"Aa. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"Hello World\".\n */\nsds sdstrim(sds s, const char *cset) {\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > sp && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (s != sp) memmove(s, sp, len);\n    s[len] = '\\0';\n    sdssetlen(s,len);\n    return s;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nvoid sdsrange(sds s, int start, int end) {\n    size_t newlen, len = sdslen(s);\n\n    if (len == 0) return;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (signed)len) {\n            newlen = 0;\n        } else if (end >= (signed)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(s, s+start, newlen);\n    s[newlen] = 0;\n    sdssetlen(s,newlen);\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = tolower(s[j]);\n}\n\n/* Apply toupper() to every character of the sds string 's'. */\nvoid sdstoupper(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     positive if s1 > s2.\n *     negative if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1-l2;\n    return cmp;\n}\n\n/* Split 's' with separator in 'sep'. An array\n * of sds strings is returned. *count will be set\n * by reference to the number of tokens returned.\n *\n * On out of memory, zero length string, zero length\n * separator, NULL is returned.\n *\n * Note that 'sep' is able to split a string using\n * a multi-character separator. For example\n * sdssplit(\"foo_-_bar\",\"_-_\"); will return two\n * elements \"foo\" and \"bar\".\n *\n * This version of the function is binary-safe but\n * requires length arguments. sdssplit() is just the\n * same function but for zero-terminated strings.\n */\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5, start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = s_malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = s_realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        s_free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    s_free(tokens);\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\nint is_hex_digit(char c) {\n    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||\n           (c >= 'A' && c <= 'F');\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             is_hex_digit(*(p+2)) &&\n                                             is_hex_digit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            {\n                char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));\n                if (new_vector == NULL) {\n                    s_free(vector);\n                    return NULL;\n                }\n\n                vector = new_vector;\n                vector[*argc] = current;\n                (*argc)++;\n                current = NULL;\n            }\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = s_malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    s_free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscat(join,sep);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n/* Wrappers to the allocators used by SDS. Note that SDS will actually\n * just use the macros defined into sdsalloc.h in order to avoid to pay\n * the overhead of function calls. Here we define these wrappers only for\n * the programs SDS is linked to, if they want to touch the SDS internals\n * even if they use a different allocator. */\nvoid *sds_malloc(size_t size) { return s_malloc(size); }\nvoid *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }\nvoid sds_free(void *ptr) { s_free(ptr); }\n\n#if defined(SDS_TEST_MAIN)\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.h\"\n\n#define UNUSED(x) (void)(x)\nint sdsTest(void) {\n    {\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"Hello %s World %I,%I--\", \"Hi!\", LLONG_MIN,LLONG_MAX);\n        test_cond(\"sdscatfmt() seems working in the base case\",\n            sdslen(x) == 60 &&\n            memcmp(x,\"--Hello Hi! World -9223372036854775808,\"\n                     \"9223372036854775807--\",60) == 0)\n        printf(\"[%s]\\n\",x);\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"%u,%U--\", UINT_MAX, ULLONG_MAX);\n        test_cond(\"sdscatfmt() seems working with unsigned numbers\",\n            sdslen(x) == 35 &&\n            memcmp(x,\"--4294967295,18446744073709551615--\",35) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" x\");\n        test_cond(\"sdstrim() works when all chars match\",\n            sdslen(x) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" \");\n        test_cond(\"sdstrim() works when a single char remains\",\n            sdslen(x) == 1 && x[0] == 'x')\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            unsigned int oldfree;\n            char *p;\n            int step = 10, j, i;\n\n            sdsfree(x);\n            sdsfree(y);\n            x = sdsnew(\"0\");\n            test_cond(\"sdsnew() free/len buffers\", sdslen(x) == 1 && sdsavail(x) == 0);\n\n            /* Run the test a few times in order to hit the first two\n             * SDS header types. */\n            for (i = 0; i < 10; i++) {\n                int oldlen = sdslen(x);\n                x = sdsMakeRoomFor(x,step);\n                int type = x[-1]&SDS_TYPE_MASK;\n\n                test_cond(\"sdsMakeRoomFor() len\", sdslen(x) == oldlen);\n                if (type != SDS_TYPE_5) {\n                    test_cond(\"sdsMakeRoomFor() free\", sdsavail(x) >= step);\n                    oldfree = sdsavail(x);\n                }\n                p = x+oldlen;\n                for (j = 0; j < step; j++) {\n                    p[j] = 'A'+j;\n                }\n                sdsIncrLen(x,step);\n            }\n            test_cond(\"sdsMakeRoomFor() content\",\n                memcmp(\"0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ\",x,101) == 0);\n            test_cond(\"sdsMakeRoomFor() final length\",sdslen(x)==101);\n\n            sdsfree(x);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n\n#ifdef SDS_TEST_MAIN\nint main(void) {\n    return sdsTest();\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/sds.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\n#ifdef _MSC_VER\n#define __attribute__(x)\n#endif\n\n#include <sys/types.h>\n#include <stdarg.h>\n#include <stdint.h>\n\ntypedef char *sds;\n\n/* Note: sdshdr5 is never used, we just access the flags byte directly.\n * However is here to document the layout of type 5 SDS strings. */\nstruct __attribute__ ((__packed__)) sdshdr5 {\n    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr8 {\n    uint8_t len; /* used */\n    uint8_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr16 {\n    uint16_t len; /* used */\n    uint16_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr32 {\n    uint32_t len; /* used */\n    uint32_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr64 {\n    uint64_t len; /* used */\n    uint64_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\n\n#define SDS_TYPE_5  0\n#define SDS_TYPE_8  1\n#define SDS_TYPE_16 2\n#define SDS_TYPE_32 3\n#define SDS_TYPE_64 4\n#define SDS_TYPE_MASK 7\n#define SDS_TYPE_BITS 3\n#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));\n#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))\n#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)\n\nstatic inline size_t sdslen(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->len;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->len;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->len;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->len;\n    }\n    return 0;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            return 0;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            return sh->alloc - sh->len;\n        }\n    }\n    return 0;\n}\n\nstatic inline void sdssetlen(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len = (uint8_t)newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len = (uint16_t)newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len = (uint32_t)newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len = (uint64_t)newlen;\n            break;\n    }\n}\n\nstatic inline void sdsinclen(sds s, size_t inc) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len += (uint8_t)inc;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len += (uint16_t)inc;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len += (uint32_t)inc;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len += (uint64_t)inc;\n            break;\n    }\n}\n\n/* sdsalloc() = sdsavail() + sdslen() */\nstatic inline size_t sdsalloc(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->alloc;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->alloc;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->alloc;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->alloc;\n    }\n    return 0;\n}\n\nstatic inline void sdssetalloc(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            /* Nothing to do, this type has no total allocation info. */\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->alloc = (uint8_t)newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->alloc = (uint16_t)newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->alloc = (uint32_t)newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->alloc = (uint64_t)newlen;\n            break;\n    }\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nsds sdstrim(sds s, const char *cset);\nvoid sdsrange(sds s, int start, int end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, int incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\nvoid *sdsAllocPtr(sds s);\n\n/* Export the allocator used by SDS to the program using SDS.\n * Sometimes the program SDS is linked to, may use a different set of\n * allocators, but may want to allocate or free things that SDS will\n * respectively free or allocate. */\nvoid *sds_malloc(size_t size);\nvoid *sds_realloc(void *ptr, size_t size);\nvoid sds_free(void *ptr);\n\n#ifdef REDIS_TEST\nint sdsTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/sdsalloc.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* SDS allocator selection.\n *\n * This file is used in order to change the SDS allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#define s_malloc malloc\n#define s_realloc realloc\n#define s_free free\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/sockcompat.c",
    "content": "/*\n * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>\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#define REDIS_SOCKCOMPAT_IMPLEMENTATION\n#include \"sockcompat.h\"\n\n#ifdef _WIN32\nstatic int _wsaErrorToErrno(int err) {\n    switch (err) {\n        case WSAEWOULDBLOCK:\n            return EWOULDBLOCK;\n        case WSAEINPROGRESS:\n            return EINPROGRESS;\n        case WSAEALREADY:\n            return EALREADY;\n        case WSAENOTSOCK:\n            return ENOTSOCK;\n        case WSAEDESTADDRREQ:\n            return EDESTADDRREQ;\n        case WSAEMSGSIZE:\n            return EMSGSIZE;\n        case WSAEPROTOTYPE:\n            return EPROTOTYPE;\n        case WSAENOPROTOOPT:\n            return ENOPROTOOPT;\n        case WSAEPROTONOSUPPORT:\n            return EPROTONOSUPPORT;\n        case WSAEOPNOTSUPP:\n            return EOPNOTSUPP;\n        case WSAEAFNOSUPPORT:\n            return EAFNOSUPPORT;\n        case WSAEADDRINUSE:\n            return EADDRINUSE;\n        case WSAEADDRNOTAVAIL:\n            return EADDRNOTAVAIL;\n        case WSAENETDOWN:\n            return ENETDOWN;\n        case WSAENETUNREACH:\n            return ENETUNREACH;\n        case WSAENETRESET:\n            return ENETRESET;\n        case WSAECONNABORTED:\n            return ECONNABORTED;\n        case WSAECONNRESET:\n            return ECONNRESET;\n        case WSAENOBUFS:\n            return ENOBUFS;\n        case WSAEISCONN:\n            return EISCONN;\n        case WSAENOTCONN:\n            return ENOTCONN;\n        case WSAETIMEDOUT:\n            return ETIMEDOUT;\n        case WSAECONNREFUSED:\n            return ECONNREFUSED;\n        case WSAELOOP:\n            return ELOOP;\n        case WSAENAMETOOLONG:\n            return ENAMETOOLONG;\n        case WSAEHOSTUNREACH:\n            return EHOSTUNREACH;\n        case WSAENOTEMPTY:\n            return ENOTEMPTY;\n        default:\n            /* We just return a generic I/O error if we could not find a relevant error. */\n            return EIO;\n    }\n}\n\nstatic void _updateErrno(int success) {\n    errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError());\n}\n\nstatic int _initWinsock() {\n    static int s_initialized = 0;\n    if (!s_initialized) {\n        static WSADATA wsadata;\n        int err = WSAStartup(MAKEWORD(2,2), &wsadata);\n        if (err != 0) {\n            errno = _wsaErrorToErrno(err);\n            return 0;\n        }\n        s_initialized = 1;\n    }\n    return 1;\n}\n\nint win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {\n    /* Note: This function is likely to be called before other functions, so run init here. */\n    if (!_initWinsock()) {\n        return EAI_FAIL;\n    }\n\n    switch (getaddrinfo(node, service, hints, res)) {\n        case 0:                     return 0;\n        case WSATRY_AGAIN:          return EAI_AGAIN;\n        case WSAEINVAL:             return EAI_BADFLAGS;\n        case WSAEAFNOSUPPORT:       return EAI_FAMILY;\n        case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY;\n        case WSAHOST_NOT_FOUND:     return EAI_NONAME;\n        case WSATYPE_NOT_FOUND:     return EAI_SERVICE;\n        case WSAESOCKTNOSUPPORT:    return EAI_SOCKTYPE;\n        default:                    return EAI_FAIL;     /* Including WSANO_RECOVERY */\n    }\n}\n\nconst char *win32_gai_strerror(int errcode) {\n    switch (errcode) {\n        case 0:            errcode = 0;                     break;\n        case EAI_AGAIN:    errcode = WSATRY_AGAIN;          break;\n        case EAI_BADFLAGS: errcode = WSAEINVAL;             break;\n        case EAI_FAMILY:   errcode = WSAEAFNOSUPPORT;       break;\n        case EAI_MEMORY:   errcode = WSA_NOT_ENOUGH_MEMORY; break;\n        case EAI_NONAME:   errcode = WSAHOST_NOT_FOUND;     break;\n        case EAI_SERVICE:  errcode = WSATYPE_NOT_FOUND;     break;\n        case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT;    break;\n        default:           errcode = WSANO_RECOVERY;        break; /* Including EAI_FAIL */\n    }\n    return gai_strerror(errcode);\n}\n\nvoid win32_freeaddrinfo(struct addrinfo *res) {\n    freeaddrinfo(res);\n}\n\nSOCKET win32_socket(int domain, int type, int protocol) {\n    SOCKET s;\n\n    /* Note: This function is likely to be called before other functions, so run init here. */\n    if (!_initWinsock()) {\n        return INVALID_SOCKET;\n    }\n\n    _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET);\n    return s;\n}\n\nint win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) {\n    int ret = ioctlsocket(fd, (long)request, argp);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {\n    int ret = bind(sockfd, addr, addrlen);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {\n    int ret = connect(sockfd, addr, addrlen);\n    _updateErrno(ret != SOCKET_ERROR);\n\n    /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as\n     * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX\n     * logic consistent. */\n    if (errno == EWOULDBLOCK) {\n        errno = EINPROGRESS;\n    }\n\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) {\n    int ret = 0;\n    if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {\n        if (*optlen >= sizeof (struct timeval)) {\n            struct timeval *tv = optval;\n            DWORD timeout = 0;\n            socklen_t dwlen = 0;\n            ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen);\n            tv->tv_sec = timeout / 1000;\n            tv->tv_usec = (timeout * 1000) % 1000000;\n        } else {\n            ret = WSAEFAULT;\n        }\n        *optlen = sizeof (struct timeval);\n    } else {\n        ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);\n    }\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {\n    int ret = 0;\n    if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {\n        struct timeval *tv = optval;\n        DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;\n        ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));\n    } else {\n        ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen);\n    }\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_close(SOCKET fd) {\n    int ret = closesocket(fd);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) {\n    int ret = recv(sockfd, (char*)buf, (int)len, flags);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) {\n    int ret = send(sockfd, (const char*)buf, (int)len, flags);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {\n    int ret = WSAPoll(fds, nfds, timeout);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n#endif /* _WIN32 */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/sockcompat.h",
    "content": "/*\n * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>\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 __SOCKCOMPAT_H\n#define __SOCKCOMPAT_H\n\n#ifndef _WIN32\n/* For POSIX systems we use the standard BSD socket API. */\n#include <unistd.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 <netdb.h>\n#include <poll.h>\n#else\n/* For Windows we use winsock. */\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#include <stddef.h>\n\n#ifdef _MSC_VER\ntypedef signed long ssize_t;\n#endif\n\n/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */\nint win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);\nconst char *win32_gai_strerror(int errcode);\nvoid win32_freeaddrinfo(struct addrinfo *res);\nSOCKET win32_socket(int domain, int type, int protocol);\nint win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);\nint win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);\nint win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);\nint win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);\nint win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);\nint win32_close(SOCKET fd);\nssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);\nssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);\ntypedef ULONG nfds_t;\nint win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);\n\n#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION\n#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)\n#undef gai_strerror\n#define gai_strerror(errcode) win32_gai_strerror(errcode)\n#define freeaddrinfo(res) win32_freeaddrinfo(res)\n#define socket(domain, type, protocol) win32_socket(domain, type, protocol)\n#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)\n#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)\n#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)\n#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)\n#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)\n#define close(fd) win32_close(fd)\n#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)\n#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)\n#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)\n#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */\n#endif /* _WIN32 */\n\n#endif /* __SOCKCOMPAT_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/ssl.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 * Copyright (c) 2019, Redis Labs\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 \"hiredis.h\"\n#include \"async.h\"\n\n#include <assert.h>\n#include <pthread.h>\n#include <errno.h>\n#include <string.h>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#include \"async_private.h\"\n\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\n/* The SSL context is attached to SSL/TLS connections as a privdata. */\ntypedef struct redisSSLContext {\n    /**\n     * OpenSSL SSL_CTX; It is optional and will not be set when using\n     * user-supplied SSL.\n     */\n    SSL_CTX *ssl_ctx;\n\n    /**\n     * OpenSSL SSL object.\n     */\n    SSL *ssl;\n\n    /**\n     * SSL_write() requires to be called again with the same arguments it was\n     * previously called with in the event of an SSL_read/SSL_write situation\n     */\n    size_t lastLen;\n\n    /** Whether the SSL layer requires read (possibly before a write) */\n    int wantRead;\n\n    /**\n     * Whether a write was requested prior to a read. If set, the write()\n     * should resume whenever a read takes place, if possible\n     */\n    int pendingWrite;\n} redisSSLContext;\n\n/* Forward declaration */\nredisContextFuncs redisContextSSLFuncs;\n\n#ifdef HIREDIS_SSL_TRACE\n/**\n * Callback used for debugging\n */\nstatic void sslLogCallback(const SSL *ssl, int where, int ret) {\n    const char *retstr = \"\";\n    int should_log = 1;\n    /* Ignore low-level SSL stuff */\n\n    if (where & SSL_CB_ALERT) {\n        should_log = 1;\n    }\n    if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {\n        should_log = 1;\n    }\n    if ((where & SSL_CB_EXIT) && ret == 0) {\n        should_log = 1;\n    }\n\n    if (!should_log) {\n        return;\n    }\n\n    retstr = SSL_alert_type_string(ret);\n    printf(\"ST(0x%x). %s. R(0x%x)%s\\n\", where, SSL_state_string_long(ssl), ret, retstr);\n\n    if (where == SSL_CB_HANDSHAKE_DONE) {\n        printf(\"Using SSL version %s. Cipher=%s\\n\", SSL_get_version(ssl), SSL_get_cipher_name(ssl));\n    }\n}\n#endif\n\n/**\n * OpenSSL global initialization and locking handling callbacks.\n * Note that this is only required for OpenSSL < 1.1.0.\n */\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#define HIREDIS_USE_CRYPTO_LOCKS\n#endif\n\n#ifdef HIREDIS_USE_CRYPTO_LOCKS\ntypedef pthread_mutex_t sslLockType;\nstatic void sslLockInit(sslLockType *l) {\n    pthread_mutex_init(l, NULL);\n}\nstatic void sslLockAcquire(sslLockType *l) {\n    pthread_mutex_lock(l);\n}\nstatic void sslLockRelease(sslLockType *l) {\n    pthread_mutex_unlock(l);\n}\nstatic pthread_mutex_t *ossl_locks;\n\nstatic void opensslDoLock(int mode, int lkid, const char *f, int line) {\n    sslLockType *l = ossl_locks + lkid;\n\n    if (mode & CRYPTO_LOCK) {\n        sslLockAcquire(l);\n    } else {\n        sslLockRelease(l);\n    }\n\n    (void)f;\n    (void)line;\n}\n\nstatic void initOpensslLocks(void) {\n    unsigned ii, nlocks;\n    if (CRYPTO_get_locking_callback() != NULL) {\n        /* Someone already set the callback before us. Don't destroy it! */\n        return;\n    }\n    nlocks = CRYPTO_num_locks();\n    ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);\n    for (ii = 0; ii < nlocks; ii++) {\n        sslLockInit(ossl_locks + ii);\n    }\n    CRYPTO_set_locking_callback(opensslDoLock);\n}\n#endif /* HIREDIS_USE_CRYPTO_LOCKS */\n\n/**\n * SSL Connection initialization.\n */\n\nstatic int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {\n    if (c->privdata) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"redisContext was already associated\");\n        return REDIS_ERR;\n    }\n    c->privdata = calloc(1, sizeof(redisSSLContext));\n\n    c->funcs = &redisContextSSLFuncs;\n    redisSSLContext *rssl = c->privdata;\n\n    rssl->ssl_ctx = ssl_ctx;\n    rssl->ssl = ssl;\n\n    SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n    SSL_set_fd(rssl->ssl, c->fd);\n    SSL_set_connect_state(rssl->ssl);\n\n    ERR_clear_error();\n    int rv = SSL_connect(rssl->ssl);\n    if (rv == 1) {\n        return REDIS_OK;\n    }\n\n    rv = SSL_get_error(rssl->ssl, rv);\n    if (((c->flags & REDIS_BLOCK) == 0) &&\n        (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {\n        return REDIS_OK;\n    }\n\n    if (c->err == 0) {\n        char err[512];\n        if (rv == SSL_ERROR_SYSCALL)\n            snprintf(err,sizeof(err)-1,\"SSL_connect failed: %s\",strerror(errno));\n        else {\n            unsigned long e = ERR_peek_last_error();\n            snprintf(err,sizeof(err)-1,\"SSL_connect failed: %s\",\n                    ERR_reason_error_string(e));\n        }\n        __redisSetError(c, REDIS_ERR_IO, err);\n    }\n    return REDIS_ERR;\n}\n\nint redisInitiateSSL(redisContext *c, SSL *ssl) {\n    return redisSSLConnect(c, NULL, ssl);\n}\n\nint redisSecureConnection(redisContext *c, const char *capath,\n                          const char *certpath, const char *keypath, const char *servername) {\n\n    SSL_CTX *ssl_ctx = NULL;\n    SSL *ssl = NULL;\n\n    /* Initialize global OpenSSL stuff */\n    static int isInit = 0;\n    if (!isInit) {\n        isInit = 1;\n        SSL_library_init();\n#ifdef HIREDIS_USE_CRYPTO_LOCKS\n        initOpensslLocks();\n#endif\n    }\n\n    ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n    if (!ssl_ctx) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"Failed to create SSL_CTX\");\n        goto error;\n    }\n\n#ifdef HIREDIS_SSL_TRACE\n    SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);\n#endif\n    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);\n    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);\n    if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"certpath and keypath must be specified together\");\n        goto error;\n    }\n\n    if (capath) {\n        if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Invalid CA certificate\");\n            goto error;\n        }\n    }\n    if (certpath) {\n        if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Invalid client certificate\");\n            goto error;\n        }\n        if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Invalid client key\");\n            goto error;\n        }\n    }\n\n    ssl = SSL_new(ssl_ctx);\n    if (!ssl) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"Couldn't create new SSL instance\");\n        goto error;\n    }\n    if (servername) {\n        if (!SSL_set_tlsext_host_name(ssl, servername)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Couldn't set server name indication\");\n            goto error;\n        }\n    }\n\n    return redisSSLConnect(c, ssl_ctx, ssl);\n\nerror:\n    if (ssl) SSL_free(ssl);\n    if (ssl_ctx) SSL_CTX_free(ssl_ctx);\n    return REDIS_ERR;\n}\n\nstatic int maybeCheckWant(redisSSLContext *rssl, int rv) {\n    /**\n     * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set\n     * and true is returned. False is returned otherwise\n     */\n    if (rv == SSL_ERROR_WANT_READ) {\n        rssl->wantRead = 1;\n        return 1;\n    } else if (rv == SSL_ERROR_WANT_WRITE) {\n        rssl->pendingWrite = 1;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/**\n * Implementation of redisContextFuncs for SSL connections.\n */\n\nstatic void redisSSLFreeContext(void *privdata){\n    redisSSLContext *rsc = privdata;\n\n    if (!rsc) return;\n    if (rsc->ssl) {\n        SSL_free(rsc->ssl);\n        rsc->ssl = NULL;\n    }\n    if (rsc->ssl_ctx) {\n        SSL_CTX_free(rsc->ssl_ctx);\n        rsc->ssl_ctx = NULL;\n    }\n    free(rsc);\n}\n\nstatic int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {\n    redisSSLContext *rssl = c->privdata;\n\n    int nread = SSL_read(rssl->ssl, buf, bufcap);\n    if (nread > 0) {\n        return nread;\n    } else if (nread == 0) {\n        __redisSetError(c, REDIS_ERR_EOF, \"Server closed the connection\");\n        return -1;\n    } else {\n        int err = SSL_get_error(rssl->ssl, nread);\n        if (c->flags & REDIS_BLOCK) {\n            /**\n             * In blocking mode, we should never end up in a situation where\n             * we get an error without it being an actual error, except\n             * in the case of EINTR, which can be spuriously received from\n             * debuggers or whatever.\n             */\n            if (errno == EINTR) {\n                return 0;\n            } else {\n                const char *msg = NULL;\n                if (errno == EAGAIN) {\n                    msg = \"Resource temporarily unavailable\";\n                }\n                __redisSetError(c, REDIS_ERR_IO, msg);\n                return -1;\n            }\n        }\n\n        /**\n         * We can very well get an EWOULDBLOCK/EAGAIN, however\n         */\n        if (maybeCheckWant(rssl, err)) {\n            return 0;\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n}\n\nstatic int redisSSLWrite(redisContext *c) {\n    redisSSLContext *rssl = c->privdata;\n\n    size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);\n    int rv = SSL_write(rssl->ssl, c->obuf, len);\n\n    if (rv > 0) {\n        rssl->lastLen = 0;\n    } else if (rv < 0) {\n        rssl->lastLen = len;\n\n        int err = SSL_get_error(rssl->ssl, rv);\n        if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {\n            return 0;\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n    return rv;\n}\n\nstatic void redisSSLAsyncRead(redisAsyncContext *ac) {\n    int rv;\n    redisSSLContext *rssl = ac->c.privdata;\n    redisContext *c = &ac->c;\n\n    rssl->wantRead = 0;\n\n    if (rssl->pendingWrite) {\n        int done;\n\n        /* This is probably just a write event */\n        rssl->pendingWrite = 0;\n        rv = redisBufferWrite(c, &done);\n        if (rv == REDIS_ERR) {\n            __redisAsyncDisconnect(ac);\n            return;\n        } else if (!done) {\n            _EL_ADD_WRITE(ac);\n        }\n    }\n\n    rv = redisBufferRead(c);\n    if (rv == REDIS_ERR) {\n        __redisAsyncDisconnect(ac);\n    } else {\n        _EL_ADD_READ(ac);\n        redisProcessCallbacks(ac);\n    }\n}\n\nstatic void redisSSLAsyncWrite(redisAsyncContext *ac) {\n    int rv, done = 0;\n    redisSSLContext *rssl = ac->c.privdata;\n    redisContext *c = &ac->c;\n\n    rssl->pendingWrite = 0;\n    rv = redisBufferWrite(c, &done);\n    if (rv == REDIS_ERR) {\n        __redisAsyncDisconnect(ac);\n        return;\n    }\n\n    if (!done) {\n        if (rssl->wantRead) {\n            /* Need to read-before-write */\n            rssl->pendingWrite = 1;\n            _EL_DEL_WRITE(ac);\n        } else {\n            /* No extra reads needed, just need to write more */\n            _EL_ADD_WRITE(ac);\n        }\n    } else {\n        /* Already done! */\n        _EL_DEL_WRITE(ac);\n    }\n\n    /* Always reschedule a read */\n    _EL_ADD_READ(ac);\n}\n\nredisContextFuncs redisContextSSLFuncs = {\n    .free_privdata = redisSSLFreeContext,\n    .async_read = redisSSLAsyncRead,\n    .async_write = redisSSLAsyncWrite,\n    .read = redisSSLRead,\n    .write = redisSSLWrite\n};\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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/socket.h>\n#include <sys/time.h>\n#include <netdb.h>\n#include <assert.h>\n#include <unistd.h>\n#include <signal.h>\n#include <errno.h>\n#include <limits.h>\n\n#include \"hiredis.h\"\n#ifdef HIREDIS_TEST_SSL\n#include \"hiredis_ssl.h\"\n#endif\n#include \"net.h\"\n\nenum connection_type {\n    CONN_TCP,\n    CONN_UNIX,\n    CONN_FD,\n    CONN_SSL\n};\n\nstruct config {\n    enum connection_type type;\n\n    struct {\n        const char *host;\n        int port;\n        struct timeval timeout;\n    } tcp;\n\n    struct {\n        const char *path;\n    } unix_sock;\n\n    struct {\n        const char *host;\n        int port;\n        const char *ca_cert;\n        const char *cert;\n        const char *key;\n    } ssl;\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\n/* The assert() calls below have side effects, so we need assert()\n * even if we are compiling without asserts (-DNDEBUG). */\n#ifdef NDEBUG\n#undef assert\n#define assert(e) (void)(e)\n#endif\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 int disconnect(redisContext *c, int keep_fd) {\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, but keep the fd if requested. */\n    if (keep_fd)\n        return redisFreeKeepFd(c);\n    redisFree(c);\n    return -1;\n}\n\nstatic void do_ssl_handshake(redisContext *c, struct config config) {\n#ifdef HIREDIS_TEST_SSL\n    redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);\n    if (c->err) {\n        printf(\"SSL error: %s\\n\", c->errstr);\n        redisFree(c);\n        exit(1);\n    }\n#else\n    (void) c;\n    (void) config;\n#endif\n}\n\nstatic redisContext *do_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_SSL) {\n        c = redisConnect(config.ssl.host, config.ssl.port);\n    } else if (config.type == CONN_UNIX) {\n        c = redisConnectUnix(config.unix_sock.path);\n    } else if (config.type == CONN_FD) {\n        /* Create a dummy connection just to get an fd to inherit */\n        redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);\n        if (dummy_ctx) {\n            int fd = disconnect(dummy_ctx, 1);\n            printf(\"Connecting to inherited fd %d\\n\", fd);\n            c = redisConnectFd(fd);\n        }\n    } else {\n        assert(NULL);\n    }\n\n    if (c == NULL) {\n        printf(\"Connection error: can't allocate redis context\\n\");\n        exit(1);\n    } else if (c->err) {\n        printf(\"Connection error: %s\\n\", c->errstr);\n        redisFree(c);\n        exit(1);\n    }\n\n    if (config.type == CONN_SSL) {\n        do_ssl_handshake(c, config);\n    }\n\n    return select_database(c);\n}\n\nstatic void do_reconnect(redisContext *c, struct config config) {\n    redisReconnect(c);\n\n    if (config.type == CONN_SSL) {\n        do_ssl_handshake(c, config);\n    }\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\",(size_t)3,\"b\\0r\",(size_t)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\",(size_t)3,\"\",(size_t)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\",(size_t)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    sds sds_cmd;\n\n    sds_cmd = sdsempty();\n    test(\"Format command into sds by passing argc/argv without lengths: \");\n    len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);\n    test_cond(strncmp(sds_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    sdsfree(sds_cmd);\n\n    sds_cmd = sdsempty();\n    test(\"Format command into sds by passing argc/argv with lengths: \");\n    len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);\n    test_cond(strncmp(sds_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    sdsfree(sds_cmd);\n}\n\nstatic void test_append_formatted_commands(struct config config) {\n    redisContext *c;\n    redisReply *reply;\n    char *cmd;\n    int len;\n\n    c = do_connect(config);\n\n    test(\"Append format command: \");\n\n    len = redisFormatCommand(&cmd, \"SET foo bar\");\n\n    test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK);\n\n    assert(redisGetReply(c, (void*)&reply) == REDIS_OK);\n\n    free(cmd);\n    freeReplyObject(reply);\n\n    disconnect(c, 0);\n}\n\nstatic void test_reply_reader(void) {\n    redisReader *reader;\n    void *reply;\n    int ret;\n    int i;\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 > 7: \");\n    reader = redisReaderCreate();\n\n    for (i = 0; i < 9; i++) {\n        redisReaderFeed(reader,(char*)\"*1\\r\\n\",4);\n    }\n\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(\"Correctly parses LLONG_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":9223372036854775807\\r\\n\",22);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK &&\n            ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&\n            ((redisReply*)reply)->integer == LLONG_MAX);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when > LLONG_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":9223372036854775808\\r\\n\",22);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Bad integer value\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Correctly parses LLONG_MIN: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":-9223372036854775808\\r\\n\",23);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK &&\n            ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&\n            ((redisReply*)reply)->integer == LLONG_MIN);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when < LLONG_MIN: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":-9223372036854775809\\r\\n\",23);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Bad integer value\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when array < -1: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"*-2\\r\\n+asdf\\r\\n\",12);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Multi-bulk length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when bulk < -1: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"$-2\\r\\nasdf\\r\\n\",11);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Bulk string length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n#if LLONG_MAX > SIZE_MAX\n    test(\"Set error when array > SIZE_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"*9223372036854775807\\r\\n+asdf\\r\\n\",29);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n            strcasecmp(reader->errstr,\"Multi-bulk length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when bulk > SIZE_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"$9223372036854775807\\r\\nasdf\\r\\n\",28);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n            strcasecmp(reader->errstr,\"Bulk string length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n#endif\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_free_null(void) {\n    void *redisCtx = NULL;\n    void *reply = NULL;\n\n    test(\"Don't fail when redisFree is passed a NULL value: \");\n    redisFree(redisCtx);\n    test_cond(redisCtx == NULL);\n\n    test(\"Don't fail when freeReplyObject is passed a NULL value: \");\n    freeReplyObject(reply);\n    test_cond(reply == NULL);\n}\n\n#define HIREDIS_BAD_DOMAIN \"idontexist-noreally.com\"\nstatic void test_blocking_connection_errors(void) {\n    redisContext *c;\n    struct addrinfo hints = {.ai_family = AF_INET};\n    struct addrinfo *ai_tmp = NULL;\n\n    int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, \"6379\", &hints, &ai_tmp);\n    if (rv != 0) {\n        // Address does *not* exist\n        test(\"Returns error when host cannot be resolved: \");\n        // First see if this domain name *actually* resolves to NXDOMAIN\n        c = redisConnect(HIREDIS_BAD_DOMAIN, 6379);\n        test_cond(\n            c->err == REDIS_ERR_OTHER &&\n            (strcmp(c->errstr, \"Name or service not known\") == 0 ||\n             strcmp(c->errstr, \"Can't resolve: \" HIREDIS_BAD_DOMAIN) == 0 ||\n             strcmp(c->errstr, \"Name does not resolve\") == 0 ||\n             strcmp(c->errstr,\n                    \"nodename nor servname provided, or not known\") == 0 ||\n             strcmp(c->errstr, \"No address associated with hostname\") == 0 ||\n             strcmp(c->errstr, \"Temporary failure in name resolution\") == 0 ||\n             strcmp(c->errstr,\n                    \"hostname nor servname provided, or not known\") == 0 ||\n             strcmp(c->errstr, \"no address associated with name\") == 0));\n        redisFree(c);\n    } else {\n        printf(\"Skipping NXDOMAIN test. Found evil ISP!\\n\");\n        freeaddrinfo(ai_tmp);\n    }\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_sock 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 = do_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\",(size_t)3,\"hello\\x00world\",(size_t)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, 0);\n}\n\nstatic void test_blocking_connection_timeouts(struct config config) {\n    redisContext *c;\n    redisReply *reply;\n    ssize_t s;\n    const char *cmd = \"DEBUG SLEEP 3\\r\\n\";\n    struct timeval tv;\n\n    c = do_connect(config);\n    test(\"Successfully completes a command when the timeout is not exceeded: \");\n    reply = redisCommand(c,\"SET foo fast\");\n    freeReplyObject(reply);\n    tv.tv_sec = 0;\n    tv.tv_usec = 10000;\n    redisSetTimeout(c, tv);\n    reply = redisCommand(c, \"GET foo\");\n    test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, \"fast\", 4) == 0);\n    freeReplyObject(reply);\n    disconnect(c, 0);\n\n    c = do_connect(config);\n    test(\"Does not return a reply when the command times out: \");\n    redisAppendFormattedCommand(c, cmd, strlen(cmd));\n    s = c->funcs->write(c);\n    tv.tv_sec = 0;\n    tv.tv_usec = 10000;\n    redisSetTimeout(c, tv);\n    reply = redisCommand(c, \"GET foo\");\n    test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, \"Resource temporarily unavailable\") == 0);\n    freeReplyObject(reply);\n\n    test(\"Reconnect properly reconnects after a timeout: \");\n    do_reconnect(c, config);\n    reply = redisCommand(c, \"PING\");\n    test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, \"PONG\") == 0);\n    freeReplyObject(reply);\n\n    test(\"Reconnect properly uses owned parameters: \");\n    config.tcp.host = \"foo\";\n    config.unix_sock.path = \"foo\";\n    do_reconnect(c, config);\n    reply = redisCommand(c, \"PING\");\n    test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, \"PONG\") == 0);\n    freeReplyObject(reply);\n\n    disconnect(c, 0);\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 = do_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 || (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 = do_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_invalid_timeout_errors(struct config config) {\n    redisContext *c;\n\n    test(\"Set error when an invalid timeout usec value is given to redisConnectWithTimeout: \");\n\n    config.tcp.timeout.tv_sec = 0;\n    config.tcp.timeout.tv_usec = 10000001;\n\n    c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);\n\n    test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, \"Invalid timeout specified\") == 0);\n    redisFree(c);\n\n    test(\"Set error when an invalid timeout sec value is given to redisConnectWithTimeout: \");\n\n    config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;\n    config.tcp.timeout.tv_usec = 0;\n\n    c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);\n\n    test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, \"Invalid timeout specified\") == 0);\n    redisFree(c);\n}\n\nstatic void test_throughput(struct config config) {\n    redisContext *c = do_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    replies = malloc(sizeof(redisReply*)*num);\n    t1 = usec();\n    for (i = 0; i < num; i++) {\n        replies[i] = redisCommand(c, \"INCRBY incrkey %d\", 1000000);\n        assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx INCRBY: %.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    replies = malloc(sizeof(redisReply*)*num);\n    for (i = 0; i < num; i++)\n        redisAppendCommand(c,\"INCRBY incrkey %d\", 1000000);\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_INTEGER);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx INCRBY (pipelined): %.3fs)\\n\", num, (t2-t1)/1000000.0);\n\n    disconnect(c, 0);\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_sock = {\n            .path = \"/tmp/redis.sock\"\n        }\n    };\n    int throughput = 1;\n    int test_inherit_fd = 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_sock.path = argv[0];\n        } else if (argc >= 1 && !strcmp(argv[0],\"--skip-throughput\")) {\n            throughput = 0;\n        } else if (argc >= 1 && !strcmp(argv[0],\"--skip-inherit-fd\")) {\n            test_inherit_fd = 0;\n#ifdef HIREDIS_TEST_SSL\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-port\")) {\n            argv++; argc--;\n            cfg.ssl.port = atoi(argv[0]);\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-host\")) {\n            argv++; argc--;\n            cfg.ssl.host = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-ca-cert\")) {\n            argv++; argc--;\n            cfg.ssl.ca_cert  = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-cert\")) {\n            argv++; argc--;\n            cfg.ssl.cert = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-key\")) {\n            argv++; argc--;\n            cfg.ssl.key = argv[0];\n#endif\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    test_free_null();\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_connection_timeouts(cfg);\n    test_blocking_io_errors(cfg);\n    test_invalid_timeout_errors(cfg);\n    test_append_formatted_commands(cfg);\n    if (throughput) test_throughput(cfg);\n\n    printf(\"\\nTesting against Unix socket connection (%s):\\n\", cfg.unix_sock.path);\n    cfg.type = CONN_UNIX;\n    test_blocking_connection(cfg);\n    test_blocking_connection_timeouts(cfg);\n    test_blocking_io_errors(cfg);\n    if (throughput) test_throughput(cfg);\n\n#ifdef HIREDIS_TEST_SSL\n    if (cfg.ssl.port && cfg.ssl.host) {\n        printf(\"\\nTesting against SSL connection (%s:%d):\\n\", cfg.ssl.host, cfg.ssl.port);\n        cfg.type = CONN_SSL;\n\n        test_blocking_connection(cfg);\n        test_blocking_connection_timeouts(cfg);\n        test_blocking_io_errors(cfg);\n        test_invalid_timeout_errors(cfg);\n        test_append_formatted_commands(cfg);\n        if (throughput) test_throughput(cfg);\n    }\n#endif\n\n    if (test_inherit_fd) {\n        printf(\"\\nTesting against inherited fd (%s):\\n\", cfg.unix_sock.path);\n        cfg.type = CONN_FD;\n        test_blocking_connection(cfg);\n    }\n\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": "Chapter02/redis-6.0.3/deps/hiredis/test.sh",
    "content": "#!/bin/sh -ue\n\nREDIS_SERVER=${REDIS_SERVER:-redis-server}\nREDIS_PORT=${REDIS_PORT:-56379}\nREDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}\nTEST_SSL=${TEST_SSL:-0}\nSSL_TEST_ARGS=\n\ntmpdir=$(mktemp -d)\nPID_FILE=${tmpdir}/hiredis-test-redis.pid\nSOCK_FILE=${tmpdir}/hiredis-test-redis.sock\n\nif [ \"$TEST_SSL\" = \"1\" ]; then\n    SSL_CA_CERT=${tmpdir}/ca.crt\n    SSL_CA_KEY=${tmpdir}/ca.key\n    SSL_CERT=${tmpdir}/redis.crt\n    SSL_KEY=${tmpdir}/redis.key\n\n    openssl genrsa -out ${tmpdir}/ca.key 4096\n    openssl req \\\n        -x509 -new -nodes -sha256 \\\n        -key ${SSL_CA_KEY} \\\n        -days 3650 \\\n        -subj '/CN=Hiredis Test CA' \\\n        -out ${SSL_CA_CERT}\n    openssl genrsa -out ${SSL_KEY} 2048\n    openssl req \\\n        -new -sha256 \\\n        -key ${SSL_KEY} \\\n        -subj '/CN=Hiredis Test Cert' | \\\n        openssl x509 \\\n            -req -sha256 \\\n            -CA ${SSL_CA_CERT} \\\n            -CAkey ${SSL_CA_KEY} \\\n            -CAserial ${tmpdir}/ca.txt \\\n            -CAcreateserial \\\n            -days 365 \\\n            -out ${SSL_CERT}\n\n    SSL_TEST_ARGS=\"--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}\"\nfi\n\ncleanup() {\n  set +e\n  kill $(cat ${PID_FILE})\n  rm -rf ${tmpdir}\n}\ntrap cleanup INT TERM EXIT\n\ncat > ${tmpdir}/redis.conf <<EOF\ndaemonize yes\npidfile ${PID_FILE}\nport ${REDIS_PORT}\nbind 127.0.0.1\nunixsocket ${SOCK_FILE}\nEOF\n\nif [ \"$TEST_SSL\" = \"1\" ]; then\n    cat >> ${tmpdir}/redis.conf <<EOF\ntls-port ${REDIS_SSL_PORT}\ntls-ca-cert-file ${SSL_CA_CERT}\ntls-cert-file ${SSL_CERT}\ntls-key-file ${SSL_KEY}\nEOF\nfi\n\ncat ${tmpdir}/redis.conf\n${REDIS_SERVER} ${tmpdir}/redis.conf\n\n${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/hiredis/win32.h",
    "content": "#ifndef _WIN32_HELPER_INCLUDE\n#define _WIN32_HELPER_INCLUDE\n#ifdef _MSC_VER\n\n#include <winsock2.h> /* for struct timeval */\n\n#ifndef inline\n#define inline __inline\n#endif\n\n#ifndef strcasecmp\n#define strcasecmp stricmp\n#endif\n\n#ifndef strncasecmp\n#define strncasecmp strnicmp\n#endif\n\n#ifndef va_copy\n#define va_copy(d,s) ((d) = (s))\n#endif\n\n#ifndef snprintf\n#define snprintf c99_snprintf\n\n__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)\n{\n    int count = -1;\n\n    if (size != 0)\n        count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);\n    if (count == -1)\n        count = _vscprintf(format, ap);\n\n    return count;\n}\n\n__inline int c99_snprintf(char* str, size_t size, const char* format, ...)\n{\n    int count;\n    va_list ap;\n\n    va_start(ap, format);\n    count = c99_vsnprintf(str, size, format, ap);\n    va_end(ap);\n\n    return count;\n}\n#endif\n#endif /* _MSC_VER */\n\n#ifdef _WIN32\n#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)\n#endif /* _WIN32 */\n\n#endif /* _WIN32_HELPER_INCLUDE */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/.appveyor.yml",
    "content": "version: '{build}'\n\nenvironment:\n  matrix:\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n    MSVC: amd64\n  - MSYSTEM: MINGW32\n    CPU: i686\n    MSVC: x86\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n  - MSYSTEM: MINGW32\n    CPU: i686\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n    MSVC: amd64\n    CONFIG_FLAGS: --enable-debug\n  - MSYSTEM: MINGW32\n    CPU: i686\n    MSVC: x86\n    CONFIG_FLAGS: --enable-debug\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n    CONFIG_FLAGS: --enable-debug\n  - MSYSTEM: MINGW32\n    CPU: i686\n    CONFIG_FLAGS: --enable-debug\n\ninstall:\n  - set PATH=c:\\msys64\\%MSYSTEM%\\bin;c:\\msys64\\usr\\bin;%PATH%\n  - if defined MSVC call \"c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" %MSVC%\n  - if defined MSVC pacman --noconfirm -Rsc mingw-w64-%CPU%-gcc gcc\n  - pacman --noconfirm -Suy mingw-w64-%CPU%-make\n\nbuild_script:\n  - bash -c \"autoconf\"\n  - bash -c \"./configure $CONFIG_FLAGS\"\n  - mingw32-make\n  - file lib/jemalloc.dll\n  - mingw32-make tests\n  - mingw32-make -k check\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/.autom4te.cfg",
    "content": "begin-language: \"Autoconf-without-aclocal-m4\"\nargs: --no-cache\nend-language: \"Autoconf-without-aclocal-m4\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/.gitattributes",
    "content": "* text=auto eol=lf\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/.gitignore",
    "content": "/bin/jemalloc-config\n/bin/jemalloc.sh\n/bin/jeprof\n\n/config.stamp\n/config.log\n/config.status\n/configure\n\n/doc/html.xsl\n/doc/manpages.xsl\n/doc/jemalloc.xml\n/doc/jemalloc.html\n/doc/jemalloc.3\n\n/jemalloc.pc\n\n/lib/\n\n/Makefile\n\n/include/jemalloc/internal/jemalloc_preamble.h\n/include/jemalloc/internal/jemalloc_internal_defs.h\n/include/jemalloc/internal/private_namespace.gen.h\n/include/jemalloc/internal/private_namespace.h\n/include/jemalloc/internal/private_namespace_jet.gen.h\n/include/jemalloc/internal/private_namespace_jet.h\n/include/jemalloc/internal/private_symbols.awk\n/include/jemalloc/internal/private_symbols_jet.awk\n/include/jemalloc/internal/public_namespace.h\n/include/jemalloc/internal/public_symbols.txt\n/include/jemalloc/internal/public_unnamespace.h\n/include/jemalloc/internal/size_classes.h\n/include/jemalloc/jemalloc.h\n/include/jemalloc/jemalloc_defs.h\n/include/jemalloc/jemalloc_macros.h\n/include/jemalloc/jemalloc_mangle.h\n/include/jemalloc/jemalloc_mangle_jet.h\n/include/jemalloc/jemalloc_protos.h\n/include/jemalloc/jemalloc_protos_jet.h\n/include/jemalloc/jemalloc_rename.h\n/include/jemalloc/jemalloc_typedefs.h\n\n/src/*.[od]\n/src/*.sym\n\n/run_tests.out/\n\n/test/test.sh\ntest/include/test/jemalloc_test.h\ntest/include/test/jemalloc_test_defs.h\n\n/test/integration/[A-Za-z]*\n!/test/integration/[A-Za-z]*.*\n/test/integration/*.[od]\n/test/integration/*.out\n\n/test/integration/cpp/[A-Za-z]*\n!/test/integration/cpp/[A-Za-z]*.*\n/test/integration/cpp/*.[od]\n/test/integration/cpp/*.out\n\n/test/src/*.[od]\n\n/test/stress/[A-Za-z]*\n!/test/stress/[A-Za-z]*.*\n/test/stress/*.[od]\n/test/stress/*.out\n\n/test/unit/[A-Za-z]*\n!/test/unit/[A-Za-z]*.*\n/test/unit/*.[od]\n/test/unit/*.out\n\n/VERSION\n\n*.pdb\n*.sdf\n*.opendb\n*.VC.db\n*.opensdf\n*.cachefile\n*.suo\n*.user\n*.sln.docstates\n*.tmp\n.vs/\n/msvc/Win32/\n/msvc/x64/\n/msvc/projects/*/*/Debug*/\n/msvc/projects/*/*/Release*/\n/msvc/projects/*/*/Win32/\n/msvc/projects/*/*/x64/\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/.travis.yml",
    "content": "language: generic\ndist: precise\n\nmatrix:\n  include:\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false,dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false,percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false,background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary,percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary,background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu,background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n\n\nbefore_script:\n  - autoconf\n  - ./configure ${COMPILER_FLAGS:+       CC=\"$CC $COMPILER_FLAGS\"       CXX=\"$CXX $COMPILER_FLAGS\" }       $CONFIGURE_FLAGS\n  - make -j3\n  - make -j3 tests\n\nscript:\n  - make check\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/COPYING",
    "content": "Unless otherwise specified, files in the jemalloc source distribution are\nsubject to the following license:\n--------------------------------------------------------------------------------\nCopyright (C) 2002-2018 Jason Evans <jasone@canonware.com>.\nAll rights reserved.\nCopyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.\nCopyright (C) 2009-2018 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": "Chapter02/redis-6.0.3/deps/jemalloc/ChangeLog",
    "content": "Following are change highlights associated with official releases.  Important\nbug fixes are all mentioned, but some internal enhancements are omitted here for\nbrevity.  Much more detail can be found in the git revision history:\n\n    https://github.com/jemalloc/jemalloc\n\n* 5.1.0 (May 4th, 2018)\n\n  This release is primarily about fine-tuning, ranging from several new features\n  to numerous notable performance and portability enhancements.  The release and\n  prior dev versions have been running in multiple large scale applications for\n  months, and the cumulative improvements are substantial in many cases.\n\n  Given the long and successful production runs, this release is likely a good\n  candidate for applications to upgrade, from both jemalloc 5.0 and before.  For\n  performance-critical applications, the newly added TUNING.md provides\n  guidelines on jemalloc tuning.\n\n  New features:\n  - Implement transparent huge page support for internal metadata.  (@interwq)\n  - Add opt.thp to allow enabling / disabling transparent huge pages for all\n    mappings.  (@interwq)\n  - Add maximum background thread count option.  (@djwatson)\n  - Allow prof_active to control opt.lg_prof_interval and prof.gdump.\n    (@interwq)\n  - Allow arena index lookup based on allocation addresses via mallctl.\n    (@lionkov)\n  - Allow disabling initial-exec TLS model.  (@davidtgoldblatt, @KenMacD)\n  - Add opt.lg_extent_max_active_fit to set the max ratio between the size of\n    the active extent selected (to split off from) and the size of the requested\n    allocation.  (@interwq, @davidtgoldblatt)\n  - Add retain_grow_limit to set the max size when growing virtual address\n    space.  (@interwq)\n  - Add mallctl interfaces:\n    + arena.<i>.retain_grow_limit  (@interwq)\n    + arenas.lookup  (@lionkov)\n    + max_background_threads  (@djwatson)\n    + opt.lg_extent_max_active_fit  (@interwq)\n    + opt.max_background_threads  (@djwatson)\n    + opt.metadata_thp  (@interwq)\n    + opt.thp  (@interwq)\n    + stats.metadata_thp  (@interwq)\n\n  Portability improvements:\n  - Support GNU/kFreeBSD configuration.  (@paravoid)\n  - Support m68k, nios2 and SH3 architectures.  (@paravoid)\n  - Fall back to FD_CLOEXEC when O_CLOEXEC is unavailable.  (@zonyitoo)\n  - Fix symbol listing for cross-compiling.  (@tamird)\n  - Fix high bits computation on ARM.  (@davidtgoldblatt, @paravoid)\n  - Disable the CPU_SPINWAIT macro for Power.  (@davidtgoldblatt, @marxin)\n  - Fix MSVC 2015 & 2017 builds.  (@rustyx)\n  - Improve RISC-V support.  (@EdSchouten)\n  - Set name mangling script in strict mode.  (@nicolov)\n  - Avoid MADV_HUGEPAGE on ARM.  (@marxin)\n  - Modify configure to determine return value of strerror_r.\n    (@davidtgoldblatt, @cferris1000)\n  - Make sure CXXFLAGS is tested with CPP compiler.  (@nehaljwani)\n  - Fix 32-bit build on MSVC.  (@rustyx)\n  - Fix external symbol on MSVC.  (@maksqwe)\n  - Avoid a printf format specifier warning.  (@jasone)\n  - Add configure option --disable-initial-exec-tls which can allow jemalloc to\n    be dynamically loaded after program startup.  (@davidtgoldblatt, @KenMacD)\n  - AArch64: Add ILP32 support.  (@cmuellner)\n  - Add --with-lg-vaddr configure option to support cross compiling.\n    (@cmuellner, @davidtgoldblatt)\n\n  Optimizations and refactors:\n  - Improve active extent fit with extent_max_active_fit.  This considerably\n    reduces fragmentation over time and improves virtual memory and metadata\n    usage.  (@davidtgoldblatt, @interwq)\n  - Eagerly coalesce large extents to reduce fragmentation.  (@interwq)\n  - sdallocx: only read size info when page aligned (i.e. possibly sampled),\n    which speeds up the sized deallocation path significantly.  (@interwq)\n  - Avoid attempting new mappings for in place expansion with retain, since\n    it rarely succeeds in practice and causes high overhead.  (@interwq)\n  - Refactor OOM handling in newImpl.  (@wqfish)\n  - Add internal fine-grained logging functionality for debugging use.\n    (@davidtgoldblatt)\n  - Refactor arena / tcache interactions.  (@davidtgoldblatt)\n  - Refactor extent management with dumpable flag.  (@davidtgoldblatt)\n  - Add runtime detection of lazy purging.  (@interwq)\n  - Use pairing heap instead of red-black tree for extents_avail.  (@djwatson)\n  - Use sysctl on startup in FreeBSD.  (@trasz)\n  - Use thread local prng state instead of atomic.  (@djwatson)\n  - Make decay to always purge one more extent than before, because in\n    practice large extents are usually the ones that cross the decay threshold.\n    Purging the additional extent helps save memory as well as reduce VM\n    fragmentation.  (@interwq)\n  - Fast division by dynamic values.  (@davidtgoldblatt)\n  - Improve the fit for aligned allocation.  (@interwq, @edwinsmith)\n  - Refactor extent_t bitpacking.  (@rkmisra)\n  - Optimize the generated assembly for ticker operations.  (@davidtgoldblatt)\n  - Convert stats printing to use a structured text emitter.  (@davidtgoldblatt)\n  - Remove preserve_lru feature for extents management.  (@djwatson)\n  - Consolidate two memory loads into one on the fast deallocation path.\n    (@davidtgoldblatt, @interwq)\n\n  Bug fixes (most of the issues are only relevant to jemalloc 5.0):\n  - Fix deadlock with multithreaded fork in OS X.  (@davidtgoldblatt)\n  - Validate returned file descriptor before use.  (@zonyitoo)\n  - Fix a few background thread initialization and shutdown issues.  (@interwq)\n  - Fix an extent coalesce + decay race by taking both coalescing extents off\n    the LRU list.  (@interwq)\n  - Fix potentially unbound increase during decay, caused by one thread keep\n    stashing memory to purge while other threads generating new pages.  The\n    number of pages to purge is checked to prevent this.  (@interwq)\n  - Fix a FreeBSD bootstrap assertion.  (@strejda, @interwq)\n  - Handle 32 bit mutex counters.  (@rkmisra)\n  - Fix a indexing bug when creating background threads.  (@davidtgoldblatt,\n    @binliu19)\n  - Fix arguments passed to extent_init.  (@yuleniwo, @interwq)\n  - Fix addresses used for ordering mutexes.  (@rkmisra)\n  - Fix abort_conf processing during bootstrap.  (@interwq)\n  - Fix include path order for out-of-tree builds.  (@cmuellner)\n\n  Incompatible changes:\n  - Remove --disable-thp.  (@interwq)\n  - Remove mallctl interfaces:\n    + config.thp  (@interwq)\n\n  Documentation:\n  - Add TUNING.md.  (@interwq, @davidtgoldblatt, @djwatson)\n\n* 5.0.1 (July 1, 2017)\n\n  This bugfix release fixes several issues, most of which are obscure enough\n  that typical applications are not impacted.\n\n  Bug fixes:\n  - Update decay->nunpurged before purging, in order to avoid potential update\n    races and subsequent incorrect purging volume.  (@interwq)\n  - Only abort on dlsym(3) error if the failure impacts an enabled feature (lazy\n    locking and/or background threads).  This mitigates an initialization\n    failure bug for which we still do not have a clear reproduction test case.\n    (@interwq)\n  - Modify tsd management so that it neither crashes nor leaks if a thread's\n    only allocation activity is to call free() after TLS destructors have been\n    executed.  This behavior was observed when operating with GNU libc, and is\n    unlikely to be an issue with other libc implementations.  (@interwq)\n  - Mask signals during background thread creation.  This prevents signals from\n    being inadvertently delivered to background threads.  (@jasone,\n    @davidtgoldblatt, @interwq)\n  - Avoid inactivity checks within background threads, in order to prevent\n    recursive mutex acquisition.  (@interwq)\n  - Fix extent_grow_retained() to use the specified hooks when the\n    arena.<i>.extent_hooks mallctl is used to override the default hooks.\n    (@interwq)\n  - Add missing reentrancy support for custom extent hooks which allocate.\n    (@interwq)\n  - Post-fork(2), re-initialize the list of tcaches associated with each arena\n    to contain no tcaches except the forking thread's.  (@interwq)\n  - Add missing post-fork(2) mutex reinitialization for extent_grow_mtx.  This\n    fixes potential deadlocks after fork(2).  (@interwq)\n  - Enforce minimum autoconf version (currently 2.68), since 2.63 is known to\n    generate corrupt configure scripts.  (@jasone)\n  - Ensure that the configured page size (--with-lg-page) is no larger than the\n    configured huge page size (--with-lg-hugepage).  (@jasone)\n\n* 5.0.0 (June 13, 2017)\n\n  Unlike all previous jemalloc releases, this release does not use naturally\n  aligned \"chunks\" for virtual memory management, and instead uses page-aligned\n  \"extents\".  This change has few externally visible effects, but the internal\n  impacts are... extensive.  Many other internal changes combine to make this\n  the most cohesively designed version of jemalloc so far, with ample\n  opportunity for further enhancements.\n\n  Continuous integration is now an integral aspect of development thanks to the\n  efforts of @davidtgoldblatt, and the dev branch tends to remain reasonably\n  stable on the tested platforms (Linux, FreeBSD, macOS, and Windows).  As a\n  side effect the official release frequency may decrease over time.\n\n  New features:\n  - Implement optional per-CPU arena support; threads choose which arena to use\n    based on current CPU rather than on fixed thread-->arena associations.\n    (@interwq)\n  - Implement two-phase decay of unused dirty pages.  Pages transition from\n    dirty-->muzzy-->clean, where the first phase transition relies on\n    madvise(... MADV_FREE) semantics, and the second phase transition discards\n    pages such that they are replaced with demand-zeroed pages on next access.\n    (@jasone)\n  - Increase decay time resolution from seconds to milliseconds.  (@jasone)\n  - Implement opt-in per CPU background threads, and use them for asynchronous\n    decay-driven unused dirty page purging.  (@interwq)\n  - Add mutex profiling, which collects a variety of statistics useful for\n    diagnosing overhead/contention issues.  (@interwq)\n  - Add C++ new/delete operator bindings.  (@djwatson)\n  - Support manually created arena destruction, such that all data and metadata\n    are discarded.  Add MALLCTL_ARENAS_DESTROYED for accessing merged stats\n    associated with destroyed arenas.  (@jasone)\n  - Add MALLCTL_ARENAS_ALL as a fixed index for use in accessing\n    merged/destroyed arena statistics via mallctl.  (@jasone)\n  - Add opt.abort_conf to optionally abort if invalid configuration options are\n    detected during initialization.  (@interwq)\n  - Add opt.stats_print_opts, so that e.g. JSON output can be selected for the\n    stats dumped during exit if opt.stats_print is true.  (@jasone)\n  - Add --with-version=VERSION for use when embedding jemalloc into another\n    project's git repository.  (@jasone)\n  - Add --disable-thp to support cross compiling.  (@jasone)\n  - Add --with-lg-hugepage to support cross compiling.  (@jasone)\n  - Add mallctl interfaces (various authors):\n    + background_thread\n    + opt.abort_conf\n    + opt.retain\n    + opt.percpu_arena\n    + opt.background_thread\n    + opt.{dirty,muzzy}_decay_ms\n    + opt.stats_print_opts\n    + arena.<i>.initialized\n    + arena.<i>.destroy\n    + arena.<i>.{dirty,muzzy}_decay_ms\n    + arena.<i>.extent_hooks\n    + arenas.{dirty,muzzy}_decay_ms\n    + arenas.bin.<i>.slab_size\n    + arenas.nlextents\n    + arenas.lextent.<i>.size\n    + arenas.create\n    + stats.background_thread.{num_threads,num_runs,run_interval}\n    + stats.mutexes.{ctl,background_thread,prof,reset}.\n      {num_ops,num_spin_acq,num_wait,max_wait_time,total_wait_time,max_num_thds,\n      num_owner_switch}\n    + stats.arenas.<i>.{dirty,muzzy}_decay_ms\n    + stats.arenas.<i>.uptime\n    + stats.arenas.<i>.{pmuzzy,base,internal,resident}\n    + stats.arenas.<i>.{dirty,muzzy}_{npurge,nmadvise,purged}\n    + stats.arenas.<i>.bins.<j>.{nslabs,reslabs,curslabs}\n    + stats.arenas.<i>.bins.<j>.mutex.\n      {num_ops,num_spin_acq,num_wait,max_wait_time,total_wait_time,max_num_thds,\n      num_owner_switch}\n    + stats.arenas.<i>.lextents.<j>.{nmalloc,ndalloc,nrequests,curlextents}\n    + stats.arenas.i.mutexes.{large,extent_avail,extents_dirty,extents_muzzy,\n      extents_retained,decay_dirty,decay_muzzy,base,tcache_list}.\n      {num_ops,num_spin_acq,num_wait,max_wait_time,total_wait_time,max_num_thds,\n      num_owner_switch}\n\n  Portability improvements:\n  - Improve reentrant allocation support, such that deadlock is less likely if\n    e.g. a system library call in turn allocates memory.  (@davidtgoldblatt,\n    @interwq)\n  - Support static linking of jemalloc with glibc.  (@djwatson)\n\n  Optimizations and refactors:\n  - Organize virtual memory as \"extents\" of virtual memory pages, rather than as\n    naturally aligned \"chunks\", and store all metadata in arbitrarily distant\n    locations.  This reduces virtual memory external fragmentation, and will\n    interact better with huge pages (not yet explicitly supported).  (@jasone)\n  - Fold large and huge size classes together; only small and large size classes\n    remain.  (@jasone)\n  - Unify the allocation paths, and merge most fast-path branching decisions.\n    (@davidtgoldblatt, @interwq)\n  - Embed per thread automatic tcache into thread-specific data, which reduces\n    conditional branches and dereferences.  Also reorganize tcache to increase\n    fast-path data locality.  (@interwq)\n  - Rewrite atomics to closely model the C11 API, convert various\n    synchronization from mutex-based to atomic, and use the explicit memory\n    ordering control to resolve various hypothetical races without increasing\n    synchronization overhead.  (@davidtgoldblatt)\n  - Extensively optimize rtree via various methods:\n    + Add multiple layers of rtree lookup caching, since rtree lookups are now\n      part of fast-path deallocation.  (@interwq)\n    + Determine rtree layout at compile time.  (@jasone)\n    + Make the tree shallower for common configurations.  (@jasone)\n    + Embed the root node in the top-level rtree data structure, thus avoiding\n      one level of indirection.  (@jasone)\n    + Further specialize leaf elements as compared to internal node elements,\n      and directly embed extent metadata needed for fast-path deallocation.\n      (@jasone)\n    + Ignore leading always-zero address bits (architecture-specific).\n      (@jasone)\n  - Reorganize headers (ongoing work) to make them hermetic, and disentangle\n    various module dependencies.  (@davidtgoldblatt)\n  - Convert various internal data structures such as size class metadata from\n    boot-time-initialized to compile-time-initialized.  Propagate resulting data\n    structure simplifications, such as making arena metadata fixed-size.\n    (@jasone)\n  - Simplify size class lookups when constrained to size classes that are\n    multiples of the page size.  This speeds lookups, but the primary benefit is\n    complexity reduction in code that was the source of numerous regressions.\n    (@jasone)\n  - Lock individual extents when possible for localized extent operations,\n    rather than relying on a top-level arena lock.  (@davidtgoldblatt, @jasone)\n  - Use first fit layout policy instead of best fit, in order to improve\n    packing.  (@jasone)\n  - If munmap(2) is not in use, use an exponential series to grow each arena's\n    virtual memory, so that the number of disjoint virtual memory mappings\n    remains low.  (@jasone)\n  - Implement per arena base allocators, so that arenas never share any virtual\n    memory pages.  (@jasone)\n  - Automatically generate private symbol name mangling macros.  (@jasone)\n\n  Incompatible changes:\n  - Replace chunk hooks with an expanded/normalized set of extent hooks.\n    (@jasone)\n  - Remove ratio-based purging.  (@jasone)\n  - Remove --disable-tcache.  (@jasone)\n  - Remove --disable-tls.  (@jasone)\n  - Remove --enable-ivsalloc.  (@jasone)\n  - Remove --with-lg-size-class-group.  (@jasone)\n  - Remove --with-lg-tiny-min.  (@jasone)\n  - Remove --disable-cc-silence.  (@jasone)\n  - Remove --enable-code-coverage.  (@jasone)\n  - Remove --disable-munmap (replaced by opt.retain).  (@jasone)\n  - Remove Valgrind support.  (@jasone)\n  - Remove quarantine support.  (@jasone)\n  - Remove redzone support.  (@jasone)\n  - Remove mallctl interfaces (various authors):\n    + config.munmap\n    + config.tcache\n    + config.tls\n    + config.valgrind\n    + opt.lg_chunk\n    + opt.purge\n    + opt.lg_dirty_mult\n    + opt.decay_time\n    + opt.quarantine\n    + opt.redzone\n    + opt.thp\n    + arena.<i>.lg_dirty_mult\n    + arena.<i>.decay_time\n    + arena.<i>.chunk_hooks\n    + arenas.initialized\n    + arenas.lg_dirty_mult\n    + arenas.decay_time\n    + arenas.bin.<i>.run_size\n    + arenas.nlruns\n    + arenas.lrun.<i>.size\n    + arenas.nhchunks\n    + arenas.hchunk.<i>.size\n    + arenas.extend\n    + stats.cactive\n    + stats.arenas.<i>.lg_dirty_mult\n    + stats.arenas.<i>.decay_time\n    + stats.arenas.<i>.metadata.{mapped,allocated}\n    + stats.arenas.<i>.{npurge,nmadvise,purged}\n    + stats.arenas.<i>.huge.{allocated,nmalloc,ndalloc,nrequests}\n    + stats.arenas.<i>.bins.<j>.{nruns,reruns,curruns}\n    + stats.arenas.<i>.lruns.<j>.{nmalloc,ndalloc,nrequests,curruns}\n    + stats.arenas.<i>.hchunks.<j>.{nmalloc,ndalloc,nrequests,curhchunks}\n\n  Bug fixes:\n  - Improve interval-based profile dump triggering to dump only one profile when\n    a single allocation's size exceeds the interval.  (@jasone)\n  - Use prefixed function names (as controlled by --with-jemalloc-prefix) when\n    pruning backtrace frames in jeprof.  (@jasone)\n\n* 4.5.0 (February 28, 2017)\n\n  This is the first release to benefit from much broader continuous integration\n  testing, thanks to @davidtgoldblatt.  Had we had this testing infrastructure\n  in place for prior releases, it would have caught all of the most serious\n  regressions fixed by this release.\n\n  New features:\n  - Add --disable-thp and the opt.thp mallctl to provide opt-out mechanisms for\n    transparent huge page integration.  (@jasone)\n  - Update zone allocator integration to work with macOS 10.12.  (@glandium)\n  - Restructure *CFLAGS configuration, so that CFLAGS behaves typically, and\n    EXTRA_CFLAGS provides a way to specify e.g. -Werror during building, but not\n    during configuration.  (@jasone, @ronawho)\n\n  Bug fixes:\n  - Fix DSS (sbrk(2)-based) allocation.  This regression was first released in\n    4.3.0.  (@jasone)\n  - Handle race in per size class utilization computation.  This functionality\n    was first released in 4.0.0.  (@interwq)\n  - Fix lock order reversal during gdump.  (@jasone)\n  - Fix/refactor tcache synchronization.  This regression was first released in\n    4.0.0.  (@jasone)\n  - Fix various JSON-formatted malloc_stats_print() bugs.  This functionality\n    was first released in 4.3.0.  (@jasone)\n  - Fix huge-aligned allocation.  This regression was first released in 4.4.0.\n    (@jasone)\n  - When transparent huge page integration is enabled, detect what state pages\n    start in according to the kernel's current operating mode, and only convert\n    arena chunks to non-huge during purging if that is not their initial state.\n    This functionality was first released in 4.4.0.  (@jasone)\n  - Fix lg_chunk clamping for the --enable-cache-oblivious --disable-fill case.\n    This regression was first released in 4.0.0.  (@jasone, @428desmo)\n  - Properly detect sparc64 when building for Linux.  (@glaubitz)\n\n* 4.4.0 (December 3, 2016)\n\n  New features:\n  - Add configure support for *-*-linux-android.  (@cferris1000, @jasone)\n  - Add the --disable-syscall configure option, for use on systems that place\n    security-motivated limitations on syscall(2).  (@jasone)\n  - Add support for Debian GNU/kFreeBSD.  (@thesam)\n\n  Optimizations:\n  - Add extent serial numbers and use them where appropriate as a sort key that\n    is higher priority than address, so that the allocation policy prefers older\n    extents.  This tends to improve locality (decrease fragmentation) when\n    memory grows downward.  (@jasone)\n  - Refactor madvise(2) configuration so that MADV_FREE is detected and utilized\n    on Linux 4.5 and newer.  (@jasone)\n  - Mark partially purged arena chunks as non-huge-page.  This improves\n    interaction with Linux's transparent huge page functionality.  (@jasone)\n\n  Bug fixes:\n  - Fix size class computations for edge conditions involving extremely large\n    allocations.  This regression was first released in 4.0.0.  (@jasone,\n    @ingvarha)\n  - Remove overly restrictive assertions related to the cactive statistic.  This\n    regression was first released in 4.1.0.  (@jasone)\n  - Implement a more reliable detection scheme for os_unfair_lock on macOS.\n    (@jszakmeister)\n\n* 4.3.1 (November 7, 2016)\n\n  Bug fixes:\n  - Fix a severe virtual memory leak.  This regression was first released in\n    4.3.0.  (@interwq, @jasone)\n  - Refactor atomic and prng APIs to restore support for 32-bit platforms that\n    use pre-C11 toolchains, e.g. FreeBSD's mips.  (@jasone)\n\n* 4.3.0 (November 4, 2016)\n\n  This is the first release that passes the test suite for multiple Windows\n  configurations, thanks in large part to @glandium setting up continuous\n  integration via AppVeyor (and Travis CI for Linux and OS X).\n\n  New features:\n  - Add \"J\" (JSON) support to malloc_stats_print().  (@jasone)\n  - Add Cray compiler support.  (@ronawho)\n\n  Optimizations:\n  - Add/use adaptive spinning for bootstrapping and radix tree node\n    initialization.  (@jasone)\n\n  Bug fixes:\n  - Fix large allocation to search starting in the optimal size class heap,\n    which can substantially reduce virtual memory churn and fragmentation.  This\n    regression was first released in 4.0.0.  (@mjp41, @jasone)\n  - Fix stats.arenas.<i>.nthreads accounting.  (@interwq)\n  - Fix and simplify decay-based purging.  (@jasone)\n  - Make DSS (sbrk(2)-related) operations lockless, which resolves potential\n    deadlocks during thread exit.  (@jasone)\n  - Fix over-sized allocation of radix tree leaf nodes.  (@mjp41, @ogaun,\n    @jasone)\n  - Fix over-sized allocation of arena_t (plus associated stats) data\n    structures.  (@jasone, @interwq)\n  - Fix EXTRA_CFLAGS to not affect configuration.  (@jasone)\n  - Fix a Valgrind integration bug.  (@ronawho)\n  - Disallow 0x5a junk filling when running in Valgrind.  (@jasone)\n  - Fix a file descriptor leak on Linux.  This regression was first released in\n    4.2.0.  (@vsarunas, @jasone)\n  - Fix static linking of jemalloc with glibc.  (@djwatson)\n  - Use syscall(2) rather than {open,read,close}(2) during boot on Linux.  This\n    works around other libraries' system call wrappers performing reentrant\n    allocation.  (@kspinka, @Whissi, @jasone)\n  - Fix OS X default zone replacement to work with OS X 10.12.  (@glandium,\n    @jasone)\n  - Fix cached memory management to avoid needless commit/decommit operations\n    during purging, which resolves permanent virtual memory map fragmentation\n    issues on Windows.  (@mjp41, @jasone)\n  - Fix TSD fetches to avoid (recursive) allocation.  This is relevant to\n    non-TLS and Windows configurations.  (@jasone)\n  - Fix malloc_conf overriding to work on Windows.  (@jasone)\n  - Forcibly disable lazy-lock on Windows (was forcibly *enabled*).  (@jasone)\n\n* 4.2.1 (June 8, 2016)\n\n  Bug fixes:\n  - Fix bootstrapping issues for configurations that require allocation during\n    tsd initialization (e.g. --disable-tls).  (@cferris1000, @jasone)\n  - Fix gettimeofday() version of nstime_update().  (@ronawho)\n  - Fix Valgrind regressions in calloc() and chunk_alloc_wrapper().  (@ronawho)\n  - Fix potential VM map fragmentation regression.  (@jasone)\n  - Fix opt_zero-triggered in-place huge reallocation zeroing.  (@jasone)\n  - Fix heap profiling context leaks in reallocation edge cases.  (@jasone)\n\n* 4.2.0 (May 12, 2016)\n\n  New features:\n  - Add the arena.<i>.reset mallctl, which makes it possible to discard all of\n    an arena's allocations in a single operation.  (@jasone)\n  - Add the stats.retained and stats.arenas.<i>.retained statistics.  (@jasone)\n  - Add the --with-version configure option.  (@jasone)\n  - Support --with-lg-page values larger than actual page size.  (@jasone)\n\n  Optimizations:\n  - Use pairing heaps rather than red-black trees for various hot data\n    structures.  (@djwatson, @jasone)\n  - Streamline fast paths of rtree operations.  (@jasone)\n  - Optimize the fast paths of calloc() and [m,d,sd]allocx().  (@jasone)\n  - Decommit unused virtual memory if the OS does not overcommit.  (@jasone)\n  - Specify MAP_NORESERVE on Linux if [heuristic] overcommit is active, in order\n    to avoid unfortunate interactions during fork(2).  (@jasone)\n\n  Bug fixes:\n  - Fix chunk accounting related to triggering gdump profiles.  (@jasone)\n  - Link against librt for clock_gettime(2) if glibc < 2.17.  (@jasone)\n  - Scale leak report summary according to sampling probability.  (@jasone)\n\n* 4.1.1 (May 3, 2016)\n\n  This bugfix release resolves a variety of mostly minor issues, though the\n  bitmap fix is critical for 64-bit Windows.\n\n  Bug fixes:\n  - Fix the linear scan version of bitmap_sfu() to shift by the proper amount\n    even when sizeof(long) is not the same as sizeof(void *), as on 64-bit\n    Windows.  (@jasone)\n  - Fix hashing functions to avoid unaligned memory accesses (and resulting\n    crashes).  This is relevant at least to some ARM-based platforms.\n    (@rkmisra)\n  - Fix fork()-related lock rank ordering reversals.  These reversals were\n    unlikely to cause deadlocks in practice except when heap profiling was\n    enabled and active.  (@jasone)\n  - Fix various chunk leaks in OOM code paths.  (@jasone)\n  - Fix malloc_stats_print() to print opt.narenas correctly.  (@jasone)\n  - Fix MSVC-specific build/test issues.  (@rustyx, @yuslepukhin)\n  - Fix a variety of test failures that were due to test fragility rather than\n    core bugs.  (@jasone)\n\n* 4.1.0 (February 28, 2016)\n\n  This release is primarily about optimizations, but it also incorporates a lot\n  of portability-motivated refactoring and enhancements.  Many people worked on\n  this release, to an extent that even with the omission here of minor changes\n  (see git revision history), and of the people who reported and diagnosed\n  issues, so much of the work was contributed that starting with this release,\n  changes are annotated with author credits to help reflect the collaborative\n  effort involved.\n\n  New features:\n  - Implement decay-based unused dirty page purging, a major optimization with\n    mallctl API impact.  This is an alternative to the existing ratio-based\n    unused dirty page purging, and is intended to eventually become the sole\n    purging mechanism.  New mallctls:\n    + opt.purge\n    + opt.decay_time\n    + arena.<i>.decay\n    + arena.<i>.decay_time\n    + arenas.decay_time\n    + stats.arenas.<i>.decay_time\n    (@jasone, @cevans87)\n  - Add --with-malloc-conf, which makes it possible to embed a default\n    options string during configuration.  This was motivated by the desire to\n    specify --with-malloc-conf=purge:decay , since the default must remain\n    purge:ratio until the 5.0.0 release.  (@jasone)\n  - Add MS Visual Studio 2015 support.  (@rustyx, @yuslepukhin)\n  - Make *allocx() size class overflow behavior defined.  The maximum\n    size class is now less than PTRDIFF_MAX to protect applications against\n    numerical overflow, and all allocation functions are guaranteed to indicate\n    errors rather than potentially crashing if the request size exceeds the\n    maximum size class.  (@jasone)\n  - jeprof:\n    + Add raw heap profile support.  (@jasone)\n    + Add --retain and --exclude for backtrace symbol filtering.  (@jasone)\n\n  Optimizations:\n  - Optimize the fast path to combine various bootstrapping and configuration\n    checks and execute more streamlined code in the common case.  (@interwq)\n  - Use linear scan for small bitmaps (used for small object tracking).  In\n    addition to speeding up bitmap operations on 64-bit systems, this reduces\n    allocator metadata overhead by approximately 0.2%.  (@djwatson)\n  - Separate arena_avail trees, which substantially speeds up run tree\n    operations.  (@djwatson)\n  - Use memoization (boot-time-computed table) for run quantization.  Separate\n    arena_avail trees reduced the importance of this optimization.  (@jasone)\n  - Attempt mmap-based in-place huge reallocation.  This can dramatically speed\n    up incremental huge reallocation.  (@jasone)\n\n  Incompatible changes:\n  - Make opt.narenas unsigned rather than size_t.  (@jasone)\n\n  Bug fixes:\n  - Fix stats.cactive accounting regression.  (@rustyx, @jasone)\n  - Handle unaligned keys in hash().  This caused problems for some ARM systems.\n    (@jasone, @cferris1000)\n  - Refactor arenas array.  In addition to fixing a fork-related deadlock, this\n    makes arena lookups faster and simpler.  (@jasone)\n  - Move retained memory allocation out of the default chunk allocation\n    function, to a location that gets executed even if the application installs\n    a custom chunk allocation function.  This resolves a virtual memory leak.\n    (@buchgr)\n  - Fix a potential tsd cleanup leak.  (@cferris1000, @jasone)\n  - Fix run quantization.  In practice this bug had no impact unless\n    applications requested memory with alignment exceeding one page.\n    (@jasone, @djwatson)\n  - Fix LinuxThreads-specific bootstrapping deadlock.  (Cosmin Paraschiv)\n  - jeprof:\n    + Don't discard curl options if timeout is not defined.  (@djwatson)\n    + Detect failed profile fetches.  (@djwatson)\n  - Fix stats.arenas.<i>.{dss,lg_dirty_mult,decay_time,pactive,pdirty} for\n    --disable-stats case.  (@jasone)\n\n* 4.0.4 (October 24, 2015)\n\n  This bugfix release fixes another xallocx() regression.  No other regressions\n  have come to light in over a month, so this is likely a good starting point\n  for people who prefer to wait for \"dot one\" releases with all the major issues\n  shaken out.\n\n  Bug fixes:\n  - Fix xallocx(..., MALLOCX_ZERO to zero the last full trailing page of large\n    allocations that have been randomly assigned an offset of 0 when\n    --enable-cache-oblivious configure option is enabled.\n\n* 4.0.3 (September 24, 2015)\n\n  This bugfix release continues the trend of xallocx() and heap profiling fixes.\n\n  Bug fixes:\n  - Fix xallocx(..., MALLOCX_ZERO) to zero all trailing bytes of large\n    allocations when --enable-cache-oblivious configure option is enabled.\n  - Fix xallocx(..., MALLOCX_ZERO) to zero trailing bytes of huge allocations\n    when resizing from/to a size class that is not a multiple of the chunk size.\n  - Fix prof_tctx_dump_iter() to filter out nodes that were created after heap\n    profile dumping started.\n  - Work around a potentially bad thread-specific data initialization\n    interaction with NPTL (glibc's pthreads implementation).\n\n* 4.0.2 (September 21, 2015)\n\n  This bugfix release addresses a few bugs specific to heap profiling.\n\n  Bug fixes:\n  - Fix ixallocx_prof_sample() to never modify nor create sampled small\n    allocations.  xallocx() is in general incapable of moving small allocations,\n    so this fix removes buggy code without loss of generality.\n  - Fix irallocx_prof_sample() to always allocate large regions, even when\n    alignment is non-zero.\n  - Fix prof_alloc_rollback() to read tdata from thread-specific data rather\n    than dereferencing a potentially invalid tctx.\n\n* 4.0.1 (September 15, 2015)\n\n  This is a bugfix release that is somewhat high risk due to the amount of\n  refactoring required to address deep xallocx() problems.  As a side effect of\n  these fixes, xallocx() now tries harder to partially fulfill requests for\n  optional extra space.  Note that a couple of minor heap profiling\n  optimizations are included, but these are better thought of as performance\n  fixes that were integral to discovering most of the other bugs.\n\n  Optimizations:\n  - Avoid a chunk metadata read in arena_prof_tctx_set(), since it is in the\n    fast path when heap profiling is enabled.  Additionally, split a special\n    case out into arena_prof_tctx_reset(), which also avoids chunk metadata\n    reads.\n  - Optimize irallocx_prof() to optimistically update the sampler state.  The\n    prior implementation appears to have been a holdover from when\n    rallocx()/xallocx() functionality was combined as rallocm().\n\n  Bug fixes:\n  - Fix TLS configuration such that it is enabled by default for platforms on\n    which it works correctly.\n  - Fix arenas_cache_cleanup() and arena_get_hard() to handle\n    allocation/deallocation within the application's thread-specific data\n    cleanup functions even after arenas_cache is torn down.\n  - Fix xallocx() bugs related to size+extra exceeding HUGE_MAXCLASS.\n  - Fix chunk purge hook calls for in-place huge shrinking reallocation to\n    specify the old chunk size rather than the new chunk size.  This bug caused\n    no correctness issues for the default chunk purge function, but was\n    visible to custom functions set via the \"arena.<i>.chunk_hooks\" mallctl.\n  - Fix heap profiling bugs:\n    + Fix heap profiling to distinguish among otherwise identical sample sites\n      with interposed resets (triggered via the \"prof.reset\" mallctl).  This bug\n      could cause data structure corruption that would most likely result in a\n      segfault.\n    + Fix irealloc_prof() to prof_alloc_rollback() on OOM.\n    + Make one call to prof_active_get_unlocked() per allocation event, and use\n      the result throughout the relevant functions that handle an allocation\n      event.  Also add a missing check in prof_realloc().  These fixes protect\n      allocation events against concurrent prof_active changes.\n    + Fix ixallocx_prof() to pass usize_max and zero to ixallocx_prof_sample()\n      in the correct order.\n    + Fix prof_realloc() to call prof_free_sampled_object() after calling\n      prof_malloc_sample_object().  Prior to this fix, if tctx and old_tctx were\n      the same, the tctx could have been prematurely destroyed.\n  - Fix portability bugs:\n    + Don't bitshift by negative amounts when encoding/decoding run sizes in\n      chunk header maps.  This affected systems with page sizes greater than 8\n      KiB.\n    + Rename index_t to szind_t to avoid an existing type on Solaris.\n    + Add JEMALLOC_CXX_THROW to the memalign() function prototype, in order to\n      match glibc and avoid compilation errors when including both\n      jemalloc/jemalloc.h and malloc.h in C++ code.\n    + Don't assume that /bin/sh is appropriate when running size_classes.sh\n      during configuration.\n    + Consider __sparcv9 a synonym for __sparc64__ when defining LG_QUANTUM.\n    + Link tests to librt if it contains clock_gettime(2).\n\n* 4.0.0 (August 17, 2015)\n\n  This version contains many speed and space optimizations, both minor and\n  major.  The major themes are generalization, unification, and simplification.\n  Although many of these optimizations cause no visible behavior change, their\n  cumulative effect is substantial.\n\n  New features:\n  - Normalize size class spacing to be consistent across the complete size\n    range.  By default there are four size classes per size doubling, but this\n    is now configurable via the --with-lg-size-class-group option.  Also add the\n    --with-lg-page, --with-lg-page-sizes, --with-lg-quantum, and\n    --with-lg-tiny-min options, which can be used to tweak page and size class\n    settings.  Impacts:\n    + Worst case performance for incrementally growing/shrinking reallocation\n      is improved because there are far fewer size classes, and therefore\n      copying happens less often.\n    + Internal fragmentation is limited to 20% for all but the smallest size\n      classes (those less than four times the quantum).  (1B + 4 KiB)\n      and (1B + 4 MiB) previously suffered nearly 50% internal fragmentation.\n    + Chunk fragmentation tends to be lower because there are fewer distinct run\n      sizes to pack.\n  - Add support for explicit tcaches.  The \"tcache.create\", \"tcache.flush\", and\n    \"tcache.destroy\" mallctls control tcache lifetime and flushing, and the\n    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to the *allocx() API\n    control which tcache is used for each operation.\n  - Implement per thread heap profiling, as well as the ability to\n    enable/disable heap profiling on a per thread basis.  Add the \"prof.reset\",\n    \"prof.lg_sample\", \"thread.prof.name\", \"thread.prof.active\",\n    \"opt.prof_thread_active_init\", \"prof.thread_active_init\", and\n    \"thread.prof.active\" mallctls.\n  - Add support for per arena application-specified chunk allocators, configured\n    via the \"arena.<i>.chunk_hooks\" mallctl.\n  - Refactor huge allocation to be managed by arenas, so that arenas now\n    function as general purpose independent allocators.  This is important in\n    the context of user-specified chunk allocators, aside from the scalability\n    benefits.  Related new statistics:\n    + The \"stats.arenas.<i>.huge.allocated\", \"stats.arenas.<i>.huge.nmalloc\",\n      \"stats.arenas.<i>.huge.ndalloc\", and \"stats.arenas.<i>.huge.nrequests\"\n      mallctls provide high level per arena huge allocation statistics.\n    + The \"arenas.nhchunks\", \"arenas.hchunk.<i>.size\",\n      \"stats.arenas.<i>.hchunks.<j>.nmalloc\",\n      \"stats.arenas.<i>.hchunks.<j>.ndalloc\",\n      \"stats.arenas.<i>.hchunks.<j>.nrequests\", and\n      \"stats.arenas.<i>.hchunks.<j>.curhchunks\" mallctls provide per size class\n      statistics.\n  - Add the 'util' column to malloc_stats_print() output, which reports the\n    proportion of available regions that are currently in use for each small\n    size class.\n  - Add \"alloc\" and \"free\" modes for for junk filling (see the \"opt.junk\"\n    mallctl), so that it is possible to separately enable junk filling for\n    allocation versus deallocation.\n  - Add the jemalloc-config script, which provides information about how\n    jemalloc was configured, and how to integrate it into application builds.\n  - Add metadata statistics, which are accessible via the \"stats.metadata\",\n    \"stats.arenas.<i>.metadata.mapped\", and\n    \"stats.arenas.<i>.metadata.allocated\" mallctls.\n  - Add the \"stats.resident\" mallctl, which reports the upper limit of\n    physically resident memory mapped by the allocator.\n  - Add per arena control over unused dirty page purging, via the\n    \"arenas.lg_dirty_mult\", \"arena.<i>.lg_dirty_mult\", and\n    \"stats.arenas.<i>.lg_dirty_mult\" mallctls.\n  - Add the \"prof.gdump\" mallctl, which makes it possible to toggle the gdump\n    feature on/off during program execution.\n  - Add sdallocx(), which implements sized deallocation.  The primary\n    optimization over dallocx() is the removal of a metadata read, which often\n    suffers an L1 cache miss.\n  - Add missing header includes in jemalloc/jemalloc.h, so that applications\n    only have to #include <jemalloc/jemalloc.h>.\n  - Add support for additional platforms:\n    + Bitrig\n    + Cygwin\n    + DragonFlyBSD\n    + iOS\n    + OpenBSD\n    + OpenRISC/or1k\n\n  Optimizations:\n  - Maintain dirty runs in per arena LRUs rather than in per arena trees of\n    dirty-run-containing chunks.  In practice this change significantly reduces\n    dirty page purging volume.\n  - Integrate whole chunks into the unused dirty page purging machinery.  This\n    reduces the cost of repeated huge allocation/deallocation, because it\n    effectively introduces a cache of chunks.\n  - Split the arena chunk map into two separate arrays, in order to increase\n    cache locality for the frequently accessed bits.\n  - Move small run metadata out of runs, into arena chunk headers.  This reduces\n    run fragmentation, smaller runs reduce external fragmentation for small size\n    classes, and packed (less uniformly aligned) metadata layout improves CPU\n    cache set distribution.\n  - Randomly distribute large allocation base pointer alignment relative to page\n    boundaries in order to more uniformly utilize CPU cache sets.  This can be\n    disabled via the --disable-cache-oblivious configure option, and queried via\n    the \"config.cache_oblivious\" mallctl.\n  - Micro-optimize the fast paths for the public API functions.\n  - Refactor thread-specific data to reside in a single structure.  This assures\n    that only a single TLS read is necessary per call into the public API.\n  - Implement in-place huge allocation growing and shrinking.\n  - Refactor rtree (radix tree for chunk lookups) to be lock-free, and make\n    additional optimizations that reduce maximum lookup depth to one or two\n    levels.  This resolves what was a concurrency bottleneck for per arena huge\n    allocation, because a global data structure is critical for determining\n    which arenas own which huge allocations.\n\n  Incompatible changes:\n  - Replace --enable-cc-silence with --disable-cc-silence to suppress spurious\n    warnings by default.\n  - Assure that the constness of malloc_usable_size()'s return type matches that\n    of the system implementation.\n  - Change the heap profile dump format to support per thread heap profiling,\n    rename pprof to jeprof, and enhance it with the --thread=<n> option.  As a\n    result, the bundled jeprof must now be used rather than the upstream\n    (gperftools) pprof.\n  - Disable \"opt.prof_final\" by default, in order to avoid atexit(3), which can\n    internally deadlock on some platforms.\n  - Change the \"arenas.nlruns\" mallctl type from size_t to unsigned.\n  - Replace the \"stats.arenas.<i>.bins.<j>.allocated\" mallctl with\n    \"stats.arenas.<i>.bins.<j>.curregs\".\n  - Ignore MALLOC_CONF in set{uid,gid,cap} binaries.\n  - Ignore MALLOCX_ARENA(a) in dallocx(), in favor of using the\n    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to control tcache usage.\n\n  Removed features:\n  - Remove the *allocm() API, which is superseded by the *allocx() API.\n  - Remove the --enable-dss options, and make dss non-optional on all platforms\n    which support sbrk(2).\n  - Remove the \"arenas.purge\" mallctl, which was obsoleted by the\n    \"arena.<i>.purge\" mallctl in 3.1.0.\n  - Remove the unnecessary \"opt.valgrind\" mallctl; jemalloc automatically\n    detects whether it is running inside Valgrind.\n  - Remove the \"stats.huge.allocated\", \"stats.huge.nmalloc\", and\n    \"stats.huge.ndalloc\" mallctls.\n  - Remove the --enable-mremap option.\n  - Remove the \"stats.chunks.current\", \"stats.chunks.total\", and\n    \"stats.chunks.high\" mallctls.\n\n  Bug fixes:\n  - Fix the cactive statistic to decrease (rather than increase) when active\n    memory decreases.  This regression was first released in 3.5.0.\n  - Fix OOM handling in memalign() and valloc().  A variant of this bug existed\n    in all releases since 2.0.0, which introduced these functions.\n  - Fix an OOM-related regression in arena_tcache_fill_small(), which could\n    cause cache corruption on OOM.  This regression was present in all releases\n    from 2.2.0 through 3.6.0.\n  - Fix size class overflow handling for malloc(), posix_memalign(), memalign(),\n    calloc(), and realloc() when profiling is enabled.\n  - Fix the \"arena.<i>.dss\" mallctl to return an error if \"primary\" or\n    \"secondary\" precedence is specified, but sbrk(2) is not supported.\n  - Fix fallback lg_floor() implementations to handle extremely large inputs.\n  - Ensure the default purgeable zone is after the default zone on OS X.\n  - Fix latent bugs in atomic_*().\n  - Fix the \"arena.<i>.dss\" mallctl to handle read-only calls.\n  - Fix tls_model configuration to enable the initial-exec model when possible.\n  - Mark malloc_conf as a weak symbol so that the application can override it.\n  - Correctly detect glibc's adaptive pthread mutexes.\n  - Fix the --without-export configure option.\n\n* 3.6.0 (March 31, 2014)\n\n  This version contains a critical bug fix for a regression present in 3.5.0 and\n  3.5.1.\n\n  Bug fixes:\n  - Fix a regression in arena_chunk_alloc() that caused crashes during\n    small/large allocation if chunk allocation failed.  In the absence of this\n    bug, chunk allocation failure would result in allocation failure, e.g.  NULL\n    return from malloc().  This regression was introduced in 3.5.0.\n  - Fix backtracing for gcc intrinsics-based backtracing by specifying\n    -fno-omit-frame-pointer to gcc.  Note that the application (and all the\n    libraries it links to) must also be compiled with this option for\n    backtracing to be reliable.\n  - Use dss allocation precedence for huge allocations as well as small/large\n    allocations.\n  - Fix test assertion failure message formatting.  This bug did not manifest on\n    x86_64 systems because of implementation subtleties in va_list.\n  - Fix inconsequential test failures for hash and SFMT code.\n\n  New features:\n  - Support heap profiling on FreeBSD.  This feature depends on the proc\n    filesystem being mounted during heap profile dumping.\n\n* 3.5.1 (February 25, 2014)\n\n  This version primarily addresses minor bugs in test code.\n\n  Bug fixes:\n  - Configure Solaris/Illumos to use MADV_FREE.\n  - Fix junk filling for mremap(2)-based huge reallocation.  This is only\n    relevant if configuring with the --enable-mremap option specified.\n  - Avoid compilation failure if 'restrict' C99 keyword is not supported by the\n    compiler.\n  - Add a configure test for SSE2 rather than assuming it is usable on i686\n    systems.  This fixes test compilation errors, especially on 32-bit Linux\n    systems.\n  - Fix mallctl argument size mismatches (size_t vs. uint64_t) in the stats unit\n    test.\n  - Fix/remove flawed alignment-related overflow tests.\n  - Prevent compiler optimizations that could change backtraces in the\n    prof_accum unit test.\n\n* 3.5.0 (January 22, 2014)\n\n  This version focuses on refactoring and automated testing, though it also\n  includes some non-trivial heap profiling optimizations not mentioned below.\n\n  New features:\n  - Add the *allocx() API, which is a successor to the experimental *allocm()\n    API.  The *allocx() functions are slightly simpler to use because they have\n    fewer parameters, they directly return the results of primary interest, and\n    mallocx()/rallocx() avoid the strict aliasing pitfall that\n    allocm()/rallocm() share with posix_memalign().  Note that *allocm() is\n    slated for removal in the next non-bugfix release.\n  - Add support for LinuxThreads.\n\n  Bug fixes:\n  - Unless heap profiling is enabled, disable floating point code and don't link\n    with libm.  This, in combination with e.g. EXTRA_CFLAGS=-mno-sse on x64\n    systems, makes it possible to completely disable floating point register\n    use.  Some versions of glibc neglect to save/restore caller-saved floating\n    point registers during dynamic lazy symbol loading, and the symbol loading\n    code uses whatever malloc the application happens to have linked/loaded\n    with, the result being potential floating point register corruption.\n  - Report ENOMEM rather than EINVAL if an OOM occurs during heap profiling\n    backtrace creation in imemalign().  This bug impacted posix_memalign() and\n    aligned_alloc().\n  - Fix a file descriptor leak in a prof_dump_maps() error path.\n  - Fix prof_dump() to close the dump file descriptor for all relevant error\n    paths.\n  - Fix rallocm() to use the arena specified by the ALLOCM_ARENA(s) flag for\n    allocation, not just deallocation.\n  - Fix a data race for large allocation stats counters.\n  - Fix a potential infinite loop during thread exit.  This bug occurred on\n    Solaris, and could affect other platforms with similar pthreads TSD\n    implementations.\n  - Don't junk-fill reallocations unless usable size changes.  This fixes a\n    violation of the *allocx()/*allocm() semantics.\n  - Fix growing large reallocation to junk fill new space.\n  - Fix huge deallocation to junk fill when munmap is disabled.\n  - Change the default private namespace prefix from empty to je_, and change\n    --with-private-namespace-prefix so that it prepends an additional prefix\n    rather than replacing je_.  This reduces the likelihood of applications\n    which statically link jemalloc experiencing symbol name collisions.\n  - Add missing private namespace mangling (relevant when\n    --with-private-namespace is specified).\n  - Add and use JEMALLOC_INLINE_C so that static inline functions are marked as\n    static even for debug builds.\n  - Add a missing mutex unlock in a malloc_init_hard() error path.  In practice\n    this error path is never executed.\n  - Fix numerous bugs in malloc_strotumax() error handling/reporting.  These\n    bugs had no impact except for malformed inputs.\n  - Fix numerous bugs in malloc_snprintf().  These bugs were not exercised by\n    existing calls, so they had no impact.\n\n* 3.4.1 (October 20, 2013)\n\n  Bug fixes:\n  - Fix a race in the \"arenas.extend\" mallctl that could cause memory corruption\n    of internal data structures and subsequent crashes.\n  - Fix Valgrind integration flaws that caused Valgrind warnings about reads of\n    uninitialized memory in:\n    + arena chunk headers\n    + internal zero-initialized data structures (relevant to tcache and prof\n      code)\n  - Preserve errno during the first allocation.  A readlink(2) call during\n    initialization fails unless /etc/malloc.conf exists, so errno was typically\n    set during the first allocation prior to this fix.\n  - Fix compilation warnings reported by gcc 4.8.1.\n\n* 3.4.0 (June 2, 2013)\n\n  This version is essentially a small bugfix release, but the addition of\n  aarch64 support requires that the minor version be incremented.\n\n  Bug fixes:\n  - Fix race-triggered deadlocks in chunk_record().  These deadlocks were\n    typically triggered by multiple threads concurrently deallocating huge\n    objects.\n\n  New features:\n  - Add support for the aarch64 architecture.\n\n* 3.3.1 (March 6, 2013)\n\n  This version fixes bugs that are typically encountered only when utilizing\n  custom run-time options.\n\n  Bug fixes:\n  - Fix a locking order bug that could cause deadlock during fork if heap\n    profiling were enabled.\n  - Fix a chunk recycling bug that could cause the allocator to lose track of\n    whether a chunk was zeroed.  On FreeBSD, NetBSD, and OS X, it could cause\n    corruption if allocating via sbrk(2) (unlikely unless running with the\n    \"dss:primary\" option specified).  This was completely harmless on Linux\n    unless using mlockall(2) (and unlikely even then, unless the\n    --disable-munmap configure option or the \"dss:primary\" option was\n    specified).  This regression was introduced in 3.1.0 by the\n    mlockall(2)/madvise(2) interaction fix.\n  - Fix TLS-related memory corruption that could occur during thread exit if the\n    thread never allocated memory.  Only the quarantine and prof facilities were\n    susceptible.\n  - Fix two quarantine bugs:\n    + Internal reallocation of the quarantined object array leaked the old\n      array.\n    + Reallocation failure for internal reallocation of the quarantined object\n      array (very unlikely) resulted in memory corruption.\n  - Fix Valgrind integration to annotate all internally allocated memory in a\n    way that keeps Valgrind happy about internal data structure access.\n  - Fix building for s390 systems.\n\n* 3.3.0 (January 23, 2013)\n\n  This version includes a few minor performance improvements in addition to the\n  listed new features and bug fixes.\n\n  New features:\n  - Add clipping support to lg_chunk option processing.\n  - Add the --enable-ivsalloc option.\n  - Add the --without-export option.\n  - Add the --disable-zone-allocator option.\n\n  Bug fixes:\n  - Fix \"arenas.extend\" mallctl to output the number of arenas.\n  - Fix chunk_recycle() to unconditionally inform Valgrind that returned memory\n    is undefined.\n  - Fix build break on FreeBSD related to alloca.h.\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 additional 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": "Chapter02/redis-6.0.3/deps/jemalloc/INSTALL.md",
    "content": "Building and installing a packaged release of jemalloc can be as simple as\ntyping the following while in the root directory of the source tree:\n\n    ./configure\n    make\n    make install\n\nIf building from unpackaged developer sources, the simplest command sequence\nthat might work is:\n\n    ./autogen.sh\n    make dist\n    make\n    make install\n\nNote that documentation is not built by the default target because doing so\nwould create a dependency on xsltproc in packaged releases, hence the\nrequirement to either run 'make dist' or avoid installing docs via the various\ninstall_* targets documented below.\n\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\n    Print a definitive list of options.\n\n* `--prefix=<install-root-dir>`\n\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-version=(<major>.<minor>.<bugfix>-<nrev>-g<gid>|VERSION)`\n\n    The VERSION file is mandatory for successful configuration, and the\n    following steps are taken to assure its presence:\n    1) If --with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid> is specified,\n       generate VERSION using the specified value.\n    2) If --with-version is not specified in either form and the source\n       directory is inside a git repository, try to generate VERSION via 'git\n       describe' invocations that pattern-match release tags.\n    3) If VERSION is missing, generate it with a bogus version:\n       0.0.0-0-g0000000000000000000000000000000000000000\n\n    Note that --with-version=VERSION bypasses (1) and (2), which simplifies\n    VERSION configuration when embedding a jemalloc release into another\n    project's git repository.\n\n* `--with-rpath=<colon-separated-rpath>`\n\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\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\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* `--without-export`\n\n    Don't export public APIs.  This can be useful when building jemalloc as a\n    static library, or to avoid exporting public APIs when using the zone\n    allocator on OSX.\n\n* `--with-private-namespace=<prefix>`\n\n    Prefix all library-private APIs with <prefix>je_.  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, <prefix> is empty, which results in a symbol prefix of je_ .\n\n* `--with-install-suffix=<suffix>`\n\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* `--with-malloc-conf=<malloc_conf>`\n\n    Embed `<malloc_conf>` as a run-time options string that is processed prior to\n    the malloc_conf global variable, the /etc/malloc.conf symlink, and the\n    MALLOC_CONF environment variable.  For example, to change the default decay\n    time to 30 seconds:\n\n      --with-malloc-conf=decay_ms:30000\n\n* `--enable-debug`\n\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\n    Disable statistics gathering functionality.  See the \"opt.stats_print\"\n    option documentation for usage details.\n\n* `--enable-prof`\n\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\n    Use the libunwind library (http://www.nongnu.org/libunwind/) for stack\n    backtracing.\n\n* `--disable-prof-libgcc`\n\n    Disable the use of libgcc's backtracing functionality.\n\n* `--disable-prof-gcc`\n\n    Disable the use of gcc intrinsics for backtracing.\n\n* `--with-static-libunwind=<libunwind.a>`\n\n    Statically link against the specified libunwind.a rather than dynamically\n    linking with -lunwind.\n\n* `--disable-fill`\n\n    Disable support for junk/zero filling of memory.  See the \"opt.junk\" and\n    \"opt.zero\" option documentation for usage details.\n\n* `--disable-zone-allocator`\n\n    Disable zone allocator for Darwin.  This means jemalloc won't be hooked as\n    the default allocator on OSX/iOS.\n\n* `--enable-utrace`\n\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\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\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-cache-oblivious`\n\n    Disable cache-oblivious large allocation alignment for large allocation\n    requests with no alignment constraints.  If this feature is disabled, all\n    large allocations are page-aligned as an implementation artifact, which can\n    severely harm CPU cache utilization.  However, the cache-oblivious layout\n    comes at the cost of one extra page per large allocation, which in the\n    most extreme case increases physical memory usage for the 16 KiB size class\n    to 20 KiB.\n\n* `--disable-syscall`\n\n    Disable use of syscall(2) rather than {open,read,write,close}(2).  This is\n    intended as a workaround for systems that place security limitations on\n    syscall(2).\n\n* `--disable-cxx`\n\n    Disable C++ integration.  This will cause new and delete operator\n    implementations to be omitted.\n\n* `--with-xslroot=<path>`\n\n    Specify where to find DocBook XSL stylesheets when building the\n    documentation.\n\n* `--with-lg-page=<lg-page>`\n\n    Specify the base 2 log of the allocator page size, which must in turn be at\n    least as large as the system page size.  By default the configure script\n    determines the host's page size and sets the allocator page size equal to\n    the system page size, so this option need not be specified unless the\n    system page size may change between configuration and execution, e.g. when\n    cross compiling.\n\n* `--with-lg-page-sizes=<lg-page-sizes>`\n\n    Specify the comma-separated base 2 logs of the page sizes to support.  This\n    option may be useful when cross compiling in combination with\n    `--with-lg-page`, but its primary use case is for integration with FreeBSD's\n    libc, wherein jemalloc is embedded.\n\n* `--with-lg-hugepage=<lg-hugepage>`\n\n    Specify the base 2 log of the system huge page size.  This option is useful\n    when cross compiling, or when overriding the default for systems that do\n    not explicitly support huge pages.\n\n* `--with-lg-quantum=<lg-quantum>`\n\n    Specify the base 2 log of the minimum allocation alignment.  jemalloc needs\n    to know the minimum alignment that meets the following C standard\n    requirement (quoted from the April 12, 2011 draft of the C11 standard):\n\n    >  The pointer returned if the allocation succeeds is suitably aligned so\n      that it may be assigned to a pointer to any type of object with a\n      fundamental alignment requirement and then used to access such an object\n      or an array of such objects in the space allocated [...]\n\n    This setting is architecture-specific, and although jemalloc includes known\n    safe values for the most commonly used modern architectures, there is a\n    wrinkle related to GNU libc (glibc) that may impact your choice of\n    <lg-quantum>.  On most modern architectures, this mandates 16-byte\n    alignment (<lg-quantum>=4), but the glibc developers chose not to meet this\n    requirement for performance reasons.  An old discussion can be found at\n    <https://sourceware.org/bugzilla/show_bug.cgi?id=206> .  Unlike glibc,\n    jemalloc does follow the C standard by default (caveat: jemalloc\n    technically cheats for size classes smaller than the quantum), but the fact\n    that Linux systems already work around this allocator noncompliance means\n    that it is generally safe in practice to let jemalloc's minimum alignment\n    follow glibc's lead.  If you specify `--with-lg-quantum=3` during\n    configuration, jemalloc will provide additional size classes that are not\n    16-byte-aligned (24, 40, and 56).\n\n* `--with-lg-vaddr=<lg-vaddr>`\n\n    Specify the number of significant virtual address bits.  By default, the\n    configure script attempts to detect virtual address size on those platforms\n    where it knows how, and picks a default otherwise.  This option may be\n    useful when cross-compiling.\n\n* `--disable-initial-exec-tls`\n\n    Disable the initial-exec TLS model for jemalloc's internal thread-local\n    storage (on those platforms that support explicit settings).  This can allow\n    jemalloc to be dynamically loaded after program startup (e.g. using dlopen).\n    Note that in this case, there will be two malloc implementations operating\n    in the same process, which will almost certainly result in confusing runtime\n    crashes if pointers leak from one implementation to the other.\n\nThe following environment variables (not a definitive list) impact configure's\nbehavior:\n\n* `CFLAGS=\"?\"`\n* `CXXFLAGS=\"?\"`\n\n    Pass these flags to the C/C++ compiler.  Any flags set by the configure\n    script are prepended, which means explicitly set flags generally take\n    precedence.  Take care when specifying flags such as -Werror, because\n    configure tests may be affected in undesirable ways.\n\n* `EXTRA_CFLAGS=\"?\"`\n* `EXTRA_CXXFLAGS=\"?\"`\n\n    Append these flags to CFLAGS/CXXFLAGS, without passing them to the\n    compiler(s) during configuration.  This makes it possible to add flags such\n    as -Werror, while allowing the configure script to determine what other\n    flags are appropriate for the specified configuration.\n\n* `CPPFLAGS=\"?\"`\n\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\n* `LD_LIBRARY_PATH=\"?\"`\n\n    'ld' uses this colon-separated list to find libraries.\n\n* `LDFLAGS=\"?\"`\n\n    Pass these flags when linking.\n\n* `PATH=\"?\"`\n\n    'configure' uses this to find programs.\n\nIn some cases it may be necessary to work around configuration results that do\nnot match reality.  For example, Linux 4.5 added support for the MADV_FREE flag\nto madvise(2), which can cause problems if building on a host with MADV_FREE\nsupport and deploying to a target without.  To work around this, use a cache\nfile to override the relevant configuration variable defined in configure.ac,\ne.g.:\n\n    echo \"je_cv_madv_free=no\" > config.cache && ./configure -C\n\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_pc\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\n## Advanced installation\n\nOptionally, define make variables when invoking make, including (not\nexclusively):\n\n* `INCLUDEDIR=\"?\"`\n\n    Use this as the installation prefix for header files.\n\n* `LIBDIR=\"?\"`\n\n    Use this as the installation prefix for libraries.\n\n* `MANDIR=\"?\"`\n\n    Use this as the installation prefix for man pages.\n\n* `DESTDIR=\"?\"`\n\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\n* `CC=\"?\"`\n\n    Use this to invoke the C compiler.\n\n* `CFLAGS=\"?\"`\n\n    Pass these flags to the compiler.\n\n* `CPPFLAGS=\"?\"`\n\n    Pass these flags to the C preprocessor.\n\n* `LDFLAGS=\"?\"`\n\n    Pass these flags when linking.\n\n* `PATH=\"?\"`\n\n    Use this to search for programs used during configuration and building.\n\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\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": "Chapter02/redis-6.0.3/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@\nCXX := @CXX@\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$(objroot)include -I$(srcroot)include\nCONFIGURE_CFLAGS := @CONFIGURE_CFLAGS@\nSPECIFIED_CFLAGS := @SPECIFIED_CFLAGS@\nEXTRA_CFLAGS := @EXTRA_CFLAGS@\nCFLAGS := $(strip $(CONFIGURE_CFLAGS) $(SPECIFIED_CFLAGS) $(EXTRA_CFLAGS))\nCONFIGURE_CXXFLAGS := @CONFIGURE_CXXFLAGS@\nSPECIFIED_CXXFLAGS := @SPECIFIED_CXXFLAGS@\nEXTRA_CXXFLAGS := @EXTRA_CXXFLAGS@\nCXXFLAGS := $(strip $(CONFIGURE_CXXFLAGS) $(SPECIFIED_CXXFLAGS) $(EXTRA_CXXFLAGS))\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 := $(addprefix $(srcroot),@cfghdrs_in@)\ncfghdrs_out := @cfghdrs_out@\ncfgoutputs_in := $(addprefix $(srcroot),@cfgoutputs_in@)\ncfgoutputs_out := @cfgoutputs_out@\nenable_autogen := @enable_autogen@\nenable_prof := @enable_prof@\nenable_zone_allocator := @enable_zone_allocator@\nMALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF\nlink_whole_archive := @link_whole_archive@\nDSO_LDFLAGS = @DSO_LDFLAGS@\nSOREV = @SOREV@\nPIC_CFLAGS = @PIC_CFLAGS@\nCTARGET = @CTARGET@\nLDTARGET = @LDTARGET@\nTEST_LD_MODE = @TEST_LD_MODE@\nMKLIB = @MKLIB@\nAR = @AR@\nARFLAGS = @ARFLAGS@\nDUMP_SYMS = @DUMP_SYMS@\nAWK := @AWK@\nCC_MM = @CC_MM@\nLM := @LM@\nINSTALL = @INSTALL@\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 := $(objroot)bin/jemalloc-config $(objroot)bin/jemalloc.sh $(objroot)bin/jeprof\nC_HDRS := $(objroot)include/jemalloc/jemalloc$(install_suffix).h\nC_SRCS := $(srcroot)src/jemalloc.c \\\n\t$(srcroot)src/arena.c \\\n\t$(srcroot)src/background_thread.c \\\n\t$(srcroot)src/base.c \\\n\t$(srcroot)src/bin.c \\\n\t$(srcroot)src/bitmap.c \\\n\t$(srcroot)src/ckh.c \\\n\t$(srcroot)src/ctl.c \\\n\t$(srcroot)src/div.c \\\n\t$(srcroot)src/extent.c \\\n\t$(srcroot)src/extent_dss.c \\\n\t$(srcroot)src/extent_mmap.c \\\n\t$(srcroot)src/hash.c \\\n\t$(srcroot)src/hooks.c \\\n\t$(srcroot)src/large.c \\\n\t$(srcroot)src/log.c \\\n\t$(srcroot)src/malloc_io.c \\\n\t$(srcroot)src/mutex.c \\\n\t$(srcroot)src/mutex_pool.c \\\n\t$(srcroot)src/nstime.c \\\n\t$(srcroot)src/pages.c \\\n\t$(srcroot)src/prng.c \\\n\t$(srcroot)src/prof.c \\\n\t$(srcroot)src/rtree.c \\\n\t$(srcroot)src/stats.c \\\n\t$(srcroot)src/sz.c \\\n\t$(srcroot)src/tcache.c \\\n\t$(srcroot)src/ticker.c \\\n\t$(srcroot)src/tsd.c \\\n\t$(srcroot)src/witness.c\nifeq ($(enable_zone_allocator), 1)\nC_SRCS += $(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\nifeq (1, $(link_whole_archive))\nLJEMALLOC := -Wl,--whole-archive -L$(objroot)lib -l$(LIBJEMALLOC) -Wl,--no-whole-archive\nelse\nLJEMALLOC := $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\nendif\nPC := $(objroot)jemalloc.pc\nMAN3 := $(objroot)doc/jemalloc$(install_suffix).3\nDOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml\nDOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.html)\nDOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.3)\nDOCS := $(DOCS_HTML) $(DOCS_MAN3)\nC_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \\\n\t$(srcroot)test/src/btalloc_1.c $(srcroot)test/src/math.c \\\n\t$(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \\\n\t$(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \\\n\t$(srcroot)test/src/thd.c $(srcroot)test/src/timer.c\nifeq (1, $(link_whole_archive))\nC_UTIL_INTEGRATION_SRCS :=\nC_UTIL_CPP_SRCS :=\nelse\nC_UTIL_INTEGRATION_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c\nC_UTIL_CPP_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c\nendif\nTESTS_UNIT := \\\n\t$(srcroot)test/unit/a0.c \\\n\t$(srcroot)test/unit/arena_reset.c \\\n\t$(srcroot)test/unit/atomic.c \\\n\t$(srcroot)test/unit/background_thread.c \\\n\t$(srcroot)test/unit/background_thread_enable.c \\\n\t$(srcroot)test/unit/base.c \\\n\t$(srcroot)test/unit/bitmap.c \\\n\t$(srcroot)test/unit/ckh.c \\\n\t$(srcroot)test/unit/decay.c \\\n\t$(srcroot)test/unit/div.c \\\n\t$(srcroot)test/unit/emitter.c \\\n\t$(srcroot)test/unit/extent_quantize.c \\\n\t$(srcroot)test/unit/fork.c \\\n\t$(srcroot)test/unit/hash.c \\\n\t$(srcroot)test/unit/hooks.c \\\n\t$(srcroot)test/unit/junk.c \\\n\t$(srcroot)test/unit/junk_alloc.c \\\n\t$(srcroot)test/unit/junk_free.c \\\n\t$(srcroot)test/unit/log.c \\\n\t$(srcroot)test/unit/mallctl.c \\\n\t$(srcroot)test/unit/malloc_io.c \\\n\t$(srcroot)test/unit/math.c \\\n\t$(srcroot)test/unit/mq.c \\\n\t$(srcroot)test/unit/mtx.c \\\n\t$(srcroot)test/unit/pack.c \\\n\t$(srcroot)test/unit/pages.c \\\n\t$(srcroot)test/unit/ph.c \\\n\t$(srcroot)test/unit/prng.c \\\n\t$(srcroot)test/unit/prof_accum.c \\\n\t$(srcroot)test/unit/prof_active.c \\\n\t$(srcroot)test/unit/prof_gdump.c \\\n\t$(srcroot)test/unit/prof_idump.c \\\n\t$(srcroot)test/unit/prof_reset.c \\\n\t$(srcroot)test/unit/prof_tctx.c \\\n\t$(srcroot)test/unit/prof_thread_name.c \\\n\t$(srcroot)test/unit/ql.c \\\n\t$(srcroot)test/unit/qr.c \\\n\t$(srcroot)test/unit/rb.c \\\n\t$(srcroot)test/unit/retained.c \\\n\t$(srcroot)test/unit/rtree.c \\\n\t$(srcroot)test/unit/SFMT.c \\\n\t$(srcroot)test/unit/size_classes.c \\\n\t$(srcroot)test/unit/slab.c \\\n\t$(srcroot)test/unit/smoothstep.c \\\n\t$(srcroot)test/unit/spin.c \\\n\t$(srcroot)test/unit/stats.c \\\n\t$(srcroot)test/unit/stats_print.c \\\n\t$(srcroot)test/unit/ticker.c \\\n\t$(srcroot)test/unit/nstime.c \\\n\t$(srcroot)test/unit/tsd.c \\\n\t$(srcroot)test/unit/witness.c \\\n\t$(srcroot)test/unit/zero.c\nifeq (@enable_prof@, 1)\nTESTS_UNIT += \\\n\t$(srcroot)test/unit/arena_reset_prof.c\nendif\nTESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \\\n\t$(srcroot)test/integration/allocated.c \\\n\t$(srcroot)test/integration/extent.c \\\n\t$(srcroot)test/integration/mallocx.c \\\n\t$(srcroot)test/integration/MALLOCX_ARENA.c \\\n\t$(srcroot)test/integration/overflow.c \\\n\t$(srcroot)test/integration/posix_memalign.c \\\n\t$(srcroot)test/integration/rallocx.c \\\n\t$(srcroot)test/integration/sdallocx.c \\\n\t$(srcroot)test/integration/thread_arena.c \\\n\t$(srcroot)test/integration/thread_tcache_enabled.c \\\n\t$(srcroot)test/integration/xallocx.c\nifeq (@enable_cxx@, 1)\nCPP_SRCS := $(srcroot)src/jemalloc_cpp.cpp\nTESTS_INTEGRATION_CPP := $(srcroot)test/integration/cpp/basic.cpp\nelse\nCPP_SRCS :=\nTESTS_INTEGRATION_CPP :=\nendif\nTESTS_STRESS := $(srcroot)test/stress/microbench.c\n\nTESTS := $(TESTS_UNIT) $(TESTS_INTEGRATION) $(TESTS_INTEGRATION_CPP) $(TESTS_STRESS)\n\nPRIVATE_NAMESPACE_HDRS := $(objroot)include/jemalloc/internal/private_namespace.h $(objroot)include/jemalloc/internal/private_namespace_jet.h\nPRIVATE_NAMESPACE_GEN_HDRS := $(PRIVATE_NAMESPACE_HDRS:%.h=%.gen.h)\nC_SYM_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.sym.$(O))\nC_SYMS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.sym)\nC_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.$(O))\nCPP_OBJS := $(CPP_SRCS:$(srcroot)%.cpp=$(objroot)%.$(O))\nC_PIC_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.pic.$(O))\nCPP_PIC_OBJS := $(CPP_SRCS:$(srcroot)%.cpp=$(objroot)%.pic.$(O))\nC_JET_SYM_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.sym.$(O))\nC_JET_SYMS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.sym)\nC_JET_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.$(O))\nC_TESTLIB_UNIT_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.unit.$(O))\nC_TESTLIB_INTEGRATION_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))\nC_UTIL_INTEGRATION_OBJS := $(C_UTIL_INTEGRATION_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))\nC_TESTLIB_STRESS_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.stress.$(O))\nC_TESTLIB_OBJS := $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(C_TESTLIB_STRESS_OBJS)\n\nTESTS_UNIT_OBJS := $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%.$(O))\nTESTS_INTEGRATION_OBJS := $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%.$(O))\nTESTS_INTEGRATION_CPP_OBJS := $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%.$(O))\nTESTS_STRESS_OBJS := $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%.$(O))\nTESTS_OBJS := $(TESTS_UNIT_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_STRESS_OBJS)\nTESTS_CPP_OBJS := $(TESTS_INTEGRATION_CPP_OBJS)\n\n.PHONY: all dist build_doc_html build_doc_man build_doc\n.PHONY: install_bin install_include install_lib\n.PHONY: install_doc_html install_doc_man install_doc install\n.PHONY: tests check clean distclean relclean\n\n.SECONDARY : $(PRIVATE_NAMESPACE_GEN_HDRS) $(TESTS_OBJS) $(TESTS_CPP_OBJS)\n\n# Default target.\nall: build_lib\n\ndist: build_doc\n\n$(objroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl\n\t$(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<\n\n$(objroot)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 $(C_SYM_OBJS:%.$(O)=%.d)\n-include $(C_OBJS:%.$(O)=%.d)\n-include $(CPP_OBJS:%.$(O)=%.d)\n-include $(C_PIC_OBJS:%.$(O)=%.d)\n-include $(CPP_PIC_OBJS:%.$(O)=%.d)\n-include $(C_JET_SYM_OBJS:%.$(O)=%.d)\n-include $(C_JET_OBJS:%.$(O)=%.d)\n-include $(C_TESTLIB_OBJS:%.$(O)=%.d)\n-include $(TESTS_OBJS:%.$(O)=%.d)\n-include $(TESTS_CPP_OBJS:%.$(O)=%.d)\nendif\n\n$(C_SYM_OBJS): $(objroot)src/%.sym.$(O): $(srcroot)src/%.c\n$(C_SYM_OBJS): CPPFLAGS += -DJEMALLOC_NO_PRIVATE_NAMESPACE\n$(C_SYMS): $(objroot)src/%.sym: $(objroot)src/%.sym.$(O)\n$(C_OBJS): $(objroot)src/%.$(O): $(srcroot)src/%.c\n$(CPP_OBJS): $(objroot)src/%.$(O): $(srcroot)src/%.cpp\n$(C_PIC_OBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.c\n$(C_PIC_OBJS): CFLAGS += $(PIC_CFLAGS)\n$(CPP_PIC_OBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.cpp\n$(CPP_PIC_OBJS): CXXFLAGS += $(PIC_CFLAGS)\n$(C_JET_SYM_OBJS): $(objroot)src/%.jet.sym.$(O): $(srcroot)src/%.c\n$(C_JET_SYM_OBJS): CPPFLAGS += -DJEMALLOC_JET -DJEMALLOC_NO_PRIVATE_NAMESPACE\n$(C_JET_SYMS): $(objroot)src/%.jet.sym: $(objroot)src/%.jet.sym.$(O)\n$(C_JET_OBJS): $(objroot)src/%.jet.$(O): $(srcroot)src/%.c\n$(C_JET_OBJS): CPPFLAGS += -DJEMALLOC_JET\n$(C_TESTLIB_UNIT_OBJS): $(objroot)test/src/%.unit.$(O): $(srcroot)test/src/%.c\n$(C_TESTLIB_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST\n$(C_TESTLIB_INTEGRATION_OBJS): $(objroot)test/src/%.integration.$(O): $(srcroot)test/src/%.c\n$(C_TESTLIB_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST\n$(C_UTIL_INTEGRATION_OBJS): $(objroot)src/%.integration.$(O): $(srcroot)src/%.c\n$(C_TESTLIB_STRESS_OBJS): $(objroot)test/src/%.stress.$(O): $(srcroot)test/src/%.c\n$(C_TESTLIB_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST -DJEMALLOC_STRESS_TESTLIB\n$(C_TESTLIB_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include\n$(TESTS_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST\n$(TESTS_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST\n$(TESTS_INTEGRATION_CPP_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_CPP_TEST\n$(TESTS_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST\n$(TESTS_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.c\n$(TESTS_CPP_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.cpp\n$(TESTS_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include\n$(TESTS_CPP_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include\nifneq ($(IMPORTLIB),$(SO))\n$(CPP_OBJS) $(C_SYM_OBJS) $(C_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS): CPPFLAGS += -DDLLEXPORT\nendif\n\n# Dependencies.\nifndef CC_MM\nHEADER_DIRS = $(srcroot)include/jemalloc/internal \\\n\t$(objroot)include/jemalloc $(objroot)include/jemalloc/internal\nHEADERS = $(filter-out $(PRIVATE_NAMESPACE_HDRS),$(wildcard $(foreach dir,$(HEADER_DIRS),$(dir)/*.h)))\n$(C_SYM_OBJS) $(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS) $(TESTS_CPP_OBJS): $(HEADERS)\n$(TESTS_OBJS) $(TESTS_CPP_OBJS): $(objroot)test/include/test/jemalloc_test.h\nendif\n\n$(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_INTEGRATION_CPP_OBJS): $(objroot)include/jemalloc/internal/private_namespace.h\n$(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_STRESS_OBJS) $(TESTS_UNIT_OBJS) $(TESTS_STRESS_OBJS): $(objroot)include/jemalloc/internal/private_namespace_jet.h\n\n$(C_SYM_OBJS) $(C_OBJS) $(C_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS): %.$(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\n$(C_SYMS): %.sym:\n\t@mkdir -p $(@D)\n\t$(DUMP_SYMS) $< | $(AWK) -f $(objroot)include/jemalloc/internal/private_symbols.awk > $@\n\n$(C_JET_SYMS): %.sym:\n\t@mkdir -p $(@D)\n\t$(DUMP_SYMS) $< | $(AWK) -f $(objroot)include/jemalloc/internal/private_symbols_jet.awk > $@\n\n$(objroot)include/jemalloc/internal/private_namespace.gen.h: $(C_SYMS)\n\t$(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@\n\n$(objroot)include/jemalloc/internal/private_namespace_jet.gen.h: $(C_JET_SYMS)\n\t$(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@\n\n%.h: %.gen.h\n\t@if ! `cmp -s $< $@` ; then echo \"cp $< $<\"; cp $< $@ ; fi\n\n$(CPP_OBJS) $(CPP_PIC_OBJS) $(TESTS_CPP_OBJS): %.$(O):\n\t@mkdir -p $(@D)\n\t$(CXX) $(CXXFLAGS) -c $(CPPFLAGS) $(CTARGET) $<\nifdef CC_MM\n\t@$(CXX) -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),$(C_PIC_OBJS),$(C_OBJS)) $(if $(PIC_CFLAGS),$(CPP_PIC_OBJS),$(CPP_OBJS))\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) : $(C_PIC_OBJS) $(CPP_PIC_OBJS)\n$(objroot)lib/$(LIBJEMALLOC).$(A) : $(C_OBJS) $(CPP_OBJS)\n$(objroot)lib/$(LIBJEMALLOC)_s.$(A) : $(C_OBJS) $(CPP_OBJS)\n\n$(STATIC_LIBS):\n\t@mkdir -p $(@D)\n\t$(AR) $(ARFLAGS)@AROUT@ $+\n\n$(objroot)test/unit/%$(EXE): $(objroot)test/unit/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS)\n\t@mkdir -p $(@D)\n\t$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)\n\n$(objroot)test/integration/%$(EXE): $(objroot)test/integration/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\n\t@mkdir -p $(@D)\n\t$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LJEMALLOC) $(LDFLAGS) $(filter-out -lm,$(filter -lrt -lpthread -lstdc++,$(LIBS))) $(LM) $(EXTRA_LDFLAGS)\n\n$(objroot)test/integration/cpp/%$(EXE): $(objroot)test/integration/cpp/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\n\t@mkdir -p $(@D)\n\t$(CXX) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(EXTRA_LDFLAGS)\n\n$(objroot)test/stress/%$(EXE): $(objroot)test/stress/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\n\t@mkdir -p $(@D)\n\t$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)\n\nbuild_lib_shared: $(DSOS)\nbuild_lib_static: $(STATIC_LIBS)\nbuild_lib: build_lib_shared build_lib_static\n\ninstall_bin:\n\t$(INSTALL) -d $(BINDIR)\n\t@for b in $(BINS); do \\\n\techo \"$(INSTALL) -m 755 $$b $(BINDIR)\"; \\\n\t$(INSTALL) -m 755 $$b $(BINDIR); \\\ndone\n\ninstall_include:\n\t$(INSTALL) -d $(INCLUDEDIR)/jemalloc\n\t@for h in $(C_HDRS); do \\\n\techo \"$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc\"; \\\n\t$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc; \\\ndone\n\ninstall_lib_shared: $(DSOS)\n\t$(INSTALL) -d $(LIBDIR)\n\t$(INSTALL) -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\t$(INSTALL) -d $(LIBDIR)\n\t@for l in $(STATIC_LIBS); do \\\n\techo \"$(INSTALL) -m 755 $$l $(LIBDIR)\"; \\\n\t$(INSTALL) -m 755 $$l $(LIBDIR); \\\ndone\n\ninstall_lib_pc: $(PC)\n\t$(INSTALL) -d $(LIBDIR)/pkgconfig\n\t@for l in $(PC); do \\\n\techo \"$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig\"; \\\n\t$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig; \\\ndone\n\ninstall_lib: install_lib_shared install_lib_static install_lib_pc\n\ninstall_doc_html:\n\t$(INSTALL) -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\t$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \\\ndone\n\ninstall_doc_man:\n\t$(INSTALL) -d $(MANDIR)/man3\n\t@for d in $(DOCS_MAN3); do \\\n\techo \"$(INSTALL) -m 644 $$d $(MANDIR)/man3\"; \\\n\t$(INSTALL) -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_unit: $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%$(EXE))\ntests_integration: $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%$(EXE)) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%$(EXE))\ntests_stress: $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%$(EXE))\ntests: tests_unit tests_integration tests_stress\n\ncheck_unit_dir:\n\t@mkdir -p $(objroot)test/unit\ncheck_integration_dir:\n\t@mkdir -p $(objroot)test/integration\nstress_dir:\n\t@mkdir -p $(objroot)test/stress\ncheck_dir: check_unit_dir check_integration_dir\n\ncheck_unit: tests_unit check_unit_dir\n\t$(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)\ncheck_integration_prof: tests_integration check_integration_dir\nifeq ($(enable_prof), 1)\n\t$(MALLOC_CONF)=\"prof:true\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\n\t$(MALLOC_CONF)=\"prof:true,prof_active:false\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\nendif\ncheck_integration_decay: tests_integration check_integration_dir\n\t$(MALLOC_CONF)=\"dirty_decay_ms:-1,muzzy_decay_ms:-1\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\n\t$(MALLOC_CONF)=\"dirty_decay_ms:0,muzzy_decay_ms:0\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\ncheck_integration: tests_integration check_integration_dir\n\t$(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\nstress: tests_stress stress_dir\n\t$(SHELL) $(objroot)test/test.sh $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%)\ncheck: check_unit check_integration check_integration_decay check_integration_prof\n\nclean:\n\trm -f $(PRIVATE_NAMESPACE_HDRS)\n\trm -f $(PRIVATE_NAMESPACE_GEN_HDRS)\n\trm -f $(C_SYM_OBJS)\n\trm -f $(C_SYMS)\n\trm -f $(C_OBJS)\n\trm -f $(CPP_OBJS)\n\trm -f $(C_PIC_OBJS)\n\trm -f $(CPP_PIC_OBJS)\n\trm -f $(C_JET_SYM_OBJS)\n\trm -f $(C_JET_SYMS)\n\trm -f $(C_JET_OBJS)\n\trm -f $(C_TESTLIB_OBJS)\n\trm -f $(C_SYM_OBJS:%.$(O)=%.d)\n\trm -f $(C_OBJS:%.$(O)=%.d)\n\trm -f $(CPP_OBJS:%.$(O)=%.d)\n\trm -f $(C_PIC_OBJS:%.$(O)=%.d)\n\trm -f $(CPP_PIC_OBJS:%.$(O)=%.d)\n\trm -f $(C_JET_SYM_OBJS:%.$(O)=%.d)\n\trm -f $(C_JET_OBJS:%.$(O)=%.d)\n\trm -f $(C_TESTLIB_OBJS:%.$(O)=%.d)\n\trm -f $(TESTS_OBJS:%.$(O)=%$(EXE))\n\trm -f $(TESTS_OBJS)\n\trm -f $(TESTS_OBJS:%.$(O)=%.d)\n\trm -f $(TESTS_OBJS:%.$(O)=%.out)\n\trm -f $(TESTS_CPP_OBJS:%.$(O)=%$(EXE))\n\trm -f $(TESTS_CPP_OBJS)\n\trm -f $(TESTS_CPP_OBJS:%.$(O)=%.d)\n\trm -f $(TESTS_CPP_OBJS:%.$(O)=%.out)\n\trm -f $(DSOS) $(STATIC_LIBS)\n\ndistclean: clean\n\trm -f $(objroot)bin/jemalloc-config\n\trm -f $(objroot)bin/jemalloc.sh\n\trm -f $(objroot)bin/jeprof\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 $(objroot)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": "Chapter02/redis-6.0.3/deps/jemalloc/README",
    "content": "jemalloc is a general purpose malloc(3) implementation that emphasizes\nfragmentation avoidance and scalable concurrency support.  jemalloc first came\ninto use as the FreeBSD libc allocator in 2005, and since then it has found its\nway into numerous applications that rely on its predictable behavior.  In 2010\njemalloc development efforts broadened to include developer support features\nsuch as heap profiling and extensive monitoring/tuning hooks.  Modern jemalloc\nreleases continue to be integrated back into FreeBSD, and therefore versatility\nremains critical.  Ongoing development efforts trend toward making jemalloc\namong the best allocators for a broad range of demanding applications, and\neliminating/mitigating weaknesses that have practical repercussions for real\nworld applications.\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://jemalloc.net/\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/TUNING.md",
    "content": "This document summarizes the common approaches for performance fine tuning with\njemalloc (as of 5.1.0).  The default configuration of jemalloc tends to work\nreasonably well in practice, and most applications should not have to tune any\noptions. However, in order to cover a wide range of applications and avoid\npathological cases, the default setting is sometimes kept conservative and\nsuboptimal, even for many common workloads.  When jemalloc is properly tuned for\na specific application / workload, it is common to improve system level metrics\nby a few percent, or make favorable trade-offs.\n\n\n## Notable runtime options for performance tuning\n\nRuntime options can be set via\n[malloc_conf](http://jemalloc.net/jemalloc.3.html#tuning).\n\n* [background_thread](http://jemalloc.net/jemalloc.3.html#background_thread)\n\n    Enabling jemalloc background threads generally improves the tail latency for\n    application threads, since unused memory purging is shifted to the dedicated\n    background threads.  In addition, unintended purging delay caused by\n    application inactivity is avoided with background threads.\n\n    Suggested: `background_thread:true` when jemalloc managed threads can be\n    allowed.\n\n* [metadata_thp](http://jemalloc.net/jemalloc.3.html#opt.metadata_thp)\n\n    Allowing jemalloc to utilize transparent huge pages for its internal\n    metadata usually reduces TLB misses significantly, especially for programs\n    with large memory footprint and frequent allocation / deallocation\n    activities.  Metadata memory usage may increase due to the use of huge\n    pages.\n\n    Suggested for allocation intensive programs: `metadata_thp:auto` or\n    `metadata_thp:always`, which is expected to improve CPU utilization at a\n    small memory cost.\n\n* [dirty_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.dirty_decay_ms) and\n  [muzzy_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.muzzy_decay_ms)\n\n    Decay time determines how fast jemalloc returns unused pages back to the\n    operating system, and therefore provides a fairly straightforward trade-off\n    between CPU and memory usage.  Shorter decay time purges unused pages faster\n    to reduces memory usage (usually at the cost of more CPU cycles spent on\n    purging), and vice versa.\n\n    Suggested: tune the values based on the desired trade-offs.\n\n* [narenas](http://jemalloc.net/jemalloc.3.html#opt.narenas)\n\n    By default jemalloc uses multiple arenas to reduce internal lock contention.\n    However high arena count may also increase overall memory fragmentation,\n    since arenas manage memory independently.  When high degree of parallelism\n    is not expected at the allocator level, lower number of arenas often\n    improves memory usage.\n\n    Suggested: if low parallelism is expected, try lower arena count while\n    monitoring CPU and memory usage.\n\n* [percpu_arena](http://jemalloc.net/jemalloc.3.html#opt.percpu_arena)\n\n    Enable dynamic thread to arena association based on running CPU.  This has\n    the potential to improve locality, e.g. when thread to CPU affinity is\n    present.\n    \n    Suggested: try `percpu_arena:percpu` or `percpu_arena:phycpu` if\n    thread migration between processors is expected to be infrequent.\n\nExamples:\n\n* High resource consumption application, prioritizing CPU utilization:\n\n    `background_thread:true,metadata_thp:auto` combined with relaxed decay time\n    (increased `dirty_decay_ms` and / or `muzzy_decay_ms`,\n    e.g. `dirty_decay_ms:30000,muzzy_decay_ms:30000`).\n\n* High resource consumption application, prioritizing memory usage:\n\n    `background_thread:true` combined with shorter decay time (decreased\n    `dirty_decay_ms` and / or `muzzy_decay_ms`,\n    e.g. `dirty_decay_ms:5000,muzzy_decay_ms:5000`), and lower arena count\n    (e.g. number of CPUs).\n\n* Low resource consumption application:\n\n    `narenas:1,lg_tcache_max:13` combined with shorter decay time (decreased\n    `dirty_decay_ms` and / or `muzzy_decay_ms`,e.g.\n    `dirty_decay_ms:1000,muzzy_decay_ms:0`).\n\n* Extremely conservative -- minimize memory usage at all costs, only suitable when\nallocation activity is very rare:\n\n    `narenas:1,tcache:false,dirty_decay_ms:0,muzzy_decay_ms:0`\n\nNote that it is recommended to combine the options with `abort_conf:true` which\naborts immediately on illegal options.\n\n## Beyond runtime options\n\nIn addition to the runtime options, there are a number of programmatic ways to\nimprove application performance with jemalloc.\n\n* [Explicit arenas](http://jemalloc.net/jemalloc.3.html#arenas.create)\n\n    Manually created arenas can help performance in various ways, e.g. by\n    managing locality and contention for specific usages.  For example,\n    applications can explicitly allocate frequently accessed objects from a\n    dedicated arena with\n    [mallocx()](http://jemalloc.net/jemalloc.3.html#MALLOCX_ARENA) to improve\n    locality.  In addition, explicit arenas often benefit from individually\n    tuned options, e.g. relaxed [decay\n    time](http://jemalloc.net/jemalloc.3.html#arena.i.dirty_decay_ms) if\n    frequent reuse is expected.\n\n* [Extent hooks](http://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks)\n\n    Extent hooks allow customization for managing underlying memory.  One use\n    case for performance purpose is to utilize huge pages -- for example,\n    [HHVM](https://github.com/facebook/hhvm/blob/master/hphp/util/alloc.cpp)\n    uses explicit arenas with customized extent hooks to manage 1GB huge pages\n    for frequently accessed data, which reduces TLB misses significantly.\n\n* [Explicit thread-to-arena\n  binding](http://jemalloc.net/jemalloc.3.html#thread.arena)\n\n    It is common for some threads in an application to have different memory\n    access / allocation patterns.  Threads with heavy workloads often benefit\n    from explicit binding, e.g. binding very active threads to dedicated arenas\n    may reduce contention at the allocator level.\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/jemalloc/bin/jemalloc-config.in",
    "content": "#!/bin/sh\n\nusage() {\n\tcat <<EOF\nUsage:\n  @BINDIR@/jemalloc-config <option>\nOptions:\n  --help | -h  : Print usage.\n  --version    : Print jemalloc version.\n  --revision   : Print shared library revision number.\n  --config     : Print configure options used to build jemalloc.\n  --prefix     : Print installation directory prefix.\n  --bindir     : Print binary installation directory.\n  --datadir    : Print data installation directory.\n  --includedir : Print include installation directory.\n  --libdir     : Print library installation directory.\n  --mandir     : Print manual page installation directory.\n  --cc         : Print compiler used to build jemalloc.\n  --cflags     : Print compiler flags used to build jemalloc.\n  --cppflags   : Print preprocessor flags used to build jemalloc.\n  --cxxflags   : Print C++ compiler flags used to build jemalloc.\n  --ldflags    : Print library flags used to build jemalloc.\n  --libs       : Print libraries jemalloc was linked against.\nEOF\n}\n\nprefix=\"@prefix@\"\nexec_prefix=\"@exec_prefix@\"\n\ncase \"$1\" in\n--help | -h)\n\tusage\n\texit 0\n\t;;\n--version)\n\techo \"@jemalloc_version@\"\n\t;;\n--revision)\n\techo \"@rev@\"\n\t;;\n--config)\n\techo \"@CONFIG@\"\n\t;;\n--prefix)\n\techo \"@PREFIX@\"\n\t;;\n--bindir)\n\techo \"@BINDIR@\"\n\t;;\n--datadir)\n\techo \"@DATADIR@\"\n\t;;\n--includedir)\n\techo \"@INCLUDEDIR@\"\n\t;;\n--libdir)\n\techo \"@LIBDIR@\"\n\t;;\n--mandir)\n\techo \"@MANDIR@\"\n\t;;\n--cc)\n\techo \"@CC@\"\n\t;;\n--cflags)\n\techo \"@CFLAGS@\"\n\t;;\n--cppflags)\n\techo \"@CPPFLAGS@\"\n\t;;\n--cxxflags)\n\techo \"@CXXFLAGS@\"\n\t;;\n--ldflags)\n\techo \"@LDFLAGS@ @EXTRA_LDFLAGS@\"\n\t;;\n--libs)\n\techo \"@LIBS@\"\n\t;;\n*)\n\tusage\n\texit 1\nesac\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/jemalloc/bin/jeprof.in",
    "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/jeprof \"program\" \"profile\"\n#   Enters \"interactive\" mode\n#\n# % tools/jeprof --text \"program\" \"profile\"\n#   Generates one line per procedure\n#\n# % tools/jeprof --gv \"program\" \"profile\"\n#   Generates annotated call-graph and displays via \"gv\"\n#\n# % tools/jeprof --gv --focus=Mutex \"program\" \"profile\"\n#   Restrict to code paths that involve an entry that matches \"Mutex\"\n#\n# % tools/jeprof --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/jeprof --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/jeprof --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;\nuse Cwd;\n\nmy $JEPROF_VERSION = \"@jemalloc_version@\";\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 JEPROF_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\", \"--fail\");\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:\njeprof [options] <program> <profiles>\n   <profiles> is a space separated list of profile names.\njeprof [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).\njeprof [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     jeprof http://myserver.com:80$HEAP_PAGE\n   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).\njeprof --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 jeprof 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 backtraces with nodes matching <regexp>\n   --thread=<n>        Show profile for thread <n>\n   --ignore=<regexp>   Ignore backtraces with 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   --retain=<regexp>   Retain only nodes that match <regexp>\n   --exclude=<regexp>  Exclude all nodes that match <regexp>\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   JEPROF_TMPDIR        Profiles directory. Defaults to \\$HOME/jeprof\n   JEPROF_TOOLS         Prefix for object tools pathnames\n\nExamples:\n\njeprof /bin/ls ls.prof\n                       Enters \"interactive\" mode\njeprof --text /bin/ls ls.prof\n                       Outputs one line per procedure\njeprof --web /bin/ls ls.prof\n                       Displays annotated call-graph in web browser\njeprof --gv /bin/ls ls.prof\n                       Displays annotated call-graph via 'gv'\njeprof --gv --focus=Mutex /bin/ls ls.prof\n                       Restricts to code paths including a .*Mutex.* entry\njeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof\n                       Code paths including Mutex but not string\njeprof --list=getdir /bin/ls ls.prof\n                       (Per-line) annotated source listing for getdir()\njeprof --disasm=getdir /bin/ls ls.prof\n                       (Per-PC) annotated disassembly for getdir()\n\njeprof http://localhost:1234/\n                       Enters \"interactive\" mode\njeprof --text localhost:1234\n                       Outputs one line per procedure for localhost:1234\njeprof --raw localhost:1234 > ./local.raw\njeprof --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\njeprof (part of jemalloc $JEPROF_VERSION)\nbased on pprof (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/jeprof$$.sym\";\n  $main::tmpfile_ps = \"/tmp/jeprof$$\";\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_thread = undef;\n  $main::opt_ignore = '';\n  $main::opt_scale = 0;\n  $main::opt_heapcheck = 0;\n  $main::opt_retain = '';\n  $main::opt_exclude = '';\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             \"thread=s\"       => \\$main::opt_thread,\n             \"ignore=s\"       => \\$main::opt_ignore,\n             \"scale=i\"        => \\$main::opt_scale,\n             \"heapcheck\"      => \\$main::opt_heapcheck,\n             \"retain=s\"       => \\$main::opt_retain,\n             \"exclude=s\"      => \\$main::opt_exclude,\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 FilterAndPrint {\n  my ($profile, $symbols, $libs, $thread) = @_;\n\n  # Get total data in profile\n  my $total = TotalProfile($profile);\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 %s\\n\",\n               (defined($thread) ? \" (t$thread)\" : \"\"),\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\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  # 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  if (!defined($main::opt_thread)) {\n    FilterAndPrint($profile, $symbols, $libs);\n  }\n  if (defined($data->{threads})) {\n    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {\n      if (defined($main::opt_thread) &&\n          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {\n        my $thread_profile = $data->{threads}{$thread};\n        FilterAndPrint($thread_profile, $symbols, $libs, $thread);\n      }\n    }\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 jeprof!  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 'jeprof';\n    while ( defined ($_ = $term->readline('(jeprof) '))) {\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 \"(jeprof) \";\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 jeprof 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 jeprof\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  my $profile_marker;\n  if ($main::profile_type eq 'heap') {\n    $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } elsif ($main::profile_type eq 'growth') {\n    $GROWTH_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } elsif ($main::profile_type eq 'contention') {\n    $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } else { # elsif ($main::profile_type eq 'cpu')\n    $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  }\n\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    # --raw/http: For everything to work correctly for non-remote profiles, we\n    # would need to extend PrintProfileData() to handle all possible profile\n    # types, re-enable the code that is currently disabled in ReadCPUProfile()\n    # and FixCallerAddresses(), and remove the remote profile dumping code in\n    # the block above.\n    die \"--raw/http: jeprof can only dump remote profiles for --raw\\n\";\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 jeprof_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=\\\"jeprof_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 FilterFrames {\n  my $symbols = shift;\n  my $profile = shift;\n\n  if ($main::opt_retain eq '' && $main::opt_exclude eq '') {\n    return $profile;\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      my $sym;\n      if (exists($symbols->{$a})) {\n        $sym = $symbols->{$a}->[0];\n      } else {\n        $sym = $a;\n      }\n      if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {\n        next;\n      }\n      if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {\n        next;\n      }\n      push(@path, $a);\n    }\n    if (scalar(@path) > 0) {\n      my $reduced_path = join(\"\\n\", @path);\n      AddEntry($result, $reduced_path, $count);\n    }\n  }\n\n  return $result;\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 ('@JEMALLOC_PREFIX@calloc',\n                      'cfree',\n                      '@JEMALLOC_PREFIX@malloc',\n                      'newImpl',\n                      'void* newImpl',\n                      '@JEMALLOC_PREFIX@free',\n                      '@JEMALLOC_PREFIX@memalign',\n                      '@JEMALLOC_PREFIX@posix_memalign',\n                      '@JEMALLOC_PREFIX@aligned_alloc',\n                      'pvalloc',\n                      '@JEMALLOC_PREFIX@valloc',\n                      '@JEMALLOC_PREFIX@realloc',\n                      '@JEMALLOC_PREFIX@mallocx',\n                      '@JEMALLOC_PREFIX@rallocx',\n                      '@JEMALLOC_PREFIX@xallocx',\n                      '@JEMALLOC_PREFIX@dallocx',\n                      '@JEMALLOC_PREFIX@sdallocx',\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          # Throw away the portion of the backtrace seen so far, under the\n          # assumption that previous frames were for functions internal to the\n          # allocator.\n          @path = ();\n          next;\n        }\n      }\n      push(@path, $a);\n    }\n    my $reduced_path = join(\"\\n\", @path);\n    AddEntry($result, $reduced_path, $count);\n  }\n\n  $result = FilterFrames($symbols, $result);\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 = @_;\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\nsub URLEncode {\n  my $str = shift;\n  $str =~ s/([^A-Za-z0-9\\-_.!~*'()])/ sprintf \"%%%02x\", ord $1 /eg;\n  return $str;\n}\n\nsub AppendSymbolFilterParams {\n  my $url = shift;\n  my @params = ();\n  if ($main::opt_retain ne '') {\n    push(@params, sprintf(\"retain=%s\", URLEncode($main::opt_retain)));\n  }\n  if ($main::opt_exclude ne '') {\n    push(@params, sprintf(\"exclude=%s\", URLEncode($main::opt_exclude)));\n  }\n  if (scalar @params > 0) {\n    $url = sprintf(\"%s?%s\", $url, join(\"&\", @params));\n  }\n  return $url;\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      $url = AppendSymbolFilterParams($url);\n      $command_line = ShellEscape(@URL_FETCHER, \"-d\", \"\\@$main::tmpfile_sym\",\n                                  $url);\n    } else {\n      $url = AppendSymbolFilterParams($url);\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      # Set $profile_type for consumption by PrintSymbolizedProfile.\n      $main::profile_type = 'cpu';\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      # Set $profile_type for consumption by PrintSymbolizedProfile.\n      if ($path =~ m/$HEAP_PAGE/) {\n        $main::profile_type = 'heap';\n      } elsif ($path =~ m/$GROWTH_PAGE/) {\n        $main::profile_type = 'growth';\n      } elsif ($path =~ m/$CONTENTION_PAGE/) {\n        $main::profile_type = 'contention';\n      }\n    }\n\n    my $profile_dir = $ENV{\"JEPROF_TMPDIR\"} || ($ENV{HOME} . \"/jeprof\");\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 jeprof, which jeprof 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->{threads}     Map of thread IDs to profile objects\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  $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $heap_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  if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {\n    # Skip \"--- ...\" line for profile types that have their own headers.\n    $header = ReadProfileHeader(*PROFILE) || \"\";\n  }\n\n  $main::profile_type = '';\n\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/^heap/) {\n    $main::profile_type = 'heap';\n    $result = ReadThreadedHeapProfile($prog, $fname, $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  # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()\n  # dumps unadjusted profiles.\n  {\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      $pc--;\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 HeapProfileIndex {\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  return $index;\n}\n\nsub ReadMappedLibraries {\n  my $fh = shift;\n  my $map = \"\";\n  # Read the /proc/self/maps data\n  while (<$fh>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    $map .= $_;\n  }\n  return $map;\n}\n\nsub ReadMemoryMap {\n  my $fh = shift;\n  my $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  return $map;\n}\n\nsub AdjustSamples {\n  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;\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  return ($n1, $s1, $n2, $s2);\n}\n\nsub ReadHeapProfile {\n  my $prog = shift;\n  local *PROFILE = shift;\n  my $header = shift;\n\n  my $index = HeapProfileIndex();\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      $map .= ReadMappedLibraries(*PROFILE);\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      $map .= ReadMemoryMap(*PROFILE);\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      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,\n                                 $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 ReadThreadedHeapProfile {\n  my ($prog, $fname, $header) = @_;\n\n  my $index = HeapProfileIndex();\n  my $sampling_algorithm = 0;\n  my $sample_adjustment = 0;\n  chomp($header);\n  my $type = \"unknown\";\n  # Assuming a very specific type of header for now.\n  if ($header =~ m\"^heap_v2/(\\d+)\") {\n    $type = \"_v2\";\n    $sampling_algorithm = 2;\n    $sample_adjustment = int($1);\n  }\n  if ($type ne \"_v2\" || !defined($sample_adjustment)) {\n    die \"Threaded heap profiles require v2 sampling with a sample rate\\n\";\n  }\n\n  my $profile = {};\n  my $thread_profiles = {};\n  my $pcs = {};\n  my $map = \"\";\n  my $stack = \"\";\n\n  while (<PROFILE>) {\n    s/\\r//g;\n    if (/^MAPPED_LIBRARIES:/) {\n      $map .= ReadMappedLibraries(*PROFILE);\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      $map .= ReadMemoryMap(*PROFILE);\n      last;\n    }\n\n    # Read entry of the form:\n    # @ a1 a2 ... an\n    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]\n    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]\n    #     ...\n    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]\n    s/^\\s*//;\n    s/\\s*$//;\n    if (m/^@\\s+(.*)$/) {\n      $stack = $1;\n    } elsif (m/^\\s*(t(\\*|\\d+)):\\s+(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\]$/) {\n      if ($stack eq \"\") {\n        # Still in the header, so this is just a per-thread summary.\n        next;\n      }\n      my $thread = $2;\n      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);\n      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,\n                                 $n1, $s1, $n2, $s2);\n      if ($thread eq \"*\") {\n        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);\n      } else {\n        if (!exists($thread_profiles->{$thread})) {\n          $thread_profiles->{$thread} = {};\n        }\n        AddEntries($thread_profiles->{$thread}, $pcs,\n                   FixCallerAddresses($stack), $counts[$index]);\n      }\n    }\n  }\n\n  my $r = {};\n  $r->{version} = \"heap\";\n  $r->{period} = 1;\n  $r->{profile} = $profile;\n  $r->{threads} = $thread_profiles;\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 jeprof\n        # So we just silently ignore it for now\n      } elsif ($variable eq \"discarded samples\") {\n        # Currently nothing is done with this value in jeprof\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|^/|) {\n      if (-f \"/usr/lib/debug$file\") {\n        return \"/usr/lib/debug$file\";\n      } elsif (-f \"/usr/lib/debug$file.debug\") {\n        return \"/usr/lib/debug$file.debug\";\n      }\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 = Cwd::abs_path(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    } elsif (($l =~ /^($h)-($h)\\s+..x.\\s+($h)\\s+\\S+:\\S+\\s+\\d+\\s+(\\S+)$/i) && ($4 eq $prog)) {\n      # PIEs and address space randomization do not play well with our\n      # default assumption that main executable is at lowest\n      # addresses. So we're detecting main executable in\n      # /proc/self/maps as well.\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    }\n    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in\n    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)\n    #\n    # Example:\n    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s\n    # o.1 NCH -1\n    elsif ($l =~ /^(0x$h)\\s(0x$h)\\s\\d+\\s\\d+\\s0x$h\\sr-x\\s\\d+\\s\\d+\\s0x\\d+\\s(COW|NCO)\\s(NC|NNC)\\svnode\\s(\\S+\\.so(\\.\\d+)*)/) {\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = $zero_offset;\n      $lib = FindLibrary($5);\n\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    if($main::opt_debug) { printf STDERR \"$start:$finish ($offset) $lib\\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 jeprof --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 jeprof --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 jeprof --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    # Use debug library if it exists\n    my $debug_libname = DebuggingLibrary($libname);\n    if ($debug_libname) {\n        $libname = $debug_libname;\n    }\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) JEPROF_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 jeprof.\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# JEPROF_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 $JEPROF_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{\"JEPROF_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 \\$JEPROF_TOOLS) '$tools'\\n\");\n    }\n  } else {\n    # ... otherwise use the version that exists in the same directory as\n    # jeprof.  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/jeprof 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 \"  jeprof \\\\\\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 parameters 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 jeprof 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": "Chapter02/redis-6.0.3/deps/jemalloc/build-aux/config.guess",
    "content": "#! /bin/sh\n# Attempt to guess a canonical system name.\n#   Copyright 1992-2016 Free Software Foundation, Inc.\n\ntimestamp='2016-10-02'\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 3 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\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n#\n# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.\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\n#\n# Please send patches to <config-patches@gnu.org>.\n\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 1992-2016 Free 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\ncase \"${UNAME_SYSTEM}\" in\nLinux|GNU|GNU/*)\n\t# If the system lacks a compiler, then just pick glibc.\n\t# We could probably try harder.\n\tLIBC=gnu\n\n\teval $set_cc_for_build\n\tcat <<-EOF > $dummy.c\n\t#include <features.h>\n\t#if defined(__UCLIBC__)\n\tLIBC=uclibc\n\t#elif defined(__dietlibc__)\n\tLIBC=dietlibc\n\t#else\n\tLIBC=gnu\n\t#endif\n\tEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`\n\t;;\nesac\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=`(uname -p 2>/dev/null || \\\n\t    /sbin/$sysctl 2>/dev/null || \\\n\t    /usr/sbin/$sysctl 2>/dev/null || \\\n\t    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    earmv*)\n\t\tarch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\\(armv[0-9]\\).*$,\\1,'`\n\t\tendian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\\(eb\\)$,\\1,p'`\n\t\tmachine=${arch}${endian}-unknown\n\t\t;;\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) and ABI.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\tos=netbsdelf\n\t\t;;\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# Determine ABI tags.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\texpr='s/^earmv[0-9]/-eabi/;s/eb$//'\n\t\tabi=`echo ${UNAME_MACHINE_ARCH} | sed -e \"$expr\"`\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/[-_].*//' | cut -d. -f1,2`\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}${abi}\"\n\texit ;;\n    *:Bitrig:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_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    *:LibertyBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\\.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${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    *:Sortix:*:*)\n\techo ${UNAME_MACHINE}-unknown-sortix\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/lslpp ] ; then\n\t\tIBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |\n\t\t\t   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`\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    *:MINGW64*:*)\n\techo ${UNAME_MACHINE}-pc-mingw64\n\texit ;;\n    *:MINGW*:*)\n\techo ${UNAME_MACHINE}-pc-mingw32\n\texit ;;\n    *: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-${LIBC}`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 \"[:upper:]\" \"[:lower:]\"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}\n\texit ;;\n    i*86:Minix:*:*)\n\techo ${UNAME_MACHINE}-pc-minix\n\texit ;;\n    aarch64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    aarch64_be:Linux:*:*)\n\tUNAME_MACHINE=aarch64_be\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\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=gnulibc1 ; fi\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    arc:Linux:*:* | arceb:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${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-${LIBC}\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-${LIBC}eabi\n\t    else\n\t\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf\n\t    fi\n\tfi\n\texit ;;\n    avr32*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    cris:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    crisv32:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    e2k:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    frv:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    hexagon:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    i*86:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    ia64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    k1om:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m32r*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m68*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\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-${LIBC}\"; exit; }\n\t;;\n    mips64el:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    openrisc*:Linux:*:*)\n\techo or1k-unknown-linux-${LIBC}\n\texit ;;\n    or32:Linux:*:* | or1k*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    padre:Linux:*:*)\n\techo sparc-unknown-linux-${LIBC}\n\texit ;;\n    parisc64:Linux:*:* | hppa64:Linux:*:*)\n\techo hppa64-unknown-linux-${LIBC}\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-${LIBC} ;;\n\t  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;\n\t  *)    echo hppa-unknown-linux-${LIBC} ;;\n\tesac\n\texit ;;\n    ppc64:Linux:*:*)\n\techo powerpc64-unknown-linux-${LIBC}\n\texit ;;\n    ppc:Linux:*:*)\n\techo powerpc-unknown-linux-${LIBC}\n\texit ;;\n    ppc64le:Linux:*:*)\n\techo powerpc64le-unknown-linux-${LIBC}\n\texit ;;\n    ppcle:Linux:*:*)\n\techo powerpcle-unknown-linux-${LIBC}\n\texit ;;\n    riscv32:Linux:*:* | riscv64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    s390:Linux:*:* | s390x:Linux:*:*)\n\techo ${UNAME_MACHINE}-ibm-linux-${LIBC}\n\texit ;;\n    sh64*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sh*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sparc:Linux:*:* | sparc64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    tile*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    vax:Linux:*:*)\n\techo ${UNAME_MACHINE}-dec-linux-${LIBC}\n\texit ;;\n    x86_64:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    xtensa*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\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 configure 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    x86_64:Haiku:*:*)\n\techo x86_64-unknown-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    SX-ACE:SUPER-UX:*:*)\n\techo sxace-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\teval $set_cc_for_build\n\tif test \"$UNAME_PROCESSOR\" = unknown ; then\n\t    UNAME_PROCESSOR=powerpc\n\tfi\n\tif test `echo \"$UNAME_RELEASE\" | sed -e 's/\\..*//'` -le 10 ; then\n\t    if [ \"$CC_FOR_BUILD\" != no_compiler_found ]; then\n\t\tif (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\tthen\n\t\t    case $UNAME_PROCESSOR in\n\t\t\ti386) UNAME_PROCESSOR=x86_64 ;;\n\t\t\tpowerpc) UNAME_PROCESSOR=powerpc64 ;;\n\t\t    esac\n\t\tfi\n\t    fi\n\telif test \"$UNAME_PROCESSOR\" = i386 ; then\n\t    # Avoid executing cc on OS X 10.9, as it ships with a stub\n\t    # that puts up a graphical alert prompting to install\n\t    # developer tools.  Any system running Mac OS X 10.7 or\n\t    # later (Darwin 11 and later) is required to have a 64-bit\n\t    # processor. This is not true of the ARM version of Darwin\n\t    # that Apple uses in portable devices.\n\t    UNAME_PROCESSOR=x86_64\n\tfi\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 ;;\n    amd64:Isilon\\ OneFS:*:*)\n\techo x86_64-unknown-onefs\n\texit ;;\nesac\n\ncat >&2 <<EOF\n$0: unable to guess system type\n\nThis script (version $timestamp), has failed to recognize the\noperating system you are using. If your script is old, overwrite\nconfig.guess and config.sub with the latest versions from:\n\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess\nand\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub\n\nIf $0 has already been updated, send the following data and any\ninformation you think might be pertinent to config-patches@gnu.org to\nprovide the necessary information 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": "Chapter02/redis-6.0.3/deps/jemalloc/build-aux/config.sub",
    "content": "#! /bin/sh\n# Configuration validation subroutine script.\n#   Copyright 1992-2016 Free Software Foundation, Inc.\n\ntimestamp='2016-11-04'\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 3 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\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n\n\n# Please send patches to <config-patches@gnu.org>.\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\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 or 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 1992-2016 Free 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-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \\\n  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \\\n  kopensolaris*-gnu* | cloudabi*-eabi* | \\\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*178)\n\t\tos=-lynxos178\n\t\t;;\n\t-lynx*5)\n\t\tos=-lynxos5\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 | arceb \\\n\t| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \\\n\t| avr | avr32 \\\n\t| ba \\\n\t| be32 | be64 \\\n\t| bfin \\\n\t| c4x | c8051 | clipper \\\n\t| d10v | d30v | dlx | dsp16xx \\\n\t| e2k | epiphany \\\n\t| fido | fr30 | frv | ft32 \\\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| k1om \\\n\t| le32 | le64 \\\n\t| lm32 \\\n\t| m32c | m32r | m32rle | m68000 | m68k | m88k \\\n\t| maxq | mb | microblaze | microblazeel | 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| mipsisa32r6 | mipsisa32r6el \\\n\t| mipsisa64 | mipsisa64el \\\n\t| mipsisa64r2 | mipsisa64r2el \\\n\t| mipsisa64r6 | mipsisa64r6el \\\n\t| mipsisa64sb1 | mipsisa64sb1el \\\n\t| mipsisa64sr71k | mipsisa64sr71kel \\\n\t| mipsr5900 | mipsr5900el \\\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 | nios2eb | nios2el \\\n\t| ns16k | ns32k \\\n\t| open8 | or1k | or1knd | or32 \\\n\t| pdp10 | pdp11 | pj | pjl \\\n\t| powerpc | powerpc64 | powerpc64le | powerpcle \\\n\t| pru \\\n\t| pyramid \\\n\t| riscv32 | riscv64 \\\n\t| rl78 | rx \\\n\t| score \\\n\t| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]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| visium \\\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\tleon|leon[3-9])\n\t\tbasic_machine=sparc-$basic_machine\n\t\t;;\n\tm6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | 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-* | arceb-* \\\n\t| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \\\n\t| avr-* | avr32-* \\\n\t| ba-* \\\n\t| be32-* | be64-* \\\n\t| bfin-* | bs2000-* \\\n\t| c[123]* | c30-* | [cjt]90-* | c4x-* \\\n\t| c8051-* | clipper-* | craynv-* | cydra-* \\\n\t| d10v-* | d30v-* | dlx-* \\\n\t| e2k-* | 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| k1om-* \\\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-* \\\n\t| microblaze-* | microblazeel-* \\\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| mipsisa32r6-* | mipsisa32r6el-* \\\n\t| mipsisa64-* | mipsisa64el-* \\\n\t| mipsisa64r2-* | mipsisa64r2el-* \\\n\t| mipsisa64r6-* | mipsisa64r6el-* \\\n\t| mipsisa64sb1-* | mipsisa64sb1el-* \\\n\t| mipsisa64sr71k-* | mipsisa64sr71kel-* \\\n\t| mipsr5900-* | mipsr5900el-* \\\n\t| mipstx39-* | mipstx39el-* \\\n\t| mmix-* \\\n\t| mt-* \\\n\t| msp430-* \\\n\t| nds32-* | nds32le-* | nds32be-* \\\n\t| nios-* | nios2-* | nios2eb-* | nios2el-* \\\n\t| none-* | np1-* | ns16k-* | ns32k-* \\\n\t| open8-* \\\n\t| or1k*-* \\\n\t| orion-* \\\n\t| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \\\n\t| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \\\n\t| pru-* \\\n\t| pyramid-* \\\n\t| riscv32-* | riscv64-* \\\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| visium-* \\\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\tasmjs)\n\t\tbasic_machine=asmjs-unknown\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\te500v[12])\n\t\tbasic_machine=powerpc-unknown\n\t\tos=$os\"spe\"\n\t\t;;\n\te500v[12]-*)\n\t\tbasic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=$os\"spe\"\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\tleon-*|leon[3-9]-*)\n\t\tbasic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`\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\tmingw64)\n\t\tbasic_machine=x86_64-pc\n\t\tos=-mingw64\n\t\t;;\n\tmingw32)\n\t\tbasic_machine=i686-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\tmoxiebox)\n\t\tbasic_machine=moxie-unknown\n\t\tos=-moxiebox\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=i686-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)\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)\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 | rdos64)\n\t\tbasic_machine=x86_64-pc\n\t\tos=-rdos\n\t\t;;\n\trdos32)\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* | -plan9* \\\n\t      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \\\n\t      | -aos* | -aros* | -cloudabi* | -sortix* \\\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      | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \\\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      | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \\\n\t      | -linux-newlib* | -linux-musl* | -linux-uclibc* \\\n\t      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \\\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      | -onefs* | -tirtos* | -phoenix* | -fuchsia*)\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-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-ios)\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\tc8051-*)\n\t\tos=-elf\n\t\t;;\n\thexagon-*)\n\t\tos=-elf\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": "Chapter02/redis-6.0.3/deps/jemalloc/build-aux/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": "Chapter02/redis-6.0.3/deps/jemalloc/config.stamp.in",
    "content": ""
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/configure.ac",
    "content": "dnl Process this file with autoconf to produce a configure script.\nAC_PREREQ(2.68)\nAC_INIT([Makefile.in])\n\nAC_CONFIG_AUX_DIR([build-aux])\n\ndnl ============================================================================\ndnl Custom macro definitions.\n\ndnl JE_CONCAT_VVV(r, a, b)\ndnl\ndnl Set $r to the concatenation of $a and $b, with a space separating them iff\ndnl both $a and $b are non-empty.\nAC_DEFUN([JE_CONCAT_VVV],\nif test \"x[$]{$2}\" = \"x\" -o \"x[$]{$3}\" = \"x\" ; then\n  $1=\"[$]{$2}[$]{$3}\"\nelse\n  $1=\"[$]{$2} [$]{$3}\"\nfi\n)\n\ndnl JE_APPEND_VS(a, b)\ndnl\ndnl Set $a to the concatenation of $a and b, with a space separating them iff\ndnl both $a and b are non-empty.\nAC_DEFUN([JE_APPEND_VS],\n  T_APPEND_V=$2\n  JE_CONCAT_VVV($1, $1, T_APPEND_V)\n)\n\nCONFIGURE_CFLAGS=\nSPECIFIED_CFLAGS=\"${CFLAGS}\"\ndnl JE_CFLAGS_ADD(cflag)\ndnl\ndnl CFLAGS is the concatenation of CONFIGURE_CFLAGS and SPECIFIED_CFLAGS\ndnl (ignoring EXTRA_CFLAGS, which does not impact configure tests.  This macro\ndnl appends to CONFIGURE_CFLAGS and regenerates CFLAGS.\nAC_DEFUN([JE_CFLAGS_ADD],\n[\nAC_MSG_CHECKING([whether compiler supports $1])\nT_CONFIGURE_CFLAGS=\"${CONFIGURE_CFLAGS}\"\nJE_APPEND_VS(CONFIGURE_CFLAGS, $1)\nJE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[\n]], [[\n    return 0;\n]])],\n              [je_cv_cflags_added=$1]\n              AC_MSG_RESULT([yes]),\n              [je_cv_cflags_added=]\n              AC_MSG_RESULT([no])\n              [CONFIGURE_CFLAGS=\"${T_CONFIGURE_CFLAGS}\"]\n)\nJE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)\n])\n\ndnl JE_CFLAGS_SAVE()\ndnl JE_CFLAGS_RESTORE()\ndnl\ndnl Save/restore CFLAGS.  Nesting is not supported.\nAC_DEFUN([JE_CFLAGS_SAVE],\nSAVED_CONFIGURE_CFLAGS=\"${CONFIGURE_CFLAGS}\"\n)\nAC_DEFUN([JE_CFLAGS_RESTORE],\nCONFIGURE_CFLAGS=\"${SAVED_CONFIGURE_CFLAGS}\"\nJE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)\n)\n\nCONFIGURE_CXXFLAGS=\nSPECIFIED_CXXFLAGS=\"${CXXFLAGS}\"\ndnl JE_CXXFLAGS_ADD(cxxflag)\nAC_DEFUN([JE_CXXFLAGS_ADD],\n[\nAC_MSG_CHECKING([whether compiler supports $1])\nT_CONFIGURE_CXXFLAGS=\"${CONFIGURE_CXXFLAGS}\"\nJE_APPEND_VS(CONFIGURE_CXXFLAGS, $1)\nJE_CONCAT_VVV(CXXFLAGS, CONFIGURE_CXXFLAGS, SPECIFIED_CXXFLAGS)\nAC_LANG_PUSH([C++])\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[\n]], [[\n    return 0;\n]])],\n              [je_cv_cxxflags_added=$1]\n              AC_MSG_RESULT([yes]),\n              [je_cv_cxxflags_added=]\n              AC_MSG_RESULT([no])\n              [CONFIGURE_CXXFLAGS=\"${T_CONFIGURE_CXXFLAGS}\"]\n)\nAC_LANG_POP([C++])\nJE_CONCAT_VVV(CXXFLAGS, CONFIGURE_CXXFLAGS, SPECIFIED_CXXFLAGS)\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\nCONFIG=`echo ${ac_configure_args} | sed -e 's#'\"'\"'\\([^ ]*\\)'\"'\"'#\\1#g'`\nAC_SUBST([CONFIG])\n\ndnl Library revision.\nrev=2\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], [false], [$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\n\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\ndnl check if a cray prgenv wrapper compiler is being used\nje_cv_cray_prgenv_wrapper=\"\"\nif test \"x${PE_ENV}\" != \"x\" ; then\n  case \"${CC}\" in\n    CC|cc)\n\tje_cv_cray_prgenv_wrapper=\"yes\"\n\t;;\n    *)\n       ;;\n  esac\nfi\n\nAC_CACHE_CHECK([whether compiler is cray],\n              [je_cv_cray],\n              [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],\n                                                  [\n#ifndef _CRAYC\n  int fail[-1];\n#endif\n])],\n                            [je_cv_cray=yes],\n                            [je_cv_cray=no])])\n\nif test \"x${je_cv_cray}\" = \"xyes\" ; then\n  AC_CACHE_CHECK([whether cray compiler version is 8.4],\n                [je_cv_cray_84],\n                [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],\n                                                      [\n#if !(_RELEASE_MAJOR == 8 && _RELEASE_MINOR == 4)\n  int fail[-1];\n#endif\n])],\n                              [je_cv_cray_84=yes],\n                              [je_cv_cray_84=no])])\nfi\n\nif test \"x$GCC\" = \"xyes\" ; then\n  JE_CFLAGS_ADD([-std=gnu11])\n  if test \"x$je_cv_cflags_added\" = \"x-std=gnu11\" ; then\n    AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])\n  else\n    JE_CFLAGS_ADD([-std=gnu99])\n    if test \"x$je_cv_cflags_added\" = \"x-std=gnu99\" ; then\n      AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])\n    fi\n  fi\n  JE_CFLAGS_ADD([-Wall])\n  JE_CFLAGS_ADD([-Wshorten-64-to-32])\n  JE_CFLAGS_ADD([-Wsign-compare])\n  JE_CFLAGS_ADD([-Wundef])\n  JE_CFLAGS_ADD([-Wno-format-zero-length])\n  JE_CFLAGS_ADD([-pipe])\n  JE_CFLAGS_ADD([-g3])\nelif test \"x$je_cv_msvc\" = \"xyes\" ; then\n  CC=\"$CC -nologo\"\n  JE_CFLAGS_ADD([-Zi])\n  JE_CFLAGS_ADD([-MT])\n  JE_CFLAGS_ADD([-W3])\n  JE_CFLAGS_ADD([-FS])\n  JE_APPEND_VS(CPPFLAGS, -I${srcdir}/include/msvc_compat)\nfi\nif test \"x$je_cv_cray\" = \"xyes\" ; then\n  dnl cray compiler 8.4 has an inlining bug\n  if test \"x$je_cv_cray_84\" = \"xyes\" ; then\n    JE_CFLAGS_ADD([-hipa2])\n    JE_CFLAGS_ADD([-hnognu])\n  fi\n  dnl ignore unreachable code warning\n  JE_CFLAGS_ADD([-hnomessage=128])\n  dnl ignore redefinition of \"malloc\", \"free\", etc warning\n  JE_CFLAGS_ADD([-hnomessage=1357])\nfi\nAC_SUBST([CONFIGURE_CFLAGS])\nAC_SUBST([SPECIFIED_CFLAGS])\nAC_SUBST([EXTRA_CFLAGS])\nAC_PROG_CPP\n\nAC_ARG_ENABLE([cxx],\n  [AS_HELP_STRING([--disable-cxx], [Disable C++ integration])],\nif test \"x$enable_cxx\" = \"xno\" ; then\n  enable_cxx=\"0\"\nelse\n  enable_cxx=\"1\"\nfi\n,\nenable_cxx=\"1\"\n)\nif test \"x$enable_cxx\" = \"x1\" ; then\n  dnl Require at least c++14, which is the first version to support sized\n  dnl deallocation.  C++ support is not compiled otherwise.\n  m4_include([m4/ax_cxx_compile_stdcxx.m4])\n  AX_CXX_COMPILE_STDCXX([14], [noext], [optional])\n  if test \"x${HAVE_CXX14}\" = \"x1\" ; then\n    JE_CXXFLAGS_ADD([-Wall])\n    JE_CXXFLAGS_ADD([-g3])\n\n    SAVED_LIBS=\"${LIBS}\"\n    JE_APPEND_VS(LIBS, -lstdc++)\n    JE_COMPILABLE([libstdc++ linkage], [\n#include <stdlib.h>\n], [[\n\tint *arr = (int *)malloc(sizeof(int) * 42);\n\tif (arr == NULL)\n\t\treturn 1;\n]], [je_cv_libstdcxx])\n    if test \"x${je_cv_libstdcxx}\" = \"xno\" ; then\n      LIBS=\"${SAVED_LIBS}\"\n    fi\n  else\n    enable_cxx=\"0\"\n  fi\nfi\nAC_SUBST([enable_cxx])\nAC_SUBST([CONFIGURE_CXXFLAGS])\nAC_SUBST([SPECIFIED_CXXFLAGS])\nAC_SUBST([EXTRA_CXXFLAGS])\n\nAC_C_BIGENDIAN([ac_cv_big_endian=1], [ac_cv_big_endian=0])\nif test \"x${ac_cv_big_endian}\" = \"x1\" ; then\n  AC_DEFINE_UNQUOTED([JEMALLOC_BIG_ENDIAN], [ ])\nfi\n\nif test \"x${je_cv_msvc}\" = \"xyes\" -a \"x${ac_cv_header_inttypes_h}\" = \"xno\"; then\n  JE_APPEND_VS(CPPFLAGS, -I${srcdir}/include/msvc_compat/C99)\nfi\n\nif test \"x${je_cv_msvc}\" = \"xyes\" ; then\n  LG_SIZEOF_PTR=LG_SIZEOF_PTR_WIN\n  AC_MSG_RESULT([Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit])\nelse\n  AC_CHECK_SIZEOF([void *])\n  if test \"x${ac_cv_sizeof_void_p}\" = \"x8\" ; then\n    LG_SIZEOF_PTR=3\n  elif test \"x${ac_cv_sizeof_void_p}\" = \"x4\" ; then\n    LG_SIZEOF_PTR=2\n  else\n    AC_MSG_ERROR([Unsupported pointer size: ${ac_cv_sizeof_void_p}])\n  fi\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([long long])\nif test \"x${ac_cv_sizeof_long_long}\" = \"x8\" ; then\n  LG_SIZEOF_LONG_LONG=3\nelif test \"x${ac_cv_sizeof_long_long}\" = \"x4\" ; then\n  LG_SIZEOF_LONG_LONG=2\nelse\n  AC_MSG_ERROR([Unsupported long long size: ${ac_cv_sizeof_long_long}])\nfi\nAC_DEFINE_UNQUOTED([LG_SIZEOF_LONG_LONG], [$LG_SIZEOF_LONG_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  i686|x86_64)\n\tHAVE_CPU_SPINWAIT=1\n\tif test \"x${je_cv_msvc}\" = \"xyes\" ; then\n\t    AC_CACHE_VAL([je_cv_pause_msvc],\n\t      [JE_COMPILABLE([pause instruction MSVC], [],\n\t\t\t\t\t[[_mm_pause(); return 0;]],\n\t\t\t\t\t[je_cv_pause_msvc])])\n\t    if test \"x${je_cv_pause_msvc}\" = \"xyes\" ; then\n\t\tCPU_SPINWAIT='_mm_pause()'\n\t    fi\n\telse\n\t    AC_CACHE_VAL([je_cv_pause],\n\t      [JE_COMPILABLE([pause instruction], [],\n\t\t\t\t\t[[__asm__ volatile(\"pause\"); return 0;]],\n\t\t\t\t\t[je_cv_pause])])\n\t    if test \"x${je_cv_pause}\" = \"xyes\" ; then\n\t\tCPU_SPINWAIT='__asm__ volatile(\"pause\")'\n\t    fi\n\tfi\n\t;;\n  *)\n\tHAVE_CPU_SPINWAIT=0\n\t;;\nesac\nAC_DEFINE_UNQUOTED([HAVE_CPU_SPINWAIT], [$HAVE_CPU_SPINWAIT])\nAC_DEFINE_UNQUOTED([CPU_SPINWAIT], [$CPU_SPINWAIT])\n\nAC_ARG_WITH([lg_vaddr],\n  [AS_HELP_STRING([--with-lg-vaddr=<lg-vaddr>], [Number of significant virtual address bits])],\n  [LG_VADDR=\"$with_lg_vaddr\"], [LG_VADDR=\"detect\"])\n\ncase \"${host_cpu}\" in\n  aarch64)\n    if test \"x$LG_VADDR\" = \"xdetect\"; then\n      AC_MSG_CHECKING([number of significant virtual address bits])\n      if test \"x${LG_SIZEOF_PTR}\" = \"x2\" ; then\n        #aarch64 ILP32\n        LG_VADDR=32\n      else\n        #aarch64 LP64\n        LG_VADDR=48\n      fi\n      AC_MSG_RESULT([$LG_VADDR])\n    fi\n    ;;\n  x86_64)\n    if test \"x$LG_VADDR\" = \"xdetect\"; then\n      AC_CACHE_CHECK([number of significant virtual address bits],\n                     [je_cv_lg_vaddr],\n                     AC_RUN_IFELSE([AC_LANG_PROGRAM(\n[[\n#include <stdio.h>\n#ifdef _WIN32\n#include <limits.h>\n#include <intrin.h>\ntypedef unsigned __int32 uint32_t;\n#else\n#include <stdint.h>\n#endif\n]], [[\n\tuint32_t r[[4]];\n\tuint32_t eax_in = 0x80000008U;\n#ifdef _WIN32\n\t__cpuid((int *)r, (int)eax_in);\n#else\n\tasm volatile (\"cpuid\"\n\t    : \"=a\" (r[[0]]), \"=b\" (r[[1]]), \"=c\" (r[[2]]), \"=d\" (r[[3]])\n\t    : \"a\" (eax_in), \"c\" (0)\n\t);\n#endif\n\tuint32_t eax_out = r[[0]];\n\tuint32_t vaddr = ((eax_out & 0x0000ff00U) >> 8);\n\tFILE *f = fopen(\"conftest.out\", \"w\");\n\tif (f == NULL) {\n\t\treturn 1;\n\t}\n\tif (vaddr > (sizeof(void *) << 3)) {\n\t\tvaddr = sizeof(void *) << 3;\n\t}\n\tfprintf(f, \"%u\", vaddr);\n\tfclose(f);\n\treturn 0;\n]])],\n                   [je_cv_lg_vaddr=`cat conftest.out`],\n                   [je_cv_lg_vaddr=error],\n                   [je_cv_lg_vaddr=57]))\n      if test \"x${je_cv_lg_vaddr}\" != \"x\" ; then\n        LG_VADDR=\"${je_cv_lg_vaddr}\"\n      fi\n      if test \"x${LG_VADDR}\" != \"xerror\" ; then\n        AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR])\n      else\n        AC_MSG_ERROR([cannot determine number of significant virtual address bits])\n      fi\n    fi\n    ;;\n  *)\n    if test \"x$LG_VADDR\" = \"xdetect\"; then\n      AC_MSG_CHECKING([number of significant virtual address bits])\n      if test \"x${LG_SIZEOF_PTR}\" = \"x3\" ; then\n        LG_VADDR=64\n      elif test \"x${LG_SIZEOF_PTR}\" = \"x2\" ; then\n        LG_VADDR=32\n      elif test \"x${LG_SIZEOF_PTR}\" = \"xLG_SIZEOF_PTR_WIN\" ; then\n        LG_VADDR=\"(1U << (LG_SIZEOF_PTR_WIN+3))\"\n      else\n        AC_MSG_ERROR([Unsupported lg(pointer size): ${LG_SIZEOF_PTR}])\n      fi\n      AC_MSG_RESULT([$LG_VADDR])\n    fi\n    ;;\nesac\nAC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR])\n\nLD_PRELOAD_VAR=\"LD_PRELOAD\"\nso=\"so\"\nimportlib=\"${so}\"\no=\"$ac_objext\"\na=\"a\"\nexe=\"$ac_exeext\"\nlibprefix=\"lib\"\nlink_whole_archive=\"0\"\nDSO_LDFLAGS='-shared -Wl,-soname,$(@F)'\nRPATH='-Wl,-rpath,$(1)'\nSOREV=\"${so}.${rev}\"\nPIC_CFLAGS='-fPIC -DPIC'\nCTARGET='-o $@'\nLDTARGET='-o $@'\nTEST_LD_MODE=\nEXTRA_LDFLAGS=\nARFLAGS='crus'\nAROUT=' $@'\nCC_MM=1\n\nif test \"x$je_cv_cray_prgenv_wrapper\" = \"xyes\" ; then\n  TEST_LD_MODE='-dynamic'\nfi\n\nif test \"x${je_cv_cray}\" = \"xyes\" ; then\n  CC_MM=\nfi\n\nAN_MAKEVAR([AR], [AC_PROG_AR])\nAN_PROGRAM([ar], [AC_PROG_AR])\nAC_DEFUN([AC_PROG_AR], [AC_CHECK_TOOL(AR, ar, :)])\nAC_PROG_AR\n\nAN_MAKEVAR([NM], [AC_PROG_NM])\nAN_PROGRAM([nm], [AC_PROG_NM])\nAC_DEFUN([AC_PROG_NM], [AC_CHECK_TOOL(NM, nm, :)])\nAC_PROG_NM\n\nAC_PROG_AWK\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_retain=\"0\"\nmaps_coalesce=\"1\"\nDUMP_SYMS=\"${NM} -a\"\nSYM_PREFIX=\"\"\ncase \"${host}\" in\n  *-*-darwin* | *-*-ios*)\n\tabi=\"macho\"\n\tRPATH=\"\"\n\tLD_PRELOAD_VAR=\"DYLD_INSERT_LIBRARIES\"\n\tso=\"dylib\"\n\timportlib=\"${so}\"\n\tforce_tls=\"0\"\n\tDSO_LDFLAGS='-shared -Wl,-install_name,$(LIBDIR)/$(@F)'\n\tSOREV=\"${rev}.${so}\"\n\tsbrk_deprecated=\"1\"\n\tSYM_PREFIX=\"_\"\n\t;;\n  *-*-freebsd*)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ])\n\tforce_lazy_lock=\"1\"\n\t;;\n  *-*-dragonfly*)\n\tabi=\"elf\"\n\t;;\n  *-*-openbsd*)\n\tabi=\"elf\"\n\tforce_tls=\"0\"\n\t;;\n  *-*-bitrig*)\n\tabi=\"elf\"\n\t;;\n  *-*-linux-android)\n\tdnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.\n\tJE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS], [ ])\n\tAC_DEFINE([JEMALLOC_HAS_ALLOCA_H])\n\tAC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tAC_DEFINE([JEMALLOC_C11_ATOMICS])\n\tforce_tls=\"0\"\n\tif test \"${LG_SIZEOF_PTR}\" = \"3\"; then\n\t  default_retain=\"1\"\n\tfi\n\t;;\n  *-*-linux*)\n\tdnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.\n\tJE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS], [ ])\n\tAC_DEFINE([JEMALLOC_HAS_ALLOCA_H])\n\tAC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tAC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ])\n\tif test \"${LG_SIZEOF_PTR}\" = \"3\"; then\n\t  default_retain=\"1\"\n\tfi\n\t;;\n  *-*-kfreebsd*)\n\tdnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.\n\tJE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_HAS_ALLOCA_H])\n\tAC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tAC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ])\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                          [abi=\"elf\"],\n                          [abi=\"aout\"])\n\tAC_MSG_RESULT([$abi])\n\t;;\n  *-*-solaris2*)\n\tabi=\"elf\"\n\tRPATH='-Wl,-R,$(1)'\n\tdnl Solaris needs this for sigwait().\n\tJE_APPEND_VS(CPPFLAGS, -D_POSIX_PTHREAD_SEMANTICS)\n\tJE_APPEND_VS(LIBS, -lposix4 -lsocket -lnsl)\n\t;;\n  *-ibm-aix*)\n\tif test \"${LG_SIZEOF_PTR}\" = \"3\"; 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* | *-*-cygwin*)\n\tabi=\"pecoff\"\n\tforce_tls=\"0\"\n\tmaps_coalesce=\"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  AR='lib'\n\t  ARFLAGS='-nologo -out:'\n\t  AROUT='$@'\n\t  CC_MM=\n        else\n\t  importlib=\"${so}\"\n\t  DSO_LDFLAGS=\"-shared\"\n\t  link_whole_archive=\"1\"\n\tfi\n\tcase \"${host}\" in\n\t  *-*-cygwin*)\n\t    DUMP_SYMS=\"dumpbin /SYMBOLS\"\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\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\n\nJEMALLOC_USABLE_SIZE_CONST=const\nAC_CHECK_HEADERS([malloc.h], [\n  AC_MSG_CHECKING([whether malloc_usable_size definition can use const argument])\n  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n    [#include <malloc.h>\n     #include <stddef.h>\n    size_t malloc_usable_size(const void *ptr);\n    ],\n    [])],[\n                AC_MSG_RESULT([yes])\n         ],[\n                JEMALLOC_USABLE_SIZE_CONST=\n                AC_MSG_RESULT([no])\n         ])\n])\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([link_whole_archive])\nAC_SUBST([DSO_LDFLAGS])\nAC_SUBST([EXTRA_LDFLAGS])\nAC_SUBST([SOREV])\nAC_SUBST([PIC_CFLAGS])\nAC_SUBST([CTARGET])\nAC_SUBST([LDTARGET])\nAC_SUBST([TEST_LD_MODE])\nAC_SUBST([MKLIB])\nAC_SUBST([ARFLAGS])\nAC_SUBST([AROUT])\nAC_SUBST([DUMP_SYMS])\nAC_SUBST([CC_MM])\n\ndnl Determine whether libm must be linked to use e.g. log(3).\nAC_SEARCH_LIBS([log], [m], , [AC_MSG_ERROR([Missing math functions])])\nif test \"x$ac_cv_search_log\" != \"xnone required\" ; then\n  LM=\"$ac_cv_search_log\"\nelse\n  LM=\nfi\nAC_SUBST(LM)\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_ADD([-fvisibility=hidden])\n    JE_CXXFLAGS_ADD([-fvisibility=hidden])\n  fi\nfi\ndnl Check for tls_model attribute support (clang 3.0 still lacks support).\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([tls_model attribute], [],\n              [static __thread int\n               __attribute__((tls_model(\"initial-exec\"), unused)) foo;\n               foo = 0;],\n              [je_cv_tls_model])\nJE_CFLAGS_RESTORE()\ndnl (Setting of JEMALLOC_TLS_MODEL is done later, after we've checked for\ndnl --disable-initial-exec-tls)\n\ndnl Check for alloc_size attribute support.\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([alloc_size attribute], [#include <stdlib.h>],\n              [void *foo(size_t size) __attribute__((alloc_size(1)));],\n              [je_cv_alloc_size])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_alloc_size}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR_ALLOC_SIZE], [ ])\nfi\ndnl Check for format(gnu_printf, ...) attribute support.\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([format(gnu_printf, ...) attribute], [#include <stdlib.h>],\n              [void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));],\n              [je_cv_format_gnu_printf])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_format_gnu_printf}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF], [ ])\nfi\ndnl Check for format(printf, ...) attribute support.\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],\n              [void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));],\n              [je_cv_format_printf])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_format_printf}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_PRINTF], [ ])\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([LD], [ld], [false], [$PATH])\nAC_PATH_PROG([AUTOCONF], [autoconf], [false], [$PATH])\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=\"\"])\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  AC_DEFINE([JEMALLOC_IS_MALLOC])\nelse\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\nAC_SUBST([JEMALLOC_PREFIX])\nAC_SUBST([JEMALLOC_CPREFIX])\n\nAC_ARG_WITH([export],\n  [AS_HELP_STRING([--without-export], [disable exporting jemalloc public APIs])],\n  [if test \"x$with_export\" = \"xno\"; then\n  AC_DEFINE([JEMALLOC_EXPORT],[])\nfi]\n)\n\npublic_syms=\"aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_message malloc_stats_print malloc_usable_size mallocx nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx\"\ndnl Check for additional platform-specific public API functions.\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 Check for allocator-related functions that should be wrapped.\nwrap_syms=\nif test \"x${JEMALLOC_PREFIX}\" = \"x\" ; then\n  AC_CHECK_FUNC([__libc_calloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_CALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_calloc\"])\n  AC_CHECK_FUNC([__libc_free],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_FREE], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_free\"])\n  AC_CHECK_FUNC([__libc_malloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_malloc\"])\n  AC_CHECK_FUNC([__libc_memalign],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MEMALIGN], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_memalign\"])\n  AC_CHECK_FUNC([__libc_realloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_REALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_realloc\"])\n  AC_CHECK_FUNC([__libc_valloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_VALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_valloc\"])\n  AC_CHECK_FUNC([__posix_memalign],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___POSIX_MEMALIGN], [ ])\n\t\t wrap_syms=\"${wrap_syms} __posix_memalign\"])\nfi\n\ncase \"${host}\" in\n  *-*-mingw* | *-*-cygwin*)\n    wrap_syms=\"${wrap_syms} tls_callback\"\n    ;;\n  *)\n    ;;\nesac\n\ndnl Mangle library-private APIs.\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}je_\"],\n  [JEMALLOC_PRIVATE_NAMESPACE=\"je_\"]\n)\nAC_DEFINE_UNQUOTED([JEMALLOC_PRIVATE_NAMESPACE], [$JEMALLOC_PRIVATE_NAMESPACE])\nprivate_namespace=\"$JEMALLOC_PRIVATE_NAMESPACE\"\nAC_SUBST([private_namespace])\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\ndnl Specify default malloc_conf.\nAC_ARG_WITH([malloc_conf],\n  [AS_HELP_STRING([--with-malloc-conf=<malloc_conf>], [config.malloc_conf options string])],\n  [JEMALLOC_CONFIG_MALLOC_CONF=\"$with_malloc_conf\"],\n  [JEMALLOC_CONFIG_MALLOC_CONF=\"\"]\n)\nconfig_malloc_conf=\"$JEMALLOC_CONFIG_MALLOC_CONF\"\nAC_DEFINE_UNQUOTED([JEMALLOC_CONFIG_MALLOC_CONF], [\"$config_malloc_conf\"])\n\ndnl Substitute @je_@ in jemalloc_protos.h.in, primarily to make generation of\ndnl jemalloc_protos_jet.h easy.\nje_=\"je_\"\nAC_SUBST([je_])\n\ncfgoutputs_in=\"Makefile.in\"\ncfgoutputs_in=\"${cfgoutputs_in} jemalloc.pc.in\"\ncfgoutputs_in=\"${cfgoutputs_in} doc/html.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} doc/manpages.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} doc/jemalloc.xml.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/jemalloc_macros.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/jemalloc_protos.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/jemalloc_typedefs.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/internal/jemalloc_preamble.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} test/test.sh.in\"\ncfgoutputs_in=\"${cfgoutputs_in} test/include/test/jemalloc_test.h.in\"\n\ncfgoutputs_out=\"Makefile\"\ncfgoutputs_out=\"${cfgoutputs_out} jemalloc.pc\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/html.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/manpages.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/jemalloc.xml\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc_macros.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc_protos.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc_typedefs.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/internal/jemalloc_preamble.h\"\ncfgoutputs_out=\"${cfgoutputs_out} test/test.sh\"\ncfgoutputs_out=\"${cfgoutputs_out} test/include/test/jemalloc_test.h\"\n\ncfgoutputs_tup=\"Makefile\"\ncfgoutputs_tup=\"${cfgoutputs_tup} jemalloc.pc:jemalloc.pc.in\"\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.xml:doc/jemalloc.xml.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc_macros.h:include/jemalloc/jemalloc_macros.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc_protos.h:include/jemalloc/jemalloc_protos.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc_typedefs.h:include/jemalloc/jemalloc_typedefs.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/internal/jemalloc_preamble.h\"\ncfgoutputs_tup=\"${cfgoutputs_tup} test/test.sh:test/test.sh.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} test/include/test/jemalloc_test.h:test/include/test/jemalloc_test.h.in\"\n\ncfghdrs_in=\"include/jemalloc/jemalloc_defs.h.in\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/jemalloc_internal_defs.h.in\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/private_symbols.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/private_namespace.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/public_namespace.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/public_unnamespace.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/size_classes.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/jemalloc_rename.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/jemalloc_mangle.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/jemalloc.sh\"\ncfghdrs_in=\"${cfghdrs_in} test/include/test/jemalloc_test_defs.h.in\"\n\ncfghdrs_out=\"include/jemalloc/jemalloc_defs.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc${install_suffix}.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/private_symbols.awk\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/private_symbols_jet.awk\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/public_symbols.txt\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/public_namespace.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/public_unnamespace.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/size_classes.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_protos_jet.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_rename.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_mangle.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_mangle_jet.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/jemalloc_internal_defs.h\"\ncfghdrs_out=\"${cfghdrs_out} test/include/test/jemalloc_test_defs.h\"\n\ncfghdrs_tup=\"include/jemalloc/jemalloc_defs.h:include/jemalloc/jemalloc_defs.h.in\"\ncfghdrs_tup=\"${cfghdrs_tup} include/jemalloc/internal/jemalloc_internal_defs.h:include/jemalloc/internal/jemalloc_internal_defs.h.in\"\ncfghdrs_tup=\"${cfghdrs_tup} test/include/test/jemalloc_test_defs.h:test/include/test/jemalloc_test_defs.h.in\"\n\ndnl Do not compile with debugging by default.\nAC_ARG_ENABLE([debug],\n  [AS_HELP_STRING([--enable-debug],\n                  [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], [ ])\nfi\nif test \"x$enable_debug\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_DEBUG], [ ])\nfi\nAC_SUBST([enable_debug])\n\ndnl Only optimize if not debugging.\nif test \"x$enable_debug\" = \"x0\" ; then\n  if test \"x$GCC\" = \"xyes\" ; then\n    JE_CFLAGS_ADD([-O3])\n    JE_CXXFLAGS_ADD([-O3])\n    JE_CFLAGS_ADD([-funroll-loops])\n  elif test \"x$je_cv_msvc\" = \"xyes\" ; then\n    JE_CFLAGS_ADD([-O2])\n    JE_CXXFLAGS_ADD([-O2])\n  else\n    JE_CFLAGS_ADD([-O])\n    JE_CXXFLAGS_ADD([-O])\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], [unw_backtrace], [JE_APPEND_VS(LIBS, $LUNWIND)],\n                 [enable_prof_libunwind=\"0\"])\n  else\n    JE_APPEND_VS(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  if test \"x${enable_prof_libgcc}\" = \"x1\" ; then\n    AC_CHECK_LIB([gcc], [_Unwind_Backtrace], [JE_APPEND_VS(LIBS, -lgcc)], [enable_prof_libgcc=\"0\"])\n  fi\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  JE_CFLAGS_ADD([-fno-omit-frame-pointer])\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  dnl Heap profiling uses the log(3) function.\n  JE_APPEND_VS(LIBS, $LM)\n\n  AC_DEFINE([JEMALLOC_PROF], [ ])\nfi\nAC_SUBST([enable_prof])\n\ndnl Indicate whether adjacent virtual memory mappings automatically coalesce\ndnl (and fragment on demand).\nif test \"x${maps_coalesce}\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_MAPS_COALESCE], [ ])\nfi\n\ndnl Indicate whether to retain memory (rather than using munmap()) by default.\nif test \"x$default_retain\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_RETAIN], [ ])\nfi\n\ndnl Enable allocation from DSS if supported by the OS.\nhave_dss=\"1\"\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  if test \"x$sbrk_deprecated\" = \"x1\" ; then\n    AC_MSG_RESULT([Disabling dss allocation because sbrk is deprecated])\n    have_dss=\"0\"\n  fi\nelse\n  have_dss=\"0\"\nfi\n\nif test \"x$have_dss\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_DSS], [ ])\nfi\n\ndnl Support the junk/zero filling option by default.\nAC_ARG_ENABLE([fill],\n  [AS_HELP_STRING([--disable-fill], [Disable support for junk/zero filling])],\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 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\ndnl Support cache-oblivious allocation alignment by default.\nAC_ARG_ENABLE([cache-oblivious],\n  [AS_HELP_STRING([--disable-cache-oblivious],\n                  [Disable support for cache-oblivious allocation alignment])],\n[if test \"x$enable_cache_oblivious\" = \"xno\" ; then\n  enable_cache_oblivious=\"0\"\nelse\n  enable_cache_oblivious=\"1\"\nfi\n],\n[enable_cache_oblivious=\"1\"]\n)\nif test \"x$enable_cache_oblivious\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_CACHE_OBLIVIOUS], [ ])\nfi\nAC_SUBST([enable_cache_oblivious])\n\ndnl Do not log by default.\nAC_ARG_ENABLE([log],\n  [AS_HELP_STRING([--enable-log], [Support debug logging])],\n[if test \"x$enable_log\" = \"xno\" ; then\n  enable_log=\"0\"\nelse\n  enable_log=\"1\"\nfi\n],\n[enable_log=\"0\"]\n)\nif test \"x$enable_log\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_LOG], [ ])\nfi\nAC_SUBST([enable_log])\n\n\nJE_COMPILABLE([a program using __builtin_unreachable], [\nvoid foo (void) {\n  __builtin_unreachable();\n}\n], [\n\t{\n\t\tfoo();\n\t}\n], [je_cv_gcc_builtin_unreachable])\nif test \"x${je_cv_gcc_builtin_unreachable}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [__builtin_unreachable])\nelse\n  AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [abort])\nfi\n\ndnl ============================================================================\ndnl Check for  __builtin_ffsl(), then ffsl(3), and fail if neither are found.\ndnl One of those two functions should (theoretically) exist on all platforms\ndnl that jemalloc currently has a chance of functioning on without modification.\ndnl We additionally assume ffs[ll]() or __builtin_ffs[ll]() are defined if\ndnl ffsl() or __builtin_ffsl() are defined, respectively.\nJE_COMPILABLE([a program using __builtin_ffsl], [\n#include <stdio.h>\n#include <strings.h>\n#include <string.h>\n], [\n\t{\n\t\tint rv = __builtin_ffsl(0x08);\n\t\tprintf(\"%d\\n\", rv);\n\t}\n], [je_cv_gcc_builtin_ffsl])\nif test \"x${je_cv_gcc_builtin_ffsl}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_INTERNAL_FFSLL], [__builtin_ffsll])\n  AC_DEFINE([JEMALLOC_INTERNAL_FFSL], [__builtin_ffsl])\n  AC_DEFINE([JEMALLOC_INTERNAL_FFS], [__builtin_ffs])\nelse\n  JE_COMPILABLE([a program using ffsl], [\n  #include <stdio.h>\n  #include <strings.h>\n  #include <string.h>\n  ], [\n\t{\n\t\tint rv = ffsl(0x08);\n\t\tprintf(\"%d\\n\", rv);\n\t}\n  ], [je_cv_function_ffsl])\n  if test \"x${je_cv_function_ffsl}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_INTERNAL_FFSLL], [ffsll])\n    AC_DEFINE([JEMALLOC_INTERNAL_FFSL], [ffsl])\n    AC_DEFINE([JEMALLOC_INTERNAL_FFS], [ffs])\n  else\n    AC_MSG_ERROR([Cannot build without ffsl(3) or __builtin_ffsl()])\n  fi\nfi\n\nAC_ARG_WITH([lg_quantum],\n  [AS_HELP_STRING([--with-lg-quantum=<lg-quantum>],\n   [Base 2 log of minimum allocation alignment])],\n  [LG_QUANTA=\"$with_lg_quantum\"],\n  [LG_QUANTA=\"3 4\"])\nif test \"x$with_lg_quantum\" != \"x\" ; then\n  AC_DEFINE_UNQUOTED([LG_QUANTUM], [$with_lg_quantum])\nfi\n\nAC_ARG_WITH([lg_page],\n  [AS_HELP_STRING([--with-lg-page=<lg-page>], [Base 2 log of system page size])],\n  [LG_PAGE=\"$with_lg_page\"], [LG_PAGE=\"detect\"])\nif test \"x$LG_PAGE\" = \"xdetect\"; then\n  AC_CACHE_CHECK([LG_PAGE],\n               [je_cv_lg_page],\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    int 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 = JEMALLOC_INTERNAL_FFSL(result) - 1;\n\n    f = fopen(\"conftest.out\", \"w\");\n    if (f == NULL) {\n\treturn 1;\n    }\n    fprintf(f, \"%d\", result);\n    fclose(f);\n\n    return 0;\n]])],\n                             [je_cv_lg_page=`cat conftest.out`],\n                             [je_cv_lg_page=undefined],\n                             [je_cv_lg_page=12]))\nfi\nif test \"x${je_cv_lg_page}\" != \"x\" ; then\n  LG_PAGE=\"${je_cv_lg_page}\"\nfi\nif test \"x${LG_PAGE}\" != \"xundefined\" ; then\n   AC_DEFINE_UNQUOTED([LG_PAGE], [$LG_PAGE])\nelse\n   AC_MSG_ERROR([cannot determine value for LG_PAGE])\nfi\n\nAC_ARG_WITH([lg_hugepage],\n  [AS_HELP_STRING([--with-lg-hugepage=<lg-hugepage>],\n   [Base 2 log of system huge page size])],\n  [je_cv_lg_hugepage=\"${with_lg_hugepage}\"],\n  [je_cv_lg_hugepage=\"\"])\nif test \"x${je_cv_lg_hugepage}\" = \"x\" ; then\n  dnl Look in /proc/meminfo (Linux-specific) for information on the default huge\n  dnl page size, if any.  The relevant line looks like:\n  dnl\n  dnl   Hugepagesize:       2048 kB\n  if test -e \"/proc/meminfo\" ; then\n    hpsk=[`cat /proc/meminfo 2>/dev/null | \\\n          grep -e '^Hugepagesize:[[:space:]]\\+[0-9]\\+[[:space:]]kB$' | \\\n          awk '{print $2}'`]\n    if test \"x${hpsk}\" != \"x\" ; then\n      je_cv_lg_hugepage=10\n      while test \"${hpsk}\" -gt 1 ; do\n        hpsk=\"$((hpsk / 2))\"\n        je_cv_lg_hugepage=\"$((je_cv_lg_hugepage + 1))\"\n      done\n    fi\n  fi\n\n  dnl Set default if unable to automatically configure.\n  if test \"x${je_cv_lg_hugepage}\" = \"x\" ; then\n    je_cv_lg_hugepage=21\n  fi\nfi\nif test \"x${LG_PAGE}\" != \"xundefined\" -a \\\n        \"${je_cv_lg_hugepage}\" -lt \"${LG_PAGE}\" ; then\n  AC_MSG_ERROR([Huge page size (2^${je_cv_lg_hugepage}) must be at least page size (2^${LG_PAGE})])\nfi\nAC_DEFINE_UNQUOTED([LG_HUGEPAGE], [${je_cv_lg_hugepage}])\n\nAC_ARG_WITH([lg_page_sizes],\n  [AS_HELP_STRING([--with-lg-page-sizes=<lg-page-sizes>],\n   [Base 2 logs of system page sizes to support])],\n  [LG_PAGE_SIZES=\"$with_lg_page_sizes\"], [LG_PAGE_SIZES=\"$LG_PAGE\"])\n\ndnl ============================================================================\ndnl jemalloc configuration.\ndnl\n\nAC_ARG_WITH([version],\n  [AS_HELP_STRING([--with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid>],\n   [Version string])],\n  [\n    echo \"${with_version}\" | grep ['^[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+-[0-9]\\+-g[0-9a-f]\\+$'] 2>&1 1>/dev/null\n    if test $? -eq 0 ; then\n      echo \"$with_version\" > \"${objroot}VERSION\"\n    else\n      echo \"${with_version}\" | grep ['^VERSION$'] 2>&1 1>/dev/null\n      if test $? -ne 0 ; then\n        AC_MSG_ERROR([${with_version} does not match <major>.<minor>.<bugfix>-<nrev>-g<gid> or VERSION])\n      fi\n    fi\n  ], [\n    dnl Set VERSION if source directory is inside a git repository.\n    if test \"x`test ! \\\"${srcroot}\\\" && cd \\\"${srcroot}\\\"; git rev-parse --is-inside-work-tree 2>/dev/null`\" = \"xtrue\" ; then\n      dnl Pattern globs aren't powerful enough to match both single- and\n      dnl double-digit version numbers, so iterate over patterns to support up\n      dnl to version 99.99.99 without any accidental matches.\n      for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \\\n                     '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \\\n                     '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \\\n                     '[0-9][0-9].[0-9][0-9].[0-9]' \\\n                     '[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do\n        (test ! \"${srcroot}\" && cd \"${srcroot}\"; git describe --long --abbrev=40 --match=\"${pattern}\") > \"${objroot}VERSION.tmp\" 2>/dev/null\n        if test $? -eq 0 ; then\n          mv \"${objroot}VERSION.tmp\" \"${objroot}VERSION\"\n          break\n        fi\n      done\n    fi\n    rm -f \"${objroot}VERSION.tmp\"\n  ])\n\nif test ! -e \"${objroot}VERSION\" ; then\n  if test ! -e \"${srcroot}VERSION\" ; then\n    AC_MSG_RESULT(\n      [Missing VERSION file, and unable to generate it; creating bogus VERSION])\n    echo \"0.0.0-0-g0000000000000000000000000000000000000000\" > \"${objroot}VERSION\"\n  else\n    cp ${srcroot}VERSION ${objroot}VERSION\n  fi\nfi\njemalloc_version=`cat \"${objroot}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_DEFINE([JEMALLOC_HAVE_PTHREAD], [ ])\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], [JE_APPEND_VS(LIBS, -lpthread)],\n               [AC_SEARCH_LIBS([pthread_create], , ,\n                               AC_MSG_ERROR([libpthread is missing]))])\n  wrap_syms=\"${wrap_syms} pthread_create\"\n  have_pthread=\"1\"\n  dnl Check if we have dlsym support.\n  have_dlsym=\"1\"\n  AC_CHECK_HEADERS([dlfcn.h],\n    AC_CHECK_FUNC([dlsym], [],\n      [AC_CHECK_LIB([dl], [dlsym], [LIBS=\"$LIBS -ldl\"], [have_dlsym=\"0\"])]),\n    [have_dlsym=\"0\"])\n  if test \"x$have_dlsym\" = \"x1\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_DLSYM], [ ])\n  fi\n  JE_COMPILABLE([pthread_atfork(3)], [\n#include <pthread.h>\n], [\n  pthread_atfork((void *)0, (void *)0, (void *)0);\n], [je_cv_pthread_atfork])\n  if test \"x${je_cv_pthread_atfork}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_PTHREAD_ATFORK], [ ])\n  fi\n  dnl Check if pthread_setname_np is available with the expected API.\n  JE_COMPILABLE([pthread_setname_np(3)], [\n#include <pthread.h>\n], [\n  pthread_setname_np(pthread_self(), \"setname_test\");\n], [je_cv_pthread_setname_np])\n  if test \"x${je_cv_pthread_setname_np}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_PTHREAD_SETNAME_NP], [ ])\n  fi\nfi\n\nJE_APPEND_VS(CPPFLAGS, -D_REENTRANT)\n\ndnl Check whether clock_gettime(2) is in libc or librt.\nAC_SEARCH_LIBS([clock_gettime], [rt])\n\ndnl Cray wrapper compiler often adds `-lrt` when using `-static`. Check with\ndnl `-dynamic` as well in case a user tries to dynamically link in jemalloc\nif test \"x$je_cv_cray_prgenv_wrapper\" = \"xyes\" ; then\n  if test \"$ac_cv_search_clock_gettime\" != \"-lrt\"; then\n    JE_CFLAGS_SAVE()\n\n    unset ac_cv_search_clock_gettime\n    JE_CFLAGS_ADD([-dynamic])\n    AC_SEARCH_LIBS([clock_gettime], [rt])\n\n    JE_CFLAGS_RESTORE()\n  fi\nfi\n\ndnl check for CLOCK_MONOTONIC_COARSE (Linux-specific).\nJE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC_COARSE, ...)], [\n#include <time.h>\n], [\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC_COARSE, &ts);\n], [je_cv_clock_monotonic_coarse])\nif test \"x${je_cv_clock_monotonic_coarse}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE])\nfi\n\ndnl check for CLOCK_MONOTONIC.\nJE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC, ...)], [\n#include <unistd.h>\n#include <time.h>\n], [\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\n#if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0\n#  error _POSIX_MONOTONIC_CLOCK missing/invalid\n#endif\n], [je_cv_clock_monotonic])\nif test \"x${je_cv_clock_monotonic}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC])\nfi\n\ndnl Check for mach_absolute_time().\nJE_COMPILABLE([mach_absolute_time()], [\n#include <mach/mach_time.h>\n], [\n\tmach_absolute_time();\n], [je_cv_mach_absolute_time])\nif test \"x${je_cv_mach_absolute_time}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_MACH_ABSOLUTE_TIME])\nfi\n\ndnl Use syscall(2) (if available) by default.\nAC_ARG_ENABLE([syscall],\n  [AS_HELP_STRING([--disable-syscall], [Disable use of syscall(2)])],\n[if test \"x$enable_syscall\" = \"xno\" ; then\n  enable_syscall=\"0\"\nelse\n  enable_syscall=\"1\"\nfi\n],\n[enable_syscall=\"1\"]\n)\nif test \"x$enable_syscall\" = \"x1\" ; then\n  dnl Check if syscall(2) is usable.  Treat warnings as errors, so that e.g. OS\n  dnl X 10.12's deprecation warning prevents use.\n  JE_CFLAGS_SAVE()\n  JE_CFLAGS_ADD([-Werror])\n  JE_COMPILABLE([syscall(2)], [\n#include <sys/syscall.h>\n#include <unistd.h>\n], [\n\tsyscall(SYS_write, 2, \"hello\", 5);\n],\n                [je_cv_syscall])\n  JE_CFLAGS_RESTORE()\n  if test \"x$je_cv_syscall\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_USE_SYSCALL], [ ])\n  fi\nfi\n\ndnl Check if the GNU-specific secure_getenv function exists.\nAC_CHECK_FUNC([secure_getenv],\n              [have_secure_getenv=\"1\"],\n              [have_secure_getenv=\"0\"]\n             )\nif test \"x$have_secure_getenv\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SECURE_GETENV], [ ])\nfi\n\ndnl Check if the GNU-specific sched_getcpu function exists.\nAC_CHECK_FUNC([sched_getcpu],\n              [have_sched_getcpu=\"1\"],\n              [have_sched_getcpu=\"0\"]\n             )\nif test \"x$have_sched_getcpu\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SCHED_GETCPU], [ ])\nfi\n\ndnl Check if the GNU-specific sched_setaffinity function exists.\nAC_CHECK_FUNC([sched_setaffinity],\n              [have_sched_setaffinity=\"1\"],\n              [have_sched_setaffinity=\"0\"]\n             )\nif test \"x$have_sched_setaffinity\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SCHED_SETAFFINITY], [ ])\nfi\n\ndnl Check if the Solaris/BSD issetugid function exists.\nAC_CHECK_FUNC([issetugid],\n              [have_issetugid=\"1\"],\n              [have_issetugid=\"0\"]\n             )\nif test \"x$have_issetugid\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ISSETUGID], [ ])\nfi\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  wrap_syms=\"${wrap_syms} _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])\n  wrap_syms=\"${wrap_syms} _malloc_prefork _malloc_postfork\"\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=\"\"]\n)\nif test \"x${enable_lazy_lock}\" = \"x\" ; then\n  if test \"x${force_lazy_lock}\" = \"x1\" ; then\n    AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])\n    enable_lazy_lock=\"1\"\n  else\n    enable_lazy_lock=\"0\"\n  fi\nfi\nif test \"x${enable_lazy_lock}\" = \"x1\" -a \"x${abi}\" = \"xpecoff\" ; then\n  AC_MSG_RESULT([Forcing no lazy-lock because thread creation monitoring is unimplemented])\n  enable_lazy_lock=\"0\"\nfi\nif test \"x$enable_lazy_lock\" = \"x1\" ; then\n  if test \"x$have_dlsym\" = \"x1\" ; then\n    AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ])\n  else\n    AC_MSG_ERROR([Missing dlsym support: lazy-lock cannot be enabled.])\n  fi\nfi\nAC_SUBST([enable_lazy_lock])\n\ndnl Automatically configure TLS.\nif test \"x${force_tls}\" = \"x1\" ; then\n  enable_tls=\"1\"\nelif test \"x${force_tls}\" = \"x0\" ; then\n  enable_tls=\"0\"\nelse\n  enable_tls=\"1\"\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\")\nelse\n  enable_tls=\"0\"\nfi\nAC_SUBST([enable_tls])\nif test \"x${enable_tls}\" = \"x1\" ; then\n  AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ])\nfi\n\ndnl ============================================================================\ndnl Check for C11 atomics.\n\nJE_COMPILABLE([C11 atomics], [\n#include <stdint.h>\n#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)\n#include <stdatomic.h>\n#else\n#error Atomics not available\n#endif\n], [\n    uint64_t *p = (uint64_t *)0;\n    uint64_t x = 1;\n    volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;\n    uint64_t r = atomic_fetch_add(a, x) + x;\n    return r == 0;\n], [je_cv_c11_atomics])\nif test \"x${je_cv_c11_atomics}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_C11_ATOMICS])\nfi\n\ndnl ============================================================================\ndnl Check for GCC-style __atomic atomics.\n\nJE_COMPILABLE([GCC __atomic atomics], [\n], [\n    int x = 0;\n    int val = 1;\n    int y = __atomic_fetch_add(&x, val, __ATOMIC_RELAXED);\n    int after_add = x;\n    return after_add == 1;\n], [je_cv_gcc_atomic_atomics])\nif test \"x${je_cv_gcc_atomic_atomics}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_GCC_ATOMIC_ATOMICS])\nfi\n\ndnl ============================================================================\ndnl Check for GCC-style __sync atomics.\n\nJE_COMPILABLE([GCC __sync atomics], [\n], [\n    int x = 0;\n    int before_add = __sync_fetch_and_add(&x, 1);\n    int after_add = x;\n    return (before_add == 0) && (after_add == 1);\n], [je_cv_gcc_sync_atomics])\nif test \"x${je_cv_gcc_sync_atomics}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_GCC_SYNC_ATOMICS])\nfi\n\ndnl ============================================================================\ndnl Check for atomic(3) operations as provided on Darwin.\ndnl We need this not for the atomic operations (which are provided above), but\ndnl rather for the OSSpinLock type it exposes.\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 for madvise(2).\n\nJE_COMPILABLE([madvise(2)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, 0);\n], [je_cv_madvise])\nif test \"x${je_cv_madvise}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ])\n\n  dnl Check for madvise(..., MADV_FREE).\n  JE_COMPILABLE([madvise(..., MADV_FREE)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_FREE);\n], [je_cv_madv_free])\n  if test \"x${je_cv_madv_free}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n  elif test \"x${je_cv_madvise}\" = \"xyes\" ; then\n    case \"${host_cpu}\" in i686|x86_64)\n        case \"${host}\" in *-*-linux*)\n            AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n            AC_DEFINE([JEMALLOC_DEFINE_MADVISE_FREE], [ ])\n\t    ;;\n        esac\n        ;;\n    esac\n  fi\n\n  dnl Check for madvise(..., MADV_DONTNEED).\n  JE_COMPILABLE([madvise(..., MADV_DONTNEED)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_DONTNEED);\n], [je_cv_madv_dontneed])\n  if test \"x${je_cv_madv_dontneed}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])\n  fi\n\n  dnl Check for madvise(..., MADV_DO[NT]DUMP).\n  JE_COMPILABLE([madvise(..., MADV_DO[[NT]]DUMP)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_DONTDUMP);\n\tmadvise((void *)0, 0, MADV_DODUMP);\n], [je_cv_madv_dontdump])\n  if test \"x${je_cv_madv_dontdump}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_MADVISE_DONTDUMP], [ ])\n  fi\n \n  dnl Check for madvise(..., MADV_[NO]HUGEPAGE).\n  JE_COMPILABLE([madvise(..., MADV_[[NO]]HUGEPAGE)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_HUGEPAGE);\n\tmadvise((void *)0, 0, MADV_NOHUGEPAGE);\n], [je_cv_thp])\ncase \"${host_cpu}\" in\n  arm*)\n    ;;\n  *)\n  if test \"x${je_cv_thp}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_MADVISE_HUGE], [ ])\n  fi\n  ;;\nesac\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 __builtin_clz() and __builtin_clzl().\n\nAC_CACHE_CHECK([for __builtin_clz],\n               [je_cv_builtin_clz],\n               [AC_LINK_IFELSE([AC_LANG_PROGRAM([],\n                                                [\n                                                {\n                                                        unsigned x = 0;\n                                                        int y = __builtin_clz(x);\n                                                }\n                                                {\n                                                        unsigned long x = 0;\n                                                        int y = __builtin_clzl(x);\n                                                }\n                                                ])],\n                               [je_cv_builtin_clz=yes],\n                               [je_cv_builtin_clz=no])])\n\nif test \"x${je_cv_builtin_clz}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_BUILTIN_CLZ], [ ])\nfi\n\ndnl ============================================================================\ndnl Check for os_unfair_lock operations as provided on Darwin.\n\nJE_COMPILABLE([Darwin os_unfair_lock_*()], [\n#include <os/lock.h>\n#include <AvailabilityMacros.h>\n], [\n\t#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200\n\t#error \"os_unfair_lock is not supported\"\n\t#else\n\tos_unfair_lock lock = OS_UNFAIR_LOCK_INIT;\n\tos_unfair_lock_lock(&lock);\n\tos_unfair_lock_unlock(&lock);\n\t#endif\n], [je_cv_os_unfair_lock])\nif test \"x${je_cv_os_unfair_lock}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_OS_UNFAIR_LOCK], [ ])\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\nAC_ARG_ENABLE([zone-allocator],\n  [AS_HELP_STRING([--disable-zone-allocator],\n                  [Disable zone allocator for Darwin])],\n[if test \"x$enable_zone_allocator\" = \"xno\" ; then\n  enable_zone_allocator=\"0\"\nelse\n  enable_zone_allocator=\"1\"\nfi\n],\n[if test \"x${abi}\" = \"xmacho\"; then\n  enable_zone_allocator=\"1\"\nfi\n]\n)\nAC_SUBST([enable_zone_allocator])\n\nif test \"x${enable_zone_allocator}\" = \"x1\" ; then\n  if test \"x${abi}\" != \"xmacho\"; then\n    AC_MSG_ERROR([--enable-zone-allocator is only supported on Darwin])\n  fi\n  AC_DEFINE([JEMALLOC_ZONE], [ ])\nfi\n\ndnl ============================================================================\ndnl Use initial-exec TLS by default.\nAC_ARG_ENABLE([initial-exec-tls],\n  [AS_HELP_STRING([--disable-initial-exec-tls],\n                  [Disable the initial-exec tls model])],\n[if test \"x$enable_initial_exec_tls\" = \"xno\" ; then\n  enable_initial_exec_tls=\"0\"\nelse\n  enable_initial_exec_tls=\"1\"\nfi\n],\n[enable_initial_exec_tls=\"1\"]\n)\nAC_SUBST([enable_initial_exec_tls])\n\nif test \"x${je_cv_tls_model}\" = \"xyes\" -a \\\n       \"x${enable_initial_exec_tls}\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_TLS_MODEL],\n            [__attribute__((tls_model(\"initial-exec\")))])\nelse\n  AC_DEFINE([JEMALLOC_TLS_MODEL], [ ])\nfi\n\ndnl ============================================================================\ndnl Enable background threads if possible.\n\nif test \"x${have_pthread}\" = \"x1\" -a \"x${have_dlsym}\" = \"x1\" \\\n    -a \"x${je_cv_os_unfair_lock}\" != \"xyes\" \\\n    -a \"x${je_cv_osspin}\" != \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_BACKGROUND_THREAD])\nfi\n\ndnl ============================================================================\ndnl Check for glibc malloc hooks\n\nJE_COMPILABLE([glibc malloc hook], [\n#include <stddef.h>\n\nextern void (* __free_hook)(void *ptr);\nextern void *(* __malloc_hook)(size_t size);\nextern void *(* __realloc_hook)(void *ptr, size_t size);\n], [\n  void *ptr = 0L;\n  if (__malloc_hook) ptr = __malloc_hook(1);\n  if (__realloc_hook) ptr = __realloc_hook(ptr, 2);\n  if (__free_hook && ptr) __free_hook(ptr);\n], [je_cv_glibc_malloc_hook])\nif test \"x${je_cv_glibc_malloc_hook}\" = \"xyes\" ; then\n  if test \"x${JEMALLOC_PREFIX}\" = \"x\" ; then\n    AC_DEFINE([JEMALLOC_GLIBC_MALLOC_HOOK], [ ])\n    wrap_syms=\"${wrap_syms} __free_hook __malloc_hook __realloc_hook\"\n  fi\nfi\n\nJE_COMPILABLE([glibc memalign hook], [\n#include <stddef.h>\n\nextern void *(* __memalign_hook)(size_t alignment, size_t size);\n], [\n  void *ptr = 0L;\n  if (__memalign_hook) ptr = __memalign_hook(16, 7);\n], [je_cv_glibc_memalign_hook])\nif test \"x${je_cv_glibc_memalign_hook}\" = \"xyes\" ; then\n  if test \"x${JEMALLOC_PREFIX}\" = \"x\" ; then\n    AC_DEFINE([JEMALLOC_GLIBC_MEMALIGN_HOOK], [ ])\n    wrap_syms=\"${wrap_syms} __memalign_hook\"\n  fi\nfi\n\nJE_COMPILABLE([pthreads adaptive mutexes], [\n#include <pthread.h>\n], [\n  pthread_mutexattr_t attr;\n  pthread_mutexattr_init(&attr);\n  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);\n  pthread_mutexattr_destroy(&attr);\n], [je_cv_pthread_mutex_adaptive_np])\nif test \"x${je_cv_pthread_mutex_adaptive_np}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP], [ ])\nfi\n\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-D_GNU_SOURCE])\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([strerror_r returns char with gnu source], [\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n], [\n  char *buffer = (char *) malloc(100);\n  char *error = strerror_r(EINVAL, buffer, 100);\n  printf(\"%s\\n\", error);\n], [je_cv_strerror_r_returns_char_with_gnu_source])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_strerror_r_returns_char_with_gnu_source}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE], [ ])\nfi\n\ndnl ============================================================================\ndnl Check for typedefs, structures, and compiler characteristics.\nAC_HEADER_STDBOOL\n\ndnl ============================================================================\ndnl Define commands that generate output files.\n\nAC_CONFIG_COMMANDS([include/jemalloc/internal/public_symbols.txt], [\n  f=\"${objroot}include/jemalloc/internal/public_symbols.txt\"\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  cp /dev/null \"${f}\"\n  for nm in `echo ${mangling_map} |tr ',' ' '` ; do\n    n=`echo ${nm} |tr ':' ' ' |awk '{print $[]1}'`\n    m=`echo ${nm} |tr ':' ' ' |awk '{print $[]2}'`\n    echo \"${n}:${m}\" >> \"${f}\"\n    dnl Remove name from public_syms so that it isn't redefined later.\n    public_syms=`for sym in ${public_syms}; do echo \"${sym}\"; done |grep -v \"^${n}\\$\" |tr '\\n' ' '`\n  done\n  for sym in ${public_syms} ; do\n    n=\"${sym}\"\n    m=\"${JEMALLOC_PREFIX}${sym}\"\n    echo \"${n}:${m}\" >> \"${f}\"\n  done\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  mangling_map=\"${mangling_map}\"\n  public_syms=\"${public_syms}\"\n  JEMALLOC_PREFIX=\"${JEMALLOC_PREFIX}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/private_symbols.awk], [\n  f=\"${objroot}include/jemalloc/internal/private_symbols.awk\"\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  export_syms=`for sym in ${public_syms}; do echo \"${JEMALLOC_PREFIX}${sym}\"; done; for sym in ${wrap_syms}; do echo \"${sym}\"; done;`\n  \"${srcdir}/include/jemalloc/internal/private_symbols.sh\" \"${SYM_PREFIX}\" ${export_syms} > \"${objroot}include/jemalloc/internal/private_symbols.awk\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  public_syms=\"${public_syms}\"\n  wrap_syms=\"${wrap_syms}\"\n  SYM_PREFIX=\"${SYM_PREFIX}\"\n  JEMALLOC_PREFIX=\"${JEMALLOC_PREFIX}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/private_symbols_jet.awk], [\n  f=\"${objroot}include/jemalloc/internal/private_symbols_jet.awk\"\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  export_syms=`for sym in ${public_syms}; do echo \"jet_${sym}\"; done; for sym in ${wrap_syms}; do echo \"${sym}\"; done;`\n  \"${srcdir}/include/jemalloc/internal/private_symbols.sh\" \"${SYM_PREFIX}\" ${export_syms} > \"${objroot}include/jemalloc/internal/private_symbols_jet.awk\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  public_syms=\"${public_syms}\"\n  wrap_syms=\"${wrap_syms}\"\n  SYM_PREFIX=\"${SYM_PREFIX}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/public_namespace.h], [\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  \"${srcdir}/include/jemalloc/internal/public_namespace.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" > \"${objroot}include/jemalloc/internal/public_namespace.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/public_unnamespace.h], [\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  \"${srcdir}/include/jemalloc/internal/public_unnamespace.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" > \"${objroot}include/jemalloc/internal/public_unnamespace.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/size_classes.h], [\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  \"${SHELL}\" \"${srcdir}/include/jemalloc/internal/size_classes.sh\" \"${LG_QUANTA}\" 3 \"${LG_PAGE_SIZES}\" 2 > \"${objroot}include/jemalloc/internal/size_classes.h\"\n], [\n  SHELL=\"${SHELL}\"\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  LG_QUANTA=\"${LG_QUANTA}\"\n  LG_PAGE_SIZES=\"${LG_PAGE_SIZES}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_protos_jet.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  cat \"${srcdir}/include/jemalloc/jemalloc_protos.h.in\" | sed -e 's/@je_@/jet_/g' > \"${objroot}include/jemalloc/jemalloc_protos_jet.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_rename.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc_rename.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" > \"${objroot}include/jemalloc/jemalloc_rename.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_mangle.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc_mangle.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" je_ > \"${objroot}include/jemalloc/jemalloc_mangle.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_mangle_jet.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc_mangle.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" jet_ > \"${objroot}include/jemalloc/jemalloc_mangle_jet.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc.sh\" \"${objroot}\" > \"${objroot}include/jemalloc/jemalloc${install_suffix}.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  install_suffix=\"${install_suffix}\"\n])\n\ndnl Process .in files.\nAC_SUBST([cfghdrs_in])\nAC_SUBST([cfghdrs_out])\nAC_CONFIG_HEADERS([$cfghdrs_tup])\n\ndnl ============================================================================\ndnl Generate outputs.\n\nAC_CONFIG_FILES([$cfgoutputs_tup config.stamp bin/jemalloc-config bin/jemalloc.sh bin/jeprof])\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([CONFIG             : ${CONFIG}])\nAC_MSG_RESULT([CC                 : ${CC}])\nAC_MSG_RESULT([CONFIGURE_CFLAGS   : ${CONFIGURE_CFLAGS}])\nAC_MSG_RESULT([SPECIFIED_CFLAGS   : ${SPECIFIED_CFLAGS}])\nAC_MSG_RESULT([EXTRA_CFLAGS       : ${EXTRA_CFLAGS}])\nAC_MSG_RESULT([CPPFLAGS           : ${CPPFLAGS}])\nAC_MSG_RESULT([CXX                : ${CXX}])\nAC_MSG_RESULT([CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}])\nAC_MSG_RESULT([SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}])\nAC_MSG_RESULT([EXTRA_CXXFLAGS     : ${EXTRA_CXXFLAGS}])\nAC_MSG_RESULT([LDFLAGS            : ${LDFLAGS}])\nAC_MSG_RESULT([EXTRA_LDFLAGS      : ${EXTRA_LDFLAGS}])\nAC_MSG_RESULT([DSO_LDFLAGS        : ${DSO_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([DATADIR            : ${DATADIR}])\nAC_MSG_RESULT([INCLUDEDIR         : ${INCLUDEDIR}])\nAC_MSG_RESULT([LIBDIR             : ${LIBDIR}])\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([malloc_conf        : ${config_malloc_conf}])\nAC_MSG_RESULT([autogen            : ${enable_autogen}])\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([fill               : ${enable_fill}])\nAC_MSG_RESULT([utrace             : ${enable_utrace}])\nAC_MSG_RESULT([xmalloc            : ${enable_xmalloc}])\nAC_MSG_RESULT([log                : ${enable_log}])\nAC_MSG_RESULT([lazy_lock          : ${enable_lazy_lock}])\nAC_MSG_RESULT([cache-oblivious    : ${enable_cache_oblivious}])\nAC_MSG_RESULT([cxx                : ${enable_cxx}])\nAC_MSG_RESULT([===============================================================================])\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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:output method=\"xml\" encoding=\"utf-8\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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>mallocx</refname>\n    <refname>rallocx</refname>\n    <refname>xallocx</refname>\n    <refname>sallocx</refname>\n    <refname>dallocx</refname>\n    <refname>sdallocx</refname>\n    <refname>nallocx</refname>\n    <refname>mallctl</refname>\n    <refname>mallctlnametomib</refname>\n    <refname>mallctlbymib</refname>\n    <refname>malloc_stats_print</refname>\n    <refname>malloc_usable_size</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://jemalloc.net/\">jemalloc website</ulink>.</para>\n  </refsect1>\n  <refsynopsisdiv>\n    <title>SYNOPSIS</title>\n    <funcsynopsis>\n      <funcsynopsisinfo>#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>void *<function>mallocx</function></funcdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void *<function>rallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>size_t <function>xallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</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>size_t <function>sallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>dallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>sdallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>size_t <function>nallocx</function></funcdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</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_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>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_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    </funcsynopsis>\n  </refsynopsisdiv>\n  <refsect1 id=\"description\">\n    <title>DESCRIPTION</title>\n    <refsect2>\n      <title>Standard API</title>\n\n      <para>The <function>malloc()</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()</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()</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()</function> function\n      allocates <parameter>size</parameter> bytes of memory such that the\n      allocation's base address is a 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 as\n      <code language=\"C\">sizeof(<type>void *</type>)</code>.</para>\n\n      <para>The <function>aligned_alloc()</function> function\n      allocates <parameter>size</parameter> bytes of memory such that the\n      allocation's base address is a 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()</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()</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()</function> function behaves identically to\n      <function>malloc()</function> for the specified size.</para>\n\n      <para>The <function>free()</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      <para>The <function>mallocx()</function>,\n      <function>rallocx()</function>,\n      <function>xallocx()</function>,\n      <function>sallocx()</function>,\n      <function>dallocx()</function>,\n      <function>sdallocx()</function>, and\n      <function>nallocx()</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 id=\"MALLOCX_LG_ALIGN\">\n            <term><constant>MALLOCX_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 id=\"MALLOCX_ALIGN\">\n            <term><constant>MALLOCX_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 id=\"MALLOCX_ZERO\">\n            <term><constant>MALLOCX_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 macro is\n            absent, newly allocated memory is uninitialized.</para></listitem>\n          </varlistentry>\n          <varlistentry id=\"MALLOCX_TCACHE\">\n            <term><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)\n            </constant></term>\n\n            <listitem><para>Use the thread-specific cache (tcache) specified by\n            the identifier <parameter>tc</parameter>, which must have been\n            acquired via the <link\n            linkend=\"tcache.create\"><mallctl>tcache.create</mallctl></link>\n            mallctl.  This macro does not validate that\n            <parameter>tc</parameter> specifies a valid\n            identifier.</para></listitem>\n          </varlistentry>\n          <varlistentry id=\"MALLOC_TCACHE_NONE\">\n            <term><constant>MALLOCX_TCACHE_NONE</constant></term>\n\n            <listitem><para>Do not use a thread-specific cache (tcache).  Unless\n            <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant> or\n            <constant>MALLOCX_TCACHE_NONE</constant> is specified, an\n            automatically managed tcache will be used under many circumstances.\n            This macro cannot be used in the same <parameter>flags</parameter>\n            argument as\n            <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant>.</para></listitem>\n          </varlistentry>\n          <varlistentry id=\"MALLOCX_ARENA\">\n            <term><constant>MALLOCX_ARENA(<parameter>a</parameter>)\n            </constant></term>\n\n            <listitem><para>Use the arena specified by the index\n            <parameter>a</parameter>.  This macro has no effect for regions that\n            were allocated via an arena other than the one specified.  This\n            macro does not validate that <parameter>a</parameter> specifies an\n            arena index in the valid range.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>mallocx()</function> function allocates at\n      least <parameter>size</parameter> bytes of memory, and returns a pointer\n      to the base address of the allocation.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n\n      <para>The <function>rallocx()</function> function resizes the\n      allocation at <parameter>ptr</parameter> to be at least\n      <parameter>size</parameter> bytes, and returns a pointer to the base\n      address of the resulting allocation, which may or may not have moved from\n      its original location.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n\n      <para>The <function>xallocx()</function> function resizes the\n      allocation at <parameter>ptr</parameter> in place to be at least\n      <parameter>size</parameter> bytes, and returns the real size of the\n      allocation.  If <parameter>extra</parameter> is non-zero, an attempt is\n      made to resize 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 to resize.\n      Behavior is undefined if <parameter>size</parameter> is\n      <constant>0</constant>, or if <code\n      language=\"C\">(<parameter>size</parameter> + <parameter>extra</parameter>\n      &gt; <constant>SIZE_T_MAX</constant>)</code>.</para>\n\n      <para>The <function>sallocx()</function> function returns the\n      real size of the allocation at <parameter>ptr</parameter>.</para>\n\n      <para>The <function>dallocx()</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>sdallocx()</function> function is an\n      extension of <function>dallocx()</function> with a\n      <parameter>size</parameter> parameter to allow the caller to pass in the\n      allocation size as an optimization.  The minimum valid input size is the\n      original requested size of the allocation, and the maximum valid input\n      size is the corresponding value returned by\n      <function>nallocx()</function> or\n      <function>sallocx()</function>.</para>\n\n      <para>The <function>nallocx()</function> function allocates no\n      memory, but it performs the same size computation as the\n      <function>mallocx()</function> function, and returns the real\n      size of the allocation that would result from the equivalent\n      <function>mallocx()</function> function call, or\n      <constant>0</constant> if the inputs exceed the maximum supported size\n      class and/or alignment.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n\n      <para>The <function>mallctl()</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()</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 <quote>Management Information Base</quote> (MIB) that can be passed\n      repeatedly to <function>mallctlbymib()</function>.  Upon\n      successful return from <function>mallctlnametomib()</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;\nsize_t mib[4];\nsize_t len, miblen;\n\nlen = sizeof(nbins);\nmallctl(\"arenas.nbins\", &nbins, &len, NULL, 0);\n\nmiblen = 4;\nmallctlnametomib(\"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, (void *)&bin_size, &len, NULL, 0);\n\t/* Do something with bin_size... */\n}]]></programlisting></para>\n\n      <varlistentry id=\"malloc_stats_print_opts\">\n      </varlistentry>\n      <para>The <function>malloc_stats_print()</function> function writes\n      summary statistics via the <parameter>write_cb</parameter> callback\n      function pointer and <parameter>cbopaque</parameter> data passed to\n      <parameter>write_cb</parameter>, or <function>malloc_message()</function>\n      if <parameter>write_cb</parameter> is <constant>NULL</constant>.  The\n      statistics are presented in human-readable form unless <quote>J</quote> is\n      specified as a character within the <parameter>opts</parameter> string, in\n      which case the statistics are presented in <ulink\n      url=\"http://www.json.org/\">JSON format</ulink>.  This function can be\n      called repeatedly.  General information that never changes during\n      execution can be omitted by specifying <quote>g</quote> as a character\n      within the <parameter>opts</parameter> string.  Note that\n      <function>malloc_message()</function> uses the\n      <function>mallctl*()</function> functions internally, so inconsistent\n      statistics can be reported if multiple threads use these functions\n      simultaneously.  If <option>--enable-stats</option> is specified during\n      configuration, <quote>m</quote>, <quote>d</quote>, and <quote>a</quote>\n      can be specified to omit merged arena, destroyed merged arena, and per\n      arena statistics, respectively; <quote>b</quote> and <quote>l</quote> can\n      be specified to omit per size class statistics for bins and large objects,\n      respectively; <quote>x</quote> can be specified to omit all mutex\n      statistics.  Unrecognized characters are silently ignored.  Note that\n      thread caching may prevent some statistics from being completely up to\n      date, since extra locking would be required to merge counters that track\n      thread cache operations.</para>\n\n      <para>The <function>malloc_usable_size()</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()</function> function is not a\n      mechanism for in-place <function>realloc()</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()</function> should not be\n      depended on, since such behavior is entirely implementation-dependent.\n      </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 specified via <option>--with-malloc-conf</option>, the\n    string pointed to by the global variable <varname>malloc_conf</varname>, the\n    <quote>name</quote> of the file referenced by the symbolic link named\n    <filename 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.  Note that\n    <varname>malloc_conf</varname> may be read before\n    <function>main()</function> is entered, so the declaration of\n    <varname>malloc_conf</varname> should specify an initializer that contains\n    the final value to be read by jemalloc.  <option>--with-malloc-conf</option>\n    and <varname>malloc_conf</varname> are compile-time mechanisms, whereas\n    <filename class=\"symlink\">/etc/malloc.conf</filename> and\n    <envar>MALLOC_CONF</envar> can be safely set any time prior to program\n    invocation.</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    <citerefentry><refentrytitle>sbrk</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry> is supported by the operating\n    system, this allocator uses both\n    <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, this allocator supports\n    thread-specific caching, in order to make it possible to completely avoid\n    synchronization for most allocation requests.  Such caching allows very fast\n    allocation in the common case, but it increases memory usage and\n    fragmentation, since a bounded number of objects can remain allocated in\n    each thread cache.</para>\n\n    <para>Memory is conceptually broken into extents.  Extents are always\n    aligned to multiples of the page size.  This alignment makes it possible to\n    find metadata for user objects quickly.  User objects are broken into two\n    categories according to size: small and large.  Contiguous small objects\n    comprise a slab, which resides within a single extent, whereas large objects\n    each have their own extents backing them.</para>\n\n    <para>Small objects are managed in groups by slabs.  Each slab maintains\n    a bitmap to track which regions are in use.  Allocation requests that are no\n    more than half the quantum (8 or 16, depending on architecture) are rounded\n    up to the nearest power of two that is at least <code\n    language=\"C\">sizeof(<type>double</type>)</code>.  All other object size\n    classes are multiples of the quantum, spaced such that there are four size\n    classes for each doubling in size, which limits internal fragmentation to\n    approximately 20% for all but the smallest size classes.  Small size classes\n    are smaller than four times the page size, and large size classes extend\n    from four times the page size up to the largest size class that does not\n    exceed <constant>PTRDIFF_MAX</constant>.</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>The <function>realloc()</function>,\n    <function>rallocx()</function>, and\n    <function>xallocx()</function> functions may resize allocations\n    without moving them under limited circumstances.  Unlike the\n    <function>*allocx()</function> API, the standard API does not\n    officially round up the usable size of an allocation to the nearest size\n    class, so technically it is necessary to call\n    <function>realloc()</function> to grow e.g. a 9-byte allocation to\n    16 bytes, or shrink a 16-byte allocation to 9 bytes.  Growth and shrinkage\n    trivially succeeds in place as long as the pre-size and post-size both round\n    up to the same size class.  No other API guarantees are made regarding\n    in-place resizing, but the current implementation also tries to resize large\n    allocations in place, as long as the pre-size and post-size are both large.\n    For shrinkage to succeed, the extent allocator must support splitting (see\n    <link\n    linkend=\"arena.i.extent_hooks\"><mallctl>arena.&lt;i&gt;.extent_hooks</mallctl></link>).\n    Growth only succeeds if the trailing memory is currently available, and the\n    extent allocator supports merging.</para>\n\n    <para>Assuming 4 KiB pages and a 16-byte quantum on a 64-bit system, the\n    size classes in each category are as shown in <xref linkend=\"size_classes\"\n    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=\"8\">Small</entry>\n          <entry>lg</entry>\n          <entry>[8]</entry>\n        </row>\n        <row>\n          <entry>16</entry>\n          <entry>[16, 32, 48, 64, 80, 96, 112, 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, 4096]</entry>\n        </row>\n        <row>\n          <entry>1 KiB</entry>\n          <entry>[5 KiB, 6 KiB, 7 KiB, 8 KiB]</entry>\n        </row>\n        <row>\n          <entry>2 KiB</entry>\n          <entry>[10 KiB, 12 KiB, 14 KiB]</entry>\n        </row>\n        <row>\n          <entry morerows=\"15\">Large</entry>\n          <entry>2 KiB</entry>\n          <entry>[16 KiB]</entry>\n        </row>\n        <row>\n          <entry>4 KiB</entry>\n          <entry>[20 KiB, 24 KiB, 28 KiB, 32 KiB]</entry>\n        </row>\n        <row>\n          <entry>8 KiB</entry>\n          <entry>[40 KiB, 48 KiB, 54 KiB, 64 KiB]</entry>\n        </row>\n        <row>\n          <entry>16 KiB</entry>\n          <entry>[80 KiB, 96 KiB, 112 KiB, 128 KiB]</entry>\n        </row>\n        <row>\n          <entry>32 KiB</entry>\n          <entry>[160 KiB, 192 KiB, 224 KiB, 256 KiB]</entry>\n        </row>\n        <row>\n          <entry>64 KiB</entry>\n          <entry>[320 KiB, 384 KiB, 448 KiB, 512 KiB]</entry>\n        </row>\n        <row>\n          <entry>128 KiB</entry>\n          <entry>[640 KiB, 768 KiB, 896 KiB, 1 MiB]</entry>\n        </row>\n        <row>\n          <entry>256 KiB</entry>\n          <entry>[1280 KiB, 1536 KiB, 1792 KiB, 2 MiB]</entry>\n        </row>\n        <row>\n          <entry>512 KiB</entry>\n          <entry>[2560 KiB, 3 MiB, 3584 KiB, 4 MiB]</entry>\n        </row>\n        <row>\n          <entry>1 MiB</entry>\n          <entry>[5 MiB, 6 MiB, 7 MiB, 8 MiB]</entry>\n        </row>\n        <row>\n          <entry>2 MiB</entry>\n          <entry>[10 MiB, 12 MiB, 14 MiB, 16 MiB]</entry>\n        </row>\n        <row>\n          <entry>4 MiB</entry>\n          <entry>[20 MiB, 24 MiB, 28 MiB, 32 MiB]</entry>\n        </row>\n        <row>\n          <entry>8 MiB</entry>\n          <entry>[40 MiB, 48 MiB, 56 MiB, 64 MiB]</entry>\n        </row>\n        <row>\n          <entry>...</entry>\n          <entry>...</entry>\n        </row>\n        <row>\n          <entry>512 PiB</entry>\n          <entry>[2560 PiB, 3 EiB, 3584 PiB, 4 EiB]</entry>\n        </row>\n        <row>\n          <entry>1 EiB</entry>\n          <entry>[5 EiB, 6 EiB, 7 EiB]</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*()</function> functions.  Value types are specified in\n    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    and <mallctl>arena.&lt;i&gt;.{initialized,purge,decay,dss}</mallctl>,\n    <literal>&lt;i&gt;</literal> equal to\n    <constant>MALLCTL_ARENAS_ALL</constant> can be used to operate on all arenas\n    or access the summation of statistics from all arenas; similarly\n    <literal>&lt;i&gt;</literal> equal to\n    <constant>MALLCTL_ARENAS_DESTROYED</constant> can be used to access the\n    summation of statistics from all destroyed arenas.  These constants can be\n    utilized either via <function>mallctlnametomib()</function> followed by\n    <function>mallctlbymib()</function>, or via code such as the following:\n    <programlisting language=\"C\"><![CDATA[\n#define STRINGIFY_HELPER(x) #x\n#define STRINGIFY(x) STRINGIFY_HELPER(x)\n\nmallctl(\"arena.\" STRINGIFY(MALLCTL_ARENAS_ALL) \".decay\",\n    NULL, NULL, NULL, 0);]]></programlisting>\n    Take special note of the <link\n    linkend=\"epoch\"><mallctl>epoch</mallctl></link> mallctl, which controls\n    refreshing of cached dynamic statistics.</para>\n\n    <variablelist>\n      <varlistentry id=\"version\">\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*()</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 id=\"background_thread\">\n        <term>\n          <mallctl>background_thread</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Enable/disable internal background worker threads.  When\n        set to true, background threads are created on demand (the number of\n        background threads will be no more than the number of CPUs or active\n        arenas).  Threads run periodically, and handle <link\n        linkend=\"arena.i.decay\">purging</link> asynchronously.  When switching\n        off, background threads are terminated synchronously.  Note that after\n        <citerefentry><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry>\n        function, the state in the child process will be disabled regardless\n        the state in parent process. See <link\n        linkend=\"stats.background_thread.num_threads\"><mallctl>stats.background_thread</mallctl></link>\n        for related stats.  <link\n        linkend=\"opt.background_thread\"><mallctl>opt.background_thread</mallctl></link>\n        can be used to set the default option.  This option is only available on\n        selected pthread-based platforms.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"max_background_threads\">\n        <term>\n          <mallctl>max_background_threads</mallctl>\n          (<type>size_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Maximum number of background worker threads that will\n        be created.  This value is capped at <link\n        linkend=\"opt.max_background_threads\"><mallctl>opt.max_background_threads</mallctl></link> at\n        startup.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"config.cache_oblivious\">\n        <term>\n          <mallctl>config.cache_oblivious</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-cache-oblivious</option> was specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"config.debug\">\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 id=\"config.fill\">\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 id=\"config.lazy_lock\">\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 id=\"config.malloc_conf\">\n        <term>\n          <mallctl>config.malloc_conf</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Embedded configure-time-specified run-time options\n        string, empty unless <option>--with-malloc-conf</option> was specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"config.prof\">\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 id=\"config.prof_libgcc\">\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 id=\"config.prof_libunwind\">\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 id=\"config.stats\">\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\n      <varlistentry id=\"config.utrace\">\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 id=\"config.xmalloc\">\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.  Note that runtime option warnings are not included\n        (see <link\n        linkend=\"opt.abort_conf\"><mallctl>opt.abort_conf</mallctl></link> for\n        that). 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.abort_conf\">\n        <term>\n          <mallctl>opt.abort_conf</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Abort-on-invalid-configuration enabled/disabled.  If\n        true, invalid runtime options 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.metadata_thp\">\n        <term>\n          <mallctl>opt.metadata_thp</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Controls whether to allow jemalloc to use transparent\n        huge page (THP) for internal metadata (see <link\n        linkend=\"stats.metadata\">stats.metadata</link>).  <quote>always</quote>\n        allows such usage.  <quote>auto</quote> uses no THP initially, but may\n        begin to do so when metadata usage reaches certain level.  The default\n        is <quote>disabled</quote>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.retain\">\n        <term>\n          <mallctl>opt.retain</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>If true, retain unused virtual memory for later reuse\n        rather than discarding it by calling\n        <citerefentry><refentrytitle>munmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> or equivalent (see <link\n        linkend=\"stats.retained\">stats.retained</link> for related details).\n        This option is disabled by default unless discarding virtual memory is\n        known to trigger\n        platform-specific performance problems, e.g. for [64-bit] Linux, which\n        has a quirk in its virtual memory allocation algorithm that causes\n        semi-permanent VM map holes under normal jemalloc operation.  Although\n        <citerefentry><refentrytitle>munmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> causes issues on 32-bit Linux as\n        well, retaining virtual memory for 32-bit Linux is disabled by default\n        due to the practical possibility of address space exhaustion.\n        </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 if\n        <citerefentry><refentrytitle>sbrk</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> is supported by the operating\n        system: <quote>disabled</quote>, <quote>primary</quote>, and\n        <quote>secondary</quote>; otherwise only <quote>disabled</quote> is\n        supported.  The default is <quote>secondary</quote> if\n        <citerefentry><refentrytitle>sbrk</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> is supported by the operating\n        system; <quote>disabled</quote> otherwise.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.narenas\">\n        <term>\n          <mallctl>opt.narenas</mallctl>\n          (<type>unsigned</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.percpu_arena\">\n        <term>\n          <mallctl>opt.percpu_arena</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Per CPU arena mode.  Use the <quote>percpu</quote>\n        setting to enable this feature, which uses number of CPUs to determine\n        number of arenas, and bind threads to arenas dynamically based on the\n        CPU the thread runs on currently.  <quote>phycpu</quote> setting uses\n        one arena per physical CPU, which means the two hyper threads on the\n        same CPU share one arena.  Note that no runtime checking regarding the\n        availability of hyper threading is done at the moment.  When set to\n        <quote>disabled</quote>, narenas and thread to arena association will\n        not be impacted by this option.  The default is <quote>disabled</quote>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.background_thread\">\n        <term>\n          <mallctl>opt.background_thread</mallctl>\n          (<type>const bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Internal background worker threads enabled/disabled.\n        Because of potential circular dependencies, enabling background thread\n        using this option may cause crash or deadlock during initialization. For\n        a reliable way to use this feature, see <link\n        linkend=\"background_thread\">background_thread</link> for dynamic control\n        options and details.  This option is disabled by\n        default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.max_background_threads\">\n        <term>\n          <mallctl>opt.max_background_threads</mallctl>\n          (<type>const size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum number of background threads that will be created\n        if <link linkend=\"background_thread\">background_thread</link> is set.\n        Defaults to number of cpus.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.dirty_decay_ms\">\n        <term>\n          <mallctl>opt.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused dirty pages until an equivalent set of unused dirty pages\n        is purged (i.e. converted to muzzy via e.g.\n        <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>\n        if supported by the operating system, or converted to clean otherwise)\n        and/or reused.  Dirty pages are defined as previously having been\n        potentially written to by the application, and therefore consuming\n        physical memory, yet having no current use.  The pages are incrementally\n        purged according to a sigmoidal decay curve that starts and ends with\n        zero purge rate.  A decay time of 0 causes all unused dirty pages to be\n        purged immediately upon creation.  A decay time of -1 disables purging.\n        The default decay time is 10 seconds.  See <link\n        linkend=\"arenas.dirty_decay_ms\"><mallctl>arenas.dirty_decay_ms</mallctl></link>\n        and <link\n        linkend=\"arena.i.dirty_decay_ms\"><mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl></link>\n        for related dynamic control options.  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for a description of muzzy pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.muzzy_decay_ms\">\n        <term>\n          <mallctl>opt.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused muzzy pages until an equivalent set of unused muzzy pages\n        is purged (i.e. converted to clean) and/or reused.  Muzzy pages are\n        defined as previously having been unused dirty pages that were\n        subsequently purged in a manner that left them subject to the\n        reclamation whims of the operating system (e.g.\n        <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>),\n        and therefore in an indeterminate state.  The pages are incrementally\n        purged according to a sigmoidal decay curve that starts and ends with\n        zero purge rate.  A decay time of 0 causes all unused muzzy pages to be\n        purged immediately upon creation.  A decay time of -1 disables purging.\n        The default decay time is 10 seconds.  See <link\n        linkend=\"arenas.muzzy_decay_ms\"><mallctl>arenas.muzzy_decay_ms</mallctl></link>\n        and <link\n        linkend=\"arena.i.muzzy_decay_ms\"><mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl></link>\n        for related dynamic control options.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_extent_max_active_fit\">\n        <term>\n          <mallctl>opt.lg_extent_max_active_fit</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>When reusing dirty extents, this determines the (log\n        base 2 of the) maximum ratio between the size of the active extent\n        selected (to split off from) and the size of the requested allocation.\n        This prevents the splitting of large active extents for smaller\n        allocations, which can reduce fragmentation over the long run\n        (especially for non-active extents).  Lower value may reduce\n        fragmentation, at the cost of extra active extents.  The default value\n        is 6, which gives a maximum ratio of 64 (2^6).</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()</function>\n        function is called at program exit via an\n        <citerefentry><refentrytitle>atexit</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> function.  <link\n        linkend=\"opt.stats_print_opts\"><mallctl>opt.stats_print_opts</mallctl></link>\n        can be combined to specify output options. 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.  Furthermore, <function>atexit()</function> may\n        allocate memory during application initialization and then deadlock\n        internally when jemalloc in turn calls\n        <function>atexit()</function>, so this option is not\n        universally usable (though the application can register its own\n        <function>atexit()</function> function with equivalent\n        functionality).  Therefore, this option should only be used with care;\n        it is 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.stats_print_opts\">\n        <term>\n          <mallctl>opt.stats_print_opts</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Options (the <parameter>opts</parameter> string) to pass\n        to the <function>malloc_stats_print()</function> at exit (enabled\n        through <link\n        linkend=\"opt.stats_print\"><mallctl>opt.stats_print</mallctl></link>). See\n        available options in <link\n        linkend=\"malloc_stats_print_opts\"><function>malloc_stats_print()</function></link>.\n        Has no effect unless <link\n        linkend=\"opt.stats_print\"><mallctl>opt.stats_print</mallctl></link> is\n        enabled.  The default is <quote></quote>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.junk\">\n        <term>\n          <mallctl>opt.junk</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n          [<option>--enable-fill</option>]\n        </term>\n        <listitem><para>Junk filling.  If set to <quote>alloc</quote>, each byte\n        of uninitialized allocated memory will be initialized to\n        <literal>0xa5</literal>.  If set to <quote>free</quote>, all deallocated\n        memory will be initialized to <literal>0x5a</literal>.  If set to\n        <quote>true</quote>, both allocated and deallocated memory will be\n        initialized, and if set to <quote>false</quote>, junk filling be\n        disabled entirely.  This is intended for debugging and will impact\n        performance negatively.  This option is <quote>false</quote> by default\n        unless <option>--enable-debug</option> is specified during\n        configuration, in which case it is <quote>true</quote> by\n        default.</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()</function> and\n        <function>rallocx()</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.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        </term>\n        <listitem><para>Thread-specific caching (tcache) enabled/disabled.  When\n        there are multiple threads, each thread uses a tcache for objects up to\n        a certain size.  Thread-specific caching allows many allocations to be\n        satisfied without performing any thread synchronization, at the cost of\n        increased memory use.  See the <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.</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        </term>\n        <listitem><para>Maximum size class (log base 2) to cache in the\n        thread-specific cache (tcache).  At a minimum, all small size classes\n        are 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.thp\">\n        <term>\n          <mallctl>opt.thp</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Transparent hugepage (THP) mode. Settings \"always\",\n        \"never\" and \"default\" are available if THP is supported by the operating\n        system.  The \"always\" setting enables transparent hugepage for all user\n        memory mappings with\n        <parameter><constant>MADV_HUGEPAGE</constant></parameter>; \"never\"\n        ensures no transparent hugepage with\n        <parameter><constant>MADV_NOHUGEPAGE</constant></parameter>; the default\n        setting \"default\" makes no changes.  Note that: this option does not\n        affect THP for jemalloc internal metadata (see <link\n        linkend=\"opt.metadata_thp\"><mallctl>opt.metadata_thp</mallctl></link>);\n        in addition, for arenas with customized <link\n        linkend=\"arena.i.extent_hooks\"><mallctl>extent_hooks</mallctl></link>,\n        this option is bypassed as it is implemented as part of the default\n        extent hooks.</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 <command>jeprof</command> command, which is based on the\n        <command>pprof</command> that is developed as part of the <ulink\n        url=\"http://code.google.com/p/gperftools/\">gperftools\n        package</ulink>.  See <link linkend=\"heap_profile_format\">HEAP PROFILE\n        FORMAT</link> for heap profile format documentation.</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.prof_thread_active_init\">\n        <term>\n          <mallctl>opt.prof_thread_active_init</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Initial setting for <link\n        linkend=\"thread.prof.active\"><mallctl>thread.prof.active</mallctl></link>\n        in newly created threads.  The initial setting for newly created threads\n        can also be changed during execution via the <link\n        linkend=\"prof.thread_active_init\"><mallctl>prof.thread_active_init</mallctl></link>\n        mallctl.  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>size_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>Set the initial state of <link\n        linkend=\"prof.gdump\"><mallctl>prof.gdump</mallctl></link>, which when\n        enabled triggers a memory profile dump every time the total virtual\n        memory exceeds the previous maximum.  This option is disabled by\n        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.  Note that <function>atexit()</function> may allocate\n        memory during application initialization and then deadlock internally\n        when jemalloc in turn calls <function>atexit()</function>, so\n        this option is not universally usable (though the application can\n        register its own <function>atexit()</function> function with\n        equivalent functionality).  This option is disabled by\n        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 id=\"thread.arena\">\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=\"arena.i.initialized\"><mallctl>arena.i.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 id=\"thread.allocatedp\">\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*()</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 id=\"thread.deallocatedp\">\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*()</function> calls.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.tcache.enabled\">\n        <term>\n          <mallctl>thread.tcache.enabled</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\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        linkend=\"thread.tcache.flush\"><mallctl>thread.tcache.flush</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.tcache.flush\">\n        <term>\n          <mallctl>thread.tcache.flush</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Flush calling thread's thread-specific cache (tcache).\n        This interface releases all cached objects and internal data structures\n        associated with the calling thread's tcache.  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=\"thread.prof.name\">\n        <term>\n          <mallctl>thread.prof.name</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal> or\n          <literal>-w</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Get/set the descriptive name associated with the calling\n        thread in memory profile dumps.  An internal copy of the name string is\n        created, so the input string need not be maintained after this interface\n        completes execution.  The output string of this interface should be\n        copied for non-ephemeral uses, because multiple implementation details\n        can cause asynchronous string deallocation.  Furthermore, each\n        invocation of this interface can only read or write; simultaneous\n        read/write is not supported due to string lifetime limitations.  The\n        name string must be nil-terminated and comprised only of characters in\n        the sets recognized\n        by <citerefentry><refentrytitle>isgraph</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> and\n        <citerefentry><refentrytitle>isblank</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.prof.active\">\n        <term>\n          <mallctl>thread.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 for the\n        calling thread.  This is an activation mechanism in addition to <link\n        linkend=\"prof.active\"><mallctl>prof.active</mallctl></link>; both must\n        be active for the calling thread to sample.  This flag is enabled by\n        default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"tcache.create\">\n        <term>\n          <mallctl>tcache.create</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Create an explicit thread-specific cache (tcache) and\n        return an identifier that can be passed to the <link\n        linkend=\"MALLOCX_TCACHE\"><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant></link>\n        macro to explicitly use the specified cache rather than the\n        automatically managed one that is used by default.  Each explicit cache\n        can be used by only one thread at a time; the application must assure\n        that this constraint holds.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"tcache.flush\">\n        <term>\n          <mallctl>tcache.flush</mallctl>\n          (<type>unsigned</type>)\n          <literal>-w</literal>\n        </term>\n        <listitem><para>Flush the specified thread-specific cache (tcache).  The\n        same considerations apply to this interface as to <link\n        linkend=\"thread.tcache.flush\"><mallctl>thread.tcache.flush</mallctl></link>,\n        except that the tcache will never be automatically discarded.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"tcache.destroy\">\n        <term>\n          <mallctl>tcache.destroy</mallctl>\n          (<type>unsigned</type>)\n          <literal>-w</literal>\n        </term>\n        <listitem><para>Flush the specified thread-specific cache (tcache) and\n        make the identifier available for use during a future tcache creation.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.initialized\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.initialized</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Get whether the specified arena's statistics are\n        initialized (i.e. the arena was initialized prior to the current epoch).\n        This interface can also be nominally used to query whether the merged\n        statistics corresponding to <constant>MALLCTL_ARENAS_ALL</constant> are\n        initialized (always true).</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.decay\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.decay</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Trigger decay-based purging of unused dirty/muzzy pages\n        for arena &lt;i&gt;, or for all arenas if &lt;i&gt; equals\n        <constant>MALLCTL_ARENAS_ALL</constant>.  The proportion of unused\n        dirty/muzzy pages to be purged depends on the current time; see <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        and <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzy_decay_ms</mallctl></link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.purge\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.purge</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Purge all unused dirty pages for arena &lt;i&gt;, or for\n        all arenas if &lt;i&gt; equals <constant>MALLCTL_ARENAS_ALL</constant>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.reset\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.reset</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Discard all of the arena's extant allocations.  This\n        interface can only be used with arenas explicitly created via <link\n        linkend=\"arenas.create\"><mallctl>arenas.create</mallctl></link>.  None\n        of the arena's discarded/cached allocations may accessed afterward.  As\n        part of this requirement, all thread caches which were used to\n        allocate/deallocate in conjunction with the arena must be flushed\n        beforehand.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.destroy\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.destroy</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Destroy the arena.  Discard all of the arena's extant\n        allocations using the same mechanism as for <link\n        linkend=\"arena.i.reset\"><mallctl>arena.&lt;i&gt;.reset</mallctl></link>\n        (with all the same constraints and side effects), merge the arena stats\n        into those accessible at arena index\n        <constant>MALLCTL_ARENAS_DESTROYED</constant>, and then completely\n        discard all metadata associated with the arena.  Future calls to <link\n        linkend=\"arenas.create\"><mallctl>arenas.create</mallctl></link> may\n        recycle the arena index.  Destruction will fail if any threads are\n        currently associated with the arena as a result of calls to <link\n        linkend=\"thread.arena\"><mallctl>thread.arena</mallctl></link>.</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        <constant>MALLCTL_ARENAS_ALL</constant>.  See <link\n        linkend=\"opt.dss\"><mallctl>opt.dss</mallctl></link> for supported\n        settings.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.dirty_decay_ms\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current per-arena approximate time in milliseconds from\n        the creation of a set of unused dirty pages until an equivalent set of\n        unused dirty pages is purged and/or reused.  Each time this interface is\n        set, all currently unused dirty pages are considered to have fully\n        decayed, which causes immediate purging of all unused dirty pages unless\n        the decay time is set to -1 (i.e. purging disabled).  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.muzzy_decay_ms\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current per-arena approximate time in milliseconds from\n        the creation of a set of unused muzzy pages until an equivalent set of\n        unused muzzy pages is purged and/or reused.  Each time this interface is\n        set, all currently unused muzzy pages are considered to have fully\n        decayed, which causes immediate purging of all unused muzzy pages unless\n        the decay time is set to -1 (i.e. purging disabled).  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.retain_grow_limit\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.retain_grow_limit</mallctl>\n          (<type>size_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Maximum size to grow retained region (only relevant when\n        <link linkend=\"opt.retain\"><mallctl>opt.retain</mallctl></link> is\n        enabled).  This controls the maximum increment to expand virtual memory,\n        or allocation through <link\n        linkend=\"arena.i.extent_hooks\"><mallctl>arena.&lt;i&gt;extent_hooks</mallctl></link>.\n        In particular, if customized extent hooks reserve physical memory\n        (e.g. 1G huge pages), this is useful to control the allocation hook's\n        input size.  The default is no limit.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.extent_hooks\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.extent_hooks</mallctl>\n          (<type>extent_hooks_t *</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Get or set the extent management hook functions for\n        arena &lt;i&gt;.  The functions must be capable of operating on all\n        extant extents associated with arena &lt;i&gt;, usually by passing\n        unknown extents to the replaced functions.  In practice, it is feasible\n        to control allocation for arenas explicitly created via <link\n        linkend=\"arenas.create\"><mallctl>arenas.create</mallctl></link> such\n        that all extents originate from an application-supplied extent allocator\n        (by specifying the custom extent hook functions during arena creation),\n        but the automatically created arenas will have already created extents\n        prior to the application having an opportunity to take over extent\n        allocation.</para>\n\n        <programlisting language=\"C\"><![CDATA[\ntypedef extent_hooks_s extent_hooks_t;\nstruct extent_hooks_s {\n\textent_alloc_t\t\t*alloc;\n\textent_dalloc_t\t\t*dalloc;\n\textent_destroy_t\t*destroy;\n\textent_commit_t\t\t*commit;\n\textent_decommit_t\t*decommit;\n\textent_purge_t\t\t*purge_lazy;\n\textent_purge_t\t\t*purge_forced;\n\textent_split_t\t\t*split;\n\textent_merge_t\t\t*merge;\n};]]></programlisting>\n        <para>The <type>extent_hooks_t</type> structure comprises function\n        pointers which are described individually below.  jemalloc uses these\n        functions to manage extent lifetime, which starts off with allocation of\n        mapped committed memory, in the simplest case followed by deallocation.\n        However, there are performance and platform reasons to retain extents\n        for later reuse.  Cleanup attempts cascade from deallocation to decommit\n        to forced purging to lazy purging, which gives the extent management\n        functions opportunities to reject the most permanent cleanup operations\n        in favor of less permanent (and often less costly) operations.  All\n        operations except allocation can be universally opted out of by setting\n        the hook pointers to <constant>NULL</constant>, or selectively opted out\n        of by returning failure.  Note that once the extent hook is set, the\n        structure is accessed directly by the associated arenas, so it must\n        remain valid for the entire lifetime of the arenas.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef void *<function>(extent_alloc_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>new_addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>alignment</parameter></paramdef>\n          <paramdef>bool *<parameter>zero</parameter></paramdef>\n          <paramdef>bool *<parameter>commit</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent allocation function conforms to the\n        <type>extent_alloc_t</type> type and upon success returns a pointer to\n        <parameter>size</parameter> bytes of mapped memory on behalf of arena\n        <parameter>arena_ind</parameter> such that the extent's base address is\n        a multiple of <parameter>alignment</parameter>, as well as setting\n        <parameter>*zero</parameter> to indicate whether the extent is zeroed\n        and <parameter>*commit</parameter> to indicate whether the extent is\n        committed.  Upon error the function returns <constant>NULL</constant>\n        and leaves <parameter>*zero</parameter> and\n        <parameter>*commit</parameter> unmodified.  The\n        <parameter>size</parameter> parameter is always a multiple of the page\n        size.  The <parameter>alignment</parameter> parameter is always a power\n        of two at least as large as the page size.  Zeroing is mandatory if\n        <parameter>*zero</parameter> is true upon function entry.  Committing is\n        mandatory if <parameter>*commit</parameter> is true upon function entry.\n        If <parameter>new_addr</parameter> is not <constant>NULL</constant>, the\n        returned pointer must be <parameter>new_addr</parameter> on success or\n        <constant>NULL</constant> on error.  Committed memory may be committed\n        in absolute terms as on a system that does not overcommit, or in\n        implicit terms as on a system that overcommits and satisfies physical\n        memory needs on demand via soft page faults.  Note that replacing the\n        default extent allocation function makes the arena's <link\n        linkend=\"arena.i.dss\"><mallctl>arena.&lt;i&gt;.dss</mallctl></link>\n        setting irrelevant.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_dalloc_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>\n        An extent deallocation function conforms to the\n        <type>extent_dalloc_t</type> type and deallocates an extent at given\n        <parameter>addr</parameter> and <parameter>size</parameter> with\n        <parameter>committed</parameter>/decommited memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>, returning false upon\n        success.  If the function returns true, this indicates opt-out from\n        deallocation; the virtual memory mapping associated with the extent\n        remains mapped, in the same commit state, and available for future use,\n        in which case it will be automatically retained for later reuse.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef void <function>(extent_destroy_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>\n        An extent destruction function conforms to the\n        <type>extent_destroy_t</type> type and unconditionally destroys an\n        extent at given <parameter>addr</parameter> and\n        <parameter>size</parameter> with\n        <parameter>committed</parameter>/decommited memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>.  This function may be\n        called to destroy retained extents during arena destruction (see <link\n        linkend=\"arena.i.destroy\"><mallctl>arena.&lt;i&gt;.destroy</mallctl></link>).</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_commit_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>offset</parameter></paramdef>\n          <paramdef>size_t <parameter>length</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent commit function conforms to the\n        <type>extent_commit_t</type> type and commits zeroed physical memory to\n        back pages within an extent at given <parameter>addr</parameter> and\n        <parameter>size</parameter> at <parameter>offset</parameter> bytes,\n        extending for <parameter>length</parameter> on behalf of arena\n        <parameter>arena_ind</parameter>, returning false upon success.\n        Committed memory may be committed in absolute terms as on a system that\n        does not overcommit, or in implicit terms as on a system that\n        overcommits and satisfies physical memory needs on demand via soft page\n        faults. If the function returns true, this indicates insufficient\n        physical memory to satisfy the request.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_decommit_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>offset</parameter></paramdef>\n          <paramdef>size_t <parameter>length</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent decommit function conforms to the\n        <type>extent_decommit_t</type> type and decommits any physical memory\n        that is backing pages within an extent at given\n        <parameter>addr</parameter> and <parameter>size</parameter> at\n        <parameter>offset</parameter> bytes, extending for\n        <parameter>length</parameter> on behalf of arena\n        <parameter>arena_ind</parameter>, returning false upon success, in which\n        case the pages will be committed via the extent commit function before\n        being reused.  If the function returns true, this indicates opt-out from\n        decommit; the memory remains committed and available for future use, in\n        which case it will be automatically retained for later reuse.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_purge_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>offset</parameter></paramdef>\n          <paramdef>size_t <parameter>length</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent purge function conforms to the\n        <type>extent_purge_t</type> type and discards physical pages\n        within the virtual memory mapping associated with an extent at given\n        <parameter>addr</parameter> and <parameter>size</parameter> at\n        <parameter>offset</parameter> bytes, extending for\n        <parameter>length</parameter> on behalf of arena\n        <parameter>arena_ind</parameter>.  A lazy extent purge function (e.g.\n        implemented via\n        <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>)\n        can delay purging indefinitely and leave the pages within the purged\n        virtual memory range in an indeterminite state, whereas a forced extent\n        purge function immediately purges, and the pages within the virtual\n        memory range will be zero-filled the next time they are accessed.  If\n        the function returns true, this indicates failure to purge.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_split_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>size_a</parameter></paramdef>\n          <paramdef>size_t <parameter>size_b</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent split function conforms to the\n        <type>extent_split_t</type> type and optionally splits an extent at\n        given <parameter>addr</parameter> and <parameter>size</parameter> into\n        two adjacent extents, the first of <parameter>size_a</parameter> bytes,\n        and the second of <parameter>size_b</parameter> bytes, operating on\n        <parameter>committed</parameter>/decommitted memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>, returning false upon\n        success.  If the function returns true, this indicates that the extent\n        remains unsplit and therefore should continue to be operated on as a\n        whole.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_merge_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr_a</parameter></paramdef>\n          <paramdef>size_t <parameter>size_a</parameter></paramdef>\n          <paramdef>void *<parameter>addr_b</parameter></paramdef>\n          <paramdef>size_t <parameter>size_b</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent merge function conforms to the\n        <type>extent_merge_t</type> type and optionally merges adjacent extents,\n        at given <parameter>addr_a</parameter> and <parameter>size_a</parameter>\n        with given <parameter>addr_b</parameter> and\n        <parameter>size_b</parameter> into one contiguous extent, operating on\n        <parameter>committed</parameter>/decommitted memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>, returning false upon\n        success.  If the function returns true, this indicates that the extents\n        remain distinct mappings and therefore should continue to be operated on\n        independently.</para>\n        </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.dirty_decay_ms\">\n        <term>\n          <mallctl>arenas.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current default per-arena approximate time in\n        milliseconds from the creation of a set of unused dirty pages until an\n        equivalent set of unused dirty pages is purged and/or reused, used to\n        initialize <link\n        linkend=\"arena.i.dirty_decay_ms\"><mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl></link>\n        during arena creation.  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.muzzy_decay_ms\">\n        <term>\n          <mallctl>arenas.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current default per-arena approximate time in\n        milliseconds from the creation of a set of unused muzzy pages until an\n        equivalent set of unused muzzy pages is purged and/or reused, used to\n        initialize <link\n        linkend=\"arena.i.muzzy_decay_ms\"><mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl></link>\n        during arena creation.  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.quantum\">\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 id=\"arenas.page\">\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 id=\"arenas.tcache_max\">\n        <term>\n          <mallctl>arenas.tcache_max</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum thread-cached size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.nbins\">\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 id=\"arenas.nhbins\">\n        <term>\n          <mallctl>arenas.nhbins</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\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 id=\"arenas.bin.i.nregs\">\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 slab.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.bin.i.slab_size\">\n        <term>\n          <mallctl>arenas.bin.&lt;i&gt;.slab_size</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of bytes per slab.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.nlextents\">\n        <term>\n          <mallctl>arenas.nlextents</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Total number of large size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.lextent.i.size\">\n        <term>\n          <mallctl>arenas.lextent.&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 id=\"arenas.create\">\n        <term>\n          <mallctl>arenas.create</mallctl>\n          (<type>unsigned</type>, <type>extent_hooks_t *</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Explicitly create a new arena outside the range of\n        automatically managed arenas, with optionally specified extent hooks,\n        and return the new arena index.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.lookup\">\n        <term>\n          <mallctl>arenas.lookup</mallctl>\n          (<type>unsigned</type>, <type>void*</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Index of the arena to which an allocation belongs to.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.thread_active_init\">\n        <term>\n          <mallctl>prof.thread_active_init</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Control the initial setting for <link\n        linkend=\"thread.prof.active\"><mallctl>thread.prof.active</mallctl></link>\n        in newly created threads.  See the <link\n        linkend=\"opt.prof_thread_active_init\"><mallctl>opt.prof_thread_active_init</mallctl></link>\n        option for additional information.</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, as well as the interrelated <link\n        linkend=\"thread.prof.active\"><mallctl>thread.prof.active</mallctl></link>\n        mallctl.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.dump\">\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 id=\"prof.gdump\">\n        <term>\n          <mallctl>prof.gdump</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>When enabled, trigger a memory profile dump every time\n        the total virtual memory exceeds the previous maximum.  Profiles are\n        dumped to 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.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.reset\">\n        <term>\n          <mallctl>prof.reset</mallctl>\n          (<type>size_t</type>)\n          <literal>-w</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Reset all memory profile statistics, and optionally\n        update the sample rate (see <link\n        linkend=\"opt.lg_prof_sample\"><mallctl>opt.lg_prof_sample</mallctl></link>\n        and <link\n        linkend=\"prof.lg_sample\"><mallctl>prof.lg_sample</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.lg_sample\">\n        <term>\n          <mallctl>prof.lg_sample</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Get the current sample rate (see <link\n        linkend=\"opt.lg_prof_sample\"><mallctl>opt.lg_prof_sample</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.interval\">\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        interval-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.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>,\n        <link linkend=\"stats.arenas.i.pmuzzy\">\n        <mallctl>stats.arenas.&lt;i&gt;.pmuzzy</mallctl></link>, nor pages\n        entirely devoted to allocator metadata.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.metadata\">\n        <term>\n          <mallctl>stats.metadata</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Total number of bytes dedicated to metadata, which\n        comprise base allocations used for bootstrap-sensitive allocator\n        metadata structures (see <link\n        linkend=\"stats.arenas.i.base\"><mallctl>stats.arenas.&lt;i&gt;.base</mallctl></link>)\n        and internal allocations (see <link\n        linkend=\"stats.arenas.i.internal\"><mallctl>stats.arenas.&lt;i&gt;.internal</mallctl></link>).\n        Transparent huge page (enabled with <link\n        linkend=\"opt.metadata_thp\">opt.metadata_thp</link>) usage is not\n        considered.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.metadata_thp\">\n        <term>\n          <mallctl>stats.metadata_thp</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of transparent huge pages (THP) used for\n        metadata.  See <link\n        linkend=\"stats.metadata\"><mallctl>stats.metadata</mallctl></link> and\n        <link linkend=\"opt.metadata_thp\">opt.metadata_thp</link>) for\n        details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.resident\">\n        <term>\n          <mallctl>stats.resident</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Maximum number of bytes in physically resident data\n        pages mapped by the allocator, comprising all pages dedicated to\n        allocator metadata, pages backing active allocations, and unused dirty\n        pages.  This is a maximum rather than precise because pages may not\n        actually be physically resident if they correspond to demand-zeroed\n        virtual memory that has not yet been touched.  This is a multiple of the\n        page size, and is larger than <link\n        linkend=\"stats.active\"><mallctl>stats.active</mallctl></link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mapped\">\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 active extents mapped by the\n        allocator.  This is larger than <link\n        linkend=\"stats.active\"><mallctl>stats.active</mallctl></link>.  This\n        does not include inactive extents, even those that contain unused dirty\n        pages, which means that there is no strict ordering between this and\n        <link\n        linkend=\"stats.resident\"><mallctl>stats.resident</mallctl></link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.retained\">\n        <term>\n          <mallctl>stats.retained</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 virtual memory mappings that\n        were retained rather than being returned to the operating system via\n        e.g. <citerefentry><refentrytitle>munmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> or similar.  Retained virtual\n        memory is typically untouched, decommitted, or purged, so it has no\n        strongly associated physical memory (see <link\n        linkend=\"arena.i.extent_hooks\">extent hooks</link> for details).\n        Retained memory is excluded from mapped memory statistics, e.g. <link\n        linkend=\"stats.mapped\"><mallctl>stats.mapped</mallctl></link>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.background_thread.num_threads\">\n        <term>\n          <mallctl>stats.background_thread.num_threads</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para> Number of <link linkend=\"background_thread\">background\n        threads</link> running currently.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.background_thread.num_runs\">\n        <term>\n          <mallctl>stats.background_thread.num_runs</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para> Total number of runs from all <link\n        linkend=\"background_thread\">background threads</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.background_thread.run_interval\">\n        <term>\n          <mallctl>stats.background_thread.run_interval</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para> Average run interval in nanoseconds of <link\n        linkend=\"background_thread\">background threads</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.ctl\">\n        <term>\n          <mallctl>stats.mutexes.ctl.{counter};</mallctl>\n          (<type>counter specific type</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>ctl</varname> mutex (global\n        scope; mallctl related).  <mallctl>{counter}</mallctl> is one of the\n        counters below:</para>\n        <varlistentry id=\"mutex_counters\">\n          <listitem><para><varname>num_ops</varname> (<type>uint64_t</type>):\n          Total number of lock acquisition operations on this mutex.</para>\n\n\t  <para><varname>num_spin_acq</varname> (<type>uint64_t</type>): Number\n\t  of times the mutex was spin-acquired.  When the mutex is currently\n\t  locked and cannot be acquired immediately, a short period of\n\t  spin-retry within jemalloc will be performed.  Acquired through spin\n\t  generally means the contention was lightweight and not causing context\n\t  switches.</para>\n\n\t  <para><varname>num_wait</varname> (<type>uint64_t</type>): Number of\n\t  times the mutex was wait-acquired, which means the mutex contention\n\t  was not solved by spin-retry, and blocking operation was likely\n\t  involved in order to acquire the mutex.  This event generally implies\n\t  higher cost / longer delay, and should be investigated if it happens\n\t  often.</para>\n\n\t  <para><varname>max_wait_time</varname> (<type>uint64_t</type>):\n\t  Maximum length of time in nanoseconds spent on a single wait-acquired\n\t  lock operation.  Note that to avoid profiling overhead on the common\n\t  path, this does not consider spin-acquired cases.</para>\n\n\t  <para><varname>total_wait_time</varname> (<type>uint64_t</type>):\n\t  Cumulative time in nanoseconds spent on wait-acquired lock operations.\n\t  Similarly, spin-acquired cases are not considered.</para>\n\n\t  <para><varname>max_num_thds</varname> (<type>uint32_t</type>): Maximum\n\t  number of threads waiting on this mutex simultaneously.  Similarly,\n\t  spin-acquired cases are not considered.</para>\n\n\t  <para><varname>num_owner_switch</varname> (<type>uint64_t</type>):\n\t  Number of times the current mutex owner is different from the previous\n\t  one.  This event does not generally imply an issue; rather it is an\n\t  indicator of how often the protected data are accessed by different\n\t  threads.\n\t  </para>\n\t  </listitem>\n\t</varlistentry>\n\t</listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.background_thread\">\n        <term>\n          <mallctl>stats.mutexes.background_thread.{counter}</mallctl>\n\t  (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>background_thread</varname> mutex\n        (global scope; <link\n        linkend=\"background_thread\"><mallctl>background_thread</mallctl></link>\n        related).  <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.prof\">\n        <term>\n          <mallctl>stats.mutexes.prof.{counter}</mallctl>\n\t  (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>prof</varname> mutex (global\n        scope; profiling related).  <mallctl>{counter}</mallctl> is one of the\n        counters in <link linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.reset\">\n        <term>\n          <mallctl>stats.mutexes.reset</mallctl>\n\t  (<type>void</type>) <literal>--</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Reset all mutex profile statistics, including global\n        mutexes, arena mutexes and bin mutexes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.dss\">\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 id=\"stats.arenas.i.dirty_decay_ms\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused dirty pages until an equivalent set of unused dirty pages\n        is purged and/or reused.  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_decay_ms\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused muzzy pages until an equivalent set of unused muzzy pages\n        is purged and/or reused.  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.nthreads\">\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 id=\"stats.arenas.i.uptime\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.uptime</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Time elapsed (in nanoseconds) since the arena was\n        created.  If &lt;i&gt; equals <constant>0</constant> or\n        <constant>MALLCTL_ARENAS_ALL</constant>, this is the uptime since malloc\n        initialization.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.pactive\">\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 extents.</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 extents that are\n        potentially dirty, and for which <function>madvise()</function> or\n        similar has not been called.  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for a description of dirty pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.pmuzzy\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.pmuzzy</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of pages within unused extents that are muzzy.\n        See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for a description of muzzy pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mapped\">\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 id=\"stats.arenas.i.retained\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.retained</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of retained bytes.  See <link\n        linkend=\"stats.retained\"><mallctl>stats.retained</mallctl></link> for\n        details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.base\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.base</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>\n        Number of bytes dedicated to bootstrap-sensitive allocator metadata\n        structures.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.internal\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.internal</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of bytes dedicated to internal allocations.\n        Internal allocations differ from application-originated allocations in\n        that they are for internal use, and that they are omitted from heap\n        profiles.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.metadata_thp\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.metadata_thp</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of transparent huge pages (THP) used for\n        metadata.  See <link linkend=\"opt.metadata_thp\">opt.metadata_thp</link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.resident\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.resident</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Maximum number of bytes in physically resident data\n        pages mapped by the arena, comprising all pages dedicated to allocator\n        metadata, pages backing active allocations, and unused dirty pages.\n        This is a maximum rather than precise because pages may not actually be\n        physically resident if they correspond to demand-zeroed virtual memory\n        that has not yet been touched.  This is a multiple of the page\n        size.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.dirty_npurge\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_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 id=\"stats.arenas.i.dirty_nmadvise\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_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()</function> or similar\n        calls made to purge dirty pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.dirty_purged\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_purged</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of dirty pages purged.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_npurge\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_npurge</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of muzzy page purge sweeps performed.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_nmadvise\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_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()</function> or similar\n        calls made to purge muzzy pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_purged\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_purged</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of muzzy pages purged.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.small.allocated\">\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 id=\"stats.arenas.i.small.nmalloc\">\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 times a small allocation was\n        requested from the arena's bins, whether to fill the relevant tcache if\n        <link linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is\n        enabled, or to directly satisfy an allocation request\n        otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.small.ndalloc\">\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 times a small allocation was\n        returned to the arena's bins, whether to flush the relevant tcache if\n        <link linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is\n        enabled, or to directly deallocate an allocation\n        otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.small.nrequests\">\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 allocation requests satisfied by\n        all bin size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.large.allocated\">\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 id=\"stats.arenas.i.large.nmalloc\">\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 times a large extent was allocated\n        from the arena, whether to fill the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly satisfy\n        an allocation request otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.large.ndalloc\">\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 times a large extent was returned\n        to the arena, whether to flush the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly\n        deallocate an allocation otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.large.nrequests\">\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 allocation requests satisfied by\n        all large size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nmalloc\">\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 times a bin region of the\n        corresponding size class was allocated from the arena, whether to fill\n        the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled, or\n        to directly satisfy an allocation request otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.ndalloc\">\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 times a bin region of the\n        corresponding size class was returned to the arena, whether to flush the\n        relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled, or\n        to directly deallocate an allocation otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nrequests\">\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 requests satisfied by\n        bin regions of the corresponding size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.curregs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curregs</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of regions for this size\n        class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nfills\">\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        </term>\n        <listitem><para>Cumulative number of tcache fills.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nflushes\">\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        </term>\n        <listitem><para>Cumulative number of tcache flushes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nslabs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nslabs</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of slabs created.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nreslabs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nreslabs</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 slab from which\n        to allocate changed.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.curslabs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curslabs</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of slabs.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.mutex\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.mutex.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on\n        <varname>arena.&lt;i&gt;.bins.&lt;j&gt;</varname> mutex (arena bin\n        scope; bin operation related).  <mallctl>{counter}</mallctl> is one of\n        the counters in <link linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.nmalloc\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&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 times a large extent of the\n        corresponding size class was allocated from the arena, whether to fill\n        the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly satisfy\n        an allocation request otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.ndalloc\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&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 times a large extent of the\n        corresponding size class was returned to the arena, whether to flush the\n        relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly\n        deallocate an allocation otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.nrequests\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&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 satisfied by\n        large extents of the corresponding size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.curlextents\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&lt;j&gt;.curlextents</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of large allocations for this size class.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.large\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.large.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.large</varname>\n        mutex (arena scope; large allocation related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extent_avail\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extent_avail.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extent_avail\n        </varname> mutex (arena scope; extent avail related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extents_dirty\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_dirty.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_dirty\n        </varname> mutex (arena scope; dirty extents related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extents_muzzy\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_muzzy.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_muzzy\n        </varname> mutex (arena scope; muzzy extents related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extents_retained\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_retained.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_retained\n        </varname> mutex (arena scope; retained extents related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.decay_dirty\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.decay_dirty.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.decay_dirty\n        </varname> mutex (arena scope; decay for dirty pages related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.decay_muzzy\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.decay_muzzy.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.decay_muzzy\n        </varname> mutex (arena scope; decay for muzzy pages related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.base\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.base.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.base</varname>\n        mutex (arena scope; base allocator related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.tcache_list\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.tcache_list.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on\n        <varname>arena.&lt;i&gt;.tcache_list</varname> mutex (arena scope;\n        tcache to arena association related).  This mutex is expected to be\n        accessed less often.  <mallctl>{counter}</mallctl> is one of the\n        counters in <link linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n    </variablelist>\n  </refsect1>\n  <refsect1 id=\"heap_profile_format\">\n    <title>HEAP PROFILE FORMAT</title>\n    <para>Although the heap profiling functionality was originally designed to\n    be compatible with the\n    <command>pprof</command> command that is developed as part of the <ulink\n    url=\"http://code.google.com/p/gperftools/\">gperftools\n    package</ulink>, the addition of per thread heap profiling functionality\n    required a different heap profile format.  The <command>jeprof</command>\n    command is derived from <command>pprof</command>, with enhancements to\n    support the heap profile format described here.</para>\n\n    <para>In the following hypothetical heap profile, <constant>[...]</constant>\n    indicates elision for the sake of compactness.  <programlisting><![CDATA[\nheap_v2/524288\n  t*: 28106: 56637512 [0: 0]\n  [...]\n  t3: 352: 16777344 [0: 0]\n  [...]\n  t99: 17754: 29341640 [0: 0]\n  [...]\n@ 0x5f86da8 0x5f5a1dc [...] 0x29e4d4e 0xa200316 0xabb2988 [...]\n  t*: 13: 6688 [0: 0]\n  t3: 12: 6496 [0: ]\n  t99: 1: 192 [0: 0]\n[...]\n\nMAPPED_LIBRARIES:\n[...]]]></programlisting> The following matches the above heap profile, but most\ntokens are replaced with <constant>&lt;description&gt;</constant> to indicate\ndescriptions of the corresponding fields.  <programlisting><![CDATA[\n<heap_profile_format_version>/<mean_sample_interval>\n  <aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n  [...]\n  <thread_3_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]\n  [...]\n  <thread_99_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]\n  [...]\n@ <top_frame> <frame> [...] <frame> <frame> <frame> [...]\n  <backtrace_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n  <backtrace_thread_3>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n  <backtrace_thread_99>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n[...]\n\nMAPPED_LIBRARIES:\n</proc/<pid>/maps>]]></programlisting></para>\n  </refsect1>\n\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 <quote>uninitialized</quote>\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.</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()</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()</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    <quote><computeroutput>&lt;jemalloc&gt;: </computeroutput></quote>.</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()</function> and\n      <function>calloc()</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()</function> function\n      returns the value 0 if successful; otherwise it returns an error value.\n      The <function>posix_memalign()</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()</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()</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()</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()</function>\n      function always leaves the original buffer intact when an error occurs.\n      </para>\n\n      <para>The <function>free()</function> function returns no\n      value.</para>\n    </refsect2>\n    <refsect2>\n      <title>Non-standard API</title>\n      <para>The <function>mallocx()</function> and\n      <function>rallocx()</function> functions return a pointer to\n      the allocated memory if successful; otherwise a <constant>NULL</constant>\n      pointer is returned to indicate insufficient contiguous memory was\n      available to service the allocation request.  </para>\n\n      <para>The <function>xallocx()</function> function returns the\n      real size of the resulting resized allocation pointed to by\n      <parameter>ptr</parameter>, which is a value less than\n      <parameter>size</parameter> if the allocation could not be adequately\n      grown in place.  </para>\n\n      <para>The <function>sallocx()</function> function returns the\n      real size of the allocation pointed to by <parameter>ptr</parameter>.\n      </para>\n\n      <para>The <function>nallocx()</function> returns the real size\n      that would result from a successful equivalent\n      <function>mallocx()</function> function call, or zero if\n      insufficient memory is available to perform the size computation.  </para>\n\n      <para>The <function>mallctl()</function>,\n      <function>mallctlnametomib()</function>, and\n      <function>mallctlbymib()</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>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*()</function>\n            read/write processing.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>malloc_usable_size()</function> function\n      returns the usable size of the allocation pointed to by\n      <parameter>ptr</parameter>.  </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 that only one arena should be automatically\n    created:\n      <programlisting language=\"C\"><![CDATA[\nmalloc_conf = \"narenas:1\";]]></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()</function>,\n    <function>calloc()</function>,\n    <function>realloc()</function>, and\n    <function>free()</function> functions conform to ISO/IEC\n    9899:1990 (<quote>ISO C90</quote>).</para>\n\n    <para>The <function>posix_memalign()</function> function conforms\n    to IEEE Std 1003.1-2001 (<quote>POSIX.1</quote>).</para>\n  </refsect1>\n</refentry>\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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=\"0\"/>\n  <xsl:template match=\"function\">\n    <xsl:call-template name=\"inline.monoseq\"/>\n  </xsl:template>\n  <xsl:template match=\"mallctl\">\n    <quote><xsl:call-template name=\"inline.monoseq\"/></quote>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_EXTERNS_H\n#define JEMALLOC_INTERNAL_ARENA_EXTERNS_H\n\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/pages.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n\nextern ssize_t opt_dirty_decay_ms;\nextern ssize_t opt_muzzy_decay_ms;\n\nextern percpu_arena_mode_t opt_percpu_arena;\nextern const char *percpu_arena_mode_names[];\n\nextern const uint64_t h_steps[SMOOTHSTEP_NSTEPS];\nextern malloc_mutex_t arenas_lock;\n\nvoid arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,\n    unsigned *nthreads, const char **dss, ssize_t *dirty_decay_ms,\n    ssize_t *muzzy_decay_ms, size_t *nactive, size_t *ndirty, size_t *nmuzzy);\nvoid arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,\n    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,\n    size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,\n    bin_stats_t *bstats, arena_stats_large_t *lstats);\nvoid arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent);\n#ifdef JEMALLOC_JET\nsize_t arena_slab_regind(extent_t *slab, szind_t binind, const void *ptr);\n#endif\nextent_t *arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena,\n    size_t usize, size_t alignment, bool *zero);\nvoid arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent);\nvoid arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent, size_t oldsize);\nvoid arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent, size_t oldsize);\nssize_t arena_dirty_decay_ms_get(arena_t *arena);\nbool arena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);\nssize_t arena_muzzy_decay_ms_get(arena_t *arena);\nbool arena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);\nvoid arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,\n    bool all);\nvoid arena_reset(tsd_t *tsd, arena_t *arena);\nvoid arena_destroy(tsd_t *tsd, arena_t *arena);\nvoid arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);\nvoid arena_alloc_junk_small(void *ptr, const bin_info_t *bin_info,\n    bool zero);\n\ntypedef void (arena_dalloc_junk_small_t)(void *, const bin_info_t *);\nextern arena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small;\n\nvoid *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,\n    szind_t ind, bool zero);\nvoid *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache);\nvoid arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize);\nvoid arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,\n    bool slow_path);\nvoid arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent, void *ptr);\nvoid arena_dalloc_small(tsdn_t *tsdn, void *ptr);\nbool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,\n    size_t extra, bool zero);\nvoid *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,\n    size_t size, size_t alignment, bool zero, tcache_t *tcache);\ndss_prec_t arena_dss_prec_get(arena_t *arena);\nbool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);\nssize_t arena_dirty_decay_ms_default_get(void);\nbool arena_dirty_decay_ms_default_set(ssize_t decay_ms);\nssize_t arena_muzzy_decay_ms_default_get(void);\nbool arena_muzzy_decay_ms_default_set(ssize_t decay_ms);\nbool arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena,\n    size_t *old_limit, size_t *new_limit);\nunsigned arena_nthreads_get(arena_t *arena, bool internal);\nvoid arena_nthreads_inc(arena_t *arena, bool internal);\nvoid arena_nthreads_dec(arena_t *arena, bool internal);\nsize_t arena_extent_sn_next(arena_t *arena);\narena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);\nvoid arena_boot(void);\nvoid arena_prefork0(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork1(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork2(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork3(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork4(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork5(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork6(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork7(tsdn_t *tsdn, arena_t *arena);\nvoid arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);\nvoid arena_postfork_child(tsdn_t *tsdn, arena_t *arena);\n\n#endif /* JEMALLOC_INTERNAL_ARENA_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_inlines_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_A_H\n#define JEMALLOC_INTERNAL_ARENA_INLINES_A_H\n\nstatic inline unsigned\narena_ind_get(const arena_t *arena) {\n\treturn base_ind_get(arena->base);\n}\n\nstatic inline void\narena_internal_add(arena_t *arena, size_t size) {\n\tatomic_fetch_add_zu(&arena->stats.internal, size, ATOMIC_RELAXED);\n}\n\nstatic inline void\narena_internal_sub(arena_t *arena, size_t size) {\n\tatomic_fetch_sub_zu(&arena->stats.internal, size, ATOMIC_RELAXED);\n}\n\nstatic inline size_t\narena_internal_get(arena_t *arena) {\n\treturn atomic_load_zu(&arena->stats.internal, ATOMIC_RELAXED);\n}\n\nstatic inline bool\narena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes) {\n\tcassert(config_prof);\n\n\tif (likely(prof_interval == 0 || !prof_active_get_unlocked())) {\n\t\treturn false;\n\t}\n\n\treturn prof_accum_add(tsdn, &arena->prof_accum, accumbytes);\n}\n\nstatic inline void\npercpu_arena_update(tsd_t *tsd, unsigned cpu) {\n\tassert(have_percpu_arena);\n\tarena_t *oldarena = tsd_arena_get(tsd);\n\tassert(oldarena != NULL);\n\tunsigned oldind = arena_ind_get(oldarena);\n\n\tif (oldind != cpu) {\n\t\tunsigned newind = cpu;\n\t\tarena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);\n\t\tassert(newarena != NULL);\n\n\t\t/* Set new arena/tcache associations. */\n\t\tarena_migrate(tsd, oldind, newind);\n\t\ttcache_t *tcache = tcache_get(tsd);\n\t\tif (tcache != NULL) {\n\t\t\ttcache_arena_reassociate(tsd_tsdn(tsd), tcache,\n\t\t\t    newarena);\n\t\t}\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_A_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_inlines_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_B_H\n#define JEMALLOC_INTERNAL_ARENA_INLINES_B_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nJEMALLOC_ALWAYS_INLINE prof_tctx_t *\narena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\t/* Static check. */\n\tif (alloc_ctx == NULL) {\n\t\tconst extent_t *extent = iealloc(tsdn, ptr);\n\t\tif (unlikely(!extent_slab_get(extent))) {\n\t\t\treturn large_prof_tctx_get(tsdn, extent);\n\t\t}\n\t} else {\n\t\tif (unlikely(!alloc_ctx->slab)) {\n\t\t\treturn large_prof_tctx_get(tsdn, iealloc(tsdn, ptr));\n\t\t}\n\t}\n\treturn (prof_tctx_t *)(uintptr_t)1U;\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, UNUSED size_t usize,\n    alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\t/* Static check. */\n\tif (alloc_ctx == NULL) {\n\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\tif (unlikely(!extent_slab_get(extent))) {\n\t\t\tlarge_prof_tctx_set(tsdn, extent, tctx);\n\t\t}\n\t} else {\n\t\tif (unlikely(!alloc_ctx->slab)) {\n\t\t\tlarge_prof_tctx_set(tsdn, iealloc(tsdn, ptr), tctx);\n\t\t}\n\t}\n}\n\nstatic inline void\narena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, UNUSED prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\textent_t *extent = iealloc(tsdn, ptr);\n\tassert(!extent_slab_get(extent));\n\n\tlarge_prof_tctx_reset(tsdn, extent);\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) {\n\ttsd_t *tsd;\n\tticker_t *decay_ticker;\n\n\tif (unlikely(tsdn_null(tsdn))) {\n\t\treturn;\n\t}\n\ttsd = tsdn_tsd(tsdn);\n\tdecay_ticker = decay_ticker_get(tsd, arena_ind_get(arena));\n\tif (unlikely(decay_ticker == NULL)) {\n\t\treturn;\n\t}\n\tif (unlikely(ticker_ticks(decay_ticker, nticks))) {\n\t\tarena_decay(tsdn, arena, false, false);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_decay_tick(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_assert_not_owner(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_assert_not_owner(tsdn, &arena->decay_muzzy.mtx);\n\n\tarena_decay_ticks(tsdn, arena, 1);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\narena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,\n    tcache_t *tcache, bool slow_path) {\n\tassert(!tsdn_null(tsdn) || tcache == NULL);\n\tassert(size != 0);\n\n\tif (likely(tcache != NULL)) {\n\t\tif (likely(size <= SMALL_MAXCLASS)) {\n\t\t\treturn tcache_alloc_small(tsdn_tsd(tsdn), arena,\n\t\t\t    tcache, size, ind, zero, slow_path);\n\t\t}\n\t\tif (likely(size <= tcache_maxclass)) {\n\t\t\treturn tcache_alloc_large(tsdn_tsd(tsdn), arena,\n\t\t\t    tcache, size, ind, zero, slow_path);\n\t\t}\n\t\t/* (size > tcache_maxclass) case falls through. */\n\t\tassert(size > tcache_maxclass);\n\t}\n\n\treturn arena_malloc_hard(tsdn, arena, size, ind, zero);\n}\n\nJEMALLOC_ALWAYS_INLINE arena_t *\narena_aalloc(tsdn_t *tsdn, const void *ptr) {\n\treturn extent_arena_get(iealloc(tsdn, ptr));\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\narena_salloc(tsdn_t *tsdn, const void *ptr) {\n\tassert(ptr != NULL);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tszind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true);\n\tassert(szind != NSIZES);\n\n\treturn sz_index2size(szind);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\narena_vsalloc(tsdn_t *tsdn, const void *ptr) {\n\t/*\n\t * Return 0 if ptr is not within an extent managed by jemalloc.  This\n\t * function has two extra costs relative to isalloc():\n\t * - The rtree calls cannot claim to be dependent lookups, which induces\n\t *   rtree lookup load dependencies.\n\t * - The lookup may fail, so there is an extra branch to check for\n\t *   failure.\n\t */\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent;\n\tszind_t szind;\n\tif (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, false, &extent, &szind)) {\n\t\treturn 0;\n\t}\n\n\tif (extent == NULL) {\n\t\treturn 0;\n\t}\n\tassert(extent_state_get(extent) == extent_state_active);\n\t/* Only slab members should be looked up via interior pointers. */\n\tassert(extent_addr_get(extent) == ptr || extent_slab_get(extent));\n\n\tassert(szind != NSIZES);\n\n\treturn sz_index2size(szind);\n}\n\nstatic inline void\narena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {\n\tassert(ptr != NULL);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tszind_t szind;\n\tbool slab;\n\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,\n\t    true, &szind, &slab);\n\n\tif (config_debug) {\n\t\textent_t *extent = rtree_extent_read(tsdn, &extents_rtree,\n\t\t    rtree_ctx, (uintptr_t)ptr, true);\n\t\tassert(szind == extent_szind_get(extent));\n\t\tassert(szind < NSIZES);\n\t\tassert(slab == extent_slab_get(extent));\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\tarena_dalloc_small(tsdn, ptr);\n\t} else {\n\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\tlarge_dalloc(tsdn, extent);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,\n    alloc_ctx_t *alloc_ctx, bool slow_path) {\n\tassert(!tsdn_null(tsdn) || tcache == NULL);\n\tassert(ptr != NULL);\n\n\tif (unlikely(tcache == NULL)) {\n\t\tarena_dalloc_no_tcache(tsdn, ptr);\n\t\treturn;\n\t}\n\n\tszind_t szind;\n\tbool slab;\n\trtree_ctx_t *rtree_ctx;\n\tif (alloc_ctx != NULL) {\n\t\tszind = alloc_ctx->szind;\n\t\tslab = alloc_ctx->slab;\n\t\tassert(szind != NSIZES);\n\t} else {\n\t\trtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));\n\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &szind, &slab);\n\t}\n\n\tif (config_debug) {\n\t\trtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));\n\t\textent_t *extent = rtree_extent_read(tsdn, &extents_rtree,\n\t\t    rtree_ctx, (uintptr_t)ptr, true);\n\t\tassert(szind == extent_szind_get(extent));\n\t\tassert(szind < NSIZES);\n\t\tassert(slab == extent_slab_get(extent));\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\ttcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,\n\t\t    slow_path);\n\t} else {\n\t\tif (szind < nhbins) {\n\t\t\tif (config_prof && unlikely(szind < NBINS)) {\n\t\t\t\tarena_dalloc_promoted(tsdn, ptr, tcache,\n\t\t\t\t    slow_path);\n\t\t\t} else {\n\t\t\t\ttcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,\n\t\t\t\t    szind, slow_path);\n\t\t\t}\n\t\t} else {\n\t\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\t\tlarge_dalloc(tsdn, extent);\n\t\t}\n\t}\n}\n\nstatic inline void\narena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {\n\tassert(ptr != NULL);\n\tassert(size <= LARGE_MAXCLASS);\n\n\tszind_t szind;\n\tbool slab;\n\tif (!config_prof || !opt_prof) {\n\t\t/*\n\t\t * There is no risk of being confused by a promoted sampled\n\t\t * object, so base szind and slab on the given size.\n\t\t */\n\t\tszind = sz_size2index(size);\n\t\tslab = (szind < NBINS);\n\t}\n\n\tif ((config_prof && opt_prof) || config_debug) {\n\t\trtree_ctx_t rtree_ctx_fallback;\n\t\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,\n\t\t    &rtree_ctx_fallback);\n\n\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &szind, &slab);\n\n\t\tassert(szind == sz_size2index(size));\n\t\tassert((config_prof && opt_prof) || slab == (szind < NBINS));\n\n\t\tif (config_debug) {\n\t\t\textent_t *extent = rtree_extent_read(tsdn,\n\t\t\t    &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);\n\t\t\tassert(szind == extent_szind_get(extent));\n\t\t\tassert(slab == extent_slab_get(extent));\n\t\t}\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\tarena_dalloc_small(tsdn, ptr);\n\t} else {\n\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\tlarge_dalloc(tsdn, extent);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,\n    alloc_ctx_t *alloc_ctx, bool slow_path) {\n\tassert(!tsdn_null(tsdn) || tcache == NULL);\n\tassert(ptr != NULL);\n\tassert(size <= LARGE_MAXCLASS);\n\n\tif (unlikely(tcache == NULL)) {\n\t\tarena_sdalloc_no_tcache(tsdn, ptr, size);\n\t\treturn;\n\t}\n\n\tszind_t szind;\n\tbool slab;\n\tUNUSED alloc_ctx_t local_ctx;\n\tif (config_prof && opt_prof) {\n\t\tif (alloc_ctx == NULL) {\n\t\t\t/* Uncommon case and should be a static check. */\n\t\t\trtree_ctx_t rtree_ctx_fallback;\n\t\t\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,\n\t\t\t    &rtree_ctx_fallback);\n\t\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t\t    (uintptr_t)ptr, true, &local_ctx.szind,\n\t\t\t    &local_ctx.slab);\n\t\t\tassert(local_ctx.szind == sz_size2index(size));\n\t\t\talloc_ctx = &local_ctx;\n\t\t}\n\t\tslab = alloc_ctx->slab;\n\t\tszind = alloc_ctx->szind;\n\t} else {\n\t\t/*\n\t\t * There is no risk of being confused by a promoted sampled\n\t\t * object, so base szind and slab on the given size.\n\t\t */\n\t\tszind = sz_size2index(size);\n\t\tslab = (szind < NBINS);\n\t}\n\n\tif (config_debug) {\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));\n\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &szind, &slab);\n\t\textent_t *extent = rtree_extent_read(tsdn,\n\t\t    &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);\n\t\tassert(szind == extent_szind_get(extent));\n\t\tassert(slab == extent_slab_get(extent));\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\ttcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,\n\t\t    slow_path);\n\t} else {\n\t\tif (szind < nhbins) {\n\t\t\tif (config_prof && unlikely(szind < NBINS)) {\n\t\t\t\tarena_dalloc_promoted(tsdn, ptr, tcache,\n\t\t\t\t    slow_path);\n\t\t\t} else {\n\t\t\t\ttcache_dalloc_large(tsdn_tsd(tsdn),\n\t\t\t\t    tcache, ptr, szind, slow_path);\n\t\t\t}\n\t\t} else {\n\t\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\t\tlarge_dalloc(tsdn, extent);\n\t\t}\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_B_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_stats.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_STATS_H\n#define JEMALLOC_INTERNAL_ARENA_STATS_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\n/*\n * In those architectures that support 64-bit atomics, we use atomic updates for\n * our 64-bit values.  Otherwise, we use a plain uint64_t and synchronize\n * externally.\n */\n#ifdef JEMALLOC_ATOMIC_U64\ntypedef atomic_u64_t arena_stats_u64_t;\n#else\n/* Must hold the arena stats mutex while reading atomically. */\ntypedef uint64_t arena_stats_u64_t;\n#endif\n\ntypedef struct arena_stats_large_s arena_stats_large_t;\nstruct arena_stats_large_s {\n\t/*\n\t * Total number of allocation/deallocation requests served directly by\n\t * the arena.\n\t */\n\tarena_stats_u64_t\tnmalloc;\n\tarena_stats_u64_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\tarena_stats_u64_t\tnrequests; /* Partially derived. */\n\n\t/* Current number of allocations of this size class. */\n\tsize_t\t\tcurlextents; /* Derived. */\n};\n\ntypedef struct arena_stats_decay_s arena_stats_decay_t;\nstruct arena_stats_decay_s {\n\t/* Total number of purge sweeps. */\n\tarena_stats_u64_t\tnpurge;\n\t/* Total number of madvise calls made. */\n\tarena_stats_u64_t\tnmadvise;\n\t/* Total number of pages purged. */\n\tarena_stats_u64_t\tpurged;\n};\n\n/*\n * Arena stats.  Note that fields marked \"derived\" are not directly maintained\n * within the arena code; rather their values are derived during stats merge\n * requests.\n */\ntypedef struct arena_stats_s arena_stats_t;\nstruct arena_stats_s {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_t\t\tmtx;\n#endif\n\n\t/* Number of bytes currently mapped, excluding retained memory. */\n\tatomic_zu_t\t\tmapped; /* Partially derived. */\n\n\t/*\n\t * Number of unused virtual memory bytes currently retained.  Retained\n\t * bytes are technically mapped (though always decommitted or purged),\n\t * but they are excluded from the mapped statistic (above).\n\t */\n\tatomic_zu_t\t\tretained; /* Derived. */\n\n\tarena_stats_decay_t\tdecay_dirty;\n\tarena_stats_decay_t\tdecay_muzzy;\n\n\tatomic_zu_t\t\tbase; /* Derived. */\n\tatomic_zu_t\t\tinternal;\n\tatomic_zu_t\t\tresident; /* Derived. */\n\tatomic_zu_t\t\tmetadata_thp;\n\n\tatomic_zu_t\t\tallocated_large; /* Derived. */\n\tarena_stats_u64_t\tnmalloc_large; /* Derived. */\n\tarena_stats_u64_t\tndalloc_large; /* Derived. */\n\tarena_stats_u64_t\tnrequests_large; /* Derived. */\n\n\t/* Number of bytes cached in tcache associated with this arena. */\n\tatomic_zu_t\t\ttcache_bytes; /* Derived. */\n\n\tmutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes];\n\n\t/* One element for each large size class. */\n\tarena_stats_large_t\tlstats[NSIZES - NBINS];\n\n\t/* Arena uptime. */\n\tnstime_t\t\tuptime;\n};\n\nstatic inline bool\narena_stats_init(UNUSED tsdn_t *tsdn, arena_stats_t *arena_stats) {\n\tif (config_debug) {\n\t\tfor (size_t i = 0; i < sizeof(arena_stats_t); i++) {\n\t\t\tassert(((char *)arena_stats)[i] == 0);\n\t\t}\n\t}\n#ifndef JEMALLOC_ATOMIC_U64\n\tif (malloc_mutex_init(&arena_stats->mtx, \"arena_stats\",\n\t    WITNESS_RANK_ARENA_STATS, malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n#endif\n\t/* Memory is zeroed, so there is no need to clear stats. */\n\treturn false;\n}\n\nstatic inline void\narena_stats_lock(tsdn_t *tsdn, arena_stats_t *arena_stats) {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_lock(tsdn, &arena_stats->mtx);\n#endif\n}\n\nstatic inline void\narena_stats_unlock(tsdn_t *tsdn, arena_stats_t *arena_stats) {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_unlock(tsdn, &arena_stats->mtx);\n#endif\n}\n\nstatic inline uint64_t\narena_stats_read_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    arena_stats_u64_t *p) {\n#ifdef JEMALLOC_ATOMIC_U64\n\treturn atomic_load_u64(p, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\treturn *p;\n#endif\n}\n\nstatic inline void\narena_stats_add_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    arena_stats_u64_t *p, uint64_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tatomic_fetch_add_u64(p, x, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\t*p += x;\n#endif\n}\n\nUNUSED static inline void\narena_stats_sub_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    arena_stats_u64_t *p, uint64_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tUNUSED uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);\n\tassert(r - x <= r);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\t*p -= x;\n\tassert(*p + x >= *p);\n#endif\n}\n\n/*\n * Non-atomically sets *dst += src.  *dst needs external synchronization.\n * This lets us avoid the cost of a fetch_add when its unnecessary (note that\n * the types here are atomic).\n */\nstatic inline void\narena_stats_accum_u64(arena_stats_u64_t *dst, uint64_t src) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tuint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);\n\tatomic_store_u64(dst, src + cur_dst, ATOMIC_RELAXED);\n#else\n\t*dst += src;\n#endif\n}\n\nstatic inline size_t\narena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p) {\n#ifdef JEMALLOC_ATOMIC_U64\n\treturn atomic_load_zu(p, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\treturn atomic_load_zu(p, ATOMIC_RELAXED);\n#endif\n}\n\nstatic inline void\narena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,\n    size_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tatomic_fetch_add_zu(p, x, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\tsize_t cur = atomic_load_zu(p, ATOMIC_RELAXED);\n\tatomic_store_zu(p, cur + x, ATOMIC_RELAXED);\n#endif\n}\n\nstatic inline void\narena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,\n    size_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tUNUSED size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);\n\tassert(r - x <= r);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\tsize_t cur = atomic_load_zu(p, ATOMIC_RELAXED);\n\tatomic_store_zu(p, cur - x, ATOMIC_RELAXED);\n#endif\n}\n\n/* Like the _u64 variant, needs an externally synchronized *dst. */\nstatic inline void\narena_stats_accum_zu(atomic_zu_t *dst, size_t src) {\n\tsize_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);\n\tatomic_store_zu(dst, src + cur_dst, ATOMIC_RELAXED);\n}\n\nstatic inline void\narena_stats_large_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    szind_t szind, uint64_t nrequests) {\n\tarena_stats_lock(tsdn, arena_stats);\n\tarena_stats_add_u64(tsdn, arena_stats, &arena_stats->lstats[szind -\n\t    NBINS].nrequests, nrequests);\n\tarena_stats_unlock(tsdn, arena_stats);\n}\n\nstatic inline void\narena_stats_mapped_add(tsdn_t *tsdn, arena_stats_t *arena_stats, size_t size) {\n\tarena_stats_lock(tsdn, arena_stats);\n\tarena_stats_add_zu(tsdn, arena_stats, &arena_stats->mapped, size);\n\tarena_stats_unlock(tsdn, arena_stats);\n}\n\n\n#endif /* JEMALLOC_INTERNAL_ARENA_STATS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_structs_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H\n#define JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H\n\n#include \"jemalloc/internal/bitmap.h\"\n\nstruct arena_slab_data_s {\n\t/* Per region allocated/deallocated bitmap. */\n\tbitmap_t\tbitmap[BITMAP_GROUPS_MAX];\n};\n\n#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_structs_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H\n#define JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H\n\n#include \"jemalloc/internal/arena_stats.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/nstime.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/smoothstep.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nstruct arena_decay_s {\n\t/* Synchronizes all non-atomic fields. */\n\tmalloc_mutex_t\t\tmtx;\n\t/*\n\t * True if a thread is currently purging the extents associated with\n\t * this decay structure.\n\t */\n\tbool\t\t\tpurging;\n\t/*\n\t * Approximate time in milliseconds from the creation of a set of unused\n\t * dirty pages until an equivalent set of unused dirty pages is purged\n\t * and/or reused.\n\t */\n\tatomic_zd_t\t\ttime_ms;\n\t/* time / SMOOTHSTEP_NSTEPS. */\n\tnstime_t\t\tinterval;\n\t/*\n\t * Time at which the current decay interval logically started.  We do\n\t * not actually advance to a new epoch until sometime after it starts\n\t * because of scheduling and computation delays, and it is even possible\n\t * to completely skip epochs.  In all cases, during epoch advancement we\n\t * merge all relevant activity into the most recently recorded epoch.\n\t */\n\tnstime_t\t\tepoch;\n\t/* Deadline randomness generator. */\n\tuint64_t\t\tjitter_state;\n\t/*\n\t * Deadline for current epoch.  This is the sum of interval and per\n\t * epoch jitter which is a uniform random variable in [0..interval).\n\t * Epochs always advance by precise multiples of interval, but we\n\t * randomize the deadline to reduce the likelihood of arenas purging in\n\t * lockstep.\n\t */\n\tnstime_t\t\tdeadline;\n\t/*\n\t * Number of unpurged pages at beginning of current epoch.  During epoch\n\t * advancement we use the delta between arena->decay_*.nunpurged and\n\t * extents_npages_get(&arena->extents_*) to determine how many dirty\n\t * pages, if any, were generated.\n\t */\n\tsize_t\t\t\tnunpurged;\n\t/*\n\t * Trailing log of how many unused dirty pages were generated during\n\t * each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last\n\t * element is the most recent epoch.  Corresponding epoch times are\n\t * relative to epoch.\n\t */\n\tsize_t\t\t\tbacklog[SMOOTHSTEP_NSTEPS];\n\n\t/*\n\t * Pointer to associated stats.  These stats are embedded directly in\n\t * the arena's stats due to how stats structures are shared between the\n\t * arena and ctl code.\n\t *\n\t * Synchronization: Same as associated arena's stats field. */\n\tarena_stats_decay_t\t*stats;\n\t/* Peak number of pages in associated extents.  Used for debug only. */\n\tuint64_t\t\tceil_npages;\n};\n\nstruct arena_s {\n\t/*\n\t * Number of threads currently assigned to this arena.  Each thread has\n\t * two distinct assignments, one for application-serving allocation, and\n\t * the other for internal metadata allocation.  Internal metadata must\n\t * not be allocated from arenas explicitly created via the arenas.create\n\t * mallctl, because the arena.<i>.reset mallctl indiscriminately\n\t * discards all allocations for the affected arena.\n\t *\n\t *   0: Application allocation.\n\t *   1: Internal metadata allocation.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_u_t\t\tnthreads[2];\n\n\t/*\n\t * When percpu_arena is enabled, to amortize the cost of reading /\n\t * updating the current CPU id, track the most recent thread accessing\n\t * this arena, and only read CPU if there is a mismatch.\n\t */\n\ttsdn_t\t\t*last_thd;\n\n\t/* Synchronization: internal. */\n\tarena_stats_t\t\tstats;\n\n\t/*\n\t * Lists of tcaches and cache_bin_array_descriptors for extant threads\n\t * associated with this arena.  Stats from these are merged\n\t * incrementally, and at exit if opt_stats_print is enabled.\n\t *\n\t * Synchronization: tcache_ql_mtx.\n\t */\n\tql_head(tcache_t)\t\t\ttcache_ql;\n\tql_head(cache_bin_array_descriptor_t)\tcache_bin_array_descriptor_ql;\n\tmalloc_mutex_t\t\t\t\ttcache_ql_mtx;\n\n\t/* Synchronization: internal. */\n\tprof_accum_t\t\tprof_accum;\n\tuint64_t\t\tprof_accumbytes;\n\n\t/*\n\t * PRNG state for cache index randomization of large allocation base\n\t * pointers.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_zu_t\t\toffset_state;\n\n\t/*\n\t * Extent serial number generator state.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_zu_t\t\textent_sn_next;\n\n\t/*\n\t * Represents a dss_prec_t, but atomically.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_u_t\t\tdss_prec;\n\n\t/*\n\t * Number of pages in active extents.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_zu_t\t\tnactive;\n\n\t/*\n\t * Extant large allocations.\n\t *\n\t * Synchronization: large_mtx.\n\t */\n\textent_list_t\t\tlarge;\n\t/* Synchronizes all large allocation/update/deallocation. */\n\tmalloc_mutex_t\t\tlarge_mtx;\n\n\t/*\n\t * Collections of extents that were previously allocated.  These are\n\t * used when allocating extents, in an attempt to re-use address space.\n\t *\n\t * Synchronization: internal.\n\t */\n\textents_t\t\textents_dirty;\n\textents_t\t\textents_muzzy;\n\textents_t\t\textents_retained;\n\n\t/*\n\t * Decay-based purging state, responsible for scheduling extent state\n\t * transitions.\n\t *\n\t * Synchronization: internal.\n\t */\n\tarena_decay_t\t\tdecay_dirty; /* dirty --> muzzy */\n\tarena_decay_t\t\tdecay_muzzy; /* muzzy --> retained */\n\n\t/*\n\t * Next extent size class in a growing series to use when satisfying a\n\t * request via the extent hooks (only if opt_retain).  This limits the\n\t * number of disjoint virtual memory ranges so that extent merging can\n\t * be effective even if multiple arenas' extent allocation requests are\n\t * highly interleaved.\n\t *\n\t * retain_grow_limit is the max allowed size ind to expand (unless the\n\t * required size is greater).  Default is no limit, and controlled\n\t * through mallctl only.\n\t *\n\t * Synchronization: extent_grow_mtx\n\t */\n\tpszind_t\t\textent_grow_next;\n\tpszind_t\t\tretain_grow_limit;\n\tmalloc_mutex_t\t\textent_grow_mtx;\n\n\t/*\n\t * Available extent structures that were allocated via\n\t * base_alloc_extent().\n\t *\n\t * Synchronization: extent_avail_mtx.\n\t */\n\textent_tree_t\t\textent_avail;\n\tmalloc_mutex_t\t\textent_avail_mtx;\n\n\t/*\n\t * bins is used to store heaps of free regions.\n\t *\n\t * Synchronization: internal.\n\t */\n\tbin_t\t\t\tbins[NBINS];\n\n\t/*\n\t * Base allocator, from which arena metadata are allocated.\n\t *\n\t * Synchronization: internal.\n\t */\n\tbase_t\t\t\t*base;\n\t/* Used to determine uptime.  Read-only after initialization. */\n\tnstime_t\t\tcreate_time;\n};\n\n/* Used in conjunction with tsd for fast arena-related context lookup. */\nstruct arena_tdata_s {\n\tticker_t\t\tdecay_ticker;\n};\n\n/* Used to pass rtree lookup context down the path. */\nstruct alloc_ctx_s {\n\tszind_t szind;\n\tbool slab;\n};\n\n#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_TYPES_H\n#define JEMALLOC_INTERNAL_ARENA_TYPES_H\n\n/* Maximum number of regions in one slab. */\n#define LG_SLAB_MAXREGS\t\t(LG_PAGE - LG_TINY_MIN)\n#define SLAB_MAXREGS\t\t(1U << LG_SLAB_MAXREGS)\n\n/* Default decay times in milliseconds. */\n#define DIRTY_DECAY_MS_DEFAULT\tZD(10 * 1000)\n#define MUZZY_DECAY_MS_DEFAULT\tZD(10 * 1000)\n/* Number of event ticks between time checks. */\n#define DECAY_NTICKS_PER_UPDATE\t1000\n\ntypedef struct arena_slab_data_s arena_slab_data_t;\ntypedef struct arena_decay_s arena_decay_t;\ntypedef struct arena_s arena_t;\ntypedef struct arena_tdata_s arena_tdata_t;\ntypedef struct alloc_ctx_s alloc_ctx_t;\n\ntypedef enum {\n\tpercpu_arena_mode_names_base   = 0, /* Used for options processing. */\n\n\t/*\n\t * *_uninit are used only during bootstrapping, and must correspond\n\t * to initialized variant plus percpu_arena_mode_enabled_base.\n\t */\n\tpercpu_arena_uninit            = 0,\n\tper_phycpu_arena_uninit        = 1,\n\n\t/* All non-disabled modes must come after percpu_arena_disabled. */\n\tpercpu_arena_disabled          = 2,\n\n\tpercpu_arena_mode_names_limit  = 3, /* Used for options processing. */\n\tpercpu_arena_mode_enabled_base = 3,\n\n\tpercpu_arena                   = 3,\n\tper_phycpu_arena               = 4  /* Hyper threads share arena. */\n} percpu_arena_mode_t;\n\n#define PERCPU_ARENA_ENABLED(m)\t((m) >= percpu_arena_mode_enabled_base)\n#define PERCPU_ARENA_DEFAULT\tpercpu_arena_disabled\n\n#endif /* JEMALLOC_INTERNAL_ARENA_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/assert.h",
    "content": "#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/util.h\"\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 assert(e) do {\t\t\t\t\t\t\t\\\n\tif (unlikely(config_debug && !(e))) {\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#ifndef not_reached\n#define not_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\tunreachable();\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n\n#ifndef not_implemented\n#define not_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#ifndef assert_not_implemented\n#define assert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (unlikely(config_debug && !(e))) {\t\t\t\t\\\n\t\tnot_implemented();\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#ifndef cassert\n#define cassert(c) do {\t\t\t\t\t\t\t\\\n\tif (unlikely(!(c))) {\t\t\t\t\t\t\\\n\t\tnot_reached();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_H\n#define JEMALLOC_INTERNAL_ATOMIC_H\n\n#define ATOMIC_INLINE static inline\n\n#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS)\n#  include \"jemalloc/internal/atomic_gcc_atomic.h\"\n#elif defined(JEMALLOC_GCC_SYNC_ATOMICS)\n#  include \"jemalloc/internal/atomic_gcc_sync.h\"\n#elif defined(_MSC_VER)\n#  include \"jemalloc/internal/atomic_msvc.h\"\n#elif defined(JEMALLOC_C11_ATOMICS)\n#  include \"jemalloc/internal/atomic_c11.h\"\n#else\n#  error \"Don't have atomics implemented on this platform.\"\n#endif\n\n/*\n * This header gives more or less a backport of C11 atomics. The user can write\n * JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_sizeof_type); to generate\n * counterparts of the C11 atomic functions for type, as so:\n *   JEMALLOC_GENERATE_ATOMICS(int *, pi, 3);\n * and then write things like:\n *   int *some_ptr;\n *   atomic_pi_t atomic_ptr_to_int;\n *   atomic_store_pi(&atomic_ptr_to_int, some_ptr, ATOMIC_RELAXED);\n *   int *prev_value = atomic_exchange_pi(&ptr_to_int, NULL, ATOMIC_ACQ_REL);\n *   assert(some_ptr == prev_value);\n * and expect things to work in the obvious way.\n *\n * Also included (with naming differences to avoid conflicts with the standard\n * library):\n *   atomic_fence(atomic_memory_order_t) (mimics C11's atomic_thread_fence).\n *   ATOMIC_INIT (mimics C11's ATOMIC_VAR_INIT).\n */\n\n/*\n * Pure convenience, so that we don't have to type \"atomic_memory_order_\"\n * quite so often.\n */\n#define ATOMIC_RELAXED atomic_memory_order_relaxed\n#define ATOMIC_ACQUIRE atomic_memory_order_acquire\n#define ATOMIC_RELEASE atomic_memory_order_release\n#define ATOMIC_ACQ_REL atomic_memory_order_acq_rel\n#define ATOMIC_SEQ_CST atomic_memory_order_seq_cst\n\n/*\n * Not all platforms have 64-bit atomics.  If we do, this #define exposes that\n * fact.\n */\n#if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)\n#  define JEMALLOC_ATOMIC_U64\n#endif\n\nJEMALLOC_GENERATE_ATOMICS(void *, p, LG_SIZEOF_PTR)\n\n/*\n * There's no actual guarantee that sizeof(bool) == 1, but it's true on the only\n * platform that actually needs to know the size, MSVC.\n */\nJEMALLOC_GENERATE_ATOMICS(bool, b, 0)\n\nJEMALLOC_GENERATE_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)\n\nJEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)\n\nJEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)\n\nJEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)\n\n#ifdef JEMALLOC_ATOMIC_U64\nJEMALLOC_GENERATE_INT_ATOMICS(uint64_t, u64, 3)\n#endif\n\n#undef ATOMIC_INLINE\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_c11.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_C11_H\n#define JEMALLOC_INTERNAL_ATOMIC_C11_H\n\n#include <stdatomic.h>\n\n#define ATOMIC_INIT(...) ATOMIC_VAR_INIT(__VA_ARGS__)\n\n#define atomic_memory_order_t memory_order\n#define atomic_memory_order_relaxed memory_order_relaxed\n#define atomic_memory_order_acquire memory_order_acquire\n#define atomic_memory_order_release memory_order_release\n#define atomic_memory_order_acq_rel memory_order_acq_rel\n#define atomic_memory_order_seq_cst memory_order_seq_cst\n\n#define atomic_fence atomic_thread_fence\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\ntypedef _Atomic(type) atomic_##short_type##_t;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * A strict interpretation of the C standard prevents\t\t\\\n\t * atomic_load from taking a const argument, but it's\t\t\\\n\t * convenient for our purposes. This cast is a workaround.\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tatomic_##short_type##_t* a_nonconst =\t\t\t\t\\\n\t    (atomic_##short_type##_t*)a;\t\t\t\t\\\n\treturn atomic_load_explicit(a_nonconst, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a,\t\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\tatomic_store_explicit(a, val, mo);\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn atomic_exchange_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn atomic_compare_exchange_weak_explicit(a, expected,\t\\\n\t    desired, success_mo, failure_mo);\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn atomic_compare_exchange_strong_explicit(a, expected,\t\\\n\t    desired, success_mo, failure_mo);\t\t\t\t\\\n}\n\n/*\n * Integral types have some special operations available that non-integral ones\n * lack.\n */\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, \t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_add_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_sub_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_and_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_or_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_xor_explicit(a, val, mo);\t\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_C11_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_gcc_atomic.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H\n#define JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H\n\n#include \"jemalloc/internal/assert.h\"\n\n#define ATOMIC_INIT(...) {__VA_ARGS__}\n\ntypedef enum {\n\tatomic_memory_order_relaxed,\n\tatomic_memory_order_acquire,\n\tatomic_memory_order_release,\n\tatomic_memory_order_acq_rel,\n\tatomic_memory_order_seq_cst\n} atomic_memory_order_t;\n\nATOMIC_INLINE int\natomic_enum_to_builtin(atomic_memory_order_t mo) {\n\tswitch (mo) {\n\tcase atomic_memory_order_relaxed:\n\t\treturn __ATOMIC_RELAXED;\n\tcase atomic_memory_order_acquire:\n\t\treturn __ATOMIC_ACQUIRE;\n\tcase atomic_memory_order_release:\n\t\treturn __ATOMIC_RELEASE;\n\tcase atomic_memory_order_acq_rel:\n\t\treturn __ATOMIC_ACQ_REL;\n\tcase atomic_memory_order_seq_cst:\n\t\treturn __ATOMIC_SEQ_CST;\n\t}\n\t/* Can't happen; the switch is exhaustive. */\n\tnot_reached();\n}\n\nATOMIC_INLINE void\natomic_fence(atomic_memory_order_t mo) {\n\t__atomic_thread_fence(atomic_enum_to_builtin(mo));\n}\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\ttype repr;\t\t\t\t\t\t\t\\\n} atomic_##short_type##_t;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\ttype result;\t\t\t\t\t\t\t\\\n\t__atomic_load(&a->repr, &result, atomic_enum_to_builtin(mo));\t\\\n\treturn result;\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\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a, type val,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\t__atomic_store(&a->repr, &val, atomic_enum_to_builtin(mo));\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\ttype result;\t\t\t\t\t\t\t\\\n\t__atomic_exchange(&a->repr, &val, &result,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n\treturn result;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn __atomic_compare_exchange(&a->repr, expected, &desired,\t\\\n\t    true, atomic_enum_to_builtin(success_mo),\t\t\t\\\n\t    atomic_enum_to_builtin(failure_mo));\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn __atomic_compare_exchange(&a->repr, expected, &desired,\t\\\n\t    false,\t\t\t\t\t\t\t\\\n\t    atomic_enum_to_builtin(success_mo),\t\t\t\t\\\n\t    atomic_enum_to_builtin(failure_mo));\t\t\t\\\n}\n\n\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_add(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_sub(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_and(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_or(&a->repr, val,\t\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_xor(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_gcc_sync.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H\n#define JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H\n\n#define ATOMIC_INIT(...) {__VA_ARGS__}\n\ntypedef enum {\n\tatomic_memory_order_relaxed,\n\tatomic_memory_order_acquire,\n\tatomic_memory_order_release,\n\tatomic_memory_order_acq_rel,\n\tatomic_memory_order_seq_cst\n} atomic_memory_order_t;\n\nATOMIC_INLINE void\natomic_fence(atomic_memory_order_t mo) {\n\t/* Easy cases first: no barrier, and full barrier. */\n\tif (mo == atomic_memory_order_relaxed) {\n\t\tasm volatile(\"\" ::: \"memory\");\n\t\treturn;\n\t}\n\tif (mo == atomic_memory_order_seq_cst) {\n\t\tasm volatile(\"\" ::: \"memory\");\n\t\t__sync_synchronize();\n\t\tasm volatile(\"\" ::: \"memory\");\n\t\treturn;\n\t}\n\tasm volatile(\"\" ::: \"memory\");\n#  if defined(__i386__) || defined(__x86_64__)\n\t/* This is implicit on x86. */\n#  elif defined(__ppc__)\n\tasm volatile(\"lwsync\");\n#  elif defined(__sparc__) && defined(__arch64__)\n\tif (mo == atomic_memory_order_acquire) {\n\t\tasm volatile(\"membar #LoadLoad | #LoadStore\");\n\t} else if (mo == atomic_memory_order_release) {\n\t\tasm volatile(\"membar #LoadStore | #StoreStore\");\n\t} else {\n\t\tasm volatile(\"membar #LoadLoad | #LoadStore | #StoreStore\");\n\t}\n#  else\n\t__sync_synchronize();\n#  endif\n\tasm volatile(\"\" ::: \"memory\");\n}\n\n/*\n * A correct implementation of seq_cst loads and stores on weakly ordered\n * architectures could do either of the following:\n *   1. store() is weak-fence -> store -> strong fence, load() is load ->\n *      strong-fence.\n *   2. store() is strong-fence -> store, load() is strong-fence -> load ->\n *      weak-fence.\n * The tricky thing is, load() and store() above can be the load or store\n * portions of a gcc __sync builtin, so we have to follow GCC's lead, which\n * means going with strategy 2.\n * On strongly ordered architectures, the natural strategy is to stick a strong\n * fence after seq_cst stores, and have naked loads.  So we want the strong\n * fences in different places on different architectures.\n * atomic_pre_sc_load_fence and atomic_post_sc_store_fence allow us to\n * accomplish this.\n */\n\nATOMIC_INLINE void\natomic_pre_sc_load_fence() {\n#  if defined(__i386__) || defined(__x86_64__) ||\t\t\t\\\n    (defined(__sparc__) && defined(__arch64__))\n\tatomic_fence(atomic_memory_order_relaxed);\n#  else\n\tatomic_fence(atomic_memory_order_seq_cst);\n#  endif\n}\n\nATOMIC_INLINE void\natomic_post_sc_store_fence() {\n#  if defined(__i386__) || defined(__x86_64__) ||\t\t\t\\\n    (defined(__sparc__) && defined(__arch64__))\n\tatomic_fence(atomic_memory_order_seq_cst);\n#  else\n\tatomic_fence(atomic_memory_order_relaxed);\n#  endif\n\n}\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\ttype volatile repr;\t\t\t\t\t\t\\\n} atomic_##short_type##_t;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\tif (mo == atomic_memory_order_seq_cst) {\t\t\t\\\n\t\tatomic_pre_sc_load_fence();\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ttype result = a->repr;\t\t\t\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_acquire);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn result;\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\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a,\t\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_release);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta->repr = val;\t\t\t\t\t\t\t\\\n\tif (mo == atomic_memory_order_seq_cst) {\t\t\t\\\n\t\tatomic_post_sc_store_fence();\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\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * Because of FreeBSD, we care about gcc 4.2, which doesn't have\\\n\t * an atomic exchange builtin.  We fake it with a CAS loop.\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\twhile (true) {\t\t\t\t\t\t\t\\\n\t\ttype old = a->repr;\t\t\t\t\t\\\n\t\tif (__sync_bool_compare_and_swap(&a->repr, old, val)) {\t\\\n\t\t\treturn old;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\ttype prev = __sync_val_compare_and_swap(&a->repr, *expected,\t\\\n\t    desired);\t\t\t\t\t\t\t\\\n\tif (prev == *expected) {\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*expected = prev;\t\t\t\t\t\\\n\t\treturn false;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\ttype prev = __sync_val_compare_and_swap(&a->repr, *expected,\t\\\n\t    desired);\t\t\t\t\t\t\t\\\n\tif (prev == *expected) {\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*expected = prev;\t\t\t\t\t\\\n\t\treturn false;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_add(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_sub(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_and(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_or(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_xor(&a->repr, val);\t\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_msvc.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_MSVC_H\n#define JEMALLOC_INTERNAL_ATOMIC_MSVC_H\n\n#define ATOMIC_INIT(...) {__VA_ARGS__}\n\ntypedef enum {\n\tatomic_memory_order_relaxed,\n\tatomic_memory_order_acquire,\n\tatomic_memory_order_release,\n\tatomic_memory_order_acq_rel,\n\tatomic_memory_order_seq_cst\n} atomic_memory_order_t;\n\ntypedef char atomic_repr_0_t;\ntypedef short atomic_repr_1_t;\ntypedef long atomic_repr_2_t;\ntypedef __int64 atomic_repr_3_t;\n\nATOMIC_INLINE void\natomic_fence(atomic_memory_order_t mo) {\n\t_ReadWriteBarrier();\n#  if defined(_M_ARM) || defined(_M_ARM64)\n\t/* ARM needs a barrier for everything but relaxed. */\n\tif (mo != atomic_memory_order_relaxed) {\n\t\tMemoryBarrier();\n\t}\n#  elif defined(_M_IX86) || defined (_M_X64)\n\t/* x86 needs a barrier only for seq_cst. */\n\tif (mo == atomic_memory_order_seq_cst) {\n\t\tMemoryBarrier();\n\t}\n#  else\n#  error \"Don't know how to create atomics for this platform for MSVC.\"\n#  endif\n\t_ReadWriteBarrier();\n}\n\n#define ATOMIC_INTERLOCKED_REPR(lg_size) atomic_repr_ ## lg_size ## _t\n\n#define ATOMIC_CONCAT(a, b) ATOMIC_RAW_CONCAT(a, b)\n#define ATOMIC_RAW_CONCAT(a, b) a ## b\n\n#define ATOMIC_INTERLOCKED_NAME(base_name, lg_size) ATOMIC_CONCAT(\t\\\n    base_name, ATOMIC_INTERLOCKED_SUFFIX(lg_size))\n\n#define ATOMIC_INTERLOCKED_SUFFIX(lg_size)\t\t\t\t\\\n    ATOMIC_CONCAT(ATOMIC_INTERLOCKED_SUFFIX_, lg_size)\n\n#define ATOMIC_INTERLOCKED_SUFFIX_0 8\n#define ATOMIC_INTERLOCKED_SUFFIX_1 16\n#define ATOMIC_INTERLOCKED_SUFFIX_2\n#define ATOMIC_INTERLOCKED_SUFFIX_3 64\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size)\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) repr;\t\t\t\t\\\n} atomic_##short_type##_t;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) ret = a->repr;\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_acquire);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (type) ret;\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\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a,\t\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_release);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta->repr = (ATOMIC_INTERLOCKED_REPR(lg_size)) val;\t\t\\\n\tif (mo == atomic_memory_order_seq_cst) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_seq_cst);\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\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchange,\t\\\n\t    lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) e =\t\t\t\t\\\n\t    (ATOMIC_INTERLOCKED_REPR(lg_size))*expected;\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) d =\t\t\t\t\\\n\t    (ATOMIC_INTERLOCKED_REPR(lg_size))desired;\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) old =\t\t\t\t\\\n\t    ATOMIC_INTERLOCKED_NAME(_InterlockedCompareExchange, \t\\\n\t\tlg_size)(&a->repr, d, e);\t\t\t\t\\\n\tif (old == e) {\t\t\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*expected = (type)old;\t\t\t\t\t\\\n\t\treturn false;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\t/* We implement the weak version with strong semantics. */\t\\\n\treturn atomic_compare_exchange_weak_##short_type(a, expected,\t\\\n\t    desired, success_mo, failure_mo);\t\t\t\t\\\n}\n\n\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size)\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size)\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchangeAdd,\t\\\n\t    lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * MSVC warns on negation of unsigned operands, but for us it\t\\\n\t * gives exactly the right semantics (MAX_TYPE + 1 - operand).\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\t__pragma(warning(push))\t\t\t\t\t\t\\\n\t__pragma(warning(disable: 4146))\t\t\t\t\\\n\treturn atomic_fetch_add_##short_type(a, -val, mo);\t\t\\\n\t__pragma(warning(pop))\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedAnd, lg_size)(\t\\\n\t    &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedOr, lg_size)(\t\\\n\t    &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedXor, lg_size)(\t\\\n\t    &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_MSVC_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/background_thread_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H\n#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H\n\nextern bool opt_background_thread;\nextern size_t opt_max_background_threads;\nextern malloc_mutex_t background_thread_lock;\nextern atomic_b_t background_thread_enabled_state;\nextern size_t n_background_threads;\nextern size_t max_background_threads;\nextern background_thread_info_t *background_thread_info;\nextern bool can_enable_background_thread;\n\nbool background_thread_create(tsd_t *tsd, unsigned arena_ind);\nbool background_threads_enable(tsd_t *tsd);\nbool background_threads_disable(tsd_t *tsd);\nvoid background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, size_t npages_new);\nvoid background_thread_prefork0(tsdn_t *tsdn);\nvoid background_thread_prefork1(tsdn_t *tsdn);\nvoid background_thread_postfork_parent(tsdn_t *tsdn);\nvoid background_thread_postfork_child(tsdn_t *tsdn);\nbool background_thread_stats_read(tsdn_t *tsdn,\n    background_thread_stats_t *stats);\nvoid background_thread_ctl_init(tsdn_t *tsdn);\n\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\nextern int pthread_create_wrapper(pthread_t *__restrict, const pthread_attr_t *,\n    void *(*)(void *), void *__restrict);\n#endif\nbool background_thread_boot0(void);\nbool background_thread_boot1(tsdn_t *tsdn);\n\n#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/background_thread_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H\n#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H\n\nJEMALLOC_ALWAYS_INLINE bool\nbackground_thread_enabled(void) {\n\treturn atomic_load_b(&background_thread_enabled_state, ATOMIC_RELAXED);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nbackground_thread_enabled_set(tsdn_t *tsdn, bool state) {\n\tmalloc_mutex_assert_owner(tsdn, &background_thread_lock);\n\tatomic_store_b(&background_thread_enabled_state, state, ATOMIC_RELAXED);\n}\n\nJEMALLOC_ALWAYS_INLINE background_thread_info_t *\narena_background_thread_info_get(arena_t *arena) {\n\tunsigned arena_ind = arena_ind_get(arena);\n\treturn &background_thread_info[arena_ind % ncpus];\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nbackground_thread_wakeup_time_get(background_thread_info_t *info) {\n\tuint64_t next_wakeup = nstime_ns(&info->next_wakeup);\n\tassert(atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE) ==\n\t    (next_wakeup == BACKGROUND_THREAD_INDEFINITE_SLEEP));\n\treturn next_wakeup;\n}\n\nJEMALLOC_ALWAYS_INLINE void\nbackground_thread_wakeup_time_set(tsdn_t *tsdn, background_thread_info_t *info,\n    uint64_t wakeup_time) {\n\tmalloc_mutex_assert_owner(tsdn, &info->mtx);\n\tatomic_store_b(&info->indefinite_sleep,\n\t    wakeup_time == BACKGROUND_THREAD_INDEFINITE_SLEEP, ATOMIC_RELEASE);\n\tnstime_init(&info->next_wakeup, wakeup_time);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nbackground_thread_indefinite_sleep(background_thread_info_t *info) {\n\treturn atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE);\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena,\n    bool is_background_thread) {\n\tif (!background_thread_enabled() || is_background_thread) {\n\t\treturn;\n\t}\n\tbackground_thread_info_t *info =\n\t    arena_background_thread_info_get(arena);\n\tif (background_thread_indefinite_sleep(info)) {\n\t\tbackground_thread_interval_check(tsdn, arena,\n\t\t    &arena->decay_dirty, 0);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/background_thread_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H\n#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H\n\n/* This file really combines \"structs\" and \"types\", but only transitionally. */\n\n#if defined(JEMALLOC_BACKGROUND_THREAD) || defined(JEMALLOC_LAZY_LOCK)\n#  define JEMALLOC_PTHREAD_CREATE_WRAPPER\n#endif\n\n#define BACKGROUND_THREAD_INDEFINITE_SLEEP UINT64_MAX\n#define MAX_BACKGROUND_THREAD_LIMIT MALLOCX_ARENA_LIMIT\n\ntypedef enum {\n\tbackground_thread_stopped,\n\tbackground_thread_started,\n\t/* Thread waits on the global lock when paused (for arena_reset). */\n\tbackground_thread_paused,\n} background_thread_state_t;\n\nstruct background_thread_info_s {\n#ifdef JEMALLOC_BACKGROUND_THREAD\n\t/* Background thread is pthread specific. */\n\tpthread_t\t\tthread;\n\tpthread_cond_t\t\tcond;\n#endif\n\tmalloc_mutex_t\t\tmtx;\n\tbackground_thread_state_t\tstate;\n\t/* When true, it means no wakeup scheduled. */\n\tatomic_b_t\t\tindefinite_sleep;\n\t/* Next scheduled wakeup time (absolute time in ns). */\n\tnstime_t\t\tnext_wakeup;\n\t/*\n\t *  Since the last background thread run, newly added number of pages\n\t *  that need to be purged by the next wakeup.  This is adjusted on\n\t *  epoch advance, and is used to determine whether we should signal the\n\t *  background thread to wake up earlier.\n\t */\n\tsize_t\t\t\tnpages_to_purge_new;\n\t/* Stats: total number of runs since started. */\n\tuint64_t\t\ttot_n_runs;\n\t/* Stats: total sleep time since started. */\n\tnstime_t\t\ttot_sleep_time;\n};\ntypedef struct background_thread_info_s background_thread_info_t;\n\nstruct background_thread_stats_s {\n\tsize_t num_threads;\n\tuint64_t num_runs;\n\tnstime_t run_interval;\n};\ntypedef struct background_thread_stats_s background_thread_stats_t;\n\n#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_EXTERNS_H\n#define JEMALLOC_INTERNAL_BASE_EXTERNS_H\n\nextern metadata_thp_mode_t opt_metadata_thp;\nextern const char *metadata_thp_mode_names[];\n\nbase_t *b0get(void);\nbase_t *base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);\nvoid base_delete(tsdn_t *tsdn, base_t *base);\nextent_hooks_t *base_extent_hooks_get(base_t *base);\nextent_hooks_t *base_extent_hooks_set(base_t *base,\n    extent_hooks_t *extent_hooks);\nvoid *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);\nextent_t *base_alloc_extent(tsdn_t *tsdn, base_t *base);\nvoid base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,\n    size_t *resident, size_t *mapped, size_t *n_thp);\nvoid base_prefork(tsdn_t *tsdn, base_t *base);\nvoid base_postfork_parent(tsdn_t *tsdn, base_t *base);\nvoid base_postfork_child(tsdn_t *tsdn, base_t *base);\nbool base_boot(tsdn_t *tsdn);\n\n#endif /* JEMALLOC_INTERNAL_BASE_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_INLINES_H\n#define JEMALLOC_INTERNAL_BASE_INLINES_H\n\nstatic inline unsigned\nbase_ind_get(const base_t *base) {\n\treturn base->ind;\n}\n\nstatic inline bool\nmetadata_thp_enabled(void) {\n\treturn (opt_metadata_thp != metadata_thp_disabled);\n}\n#endif /* JEMALLOC_INTERNAL_BASE_INLINES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_STRUCTS_H\n#define JEMALLOC_INTERNAL_BASE_STRUCTS_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\n/* Embedded at the beginning of every block of base-managed virtual memory. */\nstruct base_block_s {\n\t/* Total size of block's virtual memory mapping. */\n\tsize_t\t\tsize;\n\n\t/* Next block in list of base's blocks. */\n\tbase_block_t\t*next;\n\n\t/* Tracks unused trailing space. */\n\textent_t\textent;\n};\n\nstruct base_s {\n\t/* Associated arena's index within the arenas array. */\n\tunsigned\tind;\n\n\t/*\n\t * User-configurable extent hook functions.  Points to an\n\t * extent_hooks_t.\n\t */\n\tatomic_p_t\textent_hooks;\n\n\t/* Protects base_alloc() and base_stats_get() operations. */\n\tmalloc_mutex_t\tmtx;\n\n\t/* Using THP when true (metadata_thp auto mode). */\n\tbool\t\tauto_thp_switched;\n\t/*\n\t * Most recent size class in the series of increasingly large base\n\t * extents.  Logarithmic spacing between subsequent allocations ensures\n\t * that the total number of distinct mappings remains small.\n\t */\n\tpszind_t\tpind_last;\n\n\t/* Serial number generation state. */\n\tsize_t\t\textent_sn_next;\n\n\t/* Chain of all blocks associated with base. */\n\tbase_block_t\t*blocks;\n\n\t/* Heap of extents that track unused trailing space within blocks. */\n\textent_heap_t\tavail[NSIZES];\n\n\t/* Stats, only maintained if config_stats. */\n\tsize_t\t\tallocated;\n\tsize_t\t\tresident;\n\tsize_t\t\tmapped;\n\t/* Number of THP regions touched. */\n\tsize_t\t\tn_thp;\n};\n\n#endif /* JEMALLOC_INTERNAL_BASE_STRUCTS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_TYPES_H\n#define JEMALLOC_INTERNAL_BASE_TYPES_H\n\ntypedef struct base_block_s base_block_t;\ntypedef struct base_s base_t;\n\n#define METADATA_THP_DEFAULT metadata_thp_disabled\n\n/*\n * In auto mode, arenas switch to huge pages for the base allocator on the\n * second base block.  a0 switches to thp on the 5th block (after 20 megabytes\n * of metadata), since more metadata (e.g. rtree nodes) come from a0's base.\n */\n\n#define BASE_AUTO_THP_THRESHOLD    2\n#define BASE_AUTO_THP_THRESHOLD_A0 5\n\ntypedef enum {\n\tmetadata_thp_disabled   = 0,\n\t/*\n\t * Lazily enable hugepage for metadata. To avoid high RSS caused by THP\n\t * + low usage arena (i.e. THP becomes a significant percentage), the\n\t * \"auto\" option only starts using THP after a base allocator used up\n\t * the first THP region.  Starting from the second hugepage (in a single\n\t * arena), \"auto\" behaves the same as \"always\", i.e. madvise hugepage\n\t * right away.\n\t */\n\tmetadata_thp_auto       = 1,\n\tmetadata_thp_always     = 2,\n\tmetadata_thp_mode_limit = 3\n} metadata_thp_mode_t;\n\n#endif /* JEMALLOC_INTERNAL_BASE_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bin.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BIN_H\n#define JEMALLOC_INTERNAL_BIN_H\n\n#include \"jemalloc/internal/extent_types.h\"\n#include \"jemalloc/internal/extent_structs.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/bin_stats.h\"\n\n/*\n * A bin contains a set of extents that are currently being used for slab\n * allocations.\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 slab has the following layout:\n *\n *   /--------------------\\\n *   | region 0           |\n *   |--------------------|\n *   | region 1           |\n *   |--------------------|\n *   | ...                |\n *   | ...                |\n *   | ...                |\n *   |--------------------|\n *   | region nregs-1     |\n *   \\--------------------/\n */\ntypedef struct bin_info_s bin_info_t;\nstruct bin_info_s {\n\t/* Size of regions in a slab for this bin's size class. */\n\tsize_t\t\t\treg_size;\n\n\t/* Total size of a slab for this bin's size class. */\n\tsize_t\t\t\tslab_size;\n\n\t/* Total number of regions in a slab for this bin's size class. */\n\tuint32_t\t\tnregs;\n\n\t/*\n\t * Metadata used to manipulate bitmaps for slabs associated with this\n\t * bin.\n\t */\n\tbitmap_info_t\t\tbitmap_info;\n};\n\nextern const bin_info_t bin_infos[NBINS];\n\n\ntypedef struct bin_s bin_t;\nstruct bin_s {\n\t/* All operations on bin_t fields require lock ownership. */\n\tmalloc_mutex_t\t\tlock;\n\n\t/*\n\t * Current slab being used to service allocations of this bin's size\n\t * class.  slabcur is independent of slabs_{nonfull,full}; whenever\n\t * slabcur is reassigned, the previous slab must be deallocated or\n\t * inserted into slabs_{nonfull,full}.\n\t */\n\textent_t\t\t*slabcur;\n\n\t/*\n\t * Heap of non-full slabs.  This heap is used to assure that new\n\t * allocations come from the non-full slab that is oldest/lowest in\n\t * memory.\n\t */\n\textent_heap_t\t\tslabs_nonfull;\n\n\t/* List used to track full slabs. */\n\textent_list_t\t\tslabs_full;\n\n\t/* Bin statistics. */\n\tbin_stats_t\tstats;\n};\n\n/* Initializes a bin to empty.  Returns true on error. */\nbool bin_init(bin_t *bin);\n\n/* Forking. */\nvoid bin_prefork(tsdn_t *tsdn, bin_t *bin);\nvoid bin_postfork_parent(tsdn_t *tsdn, bin_t *bin);\nvoid bin_postfork_child(tsdn_t *tsdn, bin_t *bin);\n\n/* Stats. */\nstatic inline void\nbin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tmalloc_mutex_prof_read(tsdn, &dst_bin_stats->mutex_data, &bin->lock);\n\tdst_bin_stats->nmalloc += bin->stats.nmalloc;\n\tdst_bin_stats->ndalloc += bin->stats.ndalloc;\n\tdst_bin_stats->nrequests += bin->stats.nrequests;\n\tdst_bin_stats->curregs += bin->stats.curregs;\n\tdst_bin_stats->nfills += bin->stats.nfills;\n\tdst_bin_stats->nflushes += bin->stats.nflushes;\n\tdst_bin_stats->nslabs += bin->stats.nslabs;\n\tdst_bin_stats->reslabs += bin->stats.reslabs;\n\tdst_bin_stats->curslabs += bin->stats.curslabs;\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n}\n\n#endif /* JEMALLOC_INTERNAL_BIN_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bin_stats.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BIN_STATS_H\n#define JEMALLOC_INTERNAL_BIN_STATS_H\n\n#include \"jemalloc/internal/mutex_prof.h\"\n\ntypedef struct bin_stats_s bin_stats_t;\nstruct bin_stats_s {\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/*\n\t * Current number of regions of this size class, including regions\n\t * currently cached by tcache.\n\t */\n\tsize_t\t\tcurregs;\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 slabs created for this bin's size class. */\n\tuint64_t\tnslabs;\n\n\t/*\n\t * Total number of slabs reused by extracting them from the slabs heap\n\t * for this bin's size class.\n\t */\n\tuint64_t\treslabs;\n\n\t/* Current number of slabs in this bin. */\n\tsize_t\t\tcurslabs;\n\n\tmutex_prof_data_t mutex_data;\n};\n\n#endif /* JEMALLOC_INTERNAL_BIN_STATS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bit_util.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BIT_UTIL_H\n#define JEMALLOC_INTERNAL_BIT_UTIL_H\n\n#include \"jemalloc/internal/assert.h\"\n\n#define BIT_UTIL_INLINE static inline\n\n/* Sanity check. */\n#if !defined(JEMALLOC_INTERNAL_FFSLL) || !defined(JEMALLOC_INTERNAL_FFSL) \\\n    || !defined(JEMALLOC_INTERNAL_FFS)\n#  error JEMALLOC_INTERNAL_FFS{,L,LL} should have been defined by configure\n#endif\n\n\nBIT_UTIL_INLINE unsigned\nffs_llu(unsigned long long bitmap) {\n\treturn JEMALLOC_INTERNAL_FFSLL(bitmap);\n}\n\nBIT_UTIL_INLINE unsigned\nffs_lu(unsigned long bitmap) {\n\treturn JEMALLOC_INTERNAL_FFSL(bitmap);\n}\n\nBIT_UTIL_INLINE unsigned\nffs_u(unsigned bitmap) {\n\treturn JEMALLOC_INTERNAL_FFS(bitmap);\n}\n\nBIT_UTIL_INLINE unsigned\nffs_zu(size_t bitmap) {\n#if LG_SIZEOF_PTR == LG_SIZEOF_INT\n\treturn ffs_u(bitmap);\n#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG\n\treturn ffs_lu(bitmap);\n#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG\n\treturn ffs_llu(bitmap);\n#else\n#error No implementation for size_t ffs()\n#endif\n}\n\nBIT_UTIL_INLINE unsigned\nffs_u64(uint64_t bitmap) {\n#if LG_SIZEOF_LONG == 3\n\treturn ffs_lu(bitmap);\n#elif LG_SIZEOF_LONG_LONG == 3\n\treturn ffs_llu(bitmap);\n#else\n#error No implementation for 64-bit ffs()\n#endif\n}\n\nBIT_UTIL_INLINE unsigned\nffs_u32(uint32_t bitmap) {\n#if LG_SIZEOF_INT == 2\n\treturn ffs_u(bitmap);\n#else\n#error No implementation for 32-bit ffs()\n#endif\n\treturn ffs_u(bitmap);\n}\n\nBIT_UTIL_INLINE uint64_t\npow2_ceil_u64(uint64_t x) {\n\tx--;\n\tx |= x >> 1;\n\tx |= x >> 2;\n\tx |= x >> 4;\n\tx |= x >> 8;\n\tx |= x >> 16;\n\tx |= x >> 32;\n\tx++;\n\treturn x;\n}\n\nBIT_UTIL_INLINE uint32_t\npow2_ceil_u32(uint32_t x) {\n\tx--;\n\tx |= x >> 1;\n\tx |= x >> 2;\n\tx |= x >> 4;\n\tx |= x >> 8;\n\tx |= x >> 16;\n\tx++;\n\treturn x;\n}\n\n/* Compute the smallest power of 2 that is >= x. */\nBIT_UTIL_INLINE size_t\npow2_ceil_zu(size_t x) {\n#if (LG_SIZEOF_PTR == 3)\n\treturn pow2_ceil_u64(x);\n#else\n\treturn pow2_ceil_u32(x);\n#endif\n}\n\n#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tsize_t ret;\n\tassert(x != 0);\n\n\tasm (\"bsr %1, %0\"\n\t    : \"=r\"(ret) // Outputs.\n\t    : \"r\"(x)    // Inputs.\n\t    );\n\tassert(ret < UINT_MAX);\n\treturn (unsigned)ret;\n}\n#elif (defined(_MSC_VER))\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tunsigned long ret;\n\n\tassert(x != 0);\n\n#if (LG_SIZEOF_PTR == 3)\n\t_BitScanReverse64(&ret, x);\n#elif (LG_SIZEOF_PTR == 2)\n\t_BitScanReverse(&ret, x);\n#else\n#  error \"Unsupported type size for lg_floor()\"\n#endif\n\tassert(ret < UINT_MAX);\n\treturn (unsigned)ret;\n}\n#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tassert(x != 0);\n\n#if (LG_SIZEOF_PTR == LG_SIZEOF_INT)\n\treturn ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x);\n#elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG)\n\treturn ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clzl(x);\n#else\n#  error \"Unsupported type size for lg_floor()\"\n#endif\n}\n#else\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tassert(x != 0);\n\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\tif (x == SIZE_T_MAX) {\n\t\treturn (8 << LG_SIZEOF_PTR) - 1;\n\t}\n\tx++;\n\treturn ffs_zu(x) - 2;\n}\n#endif\n\n#undef BIT_UTIL_INLINE\n\n#endif /* JEMALLOC_INTERNAL_BIT_UTIL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bitmap.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BITMAP_H\n#define JEMALLOC_INTERNAL_BITMAP_H\n\n#include \"jemalloc/internal/arena_types.h\"\n#include \"jemalloc/internal/bit_util.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\ntypedef unsigned long bitmap_t;\n#define LG_SIZEOF_BITMAP\tLG_SIZEOF_LONG\n\n/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */\n#if LG_SLAB_MAXREGS > LG_CEIL_NSIZES\n/* Maximum bitmap bit count is determined by maximum regions per slab. */\n#  define LG_BITMAP_MAXBITS\tLG_SLAB_MAXREGS\n#else\n/* Maximum bitmap bit count is determined by number of extent size classes. */\n#  define LG_BITMAP_MAXBITS\tLG_CEIL_NSIZES\n#endif\n#define BITMAP_MAXBITS\t\t(ZU(1) << LG_BITMAP_MAXBITS)\n\n/* Number of bits per group. */\n#define LG_BITMAP_GROUP_NBITS\t\t(LG_SIZEOF_BITMAP + 3)\n#define BITMAP_GROUP_NBITS\t\t(1U << LG_BITMAP_GROUP_NBITS)\n#define BITMAP_GROUP_NBITS_MASK\t\t(BITMAP_GROUP_NBITS-1)\n\n/*\n * Do some analysis on how big the bitmap is before we use a tree.  For a brute\n * force linear search, if we would have to call ffs_lu() more than 2^3 times,\n * use a tree instead.\n */\n#if LG_BITMAP_MAXBITS - LG_BITMAP_GROUP_NBITS > 3\n#  define BITMAP_USE_TREE\n#endif\n\n/* Number of groups required to store a given number of bits. */\n#define BITMAP_BITS2GROUPS(nbits)\t\t\t\t\t\\\n    (((nbits) + BITMAP_GROUP_NBITS_MASK) >> LG_BITMAP_GROUP_NBITS)\n\n/*\n * Number of groups required at a particular level for a given number of bits.\n */\n#define BITMAP_GROUPS_L0(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(nbits)\n#define BITMAP_GROUPS_L1(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(nbits))\n#define BITMAP_GROUPS_L2(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))\n#define BITMAP_GROUPS_L3(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(\t\t\\\n\tBITMAP_BITS2GROUPS((nbits)))))\n#define BITMAP_GROUPS_L4(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(\t\t\\\n\tBITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))))\n\n/*\n * Assuming the number of levels, number of groups required for a given number\n * of bits.\n */\n#define BITMAP_GROUPS_1_LEVEL(nbits)\t\t\t\t\t\\\n    BITMAP_GROUPS_L0(nbits)\n#define BITMAP_GROUPS_2_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_1_LEVEL(nbits) + BITMAP_GROUPS_L1(nbits))\n#define BITMAP_GROUPS_3_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_2_LEVEL(nbits) + BITMAP_GROUPS_L2(nbits))\n#define BITMAP_GROUPS_4_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_3_LEVEL(nbits) + BITMAP_GROUPS_L3(nbits))\n#define BITMAP_GROUPS_5_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_4_LEVEL(nbits) + BITMAP_GROUPS_L4(nbits))\n\n/*\n * Maximum number of groups required to support LG_BITMAP_MAXBITS.\n */\n#ifdef BITMAP_USE_TREE\n\n#if LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_1_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_1_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 2\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_2_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_2_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 3\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_3_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_3_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 4\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_4_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_4_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 5\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_5_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_5_LEVEL(BITMAP_MAXBITS)\n#else\n#  error \"Unsupported bitmap size\"\n#endif\n\n/*\n * Maximum number of levels possible.  This could be statically computed based\n * on LG_BITMAP_MAXBITS:\n *\n * #define BITMAP_MAX_LEVELS \\\n *     (LG_BITMAP_MAXBITS / LG_SIZEOF_BITMAP) \\\n *     + !!(LG_BITMAP_MAXBITS % LG_SIZEOF_BITMAP)\n *\n * However, that would not allow the generic BITMAP_INFO_INITIALIZER() macro, so\n * instead hardcode BITMAP_MAX_LEVELS to the largest number supported by the\n * various cascading macros.  The only additional cost this incurs is some\n * unused trailing entries in bitmap_info_t structures; the bitmaps themselves\n * are not impacted.\n */\n#define BITMAP_MAX_LEVELS\t5\n\n#define BITMAP_INFO_INITIALIZER(nbits) {\t\t\t\t\\\n\t/* nbits. */\t\t\t\t\t\t\t\\\n\tnbits,\t\t\t\t\t\t\t\t\\\n\t/* nlevels. */\t\t\t\t\t\t\t\\\n\t(BITMAP_GROUPS_L0(nbits) > BITMAP_GROUPS_L1(nbits)) +\t\t\\\n\t    (BITMAP_GROUPS_L1(nbits) > BITMAP_GROUPS_L2(nbits)) +\t\\\n\t    (BITMAP_GROUPS_L2(nbits) > BITMAP_GROUPS_L3(nbits)) +\t\\\n\t    (BITMAP_GROUPS_L3(nbits) > BITMAP_GROUPS_L4(nbits)) + 1,\t\\\n\t/* levels. */\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\\\n\t\t{0},\t\t\t\t\t\t\t\\\n\t\t{BITMAP_GROUPS_L0(nbits)},\t\t\t\t\\\n\t\t{BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)},\t\\\n\t\t{BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits) +\t\\\n\t\t    BITMAP_GROUPS_L0(nbits)},\t\t\t\t\\\n\t\t{BITMAP_GROUPS_L3(nbits) + BITMAP_GROUPS_L2(nbits) +\t\\\n\t\t    BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)},\t\\\n\t\t{BITMAP_GROUPS_L4(nbits) + BITMAP_GROUPS_L3(nbits) +\t\\\n\t\t     BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits)\t\\\n\t\t     + BITMAP_GROUPS_L0(nbits)}\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n\n#else /* BITMAP_USE_TREE */\n\n#define BITMAP_GROUPS(nbits)\tBITMAP_BITS2GROUPS(nbits)\n#define BITMAP_GROUPS_MAX\tBITMAP_BITS2GROUPS(BITMAP_MAXBITS)\n\n#define BITMAP_INFO_INITIALIZER(nbits) {\t\t\t\t\\\n\t/* nbits. */\t\t\t\t\t\t\t\\\n\tnbits,\t\t\t\t\t\t\t\t\\\n\t/* ngroups. */\t\t\t\t\t\t\t\\\n\tBITMAP_BITS2GROUPS(nbits)\t\t\t\t\t\\\n}\n\n#endif /* BITMAP_USE_TREE */\n\ntypedef struct bitmap_level_s {\n\t/* Offset of this level's groups within the array of groups. */\n\tsize_t group_offset;\n} bitmap_level_t;\n\ntypedef struct bitmap_info_s {\n\t/* Logical number of bits in bitmap (stored at bottom level). */\n\tsize_t nbits;\n\n#ifdef BITMAP_USE_TREE\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#else /* BITMAP_USE_TREE */\n\t/* Number of groups necessary for nbits. */\n\tsize_t ngroups;\n#endif /* BITMAP_USE_TREE */\n} bitmap_info_t;\n\nvoid bitmap_info_init(bitmap_info_t *binfo, size_t nbits);\nvoid bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill);\nsize_t bitmap_size(const bitmap_info_t *binfo);\n\nstatic inline bool\nbitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo) {\n#ifdef BITMAP_USE_TREE\n\tsize_t 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#else\n\tsize_t i;\n\n\tfor (i = 0; i < binfo->ngroups; i++) {\n\t\tif (bitmap[i] != 0) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n#endif\n}\n\nstatic inline bool\nbitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {\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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));\n}\n\nstatic inline void\nbitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {\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));\n\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\tgp = &bitmap[goff];\n\tg = *gp;\n\tassert(g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));\n\tg ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);\n\t*gp = g;\n\tassert(bitmap_get(bitmap, binfo, bit));\n#ifdef BITMAP_USE_TREE\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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));\n\t\t\tg ^= ZU(1) << (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\t}\n\t\t}\n\t}\n#endif\n}\n\n/* ffu: find first unset >= bit. */\nstatic inline size_t\nbitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {\n\tassert(min_bit < binfo->nbits);\n\n#ifdef BITMAP_USE_TREE\n\tsize_t bit = 0;\n\tfor (unsigned level = binfo->nlevels; level--;) {\n\t\tsize_t lg_bits_per_group = (LG_BITMAP_GROUP_NBITS * (level +\n\t\t    1));\n\t\tbitmap_t group = bitmap[binfo->levels[level].group_offset + (bit\n\t\t    >> lg_bits_per_group)];\n\t\tunsigned group_nmask = (unsigned)(((min_bit > bit) ? (min_bit -\n\t\t    bit) : 0) >> (lg_bits_per_group - LG_BITMAP_GROUP_NBITS));\n\t\tassert(group_nmask <= BITMAP_GROUP_NBITS);\n\t\tbitmap_t group_mask = ~((1LU << group_nmask) - 1);\n\t\tbitmap_t group_masked = group & group_mask;\n\t\tif (group_masked == 0LU) {\n\t\t\tif (group == 0LU) {\n\t\t\t\treturn binfo->nbits;\n\t\t\t}\n\t\t\t/*\n\t\t\t * min_bit was preceded by one or more unset bits in\n\t\t\t * this group, but there are no other unset bits in this\n\t\t\t * group.  Try again starting at the first bit of the\n\t\t\t * next sibling.  This will recurse at most once per\n\t\t\t * non-root level.\n\t\t\t */\n\t\t\tsize_t sib_base = bit + (ZU(1) << lg_bits_per_group);\n\t\t\tassert(sib_base > min_bit);\n\t\t\tassert(sib_base > bit);\n\t\t\tif (sib_base >= binfo->nbits) {\n\t\t\t\treturn binfo->nbits;\n\t\t\t}\n\t\t\treturn bitmap_ffu(bitmap, binfo, sib_base);\n\t\t}\n\t\tbit += ((size_t)(ffs_lu(group_masked) - 1)) <<\n\t\t    (lg_bits_per_group - LG_BITMAP_GROUP_NBITS);\n\t}\n\tassert(bit >= min_bit);\n\tassert(bit < binfo->nbits);\n\treturn bit;\n#else\n\tsize_t i = min_bit >> LG_BITMAP_GROUP_NBITS;\n\tbitmap_t g = bitmap[i] & ~((1LU << (min_bit & BITMAP_GROUP_NBITS_MASK))\n\t    - 1);\n\tsize_t bit;\n\tdo {\n\t\tbit = ffs_lu(g);\n\t\tif (bit != 0) {\n\t\t\treturn (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);\n\t\t}\n\t\ti++;\n\t\tg = bitmap[i];\n\t} while (i < binfo->ngroups);\n\treturn binfo->nbits;\n#endif\n}\n\n/* sfu: set first unset. */\nstatic inline size_t\nbitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {\n\tsize_t bit;\n\tbitmap_t g;\n\tunsigned i;\n\n\tassert(!bitmap_full(bitmap, binfo));\n\n#ifdef BITMAP_USE_TREE\n\ti = binfo->nlevels - 1;\n\tg = bitmap[binfo->levels[i].group_offset];\n\tbit = ffs_lu(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) + (ffs_lu(g) - 1);\n\t}\n#else\n\ti = 0;\n\tg = bitmap[0];\n\twhile ((bit = ffs_lu(g)) == 0) {\n\t\ti++;\n\t\tg = bitmap[i];\n\t}\n\tbit = (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);\n#endif\n\tbitmap_set(bitmap, binfo, bit);\n\treturn bit;\n}\n\nstatic inline void\nbitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {\n\tsize_t goff;\n\tbitmap_t *gp;\n\tbitmap_t g;\n\tUNUSED bool 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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK))) == 0);\n\tg ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);\n\t*gp = g;\n\tassert(!bitmap_get(bitmap, binfo, bit));\n#ifdef BITMAP_USE_TREE\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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)))\n\t\t\t    == 0);\n\t\t\tg ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);\n\t\t\t*gp = g;\n\t\t\tif (!propagate) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n#endif /* BITMAP_USE_TREE */\n}\n\n#endif /* JEMALLOC_INTERNAL_BITMAP_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/cache_bin.h",
    "content": "#ifndef JEMALLOC_INTERNAL_CACHE_BIN_H\n#define JEMALLOC_INTERNAL_CACHE_BIN_H\n\n#include \"jemalloc/internal/ql.h\"\n\n/*\n * The cache_bins are the mechanism that the tcache and the arena use to\n * communicate.  The tcache fills from and flushes to the arena by passing a\n * cache_bin_t to fill/flush.  When the arena needs to pull stats from the\n * tcaches associated with it, it does so by iterating over its\n * cache_bin_array_descriptor_t objects and reading out per-bin stats it\n * contains.  This makes it so that the arena need not know about the existence\n * of the tcache at all.\n */\n\n\n/*\n * The count of the number of cached allocations in a bin.  We make this signed\n * so that negative numbers can encode \"invalid\" states (e.g. a low water mark\n * of -1 for a cache that has been depleted).\n */\ntypedef int32_t cache_bin_sz_t;\n\ntypedef struct cache_bin_stats_s cache_bin_stats_t;\nstruct cache_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 nrequests;\n};\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 */\ntypedef struct cache_bin_info_s cache_bin_info_t;\nstruct cache_bin_info_s {\n\t/* Upper limit on ncached. */\n\tcache_bin_sz_t ncached_max;\n};\n\ntypedef struct cache_bin_s cache_bin_t;\nstruct cache_bin_s {\n\t/* Min # cached since last GC. */\n\tcache_bin_sz_t low_water;\n\t/* # of cached objects. */\n\tcache_bin_sz_t ncached;\n\t/*\n\t * ncached and stats are both modified frequently.  Let's keep them\n\t * close so that they have a higher chance of being on the same\n\t * cacheline, thus less write-backs.\n\t */\n\tcache_bin_stats_t tstats;\n\t/*\n\t * Stack of available objects.\n\t *\n\t * To make use of adjacent cacheline prefetch, the items in the avail\n\t * stack goes to higher address for newer allocations.  avail points\n\t * just above the available space, which means that\n\t * avail[-ncached, ... -1] are available items and the lowest item will\n\t * be allocated first.\n\t */\n\tvoid **avail;\n};\n\ntypedef struct cache_bin_array_descriptor_s cache_bin_array_descriptor_t;\nstruct cache_bin_array_descriptor_s {\n\t/*\n\t * The arena keeps a list of the cache bins associated with it, for\n\t * stats collection.\n\t */\n\tql_elm(cache_bin_array_descriptor_t) link;\n\t/* Pointers to the tcache bins. */\n\tcache_bin_t *bins_small;\n\tcache_bin_t *bins_large;\n};\n\nstatic inline void\ncache_bin_array_descriptor_init(cache_bin_array_descriptor_t *descriptor,\n    cache_bin_t *bins_small, cache_bin_t *bins_large) {\n\tql_elm_new(descriptor, link);\n\tdescriptor->bins_small = bins_small;\n\tdescriptor->bins_large = bins_large;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\ncache_bin_alloc_easy(cache_bin_t *bin, bool *success) {\n\tvoid *ret;\n\n\tif (unlikely(bin->ncached == 0)) {\n\t\tbin->low_water = -1;\n\t\t*success = false;\n\t\treturn NULL;\n\t}\n\t/*\n\t * success (instead of ret) should be checked upon the return of this\n\t * function.  We avoid checking (ret == NULL) because there is never a\n\t * null stored on the avail stack (which is unknown to the compiler),\n\t * and eagerly checking ret would cause pipeline stall (waiting for the\n\t * cacheline).\n\t */\n\t*success = true;\n\tret = *(bin->avail - bin->ncached);\n\tbin->ncached--;\n\n\tif (unlikely(bin->ncached < bin->low_water)) {\n\t\tbin->low_water = bin->ncached;\n\t}\n\n\treturn ret;\n}\n\n#endif /* JEMALLOC_INTERNAL_CACHE_BIN_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ckh.h",
    "content": "#ifndef JEMALLOC_INTERNAL_CKH_H\n#define JEMALLOC_INTERNAL_CKH_H\n\n#include \"jemalloc/internal/tsd.h\"\n\n/* Cuckoo hashing implementation.  Skip to the end for the interface. */\n\n/******************************************************************************/\n/* INTERNAL DEFINITIONS -- IGNORE */\n/******************************************************************************/\n\n/* Maintain counters used to get an idea of performance. */\n/* #define CKH_COUNT */\n/* Print counter values in ckh_delete() (requires CKH_COUNT). */\n/* #define CKH_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/* Typedefs to allow easy function pointer passing. */\ntypedef void ckh_hash_t (const void *, size_t[2]);\ntypedef bool ckh_keycomp_t (const void *, const void *);\n\n/* Hash table cell. */\ntypedef struct {\n\tconst void *key;\n\tconst void *data;\n} ckhc_t;\n\n/* The hash table itself. */\ntypedef struct {\n#ifdef CKH_COUNT\n\t/* Counters used to get an idea of performance. */\n\tuint64_t ngrows;\n\tuint64_t nshrinks;\n\tuint64_t nshrinkfails;\n\tuint64_t ninserts;\n\tuint64_t nrelocs;\n#endif\n\n\t/* Used for pseudo-random number generation. */\n\tuint64_t prng_state;\n\n\t/* Total number of items. */\n\tsize_t count;\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 lg_minbuckets;\n\tunsigned lg_curbuckets;\n\n\t/* Hash and comparison functions. */\n\tckh_hash_t *hash;\n\tckh_keycomp_t *keycomp;\n\n\t/* Hash table with 2^lg_curbuckets buckets. */\n\tckhc_t *tab;\n} ckh_t;\n\n/******************************************************************************/\n/* BEGIN PUBLIC API */\n/******************************************************************************/\n\n/* Lifetime management.  Minitems is the initial capacity. */\nbool ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,\n    ckh_keycomp_t *keycomp);\nvoid ckh_delete(tsd_t *tsd, ckh_t *ckh);\n\n/* Get the number of elements in the set. */\nsize_t ckh_count(ckh_t *ckh);\n\n/*\n * To iterate over the elements in the table, initialize *tabind to 0 and call\n * this function until it returns true.  Each call that returns false will\n * update *key and *data to the next element in the table, assuming the pointers\n * are non-NULL.\n */\nbool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data);\n\n/*\n * Basic hash table operations -- insert, removal, lookup.  For ckh_remove and\n * ckh_search, key or data can be NULL.  The hash-table only stores pointers to\n * the key and value, and doesn't do any lifetime management.\n */\nbool ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data);\nbool ckh_remove(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key,\n    void **data);\nbool ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data);\n\n/* Some useful hash and comparison functions for strings and pointers. */\nvoid ckh_string_hash(const void *key, size_t r_hash[2]);\nbool ckh_string_keycomp(const void *k1, const void *k2);\nvoid ckh_pointer_hash(const void *key, size_t r_hash[2]);\nbool ckh_pointer_keycomp(const void *k1, const void *k2);\n\n#endif /* JEMALLOC_INTERNAL_CKH_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ctl.h",
    "content": "#ifndef JEMALLOC_INTERNAL_CTL_H\n#define JEMALLOC_INTERNAL_CTL_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n\n/* Maximum ctl tree depth. */\n#define CTL_MAX_DEPTH\t7\n\ntypedef struct ctl_node_s {\n\tbool named;\n} ctl_node_t;\n\ntypedef struct ctl_named_node_s {\n\tctl_node_t node;\n\tconst char *name;\n\t/* If (nchildren == 0), this is a terminal node. */\n\tsize_t nchildren;\n\tconst ctl_node_t *children;\n\tint (*ctl)(tsd_t *, const size_t *, size_t, void *, size_t *, void *,\n\t    size_t);\n} ctl_named_node_t;\n\ntypedef struct ctl_indexed_node_s {\n\tstruct ctl_node_s node;\n\tconst ctl_named_node_t *(*index)(tsdn_t *, const size_t *, size_t,\n\t    size_t);\n} ctl_indexed_node_t;\n\ntypedef struct ctl_arena_stats_s {\n\tarena_stats_t astats;\n\n\t/* Aggregate stats for small size classes, based on bin stats. */\n\tsize_t allocated_small;\n\tuint64_t nmalloc_small;\n\tuint64_t ndalloc_small;\n\tuint64_t nrequests_small;\n\n\tbin_stats_t bstats[NBINS];\n\tarena_stats_large_t lstats[NSIZES - NBINS];\n} ctl_arena_stats_t;\n\ntypedef struct ctl_stats_s {\n\tsize_t allocated;\n\tsize_t active;\n\tsize_t metadata;\n\tsize_t metadata_thp;\n\tsize_t resident;\n\tsize_t mapped;\n\tsize_t retained;\n\n\tbackground_thread_stats_t background_thread;\n\tmutex_prof_data_t mutex_prof_data[mutex_prof_num_global_mutexes];\n} ctl_stats_t;\n\ntypedef struct ctl_arena_s ctl_arena_t;\nstruct ctl_arena_s {\n\tunsigned arena_ind;\n\tbool initialized;\n\tql_elm(ctl_arena_t) destroyed_link;\n\n\t/* Basic stats, supported even if !config_stats. */\n\tunsigned nthreads;\n\tconst char *dss;\n\tssize_t dirty_decay_ms;\n\tssize_t muzzy_decay_ms;\n\tsize_t pactive;\n\tsize_t pdirty;\n\tsize_t pmuzzy;\n\n\t/* NULL if !config_stats. */\n\tctl_arena_stats_t *astats;\n};\n\ntypedef struct ctl_arenas_s {\n\tuint64_t epoch;\n\tunsigned narenas;\n\tql_head(ctl_arena_t) destroyed;\n\n\t/*\n\t * Element 0 corresponds to merged stats for extant arenas (accessed via\n\t * MALLCTL_ARENAS_ALL), element 1 corresponds to merged stats for\n\t * destroyed arenas (accessed via MALLCTL_ARENAS_DESTROYED), and the\n\t * remaining MALLOCX_ARENA_LIMIT elements correspond to arenas.\n\t */\n\tctl_arena_t *arenas[2 + MALLOCX_ARENA_LIMIT];\n} ctl_arenas_t;\n\nint ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen);\nint ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp);\n\nint ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen);\nbool ctl_boot(void);\nvoid ctl_prefork(tsdn_t *tsdn);\nvoid ctl_postfork_parent(tsdn_t *tsdn);\nvoid ctl_postfork_child(tsdn_t *tsdn);\n\n#define xmallctl(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 xmallctlnametomib(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 xmallctlbymib(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_INTERNAL_CTL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/div.h",
    "content": "#ifndef JEMALLOC_INTERNAL_DIV_H\n#define JEMALLOC_INTERNAL_DIV_H\n\n#include \"jemalloc/internal/assert.h\"\n\n/*\n * This module does the division that computes the index of a region in a slab,\n * given its offset relative to the base.\n * That is, given a divisor d, an n = i * d (all integers), we'll return i.\n * We do some pre-computation to do this more quickly than a CPU division\n * instruction.\n * We bound n < 2^32, and don't support dividing by one.\n */\n\ntypedef struct div_info_s div_info_t;\nstruct div_info_s {\n\tuint32_t magic;\n#ifdef JEMALLOC_DEBUG\n\tsize_t d;\n#endif\n};\n\nvoid div_init(div_info_t *div_info, size_t divisor);\n\nstatic inline size_t\ndiv_compute(div_info_t *div_info, size_t n) {\n\tassert(n <= (uint32_t)-1);\n\t/*\n\t * This generates, e.g. mov; imul; shr on x86-64. On a 32-bit machine,\n\t * the compilers I tried were all smart enough to turn this into the\n\t * appropriate \"get the high 32 bits of the result of a multiply\" (e.g.\n\t * mul; mov edx eax; on x86, umull on arm, etc.).\n\t */\n\tsize_t i = ((uint64_t)n * (uint64_t)div_info->magic) >> 32;\n#ifdef JEMALLOC_DEBUG\n\tassert(i * div_info->d == n);\n#endif\n\treturn i;\n}\n\n#endif /* JEMALLOC_INTERNAL_DIV_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/emitter.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EMITTER_H\n#define JEMALLOC_INTERNAL_EMITTER_H\n\n#include \"jemalloc/internal/ql.h\"\n\ntypedef enum emitter_output_e emitter_output_t;\nenum emitter_output_e {\n\temitter_output_json,\n\temitter_output_table\n};\n\ntypedef enum emitter_justify_e emitter_justify_t;\nenum emitter_justify_e {\n\temitter_justify_left,\n\temitter_justify_right,\n\t/* Not for users; just to pass to internal functions. */\n\temitter_justify_none\n};\n\ntypedef enum emitter_type_e emitter_type_t;\nenum emitter_type_e {\n\temitter_type_bool,\n\temitter_type_int,\n\temitter_type_unsigned,\n\temitter_type_uint32,\n\temitter_type_uint64,\n\temitter_type_size,\n\temitter_type_ssize,\n\temitter_type_string,\n\t/*\n\t * A title is a column title in a table; it's just a string, but it's\n\t * not quoted.\n\t */\n\temitter_type_title,\n};\n\ntypedef struct emitter_col_s emitter_col_t;\nstruct emitter_col_s {\n\t/* Filled in by the user. */\n\temitter_justify_t justify;\n\tint width;\n\temitter_type_t type;\n\tunion {\n\t\tbool bool_val;\n\t\tint int_val;\n\t\tunsigned unsigned_val;\n\t\tuint32_t uint32_val;\n\t\tuint64_t uint64_val;\n\t\tsize_t size_val;\n\t\tssize_t ssize_val;\n\t\tconst char *str_val;\n\t};\n\n\t/* Filled in by initialization. */\n\tql_elm(emitter_col_t) link;\n};\n\ntypedef struct emitter_row_s emitter_row_t;\nstruct emitter_row_s {\n\tql_head(emitter_col_t) cols;\n};\n\nstatic inline void\nemitter_row_init(emitter_row_t *row) {\n\tql_new(&row->cols);\n}\n\nstatic inline void\nemitter_col_init(emitter_col_t *col, emitter_row_t *row) {\n\tql_elm_new(col, link);\n\tql_tail_insert(&row->cols, col, link);\n}\n\ntypedef struct emitter_s emitter_t;\nstruct emitter_s {\n\temitter_output_t output;\n\t/* The output information. */\n\tvoid (*write_cb)(void *, const char *);\n\tvoid *cbopaque;\n\tint nesting_depth;\n\t/* True if we've already emitted a value at the given depth. */\n\tbool item_at_depth;\n};\n\nstatic inline void\nemitter_init(emitter_t *emitter, emitter_output_t emitter_output,\n    void (*write_cb)(void *, const char *), void *cbopaque) {\n\temitter->output = emitter_output;\n\temitter->write_cb = write_cb;\n\temitter->cbopaque = cbopaque;\n\temitter->item_at_depth = false;\n\temitter->nesting_depth = 0;\n}\n\n/* Internal convenience function.  Write to the emitter the given string. */\nJEMALLOC_FORMAT_PRINTF(2, 3)\nstatic inline void\nemitter_printf(emitter_t *emitter, const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);\n\tva_end(ap);\n}\n\n/* Write to the emitter the given string, but only in table mode. */\nJEMALLOC_FORMAT_PRINTF(2, 3)\nstatic inline void\nemitter_table_printf(emitter_t *emitter, const char *format, ...) {\n\tif (emitter->output == emitter_output_table) {\n\t\tva_list ap;\n\t\tva_start(ap, format);\n\t\tmalloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);\n\t\tva_end(ap);\n\t}\n}\n\nstatic inline void\nemitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,\n    emitter_justify_t justify, int width) {\n\tsize_t written;\n\tif (justify == emitter_justify_none) {\n\t\twritten = malloc_snprintf(out_fmt, out_size,\n\t\t    \"%%%s\", fmt_specifier);\n\t} else if (justify == emitter_justify_left) {\n\t\twritten = malloc_snprintf(out_fmt, out_size,\n\t\t    \"%%-%d%s\", width, fmt_specifier);\n\t} else {\n\t\twritten = malloc_snprintf(out_fmt, out_size,\n\t\t    \"%%%d%s\", width, fmt_specifier);\n\t}\n\t/* Only happens in case of bad format string, which *we* choose. */\n\tassert(written <  out_size);\n}\n\n/*\n * Internal.  Emit the given value type in the relevant encoding (so that the\n * bool true gets mapped to json \"true\", but the string \"true\" gets mapped to\n * json \"\\\"true\\\"\", for instance.\n *\n * Width is ignored if justify is emitter_justify_none.\n */\nstatic inline void\nemitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,\n    emitter_type_t value_type, const void *value) {\n\tsize_t str_written;\n#define BUF_SIZE 256\n#define FMT_SIZE 10\n\t/*\n\t * We dynamically generate a format string to emit, to let us use the\n\t * snprintf machinery.  This is kinda hacky, but gets the job done\n\t * quickly without having to think about the various snprintf edge\n\t * cases.\n\t */\n\tchar fmt[FMT_SIZE];\n\tchar buf[BUF_SIZE];\n\n#define EMIT_SIMPLE(type, format)\t\t\t\t\t\\\n\temitter_gen_fmt(fmt, FMT_SIZE, format, justify, width);\t\t\\\n\temitter_printf(emitter, fmt, *(const type *)value);\t\t\\\n\n\tswitch (value_type) {\n\tcase emitter_type_bool:\n\t\temitter_gen_fmt(fmt, FMT_SIZE, \"s\", justify, width);\n\t\temitter_printf(emitter, fmt, *(const bool *)value ?\n\t\t    \"true\" : \"false\");\n\t\tbreak;\n\tcase emitter_type_int:\n\t\tEMIT_SIMPLE(int, \"d\")\n\t\tbreak;\n\tcase emitter_type_unsigned:\n\t\tEMIT_SIMPLE(unsigned, \"u\")\n\t\tbreak;\n\tcase emitter_type_ssize:\n\t\tEMIT_SIMPLE(ssize_t, \"zd\")\n\t\tbreak;\n\tcase emitter_type_size:\n\t\tEMIT_SIMPLE(size_t, \"zu\")\n\t\tbreak;\n\tcase emitter_type_string:\n\t\tstr_written = malloc_snprintf(buf, BUF_SIZE, \"\\\"%s\\\"\",\n\t\t    *(const char *const *)value);\n\t\t/*\n\t\t * We control the strings we output; we shouldn't get anything\n\t\t * anywhere near the fmt size.\n\t\t */\n\t\tassert(str_written < BUF_SIZE);\n\t\temitter_gen_fmt(fmt, FMT_SIZE, \"s\", justify, width);\n\t\temitter_printf(emitter, fmt, buf);\n\t\tbreak;\n\tcase emitter_type_uint32:\n\t\tEMIT_SIMPLE(uint32_t, FMTu32)\n\t\tbreak;\n\tcase emitter_type_uint64:\n\t\tEMIT_SIMPLE(uint64_t, FMTu64)\n\t\tbreak;\n\tcase emitter_type_title:\n\t\tEMIT_SIMPLE(char *const, \"s\");\n\t\tbreak;\n\tdefault:\n\t\tunreachable();\n\t}\n#undef BUF_SIZE\n#undef FMT_SIZE\n}\n\n\n/* Internal functions.  In json mode, tracks nesting state. */\nstatic inline void\nemitter_nest_inc(emitter_t *emitter) {\n\temitter->nesting_depth++;\n\temitter->item_at_depth = false;\n}\n\nstatic inline void\nemitter_nest_dec(emitter_t *emitter) {\n\temitter->nesting_depth--;\n\temitter->item_at_depth = true;\n}\n\nstatic inline void\nemitter_indent(emitter_t *emitter) {\n\tint amount = emitter->nesting_depth;\n\tconst char *indent_str;\n\tif (emitter->output == emitter_output_json) {\n\t\tindent_str = \"\\t\";\n\t} else {\n\t\tamount *= 2;\n\t\tindent_str = \" \";\n\t}\n\tfor (int i = 0; i < amount; i++) {\n\t\temitter_printf(emitter, \"%s\", indent_str);\n\t}\n}\n\nstatic inline void\nemitter_json_key_prefix(emitter_t *emitter) {\n\temitter_printf(emitter, \"%s\\n\", emitter->item_at_depth ? \",\" : \"\");\n\temitter_indent(emitter);\n}\n\nstatic inline void\nemitter_begin(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth == 0);\n\t\temitter_printf(emitter, \"{\");\n\t\temitter_nest_inc(emitter);\n\t} else {\n\t\t// tabular init\n\t\temitter_printf(emitter, \"%s\", \"\");\n\t}\n}\n\nstatic inline void\nemitter_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth == 1);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n}\\n\");\n\t}\n}\n\n/*\n * Note emits a different kv pair as well, but only in table mode.  Omits the\n * note if table_note_key is NULL.\n */\nstatic inline void\nemitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,\n    emitter_type_t value_type, const void *value,\n    const char *table_note_key, emitter_type_t table_note_value_type,\n    const void *table_note_value) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"\\\"%s\\\": \", json_key);\n\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t    value_type, value);\n\t} else {\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"%s: \", table_key);\n\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t    value_type, value);\n\t\tif (table_note_key != NULL) {\n\t\t\temitter_printf(emitter, \" (%s: \", table_note_key);\n\t\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t\t    table_note_value_type, table_note_value);\n\t\t\temitter_printf(emitter, \")\");\n\t\t}\n\t\temitter_printf(emitter, \"\\n\");\n\t}\n\temitter->item_at_depth = true;\n}\n\nstatic inline void\nemitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,\n    emitter_type_t value_type, const void *value) {\n\temitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,\n\t    emitter_type_bool, NULL);\n}\n\nstatic inline void\nemitter_json_kv(emitter_t *emitter, const char *json_key,\n    emitter_type_t value_type, const void *value) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_kv(emitter, json_key, NULL, value_type, value);\n\t}\n}\n\nstatic inline void\nemitter_table_kv(emitter_t *emitter, const char *table_key,\n    emitter_type_t value_type, const void *value) {\n\tif (emitter->output == emitter_output_table) {\n\t\temitter_kv(emitter, NULL, table_key, value_type, value);\n\t}\n}\n\nstatic inline void\nemitter_dict_begin(emitter_t *emitter, const char *json_key,\n    const char *table_header) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"\\\"%s\\\": {\", json_key);\n\t\temitter_nest_inc(emitter);\n\t} else {\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"%s\\n\", table_header);\n\t\temitter_nest_inc(emitter);\n\t}\n}\n\nstatic inline void\nemitter_dict_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n\");\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"}\");\n\t} else {\n\t\temitter_nest_dec(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_dict_begin(emitter_t *emitter, const char *json_key) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_dict_begin(emitter, json_key, NULL);\n\t}\n}\n\nstatic inline void\nemitter_json_dict_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_dict_end(emitter);\n\t}\n}\n\nstatic inline void\nemitter_table_dict_begin(emitter_t *emitter, const char *table_key) {\n\tif (emitter->output == emitter_output_table) {\n\t\temitter_dict_begin(emitter, NULL, table_key);\n\t}\n}\n\nstatic inline void\nemitter_table_dict_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_table) {\n\t\temitter_dict_end(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_arr_begin(emitter_t *emitter, const char *json_key) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"\\\"%s\\\": [\", json_key);\n\t\temitter_nest_inc(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_arr_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n\");\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"]\");\n\t}\n}\n\nstatic inline void\nemitter_json_arr_obj_begin(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"{\");\n\t\temitter_nest_inc(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_arr_obj_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n\");\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"}\");\n\t}\n}\n\nstatic inline void\nemitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,\n    const void *value) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t    value_type, value);\n\t}\n}\n\nstatic inline void\nemitter_table_row(emitter_t *emitter, emitter_row_t *row) {\n\tif (emitter->output != emitter_output_table) {\n\t\treturn;\n\t}\n\temitter_col_t *col;\n\tql_foreach(col, &row->cols, link) {\n\t\temitter_print_value(emitter, col->justify, col->width,\n\t\t    col->type, (const void *)&col->bool_val);\n\t}\n\temitter_table_printf(emitter, \"\\n\");\n}\n\n#endif /* JEMALLOC_INTERNAL_EMITTER_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_dss.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_DSS_H\n#define JEMALLOC_INTERNAL_EXTENT_DSS_H\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 DSS_PREC_DEFAULT dss_prec_secondary\n#define DSS_DEFAULT \"secondary\"\n\nextern const char *dss_prec_names[];\n\nextern const char *opt_dss;\n\ndss_prec_t extent_dss_prec_get(void);\nbool extent_dss_prec_set(dss_prec_t dss_prec);\nvoid *extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit);\nbool extent_in_dss(void *addr);\nbool extent_dss_mergeable(void *addr_a, void *addr_b);\nvoid extent_dss_boot(void);\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_DSS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_EXTERNS_H\n#define JEMALLOC_INTERNAL_EXTENT_EXTERNS_H\n\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n#include \"jemalloc/internal/ph.h\"\n#include \"jemalloc/internal/rtree.h\"\n\nextern size_t opt_lg_extent_max_active_fit;\n\nextern rtree_t extents_rtree;\nextern const extent_hooks_t extent_hooks_default;\nextern mutex_pool_t extent_mutex_pool;\n\nextent_t *extent_alloc(tsdn_t *tsdn, arena_t *arena);\nvoid extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent);\n\nextent_hooks_t *extent_hooks_get(arena_t *arena);\nextent_hooks_t *extent_hooks_set(tsd_t *tsd, arena_t *arena,\n    extent_hooks_t *extent_hooks);\n\n#ifdef JEMALLOC_JET\nsize_t extent_size_quantize_floor(size_t size);\nsize_t extent_size_quantize_ceil(size_t size);\n#endif\n\nrb_proto(, extent_avail_, extent_tree_t, extent_t)\nph_proto(, extent_heap_, extent_heap_t, extent_t)\n\nbool extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,\n    bool delay_coalesce);\nextent_state_t extents_state_get(const extents_t *extents);\nsize_t extents_npages_get(extents_t *extents);\nextent_t *extents_alloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,\n    size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,\n    bool *zero, bool *commit);\nvoid extents_dalloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent);\nextent_t *extents_evict(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_min);\nvoid extents_prefork(tsdn_t *tsdn, extents_t *extents);\nvoid extents_postfork_parent(tsdn_t *tsdn, extents_t *extents);\nvoid extents_postfork_child(tsdn_t *tsdn, extents_t *extents);\nextent_t *extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit);\nvoid extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent);\nvoid extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent);\nvoid extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent);\nbool extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nbool extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nbool extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nbool extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nextent_t *extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b);\nbool extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b);\n\nbool extent_boot(void);\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_INLINES_H\n#define JEMALLOC_INTERNAL_EXTENT_INLINES_H\n\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n#include \"jemalloc/internal/pages.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/sz.h\"\n\nstatic inline void\nextent_lock(tsdn_t *tsdn, extent_t *extent) {\n\tassert(extent != NULL);\n\tmutex_pool_lock(tsdn, &extent_mutex_pool, (uintptr_t)extent);\n}\n\nstatic inline void\nextent_unlock(tsdn_t *tsdn, extent_t *extent) {\n\tassert(extent != NULL);\n\tmutex_pool_unlock(tsdn, &extent_mutex_pool, (uintptr_t)extent);\n}\n\nstatic inline void\nextent_lock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {\n\tassert(extent1 != NULL && extent2 != NULL);\n\tmutex_pool_lock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,\n\t    (uintptr_t)extent2);\n}\n\nstatic inline void\nextent_unlock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {\n\tassert(extent1 != NULL && extent2 != NULL);\n\tmutex_pool_unlock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,\n\t    (uintptr_t)extent2);\n}\n\nstatic inline arena_t *\nextent_arena_get(const extent_t *extent) {\n\tunsigned arena_ind = (unsigned)((extent->e_bits &\n\t    EXTENT_BITS_ARENA_MASK) >> EXTENT_BITS_ARENA_SHIFT);\n\t/*\n\t * The following check is omitted because we should never actually read\n\t * a NULL arena pointer.\n\t */\n\tif (false && arena_ind >= MALLOCX_ARENA_LIMIT) {\n\t\treturn NULL;\n\t}\n\tassert(arena_ind < MALLOCX_ARENA_LIMIT);\n\treturn (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_ACQUIRE);\n}\n\nstatic inline szind_t\nextent_szind_get_maybe_invalid(const extent_t *extent) {\n\tszind_t szind = (szind_t)((extent->e_bits & EXTENT_BITS_SZIND_MASK) >>\n\t    EXTENT_BITS_SZIND_SHIFT);\n\tassert(szind <= NSIZES);\n\treturn szind;\n}\n\nstatic inline szind_t\nextent_szind_get(const extent_t *extent) {\n\tszind_t szind = extent_szind_get_maybe_invalid(extent);\n\tassert(szind < NSIZES); /* Never call when \"invalid\". */\n\treturn szind;\n}\n\nstatic inline size_t\nextent_usize_get(const extent_t *extent) {\n\treturn sz_index2size(extent_szind_get(extent));\n}\n\nstatic inline size_t\nextent_sn_get(const extent_t *extent) {\n\treturn (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>\n\t    EXTENT_BITS_SN_SHIFT);\n}\n\nstatic inline extent_state_t\nextent_state_get(const extent_t *extent) {\n\treturn (extent_state_t)((extent->e_bits & EXTENT_BITS_STATE_MASK) >>\n\t    EXTENT_BITS_STATE_SHIFT);\n}\n\nstatic inline bool\nextent_zeroed_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_ZEROED_MASK) >>\n\t    EXTENT_BITS_ZEROED_SHIFT);\n}\n\nstatic inline bool\nextent_committed_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_COMMITTED_MASK) >>\n\t    EXTENT_BITS_COMMITTED_SHIFT);\n}\n\nstatic inline bool\nextent_dumpable_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_DUMPABLE_MASK) >>\n\t    EXTENT_BITS_DUMPABLE_SHIFT);\n}\n\nstatic inline bool\nextent_slab_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_SLAB_MASK) >>\n\t    EXTENT_BITS_SLAB_SHIFT);\n}\n\nstatic inline unsigned\nextent_nfree_get(const extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\treturn (unsigned)((extent->e_bits & EXTENT_BITS_NFREE_MASK) >>\n\t    EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void *\nextent_base_get(const extent_t *extent) {\n\tassert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||\n\t    !extent_slab_get(extent));\n\treturn PAGE_ADDR2BASE(extent->e_addr);\n}\n\nstatic inline void *\nextent_addr_get(const extent_t *extent) {\n\tassert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||\n\t    !extent_slab_get(extent));\n\treturn extent->e_addr;\n}\n\nstatic inline size_t\nextent_size_get(const extent_t *extent) {\n\treturn (extent->e_size_esn & EXTENT_SIZE_MASK);\n}\n\nstatic inline size_t\nextent_esn_get(const extent_t *extent) {\n\treturn (extent->e_size_esn & EXTENT_ESN_MASK);\n}\n\nstatic inline size_t\nextent_bsize_get(const extent_t *extent) {\n\treturn extent->e_bsize;\n}\n\nstatic inline void *\nextent_before_get(const extent_t *extent) {\n\treturn (void *)((uintptr_t)extent_base_get(extent) - PAGE);\n}\n\nstatic inline void *\nextent_last_get(const extent_t *extent) {\n\treturn (void *)((uintptr_t)extent_base_get(extent) +\n\t    extent_size_get(extent) - PAGE);\n}\n\nstatic inline void *\nextent_past_get(const extent_t *extent) {\n\treturn (void *)((uintptr_t)extent_base_get(extent) +\n\t    extent_size_get(extent));\n}\n\nstatic inline arena_slab_data_t *\nextent_slab_data_get(extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\treturn &extent->e_slab_data;\n}\n\nstatic inline const arena_slab_data_t *\nextent_slab_data_get_const(const extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\treturn &extent->e_slab_data;\n}\n\nstatic inline prof_tctx_t *\nextent_prof_tctx_get(const extent_t *extent) {\n\treturn (prof_tctx_t *)atomic_load_p(&extent->e_prof_tctx,\n\t    ATOMIC_ACQUIRE);\n}\n\nstatic inline void\nextent_arena_set(extent_t *extent, arena_t *arena) {\n\tunsigned arena_ind = (arena != NULL) ? arena_ind_get(arena) : ((1U <<\n\t    MALLOCX_ARENA_BITS) - 1);\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_ARENA_MASK) |\n\t    ((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);\n}\n\nstatic inline void\nextent_addr_set(extent_t *extent, void *addr) {\n\textent->e_addr = addr;\n}\n\nstatic inline void\nextent_addr_randomize(UNUSED tsdn_t *tsdn, extent_t *extent, size_t alignment) {\n\tassert(extent_base_get(extent) == extent_addr_get(extent));\n\n\tif (alignment < PAGE) {\n\t\tunsigned lg_range = LG_PAGE -\n\t\t    lg_floor(CACHELINE_CEILING(alignment));\n\t\tsize_t r;\n\t\tif (!tsdn_null(tsdn)) {\n\t\t\ttsd_t *tsd = tsdn_tsd(tsdn);\n\t\t\tr = (size_t)prng_lg_range_u64(\n\t\t\t    tsd_offset_statep_get(tsd), lg_range);\n\t\t} else {\n\t\t\tr = prng_lg_range_zu(\n\t\t\t    &extent_arena_get(extent)->offset_state,\n\t\t\t    lg_range, true);\n\t\t}\n\t\tuintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -\n\t\t    lg_range);\n\t\textent->e_addr = (void *)((uintptr_t)extent->e_addr +\n\t\t    random_offset);\n\t\tassert(ALIGNMENT_ADDR2BASE(extent->e_addr, alignment) ==\n\t\t    extent->e_addr);\n\t}\n}\n\nstatic inline void\nextent_size_set(extent_t *extent, size_t size) {\n\tassert((size & ~EXTENT_SIZE_MASK) == 0);\n\textent->e_size_esn = size | (extent->e_size_esn & ~EXTENT_SIZE_MASK);\n}\n\nstatic inline void\nextent_esn_set(extent_t *extent, size_t esn) {\n\textent->e_size_esn = (extent->e_size_esn & ~EXTENT_ESN_MASK) | (esn &\n\t    EXTENT_ESN_MASK);\n}\n\nstatic inline void\nextent_bsize_set(extent_t *extent, size_t bsize) {\n\textent->e_bsize = bsize;\n}\n\nstatic inline void\nextent_szind_set(extent_t *extent, szind_t szind) {\n\tassert(szind <= NSIZES); /* NSIZES means \"invalid\". */\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_SZIND_MASK) |\n\t    ((uint64_t)szind << EXTENT_BITS_SZIND_SHIFT);\n}\n\nstatic inline void\nextent_nfree_set(extent_t *extent, unsigned nfree) {\n\tassert(extent_slab_get(extent));\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_NFREE_MASK) |\n\t    ((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void\nextent_nfree_inc(extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\textent->e_bits += ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void\nextent_nfree_dec(extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\textent->e_bits -= ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void\nextent_sn_set(extent_t *extent, size_t sn) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_SN_MASK) |\n\t    ((uint64_t)sn << EXTENT_BITS_SN_SHIFT);\n}\n\nstatic inline void\nextent_state_set(extent_t *extent, extent_state_t state) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_STATE_MASK) |\n\t    ((uint64_t)state << EXTENT_BITS_STATE_SHIFT);\n}\n\nstatic inline void\nextent_zeroed_set(extent_t *extent, bool zeroed) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_ZEROED_MASK) |\n\t    ((uint64_t)zeroed << EXTENT_BITS_ZEROED_SHIFT);\n}\n\nstatic inline void\nextent_committed_set(extent_t *extent, bool committed) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_COMMITTED_MASK) |\n\t    ((uint64_t)committed << EXTENT_BITS_COMMITTED_SHIFT);\n}\n\nstatic inline void\nextent_dumpable_set(extent_t *extent, bool dumpable) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_DUMPABLE_MASK) |\n\t    ((uint64_t)dumpable << EXTENT_BITS_DUMPABLE_SHIFT);\n}\n\nstatic inline void\nextent_slab_set(extent_t *extent, bool slab) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_SLAB_MASK) |\n\t    ((uint64_t)slab << EXTENT_BITS_SLAB_SHIFT);\n}\n\nstatic inline void\nextent_prof_tctx_set(extent_t *extent, prof_tctx_t *tctx) {\n\tatomic_store_p(&extent->e_prof_tctx, tctx, ATOMIC_RELEASE);\n}\n\nstatic inline void\nextent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,\n    bool slab, szind_t szind, size_t sn, extent_state_t state, bool zeroed,\n    bool committed, bool dumpable) {\n\tassert(addr == PAGE_ADDR2BASE(addr) || !slab);\n\n\textent_arena_set(extent, arena);\n\textent_addr_set(extent, addr);\n\textent_size_set(extent, size);\n\textent_slab_set(extent, slab);\n\textent_szind_set(extent, szind);\n\textent_sn_set(extent, sn);\n\textent_state_set(extent, state);\n\textent_zeroed_set(extent, zeroed);\n\textent_committed_set(extent, committed);\n\textent_dumpable_set(extent, dumpable);\n\tql_elm_new(extent, ql_link);\n\tif (config_prof) {\n\t\textent_prof_tctx_set(extent, NULL);\n\t}\n}\n\nstatic inline void\nextent_binit(extent_t *extent, void *addr, size_t bsize, size_t sn) {\n\textent_arena_set(extent, NULL);\n\textent_addr_set(extent, addr);\n\textent_bsize_set(extent, bsize);\n\textent_slab_set(extent, false);\n\textent_szind_set(extent, NSIZES);\n\textent_sn_set(extent, sn);\n\textent_state_set(extent, extent_state_active);\n\textent_zeroed_set(extent, true);\n\textent_committed_set(extent, true);\n\textent_dumpable_set(extent, true);\n}\n\nstatic inline void\nextent_list_init(extent_list_t *list) {\n\tql_new(list);\n}\n\nstatic inline extent_t *\nextent_list_first(const extent_list_t *list) {\n\treturn ql_first(list);\n}\n\nstatic inline extent_t *\nextent_list_last(const extent_list_t *list) {\n\treturn ql_last(list, ql_link);\n}\n\nstatic inline void\nextent_list_append(extent_list_t *list, extent_t *extent) {\n\tql_tail_insert(list, extent, ql_link);\n}\n\nstatic inline void\nextent_list_prepend(extent_list_t *list, extent_t *extent) {\n\tql_head_insert(list, extent, ql_link);\n}\n\nstatic inline void\nextent_list_replace(extent_list_t *list, extent_t *to_remove,\n    extent_t *to_insert) {\n\tql_after_insert(to_remove, to_insert, ql_link);\n\tql_remove(list, to_remove, ql_link);\n}\n\nstatic inline void\nextent_list_remove(extent_list_t *list, extent_t *extent) {\n\tql_remove(list, extent, ql_link);\n}\n\nstatic inline int\nextent_sn_comp(const extent_t *a, const extent_t *b) {\n\tsize_t a_sn = extent_sn_get(a);\n\tsize_t b_sn = extent_sn_get(b);\n\n\treturn (a_sn > b_sn) - (a_sn < b_sn);\n}\n\nstatic inline int\nextent_esn_comp(const extent_t *a, const extent_t *b) {\n\tsize_t a_esn = extent_esn_get(a);\n\tsize_t b_esn = extent_esn_get(b);\n\n\treturn (a_esn > b_esn) - (a_esn < b_esn);\n}\n\nstatic inline int\nextent_ad_comp(const extent_t *a, const extent_t *b) {\n\tuintptr_t a_addr = (uintptr_t)extent_addr_get(a);\n\tuintptr_t b_addr = (uintptr_t)extent_addr_get(b);\n\n\treturn (a_addr > b_addr) - (a_addr < b_addr);\n}\n\nstatic inline int\nextent_ead_comp(const extent_t *a, const extent_t *b) {\n\tuintptr_t a_eaddr = (uintptr_t)a;\n\tuintptr_t b_eaddr = (uintptr_t)b;\n\n\treturn (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);\n}\n\nstatic inline int\nextent_snad_comp(const extent_t *a, const extent_t *b) {\n\tint ret;\n\n\tret = extent_sn_comp(a, b);\n\tif (ret != 0) {\n\t\treturn ret;\n\t}\n\n\tret = extent_ad_comp(a, b);\n\treturn ret;\n}\n\nstatic inline int\nextent_esnead_comp(const extent_t *a, const extent_t *b) {\n\tint ret;\n\n\tret = extent_esn_comp(a, b);\n\tif (ret != 0) {\n\t\treturn ret;\n\t}\n\n\tret = extent_ead_comp(a, b);\n\treturn ret;\n}\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_INLINES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_mmap.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H\n#define JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H\n\nextern bool opt_retain;\n\nvoid *extent_alloc_mmap(void *new_addr, size_t size, size_t alignment,\n    bool *zero, bool *commit);\nbool extent_dalloc_mmap(void *addr, size_t size);\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_STRUCTS_H\n#define JEMALLOC_INTERNAL_EXTENT_STRUCTS_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/ph.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\ntypedef enum {\n\textent_state_active   = 0,\n\textent_state_dirty    = 1,\n\textent_state_muzzy    = 2,\n\textent_state_retained = 3\n} extent_state_t;\n\n/* Extent (span of pages).  Use accessor functions for e_* fields. */\nstruct extent_s {\n\t/*\n\t * Bitfield containing several fields:\n\t *\n\t * a: arena_ind\n\t * b: slab\n\t * c: committed\n\t * d: dumpable\n\t * z: zeroed\n\t * t: state\n\t * i: szind\n\t * f: nfree\n\t * n: sn\n\t *\n\t * nnnnnnnn ... nnnnffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa\n\t *\n\t * arena_ind: Arena from which this extent came, or all 1 bits if\n\t *            unassociated.\n\t *\n\t * slab: The slab flag indicates whether the extent is used for a slab\n\t *       of small regions.  This helps differentiate small size classes,\n\t *       and it indicates whether interior pointers can be looked up via\n\t *       iealloc().\n\t *\n\t * committed: The committed flag indicates whether physical memory is\n\t *            committed to the extent, whether explicitly or implicitly\n\t *            as on a system that overcommits and satisfies physical\n\t *            memory needs on demand via soft page faults.\n\t *\n\t * dumpable: The dumpable flag indicates whether or not we've set the\n\t *           memory in question to be dumpable.  Note that this\n\t *           interacts somewhat subtly with user-specified extent hooks,\n\t *           since we don't know if *they* are fiddling with\n\t *           dumpability (in which case, we don't want to undo whatever\n\t *           they're doing).  To deal with this scenario, we:\n\t *             - Make dumpable false only for memory allocated with the\n\t *               default hooks.\n\t *             - Only allow memory to go from non-dumpable to dumpable,\n\t *               and only once.\n\t *             - Never make the OS call to allow dumping when the\n\t *               dumpable bit is already set.\n\t *           These three constraints mean that we will never\n\t *           accidentally dump user memory that the user meant to set\n\t *           nondumpable with their extent hooks.\n\t *\n\t *\n\t * zeroed: The zeroed flag is used by extent recycling code to track\n\t *         whether memory is zero-filled.\n\t *\n\t * state: The state flag is an extent_state_t.\n\t *\n\t * szind: The szind flag indicates usable size class index for\n\t *        allocations residing in this extent, regardless of whether the\n\t *        extent is a slab.  Extent size and usable size often differ\n\t *        even for non-slabs, either due to sz_large_pad or promotion of\n\t *        sampled small regions.\n\t *\n\t * nfree: Number of free regions in slab.\n\t *\n\t * sn: Serial number (potentially non-unique).\n\t *\n\t *     Serial numbers may wrap around if !opt_retain, but as long as\n\t *     comparison functions fall back on address comparison for equal\n\t *     serial numbers, stable (if imperfect) ordering is maintained.\n\t *\n\t *     Serial numbers may not be unique even in the absence of\n\t *     wrap-around, e.g. when splitting an extent and assigning the same\n\t *     serial number to both resulting adjacent extents.\n\t */\n\tuint64_t\t\te_bits;\n#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))\n\n#define EXTENT_BITS_ARENA_WIDTH  MALLOCX_ARENA_BITS\n#define EXTENT_BITS_ARENA_SHIFT  0\n#define EXTENT_BITS_ARENA_MASK  MASK(EXTENT_BITS_ARENA_WIDTH, EXTENT_BITS_ARENA_SHIFT)\n\n#define EXTENT_BITS_SLAB_WIDTH  1\n#define EXTENT_BITS_SLAB_SHIFT  (EXTENT_BITS_ARENA_WIDTH + EXTENT_BITS_ARENA_SHIFT)\n#define EXTENT_BITS_SLAB_MASK  MASK(EXTENT_BITS_SLAB_WIDTH, EXTENT_BITS_SLAB_SHIFT)\n\n#define EXTENT_BITS_COMMITTED_WIDTH  1\n#define EXTENT_BITS_COMMITTED_SHIFT  (EXTENT_BITS_SLAB_WIDTH + EXTENT_BITS_SLAB_SHIFT)\n#define EXTENT_BITS_COMMITTED_MASK  MASK(EXTENT_BITS_COMMITTED_WIDTH, EXTENT_BITS_COMMITTED_SHIFT)\n\n#define EXTENT_BITS_DUMPABLE_WIDTH  1\n#define EXTENT_BITS_DUMPABLE_SHIFT  (EXTENT_BITS_COMMITTED_WIDTH + EXTENT_BITS_COMMITTED_SHIFT)\n#define EXTENT_BITS_DUMPABLE_MASK  MASK(EXTENT_BITS_DUMPABLE_WIDTH, EXTENT_BITS_DUMPABLE_SHIFT)\n\n#define EXTENT_BITS_ZEROED_WIDTH  1\n#define EXTENT_BITS_ZEROED_SHIFT  (EXTENT_BITS_DUMPABLE_WIDTH + EXTENT_BITS_DUMPABLE_SHIFT)\n#define EXTENT_BITS_ZEROED_MASK  MASK(EXTENT_BITS_ZEROED_WIDTH, EXTENT_BITS_ZEROED_SHIFT)\n\n#define EXTENT_BITS_STATE_WIDTH  2\n#define EXTENT_BITS_STATE_SHIFT  (EXTENT_BITS_ZEROED_WIDTH + EXTENT_BITS_ZEROED_SHIFT)\n#define EXTENT_BITS_STATE_MASK  MASK(EXTENT_BITS_STATE_WIDTH, EXTENT_BITS_STATE_SHIFT)\n\n#define EXTENT_BITS_SZIND_WIDTH  LG_CEIL_NSIZES\n#define EXTENT_BITS_SZIND_SHIFT  (EXTENT_BITS_STATE_WIDTH + EXTENT_BITS_STATE_SHIFT)\n#define EXTENT_BITS_SZIND_MASK  MASK(EXTENT_BITS_SZIND_WIDTH, EXTENT_BITS_SZIND_SHIFT)\n\n#define EXTENT_BITS_NFREE_WIDTH  (LG_SLAB_MAXREGS + 1)\n#define EXTENT_BITS_NFREE_SHIFT  (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)\n#define EXTENT_BITS_NFREE_MASK  MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)\n\n#define EXTENT_BITS_SN_SHIFT  (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)\n#define EXTENT_BITS_SN_MASK  (UINT64_MAX << EXTENT_BITS_SN_SHIFT)\n\n\t/* Pointer to the extent that this structure is responsible for. */\n\tvoid\t\t\t*e_addr;\n\n\tunion {\n\t\t/*\n\t\t * Extent size and serial number associated with the extent\n\t\t * structure (different than the serial number for the extent at\n\t\t * e_addr).\n\t\t *\n\t\t * ssssssss [...] ssssssss ssssnnnn nnnnnnnn\n\t\t */\n\t\tsize_t\t\t\te_size_esn;\n\t#define EXTENT_SIZE_MASK\t((size_t)~(PAGE-1))\n\t#define EXTENT_ESN_MASK\t\t((size_t)PAGE-1)\n\t\t/* Base extent size, which may not be a multiple of PAGE. */\n\t\tsize_t\t\t\te_bsize;\n\t};\n\n\t/*\n\t * List linkage, used by a variety of lists:\n\t * - bin_t's slabs_full\n\t * - extents_t's LRU\n\t * - stashed dirty extents\n\t * - arena's large allocations\n\t */\n\tql_elm(extent_t)\tql_link;\n\n\t/*\n\t * Linkage for per size class sn/address-ordered heaps, and\n\t * for extent_avail\n\t */\n\tphn(extent_t)\t\tph_link;\n\n\tunion {\n\t\t/* Small region slab metadata. */\n\t\tarena_slab_data_t\te_slab_data;\n\n\t\t/*\n\t\t * Profile counters, used for large objects.  Points to a\n\t\t * prof_tctx_t.\n\t\t */\n\t\tatomic_p_t\t\te_prof_tctx;\n\t};\n};\ntypedef ql_head(extent_t) extent_list_t;\ntypedef ph(extent_t) extent_tree_t;\ntypedef ph(extent_t) extent_heap_t;\n\n/* Quantized collection of extents, with built-in LRU queue. */\nstruct extents_s {\n\tmalloc_mutex_t\t\tmtx;\n\n\t/*\n\t * Quantized per size class heaps of extents.\n\t *\n\t * Synchronization: mtx.\n\t */\n\textent_heap_t\t\theaps[NPSIZES+1];\n\n\t/*\n\t * Bitmap for which set bits correspond to non-empty heaps.\n\t *\n\t * Synchronization: mtx.\n\t */\n\tbitmap_t\t\tbitmap[BITMAP_GROUPS(NPSIZES+1)];\n\n\t/*\n\t * LRU of all extents in heaps.\n\t *\n\t * Synchronization: mtx.\n\t */\n\textent_list_t\t\tlru;\n\n\t/*\n\t * Page sum for all extents in heaps.\n\t *\n\t * The synchronization here is a little tricky.  Modifications to npages\n\t * must hold mtx, but reads need not (though, a reader who sees npages\n\t * without holding the mutex can't assume anything about the rest of the\n\t * state of the extents_t).\n\t */\n\tatomic_zu_t\t\tnpages;\n\n\t/* All stored extents must be in the same state. */\n\textent_state_t\t\tstate;\n\n\t/*\n\t * If true, delay coalescing until eviction; otherwise coalesce during\n\t * deallocation.\n\t */\n\tbool\t\t\tdelay_coalesce;\n};\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_STRUCTS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_TYPES_H\n#define JEMALLOC_INTERNAL_EXTENT_TYPES_H\n\ntypedef struct extent_s extent_t;\ntypedef struct extents_s extents_t;\n\n#define EXTENT_HOOKS_INITIALIZER\tNULL\n\n#define EXTENT_GROW_MAX_PIND (NPSIZES - 1)\n\n/*\n * When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)\n * is the max ratio between the size of the active extent and the new extent.\n */\n#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/hash.h",
    "content": "#ifndef JEMALLOC_INTERNAL_HASH_H\n#define JEMALLOC_INTERNAL_HASH_H\n\n#include \"jemalloc/internal/assert.h\"\n\n/*\n * The following hash function is based on MurmurHash3, placed into the public\n * domain by Austin Appleby.  See https://github.com/aappleby/smhasher for\n * details.\n */\n\n/******************************************************************************/\n/* Internal implementation. */\nstatic inline uint32_t\nhash_rotl_32(uint32_t x, int8_t r) {\n\treturn ((x << r) | (x >> (32 - r)));\n}\n\nstatic inline uint64_t\nhash_rotl_64(uint64_t x, int8_t r) {\n\treturn ((x << r) | (x >> (64 - r)));\n}\n\nstatic inline uint32_t\nhash_get_block_32(const uint32_t *p, int i) {\n\t/* Handle unaligned read. */\n\tif (unlikely((uintptr_t)p & (sizeof(uint32_t)-1)) != 0) {\n\t\tuint32_t ret;\n\n\t\tmemcpy(&ret, (uint8_t *)(p + i), sizeof(uint32_t));\n\t\treturn ret;\n\t}\n\n\treturn p[i];\n}\n\nstatic inline uint64_t\nhash_get_block_64(const uint64_t *p, int i) {\n\t/* Handle unaligned read. */\n\tif (unlikely((uintptr_t)p & (sizeof(uint64_t)-1)) != 0) {\n\t\tuint64_t ret;\n\n\t\tmemcpy(&ret, (uint8_t *)(p + i), sizeof(uint64_t));\n\t\treturn ret;\n\t}\n\n\treturn p[i];\n}\n\nstatic inline uint32_t\nhash_fmix_32(uint32_t h) {\n\th ^= h >> 16;\n\th *= 0x85ebca6b;\n\th ^= h >> 13;\n\th *= 0xc2b2ae35;\n\th ^= h >> 16;\n\n\treturn h;\n}\n\nstatic inline uint64_t\nhash_fmix_64(uint64_t k) {\n\tk ^= k >> 33;\n\tk *= KQU(0xff51afd7ed558ccd);\n\tk ^= k >> 33;\n\tk *= KQU(0xc4ceb9fe1a85ec53);\n\tk ^= k >> 33;\n\n\treturn k;\n}\n\nstatic inline uint32_t\nhash_x86_32(const void *key, int len, uint32_t seed) {\n\tconst uint8_t *data = (const uint8_t *) key;\n\tconst int nblocks = len / 4;\n\n\tuint32_t h1 = seed;\n\n\tconst uint32_t c1 = 0xcc9e2d51;\n\tconst uint32_t c2 = 0x1b873593;\n\n\t/* body */\n\t{\n\t\tconst uint32_t *blocks = (const uint32_t *) (data + nblocks*4);\n\t\tint i;\n\n\t\tfor (i = -nblocks; i; i++) {\n\t\t\tuint32_t k1 = hash_get_block_32(blocks, i);\n\n\t\t\tk1 *= c1;\n\t\t\tk1 = hash_rotl_32(k1, 15);\n\t\t\tk1 *= c2;\n\n\t\t\th1 ^= k1;\n\t\t\th1 = hash_rotl_32(h1, 13);\n\t\t\th1 = h1*5 + 0xe6546b64;\n\t\t}\n\t}\n\n\t/* tail */\n\t{\n\t\tconst uint8_t *tail = (const uint8_t *) (data + nblocks*4);\n\n\t\tuint32_t k1 = 0;\n\n\t\tswitch (len & 3) {\n\t\tcase 3: k1 ^= tail[2] << 16;\n\t\tcase 2: k1 ^= tail[1] << 8;\n\t\tcase 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);\n\t\t\tk1 *= c2; h1 ^= k1;\n\t\t}\n\t}\n\n\t/* finalization */\n\th1 ^= len;\n\n\th1 = hash_fmix_32(h1);\n\n\treturn h1;\n}\n\nUNUSED static inline void\nhash_x86_128(const void *key, const int len, uint32_t seed,\n    uint64_t r_out[2]) {\n\tconst uint8_t * data = (const uint8_t *) key;\n\tconst int nblocks = len / 16;\n\n\tuint32_t h1 = seed;\n\tuint32_t h2 = seed;\n\tuint32_t h3 = seed;\n\tuint32_t h4 = seed;\n\n\tconst uint32_t c1 = 0x239b961b;\n\tconst uint32_t c2 = 0xab0e9789;\n\tconst uint32_t c3 = 0x38b34ae5;\n\tconst uint32_t c4 = 0xa1e38b93;\n\n\t/* body */\n\t{\n\t\tconst uint32_t *blocks = (const uint32_t *) (data + nblocks*16);\n\t\tint i;\n\n\t\tfor (i = -nblocks; i; i++) {\n\t\t\tuint32_t k1 = hash_get_block_32(blocks, i*4 + 0);\n\t\t\tuint32_t k2 = hash_get_block_32(blocks, i*4 + 1);\n\t\t\tuint32_t k3 = hash_get_block_32(blocks, i*4 + 2);\n\t\t\tuint32_t k4 = hash_get_block_32(blocks, i*4 + 3);\n\n\t\t\tk1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;\n\n\t\t\th1 = hash_rotl_32(h1, 19); h1 += h2;\n\t\t\th1 = h1*5 + 0x561ccd1b;\n\n\t\t\tk2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;\n\n\t\t\th2 = hash_rotl_32(h2, 17); h2 += h3;\n\t\t\th2 = h2*5 + 0x0bcaa747;\n\n\t\t\tk3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;\n\n\t\t\th3 = hash_rotl_32(h3, 15); h3 += h4;\n\t\t\th3 = h3*5 + 0x96cd1c35;\n\n\t\t\tk4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;\n\n\t\t\th4 = hash_rotl_32(h4, 13); h4 += h1;\n\t\t\th4 = h4*5 + 0x32ac3b17;\n\t\t}\n\t}\n\n\t/* tail */\n\t{\n\t\tconst uint8_t *tail = (const uint8_t *) (data + nblocks*16);\n\t\tuint32_t k1 = 0;\n\t\tuint32_t k2 = 0;\n\t\tuint32_t k3 = 0;\n\t\tuint32_t k4 = 0;\n\n\t\tswitch (len & 15) {\n\t\tcase 15: k4 ^= tail[14] << 16;\n\t\tcase 14: k4 ^= tail[13] << 8;\n\t\tcase 13: k4 ^= tail[12] << 0;\n\t\t\tk4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;\n\n\t\tcase 12: k3 ^= tail[11] << 24;\n\t\tcase 11: k3 ^= tail[10] << 16;\n\t\tcase 10: k3 ^= tail[ 9] << 8;\n\t\tcase  9: k3 ^= tail[ 8] << 0;\n\t\t     k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;\n\n\t\tcase  8: k2 ^= tail[ 7] << 24;\n\t\tcase  7: k2 ^= tail[ 6] << 16;\n\t\tcase  6: k2 ^= tail[ 5] << 8;\n\t\tcase  5: k2 ^= tail[ 4] << 0;\n\t\t\tk2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;\n\n\t\tcase  4: k1 ^= tail[ 3] << 24;\n\t\tcase  3: k1 ^= tail[ 2] << 16;\n\t\tcase  2: k1 ^= tail[ 1] << 8;\n\t\tcase  1: k1 ^= tail[ 0] << 0;\n\t\t\tk1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;\n\t\t}\n\t}\n\n\t/* finalization */\n\th1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;\n\n\th1 += h2; h1 += h3; h1 += h4;\n\th2 += h1; h3 += h1; h4 += h1;\n\n\th1 = hash_fmix_32(h1);\n\th2 = hash_fmix_32(h2);\n\th3 = hash_fmix_32(h3);\n\th4 = hash_fmix_32(h4);\n\n\th1 += h2; h1 += h3; h1 += h4;\n\th2 += h1; h3 += h1; h4 += h1;\n\n\tr_out[0] = (((uint64_t) h2) << 32) | h1;\n\tr_out[1] = (((uint64_t) h4) << 32) | h3;\n}\n\nUNUSED static inline void\nhash_x64_128(const void *key, const int len, const uint32_t seed,\n    uint64_t r_out[2]) {\n\tconst uint8_t *data = (const uint8_t *) key;\n\tconst int nblocks = len / 16;\n\n\tuint64_t h1 = seed;\n\tuint64_t h2 = seed;\n\n\tconst uint64_t c1 = KQU(0x87c37b91114253d5);\n\tconst uint64_t c2 = KQU(0x4cf5ad432745937f);\n\n\t/* body */\n\t{\n\t\tconst uint64_t *blocks = (const uint64_t *) (data);\n\t\tint i;\n\n\t\tfor (i = 0; i < nblocks; i++) {\n\t\t\tuint64_t k1 = hash_get_block_64(blocks, i*2 + 0);\n\t\t\tuint64_t k2 = hash_get_block_64(blocks, i*2 + 1);\n\n\t\t\tk1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;\n\n\t\t\th1 = hash_rotl_64(h1, 27); h1 += h2;\n\t\t\th1 = h1*5 + 0x52dce729;\n\n\t\t\tk2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;\n\n\t\t\th2 = hash_rotl_64(h2, 31); h2 += h1;\n\t\t\th2 = h2*5 + 0x38495ab5;\n\t\t}\n\t}\n\n\t/* tail */\n\t{\n\t\tconst uint8_t *tail = (const uint8_t*)(data + nblocks*16);\n\t\tuint64_t k1 = 0;\n\t\tuint64_t k2 = 0;\n\n\t\tswitch (len & 15) {\n\t\tcase 15: k2 ^= ((uint64_t)(tail[14])) << 48; /* falls through */\n\t\tcase 14: k2 ^= ((uint64_t)(tail[13])) << 40; /* falls through */\n\t\tcase 13: k2 ^= ((uint64_t)(tail[12])) << 32; /* falls through */\n\t\tcase 12: k2 ^= ((uint64_t)(tail[11])) << 24; /* falls through */\n\t\tcase 11: k2 ^= ((uint64_t)(tail[10])) << 16; /* falls through */\n\t\tcase 10: k2 ^= ((uint64_t)(tail[ 9])) << 8;  /* falls through */\n\t\tcase  9: k2 ^= ((uint64_t)(tail[ 8])) << 0;\n\t\t\tk2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;\n\t\t\t/* falls through */\n\t\tcase  8: k1 ^= ((uint64_t)(tail[ 7])) << 56; /* falls through */\n\t\tcase  7: k1 ^= ((uint64_t)(tail[ 6])) << 48; /* falls through */\n\t\tcase  6: k1 ^= ((uint64_t)(tail[ 5])) << 40; /* falls through */\n\t\tcase  5: k1 ^= ((uint64_t)(tail[ 4])) << 32; /* falls through */\n\t\tcase  4: k1 ^= ((uint64_t)(tail[ 3])) << 24; /* falls through */\n\t\tcase  3: k1 ^= ((uint64_t)(tail[ 2])) << 16; /* falls through */\n\t\tcase  2: k1 ^= ((uint64_t)(tail[ 1])) << 8;  /* falls through */\n\t\tcase  1: k1 ^= ((uint64_t)(tail[ 0])) << 0;\n\t\t\tk1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;\n\t\t}\n\t}\n\n\t/* finalization */\n\th1 ^= len; h2 ^= len;\n\n\th1 += h2;\n\th2 += h1;\n\n\th1 = hash_fmix_64(h1);\n\th2 = hash_fmix_64(h2);\n\n\th1 += h2;\n\th2 += h1;\n\n\tr_out[0] = h1;\n\tr_out[1] = h2;\n}\n\n/******************************************************************************/\n/* API. */\nstatic inline void\nhash(const void *key, size_t len, const uint32_t seed, size_t r_hash[2]) {\n\tassert(len <= INT_MAX); /* Unfortunate implementation limitation. */\n\n#if (LG_SIZEOF_PTR == 3 && !defined(JEMALLOC_BIG_ENDIAN))\n\thash_x64_128(key, (int)len, seed, (uint64_t *)r_hash);\n#else\n\t{\n\t\tuint64_t hashes[2];\n\t\thash_x86_128(key, (int)len, seed, hashes);\n\t\tr_hash[0] = (size_t)hashes[0];\n\t\tr_hash[1] = (size_t)hashes[1];\n\t}\n#endif\n}\n\n#endif /* JEMALLOC_INTERNAL_HASH_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/hooks.h",
    "content": "#ifndef JEMALLOC_INTERNAL_HOOKS_H\n#define JEMALLOC_INTERNAL_HOOKS_H\n\nextern JEMALLOC_EXPORT void (*hooks_arena_new_hook)();\nextern JEMALLOC_EXPORT void (*hooks_libc_hook)();\n\n#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)\n\n#define open JEMALLOC_HOOK(open, hooks_libc_hook)\n#define read JEMALLOC_HOOK(read, hooks_libc_hook)\n#define write JEMALLOC_HOOK(write, hooks_libc_hook)\n#define readlink JEMALLOC_HOOK(readlink, hooks_libc_hook)\n#define close JEMALLOC_HOOK(close, hooks_libc_hook)\n#define creat JEMALLOC_HOOK(creat, hooks_libc_hook)\n#define secure_getenv JEMALLOC_HOOK(secure_getenv, hooks_libc_hook)\n/* Note that this is undef'd and re-define'd in src/prof.c. */\n#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)\n\n#endif /* JEMALLOC_INTERNAL_HOOKS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h",
    "content": "#ifndef JEMALLOC_INTERNAL_DECLS_H\n#define JEMALLOC_INTERNAL_DECLS_H\n\n#include <math.h>\n#ifdef _WIN32\n#  include <windows.h>\n#  include \"msvc_compat/windows_extra.h\"\n#  ifdef _WIN64\n#    if LG_VADDR <= 32\n#      error Generate the headers using x64 vcargs\n#    endif\n#  else\n#    if LG_VADDR > 32\n#      undef LG_VADDR\n#      define LG_VADDR 32\n#    endif\n#  endif\n#else\n#  include <sys/param.h>\n#  include <sys/mman.h>\n#  if !defined(__pnacl__) && !defined(__native_client__)\n#    include <sys/syscall.h>\n#    if !defined(SYS_write) && defined(__NR_write)\n#      define SYS_write __NR_write\n#    endif\n#    if defined(SYS_open) && defined(__aarch64__)\n       /* Android headers may define SYS_open to __NR_open even though\n        * __NR_open may not exist on AArch64 (superseded by __NR_openat). */\n#      undef SYS_open\n#    endif\n#    include <sys/uio.h>\n#  endif\n#  include <pthread.h>\n#  include <signal.h>\n#  ifdef JEMALLOC_OS_UNFAIR_LOCK\n#    include <os/lock.h>\n#  endif\n#  ifdef JEMALLOC_GLIBC_MALLOC_HOOK\n#    include <sched.h>\n#  endif\n#  include <errno.h>\n#  include <sys/time.h>\n#  include <time.h>\n#  ifdef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME\n#    include <mach/mach_time.h>\n#  endif\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#ifndef SSIZE_MAX\n#  define SSIZE_MAX\t((ssize_t)(SIZE_T_MAX >> 1))\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 <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#  ifdef JEMALLOC_HAS_RESTRICT\n#    define restrict __restrict\n#  endif\n/* Disable warnings about deprecated system functions. */\n#  pragma warning(disable: 4996)\n#if _MSC_VER < 1800\nstatic int\nisblank(int c) {\n\treturn (c == '\\t' || c == ' ');\n}\n#endif\n#else\n#  include <unistd.h>\n#endif\n#include <fcntl.h>\n\n#endif /* JEMALLOC_INTERNAL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in",
    "content": "#ifndef JEMALLOC_INTERNAL_DEFS_H_\n#define JEMALLOC_INTERNAL_DEFS_H_\n/*\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 * Define overrides for non-standard allocator-related functions if they are\n * present on the system.\n */\n#undef JEMALLOC_OVERRIDE___LIBC_CALLOC\n#undef JEMALLOC_OVERRIDE___LIBC_FREE\n#undef JEMALLOC_OVERRIDE___LIBC_MALLOC\n#undef JEMALLOC_OVERRIDE___LIBC_MEMALIGN\n#undef JEMALLOC_OVERRIDE___LIBC_REALLOC\n#undef JEMALLOC_OVERRIDE___LIBC_VALLOC\n#undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN\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\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/* 1 if CPU_SPINWAIT is defined, 0 otherwise. */\n#undef HAVE_CPU_SPINWAIT\n\n/*\n * Number of significant bits in virtual addresses.  This may be less than the\n * total number of bits in a pointer, e.g. on x64, for which the uppermost 16\n * bits are the same as bit 47.\n */\n#undef LG_VADDR\n\n/* Defined if C11 atomics are available. */\n#undef JEMALLOC_C11_ATOMICS\n\n/* Defined if GCC __atomic atomics are available. */\n#undef JEMALLOC_GCC_ATOMIC_ATOMICS\n\n/* Defined if GCC __sync atomics are available. */\n#undef JEMALLOC_GCC_SYNC_ATOMICS\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 __builtin_clz() and __builtin_clzl() are available.\n */\n#undef JEMALLOC_HAVE_BUILTIN_CLZ\n\n/*\n * Defined if os_unfair_lock_*() functions are available, as provided by Darwin.\n */\n#undef JEMALLOC_OS_UNFAIR_LOCK\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/* Defined if syscall(2) is usable. */\n#undef JEMALLOC_USE_SYSCALL\n\n/*\n * Defined if secure_getenv(3) is available.\n */\n#undef JEMALLOC_HAVE_SECURE_GETENV\n\n/*\n * Defined if issetugid(2) is available.\n */\n#undef JEMALLOC_HAVE_ISSETUGID\n\n/* Defined if pthread_atfork(3) is available. */\n#undef JEMALLOC_HAVE_PTHREAD_ATFORK\n\n/* Defined if pthread_setname_np(3) is available. */\n#undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP\n\n/*\n * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.\n */\n#undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE\n\n/*\n * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.\n */\n#undef JEMALLOC_HAVE_CLOCK_MONOTONIC\n\n/*\n * Defined if mach_absolute_time() is available.\n */\n#undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME\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/* Non-empty if the tls_model attribute is supported. */\n#undef JEMALLOC_TLS_MODEL\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_DSS enables use of sbrk(2) to allocate extents from the data storage\n * segment (DSS).\n */\n#undef JEMALLOC_DSS\n\n/* Support memory filling (junk/zero). */\n#undef JEMALLOC_FILL\n\n/* Support utrace(2)-based tracing. */\n#undef JEMALLOC_UTRACE\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/*\n * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size\n * classes).\n */\n#undef LG_QUANTUM\n\n/* One page is 2^LG_PAGE bytes. */\n#undef LG_PAGE\n\n/*\n * One huge page is 2^LG_HUGEPAGE bytes.  Note that this is defined even if the\n * system does not explicitly support huge pages; system calls that require\n * explicit huge page support are separately configured.\n */\n#undef LG_HUGEPAGE\n\n/*\n * If defined, adjacent virtual memory mappings with identical attributes\n * automatically coalesce, and they fragment when changes are made to subranges.\n * This is the normal order of things for mmap()/munmap(), but on Windows\n * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.\n * mappings do *not* coalesce/fragment.\n */\n#undef JEMALLOC_MAPS_COALESCE\n\n/*\n * If defined, retain memory for later reuse by default rather than using e.g.\n * munmap() to unmap freed extents.  This is enabled on 64-bit Linux because\n * common sequences of mmap()/munmap() calls will cause virtual memory map\n * holes.\n */\n#undef JEMALLOC_RETAIN\n\n/* TLS is used to map arenas and magazine caches to threads. */\n#undef JEMALLOC_TLS\n\n/*\n * Used to mark unreachable code to quiet \"end of non-void\" compiler warnings.\n * Don't use this directly; instead use unreachable() from util.h\n */\n#undef JEMALLOC_INTERNAL_UNREACHABLE\n\n/*\n * ffs*() functions to use for bitmapping.  Don't use these directly; instead,\n * use ffs_*() from util.h.\n */\n#undef JEMALLOC_INTERNAL_FFSLL\n#undef JEMALLOC_INTERNAL_FFSL\n#undef JEMALLOC_INTERNAL_FFS\n\n/*\n * If defined, explicitly attempt to more uniformly distribute large allocation\n * pointer alignments across all cache indices.\n */\n#undef JEMALLOC_CACHE_OBLIVIOUS\n\n/*\n * If defined, enable logging facilities.  We make this a configure option to\n * avoid taking extra branches everywhere.\n */\n#undef JEMALLOC_LOG\n\n/*\n * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.\n */\n#undef JEMALLOC_ZONE\n\n/*\n * Methods for determining whether the OS overcommits.\n * JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's\n *                                         /proc/sys/vm.overcommit_memory file.\n * JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.\n */\n#undef JEMALLOC_SYSCTL_VM_OVERCOMMIT\n#undef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY\n\n/* Defined if madvise(2) is available. */\n#undef JEMALLOC_HAVE_MADVISE\n\n/*\n * Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE\n * arguments to madvise(2).\n */\n#undef JEMALLOC_HAVE_MADVISE_HUGE\n\n/*\n * Methods for purging unused pages differ between operating systems.\n *\n *   madvise(..., MADV_FREE) : This marks pages as being unused, such that they\n *                             will be discarded rather than swapped out.\n *   madvise(..., MADV_DONTNEED) : If JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS is\n *                                 defined, this immediately discards pages,\n *                                 such that new pages will be demand-zeroed if\n *                                 the address region is later touched;\n *                                 otherwise this behaves similarly to\n *                                 MADV_FREE, though typically with higher\n *                                 system overhead.\n */\n#undef JEMALLOC_PURGE_MADVISE_FREE\n#undef JEMALLOC_PURGE_MADVISE_DONTNEED\n#undef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS\n\n/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */\n#undef JEMALLOC_DEFINE_MADVISE_FREE\n\n/*\n * Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.\n */\n#undef JEMALLOC_MADVISE_DONTDUMP\n\n/*\n * Defined if transparent huge pages (THPs) are supported via the\n * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.\n */\n#undef JEMALLOC_THP\n\n/* Define if operating system has alloca.h header. */\n#undef JEMALLOC_HAS_ALLOCA_H\n\n/* C99 restrict keyword supported. */\n#undef JEMALLOC_HAS_RESTRICT\n\n/* For use by hash code. */\n#undef JEMALLOC_BIG_ENDIAN\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(long long) == 2^LG_SIZEOF_LONG_LONG. */\n#undef LG_SIZEOF_LONG_LONG\n\n/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */\n#undef LG_SIZEOF_INTMAX_T\n\n/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */\n#undef JEMALLOC_GLIBC_MALLOC_HOOK\n\n/* glibc memalign hook. */\n#undef JEMALLOC_GLIBC_MEMALIGN_HOOK\n\n/* pthread support */\n#undef JEMALLOC_HAVE_PTHREAD\n\n/* dlsym() support */\n#undef JEMALLOC_HAVE_DLSYM\n\n/* Adaptive mutex support in pthreads. */\n#undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP\n\n/* GNU specific sched_getcpu support */\n#undef JEMALLOC_HAVE_SCHED_GETCPU\n\n/* GNU specific sched_setaffinity support */\n#undef JEMALLOC_HAVE_SCHED_SETAFFINITY\n\n/*\n * If defined, all the features necessary for background threads are present.\n */\n#undef JEMALLOC_BACKGROUND_THREAD\n\n/*\n * If defined, jemalloc symbols are not exported (doesn't work when\n * JEMALLOC_PREFIX is not defined).\n */\n#undef JEMALLOC_EXPORT\n\n/* config.malloc_conf options string. */\n#undef JEMALLOC_CONFIG_MALLOC_CONF\n\n/* If defined, jemalloc takes the malloc/free/etc. symbol names. */\n#undef JEMALLOC_IS_MALLOC\n\n/*\n * Defined if strerror_r returns char * if _GNU_SOURCE is defined.\n */\n#undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE\n\n#endif /* JEMALLOC_INTERNAL_DEFS_H_ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTERNS_H\n#define JEMALLOC_INTERNAL_EXTERNS_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/tsd_types.h\"\n\n/* TSD checks this to set thread local slow state accordingly. */\nextern bool malloc_slow;\n\n/* Run-time options. */\nextern bool opt_abort;\nextern bool opt_abort_conf;\nextern const char *opt_junk;\nextern bool opt_junk_alloc;\nextern bool opt_junk_free;\nextern bool opt_utrace;\nextern bool opt_xmalloc;\nextern bool opt_zero;\nextern unsigned opt_narenas;\n\n/* Number of CPUs. */\nextern unsigned ncpus;\n\n/* Number of arenas used for automatic multiplexing of threads and arenas. */\nextern unsigned narenas_auto;\n\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 */\nextern atomic_p_t arenas[];\n\nvoid *a0malloc(size_t size);\nvoid a0dalloc(void *ptr);\nvoid *bootstrap_malloc(size_t size);\nvoid *bootstrap_calloc(size_t num, size_t size);\nvoid bootstrap_free(void *ptr);\nvoid arena_set(unsigned ind, arena_t *arena);\nunsigned narenas_total_get(void);\narena_t *arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);\narena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);\narena_t *arena_choose_hard(tsd_t *tsd, bool internal);\nvoid arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);\nvoid iarena_cleanup(tsd_t *tsd);\nvoid arena_cleanup(tsd_t *tsd);\nvoid arenas_tdata_cleanup(tsd_t *tsd);\nvoid jemalloc_prefork(void);\nvoid jemalloc_postfork_parent(void);\nvoid jemalloc_postfork_child(void);\nbool malloc_initialized(void);\n\n#endif /* JEMALLOC_INTERNAL_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INCLUDES_H\n#define JEMALLOC_INTERNAL_INCLUDES_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.\n *\n * Historically, we dealt with this by each header into four sections (types,\n * structs, externs, and inlines), and included each header file multiple times\n * in this file, picking out the portion we want on each pass using the\n * following #defines:\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 * We're moving toward a world in which the dependencies are explicit; each file\n * will #include the headers it depends on (rather than relying on them being\n * implicitly available via this file including every header file in the\n * project).\n *\n * We're now in an intermediate state: we've broken up the header files to avoid\n * having to include each one multiple times, but have not yet moved the\n * dependency information into the header files (i.e. we still rely on the\n * ordering in this file to ensure all a header's dependencies are available in\n * its translation unit).  Each component is now broken up into multiple header\n * files, corresponding to the sections above (e.g. instead of \"foo.h\", we now\n * have \"foo_types.h\", \"foo_structs.h\", \"foo_externs.h\", \"foo_inlines.h\").\n *\n * Those files which have been converted to explicitly include their\n * inter-component dependencies are now in the initial HERMETIC HEADERS\n * section.  All headers may still rely on jemalloc_preamble.h (which, by fiat,\n * must be included first in every translation unit) for system headers and\n * global jemalloc definitions, however.\n */\n\n/******************************************************************************/\n/* TYPES */\n/******************************************************************************/\n\n#include \"jemalloc/internal/extent_types.h\"\n#include \"jemalloc/internal/base_types.h\"\n#include \"jemalloc/internal/arena_types.h\"\n#include \"jemalloc/internal/tcache_types.h\"\n#include \"jemalloc/internal/prof_types.h\"\n\n/******************************************************************************/\n/* STRUCTS */\n/******************************************************************************/\n\n#include \"jemalloc/internal/arena_structs_a.h\"\n#include \"jemalloc/internal/extent_structs.h\"\n#include \"jemalloc/internal/base_structs.h\"\n#include \"jemalloc/internal/prof_structs.h\"\n#include \"jemalloc/internal/arena_structs_b.h\"\n#include \"jemalloc/internal/tcache_structs.h\"\n#include \"jemalloc/internal/background_thread_structs.h\"\n\n/******************************************************************************/\n/* EXTERNS */\n/******************************************************************************/\n\n#include \"jemalloc/internal/jemalloc_internal_externs.h\"\n#include \"jemalloc/internal/extent_externs.h\"\n#include \"jemalloc/internal/base_externs.h\"\n#include \"jemalloc/internal/arena_externs.h\"\n#include \"jemalloc/internal/large_externs.h\"\n#include \"jemalloc/internal/tcache_externs.h\"\n#include \"jemalloc/internal/prof_externs.h\"\n#include \"jemalloc/internal/background_thread_externs.h\"\n\n/******************************************************************************/\n/* INLINES */\n/******************************************************************************/\n\n#include \"jemalloc/internal/jemalloc_internal_inlines_a.h\"\n#include \"jemalloc/internal/base_inlines.h\"\n/*\n * Include portions of arena code interleaved with tcache code in order to\n * resolve circular dependencies.\n */\n#include \"jemalloc/internal/prof_inlines_a.h\"\n#include \"jemalloc/internal/arena_inlines_a.h\"\n#include \"jemalloc/internal/extent_inlines.h\"\n#include \"jemalloc/internal/jemalloc_internal_inlines_b.h\"\n#include \"jemalloc/internal/tcache_inlines.h\"\n#include \"jemalloc/internal/arena_inlines_b.h\"\n#include \"jemalloc/internal/jemalloc_internal_inlines_c.h\"\n#include \"jemalloc/internal/prof_inlines_b.h\"\n#include \"jemalloc/internal/background_thread_inlines.h\"\n\n#endif /* JEMALLOC_INTERNAL_INCLUDES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INLINES_A_H\n#define JEMALLOC_INTERNAL_INLINES_A_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bit_util.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nJEMALLOC_ALWAYS_INLINE malloc_cpuid_t\nmalloc_getcpu(void) {\n\tassert(have_percpu_arena);\n#if defined(JEMALLOC_HAVE_SCHED_GETCPU)\n\treturn (malloc_cpuid_t)sched_getcpu();\n#else\n\tnot_reached();\n\treturn -1;\n#endif\n}\n\n/* Return the chosen arena index based on current cpu. */\nJEMALLOC_ALWAYS_INLINE unsigned\npercpu_arena_choose(void) {\n\tassert(have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena));\n\n\tmalloc_cpuid_t cpuid = malloc_getcpu();\n\tassert(cpuid >= 0);\n\n\tunsigned arena_ind;\n\tif ((opt_percpu_arena == percpu_arena) || ((unsigned)cpuid < ncpus /\n\t    2)) {\n\t\tarena_ind = cpuid;\n\t} else {\n\t\tassert(opt_percpu_arena == per_phycpu_arena);\n\t\t/* Hyper threads on the same physical CPU share arena. */\n\t\tarena_ind = cpuid - ncpus / 2;\n\t}\n\n\treturn arena_ind;\n}\n\n/* Return the limit of percpu auto arena range, i.e. arenas[0...ind_limit). */\nJEMALLOC_ALWAYS_INLINE unsigned\npercpu_arena_ind_limit(percpu_arena_mode_t mode) {\n\tassert(have_percpu_arena && PERCPU_ARENA_ENABLED(mode));\n\tif (mode == per_phycpu_arena && ncpus > 1) {\n\t\tif (ncpus % 2) {\n\t\t\t/* This likely means a misconfig. */\n\t\t\treturn ncpus / 2 + 1;\n\t\t}\n\t\treturn ncpus / 2;\n\t} else {\n\t\treturn ncpus;\n\t}\n}\n\nstatic inline arena_tdata_t *\narena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing) {\n\tarena_tdata_t *tdata;\n\tarena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);\n\n\tif (unlikely(arenas_tdata == NULL)) {\n\t\t/* arenas_tdata hasn't been initialized yet. */\n\t\treturn arena_tdata_get_hard(tsd, ind);\n\t}\n\tif (unlikely(ind >= tsd_narenas_tdata_get(tsd))) {\n\t\t/*\n\t\t * ind is invalid, cache is old (too small), or tdata to be\n\t\t * initialized.\n\t\t */\n\t\treturn (refresh_if_missing ? arena_tdata_get_hard(tsd, ind) :\n\t\t    NULL);\n\t}\n\n\ttdata = &arenas_tdata[ind];\n\tif (likely(tdata != NULL) || !refresh_if_missing) {\n\t\treturn tdata;\n\t}\n\treturn arena_tdata_get_hard(tsd, ind);\n}\n\nstatic inline arena_t *\narena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {\n\tarena_t *ret;\n\n\tassert(ind < MALLOCX_ARENA_LIMIT);\n\n\tret = (arena_t *)atomic_load_p(&arenas[ind], ATOMIC_ACQUIRE);\n\tif (unlikely(ret == NULL)) {\n\t\tif (init_if_missing) {\n\t\t\tret = arena_init(tsdn, ind,\n\t\t\t    (extent_hooks_t *)&extent_hooks_default);\n\t\t}\n\t}\n\treturn ret;\n}\n\nstatic inline ticker_t *\ndecay_ticker_get(tsd_t *tsd, unsigned ind) {\n\tarena_tdata_t *tdata;\n\n\ttdata = arena_tdata_get(tsd, ind, true);\n\tif (unlikely(tdata == NULL)) {\n\t\treturn NULL;\n\t}\n\treturn &tdata->decay_ticker;\n}\n\nJEMALLOC_ALWAYS_INLINE cache_bin_t *\ntcache_small_bin_get(tcache_t *tcache, szind_t binind) {\n\tassert(binind < NBINS);\n\treturn &tcache->bins_small[binind];\n}\n\nJEMALLOC_ALWAYS_INLINE cache_bin_t *\ntcache_large_bin_get(tcache_t *tcache, szind_t binind) {\n\tassert(binind >= NBINS &&binind < nhbins);\n\treturn &tcache->bins_large[binind - NBINS];\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntcache_available(tsd_t *tsd) {\n\t/*\n\t * Thread specific auto tcache might be unavailable if: 1) during tcache\n\t * initialization, or 2) disabled through thread.tcache.enabled mallctl\n\t * or config options.  This check covers all cases.\n\t */\n\tif (likely(tsd_tcache_enabled_get(tsd))) {\n\t\t/* Associated arena == NULL implies tcache init in progress. */\n\t\tassert(tsd_tcachep_get(tsd)->arena == NULL ||\n\t\t    tcache_small_bin_get(tsd_tcachep_get(tsd), 0)->avail !=\n\t\t    NULL);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE tcache_t *\ntcache_get(tsd_t *tsd) {\n\tif (!tcache_available(tsd)) {\n\t\treturn NULL;\n\t}\n\n\treturn tsd_tcachep_get(tsd);\n}\n\nstatic inline void\npre_reentrancy(tsd_t *tsd, arena_t *arena) {\n\t/* arena is the current context.  Reentry from a0 is not allowed. */\n\tassert(arena != arena_get(tsd_tsdn(tsd), 0, false));\n\n\tbool fast = tsd_fast(tsd);\n\tassert(tsd_reentrancy_level_get(tsd) < INT8_MAX);\n\t++*tsd_reentrancy_levelp_get(tsd);\n\tif (fast) {\n\t\t/* Prepare slow path for reentrancy. */\n\t\ttsd_slow_update(tsd);\n\t\tassert(tsd->state == tsd_state_nominal_slow);\n\t}\n}\n\nstatic inline void\npost_reentrancy(tsd_t *tsd) {\n\tint8_t *reentrancy_level = tsd_reentrancy_levelp_get(tsd);\n\tassert(*reentrancy_level > 0);\n\tif (--*reentrancy_level == 0) {\n\t\ttsd_slow_update(tsd);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_INLINES_A_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INLINES_B_H\n#define JEMALLOC_INTERNAL_INLINES_B_H\n\n#include \"jemalloc/internal/rtree.h\"\n\n/* Choose an arena based on a per-thread value. */\nstatic inline arena_t *\narena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal) {\n\tarena_t *ret;\n\n\tif (arena != NULL) {\n\t\treturn arena;\n\t}\n\n\t/* During reentrancy, arena 0 is the safest bet. */\n\tif (unlikely(tsd_reentrancy_level_get(tsd) > 0)) {\n\t\treturn arena_get(tsd_tsdn(tsd), 0, true);\n\t}\n\n\tret = internal ? tsd_iarena_get(tsd) : tsd_arena_get(tsd);\n\tif (unlikely(ret == NULL)) {\n\t\tret = arena_choose_hard(tsd, internal);\n\t\tassert(ret);\n\t\tif (tcache_available(tsd)) {\n\t\t\ttcache_t *tcache = tcache_get(tsd);\n\t\t\tif (tcache->arena != NULL) {\n\t\t\t\t/* See comments in tcache_data_init().*/\n\t\t\t\tassert(tcache->arena ==\n\t\t\t\t    arena_get(tsd_tsdn(tsd), 0, false));\n\t\t\t\tif (tcache->arena != ret) {\n\t\t\t\t\ttcache_arena_reassociate(tsd_tsdn(tsd),\n\t\t\t\t\t    tcache, ret);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttcache_arena_associate(tsd_tsdn(tsd), tcache,\n\t\t\t\t    ret);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Note that for percpu arena, if the current arena is outside of the\n\t * auto percpu arena range, (i.e. thread is assigned to a manually\n\t * managed arena), then percpu arena is skipped.\n\t */\n\tif (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena) &&\n\t    !internal && (arena_ind_get(ret) <\n\t    percpu_arena_ind_limit(opt_percpu_arena)) && (ret->last_thd !=\n\t    tsd_tsdn(tsd))) {\n\t\tunsigned ind = percpu_arena_choose();\n\t\tif (arena_ind_get(ret) != ind) {\n\t\t\tpercpu_arena_update(tsd, ind);\n\t\t\tret = tsd_arena_get(tsd);\n\t\t}\n\t\tret->last_thd = tsd_tsdn(tsd);\n\t}\n\n\treturn ret;\n}\n\nstatic inline arena_t *\narena_choose(tsd_t *tsd, arena_t *arena) {\n\treturn arena_choose_impl(tsd, arena, false);\n}\n\nstatic inline arena_t *\narena_ichoose(tsd_t *tsd, arena_t *arena) {\n\treturn arena_choose_impl(tsd, arena, true);\n}\n\nstatic inline bool\narena_is_auto(arena_t *arena) {\n\tassert(narenas_auto > 0);\n\treturn (arena_ind_get(arena) < narenas_auto);\n}\n\nJEMALLOC_ALWAYS_INLINE extent_t *\niealloc(tsdn_t *tsdn, const void *ptr) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\treturn rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true);\n}\n\n#endif /* JEMALLOC_INTERNAL_INLINES_B_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INLINES_C_H\n#define JEMALLOC_INTERNAL_INLINES_C_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/witness.h\"\n\n/*\n * Translating the names of the 'i' functions:\n *   Abbreviations used in the first part of the function name (before\n *   alloc/dalloc) describe what that function accomplishes:\n *     a: arena (query)\n *     s: size (query, or sized deallocation)\n *     e: extent (query)\n *     p: aligned (allocates)\n *     vs: size (query, without knowing that the pointer is into the heap)\n *     r: rallocx implementation\n *     x: xallocx implementation\n *   Abbreviations used in the second part of the function name (after\n *   alloc/dalloc) describe the arguments it takes\n *     z: whether to return zeroed memory\n *     t: accepts a tcache_t * parameter\n *     m: accepts an arena_t * parameter\n */\n\nJEMALLOC_ALWAYS_INLINE arena_t *\niaalloc(tsdn_t *tsdn, const void *ptr) {\n\tassert(ptr != NULL);\n\n\treturn arena_aalloc(tsdn, ptr);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nisalloc(tsdn_t *tsdn, const void *ptr) {\n\tassert(ptr != NULL);\n\n\treturn arena_salloc(tsdn, ptr);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,\n    bool is_internal, arena_t *arena, bool slow_path) {\n\tvoid *ret;\n\n\tassert(size != 0);\n\tassert(!is_internal || tcache == NULL);\n\tassert(!is_internal || arena == NULL || arena_is_auto(arena));\n\tif (!tsdn_null(tsdn) && tsd_reentrancy_level_get(tsdn_tsd(tsdn)) == 0) {\n\t\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t\t    WITNESS_RANK_CORE, 0);\n\t}\n\n\tret = arena_malloc(tsdn, arena, size, ind, zero, tcache, slow_path);\n\tif (config_stats && is_internal && likely(ret != NULL)) {\n\t\tarena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));\n\t}\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path) {\n\treturn iallocztm(tsd_tsdn(tsd), size, ind, zero, tcache_get(tsd), false,\n\t    NULL, slow_path);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,\n    tcache_t *tcache, bool is_internal, arena_t *arena) {\n\tvoid *ret;\n\n\tassert(usize != 0);\n\tassert(usize == sz_sa2u(usize, alignment));\n\tassert(!is_internal || tcache == NULL);\n\tassert(!is_internal || arena == NULL || arena_is_auto(arena));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tret = arena_palloc(tsdn, arena, usize, alignment, zero, tcache);\n\tassert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);\n\tif (config_stats && is_internal && likely(ret != NULL)) {\n\t\tarena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));\n\t}\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,\n    tcache_t *tcache, arena_t *arena) {\n\treturn ipallocztm(tsdn, usize, alignment, zero, tcache, false, arena);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero) {\n\treturn ipallocztm(tsd_tsdn(tsd), usize, alignment, zero,\n\t    tcache_get(tsd), false, NULL);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nivsalloc(tsdn_t *tsdn, const void *ptr) {\n\treturn arena_vsalloc(tsdn, ptr);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nidalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, alloc_ctx_t *alloc_ctx,\n    bool is_internal, bool slow_path) {\n\tassert(ptr != NULL);\n\tassert(!is_internal || tcache == NULL);\n\tassert(!is_internal || arena_is_auto(iaalloc(tsdn, ptr)));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\tif (config_stats && is_internal) {\n\t\tarena_internal_sub(iaalloc(tsdn, ptr), isalloc(tsdn, ptr));\n\t}\n\tif (!is_internal && !tsdn_null(tsdn) &&\n\t    tsd_reentrancy_level_get(tsdn_tsd(tsdn)) != 0) {\n\t\tassert(tcache == NULL);\n\t}\n\tarena_dalloc(tsdn, ptr, tcache, alloc_ctx, slow_path);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nidalloc(tsd_t *tsd, void *ptr) {\n\tidalloctm(tsd_tsdn(tsd), ptr, tcache_get(tsd), NULL, false, true);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nisdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,\n    alloc_ctx_t *alloc_ctx, bool slow_path) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\tarena_sdalloc(tsdn, ptr, size, tcache, alloc_ctx, slow_path);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,\n    size_t extra, size_t alignment, bool zero, tcache_t *tcache,\n    arena_t *arena) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\tvoid *p;\n\tsize_t usize, copysize;\n\n\tusize = sz_sa2u(size + extra, alignment);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\tp = ipalloct(tsdn, usize, alignment, zero, tcache, arena);\n\tif (p == NULL) {\n\t\tif (extra == 0) {\n\t\t\treturn NULL;\n\t\t}\n\t\t/* Try again, without extra this time. */\n\t\tusize = sz_sa2u(size, alignment);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\treturn NULL;\n\t\t}\n\t\tp = ipalloct(tsdn, usize, alignment, zero, tcache, arena);\n\t\tif (p == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t}\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\tmemcpy(p, ptr, copysize);\n\tisdalloct(tsdn, ptr, oldsize, tcache, NULL, true);\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,\n    bool zero, tcache_t *tcache, arena_t *arena) {\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))\n\t    != 0) {\n\t\t/*\n\t\t * Existing object alignment is inadequate; allocate new space\n\t\t * and copy.\n\t\t */\n\t\treturn iralloct_realign(tsdn, ptr, oldsize, size, 0, alignment,\n\t\t    zero, tcache, arena);\n\t}\n\n\treturn arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,\n\t    tcache);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,\n    bool zero) {\n\treturn iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,\n\t    tcache_get(tsd), NULL);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,\n    size_t alignment, bool zero) {\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))\n\t    != 0) {\n\t\t/* Existing object alignment is inadequate. */\n\t\treturn true;\n\t}\n\n\treturn arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero);\n}\n\nJEMALLOC_ALWAYS_INLINE int\niget_defrag_hint(tsdn_t *tsdn, void* ptr, int *bin_util, int *run_util) {\n\tint defrag = 0;\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\tszind_t szind;\n\tbool is_slab;\n\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr, true, &szind, &is_slab);\n\tif (likely(is_slab)) {\n\t\t/* Small allocation. */\n\t\textent_t *slab = iealloc(tsdn, ptr);\n\t\tarena_t *arena = extent_arena_get(slab);\n\t\tszind_t binind = extent_szind_get(slab);\n\t\tbin_t *bin = &arena->bins[binind];\n\t\tmalloc_mutex_lock(tsdn, &bin->lock);\n\t\t/* don't bother moving allocations from the slab currently used for new allocations */\n\t\tif (slab != bin->slabcur) {\n\t\t\tconst bin_info_t *bin_info = &bin_infos[binind];\n\t\t\tsize_t availregs = bin_info->nregs * bin->stats.curslabs;\n\t\t\t*bin_util = ((long long)bin->stats.curregs<<16) / availregs;\n\t\t\t*run_util = ((long long)(bin_info->nregs - extent_nfree_get(slab))<<16) / bin_info->nregs;\n\t\t\tdefrag = 1;\n\t\t}\n\t\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t}\n\treturn defrag;\n}\n\n#endif /* JEMALLOC_INTERNAL_INLINES_C_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MACROS_H\n#define JEMALLOC_INTERNAL_MACROS_H\n\n#ifdef JEMALLOC_DEBUG\n#  define JEMALLOC_ALWAYS_INLINE static inline\n#else\n#  define JEMALLOC_ALWAYS_INLINE JEMALLOC_ATTR(always_inline) static inline\n#endif\n#ifdef _MSC_VER\n#  define inline _inline\n#endif\n\n#define UNUSED JEMALLOC_ATTR(unused)\n\n#define ZU(z)\t((size_t)z)\n#define ZD(z)\t((ssize_t)z)\n#define QU(q)\t((uint64_t)q)\n#define QD(q)\t((int64_t)q)\n\n#define KZU(z)\tZU(z##ULL)\n#define KZD(z)\tZD(z##LL)\n#define KQU(q)\tQU(q##ULL)\n#define KQD(q)\tQI(q##LL)\n\n#ifndef __DECONST\n#  define\t__DECONST(type, var)\t((type)(uintptr_t)(const void *)(var))\n#endif\n\n#if !defined(JEMALLOC_HAS_RESTRICT) || defined(__cplusplus)\n#  define restrict\n#endif\n\n/* Various function pointers are statick and immutable except during testing. */\n#ifdef JEMALLOC_JET\n#  define JET_MUTABLE\n#else\n#  define JET_MUTABLE const\n#endif\n\n#define JEMALLOC_VA_ARGS_HEAD(head, ...) head\n#define JEMALLOC_VA_ARGS_TAIL(head, ...) __VA_ARGS__\n\n#endif /* JEMALLOC_INTERNAL_MACROS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TYPES_H\n#define JEMALLOC_INTERNAL_TYPES_H\n\n/* Page size index type. */\ntypedef unsigned pszind_t;\n\n/* Size class index type. */\ntypedef unsigned szind_t;\n\n/* Processor / core id type. */\ntypedef int malloc_cpuid_t;\n\n/*\n * Flags bits:\n *\n * a: arena\n * t: tcache\n * 0: unused\n * z: zero\n * n: alignment\n *\n * aaaaaaaa aaaatttt tttttttt 0znnnnnn\n */\n#define MALLOCX_ARENA_BITS\t12\n#define MALLOCX_TCACHE_BITS\t12\n#define MALLOCX_LG_ALIGN_BITS\t6\n#define MALLOCX_ARENA_SHIFT\t20\n#define MALLOCX_TCACHE_SHIFT\t8\n#define MALLOCX_ARENA_MASK \\\n    (((1 << MALLOCX_ARENA_BITS) - 1) << MALLOCX_ARENA_SHIFT)\n/* NB: Arena index bias decreases the maximum number of arenas by 1. */\n#define MALLOCX_ARENA_LIMIT\t((1 << MALLOCX_ARENA_BITS) - 1)\n#define MALLOCX_TCACHE_MASK \\\n    (((1 << MALLOCX_TCACHE_BITS) - 1) << MALLOCX_TCACHE_SHIFT)\n#define MALLOCX_TCACHE_MAX\t((1 << MALLOCX_TCACHE_BITS) - 3)\n#define MALLOCX_LG_ALIGN_MASK\t((1 << MALLOCX_LG_ALIGN_BITS) - 1)\n/* Use MALLOCX_ALIGN_GET() if alignment may not be specified in flags. */\n#define MALLOCX_ALIGN_GET_SPECIFIED(flags)\t\t\t\t\\\n    (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK))\n#define MALLOCX_ALIGN_GET(flags)\t\t\t\t\t\\\n    (MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX-1))\n#define MALLOCX_ZERO_GET(flags)\t\t\t\t\t\t\\\n    ((bool)(flags & MALLOCX_ZERO))\n\n#define MALLOCX_TCACHE_GET(flags)\t\t\t\t\t\\\n    (((unsigned)((flags & MALLOCX_TCACHE_MASK) >> MALLOCX_TCACHE_SHIFT)) - 2)\n#define MALLOCX_ARENA_GET(flags)\t\t\t\t\t\\\n    (((unsigned)(((unsigned)flags) >> MALLOCX_ARENA_SHIFT)) - 1)\n\n/* Smallest size class to support. */\n#define TINY_MIN\t\t(1U << LG_TINY_MIN)\n\n/*\n * Minimum allocation alignment 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#  if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))\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 __aarch64__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __hppa__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __m68k__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __mips__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __nios2__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __or1k__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __powerpc__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  if defined(__riscv) || defined(__riscv__)\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __s390__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  if (defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || \\\n\tdefined(__SH4_SINGLE_ONLY__))\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __tile__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __le32__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifndef LG_QUANTUM\n#    error \"Unknown minimum alignment for architecture; specify via \"\n\t \"--with-lg-quantum\"\n#  endif\n#endif\n\n#define QUANTUM\t\t\t((size_t)(1U << LG_QUANTUM))\n#define QUANTUM_MASK\t\t(QUANTUM - 1)\n\n/* Return the smallest quantum multiple that is >= a. */\n#define QUANTUM_CEILING(a)\t\t\t\t\t\t\\\n\t(((a) + QUANTUM_MASK) & ~QUANTUM_MASK)\n\n#define LONG\t\t\t((size_t)(1U << LG_SIZEOF_LONG))\n#define LONG_MASK\t\t(LONG - 1)\n\n/* Return the smallest long multiple that is >= a. */\n#define LONG_CEILING(a)\t\t\t\t\t\t\t\\\n\t(((a) + LONG_MASK) & ~LONG_MASK)\n\n#define SIZEOF_PTR\t\t(1U << LG_SIZEOF_PTR)\n#define PTR_MASK\t\t(SIZEOF_PTR - 1)\n\n/* Return the smallest (void *) multiple that is >= a. */\n#define PTR_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 LG_CACHELINE\t\t6\n#define CACHELINE\t\t64\n#define CACHELINE_MASK\t\t(CACHELINE - 1)\n\n/* Return the smallest cacheline multiple that is >= s. */\n#define CACHELINE_CEILING(s)\t\t\t\t\t\t\\\n\t(((s) + CACHELINE_MASK) & ~CACHELINE_MASK)\n\n/* Return the nearest aligned address at or below a. */\n#define ALIGNMENT_ADDR2BASE(a, alignment)\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ((~(alignment)) + 1)))\n\n/* Return the offset between a and the nearest aligned address at or below a. */\n#define ALIGNMENT_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 ALIGNMENT_CEILING(s, alignment)\t\t\t\t\t\\\n\t(((s) + (alignment - 1)) & ((~(alignment)) + 1))\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#    ifdef JEMALLOC_HAS_ALLOCA_H\n#      include <alloca.h>\n#    else\n#      include <stdlib.h>\n#    endif\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#endif /* JEMALLOC_INTERNAL_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in",
    "content": "#ifndef JEMALLOC_PREAMBLE_H\n#define JEMALLOC_PREAMBLE_H\n\n#include \"jemalloc_internal_defs.h\"\n#include \"jemalloc/internal/jemalloc_internal_decls.h\"\n\n#ifdef JEMALLOC_UTRACE\n#include <sys/ktrace.h>\n#endif\n\n#define JEMALLOC_NO_DEMANGLE\n#ifdef JEMALLOC_JET\n#  undef JEMALLOC_IS_MALLOC\n#  define JEMALLOC_N(n) jet_##n\n#  include \"jemalloc/internal/public_namespace.h\"\n#  define JEMALLOC_NO_RENAME\n#  include \"../jemalloc@install_suffix@.h\"\n#  undef JEMALLOC_NO_RENAME\n#else\n#  define JEMALLOC_N(n) @private_namespace@##n\n#  include \"../jemalloc@install_suffix@.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#endif\n\n#include \"jemalloc/internal/jemalloc_internal_macros.h\"\n\n/*\n * Note that the ordering matters here; the hook itself is name-mangled.  We\n * want the inclusion of hooks to happen early, so that we hook as much as\n * possible.\n */\n#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE\n#  ifndef JEMALLOC_JET\n#    include \"jemalloc/internal/private_namespace.h\"\n#  else\n#    include \"jemalloc/internal/private_namespace_jet.h\"\n#  endif\n#endif\n#include \"jemalloc/internal/hooks.h\"\n\n#ifdef JEMALLOC_DEFINE_MADVISE_FREE\n#  define JEMALLOC_MADV_FREE 8\n#endif\n\nstatic const bool config_debug =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool have_dss =\n#ifdef JEMALLOC_DSS\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool have_madvise_huge =\n#ifdef JEMALLOC_HAVE_MADVISE_HUGE\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 char * const config_malloc_conf = JEMALLOC_CONFIG_MALLOC_CONF;\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 maps_coalesce =\n#ifdef JEMALLOC_MAPS_COALESCE\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_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_xmalloc =\n#ifdef JEMALLOC_XMALLOC\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_cache_oblivious =\n#ifdef JEMALLOC_CACHE_OBLIVIOUS\n    true\n#else\n    false\n#endif\n    ;\n/*\n * Undocumented, for jemalloc development use only at the moment.  See the note\n * in jemalloc/internal/log.h.\n */\nstatic const bool config_log =\n#ifdef JEMALLOC_LOG\n    true\n#else\n    false\n#endif\n    ;\n#ifdef JEMALLOC_HAVE_SCHED_GETCPU\n/* Currently percpu_arena depends on sched_getcpu. */\n#define JEMALLOC_PERCPU_ARENA\n#endif\nstatic const bool have_percpu_arena =\n#ifdef JEMALLOC_PERCPU_ARENA\n    true\n#else\n    false\n#endif\n    ;\n/*\n * Undocumented, and not recommended; the application should take full\n * responsibility for tracking provenance.\n */\nstatic const bool force_ivsalloc =\n#ifdef JEMALLOC_FORCE_IVSALLOC\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool have_background_thread =\n#ifdef JEMALLOC_BACKGROUND_THREAD\n    true\n#else\n    false\n#endif\n    ;\n\n#endif /* JEMALLOC_PREAMBLE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/large_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_LARGE_EXTERNS_H\n#define JEMALLOC_INTERNAL_LARGE_EXTERNS_H\n\nvoid *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);\nvoid *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,\n    bool zero);\nbool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,\n    size_t usize_max, bool zero);\nvoid *large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache);\n\ntypedef void (large_dalloc_junk_t)(void *, size_t);\nextern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;\n\ntypedef void (large_dalloc_maybe_junk_t)(void *, size_t);\nextern large_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk;\n\nvoid large_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent);\nvoid large_dalloc_finish(tsdn_t *tsdn, extent_t *extent);\nvoid large_dalloc(tsdn_t *tsdn, extent_t *extent);\nsize_t large_salloc(tsdn_t *tsdn, const extent_t *extent);\nprof_tctx_t *large_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent);\nvoid large_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx);\nvoid large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent);\n\n#endif /* JEMALLOC_INTERNAL_LARGE_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/log.h",
    "content": "#ifndef JEMALLOC_INTERNAL_LOG_H\n#define JEMALLOC_INTERNAL_LOG_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex.h\"\n\n#ifdef JEMALLOC_LOG\n#  define JEMALLOC_LOG_VAR_BUFSIZE 1000\n#else\n#  define JEMALLOC_LOG_VAR_BUFSIZE 1\n#endif\n\n#define JEMALLOC_LOG_BUFSIZE 4096\n\n/*\n * The log malloc_conf option is a '|'-delimited list of log_var name segments\n * which should be logged.  The names are themselves hierarchical, with '.' as\n * the delimiter (a \"segment\" is just a prefix in the log namespace).  So, if\n * you have:\n *\n * log(\"arena\", \"log msg for arena\"); // 1\n * log(\"arena.a\", \"log msg for arena.a\"); // 2\n * log(\"arena.b\", \"log msg for arena.b\"); // 3\n * log(\"arena.a.a\", \"log msg for arena.a.a\"); // 4\n * log(\"extent.a\", \"log msg for extent.a\"); // 5\n * log(\"extent.b\", \"log msg for extent.b\"); // 6\n *\n * And your malloc_conf option is \"log=arena.a|extent\", then lines 2, 4, 5, and\n * 6 will print at runtime.  You can enable logging from all log vars by\n * writing \"log=.\".\n *\n * None of this should be regarded as a stable API for right now.  It's intended\n * as a debugging interface, to let us keep around some of our printf-debugging\n * statements.\n */\n\nextern char log_var_names[JEMALLOC_LOG_VAR_BUFSIZE];\nextern atomic_b_t log_init_done;\n\ntypedef struct log_var_s log_var_t;\nstruct log_var_s {\n\t/*\n\t * Lowest bit is \"inited\", second lowest is \"enabled\".  Putting them in\n\t * a single word lets us avoid any fences on weak architectures.\n\t */\n\tatomic_u_t state;\n\tconst char *name;\n};\n\n#define LOG_NOT_INITIALIZED 0U\n#define LOG_INITIALIZED_NOT_ENABLED 1U\n#define LOG_ENABLED 2U\n\n#define LOG_VAR_INIT(name_str) {ATOMIC_INIT(LOG_NOT_INITIALIZED), name_str}\n\n/*\n * Returns the value we should assume for state (which is not necessarily\n * accurate; if logging is done before logging has finished initializing, then\n * we default to doing the safe thing by logging everything).\n */\nunsigned log_var_update_state(log_var_t *log_var);\n\n/* We factor out the metadata management to allow us to test more easily. */\n#define log_do_begin(log_var)\t\t\t\t\t\t\\\nif (config_log) {\t\t\t\t\t\t\t\\\n\tunsigned log_state = atomic_load_u(&(log_var).state,\t\t\\\n\t    ATOMIC_RELAXED);\t\t\t\t\t\t\\\n\tif (unlikely(log_state == LOG_NOT_INITIALIZED)) {\t\t\\\n\t\tlog_state = log_var_update_state(&(log_var));\t\t\\\n\t\tassert(log_state != LOG_NOT_INITIALIZED);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif (log_state == LOG_ENABLED) {\t\t\t\t\t\\\n\t\t{\n\t\t\t/* User code executes here. */\n#define log_do_end(log_var)\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}\n\n/*\n * MSVC has some preprocessor bugs in its expansion of __VA_ARGS__ during\n * preprocessing.  To work around this, we take all potential extra arguments in\n * a var-args functions.  Since a varargs macro needs at least one argument in\n * the \"...\", we accept the format string there, and require that the first\n * argument in this \"...\" is a const char *.\n */\nstatic inline void\nlog_impl_varargs(const char *name, ...) {\n\tchar buf[JEMALLOC_LOG_BUFSIZE];\n\tva_list ap;\n\n\tva_start(ap, name);\n\tconst char *format = va_arg(ap, const char *);\n\tsize_t dst_offset = 0;\n\tdst_offset += malloc_snprintf(buf, JEMALLOC_LOG_BUFSIZE, \"%s: \", name);\n\tdst_offset += malloc_vsnprintf(buf + dst_offset,\n\t    JEMALLOC_LOG_BUFSIZE - dst_offset, format, ap);\n\tdst_offset += malloc_snprintf(buf + dst_offset,\n\t    JEMALLOC_LOG_BUFSIZE - dst_offset, \"\\n\");\n\tva_end(ap);\n\n\tmalloc_write(buf);\n}\n\n/* Call as log(\"log.var.str\", \"format_string %d\", arg_for_format_string); */\n#define LOG(log_var_str, ...)\t\t\t\t\t\t\\\ndo {\t\t\t\t\t\t\t\t\t\\\n\tstatic log_var_t log_var = LOG_VAR_INIT(log_var_str);\t\t\\\n\tlog_do_begin(log_var)\t\t\t\t\t\t\\\n\t\tlog_impl_varargs((log_var).name, __VA_ARGS__);\t\t\\\n\tlog_do_end(log_var)\t\t\t\t\t\t\\\n} while (0)\n\n#endif /* JEMALLOC_INTERNAL_LOG_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/malloc_io.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MALLOC_IO_H\n#define JEMALLOC_INTERNAL_MALLOC_IO_H\n\n#ifdef _WIN32\n#  ifdef _WIN64\n#    define FMT64_PREFIX \"ll\"\n#    define FMTPTR_PREFIX \"ll\"\n#  else\n#    define FMT64_PREFIX \"ll\"\n#    define FMTPTR_PREFIX \"\"\n#  endif\n#  define FMTd32 \"d\"\n#  define FMTu32 \"u\"\n#  define FMTx32 \"x\"\n#  define FMTd64 FMT64_PREFIX \"d\"\n#  define FMTu64 FMT64_PREFIX \"u\"\n#  define FMTx64 FMT64_PREFIX \"x\"\n#  define FMTdPTR FMTPTR_PREFIX \"d\"\n#  define FMTuPTR FMTPTR_PREFIX \"u\"\n#  define FMTxPTR FMTPTR_PREFIX \"x\"\n#else\n#  include <inttypes.h>\n#  define FMTd32 PRId32\n#  define FMTu32 PRIu32\n#  define FMTx32 PRIx32\n#  define FMTd64 PRId64\n#  define FMTu64 PRIu64\n#  define FMTx64 PRIx64\n#  define FMTdPTR PRIdPTR\n#  define FMTuPTR PRIuPTR\n#  define FMTxPTR PRIxPTR\n#endif\n\n/* Size of stack-allocated buffer passed to buferror(). */\n#define BUFERROR_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 MALLOC_PRINTF_BUFSIZE\t4096\n\nint buferror(int err, char *buf, size_t buflen);\nuintmax_t malloc_strtoumax(const char *restrict nptr, char **restrict endptr,\n    int base);\nvoid malloc_write(const char *s);\n\n/*\n * malloc_vsnprintf() supports a subset of snprintf(3) that avoids floating\n * point math.\n */\nsize_t malloc_vsnprintf(char *str, size_t size, const char *format,\n    va_list ap);\nsize_t malloc_snprintf(char *str, size_t size, const char *format, ...)\n    JEMALLOC_FORMAT_PRINTF(3, 4);\n/*\n * The caller can set write_cb and cbopaque to null to choose to print with the\n * je_malloc_message hook.\n */\nvoid malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, va_list ap);\nvoid malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, ...) JEMALLOC_FORMAT_PRINTF(3, 4);\nvoid malloc_printf(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);\n\nstatic inline ssize_t\nmalloc_write_fd(int fd, const void *buf, size_t count) {\n#if defined(JEMALLOC_USE_SYSCALL) && defined(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\t * syscall() returns long or int, depending on platform, so capture the\n\t * result in the widest plausible type to avoid compiler warnings.\n\t */\n\tlong result = syscall(SYS_write, fd, buf, count);\n#else\n\tssize_t result = (ssize_t)write(fd, buf,\n#ifdef _WIN32\n\t    (unsigned int)\n#endif\n\t    count);\n#endif\n\treturn (ssize_t)result;\n}\n\nstatic inline ssize_t\nmalloc_read_fd(int fd, void *buf, size_t count) {\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_read)\n\tlong result = syscall(SYS_read, fd, buf, count);\n#else\n\tssize_t result = read(fd, buf,\n#ifdef _WIN32\n\t    (unsigned int)\n#endif\n\t    count);\n#endif\n\treturn (ssize_t)result;\n}\n\n#endif /* JEMALLOC_INTERNAL_MALLOC_IO_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/mutex.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MUTEX_H\n#define JEMALLOC_INTERNAL_MUTEX_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n#include \"jemalloc/internal/tsd.h\"\n#include \"jemalloc/internal/witness.h\"\n\ntypedef enum {\n\t/* Can only acquire one mutex of a given witness rank at a time. */\n\tmalloc_mutex_rank_exclusive,\n\t/*\n\t * Can acquire multiple mutexes of the same witness rank, but in\n\t * address-ascending order only.\n\t */\n\tmalloc_mutex_address_ordered\n} malloc_mutex_lock_order_t;\n\ntypedef struct malloc_mutex_s malloc_mutex_t;\nstruct malloc_mutex_s {\n\tunion {\n\t\tstruct {\n\t\t\t/*\n\t\t\t * prof_data is defined first to reduce cacheline\n\t\t\t * bouncing: the data is not touched by the mutex holder\n\t\t\t * during unlocking, while might be modified by\n\t\t\t * contenders.  Having it before the mutex itself could\n\t\t\t * avoid prefetching a modified cacheline (for the\n\t\t\t * unlocking thread).\n\t\t\t */\n\t\t\tmutex_prof_data_t\tprof_data;\n#ifdef _WIN32\n#  if _WIN32_WINNT >= 0x0600\n\t\t\tSRWLOCK         \tlock;\n#  else\n\t\t\tCRITICAL_SECTION\tlock;\n#  endif\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\t\t\tos_unfair_lock\t\tlock;\n#elif (defined(JEMALLOC_OSSPIN))\n\t\t\tOSSpinLock\t\tlock;\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n\t\t\tpthread_mutex_t\t\tlock;\n\t\t\tmalloc_mutex_t\t\t*postponed_next;\n#else\n\t\t\tpthread_mutex_t\t\tlock;\n#endif\n\t\t};\n\t\t/*\n\t\t * We only touch witness when configured w/ debug.  However we\n\t\t * keep the field in a union when !debug so that we don't have\n\t\t * to pollute the code base with #ifdefs, while avoid paying the\n\t\t * memory cost.\n\t\t */\n#if !defined(JEMALLOC_DEBUG)\n\t\twitness_t\t\t\twitness;\n\t\tmalloc_mutex_lock_order_t\tlock_order;\n#endif\n\t};\n\n#if defined(JEMALLOC_DEBUG)\n\twitness_t\t\t\twitness;\n\tmalloc_mutex_lock_order_t\tlock_order;\n#endif\n};\n\n/*\n * Based on benchmark results, a fixed spin with this amount of retries works\n * well for our critical sections.\n */\n#define MALLOC_MUTEX_MAX_SPIN 250\n\n#ifdef _WIN32\n#  if _WIN32_WINNT >= 0x0600\n#    define MALLOC_MUTEX_LOCK(m)    AcquireSRWLockExclusive(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  ReleaseSRWLockExclusive(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!TryAcquireSRWLockExclusive(&(m)->lock))\n#  else\n#    define MALLOC_MUTEX_LOCK(m)    EnterCriticalSection(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  LeaveCriticalSection(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!TryEnterCriticalSection(&(m)->lock))\n#  endif\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n#    define MALLOC_MUTEX_LOCK(m)    os_unfair_lock_lock(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  os_unfair_lock_unlock(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!os_unfair_lock_trylock(&(m)->lock))\n#elif (defined(JEMALLOC_OSSPIN))\n#    define MALLOC_MUTEX_LOCK(m)    OSSpinLockLock(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  OSSpinLockUnlock(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!OSSpinLockTry(&(m)->lock))\n#else\n#    define MALLOC_MUTEX_LOCK(m)    pthread_mutex_lock(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  pthread_mutex_unlock(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (pthread_mutex_trylock(&(m)->lock) != 0)\n#endif\n\n#define LOCK_PROF_DATA_INITIALIZER\t\t\t\t\t\\\n    {NSTIME_ZERO_INITIALIZER, NSTIME_ZERO_INITIALIZER, 0, 0, 0,\t\t\\\n\t    ATOMIC_INIT(0), 0, NULL, 0}\n\n#ifdef _WIN32\n#  define MALLOC_MUTEX_INITIALIZER\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n#  define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n     {{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT}},\t\t\\\n      WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#elif (defined(JEMALLOC_OSSPIN))\n#  define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n     {{{LOCK_PROF_DATA_INITIALIZER, 0}},\t\t\t\t\\\n      WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n#  define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n     {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL}},\t\\\n      WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#else\n#    define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT\n#    define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n       {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}},\t\\\n        WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#endif\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 malloc_mutex_init(malloc_mutex_t *mutex, const char *name,\n    witness_rank_t rank, malloc_mutex_lock_order_t lock_order);\nvoid malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex);\nvoid malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex);\nvoid malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex);\nbool malloc_mutex_boot(void);\nvoid malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex);\n\nvoid malloc_mutex_lock_slow(malloc_mutex_t *mutex);\n\nstatic inline void\nmalloc_mutex_lock_final(malloc_mutex_t *mutex) {\n\tMALLOC_MUTEX_LOCK(mutex);\n}\n\nstatic inline bool\nmalloc_mutex_trylock_final(malloc_mutex_t *mutex) {\n\treturn MALLOC_MUTEX_TRYLOCK(mutex);\n}\n\nstatic inline void\nmutex_owner_stats_update(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tif (config_stats) {\n\t\tmutex_prof_data_t *data = &mutex->prof_data;\n\t\tdata->n_lock_ops++;\n\t\tif (data->prev_owner != tsdn) {\n\t\t\tdata->prev_owner = tsdn;\n\t\t\tdata->n_owner_switches++;\n\t\t}\n\t}\n}\n\n/* Trylock: return false if the lock is successfully acquired. */\nstatic inline bool\nmalloc_mutex_trylock(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\tif (isthreaded) {\n\t\tif (malloc_mutex_trylock_final(mutex)) {\n\t\t\treturn true;\n\t\t}\n\t\tmutex_owner_stats_update(tsdn, mutex);\n\t}\n\twitness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\n\treturn false;\n}\n\n/* Aggregate lock prof data. */\nstatic inline void\nmalloc_mutex_prof_merge(mutex_prof_data_t *sum, mutex_prof_data_t *data) {\n\tnstime_add(&sum->tot_wait_time, &data->tot_wait_time);\n\tif (nstime_compare(&sum->max_wait_time, &data->max_wait_time) < 0) {\n\t\tnstime_copy(&sum->max_wait_time, &data->max_wait_time);\n\t}\n\n\tsum->n_wait_times += data->n_wait_times;\n\tsum->n_spin_acquired += data->n_spin_acquired;\n\n\tif (sum->max_n_thds < data->max_n_thds) {\n\t\tsum->max_n_thds = data->max_n_thds;\n\t}\n\tuint32_t cur_n_waiting_thds = atomic_load_u32(&sum->n_waiting_thds,\n\t    ATOMIC_RELAXED);\n\tuint32_t new_n_waiting_thds = cur_n_waiting_thds + atomic_load_u32(\n\t    &data->n_waiting_thds, ATOMIC_RELAXED);\n\tatomic_store_u32(&sum->n_waiting_thds, new_n_waiting_thds,\n\t    ATOMIC_RELAXED);\n\tsum->n_owner_switches += data->n_owner_switches;\n\tsum->n_lock_ops += data->n_lock_ops;\n}\n\nstatic inline void\nmalloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\tif (isthreaded) {\n\t\tif (malloc_mutex_trylock_final(mutex)) {\n\t\t\tmalloc_mutex_lock_slow(mutex);\n\t\t}\n\t\tmutex_owner_stats_update(tsdn, mutex);\n\t}\n\twitness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n}\n\nstatic inline void\nmalloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_unlock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\tif (isthreaded) {\n\t\tMALLOC_MUTEX_UNLOCK(mutex);\n\t}\n}\n\nstatic inline void\nmalloc_mutex_assert_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n}\n\nstatic inline void\nmalloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n}\n\n/* Copy the prof data from mutex for processing. */\nstatic inline void\nmalloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,\n    malloc_mutex_t *mutex) {\n\tmutex_prof_data_t *source = &mutex->prof_data;\n\t/* Can only read holding the mutex. */\n\tmalloc_mutex_assert_owner(tsdn, mutex);\n\n\t/*\n\t * Not *really* allowed (we shouldn't be doing non-atomic loads of\n\t * atomic data), but the mutex protection makes this safe, and writing\n\t * a member-for-member copy is tedious for this situation.\n\t */\n\t*data = *source;\n\t/* n_wait_thds is not reported (modified w/o locking). */\n\tatomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);\n}\n\n#endif /* JEMALLOC_INTERNAL_MUTEX_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/mutex_pool.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MUTEX_POOL_H\n#define JEMALLOC_INTERNAL_MUTEX_POOL_H\n\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/witness.h\"\n\n/* We do mod reductions by this value, so it should be kept a power of 2. */\n#define MUTEX_POOL_SIZE 256\n\ntypedef struct mutex_pool_s mutex_pool_t;\nstruct mutex_pool_s {\n\tmalloc_mutex_t mutexes[MUTEX_POOL_SIZE];\n};\n\nbool mutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank);\n\n/* Internal helper - not meant to be called outside this module. */\nstatic inline malloc_mutex_t *\nmutex_pool_mutex(mutex_pool_t *pool, uintptr_t key) {\n\tsize_t hash_result[2];\n\thash(&key, sizeof(key), 0xd50dcc1b, hash_result);\n\treturn &pool->mutexes[hash_result[0] % MUTEX_POOL_SIZE];\n}\n\nstatic inline void\nmutex_pool_assert_not_held(tsdn_t *tsdn, mutex_pool_t *pool) {\n\tfor (int i = 0; i < MUTEX_POOL_SIZE; i++) {\n\t\tmalloc_mutex_assert_not_owner(tsdn, &pool->mutexes[i]);\n\t}\n}\n\n/*\n * Note that a mutex pool doesn't work exactly the way an embdedded mutex would.\n * You're not allowed to acquire mutexes in the pool one at a time.  You have to\n * acquire all the mutexes you'll need in a single function call, and then\n * release them all in a single function call.\n */\n\nstatic inline void\nmutex_pool_lock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {\n\tmutex_pool_assert_not_held(tsdn, pool);\n\n\tmalloc_mutex_t *mutex = mutex_pool_mutex(pool, key);\n\tmalloc_mutex_lock(tsdn, mutex);\n}\n\nstatic inline void\nmutex_pool_unlock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {\n\tmalloc_mutex_t *mutex = mutex_pool_mutex(pool, key);\n\tmalloc_mutex_unlock(tsdn, mutex);\n\n\tmutex_pool_assert_not_held(tsdn, pool);\n}\n\nstatic inline void\nmutex_pool_lock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,\n    uintptr_t key2) {\n\tmutex_pool_assert_not_held(tsdn, pool);\n\n\tmalloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);\n\tmalloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);\n\tif ((uintptr_t)mutex1 < (uintptr_t)mutex2) {\n\t\tmalloc_mutex_lock(tsdn, mutex1);\n\t\tmalloc_mutex_lock(tsdn, mutex2);\n\t} else if ((uintptr_t)mutex1 == (uintptr_t)mutex2) {\n\t\tmalloc_mutex_lock(tsdn, mutex1);\n\t} else {\n\t\tmalloc_mutex_lock(tsdn, mutex2);\n\t\tmalloc_mutex_lock(tsdn, mutex1);\n\t}\n}\n\nstatic inline void\nmutex_pool_unlock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,\n    uintptr_t key2) {\n\tmalloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);\n\tmalloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);\n\tif (mutex1 == mutex2) {\n\t\tmalloc_mutex_unlock(tsdn, mutex1);\n\t} else {\n\t\tmalloc_mutex_unlock(tsdn, mutex1);\n\t\tmalloc_mutex_unlock(tsdn, mutex2);\n\t}\n\n\tmutex_pool_assert_not_held(tsdn, pool);\n}\n\nstatic inline void\nmutex_pool_assert_owner(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {\n\tmalloc_mutex_assert_owner(tsdn, mutex_pool_mutex(pool, key));\n}\n\n#endif /* JEMALLOC_INTERNAL_MUTEX_POOL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/mutex_prof.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MUTEX_PROF_H\n#define JEMALLOC_INTERNAL_MUTEX_PROF_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/nstime.h\"\n#include \"jemalloc/internal/tsd_types.h\"\n\n#define MUTEX_PROF_GLOBAL_MUTEXES\t\t\t\t\t\\\n    OP(background_thread)\t\t\t\t\t\t\\\n    OP(ctl)\t\t\t\t\t\t\t\t\\\n    OP(prof)\n\ntypedef enum {\n#define OP(mtx) global_prof_mutex_##mtx,\n\tMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\tmutex_prof_num_global_mutexes\n} mutex_prof_global_ind_t;\n\n#define MUTEX_PROF_ARENA_MUTEXES\t\t\t\t\t\\\n    OP(large)\t\t\t\t\t\t\t\t\\\n    OP(extent_avail)\t\t\t\t\t\t\t\\\n    OP(extents_dirty)\t\t\t\t\t\t\t\\\n    OP(extents_muzzy)\t\t\t\t\t\t\t\\\n    OP(extents_retained)\t\t\t\t\t\t\\\n    OP(decay_dirty)\t\t\t\t\t\t\t\\\n    OP(decay_muzzy)\t\t\t\t\t\t\t\\\n    OP(base)\t\t\t\t\t\t\t\t\\\n    OP(tcache_list)\n\ntypedef enum {\n#define OP(mtx) arena_prof_mutex_##mtx,\n\tMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\tmutex_prof_num_arena_mutexes\n} mutex_prof_arena_ind_t;\n\n#define MUTEX_PROF_UINT64_COUNTERS\t\t\t\t\t\\\n    OP(num_ops, uint64_t, \"n_lock_ops\")\t\t\t\t\t\\\n    OP(num_wait, uint64_t, \"n_waiting\")\t\t\t\t\t\\\n    OP(num_spin_acq, uint64_t, \"n_spin_acq\")\t\t\t\t\\\n    OP(num_owner_switch, uint64_t, \"n_owner_switch\")\t\t\t\\\n    OP(total_wait_time, uint64_t, \"total_wait_ns\")\t\t\t\\\n    OP(max_wait_time, uint64_t, \"max_wait_ns\")\n\n#define MUTEX_PROF_UINT32_COUNTERS\t\t\t\t\t\\\n    OP(max_num_thds, uint32_t, \"max_n_thds\")\n\n#define MUTEX_PROF_COUNTERS\t\t\t\t\t\t\\\n\t\tMUTEX_PROF_UINT64_COUNTERS\t\t\t\t\\\n\t\tMUTEX_PROF_UINT32_COUNTERS\n\n#define OP(counter, type, human) mutex_counter_##counter,\n\n#define COUNTER_ENUM(counter_list, t)\t\t\t\t\t\\\n\t\ttypedef enum {\t\t\t\t\t\t\\\n\t\t\tcounter_list\t\t\t\t\t\\\n\t\t\tmutex_prof_num_##t##_counters\t\t\t\\\n\t\t} mutex_prof_##t##_counter_ind_t;\n\nCOUNTER_ENUM(MUTEX_PROF_UINT64_COUNTERS, uint64_t)\nCOUNTER_ENUM(MUTEX_PROF_UINT32_COUNTERS, uint32_t)\n\n#undef COUNTER_ENUM\n#undef OP\n\ntypedef struct {\n\t/*\n\t * Counters touched on the slow path, i.e. when there is lock\n\t * contention.  We update them once we have the lock.\n\t */\n\t/* Total time (in nano seconds) spent waiting on this mutex. */\n\tnstime_t\t\ttot_wait_time;\n\t/* Max time (in nano seconds) spent on a single lock operation. */\n\tnstime_t\t\tmax_wait_time;\n\t/* # of times have to wait for this mutex (after spinning). */\n\tuint64_t\t\tn_wait_times;\n\t/* # of times acquired the mutex through local spinning. */\n\tuint64_t\t\tn_spin_acquired;\n\t/* Max # of threads waiting for the mutex at the same time. */\n\tuint32_t\t\tmax_n_thds;\n\t/* Current # of threads waiting on the lock.  Atomic synced. */\n\tatomic_u32_t\t\tn_waiting_thds;\n\n\t/*\n\t * Data touched on the fast path.  These are modified right after we\n\t * grab the lock, so it's placed closest to the end (i.e. right before\n\t * the lock) so that we have a higher chance of them being on the same\n\t * cacheline.\n\t */\n\t/* # of times the mutex holder is different than the previous one. */\n\tuint64_t\t\tn_owner_switches;\n\t/* Previous mutex holder, to facilitate n_owner_switches. */\n\ttsdn_t\t\t\t*prev_owner;\n\t/* # of lock() operations in total. */\n\tuint64_t\t\tn_lock_ops;\n} mutex_prof_data_t;\n\n#endif /* JEMALLOC_INTERNAL_MUTEX_PROF_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/nstime.h",
    "content": "#ifndef JEMALLOC_INTERNAL_NSTIME_H\n#define JEMALLOC_INTERNAL_NSTIME_H\n\n/* Maximum supported number of seconds (~584 years). */\n#define NSTIME_SEC_MAX KQU(18446744072)\n#define NSTIME_ZERO_INITIALIZER {0}\n\ntypedef struct {\n\tuint64_t ns;\n} nstime_t;\n\nvoid nstime_init(nstime_t *time, uint64_t ns);\nvoid nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec);\nuint64_t nstime_ns(const nstime_t *time);\nuint64_t nstime_sec(const nstime_t *time);\nuint64_t nstime_msec(const nstime_t *time);\nuint64_t nstime_nsec(const nstime_t *time);\nvoid nstime_copy(nstime_t *time, const nstime_t *source);\nint nstime_compare(const nstime_t *a, const nstime_t *b);\nvoid nstime_add(nstime_t *time, const nstime_t *addend);\nvoid nstime_iadd(nstime_t *time, uint64_t addend);\nvoid nstime_subtract(nstime_t *time, const nstime_t *subtrahend);\nvoid nstime_isubtract(nstime_t *time, uint64_t subtrahend);\nvoid nstime_imultiply(nstime_t *time, uint64_t multiplier);\nvoid nstime_idivide(nstime_t *time, uint64_t divisor);\nuint64_t nstime_divide(const nstime_t *time, const nstime_t *divisor);\n\ntypedef bool (nstime_monotonic_t)(void);\nextern nstime_monotonic_t *JET_MUTABLE nstime_monotonic;\n\ntypedef bool (nstime_update_t)(nstime_t *);\nextern nstime_update_t *JET_MUTABLE nstime_update;\n\n#endif /* JEMALLOC_INTERNAL_NSTIME_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/pages.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PAGES_EXTERNS_H\n#define JEMALLOC_INTERNAL_PAGES_EXTERNS_H\n\n/* Page size.  LG_PAGE is determined by the configure script. */\n#ifdef PAGE_MASK\n#  undef PAGE_MASK\n#endif\n#define PAGE\t\t((size_t)(1U << LG_PAGE))\n#define PAGE_MASK\t((size_t)(PAGE - 1))\n/* Return the page base address for the page containing address a. */\n#define PAGE_ADDR2BASE(a)\t\t\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ~PAGE_MASK))\n/* Return the smallest pagesize multiple that is >= s. */\n#define PAGE_CEILING(s)\t\t\t\t\t\t\t\\\n\t(((s) + PAGE_MASK) & ~PAGE_MASK)\n\n/* Huge page size.  LG_HUGEPAGE is determined by the configure script. */\n#define HUGEPAGE\t((size_t)(1U << LG_HUGEPAGE))\n#define HUGEPAGE_MASK\t((size_t)(HUGEPAGE - 1))\n/* Return the huge page base address for the huge page containing address a. */\n#define HUGEPAGE_ADDR2BASE(a)\t\t\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ~HUGEPAGE_MASK))\n/* Return the smallest pagesize multiple that is >= s. */\n#define HUGEPAGE_CEILING(s)\t\t\t\t\t\t\\\n\t(((s) + HUGEPAGE_MASK) & ~HUGEPAGE_MASK)\n\n/* PAGES_CAN_PURGE_LAZY is defined if lazy purging is supported. */\n#if defined(_WIN32) || defined(JEMALLOC_PURGE_MADVISE_FREE)\n#  define PAGES_CAN_PURGE_LAZY\n#endif\n/*\n * PAGES_CAN_PURGE_FORCED is defined if forced purging is supported.\n *\n * The only supported way to hard-purge on Windows is to decommit and then\n * re-commit, but doing so is racy, and if re-commit fails it's a pain to\n * propagate the \"poisoned\" memory state.  Since we typically decommit as the\n * next step after purging on Windows anyway, there's no point in adding such\n * complexity.\n */\n#if !defined(_WIN32) && ((defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \\\n    defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)) || \\\n    defined(JEMALLOC_MAPS_COALESCE))\n#  define PAGES_CAN_PURGE_FORCED\n#endif\n\nstatic const bool pages_can_purge_lazy =\n#ifdef PAGES_CAN_PURGE_LAZY\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool pages_can_purge_forced =\n#ifdef PAGES_CAN_PURGE_FORCED\n    true\n#else\n    false\n#endif\n    ;\n\ntypedef enum {\n\tthp_mode_default       = 0, /* Do not change hugepage settings. */\n\tthp_mode_always        = 1, /* Always set MADV_HUGEPAGE. */\n\tthp_mode_never         = 2, /* Always set MADV_NOHUGEPAGE. */\n\n\tthp_mode_names_limit   = 3, /* Used for option processing. */\n\tthp_mode_not_supported = 3  /* No THP support detected. */\n} thp_mode_t;\n\n#define THP_MODE_DEFAULT thp_mode_default\nextern thp_mode_t opt_thp;\nextern thp_mode_t init_system_thp_mode; /* Initial system wide state. */\nextern const char *thp_mode_names[];\n\nvoid *pages_map(void *addr, size_t size, size_t alignment, bool *commit);\nvoid pages_unmap(void *addr, size_t size);\nbool pages_commit(void *addr, size_t size);\nbool pages_decommit(void *addr, size_t size);\nbool pages_purge_lazy(void *addr, size_t size);\nbool pages_purge_forced(void *addr, size_t size);\nbool pages_huge(void *addr, size_t size);\nbool pages_nohuge(void *addr, size_t size);\nbool pages_dontdump(void *addr, size_t size);\nbool pages_dodump(void *addr, size_t size);\nbool pages_boot(void);\nvoid pages_set_thp_state (void *ptr, size_t size);\n\n#endif /* JEMALLOC_INTERNAL_PAGES_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ph.h",
    "content": "/*\n * A Pairing Heap implementation.\n *\n * \"The Pairing Heap: A New Form of Self-Adjusting Heap\"\n * https://www.cs.cmu.edu/~sleator/papers/pairing-heaps.pdf\n *\n * With auxiliary twopass list, described in a follow on paper.\n *\n * \"Pairing Heaps: Experiments and Analysis\"\n * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.2988&rep=rep1&type=pdf\n *\n *******************************************************************************\n */\n\n#ifndef PH_H_\n#define PH_H_\n\n/* Node structure. */\n#define phn(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\ta_type\t*phn_prev;\t\t\t\t\t\t\\\n\ta_type\t*phn_next;\t\t\t\t\t\t\\\n\ta_type\t*phn_lchild;\t\t\t\t\t\t\\\n}\n\n/* Root structure. */\n#define ph(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\ta_type\t*ph_root;\t\t\t\t\t\t\\\n}\n\n/* Internal utility macros. */\n#define phn_lchild_get(a_type, a_field, a_phn)\t\t\t\t\\\n\t(a_phn->a_field.phn_lchild)\n#define phn_lchild_set(a_type, a_field, a_phn, a_lchild) do {\t\t\\\n\ta_phn->a_field.phn_lchild = a_lchild;\t\t\t\t\\\n} while (0)\n\n#define phn_next_get(a_type, a_field, a_phn)\t\t\t\t\\\n\t(a_phn->a_field.phn_next)\n#define phn_prev_set(a_type, a_field, a_phn, a_prev) do {\t\t\\\n\ta_phn->a_field.phn_prev = a_prev;\t\t\t\t\\\n} while (0)\n\n#define phn_prev_get(a_type, a_field, a_phn)\t\t\t\t\\\n\t(a_phn->a_field.phn_prev)\n#define phn_next_set(a_type, a_field, a_phn, a_next) do {\t\t\\\n\ta_phn->a_field.phn_next = a_next;\t\t\t\t\\\n} while (0)\n\n#define phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, a_cmp) do {\t\\\n\ta_type *phn0child;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_phn0 != NULL);\t\t\t\t\t\t\\\n\tassert(a_phn1 != NULL);\t\t\t\t\t\t\\\n\tassert(a_cmp(a_phn0, a_phn1) <= 0);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tphn_prev_set(a_type, a_field, a_phn1, a_phn0);\t\t\t\\\n\tphn0child = phn_lchild_get(a_type, a_field, a_phn0);\t\t\\\n\tphn_next_set(a_type, a_field, a_phn1, phn0child);\t\t\\\n\tif (phn0child != NULL) {\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn0child, a_phn1);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tphn_lchild_set(a_type, a_field, a_phn0, a_phn1);\t\t\\\n} while (0)\n\n#define phn_merge(a_type, a_field, a_phn0, a_phn1, a_cmp, r_phn) do {\t\\\n\tif (a_phn0 == NULL) {\t\t\t\t\t\t\\\n\t\tr_phn = a_phn1;\t\t\t\t\t\t\\\n\t} else if (a_phn1 == NULL) {\t\t\t\t\t\\\n\t\tr_phn = a_phn0;\t\t\t\t\t\t\\\n\t} else if (a_cmp(a_phn0, a_phn1) < 0) {\t\t\t\t\\\n\t\tphn_merge_ordered(a_type, a_field, a_phn0, a_phn1,\t\\\n\t\t    a_cmp);\t\t\t\t\t\t\\\n\t\tr_phn = a_phn0;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tphn_merge_ordered(a_type, a_field, a_phn1, a_phn0,\t\\\n\t\t    a_cmp);\t\t\t\t\t\t\\\n\t\tr_phn = a_phn1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ph_merge_siblings(a_type, a_field, a_phn, a_cmp, r_phn) do {\t\\\n\ta_type *head = NULL;\t\t\t\t\t\t\\\n\ta_type *tail = NULL;\t\t\t\t\t\t\\\n\ta_type *phn0 = a_phn;\t\t\t\t\t\t\\\n\ta_type *phn1 = phn_next_get(a_type, a_field, phn0);\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * Multipass merge, wherein the first two elements of a FIFO\t\\\n\t * are repeatedly merged, and each result is appended to the\t\\\n\t * singly linked FIFO, until the FIFO contains only a single\t\\\n\t * element.  We start with a sibling list but no reference to\t\\\n\t * its tail, so we do a single pass over the sibling list to\t\\\n\t * populate the FIFO.\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tif (phn1 != NULL) {\t\t\t\t\t\t\\\n\t\ta_type *phnrest = phn_next_get(a_type, a_field, phn1);\t\\\n\t\tif (phnrest != NULL) {\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, phnrest, NULL);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn0, NULL);\t\t\\\n\t\tphn_next_set(a_type, a_field, phn0, NULL);\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn1, NULL);\t\t\\\n\t\tphn_next_set(a_type, a_field, phn1, NULL);\t\t\\\n\t\tphn_merge(a_type, a_field, phn0, phn1, a_cmp, phn0);\t\\\n\t\thead = tail = phn0;\t\t\t\t\t\\\n\t\tphn0 = phnrest;\t\t\t\t\t\t\\\n\t\twhile (phn0 != NULL) {\t\t\t\t\t\\\n\t\t\tphn1 = phn_next_get(a_type, a_field, phn0);\t\\\n\t\t\tif (phn1 != NULL) {\t\t\t\t\\\n\t\t\t\tphnrest = phn_next_get(a_type, a_field,\t\\\n\t\t\t\t    phn1);\t\t\t\t\\\n\t\t\t\tif (phnrest != NULL) {\t\t\t\\\n\t\t\t\t\tphn_prev_set(a_type, a_field,\t\\\n\t\t\t\t\t    phnrest, NULL);\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field, phn0,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn0,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field, phn1,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn1,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_merge(a_type, a_field, phn0, phn1,\t\\\n\t\t\t\t    a_cmp, phn0);\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, tail,\t\\\n\t\t\t\t    phn0);\t\t\t\t\\\n\t\t\t\ttail = phn0;\t\t\t\t\\\n\t\t\t\tphn0 = phnrest;\t\t\t\t\\\n\t\t\t} else {\t\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, tail,\t\\\n\t\t\t\t    phn0);\t\t\t\t\\\n\t\t\t\ttail = phn0;\t\t\t\t\\\n\t\t\t\tphn0 = NULL;\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\tphn0 = head;\t\t\t\t\t\t\\\n\t\tphn1 = phn_next_get(a_type, a_field, phn0);\t\t\\\n\t\tif (phn1 != NULL) {\t\t\t\t\t\\\n\t\t\twhile (true) {\t\t\t\t\t\\\n\t\t\t\thead = phn_next_get(a_type, a_field,\t\\\n\t\t\t\t    phn1);\t\t\t\t\\\n\t\t\t\tassert(phn_prev_get(a_type, a_field,\t\\\n\t\t\t\t    phn0) == NULL);\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn0,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tassert(phn_prev_get(a_type, a_field,\t\\\n\t\t\t\t    phn1) == NULL);\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn1,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_merge(a_type, a_field, phn0, phn1,\t\\\n\t\t\t\t    a_cmp, phn0);\t\t\t\\\n\t\t\t\tif (head == NULL) {\t\t\t\\\n\t\t\t\t\tbreak;\t\t\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, tail,\t\\\n\t\t\t\t    phn0);\t\t\t\t\\\n\t\t\t\ttail = phn0;\t\t\t\t\\\n\t\t\t\tphn0 = head;\t\t\t\t\\\n\t\t\t\tphn1 = phn_next_get(a_type, a_field,\t\\\n\t\t\t\t    phn0);\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\tr_phn = phn0;\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ph_merge_aux(a_type, a_field, a_ph, a_cmp) do {\t\t\t\\\n\ta_type *phn = phn_next_get(a_type, a_field, a_ph->ph_root);\t\\\n\tif (phn != NULL) {\t\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, a_ph->ph_root, NULL);\t\\\n\t\tphn_next_set(a_type, a_field, a_ph->ph_root, NULL);\t\\\n\t\tphn_prev_set(a_type, a_field, phn, NULL);\t\t\\\n\t\tph_merge_siblings(a_type, a_field, phn, a_cmp, phn);\t\\\n\t\tassert(phn_next_get(a_type, a_field, phn) == NULL);\t\\\n\t\tphn_merge(a_type, a_field, a_ph->ph_root, phn, a_cmp,\t\\\n\t\t    a_ph->ph_root);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ph_merge_children(a_type, a_field, a_phn, a_cmp, r_phn) do {\t\\\n\ta_type *lchild = phn_lchild_get(a_type, a_field, a_phn);\t\\\n\tif (lchild == NULL) {\t\t\t\t\t\t\\\n\t\tr_phn = NULL;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tph_merge_siblings(a_type, a_field, lchild, a_cmp,\t\\\n\t\t    r_phn);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/*\n * The ph_proto() macro generates function prototypes that correspond to the\n * functions generated by an equivalently parameterized call to ph_gen().\n */\n#define ph_proto(a_attr, a_prefix, a_ph_type, a_type)\t\t\t\\\na_attr void\ta_prefix##new(a_ph_type *ph);\t\t\t\t\\\na_attr bool\ta_prefix##empty(a_ph_type *ph);\t\t\t\t\\\na_attr a_type\t*a_prefix##first(a_ph_type *ph);\t\t\t\\\na_attr a_type\t*a_prefix##any(a_ph_type *ph);\t\t\t\t\\\na_attr void\ta_prefix##insert(a_ph_type *ph, a_type *phn);\t\t\\\na_attr a_type\t*a_prefix##remove_first(a_ph_type *ph);\t\t\t\\\na_attr a_type\t*a_prefix##remove_any(a_ph_type *ph);\t\t\t\\\na_attr void\ta_prefix##remove(a_ph_type *ph, a_type *phn);\n\n/*\n * The ph_gen() macro generates a type-specific pairing heap implementation,\n * based on the above cpp macros.\n */\n#define ph_gen(a_attr, a_prefix, a_ph_type, a_type, a_field, a_cmp)\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##new(a_ph_type *ph) {\t\t\t\t\t\t\\\n\tmemset(ph, 0, sizeof(ph(a_type)));\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_prefix##empty(a_ph_type *ph) {\t\t\t\t\t\\\n\treturn (ph->ph_root == NULL);\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_ph_type *ph) {\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tph_merge_aux(a_type, a_field, ph, a_cmp);\t\t\t\\\n\treturn ph->ph_root;\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##any(a_ph_type *ph) {\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_type *aux = phn_next_get(a_type, a_field, ph->ph_root);\t\\\n\tif (aux != NULL) {\t\t\t\t\t\t\\\n\t\treturn aux;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn ph->ph_root;\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_ph_type *ph, a_type *phn) {\t\t\t\t\\\n\tmemset(&phn->a_field, 0, sizeof(phn(a_type)));\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 * Treat the root as an aux list during insertion, and lazily\t\\\n\t * merge during a_prefix##remove_first().  For elements that\t\\\n\t * are inserted, then removed via a_prefix##remove() before the\t\\\n\t * aux list is ever processed, this makes insert/remove\t\t\\\n\t * constant-time, whereas eager merging would make insert\t\\\n\t * O(log n).\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\tph->ph_root = phn;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tphn_next_set(a_type, a_field, phn, phn_next_get(a_type,\t\\\n\t\t    a_field, ph->ph_root));\t\t\t\t\\\n\t\tif (phn_next_get(a_type, a_field, ph->ph_root) !=\t\\\n\t\t    NULL) {\t\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, ph->ph_root),\t\\\n\t\t\t    phn);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn, ph->ph_root);\t\\\n\t\tphn_next_set(a_type, a_field, ph->ph_root, phn);\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##remove_first(a_ph_type *ph) {\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tph_merge_aux(a_type, a_field, ph, a_cmp);\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = ph->ph_root;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tph_merge_children(a_type, a_field, ph->ph_root, a_cmp,\t\t\\\n\t    ph->ph_root);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\treturn 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##remove_any(a_ph_type *ph) {\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * Remove the most recently inserted aux list element, or the\t\\\n\t * root if the aux list is empty.  This has the effect of\t\\\n\t * behaving as a LIFO (and insertion/removal is therefore\t\\\n\t * constant-time) if a_prefix##[remove_]first() are never\t\\\n\t * called.\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_type *ret = phn_next_get(a_type, a_field, ph->ph_root);\t\\\n\tif (ret != NULL) {\t\t\t\t\t\t\\\n\t\ta_type *aux = phn_next_get(a_type, a_field, ret);\t\\\n\t\tphn_next_set(a_type, a_field, ph->ph_root, aux);\t\\\n\t\tif (aux != NULL) {\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, aux,\t\t\\\n\t\t\t    ph->ph_root);\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\treturn ret;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tret = ph->ph_root;\t\t\t\t\t\t\\\n\tph_merge_children(a_type, a_field, ph->ph_root, a_cmp,\t\t\\\n\t    ph->ph_root);\t\t\t\t\t\t\\\n\treturn 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##remove(a_ph_type *ph, a_type *phn) {\t\t\t\t\\\n\ta_type *replace, *parent;\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == phn) {\t\t\t\t\t\\\n\t\t/*\t\t\t\t\t\t\t\\\n\t\t * We can delete from aux list without merging it, but\t\\\n\t\t * we need to merge if we are dealing with the root\t\\\n\t\t * node and it has children.\t\t\t\t\\\n\t\t */\t\t\t\t\t\t\t\\\n\t\tif (phn_lchild_get(a_type, a_field, phn) == NULL) {\t\\\n\t\t\tph->ph_root = phn_next_get(a_type, a_field,\t\\\n\t\t\t    phn);\t\t\t\t\t\\\n\t\t\tif (ph->ph_root != NULL) {\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field,\t\t\\\n\t\t\t\t    ph->ph_root, NULL);\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\tph_merge_aux(a_type, a_field, ph, a_cmp);\t\t\\\n\t\tif (ph->ph_root == phn) {\t\t\t\t\\\n\t\t\tph_merge_children(a_type, a_field, ph->ph_root,\t\\\n\t\t\t    a_cmp, ph->ph_root);\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\t\t\t\t\t\t\t\t\t\\\n\t/* Get parent (if phn is leftmost child) before mutating. */\t\\\n\tif ((parent = phn_prev_get(a_type, a_field, phn)) != NULL) {\t\\\n\t\tif (phn_lchild_get(a_type, a_field, parent) != phn) {\t\\\n\t\t\tparent = 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/* Find a possible replacement node, and link to parent. */\t\\\n\tph_merge_children(a_type, a_field, phn, a_cmp, replace);\t\\\n\t/* Set next/prev for sibling linked list. */\t\t\t\\\n\tif (replace != NULL) {\t\t\t\t\t\t\\\n\t\tif (parent != NULL) {\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, replace, parent);\t\\\n\t\t\tphn_lchild_set(a_type, a_field, parent,\t\t\\\n\t\t\t    replace);\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, replace,\t\t\\\n\t\t\t    phn_prev_get(a_type, a_field, phn));\t\\\n\t\t\tif (phn_prev_get(a_type, a_field, phn) !=\t\\\n\t\t\t    NULL) {\t\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field,\t\t\\\n\t\t\t\t    phn_prev_get(a_type, a_field, phn),\t\\\n\t\t\t\t    replace);\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\tphn_next_set(a_type, a_field, replace,\t\t\t\\\n\t\t    phn_next_get(a_type, a_field, phn));\t\t\\\n\t\tif (phn_next_get(a_type, a_field, phn) != NULL) {\t\\\n\t\t\tphn_prev_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, phn),\t\t\\\n\t\t\t    replace);\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\tif (parent != NULL) {\t\t\t\t\t\\\n\t\t\ta_type *next = phn_next_get(a_type, a_field,\t\\\n\t\t\t    phn);\t\t\t\t\t\\\n\t\t\tphn_lchild_set(a_type, a_field, parent, next);\t\\\n\t\t\tif (next != NULL) {\t\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field, next,\t\\\n\t\t\t\t    parent);\t\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tassert(phn_prev_get(a_type, a_field, phn) !=\t\\\n\t\t\t    NULL);\t\t\t\t\t\\\n\t\t\tphn_next_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_prev_get(a_type, a_field, phn),\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, phn));\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (phn_next_get(a_type, a_field, phn) != NULL) {\t\\\n\t\t\tphn_prev_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, phn),\t\t\\\n\t\t\t    phn_prev_get(a_type, a_field, phn));\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n\n#endif /* PH_H_ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/private_namespace.sh",
    "content": "#!/bin/sh\n\nfor symbol in `cat \"$@\"` ; do\n  echo \"#define ${symbol} JEMALLOC_N(${symbol})\"\ndone\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/private_symbols.sh",
    "content": "#!/bin/sh\n#\n# Generate private_symbols[_jet].awk.\n#\n# Usage: private_symbols.sh <sym_prefix> <sym>*\n#\n# <sym_prefix> is typically \"\" or \"_\".\n\nsym_prefix=$1\nshift\n\ncat <<EOF\n#!/usr/bin/env awk -f\n\nBEGIN {\n  sym_prefix = \"${sym_prefix}\"\n  split(\"\\\\\nEOF\n\nfor public_sym in \"$@\" ; do\n  cat <<EOF\n        ${sym_prefix}${public_sym} \\\\\nEOF\ndone\n\ncat <<\"EOF\"\n        \", exported_symbol_names)\n  # Store exported symbol names as keys in exported_symbols.\n  for (i in exported_symbol_names) {\n    exported_symbols[exported_symbol_names[i]] = 1\n  }\n}\n\n# Process 'nm -a <c_source.o>' output.\n#\n# Handle lines like:\n#   0000000000000008 D opt_junk\n#   0000000000007574 T malloc_initialized\n(NF == 3 && $2 ~ /^[ABCDGRSTVW]$/ && !($3 in exported_symbols) && $3 ~ /^[A-Za-z0-9_]+$/) {\n  print substr($3, 1+length(sym_prefix), length($3)-length(sym_prefix))\n}\n\n# Process 'dumpbin /SYMBOLS <c_source.obj>' output.\n#\n# Handle lines like:\n#   353 00008098 SECT4  notype       External     | opt_junk\n#   3F1 00000000 SECT7  notype ()    External     | malloc_initialized\n($3 ~ /^SECT[0-9]+/ && $(NF-2) == \"External\" && !($NF in exported_symbols)) {\n  print $NF\n}\nEOF\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prng.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PRNG_H\n#define JEMALLOC_INTERNAL_PRNG_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bit_util.h\"\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\n/******************************************************************************/\n/* INTERNAL DEFINITIONS -- IGNORE */\n/******************************************************************************/\n#define PRNG_A_32\tUINT32_C(1103515241)\n#define PRNG_C_32\tUINT32_C(12347)\n\n#define PRNG_A_64\tUINT64_C(6364136223846793005)\n#define PRNG_C_64\tUINT64_C(1442695040888963407)\n\nJEMALLOC_ALWAYS_INLINE uint32_t\nprng_state_next_u32(uint32_t state) {\n\treturn (state * PRNG_A_32) + PRNG_C_32;\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nprng_state_next_u64(uint64_t state) {\n\treturn (state * PRNG_A_64) + PRNG_C_64;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nprng_state_next_zu(size_t state) {\n#if LG_SIZEOF_PTR == 2\n\treturn (state * PRNG_A_32) + PRNG_C_32;\n#elif LG_SIZEOF_PTR == 3\n\treturn (state * PRNG_A_64) + PRNG_C_64;\n#else\n#error Unsupported pointer size\n#endif\n}\n\n/******************************************************************************/\n/* BEGIN PUBLIC API */\n/******************************************************************************/\n\n/*\n * The prng_lg_range functions give a uniform int in the half-open range [0,\n * 2**lg_range).  If atomic is true, they do so safely from multiple threads.\n * Multithreaded 64-bit prngs aren't supported.\n */\n\nJEMALLOC_ALWAYS_INLINE uint32_t\nprng_lg_range_u32(atomic_u32_t *state, unsigned lg_range, bool atomic) {\n\tuint32_t ret, state0, state1;\n\n\tassert(lg_range > 0);\n\tassert(lg_range <= 32);\n\n\tstate0 = atomic_load_u32(state, ATOMIC_RELAXED);\n\n\tif (atomic) {\n\t\tdo {\n\t\t\tstate1 = prng_state_next_u32(state0);\n\t\t} while (!atomic_compare_exchange_weak_u32(state, &state0,\n\t\t    state1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n\t} else {\n\t\tstate1 = prng_state_next_u32(state0);\n\t\tatomic_store_u32(state, state1, ATOMIC_RELAXED);\n\t}\n\tret = state1 >> (32 - lg_range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nprng_lg_range_u64(uint64_t *state, unsigned lg_range) {\n\tuint64_t ret, state1;\n\n\tassert(lg_range > 0);\n\tassert(lg_range <= 64);\n\n\tstate1 = prng_state_next_u64(*state);\n\t*state = state1;\n\tret = state1 >> (64 - lg_range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nprng_lg_range_zu(atomic_zu_t *state, unsigned lg_range, bool atomic) {\n\tsize_t ret, state0, state1;\n\n\tassert(lg_range > 0);\n\tassert(lg_range <= ZU(1) << (3 + LG_SIZEOF_PTR));\n\n\tstate0 = atomic_load_zu(state, ATOMIC_RELAXED);\n\n\tif (atomic) {\n\t\tdo {\n\t\t\tstate1 = prng_state_next_zu(state0);\n\t\t} while (atomic_compare_exchange_weak_zu(state, &state0,\n\t\t    state1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n\t} else {\n\t\tstate1 = prng_state_next_zu(state0);\n\t\tatomic_store_zu(state, state1, ATOMIC_RELAXED);\n\t}\n\tret = state1 >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) - lg_range);\n\n\treturn ret;\n}\n\n/*\n * The prng_range functions behave like the prng_lg_range, but return a result\n * in [0, range) instead of [0, 2**lg_range).\n */\n\nJEMALLOC_ALWAYS_INLINE uint32_t\nprng_range_u32(atomic_u32_t *state, uint32_t range, bool atomic) {\n\tuint32_t ret;\n\tunsigned lg_range;\n\n\tassert(range > 1);\n\n\t/* Compute the ceiling of lg(range). */\n\tlg_range = ffs_u32(pow2_ceil_u32(range)) - 1;\n\n\t/* Generate a result in [0..range) via repeated trial. */\n\tdo {\n\t\tret = prng_lg_range_u32(state, lg_range, atomic);\n\t} while (ret >= range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nprng_range_u64(uint64_t *state, uint64_t range) {\n\tuint64_t ret;\n\tunsigned lg_range;\n\n\tassert(range > 1);\n\n\t/* Compute the ceiling of lg(range). */\n\tlg_range = ffs_u64(pow2_ceil_u64(range)) - 1;\n\n\t/* Generate a result in [0..range) via repeated trial. */\n\tdo {\n\t\tret = prng_lg_range_u64(state, lg_range);\n\t} while (ret >= range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nprng_range_zu(atomic_zu_t *state, size_t range, bool atomic) {\n\tsize_t ret;\n\tunsigned lg_range;\n\n\tassert(range > 1);\n\n\t/* Compute the ceiling of lg(range). */\n\tlg_range = ffs_u64(pow2_ceil_u64(range)) - 1;\n\n\t/* Generate a result in [0..range) via repeated trial. */\n\tdo {\n\t\tret = prng_lg_range_zu(state, lg_range, atomic);\n\t} while (ret >= range);\n\n\treturn ret;\n}\n\n#endif /* JEMALLOC_INTERNAL_PRNG_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_EXTERNS_H\n#define JEMALLOC_INTERNAL_PROF_EXTERNS_H\n\n#include \"jemalloc/internal/mutex.h\"\n\nextern malloc_mutex_t\tbt2gctx_mtx;\n\nextern bool\topt_prof;\nextern bool\topt_prof_active;\nextern bool\topt_prof_thread_active_init;\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[\n    /* Minimize memory bloat for non-prof builds. */\n#ifdef JEMALLOC_PROF\n    PATH_MAX +\n#endif\n    1];\n\n/* Accessed via prof_active_[gs]et{_unlocked,}(). */\nextern bool\tprof_active;\n\n/* Accessed via prof_gdump_[gs]et{_unlocked,}(). */\nextern bool\tprof_gdump_val;\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 * Initialized as opt_lg_prof_sample, and potentially modified during profiling\n * resets.\n */\nextern size_t\tlg_prof_sample;\n\nvoid prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated);\nvoid prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,\n    prof_tctx_t *tctx);\nvoid prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx);\nvoid bt_init(prof_bt_t *bt, void **vec);\nvoid prof_backtrace(prof_bt_t *bt);\nprof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt);\n#ifdef JEMALLOC_JET\nsize_t prof_tdata_count(void);\nsize_t prof_bt_count(void);\n#endif\ntypedef int (prof_dump_open_t)(bool, const char *);\nextern prof_dump_open_t *JET_MUTABLE prof_dump_open;\n\ntypedef bool (prof_dump_header_t)(tsdn_t *, bool, const prof_cnt_t *);\nextern prof_dump_header_t *JET_MUTABLE prof_dump_header;\n#ifdef JEMALLOC_JET\nvoid prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,\n    uint64_t *accumbytes);\n#endif\nbool prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum);\nvoid prof_idump(tsdn_t *tsdn);\nbool prof_mdump(tsd_t *tsd, const char *filename);\nvoid prof_gdump(tsdn_t *tsdn);\nprof_tdata_t *prof_tdata_init(tsd_t *tsd);\nprof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata);\nvoid prof_reset(tsd_t *tsd, size_t lg_sample);\nvoid prof_tdata_cleanup(tsd_t *tsd);\nbool prof_active_get(tsdn_t *tsdn);\nbool prof_active_set(tsdn_t *tsdn, bool active);\nconst char *prof_thread_name_get(tsd_t *tsd);\nint prof_thread_name_set(tsd_t *tsd, const char *thread_name);\nbool prof_thread_active_get(tsd_t *tsd);\nbool prof_thread_active_set(tsd_t *tsd, bool active);\nbool prof_thread_active_init_get(tsdn_t *tsdn);\nbool prof_thread_active_init_set(tsdn_t *tsdn, bool active_init);\nbool prof_gdump_get(tsdn_t *tsdn);\nbool prof_gdump_set(tsdn_t *tsdn, bool active);\nvoid prof_boot0(void);\nvoid prof_boot1(void);\nbool prof_boot2(tsd_t *tsd);\nvoid prof_prefork0(tsdn_t *tsdn);\nvoid prof_prefork1(tsdn_t *tsdn);\nvoid prof_postfork_parent(tsdn_t *tsdn);\nvoid prof_postfork_child(tsdn_t *tsdn);\nvoid prof_sample_threshold_update(prof_tdata_t *tdata);\n\n#endif /* JEMALLOC_INTERNAL_PROF_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_inlines_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_INLINES_A_H\n#define JEMALLOC_INTERNAL_PROF_INLINES_A_H\n\n#include \"jemalloc/internal/mutex.h\"\n\nstatic inline bool\nprof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum, uint64_t accumbytes) {\n\tcassert(config_prof);\n\n\tbool overflow;\n\tuint64_t a0, a1;\n\n\t/*\n\t * If the application allocates fast enough (and/or if idump is slow\n\t * enough), extreme overflow here (a1 >= prof_interval * 2) can cause\n\t * idump trigger coalescing.  This is an intentional mechanism that\n\t * avoids rate-limiting allocation.\n\t */\n#ifdef JEMALLOC_ATOMIC_U64\n\ta0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);\n\tdo {\n\t\ta1 = a0 + accumbytes;\n\t\tassert(a1 >= a0);\n\t\toverflow = (a1 >= prof_interval);\n\t\tif (overflow) {\n\t\t\ta1 %= prof_interval;\n\t\t}\n\t} while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,\n\t    a1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n#else\n\tmalloc_mutex_lock(tsdn, &prof_accum->mtx);\n\ta0 = prof_accum->accumbytes;\n\ta1 = a0 + accumbytes;\n\toverflow = (a1 >= prof_interval);\n\tif (overflow) {\n\t\ta1 %= prof_interval;\n\t}\n\tprof_accum->accumbytes = a1;\n\tmalloc_mutex_unlock(tsdn, &prof_accum->mtx);\n#endif\n\treturn overflow;\n}\n\nstatic inline void\nprof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum, size_t usize) {\n\tcassert(config_prof);\n\n\t/*\n\t * Cancel out as much of the excessive prof_accumbytes increase as\n\t * possible without underflowing.  Interval-triggered dumps occur\n\t * slightly more often than intended as a result of incomplete\n\t * canceling.\n\t */\n\tuint64_t a0, a1;\n#ifdef JEMALLOC_ATOMIC_U64\n\ta0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);\n\tdo {\n\t\ta1 = (a0 >= LARGE_MINCLASS - usize) ?  a0 - (LARGE_MINCLASS -\n\t\t    usize) : 0;\n\t} while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,\n\t    a1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n#else\n\tmalloc_mutex_lock(tsdn, &prof_accum->mtx);\n\ta0 = prof_accum->accumbytes;\n\ta1 = (a0 >= LARGE_MINCLASS - usize) ?  a0 - (LARGE_MINCLASS - usize) :\n\t    0;\n\tprof_accum->accumbytes = a1;\n\tmalloc_mutex_unlock(tsdn, &prof_accum->mtx);\n#endif\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nprof_active_get_unlocked(void) {\n\t/*\n\t * Even if opt_prof is true, sampling can be temporarily disabled by\n\t * setting prof_active to false.  No locking is used when reading\n\t * prof_active in the fast path, so there are no guarantees regarding\n\t * how long it will take for all threads to notice state changes.\n\t */\n\treturn prof_active;\n}\n\n#endif /* JEMALLOC_INTERNAL_PROF_INLINES_A_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_inlines_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H\n#define JEMALLOC_INTERNAL_PROF_INLINES_B_H\n\n#include \"jemalloc/internal/sz.h\"\n\nJEMALLOC_ALWAYS_INLINE bool\nprof_gdump_get_unlocked(void) {\n\t/*\n\t * No locking is used when reading prof_gdump_val in the fast path, so\n\t * there are no guarantees regarding how long it will take for all\n\t * threads to notice state changes.\n\t */\n\treturn prof_gdump_val;\n}\n\nJEMALLOC_ALWAYS_INLINE prof_tdata_t *\nprof_tdata_get(tsd_t *tsd, bool create) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\ttdata = tsd_prof_tdata_get(tsd);\n\tif (create) {\n\t\tif (unlikely(tdata == NULL)) {\n\t\t\tif (tsd_nominal(tsd)) {\n\t\t\t\ttdata = prof_tdata_init(tsd);\n\t\t\t\ttsd_prof_tdata_set(tsd, tdata);\n\t\t\t}\n\t\t} else if (unlikely(tdata->expired)) {\n\t\t\ttdata = prof_tdata_reinit(tsd, tdata);\n\t\t\ttsd_prof_tdata_set(tsd, tdata);\n\t\t}\n\t\tassert(tdata == NULL || tdata->attached);\n\t}\n\n\treturn tdata;\n}\n\nJEMALLOC_ALWAYS_INLINE prof_tctx_t *\nprof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\treturn arena_prof_tctx_get(tsdn, ptr, alloc_ctx);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,\n    alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\tarena_prof_tctx_set(tsdn, ptr, usize, alloc_ctx, tctx);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\tarena_prof_tctx_reset(tsdn, ptr, tctx);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nprof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,\n    prof_tdata_t **tdata_out) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (unlikely((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)) {\n\t\ttdata = NULL;\n\t}\n\n\tif (tdata_out != NULL) {\n\t\t*tdata_out = tdata;\n\t}\n\n\tif (unlikely(tdata == NULL)) {\n\t\treturn true;\n\t}\n\n\tif (likely(tdata->bytes_until_sample >= usize)) {\n\t\tif (update) {\n\t\t\ttdata->bytes_until_sample -= usize;\n\t\t}\n\t\treturn true;\n\t} else {\n\t\tif (tsd_reentrancy_level_get(tsd) > 0) {\n\t\t\treturn true;\n\t\t}\n\t\t/* Compute new sample threshold. */\n\t\tif (update) {\n\t\t\tprof_sample_threshold_update(tdata);\n\t\t}\n\t\treturn !tdata->active;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE prof_tctx_t *\nprof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active, bool update) {\n\tprof_tctx_t *ret;\n\tprof_tdata_t *tdata;\n\tprof_bt_t bt;\n\n\tassert(usize == sz_s2u(usize));\n\n\tif (!prof_active || likely(prof_sample_accum_update(tsd, usize, update,\n\t    &tdata))) {\n\t\tret = (prof_tctx_t *)(uintptr_t)1U;\n\t} else {\n\t\tbt_init(&bt, tdata->vec);\n\t\tprof_backtrace(&bt);\n\t\tret = prof_lookup(tsd, &bt);\n\t}\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx,\n    prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(usize == isalloc(tsdn, ptr));\n\n\tif (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {\n\t\tprof_malloc_sample_object(tsdn, ptr, usize, tctx);\n\t} else {\n\t\tprof_tctx_set(tsdn, ptr, usize, alloc_ctx,\n\t\t    (prof_tctx_t *)(uintptr_t)1U);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,\n    bool prof_active, bool updated, const void *old_ptr, size_t old_usize,\n    prof_tctx_t *old_tctx) {\n\tbool sampled, old_sampled, moved;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);\n\n\tif (prof_active && !updated && ptr != NULL) {\n\t\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\t\tif (prof_sample_accum_update(tsd, usize, true, NULL)) {\n\t\t\t/*\n\t\t\t * Don't sample.  The usize passed to prof_alloc_prep()\n\t\t\t * was larger than what actually got allocated, so a\n\t\t\t * backtrace was captured for this allocation, even\n\t\t\t * though its actual usize was insufficient to cross the\n\t\t\t * sample threshold.\n\t\t\t */\n\t\t\tprof_alloc_rollback(tsd, tctx, true);\n\t\t\ttctx = (prof_tctx_t *)(uintptr_t)1U;\n\t\t}\n\t}\n\n\tsampled = ((uintptr_t)tctx > (uintptr_t)1U);\n\told_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U);\n\tmoved = (ptr != old_ptr);\n\n\tif (unlikely(sampled)) {\n\t\tprof_malloc_sample_object(tsd_tsdn(tsd), ptr, usize, tctx);\n\t} else if (moved) {\n\t\tprof_tctx_set(tsd_tsdn(tsd), ptr, usize, NULL,\n\t\t    (prof_tctx_t *)(uintptr_t)1U);\n\t} else if (unlikely(old_sampled)) {\n\t\t/*\n\t\t * prof_tctx_set() would work for the !moved case as well, but\n\t\t * prof_tctx_reset() is slightly cheaper, and the proper thing\n\t\t * to do here in the presence of explicit knowledge re: moved\n\t\t * state.\n\t\t */\n\t\tprof_tctx_reset(tsd_tsdn(tsd), ptr, tctx);\n\t} else {\n\t\tassert((uintptr_t)prof_tctx_get(tsd_tsdn(tsd), ptr, NULL) ==\n\t\t    (uintptr_t)1U);\n\t}\n\n\t/*\n\t * The prof_free_sampled_object() call must come after the\n\t * prof_malloc_sample_object() call, because tctx and old_tctx may be\n\t * the same, in which case reversing the call order could cause the tctx\n\t * to be prematurely destroyed as a side effect of momentarily zeroed\n\t * counters.\n\t */\n\tif (unlikely(old_sampled)) {\n\t\tprof_free_sampled_object(tsd, old_usize, old_tctx);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_free(tsd_t *tsd, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx) {\n\tprof_tctx_t *tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);\n\n\tcassert(config_prof);\n\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\n\tif (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {\n\t\tprof_free_sampled_object(tsd, usize, tctx);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_PROF_INLINES_B_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_STRUCTS_H\n#define JEMALLOC_INTERNAL_PROF_STRUCTS_H\n\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/rb.h\"\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\tmax;\n} prof_unwind_data_t;\n#endif\n\nstruct prof_accum_s {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_t\tmtx;\n\tuint64_t\taccumbytes;\n#else\n\tatomic_u64_t\taccumbytes;\n#endif\n};\n\nstruct prof_cnt_s {\n\t/* Profiling counters. */\n\tuint64_t\tcurobjs;\n\tuint64_t\tcurbytes;\n\tuint64_t\taccumobjs;\n\tuint64_t\taccumbytes;\n};\n\ntypedef enum {\n\tprof_tctx_state_initializing,\n\tprof_tctx_state_nominal,\n\tprof_tctx_state_dumping,\n\tprof_tctx_state_purgatory /* Dumper must finish destroying. */\n} prof_tctx_state_t;\n\nstruct prof_tctx_s {\n\t/* Thread data for thread that performed the allocation. */\n\tprof_tdata_t\t\t*tdata;\n\n\t/*\n\t * Copy of tdata->thr_{uid,discrim}, necessary because tdata may be\n\t * defunct during teardown.\n\t */\n\tuint64_t\t\tthr_uid;\n\tuint64_t\t\tthr_discrim;\n\n\t/* Profiling counters, protected by tdata->lock. */\n\tprof_cnt_t\t\tcnts;\n\n\t/* Associated global context. */\n\tprof_gctx_t\t\t*gctx;\n\n\t/*\n\t * UID that distinguishes multiple tctx's created by the same thread,\n\t * but coexisting in gctx->tctxs.  There are two ways that such\n\t * coexistence can occur:\n\t * - A dumper thread can cause a tctx to be retained in the purgatory\n\t *   state.\n\t * - Although a single \"producer\" thread must create all tctx's which\n\t *   share the same thr_uid, multiple \"consumers\" can each concurrently\n\t *   execute portions of prof_tctx_destroy().  prof_tctx_destroy() only\n\t *   gets called once each time cnts.cur{objs,bytes} drop to 0, but this\n\t *   threshold can be hit again before the first consumer finishes\n\t *   executing prof_tctx_destroy().\n\t */\n\tuint64_t\t\ttctx_uid;\n\n\t/* Linkage into gctx's tctxs. */\n\trb_node(prof_tctx_t)\ttctx_link;\n\n\t/*\n\t * True during prof_alloc_prep()..prof_malloc_sample_object(), prevents\n\t * sample vs destroy race.\n\t */\n\tbool\t\t\tprepared;\n\n\t/* Current dump-related state, protected by gctx->lock. */\n\tprof_tctx_state_t\tstate;\n\n\t/*\n\t * Copy of cnts snapshotted during early dump phase, protected by\n\t * dump_mtx.\n\t */\n\tprof_cnt_t\t\tdump_cnts;\n};\ntypedef rb_tree(prof_tctx_t) prof_tctx_tree_t;\n\nstruct prof_gctx_s {\n\t/* Protects nlimbo, cnt_summed, and tctxs. */\n\tmalloc_mutex_t\t\t*lock;\n\n\t/*\n\t * Number of threads that currently cause this gctx to be in a state of\n\t * limbo due to one of:\n\t *   - Initializing this gctx.\n\t *   - Initializing per thread counters associated with this gctx.\n\t *   - Preparing to destroy this gctx.\n\t *   - Dumping a heap profile that includes this gctx.\n\t * nlimbo must be 1 (single destroyer) in order to safely destroy the\n\t * gctx.\n\t */\n\tunsigned\t\tnlimbo;\n\n\t/*\n\t * Tree of profile counters, one for each thread that has allocated in\n\t * this context.\n\t */\n\tprof_tctx_tree_t\ttctxs;\n\n\t/* Linkage for tree of contexts to be dumped. */\n\trb_node(prof_gctx_t)\tdump_link;\n\n\t/* Temporary storage for summation during dump. */\n\tprof_cnt_t\t\tcnt_summed;\n\n\t/* Associated backtrace. */\n\tprof_bt_t\t\tbt;\n\n\t/* Backtrace vector, variable size, referred to by bt. */\n\tvoid\t\t\t*vec[1];\n};\ntypedef rb_tree(prof_gctx_t) prof_gctx_tree_t;\n\nstruct prof_tdata_s {\n\tmalloc_mutex_t\t\t*lock;\n\n\t/* Monotonically increasing unique thread identifier. */\n\tuint64_t\t\tthr_uid;\n\n\t/*\n\t * Monotonically increasing discriminator among tdata structures\n\t * associated with the same thr_uid.\n\t */\n\tuint64_t\t\tthr_discrim;\n\n\t/* Included in heap profile dumps if non-NULL. */\n\tchar\t\t\t*thread_name;\n\n\tbool\t\t\tattached;\n\tbool\t\t\texpired;\n\n\trb_node(prof_tdata_t)\ttdata_link;\n\n\t/*\n\t * Counter used to initialize prof_tctx_t's tctx_uid.  No locking is\n\t * necessary when incrementing this field, because only one thread ever\n\t * does so.\n\t */\n\tuint64_t\t\ttctx_uid_next;\n\n\t/*\n\t * Hash of (prof_bt_t *)-->(prof_tctx_t *).  Each thread tracks\n\t * backtraces for which it has non-zero allocation/deallocation counters\n\t * associated with thread-specific prof_tctx_t objects.  Other threads\n\t * may write to prof_tctx_t contents when freeing associated objects.\n\t */\n\tckh_t\t\t\tbt2tctx;\n\n\t/* Sampling state. */\n\tuint64_t\t\tprng_state;\n\tuint64_t\t\tbytes_until_sample;\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\t/*\n\t * Set to true during an early dump phase for tdata's which are\n\t * currently being dumped.  New threads' tdata's have this initialized\n\t * to false so that they aren't accidentally included in later dump\n\t * phases.\n\t */\n\tbool\t\t\tdumping;\n\n\t/*\n\t * True if profiling is active for this tdata's thread\n\t * (thread.prof.active mallctl).\n\t */\n\tbool\t\t\tactive;\n\n\t/* Temporary storage for summation during dump. */\n\tprof_cnt_t\t\tcnt_summed;\n\n\t/* Backtrace vector, used for calls to prof_backtrace(). */\n\tvoid\t\t\t*vec[PROF_BT_MAX];\n};\ntypedef rb_tree(prof_tdata_t) prof_tdata_tree_t;\n\n#endif /* JEMALLOC_INTERNAL_PROF_STRUCTS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_TYPES_H\n#define JEMALLOC_INTERNAL_PROF_TYPES_H\n\ntypedef struct prof_bt_s prof_bt_t;\ntypedef struct prof_accum_s prof_accum_t;\ntypedef struct prof_cnt_s prof_cnt_t;\ntypedef struct prof_tctx_s prof_tctx_t;\ntypedef struct prof_gctx_s prof_gctx_t;\ntypedef struct prof_tdata_s prof_tdata_t;\n\n/* Option defaults. */\n#ifdef JEMALLOC_PROF\n#  define PROF_PREFIX_DEFAULT\t\t\"jeprof\"\n#else\n#  define PROF_PREFIX_DEFAULT\t\t\"\"\n#endif\n#define LG_PROF_SAMPLE_DEFAULT\t\t19\n#define LG_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 PROF_BT_MAX\t\t\t128\n\n/* Initial hash table size. */\n#define PROF_CKH_MINITEMS\t\t64\n\n/* Size of memory buffer to use when writing dump files. */\n#define PROF_DUMP_BUFSIZE\t\t65536\n\n/* Size of stack-allocated buffer used by prof_printf(). */\n#define PROF_PRINTF_BUFSIZE\t\t128\n\n/*\n * Number of mutexes shared among all gctx's.  No space is allocated for these\n * unless profiling is enabled, so it's okay to over-provision.\n */\n#define PROF_NCTX_LOCKS\t\t\t1024\n\n/*\n * Number of mutexes shared among all tdata's.  No space is allocated for these\n * unless profiling is enabled, so it's okay to over-provision.\n */\n#define PROF_NTDATA_LOCKS\t\t256\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 PROF_TDATA_STATE_REINCARNATED\t((prof_tdata_t *)(uintptr_t)1)\n#define PROF_TDATA_STATE_PURGATORY\t((prof_tdata_t *)(uintptr_t)2)\n#define PROF_TDATA_STATE_MAX\t\tPROF_TDATA_STATE_PURGATORY\n\n#endif /* JEMALLOC_INTERNAL_PROF_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/public_namespace.sh",
    "content": "#!/bin/sh\n\nfor nm in `cat $1` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#define je_${n} JEMALLOC_N(${n})\"\ndone\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/public_unnamespace.sh",
    "content": "#!/bin/sh\n\nfor nm in `cat $1` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#undef je_${n}\"\ndone\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ql.h",
    "content": "#ifndef JEMALLOC_INTERNAL_QL_H\n#define JEMALLOC_INTERNAL_QL_H\n\n#include \"jemalloc/internal/qr.h\"\n\n/* List definitions. */\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\n#endif /* JEMALLOC_INTERNAL_QL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/qr.h",
    "content": "#ifndef JEMALLOC_INTERNAL_QR_H\n#define JEMALLOC_INTERNAL_QR_H\n\n/* 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) do {\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_type, a_field) do {\t\t\t\\\n\ta_type *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/*\n * qr_meld() and qr_split() are functionally equivalent, so there's no need to\n * have two copies of the code.\n */\n#define qr_split(a_qr_a, a_qr_b, a_type, a_field)\t\t\t\\\n\tqr_meld((a_qr_a), (a_qr_b), a_type, 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\n#endif /* JEMALLOC_INTERNAL_QR_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 RB_H_\n\n#ifndef __PGI\n#define RB_COMPACT\n#endif\n\n#ifdef RB_COMPACT\n/* Node structure. */\n#define rb_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 rb_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 rb_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}\n\n/* Left accessors. */\n#define rbtn_left_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_left)\n#define rbtn_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 rbtn_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 rbtn_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 rbtn_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 rbtn_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 rbtn_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 rbtn_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\n/* Node initializer. */\n#define rbt_node_new(a_type, a_field, a_rbt, a_node) do {\t\t\\\n    /* Bookkeeping bit cannot be used by node pointer. */\t\t\\\n    assert(((uintptr_t)(a_node) & 0x1) == 0);\t\t\t\t\\\n    rbtn_left_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_right_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_red_set(a_type, a_field, (a_node));\t\t\t\t\\\n} while (0)\n#else\n/* Right accessors. */\n#define rbtn_right_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_right)\n#define rbtn_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 rbtn_red_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_red)\n#define rbtn_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 rbtn_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 rbtn_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\n/* Node initializer. */\n#define rbt_node_new(a_type, a_field, a_rbt, a_node) do {\t\t\\\n    rbtn_left_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_right_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_red_set(a_type, a_field, (a_node));\t\t\t\t\\\n} while (0)\n#endif\n\n/* Tree initializer. */\n#define rb_new(a_type, a_field, a_rbt) do {\t\t\t\t\\\n    (a_rbt)->rbt_root = NULL;\t\t\t\t\t\t\\\n} while (0)\n\n/* Internal utility macros. */\n#define rbtn_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) != NULL) {\t\t\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)) != NULL;\t\t\\\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 rbtn_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) != NULL) {\t\t\t\t\t\t\\\n\tfor (; rbtn_right_get(a_type, a_field, (r_node)) != NULL;\t\\\n\t  (r_node) = rbtn_right_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 rbtn_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 rbtn_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 rb_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 bool\t\t\t\t\t\t\t\t\\\na_prefix##empty(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, const a_type *key);\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##nsearch(a_rbt_type *rbtree, const a_type *key);\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##psearch(a_rbt_type *rbtree, const a_type *key);\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);\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *),\t\\\n  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 comparison 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 bool\n *   ex_empty(ex_t *tree);\n *       Description: Determine whether tree is empty.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *       Ret: True if tree is empty, false otherwise.\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, const 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, const ex_node_t *key);\n *   static ex_node_t *\n *   ex_psearch(ex_t *tree, const 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 *   static void\n *   ex_destroy(ex_t *tree, void (*cb)(ex_node_t *, void *), void *arg);\n *       Description: Iterate over the tree with post-order traversal, remove\n *                    each node, and run the callback if non-null.  This is\n *                    used for destroying a tree without paying the cost to\n *                    rebalance it.  The tree must not be otherwise altered\n *                    during traversal.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         cb  : Callback function, which, if non-null, is called for each node\n *               during iteration.  There is no way to stop iteration once it\n *               has begun.\n *         arg : Opaque pointer passed to cb().\n */\n#define rb_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 bool\t\t\t\t\t\t\t\t\\\na_prefix##empty(a_rbt_type *rbtree) {\t\t\t\t\t\\\n    return (rbtree->rbt_root == NULL);\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    return ret;\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##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    return ret;\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##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) != NULL) {\t\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 != NULL);\t\t\t\t\t\t\\\n\tret = NULL;\t\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 != 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    return ret;\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##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) != NULL) {\t\t\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 != NULL);\t\t\t\t\t\t\\\n\tret = NULL;\t\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 != 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    return ret;\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##search(a_rbt_type *rbtree, const a_type *key) {\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 != NULL\t\t\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    return ret;\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##nsearch(a_rbt_type *rbtree, const a_type *key) {\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 = NULL;\t\t\t\t\t\t\t\t\\\n    while (tnode != NULL) {\t\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    return ret;\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##psearch(a_rbt_type *rbtree, const a_type *key) {\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 = NULL;\t\t\t\t\t\t\t\t\\\n    while (tnode != NULL) {\t\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    return ret;\t\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 != NULL; pathp++) {\t\t\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 (leftleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  leftleft)) {\t\t\t\t\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 (left != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  left)) {\t\t\t\t\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 != NULL; pathp++) {\t\t\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 != NULL; pathp++) {\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 != NULL) {\t\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));\t\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 = NULL;\t\t\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, NULL);\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 = NULL;\t\t\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    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 (rightleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  rightleft)) {\t\t\t\t\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 (rightleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  rightleft)) {\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    /*          (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    /* 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    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 (leftrightleft != NULL && rbtn_red_get(a_type,\t\\\n\t\t  a_field, leftrightleft)) {\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    /* (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 != NULL);\t\t\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 (leftleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  leftleft)) {\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    /* (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 (leftleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  leftleft)) {\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    /*        (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));\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_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 == NULL) {\t\t\t\t\t\t\t\\\n\treturn NULL;\t\t\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)) != NULL || (ret = cb(rbtree, node,\t\\\n\t  arg)) != NULL) {\t\t\t\t\t\t\\\n\t    return ret;\t\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)) != NULL ||\t\\\n\t  (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return ret;\t\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\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    return ret;\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_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 == NULL) {\t\t\t\t\t\t\t\\\n\treturn NULL;\t\t\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)) != NULL ||\t\\\n\t  (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return ret;\t\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)) != NULL ||\t\\\n\t  (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return ret;\t\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\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    return ret;\t\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##destroy_recurse(a_rbt_type *rbtree, a_type *node, void (*cb)(\t\\\n  a_type *, void *), void *arg) {\t\t\t\t\t\\\n    if (node == NULL) {\t\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    a_prefix##destroy_recurse(rbtree, rbtn_left_get(a_type, a_field,\t\\\n      node), cb, arg);\t\t\t\t\t\t\t\\\n    rbtn_left_set(a_type, a_field, (node), NULL);\t\t\t\\\n    a_prefix##destroy_recurse(rbtree, rbtn_right_get(a_type, a_field,\t\\\n      node), cb, arg);\t\t\t\t\t\t\t\\\n    rbtn_right_set(a_type, a_field, (node), NULL);\t\t\t\\\n    if (cb) {\t\t\t\t\t\t\t\t\\\n\tcb(node, arg);\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\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *),\t\\\n  void *arg) {\t\t\t\t\t\t\t\t\\\n    a_prefix##destroy_recurse(rbtree, rbtree->rbt_root, cb, arg);\t\\\n    rbtree->rbt_root = NULL;\t\t\t\t\t\t\\\n}\n\n#endif /* RB_H_ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/rtree.h",
    "content": "#ifndef JEMALLOC_INTERNAL_RTREE_H\n#define JEMALLOC_INTERNAL_RTREE_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree_tsd.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/tsd.h\"\n\n/*\n * This radix tree implementation is tailored to the singular purpose of\n * associating metadata with extents that are currently owned by jemalloc.\n *\n *******************************************************************************\n */\n\n/* Number of high insignificant bits. */\n#define RTREE_NHIB ((1U << (LG_SIZEOF_PTR+3)) - LG_VADDR)\n/* Number of low insigificant bits. */\n#define RTREE_NLIB LG_PAGE\n/* Number of significant bits. */\n#define RTREE_NSB (LG_VADDR - RTREE_NLIB)\n/* Number of levels in radix tree. */\n#if RTREE_NSB <= 10\n#  define RTREE_HEIGHT 1\n#elif RTREE_NSB <= 36\n#  define RTREE_HEIGHT 2\n#elif RTREE_NSB <= 52\n#  define RTREE_HEIGHT 3\n#else\n#  error Unsupported number of significant virtual address bits\n#endif\n/* Use compact leaf representation if virtual address encoding allows. */\n#if RTREE_NHIB >= LG_CEIL_NSIZES\n#  define RTREE_LEAF_COMPACT\n#endif\n\n/* Needed for initialization only. */\n#define RTREE_LEAFKEY_INVALID ((uintptr_t)1)\n\ntypedef struct rtree_node_elm_s rtree_node_elm_t;\nstruct rtree_node_elm_s {\n\tatomic_p_t\tchild; /* (rtree_{node,leaf}_elm_t *) */\n};\n\nstruct rtree_leaf_elm_s {\n#ifdef RTREE_LEAF_COMPACT\n\t/*\n\t * Single pointer-width field containing all three leaf element fields.\n\t * For example, on a 64-bit x64 system with 48 significant virtual\n\t * memory address bits, the index, extent, and slab fields are packed as\n\t * such:\n\t *\n\t * x: index\n\t * e: extent\n\t * b: slab\n\t *\n\t *   00000000 xxxxxxxx eeeeeeee [...] eeeeeeee eeee000b\n\t */\n\tatomic_p_t\tle_bits;\n#else\n\tatomic_p_t\tle_extent; /* (extent_t *) */\n\tatomic_u_t\tle_szind; /* (szind_t) */\n\tatomic_b_t\tle_slab; /* (bool) */\n#endif\n};\n\ntypedef struct rtree_level_s rtree_level_t;\nstruct rtree_level_s {\n\t/* Number of key bits distinguished by this level. */\n\tunsigned\t\tbits;\n\t/*\n\t * Cumulative number of key bits distinguished by traversing to\n\t * corresponding tree level.\n\t */\n\tunsigned\t\tcumbits;\n};\n\ntypedef struct rtree_s rtree_t;\nstruct rtree_s {\n\tmalloc_mutex_t\t\tinit_lock;\n\t/* Number of elements based on rtree_levels[0].bits. */\n#if RTREE_HEIGHT > 1\n\trtree_node_elm_t\troot[1U << (RTREE_NSB/RTREE_HEIGHT)];\n#else\n\trtree_leaf_elm_t\troot[1U << (RTREE_NSB/RTREE_HEIGHT)];\n#endif\n};\n\n/*\n * Split the bits into one to three partitions depending on number of\n * significant bits.  It the number of bits does not divide evenly into the\n * number of levels, place one remainder bit per level starting at the leaf\n * level.\n */\nstatic const rtree_level_t rtree_levels[] = {\n#if RTREE_HEIGHT == 1\n\t{RTREE_NSB, RTREE_NHIB + RTREE_NSB}\n#elif RTREE_HEIGHT == 2\n\t{RTREE_NSB/2, RTREE_NHIB + RTREE_NSB/2},\n\t{RTREE_NSB/2 + RTREE_NSB%2, RTREE_NHIB + RTREE_NSB}\n#elif RTREE_HEIGHT == 3\n\t{RTREE_NSB/3, RTREE_NHIB + RTREE_NSB/3},\n\t{RTREE_NSB/3 + RTREE_NSB%3/2,\n\t    RTREE_NHIB + RTREE_NSB/3*2 + RTREE_NSB%3/2},\n\t{RTREE_NSB/3 + RTREE_NSB%3 - RTREE_NSB%3/2, RTREE_NHIB + RTREE_NSB}\n#else\n#  error Unsupported rtree height\n#endif\n};\n\nbool rtree_new(rtree_t *rtree, bool zeroed);\n\ntypedef rtree_node_elm_t *(rtree_node_alloc_t)(tsdn_t *, rtree_t *, size_t);\nextern rtree_node_alloc_t *JET_MUTABLE rtree_node_alloc;\n\ntypedef rtree_leaf_elm_t *(rtree_leaf_alloc_t)(tsdn_t *, rtree_t *, size_t);\nextern rtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc;\n\ntypedef void (rtree_node_dalloc_t)(tsdn_t *, rtree_t *, rtree_node_elm_t *);\nextern rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc;\n\ntypedef void (rtree_leaf_dalloc_t)(tsdn_t *, rtree_t *, rtree_leaf_elm_t *);\nextern rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc;\n#ifdef JEMALLOC_JET\nvoid rtree_delete(tsdn_t *tsdn, rtree_t *rtree);\n#endif\nrtree_leaf_elm_t *rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_ctx_t *rtree_ctx, uintptr_t key, bool dependent, bool init_missing);\n\nJEMALLOC_ALWAYS_INLINE uintptr_t\nrtree_leafkey(uintptr_t key) {\n\tunsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);\n\tunsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -\n\t    rtree_levels[RTREE_HEIGHT-1].bits);\n\tunsigned maskbits = ptrbits - cumbits;\n\tuintptr_t mask = ~((ZU(1) << maskbits) - 1);\n\treturn (key & mask);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nrtree_cache_direct_map(uintptr_t key) {\n\tunsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);\n\tunsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -\n\t    rtree_levels[RTREE_HEIGHT-1].bits);\n\tunsigned maskbits = ptrbits - cumbits;\n\treturn (size_t)((key >> maskbits) & (RTREE_CTX_NCACHE - 1));\n}\n\nJEMALLOC_ALWAYS_INLINE uintptr_t\nrtree_subkey(uintptr_t key, unsigned level) {\n\tunsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);\n\tunsigned cumbits = rtree_levels[level].cumbits;\n\tunsigned shiftbits = ptrbits - cumbits;\n\tunsigned maskbits = rtree_levels[level].bits;\n\tuintptr_t mask = (ZU(1) << maskbits) - 1;\n\treturn ((key >> shiftbits) & mask);\n}\n\n/*\n * Atomic getters.\n *\n * dependent: Reading a value on behalf of a pointer to a valid allocation\n *            is guaranteed to be a clean read even without synchronization,\n *            because the rtree update became visible in memory before the\n *            pointer came into existence.\n * !dependent: An arbitrary read, e.g. on behalf of ivsalloc(), may not be\n *             dependent on a previous rtree write, which means a stale read\n *             could result if synchronization were omitted here.\n */\n#  ifdef RTREE_LEAF_COMPACT\nJEMALLOC_ALWAYS_INLINE uintptr_t\nrtree_leaf_elm_bits_read(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,\n    bool dependent) {\n\treturn (uintptr_t)atomic_load_p(&elm->le_bits, dependent\n\t    ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);\n}\n\nJEMALLOC_ALWAYS_INLINE extent_t *\nrtree_leaf_elm_bits_extent_get(uintptr_t bits) {\n#    ifdef __aarch64__\n\t/*\n\t * aarch64 doesn't sign extend the highest virtual address bit to set\n\t * the higher ones.  Instead, the high bits gets zeroed.\n\t */\n\tuintptr_t high_bit_mask = ((uintptr_t)1 << LG_VADDR) - 1;\n\t/* Mask off the slab bit. */\n\tuintptr_t low_bit_mask = ~(uintptr_t)1;\n\tuintptr_t mask = high_bit_mask & low_bit_mask;\n\treturn (extent_t *)(bits & mask);\n#    else\n\t/* Restore sign-extended high bits, mask slab bit. */\n\treturn (extent_t *)((uintptr_t)((intptr_t)(bits << RTREE_NHIB) >>\n\t    RTREE_NHIB) & ~((uintptr_t)0x1));\n#    endif\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nrtree_leaf_elm_bits_szind_get(uintptr_t bits) {\n\treturn (szind_t)(bits >> LG_VADDR);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_leaf_elm_bits_slab_get(uintptr_t bits) {\n\treturn (bool)(bits & (uintptr_t)0x1);\n}\n\n#  endif\n\nJEMALLOC_ALWAYS_INLINE extent_t *\nrtree_leaf_elm_extent_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool dependent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\treturn rtree_leaf_elm_bits_extent_get(bits);\n#else\n\textent_t *extent = (extent_t *)atomic_load_p(&elm->le_extent, dependent\n\t    ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);\n\treturn extent;\n#endif\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nrtree_leaf_elm_szind_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool dependent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\treturn rtree_leaf_elm_bits_szind_get(bits);\n#else\n\treturn (szind_t)atomic_load_u(&elm->le_szind, dependent ? ATOMIC_RELAXED\n\t    : ATOMIC_ACQUIRE);\n#endif\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_leaf_elm_slab_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool dependent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\treturn rtree_leaf_elm_bits_slab_get(bits);\n#else\n\treturn atomic_load_b(&elm->le_slab, dependent ? ATOMIC_RELAXED :\n\t    ATOMIC_ACQUIRE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_extent_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, extent_t *extent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, true);\n\tuintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<\n\t    LG_VADDR) | ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1))\n\t    | ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\tatomic_store_p(&elm->le_extent, extent, ATOMIC_RELEASE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_szind_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, szind_t szind) {\n\tassert(szind <= NSIZES);\n\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,\n\t    true);\n\tuintptr_t bits = ((uintptr_t)szind << LG_VADDR) |\n\t    ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &\n\t    (((uintptr_t)0x1 << LG_VADDR) - 1)) |\n\t    ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\tatomic_store_u(&elm->le_szind, szind, ATOMIC_RELEASE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_slab_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool slab) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,\n\t    true);\n\tuintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<\n\t    LG_VADDR) | ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &\n\t    (((uintptr_t)0x1 << LG_VADDR) - 1)) | ((uintptr_t)slab);\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\tatomic_store_b(&elm->le_slab, slab, ATOMIC_RELEASE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,\n    extent_t *extent, szind_t szind, bool slab) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = ((uintptr_t)szind << LG_VADDR) |\n\t    ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1)) |\n\t    ((uintptr_t)slab);\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\trtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);\n\trtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);\n\t/*\n\t * Write extent last, since the element is atomically considered valid\n\t * as soon as the extent field is non-NULL.\n\t */\n\trtree_leaf_elm_extent_write(tsdn, rtree, elm, extent);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_leaf_elm_t *elm, szind_t szind, bool slab) {\n\tassert(!slab || szind < NBINS);\n\n\t/*\n\t * The caller implicitly assures that it is the only writer to the szind\n\t * and slab fields, and that the extent field cannot currently change.\n\t */\n\trtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);\n\trtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *\nrtree_leaf_elm_lookup(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, bool init_missing) {\n\tassert(key != 0);\n\tassert(!dependent || !init_missing);\n\n\tsize_t slot = rtree_cache_direct_map(key);\n\tuintptr_t leafkey = rtree_leafkey(key);\n\tassert(leafkey != RTREE_LEAFKEY_INVALID);\n\n\t/* Fast path: L1 direct mapped cache. */\n\tif (likely(rtree_ctx->cache[slot].leafkey == leafkey)) {\n\t\trtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;\n\t\tassert(leaf != NULL);\n\t\tuintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);\n\t\treturn &leaf[subkey];\n\t}\n\t/*\n\t * Search the L2 LRU cache.  On hit, swap the matching element into the\n\t * slot in L1 cache, and move the position in L2 up by 1.\n\t */\n#define RTREE_CACHE_CHECK_L2(i) do {\t\t\t\t\t\\\n\tif (likely(rtree_ctx->l2_cache[i].leafkey == leafkey)) {\t\\\n\t\trtree_leaf_elm_t *leaf = rtree_ctx->l2_cache[i].leaf;\t\\\n\t\tassert(leaf != NULL);\t\t\t\t\t\\\n\t\tif (i > 0) {\t\t\t\t\t\t\\\n\t\t\t/* Bubble up by one. */\t\t\t\t\\\n\t\t\trtree_ctx->l2_cache[i].leafkey =\t\t\\\n\t\t\t\trtree_ctx->l2_cache[i - 1].leafkey;\t\\\n\t\t\trtree_ctx->l2_cache[i].leaf =\t\t\t\\\n\t\t\t\trtree_ctx->l2_cache[i - 1].leaf;\t\\\n\t\t\trtree_ctx->l2_cache[i - 1].leafkey =\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leafkey;\t\t\\\n\t\t\trtree_ctx->l2_cache[i - 1].leaf =\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leaf;\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\trtree_ctx->l2_cache[0].leafkey =\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leafkey;\t\t\\\n\t\t\trtree_ctx->l2_cache[0].leaf =\t\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leaf;\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\trtree_ctx->cache[slot].leafkey = leafkey;\t\t\\\n\t\trtree_ctx->cache[slot].leaf = leaf;\t\t\t\\\n\t\tuintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);\t\\\n\t\treturn &leaf[subkey];\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\t/* Check the first cache entry. */\n\tRTREE_CACHE_CHECK_L2(0);\n\t/* Search the remaining cache elements. */\n\tfor (unsigned i = 1; i < RTREE_CTX_NCACHE_L2; i++) {\n\t\tRTREE_CACHE_CHECK_L2(i);\n\t}\n#undef RTREE_CACHE_CHECK_L2\n\n\treturn rtree_leaf_elm_lookup_hard(tsdn, rtree, rtree_ctx, key,\n\t    dependent, init_missing);\n}\n\nstatic inline bool\nrtree_write(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,\n    extent_t *extent, szind_t szind, bool slab) {\n\t/* Use rtree_clear() to set the extent to NULL. */\n\tassert(extent != NULL);\n\n\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,\n\t    key, false, true);\n\tif (elm == NULL) {\n\t\treturn true;\n\t}\n\n\tassert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) == NULL);\n\trtree_leaf_elm_write(tsdn, rtree, elm, extent, szind, slab);\n\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *\nrtree_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,\n    bool dependent) {\n\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,\n\t    key, dependent, false);\n\tif (!dependent && elm == NULL) {\n\t\treturn NULL;\n\t}\n\tassert(elm != NULL);\n\treturn elm;\n}\n\nJEMALLOC_ALWAYS_INLINE extent_t *\nrtree_extent_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn NULL;\n\t}\n\treturn rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nrtree_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn NSIZES;\n\t}\n\treturn rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);\n}\n\n/*\n * rtree_slab_read() is intentionally omitted because slab is always read in\n * conjunction with szind, which makes rtree_szind_slab_read() a better choice.\n */\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_extent_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, extent_t **r_extent, szind_t *r_szind) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn true;\n\t}\n\t*r_extent = rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);\n\t*r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_szind_slab_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, szind_t *r_szind, bool *r_slab) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn true;\n\t}\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\t*r_szind = rtree_leaf_elm_bits_szind_get(bits);\n\t*r_slab = rtree_leaf_elm_bits_slab_get(bits);\n#else\n\t*r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);\n\t*r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, dependent);\n#endif\n\treturn false;\n}\n\nstatic inline void\nrtree_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, szind_t szind, bool slab) {\n\tassert(!slab || szind < NBINS);\n\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);\n\trtree_leaf_elm_szind_slab_update(tsdn, rtree, elm, szind, slab);\n}\n\nstatic inline void\nrtree_clear(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);\n\tassert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) !=\n\t    NULL);\n\trtree_leaf_elm_write(tsdn, rtree, elm, NULL, NSIZES, false);\n}\n\n#endif /* JEMALLOC_INTERNAL_RTREE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/rtree_tsd.h",
    "content": "#ifndef JEMALLOC_INTERNAL_RTREE_CTX_H\n#define JEMALLOC_INTERNAL_RTREE_CTX_H\n\n/*\n * Number of leafkey/leaf pairs to cache in L1 and L2 level respectively.  Each\n * entry supports an entire leaf, so the cache hit rate is typically high even\n * with a small number of entries.  In rare cases extent activity will straddle\n * the boundary between two leaf nodes.  Furthermore, an arena may use a\n * combination of dss and mmap.  Note that as memory usage grows past the amount\n * that this cache can directly cover, the cache will become less effective if\n * locality of reference is low, but the consequence is merely cache misses\n * while traversing the tree nodes.\n *\n * The L1 direct mapped cache offers consistent and low cost on cache hit.\n * However collision could affect hit rate negatively.  This is resolved by\n * combining with a L2 LRU cache, which requires linear search and re-ordering\n * on access but suffers no collision.  Note that, the cache will itself suffer\n * cache misses if made overly large, plus the cost of linear search in the LRU\n * cache.\n */\n#define RTREE_CTX_LG_NCACHE 4\n#define RTREE_CTX_NCACHE (1 << RTREE_CTX_LG_NCACHE)\n#define RTREE_CTX_NCACHE_L2 8\n\n/*\n * Zero initializer required for tsd initialization only.  Proper initialization\n * done via rtree_ctx_data_init().\n */\n#define RTREE_CTX_ZERO_INITIALIZER {{{0}}, {{0}}}\n\n\ntypedef struct rtree_leaf_elm_s rtree_leaf_elm_t;\n\ntypedef struct rtree_ctx_cache_elm_s rtree_ctx_cache_elm_t;\nstruct rtree_ctx_cache_elm_s {\n\tuintptr_t\t\tleafkey;\n\trtree_leaf_elm_t\t*leaf;\n};\n\ntypedef struct rtree_ctx_s rtree_ctx_t;\nstruct rtree_ctx_s {\n\t/* Direct mapped cache. */\n\trtree_ctx_cache_elm_t\tcache[RTREE_CTX_NCACHE];\n\t/* L2 LRU cache. */\n\trtree_ctx_cache_elm_t\tl2_cache[RTREE_CTX_NCACHE_L2];\n};\n\nvoid rtree_ctx_data_init(rtree_ctx_t *ctx);\n\n#endif /* JEMALLOC_INTERNAL_RTREE_CTX_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/size_classes.sh",
    "content": "#!/bin/sh\n#\n# Usage: size_classes.sh <lg_qarr> <lg_tmin> <lg_parr> <lg_g>\n\n# The following limits are chosen such that they cover all supported platforms.\n\n# Pointer sizes.\nlg_zarr=\"2 3\"\n\n# Quanta.\nlg_qarr=$1\n\n# The range of tiny size classes is [2^lg_tmin..2^(lg_q-1)].\nlg_tmin=$2\n\n# Maximum lookup size.\nlg_kmax=12\n\n# Page sizes.\nlg_parr=`echo $3 | tr ',' ' '`\n\n# Size class group size (number of size classes for each size doubling).\nlg_g=$4\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\nlg() {\n  x=$1\n  lg_result=0\n  while [ ${x} -gt 1 ] ; do\n    lg_result=$((${lg_result} + 1))\n    x=$((${x} / 2))\n  done\n}\n\nlg_ceil() {\n  y=$1\n  lg ${y}; lg_floor=${lg_result}\n  pow2 ${lg_floor}; pow2_floor=${pow2_result}\n  if [ ${pow2_floor} -lt ${y} ] ; then\n    lg_ceil_result=$((${lg_floor} + 1))\n  else\n    lg_ceil_result=${lg_floor}\n  fi\n}\n\nreg_size_compute() {\n  lg_grp=$1\n  lg_delta=$2\n  ndelta=$3\n\n  pow2 ${lg_grp}; grp=${pow2_result}\n  pow2 ${lg_delta}; delta=${pow2_result}\n  reg_size=$((${grp} + ${delta}*${ndelta}))\n}\n\nslab_size() {\n  lg_p=$1\n  lg_grp=$2\n  lg_delta=$3\n  ndelta=$4\n\n  pow2 ${lg_p}; p=${pow2_result}\n  reg_size_compute ${lg_grp} ${lg_delta} ${ndelta}\n\n  # Compute smallest slab size that is an integer multiple of reg_size.\n  try_slab_size=${p}\n  try_nregs=$((${try_slab_size} / ${reg_size}))\n  perfect=0\n  while [ ${perfect} -eq 0 ] ; do\n    perfect_slab_size=${try_slab_size}\n    perfect_nregs=${try_nregs}\n\n    try_slab_size=$((${try_slab_size} + ${p}))\n    try_nregs=$((${try_slab_size} / ${reg_size}))\n    if [ ${perfect_slab_size} -eq $((${perfect_nregs} * ${reg_size})) ] ; then\n      perfect=1\n    fi\n  done\n\n  slab_size_pgs=$((${perfect_slab_size} / ${p}))\n}\n\nsize_class() {\n  index=$1\n  lg_grp=$2\n  lg_delta=$3\n  ndelta=$4\n  lg_p=$5\n  lg_kmax=$6\n\n  if [ ${lg_delta} -ge ${lg_p} ] ; then\n    psz=\"yes\"\n  else\n    pow2 ${lg_p}; p=${pow2_result}\n    pow2 ${lg_grp}; grp=${pow2_result}\n    pow2 ${lg_delta}; delta=${pow2_result}\n    sz=$((${grp} + ${delta} * ${ndelta}))\n    npgs=$((${sz} / ${p}))\n    if [ ${sz} -eq $((${npgs} * ${p})) ] ; then\n      psz=\"yes\"\n    else\n      psz=\"no\"\n    fi\n  fi\n\n  lg ${ndelta}; lg_ndelta=${lg_result}; pow2 ${lg_ndelta}\n  if [ ${pow2_result} -lt ${ndelta} ] ; then\n    rem=\"yes\"\n  else\n    rem=\"no\"\n  fi\n\n  lg_size=${lg_grp}\n  if [ $((${lg_delta} + ${lg_ndelta})) -eq ${lg_grp} ] ; then\n    lg_size=$((${lg_grp} + 1))\n  else\n    lg_size=${lg_grp}\n    rem=\"yes\"\n  fi\n\n  if [ ${lg_size} -lt $((${lg_p} + ${lg_g})) ] ; then\n    bin=\"yes\"\n    slab_size ${lg_p} ${lg_grp} ${lg_delta} ${ndelta}; pgs=${slab_size_pgs}\n  else\n    bin=\"no\"\n    pgs=0\n  fi\n  if [ ${lg_size} -lt ${lg_kmax} \\\n      -o ${lg_size} -eq ${lg_kmax} -a ${rem} = \"no\" ] ; then\n    lg_delta_lookup=${lg_delta}\n  else\n    lg_delta_lookup=\"no\"\n  fi\n  printf '    SC(%3d, %6d, %8d, %6d, %3s, %3s, %3d, %2s) \\\\\\n' ${index} ${lg_grp} ${lg_delta} ${ndelta} ${psz} ${bin} ${pgs} ${lg_delta_lookup}\n  # Defined upon return:\n  # - psz (\"yes\" or \"no\")\n  # - bin (\"yes\" or \"no\")\n  # - pgs\n  # - lg_delta_lookup (${lg_delta} or \"no\")\n}\n\nsep_line() {\n  echo \"                                                         \\\\\"\n}\n\nsize_classes() {\n  lg_z=$1\n  lg_q=$2\n  lg_t=$3\n  lg_p=$4\n  lg_g=$5\n\n  pow2 $((${lg_z} + 3)); ptr_bits=${pow2_result}\n  pow2 ${lg_g}; g=${pow2_result}\n\n  echo \"#define SIZE_CLASSES \\\\\"\n  echo \"  /* index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup */ \\\\\"\n\n  ntbins=0\n  nlbins=0\n  lg_tiny_maxclass='\"NA\"'\n  nbins=0\n  npsizes=0\n\n  # Tiny size classes.\n  ndelta=0\n  index=0\n  lg_grp=${lg_t}\n  lg_delta=${lg_grp}\n  while [ ${lg_grp} -lt ${lg_q} ] ; do\n    size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n    if [ ${lg_delta_lookup} != \"no\" ] ; then\n      nlbins=$((${index} + 1))\n    fi\n    if [ ${psz} = \"yes\" ] ; then\n      npsizes=$((${npsizes} + 1))\n    fi\n    if [ ${bin} != \"no\" ] ; then\n      nbins=$((${index} + 1))\n    fi\n    ntbins=$((${ntbins} + 1))\n    lg_tiny_maxclass=${lg_grp} # Final written value is correct.\n    index=$((${index} + 1))\n    lg_delta=${lg_grp}\n    lg_grp=$((${lg_grp} + 1))\n  done\n\n  # First non-tiny group.\n  if [ ${ntbins} -gt 0 ] ; then\n    sep_line\n    # The first size class has an unusual encoding, because the size has to be\n    # split between grp and delta*ndelta.\n    lg_grp=$((${lg_grp} - 1))\n    ndelta=1\n    size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n    index=$((${index} + 1))\n    lg_grp=$((${lg_grp} + 1))\n    lg_delta=$((${lg_delta} + 1))\n    if [ ${psz} = \"yes\" ] ; then\n      npsizes=$((${npsizes} + 1))\n    fi\n  fi\n  while [ ${ndelta} -lt ${g} ] ; do\n    size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n    index=$((${index} + 1))\n    ndelta=$((${ndelta} + 1))\n    if [ ${psz} = \"yes\" ] ; then\n      npsizes=$((${npsizes} + 1))\n    fi\n  done\n\n  # All remaining groups.\n  lg_grp=$((${lg_grp} + ${lg_g}))\n  while [ ${lg_grp} -lt $((${ptr_bits} - 1)) ] ; do\n    sep_line\n    ndelta=1\n    if [ ${lg_grp} -eq $((${ptr_bits} - 2)) ] ; then\n      ndelta_limit=$((${g} - 1))\n    else\n      ndelta_limit=${g}\n    fi\n    while [ ${ndelta} -le ${ndelta_limit} ] ; do\n      size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n      if [ ${lg_delta_lookup} != \"no\" ] ; then\n        nlbins=$((${index} + 1))\n        # Final written value is correct:\n        lookup_maxclass=\"((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))\"\n      fi\n      if [ ${psz} = \"yes\" ] ; then\n        npsizes=$((${npsizes} + 1))\n      fi\n      if [ ${bin} != \"no\" ] ; then\n        nbins=$((${index} + 1))\n        # Final written value is correct:\n        small_maxclass=\"((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))\"\n        if [ ${lg_g} -gt 0 ] ; then\n          lg_large_minclass=$((${lg_grp} + 1))\n        else\n          lg_large_minclass=$((${lg_grp} + 2))\n        fi\n      fi\n      # Final written value is correct:\n      large_maxclass=\"((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))\"\n      index=$((${index} + 1))\n      ndelta=$((${ndelta} + 1))\n    done\n    lg_grp=$((${lg_grp} + 1))\n    lg_delta=$((${lg_delta} + 1))\n  done\n  echo\n  nsizes=${index}\n  lg_ceil ${nsizes}; lg_ceil_nsizes=${lg_ceil_result}\n\n  # Defined upon completion:\n  # - ntbins\n  # - nlbins\n  # - nbins\n  # - nsizes\n  # - lg_ceil_nsizes\n  # - npsizes\n  # - lg_tiny_maxclass\n  # - lookup_maxclass\n  # - small_maxclass\n  # - lg_large_minclass\n  # - large_maxclass\n}\n\ncat <<EOF\n#ifndef JEMALLOC_INTERNAL_SIZE_CLASSES_H\n#define JEMALLOC_INTERNAL_SIZE_CLASSES_H\n\n/* This file was automatically generated by size_classes.sh. */\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n\n/*\n * This header file defines:\n *\n *   LG_SIZE_CLASS_GROUP: Lg of size class count for each size doubling.\n *   LG_TINY_MIN: Lg of minimum size class to support.\n *   SIZE_CLASSES: Complete table of SC(index, lg_grp, lg_delta, ndelta, psz,\n *                 bin, pgs, lg_delta_lookup) tuples.\n *     index: Size class index.\n *     lg_grp: Lg group base size (no deltas added).\n *     lg_delta: Lg delta to previous size class.\n *     ndelta: Delta multiplier.  size == 1<<lg_grp + ndelta<<lg_delta\n *     psz: 'yes' if a multiple of the page size, 'no' otherwise.\n *     bin: 'yes' if a small bin size class, 'no' otherwise.\n *     pgs: Slab page count if a small bin size class, 0 otherwise.\n *     lg_delta_lookup: Same as lg_delta if a lookup table size class, 'no'\n *                      otherwise.\n *   NTBINS: Number of tiny bins.\n *   NLBINS: Number of bins supported by the lookup table.\n *   NBINS: Number of small size class bins.\n *   NSIZES: Number of size classes.\n *   LG_CEIL_NSIZES: Number of bits required to store NSIZES.\n *   NPSIZES: Number of size classes that are a multiple of (1U << LG_PAGE).\n *   LG_TINY_MAXCLASS: Lg of maximum tiny size class.\n *   LOOKUP_MAXCLASS: Maximum size class included in lookup table.\n *   SMALL_MAXCLASS: Maximum small size class.\n *   LG_LARGE_MINCLASS: Lg of minimum large size class.\n *   LARGE_MAXCLASS: Maximum (large) size class.\n */\n\n#define LG_SIZE_CLASS_GROUP\t${lg_g}\n#define LG_TINY_MIN\t\t${lg_tmin}\n\nEOF\n\nfor lg_z in ${lg_zarr} ; do\n  for lg_q in ${lg_qarr} ; do\n    lg_t=${lg_tmin}\n    while [ ${lg_t} -le ${lg_q} ] ; do\n      # Iterate through page sizes and compute how many bins there are.\n      for lg_p in ${lg_parr} ; do\n        echo \"#if (LG_SIZEOF_PTR == ${lg_z} && LG_TINY_MIN == ${lg_t} && LG_QUANTUM == ${lg_q} && LG_PAGE == ${lg_p})\"\n        size_classes ${lg_z} ${lg_q} ${lg_t} ${lg_p} ${lg_g}\n        echo \"#define SIZE_CLASSES_DEFINED\"\n        echo \"#define NTBINS\t\t\t${ntbins}\"\n        echo \"#define NLBINS\t\t\t${nlbins}\"\n        echo \"#define NBINS\t\t\t${nbins}\"\n        echo \"#define NSIZES\t\t\t${nsizes}\"\n        echo \"#define LG_CEIL_NSIZES\t\t${lg_ceil_nsizes}\"\n        echo \"#define NPSIZES\t\t\t${npsizes}\"\n        echo \"#define LG_TINY_MAXCLASS\t${lg_tiny_maxclass}\"\n        echo \"#define LOOKUP_MAXCLASS\t\t${lookup_maxclass}\"\n        echo \"#define SMALL_MAXCLASS\t\t${small_maxclass}\"\n        echo \"#define LG_LARGE_MINCLASS\t${lg_large_minclass}\"\n        echo \"#define LARGE_MINCLASS\t\t(ZU(1) << LG_LARGE_MINCLASS)\"\n        echo \"#define LARGE_MAXCLASS\t\t${large_maxclass}\"\n        echo \"#endif\"\n        echo\n      done\n      lg_t=$((${lg_t} + 1))\n    done\n  done\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 size2index_tab lookup table uses uint8_t to encode each bin index, so we\n * cannot support more than 256 small size classes.\n */\n#if (NBINS > 256)\n#  error \"Too many small size classes\"\n#endif\n\n#endif /* JEMALLOC_INTERNAL_SIZE_CLASSES_H */\nEOF\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/smoothstep.h",
    "content": "#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H\n#define JEMALLOC_INTERNAL_SMOOTHSTEP_H\n\n/*\n * This file was generated by the following command:\n *   sh smoothstep.sh smoother 200 24 3 15\n */\n/******************************************************************************/\n\n/*\n * This header defines a precomputed table based on the smoothstep family of\n * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0\n * to 1 in 0 <= x <= 1.  The table is stored as integer fixed point values so\n * that floating point math can be avoided.\n *\n *                      3     2\n *   smoothstep(x) = -2x  + 3x\n *\n *                       5      4      3\n *   smootherstep(x) = 6x  - 15x  + 10x\n *\n *                          7      6      5      4\n *   smootheststep(x) = -20x  + 70x  - 84x  + 35x\n */\n\n#define SMOOTHSTEP_VARIANT\t\"smoother\"\n#define SMOOTHSTEP_NSTEPS\t200\n#define SMOOTHSTEP_BFP\t\t24\n#define SMOOTHSTEP \\\n /* STEP(step, h,                            x,     y) */ \\\n    STEP(   1, UINT64_C(0x0000000000000014), 0.005, 0.000001240643750) \\\n    STEP(   2, UINT64_C(0x00000000000000a5), 0.010, 0.000009850600000) \\\n    STEP(   3, UINT64_C(0x0000000000000229), 0.015, 0.000032995181250) \\\n    STEP(   4, UINT64_C(0x0000000000000516), 0.020, 0.000077619200000) \\\n    STEP(   5, UINT64_C(0x00000000000009dc), 0.025, 0.000150449218750) \\\n    STEP(   6, UINT64_C(0x00000000000010e8), 0.030, 0.000257995800000) \\\n    STEP(   7, UINT64_C(0x0000000000001aa4), 0.035, 0.000406555756250) \\\n    STEP(   8, UINT64_C(0x0000000000002777), 0.040, 0.000602214400000) \\\n    STEP(   9, UINT64_C(0x00000000000037c2), 0.045, 0.000850847793750) \\\n    STEP(  10, UINT64_C(0x0000000000004be6), 0.050, 0.001158125000000) \\\n    STEP(  11, UINT64_C(0x000000000000643c), 0.055, 0.001529510331250) \\\n    STEP(  12, UINT64_C(0x000000000000811f), 0.060, 0.001970265600000) \\\n    STEP(  13, UINT64_C(0x000000000000a2e2), 0.065, 0.002485452368750) \\\n    STEP(  14, UINT64_C(0x000000000000c9d8), 0.070, 0.003079934200000) \\\n    STEP(  15, UINT64_C(0x000000000000f64f), 0.075, 0.003758378906250) \\\n    STEP(  16, UINT64_C(0x0000000000012891), 0.080, 0.004525260800000) \\\n    STEP(  17, UINT64_C(0x00000000000160e7), 0.085, 0.005384862943750) \\\n    STEP(  18, UINT64_C(0x0000000000019f95), 0.090, 0.006341279400000) \\\n    STEP(  19, UINT64_C(0x000000000001e4dc), 0.095, 0.007398417481250) \\\n    STEP(  20, UINT64_C(0x00000000000230fc), 0.100, 0.008560000000000) \\\n    STEP(  21, UINT64_C(0x0000000000028430), 0.105, 0.009829567518750) \\\n    STEP(  22, UINT64_C(0x000000000002deb0), 0.110, 0.011210480600000) \\\n    STEP(  23, UINT64_C(0x00000000000340b1), 0.115, 0.012705922056250) \\\n    STEP(  24, UINT64_C(0x000000000003aa67), 0.120, 0.014318899200000) \\\n    STEP(  25, UINT64_C(0x0000000000041c00), 0.125, 0.016052246093750) \\\n    STEP(  26, UINT64_C(0x00000000000495a8), 0.130, 0.017908625800000) \\\n    STEP(  27, UINT64_C(0x000000000005178b), 0.135, 0.019890532631250) \\\n    STEP(  28, UINT64_C(0x000000000005a1cf), 0.140, 0.022000294400000) \\\n    STEP(  29, UINT64_C(0x0000000000063498), 0.145, 0.024240074668750) \\\n    STEP(  30, UINT64_C(0x000000000006d009), 0.150, 0.026611875000000) \\\n    STEP(  31, UINT64_C(0x000000000007743f), 0.155, 0.029117537206250) \\\n    STEP(  32, UINT64_C(0x0000000000082157), 0.160, 0.031758745600000) \\\n    STEP(  33, UINT64_C(0x000000000008d76b), 0.165, 0.034537029243750) \\\n    STEP(  34, UINT64_C(0x0000000000099691), 0.170, 0.037453764200000) \\\n    STEP(  35, UINT64_C(0x00000000000a5edf), 0.175, 0.040510175781250) \\\n    STEP(  36, UINT64_C(0x00000000000b3067), 0.180, 0.043707340800000) \\\n    STEP(  37, UINT64_C(0x00000000000c0b38), 0.185, 0.047046189818750) \\\n    STEP(  38, UINT64_C(0x00000000000cef5e), 0.190, 0.050527509400000) \\\n    STEP(  39, UINT64_C(0x00000000000ddce6), 0.195, 0.054151944356250) \\\n    STEP(  40, UINT64_C(0x00000000000ed3d8), 0.200, 0.057920000000000) \\\n    STEP(  41, UINT64_C(0x00000000000fd439), 0.205, 0.061832044393750) \\\n    STEP(  42, UINT64_C(0x000000000010de0e), 0.210, 0.065888310600000) \\\n    STEP(  43, UINT64_C(0x000000000011f158), 0.215, 0.070088898931250) \\\n    STEP(  44, UINT64_C(0x0000000000130e17), 0.220, 0.074433779200000) \\\n    STEP(  45, UINT64_C(0x0000000000143448), 0.225, 0.078922792968750) \\\n    STEP(  46, UINT64_C(0x00000000001563e7), 0.230, 0.083555655800000) \\\n    STEP(  47, UINT64_C(0x0000000000169cec), 0.235, 0.088331959506250) \\\n    STEP(  48, UINT64_C(0x000000000017df4f), 0.240, 0.093251174400000) \\\n    STEP(  49, UINT64_C(0x0000000000192b04), 0.245, 0.098312651543750) \\\n    STEP(  50, UINT64_C(0x00000000001a8000), 0.250, 0.103515625000000) \\\n    STEP(  51, UINT64_C(0x00000000001bde32), 0.255, 0.108859214081250) \\\n    STEP(  52, UINT64_C(0x00000000001d458b), 0.260, 0.114342425600000) \\\n    STEP(  53, UINT64_C(0x00000000001eb5f8), 0.265, 0.119964156118750) \\\n    STEP(  54, UINT64_C(0x0000000000202f65), 0.270, 0.125723194200000) \\\n    STEP(  55, UINT64_C(0x000000000021b1bb), 0.275, 0.131618222656250) \\\n    STEP(  56, UINT64_C(0x0000000000233ce3), 0.280, 0.137647820800000) \\\n    STEP(  57, UINT64_C(0x000000000024d0c3), 0.285, 0.143810466693750) \\\n    STEP(  58, UINT64_C(0x0000000000266d40), 0.290, 0.150104539400000) \\\n    STEP(  59, UINT64_C(0x000000000028123d), 0.295, 0.156528321231250) \\\n    STEP(  60, UINT64_C(0x000000000029bf9c), 0.300, 0.163080000000000) \\\n    STEP(  61, UINT64_C(0x00000000002b753d), 0.305, 0.169757671268750) \\\n    STEP(  62, UINT64_C(0x00000000002d32fe), 0.310, 0.176559340600000) \\\n    STEP(  63, UINT64_C(0x00000000002ef8bc), 0.315, 0.183482925806250) \\\n    STEP(  64, UINT64_C(0x000000000030c654), 0.320, 0.190526259200000) \\\n    STEP(  65, UINT64_C(0x0000000000329b9f), 0.325, 0.197687089843750) \\\n    STEP(  66, UINT64_C(0x0000000000347875), 0.330, 0.204963085800000) \\\n    STEP(  67, UINT64_C(0x0000000000365cb0), 0.335, 0.212351836381250) \\\n    STEP(  68, UINT64_C(0x0000000000384825), 0.340, 0.219850854400000) \\\n    STEP(  69, UINT64_C(0x00000000003a3aa8), 0.345, 0.227457578418750) \\\n    STEP(  70, UINT64_C(0x00000000003c340f), 0.350, 0.235169375000000) \\\n    STEP(  71, UINT64_C(0x00000000003e342b), 0.355, 0.242983540956250) \\\n    STEP(  72, UINT64_C(0x0000000000403ace), 0.360, 0.250897305600000) \\\n    STEP(  73, UINT64_C(0x00000000004247c8), 0.365, 0.258907832993750) \\\n    STEP(  74, UINT64_C(0x0000000000445ae9), 0.370, 0.267012224200000) \\\n    STEP(  75, UINT64_C(0x0000000000467400), 0.375, 0.275207519531250) \\\n    STEP(  76, UINT64_C(0x00000000004892d8), 0.380, 0.283490700800000) \\\n    STEP(  77, UINT64_C(0x00000000004ab740), 0.385, 0.291858693568750) \\\n    STEP(  78, UINT64_C(0x00000000004ce102), 0.390, 0.300308369400000) \\\n    STEP(  79, UINT64_C(0x00000000004f0fe9), 0.395, 0.308836548106250) \\\n    STEP(  80, UINT64_C(0x00000000005143bf), 0.400, 0.317440000000000) \\\n    STEP(  81, UINT64_C(0x0000000000537c4d), 0.405, 0.326115448143750) \\\n    STEP(  82, UINT64_C(0x000000000055b95b), 0.410, 0.334859570600000) \\\n    STEP(  83, UINT64_C(0x000000000057fab1), 0.415, 0.343669002681250) \\\n    STEP(  84, UINT64_C(0x00000000005a4015), 0.420, 0.352540339200000) \\\n    STEP(  85, UINT64_C(0x00000000005c894e), 0.425, 0.361470136718750) \\\n    STEP(  86, UINT64_C(0x00000000005ed622), 0.430, 0.370454915800000) \\\n    STEP(  87, UINT64_C(0x0000000000612655), 0.435, 0.379491163256250) \\\n    STEP(  88, UINT64_C(0x00000000006379ac), 0.440, 0.388575334400000) \\\n    STEP(  89, UINT64_C(0x000000000065cfeb), 0.445, 0.397703855293750) \\\n    STEP(  90, UINT64_C(0x00000000006828d6), 0.450, 0.406873125000000) \\\n    STEP(  91, UINT64_C(0x00000000006a842f), 0.455, 0.416079517831250) \\\n    STEP(  92, UINT64_C(0x00000000006ce1bb), 0.460, 0.425319385600000) \\\n    STEP(  93, UINT64_C(0x00000000006f413a), 0.465, 0.434589059868750) \\\n    STEP(  94, UINT64_C(0x000000000071a270), 0.470, 0.443884854200000) \\\n    STEP(  95, UINT64_C(0x000000000074051d), 0.475, 0.453203066406250) \\\n    STEP(  96, UINT64_C(0x0000000000766905), 0.480, 0.462539980800000) \\\n    STEP(  97, UINT64_C(0x000000000078cde7), 0.485, 0.471891870443750) \\\n    STEP(  98, UINT64_C(0x00000000007b3387), 0.490, 0.481254999400000) \\\n    STEP(  99, UINT64_C(0x00000000007d99a4), 0.495, 0.490625624981250) \\\n    STEP( 100, UINT64_C(0x0000000000800000), 0.500, 0.500000000000000) \\\n    STEP( 101, UINT64_C(0x000000000082665b), 0.505, 0.509374375018750) \\\n    STEP( 102, UINT64_C(0x000000000084cc78), 0.510, 0.518745000600000) \\\n    STEP( 103, UINT64_C(0x0000000000873218), 0.515, 0.528108129556250) \\\n    STEP( 104, UINT64_C(0x00000000008996fa), 0.520, 0.537460019200000) \\\n    STEP( 105, UINT64_C(0x00000000008bfae2), 0.525, 0.546796933593750) \\\n    STEP( 106, UINT64_C(0x00000000008e5d8f), 0.530, 0.556115145800000) \\\n    STEP( 107, UINT64_C(0x000000000090bec5), 0.535, 0.565410940131250) \\\n    STEP( 108, UINT64_C(0x0000000000931e44), 0.540, 0.574680614400000) \\\n    STEP( 109, UINT64_C(0x0000000000957bd0), 0.545, 0.583920482168750) \\\n    STEP( 110, UINT64_C(0x000000000097d729), 0.550, 0.593126875000000) \\\n    STEP( 111, UINT64_C(0x00000000009a3014), 0.555, 0.602296144706250) \\\n    STEP( 112, UINT64_C(0x00000000009c8653), 0.560, 0.611424665600000) \\\n    STEP( 113, UINT64_C(0x00000000009ed9aa), 0.565, 0.620508836743750) \\\n    STEP( 114, UINT64_C(0x0000000000a129dd), 0.570, 0.629545084200000) \\\n    STEP( 115, UINT64_C(0x0000000000a376b1), 0.575, 0.638529863281250) \\\n    STEP( 116, UINT64_C(0x0000000000a5bfea), 0.580, 0.647459660800000) \\\n    STEP( 117, UINT64_C(0x0000000000a8054e), 0.585, 0.656330997318750) \\\n    STEP( 118, UINT64_C(0x0000000000aa46a4), 0.590, 0.665140429400000) \\\n    STEP( 119, UINT64_C(0x0000000000ac83b2), 0.595, 0.673884551856250) \\\n    STEP( 120, UINT64_C(0x0000000000aebc40), 0.600, 0.682560000000000) \\\n    STEP( 121, UINT64_C(0x0000000000b0f016), 0.605, 0.691163451893750) \\\n    STEP( 122, UINT64_C(0x0000000000b31efd), 0.610, 0.699691630600000) \\\n    STEP( 123, UINT64_C(0x0000000000b548bf), 0.615, 0.708141306431250) \\\n    STEP( 124, UINT64_C(0x0000000000b76d27), 0.620, 0.716509299200000) \\\n    STEP( 125, UINT64_C(0x0000000000b98c00), 0.625, 0.724792480468750) \\\n    STEP( 126, UINT64_C(0x0000000000bba516), 0.630, 0.732987775800000) \\\n    STEP( 127, UINT64_C(0x0000000000bdb837), 0.635, 0.741092167006250) \\\n    STEP( 128, UINT64_C(0x0000000000bfc531), 0.640, 0.749102694400000) \\\n    STEP( 129, UINT64_C(0x0000000000c1cbd4), 0.645, 0.757016459043750) \\\n    STEP( 130, UINT64_C(0x0000000000c3cbf0), 0.650, 0.764830625000000) \\\n    STEP( 131, UINT64_C(0x0000000000c5c557), 0.655, 0.772542421581250) \\\n    STEP( 132, UINT64_C(0x0000000000c7b7da), 0.660, 0.780149145600000) \\\n    STEP( 133, UINT64_C(0x0000000000c9a34f), 0.665, 0.787648163618750) \\\n    STEP( 134, UINT64_C(0x0000000000cb878a), 0.670, 0.795036914200000) \\\n    STEP( 135, UINT64_C(0x0000000000cd6460), 0.675, 0.802312910156250) \\\n    STEP( 136, UINT64_C(0x0000000000cf39ab), 0.680, 0.809473740800000) \\\n    STEP( 137, UINT64_C(0x0000000000d10743), 0.685, 0.816517074193750) \\\n    STEP( 138, UINT64_C(0x0000000000d2cd01), 0.690, 0.823440659400000) \\\n    STEP( 139, UINT64_C(0x0000000000d48ac2), 0.695, 0.830242328731250) \\\n    STEP( 140, UINT64_C(0x0000000000d64063), 0.700, 0.836920000000000) \\\n    STEP( 141, UINT64_C(0x0000000000d7edc2), 0.705, 0.843471678768750) \\\n    STEP( 142, UINT64_C(0x0000000000d992bf), 0.710, 0.849895460600000) \\\n    STEP( 143, UINT64_C(0x0000000000db2f3c), 0.715, 0.856189533306250) \\\n    STEP( 144, UINT64_C(0x0000000000dcc31c), 0.720, 0.862352179200000) \\\n    STEP( 145, UINT64_C(0x0000000000de4e44), 0.725, 0.868381777343750) \\\n    STEP( 146, UINT64_C(0x0000000000dfd09a), 0.730, 0.874276805800000) \\\n    STEP( 147, UINT64_C(0x0000000000e14a07), 0.735, 0.880035843881250) \\\n    STEP( 148, UINT64_C(0x0000000000e2ba74), 0.740, 0.885657574400000) \\\n    STEP( 149, UINT64_C(0x0000000000e421cd), 0.745, 0.891140785918750) \\\n    STEP( 150, UINT64_C(0x0000000000e58000), 0.750, 0.896484375000000) \\\n    STEP( 151, UINT64_C(0x0000000000e6d4fb), 0.755, 0.901687348456250) \\\n    STEP( 152, UINT64_C(0x0000000000e820b0), 0.760, 0.906748825600000) \\\n    STEP( 153, UINT64_C(0x0000000000e96313), 0.765, 0.911668040493750) \\\n    STEP( 154, UINT64_C(0x0000000000ea9c18), 0.770, 0.916444344200000) \\\n    STEP( 155, UINT64_C(0x0000000000ebcbb7), 0.775, 0.921077207031250) \\\n    STEP( 156, UINT64_C(0x0000000000ecf1e8), 0.780, 0.925566220800000) \\\n    STEP( 157, UINT64_C(0x0000000000ee0ea7), 0.785, 0.929911101068750) \\\n    STEP( 158, UINT64_C(0x0000000000ef21f1), 0.790, 0.934111689400000) \\\n    STEP( 159, UINT64_C(0x0000000000f02bc6), 0.795, 0.938167955606250) \\\n    STEP( 160, UINT64_C(0x0000000000f12c27), 0.800, 0.942080000000000) \\\n    STEP( 161, UINT64_C(0x0000000000f22319), 0.805, 0.945848055643750) \\\n    STEP( 162, UINT64_C(0x0000000000f310a1), 0.810, 0.949472490600000) \\\n    STEP( 163, UINT64_C(0x0000000000f3f4c7), 0.815, 0.952953810181250) \\\n    STEP( 164, UINT64_C(0x0000000000f4cf98), 0.820, 0.956292659200000) \\\n    STEP( 165, UINT64_C(0x0000000000f5a120), 0.825, 0.959489824218750) \\\n    STEP( 166, UINT64_C(0x0000000000f6696e), 0.830, 0.962546235800000) \\\n    STEP( 167, UINT64_C(0x0000000000f72894), 0.835, 0.965462970756250) \\\n    STEP( 168, UINT64_C(0x0000000000f7dea8), 0.840, 0.968241254400000) \\\n    STEP( 169, UINT64_C(0x0000000000f88bc0), 0.845, 0.970882462793750) \\\n    STEP( 170, UINT64_C(0x0000000000f92ff6), 0.850, 0.973388125000000) \\\n    STEP( 171, UINT64_C(0x0000000000f9cb67), 0.855, 0.975759925331250) \\\n    STEP( 172, UINT64_C(0x0000000000fa5e30), 0.860, 0.977999705600000) \\\n    STEP( 173, UINT64_C(0x0000000000fae874), 0.865, 0.980109467368750) \\\n    STEP( 174, UINT64_C(0x0000000000fb6a57), 0.870, 0.982091374200000) \\\n    STEP( 175, UINT64_C(0x0000000000fbe400), 0.875, 0.983947753906250) \\\n    STEP( 176, UINT64_C(0x0000000000fc5598), 0.880, 0.985681100800000) \\\n    STEP( 177, UINT64_C(0x0000000000fcbf4e), 0.885, 0.987294077943750) \\\n    STEP( 178, UINT64_C(0x0000000000fd214f), 0.890, 0.988789519400000) \\\n    STEP( 179, UINT64_C(0x0000000000fd7bcf), 0.895, 0.990170432481250) \\\n    STEP( 180, UINT64_C(0x0000000000fdcf03), 0.900, 0.991440000000000) \\\n    STEP( 181, UINT64_C(0x0000000000fe1b23), 0.905, 0.992601582518750) \\\n    STEP( 182, UINT64_C(0x0000000000fe606a), 0.910, 0.993658720600000) \\\n    STEP( 183, UINT64_C(0x0000000000fe9f18), 0.915, 0.994615137056250) \\\n    STEP( 184, UINT64_C(0x0000000000fed76e), 0.920, 0.995474739200000) \\\n    STEP( 185, UINT64_C(0x0000000000ff09b0), 0.925, 0.996241621093750) \\\n    STEP( 186, UINT64_C(0x0000000000ff3627), 0.930, 0.996920065800000) \\\n    STEP( 187, UINT64_C(0x0000000000ff5d1d), 0.935, 0.997514547631250) \\\n    STEP( 188, UINT64_C(0x0000000000ff7ee0), 0.940, 0.998029734400000) \\\n    STEP( 189, UINT64_C(0x0000000000ff9bc3), 0.945, 0.998470489668750) \\\n    STEP( 190, UINT64_C(0x0000000000ffb419), 0.950, 0.998841875000000) \\\n    STEP( 191, UINT64_C(0x0000000000ffc83d), 0.955, 0.999149152206250) \\\n    STEP( 192, UINT64_C(0x0000000000ffd888), 0.960, 0.999397785600000) \\\n    STEP( 193, UINT64_C(0x0000000000ffe55b), 0.965, 0.999593444243750) \\\n    STEP( 194, UINT64_C(0x0000000000ffef17), 0.970, 0.999742004200000) \\\n    STEP( 195, UINT64_C(0x0000000000fff623), 0.975, 0.999849550781250) \\\n    STEP( 196, UINT64_C(0x0000000000fffae9), 0.980, 0.999922380800000) \\\n    STEP( 197, UINT64_C(0x0000000000fffdd6), 0.985, 0.999967004818750) \\\n    STEP( 198, UINT64_C(0x0000000000ffff5a), 0.990, 0.999990149400000) \\\n    STEP( 199, UINT64_C(0x0000000000ffffeb), 0.995, 0.999998759356250) \\\n    STEP( 200, UINT64_C(0x0000000001000000), 1.000, 1.000000000000000) \\\n\n#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/smoothstep.sh",
    "content": "#!/bin/sh\n#\n# Generate a discrete lookup table for a sigmoid function in the smoothstep\n# family (https://en.wikipedia.org/wiki/Smoothstep), where the lookup table\n# entries correspond to x in [1/nsteps, 2/nsteps, ..., nsteps/nsteps].  Encode\n# the entries using a binary fixed point representation.\n#\n# Usage: smoothstep.sh <variant> <nsteps> <bfp> <xprec> <yprec>\n#\n#        <variant> is in {smooth, smoother, smoothest}.\n#        <nsteps> must be greater than zero.\n#        <bfp> must be in [0..62]; reasonable values are roughly [10..30].\n#        <xprec> is x decimal precision.\n#        <yprec> is y decimal precision.\n\n#set -x\n\ncmd=\"sh smoothstep.sh $*\"\nvariant=$1\nnsteps=$2\nbfp=$3\nxprec=$4\nyprec=$5\n\ncase \"${variant}\" in\n  smooth)\n    ;;\n  smoother)\n    ;;\n  smoothest)\n    ;;\n  *)\n    echo \"Unsupported variant\"\n    exit 1\n    ;;\nesac\n\nsmooth() {\n  step=$1\n  y=`echo ${yprec} k ${step} ${nsteps} / sx _2 lx 3 ^ '*' 3 lx 2 ^ '*' + p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g' | tr '.' ' ' | awk '{print $1}' `\n}\n\nsmoother() {\n  step=$1\n  y=`echo ${yprec} k ${step} ${nsteps} / sx 6 lx 5 ^ '*' _15 lx 4 ^ '*' + 10 lx 3 ^ '*' + p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g' | tr '.' ' ' | awk '{print $1}' `\n}\n\nsmoothest() {\n  step=$1\n  y=`echo ${yprec} k ${step} ${nsteps} / sx _20 lx 7 ^ '*' 70 lx 6 ^ '*' + _84 lx 5 ^ '*' + 35 lx 4 ^ '*' + p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g' | tr '.' ' ' | awk '{print $1}' `\n}\n\ncat <<EOF\n#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H\n#define JEMALLOC_INTERNAL_SMOOTHSTEP_H\n\n/*\n * This file was generated by the following command:\n *   $cmd\n */\n/******************************************************************************/\n\n/*\n * This header defines a precomputed table based on the smoothstep family of\n * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0\n * to 1 in 0 <= x <= 1.  The table is stored as integer fixed point values so\n * that floating point math can be avoided.\n *\n *                      3     2\n *   smoothstep(x) = -2x  + 3x\n *\n *                       5      4      3\n *   smootherstep(x) = 6x  - 15x  + 10x\n *\n *                          7      6      5      4\n *   smootheststep(x) = -20x  + 70x  - 84x  + 35x\n */\n\n#define SMOOTHSTEP_VARIANT\t\"${variant}\"\n#define SMOOTHSTEP_NSTEPS\t${nsteps}\n#define SMOOTHSTEP_BFP\t\t${bfp}\n#define SMOOTHSTEP \\\\\n /* STEP(step, h,                            x,     y) */ \\\\\nEOF\n\ns=1\nwhile [ $s -le $nsteps ] ; do\n  $variant ${s}\n  x=`echo ${xprec} k ${s} ${nsteps} / p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  printf '    STEP(%4d, UINT64_C(0x%016x), %s, %s) \\\\\\n' ${s} ${h} ${x} ${y}\n\n  s=$((s+1))\ndone\necho\n\ncat <<EOF\n#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */\nEOF\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/spin.h",
    "content": "#ifndef JEMALLOC_INTERNAL_SPIN_H\n#define JEMALLOC_INTERNAL_SPIN_H\n\n#define SPIN_INITIALIZER {0U}\n\ntypedef struct {\n\tunsigned iteration;\n} spin_t;\n\nstatic inline void\nspin_cpu_spinwait() {\n#  if HAVE_CPU_SPINWAIT\n\tCPU_SPINWAIT;\n#  else\n\tvolatile int x = 0;\n\tx = x;\n#  endif\n}\n\nstatic inline void\nspin_adaptive(spin_t *spin) {\n\tvolatile uint32_t i;\n\n\tif (spin->iteration < 5) {\n\t\tfor (i = 0; i < (1U << spin->iteration); i++) {\n\t\t\tspin_cpu_spinwait();\n\t\t}\n\t\tspin->iteration++;\n\t} else {\n#ifdef _WIN32\n\t\tSwitchToThread();\n#else\n\t\tsched_yield();\n#endif\n\t}\n}\n\n#undef SPIN_INLINE\n\n#endif /* JEMALLOC_INTERNAL_SPIN_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/stats.h",
    "content": "#ifndef JEMALLOC_INTERNAL_STATS_H\n#define JEMALLOC_INTERNAL_STATS_H\n\n/*  OPTION(opt,\t\tvar_name,\tdefault,\tset_value_to) */\n#define STATS_PRINT_OPTIONS\t\t\t\t\t\t\\\n    OPTION('J',\t\tjson,\t\tfalse,\t\ttrue)\t\t\\\n    OPTION('g',\t\tgeneral,\ttrue,\t\tfalse)\t\t\\\n    OPTION('m',\t\tmerged,\t\tconfig_stats,\tfalse)\t\t\\\n    OPTION('d',\t\tdestroyed,\tconfig_stats,\tfalse)\t\t\\\n    OPTION('a',\t\tunmerged,\tconfig_stats,\tfalse)\t\t\\\n    OPTION('b',\t\tbins,\t\ttrue,\t\tfalse)\t\t\\\n    OPTION('l',\t\tlarge,\t\ttrue,\t\tfalse)\t\t\\\n    OPTION('x',\t\tmutex,\t\ttrue,\t\tfalse)\n\nenum {\n#define OPTION(o, v, d, s) stats_print_option_num_##v,\n    STATS_PRINT_OPTIONS\n#undef OPTION\n    stats_print_tot_num_options\n};\n\n/* Options for stats_print. */\nextern bool opt_stats_print;\nextern char opt_stats_print_opts[stats_print_tot_num_options+1];\n\n/* Implements je_malloc_stats_print. */\nvoid stats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts);\n\n#endif /* JEMALLOC_INTERNAL_STATS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/sz.h",
    "content": "#ifndef JEMALLOC_INTERNAL_SIZE_H\n#define JEMALLOC_INTERNAL_SIZE_H\n\n#include \"jemalloc/internal/bit_util.h\"\n#include \"jemalloc/internal/pages.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/util.h\"\n\n/*\n * sz module: Size computations.\n *\n * Some abbreviations used here:\n *   p: Page\n *   ind: Index\n *   s, sz: Size\n *   u: Usable size\n *   a: Aligned\n *\n * These are not always used completely consistently, but should be enough to\n * interpret function names.  E.g. sz_psz2ind converts page size to page size\n * index; sz_sa2u converts a (size, alignment) allocation request to the usable\n * size that would result from such an allocation.\n */\n\n/*\n * sz_pind2sz_tab encodes the same information as could be computed by\n * sz_pind2sz_compute().\n */\nextern size_t const sz_pind2sz_tab[NPSIZES+1];\n/*\n * sz_index2size_tab encodes the same information as could be computed (at\n * unacceptable cost in some code paths) by sz_index2size_compute().\n */\nextern size_t const sz_index2size_tab[NSIZES];\n/*\n * sz_size2index_tab 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 sz_size2index().\n */\nextern uint8_t const sz_size2index_tab[];\n\nstatic const size_t sz_large_pad =\n#ifdef JEMALLOC_CACHE_OBLIVIOUS\n    PAGE\n#else\n    0\n#endif\n    ;\n\nJEMALLOC_ALWAYS_INLINE pszind_t\nsz_psz2ind(size_t psz) {\n\tif (unlikely(psz > LARGE_MAXCLASS)) {\n\t\treturn NPSIZES;\n\t}\n\t{\n\t\tpszind_t x = lg_floor((psz<<1)-1);\n\t\tpszind_t shift = (x < LG_SIZE_CLASS_GROUP + LG_PAGE) ? 0 : x -\n\t\t    (LG_SIZE_CLASS_GROUP + LG_PAGE);\n\t\tpszind_t grp = shift << LG_SIZE_CLASS_GROUP;\n\n\t\tpszind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?\n\t\t    LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;\n\n\t\tsize_t delta_inverse_mask = ZU(-1) << lg_delta;\n\t\tpszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &\n\t\t    ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);\n\n\t\tpszind_t ind = grp + mod;\n\t\treturn ind;\n\t}\n}\n\nstatic inline size_t\nsz_pind2sz_compute(pszind_t pind) {\n\tif (unlikely(pind == NPSIZES)) {\n\t\treturn LARGE_MAXCLASS + PAGE;\n\t}\n\t{\n\t\tsize_t grp = pind >> LG_SIZE_CLASS_GROUP;\n\t\tsize_t mod = pind & ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);\n\n\t\tsize_t grp_size_mask = ~((!!grp)-1);\n\t\tsize_t grp_size = ((ZU(1) << (LG_PAGE +\n\t\t    (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;\n\n\t\tsize_t shift = (grp == 0) ? 1 : grp;\n\t\tsize_t lg_delta = shift + (LG_PAGE-1);\n\t\tsize_t mod_size = (mod+1) << lg_delta;\n\n\t\tsize_t sz = grp_size + mod_size;\n\t\treturn sz;\n\t}\n}\n\nstatic inline size_t\nsz_pind2sz_lookup(pszind_t pind) {\n\tsize_t ret = (size_t)sz_pind2sz_tab[pind];\n\tassert(ret == sz_pind2sz_compute(pind));\n\treturn ret;\n}\n\nstatic inline size_t\nsz_pind2sz(pszind_t pind) {\n\tassert(pind < NPSIZES+1);\n\treturn sz_pind2sz_lookup(pind);\n}\n\nstatic inline size_t\nsz_psz2u(size_t psz) {\n\tif (unlikely(psz > LARGE_MAXCLASS)) {\n\t\treturn LARGE_MAXCLASS + PAGE;\n\t}\n\t{\n\t\tsize_t x = lg_floor((psz<<1)-1);\n\t\tsize_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?\n\t\t    LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;\n\t\tsize_t delta = ZU(1) << lg_delta;\n\t\tsize_t delta_mask = delta - 1;\n\t\tsize_t usize = (psz + delta_mask) & ~delta_mask;\n\t\treturn usize;\n\t}\n}\n\nstatic inline szind_t\nsz_size2index_compute(size_t size) {\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\treturn NSIZES;\n\t}\n#if (NTBINS != 0)\n\tif (size <= (ZU(1) << LG_TINY_MAXCLASS)) {\n\t\tszind_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;\n\t\tszind_t lg_ceil = lg_floor(pow2_ceil_zu(size));\n\t\treturn (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin);\n\t}\n#endif\n\t{\n\t\tszind_t x = lg_floor((size<<1)-1);\n\t\tszind_t shift = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM) ? 0 :\n\t\t    x - (LG_SIZE_CLASS_GROUP + LG_QUANTUM);\n\t\tszind_t grp = shift << LG_SIZE_CLASS_GROUP;\n\n\t\tszind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)\n\t\t    ? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;\n\n\t\tsize_t delta_inverse_mask = ZU(-1) << lg_delta;\n\t\tszind_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) &\n\t\t    ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);\n\n\t\tszind_t index = NTBINS + grp + mod;\n\t\treturn index;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nsz_size2index_lookup(size_t size) {\n\tassert(size <= LOOKUP_MAXCLASS);\n\t{\n\t\tszind_t ret = (sz_size2index_tab[(size-1) >> LG_TINY_MIN]);\n\t\tassert(ret == sz_size2index_compute(size));\n\t\treturn ret;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nsz_size2index(size_t size) {\n\tassert(size > 0);\n\tif (likely(size <= LOOKUP_MAXCLASS)) {\n\t\treturn sz_size2index_lookup(size);\n\t}\n\treturn sz_size2index_compute(size);\n}\n\nstatic inline size_t\nsz_index2size_compute(szind_t index) {\n#if (NTBINS > 0)\n\tif (index < NTBINS) {\n\t\treturn (ZU(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + index));\n\t}\n#endif\n\t{\n\t\tsize_t reduced_index = index - NTBINS;\n\t\tsize_t grp = reduced_index >> LG_SIZE_CLASS_GROUP;\n\t\tsize_t mod = reduced_index & ((ZU(1) << LG_SIZE_CLASS_GROUP) -\n\t\t    1);\n\n\t\tsize_t grp_size_mask = ~((!!grp)-1);\n\t\tsize_t grp_size = ((ZU(1) << (LG_QUANTUM +\n\t\t    (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;\n\n\t\tsize_t shift = (grp == 0) ? 1 : grp;\n\t\tsize_t lg_delta = shift + (LG_QUANTUM-1);\n\t\tsize_t mod_size = (mod+1) << lg_delta;\n\n\t\tsize_t usize = grp_size + mod_size;\n\t\treturn usize;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_index2size_lookup(szind_t index) {\n\tsize_t ret = (size_t)sz_index2size_tab[index];\n\tassert(ret == sz_index2size_compute(index));\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_index2size(szind_t index) {\n\tassert(index < NSIZES);\n\treturn sz_index2size_lookup(index);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_s2u_compute(size_t size) {\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\treturn 0;\n\t}\n#if (NTBINS > 0)\n\tif (size <= (ZU(1) << LG_TINY_MAXCLASS)) {\n\t\tsize_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;\n\t\tsize_t lg_ceil = lg_floor(pow2_ceil_zu(size));\n\t\treturn (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) :\n\t\t    (ZU(1) << lg_ceil));\n\t}\n#endif\n\t{\n\t\tsize_t x = lg_floor((size<<1)-1);\n\t\tsize_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)\n\t\t    ?  LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;\n\t\tsize_t delta = ZU(1) << lg_delta;\n\t\tsize_t delta_mask = delta - 1;\n\t\tsize_t usize = (size + delta_mask) & ~delta_mask;\n\t\treturn usize;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_s2u_lookup(size_t size) {\n\tsize_t ret = sz_index2size_lookup(sz_size2index_lookup(size));\n\n\tassert(ret == sz_s2u_compute(size));\n\treturn ret;\n}\n\n/*\n * Compute usable size that would result from allocating an object with the\n * specified size.\n */\nJEMALLOC_ALWAYS_INLINE size_t\nsz_s2u(size_t size) {\n\tassert(size > 0);\n\tif (likely(size <= LOOKUP_MAXCLASS)) {\n\t\treturn sz_s2u_lookup(size);\n\t}\n\treturn sz_s2u_compute(size);\n}\n\n/*\n * Compute usable size that would result from allocating an object with the\n * specified size and alignment.\n */\nJEMALLOC_ALWAYS_INLINE size_t\nsz_sa2u(size_t size, size_t alignment) {\n\tsize_t usize;\n\n\tassert(alignment != 0 && ((alignment - 1) & alignment) == 0);\n\n\t/* Try for a small size class. */\n\tif (size <= SMALL_MAXCLASS && alignment < PAGE) {\n\t\t/*\n\t\t * Round size up to the nearest multiple of alignment.\n\t\t *\n\t\t * This done, we can take advantage of the fact that for each\n\t\t * small size class, every object is aligned at the smallest\n\t\t * power of two that is non-zero in the base two representation\n\t\t * of the size.  For example:\n\t\t *\n\t\t *   Size |   Base 2 | Minimum alignment\n\t\t *   -----+----------+------------------\n\t\t *     96 |  1100000 |  32\n\t\t *    144 | 10100000 |  32\n\t\t *    192 | 11000000 |  64\n\t\t */\n\t\tusize = sz_s2u(ALIGNMENT_CEILING(size, alignment));\n\t\tif (usize < LARGE_MINCLASS) {\n\t\t\treturn usize;\n\t\t}\n\t}\n\n\t/* Large size class.  Beware of overflow. */\n\n\tif (unlikely(alignment > LARGE_MAXCLASS)) {\n\t\treturn 0;\n\t}\n\n\t/* Make sure result is a large size class. */\n\tif (size <= LARGE_MINCLASS) {\n\t\tusize = LARGE_MINCLASS;\n\t} else {\n\t\tusize = sz_s2u(size);\n\t\tif (usize < size) {\n\t\t\t/* size_t overflow. */\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/*\n\t * Calculate the multi-page mapping that large_palloc() would need in\n\t * order to guarantee the alignment.\n\t */\n\tif (usize + sz_large_pad + PAGE_CEILING(alignment) - PAGE < usize) {\n\t\t/* size_t overflow. */\n\t\treturn 0;\n\t}\n\treturn usize;\n}\n\n#endif /* JEMALLOC_INTERNAL_SIZE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_EXTERNS_H\n#define JEMALLOC_INTERNAL_TCACHE_EXTERNS_H\n\n#include \"jemalloc/internal/size_classes.h\"\n\nextern bool\topt_tcache;\nextern ssize_t\topt_lg_tcache_max;\n\nextern cache_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 unsigned\tnhbins;\n\n/* Maximum cached size class. */\nextern size_t\ttcache_maxclass;\n\n/*\n * Explicit tcaches, managed via the tcache.{create,flush,destroy} mallctls and\n * usable via the MALLOCX_TCACHE() flag.  The automatic per thread tcaches are\n * completely disjoint from this data structure.  tcaches starts off as a sparse\n * array, so it has no physical memory footprint until individual pages are\n * touched.  This allows the entire array to be allocated the first time an\n * explicit tcache is created without a disproportionate impact on memory usage.\n */\nextern tcaches_t\t*tcaches;\n\nsize_t\ttcache_salloc(tsdn_t *tsdn, const void *ptr);\nvoid\ttcache_event_hard(tsd_t *tsd, tcache_t *tcache);\nvoid\t*tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, bool *tcache_success);\nvoid\ttcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,\n    szind_t binind, unsigned rem);\nvoid\ttcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,\n    unsigned rem, tcache_t *tcache);\nvoid\ttcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache,\n    arena_t *arena);\ntcache_t *tcache_create_explicit(tsd_t *tsd);\nvoid\ttcache_cleanup(tsd_t *tsd);\nvoid\ttcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);\nbool\ttcaches_create(tsd_t *tsd, unsigned *r_ind);\nvoid\ttcaches_flush(tsd_t *tsd, unsigned ind);\nvoid\ttcaches_destroy(tsd_t *tsd, unsigned ind);\nbool\ttcache_boot(tsdn_t *tsdn);\nvoid tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);\nvoid tcache_prefork(tsdn_t *tsdn);\nvoid tcache_postfork_parent(tsdn_t *tsdn);\nvoid tcache_postfork_child(tsdn_t *tsdn);\nvoid tcache_flush(tsd_t *tsd);\nbool tsd_tcache_data_init(tsd_t *tsd);\nbool tsd_tcache_enabled_data_init(tsd_t *tsd);\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_EXTERNS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_INLINES_H\n#define JEMALLOC_INTERNAL_TCACHE_INLINES_H\n\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/ticker.h\"\n#include \"jemalloc/internal/util.h\"\n\nstatic inline bool\ntcache_enabled_get(tsd_t *tsd) {\n\treturn tsd_tcache_enabled_get(tsd);\n}\n\nstatic inline void\ntcache_enabled_set(tsd_t *tsd, bool enabled) {\n\tbool was_enabled = tsd_tcache_enabled_get(tsd);\n\n\tif (!was_enabled && enabled) {\n\t\ttsd_tcache_data_init(tsd);\n\t} else if (was_enabled && !enabled) {\n\t\ttcache_cleanup(tsd);\n\t}\n\t/* Commit the state last.  Above calls check current state. */\n\ttsd_tcache_enabled_set(tsd, enabled);\n\ttsd_slow_update(tsd);\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntcache_event(tsd_t *tsd, tcache_t *tcache) {\n\tif (TCACHE_GC_INCR == 0) {\n\t\treturn;\n\t}\n\n\tif (unlikely(ticker_tick(&tcache->gc_ticker))) {\n\t\ttcache_event_hard(tsd, tcache);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void *\ntcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,\n    UNUSED size_t size, szind_t binind, bool zero, bool slow_path) {\n\tvoid *ret;\n\tcache_bin_t *bin;\n\tbool tcache_success;\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\n\tassert(binind < NBINS);\n\tbin = tcache_small_bin_get(tcache, binind);\n\tret = cache_bin_alloc_easy(bin, &tcache_success);\n\tassert(tcache_success == (ret != NULL));\n\tif (unlikely(!tcache_success)) {\n\t\tbool tcache_hard_success;\n\t\tarena = arena_choose(tsd, arena);\n\t\tif (unlikely(arena == NULL)) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\tret = tcache_alloc_small_hard(tsd_tsdn(tsd), arena, tcache,\n\t\t    bin, binind, &tcache_hard_success);\n\t\tif (tcache_hard_success == false) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tassert(ret);\n\t/*\n\t * Only compute usize if required.  The checks in the following if\n\t * statement are all static.\n\t */\n\tif (config_prof || (slow_path && config_fill) || unlikely(zero)) {\n\t\tusize = sz_index2size(binind);\n\t\tassert(tcache_salloc(tsd_tsdn(tsd), ret) == usize);\n\t}\n\n\tif (likely(!zero)) {\n\t\tif (slow_path && config_fill) {\n\t\t\tif (unlikely(opt_junk_alloc)) {\n\t\t\t\tarena_alloc_junk_small(ret, &bin_infos[binind],\n\t\t\t\t    false);\n\t\t\t} else if (unlikely(opt_zero)) {\n\t\t\t\tmemset(ret, 0, usize);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (slow_path && config_fill && unlikely(opt_junk_alloc)) {\n\t\t\tarena_alloc_junk_small(ret, &bin_infos[binind], true);\n\t\t}\n\t\tmemset(ret, 0, usize);\n\t}\n\n\tif (config_stats) {\n\t\tbin->tstats.nrequests++;\n\t}\n\tif (config_prof) {\n\t\ttcache->prof_accumbytes += usize;\n\t}\n\ttcache_event(tsd, tcache);\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\ntcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,\n    szind_t binind, bool zero, bool slow_path) {\n\tvoid *ret;\n\tcache_bin_t *bin;\n\tbool tcache_success;\n\n\tassert(binind >= NBINS &&binind < nhbins);\n\tbin = tcache_large_bin_get(tcache, binind);\n\tret = cache_bin_alloc_easy(bin, &tcache_success);\n\tassert(tcache_success == (ret != NULL));\n\tif (unlikely(!tcache_success)) {\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\tarena = arena_choose(tsd, arena);\n\t\tif (unlikely(arena == NULL)) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\tret = large_malloc(tsd_tsdn(tsd), arena, sz_s2u(size), zero);\n\t\tif (ret == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t} else {\n\t\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\n\t\t/* Only compute usize on demand */\n\t\tif (config_prof || (slow_path && config_fill) ||\n\t\t    unlikely(zero)) {\n\t\t\tusize = sz_index2size(binind);\n\t\t\tassert(usize <= tcache_maxclass);\n\t\t}\n\n\t\tif (likely(!zero)) {\n\t\t\tif (slow_path && config_fill) {\n\t\t\t\tif (unlikely(opt_junk_alloc)) {\n\t\t\t\t\tmemset(ret, JEMALLOC_ALLOC_JUNK,\n\t\t\t\t\t    usize);\n\t\t\t\t} else if (unlikely(opt_zero)) {\n\t\t\t\t\tmemset(ret, 0, usize);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tmemset(ret, 0, usize);\n\t\t}\n\n\t\tif (config_stats) {\n\t\t\tbin->tstats.nrequests++;\n\t\t}\n\t\tif (config_prof) {\n\t\t\ttcache->prof_accumbytes += usize;\n\t\t}\n\t}\n\n\ttcache_event(tsd, tcache);\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,\n    bool slow_path) {\n\tcache_bin_t *bin;\n\tcache_bin_info_t *bin_info;\n\n\tassert(tcache_salloc(tsd_tsdn(tsd), ptr) <= SMALL_MAXCLASS);\n\n\tif (slow_path && config_fill && unlikely(opt_junk_free)) {\n\t\tarena_dalloc_junk_small(ptr, &bin_infos[binind]);\n\t}\n\n\tbin = tcache_small_bin_get(tcache, binind);\n\tbin_info = &tcache_bin_info[binind];\n\tif (unlikely(bin->ncached == bin_info->ncached_max)) {\n\t\ttcache_bin_flush_small(tsd, tcache, bin, binind,\n\t\t    (bin_info->ncached_max >> 1));\n\t}\n\tassert(bin->ncached < bin_info->ncached_max);\n\tbin->ncached++;\n\t*(bin->avail - bin->ncached) = ptr;\n\n\ttcache_event(tsd, tcache);\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,\n    bool slow_path) {\n\tcache_bin_t *bin;\n\tcache_bin_info_t *bin_info;\n\n\tassert(tcache_salloc(tsd_tsdn(tsd), ptr) > SMALL_MAXCLASS);\n\tassert(tcache_salloc(tsd_tsdn(tsd), ptr) <= tcache_maxclass);\n\n\tif (slow_path && config_fill && unlikely(opt_junk_free)) {\n\t\tlarge_dalloc_junk(ptr, sz_index2size(binind));\n\t}\n\n\tbin = tcache_large_bin_get(tcache, binind);\n\tbin_info = &tcache_bin_info[binind];\n\tif (unlikely(bin->ncached == bin_info->ncached_max)) {\n\t\ttcache_bin_flush_large(tsd, bin, binind,\n\t\t    (bin_info->ncached_max >> 1), tcache);\n\t}\n\tassert(bin->ncached < bin_info->ncached_max);\n\tbin->ncached++;\n\t*(bin->avail - bin->ncached) = ptr;\n\n\ttcache_event(tsd, tcache);\n}\n\nJEMALLOC_ALWAYS_INLINE tcache_t *\ntcaches_get(tsd_t *tsd, unsigned ind) {\n\ttcaches_t *elm = &tcaches[ind];\n\tif (unlikely(elm->tcache == NULL)) {\n\t\telm->tcache = tcache_create_explicit(tsd);\n\t}\n\treturn elm->tcache;\n}\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_INLINES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_STRUCTS_H\n#define JEMALLOC_INTERNAL_TCACHE_STRUCTS_H\n\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/cache_bin.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nstruct tcache_s {\n\t/*\n\t * To minimize our cache-footprint, we put the frequently accessed data\n\t * together at the start of this struct.\n\t */\n\n\t/* Cleared after arena_prof_accum(). */\n\tuint64_t\tprof_accumbytes;\n\t/* Drives incremental GC. */\n\tticker_t\tgc_ticker;\n\t/*\n\t * The pointer stacks associated with bins follow as a contiguous array.\n\t * During tcache initialization, the avail pointer in each element of\n\t * tbins is initialized to point to the proper offset within this array.\n\t */\n\tcache_bin_t\tbins_small[NBINS];\n\n\t/*\n\t * This data is less hot; we can be a little less careful with our\n\t * footprint here.\n\t */\n\t/* Lets us track all the tcaches in an arena. */\n\tql_elm(tcache_t) link;\n\t/*\n\t * The descriptor lets the arena find our cache bins without seeing the\n\t * tcache definition.  This enables arenas to aggregate stats across\n\t * tcaches without having a tcache dependency.\n\t */\n\tcache_bin_array_descriptor_t cache_bin_array_descriptor;\n\n\t/* The arena this tcache is associated with. */\n\tarena_t\t\t*arena;\n\t/* Next bin to GC. */\n\tszind_t\t\tnext_gc_bin;\n\t/* For small bins, fill (ncached_max >> lg_fill_div). */\n\tuint8_t\t\tlg_fill_div[NBINS];\n\t/*\n\t * We put the cache bins for large size classes at the end of the\n\t * struct, since some of them might not get used.  This might end up\n\t * letting us avoid touching an extra page if we don't have to.\n\t */\n\tcache_bin_t\tbins_large[NSIZES-NBINS];\n};\n\n/* Linkage for list of available (previously used) explicit tcache IDs. */\nstruct tcaches_s {\n\tunion {\n\t\ttcache_t\t*tcache;\n\t\ttcaches_t\t*next;\n\t};\n};\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_STRUCTS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_TYPES_H\n#define JEMALLOC_INTERNAL_TCACHE_TYPES_H\n\n#include \"jemalloc/internal/size_classes.h\"\n\ntypedef struct tcache_s tcache_t;\ntypedef struct tcaches_s tcaches_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 TCACHE_STATE_DISABLED\t\t((tcache_t *)(uintptr_t)1)\n#define TCACHE_STATE_REINCARNATED\t((tcache_t *)(uintptr_t)2)\n#define TCACHE_STATE_PURGATORY\t\t((tcache_t *)(uintptr_t)3)\n#define TCACHE_STATE_MAX\t\tTCACHE_STATE_PURGATORY\n\n/*\n * Absolute minimum number of cache slots for each small bin.\n */\n#define TCACHE_NSLOTS_SMALL_MIN\t\t20\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 slab for this size class.\n *\n * This constant must be an even number.\n */\n#define TCACHE_NSLOTS_SMALL_MAX\t\t200\n\n/* Number of cache slots for large size classes. */\n#define TCACHE_NSLOTS_LARGE\t\t20\n\n/* (1U << opt_lg_tcache_max) is used to compute tcache_maxclass. */\n#define LG_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 TCACHE_GC_SWEEP\t\t\t8192\n\n/* Number of tcache allocation/deallocation events between incremental GCs. */\n#define TCACHE_GC_INCR\t\t\t\t\t\t\t\\\n    ((TCACHE_GC_SWEEP / NBINS) + ((TCACHE_GC_SWEEP / NBINS == 0) ? 0 : 1))\n\n/* Used in TSD static initializer only. Real init in tcache_data_init(). */\n#define TCACHE_ZERO_INITIALIZER {0}\n\n/* Used in TSD static initializer only. Will be initialized to opt_tcache. */\n#define TCACHE_ENABLED_ZERO_INITIALIZER false\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ticker.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TICKER_H\n#define JEMALLOC_INTERNAL_TICKER_H\n\n#include \"jemalloc/internal/util.h\"\n\n/**\n * A ticker makes it easy to count-down events until some limit.  You\n * ticker_init the ticker to trigger every nticks events.  You then notify it\n * that an event has occurred with calls to ticker_tick (or that nticks events\n * have occurred with a call to ticker_ticks), which will return true (and reset\n * the counter) if the countdown hit zero.\n */\n\ntypedef struct {\n\tint32_t tick;\n\tint32_t nticks;\n} ticker_t;\n\nstatic inline void\nticker_init(ticker_t *ticker, int32_t nticks) {\n\tticker->tick = nticks;\n\tticker->nticks = nticks;\n}\n\nstatic inline void\nticker_copy(ticker_t *ticker, const ticker_t *other) {\n\t*ticker = *other;\n}\n\nstatic inline int32_t\nticker_read(const ticker_t *ticker) {\n\treturn ticker->tick;\n}\n\n/*\n * Not intended to be a public API.  Unfortunately, on x86, neither gcc nor\n * clang seems smart enough to turn\n *   ticker->tick -= nticks;\n *   if (unlikely(ticker->tick < 0)) {\n *     fixup ticker\n *     return true;\n *   }\n *   return false;\n * into\n *   subq %nticks_reg, (%ticker_reg)\n *   js fixup ticker\n *\n * unless we force \"fixup ticker\" out of line.  In that case, gcc gets it right,\n * but clang now does worse than before.  So, on x86 with gcc, we force it out\n * of line, but otherwise let the inlining occur.  Ordinarily this wouldn't be\n * worth the hassle, but this is on the fast path of both malloc and free (via\n * tcache_event).\n */\n#if defined(__GNUC__) && !defined(__clang__)\t\t\t\t\\\n    && (defined(__x86_64__) || defined(__i386__))\nJEMALLOC_NOINLINE\n#endif\nstatic bool\nticker_fixup(ticker_t *ticker) {\n\tticker->tick = ticker->nticks;\n\treturn true;\n}\n\nstatic inline bool\nticker_ticks(ticker_t *ticker, int32_t nticks) {\n\tticker->tick -= nticks;\n\tif (unlikely(ticker->tick < 0)) {\n\t\treturn ticker_fixup(ticker);\n\t}\n\treturn false;\n}\n\nstatic inline bool\nticker_tick(ticker_t *ticker) {\n\treturn ticker_ticks(ticker, 1);\n}\n\n#endif /* JEMALLOC_INTERNAL_TICKER_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TSD_H\n#define JEMALLOC_INTERNAL_TSD_H\n\n#include \"jemalloc/internal/arena_types.h\"\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/jemalloc_internal_externs.h\"\n#include \"jemalloc/internal/prof_types.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/rtree_tsd.h\"\n#include \"jemalloc/internal/tcache_types.h\"\n#include \"jemalloc/internal/tcache_structs.h\"\n#include \"jemalloc/internal/util.h\"\n#include \"jemalloc/internal/witness.h\"\n\n/*\n * Thread-Specific-Data layout\n * --- data accessed on tcache fast path: state, rtree_ctx, stats, prof ---\n * s: state\n * e: tcache_enabled\n * m: thread_allocated (config_stats)\n * f: thread_deallocated (config_stats)\n * p: prof_tdata (config_prof)\n * c: rtree_ctx (rtree cache accessed on deallocation)\n * t: tcache\n * --- data not accessed on tcache fast path: arena-related fields ---\n * d: arenas_tdata_bypass\n * r: reentrancy_level\n * x: narenas_tdata\n * i: iarena\n * a: arena\n * o: arenas_tdata\n * Loading TSD data is on the critical path of basically all malloc operations.\n * In particular, tcache and rtree_ctx rely on hot CPU cache to be effective.\n * Use a compact layout to reduce cache footprint.\n * +--- 64-bit and 64B cacheline; 1B each letter; First byte on the left. ---+\n * |----------------------------  1st cacheline  ----------------------------|\n * | sedrxxxx mmmmmmmm ffffffff pppppppp [c * 32  ........ ........ .......] |\n * |----------------------------  2nd cacheline  ----------------------------|\n * | [c * 64  ........ ........ ........ ........ ........ ........ .......] |\n * |----------------------------  3nd cacheline  ----------------------------|\n * | [c * 32  ........ ........ .......] iiiiiiii aaaaaaaa oooooooo [t...... |\n * +-------------------------------------------------------------------------+\n * Note: the entire tcache is embedded into TSD and spans multiple cachelines.\n *\n * The last 3 members (i, a and o) before tcache isn't really needed on tcache\n * fast path.  However we have a number of unused tcache bins and witnesses\n * (never touched unless config_debug) at the end of tcache, so we place them\n * there to avoid breaking the cachelines and possibly paging in an extra page.\n */\n#ifdef JEMALLOC_JET\ntypedef void (*test_callback_t)(int *);\n#  define MALLOC_TSD_TEST_DATA_INIT 0x72b65c10\n#  define MALLOC_TEST_TSD \\\n    O(test_data,\t\tint,\t\t\tint)\t\t\\\n    O(test_callback,\t\ttest_callback_t,\tint)\n#  define MALLOC_TEST_TSD_INITIALIZER , MALLOC_TSD_TEST_DATA_INIT, NULL\n#else\n#  define MALLOC_TEST_TSD\n#  define MALLOC_TEST_TSD_INITIALIZER\n#endif\n\n/*  O(name,\t\t\ttype,\t\t\tnullable type */\n#define MALLOC_TSD\t\t\t\t\t\t\t\\\n    O(tcache_enabled,\t\tbool,\t\t\tbool)\t\t\\\n    O(arenas_tdata_bypass,\tbool,\t\t\tbool)\t\t\\\n    O(reentrancy_level,\t\tint8_t,\t\t\tint8_t)\t\t\\\n    O(narenas_tdata,\t\tuint32_t,\t\tuint32_t)\t\\\n    O(offset_state,\t\tuint64_t,\t\tuint64_t)\t\\\n    O(thread_allocated,\t\tuint64_t,\t\tuint64_t)\t\\\n    O(thread_deallocated,\tuint64_t,\t\tuint64_t)\t\\\n    O(prof_tdata,\t\tprof_tdata_t *,\t\tprof_tdata_t *)\t\\\n    O(rtree_ctx,\t\trtree_ctx_t,\t\trtree_ctx_t)\t\\\n    O(iarena,\t\t\tarena_t *,\t\tarena_t *)\t\\\n    O(arena,\t\t\tarena_t *,\t\tarena_t *)\t\\\n    O(arenas_tdata,\t\tarena_tdata_t *,\tarena_tdata_t *)\\\n    O(tcache,\t\t\ttcache_t,\t\ttcache_t)\t\\\n    O(witness_tsd,              witness_tsd_t,\t\twitness_tsdn_t)\t\\\n    MALLOC_TEST_TSD\n\n#define TSD_INITIALIZER {\t\t\t\t\t\t\\\n    tsd_state_uninitialized,\t\t\t\t\t\t\\\n    TCACHE_ENABLED_ZERO_INITIALIZER,\t\t\t\t\t\\\n    false,\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    RTREE_CTX_ZERO_INITIALIZER,\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    TCACHE_ZERO_INITIALIZER,\t\t\t\t\t\t\\\n    WITNESS_TSD_INITIALIZER\t\t\t\t\t\t\\\n    MALLOC_TEST_TSD_INITIALIZER\t\t\t\t\t\t\\\n}\n\nenum {\n\ttsd_state_nominal = 0, /* Common case --> jnz. */\n\ttsd_state_nominal_slow = 1, /* Initialized but on slow path. */\n\t/* the above 2 nominal states should be lower values. */\n\ttsd_state_nominal_max = 1, /* used for comparison only. */\n\ttsd_state_minimal_initialized = 2,\n\ttsd_state_purgatory = 3,\n\ttsd_state_reincarnated = 4,\n\ttsd_state_uninitialized = 5\n};\n\n/* Manually limit tsd_state_t to a single byte. */\ntypedef uint8_t tsd_state_t;\n\n/* The actual tsd. */\nstruct tsd_s {\n\t/*\n\t * The contents should be treated as totally opaque outside the tsd\n\t * module.  Access any thread-local state through the getters and\n\t * setters below.\n\t */\n\ttsd_state_t\tstate;\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\n\tt use_a_getter_or_setter_instead_##n;\nMALLOC_TSD\n#undef O\n};\n\n/*\n * Wrapper around tsd_t that makes it possible to avoid implicit conversion\n * between tsd_t and tsdn_t, where tsdn_t is \"nullable\" and has to be\n * explicitly converted to tsd_t, which is non-nullable.\n */\nstruct tsdn_s {\n\ttsd_t tsd;\n};\n#define TSDN_NULL ((tsdn_t *)0)\nJEMALLOC_ALWAYS_INLINE tsdn_t *\ntsd_tsdn(tsd_t *tsd) {\n\treturn (tsdn_t *)tsd;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsdn_null(const tsdn_t *tsdn) {\n\treturn tsdn == NULL;\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsdn_tsd(tsdn_t *tsdn) {\n\tassert(!tsdn_null(tsdn));\n\n\treturn &tsdn->tsd;\n}\n\nvoid *malloc_tsd_malloc(size_t size);\nvoid malloc_tsd_dalloc(void *wrapper);\nvoid malloc_tsd_cleanup_register(bool (*f)(void));\ntsd_t *malloc_tsd_boot0(void);\nvoid malloc_tsd_boot1(void);\nvoid tsd_cleanup(void *arg);\ntsd_t *tsd_fetch_slow(tsd_t *tsd, bool internal);\nvoid tsd_slow_update(tsd_t *tsd);\n\n/*\n * We put the platform-specific data declarations and inlines into their own\n * header files to avoid cluttering this file.  They define tsd_boot0,\n * tsd_boot1, tsd_boot, tsd_booted_get, tsd_get_allocates, tsd_get, and tsd_set.\n */\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n#include \"jemalloc/internal/tsd_malloc_thread_cleanup.h\"\n#elif (defined(JEMALLOC_TLS))\n#include \"jemalloc/internal/tsd_tls.h\"\n#elif (defined(_WIN32))\n#include \"jemalloc/internal/tsd_win.h\"\n#else\n#include \"jemalloc/internal/tsd_generic.h\"\n#endif\n\n/*\n * tsd_foop_get_unsafe(tsd) returns a pointer to the thread-local instance of\n * foo.  This omits some safety checks, and so can be used during tsd\n * initialization and cleanup.\n */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE t *\t\t\t\t\t\t\\\ntsd_##n##p_get_unsafe(tsd_t *tsd) {\t\t\t\t\t\\\n\treturn &tsd->use_a_getter_or_setter_instead_##n;\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/* tsd_foop_get(tsd) returns a pointer to the thread-local instance of foo. */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE t *\t\t\t\t\t\t\\\ntsd_##n##p_get(tsd_t *tsd) {\t\t\t\t\t\t\\\n\tassert(tsd->state == tsd_state_nominal ||\t\t\t\\\n\t    tsd->state == tsd_state_nominal_slow ||\t\t\t\\\n\t    tsd->state == tsd_state_reincarnated ||\t\t\t\\\n\t    tsd->state == tsd_state_minimal_initialized);\t\t\\\n\treturn tsd_##n##p_get_unsafe(tsd);\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/*\n * tsdn_foop_get(tsdn) returns either the thread-local instance of foo (if tsdn\n * isn't NULL), or NULL (if tsdn is NULL), cast to the nullable pointer type.\n */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE nt *\t\t\t\t\t\t\\\ntsdn_##n##p_get(tsdn_t *tsdn) {\t\t\t\t\t\t\\\n\tif (tsdn_null(tsdn)) {\t\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ttsd_t *tsd = tsdn_tsd(tsdn);\t\t\t\t\t\\\n\treturn (nt *)tsd_##n##p_get(tsd);\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/* tsd_foo_get(tsd) returns the value of the thread-local instance of foo. */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE t\t\t\t\t\t\t\\\ntsd_##n##_get(tsd_t *tsd) {\t\t\t\t\t\t\\\n\treturn *tsd_##n##p_get(tsd);\t\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/* tsd_foo_set(tsd, val) updates the thread-local instance of foo to be val. */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE void\t\t\t\t\t\t\\\ntsd_##n##_set(tsd_t *tsd, t val) {\t\t\t\t\t\\\n\tassert(tsd->state != tsd_state_reincarnated &&\t\t\t\\\n\t    tsd->state != tsd_state_minimal_initialized);\t\t\\\n\t*tsd_##n##p_get(tsd) = val;\t\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_assert_fast(tsd_t *tsd) {\n\tassert(!malloc_slow && tsd_tcache_enabled_get(tsd) &&\n\t    tsd_reentrancy_level_get(tsd) == 0);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_fast(tsd_t *tsd) {\n\tbool fast = (tsd->state == tsd_state_nominal);\n\tif (fast) {\n\t\ttsd_assert_fast(tsd);\n\t}\n\n\treturn fast;\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_fetch_impl(bool init, bool minimal) {\n\ttsd_t *tsd = tsd_get(init);\n\n\tif (!init && tsd_get_allocates() && tsd == NULL) {\n\t\treturn NULL;\n\t}\n\tassert(tsd != NULL);\n\n\tif (unlikely(tsd->state != tsd_state_nominal)) {\n\t\treturn tsd_fetch_slow(tsd, minimal);\n\t}\n\tassert(tsd_fast(tsd));\n\ttsd_assert_fast(tsd);\n\n\treturn tsd;\n}\n\n/* Get a minimal TSD that requires no cleanup.  See comments in free(). */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_fetch_min(void) {\n\treturn tsd_fetch_impl(true, true);\n}\n\n/* For internal background threads use only. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_internal_fetch(void) {\n\ttsd_t *tsd = tsd_fetch_min();\n\t/* Use reincarnated state to prevent full initialization. */\n\ttsd->state = tsd_state_reincarnated;\n\n\treturn tsd;\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_fetch(void) {\n\treturn tsd_fetch_impl(true, false);\n}\n\nstatic inline bool\ntsd_nominal(tsd_t *tsd) {\n\treturn (tsd->state <= tsd_state_nominal_max);\n}\n\nJEMALLOC_ALWAYS_INLINE tsdn_t *\ntsdn_fetch(void) {\n\tif (!tsd_booted_get()) {\n\t\treturn NULL;\n\t}\n\n\treturn tsd_tsdn(tsd_fetch_impl(false, false));\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_ctx_t *\ntsd_rtree_ctx(tsd_t *tsd) {\n\treturn tsd_rtree_ctxp_get(tsd);\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_ctx_t *\ntsdn_rtree_ctx(tsdn_t *tsdn, rtree_ctx_t *fallback) {\n\t/*\n\t * If tsd cannot be accessed, initialize the fallback rtree_ctx and\n\t * return a pointer to it.\n\t */\n\tif (unlikely(tsdn_null(tsdn))) {\n\t\trtree_ctx_data_init(fallback);\n\t\treturn fallback;\n\t}\n\treturn tsd_rtree_ctx(tsdn_tsd(tsdn));\n}\n\n#endif /* JEMALLOC_INTERNAL_TSD_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_generic.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_GENERIC_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_GENERIC_H\n\ntypedef struct tsd_init_block_s tsd_init_block_t;\nstruct tsd_init_block_s {\n\tql_elm(tsd_init_block_t) link;\n\tpthread_t thread;\n\tvoid *data;\n};\n\n/* Defined in tsd.c, to allow the mutex headers to have tsd dependencies. */\ntypedef struct tsd_init_head_s tsd_init_head_t;\n\ntypedef struct {\n\tbool initialized;\n\ttsd_t val;\n} tsd_wrapper_t;\n\nvoid *tsd_init_check_recursion(tsd_init_head_t *head,\n    tsd_init_block_t *block);\nvoid tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block);\n\nextern pthread_key_t tsd_tsd;\nextern tsd_init_head_t tsd_init_head;\nextern tsd_wrapper_t tsd_boot_wrapper;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE void\ntsd_cleanup_wrapper(void *arg) {\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *)arg;\n\n\tif (wrapper->initialized) {\n\t\twrapper->initialized = false;\n\t\ttsd_cleanup(&wrapper->val);\n\t\tif (wrapper->initialized) {\n\t\t\t/* Trigger another cleanup round. */\n\t\t\tif (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0)\n\t\t\t{\n\t\t\t\tmalloc_write(\"<jemalloc>: Error setting TSD\\n\");\n\t\t\t\tif (opt_abort) {\n\t\t\t\t\tabort();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\tmalloc_tsd_dalloc(wrapper);\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_wrapper_set(tsd_wrapper_t *wrapper) {\n\tif (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0) {\n\t\tmalloc_write(\"<jemalloc>: Error setting TSD\\n\");\n\t\tabort();\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_wrapper_t *\ntsd_wrapper_get(bool init) {\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *)pthread_getspecific(tsd_tsd);\n\n\tif (init && unlikely(wrapper == NULL)) {\n\t\ttsd_init_block_t block;\n\t\twrapper = (tsd_wrapper_t *)\n\t\t    tsd_init_check_recursion(&tsd_init_head, &block);\n\t\tif (wrapper) {\n\t\t\treturn wrapper;\n\t\t}\n\t\twrapper = (tsd_wrapper_t *)\n\t\t    malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\t\tblock.data = (void *)wrapper;\n\t\tif (wrapper == NULL) {\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\t\tabort();\n\t\t} else {\n\t\t\twrapper->initialized = false;\n\t\t\ttsd_t initializer = TSD_INITIALIZER;\n\t\t\twrapper->val = initializer;\n\t\t}\n\t\ttsd_wrapper_set(wrapper);\n\t\ttsd_init_finish(&tsd_init_head, &block);\n\t}\n\treturn wrapper;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\tif (pthread_key_create(&tsd_tsd, tsd_cleanup_wrapper) != 0) {\n\t\treturn true;\n\t}\n\ttsd_wrapper_set(&tsd_boot_wrapper);\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\ttsd_wrapper_t *wrapper;\n\twrapper = (tsd_wrapper_t *)malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\tif (wrapper == NULL) {\n\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\tabort();\n\t}\n\ttsd_boot_wrapper.initialized = false;\n\ttsd_cleanup(&tsd_boot_wrapper.val);\n\twrapper->initialized = false;\n\ttsd_t initializer = TSD_INITIALIZER;\n\twrapper->val = initializer;\n\ttsd_wrapper_set(wrapper);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\tif (tsd_boot0()) {\n\t\treturn true;\n\t}\n\ttsd_boot1();\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn true;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(bool init) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(init);\n\tif (tsd_get_allocates() && !init && wrapper == NULL) {\n\t\treturn NULL;\n\t}\n\treturn &wrapper->val;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(true);\n\tif (likely(&wrapper->val != val)) {\n\t\twrapper->val = *(val);\n\t}\n\twrapper->initialized = true;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H\n\nextern __thread tsd_t tsd_tls;\nextern __thread bool tsd_initialized;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE bool\ntsd_cleanup_wrapper(void) {\n\tif (tsd_initialized) {\n\t\ttsd_initialized = false;\n\t\ttsd_cleanup(&tsd_tls);\n\t}\n\treturn tsd_initialized;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\tmalloc_tsd_cleanup_register(&tsd_cleanup_wrapper);\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\t/* Do nothing. */\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\treturn tsd_boot0();\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn false;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(bool init) {\n\tassert(tsd_booted);\n\treturn &tsd_tls;\n}\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\tassert(tsd_booted);\n\tif (likely(&tsd_tls != val)) {\n\t\ttsd_tls = (*val);\n\t}\n\ttsd_initialized = true;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_tls.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_TLS_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_TLS_H\n\nextern __thread tsd_t tsd_tls;\nextern pthread_key_t tsd_tsd;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\tif (pthread_key_create(&tsd_tsd, &tsd_cleanup) != 0) {\n\t\treturn true;\n\t}\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\t/* Do nothing. */\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\treturn tsd_boot0();\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn false;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(UNUSED bool init) {\n\tassert(tsd_booted);\n\treturn &tsd_tls;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\tassert(tsd_booted);\n\tif (likely(&tsd_tls != val)) {\n\t\ttsd_tls = (*val);\n\t}\n\tif (pthread_setspecific(tsd_tsd, (void *)(&tsd_tls)) != 0) {\n\t\tmalloc_write(\"<jemalloc>: Error setting tsd.\\n\");\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TSD_TYPES_H\n#define JEMALLOC_INTERNAL_TSD_TYPES_H\n\n#define MALLOC_TSD_CLEANUPS_MAX\t2\n\ntypedef struct tsd_s tsd_t;\ntypedef struct tsdn_s tsdn_t;\ntypedef bool (*malloc_tsd_cleanup_t)(void);\n\n#endif /* JEMALLOC_INTERNAL_TSD_TYPES_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_win.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_WIN_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_WIN_H\n\ntypedef struct {\n\tbool initialized;\n\ttsd_t val;\n} tsd_wrapper_t;\n\nextern DWORD tsd_tsd;\nextern tsd_wrapper_t tsd_boot_wrapper;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE bool\ntsd_cleanup_wrapper(void) {\n\tDWORD error = GetLastError();\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *)TlsGetValue(tsd_tsd);\n\tSetLastError(error);\n\n\tif (wrapper == NULL) {\n\t\treturn false;\n\t}\n\n\tif (wrapper->initialized) {\n\t\twrapper->initialized = false;\n\t\ttsd_cleanup(&wrapper->val);\n\t\tif (wrapper->initialized) {\n\t\t\t/* Trigger another cleanup round. */\n\t\t\treturn true;\n\t\t}\n\t}\n\tmalloc_tsd_dalloc(wrapper);\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_wrapper_set(tsd_wrapper_t *wrapper) {\n\tif (!TlsSetValue(tsd_tsd, (void *)wrapper)) {\n\t\tmalloc_write(\"<jemalloc>: Error setting TSD\\n\");\n\t\tabort();\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_wrapper_t *\ntsd_wrapper_get(bool init) {\n\tDWORD error = GetLastError();\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *) TlsGetValue(tsd_tsd);\n\tSetLastError(error);\n\n\tif (init && unlikely(wrapper == NULL)) {\n\t\twrapper = (tsd_wrapper_t *)\n\t\t    malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\t\tif (wrapper == NULL) {\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\t\tabort();\n\t\t} else {\n\t\t\twrapper->initialized = false;\n\t\t\t/* MSVC is finicky about aggregate initialization. */\n\t\t\ttsd_t tsd_initializer = TSD_INITIALIZER;\n\t\t\twrapper->val = tsd_initializer;\n\t\t}\n\t\ttsd_wrapper_set(wrapper);\n\t}\n\treturn wrapper;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\ttsd_tsd = TlsAlloc();\n\tif (tsd_tsd == TLS_OUT_OF_INDEXES) {\n\t\treturn true;\n\t}\n\tmalloc_tsd_cleanup_register(&tsd_cleanup_wrapper);\n\ttsd_wrapper_set(&tsd_boot_wrapper);\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\ttsd_wrapper_t *wrapper;\n\twrapper = (tsd_wrapper_t *)\n\t    malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\tif (wrapper == NULL) {\n\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\tabort();\n\t}\n\ttsd_boot_wrapper.initialized = false;\n\ttsd_cleanup(&tsd_boot_wrapper.val);\n\twrapper->initialized = false;\n\ttsd_t initializer = TSD_INITIALIZER;\n\twrapper->val = initializer;\n\ttsd_wrapper_set(wrapper);\n}\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\tif (tsd_boot0()) {\n\t\treturn true;\n\t}\n\ttsd_boot1();\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn true;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(bool init) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(init);\n\tif (tsd_get_allocates() && !init && wrapper == NULL) {\n\t\treturn NULL;\n\t}\n\treturn &wrapper->val;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(true);\n\tif (likely(&wrapper->val != val)) {\n\t\twrapper->val = *(val);\n\t}\n\twrapper->initialized = true;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/util.h",
    "content": "#ifndef JEMALLOC_INTERNAL_UTIL_H\n#define JEMALLOC_INTERNAL_UTIL_H\n\n#define UTIL_INLINE static inline\n\n/* Junk fill patterns. */\n#ifndef JEMALLOC_ALLOC_JUNK\n#  define JEMALLOC_ALLOC_JUNK\t((uint8_t)0xa5)\n#endif\n#ifndef JEMALLOC_FREE_JUNK\n#  define JEMALLOC_FREE_JUNK\t((uint8_t)0x5a)\n#endif\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_ARG_CONCAT(...) __VA_ARGS__\n\n/* cpp macro definition stringification. */\n#define STRINGIFY_HELPER(x) #x\n#define STRINGIFY(x) STRINGIFY_HELPER(x)\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#define JEMALLOC_CC_SILENCE_INIT(v) = v\n\n#ifdef __GNUC__\n#  define likely(x)   __builtin_expect(!!(x), 1)\n#  define unlikely(x) __builtin_expect(!!(x), 0)\n#else\n#  define likely(x)   !!(x)\n#  define unlikely(x) !!(x)\n#endif\n\n#if !defined(JEMALLOC_INTERNAL_UNREACHABLE)\n#  error JEMALLOC_INTERNAL_UNREACHABLE should have been defined by configure\n#endif\n\n#define unreachable() JEMALLOC_INTERNAL_UNREACHABLE()\n\n/* Set error code. */\nUTIL_INLINE void\nset_errno(int errnum) {\n#ifdef _WIN32\n\tSetLastError(errnum);\n#else\n\terrno = errnum;\n#endif\n}\n\n/* Get last error code. */\nUTIL_INLINE int\nget_errno(void) {\n#ifdef _WIN32\n\treturn GetLastError();\n#else\n\treturn errno;\n#endif\n}\n\n#undef UTIL_INLINE\n\n#endif /* JEMALLOC_INTERNAL_UTIL_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/witness.h",
    "content": "#ifndef JEMALLOC_INTERNAL_WITNESS_H\n#define JEMALLOC_INTERNAL_WITNESS_H\n\n#include \"jemalloc/internal/ql.h\"\n\n/******************************************************************************/\n/* LOCK RANKS */\n/******************************************************************************/\n\n/*\n * Witnesses with rank WITNESS_RANK_OMIT are completely ignored by the witness\n * machinery.\n */\n\n#define WITNESS_RANK_OMIT\t\t0U\n\n#define WITNESS_RANK_MIN\t\t1U\n\n#define WITNESS_RANK_INIT\t\t1U\n#define WITNESS_RANK_CTL\t\t1U\n#define WITNESS_RANK_TCACHES\t\t2U\n#define WITNESS_RANK_ARENAS\t\t3U\n\n#define WITNESS_RANK_BACKGROUND_THREAD_GLOBAL\t4U\n\n#define WITNESS_RANK_PROF_DUMP\t\t5U\n#define WITNESS_RANK_PROF_BT2GCTX\t6U\n#define WITNESS_RANK_PROF_TDATAS\t7U\n#define WITNESS_RANK_PROF_TDATA\t\t8U\n#define WITNESS_RANK_PROF_GCTX\t\t9U\n\n#define WITNESS_RANK_BACKGROUND_THREAD\t10U\n\n/*\n * Used as an argument to witness_assert_depth_to_rank() in order to validate\n * depth excluding non-core locks with lower ranks.  Since the rank argument to\n * witness_assert_depth_to_rank() is inclusive rather than exclusive, this\n * definition can have the same value as the minimally ranked core lock.\n */\n#define WITNESS_RANK_CORE\t\t11U\n\n#define WITNESS_RANK_DECAY\t\t11U\n#define WITNESS_RANK_TCACHE_QL\t\t12U\n#define WITNESS_RANK_EXTENT_GROW\t13U\n#define WITNESS_RANK_EXTENTS\t\t14U\n#define WITNESS_RANK_EXTENT_AVAIL\t15U\n\n#define WITNESS_RANK_EXTENT_POOL\t16U\n#define WITNESS_RANK_RTREE\t\t17U\n#define WITNESS_RANK_BASE\t\t18U\n#define WITNESS_RANK_ARENA_LARGE\t19U\n\n#define WITNESS_RANK_LEAF\t\t0xffffffffU\n#define WITNESS_RANK_BIN\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_ARENA_STATS\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_DSS\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_ACTIVE\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_ACCUM\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_DUMP_SEQ\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_GDUMP\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_NEXT_THR_UID\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_THREAD_ACTIVE_INIT\tWITNESS_RANK_LEAF\n\n/******************************************************************************/\n/* PER-WITNESS DATA */\n/******************************************************************************/\n#if defined(JEMALLOC_DEBUG)\n#  define WITNESS_INITIALIZER(name, rank) {name, rank, NULL, NULL, {NULL, NULL}}\n#else\n#  define WITNESS_INITIALIZER(name, rank)\n#endif\n\ntypedef struct witness_s witness_t;\ntypedef unsigned witness_rank_t;\ntypedef ql_head(witness_t) witness_list_t;\ntypedef int witness_comp_t (const witness_t *, void *, const witness_t *,\n    void *);\n\nstruct witness_s {\n\t/* Name, used for printing lock order reversal messages. */\n\tconst char\t\t*name;\n\n\t/*\n\t * Witness rank, where 0 is lowest and UINT_MAX is highest.  Witnesses\n\t * must be acquired in order of increasing rank.\n\t */\n\twitness_rank_t\t\trank;\n\n\t/*\n\t * If two witnesses are of equal rank and they have the samp comp\n\t * function pointer, it is called as a last attempt to differentiate\n\t * between witnesses of equal rank.\n\t */\n\twitness_comp_t\t\t*comp;\n\n\t/* Opaque data, passed to comp(). */\n\tvoid\t\t\t*opaque;\n\n\t/* Linkage for thread's currently owned locks. */\n\tql_elm(witness_t)\tlink;\n};\n\n/******************************************************************************/\n/* PER-THREAD DATA */\n/******************************************************************************/\ntypedef struct witness_tsd_s witness_tsd_t;\nstruct witness_tsd_s {\n\twitness_list_t witnesses;\n\tbool forking;\n};\n\n#define WITNESS_TSD_INITIALIZER { ql_head_initializer(witnesses), false }\n#define WITNESS_TSDN_NULL ((witness_tsdn_t *)0)\n\n/******************************************************************************/\n/* (PER-THREAD) NULLABILITY HELPERS */\n/******************************************************************************/\ntypedef struct witness_tsdn_s witness_tsdn_t;\nstruct witness_tsdn_s {\n\twitness_tsd_t witness_tsd;\n};\n\nJEMALLOC_ALWAYS_INLINE witness_tsdn_t *\nwitness_tsd_tsdn(witness_tsd_t *witness_tsd) {\n\treturn (witness_tsdn_t *)witness_tsd;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nwitness_tsdn_null(witness_tsdn_t *witness_tsdn) {\n\treturn witness_tsdn == NULL;\n}\n\nJEMALLOC_ALWAYS_INLINE witness_tsd_t *\nwitness_tsdn_tsd(witness_tsdn_t *witness_tsdn) {\n\tassert(!witness_tsdn_null(witness_tsdn));\n\treturn &witness_tsdn->witness_tsd;\n}\n\n/******************************************************************************/\n/* API */\n/******************************************************************************/\nvoid witness_init(witness_t *witness, const char *name, witness_rank_t rank,\n    witness_comp_t *comp, void *opaque);\n\ntypedef void (witness_lock_error_t)(const witness_list_t *, const witness_t *);\nextern witness_lock_error_t *JET_MUTABLE witness_lock_error;\n\ntypedef void (witness_owner_error_t)(const witness_t *);\nextern witness_owner_error_t *JET_MUTABLE witness_owner_error;\n\ntypedef void (witness_not_owner_error_t)(const witness_t *);\nextern witness_not_owner_error_t *JET_MUTABLE witness_not_owner_error;\n\ntypedef void (witness_depth_error_t)(const witness_list_t *,\n    witness_rank_t rank_inclusive, unsigned depth);\nextern witness_depth_error_t *JET_MUTABLE witness_depth_error;\n\nvoid witnesses_cleanup(witness_tsd_t *witness_tsd);\nvoid witness_prefork(witness_tsd_t *witness_tsd);\nvoid witness_postfork_parent(witness_tsd_t *witness_tsd);\nvoid witness_postfork_child(witness_tsd_t *witness_tsd);\n\n/* Helper, not intended for direct use. */\nstatic inline bool\nwitness_owner(witness_tsd_t *witness_tsd, const witness_t *witness) {\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tcassert(config_debug);\n\n\twitnesses = &witness_tsd->witnesses;\n\tql_foreach(w, witnesses, link) {\n\t\tif (w == witness) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic inline void\nwitness_assert_owner(witness_tsdn_t *witness_tsdn, const witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\tif (witness_owner(witness_tsd, witness)) {\n\t\treturn;\n\t}\n\twitness_owner_error(witness);\n}\n\nstatic inline void\nwitness_assert_not_owner(witness_tsdn_t *witness_tsdn,\n    const witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\twitnesses = &witness_tsd->witnesses;\n\tql_foreach(w, witnesses, link) {\n\t\tif (w == witness) {\n\t\t\twitness_not_owner_error(witness);\n\t\t}\n\t}\n}\n\nstatic inline void\nwitness_assert_depth_to_rank(witness_tsdn_t *witness_tsdn,\n    witness_rank_t rank_inclusive, unsigned depth) {\n\twitness_tsd_t *witness_tsd;\n\tunsigned d;\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\n\td = 0;\n\twitnesses = &witness_tsd->witnesses;\n\tw = ql_last(witnesses, link);\n\tif (w != NULL) {\n\t\tql_reverse_foreach(w, witnesses, link) {\n\t\t\tif (w->rank < rank_inclusive) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\td++;\n\t\t}\n\t}\n\tif (d != depth) {\n\t\twitness_depth_error(witnesses, rank_inclusive, depth);\n\t}\n}\n\nstatic inline void\nwitness_assert_depth(witness_tsdn_t *witness_tsdn, unsigned depth) {\n\twitness_assert_depth_to_rank(witness_tsdn, WITNESS_RANK_MIN, depth);\n}\n\nstatic inline void\nwitness_assert_lockless(witness_tsdn_t *witness_tsdn) {\n\twitness_assert_depth(witness_tsdn, 0);\n}\n\nstatic inline void\nwitness_lock(witness_tsdn_t *witness_tsdn, witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\twitness_assert_not_owner(witness_tsdn, witness);\n\n\twitnesses = &witness_tsd->witnesses;\n\tw = ql_last(witnesses, link);\n\tif (w == NULL) {\n\t\t/* No other locks; do nothing. */\n\t} else if (witness_tsd->forking && w->rank <= witness->rank) {\n\t\t/* Forking, and relaxed ranking satisfied. */\n\t} else if (w->rank > witness->rank) {\n\t\t/* Not forking, rank order reversal. */\n\t\twitness_lock_error(witnesses, witness);\n\t} else if (w->rank == witness->rank && (w->comp == NULL || w->comp !=\n\t    witness->comp || w->comp(w, w->opaque, witness, witness->opaque) >\n\t    0)) {\n\t\t/*\n\t\t * Missing/incompatible comparison function, or comparison\n\t\t * function indicates rank order reversal.\n\t\t */\n\t\twitness_lock_error(witnesses, witness);\n\t}\n\n\tql_elm_new(witness, link);\n\tql_tail_insert(witnesses, witness, link);\n}\n\nstatic inline void\nwitness_unlock(witness_tsdn_t *witness_tsdn, witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\twitness_list_t *witnesses;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\t/*\n\t * Check whether owner before removal, rather than relying on\n\t * witness_assert_owner() to abort, so that unit tests can test this\n\t * function's failure mode without causing undefined behavior.\n\t */\n\tif (witness_owner(witness_tsd, witness)) {\n\t\twitnesses = &witness_tsd->witnesses;\n\t\tql_remove(witnesses, witness, link);\n\t} else {\n\t\twitness_assert_owner(witness_tsdn, witness);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_WITNESS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc.sh",
    "content": "#!/bin/sh\n\nobjroot=$1\n\ncat <<EOF\n#ifndef JEMALLOC_H_\n#define JEMALLOC_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nEOF\n\nfor hdr in jemalloc_defs.h jemalloc_rename.h jemalloc_macros.h \\\n           jemalloc_protos.h jemalloc_typedefs.h jemalloc_mangle.h ; do\n  cat \"${objroot}include/jemalloc/${hdr}\" \\\n      | grep -v 'Generated from .* by configure\\.' \\\n      | sed -e 's/ $//g'\n  echo\ndone\n\ncat <<EOF\n#ifdef __cplusplus\n}\n#endif\n#endif /* JEMALLOC_H_ */\nEOF\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_defs.h.in",
    "content": "/* Defined if __attribute__((...)) syntax is supported. */\n#undef JEMALLOC_HAVE_ATTR\n\n/* Defined if alloc_size attribute is supported. */\n#undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE\n\n/* Defined if format(gnu_printf, ...) attribute is supported. */\n#undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF\n\n/* Defined if format(printf, ...) attribute is supported. */\n#undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF\n\n/*\n * Define overrides for non-standard allocator-related functions if they are\n * 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 * If defined, specify throw() for the public function prototypes when compiling\n * with C++.  The only justification for this is to match the prototypes that\n * glibc defines.\n */\n#undef JEMALLOC_USE_CXX_THROW\n\n#ifdef _MSC_VER\n#  ifdef _WIN64\n#    define LG_SIZEOF_PTR_WIN 3\n#  else\n#    define LG_SIZEOF_PTR_WIN 2\n#  endif\n#endif\n\n/* sizeof(void *) == 2^LG_SIZEOF_PTR. */\n#undef LG_SIZEOF_PTR\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_macros.h.in",
    "content": "#include <stdlib.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <limits.h>\n#include <strings.h>\n\n#define JEMALLOC_VERSION \"@jemalloc_version@\"\n#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@\n#define JEMALLOC_VERSION_MINOR @jemalloc_version_minor@\n#define JEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@\n#define JEMALLOC_VERSION_NREV @jemalloc_version_nrev@\n#define JEMALLOC_VERSION_GID \"@jemalloc_version_gid@\"\n\n#define MALLOCX_LG_ALIGN(la)\t((int)(la))\n#if LG_SIZEOF_PTR == 2\n#  define MALLOCX_ALIGN(a)\t((int)(ffs((int)(a))-1))\n#else\n#  define MALLOCX_ALIGN(a)\t\t\t\t\t\t\\\n     ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 :\t\\\n     ffs((int)(((size_t)(a))>>32))+31))\n#endif\n#define MALLOCX_ZERO\t((int)0x40)\n/*\n * Bias tcache index bits so that 0 encodes \"automatic tcache management\", and 1\n * encodes MALLOCX_TCACHE_NONE.\n */\n#define MALLOCX_TCACHE(tc)\t((int)(((tc)+2) << 8))\n#define MALLOCX_TCACHE_NONE\tMALLOCX_TCACHE(-1)\n/*\n * Bias arena index bits so that 0 encodes \"use an automatically chosen arena\".\n */\n#define MALLOCX_ARENA(a)\t((((int)(a))+1) << 20)\n\n/*\n * Use as arena index in \"arena.<i>.{purge,decay,dss}\" and\n * \"stats.arenas.<i>.*\" mallctl interfaces to select all arenas.  This\n * definition is intentionally specified in raw decimal format to support\n * cpp-based string concatenation, e.g.\n *\n *   #define STRINGIFY_HELPER(x) #x\n *   #define STRINGIFY(x) STRINGIFY_HELPER(x)\n *\n *   mallctl(\"arena.\" STRINGIFY(MALLCTL_ARENAS_ALL) \".purge\", NULL, NULL, NULL,\n *       0);\n */\n#define MALLCTL_ARENAS_ALL\t4096\n/*\n * Use as arena index in \"stats.arenas.<i>.*\" mallctl interfaces to select\n * destroyed arenas.\n */\n#define MALLCTL_ARENAS_DESTROYED\t4097\n\n#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)\n#  define JEMALLOC_CXX_THROW throw()\n#else\n#  define JEMALLOC_CXX_THROW\n#endif\n\n#if defined(_MSC_VER)\n#  define JEMALLOC_ATTR(s)\n#  define JEMALLOC_ALIGNED(s) __declspec(align(s))\n#  define JEMALLOC_ALLOC_SIZE(s)\n#  define JEMALLOC_ALLOC_SIZE2(s1, s2)\n#  ifndef JEMALLOC_EXPORT\n#    ifdef DLLEXPORT\n#      define JEMALLOC_EXPORT __declspec(dllexport)\n#    else\n#      define JEMALLOC_EXPORT __declspec(dllimport)\n#    endif\n#  endif\n#  define JEMALLOC_FORMAT_PRINTF(s, i)\n#  define JEMALLOC_NOINLINE __declspec(noinline)\n#  ifdef __cplusplus\n#    define JEMALLOC_NOTHROW __declspec(nothrow)\n#  else\n#    define JEMALLOC_NOTHROW\n#  endif\n#  define JEMALLOC_SECTION(s) __declspec(allocate(s))\n#  define JEMALLOC_RESTRICT_RETURN __declspec(restrict)\n#  if _MSC_VER >= 1900 && !defined(__EDG__)\n#    define JEMALLOC_ALLOCATOR __declspec(allocator)\n#  else\n#    define JEMALLOC_ALLOCATOR\n#  endif\n#elif defined(JEMALLOC_HAVE_ATTR)\n#  define JEMALLOC_ATTR(s) __attribute__((s))\n#  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))\n#  ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE\n#    define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))\n#    define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))\n#  else\n#    define JEMALLOC_ALLOC_SIZE(s)\n#    define JEMALLOC_ALLOC_SIZE2(s1, s2)\n#  endif\n#  ifndef JEMALLOC_EXPORT\n#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility(\"default\"))\n#  endif\n#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF\n#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))\n#  elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)\n#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))\n#  else\n#    define JEMALLOC_FORMAT_PRINTF(s, i)\n#  endif\n#  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)\n#  define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)\n#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))\n#  define JEMALLOC_RESTRICT_RETURN\n#  define JEMALLOC_ALLOCATOR\n#else\n#  define JEMALLOC_ATTR(s)\n#  define JEMALLOC_ALIGNED(s)\n#  define JEMALLOC_ALLOC_SIZE(s)\n#  define JEMALLOC_ALLOC_SIZE2(s1, s2)\n#  define JEMALLOC_EXPORT\n#  define JEMALLOC_FORMAT_PRINTF(s, i)\n#  define JEMALLOC_NOINLINE\n#  define JEMALLOC_NOTHROW\n#  define JEMALLOC_SECTION(s)\n#  define JEMALLOC_RESTRICT_RETURN\n#  define JEMALLOC_ALLOCATOR\n#endif\n\n/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()\n * function. */\n#define JEMALLOC_FRAG_HINT\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_mangle.sh",
    "content": "#!/bin/sh -eu\n\npublic_symbols_txt=$1\nsymbol_prefix=$2\n\ncat <<EOF\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 JEMALLOC_NO_DEMANGLE\n#  endif\nEOF\n\nfor nm in `cat ${public_symbols_txt}` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#  define ${n} ${symbol_prefix}${n}\"\ndone\n\ncat <<EOF\n#endif\n\n/*\n * The ${symbol_prefix}* macros can be used as stable alternative names for the\n * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily\n * meant 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\nEOF\n\nfor nm in `cat ${public_symbols_txt}` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#  undef ${symbol_prefix}${n}\"\ndone\n\ncat <<EOF\n#endif\nEOF\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_protos.h.in",
    "content": "/*\n * The @je_@ prefix on the following public symbol declarations is an artifact\n * of namespace management, and should be omitted in application code unless\n * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle@install_suffix@.h).\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 JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@malloc(size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@calloc(size_t num, size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@posix_memalign(void **memptr,\n    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@aligned_alloc(size_t alignment,\n    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)\n    JEMALLOC_ALLOC_SIZE(2);\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@realloc(void *ptr, size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@free(void *ptr)\n    JEMALLOC_CXX_THROW;\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@mallocx(size_t size, int flags)\n    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@rallocx(void *ptr, size_t size,\n    int flags) JEMALLOC_ALLOC_SIZE(2);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@xallocx(void *ptr, size_t size,\n    size_t extra, int flags);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@sallocx(const void *ptr,\n    int flags) JEMALLOC_ATTR(pure);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@dallocx(void *ptr, int flags);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@sdallocx(void *ptr, size_t size,\n    int flags);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@nallocx(size_t size, int flags)\n    JEMALLOC_ATTR(pure);\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@mallctl(const char *name,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen);\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@mallctlnametomib(const char *name,\n    size_t *mibp, size_t *miblenp);\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@mallctlbymib(const size_t *mib,\n    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@malloc_stats_print(\n    void (*write_cb)(void *, const char *), void *@je_@cbopaque,\n    const char *opts);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@malloc_usable_size(\n    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;\n\n#ifdef JEMALLOC_OVERRIDE_MEMALIGN\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@memalign(size_t alignment, size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);\n#endif\n\n#ifdef JEMALLOC_OVERRIDE_VALLOC\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@valloc(size_t size) JEMALLOC_CXX_THROW\n    JEMALLOC_ATTR(malloc);\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_rename.sh",
    "content": "#!/bin/sh\n\npublic_symbols_txt=$1\n\ncat <<EOF\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#ifndef JEMALLOC_NO_RENAME\nEOF\n\nfor nm in `cat ${public_symbols_txt}` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  m=`echo ${nm} |tr ':' ' ' |awk '{print $2}'`\n  echo \"#  define je_${n} ${m}\"\ndone\n\ncat <<EOF\n#endif\nEOF\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_typedefs.h.in",
    "content": "typedef struct extent_hooks_s extent_hooks_t;\n\n/*\n * void *\n * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,\n *     size_t alignment, bool *zero, bool *commit, unsigned arena_ind);\n */\ntypedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *,\n    bool *, unsigned);\n\n/*\n * bool\n * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     bool committed, unsigned arena_ind);\n */\ntypedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool,\n    unsigned);\n\n/*\n * void\n * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     bool committed, unsigned arena_ind);\n */\ntypedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool,\n    unsigned);\n\n/*\n * bool\n * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t offset, size_t length, unsigned arena_ind);\n */\ntypedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t,\n    unsigned);\n\n/*\n * bool\n * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t offset, size_t length, unsigned arena_ind);\n */\ntypedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t,\n    size_t, unsigned);\n\n/*\n * bool\n * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t offset, size_t length, unsigned arena_ind);\n */\ntypedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t,\n    unsigned);\n\n/*\n * bool\n * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t size_a, size_t size_b, bool committed, unsigned arena_ind);\n */\ntypedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t,\n    bool, unsigned);\n\n/*\n * bool\n * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,\n *     void *addr_b, size_t size_b, bool committed, unsigned arena_ind);\n */\ntypedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t,\n    bool, unsigned);\n\nstruct extent_hooks_s {\n\textent_alloc_t\t\t*alloc;\n\textent_dalloc_t\t\t*dalloc;\n\textent_destroy_t\t*destroy;\n\textent_commit_t\t\t*commit;\n\textent_decommit_t\t*decommit;\n\textent_purge_t\t\t*purge_lazy;\n\textent_purge_t\t\t*purge_forced;\n\textent_split_t\t\t*split;\n\textent_merge_t\t\t*merge;\n};\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/msvc_compat/C99/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 */\n/* Clang-cl uses MSVC headers, so needs msvc_compat, but has _Bool as\n * a built-in type. */\n#ifndef __clang__\ntypedef BOOL _Bool;\n#endif\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": "Chapter02/redis-6.0.3/deps/jemalloc/include/msvc_compat/C99/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": "Chapter02/redis-6.0.3/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#ifdef _MSC_VER\n#  include <intrin.h>\n#  pragma intrinsic(_BitScanForward)\nstatic __forceinline int ffsl(long x) {\n\tunsigned long i;\n\n\tif (_BitScanForward(&i, x)) {\n\t\treturn i + 1;\n\t}\n\treturn 0;\n}\n\nstatic __forceinline int ffs(int x) {\n\treturn ffsl(x);\n}\n\n#  ifdef  _M_X64\n#    pragma intrinsic(_BitScanForward64)\n#  endif\n\nstatic __forceinline int ffsll(unsigned __int64 x) {\n\tunsigned long i;\n#ifdef  _M_X64\n\tif (_BitScanForward64(&i, x)) {\n\t\treturn i + 1;\n\t}\n\treturn 0;\n#else\n// Fallback for 32-bit build where 64-bit version not available\n// assuming little endian\n\tunion {\n\t\tunsigned __int64 ll;\n\t\tunsigned   long l[2];\n\t} s;\n\n\ts.ll = x;\n\n\tif (_BitScanForward(&i, s.l[0])) {\n\t\treturn i + 1;\n\t} else if(_BitScanForward(&i, s.l[1])) {\n\t\treturn i + 33;\n\t}\n\treturn 0;\n#endif\n}\n\n#else\n#  define ffsll(x) __builtin_ffsll(x)\n#  define ffsl(x) __builtin_ffsl(x)\n#  define ffs(x) __builtin_ffs(x)\n#endif\n\n#endif /* strings_h */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/include/msvc_compat/windows_extra.h",
    "content": "#ifndef MSVC_COMPAT_WINDOWS_EXTRA_H\n#define MSVC_COMPAT_WINDOWS_EXTRA_H\n\n#include <errno.h>\n\n#endif /* MSVC_COMPAT_WINDOWS_EXTRA_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/jemalloc.pc.in",
    "content": "prefix=@prefix@\nexec_prefix=@exec_prefix@\nlibdir=@libdir@\nincludedir=@includedir@\ninstall_suffix=@install_suffix@\n\nName: jemalloc\nDescription: A general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.\nURL: http://jemalloc.net/\nVersion: @jemalloc_version_major@.@jemalloc_version_minor@.@jemalloc_version_bugfix@_@jemalloc_version_nrev@\nCflags: -I${includedir}\nLibs: -L${libdir} -ljemalloc${install_suffix}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4",
    "content": "# ===========================================================================\n#   http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html\n# ===========================================================================\n#\n# SYNOPSIS\n#\n#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])\n#\n# DESCRIPTION\n#\n#   Check for baseline language coverage in the compiler for the specified\n#   version of the C++ standard.  If necessary, add switches to CXX and\n#   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard)\n#   or '14' (for the C++14 standard).\n#\n#   The second argument, if specified, indicates whether you insist on an\n#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.\n#   -std=c++11).  If neither is specified, you get whatever works, with\n#   preference for an extended mode.\n#\n#   The third argument, if specified 'mandatory' or if left unspecified,\n#   indicates that baseline support for the specified C++ standard is\n#   required and that the macro should error out if no mode with that\n#   support is found.  If specified 'optional', then configuration proceeds\n#   regardless, after defining HAVE_CXX${VERSION} if and only if a\n#   supporting mode is found.\n#\n# LICENSE\n#\n#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>\n#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>\n#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>\n#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>\n#   Copyright (c) 2015 Paul Norman <penorman@mac.com>\n#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>\n#\n#   Copying and distribution of this file, with or without modification, are\n#   permitted in any medium without royalty provided the copyright notice\n#   and this notice are preserved.  This file is offered as-is, without any\n#   warranty.\n\n#serial 4\n\ndnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro\ndnl  (serial version number 13).\n\nAC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl\n  m4_if([$1], [11], [],\n        [$1], [14], [],\n        [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],\n        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl\n  m4_if([$2], [], [],\n        [$2], [ext], [],\n        [$2], [noext], [],\n        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl\n  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],\n        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],\n        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],\n        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])\n  AC_LANG_PUSH([C++])dnl\n  ac_success=no\n  AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,\n  ax_cv_cxx_compile_cxx$1,\n  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],\n    [ax_cv_cxx_compile_cxx$1=yes],\n    [ax_cv_cxx_compile_cxx$1=no])])\n  if test x$ax_cv_cxx_compile_cxx$1 = xyes; then\n    ac_success=yes\n  fi\n\n  m4_if([$2], [noext], [], [dnl\n  if test x$ac_success = xno; then\n    for switch in -std=gnu++$1 -std=gnu++0x; do\n      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])\n      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,\n                     $cachevar,\n        [ac_save_CXX=\"$CXX\"\n         CXX=\"$CXX $switch\"\n         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],\n          [eval $cachevar=yes],\n          [eval $cachevar=no])\n         CXX=\"$ac_save_CXX\"])\n      if eval test x\\$$cachevar = xyes; then\n        CXX=\"$CXX $switch\"\n        if test -n \"$CXXCPP\" ; then\n          CXXCPP=\"$CXXCPP $switch\"\n        fi\n        ac_success=yes\n        break\n      fi\n    done\n  fi])\n\n  m4_if([$2], [ext], [], [dnl\n  if test x$ac_success = xno; then\n    dnl HP's aCC needs +std=c++11 according to:\n    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf\n    dnl Cray's crayCC needs \"-h std=c++11\"\n    for switch in -std=c++$1 -std=c++0x +std=c++$1 \"-h std=c++$1\"; do\n      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])\n      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,\n                     $cachevar,\n        [ac_save_CXX=\"$CXX\"\n         CXX=\"$CXX $switch\"\n         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],\n          [eval $cachevar=yes],\n          [eval $cachevar=no])\n         CXX=\"$ac_save_CXX\"])\n      if eval test x\\$$cachevar = xyes; then\n        CXX=\"$CXX $switch\"\n        if test -n \"$CXXCPP\" ; then\n          CXXCPP=\"$CXXCPP $switch\"\n        fi\n        ac_success=yes\n        break\n      fi\n    done\n  fi])\n  AC_LANG_POP([C++])\n  if test x$ax_cxx_compile_cxx$1_required = xtrue; then\n    if test x$ac_success = xno; then\n      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])\n    fi\n  fi\n  if test x$ac_success = xno; then\n    HAVE_CXX$1=0\n    AC_MSG_NOTICE([No compiler with C++$1 support was found])\n  else\n    HAVE_CXX$1=1\n    AC_DEFINE(HAVE_CXX$1,1,\n              [define if the compiler supports basic C++$1 syntax])\n  fi\n  AC_SUBST(HAVE_CXX$1)\n])\n\n\ndnl  Test body for checking C++11 support\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],\n  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11\n)\n\n\ndnl  Test body for checking C++14 support\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],\n  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11\n  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14\n)\n\n\ndnl  Tests for new features in C++11\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[\n\n// If the compiler admits that it is not ready for C++11, why torture it?\n// Hopefully, this will speed up the test.\n\n#ifndef __cplusplus\n\n#error \"This is not a C++ compiler\"\n\n#elif __cplusplus < 201103L\n\n#error \"This is not a C++11 compiler\"\n\n#else\n\nnamespace cxx11\n{\n\n  namespace test_static_assert\n  {\n\n    template <typename T>\n    struct check\n    {\n      static_assert(sizeof(int) <= sizeof(T), \"not big enough\");\n    };\n\n  }\n\n  namespace test_final_override\n  {\n\n    struct Base\n    {\n      virtual void f() {}\n    };\n\n    struct Derived : public Base\n    {\n      virtual void f() override {}\n    };\n\n  }\n\n  namespace test_double_right_angle_brackets\n  {\n\n    template < typename T >\n    struct check {};\n\n    typedef check<void> single_type;\n    typedef check<check<void>> double_type;\n    typedef check<check<check<void>>> triple_type;\n    typedef check<check<check<check<void>>>> quadruple_type;\n\n  }\n\n  namespace test_decltype\n  {\n\n    int\n    f()\n    {\n      int a = 1;\n      decltype(a) b = 2;\n      return a + b;\n    }\n\n  }\n\n  namespace test_type_deduction\n  {\n\n    template < typename T1, typename T2 >\n    struct is_same\n    {\n      static const bool value = false;\n    };\n\n    template < typename T >\n    struct is_same<T, T>\n    {\n      static const bool value = true;\n    };\n\n    template < typename T1, typename T2 >\n    auto\n    add(T1 a1, T2 a2) -> decltype(a1 + a2)\n    {\n      return a1 + a2;\n    }\n\n    int\n    test(const int c, volatile int v)\n    {\n      static_assert(is_same<int, decltype(0)>::value == true, \"\");\n      static_assert(is_same<int, decltype(c)>::value == false, \"\");\n      static_assert(is_same<int, decltype(v)>::value == false, \"\");\n      auto ac = c;\n      auto av = v;\n      auto sumi = ac + av + 'x';\n      auto sumf = ac + av + 1.0;\n      static_assert(is_same<int, decltype(ac)>::value == true, \"\");\n      static_assert(is_same<int, decltype(av)>::value == true, \"\");\n      static_assert(is_same<int, decltype(sumi)>::value == true, \"\");\n      static_assert(is_same<int, decltype(sumf)>::value == false, \"\");\n      static_assert(is_same<int, decltype(add(c, v))>::value == true, \"\");\n      return (sumf > 0.0) ? sumi : add(c, v);\n    }\n\n  }\n\n  namespace test_noexcept\n  {\n\n    int f() { return 0; }\n    int g() noexcept { return 0; }\n\n    static_assert(noexcept(f()) == false, \"\");\n    static_assert(noexcept(g()) == true, \"\");\n\n  }\n\n  namespace test_constexpr\n  {\n\n    template < typename CharT >\n    unsigned long constexpr\n    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept\n    {\n      return *s ? strlen_c_r(s + 1, acc + 1) : acc;\n    }\n\n    template < typename CharT >\n    unsigned long constexpr\n    strlen_c(const CharT *const s) noexcept\n    {\n      return strlen_c_r(s, 0UL);\n    }\n\n    static_assert(strlen_c(\"\") == 0UL, \"\");\n    static_assert(strlen_c(\"1\") == 1UL, \"\");\n    static_assert(strlen_c(\"example\") == 7UL, \"\");\n    static_assert(strlen_c(\"another\\0example\") == 7UL, \"\");\n\n  }\n\n  namespace test_rvalue_references\n  {\n\n    template < int N >\n    struct answer\n    {\n      static constexpr int value = N;\n    };\n\n    answer<1> f(int&)       { return answer<1>(); }\n    answer<2> f(const int&) { return answer<2>(); }\n    answer<3> f(int&&)      { return answer<3>(); }\n\n    void\n    test()\n    {\n      int i = 0;\n      const int c = 0;\n      static_assert(decltype(f(i))::value == 1, \"\");\n      static_assert(decltype(f(c))::value == 2, \"\");\n      static_assert(decltype(f(0))::value == 3, \"\");\n    }\n\n  }\n\n  namespace test_uniform_initialization\n  {\n\n    struct test\n    {\n      static const int zero {};\n      static const int one {1};\n    };\n\n    static_assert(test::zero == 0, \"\");\n    static_assert(test::one == 1, \"\");\n\n  }\n\n  namespace test_lambdas\n  {\n\n    void\n    test1()\n    {\n      auto lambda1 = [](){};\n      auto lambda2 = lambda1;\n      lambda1();\n      lambda2();\n    }\n\n    int\n    test2()\n    {\n      auto a = [](int i, int j){ return i + j; }(1, 2);\n      auto b = []() -> int { return '0'; }();\n      auto c = [=](){ return a + b; }();\n      auto d = [&](){ return c; }();\n      auto e = [a, &b](int x) mutable {\n        const auto identity = [](int y){ return y; };\n        for (auto i = 0; i < a; ++i)\n          a += b--;\n        return x + identity(a + b);\n      }(0);\n      return a + b + c + d + e;\n    }\n\n    int\n    test3()\n    {\n      const auto nullary = [](){ return 0; };\n      const auto unary = [](int x){ return x; };\n      using nullary_t = decltype(nullary);\n      using unary_t = decltype(unary);\n      const auto higher1st = [](nullary_t f){ return f(); };\n      const auto higher2nd = [unary](nullary_t f1){\n        return [unary, f1](unary_t f2){ return f2(unary(f1())); };\n      };\n      return higher1st(nullary) + higher2nd(nullary)(unary);\n    }\n\n  }\n\n  namespace test_variadic_templates\n  {\n\n    template <int...>\n    struct sum;\n\n    template <int N0, int... N1toN>\n    struct sum<N0, N1toN...>\n    {\n      static constexpr auto value = N0 + sum<N1toN...>::value;\n    };\n\n    template <>\n    struct sum<>\n    {\n      static constexpr auto value = 0;\n    };\n\n    static_assert(sum<>::value == 0, \"\");\n    static_assert(sum<1>::value == 1, \"\");\n    static_assert(sum<23>::value == 23, \"\");\n    static_assert(sum<1, 2>::value == 3, \"\");\n    static_assert(sum<5, 5, 11>::value == 21, \"\");\n    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, \"\");\n\n  }\n\n  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae\n  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function\n  // because of this.\n  namespace test_template_alias_sfinae\n  {\n\n    struct foo {};\n\n    template<typename T>\n    using member = typename T::member_type;\n\n    template<typename T>\n    void func(...) {}\n\n    template<typename T>\n    void func(member<T>*) {}\n\n    void test();\n\n    void test() { func<foo>(0); }\n\n  }\n\n}  // namespace cxx11\n\n#endif  // __cplusplus >= 201103L\n\n]])\n\n\ndnl  Tests for new features in C++14\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[\n\n// If the compiler admits that it is not ready for C++14, why torture it?\n// Hopefully, this will speed up the test.\n\n#ifndef __cplusplus\n\n#error \"This is not a C++ compiler\"\n\n#elif __cplusplus < 201402L\n\n#error \"This is not a C++14 compiler\"\n\n#else\n\nnamespace cxx14\n{\n\n  namespace test_polymorphic_lambdas\n  {\n\n    int\n    test()\n    {\n      const auto lambda = [](auto&&... args){\n        const auto istiny = [](auto x){\n          return (sizeof(x) == 1UL) ? 1 : 0;\n        };\n        const int aretiny[] = { istiny(args)... };\n        return aretiny[0];\n      };\n      return lambda(1, 1L, 1.0f, '1');\n    }\n\n  }\n\n  namespace test_binary_literals\n  {\n\n    constexpr auto ivii = 0b0000000000101010;\n    static_assert(ivii == 42, \"wrong value\");\n\n  }\n\n  namespace test_generalized_constexpr\n  {\n\n    template < typename CharT >\n    constexpr unsigned long\n    strlen_c(const CharT *const s) noexcept\n    {\n      auto length = 0UL;\n      for (auto p = s; *p; ++p)\n        ++length;\n      return length;\n    }\n\n    static_assert(strlen_c(\"\") == 0UL, \"\");\n    static_assert(strlen_c(\"x\") == 1UL, \"\");\n    static_assert(strlen_c(\"test\") == 4UL, \"\");\n    static_assert(strlen_c(\"another\\0test\") == 7UL, \"\");\n\n  }\n\n  namespace test_lambda_init_capture\n  {\n\n    int\n    test()\n    {\n      auto x = 0;\n      const auto lambda1 = [a = x](int b){ return a + b; };\n      const auto lambda2 = [a = lambda1(x)](){ return a; };\n      return lambda2();\n    }\n\n  }\n\n  namespace test_digit_seperators\n  {\n\n    constexpr auto ten_million = 100'000'000;\n    static_assert(ten_million == 100000000, \"\");\n\n  }\n\n  namespace test_return_type_deduction\n  {\n\n    auto f(int& x) { return x; }\n    decltype(auto) g(int& x) { return x; }\n\n    template < typename T1, typename T2 >\n    struct is_same\n    {\n      static constexpr auto value = false;\n    };\n\n    template < typename T >\n    struct is_same<T, T>\n    {\n      static constexpr auto value = true;\n    };\n\n    int\n    test()\n    {\n      auto x = 0;\n      static_assert(is_same<int, decltype(f(x))>::value, \"\");\n      static_assert(is_same<int&, decltype(g(x))>::value, \"\");\n      return x;\n    }\n\n  }\n\n}  // namespace cxx14\n\n#endif  // __cplusplus >= 201402L\n\n]])\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/ReadMe.txt",
    "content": "\nHow to build jemalloc for Windows\n=================================\n\n1. Install Cygwin with at least the following packages:\n   * autoconf\n   * autogen\n   * gawk\n   * grep\n   * sed\n\n2. Install Visual Studio 2015 or 2017 with Visual C++\n\n3. Add Cygwin\\bin to the PATH environment variable\n\n4. Open \"x64 Native Tools Command Prompt for VS 2017\"\n   (note: x86/x64 doesn't matter at this point)\n\n5. Generate header files:\n   sh -c \"CC=cl ./autogen.sh\"\n\n6. Now the project can be opened and built in Visual Studio:\n   msvc\\jemalloc_vc2017.sln\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/jemalloc_vc2015.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 14\nVisualStudioVersion = 14.0.24720.0\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{70A99006-6DE9-472B-8F83-4CEE6C616DF3}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tReadMe.txt = ReadMe.txt\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"jemalloc\", \"projects\\vc2015\\jemalloc\\jemalloc.vcxproj\", \"{8D6BB292-9E1C-413D-9F98-4864BDC1514A}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"test_threads\", \"projects\\vc2015\\test_threads\\test_threads.vcxproj\", \"{09028CFD-4EB7-491D-869C-0708DB97ED44}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tDebug-static|x64 = Debug-static|x64\n\t\tDebug-static|x86 = Debug-static|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\t\tRelease-static|x64 = Release-static|x64\n\t\tRelease-static|x86 = Release-static|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.Build.0 = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.Build.0 = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.ActiveCfg = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.Build.0 = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.ActiveCfg = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.Build.0 = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.Build.0 = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.Build.0 = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.Build.0 = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.ActiveCfg = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.Build.0 = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.ActiveCfg = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.Build.0 = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.Build.0 = Release-static|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/jemalloc_vc2017.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 14\nVisualStudioVersion = 14.0.24720.0\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{70A99006-6DE9-472B-8F83-4CEE6C616DF3}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tReadMe.txt = ReadMe.txt\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"jemalloc\", \"projects\\vc2017\\jemalloc\\jemalloc.vcxproj\", \"{8D6BB292-9E1C-413D-9F98-4864BDC1514A}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"test_threads\", \"projects\\vc2017\\test_threads\\test_threads.vcxproj\", \"{09028CFD-4EB7-491D-869C-0708DB97ED44}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tDebug-static|x64 = Debug-static|x64\n\t\tDebug-static|x86 = Debug-static|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\t\tRelease-static|x64 = Release-static|x64\n\t\tRelease-static|x86 = Release-static|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.Build.0 = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.Build.0 = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.ActiveCfg = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.Build.0 = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.ActiveCfg = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.Build.0 = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.Build.0 = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.Build.0 = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.Build.0 = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.ActiveCfg = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.Build.0 = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.ActiveCfg = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.Build.0 = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.Build.0 = Release-static|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\" />\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>jemalloc</RootNamespace>\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n      <MinimalRebuild>false</MinimalRebuild>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/test_threads/test_threads.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{09028CFD-4EB7-491D-869C-0708DB97ED44}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>test_threads</RootNamespace>\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\" />\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\jemalloc\\jemalloc.vcxproj\">\n      <Project>{8d6bb292-9e1c-413d-9f98-4864bdc1514a}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/test_threads/test_threads.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\" />\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>jemalloc</RootNamespace>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n      <MinimalRebuild>false</MinimalRebuild>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/test_threads/test_threads.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{09028CFD-4EB7-491D-869C-0708DB97ED44}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>test_threads</RootNamespace>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\" />\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\jemalloc\\jemalloc.vcxproj\">\n      <Project>{8d6bb292-9e1c-413d-9f98-4864bdc1514a}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/test_threads/test_threads.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/test_threads/test_threads.cpp",
    "content": "// jemalloc C++ threaded test\n// Author: Rustam Abdullaev\n// Public Domain\n\n#include <atomic>\n#include <functional>\n#include <future>\n#include <random>\n#include <thread>\n#include <vector>\n#include <stdio.h>\n#include <jemalloc/jemalloc.h>\n\nusing std::vector;\nusing std::thread;\nusing std::uniform_int_distribution;\nusing std::minstd_rand;\n\nint test_threads() {\n  je_malloc_conf = \"narenas:3\";\n  int narenas = 0;\n  size_t sz = sizeof(narenas);\n  je_mallctl(\"opt.narenas\", (void *)&narenas, &sz, NULL, 0);\n  if (narenas != 3) {\n    printf(\"Error: unexpected number of arenas: %d\\n\", narenas);\n    return 1;\n  }\n  static const int sizes[] = { 7, 16, 32, 60, 91, 100, 120, 144, 169, 199, 255, 400, 670, 900, 917, 1025, 3333, 5190, 13131, 49192, 99999, 123123, 255265, 2333111 };\n  static const int numSizes = (int)(sizeof(sizes) / sizeof(sizes[0]));\n  vector<thread> workers;\n  static const int numThreads = narenas + 1, numAllocsMax = 25, numIter1 = 50, numIter2 = 50;\n  je_malloc_stats_print(NULL, NULL, NULL);\n  size_t allocated1;\n  size_t sz1 = sizeof(allocated1);\n  je_mallctl(\"stats.active\", (void *)&allocated1, &sz1, NULL, 0);\n  printf(\"\\nPress Enter to start threads...\\n\");\n  getchar();\n  printf(\"Starting %d threads x %d x %d iterations...\\n\", numThreads, numIter1, numIter2);\n  for (int i = 0; i < numThreads; i++) {\n    workers.emplace_back([tid=i]() {\n      uniform_int_distribution<int> sizeDist(0, numSizes - 1);\n      minstd_rand rnd(tid * 17);\n      uint8_t* ptrs[numAllocsMax];\n      int ptrsz[numAllocsMax];\n      for (int i = 0; i < numIter1; ++i) {\n        thread t([&]() {\n          for (int i = 0; i < numIter2; ++i) {\n            const int numAllocs = numAllocsMax - sizeDist(rnd);\n            for (int j = 0; j < numAllocs; j += 64) {\n              const int x = sizeDist(rnd);\n              const int sz = sizes[x];\n              ptrsz[j] = sz;\n              ptrs[j] = (uint8_t*)je_malloc(sz);\n              if (!ptrs[j]) {\n                printf(\"Unable to allocate %d bytes in thread %d, iter %d, alloc %d. %d\\n\", sz, tid, i, j, x);\n                exit(1);\n              }\n              for (int k = 0; k < sz; k++)\n                ptrs[j][k] = tid + k;\n            }\n            for (int j = 0; j < numAllocs; j += 64) {\n              for (int k = 0, sz = ptrsz[j]; k < sz; k++)\n                if (ptrs[j][k] != (uint8_t)(tid + k)) {\n                  printf(\"Memory error in thread %d, iter %d, alloc %d @ %d : %02X!=%02X\\n\", tid, i, j, k, ptrs[j][k], (uint8_t)(tid + k));\n                  exit(1);\n                }\n              je_free(ptrs[j]);\n            }\n          }\n        });\n        t.join();\n      }\n    });\n  }\n  for (thread& t : workers) {\n    t.join();\n  }\n  je_malloc_stats_print(NULL, NULL, NULL);\n  size_t allocated2;\n  je_mallctl(\"stats.active\", (void *)&allocated2, &sz1, NULL, 0);\n  size_t leaked = allocated2 - allocated1;\n  printf(\"\\nDone. Leaked: %zd bytes\\n\", leaked);\n  bool failed = leaked > 65536; // in case C++ runtime allocated something (e.g. iostream locale or facet)\n  printf(\"\\nTest %s!\\n\", (failed ? \"FAILED\" : \"successful\"));\n  printf(\"\\nPress Enter to continue...\\n\");\n  getchar();\n  return failed ? 1 : 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/test_threads/test_threads.h",
    "content": "#pragma once\n\nint test_threads();\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/msvc/test_threads/test_threads_main.cpp",
    "content": "#include \"test_threads.h\"\n#include <future>\n#include <functional>\n#include <chrono>\n\nusing namespace std::chrono_literals;\n\nint main(int argc, char** argv) {\n  int rc = test_threads();\n  return rc;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/run_tests.sh",
    "content": "$(dirname \"$)\")/scripts/gen_run_tests.py | bash\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/scripts/gen_run_tests.py",
    "content": "#!/usr/bin/env python\n\nimport sys\nfrom itertools import combinations\nfrom os import uname\nfrom multiprocessing import cpu_count\n\n# Later, we want to test extended vaddr support.  Apparently, the \"real\" way of\n# checking this is flaky on OS X.\nbits_64 = sys.maxsize > 2**32\n\nnparallel = cpu_count() * 2\n\nuname = uname()[0]\n\ndef powerset(items):\n    result = []\n    for i in xrange(len(items) + 1):\n        result += combinations(items, i)\n    return result\n\npossible_compilers = [('gcc', 'g++'), ('clang', 'clang++')]\npossible_compiler_opts = [\n    '-m32',\n]\npossible_config_opts = [\n    '--enable-debug',\n    '--enable-prof',\n    '--disable-stats',\n]\nif bits_64:\n    possible_config_opts.append('--with-lg-vaddr=56')\n\npossible_malloc_conf_opts = [\n    'tcache:false',\n    'dss:primary',\n    'percpu_arena:percpu',\n    'background_thread:true',\n]\n\nprint 'set -e'\nprint 'if [ -f Makefile ] ; then make relclean ; fi'\nprint 'autoconf'\nprint 'rm -rf run_tests.out'\nprint 'mkdir run_tests.out'\nprint 'cd run_tests.out'\n\nind = 0\nfor cc, cxx in possible_compilers:\n    for compiler_opts in powerset(possible_compiler_opts):\n        for config_opts in powerset(possible_config_opts):\n            for malloc_conf_opts in powerset(possible_malloc_conf_opts):\n                if cc is 'clang' \\\n                  and '-m32' in possible_compiler_opts \\\n                  and '--enable-prof' in config_opts:\n                    continue\n                config_line = (\n                    'EXTRA_CFLAGS=-Werror EXTRA_CXXFLAGS=-Werror '\n                    + 'CC=\"{} {}\" '.format(cc, \" \".join(compiler_opts))\n                    + 'CXX=\"{} {}\" '.format(cxx, \" \".join(compiler_opts))\n                    + '../../configure '\n                    + \" \".join(config_opts) + (' --with-malloc-conf=' +\n                    \",\".join(malloc_conf_opts) if len(malloc_conf_opts) > 0\n                    else '')\n                )\n\n                # We don't want to test large vaddr spaces in 32-bit mode.\n\t\tif ('-m32' in compiler_opts and '--with-lg-vaddr=56' in\n                  config_opts):\n\t\t    continue\n\n                # Per CPU arenas are only supported on Linux.\n                linux_supported = ('percpu_arena:percpu' in malloc_conf_opts \\\n                  or 'background_thread:true' in malloc_conf_opts)\n                # Heap profiling and dss are not supported on OS X.\n                darwin_unsupported = ('--enable-prof' in config_opts or \\\n                  'dss:primary' in malloc_conf_opts)\n                if (uname == 'Linux' and linux_supported) \\\n                  or (not linux_supported and (uname != 'Darwin' or \\\n                  not darwin_unsupported)):\n                    print \"\"\"cat <<EOF > run_test_%(ind)d.sh\n#!/bin/sh\n\nset -e\n\nabort() {\n    echo \"==> Error\" >> run_test.log\n    echo \"Error; see run_tests.out/run_test_%(ind)d.out/run_test.log\"\n    exit 255 # Special exit code tells xargs to terminate.\n}\n\n# Environment variables are not supported.\nrun_cmd() {\n    echo \"==> \\$@\" >> run_test.log\n    \\$@ >> run_test.log 2>&1 || abort\n}\n\necho \"=> run_test_%(ind)d: %(config_line)s\"\nmkdir run_test_%(ind)d.out\ncd run_test_%(ind)d.out\n\necho \"==> %(config_line)s\" >> run_test.log\n%(config_line)s >> run_test.log 2>&1 || abort\n\nrun_cmd make all tests\nrun_cmd make check\nrun_cmd make distclean\nEOF\nchmod 755 run_test_%(ind)d.sh\"\"\" % {'ind': ind, 'config_line': config_line}\n                    ind += 1\n\nprint 'for i in `seq 0 %(last_ind)d` ; do echo run_test_${i}.sh ; done | xargs -P %(nparallel)d -n 1 sh' % {'last_ind': ind-1, 'nparallel': nparallel}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/scripts/gen_travis.py",
    "content": "#!/usr/bin/env python\n\nfrom itertools import combinations\n\ntravis_template = \"\"\"\\\nlanguage: generic\n\nmatrix:\n  include:\n%s\n\nbefore_script:\n  - autoconf\n  - ./configure ${COMPILER_FLAGS:+ \\\n      CC=\"$CC $COMPILER_FLAGS\" \\\n      CXX=\"$CXX $COMPILER_FLAGS\" } \\\n      $CONFIGURE_FLAGS\n  - make -j3\n  - make -j3 tests\n\nscript:\n  - make check\n\"\"\"\n\n# The 'default' configuration is gcc, on linux, with no compiler or configure\n# flags.  We also test with clang, -m32, --enable-debug, --enable-prof,\n# --disable-stats, and --with-malloc-conf=tcache:false.  To avoid abusing\n# travis though, we don't test all 2**7 = 128 possible combinations of these;\n# instead, we only test combinations of up to 2 'unusual' settings, under the\n# hope that bugs involving interactions of such settings are rare.\n# Things at once, for C(7, 0) + C(7, 1) + C(7, 2) = 29\nMAX_UNUSUAL_OPTIONS = 2\n\nos_default = 'linux'\nos_unusual = 'osx'\n\ncompilers_default = 'CC=gcc CXX=g++'\ncompilers_unusual = 'CC=clang CXX=clang++'\n\ncompiler_flag_unusuals = ['-m32']\n\nconfigure_flag_unusuals = [\n    '--enable-debug',\n    '--enable-prof',\n    '--disable-stats',\n]\n\nmalloc_conf_unusuals = [\n    'tcache:false',\n    'dss:primary',\n    'percpu_arena:percpu',\n    'background_thread:true',\n]\n\nall_unusuals = (\n    [os_unusual] + [compilers_unusual] + compiler_flag_unusuals\n    + configure_flag_unusuals + malloc_conf_unusuals\n)\n\nunusual_combinations_to_test = []\nfor i in xrange(MAX_UNUSUAL_OPTIONS + 1):\n    unusual_combinations_to_test += combinations(all_unusuals, i)\n\ninclude_rows = \"\"\nfor unusual_combination in unusual_combinations_to_test:\n    os = os_default\n    if os_unusual in unusual_combination:\n        os = os_unusual\n\n    compilers = compilers_default\n    if compilers_unusual in unusual_combination:\n        compilers = compilers_unusual\n\n    compiler_flags = [\n        x for x in unusual_combination if x in compiler_flag_unusuals]\n\n    configure_flags = [\n        x for x in unusual_combination if x in configure_flag_unusuals]\n\n    malloc_conf = [\n        x for x in unusual_combination if x in malloc_conf_unusuals]\n    # Filter out unsupported configurations on OS X.\n    if os == 'osx' and ('dss:primary' in malloc_conf or \\\n      'percpu_arena:percpu' in malloc_conf or 'background_thread:true' \\\n      in malloc_conf):\n        continue\n    if len(malloc_conf) > 0:\n        configure_flags.append('--with-malloc-conf=' + \",\".join(malloc_conf))\n\n    # Filter out an unsupported configuration - heap profiling on OS X.\n    if os == 'osx' and '--enable-prof' in configure_flags:\n        continue\n\n    # We get some spurious errors when -Warray-bounds is enabled.\n    env_string = ('{} COMPILER_FLAGS=\"{}\" CONFIGURE_FLAGS=\"{}\" '\n\t'EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"').format(\n        compilers, \" \".join(compiler_flags), \" \".join(configure_flags))\n\n    include_rows += '    - os: %s\\n' % os\n    include_rows += '      env: %s\\n' % env_string\n    if '-m32' in unusual_combination and os == 'linux':\n        include_rows += '      addons:\\n'\n\tinclude_rows += '        apt:\\n'\n\tinclude_rows += '          packages:\\n'\n\tinclude_rows += '            - gcc-multilib\\n'\n\nprint travis_template % include_rows\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/arena.c",
    "content": "#define JEMALLOC_ARENA_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/div.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/*\n * Define names for both unininitialized and initialized phases, so that\n * options and mallctl processing are straightforward.\n */\nconst char *percpu_arena_mode_names[] = {\n\t\"percpu\",\n\t\"phycpu\",\n\t\"disabled\",\n\t\"percpu\",\n\t\"phycpu\"\n};\npercpu_arena_mode_t opt_percpu_arena = PERCPU_ARENA_DEFAULT;\n\nssize_t opt_dirty_decay_ms = DIRTY_DECAY_MS_DEFAULT;\nssize_t opt_muzzy_decay_ms = MUZZY_DECAY_MS_DEFAULT;\n\nstatic atomic_zd_t dirty_decay_ms_default;\nstatic atomic_zd_t muzzy_decay_ms_default;\n\nconst uint64_t h_steps[SMOOTHSTEP_NSTEPS] = {\n#define STEP(step, h, x, y)\t\t\t\\\n\t\th,\n\t\tSMOOTHSTEP\n#undef STEP\n};\n\nstatic div_info_t arena_binind_div_info[NBINS];\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic void arena_decay_to_limit(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, extents_t *extents, bool all, size_t npages_limit,\n    size_t npages_decay_max, bool is_background_thread);\nstatic bool arena_decay_dirty(tsdn_t *tsdn, arena_t *arena,\n    bool is_background_thread, bool all);\nstatic void arena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin);\nstatic void arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin);\n\n/******************************************************************************/\n\nvoid\narena_basic_stats_merge(UNUSED tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,\n    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,\n    size_t *nactive, size_t *ndirty, size_t *nmuzzy) {\n\t*nthreads += arena_nthreads_get(arena, false);\n\t*dss = dss_prec_names[arena_dss_prec_get(arena)];\n\t*dirty_decay_ms = arena_dirty_decay_ms_get(arena);\n\t*muzzy_decay_ms = arena_muzzy_decay_ms_get(arena);\n\t*nactive += atomic_load_zu(&arena->nactive, ATOMIC_RELAXED);\n\t*ndirty += extents_npages_get(&arena->extents_dirty);\n\t*nmuzzy += extents_npages_get(&arena->extents_muzzy);\n}\n\nvoid\narena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,\n    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,\n    size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,\n    bin_stats_t *bstats, arena_stats_large_t *lstats) {\n\tcassert(config_stats);\n\n\tarena_basic_stats_merge(tsdn, arena, nthreads, dss, dirty_decay_ms,\n\t    muzzy_decay_ms, nactive, ndirty, nmuzzy);\n\n\tsize_t base_allocated, base_resident, base_mapped, metadata_thp;\n\tbase_stats_get(tsdn, arena->base, &base_allocated, &base_resident,\n\t    &base_mapped, &metadata_thp);\n\n\tarena_stats_lock(tsdn, &arena->stats);\n\n\tarena_stats_accum_zu(&astats->mapped, base_mapped\n\t    + arena_stats_read_zu(tsdn, &arena->stats, &arena->stats.mapped));\n\tarena_stats_accum_zu(&astats->retained,\n\t    extents_npages_get(&arena->extents_retained) << LG_PAGE);\n\n\tarena_stats_accum_u64(&astats->decay_dirty.npurge,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_dirty.npurge));\n\tarena_stats_accum_u64(&astats->decay_dirty.nmadvise,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_dirty.nmadvise));\n\tarena_stats_accum_u64(&astats->decay_dirty.purged,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_dirty.purged));\n\n\tarena_stats_accum_u64(&astats->decay_muzzy.npurge,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_muzzy.npurge));\n\tarena_stats_accum_u64(&astats->decay_muzzy.nmadvise,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_muzzy.nmadvise));\n\tarena_stats_accum_u64(&astats->decay_muzzy.purged,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_muzzy.purged));\n\n\tarena_stats_accum_zu(&astats->base, base_allocated);\n\tarena_stats_accum_zu(&astats->internal, arena_internal_get(arena));\n\tarena_stats_accum_zu(&astats->metadata_thp, metadata_thp);\n\tarena_stats_accum_zu(&astats->resident, base_resident +\n\t    (((atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) +\n\t    extents_npages_get(&arena->extents_dirty) +\n\t    extents_npages_get(&arena->extents_muzzy)) << LG_PAGE)));\n\n\tfor (szind_t i = 0; i < NSIZES - NBINS; i++) {\n\t\tuint64_t nmalloc = arena_stats_read_u64(tsdn, &arena->stats,\n\t\t    &arena->stats.lstats[i].nmalloc);\n\t\tarena_stats_accum_u64(&lstats[i].nmalloc, nmalloc);\n\t\tarena_stats_accum_u64(&astats->nmalloc_large, nmalloc);\n\n\t\tuint64_t ndalloc = arena_stats_read_u64(tsdn, &arena->stats,\n\t\t    &arena->stats.lstats[i].ndalloc);\n\t\tarena_stats_accum_u64(&lstats[i].ndalloc, ndalloc);\n\t\tarena_stats_accum_u64(&astats->ndalloc_large, ndalloc);\n\n\t\tuint64_t nrequests = arena_stats_read_u64(tsdn, &arena->stats,\n\t\t    &arena->stats.lstats[i].nrequests);\n\t\tarena_stats_accum_u64(&lstats[i].nrequests,\n\t\t    nmalloc + nrequests);\n\t\tarena_stats_accum_u64(&astats->nrequests_large,\n\t\t    nmalloc + nrequests);\n\n\t\tassert(nmalloc >= ndalloc);\n\t\tassert(nmalloc - ndalloc <= SIZE_T_MAX);\n\t\tsize_t curlextents = (size_t)(nmalloc - ndalloc);\n\t\tlstats[i].curlextents += curlextents;\n\t\tarena_stats_accum_zu(&astats->allocated_large,\n\t\t    curlextents * sz_index2size(NBINS + i));\n\t}\n\n\tarena_stats_unlock(tsdn, &arena->stats);\n\n\t/* tcache_bytes counts currently cached bytes. */\n\tatomic_store_zu(&astats->tcache_bytes, 0, ATOMIC_RELAXED);\n\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\tcache_bin_array_descriptor_t *descriptor;\n\tql_foreach(descriptor, &arena->cache_bin_array_descriptor_ql, link) {\n\t\tszind_t i = 0;\n\t\tfor (; i < NBINS; i++) {\n\t\t\tcache_bin_t *tbin = &descriptor->bins_small[i];\n\t\t\tarena_stats_accum_zu(&astats->tcache_bytes,\n\t\t\t    tbin->ncached * sz_index2size(i));\n\t\t}\n\t\tfor (; i < nhbins; i++) {\n\t\t\tcache_bin_t *tbin = &descriptor->bins_large[i];\n\t\t\tarena_stats_accum_zu(&astats->tcache_bytes,\n\t\t\t    tbin->ncached * sz_index2size(i));\n\t\t}\n\t}\n\tmalloc_mutex_prof_read(tsdn,\n\t    &astats->mutex_prof_data[arena_prof_mutex_tcache_list],\n\t    &arena->tcache_ql_mtx);\n\tmalloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);\n\n#define READ_ARENA_MUTEX_PROF_DATA(mtx, ind)\t\t\t\t\\\n    malloc_mutex_lock(tsdn, &arena->mtx);\t\t\t\t\\\n    malloc_mutex_prof_read(tsdn, &astats->mutex_prof_data[ind],\t\t\\\n        &arena->mtx);\t\t\t\t\t\t\t\\\n    malloc_mutex_unlock(tsdn, &arena->mtx);\n\n\t/* Gather per arena mutex profiling data. */\n\tREAD_ARENA_MUTEX_PROF_DATA(large_mtx, arena_prof_mutex_large);\n\tREAD_ARENA_MUTEX_PROF_DATA(extent_avail_mtx,\n\t    arena_prof_mutex_extent_avail)\n\tREAD_ARENA_MUTEX_PROF_DATA(extents_dirty.mtx,\n\t    arena_prof_mutex_extents_dirty)\n\tREAD_ARENA_MUTEX_PROF_DATA(extents_muzzy.mtx,\n\t    arena_prof_mutex_extents_muzzy)\n\tREAD_ARENA_MUTEX_PROF_DATA(extents_retained.mtx,\n\t    arena_prof_mutex_extents_retained)\n\tREAD_ARENA_MUTEX_PROF_DATA(decay_dirty.mtx,\n\t    arena_prof_mutex_decay_dirty)\n\tREAD_ARENA_MUTEX_PROF_DATA(decay_muzzy.mtx,\n\t    arena_prof_mutex_decay_muzzy)\n\tREAD_ARENA_MUTEX_PROF_DATA(base->mtx,\n\t    arena_prof_mutex_base)\n#undef READ_ARENA_MUTEX_PROF_DATA\n\n\tnstime_copy(&astats->uptime, &arena->create_time);\n\tnstime_update(&astats->uptime);\n\tnstime_subtract(&astats->uptime, &arena->create_time);\n\n\tfor (szind_t i = 0; i < NBINS; i++) {\n\t\tbin_stats_merge(tsdn, &bstats[i], &arena->bins[i]);\n\t}\n}\n\nvoid\narena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textents_dalloc(tsdn, arena, r_extent_hooks, &arena->extents_dirty,\n\t    extent);\n\tif (arena_dirty_decay_ms_get(arena) == 0) {\n\t\tarena_decay_dirty(tsdn, arena, false, true);\n\t} else {\n\t\tarena_background_thread_inactivity_check(tsdn, arena, false);\n\t}\n}\n\nstatic void *\narena_slab_reg_alloc(extent_t *slab, const bin_info_t *bin_info) {\n\tvoid *ret;\n\tarena_slab_data_t *slab_data = extent_slab_data_get(slab);\n\tsize_t regind;\n\n\tassert(extent_nfree_get(slab) > 0);\n\tassert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info));\n\n\tregind = bitmap_sfu(slab_data->bitmap, &bin_info->bitmap_info);\n\tret = (void *)((uintptr_t)extent_addr_get(slab) +\n\t    (uintptr_t)(bin_info->reg_size * regind));\n\textent_nfree_dec(slab);\n\treturn ret;\n}\n\n#ifndef JEMALLOC_JET\nstatic\n#endif\nsize_t\narena_slab_regind(extent_t *slab, szind_t binind, const void *ptr) {\n\tsize_t diff, regind;\n\n\t/* Freeing a pointer outside the slab can cause assertion failure. */\n\tassert((uintptr_t)ptr >= (uintptr_t)extent_addr_get(slab));\n\tassert((uintptr_t)ptr < (uintptr_t)extent_past_get(slab));\n\t/* Freeing an interior pointer can cause assertion failure. */\n\tassert(((uintptr_t)ptr - (uintptr_t)extent_addr_get(slab)) %\n\t    (uintptr_t)bin_infos[binind].reg_size == 0);\n\n\tdiff = (size_t)((uintptr_t)ptr - (uintptr_t)extent_addr_get(slab));\n\n\t/* Avoid doing division with a variable divisor. */\n\tregind = div_compute(&arena_binind_div_info[binind], diff);\n\n\tassert(regind < bin_infos[binind].nregs);\n\n\treturn regind;\n}\n\nstatic void\narena_slab_reg_dalloc(extent_t *slab, arena_slab_data_t *slab_data, void *ptr) {\n\tszind_t binind = extent_szind_get(slab);\n\tconst bin_info_t *bin_info = &bin_infos[binind];\n\tsize_t regind = arena_slab_regind(slab, binind, ptr);\n\n\tassert(extent_nfree_get(slab) < bin_info->nregs);\n\t/* Freeing an unallocated pointer can cause assertion failure. */\n\tassert(bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, regind));\n\n\tbitmap_unset(slab_data->bitmap, &bin_info->bitmap_info, regind);\n\textent_nfree_inc(slab);\n}\n\nstatic void\narena_nactive_add(arena_t *arena, size_t add_pages) {\n\tatomic_fetch_add_zu(&arena->nactive, add_pages, ATOMIC_RELAXED);\n}\n\nstatic void\narena_nactive_sub(arena_t *arena, size_t sub_pages) {\n\tassert(atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) >= sub_pages);\n\tatomic_fetch_sub_zu(&arena->nactive, sub_pages, ATOMIC_RELAXED);\n}\n\nstatic void\narena_large_malloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {\n\tszind_t index, hindex;\n\n\tcassert(config_stats);\n\n\tif (usize < LARGE_MINCLASS) {\n\t\tusize = LARGE_MINCLASS;\n\t}\n\tindex = sz_size2index(usize);\n\thindex = (index >= NBINS) ? index - NBINS : 0;\n\n\tarena_stats_add_u64(tsdn, &arena->stats,\n\t    &arena->stats.lstats[hindex].nmalloc, 1);\n}\n\nstatic void\narena_large_dalloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {\n\tszind_t index, hindex;\n\n\tcassert(config_stats);\n\n\tif (usize < LARGE_MINCLASS) {\n\t\tusize = LARGE_MINCLASS;\n\t}\n\tindex = sz_size2index(usize);\n\thindex = (index >= NBINS) ? index - NBINS : 0;\n\n\tarena_stats_add_u64(tsdn, &arena->stats,\n\t    &arena->stats.lstats[hindex].ndalloc, 1);\n}\n\nstatic void\narena_large_ralloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t oldusize,\n    size_t usize) {\n\tarena_large_dalloc_stats_update(tsdn, arena, oldusize);\n\tarena_large_malloc_stats_update(tsdn, arena, usize);\n}\n\nextent_t *\narena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool *zero) {\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tszind_t szind = sz_size2index(usize);\n\tsize_t mapped_add;\n\tbool commit = true;\n\textent_t *extent = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_dirty, NULL, usize, sz_large_pad, alignment, false,\n\t    szind, zero, &commit);\n\tif (extent == NULL) {\n\t\textent = extents_alloc(tsdn, arena, &extent_hooks,\n\t\t    &arena->extents_muzzy, NULL, usize, sz_large_pad, alignment,\n\t\t    false, szind, zero, &commit);\n\t}\n\tsize_t size = usize + sz_large_pad;\n\tif (extent == NULL) {\n\t\textent = extent_alloc_wrapper(tsdn, arena, &extent_hooks, NULL,\n\t\t    usize, sz_large_pad, alignment, false, szind, zero,\n\t\t    &commit);\n\t\tif (config_stats) {\n\t\t\t/*\n\t\t\t * extent may be NULL on OOM, but in that case\n\t\t\t * mapped_add isn't used below, so there's no need to\n\t\t\t * conditionlly set it to 0 here.\n\t\t\t */\n\t\t\tmapped_add = size;\n\t\t}\n\t} else if (config_stats) {\n\t\tmapped_add = 0;\n\t}\n\n\tif (extent != NULL) {\n\t\tif (config_stats) {\n\t\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\t\tarena_large_malloc_stats_update(tsdn, arena, usize);\n\t\t\tif (mapped_add != 0) {\n\t\t\t\tarena_stats_add_zu(tsdn, &arena->stats,\n\t\t\t\t    &arena->stats.mapped, mapped_add);\n\t\t\t}\n\t\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t\t}\n\t\tarena_nactive_add(arena, size >> LG_PAGE);\n\t}\n\n\treturn extent;\n}\n\nvoid\narena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_large_dalloc_stats_update(tsdn, arena,\n\t\t    extent_usize_get(extent));\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\tarena_nactive_sub(arena, extent_size_get(extent) >> LG_PAGE);\n}\n\nvoid\narena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    size_t oldusize) {\n\tsize_t usize = extent_usize_get(extent);\n\tsize_t udiff = oldusize - usize;\n\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_large_ralloc_stats_update(tsdn, arena, oldusize, usize);\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\tarena_nactive_sub(arena, udiff >> LG_PAGE);\n}\n\nvoid\narena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    size_t oldusize) {\n\tsize_t usize = extent_usize_get(extent);\n\tsize_t udiff = usize - oldusize;\n\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_large_ralloc_stats_update(tsdn, arena, oldusize, usize);\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\tarena_nactive_add(arena, udiff >> LG_PAGE);\n}\n\nstatic ssize_t\narena_decay_ms_read(arena_decay_t *decay) {\n\treturn atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);\n}\n\nstatic void\narena_decay_ms_write(arena_decay_t *decay, ssize_t decay_ms) {\n\tatomic_store_zd(&decay->time_ms, decay_ms, ATOMIC_RELAXED);\n}\n\nstatic void\narena_decay_deadline_init(arena_decay_t *decay) {\n\t/*\n\t * Generate a new deadline that is uniformly random within the next\n\t * epoch after the current one.\n\t */\n\tnstime_copy(&decay->deadline, &decay->epoch);\n\tnstime_add(&decay->deadline, &decay->interval);\n\tif (arena_decay_ms_read(decay) > 0) {\n\t\tnstime_t jitter;\n\n\t\tnstime_init(&jitter, prng_range_u64(&decay->jitter_state,\n\t\t    nstime_ns(&decay->interval)));\n\t\tnstime_add(&decay->deadline, &jitter);\n\t}\n}\n\nstatic bool\narena_decay_deadline_reached(const arena_decay_t *decay, const nstime_t *time) {\n\treturn (nstime_compare(&decay->deadline, time) <= 0);\n}\n\nstatic size_t\narena_decay_backlog_npages_limit(const arena_decay_t *decay) {\n\tuint64_t sum;\n\tsize_t npages_limit_backlog;\n\tunsigned i;\n\n\t/*\n\t * For each element of decay_backlog, multiply by the corresponding\n\t * fixed-point smoothstep decay factor.  Sum the products, then divide\n\t * to round down to the nearest whole number of pages.\n\t */\n\tsum = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tsum += decay->backlog[i] * h_steps[i];\n\t}\n\tnpages_limit_backlog = (size_t)(sum >> SMOOTHSTEP_BFP);\n\n\treturn npages_limit_backlog;\n}\n\nstatic void\narena_decay_backlog_update_last(arena_decay_t *decay, size_t current_npages) {\n\tsize_t npages_delta = (current_npages > decay->nunpurged) ?\n\t    current_npages - decay->nunpurged : 0;\n\tdecay->backlog[SMOOTHSTEP_NSTEPS-1] = npages_delta;\n\n\tif (config_debug) {\n\t\tif (current_npages > decay->ceil_npages) {\n\t\t\tdecay->ceil_npages = current_npages;\n\t\t}\n\t\tsize_t npages_limit = arena_decay_backlog_npages_limit(decay);\n\t\tassert(decay->ceil_npages >= npages_limit);\n\t\tif (decay->ceil_npages > npages_limit) {\n\t\t\tdecay->ceil_npages = npages_limit;\n\t\t}\n\t}\n}\n\nstatic void\narena_decay_backlog_update(arena_decay_t *decay, uint64_t nadvance_u64,\n    size_t current_npages) {\n\tif (nadvance_u64 >= SMOOTHSTEP_NSTEPS) {\n\t\tmemset(decay->backlog, 0, (SMOOTHSTEP_NSTEPS-1) *\n\t\t    sizeof(size_t));\n\t} else {\n\t\tsize_t nadvance_z = (size_t)nadvance_u64;\n\n\t\tassert((uint64_t)nadvance_z == nadvance_u64);\n\n\t\tmemmove(decay->backlog, &decay->backlog[nadvance_z],\n\t\t    (SMOOTHSTEP_NSTEPS - nadvance_z) * sizeof(size_t));\n\t\tif (nadvance_z > 1) {\n\t\t\tmemset(&decay->backlog[SMOOTHSTEP_NSTEPS -\n\t\t\t    nadvance_z], 0, (nadvance_z-1) * sizeof(size_t));\n\t\t}\n\t}\n\n\tarena_decay_backlog_update_last(decay, current_npages);\n}\n\nstatic void\narena_decay_try_purge(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, size_t current_npages, size_t npages_limit,\n    bool is_background_thread) {\n\tif (current_npages > npages_limit) {\n\t\tarena_decay_to_limit(tsdn, arena, decay, extents, false,\n\t\t    npages_limit, current_npages - npages_limit,\n\t\t    is_background_thread);\n\t}\n}\n\nstatic void\narena_decay_epoch_advance_helper(arena_decay_t *decay, const nstime_t *time,\n    size_t current_npages) {\n\tassert(arena_decay_deadline_reached(decay, time));\n\n\tnstime_t delta;\n\tnstime_copy(&delta, time);\n\tnstime_subtract(&delta, &decay->epoch);\n\n\tuint64_t nadvance_u64 = nstime_divide(&delta, &decay->interval);\n\tassert(nadvance_u64 > 0);\n\n\t/* Add nadvance_u64 decay intervals to epoch. */\n\tnstime_copy(&delta, &decay->interval);\n\tnstime_imultiply(&delta, nadvance_u64);\n\tnstime_add(&decay->epoch, &delta);\n\n\t/* Set a new deadline. */\n\tarena_decay_deadline_init(decay);\n\n\t/* Update the backlog. */\n\tarena_decay_backlog_update(decay, nadvance_u64, current_npages);\n}\n\nstatic void\narena_decay_epoch_advance(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, const nstime_t *time, bool is_background_thread) {\n\tsize_t current_npages = extents_npages_get(extents);\n\tarena_decay_epoch_advance_helper(decay, time, current_npages);\n\n\tsize_t npages_limit = arena_decay_backlog_npages_limit(decay);\n\t/* We may unlock decay->mtx when try_purge(). Finish logging first. */\n\tdecay->nunpurged = (npages_limit > current_npages) ? npages_limit :\n\t    current_npages;\n\n\tif (!background_thread_enabled() || is_background_thread) {\n\t\tarena_decay_try_purge(tsdn, arena, decay, extents,\n\t\t    current_npages, npages_limit, is_background_thread);\n\t}\n}\n\nstatic void\narena_decay_reinit(arena_decay_t *decay, ssize_t decay_ms) {\n\tarena_decay_ms_write(decay, decay_ms);\n\tif (decay_ms > 0) {\n\t\tnstime_init(&decay->interval, (uint64_t)decay_ms *\n\t\t    KQU(1000000));\n\t\tnstime_idivide(&decay->interval, SMOOTHSTEP_NSTEPS);\n\t}\n\n\tnstime_init(&decay->epoch, 0);\n\tnstime_update(&decay->epoch);\n\tdecay->jitter_state = (uint64_t)(uintptr_t)decay;\n\tarena_decay_deadline_init(decay);\n\tdecay->nunpurged = 0;\n\tmemset(decay->backlog, 0, SMOOTHSTEP_NSTEPS * sizeof(size_t));\n}\n\nstatic bool\narena_decay_init(arena_decay_t *decay, ssize_t decay_ms,\n    arena_stats_decay_t *stats) {\n\tif (config_debug) {\n\t\tfor (size_t i = 0; i < sizeof(arena_decay_t); i++) {\n\t\t\tassert(((char *)decay)[i] == 0);\n\t\t}\n\t\tdecay->ceil_npages = 0;\n\t}\n\tif (malloc_mutex_init(&decay->mtx, \"decay\", WITNESS_RANK_DECAY,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tdecay->purging = false;\n\tarena_decay_reinit(decay, decay_ms);\n\t/* Memory is zeroed, so there is no need to clear stats. */\n\tif (config_stats) {\n\t\tdecay->stats = stats;\n\t}\n\treturn false;\n}\n\nstatic bool\narena_decay_ms_valid(ssize_t decay_ms) {\n\tif (decay_ms < -1) {\n\t\treturn false;\n\t}\n\tif (decay_ms == -1 || (uint64_t)decay_ms <= NSTIME_SEC_MAX *\n\t    KQU(1000)) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic bool\narena_maybe_decay(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, bool is_background_thread) {\n\tmalloc_mutex_assert_owner(tsdn, &decay->mtx);\n\n\t/* Purge all or nothing if the option is disabled. */\n\tssize_t decay_ms = arena_decay_ms_read(decay);\n\tif (decay_ms <= 0) {\n\t\tif (decay_ms == 0) {\n\t\t\tarena_decay_to_limit(tsdn, arena, decay, extents, false,\n\t\t\t    0, extents_npages_get(extents),\n\t\t\t    is_background_thread);\n\t\t}\n\t\treturn false;\n\t}\n\n\tnstime_t time;\n\tnstime_init(&time, 0);\n\tnstime_update(&time);\n\tif (unlikely(!nstime_monotonic() && nstime_compare(&decay->epoch, &time)\n\t    > 0)) {\n\t\t/*\n\t\t * Time went backwards.  Move the epoch back in time and\n\t\t * generate a new deadline, with the expectation that time\n\t\t * typically flows forward for long enough periods of time that\n\t\t * epochs complete.  Unfortunately, this strategy is susceptible\n\t\t * to clock jitter triggering premature epoch advances, but\n\t\t * clock jitter estimation and compensation isn't feasible here\n\t\t * because calls into this code are event-driven.\n\t\t */\n\t\tnstime_copy(&decay->epoch, &time);\n\t\tarena_decay_deadline_init(decay);\n\t} else {\n\t\t/* Verify that time does not go backwards. */\n\t\tassert(nstime_compare(&decay->epoch, &time) <= 0);\n\t}\n\n\t/*\n\t * If the deadline has been reached, advance to the current epoch and\n\t * purge to the new limit if necessary.  Note that dirty pages created\n\t * during the current epoch are not subject to purge until a future\n\t * epoch, so as a result purging only happens during epoch advances, or\n\t * being triggered by background threads (scheduled event).\n\t */\n\tbool advance_epoch = arena_decay_deadline_reached(decay, &time);\n\tif (advance_epoch) {\n\t\tarena_decay_epoch_advance(tsdn, arena, decay, extents, &time,\n\t\t    is_background_thread);\n\t} else if (is_background_thread) {\n\t\tarena_decay_try_purge(tsdn, arena, decay, extents,\n\t\t    extents_npages_get(extents),\n\t\t    arena_decay_backlog_npages_limit(decay),\n\t\t    is_background_thread);\n\t}\n\n\treturn advance_epoch;\n}\n\nstatic ssize_t\narena_decay_ms_get(arena_decay_t *decay) {\n\treturn arena_decay_ms_read(decay);\n}\n\nssize_t\narena_dirty_decay_ms_get(arena_t *arena) {\n\treturn arena_decay_ms_get(&arena->decay_dirty);\n}\n\nssize_t\narena_muzzy_decay_ms_get(arena_t *arena) {\n\treturn arena_decay_ms_get(&arena->decay_muzzy);\n}\n\nstatic bool\narena_decay_ms_set(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, ssize_t decay_ms) {\n\tif (!arena_decay_ms_valid(decay_ms)) {\n\t\treturn true;\n\t}\n\n\tmalloc_mutex_lock(tsdn, &decay->mtx);\n\t/*\n\t * Restart decay backlog from scratch, which may cause many dirty pages\n\t * to be immediately purged.  It would conceptually be possible to map\n\t * the old backlog onto the new backlog, but there is no justification\n\t * for such complexity since decay_ms changes are intended to be\n\t * infrequent, either between the {-1, 0, >0} states, or a one-time\n\t * arbitrary change during initial arena configuration.\n\t */\n\tarena_decay_reinit(decay, decay_ms);\n\tarena_maybe_decay(tsdn, arena, decay, extents, false);\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\treturn false;\n}\n\nbool\narena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena,\n    ssize_t decay_ms) {\n\treturn arena_decay_ms_set(tsdn, arena, &arena->decay_dirty,\n\t    &arena->extents_dirty, decay_ms);\n}\n\nbool\narena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena,\n    ssize_t decay_ms) {\n\treturn arena_decay_ms_set(tsdn, arena, &arena->decay_muzzy,\n\t    &arena->extents_muzzy, decay_ms);\n}\n\nstatic size_t\narena_stash_decayed(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_limit,\n\tsize_t npages_decay_max, extent_list_t *decay_extents) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\t/* Stash extents according to npages_limit. */\n\tsize_t nstashed = 0;\n\textent_t *extent;\n\twhile (nstashed < npages_decay_max &&\n\t    (extent = extents_evict(tsdn, arena, r_extent_hooks, extents,\n\t    npages_limit)) != NULL) {\n\t\textent_list_append(decay_extents, extent);\n\t\tnstashed += extent_size_get(extent) >> LG_PAGE;\n\t}\n\treturn nstashed;\n}\n\nstatic size_t\narena_decay_stashed(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, arena_decay_t *decay, extents_t *extents,\n    bool all, extent_list_t *decay_extents, bool is_background_thread) {\n\tUNUSED size_t nmadvise, nunmapped;\n\tsize_t npurged;\n\n\tif (config_stats) {\n\t\tnmadvise = 0;\n\t\tnunmapped = 0;\n\t}\n\tnpurged = 0;\n\n\tssize_t muzzy_decay_ms = arena_muzzy_decay_ms_get(arena);\n\tfor (extent_t *extent = extent_list_first(decay_extents); extent !=\n\t    NULL; extent = extent_list_first(decay_extents)) {\n\t\tif (config_stats) {\n\t\t\tnmadvise++;\n\t\t}\n\t\tsize_t npages = extent_size_get(extent) >> LG_PAGE;\n\t\tnpurged += npages;\n\t\textent_list_remove(decay_extents, extent);\n\t\tswitch (extents_state_get(extents)) {\n\t\tcase extent_state_active:\n\t\t\tnot_reached();\n\t\tcase extent_state_dirty:\n\t\t\tif (!all && muzzy_decay_ms != 0 &&\n\t\t\t    !extent_purge_lazy_wrapper(tsdn, arena,\n\t\t\t    r_extent_hooks, extent, 0,\n\t\t\t    extent_size_get(extent))) {\n\t\t\t\textents_dalloc(tsdn, arena, r_extent_hooks,\n\t\t\t\t    &arena->extents_muzzy, extent);\n\t\t\t\tarena_background_thread_inactivity_check(tsdn,\n\t\t\t\t    arena, is_background_thread);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* Fall through. */\n\t\tcase extent_state_muzzy:\n\t\t\textent_dalloc_wrapper(tsdn, arena, r_extent_hooks,\n\t\t\t    extent);\n\t\t\tif (config_stats) {\n\t\t\t\tnunmapped += npages;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase extent_state_retained:\n\t\tdefault:\n\t\t\tnot_reached();\n\t\t}\n\t}\n\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_stats_add_u64(tsdn, &arena->stats, &decay->stats->npurge,\n\t\t    1);\n\t\tarena_stats_add_u64(tsdn, &arena->stats,\n\t\t    &decay->stats->nmadvise, nmadvise);\n\t\tarena_stats_add_u64(tsdn, &arena->stats, &decay->stats->purged,\n\t\t    npurged);\n\t\tarena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped,\n\t\t    nunmapped << LG_PAGE);\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\n\treturn npurged;\n}\n\n/*\n * npages_limit: Decay at most npages_decay_max pages without violating the\n * invariant: (extents_npages_get(extents) >= npages_limit).  We need an upper\n * bound on number of pages in order to prevent unbounded growth (namely in\n * stashed), otherwise unbounded new pages could be added to extents during the\n * current decay run, so that the purging thread never finishes.\n */\nstatic void\narena_decay_to_limit(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, bool all, size_t npages_limit, size_t npages_decay_max,\n    bool is_background_thread) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 1);\n\tmalloc_mutex_assert_owner(tsdn, &decay->mtx);\n\n\tif (decay->purging) {\n\t\treturn;\n\t}\n\tdecay->purging = true;\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\n\textent_list_t decay_extents;\n\textent_list_init(&decay_extents);\n\n\tsize_t npurge = arena_stash_decayed(tsdn, arena, &extent_hooks, extents,\n\t    npages_limit, npages_decay_max, &decay_extents);\n\tif (npurge != 0) {\n\t\tUNUSED size_t npurged = arena_decay_stashed(tsdn, arena,\n\t\t    &extent_hooks, decay, extents, all, &decay_extents,\n\t\t    is_background_thread);\n\t\tassert(npurged == npurge);\n\t}\n\n\tmalloc_mutex_lock(tsdn, &decay->mtx);\n\tdecay->purging = false;\n}\n\nstatic bool\narena_decay_impl(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, bool is_background_thread, bool all) {\n\tif (all) {\n\t\tmalloc_mutex_lock(tsdn, &decay->mtx);\n\t\tarena_decay_to_limit(tsdn, arena, decay, extents, all, 0,\n\t\t    extents_npages_get(extents), is_background_thread);\n\t\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\t\treturn false;\n\t}\n\n\tif (malloc_mutex_trylock(tsdn, &decay->mtx)) {\n\t\t/* No need to wait if another thread is in progress. */\n\t\treturn true;\n\t}\n\n\tbool epoch_advanced = arena_maybe_decay(tsdn, arena, decay, extents,\n\t    is_background_thread);\n\tUNUSED size_t npages_new;\n\tif (epoch_advanced) {\n\t\t/* Backlog is updated on epoch advance. */\n\t\tnpages_new = decay->backlog[SMOOTHSTEP_NSTEPS-1];\n\t}\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\tif (have_background_thread && background_thread_enabled() &&\n\t    epoch_advanced && !is_background_thread) {\n\t\tbackground_thread_interval_check(tsdn, arena, decay,\n\t\t    npages_new);\n\t}\n\n\treturn false;\n}\n\nstatic bool\narena_decay_dirty(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,\n    bool all) {\n\treturn arena_decay_impl(tsdn, arena, &arena->decay_dirty,\n\t    &arena->extents_dirty, is_background_thread, all);\n}\n\nstatic bool\narena_decay_muzzy(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,\n    bool all) {\n\treturn arena_decay_impl(tsdn, arena, &arena->decay_muzzy,\n\t    &arena->extents_muzzy, is_background_thread, all);\n}\n\nvoid\narena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread, bool all) {\n\tif (arena_decay_dirty(tsdn, arena, is_background_thread, all)) {\n\t\treturn;\n\t}\n\tarena_decay_muzzy(tsdn, arena, is_background_thread, all);\n}\n\nstatic void\narena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *slab) {\n\tarena_nactive_sub(arena, extent_size_get(slab) >> LG_PAGE);\n\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\tarena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, slab);\n}\n\nstatic void\narena_bin_slabs_nonfull_insert(bin_t *bin, extent_t *slab) {\n\tassert(extent_nfree_get(slab) > 0);\n\textent_heap_insert(&bin->slabs_nonfull, slab);\n}\n\nstatic void\narena_bin_slabs_nonfull_remove(bin_t *bin, extent_t *slab) {\n\textent_heap_remove(&bin->slabs_nonfull, slab);\n}\n\nstatic extent_t *\narena_bin_slabs_nonfull_tryget(bin_t *bin) {\n\textent_t *slab = extent_heap_remove_first(&bin->slabs_nonfull);\n\tif (slab == NULL) {\n\t\treturn NULL;\n\t}\n\tif (config_stats) {\n\t\tbin->stats.reslabs++;\n\t}\n\treturn slab;\n}\n\nstatic void\narena_bin_slabs_full_insert(arena_t *arena, bin_t *bin, extent_t *slab) {\n\tassert(extent_nfree_get(slab) == 0);\n\t/*\n\t *  Tracking extents is required by arena_reset, which is not allowed\n\t *  for auto arenas.  Bypass this step to avoid touching the extent\n\t *  linkage (often results in cache misses) for auto arenas.\n\t */\n\tif (arena_is_auto(arena)) {\n\t\treturn;\n\t}\n\textent_list_append(&bin->slabs_full, slab);\n}\n\nstatic void\narena_bin_slabs_full_remove(arena_t *arena, bin_t *bin, extent_t *slab) {\n\tif (arena_is_auto(arena)) {\n\t\treturn;\n\t}\n\textent_list_remove(&bin->slabs_full, slab);\n}\n\nvoid\narena_reset(tsd_t *tsd, arena_t *arena) {\n\t/*\n\t * Locking in this function is unintuitive.  The caller guarantees that\n\t * no concurrent operations are happening in this arena, but there are\n\t * still reasons that some locking is necessary:\n\t *\n\t * - Some of the functions in the transitive closure of calls assume\n\t *   appropriate locks are held, and in some cases these locks are\n\t *   temporarily dropped to avoid lock order reversal or deadlock due to\n\t *   reentry.\n\t * - mallctl(\"epoch\", ...) may concurrently refresh stats.  While\n\t *   strictly speaking this is a \"concurrent operation\", disallowing\n\t *   stats refreshes would impose an inconvenient burden.\n\t */\n\n\t/* Large allocations. */\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx);\n\n\tfor (extent_t *extent = extent_list_first(&arena->large); extent !=\n\t    NULL; extent = extent_list_first(&arena->large)) {\n\t\tvoid *ptr = extent_base_get(extent);\n\t\tsize_t usize;\n\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);\n\t\talloc_ctx_t alloc_ctx;\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\t\tassert(alloc_ctx.szind != NSIZES);\n\n\t\tif (config_stats || (config_prof && opt_prof)) {\n\t\t\tusize = sz_index2size(alloc_ctx.szind);\n\t\t\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\t\t}\n\t\t/* Remove large allocation from prof sample set. */\n\t\tif (config_prof && opt_prof) {\n\t\t\tprof_free(tsd, ptr, usize, &alloc_ctx);\n\t\t}\n\t\tlarge_dalloc(tsd_tsdn(tsd), extent);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx);\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);\n\n\t/* Bins. */\n\tfor (unsigned i = 0; i < NBINS; i++) {\n\t\textent_t *slab;\n\t\tbin_t *bin = &arena->bins[i];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\tif (bin->slabcur != NULL) {\n\t\t\tslab = bin->slabcur;\n\t\t\tbin->slabcur = NULL;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\t\tarena_slab_dalloc(tsd_tsdn(tsd), arena, slab);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\t}\n\t\twhile ((slab = extent_heap_remove_first(&bin->slabs_nonfull)) !=\n\t\t    NULL) {\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\t\tarena_slab_dalloc(tsd_tsdn(tsd), arena, slab);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\t}\n\t\tfor (slab = extent_list_first(&bin->slabs_full); slab != NULL;\n\t\t    slab = extent_list_first(&bin->slabs_full)) {\n\t\t\tarena_bin_slabs_full_remove(arena, bin, slab);\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\t\tarena_slab_dalloc(tsd_tsdn(tsd), arena, slab);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\t}\n\t\tif (config_stats) {\n\t\t\tbin->stats.curregs = 0;\n\t\t\tbin->stats.curslabs = 0;\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t}\n\n\tatomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);\n}\n\nstatic void\narena_destroy_retained(tsdn_t *tsdn, arena_t *arena) {\n\t/*\n\t * Iterate over the retained extents and destroy them.  This gives the\n\t * extent allocator underlying the extent hooks an opportunity to unmap\n\t * all retained memory without having to keep its own metadata\n\t * structures.  In practice, virtual memory for dss-allocated extents is\n\t * leaked here, so best practice is to avoid dss for arenas to be\n\t * destroyed, or provide custom extent hooks that track retained\n\t * dss-based extents for later reuse.\n\t */\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\textent_t *extent;\n\twhile ((extent = extents_evict(tsdn, arena, &extent_hooks,\n\t    &arena->extents_retained, 0)) != NULL) {\n\t\textent_destroy_wrapper(tsdn, arena, &extent_hooks, extent);\n\t}\n}\n\nvoid\narena_destroy(tsd_t *tsd, arena_t *arena) {\n\tassert(base_ind_get(arena->base) >= narenas_auto);\n\tassert(arena_nthreads_get(arena, false) == 0);\n\tassert(arena_nthreads_get(arena, true) == 0);\n\n\t/*\n\t * No allocations have occurred since arena_reset() was called.\n\t * Furthermore, the caller (arena_i_destroy_ctl()) purged all cached\n\t * extents, so only retained extents may remain.\n\t */\n\tassert(extents_npages_get(&arena->extents_dirty) == 0);\n\tassert(extents_npages_get(&arena->extents_muzzy) == 0);\n\n\t/* Deallocate retained memory. */\n\tarena_destroy_retained(tsd_tsdn(tsd), arena);\n\n\t/*\n\t * Remove the arena pointer from the arenas array.  We rely on the fact\n\t * that there is no way for the application to get a dirty read from the\n\t * arenas array unless there is an inherent race in the application\n\t * involving access of an arena being concurrently destroyed.  The\n\t * application must synchronize knowledge of the arena's validity, so as\n\t * long as we use an atomic write to update the arenas array, the\n\t * application will get a clean read any time after it synchronizes\n\t * knowledge that the arena is no longer valid.\n\t */\n\tarena_set(base_ind_get(arena->base), NULL);\n\n\t/*\n\t * Destroy the base allocator, which manages all metadata ever mapped by\n\t * this arena.\n\t */\n\tbase_delete(tsd_tsdn(tsd), arena->base);\n}\n\nstatic extent_t *\narena_slab_alloc_hard(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, const bin_info_t *bin_info,\n    szind_t szind) {\n\textent_t *slab;\n\tbool zero, commit;\n\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tzero = false;\n\tcommit = true;\n\tslab = extent_alloc_wrapper(tsdn, arena, r_extent_hooks, NULL,\n\t    bin_info->slab_size, 0, PAGE, true, szind, &zero, &commit);\n\n\tif (config_stats && slab != NULL) {\n\t\tarena_stats_mapped_add(tsdn, &arena->stats,\n\t\t    bin_info->slab_size);\n\t}\n\n\treturn slab;\n}\n\nstatic extent_t *\narena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind,\n    const bin_info_t *bin_info) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\tszind_t szind = sz_size2index(bin_info->reg_size);\n\tbool zero = false;\n\tbool commit = true;\n\textent_t *slab = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_dirty, NULL, bin_info->slab_size, 0, PAGE, true,\n\t    binind, &zero, &commit);\n\tif (slab == NULL) {\n\t\tslab = extents_alloc(tsdn, arena, &extent_hooks,\n\t\t    &arena->extents_muzzy, NULL, bin_info->slab_size, 0, PAGE,\n\t\t    true, binind, &zero, &commit);\n\t}\n\tif (slab == NULL) {\n\t\tslab = arena_slab_alloc_hard(tsdn, arena, &extent_hooks,\n\t\t    bin_info, szind);\n\t\tif (slab == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tassert(extent_slab_get(slab));\n\n\t/* Initialize slab internals. */\n\tarena_slab_data_t *slab_data = extent_slab_data_get(slab);\n\textent_nfree_set(slab, bin_info->nregs);\n\tbitmap_init(slab_data->bitmap, &bin_info->bitmap_info, false);\n\n\tarena_nactive_add(arena, extent_size_get(slab) >> LG_PAGE);\n\n\treturn slab;\n}\n\nstatic extent_t *\narena_bin_nonfull_slab_get(tsdn_t *tsdn, arena_t *arena, bin_t *bin,\n    szind_t binind) {\n\textent_t *slab;\n\tconst bin_info_t *bin_info;\n\n\t/* Look for a usable slab. */\n\tslab = arena_bin_slabs_nonfull_tryget(bin);\n\tif (slab != NULL) {\n\t\treturn slab;\n\t}\n\t/* No existing slabs have any space available. */\n\n\tbin_info = &bin_infos[binind];\n\n\t/* Allocate a new slab. */\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t/******************************/\n\tslab = arena_slab_alloc(tsdn, arena, binind, bin_info);\n\t/********************************/\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tif (slab != NULL) {\n\t\tif (config_stats) {\n\t\t\tbin->stats.nslabs++;\n\t\t\tbin->stats.curslabs++;\n\t\t}\n\t\treturn slab;\n\t}\n\n\t/*\n\t * arena_slab_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\tslab = arena_bin_slabs_nonfull_tryget(bin);\n\tif (slab != NULL) {\n\t\treturn slab;\n\t}\n\n\treturn NULL;\n}\n\n/* Re-fill bin->slabcur, then call arena_slab_reg_alloc(). */\nstatic void *\narena_bin_malloc_hard(tsdn_t *tsdn, arena_t *arena, bin_t *bin,\n    szind_t binind) {\n\tconst bin_info_t *bin_info;\n\textent_t *slab;\n\n\tbin_info = &bin_infos[binind];\n\tif (!arena_is_auto(arena) && bin->slabcur != NULL) {\n\t\tarena_bin_slabs_full_insert(arena, bin, bin->slabcur);\n\t\tbin->slabcur = NULL;\n\t}\n\tslab = arena_bin_nonfull_slab_get(tsdn, arena, bin, binind);\n\tif (bin->slabcur != NULL) {\n\t\t/*\n\t\t * Another thread updated slabcur while this one ran without the\n\t\t * bin lock in arena_bin_nonfull_slab_get().\n\t\t */\n\t\tif (extent_nfree_get(bin->slabcur) > 0) {\n\t\t\tvoid *ret = arena_slab_reg_alloc(bin->slabcur,\n\t\t\t    bin_info);\n\t\t\tif (slab != NULL) {\n\t\t\t\t/*\n\t\t\t\t * arena_slab_alloc() may have allocated slab,\n\t\t\t\t * or it may have been pulled from\n\t\t\t\t * slabs_nonfull.  Therefore it is unsafe to\n\t\t\t\t * make any assumptions about how slab has\n\t\t\t\t * previously been used, and\n\t\t\t\t * arena_bin_lower_slab() must be called, as if\n\t\t\t\t * a region were just deallocated from the slab.\n\t\t\t\t */\n\t\t\t\tif (extent_nfree_get(slab) == bin_info->nregs) {\n\t\t\t\t\tarena_dalloc_bin_slab(tsdn, arena, slab,\n\t\t\t\t\t    bin);\n\t\t\t\t} else {\n\t\t\t\t\tarena_bin_lower_slab(tsdn, arena, slab,\n\t\t\t\t\t    bin);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tarena_bin_slabs_full_insert(arena, bin, bin->slabcur);\n\t\tbin->slabcur = NULL;\n\t}\n\n\tif (slab == NULL) {\n\t\treturn NULL;\n\t}\n\tbin->slabcur = slab;\n\n\tassert(extent_nfree_get(bin->slabcur) > 0);\n\n\treturn arena_slab_reg_alloc(slab, bin_info);\n}\n\nvoid\narena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes) {\n\tunsigned i, nfill;\n\tbin_t *bin;\n\n\tassert(tbin->ncached == 0);\n\n\tif (config_prof && arena_prof_accum(tsdn, arena, prof_accumbytes)) {\n\t\tprof_idump(tsdn);\n\t}\n\tbin = &arena->bins[binind];\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tfor (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>\n\t    tcache->lg_fill_div[binind]); i < nfill; i++) {\n\t\textent_t *slab;\n\t\tvoid *ptr;\n\t\tif ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) >\n\t\t    0) {\n\t\t\tptr = arena_slab_reg_alloc(slab, &bin_infos[binind]);\n\t\t} else {\n\t\t\tptr = arena_bin_malloc_hard(tsdn, arena, bin, binind);\n\t\t}\n\t\tif (ptr == NULL) {\n\t\t\t/*\n\t\t\t * OOM.  tbin->avail isn't yet filled down to its first\n\t\t\t * element, so the successful allocations (if any) must\n\t\t\t * be moved just before tbin->avail before bailing out.\n\t\t\t */\n\t\t\tif (i > 0) {\n\t\t\t\tmemmove(tbin->avail - i, tbin->avail - nfill,\n\t\t\t\t    i * sizeof(void *));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (config_fill && unlikely(opt_junk_alloc)) {\n\t\t\tarena_alloc_junk_small(ptr, &bin_infos[binind], true);\n\t\t}\n\t\t/* Insert such that low regions get used first. */\n\t\t*(tbin->avail - nfill + i) = ptr;\n\t}\n\tif (config_stats) {\n\t\tbin->stats.nmalloc += i;\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\tbin->stats.curregs += i;\n\t\tbin->stats.nfills++;\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\ttbin->ncached = i;\n\tarena_decay_tick(tsdn, arena);\n}\n\nvoid\narena_alloc_junk_small(void *ptr, const bin_info_t *bin_info, bool zero) {\n\tif (!zero) {\n\t\tmemset(ptr, JEMALLOC_ALLOC_JUNK, bin_info->reg_size);\n\t}\n}\n\nstatic void\narena_dalloc_junk_small_impl(void *ptr, const bin_info_t *bin_info) {\n\tmemset(ptr, JEMALLOC_FREE_JUNK, bin_info->reg_size);\n}\narena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small =\n    arena_dalloc_junk_small_impl;\n\nstatic void *\narena_malloc_small(tsdn_t *tsdn, arena_t *arena, szind_t binind, bool zero) {\n\tvoid *ret;\n\tbin_t *bin;\n\tsize_t usize;\n\textent_t *slab;\n\n\tassert(binind < NBINS);\n\tbin = &arena->bins[binind];\n\tusize = sz_index2size(binind);\n\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tif ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) > 0) {\n\t\tret = arena_slab_reg_alloc(slab, &bin_infos[binind]);\n\t} else {\n\t\tret = arena_bin_malloc_hard(tsdn, arena, bin, binind);\n\t}\n\n\tif (ret == NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t\treturn NULL;\n\t}\n\n\tif (config_stats) {\n\t\tbin->stats.nmalloc++;\n\t\tbin->stats.nrequests++;\n\t\tbin->stats.curregs++;\n\t}\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\tif (config_prof && arena_prof_accum(tsdn, arena, usize)) {\n\t\tprof_idump(tsdn);\n\t}\n\n\tif (!zero) {\n\t\tif (config_fill) {\n\t\t\tif (unlikely(opt_junk_alloc)) {\n\t\t\t\tarena_alloc_junk_small(ret,\n\t\t\t\t    &bin_infos[binind], false);\n\t\t\t} else if (unlikely(opt_zero)) {\n\t\t\t\tmemset(ret, 0, usize);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (config_fill && unlikely(opt_junk_alloc)) {\n\t\t\tarena_alloc_junk_small(ret, &bin_infos[binind],\n\t\t\t    true);\n\t\t}\n\t\tmemset(ret, 0, usize);\n\t}\n\n\tarena_decay_tick(tsdn, arena);\n\treturn ret;\n}\n\nvoid *\narena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind,\n    bool zero) {\n\tassert(!tsdn_null(tsdn) || arena != NULL);\n\n\tif (likely(!tsdn_null(tsdn))) {\n\t\tarena = arena_choose(tsdn_tsd(tsdn), arena);\n\t}\n\tif (unlikely(arena == NULL)) {\n\t\treturn NULL;\n\t}\n\n\tif (likely(size <= SMALL_MAXCLASS)) {\n\t\treturn arena_malloc_small(tsdn, arena, ind, zero);\n\t}\n\treturn large_malloc(tsdn, arena, sz_index2size(ind), zero);\n}\n\nvoid *\narena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,\n    bool zero, tcache_t *tcache) {\n\tvoid *ret;\n\n\tif (usize <= SMALL_MAXCLASS && (alignment < PAGE || (alignment == PAGE\n\t    && (usize & PAGE_MASK) == 0))) {\n\t\t/* Small; alignment doesn't require special slab placement. */\n\t\tret = arena_malloc(tsdn, arena, usize, sz_size2index(usize),\n\t\t    zero, tcache, true);\n\t} else {\n\t\tif (likely(alignment <= CACHELINE)) {\n\t\t\tret = large_malloc(tsdn, arena, usize, zero);\n\t\t} else {\n\t\t\tret = large_palloc(tsdn, arena, usize, alignment, zero);\n\t\t}\n\t}\n\treturn ret;\n}\n\nvoid\narena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(isalloc(tsdn, ptr) == LARGE_MINCLASS);\n\tassert(usize <= SMALL_MAXCLASS);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent = rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true);\n\tarena_t *arena = extent_arena_get(extent);\n\n\tszind_t szind = sz_size2index(usize);\n\textent_szind_set(extent, szind);\n\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,\n\t    szind, false);\n\n\tprof_accum_cancel(tsdn, &arena->prof_accum, usize);\n\n\tassert(isalloc(tsdn, ptr) == usize);\n}\n\nstatic size_t\narena_prof_demote(tsdn_t *tsdn, extent_t *extent, const void *ptr) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\textent_szind_set(extent, NBINS);\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,\n\t    NBINS, false);\n\n\tassert(isalloc(tsdn, ptr) == LARGE_MINCLASS);\n\n\treturn LARGE_MINCLASS;\n}\n\nvoid\narena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,\n    bool slow_path) {\n\tcassert(config_prof);\n\tassert(opt_prof);\n\n\textent_t *extent = iealloc(tsdn, ptr);\n\tsize_t usize = arena_prof_demote(tsdn, extent, ptr);\n\tif (usize <= tcache_maxclass) {\n\t\ttcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,\n\t\t    sz_size2index(usize), slow_path);\n\t} else {\n\t\tlarge_dalloc(tsdn, extent);\n\t}\n}\n\nstatic void\narena_dissociate_bin_slab(arena_t *arena, extent_t *slab, bin_t *bin) {\n\t/* Dissociate slab from bin. */\n\tif (slab == bin->slabcur) {\n\t\tbin->slabcur = NULL;\n\t} else {\n\t\tszind_t binind = extent_szind_get(slab);\n\t\tconst bin_info_t *bin_info = &bin_infos[binind];\n\n\t\t/*\n\t\t * The following block's conditional is necessary because if the\n\t\t * slab only contains one region, then it never gets inserted\n\t\t * into the non-full slabs heap.\n\t\t */\n\t\tif (bin_info->nregs == 1) {\n\t\t\tarena_bin_slabs_full_remove(arena, bin, slab);\n\t\t} else {\n\t\t\tarena_bin_slabs_nonfull_remove(bin, slab);\n\t\t}\n\t}\n}\n\nstatic void\narena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin) {\n\tassert(slab != bin->slabcur);\n\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t/******************************/\n\tarena_slab_dalloc(tsdn, arena, slab);\n\t/****************************/\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tif (config_stats) {\n\t\tbin->stats.curslabs--;\n\t}\n}\n\nstatic void\narena_bin_lower_slab(UNUSED tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin) {\n\tassert(extent_nfree_get(slab) > 0);\n\n\t/*\n\t * Make sure that if bin->slabcur is non-NULL, it refers to the\n\t * oldest/lowest non-full slab.  It is okay to NULL slabcur out rather\n\t * than proactively keeping it pointing at the oldest/lowest non-full\n\t * slab.\n\t */\n\tif (bin->slabcur != NULL && extent_snad_comp(bin->slabcur, slab) > 0) {\n\t\t/* Switch slabcur. */\n\t\tif (extent_nfree_get(bin->slabcur) > 0) {\n\t\t\tarena_bin_slabs_nonfull_insert(bin, bin->slabcur);\n\t\t} else {\n\t\t\tarena_bin_slabs_full_insert(arena, bin, bin->slabcur);\n\t\t}\n\t\tbin->slabcur = slab;\n\t\tif (config_stats) {\n\t\t\tbin->stats.reslabs++;\n\t\t}\n\t} else {\n\t\tarena_bin_slabs_nonfull_insert(bin, slab);\n\t}\n}\n\nstatic void\narena_dalloc_bin_locked_impl(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    void *ptr, bool junked) {\n\tarena_slab_data_t *slab_data = extent_slab_data_get(slab);\n\tszind_t binind = extent_szind_get(slab);\n\tbin_t *bin = &arena->bins[binind];\n\tconst bin_info_t *bin_info = &bin_infos[binind];\n\n\tif (!junked && config_fill && unlikely(opt_junk_free)) {\n\t\tarena_dalloc_junk_small(ptr, bin_info);\n\t}\n\n\tarena_slab_reg_dalloc(slab, slab_data, ptr);\n\tunsigned nfree = extent_nfree_get(slab);\n\tif (nfree == bin_info->nregs) {\n\t\tarena_dissociate_bin_slab(arena, slab, bin);\n\t\tarena_dalloc_bin_slab(tsdn, arena, slab, bin);\n\t} else if (nfree == 1 && slab != bin->slabcur) {\n\t\tarena_bin_slabs_full_remove(arena, bin, slab);\n\t\tarena_bin_lower_slab(tsdn, arena, slab, bin);\n\t}\n\n\tif (config_stats) {\n\t\tbin->stats.ndalloc++;\n\t\tbin->stats.curregs--;\n\t}\n}\n\nvoid\narena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    void *ptr) {\n\tarena_dalloc_bin_locked_impl(tsdn, arena, extent, ptr, true);\n}\n\nstatic void\narena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, extent_t *extent, void *ptr) {\n\tszind_t binind = extent_szind_get(extent);\n\tbin_t *bin = &arena->bins[binind];\n\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tarena_dalloc_bin_locked_impl(tsdn, arena, extent, ptr, false);\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n}\n\nvoid\narena_dalloc_small(tsdn_t *tsdn, void *ptr) {\n\textent_t *extent = iealloc(tsdn, ptr);\n\tarena_t *arena = extent_arena_get(extent);\n\n\tarena_dalloc_bin(tsdn, arena, extent, ptr);\n\tarena_decay_tick(tsdn, arena);\n}\n\nbool\narena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,\n    size_t extra, bool zero) {\n\t/* Calls with non-zero extra had to clamp extra. */\n\tassert(extra == 0 || size + extra <= LARGE_MAXCLASS);\n\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\treturn true;\n\t}\n\n\textent_t *extent = iealloc(tsdn, ptr);\n\tsize_t usize_min = sz_s2u(size);\n\tsize_t usize_max = sz_s2u(size + extra);\n\tif (likely(oldsize <= SMALL_MAXCLASS && usize_min <= SMALL_MAXCLASS)) {\n\t\t/*\n\t\t * Avoid moving the allocation if the size class can be left the\n\t\t * same.\n\t\t */\n\t\tassert(bin_infos[sz_size2index(oldsize)].reg_size ==\n\t\t    oldsize);\n\t\tif ((usize_max > SMALL_MAXCLASS || sz_size2index(usize_max) !=\n\t\t    sz_size2index(oldsize)) && (size > oldsize || usize_max <\n\t\t    oldsize)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\treturn false;\n\t} else if (oldsize >= LARGE_MINCLASS && usize_max >= LARGE_MINCLASS) {\n\t\treturn large_ralloc_no_move(tsdn, extent, usize_min, usize_max,\n\t\t    zero);\n\t}\n\n\treturn true;\n}\n\nstatic void *\narena_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache) {\n\tif (alignment == 0) {\n\t\treturn arena_malloc(tsdn, arena, usize, sz_size2index(usize),\n\t\t    zero, tcache, true);\n\t}\n\tusize = sz_sa2u(usize, alignment);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\treturn ipalloct(tsdn, usize, alignment, zero, tcache, arena);\n}\n\nvoid *\narena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,\n    size_t size, size_t alignment, bool zero, tcache_t *tcache) {\n\tsize_t usize = sz_s2u(size);\n\tif (unlikely(usize == 0 || size > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\n\tif (likely(usize <= SMALL_MAXCLASS)) {\n\t\t/* Try to avoid moving the allocation. */\n\t\tif (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero)) {\n\t\t\treturn ptr;\n\t\t}\n\t}\n\n\tif (oldsize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS) {\n\t\treturn large_ralloc(tsdn, arena, iealloc(tsdn, ptr), usize,\n\t\t    alignment, zero, tcache);\n\t}\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 copying.\n\t */\n\tvoid *ret = arena_ralloc_move_helper(tsdn, arena, usize, alignment,\n\t    zero, tcache);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Junk/zero-filling were already done by\n\t * ipalloc()/arena_malloc().\n\t */\n\n\tsize_t copysize = (usize < oldsize) ? usize : oldsize;\n\tmemcpy(ret, ptr, copysize);\n\tisdalloct(tsdn, ptr, oldsize, tcache, NULL, true);\n\treturn ret;\n}\n\ndss_prec_t\narena_dss_prec_get(arena_t *arena) {\n\treturn (dss_prec_t)atomic_load_u(&arena->dss_prec, ATOMIC_ACQUIRE);\n}\n\nbool\narena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) {\n\tif (!have_dss) {\n\t\treturn (dss_prec != dss_prec_disabled);\n\t}\n\tatomic_store_u(&arena->dss_prec, (unsigned)dss_prec, ATOMIC_RELEASE);\n\treturn false;\n}\n\nssize_t\narena_dirty_decay_ms_default_get(void) {\n\treturn atomic_load_zd(&dirty_decay_ms_default, ATOMIC_RELAXED);\n}\n\nbool\narena_dirty_decay_ms_default_set(ssize_t decay_ms) {\n\tif (!arena_decay_ms_valid(decay_ms)) {\n\t\treturn true;\n\t}\n\tatomic_store_zd(&dirty_decay_ms_default, decay_ms, ATOMIC_RELAXED);\n\treturn false;\n}\n\nssize_t\narena_muzzy_decay_ms_default_get(void) {\n\treturn atomic_load_zd(&muzzy_decay_ms_default, ATOMIC_RELAXED);\n}\n\nbool\narena_muzzy_decay_ms_default_set(ssize_t decay_ms) {\n\tif (!arena_decay_ms_valid(decay_ms)) {\n\t\treturn true;\n\t}\n\tatomic_store_zd(&muzzy_decay_ms_default, decay_ms, ATOMIC_RELAXED);\n\treturn false;\n}\n\nbool\narena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena, size_t *old_limit,\n    size_t *new_limit) {\n\tassert(opt_retain);\n\n\tpszind_t new_ind JEMALLOC_CC_SILENCE_INIT(0);\n\tif (new_limit != NULL) {\n\t\tsize_t limit = *new_limit;\n\t\t/* Grow no more than the new limit. */\n\t\tif ((new_ind = sz_psz2ind(limit + 1) - 1) >\n\t\t     EXTENT_GROW_MAX_PIND) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &arena->extent_grow_mtx);\n\tif (old_limit != NULL) {\n\t\t*old_limit = sz_pind2sz(arena->retain_grow_limit);\n\t}\n\tif (new_limit != NULL) {\n\t\tarena->retain_grow_limit = new_ind;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arena->extent_grow_mtx);\n\n\treturn false;\n}\n\nunsigned\narena_nthreads_get(arena_t *arena, bool internal) {\n\treturn atomic_load_u(&arena->nthreads[internal], ATOMIC_RELAXED);\n}\n\nvoid\narena_nthreads_inc(arena_t *arena, bool internal) {\n\tatomic_fetch_add_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED);\n}\n\nvoid\narena_nthreads_dec(arena_t *arena, bool internal) {\n\tatomic_fetch_sub_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED);\n}\n\nsize_t\narena_extent_sn_next(arena_t *arena) {\n\treturn atomic_fetch_add_zu(&arena->extent_sn_next, 1, ATOMIC_RELAXED);\n}\n\narena_t *\narena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tarena_t *arena;\n\tbase_t *base;\n\tunsigned i;\n\n\tif (ind == 0) {\n\t\tbase = b0get();\n\t} else {\n\t\tbase = base_new(tsdn, ind, extent_hooks);\n\t\tif (base == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tarena = (arena_t *)base_alloc(tsdn, base, sizeof(arena_t), CACHELINE);\n\tif (arena == NULL) {\n\t\tgoto label_error;\n\t}\n\n\tatomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED);\n\tatomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED);\n\tarena->last_thd = NULL;\n\n\tif (config_stats) {\n\t\tif (arena_stats_init(tsdn, &arena->stats)) {\n\t\t\tgoto label_error;\n\t\t}\n\n\t\tql_new(&arena->tcache_ql);\n\t\tql_new(&arena->cache_bin_array_descriptor_ql);\n\t\tif (malloc_mutex_init(&arena->tcache_ql_mtx, \"tcache_ql\",\n\t\t    WITNESS_RANK_TCACHE_QL, malloc_mutex_rank_exclusive)) {\n\t\t\tgoto label_error;\n\t\t}\n\t}\n\n\tif (config_prof) {\n\t\tif (prof_accum_init(tsdn, &arena->prof_accum)) {\n\t\t\tgoto label_error;\n\t\t}\n\t}\n\n\tif (config_cache_oblivious) {\n\t\t/*\n\t\t * A nondeterministic seed based on the address of arena reduces\n\t\t * the likelihood of lockstep non-uniform cache index\n\t\t * utilization among identical concurrent processes, but at the\n\t\t * cost of test repeatability.  For debug builds, instead use a\n\t\t * deterministic seed.\n\t\t */\n\t\tatomic_store_zu(&arena->offset_state, config_debug ? ind :\n\t\t    (size_t)(uintptr_t)arena, ATOMIC_RELAXED);\n\t}\n\n\tatomic_store_zu(&arena->extent_sn_next, 0, ATOMIC_RELAXED);\n\n\tatomic_store_u(&arena->dss_prec, (unsigned)extent_dss_prec_get(),\n\t    ATOMIC_RELAXED);\n\n\tatomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);\n\n\textent_list_init(&arena->large);\n\tif (malloc_mutex_init(&arena->large_mtx, \"arena_large\",\n\t    WITNESS_RANK_ARENA_LARGE, malloc_mutex_rank_exclusive)) {\n\t\tgoto label_error;\n\t}\n\n\t/*\n\t * Delay coalescing for dirty extents despite the disruptive effect on\n\t * memory layout for best-fit extent allocation, since cached extents\n\t * are likely to be reused soon after deallocation, and the cost of\n\t * merging/splitting extents is non-trivial.\n\t */\n\tif (extents_init(tsdn, &arena->extents_dirty, extent_state_dirty,\n\t    true)) {\n\t\tgoto label_error;\n\t}\n\t/*\n\t * Coalesce muzzy extents immediately, because operations on them are in\n\t * the critical path much less often than for dirty extents.\n\t */\n\tif (extents_init(tsdn, &arena->extents_muzzy, extent_state_muzzy,\n\t    false)) {\n\t\tgoto label_error;\n\t}\n\t/*\n\t * Coalesce retained extents immediately, in part because they will\n\t * never be evicted (and therefore there's no opportunity for delayed\n\t * coalescing), but also because operations on retained extents are not\n\t * in the critical path.\n\t */\n\tif (extents_init(tsdn, &arena->extents_retained, extent_state_retained,\n\t    false)) {\n\t\tgoto label_error;\n\t}\n\n\tif (arena_decay_init(&arena->decay_dirty,\n\t    arena_dirty_decay_ms_default_get(), &arena->stats.decay_dirty)) {\n\t\tgoto label_error;\n\t}\n\tif (arena_decay_init(&arena->decay_muzzy,\n\t    arena_muzzy_decay_ms_default_get(), &arena->stats.decay_muzzy)) {\n\t\tgoto label_error;\n\t}\n\n\tarena->extent_grow_next = sz_psz2ind(HUGEPAGE);\n\tarena->retain_grow_limit = EXTENT_GROW_MAX_PIND;\n\tif (malloc_mutex_init(&arena->extent_grow_mtx, \"extent_grow\",\n\t    WITNESS_RANK_EXTENT_GROW, malloc_mutex_rank_exclusive)) {\n\t\tgoto label_error;\n\t}\n\n\textent_avail_new(&arena->extent_avail);\n\tif (malloc_mutex_init(&arena->extent_avail_mtx, \"extent_avail\",\n\t    WITNESS_RANK_EXTENT_AVAIL, malloc_mutex_rank_exclusive)) {\n\t\tgoto label_error;\n\t}\n\n\t/* Initialize bins. */\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbool err = bin_init(&arena->bins[i]);\n\t\tif (err) {\n\t\t\tgoto label_error;\n\t\t}\n\t}\n\n\tarena->base = base;\n\t/* Set arena before creating background threads. */\n\tarena_set(ind, arena);\n\n\tnstime_init(&arena->create_time, 0);\n\tnstime_update(&arena->create_time);\n\n\t/* We don't support reentrancy for arena 0 bootstrapping. */\n\tif (ind != 0) {\n\t\t/*\n\t\t * If we're here, then arena 0 already exists, so bootstrapping\n\t\t * is done enough that we should have tsd.\n\t\t */\n\t\tassert(!tsdn_null(tsdn));\n\t\tpre_reentrancy(tsdn_tsd(tsdn), arena);\n\t\tif (hooks_arena_new_hook) {\n\t\t\thooks_arena_new_hook();\n\t\t}\n\t\tpost_reentrancy(tsdn_tsd(tsdn));\n\t}\n\n\treturn arena;\nlabel_error:\n\tif (ind != 0) {\n\t\tbase_delete(tsdn, base);\n\t}\n\treturn NULL;\n}\n\nvoid\narena_boot(void) {\n\tarena_dirty_decay_ms_default_set(opt_dirty_decay_ms);\n\tarena_muzzy_decay_ms_default_set(opt_muzzy_decay_ms);\n#define REGIND_bin_yes(index, reg_size) \t\t\t\t\\\n\tdiv_init(&arena_binind_div_info[(index)], (reg_size));\n#define REGIND_bin_no(index, reg_size)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs,\t\t\\\n    lg_delta_lookup)\t\t\t\t\t\t\t\\\n\tREGIND_bin_##bin(index, (1U<<lg_grp) + (ndelta << lg_delta))\n\tSIZE_CLASSES\n#undef REGIND_bin_yes\n#undef REGIND_bin_no\n#undef SC\n}\n\nvoid\narena_prefork0(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_prefork(tsdn, &arena->decay_muzzy.mtx);\n}\n\nvoid\narena_prefork1(tsdn_t *tsdn, arena_t *arena) {\n\tif (config_stats) {\n\t\tmalloc_mutex_prefork(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n\nvoid\narena_prefork2(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->extent_grow_mtx);\n}\n\nvoid\narena_prefork3(tsdn_t *tsdn, arena_t *arena) {\n\textents_prefork(tsdn, &arena->extents_dirty);\n\textents_prefork(tsdn, &arena->extents_muzzy);\n\textents_prefork(tsdn, &arena->extents_retained);\n}\n\nvoid\narena_prefork4(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->extent_avail_mtx);\n}\n\nvoid\narena_prefork5(tsdn_t *tsdn, arena_t *arena) {\n\tbase_prefork(tsdn, arena->base);\n}\n\nvoid\narena_prefork6(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->large_mtx);\n}\n\nvoid\narena_prefork7(tsdn_t *tsdn, arena_t *arena) {\n\tfor (unsigned i = 0; i < NBINS; i++) {\n\t\tbin_prefork(tsdn, &arena->bins[i]);\n\t}\n}\n\nvoid\narena_postfork_parent(tsdn_t *tsdn, arena_t *arena) {\n\tunsigned i;\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin_postfork_parent(tsdn, &arena->bins[i]);\n\t}\n\tmalloc_mutex_postfork_parent(tsdn, &arena->large_mtx);\n\tbase_postfork_parent(tsdn, arena->base);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->extent_avail_mtx);\n\textents_postfork_parent(tsdn, &arena->extents_dirty);\n\textents_postfork_parent(tsdn, &arena->extents_muzzy);\n\textents_postfork_parent(tsdn, &arena->extents_retained);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->extent_grow_mtx);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->decay_muzzy.mtx);\n\tif (config_stats) {\n\t\tmalloc_mutex_postfork_parent(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n\nvoid\narena_postfork_child(tsdn_t *tsdn, arena_t *arena) {\n\tunsigned i;\n\n\tatomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED);\n\tatomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED);\n\tif (tsd_arena_get(tsdn_tsd(tsdn)) == arena) {\n\t\tarena_nthreads_inc(arena, false);\n\t}\n\tif (tsd_iarena_get(tsdn_tsd(tsdn)) == arena) {\n\t\tarena_nthreads_inc(arena, true);\n\t}\n\tif (config_stats) {\n\t\tql_new(&arena->tcache_ql);\n\t\tql_new(&arena->cache_bin_array_descriptor_ql);\n\t\ttcache_t *tcache = tcache_get(tsdn_tsd(tsdn));\n\t\tif (tcache != NULL && tcache->arena == arena) {\n\t\t\tql_elm_new(tcache, link);\n\t\t\tql_tail_insert(&arena->tcache_ql, tcache, link);\n\t\t\tcache_bin_array_descriptor_init(\n\t\t\t    &tcache->cache_bin_array_descriptor,\n\t\t\t    tcache->bins_small, tcache->bins_large);\n\t\t\tql_tail_insert(&arena->cache_bin_array_descriptor_ql,\n\t\t\t    &tcache->cache_bin_array_descriptor, link);\n\t\t}\n\t}\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin_postfork_child(tsdn, &arena->bins[i]);\n\t}\n\tmalloc_mutex_postfork_child(tsdn, &arena->large_mtx);\n\tbase_postfork_child(tsdn, arena->base);\n\tmalloc_mutex_postfork_child(tsdn, &arena->extent_avail_mtx);\n\textents_postfork_child(tsdn, &arena->extents_dirty);\n\textents_postfork_child(tsdn, &arena->extents_muzzy);\n\textents_postfork_child(tsdn, &arena->extents_retained);\n\tmalloc_mutex_postfork_child(tsdn, &arena->extent_grow_mtx);\n\tmalloc_mutex_postfork_child(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_postfork_child(tsdn, &arena->decay_muzzy.mtx);\n\tif (config_stats) {\n\t\tmalloc_mutex_postfork_child(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/background_thread.c",
    "content": "#define JEMALLOC_BACKGROUND_THREAD_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/* This option should be opt-in only. */\n#define BACKGROUND_THREAD_DEFAULT false\n/* Read-only after initialization. */\nbool opt_background_thread = BACKGROUND_THREAD_DEFAULT;\nsize_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT;\n\n/* Used for thread creation, termination and stats. */\nmalloc_mutex_t background_thread_lock;\n/* Indicates global state.  Atomic because decay reads this w/o locking. */\natomic_b_t background_thread_enabled_state;\nsize_t n_background_threads;\nsize_t max_background_threads;\n/* Thread info per-index. */\nbackground_thread_info_t *background_thread_info;\n\n/* False if no necessary runtime support. */\nbool can_enable_background_thread;\n\n/******************************************************************************/\n\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\n#include <dlfcn.h>\n\nstatic int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *,\n    void *(*)(void *), void *__restrict);\n\nstatic void\npthread_create_wrapper_init(void) {\n#ifdef JEMALLOC_LAZY_LOCK\n\tif (!isthreaded) {\n\t\tisthreaded = true;\n\t}\n#endif\n}\n\nint\npthread_create_wrapper(pthread_t *__restrict thread, const pthread_attr_t *attr,\n    void *(*start_routine)(void *), void *__restrict arg) {\n\tpthread_create_wrapper_init();\n\n\treturn pthread_create_fptr(thread, attr, start_routine, arg);\n}\n#endif /* JEMALLOC_PTHREAD_CREATE_WRAPPER */\n\n#ifndef JEMALLOC_BACKGROUND_THREAD\n#define NOT_REACHED { not_reached(); }\nbool background_thread_create(tsd_t *tsd, unsigned arena_ind) NOT_REACHED\nbool background_threads_enable(tsd_t *tsd) NOT_REACHED\nbool background_threads_disable(tsd_t *tsd) NOT_REACHED\nvoid background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, size_t npages_new) NOT_REACHED\nvoid background_thread_prefork0(tsdn_t *tsdn) NOT_REACHED\nvoid background_thread_prefork1(tsdn_t *tsdn) NOT_REACHED\nvoid background_thread_postfork_parent(tsdn_t *tsdn) NOT_REACHED\nvoid background_thread_postfork_child(tsdn_t *tsdn) NOT_REACHED\nbool background_thread_stats_read(tsdn_t *tsdn,\n    background_thread_stats_t *stats) NOT_REACHED\nvoid background_thread_ctl_init(tsdn_t *tsdn) NOT_REACHED\n#undef NOT_REACHED\n#else\n\nstatic bool background_thread_enabled_at_fork;\n\nstatic void\nbackground_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) {\n\tbackground_thread_wakeup_time_set(tsdn, info, 0);\n\tinfo->npages_to_purge_new = 0;\n\tif (config_stats) {\n\t\tinfo->tot_n_runs = 0;\n\t\tnstime_init(&info->tot_sleep_time, 0);\n\t}\n}\n\nstatic inline bool\nset_current_thread_affinity(UNUSED int cpu) {\n#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)\n\tcpu_set_t cpuset;\n\tCPU_ZERO(&cpuset);\n\tCPU_SET(cpu, &cpuset);\n\tint ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);\n\n\treturn (ret != 0);\n#else\n\treturn false;\n#endif\n}\n\n/* Threshold for determining when to wake up the background thread. */\n#define BACKGROUND_THREAD_NPAGES_THRESHOLD UINT64_C(1024)\n#define BILLION UINT64_C(1000000000)\n/* Minimal sleep interval 100 ms. */\n#define BACKGROUND_THREAD_MIN_INTERVAL_NS (BILLION / 10)\n\nstatic inline size_t\ndecay_npurge_after_interval(arena_decay_t *decay, size_t interval) {\n\tsize_t i;\n\tuint64_t sum = 0;\n\tfor (i = 0; i < interval; i++) {\n\t\tsum += decay->backlog[i] * h_steps[i];\n\t}\n\tfor (; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tsum += decay->backlog[i] * (h_steps[i] - h_steps[i - interval]);\n\t}\n\n\treturn (size_t)(sum >> SMOOTHSTEP_BFP);\n}\n\nstatic uint64_t\narena_decay_compute_purge_interval_impl(tsdn_t *tsdn, arena_decay_t *decay,\n    extents_t *extents) {\n\tif (malloc_mutex_trylock(tsdn, &decay->mtx)) {\n\t\t/* Use minimal interval if decay is contended. */\n\t\treturn BACKGROUND_THREAD_MIN_INTERVAL_NS;\n\t}\n\n\tuint64_t interval;\n\tssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);\n\tif (decay_time <= 0) {\n\t\t/* Purging is eagerly done or disabled currently. */\n\t\tinterval = BACKGROUND_THREAD_INDEFINITE_SLEEP;\n\t\tgoto label_done;\n\t}\n\n\tuint64_t decay_interval_ns = nstime_ns(&decay->interval);\n\tassert(decay_interval_ns > 0);\n\tsize_t npages = extents_npages_get(extents);\n\tif (npages == 0) {\n\t\tunsigned i;\n\t\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\t\tif (decay->backlog[i] > 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == SMOOTHSTEP_NSTEPS) {\n\t\t\t/* No dirty pages recorded.  Sleep indefinitely. */\n\t\t\tinterval = BACKGROUND_THREAD_INDEFINITE_SLEEP;\n\t\t\tgoto label_done;\n\t\t}\n\t}\n\tif (npages <= BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\t/* Use max interval. */\n\t\tinterval = decay_interval_ns * SMOOTHSTEP_NSTEPS;\n\t\tgoto label_done;\n\t}\n\n\tsize_t lb = BACKGROUND_THREAD_MIN_INTERVAL_NS / decay_interval_ns;\n\tsize_t ub = SMOOTHSTEP_NSTEPS;\n\t/* Minimal 2 intervals to ensure reaching next epoch deadline. */\n\tlb = (lb < 2) ? 2 : lb;\n\tif ((decay_interval_ns * ub <= BACKGROUND_THREAD_MIN_INTERVAL_NS) ||\n\t    (lb + 2 > ub)) {\n\t\tinterval = BACKGROUND_THREAD_MIN_INTERVAL_NS;\n\t\tgoto label_done;\n\t}\n\n\tassert(lb + 2 <= ub);\n\tsize_t npurge_lb, npurge_ub;\n\tnpurge_lb = decay_npurge_after_interval(decay, lb);\n\tif (npurge_lb > BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\tinterval = decay_interval_ns * lb;\n\t\tgoto label_done;\n\t}\n\tnpurge_ub = decay_npurge_after_interval(decay, ub);\n\tif (npurge_ub < BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\tinterval = decay_interval_ns * ub;\n\t\tgoto label_done;\n\t}\n\n\tunsigned n_search = 0;\n\tsize_t target, npurge;\n\twhile ((npurge_lb + BACKGROUND_THREAD_NPAGES_THRESHOLD < npurge_ub)\n\t    && (lb + 2 < ub)) {\n\t\ttarget = (lb + ub) / 2;\n\t\tnpurge = decay_npurge_after_interval(decay, target);\n\t\tif (npurge > BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\t\tub = target;\n\t\t\tnpurge_ub = npurge;\n\t\t} else {\n\t\t\tlb = target;\n\t\t\tnpurge_lb = npurge;\n\t\t}\n\t\tassert(n_search++ < lg_floor(SMOOTHSTEP_NSTEPS) + 1);\n\t}\n\tinterval = decay_interval_ns * (ub + lb) / 2;\nlabel_done:\n\tinterval = (interval < BACKGROUND_THREAD_MIN_INTERVAL_NS) ?\n\t    BACKGROUND_THREAD_MIN_INTERVAL_NS : interval;\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\treturn interval;\n}\n\n/* Compute purge interval for background threads. */\nstatic uint64_t\narena_decay_compute_purge_interval(tsdn_t *tsdn, arena_t *arena) {\n\tuint64_t i1, i2;\n\ti1 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_dirty,\n\t    &arena->extents_dirty);\n\tif (i1 == BACKGROUND_THREAD_MIN_INTERVAL_NS) {\n\t\treturn i1;\n\t}\n\ti2 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_muzzy,\n\t    &arena->extents_muzzy);\n\n\treturn i1 < i2 ? i1 : i2;\n}\n\nstatic void\nbackground_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,\n    uint64_t interval) {\n\tif (config_stats) {\n\t\tinfo->tot_n_runs++;\n\t}\n\tinfo->npages_to_purge_new = 0;\n\n\tstruct timeval tv;\n\t/* Specific clock required by timedwait. */\n\tgettimeofday(&tv, NULL);\n\tnstime_t before_sleep;\n\tnstime_init2(&before_sleep, tv.tv_sec, tv.tv_usec * 1000);\n\n\tint ret;\n\tif (interval == BACKGROUND_THREAD_INDEFINITE_SLEEP) {\n\t\tassert(background_thread_indefinite_sleep(info));\n\t\tret = pthread_cond_wait(&info->cond, &info->mtx.lock);\n\t\tassert(ret == 0);\n\t} else {\n\t\tassert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS &&\n\t\t    interval <= BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\t\t/* We need malloc clock (can be different from tv). */\n\t\tnstime_t next_wakeup;\n\t\tnstime_init(&next_wakeup, 0);\n\t\tnstime_update(&next_wakeup);\n\t\tnstime_iadd(&next_wakeup, interval);\n\t\tassert(nstime_ns(&next_wakeup) <\n\t\t    BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\t\tbackground_thread_wakeup_time_set(tsdn, info,\n\t\t    nstime_ns(&next_wakeup));\n\n\t\tnstime_t ts_wakeup;\n\t\tnstime_copy(&ts_wakeup, &before_sleep);\n\t\tnstime_iadd(&ts_wakeup, interval);\n\t\tstruct timespec ts;\n\t\tts.tv_sec = (size_t)nstime_sec(&ts_wakeup);\n\t\tts.tv_nsec = (size_t)nstime_nsec(&ts_wakeup);\n\n\t\tassert(!background_thread_indefinite_sleep(info));\n\t\tret = pthread_cond_timedwait(&info->cond, &info->mtx.lock, &ts);\n\t\tassert(ret == ETIMEDOUT || ret == 0);\n\t\tbackground_thread_wakeup_time_set(tsdn, info,\n\t\t    BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\t}\n\tif (config_stats) {\n\t\tgettimeofday(&tv, NULL);\n\t\tnstime_t after_sleep;\n\t\tnstime_init2(&after_sleep, tv.tv_sec, tv.tv_usec * 1000);\n\t\tif (nstime_compare(&after_sleep, &before_sleep) > 0) {\n\t\t\tnstime_subtract(&after_sleep, &before_sleep);\n\t\t\tnstime_add(&info->tot_sleep_time, &after_sleep);\n\t\t}\n\t}\n}\n\nstatic bool\nbackground_thread_pause_check(tsdn_t *tsdn, background_thread_info_t *info) {\n\tif (unlikely(info->state == background_thread_paused)) {\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t\t/* Wait on global lock to update status. */\n\t\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\t\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline void\nbackground_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info, unsigned ind) {\n\tuint64_t min_interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;\n\tunsigned narenas = narenas_total_get();\n\n\tfor (unsigned i = ind; i < narenas; i += max_background_threads) {\n\t\tarena_t *arena = arena_get(tsdn, i, false);\n\t\tif (!arena) {\n\t\t\tcontinue;\n\t\t}\n\t\tarena_decay(tsdn, arena, true, false);\n\t\tif (min_interval == BACKGROUND_THREAD_MIN_INTERVAL_NS) {\n\t\t\t/* Min interval will be used. */\n\t\t\tcontinue;\n\t\t}\n\t\tuint64_t interval = arena_decay_compute_purge_interval(tsdn,\n\t\t    arena);\n\t\tassert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS);\n\t\tif (min_interval > interval) {\n\t\t\tmin_interval = interval;\n\t\t}\n\t}\n\tbackground_thread_sleep(tsdn, info, min_interval);\n}\n\nstatic bool\nbackground_threads_disable_single(tsd_t *tsd, background_thread_info_t *info) {\n\tif (info == &background_thread_info[0]) {\n\t\tmalloc_mutex_assert_owner(tsd_tsdn(tsd),\n\t\t    &background_thread_lock);\n\t} else {\n\t\tmalloc_mutex_assert_not_owner(tsd_tsdn(tsd),\n\t\t    &background_thread_lock);\n\t}\n\n\tpre_reentrancy(tsd, NULL);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\tbool has_thread;\n\tassert(info->state != background_thread_paused);\n\tif (info->state == background_thread_started) {\n\t\thas_thread = true;\n\t\tinfo->state = background_thread_stopped;\n\t\tpthread_cond_signal(&info->cond);\n\t} else {\n\t\thas_thread = false;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\n\tif (!has_thread) {\n\t\tpost_reentrancy(tsd);\n\t\treturn false;\n\t}\n\tvoid *ret;\n\tif (pthread_join(info->thread, &ret)) {\n\t\tpost_reentrancy(tsd);\n\t\treturn true;\n\t}\n\tassert(ret == NULL);\n\tn_background_threads--;\n\tpost_reentrancy(tsd);\n\n\treturn false;\n}\n\nstatic void *background_thread_entry(void *ind_arg);\n\nstatic int\nbackground_thread_create_signals_masked(pthread_t *thread,\n    const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) {\n\t/*\n\t * Mask signals during thread creation so that the thread inherits\n\t * an empty signal set.\n\t */\n\tsigset_t set;\n\tsigfillset(&set);\n\tsigset_t oldset;\n\tint mask_err = pthread_sigmask(SIG_SETMASK, &set, &oldset);\n\tif (mask_err != 0) {\n\t\treturn mask_err;\n\t}\n\tint create_err = pthread_create_wrapper(thread, attr, start_routine,\n\t    arg);\n\t/*\n\t * Restore the signal mask.  Failure to restore the signal mask here\n\t * changes program behavior.\n\t */\n\tint restore_err = pthread_sigmask(SIG_SETMASK, &oldset, NULL);\n\tif (restore_err != 0) {\n\t\tmalloc_printf(\"<jemalloc>: background thread creation \"\n\t\t    \"failed (%d), and signal mask restoration failed \"\n\t\t    \"(%d)\\n\", create_err, restore_err);\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t}\n\treturn create_err;\n}\n\nstatic bool\ncheck_background_thread_creation(tsd_t *tsd, unsigned *n_created,\n    bool *created_threads) {\n\tbool ret = false;\n\tif (likely(*n_created == n_background_threads)) {\n\t\treturn ret;\n\t}\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\tmalloc_mutex_unlock(tsdn, &background_thread_info[0].mtx);\n\tfor (unsigned i = 1; i < max_background_threads; i++) {\n\t\tif (created_threads[i]) {\n\t\t\tcontinue;\n\t\t}\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\t/*\n\t\t * In case of the background_thread_paused state because of\n\t\t * arena reset, delay the creation.\n\t\t */\n\t\tbool create = (info->state == background_thread_started);\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t\tif (!create) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tpre_reentrancy(tsd, NULL);\n\t\tint err = background_thread_create_signals_masked(&info->thread,\n\t\t    NULL, background_thread_entry, (void *)(uintptr_t)i);\n\t\tpost_reentrancy(tsd);\n\n\t\tif (err == 0) {\n\t\t\t(*n_created)++;\n\t\t\tcreated_threads[i] = true;\n\t\t} else {\n\t\t\tmalloc_printf(\"<jemalloc>: background thread \"\n\t\t\t    \"creation failed (%d)\\n\", err);\n\t\t\tif (opt_abort) {\n\t\t\t\tabort();\n\t\t\t}\n\t\t}\n\t\t/* Return to restart the loop since we unlocked. */\n\t\tret = true;\n\t\tbreak;\n\t}\n\tmalloc_mutex_lock(tsdn, &background_thread_info[0].mtx);\n\n\treturn ret;\n}\n\nstatic void\nbackground_thread0_work(tsd_t *tsd) {\n\t/* Thread0 is also responsible for launching / terminating threads. */\n\tVARIABLE_ARRAY(bool, created_threads, max_background_threads);\n\tunsigned i;\n\tfor (i = 1; i < max_background_threads; i++) {\n\t\tcreated_threads[i] = false;\n\t}\n\t/* Start working, and create more threads when asked. */\n\tunsigned n_created = 1;\n\twhile (background_thread_info[0].state != background_thread_stopped) {\n\t\tif (background_thread_pause_check(tsd_tsdn(tsd),\n\t\t    &background_thread_info[0])) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (check_background_thread_creation(tsd, &n_created,\n\t\t    (bool *)&created_threads)) {\n\t\t\tcontinue;\n\t\t}\n\t\tbackground_work_sleep_once(tsd_tsdn(tsd),\n\t\t    &background_thread_info[0], 0);\n\t}\n\n\t/*\n\t * Shut down other threads at exit.  Note that the ctl thread is holding\n\t * the global background_thread mutex (and is waiting) for us.\n\t */\n\tassert(!background_thread_enabled());\n\tfor (i = 1; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tassert(info->state != background_thread_paused);\n\t\tif (created_threads[i]) {\n\t\t\tbackground_threads_disable_single(tsd, info);\n\t\t} else {\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\t\tif (info->state != background_thread_stopped) {\n\t\t\t\t/* The thread was not created. */\n\t\t\t\tassert(info->state ==\n\t\t\t\t    background_thread_started);\n\t\t\t\tn_background_threads--;\n\t\t\t\tinfo->state = background_thread_stopped;\n\t\t\t}\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\t}\n\t}\n\tbackground_thread_info[0].state = background_thread_stopped;\n\tassert(n_background_threads == 1);\n}\n\nstatic void\nbackground_work(tsd_t *tsd, unsigned ind) {\n\tbackground_thread_info_t *info = &background_thread_info[ind];\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\tbackground_thread_wakeup_time_set(tsd_tsdn(tsd), info,\n\t    BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\tif (ind == 0) {\n\t\tbackground_thread0_work(tsd);\n\t} else {\n\t\twhile (info->state != background_thread_stopped) {\n\t\t\tif (background_thread_pause_check(tsd_tsdn(tsd),\n\t\t\t    info)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbackground_work_sleep_once(tsd_tsdn(tsd), info, ind);\n\t\t}\n\t}\n\tassert(info->state == background_thread_stopped);\n\tbackground_thread_wakeup_time_set(tsd_tsdn(tsd), info, 0);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n}\n\nstatic void *\nbackground_thread_entry(void *ind_arg) {\n\tunsigned thread_ind = (unsigned)(uintptr_t)ind_arg;\n\tassert(thread_ind < max_background_threads);\n#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP\n\tpthread_setname_np(pthread_self(), \"jemalloc_bg_thd\");\n#endif\n\tif (opt_percpu_arena != percpu_arena_disabled) {\n\t\tset_current_thread_affinity((int)thread_ind);\n\t}\n\t/*\n\t * Start periodic background work.  We use internal tsd which avoids\n\t * side effects, for example triggering new arena creation (which in\n\t * turn triggers another background thread creation).\n\t */\n\tbackground_work(tsd_internal_fetch(), thread_ind);\n\tassert(pthread_equal(pthread_self(),\n\t    background_thread_info[thread_ind].thread));\n\n\treturn NULL;\n}\n\nstatic void\nbackground_thread_init(tsd_t *tsd, background_thread_info_t *info) {\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\tinfo->state = background_thread_started;\n\tbackground_thread_info_init(tsd_tsdn(tsd), info);\n\tn_background_threads++;\n}\n\n/* Create a new background thread if needed. */\nbool\nbackground_thread_create(tsd_t *tsd, unsigned arena_ind) {\n\tassert(have_background_thread);\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\n\t/* We create at most NCPUs threads. */\n\tsize_t thread_ind = arena_ind % max_background_threads;\n\tbackground_thread_info_t *info = &background_thread_info[thread_ind];\n\n\tbool need_new_thread;\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\tneed_new_thread = background_thread_enabled() &&\n\t    (info->state == background_thread_stopped);\n\tif (need_new_thread) {\n\t\tbackground_thread_init(tsd, info);\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\tif (!need_new_thread) {\n\t\treturn false;\n\t}\n\tif (arena_ind != 0) {\n\t\t/* Threads are created asynchronously by Thread 0. */\n\t\tbackground_thread_info_t *t0 = &background_thread_info[0];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &t0->mtx);\n\t\tassert(t0->state == background_thread_started);\n\t\tpthread_cond_signal(&t0->cond);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &t0->mtx);\n\n\t\treturn false;\n\t}\n\n\tpre_reentrancy(tsd, NULL);\n\t/*\n\t * To avoid complications (besides reentrancy), create internal\n\t * background threads with the underlying pthread_create.\n\t */\n\tint err = background_thread_create_signals_masked(&info->thread, NULL,\n\t    background_thread_entry, (void *)thread_ind);\n\tpost_reentrancy(tsd);\n\n\tif (err != 0) {\n\t\tmalloc_printf(\"<jemalloc>: arena 0 background thread creation \"\n\t\t    \"failed (%d)\\n\", err);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\tinfo->state = background_thread_stopped;\n\t\tn_background_threads--;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool\nbackground_threads_enable(tsd_t *tsd) {\n\tassert(n_background_threads == 0);\n\tassert(background_thread_enabled());\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\n\tVARIABLE_ARRAY(bool, marked, max_background_threads);\n\tunsigned i, nmarked;\n\tfor (i = 0; i < max_background_threads; i++) {\n\t\tmarked[i] = false;\n\t}\n\tnmarked = 0;\n\t/* Thread 0 is required and created at the end. */\n\tmarked[0] = true;\n\t/* Mark the threads we need to create for thread 0. */\n\tunsigned n = narenas_total_get();\n\tfor (i = 1; i < n; i++) {\n\t\tif (marked[i % max_background_threads] ||\n\t\t    arena_get(tsd_tsdn(tsd), i, false) == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\tbackground_thread_info_t *info = &background_thread_info[\n\t\t    i % max_background_threads];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\tassert(info->state == background_thread_stopped);\n\t\tbackground_thread_init(tsd, info);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\tmarked[i % max_background_threads] = true;\n\t\tif (++nmarked == max_background_threads) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn background_thread_create(tsd, 0);\n}\n\nbool\nbackground_threads_disable(tsd_t *tsd) {\n\tassert(!background_thread_enabled());\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\n\t/* Thread 0 will be responsible for terminating other threads. */\n\tif (background_threads_disable_single(tsd,\n\t    &background_thread_info[0])) {\n\t\treturn true;\n\t}\n\tassert(n_background_threads == 0);\n\n\treturn false;\n}\n\n/* Check if we need to signal the background thread early. */\nvoid\nbackground_thread_interval_check(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, size_t npages_new) {\n\tbackground_thread_info_t *info = arena_background_thread_info_get(\n\t    arena);\n\tif (malloc_mutex_trylock(tsdn, &info->mtx)) {\n\t\t/*\n\t\t * Background thread may hold the mutex for a long period of\n\t\t * time.  We'd like to avoid the variance on application\n\t\t * threads.  So keep this non-blocking, and leave the work to a\n\t\t * future epoch.\n\t\t */\n\t\treturn;\n\t}\n\n\tif (info->state != background_thread_started) {\n\t\tgoto label_done;\n\t}\n\tif (malloc_mutex_trylock(tsdn, &decay->mtx)) {\n\t\tgoto label_done;\n\t}\n\n\tssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);\n\tif (decay_time <= 0) {\n\t\t/* Purging is eagerly done or disabled currently. */\n\t\tgoto label_done_unlock2;\n\t}\n\tuint64_t decay_interval_ns = nstime_ns(&decay->interval);\n\tassert(decay_interval_ns > 0);\n\n\tnstime_t diff;\n\tnstime_init(&diff, background_thread_wakeup_time_get(info));\n\tif (nstime_compare(&diff, &decay->epoch) <= 0) {\n\t\tgoto label_done_unlock2;\n\t}\n\tnstime_subtract(&diff, &decay->epoch);\n\tif (nstime_ns(&diff) < BACKGROUND_THREAD_MIN_INTERVAL_NS) {\n\t\tgoto label_done_unlock2;\n\t}\n\n\tif (npages_new > 0) {\n\t\tsize_t n_epoch = (size_t)(nstime_ns(&diff) / decay_interval_ns);\n\t\t/*\n\t\t * Compute how many new pages we would need to purge by the next\n\t\t * wakeup, which is used to determine if we should signal the\n\t\t * background thread.\n\t\t */\n\t\tuint64_t npurge_new;\n\t\tif (n_epoch >= SMOOTHSTEP_NSTEPS) {\n\t\t\tnpurge_new = npages_new;\n\t\t} else {\n\t\t\tuint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1];\n\t\t\tassert(h_steps_max >=\n\t\t\t    h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);\n\t\t\tnpurge_new = npages_new * (h_steps_max -\n\t\t\t    h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);\n\t\t\tnpurge_new >>= SMOOTHSTEP_BFP;\n\t\t}\n\t\tinfo->npages_to_purge_new += npurge_new;\n\t}\n\n\tbool should_signal;\n\tif (info->npages_to_purge_new > BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\tshould_signal = true;\n\t} else if (unlikely(background_thread_indefinite_sleep(info)) &&\n\t    (extents_npages_get(&arena->extents_dirty) > 0 ||\n\t    extents_npages_get(&arena->extents_muzzy) > 0 ||\n\t    info->npages_to_purge_new > 0)) {\n\t\tshould_signal = true;\n\t} else {\n\t\tshould_signal = false;\n\t}\n\n\tif (should_signal) {\n\t\tinfo->npages_to_purge_new = 0;\n\t\tpthread_cond_signal(&info->cond);\n\t}\nlabel_done_unlock2:\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\nlabel_done:\n\tmalloc_mutex_unlock(tsdn, &info->mtx);\n}\n\nvoid\nbackground_thread_prefork0(tsdn_t *tsdn) {\n\tmalloc_mutex_prefork(tsdn, &background_thread_lock);\n\tbackground_thread_enabled_at_fork = background_thread_enabled();\n}\n\nvoid\nbackground_thread_prefork1(tsdn_t *tsdn) {\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tmalloc_mutex_prefork(tsdn, &background_thread_info[i].mtx);\n\t}\n}\n\nvoid\nbackground_thread_postfork_parent(tsdn_t *tsdn) {\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tmalloc_mutex_postfork_parent(tsdn,\n\t\t    &background_thread_info[i].mtx);\n\t}\n\tmalloc_mutex_postfork_parent(tsdn, &background_thread_lock);\n}\n\nvoid\nbackground_thread_postfork_child(tsdn_t *tsdn) {\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tmalloc_mutex_postfork_child(tsdn,\n\t\t    &background_thread_info[i].mtx);\n\t}\n\tmalloc_mutex_postfork_child(tsdn, &background_thread_lock);\n\tif (!background_thread_enabled_at_fork) {\n\t\treturn;\n\t}\n\n\t/* Clear background_thread state (reset to disabled for child). */\n\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\tn_background_threads = 0;\n\tbackground_thread_enabled_set(tsdn, false);\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\tinfo->state = background_thread_stopped;\n\t\tint ret = pthread_cond_init(&info->cond, NULL);\n\t\tassert(ret == 0);\n\t\tbackground_thread_info_init(tsdn, info);\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t}\n\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n}\n\nbool\nbackground_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {\n\tassert(config_stats);\n\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\tif (!background_thread_enabled()) {\n\t\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\t\treturn true;\n\t}\n\n\tstats->num_threads = n_background_threads;\n\tuint64_t num_runs = 0;\n\tnstime_init(&stats->run_interval, 0);\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tif (malloc_mutex_trylock(tsdn, &info->mtx)) {\n\t\t\t/*\n\t\t\t * Each background thread run may take a long time;\n\t\t\t * avoid waiting on the stats if the thread is active.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\tif (info->state != background_thread_stopped) {\n\t\t\tnum_runs += info->tot_n_runs;\n\t\t\tnstime_add(&stats->run_interval, &info->tot_sleep_time);\n\t\t}\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t}\n\tstats->num_runs = num_runs;\n\tif (num_runs > 0) {\n\t\tnstime_idivide(&stats->run_interval, num_runs);\n\t}\n\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\n\treturn false;\n}\n\n#undef BACKGROUND_THREAD_NPAGES_THRESHOLD\n#undef BILLION\n#undef BACKGROUND_THREAD_MIN_INTERVAL_NS\n\nstatic bool\npthread_create_fptr_init(void) {\n\tif (pthread_create_fptr != NULL) {\n\t\treturn false;\n\t}\n\tpthread_create_fptr = dlsym(RTLD_NEXT, \"pthread_create\");\n\tif (pthread_create_fptr == NULL) {\n\t\tcan_enable_background_thread = false;\n\t\tif (config_lazy_lock || opt_background_thread) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in dlsym(RTLD_NEXT, \"\n\t\t\t    \"\\\"pthread_create\\\")\\n\");\n\t\t\tabort();\n\t\t}\n\t} else {\n\t\tcan_enable_background_thread = true;\n\t}\n\n\treturn false;\n}\n\n/*\n * When lazy lock is enabled, we need to make sure setting isthreaded before\n * taking any background_thread locks.  This is called early in ctl (instead of\n * wait for the pthread_create calls to trigger) because the mutex is required\n * before creating background threads.\n */\nvoid\nbackground_thread_ctl_init(tsdn_t *tsdn) {\n\tmalloc_mutex_assert_not_owner(tsdn, &background_thread_lock);\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\n\tpthread_create_fptr_init();\n\tpthread_create_wrapper_init();\n#endif\n}\n\n#endif /* defined(JEMALLOC_BACKGROUND_THREAD) */\n\nbool\nbackground_thread_boot0(void) {\n\tif (!have_background_thread && opt_background_thread) {\n\t\tmalloc_printf(\"<jemalloc>: option background_thread currently \"\n\t\t    \"supports pthread only\\n\");\n\t\treturn true;\n\t}\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\n\tif ((config_lazy_lock || opt_background_thread) &&\n\t    pthread_create_fptr_init()) {\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\nbool\nbackground_thread_boot1(tsdn_t *tsdn) {\n#ifdef JEMALLOC_BACKGROUND_THREAD\n\tassert(have_background_thread);\n\tassert(narenas_total_get() > 0);\n\n\tif (opt_max_background_threads == MAX_BACKGROUND_THREAD_LIMIT &&\n\t    ncpus < MAX_BACKGROUND_THREAD_LIMIT) {\n\t\topt_max_background_threads = ncpus;\n\t}\n\tmax_background_threads = opt_max_background_threads;\n\n\tbackground_thread_enabled_set(tsdn, opt_background_thread);\n\tif (malloc_mutex_init(&background_thread_lock,\n\t    \"background_thread_global\",\n\t    WITNESS_RANK_BACKGROUND_THREAD_GLOBAL,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\tbackground_thread_info = (background_thread_info_t *)base_alloc(tsdn,\n\t    b0get(), opt_max_background_threads *\n\t    sizeof(background_thread_info_t), CACHELINE);\n\tif (background_thread_info == NULL) {\n\t\treturn true;\n\t}\n\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\t/* Thread mutex is rank_inclusive because of thread0. */\n\t\tif (malloc_mutex_init(&info->mtx, \"background_thread\",\n\t\t    WITNESS_RANK_BACKGROUND_THREAD,\n\t\t    malloc_mutex_address_ordered)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (pthread_cond_init(&info->cond, NULL)) {\n\t\t\treturn true;\n\t\t}\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\tinfo->state = background_thread_stopped;\n\t\tbackground_thread_info_init(tsdn, info);\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t}\n#endif\n\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/base.c",
    "content": "#define JEMALLOC_BASE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/sz.h\"\n\n/******************************************************************************/\n/* Data. */\n\nstatic base_t *b0;\n\nmetadata_thp_mode_t opt_metadata_thp = METADATA_THP_DEFAULT;\n\nconst char *metadata_thp_mode_names[] = {\n\t\"disabled\",\n\t\"auto\",\n\t\"always\"\n};\n\n/******************************************************************************/\n\nstatic inline bool\nmetadata_thp_madvise(void) {\n\treturn (metadata_thp_enabled() &&\n\t    (init_system_thp_mode == thp_mode_default));\n}\n\nstatic void *\nbase_map(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, size_t size) {\n\tvoid *addr;\n\tbool zero = true;\n\tbool commit = true;\n\n\t/* Use huge page sizes and alignment regardless of opt_metadata_thp. */\n\tassert(size == HUGEPAGE_CEILING(size));\n\tsize_t alignment = HUGEPAGE;\n\tif (extent_hooks == &extent_hooks_default) {\n\t\taddr = extent_alloc_mmap(NULL, size, alignment, &zero, &commit);\n\t} else {\n\t\t/* No arena context as we are creating new arenas. */\n\t\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\t\tpre_reentrancy(tsd, NULL);\n\t\taddr = extent_hooks->alloc(extent_hooks, NULL, size, alignment,\n\t\t    &zero, &commit, ind);\n\t\tpost_reentrancy(tsd);\n\t}\n\n\treturn addr;\n}\n\nstatic void\nbase_unmap(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, void *addr,\n    size_t size) {\n\t/*\n\t * Cascade through dalloc, decommit, purge_forced, and purge_lazy,\n\t * stopping at first success.  This cascade is performed for consistency\n\t * with the cascade in extent_dalloc_wrapper() because an application's\n\t * custom hooks may not support e.g. dalloc.  This function is only ever\n\t * called as a side effect of arena destruction, so although it might\n\t * seem pointless to do anything besides dalloc here, the application\n\t * may in fact want the end state of all associated virtual memory to be\n\t * in some consistent-but-allocated state.\n\t */\n\tif (extent_hooks == &extent_hooks_default) {\n\t\tif (!extent_dalloc_mmap(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\tif (!pages_decommit(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\tif (!pages_purge_forced(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\tif (!pages_purge_lazy(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\t/* Nothing worked.  This should never happen. */\n\t\tnot_reached();\n\t} else {\n\t\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\t\tpre_reentrancy(tsd, NULL);\n\t\tif (extent_hooks->dalloc != NULL &&\n\t\t    !extent_hooks->dalloc(extent_hooks, addr, size, true,\n\t\t    ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\tif (extent_hooks->decommit != NULL &&\n\t\t    !extent_hooks->decommit(extent_hooks, addr, size, 0, size,\n\t\t    ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\tif (extent_hooks->purge_forced != NULL &&\n\t\t    !extent_hooks->purge_forced(extent_hooks, addr, size, 0,\n\t\t    size, ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\tif (extent_hooks->purge_lazy != NULL &&\n\t\t    !extent_hooks->purge_lazy(extent_hooks, addr, size, 0, size,\n\t\t    ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\t/* Nothing worked.  That's the application's problem. */\n\tlabel_post_reentrancy:\n\t\tpost_reentrancy(tsd);\n\t}\nlabel_done:\n\tif (metadata_thp_madvise()) {\n\t\t/* Set NOHUGEPAGE after unmap to avoid kernel defrag. */\n\t\tassert(((uintptr_t)addr & HUGEPAGE_MASK) == 0 &&\n\t\t    (size & HUGEPAGE_MASK) == 0);\n\t\tpages_nohuge(addr, size);\n\t}\n}\n\nstatic void\nbase_extent_init(size_t *extent_sn_next, extent_t *extent, void *addr,\n    size_t size) {\n\tsize_t sn;\n\n\tsn = *extent_sn_next;\n\t(*extent_sn_next)++;\n\n\textent_binit(extent, addr, size, sn);\n}\n\nstatic size_t\nbase_get_num_blocks(base_t *base, bool with_new_block) {\n\tbase_block_t *b = base->blocks;\n\tassert(b != NULL);\n\n\tsize_t n_blocks = with_new_block ? 2 : 1;\n\twhile (b->next != NULL) {\n\t\tn_blocks++;\n\t\tb = b->next;\n\t}\n\n\treturn n_blocks;\n}\n\nstatic void\nbase_auto_thp_switch(tsdn_t *tsdn, base_t *base) {\n\tassert(opt_metadata_thp == metadata_thp_auto);\n\tmalloc_mutex_assert_owner(tsdn, &base->mtx);\n\tif (base->auto_thp_switched) {\n\t\treturn;\n\t}\n\t/* Called when adding a new block. */\n\tbool should_switch;\n\tif (base_ind_get(base) != 0) {\n\t\tshould_switch = (base_get_num_blocks(base, true) ==\n\t\t    BASE_AUTO_THP_THRESHOLD);\n\t} else {\n\t\tshould_switch = (base_get_num_blocks(base, true) ==\n\t\t    BASE_AUTO_THP_THRESHOLD_A0);\n\t}\n\tif (!should_switch) {\n\t\treturn;\n\t}\n\n\tbase->auto_thp_switched = true;\n\tassert(!config_stats || base->n_thp == 0);\n\t/* Make the initial blocks THP lazily. */\n\tbase_block_t *block = base->blocks;\n\twhile (block != NULL) {\n\t\tassert((block->size & HUGEPAGE_MASK) == 0);\n\t\tpages_huge(block, block->size);\n\t\tif (config_stats) {\n\t\t\tbase->n_thp += HUGEPAGE_CEILING(block->size -\n\t\t\t    extent_bsize_get(&block->extent)) >> LG_HUGEPAGE;\n\t\t}\n\t\tblock = block->next;\n\t\tassert(block == NULL || (base_ind_get(base) == 0));\n\t}\n}\n\nstatic void *\nbase_extent_bump_alloc_helper(extent_t *extent, size_t *gap_size, size_t size,\n    size_t alignment) {\n\tvoid *ret;\n\n\tassert(alignment == ALIGNMENT_CEILING(alignment, QUANTUM));\n\tassert(size == ALIGNMENT_CEILING(size, alignment));\n\n\t*gap_size = ALIGNMENT_CEILING((uintptr_t)extent_addr_get(extent),\n\t    alignment) - (uintptr_t)extent_addr_get(extent);\n\tret = (void *)((uintptr_t)extent_addr_get(extent) + *gap_size);\n\tassert(extent_bsize_get(extent) >= *gap_size + size);\n\textent_binit(extent, (void *)((uintptr_t)extent_addr_get(extent) +\n\t    *gap_size + size), extent_bsize_get(extent) - *gap_size - size,\n\t    extent_sn_get(extent));\n\treturn ret;\n}\n\nstatic void\nbase_extent_bump_alloc_post(base_t *base, extent_t *extent, size_t gap_size,\n    void *addr, size_t size) {\n\tif (extent_bsize_get(extent) > 0) {\n\t\t/*\n\t\t * Compute the index for the largest size class that does not\n\t\t * exceed extent's size.\n\t\t */\n\t\tszind_t index_floor =\n\t\t    sz_size2index(extent_bsize_get(extent) + 1) - 1;\n\t\textent_heap_insert(&base->avail[index_floor], extent);\n\t}\n\n\tif (config_stats) {\n\t\tbase->allocated += size;\n\t\t/*\n\t\t * Add one PAGE to base_resident for every page boundary that is\n\t\t * crossed by the new allocation. Adjust n_thp similarly when\n\t\t * metadata_thp is enabled.\n\t\t */\n\t\tbase->resident += PAGE_CEILING((uintptr_t)addr + size) -\n\t\t    PAGE_CEILING((uintptr_t)addr - gap_size);\n\t\tassert(base->allocated <= base->resident);\n\t\tassert(base->resident <= base->mapped);\n\t\tif (metadata_thp_madvise() && (opt_metadata_thp ==\n\t\t    metadata_thp_always || base->auto_thp_switched)) {\n\t\t\tbase->n_thp += (HUGEPAGE_CEILING((uintptr_t)addr + size)\n\t\t\t    - HUGEPAGE_CEILING((uintptr_t)addr - gap_size)) >>\n\t\t\t    LG_HUGEPAGE;\n\t\t\tassert(base->mapped >= base->n_thp << LG_HUGEPAGE);\n\t\t}\n\t}\n}\n\nstatic void *\nbase_extent_bump_alloc(base_t *base, extent_t *extent, size_t size,\n    size_t alignment) {\n\tvoid *ret;\n\tsize_t gap_size;\n\n\tret = base_extent_bump_alloc_helper(extent, &gap_size, size, alignment);\n\tbase_extent_bump_alloc_post(base, extent, gap_size, ret, size);\n\treturn ret;\n}\n\n/*\n * Allocate a block of virtual memory that is large enough to start with a\n * base_block_t header, followed by an object of specified size and alignment.\n * On success a pointer to the initialized base_block_t header is returned.\n */\nstatic base_block_t *\nbase_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,\n    unsigned ind, pszind_t *pind_last, size_t *extent_sn_next, size_t size,\n    size_t alignment) {\n\talignment = ALIGNMENT_CEILING(alignment, QUANTUM);\n\tsize_t usize = ALIGNMENT_CEILING(size, alignment);\n\tsize_t header_size = sizeof(base_block_t);\n\tsize_t gap_size = ALIGNMENT_CEILING(header_size, alignment) -\n\t    header_size;\n\t/*\n\t * Create increasingly larger blocks in order to limit the total number\n\t * of disjoint virtual memory ranges.  Choose the next size in the page\n\t * size class series (skipping size classes that are not a multiple of\n\t * HUGEPAGE), or a size large enough to satisfy the requested size and\n\t * alignment, whichever is larger.\n\t */\n\tsize_t min_block_size = HUGEPAGE_CEILING(sz_psz2u(header_size + gap_size\n\t    + usize));\n\tpszind_t pind_next = (*pind_last + 1 < NPSIZES) ? *pind_last + 1 :\n\t    *pind_last;\n\tsize_t next_block_size = HUGEPAGE_CEILING(sz_pind2sz(pind_next));\n\tsize_t block_size = (min_block_size > next_block_size) ? min_block_size\n\t    : next_block_size;\n\tbase_block_t *block = (base_block_t *)base_map(tsdn, extent_hooks, ind,\n\t    block_size);\n\tif (block == NULL) {\n\t\treturn NULL;\n\t}\n\n\tif (metadata_thp_madvise()) {\n\t\tvoid *addr = (void *)block;\n\t\tassert(((uintptr_t)addr & HUGEPAGE_MASK) == 0 &&\n\t\t    (block_size & HUGEPAGE_MASK) == 0);\n\t\tif (opt_metadata_thp == metadata_thp_always) {\n\t\t\tpages_huge(addr, block_size);\n\t\t} else if (opt_metadata_thp == metadata_thp_auto &&\n\t\t    base != NULL) {\n\t\t\t/* base != NULL indicates this is not a new base. */\n\t\t\tmalloc_mutex_lock(tsdn, &base->mtx);\n\t\t\tbase_auto_thp_switch(tsdn, base);\n\t\t\tif (base->auto_thp_switched) {\n\t\t\t\tpages_huge(addr, block_size);\n\t\t\t}\n\t\t\tmalloc_mutex_unlock(tsdn, &base->mtx);\n\t\t}\n\t}\n\n\t*pind_last = sz_psz2ind(block_size);\n\tblock->size = block_size;\n\tblock->next = NULL;\n\tassert(block_size >= header_size);\n\tbase_extent_init(extent_sn_next, &block->extent,\n\t    (void *)((uintptr_t)block + header_size), block_size - header_size);\n\treturn block;\n}\n\n/*\n * Allocate an extent that is at least as large as specified size, with\n * specified alignment.\n */\nstatic extent_t *\nbase_extent_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {\n\tmalloc_mutex_assert_owner(tsdn, &base->mtx);\n\n\textent_hooks_t *extent_hooks = base_extent_hooks_get(base);\n\t/*\n\t * Drop mutex during base_block_alloc(), because an extent hook will be\n\t * called.\n\t */\n\tmalloc_mutex_unlock(tsdn, &base->mtx);\n\tbase_block_t *block = base_block_alloc(tsdn, base, extent_hooks,\n\t    base_ind_get(base), &base->pind_last, &base->extent_sn_next, size,\n\t    alignment);\n\tmalloc_mutex_lock(tsdn, &base->mtx);\n\tif (block == NULL) {\n\t\treturn NULL;\n\t}\n\tblock->next = base->blocks;\n\tbase->blocks = block;\n\tif (config_stats) {\n\t\tbase->allocated += sizeof(base_block_t);\n\t\tbase->resident += PAGE_CEILING(sizeof(base_block_t));\n\t\tbase->mapped += block->size;\n\t\tif (metadata_thp_madvise() &&\n\t\t    !(opt_metadata_thp == metadata_thp_auto\n\t\t      && !base->auto_thp_switched)) {\n\t\t\tassert(base->n_thp > 0);\n\t\t\tbase->n_thp += HUGEPAGE_CEILING(sizeof(base_block_t)) >>\n\t\t\t    LG_HUGEPAGE;\n\t\t}\n\t\tassert(base->allocated <= base->resident);\n\t\tassert(base->resident <= base->mapped);\n\t\tassert(base->n_thp << LG_HUGEPAGE <= base->mapped);\n\t}\n\treturn &block->extent;\n}\n\nbase_t *\nb0get(void) {\n\treturn b0;\n}\n\nbase_t *\nbase_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tpszind_t pind_last = 0;\n\tsize_t extent_sn_next = 0;\n\tbase_block_t *block = base_block_alloc(tsdn, NULL, extent_hooks, ind,\n\t    &pind_last, &extent_sn_next, sizeof(base_t), QUANTUM);\n\tif (block == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsize_t gap_size;\n\tsize_t base_alignment = CACHELINE;\n\tsize_t base_size = ALIGNMENT_CEILING(sizeof(base_t), base_alignment);\n\tbase_t *base = (base_t *)base_extent_bump_alloc_helper(&block->extent,\n\t    &gap_size, base_size, base_alignment);\n\tbase->ind = ind;\n\tatomic_store_p(&base->extent_hooks, extent_hooks, ATOMIC_RELAXED);\n\tif (malloc_mutex_init(&base->mtx, \"base\", WITNESS_RANK_BASE,\n\t    malloc_mutex_rank_exclusive)) {\n\t\tbase_unmap(tsdn, extent_hooks, ind, block, block->size);\n\t\treturn NULL;\n\t}\n\tbase->pind_last = pind_last;\n\tbase->extent_sn_next = extent_sn_next;\n\tbase->blocks = block;\n\tbase->auto_thp_switched = false;\n\tfor (szind_t i = 0; i < NSIZES; i++) {\n\t\textent_heap_new(&base->avail[i]);\n\t}\n\tif (config_stats) {\n\t\tbase->allocated = sizeof(base_block_t);\n\t\tbase->resident = PAGE_CEILING(sizeof(base_block_t));\n\t\tbase->mapped = block->size;\n\t\tbase->n_thp = (opt_metadata_thp == metadata_thp_always) &&\n\t\t    metadata_thp_madvise() ? HUGEPAGE_CEILING(sizeof(base_block_t))\n\t\t    >> LG_HUGEPAGE : 0;\n\t\tassert(base->allocated <= base->resident);\n\t\tassert(base->resident <= base->mapped);\n\t\tassert(base->n_thp << LG_HUGEPAGE <= base->mapped);\n\t}\n\tbase_extent_bump_alloc_post(base, &block->extent, gap_size, base,\n\t    base_size);\n\n\treturn base;\n}\n\nvoid\nbase_delete(tsdn_t *tsdn, base_t *base) {\n\textent_hooks_t *extent_hooks = base_extent_hooks_get(base);\n\tbase_block_t *next = base->blocks;\n\tdo {\n\t\tbase_block_t *block = next;\n\t\tnext = block->next;\n\t\tbase_unmap(tsdn, extent_hooks, base_ind_get(base), block,\n\t\t    block->size);\n\t} while (next != NULL);\n}\n\nextent_hooks_t *\nbase_extent_hooks_get(base_t *base) {\n\treturn (extent_hooks_t *)atomic_load_p(&base->extent_hooks,\n\t    ATOMIC_ACQUIRE);\n}\n\nextent_hooks_t *\nbase_extent_hooks_set(base_t *base, extent_hooks_t *extent_hooks) {\n\textent_hooks_t *old_extent_hooks = base_extent_hooks_get(base);\n\tatomic_store_p(&base->extent_hooks, extent_hooks, ATOMIC_RELEASE);\n\treturn old_extent_hooks;\n}\n\nstatic void *\nbase_alloc_impl(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment,\n    size_t *esn) {\n\talignment = QUANTUM_CEILING(alignment);\n\tsize_t usize = ALIGNMENT_CEILING(size, alignment);\n\tsize_t asize = usize + alignment - QUANTUM;\n\n\textent_t *extent = NULL;\n\tmalloc_mutex_lock(tsdn, &base->mtx);\n\tfor (szind_t i = sz_size2index(asize); i < NSIZES; i++) {\n\t\textent = extent_heap_remove_first(&base->avail[i]);\n\t\tif (extent != NULL) {\n\t\t\t/* Use existing space. */\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (extent == NULL) {\n\t\t/* Try to allocate more space. */\n\t\textent = base_extent_alloc(tsdn, base, usize, alignment);\n\t}\n\tvoid *ret;\n\tif (extent == NULL) {\n\t\tret = NULL;\n\t\tgoto label_return;\n\t}\n\n\tret = base_extent_bump_alloc(base, extent, usize, alignment);\n\tif (esn != NULL) {\n\t\t*esn = extent_sn_get(extent);\n\t}\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &base->mtx);\n\treturn ret;\n}\n\n/*\n * base_alloc() returns zeroed memory, which is always demand-zeroed for the\n * auto arenas, in order to make multi-page sparse data structures such as radix\n * tree nodes efficient with respect to physical memory usage.  Upon success a\n * pointer to at least size bytes with specified alignment is returned.  Note\n * that size is rounded up to the nearest multiple of alignment to avoid false\n * sharing.\n */\nvoid *\nbase_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {\n\treturn base_alloc_impl(tsdn, base, size, alignment, NULL);\n}\n\nextent_t *\nbase_alloc_extent(tsdn_t *tsdn, base_t *base) {\n\tsize_t esn;\n\textent_t *extent = base_alloc_impl(tsdn, base, sizeof(extent_t),\n\t    CACHELINE, &esn);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\textent_esn_set(extent, esn);\n\treturn extent;\n}\n\nvoid\nbase_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated, size_t *resident,\n    size_t *mapped, size_t *n_thp) {\n\tcassert(config_stats);\n\n\tmalloc_mutex_lock(tsdn, &base->mtx);\n\tassert(base->allocated <= base->resident);\n\tassert(base->resident <= base->mapped);\n\t*allocated = base->allocated;\n\t*resident = base->resident;\n\t*mapped = base->mapped;\n\t*n_thp = base->n_thp;\n\tmalloc_mutex_unlock(tsdn, &base->mtx);\n}\n\nvoid\nbase_prefork(tsdn_t *tsdn, base_t *base) {\n\tmalloc_mutex_prefork(tsdn, &base->mtx);\n}\n\nvoid\nbase_postfork_parent(tsdn_t *tsdn, base_t *base) {\n\tmalloc_mutex_postfork_parent(tsdn, &base->mtx);\n}\n\nvoid\nbase_postfork_child(tsdn_t *tsdn, base_t *base) {\n\tmalloc_mutex_postfork_child(tsdn, &base->mtx);\n}\n\nbool\nbase_boot(tsdn_t *tsdn) {\n\tb0 = base_new(tsdn, 0, (extent_hooks_t *)&extent_hooks_default);\n\treturn (b0 == NULL);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/bin.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/witness.h\"\n\nconst bin_info_t bin_infos[NBINS] = {\n#define BIN_INFO_bin_yes(reg_size, slab_size, nregs)\t\t\t\\\n\t{reg_size, slab_size, nregs, BITMAP_INFO_INITIALIZER(nregs)},\n#define BIN_INFO_bin_no(reg_size, slab_size, nregs)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs,\t\t\\\n    lg_delta_lookup)\t\t\t\t\t\t\t\\\n\tBIN_INFO_bin_##bin((1U<<lg_grp) + (ndelta<<lg_delta),\t\t\\\n\t    (pgs << LG_PAGE), (pgs << LG_PAGE) / ((1U<<lg_grp) +\t\\\n\t    (ndelta<<lg_delta)))\n\tSIZE_CLASSES\n#undef BIN_INFO_bin_yes\n#undef BIN_INFO_bin_no\n#undef SC\n};\n\nbool\nbin_init(bin_t *bin) {\n\tif (malloc_mutex_init(&bin->lock, \"bin\", WITNESS_RANK_BIN,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tbin->slabcur = NULL;\n\textent_heap_new(&bin->slabs_nonfull);\n\textent_list_init(&bin->slabs_full);\n\tif (config_stats) {\n\t\tmemset(&bin->stats, 0, sizeof(bin_stats_t));\n\t}\n\treturn false;\n}\n\nvoid\nbin_prefork(tsdn_t *tsdn, bin_t *bin) {\n\tmalloc_mutex_prefork(tsdn, &bin->lock);\n}\n\nvoid\nbin_postfork_parent(tsdn_t *tsdn, bin_t *bin) {\n\tmalloc_mutex_postfork_parent(tsdn, &bin->lock);\n}\n\nvoid\nbin_postfork_child(tsdn_t *tsdn, bin_t *bin) {\n\tmalloc_mutex_postfork_child(tsdn, &bin->lock);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/bitmap.c",
    "content": "#define JEMALLOC_BITMAP_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n/******************************************************************************/\n\n#ifdef BITMAP_USE_TREE\n\nvoid\nbitmap_info_init(bitmap_info_t *binfo, size_t nbits) {\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 = BITMAP_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 = BITMAP_BITS2GROUPS(group_count);\n\t}\n\tbinfo->levels[i].group_offset = binfo->levels[i-1].group_offset\n\t    + group_count;\n\tassert(binfo->levels[i].group_offset <= BITMAP_GROUPS_MAX);\n\tbinfo->nlevels = i;\n\tbinfo->nbits = nbits;\n}\n\nstatic size_t\nbitmap_info_ngroups(const bitmap_info_t *binfo) {\n\treturn binfo->levels[binfo->nlevels].group_offset;\n}\n\nvoid\nbitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill) {\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.\n\t */\n\n\tif (fill) {\n\t\t/* The \"filled\" bitmap starts out with all 0 bits. */\n\t\tmemset(bitmap, 0, bitmap_size(binfo));\n\t\treturn;\n\t}\n\n\t/*\n\t * The \"empty\" bitmap starts out with all 1 bits, except for trailing\n\t * unused bits (if any).  Note that each group uses bit 0 to correspond\n\t * to the first logical bit in the group, so extra bits are the most\n\t * significant bits of the last group.\n\t */\n\tmemset(bitmap, 0xffU, bitmap_size(binfo));\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\t}\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\t}\n\t}\n}\n\n#else /* BITMAP_USE_TREE */\n\nvoid\nbitmap_info_init(bitmap_info_t *binfo, size_t nbits) {\n\tassert(nbits > 0);\n\tassert(nbits <= (ZU(1) << LG_BITMAP_MAXBITS));\n\n\tbinfo->ngroups = BITMAP_BITS2GROUPS(nbits);\n\tbinfo->nbits = nbits;\n}\n\nstatic size_t\nbitmap_info_ngroups(const bitmap_info_t *binfo) {\n\treturn binfo->ngroups;\n}\n\nvoid\nbitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill) {\n\tsize_t extra;\n\n\tif (fill) {\n\t\tmemset(bitmap, 0, bitmap_size(binfo));\n\t\treturn;\n\t}\n\n\tmemset(bitmap, 0xffU, bitmap_size(binfo));\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->ngroups - 1] >>= extra;\n\t}\n}\n\n#endif /* BITMAP_USE_TREE */\n\nsize_t\nbitmap_size(const bitmap_info_t *binfo) {\n\treturn (bitmap_info_ngroups(binfo) << LG_SIZEOF_BITMAP);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 JEMALLOC_CKH_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n#include \"jemalloc/internal/ckh.h\"\n\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic bool\tckh_grow(tsd_t *tsd, ckh_t *ckh);\nstatic void\tckh_shrink(tsd_t *tsd, 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 */\nstatic size_t\nckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key) {\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\t}\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 */\nstatic size_t\nckh_isearch(ckh_t *ckh, const void *key) {\n\tsize_t hashes[2], bucket, cell;\n\n\tassert(ckh != NULL);\n\n\tckh->hash(key, hashes);\n\n\t/* Search primary bucket. */\n\tbucket = hashes[0] & ((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\t}\n\n\t/* Search secondary bucket. */\n\tbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tcell = ckh_bucket_search(ckh, bucket, key);\n\treturn cell;\n}\n\nstatic bool\nckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key,\n    const void *data) {\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\toffset = (unsigned)prng_lg_range_u64(&ckh->prng_state,\n\t    LG_CKH_BUCKET_CELLS);\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 */\nstatic bool\nckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,\n    void const **argdata) {\n\tconst void *key, *data, *tkey, *tdata;\n\tckhc_t *cell;\n\tsize_t hashes[2], 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\ti = (unsigned)prng_lg_range_u64(&ckh->prng_state,\n\t\t    LG_CKH_BUCKET_CELLS);\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, hashes);\n\t\ttbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\t\tif (tbucket == bucket) {\n\t\t\ttbucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets)\n\t\t\t    - 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)) {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nstatic bool\nckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) {\n\tsize_t hashes[2], bucket;\n\tconst void *key = *argkey;\n\tconst void *data = *argdata;\n\n\tckh->hash(key, hashes);\n\n\t/* Try to insert in primary bucket. */\n\tbucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tif (!ckh_try_bucket_insert(ckh, bucket, key, data)) {\n\t\treturn false;\n\t}\n\n\t/* Try to insert in secondary bucket. */\n\tbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tif (!ckh_try_bucket_insert(ckh, bucket, key, data)) {\n\t\treturn false;\n\t}\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 */\nstatic bool\nckh_rebuild(ckh_t *ckh, ckhc_t *aTab) {\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(tsd_t *tsd, ckh_t *ckh) {\n\tbool ret;\n\tckhc_t *tab, *ttab;\n\tunsigned lg_prevbuckets, lg_curcells;\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 = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\ttab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE,\n\t\t    true, NULL, true, arena_ichoose(tsd, NULL));\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)) {\n\t\t\tidalloctm(tsd_tsdn(tsd), tab, NULL, NULL, true, true);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Rebuilding failed, so back out partially rebuilt table. */\n\t\tidalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true);\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(tsd_t *tsd, ckh_t *ckh) {\n\tckhc_t *tab, *ttab;\n\tsize_t usize;\n\tunsigned lg_prevbuckets, lg_curcells;\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 = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\treturn;\n\t}\n\ttab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true, NULL,\n\t    true, arena_ichoose(tsd, NULL));\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)) {\n\t\tidalloctm(tsd_tsdn(tsd), tab, NULL, NULL, true, true);\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\tidalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true);\n\tckh->tab = tab;\n\tckh->lg_curbuckets = lg_prevbuckets;\n#ifdef CKH_COUNT\n\tckh->nshrinkfails++;\n#endif\n}\n\nbool\nckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,\n    ckh_keycomp_t *keycomp) {\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 minitems\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 mincells items 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\t}\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 = sz_sa2u(sizeof(ckhc_t) << lg_mincells, CACHELINE);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\tckh->tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true,\n\t    NULL, true, arena_ichoose(tsd, NULL));\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(tsd_t *tsd, ckh_t *ckh) {\n\tassert(ckh != NULL);\n\n#ifdef CKH_VERBOSE\n\tmalloc_printf(\n\t    \"%s(%p): ngrows: %\"FMTu64\", nshrinks: %\"FMTu64\",\"\n\t    \" nshrinkfails: %\"FMTu64\", ninserts: %\"FMTu64\",\"\n\t    \" nrelocs: %\"FMTu64\"\\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\tidalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true);\n\tif (config_debug) {\n\t\tmemset(ckh, JEMALLOC_FREE_JUNK, sizeof(ckh_t));\n\t}\n}\n\nsize_t\nckh_count(ckh_t *ckh) {\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\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\t}\n\t\t\tif (data != NULL) {\n\t\t\t\t*data = (void *)ckh->tab[i].data;\n\t\t\t}\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(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data) {\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(tsd, 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(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key,\n    void **data) {\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\t}\n\t\tif (data != NULL) {\n\t\t\t*data = (void *)ckh->tab[cell].data;\n\t\t}\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(tsd, 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\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\t}\n\t\tif (data != NULL) {\n\t\t\t*data = (void *)ckh->tab[cell].data;\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid\nckh_string_hash(const void *key, size_t r_hash[2]) {\n\thash(key, strlen((const char *)key), 0x94122f33U, r_hash);\n}\n\nbool\nckh_string_keycomp(const void *k1, const void *k2) {\n\tassert(k1 != NULL);\n\tassert(k2 != NULL);\n\n\treturn !strcmp((char *)k1, (char *)k2);\n}\n\nvoid\nckh_pointer_hash(const void *key, size_t r_hash[2]) {\n\tunion {\n\t\tconst void\t*v;\n\t\tsize_t\t\ti;\n\t} u;\n\n\tassert(sizeof(u.v) == sizeof(u.i));\n\tu.v = key;\n\thash(&u.i, sizeof(u.i), 0xd983396eU, r_hash);\n}\n\nbool\nckh_pointer_keycomp(const void *k1, const void *k2) {\n\treturn (k1 == k2);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/ctl.c",
    "content": "#define JEMALLOC_CTL_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/nstime.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/*\n * ctl_mtx protects the following:\n * - ctl_stats->*\n */\nstatic malloc_mutex_t\tctl_mtx;\nstatic bool\t\tctl_initialized;\nstatic ctl_stats_t\t*ctl_stats;\nstatic ctl_arenas_t\t*ctl_arenas;\n\n/******************************************************************************/\n/* Helpers for named and indexed nodes. */\n\nstatic const ctl_named_node_t *\nctl_named_node(const ctl_node_t *node) {\n\treturn ((node->named) ? (const ctl_named_node_t *)node : NULL);\n}\n\nstatic const ctl_named_node_t *\nctl_named_children(const ctl_named_node_t *node, size_t index) {\n\tconst ctl_named_node_t *children = ctl_named_node(node->children);\n\n\treturn (children ? &children[index] : NULL);\n}\n\nstatic const ctl_indexed_node_t *\nctl_indexed_node(const ctl_node_t *node) {\n\treturn (!node->named ? (const ctl_indexed_node_t *)node : NULL);\n}\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\n#define CTL_PROTO(n)\t\t\t\t\t\t\t\\\nstatic int\tn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\t\\\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen);\n\n#define INDEX_PROTO(n)\t\t\t\t\t\t\t\\\nstatic const ctl_named_node_t\t*n##_index(tsdn_t *tsdn,\t\t\\\n    const size_t *mib, size_t miblen, size_t i);\n\nCTL_PROTO(version)\nCTL_PROTO(epoch)\nCTL_PROTO(background_thread)\nCTL_PROTO(max_background_threads)\nCTL_PROTO(thread_tcache_enabled)\nCTL_PROTO(thread_tcache_flush)\nCTL_PROTO(thread_prof_name)\nCTL_PROTO(thread_prof_active)\nCTL_PROTO(thread_arena)\nCTL_PROTO(thread_allocated)\nCTL_PROTO(thread_allocatedp)\nCTL_PROTO(thread_deallocated)\nCTL_PROTO(thread_deallocatedp)\nCTL_PROTO(config_cache_oblivious)\nCTL_PROTO(config_debug)\nCTL_PROTO(config_fill)\nCTL_PROTO(config_lazy_lock)\nCTL_PROTO(config_malloc_conf)\nCTL_PROTO(config_prof)\nCTL_PROTO(config_prof_libgcc)\nCTL_PROTO(config_prof_libunwind)\nCTL_PROTO(config_stats)\nCTL_PROTO(config_utrace)\nCTL_PROTO(config_xmalloc)\nCTL_PROTO(opt_abort)\nCTL_PROTO(opt_abort_conf)\nCTL_PROTO(opt_metadata_thp)\nCTL_PROTO(opt_retain)\nCTL_PROTO(opt_dss)\nCTL_PROTO(opt_narenas)\nCTL_PROTO(opt_percpu_arena)\nCTL_PROTO(opt_background_thread)\nCTL_PROTO(opt_max_background_threads)\nCTL_PROTO(opt_dirty_decay_ms)\nCTL_PROTO(opt_muzzy_decay_ms)\nCTL_PROTO(opt_stats_print)\nCTL_PROTO(opt_stats_print_opts)\nCTL_PROTO(opt_junk)\nCTL_PROTO(opt_zero)\nCTL_PROTO(opt_utrace)\nCTL_PROTO(opt_xmalloc)\nCTL_PROTO(opt_tcache)\nCTL_PROTO(opt_thp)\nCTL_PROTO(opt_lg_extent_max_active_fit)\nCTL_PROTO(opt_lg_tcache_max)\nCTL_PROTO(opt_prof)\nCTL_PROTO(opt_prof_prefix)\nCTL_PROTO(opt_prof_active)\nCTL_PROTO(opt_prof_thread_active_init)\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(tcache_create)\nCTL_PROTO(tcache_flush)\nCTL_PROTO(tcache_destroy)\nCTL_PROTO(arena_i_initialized)\nCTL_PROTO(arena_i_decay)\nCTL_PROTO(arena_i_purge)\nCTL_PROTO(arena_i_reset)\nCTL_PROTO(arena_i_destroy)\nCTL_PROTO(arena_i_dss)\nCTL_PROTO(arena_i_dirty_decay_ms)\nCTL_PROTO(arena_i_muzzy_decay_ms)\nCTL_PROTO(arena_i_extent_hooks)\nCTL_PROTO(arena_i_retain_grow_limit)\nINDEX_PROTO(arena_i)\nCTL_PROTO(arenas_bin_i_size)\nCTL_PROTO(arenas_bin_i_nregs)\nCTL_PROTO(arenas_bin_i_slab_size)\nINDEX_PROTO(arenas_bin_i)\nCTL_PROTO(arenas_lextent_i_size)\nINDEX_PROTO(arenas_lextent_i)\nCTL_PROTO(arenas_narenas)\nCTL_PROTO(arenas_dirty_decay_ms)\nCTL_PROTO(arenas_muzzy_decay_ms)\nCTL_PROTO(arenas_quantum)\nCTL_PROTO(arenas_page)\nCTL_PROTO(arenas_tcache_max)\nCTL_PROTO(arenas_nbins)\nCTL_PROTO(arenas_nhbins)\nCTL_PROTO(arenas_nlextents)\nCTL_PROTO(arenas_create)\nCTL_PROTO(arenas_lookup)\nCTL_PROTO(prof_thread_active_init)\nCTL_PROTO(prof_active)\nCTL_PROTO(prof_dump)\nCTL_PROTO(prof_gdump)\nCTL_PROTO(prof_reset)\nCTL_PROTO(prof_interval)\nCTL_PROTO(lg_prof_sample)\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_nmalloc)\nCTL_PROTO(stats_arenas_i_bins_j_ndalloc)\nCTL_PROTO(stats_arenas_i_bins_j_nrequests)\nCTL_PROTO(stats_arenas_i_bins_j_curregs)\nCTL_PROTO(stats_arenas_i_bins_j_nfills)\nCTL_PROTO(stats_arenas_i_bins_j_nflushes)\nCTL_PROTO(stats_arenas_i_bins_j_nslabs)\nCTL_PROTO(stats_arenas_i_bins_j_nreslabs)\nCTL_PROTO(stats_arenas_i_bins_j_curslabs)\nINDEX_PROTO(stats_arenas_i_bins_j)\nCTL_PROTO(stats_arenas_i_lextents_j_nmalloc)\nCTL_PROTO(stats_arenas_i_lextents_j_ndalloc)\nCTL_PROTO(stats_arenas_i_lextents_j_nrequests)\nCTL_PROTO(stats_arenas_i_lextents_j_curlextents)\nINDEX_PROTO(stats_arenas_i_lextents_j)\nCTL_PROTO(stats_arenas_i_nthreads)\nCTL_PROTO(stats_arenas_i_uptime)\nCTL_PROTO(stats_arenas_i_dss)\nCTL_PROTO(stats_arenas_i_dirty_decay_ms)\nCTL_PROTO(stats_arenas_i_muzzy_decay_ms)\nCTL_PROTO(stats_arenas_i_pactive)\nCTL_PROTO(stats_arenas_i_pdirty)\nCTL_PROTO(stats_arenas_i_pmuzzy)\nCTL_PROTO(stats_arenas_i_mapped)\nCTL_PROTO(stats_arenas_i_retained)\nCTL_PROTO(stats_arenas_i_dirty_npurge)\nCTL_PROTO(stats_arenas_i_dirty_nmadvise)\nCTL_PROTO(stats_arenas_i_dirty_purged)\nCTL_PROTO(stats_arenas_i_muzzy_npurge)\nCTL_PROTO(stats_arenas_i_muzzy_nmadvise)\nCTL_PROTO(stats_arenas_i_muzzy_purged)\nCTL_PROTO(stats_arenas_i_base)\nCTL_PROTO(stats_arenas_i_internal)\nCTL_PROTO(stats_arenas_i_metadata_thp)\nCTL_PROTO(stats_arenas_i_tcache_bytes)\nCTL_PROTO(stats_arenas_i_resident)\nINDEX_PROTO(stats_arenas_i)\nCTL_PROTO(stats_allocated)\nCTL_PROTO(stats_active)\nCTL_PROTO(stats_background_thread_num_threads)\nCTL_PROTO(stats_background_thread_num_runs)\nCTL_PROTO(stats_background_thread_run_interval)\nCTL_PROTO(stats_metadata)\nCTL_PROTO(stats_metadata_thp)\nCTL_PROTO(stats_resident)\nCTL_PROTO(stats_mapped)\nCTL_PROTO(stats_retained)\n\n#define MUTEX_STATS_CTL_PROTO_GEN(n)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_ops)\t\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_wait)\t\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_spin_acq)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_owner_switch)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_total_wait_time)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_max_wait_time)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_max_num_thds)\n\n/* Global mutexes. */\n#define OP(mtx) MUTEX_STATS_CTL_PROTO_GEN(mutexes_##mtx)\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\n/* Per arena mutexes. */\n#define OP(mtx) MUTEX_STATS_CTL_PROTO_GEN(arenas_i_mutexes_##mtx)\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\n/* Arena bin mutexes. */\nMUTEX_STATS_CTL_PROTO_GEN(arenas_i_bins_j_mutex)\n#undef MUTEX_STATS_CTL_PROTO_GEN\n\nCTL_PROTO(stats_mutexes_reset)\n\n/******************************************************************************/\n/* mallctl tree. */\n\n#define NAME(n)\t{true},\tn\n#define CHILD(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 CTL(c)\t0, NULL, c##_ctl\n\n/*\n * Only handles internal indexed nodes, since there are currently no external\n * ones.\n */\n#define INDEX(i)\t{false},\ti##_index\n\nstatic const ctl_named_node_t\tthread_tcache_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_prof_node[] = {\n\t{NAME(\"name\"),\t\tCTL(thread_prof_name)},\n\t{NAME(\"active\"),\tCTL(thread_prof_active)}\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, thread_tcache)},\n\t{NAME(\"prof\"),\t\tCHILD(named, thread_prof)}\n};\n\nstatic const ctl_named_node_t\tconfig_node[] = {\n\t{NAME(\"cache_oblivious\"), CTL(config_cache_oblivious)},\n\t{NAME(\"debug\"),\t\tCTL(config_debug)},\n\t{NAME(\"fill\"),\t\tCTL(config_fill)},\n\t{NAME(\"lazy_lock\"),\tCTL(config_lazy_lock)},\n\t{NAME(\"malloc_conf\"),\tCTL(config_malloc_conf)},\n\t{NAME(\"prof\"),\t\tCTL(config_prof)},\n\t{NAME(\"prof_libgcc\"),\tCTL(config_prof_libgcc)},\n\t{NAME(\"prof_libunwind\"), CTL(config_prof_libunwind)},\n\t{NAME(\"stats\"),\t\tCTL(config_stats)},\n\t{NAME(\"utrace\"),\tCTL(config_utrace)},\n\t{NAME(\"xmalloc\"),\tCTL(config_xmalloc)}\n};\n\nstatic const ctl_named_node_t opt_node[] = {\n\t{NAME(\"abort\"),\t\tCTL(opt_abort)},\n\t{NAME(\"abort_conf\"),\tCTL(opt_abort_conf)},\n\t{NAME(\"metadata_thp\"),\tCTL(opt_metadata_thp)},\n\t{NAME(\"retain\"),\tCTL(opt_retain)},\n\t{NAME(\"dss\"),\t\tCTL(opt_dss)},\n\t{NAME(\"narenas\"),\tCTL(opt_narenas)},\n\t{NAME(\"percpu_arena\"),\tCTL(opt_percpu_arena)},\n\t{NAME(\"background_thread\"),\tCTL(opt_background_thread)},\n\t{NAME(\"max_background_threads\"),\tCTL(opt_max_background_threads)},\n\t{NAME(\"dirty_decay_ms\"), CTL(opt_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(opt_muzzy_decay_ms)},\n\t{NAME(\"stats_print\"),\tCTL(opt_stats_print)},\n\t{NAME(\"stats_print_opts\"),\tCTL(opt_stats_print_opts)},\n\t{NAME(\"junk\"),\t\tCTL(opt_junk)},\n\t{NAME(\"zero\"),\t\tCTL(opt_zero)},\n\t{NAME(\"utrace\"),\tCTL(opt_utrace)},\n\t{NAME(\"xmalloc\"),\tCTL(opt_xmalloc)},\n\t{NAME(\"tcache\"),\tCTL(opt_tcache)},\n\t{NAME(\"thp\"),\t\tCTL(opt_thp)},\n\t{NAME(\"lg_extent_max_active_fit\"), CTL(opt_lg_extent_max_active_fit)},\n\t{NAME(\"lg_tcache_max\"),\tCTL(opt_lg_tcache_max)},\n\t{NAME(\"prof\"),\t\tCTL(opt_prof)},\n\t{NAME(\"prof_prefix\"),\tCTL(opt_prof_prefix)},\n\t{NAME(\"prof_active\"),\tCTL(opt_prof_active)},\n\t{NAME(\"prof_thread_active_init\"), CTL(opt_prof_thread_active_init)},\n\t{NAME(\"lg_prof_sample\"), CTL(opt_lg_prof_sample)},\n\t{NAME(\"lg_prof_interval\"), CTL(opt_lg_prof_interval)},\n\t{NAME(\"prof_gdump\"),\tCTL(opt_prof_gdump)},\n\t{NAME(\"prof_final\"),\tCTL(opt_prof_final)},\n\t{NAME(\"prof_leak\"),\tCTL(opt_prof_leak)},\n\t{NAME(\"prof_accum\"),\tCTL(opt_prof_accum)}\n};\n\nstatic const ctl_named_node_t\ttcache_node[] = {\n\t{NAME(\"create\"),\tCTL(tcache_create)},\n\t{NAME(\"flush\"),\t\tCTL(tcache_flush)},\n\t{NAME(\"destroy\"),\tCTL(tcache_destroy)}\n};\n\nstatic const ctl_named_node_t arena_i_node[] = {\n\t{NAME(\"initialized\"),\tCTL(arena_i_initialized)},\n\t{NAME(\"decay\"),\t\tCTL(arena_i_decay)},\n\t{NAME(\"purge\"),\t\tCTL(arena_i_purge)},\n\t{NAME(\"reset\"),\t\tCTL(arena_i_reset)},\n\t{NAME(\"destroy\"),\tCTL(arena_i_destroy)},\n\t{NAME(\"dss\"),\t\tCTL(arena_i_dss)},\n\t{NAME(\"dirty_decay_ms\"), CTL(arena_i_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(arena_i_muzzy_decay_ms)},\n\t{NAME(\"extent_hooks\"),\tCTL(arena_i_extent_hooks)},\n\t{NAME(\"retain_grow_limit\"),\tCTL(arena_i_retain_grow_limit)}\n};\nstatic const ctl_named_node_t super_arena_i_node[] = {\n\t{NAME(\"\"),\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\tCTL(arenas_bin_i_size)},\n\t{NAME(\"nregs\"),\t\tCTL(arenas_bin_i_nregs)},\n\t{NAME(\"slab_size\"),\tCTL(arenas_bin_i_slab_size)}\n};\nstatic const ctl_named_node_t super_arenas_bin_i_node[] = {\n\t{NAME(\"\"),\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_lextent_i_node[] = {\n\t{NAME(\"size\"),\t\tCTL(arenas_lextent_i_size)}\n};\nstatic const ctl_named_node_t super_arenas_lextent_i_node[] = {\n\t{NAME(\"\"),\t\tCHILD(named, arenas_lextent_i)}\n};\n\nstatic const ctl_indexed_node_t arenas_lextent_node[] = {\n\t{INDEX(arenas_lextent_i)}\n};\n\nstatic const ctl_named_node_t arenas_node[] = {\n\t{NAME(\"narenas\"),\tCTL(arenas_narenas)},\n\t{NAME(\"dirty_decay_ms\"), CTL(arenas_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(arenas_muzzy_decay_ms)},\n\t{NAME(\"quantum\"),\tCTL(arenas_quantum)},\n\t{NAME(\"page\"),\t\tCTL(arenas_page)},\n\t{NAME(\"tcache_max\"),\tCTL(arenas_tcache_max)},\n\t{NAME(\"nbins\"),\t\tCTL(arenas_nbins)},\n\t{NAME(\"nhbins\"),\tCTL(arenas_nhbins)},\n\t{NAME(\"bin\"),\t\tCHILD(indexed, arenas_bin)},\n\t{NAME(\"nlextents\"),\tCTL(arenas_nlextents)},\n\t{NAME(\"lextent\"),\tCHILD(indexed, arenas_lextent)},\n\t{NAME(\"create\"),\tCTL(arenas_create)},\n\t{NAME(\"lookup\"),\tCTL(arenas_lookup)}\n};\n\nstatic const ctl_named_node_t\tprof_node[] = {\n\t{NAME(\"thread_active_init\"), CTL(prof_thread_active_init)},\n\t{NAME(\"active\"),\tCTL(prof_active)},\n\t{NAME(\"dump\"),\t\tCTL(prof_dump)},\n\t{NAME(\"gdump\"),\t\tCTL(prof_gdump)},\n\t{NAME(\"reset\"),\t\tCTL(prof_reset)},\n\t{NAME(\"interval\"),\tCTL(prof_interval)},\n\t{NAME(\"lg_sample\"),\tCTL(lg_prof_sample)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_small_node[] = {\n\t{NAME(\"allocated\"),\tCTL(stats_arenas_i_small_allocated)},\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_small_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_small_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_small_nrequests)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_large_node[] = {\n\t{NAME(\"allocated\"),\tCTL(stats_arenas_i_large_allocated)},\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_large_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_large_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_large_nrequests)}\n};\n\n#define MUTEX_PROF_DATA_NODE(prefix)\t\t\t\t\t\\\nstatic const ctl_named_node_t stats_##prefix##_node[] = {\t\t\\\n\t{NAME(\"num_ops\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_ops)},\t\t\t\t\\\n\t{NAME(\"num_wait\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_wait)},\t\t\t\t\\\n\t{NAME(\"num_spin_acq\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_spin_acq)},\t\t\t\t\\\n\t{NAME(\"num_owner_switch\"),\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_owner_switch)},\t\t\t\\\n\t{NAME(\"total_wait_time\"),\t\t\t\t\t\\\n\t CTL(stats_##prefix##_total_wait_time)},\t\t\t\\\n\t{NAME(\"max_wait_time\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_max_wait_time)},\t\t\t\t\\\n\t{NAME(\"max_num_thds\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_max_num_thds)}\t\t\t\t\\\n\t/* Note that # of current waiting thread not provided. */\t\\\n};\n\nMUTEX_PROF_DATA_NODE(arenas_i_bins_j_mutex)\n\nstatic const ctl_named_node_t stats_arenas_i_bins_j_node[] = {\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_bins_j_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_bins_j_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_bins_j_nrequests)},\n\t{NAME(\"curregs\"),\tCTL(stats_arenas_i_bins_j_curregs)},\n\t{NAME(\"nfills\"),\tCTL(stats_arenas_i_bins_j_nfills)},\n\t{NAME(\"nflushes\"),\tCTL(stats_arenas_i_bins_j_nflushes)},\n\t{NAME(\"nslabs\"),\tCTL(stats_arenas_i_bins_j_nslabs)},\n\t{NAME(\"nreslabs\"),\tCTL(stats_arenas_i_bins_j_nreslabs)},\n\t{NAME(\"curslabs\"),\tCTL(stats_arenas_i_bins_j_curslabs)},\n\t{NAME(\"mutex\"),\t\tCHILD(named, stats_arenas_i_bins_j_mutex)}\n};\n\nstatic const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = {\n\t{NAME(\"\"),\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_lextents_j_node[] = {\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_lextents_j_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_lextents_j_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_lextents_j_nrequests)},\n\t{NAME(\"curlextents\"),\tCTL(stats_arenas_i_lextents_j_curlextents)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_lextents_j_node[] = {\n\t{NAME(\"\"),\t\tCHILD(named, stats_arenas_i_lextents_j)}\n};\n\nstatic const ctl_indexed_node_t stats_arenas_i_lextents_node[] = {\n\t{INDEX(stats_arenas_i_lextents_j)}\n};\n\n#define OP(mtx)  MUTEX_PROF_DATA_NODE(arenas_i_mutexes_##mtx)\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\nstatic const ctl_named_node_t stats_arenas_i_mutexes_node[] = {\n#define OP(mtx) {NAME(#mtx), CHILD(named, stats_arenas_i_mutexes_##mtx)},\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n};\n\nstatic const ctl_named_node_t stats_arenas_i_node[] = {\n\t{NAME(\"nthreads\"),\tCTL(stats_arenas_i_nthreads)},\n\t{NAME(\"uptime\"),\tCTL(stats_arenas_i_uptime)},\n\t{NAME(\"dss\"),\t\tCTL(stats_arenas_i_dss)},\n\t{NAME(\"dirty_decay_ms\"), CTL(stats_arenas_i_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(stats_arenas_i_muzzy_decay_ms)},\n\t{NAME(\"pactive\"),\tCTL(stats_arenas_i_pactive)},\n\t{NAME(\"pdirty\"),\tCTL(stats_arenas_i_pdirty)},\n\t{NAME(\"pmuzzy\"),\tCTL(stats_arenas_i_pmuzzy)},\n\t{NAME(\"mapped\"),\tCTL(stats_arenas_i_mapped)},\n\t{NAME(\"retained\"),\tCTL(stats_arenas_i_retained)},\n\t{NAME(\"dirty_npurge\"),\tCTL(stats_arenas_i_dirty_npurge)},\n\t{NAME(\"dirty_nmadvise\"), CTL(stats_arenas_i_dirty_nmadvise)},\n\t{NAME(\"dirty_purged\"),\tCTL(stats_arenas_i_dirty_purged)},\n\t{NAME(\"muzzy_npurge\"),\tCTL(stats_arenas_i_muzzy_npurge)},\n\t{NAME(\"muzzy_nmadvise\"), CTL(stats_arenas_i_muzzy_nmadvise)},\n\t{NAME(\"muzzy_purged\"),\tCTL(stats_arenas_i_muzzy_purged)},\n\t{NAME(\"base\"),\t\tCTL(stats_arenas_i_base)},\n\t{NAME(\"internal\"),\tCTL(stats_arenas_i_internal)},\n\t{NAME(\"metadata_thp\"),\tCTL(stats_arenas_i_metadata_thp)},\n\t{NAME(\"tcache_bytes\"),\tCTL(stats_arenas_i_tcache_bytes)},\n\t{NAME(\"resident\"),\tCTL(stats_arenas_i_resident)},\n\t{NAME(\"small\"),\t\tCHILD(named, stats_arenas_i_small)},\n\t{NAME(\"large\"),\t\tCHILD(named, stats_arenas_i_large)},\n\t{NAME(\"bins\"),\t\tCHILD(indexed, stats_arenas_i_bins)},\n\t{NAME(\"lextents\"),\tCHILD(indexed, stats_arenas_i_lextents)},\n\t{NAME(\"mutexes\"),\tCHILD(named, stats_arenas_i_mutexes)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_node[] = {\n\t{NAME(\"\"),\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_background_thread_node[] = {\n\t{NAME(\"num_threads\"),\tCTL(stats_background_thread_num_threads)},\n\t{NAME(\"num_runs\"),\tCTL(stats_background_thread_num_runs)},\n\t{NAME(\"run_interval\"),\tCTL(stats_background_thread_run_interval)}\n};\n\n#define OP(mtx) MUTEX_PROF_DATA_NODE(mutexes_##mtx)\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\nstatic const ctl_named_node_t stats_mutexes_node[] = {\n#define OP(mtx) {NAME(#mtx), CHILD(named, stats_mutexes_##mtx)},\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\t{NAME(\"reset\"),\t\tCTL(stats_mutexes_reset)}\n};\n#undef MUTEX_PROF_DATA_NODE\n\nstatic const ctl_named_node_t stats_node[] = {\n\t{NAME(\"allocated\"),\tCTL(stats_allocated)},\n\t{NAME(\"active\"),\tCTL(stats_active)},\n\t{NAME(\"metadata\"),\tCTL(stats_metadata)},\n\t{NAME(\"metadata_thp\"),\tCTL(stats_metadata_thp)},\n\t{NAME(\"resident\"),\tCTL(stats_resident)},\n\t{NAME(\"mapped\"),\tCTL(stats_mapped)},\n\t{NAME(\"retained\"),\tCTL(stats_retained)},\n\t{NAME(\"background_thread\"),\n\t CHILD(named, stats_background_thread)},\n\t{NAME(\"mutexes\"),\tCHILD(named, stats_mutexes)},\n\t{NAME(\"arenas\"),\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(\"background_thread\"),\tCTL(background_thread)},\n\t{NAME(\"max_background_threads\"),\tCTL(max_background_threads)},\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(\"tcache\"),\tCHILD(named, tcache)},\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\n/*\n * Sets *dst + *src non-atomically.  This is safe, since everything is\n * synchronized by the ctl mutex.\n */\nstatic void\nctl_accum_arena_stats_u64(arena_stats_u64_t *dst, arena_stats_u64_t *src) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tuint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);\n\tuint64_t cur_src = atomic_load_u64(src, ATOMIC_RELAXED);\n\tatomic_store_u64(dst, cur_dst + cur_src, ATOMIC_RELAXED);\n#else\n\t*dst += *src;\n#endif\n}\n\n/* Likewise: with ctl mutex synchronization, reading is simple. */\nstatic uint64_t\nctl_arena_stats_read_u64(arena_stats_u64_t *p) {\n#ifdef JEMALLOC_ATOMIC_U64\n\treturn atomic_load_u64(p, ATOMIC_RELAXED);\n#else\n\treturn *p;\n#endif\n}\n\nstatic void\naccum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) {\n\tsize_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);\n\tsize_t cur_src = atomic_load_zu(src, ATOMIC_RELAXED);\n\tatomic_store_zu(dst, cur_dst + cur_src, ATOMIC_RELAXED);\n}\n\n/******************************************************************************/\n\nstatic unsigned\narenas_i2a_impl(size_t i, bool compat, bool validate) {\n\tunsigned a;\n\n\tswitch (i) {\n\tcase MALLCTL_ARENAS_ALL:\n\t\ta = 0;\n\t\tbreak;\n\tcase MALLCTL_ARENAS_DESTROYED:\n\t\ta = 1;\n\t\tbreak;\n\tdefault:\n\t\tif (compat && i == ctl_arenas->narenas) {\n\t\t\t/*\n\t\t\t * Provide deprecated backward compatibility for\n\t\t\t * accessing the merged stats at index narenas rather\n\t\t\t * than via MALLCTL_ARENAS_ALL.  This is scheduled for\n\t\t\t * removal in 6.0.0.\n\t\t\t */\n\t\t\ta = 0;\n\t\t} else if (validate && i >= ctl_arenas->narenas) {\n\t\t\ta = UINT_MAX;\n\t\t} else {\n\t\t\t/*\n\t\t\t * This function should never be called for an index\n\t\t\t * more than one past the range of indices that have\n\t\t\t * initialized ctl data.\n\t\t\t */\n\t\t\tassert(i < ctl_arenas->narenas || (!validate && i ==\n\t\t\t    ctl_arenas->narenas));\n\t\t\ta = (unsigned)i + 2;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn a;\n}\n\nstatic unsigned\narenas_i2a(size_t i) {\n\treturn arenas_i2a_impl(i, true, false);\n}\n\nstatic ctl_arena_t *\narenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) {\n\tctl_arena_t *ret;\n\n\tassert(!compat || !init);\n\n\tret = ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)];\n\tif (init && ret == NULL) {\n\t\tif (config_stats) {\n\t\t\tstruct container_s {\n\t\t\t\tctl_arena_t\t\tctl_arena;\n\t\t\t\tctl_arena_stats_t\tastats;\n\t\t\t};\n\t\t\tstruct container_s *cont =\n\t\t\t    (struct container_s *)base_alloc(tsd_tsdn(tsd),\n\t\t\t    b0get(), sizeof(struct container_s), QUANTUM);\n\t\t\tif (cont == NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tret = &cont->ctl_arena;\n\t\t\tret->astats = &cont->astats;\n\t\t} else {\n\t\t\tret = (ctl_arena_t *)base_alloc(tsd_tsdn(tsd), b0get(),\n\t\t\t    sizeof(ctl_arena_t), QUANTUM);\n\t\t\tif (ret == NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t\tret->arena_ind = (unsigned)i;\n\t\tctl_arenas->arenas[arenas_i2a_impl(i, compat, false)] = ret;\n\t}\n\n\tassert(ret == NULL || arenas_i2a(ret->arena_ind) == arenas_i2a(i));\n\treturn ret;\n}\n\nstatic ctl_arena_t *\narenas_i(size_t i) {\n\tctl_arena_t *ret = arenas_i_impl(tsd_fetch(), i, true, false);\n\tassert(ret != NULL);\n\treturn ret;\n}\n\nstatic void\nctl_arena_clear(ctl_arena_t *ctl_arena) {\n\tctl_arena->nthreads = 0;\n\tctl_arena->dss = dss_prec_names[dss_prec_limit];\n\tctl_arena->dirty_decay_ms = -1;\n\tctl_arena->muzzy_decay_ms = -1;\n\tctl_arena->pactive = 0;\n\tctl_arena->pdirty = 0;\n\tctl_arena->pmuzzy = 0;\n\tif (config_stats) {\n\t\tmemset(&ctl_arena->astats->astats, 0, sizeof(arena_stats_t));\n\t\tctl_arena->astats->allocated_small = 0;\n\t\tctl_arena->astats->nmalloc_small = 0;\n\t\tctl_arena->astats->ndalloc_small = 0;\n\t\tctl_arena->astats->nrequests_small = 0;\n\t\tmemset(ctl_arena->astats->bstats, 0, NBINS *\n\t\t    sizeof(bin_stats_t));\n\t\tmemset(ctl_arena->astats->lstats, 0, (NSIZES - NBINS) *\n\t\t    sizeof(arena_stats_large_t));\n\t}\n}\n\nstatic void\nctl_arena_stats_amerge(tsdn_t *tsdn, ctl_arena_t *ctl_arena, arena_t *arena) {\n\tunsigned i;\n\n\tif (config_stats) {\n\t\tarena_stats_merge(tsdn, arena, &ctl_arena->nthreads,\n\t\t    &ctl_arena->dss, &ctl_arena->dirty_decay_ms,\n\t\t    &ctl_arena->muzzy_decay_ms, &ctl_arena->pactive,\n\t\t    &ctl_arena->pdirty, &ctl_arena->pmuzzy,\n\t\t    &ctl_arena->astats->astats, ctl_arena->astats->bstats,\n\t\t    ctl_arena->astats->lstats);\n\n\t\tfor (i = 0; i < NBINS; i++) {\n\t\t\tctl_arena->astats->allocated_small +=\n\t\t\t    ctl_arena->astats->bstats[i].curregs *\n\t\t\t    sz_index2size(i);\n\t\t\tctl_arena->astats->nmalloc_small +=\n\t\t\t    ctl_arena->astats->bstats[i].nmalloc;\n\t\t\tctl_arena->astats->ndalloc_small +=\n\t\t\t    ctl_arena->astats->bstats[i].ndalloc;\n\t\t\tctl_arena->astats->nrequests_small +=\n\t\t\t    ctl_arena->astats->bstats[i].nrequests;\n\t\t}\n\t} else {\n\t\tarena_basic_stats_merge(tsdn, arena, &ctl_arena->nthreads,\n\t\t    &ctl_arena->dss, &ctl_arena->dirty_decay_ms,\n\t\t    &ctl_arena->muzzy_decay_ms, &ctl_arena->pactive,\n\t\t    &ctl_arena->pdirty, &ctl_arena->pmuzzy);\n\t}\n}\n\nstatic void\nctl_arena_stats_sdmerge(ctl_arena_t *ctl_sdarena, ctl_arena_t *ctl_arena,\n    bool destroyed) {\n\tunsigned i;\n\n\tif (!destroyed) {\n\t\tctl_sdarena->nthreads += ctl_arena->nthreads;\n\t\tctl_sdarena->pactive += ctl_arena->pactive;\n\t\tctl_sdarena->pdirty += ctl_arena->pdirty;\n\t\tctl_sdarena->pmuzzy += ctl_arena->pmuzzy;\n\t} else {\n\t\tassert(ctl_arena->nthreads == 0);\n\t\tassert(ctl_arena->pactive == 0);\n\t\tassert(ctl_arena->pdirty == 0);\n\t\tassert(ctl_arena->pmuzzy == 0);\n\t}\n\n\tif (config_stats) {\n\t\tctl_arena_stats_t *sdstats = ctl_sdarena->astats;\n\t\tctl_arena_stats_t *astats = ctl_arena->astats;\n\n\t\tif (!destroyed) {\n\t\t\taccum_atomic_zu(&sdstats->astats.mapped,\n\t\t\t    &astats->astats.mapped);\n\t\t\taccum_atomic_zu(&sdstats->astats.retained,\n\t\t\t    &astats->astats.retained);\n\t\t}\n\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.npurge,\n\t\t    &astats->astats.decay_dirty.npurge);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.nmadvise,\n\t\t    &astats->astats.decay_dirty.nmadvise);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.purged,\n\t\t    &astats->astats.decay_dirty.purged);\n\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.npurge,\n\t\t    &astats->astats.decay_muzzy.npurge);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.nmadvise,\n\t\t    &astats->astats.decay_muzzy.nmadvise);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.purged,\n\t\t    &astats->astats.decay_muzzy.purged);\n\n#define OP(mtx) malloc_mutex_prof_merge(\t\t\t\t\\\n\t\t    &(sdstats->astats.mutex_prof_data[\t\t\t\\\n\t\t        arena_prof_mutex_##mtx]),\t\t\t\\\n\t\t    &(astats->astats.mutex_prof_data[\t\t\t\\\n\t\t        arena_prof_mutex_##mtx]));\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\t\tif (!destroyed) {\n\t\t\taccum_atomic_zu(&sdstats->astats.base,\n\t\t\t    &astats->astats.base);\n\t\t\taccum_atomic_zu(&sdstats->astats.internal,\n\t\t\t    &astats->astats.internal);\n\t\t\taccum_atomic_zu(&sdstats->astats.resident,\n\t\t\t    &astats->astats.resident);\n\t\t\taccum_atomic_zu(&sdstats->astats.metadata_thp,\n\t\t\t    &astats->astats.metadata_thp);\n\t\t} else {\n\t\t\tassert(atomic_load_zu(\n\t\t\t    &astats->astats.internal, ATOMIC_RELAXED) == 0);\n\t\t}\n\n\t\tif (!destroyed) {\n\t\t\tsdstats->allocated_small += astats->allocated_small;\n\t\t} else {\n\t\t\tassert(astats->allocated_small == 0);\n\t\t}\n\t\tsdstats->nmalloc_small += astats->nmalloc_small;\n\t\tsdstats->ndalloc_small += astats->ndalloc_small;\n\t\tsdstats->nrequests_small += astats->nrequests_small;\n\n\t\tif (!destroyed) {\n\t\t\taccum_atomic_zu(&sdstats->astats.allocated_large,\n\t\t\t    &astats->astats.allocated_large);\n\t\t} else {\n\t\t\tassert(atomic_load_zu(&astats->astats.allocated_large,\n\t\t\t    ATOMIC_RELAXED) == 0);\n\t\t}\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.nmalloc_large,\n\t\t    &astats->astats.nmalloc_large);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.ndalloc_large,\n\t\t    &astats->astats.ndalloc_large);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.nrequests_large,\n\t\t    &astats->astats.nrequests_large);\n\n\t\taccum_atomic_zu(&sdstats->astats.tcache_bytes,\n\t\t    &astats->astats.tcache_bytes);\n\n\t\tif (ctl_arena->arena_ind == 0) {\n\t\t\tsdstats->astats.uptime = astats->astats.uptime;\n\t\t}\n\n\t\tfor (i = 0; i < NBINS; i++) {\n\t\t\tsdstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;\n\t\t\tsdstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;\n\t\t\tsdstats->bstats[i].nrequests +=\n\t\t\t    astats->bstats[i].nrequests;\n\t\t\tif (!destroyed) {\n\t\t\t\tsdstats->bstats[i].curregs +=\n\t\t\t\t    astats->bstats[i].curregs;\n\t\t\t} else {\n\t\t\t\tassert(astats->bstats[i].curregs == 0);\n\t\t\t}\n\t\t\tsdstats->bstats[i].nfills += astats->bstats[i].nfills;\n\t\t\tsdstats->bstats[i].nflushes +=\n\t\t\t    astats->bstats[i].nflushes;\n\t\t\tsdstats->bstats[i].nslabs += astats->bstats[i].nslabs;\n\t\t\tsdstats->bstats[i].reslabs += astats->bstats[i].reslabs;\n\t\t\tif (!destroyed) {\n\t\t\t\tsdstats->bstats[i].curslabs +=\n\t\t\t\t    astats->bstats[i].curslabs;\n\t\t\t} else {\n\t\t\t\tassert(astats->bstats[i].curslabs == 0);\n\t\t\t}\n\t\t\tmalloc_mutex_prof_merge(&sdstats->bstats[i].mutex_data,\n\t\t\t    &astats->bstats[i].mutex_data);\n\t\t}\n\n\t\tfor (i = 0; i < NSIZES - NBINS; i++) {\n\t\t\tctl_accum_arena_stats_u64(&sdstats->lstats[i].nmalloc,\n\t\t\t    &astats->lstats[i].nmalloc);\n\t\t\tctl_accum_arena_stats_u64(&sdstats->lstats[i].ndalloc,\n\t\t\t    &astats->lstats[i].ndalloc);\n\t\t\tctl_accum_arena_stats_u64(&sdstats->lstats[i].nrequests,\n\t\t\t    &astats->lstats[i].nrequests);\n\t\t\tif (!destroyed) {\n\t\t\t\tsdstats->lstats[i].curlextents +=\n\t\t\t\t    astats->lstats[i].curlextents;\n\t\t\t} else {\n\t\t\t\tassert(astats->lstats[i].curlextents == 0);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void\nctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, ctl_arena_t *ctl_sdarena,\n    unsigned i, bool destroyed) {\n\tctl_arena_t *ctl_arena = arenas_i(i);\n\n\tctl_arena_clear(ctl_arena);\n\tctl_arena_stats_amerge(tsdn, ctl_arena, arena);\n\t/* Merge into sum stats as well. */\n\tctl_arena_stats_sdmerge(ctl_sdarena, ctl_arena, destroyed);\n}\n\nstatic unsigned\nctl_arena_init(tsd_t *tsd, extent_hooks_t *extent_hooks) {\n\tunsigned arena_ind;\n\tctl_arena_t *ctl_arena;\n\n\tif ((ctl_arena = ql_last(&ctl_arenas->destroyed, destroyed_link)) !=\n\t    NULL) {\n\t\tql_remove(&ctl_arenas->destroyed, ctl_arena, destroyed_link);\n\t\tarena_ind = ctl_arena->arena_ind;\n\t} else {\n\t\tarena_ind = ctl_arenas->narenas;\n\t}\n\n\t/* Trigger stats allocation. */\n\tif (arenas_i_impl(tsd, arena_ind, false, true) == NULL) {\n\t\treturn UINT_MAX;\n\t}\n\n\t/* Initialize new arena. */\n\tif (arena_init(tsd_tsdn(tsd), arena_ind, extent_hooks) == NULL) {\n\t\treturn UINT_MAX;\n\t}\n\n\tif (arena_ind == ctl_arenas->narenas) {\n\t\tctl_arenas->narenas++;\n\t}\n\n\treturn arena_ind;\n}\n\nstatic void\nctl_background_thread_stats_read(tsdn_t *tsdn) {\n\tbackground_thread_stats_t *stats = &ctl_stats->background_thread;\n\tif (!have_background_thread ||\n\t    background_thread_stats_read(tsdn, stats)) {\n\t\tmemset(stats, 0, sizeof(background_thread_stats_t));\n\t\tnstime_init(&stats->run_interval, 0);\n\t}\n}\n\nstatic void\nctl_refresh(tsdn_t *tsdn) {\n\tunsigned i;\n\tctl_arena_t *ctl_sarena = arenas_i(MALLCTL_ARENAS_ALL);\n\tVARIABLE_ARRAY(arena_t *, tarenas, ctl_arenas->narenas);\n\n\t/*\n\t * Clear sum stats, since they will be merged into by\n\t * ctl_arena_refresh().\n\t */\n\tctl_arena_clear(ctl_sarena);\n\n\tfor (i = 0; i < ctl_arenas->narenas; i++) {\n\t\ttarenas[i] = arena_get(tsdn, i, false);\n\t}\n\n\tfor (i = 0; i < ctl_arenas->narenas; i++) {\n\t\tctl_arena_t *ctl_arena = arenas_i(i);\n\t\tbool initialized = (tarenas[i] != NULL);\n\n\t\tctl_arena->initialized = initialized;\n\t\tif (initialized) {\n\t\t\tctl_arena_refresh(tsdn, tarenas[i], ctl_sarena, i,\n\t\t\t    false);\n\t\t}\n\t}\n\n\tif (config_stats) {\n\t\tctl_stats->allocated = ctl_sarena->astats->allocated_small +\n\t\t    atomic_load_zu(&ctl_sarena->astats->astats.allocated_large,\n\t\t\tATOMIC_RELAXED);\n\t\tctl_stats->active = (ctl_sarena->pactive << LG_PAGE);\n\t\tctl_stats->metadata = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.base, ATOMIC_RELAXED) +\n\t\t    atomic_load_zu(&ctl_sarena->astats->astats.internal,\n\t\t\tATOMIC_RELAXED);\n\t\tctl_stats->metadata_thp = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.metadata_thp, ATOMIC_RELAXED);\n\t\tctl_stats->resident = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.resident, ATOMIC_RELAXED);\n\t\tctl_stats->mapped = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.mapped, ATOMIC_RELAXED);\n\t\tctl_stats->retained = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.retained, ATOMIC_RELAXED);\n\n\t\tctl_background_thread_stats_read(tsdn);\n\n#define READ_GLOBAL_MUTEX_PROF_DATA(i, mtx)\t\t\t\t\\\n    malloc_mutex_lock(tsdn, &mtx);\t\t\t\t\t\\\n    malloc_mutex_prof_read(tsdn, &ctl_stats->mutex_prof_data[i], &mtx);\t\\\n    malloc_mutex_unlock(tsdn, &mtx);\n\n\t\tif (config_prof && opt_prof) {\n\t\t\tREAD_GLOBAL_MUTEX_PROF_DATA(global_prof_mutex_prof,\n\t\t\t    bt2gctx_mtx);\n\t\t}\n\t\tif (have_background_thread) {\n\t\t\tREAD_GLOBAL_MUTEX_PROF_DATA(\n\t\t\t    global_prof_mutex_background_thread,\n\t\t\t    background_thread_lock);\n\t\t} else {\n\t\t\tmemset(&ctl_stats->mutex_prof_data[\n\t\t\t    global_prof_mutex_background_thread], 0,\n\t\t\t    sizeof(mutex_prof_data_t));\n\t\t}\n\t\t/* We own ctl mutex already. */\n\t\tmalloc_mutex_prof_read(tsdn,\n\t\t    &ctl_stats->mutex_prof_data[global_prof_mutex_ctl],\n\t\t    &ctl_mtx);\n#undef READ_GLOBAL_MUTEX_PROF_DATA\n\t}\n\tctl_arenas->epoch++;\n}\n\nstatic bool\nctl_init(tsd_t *tsd) {\n\tbool ret;\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\tif (!ctl_initialized) {\n\t\tctl_arena_t *ctl_sarena, *ctl_darena;\n\t\tunsigned i;\n\n\t\t/*\n\t\t * Allocate demand-zeroed space for pointers to the full\n\t\t * range of supported arena indices.\n\t\t */\n\t\tif (ctl_arenas == NULL) {\n\t\t\tctl_arenas = (ctl_arenas_t *)base_alloc(tsdn,\n\t\t\t    b0get(), sizeof(ctl_arenas_t), QUANTUM);\n\t\t\tif (ctl_arenas == NULL) {\n\t\t\t\tret = true;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\tif (config_stats && ctl_stats == NULL) {\n\t\t\tctl_stats = (ctl_stats_t *)base_alloc(tsdn, b0get(),\n\t\t\t    sizeof(ctl_stats_t), QUANTUM);\n\t\t\tif (ctl_stats == NULL) {\n\t\t\t\tret = true;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Allocate space for the current full range of arenas\n\t\t * here rather than doing it lazily elsewhere, in order\n\t\t * to limit when OOM-caused errors can occur.\n\t\t */\n\t\tif ((ctl_sarena = arenas_i_impl(tsd, MALLCTL_ARENAS_ALL, false,\n\t\t    true)) == NULL) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\tctl_sarena->initialized = true;\n\n\t\tif ((ctl_darena = arenas_i_impl(tsd, MALLCTL_ARENAS_DESTROYED,\n\t\t    false, true)) == NULL) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\tctl_arena_clear(ctl_darena);\n\t\t/*\n\t\t * Don't toggle ctl_darena to initialized until an arena is\n\t\t * actually destroyed, so that arena.<i>.initialized can be used\n\t\t * to query whether the stats are relevant.\n\t\t */\n\n\t\tctl_arenas->narenas = narenas_total_get();\n\t\tfor (i = 0; i < ctl_arenas->narenas; i++) {\n\t\t\tif (arenas_i_impl(tsd, i, false, true) == NULL) {\n\t\t\t\tret = true;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\tql_new(&ctl_arenas->destroyed);\n\t\tctl_refresh(tsdn);\n\n\t\tctl_initialized = true;\n\t}\n\n\tret = false;\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\nctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,\n    size_t *mibp, size_t *depthp) {\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\t}\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(tsdn, 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\t}\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(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen) {\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 && ctl_init(tsd)) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\tdepth = CTL_MAX_DEPTH;\n\tret = ctl_lookup(tsd_tsdn(tsd), name, nodes, mib, &depth);\n\tif (ret != 0) {\n\t\tgoto label_return;\n\t}\n\n\tnode = ctl_named_node(nodes[depth-1]);\n\tif (node != NULL && node->ctl) {\n\t\tret = node->ctl(tsd, mib, depth, oldp, oldlenp, newp, newlen);\n\t} else {\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(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp) {\n\tint ret;\n\n\tif (!ctl_initialized && ctl_init(tsd)) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\tret = ctl_lookup(tsd_tsdn(tsd), name, NULL, mibp, miblenp);\nlabel_return:\n\treturn(ret);\n}\n\nint\nctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tconst ctl_named_node_t *node;\n\tsize_t i;\n\n\tif (!ctl_initialized && ctl_init(tsd)) {\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(tsd_tsdn(tsd), 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(tsd, mib, miblen, oldp, oldlenp, newp, newlen);\n\t} else {\n\t\t/* Partial MIB. */\n\t\tret = ENOENT;\n\t}\n\nlabel_return:\n\treturn(ret);\n}\n\nbool\nctl_boot(void) {\n\tif (malloc_mutex_init(&ctl_mtx, \"ctl\", WITNESS_RANK_CTL,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\tctl_initialized = false;\n\n\treturn false;\n}\n\nvoid\nctl_prefork(tsdn_t *tsdn) {\n\tmalloc_mutex_prefork(tsdn, &ctl_mtx);\n}\n\nvoid\nctl_postfork_parent(tsdn_t *tsdn) {\n\tmalloc_mutex_postfork_parent(tsdn, &ctl_mtx);\n}\n\nvoid\nctl_postfork_child(tsdn_t *tsdn) {\n\tmalloc_mutex_postfork_child(tsdn, &ctl_mtx);\n}\n\n/******************************************************************************/\n/* *_ctl() functions. */\n\n#define READONLY()\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 WRITEONLY()\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 READ_XOR_WRITE()\tdo {\t\t\t\t\t\\\n\tif ((oldp != NULL && oldlenp != NULL) && (newp != NULL ||\t\\\n\t    newlen != 0)) {\t\t\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 READ(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}\t\t\t\t\t\t\t\\\n\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 WRITE(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\t(v) = *(t *)newp;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define MIB_UNSIGNED(v, i) do {\t\t\t\t\t\t\\\n\tif (mib[i] > UINT_MAX) {\t\t\t\t\t\\\n\t\tret = EFAULT;\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\tv = (unsigned)mib[i];\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 CTL_RO_CLGEN(c, l, n, v, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif (l) {\t\t\t\t\t\t\t\\\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\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\tif (l) {\t\t\t\t\t\t\t\\\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn ret;\t\t\t\t\t\t\t\\\n}\n\n#define CTL_RO_CGEN(c, n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\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(tsd_tsdn(tsd), &ctl_mtx);\t\t\t\\\n\treturn ret;\t\t\t\t\t\t\t\\\n}\n\n#define CTL_RO_GEN(n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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(tsd_tsdn(tsd), &ctl_mtx);\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(tsd_tsdn(tsd), &ctl_mtx);\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 CTL_RO_NL_CGEN(c, n, v, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\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 CTL_RO_NL_GEN(n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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 CTL_TSD_RO_NL_CGEN(c, n, m, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\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 = (m(tsd));\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 CTL_RO_CONFIG_GEN(n, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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 = n;\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/******************************************************************************/\n\nCTL_RO_NL_GEN(version, JEMALLOC_VERSION, const char *)\n\nstatic int\nepoch_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tUNUSED uint64_t newval;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tWRITE(newval, uint64_t);\n\tif (newp != NULL) {\n\t\tctl_refresh(tsd_tsdn(tsd));\n\t}\n\tREAD(ctl_arenas->epoch, uint64_t);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\nbackground_thread_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!have_background_thread) {\n\t\treturn ENOENT;\n\t}\n\tbackground_thread_ctl_init(tsd_tsdn(tsd));\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\tif (newp == NULL) {\n\t\toldval = background_thread_enabled();\n\t\tREAD(oldval, bool);\n\t} else {\n\t\tif (newlen != sizeof(bool)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\toldval = background_thread_enabled();\n\t\tREAD(oldval, bool);\n\n\t\tbool newval = *(bool *)newp;\n\t\tif (newval == oldval) {\n\t\t\tret = 0;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tbackground_thread_enabled_set(tsd_tsdn(tsd), newval);\n\t\tif (newval) {\n\t\t\tif (!can_enable_background_thread) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: Error in dlsym(\"\n\t\t\t            \"RTLD_NEXT, \\\"pthread_create\\\"). Cannot \"\n\t\t\t\t    \"enable background_thread\\n\");\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tif (background_threads_enable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t} else {\n\t\t\tif (background_threads_disable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\t}\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\n\treturn ret;\n}\n\nstatic int\nmax_background_threads_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tsize_t oldval;\n\n\tif (!have_background_thread) {\n\t\treturn ENOENT;\n\t}\n\tbackground_thread_ctl_init(tsd_tsdn(tsd));\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\tif (newp == NULL) {\n\t\toldval = max_background_threads;\n\t\tREAD(oldval, size_t);\n\t} else {\n\t\tif (newlen != sizeof(size_t)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\toldval = max_background_threads;\n\t\tREAD(oldval, size_t);\n\n\t\tsize_t newval = *(size_t *)newp;\n\t\tif (newval == oldval) {\n\t\t\tret = 0;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (newval > opt_max_background_threads) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tif (background_thread_enabled()) {\n\t\t\tif (!can_enable_background_thread) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: Error in dlsym(\"\n\t\t\t            \"RTLD_NEXT, \\\"pthread_create\\\"). Cannot \"\n\t\t\t\t    \"enable background_thread\\n\");\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tbackground_thread_enabled_set(tsd_tsdn(tsd), false);\n\t\t\tif (background_threads_disable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tmax_background_threads = newval;\n\t\t\tbackground_thread_enabled_set(tsd_tsdn(tsd), true);\n\t\t\tif (background_threads_enable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t} else {\n\t\t\tmax_background_threads = newval;\n\t\t}\n\t}\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\n\treturn ret;\n}\n\n/******************************************************************************/\n\nCTL_RO_CONFIG_GEN(config_cache_oblivious, bool)\nCTL_RO_CONFIG_GEN(config_debug, bool)\nCTL_RO_CONFIG_GEN(config_fill, bool)\nCTL_RO_CONFIG_GEN(config_lazy_lock, bool)\nCTL_RO_CONFIG_GEN(config_malloc_conf, const char *)\nCTL_RO_CONFIG_GEN(config_prof, bool)\nCTL_RO_CONFIG_GEN(config_prof_libgcc, bool)\nCTL_RO_CONFIG_GEN(config_prof_libunwind, bool)\nCTL_RO_CONFIG_GEN(config_stats, bool)\nCTL_RO_CONFIG_GEN(config_utrace, bool)\nCTL_RO_CONFIG_GEN(config_xmalloc, bool)\n\n/******************************************************************************/\n\nCTL_RO_NL_GEN(opt_abort, opt_abort, bool)\nCTL_RO_NL_GEN(opt_abort_conf, opt_abort_conf, bool)\nCTL_RO_NL_GEN(opt_metadata_thp, metadata_thp_mode_names[opt_metadata_thp],\n    const char *)\nCTL_RO_NL_GEN(opt_retain, opt_retain, bool)\nCTL_RO_NL_GEN(opt_dss, opt_dss, const char *)\nCTL_RO_NL_GEN(opt_narenas, opt_narenas, unsigned)\nCTL_RO_NL_GEN(opt_percpu_arena, percpu_arena_mode_names[opt_percpu_arena],\n    const char *)\nCTL_RO_NL_GEN(opt_background_thread, opt_background_thread, bool)\nCTL_RO_NL_GEN(opt_max_background_threads, opt_max_background_threads, size_t)\nCTL_RO_NL_GEN(opt_dirty_decay_ms, opt_dirty_decay_ms, ssize_t)\nCTL_RO_NL_GEN(opt_muzzy_decay_ms, opt_muzzy_decay_ms, ssize_t)\nCTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)\nCTL_RO_NL_GEN(opt_stats_print_opts, opt_stats_print_opts, const char *)\nCTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *)\nCTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool)\nCTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool)\nCTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool)\nCTL_RO_NL_GEN(opt_tcache, opt_tcache, bool)\nCTL_RO_NL_GEN(opt_thp, thp_mode_names[opt_thp], const char *)\nCTL_RO_NL_GEN(opt_lg_extent_max_active_fit, opt_lg_extent_max_active_fit,\n    size_t)\nCTL_RO_NL_GEN(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_NL_CGEN(config_prof, opt_prof_active, opt_prof_active, bool)\nCTL_RO_NL_CGEN(config_prof, opt_prof_thread_active_init,\n    opt_prof_thread_active_init, bool)\nCTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t)\nCTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool)\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)\n\n/******************************************************************************/\n\nstatic int\nthread_arena_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tarena_t *oldarena;\n\tunsigned newind, oldind;\n\n\toldarena = arena_choose(tsd, NULL);\n\tif (oldarena == NULL) {\n\t\treturn EAGAIN;\n\t}\n\tnewind = oldind = arena_ind_get(oldarena);\n\tWRITE(newind, unsigned);\n\tREAD(oldind, unsigned);\n\n\tif (newind != oldind) {\n\t\tarena_t *newarena;\n\n\t\tif (newind >= narenas_total_get()) {\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\tif (have_percpu_arena &&\n\t\t    PERCPU_ARENA_ENABLED(opt_percpu_arena)) {\n\t\t\tif (newind < percpu_arena_ind_limit(opt_percpu_arena)) {\n\t\t\t\t/*\n\t\t\t\t * If perCPU arena is enabled, thread_arena\n\t\t\t\t * control is not allowed for the auto arena\n\t\t\t\t * range.\n\t\t\t\t */\n\t\t\t\tret = EPERM;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\t/* Initialize arena if necessary. */\n\t\tnewarena = arena_get(tsd_tsdn(tsd), newind, true);\n\t\tif (newarena == NULL) {\n\t\t\tret = EAGAIN;\n\t\t\tgoto label_return;\n\t\t}\n\t\t/* Set new arena/tcache associations. */\n\t\tarena_migrate(tsd, oldind, newind);\n\t\tif (tcache_available(tsd)) {\n\t\t\ttcache_arena_reassociate(tsd_tsdn(tsd),\n\t\t\t    tsd_tcachep_get(tsd), newarena);\n\t\t}\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nCTL_TSD_RO_NL_CGEN(config_stats, thread_allocated, tsd_thread_allocated_get,\n    uint64_t)\nCTL_TSD_RO_NL_CGEN(config_stats, thread_allocatedp, tsd_thread_allocatedp_get,\n    uint64_t *)\nCTL_TSD_RO_NL_CGEN(config_stats, thread_deallocated, tsd_thread_deallocated_get,\n    uint64_t)\nCTL_TSD_RO_NL_CGEN(config_stats, thread_deallocatedp,\n    tsd_thread_deallocatedp_get, uint64_t *)\n\nstatic int\nthread_tcache_enabled_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\toldval = tcache_enabled_get(tsd);\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(tsd, *(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(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\n\tif (!tcache_available(tsd)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tREADONLY();\n\tWRITEONLY();\n\n\ttcache_flush(tsd);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nthread_prof_name_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\tREAD_XOR_WRITE();\n\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(const char *)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tif ((ret = prof_thread_name_set(tsd, *(const char **)newp)) !=\n\t\t    0) {\n\t\t\tgoto label_return;\n\t\t}\n\t} else {\n\t\tconst char *oldname = prof_thread_name_get(tsd);\n\t\tREAD(oldname, const char *);\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nthread_prof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\toldval = prof_thread_active_get(tsd);\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\tif (prof_thread_active_set(tsd, *(bool *)newp)) {\n\t\t\tret = EAGAIN;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\ntcache_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned tcache_ind;\n\n\tREADONLY();\n\tif (tcaches_create(tsd, &tcache_ind)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\tREAD(tcache_ind, unsigned);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\ntcache_flush_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned tcache_ind;\n\n\tWRITEONLY();\n\ttcache_ind = UINT_MAX;\n\tWRITE(tcache_ind, unsigned);\n\tif (tcache_ind == UINT_MAX) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\ttcaches_flush(tsd, tcache_ind);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\ntcache_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned tcache_ind;\n\n\tWRITEONLY();\n\ttcache_ind = UINT_MAX;\n\tWRITE(tcache_ind, unsigned);\n\tif (tcache_ind == UINT_MAX) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\ttcaches_destroy(tsd, tcache_ind);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\narena_i_initialized_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\tunsigned arena_ind;\n\tbool initialized;\n\n\tREADONLY();\n\tMIB_UNSIGNED(arena_ind, 1);\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\tinitialized = arenas_i(arena_ind)->initialized;\n\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\n\tREAD(initialized, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic void\narena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) {\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\t{\n\t\tunsigned narenas = ctl_arenas->narenas;\n\n\t\t/*\n\t\t * Access via index narenas is deprecated, and scheduled for\n\t\t * removal in 6.0.0.\n\t\t */\n\t\tif (arena_ind == MALLCTL_ARENAS_ALL || arena_ind == narenas) {\n\t\t\tunsigned i;\n\t\t\tVARIABLE_ARRAY(arena_t *, tarenas, narenas);\n\n\t\t\tfor (i = 0; i < narenas; i++) {\n\t\t\t\ttarenas[i] = arena_get(tsdn, i, false);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * No further need to hold ctl_mtx, since narenas and\n\t\t\t * tarenas contain everything needed below.\n\t\t\t */\n\t\t\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\n\t\t\tfor (i = 0; i < narenas; i++) {\n\t\t\t\tif (tarenas[i] != NULL) {\n\t\t\t\t\tarena_decay(tsdn, tarenas[i], false,\n\t\t\t\t\t    all);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tarena_t *tarena;\n\n\t\t\tassert(arena_ind < narenas);\n\n\t\t\ttarena = arena_get(tsdn, arena_ind, false);\n\n\t\t\t/* No further need to hold ctl_mtx. */\n\t\t\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\n\t\t\tif (tarena != NULL) {\n\t\t\t\tarena_decay(tsdn, tarena, false, all);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic int\narena_i_decay_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\n\tREADONLY();\n\tWRITEONLY();\n\tMIB_UNSIGNED(arena_ind, 1);\n\tarena_i_decay(tsd_tsdn(tsd), arena_ind, false);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_purge_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\n\tREADONLY();\n\tWRITEONLY();\n\tMIB_UNSIGNED(arena_ind, 1);\n\tarena_i_decay(tsd_tsdn(tsd), arena_ind, true);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_reset_destroy_helper(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen, unsigned *arena_ind,\n    arena_t **arena) {\n\tint ret;\n\n\tREADONLY();\n\tWRITEONLY();\n\tMIB_UNSIGNED(*arena_ind, 1);\n\n\t*arena = arena_get(tsd_tsdn(tsd), *arena_ind, false);\n\tif (*arena == NULL || arena_is_auto(*arena)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic void\narena_reset_prepare_background_thread(tsd_t *tsd, unsigned arena_ind) {\n\t/* Temporarily disable the background thread during arena reset. */\n\tif (have_background_thread) {\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\t\tif (background_thread_enabled()) {\n\t\t\tunsigned ind = arena_ind % ncpus;\n\t\t\tbackground_thread_info_t *info =\n\t\t\t    &background_thread_info[ind];\n\t\t\tassert(info->state == background_thread_started);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\t\tinfo->state = background_thread_paused;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\t}\n\t}\n}\n\nstatic void\narena_reset_finish_background_thread(tsd_t *tsd, unsigned arena_ind) {\n\tif (have_background_thread) {\n\t\tif (background_thread_enabled()) {\n\t\t\tunsigned ind = arena_ind % ncpus;\n\t\t\tbackground_thread_info_t *info =\n\t\t\t    &background_thread_info[ind];\n\t\t\tassert(info->state == background_thread_paused);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\t\tinfo->state = background_thread_started;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\t}\n}\n\nstatic int\narena_i_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,\n\t    newp, newlen, &arena_ind, &arena);\n\tif (ret != 0) {\n\t\treturn ret;\n\t}\n\n\tarena_reset_prepare_background_thread(tsd, arena_ind);\n\tarena_reset(tsd, arena);\n\tarena_reset_finish_background_thread(tsd, arena_ind);\n\n\treturn ret;\n}\n\nstatic int\narena_i_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\tctl_arena_t *ctl_darena, *ctl_arena;\n\n\tret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,\n\t    newp, newlen, &arena_ind, &arena);\n\tif (ret != 0) {\n\t\tgoto label_return;\n\t}\n\n\tif (arena_nthreads_get(arena, false) != 0 || arena_nthreads_get(arena,\n\t    true) != 0) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tarena_reset_prepare_background_thread(tsd, arena_ind);\n\t/* Merge stats after resetting and purging arena. */\n\tarena_reset(tsd, arena);\n\tarena_decay(tsd_tsdn(tsd), arena, false, true);\n\tctl_darena = arenas_i(MALLCTL_ARENAS_DESTROYED);\n\tctl_darena->initialized = true;\n\tctl_arena_refresh(tsd_tsdn(tsd), arena, ctl_darena, arena_ind, true);\n\t/* Destroy arena. */\n\tarena_destroy(tsd, arena);\n\tctl_arena = arenas_i(arena_ind);\n\tctl_arena->initialized = false;\n\t/* Record arena index for later recycling via arenas.create. */\n\tql_elm_new(ctl_arena, destroyed_link);\n\tql_tail_insert(&ctl_arenas->destroyed, ctl_arena, destroyed_link);\n\tarena_reset_finish_background_thread(tsd, arena_ind);\n\n\tassert(ret == 0);\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_dss_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tconst char *dss = NULL;\n\tunsigned arena_ind;\n\tdss_prec_t dss_prec_old = dss_prec_limit;\n\tdss_prec_t dss_prec = dss_prec_limit;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tWRITE(dss, const char *);\n\tMIB_UNSIGNED(arena_ind, 1);\n\tif (dss != NULL) {\n\t\tint i;\n\t\tbool match = false;\n\n\t\tfor (i = 0; i < dss_prec_limit; i++) {\n\t\t\tif (strcmp(dss_prec_names[i], dss) == 0) {\n\t\t\t\tdss_prec = i;\n\t\t\t\tmatch = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!match) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\t/*\n\t * Access via index narenas is deprecated, and scheduled for removal in\n\t * 6.0.0.\n\t */\n\tif (arena_ind == MALLCTL_ARENAS_ALL || arena_ind ==\n\t    ctl_arenas->narenas) {\n\t\tif (dss_prec != dss_prec_limit &&\n\t\t    extent_dss_prec_set(dss_prec)) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t\tdss_prec_old = extent_dss_prec_get();\n\t} else {\n\t\tarena_t *arena = arena_get(tsd_tsdn(tsd), arena_ind, false);\n\t\tif (arena == NULL || (dss_prec != dss_prec_limit &&\n\t\t    arena_dss_prec_set(arena, dss_prec))) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t\tdss_prec_old = arena_dss_prec_get(arena);\n\t}\n\n\tdss = dss_prec_names[dss_prec_old];\n\tREAD(dss, const char *);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen, bool dirty) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tMIB_UNSIGNED(arena_ind, 1);\n\tarena = arena_get(tsd_tsdn(tsd), arena_ind, false);\n\tif (arena == NULL) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tif (oldp != NULL && oldlenp != NULL) {\n\t\tsize_t oldval = dirty ? arena_dirty_decay_ms_get(arena) :\n\t\t    arena_muzzy_decay_ms_get(arena);\n\t\tREAD(oldval, ssize_t);\n\t}\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(ssize_t)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (dirty ? arena_dirty_decay_ms_set(tsd_tsdn(tsd), arena,\n\t\t    *(ssize_t *)newp) : arena_muzzy_decay_ms_set(tsd_tsdn(tsd),\n\t\t    arena, *(ssize_t *)newp)) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_dirty_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arena_i_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, true);\n}\n\nstatic int\narena_i_muzzy_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arena_i_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, false);\n}\n\nstatic int\narena_i_extent_hooks_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tMIB_UNSIGNED(arena_ind, 1);\n\tif (arena_ind < narenas_total_get()) {\n\t\textent_hooks_t *old_extent_hooks;\n\t\tarena = arena_get(tsd_tsdn(tsd), arena_ind, false);\n\t\tif (arena == NULL) {\n\t\t\tif (arena_ind >= narenas_auto) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\told_extent_hooks =\n\t\t\t    (extent_hooks_t *)&extent_hooks_default;\n\t\t\tREAD(old_extent_hooks, extent_hooks_t *);\n\t\t\tif (newp != NULL) {\n\t\t\t\t/* Initialize a new arena as a side effect. */\n\t\t\t\textent_hooks_t *new_extent_hooks\n\t\t\t\t    JEMALLOC_CC_SILENCE_INIT(NULL);\n\t\t\t\tWRITE(new_extent_hooks, extent_hooks_t *);\n\t\t\t\tarena = arena_init(tsd_tsdn(tsd), arena_ind,\n\t\t\t\t    new_extent_hooks);\n\t\t\t\tif (arena == NULL) {\n\t\t\t\t\tret = EFAULT;\n\t\t\t\t\tgoto label_return;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (newp != NULL) {\n\t\t\t\textent_hooks_t *new_extent_hooks\n\t\t\t\t    JEMALLOC_CC_SILENCE_INIT(NULL);\n\t\t\t\tWRITE(new_extent_hooks, extent_hooks_t *);\n\t\t\t\told_extent_hooks = extent_hooks_set(tsd, arena,\n\t\t\t\t    new_extent_hooks);\n\t\t\t\tREAD(old_extent_hooks, extent_hooks_t *);\n\t\t\t} else {\n\t\t\t\told_extent_hooks = extent_hooks_get(arena);\n\t\t\t\tREAD(old_extent_hooks, extent_hooks_t *);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narena_i_retain_grow_limit_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tif (!opt_retain) {\n\t\t/* Only relevant when retain is enabled. */\n\t\treturn ENOENT;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tMIB_UNSIGNED(arena_ind, 1);\n\tif (arena_ind < narenas_total_get() && (arena =\n\t    arena_get(tsd_tsdn(tsd), arena_ind, false)) != NULL) {\n\t\tsize_t old_limit, new_limit;\n\t\tif (newp != NULL) {\n\t\t\tWRITE(new_limit, size_t);\n\t\t}\n\t\tbool err = arena_retain_grow_limit_get_set(tsd, arena,\n\t\t    &old_limit, newp != NULL ? &new_limit : NULL);\n\t\tif (!err) {\n\t\t\tREAD(old_limit, size_t);\n\t\t\tret = 0;\n\t\t} else {\n\t\t\tret = EFAULT;\n\t\t}\n\t} else {\n\t\tret = EFAULT;\n\t}\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic const ctl_named_node_t *\narena_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {\n\tconst ctl_named_node_t *ret;\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\tswitch (i) {\n\tcase MALLCTL_ARENAS_ALL:\n\tcase MALLCTL_ARENAS_DESTROYED:\n\t\tbreak;\n\tdefault:\n\t\tif (i > ctl_arenas->narenas) {\n\t\t\tret = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tbreak;\n\t}\n\n\tret = super_arena_i_node;\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\narenas_narenas_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned narenas;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tREADONLY();\n\tif (*oldlenp != sizeof(unsigned)) {\n\t\tret = EINVAL;\n\t\tgoto label_return;\n\t}\n\tnarenas = ctl_arenas->narenas;\n\tREAD(narenas, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narenas_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen, bool dirty) {\n\tint ret;\n\n\tif (oldp != NULL && oldlenp != NULL) {\n\t\tsize_t oldval = (dirty ? arena_dirty_decay_ms_default_get() :\n\t\t    arena_muzzy_decay_ms_default_get());\n\t\tREAD(oldval, ssize_t);\n\t}\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(ssize_t)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (dirty ? arena_dirty_decay_ms_default_set(*(ssize_t *)newp)\n\t\t    : arena_muzzy_decay_ms_default_set(*(ssize_t *)newp)) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narenas_dirty_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arenas_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, true);\n}\n\nstatic int\narenas_muzzy_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arenas_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, false);\n}\n\nCTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t)\nCTL_RO_NL_GEN(arenas_page, PAGE, size_t)\nCTL_RO_NL_GEN(arenas_tcache_max, tcache_maxclass, size_t)\nCTL_RO_NL_GEN(arenas_nbins, NBINS, unsigned)\nCTL_RO_NL_GEN(arenas_nhbins, nhbins, unsigned)\nCTL_RO_NL_GEN(arenas_bin_i_size, bin_infos[mib[2]].reg_size, size_t)\nCTL_RO_NL_GEN(arenas_bin_i_nregs, bin_infos[mib[2]].nregs, uint32_t)\nCTL_RO_NL_GEN(arenas_bin_i_slab_size, bin_infos[mib[2]].slab_size, size_t)\nstatic const ctl_named_node_t *\narenas_bin_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {\n\tif (i > NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_arenas_bin_i_node;\n}\n\nCTL_RO_NL_GEN(arenas_nlextents, NSIZES - NBINS, unsigned)\nCTL_RO_NL_GEN(arenas_lextent_i_size, sz_index2size(NBINS+(szind_t)mib[2]),\n    size_t)\nstatic const ctl_named_node_t *\narenas_lextent_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,\n    size_t i) {\n\tif (i > NSIZES - NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_arenas_lextent_i_node;\n}\n\nstatic int\narenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\textent_hooks_t *extent_hooks;\n\tunsigned arena_ind;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\n\textent_hooks = (extent_hooks_t *)&extent_hooks_default;\n\tWRITE(extent_hooks, extent_hooks_t *);\n\tif ((arena_ind = ctl_arena_init(tsd, extent_hooks)) == UINT_MAX) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\tREAD(arena_ind, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narenas_lookup_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tvoid *ptr;\n\textent_t *extent;\n\tarena_t *arena;\n\n\tptr = NULL;\n\tret = EINVAL;\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tWRITE(ptr, void *);\n\textent = iealloc(tsd_tsdn(tsd), ptr);\n\tif (extent == NULL)\n\t\tgoto label_return;\n\n\tarena = extent_arena_get(extent);\n\tif (arena == NULL)\n\t\tgoto label_return;\n\n\tarena_ind = arena_ind_get(arena);\n\tREAD(arena_ind, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\nprof_thread_active_init_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\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\toldval = prof_thread_active_init_set(tsd_tsdn(tsd),\n\t\t    *(bool *)newp);\n\t} else {\n\t\toldval = prof_thread_active_init_get(tsd_tsdn(tsd));\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\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\toldval = prof_active_set(tsd_tsdn(tsd), *(bool *)newp);\n\t} else {\n\t\toldval = prof_active_get(tsd_tsdn(tsd));\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_dump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tconst char *filename = NULL;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\tWRITEONLY();\n\tWRITE(filename, const char *);\n\n\tif (prof_mdump(tsd, filename)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_gdump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\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\toldval = prof_gdump_set(tsd_tsdn(tsd), *(bool *)newp);\n\t} else {\n\t\toldval = prof_gdump_get(tsd_tsdn(tsd));\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tsize_t lg_sample = lg_prof_sample;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\tWRITEONLY();\n\tWRITE(lg_sample, size_t);\n\tif (lg_sample >= (sizeof(uint64_t) << 3)) {\n\t\tlg_sample = (sizeof(uint64_t) << 3) - 1;\n\t}\n\n\tprof_reset(tsd, lg_sample);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nCTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t)\nCTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t)\n\n/******************************************************************************/\n\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_metadata, ctl_stats->metadata, size_t)\nCTL_RO_CGEN(config_stats, stats_metadata_thp, ctl_stats->metadata_thp, size_t)\nCTL_RO_CGEN(config_stats, stats_resident, ctl_stats->resident, size_t)\nCTL_RO_CGEN(config_stats, stats_mapped, ctl_stats->mapped, size_t)\nCTL_RO_CGEN(config_stats, stats_retained, ctl_stats->retained, size_t)\n\nCTL_RO_CGEN(config_stats, stats_background_thread_num_threads,\n    ctl_stats->background_thread.num_threads, size_t)\nCTL_RO_CGEN(config_stats, stats_background_thread_num_runs,\n    ctl_stats->background_thread.num_runs, uint64_t)\nCTL_RO_CGEN(config_stats, stats_background_thread_run_interval,\n    nstime_ns(&ctl_stats->background_thread.run_interval), uint64_t)\n\nCTL_RO_GEN(stats_arenas_i_dss, arenas_i(mib[2])->dss, const char *)\nCTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms,\n    ssize_t)\nCTL_RO_GEN(stats_arenas_i_muzzy_decay_ms, arenas_i(mib[2])->muzzy_decay_ms,\n    ssize_t)\nCTL_RO_GEN(stats_arenas_i_nthreads, arenas_i(mib[2])->nthreads, unsigned)\nCTL_RO_GEN(stats_arenas_i_uptime,\n    nstime_ns(&arenas_i(mib[2])->astats->astats.uptime), uint64_t)\nCTL_RO_GEN(stats_arenas_i_pactive, arenas_i(mib[2])->pactive, size_t)\nCTL_RO_GEN(stats_arenas_i_pdirty, arenas_i(mib[2])->pdirty, size_t)\nCTL_RO_GEN(stats_arenas_i_pmuzzy, arenas_i(mib[2])->pmuzzy, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_mapped,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.mapped, ATOMIC_RELAXED),\n    size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_retained,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.retained, ATOMIC_RELAXED),\n    size_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_dirty_npurge,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_dirty.npurge), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_dirty_nmadvise,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_dirty.nmadvise), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_dirty_purged,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_dirty.purged), uint64_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_npurge,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_muzzy.npurge), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_nmadvise,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_muzzy.nmadvise), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_purged,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_muzzy.purged), uint64_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_base,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.base, ATOMIC_RELAXED),\n    size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_internal,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.internal, ATOMIC_RELAXED),\n    size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_metadata_thp,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.metadata_thp,\n    ATOMIC_RELAXED), size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_tcache_bytes,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.tcache_bytes,\n    ATOMIC_RELAXED), size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_resident,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.resident, ATOMIC_RELAXED),\n    size_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated,\n    arenas_i(mib[2])->astats->allocated_small, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc,\n    arenas_i(mib[2])->astats->nmalloc_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_ndalloc,\n    arenas_i(mib[2])->astats->ndalloc_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_nrequests,\n    arenas_i(mib[2])->astats->nrequests_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_allocated,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.allocated_large,\n    ATOMIC_RELAXED), size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_nmalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.nmalloc_large), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.ndalloc_large), uint64_t)\n/*\n * Note: \"nmalloc\" here instead of \"nrequests\" in the read.  This is intentional.\n */\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.nmalloc_large), uint64_t) /* Intentional. */\n\n/* Lock profiling related APIs below. */\n#define RO_MUTEX_CTL_GEN(n, l)\t\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_ops,\t\t\t\t\\\n    l.n_lock_ops, uint64_t)\t\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_wait,\t\t\t\t\\\n    l.n_wait_times, uint64_t)\t\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_spin_acq,\t\t\t\\\n    l.n_spin_acquired, uint64_t)\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_owner_switch,\t\t\t\\\n    l.n_owner_switches, uint64_t) \t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_total_wait_time,\t\t\t\\\n    nstime_ns(&l.tot_wait_time), uint64_t)\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_max_wait_time,\t\t\t\\\n    nstime_ns(&l.max_wait_time), uint64_t)\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_max_num_thds,\t\t\t\\\n    l.max_n_thds, uint32_t)\n\n/* Global mutexes. */\n#define OP(mtx)\t\t\t\t\t\t\t\t\\\n    RO_MUTEX_CTL_GEN(mutexes_##mtx,\t\t\t\t\t\\\n        ctl_stats->mutex_prof_data[global_prof_mutex_##mtx])\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\n/* Per arena mutexes */\n#define OP(mtx) RO_MUTEX_CTL_GEN(arenas_i_mutexes_##mtx,\t\t\\\n    arenas_i(mib[2])->astats->astats.mutex_prof_data[arena_prof_mutex_##mtx])\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\n/* tcache bin mutex */\nRO_MUTEX_CTL_GEN(arenas_i_bins_j_mutex,\n    arenas_i(mib[2])->astats->bstats[mib[4]].mutex_data)\n#undef RO_MUTEX_CTL_GEN\n\n/* Resets all mutex stats, including global, arena and bin mutexes. */\nstatic int\nstats_mutexes_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tif (!config_stats) {\n\t\treturn ENOENT;\n\t}\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\n#define MUTEX_PROF_RESET(mtx)\t\t\t\t\t\t\\\n    malloc_mutex_lock(tsdn, &mtx);\t\t\t\t\t\\\n    malloc_mutex_prof_data_reset(tsdn, &mtx);\t\t\t\t\\\n    malloc_mutex_unlock(tsdn, &mtx);\n\n\t/* Global mutexes: ctl and prof. */\n\tMUTEX_PROF_RESET(ctl_mtx);\n\tif (have_background_thread) {\n\t\tMUTEX_PROF_RESET(background_thread_lock);\n\t}\n\tif (config_prof && opt_prof) {\n\t\tMUTEX_PROF_RESET(bt2gctx_mtx);\n\t}\n\n\n\t/* Per arena mutexes. */\n\tunsigned n = narenas_total_get();\n\n\tfor (unsigned i = 0; i < n; i++) {\n\t\tarena_t *arena = arena_get(tsdn, i, false);\n\t\tif (!arena) {\n\t\t\tcontinue;\n\t\t}\n\t\tMUTEX_PROF_RESET(arena->large_mtx);\n\t\tMUTEX_PROF_RESET(arena->extent_avail_mtx);\n\t\tMUTEX_PROF_RESET(arena->extents_dirty.mtx);\n\t\tMUTEX_PROF_RESET(arena->extents_muzzy.mtx);\n\t\tMUTEX_PROF_RESET(arena->extents_retained.mtx);\n\t\tMUTEX_PROF_RESET(arena->decay_dirty.mtx);\n\t\tMUTEX_PROF_RESET(arena->decay_muzzy.mtx);\n\t\tMUTEX_PROF_RESET(arena->tcache_ql_mtx);\n\t\tMUTEX_PROF_RESET(arena->base->mtx);\n\n\t\tfor (szind_t i = 0; i < NBINS; i++) {\n\t\t\tbin_t *bin = &arena->bins[i];\n\t\t\tMUTEX_PROF_RESET(bin->lock);\n\t\t}\n\t}\n#undef MUTEX_PROF_RESET\n\treturn 0;\n}\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nmalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc,\n    arenas_i(mib[2])->astats->bstats[mib[4]].ndalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nrequests, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].curregs, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nfills,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nfills, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nflushes,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nflushes, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nslabs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nslabs, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreslabs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].reslabs, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curslabs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].curslabs, size_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_bins_j_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,\n    size_t j) {\n\tif (j > NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_stats_arenas_i_bins_j_node;\n}\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nmalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->lstats[mib[4]].nmalloc), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_ndalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->lstats[mib[4]].ndalloc), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nrequests,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->lstats[mib[4]].nrequests), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_curlextents,\n    arenas_i(mib[2])->astats->lstats[mib[4]].curlextents, size_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_lextents_j_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,\n    size_t j) {\n\tif (j > NSIZES - NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_stats_arenas_i_lextents_j_node;\n}\n\nstatic const ctl_named_node_t *\nstats_arenas_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {\n\tconst ctl_named_node_t *ret;\n\tsize_t a;\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\ta = arenas_i2a_impl(i, true, true);\n\tif (a == UINT_MAX || !ctl_arenas->arenas[a]->initialized) {\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(tsdn, &ctl_mtx);\n\treturn ret;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/div.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n#include \"jemalloc/internal/div.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n/*\n * Suppose we have n = q * d, all integers. We know n and d, and want q = n / d.\n *\n * For any k, we have (here, all division is exact; not C-style rounding):\n * floor(ceil(2^k / d) * n / 2^k) = floor((2^k + r) / d * n / 2^k), where\n * r = (-2^k) mod d.\n *\n * Expanding this out:\n * ... = floor(2^k / d * n / 2^k + r / d * n / 2^k)\n *     = floor(n / d + (r / d) * (n / 2^k)).\n *\n * The fractional part of n / d is 0 (because of the assumption that d divides n\n * exactly), so we have:\n * ... = n / d + floor((r / d) * (n / 2^k))\n *\n * So that our initial expression is equal to the quantity we seek, so long as\n * (r / d) * (n / 2^k) < 1.\n *\n * r is a remainder mod d, so r < d and r / d < 1 always. We can make\n * n / 2 ^ k < 1 by setting k = 32. This gets us a value of magic that works.\n */\n\nvoid\ndiv_init(div_info_t *div_info, size_t d) {\n\t/* Nonsensical. */\n\tassert(d != 0);\n\t/*\n\t * This would make the value of magic too high to fit into a uint32_t\n\t * (we would want magic = 2^32 exactly). This would mess with code gen\n\t * on 32-bit machines.\n\t */\n\tassert(d != 1);\n\n\tuint64_t two_to_k = ((uint64_t)1 << 32);\n\tuint32_t magic = (uint32_t)(two_to_k / d);\n\n\t/*\n\t * We want magic = ceil(2^k / d), but C gives us floor. We have to\n\t * increment it unless the result was exact (i.e. unless d is a power of\n\t * two).\n\t */\n\tif (two_to_k % d != 0) {\n\t\tmagic++;\n\t}\n\tdiv_info->magic = magic;\n#ifdef JEMALLOC_DEBUG\n\tdiv_info->d = d;\n#endif\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/extent.c",
    "content": "#define JEMALLOC_EXTENT_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/ph.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n\n/******************************************************************************/\n/* Data. */\n\nrtree_t\t\textents_rtree;\n/* Keyed by the address of the extent_t being protected. */\nmutex_pool_t\textent_mutex_pool;\n\nsize_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT;\n\nstatic const bitmap_info_t extents_bitmap_info =\n    BITMAP_INFO_INITIALIZER(NPSIZES+1);\n\nstatic void *extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit,\n    unsigned arena_ind);\nstatic bool extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic void extent_destroy_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic bool extent_commit_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool extent_commit_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained);\nstatic bool extent_decommit_default(extent_hooks_t *extent_hooks,\n    void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);\n#ifdef PAGES_CAN_PURGE_LAZY\nstatic bool extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\n#endif\nstatic bool extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained);\n#ifdef PAGES_CAN_PURGE_FORCED\nstatic bool extent_purge_forced_default(extent_hooks_t *extent_hooks,\n    void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);\n#endif\nstatic bool extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained);\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool extent_split_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t size_a, size_t size_b, bool committed,\n    unsigned arena_ind);\n#endif\nstatic extent_t *extent_split_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,\n    bool growing_retained);\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a,\n    size_t size_a, void *addr_b, size_t size_b, bool committed,\n    unsigned arena_ind);\n#endif\nstatic bool extent_merge_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,\n    bool growing_retained);\n\nconst extent_hooks_t\textent_hooks_default = {\n\textent_alloc_default,\n\textent_dalloc_default,\n\textent_destroy_default,\n\textent_commit_default,\n\textent_decommit_default\n#ifdef PAGES_CAN_PURGE_LAZY\n\t,\n\textent_purge_lazy_default\n#else\n\t,\n\tNULL\n#endif\n#ifdef PAGES_CAN_PURGE_FORCED\n\t,\n\textent_purge_forced_default\n#else\n\t,\n\tNULL\n#endif\n#ifdef JEMALLOC_MAPS_COALESCE\n\t,\n\textent_split_default,\n\textent_merge_default\n#endif\n};\n\n/* Used exclusively for gdump triggering. */\nstatic atomic_zu_t curpages;\nstatic atomic_zu_t highpages;\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic void extent_deregister(tsdn_t *tsdn, extent_t *extent);\nstatic extent_t *extent_recycle(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,\n    size_t usize, size_t pad, size_t alignment, bool slab, szind_t szind,\n    bool *zero, bool *commit, bool growing_retained);\nstatic extent_t *extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    extent_t *extent, bool *coalesced, bool growing_retained);\nstatic void extent_record(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent,\n    bool growing_retained);\n\n/******************************************************************************/\n\nph_gen(UNUSED, extent_avail_, extent_tree_t, extent_t, ph_link,\n    extent_esnead_comp)\n\ntypedef enum {\n\tlock_result_success,\n\tlock_result_failure,\n\tlock_result_no_extent\n} lock_result_t;\n\nstatic lock_result_t\nextent_rtree_leaf_elm_try_lock(tsdn_t *tsdn, rtree_leaf_elm_t *elm,\n    extent_t **result) {\n\textent_t *extent1 = rtree_leaf_elm_extent_read(tsdn, &extents_rtree,\n\t    elm, true);\n\n\tif (extent1 == NULL) {\n\t\treturn lock_result_no_extent;\n\t}\n\t/*\n\t * It's possible that the extent changed out from under us, and with it\n\t * the leaf->extent mapping.  We have to recheck while holding the lock.\n\t */\n\textent_lock(tsdn, extent1);\n\textent_t *extent2 = rtree_leaf_elm_extent_read(tsdn,\n\t    &extents_rtree, elm, true);\n\n\tif (extent1 == extent2) {\n\t\t*result = extent1;\n\t\treturn lock_result_success;\n\t} else {\n\t\textent_unlock(tsdn, extent1);\n\t\treturn lock_result_failure;\n\t}\n}\n\n/*\n * Returns a pool-locked extent_t * if there's one associated with the given\n * address, and NULL otherwise.\n */\nstatic extent_t *\nextent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr) {\n\textent_t *ret = NULL;\n\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &extents_rtree,\n\t    rtree_ctx, (uintptr_t)addr, false, false);\n\tif (elm == NULL) {\n\t\treturn NULL;\n\t}\n\tlock_result_t lock_result;\n\tdo {\n\t\tlock_result = extent_rtree_leaf_elm_try_lock(tsdn, elm, &ret);\n\t} while (lock_result == lock_result_failure);\n\treturn ret;\n}\n\nextent_t *\nextent_alloc(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_lock(tsdn, &arena->extent_avail_mtx);\n\textent_t *extent = extent_avail_first(&arena->extent_avail);\n\tif (extent == NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);\n\t\treturn base_alloc_extent(tsdn, arena->base);\n\t}\n\textent_avail_remove(&arena->extent_avail, extent);\n\tmalloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);\n\treturn extent;\n}\n\nvoid\nextent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\tmalloc_mutex_lock(tsdn, &arena->extent_avail_mtx);\n\textent_avail_insert(&arena->extent_avail, extent);\n\tmalloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);\n}\n\nextent_hooks_t *\nextent_hooks_get(arena_t *arena) {\n\treturn base_extent_hooks_get(arena->base);\n}\n\nextent_hooks_t *\nextent_hooks_set(tsd_t *tsd, arena_t *arena, extent_hooks_t *extent_hooks) {\n\tbackground_thread_info_t *info;\n\tif (have_background_thread) {\n\t\tinfo = arena_background_thread_info_get(arena);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t}\n\textent_hooks_t *ret = base_extent_hooks_set(arena->base, extent_hooks);\n\tif (have_background_thread) {\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t}\n\n\treturn ret;\n}\n\nstatic void\nextent_hooks_assure_initialized(arena_t *arena,\n    extent_hooks_t **r_extent_hooks) {\n\tif (*r_extent_hooks == EXTENT_HOOKS_INITIALIZER) {\n\t\t*r_extent_hooks = extent_hooks_get(arena);\n\t}\n}\n\n#ifndef JEMALLOC_JET\nstatic\n#endif\nsize_t\nextent_size_quantize_floor(size_t size) {\n\tsize_t ret;\n\tpszind_t pind;\n\n\tassert(size > 0);\n\tassert((size & PAGE_MASK) == 0);\n\n\tpind = sz_psz2ind(size - sz_large_pad + 1);\n\tif (pind == 0) {\n\t\t/*\n\t\t * Avoid underflow.  This short-circuit would also do the right\n\t\t * thing for all sizes in the range for which there are\n\t\t * PAGE-spaced size classes, but it's simplest to just handle\n\t\t * the one case that would cause erroneous results.\n\t\t */\n\t\treturn size;\n\t}\n\tret = sz_pind2sz(pind - 1) + sz_large_pad;\n\tassert(ret <= size);\n\treturn ret;\n}\n\n#ifndef JEMALLOC_JET\nstatic\n#endif\nsize_t\nextent_size_quantize_ceil(size_t size) {\n\tsize_t ret;\n\n\tassert(size > 0);\n\tassert(size - sz_large_pad <= LARGE_MAXCLASS);\n\tassert((size & PAGE_MASK) == 0);\n\n\tret = extent_size_quantize_floor(size);\n\tif (ret < size) {\n\t\t/*\n\t\t * Skip a quantization that may have an adequately large extent,\n\t\t * because under-sized extents may be mixed in.  This only\n\t\t * happens when an unusual size is requested, i.e. for aligned\n\t\t * allocation, and is just one of several places where linear\n\t\t * search would potentially find sufficiently aligned available\n\t\t * memory somewhere lower.\n\t\t */\n\t\tret = sz_pind2sz(sz_psz2ind(ret - sz_large_pad + 1)) +\n\t\t    sz_large_pad;\n\t}\n\treturn ret;\n}\n\n/* Generate pairing heap functions. */\nph_gen(, extent_heap_, extent_heap_t, extent_t, ph_link, extent_snad_comp)\n\nbool\nextents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,\n    bool delay_coalesce) {\n\tif (malloc_mutex_init(&extents->mtx, \"extents\", WITNESS_RANK_EXTENTS,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tfor (unsigned i = 0; i < NPSIZES+1; i++) {\n\t\textent_heap_new(&extents->heaps[i]);\n\t}\n\tbitmap_init(extents->bitmap, &extents_bitmap_info, true);\n\textent_list_init(&extents->lru);\n\tatomic_store_zu(&extents->npages, 0, ATOMIC_RELAXED);\n\textents->state = state;\n\textents->delay_coalesce = delay_coalesce;\n\treturn false;\n}\n\nextent_state_t\nextents_state_get(const extents_t *extents) {\n\treturn extents->state;\n}\n\nsize_t\nextents_npages_get(extents_t *extents) {\n\treturn atomic_load_zu(&extents->npages, ATOMIC_RELAXED);\n}\n\nstatic void\nextents_insert_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {\n\tmalloc_mutex_assert_owner(tsdn, &extents->mtx);\n\tassert(extent_state_get(extent) == extents->state);\n\n\tsize_t size = extent_size_get(extent);\n\tsize_t psz = extent_size_quantize_floor(size);\n\tpszind_t pind = sz_psz2ind(psz);\n\tif (extent_heap_empty(&extents->heaps[pind])) {\n\t\tbitmap_unset(extents->bitmap, &extents_bitmap_info,\n\t\t    (size_t)pind);\n\t}\n\textent_heap_insert(&extents->heaps[pind], extent);\n\textent_list_append(&extents->lru, extent);\n\tsize_t npages = size >> LG_PAGE;\n\t/*\n\t * All modifications to npages hold the mutex (as asserted above), so we\n\t * don't need an atomic fetch-add; we can get by with a load followed by\n\t * a store.\n\t */\n\tsize_t cur_extents_npages =\n\t    atomic_load_zu(&extents->npages, ATOMIC_RELAXED);\n\tatomic_store_zu(&extents->npages, cur_extents_npages + npages,\n\t    ATOMIC_RELAXED);\n}\n\nstatic void\nextents_remove_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {\n\tmalloc_mutex_assert_owner(tsdn, &extents->mtx);\n\tassert(extent_state_get(extent) == extents->state);\n\n\tsize_t size = extent_size_get(extent);\n\tsize_t psz = extent_size_quantize_floor(size);\n\tpszind_t pind = sz_psz2ind(psz);\n\textent_heap_remove(&extents->heaps[pind], extent);\n\tif (extent_heap_empty(&extents->heaps[pind])) {\n\t\tbitmap_set(extents->bitmap, &extents_bitmap_info,\n\t\t    (size_t)pind);\n\t}\n\textent_list_remove(&extents->lru, extent);\n\tsize_t npages = size >> LG_PAGE;\n\t/*\n\t * As in extents_insert_locked, we hold extents->mtx and so don't need\n\t * atomic operations for updating extents->npages.\n\t */\n\tsize_t cur_extents_npages =\n\t    atomic_load_zu(&extents->npages, ATOMIC_RELAXED);\n\tassert(cur_extents_npages >= npages);\n\tatomic_store_zu(&extents->npages,\n\t    cur_extents_npages - (size >> LG_PAGE), ATOMIC_RELAXED);\n}\n\n/*\n * Find an extent with size [min_size, max_size) to satisfy the alignment\n * requirement.  For each size, try only the first extent in the heap.\n */\nstatic extent_t *\nextents_fit_alignment(extents_t *extents, size_t min_size, size_t max_size,\n    size_t alignment) {\n        pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(min_size));\n        pszind_t pind_max = sz_psz2ind(extent_size_quantize_ceil(max_size));\n\n\tfor (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,\n\t    &extents_bitmap_info, (size_t)pind); i < pind_max; i =\n\t    (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,\n\t    (size_t)i+1)) {\n\t\tassert(i < NPSIZES);\n\t\tassert(!extent_heap_empty(&extents->heaps[i]));\n\t\textent_t *extent = extent_heap_first(&extents->heaps[i]);\n\t\tuintptr_t base = (uintptr_t)extent_base_get(extent);\n\t\tsize_t candidate_size = extent_size_get(extent);\n\t\tassert(candidate_size >= min_size);\n\n\t\tuintptr_t next_align = ALIGNMENT_CEILING((uintptr_t)base,\n\t\t    PAGE_CEILING(alignment));\n\t\tif (base > next_align || base + candidate_size <= next_align) {\n\t\t\t/* Overflow or not crossing the next alignment. */\n\t\t\tcontinue;\n\t\t}\n\n\t\tsize_t leadsize = next_align - base;\n\t\tif (candidate_size - leadsize >= min_size) {\n\t\t\treturn extent;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n/* Do any-best-fit extent selection, i.e. select any extent that best fits. */\nstatic extent_t *\nextents_best_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    size_t size) {\n\tpszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));\n\tpszind_t i = (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,\n\t    (size_t)pind);\n\tif (i < NPSIZES+1) {\n\t\t/*\n\t\t * In order to reduce fragmentation, avoid reusing and splitting\n\t\t * large extents for much smaller sizes.\n\t\t */\n\t\tif ((sz_pind2sz(i) >> opt_lg_extent_max_active_fit) > size) {\n\t\t\treturn NULL;\n\t\t}\n\t\tassert(!extent_heap_empty(&extents->heaps[i]));\n\t\textent_t *extent = extent_heap_first(&extents->heaps[i]);\n\t\tassert(extent_size_get(extent) >= size);\n\t\treturn extent;\n\t}\n\n\treturn NULL;\n}\n\n/*\n * Do first-fit extent selection, i.e. select the oldest/lowest extent that is\n * large enough.\n */\nstatic extent_t *\nextents_first_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    size_t size) {\n\textent_t *ret = NULL;\n\n\tpszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));\n\tfor (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,\n\t    &extents_bitmap_info, (size_t)pind); i < NPSIZES+1; i =\n\t    (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,\n\t    (size_t)i+1)) {\n\t\tassert(!extent_heap_empty(&extents->heaps[i]));\n\t\textent_t *extent = extent_heap_first(&extents->heaps[i]);\n\t\tassert(extent_size_get(extent) >= size);\n\t\tif (ret == NULL || extent_snad_comp(extent, ret) < 0) {\n\t\t\tret = extent;\n\t\t}\n\t\tif (i == NPSIZES) {\n\t\t\tbreak;\n\t\t}\n\t\tassert(i < NPSIZES);\n\t}\n\n\treturn ret;\n}\n\n/*\n * Do {best,first}-fit extent selection, where the selection policy choice is\n * based on extents->delay_coalesce.  Best-fit selection requires less\n * searching, but its layout policy is less stable and may cause higher virtual\n * memory fragmentation as a side effect.\n */\nstatic extent_t *\nextents_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    size_t esize, size_t alignment) {\n\tmalloc_mutex_assert_owner(tsdn, &extents->mtx);\n\n\tsize_t max_size = esize + PAGE_CEILING(alignment) - PAGE;\n\t/* Beware size_t wrap-around. */\n\tif (max_size < esize) {\n\t\treturn NULL;\n\t}\n\n\textent_t *extent = extents->delay_coalesce ?\n\t    extents_best_fit_locked(tsdn, arena, extents, max_size) :\n\t    extents_first_fit_locked(tsdn, arena, extents, max_size);\n\n\tif (alignment > PAGE && extent == NULL) {\n\t\t/*\n\t\t * max_size guarantees the alignment requirement but is rather\n\t\t * pessimistic.  Next we try to satisfy the aligned allocation\n\t\t * with sizes in [esize, max_size).\n\t\t */\n\t\textent = extents_fit_alignment(extents, esize, max_size,\n\t\t    alignment);\n\t}\n\n\treturn extent;\n}\n\nstatic bool\nextent_try_delayed_coalesce(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    extent_t *extent) {\n\textent_state_set(extent, extent_state_active);\n\tbool coalesced;\n\textent = extent_try_coalesce(tsdn, arena, r_extent_hooks, rtree_ctx,\n\t    extents, extent, &coalesced, false);\n\textent_state_set(extent, extents_state_get(extents));\n\n\tif (!coalesced) {\n\t\treturn true;\n\t}\n\textents_insert_locked(tsdn, extents, extent);\n\treturn false;\n}\n\nextent_t *\nextents_alloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\tassert(size + pad != 0);\n\tassert(alignment != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks, extents,\n\t    new_addr, size, pad, alignment, slab, szind, zero, commit, false);\n\tassert(extent == NULL || extent_dumpable_get(extent));\n\treturn extent;\n}\n\nvoid\nextents_dalloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *extent) {\n\tassert(extent_base_get(extent) != NULL);\n\tassert(extent_size_get(extent) != 0);\n\tassert(extent_dumpable_get(extent));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_addr_set(extent, extent_base_get(extent));\n\textent_zeroed_set(extent, false);\n\n\textent_record(tsdn, arena, r_extent_hooks, extents, extent, false);\n}\n\nextent_t *\nextents_evict(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, size_t npages_min) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\n\t/*\n\t * Get the LRU coalesced extent, if any.  If coalescing was delayed,\n\t * the loop will iterate until the LRU extent is fully coalesced.\n\t */\n\textent_t *extent;\n\twhile (true) {\n\t\t/* Get the LRU extent, if any. */\n\t\textent = extent_list_first(&extents->lru);\n\t\tif (extent == NULL) {\n\t\t\tgoto label_return;\n\t\t}\n\t\t/* Check the eviction limit. */\n\t\tsize_t extents_npages = atomic_load_zu(&extents->npages,\n\t\t    ATOMIC_RELAXED);\n\t\tif (extents_npages <= npages_min) {\n\t\t\textent = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\textents_remove_locked(tsdn, extents, extent);\n\t\tif (!extents->delay_coalesce) {\n\t\t\tbreak;\n\t\t}\n\t\t/* Try to coalesce. */\n\t\tif (extent_try_delayed_coalesce(tsdn, arena, r_extent_hooks,\n\t\t    rtree_ctx, extents, extent)) {\n\t\t\tbreak;\n\t\t}\n\t\t/*\n\t\t * The LRU extent was just coalesced and the result placed in\n\t\t * the LRU at its neighbor's position.  Start over.\n\t\t */\n\t}\n\n\t/*\n\t * Either mark the extent active or deregister it to protect against\n\t * concurrent operations.\n\t */\n\tswitch (extents_state_get(extents)) {\n\tcase extent_state_active:\n\t\tnot_reached();\n\tcase extent_state_dirty:\n\tcase extent_state_muzzy:\n\t\textent_state_set(extent, extent_state_active);\n\t\tbreak;\n\tcase extent_state_retained:\n\t\textent_deregister(tsdn, extent);\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\treturn extent;\n}\n\nstatic void\nextents_leak(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *extent, bool growing_retained) {\n\t/*\n\t * Leak extent after making sure its pages have already been purged, so\n\t * that this is only a virtual memory leak.\n\t */\n\tif (extents_state_get(extents) == extent_state_dirty) {\n\t\tif (extent_purge_lazy_impl(tsdn, arena, r_extent_hooks,\n\t\t    extent, 0, extent_size_get(extent), growing_retained)) {\n\t\t\textent_purge_forced_impl(tsdn, arena, r_extent_hooks,\n\t\t\t    extent, 0, extent_size_get(extent),\n\t\t\t    growing_retained);\n\t\t}\n\t}\n\textent_dalloc(tsdn, arena, extent);\n}\n\nvoid\nextents_prefork(tsdn_t *tsdn, extents_t *extents) {\n\tmalloc_mutex_prefork(tsdn, &extents->mtx);\n}\n\nvoid\nextents_postfork_parent(tsdn_t *tsdn, extents_t *extents) {\n\tmalloc_mutex_postfork_parent(tsdn, &extents->mtx);\n}\n\nvoid\nextents_postfork_child(tsdn_t *tsdn, extents_t *extents) {\n\tmalloc_mutex_postfork_child(tsdn, &extents->mtx);\n}\n\nstatic void\nextent_deactivate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    extent_t *extent) {\n\tassert(extent_arena_get(extent) == arena);\n\tassert(extent_state_get(extent) == extent_state_active);\n\n\textent_state_set(extent, extents_state_get(extents));\n\textents_insert_locked(tsdn, extents, extent);\n}\n\nstatic void\nextent_deactivate(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    extent_t *extent) {\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\textent_deactivate_locked(tsdn, arena, extents, extent);\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n}\n\nstatic void\nextent_activate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    extent_t *extent) {\n\tassert(extent_arena_get(extent) == arena);\n\tassert(extent_state_get(extent) == extents_state_get(extents));\n\n\textents_remove_locked(tsdn, extents, extent);\n\textent_state_set(extent, extent_state_active);\n}\n\nstatic bool\nextent_rtree_leaf_elms_lookup(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx,\n    const extent_t *extent, bool dependent, bool init_missing,\n    rtree_leaf_elm_t **r_elm_a, rtree_leaf_elm_t **r_elm_b) {\n\t*r_elm_a = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_base_get(extent), dependent, init_missing);\n\tif (!dependent && *r_elm_a == NULL) {\n\t\treturn true;\n\t}\n\tassert(*r_elm_a != NULL);\n\n\t*r_elm_b = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_last_get(extent), dependent, init_missing);\n\tif (!dependent && *r_elm_b == NULL) {\n\t\treturn true;\n\t}\n\tassert(*r_elm_b != NULL);\n\n\treturn false;\n}\n\nstatic void\nextent_rtree_write_acquired(tsdn_t *tsdn, rtree_leaf_elm_t *elm_a,\n    rtree_leaf_elm_t *elm_b, extent_t *extent, szind_t szind, bool slab) {\n\trtree_leaf_elm_write(tsdn, &extents_rtree, elm_a, extent, szind, slab);\n\tif (elm_b != NULL) {\n\t\trtree_leaf_elm_write(tsdn, &extents_rtree, elm_b, extent, szind,\n\t\t    slab);\n\t}\n}\n\nstatic void\nextent_interior_register(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, extent_t *extent,\n    szind_t szind) {\n\tassert(extent_slab_get(extent));\n\n\t/* Register interior. */\n\tfor (size_t i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) {\n\t\trtree_write(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)extent_base_get(extent) + (uintptr_t)(i <<\n\t\t    LG_PAGE), extent, szind, true);\n\t}\n}\n\nstatic void\nextent_gdump_add(tsdn_t *tsdn, const extent_t *extent) {\n\tcassert(config_prof);\n\t/* prof_gdump() requirement. */\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (opt_prof && extent_state_get(extent) == extent_state_active) {\n\t\tsize_t nadd = extent_size_get(extent) >> LG_PAGE;\n\t\tsize_t cur = atomic_fetch_add_zu(&curpages, nadd,\n\t\t    ATOMIC_RELAXED) + nadd;\n\t\tsize_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED);\n\t\twhile (cur > high && !atomic_compare_exchange_weak_zu(\n\t\t    &highpages, &high, cur, ATOMIC_RELAXED, ATOMIC_RELAXED)) {\n\t\t\t/*\n\t\t\t * Don't refresh cur, because it may have decreased\n\t\t\t * since this thread lost the highpages update race.\n\t\t\t * Note that high is updated in case of CAS failure.\n\t\t\t */\n\t\t}\n\t\tif (cur > high && prof_gdump_get_unlocked()) {\n\t\t\tprof_gdump(tsdn);\n\t\t}\n\t}\n}\n\nstatic void\nextent_gdump_sub(tsdn_t *tsdn, const extent_t *extent) {\n\tcassert(config_prof);\n\n\tif (opt_prof && extent_state_get(extent) == extent_state_active) {\n\t\tsize_t nsub = extent_size_get(extent) >> LG_PAGE;\n\t\tassert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub);\n\t\tatomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED);\n\t}\n}\n\nstatic bool\nextent_register_impl(tsdn_t *tsdn, extent_t *extent, bool gdump_add) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *elm_a, *elm_b;\n\n\t/*\n\t * We need to hold the lock to protect against a concurrent coalesce\n\t * operation that sees us in a partial state.\n\t */\n\textent_lock(tsdn, extent);\n\n\tif (extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, false, true,\n\t    &elm_a, &elm_b)) {\n\t\treturn true;\n\t}\n\n\tszind_t szind = extent_szind_get_maybe_invalid(extent);\n\tbool slab = extent_slab_get(extent);\n\textent_rtree_write_acquired(tsdn, elm_a, elm_b, extent, szind, slab);\n\tif (slab) {\n\t\textent_interior_register(tsdn, rtree_ctx, extent, szind);\n\t}\n\n\textent_unlock(tsdn, extent);\n\n\tif (config_prof && gdump_add) {\n\t\textent_gdump_add(tsdn, extent);\n\t}\n\n\treturn false;\n}\n\nstatic bool\nextent_register(tsdn_t *tsdn, extent_t *extent) {\n\treturn extent_register_impl(tsdn, extent, true);\n}\n\nstatic bool\nextent_register_no_gdump_add(tsdn_t *tsdn, extent_t *extent) {\n\treturn extent_register_impl(tsdn, extent, false);\n}\n\nstatic void\nextent_reregister(tsdn_t *tsdn, extent_t *extent) {\n\tbool err = extent_register(tsdn, extent);\n\tassert(!err);\n}\n\n/*\n * Removes all pointers to the given extent from the global rtree indices for\n * its interior.  This is relevant for slab extents, for which we need to do\n * metadata lookups at places other than the head of the extent.  We deregister\n * on the interior, then, when an extent moves from being an active slab to an\n * inactive state.\n */\nstatic void\nextent_interior_deregister(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx,\n    extent_t *extent) {\n\tsize_t i;\n\n\tassert(extent_slab_get(extent));\n\n\tfor (i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) {\n\t\trtree_clear(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)extent_base_get(extent) + (uintptr_t)(i <<\n\t\t    LG_PAGE));\n\t}\n}\n\n/*\n * Removes all pointers to the given extent from the global rtree.\n */\nstatic void\nextent_deregister_impl(tsdn_t *tsdn, extent_t *extent, bool gdump) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *elm_a, *elm_b;\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, true, false,\n\t    &elm_a, &elm_b);\n\n\textent_lock(tsdn, extent);\n\n\textent_rtree_write_acquired(tsdn, elm_a, elm_b, NULL, NSIZES, false);\n\tif (extent_slab_get(extent)) {\n\t\textent_interior_deregister(tsdn, rtree_ctx, extent);\n\t\textent_slab_set(extent, false);\n\t}\n\n\textent_unlock(tsdn, extent);\n\n\tif (config_prof && gdump) {\n\t\textent_gdump_sub(tsdn, extent);\n\t}\n}\n\nstatic void\nextent_deregister(tsdn_t *tsdn, extent_t *extent) {\n\textent_deregister_impl(tsdn, extent, true);\n}\n\nstatic void\nextent_deregister_no_gdump_sub(tsdn_t *tsdn, extent_t *extent) {\n\textent_deregister_impl(tsdn, extent, false);\n}\n\n/*\n * Tries to find and remove an extent from extents that can be used for the\n * given allocation request.\n */\nstatic extent_t *\nextent_recycle_extract(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,\n    bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\tassert(alignment > 0);\n\tif (config_debug && new_addr != NULL) {\n\t\t/*\n\t\t * Non-NULL new_addr has two use cases:\n\t\t *\n\t\t *   1) Recycle a known-extant extent, e.g. during purging.\n\t\t *   2) Perform in-place expanding reallocation.\n\t\t *\n\t\t * Regardless of use case, new_addr must either refer to a\n\t\t * non-existing extent, or to the base of an extant extent,\n\t\t * since only active slabs support interior lookups (which of\n\t\t * course cannot be recycled).\n\t\t */\n\t\tassert(PAGE_ADDR2BASE(new_addr) == new_addr);\n\t\tassert(pad == 0);\n\t\tassert(alignment <= PAGE);\n\t}\n\n\tsize_t esize = size + pad;\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\textent_t *extent;\n\tif (new_addr != NULL) {\n\t\textent = extent_lock_from_addr(tsdn, rtree_ctx, new_addr);\n\t\tif (extent != NULL) {\n\t\t\t/*\n\t\t\t * We might null-out extent to report an error, but we\n\t\t\t * still need to unlock the associated mutex after.\n\t\t\t */\n\t\t\textent_t *unlock_extent = extent;\n\t\t\tassert(extent_base_get(extent) == new_addr);\n\t\t\tif (extent_arena_get(extent) != arena ||\n\t\t\t    extent_size_get(extent) < esize ||\n\t\t\t    extent_state_get(extent) !=\n\t\t\t    extents_state_get(extents)) {\n\t\t\t\textent = NULL;\n\t\t\t}\n\t\t\textent_unlock(tsdn, unlock_extent);\n\t\t}\n\t} else {\n\t\textent = extents_fit_locked(tsdn, arena, extents, esize,\n\t\t    alignment);\n\t}\n\tif (extent == NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\t\treturn NULL;\n\t}\n\n\textent_activate_locked(tsdn, arena, extents, extent);\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\n\treturn extent;\n}\n\n/*\n * Given an allocation request and an extent guaranteed to be able to satisfy\n * it, this splits off lead and trail extents, leaving extent pointing to an\n * extent satisfying the allocation.\n * This function doesn't put lead or trail into any extents_t; it's the caller's\n * job to ensure that they can be reused.\n */\ntypedef enum {\n\t/*\n\t * Split successfully.  lead, extent, and trail, are modified to extents\n\t * describing the ranges before, in, and after the given allocation.\n\t */\n\textent_split_interior_ok,\n\t/*\n\t * The extent can't satisfy the given allocation request.  None of the\n\t * input extent_t *s are touched.\n\t */\n\textent_split_interior_cant_alloc,\n\t/*\n\t * In a potentially invalid state.  Must leak (if *to_leak is non-NULL),\n\t * and salvage what's still salvageable (if *to_salvage is non-NULL).\n\t * None of lead, extent, or trail are valid.\n\t */\n\textent_split_interior_error\n} extent_split_interior_result_t;\n\nstatic extent_split_interior_result_t\nextent_split_interior(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx,\n    /* The result of splitting, in case of success. */\n    extent_t **extent, extent_t **lead, extent_t **trail,\n    /* The mess to clean up, in case of error. */\n    extent_t **to_leak, extent_t **to_salvage,\n    void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,\n    szind_t szind, bool growing_retained) {\n\tsize_t esize = size + pad;\n\tsize_t leadsize = ALIGNMENT_CEILING((uintptr_t)extent_base_get(*extent),\n\t    PAGE_CEILING(alignment)) - (uintptr_t)extent_base_get(*extent);\n\tassert(new_addr == NULL || leadsize == 0);\n\tif (extent_size_get(*extent) < leadsize + esize) {\n\t\treturn extent_split_interior_cant_alloc;\n\t}\n\tsize_t trailsize = extent_size_get(*extent) - leadsize - esize;\n\n\t*lead = NULL;\n\t*trail = NULL;\n\t*to_leak = NULL;\n\t*to_salvage = NULL;\n\n\t/* Split the lead. */\n\tif (leadsize != 0) {\n\t\t*lead = *extent;\n\t\t*extent = extent_split_impl(tsdn, arena, r_extent_hooks,\n\t\t    *lead, leadsize, NSIZES, false, esize + trailsize, szind,\n\t\t    slab, growing_retained);\n\t\tif (*extent == NULL) {\n\t\t\t*to_leak = *lead;\n\t\t\t*lead = NULL;\n\t\t\treturn extent_split_interior_error;\n\t\t}\n\t}\n\n\t/* Split the trail. */\n\tif (trailsize != 0) {\n\t\t*trail = extent_split_impl(tsdn, arena, r_extent_hooks, *extent,\n\t\t    esize, szind, slab, trailsize, NSIZES, false,\n\t\t    growing_retained);\n\t\tif (*trail == NULL) {\n\t\t\t*to_leak = *extent;\n\t\t\t*to_salvage = *lead;\n\t\t\t*lead = NULL;\n\t\t\t*extent = NULL;\n\t\t\treturn extent_split_interior_error;\n\t\t}\n\t}\n\n\tif (leadsize == 0 && trailsize == 0) {\n\t\t/*\n\t\t * Splitting causes szind to be set as a side effect, but no\n\t\t * splitting occurred.\n\t\t */\n\t\textent_szind_set(*extent, szind);\n\t\tif (szind != NSIZES) {\n\t\t\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,\n\t\t\t    (uintptr_t)extent_addr_get(*extent), szind, slab);\n\t\t\tif (slab && extent_size_get(*extent) > PAGE) {\n\t\t\t\trtree_szind_slab_update(tsdn, &extents_rtree,\n\t\t\t\t    rtree_ctx,\n\t\t\t\t    (uintptr_t)extent_past_get(*extent) -\n\t\t\t\t    (uintptr_t)PAGE, szind, slab);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn extent_split_interior_ok;\n}\n\n/*\n * This fulfills the indicated allocation request out of the given extent (which\n * the caller should have ensured was big enough).  If there's any unused space\n * before or after the resulting allocation, that space is given its own extent\n * and put back into extents.\n */\nstatic extent_t *\nextent_recycle_split(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,\n    szind_t szind, extent_t *extent, bool growing_retained) {\n\textent_t *lead;\n\textent_t *trail;\n\textent_t *to_leak;\n\textent_t *to_salvage;\n\n\textent_split_interior_result_t result = extent_split_interior(\n\t    tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail,\n\t    &to_leak, &to_salvage, new_addr, size, pad, alignment, slab, szind,\n\t    growing_retained);\n\n\tif (result == extent_split_interior_ok) {\n\t\tif (lead != NULL) {\n\t\t\textent_deactivate(tsdn, arena, extents, lead);\n\t\t}\n\t\tif (trail != NULL) {\n\t\t\textent_deactivate(tsdn, arena, extents, trail);\n\t\t}\n\t\treturn extent;\n\t} else {\n\t\t/*\n\t\t * We should have picked an extent that was large enough to\n\t\t * fulfill our allocation request.\n\t\t */\n\t\tassert(result == extent_split_interior_error);\n\t\tif (to_salvage != NULL) {\n\t\t\textent_deregister(tsdn, to_salvage);\n\t\t}\n\t\tif (to_leak != NULL) {\n\t\t\tvoid *leak = extent_base_get(to_leak);\n\t\t\textent_deregister_no_gdump_sub(tsdn, to_leak);\n\t\t\textents_leak(tsdn, arena, r_extent_hooks, extents,\n\t\t\t    to_leak, growing_retained);\n\t\t\tassert(extent_lock_from_addr(tsdn, rtree_ctx, leak)\n\t\t\t    == NULL);\n\t\t}\n\t\treturn NULL;\n\t}\n\tunreachable();\n}\n\n/*\n * Tries to satisfy the given allocation request by reusing one of the extents\n * in the given extents_t.\n */\nstatic extent_t *\nextent_recycle(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit,\n    bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\tassert(new_addr == NULL || !slab);\n\tassert(pad == 0 || !slab);\n\tassert(!*zero || !slab);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent = extent_recycle_extract(tsdn, arena, r_extent_hooks,\n\t    rtree_ctx, extents, new_addr, size, pad, alignment, slab,\n\t    growing_retained);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\n\textent = extent_recycle_split(tsdn, arena, r_extent_hooks, rtree_ctx,\n\t    extents, new_addr, size, pad, alignment, slab, szind, extent,\n\t    growing_retained);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\n\tif (*commit && !extent_committed_get(extent)) {\n\t\tif (extent_commit_impl(tsdn, arena, r_extent_hooks, extent,\n\t\t    0, extent_size_get(extent), growing_retained)) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks, extents,\n\t\t\t    extent, growing_retained);\n\t\t\treturn NULL;\n\t\t}\n\t\textent_zeroed_set(extent, true);\n\t}\n\n\tif (extent_committed_get(extent)) {\n\t\t*commit = true;\n\t}\n\tif (extent_zeroed_get(extent)) {\n\t\t*zero = true;\n\t}\n\n\tif (pad != 0) {\n\t\textent_addr_randomize(tsdn, extent, alignment);\n\t}\n\tassert(extent_state_get(extent) == extent_state_active);\n\tif (slab) {\n\t\textent_slab_set(extent, slab);\n\t\textent_interior_register(tsdn, rtree_ctx, extent, szind);\n\t}\n\n\tif (*zero) {\n\t\tvoid *addr = extent_base_get(extent);\n\t\tsize_t size = extent_size_get(extent);\n\t\tif (!extent_zeroed_get(extent)) {\n\t\t\tif (pages_purge_forced(addr, size)) {\n\t\t\t\tmemset(addr, 0, size);\n\t\t\t}\n\t\t} else if (config_debug) {\n\t\t\tsize_t *p = (size_t *)(uintptr_t)addr;\n\t\t\tfor (size_t i = 0; i < size / sizeof(size_t); i++) {\n\t\t\t\tassert(p[i] == 0);\n\t\t\t}\n\t\t}\n\t}\n\treturn extent;\n}\n\n/*\n * If the caller specifies (!*zero), it is still possible to receive zeroed\n * memory, in which case *zero is toggled to true.  arena_extent_alloc() takes\n * advantage of this to avoid demanding zeroed extents, but taking advantage of\n * them if they are returned.\n */\nstatic void *\nextent_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec) {\n\tvoid *ret;\n\n\tassert(size != 0);\n\tassert(alignment != 0);\n\n\t/* \"primary\" dss. */\n\tif (have_dss && dss_prec == dss_prec_primary && (ret =\n\t    extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,\n\t    commit)) != NULL) {\n\t\treturn ret;\n\t}\n\t/* mmap. */\n\tif ((ret = extent_alloc_mmap(new_addr, size, alignment, zero, commit))\n\t    != NULL) {\n\t\treturn ret;\n\t}\n\t/* \"secondary\" dss. */\n\tif (have_dss && dss_prec == dss_prec_secondary && (ret =\n\t    extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,\n\t    commit)) != NULL) {\n\t\treturn ret;\n\t}\n\n\t/* All strategies for allocation failed. */\n\treturn NULL;\n}\n\nstatic void *\nextent_alloc_default_impl(tsdn_t *tsdn, arena_t *arena, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit) {\n\tvoid *ret = extent_alloc_core(tsdn, arena, new_addr, size, alignment, zero,\n\t    commit, (dss_prec_t)atomic_load_u(&arena->dss_prec,\n\t    ATOMIC_RELAXED));\n\tif (have_madvise_huge && ret) {\n\t\tpages_set_thp_state(ret, size);\n\t}\n\treturn ret;\n}\n\nstatic void *\nextent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {\n\ttsdn_t *tsdn;\n\tarena_t *arena;\n\n\ttsdn = tsdn_fetch();\n\tarena = arena_get(tsdn, arena_ind, false);\n\t/*\n\t * The arena we're allocating on behalf of must have been initialized\n\t * already.\n\t */\n\tassert(arena != NULL);\n\n\treturn extent_alloc_default_impl(tsdn, arena, new_addr, size,\n\t    alignment, zero, commit);\n}\n\nstatic void\nextent_hook_pre_reentrancy(tsdn_t *tsdn, arena_t *arena) {\n\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\tif (arena == arena_get(tsd_tsdn(tsd), 0, false)) {\n\t\t/*\n\t\t * The only legitimate case of customized extent hooks for a0 is\n\t\t * hooks with no allocation activities.  One such example is to\n\t\t * place metadata on pre-allocated resources such as huge pages.\n\t\t * In that case, rely on reentrancy_level checks to catch\n\t\t * infinite recursions.\n\t\t */\n\t\tpre_reentrancy(tsd, NULL);\n\t} else {\n\t\tpre_reentrancy(tsd, arena);\n\t}\n}\n\nstatic void\nextent_hook_post_reentrancy(tsdn_t *tsdn) {\n\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\tpost_reentrancy(tsd);\n}\n\n/*\n * If virtual memory is retained, create increasingly larger extents from which\n * to split requested extents in order to limit the total number of disjoint\n * virtual memory ranges retained by each arena.\n */\nstatic extent_t *\nextent_grow_retained(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, size_t size, size_t pad, size_t alignment,\n    bool slab, szind_t szind, bool *zero, bool *commit) {\n\tmalloc_mutex_assert_owner(tsdn, &arena->extent_grow_mtx);\n\tassert(pad == 0 || !slab);\n\tassert(!*zero || !slab);\n\n\tsize_t esize = size + pad;\n\tsize_t alloc_size_min = esize + PAGE_CEILING(alignment) - PAGE;\n\t/* Beware size_t wrap-around. */\n\tif (alloc_size_min < esize) {\n\t\tgoto label_err;\n\t}\n\t/*\n\t * Find the next extent size in the series that would be large enough to\n\t * satisfy this request.\n\t */\n\tpszind_t egn_skip = 0;\n\tsize_t alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);\n\twhile (alloc_size < alloc_size_min) {\n\t\tegn_skip++;\n\t\tif (arena->extent_grow_next + egn_skip == NPSIZES) {\n\t\t\t/* Outside legal range. */\n\t\t\tgoto label_err;\n\t\t}\n\t\tassert(arena->extent_grow_next + egn_skip < NPSIZES);\n\t\talloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);\n\t}\n\n\textent_t *extent = extent_alloc(tsdn, arena);\n\tif (extent == NULL) {\n\t\tgoto label_err;\n\t}\n\tbool zeroed = false;\n\tbool committed = false;\n\n\tvoid *ptr;\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\tptr = extent_alloc_default_impl(tsdn, arena, NULL,\n\t\t    alloc_size, PAGE, &zeroed, &committed);\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\tptr = (*r_extent_hooks)->alloc(*r_extent_hooks, NULL,\n\t\t    alloc_size, PAGE, &zeroed, &committed,\n\t\t    arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\textent_init(extent, arena, ptr, alloc_size, false, NSIZES,\n\t    arena_extent_sn_next(arena), extent_state_active, zeroed,\n\t    committed, true);\n\tif (ptr == NULL) {\n\t\textent_dalloc(tsdn, arena, extent);\n\t\tgoto label_err;\n\t}\n\n\tif (extent_register_no_gdump_add(tsdn, extent)) {\n\t\textents_leak(tsdn, arena, r_extent_hooks,\n\t\t    &arena->extents_retained, extent, true);\n\t\tgoto label_err;\n\t}\n\n\tif (extent_zeroed_get(extent) && extent_committed_get(extent)) {\n\t\t*zero = true;\n\t}\n\tif (extent_committed_get(extent)) {\n\t\t*commit = true;\n\t}\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *lead;\n\textent_t *trail;\n\textent_t *to_leak;\n\textent_t *to_salvage;\n\textent_split_interior_result_t result = extent_split_interior(\n\t    tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail,\n\t    &to_leak, &to_salvage, NULL, size, pad, alignment, slab, szind,\n\t    true);\n\n\tif (result == extent_split_interior_ok) {\n\t\tif (lead != NULL) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, lead, true);\n\t\t}\n\t\tif (trail != NULL) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, trail, true);\n\t\t}\n\t} else {\n\t\t/*\n\t\t * We should have allocated a sufficiently large extent; the\n\t\t * cant_alloc case should not occur.\n\t\t */\n\t\tassert(result == extent_split_interior_error);\n\t\tif (to_salvage != NULL) {\n\t\t\tif (config_prof) {\n\t\t\t\textent_gdump_add(tsdn, to_salvage);\n\t\t\t}\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, to_salvage, true);\n\t\t}\n\t\tif (to_leak != NULL) {\n\t\t\textent_deregister_no_gdump_sub(tsdn, to_leak);\n\t\t\textents_leak(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, to_leak, true);\n\t\t}\n\t\tgoto label_err;\n\t}\n\n\tif (*commit && !extent_committed_get(extent)) {\n\t\tif (extent_commit_impl(tsdn, arena, r_extent_hooks, extent, 0,\n\t\t    extent_size_get(extent), true)) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, extent, true);\n\t\t\tgoto label_err;\n\t\t}\n\t\textent_zeroed_set(extent, true);\n\t}\n\n\t/*\n\t * Increment extent_grow_next if doing so wouldn't exceed the allowed\n\t * range.\n\t */\n\tif (arena->extent_grow_next + egn_skip + 1 <=\n\t    arena->retain_grow_limit) {\n\t\tarena->extent_grow_next += egn_skip + 1;\n\t} else {\n\t\tarena->extent_grow_next = arena->retain_grow_limit;\n\t}\n\t/* All opportunities for failure are past. */\n\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\n\tif (config_prof) {\n\t\t/* Adjust gdump stats now that extent is final size. */\n\t\textent_gdump_add(tsdn, extent);\n\t}\n\tif (pad != 0) {\n\t\textent_addr_randomize(tsdn, extent, alignment);\n\t}\n\tif (slab) {\n\t\trtree_ctx_t rtree_ctx_fallback;\n\t\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,\n\t\t    &rtree_ctx_fallback);\n\n\t\textent_slab_set(extent, true);\n\t\textent_interior_register(tsdn, rtree_ctx, extent, szind);\n\t}\n\tif (*zero && !extent_zeroed_get(extent)) {\n\t\tvoid *addr = extent_base_get(extent);\n\t\tsize_t size = extent_size_get(extent);\n\t\tif (pages_purge_forced(addr, size)) {\n\t\t\tmemset(addr, 0, size);\n\t\t}\n\t}\n\n\treturn extent;\nlabel_err:\n\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\treturn NULL;\n}\n\nstatic extent_t *\nextent_alloc_retained(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\tassert(size != 0);\n\tassert(alignment != 0);\n\n\tmalloc_mutex_lock(tsdn, &arena->extent_grow_mtx);\n\n\textent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks,\n\t    &arena->extents_retained, new_addr, size, pad, alignment, slab,\n\t    szind, zero, commit, true);\n\tif (extent != NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\t\tif (config_prof) {\n\t\t\textent_gdump_add(tsdn, extent);\n\t\t}\n\t} else if (opt_retain && new_addr == NULL) {\n\t\textent = extent_grow_retained(tsdn, arena, r_extent_hooks, size,\n\t\t    pad, alignment, slab, szind, zero, commit);\n\t\t/* extent_grow_retained() always releases extent_grow_mtx. */\n\t} else {\n\t\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\t}\n\tmalloc_mutex_assert_not_owner(tsdn, &arena->extent_grow_mtx);\n\n\treturn extent;\n}\n\nstatic extent_t *\nextent_alloc_wrapper_hard(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\tsize_t esize = size + pad;\n\textent_t *extent = extent_alloc(tsdn, arena);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\tvoid *addr;\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\taddr = extent_alloc_default_impl(tsdn, arena, new_addr, esize,\n\t\t    alignment, zero, commit);\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\taddr = (*r_extent_hooks)->alloc(*r_extent_hooks, new_addr,\n\t\t    esize, alignment, zero, commit, arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\tif (addr == NULL) {\n\t\textent_dalloc(tsdn, arena, extent);\n\t\treturn NULL;\n\t}\n\textent_init(extent, arena, addr, esize, slab, szind,\n\t    arena_extent_sn_next(arena), extent_state_active, *zero, *commit,\n\t    true);\n\tif (pad != 0) {\n\t\textent_addr_randomize(tsdn, extent, alignment);\n\t}\n\tif (extent_register(tsdn, extent)) {\n\t\textents_leak(tsdn, arena, r_extent_hooks,\n\t\t    &arena->extents_retained, extent, false);\n\t\treturn NULL;\n\t}\n\n\treturn extent;\n}\n\nextent_t *\nextent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\textent_t *extent = extent_alloc_retained(tsdn, arena, r_extent_hooks,\n\t    new_addr, size, pad, alignment, slab, szind, zero, commit);\n\tif (extent == NULL) {\n\t\tif (opt_retain && new_addr != NULL) {\n\t\t\t/*\n\t\t\t * When retain is enabled and new_addr is set, we do not\n\t\t\t * attempt extent_alloc_wrapper_hard which does mmap\n\t\t\t * that is very unlikely to succeed (unless it happens\n\t\t\t * to be at the end).\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\t\textent = extent_alloc_wrapper_hard(tsdn, arena, r_extent_hooks,\n\t\t    new_addr, size, pad, alignment, slab, szind, zero, commit);\n\t}\n\n\tassert(extent == NULL || extent_dumpable_get(extent));\n\treturn extent;\n}\n\nstatic bool\nextent_can_coalesce(arena_t *arena, extents_t *extents, const extent_t *inner,\n    const extent_t *outer) {\n\tassert(extent_arena_get(inner) == arena);\n\tif (extent_arena_get(outer) != arena) {\n\t\treturn false;\n\t}\n\n\tassert(extent_state_get(inner) == extent_state_active);\n\tif (extent_state_get(outer) != extents->state) {\n\t\treturn false;\n\t}\n\n\tif (extent_committed_get(inner) != extent_committed_get(outer)) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic bool\nextent_coalesce(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *inner, extent_t *outer, bool forward,\n    bool growing_retained) {\n\tassert(extent_can_coalesce(arena, extents, inner, outer));\n\n\textent_activate_locked(tsdn, arena, extents, outer);\n\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\tbool err = extent_merge_impl(tsdn, arena, r_extent_hooks,\n\t    forward ? inner : outer, forward ? outer : inner, growing_retained);\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\n\tif (err) {\n\t\textent_deactivate_locked(tsdn, arena, extents, outer);\n\t}\n\n\treturn err;\n}\n\nstatic extent_t *\nextent_try_coalesce(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    extent_t *extent, bool *coalesced, bool growing_retained) {\n\t/*\n\t * Continue attempting to coalesce until failure, to protect against\n\t * races with other threads that are thwarted by this one.\n\t */\n\tbool again;\n\tdo {\n\t\tagain = false;\n\n\t\t/* Try to coalesce forward. */\n\t\textent_t *next = extent_lock_from_addr(tsdn, rtree_ctx,\n\t\t    extent_past_get(extent));\n\t\tif (next != NULL) {\n\t\t\t/*\n\t\t\t * extents->mtx only protects against races for\n\t\t\t * like-state extents, so call extent_can_coalesce()\n\t\t\t * before releasing next's pool lock.\n\t\t\t */\n\t\t\tbool can_coalesce = extent_can_coalesce(arena, extents,\n\t\t\t    extent, next);\n\n\t\t\textent_unlock(tsdn, next);\n\n\t\t\tif (can_coalesce && !extent_coalesce(tsdn, arena,\n\t\t\t    r_extent_hooks, extents, extent, next, true,\n\t\t\t    growing_retained)) {\n\t\t\t\tif (extents->delay_coalesce) {\n\t\t\t\t\t/* Do minimal coalescing. */\n\t\t\t\t\t*coalesced = true;\n\t\t\t\t\treturn extent;\n\t\t\t\t}\n\t\t\t\tagain = true;\n\t\t\t}\n\t\t}\n\n\t\t/* Try to coalesce backward. */\n\t\textent_t *prev = extent_lock_from_addr(tsdn, rtree_ctx,\n\t\t    extent_before_get(extent));\n\t\tif (prev != NULL) {\n\t\t\tbool can_coalesce = extent_can_coalesce(arena, extents,\n\t\t\t    extent, prev);\n\t\t\textent_unlock(tsdn, prev);\n\n\t\t\tif (can_coalesce && !extent_coalesce(tsdn, arena,\n\t\t\t    r_extent_hooks, extents, extent, prev, false,\n\t\t\t    growing_retained)) {\n\t\t\t\textent = prev;\n\t\t\t\tif (extents->delay_coalesce) {\n\t\t\t\t\t/* Do minimal coalescing. */\n\t\t\t\t\t*coalesced = true;\n\t\t\t\t\treturn extent;\n\t\t\t\t}\n\t\t\t\tagain = true;\n\t\t\t}\n\t\t}\n\t} while (again);\n\n\tif (extents->delay_coalesce) {\n\t\t*coalesced = false;\n\t}\n\treturn extent;\n}\n\n/*\n * Does the metadata management portions of putting an unused extent into the\n * given extents_t (coalesces, deregisters slab interiors, the heap operations).\n */\nstatic void\nextent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *extent, bool growing_retained) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tassert((extents_state_get(extents) != extent_state_dirty &&\n\t    extents_state_get(extents) != extent_state_muzzy) ||\n\t    !extent_zeroed_get(extent));\n\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\textent_szind_set(extent, NSIZES);\n\tif (extent_slab_get(extent)) {\n\t\textent_interior_deregister(tsdn, rtree_ctx, extent);\n\t\textent_slab_set(extent, false);\n\t}\n\n\tassert(rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_base_get(extent), true) == extent);\n\n\tif (!extents->delay_coalesce) {\n\t\textent = extent_try_coalesce(tsdn, arena, r_extent_hooks,\n\t\t    rtree_ctx, extents, extent, NULL, growing_retained);\n\t} else if (extent_size_get(extent) >= LARGE_MINCLASS) {\n\t\t/* Always coalesce large extents eagerly. */\n\t\tbool coalesced;\n\t\tsize_t prev_size;\n\t\tdo {\n\t\t\tprev_size = extent_size_get(extent);\n\t\t\tassert(extent_state_get(extent) == extent_state_active);\n\t\t\textent = extent_try_coalesce(tsdn, arena,\n\t\t\t    r_extent_hooks, rtree_ctx, extents, extent,\n\t\t\t    &coalesced, growing_retained);\n\t\t} while (coalesced &&\n\t\t    extent_size_get(extent) >= prev_size + LARGE_MINCLASS);\n\t}\n\textent_deactivate_locked(tsdn, arena, extents, extent);\n\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n}\n\nvoid\nextent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (extent_register(tsdn, extent)) {\n\t\textents_leak(tsdn, arena, &extent_hooks,\n\t\t    &arena->extents_retained, extent, false);\n\t\treturn;\n\t}\n\textent_dalloc_wrapper(tsdn, arena, &extent_hooks, extent);\n}\n\nstatic bool\nextent_dalloc_default_impl(void *addr, size_t size) {\n\tif (!have_dss || !extent_in_dss(addr)) {\n\t\treturn extent_dalloc_mmap(addr, size);\n\t}\n\treturn true;\n}\n\nstatic bool\nextent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\treturn extent_dalloc_default_impl(addr, size);\n}\n\nstatic bool\nextent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\tbool err;\n\n\tassert(extent_base_get(extent) != NULL);\n\tassert(extent_size_get(extent) != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_addr_set(extent, extent_base_get(extent));\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\t/* Try to deallocate. */\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\terr = extent_dalloc_default_impl(extent_base_get(extent),\n\t\t    extent_size_get(extent));\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\terr = ((*r_extent_hooks)->dalloc == NULL ||\n\t\t    (*r_extent_hooks)->dalloc(*r_extent_hooks,\n\t\t    extent_base_get(extent), extent_size_get(extent),\n\t\t    extent_committed_get(extent), arena_ind_get(arena)));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\tif (!err) {\n\t\textent_dalloc(tsdn, arena, extent);\n\t}\n\n\treturn err;\n}\n\nvoid\nextent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\tassert(extent_dumpable_get(extent));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\t/*\n\t * Deregister first to avoid a race with other allocating threads, and\n\t * reregister if deallocation fails.\n\t */\n\textent_deregister(tsdn, extent);\n\tif (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks, extent)) {\n\t\treturn;\n\t}\n\n\textent_reregister(tsdn, extent);\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\t/* Try to decommit; purge if that fails. */\n\tbool zeroed;\n\tif (!extent_committed_get(extent)) {\n\t\tzeroed = true;\n\t} else if (!extent_decommit_wrapper(tsdn, arena, r_extent_hooks, extent,\n\t    0, extent_size_get(extent))) {\n\t\tzeroed = true;\n\t} else if ((*r_extent_hooks)->purge_forced != NULL &&\n\t    !(*r_extent_hooks)->purge_forced(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), 0,\n\t    extent_size_get(extent), arena_ind_get(arena))) {\n\t\tzeroed = true;\n\t} else if (extent_state_get(extent) == extent_state_muzzy ||\n\t    ((*r_extent_hooks)->purge_lazy != NULL &&\n\t    !(*r_extent_hooks)->purge_lazy(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), 0,\n\t    extent_size_get(extent), arena_ind_get(arena)))) {\n\t\tzeroed = false;\n\t} else {\n\t\tzeroed = false;\n\t}\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\textent_zeroed_set(extent, zeroed);\n\n\tif (config_prof) {\n\t\textent_gdump_sub(tsdn, extent);\n\t}\n\n\textent_record(tsdn, arena, r_extent_hooks, &arena->extents_retained,\n\t    extent, false);\n}\n\nstatic void\nextent_destroy_default_impl(void *addr, size_t size) {\n\tif (!have_dss || !extent_in_dss(addr)) {\n\t\tpages_unmap(addr, size);\n\t}\n}\n\nstatic void\nextent_destroy_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\textent_destroy_default_impl(addr, size);\n}\n\nvoid\nextent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\tassert(extent_base_get(extent) != NULL);\n\tassert(extent_size_get(extent) != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\t/* Deregister first to avoid a race with other allocating threads. */\n\textent_deregister(tsdn, extent);\n\n\textent_addr_set(extent, extent_base_get(extent));\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\t/* Try to destroy; silently fail otherwise. */\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\textent_destroy_default_impl(extent_base_get(extent),\n\t\t    extent_size_get(extent));\n\t} else if ((*r_extent_hooks)->destroy != NULL) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\t(*r_extent_hooks)->destroy(*r_extent_hooks,\n\t\t    extent_base_get(extent), extent_size_get(extent),\n\t\t    extent_committed_get(extent), arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\textent_dalloc(tsdn, arena, extent);\n}\n\nstatic bool\nextent_commit_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\treturn pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset),\n\t    length);\n}\n\nstatic bool\nextent_commit_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = ((*r_extent_hooks)->commit == NULL ||\n\t    (*r_extent_hooks)->commit(*r_extent_hooks, extent_base_get(extent),\n\t    extent_size_get(extent), offset, length, arena_ind_get(arena)));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\textent_committed_set(extent, extent_committed_get(extent) || !err);\n\treturn err;\n}\n\nbool\nextent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\treturn extent_commit_impl(tsdn, arena, r_extent_hooks, extent, offset,\n\t    length, false);\n}\n\nstatic bool\nextent_decommit_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\treturn pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset),\n\t    length);\n}\n\nbool\nextent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = ((*r_extent_hooks)->decommit == NULL ||\n\t    (*r_extent_hooks)->decommit(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), offset, length,\n\t    arena_ind_get(arena)));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\textent_committed_set(extent, extent_committed_get(extent) && err);\n\treturn err;\n}\n\n#ifdef PAGES_CAN_PURGE_LAZY\nstatic bool\nextent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tassert(addr != NULL);\n\tassert((offset & PAGE_MASK) == 0);\n\tassert(length != 0);\n\tassert((length & PAGE_MASK) == 0);\n\n\treturn pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset),\n\t    length);\n}\n#endif\n\nstatic bool\nextent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->purge_lazy == NULL) {\n\t\treturn true;\n\t}\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = (*r_extent_hooks)->purge_lazy(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), offset, length,\n\t    arena_ind_get(arena));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\treturn err;\n}\n\nbool\nextent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\treturn extent_purge_lazy_impl(tsdn, arena, r_extent_hooks, extent,\n\t    offset, length, false);\n}\n\n#ifdef PAGES_CAN_PURGE_FORCED\nstatic bool\nextent_purge_forced_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind) {\n\tassert(addr != NULL);\n\tassert((offset & PAGE_MASK) == 0);\n\tassert(length != 0);\n\tassert((length & PAGE_MASK) == 0);\n\n\treturn pages_purge_forced((void *)((uintptr_t)addr +\n\t    (uintptr_t)offset), length);\n}\n#endif\n\nstatic bool\nextent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->purge_forced == NULL) {\n\t\treturn true;\n\t}\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = (*r_extent_hooks)->purge_forced(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), offset, length,\n\t    arena_ind_get(arena));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\treturn err;\n}\n\nbool\nextent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\treturn extent_purge_forced_impl(tsdn, arena, r_extent_hooks, extent,\n\t    offset, length, false);\n}\n\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool\nextent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {\n\treturn !maps_coalesce;\n}\n#endif\n\n/*\n * Accepts the extent to split, and the characteristics of each side of the\n * split.  The 'a' parameters go with the 'lead' of the resulting pair of\n * extents (the lower addressed portion of the split), and the 'b' parameters go\n * with the trail (the higher addressed portion).  This makes 'extent' the lead,\n * and returns the trail (except in case of error).\n */\nstatic extent_t *\nextent_split_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,\n    bool growing_retained) {\n\tassert(extent_size_get(extent) == size_a + size_b);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->split == NULL) {\n\t\treturn NULL;\n\t}\n\n\textent_t *trail = extent_alloc(tsdn, arena);\n\tif (trail == NULL) {\n\t\tgoto label_error_a;\n\t}\n\n\textent_init(trail, arena, (void *)((uintptr_t)extent_base_get(extent) +\n\t    size_a), size_b, slab_b, szind_b, extent_sn_get(extent),\n\t    extent_state_get(extent), extent_zeroed_get(extent),\n\t    extent_committed_get(extent), extent_dumpable_get(extent));\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *lead_elm_a, *lead_elm_b;\n\t{\n\t\textent_t lead;\n\n\t\textent_init(&lead, arena, extent_addr_get(extent), size_a,\n\t\t    slab_a, szind_a, extent_sn_get(extent),\n\t\t    extent_state_get(extent), extent_zeroed_get(extent),\n\t\t    extent_committed_get(extent), extent_dumpable_get(extent));\n\n\t\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, &lead, false,\n\t\t    true, &lead_elm_a, &lead_elm_b);\n\t}\n\trtree_leaf_elm_t *trail_elm_a, *trail_elm_b;\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, trail, false, true,\n\t    &trail_elm_a, &trail_elm_b);\n\n\tif (lead_elm_a == NULL || lead_elm_b == NULL || trail_elm_a == NULL\n\t    || trail_elm_b == NULL) {\n\t\tgoto label_error_b;\n\t}\n\n\textent_lock2(tsdn, extent, trail);\n\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = (*r_extent_hooks)->split(*r_extent_hooks, extent_base_get(extent),\n\t    size_a + size_b, size_a, size_b, extent_committed_get(extent),\n\t    arena_ind_get(arena));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\tif (err) {\n\t\tgoto label_error_c;\n\t}\n\n\textent_size_set(extent, size_a);\n\textent_szind_set(extent, szind_a);\n\n\textent_rtree_write_acquired(tsdn, lead_elm_a, lead_elm_b, extent,\n\t    szind_a, slab_a);\n\textent_rtree_write_acquired(tsdn, trail_elm_a, trail_elm_b, trail,\n\t    szind_b, slab_b);\n\n\textent_unlock2(tsdn, extent, trail);\n\n\treturn trail;\nlabel_error_c:\n\textent_unlock2(tsdn, extent, trail);\nlabel_error_b:\n\textent_dalloc(tsdn, arena, trail);\nlabel_error_a:\n\treturn NULL;\n}\n\nextent_t *\nextent_split_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b) {\n\treturn extent_split_impl(tsdn, arena, r_extent_hooks, extent, size_a,\n\t    szind_a, slab_a, size_b, szind_b, slab_b, false);\n}\n\nstatic bool\nextent_merge_default_impl(void *addr_a, void *addr_b) {\n\tif (!maps_coalesce) {\n\t\treturn true;\n\t}\n\tif (have_dss && !extent_dss_mergeable(addr_a, addr_b)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool\nextent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,\n    void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {\n\treturn extent_merge_default_impl(addr_a, addr_b);\n}\n#endif\n\nstatic bool\nextent_merge_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,\n    bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->merge == NULL) {\n\t\treturn true;\n\t}\n\n\tbool err;\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\terr = extent_merge_default_impl(extent_base_get(a),\n\t\t    extent_base_get(b));\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\terr = (*r_extent_hooks)->merge(*r_extent_hooks,\n\t\t    extent_base_get(a), extent_size_get(a), extent_base_get(b),\n\t\t    extent_size_get(b), extent_committed_get(a),\n\t\t    arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\tif (err) {\n\t\treturn true;\n\t}\n\n\t/*\n\t * The rtree writes must happen while all the relevant elements are\n\t * owned, so the following code uses decomposed helper functions rather\n\t * than extent_{,de}register() to do things in the right order.\n\t */\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *a_elm_a, *a_elm_b, *b_elm_a, *b_elm_b;\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, a, true, false, &a_elm_a,\n\t    &a_elm_b);\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, b, true, false, &b_elm_a,\n\t    &b_elm_b);\n\n\textent_lock2(tsdn, a, b);\n\n\tif (a_elm_b != NULL) {\n\t\trtree_leaf_elm_write(tsdn, &extents_rtree, a_elm_b, NULL,\n\t\t    NSIZES, false);\n\t}\n\tif (b_elm_b != NULL) {\n\t\trtree_leaf_elm_write(tsdn, &extents_rtree, b_elm_a, NULL,\n\t\t    NSIZES, false);\n\t} else {\n\t\tb_elm_b = b_elm_a;\n\t}\n\n\textent_size_set(a, extent_size_get(a) + extent_size_get(b));\n\textent_szind_set(a, NSIZES);\n\textent_sn_set(a, (extent_sn_get(a) < extent_sn_get(b)) ?\n\t    extent_sn_get(a) : extent_sn_get(b));\n\textent_zeroed_set(a, extent_zeroed_get(a) && extent_zeroed_get(b));\n\n\textent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, NSIZES, false);\n\n\textent_unlock2(tsdn, a, b);\n\n\textent_dalloc(tsdn, extent_arena_get(b), b);\n\n\treturn false;\n}\n\nbool\nextent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b) {\n\treturn extent_merge_impl(tsdn, arena, r_extent_hooks, a, b, false);\n}\n\nbool\nextent_boot(void) {\n\tif (rtree_new(&extents_rtree, true)) {\n\t\treturn true;\n\t}\n\n\tif (mutex_pool_init(&extent_mutex_pool, \"extent_mutex_pool\",\n\t    WITNESS_RANK_EXTENT_POOL)) {\n\t\treturn true;\n\t}\n\n\tif (have_dss) {\n\t\textent_dss_boot();\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/extent_dss.c",
    "content": "#define JEMALLOC_EXTENT_DSS_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/spin.h\"\n\n/******************************************************************************/\n/* Data. */\n\nconst char\t*opt_dss = DSS_DEFAULT;\n\nconst char\t*dss_prec_names[] = {\n\t\"disabled\",\n\t\"primary\",\n\t\"secondary\",\n\t\"N/A\"\n};\n\n/*\n * Current dss precedence default, used when creating new arenas.  NB: This is\n * stored as unsigned rather than dss_prec_t because in principle there's no\n * guarantee that sizeof(dss_prec_t) is the same as sizeof(unsigned), and we use\n * atomic operations to synchronize the setting.\n */\nstatic atomic_u_t\tdss_prec_default = ATOMIC_INIT(\n    (unsigned)DSS_PREC_DEFAULT);\n\n/* Base address of the DSS. */\nstatic void\t\t*dss_base;\n/* Atomic boolean indicating whether a thread is currently extending DSS. */\nstatic atomic_b_t\tdss_extending;\n/* Atomic boolean indicating whether the DSS is exhausted. */\nstatic atomic_b_t\tdss_exhausted;\n/* Atomic current upper limit on DSS addresses. */\nstatic atomic_p_t\tdss_max;\n\n/******************************************************************************/\n\nstatic void *\nextent_dss_sbrk(intptr_t increment) {\n#ifdef JEMALLOC_DSS\n\treturn sbrk(increment);\n#else\n\tnot_implemented();\n\treturn NULL;\n#endif\n}\n\ndss_prec_t\nextent_dss_prec_get(void) {\n\tdss_prec_t ret;\n\n\tif (!have_dss) {\n\t\treturn dss_prec_disabled;\n\t}\n\tret = (dss_prec_t)atomic_load_u(&dss_prec_default, ATOMIC_ACQUIRE);\n\treturn ret;\n}\n\nbool\nextent_dss_prec_set(dss_prec_t dss_prec) {\n\tif (!have_dss) {\n\t\treturn (dss_prec != dss_prec_disabled);\n\t}\n\tatomic_store_u(&dss_prec_default, (unsigned)dss_prec, ATOMIC_RELEASE);\n\treturn false;\n}\n\nstatic void\nextent_dss_extending_start(void) {\n\tspin_t spinner = SPIN_INITIALIZER;\n\twhile (true) {\n\t\tbool expected = false;\n\t\tif (atomic_compare_exchange_weak_b(&dss_extending, &expected,\n\t\t    true, ATOMIC_ACQ_REL, ATOMIC_RELAXED)) {\n\t\t\tbreak;\n\t\t}\n\t\tspin_adaptive(&spinner);\n\t}\n}\n\nstatic void\nextent_dss_extending_finish(void) {\n\tassert(atomic_load_b(&dss_extending, ATOMIC_RELAXED));\n\n\tatomic_store_b(&dss_extending, false, ATOMIC_RELEASE);\n}\n\nstatic void *\nextent_dss_max_update(void *new_addr) {\n\t/*\n\t * Get the current end of the DSS as max_cur and assure that dss_max is\n\t * up to date.\n\t */\n\tvoid *max_cur = extent_dss_sbrk(0);\n\tif (max_cur == (void *)-1) {\n\t\treturn NULL;\n\t}\n\tatomic_store_p(&dss_max, max_cur, ATOMIC_RELEASE);\n\t/* Fixed new_addr can only be supported if it is at the edge of DSS. */\n\tif (new_addr != NULL && max_cur != new_addr) {\n\t\treturn NULL;\n\t}\n\treturn max_cur;\n}\n\nvoid *\nextent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit) {\n\textent_t *gap;\n\n\tcassert(have_dss);\n\tassert(size > 0);\n\tassert(alignment > 0);\n\n\t/*\n\t * sbrk() uses a signed increment argument, so take care not to\n\t * interpret a large allocation request as a negative increment.\n\t */\n\tif ((intptr_t)size < 0) {\n\t\treturn NULL;\n\t}\n\n\tgap = extent_alloc(tsdn, arena);\n\tif (gap == NULL) {\n\t\treturn NULL;\n\t}\n\n\textent_dss_extending_start();\n\tif (!atomic_load_b(&dss_exhausted, ATOMIC_ACQUIRE)) {\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\twhile (true) {\n\t\t\tvoid *max_cur = extent_dss_max_update(new_addr);\n\t\t\tif (max_cur == NULL) {\n\t\t\t\tgoto label_oom;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Compute how much page-aligned gap 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\tvoid *gap_addr_page = (void *)(PAGE_CEILING(\n\t\t\t    (uintptr_t)max_cur));\n\t\t\tvoid *ret = (void *)ALIGNMENT_CEILING(\n\t\t\t    (uintptr_t)gap_addr_page, alignment);\n\t\t\tsize_t gap_size_page = (uintptr_t)ret -\n\t\t\t    (uintptr_t)gap_addr_page;\n\t\t\tif (gap_size_page != 0) {\n\t\t\t\textent_init(gap, arena, gap_addr_page,\n\t\t\t\t    gap_size_page, false, NSIZES,\n\t\t\t\t    arena_extent_sn_next(arena),\n\t\t\t\t    extent_state_active, false, true, true);\n\t\t\t}\n\t\t\t/*\n\t\t\t * Compute the address just past the end of the desired\n\t\t\t * allocation space.\n\t\t\t */\n\t\t\tvoid *dss_next = (void *)((uintptr_t)ret + size);\n\t\t\tif ((uintptr_t)ret < (uintptr_t)max_cur ||\n\t\t\t    (uintptr_t)dss_next < (uintptr_t)max_cur) {\n\t\t\t\tgoto label_oom; /* Wrap-around. */\n\t\t\t}\n\t\t\t/* Compute the increment, including subpage bytes. */\n\t\t\tvoid *gap_addr_subpage = max_cur;\n\t\t\tsize_t gap_size_subpage = (uintptr_t)ret -\n\t\t\t    (uintptr_t)gap_addr_subpage;\n\t\t\tintptr_t incr = gap_size_subpage + size;\n\n\t\t\tassert((uintptr_t)max_cur + incr == (uintptr_t)ret +\n\t\t\t    size);\n\n\t\t\t/* Try to allocate. */\n\t\t\tvoid *dss_prev = extent_dss_sbrk(incr);\n\t\t\tif (dss_prev == max_cur) {\n\t\t\t\t/* Success. */\n\t\t\t\tatomic_store_p(&dss_max, dss_next,\n\t\t\t\t    ATOMIC_RELEASE);\n\t\t\t\textent_dss_extending_finish();\n\n\t\t\t\tif (gap_size_page != 0) {\n\t\t\t\t\textent_dalloc_gap(tsdn, arena, gap);\n\t\t\t\t} else {\n\t\t\t\t\textent_dalloc(tsdn, arena, gap);\n\t\t\t\t}\n\t\t\t\tif (!*commit) {\n\t\t\t\t\t*commit = pages_decommit(ret, size);\n\t\t\t\t}\n\t\t\t\tif (*zero && *commit) {\n\t\t\t\t\textent_hooks_t *extent_hooks =\n\t\t\t\t\t    EXTENT_HOOKS_INITIALIZER;\n\t\t\t\t\textent_t extent;\n\n\t\t\t\t\textent_init(&extent, arena, ret, size,\n\t\t\t\t\t    size, false, NSIZES,\n\t\t\t\t\t    extent_state_active, false, true,\n\t\t\t\t\t    true);\n\t\t\t\t\tif (extent_purge_forced_wrapper(tsdn,\n\t\t\t\t\t    arena, &extent_hooks, &extent, 0,\n\t\t\t\t\t    size)) {\n\t\t\t\t\t\tmemset(ret, 0, size);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\t/*\n\t\t\t * Failure, whether due to OOM or a race with a raw\n\t\t\t * sbrk() call from outside the allocator.\n\t\t\t */\n\t\t\tif (dss_prev == (void *)-1) {\n\t\t\t\t/* OOM. */\n\t\t\t\tatomic_store_b(&dss_exhausted, true,\n\t\t\t\t    ATOMIC_RELEASE);\n\t\t\t\tgoto label_oom;\n\t\t\t}\n\t\t}\n\t}\nlabel_oom:\n\textent_dss_extending_finish();\n\textent_dalloc(tsdn, arena, gap);\n\treturn NULL;\n}\n\nstatic bool\nextent_in_dss_helper(void *addr, void *max) {\n\treturn ((uintptr_t)addr >= (uintptr_t)dss_base && (uintptr_t)addr <\n\t    (uintptr_t)max);\n}\n\nbool\nextent_in_dss(void *addr) {\n\tcassert(have_dss);\n\n\treturn extent_in_dss_helper(addr, atomic_load_p(&dss_max,\n\t    ATOMIC_ACQUIRE));\n}\n\nbool\nextent_dss_mergeable(void *addr_a, void *addr_b) {\n\tvoid *max;\n\n\tcassert(have_dss);\n\n\tif ((uintptr_t)addr_a < (uintptr_t)dss_base && (uintptr_t)addr_b <\n\t    (uintptr_t)dss_base) {\n\t\treturn true;\n\t}\n\n\tmax = atomic_load_p(&dss_max, ATOMIC_ACQUIRE);\n\treturn (extent_in_dss_helper(addr_a, max) ==\n\t    extent_in_dss_helper(addr_b, max));\n}\n\nvoid\nextent_dss_boot(void) {\n\tcassert(have_dss);\n\n\tdss_base = extent_dss_sbrk(0);\n\tatomic_store_b(&dss_extending, false, ATOMIC_RELAXED);\n\tatomic_store_b(&dss_exhausted, dss_base == (void *)-1, ATOMIC_RELAXED);\n\tatomic_store_p(&dss_max, dss_base, ATOMIC_RELAXED);\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/extent_mmap.c",
    "content": "#define JEMALLOC_EXTENT_MMAP_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n\n/******************************************************************************/\n/* Data. */\n\nbool\topt_retain =\n#ifdef JEMALLOC_RETAIN\n    true\n#else\n    false\n#endif\n    ;\n\n/******************************************************************************/\n\nvoid *\nextent_alloc_mmap(void *new_addr, size_t size, size_t alignment, bool *zero,\n    bool *commit) {\n\tvoid *ret = pages_map(new_addr, size, ALIGNMENT_CEILING(alignment,\n\t    PAGE), commit);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\tassert(ret != NULL);\n\tif (*commit) {\n\t\t*zero = true;\n\t}\n\treturn ret;\n}\n\nbool\nextent_dalloc_mmap(void *addr, size_t size) {\n\tif (!opt_retain) {\n\t\tpages_unmap(addr, size);\n\t}\n\treturn opt_retain;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/hash.c",
    "content": "#define JEMALLOC_HASH_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/hooks.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n/*\n * The hooks are a little bit screwy -- they're not genuinely exported in the\n * sense that we want them available to end-users, but we do want them visible\n * from outside the generated library, so that we can use them in test code.\n */\nJEMALLOC_EXPORT\nvoid (*hooks_arena_new_hook)() = NULL;\n\nJEMALLOC_EXPORT\nvoid (*hooks_libc_hook)() = NULL;\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/jemalloc.c",
    "content": "#define JEMALLOC_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/log.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/spin.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/ticker.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/* Runtime configuration options. */\nconst char\t*je_malloc_conf\n#ifndef _WIN32\n    JEMALLOC_ATTR(weak)\n#endif\n    ;\nbool\topt_abort =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nbool\topt_abort_conf =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nconst char\t*opt_junk =\n#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))\n    \"true\"\n#else\n    \"false\"\n#endif\n    ;\nbool\topt_junk_alloc =\n#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))\n    true\n#else\n    false\n#endif\n    ;\nbool\topt_junk_free =\n#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))\n    true\n#else\n    false\n#endif\n    ;\n\nbool\topt_utrace = false;\nbool\topt_xmalloc = false;\nbool\topt_zero = false;\nunsigned\topt_narenas = 0;\n\nunsigned\tncpus;\n\n/* Protects arenas initialization. */\nmalloc_mutex_t arenas_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 *\n * Points to an arena_t.\n */\nJEMALLOC_ALIGNED(CACHELINE)\natomic_p_t\t\tarenas[MALLOCX_ARENA_LIMIT];\nstatic atomic_u_t\tnarenas_total; /* Use narenas_total_*(). */\nstatic arena_t\t\t*a0; /* arenas[0]; read-only after initialization. */\nunsigned\t\tnarenas_auto; /* Read-only after initialization. */\n\ntypedef enum {\n\tmalloc_init_uninitialized\t= 3,\n\tmalloc_init_a0_initialized\t= 2,\n\tmalloc_init_recursible\t\t= 1,\n\tmalloc_init_initialized\t\t= 0 /* Common case --> jnz. */\n} malloc_init_t;\nstatic malloc_init_t\tmalloc_init_state = malloc_init_uninitialized;\n\n/* False should be the common case.  Set to true to trigger initialization. */\nbool\t\t\tmalloc_slow = true;\n\n/* When malloc_slow is true, set the corresponding bits for sanity check. */\nenum {\n\tflag_opt_junk_alloc\t= (1U),\n\tflag_opt_junk_free\t= (1U << 1),\n\tflag_opt_zero\t\t= (1U << 2),\n\tflag_opt_utrace\t\t= (1U << 3),\n\tflag_opt_xmalloc\t= (1U << 4)\n};\nstatic uint8_t\tmalloc_slow_flags;\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\n#if _WIN32_WINNT >= 0x0600\nstatic malloc_mutex_t\tinit_lock = SRWLOCK_INIT;\n#else\nstatic malloc_mutex_t\tinit_lock;\nstatic bool init_lock_initialized = false;\n\nJEMALLOC_ATTR(constructor)\nstatic void WINAPI\n_init_init_lock(void) {\n\t/*\n\t * If another constructor in the same binary is using mallctl to e.g.\n\t * set up extent hooks, it may end up running before this one, and\n\t * malloc_init_hard will crash trying to lock the uninitialized lock. So\n\t * we force an initialization of the lock in malloc_init_hard as well.\n\t * We don't try to care about atomicity of the accessed to the\n\t * init_lock_initialized boolean, since it really only matters early in\n\t * the process creation, before any separate thread normally starts\n\t * doing anything.\n\t */\n\tif (!init_lock_initialized) {\n\t\tmalloc_mutex_init(&init_lock, \"init\", WITNESS_RANK_INIT,\n\t\t    malloc_mutex_rank_exclusive);\n\t}\n\tinit_lock_initialized = true;\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#endif\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 (unlikely(opt_utrace)) {\t\t\t\t\t\\\n\t\tint utrace_serrno = errno;\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\terrno = utrace_serrno;\t\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/* Whether encountered any invalid config options. */\nstatic bool had_conf_error = false;\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic bool\tmalloc_init_hard_a0(void);\nstatic bool\tmalloc_init_hard(void);\n\n/******************************************************************************/\n/*\n * Begin miscellaneous support functions.\n */\n\nbool\nmalloc_initialized(void) {\n\treturn (malloc_init_state == malloc_init_initialized);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nmalloc_init_a0(void) {\n\tif (unlikely(malloc_init_state == malloc_init_uninitialized)) {\n\t\treturn malloc_init_hard_a0();\n\t}\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nmalloc_init(void) {\n\tif (unlikely(!malloc_initialized()) && malloc_init_hard()) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*\n * The a0*() functions are used instead of i{d,}alloc() in situations that\n * cannot tolerate TLS variable access.\n */\n\nstatic void *\na0ialloc(size_t size, bool zero, bool is_internal) {\n\tif (unlikely(malloc_init_a0())) {\n\t\treturn NULL;\n\t}\n\n\treturn iallocztm(TSDN_NULL, size, sz_size2index(size), zero, NULL,\n\t    is_internal, arena_get(TSDN_NULL, 0, true), true);\n}\n\nstatic void\na0idalloc(void *ptr, bool is_internal) {\n\tidalloctm(TSDN_NULL, ptr, NULL, NULL, is_internal, true);\n}\n\nvoid *\na0malloc(size_t size) {\n\treturn a0ialloc(size, false, true);\n}\n\nvoid\na0dalloc(void *ptr) {\n\ta0idalloc(ptr, true);\n}\n\n/*\n * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-senstive\n * situations that cannot tolerate TLS variable access (TLS allocation and very\n * early internal data structure initialization).\n */\n\nvoid *\nbootstrap_malloc(size_t size) {\n\tif (unlikely(size == 0)) {\n\t\tsize = 1;\n\t}\n\n\treturn a0ialloc(size, false, false);\n}\n\nvoid *\nbootstrap_calloc(size_t num, size_t size) {\n\tsize_t num_size;\n\n\tnum_size = num * size;\n\tif (unlikely(num_size == 0)) {\n\t\tassert(num == 0 || size == 0);\n\t\tnum_size = 1;\n\t}\n\n\treturn a0ialloc(num_size, true, false);\n}\n\nvoid\nbootstrap_free(void *ptr) {\n\tif (unlikely(ptr == NULL)) {\n\t\treturn;\n\t}\n\n\ta0idalloc(ptr, false);\n}\n\nvoid\narena_set(unsigned ind, arena_t *arena) {\n\tatomic_store_p(&arenas[ind], arena, ATOMIC_RELEASE);\n}\n\nstatic void\nnarenas_total_set(unsigned narenas) {\n\tatomic_store_u(&narenas_total, narenas, ATOMIC_RELEASE);\n}\n\nstatic void\nnarenas_total_inc(void) {\n\tatomic_fetch_add_u(&narenas_total, 1, ATOMIC_RELEASE);\n}\n\nunsigned\nnarenas_total_get(void) {\n\treturn atomic_load_u(&narenas_total, ATOMIC_ACQUIRE);\n}\n\n/* Create a new arena and insert it into the arenas array at index ind. */\nstatic arena_t *\narena_init_locked(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tarena_t *arena;\n\n\tassert(ind <= narenas_total_get());\n\tif (ind >= MALLOCX_ARENA_LIMIT) {\n\t\treturn NULL;\n\t}\n\tif (ind == narenas_total_get()) {\n\t\tnarenas_total_inc();\n\t}\n\n\t/*\n\t * Another thread may have already initialized arenas[ind] if it's an\n\t * auto arena.\n\t */\n\tarena = arena_get(tsdn, ind, false);\n\tif (arena != NULL) {\n\t\tassert(ind < narenas_auto);\n\t\treturn arena;\n\t}\n\n\t/* Actually initialize the arena. */\n\tarena = arena_new(tsdn, ind, extent_hooks);\n\n\treturn arena;\n}\n\nstatic void\narena_new_create_background_thread(tsdn_t *tsdn, unsigned ind) {\n\tif (ind == 0) {\n\t\treturn;\n\t}\n\tif (have_background_thread) {\n\t\tbool err;\n\t\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\t\terr = background_thread_create(tsdn_tsd(tsdn), ind);\n\t\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\t\tif (err) {\n\t\t\tmalloc_printf(\"<jemalloc>: error in background thread \"\n\t\t\t\t      \"creation for arena %u. Abort.\\n\", ind);\n\t\t\tabort();\n\t\t}\n\t}\n}\n\narena_t *\narena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tarena_t *arena;\n\n\tmalloc_mutex_lock(tsdn, &arenas_lock);\n\tarena = arena_init_locked(tsdn, ind, extent_hooks);\n\tmalloc_mutex_unlock(tsdn, &arenas_lock);\n\n\tarena_new_create_background_thread(tsdn, ind);\n\n\treturn arena;\n}\n\nstatic void\narena_bind(tsd_t *tsd, unsigned ind, bool internal) {\n\tarena_t *arena = arena_get(tsd_tsdn(tsd), ind, false);\n\tarena_nthreads_inc(arena, internal);\n\n\tif (internal) {\n\t\ttsd_iarena_set(tsd, arena);\n\t} else {\n\t\ttsd_arena_set(tsd, arena);\n\t}\n}\n\nvoid\narena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind) {\n\tarena_t *oldarena, *newarena;\n\n\toldarena = arena_get(tsd_tsdn(tsd), oldind, false);\n\tnewarena = arena_get(tsd_tsdn(tsd), newind, false);\n\tarena_nthreads_dec(oldarena, false);\n\tarena_nthreads_inc(newarena, false);\n\ttsd_arena_set(tsd, newarena);\n}\n\nstatic void\narena_unbind(tsd_t *tsd, unsigned ind, bool internal) {\n\tarena_t *arena;\n\n\tarena = arena_get(tsd_tsdn(tsd), ind, false);\n\tarena_nthreads_dec(arena, internal);\n\n\tif (internal) {\n\t\ttsd_iarena_set(tsd, NULL);\n\t} else {\n\t\ttsd_arena_set(tsd, NULL);\n\t}\n}\n\narena_tdata_t *\narena_tdata_get_hard(tsd_t *tsd, unsigned ind) {\n\tarena_tdata_t *tdata, *arenas_tdata_old;\n\tarena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);\n\tunsigned narenas_tdata_old, i;\n\tunsigned narenas_tdata = tsd_narenas_tdata_get(tsd);\n\tunsigned narenas_actual = narenas_total_get();\n\n\t/*\n\t * Dissociate old tdata array (and set up for deallocation upon return)\n\t * if it's too small.\n\t */\n\tif (arenas_tdata != NULL && narenas_tdata < narenas_actual) {\n\t\tarenas_tdata_old = arenas_tdata;\n\t\tnarenas_tdata_old = narenas_tdata;\n\t\tarenas_tdata = NULL;\n\t\tnarenas_tdata = 0;\n\t\ttsd_arenas_tdata_set(tsd, arenas_tdata);\n\t\ttsd_narenas_tdata_set(tsd, narenas_tdata);\n\t} else {\n\t\tarenas_tdata_old = NULL;\n\t\tnarenas_tdata_old = 0;\n\t}\n\n\t/* Allocate tdata array if it's missing. */\n\tif (arenas_tdata == NULL) {\n\t\tbool *arenas_tdata_bypassp = tsd_arenas_tdata_bypassp_get(tsd);\n\t\tnarenas_tdata = (ind < narenas_actual) ? narenas_actual : ind+1;\n\n\t\tif (tsd_nominal(tsd) && !*arenas_tdata_bypassp) {\n\t\t\t*arenas_tdata_bypassp = true;\n\t\t\tarenas_tdata = (arena_tdata_t *)a0malloc(\n\t\t\t    sizeof(arena_tdata_t) * narenas_tdata);\n\t\t\t*arenas_tdata_bypassp = false;\n\t\t}\n\t\tif (arenas_tdata == NULL) {\n\t\t\ttdata = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tassert(tsd_nominal(tsd) && !*arenas_tdata_bypassp);\n\t\ttsd_arenas_tdata_set(tsd, arenas_tdata);\n\t\ttsd_narenas_tdata_set(tsd, narenas_tdata);\n\t}\n\n\t/*\n\t * Copy to tdata array.  It's possible that the actual number of arenas\n\t * has increased since narenas_total_get() was called above, but that\n\t * causes no correctness issues unless two threads concurrently execute\n\t * the arenas.create mallctl, which we trust mallctl synchronization to\n\t * prevent.\n\t */\n\n\t/* Copy/initialize tickers. */\n\tfor (i = 0; i < narenas_actual; i++) {\n\t\tif (i < narenas_tdata_old) {\n\t\t\tticker_copy(&arenas_tdata[i].decay_ticker,\n\t\t\t    &arenas_tdata_old[i].decay_ticker);\n\t\t} else {\n\t\t\tticker_init(&arenas_tdata[i].decay_ticker,\n\t\t\t    DECAY_NTICKS_PER_UPDATE);\n\t\t}\n\t}\n\tif (narenas_tdata > narenas_actual) {\n\t\tmemset(&arenas_tdata[narenas_actual], 0, sizeof(arena_tdata_t)\n\t\t    * (narenas_tdata - narenas_actual));\n\t}\n\n\t/* Read the refreshed tdata array. */\n\ttdata = &arenas_tdata[ind];\nlabel_return:\n\tif (arenas_tdata_old != NULL) {\n\t\ta0dalloc(arenas_tdata_old);\n\t}\n\treturn tdata;\n}\n\n/* Slow path, called only by arena_choose(). */\narena_t *\narena_choose_hard(tsd_t *tsd, bool internal) {\n\tarena_t *ret JEMALLOC_CC_SILENCE_INIT(NULL);\n\n\tif (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena)) {\n\t\tunsigned choose = percpu_arena_choose();\n\t\tret = arena_get(tsd_tsdn(tsd), choose, true);\n\t\tassert(ret != NULL);\n\t\tarena_bind(tsd, arena_ind_get(ret), false);\n\t\tarena_bind(tsd, arena_ind_get(ret), true);\n\n\t\treturn ret;\n\t}\n\n\tif (narenas_auto > 1) {\n\t\tunsigned i, j, choose[2], first_null;\n\t\tbool is_new_arena[2];\n\n\t\t/*\n\t\t * Determine binding for both non-internal and internal\n\t\t * allocation.\n\t\t *\n\t\t *   choose[0]: For application allocation.\n\t\t *   choose[1]: For internal metadata allocation.\n\t\t */\n\n\t\tfor (j = 0; j < 2; j++) {\n\t\t\tchoose[j] = 0;\n\t\t\tis_new_arena[j] = false;\n\t\t}\n\n\t\tfirst_null = narenas_auto;\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &arenas_lock);\n\t\tassert(arena_get(tsd_tsdn(tsd), 0, false) != NULL);\n\t\tfor (i = 1; i < narenas_auto; i++) {\n\t\t\tif (arena_get(tsd_tsdn(tsd), i, false) != 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\tfor (j = 0; j < 2; j++) {\n\t\t\t\t\tif (arena_nthreads_get(arena_get(\n\t\t\t\t\t    tsd_tsdn(tsd), i, false), !!j) <\n\t\t\t\t\t    arena_nthreads_get(arena_get(\n\t\t\t\t\t    tsd_tsdn(tsd), choose[j], false),\n\t\t\t\t\t    !!j)) {\n\t\t\t\t\t\tchoose[j] = i;\n\t\t\t\t\t}\n\t\t\t\t}\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\tfor (j = 0; j < 2; j++) {\n\t\t\tif (arena_nthreads_get(arena_get(tsd_tsdn(tsd),\n\t\t\t    choose[j], false), !!j) == 0 || first_null ==\n\t\t\t    narenas_auto) {\n\t\t\t\t/*\n\t\t\t\t * Use an unloaded arena, or the least loaded\n\t\t\t\t * arena if all arenas are already initialized.\n\t\t\t\t */\n\t\t\t\tif (!!j == internal) {\n\t\t\t\t\tret = arena_get(tsd_tsdn(tsd),\n\t\t\t\t\t    choose[j], false);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tarena_t *arena;\n\n\t\t\t\t/* Initialize a new arena. */\n\t\t\t\tchoose[j] = first_null;\n\t\t\t\tarena = arena_init_locked(tsd_tsdn(tsd),\n\t\t\t\t    choose[j],\n\t\t\t\t    (extent_hooks_t *)&extent_hooks_default);\n\t\t\t\tif (arena == NULL) {\n\t\t\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd),\n\t\t\t\t\t    &arenas_lock);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tis_new_arena[j] = true;\n\t\t\t\tif (!!j == internal) {\n\t\t\t\t\tret = arena;\n\t\t\t\t}\n\t\t\t}\n\t\t\tarena_bind(tsd, choose[j], !!j);\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arenas_lock);\n\n\t\tfor (j = 0; j < 2; j++) {\n\t\t\tif (is_new_arena[j]) {\n\t\t\t\tassert(choose[j] > 0);\n\t\t\t\tarena_new_create_background_thread(\n\t\t\t\t    tsd_tsdn(tsd), choose[j]);\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tret = arena_get(tsd_tsdn(tsd), 0, false);\n\t\tarena_bind(tsd, 0, false);\n\t\tarena_bind(tsd, 0, true);\n\t}\n\n\treturn ret;\n}\n\nvoid\niarena_cleanup(tsd_t *tsd) {\n\tarena_t *iarena;\n\n\tiarena = tsd_iarena_get(tsd);\n\tif (iarena != NULL) {\n\t\tarena_unbind(tsd, arena_ind_get(iarena), true);\n\t}\n}\n\nvoid\narena_cleanup(tsd_t *tsd) {\n\tarena_t *arena;\n\n\tarena = tsd_arena_get(tsd);\n\tif (arena != NULL) {\n\t\tarena_unbind(tsd, arena_ind_get(arena), false);\n\t}\n}\n\nvoid\narenas_tdata_cleanup(tsd_t *tsd) {\n\tarena_tdata_t *arenas_tdata;\n\n\t/* Prevent tsd->arenas_tdata from being (re)created. */\n\t*tsd_arenas_tdata_bypassp_get(tsd) = true;\n\n\tarenas_tdata = tsd_arenas_tdata_get(tsd);\n\tif (arenas_tdata != NULL) {\n\t\ttsd_arenas_tdata_set(tsd, NULL);\n\t\ta0dalloc(arenas_tdata);\n\t}\n}\n\nstatic void\nstats_print_atexit(void) {\n\tif (config_stats) {\n\t\ttsdn_t *tsdn;\n\t\tunsigned narenas, i;\n\n\t\ttsdn = tsdn_fetch();\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 = arena_get(tsdn, i, false);\n\t\t\tif (arena != NULL) {\n\t\t\t\ttcache_t *tcache;\n\n\t\t\t\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\t\t\t\tql_foreach(tcache, &arena->tcache_ql, link) {\n\t\t\t\t\ttcache_stats_merge(tsdn, tcache, arena);\n\t\t\t\t}\n\t\t\t\tmalloc_mutex_unlock(tsdn,\n\t\t\t\t    &arena->tcache_ql_mtx);\n\t\t\t}\n\t\t}\n\t}\n\tje_malloc_stats_print(NULL, NULL, opt_stats_print_opts);\n}\n\n/*\n * Ensure that we don't hold any locks upon entry to or exit from allocator\n * code (in a \"broad\" sense that doesn't count a reentrant allocation as an\n * entrance or exit).\n */\nJEMALLOC_ALWAYS_INLINE void\ncheck_entry_exit_locking(tsdn_t *tsdn) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\tif (tsdn_null(tsdn)) {\n\t\treturn;\n\t}\n\ttsd_t *tsd = tsdn_tsd(tsdn);\n\t/*\n\t * It's possible we hold locks at entry/exit if we're in a nested\n\t * allocation.\n\t */\n\tint8_t reentrancy_level = tsd_reentrancy_level_get(tsd);\n\tif (reentrancy_level != 0) {\n\t\treturn;\n\t}\n\twitness_assert_lockless(tsdn_witness_tsdp_get(tsdn));\n}\n\n/*\n * End miscellaneous support functions.\n */\n/******************************************************************************/\n/*\n * Begin initialization functions.\n */\n\nstatic char *\njemalloc_secure_getenv(const char *name) {\n#ifdef JEMALLOC_HAVE_SECURE_GETENV\n\treturn secure_getenv(name);\n#else\n#  ifdef JEMALLOC_HAVE_ISSETUGID\n\tif (issetugid() != 0) {\n\t\treturn NULL;\n\t}\n#  endif\n\treturn getenv(name);\n#endif\n}\n\nstatic unsigned\nmalloc_ncpus(void) {\n\tlong result;\n\n#ifdef _WIN32\n\tSYSTEM_INFO si;\n\tGetSystemInfo(&si);\n\tresult = si.dwNumberOfProcessors;\n#elif defined(JEMALLOC_GLIBC_MALLOC_HOOK) && defined(CPU_COUNT)\n\t/*\n\t * glibc >= 2.6 has the CPU_COUNT macro.\n\t *\n\t * glibc's sysconf() uses isspace().  glibc allocates for the first time\n\t * *before* setting up the isspace tables.  Therefore we need a\n\t * different method to get the number of CPUs.\n\t */\n\t{\n\t\tcpu_set_t set;\n\n\t\tpthread_getaffinity_np(pthread_self(), sizeof(set), &set);\n\t\tresult = CPU_COUNT(&set);\n\t}\n#else\n\tresult = sysconf(_SC_NPROCESSORS_ONLN);\n#endif\n\treturn ((result == -1) ? 1 : (unsigned)result);\n}\n\nstatic void\ninit_opt_stats_print_opts(const char *v, size_t vlen) {\n\tsize_t opts_len = strlen(opt_stats_print_opts);\n\tassert(opts_len <= stats_print_tot_num_options);\n\n\tfor (size_t i = 0; i < vlen; i++) {\n\t\tswitch (v[i]) {\n#define OPTION(o, v, d, s) case o: break;\n\t\t\tSTATS_PRINT_OPTIONS\n#undef OPTION\n\t\tdefault: continue;\n\t\t}\n\n\t\tif (strchr(opt_stats_print_opts, v[i]) != NULL) {\n\t\t\t/* Ignore repeated. */\n\t\t\tcontinue;\n\t\t}\n\n\t\topt_stats_print_opts[opts_len++] = v[i];\n\t\topt_stats_print_opts[opts_len] = '\\0';\n\t\tassert(opts_len <= stats_print_tot_num_options);\n\t}\n\tassert(opts_len == strlen(opt_stats_print_opts));\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\tbool accept;\n\tconst char *opts = *opts_p;\n\n\t*k_p = opts;\n\n\tfor (accept = false; !accept;) {\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;) {\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_abort_invalid_conf(void) {\n\tassert(opt_abort_conf);\n\tmalloc_printf(\"<jemalloc>: Abort (abort_conf:true) on invalid conf \"\n\t    \"value (see above).\\n\");\n\tabort();\n}\n\nstatic void\nmalloc_conf_error(const char *msg, const char *k, size_t klen, const char *v,\n    size_t vlen) {\n\tmalloc_printf(\"<jemalloc>: %s: %.*s:%.*s\\n\", msg, (int)klen, k,\n\t    (int)vlen, v);\n\t/* If abort_conf is set, error out after processing all options. */\n\thad_conf_error = true;\n}\n\nstatic void\nmalloc_slow_flag_init(void) {\n\t/*\n\t * Combine the runtime options into malloc_slow for fast path.  Called\n\t * after processing all the options.\n\t */\n\tmalloc_slow_flags |= (opt_junk_alloc ? flag_opt_junk_alloc : 0)\n\t    | (opt_junk_free ? flag_opt_junk_free : 0)\n\t    | (opt_zero ? flag_opt_zero : 0)\n\t    | (opt_utrace ? flag_opt_utrace : 0)\n\t    | (opt_xmalloc ? flag_opt_xmalloc : 0);\n\n\tmalloc_slow = (malloc_slow_flags != 0);\n}\n\nstatic void\nmalloc_conf_init(void) {\n\tunsigned i;\n\tchar buf[PATH_MAX + 1];\n\tconst char *opts, *k, *v;\n\tsize_t klen, vlen;\n\n\tfor (i = 0; i < 4; i++) {\n\t\t/* Get runtime configuration. */\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\topts = config_malloc_conf;\n\t\t\tbreak;\n\t\tcase 1:\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 2: {\n\t\t\tssize_t linklen = 0;\n#ifndef _WIN32\n\t\t\tint saved_errno = errno;\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\t/*\n\t\t\t * Try to use the contents of the \"/etc/malloc.conf\"\n\t\t\t * symbolic link's name.\n\t\t\t */\n\t\t\tlinklen = readlink(linkname, buf, sizeof(buf) - 1);\n\t\t\tif (linklen == -1) {\n\t\t\t\t/* No configuration specified. */\n\t\t\t\tlinklen = 0;\n\t\t\t\t/* Restore errno. */\n\t\t\t\tset_errno(saved_errno);\n\t\t\t}\n#endif\n\t\t\tbuf[linklen] = '\\0';\n\t\t\topts = buf;\n\t\t\tbreak;\n\t\t} case 3: {\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 = jemalloc_secure_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\tnot_reached();\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)) {\n#define CONF_MATCH(n)\t\t\t\t\t\t\t\\\n\t(sizeof(n)-1 == klen && strncmp(n, k, klen) == 0)\n#define CONF_MATCH_VALUE(n)\t\t\t\t\t\t\\\n\t(sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0)\n#define CONF_HANDLE_BOOL(o, n)\t\t\t\t\t\t\\\n\t\t\tif (CONF_MATCH(n)) {\t\t\t\t\\\n\t\t\t\tif (CONF_MATCH_VALUE(\"true\")) {\t\t\\\n\t\t\t\t\to = true;\t\t\t\\\n\t\t\t\t} else if (CONF_MATCH_VALUE(\"false\")) {\t\\\n\t\t\t\t\to = false;\t\t\t\\\n\t\t\t\t} else {\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\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define CONF_MIN_no(um, min)\tfalse\n#define CONF_MIN_yes(um, min)\t((um) < (min))\n#define CONF_MAX_no(um, max)\tfalse\n#define CONF_MAX_yes(um, max)\t((um) > (max))\n#define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip)\t\\\n\t\t\tif (CONF_MATCH(n)) {\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 (clip) {\t\t\t\\\n\t\t\t\t\tif (CONF_MIN_##check_min(um,\t\\\n\t\t\t\t\t    (t)(min))) {\t\t\\\n\t\t\t\t\t\to = (t)(min);\t\t\\\n\t\t\t\t\t} else if (\t\t\t\\\n\t\t\t\t\t    CONF_MAX_##check_max(um,\t\\\n\t\t\t\t\t    (t)(max))) {\t\t\\\n\t\t\t\t\t\to = (t)(max);\t\t\\\n\t\t\t\t\t} else {\t\t\t\\\n\t\t\t\t\t\to = (t)um;\t\t\\\n\t\t\t\t\t}\t\t\t\t\\\n\t\t\t\t} else {\t\t\t\t\\\n\t\t\t\t\tif (CONF_MIN_##check_min(um,\t\\\n\t\t\t\t\t    (t)(min)) ||\t\t\\\n\t\t\t\t\t    CONF_MAX_##check_max(um,\t\\\n\t\t\t\t\t    (t)(max))) {\t\t\\\n\t\t\t\t\t\tmalloc_conf_error(\t\\\n\t\t\t\t\t\t    \"Out-of-range \"\t\\\n\t\t\t\t\t\t    \"conf value\",\t\\\n\t\t\t\t\t\t    k, klen, v, vlen);\t\\\n\t\t\t\t\t} else {\t\t\t\\\n\t\t\t\t\t\to = (t)um;\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\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max,\t\\\n    clip)\t\t\t\t\t\t\t\t\\\n\t\t\tCONF_HANDLE_T_U(unsigned, o, n, min, max,\t\\\n\t\t\t    check_min, check_max, clip)\n#define CONF_HANDLE_SIZE_T(o, n, min, max, check_min, check_max, clip)\t\\\n\t\t\tCONF_HANDLE_T_U(size_t, o, n, min, max,\t\t\\\n\t\t\t    check_min, check_max, clip)\n#define CONF_HANDLE_SSIZE_T(o, n, min, max)\t\t\t\t\\\n\t\t\tif (CONF_MATCH(n)) {\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\\\n\t\t\t\t\to = l;\t\t\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define CONF_HANDLE_CHAR_P(o, n, d)\t\t\t\t\t\\\n\t\t\tif (CONF_MATCH(n)) {\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\tCONF_HANDLE_BOOL(opt_abort_conf, \"abort_conf\")\n\t\t\tif (strncmp(\"metadata_thp\", k, klen) == 0) {\n\t\t\t\tint i;\n\t\t\t\tbool match = false;\n\t\t\t\tfor (i = 0; i < metadata_thp_mode_limit; i++) {\n\t\t\t\t\tif (strncmp(metadata_thp_mode_names[i],\n\t\t\t\t\t    v, vlen) == 0) {\n\t\t\t\t\t\topt_metadata_thp = i;\n\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!match) {\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_BOOL(opt_retain, \"retain\")\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 (extent_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) {\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_UNSIGNED(opt_narenas, \"narenas\", 1,\n\t\t\t    UINT_MAX, yes, no, false)\n\t\t\tCONF_HANDLE_SSIZE_T(opt_dirty_decay_ms,\n\t\t\t    \"dirty_decay_ms\", -1, NSTIME_SEC_MAX * KQU(1000) <\n\t\t\t    QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :\n\t\t\t    SSIZE_MAX);\n\t\t\tCONF_HANDLE_SSIZE_T(opt_muzzy_decay_ms,\n\t\t\t    \"muzzy_decay_ms\", -1, NSTIME_SEC_MAX * KQU(1000) <\n\t\t\t    QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :\n\t\t\t    SSIZE_MAX);\n\t\t\tCONF_HANDLE_BOOL(opt_stats_print, \"stats_print\")\n\t\t\tif (CONF_MATCH(\"stats_print_opts\")) {\n\t\t\t\tinit_opt_stats_print_opts(v, vlen);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (config_fill) {\n\t\t\t\tif (CONF_MATCH(\"junk\")) {\n\t\t\t\t\tif (CONF_MATCH_VALUE(\"true\")) {\n\t\t\t\t\t\topt_junk = \"true\";\n\t\t\t\t\t\topt_junk_alloc = opt_junk_free =\n\t\t\t\t\t\t    true;\n\t\t\t\t\t} else if (CONF_MATCH_VALUE(\"false\")) {\n\t\t\t\t\t\topt_junk = \"false\";\n\t\t\t\t\t\topt_junk_alloc = opt_junk_free =\n\t\t\t\t\t\t    false;\n\t\t\t\t\t} else if (CONF_MATCH_VALUE(\"alloc\")) {\n\t\t\t\t\t\topt_junk = \"alloc\";\n\t\t\t\t\t\topt_junk_alloc = true;\n\t\t\t\t\t\topt_junk_free = false;\n\t\t\t\t\t} else if (CONF_MATCH_VALUE(\"free\")) {\n\t\t\t\t\t\topt_junk = \"free\";\n\t\t\t\t\t\topt_junk_alloc = false;\n\t\t\t\t\t\topt_junk_free = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t    \"Invalid conf value\", k,\n\t\t\t\t\t\t    klen, v, vlen);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\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_xmalloc) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_xmalloc, \"xmalloc\")\n\t\t\t}\n\t\t\tCONF_HANDLE_BOOL(opt_tcache, \"tcache\")\n\t\t\tCONF_HANDLE_SIZE_T(opt_lg_extent_max_active_fit,\n\t\t\t    \"lg_extent_max_active_fit\", 0,\n\t\t\t    (sizeof(size_t) << 3), yes, yes, false)\n\t\t\tCONF_HANDLE_SSIZE_T(opt_lg_tcache_max, \"lg_tcache_max\",\n\t\t\t    -1, (sizeof(size_t) << 3) - 1)\n\t\t\tif (strncmp(\"percpu_arena\", k, klen) == 0) {\n\t\t\t\tbool match = false;\n\t\t\t\tfor (int i = percpu_arena_mode_names_base; i <\n\t\t\t\t    percpu_arena_mode_names_limit; i++) {\n\t\t\t\t\tif (strncmp(percpu_arena_mode_names[i],\n\t\t\t\t\t    v, vlen) == 0) {\n\t\t\t\t\t\tif (!have_percpu_arena) {\n\t\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t\t    \"No getcpu support\",\n\t\t\t\t\t\t\t    k, klen, v, vlen);\n\t\t\t\t\t\t}\n\t\t\t\t\t\topt_percpu_arena = i;\n\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!match) {\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_BOOL(opt_background_thread,\n\t\t\t    \"background_thread\");\n\t\t\tCONF_HANDLE_SIZE_T(opt_max_background_threads,\n\t\t\t\t\t   \"max_background_threads\", 1,\n\t\t\t\t\t   opt_max_background_threads, yes, yes,\n\t\t\t\t\t   true);\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_BOOL(opt_prof_thread_active_init,\n\t\t\t\t    \"prof_thread_active_init\")\n\t\t\t\tCONF_HANDLE_SIZE_T(opt_lg_prof_sample,\n\t\t\t\t    \"lg_prof_sample\", 0, (sizeof(uint64_t) << 3)\n\t\t\t\t    - 1, no, yes, true)\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\tif (config_log) {\n\t\t\t\tif (CONF_MATCH(\"log\")) {\n\t\t\t\t\tsize_t cpylen = (\n\t\t\t\t\t    vlen <= sizeof(log_var_names) ?\n\t\t\t\t\t    vlen : sizeof(log_var_names) - 1);\n\t\t\t\t\tstrncpy(log_var_names, v, cpylen);\n\t\t\t\t\tlog_var_names[cpylen] = '\\0';\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (CONF_MATCH(\"thp\")) {\n\t\t\t\tbool match = false;\n\t\t\t\tfor (int i = 0; i < thp_mode_names_limit; i++) {\n\t\t\t\t\tif (strncmp(thp_mode_names[i],v, vlen)\n\t\t\t\t\t    == 0) {\n\t\t\t\t\t\tif (!have_madvise_huge) {\n\t\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t\t    \"No THP support\",\n\t\t\t\t\t\t\t    k, klen, v, vlen);\n\t\t\t\t\t\t}\n\t\t\t\t\t\topt_thp = i;\n\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!match) {\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\tmalloc_conf_error(\"Invalid conf pair\", k, klen, v,\n\t\t\t    vlen);\n#undef CONF_MATCH\n#undef CONF_MATCH_VALUE\n#undef CONF_HANDLE_BOOL\n#undef CONF_MIN_no\n#undef CONF_MIN_yes\n#undef CONF_MAX_no\n#undef CONF_MAX_yes\n#undef CONF_HANDLE_T_U\n#undef CONF_HANDLE_UNSIGNED\n#undef CONF_HANDLE_SIZE_T\n#undef CONF_HANDLE_SSIZE_T\n#undef CONF_HANDLE_CHAR_P\n\t\t}\n\t\tif (opt_abort_conf && had_conf_error) {\n\t\t\tmalloc_abort_invalid_conf();\n\t\t}\n\t}\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELEASE);\n}\n\nstatic bool\nmalloc_init_hard_needed(void) {\n\tif (malloc_initialized() || (IS_INITIALIZER && malloc_init_state ==\n\t    malloc_init_recursible)) {\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\treturn false;\n\t}\n#ifdef JEMALLOC_THREADED_INIT\n\tif (malloc_initializer != NO_INITIALIZER && !IS_INITIALIZER) {\n\t\t/* Busy-wait until the initializing thread completes. */\n\t\tspin_t spinner = SPIN_INITIALIZER;\n\t\tdo {\n\t\t\tmalloc_mutex_unlock(TSDN_NULL, &init_lock);\n\t\t\tspin_adaptive(&spinner);\n\t\t\tmalloc_mutex_lock(TSDN_NULL, &init_lock);\n\t\t} while (!malloc_initialized());\n\t\treturn false;\n\t}\n#endif\n\treturn true;\n}\n\nstatic bool\nmalloc_init_hard_a0_locked() {\n\tmalloc_initializer = INITIALIZER;\n\n\tif (config_prof) {\n\t\tprof_boot0();\n\t}\n\tmalloc_conf_init();\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\t}\n\t\t}\n\t}\n\tif (pages_boot()) {\n\t\treturn true;\n\t}\n\tif (base_boot(TSDN_NULL)) {\n\t\treturn true;\n\t}\n\tif (extent_boot()) {\n\t\treturn true;\n\t}\n\tif (ctl_boot()) {\n\t\treturn true;\n\t}\n\tif (config_prof) {\n\t\tprof_boot1();\n\t}\n\tarena_boot();\n\tif (tcache_boot(TSDN_NULL)) {\n\t\treturn true;\n\t}\n\tif (malloc_mutex_init(&arenas_lock, \"arenas\", WITNESS_RANK_ARENAS,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\t/*\n\t * Create enough scaffolding to allow recursive allocation in\n\t * malloc_ncpus().\n\t */\n\tnarenas_auto = 1;\n\tmemset(arenas, 0, sizeof(arena_t *) * narenas_auto);\n\t/*\n\t * Initialize one arena here.  The rest are lazily created in\n\t * arena_choose_hard().\n\t */\n\tif (arena_init(TSDN_NULL, 0, (extent_hooks_t *)&extent_hooks_default)\n\t    == NULL) {\n\t\treturn true;\n\t}\n\ta0 = arena_get(TSDN_NULL, 0, false);\n\tmalloc_init_state = malloc_init_a0_initialized;\n\n\treturn false;\n}\n\nstatic bool\nmalloc_init_hard_a0(void) {\n\tbool ret;\n\n\tmalloc_mutex_lock(TSDN_NULL, &init_lock);\n\tret = malloc_init_hard_a0_locked();\n\tmalloc_mutex_unlock(TSDN_NULL, &init_lock);\n\treturn ret;\n}\n\n/* Initialize data structures which may trigger recursive allocation. */\nstatic bool\nmalloc_init_hard_recursible(void) {\n\tmalloc_init_state = malloc_init_recursible;\n\n\tncpus = malloc_ncpus();\n\n#if (defined(JEMALLOC_HAVE_PTHREAD_ATFORK) && !defined(JEMALLOC_MUTEX_INIT_CB) \\\n    && !defined(JEMALLOC_ZONE) && !defined(_WIN32) && \\\n    !defined(__native_client__))\n\t/* LinuxThreads' pthread_atfork() allocates. */\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\t}\n\t\treturn true;\n\t}\n#endif\n\n\tif (background_thread_boot0()) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic unsigned\nmalloc_narenas_default(void) {\n\tassert(ncpus > 0);\n\t/*\n\t * For SMP systems, create more than one arena per CPU by\n\t * default.\n\t */\n\tif (ncpus > 1) {\n\t\treturn ncpus << 2;\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nstatic percpu_arena_mode_t\npercpu_arena_as_initialized(percpu_arena_mode_t mode) {\n\tassert(!malloc_initialized());\n\tassert(mode <= percpu_arena_disabled);\n\n\tif (mode != percpu_arena_disabled) {\n\t\tmode += percpu_arena_mode_enabled_base;\n\t}\n\n\treturn mode;\n}\n\nstatic bool\nmalloc_init_narenas(void) {\n\tassert(ncpus > 0);\n\n\tif (opt_percpu_arena != percpu_arena_disabled) {\n\t\tif (!have_percpu_arena || malloc_getcpu() < 0) {\n\t\t\topt_percpu_arena = percpu_arena_disabled;\n\t\t\tmalloc_printf(\"<jemalloc>: perCPU arena getcpu() not \"\n\t\t\t    \"available. Setting narenas to %u.\\n\", opt_narenas ?\n\t\t\t    opt_narenas : malloc_narenas_default());\n\t\t\tif (opt_abort) {\n\t\t\t\tabort();\n\t\t\t}\n\t\t} else {\n\t\t\tif (ncpus >= MALLOCX_ARENA_LIMIT) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: narenas w/ percpu\"\n\t\t\t\t    \"arena beyond limit (%d)\\n\", ncpus);\n\t\t\t\tif (opt_abort) {\n\t\t\t\t\tabort();\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t/* NB: opt_percpu_arena isn't fully initialized yet. */\n\t\t\tif (percpu_arena_as_initialized(opt_percpu_arena) ==\n\t\t\t    per_phycpu_arena && ncpus % 2 != 0) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: invalid \"\n\t\t\t\t    \"configuration -- per physical CPU arena \"\n\t\t\t\t    \"with odd number (%u) of CPUs (no hyper \"\n\t\t\t\t    \"threading?).\\n\", ncpus);\n\t\t\t\tif (opt_abort)\n\t\t\t\t\tabort();\n\t\t\t}\n\t\t\tunsigned n = percpu_arena_ind_limit(\n\t\t\t    percpu_arena_as_initialized(opt_percpu_arena));\n\t\t\tif (opt_narenas < n) {\n\t\t\t\t/*\n\t\t\t\t * If narenas is specified with percpu_arena\n\t\t\t\t * enabled, actual narenas is set as the greater\n\t\t\t\t * of the two. percpu_arena_choose will be free\n\t\t\t\t * to use any of the arenas based on CPU\n\t\t\t\t * id. This is conservative (at a small cost)\n\t\t\t\t * but ensures correctness.\n\t\t\t\t *\n\t\t\t\t * If for some reason the ncpus determined at\n\t\t\t\t * boot is not the actual number (e.g. because\n\t\t\t\t * of affinity setting from numactl), reserving\n\t\t\t\t * narenas this way provides a workaround for\n\t\t\t\t * percpu_arena.\n\t\t\t\t */\n\t\t\t\topt_narenas = n;\n\t\t\t}\n\t\t}\n\t}\n\tif (opt_narenas == 0) {\n\t\topt_narenas = malloc_narenas_default();\n\t}\n\tassert(opt_narenas > 0);\n\n\tnarenas_auto = opt_narenas;\n\t/*\n\t * Limit the number of arenas to the indexing range of MALLOCX_ARENA().\n\t */\n\tif (narenas_auto >= MALLOCX_ARENA_LIMIT) {\n\t\tnarenas_auto = MALLOCX_ARENA_LIMIT - 1;\n\t\tmalloc_printf(\"<jemalloc>: Reducing narenas to limit (%d)\\n\",\n\t\t    narenas_auto);\n\t}\n\tnarenas_total_set(narenas_auto);\n\n\treturn false;\n}\n\nstatic void\nmalloc_init_percpu(void) {\n\topt_percpu_arena = percpu_arena_as_initialized(opt_percpu_arena);\n}\n\nstatic bool\nmalloc_init_hard_finish(void) {\n\tif (malloc_mutex_boot()) {\n\t\treturn true;\n\t}\n\n\tmalloc_init_state = malloc_init_initialized;\n\tmalloc_slow_flag_init();\n\n\treturn false;\n}\n\nstatic void\nmalloc_init_hard_cleanup(tsdn_t *tsdn, bool reentrancy_set) {\n\tmalloc_mutex_assert_owner(tsdn, &init_lock);\n\tmalloc_mutex_unlock(tsdn, &init_lock);\n\tif (reentrancy_set) {\n\t\tassert(!tsdn_null(tsdn));\n\t\ttsd_t *tsd = tsdn_tsd(tsdn);\n\t\tassert(tsd_reentrancy_level_get(tsd) > 0);\n\t\tpost_reentrancy(tsd);\n\t}\n}\n\nstatic bool\nmalloc_init_hard(void) {\n\ttsd_t *tsd;\n\n#if defined(_WIN32) && _WIN32_WINNT < 0x0600\n\t_init_init_lock();\n#endif\n\tmalloc_mutex_lock(TSDN_NULL, &init_lock);\n\n#define UNLOCK_RETURN(tsdn, ret, reentrancy)\t\t\\\n\tmalloc_init_hard_cleanup(tsdn, reentrancy);\t\\\n\treturn ret;\n\n\tif (!malloc_init_hard_needed()) {\n\t\tUNLOCK_RETURN(TSDN_NULL, false, false)\n\t}\n\n\tif (malloc_init_state != malloc_init_a0_initialized &&\n\t    malloc_init_hard_a0_locked()) {\n\t\tUNLOCK_RETURN(TSDN_NULL, true, false)\n\t}\n\n\tmalloc_mutex_unlock(TSDN_NULL, &init_lock);\n\t/* Recursive allocation relies on functional tsd. */\n\ttsd = malloc_tsd_boot0();\n\tif (tsd == NULL) {\n\t\treturn true;\n\t}\n\tif (malloc_init_hard_recursible()) {\n\t\treturn true;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &init_lock);\n\t/* Set reentrancy level to 1 during init. */\n\tpre_reentrancy(tsd, NULL);\n\t/* Initialize narenas before prof_boot2 (for allocation). */\n\tif (malloc_init_narenas() || background_thread_boot1(tsd_tsdn(tsd))) {\n\t\tUNLOCK_RETURN(tsd_tsdn(tsd), true, true)\n\t}\n\tif (config_prof && prof_boot2(tsd)) {\n\t\tUNLOCK_RETURN(tsd_tsdn(tsd), true, true)\n\t}\n\n\tmalloc_init_percpu();\n\n\tif (malloc_init_hard_finish()) {\n\t\tUNLOCK_RETURN(tsd_tsdn(tsd), true, true)\n\t}\n\tpost_reentrancy(tsd);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &init_lock);\n\n\twitness_assert_lockless(witness_tsd_tsdn(\n\t    tsd_witness_tsdp_get_unsafe(tsd)));\n\tmalloc_tsd_boot1();\n\t/* Update TSD after tsd_boot1. */\n\ttsd = tsd_fetch();\n\tif (opt_background_thread) {\n\t\tassert(have_background_thread);\n\t\t/*\n\t\t * Need to finish init & unlock first before creating background\n\t\t * threads (pthread_create depends on malloc).  ctl_init (which\n\t\t * sets isthreaded) needs to be called without holding any lock.\n\t\t */\n\t\tbackground_thread_ctl_init(tsd_tsdn(tsd));\n\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\t\tbool err = background_thread_create(tsd, 0);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\t\tif (err) {\n\t\t\treturn true;\n\t\t}\n\t}\n#undef UNLOCK_RETURN\n\treturn false;\n}\n\n/*\n * End initialization functions.\n */\n/******************************************************************************/\n/*\n * Begin allocation-path internal functions and data structures.\n */\n\n/*\n * Settings determined by the documented behavior of the allocation functions.\n */\ntypedef struct static_opts_s static_opts_t;\nstruct static_opts_s {\n\t/* Whether or not allocation size may overflow. */\n\tbool may_overflow;\n\t/* Whether or not allocations of size 0 should be treated as size 1. */\n\tbool bump_empty_alloc;\n\t/*\n\t * Whether to assert that allocations are not of size 0 (after any\n\t * bumping).\n\t */\n\tbool assert_nonempty_alloc;\n\n\t/*\n\t * Whether or not to modify the 'result' argument to malloc in case of\n\t * error.\n\t */\n\tbool null_out_result_on_error;\n\t/* Whether to set errno when we encounter an error condition. */\n\tbool set_errno_on_error;\n\n\t/*\n\t * The minimum valid alignment for functions requesting aligned storage.\n\t */\n\tsize_t min_alignment;\n\n\t/* The error string to use if we oom. */\n\tconst char *oom_string;\n\t/* The error string to use if the passed-in alignment is invalid. */\n\tconst char *invalid_alignment_string;\n\n\t/*\n\t * False if we're configured to skip some time-consuming operations.\n\t *\n\t * This isn't really a malloc \"behavior\", but it acts as a useful\n\t * summary of several other static (or at least, static after program\n\t * initialization) options.\n\t */\n\tbool slow;\n};\n\nJEMALLOC_ALWAYS_INLINE void\nstatic_opts_init(static_opts_t *static_opts) {\n\tstatic_opts->may_overflow = false;\n\tstatic_opts->bump_empty_alloc = false;\n\tstatic_opts->assert_nonempty_alloc = false;\n\tstatic_opts->null_out_result_on_error = false;\n\tstatic_opts->set_errno_on_error = false;\n\tstatic_opts->min_alignment = 0;\n\tstatic_opts->oom_string = \"\";\n\tstatic_opts->invalid_alignment_string = \"\";\n\tstatic_opts->slow = false;\n}\n\n/*\n * These correspond to the macros in jemalloc/jemalloc_macros.h.  Broadly, we\n * should have one constant here per magic value there.  Note however that the\n * representations need not be related.\n */\n#define TCACHE_IND_NONE ((unsigned)-1)\n#define TCACHE_IND_AUTOMATIC ((unsigned)-2)\n#define ARENA_IND_AUTOMATIC ((unsigned)-1)\n\ntypedef struct dynamic_opts_s dynamic_opts_t;\nstruct dynamic_opts_s {\n\tvoid **result;\n\tsize_t num_items;\n\tsize_t item_size;\n\tsize_t alignment;\n\tbool zero;\n\tunsigned tcache_ind;\n\tunsigned arena_ind;\n};\n\nJEMALLOC_ALWAYS_INLINE void\ndynamic_opts_init(dynamic_opts_t *dynamic_opts) {\n\tdynamic_opts->result = NULL;\n\tdynamic_opts->num_items = 0;\n\tdynamic_opts->item_size = 0;\n\tdynamic_opts->alignment = 0;\n\tdynamic_opts->zero = false;\n\tdynamic_opts->tcache_ind = TCACHE_IND_AUTOMATIC;\n\tdynamic_opts->arena_ind = ARENA_IND_AUTOMATIC;\n}\n\n/* ind is ignored if dopts->alignment > 0. */\nJEMALLOC_ALWAYS_INLINE void *\nimalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,\n    size_t size, size_t usize, szind_t ind) {\n\ttcache_t *tcache;\n\tarena_t *arena;\n\n\t/* Fill in the tcache. */\n\tif (dopts->tcache_ind == TCACHE_IND_AUTOMATIC) {\n\t\tif (likely(!sopts->slow)) {\n\t\t\t/* Getting tcache ptr unconditionally. */\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tassert(tcache == tcache_get(tsd));\n\t\t} else {\n\t\t\ttcache = tcache_get(tsd);\n\t\t}\n\t} else if (dopts->tcache_ind == TCACHE_IND_NONE) {\n\t\ttcache = NULL;\n\t} else {\n\t\ttcache = tcaches_get(tsd, dopts->tcache_ind);\n\t}\n\n\t/* Fill in the arena. */\n\tif (dopts->arena_ind == ARENA_IND_AUTOMATIC) {\n\t\t/*\n\t\t * In case of automatic arena management, we defer arena\n\t\t * computation until as late as we can, hoping to fill the\n\t\t * allocation out of the tcache.\n\t\t */\n\t\tarena = NULL;\n\t} else {\n\t\tarena = arena_get(tsd_tsdn(tsd), dopts->arena_ind, true);\n\t}\n\n\tif (unlikely(dopts->alignment != 0)) {\n\t\treturn ipalloct(tsd_tsdn(tsd), usize, dopts->alignment,\n\t\t    dopts->zero, tcache, arena);\n\t}\n\n\treturn iallocztm(tsd_tsdn(tsd), size, ind, dopts->zero, tcache, false,\n\t    arena, sopts->slow);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nimalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,\n    size_t usize, szind_t ind) {\n\tvoid *ret;\n\n\t/*\n\t * For small allocations, sampling bumps the usize.  If so, we allocate\n\t * from the ind_large bucket.\n\t */\n\tszind_t ind_large;\n\tsize_t bumped_usize = usize;\n\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tassert(((dopts->alignment == 0) ? sz_s2u(LARGE_MINCLASS) :\n\t\t    sz_sa2u(LARGE_MINCLASS, dopts->alignment))\n\t\t    == LARGE_MINCLASS);\n\t\tind_large = sz_size2index(LARGE_MINCLASS);\n\t\tbumped_usize = sz_s2u(LARGE_MINCLASS);\n\t\tret = imalloc_no_sample(sopts, dopts, tsd, bumped_usize,\n\t\t    bumped_usize, ind_large);\n\t\tif (unlikely(ret == NULL)) {\n\t\t\treturn NULL;\n\t\t}\n\t\tarena_prof_promote(tsd_tsdn(tsd), ret, usize);\n\t} else {\n\t\tret = imalloc_no_sample(sopts, dopts, tsd, usize, usize, ind);\n\t}\n\n\treturn ret;\n}\n\n/*\n * Returns true if the allocation will overflow, and false otherwise.  Sets\n * *size to the product either way.\n */\nJEMALLOC_ALWAYS_INLINE bool\ncompute_size_with_overflow(bool may_overflow, dynamic_opts_t *dopts,\n    size_t *size) {\n\t/*\n\t * This function is just num_items * item_size, except that we may have\n\t * to check for overflow.\n\t */\n\n\tif (!may_overflow) {\n\t\tassert(dopts->num_items == 1);\n\t\t*size = dopts->item_size;\n\t\treturn false;\n\t}\n\n\t/* A size_t with its high-half bits all set to 1. */\n\tstatic const size_t high_bits = SIZE_T_MAX << (sizeof(size_t) * 8 / 2);\n\n\t*size = dopts->item_size * dopts->num_items;\n\n\tif (unlikely(*size == 0)) {\n\t\treturn (dopts->num_items != 0 && dopts->item_size != 0);\n\t}\n\n\t/*\n\t * We got a non-zero size, but we don't know if we overflowed to get\n\t * there.  To avoid having to do a divide, we'll be clever and note that\n\t * if both A and B can be represented in N/2 bits, then their product\n\t * can be represented in N bits (without the possibility of overflow).\n\t */\n\tif (likely((high_bits & (dopts->num_items | dopts->item_size)) == 0)) {\n\t\treturn false;\n\t}\n\tif (likely(*size / dopts->item_size == dopts->num_items)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nJEMALLOC_ALWAYS_INLINE int\nimalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {\n\t/* Where the actual allocated memory will live. */\n\tvoid *allocation = NULL;\n\t/* Filled in by compute_size_with_overflow below. */\n\tsize_t size = 0;\n\t/*\n\t * For unaligned allocations, we need only ind.  For aligned\n\t * allocations, or in case of stats or profiling we need usize.\n\t *\n\t * These are actually dead stores, in that their values are reset before\n\t * any branch on their value is taken.  Sometimes though, it's\n\t * convenient to pass them as arguments before this point.  To avoid\n\t * undefined behavior then, we initialize them with dummy stores.\n\t */\n\tszind_t ind = 0;\n\tsize_t usize = 0;\n\n\t/* Reentrancy is only checked on slow path. */\n\tint8_t reentrancy_level;\n\n\t/* Compute the amount of memory the user wants. */\n\tif (unlikely(compute_size_with_overflow(sopts->may_overflow, dopts,\n\t    &size))) {\n\t\tgoto label_oom;\n\t}\n\n\t/* Validate the user input. */\n\tif (sopts->bump_empty_alloc) {\n\t\tif (unlikely(size == 0)) {\n\t\t\tsize = 1;\n\t\t}\n\t}\n\n\tif (sopts->assert_nonempty_alloc) {\n\t\tassert (size != 0);\n\t}\n\n\tif (unlikely(dopts->alignment < sopts->min_alignment\n\t    || (dopts->alignment & (dopts->alignment - 1)) != 0)) {\n\t\tgoto label_invalid_alignment;\n\t}\n\n\t/* This is the beginning of the \"core\" algorithm. */\n\n\tif (dopts->alignment == 0) {\n\t\tind = sz_size2index(size);\n\t\tif (unlikely(ind >= NSIZES)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t\tif (config_stats || (config_prof && opt_prof)) {\n\t\t\tusize = sz_index2size(ind);\n\t\t\tassert(usize > 0 && usize <= LARGE_MAXCLASS);\n\t\t}\n\t} else {\n\t\tusize = sz_sa2u(size, dopts->alignment);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t}\n\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\t/*\n\t * If we need to handle reentrancy, we can do it out of a\n\t * known-initialized arena (i.e. arena 0).\n\t */\n\treentrancy_level = tsd_reentrancy_level_get(tsd);\n\tif (sopts->slow && unlikely(reentrancy_level > 0)) {\n\t\t/*\n\t\t * We should never specify particular arenas or tcaches from\n\t\t * within our internal allocations.\n\t\t */\n\t\tassert(dopts->tcache_ind == TCACHE_IND_AUTOMATIC ||\n\t\t    dopts->tcache_ind == TCACHE_IND_NONE);\n\t\tassert(dopts->arena_ind == ARENA_IND_AUTOMATIC);\n\t\tdopts->tcache_ind = TCACHE_IND_NONE;\n\t\t/* We know that arena 0 has already been initialized. */\n\t\tdopts->arena_ind = 0;\n\t}\n\n\t/* If profiling is on, get our profiling context. */\n\tif (config_prof && opt_prof) {\n\t\t/*\n\t\t * Note that if we're going down this path, usize must have been\n\t\t * initialized in the previous if statement.\n\t\t */\n\t\tprof_tctx_t *tctx = prof_alloc_prep(\n\t\t    tsd, usize, prof_active_get_unlocked(), true);\n\n\t\talloc_ctx_t alloc_ctx;\n\t\tif (likely((uintptr_t)tctx == (uintptr_t)1U)) {\n\t\t\talloc_ctx.slab = (usize <= SMALL_MAXCLASS);\n\t\t\tallocation = imalloc_no_sample(\n\t\t\t    sopts, dopts, tsd, usize, usize, ind);\n\t\t} else if ((uintptr_t)tctx > (uintptr_t)1U) {\n\t\t\t/*\n\t\t\t * Note that ind might still be 0 here.  This is fine;\n\t\t\t * imalloc_sample ignores ind if dopts->alignment > 0.\n\t\t\t */\n\t\t\tallocation = imalloc_sample(\n\t\t\t    sopts, dopts, tsd, usize, ind);\n\t\t\talloc_ctx.slab = false;\n\t\t} else {\n\t\t\tallocation = NULL;\n\t\t}\n\n\t\tif (unlikely(allocation == NULL)) {\n\t\t\tprof_alloc_rollback(tsd, tctx, true);\n\t\t\tgoto label_oom;\n\t\t}\n\t\tprof_malloc(tsd_tsdn(tsd), allocation, usize, &alloc_ctx, tctx);\n\t} else {\n\t\t/*\n\t\t * If dopts->alignment > 0, then ind is still 0, but usize was\n\t\t * computed in the previous if statement.  Down the positive\n\t\t * alignment path, imalloc_no_sample ignores ind and size\n\t\t * (relying only on usize).\n\t\t */\n\t\tallocation = imalloc_no_sample(sopts, dopts, tsd, size, usize,\n\t\t    ind);\n\t\tif (unlikely(allocation == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t}\n\n\t/*\n\t * Allocation has been done at this point.  We still have some\n\t * post-allocation work to do though.\n\t */\n\tassert(dopts->alignment == 0\n\t    || ((uintptr_t)allocation & (dopts->alignment - 1)) == ZU(0));\n\n\tif (config_stats) {\n\t\tassert(usize == isalloc(tsd_tsdn(tsd), allocation));\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t}\n\n\tif (sopts->slow) {\n\t\tUTRACE(0, size, allocation);\n\t}\n\n\t/* Success! */\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\t*dopts->result = allocation;\n\treturn 0;\n\nlabel_oom:\n\tif (unlikely(sopts->slow) && config_xmalloc && unlikely(opt_xmalloc)) {\n\t\tmalloc_write(sopts->oom_string);\n\t\tabort();\n\t}\n\n\tif (sopts->slow) {\n\t\tUTRACE(NULL, size, NULL);\n\t}\n\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tif (sopts->set_errno_on_error) {\n\t\tset_errno(ENOMEM);\n\t}\n\n\tif (sopts->null_out_result_on_error) {\n\t\t*dopts->result = NULL;\n\t}\n\n\treturn ENOMEM;\n\n\t/*\n\t * This label is only jumped to by one goto; we move it out of line\n\t * anyways to avoid obscuring the non-error paths, and for symmetry with\n\t * the oom case.\n\t */\nlabel_invalid_alignment:\n\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\tmalloc_write(sopts->invalid_alignment_string);\n\t\tabort();\n\t}\n\n\tif (sopts->set_errno_on_error) {\n\t\tset_errno(EINVAL);\n\t}\n\n\tif (sopts->slow) {\n\t\tUTRACE(NULL, size, NULL);\n\t}\n\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tif (sopts->null_out_result_on_error) {\n\t\t*dopts->result = NULL;\n\t}\n\n\treturn EINVAL;\n}\n\n/* Returns the errno-style error code of the allocation. */\nJEMALLOC_ALWAYS_INLINE int\nimalloc(static_opts_t *sopts, dynamic_opts_t *dopts) {\n\tif (unlikely(!malloc_initialized()) && unlikely(malloc_init())) {\n\t\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\t\tmalloc_write(sopts->oom_string);\n\t\t\tabort();\n\t\t}\n\t\tUTRACE(NULL, dopts->num_items * dopts->item_size, NULL);\n\t\tset_errno(ENOMEM);\n\t\t*dopts->result = NULL;\n\n\t\treturn ENOMEM;\n\t}\n\n\t/* We always need the tsd.  Let's grab it right away. */\n\ttsd_t *tsd = tsd_fetch();\n\tassert(tsd);\n\tif (likely(tsd_fast(tsd))) {\n\t\t/* Fast and common path. */\n\t\ttsd_assert_fast(tsd);\n\t\tsopts->slow = false;\n\t\treturn imalloc_body(sopts, dopts, tsd);\n\t} else {\n\t\tsopts->slow = true;\n\t\treturn imalloc_body(sopts, dopts, tsd);\n\t}\n}\n/******************************************************************************/\n/*\n * Begin malloc(3)-compatible functions.\n */\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)\nje_malloc(size_t size) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.malloc.entry\", \"size: %zu\", size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.set_errno_on_error = true;\n\tsopts.oom_string = \"<jemalloc>: Error in malloc(): out of memory\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.malloc.exit\", \"result: %p\", ret);\n\n\treturn ret;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nJEMALLOC_ATTR(nonnull(1))\nje_posix_memalign(void **memptr, size_t alignment, size_t size) {\n\tint ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.posix_memalign.entry\", \"mem ptr: %p, alignment: %zu, \"\n\t    \"size: %zu\", memptr, alignment, size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.min_alignment = sizeof(void *);\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\n\tdopts.result = memptr;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = alignment;\n\n\tret = imalloc(&sopts, &dopts);\n\n\tLOG(\"core.posix_memalign.exit\", \"result: %d, alloc ptr: %p\", ret,\n\t    *memptr);\n\n\treturn ret;\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2)\nje_aligned_alloc(size_t alignment, size_t size) {\n\tvoid *ret;\n\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.aligned_alloc.entry\", \"alignment: %zu, size: %zu\\n\",\n\t    alignment, size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.set_errno_on_error = true;\n\tsopts.min_alignment = 1;\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = alignment;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.aligned_alloc.exit\", \"result: %p\", ret);\n\n\treturn ret;\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2)\nje_calloc(size_t num, size_t size) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.calloc.entry\", \"num: %zu, size: %zu\\n\", num, size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.may_overflow = true;\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.set_errno_on_error = true;\n\tsopts.oom_string = \"<jemalloc>: Error in calloc(): out of memory\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = num;\n\tdopts.item_size = size;\n\tdopts.zero = true;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.calloc.exit\", \"result: %p\", ret);\n\n\treturn ret;\n}\n\nstatic void *\nirealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,\n    prof_tctx_t *tctx) {\n\tvoid *p;\n\n\tif (tctx == NULL) {\n\t\treturn NULL;\n\t}\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tp = iralloc(tsd, old_ptr, old_usize, LARGE_MINCLASS, 0, false);\n\t\tif (p == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tarena_prof_promote(tsd_tsdn(tsd), p, usize);\n\t} else {\n\t\tp = iralloc(tsd, old_ptr, old_usize, usize, 0, false);\n\t}\n\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nirealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,\n   alloc_ctx_t *alloc_ctx) {\n\tvoid *p;\n\tbool prof_active;\n\tprof_tctx_t *old_tctx, *tctx;\n\n\tprof_active = prof_active_get_unlocked();\n\told_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);\n\ttctx = prof_alloc_prep(tsd, usize, prof_active, true);\n\tif (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {\n\t\tp = irealloc_prof_sample(tsd, old_ptr, old_usize, usize, tctx);\n\t} else {\n\t\tp = iralloc(tsd, old_ptr, old_usize, usize, 0, false);\n\t}\n\tif (unlikely(p == NULL)) {\n\t\tprof_alloc_rollback(tsd, tctx, true);\n\t\treturn NULL;\n\t}\n\tprof_realloc(tsd, p, usize, tctx, prof_active, true, old_ptr, old_usize,\n\t    old_tctx);\n\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void\nifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) {\n\tif (!slow_path) {\n\t\ttsd_assert_fast(tsd);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tif (tsd_reentrancy_level_get(tsd) != 0) {\n\t\tassert(slow_path);\n\t}\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\talloc_ctx_t alloc_ctx;\n\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\tassert(alloc_ctx.szind != NSIZES);\n\n\tsize_t usize;\n\tif (config_prof && opt_prof) {\n\t\tusize = sz_index2size(alloc_ctx.szind);\n\t\tprof_free(tsd, ptr, usize, &alloc_ctx);\n\t} else if (config_stats) {\n\t\tusize = sz_index2size(alloc_ctx.szind);\n\t}\n\tif (config_stats) {\n\t\t*tsd_thread_deallocatedp_get(tsd) += usize;\n\t}\n\n\tif (likely(!slow_path)) {\n\t\tidalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,\n\t\t    false);\n\t} else {\n\t\tidalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,\n\t\t    true);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\nisfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache, bool slow_path) {\n\tif (!slow_path) {\n\t\ttsd_assert_fast(tsd);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tif (tsd_reentrancy_level_get(tsd) != 0) {\n\t\tassert(slow_path);\n\t}\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\talloc_ctx_t alloc_ctx, *ctx;\n\tif (!config_cache_oblivious && ((uintptr_t)ptr & PAGE_MASK) != 0) {\n\t\t/*\n\t\t * When cache_oblivious is disabled and ptr is not page aligned,\n\t\t * the allocation was not sampled -- usize can be used to\n\t\t * determine szind directly.\n\t\t */\n\t\talloc_ctx.szind = sz_size2index(usize);\n\t\talloc_ctx.slab = true;\n\t\tctx = &alloc_ctx;\n\t\tif (config_debug) {\n\t\t\talloc_ctx_t dbg_ctx;\n\t\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree,\n\t\t\t    rtree_ctx, (uintptr_t)ptr, true, &dbg_ctx.szind,\n\t\t\t    &dbg_ctx.slab);\n\t\t\tassert(dbg_ctx.szind == alloc_ctx.szind);\n\t\t\tassert(dbg_ctx.slab == alloc_ctx.slab);\n\t\t}\n\t} else if (config_prof && opt_prof) {\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\t\tassert(alloc_ctx.szind == sz_size2index(usize));\n\t\tctx = &alloc_ctx;\n\t} else {\n\t\tctx = NULL;\n\t}\n\n\tif (config_prof && opt_prof) {\n\t\tprof_free(tsd, ptr, usize, ctx);\n\t}\n\tif (config_stats) {\n\t\t*tsd_thread_deallocatedp_get(tsd) += usize;\n\t}\n\n\tif (likely(!slow_path)) {\n\t\tisdalloct(tsd_tsdn(tsd), ptr, usize, tcache, ctx, false);\n\t} else {\n\t\tisdalloct(tsd_tsdn(tsd), ptr, usize, tcache, ctx, true);\n\t}\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ALLOC_SIZE(2)\nje_realloc(void *ptr, size_t size) {\n\tvoid *ret;\n\ttsdn_t *tsdn JEMALLOC_CC_SILENCE_INIT(NULL);\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\tsize_t old_usize = 0;\n\n\tLOG(\"core.realloc.entry\", \"ptr: %p, size: %zu\\n\", ptr, size);\n\n\tif (unlikely(size == 0)) {\n\t\tif (ptr != NULL) {\n\t\t\t/* realloc(ptr, 0) is equivalent to free(ptr). */\n\t\t\tUTRACE(ptr, 0, 0);\n\t\t\ttcache_t *tcache;\n\t\t\ttsd_t *tsd = tsd_fetch();\n\t\t\tif (tsd_reentrancy_level_get(tsd) == 0) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t} else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t\tifree(tsd, ptr, tcache, true);\n\n\t\t\tLOG(\"core.realloc.exit\", \"result: %p\", NULL);\n\t\t\treturn NULL;\n\t\t}\n\t\tsize = 1;\n\t}\n\n\tif (likely(ptr != NULL)) {\n\t\tassert(malloc_initialized() || IS_INITIALIZER);\n\t\ttsd_t *tsd = tsd_fetch();\n\n\t\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\t\talloc_ctx_t alloc_ctx;\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\t\tassert(alloc_ctx.szind != NSIZES);\n\t\told_usize = sz_index2size(alloc_ctx.szind);\n\t\tassert(old_usize == isalloc(tsd_tsdn(tsd), ptr));\n\t\tif (config_prof && opt_prof) {\n\t\t\tusize = sz_s2u(size);\n\t\t\tret = unlikely(usize == 0 || usize > LARGE_MAXCLASS) ?\n\t\t\t    NULL : irealloc_prof(tsd, ptr, old_usize, usize,\n\t\t\t    &alloc_ctx);\n\t\t} else {\n\t\t\tif (config_stats) {\n\t\t\t\tusize = sz_s2u(size);\n\t\t\t}\n\t\t\tret = iralloc(tsd, ptr, old_usize, size, 0, false);\n\t\t}\n\t\ttsdn = tsd_tsdn(tsd);\n\t} else {\n\t\t/* realloc(NULL, size) is equivalent to malloc(size). */\n\t\tvoid *ret = je_malloc(size);\n\t\tLOG(\"core.realloc.exit\", \"result: %p\", ret);\n\t\treturn ret;\n\t}\n\n\tif (unlikely(ret == NULL)) {\n\t\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in realloc(): \"\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_stats && likely(ret != NULL)) {\n\t\ttsd_t *tsd;\n\n\t\tassert(usize == isalloc(tsdn, ret));\n\t\ttsd = tsdn_tsd(tsdn);\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t\t*tsd_thread_deallocatedp_get(tsd) += old_usize;\n\t}\n\tUTRACE(ptr, size, ret);\n\tcheck_entry_exit_locking(tsdn);\n\n\tLOG(\"core.realloc.exit\", \"result: %p\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_free(void *ptr) {\n\tLOG(\"core.free.entry\", \"ptr: %p\", ptr);\n\n\tUTRACE(ptr, 0, 0);\n\tif (likely(ptr != NULL)) {\n\t\t/*\n\t\t * We avoid setting up tsd fully (e.g. tcache, arena binding)\n\t\t * based on only free() calls -- other activities trigger the\n\t\t * minimal to full transition.  This is because free() may\n\t\t * happen during thread shutdown after tls deallocation: if a\n\t\t * thread never had any malloc activities until then, a\n\t\t * fully-setup tsd won't be destructed properly.\n\t\t */\n\t\ttsd_t *tsd = tsd_fetch_min();\n\t\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\t\ttcache_t *tcache;\n\t\tif (likely(tsd_fast(tsd))) {\n\t\t\ttsd_assert_fast(tsd);\n\t\t\t/* Unconditionally get tcache ptr on fast path. */\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tifree(tsd, ptr, tcache, false);\n\t\t} else {\n\t\t\tif (likely(tsd_reentrancy_level_get(tsd) == 0)) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t} else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t\tifree(tsd, ptr, tcache, true);\n\t\t}\n\t\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\t}\n\tLOG(\"core.free.exit\", \"\");\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\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc)\nje_memalign(size_t alignment, size_t size) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.memalign.entry\", \"alignment: %zu, size: %zu\\n\", alignment,\n\t    size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.min_alignment = 1;\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\tsopts.null_out_result_on_error = true;\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = alignment;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.memalign.exit\", \"result: %p\", ret);\n\treturn ret;\n}\n#endif\n\n#ifdef JEMALLOC_OVERRIDE_VALLOC\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc)\nje_valloc(size_t size) {\n\tvoid *ret;\n\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.valloc.entry\", \"size: %zu\\n\", size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.min_alignment = PAGE;\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = PAGE;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.valloc.exit\", \"result: %p\\n\", ret);\n\treturn ret;\n}\n#endif\n\n#if defined(JEMALLOC_IS_MALLOC) && defined(JEMALLOC_GLIBC_MALLOC_HOOK)\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;\n#  ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK\nJEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =\n    je_memalign;\n#  endif\n\n#  ifdef CPU_COUNT\n/*\n * To enable static linking with glibc, the libc specific malloc interface must\n * be implemented also, so none of glibc's malloc.o functions are added to the\n * link.\n */\n#    define ALIAS(je_fn)\t__attribute__((alias (#je_fn), used))\n/* To force macro expansion of je_ prefix before stringification. */\n#    define PREALIAS(je_fn)\tALIAS(je_fn)\n#    ifdef JEMALLOC_OVERRIDE___LIBC_CALLOC\nvoid *__libc_calloc(size_t n, size_t size) PREALIAS(je_calloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_FREE\nvoid __libc_free(void* ptr) PREALIAS(je_free);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_MALLOC\nvoid *__libc_malloc(size_t size) PREALIAS(je_malloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_MEMALIGN\nvoid *__libc_memalign(size_t align, size_t s) PREALIAS(je_memalign);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_REALLOC\nvoid *__libc_realloc(void* ptr, size_t size) PREALIAS(je_realloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_VALLOC\nvoid *__libc_valloc(size_t size) PREALIAS(je_valloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___POSIX_MEMALIGN\nint __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);\n#    endif\n#    undef PREALIAS\n#    undef ALIAS\n#  endif\n#endif\n\n/*\n * End non-standard override functions.\n */\n/******************************************************************************/\n/*\n * Begin non-standard functions.\n */\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)\nje_mallocx(size_t size, int flags) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.mallocx.entry\", \"size: %zu, flags: %d\", size, flags);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.assert_nonempty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.oom_string = \"<jemalloc>: Error in mallocx(): out of memory\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tif (unlikely(flags != 0)) {\n\t\tif ((flags & MALLOCX_LG_ALIGN_MASK) != 0) {\n\t\t\tdopts.alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags);\n\t\t}\n\n\t\tdopts.zero = MALLOCX_ZERO_GET(flags);\n\n\t\tif ((flags & MALLOCX_TCACHE_MASK) != 0) {\n\t\t\tif ((flags & MALLOCX_TCACHE_MASK)\n\t\t\t    == MALLOCX_TCACHE_NONE) {\n\t\t\t\tdopts.tcache_ind = TCACHE_IND_NONE;\n\t\t\t} else {\n\t\t\t\tdopts.tcache_ind = MALLOCX_TCACHE_GET(flags);\n\t\t\t}\n\t\t} else {\n\t\t\tdopts.tcache_ind = TCACHE_IND_AUTOMATIC;\n\t\t}\n\n\t\tif ((flags & MALLOCX_ARENA_MASK) != 0)\n\t\t\tdopts.arena_ind = MALLOCX_ARENA_GET(flags);\n\t}\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.mallocx.exit\", \"result: %p\", ret);\n\treturn ret;\n}\n\nstatic void *\nirallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,\n    size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,\n    prof_tctx_t *tctx) {\n\tvoid *p;\n\n\tif (tctx == NULL) {\n\t\treturn NULL;\n\t}\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tp = iralloct(tsdn, old_ptr, old_usize, LARGE_MINCLASS,\n\t\t    alignment, zero, tcache, arena);\n\t\tif (p == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tarena_prof_promote(tsdn, p, usize);\n\t} else {\n\t\tp = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,\n\t\t    tcache, arena);\n\t}\n\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nirallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,\n    size_t alignment, size_t *usize, bool zero, tcache_t *tcache,\n    arena_t *arena, alloc_ctx_t *alloc_ctx) {\n\tvoid *p;\n\tbool prof_active;\n\tprof_tctx_t *old_tctx, *tctx;\n\n\tprof_active = prof_active_get_unlocked();\n\told_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);\n\ttctx = prof_alloc_prep(tsd, *usize, prof_active, false);\n\tif (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {\n\t\tp = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,\n\t\t    *usize, alignment, zero, tcache, arena, tctx);\n\t} else {\n\t\tp = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,\n\t\t    zero, tcache, arena);\n\t}\n\tif (unlikely(p == NULL)) {\n\t\tprof_alloc_rollback(tsd, tctx, false);\n\t\treturn NULL;\n\t}\n\n\tif (p == old_ptr && alignment != 0) {\n\t\t/*\n\t\t * The allocation did not move, so it is possible that the size\n\t\t * class is smaller than would guarantee the requested\n\t\t * alignment, and that the alignment constraint was\n\t\t * serendipitously satisfied.  Additionally, old_usize may not\n\t\t * be the same as the current usize because of in-place large\n\t\t * reallocation.  Therefore, query the actual value of usize.\n\t\t */\n\t\t*usize = isalloc(tsd_tsdn(tsd), p);\n\t}\n\tprof_realloc(tsd, p, *usize, tctx, prof_active, false, old_ptr,\n\t    old_usize, old_tctx);\n\n\treturn p;\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ALLOC_SIZE(2)\nje_rallocx(void *ptr, size_t size, int flags) {\n\tvoid *p;\n\ttsd_t *tsd;\n\tsize_t usize;\n\tsize_t old_usize;\n\tsize_t alignment = MALLOCX_ALIGN_GET(flags);\n\tbool zero = flags & MALLOCX_ZERO;\n\tarena_t *arena;\n\ttcache_t *tcache;\n\n\tLOG(\"core.rallocx.entry\", \"ptr: %p, size: %zu, flags: %d\", ptr,\n\t    size, flags);\n\n\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tif (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {\n\t\tunsigned arena_ind = MALLOCX_ARENA_GET(flags);\n\t\tarena = arena_get(tsd_tsdn(tsd), arena_ind, true);\n\t\tif (unlikely(arena == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t} else {\n\t\tarena = NULL;\n\t}\n\n\tif (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {\n\t\tif ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {\n\t\t\ttcache = NULL;\n\t\t} else {\n\t\t\ttcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));\n\t\t}\n\t} else {\n\t\ttcache = tcache_get(tsd);\n\t}\n\n\talloc_ctx_t alloc_ctx;\n\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\tassert(alloc_ctx.szind != NSIZES);\n\told_usize = sz_index2size(alloc_ctx.szind);\n\tassert(old_usize == isalloc(tsd_tsdn(tsd), ptr));\n\tif (config_prof && opt_prof) {\n\t\tusize = (alignment == 0) ?\n\t\t    sz_s2u(size) : sz_sa2u(size, alignment);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t\tp = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize,\n\t\t    zero, tcache, arena, &alloc_ctx);\n\t\tif (unlikely(p == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t} else {\n\t\tp = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment,\n\t\t    zero, tcache, arena);\n\t\tif (unlikely(p == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t\tif (config_stats) {\n\t\t\tusize = isalloc(tsd_tsdn(tsd), p);\n\t\t}\n\t}\n\tassert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));\n\n\tif (config_stats) {\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t\t*tsd_thread_deallocatedp_get(tsd) += old_usize;\n\t}\n\tUTRACE(ptr, size, p);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.rallocx.exit\", \"result: %p\", p);\n\treturn p;\nlabel_oom:\n\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\tmalloc_write(\"<jemalloc>: Error in rallocx(): out of memory\\n\");\n\t\tabort();\n\t}\n\tUTRACE(ptr, size, 0);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.rallocx.exit\", \"result: %p\", NULL);\n\treturn NULL;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,\n    size_t extra, size_t alignment, bool zero) {\n\tsize_t usize;\n\n\tif (ixalloc(tsdn, ptr, old_usize, size, extra, alignment, zero)) {\n\t\treturn old_usize;\n\t}\n\tusize = isalloc(tsdn, ptr);\n\n\treturn usize;\n}\n\nstatic size_t\nixallocx_prof_sample(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,\n    size_t extra, size_t alignment, bool zero, prof_tctx_t *tctx) {\n\tsize_t usize;\n\n\tif (tctx == NULL) {\n\t\treturn old_usize;\n\t}\n\tusize = ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment,\n\t    zero);\n\n\treturn usize;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,\n    size_t extra, size_t alignment, bool zero, alloc_ctx_t *alloc_ctx) {\n\tsize_t usize_max, usize;\n\tbool prof_active;\n\tprof_tctx_t *old_tctx, *tctx;\n\n\tprof_active = prof_active_get_unlocked();\n\told_tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);\n\t/*\n\t * usize isn't knowable before ixalloc() returns when extra is non-zero.\n\t * Therefore, compute its maximum possible value and use that in\n\t * prof_alloc_prep() to decide whether to capture a backtrace.\n\t * prof_realloc() will use the actual usize to decide whether to sample.\n\t */\n\tif (alignment == 0) {\n\t\tusize_max = sz_s2u(size+extra);\n\t\tassert(usize_max > 0 && usize_max <= LARGE_MAXCLASS);\n\t} else {\n\t\tusize_max = sz_sa2u(size+extra, alignment);\n\t\tif (unlikely(usize_max == 0 || usize_max > LARGE_MAXCLASS)) {\n\t\t\t/*\n\t\t\t * usize_max is out of range, and chances are that\n\t\t\t * allocation will fail, but use the maximum possible\n\t\t\t * value and carry on with prof_alloc_prep(), just in\n\t\t\t * case allocation succeeds.\n\t\t\t */\n\t\t\tusize_max = LARGE_MAXCLASS;\n\t\t}\n\t}\n\ttctx = prof_alloc_prep(tsd, usize_max, prof_active, false);\n\n\tif (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {\n\t\tusize = ixallocx_prof_sample(tsd_tsdn(tsd), ptr, old_usize,\n\t\t    size, extra, alignment, zero, tctx);\n\t} else {\n\t\tusize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,\n\t\t    extra, alignment, zero);\n\t}\n\tif (usize == old_usize) {\n\t\tprof_alloc_rollback(tsd, tctx, false);\n\t\treturn usize;\n\t}\n\tprof_realloc(tsd, ptr, usize, tctx, prof_active, false, ptr, old_usize,\n\t    old_tctx);\n\n\treturn usize;\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nje_xallocx(void *ptr, size_t size, size_t extra, int flags) {\n\ttsd_t *tsd;\n\tsize_t usize, old_usize;\n\tsize_t alignment = MALLOCX_ALIGN_GET(flags);\n\tbool zero = flags & MALLOCX_ZERO;\n\n\tLOG(\"core.xallocx.entry\", \"ptr: %p, size: %zu, extra: %zu, \"\n\t    \"flags: %d\", ptr, size, extra, flags);\n\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\tassert(SIZE_T_MAX - size >= extra);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\talloc_ctx_t alloc_ctx;\n\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\tassert(alloc_ctx.szind != NSIZES);\n\told_usize = sz_index2size(alloc_ctx.szind);\n\tassert(old_usize == isalloc(tsd_tsdn(tsd), ptr));\n\t/*\n\t * The API explicitly absolves itself of protecting against (size +\n\t * extra) numerical overflow, but we may need to clamp extra to avoid\n\t * exceeding LARGE_MAXCLASS.\n\t *\n\t * Ordinarily, size limit checking is handled deeper down, but here we\n\t * have to check as part of (size + extra) clamping, since we need the\n\t * clamped value in the above helper functions.\n\t */\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\tusize = old_usize;\n\t\tgoto label_not_resized;\n\t}\n\tif (unlikely(LARGE_MAXCLASS - size < extra)) {\n\t\textra = LARGE_MAXCLASS - size;\n\t}\n\n\tif (config_prof && opt_prof) {\n\t\tusize = ixallocx_prof(tsd, ptr, old_usize, size, extra,\n\t\t    alignment, zero, &alloc_ctx);\n\t} else {\n\t\tusize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,\n\t\t    extra, alignment, zero);\n\t}\n\tif (unlikely(usize == old_usize)) {\n\t\tgoto label_not_resized;\n\t}\n\n\tif (config_stats) {\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t\t*tsd_thread_deallocatedp_get(tsd) += old_usize;\n\t}\nlabel_not_resized:\n\tUTRACE(ptr, size, ptr);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.xallocx.exit\", \"result: %zu\", usize);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nJEMALLOC_ATTR(pure)\nje_sallocx(const void *ptr, UNUSED int flags) {\n\tsize_t usize;\n\ttsdn_t *tsdn;\n\n\tLOG(\"core.sallocx.entry\", \"ptr: %p, flags: %d\", ptr, flags);\n\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\tassert(ptr != NULL);\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\n\tif (config_debug || force_ivsalloc) {\n\t\tusize = ivsalloc(tsdn, ptr);\n\t\tassert(force_ivsalloc || usize != 0);\n\t} else {\n\t\tusize = isalloc(tsdn, ptr);\n\t}\n\n\tcheck_entry_exit_locking(tsdn);\n\n\tLOG(\"core.sallocx.exit\", \"result: %zu\", usize);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_dallocx(void *ptr, int flags) {\n\tLOG(\"core.dallocx.entry\", \"ptr: %p, flags: %d\", ptr, flags);\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\ttsd_t *tsd = tsd_fetch();\n\tbool fast = tsd_fast(tsd);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\ttcache_t *tcache;\n\tif (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {\n\t\t/* Not allowed to be reentrant and specify a custom tcache. */\n\t\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\t\tif ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {\n\t\t\ttcache = NULL;\n\t\t} else {\n\t\t\ttcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));\n\t\t}\n\t} else {\n\t\tif (likely(fast)) {\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tassert(tcache == tcache_get(tsd));\n\t\t} else {\n\t\t\tif (likely(tsd_reentrancy_level_get(tsd) == 0)) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t}  else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tUTRACE(ptr, 0, 0);\n\tif (likely(fast)) {\n\t\ttsd_assert_fast(tsd);\n\t\tifree(tsd, ptr, tcache, false);\n\t} else {\n\t\tifree(tsd, ptr, tcache, true);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.dallocx.exit\", \"\");\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\ninallocx(tsdn_t *tsdn, size_t size, int flags) {\n\tcheck_entry_exit_locking(tsdn);\n\n\tsize_t usize;\n\tif (likely((flags & MALLOCX_LG_ALIGN_MASK) == 0)) {\n\t\tusize = sz_s2u(size);\n\t} else {\n\t\tusize = sz_sa2u(size, MALLOCX_ALIGN_GET_SPECIFIED(flags));\n\t}\n\tcheck_entry_exit_locking(tsdn);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_sdallocx(void *ptr, size_t size, int flags) {\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\tLOG(\"core.sdallocx.entry\", \"ptr: %p, size: %zu, flags: %d\", ptr,\n\t    size, flags);\n\n\ttsd_t *tsd = tsd_fetch();\n\tbool fast = tsd_fast(tsd);\n\tsize_t usize = inallocx(tsd_tsdn(tsd), size, flags);\n\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\ttcache_t *tcache;\n\tif (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {\n\t\t/* Not allowed to be reentrant and specify a custom tcache. */\n\t\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\t\tif ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {\n\t\t\ttcache = NULL;\n\t\t} else {\n\t\t\ttcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));\n\t\t}\n\t} else {\n\t\tif (likely(fast)) {\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tassert(tcache == tcache_get(tsd));\n\t\t} else {\n\t\t\tif (likely(tsd_reentrancy_level_get(tsd) == 0)) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t} else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tUTRACE(ptr, 0, 0);\n\tif (likely(fast)) {\n\t\ttsd_assert_fast(tsd);\n\t\tisfree(tsd, ptr, usize, tcache, false);\n\t} else {\n\t\tisfree(tsd, ptr, usize, tcache, true);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.sdallocx.exit\", \"\");\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nJEMALLOC_ATTR(pure)\nje_nallocx(size_t size, int flags) {\n\tsize_t usize;\n\ttsdn_t *tsdn;\n\n\tassert(size != 0);\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.nallocx.exit\", \"result: %zu\", ZU(0));\n\t\treturn 0;\n\t}\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\n\tusize = inallocx(tsdn, size, flags);\n\tif (unlikely(usize > LARGE_MAXCLASS)) {\n\t\tLOG(\"core.nallocx.exit\", \"result: %zu\", ZU(0));\n\t\treturn 0;\n\t}\n\n\tcheck_entry_exit_locking(tsdn);\n\tLOG(\"core.nallocx.exit\", \"result: %zu\", usize);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nje_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,\n    size_t newlen) {\n\tint ret;\n\ttsd_t *tsd;\n\n\tLOG(\"core.mallctl.entry\", \"name: %s\", name);\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.mallctl.exit\", \"result: %d\", EAGAIN);\n\t\treturn EAGAIN;\n\t}\n\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tret = ctl_byname(tsd, name, oldp, oldlenp, newp, newlen);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.mallctl.exit\", \"result: %d\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nje_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) {\n\tint ret;\n\n\tLOG(\"core.mallctlnametomib.entry\", \"name: %s\", name);\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.mallctlnametomib.exit\", \"result: %d\", EAGAIN);\n\t\treturn EAGAIN;\n\t}\n\n\ttsd_t *tsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tret = ctl_nametomib(tsd, name, mibp, miblenp);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.mallctlnametomib.exit\", \"result: %d\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nje_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n  void *newp, size_t newlen) {\n\tint ret;\n\ttsd_t *tsd;\n\n\tLOG(\"core.mallctlbymib.entry\", \"\");\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.mallctlbymib.exit\", \"result: %d\", EAGAIN);\n\t\treturn EAGAIN;\n\t}\n\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tret = ctl_bymib(tsd, mib, miblen, oldp, oldlenp, newp, newlen);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tLOG(\"core.mallctlbymib.exit\", \"result: %d\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts) {\n\ttsdn_t *tsdn;\n\n\tLOG(\"core.malloc_stats_print.entry\", \"\");\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\tstats_print(write_cb, cbopaque, opts);\n\tcheck_entry_exit_locking(tsdn);\n\tLOG(\"core.malloc_stats_print.exit\", \"\");\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nje_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {\n\tsize_t ret;\n\ttsdn_t *tsdn;\n\n\tLOG(\"core.malloc_usable_size.entry\", \"ptr: %p\", ptr);\n\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\n\tif (unlikely(ptr == NULL)) {\n\t\tret = 0;\n\t} else {\n\t\tif (config_debug || force_ivsalloc) {\n\t\t\tret = ivsalloc(tsdn, ptr);\n\t\t\tassert(force_ivsalloc || ret != 0);\n\t\t} else {\n\t\t\tret = isalloc(tsdn, ptr);\n\t\t}\n\t}\n\n\tcheck_entry_exit_locking(tsdn);\n\tLOG(\"core.malloc_usable_size.exit\", \"result: %zu\", ret);\n\treturn ret;\n}\n\n/*\n * End non-standard 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 be possible\n * to trigger the deadlock described above, but doing so would involve forking\n * via a library constructor that runs before jemalloc's runs.\n */\n#ifndef JEMALLOC_JET\nJEMALLOC_ATTR(constructor)\nstatic void\njemalloc_constructor(void) {\n\tmalloc_init();\n}\n#endif\n\n#ifndef JEMALLOC_MUTEX_INIT_CB\nvoid\njemalloc_prefork(void)\n#else\nJEMALLOC_EXPORT void\n_malloc_prefork(void)\n#endif\n{\n\ttsd_t *tsd;\n\tunsigned i, j, narenas;\n\tarena_t *arena;\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tif (!malloc_initialized()) {\n\t\treturn;\n\t}\n#endif\n\tassert(malloc_initialized());\n\n\ttsd = tsd_fetch();\n\n\tnarenas = narenas_total_get();\n\n\twitness_prefork(tsd_witness_tsdp_get(tsd));\n\t/* Acquire all mutexes in a safe order. */\n\tctl_prefork(tsd_tsdn(tsd));\n\ttcache_prefork(tsd_tsdn(tsd));\n\tmalloc_mutex_prefork(tsd_tsdn(tsd), &arenas_lock);\n\tif (have_background_thread) {\n\t\tbackground_thread_prefork0(tsd_tsdn(tsd));\n\t}\n\tprof_prefork0(tsd_tsdn(tsd));\n\tif (have_background_thread) {\n\t\tbackground_thread_prefork1(tsd_tsdn(tsd));\n\t}\n\t/* Break arena prefork into stages to preserve lock order. */\n\tfor (i = 0; i < 8; i++) {\n\t\tfor (j = 0; j < narenas; j++) {\n\t\t\tif ((arena = arena_get(tsd_tsdn(tsd), j, false)) !=\n\t\t\t    NULL) {\n\t\t\t\tswitch (i) {\n\t\t\t\tcase 0:\n\t\t\t\t\tarena_prefork0(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tarena_prefork1(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tarena_prefork2(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tarena_prefork3(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tarena_prefork4(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tarena_prefork5(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\tarena_prefork6(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\tarena_prefork7(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: not_reached();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprof_prefork1(tsd_tsdn(tsd));\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\ttsd_t *tsd;\n\tunsigned i, narenas;\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tif (!malloc_initialized()) {\n\t\treturn;\n\t}\n#endif\n\tassert(malloc_initialized());\n\n\ttsd = tsd_fetch();\n\n\twitness_postfork_parent(tsd_witness_tsdp_get(tsd));\n\t/* Release all mutexes, now that fork() has completed. */\n\tfor (i = 0, narenas = narenas_total_get(); i < narenas; i++) {\n\t\tarena_t *arena;\n\n\t\tif ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {\n\t\t\tarena_postfork_parent(tsd_tsdn(tsd), arena);\n\t\t}\n\t}\n\tprof_postfork_parent(tsd_tsdn(tsd));\n\tif (have_background_thread) {\n\t\tbackground_thread_postfork_parent(tsd_tsdn(tsd));\n\t}\n\tmalloc_mutex_postfork_parent(tsd_tsdn(tsd), &arenas_lock);\n\ttcache_postfork_parent(tsd_tsdn(tsd));\n\tctl_postfork_parent(tsd_tsdn(tsd));\n}\n\nvoid\njemalloc_postfork_child(void) {\n\ttsd_t *tsd;\n\tunsigned i, narenas;\n\n\tassert(malloc_initialized());\n\n\ttsd = tsd_fetch();\n\n\twitness_postfork_child(tsd_witness_tsdp_get(tsd));\n\t/* Release all mutexes, now that fork() has completed. */\n\tfor (i = 0, narenas = narenas_total_get(); i < narenas; i++) {\n\t\tarena_t *arena;\n\n\t\tif ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {\n\t\t\tarena_postfork_child(tsd_tsdn(tsd), arena);\n\t\t}\n\t}\n\tprof_postfork_child(tsd_tsdn(tsd));\n\tif (have_background_thread) {\n\t\tbackground_thread_postfork_child(tsd_tsdn(tsd));\n\t}\n\tmalloc_mutex_postfork_child(tsd_tsdn(tsd), &arenas_lock);\n\ttcache_postfork_child(tsd_tsdn(tsd));\n\tctl_postfork_child(tsd_tsdn(tsd));\n}\n\n/******************************************************************************/\n\n/* Helps the application decide if a pointer is worth re-allocating in order to reduce fragmentation.\n * returns 0 if the allocation is in the currently active run,\n * or when it is not causing any frag issue (large or huge bin)\n * returns the bin utilization and run utilization both in fixed point 16:16.\n * If the application decides to re-allocate it should use MALLOCX_TCACHE_NONE when doing so. */\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nget_defrag_hint(void* ptr, int *bin_util, int *run_util) {\n\tassert(ptr != NULL);\n\treturn iget_defrag_hint(TSDN_NULL, ptr, bin_util, run_util);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/jemalloc_cpp.cpp",
    "content": "#include <mutex>\n#include <new>\n\n#define JEMALLOC_CPP_CPP_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#ifdef __cplusplus\n}\n#endif\n\n// All operators in this file are exported.\n\n// Possibly alias hidden versions of malloc and sdallocx to avoid an extra plt\n// thunk?\n//\n// extern __typeof (sdallocx) sdallocx_int\n//  __attribute ((alias (\"sdallocx\"),\n//\t\tvisibility (\"hidden\")));\n//\n// ... but it needs to work with jemalloc namespaces.\n\nvoid\t*operator new(std::size_t size);\nvoid\t*operator new[](std::size_t size);\nvoid\t*operator new(std::size_t size, const std::nothrow_t &) noexcept;\nvoid\t*operator new[](std::size_t size, const std::nothrow_t &) noexcept;\nvoid\toperator delete(void *ptr) noexcept;\nvoid\toperator delete[](void *ptr) noexcept;\nvoid\toperator delete(void *ptr, const std::nothrow_t &) noexcept;\nvoid\toperator delete[](void *ptr, const std::nothrow_t &) noexcept;\n\n#if __cpp_sized_deallocation >= 201309\n/* C++14's sized-delete operators. */\nvoid\toperator delete(void *ptr, std::size_t size) noexcept;\nvoid\toperator delete[](void *ptr, std::size_t size) noexcept;\n#endif\n\nJEMALLOC_NOINLINE\nstatic void *\nhandleOOM(std::size_t size, bool nothrow) {\n\tvoid *ptr = nullptr;\n\n\twhile (ptr == nullptr) {\n\t\tstd::new_handler handler;\n\t\t// GCC-4.8 and clang 4.0 do not have std::get_new_handler.\n\t\t{\n\t\t\tstatic std::mutex mtx;\n\t\t\tstd::lock_guard<std::mutex> lock(mtx);\n\n\t\t\thandler = std::set_new_handler(nullptr);\n\t\t\tstd::set_new_handler(handler);\n\t\t}\n\t\tif (handler == nullptr)\n\t\t\tbreak;\n\n\t\ttry {\n\t\t\thandler();\n\t\t} catch (const std::bad_alloc &) {\n\t\t\tbreak;\n\t\t}\n\n\t\tptr = je_malloc(size);\n\t}\n\n\tif (ptr == nullptr && !nothrow)\n\t\tstd::__throw_bad_alloc();\n\treturn ptr;\n}\n\ntemplate <bool IsNoExcept>\nJEMALLOC_ALWAYS_INLINE\nvoid *\nnewImpl(std::size_t size) noexcept(IsNoExcept) {\n\tvoid *ptr = je_malloc(size);\n\tif (likely(ptr != nullptr))\n\t\treturn ptr;\n\n\treturn handleOOM(size, IsNoExcept);\n}\n\nvoid *\noperator new(std::size_t size) {\n\treturn newImpl<false>(size);\n}\n\nvoid *\noperator new[](std::size_t size) {\n\treturn newImpl<false>(size);\n}\n\nvoid *\noperator new(std::size_t size, const std::nothrow_t &) noexcept {\n\treturn newImpl<true>(size);\n}\n\nvoid *\noperator new[](std::size_t size, const std::nothrow_t &) noexcept {\n\treturn newImpl<true>(size);\n}\n\nvoid\noperator delete(void *ptr) noexcept {\n\tje_free(ptr);\n}\n\nvoid\noperator delete[](void *ptr) noexcept {\n\tje_free(ptr);\n}\n\nvoid\noperator delete(void *ptr, const std::nothrow_t &) noexcept {\n\tje_free(ptr);\n}\n\nvoid operator delete[](void *ptr, const std::nothrow_t &) noexcept {\n\tje_free(ptr);\n}\n\n#if __cpp_sized_deallocation >= 201309\n\nvoid\noperator delete(void *ptr, std::size_t size) noexcept {\n\tif (unlikely(ptr == nullptr)) {\n\t\treturn;\n\t}\n\tje_sdallocx(ptr, size, /*flags=*/0);\n}\n\nvoid operator delete[](void *ptr, std::size_t size) noexcept {\n\tif (unlikely(ptr == nullptr)) {\n\t\treturn;\n\t}\n\tje_sdallocx(ptr, size, /*flags=*/0);\n}\n\n#endif  // __cpp_sized_deallocation\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/large.c",
    "content": "#define JEMALLOC_LARGE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n\nvoid *\nlarge_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero) {\n\tassert(usize == sz_s2u(usize));\n\n\treturn large_palloc(tsdn, arena, usize, CACHELINE, zero);\n}\n\nvoid *\nlarge_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,\n    bool zero) {\n\tsize_t ausize;\n\textent_t *extent;\n\tbool is_zeroed;\n\tUNUSED bool idump JEMALLOC_CC_SILENCE_INIT(false);\n\n\tassert(!tsdn_null(tsdn) || arena != NULL);\n\n\tausize = sz_sa2u(usize, alignment);\n\tif (unlikely(ausize == 0 || ausize > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\n\tif (config_fill && unlikely(opt_zero)) {\n\t\tzero = true;\n\t}\n\t/*\n\t * Copy zero into is_zeroed and pass the copy when allocating the\n\t * extent, so that it is possible to make correct junk/zero fill\n\t * decisions below, even if is_zeroed ends up true when zero is false.\n\t */\n\tis_zeroed = zero;\n\tif (likely(!tsdn_null(tsdn))) {\n\t\tarena = arena_choose(tsdn_tsd(tsdn), arena);\n\t}\n\tif (unlikely(arena == NULL) || (extent = arena_extent_alloc_large(tsdn,\n\t    arena, usize, alignment, &is_zeroed)) == NULL) {\n\t\treturn NULL;\n\t}\n\n\t/* See comments in arena_bin_slabs_full_insert(). */\n\tif (!arena_is_auto(arena)) {\n\t\t/* Insert extent into large. */\n\t\tmalloc_mutex_lock(tsdn, &arena->large_mtx);\n\t\textent_list_append(&arena->large, extent);\n\t\tmalloc_mutex_unlock(tsdn, &arena->large_mtx);\n\t}\n\tif (config_prof && arena_prof_accum(tsdn, arena, usize)) {\n\t\tprof_idump(tsdn);\n\t}\n\n\tif (zero) {\n\t\tassert(is_zeroed);\n\t} else if (config_fill && unlikely(opt_junk_alloc)) {\n\t\tmemset(extent_addr_get(extent), JEMALLOC_ALLOC_JUNK,\n\t\t    extent_usize_get(extent));\n\t}\n\n\tarena_decay_tick(tsdn, arena);\n\treturn extent_addr_get(extent);\n}\n\nstatic void\nlarge_dalloc_junk_impl(void *ptr, size_t size) {\n\tmemset(ptr, JEMALLOC_FREE_JUNK, size);\n}\nlarge_dalloc_junk_t *JET_MUTABLE large_dalloc_junk = large_dalloc_junk_impl;\n\nstatic void\nlarge_dalloc_maybe_junk_impl(void *ptr, size_t size) {\n\tif (config_fill && have_dss && unlikely(opt_junk_free)) {\n\t\t/*\n\t\t * Only bother junk filling if the extent isn't about to be\n\t\t * unmapped.\n\t\t */\n\t\tif (opt_retain || (have_dss && extent_in_dss(ptr))) {\n\t\t\tlarge_dalloc_junk(ptr, size);\n\t\t}\n\t}\n}\nlarge_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk =\n    large_dalloc_maybe_junk_impl;\n\nstatic bool\nlarge_ralloc_no_move_shrink(tsdn_t *tsdn, extent_t *extent, size_t usize) {\n\tarena_t *arena = extent_arena_get(extent);\n\tsize_t oldusize = extent_usize_get(extent);\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\tsize_t diff = extent_size_get(extent) - (usize + sz_large_pad);\n\n\tassert(oldusize > usize);\n\n\tif (extent_hooks->split == NULL) {\n\t\treturn true;\n\t}\n\n\t/* Split excess pages. */\n\tif (diff != 0) {\n\t\textent_t *trail = extent_split_wrapper(tsdn, arena,\n\t\t    &extent_hooks, extent, usize + sz_large_pad,\n\t\t    sz_size2index(usize), false, diff, NSIZES, false);\n\t\tif (trail == NULL) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (config_fill && unlikely(opt_junk_free)) {\n\t\t\tlarge_dalloc_maybe_junk(extent_addr_get(trail),\n\t\t\t    extent_size_get(trail));\n\t\t}\n\n\t\tarena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, trail);\n\t}\n\n\tarena_extent_ralloc_large_shrink(tsdn, arena, extent, oldusize);\n\n\treturn false;\n}\n\nstatic bool\nlarge_ralloc_no_move_expand(tsdn_t *tsdn, extent_t *extent, size_t usize,\n    bool zero) {\n\tarena_t *arena = extent_arena_get(extent);\n\tsize_t oldusize = extent_usize_get(extent);\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\tsize_t trailsize = usize - oldusize;\n\n\tif (extent_hooks->merge == NULL) {\n\t\treturn true;\n\t}\n\n\tif (config_fill && unlikely(opt_zero)) {\n\t\tzero = true;\n\t}\n\t/*\n\t * Copy zero into is_zeroed_trail and pass the copy when allocating the\n\t * extent, so that it is possible to make correct junk/zero fill\n\t * decisions below, even if is_zeroed_trail ends up true when zero is\n\t * false.\n\t */\n\tbool is_zeroed_trail = zero;\n\tbool commit = true;\n\textent_t *trail;\n\tbool new_mapping;\n\tif ((trail = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_dirty, extent_past_get(extent), trailsize, 0,\n\t    CACHELINE, false, NSIZES, &is_zeroed_trail, &commit)) != NULL\n\t    || (trail = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_muzzy, extent_past_get(extent), trailsize, 0,\n\t    CACHELINE, false, NSIZES, &is_zeroed_trail, &commit)) != NULL) {\n\t\tif (config_stats) {\n\t\t\tnew_mapping = false;\n\t\t}\n\t} else {\n\t\tif ((trail = extent_alloc_wrapper(tsdn, arena, &extent_hooks,\n\t\t    extent_past_get(extent), trailsize, 0, CACHELINE, false,\n\t\t    NSIZES, &is_zeroed_trail, &commit)) == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tif (config_stats) {\n\t\t\tnew_mapping = true;\n\t\t}\n\t}\n\n\tif (extent_merge_wrapper(tsdn, arena, &extent_hooks, extent, trail)) {\n\t\textent_dalloc_wrapper(tsdn, arena, &extent_hooks, trail);\n\t\treturn true;\n\t}\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\tszind_t szind = sz_size2index(usize);\n\textent_szind_set(extent, szind);\n\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_addr_get(extent), szind, false);\n\n\tif (config_stats && new_mapping) {\n\t\tarena_stats_mapped_add(tsdn, &arena->stats, trailsize);\n\t}\n\n\tif (zero) {\n\t\tif (config_cache_oblivious) {\n\t\t\t/*\n\t\t\t * Zero the trailing bytes of the original allocation's\n\t\t\t * last page, since they are in an indeterminate state.\n\t\t\t * There will always be trailing bytes, because ptr's\n\t\t\t * offset from the beginning of the extent is a multiple\n\t\t\t * of CACHELINE in [0 .. PAGE).\n\t\t\t */\n\t\t\tvoid *zbase = (void *)\n\t\t\t    ((uintptr_t)extent_addr_get(extent) + oldusize);\n\t\t\tvoid *zpast = PAGE_ADDR2BASE((void *)((uintptr_t)zbase +\n\t\t\t    PAGE));\n\t\t\tsize_t nzero = (uintptr_t)zpast - (uintptr_t)zbase;\n\t\t\tassert(nzero > 0);\n\t\t\tmemset(zbase, 0, nzero);\n\t\t}\n\t\tassert(is_zeroed_trail);\n\t} else if (config_fill && unlikely(opt_junk_alloc)) {\n\t\tmemset((void *)((uintptr_t)extent_addr_get(extent) + oldusize),\n\t\t    JEMALLOC_ALLOC_JUNK, usize - oldusize);\n\t}\n\n\tarena_extent_ralloc_large_expand(tsdn, arena, extent, oldusize);\n\n\treturn false;\n}\n\nbool\nlarge_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,\n    size_t usize_max, bool zero) {\n\tsize_t oldusize = extent_usize_get(extent);\n\n\t/* The following should have been caught by callers. */\n\tassert(usize_min > 0 && usize_max <= LARGE_MAXCLASS);\n\t/* Both allocation sizes must be large to avoid a move. */\n\tassert(oldusize >= LARGE_MINCLASS && usize_max >= LARGE_MINCLASS);\n\n\tif (usize_max > oldusize) {\n\t\t/* Attempt to expand the allocation in-place. */\n\t\tif (!large_ralloc_no_move_expand(tsdn, extent, usize_max,\n\t\t    zero)) {\n\t\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\t\treturn false;\n\t\t}\n\t\t/* Try again, this time with usize_min. */\n\t\tif (usize_min < usize_max && usize_min > oldusize &&\n\t\t    large_ralloc_no_move_expand(tsdn, extent, usize_min,\n\t\t    zero)) {\n\t\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * Avoid moving the allocation if the existing extent size accommodates\n\t * the new size.\n\t */\n\tif (oldusize >= usize_min && oldusize <= usize_max) {\n\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\treturn false;\n\t}\n\n\t/* Attempt to shrink the allocation in-place. */\n\tif (oldusize > usize_max) {\n\t\tif (!large_ralloc_no_move_shrink(tsdn, extent, usize_max)) {\n\t\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic void *\nlarge_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool zero) {\n\tif (alignment <= CACHELINE) {\n\t\treturn large_malloc(tsdn, arena, usize, zero);\n\t}\n\treturn large_palloc(tsdn, arena, usize, alignment, zero);\n}\n\nvoid *\nlarge_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache) {\n\tsize_t oldusize = extent_usize_get(extent);\n\n\t/* The following should have been caught by callers. */\n\tassert(usize > 0 && usize <= LARGE_MAXCLASS);\n\t/* Both allocation sizes must be large to avoid a move. */\n\tassert(oldusize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS);\n\n\t/* Try to avoid moving the allocation. */\n\tif (!large_ralloc_no_move(tsdn, extent, usize, usize, zero)) {\n\t\treturn extent_addr_get(extent);\n\t}\n\n\t/*\n\t * usize and old size 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\tvoid *ret = large_ralloc_move_helper(tsdn, arena, usize, alignment,\n\t    zero);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsize_t copysize = (usize < oldusize) ? usize : oldusize;\n\tmemcpy(ret, extent_addr_get(extent), copysize);\n\tisdalloct(tsdn, extent_addr_get(extent), oldusize, tcache, NULL, true);\n\treturn ret;\n}\n\n/*\n * junked_locked indicates whether the extent's data have been junk-filled, and\n * whether the arena's large_mtx is currently held.\n */\nstatic void\nlarge_dalloc_prep_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    bool junked_locked) {\n\tif (!junked_locked) {\n\t\t/* See comments in arena_bin_slabs_full_insert(). */\n\t\tif (!arena_is_auto(arena)) {\n\t\t\tmalloc_mutex_lock(tsdn, &arena->large_mtx);\n\t\t\textent_list_remove(&arena->large, extent);\n\t\t\tmalloc_mutex_unlock(tsdn, &arena->large_mtx);\n\t\t}\n\t\tlarge_dalloc_maybe_junk(extent_addr_get(extent),\n\t\t    extent_usize_get(extent));\n\t} else {\n\t\tmalloc_mutex_assert_owner(tsdn, &arena->large_mtx);\n\t\tif (!arena_is_auto(arena)) {\n\t\t\textent_list_remove(&arena->large, extent);\n\t\t}\n\t}\n\tarena_extent_dalloc_large_prep(tsdn, arena, extent);\n}\n\nstatic void\nlarge_dalloc_finish_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\tarena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, extent);\n}\n\nvoid\nlarge_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent) {\n\tlarge_dalloc_prep_impl(tsdn, extent_arena_get(extent), extent, true);\n}\n\nvoid\nlarge_dalloc_finish(tsdn_t *tsdn, extent_t *extent) {\n\tlarge_dalloc_finish_impl(tsdn, extent_arena_get(extent), extent);\n}\n\nvoid\nlarge_dalloc(tsdn_t *tsdn, extent_t *extent) {\n\tarena_t *arena = extent_arena_get(extent);\n\tlarge_dalloc_prep_impl(tsdn, arena, extent, false);\n\tlarge_dalloc_finish_impl(tsdn, arena, extent);\n\tarena_decay_tick(tsdn, arena);\n}\n\nsize_t\nlarge_salloc(tsdn_t *tsdn, const extent_t *extent) {\n\treturn extent_usize_get(extent);\n}\n\nprof_tctx_t *\nlarge_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent) {\n\treturn extent_prof_tctx_get(extent);\n}\n\nvoid\nlarge_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx) {\n\textent_prof_tctx_set(extent, tctx);\n}\n\nvoid\nlarge_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent) {\n\tlarge_prof_tctx_set(tsdn, extent, (prof_tctx_t *)(uintptr_t)1U);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/log.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/log.h\"\n\nchar log_var_names[JEMALLOC_LOG_VAR_BUFSIZE];\natomic_b_t log_init_done = ATOMIC_INIT(false);\n\n/*\n * Returns true if we were able to pick out a segment.  Fills in r_segment_end\n * with a pointer to the first character after the end of the string.\n */\nstatic const char *\nlog_var_extract_segment(const char* segment_begin) {\n\tconst char *end;\n\tfor (end = segment_begin; *end != '\\0' && *end != '|'; end++) {\n\t}\n\treturn end;\n}\n\nstatic bool\nlog_var_matches_segment(const char *segment_begin, const char *segment_end,\n    const char *log_var_begin, const char *log_var_end) {\n\tassert(segment_begin <= segment_end);\n\tassert(log_var_begin < log_var_end);\n\n\tptrdiff_t segment_len = segment_end - segment_begin;\n\tptrdiff_t log_var_len = log_var_end - log_var_begin;\n\t/* The special '.' segment matches everything. */\n\tif (segment_len == 1 && *segment_begin == '.') {\n\t\treturn true;\n\t}\n        if (segment_len == log_var_len) {\n\t\treturn strncmp(segment_begin, log_var_begin, segment_len) == 0;\n\t} else if (segment_len < log_var_len) {\n\t\treturn strncmp(segment_begin, log_var_begin, segment_len) == 0\n\t\t    && log_var_begin[segment_len] == '.';\n        } else {\n\t\treturn false;\n\t}\n}\n\nunsigned\nlog_var_update_state(log_var_t *log_var) {\n\tconst char *log_var_begin = log_var->name;\n\tconst char *log_var_end = log_var->name + strlen(log_var->name);\n\n\t/* Pointer to one before the beginning of the current segment. */\n\tconst char *segment_begin = log_var_names;\n\n\t/*\n\t * If log_init done is false, we haven't parsed the malloc conf yet.  To\n\t * avoid log-spew, we default to not displaying anything.\n\t */\n\tif (!atomic_load_b(&log_init_done, ATOMIC_ACQUIRE)) {\n\t\treturn LOG_INITIALIZED_NOT_ENABLED;\n\t}\n\n\twhile (true) {\n\t\tconst char *segment_end = log_var_extract_segment(\n\t\t    segment_begin);\n\t\tassert(segment_end < log_var_names + JEMALLOC_LOG_VAR_BUFSIZE);\n\t\tif (log_var_matches_segment(segment_begin, segment_end,\n\t\t    log_var_begin, log_var_end)) {\n\t\t\tatomic_store_u(&log_var->state, LOG_ENABLED,\n\t\t\t    ATOMIC_RELAXED);\n\t\t\treturn LOG_ENABLED;\n\t\t}\n\t\tif (*segment_end == '\\0') {\n\t\t\t/* Hit the end of the segment string with no match. */\n\t\t\tatomic_store_u(&log_var->state,\n\t\t\t    LOG_INITIALIZED_NOT_ENABLED, ATOMIC_RELAXED);\n\t\t\treturn LOG_INITIALIZED_NOT_ENABLED;\n\t\t}\n\t\t/* Otherwise, skip the delimiter and continue. */\n\t\tsegment_begin = segment_end + 1;\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/malloc_io.c",
    "content": "#define JEMALLOC_MALLOC_IO_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/util.h\"\n\n#ifdef assert\n#  undef assert\n#endif\n#ifdef not_reached\n#  undef not_reached\n#endif\n#ifdef not_implemented\n#  undef not_implemented\n#endif\n#ifdef assert_not_implemented\n#  undef assert_not_implemented\n#endif\n\n/*\n * Define simple versions of assertion macros that won't recurse in case\n * of assertion failures in malloc_*printf().\n */\n#define assert(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 not_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\tunreachable();\t\t\t\t\t\t\t\\\n} while (0)\n\n#define not_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 assert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (unlikely(config_debug && !(e))) {\t\t\t\t\\\n\t\tnot_implemented();\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void wrtmessage(void *cbopaque, const char *s);\n#define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)\nstatic char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,\n    size_t *slen_p);\n#define D2S_BUFSIZE (1 + U2S_BUFSIZE)\nstatic char *d2s(intmax_t x, char sign, char *s, size_t *slen_p);\n#define O2S_BUFSIZE (1 + U2S_BUFSIZE)\nstatic char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);\n#define X2S_BUFSIZE (2 + U2S_BUFSIZE)\nstatic char *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\tmalloc_write_fd(STDERR_FILENO, s, strlen(s));\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\tif (je_malloc_message != NULL) {\n\t\tje_malloc_message(NULL, s);\n\t} else {\n\t\twrtmessage(NULL, s);\n\t}\n}\n\n/*\n * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so\n * provide a wrapper.\n */\nint\nbuferror(int err, char *buf, size_t buflen) {\n#ifdef _WIN32\n\tFormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,\n\t    (LPSTR)buf, (DWORD)buflen, NULL);\n\treturn 0;\n#elif defined(JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE) && defined(_GNU_SOURCE)\n\tchar *b = strerror_r(err, 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(err, buf, buflen);\n#endif\n}\n\nuintmax_t\nmalloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) {\n\tuintmax_t ret, digit;\n\tunsigned b;\n\tbool neg;\n\tconst char *p, *ns;\n\n\tp = nptr;\n\tif (base < 0 || base == 1 || base > 36) {\n\t\tns = p;\n\t\tset_errno(EINVAL);\n\t\tret = UINTMAX_MAX;\n\t\tgoto label_return;\n\t}\n\tb = base;\n\n\t/* Swallow leading whitespace and get sign, if any. */\n\tneg = false;\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\t}\n\t\t\tif (b == 8) {\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'X': case '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\t}\n\t\t\t\tif (b == 16) {\n\t\t\t\t\tp += 2;\n\t\t\t\t}\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\tp++;\n\t\t\tret = 0;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\tif (b == 0) {\n\t\tb = 10;\n\t}\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\tret = UINTMAX_MAX;\n\t\t\tgoto label_return;\n\t\t}\n\t\tp++;\n\t}\n\tif (neg) {\n\t\tret = (uintmax_t)(-((intmax_t)ret));\n\t}\n\n\tif (p == ns) {\n\t\t/* No conversion performed. */\n\t\tset_errno(EINVAL);\n\t\tret = UINTMAX_MAX;\n\t\tgoto label_return;\n\t}\n\nlabel_return:\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\t}\n\t}\n\treturn ret;\n}\n\nstatic char *\nu2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p) {\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\tbool neg;\n\n\tif ((neg = (x < 0))) {\n\t\tx = -x;\n\t}\n\ts = u2s(x, 10, false, s, slen_p);\n\tif (neg) {\n\t\tsign = '-';\n\t}\n\tswitch (sign) {\n\tcase '-':\n\t\tif (!neg) {\n\t\t\tbreak;\n\t\t}\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\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\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\nsize_t\nmalloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {\n\tsize_t i;\n\tconst char *f;\n\n#define APPEND_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\t}\t\t\t\t\t\t\t\t\\\n\ti++;\t\t\t\t\t\t\t\t\\\n} while (0)\n#define APPEND_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 APPEND_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 && 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\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\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 'j' | 0x80:\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, uintmax_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:\t\t\t\t\t\t\t\\\n\t\tnot_reached();\t\t\t\t\t\t\\\n\t\tval = 0;\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\t\t\tchar *s;\n\t\t\tsize_t slen;\n\n\t\t\tf++;\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);\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);\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);\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);\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\tif (width < 0) {\n\t\t\t\t\tleft_justify = true;\n\t\t\t\t\twidth = -width;\n\t\t\t\t}\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\tbreak;\n\t\t\t} default:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* Width/precision separator. */\n\t\t\tif (*f == '.') {\n\t\t\t\tf++;\n\t\t\t} else {\n\t\t\t\tgoto label_length;\n\t\t\t}\n\t\t\t/* 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\t}\n\t\t\t\tbreak;\n\t\t\tcase 'q': case 'j': case 't': case 'z':\n\t\t\t\tlen = *f;\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\tcase '%':\n\t\t\t\t/* %% */\n\t\t\t\tAPPEND_C(*f);\n\t\t\t\tf++;\n\t\t\t\tbreak;\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 < 0) ? strlen(s) : (size_t)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} default: not_reached();\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\t} else {\n\t\tstr[size - 1] = '\\0';\n\t}\n\n#undef APPEND_C\n#undef APPEND_S\n#undef APPEND_PADDED_S\n#undef GET_ARG_NUMERIC\n\treturn i;\n}\n\nJEMALLOC_FORMAT_PRINTF(3, 4)\nsize_t\nmalloc_snprintf(char *str, size_t size, const char *format, ...) {\n\tsize_t 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\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_FORMAT_PRINTF(3, 4)\nvoid\nmalloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, ...) {\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_FORMAT_PRINTF(1, 2)\nvoid\nmalloc_printf(const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n}\n\n/*\n * Restore normal assertion macros, in order to make it possible to compile all\n * C files as a single concatenation.\n */\n#undef assert\n#undef not_reached\n#undef not_implemented\n#undef assert_not_implemented\n#include \"jemalloc/internal/assert.h\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/mutex.c",
    "content": "#define JEMALLOC_MUTEX_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/spin.h\"\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/******************************************************************************/\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)\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\treturn pthread_create_wrapper(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\nvoid\nmalloc_mutex_lock_slow(malloc_mutex_t *mutex) {\n\tmutex_prof_data_t *data = &mutex->prof_data;\n\tUNUSED nstime_t before = NSTIME_ZERO_INITIALIZER;\n\n\tif (ncpus == 1) {\n\t\tgoto label_spin_done;\n\t}\n\n\tint cnt = 0, max_cnt = MALLOC_MUTEX_MAX_SPIN;\n\tdo {\n\t\tspin_cpu_spinwait();\n\t\tif (!malloc_mutex_trylock_final(mutex)) {\n\t\t\tdata->n_spin_acquired++;\n\t\t\treturn;\n\t\t}\n\t} while (cnt++ < max_cnt);\n\n\tif (!config_stats) {\n\t\t/* Only spin is useful when stats is off. */\n\t\tmalloc_mutex_lock_final(mutex);\n\t\treturn;\n\t}\nlabel_spin_done:\n\tnstime_update(&before);\n\t/* Copy before to after to avoid clock skews. */\n\tnstime_t after;\n\tnstime_copy(&after, &before);\n\tuint32_t n_thds = atomic_fetch_add_u32(&data->n_waiting_thds, 1,\n\t    ATOMIC_RELAXED) + 1;\n\t/* One last try as above two calls may take quite some cycles. */\n\tif (!malloc_mutex_trylock_final(mutex)) {\n\t\tatomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED);\n\t\tdata->n_spin_acquired++;\n\t\treturn;\n\t}\n\n\t/* True slow path. */\n\tmalloc_mutex_lock_final(mutex);\n\t/* Update more slow-path only counters. */\n\tatomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED);\n\tnstime_update(&after);\n\n\tnstime_t delta;\n\tnstime_copy(&delta, &after);\n\tnstime_subtract(&delta, &before);\n\n\tdata->n_wait_times++;\n\tnstime_add(&data->tot_wait_time, &delta);\n\tif (nstime_compare(&data->max_wait_time, &delta) < 0) {\n\t\tnstime_copy(&data->max_wait_time, &delta);\n\t}\n\tif (n_thds > data->max_n_thds) {\n\t\tdata->max_n_thds = n_thds;\n\t}\n}\n\nstatic void\nmutex_prof_data_init(mutex_prof_data_t *data) {\n\tmemset(data, 0, sizeof(mutex_prof_data_t));\n\tnstime_init(&data->max_wait_time, 0);\n\tnstime_init(&data->tot_wait_time, 0);\n\tdata->prev_owner = NULL;\n}\n\nvoid\nmalloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tmalloc_mutex_assert_owner(tsdn, mutex);\n\tmutex_prof_data_init(&mutex->prof_data);\n}\n\nstatic int\nmutex_addr_comp(const witness_t *witness1, void *mutex1,\n    const witness_t *witness2, void *mutex2) {\n\tassert(mutex1 != NULL);\n\tassert(mutex2 != NULL);\n\tuintptr_t mu1int = (uintptr_t)mutex1;\n\tuintptr_t mu2int = (uintptr_t)mutex2;\n\tif (mu1int < mu2int) {\n\t\treturn -1;\n\t} else if (mu1int == mu2int) {\n\t\treturn 0;\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nbool\nmalloc_mutex_init(malloc_mutex_t *mutex, const char *name,\n    witness_rank_t rank, malloc_mutex_lock_order_t lock_order) {\n\tmutex_prof_data_init(&mutex->prof_data);\n#ifdef _WIN32\n#  if _WIN32_WINNT >= 0x0600\n\tInitializeSRWLock(&mutex->lock);\n#  else\n\tif (!InitializeCriticalSectionAndSpinCount(&mutex->lock,\n\t    _CRT_SPINCOUNT)) {\n\t\treturn true;\n\t}\n#  endif\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tmutex->lock = OS_UNFAIR_LOCK_INIT;\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,\n\t\t    bootstrap_calloc) != 0) {\n\t\t\treturn true;\n\t\t}\n\t}\n#else\n\tpthread_mutexattr_t attr;\n\n\tif (pthread_mutexattr_init(&attr) != 0) {\n\t\treturn true;\n\t}\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\tif (config_debug) {\n\t\tmutex->lock_order = lock_order;\n\t\tif (lock_order == malloc_mutex_address_ordered) {\n\t\t\twitness_init(&mutex->witness, name, rank,\n\t\t\t    mutex_addr_comp, mutex);\n\t\t} else {\n\t\t\twitness_init(&mutex->witness, name, rank, NULL, NULL);\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid\nmalloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tmalloc_mutex_lock(tsdn, mutex);\n}\n\nvoid\nmalloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tmalloc_mutex_unlock(tsdn, mutex);\n}\n\nvoid\nmalloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tmalloc_mutex_unlock(tsdn, mutex);\n#else\n\tif (malloc_mutex_init(mutex, mutex->witness.name,\n\t    mutex->witness.rank, mutex->lock_order)) {\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\t}\n\t}\n#endif\n}\n\nbool\nmalloc_mutex_boot(void) {\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    bootstrap_calloc) != 0) {\n\t\t\treturn true;\n\t\t}\n\t\tpostponed_mutexes = postponed_mutexes->postponed_next;\n\t}\n#endif\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/mutex_pool.c",
    "content": "#define JEMALLOC_MUTEX_POOL_C_\n\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n\nbool\nmutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank) {\n\tfor (int i = 0; i < MUTEX_POOL_SIZE; ++i) {\n\t\tif (malloc_mutex_init(&pool->mutexes[i], name, rank,\n\t\t    malloc_mutex_address_ordered)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/nstime.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/nstime.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n#define BILLION\tUINT64_C(1000000000)\n#define MILLION\tUINT64_C(1000000)\n\nvoid\nnstime_init(nstime_t *time, uint64_t ns) {\n\ttime->ns = ns;\n}\n\nvoid\nnstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) {\n\ttime->ns = sec * BILLION + nsec;\n}\n\nuint64_t\nnstime_ns(const nstime_t *time) {\n\treturn time->ns;\n}\n\nuint64_t\nnstime_msec(const nstime_t *time) {\n\treturn time->ns / MILLION;\n}\n\nuint64_t\nnstime_sec(const nstime_t *time) {\n\treturn time->ns / BILLION;\n}\n\nuint64_t\nnstime_nsec(const nstime_t *time) {\n\treturn time->ns % BILLION;\n}\n\nvoid\nnstime_copy(nstime_t *time, const nstime_t *source) {\n\t*time = *source;\n}\n\nint\nnstime_compare(const nstime_t *a, const nstime_t *b) {\n\treturn (a->ns > b->ns) - (a->ns < b->ns);\n}\n\nvoid\nnstime_add(nstime_t *time, const nstime_t *addend) {\n\tassert(UINT64_MAX - time->ns >= addend->ns);\n\n\ttime->ns += addend->ns;\n}\n\nvoid\nnstime_iadd(nstime_t *time, uint64_t addend) {\n\tassert(UINT64_MAX - time->ns >= addend);\n\n\ttime->ns += addend;\n}\n\nvoid\nnstime_subtract(nstime_t *time, const nstime_t *subtrahend) {\n\tassert(nstime_compare(time, subtrahend) >= 0);\n\n\ttime->ns -= subtrahend->ns;\n}\n\nvoid\nnstime_isubtract(nstime_t *time, uint64_t subtrahend) {\n\tassert(time->ns >= subtrahend);\n\n\ttime->ns -= subtrahend;\n}\n\nvoid\nnstime_imultiply(nstime_t *time, uint64_t multiplier) {\n\tassert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) <<\n\t    2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns));\n\n\ttime->ns *= multiplier;\n}\n\nvoid\nnstime_idivide(nstime_t *time, uint64_t divisor) {\n\tassert(divisor != 0);\n\n\ttime->ns /= divisor;\n}\n\nuint64_t\nnstime_divide(const nstime_t *time, const nstime_t *divisor) {\n\tassert(divisor->ns != 0);\n\n\treturn time->ns / divisor->ns;\n}\n\n#ifdef _WIN32\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tFILETIME ft;\n\tuint64_t ticks_100ns;\n\n\tGetSystemTimeAsFileTime(&ft);\n\tticks_100ns = (((uint64_t)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;\n\n\tnstime_init(time, ticks_100ns * 100);\n}\n#elif defined(JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE)\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC_COARSE, &ts);\n\tnstime_init2(time, ts.tv_sec, ts.tv_nsec);\n}\n#elif defined(JEMALLOC_HAVE_CLOCK_MONOTONIC)\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\n\tnstime_init2(time, ts.tv_sec, ts.tv_nsec);\n}\n#elif defined(JEMALLOC_HAVE_MACH_ABSOLUTE_TIME)\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tnstime_init(time, mach_absolute_time());\n}\n#else\n#  define NSTIME_MONOTONIC false\nstatic void\nnstime_get(nstime_t *time) {\n\tstruct timeval tv;\n\n\tgettimeofday(&tv, NULL);\n\tnstime_init2(time, tv.tv_sec, tv.tv_usec * 1000);\n}\n#endif\n\nstatic bool\nnstime_monotonic_impl(void) {\n\treturn NSTIME_MONOTONIC;\n#undef NSTIME_MONOTONIC\n}\nnstime_monotonic_t *JET_MUTABLE nstime_monotonic = nstime_monotonic_impl;\n\nstatic bool\nnstime_update_impl(nstime_t *time) {\n\tnstime_t old_time;\n\n\tnstime_copy(&old_time, time);\n\tnstime_get(time);\n\n\t/* Handle non-monotonic clocks. */\n\tif (unlikely(nstime_compare(&old_time, time) > 0)) {\n\t\tnstime_copy(time, &old_time);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\nnstime_update_t *JET_MUTABLE nstime_update = nstime_update_impl;\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/pages.c",
    "content": "#define JEMALLOC_PAGES_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n#include \"jemalloc/internal/pages.h\"\n\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n\n#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT\n#include <sys/sysctl.h>\n#ifdef __FreeBSD__\n#include <vm/vm_param.h>\n#endif\n#endif\n\n/******************************************************************************/\n/* Data. */\n\n/* Actual operating system page size, detected during bootstrap, <= PAGE. */\nstatic size_t\tos_page;\n\n#ifndef _WIN32\n#  define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE)\n#  define PAGES_PROT_DECOMMIT (PROT_NONE)\nstatic int\tmmap_flags;\n#endif\nstatic bool\tos_overcommits;\n\nconst char *thp_mode_names[] = {\n\t\"default\",\n\t\"always\",\n\t\"never\",\n\t\"not supported\"\n};\nthp_mode_t opt_thp = THP_MODE_DEFAULT;\nthp_mode_t init_system_thp_mode;\n\n/* Runtime support for lazy purge. Irrelevant when !pages_can_purge_lazy. */\nstatic bool pages_can_purge_lazy_runtime = true;\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic void os_pages_unmap(void *addr, size_t size);\n\n/******************************************************************************/\n\nstatic void *\nos_pages_map(void *addr, size_t size, size_t alignment, bool *commit) {\n\tassert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);\n\tassert(ALIGNMENT_CEILING(size, os_page) == size);\n\tassert(size != 0);\n\n\tif (os_overcommits) {\n\t\t*commit = true;\n\t}\n\n\tvoid *ret;\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_RESERVE | (*commit ? MEM_COMMIT : 0),\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\t{\n\t\tint prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;\n\n\t\tret = mmap(addr, size, prot, mmap_flags, -1, 0);\n\t}\n\tassert(ret != NULL);\n\n\tif (ret == MAP_FAILED) {\n\t\tret = NULL;\n\t} else 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\tos_pages_unmap(ret, size);\n\t\tret = NULL;\n\t}\n#endif\n\tassert(ret == NULL || (addr == NULL && ret != addr) || (addr != NULL &&\n\t    ret == addr));\n\treturn ret;\n}\n\nstatic void *\nos_pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size,\n    bool *commit) {\n\tvoid *ret = (void *)((uintptr_t)addr + leadsize);\n\n\tassert(alloc_size >= leadsize + size);\n#ifdef _WIN32\n\tos_pages_unmap(addr, alloc_size);\n\tvoid *new_addr = os_pages_map(ret, size, PAGE, commit);\n\tif (new_addr == ret) {\n\t\treturn ret;\n\t}\n\tif (new_addr != NULL) {\n\t\tos_pages_unmap(new_addr, size);\n\t}\n\treturn NULL;\n#else\n\tsize_t trailsize = alloc_size - leadsize - size;\n\n\tif (leadsize != 0) {\n\t\tos_pages_unmap(addr, leadsize);\n\t}\n\tif (trailsize != 0) {\n\t\tos_pages_unmap((void *)((uintptr_t)ret + size), trailsize);\n\t}\n\treturn ret;\n#endif\n}\n\nstatic void\nos_pages_unmap(void *addr, size_t size) {\n\tassert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);\n\tassert(ALIGNMENT_CEILING(size, os_page) == size);\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(get_errno(), 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\t}\n\t}\n}\n\nstatic void *\npages_map_slow(size_t size, size_t alignment, bool *commit) {\n\tsize_t alloc_size = size + alignment - os_page;\n\t/* Beware size_t wrap-around. */\n\tif (alloc_size < size) {\n\t\treturn NULL;\n\t}\n\n\tvoid *ret;\n\tdo {\n\t\tvoid *pages = os_pages_map(NULL, alloc_size, alignment, commit);\n\t\tif (pages == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tsize_t leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment)\n\t\t    - (uintptr_t)pages;\n\t\tret = os_pages_trim(pages, alloc_size, leadsize, size, commit);\n\t} while (ret == NULL);\n\n\tassert(ret != NULL);\n\tassert(PAGE_ADDR2BASE(ret) == ret);\n\treturn ret;\n}\n\nvoid *\npages_map(void *addr, size_t size, size_t alignment, bool *commit) {\n\tassert(alignment >= PAGE);\n\tassert(ALIGNMENT_ADDR2BASE(addr, alignment) == addr);\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 * os_pages_unmap(), and it can leave holes in the process's virtual\n\t * memory map if memory grows downward.\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\tvoid *ret = os_pages_map(addr, size, os_page, commit);\n\tif (ret == NULL || ret == addr) {\n\t\treturn ret;\n\t}\n\tassert(addr == NULL);\n\tif (ALIGNMENT_ADDR2OFFSET(ret, alignment) != 0) {\n\t\tos_pages_unmap(ret, size);\n\t\treturn pages_map_slow(size, alignment, commit);\n\t}\n\n\tassert(PAGE_ADDR2BASE(ret) == ret);\n\treturn ret;\n}\n\nvoid\npages_unmap(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tos_pages_unmap(addr, size);\n}\n\nstatic bool\npages_commit_impl(void *addr, size_t size, bool commit) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tif (os_overcommits) {\n\t\treturn true;\n\t}\n\n#ifdef _WIN32\n\treturn (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT,\n\t    PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT)));\n#else\n\t{\n\t\tint prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;\n\t\tvoid *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED,\n\t\t    -1, 0);\n\t\tif (result == MAP_FAILED) {\n\t\t\treturn true;\n\t\t}\n\t\tif (result != addr) {\n\t\t\t/*\n\t\t\t * We succeeded in mapping memory, but not in the right\n\t\t\t * place.\n\t\t\t */\n\t\t\tos_pages_unmap(result, size);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n#endif\n}\n\nbool\npages_commit(void *addr, size_t size) {\n\treturn pages_commit_impl(addr, size, true);\n}\n\nbool\npages_decommit(void *addr, size_t size) {\n\treturn pages_commit_impl(addr, size, false);\n}\n\nbool\npages_purge_lazy(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tif (!pages_can_purge_lazy) {\n\t\treturn true;\n\t}\n\tif (!pages_can_purge_lazy_runtime) {\n\t\t/*\n\t\t * Built with lazy purge enabled, but detected it was not\n\t\t * supported on the current system.\n\t\t */\n\t\treturn true;\n\t}\n\n#ifdef _WIN32\n\tVirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE);\n\treturn false;\n#elif defined(JEMALLOC_PURGE_MADVISE_FREE)\n\treturn (madvise(addr, size,\n#  ifdef MADV_FREE\n\t    MADV_FREE\n#  else\n\t    JEMALLOC_MADV_FREE\n#  endif\n\t    ) != 0);\n#elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \\\n    !defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)\n\treturn (madvise(addr, size, MADV_DONTNEED) != 0);\n#else\n\tnot_reached();\n#endif\n}\n\nbool\npages_purge_forced(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tif (!pages_can_purge_forced) {\n\t\treturn true;\n\t}\n\n#if defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \\\n    defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)\n\treturn (madvise(addr, size, MADV_DONTNEED) != 0);\n#elif defined(JEMALLOC_MAPS_COALESCE)\n\t/* Try to overlay a new demand-zeroed mapping. */\n\treturn pages_commit(addr, size);\n#else\n\tnot_reached();\n#endif\n}\n\nstatic bool\npages_huge_impl(void *addr, size_t size, bool aligned) {\n\tif (aligned) {\n\t\tassert(HUGEPAGE_ADDR2BASE(addr) == addr);\n\t\tassert(HUGEPAGE_CEILING(size) == size);\n\t}\n#ifdef JEMALLOC_HAVE_MADVISE_HUGE\n\treturn (madvise(addr, size, MADV_HUGEPAGE) != 0);\n#else\n\treturn true;\n#endif\n}\n\nbool\npages_huge(void *addr, size_t size) {\n\treturn pages_huge_impl(addr, size, true);\n}\n\nstatic bool\npages_huge_unaligned(void *addr, size_t size) {\n\treturn pages_huge_impl(addr, size, false);\n}\n\nstatic bool\npages_nohuge_impl(void *addr, size_t size, bool aligned) {\n\tif (aligned) {\n\t\tassert(HUGEPAGE_ADDR2BASE(addr) == addr);\n\t\tassert(HUGEPAGE_CEILING(size) == size);\n\t}\n\n#ifdef JEMALLOC_HAVE_MADVISE_HUGE\n\treturn (madvise(addr, size, MADV_NOHUGEPAGE) != 0);\n#else\n\treturn false;\n#endif\n}\n\nbool\npages_nohuge(void *addr, size_t size) {\n\treturn pages_nohuge_impl(addr, size, true);\n}\n\nstatic bool\npages_nohuge_unaligned(void *addr, size_t size) {\n\treturn pages_nohuge_impl(addr, size, false);\n}\n\nbool\npages_dontdump(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n#ifdef JEMALLOC_MADVISE_DONTDUMP\n\treturn madvise(addr, size, MADV_DONTDUMP) != 0;\n#else\n\treturn false;\n#endif\n}\n\nbool\npages_dodump(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n#ifdef JEMALLOC_MADVISE_DONTDUMP\n\treturn madvise(addr, size, MADV_DODUMP) != 0;\n#else\n\treturn false;\n#endif\n}\n\n\nstatic size_t\nos_page_detect(void) {\n#ifdef _WIN32\n\tSYSTEM_INFO si;\n\tGetSystemInfo(&si);\n\treturn si.dwPageSize;\n#elif defined(__FreeBSD__)\n\treturn getpagesize();\n#else\n\tlong result = sysconf(_SC_PAGESIZE);\n\tif (result == -1) {\n\t\treturn LG_PAGE;\n\t}\n\treturn (size_t)result;\n#endif\n}\n\n#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT\nstatic bool\nos_overcommits_sysctl(void) {\n\tint vm_overcommit;\n\tsize_t sz;\n\n\tsz = sizeof(vm_overcommit);\n#if defined(__FreeBSD__) && defined(VM_OVERCOMMIT)\n\tint mib[2];\n\n\tmib[0] = CTL_VM;\n\tmib[1] = VM_OVERCOMMIT;\n\tif (sysctl(mib, 2, &vm_overcommit, &sz, NULL, 0) != 0) {\n\t\treturn false; /* Error. */\n\t}\n#else\n\tif (sysctlbyname(\"vm.overcommit\", &vm_overcommit, &sz, NULL, 0) != 0) {\n\t\treturn false; /* Error. */\n\t}\n#endif\n\n\treturn ((vm_overcommit & 0x3) == 0);\n}\n#endif\n\n#ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY\n/*\n * Use syscall(2) rather than {open,read,close}(2) when possible to avoid\n * reentry during bootstrapping if another library has interposed system call\n * wrappers.\n */\nstatic bool\nos_overcommits_proc(void) {\n\tint fd;\n\tchar buf[1];\n\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)\n\t#if defined(O_CLOEXEC)\n\t\tfd = (int)syscall(SYS_open, \"/proc/sys/vm/overcommit_memory\", O_RDONLY |\n\t\t\tO_CLOEXEC);\n\t#else\n\t\tfd = (int)syscall(SYS_open, \"/proc/sys/vm/overcommit_memory\", O_RDONLY);\n\t\tif (fd != -1) {\n\t\t\tfcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);\n\t\t}\n\t#endif\n#elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat)\n\t#if defined(O_CLOEXEC)\n\t\tfd = (int)syscall(SYS_openat,\n\t\t\tAT_FDCWD, \"/proc/sys/vm/overcommit_memory\", O_RDONLY | O_CLOEXEC);\n\t#else\n\t\tfd = (int)syscall(SYS_openat,\n\t\t\tAT_FDCWD, \"/proc/sys/vm/overcommit_memory\", O_RDONLY);\n\t\tif (fd != -1) {\n\t\t\tfcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);\n\t\t}\n\t#endif\n#else\n\t#if defined(O_CLOEXEC)\n\t\tfd = open(\"/proc/sys/vm/overcommit_memory\", O_RDONLY | O_CLOEXEC);\n\t#else\n\t\tfd = open(\"/proc/sys/vm/overcommit_memory\", O_RDONLY);\n\t\tif (fd != -1) {\n\t\t\tfcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);\n\t\t}\n\t#endif\n#endif\n\n\tif (fd == -1) {\n\t\treturn false; /* Error. */\n\t}\n\n\tssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf));\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close)\n\tsyscall(SYS_close, fd);\n#else\n\tclose(fd);\n#endif\n\n\tif (nread < 1) {\n\t\treturn false; /* Error. */\n\t}\n\t/*\n\t * /proc/sys/vm/overcommit_memory meanings:\n\t * 0: Heuristic overcommit.\n\t * 1: Always overcommit.\n\t * 2: Never overcommit.\n\t */\n\treturn (buf[0] == '0' || buf[0] == '1');\n}\n#endif\n\nvoid\npages_set_thp_state (void *ptr, size_t size) {\n\tif (opt_thp == thp_mode_default || opt_thp == init_system_thp_mode) {\n\t\treturn;\n\t}\n\tassert(opt_thp != thp_mode_not_supported &&\n\t    init_system_thp_mode != thp_mode_not_supported);\n\n\tif (opt_thp == thp_mode_always\n\t    && init_system_thp_mode != thp_mode_never) {\n\t\tassert(init_system_thp_mode == thp_mode_default);\n\t\tpages_huge_unaligned(ptr, size);\n\t} else if (opt_thp == thp_mode_never) {\n\t\tassert(init_system_thp_mode == thp_mode_default ||\n\t\t    init_system_thp_mode == thp_mode_always);\n\t\tpages_nohuge_unaligned(ptr, size);\n\t}\n}\n\nstatic void\ninit_thp_state(void) {\n\tif (!have_madvise_huge) {\n\t\tif (metadata_thp_enabled() && opt_abort) {\n\t\t\tmalloc_write(\"<jemalloc>: no MADV_HUGEPAGE support\\n\");\n\t\t\tabort();\n\t\t}\n\t\tgoto label_error;\n\t}\n\n\tstatic const char sys_state_madvise[] = \"always [madvise] never\\n\";\n\tstatic const char sys_state_always[] = \"[always] madvise never\\n\";\n\tstatic const char sys_state_never[] = \"always madvise [never]\\n\";\n\tchar buf[sizeof(sys_state_madvise)];\n\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)\n\tint fd = (int)syscall(SYS_open,\n\t    \"/sys/kernel/mm/transparent_hugepage/enabled\", O_RDONLY);\n#else\n\tint fd = open(\"/sys/kernel/mm/transparent_hugepage/enabled\", O_RDONLY);\n#endif\n\tif (fd == -1) {\n\t\tgoto label_error;\n\t}\n\n\tssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf));\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close)\n\tsyscall(SYS_close, fd);\n#else\n\tclose(fd);\n#endif\n\n\tif (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) {\n\t\tinit_system_thp_mode = thp_mode_default;\n\t} else if (strncmp(buf, sys_state_always, (size_t)nread) == 0) {\n\t\tinit_system_thp_mode = thp_mode_always;\n\t} else if (strncmp(buf, sys_state_never, (size_t)nread) == 0) {\n\t\tinit_system_thp_mode = thp_mode_never;\n\t} else {\n\t\tgoto label_error;\n\t}\n\treturn;\nlabel_error:\n\topt_thp = init_system_thp_mode = thp_mode_not_supported;\n}\n\nbool\npages_boot(void) {\n\tos_page = os_page_detect();\n\tif (os_page > PAGE) {\n\t\tmalloc_write(\"<jemalloc>: Unsupported system page size\\n\");\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t\treturn true;\n\t}\n\n#ifndef _WIN32\n\tmmap_flags = MAP_PRIVATE | MAP_ANON;\n#endif\n\n#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT\n\tos_overcommits = os_overcommits_sysctl();\n#elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY)\n\tos_overcommits = os_overcommits_proc();\n#  ifdef MAP_NORESERVE\n\tif (os_overcommits) {\n\t\tmmap_flags |= MAP_NORESERVE;\n\t}\n#  endif\n#else\n\tos_overcommits = false;\n#endif\n\n\tinit_thp_state();\n\n\t/* Detect lazy purge runtime support. */\n\tif (pages_can_purge_lazy) {\n\t\tbool committed = false;\n\t\tvoid *madv_free_page = os_pages_map(NULL, PAGE, PAGE, &committed);\n\t\tif (madv_free_page == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tassert(pages_can_purge_lazy_runtime);\n\t\tif (pages_purge_lazy(madv_free_page, PAGE)) {\n\t\t\tpages_can_purge_lazy_runtime = false;\n\t\t}\n\t\tos_pages_unmap(madv_free_page, PAGE);\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/prng.c",
    "content": "#define JEMALLOC_PRNG_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/prof.c",
    "content": "#define JEMALLOC_PROF_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex.h\"\n\n/******************************************************************************/\n\n#ifdef JEMALLOC_PROF_LIBUNWIND\n#define UNW_LOCAL_ONLY\n#include <libunwind.h>\n#endif\n\n#ifdef JEMALLOC_PROF_LIBGCC\n/*\n * We have a circular dependency -- jemalloc_internal.h tells us if we should\n * use libgcc's unwinding functionality, but after we've included that, we've\n * already hooked _Unwind_Backtrace.  We'll temporarily disable hooking.\n */\n#undef _Unwind_Backtrace\n#include <unwind.h>\n#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)\n#endif\n\n/******************************************************************************/\n/* Data. */\n\nbool\t\topt_prof = false;\nbool\t\topt_prof_active = true;\nbool\t\topt_prof_thread_active_init = 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 = false;\nbool\t\topt_prof_leak = false;\nbool\t\topt_prof_accum = false;\nchar\t\topt_prof_prefix[\n    /* Minimize memory bloat for non-prof builds. */\n#ifdef JEMALLOC_PROF\n    PATH_MAX +\n#endif\n    1];\n\n/*\n * Initialized as opt_prof_active, and accessed via\n * prof_active_[gs]et{_unlocked,}().\n */\nbool\t\t\tprof_active;\nstatic malloc_mutex_t\tprof_active_mtx;\n\n/*\n * Initialized as opt_prof_thread_active_init, and accessed via\n * prof_thread_active_init_[gs]et().\n */\nstatic bool\t\tprof_thread_active_init;\nstatic malloc_mutex_t\tprof_thread_active_init_mtx;\n\n/*\n * Initialized as opt_prof_gdump, and accessed via\n * prof_gdump_[gs]et{_unlocked,}().\n */\nbool\t\t\tprof_gdump_val;\nstatic malloc_mutex_t\tprof_gdump_mtx;\n\nuint64_t\tprof_interval = 0;\n\nsize_t\t\tlg_prof_sample;\n\n/*\n * Table of mutexes that are shared among gctx's.  These are leaf locks, so\n * there is no problem with using them for more than one gctx at the same time.\n * The primary motivation for this sharing though is that gctx's are ephemeral,\n * and destroying mutexes causes complications for systems that allocate when\n * creating/destroying mutexes.\n */\nstatic malloc_mutex_t\t*gctx_locks;\nstatic atomic_u_t\tcum_gctxs; /* Atomic counter. */\n\n/*\n * Table of mutexes that are shared among tdata's.  No operations require\n * holding multiple tdata locks, so there is no problem with using them for more\n * than one tdata at the same time, even though a gctx lock may be acquired\n * while holding a tdata lock.\n */\nstatic malloc_mutex_t\t*tdata_locks;\n\n/*\n * Global hash of (prof_bt_t *)-->(prof_gctx_t *).  This is the master data\n * structure that knows about all backtraces currently captured.\n */\nstatic ckh_t\t\tbt2gctx;\n/* Non static to enable profiling. */\nmalloc_mutex_t\t\tbt2gctx_mtx;\n\n/*\n * Tree of all extant prof_tdata_t structures, regardless of state,\n * {attached,detached,expired}.\n */\nstatic prof_tdata_tree_t\ttdatas;\nstatic malloc_mutex_t\ttdatas_mtx;\n\nstatic uint64_t\t\tnext_thr_uid;\nstatic malloc_mutex_t\tnext_thr_uid_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.\n */\nstatic malloc_mutex_t\tprof_dump_mtx;\nstatic char\t\tprof_dump_buf[\n    /* Minimize memory bloat for non-prof builds. */\n#ifdef JEMALLOC_PROF\n    PROF_DUMP_BUFSIZE\n#else\n    1\n#endif\n];\nstatic size_t\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/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic bool\tprof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx);\nstatic void\tprof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx);\nstatic bool\tprof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,\n    bool even_if_attached);\nstatic void\tprof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata,\n    bool even_if_attached);\nstatic char\t*prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name);\n\n/******************************************************************************/\n/* Red-black trees. */\n\nstatic int\nprof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) {\n\tuint64_t a_thr_uid = a->thr_uid;\n\tuint64_t b_thr_uid = b->thr_uid;\n\tint ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);\n\tif (ret == 0) {\n\t\tuint64_t a_thr_discrim = a->thr_discrim;\n\t\tuint64_t b_thr_discrim = b->thr_discrim;\n\t\tret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <\n\t\t    b_thr_discrim);\n\t\tif (ret == 0) {\n\t\t\tuint64_t a_tctx_uid = a->tctx_uid;\n\t\t\tuint64_t b_tctx_uid = b->tctx_uid;\n\t\t\tret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <\n\t\t\t    b_tctx_uid);\n\t\t}\n\t}\n\treturn ret;\n}\n\nrb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,\n    tctx_link, prof_tctx_comp)\n\nstatic int\nprof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) {\n\tunsigned a_len = a->bt.len;\n\tunsigned b_len = b->bt.len;\n\tunsigned comp_len = (a_len < b_len) ? a_len : b_len;\n\tint ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *));\n\tif (ret == 0) {\n\t\tret = (a_len > b_len) - (a_len < b_len);\n\t}\n\treturn ret;\n}\n\nrb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link,\n    prof_gctx_comp)\n\nstatic int\nprof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) {\n\tint ret;\n\tuint64_t a_uid = a->thr_uid;\n\tuint64_t b_uid = b->thr_uid;\n\n\tret = ((a_uid > b_uid) - (a_uid < b_uid));\n\tif (ret == 0) {\n\t\tuint64_t a_discrim = a->thr_discrim;\n\t\tuint64_t b_discrim = b->thr_discrim;\n\n\t\tret = ((a_discrim > b_discrim) - (a_discrim < b_discrim));\n\t}\n\treturn ret;\n}\n\nrb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link,\n    prof_tdata_comp)\n\n/******************************************************************************/\n\nvoid\nprof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\tif (updated) {\n\t\t/*\n\t\t * Compute a new sample threshold.  This isn't very important in\n\t\t * practice, because this function is rarely executed, so the\n\t\t * potential for sample bias is minimal except in contrived\n\t\t * programs.\n\t\t */\n\t\ttdata = prof_tdata_get(tsd, true);\n\t\tif (tdata != NULL) {\n\t\t\tprof_sample_threshold_update(tdata);\n\t\t}\n\t}\n\n\tif ((uintptr_t)tctx > (uintptr_t)1U) {\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);\n\t\ttctx->prepared = false;\n\t\tif (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {\n\t\t\tprof_tctx_destroy(tsd, tctx);\n\t\t} else {\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);\n\t\t}\n\t}\n}\n\nvoid\nprof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,\n    prof_tctx_t *tctx) {\n\tprof_tctx_set(tsdn, ptr, usize, NULL, tctx);\n\n\tmalloc_mutex_lock(tsdn, tctx->tdata->lock);\n\ttctx->cnts.curobjs++;\n\ttctx->cnts.curbytes += usize;\n\tif (opt_prof_accum) {\n\t\ttctx->cnts.accumobjs++;\n\t\ttctx->cnts.accumbytes += usize;\n\t}\n\ttctx->prepared = false;\n\tmalloc_mutex_unlock(tsdn, tctx->tdata->lock);\n}\n\nvoid\nprof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);\n\tassert(tctx->cnts.curobjs > 0);\n\tassert(tctx->cnts.curbytes >= usize);\n\ttctx->cnts.curobjs--;\n\ttctx->cnts.curbytes -= usize;\n\n\tif (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {\n\t\tprof_tctx_destroy(tsd, tctx);\n\t} else {\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);\n\t}\n}\n\nvoid\nbt_init(prof_bt_t *bt, void **vec) {\n\tcassert(config_prof);\n\n\tbt->vec = vec;\n\tbt->len = 0;\n}\n\nstatic void\nprof_enter(tsd_t *tsd, prof_tdata_t *tdata) {\n\tcassert(config_prof);\n\tassert(tdata == prof_tdata_get(tsd, false));\n\n\tif (tdata != NULL) {\n\t\tassert(!tdata->enq);\n\t\ttdata->enq = true;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);\n}\n\nstatic void\nprof_leave(tsd_t *tsd, prof_tdata_t *tdata) {\n\tcassert(config_prof);\n\tassert(tdata == prof_tdata_get(tsd, false));\n\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);\n\n\tif (tdata != NULL) {\n\t\tbool idump, gdump;\n\n\t\tassert(tdata->enq);\n\t\ttdata->enq = false;\n\t\tidump = tdata->enq_idump;\n\t\ttdata->enq_idump = false;\n\t\tgdump = tdata->enq_gdump;\n\t\ttdata->enq_gdump = false;\n\n\t\tif (idump) {\n\t\t\tprof_idump(tsd_tsdn(tsd));\n\t\t}\n\t\tif (gdump) {\n\t\t\tprof_gdump(tsd_tsdn(tsd));\n\t\t}\n\t}\n}\n\n#ifdef JEMALLOC_PROF_LIBUNWIND\nvoid\nprof_backtrace(prof_bt_t *bt) {\n\tint nframes;\n\n\tcassert(config_prof);\n\tassert(bt->len == 0);\n\tassert(bt->vec != NULL);\n\n\tnframes = unw_backtrace(bt->vec, PROF_BT_MAX);\n\tif (nframes <= 0) {\n\t\treturn;\n\t}\n\tbt->len = nframes;\n}\n#elif (defined(JEMALLOC_PROF_LIBGCC))\nstatic _Unwind_Reason_Code\nprof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {\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\tprof_unwind_data_t *data = (prof_unwind_data_t *)arg;\n\tvoid *ip;\n\n\tcassert(config_prof);\n\n\tip = (void *)_Unwind_GetIP(context);\n\tif (ip == NULL) {\n\t\treturn _URC_END_OF_STACK;\n\t}\n\tdata->bt->vec[data->bt->len] = ip;\n\tdata->bt->len++;\n\tif (data->bt->len == data->max) {\n\t\treturn _URC_END_OF_STACK;\n\t}\n\n\treturn _URC_NO_REASON;\n}\n\nvoid\nprof_backtrace(prof_bt_t *bt) {\n\tprof_unwind_data_t data = {bt, 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) {\n#define BT_FRAME(i)\t\t\t\t\t\t\t\\\n\tif ((i) < PROF_BT_MAX) {\t\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\t}\t\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\\\n\t\t\treturn;\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tbt->vec[(i)] = p;\t\t\t\t\t\\\n\t\tbt->len = (i) + 1;\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}\n\n\tcassert(config_prof);\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#undef BT_FRAME\n}\n#else\nvoid\nprof_backtrace(prof_bt_t *bt) {\n\tcassert(config_prof);\n\tnot_reached();\n}\n#endif\n\nstatic malloc_mutex_t *\nprof_gctx_mutex_choose(void) {\n\tunsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED);\n\n\treturn &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS];\n}\n\nstatic malloc_mutex_t *\nprof_tdata_mutex_choose(uint64_t thr_uid) {\n\treturn &tdata_locks[thr_uid % PROF_NTDATA_LOCKS];\n}\n\nstatic prof_gctx_t *\nprof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) {\n\t/*\n\t * Create a single allocation that has space for vec of length bt->len.\n\t */\n\tsize_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *));\n\tprof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size,\n\t    sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true),\n\t    true);\n\tif (gctx == NULL) {\n\t\treturn NULL;\n\t}\n\tgctx->lock = prof_gctx_mutex_choose();\n\t/*\n\t * Set nlimbo to 1, in order to avoid a race condition with\n\t * prof_tctx_destroy()/prof_gctx_try_destroy().\n\t */\n\tgctx->nlimbo = 1;\n\ttctx_tree_new(&gctx->tctxs);\n\t/* Duplicate bt. */\n\tmemcpy(gctx->vec, bt->vec, bt->len * sizeof(void *));\n\tgctx->bt.vec = gctx->vec;\n\tgctx->bt.len = bt->len;\n\treturn gctx;\n}\n\nstatic void\nprof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx,\n    prof_tdata_t *tdata) {\n\tcassert(config_prof);\n\n\t/*\n\t * Check that gctx is still unused by any thread cache before destroying\n\t * it.  prof_lookup() increments gctx->nlimbo in order to avoid a race\n\t * condition with this function, as does prof_tctx_destroy() in order to\n\t * avoid a race between the main body of prof_tctx_destroy() and entry\n\t * into this function.\n\t */\n\tprof_enter(tsd, tdata_self);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\tassert(gctx->nlimbo != 0);\n\tif (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {\n\t\t/* Remove gctx from bt2gctx. */\n\t\tif (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) {\n\t\t\tnot_reached();\n\t\t}\n\t\tprof_leave(tsd, tdata_self);\n\t\t/* Destroy gctx. */\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\tidalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true);\n\t} else {\n\t\t/*\n\t\t * Compensate for increment in prof_tctx_destroy() or\n\t\t * prof_lookup().\n\t\t */\n\t\tgctx->nlimbo--;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\tprof_leave(tsd, tdata_self);\n\t}\n}\n\nstatic bool\nprof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx) {\n\tmalloc_mutex_assert_owner(tsdn, tctx->tdata->lock);\n\n\tif (opt_prof_accum) {\n\t\treturn false;\n\t}\n\tif (tctx->cnts.curobjs != 0) {\n\t\treturn false;\n\t}\n\tif (tctx->prepared) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool\nprof_gctx_should_destroy(prof_gctx_t *gctx) {\n\tif (opt_prof_accum) {\n\t\treturn false;\n\t}\n\tif (!tctx_tree_empty(&gctx->tctxs)) {\n\t\treturn false;\n\t}\n\tif (gctx->nlimbo != 0) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic void\nprof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) {\n\tprof_tdata_t *tdata = tctx->tdata;\n\tprof_gctx_t *gctx = tctx->gctx;\n\tbool destroy_tdata, destroy_tctx, destroy_gctx;\n\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);\n\n\tassert(tctx->cnts.curobjs == 0);\n\tassert(tctx->cnts.curbytes == 0);\n\tassert(!opt_prof_accum);\n\tassert(tctx->cnts.accumobjs == 0);\n\tassert(tctx->cnts.accumbytes == 0);\n\n\tckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL);\n\tdestroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata, false);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_nominal:\n\t\ttctx_tree_remove(&gctx->tctxs, tctx);\n\t\tdestroy_tctx = true;\n\t\tif (prof_gctx_should_destroy(gctx)) {\n\t\t\t/*\n\t\t\t * Increment gctx->nlimbo in order to keep another\n\t\t\t * thread from winning the race to destroy gctx while\n\t\t\t * this one has gctx->lock dropped.  Without this, it\n\t\t\t * would be possible for another thread to:\n\t\t\t *\n\t\t\t * 1) Sample an allocation associated with gctx.\n\t\t\t * 2) Deallocate the sampled object.\n\t\t\t * 3) Successfully prof_gctx_try_destroy(gctx).\n\t\t\t *\n\t\t\t * The result would be that gctx no longer exists by the\n\t\t\t * time this thread accesses it in\n\t\t\t * prof_gctx_try_destroy().\n\t\t\t */\n\t\t\tgctx->nlimbo++;\n\t\t\tdestroy_gctx = true;\n\t\t} else {\n\t\t\tdestroy_gctx = false;\n\t\t}\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\t\t/*\n\t\t * A dumping thread needs tctx to remain valid until dumping\n\t\t * has finished.  Change state such that the dumping thread will\n\t\t * complete destruction during a late dump iteration phase.\n\t\t */\n\t\ttctx->state = prof_tctx_state_purgatory;\n\t\tdestroy_tctx = false;\n\t\tdestroy_gctx = false;\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t\tdestroy_tctx = false;\n\t\tdestroy_gctx = false;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\tif (destroy_gctx) {\n\t\tprof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx,\n\t\t    tdata);\n\t}\n\n\tmalloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock);\n\n\tif (destroy_tdata) {\n\t\tprof_tdata_destroy(tsd, tdata, false);\n\t}\n\n\tif (destroy_tctx) {\n\t\tidalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true);\n\t}\n}\n\nstatic bool\nprof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,\n    void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) {\n\tunion {\n\t\tprof_gctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} gctx, tgctx;\n\tunion {\n\t\tprof_bt_t\t*p;\n\t\tvoid\t\t*v;\n\t} btkey;\n\tbool new_gctx;\n\n\tprof_enter(tsd, tdata);\n\tif (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {\n\t\t/* bt has never been seen before.  Insert it. */\n\t\tprof_leave(tsd, tdata);\n\t\ttgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt);\n\t\tif (tgctx.v == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tprof_enter(tsd, tdata);\n\t\tif (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {\n\t\t\tgctx.p = tgctx.p;\n\t\t\tbtkey.p = &gctx.p->bt;\n\t\t\tif (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {\n\t\t\t\t/* OOM. */\n\t\t\t\tprof_leave(tsd, tdata);\n\t\t\t\tidalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL,\n\t\t\t\t    true, true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tnew_gctx = true;\n\t\t} else {\n\t\t\tnew_gctx = false;\n\t\t}\n\t} else {\n\t\ttgctx.v = NULL;\n\t\tnew_gctx = false;\n\t}\n\n\tif (!new_gctx) {\n\t\t/*\n\t\t * Increment nlimbo, in order to avoid a race condition with\n\t\t * prof_tctx_destroy()/prof_gctx_try_destroy().\n\t\t */\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock);\n\t\tgctx.p->nlimbo++;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock);\n\t\tnew_gctx = false;\n\n\t\tif (tgctx.v != NULL) {\n\t\t\t/* Lost race to insert. */\n\t\t\tidalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true,\n\t\t\t    true);\n\t\t}\n\t}\n\tprof_leave(tsd, tdata);\n\n\t*p_btkey = btkey.v;\n\t*p_gctx = gctx.p;\n\t*p_new_gctx = new_gctx;\n\treturn false;\n}\n\nprof_tctx_t *\nprof_lookup(tsd_t *tsd, prof_bt_t *bt) {\n\tunion {\n\t\tprof_tctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} ret;\n\tprof_tdata_t *tdata;\n\tbool not_found;\n\n\tcassert(config_prof);\n\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn NULL;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);\n\tnot_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v);\n\tif (!not_found) { /* Note double negative! */\n\t\tret.p->prepared = true;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\tif (not_found) {\n\t\tvoid *btkey;\n\t\tprof_gctx_t *gctx;\n\t\tbool new_gctx, error;\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\tif (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx,\n\t\t    &new_gctx)) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/* Link a prof_tctx_t into gctx for this thread. */\n\t\tret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t),\n\t\t    sz_size2index(sizeof(prof_tctx_t)), false, NULL, true,\n\t\t    arena_ichoose(tsd, NULL), true);\n\t\tif (ret.p == NULL) {\n\t\t\tif (new_gctx) {\n\t\t\t\tprof_gctx_try_destroy(tsd, tdata, gctx, tdata);\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t\tret.p->tdata = tdata;\n\t\tret.p->thr_uid = tdata->thr_uid;\n\t\tret.p->thr_discrim = tdata->thr_discrim;\n\t\tmemset(&ret.p->cnts, 0, sizeof(prof_cnt_t));\n\t\tret.p->gctx = gctx;\n\t\tret.p->tctx_uid = tdata->tctx_uid_next++;\n\t\tret.p->prepared = true;\n\t\tret.p->state = prof_tctx_state_initializing;\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);\n\t\terror = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\t\tif (error) {\n\t\t\tif (new_gctx) {\n\t\t\t\tprof_gctx_try_destroy(tsd, tdata, gctx, tdata);\n\t\t\t}\n\t\t\tidalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true);\n\t\t\treturn NULL;\n\t\t}\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\t\tret.p->state = prof_tctx_state_nominal;\n\t\ttctx_tree_insert(&gctx->tctxs, ret.p);\n\t\tgctx->nlimbo--;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t}\n\n\treturn ret.p;\n}\n\n/*\n * The bodies of this function and prof_leakcheck() are compiled out unless heap\n * profiling is enabled, so that it is possible to compile jemalloc with\n * floating point support completely disabled.  Avoiding floating point code is\n * important on memory-constrained systems, but it also enables a workaround for\n * versions of glibc that don't properly save/restore floating point registers\n * during dynamic lazy symbol loading (which internally calls into whatever\n * malloc implementation happens to be integrated into the application).  Note\n * that some compilers (e.g.  gcc 4.8) may use floating point registers for fast\n * memory moves, so jemalloc must be compiled with such optimizations disabled\n * (e.g.\n * -mno-sse) in order for the workaround to be complete.\n */\nvoid\nprof_sample_threshold_update(prof_tdata_t *tdata) {\n#ifdef JEMALLOC_PROF\n\tuint64_t r;\n\tdouble u;\n\n\tif (!config_prof) {\n\t\treturn;\n\t}\n\n\tif (lg_prof_sample == 0) {\n\t\ttdata->bytes_until_sample = 0;\n\t\treturn;\n\t}\n\n\t/*\n\t * Compute sample interval as a geometrically distributed random\n\t * variable with mean (2^lg_prof_sample).\n\t *\n\t *                             __        __\n\t *                             |  log(u)  |                     1\n\t * tdata->bytes_until_sample = | -------- |, where p = ---------------\n\t *                             | log(1-p) |             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://luc.devroye.org/rnbookindex.html)\n\t */\n\tr = prng_lg_range_u64(&tdata->prng_state, 53);\n\tu = (double)r * (1.0/9007199254740992.0L);\n\ttdata->bytes_until_sample = (uint64_t)(log(u) /\n\t    log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))\n\t    + (uint64_t)1U;\n#endif\n}\n\n#ifdef JEMALLOC_JET\nstatic prof_tdata_t *\nprof_tdata_count_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *arg) {\n\tsize_t *tdata_count = (size_t *)arg;\n\n\t(*tdata_count)++;\n\n\treturn NULL;\n}\n\nsize_t\nprof_tdata_count(void) {\n\tsize_t tdata_count = 0;\n\ttsdn_t *tsdn;\n\n\ttsdn = tsdn_fetch();\n\tmalloc_mutex_lock(tsdn, &tdatas_mtx);\n\ttdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter,\n\t    (void *)&tdata_count);\n\tmalloc_mutex_unlock(tsdn, &tdatas_mtx);\n\n\treturn tdata_count;\n}\n\nsize_t\nprof_bt_count(void) {\n\tsize_t bt_count;\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\n\ttsd = tsd_fetch();\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn 0;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);\n\tbt_count = ckh_count(&bt2gctx);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);\n\n\treturn bt_count;\n}\n#endif\n\nstatic int\nprof_dump_open_impl(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tfd = creat(filename, 0644);\n\tif (fd == -1 && !propagate_err) {\n\t\tmalloc_printf(\"<jemalloc>: creat(\\\"%s\\\"), 0644) failed\\n\",\n\t\t    filename);\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t}\n\n\treturn fd;\n}\nprof_dump_open_t *JET_MUTABLE prof_dump_open = prof_dump_open_impl;\n\nstatic bool\nprof_dump_flush(bool propagate_err) {\n\tbool ret = false;\n\tssize_t err;\n\n\tcassert(config_prof);\n\n\terr = malloc_write_fd(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);\n\tif (err == -1) {\n\t\tif (!propagate_err) {\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\t}\n\t\t}\n\t\tret = true;\n\t}\n\tprof_dump_buf_end = 0;\n\n\treturn ret;\n}\n\nstatic bool\nprof_dump_close(bool propagate_err) {\n\tbool ret;\n\n\tassert(prof_dump_fd != -1);\n\tret = prof_dump_flush(propagate_err);\n\tclose(prof_dump_fd);\n\tprof_dump_fd = -1;\n\n\treturn ret;\n}\n\nstatic bool\nprof_dump_write(bool propagate_err, const char *s) {\n\tsize_t 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_dump_flush(propagate_err) && propagate_err) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\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_FORMAT_PRINTF(2, 3)\nstatic bool\nprof_dump_printf(bool propagate_err, const char *format, ...) {\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_dump_write(propagate_err, buf);\n\n\treturn ret;\n}\n\nstatic void\nprof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) {\n\tmalloc_mutex_assert_owner(tsdn, tctx->tdata->lock);\n\n\tmalloc_mutex_lock(tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_initializing:\n\t\tmalloc_mutex_unlock(tsdn, tctx->gctx->lock);\n\t\treturn;\n\tcase prof_tctx_state_nominal:\n\t\ttctx->state = prof_tctx_state_dumping;\n\t\tmalloc_mutex_unlock(tsdn, tctx->gctx->lock);\n\n\t\tmemcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));\n\n\t\ttdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;\n\t\ttdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;\n\t\tif (opt_prof_accum) {\n\t\t\ttdata->cnt_summed.accumobjs +=\n\t\t\t    tctx->dump_cnts.accumobjs;\n\t\t\ttdata->cnt_summed.accumbytes +=\n\t\t\t    tctx->dump_cnts.accumbytes;\n\t\t}\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\tcase prof_tctx_state_purgatory:\n\t\tnot_reached();\n\t}\n}\n\nstatic void\nprof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) {\n\tmalloc_mutex_assert_owner(tsdn, gctx->lock);\n\n\tgctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs;\n\tgctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes;\n\tif (opt_prof_accum) {\n\t\tgctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;\n\t\tgctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;\n\t}\n}\n\nstatic prof_tctx_t *\nprof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {\n\ttsdn_t *tsdn = (tsdn_t *)arg;\n\n\tmalloc_mutex_assert_owner(tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_nominal:\n\t\t/* New since dumping started; ignore. */\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\tcase prof_tctx_state_purgatory:\n\t\tprof_tctx_merge_gctx(tsdn, tctx, tctx->gctx);\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n\n\treturn NULL;\n}\n\nstruct prof_tctx_dump_iter_arg_s {\n\ttsdn_t\t*tsdn;\n\tbool\tpropagate_err;\n};\n\nstatic prof_tctx_t *\nprof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) {\n\tstruct prof_tctx_dump_iter_arg_s *arg =\n\t    (struct prof_tctx_dump_iter_arg_s *)opaque;\n\n\tmalloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_initializing:\n\tcase prof_tctx_state_nominal:\n\t\t/* Not captured by this dump. */\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\tcase prof_tctx_state_purgatory:\n\t\tif (prof_dump_printf(arg->propagate_err,\n\t\t    \"  t%\"FMTu64\": %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": \"\n\t\t    \"%\"FMTu64\"]\\n\", tctx->thr_uid, tctx->dump_cnts.curobjs,\n\t\t    tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,\n\t\t    tctx->dump_cnts.accumbytes)) {\n\t\t\treturn tctx;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n\treturn NULL;\n}\n\nstatic prof_tctx_t *\nprof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {\n\ttsdn_t *tsdn = (tsdn_t *)arg;\n\tprof_tctx_t *ret;\n\n\tmalloc_mutex_assert_owner(tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_nominal:\n\t\t/* New since dumping started; ignore. */\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\t\ttctx->state = prof_tctx_state_nominal;\n\t\tbreak;\n\tcase prof_tctx_state_purgatory:\n\t\tret = tctx;\n\t\tgoto label_return;\n\tdefault:\n\t\tnot_reached();\n\t}\n\n\tret = NULL;\nlabel_return:\n\treturn ret;\n}\n\nstatic void\nprof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) {\n\tcassert(config_prof);\n\n\tmalloc_mutex_lock(tsdn, gctx->lock);\n\n\t/*\n\t * Increment nlimbo so that gctx won't go away before dump.\n\t * Additionally, link gctx into the dump list so that it is included in\n\t * prof_dump()'s second pass.\n\t */\n\tgctx->nlimbo++;\n\tgctx_tree_insert(gctxs, gctx);\n\n\tmemset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t));\n\n\tmalloc_mutex_unlock(tsdn, gctx->lock);\n}\n\nstruct prof_gctx_merge_iter_arg_s {\n\ttsdn_t\t*tsdn;\n\tsize_t\tleak_ngctx;\n};\n\nstatic prof_gctx_t *\nprof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {\n\tstruct prof_gctx_merge_iter_arg_s *arg =\n\t    (struct prof_gctx_merge_iter_arg_s *)opaque;\n\n\tmalloc_mutex_lock(arg->tsdn, gctx->lock);\n\ttctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter,\n\t    (void *)arg->tsdn);\n\tif (gctx->cnt_summed.curobjs != 0) {\n\t\targ->leak_ngctx++;\n\t}\n\tmalloc_mutex_unlock(arg->tsdn, gctx->lock);\n\n\treturn NULL;\n}\n\nstatic void\nprof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) {\n\tprof_tdata_t *tdata = prof_tdata_get(tsd, false);\n\tprof_gctx_t *gctx;\n\n\t/*\n\t * Standard tree iteration won't work here, because as soon as we\n\t * decrement gctx->nlimbo and unlock gctx, another thread can\n\t * concurrently destroy it, which will corrupt the tree.  Therefore,\n\t * tear down the tree one node at a time during iteration.\n\t */\n\twhile ((gctx = gctx_tree_first(gctxs)) != NULL) {\n\t\tgctx_tree_remove(gctxs, gctx);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\t\t{\n\t\t\tprof_tctx_t *next;\n\n\t\t\tnext = NULL;\n\t\t\tdo {\n\t\t\t\tprof_tctx_t *to_destroy =\n\t\t\t\t    tctx_tree_iter(&gctx->tctxs, next,\n\t\t\t\t    prof_tctx_finish_iter,\n\t\t\t\t    (void *)tsd_tsdn(tsd));\n\t\t\t\tif (to_destroy != NULL) {\n\t\t\t\t\tnext = tctx_tree_next(&gctx->tctxs,\n\t\t\t\t\t    to_destroy);\n\t\t\t\t\ttctx_tree_remove(&gctx->tctxs,\n\t\t\t\t\t    to_destroy);\n\t\t\t\t\tidalloctm(tsd_tsdn(tsd), to_destroy,\n\t\t\t\t\t    NULL, NULL, true, true);\n\t\t\t\t} else {\n\t\t\t\t\tnext = NULL;\n\t\t\t\t}\n\t\t\t} while (next != NULL);\n\t\t}\n\t\tgctx->nlimbo--;\n\t\tif (prof_gctx_should_destroy(gctx)) {\n\t\t\tgctx->nlimbo++;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\t\tprof_gctx_try_destroy(tsd, tdata, gctx, tdata);\n\t\t} else {\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\t}\n\t}\n}\n\nstruct prof_tdata_merge_iter_arg_s {\n\ttsdn_t\t\t*tsdn;\n\tprof_cnt_t\tcnt_all;\n};\n\nstatic prof_tdata_t *\nprof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *opaque) {\n\tstruct prof_tdata_merge_iter_arg_s *arg =\n\t    (struct prof_tdata_merge_iter_arg_s *)opaque;\n\n\tmalloc_mutex_lock(arg->tsdn, tdata->lock);\n\tif (!tdata->expired) {\n\t\tsize_t tabind;\n\t\tunion {\n\t\t\tprof_tctx_t\t*p;\n\t\t\tvoid\t\t*v;\n\t\t} tctx;\n\n\t\ttdata->dumping = true;\n\t\tmemset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t));\n\t\tfor (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL,\n\t\t    &tctx.v);) {\n\t\t\tprof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata);\n\t\t}\n\n\t\targ->cnt_all.curobjs += tdata->cnt_summed.curobjs;\n\t\targ->cnt_all.curbytes += tdata->cnt_summed.curbytes;\n\t\tif (opt_prof_accum) {\n\t\t\targ->cnt_all.accumobjs += tdata->cnt_summed.accumobjs;\n\t\t\targ->cnt_all.accumbytes += tdata->cnt_summed.accumbytes;\n\t\t}\n\t} else {\n\t\ttdata->dumping = false;\n\t}\n\tmalloc_mutex_unlock(arg->tsdn, tdata->lock);\n\n\treturn NULL;\n}\n\nstatic prof_tdata_t *\nprof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *arg) {\n\tbool propagate_err = *(bool *)arg;\n\n\tif (!tdata->dumping) {\n\t\treturn NULL;\n\t}\n\n\tif (prof_dump_printf(propagate_err,\n\t    \"  t%\"FMTu64\": %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": %\"FMTu64\"]%s%s\\n\",\n\t    tdata->thr_uid, tdata->cnt_summed.curobjs,\n\t    tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs,\n\t    tdata->cnt_summed.accumbytes,\n\t    (tdata->thread_name != NULL) ? \" \" : \"\",\n\t    (tdata->thread_name != NULL) ? tdata->thread_name : \"\")) {\n\t\treturn tdata;\n\t}\n\treturn NULL;\n}\n\nstatic bool\nprof_dump_header_impl(tsdn_t *tsdn, bool propagate_err,\n    const prof_cnt_t *cnt_all) {\n\tbool ret;\n\n\tif (prof_dump_printf(propagate_err,\n\t    \"heap_v2/%\"FMTu64\"\\n\"\n\t    \"  t*: %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": %\"FMTu64\"]\\n\",\n\t    ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs,\n\t    cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) {\n\t\treturn true;\n\t}\n\n\tmalloc_mutex_lock(tsdn, &tdatas_mtx);\n\tret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter,\n\t    (void *)&propagate_err) != NULL);\n\tmalloc_mutex_unlock(tsdn, &tdatas_mtx);\n\treturn ret;\n}\nprof_dump_header_t *JET_MUTABLE prof_dump_header = prof_dump_header_impl;\n\nstatic bool\nprof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx,\n    const prof_bt_t *bt, prof_gctx_tree_t *gctxs) {\n\tbool ret;\n\tunsigned i;\n\tstruct prof_tctx_dump_iter_arg_s prof_tctx_dump_iter_arg;\n\n\tcassert(config_prof);\n\tmalloc_mutex_assert_owner(tsdn, gctx->lock);\n\n\t/* Avoid dumping such gctx's that have no useful data. */\n\tif ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) ||\n\t    (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) {\n\t\tassert(gctx->cnt_summed.curobjs == 0);\n\t\tassert(gctx->cnt_summed.curbytes == 0);\n\t\tassert(gctx->cnt_summed.accumobjs == 0);\n\t\tassert(gctx->cnt_summed.accumbytes == 0);\n\t\tret = false;\n\t\tgoto label_return;\n\t}\n\n\tif (prof_dump_printf(propagate_err, \"@\")) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\tfor (i = 0; i < bt->len; i++) {\n\t\tif (prof_dump_printf(propagate_err, \" %#\"FMTxPTR,\n\t\t    (uintptr_t)bt->vec[i])) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tif (prof_dump_printf(propagate_err,\n\t    \"\\n\"\n\t    \"  t*: %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": %\"FMTu64\"]\\n\",\n\t    gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes,\n\t    gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tprof_tctx_dump_iter_arg.tsdn = tsdn;\n\tprof_tctx_dump_iter_arg.propagate_err = propagate_err;\n\tif (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter,\n\t    (void *)&prof_tctx_dump_iter_arg) != NULL) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tret = false;\nlabel_return:\n\treturn ret;\n}\n\n#ifndef _WIN32\nJEMALLOC_FORMAT_PRINTF(1, 2)\nstatic int\nprof_open_maps(const char *format, ...) {\n\tint mfd;\n\tva_list ap;\n\tchar filename[PATH_MAX + 1];\n\n\tva_start(ap, format);\n\tmalloc_vsnprintf(filename, sizeof(filename), format, ap);\n\tva_end(ap);\n\n#if defined(O_CLOEXEC)\n\tmfd = open(filename, O_RDONLY | O_CLOEXEC);\n#else\n\tmfd = open(filename, O_RDONLY);\n\tif (mfd != -1) {\n\t\tfcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);\n\t}\n#endif\n\n\treturn mfd;\n}\n#endif\n\nstatic int\nprof_getpid(void) {\n#ifdef _WIN32\n\treturn GetCurrentProcessId();\n#else\n\treturn getpid();\n#endif\n}\n\nstatic bool\nprof_dump_maps(bool propagate_err) {\n\tbool ret;\n\tint mfd;\n\n\tcassert(config_prof);\n#ifdef __FreeBSD__\n\tmfd = prof_open_maps(\"/proc/curproc/map\");\n#elif defined(_WIN32)\n\tmfd = -1; // Not implemented\n#else\n\t{\n\t\tint pid = prof_getpid();\n\n\t\tmfd = prof_open_maps(\"/proc/%d/task/%d/maps\", pid, pid);\n\t\tif (mfd == -1) {\n\t\t\tmfd = prof_open_maps(\"/proc/%d/maps\", pid);\n\t\t}\n\t}\n#endif\n\tif (mfd != -1) {\n\t\tssize_t nread;\n\n\t\tif (prof_dump_write(propagate_err, \"\\nMAPPED_LIBRARIES:\\n\") &&\n\t\t    propagate_err) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\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_dump_flush(propagate_err) &&\n\t\t\t\t    propagate_err) {\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\tnread = malloc_read_fd(mfd,\n\t\t\t    &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE\n\t\t\t    - prof_dump_buf_end);\n\t\t} while (nread > 0);\n\t} else {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tret = false;\nlabel_return:\n\tif (mfd != -1) {\n\t\tclose(mfd);\n\t}\n\treturn ret;\n}\n\n/*\n * See prof_sample_threshold_update() comment for why the body of this function\n * is conditionally compiled.\n */\nstatic void\nprof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx,\n    const char *filename) {\n#ifdef JEMALLOC_PROF\n\t/*\n\t * Scaling is equivalent AdjustSamples() in jeprof, but the result may\n\t * differ slightly from what jeprof reports, because here we scale the\n\t * summary values, whereas jeprof scales each context individually and\n\t * reports the sums of the scaled values.\n\t */\n\tif (cnt_all->curbytes != 0) {\n\t\tdouble sample_period = (double)((uint64_t)1 << lg_prof_sample);\n\t\tdouble ratio = (((double)cnt_all->curbytes) /\n\t\t    (double)cnt_all->curobjs) / sample_period;\n\t\tdouble scale_factor = 1.0 / (1.0 - exp(-ratio));\n\t\tuint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes)\n\t\t    * scale_factor);\n\t\tuint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) *\n\t\t    scale_factor);\n\n\t\tmalloc_printf(\"<jemalloc>: Leak approximation summary: ~%\"FMTu64\n\t\t    \" byte%s, ~%\"FMTu64\" object%s, >= %zu context%s\\n\",\n\t\t    curbytes, (curbytes != 1) ? \"s\" : \"\", curobjs, (curobjs !=\n\t\t    1) ? \"s\" : \"\", leak_ngctx, (leak_ngctx != 1) ? \"s\" : \"\");\n\t\tmalloc_printf(\n\t\t    \"<jemalloc>: Run jeprof on \\\"%s\\\" for leak detail\\n\",\n\t\t    filename);\n\t}\n#endif\n}\n\nstruct prof_gctx_dump_iter_arg_s {\n\ttsdn_t\t*tsdn;\n\tbool\tpropagate_err;\n};\n\nstatic prof_gctx_t *\nprof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {\n\tprof_gctx_t *ret;\n\tstruct prof_gctx_dump_iter_arg_s *arg =\n\t    (struct prof_gctx_dump_iter_arg_s *)opaque;\n\n\tmalloc_mutex_lock(arg->tsdn, gctx->lock);\n\n\tif (prof_dump_gctx(arg->tsdn, arg->propagate_err, gctx, &gctx->bt,\n\t    gctxs)) {\n\t\tret = gctx;\n\t\tgoto label_return;\n\t}\n\n\tret = NULL;\nlabel_return:\n\tmalloc_mutex_unlock(arg->tsdn, gctx->lock);\n\treturn ret;\n}\n\nstatic void\nprof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata,\n    struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,\n    struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,\n    prof_gctx_tree_t *gctxs) {\n\tsize_t tabind;\n\tunion {\n\t\tprof_gctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} gctx;\n\n\tprof_enter(tsd, tdata);\n\n\t/*\n\t * Put gctx's in limbo and clear their counters in preparation for\n\t * summing.\n\t */\n\tgctx_tree_new(gctxs);\n\tfor (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) {\n\t\tprof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs);\n\t}\n\n\t/*\n\t * Iterate over tdatas, and for the non-expired ones snapshot their tctx\n\t * stats and merge them into the associated gctx's.\n\t */\n\tprof_tdata_merge_iter_arg->tsdn = tsd_tsdn(tsd);\n\tmemset(&prof_tdata_merge_iter_arg->cnt_all, 0, sizeof(prof_cnt_t));\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\ttdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter,\n\t    (void *)prof_tdata_merge_iter_arg);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n\n\t/* Merge tctx stats into gctx's. */\n\tprof_gctx_merge_iter_arg->tsdn = tsd_tsdn(tsd);\n\tprof_gctx_merge_iter_arg->leak_ngctx = 0;\n\tgctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter,\n\t    (void *)prof_gctx_merge_iter_arg);\n\n\tprof_leave(tsd, tdata);\n}\n\nstatic bool\nprof_dump_file(tsd_t *tsd, bool propagate_err, const char *filename,\n    bool leakcheck, prof_tdata_t *tdata,\n    struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,\n    struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,\n    struct prof_gctx_dump_iter_arg_s *prof_gctx_dump_iter_arg,\n    prof_gctx_tree_t *gctxs) {\n\t/* Create dump file. */\n\tif ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) {\n\t\treturn true;\n\t}\n\n\t/* Dump profile header. */\n\tif (prof_dump_header(tsd_tsdn(tsd), propagate_err,\n\t    &prof_tdata_merge_iter_arg->cnt_all)) {\n\t\tgoto label_write_error;\n\t}\n\n\t/* Dump per gctx profile stats. */\n\tprof_gctx_dump_iter_arg->tsdn = tsd_tsdn(tsd);\n\tprof_gctx_dump_iter_arg->propagate_err = propagate_err;\n\tif (gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter,\n\t    (void *)prof_gctx_dump_iter_arg) != NULL) {\n\t\tgoto label_write_error;\n\t}\n\n\t/* Dump /proc/<pid>/maps if possible. */\n\tif (prof_dump_maps(propagate_err)) {\n\t\tgoto label_write_error;\n\t}\n\n\tif (prof_dump_close(propagate_err)) {\n\t\treturn true;\n\t}\n\n\treturn false;\nlabel_write_error:\n\tprof_dump_close(propagate_err);\n\treturn true;\n}\n\nstatic bool\nprof_dump(tsd_t *tsd, bool propagate_err, const char *filename,\n    bool leakcheck) {\n\tcassert(config_prof);\n\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\n\tprof_tdata_t * tdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn true;\n\t}\n\n\tpre_reentrancy(tsd, NULL);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);\n\n\tprof_gctx_tree_t gctxs;\n\tstruct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;\n\tstruct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;\n\tstruct prof_gctx_dump_iter_arg_s prof_gctx_dump_iter_arg;\n\tprof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,\n\t    &prof_gctx_merge_iter_arg, &gctxs);\n\tbool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, tdata,\n\t    &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg,\n\t    &prof_gctx_dump_iter_arg, &gctxs);\n\tprof_gctx_finish(tsd, &gctxs);\n\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);\n\tpost_reentrancy(tsd);\n\n\tif (err) {\n\t\treturn true;\n\t}\n\n\tif (leakcheck) {\n\t\tprof_leakcheck(&prof_tdata_merge_iter_arg.cnt_all,\n\t\t    prof_gctx_merge_iter_arg.leak_ngctx, filename);\n\t}\n\treturn false;\n}\n\n#ifdef JEMALLOC_JET\nvoid\nprof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,\n    uint64_t *accumbytes) {\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\tstruct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;\n\tstruct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;\n\tprof_gctx_tree_t gctxs;\n\n\ttsd = tsd_fetch();\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\tif (curobjs != NULL) {\n\t\t\t*curobjs = 0;\n\t\t}\n\t\tif (curbytes != NULL) {\n\t\t\t*curbytes = 0;\n\t\t}\n\t\tif (accumobjs != NULL) {\n\t\t\t*accumobjs = 0;\n\t\t}\n\t\tif (accumbytes != NULL) {\n\t\t\t*accumbytes = 0;\n\t\t}\n\t\treturn;\n\t}\n\n\tprof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,\n\t    &prof_gctx_merge_iter_arg, &gctxs);\n\tprof_gctx_finish(tsd, &gctxs);\n\n\tif (curobjs != NULL) {\n\t\t*curobjs = prof_tdata_merge_iter_arg.cnt_all.curobjs;\n\t}\n\tif (curbytes != NULL) {\n\t\t*curbytes = prof_tdata_merge_iter_arg.cnt_all.curbytes;\n\t}\n\tif (accumobjs != NULL) {\n\t\t*accumobjs = prof_tdata_merge_iter_arg.cnt_all.accumobjs;\n\t}\n\tif (accumbytes != NULL) {\n\t\t*accumbytes = prof_tdata_merge_iter_arg.cnt_all.accumbytes;\n\t}\n}\n#endif\n\n#define DUMP_FILENAME_BUFSIZE\t(PATH_MAX + 1)\n#define VSEQ_INVALID\t\tUINT64_C(0xffffffffffffffff)\nstatic void\nprof_dump_filename(char *filename, char v, uint64_t vseq) {\n\tcassert(config_prof);\n\n\tif (vseq != VSEQ_INVALID) {\n\t        /* \"<prefix>.<pid>.<seq>.v<vseq>.heap\" */\n\t\tmalloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,\n\t\t    \"%s.%d.%\"FMTu64\".%c%\"FMTu64\".heap\",\n\t\t    opt_prof_prefix, prof_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.%\"FMTu64\".%c.heap\",\n\t\t    opt_prof_prefix, prof_getpid(), prof_dump_seq, v);\n\t}\n\tprof_dump_seq++;\n}\n\nstatic void\nprof_fdump(void) {\n\ttsd_t *tsd;\n\tchar filename[DUMP_FILENAME_BUFSIZE];\n\n\tcassert(config_prof);\n\tassert(opt_prof_final);\n\tassert(opt_prof_prefix[0] != '\\0');\n\n\tif (!prof_booted) {\n\t\treturn;\n\t}\n\ttsd = tsd_fetch();\n\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\tprof_dump_filename(filename, 'f', VSEQ_INVALID);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\tprof_dump(tsd, false, filename, opt_prof_leak);\n}\n\nbool\nprof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum) {\n\tcassert(config_prof);\n\n#ifndef JEMALLOC_ATOMIC_U64\n\tif (malloc_mutex_init(&prof_accum->mtx, \"prof_accum\",\n\t    WITNESS_RANK_PROF_ACCUM, malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tprof_accum->accumbytes = 0;\n#else\n\tatomic_store_u64(&prof_accum->accumbytes, 0, ATOMIC_RELAXED);\n#endif\n\treturn false;\n}\n\nvoid\nprof_idump(tsdn_t *tsdn) {\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\tif (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {\n\t\treturn;\n\t}\n\ttsd = tsdn_tsd(tsdn);\n\tif (tsd_reentrancy_level_get(tsd) > 0) {\n\t\treturn;\n\t}\n\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn;\n\t}\n\tif (tdata->enq) {\n\t\ttdata->enq_idump = true;\n\t\treturn;\n\t}\n\n\tif (opt_prof_prefix[0] != '\\0') {\n\t\tchar filename[PATH_MAX + 1];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'i', prof_dump_iseq);\n\t\tprof_dump_iseq++;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\t\tprof_dump(tsd, false, filename, false);\n\t}\n}\n\nbool\nprof_mdump(tsd_t *tsd, const char *filename) {\n\tcassert(config_prof);\n\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\n\tif (!opt_prof || !prof_booted) {\n\t\treturn true;\n\t}\n\tchar filename_buf[DUMP_FILENAME_BUFSIZE];\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\t}\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &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(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\t\tfilename = filename_buf;\n\t}\n\treturn prof_dump(tsd, true, filename, false);\n}\n\nvoid\nprof_gdump(tsdn_t *tsdn) {\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\tif (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {\n\t\treturn;\n\t}\n\ttsd = tsdn_tsd(tsdn);\n\tif (tsd_reentrancy_level_get(tsd) > 0) {\n\t\treturn;\n\t}\n\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn;\n\t}\n\tif (tdata->enq) {\n\t\ttdata->enq_gdump = true;\n\t\treturn;\n\t}\n\n\tif (opt_prof_prefix[0] != '\\0') {\n\t\tchar filename[DUMP_FILENAME_BUFSIZE];\n\t\tmalloc_mutex_lock(tsdn, &prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'u', prof_dump_useq);\n\t\tprof_dump_useq++;\n\t\tmalloc_mutex_unlock(tsdn, &prof_dump_seq_mtx);\n\t\tprof_dump(tsd, false, filename, false);\n\t}\n}\n\nstatic void\nprof_bt_hash(const void *key, size_t r_hash[2]) {\n\tprof_bt_t *bt = (prof_bt_t *)key;\n\n\tcassert(config_prof);\n\n\thash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);\n}\n\nstatic bool\nprof_bt_keycomp(const void *k1, const void *k2) {\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\t}\n\treturn (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);\n}\n\nstatic uint64_t\nprof_thr_uid_alloc(tsdn_t *tsdn) {\n\tuint64_t thr_uid;\n\n\tmalloc_mutex_lock(tsdn, &next_thr_uid_mtx);\n\tthr_uid = next_thr_uid;\n\tnext_thr_uid++;\n\tmalloc_mutex_unlock(tsdn, &next_thr_uid_mtx);\n\n\treturn thr_uid;\n}\n\nstatic prof_tdata_t *\nprof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,\n    char *thread_name, bool active) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\t/* Initialize an empty cache for this thread. */\n\ttdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t),\n\t    sz_size2index(sizeof(prof_tdata_t)), false, NULL, true,\n\t    arena_get(TSDN_NULL, 0, true), true);\n\tif (tdata == NULL) {\n\t\treturn NULL;\n\t}\n\n\ttdata->lock = prof_tdata_mutex_choose(thr_uid);\n\ttdata->thr_uid = thr_uid;\n\ttdata->thr_discrim = thr_discrim;\n\ttdata->thread_name = thread_name;\n\ttdata->attached = true;\n\ttdata->expired = false;\n\ttdata->tctx_uid_next = 0;\n\n\tif (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,\n\t    prof_bt_keycomp)) {\n\t\tidalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);\n\t\treturn NULL;\n\t}\n\n\ttdata->prng_state = (uint64_t)(uintptr_t)tdata;\n\tprof_sample_threshold_update(tdata);\n\n\ttdata->enq = false;\n\ttdata->enq_idump = false;\n\ttdata->enq_gdump = false;\n\n\ttdata->dumping = false;\n\ttdata->active = active;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\ttdata_tree_insert(&tdatas, tdata);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n\n\treturn tdata;\n}\n\nprof_tdata_t *\nprof_tdata_init(tsd_t *tsd) {\n\treturn prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0,\n\t    NULL, prof_thread_active_init_get(tsd_tsdn(tsd)));\n}\n\nstatic bool\nprof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) {\n\tif (tdata->attached && !even_if_attached) {\n\t\treturn false;\n\t}\n\tif (ckh_count(&tdata->bt2tctx) != 0) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool\nprof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,\n    bool even_if_attached) {\n\tmalloc_mutex_assert_owner(tsdn, tdata->lock);\n\n\treturn prof_tdata_should_destroy_unlocked(tdata, even_if_attached);\n}\n\nstatic void\nprof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,\n    bool even_if_attached) {\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx);\n\n\ttdata_tree_remove(&tdatas, tdata);\n\n\tassert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));\n\n\tif (tdata->thread_name != NULL) {\n\t\tidalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,\n\t\t    true);\n\t}\n\tckh_delete(tsd, &tdata->bt2tctx);\n\tidalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);\n}\n\nstatic void\nprof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\tprof_tdata_destroy_locked(tsd, tdata, even_if_attached);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n}\n\nstatic void\nprof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) {\n\tbool destroy_tdata;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);\n\tif (tdata->attached) {\n\t\tdestroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata,\n\t\t    true);\n\t\t/*\n\t\t * Only detach if !destroy_tdata, because detaching would allow\n\t\t * another thread to win the race to destroy tdata.\n\t\t */\n\t\tif (!destroy_tdata) {\n\t\t\ttdata->attached = false;\n\t\t}\n\t\ttsd_prof_tdata_set(tsd, NULL);\n\t} else {\n\t\tdestroy_tdata = false;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\tif (destroy_tdata) {\n\t\tprof_tdata_destroy(tsd, tdata, true);\n\t}\n}\n\nprof_tdata_t *\nprof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {\n\tuint64_t thr_uid = tdata->thr_uid;\n\tuint64_t thr_discrim = tdata->thr_discrim + 1;\n\tchar *thread_name = (tdata->thread_name != NULL) ?\n\t    prof_thread_name_alloc(tsd_tsdn(tsd), tdata->thread_name) : NULL;\n\tbool active = tdata->active;\n\n\tprof_tdata_detach(tsd, tdata);\n\treturn prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name,\n\t    active);\n}\n\nstatic bool\nprof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) {\n\tbool destroy_tdata;\n\n\tmalloc_mutex_lock(tsdn, tdata->lock);\n\tif (!tdata->expired) {\n\t\ttdata->expired = true;\n\t\tdestroy_tdata = tdata->attached ? false :\n\t\t    prof_tdata_should_destroy(tsdn, tdata, false);\n\t} else {\n\t\tdestroy_tdata = false;\n\t}\n\tmalloc_mutex_unlock(tsdn, tdata->lock);\n\n\treturn destroy_tdata;\n}\n\nstatic prof_tdata_t *\nprof_tdata_reset_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *arg) {\n\ttsdn_t *tsdn = (tsdn_t *)arg;\n\n\treturn (prof_tdata_expire(tsdn, tdata) ? tdata : NULL);\n}\n\nvoid\nprof_reset(tsd_t *tsd, size_t lg_sample) {\n\tprof_tdata_t *next;\n\n\tassert(lg_sample < (sizeof(uint64_t) << 3));\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\n\tlg_prof_sample = lg_sample;\n\n\tnext = NULL;\n\tdo {\n\t\tprof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next,\n\t\t    prof_tdata_reset_iter, (void *)tsd);\n\t\tif (to_destroy != NULL) {\n\t\t\tnext = tdata_tree_next(&tdatas, to_destroy);\n\t\t\tprof_tdata_destroy_locked(tsd, to_destroy, false);\n\t\t} else {\n\t\t\tnext = NULL;\n\t\t}\n\t} while (next != NULL);\n\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);\n}\n\nvoid\nprof_tdata_cleanup(tsd_t *tsd) {\n\tprof_tdata_t *tdata;\n\n\tif (!config_prof) {\n\t\treturn;\n\t}\n\n\ttdata = tsd_prof_tdata_get(tsd);\n\tif (tdata != NULL) {\n\t\tprof_tdata_detach(tsd, tdata);\n\t}\n}\n\nbool\nprof_active_get(tsdn_t *tsdn) {\n\tbool prof_active_current;\n\n\tmalloc_mutex_lock(tsdn, &prof_active_mtx);\n\tprof_active_current = prof_active;\n\tmalloc_mutex_unlock(tsdn, &prof_active_mtx);\n\treturn prof_active_current;\n}\n\nbool\nprof_active_set(tsdn_t *tsdn, bool active) {\n\tbool prof_active_old;\n\n\tmalloc_mutex_lock(tsdn, &prof_active_mtx);\n\tprof_active_old = prof_active;\n\tprof_active = active;\n\tmalloc_mutex_unlock(tsdn, &prof_active_mtx);\n\treturn prof_active_old;\n}\n\nconst char *\nprof_thread_name_get(tsd_t *tsd) {\n\tprof_tdata_t *tdata;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn \"\";\n\t}\n\treturn (tdata->thread_name != NULL ? tdata->thread_name : \"\");\n}\n\nstatic char *\nprof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) {\n\tchar *ret;\n\tsize_t size;\n\n\tif (thread_name == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsize = strlen(thread_name) + 1;\n\tif (size == 1) {\n\t\treturn \"\";\n\t}\n\n\tret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true,\n\t    arena_get(TSDN_NULL, 0, true), true);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\tmemcpy(ret, thread_name, size);\n\treturn ret;\n}\n\nint\nprof_thread_name_set(tsd_t *tsd, const char *thread_name) {\n\tprof_tdata_t *tdata;\n\tunsigned i;\n\tchar *s;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn EAGAIN;\n\t}\n\n\t/* Validate input. */\n\tif (thread_name == NULL) {\n\t\treturn EFAULT;\n\t}\n\tfor (i = 0; thread_name[i] != '\\0'; i++) {\n\t\tchar c = thread_name[i];\n\t\tif (!isgraph(c) && !isblank(c)) {\n\t\t\treturn EFAULT;\n\t\t}\n\t}\n\n\ts = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name);\n\tif (s == NULL) {\n\t\treturn EAGAIN;\n\t}\n\n\tif (tdata->thread_name != NULL) {\n\t\tidalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,\n\t\t    true);\n\t\ttdata->thread_name = NULL;\n\t}\n\tif (strlen(s) > 0) {\n\t\ttdata->thread_name = s;\n\t}\n\treturn 0;\n}\n\nbool\nprof_thread_active_get(tsd_t *tsd) {\n\tprof_tdata_t *tdata;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn false;\n\t}\n\treturn tdata->active;\n}\n\nbool\nprof_thread_active_set(tsd_t *tsd, bool active) {\n\tprof_tdata_t *tdata;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn true;\n\t}\n\ttdata->active = active;\n\treturn false;\n}\n\nbool\nprof_thread_active_init_get(tsdn_t *tsdn) {\n\tbool active_init;\n\n\tmalloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);\n\tactive_init = prof_thread_active_init;\n\tmalloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);\n\treturn active_init;\n}\n\nbool\nprof_thread_active_init_set(tsdn_t *tsdn, bool active_init) {\n\tbool active_init_old;\n\n\tmalloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);\n\tactive_init_old = prof_thread_active_init;\n\tprof_thread_active_init = active_init;\n\tmalloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);\n\treturn active_init_old;\n}\n\nbool\nprof_gdump_get(tsdn_t *tsdn) {\n\tbool prof_gdump_current;\n\n\tmalloc_mutex_lock(tsdn, &prof_gdump_mtx);\n\tprof_gdump_current = prof_gdump_val;\n\tmalloc_mutex_unlock(tsdn, &prof_gdump_mtx);\n\treturn prof_gdump_current;\n}\n\nbool\nprof_gdump_set(tsdn_t *tsdn, bool gdump) {\n\tbool prof_gdump_old;\n\n\tmalloc_mutex_lock(tsdn, &prof_gdump_mtx);\n\tprof_gdump_old = prof_gdump_val;\n\tprof_gdump_val = gdump;\n\tmalloc_mutex_unlock(tsdn, &prof_gdump_mtx);\n\treturn prof_gdump_old;\n}\n\nvoid\nprof_boot0(void) {\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\tcassert(config_prof);\n\n\t/*\n\t * opt_prof must be in its final state before any arenas are\n\t * initialized, so this function must be executed early.\n\t */\n\n\tif (opt_prof_leak && !opt_prof) {\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} 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}\n\t}\n}\n\nbool\nprof_boot2(tsd_t *tsd) {\n\tcassert(config_prof);\n\n\tif (opt_prof) {\n\t\tunsigned i;\n\n\t\tlg_prof_sample = opt_lg_prof_sample;\n\n\t\tprof_active = opt_prof_active;\n\t\tif (malloc_mutex_init(&prof_active_mtx, \"prof_active\",\n\t\t    WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tprof_gdump_val = opt_prof_gdump;\n\t\tif (malloc_mutex_init(&prof_gdump_mtx, \"prof_gdump\",\n\t\t    WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tprof_thread_active_init = opt_prof_thread_active_init;\n\t\tif (malloc_mutex_init(&prof_thread_active_init_mtx,\n\t\t    \"prof_thread_active_init\",\n\t\t    WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,\n\t\t    malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, prof_bt_hash,\n\t\t    prof_bt_keycomp)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (malloc_mutex_init(&bt2gctx_mtx, \"prof_bt2gctx\",\n\t\t    WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\ttdata_tree_new(&tdatas);\n\t\tif (malloc_mutex_init(&tdatas_mtx, \"prof_tdatas\",\n\t\t    WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tnext_thr_uid = 0;\n\t\tif (malloc_mutex_init(&next_thr_uid_mtx, \"prof_next_thr_uid\",\n\t\t    WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (malloc_mutex_init(&prof_dump_seq_mtx, \"prof_dump_seq\",\n\t\t    WITNESS_RANK_PROF_DUMP_SEQ, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (malloc_mutex_init(&prof_dump_mtx, \"prof_dump\",\n\t\t    WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (opt_prof_final && opt_prof_prefix[0] != '\\0' &&\n\t\t    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\t}\n\t\t}\n\n\t\tgctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),\n\t\t    b0get(), PROF_NCTX_LOCKS * sizeof(malloc_mutex_t),\n\t\t    CACHELINE);\n\t\tif (gctx_locks == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tif (malloc_mutex_init(&gctx_locks[i], \"prof_gctx\",\n\t\t\t    WITNESS_RANK_PROF_GCTX,\n\t\t\t    malloc_mutex_rank_exclusive)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\ttdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),\n\t\t    b0get(), PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t),\n\t\t    CACHELINE);\n\t\tif (tdata_locks == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tif (malloc_mutex_init(&tdata_locks[i], \"prof_tdata\",\n\t\t\t    WITNESS_RANK_PROF_TDATA,\n\t\t\t    malloc_mutex_rank_exclusive)) {\n\t\t\t\treturn true;\n\t\t\t}\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_prefork0(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_prefork(tsdn, &prof_dump_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &bt2gctx_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &tdatas_mtx);\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tmalloc_mutex_prefork(tsdn, &tdata_locks[i]);\n\t\t}\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tmalloc_mutex_prefork(tsdn, &gctx_locks[i]);\n\t\t}\n\t}\n}\n\nvoid\nprof_prefork1(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tmalloc_mutex_prefork(tsdn, &prof_active_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &prof_dump_seq_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &prof_gdump_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &next_thr_uid_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx);\n\t}\n}\n\nvoid\nprof_postfork_parent(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_postfork_parent(tsdn,\n\t\t    &prof_thread_active_init_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_dump_seq_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_active_mtx);\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_parent(tsdn, &gctx_locks[i]);\n\t\t}\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_parent(tsdn, &tdata_locks[i]);\n\t\t}\n\t\tmalloc_mutex_postfork_parent(tsdn, &tdatas_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &bt2gctx_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_dump_mtx);\n\t}\n}\n\nvoid\nprof_postfork_child(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_gdump_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_dump_seq_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_active_mtx);\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_child(tsdn, &gctx_locks[i]);\n\t\t}\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_child(tsdn, &tdata_locks[i]);\n\t\t}\n\t\tmalloc_mutex_postfork_child(tsdn, &tdatas_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &bt2gctx_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_dump_mtx);\n\t}\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/rtree.c",
    "content": "#define JEMALLOC_RTREE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/mutex.h\"\n\n/*\n * Only the most significant bits of keys passed to rtree_{read,write}() are\n * used.\n */\nbool\nrtree_new(rtree_t *rtree, bool zeroed) {\n#ifdef JEMALLOC_JET\n\tif (!zeroed) {\n\t\tmemset(rtree, 0, sizeof(rtree_t)); /* Clear root. */\n\t}\n#else\n\tassert(zeroed);\n#endif\n\n\tif (malloc_mutex_init(&rtree->init_lock, \"rtree\", WITNESS_RANK_RTREE,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic rtree_node_elm_t *\nrtree_node_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\treturn (rtree_node_elm_t *)base_alloc(tsdn, b0get(), nelms *\n\t    sizeof(rtree_node_elm_t), CACHELINE);\n}\nrtree_node_alloc_t *JET_MUTABLE rtree_node_alloc = rtree_node_alloc_impl;\n\nstatic void\nrtree_node_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *node) {\n\t/* Nodes are never deleted during normal operation. */\n\tnot_reached();\n}\nUNUSED rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc =\n    rtree_node_dalloc_impl;\n\nstatic rtree_leaf_elm_t *\nrtree_leaf_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\treturn (rtree_leaf_elm_t *)base_alloc(tsdn, b0get(), nelms *\n\t    sizeof(rtree_leaf_elm_t), CACHELINE);\n}\nrtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc = rtree_leaf_alloc_impl;\n\nstatic void\nrtree_leaf_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *leaf) {\n\t/* Leaves are never deleted during normal operation. */\n\tnot_reached();\n}\nUNUSED rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc =\n    rtree_leaf_dalloc_impl;\n\n#ifdef JEMALLOC_JET\n#  if RTREE_HEIGHT > 1\nstatic void\nrtree_delete_subtree(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *subtree,\n    unsigned level) {\n\tsize_t nchildren = ZU(1) << rtree_levels[level].bits;\n\tif (level + 2 < RTREE_HEIGHT) {\n\t\tfor (size_t i = 0; i < nchildren; i++) {\n\t\t\trtree_node_elm_t *node =\n\t\t\t    (rtree_node_elm_t *)atomic_load_p(&subtree[i].child,\n\t\t\t    ATOMIC_RELAXED);\n\t\t\tif (node != NULL) {\n\t\t\t\trtree_delete_subtree(tsdn, rtree, node, level +\n\t\t\t\t    1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (size_t i = 0; i < nchildren; i++) {\n\t\t\trtree_leaf_elm_t *leaf =\n\t\t\t    (rtree_leaf_elm_t *)atomic_load_p(&subtree[i].child,\n\t\t\t    ATOMIC_RELAXED);\n\t\t\tif (leaf != NULL) {\n\t\t\t\trtree_leaf_dalloc(tsdn, rtree, leaf);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (subtree != rtree->root) {\n\t\trtree_node_dalloc(tsdn, rtree, subtree);\n\t}\n}\n#  endif\n\nvoid\nrtree_delete(tsdn_t *tsdn, rtree_t *rtree) {\n#  if RTREE_HEIGHT > 1\n\trtree_delete_subtree(tsdn, rtree, rtree->root, 0);\n#  endif\n}\n#endif\n\nstatic rtree_node_elm_t *\nrtree_node_init(tsdn_t *tsdn, rtree_t *rtree, unsigned level,\n    atomic_p_t *elmp) {\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\t/*\n\t * If *elmp is non-null, then it was initialized with the init lock\n\t * held, so we can get by with 'relaxed' here.\n\t */\n\trtree_node_elm_t *node = atomic_load_p(elmp, ATOMIC_RELAXED);\n\tif (node == NULL) {\n\t\tnode = rtree_node_alloc(tsdn, rtree, ZU(1) <<\n\t\t    rtree_levels[level].bits);\n\t\tif (node == NULL) {\n\t\t\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\t\t\treturn NULL;\n\t\t}\n\t\t/*\n\t\t * Even though we hold the lock, a later reader might not; we\n\t\t * need release semantics.\n\t\t */\n\t\tatomic_store_p(elmp, node, ATOMIC_RELEASE);\n\t}\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\n\treturn node;\n}\n\nstatic rtree_leaf_elm_t *\nrtree_leaf_init(tsdn_t *tsdn, rtree_t *rtree, atomic_p_t *elmp) {\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\t/*\n\t * If *elmp is non-null, then it was initialized with the init lock\n\t * held, so we can get by with 'relaxed' here.\n\t */\n\trtree_leaf_elm_t *leaf = atomic_load_p(elmp, ATOMIC_RELAXED);\n\tif (leaf == NULL) {\n\t\tleaf = rtree_leaf_alloc(tsdn, rtree, ZU(1) <<\n\t\t    rtree_levels[RTREE_HEIGHT-1].bits);\n\t\tif (leaf == NULL) {\n\t\t\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\t\t\treturn NULL;\n\t\t}\n\t\t/*\n\t\t * Even though we hold the lock, a later reader might not; we\n\t\t * need release semantics.\n\t\t */\n\t\tatomic_store_p(elmp, leaf, ATOMIC_RELEASE);\n\t}\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\n\treturn leaf;\n}\n\nstatic bool\nrtree_node_valid(rtree_node_elm_t *node) {\n\treturn ((uintptr_t)node != (uintptr_t)0);\n}\n\nstatic bool\nrtree_leaf_valid(rtree_leaf_elm_t *leaf) {\n\treturn ((uintptr_t)leaf != (uintptr_t)0);\n}\n\nstatic rtree_node_elm_t *\nrtree_child_node_tryread(rtree_node_elm_t *elm, bool dependent) {\n\trtree_node_elm_t *node;\n\n\tif (dependent) {\n\t\tnode = (rtree_node_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_RELAXED);\n\t} else {\n\t\tnode = (rtree_node_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_ACQUIRE);\n\t}\n\n\tassert(!dependent || node != NULL);\n\treturn node;\n}\n\nstatic rtree_node_elm_t *\nrtree_child_node_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm,\n    unsigned level, bool dependent) {\n\trtree_node_elm_t *node;\n\n\tnode = rtree_child_node_tryread(elm, dependent);\n\tif (!dependent && unlikely(!rtree_node_valid(node))) {\n\t\tnode = rtree_node_init(tsdn, rtree, level + 1, &elm->child);\n\t}\n\tassert(!dependent || node != NULL);\n\treturn node;\n}\n\nstatic rtree_leaf_elm_t *\nrtree_child_leaf_tryread(rtree_node_elm_t *elm, bool dependent) {\n\trtree_leaf_elm_t *leaf;\n\n\tif (dependent) {\n\t\tleaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_RELAXED);\n\t} else {\n\t\tleaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_ACQUIRE);\n\t}\n\n\tassert(!dependent || leaf != NULL);\n\treturn leaf;\n}\n\nstatic rtree_leaf_elm_t *\nrtree_child_leaf_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm,\n    unsigned level, bool dependent) {\n\trtree_leaf_elm_t *leaf;\n\n\tleaf = rtree_child_leaf_tryread(elm, dependent);\n\tif (!dependent && unlikely(!rtree_leaf_valid(leaf))) {\n\t\tleaf = rtree_leaf_init(tsdn, rtree, &elm->child);\n\t}\n\tassert(!dependent || leaf != NULL);\n\treturn leaf;\n}\n\nrtree_leaf_elm_t *\nrtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, bool init_missing) {\n\trtree_node_elm_t *node;\n\trtree_leaf_elm_t *leaf;\n#if RTREE_HEIGHT > 1\n\tnode = rtree->root;\n#else\n\tleaf = rtree->root;\n#endif\n\n\tif (config_debug) {\n\t\tuintptr_t leafkey = rtree_leafkey(key);\n\t\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) {\n\t\t\tassert(rtree_ctx->cache[i].leafkey != leafkey);\n\t\t}\n\t\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) {\n\t\t\tassert(rtree_ctx->l2_cache[i].leafkey != leafkey);\n\t\t}\n\t}\n\n#define RTREE_GET_CHILD(level) {\t\t\t\t\t\\\n\t\tassert(level < RTREE_HEIGHT-1);\t\t\t\t\\\n\t\tif (level != 0 && !dependent &&\t\t\t\t\\\n\t\t    unlikely(!rtree_node_valid(node))) {\t\t\\\n\t\t\treturn NULL;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tuintptr_t subkey = rtree_subkey(key, level);\t\t\\\n\t\tif (level + 2 < RTREE_HEIGHT) {\t\t\t\t\\\n\t\t\tnode = init_missing ?\t\t\t\t\\\n\t\t\t    rtree_child_node_read(tsdn, rtree,\t\t\\\n\t\t\t    &node[subkey], level, dependent) :\t\t\\\n\t\t\t    rtree_child_node_tryread(&node[subkey],\t\\\n\t\t\t    dependent);\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tleaf = init_missing ?\t\t\t\t\\\n\t\t\t    rtree_child_leaf_read(tsdn, rtree,\t\t\\\n\t\t\t    &node[subkey], level, dependent) :\t\t\\\n\t\t\t    rtree_child_leaf_tryread(&node[subkey],\t\\\n\t\t\t    dependent);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\n\t/*\n\t * Cache replacement upon hard lookup (i.e. L1 & L2 rtree cache miss):\n\t * (1) evict last entry in L2 cache; (2) move the collision slot from L1\n\t * cache down to L2; and 3) fill L1.\n\t */\n#define RTREE_GET_LEAF(level) {\t\t\t\t\t\t\\\n\t\tassert(level == RTREE_HEIGHT-1);\t\t\t\\\n\t\tif (!dependent && unlikely(!rtree_leaf_valid(leaf))) {\t\\\n\t\t\treturn NULL;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (RTREE_CTX_NCACHE_L2 > 1) {\t\t\t\t\\\n\t\t\tmemmove(&rtree_ctx->l2_cache[1],\t\t\\\n\t\t\t    &rtree_ctx->l2_cache[0],\t\t\t\\\n\t\t\t    sizeof(rtree_ctx_cache_elm_t) *\t\t\\\n\t\t\t    (RTREE_CTX_NCACHE_L2 - 1));\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tsize_t slot = rtree_cache_direct_map(key);\t\t\\\n\t\trtree_ctx->l2_cache[0].leafkey =\t\t\t\\\n\t\t    rtree_ctx->cache[slot].leafkey;\t\t\t\\\n\t\trtree_ctx->l2_cache[0].leaf =\t\t\t\t\\\n\t\t    rtree_ctx->cache[slot].leaf;\t\t\t\\\n\t\tuintptr_t leafkey = rtree_leafkey(key);\t\t\t\\\n\t\trtree_ctx->cache[slot].leafkey = leafkey;\t\t\\\n\t\trtree_ctx->cache[slot].leaf = leaf;\t\t\t\\\n\t\tuintptr_t subkey = rtree_subkey(key, level);\t\t\\\n\t\treturn &leaf[subkey];\t\t\t\t\t\\\n\t}\n\tif (RTREE_HEIGHT > 1) {\n\t\tRTREE_GET_CHILD(0)\n\t}\n\tif (RTREE_HEIGHT > 2) {\n\t\tRTREE_GET_CHILD(1)\n\t}\n\tif (RTREE_HEIGHT > 3) {\n\t\tfor (unsigned i = 2; i < RTREE_HEIGHT-1; i++) {\n\t\t\tRTREE_GET_CHILD(i)\n\t\t}\n\t}\n\tRTREE_GET_LEAF(RTREE_HEIGHT-1)\n#undef RTREE_GET_CHILD\n#undef RTREE_GET_LEAF\n\tnot_reached();\n}\n\nvoid\nrtree_ctx_data_init(rtree_ctx_t *ctx) {\n\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) {\n\t\trtree_ctx_cache_elm_t *cache = &ctx->cache[i];\n\t\tcache->leafkey = RTREE_LEAFKEY_INVALID;\n\t\tcache->leaf = NULL;\n\t}\n\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) {\n\t\trtree_ctx_cache_elm_t *cache = &ctx->l2_cache[i];\n\t\tcache->leafkey = RTREE_LEAFKEY_INVALID;\n\t\tcache->leaf = NULL;\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/stats.c",
    "content": "#define JEMALLOC_STATS_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/emitter.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n\nconst char *global_mutex_names[mutex_prof_num_global_mutexes] = {\n#define OP(mtx) #mtx,\n\tMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n};\n\nconst char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {\n#define OP(mtx) #mtx,\n\tMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n};\n\n#define CTL_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, (void *)v, &sz, NULL, 0);\t\t\t\t\\\n} while (0)\n\n#define CTL_M2_GET(n, i, v, t) do {\t\t\t\t\t\\\n\tsize_t mib[CTL_MAX_DEPTH];\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, (void *)v, &sz, NULL, 0);\t\t\\\n} while (0)\n\n#define CTL_M2_M4_GET(n, i, j, v, t) do {\t\t\t\t\\\n\tsize_t mib[CTL_MAX_DEPTH];\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, (void *)v, &sz, NULL, 0);\t\t\\\n} while (0)\n\n/******************************************************************************/\n/* Data. */\n\nbool opt_stats_print = false;\nchar opt_stats_print_opts[stats_print_tot_num_options+1] = \"\";\n\n/******************************************************************************/\n\n/* Calculate x.yyy and output a string (takes a fixed sized char array). */\nstatic bool\nget_rate_str(uint64_t dividend, uint64_t divisor, char str[6]) {\n\tif (divisor == 0 || dividend > divisor) {\n\t\t/* The rate is not supposed to be greater than 1. */\n\t\treturn true;\n\t}\n\tif (dividend > 0) {\n\t\tassert(UINT64_MAX / dividend >= 1000);\n\t}\n\n\tunsigned n = (unsigned)((dividend * 1000) / divisor);\n\tif (n < 10) {\n\t\tmalloc_snprintf(str, 6, \"0.00%u\", n);\n\t} else if (n < 100) {\n\t\tmalloc_snprintf(str, 6, \"0.0%u\", n);\n\t} else if (n < 1000) {\n\t\tmalloc_snprintf(str, 6, \"0.%u\", n);\n\t} else {\n\t\tmalloc_snprintf(str, 6, \"1\");\n\t}\n\n\treturn false;\n}\n\n#define MUTEX_CTL_STR_MAX_LENGTH 128\nstatic void\ngen_mutex_ctl_str(char *str, size_t buf_len, const char *prefix,\n    const char *mutex, const char *counter) {\n\tmalloc_snprintf(str, buf_len, \"stats.%s.%s.%s\", prefix, mutex, counter);\n}\n\nstatic void\nmutex_stats_init_cols(emitter_row_t *row, const char *table_name,\n    emitter_col_t *name,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tmutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;\n\tmutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;\n\n\temitter_col_t *col;\n\n\tif (name != NULL) {\n\t\temitter_col_init(name, row);\n\t\tname->justify = emitter_justify_left;\n\t\tname->width = 21;\n\t\tname->type = emitter_type_title;\n\t\tname->str_val = table_name;\n\t}\n\n#define WIDTH_uint32_t 12\n#define WIDTH_uint64_t 16\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tcol = &col_##counter_type[k_##counter_type];\t\t\t\\\n\t++k_##counter_type;\t\t\t\t\t\t\\\n\temitter_col_init(col, row);\t\t\t\t\t\\\n\tcol->justify = emitter_justify_right;\t\t\t\t\\\n\tcol->width = WIDTH_##counter_type;\t\t\t\t\\\n\tcol->type = emitter_type_title;\t\t\t\t\t\\\n\tcol->str_val = human;\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef WIDTH_uint32_t\n#undef WIDTH_uint64_t\n}\n\nstatic void\nmutex_stats_read_global(const char *name, emitter_col_t *col_name,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tchar cmd[MUTEX_CTL_STR_MAX_LENGTH];\n\n\tcol_name->str_val = name;\n\n\temitter_col_t *dst;\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tdst = &col_##counter_type[mutex_counter_##counter];\t\t\\\n\tdst->type = EMITTER_TYPE_##counter_type;\t\t\t\\\n\tgen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,\t\t\\\n\t    \"mutexes\", name, #counter);\t\t\t\t\t\\\n\tCTL_GET(cmd, (counter_type *)&dst->bool_val, counter_type);\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\nstatic void\nmutex_stats_read_arena(unsigned arena_ind, mutex_prof_arena_ind_t mutex_ind,\n    const char *name, emitter_col_t *col_name,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tchar cmd[MUTEX_CTL_STR_MAX_LENGTH];\n\n\tcol_name->str_val = name;\n\n\temitter_col_t *dst;\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tdst = &col_##counter_type[mutex_counter_##counter];\t\t\\\n\tdst->type = EMITTER_TYPE_##counter_type;\t\t\t\\\n\tgen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,\t\t\\\n\t    \"arenas.0.mutexes\",\tarena_mutex_names[mutex_ind], #counter);\\\n\tCTL_M2_GET(cmd, arena_ind,\t\t\t\t\t\\\n\t    (counter_type *)&dst->bool_val, counter_type);\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\nstatic void\nmutex_stats_read_arena_bin(unsigned arena_ind, unsigned bin_ind,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tchar cmd[MUTEX_CTL_STR_MAX_LENGTH];\n\temitter_col_t *dst;\n\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tdst = &col_##counter_type[mutex_counter_##counter];\t\t\\\n\tdst->type = EMITTER_TYPE_##counter_type;\t\t\t\\\n\tgen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,\t\t\\\n\t    \"arenas.0.bins.0\",\"mutex\", #counter);\t\t\t\\\n\tCTL_M2_M4_GET(cmd, arena_ind, bin_ind,\t\t\t\t\\\n\t    (counter_type *)&dst->bool_val, counter_type);\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\n/* \"row\" can be NULL to avoid emitting in table mode. */\nstatic void\nmutex_stats_emit(emitter_t *emitter, emitter_row_t *row,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tif (row != NULL) {\n\t\temitter_table_row(emitter, row);\n\t}\n\n\tmutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;\n\tmutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;\n\n\temitter_col_t *col;\n\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, type, human)\t\t\t\t\t\\\n\tcol = &col_##type[k_##type];\t\t\t\t\t\t\\\n\t++k_##type;\t\t\t\t\t\t\t\\\n\temitter_json_kv(emitter, #counter, EMITTER_TYPE_##type,\t\t\\\n\t    (const void *)&col->bool_val);\n\tMUTEX_PROF_COUNTERS;\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\nstatic void\nstats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {\n\tsize_t page;\n\tbool in_gap, in_gap_prev;\n\tunsigned nbins, j;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\n\temitter_row_t header_row;\n\temitter_row_init(&header_row);\n\n\temitter_row_t row;\n\temitter_row_init(&row);\n#define COL(name, left_or_right, col_width, etype)\t\t\t\\\n\temitter_col_t col_##name;\t\t\t\t\t\\\n\temitter_col_init(&col_##name, &row);\t\t\t\t\\\n\tcol_##name.justify = emitter_justify_##left_or_right;\t\t\\\n\tcol_##name.width = col_width;\t\t\t\t\t\\\n\tcol_##name.type = emitter_type_##etype;\t\t\t\t\\\n\temitter_col_t header_col_##name;\t\t\t\t\\\n\temitter_col_init(&header_col_##name, &header_row);\t\t\\\n\theader_col_##name.justify = emitter_justify_##left_or_right;\t\\\n\theader_col_##name.width = col_width;\t\t\t\t\\\n\theader_col_##name.type = emitter_type_title;\t\t\t\\\n\theader_col_##name.str_val = #name;\n\n\tCOL(size, right, 20, size)\n\tCOL(ind, right, 4, unsigned)\n\tCOL(allocated, right, 13, uint64)\n\tCOL(nmalloc, right, 13, uint64)\n\tCOL(ndalloc, right, 13, uint64)\n\tCOL(nrequests, right, 13, uint64)\n\tCOL(curregs, right, 13, size)\n\tCOL(curslabs, right, 13, size)\n\tCOL(regs, right, 5, unsigned)\n\tCOL(pgs, right, 4, size)\n\t/* To buffer a right- and left-justified column. */\n\tCOL(justify_spacer, right, 1, title)\n\tCOL(util, right, 6, title)\n\tCOL(nfills, right, 13, uint64)\n\tCOL(nflushes, right, 13, uint64)\n\tCOL(nslabs, right, 13, uint64)\n\tCOL(nreslabs, right, 13, uint64)\n#undef COL\n\n\t/* Don't want to actually print the name. */\n\theader_col_justify_spacer.str_val = \" \";\n\tcol_justify_spacer.str_val = \" \";\n\n\n\temitter_col_t col_mutex64[mutex_prof_num_uint64_t_counters];\n\temitter_col_t col_mutex32[mutex_prof_num_uint32_t_counters];\n\n\temitter_col_t header_mutex64[mutex_prof_num_uint64_t_counters];\n\temitter_col_t header_mutex32[mutex_prof_num_uint32_t_counters];\n\n\tif (mutex) {\n\t\tmutex_stats_init_cols(&row, NULL, NULL, col_mutex64,\n\t\t    col_mutex32);\n\t\tmutex_stats_init_cols(&header_row, NULL, NULL, header_mutex64,\n\t\t    header_mutex32);\n\t}\n\n\t/*\n\t * We print a \"bins:\" header as part of the table row; we need to adjust\n\t * the header size column to compensate.\n\t */\n\theader_col_size.width -=5;\n\temitter_table_printf(emitter, \"bins:\");\n\temitter_table_row(emitter, &header_row);\n\temitter_json_arr_begin(emitter, \"bins\");\n\n\tfor (j = 0, in_gap = false; j < nbins; j++) {\n\t\tuint64_t nslabs;\n\t\tsize_t reg_size, slab_size, curregs;\n\t\tsize_t curslabs;\n\t\tuint32_t nregs;\n\t\tuint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;\n\t\tuint64_t nreslabs;\n\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nslabs\", i, j, &nslabs,\n\t\t    uint64_t);\n\t\tin_gap_prev = in_gap;\n\t\tin_gap = (nslabs == 0);\n\n\t\tif (in_gap_prev && !in_gap) {\n\t\t\temitter_table_printf(emitter,\n\t\t\t    \"                     ---\\n\");\n\t\t}\n\n\t\tCTL_M2_GET(\"arenas.bin.0.size\", j, &reg_size, size_t);\n\t\tCTL_M2_GET(\"arenas.bin.0.nregs\", j, &nregs, uint32_t);\n\t\tCTL_M2_GET(\"arenas.bin.0.slab_size\", j, &slab_size, size_t);\n\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nmalloc\", i, j, &nmalloc,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.ndalloc\", i, j, &ndalloc,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.curregs\", i, j, &curregs,\n\t\t    size_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nrequests\", i, j,\n\t\t    &nrequests, uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nfills\", i, j, &nfills,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nflushes\", i, j, &nflushes,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nreslabs\", i, j, &nreslabs,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.curslabs\", i, j, &curslabs,\n\t\t    size_t);\n\n\t\tif (mutex) {\n\t\t\tmutex_stats_read_arena_bin(i, j, col_mutex64,\n\t\t\t    col_mutex32);\n\t\t}\n\n\t\temitter_json_arr_obj_begin(emitter);\n\t\temitter_json_kv(emitter, \"nmalloc\", emitter_type_uint64,\n\t\t    &nmalloc);\n\t\temitter_json_kv(emitter, \"ndalloc\", emitter_type_uint64,\n\t\t    &ndalloc);\n\t\temitter_json_kv(emitter, \"curregs\", emitter_type_size,\n\t\t    &curregs);\n\t\temitter_json_kv(emitter, \"nrequests\", emitter_type_uint64,\n\t\t    &nrequests);\n\t\temitter_json_kv(emitter, \"nfills\", emitter_type_uint64,\n\t\t    &nfills);\n\t\temitter_json_kv(emitter, \"nflushes\", emitter_type_uint64,\n\t\t    &nflushes);\n\t\temitter_json_kv(emitter, \"nreslabs\", emitter_type_uint64,\n\t\t    &nreslabs);\n\t\temitter_json_kv(emitter, \"curslabs\", emitter_type_size,\n\t\t    &curslabs);\n\t\tif (mutex) {\n\t\t\temitter_json_dict_begin(emitter, \"mutex\");\n\t\t\tmutex_stats_emit(emitter, NULL, col_mutex64,\n\t\t\t    col_mutex32);\n\t\t\temitter_json_dict_end(emitter);\n\t\t}\n\t\temitter_json_arr_obj_end(emitter);\n\n\t\tsize_t availregs = nregs * curslabs;\n\t\tchar util[6];\n\t\tif (get_rate_str((uint64_t)curregs, (uint64_t)availregs, util))\n\t\t{\n\t\t\tif (availregs == 0) {\n\t\t\t\tmalloc_snprintf(util, sizeof(util), \"1\");\n\t\t\t} else if (curregs > availregs) {\n\t\t\t\t/*\n\t\t\t\t * Race detected: the counters were read in\n\t\t\t\t * separate mallctl calls and concurrent\n\t\t\t\t * operations happened in between.  In this case\n\t\t\t\t * no meaningful utilization can be computed.\n\t\t\t\t */\n\t\t\t\tmalloc_snprintf(util, sizeof(util), \" race\");\n\t\t\t} else {\n\t\t\t\tnot_reached();\n\t\t\t}\n\t\t}\n\n\t\tcol_size.size_val = reg_size;\n\t\tcol_ind.unsigned_val = j;\n\t\tcol_allocated.size_val = curregs * reg_size;\n\t\tcol_nmalloc.uint64_val = nmalloc;\n\t\tcol_ndalloc.uint64_val = ndalloc;\n\t\tcol_nrequests.uint64_val = nrequests;\n\t\tcol_curregs.size_val = curregs;\n\t\tcol_curslabs.size_val = curslabs;\n\t\tcol_regs.unsigned_val = nregs;\n\t\tcol_pgs.size_val = slab_size / page;\n\t\tcol_util.str_val = util;\n\t\tcol_nfills.uint64_val = nfills;\n\t\tcol_nflushes.uint64_val = nflushes;\n\t\tcol_nslabs.uint64_val = nslabs;\n\t\tcol_nreslabs.uint64_val = nreslabs;\n\n\t\t/*\n\t\t * Note that mutex columns were initialized above, if mutex ==\n\t\t * true.\n\t\t */\n\n\t\temitter_table_row(emitter, &row);\n\t}\n\temitter_json_arr_end(emitter); /* Close \"bins\". */\n\n\tif (in_gap) {\n\t\temitter_table_printf(emitter, \"                     ---\\n\");\n\t}\n}\n\nstatic void\nstats_arena_lextents_print(emitter_t *emitter, unsigned i) {\n\tunsigned nbins, nlextents, j;\n\tbool in_gap, in_gap_prev;\n\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\tCTL_GET(\"arenas.nlextents\", &nlextents, unsigned);\n\n\temitter_row_t header_row;\n\temitter_row_init(&header_row);\n\temitter_row_t row;\n\temitter_row_init(&row);\n\n#define COL(name, left_or_right, col_width, etype)\t\t\t\\\n\temitter_col_t header_##name;\t\t\t\t\t\\\n\temitter_col_init(&header_##name, &header_row);\t\t\t\\\n\theader_##name.justify = emitter_justify_##left_or_right;\t\\\n\theader_##name.width = col_width;\t\t\t\t\\\n\theader_##name.type = emitter_type_title;\t\t\t\\\n\theader_##name.str_val = #name;\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\temitter_col_t col_##name;\t\t\t\t\t\\\n\temitter_col_init(&col_##name, &row);\t\t\t\t\\\n\tcol_##name.justify = emitter_justify_##left_or_right;\t\t\\\n\tcol_##name.width = col_width;\t\t\t\t\t\\\n\tcol_##name.type = emitter_type_##etype;\n\n\tCOL(size, right, 20, size)\n\tCOL(ind, right, 4, unsigned)\n\tCOL(allocated, right, 13, size)\n\tCOL(nmalloc, right, 13, uint64)\n\tCOL(ndalloc, right, 13, uint64)\n\tCOL(nrequests, right, 13, uint64)\n\tCOL(curlextents, right, 13, size)\n#undef COL\n\n\t/* As with bins, we label the large extents table. */\n\theader_size.width -= 6;\n\temitter_table_printf(emitter, \"large:\");\n\temitter_table_row(emitter, &header_row);\n\temitter_json_arr_begin(emitter, \"lextents\");\n\n\tfor (j = 0, in_gap = false; j < nlextents; j++) {\n\t\tuint64_t nmalloc, ndalloc, nrequests;\n\t\tsize_t lextent_size, curlextents;\n\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.nmalloc\", i, j,\n\t\t    &nmalloc, uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.ndalloc\", i, j,\n\t\t    &ndalloc, uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.nrequests\", i, j,\n\t\t    &nrequests, uint64_t);\n\t\tin_gap_prev = in_gap;\n\t\tin_gap = (nrequests == 0);\n\n\t\tif (in_gap_prev && !in_gap) {\n\t\t\temitter_table_printf(emitter,\n\t\t\t    \"                     ---\\n\");\n\t\t}\n\n\t\tCTL_M2_GET(\"arenas.lextent.0.size\", j, &lextent_size, size_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.curlextents\", i, j,\n\t\t    &curlextents, size_t);\n\n\t\temitter_json_arr_obj_begin(emitter);\n\t\temitter_json_kv(emitter, \"curlextents\", emitter_type_size,\n\t\t    &curlextents);\n\t\temitter_json_arr_obj_end(emitter);\n\n\t\tcol_size.size_val = lextent_size;\n\t\tcol_ind.unsigned_val = nbins + j;\n\t\tcol_allocated.size_val = curlextents * lextent_size;\n\t\tcol_nmalloc.uint64_val = nmalloc;\n\t\tcol_ndalloc.uint64_val = ndalloc;\n\t\tcol_nrequests.uint64_val = nrequests;\n\t\tcol_curlextents.size_val = curlextents;\n\n\t\tif (!in_gap) {\n\t\t\temitter_table_row(emitter, &row);\n\t\t}\n\t}\n\temitter_json_arr_end(emitter); /* Close \"lextents\". */\n\tif (in_gap) {\n\t\temitter_table_printf(emitter, \"                     ---\\n\");\n\t}\n}\n\nstatic void\nstats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind) {\n\temitter_row_t row;\n\temitter_col_t col_name;\n\temitter_col_t col64[mutex_prof_num_uint64_t_counters];\n\temitter_col_t col32[mutex_prof_num_uint32_t_counters];\n\n\temitter_row_init(&row);\n\tmutex_stats_init_cols(&row, \"\", &col_name, col64, col32);\n\n\temitter_json_dict_begin(emitter, \"mutexes\");\n\temitter_table_row(emitter, &row);\n\n\tfor (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes;\n\t    i++) {\n\t\tconst char *name = arena_mutex_names[i];\n\t\temitter_json_dict_begin(emitter, name);\n\t\tmutex_stats_read_arena(arena_ind, i, name, &col_name, col64,\n\t\t    col32);\n\t\tmutex_stats_emit(emitter, &row, col64, col32);\n\t\temitter_json_dict_end(emitter); /* Close the mutex dict. */\n\t}\n\temitter_json_dict_end(emitter); /* End \"mutexes\". */\n}\n\nstatic void\nstats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,\n    bool mutex) {\n\tunsigned nthreads;\n\tconst char *dss;\n\tssize_t dirty_decay_ms, muzzy_decay_ms;\n\tsize_t page, pactive, pdirty, pmuzzy, mapped, retained;\n\tsize_t base, internal, resident, metadata_thp;\n\tuint64_t dirty_npurge, dirty_nmadvise, dirty_purged;\n\tuint64_t muzzy_npurge, muzzy_nmadvise, muzzy_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\tsize_t tcache_bytes;\n\tuint64_t uptime;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tCTL_M2_GET(\"stats.arenas.0.nthreads\", i, &nthreads, unsigned);\n\temitter_kv(emitter, \"nthreads\", \"assigned threads\",\n\t    emitter_type_unsigned, &nthreads);\n\n\tCTL_M2_GET(\"stats.arenas.0.uptime\", i, &uptime, uint64_t);\n\temitter_kv(emitter, \"uptime_ns\", \"uptime\", emitter_type_uint64,\n\t    &uptime);\n\n\tCTL_M2_GET(\"stats.arenas.0.dss\", i, &dss, const char *);\n\temitter_kv(emitter, \"dss\", \"dss allocation precedence\",\n\t    emitter_type_string, &dss);\n\n\tCTL_M2_GET(\"stats.arenas.0.dirty_decay_ms\", i, &dirty_decay_ms,\n\t    ssize_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_decay_ms\", i, &muzzy_decay_ms,\n\t    ssize_t);\n\tCTL_M2_GET(\"stats.arenas.0.pactive\", i, &pactive, size_t);\n\tCTL_M2_GET(\"stats.arenas.0.pdirty\", i, &pdirty, size_t);\n\tCTL_M2_GET(\"stats.arenas.0.pmuzzy\", i, &pmuzzy, size_t);\n\tCTL_M2_GET(\"stats.arenas.0.dirty_npurge\", i, &dirty_npurge, uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.dirty_nmadvise\", i, &dirty_nmadvise,\n\t    uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.dirty_purged\", i, &dirty_purged, uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_npurge\", i, &muzzy_npurge, uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_nmadvise\", i, &muzzy_nmadvise,\n\t    uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_purged\", i, &muzzy_purged, uint64_t);\n\n\temitter_row_t decay_row;\n\temitter_row_init(&decay_row);\n\n\t/* JSON-style emission. */\n\temitter_json_kv(emitter, \"dirty_decay_ms\", emitter_type_ssize,\n\t    &dirty_decay_ms);\n\temitter_json_kv(emitter, \"muzzy_decay_ms\", emitter_type_ssize,\n\t    &muzzy_decay_ms);\n\n\temitter_json_kv(emitter, \"pactive\", emitter_type_size, &pactive);\n\temitter_json_kv(emitter, \"pdirty\", emitter_type_size, &pdirty);\n\temitter_json_kv(emitter, \"pmuzzy\", emitter_type_size, &pmuzzy);\n\n\temitter_json_kv(emitter, \"dirty_npurge\", emitter_type_uint64,\n\t    &dirty_npurge);\n\temitter_json_kv(emitter, \"dirty_nmadvise\", emitter_type_uint64,\n\t    &dirty_nmadvise);\n\temitter_json_kv(emitter, \"dirty_purged\", emitter_type_uint64,\n\t    &dirty_purged);\n\n\temitter_json_kv(emitter, \"muzzy_npurge\", emitter_type_uint64,\n\t    &muzzy_npurge);\n\temitter_json_kv(emitter, \"muzzy_nmadvise\", emitter_type_uint64,\n\t    &muzzy_nmadvise);\n\temitter_json_kv(emitter, \"muzzy_purged\", emitter_type_uint64,\n\t    &muzzy_purged);\n\n\t/* Table-style emission. */\n\temitter_col_t decay_type;\n\temitter_col_init(&decay_type, &decay_row);\n\tdecay_type.justify = emitter_justify_right;\n\tdecay_type.width = 9;\n\tdecay_type.type = emitter_type_title;\n\tdecay_type.str_val = \"decaying:\";\n\n\temitter_col_t decay_time;\n\temitter_col_init(&decay_time, &decay_row);\n\tdecay_time.justify = emitter_justify_right;\n\tdecay_time.width = 6;\n\tdecay_time.type = emitter_type_title;\n\tdecay_time.str_val = \"time\";\n\n\temitter_col_t decay_npages;\n\temitter_col_init(&decay_npages, &decay_row);\n\tdecay_npages.justify = emitter_justify_right;\n\tdecay_npages.width = 13;\n\tdecay_npages.type = emitter_type_title;\n\tdecay_npages.str_val = \"npages\";\n\n\temitter_col_t decay_sweeps;\n\temitter_col_init(&decay_sweeps, &decay_row);\n\tdecay_sweeps.justify = emitter_justify_right;\n\tdecay_sweeps.width = 13;\n\tdecay_sweeps.type = emitter_type_title;\n\tdecay_sweeps.str_val = \"sweeps\";\n\n\temitter_col_t decay_madvises;\n\temitter_col_init(&decay_madvises, &decay_row);\n\tdecay_madvises.justify = emitter_justify_right;\n\tdecay_madvises.width = 13;\n\tdecay_madvises.type = emitter_type_title;\n\tdecay_madvises.str_val = \"madvises\";\n\n\temitter_col_t decay_purged;\n\temitter_col_init(&decay_purged, &decay_row);\n\tdecay_purged.justify = emitter_justify_right;\n\tdecay_purged.width = 13;\n\tdecay_purged.type = emitter_type_title;\n\tdecay_purged.str_val = \"purged\";\n\n\t/* Title row. */\n\temitter_table_row(emitter, &decay_row);\n\n\t/* Dirty row. */\n\tdecay_type.str_val = \"dirty:\";\n\n\tif (dirty_decay_ms >= 0) {\n\t\tdecay_time.type = emitter_type_ssize;\n\t\tdecay_time.ssize_val = dirty_decay_ms;\n\t} else {\n\t\tdecay_time.type = emitter_type_title;\n\t\tdecay_time.str_val = \"N/A\";\n\t}\n\n\tdecay_npages.type = emitter_type_size;\n\tdecay_npages.size_val = pdirty;\n\n\tdecay_sweeps.type = emitter_type_uint64;\n\tdecay_sweeps.uint64_val = dirty_npurge;\n\n\tdecay_madvises.type = emitter_type_uint64;\n\tdecay_madvises.uint64_val = dirty_nmadvise;\n\n\tdecay_purged.type = emitter_type_uint64;\n\tdecay_purged.uint64_val = dirty_purged;\n\n\temitter_table_row(emitter, &decay_row);\n\n\t/* Muzzy row. */\n\tdecay_type.str_val = \"muzzy:\";\n\n\tif (muzzy_decay_ms >= 0) {\n\t\tdecay_time.type = emitter_type_ssize;\n\t\tdecay_time.ssize_val = muzzy_decay_ms;\n\t} else {\n\t\tdecay_time.type = emitter_type_title;\n\t\tdecay_time.str_val = \"N/A\";\n\t}\n\n\tdecay_npages.type = emitter_type_size;\n\tdecay_npages.size_val = pmuzzy;\n\n\tdecay_sweeps.type = emitter_type_uint64;\n\tdecay_sweeps.uint64_val = muzzy_npurge;\n\n\tdecay_madvises.type = emitter_type_uint64;\n\tdecay_madvises.uint64_val = muzzy_nmadvise;\n\n\tdecay_purged.type = emitter_type_uint64;\n\tdecay_purged.uint64_val = muzzy_purged;\n\n\temitter_table_row(emitter, &decay_row);\n\n\t/* Small / large / total allocation counts. */\n\temitter_row_t alloc_count_row;\n\temitter_row_init(&alloc_count_row);\n\n\temitter_col_t alloc_count_title;\n\temitter_col_init(&alloc_count_title, &alloc_count_row);\n\talloc_count_title.justify = emitter_justify_left;\n\talloc_count_title.width = 25;\n\talloc_count_title.type = emitter_type_title;\n\talloc_count_title.str_val = \"\";\n\n\temitter_col_t alloc_count_allocated;\n\temitter_col_init(&alloc_count_allocated, &alloc_count_row);\n\talloc_count_allocated.justify = emitter_justify_right;\n\talloc_count_allocated.width = 12;\n\talloc_count_allocated.type = emitter_type_title;\n\talloc_count_allocated.str_val = \"allocated\";\n\n\temitter_col_t alloc_count_nmalloc;\n\temitter_col_init(&alloc_count_nmalloc, &alloc_count_row);\n\talloc_count_nmalloc.justify = emitter_justify_right;\n\talloc_count_nmalloc.width = 12;\n\talloc_count_nmalloc.type = emitter_type_title;\n\talloc_count_nmalloc.str_val = \"nmalloc\";\n\n\temitter_col_t alloc_count_ndalloc;\n\temitter_col_init(&alloc_count_ndalloc, &alloc_count_row);\n\talloc_count_ndalloc.justify = emitter_justify_right;\n\talloc_count_ndalloc.width = 12;\n\talloc_count_ndalloc.type = emitter_type_title;\n\talloc_count_ndalloc.str_val = \"ndalloc\";\n\n\temitter_col_t alloc_count_nrequests;\n\temitter_col_init(&alloc_count_nrequests, &alloc_count_row);\n\talloc_count_nrequests.justify = emitter_justify_right;\n\talloc_count_nrequests.width = 12;\n\talloc_count_nrequests.type = emitter_type_title;\n\talloc_count_nrequests.str_val = \"nrequests\";\n\n\temitter_table_row(emitter, &alloc_count_row);\n\n#define GET_AND_EMIT_ALLOC_STAT(small_or_large, name, valtype)\t\t\\\n\tCTL_M2_GET(\"stats.arenas.0.\" #small_or_large \".\" #name, i,\t\\\n\t    &small_or_large##_##name, valtype##_t);\t\t\t\\\n\temitter_json_kv(emitter, #name, emitter_type_##valtype,\t\t\\\n\t    &small_or_large##_##name);\t\t\t\t\t\\\n\talloc_count_##name.type = emitter_type_##valtype;\t\t\\\n\talloc_count_##name.valtype##_val = small_or_large##_##name;\n\n\temitter_json_dict_begin(emitter, \"small\");\n\talloc_count_title.str_val = \"small:\";\n\n\tGET_AND_EMIT_ALLOC_STAT(small, allocated, size)\n\tGET_AND_EMIT_ALLOC_STAT(small, nmalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(small, ndalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64)\n\n\temitter_table_row(emitter, &alloc_count_row);\n\temitter_json_dict_end(emitter); /* Close \"small\". */\n\n\temitter_json_dict_begin(emitter, \"large\");\n\talloc_count_title.str_val = \"large:\";\n\n\tGET_AND_EMIT_ALLOC_STAT(large, allocated, size)\n\tGET_AND_EMIT_ALLOC_STAT(large, nmalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(large, ndalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64)\n\n\temitter_table_row(emitter, &alloc_count_row);\n\temitter_json_dict_end(emitter); /* Close \"large\". */\n\n#undef GET_AND_EMIT_ALLOC_STAT\n\n\t/* Aggregated small + large stats are emitter only in table mode. */\n\talloc_count_title.str_val = \"total:\";\n\talloc_count_allocated.size_val = small_allocated + large_allocated;\n\talloc_count_nmalloc.uint64_val = small_nmalloc + large_nmalloc;\n\talloc_count_ndalloc.uint64_val = small_ndalloc + large_ndalloc;\n\talloc_count_nrequests.uint64_val = small_nrequests + large_nrequests;\n\temitter_table_row(emitter, &alloc_count_row);\n\n\temitter_row_t mem_count_row;\n\temitter_row_init(&mem_count_row);\n\n\temitter_col_t mem_count_title;\n\temitter_col_init(&mem_count_title, &mem_count_row);\n\tmem_count_title.justify = emitter_justify_left;\n\tmem_count_title.width = 25;\n\tmem_count_title.type = emitter_type_title;\n\tmem_count_title.str_val = \"\";\n\n\temitter_col_t mem_count_val;\n\temitter_col_init(&mem_count_val, &mem_count_row);\n\tmem_count_val.justify = emitter_justify_right;\n\tmem_count_val.width = 12;\n\tmem_count_val.type = emitter_type_title;\n\tmem_count_val.str_val = \"\";\n\n\temitter_table_row(emitter, &mem_count_row);\n\tmem_count_val.type = emitter_type_size;\n\n\t/* Active count in bytes is emitted only in table mode. */\n\tmem_count_title.str_val = \"active:\";\n\tmem_count_val.size_val = pactive * page;\n\temitter_table_row(emitter, &mem_count_row);\n\n#define GET_AND_EMIT_MEM_STAT(stat)\t\t\t\t\t\\\n\tCTL_M2_GET(\"stats.arenas.0.\"#stat, i, &stat, size_t);\t\t\\\n\temitter_json_kv(emitter, #stat, emitter_type_size, &stat);\t\\\n\tmem_count_title.str_val = #stat\":\";\t\t\t\t\\\n\tmem_count_val.size_val = stat;\t\t\t\t\t\\\n\temitter_table_row(emitter, &mem_count_row);\n\n\tGET_AND_EMIT_MEM_STAT(mapped)\n\tGET_AND_EMIT_MEM_STAT(retained)\n\tGET_AND_EMIT_MEM_STAT(base)\n\tGET_AND_EMIT_MEM_STAT(internal)\n\tGET_AND_EMIT_MEM_STAT(metadata_thp)\n\tGET_AND_EMIT_MEM_STAT(tcache_bytes)\n\tGET_AND_EMIT_MEM_STAT(resident)\n#undef GET_AND_EMIT_MEM_STAT\n\n\tif (mutex) {\n\t\tstats_arena_mutexes_print(emitter, i);\n\t}\n\tif (bins) {\n\t\tstats_arena_bins_print(emitter, mutex, i);\n\t}\n\tif (large) {\n\t\tstats_arena_lextents_print(emitter, i);\n\t}\n}\n\nstatic void\nstats_general_print(emitter_t *emitter) {\n\tconst char *cpv;\n\tbool bv, bv2;\n\tunsigned uv;\n\tuint32_t u32v;\n\tuint64_t u64v;\n\tssize_t ssv, ssv2;\n\tsize_t sv, bsz, usz, ssz, sssz, cpsz;\n\n\tbsz = sizeof(bool);\n\tusz = sizeof(unsigned);\n\tssz = sizeof(size_t);\n\tsssz = sizeof(ssize_t);\n\tcpsz = sizeof(const char *);\n\n\tCTL_GET(\"version\", &cpv, const char *);\n\temitter_kv(emitter, \"version\", \"Version\", emitter_type_string, &cpv);\n\n\t/* config. */\n\temitter_dict_begin(emitter, \"config\", \"Build-time option settings\");\n#define CONFIG_WRITE_BOOL(name)\t\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tCTL_GET(\"config.\"#name, &bv, bool);\t\t\t\\\n\t\temitter_kv(emitter, #name, \"config.\"#name,\t\t\\\n\t\t    emitter_type_bool, &bv);\t\t\t\t\\\n\t} while (0)\n\n\tCONFIG_WRITE_BOOL(cache_oblivious);\n\tCONFIG_WRITE_BOOL(debug);\n\tCONFIG_WRITE_BOOL(fill);\n\tCONFIG_WRITE_BOOL(lazy_lock);\n\temitter_kv(emitter, \"malloc_conf\", \"config.malloc_conf\",\n\t    emitter_type_string, &config_malloc_conf);\n\n\tCONFIG_WRITE_BOOL(prof);\n\tCONFIG_WRITE_BOOL(prof_libgcc);\n\tCONFIG_WRITE_BOOL(prof_libunwind);\n\tCONFIG_WRITE_BOOL(stats);\n\tCONFIG_WRITE_BOOL(utrace);\n\tCONFIG_WRITE_BOOL(xmalloc);\n#undef CONFIG_WRITE_BOOL\n\temitter_dict_end(emitter); /* Close \"config\" dict. */\n\n\t/* opt. */\n#define OPT_WRITE(name, var, size, emitter_type)\t\t\t\\\n\tif (je_mallctl(\"opt.\"name, (void *)&var, &size, NULL, 0) ==\t\\\n\t    0) {\t\t\t\t\t\t\t\\\n\t\temitter_kv(emitter, name, \"opt.\"name, emitter_type,\t\\\n\t\t    &var);\t\t\t\t\t\t\\\n\t}\n\n#define OPT_WRITE_MUTABLE(name, var1, var2, size, emitter_type,\t\t\\\n    altname)\t\t\t\t\t\t\t\t\\\n\tif (je_mallctl(\"opt.\"name, (void *)&var1, &size, NULL, 0) ==\t\\\n\t    0 && je_mallctl(altname, (void *)&var2, &size, NULL, 0)\t\\\n\t    == 0) {\t\t\t\t\t\t\t\\\n\t\temitter_kv_note(emitter, name, \"opt.\"name,\t\t\\\n\t\t    emitter_type, &var1, altname, emitter_type,\t\t\\\n\t\t    &var2);\t\t\t\t\t\t\\\n\t}\n\n#define OPT_WRITE_BOOL(name) OPT_WRITE(name, bv, bsz, emitter_type_bool)\n#define OPT_WRITE_BOOL_MUTABLE(name, altname)\t\t\t\t\\\n\tOPT_WRITE_MUTABLE(name, bv, bv2, bsz, emitter_type_bool, altname)\n\n#define OPT_WRITE_UNSIGNED(name)\t\t\t\t\t\\\n\tOPT_WRITE(name, uv, usz, emitter_type_unsigned)\n\n#define OPT_WRITE_SSIZE_T(name)\t\t\t\t\t\t\\\n\tOPT_WRITE(name, ssv, sssz, emitter_type_ssize)\n#define OPT_WRITE_SSIZE_T_MUTABLE(name, altname)\t\t\t\\\n\tOPT_WRITE_MUTABLE(name, ssv, ssv2, sssz, emitter_type_ssize,\t\\\n\t    altname)\n\n#define OPT_WRITE_CHAR_P(name)\t\t\t\t\t\t\\\n\tOPT_WRITE(name, cpv, cpsz, emitter_type_string)\n\n\temitter_dict_begin(emitter, \"opt\", \"Run-time option settings\");\n\n\tOPT_WRITE_BOOL(\"abort\")\n\tOPT_WRITE_BOOL(\"abort_conf\")\n\tOPT_WRITE_BOOL(\"retain\")\n\tOPT_WRITE_CHAR_P(\"dss\")\n\tOPT_WRITE_UNSIGNED(\"narenas\")\n\tOPT_WRITE_CHAR_P(\"percpu_arena\")\n\tOPT_WRITE_CHAR_P(\"metadata_thp\")\n\tOPT_WRITE_BOOL_MUTABLE(\"background_thread\", \"background_thread\")\n\tOPT_WRITE_SSIZE_T_MUTABLE(\"dirty_decay_ms\", \"arenas.dirty_decay_ms\")\n\tOPT_WRITE_SSIZE_T_MUTABLE(\"muzzy_decay_ms\", \"arenas.muzzy_decay_ms\")\n\tOPT_WRITE_UNSIGNED(\"lg_extent_max_active_fit\")\n\tOPT_WRITE_CHAR_P(\"junk\")\n\tOPT_WRITE_BOOL(\"zero\")\n\tOPT_WRITE_BOOL(\"utrace\")\n\tOPT_WRITE_BOOL(\"xmalloc\")\n\tOPT_WRITE_BOOL(\"tcache\")\n\tOPT_WRITE_SSIZE_T(\"lg_tcache_max\")\n\tOPT_WRITE_CHAR_P(\"thp\")\n\tOPT_WRITE_BOOL(\"prof\")\n\tOPT_WRITE_CHAR_P(\"prof_prefix\")\n\tOPT_WRITE_BOOL_MUTABLE(\"prof_active\", \"prof.active\")\n\tOPT_WRITE_BOOL_MUTABLE(\"prof_thread_active_init\",\n\t    \"prof.thread_active_init\")\n\tOPT_WRITE_SSIZE_T_MUTABLE(\"lg_prof_sample\", \"prof.lg_sample\")\n\tOPT_WRITE_BOOL(\"prof_accum\")\n\tOPT_WRITE_SSIZE_T(\"lg_prof_interval\")\n\tOPT_WRITE_BOOL(\"prof_gdump\")\n\tOPT_WRITE_BOOL(\"prof_final\")\n\tOPT_WRITE_BOOL(\"prof_leak\")\n\tOPT_WRITE_BOOL(\"stats_print\")\n\tOPT_WRITE_CHAR_P(\"stats_print_opts\")\n\n\temitter_dict_end(emitter);\n\n#undef OPT_WRITE\n#undef OPT_WRITE_MUTABLE\n#undef OPT_WRITE_BOOL\n#undef OPT_WRITE_BOOL_MUTABLE\n#undef OPT_WRITE_UNSIGNED\n#undef OPT_WRITE_SSIZE_T\n#undef OPT_WRITE_SSIZE_T_MUTABLE\n#undef OPT_WRITE_CHAR_P\n\n\t/* prof. */\n\tif (config_prof) {\n\t\temitter_dict_begin(emitter, \"prof\", \"Profiling settings\");\n\n\t\tCTL_GET(\"prof.thread_active_init\", &bv, bool);\n\t\temitter_kv(emitter, \"thread_active_init\",\n\t\t    \"prof.thread_active_init\", emitter_type_bool, &bv);\n\n\t\tCTL_GET(\"prof.active\", &bv, bool);\n\t\temitter_kv(emitter, \"active\", \"prof.active\", emitter_type_bool,\n\t\t    &bv);\n\n\t\tCTL_GET(\"prof.gdump\", &bv, bool);\n\t\temitter_kv(emitter, \"gdump\", \"prof.gdump\", emitter_type_bool,\n\t\t    &bv);\n\n\t\tCTL_GET(\"prof.interval\", &u64v, uint64_t);\n\t\temitter_kv(emitter, \"interval\", \"prof.interval\",\n\t\t    emitter_type_uint64, &u64v);\n\n\t\tCTL_GET(\"prof.lg_sample\", &ssv, ssize_t);\n\t\temitter_kv(emitter, \"lg_sample\", \"prof.lg_sample\",\n\t\t    emitter_type_ssize, &ssv);\n\n\t\temitter_dict_end(emitter); /* Close \"prof\". */\n\t}\n\n\t/* arenas. */\n\t/*\n\t * The json output sticks arena info into an \"arenas\" dict; the table\n\t * output puts them at the top-level.\n\t */\n\temitter_json_dict_begin(emitter, \"arenas\");\n\n\tCTL_GET(\"arenas.narenas\", &uv, unsigned);\n\temitter_kv(emitter, \"narenas\", \"Arenas\", emitter_type_unsigned, &uv);\n\n\t/*\n\t * Decay settings are emitted only in json mode; in table mode, they're\n\t * emitted as notes with the opt output, above.\n\t */\n\tCTL_GET(\"arenas.dirty_decay_ms\", &ssv, ssize_t);\n\temitter_json_kv(emitter, \"dirty_decay_ms\", emitter_type_ssize, &ssv);\n\n\tCTL_GET(\"arenas.muzzy_decay_ms\", &ssv, ssize_t);\n\temitter_json_kv(emitter, \"muzzy_decay_ms\", emitter_type_ssize, &ssv);\n\n\tCTL_GET(\"arenas.quantum\", &sv, size_t);\n\temitter_kv(emitter, \"quantum\", \"Quantum size\", emitter_type_size, &sv);\n\n\tCTL_GET(\"arenas.page\", &sv, size_t);\n\temitter_kv(emitter, \"page\", \"Page size\", emitter_type_size, &sv);\n\n\tif (je_mallctl(\"arenas.tcache_max\", (void *)&sv, &ssz, NULL, 0) == 0) {\n\t\temitter_kv(emitter, \"tcache_max\",\n\t\t    \"Maximum thread-cached size class\", emitter_type_size, &sv);\n\t}\n\n\tunsigned nbins;\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\temitter_kv(emitter, \"nbins\", \"Number of bin size classes\",\n\t    emitter_type_unsigned, &nbins);\n\n\tunsigned nhbins;\n\tCTL_GET(\"arenas.nhbins\", &nhbins, unsigned);\n\temitter_kv(emitter, \"nhbins\", \"Number of thread-cache bin size classes\",\n\t    emitter_type_unsigned, &nhbins);\n\n\t/*\n\t * We do enough mallctls in a loop that we actually want to omit them\n\t * (not just omit the printing).\n\t */\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_arr_begin(emitter, \"bin\");\n\t\tfor (unsigned i = 0; i < nbins; i++) {\n\t\t\temitter_json_arr_obj_begin(emitter);\n\n\t\t\tCTL_M2_GET(\"arenas.bin.0.size\", i, &sv, size_t);\n\t\t\temitter_json_kv(emitter, \"size\", emitter_type_size,\n\t\t\t    &sv);\n\n\t\t\tCTL_M2_GET(\"arenas.bin.0.nregs\", i, &u32v, uint32_t);\n\t\t\temitter_json_kv(emitter, \"nregs\", emitter_type_uint32,\n\t\t\t    &u32v);\n\n\t\t\tCTL_M2_GET(\"arenas.bin.0.slab_size\", i, &sv, size_t);\n\t\t\temitter_json_kv(emitter, \"slab_size\", emitter_type_size,\n\t\t\t    &sv);\n\n\t\t\temitter_json_arr_obj_end(emitter);\n\t\t}\n\t\temitter_json_arr_end(emitter); /* Close \"bin\". */\n\t}\n\n\tunsigned nlextents;\n\tCTL_GET(\"arenas.nlextents\", &nlextents, unsigned);\n\temitter_kv(emitter, \"nlextents\", \"Number of large size classes\",\n\t    emitter_type_unsigned, &nlextents);\n\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_arr_begin(emitter, \"lextent\");\n\t\tfor (unsigned i = 0; i < nlextents; i++) {\n\t\t\temitter_json_arr_obj_begin(emitter);\n\n\t\t\tCTL_M2_GET(\"arenas.lextent.0.size\", i, &sv, size_t);\n\t\t\temitter_json_kv(emitter, \"size\", emitter_type_size,\n\t\t\t    &sv);\n\n\t\t\temitter_json_arr_obj_end(emitter);\n\t\t}\n\t\temitter_json_arr_end(emitter); /* Close \"lextent\". */\n\t}\n\n\temitter_json_dict_end(emitter); /* Close \"arenas\" */\n}\n\nstatic void\nstats_print_helper(emitter_t *emitter, bool merged, bool destroyed,\n    bool unmerged, bool bins, bool large, bool mutex) {\n\t/*\n\t * These should be deleted.  We keep them around for a while, to aid in\n\t * the transition to the emitter code.\n\t */\n\tsize_t allocated, active, metadata, metadata_thp, resident, mapped,\n\t    retained;\n\tsize_t num_background_threads;\n\tuint64_t background_thread_num_runs, background_thread_run_interval;\n\n\tCTL_GET(\"stats.allocated\", &allocated, size_t);\n\tCTL_GET(\"stats.active\", &active, size_t);\n\tCTL_GET(\"stats.metadata\", &metadata, size_t);\n\tCTL_GET(\"stats.metadata_thp\", &metadata_thp, size_t);\n\tCTL_GET(\"stats.resident\", &resident, size_t);\n\tCTL_GET(\"stats.mapped\", &mapped, size_t);\n\tCTL_GET(\"stats.retained\", &retained, size_t);\n\n\tif (have_background_thread) {\n\t\tCTL_GET(\"stats.background_thread.num_threads\",\n\t\t    &num_background_threads, size_t);\n\t\tCTL_GET(\"stats.background_thread.num_runs\",\n\t\t    &background_thread_num_runs, uint64_t);\n\t\tCTL_GET(\"stats.background_thread.run_interval\",\n\t\t    &background_thread_run_interval, uint64_t);\n\t} else {\n\t\tnum_background_threads = 0;\n\t\tbackground_thread_num_runs = 0;\n\t\tbackground_thread_run_interval = 0;\n\t}\n\n\t/* Generic global stats. */\n\temitter_json_dict_begin(emitter, \"stats\");\n\temitter_json_kv(emitter, \"allocated\", emitter_type_size, &allocated);\n\temitter_json_kv(emitter, \"active\", emitter_type_size, &active);\n\temitter_json_kv(emitter, \"metadata\", emitter_type_size, &metadata);\n\temitter_json_kv(emitter, \"metadata_thp\", emitter_type_size,\n\t    &metadata_thp);\n\temitter_json_kv(emitter, \"resident\", emitter_type_size, &resident);\n\temitter_json_kv(emitter, \"mapped\", emitter_type_size, &mapped);\n\temitter_json_kv(emitter, \"retained\", emitter_type_size, &retained);\n\n\temitter_table_printf(emitter, \"Allocated: %zu, active: %zu, \"\n\t    \"metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, \"\n\t    \"retained: %zu\\n\", allocated, active, metadata, metadata_thp,\n\t    resident, mapped, retained);\n\n\t/* Background thread stats. */\n\temitter_json_dict_begin(emitter, \"background_thread\");\n\temitter_json_kv(emitter, \"num_threads\", emitter_type_size,\n\t    &num_background_threads);\n\temitter_json_kv(emitter, \"num_runs\", emitter_type_uint64,\n\t    &background_thread_num_runs);\n\temitter_json_kv(emitter, \"run_interval\", emitter_type_uint64,\n\t    &background_thread_run_interval);\n\temitter_json_dict_end(emitter); /* Close \"background_thread\". */\n\n\temitter_table_printf(emitter, \"Background threads: %zu, \"\n\t    \"num_runs: %\"FMTu64\", run_interval: %\"FMTu64\" ns\\n\",\n\t    num_background_threads, background_thread_num_runs,\n\t    background_thread_run_interval);\n\n\tif (mutex) {\n\t\temitter_row_t row;\n\t\temitter_col_t name;\n\t\temitter_col_t col64[mutex_prof_num_uint64_t_counters];\n\t\temitter_col_t col32[mutex_prof_num_uint32_t_counters];\n\n\t\temitter_row_init(&row);\n\t\tmutex_stats_init_cols(&row, \"\", &name, col64, col32);\n\n\t\temitter_table_row(emitter, &row);\n\t\temitter_json_dict_begin(emitter, \"mutexes\");\n\n\t\tfor (int i = 0; i < mutex_prof_num_global_mutexes; i++) {\n\t\t\tmutex_stats_read_global(global_mutex_names[i], &name,\n\t\t\t    col64, col32);\n\t\t\temitter_json_dict_begin(emitter, global_mutex_names[i]);\n\t\t\tmutex_stats_emit(emitter, &row, col64, col32);\n\t\t\temitter_json_dict_end(emitter);\n\t\t}\n\n\t\temitter_json_dict_end(emitter); /* Close \"mutexes\". */\n\t}\n\n\temitter_json_dict_end(emitter); /* Close \"stats\". */\n\n\tif (merged || destroyed || unmerged) {\n\t\tunsigned narenas;\n\n\t\temitter_json_dict_begin(emitter, \"stats.arenas\");\n\n\t\tCTL_GET(\"arenas.narenas\", &narenas, unsigned);\n\t\tsize_t mib[3];\n\t\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\t\tsize_t sz;\n\t\tVARIABLE_ARRAY(bool, initialized, narenas);\n\t\tbool destroyed_initialized;\n\t\tunsigned i, j, ninitialized;\n\n\t\txmallctlnametomib(\"arena.0.initialized\", mib, &miblen);\n\t\tfor (i = ninitialized = 0; i < narenas; i++) {\n\t\t\tmib[1] = i;\n\t\t\tsz = sizeof(bool);\n\t\t\txmallctlbymib(mib, miblen, &initialized[i], &sz,\n\t\t\t    NULL, 0);\n\t\t\tif (initialized[i]) {\n\t\t\t\tninitialized++;\n\t\t\t}\n\t\t}\n\t\tmib[1] = MALLCTL_ARENAS_DESTROYED;\n\t\tsz = sizeof(bool);\n\t\txmallctlbymib(mib, miblen, &destroyed_initialized, &sz,\n\t\t    NULL, 0);\n\n\t\t/* Merged stats. */\n\t\tif (merged && (ninitialized > 1 || !unmerged)) {\n\t\t\t/* Print merged arena stats. */\n\t\t\temitter_table_printf(emitter, \"Merged arenas stats:\\n\");\n\t\t\temitter_json_dict_begin(emitter, \"merged\");\n\t\t\tstats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins,\n\t\t\t    large, mutex);\n\t\t\temitter_json_dict_end(emitter); /* Close \"merged\". */\n\t\t}\n\n\t\t/* Destroyed stats. */\n\t\tif (destroyed_initialized && destroyed) {\n\t\t\t/* Print destroyed arena stats. */\n\t\t\temitter_table_printf(emitter,\n\t\t\t    \"Destroyed arenas stats:\\n\");\n\t\t\temitter_json_dict_begin(emitter, \"destroyed\");\n\t\t\tstats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED,\n\t\t\t    bins, large, mutex);\n\t\t\temitter_json_dict_end(emitter); /* Close \"destroyed\". */\n\t\t}\n\n\t\t/* Unmerged stats. */\n\t\tif (unmerged) {\n\t\t\tfor (i = j = 0; i < narenas; i++) {\n\t\t\t\tif (initialized[i]) {\n\t\t\t\t\tchar arena_ind_str[20];\n\t\t\t\t\tmalloc_snprintf(arena_ind_str,\n\t\t\t\t\t    sizeof(arena_ind_str), \"%u\", i);\n\t\t\t\t\temitter_json_dict_begin(emitter,\n\t\t\t\t\t    arena_ind_str);\n\t\t\t\t\temitter_table_printf(emitter,\n\t\t\t\t\t    \"arenas[%s]:\\n\", arena_ind_str);\n\t\t\t\t\tstats_arena_print(emitter, i, bins,\n\t\t\t\t\t    large, mutex);\n\t\t\t\t\t/* Close \"<arena-ind>\". */\n\t\t\t\t\temitter_json_dict_end(emitter);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\temitter_json_dict_end(emitter); /* Close \"stats.arenas\". */\n\t}\n}\n\nvoid\nstats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts) {\n\tint err;\n\tuint64_t epoch;\n\tsize_t u64sz;\n#define OPTION(o, v, d, s) bool v = d;\n\tSTATS_PRINT_OPTIONS\n#undef OPTION\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\", (void *)&epoch, &u64sz, (void *)&epoch,\n\t    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\tfor (unsigned i = 0; opts[i] != '\\0'; i++) {\n\t\t\tswitch (opts[i]) {\n#define OPTION(o, v, d, s) case o: v = s; break;\n\t\t\t\tSTATS_PRINT_OPTIONS\n#undef OPTION\n\t\t\tdefault:;\n\t\t\t}\n\t\t}\n\t}\n\n\temitter_t emitter;\n\temitter_init(&emitter,\n\t    json ? emitter_output_json : emitter_output_table, write_cb,\n\t    cbopaque);\n\temitter_begin(&emitter);\n\temitter_table_printf(&emitter, \"___ Begin jemalloc statistics ___\\n\");\n\temitter_json_dict_begin(&emitter, \"jemalloc\");\n\n\tif (general) {\n\t\tstats_general_print(&emitter);\n\t}\n\tif (config_stats) {\n\t\tstats_print_helper(&emitter, merged, destroyed, unmerged,\n\t\t    bins, large, mutex);\n\t}\n\n\temitter_json_dict_end(&emitter); /* Closes the \"jemalloc\" dict. */\n\temitter_table_printf(&emitter, \"--- End jemalloc statistics ---\\n\");\n\temitter_end(&emitter);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/sz.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/sz.h\"\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst size_t sz_pind2sz_tab[NPSIZES+1] = {\n#define PSZ_yes(lg_grp, ndelta, lg_delta)\t\t\t\t\\\n\t(((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))),\n#define PSZ_no(lg_grp, ndelta, lg_delta)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \\\n\tPSZ_##psz(lg_grp, ndelta, lg_delta)\n\tSIZE_CLASSES\n#undef PSZ_yes\n#undef PSZ_no\n#undef SC\n\t(LARGE_MAXCLASS + PAGE)\n};\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst size_t sz_index2size_tab[NSIZES] = {\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \\\n\t((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta)),\n\tSIZE_CLASSES\n#undef SC\n};\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst uint8_t sz_size2index_tab[] = {\n#if LG_TINY_MIN == 0\n/* The div module doesn't support division by 1. */\n#error \"Unsupported LG_TINY_MIN\"\n#define S2B_0(i)\ti,\n#elif LG_TINY_MIN == 1\n#warning \"Dangerous LG_TINY_MIN\"\n#define S2B_1(i)\ti,\n#elif LG_TINY_MIN == 2\n#warning \"Dangerous LG_TINY_MIN\"\n#define S2B_2(i)\ti,\n#elif LG_TINY_MIN == 3\n#define S2B_3(i)\ti,\n#elif LG_TINY_MIN == 4\n#define S2B_4(i)\ti,\n#elif LG_TINY_MIN == 5\n#define S2B_5(i)\ti,\n#elif LG_TINY_MIN == 6\n#define S2B_6(i)\ti,\n#elif LG_TINY_MIN == 7\n#define S2B_7(i)\ti,\n#elif LG_TINY_MIN == 8\n#define S2B_8(i)\ti,\n#elif LG_TINY_MIN == 9\n#define S2B_9(i)\ti,\n#elif LG_TINY_MIN == 10\n#define S2B_10(i)\ti,\n#elif LG_TINY_MIN == 11\n#define S2B_11(i)\ti,\n#else\n#error \"Unsupported LG_TINY_MIN\"\n#endif\n#if LG_TINY_MIN < 1\n#define S2B_1(i)\tS2B_0(i) S2B_0(i)\n#endif\n#if LG_TINY_MIN < 2\n#define S2B_2(i)\tS2B_1(i) S2B_1(i)\n#endif\n#if LG_TINY_MIN < 3\n#define S2B_3(i)\tS2B_2(i) S2B_2(i)\n#endif\n#if LG_TINY_MIN < 4\n#define S2B_4(i)\tS2B_3(i) S2B_3(i)\n#endif\n#if LG_TINY_MIN < 5\n#define S2B_5(i)\tS2B_4(i) S2B_4(i)\n#endif\n#if LG_TINY_MIN < 6\n#define S2B_6(i)\tS2B_5(i) S2B_5(i)\n#endif\n#if LG_TINY_MIN < 7\n#define S2B_7(i)\tS2B_6(i) S2B_6(i)\n#endif\n#if LG_TINY_MIN < 8\n#define S2B_8(i)\tS2B_7(i) S2B_7(i)\n#endif\n#if LG_TINY_MIN < 9\n#define S2B_9(i)\tS2B_8(i) S2B_8(i)\n#endif\n#if LG_TINY_MIN < 10\n#define S2B_10(i)\tS2B_9(i) S2B_9(i)\n#endif\n#if LG_TINY_MIN < 11\n#define S2B_11(i)\tS2B_10(i) S2B_10(i)\n#endif\n#define S2B_no(i)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \\\n\tS2B_##lg_delta_lookup(index)\n\tSIZE_CLASSES\n#undef S2B_3\n#undef S2B_4\n#undef S2B_5\n#undef S2B_6\n#undef S2B_7\n#undef S2B_8\n#undef S2B_9\n#undef S2B_10\n#undef S2B_11\n#undef S2B_no\n#undef SC\n};\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/tcache.c",
    "content": "#define JEMALLOC_TCACHE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\n/******************************************************************************/\n/* Data. */\n\nbool\topt_tcache = true;\nssize_t\topt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT;\n\ncache_bin_info_t\t*tcache_bin_info;\nstatic unsigned\t\tstack_nelms; /* Total stack elms per tcache. */\n\nunsigned\t\tnhbins;\nsize_t\t\t\ttcache_maxclass;\n\ntcaches_t\t\t*tcaches;\n\n/* Index of first element within tcaches that has never been used. */\nstatic unsigned\t\ttcaches_past;\n\n/* Head of singly linked list tracking available tcaches elements. */\nstatic tcaches_t\t*tcaches_avail;\n\n/* Protects tcaches{,_past,_avail}. */\nstatic malloc_mutex_t\ttcaches_mtx;\n\n/******************************************************************************/\n\nsize_t\ntcache_salloc(tsdn_t *tsdn, const void *ptr) {\n\treturn arena_salloc(tsdn, ptr);\n}\n\nvoid\ntcache_event_hard(tsd_t *tsd, tcache_t *tcache) {\n\tszind_t binind = tcache->next_gc_bin;\n\n\tcache_bin_t *tbin;\n\tif (binind < NBINS) {\n\t\ttbin = tcache_small_bin_get(tcache, binind);\n\t} else {\n\t\ttbin = tcache_large_bin_get(tcache, binind);\n\t}\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(tsd, tcache, tbin, binind,\n\t\t\t    tbin->ncached - tbin->low_water + (tbin->low_water\n\t\t\t    >> 2));\n\t\t\t/*\n\t\t\t * Reduce fill count by 2X.  Limit lg_fill_div such that\n\t\t\t * the fill count is always at least 1.\n\t\t\t */\n\t\t\tcache_bin_info_t *tbin_info = &tcache_bin_info[binind];\n\t\t\tif ((tbin_info->ncached_max >>\n\t\t\t     (tcache->lg_fill_div[binind] + 1)) >= 1) {\n\t\t\t\ttcache->lg_fill_div[binind]++;\n\t\t\t}\n\t\t} else {\n\t\t\ttcache_bin_flush_large(tsd, tbin, binind, tbin->ncached\n\t\t\t    - tbin->low_water + (tbin->low_water >> 2), tcache);\n\t\t}\n\t} else if (tbin->low_water < 0) {\n\t\t/*\n\t\t * Increase fill count by 2X for small bins.  Make sure\n\t\t * lg_fill_div stays greater than 0.\n\t\t */\n\t\tif (binind < NBINS && tcache->lg_fill_div[binind] > 1) {\n\t\t\ttcache->lg_fill_div[binind]--;\n\t\t}\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\t}\n}\n\nvoid *\ntcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, bool *tcache_success) {\n\tvoid *ret;\n\n\tassert(tcache->arena != NULL);\n\tarena_tcache_fill_small(tsdn, arena, tcache, tbin, binind,\n\t    config_prof ? tcache->prof_accumbytes : 0);\n\tif (config_prof) {\n\t\ttcache->prof_accumbytes = 0;\n\t}\n\tret = cache_bin_alloc_easy(tbin, tcache_success);\n\n\treturn ret;\n}\n\nvoid\ntcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,\n    szind_t binind, unsigned rem) {\n\tbool merged_stats = false;\n\n\tassert(binind < NBINS);\n\tassert((cache_bin_sz_t)rem <= tbin->ncached);\n\n\tarena_t *arena = tcache->arena;\n\tassert(arena != NULL);\n\tunsigned nflush = tbin->ncached - rem;\n\tVARIABLE_ARRAY(extent_t *, item_extent, nflush);\n\t/* Look up extent once per item. */\n\tfor (unsigned i = 0 ; i < nflush; i++) {\n\t\titem_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));\n\t}\n\n\twhile (nflush > 0) {\n\t\t/* Lock the arena bin associated with the first object. */\n\t\textent_t *extent = item_extent[0];\n\t\tarena_t *bin_arena = extent_arena_get(extent);\n\t\tbin_t *bin = &bin_arena->bins[binind];\n\n\t\tif (config_prof && bin_arena == arena) {\n\t\t\tif (arena_prof_accum(tsd_tsdn(tsd), arena,\n\t\t\t    tcache->prof_accumbytes)) {\n\t\t\t\tprof_idump(tsd_tsdn(tsd));\n\t\t\t}\n\t\t\ttcache->prof_accumbytes = 0;\n\t\t}\n\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\tif (config_stats && bin_arena == arena) {\n\t\t\tassert(!merged_stats);\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\tunsigned ndeferred = 0;\n\t\tfor (unsigned i = 0; i < nflush; i++) {\n\t\t\tvoid *ptr = *(tbin->avail - 1 - i);\n\t\t\textent = item_extent[i];\n\t\t\tassert(ptr != NULL && extent != NULL);\n\n\t\t\tif (extent_arena_get(extent) == bin_arena) {\n\t\t\t\tarena_dalloc_bin_junked_locked(tsd_tsdn(tsd),\n\t\t\t\t    bin_arena, extent, ptr);\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\t*(tbin->avail - 1 - ndeferred) = ptr;\n\t\t\t\titem_extent[ndeferred] = extent;\n\t\t\t\tndeferred++;\n\t\t\t}\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\tarena_decay_ticks(tsd_tsdn(tsd), bin_arena, nflush - ndeferred);\n\t\tnflush = ndeferred;\n\t}\n\tif (config_stats && !merged_stats) {\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\tbin_t *bin = &arena->bins[binind];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &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(tsd_tsdn(tsd), &bin->lock);\n\t}\n\n\tmemmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *\n\t    sizeof(void *));\n\ttbin->ncached = rem;\n\tif (tbin->ncached < tbin->low_water) {\n\t\ttbin->low_water = tbin->ncached;\n\t}\n}\n\nvoid\ntcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,\n    unsigned rem, tcache_t *tcache) {\n\tbool merged_stats = false;\n\n\tassert(binind < nhbins);\n\tassert((cache_bin_sz_t)rem <= tbin->ncached);\n\n\tarena_t *arena = tcache->arena;\n\tassert(arena != NULL);\n\tunsigned nflush = tbin->ncached - rem;\n\tVARIABLE_ARRAY(extent_t *, item_extent, nflush);\n\t/* Look up extent once per item. */\n\tfor (unsigned i = 0 ; i < nflush; i++) {\n\t\titem_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));\n\t}\n\n\twhile (nflush > 0) {\n\t\t/* Lock the arena associated with the first object. */\n\t\textent_t *extent = item_extent[0];\n\t\tarena_t *locked_arena = extent_arena_get(extent);\n\t\tUNUSED bool idump;\n\n\t\tif (config_prof) {\n\t\t\tidump = false;\n\t\t}\n\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx);\n\t\tfor (unsigned i = 0; i < nflush; i++) {\n\t\t\tvoid *ptr = *(tbin->avail - 1 - i);\n\t\t\tassert(ptr != NULL);\n\t\t\textent = item_extent[i];\n\t\t\tif (extent_arena_get(extent) == locked_arena) {\n\t\t\t\tlarge_dalloc_prep_junked_locked(tsd_tsdn(tsd),\n\t\t\t\t    extent);\n\t\t\t}\n\t\t}\n\t\tif ((config_prof || config_stats) && locked_arena == arena) {\n\t\t\tif (config_prof) {\n\t\t\t\tidump = arena_prof_accum(tsd_tsdn(tsd), 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_large_nrequests_add(tsd_tsdn(tsd),\n\t\t\t\t    &arena->stats, binind,\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\tmalloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx);\n\n\t\tunsigned ndeferred = 0;\n\t\tfor (unsigned i = 0; i < nflush; i++) {\n\t\t\tvoid *ptr = *(tbin->avail - 1 - i);\n\t\t\textent = item_extent[i];\n\t\t\tassert(ptr != NULL && extent != NULL);\n\n\t\t\tif (extent_arena_get(extent) == locked_arena) {\n\t\t\t\tlarge_dalloc_finish(tsd_tsdn(tsd), extent);\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 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\t*(tbin->avail - 1 - ndeferred) = ptr;\n\t\t\t\titem_extent[ndeferred] = extent;\n\t\t\t\tndeferred++;\n\t\t\t}\n\t\t}\n\t\tif (config_prof && idump) {\n\t\t\tprof_idump(tsd_tsdn(tsd));\n\t\t}\n\t\tarena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush -\n\t\t    ndeferred);\n\t\tnflush = ndeferred;\n\t}\n\tif (config_stats && !merged_stats) {\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_stats_large_nrequests_add(tsd_tsdn(tsd), &arena->stats,\n\t\t    binind, tbin->tstats.nrequests);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\n\tmemmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *\n\t    sizeof(void *));\n\ttbin->ncached = rem;\n\tif (tbin->ncached < tbin->low_water) {\n\t\ttbin->low_water = tbin->ncached;\n\t}\n}\n\nvoid\ntcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {\n\tassert(tcache->arena == NULL);\n\ttcache->arena = arena;\n\n\tif (config_stats) {\n\t\t/* Link into list of extant tcaches. */\n\t\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\n\t\tql_elm_new(tcache, link);\n\t\tql_tail_insert(&arena->tcache_ql, tcache, link);\n\t\tcache_bin_array_descriptor_init(\n\t\t    &tcache->cache_bin_array_descriptor, tcache->bins_small,\n\t\t    tcache->bins_large);\n\t\tql_tail_insert(&arena->cache_bin_array_descriptor_ql,\n\t\t    &tcache->cache_bin_array_descriptor, link);\n\n\t\tmalloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n\nstatic void\ntcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache) {\n\tarena_t *arena = tcache->arena;\n\tassert(arena != NULL);\n\tif (config_stats) {\n\t\t/* Unlink from list of extant tcaches. */\n\t\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\t\tif (config_debug) {\n\t\t\tbool in_ql = false;\n\t\t\ttcache_t *iter;\n\t\t\tql_foreach(iter, &arena->tcache_ql, link) {\n\t\t\t\tif (iter == tcache) {\n\t\t\t\t\tin_ql = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert(in_ql);\n\t\t}\n\t\tql_remove(&arena->tcache_ql, tcache, link);\n\t\tql_remove(&arena->cache_bin_array_descriptor_ql,\n\t\t    &tcache->cache_bin_array_descriptor, link);\n\t\ttcache_stats_merge(tsdn, tcache, arena);\n\t\tmalloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);\n\t}\n\ttcache->arena = NULL;\n}\n\nvoid\ntcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {\n\ttcache_arena_dissociate(tsdn, tcache);\n\ttcache_arena_associate(tsdn, tcache, arena);\n}\n\nbool\ntsd_tcache_enabled_data_init(tsd_t *tsd) {\n\t/* Called upon tsd initialization. */\n\ttsd_tcache_enabled_set(tsd, opt_tcache);\n\ttsd_slow_update(tsd);\n\n\tif (opt_tcache) {\n\t\t/* Trigger tcache init. */\n\t\ttsd_tcache_data_init(tsd);\n\t}\n\n\treturn false;\n}\n\n/* Initialize auto tcache (embedded in TSD). */\nstatic void\ntcache_init(tsd_t *tsd, tcache_t *tcache, void *avail_stack) {\n\tmemset(&tcache->link, 0, sizeof(ql_elm(tcache_t)));\n\ttcache->prof_accumbytes = 0;\n\ttcache->next_gc_bin = 0;\n\ttcache->arena = NULL;\n\n\tticker_init(&tcache->gc_ticker, TCACHE_GC_INCR);\n\n\tsize_t stack_offset = 0;\n\tassert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0);\n\tmemset(tcache->bins_small, 0, sizeof(cache_bin_t) * NBINS);\n\tmemset(tcache->bins_large, 0, sizeof(cache_bin_t) * (nhbins - NBINS));\n\tunsigned i = 0;\n\tfor (; i < NBINS; i++) {\n\t\ttcache->lg_fill_div[i] = 1;\n\t\tstack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);\n\t\t/*\n\t\t * avail points past the available space.  Allocations will\n\t\t * access the slots toward higher addresses (for the benefit of\n\t\t * prefetch).\n\t\t */\n\t\ttcache_small_bin_get(tcache, i)->avail =\n\t\t    (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset);\n\t}\n\tfor (; i < nhbins; i++) {\n\t\tstack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);\n\t\ttcache_large_bin_get(tcache, i)->avail =\n\t\t    (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset);\n\t}\n\tassert(stack_offset == stack_nelms * sizeof(void *));\n}\n\n/* Initialize auto tcache (embedded in TSD). */\nbool\ntsd_tcache_data_init(tsd_t *tsd) {\n\ttcache_t *tcache = tsd_tcachep_get_unsafe(tsd);\n\tassert(tcache_small_bin_get(tcache, 0)->avail == NULL);\n\tsize_t size = stack_nelms * sizeof(void *);\n\t/* Avoid false cacheline sharing. */\n\tsize = sz_sa2u(size, CACHELINE);\n\n\tvoid *avail_array = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true,\n\t    NULL, true, arena_get(TSDN_NULL, 0, true));\n\tif (avail_array == NULL) {\n\t\treturn true;\n\t}\n\n\ttcache_init(tsd, tcache, avail_array);\n\t/*\n\t * Initialization is a bit tricky here.  After malloc init is done, all\n\t * threads can rely on arena_choose and associate tcache accordingly.\n\t * However, the thread that does actual malloc bootstrapping relies on\n\t * functional tsd, and it can only rely on a0.  In that case, we\n\t * associate its tcache to a0 temporarily, and later on\n\t * arena_choose_hard() will re-associate properly.\n\t */\n\ttcache->arena = NULL;\n\tarena_t *arena;\n\tif (!malloc_initialized()) {\n\t\t/* If in initialization, assign to a0. */\n\t\tarena = arena_get(tsd_tsdn(tsd), 0, false);\n\t\ttcache_arena_associate(tsd_tsdn(tsd), tcache, arena);\n\t} else {\n\t\tarena = arena_choose(tsd, NULL);\n\t\t/* This may happen if thread.tcache.enabled is used. */\n\t\tif (tcache->arena == NULL) {\n\t\t\ttcache_arena_associate(tsd_tsdn(tsd), tcache, arena);\n\t\t}\n\t}\n\tassert(arena == tcache->arena);\n\n\treturn false;\n}\n\n/* Created manual tcache for tcache.create mallctl. */\ntcache_t *\ntcache_create_explicit(tsd_t *tsd) {\n\ttcache_t *tcache;\n\tsize_t size, stack_offset;\n\n\tsize = sizeof(tcache_t);\n\t/* Naturally align the pointer stacks. */\n\tsize = PTR_CEILING(size);\n\tstack_offset = size;\n\tsize += stack_nelms * sizeof(void *);\n\t/* Avoid false cacheline sharing. */\n\tsize = sz_sa2u(size, CACHELINE);\n\n\ttcache = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, NULL, true,\n\t    arena_get(TSDN_NULL, 0, true));\n\tif (tcache == NULL) {\n\t\treturn NULL;\n\t}\n\n\ttcache_init(tsd, tcache,\n\t    (void *)((uintptr_t)tcache + (uintptr_t)stack_offset));\n\ttcache_arena_associate(tsd_tsdn(tsd), tcache, arena_ichoose(tsd, NULL));\n\n\treturn tcache;\n}\n\nstatic void\ntcache_flush_cache(tsd_t *tsd, tcache_t *tcache) {\n\tassert(tcache->arena != NULL);\n\n\tfor (unsigned i = 0; i < NBINS; i++) {\n\t\tcache_bin_t *tbin = tcache_small_bin_get(tcache, i);\n\t\ttcache_bin_flush_small(tsd, tcache, tbin, i, 0);\n\n\t\tif (config_stats) {\n\t\t\tassert(tbin->tstats.nrequests == 0);\n\t\t}\n\t}\n\tfor (unsigned i = NBINS; i < nhbins; i++) {\n\t\tcache_bin_t *tbin = tcache_large_bin_get(tcache, i);\n\t\ttcache_bin_flush_large(tsd, tbin, i, 0, tcache);\n\n\t\tif (config_stats) {\n\t\t\tassert(tbin->tstats.nrequests == 0);\n\t\t}\n\t}\n\n\tif (config_prof && tcache->prof_accumbytes > 0 &&\n\t    arena_prof_accum(tsd_tsdn(tsd), tcache->arena,\n\t    tcache->prof_accumbytes)) {\n\t\tprof_idump(tsd_tsdn(tsd));\n\t}\n}\n\nvoid\ntcache_flush(tsd_t *tsd) {\n\tassert(tcache_available(tsd));\n\ttcache_flush_cache(tsd, tsd_tcachep_get(tsd));\n}\n\nstatic void\ntcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) {\n\ttcache_flush_cache(tsd, tcache);\n\ttcache_arena_dissociate(tsd_tsdn(tsd), tcache);\n\n\tif (tsd_tcache) {\n\t\t/* Release the avail array for the TSD embedded auto tcache. */\n\t\tvoid *avail_array =\n\t\t    (void *)((uintptr_t)tcache_small_bin_get(tcache, 0)->avail -\n\t\t    (uintptr_t)tcache_bin_info[0].ncached_max * sizeof(void *));\n\t\tidalloctm(tsd_tsdn(tsd), avail_array, NULL, NULL, true, true);\n\t} else {\n\t\t/* Release both the tcache struct and avail array. */\n\t\tidalloctm(tsd_tsdn(tsd), tcache, NULL, NULL, true, true);\n\t}\n}\n\n/* For auto tcache (embedded in TSD) only. */\nvoid\ntcache_cleanup(tsd_t *tsd) {\n\ttcache_t *tcache = tsd_tcachep_get(tsd);\n\tif (!tcache_available(tsd)) {\n\t\tassert(tsd_tcache_enabled_get(tsd) == false);\n\t\tif (config_debug) {\n\t\t\tassert(tcache_small_bin_get(tcache, 0)->avail == NULL);\n\t\t}\n\t\treturn;\n\t}\n\tassert(tsd_tcache_enabled_get(tsd));\n\tassert(tcache_small_bin_get(tcache, 0)->avail != NULL);\n\n\ttcache_destroy(tsd, tcache, true);\n\tif (config_debug) {\n\t\ttcache_small_bin_get(tcache, 0)->avail = NULL;\n\t}\n}\n\nvoid\ntcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {\n\tunsigned i;\n\n\tcassert(config_stats);\n\n\t/* Merge and reset tcache stats. */\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin_t *bin = &arena->bins[i];\n\t\tcache_bin_t *tbin = tcache_small_bin_get(tcache, i);\n\t\tmalloc_mutex_lock(tsdn, &bin->lock);\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\n\tfor (; i < nhbins; i++) {\n\t\tcache_bin_t *tbin = tcache_large_bin_get(tcache, i);\n\t\tarena_stats_large_nrequests_add(tsdn, &arena->stats, i,\n\t\t    tbin->tstats.nrequests);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n}\n\nstatic bool\ntcaches_create_prep(tsd_t *tsd) {\n\tbool err;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\n\tif (tcaches == NULL) {\n\t\ttcaches = base_alloc(tsd_tsdn(tsd), b0get(), sizeof(tcache_t *)\n\t\t    * (MALLOCX_TCACHE_MAX+1), CACHELINE);\n\t\tif (tcaches == NULL) {\n\t\t\terr = true;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tif (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) {\n\t\terr = true;\n\t\tgoto label_return;\n\t}\n\n\terr = false;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\treturn err;\n}\n\nbool\ntcaches_create(tsd_t *tsd, unsigned *r_ind) {\n\twitness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);\n\n\tbool err;\n\n\tif (tcaches_create_prep(tsd)) {\n\t\terr = true;\n\t\tgoto label_return;\n\t}\n\n\ttcache_t *tcache = tcache_create_explicit(tsd);\n\tif (tcache == NULL) {\n\t\terr = true;\n\t\tgoto label_return;\n\t}\n\n\ttcaches_t *elm;\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\tif (tcaches_avail != NULL) {\n\t\telm = tcaches_avail;\n\t\ttcaches_avail = tcaches_avail->next;\n\t\telm->tcache = tcache;\n\t\t*r_ind = (unsigned)(elm - tcaches);\n\t} else {\n\t\telm = &tcaches[tcaches_past];\n\t\telm->tcache = tcache;\n\t\t*r_ind = tcaches_past;\n\t\ttcaches_past++;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\n\terr = false;\nlabel_return:\n\twitness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);\n\treturn err;\n}\n\nstatic tcache_t *\ntcaches_elm_remove(tsd_t *tsd, tcaches_t *elm) {\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx);\n\n\tif (elm->tcache == NULL) {\n\t\treturn NULL;\n\t}\n\ttcache_t *tcache = elm->tcache;\n\telm->tcache = NULL;\n\treturn tcache;\n}\n\nvoid\ntcaches_flush(tsd_t *tsd, unsigned ind) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\ttcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind]);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\tif (tcache != NULL) {\n\t\ttcache_destroy(tsd, tcache, false);\n\t}\n}\n\nvoid\ntcaches_destroy(tsd_t *tsd, unsigned ind) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\ttcaches_t *elm = &tcaches[ind];\n\ttcache_t *tcache = tcaches_elm_remove(tsd, elm);\n\telm->next = tcaches_avail;\n\ttcaches_avail = elm;\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\tif (tcache != NULL) {\n\t\ttcache_destroy(tsd, tcache, false);\n\t}\n}\n\nbool\ntcache_boot(tsdn_t *tsdn) {\n\t/* If necessary, clamp opt_lg_tcache_max. */\n\tif (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) <\n\t    SMALL_MAXCLASS) {\n\t\ttcache_maxclass = SMALL_MAXCLASS;\n\t} else {\n\t\ttcache_maxclass = (ZU(1) << opt_lg_tcache_max);\n\t}\n\n\tif (malloc_mutex_init(&tcaches_mtx, \"tcaches\", WITNESS_RANK_TCACHES,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\tnhbins = sz_size2index(tcache_maxclass) + 1;\n\n\t/* Initialize tcache_bin_info. */\n\ttcache_bin_info = (cache_bin_info_t *)base_alloc(tsdn, b0get(), nhbins\n\t    * sizeof(cache_bin_info_t), CACHELINE);\n\tif (tcache_bin_info == NULL) {\n\t\treturn true;\n\t}\n\tstack_nelms = 0;\n\tunsigned i;\n\tfor (i = 0; i < NBINS; i++) {\n\t\tif ((bin_infos[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) {\n\t\t\ttcache_bin_info[i].ncached_max =\n\t\t\t    TCACHE_NSLOTS_SMALL_MIN;\n\t\t} else if ((bin_infos[i].nregs << 1) <=\n\t\t    TCACHE_NSLOTS_SMALL_MAX) {\n\t\t\ttcache_bin_info[i].ncached_max =\n\t\t\t    (bin_infos[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\nvoid\ntcache_prefork(tsdn_t *tsdn) {\n\tif (!config_prof && opt_tcache) {\n\t\tmalloc_mutex_prefork(tsdn, &tcaches_mtx);\n\t}\n}\n\nvoid\ntcache_postfork_parent(tsdn_t *tsdn) {\n\tif (!config_prof && opt_tcache) {\n\t\tmalloc_mutex_postfork_parent(tsdn, &tcaches_mtx);\n\t}\n}\n\nvoid\ntcache_postfork_child(tsdn_t *tsdn) {\n\tif (!config_prof && opt_tcache) {\n\t\tmalloc_mutex_postfork_child(tsdn, &tcaches_mtx);\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/ticker.c",
    "content": "#define JEMALLOC_TICKER_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/tsd.c",
    "content": "#define JEMALLOC_TSD_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n\n/******************************************************************************/\n/* Data. */\n\nstatic unsigned ncleanups;\nstatic malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];\n\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n__thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;\n__thread bool JEMALLOC_TLS_MODEL tsd_initialized = false;\nbool tsd_booted = false;\n#elif (defined(JEMALLOC_TLS))\n__thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;\npthread_key_t tsd_tsd;\nbool tsd_booted = false;\n#elif (defined(_WIN32))\nDWORD tsd_tsd;\ntsd_wrapper_t tsd_boot_wrapper = {false, TSD_INITIALIZER};\nbool tsd_booted = false;\n#else\n\n/*\n * This contains a mutex, but it's pretty convenient to allow the mutex code to\n * have a dependency on tsd.  So we define the struct here, and only refer to it\n * by pointer in the header.\n */\nstruct tsd_init_head_s {\n\tql_head(tsd_init_block_t) blocks;\n\tmalloc_mutex_t lock;\n};\n\npthread_key_t tsd_tsd;\ntsd_init_head_t\ttsd_init_head = {\n\tql_head_initializer(blocks),\n\tMALLOC_MUTEX_INITIALIZER\n};\ntsd_wrapper_t tsd_boot_wrapper = {\n\tfalse,\n\tTSD_INITIALIZER\n};\nbool tsd_booted = false;\n#endif\n\n\n/******************************************************************************/\n\nvoid\ntsd_slow_update(tsd_t *tsd) {\n\tif (tsd_nominal(tsd)) {\n\t\tif (malloc_slow || !tsd_tcache_enabled_get(tsd) ||\n\t\t    tsd_reentrancy_level_get(tsd) > 0) {\n\t\t\ttsd->state = tsd_state_nominal_slow;\n\t\t} else {\n\t\t\ttsd->state = tsd_state_nominal;\n\t\t}\n\t}\n}\n\nstatic bool\ntsd_data_init(tsd_t *tsd) {\n\t/*\n\t * We initialize the rtree context first (before the tcache), since the\n\t * tcache initialization depends on it.\n\t */\n\trtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));\n\n\t/*\n\t * A nondeterministic seed based on the address of tsd reduces\n\t * the likelihood of lockstep non-uniform cache index\n\t * utilization among identical concurrent processes, but at the\n\t * cost of test repeatability.  For debug builds, instead use a\n\t * deterministic seed.\n\t */\n\t*tsd_offset_statep_get(tsd) = config_debug ? 0 :\n\t    (uint64_t)(uintptr_t)tsd;\n\n\treturn tsd_tcache_enabled_data_init(tsd);\n}\n\nstatic void\nassert_tsd_data_cleanup_done(tsd_t *tsd) {\n\tassert(!tsd_nominal(tsd));\n\tassert(*tsd_arenap_get_unsafe(tsd) == NULL);\n\tassert(*tsd_iarenap_get_unsafe(tsd) == NULL);\n\tassert(*tsd_arenas_tdata_bypassp_get_unsafe(tsd) == true);\n\tassert(*tsd_arenas_tdatap_get_unsafe(tsd) == NULL);\n\tassert(*tsd_tcache_enabledp_get_unsafe(tsd) == false);\n\tassert(*tsd_prof_tdatap_get_unsafe(tsd) == NULL);\n}\n\nstatic bool\ntsd_data_init_nocleanup(tsd_t *tsd) {\n\tassert(tsd->state == tsd_state_reincarnated ||\n\t    tsd->state == tsd_state_minimal_initialized);\n\t/*\n\t * During reincarnation, there is no guarantee that the cleanup function\n\t * will be called (deallocation may happen after all tsd destructors).\n\t * We set up tsd in a way that no cleanup is needed.\n\t */\n\trtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));\n\t*tsd_arenas_tdata_bypassp_get(tsd) = true;\n\t*tsd_tcache_enabledp_get_unsafe(tsd) = false;\n\t*tsd_reentrancy_levelp_get(tsd) = 1;\n\tassert_tsd_data_cleanup_done(tsd);\n\n\treturn false;\n}\n\ntsd_t *\ntsd_fetch_slow(tsd_t *tsd, bool minimal) {\n\tassert(!tsd_fast(tsd));\n\n\tif (tsd->state == tsd_state_nominal_slow) {\n\t\t/* On slow path but no work needed. */\n\t\tassert(malloc_slow || !tsd_tcache_enabled_get(tsd) ||\n\t\t    tsd_reentrancy_level_get(tsd) > 0 ||\n\t\t    *tsd_arenas_tdata_bypassp_get(tsd));\n\t} else if (tsd->state == tsd_state_uninitialized) {\n\t\tif (!minimal) {\n\t\t\ttsd->state = tsd_state_nominal;\n\t\t\ttsd_slow_update(tsd);\n\t\t\t/* Trigger cleanup handler registration. */\n\t\t\ttsd_set(tsd);\n\t\t\ttsd_data_init(tsd);\n\t\t} else {\n\t\t\ttsd->state = tsd_state_minimal_initialized;\n\t\t\ttsd_set(tsd);\n\t\t\ttsd_data_init_nocleanup(tsd);\n\t\t}\n\t} else if (tsd->state == tsd_state_minimal_initialized) {\n\t\tif (!minimal) {\n\t\t\t/* Switch to fully initialized. */\n\t\t\ttsd->state = tsd_state_nominal;\n\t\t\tassert(*tsd_reentrancy_levelp_get(tsd) >= 1);\n\t\t\t(*tsd_reentrancy_levelp_get(tsd))--;\n\t\t\ttsd_slow_update(tsd);\n\t\t\ttsd_data_init(tsd);\n\t\t} else {\n\t\t\tassert_tsd_data_cleanup_done(tsd);\n\t\t}\n\t} else if (tsd->state == tsd_state_purgatory) {\n\t\ttsd->state = tsd_state_reincarnated;\n\t\ttsd_set(tsd);\n\t\ttsd_data_init_nocleanup(tsd);\n\t} else {\n\t\tassert(tsd->state == tsd_state_reincarnated);\n\t}\n\n\treturn tsd;\n}\n\nvoid *\nmalloc_tsd_malloc(size_t size) {\n\treturn a0malloc(CACHELINE_CEILING(size));\n}\n\nvoid\nmalloc_tsd_dalloc(void *wrapper) {\n\ta0dalloc(wrapper);\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\tbool pending[MALLOC_TSD_CLEANUPS_MAX], again;\n\tunsigned i;\n\n\tfor (i = 0; i < ncleanups; i++) {\n\t\tpending[i] = true;\n\t}\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\t}\n\t\t\t}\n\t\t}\n\t} while (again);\n}\n#endif\n\nvoid\nmalloc_tsd_cleanup_register(bool (*f)(void)) {\n\tassert(ncleanups < MALLOC_TSD_CLEANUPS_MAX);\n\tcleanups[ncleanups] = f;\n\tncleanups++;\n}\n\nstatic void\ntsd_do_data_cleanup(tsd_t *tsd) {\n\tprof_tdata_cleanup(tsd);\n\tiarena_cleanup(tsd);\n\tarena_cleanup(tsd);\n\tarenas_tdata_cleanup(tsd);\n\ttcache_cleanup(tsd);\n\twitnesses_cleanup(tsd_witness_tsdp_get_unsafe(tsd));\n}\n\nvoid\ntsd_cleanup(void *arg) {\n\ttsd_t *tsd = (tsd_t *)arg;\n\n\tswitch (tsd->state) {\n\tcase tsd_state_uninitialized:\n\t\t/* Do nothing. */\n\t\tbreak;\n\tcase tsd_state_minimal_initialized:\n\t\t/* This implies the thread only did free() in its life time. */\n\t\t/* Fall through. */\n\tcase tsd_state_reincarnated:\n\t\t/*\n\t\t * Reincarnated means another destructor deallocated memory\n\t\t * after the destructor was called.  Cleanup isn't required but\n\t\t * is still called for testing and completeness.\n\t\t */\n\t\tassert_tsd_data_cleanup_done(tsd);\n\t\t/* Fall through. */\n\tcase tsd_state_nominal:\n\tcase tsd_state_nominal_slow:\n\t\ttsd_do_data_cleanup(tsd);\n\t\ttsd->state = tsd_state_purgatory;\n\t\ttsd_set(tsd);\n\t\tbreak;\n\tcase tsd_state_purgatory:\n\t\t/*\n\t\t * The previous time this destructor was called, we set the\n\t\t * state to tsd_state_purgatory so that other destructors\n\t\t * wouldn't cause re-creation of the tsd.  This time, do\n\t\t * nothing, and do not request another callback.\n\t\t */\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n#ifdef JEMALLOC_JET\n\ttest_callback_t test_callback = *tsd_test_callbackp_get_unsafe(tsd);\n\tint *data = tsd_test_datap_get_unsafe(tsd);\n\tif (test_callback != NULL) {\n\t\ttest_callback(data);\n\t}\n#endif\n}\n\ntsd_t *\nmalloc_tsd_boot0(void) {\n\ttsd_t *tsd;\n\n\tncleanups = 0;\n\tif (tsd_boot0()) {\n\t\treturn NULL;\n\t}\n\ttsd = tsd_fetch();\n\t*tsd_arenas_tdata_bypassp_get(tsd) = true;\n\treturn tsd;\n}\n\nvoid\nmalloc_tsd_boot1(void) {\n\ttsd_boot1();\n\ttsd_t *tsd = tsd_fetch();\n\t/* malloc_slow has been set properly.  Update tsd_slow. */\n\ttsd_slow_update(tsd);\n\t*tsd_arenas_tdata_bypassp_get(tsd) = false;\n}\n\n#ifdef _WIN32\nstatic BOOL WINAPI\n_tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {\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/*\n * We need to be able to say \"read\" here (in the \"pragma section\"), but have\n * hooked \"read\". We won't read for the rest of the file, so we can get away\n * with unhooking.\n */\n#ifdef read\n#  undef read\n#endif\n\n#ifdef _MSC_VER\n#  ifdef _M_IX86\n#    pragma comment(linker, \"/INCLUDE:__tls_used\")\n#    pragma comment(linker, \"/INCLUDE:_tls_callback\")\n#  else\n#    pragma comment(linker, \"/INCLUDE:_tls_used\")\n#    pragma comment(linker, \"/INCLUDE:tls_callback\")\n#  endif\n#  pragma section(\".CRT$XLY\",long,read)\n#endif\nJEMALLOC_SECTION(\".CRT$XLY\") JEMALLOC_ATTR(used)\nBOOL\t(WINAPI *const tls_callback)(HINSTANCE hinstDLL,\n    DWORD fdwReason, LPVOID lpvReserved) = _tls_callback;\n#endif\n\n#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \\\n    !defined(_WIN32))\nvoid *\ntsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block) {\n\tpthread_t self = pthread_self();\n\ttsd_init_block_t *iter;\n\n\t/* Check whether this thread has already inserted into the list. */\n\tmalloc_mutex_lock(TSDN_NULL, &head->lock);\n\tql_foreach(iter, &head->blocks, link) {\n\t\tif (iter->thread == self) {\n\t\t\tmalloc_mutex_unlock(TSDN_NULL, &head->lock);\n\t\t\treturn iter->data;\n\t\t}\n\t}\n\t/* Insert block into list. */\n\tql_elm_new(block, link);\n\tblock->thread = self;\n\tql_tail_insert(&head->blocks, block, link);\n\tmalloc_mutex_unlock(TSDN_NULL, &head->lock);\n\treturn NULL;\n}\n\nvoid\ntsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) {\n\tmalloc_mutex_lock(TSDN_NULL, &head->lock);\n\tql_remove(&head->blocks, block, link);\n\tmalloc_mutex_unlock(TSDN_NULL, &head->lock);\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/witness.c",
    "content": "#define JEMALLOC_WITNESS_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n\nvoid\nwitness_init(witness_t *witness, const char *name, witness_rank_t rank,\n    witness_comp_t *comp, void *opaque) {\n\twitness->name = name;\n\twitness->rank = rank;\n\twitness->comp = comp;\n\twitness->opaque = opaque;\n}\n\nstatic void\nwitness_lock_error_impl(const witness_list_t *witnesses,\n    const witness_t *witness) {\n\twitness_t *w;\n\n\tmalloc_printf(\"<jemalloc>: Lock rank order reversal:\");\n\tql_foreach(w, witnesses, link) {\n\t\tmalloc_printf(\" %s(%u)\", w->name, w->rank);\n\t}\n\tmalloc_printf(\" %s(%u)\\n\", witness->name, witness->rank);\n\tabort();\n}\nwitness_lock_error_t *JET_MUTABLE witness_lock_error = witness_lock_error_impl;\n\nstatic void\nwitness_owner_error_impl(const witness_t *witness) {\n\tmalloc_printf(\"<jemalloc>: Should own %s(%u)\\n\", witness->name,\n\t    witness->rank);\n\tabort();\n}\nwitness_owner_error_t *JET_MUTABLE witness_owner_error =\n    witness_owner_error_impl;\n\nstatic void\nwitness_not_owner_error_impl(const witness_t *witness) {\n\tmalloc_printf(\"<jemalloc>: Should not own %s(%u)\\n\", witness->name,\n\t    witness->rank);\n\tabort();\n}\nwitness_not_owner_error_t *JET_MUTABLE witness_not_owner_error =\n    witness_not_owner_error_impl;\n\nstatic void\nwitness_depth_error_impl(const witness_list_t *witnesses,\n    witness_rank_t rank_inclusive, unsigned depth) {\n\twitness_t *w;\n\n\tmalloc_printf(\"<jemalloc>: Should own %u lock%s of rank >= %u:\", depth,\n\t    (depth != 1) ?  \"s\" : \"\", rank_inclusive);\n\tql_foreach(w, witnesses, link) {\n\t\tmalloc_printf(\" %s(%u)\", w->name, w->rank);\n\t}\n\tmalloc_printf(\"\\n\");\n\tabort();\n}\nwitness_depth_error_t *JET_MUTABLE witness_depth_error =\n    witness_depth_error_impl;\n\nvoid\nwitnesses_cleanup(witness_tsd_t *witness_tsd) {\n\twitness_assert_lockless(witness_tsd_tsdn(witness_tsd));\n\n\t/* Do nothing. */\n}\n\nvoid\nwitness_prefork(witness_tsd_t *witness_tsd) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\twitness_tsd->forking = true;\n}\n\nvoid\nwitness_postfork_parent(witness_tsd_t *witness_tsd) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\twitness_tsd->forking = false;\n}\n\nvoid\nwitness_postfork_child(witness_tsd_t *witness_tsd) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n#ifndef JEMALLOC_MUTEX_INIT_CB\n\twitness_list_t *witnesses;\n\n\twitnesses = &witness_tsd->witnesses;\n\tql_new(witnesses);\n#endif\n\twitness_tsd->forking = false;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/src/zone.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n#ifndef JEMALLOC_ZONE\n#  error \"This source file is for zones on Darwin (OS X).\"\n#endif\n\n/* Definitions of the following structs in malloc/malloc.h might be too old\n * for the built binary to run on newer versions of OSX. So use the newest\n * possible version of those structs.\n */\ntypedef struct _malloc_zone_t {\n\tvoid *reserved1;\n\tvoid *reserved2;\n\tsize_t (*size)(struct _malloc_zone_t *, const void *);\n\tvoid *(*malloc)(struct _malloc_zone_t *, size_t);\n\tvoid *(*calloc)(struct _malloc_zone_t *, size_t, size_t);\n\tvoid *(*valloc)(struct _malloc_zone_t *, size_t);\n\tvoid (*free)(struct _malloc_zone_t *, void *);\n\tvoid *(*realloc)(struct _malloc_zone_t *, void *, size_t);\n\tvoid (*destroy)(struct _malloc_zone_t *);\n\tconst char *zone_name;\n\tunsigned (*batch_malloc)(struct _malloc_zone_t *, size_t, void **, unsigned);\n\tvoid (*batch_free)(struct _malloc_zone_t *, void **, unsigned);\n\tstruct malloc_introspection_t *introspect;\n\tunsigned version;\n\tvoid *(*memalign)(struct _malloc_zone_t *, size_t, size_t);\n\tvoid (*free_definite_size)(struct _malloc_zone_t *, void *, size_t);\n\tsize_t (*pressure_relief)(struct _malloc_zone_t *, size_t);\n} malloc_zone_t;\n\ntypedef struct {\n\tvm_address_t address;\n\tvm_size_t size;\n} vm_range_t;\n\ntypedef struct malloc_statistics_t {\n\tunsigned blocks_in_use;\n\tsize_t size_in_use;\n\tsize_t max_size_in_use;\n\tsize_t size_allocated;\n} malloc_statistics_t;\n\ntypedef kern_return_t memory_reader_t(task_t, vm_address_t, vm_size_t, void **);\n\ntypedef void vm_range_recorder_t(task_t, void *, unsigned type, vm_range_t *, unsigned);\n\ntypedef struct malloc_introspection_t {\n\tkern_return_t (*enumerator)(task_t, void *, unsigned, vm_address_t, memory_reader_t, vm_range_recorder_t);\n\tsize_t (*good_size)(malloc_zone_t *, size_t);\n\tboolean_t (*check)(malloc_zone_t *);\n\tvoid (*print)(malloc_zone_t *, boolean_t);\n\tvoid (*log)(malloc_zone_t *, void *);\n\tvoid (*force_lock)(malloc_zone_t *);\n\tvoid (*force_unlock)(malloc_zone_t *);\n\tvoid (*statistics)(malloc_zone_t *, malloc_statistics_t *);\n\tboolean_t (*zone_locked)(malloc_zone_t *);\n\tboolean_t (*enable_discharge_checking)(malloc_zone_t *);\n\tboolean_t (*disable_discharge_checking)(malloc_zone_t *);\n\tvoid (*discharge)(malloc_zone_t *, void *);\n#ifdef __BLOCKS__\n\tvoid (*enumerate_discharged_pointers)(malloc_zone_t *, void (^)(void *, void *));\n#else\n\tvoid *enumerate_unavailable_without_blocks;\n#endif\n\tvoid (*reinit_lock)(malloc_zone_t *);\n} malloc_introspection_t;\n\nextern kern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *);\n\nextern malloc_zone_t *malloc_default_zone(void);\n\nextern void malloc_zone_register(malloc_zone_t *zone);\n\nextern void malloc_zone_unregister(malloc_zone_t *zone);\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 *default_zone, *purgeable_zone;\nstatic malloc_zone_t jemalloc_zone;\nstatic struct malloc_introspection_t jemalloc_zone_introspect;\nstatic pid_t zone_force_lock_pid = -1;\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic size_t\tzone_size(malloc_zone_t *zone, const 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);\nstatic void\t*zone_memalign(malloc_zone_t *zone, size_t alignment,\n    size_t size);\nstatic void\tzone_free_definite_size(malloc_zone_t *zone, void *ptr,\n    size_t size);\nstatic void\tzone_destroy(malloc_zone_t *zone);\nstatic unsigned\tzone_batch_malloc(struct _malloc_zone_t *zone, size_t size,\n    void **results, unsigned num_requested);\nstatic void\tzone_batch_free(struct _malloc_zone_t *zone,\n    void **to_be_freed, unsigned num_to_be_freed);\nstatic size_t\tzone_pressure_relief(struct _malloc_zone_t *zone, size_t goal);\nstatic size_t\tzone_good_size(malloc_zone_t *zone, size_t size);\nstatic kern_return_t\tzone_enumerator(task_t task, void *data, unsigned type_mask,\n    vm_address_t zone_address, memory_reader_t reader,\n    vm_range_recorder_t recorder);\nstatic boolean_t\tzone_check(malloc_zone_t *zone);\nstatic void\tzone_print(malloc_zone_t *zone, boolean_t verbose);\nstatic void\tzone_log(malloc_zone_t *zone, void *address);\nstatic void\tzone_force_lock(malloc_zone_t *zone);\nstatic void\tzone_force_unlock(malloc_zone_t *zone);\nstatic void\tzone_statistics(malloc_zone_t *zone,\n    malloc_statistics_t *stats);\nstatic boolean_t\tzone_locked(malloc_zone_t *zone);\nstatic void\tzone_reinit_lock(malloc_zone_t *zone);\n\n/******************************************************************************/\n/*\n * Functions.\n */\n\nstatic size_t\nzone_size(malloc_zone_t *zone, const void *ptr) {\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 extent before determining size.\n\t */\n\treturn ivsalloc(tsdn_fetch(), ptr);\n}\n\nstatic void *\nzone_malloc(malloc_zone_t *zone, size_t size) {\n\treturn je_malloc(size);\n}\n\nstatic void *\nzone_calloc(malloc_zone_t *zone, size_t num, size_t size) {\n\treturn je_calloc(num, size);\n}\n\nstatic void *\nzone_valloc(malloc_zone_t *zone, size_t size) {\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\tif (ivsalloc(tsdn_fetch(), ptr) != 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\tif (ivsalloc(tsdn_fetch(), ptr) != 0) {\n\t\treturn je_realloc(ptr, size);\n\t}\n\n\treturn realloc(ptr, size);\n}\n\nstatic void *\nzone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) {\n\tvoid *ret = NULL; /* Assignment avoids useless compiler warning. */\n\n\tje_posix_memalign(&ret, alignment, size);\n\n\treturn ret;\n}\n\nstatic void\nzone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) {\n\tsize_t alloc_size;\n\n\talloc_size = ivsalloc(tsdn_fetch(), ptr);\n\tif (alloc_size != 0) {\n\t\tassert(alloc_size == size);\n\t\tje_free(ptr);\n\t\treturn;\n\t}\n\n\tfree(ptr);\n}\n\nstatic void\nzone_destroy(malloc_zone_t *zone) {\n\t/* This function should never be called. */\n\tnot_reached();\n}\n\nstatic unsigned\nzone_batch_malloc(struct _malloc_zone_t *zone, size_t size, void **results,\n    unsigned num_requested) {\n\tunsigned i;\n\n\tfor (i = 0; i < num_requested; i++) {\n\t\tresults[i] = je_malloc(size);\n\t\tif (!results[i])\n\t\t\tbreak;\n\t}\n\n\treturn i;\n}\n\nstatic void\nzone_batch_free(struct _malloc_zone_t *zone, void **to_be_freed,\n    unsigned num_to_be_freed) {\n\tunsigned i;\n\n\tfor (i = 0; i < num_to_be_freed; i++) {\n\t\tzone_free(zone, to_be_freed[i]);\n\t\tto_be_freed[i] = NULL;\n\t}\n}\n\nstatic size_t\nzone_pressure_relief(struct _malloc_zone_t *zone, size_t goal) {\n\treturn 0;\n}\n\nstatic size_t\nzone_good_size(malloc_zone_t *zone, size_t size) {\n\tif (size == 0) {\n\t\tsize = 1;\n\t}\n\treturn sz_s2u(size);\n}\n\nstatic kern_return_t\nzone_enumerator(task_t task, void *data, unsigned type_mask,\n    vm_address_t zone_address, memory_reader_t reader,\n    vm_range_recorder_t recorder) {\n\treturn KERN_SUCCESS;\n}\n\nstatic boolean_t\nzone_check(malloc_zone_t *zone) {\n\treturn true;\n}\n\nstatic void\nzone_print(malloc_zone_t *zone, boolean_t verbose) {\n}\n\nstatic void\nzone_log(malloc_zone_t *zone, void *address) {\n}\n\nstatic void\nzone_force_lock(malloc_zone_t *zone) {\n\tif (isthreaded) {\n\t\t/*\n\t\t * See the note in zone_force_unlock, below, to see why we need\n\t\t * this.\n\t\t */\n\t\tassert(zone_force_lock_pid == -1);\n\t\tzone_force_lock_pid = getpid();\n\t\tjemalloc_prefork();\n\t}\n}\n\nstatic void\nzone_force_unlock(malloc_zone_t *zone) {\n\t/*\n\t * zone_force_lock and zone_force_unlock are the entry points to the\n\t * forking machinery on OS X.  The tricky thing is, the child is not\n\t * allowed to unlock mutexes locked in the parent, even if owned by the\n\t * forking thread (and the mutex type we use in OS X will fail an assert\n\t * if we try).  In the child, we can get away with reinitializing all\n\t * the mutexes, which has the effect of unlocking them.  In the parent,\n\t * doing this would mean we wouldn't wake any waiters blocked on the\n\t * mutexes we unlock.  So, we record the pid of the current thread in\n\t * zone_force_lock, and use that to detect if we're in the parent or\n\t * child here, to decide which unlock logic we need.\n\t */\n\tif (isthreaded) {\n\t\tassert(zone_force_lock_pid != -1);\n\t\tif (getpid() == zone_force_lock_pid) {\n\t\t\tjemalloc_postfork_parent();\n\t\t} else {\n\t\t\tjemalloc_postfork_child();\n\t\t}\n\t\tzone_force_lock_pid = -1;\n\t}\n}\n\nstatic void\nzone_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {\n\t/* We make no effort to actually fill the values */\n\tstats->blocks_in_use = 0;\n\tstats->size_in_use = 0;\n\tstats->max_size_in_use = 0;\n\tstats->size_allocated = 0;\n}\n\nstatic boolean_t\nzone_locked(malloc_zone_t *zone) {\n\t/* Pretend no lock is being held */\n\treturn false;\n}\n\nstatic void\nzone_reinit_lock(malloc_zone_t *zone) {\n\t/* As of OSX 10.12, this function is only used when force_unlock would\n\t * be used if the zone version were < 9. So just use force_unlock. */\n\tzone_force_unlock(zone);\n}\n\nstatic void\nzone_init(void) {\n\tjemalloc_zone.size = zone_size;\n\tjemalloc_zone.malloc = zone_malloc;\n\tjemalloc_zone.calloc = zone_calloc;\n\tjemalloc_zone.valloc = zone_valloc;\n\tjemalloc_zone.free = zone_free;\n\tjemalloc_zone.realloc = zone_realloc;\n\tjemalloc_zone.destroy = zone_destroy;\n\tjemalloc_zone.zone_name = \"jemalloc_zone\";\n\tjemalloc_zone.batch_malloc = zone_batch_malloc;\n\tjemalloc_zone.batch_free = zone_batch_free;\n\tjemalloc_zone.introspect = &jemalloc_zone_introspect;\n\tjemalloc_zone.version = 9;\n\tjemalloc_zone.memalign = zone_memalign;\n\tjemalloc_zone.free_definite_size = zone_free_definite_size;\n\tjemalloc_zone.pressure_relief = zone_pressure_relief;\n\n\tjemalloc_zone_introspect.enumerator = zone_enumerator;\n\tjemalloc_zone_introspect.good_size = zone_good_size;\n\tjemalloc_zone_introspect.check = zone_check;\n\tjemalloc_zone_introspect.print = zone_print;\n\tjemalloc_zone_introspect.log = zone_log;\n\tjemalloc_zone_introspect.force_lock = zone_force_lock;\n\tjemalloc_zone_introspect.force_unlock = zone_force_unlock;\n\tjemalloc_zone_introspect.statistics = zone_statistics;\n\tjemalloc_zone_introspect.zone_locked = zone_locked;\n\tjemalloc_zone_introspect.enable_discharge_checking = NULL;\n\tjemalloc_zone_introspect.disable_discharge_checking = NULL;\n\tjemalloc_zone_introspect.discharge = NULL;\n#ifdef __BLOCKS__\n\tjemalloc_zone_introspect.enumerate_discharged_pointers = NULL;\n#else\n\tjemalloc_zone_introspect.enumerate_unavailable_without_blocks = NULL;\n#endif\n\tjemalloc_zone_introspect.reinit_lock = zone_reinit_lock;\n}\n\nstatic malloc_zone_t *\nzone_default_get(void) {\n\tmalloc_zone_t **zones = NULL;\n\tunsigned int num_zones = 0;\n\n\t/*\n\t * On OSX 10.12, malloc_default_zone returns a special zone that is not\n\t * present in the list of registered zones. That zone uses a \"lite zone\"\n\t * if one is present (apparently enabled when malloc stack logging is\n\t * enabled), or the first registered zone otherwise. In practice this\n\t * means unless malloc stack logging is enabled, the first registered\n\t * zone is the default.  So get the list of zones to get the first one,\n\t * instead of relying on malloc_default_zone.\n\t */\n\tif (KERN_SUCCESS != malloc_get_all_zones(0, NULL,\n\t    (vm_address_t**)&zones, &num_zones)) {\n\t\t/*\n\t\t * Reset the value in case the failure happened after it was\n\t\t * set.\n\t\t */\n\t\tnum_zones = 0;\n\t}\n\n\tif (num_zones) {\n\t\treturn zones[0];\n\t}\n\n\treturn malloc_default_zone();\n}\n\n/* As written, this function can only promote jemalloc_zone. */\nstatic void\nzone_promote(void) {\n\tmalloc_zone_t *zone;\n\n\tdo {\n\t\t/*\n\t\t * Unregister and reregister the default zone.  On OSX >= 10.6,\n\t\t * unregistering takes the last registered zone and places it\n\t\t * at the location of the specified zone.  Unregistering the\n\t\t * default zone thus makes the last registered one the default.\n\t\t * On OSX < 10.6, unregistering shifts all registered zones.\n\t\t * The first registered zone then becomes the default.\n\t\t */\n\t\tmalloc_zone_unregister(default_zone);\n\t\tmalloc_zone_register(default_zone);\n\n\t\t/*\n\t\t * On OSX 10.6, having the default purgeable zone appear before\n\t\t * the default zone makes some things crash because it thinks it\n\t\t * owns the default zone allocated pointers.  We thus\n\t\t * unregister/re-register it in order to ensure it's always\n\t\t * after the default zone.  On OSX < 10.6, there is no purgeable\n\t\t * zone, so this does nothing.  On OSX >= 10.6, unregistering\n\t\t * replaces the purgeable zone with the last registered zone\n\t\t * above, i.e. the default zone.  Registering it again then puts\n\t\t * it at the end, obviously after the default zone.\n\t\t */\n\t\tif (purgeable_zone != NULL) {\n\t\t\tmalloc_zone_unregister(purgeable_zone);\n\t\t\tmalloc_zone_register(purgeable_zone);\n\t\t}\n\n\t\tzone = zone_default_get();\n\t} while (zone != &jemalloc_zone);\n}\n\nJEMALLOC_ATTR(constructor)\nvoid\nzone_register(void) {\n\t/*\n\t * If something else replaced the system default zone allocator, don't\n\t * register jemalloc's.\n\t */\n\tdefault_zone = zone_default_get();\n\tif (!default_zone->zone_name || strcmp(default_zone->zone_name,\n\t    \"DefaultMallocZone\") != 0) {\n\t\treturn;\n\t}\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\tpurgeable_zone = (malloc_default_purgeable_zone == NULL) ? NULL :\n\t    malloc_default_purgeable_zone();\n\n\t/* Register the custom zone.  At this point it won't be the default. */\n\tzone_init();\n\tmalloc_zone_register(&jemalloc_zone);\n\n\t/* Promote the custom zone to be default. */\n\tzone_promote();\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-alti.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file SFMT-alti.h\n *\n * @brief SIMD oriented Fast Mersenne Twister(SFMT)\n * pseudorandom number generator\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * Copyright (C) 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software.\n * see LICENSE.txt\n */\n\n#ifndef SFMT_ALTI_H\n#define SFMT_ALTI_H\n\n/**\n * This function represents the recursion formula in AltiVec and BIG ENDIAN.\n * @param a a 128-bit part of the interal state array\n * @param b a 128-bit part of the interal state array\n * @param c a 128-bit part of the interal state array\n * @param d a 128-bit part of the interal state array\n * @return output\n */\nJEMALLOC_ALWAYS_INLINE\nvector unsigned int vec_recursion(vector unsigned int a,\n\t\t\t\t\t\tvector unsigned int b,\n\t\t\t\t\t\tvector unsigned int c,\n\t\t\t\t\t\tvector unsigned int d) {\n\n    const vector unsigned int sl1 = ALTI_SL1;\n    const vector unsigned int sr1 = ALTI_SR1;\n#ifdef ONLY64\n    const vector unsigned int mask = ALTI_MSK64;\n    const vector unsigned char perm_sl = ALTI_SL2_PERM64;\n    const vector unsigned char perm_sr = ALTI_SR2_PERM64;\n#else\n    const vector unsigned int mask = ALTI_MSK;\n    const vector unsigned char perm_sl = ALTI_SL2_PERM;\n    const vector unsigned char perm_sr = ALTI_SR2_PERM;\n#endif\n    vector unsigned int v, w, x, y, z;\n    x = vec_perm(a, (vector unsigned int)perm_sl, perm_sl);\n    v = a;\n    y = vec_sr(b, sr1);\n    z = vec_perm(c, (vector unsigned int)perm_sr, perm_sr);\n    w = vec_sl(d, sl1);\n    z = vec_xor(z, w);\n    y = vec_and(y, mask);\n    v = vec_xor(v, x);\n    z = vec_xor(z, y);\n    z = vec_xor(z, v);\n    return z;\n}\n\n/**\n * This function fills the internal state array with pseudorandom\n * integers.\n */\nstatic inline void gen_rand_all(sfmt_t *ctx) {\n    int i;\n    vector unsigned int r, r1, r2;\n\n    r1 = ctx->sfmt[N - 2].s;\n    r2 = ctx->sfmt[N - 1].s;\n    for (i = 0; i < N - POS1; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, ctx->sfmt[i + POS1].s, r1, r2);\n\tctx->sfmt[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, ctx->sfmt[i + POS1 - N].s, r1, r2);\n\tctx->sfmt[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n/**\n * This function fills the user-specified array with pseudorandom\n * integers.\n *\n * @param array an 128-bit array to be filled by pseudorandom numbers.\n * @param size number of 128-bit pesudorandom numbers to be generated.\n */\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size) {\n    int i, j;\n    vector unsigned int r, r1, r2;\n\n    r1 = ctx->sfmt[N - 2].s;\n    r2 = ctx->sfmt[N - 1].s;\n    for (i = 0; i < N - POS1; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, ctx->sfmt[i + POS1].s, r1, r2);\n\tarray[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, array[i + POS1 - N].s, r1, r2);\n\tarray[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    /* main loop */\n    for (; i < size - N; i++) {\n\tr = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2);\n\tarray[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (j = 0; j < 2 * N - size; j++) {\n\tctx->sfmt[j].s = array[j + size - N].s;\n    }\n    for (; i < size; i++) {\n\tr = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2);\n\tarray[i].s = r;\n\tctx->sfmt[j++].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n#ifndef ONLY64\n#if defined(__APPLE__)\n#define ALTI_SWAP (vector unsigned char) \\\n\t(4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11)\n#else\n#define ALTI_SWAP {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}\n#endif\n/**\n * This function swaps high and low 32-bit of 64-bit integers in user\n * specified array.\n *\n * @param array an 128-bit array to be swaped.\n * @param size size of 128-bit array.\n */\nstatic inline void swap(w128_t *array, int size) {\n    int i;\n    const vector unsigned char perm = ALTI_SWAP;\n\n    for (i = 0; i < size; i++) {\n\tarray[i].s = vec_perm(array[i].s, (vector unsigned int)perm, perm);\n    }\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS_H\n#define SFMT_PARAMS_H\n\n#if !defined(MEXP)\n#ifdef __GNUC__\n  #warning \"MEXP is not defined. I assume MEXP is 19937.\"\n#endif\n  #define MEXP 19937\n#endif\n/*-----------------\n  BASIC DEFINITIONS\n  -----------------*/\n/** Mersenne Exponent. The period of the sequence \n *  is a multiple of 2^MEXP-1.\n * #define MEXP 19937 */\n/** SFMT generator has an internal state array of 128-bit integers,\n * and N is its size. */\n#define N (MEXP / 128 + 1)\n/** N32 is the size of internal state array when regarded as an array\n * of 32-bit integers.*/\n#define N32 (N * 4)\n/** N64 is the size of internal state array when regarded as an array\n * of 64-bit integers.*/\n#define N64 (N * 2)\n\n/*----------------------\n  the parameters of SFMT\n  following definitions are in paramsXXXX.h file.\n  ----------------------*/\n/** the pick up position of the array.\n#define POS1 122 \n*/\n\n/** the parameter of shift left as four 32-bit registers.\n#define SL1 18\n */\n\n/** the parameter of shift left as one 128-bit register. \n * The 128-bit integer is shifted by (SL2 * 8) bits. \n#define SL2 1 \n*/\n\n/** the parameter of shift right as four 32-bit registers.\n#define SR1 11\n*/\n\n/** the parameter of shift right as one 128-bit register. \n * The 128-bit integer is shifted by (SL2 * 8) bits. \n#define SR2 1 \n*/\n\n/** A bitmask, used in the recursion.  These parameters are introduced\n * to break symmetry of SIMD.\n#define MSK1 0xdfffffefU\n#define MSK2 0xddfecb7fU\n#define MSK3 0xbffaffffU\n#define MSK4 0xbffffff6U \n*/\n\n/** These definitions are part of a 128-bit period certification vector.\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0xc98e126aU\n*/\n\n#if MEXP == 607\n  #include \"test/SFMT-params607.h\"\n#elif MEXP == 1279\n  #include \"test/SFMT-params1279.h\"\n#elif MEXP == 2281\n  #include \"test/SFMT-params2281.h\"\n#elif MEXP == 4253\n  #include \"test/SFMT-params4253.h\"\n#elif MEXP == 11213\n  #include \"test/SFMT-params11213.h\"\n#elif MEXP == 19937\n  #include \"test/SFMT-params19937.h\"\n#elif MEXP == 44497\n  #include \"test/SFMT-params44497.h\"\n#elif MEXP == 86243\n  #include \"test/SFMT-params86243.h\"\n#elif MEXP == 132049\n  #include \"test/SFMT-params132049.h\"\n#elif MEXP == 216091\n  #include \"test/SFMT-params216091.h\"\n#else\n#ifdef __GNUC__\n  #error \"MEXP is not valid.\"\n  #undef MEXP\n#else\n  #undef MEXP\n#endif\n\n#endif\n\n#endif /* SFMT_PARAMS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params11213.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS11213_H\n#define SFMT_PARAMS11213_H\n\n#define POS1\t68\n#define SL1\t14\n#define SL2\t3\n#define SR1\t7\n#define SR2\t3\n#define MSK1\t0xeffff7fbU\n#define MSK2\t0xffffffefU\n#define MSK3\t0xdfdfbfffU\n#define MSK4\t0x7fffdbfdU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0xe8148000U\n#define PARITY4\t0xd0c7afa3U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12}\n    #define ALTI_SR2_PERM64\t{13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-11213:68-14-3-7-3:effff7fb-ffffffef-dfdfbfff-7fffdbfd\"\n\n#endif /* SFMT_PARAMS11213_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params1279.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS1279_H\n#define SFMT_PARAMS1279_H\n\n#define POS1\t7\n#define SL1\t14\n#define SL2\t3\n#define SR1\t5\n#define SR2\t1\n#define MSK1\t0xf7fefffdU\n#define MSK2\t0x7fefcfffU\n#define MSK3\t0xaff3ef3fU\n#define MSK4\t0xb5ffff7fU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x20000000U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-1279:7-14-3-5-1:f7fefffd-7fefcfff-aff3ef3f-b5ffff7f\"\n\n#endif /* SFMT_PARAMS1279_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params132049.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS132049_H\n#define SFMT_PARAMS132049_H\n\n#define POS1\t110\n#define SL1\t19\n#define SL2\t1\n#define SR1\t21\n#define SR2\t1\n#define MSK1\t0xffffbb5fU\n#define MSK2\t0xfb6ebf95U\n#define MSK3\t0xfffefffaU\n#define MSK4\t0xcff77fffU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0xcb520000U\n#define PARITY4\t0xc7e91c7dU\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-132049:110-19-1-21-1:ffffbb5f-fb6ebf95-fffefffa-cff77fff\"\n\n#endif /* SFMT_PARAMS132049_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params19937.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS19937_H\n#define SFMT_PARAMS19937_H\n\n#define POS1\t122\n#define SL1\t18\n#define SL2\t1\n#define SR1\t11\n#define SR2\t1\n#define MSK1\t0xdfffffefU\n#define MSK2\t0xddfecb7fU\n#define MSK3\t0xbffaffffU\n#define MSK4\t0xbffffff6U\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x13c9e684U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-19937:122-18-1-11-1:dfffffef-ddfecb7f-bffaffff-bffffff6\"\n\n#endif /* SFMT_PARAMS19937_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params216091.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS216091_H\n#define SFMT_PARAMS216091_H\n\n#define POS1\t627\n#define SL1\t11\n#define SL2\t3\n#define SR1\t10\n#define SR2\t1\n#define MSK1\t0xbff7bff7U\n#define MSK2\t0xbfffffffU\n#define MSK3\t0xbffffa7fU\n#define MSK4\t0xffddfbfbU\n#define PARITY1\t0xf8000001U\n#define PARITY2\t0x89e80709U\n#define PARITY3\t0x3bd2b64bU\n#define PARITY4\t0x0c64b1e4U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-216091:627-11-3-10-1:bff7bff7-bfffffff-bffffa7f-ffddfbfb\"\n\n#endif /* SFMT_PARAMS216091_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params2281.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS2281_H\n#define SFMT_PARAMS2281_H\n\n#define POS1\t12\n#define SL1\t19\n#define SL2\t1\n#define SR1\t5\n#define SR2\t1\n#define MSK1\t0xbff7ffbfU\n#define MSK2\t0xfdfffffeU\n#define MSK3\t0xf7ffef7fU\n#define MSK4\t0xf2f7cbbfU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x41dfa600U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-2281:12-19-1-5-1:bff7ffbf-fdfffffe-f7ffef7f-f2f7cbbf\"\n\n#endif /* SFMT_PARAMS2281_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params4253.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS4253_H\n#define SFMT_PARAMS4253_H\n\n#define POS1\t17\n#define SL1\t20\n#define SL2\t1\n#define SR1\t7\n#define SR2\t1\n#define MSK1\t0x9f7bffffU\n#define MSK2\t0x9fffff5fU\n#define MSK3\t0x3efffffbU\n#define MSK4\t0xfffff7bbU\n#define PARITY1\t0xa8000001U\n#define PARITY2\t0xaf5390a3U\n#define PARITY3\t0xb740b3f8U\n#define PARITY4\t0x6c11486dU\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-4253:17-20-1-7-1:9f7bffff-9fffff5f-3efffffb-fffff7bb\"\n\n#endif /* SFMT_PARAMS4253_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params44497.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS44497_H\n#define SFMT_PARAMS44497_H\n\n#define POS1\t330\n#define SL1\t5\n#define SL2\t3\n#define SR1\t9\n#define SR2\t3\n#define MSK1\t0xeffffffbU\n#define MSK2\t0xdfbebfffU\n#define MSK3\t0xbfbf7befU\n#define MSK4\t0x9ffd7bffU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0xa3ac4000U\n#define PARITY4\t0xecc1327aU\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12}\n    #define ALTI_SR2_PERM64\t{13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-44497:330-5-3-9-3:effffffb-dfbebfff-bfbf7bef-9ffd7bff\"\n\n#endif /* SFMT_PARAMS44497_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params607.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS607_H\n#define SFMT_PARAMS607_H\n\n#define POS1\t2\n#define SL1\t15\n#define SL2\t3\n#define SR1\t13\n#define SR2\t3\n#define MSK1\t0xfdff37ffU\n#define MSK2\t0xef7f3f7dU\n#define MSK3\t0xff777b7dU\n#define MSK4\t0x7ff7fb2fU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x5986f054U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12}\n    #define ALTI_SR2_PERM64\t{13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-607:2-15-3-13-3:fdff37ff-ef7f3f7d-ff777b7d-7ff7fb2f\"\n\n#endif /* SFMT_PARAMS607_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params86243.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS86243_H\n#define SFMT_PARAMS86243_H\n\n#define POS1\t366\n#define SL1\t6\n#define SL2\t7\n#define SR1\t19\n#define SR2\t1\n#define MSK1\t0xfdbffbffU\n#define MSK2\t0xbff7ff3fU\n#define MSK3\t0xfd77efffU\n#define MSK4\t0xbf9ff3ffU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0xe9528d85U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(25,25,25,25,3,25,25,25,7,0,1,2,11,4,5,6)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(7,25,25,25,25,25,25,25,15,0,1,2,3,4,5,6)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{25,25,25,25,3,25,25,25,7,0,1,2,11,4,5,6}\n    #define ALTI_SL2_PERM64\t{7,25,25,25,25,25,25,25,15,0,1,2,3,4,5,6}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-86243:366-6-7-19-1:fdbffbff-bff7ff3f-fd77efff-bf9ff3ff\"\n\n#endif /* SFMT_PARAMS86243_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-sse2.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file  SFMT-sse2.h\n * @brief SIMD oriented Fast Mersenne Twister(SFMT) for Intel SSE2\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * @note We assume LITTLE ENDIAN in this file\n *\n * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software, see LICENSE.txt\n */\n\n#ifndef SFMT_SSE2_H\n#define SFMT_SSE2_H\n\n/**\n * This function represents the recursion formula.\n * @param a a 128-bit part of the interal state array\n * @param b a 128-bit part of the interal state array\n * @param c a 128-bit part of the interal state array\n * @param d a 128-bit part of the interal state array\n * @param mask 128-bit mask\n * @return output\n */\nJEMALLOC_ALWAYS_INLINE __m128i mm_recursion(__m128i *a, __m128i *b,\n\t\t\t\t   __m128i c, __m128i d, __m128i mask) {\n    __m128i v, x, y, z;\n\n    x = _mm_load_si128(a);\n    y = _mm_srli_epi32(*b, SR1);\n    z = _mm_srli_si128(c, SR2);\n    v = _mm_slli_epi32(d, SL1);\n    z = _mm_xor_si128(z, x);\n    z = _mm_xor_si128(z, v);\n    x = _mm_slli_si128(x, SL2);\n    y = _mm_and_si128(y, mask);\n    z = _mm_xor_si128(z, x);\n    z = _mm_xor_si128(z, y);\n    return z;\n}\n\n/**\n * This function fills the internal state array with pseudorandom\n * integers.\n */\nstatic inline void gen_rand_all(sfmt_t *ctx) {\n    int i;\n    __m128i r, r1, r2, mask;\n    mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1);\n\n    r1 = _mm_load_si128(&ctx->sfmt[N - 2].si);\n    r2 = _mm_load_si128(&ctx->sfmt[N - 1].si);\n    for (i = 0; i < N - POS1; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &ctx->sfmt[i + POS1].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&ctx->sfmt[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &ctx->sfmt[i + POS1 - N].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&ctx->sfmt[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n/**\n * This function fills the user-specified array with pseudorandom\n * integers.\n *\n * @param array an 128-bit array to be filled by pseudorandom numbers.\n * @param size number of 128-bit pesudorandom numbers to be generated.\n */\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size) {\n    int i, j;\n    __m128i r, r1, r2, mask;\n    mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1);\n\n    r1 = _mm_load_si128(&ctx->sfmt[N - 2].si);\n    r2 = _mm_load_si128(&ctx->sfmt[N - 1].si);\n    for (i = 0; i < N - POS1; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &ctx->sfmt[i + POS1].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&array[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &array[i + POS1 - N].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&array[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    /* main loop */\n    for (; i < size - N; i++) {\n\tr = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2,\n\t\t\t mask);\n\t_mm_store_si128(&array[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (j = 0; j < 2 * N - size; j++) {\n\tr = _mm_load_si128(&array[j + size - N].si);\n\t_mm_store_si128(&ctx->sfmt[j].si, r);\n    }\n    for (; i < size; i++) {\n\tr = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2,\n\t\t\t mask);\n\t_mm_store_si128(&array[i].si, r);\n\t_mm_store_si128(&ctx->sfmt[j++].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/SFMT.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file SFMT.h \n *\n * @brief SIMD oriented Fast Mersenne Twister(SFMT) pseudorandom\n * number generator\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software.\n * see LICENSE.txt\n *\n * @note We assume that your system has inttypes.h.  If your system\n * doesn't have inttypes.h, you have to typedef uint32_t and uint64_t,\n * and you have to define PRIu64 and PRIx64 in this file as follows:\n * @verbatim\n typedef unsigned int uint32_t\n typedef unsigned long long uint64_t  \n #define PRIu64 \"llu\"\n #define PRIx64 \"llx\"\n@endverbatim\n * uint32_t must be exactly 32-bit unsigned integer type (no more, no\n * less), and uint64_t must be exactly 64-bit unsigned integer type.\n * PRIu64 and PRIx64 are used for printf function to print 64-bit\n * unsigned int and 64-bit unsigned int in hexadecimal format.\n */\n\n#ifndef SFMT_H\n#define SFMT_H\n\ntypedef struct sfmt_s sfmt_t;\n\nuint32_t gen_rand32(sfmt_t *ctx);\nuint32_t gen_rand32_range(sfmt_t *ctx, uint32_t limit);\nuint64_t gen_rand64(sfmt_t *ctx);\nuint64_t gen_rand64_range(sfmt_t *ctx, uint64_t limit);\nvoid fill_array32(sfmt_t *ctx, uint32_t *array, int size);\nvoid fill_array64(sfmt_t *ctx, uint64_t *array, int size);\nsfmt_t *init_gen_rand(uint32_t seed);\nsfmt_t *init_by_array(uint32_t *init_key, int key_length);\nvoid fini_gen_rand(sfmt_t *ctx);\nconst char *get_idstring(void);\nint get_min_array_size32(void);\nint get_min_array_size64(void);\n\n/* These real versions are due to Isaku Wada */\n/** generates a random number on [0,1]-real-interval */\nstatic inline double to_real1(uint32_t v) {\n    return v * (1.0/4294967295.0); \n    /* divided by 2^32-1 */ \n}\n\n/** generates a random number on [0,1]-real-interval */\nstatic inline double genrand_real1(sfmt_t *ctx) {\n    return to_real1(gen_rand32(ctx));\n}\n\n/** generates a random number on [0,1)-real-interval */\nstatic inline double to_real2(uint32_t v) {\n    return v * (1.0/4294967296.0); \n    /* divided by 2^32 */\n}\n\n/** generates a random number on [0,1)-real-interval */\nstatic inline double genrand_real2(sfmt_t *ctx) {\n    return to_real2(gen_rand32(ctx));\n}\n\n/** generates a random number on (0,1)-real-interval */\nstatic inline double to_real3(uint32_t v) {\n    return (((double)v) + 0.5)*(1.0/4294967296.0); \n    /* divided by 2^32 */\n}\n\n/** generates a random number on (0,1)-real-interval */\nstatic inline double genrand_real3(sfmt_t *ctx) {\n    return to_real3(gen_rand32(ctx));\n}\n/** These real versions are due to Isaku Wada */\n\n/** generates a random number on [0,1) with 53-bit resolution*/\nstatic inline double to_res53(uint64_t v) {\n    return v * (1.0/18446744073709551616.0L);\n}\n\n/** generates a random number on [0,1) with 53-bit resolution from two\n * 32 bit integers */\nstatic inline double to_res53_mix(uint32_t x, uint32_t y) {\n    return to_res53(x | ((uint64_t)y << 32));\n}\n\n/** generates a random number on [0,1) with 53-bit resolution\n */\nstatic inline double genrand_res53(sfmt_t *ctx) {\n    return to_res53(gen_rand64(ctx));\n}\n\n/** generates a random number on [0,1) with 53-bit resolution\n    using 32bit integer.\n */\nstatic inline double genrand_res53_mix(sfmt_t *ctx) {\n    uint32_t x, y;\n\n    x = gen_rand32(ctx);\n    y = gen_rand32(ctx);\n    return to_res53_mix(x, y);\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/btalloc.h",
    "content": "/* btalloc() provides a mechanism for allocating via permuted backtraces. */\nvoid\t*btalloc(size_t size, unsigned bits);\n\n#define btalloc_n_proto(n)\t\t\t\t\t\t\\\nvoid\t*btalloc_##n(size_t size, unsigned bits);\nbtalloc_n_proto(0)\nbtalloc_n_proto(1)\n\n#define btalloc_n_gen(n)\t\t\t\t\t\t\\\nvoid *\t\t\t\t\t\t\t\t\t\\\nbtalloc_##n(size_t size, unsigned bits) {\t\t\t\t\\\n\tvoid *p;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (bits == 0) {\t\t\t\t\t\t\\\n\t\tp = mallocx(size, 0);\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tswitch (bits & 0x1U) {\t\t\t\t\t\\\n\t\tcase 0:\t\t\t\t\t\t\t\\\n\t\t\tp = (btalloc_0(size, bits >> 1));\t\t\\\n\t\t\tbreak;\t\t\t\t\t\t\\\n\t\tcase 1:\t\t\t\t\t\t\t\\\n\t\t\tp = (btalloc_1(size, bits >> 1));\t\t\\\n\t\t\tbreak;\t\t\t\t\t\t\\\n\t\tdefault: not_reached();\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/* Intentionally sabotage tail call optimization. */\t\t\\\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\t\t\\\n\treturn p;\t\t\t\t\t\t\t\\\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/extent_hooks.h",
    "content": "/*\n * Boilerplate code used for testing extent hooks via interception and\n * passthrough.\n */\n\nstatic void\t*extent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit,\n    unsigned arena_ind);\nstatic bool\textent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic void\textent_destroy_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic bool\textent_commit_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_decommit_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_purge_forced_hook(extent_hooks_t *extent_hooks,\n    void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_split_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t size_a, size_t size_b, bool committed,\n    unsigned arena_ind);\nstatic bool\textent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a,\n    size_t size_a, void *addr_b, size_t size_b, bool committed,\n    unsigned arena_ind);\n\nstatic extent_hooks_t *default_hooks;\nstatic extent_hooks_t hooks = {\n\textent_alloc_hook,\n\textent_dalloc_hook,\n\textent_destroy_hook,\n\textent_commit_hook,\n\textent_decommit_hook,\n\textent_purge_lazy_hook,\n\textent_purge_forced_hook,\n\textent_split_hook,\n\textent_merge_hook\n};\n\n/* Control whether hook functions pass calls through to default hooks. */\nstatic bool try_alloc = true;\nstatic bool try_dalloc = true;\nstatic bool try_destroy = true;\nstatic bool try_commit = true;\nstatic bool try_decommit = true;\nstatic bool try_purge_lazy = true;\nstatic bool try_purge_forced = true;\nstatic bool try_split = true;\nstatic bool try_merge = true;\n\n/* Set to false prior to operations, then introspect after operations. */\nstatic bool called_alloc;\nstatic bool called_dalloc;\nstatic bool called_destroy;\nstatic bool called_commit;\nstatic bool called_decommit;\nstatic bool called_purge_lazy;\nstatic bool called_purge_forced;\nstatic bool called_split;\nstatic bool called_merge;\n\n/* Set to false prior to operations, then introspect after operations. */\nstatic bool did_alloc;\nstatic bool did_dalloc;\nstatic bool did_destroy;\nstatic bool did_commit;\nstatic bool did_decommit;\nstatic bool did_purge_lazy;\nstatic bool did_purge_forced;\nstatic bool did_split;\nstatic bool did_merge;\n\n#if 0\n#  define TRACE_HOOK(fmt, ...) malloc_printf(fmt, __VA_ARGS__)\n#else\n#  define TRACE_HOOK(fmt, ...)\n#endif\n\nstatic void *\nextent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {\n\tvoid *ret;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, new_addr=%p, size=%zu, alignment=%zu, \"\n\t    \"*zero=%s, *commit=%s, arena_ind=%u)\\n\", __func__, extent_hooks,\n\t    new_addr, size, alignment, *zero ?  \"true\" : \"false\", *commit ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->alloc, extent_alloc_hook,\n\t    \"Wrong hook function\");\n\tcalled_alloc = true;\n\tif (!try_alloc) {\n\t\treturn NULL;\n\t}\n\tret = default_hooks->alloc(default_hooks, new_addr, size, alignment,\n\t    zero, commit, 0);\n\tdid_alloc = (ret != NULL);\n\treturn ret;\n}\n\nstatic bool\nextent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, \"\n\t    \"arena_ind=%u)\\n\", __func__, extent_hooks, addr, size, committed ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->dalloc, extent_dalloc_hook,\n\t    \"Wrong hook function\");\n\tcalled_dalloc = true;\n\tif (!try_dalloc) {\n\t\treturn true;\n\t}\n\terr = default_hooks->dalloc(default_hooks, addr, size, committed, 0);\n\tdid_dalloc = !err;\n\treturn err;\n}\n\nstatic void\nextent_destroy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, \"\n\t    \"arena_ind=%u)\\n\", __func__, extent_hooks, addr, size, committed ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->destroy, extent_destroy_hook,\n\t    \"Wrong hook function\");\n\tcalled_destroy = true;\n\tif (!try_destroy) {\n\t\treturn;\n\t}\n\tdefault_hooks->destroy(default_hooks, addr, size, committed, 0);\n\tdid_destroy = true;\n}\n\nstatic bool\nextent_commit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu, arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->commit, extent_commit_hook,\n\t    \"Wrong hook function\");\n\tcalled_commit = true;\n\tif (!try_commit) {\n\t\treturn true;\n\t}\n\terr = default_hooks->commit(default_hooks, addr, size, offset, length,\n\t    0);\n\tdid_commit = !err;\n\treturn err;\n}\n\nstatic bool\nextent_decommit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu, arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->decommit, extent_decommit_hook,\n\t    \"Wrong hook function\");\n\tcalled_decommit = true;\n\tif (!try_decommit) {\n\t\treturn true;\n\t}\n\terr = default_hooks->decommit(default_hooks, addr, size, offset, length,\n\t    0);\n\tdid_decommit = !err;\n\treturn err;\n}\n\nstatic bool\nextent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->purge_lazy, extent_purge_lazy_hook,\n\t    \"Wrong hook function\");\n\tcalled_purge_lazy = true;\n\tif (!try_purge_lazy) {\n\t\treturn true;\n\t}\n\terr = default_hooks->purge_lazy == NULL ||\n\t    default_hooks->purge_lazy(default_hooks, addr, size, offset, length,\n\t    0);\n\tdid_purge_lazy = !err;\n\treturn err;\n}\n\nstatic bool\nextent_purge_forced_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->purge_forced, extent_purge_forced_hook,\n\t    \"Wrong hook function\");\n\tcalled_purge_forced = true;\n\tif (!try_purge_forced) {\n\t\treturn true;\n\t}\n\terr = default_hooks->purge_forced == NULL ||\n\t    default_hooks->purge_forced(default_hooks, addr, size, offset,\n\t    length, 0);\n\tdid_purge_forced = !err;\n\treturn err;\n}\n\nstatic bool\nextent_split_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, size_a=%zu, \"\n\t    \"size_b=%zu, committed=%s, arena_ind=%u)\\n\", __func__, extent_hooks,\n\t    addr, size, size_a, size_b, committed ? \"true\" : \"false\",\n\t    arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->split, extent_split_hook,\n\t    \"Wrong hook function\");\n\tcalled_split = true;\n\tif (!try_split) {\n\t\treturn true;\n\t}\n\terr = (default_hooks->split == NULL ||\n\t    default_hooks->split(default_hooks, addr, size, size_a, size_b,\n\t    committed, 0));\n\tdid_split = !err;\n\treturn err;\n}\n\nstatic bool\nextent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,\n    void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr_a=%p, size_a=%zu, addr_b=%p \"\n\t    \"size_b=%zu, committed=%s, arena_ind=%u)\\n\", __func__, extent_hooks,\n\t    addr_a, size_a, addr_b, size_b, committed ? \"true\" : \"false\",\n\t    arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->merge, extent_merge_hook,\n\t    \"Wrong hook function\");\n\tassert_ptr_eq((void *)((uintptr_t)addr_a + size_a), addr_b,\n\t    \"Extents not mergeable\");\n\tcalled_merge = true;\n\tif (!try_merge) {\n\t\treturn true;\n\t}\n\terr = (default_hooks->merge == NULL ||\n\t    default_hooks->merge(default_hooks, addr_a, size_a, addr_b, size_b,\n\t    committed, 0));\n\tdid_merge = !err;\n\treturn err;\n}\n\nstatic void\nextent_hooks_prep(void) {\n\tsize_t sz;\n\n\tsz = sizeof(default_hooks);\n\tassert_d_eq(mallctl(\"arena.0.extent_hooks\", (void *)&default_hooks, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() error\");\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/jemalloc_test.h.in",
    "content": "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <limits.h>\n#ifndef SIZE_T_MAX\n#  define SIZE_T_MAX\tSIZE_MAX\n#endif\n#include <stdlib.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <errno.h>\n#include <math.h>\n#include <string.h>\n#ifdef _WIN32\n#  include \"msvc_compat/strings.h\"\n#endif\n\n#ifdef _WIN32\n#  include <windows.h>\n#  include \"msvc_compat/windows_extra.h\"\n#else\n#  include <pthread.h>\n#endif\n\n#include \"test/jemalloc_test_defs.h\"\n\n#ifdef JEMALLOC_OSSPIN\n#  include <libkern/OSAtomic.h>\n#endif\n\n#if defined(HAVE_ALTIVEC) && !defined(__APPLE__)\n#  include <altivec.h>\n#endif\n#ifdef HAVE_SSE2\n#  include <emmintrin.h>\n#endif\n\n/******************************************************************************/\n/*\n * For unit tests, expose all public and private interfaces.\n */\n#ifdef JEMALLOC_UNIT_TEST\n#  define JEMALLOC_JET\n#  define JEMALLOC_MANGLE\n#  include \"jemalloc/internal/jemalloc_preamble.h\"\n#  include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n/******************************************************************************/\n/*\n * For integration tests, expose the public jemalloc interfaces, but only\n * expose the minimum necessary internal utility code (to avoid re-implementing\n * essentially identical code within the test infrastructure).\n */\n#elif defined(JEMALLOC_INTEGRATION_TEST) || \\\n    defined(JEMALLOC_INTEGRATION_CPP_TEST)\n#  define JEMALLOC_MANGLE\n#  include \"jemalloc/jemalloc@install_suffix@.h\"\n#  include \"jemalloc/internal/jemalloc_internal_defs.h\"\n#  include \"jemalloc/internal/jemalloc_internal_macros.h\"\n\nstatic const bool config_debug =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\n\n#  define JEMALLOC_N(n) @private_namespace@##n\n#  include \"jemalloc/internal/private_namespace.h\"\n#  include \"jemalloc/internal/hooks.h\"\n\n/* Hermetic headers. */\n#  include \"jemalloc/internal/assert.h\"\n#  include \"jemalloc/internal/malloc_io.h\"\n#  include \"jemalloc/internal/nstime.h\"\n#  include \"jemalloc/internal/util.h\"\n\n/* Non-hermetic headers. */\n#  include \"jemalloc/internal/qr.h\"\n#  include \"jemalloc/internal/ql.h\"\n\n/******************************************************************************/\n/*\n * For stress tests, expose the public jemalloc interfaces with name mangling\n * so that they can be tested as e.g. malloc() and free().  Also expose the\n * public jemalloc interfaces with jet_ prefixes, so that stress tests can use\n * a separate allocator for their internal data structures.\n */\n#elif defined(JEMALLOC_STRESS_TEST)\n#  include \"jemalloc/jemalloc@install_suffix@.h\"\n\n#  include \"jemalloc/jemalloc_protos_jet.h\"\n\n#  define JEMALLOC_JET\n#  include \"jemalloc/internal/jemalloc_preamble.h\"\n#  include \"jemalloc/internal/jemalloc_internal_includes.h\"\n#  include \"jemalloc/internal/public_unnamespace.h\"\n#  undef JEMALLOC_JET\n\n#  include \"jemalloc/jemalloc_rename.h\"\n#  define JEMALLOC_MANGLE\n#  ifdef JEMALLOC_STRESS_TESTLIB\n#    include \"jemalloc/jemalloc_mangle_jet.h\"\n#  else\n#    include \"jemalloc/jemalloc_mangle.h\"\n#  endif\n\n/******************************************************************************/\n/*\n * This header does dangerous things, the effects of which only test code\n * should be subject to.\n */\n#else\n#  error \"This header cannot be included outside a testing context\"\n#endif\n\n/******************************************************************************/\n/*\n * Common test utilities.\n */\n#include \"test/btalloc.h\"\n#include \"test/math.h\"\n#include \"test/mtx.h\"\n#include \"test/mq.h\"\n#include \"test/test.h\"\n#include \"test/timer.h\"\n#include \"test/thd.h\"\n#define MEXP 19937\n#include \"test/SFMT.h\"\n\n/******************************************************************************/\n/*\n * Define always-enabled assertion macros, so that test assertions execute even\n * if assertions are disabled in the library code.\n */\n#undef assert\n#undef not_reached\n#undef not_implemented\n#undef assert_not_implemented\n\n#define assert(e) do {\t\t\t\t\t\t\t\\\n\tif (!(e)) {\t\t\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\n#define not_reached() do {\t\t\t\t\t\t\\\n\tmalloc_printf(\t\t\t\t\t\t\t\\\n\t    \"<jemalloc>: %s:%d: Unreachable code reached\\n\",\t\t\\\n\t    __FILE__, __LINE__);\t\t\t\t\t\\\n\tabort();\t\t\t\t\t\t\t\\\n} while (0)\n\n#define not_implemented() do {\t\t\t\t\t\t\\\n\tmalloc_printf(\"<jemalloc>: %s:%d: Not implemented\\n\",\t\t\\\n\t    __FILE__, __LINE__);\t\t\t\t\t\\\n\tabort();\t\t\t\t\t\t\t\\\n} while (0)\n\n#define assert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (!(e)) {\t\t\t\t\t\t\t\\\n\t\tnot_implemented();\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/jemalloc_test_defs.h.in",
    "content": "#include \"jemalloc/internal/jemalloc_internal_defs.h\"\n#include \"jemalloc/internal/jemalloc_internal_decls.h\"\n\n/*\n * For use by SFMT.  configure.ac doesn't actually define HAVE_SSE2 because its\n * dependencies are notoriously unportable in practice.\n */\n#undef HAVE_SSE2\n#undef HAVE_ALTIVEC\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/math.h",
    "content": "/*\n * Compute the natural log of Gamma(x), accurate to 10 decimal places.\n *\n * This implementation is based on:\n *\n *   Pike, M.C., I.D. Hill (1966) Algorithm 291: Logarithm of Gamma function\n *   [S14].  Communications of the ACM 9(9):684.\n */\nstatic inline double\nln_gamma(double x) {\n\tdouble f, z;\n\n\tassert(x > 0.0);\n\n\tif (x < 7.0) {\n\t\tf = 1.0;\n\t\tz = x;\n\t\twhile (z < 7.0) {\n\t\t\tf *= z;\n\t\t\tz += 1.0;\n\t\t}\n\t\tx = z;\n\t\tf = -log(f);\n\t} else {\n\t\tf = 0.0;\n\t}\n\n\tz = 1.0 / (x * x);\n\n\treturn f + (x-0.5) * log(x) - x + 0.918938533204673 +\n\t    (((-0.000595238095238 * z + 0.000793650793651) * z -\n\t    0.002777777777778) * z + 0.083333333333333) / x;\n}\n\n/*\n * Compute the incomplete Gamma ratio for [0..x], where p is the shape\n * parameter, and ln_gamma_p is ln_gamma(p).\n *\n * This implementation is based on:\n *\n *   Bhattacharjee, G.P. (1970) Algorithm AS 32: The incomplete Gamma integral.\n *   Applied Statistics 19:285-287.\n */\nstatic inline double\ni_gamma(double x, double p, double ln_gamma_p) {\n\tdouble acu, factor, oflo, gin, term, rn, a, b, an, dif;\n\tdouble pn[6];\n\tunsigned i;\n\n\tassert(p > 0.0);\n\tassert(x >= 0.0);\n\n\tif (x == 0.0) {\n\t\treturn 0.0;\n\t}\n\n\tacu = 1.0e-10;\n\toflo = 1.0e30;\n\tgin = 0.0;\n\tfactor = exp(p * log(x) - x - ln_gamma_p);\n\n\tif (x <= 1.0 || x < p) {\n\t\t/* Calculation by series expansion. */\n\t\tgin = 1.0;\n\t\tterm = 1.0;\n\t\trn = p;\n\n\t\twhile (true) {\n\t\t\trn += 1.0;\n\t\t\tterm *= x / rn;\n\t\t\tgin += term;\n\t\t\tif (term <= acu) {\n\t\t\t\tgin *= factor / p;\n\t\t\t\treturn gin;\n\t\t\t}\n\t\t}\n\t} else {\n\t\t/* Calculation by continued fraction. */\n\t\ta = 1.0 - p;\n\t\tb = a + x + 1.0;\n\t\tterm = 0.0;\n\t\tpn[0] = 1.0;\n\t\tpn[1] = x;\n\t\tpn[2] = x + 1.0;\n\t\tpn[3] = x * b;\n\t\tgin = pn[2] / pn[3];\n\n\t\twhile (true) {\n\t\t\ta += 1.0;\n\t\t\tb += 2.0;\n\t\t\tterm += 1.0;\n\t\t\tan = a * term;\n\t\t\tfor (i = 0; i < 2; i++) {\n\t\t\t\tpn[i+4] = b * pn[i+2] - an * pn[i];\n\t\t\t}\n\t\t\tif (pn[5] != 0.0) {\n\t\t\t\trn = pn[4] / pn[5];\n\t\t\t\tdif = fabs(gin - rn);\n\t\t\t\tif (dif <= acu && dif <= acu * rn) {\n\t\t\t\t\tgin = 1.0 - factor * gin;\n\t\t\t\t\treturn gin;\n\t\t\t\t}\n\t\t\t\tgin = rn;\n\t\t\t}\n\t\t\tfor (i = 0; i < 4; i++) {\n\t\t\t\tpn[i] = pn[i+2];\n\t\t\t}\n\n\t\t\tif (fabs(pn[4]) >= oflo) {\n\t\t\t\tfor (i = 0; i < 4; i++) {\n\t\t\t\t\tpn[i] /= oflo;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n * Given a value p in [0..1] of the lower tail area of the normal distribution,\n * compute the limit on the definite integral from [-inf..z] that satisfies p,\n * accurate to 16 decimal places.\n *\n * This implementation is based on:\n *\n *   Wichura, M.J. (1988) Algorithm AS 241: The percentage points of the normal\n *   distribution.  Applied Statistics 37(3):477-484.\n */\nstatic inline double\npt_norm(double p) {\n\tdouble q, r, ret;\n\n\tassert(p > 0.0 && p < 1.0);\n\n\tq = p - 0.5;\n\tif (fabs(q) <= 0.425) {\n\t\t/* p close to 1/2. */\n\t\tr = 0.180625 - q * q;\n\t\treturn q * (((((((2.5090809287301226727e3 * r +\n\t\t    3.3430575583588128105e4) * r + 6.7265770927008700853e4) * r\n\t\t    + 4.5921953931549871457e4) * r + 1.3731693765509461125e4) *\n\t\t    r + 1.9715909503065514427e3) * r + 1.3314166789178437745e2)\n\t\t    * r + 3.3871328727963666080e0) /\n\t\t    (((((((5.2264952788528545610e3 * r +\n\t\t    2.8729085735721942674e4) * r + 3.9307895800092710610e4) * r\n\t\t    + 2.1213794301586595867e4) * r + 5.3941960214247511077e3) *\n\t\t    r + 6.8718700749205790830e2) * r + 4.2313330701600911252e1)\n\t\t    * r + 1.0);\n\t} else {\n\t\tif (q < 0.0) {\n\t\t\tr = p;\n\t\t} else {\n\t\t\tr = 1.0 - p;\n\t\t}\n\t\tassert(r > 0.0);\n\n\t\tr = sqrt(-log(r));\n\t\tif (r <= 5.0) {\n\t\t\t/* p neither close to 1/2 nor 0 or 1. */\n\t\t\tr -= 1.6;\n\t\t\tret = ((((((((7.74545014278341407640e-4 * r +\n\t\t\t    2.27238449892691845833e-2) * r +\n\t\t\t    2.41780725177450611770e-1) * r +\n\t\t\t    1.27045825245236838258e0) * r +\n\t\t\t    3.64784832476320460504e0) * r +\n\t\t\t    5.76949722146069140550e0) * r +\n\t\t\t    4.63033784615654529590e0) * r +\n\t\t\t    1.42343711074968357734e0) /\n\t\t\t    (((((((1.05075007164441684324e-9 * r +\n\t\t\t    5.47593808499534494600e-4) * r +\n\t\t\t    1.51986665636164571966e-2)\n\t\t\t    * r + 1.48103976427480074590e-1) * r +\n\t\t\t    6.89767334985100004550e-1) * r +\n\t\t\t    1.67638483018380384940e0) * r +\n\t\t\t    2.05319162663775882187e0) * r + 1.0));\n\t\t} else {\n\t\t\t/* p near 0 or 1. */\n\t\t\tr -= 5.0;\n\t\t\tret = ((((((((2.01033439929228813265e-7 * r +\n\t\t\t    2.71155556874348757815e-5) * r +\n\t\t\t    1.24266094738807843860e-3) * r +\n\t\t\t    2.65321895265761230930e-2) * r +\n\t\t\t    2.96560571828504891230e-1) * r +\n\t\t\t    1.78482653991729133580e0) * r +\n\t\t\t    5.46378491116411436990e0) * r +\n\t\t\t    6.65790464350110377720e0) /\n\t\t\t    (((((((2.04426310338993978564e-15 * r +\n\t\t\t    1.42151175831644588870e-7) * r +\n\t\t\t    1.84631831751005468180e-5) * r +\n\t\t\t    7.86869131145613259100e-4) * r +\n\t\t\t    1.48753612908506148525e-2) * r +\n\t\t\t    1.36929880922735805310e-1) * r +\n\t\t\t    5.99832206555887937690e-1)\n\t\t\t    * r + 1.0));\n\t\t}\n\t\tif (q < 0.0) {\n\t\t\tret = -ret;\n\t\t}\n\t\treturn ret;\n\t}\n}\n\n/*\n * Given a value p in [0..1] of the lower tail area of the Chi^2 distribution\n * with df degrees of freedom, where ln_gamma_df_2 is ln_gamma(df/2.0), compute\n * the upper limit on the definite integral from [0..z] that satisfies p,\n * accurate to 12 decimal places.\n *\n * This implementation is based on:\n *\n *   Best, D.J., D.E. Roberts (1975) Algorithm AS 91: The percentage points of\n *   the Chi^2 distribution.  Applied Statistics 24(3):385-388.\n *\n *   Shea, B.L. (1991) Algorithm AS R85: A remark on AS 91: The percentage\n *   points of the Chi^2 distribution.  Applied Statistics 40(1):233-235.\n */\nstatic inline double\npt_chi2(double p, double df, double ln_gamma_df_2) {\n\tdouble e, aa, xx, c, ch, a, q, p1, p2, t, x, b, s1, s2, s3, s4, s5, s6;\n\tunsigned i;\n\n\tassert(p >= 0.0 && p < 1.0);\n\tassert(df > 0.0);\n\n\te = 5.0e-7;\n\taa = 0.6931471805;\n\n\txx = 0.5 * df;\n\tc = xx - 1.0;\n\n\tif (df < -1.24 * log(p)) {\n\t\t/* Starting approximation for small Chi^2. */\n\t\tch = pow(p * xx * exp(ln_gamma_df_2 + xx * aa), 1.0 / xx);\n\t\tif (ch - e < 0.0) {\n\t\t\treturn ch;\n\t\t}\n\t} else {\n\t\tif (df > 0.32) {\n\t\t\tx = pt_norm(p);\n\t\t\t/*\n\t\t\t * Starting approximation using Wilson and Hilferty\n\t\t\t * estimate.\n\t\t\t */\n\t\t\tp1 = 0.222222 / df;\n\t\t\tch = df * pow(x * sqrt(p1) + 1.0 - p1, 3.0);\n\t\t\t/* Starting approximation for p tending to 1. */\n\t\t\tif (ch > 2.2 * df + 6.0) {\n\t\t\t\tch = -2.0 * (log(1.0 - p) - c * log(0.5 * ch) +\n\t\t\t\t    ln_gamma_df_2);\n\t\t\t}\n\t\t} else {\n\t\t\tch = 0.4;\n\t\t\ta = log(1.0 - p);\n\t\t\twhile (true) {\n\t\t\t\tq = ch;\n\t\t\t\tp1 = 1.0 + ch * (4.67 + ch);\n\t\t\t\tp2 = ch * (6.73 + ch * (6.66 + ch));\n\t\t\t\tt = -0.5 + (4.67 + 2.0 * ch) / p1 - (6.73 + ch\n\t\t\t\t    * (13.32 + 3.0 * ch)) / p2;\n\t\t\t\tch -= (1.0 - exp(a + ln_gamma_df_2 + 0.5 * ch +\n\t\t\t\t    c * aa) * p2 / p1) / t;\n\t\t\t\tif (fabs(q / ch - 1.0) - 0.01 <= 0.0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0; i < 20; i++) {\n\t\t/* Calculation of seven-term Taylor series. */\n\t\tq = ch;\n\t\tp1 = 0.5 * ch;\n\t\tif (p1 < 0.0) {\n\t\t\treturn -1.0;\n\t\t}\n\t\tp2 = p - i_gamma(p1, xx, ln_gamma_df_2);\n\t\tt = p2 * exp(xx * aa + ln_gamma_df_2 + p1 - c * log(ch));\n\t\tb = t / ch;\n\t\ta = 0.5 * t - b * c;\n\t\ts1 = (210.0 + a * (140.0 + a * (105.0 + a * (84.0 + a * (70.0 +\n\t\t    60.0 * a))))) / 420.0;\n\t\ts2 = (420.0 + a * (735.0 + a * (966.0 + a * (1141.0 + 1278.0 *\n\t\t    a)))) / 2520.0;\n\t\ts3 = (210.0 + a * (462.0 + a * (707.0 + 932.0 * a))) / 2520.0;\n\t\ts4 = (252.0 + a * (672.0 + 1182.0 * a) + c * (294.0 + a *\n\t\t    (889.0 + 1740.0 * a))) / 5040.0;\n\t\ts5 = (84.0 + 264.0 * a + c * (175.0 + 606.0 * a)) / 2520.0;\n\t\ts6 = (120.0 + c * (346.0 + 127.0 * c)) / 5040.0;\n\t\tch += t * (1.0 + 0.5 * t * s1 - b * c * (s1 - b * (s2 - b * (s3\n\t\t    - b * (s4 - b * (s5 - b * s6))))));\n\t\tif (fabs(q / ch - 1.0) <= e) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn ch;\n}\n\n/*\n * Given a value p in [0..1] and Gamma distribution shape and scale parameters,\n * compute the upper limit on the definite integral from [0..z] that satisfies\n * p.\n */\nstatic inline double\npt_gamma(double p, double shape, double scale, double ln_gamma_shape) {\n\treturn pt_chi2(p, shape * 2.0, ln_gamma_shape) * 0.5 * scale;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/mq.h",
    "content": "void\tmq_nanosleep(unsigned ns);\n\n/*\n * Simple templated message queue implementation that relies on only mutexes for\n * synchronization (which reduces portability issues).  Given the following\n * setup:\n *\n *   typedef struct mq_msg_s mq_msg_t;\n *   struct mq_msg_s {\n *           mq_msg(mq_msg_t) link;\n *           [message data]\n *   };\n *   mq_gen(, mq_, mq_t, mq_msg_t, link)\n *\n * The API is as follows:\n *\n *   bool mq_init(mq_t *mq);\n *   void mq_fini(mq_t *mq);\n *   unsigned mq_count(mq_t *mq);\n *   mq_msg_t *mq_tryget(mq_t *mq);\n *   mq_msg_t *mq_get(mq_t *mq);\n *   void mq_put(mq_t *mq, mq_msg_t *msg);\n *\n * The message queue linkage embedded in each message is to be treated as\n * externally opaque (no need to initialize or clean up externally).  mq_fini()\n * does not perform any cleanup of messages, since it knows nothing of their\n * payloads.\n */\n#define mq_msg(a_mq_msg_type)\tql_elm(a_mq_msg_type)\n\n#define mq_gen(a_attr, a_prefix, a_mq_type, a_mq_msg_type, a_field)\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tmtx_t\t\t\tlock;\t\t\t\t\t\\\n\tql_head(a_mq_msg_type)\tmsgs;\t\t\t\t\t\\\n\tunsigned\t\tcount;\t\t\t\t\t\\\n} a_mq_type;\t\t\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_prefix##init(a_mq_type *mq) {\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (mtx_init(&mq->lock)) {\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tql_new(&mq->msgs);\t\t\t\t\t\t\\\n\tmq->count = 0;\t\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\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##fini(a_mq_type *mq) {\t\t\t\t\t\t\\\n\tmtx_fini(&mq->lock);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr unsigned\t\t\t\t\t\t\t\t\\\na_prefix##count(a_mq_type *mq) {\t\t\t\t\t\\\n\tunsigned count;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmtx_lock(&mq->lock);\t\t\t\t\t\t\\\n\tcount = mq->count;\t\t\t\t\t\t\\\n\tmtx_unlock(&mq->lock);\t\t\t\t\t\t\\\n\treturn count;\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_mq_msg_type *\t\t\t\t\t\t\t\\\na_prefix##tryget(a_mq_type *mq) {\t\t\t\t\t\\\n\ta_mq_msg_type *msg;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmtx_lock(&mq->lock);\t\t\t\t\t\t\\\n\tmsg = ql_first(&mq->msgs);\t\t\t\t\t\\\n\tif (msg != NULL) {\t\t\t\t\t\t\\\n\t\tql_head_remove(&mq->msgs, a_mq_msg_type, a_field);\t\\\n\t\tmq->count--;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tmtx_unlock(&mq->lock);\t\t\t\t\t\t\\\n\treturn msg;\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_mq_msg_type *\t\t\t\t\t\t\t\\\na_prefix##get(a_mq_type *mq) {\t\t\t\t\t\t\\\n\ta_mq_msg_type *msg;\t\t\t\t\t\t\\\n\tunsigned ns;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmsg = a_prefix##tryget(mq);\t\t\t\t\t\\\n\tif (msg != NULL) {\t\t\t\t\t\t\\\n\t\treturn msg;\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\tns = 1;\t\t\t\t\t\t\t\t\\\n\twhile (true) {\t\t\t\t\t\t\t\\\n\t\tmq_nanosleep(ns);\t\t\t\t\t\\\n\t\tmsg = a_prefix##tryget(mq);\t\t\t\t\\\n\t\tif (msg != NULL) {\t\t\t\t\t\\\n\t\t\treturn msg;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (ns < 1000*1000*1000) {\t\t\t\t\\\n\t\t\t/* Double sleep time, up to max 1 second. */\t\\\n\t\t\tns <<= 1;\t\t\t\t\t\\\n\t\t\tif (ns > 1000*1000*1000) {\t\t\t\\\n\t\t\t\tns = 1000*1000*1000;\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\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##put(a_mq_type *mq, a_mq_msg_type *msg) {\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmtx_lock(&mq->lock);\t\t\t\t\t\t\\\n\tql_elm_new(msg, a_field);\t\t\t\t\t\\\n\tql_tail_insert(&mq->msgs, msg, a_field);\t\t\t\\\n\tmq->count++;\t\t\t\t\t\t\t\\\n\tmtx_unlock(&mq->lock);\t\t\t\t\t\t\\\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/mtx.h",
    "content": "/*\n * mtx is a slightly simplified version of malloc_mutex.  This code duplication\n * is unfortunate, but there are allocator bootstrapping considerations that\n * would leak into the test infrastructure if malloc_mutex were used directly\n * in tests.\n */\n\ntypedef struct {\n#ifdef _WIN32\n\tCRITICAL_SECTION\tlock;\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tos_unfair_lock\t\tlock;\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLock\t\tlock;\n#else\n\tpthread_mutex_t\t\tlock;\n#endif\n} mtx_t;\n\nbool\tmtx_init(mtx_t *mtx);\nvoid\tmtx_fini(mtx_t *mtx);\nvoid\tmtx_lock(mtx_t *mtx);\nvoid\tmtx_unlock(mtx_t *mtx);\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/test.h",
    "content": "#define ASSERT_BUFSIZE\t256\n\n#define assert_cmp(t, a, b, cmp, neg_cmp, pri, ...) do {\t\t\\\n\tt a_ = (a);\t\t\t\t\t\t\t\\\n\tt b_ = (b);\t\t\t\t\t\t\t\\\n\tif (!(a_ cmp b_)) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) \" #cmp \" (%s) --> \"\t\t\t\t\\\n\t\t    \"%\" pri \" \" #neg_cmp \" %\" pri \": \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__,\t\t\t\\\n\t\t    #a, #b, a_, b_);\t\t\t\t\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define assert_ptr_eq(a, b, ...)\tassert_cmp(void *, a, b, ==,\t\\\n    !=, \"p\", __VA_ARGS__)\n#define assert_ptr_ne(a, b, ...)\tassert_cmp(void *, a, b, !=,\t\\\n    ==, \"p\", __VA_ARGS__)\n#define assert_ptr_null(a, ...)\t\tassert_cmp(void *, a, NULL, ==,\t\\\n    !=, \"p\", __VA_ARGS__)\n#define assert_ptr_not_null(a, ...)\tassert_cmp(void *, a, NULL, !=,\t\\\n    ==, \"p\", __VA_ARGS__)\n\n#define assert_c_eq(a, b, ...)\tassert_cmp(char, a, b, ==, !=, \"c\", __VA_ARGS__)\n#define assert_c_ne(a, b, ...)\tassert_cmp(char, a, b, !=, ==, \"c\", __VA_ARGS__)\n#define assert_c_lt(a, b, ...)\tassert_cmp(char, a, b, <, >=, \"c\", __VA_ARGS__)\n#define assert_c_le(a, b, ...)\tassert_cmp(char, a, b, <=, >, \"c\", __VA_ARGS__)\n#define assert_c_ge(a, b, ...)\tassert_cmp(char, a, b, >=, <, \"c\", __VA_ARGS__)\n#define assert_c_gt(a, b, ...)\tassert_cmp(char, a, b, >, <=, \"c\", __VA_ARGS__)\n\n#define assert_x_eq(a, b, ...)\tassert_cmp(int, a, b, ==, !=, \"#x\", __VA_ARGS__)\n#define assert_x_ne(a, b, ...)\tassert_cmp(int, a, b, !=, ==, \"#x\", __VA_ARGS__)\n#define assert_x_lt(a, b, ...)\tassert_cmp(int, a, b, <, >=, \"#x\", __VA_ARGS__)\n#define assert_x_le(a, b, ...)\tassert_cmp(int, a, b, <=, >, \"#x\", __VA_ARGS__)\n#define assert_x_ge(a, b, ...)\tassert_cmp(int, a, b, >=, <, \"#x\", __VA_ARGS__)\n#define assert_x_gt(a, b, ...)\tassert_cmp(int, a, b, >, <=, \"#x\", __VA_ARGS__)\n\n#define assert_d_eq(a, b, ...)\tassert_cmp(int, a, b, ==, !=, \"d\", __VA_ARGS__)\n#define assert_d_ne(a, b, ...)\tassert_cmp(int, a, b, !=, ==, \"d\", __VA_ARGS__)\n#define assert_d_lt(a, b, ...)\tassert_cmp(int, a, b, <, >=, \"d\", __VA_ARGS__)\n#define assert_d_le(a, b, ...)\tassert_cmp(int, a, b, <=, >, \"d\", __VA_ARGS__)\n#define assert_d_ge(a, b, ...)\tassert_cmp(int, a, b, >=, <, \"d\", __VA_ARGS__)\n#define assert_d_gt(a, b, ...)\tassert_cmp(int, a, b, >, <=, \"d\", __VA_ARGS__)\n\n#define assert_u_eq(a, b, ...)\tassert_cmp(int, a, b, ==, !=, \"u\", __VA_ARGS__)\n#define assert_u_ne(a, b, ...)\tassert_cmp(int, a, b, !=, ==, \"u\", __VA_ARGS__)\n#define assert_u_lt(a, b, ...)\tassert_cmp(int, a, b, <, >=, \"u\", __VA_ARGS__)\n#define assert_u_le(a, b, ...)\tassert_cmp(int, a, b, <=, >, \"u\", __VA_ARGS__)\n#define assert_u_ge(a, b, ...)\tassert_cmp(int, a, b, >=, <, \"u\", __VA_ARGS__)\n#define assert_u_gt(a, b, ...)\tassert_cmp(int, a, b, >, <=, \"u\", __VA_ARGS__)\n\n#define assert_ld_eq(a, b, ...)\tassert_cmp(long, a, b, ==,\t\\\n    !=, \"ld\", __VA_ARGS__)\n#define assert_ld_ne(a, b, ...)\tassert_cmp(long, a, b, !=,\t\\\n    ==, \"ld\", __VA_ARGS__)\n#define assert_ld_lt(a, b, ...)\tassert_cmp(long, a, b, <,\t\\\n    >=, \"ld\", __VA_ARGS__)\n#define assert_ld_le(a, b, ...)\tassert_cmp(long, a, b, <=,\t\\\n    >, \"ld\", __VA_ARGS__)\n#define assert_ld_ge(a, b, ...)\tassert_cmp(long, a, b, >=,\t\\\n    <, \"ld\", __VA_ARGS__)\n#define assert_ld_gt(a, b, ...)\tassert_cmp(long, a, b, >,\t\\\n    <=, \"ld\", __VA_ARGS__)\n\n#define assert_lu_eq(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, ==, !=, \"lu\", __VA_ARGS__)\n#define assert_lu_ne(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, !=, ==, \"lu\", __VA_ARGS__)\n#define assert_lu_lt(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, <, >=, \"lu\", __VA_ARGS__)\n#define assert_lu_le(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, <=, >, \"lu\", __VA_ARGS__)\n#define assert_lu_ge(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, >=, <, \"lu\", __VA_ARGS__)\n#define assert_lu_gt(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, >, <=, \"lu\", __VA_ARGS__)\n\n#define assert_qd_eq(a, b, ...)\tassert_cmp(long long, a, b, ==,\t\\\n    !=, \"qd\", __VA_ARGS__)\n#define assert_qd_ne(a, b, ...)\tassert_cmp(long long, a, b, !=,\t\\\n    ==, \"qd\", __VA_ARGS__)\n#define assert_qd_lt(a, b, ...)\tassert_cmp(long long, a, b, <,\t\\\n    >=, \"qd\", __VA_ARGS__)\n#define assert_qd_le(a, b, ...)\tassert_cmp(long long, a, b, <=,\t\\\n    >, \"qd\", __VA_ARGS__)\n#define assert_qd_ge(a, b, ...)\tassert_cmp(long long, a, b, >=,\t\\\n    <, \"qd\", __VA_ARGS__)\n#define assert_qd_gt(a, b, ...)\tassert_cmp(long long, a, b, >,\t\\\n    <=, \"qd\", __VA_ARGS__)\n\n#define assert_qu_eq(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, ==, !=, \"qu\", __VA_ARGS__)\n#define assert_qu_ne(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, !=, ==, \"qu\", __VA_ARGS__)\n#define assert_qu_lt(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, <, >=, \"qu\", __VA_ARGS__)\n#define assert_qu_le(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, <=, >, \"qu\", __VA_ARGS__)\n#define assert_qu_ge(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, >=, <, \"qu\", __VA_ARGS__)\n#define assert_qu_gt(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, >, <=, \"qu\", __VA_ARGS__)\n\n#define assert_jd_eq(a, b, ...)\tassert_cmp(intmax_t, a, b, ==,\t\\\n    !=, \"jd\", __VA_ARGS__)\n#define assert_jd_ne(a, b, ...)\tassert_cmp(intmax_t, a, b, !=,\t\\\n    ==, \"jd\", __VA_ARGS__)\n#define assert_jd_lt(a, b, ...)\tassert_cmp(intmax_t, a, b, <,\t\\\n    >=, \"jd\", __VA_ARGS__)\n#define assert_jd_le(a, b, ...)\tassert_cmp(intmax_t, a, b, <=,\t\\\n    >, \"jd\", __VA_ARGS__)\n#define assert_jd_ge(a, b, ...)\tassert_cmp(intmax_t, a, b, >=,\t\\\n    <, \"jd\", __VA_ARGS__)\n#define assert_jd_gt(a, b, ...)\tassert_cmp(intmax_t, a, b, >,\t\\\n    <=, \"jd\", __VA_ARGS__)\n\n#define assert_ju_eq(a, b, ...)\tassert_cmp(uintmax_t, a, b, ==,\t\\\n    !=, \"ju\", __VA_ARGS__)\n#define assert_ju_ne(a, b, ...)\tassert_cmp(uintmax_t, a, b, !=,\t\\\n    ==, \"ju\", __VA_ARGS__)\n#define assert_ju_lt(a, b, ...)\tassert_cmp(uintmax_t, a, b, <,\t\\\n    >=, \"ju\", __VA_ARGS__)\n#define assert_ju_le(a, b, ...)\tassert_cmp(uintmax_t, a, b, <=,\t\\\n    >, \"ju\", __VA_ARGS__)\n#define assert_ju_ge(a, b, ...)\tassert_cmp(uintmax_t, a, b, >=,\t\\\n    <, \"ju\", __VA_ARGS__)\n#define assert_ju_gt(a, b, ...)\tassert_cmp(uintmax_t, a, b, >,\t\\\n    <=, \"ju\", __VA_ARGS__)\n\n#define assert_zd_eq(a, b, ...)\tassert_cmp(ssize_t, a, b, ==,\t\\\n    !=, \"zd\", __VA_ARGS__)\n#define assert_zd_ne(a, b, ...)\tassert_cmp(ssize_t, a, b, !=,\t\\\n    ==, \"zd\", __VA_ARGS__)\n#define assert_zd_lt(a, b, ...)\tassert_cmp(ssize_t, a, b, <,\t\\\n    >=, \"zd\", __VA_ARGS__)\n#define assert_zd_le(a, b, ...)\tassert_cmp(ssize_t, a, b, <=,\t\\\n    >, \"zd\", __VA_ARGS__)\n#define assert_zd_ge(a, b, ...)\tassert_cmp(ssize_t, a, b, >=,\t\\\n    <, \"zd\", __VA_ARGS__)\n#define assert_zd_gt(a, b, ...)\tassert_cmp(ssize_t, a, b, >,\t\\\n    <=, \"zd\", __VA_ARGS__)\n\n#define assert_zu_eq(a, b, ...)\tassert_cmp(size_t, a, b, ==,\t\\\n    !=, \"zu\", __VA_ARGS__)\n#define assert_zu_ne(a, b, ...)\tassert_cmp(size_t, a, b, !=,\t\\\n    ==, \"zu\", __VA_ARGS__)\n#define assert_zu_lt(a, b, ...)\tassert_cmp(size_t, a, b, <,\t\\\n    >=, \"zu\", __VA_ARGS__)\n#define assert_zu_le(a, b, ...)\tassert_cmp(size_t, a, b, <=,\t\\\n    >, \"zu\", __VA_ARGS__)\n#define assert_zu_ge(a, b, ...)\tassert_cmp(size_t, a, b, >=,\t\\\n    <, \"zu\", __VA_ARGS__)\n#define assert_zu_gt(a, b, ...)\tassert_cmp(size_t, a, b, >,\t\\\n    <=, \"zu\", __VA_ARGS__)\n\n#define assert_d32_eq(a, b, ...)\tassert_cmp(int32_t, a, b, ==,\t\\\n    !=, FMTd32, __VA_ARGS__)\n#define assert_d32_ne(a, b, ...)\tassert_cmp(int32_t, a, b, !=,\t\\\n    ==, FMTd32, __VA_ARGS__)\n#define assert_d32_lt(a, b, ...)\tassert_cmp(int32_t, a, b, <,\t\\\n    >=, FMTd32, __VA_ARGS__)\n#define assert_d32_le(a, b, ...)\tassert_cmp(int32_t, a, b, <=,\t\\\n    >, FMTd32, __VA_ARGS__)\n#define assert_d32_ge(a, b, ...)\tassert_cmp(int32_t, a, b, >=,\t\\\n    <, FMTd32, __VA_ARGS__)\n#define assert_d32_gt(a, b, ...)\tassert_cmp(int32_t, a, b, >,\t\\\n    <=, FMTd32, __VA_ARGS__)\n\n#define assert_u32_eq(a, b, ...)\tassert_cmp(uint32_t, a, b, ==,\t\\\n    !=, FMTu32, __VA_ARGS__)\n#define assert_u32_ne(a, b, ...)\tassert_cmp(uint32_t, a, b, !=,\t\\\n    ==, FMTu32, __VA_ARGS__)\n#define assert_u32_lt(a, b, ...)\tassert_cmp(uint32_t, a, b, <,\t\\\n    >=, FMTu32, __VA_ARGS__)\n#define assert_u32_le(a, b, ...)\tassert_cmp(uint32_t, a, b, <=,\t\\\n    >, FMTu32, __VA_ARGS__)\n#define assert_u32_ge(a, b, ...)\tassert_cmp(uint32_t, a, b, >=,\t\\\n    <, FMTu32, __VA_ARGS__)\n#define assert_u32_gt(a, b, ...)\tassert_cmp(uint32_t, a, b, >,\t\\\n    <=, FMTu32, __VA_ARGS__)\n\n#define assert_d64_eq(a, b, ...)\tassert_cmp(int64_t, a, b, ==,\t\\\n    !=, FMTd64, __VA_ARGS__)\n#define assert_d64_ne(a, b, ...)\tassert_cmp(int64_t, a, b, !=,\t\\\n    ==, FMTd64, __VA_ARGS__)\n#define assert_d64_lt(a, b, ...)\tassert_cmp(int64_t, a, b, <,\t\\\n    >=, FMTd64, __VA_ARGS__)\n#define assert_d64_le(a, b, ...)\tassert_cmp(int64_t, a, b, <=,\t\\\n    >, FMTd64, __VA_ARGS__)\n#define assert_d64_ge(a, b, ...)\tassert_cmp(int64_t, a, b, >=,\t\\\n    <, FMTd64, __VA_ARGS__)\n#define assert_d64_gt(a, b, ...)\tassert_cmp(int64_t, a, b, >,\t\\\n    <=, FMTd64, __VA_ARGS__)\n\n#define assert_u64_eq(a, b, ...)\tassert_cmp(uint64_t, a, b, ==,\t\\\n    !=, FMTu64, __VA_ARGS__)\n#define assert_u64_ne(a, b, ...)\tassert_cmp(uint64_t, a, b, !=,\t\\\n    ==, FMTu64, __VA_ARGS__)\n#define assert_u64_lt(a, b, ...)\tassert_cmp(uint64_t, a, b, <,\t\\\n    >=, FMTu64, __VA_ARGS__)\n#define assert_u64_le(a, b, ...)\tassert_cmp(uint64_t, a, b, <=,\t\\\n    >, FMTu64, __VA_ARGS__)\n#define assert_u64_ge(a, b, ...)\tassert_cmp(uint64_t, a, b, >=,\t\\\n    <, FMTu64, __VA_ARGS__)\n#define assert_u64_gt(a, b, ...)\tassert_cmp(uint64_t, a, b, >,\t\\\n    <=, FMTu64, __VA_ARGS__)\n\n#define assert_b_eq(a, b, ...) do {\t\t\t\t\t\\\n\tbool a_ = (a);\t\t\t\t\t\t\t\\\n\tbool b_ = (b);\t\t\t\t\t\t\t\\\n\tif (!(a_ == b_)) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) == (%s) --> %s != %s: \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__,\t\t\t\\\n\t\t    #a, #b, a_ ? \"true\" : \"false\",\t\t\t\\\n\t\t    b_ ? \"true\" : \"false\");\t\t\t\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define assert_b_ne(a, b, ...) do {\t\t\t\t\t\\\n\tbool a_ = (a);\t\t\t\t\t\t\t\\\n\tbool b_ = (b);\t\t\t\t\t\t\t\\\n\tif (!(a_ != b_)) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) != (%s) --> %s == %s: \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__,\t\t\t\\\n\t\t    #a, #b, a_ ? \"true\" : \"false\",\t\t\t\\\n\t\t    b_ ? \"true\" : \"false\");\t\t\t\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define assert_true(a, ...)\tassert_b_eq(a, true, __VA_ARGS__)\n#define assert_false(a, ...)\tassert_b_eq(a, false, __VA_ARGS__)\n\n#define assert_str_eq(a, b, ...) do {\t\t\t\t\\\n\tif (strcmp((a), (b))) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) same as (%s) --> \"\t\t\t\t\\\n\t\t    \"\\\"%s\\\" differs from \\\"%s\\\": \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__, #a, #b, a, b);\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define assert_str_ne(a, b, ...) do {\t\t\t\t\\\n\tif (!strcmp((a), (b))) {\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) differs from (%s) --> \"\t\t\t\\\n\t\t    \"\\\"%s\\\" same as \\\"%s\\\": \",\t\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__, #a, #b, a, b);\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define assert_not_reached(...) do {\t\t\t\t\t\\\n\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\t\\\n\tchar message[ASSERT_BUFSIZE];\t\t\t\t\t\\\n\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\t\\\n\t    \"%s:%s:%d: Unreachable code reached: \",\t\t\t\\\n\t    __func__, __FILE__, __LINE__);\t\t\t\t\\\n\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\t\\\n\tp_test_fail(prefix, message);\t\t\t\t\t\\\n} while (0)\n\n/*\n * If this enum changes, corresponding changes in test/test.sh.in are also\n * necessary.\n */\ntypedef enum {\n\ttest_status_pass = 0,\n\ttest_status_skip = 1,\n\ttest_status_fail = 2,\n\n\ttest_status_count = 3\n} test_status_t;\n\ntypedef void (test_t)(void);\n\n#define TEST_BEGIN(f)\t\t\t\t\t\t\t\\\nstatic void\t\t\t\t\t\t\t\t\\\nf(void) {\t\t\t\t\t\t\t\t\\\n\tp_test_init(#f);\n\n#define TEST_END\t\t\t\t\t\t\t\\\n\tgoto label_test_end;\t\t\t\t\t\t\\\nlabel_test_end:\t\t\t\t\t\t\t\t\\\n\tp_test_fini();\t\t\t\t\t\t\t\\\n}\n\n#define test(...)\t\t\t\t\t\t\t\\\n\tp_test(__VA_ARGS__, NULL)\n\n#define test_no_reentrancy(...)\t\t\t\t\t\t\t\\\n\tp_test_no_reentrancy(__VA_ARGS__, NULL)\n\n#define test_no_malloc_init(...)\t\t\t\t\t\\\n\tp_test_no_malloc_init(__VA_ARGS__, NULL)\n\n#define test_skip_if(e) do {\t\t\t\t\t\t\\\n\tif (e) {\t\t\t\t\t\t\t\\\n\t\ttest_skip(\"%s:%s:%d: Test skipped: (%s)\",\t\t\\\n\t\t    __func__, __FILE__, __LINE__, #e);\t\t\t\\\n\t\tgoto label_test_end;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\nbool test_is_reentrant();\n\nvoid\ttest_skip(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);\nvoid\ttest_fail(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);\n\n/* For private use by macros. */\ntest_status_t\tp_test(test_t *t, ...);\ntest_status_t\tp_test_no_reentrancy(test_t *t, ...);\ntest_status_t\tp_test_no_malloc_init(test_t *t, ...);\nvoid\tp_test_init(const char *name);\nvoid\tp_test_fini(void);\nvoid\tp_test_fail(const char *prefix, const char *message);\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/thd.h",
    "content": "/* Abstraction layer for threading in tests. */\n#ifdef _WIN32\ntypedef HANDLE thd_t;\n#else\ntypedef pthread_t thd_t;\n#endif\n\nvoid\tthd_create(thd_t *thd, void *(*proc)(void *), void *arg);\nvoid\tthd_join(thd_t thd, void **ret);\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/include/test/timer.h",
    "content": "/* Simple timer, for use in benchmark reporting. */\n\ntypedef struct {\n\tnstime_t t0;\n\tnstime_t t1;\n} timedelta_t;\n\nvoid\ttimer_start(timedelta_t *timer);\nvoid\ttimer_stop(timedelta_t *timer);\nuint64_t\ttimer_usec(const timedelta_t *timer);\nvoid\ttimer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen);\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/MALLOCX_ARENA.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS 10\n\nstatic bool have_dss =\n#ifdef JEMALLOC_DSS\n    true\n#else\n    false\n#endif\n    ;\n\nvoid *\nthd_start(void *arg) {\n\tunsigned thread_ind = (unsigned)(uintptr_t)arg;\n\tunsigned arena_ind;\n\tvoid *p;\n\tsize_t sz;\n\n\tsz = sizeof(arena_ind);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Error in arenas.create\");\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\tunsigned prec_ind = thread_ind %\n\t\t    (sizeof(dss_precs)/sizeof(char*));\n\t\tconst char *dss = dss_precs[prec_ind];\n\t\tint expected_err = (have_dss || prec_ind == 0) ? 0 : EFAULT;\n\t\tassert_d_eq(mallctlnametomib(\"arena.0.dss\", mib, &miblen), 0,\n\t\t    \"Error in mallctlnametomib()\");\n\t\tmib[1] = arena_ind;\n\t\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&dss,\n\t\t    sizeof(const char *)), expected_err,\n\t\t    \"Error in mallctlbymib()\");\n\t}\n\n\tp = mallocx(1, MALLOCX_ARENA(arena_ind));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tdallocx(p, 0);\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_MALLOCX_ARENA) {\n\tthd_t thds[NTHREADS];\n\tunsigned i;\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_create(&thds[i], thd_start,\n\t\t    (void *)(uintptr_t)i);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_MALLOCX_ARENA);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/aligned_alloc.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAXALIGN (((size_t)1) << 23)\n\n/*\n * On systems which can't merge extents, tests that call this function generate\n * a lot of dirty memory very quickly.  Purging between cycles mitigates\n * potential OOM on e.g. 32-bit Windows.\n */\nstatic void\npurge(void) {\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl error\");\n}\n\nTEST_BEGIN(test_alignment_errors) {\n\tsize_t alignment;\n\tvoid *p;\n\n\talignment = 0;\n\tset_errno(0);\n\tp = aligned_alloc(alignment, 1);\n\tassert_false(p != NULL || get_errno() != EINVAL,\n\t    \"Expected error for invalid alignment %zu\", alignment);\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\tassert_false(p != NULL || get_errno() != EINVAL,\n\t\t    \"Expected error for invalid alignment %zu\",\n\t\t    alignment + 1);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_oom_errors) {\n\tsize_t alignment, size;\n\tvoid *p;\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\tassert_false(p != NULL || get_errno() != ENOMEM,\n\t    \"Expected error for aligned_alloc(%zu, %zu)\",\n\t    alignment, size);\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsize      = UINT64_C(0xc000000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsize      = 0xc0000001LU;\n#endif\n\tset_errno(0);\n\tp = aligned_alloc(alignment, size);\n\tassert_false(p != NULL || get_errno() != ENOMEM,\n\t    \"Expected error for aligned_alloc(%zu, %zu)\",\n\t    alignment, size);\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\tassert_false(p != NULL || get_errno() != ENOMEM,\n\t    \"Expected error for aligned_alloc(&p, %zu, %zu)\",\n\t    alignment, size);\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n#define NITER 4\n\tsize_t alignment, size, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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(get_errno(), buf, sizeof(buf));\n\t\t\t\t\ttest_fail(\n\t\t\t\t\t    \"Error for alignment=%zu, \"\n\t\t\t\t\t    \"size=%zu (%#zx): %s\",\n\t\t\t\t\t    alignment, size, size, buf);\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\t}\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\tpurge();\n\t}\n#undef NITER\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_alignment_errors,\n\t    test_oom_errors,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/allocated.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic const bool config_stats =\n#ifdef JEMALLOC_STATS\n    true\n#else\n    false\n#endif\n    ;\n\nvoid *\nthd_start(void *arg) {\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\", (void *)&a0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tsz = sizeof(ap0);\n\tif ((err = mallctl(\"thread.allocatedp\", (void *)&ap0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tassert_u64_eq(*ap0, a0,\n\t    \"\\\"thread.allocatedp\\\" should provide a pointer to internal \"\n\t    \"storage\");\n\n\tsz = sizeof(d0);\n\tif ((err = mallctl(\"thread.deallocated\", (void *)&d0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tsz = sizeof(dp0);\n\tif ((err = mallctl(\"thread.deallocatedp\", (void *)&dp0, &sz, NULL,\n\t    0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tassert_u64_eq(*dp0, d0,\n\t    \"\\\"thread.deallocatedp\\\" should provide a pointer to internal \"\n\t    \"storage\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() error\");\n\n\tsz = sizeof(a1);\n\tmallctl(\"thread.allocated\", (void *)&a1, &sz, NULL, 0);\n\tsz = sizeof(ap1);\n\tmallctl(\"thread.allocatedp\", (void *)&ap1, &sz, NULL, 0);\n\tassert_u64_eq(*ap1, a1,\n\t    \"Dereferenced \\\"thread.allocatedp\\\" value should equal \"\n\t    \"\\\"thread.allocated\\\" value\");\n\tassert_ptr_eq(ap0, ap1,\n\t    \"Pointer returned by \\\"thread.allocatedp\\\" should not change\");\n\n\tusize = malloc_usable_size(p);\n\tassert_u64_le(a0 + usize, a1,\n\t    \"Allocated memory counter should increase by at least the amount \"\n\t    \"explicitly allocated\");\n\n\tfree(p);\n\n\tsz = sizeof(d1);\n\tmallctl(\"thread.deallocated\", (void *)&d1, &sz, NULL, 0);\n\tsz = sizeof(dp1);\n\tmallctl(\"thread.deallocatedp\", (void *)&dp1, &sz, NULL, 0);\n\tassert_u64_eq(*dp1, d1,\n\t    \"Dereferenced \\\"thread.deallocatedp\\\" value should equal \"\n\t    \"\\\"thread.deallocated\\\" value\");\n\tassert_ptr_eq(dp0, dp1,\n\t    \"Pointer returned by \\\"thread.deallocatedp\\\" should not change\");\n\n\tassert_u64_le(d0 + usize, d1,\n\t    \"Deallocated memory counter should increase by at least the amount \"\n\t    \"explicitly deallocated\");\n\n\treturn NULL;\nlabel_ENOENT:\n\tassert_false(config_stats,\n\t    \"ENOENT should only be returned if stats are disabled\");\n\ttest_skip(\"\\\"thread.allocated\\\" mallctl not available\");\n\treturn NULL;\n}\n\nTEST_BEGIN(test_main_thread) {\n\tthd_start(NULL);\n}\nTEST_END\n\nTEST_BEGIN(test_subthread) {\n\tthd_t thd;\n\n\tthd_create(&thd, thd_start, NULL);\n\tthd_join(thd, NULL);\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Run tests multiple times to check for bad interactions. */\n\treturn test(\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/extent.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"test/extent_hooks.h\"\n\nstatic bool\ncheck_background_thread_enabled(void) {\n\tbool enabled;\n\tsize_t sz = sizeof(bool);\n\tint ret = mallctl(\"background_thread\", (void *)&enabled, &sz, NULL,0);\n\tif (ret == ENOENT) {\n\t\treturn false;\n\t}\n\tassert_d_eq(ret, 0, \"Unexpected mallctl error\");\n\treturn enabled;\n}\n\nstatic void\ntest_extent_body(unsigned arena_ind) {\n\tvoid *p;\n\tsize_t large0, large1, large2, sz;\n\tsize_t purge_mib[3];\n\tsize_t purge_miblen;\n\tint flags;\n\tbool xallocx_success_a, xallocx_success_b, xallocx_success_c;\n\n\tflags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\n\t/* Get large size classes. */\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&large0, &sz, NULL,\n\t    0), 0, \"Unexpected arenas.lextent.0.size failure\");\n\tassert_d_eq(mallctl(\"arenas.lextent.1.size\", (void *)&large1, &sz, NULL,\n\t    0), 0, \"Unexpected arenas.lextent.1.size failure\");\n\tassert_d_eq(mallctl(\"arenas.lextent.2.size\", (void *)&large2, &sz, NULL,\n\t    0), 0, \"Unexpected arenas.lextent.2.size failure\");\n\n\t/* Test dalloc/decommit/purge cascade. */\n\tpurge_miblen = sizeof(purge_mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.purge\", purge_mib, &purge_miblen),\n\t    0, \"Unexpected mallctlnametomib() failure\");\n\tpurge_mib[1] = (size_t)arena_ind;\n\tcalled_alloc = false;\n\ttry_alloc = true;\n\ttry_dalloc = false;\n\ttry_decommit = false;\n\tp = mallocx(large0 * 2, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tassert_true(called_alloc, \"Expected alloc call\");\n\tcalled_dalloc = false;\n\tcalled_decommit = false;\n\tdid_purge_lazy = false;\n\tdid_purge_forced = false;\n\tcalled_split = false;\n\txallocx_success_a = (xallocx(p, large0, 0, flags) == large0);\n\tassert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),\n\t    0, \"Unexpected arena.%u.purge error\", arena_ind);\n\tif (xallocx_success_a) {\n\t\tassert_true(called_dalloc, \"Expected dalloc call\");\n\t\tassert_true(called_decommit, \"Expected decommit call\");\n\t\tassert_true(did_purge_lazy || did_purge_forced,\n\t\t    \"Expected purge\");\n\t}\n\tassert_true(called_split, \"Expected split call\");\n\tdallocx(p, flags);\n\ttry_dalloc = true;\n\n\t/* Test decommit/commit and observe split/merge. */\n\ttry_dalloc = false;\n\ttry_decommit = true;\n\tp = mallocx(large0 * 2, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tdid_decommit = false;\n\tdid_commit = false;\n\tcalled_split = false;\n\tdid_split = false;\n\tdid_merge = false;\n\txallocx_success_b = (xallocx(p, large0, 0, flags) == large0);\n\tassert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),\n\t    0, \"Unexpected arena.%u.purge error\", arena_ind);\n\tif (xallocx_success_b) {\n\t\tassert_true(did_split, \"Expected split\");\n\t}\n\txallocx_success_c = (xallocx(p, large0 * 2, 0, flags) == large0 * 2);\n\tif (did_split) {\n\t\tassert_b_eq(did_decommit, did_commit,\n\t\t    \"Expected decommit/commit match\");\n\t}\n\tif (xallocx_success_b && xallocx_success_c) {\n\t\tassert_true(did_merge, \"Expected merge\");\n\t}\n\tdallocx(p, flags);\n\ttry_dalloc = true;\n\ttry_decommit = false;\n\n\t/* Make sure non-large allocation succeeds. */\n\tp = mallocx(42, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tdallocx(p, flags);\n}\n\nstatic void\ntest_manual_hook_auto_arena(void) {\n\tunsigned narenas;\n\tsize_t old_size, new_size, sz;\n\tsize_t hooks_mib[3];\n\tsize_t hooks_miblen;\n\textent_hooks_t *new_hooks, *old_hooks;\n\n\textent_hooks_prep();\n\n\tsz = sizeof(unsigned);\n\t/* Get number of auto arenas. */\n\tassert_d_eq(mallctl(\"opt.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tif (narenas == 1) {\n\t\treturn;\n\t}\n\n\t/* Install custom extent hooks on arena 1 (might not be initialized). */\n\thooks_miblen = sizeof(hooks_mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.extent_hooks\", hooks_mib,\n\t    &hooks_miblen), 0, \"Unexpected mallctlnametomib() failure\");\n\thooks_mib[1] = 1;\n\told_size = sizeof(extent_hooks_t *);\n\tnew_hooks = &hooks;\n\tnew_size = sizeof(extent_hooks_t *);\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,\n\t    &old_size, (void *)&new_hooks, new_size), 0,\n\t    \"Unexpected extent_hooks error\");\n\tstatic bool auto_arena_created = false;\n\tif (old_hooks != &hooks) {\n\t\tassert_b_eq(auto_arena_created, false,\n\t\t    \"Expected auto arena 1 created only once.\");\n\t\tauto_arena_created = true;\n\t}\n}\n\nstatic void\ntest_manual_hook_body(void) {\n\tunsigned arena_ind;\n\tsize_t old_size, new_size, sz;\n\tsize_t hooks_mib[3];\n\tsize_t hooks_miblen;\n\textent_hooks_t *new_hooks, *old_hooks;\n\n\textent_hooks_prep();\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\n\t/* Install custom extent hooks. */\n\thooks_miblen = sizeof(hooks_mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.extent_hooks\", hooks_mib,\n\t    &hooks_miblen), 0, \"Unexpected mallctlnametomib() failure\");\n\thooks_mib[1] = (size_t)arena_ind;\n\told_size = sizeof(extent_hooks_t *);\n\tnew_hooks = &hooks;\n\tnew_size = sizeof(extent_hooks_t *);\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,\n\t    &old_size, (void *)&new_hooks, new_size), 0,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->alloc, extent_alloc_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->dalloc, extent_dalloc_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->commit, extent_commit_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->decommit, extent_decommit_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->purge_lazy, extent_purge_lazy_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->purge_forced, extent_purge_forced_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->split, extent_split_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->merge, extent_merge_hook,\n\t    \"Unexpected extent_hooks error\");\n\n\tif (!check_background_thread_enabled()) {\n\t\ttest_extent_body(arena_ind);\n\t}\n\n\t/* Restore extent hooks. */\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL,\n\t    (void *)&old_hooks, new_size), 0, \"Unexpected extent_hooks error\");\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,\n\t    &old_size, NULL, 0), 0, \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks, default_hooks, \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->alloc, default_hooks->alloc,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->dalloc, default_hooks->dalloc,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->commit, default_hooks->commit,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->decommit, default_hooks->decommit,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->purge_lazy, default_hooks->purge_lazy,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->purge_forced, default_hooks->purge_forced,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->split, default_hooks->split,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->merge, default_hooks->merge,\n\t    \"Unexpected extent_hooks error\");\n}\n\nTEST_BEGIN(test_extent_manual_hook) {\n\ttest_manual_hook_auto_arena();\n\ttest_manual_hook_body();\n\n\t/* Test failure paths. */\n\ttry_split = false;\n\ttest_manual_hook_body();\n\ttry_merge = false;\n\ttest_manual_hook_body();\n\ttry_purge_lazy = false;\n\ttry_purge_forced = false;\n\ttest_manual_hook_body();\n\n\ttry_split = try_merge = try_purge_lazy = try_purge_forced = true;\n}\nTEST_END\n\nTEST_BEGIN(test_extent_auto_hook) {\n\tunsigned arena_ind;\n\tsize_t new_size, sz;\n\textent_hooks_t *new_hooks;\n\n\textent_hooks_prep();\n\n\tsz = sizeof(unsigned);\n\tnew_hooks = &hooks;\n\tnew_size = sizeof(extent_hooks_t *);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz,\n\t    (void *)&new_hooks, new_size), 0, \"Unexpected mallctl() failure\");\n\n\ttest_skip_if(check_background_thread_enabled());\n\ttest_extent_body(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_extent_manual_hook,\n\t    test_extent_auto_hook);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/extent.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"junk:false\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/mallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\n/*\n * On systems which can't merge extents, tests that call this function generate\n * a lot of dirty memory very quickly.  Purging between cycles mitigates\n * potential OOM on e.g. 32-bit Windows.\n */\nstatic void\npurge(void) {\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl error\");\n}\n\nTEST_BEGIN(test_overflow) {\n\tsize_t largemax;\n\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tassert_ptr_null(mallocx(largemax+1, 0),\n\t    \"Expected OOM for mallocx(size=%#zx, 0)\", largemax+1);\n\n\tassert_ptr_null(mallocx(ZU(PTRDIFF_MAX)+1, 0),\n\t    \"Expected OOM for mallocx(size=%#zx, 0)\", ZU(PTRDIFF_MAX)+1);\n\n\tassert_ptr_null(mallocx(SIZE_T_MAX, 0),\n\t    \"Expected OOM for mallocx(size=%#zx, 0)\", SIZE_T_MAX);\n\n\tassert_ptr_null(mallocx(1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),\n\t    \"Expected OOM for mallocx(size=1, MALLOCX_ALIGN(%#zx))\",\n\t    ZU(PTRDIFF_MAX)+1);\n}\nTEST_END\n\nTEST_BEGIN(test_oom) {\n\tsize_t largemax;\n\tbool oom;\n\tvoid *ptrs[3];\n\tunsigned i;\n\n\t/*\n\t * It should be impossible to allocate three objects that each consume\n\t * nearly half the virtual address space.\n\t */\n\tlargemax = get_large_size(get_nlarge()-1);\n\toom = false;\n\tfor (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {\n\t\tptrs[i] = mallocx(largemax, 0);\n\t\tif (ptrs[i] == NULL) {\n\t\t\toom = true;\n\t\t}\n\t}\n\tassert_true(oom,\n\t    \"Expected OOM during series of calls to mallocx(size=%zu, 0)\",\n\t    largemax);\n\tfor (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {\n\t\tif (ptrs[i] != NULL) {\n\t\t\tdallocx(ptrs[i], 0);\n\t\t}\n\t}\n\tpurge();\n\n#if LG_SIZEOF_PTR == 3\n\tassert_ptr_null(mallocx(0x8000000000000000ULL,\n\t    MALLOCX_ALIGN(0x8000000000000000ULL)),\n\t    \"Expected OOM for mallocx()\");\n\tassert_ptr_null(mallocx(0x8000000000000000ULL,\n\t    MALLOCX_ALIGN(0x80000000)),\n\t    \"Expected OOM for mallocx()\");\n#else\n\tassert_ptr_null(mallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)),\n\t    \"Expected OOM for mallocx()\");\n#endif\n}\nTEST_END\n\nTEST_BEGIN(test_basic) {\n#define MAXSZ (((size_t)1) << 23)\n\tsize_t sz;\n\n\tfor (sz = 1; sz < MAXSZ; sz = nallocx(sz, 0) + 1) {\n\t\tsize_t nsz, rsz;\n\t\tvoid *p;\n\t\tnsz = nallocx(sz, 0);\n\t\tassert_zu_ne(nsz, 0, \"Unexpected nallocx() error\");\n\t\tp = mallocx(sz, 0);\n\t\tassert_ptr_not_null(p,\n\t\t    \"Unexpected mallocx(size=%zx, flags=0) error\", sz);\n\t\trsz = sallocx(p, 0);\n\t\tassert_zu_ge(rsz, sz, \"Real size smaller than expected\");\n\t\tassert_zu_eq(nsz, rsz, \"nallocx()/sallocx() size mismatch\");\n\t\tdallocx(p, 0);\n\n\t\tp = mallocx(sz, 0);\n\t\tassert_ptr_not_null(p,\n\t\t    \"Unexpected mallocx(size=%zx, flags=0) error\", sz);\n\t\tdallocx(p, 0);\n\n\t\tnsz = nallocx(sz, MALLOCX_ZERO);\n\t\tassert_zu_ne(nsz, 0, \"Unexpected nallocx() error\");\n\t\tp = mallocx(sz, MALLOCX_ZERO);\n\t\tassert_ptr_not_null(p,\n\t\t    \"Unexpected mallocx(size=%zx, flags=MALLOCX_ZERO) error\",\n\t\t    nsz);\n\t\trsz = sallocx(p, 0);\n\t\tassert_zu_eq(nsz, rsz, \"nallocx()/sallocx() rsize mismatch\");\n\t\tdallocx(p, 0);\n\t\tpurge();\n\t}\n#undef MAXSZ\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n\tconst char *percpu_arena;\n\tsize_t sz = sizeof(percpu_arena);\n\n\tif(mallctl(\"opt.percpu_arena\", (void *)&percpu_arena, &sz, NULL, 0) ||\n\t    strcmp(percpu_arena, \"disabled\") != 0) {\n\t\ttest_skip(\"test_alignment_and_size skipped: \"\n\t\t    \"not working with percpu arena.\");\n\t};\n#define MAXALIGN (((size_t)1) << 23)\n#define NITER 4\n\tsize_t nsz, rsz, alignment, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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 = nallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\tassert_zu_ne(nsz, 0,\n\t\t\t\t    \"nallocx() error for alignment=%zu, \"\n\t\t\t\t    \"size=%zu (%#zx)\", alignment, sz, sz);\n\t\t\t\tps[i] = mallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\tassert_ptr_not_null(ps[i],\n\t\t\t\t    \"mallocx() error for alignment=%zu, \"\n\t\t\t\t    \"size=%zu (%#zx)\", alignment, sz, sz);\n\t\t\t\trsz = sallocx(ps[i], 0);\n\t\t\t\tassert_zu_ge(rsz, sz,\n\t\t\t\t    \"Real size smaller than expected for \"\n\t\t\t\t    \"alignment=%zu, size=%zu\", alignment, sz);\n\t\t\t\tassert_zu_eq(nsz, rsz,\n\t\t\t\t    \"nallocx()/sallocx() size mismatch for \"\n\t\t\t\t    \"alignment=%zu, size=%zu\", alignment, sz);\n\t\t\t\tassert_ptr_null(\n\t\t\t\t    (void *)((uintptr_t)ps[i] & (alignment-1)),\n\t\t\t\t    \"%p inadequately aligned for\"\n\t\t\t\t    \" alignment=%zu, size=%zu\", ps[i],\n\t\t\t\t    alignment, sz);\n\t\t\t\ttotal += rsz;\n\t\t\t\tif (total >= (MAXALIGN << 1)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\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\tdallocx(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\tpurge();\n\t}\n#undef MAXALIGN\n#undef NITER\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_overflow,\n\t    test_oom,\n\t    test_basic,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/mallocx.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"junk:false\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/overflow.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_overflow) {\n\tunsigned nlextents;\n\tsize_t mib[4];\n\tsize_t sz, miblen, max_size_class;\n\tvoid *p;\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nlextents\", (void *)&nlextents, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() error\");\n\n\tmiblen = sizeof(mib) / sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arenas.lextent.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() error\");\n\tmib[2] = nlextents - 1;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctlbymib() error\");\n\n\tassert_ptr_null(malloc(max_size_class + 1),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tassert_ptr_null(malloc(SIZE_T_MAX),\n\t    \"Expected OOM due to over-sized allocation request\");\n\n\tassert_ptr_null(calloc(1, max_size_class + 1),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tassert_ptr_null(calloc(1, SIZE_T_MAX),\n\t    \"Expected OOM due to over-sized allocation request\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() OOM\");\n\tassert_ptr_null(realloc(p, max_size_class + 1),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tassert_ptr_null(realloc(p, SIZE_T_MAX),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tfree(p);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_overflow);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/posix_memalign.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAXALIGN (((size_t)1) << 23)\n\n/*\n * On systems which can't merge extents, tests that call this function generate\n * a lot of dirty memory very quickly.  Purging between cycles mitigates\n * potential OOM on e.g. 32-bit Windows.\n */\nstatic void\npurge(void) {\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl error\");\n}\n\nTEST_BEGIN(test_alignment_errors) {\n\tsize_t alignment;\n\tvoid *p;\n\n\tfor (alignment = 0; alignment < sizeof(void *); alignment++) {\n\t\tassert_d_eq(posix_memalign(&p, alignment, 1), EINVAL,\n\t\t    \"Expected error for invalid alignment %zu\",\n\t\t    alignment);\n\t}\n\n\tfor (alignment = sizeof(size_t); alignment < MAXALIGN;\n\t    alignment <<= 1) {\n\t\tassert_d_ne(posix_memalign(&p, alignment + 1, 1), 0,\n\t\t    \"Expected error for invalid alignment %zu\",\n\t\t    alignment + 1);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_oom_errors) {\n\tsize_t alignment, size;\n\tvoid *p;\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\tassert_d_ne(posix_memalign(&p, alignment, size), 0,\n\t    \"Expected error for posix_memalign(&p, %zu, %zu)\",\n\t    alignment, size);\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsize      = UINT64_C(0xc000000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsize      = 0xc0000001LU;\n#endif\n\tassert_d_ne(posix_memalign(&p, alignment, size), 0,\n\t    \"Expected error for posix_memalign(&p, %zu, %zu)\",\n\t    alignment, size);\n\n\talignment = 0x10LU;\n#if LG_SIZEOF_PTR == 3\n\tsize = UINT64_C(0xfffffffffffffff0);\n#else\n\tsize = 0xfffffff0LU;\n#endif\n\tassert_d_ne(posix_memalign(&p, alignment, size), 0,\n\t    \"Expected error for posix_memalign(&p, %zu, %zu)\",\n\t    alignment, size);\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n#define NITER 4\n\tsize_t alignment, size, total;\n\tunsigned i;\n\tint err;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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\tchar buf[BUFERROR_BUF];\n\n\t\t\t\t\tbuferror(get_errno(), buf, sizeof(buf));\n\t\t\t\t\ttest_fail(\n\t\t\t\t\t    \"Error for alignment=%zu, \"\n\t\t\t\t\t    \"size=%zu (%#zx): %s\",\n\t\t\t\t\t    alignment, size, size, buf);\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\t}\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\tpurge();\n\t}\n#undef NITER\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_alignment_errors,\n\t    test_oom_errors,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/rallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\nTEST_BEGIN(test_grow_and_shrink) {\n\tvoid *p, *q;\n\tsize_t tsz;\n#define NCYCLES 3\n\tunsigned i, j;\n#define NSZS 1024\n\tsize_t szs[NSZS];\n#define MAXSZ ZU(12 * 1024 * 1024)\n\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tszs[0] = sallocx(p, 0);\n\n\tfor (i = 0; i < NCYCLES; i++) {\n\t\tfor (j = 1; j < NSZS && szs[j-1] < MAXSZ; j++) {\n\t\t\tq = rallocx(p, szs[j-1]+1, 0);\n\t\t\tassert_ptr_not_null(q,\n\t\t\t    \"Unexpected rallocx() error for size=%zu-->%zu\",\n\t\t\t    szs[j-1], szs[j-1]+1);\n\t\t\tszs[j] = sallocx(q, 0);\n\t\t\tassert_zu_ne(szs[j], szs[j-1]+1,\n\t\t\t    \"Expected size to be at least: %zu\", szs[j-1]+1);\n\t\t\tp = q;\n\t\t}\n\n\t\tfor (j--; j > 0; j--) {\n\t\t\tq = rallocx(p, szs[j-1], 0);\n\t\t\tassert_ptr_not_null(q,\n\t\t\t    \"Unexpected rallocx() error for size=%zu-->%zu\",\n\t\t\t    szs[j], szs[j-1]);\n\t\t\ttsz = sallocx(q, 0);\n\t\t\tassert_zu_eq(tsz, szs[j-1],\n\t\t\t    \"Expected size=%zu, got size=%zu\", szs[j-1], tsz);\n\t\t\tp = q;\n\t\t}\n\t}\n\n\tdallocx(p, 0);\n#undef MAXSZ\n#undef NSZS\n#undef NCYCLES\n}\nTEST_END\n\nstatic bool\nvalidate_fill(const void *p, uint8_t c, size_t offset, size_t len) {\n\tbool ret = false;\n\tconst uint8_t *buf = (const uint8_t *)p;\n\tsize_t i;\n\n\tfor (i = 0; i < len; i++) {\n\t\tuint8_t b = buf[offset+i];\n\t\tif (b != c) {\n\t\t\ttest_fail(\"Allocation at %p (len=%zu) contains %#x \"\n\t\t\t    \"rather than %#x at offset %zu\", p, len, b, c,\n\t\t\t    offset+i);\n\t\t\tret = true;\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nTEST_BEGIN(test_zero) {\n\tvoid *p, *q;\n\tsize_t psz, qsz, i, j;\n\tsize_t start_sizes[] = {1, 3*1024, 63*1024, 4095*1024};\n#define FILL_BYTE 0xaaU\n#define RANGE 2048\n\n\tfor (i = 0; i < sizeof(start_sizes)/sizeof(size_t); i++) {\n\t\tsize_t start_size = start_sizes[i];\n\t\tp = mallocx(start_size, MALLOCX_ZERO);\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\t\tpsz = sallocx(p, 0);\n\n\t\tassert_false(validate_fill(p, 0, 0, psz),\n\t\t    \"Expected zeroed memory\");\n\t\tmemset(p, FILL_BYTE, psz);\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, psz),\n\t\t    \"Expected filled memory\");\n\n\t\tfor (j = 1; j < RANGE; j++) {\n\t\t\tq = rallocx(p, start_size+j, MALLOCX_ZERO);\n\t\t\tassert_ptr_not_null(q, \"Unexpected rallocx() error\");\n\t\t\tqsz = sallocx(q, 0);\n\t\t\tif (q != p || qsz != psz) {\n\t\t\t\tassert_false(validate_fill(q, FILL_BYTE, 0,\n\t\t\t\t    psz), \"Expected filled memory\");\n\t\t\t\tassert_false(validate_fill(q, 0, psz, qsz-psz),\n\t\t\t\t    \"Expected zeroed memory\");\n\t\t\t}\n\t\t\tif (psz != qsz) {\n\t\t\t\tmemset((void *)((uintptr_t)q+psz), FILL_BYTE,\n\t\t\t\t    qsz-psz);\n\t\t\t\tpsz = qsz;\n\t\t\t}\n\t\t\tp = q;\n\t\t}\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, psz),\n\t\t    \"Expected filled memory\");\n\t\tdallocx(p, 0);\n\t}\n#undef FILL_BYTE\n}\nTEST_END\n\nTEST_BEGIN(test_align) {\n\tvoid *p, *q;\n\tsize_t align;\n#define MAX_ALIGN (ZU(1) << 25)\n\n\talign = ZU(1);\n\tp = mallocx(1, MALLOCX_ALIGN(align));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tfor (align <<= 1; align <= MAX_ALIGN; align <<= 1) {\n\t\tq = rallocx(p, 1, MALLOCX_ALIGN(align));\n\t\tassert_ptr_not_null(q,\n\t\t    \"Unexpected rallocx() error for align=%zu\", align);\n\t\tassert_ptr_null(\n\t\t    (void *)((uintptr_t)q & (align-1)),\n\t\t    \"%p inadequately aligned for align=%zu\",\n\t\t    q, align);\n\t\tp = q;\n\t}\n\tdallocx(p, 0);\n#undef MAX_ALIGN\n}\nTEST_END\n\nTEST_BEGIN(test_lg_align_and_zero) {\n\tvoid *p, *q;\n\tunsigned lg_align;\n\tsize_t sz;\n#define MAX_LG_ALIGN 25\n#define MAX_VALIDATE (ZU(1) << 22)\n\n\tlg_align = 0;\n\tp = mallocx(1, MALLOCX_LG_ALIGN(lg_align)|MALLOCX_ZERO);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tfor (lg_align++; lg_align <= MAX_LG_ALIGN; lg_align++) {\n\t\tq = rallocx(p, 1, MALLOCX_LG_ALIGN(lg_align)|MALLOCX_ZERO);\n\t\tassert_ptr_not_null(q,\n\t\t    \"Unexpected rallocx() error for lg_align=%u\", lg_align);\n\t\tassert_ptr_null(\n\t\t    (void *)((uintptr_t)q & ((ZU(1) << lg_align)-1)),\n\t\t    \"%p inadequately aligned for lg_align=%u\", q, lg_align);\n\t\tsz = sallocx(q, 0);\n\t\tif ((sz << 1) <= MAX_VALIDATE) {\n\t\t\tassert_false(validate_fill(q, 0, 0, sz),\n\t\t\t    \"Expected zeroed memory\");\n\t\t} else {\n\t\t\tassert_false(validate_fill(q, 0, 0, MAX_VALIDATE),\n\t\t\t    \"Expected zeroed memory\");\n\t\t\tassert_false(validate_fill(\n\t\t\t    (void *)((uintptr_t)q+sz-MAX_VALIDATE),\n\t\t\t    0, 0, MAX_VALIDATE), \"Expected zeroed memory\");\n\t\t}\n\t\tp = q;\n\t}\n\tdallocx(p, 0);\n#undef MAX_VALIDATE\n#undef MAX_LG_ALIGN\n}\nTEST_END\n\nTEST_BEGIN(test_overflow) {\n\tsize_t largemax;\n\tvoid *p;\n\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_ptr_null(rallocx(p, largemax+1, 0),\n\t    \"Expected OOM for rallocx(p, size=%#zx, 0)\", largemax+1);\n\n\tassert_ptr_null(rallocx(p, ZU(PTRDIFF_MAX)+1, 0),\n\t    \"Expected OOM for rallocx(p, size=%#zx, 0)\", ZU(PTRDIFF_MAX)+1);\n\n\tassert_ptr_null(rallocx(p, SIZE_T_MAX, 0),\n\t    \"Expected OOM for rallocx(p, size=%#zx, 0)\", SIZE_T_MAX);\n\n\tassert_ptr_null(rallocx(p, 1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),\n\t    \"Expected OOM for rallocx(p, size=1, MALLOCX_ALIGN(%#zx))\",\n\t    ZU(PTRDIFF_MAX)+1);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_grow_and_shrink,\n\t    test_zero,\n\t    test_align,\n\t    test_lg_align_and_zero,\n\t    test_overflow);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/sdallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAXALIGN (((size_t)1) << 22)\n#define NITER 3\n\nTEST_BEGIN(test_basic) {\n\tvoid *ptr = mallocx(64, 0);\n\tsdallocx(ptr, 64, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n\tsize_t nsz, sz, alignment, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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 = nallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\tps[i] = mallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\ttotal += nsz;\n\t\t\t\tif (total >= (MAXALIGN << 1)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\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\tsdallocx(ps[i], sz,\n\t\t\t\t\t    MALLOCX_ALIGN(alignment));\n\t\t\t\t\tps[i] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_basic,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/thread_arena.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS 10\n\nvoid *\nthd_start(void *arg) {\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\tassert_ptr_not_null(p, \"Error in malloc()\");\n\tfree(p);\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", (void *)&arena_ind, &size,\n\t    (void *)&main_arena_ind, sizeof(main_arena_ind)))) {\n\t\tchar buf[BUFERROR_BUF];\n\n\t\tbuferror(err, buf, sizeof(buf));\n\t\ttest_fail(\"Error in mallctl(): %s\", buf);\n\t}\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", (void *)&arena_ind, &size, NULL,\n\t    0))) {\n\t\tchar buf[BUFERROR_BUF];\n\n\t\tbuferror(err, buf, sizeof(buf));\n\t\ttest_fail(\"Error in mallctl(): %s\", buf);\n\t}\n\tassert_u_eq(arena_ind, main_arena_ind,\n\t    \"Arena index should be same as for main thread\");\n\n\treturn NULL;\n}\n\nstatic void\nmallctl_failure(int err) {\n\tchar buf[BUFERROR_BUF];\n\n\tbuferror(err, buf, sizeof(buf));\n\ttest_fail(\"Error in mallctl(): %s\", buf);\n}\n\nTEST_BEGIN(test_thread_arena) {\n\tvoid *p;\n\tint err;\n\tthd_t thds[NTHREADS];\n\tunsigned i;\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Error in malloc()\");\n\n\tunsigned arena_ind, old_arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Arena creation failure\");\n\n\tsize_t size = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", (void *)&old_arena_ind, &size,\n\t    (void *)&arena_ind, sizeof(arena_ind))) != 0) {\n\t\tmallctl_failure(err);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_create(&thds[i], thd_start,\n\t\t    (void *)&arena_ind);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tintptr_t join_ret;\n\t\tthd_join(thds[i], (void *)&join_ret);\n\t\tassert_zd_eq(join_ret, 0, \"Unexpected thread join error\");\n\t}\n\tfree(p);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_thread_arena);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/thread_tcache_enabled.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nvoid *\nthd_start(void *arg) {\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\tif (e0) {\n\t\te1 = false;\n\t\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\t\tassert_true(e0, \"tcache should be enabled\");\n\t}\n\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\tfree(malloc(1));\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\tfree(malloc(1));\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\tfree(malloc(1));\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\tfree(malloc(1));\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\tfree(malloc(1));\n\treturn NULL;\n}\n\nTEST_BEGIN(test_main_thread) {\n\tthd_start(NULL);\n}\nTEST_END\n\nTEST_BEGIN(test_subthread) {\n\tthd_t thd;\n\n\tthd_create(&thd, thd_start, NULL);\n\tthd_join(thd, NULL);\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Run tests multiple times to check for bad interactions. */\n\treturn test(\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/xallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * Use a separate arena for xallocx() extension/contraction tests so that\n * internal allocation e.g. by heap profiling can't interpose allocations where\n * xallocx() would ordinarily be able to extend.\n */\nstatic unsigned\narena_ind(void) {\n\tstatic unsigned ind = 0;\n\n\tif (ind == 0) {\n\t\tsize_t sz = sizeof(ind);\n\t\tassert_d_eq(mallctl(\"arenas.create\", (void *)&ind, &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl failure creating arena\");\n\t}\n\n\treturn ind;\n}\n\nTEST_BEGIN(test_same_size) {\n\tvoid *p;\n\tsize_t sz, tsz;\n\n\tp = mallocx(42, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tsz = sallocx(p, 0);\n\n\ttsz = xallocx(p, sz, 0, 0);\n\tassert_zu_eq(tsz, sz, \"Unexpected size change: %zu --> %zu\", sz, tsz);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_extra_no_move) {\n\tvoid *p;\n\tsize_t sz, tsz;\n\n\tp = mallocx(42, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tsz = sallocx(p, 0);\n\n\ttsz = xallocx(p, sz, sz-42, 0);\n\tassert_zu_eq(tsz, sz, \"Unexpected size change: %zu --> %zu\", sz, tsz);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_no_move_fail) {\n\tvoid *p;\n\tsize_t sz, tsz;\n\n\tp = mallocx(42, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tsz = sallocx(p, 0);\n\n\ttsz = xallocx(p, sz + 5, 0, 0);\n\tassert_zu_eq(tsz, sz, \"Unexpected size change: %zu --> %zu\", sz, tsz);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nsmall(void) {\n\treturn get_nsizes_impl(\"arenas.nbins\");\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_small_size(size_t ind) {\n\treturn get_size_impl(\"arenas.bin.0.size\", ind);\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\nTEST_BEGIN(test_size) {\n\tsize_t small0, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmall0 = get_small_size(0);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(small0, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\t/* Test smallest supported size. */\n\tassert_zu_eq(xallocx(p, 1, 0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test largest supported size. */\n\tassert_zu_le(xallocx(p, largemax, 0, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test size overflow. */\n\tassert_zu_le(xallocx(p, largemax+1, 0, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_size_extra_overflow) {\n\tsize_t small0, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmall0 = get_small_size(0);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(small0, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\t/* Test overflows that can be resolved by clamping extra. */\n\tassert_zu_le(xallocx(p, largemax-1, 2, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, largemax, 1, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test overflow such that largemax-size underflows. */\n\tassert_zu_le(xallocx(p, largemax+1, 2, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, largemax+2, 3, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_extra_small) {\n\tsize_t small0, small1, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmall0 = get_small_size(0);\n\tsmall1 = get_small_size(1);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(small0, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tassert_zu_eq(xallocx(p, small1, 0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_eq(xallocx(p, small1, 0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test size+extra overflow. */\n\tassert_zu_eq(xallocx(p, small0, largemax - small0 + 1, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_extra_large) {\n\tint flags = MALLOCX_ARENA(arena_ind());\n\tsize_t smallmax, large1, large2, large3, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmallmax = get_small_size(get_nsmall()-1);\n\tlarge1 = get_large_size(1);\n\tlarge2 = get_large_size(2);\n\tlarge3 = get_large_size(3);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(large3, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tassert_zu_eq(xallocx(p, large3, 0, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size decrease with zero extra. */\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_ge(xallocx(p, smallmax, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\n\tif (xallocx(p, large3, 0, flags) != large3) {\n\t\tp = rallocx(p, large3, flags);\n\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t}\n\t/* Test size decrease with non-zero extra. */\n\tassert_zu_eq(xallocx(p, large1, large3 - large1, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_eq(xallocx(p, large2, large3 - large2, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_ge(xallocx(p, large1, large2 - large1, flags), large2,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_ge(xallocx(p, smallmax, large1 - smallmax, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size increase with zero extra. */\n\tassert_zu_le(xallocx(p, large3, 0, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, largemax+1, 0, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size increase with non-zero extra. */\n\tassert_zu_le(xallocx(p, large1, SIZE_T_MAX - large1, flags), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size increase with non-zero extra. */\n\tassert_zu_le(xallocx(p, large1, large3 - large1, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\n\tif (xallocx(p, large3, 0, flags) != large3) {\n\t\tp = rallocx(p, large3, flags);\n\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t}\n\t/* Test size+extra overflow. */\n\tassert_zu_le(xallocx(p, large3, largemax - large3 + 1, flags), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, flags);\n}\nTEST_END\n\nstatic void\nprint_filled_extents(const void *p, uint8_t c, size_t len) {\n\tconst uint8_t *pc = (const uint8_t *)p;\n\tsize_t i, range0;\n\tuint8_t c0;\n\n\tmalloc_printf(\"  p=%p, c=%#x, len=%zu:\", p, c, len);\n\trange0 = 0;\n\tc0 = pc[0];\n\tfor (i = 0; i < len; i++) {\n\t\tif (pc[i] != c0) {\n\t\t\tmalloc_printf(\" %#x[%zu..%zu)\", c0, range0, i);\n\t\t\trange0 = i;\n\t\t\tc0 = pc[i];\n\t\t}\n\t}\n\tmalloc_printf(\" %#x[%zu..%zu)\\n\", c0, range0, i);\n}\n\nstatic bool\nvalidate_fill(const void *p, uint8_t c, size_t offset, size_t len) {\n\tconst uint8_t *pc = (const uint8_t *)p;\n\tbool err;\n\tsize_t i;\n\n\tfor (i = offset, err = false; i < offset+len; i++) {\n\t\tif (pc[i] != c) {\n\t\t\terr = true;\n\t\t}\n\t}\n\n\tif (err) {\n\t\tprint_filled_extents(p, c, offset + len);\n\t}\n\n\treturn err;\n}\n\nstatic void\ntest_zero(size_t szmin, size_t szmax) {\n\tint flags = MALLOCX_ARENA(arena_ind()) | MALLOCX_ZERO;\n\tsize_t sz, nsz;\n\tvoid *p;\n#define FILL_BYTE 0x7aU\n\n\tsz = szmax;\n\tp = mallocx(sz, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tassert_false(validate_fill(p, 0x00, 0, sz), \"Memory not filled: sz=%zu\",\n\t    sz);\n\n\t/*\n\t * Fill with non-zero so that non-debug builds are more likely to detect\n\t * errors.\n\t */\n\tmemset(p, FILL_BYTE, sz);\n\tassert_false(validate_fill(p, FILL_BYTE, 0, sz),\n\t    \"Memory not filled: sz=%zu\", sz);\n\n\t/* Shrink in place so that we can expect growing in place to succeed. */\n\tsz = szmin;\n\tif (xallocx(p, sz, 0, flags) != sz) {\n\t\tp = rallocx(p, sz, flags);\n\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t}\n\tassert_false(validate_fill(p, FILL_BYTE, 0, sz),\n\t    \"Memory not filled: sz=%zu\", sz);\n\n\tfor (sz = szmin; sz < szmax; sz = nsz) {\n\t\tnsz = nallocx(sz+1, flags);\n\t\tif (xallocx(p, sz+1, 0, flags) != nsz) {\n\t\t\tp = rallocx(p, sz+1, flags);\n\t\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t\t}\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, sz),\n\t\t    \"Memory not filled: sz=%zu\", sz);\n\t\tassert_false(validate_fill(p, 0x00, sz, nsz-sz),\n\t\t    \"Memory not filled: sz=%zu, nsz-sz=%zu\", sz, nsz-sz);\n\t\tmemset((void *)((uintptr_t)p + sz), FILL_BYTE, nsz-sz);\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, nsz),\n\t\t    \"Memory not filled: nsz=%zu\", nsz);\n\t}\n\n\tdallocx(p, flags);\n}\n\nTEST_BEGIN(test_zero_large) {\n\tsize_t large0, large1;\n\n\t/* Get size classes. */\n\tlarge0 = get_large_size(0);\n\tlarge1 = get_large_size(1);\n\n\ttest_zero(large1, large0 * 2);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_same_size,\n\t    test_extra_no_move,\n\t    test_no_move_fail,\n\t    test_size,\n\t    test_size_extra_overflow,\n\t    test_extra_small,\n\t    test_extra_large,\n\t    test_zero_large);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/integration/xallocx.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"junk:false\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/SFMT.c",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file  SFMT.c\n * @brief SIMD oriented Fast Mersenne Twister(SFMT)\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * Copyright (C) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software, see LICENSE.txt\n */\n#define SFMT_C_\n#include \"test/jemalloc_test.h\"\n#include \"test/SFMT-params.h\"\n\n#if defined(JEMALLOC_BIG_ENDIAN) && !defined(BIG_ENDIAN64)\n#define BIG_ENDIAN64 1\n#endif\n#if defined(__BIG_ENDIAN__) && !defined(__amd64) && !defined(BIG_ENDIAN64)\n#define BIG_ENDIAN64 1\n#endif\n#if defined(HAVE_ALTIVEC) && !defined(BIG_ENDIAN64)\n#define BIG_ENDIAN64 1\n#endif\n#if defined(ONLY64) && !defined(BIG_ENDIAN64)\n  #if defined(__GNUC__)\n    #error \"-DONLY64 must be specified with -DBIG_ENDIAN64\"\n  #endif\n#undef ONLY64\n#endif\n/*------------------------------------------------------\n  128-bit SIMD data type for Altivec, SSE2 or standard C\n  ------------------------------------------------------*/\n#if defined(HAVE_ALTIVEC)\n/** 128-bit data structure */\nunion W128_T {\n    vector unsigned int s;\n    uint32_t u[4];\n};\n/** 128-bit data type */\ntypedef union W128_T w128_t;\n\n#elif defined(HAVE_SSE2)\n/** 128-bit data structure */\nunion W128_T {\n    __m128i si;\n    uint32_t u[4];\n};\n/** 128-bit data type */\ntypedef union W128_T w128_t;\n\n#else\n\n/** 128-bit data structure */\nstruct W128_T {\n    uint32_t u[4];\n};\n/** 128-bit data type */\ntypedef struct W128_T w128_t;\n\n#endif\n\nstruct sfmt_s {\n    /** the 128-bit internal state array */\n    w128_t sfmt[N];\n    /** index counter to the 32-bit internal state array */\n    int idx;\n    /** a flag: it is 0 if and only if the internal state is not yet\n     * initialized. */\n    int initialized;\n};\n\n/*--------------------------------------\n  FILE GLOBAL VARIABLES\n  internal state, index counter and flag\n  --------------------------------------*/\n\n/** a parity check vector which certificate the period of 2^{MEXP} */\nstatic uint32_t parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};\n\n/*----------------\n  STATIC FUNCTIONS\n  ----------------*/\nstatic inline int idxof(int i);\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\nstatic inline void rshift128(w128_t *out,  w128_t const *in, int shift);\nstatic inline void lshift128(w128_t *out,  w128_t const *in, int shift);\n#endif\nstatic inline void gen_rand_all(sfmt_t *ctx);\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size);\nstatic inline uint32_t func1(uint32_t x);\nstatic inline uint32_t func2(uint32_t x);\nstatic void period_certification(sfmt_t *ctx);\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\nstatic inline void swap(w128_t *array, int size);\n#endif\n\n#if defined(HAVE_ALTIVEC)\n  #include \"test/SFMT-alti.h\"\n#elif defined(HAVE_SSE2)\n  #include \"test/SFMT-sse2.h\"\n#endif\n\n/**\n * This function simulate a 64-bit index of LITTLE ENDIAN\n * in BIG ENDIAN machine.\n */\n#ifdef ONLY64\nstatic inline int idxof(int i) {\n    return i ^ 1;\n}\n#else\nstatic inline int idxof(int i) {\n    return i;\n}\n#endif\n/**\n * This function simulates SIMD 128-bit right shift by the standard C.\n * The 128-bit integer given in in is shifted by (shift * 8) bits.\n * This function simulates the LITTLE ENDIAN SIMD.\n * @param out the output of this function\n * @param in the 128-bit data to be shifted\n * @param shift the shift value\n */\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\n#ifdef ONLY64\nstatic inline void rshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);\n    tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);\n\n    oh = th >> (shift * 8);\n    ol = tl >> (shift * 8);\n    ol |= th << (64 - shift * 8);\n    out->u[0] = (uint32_t)(ol >> 32);\n    out->u[1] = (uint32_t)ol;\n    out->u[2] = (uint32_t)(oh >> 32);\n    out->u[3] = (uint32_t)oh;\n}\n#else\nstatic inline void rshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);\n    tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);\n\n    oh = th >> (shift * 8);\n    ol = tl >> (shift * 8);\n    ol |= th << (64 - shift * 8);\n    out->u[1] = (uint32_t)(ol >> 32);\n    out->u[0] = (uint32_t)ol;\n    out->u[3] = (uint32_t)(oh >> 32);\n    out->u[2] = (uint32_t)oh;\n}\n#endif\n/**\n * This function simulates SIMD 128-bit left shift by the standard C.\n * The 128-bit integer given in in is shifted by (shift * 8) bits.\n * This function simulates the LITTLE ENDIAN SIMD.\n * @param out the output of this function\n * @param in the 128-bit data to be shifted\n * @param shift the shift value\n */\n#ifdef ONLY64\nstatic inline void lshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);\n    tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);\n\n    oh = th << (shift * 8);\n    ol = tl << (shift * 8);\n    oh |= tl >> (64 - shift * 8);\n    out->u[0] = (uint32_t)(ol >> 32);\n    out->u[1] = (uint32_t)ol;\n    out->u[2] = (uint32_t)(oh >> 32);\n    out->u[3] = (uint32_t)oh;\n}\n#else\nstatic inline void lshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);\n    tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);\n\n    oh = th << (shift * 8);\n    ol = tl << (shift * 8);\n    oh |= tl >> (64 - shift * 8);\n    out->u[1] = (uint32_t)(ol >> 32);\n    out->u[0] = (uint32_t)ol;\n    out->u[3] = (uint32_t)(oh >> 32);\n    out->u[2] = (uint32_t)oh;\n}\n#endif\n#endif\n\n/**\n * This function represents the recursion formula.\n * @param r output\n * @param a a 128-bit part of the internal state array\n * @param b a 128-bit part of the internal state array\n * @param c a 128-bit part of the internal state array\n * @param d a 128-bit part of the internal state array\n */\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\n#ifdef ONLY64\nstatic inline void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,\n\t\t\t\tw128_t *d) {\n    w128_t x;\n    w128_t y;\n\n    lshift128(&x, a, SL2);\n    rshift128(&y, c, SR2);\n    r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0]\n\t^ (d->u[0] << SL1);\n    r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1]\n\t^ (d->u[1] << SL1);\n    r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2]\n\t^ (d->u[2] << SL1);\n    r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3]\n\t^ (d->u[3] << SL1);\n}\n#else\nstatic inline void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,\n\t\t\t\tw128_t *d) {\n    w128_t x;\n    w128_t y;\n\n    lshift128(&x, a, SL2);\n    rshift128(&y, c, SR2);\n    r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0]\n\t^ (d->u[0] << SL1);\n    r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1]\n\t^ (d->u[1] << SL1);\n    r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2]\n\t^ (d->u[2] << SL1);\n    r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3]\n\t^ (d->u[3] << SL1);\n}\n#endif\n#endif\n\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\n/**\n * This function fills the internal state array with pseudorandom\n * integers.\n */\nstatic inline void gen_rand_all(sfmt_t *ctx) {\n    int i;\n    w128_t *r1, *r2;\n\n    r1 = &ctx->sfmt[N - 2];\n    r2 = &ctx->sfmt[N - 1];\n    for (i = 0; i < N - POS1; i++) {\n\tdo_recursion(&ctx->sfmt[i], &ctx->sfmt[i], &ctx->sfmt[i + POS1], r1,\n\t  r2);\n\tr1 = r2;\n\tr2 = &ctx->sfmt[i];\n    }\n    for (; i < N; i++) {\n\tdo_recursion(&ctx->sfmt[i], &ctx->sfmt[i], &ctx->sfmt[i + POS1 - N], r1,\n\t  r2);\n\tr1 = r2;\n\tr2 = &ctx->sfmt[i];\n    }\n}\n\n/**\n * This function fills the user-specified array with pseudorandom\n * integers.\n *\n * @param array an 128-bit array to be filled by pseudorandom numbers.\n * @param size number of 128-bit pseudorandom numbers to be generated.\n */\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size) {\n    int i, j;\n    w128_t *r1, *r2;\n\n    r1 = &ctx->sfmt[N - 2];\n    r2 = &ctx->sfmt[N - 1];\n    for (i = 0; i < N - POS1; i++) {\n\tdo_recursion(&array[i], &ctx->sfmt[i], &ctx->sfmt[i + POS1], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n    }\n    for (; i < N; i++) {\n\tdo_recursion(&array[i], &ctx->sfmt[i], &array[i + POS1 - N], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n    }\n    for (; i < size - N; i++) {\n\tdo_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n    }\n    for (j = 0; j < 2 * N - size; j++) {\n\tctx->sfmt[j] = array[j + size - N];\n    }\n    for (; i < size; i++, j++) {\n\tdo_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n\tctx->sfmt[j] = array[i];\n    }\n}\n#endif\n\n#if defined(BIG_ENDIAN64) && !defined(ONLY64) && !defined(HAVE_ALTIVEC)\nstatic inline void swap(w128_t *array, int size) {\n    int i;\n    uint32_t x, y;\n\n    for (i = 0; i < size; i++) {\n\tx = array[i].u[0];\n\ty = array[i].u[2];\n\tarray[i].u[0] = array[i].u[1];\n\tarray[i].u[2] = array[i].u[3];\n\tarray[i].u[1] = x;\n\tarray[i].u[3] = y;\n    }\n}\n#endif\n/**\n * This function represents a function used in the initialization\n * by init_by_array\n * @param x 32-bit integer\n * @return 32-bit integer\n */\nstatic uint32_t func1(uint32_t x) {\n    return (x ^ (x >> 27)) * (uint32_t)1664525UL;\n}\n\n/**\n * This function represents a function used in the initialization\n * by init_by_array\n * @param x 32-bit integer\n * @return 32-bit integer\n */\nstatic uint32_t func2(uint32_t x) {\n    return (x ^ (x >> 27)) * (uint32_t)1566083941UL;\n}\n\n/**\n * This function certificate the period of 2^{MEXP}\n */\nstatic void period_certification(sfmt_t *ctx) {\n    int inner = 0;\n    int i, j;\n    uint32_t work;\n    uint32_t *psfmt32 = &ctx->sfmt[0].u[0];\n\n    for (i = 0; i < 4; i++)\n\tinner ^= psfmt32[idxof(i)] & parity[i];\n    for (i = 16; i > 0; i >>= 1)\n\tinner ^= inner >> i;\n    inner &= 1;\n    /* check OK */\n    if (inner == 1) {\n\treturn;\n    }\n    /* check NG, and modification */\n    for (i = 0; i < 4; i++) {\n\twork = 1;\n\tfor (j = 0; j < 32; j++) {\n\t    if ((work & parity[i]) != 0) {\n\t\tpsfmt32[idxof(i)] ^= work;\n\t\treturn;\n\t    }\n\t    work = work << 1;\n\t}\n    }\n}\n\n/*----------------\n  PUBLIC FUNCTIONS\n  ----------------*/\n/**\n * This function returns the identification string.\n * The string shows the word size, the Mersenne exponent,\n * and all parameters of this generator.\n */\nconst char *get_idstring(void) {\n    return IDSTR;\n}\n\n/**\n * This function returns the minimum size of array used for \\b\n * fill_array32() function.\n * @return minimum size of array used for fill_array32() function.\n */\nint get_min_array_size32(void) {\n    return N32;\n}\n\n/**\n * This function returns the minimum size of array used for \\b\n * fill_array64() function.\n * @return minimum size of array used for fill_array64() function.\n */\nint get_min_array_size64(void) {\n    return N64;\n}\n\n#ifndef ONLY64\n/**\n * This function generates and returns 32-bit pseudorandom number.\n * init_gen_rand or init_by_array must be called before this function.\n * @return 32-bit pseudorandom number\n */\nuint32_t gen_rand32(sfmt_t *ctx) {\n    uint32_t r;\n    uint32_t *psfmt32 = &ctx->sfmt[0].u[0];\n\n    assert(ctx->initialized);\n    if (ctx->idx >= N32) {\n\tgen_rand_all(ctx);\n\tctx->idx = 0;\n    }\n    r = psfmt32[ctx->idx++];\n    return r;\n}\n\n/* Generate a random integer in [0..limit). */\nuint32_t gen_rand32_range(sfmt_t *ctx, uint32_t limit) {\n    uint32_t ret, above;\n\n    above = 0xffffffffU - (0xffffffffU % limit);\n    while (1) {\n\tret = gen_rand32(ctx);\n\tif (ret < above) {\n\t    ret %= limit;\n\t    break;\n\t}\n    }\n    return ret;\n}\n#endif\n/**\n * This function generates and returns 64-bit pseudorandom number.\n * init_gen_rand or init_by_array must be called before this function.\n * The function gen_rand64 should not be called after gen_rand32,\n * unless an initialization is again executed.\n * @return 64-bit pseudorandom number\n */\nuint64_t gen_rand64(sfmt_t *ctx) {\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\n    uint32_t r1, r2;\n    uint32_t *psfmt32 = &ctx->sfmt[0].u[0];\n#else\n    uint64_t r;\n    uint64_t *psfmt64 = (uint64_t *)&ctx->sfmt[0].u[0];\n#endif\n\n    assert(ctx->initialized);\n    assert(ctx->idx % 2 == 0);\n\n    if (ctx->idx >= N32) {\n\tgen_rand_all(ctx);\n\tctx->idx = 0;\n    }\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\n    r1 = psfmt32[ctx->idx];\n    r2 = psfmt32[ctx->idx + 1];\n    ctx->idx += 2;\n    return ((uint64_t)r2 << 32) | r1;\n#else\n    r = psfmt64[ctx->idx / 2];\n    ctx->idx += 2;\n    return r;\n#endif\n}\n\n/* Generate a random integer in [0..limit). */\nuint64_t gen_rand64_range(sfmt_t *ctx, uint64_t limit) {\n    uint64_t ret, above;\n\n    above = KQU(0xffffffffffffffff) - (KQU(0xffffffffffffffff) % limit);\n    while (1) {\n\tret = gen_rand64(ctx);\n\tif (ret < above) {\n\t    ret %= limit;\n\t    break;\n\t}\n    }\n    return ret;\n}\n\n#ifndef ONLY64\n/**\n * This function generates pseudorandom 32-bit integers in the\n * specified array[] by one call. The number of pseudorandom integers\n * is specified by the argument size, which must be at least 624 and a\n * multiple of four.  The generation by this function is much faster\n * than the following gen_rand function.\n *\n * For initialization, init_gen_rand or init_by_array must be called\n * before the first call of this function. This function can not be\n * used after calling gen_rand function, without initialization.\n *\n * @param array an array where pseudorandom 32-bit integers are filled\n * by this function.  The pointer to the array must be \\b \"aligned\"\n * (namely, must be a multiple of 16) in the SIMD version, since it\n * refers to the address of a 128-bit integer.  In the standard C\n * version, the pointer is arbitrary.\n *\n * @param size the number of 32-bit pseudorandom integers to be\n * generated.  size must be a multiple of 4, and greater than or equal\n * to (MEXP / 128 + 1) * 4.\n *\n * @note \\b memalign or \\b posix_memalign is available to get aligned\n * memory. Mac OSX doesn't have these functions, but \\b malloc of OSX\n * returns the pointer to the aligned memory block.\n */\nvoid fill_array32(sfmt_t *ctx, uint32_t *array, int size) {\n    assert(ctx->initialized);\n    assert(ctx->idx == N32);\n    assert(size % 4 == 0);\n    assert(size >= N32);\n\n    gen_rand_array(ctx, (w128_t *)array, size / 4);\n    ctx->idx = N32;\n}\n#endif\n\n/**\n * This function generates pseudorandom 64-bit integers in the\n * specified array[] by one call. The number of pseudorandom integers\n * is specified by the argument size, which must be at least 312 and a\n * multiple of two.  The generation by this function is much faster\n * than the following gen_rand function.\n *\n * For initialization, init_gen_rand or init_by_array must be called\n * before the first call of this function. This function can not be\n * used after calling gen_rand function, without initialization.\n *\n * @param array an array where pseudorandom 64-bit integers are filled\n * by this function.  The pointer to the array must be \"aligned\"\n * (namely, must be a multiple of 16) in the SIMD version, since it\n * refers to the address of a 128-bit integer.  In the standard C\n * version, the pointer is arbitrary.\n *\n * @param size the number of 64-bit pseudorandom integers to be\n * generated.  size must be a multiple of 2, and greater than or equal\n * to (MEXP / 128 + 1) * 2\n *\n * @note \\b memalign or \\b posix_memalign is available to get aligned\n * memory. Mac OSX doesn't have these functions, but \\b malloc of OSX\n * returns the pointer to the aligned memory block.\n */\nvoid fill_array64(sfmt_t *ctx, uint64_t *array, int size) {\n    assert(ctx->initialized);\n    assert(ctx->idx == N32);\n    assert(size % 2 == 0);\n    assert(size >= N64);\n\n    gen_rand_array(ctx, (w128_t *)array, size / 2);\n    ctx->idx = N32;\n\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\n    swap((w128_t *)array, size /2);\n#endif\n}\n\n/**\n * This function initializes the internal state array with a 32-bit\n * integer seed.\n *\n * @param seed a 32-bit integer used as the seed.\n */\nsfmt_t *init_gen_rand(uint32_t seed) {\n    void *p;\n    sfmt_t *ctx;\n    int i;\n    uint32_t *psfmt32;\n\n    if (posix_memalign(&p, sizeof(w128_t), sizeof(sfmt_t)) != 0) {\n\treturn NULL;\n    }\n    ctx = (sfmt_t *)p;\n    psfmt32 = &ctx->sfmt[0].u[0];\n\n    psfmt32[idxof(0)] = seed;\n    for (i = 1; i < N32; i++) {\n\tpsfmt32[idxof(i)] = 1812433253UL * (psfmt32[idxof(i - 1)]\n\t\t\t\t\t    ^ (psfmt32[idxof(i - 1)] >> 30))\n\t    + i;\n    }\n    ctx->idx = N32;\n    period_certification(ctx);\n    ctx->initialized = 1;\n\n    return ctx;\n}\n\n/**\n * This function initializes the internal state array,\n * with an array of 32-bit integers used as the seeds\n * @param init_key the array of 32-bit integers, used as a seed.\n * @param key_length the length of init_key.\n */\nsfmt_t *init_by_array(uint32_t *init_key, int key_length) {\n    void *p;\n    sfmt_t *ctx;\n    int i, j, count;\n    uint32_t r;\n    int lag;\n    int mid;\n    int size = N * 4;\n    uint32_t *psfmt32;\n\n    if (posix_memalign(&p, sizeof(w128_t), sizeof(sfmt_t)) != 0) {\n\treturn NULL;\n    }\n    ctx = (sfmt_t *)p;\n    psfmt32 = &ctx->sfmt[0].u[0];\n\n    if (size >= 623) {\n\tlag = 11;\n    } else if (size >= 68) {\n\tlag = 7;\n    } else if (size >= 39) {\n\tlag = 5;\n    } else {\n\tlag = 3;\n    }\n    mid = (size - lag) / 2;\n\n    memset(ctx->sfmt, 0x8b, sizeof(ctx->sfmt));\n    if (key_length + 1 > N32) {\n\tcount = key_length + 1;\n    } else {\n\tcount = N32;\n    }\n    r = func1(psfmt32[idxof(0)] ^ psfmt32[idxof(mid)]\n\t      ^ psfmt32[idxof(N32 - 1)]);\n    psfmt32[idxof(mid)] += r;\n    r += key_length;\n    psfmt32[idxof(mid + lag)] += r;\n    psfmt32[idxof(0)] = r;\n\n    count--;\n    for (i = 1, j = 0; (j < count) && (j < key_length); j++) {\n\tr = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)]\n\t\t  ^ psfmt32[idxof((i + N32 - 1) % N32)]);\n\tpsfmt32[idxof((i + mid) % N32)] += r;\n\tr += init_key[j] + i;\n\tpsfmt32[idxof((i + mid + lag) % N32)] += r;\n\tpsfmt32[idxof(i)] = r;\n\ti = (i + 1) % N32;\n    }\n    for (; j < count; j++) {\n\tr = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)]\n\t\t  ^ psfmt32[idxof((i + N32 - 1) % N32)]);\n\tpsfmt32[idxof((i + mid) % N32)] += r;\n\tr += i;\n\tpsfmt32[idxof((i + mid + lag) % N32)] += r;\n\tpsfmt32[idxof(i)] = r;\n\ti = (i + 1) % N32;\n    }\n    for (j = 0; j < N32; j++) {\n\tr = func2(psfmt32[idxof(i)] + psfmt32[idxof((i + mid) % N32)]\n\t\t  + psfmt32[idxof((i + N32 - 1) % N32)]);\n\tpsfmt32[idxof((i + mid) % N32)] ^= r;\n\tr -= i;\n\tpsfmt32[idxof((i + mid + lag) % N32)] ^= r;\n\tpsfmt32[idxof(i)] = r;\n\ti = (i + 1) % N32;\n    }\n\n    ctx->idx = N32;\n    period_certification(ctx);\n    ctx->initialized = 1;\n\n    return ctx;\n}\n\nvoid fini_gen_rand(sfmt_t *ctx) {\n    assert(ctx != NULL);\n\n    ctx->initialized = 0;\n    free(ctx);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/btalloc.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nvoid *\nbtalloc(size_t size, unsigned bits) {\n\treturn btalloc_0(size, bits);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/btalloc_0.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nbtalloc_n_gen(0)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/btalloc_1.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nbtalloc_n_gen(1)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/math.c",
    "content": "#define MATH_C_\n#include \"test/jemalloc_test.h\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/mq.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * Sleep for approximately ns nanoseconds.  No lower *nor* upper bound on sleep\n * time is guaranteed.\n */\nvoid\nmq_nanosleep(unsigned ns) {\n\tassert(ns <= 1000*1000*1000);\n\n#ifdef _WIN32\n\tSleep(ns / 1000);\n#else\n\t{\n\t\tstruct timespec timeout;\n\n\t\tif (ns < 1000*1000*1000) {\n\t\t\ttimeout.tv_sec = 0;\n\t\t\ttimeout.tv_nsec = ns;\n\t\t} else {\n\t\t\ttimeout.tv_sec = 1;\n\t\t\ttimeout.tv_nsec = 0;\n\t\t}\n\t\tnanosleep(&timeout, NULL);\n\t}\n#endif\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/mtx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#ifndef _CRT_SPINCOUNT\n#define _CRT_SPINCOUNT 4000\n#endif\n\nbool\nmtx_init(mtx_t *mtx) {\n#ifdef _WIN32\n\tif (!InitializeCriticalSectionAndSpinCount(&mtx->lock,\n\t    _CRT_SPINCOUNT)) {\n\t\treturn true;\n\t}\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tmtx->lock = OS_UNFAIR_LOCK_INIT;\n#elif (defined(JEMALLOC_OSSPIN))\n\tmtx->lock = 0;\n#else\n\tpthread_mutexattr_t attr;\n\n\tif (pthread_mutexattr_init(&attr) != 0) {\n\t\treturn true;\n\t}\n\tpthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);\n\tif (pthread_mutex_init(&mtx->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\nmtx_fini(mtx_t *mtx) {\n#ifdef _WIN32\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n#elif (defined(JEMALLOC_OSSPIN))\n#else\n\tpthread_mutex_destroy(&mtx->lock);\n#endif\n}\n\nvoid\nmtx_lock(mtx_t *mtx) {\n#ifdef _WIN32\n\tEnterCriticalSection(&mtx->lock);\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tos_unfair_lock_lock(&mtx->lock);\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLockLock(&mtx->lock);\n#else\n\tpthread_mutex_lock(&mtx->lock);\n#endif\n}\n\nvoid\nmtx_unlock(mtx_t *mtx) {\n#ifdef _WIN32\n\tLeaveCriticalSection(&mtx->lock);\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tos_unfair_lock_unlock(&mtx->lock);\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLockUnlock(&mtx->lock);\n#else\n\tpthread_mutex_unlock(&mtx->lock);\n#endif\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/test.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/* Test status state. */\n\nstatic unsigned\t\ttest_count = 0;\nstatic test_status_t\ttest_counts[test_status_count] = {0, 0, 0};\nstatic test_status_t\ttest_status = test_status_pass;\nstatic const char *\ttest_name = \"\";\n\n/* Reentrancy testing helpers. */\n\n#define NUM_REENTRANT_ALLOCS 20\ntypedef enum {\n\tnon_reentrant = 0,\n\tlibc_reentrant = 1,\n\tarena_new_reentrant = 2\n} reentrancy_t;\nstatic reentrancy_t reentrancy;\n\nstatic bool libc_hook_ran = false;\nstatic bool arena_new_hook_ran = false;\n\nstatic const char *\nreentrancy_t_str(reentrancy_t r) {\n\tswitch (r) {\n\tcase non_reentrant:\n\t\treturn \"non-reentrant\";\n\tcase libc_reentrant:\n\t\treturn \"libc-reentrant\";\n\tcase arena_new_reentrant:\n\t\treturn \"arena_new-reentrant\";\n\tdefault:\n\t\tunreachable();\n\t}\n}\n\nstatic void\ndo_hook(bool *hook_ran, void (**hook)()) {\n\t*hook_ran = true;\n\t*hook = NULL;\n\n\tsize_t alloc_size = 1;\n\tfor (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {\n\t\tfree(malloc(alloc_size));\n\t\talloc_size *= 2;\n\t}\n}\n\nstatic void\nlibc_reentrancy_hook() {\n\tdo_hook(&libc_hook_ran, &hooks_libc_hook);\n}\n\nstatic void\narena_new_reentrancy_hook() {\n\tdo_hook(&arena_new_hook_ran, &hooks_arena_new_hook);\n}\n\n/* Actual test infrastructure. */\nbool\ntest_is_reentrant() {\n\treturn reentrancy != non_reentrant;\n}\n\nJEMALLOC_FORMAT_PRINTF(1, 2)\nvoid\ntest_skip(const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n\tmalloc_printf(\"\\n\");\n\ttest_status = test_status_skip;\n}\n\nJEMALLOC_FORMAT_PRINTF(1, 2)\nvoid\ntest_fail(const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n\tmalloc_printf(\"\\n\");\n\ttest_status = test_status_fail;\n}\n\nstatic const char *\ntest_status_string(test_status_t test_status) {\n\tswitch (test_status) {\n\tcase test_status_pass: return \"pass\";\n\tcase test_status_skip: return \"skip\";\n\tcase test_status_fail: return \"fail\";\n\tdefault: not_reached();\n\t}\n}\n\nvoid\np_test_init(const char *name) {\n\ttest_count++;\n\ttest_status = test_status_pass;\n\ttest_name = name;\n}\n\nvoid\np_test_fini(void) {\n\ttest_counts[test_status]++;\n\tmalloc_printf(\"%s (%s): %s\\n\", test_name, reentrancy_t_str(reentrancy),\n\t    test_status_string(test_status));\n}\n\nstatic test_status_t\np_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {\n\ttest_status_t ret;\n\n\tif (do_malloc_init) {\n\t\t/*\n\t\t * Make sure initialization occurs prior to running tests.\n\t\t * Tests are special because they may use internal facilities\n\t\t * prior to triggering initialization as a side effect of\n\t\t * calling into the public API.\n\t\t */\n\t\tif (nallocx(1, 0) == 0) {\n\t\t\tmalloc_printf(\"Initialization error\");\n\t\t\treturn test_status_fail;\n\t\t}\n\t}\n\n\tret = test_status_pass;\n\tfor (; t != NULL; t = va_arg(ap, test_t *)) {\n\t\t/* Non-reentrant run. */\n\t\treentrancy = non_reentrant;\n\t\thooks_arena_new_hook = hooks_libc_hook = NULL;\n\t\tt();\n\t\tif (test_status > ret) {\n\t\t\tret = test_status;\n\t\t}\n\t\t/* Reentrant run. */\n\t\tif (do_reentrant) {\n\t\t\treentrancy = libc_reentrant;\n\t\t\thooks_arena_new_hook = NULL;\n\t\t\thooks_libc_hook = &libc_reentrancy_hook;\n\t\t\tt();\n\t\t\tif (test_status > ret) {\n\t\t\t\tret = test_status;\n\t\t\t}\n\n\t\t\treentrancy = arena_new_reentrant;\n\t\t\thooks_libc_hook = NULL;\n\t\t\thooks_arena_new_hook = &arena_new_reentrancy_hook;\n\t\t\tt();\n\t\t\tif (test_status > ret) {\n\t\t\t\tret = test_status;\n\t\t\t}\n\t\t}\n\t}\n\n\tmalloc_printf(\"--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\\n\",\n\t    test_status_string(test_status_pass),\n\t    test_counts[test_status_pass], test_count,\n\t    test_status_string(test_status_skip),\n\t    test_counts[test_status_skip], test_count,\n\t    test_status_string(test_status_fail),\n\t    test_counts[test_status_fail], test_count);\n\n\treturn ret;\n}\n\ntest_status_t\np_test(test_t *t, ...) {\n\ttest_status_t ret;\n\tva_list ap;\n\n\tret = test_status_pass;\n\tva_start(ap, t);\n\tret = p_test_impl(true, true, t, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\ntest_status_t\np_test_no_reentrancy(test_t *t, ...) {\n\ttest_status_t ret;\n\tva_list ap;\n\n\tret = test_status_pass;\n\tva_start(ap, t);\n\tret = p_test_impl(true, false, t, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\ntest_status_t\np_test_no_malloc_init(test_t *t, ...) {\n\ttest_status_t ret;\n\tva_list ap;\n\n\tret = test_status_pass;\n\tva_start(ap, t);\n\t/*\n\t * We also omit reentrancy from bootstrapping tests, since we don't\n\t * (yet) care about general reentrancy during bootstrapping.\n\t */\n\tret = p_test_impl(false, false, t, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\nvoid\np_test_fail(const char *prefix, const char *message) {\n\tmalloc_cprintf(NULL, NULL, \"%s%s\\n\", prefix, message);\n\ttest_status = test_status_fail;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/thd.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#ifdef _WIN32\nvoid\nthd_create(thd_t *thd, void *(*proc)(void *), void *arg) {\n\tLPTHREAD_START_ROUTINE routine = (LPTHREAD_START_ROUTINE)proc;\n\t*thd = CreateThread(NULL, 0, routine, arg, 0, NULL);\n\tif (*thd == NULL) {\n\t\ttest_fail(\"Error in CreateThread()\\n\");\n\t}\n}\n\nvoid\nthd_join(thd_t thd, void **ret) {\n\tif (WaitForSingleObject(thd, INFINITE) == WAIT_OBJECT_0 && ret) {\n\t\tDWORD exit_code;\n\t\tGetExitCodeThread(thd, (LPDWORD) &exit_code);\n\t\t*ret = (void *)(uintptr_t)exit_code;\n\t}\n}\n\n#else\nvoid\nthd_create(thd_t *thd, void *(*proc)(void *), void *arg) {\n\tif (pthread_create(thd, NULL, proc, arg) != 0) {\n\t\ttest_fail(\"Error in pthread_create()\\n\");\n\t}\n}\n\nvoid\nthd_join(thd_t thd, void **ret) {\n\tpthread_join(thd, ret);\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/src/timer.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nvoid\ntimer_start(timedelta_t *timer) {\n\tnstime_init(&timer->t0, 0);\n\tnstime_update(&timer->t0);\n}\n\nvoid\ntimer_stop(timedelta_t *timer) {\n\tnstime_copy(&timer->t1, &timer->t0);\n\tnstime_update(&timer->t1);\n}\n\nuint64_t\ntimer_usec(const timedelta_t *timer) {\n\tnstime_t delta;\n\n\tnstime_copy(&delta, &timer->t1);\n\tnstime_subtract(&delta, &timer->t0);\n\treturn nstime_ns(&delta) / 1000;\n}\n\nvoid\ntimer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen) {\n\tuint64_t t0 = timer_usec(a);\n\tuint64_t t1 = timer_usec(b);\n\tuint64_t mult;\n\tsize_t i = 0;\n\tsize_t j, n;\n\n\t/* Whole. */\n\tn = malloc_snprintf(&buf[i], buflen-i, \"%\"FMTu64, t0 / t1);\n\ti += n;\n\tif (i >= buflen) {\n\t\treturn;\n\t}\n\tmult = 1;\n\tfor (j = 0; j < n; j++) {\n\t\tmult *= 10;\n\t}\n\n\t/* Decimal. */\n\tn = malloc_snprintf(&buf[i], buflen-i, \".\");\n\ti += n;\n\n\t/* Fraction. */\n\twhile (i < buflen-1) {\n\t\tuint64_t round = (i+1 == buflen-1 && ((t0 * mult * 10 / t1) % 10\n\t\t    >= 5)) ? 1 : 0;\n\t\tn = malloc_snprintf(&buf[i], buflen-i,\n\t\t    \"%\"FMTu64, (t0 * mult / t1) % 10 + round);\n\t\ti += n;\n\t\tmult *= 10;\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/stress/microbench.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic inline void\ntime_func(timedelta_t *timer, uint64_t nwarmup, uint64_t niter,\n    void (*func)(void)) {\n\tuint64_t i;\n\n\tfor (i = 0; i < nwarmup; i++) {\n\t\tfunc();\n\t}\n\ttimer_start(timer);\n\tfor (i = 0; i < niter; i++) {\n\t\tfunc();\n\t}\n\ttimer_stop(timer);\n}\n\nvoid\ncompare_funcs(uint64_t nwarmup, uint64_t niter, const char *name_a,\n    void (*func_a), const char *name_b, void (*func_b)) {\n\ttimedelta_t timer_a, timer_b;\n\tchar ratio_buf[6];\n\tvoid *p;\n\n\tp = mallocx(1, 0);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected mallocx() failure\");\n\t\treturn;\n\t}\n\n\ttime_func(&timer_a, nwarmup, niter, func_a);\n\ttime_func(&timer_b, nwarmup, niter, func_b);\n\n\ttimer_ratio(&timer_a, &timer_b, ratio_buf, sizeof(ratio_buf));\n\tmalloc_printf(\"%\"FMTu64\" iterations, %s=%\"FMTu64\"us, \"\n\t    \"%s=%\"FMTu64\"us, ratio=1:%s\\n\",\n\t    niter, name_a, timer_usec(&timer_a), name_b, timer_usec(&timer_b),\n\t    ratio_buf);\n\n\tdallocx(p, 0);\n}\n\nstatic void\nmalloc_free(void) {\n\t/* The compiler can optimize away free(malloc(1))! */\n\tvoid *p = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tfree(p);\n}\n\nstatic void\nmallocx_free(void) {\n\tvoid *p = mallocx(1, 0);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected mallocx() failure\");\n\t\treturn;\n\t}\n\tfree(p);\n}\n\nTEST_BEGIN(test_malloc_vs_mallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"malloc\",\n\t    malloc_free, \"mallocx\", mallocx_free);\n}\nTEST_END\n\nstatic void\nmalloc_dallocx(void) {\n\tvoid *p = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tdallocx(p, 0);\n}\n\nstatic void\nmalloc_sdallocx(void) {\n\tvoid *p = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tsdallocx(p, 1, 0);\n}\n\nTEST_BEGIN(test_free_vs_dallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"free\", malloc_free,\n\t    \"dallocx\", malloc_dallocx);\n}\nTEST_END\n\nTEST_BEGIN(test_dallocx_vs_sdallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"dallocx\", malloc_dallocx,\n\t    \"sdallocx\", malloc_sdallocx);\n}\nTEST_END\n\nstatic void\nmalloc_mus_free(void) {\n\tvoid *p;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tmalloc_usable_size(p);\n\tfree(p);\n}\n\nstatic void\nmalloc_sallocx_free(void) {\n\tvoid *p;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tif (sallocx(p, 0) < 1) {\n\t\ttest_fail(\"Unexpected sallocx() failure\");\n\t}\n\tfree(p);\n}\n\nTEST_BEGIN(test_mus_vs_sallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"malloc_usable_size\",\n\t    malloc_mus_free, \"sallocx\", malloc_sallocx_free);\n}\nTEST_END\n\nstatic void\nmalloc_nallocx_free(void) {\n\tvoid *p;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tif (nallocx(1, 0) < 1) {\n\t\ttest_fail(\"Unexpected nallocx() failure\");\n\t}\n\tfree(p);\n}\n\nTEST_BEGIN(test_sallocx_vs_nallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"sallocx\",\n\t    malloc_sallocx_free, \"nallocx\", malloc_nallocx_free);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_malloc_vs_mallocx,\n\t    test_free_vs_dallocx,\n\t    test_dallocx_vs_sdallocx,\n\t    test_mus_vs_sallocx,\n\t    test_sallocx_vs_nallocx);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/test.sh.in",
    "content": "#!/bin/sh\n\ncase @abi@ in\n  macho)\n    export DYLD_FALLBACK_LIBRARY_PATH=\"@objroot@lib\"\n    ;;\n  pecoff)\n    export PATH=\"${PATH}:@objroot@lib\"\n    ;;\n  *)\n    ;;\nesac\n\n# Make a copy of the @JEMALLOC_CPREFIX@MALLOC_CONF passed in to this script, so\n# it can be repeatedly concatenated with per test settings.\nexport MALLOC_CONF_ALL=${@JEMALLOC_CPREFIX@MALLOC_CONF}\n# Concatenate the individual test's MALLOC_CONF and MALLOC_CONF_ALL.\nexport_malloc_conf() {\n  if [ \"x${MALLOC_CONF}\" != \"x\" -a \"x${MALLOC_CONF_ALL}\" != \"x\" ] ; then\n    export @JEMALLOC_CPREFIX@MALLOC_CONF=\"${MALLOC_CONF},${MALLOC_CONF_ALL}\"\n  else\n    export @JEMALLOC_CPREFIX@MALLOC_CONF=\"${MALLOC_CONF}${MALLOC_CONF_ALL}\"\n  fi\n}\n\n# Corresponds to test_status_t.\npass_code=0\nskip_code=1\nfail_code=2\n\npass_count=0\nskip_count=0\nfail_count=0\nfor t in $@; do\n  if [ $pass_count -ne 0 -o $skip_count -ne 0 -o $fail_count != 0 ] ; then\n    echo\n  fi\n  echo \"=== ${t} ===\"\n  if [ -e \"@srcroot@${t}.sh\" ] ; then\n    # Source the shell script corresponding to the test in a subshell and\n    # execute the test.  This allows the shell script to set MALLOC_CONF, which\n    # is then used to set @JEMALLOC_CPREFIX@MALLOC_CONF (thus allowing the\n    # per test shell script to ignore the @JEMALLOC_CPREFIX@ detail).\n    enable_fill=@enable_fill@ \\\n    enable_prof=@enable_prof@ \\\n    . @srcroot@${t}.sh && \\\n    export_malloc_conf && \\\n    $JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@\n  else\n    export MALLOC_CONF= && \\\n    export_malloc_conf && \\\n    $JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@\n  fi\n  result_code=$?\n  case ${result_code} in\n    ${pass_code})\n      pass_count=$((pass_count+1))\n      ;;\n    ${skip_code})\n      skip_count=$((skip_count+1))\n      ;;\n    ${fail_code})\n      fail_count=$((fail_count+1))\n      ;;\n    *)\n      echo \"Test harness error: ${t} w/ MALLOC_CONF=\\\"${MALLOC_CONF}\\\"\" 1>&2\n      echo \"Use prefix to debug, e.g. JEMALLOC_TEST_PREFIX=\\\"gdb --args\\\" sh test/test.sh ${t}\" 1>&2\n      exit 1\n  esac\ndone\n\ntotal_count=`expr ${pass_count} + ${skip_count} + ${fail_count}`\necho\necho \"Test suite summary: pass: ${pass_count}/${total_count}, skip: ${skip_count}/${total_count}, fail: ${fail_count}/${total_count}\"\n\nif [ ${fail_count} -eq 0 ] ; then\n  exit 0\nelse\n  exit 1\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/SFMT.c",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#include \"test/jemalloc_test.h\"\n\n#define BLOCK_SIZE 10000\n#define BLOCK_SIZE64 (BLOCK_SIZE / 2)\n#define COUNT_1 1000\n#define COUNT_2 700\n\nstatic const uint32_t init_gen_rand_32_expected[] = {\n\t3440181298U, 1564997079U, 1510669302U, 2930277156U, 1452439940U,\n\t3796268453U,  423124208U, 2143818589U, 3827219408U, 2987036003U,\n\t2674978610U, 1536842514U, 2027035537U, 2534897563U, 1686527725U,\n\t 545368292U, 1489013321U, 1370534252U, 4231012796U, 3994803019U,\n\t1764869045U,  824597505U,  862581900U, 2469764249U,  812862514U,\n\t 359318673U,  116957936U, 3367389672U, 2327178354U, 1898245200U,\n\t3206507879U, 2378925033U, 1040214787U, 2524778605U, 3088428700U,\n\t1417665896U,  964324147U, 2282797708U, 2456269299U,  313400376U,\n\t2245093271U, 1015729427U, 2694465011U, 3246975184U, 1992793635U,\n\t 463679346U, 3721104591U, 3475064196U,  856141236U, 1499559719U,\n\t3522818941U, 3721533109U, 1954826617U, 1282044024U, 1543279136U,\n\t1301863085U, 2669145051U, 4221477354U, 3896016841U, 3392740262U,\n\t 462466863U, 1037679449U, 1228140306U,  922298197U, 1205109853U,\n\t1872938061U, 3102547608U, 2742766808U, 1888626088U, 4028039414U,\n\t 157593879U, 1136901695U, 4038377686U, 3572517236U, 4231706728U,\n\t2997311961U, 1189931652U, 3981543765U, 2826166703U,   87159245U,\n\t1721379072U, 3897926942U, 1790395498U, 2569178939U, 1047368729U,\n\t2340259131U, 3144212906U, 2301169789U, 2442885464U, 3034046771U,\n\t3667880593U, 3935928400U, 2372805237U, 1666397115U, 2460584504U,\n\t 513866770U, 3810869743U, 2147400037U, 2792078025U, 2941761810U,\n\t3212265810U,  984692259U,  346590253U, 1804179199U, 3298543443U,\n\t 750108141U, 2880257022U,  243310542U, 1869036465U, 1588062513U,\n\t2983949551U, 1931450364U, 4034505847U, 2735030199U, 1628461061U,\n\t2539522841U,  127965585U, 3992448871U,  913388237U,  559130076U,\n\t1202933193U, 4087643167U, 2590021067U, 2256240196U, 1746697293U,\n\t1013913783U, 1155864921U, 2715773730U,  915061862U, 1948766573U,\n\t2322882854U, 3761119102U, 1343405684U, 3078711943U, 3067431651U,\n\t3245156316U, 3588354584U, 3484623306U, 3899621563U, 4156689741U,\n\t3237090058U, 3880063844U,  862416318U, 4039923869U, 2303788317U,\n\t3073590536U,  701653667U, 2131530884U, 3169309950U, 2028486980U,\n\t 747196777U, 3620218225U,  432016035U, 1449580595U, 2772266392U,\n\t 444224948U, 1662832057U, 3184055582U, 3028331792U, 1861686254U,\n\t1104864179U,  342430307U, 1350510923U, 3024656237U, 1028417492U,\n\t2870772950U,  290847558U, 3675663500U,  508431529U, 4264340390U,\n\t2263569913U, 1669302976U,  519511383U, 2706411211U, 3764615828U,\n\t3883162495U, 4051445305U, 2412729798U, 3299405164U, 3991911166U,\n\t2348767304U, 2664054906U, 3763609282U,  593943581U, 3757090046U,\n\t2075338894U, 2020550814U, 4287452920U, 4290140003U, 1422957317U,\n\t2512716667U, 2003485045U, 2307520103U, 2288472169U, 3940751663U,\n\t4204638664U, 2892583423U, 1710068300U, 3904755993U, 2363243951U,\n\t3038334120U,  547099465U,  771105860U, 3199983734U, 4282046461U,\n\t2298388363U,  934810218U, 2837827901U, 3952500708U, 2095130248U,\n\t3083335297U,   26885281U, 3932155283U, 1531751116U, 1425227133U,\n\t 495654159U, 3279634176U, 3855562207U, 3957195338U, 4159985527U,\n\t 893375062U, 1875515536U, 1327247422U, 3754140693U, 1028923197U,\n\t1729880440U,  805571298U,  448971099U, 2726757106U, 2749436461U,\n\t2485987104U,  175337042U, 3235477922U, 3882114302U, 2020970972U,\n\t 943926109U, 2762587195U, 1904195558U, 3452650564U,  108432281U,\n\t3893463573U, 3977583081U, 2636504348U, 1110673525U, 3548479841U,\n\t4258854744U,  980047703U, 4057175418U, 3890008292U,  145653646U,\n\t3141868989U, 3293216228U, 1194331837U, 1254570642U, 3049934521U,\n\t2868313360U, 2886032750U, 1110873820U,  279553524U, 3007258565U,\n\t1104807822U, 3186961098U,  315764646U, 2163680838U, 3574508994U,\n\t3099755655U,  191957684U, 3642656737U, 3317946149U, 3522087636U,\n\t 444526410U,  779157624U, 1088229627U, 1092460223U, 1856013765U,\n\t3659877367U,  368270451U,  503570716U, 3000984671U, 2742789647U,\n\t 928097709U, 2914109539U,  308843566U, 2816161253U, 3667192079U,\n\t2762679057U, 3395240989U, 2928925038U, 1491465914U, 3458702834U,\n\t3787782576U, 2894104823U, 1296880455U, 1253636503U,  989959407U,\n\t2291560361U, 2776790436U, 1913178042U, 1584677829U,  689637520U,\n\t1898406878U,  688391508U, 3385234998U,  845493284U, 1943591856U,\n\t2720472050U,  222695101U, 1653320868U, 2904632120U, 4084936008U,\n\t1080720688U, 3938032556U,  387896427U, 2650839632U,   99042991U,\n\t1720913794U, 1047186003U, 1877048040U, 2090457659U,  517087501U,\n\t4172014665U, 2129713163U, 2413533132U, 2760285054U, 4129272496U,\n\t1317737175U, 2309566414U, 2228873332U, 3889671280U, 1110864630U,\n\t3576797776U, 2074552772U,  832002644U, 3097122623U, 2464859298U,\n\t2679603822U, 1667489885U, 3237652716U, 1478413938U, 1719340335U,\n\t2306631119U,  639727358U, 3369698270U,  226902796U, 2099920751U,\n\t1892289957U, 2201594097U, 3508197013U, 3495811856U, 3900381493U,\n\t 841660320U, 3974501451U, 3360949056U, 1676829340U,  728899254U,\n\t2047809627U, 2390948962U,  670165943U, 3412951831U, 4189320049U,\n\t1911595255U, 2055363086U,  507170575U,  418219594U, 4141495280U,\n\t2692088692U, 4203630654U, 3540093932U,  791986533U, 2237921051U,\n\t2526864324U, 2956616642U, 1394958700U, 1983768223U, 1893373266U,\n\t 591653646U,  228432437U, 1611046598U, 3007736357U, 1040040725U,\n\t2726180733U, 2789804360U, 4263568405U,  829098158U, 3847722805U,\n\t1123578029U, 1804276347U,  997971319U, 4203797076U, 4185199713U,\n\t2811733626U, 2343642194U, 2985262313U, 1417930827U, 3759587724U,\n\t1967077982U, 1585223204U, 1097475516U, 1903944948U,  740382444U,\n\t1114142065U, 1541796065U, 1718384172U, 1544076191U, 1134682254U,\n\t3519754455U, 2866243923U,  341865437U,  645498576U, 2690735853U,\n\t1046963033U, 2493178460U, 1187604696U, 1619577821U,  488503634U,\n\t3255768161U, 2306666149U, 1630514044U, 2377698367U, 2751503746U,\n\t3794467088U, 1796415981U, 3657173746U,  409136296U, 1387122342U,\n\t1297726519U,  219544855U, 4270285558U,  437578827U, 1444698679U,\n\t2258519491U,  963109892U, 3982244073U, 3351535275U,  385328496U,\n\t1804784013U,  698059346U, 3920535147U,  708331212U,  784338163U,\n\t 785678147U, 1238376158U, 1557298846U, 2037809321U,  271576218U,\n\t4145155269U, 1913481602U, 2763691931U,  588981080U, 1201098051U,\n\t3717640232U, 1509206239U,  662536967U, 3180523616U, 1133105435U,\n\t2963500837U, 2253971215U, 3153642623U, 1066925709U, 2582781958U,\n\t3034720222U, 1090798544U, 2942170004U, 4036187520U,  686972531U,\n\t2610990302U, 2641437026U, 1837562420U,  722096247U, 1315333033U,\n\t2102231203U, 3402389208U, 3403698140U, 1312402831U, 2898426558U,\n\t 814384596U,  385649582U, 1916643285U, 1924625106U, 2512905582U,\n\t2501170304U, 4275223366U, 2841225246U, 1467663688U, 3563567847U,\n\t2969208552U,  884750901U,  102992576U,  227844301U, 3681442994U,\n\t3502881894U, 4034693299U, 1166727018U, 1697460687U, 1737778332U,\n\t1787161139U, 1053003655U, 1215024478U, 2791616766U, 2525841204U,\n\t1629323443U,    3233815U, 2003823032U, 3083834263U, 2379264872U,\n\t3752392312U, 1287475550U, 3770904171U, 3004244617U, 1502117784U,\n\t 918698423U, 2419857538U, 3864502062U, 1751322107U, 2188775056U,\n\t4018728324U,  983712955U,  440071928U, 3710838677U, 2001027698U,\n\t3994702151U,   22493119U, 3584400918U, 3446253670U, 4254789085U,\n\t1405447860U, 1240245579U, 1800644159U, 1661363424U, 3278326132U,\n\t3403623451U,   67092802U, 2609352193U, 3914150340U, 1814842761U,\n\t3610830847U,  591531412U, 3880232807U, 1673505890U, 2585326991U,\n\t1678544474U, 3148435887U, 3457217359U, 1193226330U, 2816576908U,\n\t 154025329U,  121678860U, 1164915738U,  973873761U,  269116100U,\n\t  52087970U,  744015362U,  498556057U,   94298882U, 1563271621U,\n\t2383059628U, 4197367290U, 3958472990U, 2592083636U, 2906408439U,\n\t1097742433U, 3924840517U,  264557272U, 2292287003U, 3203307984U,\n\t4047038857U, 3820609705U, 2333416067U, 1839206046U, 3600944252U,\n\t3412254904U,  583538222U, 2390557166U, 4140459427U, 2810357445U,\n\t 226777499U, 2496151295U, 2207301712U, 3283683112U,  611630281U,\n\t1933218215U, 3315610954U, 3889441987U, 3719454256U, 3957190521U,\n\t1313998161U, 2365383016U, 3146941060U, 1801206260U,  796124080U,\n\t2076248581U, 1747472464U, 3254365145U,  595543130U, 3573909503U,\n\t3758250204U, 2020768540U, 2439254210U,   93368951U, 3155792250U,\n\t2600232980U, 3709198295U, 3894900440U, 2971850836U, 1578909644U,\n\t1443493395U, 2581621665U, 3086506297U, 2443465861U,  558107211U,\n\t1519367835U,  249149686U,  908102264U, 2588765675U, 1232743965U,\n\t1001330373U, 3561331654U, 2259301289U, 1564977624U, 3835077093U,\n\t 727244906U, 4255738067U, 1214133513U, 2570786021U, 3899704621U,\n\t1633861986U, 1636979509U, 1438500431U,   58463278U, 2823485629U,\n\t2297430187U, 2926781924U, 3371352948U, 1864009023U, 2722267973U,\n\t1444292075U,  437703973U, 1060414512U,  189705863U,  910018135U,\n\t4077357964U,  884213423U, 2644986052U, 3973488374U, 1187906116U,\n\t2331207875U,  780463700U, 3713351662U, 3854611290U,  412805574U,\n\t2978462572U, 2176222820U,  829424696U, 2790788332U, 2750819108U,\n\t1594611657U, 3899878394U, 3032870364U, 1702887682U, 1948167778U,\n\t  14130042U,  192292500U,  947227076U,   90719497U, 3854230320U,\n\t 784028434U, 2142399787U, 1563449646U, 2844400217U,  819143172U,\n\t2883302356U, 2328055304U, 1328532246U, 2603885363U, 3375188924U,\n\t 933941291U, 3627039714U, 2129697284U, 2167253953U, 2506905438U,\n\t1412424497U, 2981395985U, 1418359660U, 2925902456U,   52752784U,\n\t3713667988U, 3924669405U,  648975707U, 1145520213U, 4018650664U,\n\t3805915440U, 2380542088U, 2013260958U, 3262572197U, 2465078101U,\n\t1114540067U, 3728768081U, 2396958768U,  590672271U,  904818725U,\n\t4263660715U,  700754408U, 1042601829U, 4094111823U, 4274838909U,\n\t2512692617U, 2774300207U, 2057306915U, 3470942453U,   99333088U,\n\t1142661026U, 2889931380U,   14316674U, 2201179167U,  415289459U,\n\t 448265759U, 3515142743U, 3254903683U,  246633281U, 1184307224U,\n\t2418347830U, 2092967314U, 2682072314U, 2558750234U, 2000352263U,\n\t1544150531U,  399010405U, 1513946097U,  499682937U,  461167460U,\n\t3045570638U, 1633669705U,  851492362U, 4052801922U, 2055266765U,\n\t 635556996U,  368266356U, 2385737383U, 3218202352U, 2603772408U,\n\t 349178792U,  226482567U, 3102426060U, 3575998268U, 2103001871U,\n\t3243137071U,  225500688U, 1634718593U, 4283311431U, 4292122923U,\n\t3842802787U,  811735523U,  105712518U,  663434053U, 1855889273U,\n\t2847972595U, 1196355421U, 2552150115U, 4254510614U, 3752181265U,\n\t3430721819U, 3828705396U, 3436287905U, 3441964937U, 4123670631U,\n\t 353001539U,  459496439U, 3799690868U, 1293777660U, 2761079737U,\n\t 498096339U, 3398433374U, 4080378380U, 2304691596U, 2995729055U,\n\t4134660419U, 3903444024U, 3576494993U,  203682175U, 3321164857U,\n\t2747963611U,   79749085U, 2992890370U, 1240278549U, 1772175713U,\n\t2111331972U, 2655023449U, 1683896345U, 2836027212U, 3482868021U,\n\t2489884874U,  756853961U, 2298874501U, 4013448667U, 4143996022U,\n\t2948306858U, 4132920035U, 1283299272U,  995592228U, 3450508595U,\n\t1027845759U, 1766942720U, 3861411826U, 1446861231U,   95974993U,\n\t3502263554U, 1487532194U,  601502472U, 4129619129U,  250131773U,\n\t2050079547U, 3198903947U, 3105589778U, 4066481316U, 3026383978U,\n\t2276901713U,  365637751U, 2260718426U, 1394775634U, 1791172338U,\n\t2690503163U, 2952737846U, 1568710462U,  732623190U, 2980358000U,\n\t1053631832U, 1432426951U, 3229149635U, 1854113985U, 3719733532U,\n\t3204031934U,  735775531U,  107468620U, 3734611984U,  631009402U,\n\t3083622457U, 4109580626U,  159373458U, 1301970201U, 4132389302U,\n\t1293255004U,  847182752U, 4170022737U,   96712900U, 2641406755U,\n\t1381727755U,  405608287U, 4287919625U, 1703554290U, 3589580244U,\n\t2911403488U,    2166565U, 2647306451U, 2330535117U, 1200815358U,\n\t1165916754U,  245060911U, 4040679071U, 3684908771U, 2452834126U,\n\t2486872773U, 2318678365U, 2940627908U, 1837837240U, 3447897409U,\n\t4270484676U, 1495388728U, 3754288477U, 4204167884U, 1386977705U,\n\t2692224733U, 3076249689U, 4109568048U, 4170955115U, 4167531356U,\n\t4020189950U, 4261855038U, 3036907575U, 3410399885U, 3076395737U,\n\t1046178638U,  144496770U,  230725846U, 3349637149U,   17065717U,\n\t2809932048U, 2054581785U, 3608424964U, 3259628808U,  134897388U,\n\t3743067463U,  257685904U, 3795656590U, 1562468719U, 3589103904U,\n\t3120404710U,  254684547U, 2653661580U, 3663904795U, 2631942758U,\n\t1063234347U, 2609732900U, 2332080715U, 3521125233U, 1180599599U,\n\t1935868586U, 4110970440U,  296706371U, 2128666368U, 1319875791U,\n\t1570900197U, 3096025483U, 1799882517U, 1928302007U, 1163707758U,\n\t1244491489U, 3533770203U,  567496053U, 2757924305U, 2781639343U,\n\t2818420107U,  560404889U, 2619609724U, 4176035430U, 2511289753U,\n\t2521842019U, 3910553502U, 2926149387U, 3302078172U, 4237118867U,\n\t 330725126U,  367400677U,  888239854U,  545570454U, 4259590525U,\n\t 134343617U, 1102169784U, 1647463719U, 3260979784U, 1518840883U,\n\t3631537963U, 3342671457U, 1301549147U, 2083739356U,  146593792U,\n\t3217959080U,  652755743U, 2032187193U, 3898758414U, 1021358093U,\n\t4037409230U, 2176407931U, 3427391950U, 2883553603U,  985613827U,\n\t3105265092U, 3423168427U, 3387507672U,  467170288U, 2141266163U,\n\t3723870208U,  916410914U, 1293987799U, 2652584950U,  769160137U,\n\t3205292896U, 1561287359U, 1684510084U, 3136055621U, 3765171391U,\n\t 639683232U, 2639569327U, 1218546948U, 4263586685U, 3058215773U,\n\t2352279820U,  401870217U, 2625822463U, 1529125296U, 2981801895U,\n\t1191285226U, 4027725437U, 3432700217U, 4098835661U,  971182783U,\n\t2443861173U, 3881457123U, 3874386651U,  457276199U, 2638294160U,\n\t4002809368U,  421169044U, 1112642589U, 3076213779U, 3387033971U,\n\t2499610950U, 3057240914U, 1662679783U,  461224431U, 1168395933U\n};\nstatic const uint32_t init_by_array_32_expected[] = {\n\t2920711183U, 3885745737U, 3501893680U,  856470934U, 1421864068U,\n\t 277361036U, 1518638004U, 2328404353U, 3355513634U,   64329189U,\n\t1624587673U, 3508467182U, 2481792141U, 3706480799U, 1925859037U,\n\t2913275699U,  882658412U,  384641219U,  422202002U, 1873384891U,\n\t2006084383U, 3924929912U, 1636718106U, 3108838742U, 1245465724U,\n\t4195470535U,  779207191U, 1577721373U, 1390469554U, 2928648150U,\n\t 121399709U, 3170839019U, 4044347501U,  953953814U, 3821710850U,\n\t3085591323U, 3666535579U, 3577837737U, 2012008410U, 3565417471U,\n\t4044408017U,  433600965U, 1637785608U, 1798509764U,  860770589U,\n\t3081466273U, 3982393409U, 2451928325U, 3437124742U, 4093828739U,\n\t3357389386U, 2154596123U,  496568176U, 2650035164U, 2472361850U,\n\t   3438299U, 2150366101U, 1577256676U, 3802546413U, 1787774626U,\n\t4078331588U, 3706103141U,  170391138U, 3806085154U, 1680970100U,\n\t1961637521U, 3316029766U,  890610272U, 1453751581U, 1430283664U,\n\t3051057411U, 3597003186U,  542563954U, 3796490244U, 1690016688U,\n\t3448752238U,  440702173U,  347290497U, 1121336647U, 2540588620U,\n\t 280881896U, 2495136428U,  213707396U,   15104824U, 2946180358U,\n\t 659000016U,  566379385U, 2614030979U, 2855760170U,  334526548U,\n\t2315569495U, 2729518615U,  564745877U, 1263517638U, 3157185798U,\n\t1604852056U, 1011639885U, 2950579535U, 2524219188U,  312951012U,\n\t1528896652U, 1327861054U, 2846910138U, 3966855905U, 2536721582U,\n\t 855353911U, 1685434729U, 3303978929U, 1624872055U, 4020329649U,\n\t3164802143U, 1642802700U, 1957727869U, 1792352426U, 3334618929U,\n\t2631577923U, 3027156164U,  842334259U, 3353446843U, 1226432104U,\n\t1742801369U, 3552852535U, 3471698828U, 1653910186U, 3380330939U,\n\t2313782701U, 3351007196U, 2129839995U, 1800682418U, 4085884420U,\n\t1625156629U, 3669701987U,  615211810U, 3294791649U, 4131143784U,\n\t2590843588U, 3207422808U, 3275066464U,  561592872U, 3957205738U,\n\t3396578098U,   48410678U, 3505556445U, 1005764855U, 3920606528U,\n\t2936980473U, 2378918600U, 2404449845U, 1649515163U,  701203563U,\n\t3705256349U,   83714199U, 3586854132U,  922978446U, 2863406304U,\n\t3523398907U, 2606864832U, 2385399361U, 3171757816U, 4262841009U,\n\t3645837721U, 1169579486U, 3666433897U, 3174689479U, 1457866976U,\n\t3803895110U, 3346639145U, 1907224409U, 1978473712U, 1036712794U,\n\t 980754888U, 1302782359U, 1765252468U,  459245755U, 3728923860U,\n\t1512894209U, 2046491914U,  207860527U,  514188684U, 2288713615U,\n\t1597354672U, 3349636117U, 2357291114U, 3995796221U,  945364213U,\n\t1893326518U, 3770814016U, 1691552714U, 2397527410U,  967486361U,\n\t 776416472U, 4197661421U,  951150819U, 1852770983U, 4044624181U,\n\t1399439738U, 4194455275U, 2284037669U, 1550734958U, 3321078108U,\n\t1865235926U, 2912129961U, 2664980877U, 1357572033U, 2600196436U,\n\t2486728200U, 2372668724U, 1567316966U, 2374111491U, 1839843570U,\n\t  20815612U, 3727008608U, 3871996229U,  824061249U, 1932503978U,\n\t3404541726U,  758428924U, 2609331364U, 1223966026U, 1299179808U,\n\t 648499352U, 2180134401U,  880821170U, 3781130950U,  113491270U,\n\t1032413764U, 4185884695U, 2490396037U, 1201932817U, 4060951446U,\n\t4165586898U, 1629813212U, 2887821158U,  415045333U,  628926856U,\n\t2193466079U, 3391843445U, 2227540681U, 1907099846U, 2848448395U,\n\t1717828221U, 1372704537U, 1707549841U, 2294058813U, 2101214437U,\n\t2052479531U, 1695809164U, 3176587306U, 2632770465U,   81634404U,\n\t1603220563U,  644238487U,  302857763U,  897352968U, 2613146653U,\n\t1391730149U, 4245717312U, 4191828749U, 1948492526U, 2618174230U,\n\t3992984522U, 2178852787U, 3596044509U, 3445573503U, 2026614616U,\n\t 915763564U, 3415689334U, 2532153403U, 3879661562U, 2215027417U,\n\t3111154986U, 2929478371U,  668346391U, 1152241381U, 2632029711U,\n\t3004150659U, 2135025926U,  948690501U, 2799119116U, 4228829406U,\n\t1981197489U, 4209064138U,  684318751U, 3459397845U,  201790843U,\n\t4022541136U, 3043635877U,  492509624U, 3263466772U, 1509148086U,\n\t 921459029U, 3198857146U,  705479721U, 3835966910U, 3603356465U,\n\t 576159741U, 1742849431U,  594214882U, 2055294343U, 3634861861U,\n\t 449571793U, 3246390646U, 3868232151U, 1479156585U, 2900125656U,\n\t2464815318U, 3960178104U, 1784261920U,   18311476U, 3627135050U,\n\t 644609697U,  424968996U,  919890700U, 2986824110U,  816423214U,\n\t4003562844U, 1392714305U, 1757384428U, 2569030598U,  995949559U,\n\t3875659880U, 2933807823U, 2752536860U, 2993858466U, 4030558899U,\n\t2770783427U, 2775406005U, 2777781742U, 1931292655U,  472147933U,\n\t3865853827U, 2726470545U, 2668412860U, 2887008249U,  408979190U,\n\t3578063323U, 3242082049U, 1778193530U,   27981909U, 2362826515U,\n\t 389875677U, 1043878156U,  581653903U, 3830568952U,  389535942U,\n\t3713523185U, 2768373359U, 2526101582U, 1998618197U, 1160859704U,\n\t3951172488U, 1098005003U,  906275699U, 3446228002U, 2220677963U,\n\t2059306445U,  132199571U,  476838790U, 1868039399U, 3097344807U,\n\t 857300945U,  396345050U, 2835919916U, 1782168828U, 1419519470U,\n\t4288137521U,  819087232U,  596301494U,  872823172U, 1526888217U,\n\t 805161465U, 1116186205U, 2829002754U, 2352620120U,  620121516U,\n\t 354159268U, 3601949785U,  209568138U, 1352371732U, 2145977349U,\n\t4236871834U, 1539414078U, 3558126206U, 3224857093U, 4164166682U,\n\t3817553440U, 3301780278U, 2682696837U, 3734994768U, 1370950260U,\n\t1477421202U, 2521315749U, 1330148125U, 1261554731U, 2769143688U,\n\t3554756293U, 4235882678U, 3254686059U, 3530579953U, 1215452615U,\n\t3574970923U, 4057131421U,  589224178U, 1000098193U,  171190718U,\n\t2521852045U, 2351447494U, 2284441580U, 2646685513U, 3486933563U,\n\t3789864960U, 1190528160U, 1702536782U, 1534105589U, 4262946827U,\n\t2726686826U, 3584544841U, 2348270128U, 2145092281U, 2502718509U,\n\t1027832411U, 3571171153U, 1287361161U, 4011474411U, 3241215351U,\n\t2419700818U,  971242709U, 1361975763U, 1096842482U, 3271045537U,\n\t  81165449U,  612438025U, 3912966678U, 1356929810U,  733545735U,\n\t 537003843U, 1282953084U,  884458241U,  588930090U, 3930269801U,\n\t2961472450U, 1219535534U, 3632251943U,  268183903U, 1441240533U,\n\t3653903360U, 3854473319U, 2259087390U, 2548293048U, 2022641195U,\n\t2105543911U, 1764085217U, 3246183186U,  482438805U,  888317895U,\n\t2628314765U, 2466219854U,  717546004U, 2322237039U,  416725234U,\n\t1544049923U, 1797944973U, 3398652364U, 3111909456U,  485742908U,\n\t2277491072U, 1056355088U, 3181001278U,  129695079U, 2693624550U,\n\t1764438564U, 3797785470U,  195503713U, 3266519725U, 2053389444U,\n\t1961527818U, 3400226523U, 3777903038U, 2597274307U, 4235851091U,\n\t4094406648U, 2171410785U, 1781151386U, 1378577117U,  654643266U,\n\t3424024173U, 3385813322U,  679385799U,  479380913U,  681715441U,\n\t3096225905U,  276813409U, 3854398070U, 2721105350U,  831263315U,\n\t3276280337U, 2628301522U, 3984868494U, 1466099834U, 2104922114U,\n\t1412672743U,  820330404U, 3491501010U,  942735832U,  710652807U,\n\t3972652090U,  679881088U,   40577009U, 3705286397U, 2815423480U,\n\t3566262429U,  663396513U, 3777887429U, 4016670678U,  404539370U,\n\t1142712925U, 1140173408U, 2913248352U, 2872321286U,  263751841U,\n\t3175196073U, 3162557581U, 2878996619U,   75498548U, 3836833140U,\n\t3284664959U, 1157523805U,  112847376U,  207855609U, 1337979698U,\n\t1222578451U,  157107174U,  901174378U, 3883717063U, 1618632639U,\n\t1767889440U, 4264698824U, 1582999313U,  884471997U, 2508825098U,\n\t3756370771U, 2457213553U, 3565776881U, 3709583214U,  915609601U,\n\t 460833524U, 1091049576U,   85522880U,    2553251U,  132102809U,\n\t2429882442U, 2562084610U, 1386507633U, 4112471229U,   21965213U,\n\t1981516006U, 2418435617U, 3054872091U, 4251511224U, 2025783543U,\n\t1916911512U, 2454491136U, 3938440891U, 3825869115U, 1121698605U,\n\t3463052265U,  802340101U, 1912886800U, 4031997367U, 3550640406U,\n\t1596096923U,  610150600U,  431464457U, 2541325046U,  486478003U,\n\t 739704936U, 2862696430U, 3037903166U, 1129749694U, 2611481261U,\n\t1228993498U,  510075548U, 3424962587U, 2458689681U,  818934833U,\n\t4233309125U, 1608196251U, 3419476016U, 1858543939U, 2682166524U,\n\t3317854285U,  631986188U, 3008214764U,  613826412U, 3567358221U,\n\t3512343882U, 1552467474U, 3316162670U, 1275841024U, 4142173454U,\n\t 565267881U,  768644821U,  198310105U, 2396688616U, 1837659011U,\n\t 203429334U,  854539004U, 4235811518U, 3338304926U, 3730418692U,\n\t3852254981U, 3032046452U, 2329811860U, 2303590566U, 2696092212U,\n\t3894665932U,  145835667U,  249563655U, 1932210840U, 2431696407U,\n\t3312636759U,  214962629U, 2092026914U, 3020145527U, 4073039873U,\n\t2739105705U, 1308336752U,  855104522U, 2391715321U,   67448785U,\n\t 547989482U,  854411802U, 3608633740U,  431731530U,  537375589U,\n\t3888005760U,  696099141U,  397343236U, 1864511780U,   44029739U,\n\t1729526891U, 1993398655U, 2010173426U, 2591546756U,  275223291U,\n\t1503900299U, 4217765081U, 2185635252U, 1122436015U, 3550155364U,\n\t 681707194U, 3260479338U,  933579397U, 2983029282U, 2505504587U,\n\t2667410393U, 2962684490U, 4139721708U, 2658172284U, 2452602383U,\n\t2607631612U, 1344296217U, 3075398709U, 2949785295U, 1049956168U,\n\t3917185129U, 2155660174U, 3280524475U, 1503827867U,  674380765U,\n\t1918468193U, 3843983676U,  634358221U, 2538335643U, 1873351298U,\n\t3368723763U, 2129144130U, 3203528633U, 3087174986U, 2691698871U,\n\t2516284287U,   24437745U, 1118381474U, 2816314867U, 2448576035U,\n\t4281989654U,  217287825U,  165872888U, 2628995722U, 3533525116U,\n\t2721669106U,  872340568U, 3429930655U, 3309047304U, 3916704967U,\n\t3270160355U, 1348884255U, 1634797670U,  881214967U, 4259633554U,\n\t 174613027U, 1103974314U, 1625224232U, 2678368291U, 1133866707U,\n\t3853082619U, 4073196549U, 1189620777U,  637238656U,  930241537U,\n\t4042750792U, 3842136042U, 2417007212U, 2524907510U, 1243036827U,\n\t1282059441U, 3764588774U, 1394459615U, 2323620015U, 1166152231U,\n\t3307479609U, 3849322257U, 3507445699U, 4247696636U,  758393720U,\n\t 967665141U, 1095244571U, 1319812152U,  407678762U, 2640605208U,\n\t2170766134U, 3663594275U, 4039329364U, 2512175520U,  725523154U,\n\t2249807004U, 3312617979U, 2414634172U, 1278482215U,  349206484U,\n\t1573063308U, 1196429124U, 3873264116U, 2400067801U,  268795167U,\n\t 226175489U, 2961367263U, 1968719665U,   42656370U, 1010790699U,\n\t 561600615U, 2422453992U, 3082197735U, 1636700484U, 3977715296U,\n\t3125350482U, 3478021514U, 2227819446U, 1540868045U, 3061908980U,\n\t1087362407U, 3625200291U,  361937537U,  580441897U, 1520043666U,\n\t2270875402U, 1009161260U, 2502355842U, 4278769785U,  473902412U,\n\t1057239083U, 1905829039U, 1483781177U, 2080011417U, 1207494246U,\n\t1806991954U, 2194674403U, 3455972205U,  807207678U, 3655655687U,\n\t 674112918U,  195425752U, 3917890095U, 1874364234U, 1837892715U,\n\t3663478166U, 1548892014U, 2570748714U, 2049929836U, 2167029704U,\n\t 697543767U, 3499545023U, 3342496315U, 1725251190U, 3561387469U,\n\t2905606616U, 1580182447U, 3934525927U, 4103172792U, 1365672522U,\n\t1534795737U, 3308667416U, 2841911405U, 3943182730U, 4072020313U,\n\t3494770452U, 3332626671U,   55327267U,  478030603U,  411080625U,\n\t3419529010U, 1604767823U, 3513468014U,  570668510U,  913790824U,\n\t2283967995U,  695159462U, 3825542932U, 4150698144U, 1829758699U,\n\t 202895590U, 1609122645U, 1267651008U, 2910315509U, 2511475445U,\n\t2477423819U, 3932081579U,  900879979U, 2145588390U, 2670007504U,\n\t 580819444U, 1864996828U, 2526325979U, 1019124258U,  815508628U,\n\t2765933989U, 1277301341U, 3006021786U,  855540956U,  288025710U,\n\t1919594237U, 2331223864U,  177452412U, 2475870369U, 2689291749U,\n\t 865194284U,  253432152U, 2628531804U, 2861208555U, 2361597573U,\n\t1653952120U, 1039661024U, 2159959078U, 3709040440U, 3564718533U,\n\t2596878672U, 2041442161U,   31164696U, 2662962485U, 3665637339U,\n\t1678115244U, 2699839832U, 3651968520U, 3521595541U,  458433303U,\n\t2423096824U,   21831741U,  380011703U, 2498168716U,  861806087U,\n\t1673574843U, 4188794405U, 2520563651U, 2632279153U, 2170465525U,\n\t4171949898U, 3886039621U, 1661344005U, 3424285243U,  992588372U,\n\t2500984144U, 2993248497U, 3590193895U, 1535327365U,  515645636U,\n\t 131633450U, 3729760261U, 1613045101U, 3254194278U,   15889678U,\n\t1493590689U,  244148718U, 2991472662U, 1401629333U,  777349878U,\n\t2501401703U, 4285518317U, 3794656178U,  955526526U, 3442142820U,\n\t3970298374U,  736025417U, 2737370764U, 1271509744U,  440570731U,\n\t 136141826U, 1596189518U,  923399175U,  257541519U, 3505774281U,\n\t2194358432U, 2518162991U, 1379893637U, 2667767062U, 3748146247U,\n\t1821712620U, 3923161384U, 1947811444U, 2392527197U, 4127419685U,\n\t1423694998U, 4156576871U, 1382885582U, 3420127279U, 3617499534U,\n\t2994377493U, 4038063986U, 1918458672U, 2983166794U, 4200449033U,\n\t 353294540U, 1609232588U,  243926648U, 2332803291U,  507996832U,\n\t2392838793U, 4075145196U, 2060984340U, 4287475136U,   88232602U,\n\t2491531140U, 4159725633U, 2272075455U,  759298618U,  201384554U,\n\t 838356250U, 1416268324U,  674476934U,   90795364U,  141672229U,\n\t3660399588U, 4196417251U, 3249270244U, 3774530247U,   59587265U,\n\t3683164208U,   19392575U, 1463123697U, 1882205379U,  293780489U,\n\t2553160622U, 2933904694U,  675638239U, 2851336944U, 1435238743U,\n\t2448730183U,  804436302U, 2119845972U,  322560608U, 4097732704U,\n\t2987802540U,  641492617U, 2575442710U, 4217822703U, 3271835300U,\n\t2836418300U, 3739921620U, 2138378768U, 2879771855U, 4294903423U,\n\t3121097946U, 2603440486U, 2560820391U, 1012930944U, 2313499967U,\n\t 584489368U, 3431165766U,  897384869U, 2062537737U, 2847889234U,\n\t3742362450U, 2951174585U, 4204621084U, 1109373893U, 3668075775U,\n\t2750138839U, 3518055702U,  733072558U, 4169325400U,  788493625U\n};\nstatic const uint64_t init_gen_rand_64_expected[] = {\n\tKQU(16924766246869039260), KQU( 8201438687333352714),\n\tKQU( 2265290287015001750), KQU(18397264611805473832),\n\tKQU( 3375255223302384358), KQU( 6345559975416828796),\n\tKQU(18229739242790328073), KQU( 7596792742098800905),\n\tKQU(  255338647169685981), KQU( 2052747240048610300),\n\tKQU(18328151576097299343), KQU(12472905421133796567),\n\tKQU(11315245349717600863), KQU(16594110197775871209),\n\tKQU(15708751964632456450), KQU(10452031272054632535),\n\tKQU(11097646720811454386), KQU( 4556090668445745441),\n\tKQU(17116187693090663106), KQU(14931526836144510645),\n\tKQU( 9190752218020552591), KQU( 9625800285771901401),\n\tKQU(13995141077659972832), KQU( 5194209094927829625),\n\tKQU( 4156788379151063303), KQU( 8523452593770139494),\n\tKQU(14082382103049296727), KQU( 2462601863986088483),\n\tKQU( 3030583461592840678), KQU( 5221622077872827681),\n\tKQU( 3084210671228981236), KQU(13956758381389953823),\n\tKQU(13503889856213423831), KQU(15696904024189836170),\n\tKQU( 4612584152877036206), KQU( 6231135538447867881),\n\tKQU(10172457294158869468), KQU( 6452258628466708150),\n\tKQU(14044432824917330221), KQU(  370168364480044279),\n\tKQU(10102144686427193359), KQU(  667870489994776076),\n\tKQU( 2732271956925885858), KQU(18027788905977284151),\n\tKQU(15009842788582923859), KQU( 7136357960180199542),\n\tKQU(15901736243475578127), KQU(16951293785352615701),\n\tKQU(10551492125243691632), KQU(17668869969146434804),\n\tKQU(13646002971174390445), KQU( 9804471050759613248),\n\tKQU( 5511670439655935493), KQU(18103342091070400926),\n\tKQU(17224512747665137533), KQU(15534627482992618168),\n\tKQU( 1423813266186582647), KQU(15821176807932930024),\n\tKQU(   30323369733607156), KQU(11599382494723479403),\n\tKQU(  653856076586810062), KQU( 3176437395144899659),\n\tKQU(14028076268147963917), KQU(16156398271809666195),\n\tKQU( 3166955484848201676), KQU( 5746805620136919390),\n\tKQU(17297845208891256593), KQU(11691653183226428483),\n\tKQU(17900026146506981577), KQU(15387382115755971042),\n\tKQU(16923567681040845943), KQU( 8039057517199388606),\n\tKQU(11748409241468629263), KQU(  794358245539076095),\n\tKQU(13438501964693401242), KQU(14036803236515618962),\n\tKQU( 5252311215205424721), KQU(17806589612915509081),\n\tKQU( 6802767092397596006), KQU(14212120431184557140),\n\tKQU( 1072951366761385712), KQU(13098491780722836296),\n\tKQU( 9466676828710797353), KQU(12673056849042830081),\n\tKQU(12763726623645357580), KQU(16468961652999309493),\n\tKQU(15305979875636438926), KQU(17444713151223449734),\n\tKQU( 5692214267627883674), KQU(13049589139196151505),\n\tKQU(  880115207831670745), KQU( 1776529075789695498),\n\tKQU(16695225897801466485), KQU(10666901778795346845),\n\tKQU( 6164389346722833869), KQU( 2863817793264300475),\n\tKQU( 9464049921886304754), KQU( 3993566636740015468),\n\tKQU( 9983749692528514136), KQU(16375286075057755211),\n\tKQU(16042643417005440820), KQU(11445419662923489877),\n\tKQU( 7999038846885158836), KQU( 6721913661721511535),\n\tKQU( 5363052654139357320), KQU( 1817788761173584205),\n\tKQU(13290974386445856444), KQU( 4650350818937984680),\n\tKQU( 8219183528102484836), KQU( 1569862923500819899),\n\tKQU( 4189359732136641860), KQU(14202822961683148583),\n\tKQU( 4457498315309429058), KQU(13089067387019074834),\n\tKQU(11075517153328927293), KQU(10277016248336668389),\n\tKQU( 7070509725324401122), KQU(17808892017780289380),\n\tKQU(13143367339909287349), KQU( 1377743745360085151),\n\tKQU( 5749341807421286485), KQU(14832814616770931325),\n\tKQU( 7688820635324359492), KQU(10960474011539770045),\n\tKQU(   81970066653179790), KQU(12619476072607878022),\n\tKQU( 4419566616271201744), KQU(15147917311750568503),\n\tKQU( 5549739182852706345), KQU( 7308198397975204770),\n\tKQU(13580425496671289278), KQU(17070764785210130301),\n\tKQU( 8202832846285604405), KQU( 6873046287640887249),\n\tKQU( 6927424434308206114), KQU( 6139014645937224874),\n\tKQU(10290373645978487639), KQU(15904261291701523804),\n\tKQU( 9628743442057826883), KQU(18383429096255546714),\n\tKQU( 4977413265753686967), KQU( 7714317492425012869),\n\tKQU( 9025232586309926193), KQU(14627338359776709107),\n\tKQU(14759849896467790763), KQU(10931129435864423252),\n\tKQU( 4588456988775014359), KQU(10699388531797056724),\n\tKQU(  468652268869238792), KQU( 5755943035328078086),\n\tKQU( 2102437379988580216), KQU( 9986312786506674028),\n\tKQU( 2654207180040945604), KQU( 8726634790559960062),\n\tKQU(  100497234871808137), KQU( 2800137176951425819),\n\tKQU( 6076627612918553487), KQU( 5780186919186152796),\n\tKQU( 8179183595769929098), KQU( 6009426283716221169),\n\tKQU( 2796662551397449358), KQU( 1756961367041986764),\n\tKQU( 6972897917355606205), KQU(14524774345368968243),\n\tKQU( 2773529684745706940), KQU( 4853632376213075959),\n\tKQU( 4198177923731358102), KQU( 8271224913084139776),\n\tKQU( 2741753121611092226), KQU(16782366145996731181),\n\tKQU(15426125238972640790), KQU(13595497100671260342),\n\tKQU( 3173531022836259898), KQU( 6573264560319511662),\n\tKQU(18041111951511157441), KQU( 2351433581833135952),\n\tKQU( 3113255578908173487), KQU( 1739371330877858784),\n\tKQU(16046126562789165480), KQU( 8072101652214192925),\n\tKQU(15267091584090664910), KQU( 9309579200403648940),\n\tKQU( 5218892439752408722), KQU(14492477246004337115),\n\tKQU(17431037586679770619), KQU( 7385248135963250480),\n\tKQU( 9580144956565560660), KQU( 4919546228040008720),\n\tKQU(15261542469145035584), KQU(18233297270822253102),\n\tKQU( 5453248417992302857), KQU( 9309519155931460285),\n\tKQU(10342813012345291756), KQU(15676085186784762381),\n\tKQU(15912092950691300645), KQU( 9371053121499003195),\n\tKQU( 9897186478226866746), KQU(14061858287188196327),\n\tKQU(  122575971620788119), KQU(12146750969116317754),\n\tKQU( 4438317272813245201), KQU( 8332576791009527119),\n\tKQU(13907785691786542057), KQU(10374194887283287467),\n\tKQU( 2098798755649059566), KQU( 3416235197748288894),\n\tKQU( 8688269957320773484), KQU( 7503964602397371571),\n\tKQU(16724977015147478236), KQU( 9461512855439858184),\n\tKQU(13259049744534534727), KQU( 3583094952542899294),\n\tKQU( 8764245731305528292), KQU(13240823595462088985),\n\tKQU(13716141617617910448), KQU(18114969519935960955),\n\tKQU( 2297553615798302206), KQU( 4585521442944663362),\n\tKQU(17776858680630198686), KQU( 4685873229192163363),\n\tKQU(  152558080671135627), KQU(15424900540842670088),\n\tKQU(13229630297130024108), KQU(17530268788245718717),\n\tKQU(16675633913065714144), KQU( 3158912717897568068),\n\tKQU(15399132185380087288), KQU( 7401418744515677872),\n\tKQU(13135412922344398535), KQU( 6385314346100509511),\n\tKQU(13962867001134161139), KQU(10272780155442671999),\n\tKQU(12894856086597769142), KQU(13340877795287554994),\n\tKQU(12913630602094607396), KQU(12543167911119793857),\n\tKQU(17343570372251873096), KQU(10959487764494150545),\n\tKQU( 6966737953093821128), KQU(13780699135496988601),\n\tKQU( 4405070719380142046), KQU(14923788365607284982),\n\tKQU( 2869487678905148380), KQU( 6416272754197188403),\n\tKQU(15017380475943612591), KQU( 1995636220918429487),\n\tKQU( 3402016804620122716), KQU(15800188663407057080),\n\tKQU(11362369990390932882), KQU(15262183501637986147),\n\tKQU(10239175385387371494), KQU( 9352042420365748334),\n\tKQU( 1682457034285119875), KQU( 1724710651376289644),\n\tKQU( 2038157098893817966), KQU( 9897825558324608773),\n\tKQU( 1477666236519164736), KQU(16835397314511233640),\n\tKQU(10370866327005346508), KQU(10157504370660621982),\n\tKQU(12113904045335882069), KQU(13326444439742783008),\n\tKQU(11302769043000765804), KQU(13594979923955228484),\n\tKQU(11779351762613475968), KQU( 3786101619539298383),\n\tKQU( 8021122969180846063), KQU(15745904401162500495),\n\tKQU(10762168465993897267), KQU(13552058957896319026),\n\tKQU(11200228655252462013), KQU( 5035370357337441226),\n\tKQU( 7593918984545500013), KQU( 5418554918361528700),\n\tKQU( 4858270799405446371), KQU( 9974659566876282544),\n\tKQU(18227595922273957859), KQU( 2772778443635656220),\n\tKQU(14285143053182085385), KQU( 9939700992429600469),\n\tKQU(12756185904545598068), KQU( 2020783375367345262),\n\tKQU(   57026775058331227), KQU(  950827867930065454),\n\tKQU( 6602279670145371217), KQU( 2291171535443566929),\n\tKQU( 5832380724425010313), KQU( 1220343904715982285),\n\tKQU(17045542598598037633), KQU(15460481779702820971),\n\tKQU(13948388779949365130), KQU(13975040175430829518),\n\tKQU(17477538238425541763), KQU(11104663041851745725),\n\tKQU(15860992957141157587), KQU(14529434633012950138),\n\tKQU( 2504838019075394203), KQU( 7512113882611121886),\n\tKQU( 4859973559980886617), KQU( 1258601555703250219),\n\tKQU(15594548157514316394), KQU( 4516730171963773048),\n\tKQU(11380103193905031983), KQU( 6809282239982353344),\n\tKQU(18045256930420065002), KQU( 2453702683108791859),\n\tKQU(  977214582986981460), KQU( 2006410402232713466),\n\tKQU( 6192236267216378358), KQU( 3429468402195675253),\n\tKQU(18146933153017348921), KQU(17369978576367231139),\n\tKQU( 1246940717230386603), KQU(11335758870083327110),\n\tKQU(14166488801730353682), KQU( 9008573127269635732),\n\tKQU(10776025389820643815), KQU(15087605441903942962),\n\tKQU( 1359542462712147922), KQU(13898874411226454206),\n\tKQU(17911176066536804411), KQU( 9435590428600085274),\n\tKQU(  294488509967864007), KQU( 8890111397567922046),\n\tKQU( 7987823476034328778), KQU(13263827582440967651),\n\tKQU( 7503774813106751573), KQU(14974747296185646837),\n\tKQU( 8504765037032103375), KQU(17340303357444536213),\n\tKQU( 7704610912964485743), KQU( 8107533670327205061),\n\tKQU( 9062969835083315985), KQU(16968963142126734184),\n\tKQU(12958041214190810180), KQU( 2720170147759570200),\n\tKQU( 2986358963942189566), KQU(14884226322219356580),\n\tKQU(  286224325144368520), KQU(11313800433154279797),\n\tKQU(18366849528439673248), KQU(17899725929482368789),\n\tKQU( 3730004284609106799), KQU( 1654474302052767205),\n\tKQU( 5006698007047077032), KQU( 8196893913601182838),\n\tKQU(15214541774425211640), KQU(17391346045606626073),\n\tKQU( 8369003584076969089), KQU( 3939046733368550293),\n\tKQU(10178639720308707785), KQU( 2180248669304388697),\n\tKQU(   62894391300126322), KQU( 9205708961736223191),\n\tKQU( 6837431058165360438), KQU( 3150743890848308214),\n\tKQU(17849330658111464583), KQU(12214815643135450865),\n\tKQU(13410713840519603402), KQU( 3200778126692046802),\n\tKQU(13354780043041779313), KQU(  800850022756886036),\n\tKQU(15660052933953067433), KQU( 6572823544154375676),\n\tKQU(11030281857015819266), KQU(12682241941471433835),\n\tKQU(11654136407300274693), KQU( 4517795492388641109),\n\tKQU( 9757017371504524244), KQU(17833043400781889277),\n\tKQU(12685085201747792227), KQU(10408057728835019573),\n\tKQU(   98370418513455221), KQU( 6732663555696848598),\n\tKQU(13248530959948529780), KQU( 3530441401230622826),\n\tKQU(18188251992895660615), KQU( 1847918354186383756),\n\tKQU( 1127392190402660921), KQU(11293734643143819463),\n\tKQU( 3015506344578682982), KQU(13852645444071153329),\n\tKQU( 2121359659091349142), KQU( 1294604376116677694),\n\tKQU( 5616576231286352318), KQU( 7112502442954235625),\n\tKQU(11676228199551561689), KQU(12925182803007305359),\n\tKQU( 7852375518160493082), KQU( 1136513130539296154),\n\tKQU( 5636923900916593195), KQU( 3221077517612607747),\n\tKQU(17784790465798152513), KQU( 3554210049056995938),\n\tKQU(17476839685878225874), KQU( 3206836372585575732),\n\tKQU( 2765333945644823430), KQU(10080070903718799528),\n\tKQU( 5412370818878286353), KQU( 9689685887726257728),\n\tKQU( 8236117509123533998), KQU( 1951139137165040214),\n\tKQU( 4492205209227980349), KQU(16541291230861602967),\n\tKQU( 1424371548301437940), KQU( 9117562079669206794),\n\tKQU(14374681563251691625), KQU(13873164030199921303),\n\tKQU( 6680317946770936731), KQU(15586334026918276214),\n\tKQU(10896213950976109802), KQU( 9506261949596413689),\n\tKQU( 9903949574308040616), KQU( 6038397344557204470),\n\tKQU(  174601465422373648), KQU(15946141191338238030),\n\tKQU(17142225620992044937), KQU( 7552030283784477064),\n\tKQU( 2947372384532947997), KQU(  510797021688197711),\n\tKQU( 4962499439249363461), KQU(   23770320158385357),\n\tKQU(  959774499105138124), KQU( 1468396011518788276),\n\tKQU( 2015698006852312308), KQU( 4149400718489980136),\n\tKQU( 5992916099522371188), KQU(10819182935265531076),\n\tKQU(16189787999192351131), KQU(  342833961790261950),\n\tKQU(12470830319550495336), KQU(18128495041912812501),\n\tKQU( 1193600899723524337), KQU( 9056793666590079770),\n\tKQU( 2154021227041669041), KQU( 4963570213951235735),\n\tKQU( 4865075960209211409), KQU( 2097724599039942963),\n\tKQU( 2024080278583179845), KQU(11527054549196576736),\n\tKQU(10650256084182390252), KQU( 4808408648695766755),\n\tKQU( 1642839215013788844), KQU(10607187948250398390),\n\tKQU( 7076868166085913508), KQU(  730522571106887032),\n\tKQU(12500579240208524895), KQU( 4484390097311355324),\n\tKQU(15145801330700623870), KQU( 8055827661392944028),\n\tKQU( 5865092976832712268), KQU(15159212508053625143),\n\tKQU( 3560964582876483341), KQU( 4070052741344438280),\n\tKQU( 6032585709886855634), KQU(15643262320904604873),\n\tKQU( 2565119772293371111), KQU(  318314293065348260),\n\tKQU(15047458749141511872), KQU( 7772788389811528730),\n\tKQU( 7081187494343801976), KQU( 6465136009467253947),\n\tKQU(10425940692543362069), KQU(  554608190318339115),\n\tKQU(14796699860302125214), KQU( 1638153134431111443),\n\tKQU(10336967447052276248), KQU( 8412308070396592958),\n\tKQU( 4004557277152051226), KQU( 8143598997278774834),\n\tKQU(16413323996508783221), KQU(13139418758033994949),\n\tKQU( 9772709138335006667), KQU( 2818167159287157659),\n\tKQU(17091740573832523669), KQU(14629199013130751608),\n\tKQU(18268322711500338185), KQU( 8290963415675493063),\n\tKQU( 8830864907452542588), KQU( 1614839084637494849),\n\tKQU(14855358500870422231), KQU( 3472996748392519937),\n\tKQU(15317151166268877716), KQU( 5825895018698400362),\n\tKQU(16730208429367544129), KQU(10481156578141202800),\n\tKQU( 4746166512382823750), KQU(12720876014472464998),\n\tKQU( 8825177124486735972), KQU(13733447296837467838),\n\tKQU( 6412293741681359625), KQU( 8313213138756135033),\n\tKQU(11421481194803712517), KQU( 7997007691544174032),\n\tKQU( 6812963847917605930), KQU( 9683091901227558641),\n\tKQU(14703594165860324713), KQU( 1775476144519618309),\n\tKQU( 2724283288516469519), KQU(  717642555185856868),\n\tKQU( 8736402192215092346), KQU(11878800336431381021),\n\tKQU( 4348816066017061293), KQU( 6115112756583631307),\n\tKQU( 9176597239667142976), KQU(12615622714894259204),\n\tKQU(10283406711301385987), KQU( 5111762509485379420),\n\tKQU( 3118290051198688449), KQU( 7345123071632232145),\n\tKQU( 9176423451688682359), KQU( 4843865456157868971),\n\tKQU(12008036363752566088), KQU(12058837181919397720),\n\tKQU( 2145073958457347366), KQU( 1526504881672818067),\n\tKQU( 3488830105567134848), KQU(13208362960674805143),\n\tKQU( 4077549672899572192), KQU( 7770995684693818365),\n\tKQU( 1398532341546313593), KQU(12711859908703927840),\n\tKQU( 1417561172594446813), KQU(17045191024194170604),\n\tKQU( 4101933177604931713), KQU(14708428834203480320),\n\tKQU(17447509264469407724), KQU(14314821973983434255),\n\tKQU(17990472271061617265), KQU( 5087756685841673942),\n\tKQU(12797820586893859939), KQU( 1778128952671092879),\n\tKQU( 3535918530508665898), KQU( 9035729701042481301),\n\tKQU(14808661568277079962), KQU(14587345077537747914),\n\tKQU(11920080002323122708), KQU( 6426515805197278753),\n\tKQU( 3295612216725984831), KQU(11040722532100876120),\n\tKQU(12305952936387598754), KQU(16097391899742004253),\n\tKQU( 4908537335606182208), KQU(12446674552196795504),\n\tKQU(16010497855816895177), KQU( 9194378874788615551),\n\tKQU( 3382957529567613384), KQU( 5154647600754974077),\n\tKQU( 9801822865328396141), KQU( 9023662173919288143),\n\tKQU(17623115353825147868), KQU( 8238115767443015816),\n\tKQU(15811444159859002560), KQU( 9085612528904059661),\n\tKQU( 6888601089398614254), KQU(  258252992894160189),\n\tKQU( 6704363880792428622), KQU( 6114966032147235763),\n\tKQU(11075393882690261875), KQU( 8797664238933620407),\n\tKQU( 5901892006476726920), KQU( 5309780159285518958),\n\tKQU(14940808387240817367), KQU(14642032021449656698),\n\tKQU( 9808256672068504139), KQU( 3670135111380607658),\n\tKQU(11211211097845960152), KQU( 1474304506716695808),\n\tKQU(15843166204506876239), KQU( 7661051252471780561),\n\tKQU(10170905502249418476), KQU( 7801416045582028589),\n\tKQU( 2763981484737053050), KQU( 9491377905499253054),\n\tKQU(16201395896336915095), KQU( 9256513756442782198),\n\tKQU( 5411283157972456034), KQU( 5059433122288321676),\n\tKQU( 4327408006721123357), KQU( 9278544078834433377),\n\tKQU( 7601527110882281612), KQU(11848295896975505251),\n\tKQU(12096998801094735560), KQU(14773480339823506413),\n\tKQU(15586227433895802149), KQU(12786541257830242872),\n\tKQU( 6904692985140503067), KQU( 5309011515263103959),\n\tKQU(12105257191179371066), KQU(14654380212442225037),\n\tKQU( 2556774974190695009), KQU( 4461297399927600261),\n\tKQU(14888225660915118646), KQU(14915459341148291824),\n\tKQU( 2738802166252327631), KQU( 6047155789239131512),\n\tKQU(12920545353217010338), KQU(10697617257007840205),\n\tKQU( 2751585253158203504), KQU(13252729159780047496),\n\tKQU(14700326134672815469), KQU(14082527904374600529),\n\tKQU(16852962273496542070), KQU(17446675504235853907),\n\tKQU(15019600398527572311), KQU(12312781346344081551),\n\tKQU(14524667935039810450), KQU( 5634005663377195738),\n\tKQU(11375574739525000569), KQU( 2423665396433260040),\n\tKQU( 5222836914796015410), KQU( 4397666386492647387),\n\tKQU( 4619294441691707638), KQU(  665088602354770716),\n\tKQU(13246495665281593610), KQU( 6564144270549729409),\n\tKQU(10223216188145661688), KQU( 3961556907299230585),\n\tKQU(11543262515492439914), KQU(16118031437285993790),\n\tKQU( 7143417964520166465), KQU(13295053515909486772),\n\tKQU(   40434666004899675), KQU(17127804194038347164),\n\tKQU( 8599165966560586269), KQU( 8214016749011284903),\n\tKQU(13725130352140465239), KQU( 5467254474431726291),\n\tKQU( 7748584297438219877), KQU(16933551114829772472),\n\tKQU( 2169618439506799400), KQU( 2169787627665113463),\n\tKQU(17314493571267943764), KQU(18053575102911354912),\n\tKQU(11928303275378476973), KQU(11593850925061715550),\n\tKQU(17782269923473589362), KQU( 3280235307704747039),\n\tKQU( 6145343578598685149), KQU(17080117031114086090),\n\tKQU(18066839902983594755), KQU( 6517508430331020706),\n\tKQU( 8092908893950411541), KQU(12558378233386153732),\n\tKQU( 4476532167973132976), KQU(16081642430367025016),\n\tKQU( 4233154094369139361), KQU( 8693630486693161027),\n\tKQU(11244959343027742285), KQU(12273503967768513508),\n\tKQU(14108978636385284876), KQU( 7242414665378826984),\n\tKQU( 6561316938846562432), KQU( 8601038474994665795),\n\tKQU(17532942353612365904), KQU(17940076637020912186),\n\tKQU( 7340260368823171304), KQU( 7061807613916067905),\n\tKQU(10561734935039519326), KQU(17990796503724650862),\n\tKQU( 6208732943911827159), KQU(  359077562804090617),\n\tKQU(14177751537784403113), KQU(10659599444915362902),\n\tKQU(15081727220615085833), KQU(13417573895659757486),\n\tKQU(15513842342017811524), KQU(11814141516204288231),\n\tKQU( 1827312513875101814), KQU( 2804611699894603103),\n\tKQU(17116500469975602763), KQU(12270191815211952087),\n\tKQU(12256358467786024988), KQU(18435021722453971267),\n\tKQU(  671330264390865618), KQU(  476504300460286050),\n\tKQU(16465470901027093441), KQU( 4047724406247136402),\n\tKQU( 1322305451411883346), KQU( 1388308688834322280),\n\tKQU( 7303989085269758176), KQU( 9323792664765233642),\n\tKQU( 4542762575316368936), KQU(17342696132794337618),\n\tKQU( 4588025054768498379), KQU(13415475057390330804),\n\tKQU(17880279491733405570), KQU(10610553400618620353),\n\tKQU( 3180842072658960139), KQU(13002966655454270120),\n\tKQU( 1665301181064982826), KQU( 7083673946791258979),\n\tKQU(  190522247122496820), KQU(17388280237250677740),\n\tKQU( 8430770379923642945), KQU(12987180971921668584),\n\tKQU( 2311086108365390642), KQU( 2870984383579822345),\n\tKQU(14014682609164653318), KQU(14467187293062251484),\n\tKQU(  192186361147413298), KQU(15171951713531796524),\n\tKQU( 9900305495015948728), KQU(17958004775615466344),\n\tKQU(14346380954498606514), KQU(18040047357617407096),\n\tKQU( 5035237584833424532), KQU(15089555460613972287),\n\tKQU( 4131411873749729831), KQU( 1329013581168250330),\n\tKQU(10095353333051193949), KQU(10749518561022462716),\n\tKQU( 9050611429810755847), KQU(15022028840236655649),\n\tKQU( 8775554279239748298), KQU(13105754025489230502),\n\tKQU(15471300118574167585), KQU(   89864764002355628),\n\tKQU( 8776416323420466637), KQU( 5280258630612040891),\n\tKQU( 2719174488591862912), KQU( 7599309137399661994),\n\tKQU(15012887256778039979), KQU(14062981725630928925),\n\tKQU(12038536286991689603), KQU( 7089756544681775245),\n\tKQU(10376661532744718039), KQU( 1265198725901533130),\n\tKQU(13807996727081142408), KQU( 2935019626765036403),\n\tKQU( 7651672460680700141), KQU( 3644093016200370795),\n\tKQU( 2840982578090080674), KQU(17956262740157449201),\n\tKQU(18267979450492880548), KQU(11799503659796848070),\n\tKQU( 9942537025669672388), KQU(11886606816406990297),\n\tKQU( 5488594946437447576), KQU( 7226714353282744302),\n\tKQU( 3784851653123877043), KQU(  878018453244803041),\n\tKQU(12110022586268616085), KQU(  734072179404675123),\n\tKQU(11869573627998248542), KQU(  469150421297783998),\n\tKQU(  260151124912803804), KQU(11639179410120968649),\n\tKQU( 9318165193840846253), KQU(12795671722734758075),\n\tKQU(15318410297267253933), KQU(  691524703570062620),\n\tKQU( 5837129010576994601), KQU(15045963859726941052),\n\tKQU( 5850056944932238169), KQU(12017434144750943807),\n\tKQU( 7447139064928956574), KQU( 3101711812658245019),\n\tKQU(16052940704474982954), KQU(18195745945986994042),\n\tKQU( 8932252132785575659), KQU(13390817488106794834),\n\tKQU(11582771836502517453), KQU( 4964411326683611686),\n\tKQU( 2195093981702694011), KQU(14145229538389675669),\n\tKQU(16459605532062271798), KQU(  866316924816482864),\n\tKQU( 4593041209937286377), KQU( 8415491391910972138),\n\tKQU( 4171236715600528969), KQU(16637569303336782889),\n\tKQU( 2002011073439212680), KQU(17695124661097601411),\n\tKQU( 4627687053598611702), KQU( 7895831936020190403),\n\tKQU( 8455951300917267802), KQU( 2923861649108534854),\n\tKQU( 8344557563927786255), KQU( 6408671940373352556),\n\tKQU(12210227354536675772), KQU(14294804157294222295),\n\tKQU(10103022425071085127), KQU(10092959489504123771),\n\tKQU( 6554774405376736268), KQU(12629917718410641774),\n\tKQU( 6260933257596067126), KQU( 2460827021439369673),\n\tKQU( 2541962996717103668), KQU(  597377203127351475),\n\tKQU( 5316984203117315309), KQU( 4811211393563241961),\n\tKQU(13119698597255811641), KQU( 8048691512862388981),\n\tKQU(10216818971194073842), KQU( 4612229970165291764),\n\tKQU(10000980798419974770), KQU( 6877640812402540687),\n\tKQU( 1488727563290436992), KQU( 2227774069895697318),\n\tKQU(11237754507523316593), KQU(13478948605382290972),\n\tKQU( 1963583846976858124), KQU( 5512309205269276457),\n\tKQU( 3972770164717652347), KQU( 3841751276198975037),\n\tKQU(10283343042181903117), KQU( 8564001259792872199),\n\tKQU(16472187244722489221), KQU( 8953493499268945921),\n\tKQU( 3518747340357279580), KQU( 4003157546223963073),\n\tKQU( 3270305958289814590), KQU( 3966704458129482496),\n\tKQU( 8122141865926661939), KQU(14627734748099506653),\n\tKQU(13064426990862560568), KQU( 2414079187889870829),\n\tKQU( 5378461209354225306), KQU(10841985740128255566),\n\tKQU(  538582442885401738), KQU( 7535089183482905946),\n\tKQU(16117559957598879095), KQU( 8477890721414539741),\n\tKQU( 1459127491209533386), KQU(17035126360733620462),\n\tKQU( 8517668552872379126), KQU(10292151468337355014),\n\tKQU(17081267732745344157), KQU(13751455337946087178),\n\tKQU(14026945459523832966), KQU( 6653278775061723516),\n\tKQU(10619085543856390441), KQU( 2196343631481122885),\n\tKQU(10045966074702826136), KQU(10082317330452718282),\n\tKQU( 5920859259504831242), KQU( 9951879073426540617),\n\tKQU( 7074696649151414158), KQU(15808193543879464318),\n\tKQU( 7385247772746953374), KQU( 3192003544283864292),\n\tKQU(18153684490917593847), KQU(12423498260668568905),\n\tKQU(10957758099756378169), KQU(11488762179911016040),\n\tKQU( 2099931186465333782), KQU(11180979581250294432),\n\tKQU( 8098916250668367933), KQU( 3529200436790763465),\n\tKQU(12988418908674681745), KQU( 6147567275954808580),\n\tKQU( 3207503344604030989), KQU(10761592604898615360),\n\tKQU(  229854861031893504), KQU( 8809853962667144291),\n\tKQU(13957364469005693860), KQU( 7634287665224495886),\n\tKQU(12353487366976556874), KQU( 1134423796317152034),\n\tKQU( 2088992471334107068), KQU( 7393372127190799698),\n\tKQU( 1845367839871058391), KQU(  207922563987322884),\n\tKQU(11960870813159944976), KQU(12182120053317317363),\n\tKQU(17307358132571709283), KQU(13871081155552824936),\n\tKQU(18304446751741566262), KQU( 7178705220184302849),\n\tKQU(10929605677758824425), KQU(16446976977835806844),\n\tKQU(13723874412159769044), KQU( 6942854352100915216),\n\tKQU( 1726308474365729390), KQU( 2150078766445323155),\n\tKQU(15345558947919656626), KQU(12145453828874527201),\n\tKQU( 2054448620739726849), KQU( 2740102003352628137),\n\tKQU(11294462163577610655), KQU(  756164283387413743),\n\tKQU(17841144758438810880), KQU(10802406021185415861),\n\tKQU( 8716455530476737846), KQU( 6321788834517649606),\n\tKQU(14681322910577468426), KQU(17330043563884336387),\n\tKQU(12701802180050071614), KQU(14695105111079727151),\n\tKQU( 5112098511654172830), KQU( 4957505496794139973),\n\tKQU( 8270979451952045982), KQU(12307685939199120969),\n\tKQU(12425799408953443032), KQU( 8376410143634796588),\n\tKQU(16621778679680060464), KQU( 3580497854566660073),\n\tKQU( 1122515747803382416), KQU(  857664980960597599),\n\tKQU( 6343640119895925918), KQU(12878473260854462891),\n\tKQU(10036813920765722626), KQU(14451335468363173812),\n\tKQU( 5476809692401102807), KQU(16442255173514366342),\n\tKQU(13060203194757167104), KQU(14354124071243177715),\n\tKQU(15961249405696125227), KQU(13703893649690872584),\n\tKQU(  363907326340340064), KQU( 6247455540491754842),\n\tKQU(12242249332757832361), KQU(  156065475679796717),\n\tKQU( 9351116235749732355), KQU( 4590350628677701405),\n\tKQU( 1671195940982350389), KQU(13501398458898451905),\n\tKQU( 6526341991225002255), KQU( 1689782913778157592),\n\tKQU( 7439222350869010334), KQU(13975150263226478308),\n\tKQU(11411961169932682710), KQU(17204271834833847277),\n\tKQU(  541534742544435367), KQU( 6591191931218949684),\n\tKQU( 2645454775478232486), KQU( 4322857481256485321),\n\tKQU( 8477416487553065110), KQU(12902505428548435048),\n\tKQU(  971445777981341415), KQU(14995104682744976712),\n\tKQU( 4243341648807158063), KQU( 8695061252721927661),\n\tKQU( 5028202003270177222), KQU( 2289257340915567840),\n\tKQU(13870416345121866007), KQU(13994481698072092233),\n\tKQU( 6912785400753196481), KQU( 2278309315841980139),\n\tKQU( 4329765449648304839), KQU( 5963108095785485298),\n\tKQU( 4880024847478722478), KQU(16015608779890240947),\n\tKQU( 1866679034261393544), KQU(  914821179919731519),\n\tKQU( 9643404035648760131), KQU( 2418114953615593915),\n\tKQU(  944756836073702374), KQU(15186388048737296834),\n\tKQU( 7723355336128442206), KQU( 7500747479679599691),\n\tKQU(18013961306453293634), KQU( 2315274808095756456),\n\tKQU(13655308255424029566), KQU(17203800273561677098),\n\tKQU( 1382158694422087756), KQU( 5090390250309588976),\n\tKQU(  517170818384213989), KQU( 1612709252627729621),\n\tKQU( 1330118955572449606), KQU(  300922478056709885),\n\tKQU(18115693291289091987), KQU(13491407109725238321),\n\tKQU(15293714633593827320), KQU( 5151539373053314504),\n\tKQU( 5951523243743139207), KQU(14459112015249527975),\n\tKQU( 5456113959000700739), KQU( 3877918438464873016),\n\tKQU(12534071654260163555), KQU(15871678376893555041),\n\tKQU(11005484805712025549), KQU(16353066973143374252),\n\tKQU( 4358331472063256685), KQU( 8268349332210859288),\n\tKQU(12485161590939658075), KQU(13955993592854471343),\n\tKQU( 5911446886848367039), KQU(14925834086813706974),\n\tKQU( 6590362597857994805), KQU( 1280544923533661875),\n\tKQU( 1637756018947988164), KQU( 4734090064512686329),\n\tKQU(16693705263131485912), KQU( 6834882340494360958),\n\tKQU( 8120732176159658505), KQU( 2244371958905329346),\n\tKQU(10447499707729734021), KQU( 7318742361446942194),\n\tKQU( 8032857516355555296), KQU(14023605983059313116),\n\tKQU( 1032336061815461376), KQU( 9840995337876562612),\n\tKQU( 9869256223029203587), KQU(12227975697177267636),\n\tKQU(12728115115844186033), KQU( 7752058479783205470),\n\tKQU(  729733219713393087), KQU(12954017801239007622)\n};\nstatic const uint64_t init_by_array_64_expected[] = {\n\tKQU( 2100341266307895239), KQU( 8344256300489757943),\n\tKQU(15687933285484243894), KQU( 8268620370277076319),\n\tKQU(12371852309826545459), KQU( 8800491541730110238),\n\tKQU(18113268950100835773), KQU( 2886823658884438119),\n\tKQU( 3293667307248180724), KQU( 9307928143300172731),\n\tKQU( 7688082017574293629), KQU(  900986224735166665),\n\tKQU( 9977972710722265039), KQU( 6008205004994830552),\n\tKQU(  546909104521689292), KQU( 7428471521869107594),\n\tKQU(14777563419314721179), KQU(16116143076567350053),\n\tKQU( 5322685342003142329), KQU( 4200427048445863473),\n\tKQU( 4693092150132559146), KQU(13671425863759338582),\n\tKQU( 6747117460737639916), KQU( 4732666080236551150),\n\tKQU( 5912839950611941263), KQU( 3903717554504704909),\n\tKQU( 2615667650256786818), KQU(10844129913887006352),\n\tKQU(13786467861810997820), KQU(14267853002994021570),\n\tKQU(13767807302847237439), KQU(16407963253707224617),\n\tKQU( 4802498363698583497), KQU( 2523802839317209764),\n\tKQU( 3822579397797475589), KQU( 8950320572212130610),\n\tKQU( 3745623504978342534), KQU(16092609066068482806),\n\tKQU( 9817016950274642398), KQU(10591660660323829098),\n\tKQU(11751606650792815920), KQU( 5122873818577122211),\n\tKQU(17209553764913936624), KQU( 6249057709284380343),\n\tKQU(15088791264695071830), KQU(15344673071709851930),\n\tKQU( 4345751415293646084), KQU( 2542865750703067928),\n\tKQU(13520525127852368784), KQU(18294188662880997241),\n\tKQU( 3871781938044881523), KQU( 2873487268122812184),\n\tKQU(15099676759482679005), KQU(15442599127239350490),\n\tKQU( 6311893274367710888), KQU( 3286118760484672933),\n\tKQU( 4146067961333542189), KQU(13303942567897208770),\n\tKQU( 8196013722255630418), KQU( 4437815439340979989),\n\tKQU(15433791533450605135), KQU( 4254828956815687049),\n\tKQU( 1310903207708286015), KQU(10529182764462398549),\n\tKQU(14900231311660638810), KQU( 9727017277104609793),\n\tKQU( 1821308310948199033), KQU(11628861435066772084),\n\tKQU( 9469019138491546924), KQU( 3145812670532604988),\n\tKQU( 9938468915045491919), KQU( 1562447430672662142),\n\tKQU(13963995266697989134), KQU( 3356884357625028695),\n\tKQU( 4499850304584309747), KQU( 8456825817023658122),\n\tKQU(10859039922814285279), KQU( 8099512337972526555),\n\tKQU(  348006375109672149), KQU(11919893998241688603),\n\tKQU( 1104199577402948826), KQU(16689191854356060289),\n\tKQU(10992552041730168078), KQU( 7243733172705465836),\n\tKQU( 5668075606180319560), KQU(18182847037333286970),\n\tKQU( 4290215357664631322), KQU( 4061414220791828613),\n\tKQU(13006291061652989604), KQU( 7140491178917128798),\n\tKQU(12703446217663283481), KQU( 5500220597564558267),\n\tKQU(10330551509971296358), KQU(15958554768648714492),\n\tKQU( 5174555954515360045), KQU( 1731318837687577735),\n\tKQU( 3557700801048354857), KQU(13764012341928616198),\n\tKQU(13115166194379119043), KQU( 7989321021560255519),\n\tKQU( 2103584280905877040), KQU( 9230788662155228488),\n\tKQU(16396629323325547654), KQU(  657926409811318051),\n\tKQU(15046700264391400727), KQU( 5120132858771880830),\n\tKQU( 7934160097989028561), KQU( 6963121488531976245),\n\tKQU(17412329602621742089), KQU(15144843053931774092),\n\tKQU(17204176651763054532), KQU(13166595387554065870),\n\tKQU( 8590377810513960213), KQU( 5834365135373991938),\n\tKQU( 7640913007182226243), KQU( 3479394703859418425),\n\tKQU(16402784452644521040), KQU( 4993979809687083980),\n\tKQU(13254522168097688865), KQU(15643659095244365219),\n\tKQU( 5881437660538424982), KQU(11174892200618987379),\n\tKQU(  254409966159711077), KQU(17158413043140549909),\n\tKQU( 3638048789290376272), KQU( 1376816930299489190),\n\tKQU( 4622462095217761923), KQU(15086407973010263515),\n\tKQU(13253971772784692238), KQU( 5270549043541649236),\n\tKQU(11182714186805411604), KQU(12283846437495577140),\n\tKQU( 5297647149908953219), KQU(10047451738316836654),\n\tKQU( 4938228100367874746), KQU(12328523025304077923),\n\tKQU( 3601049438595312361), KQU( 9313624118352733770),\n\tKQU(13322966086117661798), KQU(16660005705644029394),\n\tKQU(11337677526988872373), KQU(13869299102574417795),\n\tKQU(15642043183045645437), KQU( 3021755569085880019),\n\tKQU( 4979741767761188161), KQU(13679979092079279587),\n\tKQU( 3344685842861071743), KQU(13947960059899588104),\n\tKQU(  305806934293368007), KQU( 5749173929201650029),\n\tKQU(11123724852118844098), KQU(15128987688788879802),\n\tKQU(15251651211024665009), KQU( 7689925933816577776),\n\tKQU(16732804392695859449), KQU(17087345401014078468),\n\tKQU(14315108589159048871), KQU( 4820700266619778917),\n\tKQU(16709637539357958441), KQU( 4936227875177351374),\n\tKQU( 2137907697912987247), KQU(11628565601408395420),\n\tKQU( 2333250549241556786), KQU( 5711200379577778637),\n\tKQU( 5170680131529031729), KQU(12620392043061335164),\n\tKQU(   95363390101096078), KQU( 5487981914081709462),\n\tKQU( 1763109823981838620), KQU( 3395861271473224396),\n\tKQU( 1300496844282213595), KQU( 6894316212820232902),\n\tKQU(10673859651135576674), KQU( 5911839658857903252),\n\tKQU(17407110743387299102), KQU( 8257427154623140385),\n\tKQU(11389003026741800267), KQU( 4070043211095013717),\n\tKQU(11663806997145259025), KQU(15265598950648798210),\n\tKQU(  630585789434030934), KQU( 3524446529213587334),\n\tKQU( 7186424168495184211), KQU(10806585451386379021),\n\tKQU(11120017753500499273), KQU( 1586837651387701301),\n\tKQU(17530454400954415544), KQU( 9991670045077880430),\n\tKQU( 7550997268990730180), KQU( 8640249196597379304),\n\tKQU( 3522203892786893823), KQU(10401116549878854788),\n\tKQU(13690285544733124852), KQU( 8295785675455774586),\n\tKQU(15535716172155117603), KQU( 3112108583723722511),\n\tKQU(17633179955339271113), KQU(18154208056063759375),\n\tKQU( 1866409236285815666), KQU(13326075895396412882),\n\tKQU( 8756261842948020025), KQU( 6281852999868439131),\n\tKQU(15087653361275292858), KQU(10333923911152949397),\n\tKQU( 5265567645757408500), KQU(12728041843210352184),\n\tKQU( 6347959327507828759), KQU(  154112802625564758),\n\tKQU(18235228308679780218), KQU( 3253805274673352418),\n\tKQU( 4849171610689031197), KQU(17948529398340432518),\n\tKQU(13803510475637409167), KQU(13506570190409883095),\n\tKQU(15870801273282960805), KQU( 8451286481299170773),\n\tKQU( 9562190620034457541), KQU( 8518905387449138364),\n\tKQU(12681306401363385655), KQU( 3788073690559762558),\n\tKQU( 5256820289573487769), KQU( 2752021372314875467),\n\tKQU( 6354035166862520716), KQU( 4328956378309739069),\n\tKQU(  449087441228269600), KQU( 5533508742653090868),\n\tKQU( 1260389420404746988), KQU(18175394473289055097),\n\tKQU( 1535467109660399420), KQU( 8818894282874061442),\n\tKQU(12140873243824811213), KQU(15031386653823014946),\n\tKQU( 1286028221456149232), KQU( 6329608889367858784),\n\tKQU( 9419654354945132725), KQU( 6094576547061672379),\n\tKQU(17706217251847450255), KQU( 1733495073065878126),\n\tKQU(16918923754607552663), KQU( 8881949849954945044),\n\tKQU(12938977706896313891), KQU(14043628638299793407),\n\tKQU(18393874581723718233), KQU( 6886318534846892044),\n\tKQU(14577870878038334081), KQU(13541558383439414119),\n\tKQU(13570472158807588273), KQU(18300760537910283361),\n\tKQU(  818368572800609205), KQU( 1417000585112573219),\n\tKQU(12337533143867683655), KQU(12433180994702314480),\n\tKQU(  778190005829189083), KQU(13667356216206524711),\n\tKQU( 9866149895295225230), KQU(11043240490417111999),\n\tKQU( 1123933826541378598), KQU( 6469631933605123610),\n\tKQU(14508554074431980040), KQU(13918931242962026714),\n\tKQU( 2870785929342348285), KQU(14786362626740736974),\n\tKQU(13176680060902695786), KQU( 9591778613541679456),\n\tKQU( 9097662885117436706), KQU(  749262234240924947),\n\tKQU( 1944844067793307093), KQU( 4339214904577487742),\n\tKQU( 8009584152961946551), KQU(16073159501225501777),\n\tKQU( 3335870590499306217), KQU(17088312653151202847),\n\tKQU( 3108893142681931848), KQU(16636841767202792021),\n\tKQU(10423316431118400637), KQU( 8008357368674443506),\n\tKQU(11340015231914677875), KQU(17687896501594936090),\n\tKQU(15173627921763199958), KQU(  542569482243721959),\n\tKQU(15071714982769812975), KQU( 4466624872151386956),\n\tKQU( 1901780715602332461), KQU( 9822227742154351098),\n\tKQU( 1479332892928648780), KQU( 6981611948382474400),\n\tKQU( 7620824924456077376), KQU(14095973329429406782),\n\tKQU( 7902744005696185404), KQU(15830577219375036920),\n\tKQU(10287076667317764416), KQU(12334872764071724025),\n\tKQU( 4419302088133544331), KQU(14455842851266090520),\n\tKQU(12488077416504654222), KQU( 7953892017701886766),\n\tKQU( 6331484925529519007), KQU( 4902145853785030022),\n\tKQU(17010159216096443073), KQU(11945354668653886087),\n\tKQU(15112022728645230829), KQU(17363484484522986742),\n\tKQU( 4423497825896692887), KQU( 8155489510809067471),\n\tKQU(  258966605622576285), KQU( 5462958075742020534),\n\tKQU( 6763710214913276228), KQU( 2368935183451109054),\n\tKQU(14209506165246453811), KQU( 2646257040978514881),\n\tKQU( 3776001911922207672), KQU( 1419304601390147631),\n\tKQU(14987366598022458284), KQU( 3977770701065815721),\n\tKQU(  730820417451838898), KQU( 3982991703612885327),\n\tKQU( 2803544519671388477), KQU(17067667221114424649),\n\tKQU( 2922555119737867166), KQU( 1989477584121460932),\n\tKQU(15020387605892337354), KQU( 9293277796427533547),\n\tKQU(10722181424063557247), KQU(16704542332047511651),\n\tKQU( 5008286236142089514), KQU(16174732308747382540),\n\tKQU(17597019485798338402), KQU(13081745199110622093),\n\tKQU( 8850305883842258115), KQU(12723629125624589005),\n\tKQU( 8140566453402805978), KQU(15356684607680935061),\n\tKQU(14222190387342648650), KQU(11134610460665975178),\n\tKQU( 1259799058620984266), KQU(13281656268025610041),\n\tKQU(  298262561068153992), KQU(12277871700239212922),\n\tKQU(13911297774719779438), KQU(16556727962761474934),\n\tKQU(17903010316654728010), KQU( 9682617699648434744),\n\tKQU(14757681836838592850), KQU( 1327242446558524473),\n\tKQU(11126645098780572792), KQU( 1883602329313221774),\n\tKQU( 2543897783922776873), KQU(15029168513767772842),\n\tKQU(12710270651039129878), KQU(16118202956069604504),\n\tKQU(15010759372168680524), KQU( 2296827082251923948),\n\tKQU(10793729742623518101), KQU(13829764151845413046),\n\tKQU(17769301223184451213), KQU( 3118268169210783372),\n\tKQU(17626204544105123127), KQU( 7416718488974352644),\n\tKQU(10450751996212925994), KQU( 9352529519128770586),\n\tKQU(  259347569641110140), KQU( 8048588892269692697),\n\tKQU( 1774414152306494058), KQU(10669548347214355622),\n\tKQU(13061992253816795081), KQU(18432677803063861659),\n\tKQU( 8879191055593984333), KQU(12433753195199268041),\n\tKQU(14919392415439730602), KQU( 6612848378595332963),\n\tKQU( 6320986812036143628), KQU(10465592420226092859),\n\tKQU( 4196009278962570808), KQU( 3747816564473572224),\n\tKQU(17941203486133732898), KQU( 2350310037040505198),\n\tKQU( 5811779859134370113), KQU(10492109599506195126),\n\tKQU( 7699650690179541274), KQU( 1954338494306022961),\n\tKQU(14095816969027231152), KQU( 5841346919964852061),\n\tKQU(14945969510148214735), KQU( 3680200305887550992),\n\tKQU( 6218047466131695792), KQU( 8242165745175775096),\n\tKQU(11021371934053307357), KQU( 1265099502753169797),\n\tKQU( 4644347436111321718), KQU( 3609296916782832859),\n\tKQU( 8109807992218521571), KQU(18387884215648662020),\n\tKQU(14656324896296392902), KQU(17386819091238216751),\n\tKQU(17788300878582317152), KQU( 7919446259742399591),\n\tKQU( 4466613134576358004), KQU(12928181023667938509),\n\tKQU(13147446154454932030), KQU(16552129038252734620),\n\tKQU( 8395299403738822450), KQU(11313817655275361164),\n\tKQU(  434258809499511718), KQU( 2074882104954788676),\n\tKQU( 7929892178759395518), KQU( 9006461629105745388),\n\tKQU( 5176475650000323086), KQU(11128357033468341069),\n\tKQU(12026158851559118955), KQU(14699716249471156500),\n\tKQU(  448982497120206757), KQU( 4156475356685519900),\n\tKQU( 6063816103417215727), KQU(10073289387954971479),\n\tKQU( 8174466846138590962), KQU( 2675777452363449006),\n\tKQU( 9090685420572474281), KQU( 6659652652765562060),\n\tKQU(12923120304018106621), KQU(11117480560334526775),\n\tKQU(  937910473424587511), KQU( 1838692113502346645),\n\tKQU(11133914074648726180), KQU( 7922600945143884053),\n\tKQU(13435287702700959550), KQU( 5287964921251123332),\n\tKQU(11354875374575318947), KQU(17955724760748238133),\n\tKQU(13728617396297106512), KQU( 4107449660118101255),\n\tKQU( 1210269794886589623), KQU(11408687205733456282),\n\tKQU( 4538354710392677887), KQU(13566803319341319267),\n\tKQU(17870798107734050771), KQU( 3354318982568089135),\n\tKQU( 9034450839405133651), KQU(13087431795753424314),\n\tKQU(  950333102820688239), KQU( 1968360654535604116),\n\tKQU(16840551645563314995), KQU( 8867501803892924995),\n\tKQU(11395388644490626845), KQU( 1529815836300732204),\n\tKQU(13330848522996608842), KQU( 1813432878817504265),\n\tKQU( 2336867432693429560), KQU(15192805445973385902),\n\tKQU( 2528593071076407877), KQU(  128459777936689248),\n\tKQU( 9976345382867214866), KQU( 6208885766767996043),\n\tKQU(14982349522273141706), KQU( 3099654362410737822),\n\tKQU(13776700761947297661), KQU( 8806185470684925550),\n\tKQU( 8151717890410585321), KQU(  640860591588072925),\n\tKQU(14592096303937307465), KQU( 9056472419613564846),\n\tKQU(14861544647742266352), KQU(12703771500398470216),\n\tKQU( 3142372800384138465), KQU( 6201105606917248196),\n\tKQU(18337516409359270184), KQU(15042268695665115339),\n\tKQU(15188246541383283846), KQU(12800028693090114519),\n\tKQU( 5992859621101493472), KQU(18278043971816803521),\n\tKQU( 9002773075219424560), KQU( 7325707116943598353),\n\tKQU( 7930571931248040822), KQU( 5645275869617023448),\n\tKQU( 7266107455295958487), KQU( 4363664528273524411),\n\tKQU(14313875763787479809), KQU(17059695613553486802),\n\tKQU( 9247761425889940932), KQU(13704726459237593128),\n\tKQU( 2701312427328909832), KQU(17235532008287243115),\n\tKQU(14093147761491729538), KQU( 6247352273768386516),\n\tKQU( 8268710048153268415), KQU( 7985295214477182083),\n\tKQU(15624495190888896807), KQU( 3772753430045262788),\n\tKQU( 9133991620474991698), KQU( 5665791943316256028),\n\tKQU( 7551996832462193473), KQU(13163729206798953877),\n\tKQU( 9263532074153846374), KQU( 1015460703698618353),\n\tKQU(17929874696989519390), KQU(18257884721466153847),\n\tKQU(16271867543011222991), KQU( 3905971519021791941),\n\tKQU(16814488397137052085), KQU( 1321197685504621613),\n\tKQU( 2870359191894002181), KQU(14317282970323395450),\n\tKQU(13663920845511074366), KQU( 2052463995796539594),\n\tKQU(14126345686431444337), KQU( 1727572121947022534),\n\tKQU(17793552254485594241), KQU( 6738857418849205750),\n\tKQU( 1282987123157442952), KQU(16655480021581159251),\n\tKQU( 6784587032080183866), KQU(14726758805359965162),\n\tKQU( 7577995933961987349), KQU(12539609320311114036),\n\tKQU(10789773033385439494), KQU( 8517001497411158227),\n\tKQU(10075543932136339710), KQU(14838152340938811081),\n\tKQU( 9560840631794044194), KQU(17445736541454117475),\n\tKQU(10633026464336393186), KQU(15705729708242246293),\n\tKQU( 1117517596891411098), KQU( 4305657943415886942),\n\tKQU( 4948856840533979263), KQU(16071681989041789593),\n\tKQU(13723031429272486527), KQU( 7639567622306509462),\n\tKQU(12670424537483090390), KQU( 9715223453097197134),\n\tKQU( 5457173389992686394), KQU(  289857129276135145),\n\tKQU(17048610270521972512), KQU(  692768013309835485),\n\tKQU(14823232360546632057), KQU(18218002361317895936),\n\tKQU( 3281724260212650204), KQU(16453957266549513795),\n\tKQU( 8592711109774511881), KQU(  929825123473369579),\n\tKQU(15966784769764367791), KQU( 9627344291450607588),\n\tKQU(10849555504977813287), KQU( 9234566913936339275),\n\tKQU( 6413807690366911210), KQU(10862389016184219267),\n\tKQU(13842504799335374048), KQU( 1531994113376881174),\n\tKQU( 2081314867544364459), KQU(16430628791616959932),\n\tKQU( 8314714038654394368), KQU( 9155473892098431813),\n\tKQU(12577843786670475704), KQU( 4399161106452401017),\n\tKQU( 1668083091682623186), KQU( 1741383777203714216),\n\tKQU( 2162597285417794374), KQU(15841980159165218736),\n\tKQU( 1971354603551467079), KQU( 1206714764913205968),\n\tKQU( 4790860439591272330), KQU(14699375615594055799),\n\tKQU( 8374423871657449988), KQU(10950685736472937738),\n\tKQU(  697344331343267176), KQU(10084998763118059810),\n\tKQU(12897369539795983124), KQU(12351260292144383605),\n\tKQU( 1268810970176811234), KQU( 7406287800414582768),\n\tKQU(  516169557043807831), KQU( 5077568278710520380),\n\tKQU( 3828791738309039304), KQU( 7721974069946943610),\n\tKQU( 3534670260981096460), KQU( 4865792189600584891),\n\tKQU(16892578493734337298), KQU( 9161499464278042590),\n\tKQU(11976149624067055931), KQU(13219479887277343990),\n\tKQU(14161556738111500680), KQU(14670715255011223056),\n\tKQU( 4671205678403576558), KQU(12633022931454259781),\n\tKQU(14821376219869187646), KQU(  751181776484317028),\n\tKQU( 2192211308839047070), KQU(11787306362361245189),\n\tKQU(10672375120744095707), KQU( 4601972328345244467),\n\tKQU(15457217788831125879), KQU( 8464345256775460809),\n\tKQU(10191938789487159478), KQU( 6184348739615197613),\n\tKQU(11425436778806882100), KQU( 2739227089124319793),\n\tKQU(  461464518456000551), KQU( 4689850170029177442),\n\tKQU( 6120307814374078625), KQU(11153579230681708671),\n\tKQU( 7891721473905347926), KQU(10281646937824872400),\n\tKQU( 3026099648191332248), KQU( 8666750296953273818),\n\tKQU(14978499698844363232), KQU(13303395102890132065),\n\tKQU( 8182358205292864080), KQU(10560547713972971291),\n\tKQU(11981635489418959093), KQU( 3134621354935288409),\n\tKQU(11580681977404383968), KQU(14205530317404088650),\n\tKQU( 5997789011854923157), KQU(13659151593432238041),\n\tKQU(11664332114338865086), KQU( 7490351383220929386),\n\tKQU( 7189290499881530378), KQU(15039262734271020220),\n\tKQU( 2057217285976980055), KQU(  555570804905355739),\n\tKQU(11235311968348555110), KQU(13824557146269603217),\n\tKQU(16906788840653099693), KQU( 7222878245455661677),\n\tKQU( 5245139444332423756), KQU( 4723748462805674292),\n\tKQU(12216509815698568612), KQU(17402362976648951187),\n\tKQU(17389614836810366768), KQU( 4880936484146667711),\n\tKQU( 9085007839292639880), KQU(13837353458498535449),\n\tKQU(11914419854360366677), KQU(16595890135313864103),\n\tKQU( 6313969847197627222), KQU(18296909792163910431),\n\tKQU(10041780113382084042), KQU( 2499478551172884794),\n\tKQU(11057894246241189489), KQU( 9742243032389068555),\n\tKQU(12838934582673196228), KQU(13437023235248490367),\n\tKQU(13372420669446163240), KQU( 6752564244716909224),\n\tKQU( 7157333073400313737), KQU(12230281516370654308),\n\tKQU( 1182884552219419117), KQU( 2955125381312499218),\n\tKQU(10308827097079443249), KQU( 1337648572986534958),\n\tKQU(16378788590020343939), KQU(  108619126514420935),\n\tKQU( 3990981009621629188), KQU( 5460953070230946410),\n\tKQU( 9703328329366531883), KQU(13166631489188077236),\n\tKQU( 1104768831213675170), KQU( 3447930458553877908),\n\tKQU( 8067172487769945676), KQU( 5445802098190775347),\n\tKQU( 3244840981648973873), KQU(17314668322981950060),\n\tKQU( 5006812527827763807), KQU(18158695070225526260),\n\tKQU( 2824536478852417853), KQU(13974775809127519886),\n\tKQU( 9814362769074067392), KQU(17276205156374862128),\n\tKQU(11361680725379306967), KQU( 3422581970382012542),\n\tKQU(11003189603753241266), KQU(11194292945277862261),\n\tKQU( 6839623313908521348), KQU(11935326462707324634),\n\tKQU( 1611456788685878444), KQU(13112620989475558907),\n\tKQU(  517659108904450427), KQU(13558114318574407624),\n\tKQU(15699089742731633077), KQU( 4988979278862685458),\n\tKQU( 8111373583056521297), KQU( 3891258746615399627),\n\tKQU( 8137298251469718086), KQU(12748663295624701649),\n\tKQU( 4389835683495292062), KQU( 5775217872128831729),\n\tKQU( 9462091896405534927), KQU( 8498124108820263989),\n\tKQU( 8059131278842839525), KQU(10503167994254090892),\n\tKQU(11613153541070396656), KQU(18069248738504647790),\n\tKQU(  570657419109768508), KQU( 3950574167771159665),\n\tKQU( 5514655599604313077), KQU( 2908460854428484165),\n\tKQU(10777722615935663114), KQU(12007363304839279486),\n\tKQU( 9800646187569484767), KQU( 8795423564889864287),\n\tKQU(14257396680131028419), KQU( 6405465117315096498),\n\tKQU( 7939411072208774878), KQU(17577572378528990006),\n\tKQU(14785873806715994850), KQU(16770572680854747390),\n\tKQU(18127549474419396481), KQU(11637013449455757750),\n\tKQU(14371851933996761086), KQU( 3601181063650110280),\n\tKQU( 4126442845019316144), KQU(10198287239244320669),\n\tKQU(18000169628555379659), KQU(18392482400739978269),\n\tKQU( 6219919037686919957), KQU( 3610085377719446052),\n\tKQU( 2513925039981776336), KQU(16679413537926716955),\n\tKQU(12903302131714909434), KQU( 5581145789762985009),\n\tKQU(12325955044293303233), KQU(17216111180742141204),\n\tKQU( 6321919595276545740), KQU( 3507521147216174501),\n\tKQU( 9659194593319481840), KQU(11473976005975358326),\n\tKQU(14742730101435987026), KQU(  492845897709954780),\n\tKQU(16976371186162599676), KQU(17712703422837648655),\n\tKQU( 9881254778587061697), KQU( 8413223156302299551),\n\tKQU( 1563841828254089168), KQU( 9996032758786671975),\n\tKQU(  138877700583772667), KQU(13003043368574995989),\n\tKQU( 4390573668650456587), KQU( 8610287390568126755),\n\tKQU(15126904974266642199), KQU( 6703637238986057662),\n\tKQU( 2873075592956810157), KQU( 6035080933946049418),\n\tKQU(13382846581202353014), KQU( 7303971031814642463),\n\tKQU(18418024405307444267), KQU( 5847096731675404647),\n\tKQU( 4035880699639842500), KQU(11525348625112218478),\n\tKQU( 3041162365459574102), KQU( 2604734487727986558),\n\tKQU(15526341771636983145), KQU(14556052310697370254),\n\tKQU(12997787077930808155), KQU( 9601806501755554499),\n\tKQU(11349677952521423389), KQU(14956777807644899350),\n\tKQU(16559736957742852721), KQU(12360828274778140726),\n\tKQU( 6685373272009662513), KQU(16932258748055324130),\n\tKQU(15918051131954158508), KQU( 1692312913140790144),\n\tKQU(  546653826801637367), KQU( 5341587076045986652),\n\tKQU(14975057236342585662), KQU(12374976357340622412),\n\tKQU(10328833995181940552), KQU(12831807101710443149),\n\tKQU(10548514914382545716), KQU( 2217806727199715993),\n\tKQU(12627067369242845138), KQU( 4598965364035438158),\n\tKQU(  150923352751318171), KQU(14274109544442257283),\n\tKQU( 4696661475093863031), KQU( 1505764114384654516),\n\tKQU(10699185831891495147), KQU( 2392353847713620519),\n\tKQU( 3652870166711788383), KQU( 8640653276221911108),\n\tKQU( 3894077592275889704), KQU( 4918592872135964845),\n\tKQU(16379121273281400789), KQU(12058465483591683656),\n\tKQU(11250106829302924945), KQU( 1147537556296983005),\n\tKQU( 6376342756004613268), KQU(14967128191709280506),\n\tKQU(18007449949790627628), KQU( 9497178279316537841),\n\tKQU( 7920174844809394893), KQU(10037752595255719907),\n\tKQU(15875342784985217697), KQU(15311615921712850696),\n\tKQU( 9552902652110992950), KQU(14054979450099721140),\n\tKQU( 5998709773566417349), KQU(18027910339276320187),\n\tKQU( 8223099053868585554), KQU( 7842270354824999767),\n\tKQU( 4896315688770080292), KQU(12969320296569787895),\n\tKQU( 2674321489185759961), KQU( 4053615936864718439),\n\tKQU(11349775270588617578), KQU( 4743019256284553975),\n\tKQU( 5602100217469723769), KQU(14398995691411527813),\n\tKQU( 7412170493796825470), KQU(  836262406131744846),\n\tKQU( 8231086633845153022), KQU( 5161377920438552287),\n\tKQU( 8828731196169924949), KQU(16211142246465502680),\n\tKQU( 3307990879253687818), KQU( 5193405406899782022),\n\tKQU( 8510842117467566693), KQU( 6070955181022405365),\n\tKQU(14482950231361409799), KQU(12585159371331138077),\n\tKQU( 3511537678933588148), KQU( 2041849474531116417),\n\tKQU(10944936685095345792), KQU(18303116923079107729),\n\tKQU( 2720566371239725320), KQU( 4958672473562397622),\n\tKQU( 3032326668253243412), KQU(13689418691726908338),\n\tKQU( 1895205511728843996), KQU( 8146303515271990527),\n\tKQU(16507343500056113480), KQU(  473996939105902919),\n\tKQU( 9897686885246881481), KQU(14606433762712790575),\n\tKQU( 6732796251605566368), KQU( 1399778120855368916),\n\tKQU(  935023885182833777), KQU(16066282816186753477),\n\tKQU( 7291270991820612055), KQU(17530230393129853844),\n\tKQU(10223493623477451366), KQU(15841725630495676683),\n\tKQU(17379567246435515824), KQU( 8588251429375561971),\n\tKQU(18339511210887206423), KQU(17349587430725976100),\n\tKQU(12244876521394838088), KQU( 6382187714147161259),\n\tKQU(12335807181848950831), KQU(16948885622305460665),\n\tKQU(13755097796371520506), KQU(14806740373324947801),\n\tKQU( 4828699633859287703), KQU( 8209879281452301604),\n\tKQU(12435716669553736437), KQU(13970976859588452131),\n\tKQU( 6233960842566773148), KQU(12507096267900505759),\n\tKQU( 1198713114381279421), KQU(14989862731124149015),\n\tKQU(15932189508707978949), KQU( 2526406641432708722),\n\tKQU(   29187427817271982), KQU( 1499802773054556353),\n\tKQU(10816638187021897173), KQU( 5436139270839738132),\n\tKQU( 6659882287036010082), KQU( 2154048955317173697),\n\tKQU(10887317019333757642), KQU(16281091802634424955),\n\tKQU(10754549879915384901), KQU(10760611745769249815),\n\tKQU( 2161505946972504002), KQU( 5243132808986265107),\n\tKQU(10129852179873415416), KQU(  710339480008649081),\n\tKQU( 7802129453068808528), KQU(17967213567178907213),\n\tKQU(15730859124668605599), KQU(13058356168962376502),\n\tKQU( 3701224985413645909), KQU(14464065869149109264),\n\tKQU( 9959272418844311646), KQU(10157426099515958752),\n\tKQU(14013736814538268528), KQU(17797456992065653951),\n\tKQU(17418878140257344806), KQU(15457429073540561521),\n\tKQU( 2184426881360949378), KQU( 2062193041154712416),\n\tKQU( 8553463347406931661), KQU( 4913057625202871854),\n\tKQU( 2668943682126618425), KQU(17064444737891172288),\n\tKQU( 4997115903913298637), KQU(12019402608892327416),\n\tKQU(17603584559765897352), KQU(11367529582073647975),\n\tKQU( 8211476043518436050), KQU( 8676849804070323674),\n\tKQU(18431829230394475730), KQU(10490177861361247904),\n\tKQU( 9508720602025651349), KQU( 7409627448555722700),\n\tKQU( 5804047018862729008), KQU(11943858176893142594),\n\tKQU(11908095418933847092), KQU( 5415449345715887652),\n\tKQU( 1554022699166156407), KQU( 9073322106406017161),\n\tKQU( 7080630967969047082), KQU(18049736940860732943),\n\tKQU(12748714242594196794), KQU( 1226992415735156741),\n\tKQU(17900981019609531193), KQU(11720739744008710999),\n\tKQU( 3006400683394775434), KQU(11347974011751996028),\n\tKQU( 3316999628257954608), KQU( 8384484563557639101),\n\tKQU(18117794685961729767), KQU( 1900145025596618194),\n\tKQU(17459527840632892676), KQU( 5634784101865710994),\n\tKQU( 7918619300292897158), KQU( 3146577625026301350),\n\tKQU( 9955212856499068767), KQU( 1873995843681746975),\n\tKQU( 1561487759967972194), KQU( 8322718804375878474),\n\tKQU(11300284215327028366), KQU( 4667391032508998982),\n\tKQU( 9820104494306625580), KQU(17922397968599970610),\n\tKQU( 1784690461886786712), KQU(14940365084341346821),\n\tKQU( 5348719575594186181), KQU(10720419084507855261),\n\tKQU(14210394354145143274), KQU( 2426468692164000131),\n\tKQU(16271062114607059202), KQU(14851904092357070247),\n\tKQU( 6524493015693121897), KQU( 9825473835127138531),\n\tKQU(14222500616268569578), KQU(15521484052007487468),\n\tKQU(14462579404124614699), KQU(11012375590820665520),\n\tKQU(11625327350536084927), KQU(14452017765243785417),\n\tKQU( 9989342263518766305), KQU( 3640105471101803790),\n\tKQU( 4749866455897513242), KQU(13963064946736312044),\n\tKQU(10007416591973223791), KQU(18314132234717431115),\n\tKQU( 3286596588617483450), KQU( 7726163455370818765),\n\tKQU( 7575454721115379328), KQU( 5308331576437663422),\n\tKQU(18288821894903530934), KQU( 8028405805410554106),\n\tKQU(15744019832103296628), KQU(  149765559630932100),\n\tKQU( 6137705557200071977), KQU(14513416315434803615),\n\tKQU(11665702820128984473), KQU(  218926670505601386),\n\tKQU( 6868675028717769519), KQU(15282016569441512302),\n\tKQU( 5707000497782960236), KQU( 6671120586555079567),\n\tKQU( 2194098052618985448), KQU(16849577895477330978),\n\tKQU(12957148471017466283), KQU( 1997805535404859393),\n\tKQU( 1180721060263860490), KQU(13206391310193756958),\n\tKQU(12980208674461861797), KQU( 3825967775058875366),\n\tKQU(17543433670782042631), KQU( 1518339070120322730),\n\tKQU(16344584340890991669), KQU( 2611327165318529819),\n\tKQU(11265022723283422529), KQU( 4001552800373196817),\n\tKQU(14509595890079346161), KQU( 3528717165416234562),\n\tKQU(18153222571501914072), KQU( 9387182977209744425),\n\tKQU(10064342315985580021), KQU(11373678413215253977),\n\tKQU( 2308457853228798099), KQU( 9729042942839545302),\n\tKQU( 7833785471140127746), KQU( 6351049900319844436),\n\tKQU(14454610627133496067), KQU(12533175683634819111),\n\tKQU(15570163926716513029), KQU(13356980519185762498)\n};\n\nTEST_BEGIN(test_gen_rand_32) {\n\tuint32_t array32[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tuint32_t array32_2[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint32_t r32;\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size32(), BLOCK_SIZE,\n\t    \"Array size too small\");\n\tctx = init_gen_rand(1234);\n\tfill_array32(ctx, array32, BLOCK_SIZE);\n\tfill_array32(ctx, array32_2, BLOCK_SIZE);\n\tfini_gen_rand(ctx);\n\n\tctx = init_gen_rand(1234);\n\tfor (i = 0; i < BLOCK_SIZE; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u32_eq(array32[i], init_gen_rand_32_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32[i],\n\t\t    \"Mismatch at array32[%d]=%x, gen=%x\", i, array32[i], r32);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32_2[i],\n\t\t    \"Mismatch at array32_2[%d]=%x, gen=%x\", i, array32_2[i],\n\t\t    r32);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nTEST_BEGIN(test_by_array_32) {\n\tuint32_t array32[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tuint32_t array32_2[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint32_t ini[4] = {0x1234, 0x5678, 0x9abc, 0xdef0};\n\tuint32_t r32;\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size32(), BLOCK_SIZE,\n\t    \"Array size too small\");\n\tctx = init_by_array(ini, 4);\n\tfill_array32(ctx, array32, BLOCK_SIZE);\n\tfill_array32(ctx, array32_2, BLOCK_SIZE);\n\tfini_gen_rand(ctx);\n\n\tctx = init_by_array(ini, 4);\n\tfor (i = 0; i < BLOCK_SIZE; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u32_eq(array32[i], init_by_array_32_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32[i],\n\t\t    \"Mismatch at array32[%d]=%x, gen=%x\", i, array32[i], r32);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32_2[i],\n\t\t    \"Mismatch at array32_2[%d]=%x, gen=%x\", i, array32_2[i],\n\t\t    r32);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nTEST_BEGIN(test_gen_rand_64) {\n\tuint64_t array64[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tuint64_t array64_2[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint64_t r;\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size64(), BLOCK_SIZE64,\n\t    \"Array size too small\");\n\tctx = init_gen_rand(4321);\n\tfill_array64(ctx, array64, BLOCK_SIZE64);\n\tfill_array64(ctx, array64_2, BLOCK_SIZE64);\n\tfini_gen_rand(ctx);\n\n\tctx = init_gen_rand(4321);\n\tfor (i = 0; i < BLOCK_SIZE64; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u64_eq(array64[i], init_gen_rand_64_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64[i],\n\t\t    \"Mismatch at array64[%d]=%\"FMTx64\", gen=%\"FMTx64, i,\n\t\t    array64[i], r);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64_2[i],\n\t\t    \"Mismatch at array64_2[%d]=%\"FMTx64\" gen=%\"FMTx64\"\", i,\n\t\t    array64_2[i], r);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nTEST_BEGIN(test_by_array_64) {\n\tuint64_t array64[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tuint64_t array64_2[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint64_t r;\n\tuint32_t ini[] = {5, 4, 3, 2, 1};\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size64(), BLOCK_SIZE64,\n\t    \"Array size too small\");\n\tctx = init_by_array(ini, 5);\n\tfill_array64(ctx, array64, BLOCK_SIZE64);\n\tfill_array64(ctx, array64_2, BLOCK_SIZE64);\n\tfini_gen_rand(ctx);\n\n\tctx = init_by_array(ini, 5);\n\tfor (i = 0; i < BLOCK_SIZE64; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u64_eq(array64[i], init_by_array_64_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64[i],\n\t\t    \"Mismatch at array64[%d]=%\"FMTx64\" gen=%\"FMTx64, i,\n\t\t    array64[i], r);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64_2[i],\n\t\t    \"Mismatch at array64_2[%d]=%\"FMTx64\" gen=%\"FMTx64, i,\n\t\t    array64_2[i], r);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_gen_rand_32,\n\t    test_by_array_32,\n\t    test_gen_rand_64,\n\t    test_by_array_64);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/a0.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_a0) {\n\tvoid *p;\n\n\tp = a0malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected a0malloc() error\");\n\ta0dalloc(p);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_malloc_init(\n\t    test_a0);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/arena_reset.c",
    "content": "#ifndef ARENA_RESET_PROF_C_\n#include \"test/jemalloc_test.h\"\n#endif\n\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/rtree.h\"\n\n#include \"test/extent_hooks.h\"\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nsmall(void) {\n\treturn get_nsizes_impl(\"arenas.nbins\");\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_small_size(size_t ind) {\n\treturn get_size_impl(\"arenas.bin.0.size\", ind);\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\n/* Like ivsalloc(), but safe to call on discarded allocations. */\nstatic size_t\nvsalloc(tsdn_t *tsdn, const void *ptr) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent;\n\tszind_t szind;\n\tif (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, false, &extent, &szind)) {\n\t\treturn 0;\n\t}\n\n\tif (extent == NULL) {\n\t\treturn 0;\n\t}\n\tif (extent_state_get(extent) != extent_state_active) {\n\t\treturn 0;\n\t}\n\n\tif (szind == NSIZES) {\n\t\treturn 0;\n\t}\n\n\treturn sz_index2size(szind);\n}\n\nstatic unsigned\ndo_arena_create(extent_hooks_t *h) {\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz,\n\t    (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,\n\t    \"Unexpected mallctl() failure\");\n\treturn arena_ind;\n}\n\nstatic void\ndo_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs) {\n#define NLARGE\t32\n\tunsigned nsmall, nlarge, i;\n\tsize_t sz;\n\tint flags;\n\ttsdn_t *tsdn;\n\n\tflags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\n\tnsmall = get_nsmall();\n\tnlarge = get_nlarge() > NLARGE ? NLARGE : get_nlarge();\n\t*nptrs = nsmall + nlarge;\n\t*ptrs = (void **)malloc(*nptrs * sizeof(void *));\n\tassert_ptr_not_null(*ptrs, \"Unexpected malloc() failure\");\n\n\t/* Allocate objects with a wide range of sizes. */\n\tfor (i = 0; i < nsmall; i++) {\n\t\tsz = get_small_size(i);\n\t\t(*ptrs)[i] = mallocx(sz, flags);\n\t\tassert_ptr_not_null((*ptrs)[i],\n\t\t    \"Unexpected mallocx(%zu, %#x) failure\", sz, flags);\n\t}\n\tfor (i = 0; i < nlarge; i++) {\n\t\tsz = get_large_size(i);\n\t\t(*ptrs)[nsmall + i] = mallocx(sz, flags);\n\t\tassert_ptr_not_null((*ptrs)[i],\n\t\t    \"Unexpected mallocx(%zu, %#x) failure\", sz, flags);\n\t}\n\n\ttsdn = tsdn_fetch();\n\n\t/* Verify allocations. */\n\tfor (i = 0; i < *nptrs; i++) {\n\t\tassert_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0,\n\t\t    \"Allocation should have queryable size\");\n\t}\n}\n\nstatic void\ndo_arena_reset_post(void **ptrs, unsigned nptrs, unsigned arena_ind) {\n\ttsdn_t *tsdn;\n\tunsigned i;\n\n\ttsdn = tsdn_fetch();\n\n\tif (have_background_thread) {\n\t\tmalloc_mutex_lock(tsdn,\n\t\t    &background_thread_info[arena_ind % ncpus].mtx);\n\t}\n\t/* Verify allocations no longer exist. */\n\tfor (i = 0; i < nptrs; i++) {\n\t\tassert_zu_eq(vsalloc(tsdn, ptrs[i]), 0,\n\t\t    \"Allocation should no longer exist\");\n\t}\n\tif (have_background_thread) {\n\t\tmalloc_mutex_unlock(tsdn,\n\t\t    &background_thread_info[arena_ind % ncpus].mtx);\n\t}\n\n\tfree(ptrs);\n}\n\nstatic void\ndo_arena_reset_destroy(const char *name, unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(name, mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nstatic void\ndo_arena_reset(unsigned arena_ind) {\n\tdo_arena_reset_destroy(\"arena.0.reset\", arena_ind);\n}\n\nstatic void\ndo_arena_destroy(unsigned arena_ind) {\n\tdo_arena_reset_destroy(\"arena.0.destroy\", arena_ind);\n}\n\nTEST_BEGIN(test_arena_reset) {\n\tunsigned arena_ind;\n\tvoid **ptrs;\n\tunsigned nptrs;\n\n\tarena_ind = do_arena_create(NULL);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\tdo_arena_reset(arena_ind);\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n}\nTEST_END\n\nstatic bool\narena_i_initialized(unsigned arena_ind, bool refresh) {\n\tbool initialized;\n\tsize_t mib[3];\n\tsize_t miblen, sz;\n\n\tif (refresh) {\n\t\tuint64_t epoch = 1;\n\t\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t\t    sizeof(epoch)), 0, \"Unexpected mallctl() failure\");\n\t}\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.initialized\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tsz = sizeof(initialized);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL,\n\t    0), 0, \"Unexpected mallctlbymib() failure\");\n\n\treturn initialized;\n}\n\nTEST_BEGIN(test_arena_destroy_initial) {\n\tassert_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),\n\t    \"Destroyed arena stats should not be initialized\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_destroy_hooks_default) {\n\tunsigned arena_ind, arena_ind_another, arena_ind_prev;\n\tvoid **ptrs;\n\tunsigned nptrs;\n\n\tarena_ind = do_arena_create(NULL);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\n\tassert_false(arena_i_initialized(arena_ind, false),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should be initialized\");\n\n\t/*\n\t * Create another arena before destroying one, to better verify arena\n\t * index reuse.\n\t */\n\tarena_ind_another = do_arena_create(NULL);\n\n\tdo_arena_destroy(arena_ind);\n\n\tassert_false(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),\n\t    \"Destroyed arena stats should be initialized\");\n\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n\n\tarena_ind_prev = arena_ind;\n\tarena_ind = do_arena_create(NULL);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\tassert_u_eq(arena_ind, arena_ind_prev,\n\t    \"Arena index should have been recycled\");\n\tdo_arena_destroy(arena_ind);\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n\n\tdo_arena_destroy(arena_ind_another);\n}\nTEST_END\n\n/*\n * Actually unmap extents, regardless of opt_retain, so that attempts to access\n * a destroyed arena's memory will segfault.\n */\nstatic bool\nextent_dalloc_unmap(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, \"\n\t    \"arena_ind=%u)\\n\", __func__, extent_hooks, addr, size, committed ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap,\n\t    \"Wrong hook function\");\n\tcalled_dalloc = true;\n\tif (!try_dalloc) {\n\t\treturn true;\n\t}\n\tpages_unmap(addr, size);\n\tdid_dalloc = true;\n\treturn false;\n}\n\nstatic extent_hooks_t hooks_orig;\n\nstatic extent_hooks_t hooks_unmap = {\n\textent_alloc_hook,\n\textent_dalloc_unmap, /* dalloc */\n\textent_destroy_hook,\n\textent_commit_hook,\n\textent_decommit_hook,\n\textent_purge_lazy_hook,\n\textent_purge_forced_hook,\n\textent_split_hook,\n\textent_merge_hook\n};\n\nTEST_BEGIN(test_arena_destroy_hooks_unmap) {\n\tunsigned arena_ind;\n\tvoid **ptrs;\n\tunsigned nptrs;\n\n\textent_hooks_prep();\n\ttry_decommit = false;\n\tmemcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));\n\tmemcpy(&hooks, &hooks_unmap, sizeof(extent_hooks_t));\n\n\tdid_alloc = false;\n\tarena_ind = do_arena_create(&hooks);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\n\tassert_true(did_alloc, \"Expected alloc\");\n\n\tassert_false(arena_i_initialized(arena_ind, false),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should be initialized\");\n\n\tdid_dalloc = false;\n\tdo_arena_destroy(arena_ind);\n\tassert_true(did_dalloc, \"Expected dalloc\");\n\n\tassert_false(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),\n\t    \"Destroyed arena stats should be initialized\");\n\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n\n\tmemcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_arena_reset,\n\t    test_arena_destroy_initial,\n\t    test_arena_destroy_hooks_default,\n\t    test_arena_destroy_hooks_unmap);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/arena_reset_prof.c",
    "content": "#include \"test/jemalloc_test.h\"\n#define ARENA_RESET_PROF_C_\n\n#include \"arena_reset.c\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/arena_reset_prof.sh",
    "content": "#!/bin/sh\n\nexport MALLOC_CONF=\"prof:true,lg_prof_sample:0\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/atomic.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * We *almost* have consistent short names (e.g. \"u32\" for uint32_t, \"b\" for\n * bool, etc.  The one exception is that the short name for void * is \"p\" in\n * some places and \"ptr\" in others.  In the long run it would be nice to unify\n * these, but in the short run we'll use this shim.\n */\n#define assert_p_eq assert_ptr_eq\n\n/*\n * t: the non-atomic type, like \"uint32_t\".\n * ta: the short name for the type, like \"u32\".\n * val[1,2,3]: Values of the given type.  The CAS tests use val2 for expected,\n * and val3 for desired.\n */\n\n#define DO_TESTS(t, ta, val1, val2, val3) do {\t\t\t\t\\\n\tt val;\t\t\t\t\t\t\t\t\\\n\tt expected;\t\t\t\t\t\t\t\\\n\tbool success;\t\t\t\t\t\t\t\\\n\t/* This (along with the load below) also tests ATOMIC_LOAD. */\t\\\n\tatomic_##ta##_t atom = ATOMIC_INIT(val1);\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* ATOMIC_INIT and load. */\t\t\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1, val, \"Load or init failed\");\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Store. */\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tatomic_store_##ta(&atom, val2, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val2, val, \"Store failed\");\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Exchange. */\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_exchange_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val, \"Exchange returned invalid value\");\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val2, val, \"Exchange store invalid value\");\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* \t\t\t\t\t\t\t\t\\\n\t * Weak CAS.  Spurious failures are allowed, so we loop a few\t\\\n\t * times.\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tsuccess = false;\t\t\t\t\t\t\\\n\tfor (int i = 0; i < 10 && !success; i++) {\t\t\t\\\n\t\texpected = val2;\t\t\t\t\t\\\n\t\tsuccess = atomic_compare_exchange_weak_##ta(&atom,\t\\\n\t\t    &expected, val3, ATOMIC_RELAXED, ATOMIC_RELAXED);\t\\\n\t\tassert_##ta##_eq(val1, expected, \t\t\t\\\n\t\t    \"CAS should update expected\");\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tassert_b_eq(val1 == val2, success,\t\t\t\t\\\n\t    \"Weak CAS did the wrong state update\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tif (success) {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val3, val,\t\t\t\t\\\n\t\t    \"Successful CAS should update atomic\");\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val1, val,\t\t\t\t\\\n\t\t    \"Unsuccessful CAS should not update atomic\");\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Strong CAS. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\texpected = val2;\t\t\t\t\t\t\\\n\tsuccess = atomic_compare_exchange_strong_##ta(&atom, &expected,\t\\\n\t    val3, ATOMIC_RELAXED, ATOMIC_RELAXED);\t\t\t\\\n\tassert_b_eq(val1 == val2, success,\t\t\t\t\\\n\t    \"Strong CAS did the wrong state update\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tif (success) {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val3, val,\t\t\t\t\\\n\t\t    \"Successful CAS should update atomic\");\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val1, val,\t\t\t\t\\\n\t\t    \"Unsuccessful CAS should not update atomic\");\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\n#define DO_INTEGER_TESTS(t, ta, val1, val2) do {\t\t\t\\\n\tatomic_##ta##_t atom;\t\t\t\t\t\t\\\n\tt val;\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-add. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_add_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-add should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 + val2, val,\t\t\t\t\\\n\t    \"Fetch-add should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-sub. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_sub_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-sub should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 - val2, val,\t\t\t\t\\\n\t    \"Fetch-sub should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-and. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_and_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-and should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 & val2, val,\t\t\t\t\\\n\t    \"Fetch-and should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-or. */\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_or_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-or should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 | val2, val,\t\t\t\t\\\n\t    \"Fetch-or should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-xor. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_xor_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-xor should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 ^ val2, val,\t\t\t\t\\\n\t    \"Fetch-xor should update atomic\");\t\t\t\t\\\n} while (0)\n\n#define TEST_STRUCT(t, ta)\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tt val1;\t\t\t\t\t\t\t\t\\\n\tt val2;\t\t\t\t\t\t\t\t\\\n\tt val3;\t\t\t\t\t\t\t\t\\\n} ta##_test_t;\n\n#define TEST_CASES(t) {\t\t\t\t\t\t\t\\\n\t{(t)-1, (t)-1, (t)-2},\t\t\t\t\t\t\\\n\t{(t)-1, (t) 0, (t)-2},\t\t\t\t\t\t\\\n\t{(t)-1, (t) 1, (t)-2},\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t{(t) 0, (t)-1, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 0, (t) 0, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 0, (t) 1, (t)-2},\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t{(t) 1, (t)-1, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 1, (t) 0, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 1, (t) 1, (t)-2},\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t{(t)0, (t)-(1 << 22), (t)-2},\t\t\t\t\t\\\n\t{(t)0, (t)(1 << 22), (t)-2},\t\t\t\t\t\\\n\t{(t)(1 << 22), (t)-(1 << 22), (t)-2},\t\t\t\t\\\n\t{(t)(1 << 22), (t)(1 << 22), (t)-2}\t\t\t\t\\\n}\n\n#define TEST_BODY(t, ta) do {\t\t\t\t\t\t\\\n\tconst ta##_test_t tests[] = TEST_CASES(t);\t\t\t\\\n\tfor (unsigned i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {\t\\\n\t\tta##_test_t test = tests[i];\t\t\t\t\\\n\t\tDO_TESTS(t, ta, test.val1, test.val2, test.val3);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define INTEGER_TEST_BODY(t, ta) do {\t\t\t\t\t\\\n\tconst ta##_test_t tests[] = TEST_CASES(t);\t\t\t\\\n\tfor (unsigned i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {\t\\\n\t\tta##_test_t test = tests[i];\t\t\t\t\\\n\t\tDO_TESTS(t, ta, test.val1, test.val2, test.val3);\t\\\n\t\tDO_INTEGER_TESTS(t, ta, test.val1, test.val2);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\nTEST_STRUCT(uint64_t, u64);\nTEST_BEGIN(test_atomic_u64) {\n#if !(LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)\n\ttest_skip(\"64-bit atomic operations not supported\");\n#else\n\tINTEGER_TEST_BODY(uint64_t, u64);\n#endif\n}\nTEST_END\n\n\nTEST_STRUCT(uint32_t, u32);\nTEST_BEGIN(test_atomic_u32) {\n\tINTEGER_TEST_BODY(uint32_t, u32);\n}\nTEST_END\n\nTEST_STRUCT(void *, p);\nTEST_BEGIN(test_atomic_p) {\n\tTEST_BODY(void *, p);\n}\nTEST_END\n\nTEST_STRUCT(size_t, zu);\nTEST_BEGIN(test_atomic_zu) {\n\tINTEGER_TEST_BODY(size_t, zu);\n}\nTEST_END\n\nTEST_STRUCT(ssize_t, zd);\nTEST_BEGIN(test_atomic_zd) {\n\tINTEGER_TEST_BODY(ssize_t, zd);\n}\nTEST_END\n\n\nTEST_STRUCT(unsigned, u);\nTEST_BEGIN(test_atomic_u) {\n\tINTEGER_TEST_BODY(unsigned, u);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_atomic_u64,\n\t    test_atomic_u32,\n\t    test_atomic_p,\n\t    test_atomic_zu,\n\t    test_atomic_zd,\n\t    test_atomic_u);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/background_thread.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\nstatic void\ntest_switch_background_thread_ctl(bool new_val) {\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\n\te1 = new_val;\n\tassert_d_eq(mallctl(\"background_thread\", (void *)&e0, &sz,\n\t    &e1, sz), 0, \"Unexpected mallctl() failure\");\n\tassert_b_eq(e0, !e1,\n\t    \"background_thread should be %d before.\\n\", !e1);\n\tif (e1) {\n\t\tassert_zu_gt(n_background_threads, 0,\n\t\t    \"Number of background threads should be non zero.\\n\");\n\t} else {\n\t\tassert_zu_eq(n_background_threads, 0,\n\t\t    \"Number of background threads should be zero.\\n\");\n\t}\n}\n\nstatic void\ntest_repeat_background_thread_ctl(bool before) {\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\n\te1 = before;\n\tassert_d_eq(mallctl(\"background_thread\", (void *)&e0, &sz,\n\t    &e1, sz), 0, \"Unexpected mallctl() failure\");\n\tassert_b_eq(e0, before,\n\t    \"background_thread should be %d.\\n\", before);\n\tif (e1) {\n\t\tassert_zu_gt(n_background_threads, 0,\n\t\t    \"Number of background threads should be non zero.\\n\");\n\t} else {\n\t\tassert_zu_eq(n_background_threads, 0,\n\t\t    \"Number of background threads should be zero.\\n\");\n\t}\n}\n\nTEST_BEGIN(test_background_thread_ctl) {\n\ttest_skip_if(!have_background_thread);\n\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\n\tassert_d_eq(mallctl(\"opt.background_thread\", (void *)&e0, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctl(\"background_thread\", (void *)&e1, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\tassert_b_eq(e0, e1,\n\t    \"Default and opt.background_thread does not match.\\n\");\n\tif (e0) {\n\t\ttest_switch_background_thread_ctl(false);\n\t}\n\tassert_zu_eq(n_background_threads, 0,\n\t    \"Number of background threads should be 0.\\n\");\n\n\tfor (unsigned i = 0; i < 4; i++) {\n\t\ttest_switch_background_thread_ctl(true);\n\t\ttest_repeat_background_thread_ctl(true);\n\t\ttest_repeat_background_thread_ctl(true);\n\n\t\ttest_switch_background_thread_ctl(false);\n\t\ttest_repeat_background_thread_ctl(false);\n\t\ttest_repeat_background_thread_ctl(false);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_background_thread_running) {\n\ttest_skip_if(!have_background_thread);\n\ttest_skip_if(!config_stats);\n\n#if defined(JEMALLOC_BACKGROUND_THREAD)\n\ttsd_t *tsd = tsd_fetch();\n\tbackground_thread_info_t *info = &background_thread_info[0];\n\n\ttest_repeat_background_thread_ctl(false);\n\ttest_switch_background_thread_ctl(true);\n\tassert_b_eq(info->state, background_thread_started,\n\t    \"Background_thread did not start.\\n\");\n\n\tnstime_t start, now;\n\tnstime_init(&start, 0);\n\tnstime_update(&start);\n\n\tbool ran = false;\n\twhile (true) {\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\tif (info->tot_n_runs > 0) {\n\t\t\tran = true;\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\tif (ran) {\n\t\t\tbreak;\n\t\t}\n\n\t\tnstime_init(&now, 0);\n\t\tnstime_update(&now);\n\t\tnstime_subtract(&now, &start);\n\t\tassert_u64_lt(nstime_sec(&now), 1000,\n\t\t    \"Background threads did not run for 1000 seconds.\");\n\t\tsleep(1);\n\t}\n\ttest_switch_background_thread_ctl(false);\n#endif\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Background_thread creation tests reentrancy naturally. */\n\treturn test_no_reentrancy(\n\t    test_background_thread_ctl,\n\t    test_background_thread_running);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/background_thread_enable.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nconst char *malloc_conf = \"background_thread:false,narenas:1,max_background_threads:20\";\n\nTEST_BEGIN(test_deferred) {\n\ttest_skip_if(!have_background_thread);\n\n\tunsigned id;\n\tsize_t sz_u = sizeof(unsigned);\n\n\t/*\n\t * 10 here is somewhat arbitrary, except insofar as we want to ensure\n\t * that the number of background threads is smaller than the number of\n\t * arenas.  I'll ragequit long before we have to spin up 10 threads per\n\t * cpu to handle background purging, so this is a conservative\n\t * approximation.\n\t */\n\tfor (unsigned i = 0; i < 10 * ncpus; i++) {\n\t\tassert_d_eq(mallctl(\"arenas.create\", &id, &sz_u, NULL, 0), 0,\n\t\t    \"Failed to create arena\");\n\t}\n\n\tbool enable = true;\n\tsize_t sz_b = sizeof(bool);\n\tassert_d_eq(mallctl(\"background_thread\", NULL, NULL, &enable, sz_b), 0,\n\t    \"Failed to enable background threads\");\n\tenable = false;\n\tassert_d_eq(mallctl(\"background_thread\", NULL, NULL, &enable, sz_b), 0,\n\t    \"Failed to disable background threads\");\n}\nTEST_END\n\nTEST_BEGIN(test_max_background_threads) {\n\ttest_skip_if(!have_background_thread);\n\n\tsize_t maxt;\n\tsize_t opt_maxt;\n\tsize_t sz_m = sizeof(maxt);\n\tassert_d_eq(mallctl(\"opt.max_background_threads\",\n\t\t\t    &opt_maxt, &sz_m, NULL, 0), 0,\n\t\t\t    \"Failed to get opt.max_background_threads\");\n\tassert_d_eq(mallctl(\"max_background_threads\", &maxt, &sz_m, NULL, 0), 0,\n\t\t    \"Failed to get max background threads\");\n\tassert_zu_eq(20, maxt, \"should be ncpus\");\n\tassert_zu_eq(opt_maxt, maxt,\n\t\t     \"max_background_threads and \"\n\t\t     \"opt.max_background_threads should match\");\n\tassert_d_eq(mallctl(\"max_background_threads\", NULL, NULL, &maxt, sz_m),\n\t\t    0, \"Failed to set max background threads\");\n\n\tunsigned id;\n\tsize_t sz_u = sizeof(unsigned);\n\n\tfor (unsigned i = 0; i < 10 * ncpus; i++) {\n\t\tassert_d_eq(mallctl(\"arenas.create\", &id, &sz_u, NULL, 0), 0,\n\t\t    \"Failed to create arena\");\n\t}\n\n\tbool enable = true;\n\tsize_t sz_b = sizeof(bool);\n\tassert_d_eq(mallctl(\"background_thread\", NULL, NULL, &enable, sz_b), 0,\n\t    \"Failed to enable background threads\");\n\tassert_zu_eq(n_background_threads, maxt,\n\t\t     \"Number of background threads should be 3.\\n\");\n\tmaxt = 10;\n\tassert_d_eq(mallctl(\"max_background_threads\", NULL, NULL, &maxt, sz_m),\n\t\t    0, \"Failed to set max background threads\");\n\tassert_zu_eq(n_background_threads, maxt,\n\t\t     \"Number of background threads should be 10.\\n\");\n\tmaxt = 3;\n\tassert_d_eq(mallctl(\"max_background_threads\", NULL, NULL, &maxt, sz_m),\n\t\t    0, \"Failed to set max background threads\");\n\tassert_zu_eq(n_background_threads, maxt,\n\t\t     \"Number of background threads should be 3.\\n\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t\ttest_deferred,\n\t\ttest_max_background_threads);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/base.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"test/extent_hooks.h\"\n\nstatic extent_hooks_t hooks_null = {\n\textent_alloc_hook,\n\tNULL, /* dalloc */\n\tNULL, /* destroy */\n\tNULL, /* commit */\n\tNULL, /* decommit */\n\tNULL, /* purge_lazy */\n\tNULL, /* purge_forced */\n\tNULL, /* split */\n\tNULL /* merge */\n};\n\nstatic extent_hooks_t hooks_not_null = {\n\textent_alloc_hook,\n\textent_dalloc_hook,\n\textent_destroy_hook,\n\tNULL, /* commit */\n\textent_decommit_hook,\n\textent_purge_lazy_hook,\n\textent_purge_forced_hook,\n\tNULL, /* split */\n\tNULL /* merge */\n};\n\nTEST_BEGIN(test_base_hooks_default) {\n\tbase_t *base;\n\tsize_t allocated0, allocated1, resident, mapped, n_thp;\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd_fetch());\n\tbase = base_new(tsdn, 0, (extent_hooks_t *)&extent_hooks_default);\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated0, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated0, sizeof(base_t),\n\t\t    \"Base header should count as allocated\");\n\t\tif (opt_metadata_thp == metadata_thp_always) {\n\t\t\tassert_zu_gt(n_thp, 0,\n\t\t\t    \"Base should have 1 THP at least.\");\n\t\t}\n\t}\n\n\tassert_ptr_not_null(base_alloc(tsdn, base, 42, 1),\n\t    \"Unexpected base_alloc() failure\");\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated1, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated1 - allocated0, 42,\n\t\t    \"At least 42 bytes were allocated by base_alloc()\");\n\t}\n\n\tbase_delete(tsdn, base);\n}\nTEST_END\n\nTEST_BEGIN(test_base_hooks_null) {\n\textent_hooks_t hooks_orig;\n\tbase_t *base;\n\tsize_t allocated0, allocated1, resident, mapped, n_thp;\n\n\textent_hooks_prep();\n\ttry_dalloc = false;\n\ttry_destroy = true;\n\ttry_decommit = false;\n\ttry_purge_lazy = false;\n\ttry_purge_forced = false;\n\tmemcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));\n\tmemcpy(&hooks, &hooks_null, sizeof(extent_hooks_t));\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd_fetch());\n\tbase = base_new(tsdn, 0, &hooks);\n\tassert_ptr_not_null(base, \"Unexpected base_new() failure\");\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated0, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated0, sizeof(base_t),\n\t\t    \"Base header should count as allocated\");\n\t\tif (opt_metadata_thp == metadata_thp_always) {\n\t\t\tassert_zu_gt(n_thp, 0,\n\t\t\t    \"Base should have 1 THP at least.\");\n\t\t}\n\t}\n\n\tassert_ptr_not_null(base_alloc(tsdn, base, 42, 1),\n\t    \"Unexpected base_alloc() failure\");\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated1, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated1 - allocated0, 42,\n\t\t    \"At least 42 bytes were allocated by base_alloc()\");\n\t}\n\n\tbase_delete(tsdn, base);\n\n\tmemcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));\n}\nTEST_END\n\nTEST_BEGIN(test_base_hooks_not_null) {\n\textent_hooks_t hooks_orig;\n\tbase_t *base;\n\tvoid *p, *q, *r, *r_exp;\n\n\textent_hooks_prep();\n\ttry_dalloc = false;\n\ttry_destroy = true;\n\ttry_decommit = false;\n\ttry_purge_lazy = false;\n\ttry_purge_forced = false;\n\tmemcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));\n\tmemcpy(&hooks, &hooks_not_null, sizeof(extent_hooks_t));\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd_fetch());\n\tdid_alloc = false;\n\tbase = base_new(tsdn, 0, &hooks);\n\tassert_ptr_not_null(base, \"Unexpected base_new() failure\");\n\tassert_true(did_alloc, \"Expected alloc\");\n\n\t/*\n\t * Check for tight packing at specified alignment under simple\n\t * conditions.\n\t */\n\t{\n\t\tconst size_t alignments[] = {\n\t\t\t1,\n\t\t\tQUANTUM,\n\t\t\tQUANTUM << 1,\n\t\t\tCACHELINE,\n\t\t\tCACHELINE << 1,\n\t\t};\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < sizeof(alignments) / sizeof(size_t); i++) {\n\t\t\tsize_t alignment = alignments[i];\n\t\t\tsize_t align_ceil = ALIGNMENT_CEILING(alignment,\n\t\t\t    QUANTUM);\n\t\t\tp = base_alloc(tsdn, base, 1, alignment);\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq(p,\n\t\t\t    (void *)(ALIGNMENT_CEILING((uintptr_t)p,\n\t\t\t    alignment)), \"Expected quantum alignment\");\n\t\t\tq = base_alloc(tsdn, base, alignment, alignment);\n\t\t\tassert_ptr_not_null(q,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq((void *)((uintptr_t)p + align_ceil), q,\n\t\t\t    \"Minimal allocation should take up %zu bytes\",\n\t\t\t    align_ceil);\n\t\t\tr = base_alloc(tsdn, base, 1, alignment);\n\t\t\tassert_ptr_not_null(r,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq((void *)((uintptr_t)q + align_ceil), r,\n\t\t\t    \"Minimal allocation should take up %zu bytes\",\n\t\t\t    align_ceil);\n\t\t}\n\t}\n\n\t/*\n\t * Allocate an object that cannot fit in the first block, then verify\n\t * that the first block's remaining space is considered for subsequent\n\t * allocation.\n\t */\n\tassert_zu_ge(extent_bsize_get(&base->blocks->extent), QUANTUM,\n\t    \"Remainder insufficient for test\");\n\t/* Use up all but one quantum of block. */\n\twhile (extent_bsize_get(&base->blocks->extent) > QUANTUM) {\n\t\tp = base_alloc(tsdn, base, QUANTUM, QUANTUM);\n\t\tassert_ptr_not_null(p, \"Unexpected base_alloc() failure\");\n\t}\n\tr_exp = extent_addr_get(&base->blocks->extent);\n\tassert_zu_eq(base->extent_sn_next, 1, \"One extant block expected\");\n\tq = base_alloc(tsdn, base, QUANTUM + 1, QUANTUM);\n\tassert_ptr_not_null(q, \"Unexpected base_alloc() failure\");\n\tassert_ptr_ne(q, r_exp, \"Expected allocation from new block\");\n\tassert_zu_eq(base->extent_sn_next, 2, \"Two extant blocks expected\");\n\tr = base_alloc(tsdn, base, QUANTUM, QUANTUM);\n\tassert_ptr_not_null(r, \"Unexpected base_alloc() failure\");\n\tassert_ptr_eq(r, r_exp, \"Expected allocation from first block\");\n\tassert_zu_eq(base->extent_sn_next, 2, \"Two extant blocks expected\");\n\n\t/*\n\t * Check for proper alignment support when normal blocks are too small.\n\t */\n\t{\n\t\tconst size_t alignments[] = {\n\t\t\tHUGEPAGE,\n\t\t\tHUGEPAGE << 1\n\t\t};\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < sizeof(alignments) / sizeof(size_t); i++) {\n\t\t\tsize_t alignment = alignments[i];\n\t\t\tp = base_alloc(tsdn, base, QUANTUM, alignment);\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq(p,\n\t\t\t    (void *)(ALIGNMENT_CEILING((uintptr_t)p,\n\t\t\t    alignment)), \"Expected %zu-byte alignment\",\n\t\t\t    alignment);\n\t\t}\n\t}\n\n\tcalled_dalloc = called_destroy = called_decommit = called_purge_lazy =\n\t    called_purge_forced = false;\n\tbase_delete(tsdn, base);\n\tassert_true(called_dalloc, \"Expected dalloc call\");\n\tassert_true(!called_destroy, \"Unexpected destroy call\");\n\tassert_true(called_decommit, \"Expected decommit call\");\n\tassert_true(called_purge_lazy, \"Expected purge_lazy call\");\n\tassert_true(called_purge_forced, \"Expected purge_forced call\");\n\n\ttry_dalloc = true;\n\ttry_destroy = true;\n\ttry_decommit = true;\n\ttry_purge_lazy = true;\n\ttry_purge_forced = true;\n\tmemcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_base_hooks_default,\n\t    test_base_hooks_null,\n\t    test_base_hooks_not_null);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/bit_util.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/bit_util.h\"\n\n#define TEST_POW2_CEIL(t, suf, pri) do {\t\t\t\t\\\n\tunsigned i, pow2;\t\t\t\t\t\t\\\n\tt x;\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert_##suf##_eq(pow2_ceil_##suf(0), 0, \"Unexpected result\");\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tfor (i = 0; i < sizeof(t) * 8; i++) {\t\t\t\t\\\n\t\tassert_##suf##_eq(pow2_ceil_##suf(((t)1) << i), ((t)1)\t\\\n\t\t    << i, \"Unexpected result\");\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\tfor (i = 2; i < sizeof(t) * 8; i++) {\t\t\t\t\\\n\t\tassert_##suf##_eq(pow2_ceil_##suf((((t)1) << i) - 1),\t\\\n\t\t    ((t)1) << i, \"Unexpected result\");\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tfor (i = 0; i < sizeof(t) * 8 - 1; i++) {\t\t\t\\\n\t\tassert_##suf##_eq(pow2_ceil_##suf((((t)1) << i) + 1),\t\\\n\t\t    ((t)1) << (i+1), \"Unexpected result\");\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tfor (pow2 = 1; pow2 < 25; pow2++) {\t\t\t\t\\\n\t\tfor (x = (((t)1) << (pow2-1)) + 1; x <= ((t)1) << pow2;\t\\\n\t\t    x++) {\t\t\t\t\t\t\\\n\t\t\tassert_##suf##_eq(pow2_ceil_##suf(x),\t\t\\\n\t\t\t    ((t)1) << pow2,\t\t\t\t\\\n\t\t\t    \"Unexpected result, x=%\"pri, x);\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\nTEST_BEGIN(test_pow2_ceil_u64) {\n\tTEST_POW2_CEIL(uint64_t, u64, FMTu64);\n}\nTEST_END\n\nTEST_BEGIN(test_pow2_ceil_u32) {\n\tTEST_POW2_CEIL(uint32_t, u32, FMTu32);\n}\nTEST_END\n\nTEST_BEGIN(test_pow2_ceil_zu) {\n\tTEST_POW2_CEIL(size_t, zu, \"zu\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_pow2_ceil_u64,\n\t    test_pow2_ceil_u32,\n\t    test_pow2_ceil_zu);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/bitmap.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NBITS_TAB \\\n    NB( 1) \\\n    NB( 2) \\\n    NB( 3) \\\n    NB( 4) \\\n    NB( 5) \\\n    NB( 6) \\\n    NB( 7) \\\n    NB( 8) \\\n    NB( 9) \\\n    NB(10) \\\n    NB(11) \\\n    NB(12) \\\n    NB(13) \\\n    NB(14) \\\n    NB(15) \\\n    NB(16) \\\n    NB(17) \\\n    NB(18) \\\n    NB(19) \\\n    NB(20) \\\n    NB(21) \\\n    NB(22) \\\n    NB(23) \\\n    NB(24) \\\n    NB(25) \\\n    NB(26) \\\n    NB(27) \\\n    NB(28) \\\n    NB(29) \\\n    NB(30) \\\n    NB(31) \\\n    NB(32) \\\n    \\\n    NB(33) \\\n    NB(34) \\\n    NB(35) \\\n    NB(36) \\\n    NB(37) \\\n    NB(38) \\\n    NB(39) \\\n    NB(40) \\\n    NB(41) \\\n    NB(42) \\\n    NB(43) \\\n    NB(44) \\\n    NB(45) \\\n    NB(46) \\\n    NB(47) \\\n    NB(48) \\\n    NB(49) \\\n    NB(50) \\\n    NB(51) \\\n    NB(52) \\\n    NB(53) \\\n    NB(54) \\\n    NB(55) \\\n    NB(56) \\\n    NB(57) \\\n    NB(58) \\\n    NB(59) \\\n    NB(60) \\\n    NB(61) \\\n    NB(62) \\\n    NB(63) \\\n    NB(64) \\\n    NB(65) \\\n    \\\n    NB(126) \\\n    NB(127) \\\n    NB(128) \\\n    NB(129) \\\n    NB(130) \\\n    \\\n    NB(254) \\\n    NB(255) \\\n    NB(256) \\\n    NB(257) \\\n    NB(258) \\\n    \\\n    NB(510) \\\n    NB(511) \\\n    NB(512) \\\n    NB(513) \\\n    NB(514) \\\n    \\\n    NB(1024) \\\n    NB(2048) \\\n    NB(4096) \\\n    NB(8192) \\\n    NB(16384) \\\n\nstatic void\ntest_bitmap_initializer_body(const bitmap_info_t *binfo, size_t nbits) {\n\tbitmap_info_t binfo_dyn;\n\tbitmap_info_init(&binfo_dyn, nbits);\n\n\tassert_zu_eq(bitmap_size(binfo), bitmap_size(&binfo_dyn),\n\t    \"Unexpected difference between static and dynamic initialization, \"\n\t    \"nbits=%zu\", nbits);\n\tassert_zu_eq(binfo->nbits, binfo_dyn.nbits,\n\t    \"Unexpected difference between static and dynamic initialization, \"\n\t    \"nbits=%zu\", nbits);\n#ifdef BITMAP_USE_TREE\n\tassert_u_eq(binfo->nlevels, binfo_dyn.nlevels,\n\t    \"Unexpected difference between static and dynamic initialization, \"\n\t    \"nbits=%zu\", nbits);\n\t{\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < binfo->nlevels; i++) {\n\t\t\tassert_zu_eq(binfo->levels[i].group_offset,\n\t\t\t    binfo_dyn.levels[i].group_offset,\n\t\t\t    \"Unexpected difference between static and dynamic \"\n\t\t\t    \"initialization, nbits=%zu, level=%u\", nbits, i);\n\t\t}\n\t}\n#else\n\tassert_zu_eq(binfo->ngroups, binfo_dyn.ngroups,\n\t    \"Unexpected difference between static and dynamic initialization\");\n#endif\n}\n\nTEST_BEGIN(test_bitmap_initializer) {\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tif (nbits <= BITMAP_MAXBITS) {\t\t\t\t\\\n\t\t\tbitmap_info_t binfo =\t\t\t\t\\\n\t\t\t    BITMAP_INFO_INITIALIZER(nbits);\t\t\\\n\t\t\ttest_bitmap_initializer_body(&binfo, nbits);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic size_t\ntest_bitmap_size_body(const bitmap_info_t *binfo, size_t nbits,\n    size_t prev_size) {\n\tsize_t size = bitmap_size(binfo);\n\tassert_zu_ge(size, (nbits >> 3),\n\t    \"Bitmap size is smaller than expected\");\n\tassert_zu_ge(size, prev_size, \"Bitmap size is smaller than expected\");\n\treturn size;\n}\n\nTEST_BEGIN(test_bitmap_size) {\n\tsize_t nbits, prev_size;\n\n\tprev_size = 0;\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\tprev_size = test_bitmap_size_body(&binfo, nbits, prev_size);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\tprev_size = test_bitmap_size_body(&binfo, nbits,\t\\\n\t\t    prev_size);\t\t\t\t\t\t\\\n\t}\n\tprev_size = 0;\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_init_body(const bitmap_info_t *binfo, size_t nbits) {\n\tsize_t i;\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\n\tbitmap_init(bitmap, binfo, false);\n\tfor (i = 0; i < nbits; i++) {\n\t\tassert_false(bitmap_get(bitmap, binfo, i),\n\t\t    \"Bit should be unset\");\n\t}\n\n\tbitmap_init(bitmap, binfo, true);\n\tfor (i = 0; i < nbits; i++) {\n\t\tassert_true(bitmap_get(bitmap, binfo, i), \"Bit should be set\");\n\t}\n\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_init) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_init_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_init_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_set_body(const bitmap_info_t *binfo, size_t nbits) {\n\tsize_t i;\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\tbitmap_init(bitmap, binfo, false);\n\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i);\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_set) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_set_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_set_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_unset_body(const bitmap_info_t *binfo, size_t nbits) {\n\tsize_t i;\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\tbitmap_init(bitmap, binfo, false);\n\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i);\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_unset(bitmap, binfo, i);\n\t}\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i);\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_unset) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_unset_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_unset_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\tbitmap_init(bitmap, binfo, false);\n\n\t/* Iteratively set bits starting at the beginning. */\n\tfor (size_t i = 0; i < nbits; i++) {\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\n\t/*\n\t * Iteratively unset bits starting at the end, and verify that\n\t * bitmap_sfu() reaches the unset bits.\n\t */\n\tfor (size_t i = nbits - 1; i < nbits; i--) { /* (nbits..0] */\n\t\tbitmap_unset(bitmap, binfo, i);\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tbitmap_unset(bitmap, binfo, i);\n\t}\n\tassert_false(bitmap_get(bitmap, binfo, 0), \"Bit should be unset\");\n\n\t/*\n\t * Iteratively set bits starting at the beginning, and verify that\n\t * bitmap_sfu() looks past them.\n\t */\n\tfor (size_t i = 1; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i - 1);\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tbitmap_unset(bitmap, binfo, i);\n\t}\n\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), nbits - 1,\n\t    \"First unset bit should be the last bit\");\n\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (nbits > 1) ? nbits-2 : nbits-1),\n\t    nbits - 1, \"First unset bit should be the last bit\");\n\tassert_zu_eq(bitmap_ffu(bitmap, binfo, nbits - 1), nbits - 1,\n\t    \"First unset bit should be the last bit\");\n\tassert_zu_eq(bitmap_sfu(bitmap, binfo), nbits - 1,\n\t    \"First unset bit should be the last bit\");\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\n\t/*\n\t * Bubble a \"usu\" pattern through the bitmap and verify that\n\t * bitmap_ffu() finds the correct bit for all five min_bit cases.\n\t */\n\tif (nbits >= 3) {\n\t\tfor (size_t i = 0; i < nbits-2; i++) {\n\t\t\tbitmap_unset(bitmap, binfo, i);\n\t\t\tbitmap_unset(bitmap, binfo, i+2);\n\t\t\tif (i > 0) {\n\t\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,\n\t\t\t\t    \"Unexpected first unset bit\");\n\t\t\t}\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+1), i+2,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+2), i+2,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tif (i + 3 < nbits) {\n\t\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+3),\n\t\t\t\t    nbits, \"Unexpected first unset bit\");\n\t\t\t}\n\t\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i+2,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t}\n\t}\n\n\t/*\n\t * Unset the last bit, bubble another unset bit through the bitmap, and\n\t * verify that bitmap_ffu() finds the correct bit for all four min_bit\n\t * cases.\n\t */\n\tif (nbits >= 3) {\n\t\tbitmap_unset(bitmap, binfo, nbits-1);\n\t\tfor (size_t i = 0; i < nbits-1; i++) {\n\t\t\tbitmap_unset(bitmap, binfo, i);\n\t\t\tif (i > 0) {\n\t\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,\n\t\t\t\t    \"Unexpected first unset bit\");\n\t\t\t}\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+1), nbits-1,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, nbits-1),\n\t\t\t    nbits-1, \"Unexpected first unset bit\");\n\n\t\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t}\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), nbits-1,\n\t\t    \"Unexpected first unset bit\");\n\t}\n\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_xfu) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_xfu_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_xfu_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_bitmap_initializer,\n\t    test_bitmap_size,\n\t    test_bitmap_init,\n\t    test_bitmap_set,\n\t    test_bitmap_unset,\n\t    test_bitmap_xfu);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/ckh.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_new_delete) {\n\ttsd_t *tsd;\n\tckh_t ckh;\n\n\ttsd = tsd_fetch();\n\n\tassert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,\n\t    ckh_string_keycomp), \"Unexpected ckh_new() error\");\n\tckh_delete(tsd, &ckh);\n\n\tassert_false(ckh_new(tsd, &ckh, 3, ckh_pointer_hash,\n\t    ckh_pointer_keycomp), \"Unexpected ckh_new() error\");\n\tckh_delete(tsd, &ckh);\n}\nTEST_END\n\nTEST_BEGIN(test_count_insert_search_remove) {\n\ttsd_t *tsd;\n\tckh_t ckh;\n\tconst char *strs[] = {\n\t    \"a string\",\n\t    \"A string\",\n\t    \"a string.\",\n\t    \"A string.\"\n\t};\n\tconst char *missing = \"A string not in the hash table.\";\n\tsize_t i;\n\n\ttsd = tsd_fetch();\n\n\tassert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,\n\t    ckh_string_keycomp), \"Unexpected ckh_new() error\");\n\tassert_zu_eq(ckh_count(&ckh), 0,\n\t    \"ckh_count() should return %zu, but it returned %zu\", ZU(0),\n\t    ckh_count(&ckh));\n\n\t/* Insert. */\n\tfor (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {\n\t\tckh_insert(tsd, &ckh, strs[i], strs[i]);\n\t\tassert_zu_eq(ckh_count(&ckh), i+1,\n\t\t    \"ckh_count() should return %zu, but it returned %zu\", i+1,\n\t\t    ckh_count(&ckh));\n\t}\n\n\t/* Search. */\n\tfor (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {\n\t\tunion {\n\t\t\tvoid *p;\n\t\t\tconst char *s;\n\t\t} k, v;\n\t\tvoid **kp, **vp;\n\t\tconst char *ks, *vs;\n\n\t\tkp = (i & 1) ? &k.p : NULL;\n\t\tvp = (i & 2) ? &v.p : NULL;\n\t\tk.p = NULL;\n\t\tv.p = NULL;\n\t\tassert_false(ckh_search(&ckh, strs[i], kp, vp),\n\t\t    \"Unexpected ckh_search() error\");\n\n\t\tks = (i & 1) ? strs[i] : (const char *)NULL;\n\t\tvs = (i & 2) ? strs[i] : (const char *)NULL;\n\t\tassert_ptr_eq((void *)ks, (void *)k.s, \"Key mismatch, i=%zu\",\n\t\t    i);\n\t\tassert_ptr_eq((void *)vs, (void *)v.s, \"Value mismatch, i=%zu\",\n\t\t    i);\n\t}\n\tassert_true(ckh_search(&ckh, missing, NULL, NULL),\n\t    \"Unexpected ckh_search() success\");\n\n\t/* Remove. */\n\tfor (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {\n\t\tunion {\n\t\t\tvoid *p;\n\t\t\tconst char *s;\n\t\t} k, v;\n\t\tvoid **kp, **vp;\n\t\tconst char *ks, *vs;\n\n\t\tkp = (i & 1) ? &k.p : NULL;\n\t\tvp = (i & 2) ? &v.p : NULL;\n\t\tk.p = NULL;\n\t\tv.p = NULL;\n\t\tassert_false(ckh_remove(tsd, &ckh, strs[i], kp, vp),\n\t\t    \"Unexpected ckh_remove() error\");\n\n\t\tks = (i & 1) ? strs[i] : (const char *)NULL;\n\t\tvs = (i & 2) ? strs[i] : (const char *)NULL;\n\t\tassert_ptr_eq((void *)ks, (void *)k.s, \"Key mismatch, i=%zu\",\n\t\t    i);\n\t\tassert_ptr_eq((void *)vs, (void *)v.s, \"Value mismatch, i=%zu\",\n\t\t    i);\n\t\tassert_zu_eq(ckh_count(&ckh),\n\t\t    sizeof(strs)/sizeof(const char *) - i - 1,\n\t\t    \"ckh_count() should return %zu, but it returned %zu\",\n\t\t        sizeof(strs)/sizeof(const char *) - i - 1,\n\t\t    ckh_count(&ckh));\n\t}\n\n\tckh_delete(tsd, &ckh);\n}\nTEST_END\n\nTEST_BEGIN(test_insert_iter_remove) {\n#define NITEMS ZU(1000)\n\ttsd_t *tsd;\n\tckh_t ckh;\n\tvoid **p[NITEMS];\n\tvoid *q, *r;\n\tsize_t i;\n\n\ttsd = tsd_fetch();\n\n\tassert_false(ckh_new(tsd, &ckh, 2, ckh_pointer_hash,\n\t    ckh_pointer_keycomp), \"Unexpected ckh_new() error\");\n\n\tfor (i = 0; i < NITEMS; i++) {\n\t\tp[i] = mallocx(i+1, 0);\n\t\tassert_ptr_not_null(p[i], \"Unexpected mallocx() failure\");\n\t}\n\n\tfor (i = 0; i < NITEMS; i++) {\n\t\tsize_t j;\n\n\t\tfor (j = i; j < NITEMS; j++) {\n\t\t\tassert_false(ckh_insert(tsd, &ckh, p[j], p[j]),\n\t\t\t    \"Unexpected ckh_insert() failure\");\n\t\t\tassert_false(ckh_search(&ckh, p[j], &q, &r),\n\t\t\t    \"Unexpected ckh_search() failure\");\n\t\t\tassert_ptr_eq(p[j], q, \"Key pointer mismatch\");\n\t\t\tassert_ptr_eq(p[j], r, \"Value pointer mismatch\");\n\t\t}\n\n\t\tassert_zu_eq(ckh_count(&ckh), NITEMS,\n\t\t    \"ckh_count() should return %zu, but it returned %zu\",\n\t\t    NITEMS, ckh_count(&ckh));\n\n\t\tfor (j = i + 1; j < NITEMS; j++) {\n\t\t\tassert_false(ckh_search(&ckh, p[j], NULL, NULL),\n\t\t\t    \"Unexpected ckh_search() failure\");\n\t\t\tassert_false(ckh_remove(tsd, &ckh, p[j], &q, &r),\n\t\t\t    \"Unexpected ckh_remove() failure\");\n\t\t\tassert_ptr_eq(p[j], q, \"Key pointer mismatch\");\n\t\t\tassert_ptr_eq(p[j], r, \"Value pointer mismatch\");\n\t\t\tassert_true(ckh_search(&ckh, p[j], NULL, NULL),\n\t\t\t    \"Unexpected ckh_search() success\");\n\t\t\tassert_true(ckh_remove(tsd, &ckh, p[j], &q, &r),\n\t\t\t    \"Unexpected ckh_remove() success\");\n\t\t}\n\n\t\t{\n\t\t\tbool seen[NITEMS];\n\t\t\tsize_t tabind;\n\n\t\t\tmemset(seen, 0, sizeof(seen));\n\n\t\t\tfor (tabind = 0; !ckh_iter(&ckh, &tabind, &q, &r);) {\n\t\t\t\tsize_t k;\n\n\t\t\t\tassert_ptr_eq(q, r, \"Key and val not equal\");\n\n\t\t\t\tfor (k = 0; k < NITEMS; k++) {\n\t\t\t\t\tif (p[k] == q) {\n\t\t\t\t\t\tassert_false(seen[k],\n\t\t\t\t\t\t    \"Item %zu already seen\", k);\n\t\t\t\t\t\tseen[k] = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (j = 0; j < i + 1; j++) {\n\t\t\t\tassert_true(seen[j], \"Item %zu not seen\", j);\n\t\t\t}\n\t\t\tfor (; j < NITEMS; j++) {\n\t\t\t\tassert_false(seen[j], \"Item %zu seen\", j);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0; i < NITEMS; i++) {\n\t\tassert_false(ckh_search(&ckh, p[i], NULL, NULL),\n\t\t    \"Unexpected ckh_search() failure\");\n\t\tassert_false(ckh_remove(tsd, &ckh, p[i], &q, &r),\n\t\t    \"Unexpected ckh_remove() failure\");\n\t\tassert_ptr_eq(p[i], q, \"Key pointer mismatch\");\n\t\tassert_ptr_eq(p[i], r, \"Value pointer mismatch\");\n\t\tassert_true(ckh_search(&ckh, p[i], NULL, NULL),\n\t\t    \"Unexpected ckh_search() success\");\n\t\tassert_true(ckh_remove(tsd, &ckh, p[i], &q, &r),\n\t\t    \"Unexpected ckh_remove() success\");\n\t\tdallocx(p[i], 0);\n\t}\n\n\tassert_zu_eq(ckh_count(&ckh), 0,\n\t    \"ckh_count() should return %zu, but it returned %zu\",\n\t    ZU(0), ckh_count(&ckh));\n\tckh_delete(tsd, &ckh);\n#undef NITEMS\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_new_delete,\n\t    test_count_insert_search_remove,\n\t    test_insert_iter_remove);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/decay.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ticker.h\"\n\nstatic nstime_monotonic_t *nstime_monotonic_orig;\nstatic nstime_update_t *nstime_update_orig;\n\nstatic unsigned nupdates_mock;\nstatic nstime_t time_mock;\nstatic bool monotonic_mock;\n\nstatic bool\ncheck_background_thread_enabled(void) {\n\tbool enabled;\n\tsize_t sz = sizeof(bool);\n\tint ret = mallctl(\"background_thread\", (void *)&enabled, &sz, NULL,0);\n\tif (ret == ENOENT) {\n\t\treturn false;\n\t}\n\tassert_d_eq(ret, 0, \"Unexpected mallctl error\");\n\treturn enabled;\n}\n\nstatic bool\nnstime_monotonic_mock(void) {\n\treturn monotonic_mock;\n}\n\nstatic bool\nnstime_update_mock(nstime_t *time) {\n\tnupdates_mock++;\n\tif (monotonic_mock) {\n\t\tnstime_copy(time, &time_mock);\n\t}\n\treturn !monotonic_mock;\n}\n\nstatic unsigned\ndo_arena_create(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.dirty_decay_ms\", mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(dirty_decay_ms)), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.muzzy_decay_ms\", mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(muzzy_decay_ms)), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\treturn arena_ind;\n}\n\nstatic void\ndo_arena_destroy(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.destroy\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nvoid\ndo_epoch(void) {\n\tuint64_t epoch = 1;\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n}\n\nvoid\ndo_purge(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.purge\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nvoid\ndo_decay(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.decay\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nstatic uint64_t\nget_arena_npurge_impl(const char *mibname, unsigned arena_ind) {\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(mibname, mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[2] = (size_t)arena_ind;\n\tuint64_t npurge = 0;\n\tsize_t sz = sizeof(npurge);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&npurge, &sz, NULL, 0),\n\t    config_stats ? 0 : ENOENT, \"Unexpected mallctlbymib() failure\");\n\treturn npurge;\n}\n\nstatic uint64_t\nget_arena_dirty_npurge(unsigned arena_ind) {\n\tdo_epoch();\n\treturn get_arena_npurge_impl(\"stats.arenas.0.dirty_npurge\", arena_ind);\n}\n\nstatic uint64_t\nget_arena_muzzy_npurge(unsigned arena_ind) {\n\tdo_epoch();\n\treturn get_arena_npurge_impl(\"stats.arenas.0.muzzy_npurge\", arena_ind);\n}\n\nstatic uint64_t\nget_arena_npurge(unsigned arena_ind) {\n\tdo_epoch();\n\treturn get_arena_npurge_impl(\"stats.arenas.0.dirty_npurge\", arena_ind) +\n\t    get_arena_npurge_impl(\"stats.arenas.0.muzzy_npurge\", arena_ind);\n}\n\nstatic size_t\nget_arena_pdirty(unsigned arena_ind) {\n\tdo_epoch();\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"stats.arenas.0.pdirty\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[2] = (size_t)arena_ind;\n\tsize_t pdirty;\n\tsize_t sz = sizeof(pdirty);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&pdirty, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\treturn pdirty;\n}\n\nstatic size_t\nget_arena_pmuzzy(unsigned arena_ind) {\n\tdo_epoch();\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"stats.arenas.0.pmuzzy\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[2] = (size_t)arena_ind;\n\tsize_t pmuzzy;\n\tsize_t sz = sizeof(pmuzzy);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&pmuzzy, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\treturn pmuzzy;\n}\n\nstatic void *\ndo_mallocx(size_t size, int flags) {\n\tvoid *p = mallocx(size, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\treturn p;\n}\n\nstatic void\ngenerate_dirty(unsigned arena_ind, size_t size) {\n\tint flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\tvoid *p = do_mallocx(size, flags);\n\tdallocx(p, flags);\n}\n\nTEST_BEGIN(test_decay_ticks) {\n\ttest_skip_if(check_background_thread_enabled());\n\n\tticker_t *decay_ticker;\n\tunsigned tick0, tick1, arena_ind;\n\tsize_t sz, large0;\n\tvoid *p;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&large0, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\t/* Set up a manually managed arena for test. */\n\tarena_ind = do_arena_create(0, 0);\n\n\t/* Migrate to the new arena, and get the ticker. */\n\tunsigned old_arena_ind;\n\tsize_t sz_arena_ind = sizeof(old_arena_ind);\n\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind,\n\t    &sz_arena_ind, (void *)&arena_ind, sizeof(arena_ind)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tdecay_ticker = decay_ticker_get(tsd_fetch(), arena_ind);\n\tassert_ptr_not_null(decay_ticker,\n\t    \"Unexpected failure getting decay ticker\");\n\n\t/*\n\t * Test the standard APIs using a large size class, since we can't\n\t * control tcache interactions for small size classes (except by\n\t * completely disabling tcache for the entire test program).\n\t */\n\n\t/* malloc(). */\n\ttick0 = ticker_read(decay_ticker);\n\tp = malloc(large0);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during malloc()\");\n\t/* free(). */\n\ttick0 = ticker_read(decay_ticker);\n\tfree(p);\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during free()\");\n\n\t/* calloc(). */\n\ttick0 = ticker_read(decay_ticker);\n\tp = calloc(1, large0);\n\tassert_ptr_not_null(p, \"Unexpected calloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during calloc()\");\n\tfree(p);\n\n\t/* posix_memalign(). */\n\ttick0 = ticker_read(decay_ticker);\n\tassert_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0,\n\t    \"Unexpected posix_memalign() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0,\n\t    \"Expected ticker to tick during posix_memalign()\");\n\tfree(p);\n\n\t/* aligned_alloc(). */\n\ttick0 = ticker_read(decay_ticker);\n\tp = aligned_alloc(sizeof(size_t), large0);\n\tassert_ptr_not_null(p, \"Unexpected aligned_alloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0,\n\t    \"Expected ticker to tick during aligned_alloc()\");\n\tfree(p);\n\n\t/* realloc(). */\n\t/* Allocate. */\n\ttick0 = ticker_read(decay_ticker);\n\tp = realloc(NULL, large0);\n\tassert_ptr_not_null(p, \"Unexpected realloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during realloc()\");\n\t/* Reallocate. */\n\ttick0 = ticker_read(decay_ticker);\n\tp = realloc(p, large0);\n\tassert_ptr_not_null(p, \"Unexpected realloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during realloc()\");\n\t/* Deallocate. */\n\ttick0 = ticker_read(decay_ticker);\n\trealloc(p, 0);\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during realloc()\");\n\n\t/*\n\t * Test the *allocx() APIs using large and small size classes, with\n\t * tcache explicitly disabled.\n\t */\n\t{\n\t\tunsigned i;\n\t\tsize_t allocx_sizes[2];\n\t\tallocx_sizes[0] = large0;\n\t\tallocx_sizes[1] = 1;\n\n\t\tfor (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {\n\t\t\tsz = allocx_sizes[i];\n\n\t\t\t/* mallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tp = mallocx(sz, MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during mallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* rallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tp = rallocx(p, sz, MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during rallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* xallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\txallocx(p, sz, 0, MALLOCX_TCACHE_NONE);\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during xallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* dallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tdallocx(p, MALLOCX_TCACHE_NONE);\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during dallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* sdallocx(). */\n\t\t\tp = mallocx(sz, MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tsdallocx(p, sz, MALLOCX_TCACHE_NONE);\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during sdallocx() \"\n\t\t\t    \"(sz=%zu)\", sz);\n\t\t}\n\t}\n\n\t/*\n\t * Test tcache fill/flush interactions for large and small size classes,\n\t * using an explicit tcache.\n\t */\n\tunsigned tcache_ind, i;\n\tsize_t tcache_sizes[2];\n\ttcache_sizes[0] = large0;\n\ttcache_sizes[1] = 1;\n\n\tsize_t tcache_max, sz_tcache_max;\n\tsz_tcache_max = sizeof(tcache_max);\n\tassert_d_eq(mallctl(\"arenas.tcache_max\", (void *)&tcache_max,\n\t    &sz_tcache_max, NULL, 0), 0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"tcache.create\", (void *)&tcache_ind, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl failure\");\n\n\tfor (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {\n\t\tsz = tcache_sizes[i];\n\n\t\t/* tcache fill. */\n\t\ttick0 = ticker_read(decay_ticker);\n\t\tp = mallocx(sz, MALLOCX_TCACHE(tcache_ind));\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\ttick1 = ticker_read(decay_ticker);\n\t\tassert_u32_ne(tick1, tick0,\n\t\t    \"Expected ticker to tick during tcache fill \"\n\t\t    \"(sz=%zu)\", sz);\n\t\t/* tcache flush. */\n\t\tdallocx(p, MALLOCX_TCACHE(tcache_ind));\n\t\ttick0 = ticker_read(decay_ticker);\n\t\tassert_d_eq(mallctl(\"tcache.flush\", NULL, NULL,\n\t\t    (void *)&tcache_ind, sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl failure\");\n\t\ttick1 = ticker_read(decay_ticker);\n\n\t\t/* Will only tick if it's in tcache. */\n\t\tif (sz <= tcache_max) {\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during tcache \"\n\t\t\t    \"flush (sz=%zu)\", sz);\n\t\t} else {\n\t\t\tassert_u32_eq(tick1, tick0,\n\t\t\t    \"Unexpected ticker tick during tcache \"\n\t\t\t    \"flush (sz=%zu)\", sz);\n\t\t}\n\t}\n}\nTEST_END\n\nstatic void\ndecay_ticker_helper(unsigned arena_ind, int flags, bool dirty, ssize_t dt,\n    uint64_t dirty_npurge0, uint64_t muzzy_npurge0, bool terminate_asap) {\n#define NINTERVALS 101\n\tnstime_t time, update_interval, decay_ms, deadline;\n\n\tnstime_init(&time, 0);\n\tnstime_update(&time);\n\n\tnstime_init2(&decay_ms, dt, 0);\n\tnstime_copy(&deadline, &time);\n\tnstime_add(&deadline, &decay_ms);\n\n\tnstime_init2(&update_interval, dt, 0);\n\tnstime_idivide(&update_interval, NINTERVALS);\n\n\t/*\n\t * Keep q's slab from being deallocated during the looping below.  If a\n\t * cached slab were to repeatedly come and go during looping, it could\n\t * prevent the decay backlog ever becoming empty.\n\t */\n\tvoid *p = do_mallocx(1, flags);\n\tuint64_t dirty_npurge1, muzzy_npurge1;\n\tdo {\n\t\tfor (unsigned i = 0; i < DECAY_NTICKS_PER_UPDATE / 2;\n\t\t    i++) {\n\t\t\tvoid *q = do_mallocx(1, flags);\n\t\t\tdallocx(q, flags);\n\t\t}\n\t\tdirty_npurge1 = get_arena_dirty_npurge(arena_ind);\n\t\tmuzzy_npurge1 = get_arena_muzzy_npurge(arena_ind);\n\n\t\tnstime_add(&time_mock, &update_interval);\n\t\tnstime_update(&time);\n\t} while (nstime_compare(&time, &deadline) <= 0 && ((dirty_npurge1 ==\n\t    dirty_npurge0 && muzzy_npurge1 == muzzy_npurge0) ||\n\t    !terminate_asap));\n\tdallocx(p, flags);\n\n\tif (config_stats) {\n\t\tassert_u64_gt(dirty_npurge1 + muzzy_npurge1, dirty_npurge0 +\n\t\t    muzzy_npurge0, \"Expected purging to occur\");\n\t}\n#undef NINTERVALS\n}\n\nTEST_BEGIN(test_decay_ticker) {\n\ttest_skip_if(check_background_thread_enabled());\n#define NPS 2048\n\tssize_t ddt = opt_dirty_decay_ms;\n\tssize_t mdt = opt_muzzy_decay_ms;\n\tunsigned arena_ind = do_arena_create(ddt, mdt);\n\tint flags = (MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE);\n\tvoid *ps[NPS];\n\tsize_t large;\n\n\t/*\n\t * Allocate a bunch of large objects, pause the clock, deallocate every\n\t * other object (to fragment virtual memory), restore the clock, then\n\t * [md]allocx() in a tight loop while advancing time rapidly to verify\n\t * the ticker triggers purging.\n\t */\n\n\tsize_t tcache_max;\n\tsize_t sz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.tcache_max\", (void *)&tcache_max, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\tlarge = nallocx(tcache_max + 1, flags);\n\n\tdo_purge(arena_ind);\n\tuint64_t dirty_npurge0 = get_arena_dirty_npurge(arena_ind);\n\tuint64_t muzzy_npurge0 = get_arena_muzzy_npurge(arena_ind);\n\n\tfor (unsigned i = 0; i < NPS; i++) {\n\t\tps[i] = do_mallocx(large, flags);\n\t}\n\n\tnupdates_mock = 0;\n\tnstime_init(&time_mock, 0);\n\tnstime_update(&time_mock);\n\tmonotonic_mock = true;\n\n\tnstime_monotonic_orig = nstime_monotonic;\n\tnstime_update_orig = nstime_update;\n\tnstime_monotonic = nstime_monotonic_mock;\n\tnstime_update = nstime_update_mock;\n\n\tfor (unsigned i = 0; i < NPS; i += 2) {\n\t\tdallocx(ps[i], flags);\n\t\tunsigned nupdates0 = nupdates_mock;\n\t\tdo_decay(arena_ind);\n\t\tassert_u_gt(nupdates_mock, nupdates0,\n\t\t    \"Expected nstime_update() to be called\");\n\t}\n\n\tdecay_ticker_helper(arena_ind, flags, true, ddt, dirty_npurge0,\n\t    muzzy_npurge0, true);\n\tdecay_ticker_helper(arena_ind, flags, false, ddt+mdt, dirty_npurge0,\n\t    muzzy_npurge0, false);\n\n\tdo_arena_destroy(arena_ind);\n\n\tnstime_monotonic = nstime_monotonic_orig;\n\tnstime_update = nstime_update_orig;\n#undef NPS\n}\nTEST_END\n\nTEST_BEGIN(test_decay_nonmonotonic) {\n\ttest_skip_if(check_background_thread_enabled());\n#define NPS (SMOOTHSTEP_NSTEPS + 1)\n\tint flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);\n\tvoid *ps[NPS];\n\tuint64_t npurge0 = 0;\n\tuint64_t npurge1 = 0;\n\tsize_t sz, large0;\n\tunsigned i, nupdates0;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&large0, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl failure\");\n\tdo_epoch();\n\tsz = sizeof(uint64_t);\n\tnpurge0 = get_arena_npurge(0);\n\n\tnupdates_mock = 0;\n\tnstime_init(&time_mock, 0);\n\tnstime_update(&time_mock);\n\tmonotonic_mock = false;\n\n\tnstime_monotonic_orig = nstime_monotonic;\n\tnstime_update_orig = nstime_update;\n\tnstime_monotonic = nstime_monotonic_mock;\n\tnstime_update = nstime_update_mock;\n\n\tfor (i = 0; i < NPS; i++) {\n\t\tps[i] = mallocx(large0, flags);\n\t\tassert_ptr_not_null(ps[i], \"Unexpected mallocx() failure\");\n\t}\n\n\tfor (i = 0; i < NPS; i++) {\n\t\tdallocx(ps[i], flags);\n\t\tnupdates0 = nupdates_mock;\n\t\tassert_d_eq(mallctl(\"arena.0.decay\", NULL, NULL, NULL, 0), 0,\n\t\t    \"Unexpected arena.0.decay failure\");\n\t\tassert_u_gt(nupdates_mock, nupdates0,\n\t\t    \"Expected nstime_update() to be called\");\n\t}\n\n\tdo_epoch();\n\tsz = sizeof(uint64_t);\n\tnpurge1 = get_arena_npurge(0);\n\n\tif (config_stats) {\n\t\tassert_u64_eq(npurge0, npurge1, \"Unexpected purging occurred\");\n\t}\n\n\tnstime_monotonic = nstime_monotonic_orig;\n\tnstime_update = nstime_update_orig;\n#undef NPS\n}\nTEST_END\n\nTEST_BEGIN(test_decay_now) {\n\ttest_skip_if(check_background_thread_enabled());\n\n\tunsigned arena_ind = do_arena_create(0, 0);\n\tassert_zu_eq(get_arena_pdirty(arena_ind), 0, \"Unexpected dirty pages\");\n\tassert_zu_eq(get_arena_pmuzzy(arena_ind), 0, \"Unexpected muzzy pages\");\n\tsize_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};\n\t/* Verify that dirty/muzzy pages never linger after deallocation. */\n\tfor (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {\n\t\tsize_t size = sizes[i];\n\t\tgenerate_dirty(arena_ind, size);\n\t\tassert_zu_eq(get_arena_pdirty(arena_ind), 0,\n\t\t    \"Unexpected dirty pages\");\n\t\tassert_zu_eq(get_arena_pmuzzy(arena_ind), 0,\n\t\t    \"Unexpected muzzy pages\");\n\t}\n\tdo_arena_destroy(arena_ind);\n}\nTEST_END\n\nTEST_BEGIN(test_decay_never) {\n\ttest_skip_if(check_background_thread_enabled());\n\n\tunsigned arena_ind = do_arena_create(-1, -1);\n\tint flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\tassert_zu_eq(get_arena_pdirty(arena_ind), 0, \"Unexpected dirty pages\");\n\tassert_zu_eq(get_arena_pmuzzy(arena_ind), 0, \"Unexpected muzzy pages\");\n\tsize_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};\n\tvoid *ptrs[sizeof(sizes)/sizeof(size_t)];\n\tfor (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {\n\t\tptrs[i] = do_mallocx(sizes[i], flags);\n\t}\n\t/* Verify that each deallocation generates additional dirty pages. */\n\tsize_t pdirty_prev = get_arena_pdirty(arena_ind);\n\tsize_t pmuzzy_prev = get_arena_pmuzzy(arena_ind);\n\tassert_zu_eq(pdirty_prev, 0, \"Unexpected dirty pages\");\n\tassert_zu_eq(pmuzzy_prev, 0, \"Unexpected muzzy pages\");\n\tfor (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {\n\t\tdallocx(ptrs[i], flags);\n\t\tsize_t pdirty = get_arena_pdirty(arena_ind);\n\t\tsize_t pmuzzy = get_arena_pmuzzy(arena_ind);\n\t\tassert_zu_gt(pdirty, pdirty_prev,\n\t\t    \"Expected dirty pages to increase.\");\n\t\tassert_zu_eq(pmuzzy, 0, \"Unexpected muzzy pages\");\n\t\tpdirty_prev = pdirty;\n\t}\n\tdo_arena_destroy(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_decay_ticks,\n\t    test_decay_ticker,\n\t    test_decay_nonmonotonic,\n\t    test_decay_now,\n\t    test_decay_never);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/decay.sh",
    "content": "#!/bin/sh\n\nexport MALLOC_CONF=\"dirty_decay_ms:1000,muzzy_decay_ms:1000,lg_tcache_max:0\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/div.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/div.h\"\n\nTEST_BEGIN(test_div_exhaustive) {\n\tfor (size_t divisor = 2; divisor < 1000 * 1000; ++divisor) {\n\t\tdiv_info_t div_info;\n\t\tdiv_init(&div_info, divisor);\n\t\tsize_t max = 1000 * divisor;\n\t\tif (max < 1000 * 1000) {\n\t\t\tmax = 1000 * 1000;\n\t\t}\n\t\tfor (size_t dividend = 0; dividend < 1000 * divisor;\n\t\t    dividend += divisor) {\n\t\t\tsize_t quotient = div_compute(\n\t\t\t    &div_info, dividend);\n\t\t\tassert_zu_eq(dividend, quotient * divisor,\n\t\t\t    \"With divisor = %zu, dividend = %zu, \"\n\t\t\t    \"got quotient %zu\", divisor, dividend, quotient);\n\t\t}\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_div_exhaustive);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/emitter.c",
    "content": "#include \"test/jemalloc_test.h\"\n#include \"jemalloc/internal/emitter.h\"\n\n/*\n * This is so useful for debugging and feature work, we'll leave printing\n * functionality committed but disabled by default.\n */\n/* Print the text as it will appear. */\nstatic bool print_raw = false;\n/* Print the text escaped, so it can be copied back into the test case. */\nstatic bool print_escaped = false;\n\ntypedef struct buf_descriptor_s buf_descriptor_t;\nstruct buf_descriptor_s {\n\tchar *buf;\n\tsize_t len;\n\tbool mid_quote;\n};\n\n/*\n * Forwards all writes to the passed-in buf_v (which should be cast from a\n * buf_descriptor_t *).\n */\nstatic void\nforwarding_cb(void *buf_descriptor_v, const char *str) {\n\tbuf_descriptor_t *buf_descriptor = (buf_descriptor_t *)buf_descriptor_v;\n\n\tif (print_raw) {\n\t\tmalloc_printf(\"%s\", str);\n\t}\n\tif (print_escaped) {\n\t\tconst char *it = str;\n\t\twhile (*it != '\\0') {\n\t\t\tif (!buf_descriptor->mid_quote) {\n\t\t\t\tmalloc_printf(\"\\\"\");\n\t\t\t\tbuf_descriptor->mid_quote = true;\n\t\t\t}\n\t\t\tswitch (*it) {\n\t\t\tcase '\\\\':\n\t\t\t\tmalloc_printf(\"\\\\\");\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\tmalloc_printf(\"\\\\\\\"\");\n\t\t\t\tbreak;\n\t\t\tcase '\\t':\n\t\t\t\tmalloc_printf(\"\\\\t\");\n\t\t\t\tbreak;\n\t\t\tcase '\\n':\n\t\t\t\tmalloc_printf(\"\\\\n\\\"\\n\");\n\t\t\t\tbuf_descriptor->mid_quote = false;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tmalloc_printf(\"%c\", *it);\n\t\t\t}\n\t\t\tit++;\n\t\t}\n\t}\n\n\tsize_t written = malloc_snprintf(buf_descriptor->buf,\n\t    buf_descriptor->len, \"%s\", str);\n\tassert_zu_eq(written, strlen(str), \"Buffer overflow!\");\n\tbuf_descriptor->buf += written;\n\tbuf_descriptor->len -= written;\n\tassert_zu_gt(buf_descriptor->len, 0, \"Buffer out of space!\");\n}\n\nstatic void\nassert_emit_output(void (*emit_fn)(emitter_t *),\n    const char *expected_json_output, const char *expected_table_output) {\n\temitter_t emitter;\n\tchar buf[MALLOC_PRINTF_BUFSIZE];\n\tbuf_descriptor_t buf_descriptor;\n\n\tbuf_descriptor.buf = buf;\n\tbuf_descriptor.len = MALLOC_PRINTF_BUFSIZE;\n\tbuf_descriptor.mid_quote = false;\n\n\temitter_init(&emitter, emitter_output_json, &forwarding_cb,\n\t    &buf_descriptor);\n\t(*emit_fn)(&emitter);\n\tassert_str_eq(expected_json_output, buf, \"json output failure\");\n\n\tbuf_descriptor.buf = buf;\n\tbuf_descriptor.len = MALLOC_PRINTF_BUFSIZE;\n\tbuf_descriptor.mid_quote = false;\n\n\temitter_init(&emitter, emitter_output_table, &forwarding_cb,\n\t    &buf_descriptor);\n\t(*emit_fn)(&emitter);\n\tassert_str_eq(expected_table_output, buf, \"table output failure\");\n}\n\nstatic void\nemit_dict(emitter_t *emitter) {\n\tbool b_false = false;\n\tbool b_true = true;\n\tint i_123 = 123;\n\tconst char *str = \"a string\";\n\n\temitter_begin(emitter);\n\temitter_dict_begin(emitter, \"foo\", \"This is the foo table:\");\n\temitter_kv(emitter, \"abc\", \"ABC\", emitter_type_bool, &b_false);\n\temitter_kv(emitter, \"def\", \"DEF\", emitter_type_bool, &b_true);\n\temitter_kv_note(emitter, \"ghi\", \"GHI\", emitter_type_int, &i_123,\n\t    \"note_key1\", emitter_type_string, &str);\n\temitter_kv_note(emitter, \"jkl\", \"JKL\", emitter_type_string, &str,\n\t    \"note_key2\", emitter_type_bool, &b_false);\n\temitter_dict_end(emitter);\n\temitter_end(emitter);\n}\nstatic const char *dict_json =\n\"{\\n\"\n\"\\t\\\"foo\\\": {\\n\"\n\"\\t\\t\\\"abc\\\": false,\\n\"\n\"\\t\\t\\\"def\\\": true,\\n\"\n\"\\t\\t\\\"ghi\\\": 123,\\n\"\n\"\\t\\t\\\"jkl\\\": \\\"a string\\\"\\n\"\n\"\\t}\\n\"\n\"}\\n\";\nstatic const char *dict_table =\n\"This is the foo table:\\n\"\n\"  ABC: false\\n\"\n\"  DEF: true\\n\"\n\"  GHI: 123 (note_key1: \\\"a string\\\")\\n\"\n\"  JKL: \\\"a string\\\" (note_key2: false)\\n\";\n\nTEST_BEGIN(test_dict) {\n\tassert_emit_output(&emit_dict, dict_json, dict_table);\n}\nTEST_END\n\nstatic void\nemit_table_printf(emitter_t *emitter) {\n\temitter_begin(emitter);\n\temitter_table_printf(emitter, \"Table note 1\\n\");\n\temitter_table_printf(emitter, \"Table note 2 %s\\n\",\n\t    \"with format string\");\n\temitter_end(emitter);\n}\n\nstatic const char *table_printf_json =\n\"{\\n\"\n\"}\\n\";\n\nstatic const char *table_printf_table =\n\"Table note 1\\n\"\n\"Table note 2 with format string\\n\";\n\nTEST_BEGIN(test_table_printf) {\n\tassert_emit_output(&emit_table_printf, table_printf_json,\n\t    table_printf_table);\n}\nTEST_END\n\nstatic void emit_nested_dict(emitter_t *emitter) {\n\tint val = 123;\n\temitter_begin(emitter);\n\temitter_dict_begin(emitter, \"json1\", \"Dict 1\");\n\temitter_dict_begin(emitter, \"json2\", \"Dict 2\");\n\temitter_kv(emitter, \"primitive\", \"A primitive\", emitter_type_int, &val);\n\temitter_dict_end(emitter); /* Close 2 */\n\temitter_dict_begin(emitter, \"json3\", \"Dict 3\");\n\temitter_dict_end(emitter); /* Close 3 */\n\temitter_dict_end(emitter); /* Close 1 */\n\temitter_dict_begin(emitter, \"json4\", \"Dict 4\");\n\temitter_kv(emitter, \"primitive\", \"Another primitive\",\n\t    emitter_type_int, &val);\n\temitter_dict_end(emitter); /* Close 4 */\n\temitter_end(emitter);\n}\n\nstatic const char *nested_dict_json =\n\"{\\n\"\n\"\\t\\\"json1\\\": {\\n\"\n\"\\t\\t\\\"json2\\\": {\\n\"\n\"\\t\\t\\t\\\"primitive\\\": 123\\n\"\n\"\\t\\t},\\n\"\n\"\\t\\t\\\"json3\\\": {\\n\"\n\"\\t\\t}\\n\"\n\"\\t},\\n\"\n\"\\t\\\"json4\\\": {\\n\"\n\"\\t\\t\\\"primitive\\\": 123\\n\"\n\"\\t}\\n\"\n\"}\\n\";\n\nstatic const char *nested_dict_table =\n\"Dict 1\\n\"\n\"  Dict 2\\n\"\n\"    A primitive: 123\\n\"\n\"  Dict 3\\n\"\n\"Dict 4\\n\"\n\"  Another primitive: 123\\n\";\n\nTEST_BEGIN(test_nested_dict) {\n\tassert_emit_output(&emit_nested_dict, nested_dict_json,\n\t    nested_dict_table);\n}\nTEST_END\n\nstatic void\nemit_types(emitter_t *emitter) {\n\tbool b = false;\n\tint i = -123;\n\tunsigned u = 123;\n\tssize_t zd = -456;\n\tsize_t zu = 456;\n\tconst char *str = \"string\";\n\tuint32_t u32 = 789;\n\tuint64_t u64 = 10000000000ULL;\n\n\temitter_begin(emitter);\n\temitter_kv(emitter, \"k1\", \"K1\", emitter_type_bool, &b);\n\temitter_kv(emitter, \"k2\", \"K2\", emitter_type_int, &i);\n\temitter_kv(emitter, \"k3\", \"K3\", emitter_type_unsigned, &u);\n\temitter_kv(emitter, \"k4\", \"K4\", emitter_type_ssize, &zd);\n\temitter_kv(emitter, \"k5\", \"K5\", emitter_type_size, &zu);\n\temitter_kv(emitter, \"k6\", \"K6\", emitter_type_string, &str);\n\temitter_kv(emitter, \"k7\", \"K7\", emitter_type_uint32, &u32);\n\temitter_kv(emitter, \"k8\", \"K8\", emitter_type_uint64, &u64);\n\t/*\n\t * We don't test the title type, since it's only used for tables.  It's\n\t * tested in the emitter_table_row tests.\n\t */\n\temitter_end(emitter);\n}\n\nstatic const char *types_json =\n\"{\\n\"\n\"\\t\\\"k1\\\": false,\\n\"\n\"\\t\\\"k2\\\": -123,\\n\"\n\"\\t\\\"k3\\\": 123,\\n\"\n\"\\t\\\"k4\\\": -456,\\n\"\n\"\\t\\\"k5\\\": 456,\\n\"\n\"\\t\\\"k6\\\": \\\"string\\\",\\n\"\n\"\\t\\\"k7\\\": 789,\\n\"\n\"\\t\\\"k8\\\": 10000000000\\n\"\n\"}\\n\";\n\nstatic const char *types_table =\n\"K1: false\\n\"\n\"K2: -123\\n\"\n\"K3: 123\\n\"\n\"K4: -456\\n\"\n\"K5: 456\\n\"\n\"K6: \\\"string\\\"\\n\"\n\"K7: 789\\n\"\n\"K8: 10000000000\\n\";\n\nTEST_BEGIN(test_types) {\n\tassert_emit_output(&emit_types, types_json, types_table);\n}\nTEST_END\n\nstatic void\nemit_modal(emitter_t *emitter) {\n\tint val = 123;\n\temitter_begin(emitter);\n\temitter_dict_begin(emitter, \"j0\", \"T0\");\n\temitter_json_dict_begin(emitter, \"j1\");\n\temitter_kv(emitter, \"i1\", \"I1\", emitter_type_int, &val);\n\temitter_json_kv(emitter, \"i2\", emitter_type_int, &val);\n\temitter_table_kv(emitter, \"I3\", emitter_type_int, &val);\n\temitter_table_dict_begin(emitter, \"T1\");\n\temitter_kv(emitter, \"i4\", \"I4\", emitter_type_int, &val);\n\temitter_json_dict_end(emitter); /* Close j1 */\n\temitter_kv(emitter, \"i5\", \"I5\", emitter_type_int, &val);\n\temitter_table_dict_end(emitter); /* Close T1 */\n\temitter_kv(emitter, \"i6\", \"I6\", emitter_type_int, &val);\n\temitter_dict_end(emitter); /* Close j0 / T0 */\n\temitter_end(emitter);\n}\n\nconst char *modal_json =\n\"{\\n\"\n\"\\t\\\"j0\\\": {\\n\"\n\"\\t\\t\\\"j1\\\": {\\n\"\n\"\\t\\t\\t\\\"i1\\\": 123,\\n\"\n\"\\t\\t\\t\\\"i2\\\": 123,\\n\"\n\"\\t\\t\\t\\\"i4\\\": 123\\n\"\n\"\\t\\t},\\n\"\n\"\\t\\t\\\"i5\\\": 123,\\n\"\n\"\\t\\t\\\"i6\\\": 123\\n\"\n\"\\t}\\n\"\n\"}\\n\";\n\nconst char *modal_table =\n\"T0\\n\"\n\"  I1: 123\\n\"\n\"  I3: 123\\n\"\n\"  T1\\n\"\n\"    I4: 123\\n\"\n\"    I5: 123\\n\"\n\"  I6: 123\\n\";\n\nTEST_BEGIN(test_modal) {\n\tassert_emit_output(&emit_modal, modal_json, modal_table);\n}\nTEST_END\n\nstatic void\nemit_json_arr(emitter_t *emitter) {\n\tint ival = 123;\n\n\temitter_begin(emitter);\n\temitter_json_dict_begin(emitter, \"dict\");\n\temitter_json_arr_begin(emitter, \"arr\");\n\temitter_json_arr_obj_begin(emitter);\n\temitter_json_kv(emitter, \"foo\", emitter_type_int, &ival);\n\temitter_json_arr_obj_end(emitter); /* Close arr[0] */\n\t/* arr[1] and arr[2] are primitives. */\n\temitter_json_arr_value(emitter, emitter_type_int, &ival);\n\temitter_json_arr_value(emitter, emitter_type_int, &ival);\n\temitter_json_arr_obj_begin(emitter);\n\temitter_json_kv(emitter, \"bar\", emitter_type_int, &ival);\n\temitter_json_kv(emitter, \"baz\", emitter_type_int, &ival);\n\temitter_json_arr_obj_end(emitter); /* Close arr[3]. */\n\temitter_json_arr_end(emitter); /* Close arr. */\n\temitter_json_dict_end(emitter); /* Close dict. */\n\temitter_end(emitter);\n}\n\nstatic const char *json_arr_json =\n\"{\\n\"\n\"\\t\\\"dict\\\": {\\n\"\n\"\\t\\t\\\"arr\\\": [\\n\"\n\"\\t\\t\\t{\\n\"\n\"\\t\\t\\t\\t\\\"foo\\\": 123\\n\"\n\"\\t\\t\\t},\\n\"\n\"\\t\\t\\t123,\\n\"\n\"\\t\\t\\t123,\\n\"\n\"\\t\\t\\t{\\n\"\n\"\\t\\t\\t\\t\\\"bar\\\": 123,\\n\"\n\"\\t\\t\\t\\t\\\"baz\\\": 123\\n\"\n\"\\t\\t\\t}\\n\"\n\"\\t\\t]\\n\"\n\"\\t}\\n\"\n\"}\\n\";\n\nstatic const char *json_arr_table = \"\";\n\nTEST_BEGIN(test_json_arr) {\n\tassert_emit_output(&emit_json_arr, json_arr_json, json_arr_table);\n}\nTEST_END\n\nstatic void\nemit_table_row(emitter_t *emitter) {\n\temitter_begin(emitter);\n\temitter_row_t row;\n\temitter_col_t abc = {emitter_justify_left, 10, emitter_type_title};\n\tabc.str_val = \"ABC title\";\n\temitter_col_t def = {emitter_justify_right, 15, emitter_type_title};\n\tdef.str_val = \"DEF title\";\n\temitter_col_t ghi = {emitter_justify_right, 5, emitter_type_title};\n\tghi.str_val = \"GHI\";\n\n\temitter_row_init(&row);\n\temitter_col_init(&abc, &row);\n\temitter_col_init(&def, &row);\n\temitter_col_init(&ghi, &row);\n\n\temitter_table_row(emitter, &row);\n\n\tabc.type = emitter_type_int;\n\tdef.type = emitter_type_bool;\n\tghi.type = emitter_type_int;\n\n\tabc.int_val = 123;\n\tdef.bool_val = true;\n\tghi.int_val = 456;\n\temitter_table_row(emitter, &row);\n\n\tabc.int_val = 789;\n\tdef.bool_val = false;\n\tghi.int_val = 1011;\n\temitter_table_row(emitter, &row);\n\n\tabc.type = emitter_type_string;\n\tabc.str_val = \"a string\";\n\tdef.bool_val = false;\n\tghi.type = emitter_type_title;\n\tghi.str_val = \"ghi\";\n\temitter_table_row(emitter, &row);\n\n\temitter_end(emitter);\n}\n\nstatic const char *table_row_json =\n\"{\\n\"\n\"}\\n\";\n\nstatic const char *table_row_table =\n\"ABC title       DEF title  GHI\\n\"\n\"123                  true  456\\n\"\n\"789                 false 1011\\n\"\n\"\\\"a string\\\"          false  ghi\\n\";\n\nTEST_BEGIN(test_table_row) {\n\tassert_emit_output(&emit_table_row, table_row_json, table_row_table);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_dict,\n\t    test_table_printf,\n\t    test_nested_dict,\n\t    test_types,\n\t    test_modal,\n\t    test_json_arr,\n\t    test_table_row);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/extent_quantize.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_small_extent_size) {\n\tunsigned nbins, i;\n\tsize_t sz, extent_size;\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\n\t/*\n\t * Iterate over all small size classes, get their extent sizes, and\n\t * verify that the quantized size is the same as the extent size.\n\t */\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nbins\", (void *)&nbins, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.slab_size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib failure\");\n\tfor (i = 0; i < nbins; i++) {\n\t\tmib[2] = i;\n\t\tsz = sizeof(size_t);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&extent_size, &sz,\n\t\t    NULL, 0), 0, \"Unexpected mallctlbymib failure\");\n\t\tassert_zu_eq(extent_size,\n\t\t    extent_size_quantize_floor(extent_size),\n\t\t    \"Small extent quantization should be a no-op \"\n\t\t    \"(extent_size=%zu)\", extent_size);\n\t\tassert_zu_eq(extent_size,\n\t\t    extent_size_quantize_ceil(extent_size),\n\t\t    \"Small extent quantization should be a no-op \"\n\t\t    \"(extent_size=%zu)\", extent_size);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_large_extent_size) {\n\tbool cache_oblivious;\n\tunsigned nlextents, i;\n\tsize_t sz, extent_size_prev, ceil_prev;\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\n\t/*\n\t * Iterate over all large size classes, get their extent sizes, and\n\t * verify that the quantized size is the same as the extent size.\n\t */\n\n\tsz = sizeof(bool);\n\tassert_d_eq(mallctl(\"config.cache_oblivious\", (void *)&cache_oblivious,\n\t    &sz, NULL, 0), 0, \"Unexpected mallctl failure\");\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nlextents\", (void *)&nlextents, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arenas.lextent.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib failure\");\n\tfor (i = 0; i < nlextents; i++) {\n\t\tsize_t lextent_size, extent_size, floor, ceil;\n\n\t\tmib[2] = i;\n\t\tsz = sizeof(size_t);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&lextent_size,\n\t\t    &sz, NULL, 0), 0, \"Unexpected mallctlbymib failure\");\n\t\textent_size = cache_oblivious ? lextent_size + PAGE :\n\t\t    lextent_size;\n\t\tfloor = extent_size_quantize_floor(extent_size);\n\t\tceil = extent_size_quantize_ceil(extent_size);\n\n\t\tassert_zu_eq(extent_size, floor,\n\t\t    \"Extent quantization should be a no-op for precise size \"\n\t\t    \"(lextent_size=%zu, extent_size=%zu)\", lextent_size,\n\t\t    extent_size);\n\t\tassert_zu_eq(extent_size, ceil,\n\t\t    \"Extent quantization should be a no-op for precise size \"\n\t\t    \"(lextent_size=%zu, extent_size=%zu)\", lextent_size,\n\t\t    extent_size);\n\n\t\tif (i > 0) {\n\t\t\tassert_zu_eq(extent_size_prev,\n\t\t\t    extent_size_quantize_floor(extent_size - PAGE),\n\t\t\t    \"Floor should be a precise size\");\n\t\t\tif (extent_size_prev < ceil_prev) {\n\t\t\t\tassert_zu_eq(ceil_prev, extent_size,\n\t\t\t\t    \"Ceiling should be a precise size \"\n\t\t\t\t    \"(extent_size_prev=%zu, ceil_prev=%zu, \"\n\t\t\t\t    \"extent_size=%zu)\", extent_size_prev,\n\t\t\t\t    ceil_prev, extent_size);\n\t\t\t}\n\t\t}\n\t\tif (i + 1 < nlextents) {\n\t\t\textent_size_prev = floor;\n\t\t\tceil_prev = extent_size_quantize_ceil(extent_size +\n\t\t\t    PAGE);\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_monotonic) {\n#define SZ_MAX\tZU(4 * 1024 * 1024)\n\tunsigned i;\n\tsize_t floor_prev, ceil_prev;\n\n\tfloor_prev = 0;\n\tceil_prev = 0;\n\tfor (i = 1; i <= SZ_MAX >> LG_PAGE; i++) {\n\t\tsize_t extent_size, floor, ceil;\n\n\t\textent_size = i << LG_PAGE;\n\t\tfloor = extent_size_quantize_floor(extent_size);\n\t\tceil = extent_size_quantize_ceil(extent_size);\n\n\t\tassert_zu_le(floor, extent_size,\n\t\t    \"Floor should be <= (floor=%zu, extent_size=%zu, ceil=%zu)\",\n\t\t    floor, extent_size, ceil);\n\t\tassert_zu_ge(ceil, extent_size,\n\t\t    \"Ceiling should be >= (floor=%zu, extent_size=%zu, \"\n\t\t    \"ceil=%zu)\", floor, extent_size, ceil);\n\n\t\tassert_zu_le(floor_prev, floor, \"Floor should be monotonic \"\n\t\t    \"(floor_prev=%zu, floor=%zu, extent_size=%zu, ceil=%zu)\",\n\t\t    floor_prev, floor, extent_size, ceil);\n\t\tassert_zu_le(ceil_prev, ceil, \"Ceiling should be monotonic \"\n\t\t    \"(floor=%zu, extent_size=%zu, ceil_prev=%zu, ceil=%zu)\",\n\t\t    floor, extent_size, ceil_prev, ceil);\n\n\t\tfloor_prev = floor;\n\t\tceil_prev = ceil;\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_small_extent_size,\n\t    test_large_extent_size,\n\t    test_monotonic);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/fork.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#ifndef _WIN32\n#include <sys/wait.h>\n#endif\n\n#ifndef _WIN32\nstatic void\nwait_for_child_exit(int pid) {\n\tint status;\n\twhile (true) {\n\t\tif (waitpid(pid, &status, 0) == -1) {\n\t\t\ttest_fail(\"Unexpected waitpid() failure.\");\n\t\t}\n\t\tif (WIFSIGNALED(status)) {\n\t\t\ttest_fail(\"Unexpected child termination due to \"\n\t\t\t    \"signal %d\", WTERMSIG(status));\n\t\t\tbreak;\n\t\t}\n\t\tif (WIFEXITED(status)) {\n\t\t\tif (WEXITSTATUS(status) != 0) {\n\t\t\t\ttest_fail(\"Unexpected child exit value %d\",\n\t\t\t\t    WEXITSTATUS(status));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\nTEST_BEGIN(test_fork) {\n#ifndef _WIN32\n\tvoid *p;\n\tpid_t pid;\n\n\t/* Set up a manually managed arena for test. */\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\n\t/* Migrate to the new arena. */\n\tunsigned old_arena_ind;\n\tsz = sizeof(old_arena_ind);\n\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t    (void *)&arena_ind, sizeof(arena_ind)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\tpid = fork();\n\n\tfree(p);\n\n\tp = malloc(64);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\tfree(p);\n\n\tif (pid == -1) {\n\t\t/* Error. */\n\t\ttest_fail(\"Unexpected fork() failure\");\n\t} else if (pid == 0) {\n\t\t/* Child. */\n\t\t_exit(0);\n\t} else {\n\t\twait_for_child_exit(pid);\n\t}\n#else\n\ttest_skip(\"fork(2) is irrelevant to Windows\");\n#endif\n}\nTEST_END\n\n#ifndef _WIN32\nstatic void *\ndo_fork_thd(void *arg) {\n\tmalloc(1);\n\tint pid = fork();\n\tif (pid == -1) {\n\t\t/* Error. */\n\t\ttest_fail(\"Unexpected fork() failure\");\n\t} else if (pid == 0) {\n\t\t/* Child. */\n\t\tchar *args[] = {\"true\", NULL};\n\t\texecvp(args[0], args);\n\t\ttest_fail(\"Exec failed\");\n\t} else {\n\t\t/* Parent */\n\t\twait_for_child_exit(pid);\n\t}\n\treturn NULL;\n}\n#endif\n\n#ifndef _WIN32\nstatic void\ndo_test_fork_multithreaded() {\n\tthd_t child;\n\tthd_create(&child, do_fork_thd, NULL);\n\tdo_fork_thd(NULL);\n\tthd_join(child, NULL);\n}\n#endif\n\nTEST_BEGIN(test_fork_multithreaded) {\n#ifndef _WIN32\n\t/*\n\t * We've seen bugs involving hanging on arenas_lock (though the same\n\t * class of bugs can happen on any mutex).  The bugs are intermittent\n\t * though, so we want to run the test multiple times.  Since we hold the\n\t * arenas lock only early in the process lifetime, we can't just run\n\t * this test in a loop (since, after all the arenas are initialized, we\n\t * won't acquire arenas_lock any further).  We therefore repeat the test\n\t * with multiple processes.\n\t */\n\tfor (int i = 0; i < 100; i++) {\n\t\tint pid = fork();\n\t\tif (pid == -1) {\n\t\t\t/* Error. */\n\t\t\ttest_fail(\"Unexpected fork() failure,\");\n\t\t} else if (pid == 0) {\n\t\t\t/* Child. */\n\t\t\tdo_test_fork_multithreaded();\n\t\t\t_exit(0);\n\t\t} else {\n\t\t\twait_for_child_exit(pid);\n\t\t}\n\t}\n#else\n\ttest_skip(\"fork(2) is irrelevant to Windows\");\n#endif\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_fork,\n\t    test_fork_multithreaded);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/hash.c",
    "content": "/*\n * This file is based on code that is part of SMHasher\n * (https://code.google.com/p/smhasher/), and is subject to the MIT license\n * (http://www.opensource.org/licenses/mit-license.php).  Both email addresses\n * associated with the source code's revision history belong to Austin Appleby,\n * and the revision history ranges from 2010 to 2012.  Therefore the copyright\n * and license are here taken to be:\n *\n * Copyright (c) 2010-2012 Austin Appleby\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"test/jemalloc_test.h\"\n#include \"jemalloc/internal/hash.h\"\n\ntypedef enum {\n\thash_variant_x86_32,\n\thash_variant_x86_128,\n\thash_variant_x64_128\n} hash_variant_t;\n\nstatic int\nhash_variant_bits(hash_variant_t variant) {\n\tswitch (variant) {\n\tcase hash_variant_x86_32: return 32;\n\tcase hash_variant_x86_128: return 128;\n\tcase hash_variant_x64_128: return 128;\n\tdefault: not_reached();\n\t}\n}\n\nstatic const char *\nhash_variant_string(hash_variant_t variant) {\n\tswitch (variant) {\n\tcase hash_variant_x86_32: return \"hash_x86_32\";\n\tcase hash_variant_x86_128: return \"hash_x86_128\";\n\tcase hash_variant_x64_128: return \"hash_x64_128\";\n\tdefault: not_reached();\n\t}\n}\n\n#define KEY_SIZE\t256\nstatic void\nhash_variant_verify_key(hash_variant_t variant, uint8_t *key) {\n\tconst int hashbytes = hash_variant_bits(variant) / 8;\n\tconst int hashes_size = hashbytes * 256;\n\tVARIABLE_ARRAY(uint8_t, hashes, hashes_size);\n\tVARIABLE_ARRAY(uint8_t, final, hashbytes);\n\tunsigned i;\n\tuint32_t computed, expected;\n\n\tmemset(key, 0, KEY_SIZE);\n\tmemset(hashes, 0, hashes_size);\n\tmemset(final, 0, hashbytes);\n\n\t/*\n\t * Hash keys of the form {0}, {0,1}, {0,1,2}, ..., {0,1,...,255} as the\n\t * seed.\n\t */\n\tfor (i = 0; i < 256; i++) {\n\t\tkey[i] = (uint8_t)i;\n\t\tswitch (variant) {\n\t\tcase hash_variant_x86_32: {\n\t\t\tuint32_t out;\n\t\t\tout = hash_x86_32(key, i, 256-i);\n\t\t\tmemcpy(&hashes[i*hashbytes], &out, hashbytes);\n\t\t\tbreak;\n\t\t} case hash_variant_x86_128: {\n\t\t\tuint64_t out[2];\n\t\t\thash_x86_128(key, i, 256-i, out);\n\t\t\tmemcpy(&hashes[i*hashbytes], out, hashbytes);\n\t\t\tbreak;\n\t\t} case hash_variant_x64_128: {\n\t\t\tuint64_t out[2];\n\t\t\thash_x64_128(key, i, 256-i, out);\n\t\t\tmemcpy(&hashes[i*hashbytes], out, hashbytes);\n\t\t\tbreak;\n\t\t} default: not_reached();\n\t\t}\n\t}\n\n\t/* Hash the result array. */\n\tswitch (variant) {\n\tcase hash_variant_x86_32: {\n\t\tuint32_t out = hash_x86_32(hashes, hashes_size, 0);\n\t\tmemcpy(final, &out, sizeof(out));\n\t\tbreak;\n\t} case hash_variant_x86_128: {\n\t\tuint64_t out[2];\n\t\thash_x86_128(hashes, hashes_size, 0, out);\n\t\tmemcpy(final, out, sizeof(out));\n\t\tbreak;\n\t} case hash_variant_x64_128: {\n\t\tuint64_t out[2];\n\t\thash_x64_128(hashes, hashes_size, 0, out);\n\t\tmemcpy(final, out, sizeof(out));\n\t\tbreak;\n\t} default: not_reached();\n\t}\n\n\tcomputed = (final[0] << 0) | (final[1] << 8) | (final[2] << 16) |\n\t    (final[3] << 24);\n\n\tswitch (variant) {\n#ifdef JEMALLOC_BIG_ENDIAN\n\tcase hash_variant_x86_32: expected = 0x6213303eU; break;\n\tcase hash_variant_x86_128: expected = 0x266820caU; break;\n\tcase hash_variant_x64_128: expected = 0xcc622b6fU; break;\n#else\n\tcase hash_variant_x86_32: expected = 0xb0f57ee3U; break;\n\tcase hash_variant_x86_128: expected = 0xb3ece62aU; break;\n\tcase hash_variant_x64_128: expected = 0x6384ba69U; break;\n#endif\n\tdefault: not_reached();\n\t}\n\n\tassert_u32_eq(computed, expected,\n\t    \"Hash mismatch for %s(): expected %#x but got %#x\",\n\t    hash_variant_string(variant), expected, computed);\n}\n\nstatic void\nhash_variant_verify(hash_variant_t variant) {\n#define MAX_ALIGN\t16\n\tuint8_t key[KEY_SIZE + (MAX_ALIGN - 1)];\n\tunsigned i;\n\n\tfor (i = 0; i < MAX_ALIGN; i++) {\n\t\thash_variant_verify_key(variant, &key[i]);\n\t}\n#undef MAX_ALIGN\n}\n#undef KEY_SIZE\n\nTEST_BEGIN(test_hash_x86_32) {\n\thash_variant_verify(hash_variant_x86_32);\n}\nTEST_END\n\nTEST_BEGIN(test_hash_x86_128) {\n\thash_variant_verify(hash_variant_x86_128);\n}\nTEST_END\n\nTEST_BEGIN(test_hash_x64_128) {\n\thash_variant_verify(hash_variant_x64_128);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_hash_x86_32,\n\t    test_hash_x86_128,\n\t    test_hash_x64_128);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/hooks.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic bool hook_called = false;\n\nstatic void\nhook() {\n\thook_called = true;\n}\n\nstatic int\nfunc_to_hook(int arg1, int arg2) {\n\treturn arg1 + arg2;\n}\n\n#define func_to_hook JEMALLOC_HOOK(func_to_hook, hooks_libc_hook)\n\nTEST_BEGIN(unhooked_call) {\n\thooks_libc_hook = NULL;\n\thook_called = false;\n\tassert_d_eq(3, func_to_hook(1, 2), \"Hooking changed return value.\");\n\tassert_false(hook_called, \"Nulling out hook didn't take.\");\n}\nTEST_END\n\nTEST_BEGIN(hooked_call) {\n\thooks_libc_hook = &hook;\n\thook_called = false;\n\tassert_d_eq(3, func_to_hook(1, 2), \"Hooking changed return value.\");\n\tassert_true(hook_called, \"Hook should have executed.\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    unhooked_call,\n\t    hooked_call);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/junk.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\nstatic arena_dalloc_junk_small_t *arena_dalloc_junk_small_orig;\nstatic large_dalloc_junk_t *large_dalloc_junk_orig;\nstatic large_dalloc_maybe_junk_t *large_dalloc_maybe_junk_orig;\nstatic void *watch_for_junking;\nstatic bool saw_junking;\n\nstatic void\nwatch_junking(void *p) {\n\twatch_for_junking = p;\n\tsaw_junking = false;\n}\n\nstatic void\narena_dalloc_junk_small_intercept(void *ptr, const bin_info_t *bin_info) {\n\tsize_t i;\n\n\tarena_dalloc_junk_small_orig(ptr, bin_info);\n\tfor (i = 0; i < bin_info->reg_size; i++) {\n\t\tassert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,\n\t\t    \"Missing junk fill for byte %zu/%zu of deallocated region\",\n\t\t    i, bin_info->reg_size);\n\t}\n\tif (ptr == watch_for_junking) {\n\t\tsaw_junking = true;\n\t}\n}\n\nstatic void\nlarge_dalloc_junk_intercept(void *ptr, size_t usize) {\n\tsize_t i;\n\n\tlarge_dalloc_junk_orig(ptr, usize);\n\tfor (i = 0; i < usize; i++) {\n\t\tassert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,\n\t\t    \"Missing junk fill for byte %zu/%zu of deallocated region\",\n\t\t    i, usize);\n\t}\n\tif (ptr == watch_for_junking) {\n\t\tsaw_junking = true;\n\t}\n}\n\nstatic void\nlarge_dalloc_maybe_junk_intercept(void *ptr, size_t usize) {\n\tlarge_dalloc_maybe_junk_orig(ptr, usize);\n\tif (ptr == watch_for_junking) {\n\t\tsaw_junking = true;\n\t}\n}\n\nstatic void\ntest_junk(size_t sz_min, size_t sz_max) {\n\tuint8_t *s;\n\tsize_t sz_prev, sz, i;\n\n\tif (opt_junk_free) {\n\t\tarena_dalloc_junk_small_orig = arena_dalloc_junk_small;\n\t\tarena_dalloc_junk_small = arena_dalloc_junk_small_intercept;\n\t\tlarge_dalloc_junk_orig = large_dalloc_junk;\n\t\tlarge_dalloc_junk = large_dalloc_junk_intercept;\n\t\tlarge_dalloc_maybe_junk_orig = large_dalloc_maybe_junk;\n\t\tlarge_dalloc_maybe_junk = large_dalloc_maybe_junk_intercept;\n\t}\n\n\tsz_prev = 0;\n\ts = (uint8_t *)mallocx(sz_min, 0);\n\tassert_ptr_not_null((void *)s, \"Unexpected mallocx() failure\");\n\n\tfor (sz = sallocx(s, 0); sz <= sz_max;\n\t    sz_prev = sz, sz = sallocx(s, 0)) {\n\t\tif (sz_prev > 0) {\n\t\t\tassert_u_eq(s[0], 'a',\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    ZU(0), sz_prev);\n\t\t\tassert_u_eq(s[sz_prev-1], 'a',\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    sz_prev-1, sz_prev);\n\t\t}\n\n\t\tfor (i = sz_prev; i < sz; i++) {\n\t\t\tif (opt_junk_alloc) {\n\t\t\t\tassert_u_eq(s[i], JEMALLOC_ALLOC_JUNK,\n\t\t\t\t    \"Newly allocated byte %zu/%zu isn't \"\n\t\t\t\t    \"junk-filled\", i, sz);\n\t\t\t}\n\t\t\ts[i] = 'a';\n\t\t}\n\n\t\tif (xallocx(s, sz+1, 0, 0) == sz) {\n\t\t\tuint8_t *t;\n\t\t\twatch_junking(s);\n\t\t\tt = (uint8_t *)rallocx(s, sz+1, 0);\n\t\t\tassert_ptr_not_null((void *)t,\n\t\t\t    \"Unexpected rallocx() failure\");\n\t\t\tassert_zu_ge(sallocx(t, 0), sz+1,\n\t\t\t    \"Unexpectedly small rallocx() result\");\n\t\t\tif (!background_thread_enabled()) {\n\t\t\t\tassert_ptr_ne(s, t,\n\t\t\t\t    \"Unexpected in-place rallocx()\");\n\t\t\t\tassert_true(!opt_junk_free || saw_junking,\n\t\t\t\t    \"Expected region of size %zu to be \"\n\t\t\t\t    \"junk-filled\", sz);\n\t\t\t}\n\t\t\ts = t;\n\t\t}\n\t}\n\n\twatch_junking(s);\n\tdallocx(s, 0);\n\tassert_true(!opt_junk_free || saw_junking,\n\t    \"Expected region of size %zu to be junk-filled\", sz);\n\n\tif (opt_junk_free) {\n\t\tarena_dalloc_junk_small = arena_dalloc_junk_small_orig;\n\t\tlarge_dalloc_junk = large_dalloc_junk_orig;\n\t\tlarge_dalloc_maybe_junk = large_dalloc_maybe_junk_orig;\n\t}\n}\n\nTEST_BEGIN(test_junk_small) {\n\ttest_skip_if(!config_fill);\n\ttest_junk(1, SMALL_MAXCLASS-1);\n}\nTEST_END\n\nTEST_BEGIN(test_junk_large) {\n\ttest_skip_if(!config_fill);\n\ttest_junk(SMALL_MAXCLASS+1, (1U << (LG_LARGE_MINCLASS+1)));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_junk_small,\n\t    test_junk_large);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/junk.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,zero:false,junk:true\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/junk_alloc.c",
    "content": "#include \"junk.c\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/junk_alloc.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,zero:false,junk:alloc\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/junk_free.c",
    "content": "#include \"junk.c\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/junk_free.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,zero:false,junk:free\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/log.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/log.h\"\n\nstatic void\nexpect_no_logging(const char *names) {\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\tlog_var_t log_l2 = LOG_VAR_INIT(\"l2\");\n\tlog_var_t log_l2_a = LOG_VAR_INIT(\"l2.a\");\n\n\tstrcpy(log_var_names, names);\n\n\tint count = 0;\n\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1)\n\n\t\tlog_do_begin(log_l2)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2)\n\n\t\tlog_do_begin(log_l2_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_a)\n\t}\n\tassert_d_eq(count, 0, \"Disabled logging not ignored!\");\n}\n\nTEST_BEGIN(test_log_disabled) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\texpect_no_logging(\"\");\n\texpect_no_logging(\"abc\");\n\texpect_no_logging(\"a.b.c\");\n\texpect_no_logging(\"l12\");\n\texpect_no_logging(\"l123|a456|b789\");\n\texpect_no_logging(\"|||\");\n}\nTEST_END\n\nTEST_BEGIN(test_log_enabled_direct) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\tlog_var_t log_l1_a = LOG_VAR_INIT(\"l1.a\");\n\tlog_var_t log_l2 = LOG_VAR_INIT(\"l2\");\n\n\tint count;\n\n\tcount = 0;\n\tstrcpy(log_var_names, \"l1\");\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1)\n\t}\n\tassert_d_eq(count, 10, \"Mis-logged!\");\n\n\tcount = 0;\n\tstrcpy(log_var_names, \"l1.a\");\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1_a)\n\t}\n\tassert_d_eq(count, 10, \"Mis-logged!\");\n\n\tcount = 0;\n\tstrcpy(log_var_names, \"l1.a|abc|l2|def\");\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1_a)\n\n\t\tlog_do_begin(log_l2)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2)\n\t}\n\tassert_d_eq(count, 20, \"Mis-logged!\");\n}\nTEST_END\n\nTEST_BEGIN(test_log_enabled_indirect) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\tstrcpy(log_var_names, \"l0|l1|abc|l2.b|def\");\n\n\t/* On. */\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\t/* Off. */\n\tlog_var_t log_l1a = LOG_VAR_INIT(\"l1a\");\n\t/* On. */\n\tlog_var_t log_l1_a = LOG_VAR_INIT(\"l1.a\");\n\t/* Off. */\n\tlog_var_t log_l2_a = LOG_VAR_INIT(\"l2.a\");\n\t/* On. */\n\tlog_var_t log_l2_b_a = LOG_VAR_INIT(\"l2.b.a\");\n\t/* On. */\n\tlog_var_t log_l2_b_b = LOG_VAR_INIT(\"l2.b.b\");\n\n\t/* 4 are on total, so should sum to 40. */\n\tint count = 0;\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1)\n\n\t\tlog_do_begin(log_l1a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1a)\n\n\t\tlog_do_begin(log_l1_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1_a)\n\n\t\tlog_do_begin(log_l2_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_a)\n\n\t\tlog_do_begin(log_l2_b_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_b_a)\n\n\t\tlog_do_begin(log_l2_b_b)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_b_b)\n\t}\n\n\tassert_d_eq(count, 40, \"Mis-logged!\");\n}\nTEST_END\n\nTEST_BEGIN(test_log_enabled_global) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\tstrcpy(log_var_names, \"abc|.|def\");\n\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\tlog_var_t log_l2_a_a = LOG_VAR_INIT(\"l2.a.a\");\n\n\tint count = 0;\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t    count++;\n\t\tlog_do_end(log_l1)\n\n\t\tlog_do_begin(log_l2_a_a)\n\t\t    count++;\n\t\tlog_do_end(log_l2_a_a)\n\t}\n\tassert_d_eq(count, 20, \"Mis-logged!\");\n}\nTEST_END\n\nTEST_BEGIN(test_logs_if_no_init) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, false, ATOMIC_RELAXED);\n\n\tlog_var_t l = LOG_VAR_INIT(\"definitely.not.enabled\");\n\n\tint count = 0;\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(l)\n\t\t\tcount++;\n\t\tlog_do_end(l)\n\t}\n\tassert_d_eq(count, 0, \"Logging shouldn't happen if not initialized.\");\n}\nTEST_END\n\n/*\n * This really just checks to make sure that this usage compiles; we don't have\n * any test code to run.\n */\nTEST_BEGIN(test_log_only_format_string) {\n\tif (false) {\n\t\tLOG(\"log_str\", \"No arguments follow this format string.\");\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_log_disabled,\n\t    test_log_enabled_direct,\n\t    test_log_enabled_indirect,\n\t    test_log_enabled_global,\n\t    test_logs_if_no_init,\n\t    test_log_only_format_string);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/mallctl.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\nTEST_BEGIN(test_mallctl_errors) {\n\tuint64_t epoch;\n\tsize_t sz;\n\n\tassert_d_eq(mallctl(\"no_such_name\", NULL, NULL, NULL, 0), ENOENT,\n\t    \"mallctl() should return ENOENT for non-existent names\");\n\n\tassert_d_eq(mallctl(\"version\", NULL, NULL, \"0.0.0\", strlen(\"0.0.0\")),\n\t    EPERM, \"mallctl() should return EPERM on attempt to write \"\n\t    \"read-only value\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)-1), EINVAL,\n\t    \"mallctl() should return EINVAL for input size mismatch\");\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)+1), EINVAL,\n\t    \"mallctl() should return EINVAL for input size mismatch\");\n\n\tsz = sizeof(epoch)-1;\n\tassert_d_eq(mallctl(\"epoch\", (void *)&epoch, &sz, NULL, 0), EINVAL,\n\t    \"mallctl() should return EINVAL for output size mismatch\");\n\tsz = sizeof(epoch)+1;\n\tassert_d_eq(mallctl(\"epoch\", (void *)&epoch, &sz, NULL, 0), EINVAL,\n\t    \"mallctl() should return EINVAL for output size mismatch\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctlnametomib_errors) {\n\tsize_t mib[1];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"no_such_name\", mib, &miblen), ENOENT,\n\t    \"mallctlnametomib() should return ENOENT for non-existent names\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctlbymib_errors) {\n\tuint64_t epoch;\n\tsize_t sz;\n\tsize_t mib[1];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"version\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, \"0.0.0\",\n\t    strlen(\"0.0.0\")), EPERM, \"mallctl() should return EPERM on \"\n\t    \"attempt to write read-only value\");\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"epoch\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)-1), EINVAL,\n\t    \"mallctlbymib() should return EINVAL for input size mismatch\");\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)+1), EINVAL,\n\t    \"mallctlbymib() should return EINVAL for input size mismatch\");\n\n\tsz = sizeof(epoch)-1;\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),\n\t    EINVAL,\n\t    \"mallctlbymib() should return EINVAL for output size mismatch\");\n\tsz = sizeof(epoch)+1;\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),\n\t    EINVAL,\n\t    \"mallctlbymib() should return EINVAL for output size mismatch\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctl_read_write) {\n\tuint64_t old_epoch, new_epoch;\n\tsize_t sz = sizeof(old_epoch);\n\n\t/* Blind. */\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n\n\t/* Read. */\n\tassert_d_eq(mallctl(\"epoch\", (void *)&old_epoch, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n\n\t/* Write. */\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&new_epoch,\n\t    sizeof(new_epoch)), 0, \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n\n\t/* Read+write. */\n\tassert_d_eq(mallctl(\"epoch\", (void *)&old_epoch, &sz,\n\t    (void *)&new_epoch, sizeof(new_epoch)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctlnametomib_short_mib) {\n\tsize_t mib[4];\n\tsize_t miblen;\n\n\tmiblen = 3;\n\tmib[3] = 42;\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.nregs\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tassert_zu_eq(miblen, 3, \"Unexpected mib output length\");\n\tassert_zu_eq(mib[3], 42,\n\t    \"mallctlnametomib() wrote past the end of the input mib\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctl_config) {\n#define TEST_MALLCTL_CONFIG(config, t) do {\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(oldval);\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"config.\"#config, (void *)&oldval, &sz,\t\\\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n\tassert_b_eq(oldval, config_##config, \"Incorrect config value\");\t\\\n\tassert_zu_eq(sz, sizeof(oldval), \"Unexpected output size\");\t\\\n} while (0)\n\n\tTEST_MALLCTL_CONFIG(cache_oblivious, bool);\n\tTEST_MALLCTL_CONFIG(debug, bool);\n\tTEST_MALLCTL_CONFIG(fill, bool);\n\tTEST_MALLCTL_CONFIG(lazy_lock, bool);\n\tTEST_MALLCTL_CONFIG(malloc_conf, const char *);\n\tTEST_MALLCTL_CONFIG(prof, bool);\n\tTEST_MALLCTL_CONFIG(prof_libgcc, bool);\n\tTEST_MALLCTL_CONFIG(prof_libunwind, bool);\n\tTEST_MALLCTL_CONFIG(stats, bool);\n\tTEST_MALLCTL_CONFIG(utrace, bool);\n\tTEST_MALLCTL_CONFIG(xmalloc, bool);\n\n#undef TEST_MALLCTL_CONFIG\n}\nTEST_END\n\nTEST_BEGIN(test_mallctl_opt) {\n\tbool config_always = true;\n\n#define TEST_MALLCTL_OPT(t, opt, config) do {\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(oldval);\t\t\t\t\t\\\n\tint expected = config_##config ? 0 : ENOENT;\t\t\t\\\n\tint result = mallctl(\"opt.\"#opt, (void *)&oldval, &sz, NULL,\t\\\n\t    0);\t\t\t\t\t\t\t\t\\\n\tassert_d_eq(result, expected,\t\t\t\t\t\\\n\t    \"Unexpected mallctl() result for opt.\"#opt);\t\t\\\n\tassert_zu_eq(sz, sizeof(oldval), \"Unexpected output size\");\t\\\n} while (0)\n\n\tTEST_MALLCTL_OPT(bool, abort, always);\n\tTEST_MALLCTL_OPT(bool, abort_conf, always);\n\tTEST_MALLCTL_OPT(const char *, metadata_thp, always);\n\tTEST_MALLCTL_OPT(bool, retain, always);\n\tTEST_MALLCTL_OPT(const char *, dss, always);\n\tTEST_MALLCTL_OPT(unsigned, narenas, always);\n\tTEST_MALLCTL_OPT(const char *, percpu_arena, always);\n\tTEST_MALLCTL_OPT(bool, background_thread, always);\n\tTEST_MALLCTL_OPT(ssize_t, dirty_decay_ms, always);\n\tTEST_MALLCTL_OPT(ssize_t, muzzy_decay_ms, always);\n\tTEST_MALLCTL_OPT(bool, stats_print, always);\n\tTEST_MALLCTL_OPT(const char *, junk, fill);\n\tTEST_MALLCTL_OPT(bool, zero, fill);\n\tTEST_MALLCTL_OPT(bool, utrace, utrace);\n\tTEST_MALLCTL_OPT(bool, xmalloc, xmalloc);\n\tTEST_MALLCTL_OPT(bool, tcache, always);\n\tTEST_MALLCTL_OPT(size_t, lg_extent_max_active_fit, always);\n\tTEST_MALLCTL_OPT(size_t, lg_tcache_max, always);\n\tTEST_MALLCTL_OPT(const char *, thp, always);\n\tTEST_MALLCTL_OPT(bool, prof, prof);\n\tTEST_MALLCTL_OPT(const char *, prof_prefix, prof);\n\tTEST_MALLCTL_OPT(bool, prof_active, prof);\n\tTEST_MALLCTL_OPT(ssize_t, lg_prof_sample, prof);\n\tTEST_MALLCTL_OPT(bool, prof_accum, prof);\n\tTEST_MALLCTL_OPT(ssize_t, lg_prof_interval, prof);\n\tTEST_MALLCTL_OPT(bool, prof_gdump, prof);\n\tTEST_MALLCTL_OPT(bool, prof_final, prof);\n\tTEST_MALLCTL_OPT(bool, prof_leak, prof);\n\n#undef TEST_MALLCTL_OPT\n}\nTEST_END\n\nTEST_BEGIN(test_manpage_example) {\n\tunsigned nbins, i;\n\tsize_t mib[4];\n\tsize_t len, miblen;\n\n\tlen = sizeof(nbins);\n\tassert_d_eq(mallctl(\"arenas.nbins\", (void *)&nbins, &len, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tmiblen = 4;\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tfor (i = 0; i < nbins; i++) {\n\t\tsize_t bin_size;\n\n\t\tmib[2] = i;\n\t\tlen = sizeof(bin_size);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&bin_size, &len,\n\t\t    NULL, 0), 0, \"Unexpected mallctlbymib() failure\");\n\t\t/* Do something with bin_size... */\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_tcache_none) {\n\ttest_skip_if(!opt_tcache);\n\n\t/* Allocate p and q. */\n\tvoid *p0 = mallocx(42, 0);\n\tassert_ptr_not_null(p0, \"Unexpected mallocx() failure\");\n\tvoid *q = mallocx(42, 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\n\t/* Deallocate p and q, but bypass the tcache for q. */\n\tdallocx(p0, 0);\n\tdallocx(q, MALLOCX_TCACHE_NONE);\n\n\t/* Make sure that tcache-based allocation returns p, not q. */\n\tvoid *p1 = mallocx(42, 0);\n\tassert_ptr_not_null(p1, \"Unexpected mallocx() failure\");\n\tassert_ptr_eq(p0, p1, \"Expected tcache to allocate cached region\");\n\n\t/* Clean up. */\n\tdallocx(p1, MALLOCX_TCACHE_NONE);\n}\nTEST_END\n\nTEST_BEGIN(test_tcache) {\n#define NTCACHES\t10\n\tunsigned tis[NTCACHES];\n\tvoid *ps[NTCACHES];\n\tvoid *qs[NTCACHES];\n\tunsigned i;\n\tsize_t sz, psz, qsz;\n\n\tpsz = 42;\n\tqsz = nallocx(psz, 0) + 1;\n\n\t/* Create tcaches. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tsz = sizeof(unsigned);\n\t\tassert_d_eq(mallctl(\"tcache.create\", (void *)&tis[i], &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n\n\t/* Exercise tcache ID recycling. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.destroy\", NULL, NULL,\n\t\t    (void *)&tis[i], sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tsz = sizeof(unsigned);\n\t\tassert_d_eq(mallctl(\"tcache.create\", (void *)&tis[i], &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n\n\t/* Flush empty tcaches. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.flush\", NULL, NULL, (void *)&tis[i],\n\t\t    sizeof(unsigned)), 0, \"Unexpected mallctl() failure, i=%u\",\n\t\t    i);\n\t}\n\n\t/* Cache some allocations. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(ps[i], \"Unexpected mallocx() failure, i=%u\",\n\t\t    i);\n\t\tdallocx(ps[i], MALLOCX_TCACHE(tis[i]));\n\n\t\tqs[i] = mallocx(qsz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(qs[i], \"Unexpected mallocx() failure, i=%u\",\n\t\t    i);\n\t\tdallocx(qs[i], MALLOCX_TCACHE(tis[i]));\n\t}\n\n\t/* Verify that tcaches allocate cached regions. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tvoid *p0 = ps[i];\n\t\tps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(ps[i], \"Unexpected mallocx() failure, i=%u\",\n\t\t    i);\n\t\tassert_ptr_eq(ps[i], p0,\n\t\t    \"Expected mallocx() to allocate cached region, i=%u\", i);\n\t}\n\n\t/* Verify that reallocation uses cached regions. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tvoid *q0 = qs[i];\n\t\tqs[i] = rallocx(ps[i], qsz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(qs[i], \"Unexpected rallocx() failure, i=%u\",\n\t\t    i);\n\t\tassert_ptr_eq(qs[i], q0,\n\t\t    \"Expected rallocx() to allocate cached region, i=%u\", i);\n\t\t/* Avoid undefined behavior in case of test failure. */\n\t\tif (qs[i] == NULL) {\n\t\t\tqs[i] = ps[i];\n\t\t}\n\t}\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tdallocx(qs[i], MALLOCX_TCACHE(tis[i]));\n\t}\n\n\t/* Flush some non-empty tcaches. */\n\tfor (i = 0; i < NTCACHES/2; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.flush\", NULL, NULL, (void *)&tis[i],\n\t\t    sizeof(unsigned)), 0, \"Unexpected mallctl() failure, i=%u\",\n\t\t    i);\n\t}\n\n\t/* Destroy tcaches. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.destroy\", NULL, NULL,\n\t\t    (void *)&tis[i], sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_thread_arena) {\n\tunsigned old_arena_ind, new_arena_ind, narenas;\n\n\tconst char *opa;\n\tsize_t sz = sizeof(opa);\n\tassert_d_eq(mallctl(\"opt.percpu_arena\", (void *)&opa, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_u_eq(narenas, opt_narenas, \"Number of arenas incorrect\");\n\n\tif (strcmp(opa, \"disabled\") == 0) {\n\t\tnew_arena_ind = narenas - 1;\n\t\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t\t    (void *)&new_arena_ind, sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure\");\n\t\tnew_arena_ind = 0;\n\t\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t\t    (void *)&new_arena_ind, sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure\");\n\t} else {\n\t\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\t\tnew_arena_ind = percpu_arena_ind_limit(opt_percpu_arena) - 1;\n\t\tif (old_arena_ind != new_arena_ind) {\n\t\t\tassert_d_eq(mallctl(\"thread.arena\",\n\t\t\t    (void *)&old_arena_ind, &sz, (void *)&new_arena_ind,\n\t\t\t    sizeof(unsigned)), EPERM, \"thread.arena ctl \"\n\t\t\t    \"should not be allowed with percpu arena\");\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_initialized) {\n\tunsigned narenas, i;\n\tsize_t sz;\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\tbool initialized;\n\n\tsz = sizeof(narenas);\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.initialized\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tfor (i = 0; i < narenas; i++) {\n\t\tmib[1] = i;\n\t\tsz = sizeof(initialized);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl() failure\");\n\t}\n\n\tmib[1] = MALLCTL_ARENAS_ALL;\n\tsz = sizeof(initialized);\n\tassert_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_true(initialized,\n\t    \"Merged arena statistics should always be initialized\");\n\n\t/* Equivalent to the above but using mallctl() directly. */\n\tsz = sizeof(initialized);\n\tassert_d_eq(mallctl(\n\t    \"arena.\" STRINGIFY(MALLCTL_ARENAS_ALL) \".initialized\",\n\t    (void *)&initialized, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_true(initialized,\n\t    \"Merged arena statistics should always be initialized\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_dirty_decay_ms) {\n\tssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\",\n\t    (void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tdirty_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tdirty_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tfor (prev_dirty_decay_ms = dirty_decay_ms, dirty_decay_ms = -1;\n\t    dirty_decay_ms < 20; prev_dirty_decay_ms = dirty_decay_ms,\n\t    dirty_decay_ms++) {\n\t\tssize_t old_dirty_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\",\n\t\t    (void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,\n\t\t    \"Unexpected old arena.0.dirty_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_muzzy_decay_ms) {\n\tssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\",\n\t    (void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tmuzzy_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tmuzzy_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tfor (prev_muzzy_decay_ms = muzzy_decay_ms, muzzy_decay_ms = -1;\n\t    muzzy_decay_ms < 20; prev_muzzy_decay_ms = muzzy_decay_ms,\n\t    muzzy_decay_ms++) {\n\t\tssize_t old_muzzy_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\",\n\t\t    (void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,\n\t\t    \"Unexpected old arena.0.muzzy_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_purge) {\n\tunsigned narenas;\n\tsize_t sz = sizeof(unsigned);\n\tsize_t mib[3];\n\tsize_t miblen = 3;\n\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlnametomib(\"arena.0.purge\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = narenas;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\tmib[1] = MALLCTL_ARENAS_ALL;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_decay) {\n\tunsigned narenas;\n\tsize_t sz = sizeof(unsigned);\n\tsize_t mib[3];\n\tsize_t miblen = 3;\n\n\tassert_d_eq(mallctl(\"arena.0.decay\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlnametomib(\"arena.0.decay\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = narenas;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\tmib[1] = MALLCTL_ARENAS_ALL;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_dss) {\n\tconst char *dss_prec_old, *dss_prec_new;\n\tsize_t sz = sizeof(dss_prec_old);\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.dss\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() error\");\n\n\tdss_prec_new = \"disabled\";\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,\n\t    (void *)&dss_prec_new, sizeof(dss_prec_new)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected default for dss precedence\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,\n\t    (void *)&dss_prec_old, sizeof(dss_prec_old)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected value for dss precedence\");\n\n\tmib[1] = narenas_total_get();\n\tdss_prec_new = \"disabled\";\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,\n\t    (void *)&dss_prec_new, sizeof(dss_prec_new)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected default for dss precedence\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,\n\t    (void *)&dss_prec_old, sizeof(dss_prec_new)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected value for dss precedence\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_retain_grow_limit) {\n\tsize_t old_limit, new_limit, default_limit;\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tbool retain_enabled;\n\tsize_t sz = sizeof(retain_enabled);\n\tassert_d_eq(mallctl(\"opt.retain\", &retain_enabled, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\ttest_skip_if(!retain_enabled);\n\n\tsz = sizeof(default_limit);\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.retain_grow_limit\", mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib() error\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, &default_limit, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(default_limit, sz_pind2sz(EXTENT_GROW_MAX_PIND),\n\t    \"Unexpected default for retain_grow_limit\");\n\n\tnew_limit = PAGE - 1;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,\n\t    sizeof(new_limit)), EFAULT, \"Unexpected mallctl() success\");\n\n\tnew_limit = PAGE + 1;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,\n\t    sizeof(new_limit)), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(old_limit, PAGE,\n\t    \"Unexpected value for retain_grow_limit\");\n\n\t/* Expect grow less than psize class 10. */\n\tnew_limit = sz_pind2sz(10) - 1;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,\n\t    sizeof(new_limit)), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(old_limit, sz_pind2sz(9),\n\t    \"Unexpected value for retain_grow_limit\");\n\n\t/* Restore to default. */\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &default_limit,\n\t    sizeof(default_limit)), 0, \"Unexpected mallctl() failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_dirty_decay_ms) {\n\tssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\",\n\t    (void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tdirty_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tdirty_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Expected mallctl() failure\");\n\n\tfor (prev_dirty_decay_ms = dirty_decay_ms, dirty_decay_ms = -1;\n\t    dirty_decay_ms < 20; prev_dirty_decay_ms = dirty_decay_ms,\n\t    dirty_decay_ms++) {\n\t\tssize_t old_dirty_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\",\n\t\t    (void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,\n\t\t    \"Unexpected old arenas.dirty_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_muzzy_decay_ms) {\n\tssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\",\n\t    (void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tmuzzy_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tmuzzy_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Expected mallctl() failure\");\n\n\tfor (prev_muzzy_decay_ms = muzzy_decay_ms, muzzy_decay_ms = -1;\n\t    muzzy_decay_ms < 20; prev_muzzy_decay_ms = muzzy_decay_ms,\n\t    muzzy_decay_ms++) {\n\t\tssize_t old_muzzy_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\",\n\t\t    (void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,\n\t\t    \"Unexpected old arenas.muzzy_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_constants) {\n#define TEST_ARENAS_CONSTANT(t, name, expected) do {\t\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"arenas.\"#name, (void *)&name, &sz, NULL,\t\\\n\t    0), 0, \"Unexpected mallctl() failure\");\t\t\t\\\n\tassert_zu_eq(name, expected, \"Incorrect \"#name\" size\");\t\t\\\n} while (0)\n\n\tTEST_ARENAS_CONSTANT(size_t, quantum, QUANTUM);\n\tTEST_ARENAS_CONSTANT(size_t, page, PAGE);\n\tTEST_ARENAS_CONSTANT(unsigned, nbins, NBINS);\n\tTEST_ARENAS_CONSTANT(unsigned, nlextents, NSIZES - NBINS);\n\n#undef TEST_ARENAS_CONSTANT\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_bin_constants) {\n#define TEST_ARENAS_BIN_CONSTANT(t, name, expected) do {\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"arenas.bin.0.\"#name, (void *)&name, &sz,\t\\\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n\tassert_zu_eq(name, expected, \"Incorrect \"#name\" size\");\t\t\\\n} while (0)\n\n\tTEST_ARENAS_BIN_CONSTANT(size_t, size, bin_infos[0].reg_size);\n\tTEST_ARENAS_BIN_CONSTANT(uint32_t, nregs, bin_infos[0].nregs);\n\tTEST_ARENAS_BIN_CONSTANT(size_t, slab_size,\n\t    bin_infos[0].slab_size);\n\n#undef TEST_ARENAS_BIN_CONSTANT\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_lextent_constants) {\n#define TEST_ARENAS_LEXTENT_CONSTANT(t, name, expected) do {\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"arenas.lextent.0.\"#name, (void *)&name,\t\\\n\t    &sz, NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n\tassert_zu_eq(name, expected, \"Incorrect \"#name\" size\");\t\t\\\n} while (0)\n\n\tTEST_ARENAS_LEXTENT_CONSTANT(size_t, size, LARGE_MINCLASS);\n\n#undef TEST_ARENAS_LEXTENT_CONSTANT\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_create) {\n\tunsigned narenas_before, arena, narenas_after;\n\tsize_t sz = sizeof(unsigned);\n\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas_before, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas_after, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\n\tassert_u_eq(narenas_before+1, narenas_after,\n\t    \"Unexpected number of arenas before versus after extension\");\n\tassert_u_eq(arena, narenas_after-1, \"Unexpected arena index\");\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_lookup) {\n\tunsigned arena, arena1;\n\tvoid *ptr;\n\tsize_t sz = sizeof(unsigned);\n\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tptr = mallocx(42, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE);\n\tassert_ptr_not_null(ptr, \"Unexpected mallocx() failure\");\n\tassert_d_eq(mallctl(\"arenas.lookup\", &arena1, &sz, &ptr, sizeof(ptr)),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_u_eq(arena, arena1, \"Unexpected arena index\");\n\tdallocx(ptr, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas) {\n#define TEST_STATS_ARENAS(t, name) do {\t\t\t\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"stats.arenas.0.\"#name, (void *)&name, &sz,\t\\\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n} while (0)\n\n\tTEST_STATS_ARENAS(unsigned, nthreads);\n\tTEST_STATS_ARENAS(const char *, dss);\n\tTEST_STATS_ARENAS(ssize_t, dirty_decay_ms);\n\tTEST_STATS_ARENAS(ssize_t, muzzy_decay_ms);\n\tTEST_STATS_ARENAS(size_t, pactive);\n\tTEST_STATS_ARENAS(size_t, pdirty);\n\n#undef TEST_STATS_ARENAS\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_mallctl_errors,\n\t    test_mallctlnametomib_errors,\n\t    test_mallctlbymib_errors,\n\t    test_mallctl_read_write,\n\t    test_mallctlnametomib_short_mib,\n\t    test_mallctl_config,\n\t    test_mallctl_opt,\n\t    test_manpage_example,\n\t    test_tcache_none,\n\t    test_tcache,\n\t    test_thread_arena,\n\t    test_arena_i_initialized,\n\t    test_arena_i_dirty_decay_ms,\n\t    test_arena_i_muzzy_decay_ms,\n\t    test_arena_i_purge,\n\t    test_arena_i_decay,\n\t    test_arena_i_dss,\n\t    test_arena_i_retain_grow_limit,\n\t    test_arenas_dirty_decay_ms,\n\t    test_arenas_muzzy_decay_ms,\n\t    test_arenas_constants,\n\t    test_arenas_bin_constants,\n\t    test_arenas_lextent_constants,\n\t    test_arenas_create,\n\t    test_arenas_lookup,\n\t    test_stats_arenas);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/malloc_io.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_malloc_strtoumax_no_endptr) {\n\tint err;\n\n\tset_errno(0);\n\tassert_ju_eq(malloc_strtoumax(\"0\", NULL, 0), 0, \"Unexpected result\");\n\terr = get_errno();\n\tassert_d_eq(err, 0, \"Unexpected failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_malloc_strtoumax) {\n\tstruct test_s {\n\t\tconst char *input;\n\t\tconst char *expected_remainder;\n\t\tint base;\n\t\tint expected_errno;\n\t\tconst char *expected_errno_name;\n\t\tuintmax_t expected_x;\n\t};\n#define ERR(e)\t\te, #e\n#define KUMAX(x)\t((uintmax_t)x##ULL)\n#define KSMAX(x)\t((uintmax_t)(intmax_t)x##LL)\n\tstruct test_s tests[] = {\n\t\t{\"0\",\t\t\"0\",\t-1,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"0\",\t\t\"0\",\t1,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"0\",\t\t\"0\",\t37,\tERR(EINVAL),\tUINTMAX_MAX},\n\n\t\t{\"\",\t\t\"\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"+\",\t\t\"+\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"++3\",\t\t\"++3\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"-\",\t\t\"-\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\n\t\t{\"42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"+42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"-42\",\t\t\"\",\t0,\tERR(0),\t\tKSMAX(-42)},\n\t\t{\"042\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(042)},\n\t\t{\"+042\",\t\"\",\t0,\tERR(0),\t\tKUMAX(042)},\n\t\t{\"-042\",\t\"\",\t0,\tERR(0),\t\tKSMAX(-042)},\n\t\t{\"0x42\",\t\"\",\t0,\tERR(0),\t\tKUMAX(0x42)},\n\t\t{\"+0x42\",\t\"\",\t0,\tERR(0),\t\tKUMAX(0x42)},\n\t\t{\"-0x42\",\t\"\",\t0,\tERR(0),\t\tKSMAX(-0x42)},\n\n\t\t{\"0\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"1\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(1)},\n\n\t\t{\"42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\" 42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"42 \",\t\t\" \",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"0x\",\t\t\"x\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"42x\",\t\t\"x\",\t0,\tERR(0),\t\tKUMAX(42)},\n\n\t\t{\"07\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(7)},\n\t\t{\"010\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(8)},\n\t\t{\"08\",\t\t\"8\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0_\",\t\t\"_\",\t0,\tERR(0),\t\tKUMAX(0)},\n\n\t\t{\"0x\",\t\t\"x\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0X\",\t\t\"X\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0xg\",\t\t\"xg\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0XA\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(10)},\n\n\t\t{\"010\",\t\t\"\",\t10,\tERR(0),\t\tKUMAX(10)},\n\t\t{\"0x3\",\t\t\"x3\",\t10,\tERR(0),\t\tKUMAX(0)},\n\n\t\t{\"12\",\t\t\"2\",\t2,\tERR(0),\t\tKUMAX(1)},\n\t\t{\"78\",\t\t\"8\",\t8,\tERR(0),\t\tKUMAX(7)},\n\t\t{\"9a\",\t\t\"a\",\t10,\tERR(0),\t\tKUMAX(9)},\n\t\t{\"9A\",\t\t\"A\",\t10,\tERR(0),\t\tKUMAX(9)},\n\t\t{\"fg\",\t\t\"g\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"FG\",\t\t\"G\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"0xfg\",\t\"g\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"0XFG\",\t\"G\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"z_\",\t\t\"_\",\t36,\tERR(0),\t\tKUMAX(35)},\n\t\t{\"Z_\",\t\t\"_\",\t36,\tERR(0),\t\tKUMAX(35)}\n\t};\n#undef ERR\n#undef KUMAX\n#undef KSMAX\n\tunsigned i;\n\n\tfor (i = 0; i < sizeof(tests)/sizeof(struct test_s); i++) {\n\t\tstruct test_s *test = &tests[i];\n\t\tint err;\n\t\tuintmax_t result;\n\t\tchar *remainder;\n\n\t\tset_errno(0);\n\t\tresult = malloc_strtoumax(test->input, &remainder, test->base);\n\t\terr = get_errno();\n\t\tassert_d_eq(err, test->expected_errno,\n\t\t    \"Expected errno %s for \\\"%s\\\", base %d\",\n\t\t    test->expected_errno_name, test->input, test->base);\n\t\tassert_str_eq(remainder, test->expected_remainder,\n\t\t    \"Unexpected remainder for \\\"%s\\\", base %d\",\n\t\t    test->input, test->base);\n\t\tif (err == 0) {\n\t\t\tassert_ju_eq(result, test->expected_x,\n\t\t\t    \"Unexpected result for \\\"%s\\\", base %d\",\n\t\t\t    test->input, test->base);\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_malloc_snprintf_truncated) {\n#define BUFLEN\t15\n\tchar buf[BUFLEN];\n\tsize_t result;\n\tsize_t len;\n#define TEST(expected_str_untruncated, ...) do {\t\t\t\\\n\tresult = malloc_snprintf(buf, len, __VA_ARGS__);\t\t\\\n\tassert_d_eq(strncmp(buf, expected_str_untruncated, len-1), 0,\t\\\n\t    \"Unexpected string inequality (\\\"%s\\\" vs \\\"%s\\\")\",\t\t\\\n\t    buf, expected_str_untruncated);\t\t\t\t\\\n\tassert_zu_eq(result, strlen(expected_str_untruncated),\t\t\\\n\t    \"Unexpected result\");\t\t\t\t\t\\\n} while (0)\n\n\tfor (len = 1; len < BUFLEN; len++) {\n\t\tTEST(\"012346789\",\t\"012346789\");\n\t\tTEST(\"a0123b\",\t\t\"a%sb\", \"0123\");\n\t\tTEST(\"a01234567\",\t\"a%s%s\", \"0123\", \"4567\");\n\t\tTEST(\"a0123  \",\t\t\"a%-6s\", \"0123\");\n\t\tTEST(\"a  0123\",\t\t\"a%6s\", \"0123\");\n\t\tTEST(\"a   012\",\t\t\"a%6.3s\", \"0123\");\n\t\tTEST(\"a   012\",\t\t\"a%*.*s\", 6, 3, \"0123\");\n\t\tTEST(\"a 123b\",\t\t\"a% db\", 123);\n\t\tTEST(\"a123b\",\t\t\"a%-db\", 123);\n\t\tTEST(\"a-123b\",\t\t\"a%-db\", -123);\n\t\tTEST(\"a+123b\",\t\t\"a%+db\", 123);\n\t}\n#undef BUFLEN\n#undef TEST\n}\nTEST_END\n\nTEST_BEGIN(test_malloc_snprintf) {\n#define BUFLEN\t128\n\tchar buf[BUFLEN];\n\tsize_t result;\n#define TEST(expected_str, ...) do {\t\t\t\t\t\\\n\tresult = malloc_snprintf(buf, sizeof(buf), __VA_ARGS__);\t\\\n\tassert_str_eq(buf, expected_str, \"Unexpected output\");\t\t\\\n\tassert_zu_eq(result, strlen(expected_str), \"Unexpected result\");\\\n} while (0)\n\n\tTEST(\"hello\", \"hello\");\n\n\tTEST(\"50%, 100%\", \"50%%, %d%%\", 100);\n\n\tTEST(\"a0123b\", \"a%sb\", \"0123\");\n\n\tTEST(\"a 0123b\", \"a%5sb\", \"0123\");\n\tTEST(\"a 0123b\", \"a%*sb\", 5, \"0123\");\n\n\tTEST(\"a0123 b\", \"a%-5sb\", \"0123\");\n\tTEST(\"a0123b\", \"a%*sb\", -1, \"0123\");\n\tTEST(\"a0123 b\", \"a%*sb\", -5, \"0123\");\n\tTEST(\"a0123 b\", \"a%-*sb\", -5, \"0123\");\n\n\tTEST(\"a012b\", \"a%.3sb\", \"0123\");\n\tTEST(\"a012b\", \"a%.*sb\", 3, \"0123\");\n\tTEST(\"a0123b\", \"a%.*sb\", -3, \"0123\");\n\n\tTEST(\"a  012b\", \"a%5.3sb\", \"0123\");\n\tTEST(\"a  012b\", \"a%5.*sb\", 3, \"0123\");\n\tTEST(\"a  012b\", \"a%*.3sb\", 5, \"0123\");\n\tTEST(\"a  012b\", \"a%*.*sb\", 5, 3, \"0123\");\n\tTEST(\"a 0123b\", \"a%*.*sb\", 5, -3, \"0123\");\n\n\tTEST(\"_abcd_\", \"_%x_\", 0xabcd);\n\tTEST(\"_0xabcd_\", \"_%#x_\", 0xabcd);\n\tTEST(\"_1234_\", \"_%o_\", 01234);\n\tTEST(\"_01234_\", \"_%#o_\", 01234);\n\tTEST(\"_1234_\", \"_%u_\", 1234);\n\n\tTEST(\"_1234_\", \"_%d_\", 1234);\n\tTEST(\"_ 1234_\", \"_% d_\", 1234);\n\tTEST(\"_+1234_\", \"_%+d_\", 1234);\n\tTEST(\"_-1234_\", \"_%d_\", -1234);\n\tTEST(\"_-1234_\", \"_% d_\", -1234);\n\tTEST(\"_-1234_\", \"_%+d_\", -1234);\n\n\tTEST(\"_-1234_\", \"_%d_\", -1234);\n\tTEST(\"_1234_\", \"_%d_\", 1234);\n\tTEST(\"_-1234_\", \"_%i_\", -1234);\n\tTEST(\"_1234_\", \"_%i_\", 1234);\n\tTEST(\"_01234_\", \"_%#o_\", 01234);\n\tTEST(\"_1234_\", \"_%u_\", 1234);\n\tTEST(\"_0x1234abc_\", \"_%#x_\", 0x1234abc);\n\tTEST(\"_0X1234ABC_\", \"_%#X_\", 0x1234abc);\n\tTEST(\"_c_\", \"_%c_\", 'c');\n\tTEST(\"_string_\", \"_%s_\", \"string\");\n\tTEST(\"_0x42_\", \"_%p_\", ((void *)0x42));\n\n\tTEST(\"_-1234_\", \"_%ld_\", ((long)-1234));\n\tTEST(\"_1234_\", \"_%ld_\", ((long)1234));\n\tTEST(\"_-1234_\", \"_%li_\", ((long)-1234));\n\tTEST(\"_1234_\", \"_%li_\", ((long)1234));\n\tTEST(\"_01234_\", \"_%#lo_\", ((long)01234));\n\tTEST(\"_1234_\", \"_%lu_\", ((long)1234));\n\tTEST(\"_0x1234abc_\", \"_%#lx_\", ((long)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#lX_\", ((long)0x1234ABC));\n\n\tTEST(\"_-1234_\", \"_%lld_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%lld_\", ((long long)1234));\n\tTEST(\"_-1234_\", \"_%lli_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%lli_\", ((long long)1234));\n\tTEST(\"_01234_\", \"_%#llo_\", ((long long)01234));\n\tTEST(\"_1234_\", \"_%llu_\", ((long long)1234));\n\tTEST(\"_0x1234abc_\", \"_%#llx_\", ((long long)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#llX_\", ((long long)0x1234ABC));\n\n\tTEST(\"_-1234_\", \"_%qd_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%qd_\", ((long long)1234));\n\tTEST(\"_-1234_\", \"_%qi_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%qi_\", ((long long)1234));\n\tTEST(\"_01234_\", \"_%#qo_\", ((long long)01234));\n\tTEST(\"_1234_\", \"_%qu_\", ((long long)1234));\n\tTEST(\"_0x1234abc_\", \"_%#qx_\", ((long long)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#qX_\", ((long long)0x1234ABC));\n\n\tTEST(\"_-1234_\", \"_%jd_\", ((intmax_t)-1234));\n\tTEST(\"_1234_\", \"_%jd_\", ((intmax_t)1234));\n\tTEST(\"_-1234_\", \"_%ji_\", ((intmax_t)-1234));\n\tTEST(\"_1234_\", \"_%ji_\", ((intmax_t)1234));\n\tTEST(\"_01234_\", \"_%#jo_\", ((intmax_t)01234));\n\tTEST(\"_1234_\", \"_%ju_\", ((intmax_t)1234));\n\tTEST(\"_0x1234abc_\", \"_%#jx_\", ((intmax_t)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#jX_\", ((intmax_t)0x1234ABC));\n\n\tTEST(\"_1234_\", \"_%td_\", ((ptrdiff_t)1234));\n\tTEST(\"_-1234_\", \"_%td_\", ((ptrdiff_t)-1234));\n\tTEST(\"_1234_\", \"_%ti_\", ((ptrdiff_t)1234));\n\tTEST(\"_-1234_\", \"_%ti_\", ((ptrdiff_t)-1234));\n\n\tTEST(\"_-1234_\", \"_%zd_\", ((ssize_t)-1234));\n\tTEST(\"_1234_\", \"_%zd_\", ((ssize_t)1234));\n\tTEST(\"_-1234_\", \"_%zi_\", ((ssize_t)-1234));\n\tTEST(\"_1234_\", \"_%zi_\", ((ssize_t)1234));\n\tTEST(\"_01234_\", \"_%#zo_\", ((ssize_t)01234));\n\tTEST(\"_1234_\", \"_%zu_\", ((ssize_t)1234));\n\tTEST(\"_0x1234abc_\", \"_%#zx_\", ((ssize_t)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#zX_\", ((ssize_t)0x1234ABC));\n#undef BUFLEN\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_malloc_strtoumax_no_endptr,\n\t    test_malloc_strtoumax,\n\t    test_malloc_snprintf_truncated,\n\t    test_malloc_snprintf);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/math.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAX_REL_ERR 1.0e-9\n#define MAX_ABS_ERR 1.0e-9\n\n#include <float.h>\n\n#ifdef __PGI\n#undef INFINITY\n#endif\n\n#ifndef INFINITY\n#define INFINITY (DBL_MAX + DBL_MAX)\n#endif\n\nstatic bool\ndouble_eq_rel(double a, double b, double max_rel_err, double max_abs_err) {\n\tdouble rel_err;\n\n\tif (fabs(a - b) < max_abs_err) {\n\t\treturn true;\n\t}\n\trel_err = (fabs(b) > fabs(a)) ? fabs((a-b)/b) : fabs((a-b)/a);\n\treturn (rel_err < max_rel_err);\n}\n\nstatic uint64_t\nfactorial(unsigned x) {\n\tuint64_t ret = 1;\n\tunsigned i;\n\n\tfor (i = 2; i <= x; i++) {\n\t\tret *= (uint64_t)i;\n\t}\n\n\treturn ret;\n}\n\nTEST_BEGIN(test_ln_gamma_factorial) {\n\tunsigned x;\n\n\t/* exp(ln_gamma(x)) == (x-1)! for integer x. */\n\tfor (x = 1; x <= 21; x++) {\n\t\tassert_true(double_eq_rel(exp(ln_gamma(x)),\n\t\t    (double)factorial(x-1), MAX_REL_ERR, MAX_ABS_ERR),\n\t\t    \"Incorrect factorial result for x=%u\", x);\n\t}\n}\nTEST_END\n\n/* Expected ln_gamma([0.0..100.0] increment=0.25). */\nstatic const double ln_gamma_misc_expected[] = {\n\tINFINITY,\n\t1.28802252469807743, 0.57236494292470008, 0.20328095143129538,\n\t0.00000000000000000, -0.09827183642181320, -0.12078223763524518,\n\t-0.08440112102048555, 0.00000000000000000, 0.12487171489239651,\n\t0.28468287047291918, 0.47521466691493719, 0.69314718055994529,\n\t0.93580193110872523, 1.20097360234707429, 1.48681557859341718,\n\t1.79175946922805496, 2.11445692745037128, 2.45373657084244234,\n\t2.80857141857573644, 3.17805383034794575, 3.56137591038669710,\n\t3.95781396761871651, 4.36671603662228680, 4.78749174278204581,\n\t5.21960398699022932, 5.66256205985714178, 6.11591589143154568,\n\t6.57925121201010121, 7.05218545073853953, 7.53436423675873268,\n\t8.02545839631598312, 8.52516136106541467, 9.03318691960512332,\n\t9.54926725730099690, 10.07315123968123949, 10.60460290274525086,\n\t11.14340011995171231, 11.68933342079726856, 12.24220494005076176,\n\t12.80182748008146909, 13.36802367147604720, 13.94062521940376342,\n\t14.51947222506051816, 15.10441257307551943, 15.69530137706046524,\n\t16.29200047656724237, 16.89437797963419285, 17.50230784587389010,\n\t18.11566950571089407, 18.73434751193644843, 19.35823122022435427,\n\t19.98721449566188468, 20.62119544270163018, 21.26007615624470048,\n\t21.90376249182879320, 22.55216385312342098, 23.20519299513386002,\n\t23.86276584168908954, 24.52480131594137802, 25.19122118273868338,\n\t25.86194990184851861, 26.53691449111561340, 27.21604439872720604,\n\t27.89927138384089389, 28.58652940490193828, 29.27775451504081516,\n\t29.97288476399884871, 30.67186010608067548, 31.37462231367769050,\n\t32.08111489594735843, 32.79128302226991565, 33.50507345013689076,\n\t34.22243445715505317, 34.94331577687681545, 35.66766853819134298,\n\t36.39544520803305261, 37.12659953718355865, 37.86108650896109395,\n\t38.59886229060776230, 39.33988418719949465, 40.08411059791735198,\n\t40.83150097453079752, 41.58201578195490100, 42.33561646075348506,\n\t43.09226539146988699, 43.85192586067515208, 44.61456202863158893,\n\t45.38013889847690052, 46.14862228684032885, 46.91997879580877395,\n\t47.69417578616628361, 48.47118135183522014, 49.25096429545256882,\n\t50.03349410501914463, 50.81874093156324790, 51.60667556776436982,\n\t52.39726942748592364, 53.19049452616926743, 53.98632346204390586,\n\t54.78472939811231157, 55.58568604486942633, 56.38916764371992940,\n\t57.19514895105859864, 58.00360522298051080, 58.81451220059079787,\n\t59.62784609588432261, 60.44358357816834371, 61.26170176100199427,\n\t62.08217818962842927, 62.90499082887649962, 63.73011805151035958,\n\t64.55753862700632340, 65.38723171073768015, 66.21917683354901385,\n\t67.05335389170279825, 67.88974313718154008, 68.72832516833013017,\n\t69.56908092082363737, 70.41199165894616385, 71.25703896716800045,\n\t72.10420474200799390, 72.95347118416940191, 73.80482079093779646,\n\t74.65823634883015814, 75.51370092648485866, 76.37119786778275454,\n\t77.23071078519033961, 78.09222355331530707, 78.95572030266725960,\n\t79.82118541361435859, 80.68860351052903468, 81.55795945611502873,\n\t82.42923834590904164, 83.30242550295004378, 84.17750647261028973,\n\t85.05446701758152983, 85.93329311301090456, 86.81397094178107920,\n\t87.69648688992882057, 88.58082754219766741, 89.46697967771913795,\n\t90.35493026581838194, 91.24466646193963015, 92.13617560368709292,\n\t93.02944520697742803, 93.92446296229978486, 94.82121673107967297,\n\t95.71969454214321615, 96.61988458827809723, 97.52177522288820910,\n\t98.42535495673848800, 99.33061245478741341, 100.23753653310367895,\n\t101.14611615586458981, 102.05634043243354370, 102.96819861451382394,\n\t103.88168009337621811, 104.79677439715833032, 105.71347118823287303,\n\t106.63176026064346047, 107.55163153760463501, 108.47307506906540198,\n\t109.39608102933323153, 110.32063971475740516, 111.24674154146920557,\n\t112.17437704317786995, 113.10353686902013237, 114.03421178146170689,\n\t114.96639265424990128, 115.90007047041454769, 116.83523632031698014,\n\t117.77188139974506953, 118.70999700805310795, 119.64957454634490830,\n\t120.59060551569974962, 121.53308151543865279, 122.47699424143097247,\n\t123.42233548443955726, 124.36909712850338394, 125.31727114935689826,\n\t126.26684961288492559, 127.21782467361175861, 128.17018857322420899,\n\t129.12393363912724453, 130.07905228303084755, 131.03553699956862033,\n\t131.99338036494577864, 132.95257503561629164, 133.91311374698926784,\n\t134.87498931216194364, 135.83819462068046846, 136.80272263732638294,\n\t137.76856640092901785, 138.73571902320256299, 139.70417368760718091,\n\t140.67392364823425055, 141.64496222871400732, 142.61728282114600574,\n\t143.59087888505104047, 144.56574394634486680, 145.54187159633210058,\n\t146.51925549072063859, 147.49788934865566148, 148.47776695177302031,\n\t149.45888214327129617, 150.44122882700193600, 151.42480096657754984,\n\t152.40959258449737490, 153.39559776128982094, 154.38281063467164245,\n\t155.37122539872302696, 156.36083630307879844, 157.35163765213474107,\n\t158.34362380426921391, 159.33678917107920370, 160.33112821663092973,\n\t161.32663545672428995, 162.32330545817117695, 163.32113283808695314,\n\t164.32011226319519892, 165.32023844914485267, 166.32150615984036790,\n\t167.32391020678358018, 168.32744544842768164, 169.33210678954270634,\n\t170.33788918059275375, 171.34478761712384198, 172.35279713916281707,\n\t173.36191283062726143, 174.37212981874515094, 175.38344327348534080,\n\t176.39584840699734514, 177.40934047306160437, 178.42391476654847793,\n\t179.43956662288721304, 180.45629141754378111, 181.47408456550741107,\n\t182.49294152078630304, 183.51285777591152737, 184.53382886144947861,\n\t185.55585034552262869, 186.57891783333786861, 187.60302696672312095,\n\t188.62817342367162610, 189.65435291789341932, 190.68156119837468054,\n\t191.70979404894376330, 192.73904728784492590, 193.76931676731820176,\n\t194.80059837318714244, 195.83288802445184729, 196.86618167288995096,\n\t197.90047530266301123, 198.93576492992946214, 199.97204660246373464,\n\t201.00931639928148797, 202.04757043027063901, 203.08680483582807597,\n\t204.12701578650228385, 205.16819948264117102, 206.21035215404597807,\n\t207.25347005962987623, 208.29754948708190909, 209.34258675253678916,\n\t210.38857820024875878, 211.43552020227099320, 212.48340915813977858,\n\t213.53224149456323744, 214.58201366511514152, 215.63272214993284592,\n\t216.68436345542014010, 217.73693411395422004, 218.79043068359703739,\n\t219.84484974781133815, 220.90018791517996988, 221.95644181913033322,\n\t223.01360811766215875, 224.07168349307951871, 225.13066465172661879,\n\t226.19054832372759734, 227.25133126272962159, 228.31301024565024704,\n\t229.37558207242807384, 230.43904356577689896, 231.50339157094342113,\n\t232.56862295546847008, 233.63473460895144740, 234.70172344281823484,\n\t235.76958639009222907, 236.83832040516844586, 237.90792246359117712,\n\t238.97838956183431947, 240.04971871708477238, 241.12190696702904802,\n\t242.19495136964280846, 243.26884900298270509, 244.34359696498191283,\n\t245.41919237324782443, 246.49563236486270057, 247.57291409618682110,\n\t248.65103474266476269, 249.72999149863338175, 250.80978157713354904,\n\t251.89040220972316320, 252.97185064629374551, 254.05412415488834199,\n\t255.13722002152300661, 256.22113555000953511, 257.30586806178126835,\n\t258.39141489572085675, 259.47777340799029844, 260.56494097186322279,\n\t261.65291497755913497, 262.74169283208021852, 263.83127195904967266,\n\t264.92164979855277807, 266.01282380697938379, 267.10479145686849733,\n\t268.19755023675537586, 269.29109765101975427, 270.38543121973674488,\n\t271.48054847852881721, 272.57644697842033565, 273.67312428569374561,\n\t274.77057798174683967, 275.86880566295326389, 276.96780494052313770,\n\t278.06757344036617496, 279.16810880295668085, 280.26940868320008349,\n\t281.37147075030043197, 282.47429268763045229, 283.57787219260217171,\n\t284.68220697654078322, 285.78729476455760050, 286.89313329542699194,\n\t287.99972032146268930, 289.10705360839756395, 290.21513093526289140,\n\t291.32395009427028754, 292.43350889069523646, 293.54380514276073200,\n\t294.65483668152336350, 295.76660135076059532, 296.87909700685889902,\n\t297.99232151870342022, 299.10627276756946458, 300.22094864701409733,\n\t301.33634706277030091, 302.45246593264130297, 303.56930318639643929,\n\t304.68685676566872189, 305.80512462385280514, 306.92410472600477078,\n\t308.04379504874236773, 309.16419358014690033, 310.28529831966631036,\n\t311.40710727801865687, 312.52961847709792664, 313.65282994987899201,\n\t314.77673974032603610, 315.90134590329950015, 317.02664650446632777,\n\t318.15263962020929966, 319.27932333753892635, 320.40669575400545455,\n\t321.53475497761127144, 322.66349912672620803, 323.79292633000159185,\n\t324.92303472628691452, 326.05382246454587403, 327.18528770377525916,\n\t328.31742861292224234, 329.45024337080525356, 330.58373016603343331,\n\t331.71788719692847280, 332.85271267144611329, 333.98820480709991898,\n\t335.12436183088397001, 336.26118197919845443, 337.39866349777429377,\n\t338.53680464159958774, 339.67560367484657036, 340.81505887079896411,\n\t341.95516851178109619, 343.09593088908627578, 344.23734430290727460,\n\t345.37940706226686416, 346.52211748494903532, 347.66547389743118401,\n\t348.80947463481720661, 349.95411804077025408, 351.09940246744753267,\n\t352.24532627543504759, 353.39188783368263103, 354.53908551944078908,\n\t355.68691771819692349, 356.83538282361303118, 357.98447923746385868,\n\t359.13420536957539753\n};\n\nTEST_BEGIN(test_ln_gamma_misc) {\n\tunsigned i;\n\n\tfor (i = 1; i < sizeof(ln_gamma_misc_expected)/sizeof(double); i++) {\n\t\tdouble x = (double)i * 0.25;\n\t\tassert_true(double_eq_rel(ln_gamma(x),\n\t\t    ln_gamma_misc_expected[i], MAX_REL_ERR, MAX_ABS_ERR),\n\t\t    \"Incorrect ln_gamma result for i=%u\", i);\n\t}\n}\nTEST_END\n\n/* Expected pt_norm([0.01..0.99] increment=0.01). */\nstatic const double pt_norm_expected[] = {\n\t-INFINITY,\n\t-2.32634787404084076, -2.05374891063182252, -1.88079360815125085,\n\t-1.75068607125216946, -1.64485362695147264, -1.55477359459685305,\n\t-1.47579102817917063, -1.40507156030963221, -1.34075503369021654,\n\t-1.28155156554460081, -1.22652812003661049, -1.17498679206608991,\n\t-1.12639112903880045, -1.08031934081495606, -1.03643338949378938,\n\t-0.99445788320975281, -0.95416525314619416, -0.91536508784281390,\n\t-0.87789629505122846, -0.84162123357291418, -0.80642124701824025,\n\t-0.77219321418868492, -0.73884684918521371, -0.70630256284008752,\n\t-0.67448975019608171, -0.64334540539291685, -0.61281299101662701,\n\t-0.58284150727121620, -0.55338471955567281, -0.52440051270804067,\n\t-0.49585034734745320, -0.46769879911450812, -0.43991316567323380,\n\t-0.41246312944140462, -0.38532046640756751, -0.35845879325119373,\n\t-0.33185334643681652, -0.30548078809939738, -0.27931903444745404,\n\t-0.25334710313579978, -0.22754497664114931, -0.20189347914185077,\n\t-0.17637416478086135, -0.15096921549677725, -0.12566134685507399,\n\t-0.10043372051146975, -0.07526986209982976, -0.05015358346473352,\n\t-0.02506890825871106, 0.00000000000000000, 0.02506890825871106,\n\t0.05015358346473366, 0.07526986209982990, 0.10043372051146990,\n\t0.12566134685507413, 0.15096921549677739, 0.17637416478086146,\n\t0.20189347914185105, 0.22754497664114931, 0.25334710313579978,\n\t0.27931903444745404, 0.30548078809939738, 0.33185334643681652,\n\t0.35845879325119373, 0.38532046640756762, 0.41246312944140484,\n\t0.43991316567323391, 0.46769879911450835, 0.49585034734745348,\n\t0.52440051270804111, 0.55338471955567303, 0.58284150727121620,\n\t0.61281299101662701, 0.64334540539291685, 0.67448975019608171,\n\t0.70630256284008752, 0.73884684918521371, 0.77219321418868492,\n\t0.80642124701824036, 0.84162123357291441, 0.87789629505122879,\n\t0.91536508784281423, 0.95416525314619460, 0.99445788320975348,\n\t1.03643338949378938, 1.08031934081495606, 1.12639112903880045,\n\t1.17498679206608991, 1.22652812003661049, 1.28155156554460081,\n\t1.34075503369021654, 1.40507156030963265, 1.47579102817917085,\n\t1.55477359459685394, 1.64485362695147308, 1.75068607125217102,\n\t1.88079360815125041, 2.05374891063182208, 2.32634787404084076\n};\n\nTEST_BEGIN(test_pt_norm) {\n\tunsigned i;\n\n\tfor (i = 1; i < sizeof(pt_norm_expected)/sizeof(double); i++) {\n\t\tdouble p = (double)i * 0.01;\n\t\tassert_true(double_eq_rel(pt_norm(p), pt_norm_expected[i],\n\t\t    MAX_REL_ERR, MAX_ABS_ERR),\n\t\t    \"Incorrect pt_norm result for i=%u\", i);\n\t}\n}\nTEST_END\n\n/*\n * Expected pt_chi2(p=[0.01..0.99] increment=0.07,\n *                  df={0.1, 1.1, 10.1, 100.1, 1000.1}).\n */\nstatic const double pt_chi2_df[] = {0.1, 1.1, 10.1, 100.1, 1000.1};\nstatic const double pt_chi2_expected[] = {\n\t1.168926411457320e-40, 1.347680397072034e-22, 3.886980416666260e-17,\n\t8.245951724356564e-14, 2.068936347497604e-11, 1.562561743309233e-09,\n\t5.459543043426564e-08, 1.114775688149252e-06, 1.532101202364371e-05,\n\t1.553884683726585e-04, 1.239396954915939e-03, 8.153872320255721e-03,\n\t4.631183739647523e-02, 2.473187311701327e-01, 2.175254800183617e+00,\n\n\t0.0003729887888876379, 0.0164409238228929513, 0.0521523015190650113,\n\t0.1064701372271216612, 0.1800913735793082115, 0.2748704281195626931,\n\t0.3939246282787986497, 0.5420727552260817816, 0.7267265822221973259,\n\t0.9596554296000253670, 1.2607440376386165326, 1.6671185084541604304,\n\t2.2604828984738705167, 3.2868613342148607082, 6.9298574921692139839,\n\n\t2.606673548632508, 4.602913725294877, 5.646152813924212,\n\t6.488971315540869, 7.249823275816285, 7.977314231410841,\n\t8.700354939944047, 9.441728024225892, 10.224338321374127,\n\t11.076435368801061, 12.039320937038386, 13.183878752697167,\n\t14.657791935084575, 16.885728216339373, 23.361991680031817,\n\n\t70.14844087392152, 80.92379498849355, 85.53325420085891,\n\t88.94433120715347, 91.83732712857017, 94.46719943606301,\n\t96.96896479994635, 99.43412843510363, 101.94074719829733,\n\t104.57228644307247, 107.43900093448734, 110.71844673417287,\n\t114.76616819871325, 120.57422505959563, 135.92318818757556,\n\n\t899.0072447849649, 937.9271278858220, 953.8117189560207,\n\t965.3079371501154, 974.8974061207954, 983.4936235182347,\n\t991.5691170518946, 999.4334123954690, 1007.3391826856553,\n\t1015.5445154999951, 1024.3777075619569, 1034.3538789836223,\n\t1046.4872561869577, 1063.5717461999654, 1107.0741966053859\n};\n\nTEST_BEGIN(test_pt_chi2) {\n\tunsigned i, j;\n\tunsigned e = 0;\n\n\tfor (i = 0; i < sizeof(pt_chi2_df)/sizeof(double); i++) {\n\t\tdouble df = pt_chi2_df[i];\n\t\tdouble ln_gamma_df = ln_gamma(df * 0.5);\n\t\tfor (j = 1; j < 100; j += 7) {\n\t\t\tdouble p = (double)j * 0.01;\n\t\t\tassert_true(double_eq_rel(pt_chi2(p, df, ln_gamma_df),\n\t\t\t    pt_chi2_expected[e], MAX_REL_ERR, MAX_ABS_ERR),\n\t\t\t    \"Incorrect pt_chi2 result for i=%u, j=%u\", i, j);\n\t\t\te++;\n\t\t}\n\t}\n}\nTEST_END\n\n/*\n * Expected pt_gamma(p=[0.1..0.99] increment=0.07,\n *                   shape=[0.5..3.0] increment=0.5).\n */\nstatic const double pt_gamma_shape[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0};\nstatic const double pt_gamma_expected[] = {\n\t7.854392895485103e-05, 5.043466107888016e-03, 1.788288957794883e-02,\n\t3.900956150232906e-02, 6.913847560638034e-02, 1.093710833465766e-01,\n\t1.613412523825817e-01, 2.274682115597864e-01, 3.114117323127083e-01,\n\t4.189466220207417e-01, 5.598106789059246e-01, 7.521856146202706e-01,\n\t1.036125427911119e+00, 1.532450860038180e+00, 3.317448300510606e+00,\n\n\t0.01005033585350144, 0.08338160893905107, 0.16251892949777497,\n\t0.24846135929849966, 0.34249030894677596, 0.44628710262841947,\n\t0.56211891815354142, 0.69314718055994529, 0.84397007029452920,\n\t1.02165124753198167, 1.23787435600161766, 1.51412773262977574,\n\t1.89711998488588196, 2.52572864430825783, 4.60517018598809091,\n\n\t0.05741590094955853, 0.24747378084860744, 0.39888572212236084,\n\t0.54394139997444901, 0.69048812513915159, 0.84311389861296104,\n\t1.00580622221479898, 1.18298694218766931, 1.38038096305861213,\n\t1.60627736383027453, 1.87396970522337947, 2.20749220408081070,\n\t2.65852391865854942, 3.37934630984842244, 5.67243336507218476,\n\n\t0.1485547402532659, 0.4657458011640391, 0.6832386130709406,\n\t0.8794297834672100, 1.0700752852474524, 1.2629614217350744,\n\t1.4638400448580779, 1.6783469900166610, 1.9132338090606940,\n\t2.1778589228618777, 2.4868823970010991, 2.8664695666264195,\n\t3.3724415436062114, 4.1682658512758071, 6.6383520679938108,\n\n\t0.2771490383641385, 0.7195001279643727, 0.9969081732265243,\n\t1.2383497880608061, 1.4675206597269927, 1.6953064251816552,\n\t1.9291243435606809, 2.1757300955477641, 2.4428032131216391,\n\t2.7406534569230616, 3.0851445039665513, 3.5043101122033367,\n\t4.0575997065264637, 4.9182956424675286, 7.5431362346944937,\n\n\t0.4360451650782932, 0.9983600902486267, 1.3306365880734528,\n\t1.6129750834753802, 1.8767241606994294, 2.1357032436097660,\n\t2.3988853336865565, 2.6740603137235603, 2.9697561737517959,\n\t3.2971457713883265, 3.6731795898504660, 4.1275751617770631,\n\t4.7230515633946677, 5.6417477865306020, 8.4059469148854635\n};\n\nTEST_BEGIN(test_pt_gamma_shape) {\n\tunsigned i, j;\n\tunsigned e = 0;\n\n\tfor (i = 0; i < sizeof(pt_gamma_shape)/sizeof(double); i++) {\n\t\tdouble shape = pt_gamma_shape[i];\n\t\tdouble ln_gamma_shape = ln_gamma(shape);\n\t\tfor (j = 1; j < 100; j += 7) {\n\t\t\tdouble p = (double)j * 0.01;\n\t\t\tassert_true(double_eq_rel(pt_gamma(p, shape, 1.0,\n\t\t\t    ln_gamma_shape), pt_gamma_expected[e], MAX_REL_ERR,\n\t\t\t    MAX_ABS_ERR),\n\t\t\t    \"Incorrect pt_gamma result for i=%u, j=%u\", i, j);\n\t\t\te++;\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_pt_gamma_scale) {\n\tdouble shape = 1.0;\n\tdouble ln_gamma_shape = ln_gamma(shape);\n\n\tassert_true(double_eq_rel(\n\t    pt_gamma(0.5, shape, 1.0, ln_gamma_shape) * 10.0,\n\t    pt_gamma(0.5, shape, 10.0, ln_gamma_shape), MAX_REL_ERR,\n\t    MAX_ABS_ERR),\n\t    \"Scale should be trivially equivalent to external multiplication\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ln_gamma_factorial,\n\t    test_ln_gamma_misc,\n\t    test_pt_norm,\n\t    test_pt_chi2,\n\t    test_pt_gamma_shape,\n\t    test_pt_gamma_scale);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/mq.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NSENDERS\t3\n#define NMSGS\t\t100000\n\ntypedef struct mq_msg_s mq_msg_t;\nstruct mq_msg_s {\n\tmq_msg(mq_msg_t)\tlink;\n};\nmq_gen(static, mq_, mq_t, mq_msg_t, link)\n\nTEST_BEGIN(test_mq_basic) {\n\tmq_t mq;\n\tmq_msg_t msg;\n\n\tassert_false(mq_init(&mq), \"Unexpected mq_init() failure\");\n\tassert_u_eq(mq_count(&mq), 0, \"mq should be empty\");\n\tassert_ptr_null(mq_tryget(&mq),\n\t    \"mq_tryget() should fail when the queue is empty\");\n\n\tmq_put(&mq, &msg);\n\tassert_u_eq(mq_count(&mq), 1, \"mq should contain one message\");\n\tassert_ptr_eq(mq_tryget(&mq), &msg, \"mq_tryget() should return msg\");\n\n\tmq_put(&mq, &msg);\n\tassert_ptr_eq(mq_get(&mq), &msg, \"mq_get() should return msg\");\n\n\tmq_fini(&mq);\n}\nTEST_END\n\nstatic void *\nthd_receiver_start(void *arg) {\n\tmq_t *mq = (mq_t *)arg;\n\tunsigned i;\n\n\tfor (i = 0; i < (NSENDERS * NMSGS); i++) {\n\t\tmq_msg_t *msg = mq_get(mq);\n\t\tassert_ptr_not_null(msg, \"mq_get() should never return NULL\");\n\t\tdallocx(msg, 0);\n\t}\n\treturn NULL;\n}\n\nstatic void *\nthd_sender_start(void *arg) {\n\tmq_t *mq = (mq_t *)arg;\n\tunsigned i;\n\n\tfor (i = 0; i < NMSGS; i++) {\n\t\tmq_msg_t *msg;\n\t\tvoid *p;\n\t\tp = mallocx(sizeof(mq_msg_t), 0);\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\tmsg = (mq_msg_t *)p;\n\t\tmq_put(mq, msg);\n\t}\n\treturn NULL;\n}\n\nTEST_BEGIN(test_mq_threaded) {\n\tmq_t mq;\n\tthd_t receiver;\n\tthd_t senders[NSENDERS];\n\tunsigned i;\n\n\tassert_false(mq_init(&mq), \"Unexpected mq_init() failure\");\n\n\tthd_create(&receiver, thd_receiver_start, (void *)&mq);\n\tfor (i = 0; i < NSENDERS; i++) {\n\t\tthd_create(&senders[i], thd_sender_start, (void *)&mq);\n\t}\n\n\tthd_join(receiver, NULL);\n\tfor (i = 0; i < NSENDERS; i++) {\n\t\tthd_join(senders[i], NULL);\n\t}\n\n\tmq_fini(&mq);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_mq_basic,\n\t    test_mq_threaded);\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/mtx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS\t2\n#define NINCRS\t\t2000000\n\nTEST_BEGIN(test_mtx_basic) {\n\tmtx_t mtx;\n\n\tassert_false(mtx_init(&mtx), \"Unexpected mtx_init() failure\");\n\tmtx_lock(&mtx);\n\tmtx_unlock(&mtx);\n\tmtx_fini(&mtx);\n}\nTEST_END\n\ntypedef struct {\n\tmtx_t\t\tmtx;\n\tunsigned\tx;\n} thd_start_arg_t;\n\nstatic void *\nthd_start(void *varg) {\n\tthd_start_arg_t *arg = (thd_start_arg_t *)varg;\n\tunsigned i;\n\n\tfor (i = 0; i < NINCRS; i++) {\n\t\tmtx_lock(&arg->mtx);\n\t\targ->x++;\n\t\tmtx_unlock(&arg->mtx);\n\t}\n\treturn NULL;\n}\n\nTEST_BEGIN(test_mtx_race) {\n\tthd_start_arg_t arg;\n\tthd_t thds[NTHREADS];\n\tunsigned i;\n\n\tassert_false(mtx_init(&arg.mtx), \"Unexpected mtx_init() failure\");\n\targ.x = 0;\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_create(&thds[i], thd_start, (void *)&arg);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n\tassert_u_eq(arg.x, NTHREADS * NINCRS,\n\t    \"Race-related counter corruption\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_mtx_basic,\n\t    test_mtx_race);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/nstime.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define BILLION\tUINT64_C(1000000000)\n\nTEST_BEGIN(test_nstime_init) {\n\tnstime_t nst;\n\n\tnstime_init(&nst, 42000000043);\n\tassert_u64_eq(nstime_ns(&nst), 42000000043, \"ns incorrectly read\");\n\tassert_u64_eq(nstime_sec(&nst), 42, \"sec incorrectly read\");\n\tassert_u64_eq(nstime_nsec(&nst), 43, \"nsec incorrectly read\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_init2) {\n\tnstime_t nst;\n\n\tnstime_init2(&nst, 42, 43);\n\tassert_u64_eq(nstime_sec(&nst), 42, \"sec incorrectly read\");\n\tassert_u64_eq(nstime_nsec(&nst), 43, \"nsec incorrectly read\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_copy) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_init(&nstb, 0);\n\tnstime_copy(&nstb, &nsta);\n\tassert_u64_eq(nstime_sec(&nstb), 42, \"sec incorrectly copied\");\n\tassert_u64_eq(nstime_nsec(&nstb), 43, \"nsec incorrectly copied\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_compare) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0, \"Times should be equal\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), 0, \"Times should be equal\");\n\n\tnstime_init2(&nstb, 42, 42);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 1,\n\t    \"nsta should be greater than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), -1,\n\t    \"nstb should be less than nsta\");\n\n\tnstime_init2(&nstb, 42, 44);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), -1,\n\t    \"nsta should be less than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), 1,\n\t    \"nstb should be greater than nsta\");\n\n\tnstime_init2(&nstb, 41, BILLION - 1);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 1,\n\t    \"nsta should be greater than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), -1,\n\t    \"nstb should be less than nsta\");\n\n\tnstime_init2(&nstb, 43, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), -1,\n\t    \"nsta should be less than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), 1,\n\t    \"nstb should be greater than nsta\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_add) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_add(&nsta, &nstb);\n\tnstime_init2(&nstb, 84, 86);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n\n\tnstime_init2(&nsta, 42, BILLION - 1);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_add(&nsta, &nstb);\n\tnstime_init2(&nstb, 85, BILLION - 2);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_iadd) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, BILLION - 1);\n\tnstime_iadd(&nsta, 1);\n\tnstime_init2(&nstb, 43, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n\n\tnstime_init2(&nsta, 42, 1);\n\tnstime_iadd(&nsta, BILLION + 1);\n\tnstime_init2(&nstb, 43, 2);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_subtract) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_subtract(&nsta, &nstb);\n\tnstime_init(&nstb, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_init2(&nstb, 41, 44);\n\tnstime_subtract(&nsta, &nstb);\n\tnstime_init2(&nstb, 0, BILLION - 1);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_isubtract) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_isubtract(&nsta, 42*BILLION + 43);\n\tnstime_init(&nstb, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_isubtract(&nsta, 41*BILLION + 44);\n\tnstime_init2(&nstb, 0, BILLION - 1);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_imultiply) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_init2(&nstb, 420, 430);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect multiplication result\");\n\n\tnstime_init2(&nsta, 42, 666666666);\n\tnstime_imultiply(&nsta, 3);\n\tnstime_init2(&nstb, 127, 999999998);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect multiplication result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_idivide) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_idivide(&nsta, 10);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect division result\");\n\n\tnstime_init2(&nsta, 42, 666666666);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 3);\n\tnstime_idivide(&nsta, 3);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect division result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_divide) {\n\tnstime_t nsta, nstb, nstc;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tassert_u64_eq(nstime_divide(&nsta, &nstb), 10,\n\t    \"Incorrect division result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_init(&nstc, 1);\n\tnstime_add(&nsta, &nstc);\n\tassert_u64_eq(nstime_divide(&nsta, &nstb), 10,\n\t    \"Incorrect division result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_init(&nstc, 1);\n\tnstime_subtract(&nsta, &nstc);\n\tassert_u64_eq(nstime_divide(&nsta, &nstb), 9,\n\t    \"Incorrect division result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_monotonic) {\n\tnstime_monotonic();\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_update) {\n\tnstime_t nst;\n\n\tnstime_init(&nst, 0);\n\n\tassert_false(nstime_update(&nst), \"Basic time update failed.\");\n\n\t/* Only Rip Van Winkle sleeps this long. */\n\t{\n\t\tnstime_t addend;\n\t\tnstime_init2(&addend, 631152000, 0);\n\t\tnstime_add(&nst, &addend);\n\t}\n\t{\n\t\tnstime_t nst0;\n\t\tnstime_copy(&nst0, &nst);\n\t\tassert_true(nstime_update(&nst),\n\t\t    \"Update should detect time roll-back.\");\n\t\tassert_d_eq(nstime_compare(&nst, &nst0), 0,\n\t\t    \"Time should not have been modified\");\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_nstime_init,\n\t    test_nstime_init2,\n\t    test_nstime_copy,\n\t    test_nstime_compare,\n\t    test_nstime_add,\n\t    test_nstime_iadd,\n\t    test_nstime_subtract,\n\t    test_nstime_isubtract,\n\t    test_nstime_imultiply,\n\t    test_nstime_idivide,\n\t    test_nstime_divide,\n\t    test_nstime_monotonic,\n\t    test_nstime_update);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/pack.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * Size class that is a divisor of the page size, ideally 4+ regions per run.\n */\n#if LG_PAGE <= 14\n#define SZ\t(ZU(1) << (LG_PAGE - 2))\n#else\n#define SZ\tZU(4096)\n#endif\n\n/*\n * Number of slabs to consume at high water mark.  Should be at least 2 so that\n * if mmap()ed memory grows downward, downward growth of mmap()ed memory is\n * tested.\n */\n#define NSLABS\t8\n\nstatic unsigned\nbinind_compute(void) {\n\tsize_t sz;\n\tunsigned nbins, i;\n\n\tsz = sizeof(nbins);\n\tassert_d_eq(mallctl(\"arenas.nbins\", (void *)&nbins, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl failure\");\n\n\tfor (i = 0; i < nbins; i++) {\n\t\tsize_t mib[4];\n\t\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\t\tsize_t size;\n\n\t\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.size\", mib,\n\t\t    &miblen), 0, \"Unexpected mallctlnametomb failure\");\n\t\tmib[2] = (size_t)i;\n\n\t\tsz = sizeof(size);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctlbymib failure\");\n\t\tif (size == SZ) {\n\t\t\treturn i;\n\t\t}\n\t}\n\n\ttest_fail(\"Unable to compute nregs_per_run\");\n\treturn 0;\n}\n\nstatic size_t\nnregs_per_run_compute(void) {\n\tuint32_t nregs;\n\tsize_t sz;\n\tunsigned binind = binind_compute();\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.nregs\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomb failure\");\n\tmib[2] = (size_t)binind;\n\tsz = sizeof(nregs);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&nregs, &sz, NULL,\n\t    0), 0, \"Unexpected mallctlbymib failure\");\n\treturn nregs;\n}\n\nstatic unsigned\narenas_create_mallctl(void) {\n\tunsigned arena_ind;\n\tsize_t sz;\n\n\tsz = sizeof(arena_ind);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Error in arenas.create\");\n\n\treturn arena_ind;\n}\n\nstatic void\narena_reset_mallctl(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.reset\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nTEST_BEGIN(test_pack) {\n\tbool prof_enabled;\n\tsize_t sz = sizeof(prof_enabled);\n\tif (mallctl(\"opt.prof\", (void *)&prof_enabled, &sz, NULL, 0) == 0) {\n\t\ttest_skip_if(prof_enabled);\n\t}\n\n\tunsigned arena_ind = arenas_create_mallctl();\n\tsize_t nregs_per_run = nregs_per_run_compute();\n\tsize_t nregs = nregs_per_run * NSLABS;\n\tVARIABLE_ARRAY(void *, ptrs, nregs);\n\tsize_t i, j, offset;\n\n\t/* Fill matrix. */\n\tfor (i = offset = 0; i < NSLABS; i++) {\n\t\tfor (j = 0; j < nregs_per_run; j++) {\n\t\t\tvoid *p = mallocx(SZ, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected mallocx(%zu, MALLOCX_ARENA(%u) |\"\n\t\t\t    \" MALLOCX_TCACHE_NONE) failure, run=%zu, reg=%zu\",\n\t\t\t    SZ, arena_ind, i, j);\n\t\t\tptrs[(i * nregs_per_run) + j] = p;\n\t\t}\n\t}\n\n\t/*\n\t * Free all but one region of each run, but rotate which region is\n\t * preserved, so that subsequent allocations exercise the within-run\n\t * layout policy.\n\t */\n\toffset = 0;\n\tfor (i = offset = 0;\n\t    i < NSLABS;\n\t    i++, offset = (offset + 1) % nregs_per_run) {\n\t\tfor (j = 0; j < nregs_per_run; j++) {\n\t\t\tvoid *p = ptrs[(i * nregs_per_run) + j];\n\t\t\tif (offset == j) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tdallocx(p, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE);\n\t\t}\n\t}\n\n\t/*\n\t * Logically refill matrix, skipping preserved regions and verifying\n\t * that the matrix is unmodified.\n\t */\n\toffset = 0;\n\tfor (i = offset = 0;\n\t    i < NSLABS;\n\t    i++, offset = (offset + 1) % nregs_per_run) {\n\t\tfor (j = 0; j < nregs_per_run; j++) {\n\t\t\tvoid *p;\n\n\t\t\tif (offset == j) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tp = mallocx(SZ, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_eq(p, ptrs[(i * nregs_per_run) + j],\n\t\t\t    \"Unexpected refill discrepancy, run=%zu, reg=%zu\\n\",\n\t\t\t    i, j);\n\t\t}\n\t}\n\n\t/* Clean up. */\n\tarena_reset_mallctl(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_pack);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/pack.sh",
    "content": "#!/bin/sh\n\n# Immediately purge to minimize fragmentation.\nexport MALLOC_CONF=\"dirty_decay_ms:0,muzzy_decay_ms:0\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/pages.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_pages_huge) {\n\tsize_t alloc_size;\n\tbool commit;\n\tvoid *pages, *hugepage;\n\n\talloc_size = HUGEPAGE * 2 - PAGE;\n\tcommit = true;\n\tpages = pages_map(NULL, alloc_size, PAGE, &commit);\n\tassert_ptr_not_null(pages, \"Unexpected pages_map() error\");\n\n\tif (init_system_thp_mode == thp_mode_default) {\n\t    hugepage = (void *)(ALIGNMENT_CEILING((uintptr_t)pages, HUGEPAGE));\n\t    assert_b_ne(pages_huge(hugepage, HUGEPAGE), have_madvise_huge,\n\t        \"Unexpected pages_huge() result\");\n\t    assert_false(pages_nohuge(hugepage, HUGEPAGE),\n\t        \"Unexpected pages_nohuge() result\");\n\t}\n\n\tpages_unmap(pages, alloc_size);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_pages_huge);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/ph.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ph.h\"\n\ntypedef struct node_s node_t;\n\nstruct node_s {\n#define NODE_MAGIC 0x9823af7e\n\tuint32_t magic;\n\tphn(node_t) link;\n\tuint64_t key;\n};\n\nstatic int\nnode_cmp(const node_t *a, const node_t *b) {\n\tint ret;\n\n\tret = (a->key > b->key) - (a->key < b->key);\n\tif (ret == 0) {\n\t\t/*\n\t\t * Duplicates are not allowed in the heap, so force an\n\t\t * arbitrary ordering for non-identical items with equal keys.\n\t\t */\n\t\tret = (((uintptr_t)a) > ((uintptr_t)b))\n\t\t    - (((uintptr_t)a) < ((uintptr_t)b));\n\t}\n\treturn ret;\n}\n\nstatic int\nnode_cmp_magic(const node_t *a, const node_t *b) {\n\n\tassert_u32_eq(a->magic, NODE_MAGIC, \"Bad magic\");\n\tassert_u32_eq(b->magic, NODE_MAGIC, \"Bad magic\");\n\n\treturn node_cmp(a, b);\n}\n\ntypedef ph(node_t) heap_t;\nph_gen(static, heap_, heap_t, node_t, link, node_cmp_magic);\n\nstatic void\nnode_print(const node_t *node, unsigned depth) {\n\tunsigned i;\n\tnode_t *leftmost_child, *sibling;\n\n\tfor (i = 0; i < depth; i++) {\n\t\tmalloc_printf(\"\\t\");\n\t}\n\tmalloc_printf(\"%2\"FMTu64\"\\n\", node->key);\n\n\tleftmost_child = phn_lchild_get(node_t, link, node);\n\tif (leftmost_child == NULL) {\n\t\treturn;\n\t}\n\tnode_print(leftmost_child, depth + 1);\n\n\tfor (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=\n\t    NULL; sibling = phn_next_get(node_t, link, sibling)) {\n\t\tnode_print(sibling, depth + 1);\n\t}\n}\n\nstatic void\nheap_print(const heap_t *heap) {\n\tnode_t *auxelm;\n\n\tmalloc_printf(\"vvv heap %p vvv\\n\", heap);\n\tif (heap->ph_root == NULL) {\n\t\tgoto label_return;\n\t}\n\n\tnode_print(heap->ph_root, 0);\n\n\tfor (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;\n\t    auxelm = phn_next_get(node_t, link, auxelm)) {\n\t\tassert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,\n\t\t    link, auxelm)), auxelm,\n\t\t    \"auxelm's prev doesn't link to auxelm\");\n\t\tnode_print(auxelm, 0);\n\t}\n\nlabel_return:\n\tmalloc_printf(\"^^^ heap %p ^^^\\n\", heap);\n}\n\nstatic unsigned\nnode_validate(const node_t *node, const node_t *parent) {\n\tunsigned nnodes = 1;\n\tnode_t *leftmost_child, *sibling;\n\n\tif (parent != NULL) {\n\t\tassert_d_ge(node_cmp_magic(node, parent), 0,\n\t\t    \"Child is less than parent\");\n\t}\n\n\tleftmost_child = phn_lchild_get(node_t, link, node);\n\tif (leftmost_child == NULL) {\n\t\treturn nnodes;\n\t}\n\tassert_ptr_eq((void *)phn_prev_get(node_t, link, leftmost_child),\n\t    (void *)node, \"Leftmost child does not link to node\");\n\tnnodes += node_validate(leftmost_child, node);\n\n\tfor (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=\n\t    NULL; sibling = phn_next_get(node_t, link, sibling)) {\n\t\tassert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,\n\t\t    link, sibling)), sibling,\n\t\t    \"sibling's prev doesn't link to sibling\");\n\t\tnnodes += node_validate(sibling, node);\n\t}\n\treturn nnodes;\n}\n\nstatic unsigned\nheap_validate(const heap_t *heap) {\n\tunsigned nnodes = 0;\n\tnode_t *auxelm;\n\n\tif (heap->ph_root == NULL) {\n\t\tgoto label_return;\n\t}\n\n\tnnodes += node_validate(heap->ph_root, NULL);\n\n\tfor (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;\n\t    auxelm = phn_next_get(node_t, link, auxelm)) {\n\t\tassert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,\n\t\t    link, auxelm)), auxelm,\n\t\t    \"auxelm's prev doesn't link to auxelm\");\n\t\tnnodes += node_validate(auxelm, NULL);\n\t}\n\nlabel_return:\n\tif (false) {\n\t\theap_print(heap);\n\t}\n\treturn nnodes;\n}\n\nTEST_BEGIN(test_ph_empty) {\n\theap_t heap;\n\n\theap_new(&heap);\n\tassert_true(heap_empty(&heap), \"Heap should be empty\");\n\tassert_ptr_null(heap_first(&heap), \"Unexpected node\");\n\tassert_ptr_null(heap_any(&heap), \"Unexpected node\");\n}\nTEST_END\n\nstatic void\nnode_remove(heap_t *heap, node_t *node) {\n\theap_remove(heap, node);\n\n\tnode->magic = 0;\n}\n\nstatic node_t *\nnode_remove_first(heap_t *heap) {\n\tnode_t *node = heap_remove_first(heap);\n\tnode->magic = 0;\n\treturn node;\n}\n\nstatic node_t *\nnode_remove_any(heap_t *heap) {\n\tnode_t *node = heap_remove_any(heap);\n\tnode->magic = 0;\n\treturn node;\n}\n\nTEST_BEGIN(test_ph_random) {\n#define NNODES 25\n#define NBAGS 250\n#define SEED 42\n\tsfmt_t *sfmt;\n\tuint64_t bag[NNODES];\n\theap_t heap;\n\tnode_t nodes[NNODES];\n\tunsigned i, j, k;\n\n\tsfmt = init_gen_rand(SEED);\n\tfor (i = 0; i < NBAGS; i++) {\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\t/* Insert in order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = j;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t/* Insert in reverse order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = NNODES - j - 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = gen_rand64_range(sfmt, NNODES);\n\t\t\t}\n\t\t}\n\n\t\tfor (j = 1; j <= NNODES; j++) {\n\t\t\t/* Initialize heap and nodes. */\n\t\t\theap_new(&heap);\n\t\t\tassert_u_eq(heap_validate(&heap), 0,\n\t\t\t    \"Incorrect node count\");\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\tnodes[k].magic = NODE_MAGIC;\n\t\t\t\tnodes[k].key = bag[k];\n\t\t\t}\n\n\t\t\t/* Insert nodes. */\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\theap_insert(&heap, &nodes[k]);\n\t\t\t\tif (i % 13 == 12) {\n\t\t\t\t\tassert_ptr_not_null(heap_any(&heap),\n\t\t\t\t\t    \"Heap should not be empty\");\n\t\t\t\t\t/* Trigger merging. */\n\t\t\t\t\tassert_ptr_not_null(heap_first(&heap),\n\t\t\t\t\t    \"Heap should not be empty\");\n\t\t\t\t}\n\t\t\t\tassert_u_eq(heap_validate(&heap), k + 1,\n\t\t\t\t    \"Incorrect node count\");\n\t\t\t}\n\n\t\t\tassert_false(heap_empty(&heap),\n\t\t\t    \"Heap should not be empty\");\n\n\t\t\t/* Remove nodes. */\n\t\t\tswitch (i % 6) {\n\t\t\tcase 0:\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t\tnode_remove(&heap, &nodes[k]);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tfor (k = j; k > 0; k--) {\n\t\t\t\t\tnode_remove(&heap, &nodes[k-1]);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), k - 1,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2: {\n\t\t\t\tnode_t *prev = NULL;\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_t *node = node_remove_first(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t\tif (prev != NULL) {\n\t\t\t\t\t\tassert_d_ge(node_cmp(node,\n\t\t\t\t\t\t    prev), 0,\n\t\t\t\t\t\t    \"Bad removal order\");\n\t\t\t\t\t}\n\t\t\t\t\tprev = node;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} case 3: {\n\t\t\t\tnode_t *prev = NULL;\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_t *node = heap_first(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t\tif (prev != NULL) {\n\t\t\t\t\t\tassert_d_ge(node_cmp(node,\n\t\t\t\t\t\t    prev), 0,\n\t\t\t\t\t\t    \"Bad removal order\");\n\t\t\t\t\t}\n\t\t\t\t\tnode_remove(&heap, node);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t\tprev = node;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} case 4: {\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_remove_any(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} case 5: {\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_t *node = heap_any(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t\tnode_remove(&heap, node);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} default:\n\t\t\t\tnot_reached();\n\t\t\t}\n\n\t\t\tassert_ptr_null(heap_first(&heap),\n\t\t\t    \"Heap should be empty\");\n\t\t\tassert_ptr_null(heap_any(&heap),\n\t\t\t    \"Heap should be empty\");\n\t\t\tassert_true(heap_empty(&heap), \"Heap should be empty\");\n\t\t}\n\t}\n\tfini_gen_rand(sfmt);\n#undef NNODES\n#undef SEED\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ph_empty,\n\t    test_ph_random);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prng.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\ntest_prng_lg_range_u32(bool atomic) {\n\tatomic_u32_t sa, sb;\n\tuint32_t ra, rb;\n\tunsigned lg_range;\n\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_u32(&sa, 32, atomic);\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_u32(&sa, 32, atomic);\n\tassert_u32_eq(ra, rb,\n\t    \"Repeated generation should produce repeated results\");\n\n\tatomic_store_u32(&sb, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_u32(&sb, 32, atomic);\n\tassert_u32_eq(ra, rb,\n\t    \"Equivalent generation should produce equivalent results\");\n\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_u32(&sa, 32, atomic);\n\trb = prng_lg_range_u32(&sa, 32, atomic);\n\tassert_u32_ne(ra, rb,\n\t    \"Full-width results must not immediately repeat\");\n\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_u32(&sa, 32, atomic);\n\tfor (lg_range = 31; lg_range > 0; lg_range--) {\n\t\tatomic_store_u32(&sb, 42, ATOMIC_RELAXED);\n\t\trb = prng_lg_range_u32(&sb, lg_range, atomic);\n\t\tassert_u32_eq((rb & (UINT32_C(0xffffffff) << lg_range)),\n\t\t    0, \"High order bits should be 0, lg_range=%u\", lg_range);\n\t\tassert_u32_eq(rb, (ra >> (32 - lg_range)),\n\t\t    \"Expected high order bits of full-width result, \"\n\t\t    \"lg_range=%u\", lg_range);\n\t}\n}\n\nstatic void\ntest_prng_lg_range_u64(void) {\n\tuint64_t sa, sb, ra, rb;\n\tunsigned lg_range;\n\n\tsa = 42;\n\tra = prng_lg_range_u64(&sa, 64);\n\tsa = 42;\n\trb = prng_lg_range_u64(&sa, 64);\n\tassert_u64_eq(ra, rb,\n\t    \"Repeated generation should produce repeated results\");\n\n\tsb = 42;\n\trb = prng_lg_range_u64(&sb, 64);\n\tassert_u64_eq(ra, rb,\n\t    \"Equivalent generation should produce equivalent results\");\n\n\tsa = 42;\n\tra = prng_lg_range_u64(&sa, 64);\n\trb = prng_lg_range_u64(&sa, 64);\n\tassert_u64_ne(ra, rb,\n\t    \"Full-width results must not immediately repeat\");\n\n\tsa = 42;\n\tra = prng_lg_range_u64(&sa, 64);\n\tfor (lg_range = 63; lg_range > 0; lg_range--) {\n\t\tsb = 42;\n\t\trb = prng_lg_range_u64(&sb, lg_range);\n\t\tassert_u64_eq((rb & (UINT64_C(0xffffffffffffffff) << lg_range)),\n\t\t    0, \"High order bits should be 0, lg_range=%u\", lg_range);\n\t\tassert_u64_eq(rb, (ra >> (64 - lg_range)),\n\t\t    \"Expected high order bits of full-width result, \"\n\t\t    \"lg_range=%u\", lg_range);\n\t}\n}\n\nstatic void\ntest_prng_lg_range_zu(bool atomic) {\n\tatomic_zu_t sa, sb;\n\tsize_t ra, rb;\n\tunsigned lg_range;\n\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tassert_zu_eq(ra, rb,\n\t    \"Repeated generation should produce repeated results\");\n\n\tatomic_store_zu(&sb, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_zu(&sb, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tassert_zu_eq(ra, rb,\n\t    \"Equivalent generation should produce equivalent results\");\n\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\trb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tassert_zu_ne(ra, rb,\n\t    \"Full-width results must not immediately repeat\");\n\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tfor (lg_range = (ZU(1) << (3 + LG_SIZEOF_PTR)) - 1; lg_range > 0;\n\t    lg_range--) {\n\t\tatomic_store_zu(&sb, 42, ATOMIC_RELAXED);\n\t\trb = prng_lg_range_zu(&sb, lg_range, atomic);\n\t\tassert_zu_eq((rb & (SIZE_T_MAX << lg_range)),\n\t\t    0, \"High order bits should be 0, lg_range=%u\", lg_range);\n\t\tassert_zu_eq(rb, (ra >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) -\n\t\t    lg_range)), \"Expected high order bits of full-width \"\n\t\t    \"result, lg_range=%u\", lg_range);\n\t}\n}\n\nTEST_BEGIN(test_prng_lg_range_u32_nonatomic) {\n\ttest_prng_lg_range_u32(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_u32_atomic) {\n\ttest_prng_lg_range_u32(true);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_u64_nonatomic) {\n\ttest_prng_lg_range_u64();\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_zu_nonatomic) {\n\ttest_prng_lg_range_zu(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_zu_atomic) {\n\ttest_prng_lg_range_zu(true);\n}\nTEST_END\n\nstatic void\ntest_prng_range_u32(bool atomic) {\n\tuint32_t range;\n#define MAX_RANGE\t10000000\n#define RANGE_STEP\t97\n#define NREPS\t\t10\n\n\tfor (range = 2; range < MAX_RANGE; range += RANGE_STEP) {\n\t\tatomic_u32_t s;\n\t\tunsigned rep;\n\n\t\tatomic_store_u32(&s, range, ATOMIC_RELAXED);\n\t\tfor (rep = 0; rep < NREPS; rep++) {\n\t\t\tuint32_t r = prng_range_u32(&s, range, atomic);\n\n\t\t\tassert_u32_lt(r, range, \"Out of range\");\n\t\t}\n\t}\n}\n\nstatic void\ntest_prng_range_u64(void) {\n\tuint64_t range;\n#define MAX_RANGE\t10000000\n#define RANGE_STEP\t97\n#define NREPS\t\t10\n\n\tfor (range = 2; range < MAX_RANGE; range += RANGE_STEP) {\n\t\tuint64_t s;\n\t\tunsigned rep;\n\n\t\ts = range;\n\t\tfor (rep = 0; rep < NREPS; rep++) {\n\t\t\tuint64_t r = prng_range_u64(&s, range);\n\n\t\t\tassert_u64_lt(r, range, \"Out of range\");\n\t\t}\n\t}\n}\n\nstatic void\ntest_prng_range_zu(bool atomic) {\n\tsize_t range;\n#define MAX_RANGE\t10000000\n#define RANGE_STEP\t97\n#define NREPS\t\t10\n\n\tfor (range = 2; range < MAX_RANGE; range += RANGE_STEP) {\n\t\tatomic_zu_t s;\n\t\tunsigned rep;\n\n\t\tatomic_store_zu(&s, range, ATOMIC_RELAXED);\n\t\tfor (rep = 0; rep < NREPS; rep++) {\n\t\t\tsize_t r = prng_range_zu(&s, range, atomic);\n\n\t\t\tassert_zu_lt(r, range, \"Out of range\");\n\t\t}\n\t}\n}\n\nTEST_BEGIN(test_prng_range_u32_nonatomic) {\n\ttest_prng_range_u32(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_u32_atomic) {\n\ttest_prng_range_u32(true);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_u64_nonatomic) {\n\ttest_prng_range_u64();\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_zu_nonatomic) {\n\ttest_prng_range_zu(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_zu_atomic) {\n\ttest_prng_range_zu(true);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_prng_lg_range_u32_nonatomic,\n\t    test_prng_lg_range_u32_atomic,\n\t    test_prng_lg_range_u64_nonatomic,\n\t    test_prng_lg_range_zu_nonatomic,\n\t    test_prng_lg_range_zu_atomic,\n\t    test_prng_range_u32_nonatomic,\n\t    test_prng_range_u32_atomic,\n\t    test_prng_range_u64_nonatomic,\n\t    test_prng_range_zu_nonatomic,\n\t    test_prng_range_zu_atomic);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_accum.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS\t\t4\n#define NALLOCS_PER_THREAD\t50\n#define DUMP_INTERVAL\t\t1\n#define BT_COUNT_CHECK_INTERVAL\t5\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nstatic void *\nalloc_from_permuted_backtrace(unsigned thd_ind, unsigned iteration) {\n\treturn btalloc(1, thd_ind*NALLOCS_PER_THREAD + iteration);\n}\n\nstatic void *\nthd_start(void *varg) {\n\tunsigned thd_ind = *(unsigned *)varg;\n\tsize_t bt_count_prev, bt_count;\n\tunsigned i_prev, i;\n\n\ti_prev = 0;\n\tbt_count_prev = 0;\n\tfor (i = 0; i < NALLOCS_PER_THREAD; i++) {\n\t\tvoid *p = alloc_from_permuted_backtrace(thd_ind, i);\n\t\tdallocx(p, 0);\n\t\tif (i % DUMP_INTERVAL == 0) {\n\t\t\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t\t\t    0, \"Unexpected error while dumping heap profile\");\n\t\t}\n\n\t\tif (i % BT_COUNT_CHECK_INTERVAL == 0 ||\n\t\t    i+1 == NALLOCS_PER_THREAD) {\n\t\t\tbt_count = prof_bt_count();\n\t\t\tassert_zu_le(bt_count_prev+(i-i_prev), bt_count,\n\t\t\t    \"Expected larger backtrace count increase\");\n\t\t\ti_prev = i;\n\t\t\tbt_count_prev = bt_count;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_idump) {\n\tbool active;\n\tthd_t thds[NTHREADS];\n\tunsigned thd_args[NTHREADS];\n\tunsigned i;\n\n\ttest_skip_if(!config_prof);\n\n\tactive = true;\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0,\n\t    \"Unexpected mallctl failure while activating profiling\");\n\n\tprof_dump_open = prof_dump_open_intercept;\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_args[i] = i;\n\t\tthd_create(&thds[i], thd_start, (void *)&thd_args[i]);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_idump);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_accum.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_accum:true,prof_active:false,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_active.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\nmallctl_bool_get(const char *name, bool expected, const char *func, int line) {\n\tbool old;\n\tsize_t sz;\n\n\tsz = sizeof(old);\n\tassert_d_eq(mallctl(name, (void *)&old, &sz, NULL, 0), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading %s\", func, line, name);\n\tassert_b_eq(old, expected, \"%s():%d: Unexpected %s value\", func, line,\n\t    name);\n}\n\nstatic void\nmallctl_bool_set(const char *name, bool old_expected, bool val_new,\n    const char *func, int line) {\n\tbool old;\n\tsize_t sz;\n\n\tsz = sizeof(old);\n\tassert_d_eq(mallctl(name, (void *)&old, &sz, (void *)&val_new,\n\t    sizeof(val_new)), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading/writing %s\", func,\n\t    line, name);\n\tassert_b_eq(old, old_expected, \"%s():%d: Unexpected %s value\", func,\n\t    line, name);\n}\n\nstatic void\nmallctl_prof_active_get_impl(bool prof_active_old_expected, const char *func,\n    int line) {\n\tmallctl_bool_get(\"prof.active\", prof_active_old_expected, func, line);\n}\n#define mallctl_prof_active_get(a)\t\t\t\t\t\\\n\tmallctl_prof_active_get_impl(a, __func__, __LINE__)\n\nstatic void\nmallctl_prof_active_set_impl(bool prof_active_old_expected,\n    bool prof_active_new, const char *func, int line) {\n\tmallctl_bool_set(\"prof.active\", prof_active_old_expected,\n\t    prof_active_new, func, line);\n}\n#define mallctl_prof_active_set(a, b)\t\t\t\t\t\\\n\tmallctl_prof_active_set_impl(a, b, __func__, __LINE__)\n\nstatic void\nmallctl_thread_prof_active_get_impl(bool thread_prof_active_old_expected,\n    const char *func, int line) {\n\tmallctl_bool_get(\"thread.prof.active\", thread_prof_active_old_expected,\n\t    func, line);\n}\n#define mallctl_thread_prof_active_get(a)\t\t\t\t\\\n\tmallctl_thread_prof_active_get_impl(a, __func__, __LINE__)\n\nstatic void\nmallctl_thread_prof_active_set_impl(bool thread_prof_active_old_expected,\n    bool thread_prof_active_new, const char *func, int line) {\n\tmallctl_bool_set(\"thread.prof.active\", thread_prof_active_old_expected,\n\t    thread_prof_active_new, func, line);\n}\n#define mallctl_thread_prof_active_set(a, b)\t\t\t\t\\\n\tmallctl_thread_prof_active_set_impl(a, b, __func__, __LINE__)\n\nstatic void\nprof_sampling_probe_impl(bool expect_sample, const char *func, int line) {\n\tvoid *p;\n\tsize_t expected_backtraces = expect_sample ? 1 : 0;\n\n\tassert_zu_eq(prof_bt_count(), 0, \"%s():%d: Expected 0 backtraces\", func,\n\t    line);\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tassert_zu_eq(prof_bt_count(), expected_backtraces,\n\t    \"%s():%d: Unexpected backtrace count\", func, line);\n\tdallocx(p, 0);\n}\n#define prof_sampling_probe(a)\t\t\t\t\t\t\\\n\tprof_sampling_probe_impl(a, __func__, __LINE__)\n\nTEST_BEGIN(test_prof_active) {\n\ttest_skip_if(!config_prof);\n\n\tmallctl_prof_active_get(true);\n\tmallctl_thread_prof_active_get(false);\n\n\tmallctl_prof_active_set(true, true);\n\tmallctl_thread_prof_active_set(false, false);\n\t/* prof.active, !thread.prof.active. */\n\tprof_sampling_probe(false);\n\n\tmallctl_prof_active_set(true, false);\n\tmallctl_thread_prof_active_set(false, false);\n\t/* !prof.active, !thread.prof.active. */\n\tprof_sampling_probe(false);\n\n\tmallctl_prof_active_set(false, false);\n\tmallctl_thread_prof_active_set(false, true);\n\t/* !prof.active, thread.prof.active. */\n\tprof_sampling_probe(false);\n\n\tmallctl_prof_active_set(false, true);\n\tmallctl_thread_prof_active_set(true, true);\n\t/* prof.active, thread.prof.active. */\n\tprof_sampling_probe(true);\n\n\t/* Restore settings. */\n\tmallctl_prof_active_set(true, true);\n\tmallctl_thread_prof_active_set(true, false);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_prof_active);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_active.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_thread_active_init:false,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_gdump.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic bool did_prof_dump_open;\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tdid_prof_dump_open = true;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nTEST_BEGIN(test_gdump) {\n\tbool active, gdump, gdump_old;\n\tvoid *p, *q, *r, *s;\n\tsize_t sz;\n\n\ttest_skip_if(!config_prof);\n\n\tactive = true;\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0,\n\t    \"Unexpected mallctl failure while activating profiling\");\n\n\tprof_dump_open = prof_dump_open_intercept;\n\n\tdid_prof_dump_open = false;\n\tp = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n\n\tdid_prof_dump_open = false;\n\tq = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n\n\tgdump = false;\n\tsz = sizeof(gdump_old);\n\tassert_d_eq(mallctl(\"prof.gdump\", (void *)&gdump_old, &sz,\n\t    (void *)&gdump, sizeof(gdump)), 0,\n\t    \"Unexpected mallctl failure while disabling prof.gdump\");\n\tassert(gdump_old);\n\tdid_prof_dump_open = false;\n\tr = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\tassert_false(did_prof_dump_open, \"Unexpected profile dump\");\n\n\tgdump = true;\n\tsz = sizeof(gdump_old);\n\tassert_d_eq(mallctl(\"prof.gdump\", (void *)&gdump_old, &sz,\n\t    (void *)&gdump, sizeof(gdump)), 0,\n\t    \"Unexpected mallctl failure while enabling prof.gdump\");\n\tassert(!gdump_old);\n\tdid_prof_dump_open = false;\n\ts = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n\n\tdallocx(p, 0);\n\tdallocx(q, 0);\n\tdallocx(r, 0);\n\tdallocx(s, 0);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_gdump);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_gdump.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_active:false,prof_gdump:true\"\nfi\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_idump.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic bool did_prof_dump_open;\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tdid_prof_dump_open = true;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nTEST_BEGIN(test_idump) {\n\tbool active;\n\tvoid *p;\n\n\ttest_skip_if(!config_prof);\n\n\tactive = true;\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0,\n\t    \"Unexpected mallctl failure while activating profiling\");\n\n\tprof_dump_open = prof_dump_open_intercept;\n\n\tdid_prof_dump_open = false;\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tdallocx(p, 0);\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_idump);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_idump.sh",
    "content": "#!/bin/sh\n\nexport MALLOC_CONF=\"tcache:false\"\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"${MALLOC_CONF},prof:true,prof_accum:true,prof_active:false,lg_prof_sample:0,lg_prof_interval:0\"\nfi\n\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_reset.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nstatic void\nset_prof_active(bool active) {\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0, \"Unexpected mallctl failure\");\n}\n\nstatic size_t\nget_lg_prof_sample(void) {\n\tsize_t lg_prof_sample;\n\tsize_t sz = sizeof(size_t);\n\n\tassert_d_eq(mallctl(\"prof.lg_sample\", (void *)&lg_prof_sample, &sz,\n\t    NULL, 0), 0,\n\t    \"Unexpected mallctl failure while reading profiling sample rate\");\n\treturn lg_prof_sample;\n}\n\nstatic void\ndo_prof_reset(size_t lg_prof_sample) {\n\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL,\n\t    (void *)&lg_prof_sample, sizeof(size_t)), 0,\n\t    \"Unexpected mallctl failure while resetting profile data\");\n\tassert_zu_eq(lg_prof_sample, get_lg_prof_sample(),\n\t    \"Expected profile sample rate change\");\n}\n\nTEST_BEGIN(test_prof_reset_basic) {\n\tsize_t lg_prof_sample_orig, lg_prof_sample, lg_prof_sample_next;\n\tsize_t sz;\n\tunsigned i;\n\n\ttest_skip_if(!config_prof);\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"opt.lg_prof_sample\", (void *)&lg_prof_sample_orig,\n\t    &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl failure while reading profiling sample rate\");\n\tassert_zu_eq(lg_prof_sample_orig, 0,\n\t    \"Unexpected profiling sample rate\");\n\tlg_prof_sample = get_lg_prof_sample();\n\tassert_zu_eq(lg_prof_sample_orig, lg_prof_sample,\n\t    \"Unexpected disagreement between \\\"opt.lg_prof_sample\\\" and \"\n\t    \"\\\"prof.lg_sample\\\"\");\n\n\t/* Test simple resets. */\n\tfor (i = 0; i < 2; i++) {\n\t\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0), 0,\n\t\t    \"Unexpected mallctl failure while resetting profile data\");\n\t\tlg_prof_sample = get_lg_prof_sample();\n\t\tassert_zu_eq(lg_prof_sample_orig, lg_prof_sample,\n\t\t    \"Unexpected profile sample rate change\");\n\t}\n\n\t/* Test resets with prof.lg_sample changes. */\n\tlg_prof_sample_next = 1;\n\tfor (i = 0; i < 2; i++) {\n\t\tdo_prof_reset(lg_prof_sample_next);\n\t\tlg_prof_sample = get_lg_prof_sample();\n\t\tassert_zu_eq(lg_prof_sample, lg_prof_sample_next,\n\t\t    \"Expected profile sample rate change\");\n\t\tlg_prof_sample_next = lg_prof_sample_orig;\n\t}\n\n\t/* Make sure the test code restored prof.lg_sample. */\n\tlg_prof_sample = get_lg_prof_sample();\n\tassert_zu_eq(lg_prof_sample_orig, lg_prof_sample,\n\t    \"Unexpected disagreement between \\\"opt.lg_prof_sample\\\" and \"\n\t    \"\\\"prof.lg_sample\\\"\");\n}\nTEST_END\n\nbool prof_dump_header_intercepted = false;\nprof_cnt_t cnt_all_copy = {0, 0, 0, 0};\nstatic bool\nprof_dump_header_intercept(tsdn_t *tsdn, bool propagate_err,\n    const prof_cnt_t *cnt_all) {\n\tprof_dump_header_intercepted = true;\n\tmemcpy(&cnt_all_copy, cnt_all, sizeof(prof_cnt_t));\n\n\treturn false;\n}\n\nTEST_BEGIN(test_prof_reset_cleanup) {\n\tvoid *p;\n\tprof_dump_header_t *prof_dump_header_orig;\n\n\ttest_skip_if(!config_prof);\n\n\tset_prof_active(true);\n\n\tassert_zu_eq(prof_bt_count(), 0, \"Expected 0 backtraces\");\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tassert_zu_eq(prof_bt_count(), 1, \"Expected 1 backtrace\");\n\n\tprof_dump_header_orig = prof_dump_header;\n\tprof_dump_header = prof_dump_header_intercept;\n\tassert_false(prof_dump_header_intercepted, \"Unexpected intercept\");\n\n\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t    0, \"Unexpected error while dumping heap profile\");\n\tassert_true(prof_dump_header_intercepted, \"Expected intercept\");\n\tassert_u64_eq(cnt_all_copy.curobjs, 1, \"Expected 1 allocation\");\n\n\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected error while resetting heap profile data\");\n\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t    0, \"Unexpected error while dumping heap profile\");\n\tassert_u64_eq(cnt_all_copy.curobjs, 0, \"Expected 0 allocations\");\n\tassert_zu_eq(prof_bt_count(), 1, \"Expected 1 backtrace\");\n\n\tprof_dump_header = prof_dump_header_orig;\n\n\tdallocx(p, 0);\n\tassert_zu_eq(prof_bt_count(), 0, \"Expected 0 backtraces\");\n\n\tset_prof_active(false);\n}\nTEST_END\n\n#define NTHREADS\t\t4\n#define NALLOCS_PER_THREAD\t(1U << 13)\n#define OBJ_RING_BUF_COUNT\t1531\n#define RESET_INTERVAL\t\t(1U << 10)\n#define DUMP_INTERVAL\t\t3677\nstatic void *\nthd_start(void *varg) {\n\tunsigned thd_ind = *(unsigned *)varg;\n\tunsigned i;\n\tvoid *objs[OBJ_RING_BUF_COUNT];\n\n\tmemset(objs, 0, sizeof(objs));\n\n\tfor (i = 0; i < NALLOCS_PER_THREAD; i++) {\n\t\tif (i % RESET_INTERVAL == 0) {\n\t\t\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0),\n\t\t\t    0, \"Unexpected error while resetting heap profile \"\n\t\t\t    \"data\");\n\t\t}\n\n\t\tif (i % DUMP_INTERVAL == 0) {\n\t\t\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t\t\t    0, \"Unexpected error while dumping heap profile\");\n\t\t}\n\n\t\t{\n\t\t\tvoid **pp = &objs[i % OBJ_RING_BUF_COUNT];\n\t\t\tif (*pp != NULL) {\n\t\t\t\tdallocx(*pp, 0);\n\t\t\t\t*pp = NULL;\n\t\t\t}\n\t\t\t*pp = btalloc(1, thd_ind*NALLOCS_PER_THREAD + i);\n\t\t\tassert_ptr_not_null(*pp,\n\t\t\t    \"Unexpected btalloc() failure\");\n\t\t}\n\t}\n\n\t/* Clean up any remaining objects. */\n\tfor (i = 0; i < OBJ_RING_BUF_COUNT; i++) {\n\t\tvoid **pp = &objs[i % OBJ_RING_BUF_COUNT];\n\t\tif (*pp != NULL) {\n\t\t\tdallocx(*pp, 0);\n\t\t\t*pp = NULL;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_prof_reset) {\n\tsize_t lg_prof_sample_orig;\n\tthd_t thds[NTHREADS];\n\tunsigned thd_args[NTHREADS];\n\tunsigned i;\n\tsize_t bt_count, tdata_count;\n\n\ttest_skip_if(!config_prof);\n\n\tbt_count = prof_bt_count();\n\tassert_zu_eq(bt_count, 0,\n\t    \"Unexpected pre-existing tdata structures\");\n\ttdata_count = prof_tdata_count();\n\n\tlg_prof_sample_orig = get_lg_prof_sample();\n\tdo_prof_reset(5);\n\n\tset_prof_active(true);\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_args[i] = i;\n\t\tthd_create(&thds[i], thd_start, (void *)&thd_args[i]);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n\n\tassert_zu_eq(prof_bt_count(), bt_count,\n\t    \"Unexpected bactrace count change\");\n\tassert_zu_eq(prof_tdata_count(), tdata_count,\n\t    \"Unexpected remaining tdata structures\");\n\n\tset_prof_active(false);\n\n\tdo_prof_reset(lg_prof_sample_orig);\n}\nTEST_END\n#undef NTHREADS\n#undef NALLOCS_PER_THREAD\n#undef OBJ_RING_BUF_COUNT\n#undef RESET_INTERVAL\n#undef DUMP_INTERVAL\n\n/* Test sampling at the same allocation site across resets. */\n#define NITER 10\nTEST_BEGIN(test_xallocx) {\n\tsize_t lg_prof_sample_orig;\n\tunsigned i;\n\tvoid *ptrs[NITER];\n\n\ttest_skip_if(!config_prof);\n\n\tlg_prof_sample_orig = get_lg_prof_sample();\n\tset_prof_active(true);\n\n\t/* Reset profiling. */\n\tdo_prof_reset(0);\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tvoid *p;\n\t\tsize_t sz, nsz;\n\n\t\t/* Reset profiling. */\n\t\tdo_prof_reset(0);\n\n\t\t/* Allocate small object (which will be promoted). */\n\t\tp = ptrs[i] = mallocx(1, 0);\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\t\t/* Reset profiling. */\n\t\tdo_prof_reset(0);\n\n\t\t/* Perform successful xallocx(). */\n\t\tsz = sallocx(p, 0);\n\t\tassert_zu_eq(xallocx(p, sz, 0, 0), sz,\n\t\t    \"Unexpected xallocx() failure\");\n\n\t\t/* Perform unsuccessful xallocx(). */\n\t\tnsz = nallocx(sz+1, 0);\n\t\tassert_zu_eq(xallocx(p, nsz, 0, 0), sz,\n\t\t    \"Unexpected xallocx() success\");\n\t}\n\n\tfor (i = 0; i < NITER; i++) {\n\t\t/* dallocx. */\n\t\tdallocx(ptrs[i], 0);\n\t}\n\n\tset_prof_active(false);\n\tdo_prof_reset(lg_prof_sample_orig);\n}\nTEST_END\n#undef NITER\n\nint\nmain(void) {\n\t/* Intercept dumping prior to running any tests. */\n\tprof_dump_open = prof_dump_open_intercept;\n\n\treturn test_no_reentrancy(\n\t    test_prof_reset_basic,\n\t    test_prof_reset_cleanup,\n\t    test_prof_reset,\n\t    test_xallocx);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_reset.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_active:false,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_tctx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_prof_realloc) {\n\ttsdn_t *tsdn;\n\tint flags;\n\tvoid *p, *q;\n\tprof_tctx_t *tctx_p, *tctx_q;\n\tuint64_t curobjs_0, curobjs_1, curobjs_2, curobjs_3;\n\n\ttest_skip_if(!config_prof);\n\n\ttsdn = tsdn_fetch();\n\tflags = MALLOCX_TCACHE_NONE;\n\n\tprof_cnt_all(&curobjs_0, NULL, NULL, NULL);\n\tp = mallocx(1024, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\ttctx_p = prof_tctx_get(tsdn, p, NULL);\n\tassert_ptr_ne(tctx_p, (prof_tctx_t *)(uintptr_t)1U,\n\t    \"Expected valid tctx\");\n\tprof_cnt_all(&curobjs_1, NULL, NULL, NULL);\n\tassert_u64_eq(curobjs_0 + 1, curobjs_1,\n\t    \"Allocation should have increased sample size\");\n\n\tq = rallocx(p, 2048, flags);\n\tassert_ptr_ne(p, q, \"Expected move\");\n\tassert_ptr_not_null(p, \"Unexpected rmallocx() failure\");\n\ttctx_q = prof_tctx_get(tsdn, q, NULL);\n\tassert_ptr_ne(tctx_q, (prof_tctx_t *)(uintptr_t)1U,\n\t    \"Expected valid tctx\");\n\tprof_cnt_all(&curobjs_2, NULL, NULL, NULL);\n\tassert_u64_eq(curobjs_1, curobjs_2,\n\t    \"Reallocation should not have changed sample size\");\n\n\tdallocx(q, flags);\n\tprof_cnt_all(&curobjs_3, NULL, NULL, NULL);\n\tassert_u64_eq(curobjs_0, curobjs_3,\n\t    \"Sample size should have returned to base level\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_prof_realloc);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_tctx.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_thread_name.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\nmallctl_thread_name_get_impl(const char *thread_name_expected, const char *func,\n    int line) {\n\tconst char *thread_name_old;\n\tsize_t sz;\n\n\tsz = sizeof(thread_name_old);\n\tassert_d_eq(mallctl(\"thread.prof.name\", (void *)&thread_name_old, &sz,\n\t    NULL, 0), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading thread.prof.name\",\n\t    func, line);\n\tassert_str_eq(thread_name_old, thread_name_expected,\n\t    \"%s():%d: Unexpected thread.prof.name value\", func, line);\n}\n#define mallctl_thread_name_get(a)\t\t\t\t\t\\\n\tmallctl_thread_name_get_impl(a, __func__, __LINE__)\n\nstatic void\nmallctl_thread_name_set_impl(const char *thread_name, const char *func,\n    int line) {\n\tassert_d_eq(mallctl(\"thread.prof.name\", NULL, NULL,\n\t    (void *)&thread_name, sizeof(thread_name)), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading thread.prof.name\",\n\t    func, line);\n\tmallctl_thread_name_get_impl(thread_name, func, line);\n}\n#define mallctl_thread_name_set(a)\t\t\t\t\t\\\n\tmallctl_thread_name_set_impl(a, __func__, __LINE__)\n\nTEST_BEGIN(test_prof_thread_name_validation) {\n\tconst char *thread_name;\n\n\ttest_skip_if(!config_prof);\n\n\tmallctl_thread_name_get(\"\");\n\tmallctl_thread_name_set(\"hi there\");\n\n\t/* NULL input shouldn't be allowed. */\n\tthread_name = NULL;\n\tassert_d_eq(mallctl(\"thread.prof.name\", NULL, NULL,\n\t    (void *)&thread_name, sizeof(thread_name)), EFAULT,\n\t    \"Unexpected mallctl result writing \\\"%s\\\" to thread.prof.name\",\n\t    thread_name);\n\n\t/* '\\n' shouldn't be allowed. */\n\tthread_name = \"hi\\nthere\";\n\tassert_d_eq(mallctl(\"thread.prof.name\", NULL, NULL,\n\t    (void *)&thread_name, sizeof(thread_name)), EFAULT,\n\t    \"Unexpected mallctl result writing \\\"%s\\\" to thread.prof.name\",\n\t    thread_name);\n\n\t/* Simultaneous read/write shouldn't be allowed. */\n\t{\n\t\tconst char *thread_name_old;\n\t\tsize_t sz;\n\n\t\tsz = sizeof(thread_name_old);\n\t\tassert_d_eq(mallctl(\"thread.prof.name\",\n\t\t    (void *)&thread_name_old, &sz, (void *)&thread_name,\n\t\t    sizeof(thread_name)), EPERM,\n\t\t    \"Unexpected mallctl result writing \\\"%s\\\" to \"\n\t\t    \"thread.prof.name\", thread_name);\n\t}\n\n\tmallctl_thread_name_set(\"\");\n}\nTEST_END\n\n#define NTHREADS\t4\n#define NRESET\t\t25\nstatic void *\nthd_start(void *varg) {\n\tunsigned thd_ind = *(unsigned *)varg;\n\tchar thread_name[16] = \"\";\n\tunsigned i;\n\n\tmalloc_snprintf(thread_name, sizeof(thread_name), \"thread %u\", thd_ind);\n\n\tmallctl_thread_name_get(\"\");\n\tmallctl_thread_name_set(thread_name);\n\n\tfor (i = 0; i < NRESET; i++) {\n\t\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0), 0,\n\t\t    \"Unexpected error while resetting heap profile data\");\n\t\tmallctl_thread_name_get(thread_name);\n\t}\n\n\tmallctl_thread_name_set(thread_name);\n\tmallctl_thread_name_set(\"\");\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_prof_thread_name_threaded) {\n\tthd_t thds[NTHREADS];\n\tunsigned thd_args[NTHREADS];\n\tunsigned i;\n\n\ttest_skip_if(!config_prof);\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_args[i] = i;\n\t\tthd_create(&thds[i], thd_start, (void *)&thd_args[i]);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n}\nTEST_END\n#undef NTHREADS\n#undef NRESET\n\nint\nmain(void) {\n\treturn test(\n\t    test_prof_thread_name_validation,\n\t    test_prof_thread_name_threaded);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/prof_thread_name.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_active:false\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/ql.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ql.h\"\n\n/* Number of ring entries, in [2..26]. */\n#define NENTRIES 9\n\ntypedef struct list_s list_t;\ntypedef ql_head(list_t) list_head_t;\n\nstruct list_s {\n\tql_elm(list_t) link;\n\tchar id;\n};\n\nstatic void\ntest_empty_list(list_head_t *head) {\n\tlist_t *t;\n\tunsigned i;\n\n\tassert_ptr_null(ql_first(head), \"Unexpected element for empty list\");\n\tassert_ptr_null(ql_last(head, link),\n\t    \"Unexpected element for empty list\");\n\n\ti = 0;\n\tql_foreach(t, head, link) {\n\t\ti++;\n\t}\n\tassert_u_eq(i, 0, \"Unexpected element for empty list\");\n\n\ti = 0;\n\tql_reverse_foreach(t, head, link) {\n\t\ti++;\n\t}\n\tassert_u_eq(i, 0, \"Unexpected element for empty list\");\n}\n\nTEST_BEGIN(test_ql_empty) {\n\tlist_head_t head;\n\n\tql_new(&head);\n\ttest_empty_list(&head);\n}\nTEST_END\n\nstatic void\ninit_entries(list_t *entries, unsigned nentries) {\n\tunsigned i;\n\n\tfor (i = 0; i < nentries; i++) {\n\t\tentries[i].id = 'a' + i;\n\t\tql_elm_new(&entries[i], link);\n\t}\n}\n\nstatic void\ntest_entries_list(list_head_t *head, list_t *entries, unsigned nentries) {\n\tlist_t *t;\n\tunsigned i;\n\n\tassert_c_eq(ql_first(head)->id, entries[0].id, \"Element id mismatch\");\n\tassert_c_eq(ql_last(head, link)->id, entries[nentries-1].id,\n\t    \"Element id mismatch\");\n\n\ti = 0;\n\tql_foreach(t, head, link) {\n\t\tassert_c_eq(t->id, entries[i].id, \"Element id mismatch\");\n\t\ti++;\n\t}\n\n\ti = 0;\n\tql_reverse_foreach(t, head, link) {\n\t\tassert_c_eq(t->id, entries[nentries-i-1].id,\n\t\t    \"Element id mismatch\");\n\t\ti++;\n\t}\n\n\tfor (i = 0; i < nentries-1; i++) {\n\t\tt = ql_next(head, &entries[i], link);\n\t\tassert_c_eq(t->id, entries[i+1].id, \"Element id mismatch\");\n\t}\n\tassert_ptr_null(ql_next(head, &entries[nentries-1], link),\n\t    \"Unexpected element\");\n\n\tassert_ptr_null(ql_prev(head, &entries[0], link), \"Unexpected element\");\n\tfor (i = 1; i < nentries; i++) {\n\t\tt = ql_prev(head, &entries[i], link);\n\t\tassert_c_eq(t->id, entries[i-1].id, \"Element id mismatch\");\n\t}\n}\n\nTEST_BEGIN(test_ql_tail_insert) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_tail_insert(&head, &entries[i], link);\n\t}\n\n\ttest_entries_list(&head, entries, NENTRIES);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_tail_remove) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_tail_insert(&head, &entries[i], link);\n\t}\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\ttest_entries_list(&head, entries, NENTRIES-i);\n\t\tql_tail_remove(&head, list_t, link);\n\t}\n\ttest_empty_list(&head);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_head_insert) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_head_insert(&head, &entries[NENTRIES-i-1], link);\n\t}\n\n\ttest_entries_list(&head, entries, NENTRIES);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_head_remove) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_head_insert(&head, &entries[NENTRIES-i-1], link);\n\t}\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\ttest_entries_list(&head, &entries[i], NENTRIES-i);\n\t\tql_head_remove(&head, list_t, link);\n\t}\n\ttest_empty_list(&head);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_insert) {\n\tlist_head_t head;\n\tlist_t entries[8];\n\tlist_t *a, *b, *c, *d, *e, *f, *g, *h;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\ta = &entries[0];\n\tb = &entries[1];\n\tc = &entries[2];\n\td = &entries[3];\n\te = &entries[4];\n\tf = &entries[5];\n\tg = &entries[6];\n\th = &entries[7];\n\n\t/*\n\t * ql_remove(), ql_before_insert(), and ql_after_insert() are used\n\t * internally by other macros that are already tested, so there's no\n\t * need to test them completely.  However, insertion/deletion from the\n\t * middle of lists is not otherwise tested; do so here.\n\t */\n\tql_tail_insert(&head, f, link);\n\tql_before_insert(&head, f, b, link);\n\tql_before_insert(&head, f, c, link);\n\tql_after_insert(f, h, link);\n\tql_after_insert(f, g, link);\n\tql_before_insert(&head, b, a, link);\n\tql_after_insert(c, d, link);\n\tql_before_insert(&head, f, e, link);\n\n\ttest_entries_list(&head, entries, sizeof(entries)/sizeof(list_t));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ql_empty,\n\t    test_ql_tail_insert,\n\t    test_ql_tail_remove,\n\t    test_ql_head_insert,\n\t    test_ql_head_remove,\n\t    test_ql_insert);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/qr.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/qr.h\"\n\n/* Number of ring entries, in [2..26]. */\n#define NENTRIES 9\n/* Split index, in [1..NENTRIES). */\n#define SPLIT_INDEX 5\n\ntypedef struct ring_s ring_t;\n\nstruct ring_s {\n\tqr(ring_t) link;\n\tchar id;\n};\n\nstatic void\ninit_entries(ring_t *entries) {\n\tunsigned i;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tqr_new(&entries[i], link);\n\t\tentries[i].id = 'a' + i;\n\t}\n}\n\nstatic void\ntest_independent_entries(ring_t *entries) {\n\tring_t *t;\n\tunsigned i, j;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tj++;\n\t\t}\n\t\tassert_u_eq(j, 1,\n\t\t    \"Iteration over single-element ring should visit precisely \"\n\t\t    \"one element\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tj++;\n\t\t}\n\t\tassert_u_eq(j, 1,\n\t\t    \"Iteration over single-element ring should visit precisely \"\n\t\t    \"one element\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_next(&entries[i], link);\n\t\tassert_ptr_eq(t, &entries[i],\n\t\t    \"Next element in single-element ring should be same as \"\n\t\t    \"current element\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_prev(&entries[i], link);\n\t\tassert_ptr_eq(t, &entries[i],\n\t\t    \"Previous element in single-element ring should be same as \"\n\t\t    \"current element\");\n\t}\n}\n\nTEST_BEGIN(test_qr_one) {\n\tring_t entries[NENTRIES];\n\n\tinit_entries(entries);\n\ttest_independent_entries(entries);\n}\nTEST_END\n\nstatic void\ntest_entries_ring(ring_t *entries) {\n\tring_t *t;\n\tunsigned i, j;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(i+j) % NENTRIES].id,\n\t\t\t    \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(NENTRIES+i-j-1) %\n\t\t\t    NENTRIES].id, \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_next(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(i+1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_prev(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n}\n\nTEST_BEGIN(test_qr_after_insert) {\n\tring_t entries[NENTRIES];\n\tunsigned i;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_after_insert(&entries[i - 1], &entries[i], link);\n\t}\n\ttest_entries_ring(entries);\n}\nTEST_END\n\nTEST_BEGIN(test_qr_remove) {\n\tring_t entries[NENTRIES];\n\tring_t *t;\n\tunsigned i, j;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_after_insert(&entries[i - 1], &entries[i], link);\n\t}\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[i+j].id,\n\t\t\t    \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[NENTRIES - 1 - j].id,\n\t\t\t\"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t\tqr_remove(&entries[i], link);\n\t}\n\ttest_independent_entries(entries);\n}\nTEST_END\n\nTEST_BEGIN(test_qr_before_insert) {\n\tring_t entries[NENTRIES];\n\tring_t *t;\n\tunsigned i, j;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_before_insert(&entries[i - 1], &entries[i], link);\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(NENTRIES+i-j) %\n\t\t\t    NENTRIES].id, \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(i+j+1) % NENTRIES].id,\n\t\t\t    \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_next(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_prev(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(i+1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n}\nTEST_END\n\nstatic void\ntest_split_entries(ring_t *entries) {\n\tring_t *t;\n\tunsigned i, j;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tif (i < SPLIT_INDEX) {\n\t\t\t\tassert_c_eq(t->id,\n\t\t\t\t    entries[(i+j) % SPLIT_INDEX].id,\n\t\t\t\t    \"Element id mismatch\");\n\t\t\t} else {\n\t\t\t\tassert_c_eq(t->id, entries[(i+j-SPLIT_INDEX) %\n\t\t\t\t    (NENTRIES-SPLIT_INDEX) + SPLIT_INDEX].id,\n\t\t\t\t    \"Element id mismatch\");\n\t\t\t}\n\t\t\tj++;\n\t\t}\n\t}\n}\n\nTEST_BEGIN(test_qr_meld_split) {\n\tring_t entries[NENTRIES];\n\tunsigned i;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_after_insert(&entries[i - 1], &entries[i], link);\n\t}\n\n\tqr_split(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_split_entries(entries);\n\n\tqr_meld(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_entries_ring(entries);\n\n\tqr_meld(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_split_entries(entries);\n\n\tqr_split(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_entries_ring(entries);\n\n\tqr_split(&entries[0], &entries[0], ring_t, link);\n\ttest_entries_ring(entries);\n\n\tqr_meld(&entries[0], &entries[0], ring_t, link);\n\ttest_entries_ring(entries);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_qr_one,\n\t    test_qr_after_insert,\n\t    test_qr_remove,\n\t    test_qr_before_insert,\n\t    test_qr_meld_split);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/rb.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/rb.h\"\n\n#define rbtn_black_height(a_type, a_field, a_rbt, r_height) do {\t\\\n\ta_type *rbp_bh_t;\t\t\t\t\t\t\\\n\tfor (rbp_bh_t = (a_rbt)->rbt_root, (r_height) = 0; rbp_bh_t !=\t\\\n\t    NULL; rbp_bh_t = rbtn_left_get(a_type, a_field,\t\t\\\n\t    rbp_bh_t)) {\t\t\t\t\t\t\\\n\t\tif (!rbtn_red_get(a_type, a_field, rbp_bh_t)) {\t\t\\\n\t\t(r_height)++;\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\ntypedef struct node_s node_t;\n\nstruct node_s {\n#define NODE_MAGIC 0x9823af7e\n\tuint32_t magic;\n\trb_node(node_t) link;\n\tuint64_t key;\n};\n\nstatic int\nnode_cmp(const node_t *a, const node_t *b) {\n\tint ret;\n\n\tassert_u32_eq(a->magic, NODE_MAGIC, \"Bad magic\");\n\tassert_u32_eq(b->magic, NODE_MAGIC, \"Bad magic\");\n\n\tret = (a->key > b->key) - (a->key < b->key);\n\tif (ret == 0) {\n\t\t/*\n\t\t * Duplicates are not allowed in the tree, so force an\n\t\t * arbitrary ordering for non-identical items with equal keys.\n\t\t */\n\t\tret = (((uintptr_t)a) > ((uintptr_t)b))\n\t\t    - (((uintptr_t)a) < ((uintptr_t)b));\n\t}\n\treturn ret;\n}\n\ntypedef rb_tree(node_t) tree_t;\nrb_gen(static, tree_, tree_t, node_t, link, node_cmp);\n\nTEST_BEGIN(test_rb_empty) {\n\ttree_t tree;\n\tnode_t key;\n\n\ttree_new(&tree);\n\n\tassert_true(tree_empty(&tree), \"Tree should be empty\");\n\tassert_ptr_null(tree_first(&tree), \"Unexpected node\");\n\tassert_ptr_null(tree_last(&tree), \"Unexpected node\");\n\n\tkey.key = 0;\n\tkey.magic = NODE_MAGIC;\n\tassert_ptr_null(tree_search(&tree, &key), \"Unexpected node\");\n\n\tkey.key = 0;\n\tkey.magic = NODE_MAGIC;\n\tassert_ptr_null(tree_nsearch(&tree, &key), \"Unexpected node\");\n\n\tkey.key = 0;\n\tkey.magic = NODE_MAGIC;\n\tassert_ptr_null(tree_psearch(&tree, &key), \"Unexpected node\");\n}\nTEST_END\n\nstatic unsigned\ntree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {\n\tunsigned ret = 0;\n\tnode_t *left_node;\n\tnode_t *right_node;\n\n\tif (node == NULL) {\n\t\treturn ret;\n\t}\n\n\tleft_node = rbtn_left_get(node_t, link, node);\n\tright_node = rbtn_right_get(node_t, link, node);\n\n\tif (!rbtn_red_get(node_t, link, node)) {\n\t\tblack_depth++;\n\t}\n\n\t/* Red nodes must be interleaved with black nodes. */\n\tif (rbtn_red_get(node_t, link, node)) {\n\t\tif (left_node != NULL) {\n\t\t\tassert_false(rbtn_red_get(node_t, link, left_node),\n\t\t\t\t\"Node should be black\");\n\t\t}\n\t\tif (right_node != NULL) {\n\t\t\tassert_false(rbtn_red_get(node_t, link, right_node),\n\t\t\t    \"Node should be black\");\n\t\t}\n\t}\n\n\t/* Self. */\n\tassert_u32_eq(node->magic, NODE_MAGIC, \"Bad magic\");\n\n\t/* Left subtree. */\n\tif (left_node != NULL) {\n\t\tret += tree_recurse(left_node, black_height, black_depth);\n\t} else {\n\t\tret += (black_depth != black_height);\n\t}\n\n\t/* Right subtree. */\n\tif (right_node != NULL) {\n\t\tret += tree_recurse(right_node, black_height, black_depth);\n\t} else {\n\t\tret += (black_depth != black_height);\n\t}\n\n\treturn ret;\n}\n\nstatic node_t *\ntree_iterate_cb(tree_t *tree, node_t *node, void *data) {\n\tunsigned *i = (unsigned *)data;\n\tnode_t *search_node;\n\n\tassert_u32_eq(node->magic, NODE_MAGIC, \"Bad magic\");\n\n\t/* Test rb_search(). */\n\tsearch_node = tree_search(tree, node);\n\tassert_ptr_eq(search_node, node,\n\t    \"tree_search() returned unexpected node\");\n\n\t/* Test rb_nsearch(). */\n\tsearch_node = tree_nsearch(tree, node);\n\tassert_ptr_eq(search_node, node,\n\t    \"tree_nsearch() returned unexpected node\");\n\n\t/* Test rb_psearch(). */\n\tsearch_node = tree_psearch(tree, node);\n\tassert_ptr_eq(search_node, node,\n\t    \"tree_psearch() returned unexpected node\");\n\n\t(*i)++;\n\n\treturn NULL;\n}\n\nstatic unsigned\ntree_iterate(tree_t *tree) {\n\tunsigned i;\n\n\ti = 0;\n\ttree_iter(tree, NULL, tree_iterate_cb, (void *)&i);\n\n\treturn i;\n}\n\nstatic unsigned\ntree_iterate_reverse(tree_t *tree) {\n\tunsigned i;\n\n\ti = 0;\n\ttree_reverse_iter(tree, NULL, tree_iterate_cb, (void *)&i);\n\n\treturn i;\n}\n\nstatic void\nnode_remove(tree_t *tree, node_t *node, unsigned nnodes) {\n\tnode_t *search_node;\n\tunsigned black_height, imbalances;\n\n\ttree_remove(tree, node);\n\n\t/* Test rb_nsearch(). */\n\tsearch_node = tree_nsearch(tree, node);\n\tif (search_node != NULL) {\n\t\tassert_u64_ge(search_node->key, node->key,\n\t\t    \"Key ordering error\");\n\t}\n\n\t/* Test rb_psearch(). */\n\tsearch_node = tree_psearch(tree, node);\n\tif (search_node != NULL) {\n\t\tassert_u64_le(search_node->key, node->key,\n\t\t    \"Key ordering error\");\n\t}\n\n\tnode->magic = 0;\n\n\trbtn_black_height(node_t, link, tree, black_height);\n\timbalances = tree_recurse(tree->rbt_root, black_height, 0);\n\tassert_u_eq(imbalances, 0, \"Tree is unbalanced\");\n\tassert_u_eq(tree_iterate(tree), nnodes-1,\n\t    \"Unexpected node iteration count\");\n\tassert_u_eq(tree_iterate_reverse(tree), nnodes-1,\n\t    \"Unexpected node iteration count\");\n}\n\nstatic node_t *\nremove_iterate_cb(tree_t *tree, node_t *node, void *data) {\n\tunsigned *nnodes = (unsigned *)data;\n\tnode_t *ret = tree_next(tree, node);\n\n\tnode_remove(tree, node, *nnodes);\n\n\treturn ret;\n}\n\nstatic node_t *\nremove_reverse_iterate_cb(tree_t *tree, node_t *node, void *data) {\n\tunsigned *nnodes = (unsigned *)data;\n\tnode_t *ret = tree_prev(tree, node);\n\n\tnode_remove(tree, node, *nnodes);\n\n\treturn ret;\n}\n\nstatic void\ndestroy_cb(node_t *node, void *data) {\n\tunsigned *nnodes = (unsigned *)data;\n\n\tassert_u_gt(*nnodes, 0, \"Destruction removed too many nodes\");\n\t(*nnodes)--;\n}\n\nTEST_BEGIN(test_rb_random) {\n#define NNODES 25\n#define NBAGS 250\n#define SEED 42\n\tsfmt_t *sfmt;\n\tuint64_t bag[NNODES];\n\ttree_t tree;\n\tnode_t nodes[NNODES];\n\tunsigned i, j, k, black_height, imbalances;\n\n\tsfmt = init_gen_rand(SEED);\n\tfor (i = 0; i < NBAGS; i++) {\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\t/* Insert in order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = j;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t/* Insert in reverse order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = NNODES - j - 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = gen_rand64_range(sfmt, NNODES);\n\t\t\t}\n\t\t}\n\n\t\tfor (j = 1; j <= NNODES; j++) {\n\t\t\t/* Initialize tree and nodes. */\n\t\t\ttree_new(&tree);\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\tnodes[k].magic = NODE_MAGIC;\n\t\t\t\tnodes[k].key = bag[k];\n\t\t\t}\n\n\t\t\t/* Insert nodes. */\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\ttree_insert(&tree, &nodes[k]);\n\n\t\t\t\trbtn_black_height(node_t, link, &tree,\n\t\t\t\t    black_height);\n\t\t\t\timbalances = tree_recurse(tree.rbt_root,\n\t\t\t\t    black_height, 0);\n\t\t\t\tassert_u_eq(imbalances, 0,\n\t\t\t\t    \"Tree is unbalanced\");\n\n\t\t\t\tassert_u_eq(tree_iterate(&tree), k+1,\n\t\t\t\t    \"Unexpected node iteration count\");\n\t\t\t\tassert_u_eq(tree_iterate_reverse(&tree), k+1,\n\t\t\t\t    \"Unexpected node iteration count\");\n\n\t\t\t\tassert_false(tree_empty(&tree),\n\t\t\t\t    \"Tree should not be empty\");\n\t\t\t\tassert_ptr_not_null(tree_first(&tree),\n\t\t\t\t    \"Tree should not be empty\");\n\t\t\t\tassert_ptr_not_null(tree_last(&tree),\n\t\t\t\t    \"Tree should not be empty\");\n\n\t\t\t\ttree_next(&tree, &nodes[k]);\n\t\t\t\ttree_prev(&tree, &nodes[k]);\n\t\t\t}\n\n\t\t\t/* Remove nodes. */\n\t\t\tswitch (i % 5) {\n\t\t\tcase 0:\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_remove(&tree, &nodes[k], j - k);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tfor (k = j; k > 0; k--) {\n\t\t\t\t\tnode_remove(&tree, &nodes[k-1], k);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2: {\n\t\t\t\tnode_t *start;\n\t\t\t\tunsigned nnodes = j;\n\n\t\t\t\tstart = NULL;\n\t\t\t\tdo {\n\t\t\t\t\tstart = tree_iter(&tree, start,\n\t\t\t\t\t    remove_iterate_cb, (void *)&nnodes);\n\t\t\t\t\tnnodes--;\n\t\t\t\t} while (start != NULL);\n\t\t\t\tassert_u_eq(nnodes, 0,\n\t\t\t\t    \"Removal terminated early\");\n\t\t\t\tbreak;\n\t\t\t} case 3: {\n\t\t\t\tnode_t *start;\n\t\t\t\tunsigned nnodes = j;\n\n\t\t\t\tstart = NULL;\n\t\t\t\tdo {\n\t\t\t\t\tstart = tree_reverse_iter(&tree, start,\n\t\t\t\t\t    remove_reverse_iterate_cb,\n\t\t\t\t\t    (void *)&nnodes);\n\t\t\t\t\tnnodes--;\n\t\t\t\t} while (start != NULL);\n\t\t\t\tassert_u_eq(nnodes, 0,\n\t\t\t\t    \"Removal terminated early\");\n\t\t\t\tbreak;\n\t\t\t} case 4: {\n\t\t\t\tunsigned nnodes = j;\n\t\t\t\ttree_destroy(&tree, destroy_cb, &nnodes);\n\t\t\t\tassert_u_eq(nnodes, 0,\n\t\t\t\t    \"Destruction terminated early\");\n\t\t\t\tbreak;\n\t\t\t} default:\n\t\t\t\tnot_reached();\n\t\t\t}\n\t\t}\n\t}\n\tfini_gen_rand(sfmt);\n#undef NNODES\n#undef NBAGS\n#undef SEED\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_rb_empty,\n\t    test_rb_random);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/retained.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/spin.h\"\n\nstatic unsigned\t\tarena_ind;\nstatic size_t\t\tsz;\nstatic size_t\t\tesz;\n#define NEPOCHS\t\t8\n#define PER_THD_NALLOCS\t1\nstatic atomic_u_t\tepoch;\nstatic atomic_u_t\tnfinished;\n\nstatic unsigned\ndo_arena_create(extent_hooks_t *h) {\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz,\n\t    (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,\n\t    \"Unexpected mallctl() failure\");\n\treturn arena_ind;\n}\n\nstatic void\ndo_arena_destroy(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.destroy\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nstatic void\ndo_refresh(void) {\n\tuint64_t epoch = 1;\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)), 0, \"Unexpected mallctl() failure\");\n}\n\nstatic size_t\ndo_get_size_impl(const char *cmd, unsigned arena_ind) {\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\tsize_t z = sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = arena_ind;\n\tsize_t size;\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\"], ...) failure\", cmd);\n\n\treturn size;\n}\n\nstatic size_t\ndo_get_active(unsigned arena_ind) {\n\treturn do_get_size_impl(\"stats.arenas.0.pactive\", arena_ind) * PAGE;\n}\n\nstatic size_t\ndo_get_mapped(unsigned arena_ind) {\n\treturn do_get_size_impl(\"stats.arenas.0.mapped\", arena_ind);\n}\n\nstatic void *\nthd_start(void *arg) {\n\tfor (unsigned next_epoch = 1; next_epoch < NEPOCHS; next_epoch++) {\n\t\t/* Busy-wait for next epoch. */\n\t\tunsigned cur_epoch;\n\t\tspin_t spinner = SPIN_INITIALIZER;\n\t\twhile ((cur_epoch = atomic_load_u(&epoch, ATOMIC_ACQUIRE)) !=\n\t\t    next_epoch) {\n\t\t\tspin_adaptive(&spinner);\n\t\t}\n\t\tassert_u_eq(cur_epoch, next_epoch, \"Unexpected epoch\");\n\n\t\t/*\n\t\t * Allocate.  The main thread will reset the arena, so there's\n\t\t * no need to deallocate.\n\t\t */\n\t\tfor (unsigned i = 0; i < PER_THD_NALLOCS; i++) {\n\t\t\tvoid *p = mallocx(sz, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE\n\t\t\t    );\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected mallocx() failure\\n\");\n\t\t}\n\n\t\t/* Let the main thread know we've finished this iteration. */\n\t\tatomic_fetch_add_u(&nfinished, 1, ATOMIC_RELEASE);\n\t}\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_retained) {\n\ttest_skip_if(!config_stats);\n\n\tarena_ind = do_arena_create(NULL);\n\tsz = nallocx(HUGEPAGE, 0);\n\tesz = sz + sz_large_pad;\n\n\tatomic_store_u(&epoch, 0, ATOMIC_RELAXED);\n\n\tunsigned nthreads = ncpus * 2;\n\tVARIABLE_ARRAY(thd_t, threads, nthreads);\n\tfor (unsigned i = 0; i < nthreads; i++) {\n\t\tthd_create(&threads[i], thd_start, NULL);\n\t}\n\n\tfor (unsigned e = 1; e < NEPOCHS; e++) {\n\t\tatomic_store_u(&nfinished, 0, ATOMIC_RELEASE);\n\t\tatomic_store_u(&epoch, e, ATOMIC_RELEASE);\n\n\t\t/* Wait for threads to finish allocating. */\n\t\tspin_t spinner = SPIN_INITIALIZER;\n\t\twhile (atomic_load_u(&nfinished, ATOMIC_ACQUIRE) < nthreads) {\n\t\t\tspin_adaptive(&spinner);\n\t\t}\n\n\t\t/*\n\t\t * Assert that retained is no more than the sum of size classes\n\t\t * that should have been used to satisfy the worker threads'\n\t\t * requests, discounting per growth fragmentation.\n\t\t */\n\t\tdo_refresh();\n\n\t\tsize_t allocated = esz * nthreads * PER_THD_NALLOCS;\n\t\tsize_t active = do_get_active(arena_ind);\n\t\tassert_zu_le(allocated, active, \"Unexpected active memory\");\n\t\tsize_t mapped = do_get_mapped(arena_ind);\n\t\tassert_zu_le(active, mapped, \"Unexpected mapped memory\");\n\n\t\tarena_t *arena = arena_get(tsdn_fetch(), arena_ind, false);\n\t\tsize_t usable = 0;\n\t\tsize_t fragmented = 0;\n\t\tfor (pszind_t pind = sz_psz2ind(HUGEPAGE); pind <\n\t\t    arena->extent_grow_next; pind++) {\n\t\t\tsize_t psz = sz_pind2sz(pind);\n\t\t\tsize_t psz_fragmented = psz % esz;\n\t\t\tsize_t psz_usable = psz - psz_fragmented;\n\t\t\t/*\n\t\t\t * Only consider size classes that wouldn't be skipped.\n\t\t\t */\n\t\t\tif (psz_usable > 0) {\n\t\t\t\tassert_zu_lt(usable, allocated,\n\t\t\t\t    \"Excessive retained memory \"\n\t\t\t\t    \"(%#zx[+%#zx] > %#zx)\", usable, psz_usable,\n\t\t\t\t    allocated);\n\t\t\t\tfragmented += psz_fragmented;\n\t\t\t\tusable += psz_usable;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Clean up arena.  Destroying and recreating the arena\n\t\t * is simpler that specifying extent hooks that deallocate\n\t\t * (rather than retaining) during reset.\n\t\t */\n\t\tdo_arena_destroy(arena_ind);\n\t\tassert_u_eq(do_arena_create(NULL), arena_ind,\n\t\t    \"Unexpected arena index\");\n\t}\n\n\tfor (unsigned i = 0; i < nthreads; i++) {\n\t\tthd_join(threads[i], NULL);\n\t}\n\n\tdo_arena_destroy(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_retained);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/rtree.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/rtree.h\"\n\nrtree_node_alloc_t *rtree_node_alloc_orig;\nrtree_node_dalloc_t *rtree_node_dalloc_orig;\nrtree_leaf_alloc_t *rtree_leaf_alloc_orig;\nrtree_leaf_dalloc_t *rtree_leaf_dalloc_orig;\n\n/* Potentially too large to safely place on the stack. */\nrtree_t test_rtree;\n\nstatic rtree_node_elm_t *\nrtree_node_alloc_intercept(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\trtree_node_elm_t *node;\n\n\tif (rtree != &test_rtree) {\n\t\treturn rtree_node_alloc_orig(tsdn, rtree, nelms);\n\t}\n\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\tnode = (rtree_node_elm_t *)calloc(nelms, sizeof(rtree_node_elm_t));\n\tassert_ptr_not_null(node, \"Unexpected calloc() failure\");\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\n\treturn node;\n}\n\nstatic void\nrtree_node_dalloc_intercept(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_node_elm_t *node) {\n\tif (rtree != &test_rtree) {\n\t\trtree_node_dalloc_orig(tsdn, rtree, node);\n\t\treturn;\n\t}\n\n\tfree(node);\n}\n\nstatic rtree_leaf_elm_t *\nrtree_leaf_alloc_intercept(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\trtree_leaf_elm_t *leaf;\n\n\tif (rtree != &test_rtree) {\n\t\treturn rtree_leaf_alloc_orig(tsdn, rtree, nelms);\n\t}\n\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\tleaf = (rtree_leaf_elm_t *)calloc(nelms, sizeof(rtree_leaf_elm_t));\n\tassert_ptr_not_null(leaf, \"Unexpected calloc() failure\");\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\n\treturn leaf;\n}\n\nstatic void\nrtree_leaf_dalloc_intercept(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_leaf_elm_t *leaf) {\n\tif (rtree != &test_rtree) {\n\t\trtree_leaf_dalloc_orig(tsdn, rtree, leaf);\n\t\treturn;\n\t}\n\n\tfree(leaf);\n}\n\nTEST_BEGIN(test_rtree_read_empty) {\n\ttsdn_t *tsdn;\n\n\ttsdn = tsdn_fetch();\n\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx, PAGE,\n\t    false), \"rtree_extent_read() should return NULL for empty tree\");\n\trtree_delete(tsdn, rtree);\n}\nTEST_END\n\n#undef NTHREADS\n#undef NITERS\n#undef SEED\n\nTEST_BEGIN(test_rtree_extrema) {\n\textent_t extent_a, extent_b;\n\textent_init(&extent_a, NULL, NULL, LARGE_MINCLASS, false,\n\t    sz_size2index(LARGE_MINCLASS), 0, extent_state_active, false,\n\t    false, true);\n\textent_init(&extent_b, NULL, NULL, 0, false, NSIZES, 0,\n\t    extent_state_active, false, false, true);\n\n\ttsdn_t *tsdn = tsdn_fetch();\n\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\n\tassert_false(rtree_write(tsdn, rtree, &rtree_ctx, PAGE, &extent_a,\n\t    extent_szind_get(&extent_a), extent_slab_get(&extent_a)),\n\t    \"Unexpected rtree_write() failure\");\n\trtree_szind_slab_update(tsdn, rtree, &rtree_ctx, PAGE,\n\t    extent_szind_get(&extent_a), extent_slab_get(&extent_a));\n\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx, PAGE, true),\n\t    &extent_a,\n\t    \"rtree_extent_read() should return previously set value\");\n\n\tassert_false(rtree_write(tsdn, rtree, &rtree_ctx, ~((uintptr_t)0),\n\t    &extent_b, extent_szind_get_maybe_invalid(&extent_b),\n\t    extent_slab_get(&extent_b)), \"Unexpected rtree_write() failure\");\n\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t    ~((uintptr_t)0), true), &extent_b,\n\t    \"rtree_extent_read() should return previously set value\");\n\n\trtree_delete(tsdn, rtree);\n}\nTEST_END\n\nTEST_BEGIN(test_rtree_bits) {\n\ttsdn_t *tsdn = tsdn_fetch();\n\n\tuintptr_t keys[] = {PAGE, PAGE + 1,\n\t    PAGE + (((uintptr_t)1) << LG_PAGE) - 1};\n\n\textent_t extent;\n\textent_init(&extent, NULL, NULL, 0, false, NSIZES, 0,\n\t    extent_state_active, false, false, true);\n\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\n\tfor (unsigned i = 0; i < sizeof(keys)/sizeof(uintptr_t); i++) {\n\t\tassert_false(rtree_write(tsdn, rtree, &rtree_ctx, keys[i],\n\t\t    &extent, NSIZES, false),\n\t\t    \"Unexpected rtree_write() failure\");\n\t\tfor (unsigned j = 0; j < sizeof(keys)/sizeof(uintptr_t); j++) {\n\t\t\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t\t    keys[j], true), &extent,\n\t\t\t    \"rtree_extent_read() should return previously set \"\n\t\t\t    \"value and ignore insignificant key bits; i=%u, \"\n\t\t\t    \"j=%u, set key=%#\"FMTxPTR\", get key=%#\"FMTxPTR, i,\n\t\t\t    j, keys[i], keys[j]);\n\t\t}\n\t\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    (((uintptr_t)2) << LG_PAGE), false),\n\t\t    \"Only leftmost rtree leaf should be set; i=%u\", i);\n\t\trtree_clear(tsdn, rtree, &rtree_ctx, keys[i]);\n\t}\n\n\trtree_delete(tsdn, rtree);\n}\nTEST_END\n\nTEST_BEGIN(test_rtree_random) {\n#define NSET 16\n#define SEED 42\n\tsfmt_t *sfmt = init_gen_rand(SEED);\n\ttsdn_t *tsdn = tsdn_fetch();\n\tuintptr_t keys[NSET];\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\n\textent_t extent;\n\textent_init(&extent, NULL, NULL, 0, false, NSIZES, 0,\n\t    extent_state_active, false, false, true);\n\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\tkeys[i] = (uintptr_t)gen_rand64(sfmt);\n\t\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree,\n\t\t    &rtree_ctx, keys[i], false, true);\n\t\tassert_ptr_not_null(elm,\n\t\t    \"Unexpected rtree_leaf_elm_lookup() failure\");\n\t\trtree_leaf_elm_write(tsdn, rtree, elm, &extent, NSIZES, false);\n\t\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true), &extent,\n\t\t    \"rtree_extent_read() should return previously set value\");\n\t}\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true), &extent,\n\t\t    \"rtree_extent_read() should return previously set value, \"\n\t\t    \"i=%u\", i);\n\t}\n\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\trtree_clear(tsdn, rtree, &rtree_ctx, keys[i]);\n\t\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true),\n\t\t   \"rtree_extent_read() should return previously set value\");\n\t}\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true),\n\t\t    \"rtree_extent_read() should return previously set value\");\n\t}\n\n\trtree_delete(tsdn, rtree);\n\tfini_gen_rand(sfmt);\n#undef NSET\n#undef SEED\n}\nTEST_END\n\nint\nmain(void) {\n\trtree_node_alloc_orig = rtree_node_alloc;\n\trtree_node_alloc = rtree_node_alloc_intercept;\n\trtree_node_dalloc_orig = rtree_node_dalloc;\n\trtree_node_dalloc = rtree_node_dalloc_intercept;\n\trtree_leaf_alloc_orig = rtree_leaf_alloc;\n\trtree_leaf_alloc = rtree_leaf_alloc_intercept;\n\trtree_leaf_dalloc_orig = rtree_leaf_dalloc;\n\trtree_leaf_dalloc = rtree_leaf_dalloc_intercept;\n\n\treturn test(\n\t    test_rtree_read_empty,\n\t    test_rtree_extrema,\n\t    test_rtree_bits,\n\t    test_rtree_random);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/size_classes.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic size_t\nget_max_size_class(void) {\n\tunsigned nlextents;\n\tsize_t mib[4];\n\tsize_t sz, miblen, max_size_class;\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nlextents\", (void *)&nlextents, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() error\");\n\n\tmiblen = sizeof(mib) / sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arenas.lextent.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() error\");\n\tmib[2] = nlextents - 1;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctlbymib() error\");\n\n\treturn max_size_class;\n}\n\nTEST_BEGIN(test_size_classes) {\n\tsize_t size_class, max_size_class;\n\tszind_t index, max_index;\n\n\tmax_size_class = get_max_size_class();\n\tmax_index = sz_size2index(max_size_class);\n\n\tfor (index = 0, size_class = sz_index2size(index); index < max_index ||\n\t    size_class < max_size_class; index++, size_class =\n\t    sz_index2size(index)) {\n\t\tassert_true(index < max_index,\n\t\t    \"Loop conditionals should be equivalent; index=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", index, size_class, size_class);\n\t\tassert_true(size_class < max_size_class,\n\t\t    \"Loop conditionals should be equivalent; index=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", index, size_class, size_class);\n\n\t\tassert_u_eq(index, sz_size2index(size_class),\n\t\t    \"sz_size2index() does not reverse sz_index2size(): index=%u\"\n\t\t    \" --> size_class=%zu --> index=%u --> size_class=%zu\",\n\t\t    index, size_class, sz_size2index(size_class),\n\t\t    sz_index2size(sz_size2index(size_class)));\n\t\tassert_zu_eq(size_class,\n\t\t    sz_index2size(sz_size2index(size_class)),\n\t\t    \"sz_index2size() does not reverse sz_size2index(): index=%u\"\n\t\t    \" --> size_class=%zu --> index=%u --> size_class=%zu\",\n\t\t    index, size_class, sz_size2index(size_class),\n\t\t    sz_index2size(sz_size2index(size_class)));\n\n\t\tassert_u_eq(index+1, sz_size2index(size_class+1),\n\t\t    \"Next size_class does not round up properly\");\n\n\t\tassert_zu_eq(size_class, (index > 0) ?\n\t\t    sz_s2u(sz_index2size(index-1)+1) : sz_s2u(1),\n\t\t    \"sz_s2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_s2u(size_class-1),\n\t\t    \"sz_s2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_s2u(size_class),\n\t\t    \"sz_s2u() does not compute same size class\");\n\t\tassert_zu_eq(sz_s2u(size_class+1), sz_index2size(index+1),\n\t\t    \"sz_s2u() does not round up to next size class\");\n\t}\n\n\tassert_u_eq(index, sz_size2index(sz_index2size(index)),\n\t    \"sz_size2index() does not reverse sz_index2size()\");\n\tassert_zu_eq(max_size_class, sz_index2size(\n\t    sz_size2index(max_size_class)),\n\t    \"sz_index2size() does not reverse sz_size2index()\");\n\n\tassert_zu_eq(size_class, sz_s2u(sz_index2size(index-1)+1),\n\t    \"sz_s2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_s2u(size_class-1),\n\t    \"sz_s2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_s2u(size_class),\n\t    \"sz_s2u() does not compute same size class\");\n}\nTEST_END\n\nTEST_BEGIN(test_psize_classes) {\n\tsize_t size_class, max_psz;\n\tpszind_t pind, max_pind;\n\n\tmax_psz = get_max_size_class() + PAGE;\n\tmax_pind = sz_psz2ind(max_psz);\n\n\tfor (pind = 0, size_class = sz_pind2sz(pind);\n\t    pind < max_pind || size_class < max_psz;\n\t    pind++, size_class = sz_pind2sz(pind)) {\n\t\tassert_true(pind < max_pind,\n\t\t    \"Loop conditionals should be equivalent; pind=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", pind, size_class, size_class);\n\t\tassert_true(size_class < max_psz,\n\t\t    \"Loop conditionals should be equivalent; pind=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", pind, size_class, size_class);\n\n\t\tassert_u_eq(pind, sz_psz2ind(size_class),\n\t\t    \"sz_psz2ind() does not reverse sz_pind2sz(): pind=%u -->\"\n\t\t    \" size_class=%zu --> pind=%u --> size_class=%zu\", pind,\n\t\t    size_class, sz_psz2ind(size_class),\n\t\t    sz_pind2sz(sz_psz2ind(size_class)));\n\t\tassert_zu_eq(size_class, sz_pind2sz(sz_psz2ind(size_class)),\n\t\t    \"sz_pind2sz() does not reverse sz_psz2ind(): pind=%u -->\"\n\t\t    \" size_class=%zu --> pind=%u --> size_class=%zu\", pind,\n\t\t    size_class, sz_psz2ind(size_class),\n\t\t    sz_pind2sz(sz_psz2ind(size_class)));\n\n\t\tassert_u_eq(pind+1, sz_psz2ind(size_class+1),\n\t\t    \"Next size_class does not round up properly\");\n\n\t\tassert_zu_eq(size_class, (pind > 0) ?\n\t\t    sz_psz2u(sz_pind2sz(pind-1)+1) : sz_psz2u(1),\n\t\t    \"sz_psz2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_psz2u(size_class-1),\n\t\t    \"sz_psz2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_psz2u(size_class),\n\t\t    \"sz_psz2u() does not compute same size class\");\n\t\tassert_zu_eq(sz_psz2u(size_class+1), sz_pind2sz(pind+1),\n\t\t    \"sz_psz2u() does not round up to next size class\");\n\t}\n\n\tassert_u_eq(pind, sz_psz2ind(sz_pind2sz(pind)),\n\t    \"sz_psz2ind() does not reverse sz_pind2sz()\");\n\tassert_zu_eq(max_psz, sz_pind2sz(sz_psz2ind(max_psz)),\n\t    \"sz_pind2sz() does not reverse sz_psz2ind()\");\n\n\tassert_zu_eq(size_class, sz_psz2u(sz_pind2sz(pind-1)+1),\n\t    \"sz_psz2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_psz2u(size_class-1),\n\t    \"sz_psz2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_psz2u(size_class),\n\t    \"sz_psz2u() does not compute same size class\");\n}\nTEST_END\n\nTEST_BEGIN(test_overflow) {\n\tsize_t max_size_class, max_psz;\n\n\tmax_size_class = get_max_size_class();\n\tmax_psz = max_size_class + PAGE;\n\n\tassert_u_eq(sz_size2index(max_size_class+1), NSIZES,\n\t    \"sz_size2index() should return NSIZES on overflow\");\n\tassert_u_eq(sz_size2index(ZU(PTRDIFF_MAX)+1), NSIZES,\n\t    \"sz_size2index() should return NSIZES on overflow\");\n\tassert_u_eq(sz_size2index(SIZE_T_MAX), NSIZES,\n\t    \"sz_size2index() should return NSIZES on overflow\");\n\n\tassert_zu_eq(sz_s2u(max_size_class+1), 0,\n\t    \"sz_s2u() should return 0 for unsupported size\");\n\tassert_zu_eq(sz_s2u(ZU(PTRDIFF_MAX)+1), 0,\n\t    \"sz_s2u() should return 0 for unsupported size\");\n\tassert_zu_eq(sz_s2u(SIZE_T_MAX), 0,\n\t    \"sz_s2u() should return 0 on overflow\");\n\n\tassert_u_eq(sz_psz2ind(max_size_class+1), NPSIZES,\n\t    \"sz_psz2ind() should return NPSIZES on overflow\");\n\tassert_u_eq(sz_psz2ind(ZU(PTRDIFF_MAX)+1), NPSIZES,\n\t    \"sz_psz2ind() should return NPSIZES on overflow\");\n\tassert_u_eq(sz_psz2ind(SIZE_T_MAX), NPSIZES,\n\t    \"sz_psz2ind() should return NPSIZES on overflow\");\n\n\tassert_zu_eq(sz_psz2u(max_size_class+1), max_psz,\n\t    \"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) for unsupported\"\n\t    \" size\");\n\tassert_zu_eq(sz_psz2u(ZU(PTRDIFF_MAX)+1), max_psz,\n\t    \"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) for unsupported \"\n\t    \"size\");\n\tassert_zu_eq(sz_psz2u(SIZE_T_MAX), max_psz,\n\t    \"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) on overflow\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_size_classes,\n\t    test_psize_classes,\n\t    test_overflow);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/slab.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_arena_slab_regind) {\n\tszind_t binind;\n\n\tfor (binind = 0; binind < NBINS; binind++) {\n\t\tsize_t regind;\n\t\textent_t slab;\n\t\tconst bin_info_t *bin_info = &bin_infos[binind];\n\t\textent_init(&slab, NULL, mallocx(bin_info->slab_size,\n\t\t    MALLOCX_LG_ALIGN(LG_PAGE)), bin_info->slab_size, true,\n\t\t    binind, 0, extent_state_active, false, true, true);\n\t\tassert_ptr_not_null(extent_addr_get(&slab),\n\t\t    \"Unexpected malloc() failure\");\n\t\tfor (regind = 0; regind < bin_info->nregs; regind++) {\n\t\t\tvoid *reg = (void *)((uintptr_t)extent_addr_get(&slab) +\n\t\t\t    (bin_info->reg_size * regind));\n\t\t\tassert_zu_eq(arena_slab_regind(&slab, binind, reg),\n\t\t\t    regind,\n\t\t\t    \"Incorrect region index computed for size %zu\",\n\t\t\t    bin_info->reg_size);\n\t\t}\n\t\tfree(extent_addr_get(&slab));\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_arena_slab_regind);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/smoothstep.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic const uint64_t smoothstep_tab[] = {\n#define STEP(step, h, x, y)\t\t\t\\\n\th,\n\tSMOOTHSTEP\n#undef STEP\n};\n\nTEST_BEGIN(test_smoothstep_integral) {\n\tuint64_t sum, min, max;\n\tunsigned i;\n\n\t/*\n\t * The integral of smoothstep in the [0..1] range equals 1/2.  Verify\n\t * that the fixed point representation's integral is no more than\n\t * rounding error distant from 1/2.  Regarding rounding, each table\n\t * element is rounded down to the nearest fixed point value, so the\n\t * integral may be off by as much as SMOOTHSTEP_NSTEPS ulps.\n\t */\n\tsum = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tsum += smoothstep_tab[i];\n\t}\n\n\tmax = (KQU(1) << (SMOOTHSTEP_BFP-1)) * (SMOOTHSTEP_NSTEPS+1);\n\tmin = max - SMOOTHSTEP_NSTEPS;\n\n\tassert_u64_ge(sum, min,\n\t    \"Integral too small, even accounting for truncation\");\n\tassert_u64_le(sum, max, \"Integral exceeds 1/2\");\n\tif (false) {\n\t\tmalloc_printf(\"%\"FMTu64\" ulps under 1/2 (limit %d)\\n\",\n\t\t    max - sum, SMOOTHSTEP_NSTEPS);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_smoothstep_monotonic) {\n\tuint64_t prev_h;\n\tunsigned i;\n\n\t/*\n\t * The smoothstep function is monotonic in [0..1], i.e. its slope is\n\t * non-negative.  In practice we want to parametrize table generation\n\t * such that piecewise slope is greater than zero, but do not require\n\t * that here.\n\t */\n\tprev_h = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tuint64_t h = smoothstep_tab[i];\n\t\tassert_u64_ge(h, prev_h, \"Piecewise non-monotonic, i=%u\", i);\n\t\tprev_h = h;\n\t}\n\tassert_u64_eq(smoothstep_tab[SMOOTHSTEP_NSTEPS-1],\n\t    (KQU(1) << SMOOTHSTEP_BFP), \"Last step must equal 1\");\n}\nTEST_END\n\nTEST_BEGIN(test_smoothstep_slope) {\n\tuint64_t prev_h, prev_delta;\n\tunsigned i;\n\n\t/*\n\t * The smoothstep slope strictly increases until x=0.5, and then\n\t * strictly decreases until x=1.0.  Verify the slightly weaker\n\t * requirement of monotonicity, so that inadequate table precision does\n\t * not cause false test failures.\n\t */\n\tprev_h = 0;\n\tprev_delta = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS / 2 + SMOOTHSTEP_NSTEPS % 2; i++) {\n\t\tuint64_t h = smoothstep_tab[i];\n\t\tuint64_t delta = h - prev_h;\n\t\tassert_u64_ge(delta, prev_delta,\n\t\t    \"Slope must monotonically increase in 0.0 <= x <= 0.5, \"\n\t\t    \"i=%u\", i);\n\t\tprev_h = h;\n\t\tprev_delta = delta;\n\t}\n\n\tprev_h = KQU(1) << SMOOTHSTEP_BFP;\n\tprev_delta = 0;\n\tfor (i = SMOOTHSTEP_NSTEPS-1; i >= SMOOTHSTEP_NSTEPS / 2; i--) {\n\t\tuint64_t h = smoothstep_tab[i];\n\t\tuint64_t delta = prev_h - h;\n\t\tassert_u64_ge(delta, prev_delta,\n\t\t    \"Slope must monotonically decrease in 0.5 <= x <= 1.0, \"\n\t\t    \"i=%u\", i);\n\t\tprev_h = h;\n\t\tprev_delta = delta;\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_smoothstep_integral,\n\t    test_smoothstep_monotonic,\n\t    test_smoothstep_slope);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/spin.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/spin.h\"\n\nTEST_BEGIN(test_spin) {\n\tspin_t spinner = SPIN_INITIALIZER;\n\n\tfor (unsigned i = 0; i < 100; i++) {\n\t\tspin_adaptive(&spinner);\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_spin);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/stats.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_stats_summary) {\n\tsize_t sz, allocated, active, resident, mapped;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.allocated\", (void *)&allocated, &sz, NULL,\n\t    0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.active\", (void *)&active, &sz, NULL, 0),\n\t    expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.resident\", (void *)&resident, &sz, NULL, 0),\n\t    expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.mapped\", (void *)&mapped, &sz, NULL, 0),\n\t    expected, \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_le(allocated, active,\n\t\t    \"allocated should be no larger than active\");\n\t\tassert_zu_lt(active, resident,\n\t\t    \"active should be less than resident\");\n\t\tassert_zu_lt(active, mapped,\n\t\t    \"active should be less than mapped\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_stats_large) {\n\tvoid *p;\n\tuint64_t epoch;\n\tsize_t allocated;\n\tuint64_t nmalloc, ndalloc, nrequests;\n\tsize_t sz;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tp = mallocx(SMALL_MAXCLASS+1, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.allocated\",\n\t    (void *)&allocated, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.nmalloc\", (void *)&nmalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.ndalloc\", (void *)&ndalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.nrequests\",\n\t    (void *)&nrequests, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_gt(allocated, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_le(nmalloc, nrequests,\n\t\t    \"nmalloc should no larger than nrequests\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas_summary) {\n\tvoid *little, *large;\n\tuint64_t epoch;\n\tsize_t sz;\n\tint expected = config_stats ? 0 : ENOENT;\n\tsize_t mapped;\n\tuint64_t dirty_npurge, dirty_nmadvise, dirty_purged;\n\tuint64_t muzzy_npurge, muzzy_nmadvise, muzzy_purged;\n\n\tlittle = mallocx(SMALL_MAXCLASS, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(little, \"Unexpected mallocx() failure\");\n\tlarge = mallocx((1U << LG_LARGE_MINCLASS), MALLOCX_ARENA(0));\n\tassert_ptr_not_null(large, \"Unexpected mallocx() failure\");\n\n\tdallocx(little, 0);\n\tdallocx(large, 0);\n\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.mapped\", (void *)&mapped, &sz, NULL,\n\t    0), expected, \"Unexepected mallctl() result\");\n\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.dirty_npurge\",\n\t    (void *)&dirty_npurge, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.dirty_nmadvise\",\n\t    (void *)&dirty_nmadvise, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.dirty_purged\",\n\t    (void *)&dirty_purged, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.muzzy_npurge\",\n\t    (void *)&muzzy_npurge, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.muzzy_nmadvise\",\n\t    (void *)&muzzy_nmadvise, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.muzzy_purged\",\n\t    (void *)&muzzy_purged, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\n\tif (config_stats) {\n\t\tif (!background_thread_enabled()) {\n\t\t\tassert_u64_gt(dirty_npurge + muzzy_npurge, 0,\n\t\t\t    \"At least one purge should have occurred\");\n\t\t}\n\t\tassert_u64_le(dirty_nmadvise, dirty_purged,\n\t\t    \"dirty_nmadvise should be no greater than dirty_purged\");\n\t\tassert_u64_le(muzzy_nmadvise, muzzy_purged,\n\t\t    \"muzzy_nmadvise should be no greater than muzzy_purged\");\n\t}\n}\nTEST_END\n\nvoid *\nthd_start(void *arg) {\n\treturn NULL;\n}\n\nstatic void\nno_lazy_lock(void) {\n\tthd_t thd;\n\n\tthd_create(&thd, thd_start, NULL);\n\tthd_join(thd, NULL);\n}\n\nTEST_BEGIN(test_stats_arenas_small) {\n\tvoid *p;\n\tsize_t sz, allocated;\n\tuint64_t epoch, nmalloc, ndalloc, nrequests;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tno_lazy_lock(); /* Lazy locking would dodge tcache testing. */\n\n\tp = mallocx(SMALL_MAXCLASS, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.allocated\",\n\t    (void *)&allocated, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.nmalloc\", (void *)&nmalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.ndalloc\", (void *)&ndalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.nrequests\",\n\t    (void *)&nrequests, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_gt(allocated, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be no greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_gt(nrequests, 0,\n\t\t    \"nrequests should be greater than zero\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas_large) {\n\tvoid *p;\n\tsize_t sz, allocated;\n\tuint64_t epoch, nmalloc, ndalloc;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tp = mallocx((1U << LG_LARGE_MINCLASS), MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.allocated\",\n\t    (void *)&allocated, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.nmalloc\", (void *)&nmalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.ndalloc\", (void *)&ndalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_gt(allocated, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nstatic void\ngen_mallctl_str(char *cmd, char *name, unsigned arena_ind) {\n\tsprintf(cmd, \"stats.arenas.%u.bins.0.%s\", arena_ind, name);\n}\n\nTEST_BEGIN(test_stats_arenas_bins) {\n\tvoid *p;\n\tsize_t sz, curslabs, curregs;\n\tuint64_t epoch, nmalloc, ndalloc, nrequests, nfills, nflushes;\n\tuint64_t nslabs, nreslabs;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\t/* Make sure allocation below isn't satisfied by tcache. */\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\n\tunsigned arena_ind, old_arena_ind;\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Arena creation failure\");\n\tsz = sizeof(arena_ind);\n\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t    (void *)&arena_ind, sizeof(arena_ind)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tp = malloc(bin_infos[0].reg_size);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tchar cmd[128];\n\tsz = sizeof(uint64_t);\n\tgen_mallctl_str(cmd, \"nmalloc\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nmalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"ndalloc\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&ndalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"nrequests\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nrequests, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(size_t);\n\tgen_mallctl_str(cmd, \"curregs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&curregs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tsz = sizeof(uint64_t);\n\tgen_mallctl_str(cmd, \"nfills\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nfills, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"nflushes\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nflushes, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tgen_mallctl_str(cmd, \"nslabs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nslabs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"nreslabs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nreslabs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(size_t);\n\tgen_mallctl_str(cmd, \"curslabs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&curslabs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_gt(nrequests, 0,\n\t\t    \"nrequests should be greater than zero\");\n\t\tassert_zu_gt(curregs, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tif (opt_tcache) {\n\t\t\tassert_u64_gt(nfills, 0,\n\t\t\t    \"At least one fill should have occurred\");\n\t\t\tassert_u64_gt(nflushes, 0,\n\t\t\t    \"At least one flush should have occurred\");\n\t\t}\n\t\tassert_u64_gt(nslabs, 0,\n\t\t    \"At least one slab should have been allocated\");\n\t\tassert_zu_gt(curslabs, 0,\n\t\t    \"At least one slab should be currently allocated\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas_lextents) {\n\tvoid *p;\n\tuint64_t epoch, nmalloc, ndalloc;\n\tsize_t curlextents, sz, hsize;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&hsize, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\n\tp = mallocx(hsize, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.lextents.0.nmalloc\",\n\t    (void *)&nmalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.lextents.0.ndalloc\",\n\t    (void *)&ndalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.lextents.0.curlextents\",\n\t    (void *)&curlextents, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_gt(curlextents, 0,\n\t\t    \"At least one extent should be currently allocated\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_stats_summary,\n\t    test_stats_large,\n\t    test_stats_arenas_summary,\n\t    test_stats_arenas_small,\n\t    test_stats_arenas_large,\n\t    test_stats_arenas_bins,\n\t    test_stats_arenas_lextents);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/stats_print.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\ntypedef enum {\n\tTOKEN_TYPE_NONE,\n\tTOKEN_TYPE_ERROR,\n\tTOKEN_TYPE_EOI,\n\tTOKEN_TYPE_NULL,\n\tTOKEN_TYPE_FALSE,\n\tTOKEN_TYPE_TRUE,\n\tTOKEN_TYPE_LBRACKET,\n\tTOKEN_TYPE_RBRACKET,\n\tTOKEN_TYPE_LBRACE,\n\tTOKEN_TYPE_RBRACE,\n\tTOKEN_TYPE_COLON,\n\tTOKEN_TYPE_COMMA,\n\tTOKEN_TYPE_STRING,\n\tTOKEN_TYPE_NUMBER\n} token_type_t;\n\ntypedef struct parser_s parser_t;\ntypedef struct {\n\tparser_t\t*parser;\n\ttoken_type_t\ttoken_type;\n\tsize_t\t\tpos;\n\tsize_t\t\tlen;\n\tsize_t\t\tline;\n\tsize_t\t\tcol;\n} token_t;\n\nstruct parser_s {\n\tbool verbose;\n\tchar\t*buf; /* '\\0'-terminated. */\n\tsize_t\tlen; /* Number of characters preceding '\\0' in buf. */\n\tsize_t\tpos;\n\tsize_t\tline;\n\tsize_t\tcol;\n\ttoken_t\ttoken;\n};\n\nstatic void\ntoken_init(token_t *token, parser_t *parser, token_type_t token_type,\n    size_t pos, size_t len, size_t line, size_t col) {\n\ttoken->parser = parser;\n\ttoken->token_type = token_type;\n\ttoken->pos = pos;\n\ttoken->len = len;\n\ttoken->line = line;\n\ttoken->col = col;\n}\n\nstatic void\ntoken_error(token_t *token) {\n\tif (!token->parser->verbose) {\n\t\treturn;\n\t}\n\tswitch (token->token_type) {\n\tcase TOKEN_TYPE_NONE:\n\t\tnot_reached();\n\tcase TOKEN_TYPE_ERROR:\n\t\tmalloc_printf(\"%zu:%zu: Unexpected character in token: \",\n\t\t    token->line, token->col);\n\t\tbreak;\n\tdefault:\n\t\tmalloc_printf(\"%zu:%zu: Unexpected token: \", token->line,\n\t\t    token->col);\n\t\tbreak;\n\t}\n\tUNUSED ssize_t err = malloc_write_fd(STDERR_FILENO,\n\t    &token->parser->buf[token->pos], token->len);\n\tmalloc_printf(\"\\n\");\n}\n\nstatic void\nparser_init(parser_t *parser, bool verbose) {\n\tparser->verbose = verbose;\n\tparser->buf = NULL;\n\tparser->len = 0;\n\tparser->pos = 0;\n\tparser->line = 1;\n\tparser->col = 0;\n}\n\nstatic void\nparser_fini(parser_t *parser) {\n\tif (parser->buf != NULL) {\n\t\tdallocx(parser->buf, MALLOCX_TCACHE_NONE);\n\t}\n}\n\nstatic bool\nparser_append(parser_t *parser, const char *str) {\n\tsize_t len = strlen(str);\n\tchar *buf = (parser->buf == NULL) ? mallocx(len + 1,\n\t    MALLOCX_TCACHE_NONE) : rallocx(parser->buf, parser->len + len + 1,\n\t    MALLOCX_TCACHE_NONE);\n\tif (buf == NULL) {\n\t\treturn true;\n\t}\n\tmemcpy(&buf[parser->len], str, len + 1);\n\tparser->buf = buf;\n\tparser->len += len;\n\treturn false;\n}\n\nstatic bool\nparser_tokenize(parser_t *parser) {\n\tenum {\n\t\tSTATE_START,\n\t\tSTATE_EOI,\n\t\tSTATE_N, STATE_NU, STATE_NUL, STATE_NULL,\n\t\tSTATE_F, STATE_FA, STATE_FAL, STATE_FALS, STATE_FALSE,\n\t\tSTATE_T, STATE_TR, STATE_TRU, STATE_TRUE,\n\t\tSTATE_LBRACKET,\n\t\tSTATE_RBRACKET,\n\t\tSTATE_LBRACE,\n\t\tSTATE_RBRACE,\n\t\tSTATE_COLON,\n\t\tSTATE_COMMA,\n\t\tSTATE_CHARS,\n\t\tSTATE_CHAR_ESCAPE,\n\t\tSTATE_CHAR_U, STATE_CHAR_UD, STATE_CHAR_UDD, STATE_CHAR_UDDD,\n\t\tSTATE_STRING,\n\t\tSTATE_MINUS,\n\t\tSTATE_LEADING_ZERO,\n\t\tSTATE_DIGITS,\n\t\tSTATE_DECIMAL,\n\t\tSTATE_FRAC_DIGITS,\n\t\tSTATE_EXP,\n\t\tSTATE_EXP_SIGN,\n\t\tSTATE_EXP_DIGITS,\n\t\tSTATE_ACCEPT\n\t} state = STATE_START;\n\tsize_t token_pos JEMALLOC_CC_SILENCE_INIT(0);\n\tsize_t token_line JEMALLOC_CC_SILENCE_INIT(1);\n\tsize_t token_col JEMALLOC_CC_SILENCE_INIT(0);\n\n\tassert_zu_le(parser->pos, parser->len,\n\t    \"Position is past end of buffer\");\n\n\twhile (state != STATE_ACCEPT) {\n\t\tchar c = parser->buf[parser->pos];\n\n\t\tswitch (state) {\n\t\tcase STATE_START:\n\t\t\ttoken_pos = parser->pos;\n\t\t\ttoken_line = parser->line;\n\t\t\ttoken_col = parser->col;\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\t\tbreak;\n\t\t\tcase '\\0':\n\t\t\t\tstate = STATE_EOI;\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\tstate = STATE_N;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\t\tstate = STATE_F;\n\t\t\t\tbreak;\n\t\t\tcase 't':\n\t\t\t\tstate = STATE_T;\n\t\t\t\tbreak;\n\t\t\tcase '[':\n\t\t\t\tstate = STATE_LBRACKET;\n\t\t\t\tbreak;\n\t\t\tcase ']':\n\t\t\t\tstate = STATE_RBRACKET;\n\t\t\t\tbreak;\n\t\t\tcase '{':\n\t\t\t\tstate = STATE_LBRACE;\n\t\t\t\tbreak;\n\t\t\tcase '}':\n\t\t\t\tstate = STATE_RBRACE;\n\t\t\t\tbreak;\n\t\t\tcase ':':\n\t\t\t\tstate = STATE_COLON;\n\t\t\t\tbreak;\n\t\t\tcase ',':\n\t\t\t\tstate = STATE_COMMA;\n\t\t\t\tbreak;\n\t\t\tcase '\"':\n\t\t\t\tstate = STATE_CHARS;\n\t\t\t\tbreak;\n\t\t\tcase '-':\n\t\t\t\tstate = STATE_MINUS;\n\t\t\t\tbreak;\n\t\t\tcase '0':\n\t\t\t\tstate = STATE_LEADING_ZERO;\n\t\t\t\tbreak;\n\t\t\tcase '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9':\n\t\t\t\tstate = STATE_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EOI:\n\t\t\ttoken_init(&parser->token, parser,\n\t\t\t    TOKEN_TYPE_EOI, token_pos, parser->pos -\n\t\t\t    token_pos, token_line, token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_N:\n\t\t\tswitch (c) {\n\t\t\tcase 'u':\n\t\t\t\tstate = STATE_NU;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_NU:\n\t\t\tswitch (c) {\n\t\t\tcase 'l':\n\t\t\t\tstate = STATE_NUL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_NUL:\n\t\t\tswitch (c) {\n\t\t\tcase 'l':\n\t\t\t\tstate = STATE_NULL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_NULL:\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\tcase '\\0':\n\t\t\tcase '[': case ']': case '{': case '}': case ':':\n\t\t\tcase ',':\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_NULL,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_F:\n\t\t\tswitch (c) {\n\t\t\tcase 'a':\n\t\t\t\tstate = STATE_FA;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FA:\n\t\t\tswitch (c) {\n\t\t\tcase 'l':\n\t\t\t\tstate = STATE_FAL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FAL:\n\t\t\tswitch (c) {\n\t\t\tcase 's':\n\t\t\t\tstate = STATE_FALS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FALS:\n\t\t\tswitch (c) {\n\t\t\tcase 'e':\n\t\t\t\tstate = STATE_FALSE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FALSE:\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\tcase '\\0':\n\t\t\tcase '[': case ']': case '{': case '}': case ':':\n\t\t\tcase ',':\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttoken_init(&parser->token, parser,\n\t\t\t    TOKEN_TYPE_FALSE, token_pos, parser->pos -\n\t\t\t    token_pos, token_line, token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_T:\n\t\t\tswitch (c) {\n\t\t\tcase 'r':\n\t\t\t\tstate = STATE_TR;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_TR:\n\t\t\tswitch (c) {\n\t\t\tcase 'u':\n\t\t\t\tstate = STATE_TRU;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_TRU:\n\t\t\tswitch (c) {\n\t\t\tcase 'e':\n\t\t\t\tstate = STATE_TRUE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_TRUE:\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\tcase '\\0':\n\t\t\tcase '[': case ']': case '{': case '}': case ':':\n\t\t\tcase ',':\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_TRUE,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_LBRACKET:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_LBRACKET,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_RBRACKET:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_RBRACKET,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_LBRACE:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_LBRACE,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_RBRACE:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_RBRACE,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_COLON:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_COLON,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_COMMA:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_COMMA,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_CHARS:\n\t\t\tswitch (c) {\n\t\t\tcase '\\\\':\n\t\t\t\tstate = STATE_CHAR_ESCAPE;\n\t\t\t\tbreak;\n\t\t\tcase '\"':\n\t\t\t\tstate = STATE_STRING;\n\t\t\t\tbreak;\n\t\t\tcase 0x00: case 0x01: case 0x02: case 0x03: case 0x04:\n\t\t\tcase 0x05: case 0x06: case 0x07: case 0x08: case 0x09:\n\t\t\tcase 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e:\n\t\t\tcase 0x0f: case 0x10: case 0x11: case 0x12: case 0x13:\n\t\t\tcase 0x14: case 0x15: case 0x16: case 0x17: case 0x18:\n\t\t\tcase 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d:\n\t\t\tcase 0x1e: case 0x1f:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_ESCAPE:\n\t\t\tswitch (c) {\n\t\t\tcase '\"': case '\\\\': case '/': case 'b': case 'n':\n\t\t\tcase 'r': case 't':\n\t\t\t\tstate = STATE_CHARS;\n\t\t\t\tbreak;\n\t\t\tcase 'u':\n\t\t\t\tstate = STATE_CHAR_U;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_U:\n\t\t\tswitch (c) {\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\tstate = STATE_CHAR_UD;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_UD:\n\t\t\tswitch (c) {\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\tstate = STATE_CHAR_UDD;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_UDD:\n\t\t\tswitch (c) {\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\tstate = STATE_CHAR_UDDD;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_UDDD:\n\t\t\tswitch (c) {\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\tstate = STATE_CHARS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_STRING:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_STRING,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_MINUS:\n\t\t\tswitch (c) {\n\t\t\tcase '0':\n\t\t\t\tstate = STATE_LEADING_ZERO;\n\t\t\t\tbreak;\n\t\t\tcase '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9':\n\t\t\t\tstate = STATE_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_LEADING_ZERO:\n\t\t\tswitch (c) {\n\t\t\tcase '.':\n\t\t\t\tstate = STATE_DECIMAL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_DIGITS:\n\t\t\tswitch (c) {\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\tbreak;\n\t\t\tcase '.':\n\t\t\t\tstate = STATE_DECIMAL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_DECIMAL:\n\t\t\tswitch (c) {\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\tstate = STATE_FRAC_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FRAC_DIGITS:\n\t\t\tswitch (c) {\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\tbreak;\n\t\t\tcase 'e': case 'E':\n\t\t\t\tstate = STATE_EXP;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EXP:\n\t\t\tswitch (c) {\n\t\t\tcase '-': case '+':\n\t\t\t\tstate = STATE_EXP_SIGN;\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\tstate = STATE_EXP_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EXP_SIGN:\n\t\t\tswitch (c) {\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\tstate = STATE_EXP_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EXP_DIGITS:\n\t\t\tswitch (c) {\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\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tnot_reached();\n\t\t}\n\n\t\tif (state != STATE_ACCEPT) {\n\t\t\tif (c == '\\n') {\n\t\t\t\tparser->line++;\n\t\t\t\tparser->col = 0;\n\t\t\t} else {\n\t\t\t\tparser->col++;\n\t\t\t}\n\t\t\tparser->pos++;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic bool\tparser_parse_array(parser_t *parser);\nstatic bool\tparser_parse_object(parser_t *parser);\n\nstatic bool\nparser_parse_value(parser_t *parser) {\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_NULL:\n\tcase TOKEN_TYPE_FALSE:\n\tcase TOKEN_TYPE_TRUE:\n\tcase TOKEN_TYPE_STRING:\n\tcase TOKEN_TYPE_NUMBER:\n\t\treturn false;\n\tcase TOKEN_TYPE_LBRACE:\n\t\treturn parser_parse_object(parser);\n\tcase TOKEN_TYPE_LBRACKET:\n\t\treturn parser_parse_array(parser);\n\tdefault:\n\t\treturn true;\n\t}\n\tnot_reached();\n}\n\nstatic bool\nparser_parse_pair(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,\n\t    \"Pair should start with string\");\n\tif (parser_tokenize(parser)) {\n\t\treturn true;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_COLON:\n\t\tif (parser_tokenize(parser)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn parser_parse_value(parser);\n\tdefault:\n\t\treturn true;\n\t}\n}\n\nstatic bool\nparser_parse_values(parser_t *parser) {\n\tif (parser_parse_value(parser)) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tif (parser_tokenize(parser)) {\n\t\t\treturn true;\n\t\t}\n\t\tswitch (parser->token.token_type) {\n\t\tcase TOKEN_TYPE_COMMA:\n\t\t\tif (parser_tokenize(parser)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (parser_parse_value(parser)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TOKEN_TYPE_RBRACKET:\n\t\t\treturn false;\n\t\tdefault:\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\nstatic bool\nparser_parse_array(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACKET,\n\t    \"Array should start with [\");\n\tif (parser_tokenize(parser)) {\n\t\treturn true;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_RBRACKET:\n\t\treturn false;\n\tdefault:\n\t\treturn parser_parse_values(parser);\n\t}\n\tnot_reached();\n}\n\nstatic bool\nparser_parse_pairs(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,\n\t    \"Object should start with string\");\n\tif (parser_parse_pair(parser)) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tif (parser_tokenize(parser)) {\n\t\t\treturn true;\n\t\t}\n\t\tswitch (parser->token.token_type) {\n\t\tcase TOKEN_TYPE_COMMA:\n\t\t\tif (parser_tokenize(parser)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tswitch (parser->token.token_type) {\n\t\t\tcase TOKEN_TYPE_STRING:\n\t\t\t\tif (parser_parse_pair(parser)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TOKEN_TYPE_RBRACE:\n\t\t\treturn false;\n\t\tdefault:\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\nstatic bool\nparser_parse_object(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACE,\n\t    \"Object should start with {\");\n\tif (parser_tokenize(parser)) {\n\t\treturn true;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_STRING:\n\t\treturn parser_parse_pairs(parser);\n\tcase TOKEN_TYPE_RBRACE:\n\t\treturn false;\n\tdefault:\n\t\treturn true;\n\t}\n\tnot_reached();\n}\n\nstatic bool\nparser_parse(parser_t *parser) {\n\tif (parser_tokenize(parser)) {\n\t\tgoto label_error;\n\t}\n\tif (parser_parse_value(parser)) {\n\t\tgoto label_error;\n\t}\n\n\tif (parser_tokenize(parser)) {\n\t\tgoto label_error;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_EOI:\n\t\treturn false;\n\tdefault:\n\t\tgoto label_error;\n\t}\n\tnot_reached();\n\nlabel_error:\n\ttoken_error(&parser->token);\n\treturn true;\n}\n\nTEST_BEGIN(test_json_parser) {\n\tsize_t i;\n\tconst char *invalid_inputs[] = {\n\t\t/* Tokenizer error case tests. */\n\t\t\"{ \\\"string\\\": X }\",\n\t\t\"{ \\\"string\\\": nXll }\",\n\t\t\"{ \\\"string\\\": nuXl }\",\n\t\t\"{ \\\"string\\\": nulX }\",\n\t\t\"{ \\\"string\\\": nullX }\",\n\t\t\"{ \\\"string\\\": fXlse }\",\n\t\t\"{ \\\"string\\\": faXse }\",\n\t\t\"{ \\\"string\\\": falXe }\",\n\t\t\"{ \\\"string\\\": falsX }\",\n\t\t\"{ \\\"string\\\": falseX }\",\n\t\t\"{ \\\"string\\\": tXue }\",\n\t\t\"{ \\\"string\\\": trXe }\",\n\t\t\"{ \\\"string\\\": truX }\",\n\t\t\"{ \\\"string\\\": trueX }\",\n\t\t\"{ \\\"string\\\": \\\"\\n\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\z\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\uX000\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\u0X00\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\u00X0\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\u000X\\\" }\",\n\t\t\"{ \\\"string\\\": -X }\",\n\t\t\"{ \\\"string\\\": 0.X }\",\n\t\t\"{ \\\"string\\\": 0.0eX }\",\n\t\t\"{ \\\"string\\\": 0.0e+X }\",\n\n\t\t/* Parser error test cases. */\n\t\t\"{\\\"string\\\": }\",\n\t\t\"{\\\"string\\\" }\",\n\t\t\"{\\\"string\\\": [ 0 }\",\n\t\t\"{\\\"string\\\": {\\\"a\\\":0, 1 } }\",\n\t\t\"{\\\"string\\\": {\\\"a\\\":0: } }\",\n\t\t\"{\",\n\t\t\"{}{\",\n\t};\n\tconst char *valid_inputs[] = {\n\t\t/* Token tests. */\n\t\t\"null\",\n\t\t\"false\",\n\t\t\"true\",\n\t\t\"{}\",\n\t\t\"{\\\"a\\\": 0}\",\n\t\t\"[]\",\n\t\t\"[0, 1]\",\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"10\",\n\t\t\"-10\",\n\t\t\"10.23\",\n\t\t\"10.23e4\",\n\t\t\"10.23e-4\",\n\t\t\"10.23e+4\",\n\t\t\"10.23E4\",\n\t\t\"10.23E-4\",\n\t\t\"10.23E+4\",\n\t\t\"-10.23\",\n\t\t\"-10.23e4\",\n\t\t\"-10.23e-4\",\n\t\t\"-10.23e+4\",\n\t\t\"-10.23E4\",\n\t\t\"-10.23E-4\",\n\t\t\"-10.23E+4\",\n\t\t\"\\\"value\\\"\",\n\t\t\"\\\" \\\\\\\" \\\\/ \\\\b \\\\n \\\\r \\\\t \\\\u0abc \\\\u1DEF \\\"\",\n\n\t\t/* Parser test with various nesting. */\n\t\t\"{\\\"a\\\":null, \\\"b\\\":[1,[{\\\"c\\\":2},3]], \\\"d\\\":{\\\"e\\\":true}}\",\n\t};\n\n\tfor (i = 0; i < sizeof(invalid_inputs)/sizeof(const char *); i++) {\n\t\tconst char *input = invalid_inputs[i];\n\t\tparser_t parser;\n\t\tparser_init(&parser, false);\n\t\tassert_false(parser_append(&parser, input),\n\t\t    \"Unexpected input appending failure\");\n\t\tassert_true(parser_parse(&parser),\n\t\t    \"Unexpected parse success for input: %s\", input);\n\t\tparser_fini(&parser);\n\t}\n\n\tfor (i = 0; i < sizeof(valid_inputs)/sizeof(const char *); i++) {\n\t\tconst char *input = valid_inputs[i];\n\t\tparser_t parser;\n\t\tparser_init(&parser, true);\n\t\tassert_false(parser_append(&parser, input),\n\t\t    \"Unexpected input appending failure\");\n\t\tassert_false(parser_parse(&parser),\n\t\t    \"Unexpected parse error for input: %s\", input);\n\t\tparser_fini(&parser);\n\t}\n}\nTEST_END\n\nvoid\nwrite_cb(void *opaque, const char *str) {\n\tparser_t *parser = (parser_t *)opaque;\n\tif (parser_append(parser, str)) {\n\t\ttest_fail(\"Unexpected input appending failure\");\n\t}\n}\n\nTEST_BEGIN(test_stats_print_json) {\n\tconst char *opts[] = {\n\t\t\"J\",\n\t\t\"Jg\",\n\t\t\"Jm\",\n\t\t\"Jd\",\n\t\t\"Jmd\",\n\t\t\"Jgd\",\n\t\t\"Jgm\",\n\t\t\"Jgmd\",\n\t\t\"Ja\",\n\t\t\"Jb\",\n\t\t\"Jl\",\n\t\t\"Jx\",\n\t\t\"Jbl\",\n\t\t\"Jal\",\n\t\t\"Jab\",\n\t\t\"Jabl\",\n\t\t\"Jax\",\n\t\t\"Jbx\",\n\t\t\"Jlx\",\n\t\t\"Jablx\",\n\t\t\"Jgmdablx\",\n\t};\n\tunsigned arena_ind, i;\n\n\tfor (i = 0; i < 3; i++) {\n\t\tunsigned j;\n\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\tbreak;\n\t\tcase 1: {\n\t\t\tsize_t sz = sizeof(arena_ind);\n\t\t\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind,\n\t\t\t    &sz, NULL, 0), 0, \"Unexpected mallctl failure\");\n\t\t\tbreak;\n\t\t} case 2: {\n\t\t\tsize_t mib[3];\n\t\t\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\t\t\tassert_d_eq(mallctlnametomib(\"arena.0.destroy\",\n\t\t\t    mib, &miblen), 0,\n\t\t\t    \"Unexpected mallctlnametomib failure\");\n\t\t\tmib[1] = arena_ind;\n\t\t\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL,\n\t\t\t    0), 0, \"Unexpected mallctlbymib failure\");\n\t\t\tbreak;\n\t\t} default:\n\t\t\tnot_reached();\n\t\t}\n\n\t\tfor (j = 0; j < sizeof(opts)/sizeof(const char *); j++) {\n\t\t\tparser_t parser;\n\n\t\t\tparser_init(&parser, true);\n\t\t\tmalloc_stats_print(write_cb, (void *)&parser, opts[j]);\n\t\t\tassert_false(parser_parse(&parser),\n\t\t\t    \"Unexpected parse error, opts=\\\"%s\\\"\", opts[j]);\n\t\t\tparser_fini(&parser);\n\t\t}\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_json_parser,\n\t    test_stats_print_json);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/ticker.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ticker.h\"\n\nTEST_BEGIN(test_ticker_tick) {\n#define NREPS 2\n#define NTICKS 3\n\tticker_t ticker;\n\tint32_t i, j;\n\n\tticker_init(&ticker, NTICKS);\n\tfor (i = 0; i < NREPS; i++) {\n\t\tfor (j = 0; j < NTICKS; j++) {\n\t\t\tassert_u_eq(ticker_read(&ticker), NTICKS - j,\n\t\t\t    \"Unexpected ticker value (i=%d, j=%d)\", i, j);\n\t\t\tassert_false(ticker_tick(&ticker),\n\t\t\t    \"Unexpected ticker fire (i=%d, j=%d)\", i, j);\n\t\t}\n\t\tassert_u32_eq(ticker_read(&ticker), 0,\n\t\t    \"Expected ticker depletion\");\n\t\tassert_true(ticker_tick(&ticker),\n\t\t    \"Expected ticker fire (i=%d)\", i);\n\t\tassert_u32_eq(ticker_read(&ticker), NTICKS,\n\t\t    \"Expected ticker reset\");\n\t}\n#undef NTICKS\n}\nTEST_END\n\nTEST_BEGIN(test_ticker_ticks) {\n#define NTICKS 3\n\tticker_t ticker;\n\n\tticker_init(&ticker, NTICKS);\n\n\tassert_u_eq(ticker_read(&ticker), NTICKS, \"Unexpected ticker value\");\n\tassert_false(ticker_ticks(&ticker, NTICKS), \"Unexpected ticker fire\");\n\tassert_u_eq(ticker_read(&ticker), 0, \"Unexpected ticker value\");\n\tassert_true(ticker_ticks(&ticker, NTICKS), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&ticker), NTICKS, \"Unexpected ticker value\");\n\n\tassert_true(ticker_ticks(&ticker, NTICKS + 1), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&ticker), NTICKS, \"Unexpected ticker value\");\n#undef NTICKS\n}\nTEST_END\n\nTEST_BEGIN(test_ticker_copy) {\n#define NTICKS 3\n\tticker_t ta, tb;\n\n\tticker_init(&ta, NTICKS);\n\tticker_copy(&tb, &ta);\n\tassert_u_eq(ticker_read(&tb), NTICKS, \"Unexpected ticker value\");\n\tassert_true(ticker_ticks(&tb, NTICKS + 1), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&tb), NTICKS, \"Unexpected ticker value\");\n\n\tticker_tick(&ta);\n\tticker_copy(&tb, &ta);\n\tassert_u_eq(ticker_read(&tb), NTICKS - 1, \"Unexpected ticker value\");\n\tassert_true(ticker_ticks(&tb, NTICKS), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&tb), NTICKS, \"Unexpected ticker value\");\n#undef NTICKS\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ticker_tick,\n\t    test_ticker_ticks,\n\t    test_ticker_copy);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/tsd.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic int data_cleanup_count;\n\nvoid\ndata_cleanup(int *data) {\n\tif (data_cleanup_count == 0) {\n\t\tassert_x_eq(*data, MALLOC_TSD_TEST_DATA_INIT,\n\t\t    \"Argument passed into cleanup function should match tsd \"\n\t\t    \"value\");\n\t}\n\t++data_cleanup_count;\n\n\t/*\n\t * Allocate during cleanup for two rounds, in order to assure that\n\t * jemalloc's internal tsd reinitialization happens.\n\t */\n\tbool reincarnate = false;\n\tswitch (*data) {\n\tcase MALLOC_TSD_TEST_DATA_INIT:\n\t\t*data = 1;\n\t\treincarnate = true;\n\t\tbreak;\n\tcase 1:\n\t\t*data = 2;\n\t\treincarnate = true;\n\t\tbreak;\n\tcase 2:\n\t\treturn;\n\tdefault:\n\t\tnot_reached();\n\t}\n\n\tif (reincarnate) {\n\t\tvoid *p = mallocx(1, 0);\n\t\tassert_ptr_not_null(p, \"Unexpeced mallocx() failure\");\n\t\tdallocx(p, 0);\n\t}\n}\n\nstatic void *\nthd_start(void *arg) {\n\tint d = (int)(uintptr_t)arg;\n\tvoid *p;\n\n\ttsd_t *tsd = tsd_fetch();\n\tassert_x_eq(tsd_test_data_get(tsd), MALLOC_TSD_TEST_DATA_INIT,\n\t    \"Initial tsd get should return initialization value\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\ttsd_test_data_set(tsd, d);\n\tassert_x_eq(tsd_test_data_get(tsd), d,\n\t    \"After tsd set, tsd get should return value that was set\");\n\n\td = 0;\n\tassert_x_eq(tsd_test_data_get(tsd), (int)(uintptr_t)arg,\n\t    \"Resetting local data should have no effect on tsd\");\n\n\ttsd_test_callback_set(tsd, &data_cleanup);\n\n\tfree(p);\n\treturn NULL;\n}\n\nTEST_BEGIN(test_tsd_main_thread) {\n\tthd_start((void *)(uintptr_t)0xa5f3e329);\n}\nTEST_END\n\nTEST_BEGIN(test_tsd_sub_thread) {\n\tthd_t thd;\n\n\tdata_cleanup_count = 0;\n\tthd_create(&thd, thd_start, (void *)MALLOC_TSD_TEST_DATA_INIT);\n\tthd_join(thd, NULL);\n\t/*\n\t * We reincarnate twice in the data cleanup, so it should execute at\n\t * least 3 times.\n\t */\n\tassert_x_ge(data_cleanup_count, 3,\n\t    \"Cleanup function should have executed multiple times.\");\n}\nTEST_END\n\nstatic void *\nthd_start_reincarnated(void *arg) {\n\ttsd_t *tsd = tsd_fetch();\n\tassert(tsd);\n\n\tvoid *p = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\t/* Manually trigger reincarnation. */\n\tassert_ptr_not_null(tsd_arena_get(tsd),\n\t    \"Should have tsd arena set.\");\n\ttsd_cleanup((void *)tsd);\n\tassert_ptr_null(*tsd_arenap_get_unsafe(tsd),\n\t    \"TSD arena should have been cleared.\");\n\tassert_u_eq(tsd->state, tsd_state_purgatory,\n\t    \"TSD state should be purgatory\\n\");\n\n\tfree(p);\n\tassert_u_eq(tsd->state, tsd_state_reincarnated,\n\t    \"TSD state should be reincarnated\\n\");\n\tp = mallocx(1, MALLOCX_TCACHE_NONE);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\tassert_ptr_null(*tsd_arenap_get_unsafe(tsd),\n\t    \"Should not have tsd arena set after reincarnation.\");\n\n\tfree(p);\n\ttsd_cleanup((void *)tsd);\n\tassert_ptr_null(*tsd_arenap_get_unsafe(tsd),\n\t    \"TSD arena should have been cleared after 2nd cleanup.\");\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_tsd_reincarnation) {\n\tthd_t thd;\n\tthd_create(&thd, thd_start_reincarnated, NULL);\n\tthd_join(thd, NULL);\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Ensure tsd bootstrapped. */\n\tif (nallocx(1, 0) == 0) {\n\t\tmalloc_printf(\"Initialization error\");\n\t\treturn test_status_fail;\n\t}\n\n\treturn test_no_reentrancy(\n\t    test_tsd_main_thread,\n\t    test_tsd_sub_thread,\n\t    test_tsd_reincarnation);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/witness.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic witness_lock_error_t *witness_lock_error_orig;\nstatic witness_owner_error_t *witness_owner_error_orig;\nstatic witness_not_owner_error_t *witness_not_owner_error_orig;\nstatic witness_depth_error_t *witness_depth_error_orig;\n\nstatic bool saw_lock_error;\nstatic bool saw_owner_error;\nstatic bool saw_not_owner_error;\nstatic bool saw_depth_error;\n\nstatic void\nwitness_lock_error_intercept(const witness_list_t *witnesses,\n    const witness_t *witness) {\n\tsaw_lock_error = true;\n}\n\nstatic void\nwitness_owner_error_intercept(const witness_t *witness) {\n\tsaw_owner_error = true;\n}\n\nstatic void\nwitness_not_owner_error_intercept(const witness_t *witness) {\n\tsaw_not_owner_error = true;\n}\n\nstatic void\nwitness_depth_error_intercept(const witness_list_t *witnesses,\n    witness_rank_t rank_inclusive, unsigned depth) {\n\tsaw_depth_error = true;\n}\n\nstatic int\nwitness_comp(const witness_t *a, void *oa, const witness_t *b, void *ob) {\n\tassert_u_eq(a->rank, b->rank, \"Witnesses should have equal rank\");\n\n\tassert(oa == (void *)a);\n\tassert(ob == (void *)b);\n\n\treturn strcmp(a->name, b->name);\n}\n\nstatic int\nwitness_comp_reverse(const witness_t *a, void *oa, const witness_t *b,\n    void *ob) {\n\tassert_u_eq(a->rank, b->rank, \"Witnesses should have equal rank\");\n\n\tassert(oa == (void *)a);\n\tassert(ob == (void *)b);\n\n\treturn -strcmp(a->name, b->name);\n}\n\nTEST_BEGIN(test_witness) {\n\twitness_t a, b;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 0);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\twitness_assert_not_owner(&witness_tsdn, &a);\n\twitness_lock(&witness_tsdn, &a);\n\twitness_assert_owner(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)2U, 0);\n\n\twitness_init(&b, \"b\", 2, NULL, NULL);\n\twitness_assert_not_owner(&witness_tsdn, &b);\n\twitness_lock(&witness_tsdn, &b);\n\twitness_assert_owner(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 2);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 2);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)2U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)3U, 0);\n\n\twitness_unlock(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)2U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)3U, 0);\n\twitness_unlock(&witness_tsdn, &b);\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_witness_comp) {\n\twitness_t a, b, c, d;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, witness_comp, &a);\n\twitness_assert_not_owner(&witness_tsdn, &a);\n\twitness_lock(&witness_tsdn, &a);\n\twitness_assert_owner(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\twitness_init(&b, \"b\", 1, witness_comp, &b);\n\twitness_assert_not_owner(&witness_tsdn, &b);\n\twitness_lock(&witness_tsdn, &b);\n\twitness_assert_owner(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 2);\n\twitness_unlock(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\twitness_lock_error_orig = witness_lock_error;\n\twitness_lock_error = witness_lock_error_intercept;\n\tsaw_lock_error = false;\n\n\twitness_init(&c, \"c\", 1, witness_comp_reverse, &c);\n\twitness_assert_not_owner(&witness_tsdn, &c);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\twitness_lock(&witness_tsdn, &c);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\twitness_unlock(&witness_tsdn, &c);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\tsaw_lock_error = false;\n\n\twitness_init(&d, \"d\", 1, NULL, NULL);\n\twitness_assert_not_owner(&witness_tsdn, &d);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\twitness_lock(&witness_tsdn, &d);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\twitness_unlock(&witness_tsdn, &d);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\twitness_unlock(&witness_tsdn, &a);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_lock_error = witness_lock_error_orig;\n}\nTEST_END\n\nTEST_BEGIN(test_witness_reversal) {\n\twitness_t a, b;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_lock_error_orig = witness_lock_error;\n\twitness_lock_error = witness_lock_error_intercept;\n\tsaw_lock_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\twitness_init(&b, \"b\", 2, NULL, NULL);\n\n\twitness_lock(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 1);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\twitness_lock(&witness_tsdn, &a);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\n\twitness_unlock(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\twitness_unlock(&witness_tsdn, &b);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_lock_error = witness_lock_error_orig;\n}\nTEST_END\n\nTEST_BEGIN(test_witness_recursive) {\n\twitness_t a;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_not_owner_error_orig = witness_not_owner_error;\n\twitness_not_owner_error = witness_not_owner_error_intercept;\n\tsaw_not_owner_error = false;\n\n\twitness_lock_error_orig = witness_lock_error;\n\twitness_lock_error = witness_lock_error_intercept;\n\tsaw_lock_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\n\twitness_lock(&witness_tsdn, &a);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\tassert_false(saw_not_owner_error, \"Unexpected witness not owner error\");\n\twitness_lock(&witness_tsdn, &a);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\tassert_true(saw_not_owner_error, \"Expected witness not owner error\");\n\n\twitness_unlock(&witness_tsdn, &a);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_owner_error = witness_owner_error_orig;\n\twitness_lock_error = witness_lock_error_orig;\n\n}\nTEST_END\n\nTEST_BEGIN(test_witness_unlock_not_owned) {\n\twitness_t a;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_owner_error_orig = witness_owner_error;\n\twitness_owner_error = witness_owner_error_intercept;\n\tsaw_owner_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\n\tassert_false(saw_owner_error, \"Unexpected owner error\");\n\twitness_unlock(&witness_tsdn, &a);\n\tassert_true(saw_owner_error, \"Expected owner error\");\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_owner_error = witness_owner_error_orig;\n}\nTEST_END\n\nTEST_BEGIN(test_witness_depth) {\n\twitness_t a;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_depth_error_orig = witness_depth_error;\n\twitness_depth_error = witness_depth_error_intercept;\n\tsaw_depth_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\n\tassert_false(saw_depth_error, \"Unexpected depth error\");\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\n\twitness_lock(&witness_tsdn, &a);\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\tassert_true(saw_depth_error, \"Expected depth error\");\n\n\twitness_unlock(&witness_tsdn, &a);\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\n\twitness_depth_error = witness_depth_error_orig;\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_witness,\n\t    test_witness_comp,\n\t    test_witness_reversal,\n\t    test_witness_recursive,\n\t    test_witness_unlock_not_owned,\n\t    test_witness_depth);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/zero.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\ntest_zero(size_t sz_min, size_t sz_max) {\n\tuint8_t *s;\n\tsize_t sz_prev, sz, i;\n#define MAGIC\t((uint8_t)0x61)\n\n\tsz_prev = 0;\n\ts = (uint8_t *)mallocx(sz_min, 0);\n\tassert_ptr_not_null((void *)s, \"Unexpected mallocx() failure\");\n\n\tfor (sz = sallocx(s, 0); sz <= sz_max;\n\t    sz_prev = sz, sz = sallocx(s, 0)) {\n\t\tif (sz_prev > 0) {\n\t\t\tassert_u_eq(s[0], MAGIC,\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    ZU(0), sz_prev);\n\t\t\tassert_u_eq(s[sz_prev-1], MAGIC,\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    sz_prev-1, sz_prev);\n\t\t}\n\n\t\tfor (i = sz_prev; i < sz; i++) {\n\t\t\tassert_u_eq(s[i], 0x0,\n\t\t\t    \"Newly allocated byte %zu/%zu isn't zero-filled\",\n\t\t\t    i, sz);\n\t\t\ts[i] = MAGIC;\n\t\t}\n\n\t\tif (xallocx(s, sz+1, 0, 0) == sz) {\n\t\t\ts = (uint8_t *)rallocx(s, sz+1, 0);\n\t\t\tassert_ptr_not_null((void *)s,\n\t\t\t    \"Unexpected rallocx() failure\");\n\t\t}\n\t}\n\n\tdallocx(s, 0);\n#undef MAGIC\n}\n\nTEST_BEGIN(test_zero_small) {\n\ttest_skip_if(!config_fill);\n\ttest_zero(1, SMALL_MAXCLASS-1);\n}\nTEST_END\n\nTEST_BEGIN(test_zero_large) {\n\ttest_skip_if(!config_fill);\n\ttest_zero(SMALL_MAXCLASS+1, (1U << (LG_LARGE_MINCLASS+1)));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_zero_small,\n\t    test_zero_large);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/jemalloc/test/unit/zero.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,junk:false,zero:true\"\nfi\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/linenoise/.gitignore",
    "content": "linenoise_example\n*.dSYM\nhistory.txt\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/linenoise/README.markdown",
    "content": "# Linenoise\n\nA minimal, zero-config, BSD licensed, readline replacement used in Redis,\nMongoDB, and Android.\n\n* Single and multi line editing mode with the usual key bindings implemented.\n* History handling.\n* Completion.\n* Hints (suggestions at the right of the prompt as you type).\n* About 1,100 lines of BSD license source code.\n* Only uses a subset of VT100 escapes (ANSI.SYS compatible).\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 (Real 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, supporting 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 basic VT100 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, and now can work even on ANSI.SYS compatible terminals, since no\nVT220 specific sequences are used anymore.\n\nThe library is currently about 1100 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 * Linux Buildroot ($TERM = vt100)\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 * ANSI.SYS\n * Emacs comint mode ($TERM = dumb)\n\nPlease test it everywhere you can and report back!\n\n## Let's push this forward!\n\nPatches should be provided in the respect of Linenoise sensibility for small\neasy to understand code.\n\nSend feedbacks to antirez at gmail\n\n# The API\n\nLinenoise is very easy to use, and reading the example shipped with the\nlibrary should get you up to speed ASAP. Here is a list of API calls\nand how to use them.\n\n    char *linenoise(const char *prompt);\n\nThis is the main Linenoise call: it shows the user a prompt with line editing\nand history capabilities. The prompt you specify is used as a prompt, that is,\nit will be printed to the left of the cursor. The library returns a buffer\nwith the line composed by the user, or NULL on end of file or when there\nis an out of memory condition.\n\nWhen a tty is detected (the user is actually typing into a terminal session)\nthe maximum editable line length is `LINENOISE_MAX_LINE`. When instead the\nstandard input is not a tty, which happens every time you redirect a file\nto a program, or use it in an Unix pipeline, there are no limits to the\nlength of the line that can be returned.\n\nThe returned line should be freed with the `free()` standard system call.\nHowever sometimes it could happen that your program uses a different dynamic\nallocation library, so you may also used `linenoiseFree` to make sure the\nline is freed with the same allocator it was created.\n\nThe canonical loop used by a program using Linenoise will be something like\nthis:\n\n    while((line = linenoise(\"hello> \")) != NULL) {\n        printf(\"You wrote: %s\\n\", line);\n        linenoiseFree(line); /* Or just free(line) if you use libc malloc. */\n    }\n\n## Single line VS multi line editing\n\nBy default, Linenoise uses single line editing, that is, a single row on the\nscreen will be used, and as the user types more, the text will scroll towards\nleft to make room. This works if your program is one where the user is\nunlikely to write a lot of text, otherwise multi line editing, where multiple\nscreens rows are used, can be a lot more comfortable.\n\nIn order to enable multi line editing use the following API call:\n\n    linenoiseSetMultiLine(1);\n\nYou can disable it using `0` as argument.\n\n## History\n\nLinenoise supporst history, so that the user does not have to retype\nagain and again the same things, but can use the down and up arrows in order\nto search and re-edit already inserted lines of text.\n\nThe followings are the history API calls:\n\n    int linenoiseHistoryAdd(const char *line);\n    int linenoiseHistorySetMaxLen(int len);\n    int linenoiseHistorySave(const char *filename);\n    int linenoiseHistoryLoad(const char *filename);\n\nUse `linenoiseHistoryAdd` every time you want to add a new element\nto the top of the history (it will be the first the user will see when\nusing the up arrow).\n\nNote that for history to work, you have to set a length for the history\n(which is zero by default, so history will be disabled if you don't set\na proper one). This is accomplished using the `linenoiseHistorySetMaxLen`\nfunction.\n\nLinenoise has direct support for persisting the history into an history\nfile. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do\njust that. Both functions return -1 on error and 0 on success.\n\n## Mask mode\n\nSometimes it is useful to allow the user to type passwords or other\nsecrets that should not be displayed. For such situations linenoise supports\na \"mask mode\" that will just replace the characters the user is typing \nwith `*` characters, like in the following example:\n\n    $ ./linenoise_example\n    hello> get mykey\n    echo: 'get mykey'\n    hello> /mask\n    hello> *********\n\nYou can enable and disable mask mode using the following two functions:\n\n    void linenoiseMaskModeEnable(void);\n    void linenoiseMaskModeDisable(void);\n\n## Completion\n\nLinenoise supports completion, which is the ability to complete the user\ninput when she or he presses the `<TAB>` key.\n\nIn order to use completion, you need to register a completion callback, which\nis called every time the user presses `<TAB>`. Your callback will return a\nlist of items that are completions for the current string.\n\nThe following is an example of registering a completion callback:\n\n    linenoiseSetCompletionCallback(completion);\n\nThe completion must be a function returning `void` and getting as input\na `const char` pointer, which is the line the user has typed so far, and\na `linenoiseCompletions` object pointer, which is used as argument of\n`linenoiseAddCompletion` in order to add completions inside the callback.\nAn example will make it more clear:\n\n    void completion(const char *buf, linenoiseCompletions *lc) {\n        if (buf[0] == 'h') {\n            linenoiseAddCompletion(lc,\"hello\");\n            linenoiseAddCompletion(lc,\"hello there\");\n        }\n    }\n\nBasically in your completion callback, you inspect the input, and return\na list of items that are good completions by using `linenoiseAddCompletion`.\n\nIf you want to test the completion feature, compile the example program\nwith `make`, run it, type `h` and press `<TAB>`.\n\n## Hints\n\nLinenoise has a feature called *hints* which is very useful when you\nuse Linenoise in order to implement a REPL (Read Eval Print Loop) for\na program that accepts commands and arguments, but may also be useful in\nother conditions.\n\nThe feature shows, on the right of the cursor, as the user types, hints that\nmay be useful. The hints can be displayed using a different color compared\nto the color the user is typing, and can also be bold.\n\nFor example as the user starts to type `\"git remote add\"`, with hints it's\npossible to show on the right of the prompt a string `<name> <url>`.\n\nThe feature works similarly to the history feature, using a callback.\nTo register the callback we use:\n\n    linenoiseSetHintsCallback(hints);\n\nThe callback itself is implemented like this:\n\n    char *hints(const char *buf, int *color, int *bold) {\n        if (!strcasecmp(buf,\"git remote add\")) {\n            *color = 35;\n            *bold = 0;\n            return \" <name> <url>\";\n        }\n        return NULL;\n    }\n\nThe callback function returns the string that should be displayed or NULL\nif no hint is available for the text the user currently typed. The returned\nstring will be trimmed as needed depending on the number of columns available\non the screen.\n\nIt is possible to return a string allocated in dynamic way, by also registering\na function to deallocate the hint string once used:\n\n    void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);\n\nThe free hint callback will just receive the pointer and free the string\nas needed (depending on how the hits callback allocated it).\n\nAs you can see in the example above, a `color` (in xterm color terminal codes)\ncan be provided together with a `bold` attribute. If no color is set, the\ncurrent terminal foreground color is used. If no bold attribute is set,\nnon-bold text is printed.\n\nColor codes are:\n\n    red = 31\n    green = 32\n    yellow = 33\n    blue = 34\n    magenta = 35\n    cyan = 36\n    white = 37;\n\n## Screen handling\n\nSometimes you may want to clear the screen as a result of something the\nuser typed. You can do this by calling the following function:\n\n    void linenoiseClearScreen(void);\n\n## Related projects\n\n* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language.\n* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift.\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/linenoise/example.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.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\nchar *hints(const char *buf, int *color, int *bold) {\n    if (!strcasecmp(buf,\"hello\")) {\n        *color = 35;\n        *bold = 0;\n        return \" World\";\n    }\n    return NULL;\n}\n\nint main(int argc, char **argv) {\n    char *line;\n    char *prgname = argv[0];\n\n    /* Parse options, with --multiline we enable multi line editing. */\n    while(argc > 1) {\n        argc--;\n        argv++;\n        if (!strcmp(*argv,\"--multiline\")) {\n            linenoiseSetMultiLine(1);\n            printf(\"Multi-line mode enabled.\\n\");\n        } else if (!strcmp(*argv,\"--keycodes\")) {\n            linenoisePrintKeyCodes();\n            exit(0);\n        } else {\n            fprintf(stderr, \"Usage: %s [--multiline] [--keycodes]\\n\", prgname);\n            exit(1);\n        }\n    }\n\n    /* Set the completion callback. This will be called every time the\n     * user uses the <tab> key. */\n    linenoiseSetCompletionCallback(completion);\n    linenoiseSetHintsCallback(hints);\n\n    /* Load history from file. The history file is just a plain text file\n     * where entries are separated by newlines. */\n    linenoiseHistoryLoad(\"history.txt\"); /* Load the history at startup */\n\n    /* Now this is the main loop of the typical linenoise-based application.\n     * The call to linenoise() will block as long as the user types something\n     * and presses enter.\n     *\n     * The typed string is returned as a malloc() allocated string by\n     * linenoise, so the user needs to free() it. */\n    \n    while((line = linenoise(\"hello> \")) != NULL) {\n        /* Do something with the string. */\n        if (line[0] != '\\0' && line[0] != '/') {\n            printf(\"echo: '%s'\\n\", line);\n            linenoiseHistoryAdd(line); /* Add to the history. */\n            linenoiseHistorySave(\"history.txt\"); /* Save the history on disk. */\n        } else if (!strncmp(line,\"/historylen\",11)) {\n            /* The \"/historylen\" command will change the history len. */\n            int len = atoi(line+11);\n            linenoiseHistorySetMaxLen(len);\n        } else if (!strncmp(line, \"/mask\", 5)) {\n            linenoiseMaskModeEnable();\n        } else if (!strncmp(line, \"/unmask\", 7)) {\n            linenoiseMaskModeDisable();\n        } else if (line[0] == '/') {\n            printf(\"Unreconized command: %s\\n\", line);\n        }\n        free(line);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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-2016, 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 * 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 n chars\n *\n * CUB (CUrsor Backward)\n *    Sequence: ESC [ n D\n *    Effect: moves cursor backward n chars\n *\n * The following is used to get the terminal width if getting\n * the width with the TIOCGWINSZ ioctl fails\n *\n * DSR (Device Status Report)\n *    Sequence: ESC [ 6 n\n *    Effect: reports the current cusor position as ESC [ n ; m R\n *            where n is the row and m is the column\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 * When linenoiseClearScreen() is called, two additional escape sequences\n * are used in order to clear the screen and position the cursor at home\n * position.\n *\n * CUP (Cursor position)\n *    Sequence: ESC [ H\n *    Effect: moves the cursor to upper left corner\n *\n * ED (Erase display)\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/stat.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;\nstatic linenoiseHintsCallback *hintsCallback = NULL;\nstatic linenoiseFreeHintsCallback *freeHintsCallback = NULL;\n\nstatic struct termios orig_termios; /* In order to restore at exit.*/\nstatic int maskmode = 0; /* Show \"***\" instead of input. For passwords. */\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/* Enable \"mask mode\". When it is enabled, instead of the input that\n * the user is typing, the terminal will just display a corresponding\n * number of asterisks, like \"****\". This is useful for passwords and other\n * secrets that should not be displayed. */\nvoid linenoiseMaskModeEnable(void) {\n    maskmode = 1;\n}\n\n/* Disable mask mode. */\nvoid linenoiseMaskModeDisable(void) {\n    maskmode = 0;\n}\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/* Register a hits function to be called to show hits to the user at the\n * right of the prompt. */\nvoid linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {\n    hintsCallback = fn;\n}\n\n/* Register a function to free the hints returned by the hints callback\n * registered with linenoiseSetHintsCallback(). */\nvoid linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {\n    freeHintsCallback = 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/* Helper of refreshSingleLine() and refreshMultiLine() to show hints\n * to the right of the prompt. */\nvoid refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {\n    char seq[64];\n    if (hintsCallback && plen+l->len < l->cols) {\n        int color = -1, bold = 0;\n        char *hint = hintsCallback(l->buf,&color,&bold);\n        if (hint) {\n            int hintlen = strlen(hint);\n            int hintmaxlen = l->cols-(plen+l->len);\n            if (hintlen > hintmaxlen) hintlen = hintmaxlen;\n            if (bold == 1 && color == -1) color = 37;\n            if (color != -1 || bold != 0)\n                snprintf(seq,64,\"\\033[%d;%d;49m\",bold,color);\n            else\n                seq[0] = '\\0';\n            abAppend(ab,seq,strlen(seq));\n            abAppend(ab,hint,hintlen);\n            if (color != -1 || bold != 0)\n                abAppend(ab,\"\\033[0m\",4);\n            /* Call the function to free the hint returned. */\n            if (freeHintsCallback) freeHintsCallback(hint);\n        }\n    }\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,\"\\r\");\n    abAppend(&ab,seq,strlen(seq));\n    /* Write the prompt and the current buffer content */\n    abAppend(&ab,l->prompt,strlen(l->prompt));\n    if (maskmode == 1) {\n        while (len--) abAppend(&ab,\"*\",1);\n    } else {\n        abAppend(&ab,buf,len);\n    }\n    /* Show hits if any. */\n    refreshShowHints(&ab,l,plen);\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,\"\\r\\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 col; /* colum position, zero-based. */\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,\"\\r\\x1b[0K\\x1b[1A\");\n        abAppend(&ab,seq,strlen(seq));\n    }\n\n    /* Clean the top line. */\n    lndebug(\"clear\");\n    snprintf(seq,64,\"\\r\\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    if (maskmode == 1) {\n        unsigned int i;\n        for (i = 0; i < l->len; i++) abAppend(&ab,\"*\",1);\n    } else {\n        abAppend(&ab,l->buf,l->len);\n    }\n\n    /* Show hits if any. */\n    refreshShowHints(&ab,l,plen);\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,\"\\r\");\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    col = (plen+(int)l->pos) % (int)l->cols;\n    lndebug(\"set col %d\", 1+col);\n    if (col)\n        snprintf(seq,64,\"\\r\\x1b[%dC\", col);\n    else\n        snprintf(seq,64,\"\\r\");\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 && !hintsCallback)) {\n                /* Avoid a full update of the line in the\n                 * trivial case. */\n                char d = (maskmode==1) ? '*' : c;\n                if (write(l->ofd,&d,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            if (mlmode) linenoiseEditMoveEnd(&l);\n            if (hintsCallback) {\n                /* Force a refresh without hints to leave the previous\n                 * line as the user typed it after a newline. */\n                linenoiseHintsCallback *hc = hintsCallback;\n                hintsCallback = NULL;\n                refreshLine(&l);\n                hintsCallback = hc;\n            }\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 if 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(\"\\r\"); /* 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\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    return count;\n}\n\n/* This function is called when linenoise() is called with the standard\n * input file descriptor not attached to a TTY. So for example when the\n * program using linenoise is called in pipe or with a file redirected\n * to its standard input. In this case, we want to be able to return the\n * line regardless of its length (by default we are limited to 4k). */\nstatic char *linenoiseNoTTY(void) {\n    char *line = NULL;\n    size_t len = 0, maxlen = 0;\n\n    while(1) {\n        if (len == maxlen) {\n            if (maxlen == 0) maxlen = 16;\n            maxlen *= 2;\n            char *oldval = line;\n            line = realloc(line,maxlen);\n            if (line == NULL) {\n                if (oldval) free(oldval);\n                return NULL;\n            }\n        }\n        int c = fgetc(stdin);\n        if (c == EOF || c == '\\n') {\n            if (c == EOF && len == 0) {\n                free(line);\n                return NULL;\n            } else {\n                line[len] = '\\0';\n                return line;\n            }\n        } else {\n            line[len] = c;\n            len++;\n        }\n    }\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 (!isatty(STDIN_FILENO)) {\n        /* Not a tty: read from file / pipe. In this mode we don't want any\n         * limit to the line size, so we call a function to handle that. */\n        return linenoiseNoTTY();\n    } else 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/* This is just a wrapper the user may want to call in order to make sure\n * the linenoise returned buffer is freed with the same allocator it was\n * created with. Useful when the main program is using an alternative\n * allocator. */\nvoid linenoiseFree(void *ptr) {\n    free(ptr);\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    mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);\n    FILE *fp;\n    int j;\n\n    fp = fopen(filename,\"w\");\n    umask(old_umask);\n    if (fp == NULL) return -1;\n    chmod(filename,S_IRUSR|S_IWUSR);\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": "Chapter02/redis-6.0.3/deps/linenoise/linenoise.h",
    "content": "/* linenoise.h -- VERSION 1.0\n *\n * Guerrilla line editing library against the idea that a line editing lib\n * needs to be 20,000 lines of C code.\n *\n * See linenoise.c for more information.\n *\n * ------------------------------------------------------------------------\n *\n * Copyright (c) 2010-2014, 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#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 *);\ntypedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);\ntypedef void(linenoiseFreeHintsCallback)(void *);\nvoid linenoiseSetCompletionCallback(linenoiseCompletionCallback *);\nvoid linenoiseSetHintsCallback(linenoiseHintsCallback *);\nvoid linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);\nvoid linenoiseAddCompletion(linenoiseCompletions *, const char *);\n\nchar *linenoise(const char *prompt);\nvoid linenoiseFree(void *ptr);\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);\nvoid linenoiseMaskModeEnable(void);\nvoid linenoiseMaskModeDisable(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __LINENOISE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 fpconv.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\tlua_bit.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": "Chapter02/redis-6.0.3/deps/lua/src/fpconv.c",
    "content": "/* fpconv - Floating point conversion routines\n *\n * Copyright (c) 2011-2012  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/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries\n * with locale support will break when the decimal separator is a comma.\n *\n * fpconv_* will around these issues with a translation buffer if required.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <string.h>\n\n#include \"fpconv.h\"\n\n/* Lua CJSON assumes the locale is the same for all threads within a\n * process and doesn't change after initialisation.\n *\n * This avoids the need for per thread storage or expensive checks\n * for call. */\nstatic char locale_decimal_point = '.';\n\n/* In theory multibyte decimal_points are possible, but\n * Lua CJSON only supports UTF-8 and known locales only have\n * single byte decimal points ([.,]).\n *\n * localconv() may not be thread safe (=>crash), and nl_langinfo() is\n * not supported on some platforms. Use sprintf() instead - if the\n * locale does change, at least Lua CJSON won't crash. */\nstatic void fpconv_update_locale()\n{\n    char buf[8];\n\n    snprintf(buf, sizeof(buf), \"%g\", 0.5);\n\n    /* Failing this test might imply the platform has a buggy dtoa\n     * implementation or wide characters */\n    if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {\n        fprintf(stderr, \"Error: wide characters found or printf() bug.\");\n        abort();\n    }\n\n    locale_decimal_point = buf[1];\n}\n\n/* Check for a valid number character: [-+0-9a-yA-Y.]\n * Eg: -0.6e+5, infinity, 0xF0.F0pF0\n *\n * Used to find the probable end of a number. It doesn't matter if\n * invalid characters are counted - strtod() will find the valid\n * number if it exists.  The risk is that slightly more memory might\n * be allocated before a parse error occurs. */\nstatic inline int valid_number_character(char ch)\n{\n    char lower_ch;\n\n    if ('0' <= ch && ch <= '9')\n        return 1;\n    if (ch == '-' || ch == '+' || ch == '.')\n        return 1;\n\n    /* Hex digits, exponent (e), base (p), \"infinity\",.. */\n    lower_ch = ch | 0x20;\n    if ('a' <= lower_ch && lower_ch <= 'y')\n        return 1;\n\n    return 0;\n}\n\n/* Calculate the size of the buffer required for a strtod locale\n * conversion. */\nstatic int strtod_buffer_size(const char *s)\n{\n    const char *p = s;\n\n    while (valid_number_character(*p))\n        p++;\n\n    return p - s;\n}\n\n/* Similar to strtod(), but must be passed the current locale's decimal point\n * character. Guaranteed to be called at the start of any valid number in a string */\ndouble fpconv_strtod(const char *nptr, char **endptr)\n{\n    char localbuf[FPCONV_G_FMT_BUFSIZE];\n    char *buf, *endbuf, *dp;\n    int buflen;\n    double value;\n\n    /* System strtod() is fine when decimal point is '.' */\n    if (locale_decimal_point == '.')\n        return strtod(nptr, endptr);\n\n    buflen = strtod_buffer_size(nptr);\n    if (!buflen) {\n        /* No valid characters found, standard strtod() return */\n        *endptr = (char *)nptr;\n        return 0;\n    }\n\n    /* Duplicate number into buffer */\n    if (buflen >= FPCONV_G_FMT_BUFSIZE) {\n        /* Handle unusually large numbers */\n        buf = malloc(buflen + 1);\n        if (!buf) {\n            fprintf(stderr, \"Out of memory\");\n            abort();\n        }\n    } else {\n        /* This is the common case.. */\n        buf = localbuf;\n    }\n    memcpy(buf, nptr, buflen);\n    buf[buflen] = 0;\n\n    /* Update decimal point character if found */\n    dp = strchr(buf, '.');\n    if (dp)\n        *dp = locale_decimal_point;\n\n    value = strtod(buf, &endbuf);\n    *endptr = (char *)&nptr[endbuf - buf];\n    if (buflen >= FPCONV_G_FMT_BUFSIZE)\n        free(buf);\n\n    return value;\n}\n\n/* \"fmt\" must point to a buffer of at least 6 characters */\nstatic void set_number_format(char *fmt, int precision)\n{\n    int d1, d2, i;\n\n    assert(1 <= precision && precision <= 14);\n\n    /* Create printf format (%.14g) from precision */\n    d1 = precision / 10;\n    d2 = precision % 10;\n    fmt[0] = '%';\n    fmt[1] = '.';\n    i = 2;\n    if (d1) {\n        fmt[i++] = '0' + d1;\n    }\n    fmt[i++] = '0' + d2;\n    fmt[i++] = 'g';\n    fmt[i] = 0;\n}\n\n/* Assumes there is always at least 32 characters available in the target buffer */\nint fpconv_g_fmt(char *str, double num, int precision)\n{\n    char buf[FPCONV_G_FMT_BUFSIZE];\n    char fmt[6];\n    int len;\n    char *b;\n\n    set_number_format(fmt, precision);\n\n    /* Pass through when decimal point character is dot. */\n    if (locale_decimal_point == '.')\n        return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);\n\n    /* snprintf() to a buffer then translate for other decimal point characters */\n    len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);\n\n    /* Copy into target location. Translate decimal point if required */\n    b = buf;\n    do {\n        *str++ = (*b == locale_decimal_point ? '.' : *b);\n    } while(*b++);\n\n    return len;\n}\n\nvoid fpconv_init()\n{\n    fpconv_update_locale();\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/lua/src/fpconv.h",
    "content": "/* Lua CJSON floating point conversion routines */\n\n/* Buffer required to store the largest string representation of a double.\n *\n * Longest double printed with %.14g is 21 characters long:\n * -1.7976931348623e+308 */\n# define FPCONV_G_FMT_BUFSIZE   32\n\n#ifdef USE_INTERNAL_FPCONV\nstatic inline void fpconv_init()\n{\n    /* Do nothing - not required */\n}\n#else\nextern void fpconv_init();\n#endif\n\nextern int fpconv_g_fmt(char*, double, int);\nextern double fpconv_strtod(const char*, char**);\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 = (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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/lua/src/lua_bit.c",
    "content": "/*\n** Lua BitOp -- a bit operations library for Lua 5.1/5.2.\n** http://bitop.luajit.org/\n**\n** Copyright (C) 2008-2012 Mike Pall. 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** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]\n*/\n\n#define LUA_BITOP_VERSION\t\"1.0.2\"\n\n#define LUA_LIB\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#ifdef _MSC_VER\n/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef unsigned __int64 uint64_t;\n#else\n#include <stdint.h>\n#endif\n\ntypedef int32_t SBits;\ntypedef uint32_t UBits;\n\ntypedef union {\n  lua_Number n;\n#ifdef LUA_NUMBER_DOUBLE\n  uint64_t b;\n#else\n  UBits b;\n#endif\n} BitNum;\n\n/* Convert argument to bit type. */\nstatic UBits barg(lua_State *L, int idx)\n{\n  BitNum bn;\n  UBits b;\n#if LUA_VERSION_NUM < 502\n  bn.n = lua_tonumber(L, idx);\n#else\n  bn.n = luaL_checknumber(L, idx);\n#endif\n#if defined(LUA_NUMBER_DOUBLE)\n  bn.n += 6755399441055744.0;  /* 2^52+2^51 */\n#ifdef SWAPPED_DOUBLE\n  b = (UBits)(bn.b >> 32);\n#else\n  b = (UBits)bn.b;\n#endif\n#elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \\\n      defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \\\n      defined(LUA_NUMBER_LLONG)\n  if (sizeof(UBits) == sizeof(lua_Number))\n    b = bn.b;\n  else\n    b = (UBits)(SBits)bn.n;\n#elif defined(LUA_NUMBER_FLOAT)\n#error \"A 'float' lua_Number type is incompatible with this library\"\n#else\n#error \"Unknown number type, check LUA_NUMBER_* in luaconf.h\"\n#endif\n#if LUA_VERSION_NUM < 502\n  if (b == 0 && !lua_isnumber(L, idx)) {\n    luaL_typerror(L, idx, \"number\");\n  }\n#endif\n  return b;\n}\n\n/* Return bit type. */\n#define BRET(b)  lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;\n\nstatic int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }\nstatic int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }\n\n#define BIT_OP(func, opr) \\\n  static int func(lua_State *L) { int i; UBits b = barg(L, 1); \\\n    for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) }\nBIT_OP(bit_band, &=)\nBIT_OP(bit_bor, |=)\nBIT_OP(bit_bxor, ^=)\n\n#define bshl(b, n)  (b << n)\n#define bshr(b, n)  (b >> n)\n#define bsar(b, n)  ((SBits)b >> n)\n#define brol(b, n)  ((b << n) | (b >> (32-n)))\n#define bror(b, n)  ((b << (32-n)) | (b >> n))\n#define BIT_SH(func, fn) \\\n  static int func(lua_State *L) { \\\n    UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }\nBIT_SH(bit_lshift, bshl)\nBIT_SH(bit_rshift, bshr)\nBIT_SH(bit_arshift, bsar)\nBIT_SH(bit_rol, brol)\nBIT_SH(bit_ror, bror)\n\nstatic int bit_bswap(lua_State *L)\n{\n  UBits b = barg(L, 1);\n  b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);\n  BRET(b)\n}\n\nstatic int bit_tohex(lua_State *L)\n{\n  UBits b = barg(L, 1);\n  SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);\n  const char *hexdigits = \"0123456789abcdef\";\n  char buf[8];\n  int i;\n  if (n < 0) { n = -n; hexdigits = \"0123456789ABCDEF\"; }\n  if (n > 8) n = 8;\n  for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }\n  lua_pushlstring(L, buf, (size_t)n);\n  return 1;\n}\n\nstatic const struct luaL_Reg bit_funcs[] = {\n  { \"tobit\",\tbit_tobit },\n  { \"bnot\",\tbit_bnot },\n  { \"band\",\tbit_band },\n  { \"bor\",\tbit_bor },\n  { \"bxor\",\tbit_bxor },\n  { \"lshift\",\tbit_lshift },\n  { \"rshift\",\tbit_rshift },\n  { \"arshift\",\tbit_arshift },\n  { \"rol\",\tbit_rol },\n  { \"ror\",\tbit_ror },\n  { \"bswap\",\tbit_bswap },\n  { \"tohex\",\tbit_tohex },\n  { NULL, NULL }\n};\n\n/* Signed right-shifts are implementation-defined per C89/C99.\n** But the de facto standard are arithmetic right-shifts on two's\n** complement CPUs. This behaviour is required here, so test for it.\n*/\n#define BAD_SAR\t\t(bsar(-8, 2) != (SBits)-2)\n\nLUALIB_API int luaopen_bit(lua_State *L)\n{\n  UBits b;\n  lua_pushnumber(L, (lua_Number)1437217655L);\n  b = barg(L, -1);\n  if (b != (UBits)1437217655L || BAD_SAR) {  /* Perform a simple self-test. */\n    const char *msg = \"compiled with incompatible luaconf.h\";\n#ifdef LUA_NUMBER_DOUBLE\n#ifdef _WIN32\n    if (b == (UBits)1610612736L)\n      msg = \"use D3DCREATE_FPU_PRESERVE with DirectX\";\n#endif\n    if (b == (UBits)1127743488L)\n      msg = \"not compiled with SWAPPED_DOUBLE\";\n#endif\n    if (BAD_SAR)\n      msg = \"arithmetic right-shift broken\";\n    luaL_error(L, \"bit library self-test failed (%s)\", msg);\n  }\n#if LUA_VERSION_NUM < 502\n  luaL_register(L, \"bit\", bit_funcs);\n#else\n  luaL_newlib(L, bit_funcs);\n#endif\n  return 1;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/deps/lua/src/lua_cjson.c",
    "content": "/* Lua CJSON - JSON support for Lua\n *\n * Copyright (c) 2010-2012  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 <limits.h>\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#include \"strbuf.h\"\n#include \"fpconv.h\"\n\n#include \"../../../src/solarisfixes.h\"\n\n#ifndef CJSON_MODNAME\n#define CJSON_MODNAME   \"cjson\"\n#endif\n\n#ifndef CJSON_VERSION\n#define CJSON_VERSION   \"2.1.0\"\n#endif\n\n/* Workaround for Solaris platforms missing isinf() */\n#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(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_ENCODE_MAX_DEPTH 1000\n#define DEFAULT_DECODE_MAX_DEPTH 1000\n#define DEFAULT_ENCODE_INVALID_NUMBERS 0\n#define DEFAULT_DECODE_INVALID_NUMBERS 1\n#define DEFAULT_ENCODE_KEEP_BUFFER 1\n#define DEFAULT_ENCODE_NUMBER_PRECISION 14\n\n#ifdef DISABLE_INVALID_NUMBERS\n#undef DEFAULT_DECODE_INVALID_NUMBERS\n#define DEFAULT_DECODE_INVALID_NUMBERS 0\n#endif\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\n    /* encode_buf is only allocated and used when\n     * encode_keep_buffer is set */\n    strbuf_t encode_buf;\n\n    int encode_sparse_convert;\n    int encode_sparse_ratio;\n    int encode_sparse_safe;\n    int encode_max_depth;\n    int encode_invalid_numbers;     /* 2 => Encode as \"null\" */\n    int encode_number_precision;\n    int encode_keep_buffer;\n\n    int decode_invalid_numbers;\n    int decode_max_depth;\n} json_config_t;\n\ntypedef struct {\n    const char *data;\n    const char *ptr;\n    strbuf_t *tmp;    /* Temporary storage for strings */\n    json_config_t *cfg;\n    int current_depth;\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\n/* ===== CONFIGURATION ===== */\n\nstatic json_config_t *json_fetch_config(lua_State *l)\n{\n    json_config_t *cfg;\n\n    cfg = lua_touserdata(l, lua_upvalueindex(1));\n    if (!cfg)\n        luaL_error(l, \"BUG: Unable to fetch CJSON configuration\");\n\n    return cfg;\n}\n\n/* Ensure the correct number of arguments have been provided.\n * Pad with nil to allow other functions to simply check arg[i]\n * to find whether an argument was provided */\nstatic json_config_t *json_arg_init(lua_State *l, int args)\n{\n    luaL_argcheck(l, lua_gettop(l) <= args, args + 1,\n                  \"found too many arguments\");\n\n    while (lua_gettop(l) < args)\n        lua_pushnil(l);\n\n    return json_fetch_config(l);\n}\n\n/* Process integer options for configuration functions */\nstatic int json_integer_option(lua_State *l, int optindex, int *setting,\n                               int min, int max)\n{\n    char errmsg[64];\n    int value;\n\n    if (!lua_isnil(l, optindex)) {\n        value = luaL_checkinteger(l, optindex);\n        snprintf(errmsg, sizeof(errmsg), \"expected integer between %d and %d\", min, max);\n        luaL_argcheck(l, min <= value && value <= max, 1, errmsg);\n        *setting = value;\n    }\n\n    lua_pushinteger(l, *setting);\n\n    return 1;\n}\n\n/* Process enumerated arguments for a configuration function */\nstatic int json_enum_option(lua_State *l, int optindex, int *setting,\n                            const char **options, int bool_true)\n{\n    static const char *bool_options[] = { \"off\", \"on\", NULL };\n\n    if (!options) {\n        options = bool_options;\n        bool_true = 1;\n    }\n\n    if (!lua_isnil(l, optindex)) {\n        if (bool_true && lua_isboolean(l, optindex))\n            *setting = lua_toboolean(l, optindex) * bool_true;\n        else\n            *setting = luaL_checkoption(l, optindex, NULL, options);\n    }\n\n    if (bool_true && (*setting == 0 || *setting == bool_true))\n        lua_pushboolean(l, *setting);\n    else\n        lua_pushstring(l, options[*setting]);\n\n    return 1;\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 = json_arg_init(l, 3);\n\n    json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1);\n    json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX);\n    json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX);\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 = json_arg_init(l, 1);\n\n    return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX);\n}\n\n/* Configures the maximum number of nested arrays/objects allowed when\n * encoding */\nstatic int json_cfg_decode_max_depth(lua_State *l)\n{\n    json_config_t *cfg = json_arg_init(l, 1);\n\n    return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX);\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 = json_arg_init(l, 1);\n\n    return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14);\n}\n\n/* Configures JSON encoding buffer persistence */\nstatic int json_cfg_encode_keep_buffer(lua_State *l)\n{\n    json_config_t *cfg = json_arg_init(l, 1);\n    int old_value;\n\n    old_value = cfg->encode_keep_buffer;\n\n    json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1);\n\n    /* Init / free the buffer if the setting has changed */\n    if (old_value ^ cfg->encode_keep_buffer) {\n        if (cfg->encode_keep_buffer)\n            strbuf_init(&cfg->encode_buf, 0);\n        else\n            strbuf_free(&cfg->encode_buf);\n    }\n\n    return 1;\n}\n\n#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV)\nvoid json_verify_invalid_number_setting(lua_State *l, int *setting)\n{\n    if (*setting == 1) {\n        *setting = 0;\n        luaL_error(l, \"Infinity, NaN, and/or hexadecimal numbers are not supported.\");\n    }\n}\n#else\n#define json_verify_invalid_number_setting(l, s)    do { } while(0)\n#endif\n\nstatic int json_cfg_encode_invalid_numbers(lua_State *l)\n{\n    static const char *options[] = { \"off\", \"on\", \"null\", NULL };\n    json_config_t *cfg = json_arg_init(l, 1);\n\n    json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1);\n\n    json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);\n\n    return 1;\n}\n\nstatic int json_cfg_decode_invalid_numbers(lua_State *l)\n{\n    json_config_t *cfg = json_arg_init(l, 1);\n\n    json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1);\n\n    json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);\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    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_ENCODE_MAX_DEPTH;\n    cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH;\n    cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS;\n    cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS;\n    cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER;\n    cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION;\n\n#if DEFAULT_ENCODE_KEEP_BUFFER > 0\n    strbuf_init(&cfg->encode_buf, 0);\n#endif\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/* ===== ENCODING ===== */\n\nstatic void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex,\n                                  const char *reason)\n{\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(json);\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, strbuf_t *json)\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, json, -1, \"excessively sparse array\");\n\n        return -1;\n    }\n\n    return max;\n}\n\nstatic void json_check_encode_depth(lua_State *l, json_config_t *cfg,\n                                    int current_depth, strbuf_t *json)\n{\n    /* Ensure there are enough slots free to traverse a table (key,\n     * value) and push a string for a potential error message.\n     *\n     * Unlike \"decode\", the key and value are still on the stack when\n     * lua_checkstack() is called.  Hence an extra slot for luaL_error()\n     * below is required just in case the next check to lua_checkstack()\n     * fails.\n     *\n     * While this won't cause a crash due to the EXTRA_STACK reserve\n     * slots, it would still be an improper use of the API. */\n    if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3))\n        return;\n\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(json);\n\n    luaL_error(l, \"Cannot serialise, excessive nesting (%d)\",\n               current_depth);\n}\n\nstatic void json_append_data(lua_State *l, json_config_t *cfg,\n                             int current_depth, 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, int current_depth,\n                              strbuf_t *json, int array_length)\n{\n    int comma, i;\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, current_depth, json);\n        lua_pop(l, 1);\n    }\n\n    strbuf_append_char(json, ']');\n}\n\nstatic void json_append_number(lua_State *l, json_config_t *cfg,\n                               strbuf_t *json, int lindex)\n{\n    double num = lua_tonumber(l, lindex);\n    int len;\n\n    if (cfg->encode_invalid_numbers == 0) {\n        /* Prevent encoding invalid numbers */\n        if (isinf(num) || isnan(num))\n            json_encode_exception(l, cfg, json, lindex, \"must not be NaN or Inf\");\n    } else if (cfg->encode_invalid_numbers == 1) {\n        /* Encode invalid numbers, but handle \"nan\" separately\n         * since some platforms may encode as \"-nan\". */\n        if (isnan(num)) {\n            strbuf_append_mem(json, \"nan\", 3);\n            return;\n        }\n    } else {\n        /* Encode invalid numbers as \"null\" */\n        if (isinf(num) || isnan(num)) {\n            strbuf_append_mem(json, \"null\", 4);\n            return;\n        }\n    }\n\n    strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE);\n    len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision);\n    strbuf_extend_length(json, len);\n}\n\nstatic void json_append_object(lua_State *l, json_config_t *cfg,\n                               int current_depth, strbuf_t *json)\n{\n    int comma, keytype;\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, cfg, json, -2);\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, json, -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, current_depth, json);\n        lua_pop(l, 1);\n        /* table, key */\n    }\n\n    strbuf_append_char(json, '}');\n}\n\n/* Serialise Lua data into JSON string. */\nstatic void json_append_data(lua_State *l, json_config_t *cfg,\n                             int current_depth, 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, cfg, json, -1);\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        current_depth++;\n        json_check_encode_depth(l, cfg, current_depth, json);\n        len = lua_array_length(l, cfg, json);\n        if (len > 0)\n            json_append_array(l, cfg, current_depth, json, len);\n        else\n            json_append_object(l, cfg, current_depth, 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, json, -1, \"type not supported\");\n        /* never returns */\n    }\n}\n\nstatic int json_encode(lua_State *l)\n{\n    json_config_t *cfg = json_fetch_config(l);\n    strbuf_t local_encode_buf;\n    strbuf_t *encode_buf;\n    char *json;\n    int len;\n\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    if (!cfg->encode_keep_buffer) {\n        /* Use private buffer */\n        encode_buf = &local_encode_buf;\n        strbuf_init(encode_buf, 0);\n    } else {\n        /* Reuse existing buffer */\n        encode_buf = &cfg->encode_buf;\n        strbuf_reset(encode_buf);\n    }\n\n    json_append_data(l, cfg, 0, encode_buf);\n    json = strbuf_string(encode_buf, &len);\n\n    lua_pushlstring(l, json, len);\n\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(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->ptr + 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->ptr + escape_len) != '\\\\' ||\n            *(json->ptr + escape_len + 1) != 'u') {\n            return -1;\n        }\n\n        /* Fetch the next codepoint */\n        surrogate_low = decode_hex4(json->ptr + 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->ptr += 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->ptr - json->data;\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->ptr == '\"');\n\n    /* Skip \" */\n    json->ptr++;\n\n    /* json->tmp is the temporary strbuf used to accumulate the\n     * decoded string value.\n     * json->tmp is sized to handle JSON containing only a string value.\n     */\n    strbuf_reset(json->tmp);\n\n    while ((ch = *json->ptr) != '\"') {\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->ptr + 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->ptr++;\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->ptr++;\n    }\n    json->ptr++;    /* 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 * - hexadecimal 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    const char *p = json->ptr;\n\n    /* Reject numbers starting with + */\n    if (*p == '+')\n        return 1;\n\n    /* Skip minus sign if it exists */\n    if (*p == '-')\n        p++;\n\n    /* Reject numbers starting with 0x, or leading zeros */\n    if (*p == '0') {\n        int ch2 = *(p + 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 (*p <= '9') {\n        return 0;                           /* Ordinary number */\n    }\n\n    /* Reject inf/nan */\n    if (!strncasecmp(p, \"inf\", 3))\n        return 1;\n    if (!strncasecmp(p, \"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    char *endptr;\n\n    token->type = T_NUMBER;\n    token->value.number = fpconv_strtod(json->ptr, &endptr);\n    if (json->ptr == endptr)\n        json_set_token_error(token, json, \"invalid number\");\n    else\n        json->ptr = endptr;     /* 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->ptr pointer at the error.\n */\nstatic void json_next_token(json_parse_t *json, json_token_t *token)\n{\n    const json_token_type_t *ch2token = json->cfg->ch2token;\n    int ch;\n\n    /* Eat whitespace. */\n    while (1) {\n        ch = (unsigned char)*(json->ptr);\n        token->type = ch2token[ch];\n        if (token->type != T_WHITESPACE)\n            break;\n        json->ptr++;\n    }\n\n    /* Store location of new token. Required when throwing errors\n     * for unexpected tokens (syntax errors). */\n    token->index = json->ptr - json->data;\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->ptr++;\n        return;\n    }\n\n    /* Process characters which triggered T_UNKNOWN\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_invalid_numbers && 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->ptr, \"true\", 4)) {\n        token->type = T_BOOLEAN;\n        token->value.boolean = 1;\n        json->ptr += 4;\n        return;\n    } else if (!strncmp(json->ptr, \"false\", 5)) {\n        token->type = T_BOOLEAN;\n        token->value.boolean = 0;\n        json->ptr += 5;\n        return;\n    } else if (!strncmp(json->ptr, \"null\", 4)) {\n        token->type = T_NULL;\n        json->ptr += 4;\n        return;\n    } else if (json->cfg->decode_invalid_numbers &&\n               json_is_invalid_number(json)) {\n        /* When decode_invalid_numbers is enabled, 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 inline void json_decode_ascend(json_parse_t *json)\n{\n    json->current_depth--;\n}\n\nstatic void json_decode_descend(lua_State *l, json_parse_t *json, int slots)\n{\n    json->current_depth++;\n\n    if (json->current_depth <= json->cfg->decode_max_depth &&\n        lua_checkstack(l, slots)) {\n        return;\n    }\n\n    strbuf_free(json->tmp);\n    luaL_error(l, \"Found too many nested data structures (%d) at character %d\",\n        json->current_depth, json->ptr - json->data);\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_descend(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        json_decode_ascend(json);\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            json_decode_ascend(json);\n            return;\n        }\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_descend(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        json_decode_ascend(json);\n        return;\n    }\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            json_decode_ascend(json);\n            return;\n        }\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\nstatic int json_decode(lua_State *l)\n{\n    json_parse_t json;\n    json_token_t token;\n    size_t json_len;\n\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    json.cfg = json_fetch_config(l);\n    json.data = luaL_checklstring(l, 1, &json_len);\n    json.current_depth = 0;\n    json.ptr = json.data;\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 (json_len >= 2 && (!json.data[0] || !json.data[1]))\n        luaL_error(l, \"JSON parser does not support UTF-16 or UTF-32\");\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    return 1;\n}\n\n/* ===== INITIALISATION ===== */\n\n#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502\n/* Compatibility for Lua 5.1.\n *\n * luaL_setfuncs() is used to create a module table where the functions have\n * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */\nstatic void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup)\n{\n    int i;\n\n    luaL_checkstack(l, nup, \"too many upvalues\");\n    for (; reg->name != NULL; reg++) {  /* fill the table with given functions */\n        for (i = 0; i < nup; i++)  /* copy upvalues to the top */\n            lua_pushvalue(l, -nup);\n        lua_pushcclosure(l, reg->func, nup);  /* closure with those upvalues */\n        lua_setfield(l, -(nup + 2), reg->name);\n    }\n    lua_pop(l, nup);  /* remove upvalues */\n}\n#endif\n\n/* Call target function in protected mode with all supplied args.\n * Assumes target function only returns a single non-nil value.\n * Convert and return thrown errors as: nil, \"error message\" */\nstatic int json_protect_conversion(lua_State *l)\n{\n    int err;\n\n    /* Deliberately throw an error for invalid arguments */\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    /* pcall() the function stored as upvalue(1) */\n    lua_pushvalue(l, lua_upvalueindex(1));\n    lua_insert(l, 1);\n    err = lua_pcall(l, 1, 1, 0);\n    if (!err)\n        return 1;\n\n    if (err == LUA_ERRRUN) {\n        lua_pushnil(l);\n        lua_insert(l, -2);\n        return 2;\n    }\n\n    /* Since we are not using a custom error handler, the only remaining\n     * errors are memory related */\n    return luaL_error(l, \"Memory allocation error in CJSON protected call\");\n}\n\n/* Return cjson module table */\nstatic int lua_cjson_new(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        { \"decode_max_depth\", json_cfg_decode_max_depth },\n        { \"encode_number_precision\", json_cfg_encode_number_precision },\n        { \"encode_keep_buffer\", json_cfg_encode_keep_buffer },\n        { \"encode_invalid_numbers\", json_cfg_encode_invalid_numbers },\n        { \"decode_invalid_numbers\", json_cfg_decode_invalid_numbers },\n        { \"new\", lua_cjson_new },\n        { NULL, NULL }\n    };\n\n    /* Initialise number conversions */\n    fpconv_init();\n\n    /* cjson module table */\n    lua_newtable(l);\n\n    /* Register functions with config data as upvalue */\n    json_create_config(l);\n    luaL_setfuncs(l, reg, 1);\n\n    /* Set cjson.null */\n    lua_pushlightuserdata(l, NULL);\n    lua_setfield(l, -2, \"null\");\n\n    /* Set module name / version fields */\n    lua_pushliteral(l, CJSON_MODNAME);\n    lua_setfield(l, -2, \"_NAME\");\n    lua_pushliteral(l, CJSON_VERSION);\n    lua_setfield(l, -2, \"_VERSION\");\n\n    return 1;\n}\n\n/* Return cjson.safe module table */\nstatic int lua_cjson_safe_new(lua_State *l)\n{\n    const char *func[] = { \"decode\", \"encode\", NULL };\n    int i;\n\n    lua_cjson_new(l);\n\n    /* Fix new() method */\n    lua_pushcfunction(l, lua_cjson_safe_new);\n    lua_setfield(l, -2, \"new\");\n\n    for (i = 0; func[i]; i++) {\n        lua_getfield(l, -1, func[i]);\n        lua_pushcclosure(l, json_protect_conversion, 1);\n        lua_setfield(l, -2, func[i]);\n    }\n\n    return 1;\n}\n\nint luaopen_cjson(lua_State *l)\n{\n    lua_cjson_new(l);\n\n#ifdef ENABLE_CJSON_GLOBAL\n    /* Register a global \"cjson\" table. */\n    lua_pushvalue(l, -1);\n    lua_setglobal(l, CJSON_MODNAME);\n#endif\n\n    /* Return cjson table */\n    return 1;\n}\n\nint luaopen_cjson_safe(lua_State *l)\n{\n    lua_cjson_safe_new(l);\n\n    /* Return cjson.safe table */\n    return 1;\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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_NAME        \"cmsgpack\"\n#define LUACMSGPACK_SAFE_NAME   \"cmsgpack_safe\"\n#define LUACMSGPACK_VERSION     \"lua-cmsgpack 0.4.0\"\n#define LUACMSGPACK_COPYRIGHT   \"Copyright (C) 2012, Salvatore Sanfilippo\"\n#define LUACMSGPACK_DESCRIPTION \"MessagePack C implementation for Lua\"\n\n/* Allows a preprocessor directive to override MAX_NESTING */\n#ifndef LUACMSGPACK_MAX_NESTING\n    #define LUACMSGPACK_MAX_NESTING  16 /* Max tables nesting. */\n#endif\n\n/* Check if float or double can be an integer without loss of precision */\n#define IS_INT_TYPE_EQUIVALENT(x, T) (!isinf(x) && (T)(x) == (x))\n\n#define IS_INT64_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int64_t)\n#define IS_INT_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int)\n\n/* If size of pointer is equal to a 4 byte integer, we're on 32 bits. */\n#if UINTPTR_MAX == UINT_MAX\n    #define BITS_32 1\n#else\n    #define BITS_32 0\n#endif\n\n#if BITS_32\n    #define lua_pushunsigned(L, n) lua_pushnumber(L, n)\n#else\n    #define lua_pushunsigned(L, n) lua_pushinteger(L, n)\n#endif\n\n/* =============================================================================\n * MessagePack implementation and bindings for Lua 5.1/5.2.\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 * 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix.\n * 07-Apr-2014 (ver 0.4.0): Multiple pack/unpack, lua allocator, efficiency.\n * ========================================================================== */\n\n/* -------------------------- Endian conversion --------------------------------\n * We use it only for floats and doubles, all the other conversions performed\n * in an endian independent fashion. So the only thing we need is a function\n * that swaps a binary string if 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 check for endianess at runtime.\n * The performance difference should be acceptable. */\nvoid memrevifle(void *ptr, size_t len) {\n    unsigned char   *p = (unsigned char *)ptr,\n                    *e = (unsigned char *)p+len-1,\n                    aux;\n    int test = 1;\n    unsigned char *testp = (unsigned char*) &test;\n\n    if (testp[0] == 0) return; /* Big endian, nothing 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 operation\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\nvoid *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {\n    void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL;\n    void *ud;\n\n    local_realloc = lua_getallocf(L, &ud);\n\n    return local_realloc(ud, target, osize, nsize);\n}\n\nmp_buf *mp_buf_new(lua_State *L) {\n    mp_buf *buf = NULL;\n\n    /* Old size = 0; new size = sizeof(*buf) */\n    buf = (mp_buf*)mp_realloc(L, NULL, 0, sizeof(*buf));\n\n    buf->b = NULL;\n    buf->len = buf->free = 0;\n    return buf;\n}\n\nvoid mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {\n    if (buf->free < len) {\n        size_t newsize = (buf->len+len)*2;\n\n        buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);\n        buf->free = newsize - buf->len;\n    }\n    memcpy(buf->b+buf->len,s,len);\n    buf->len += len;\n    buf->free -= len;\n}\n\nvoid mp_buf_free(lua_State *L, mp_buf *buf) {\n    mp_realloc(L, buf->b, buf->len + buf->free, 0); /* realloc to 0 = free */\n    mp_realloc(L, buf, sizeof(*buf), 0);\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 operation. */\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\nvoid mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {\n    cursor->p = s;\n    cursor->left = len;\n    cursor->err = MP_CUR_ERROR_NONE;\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\nvoid mp_encode_bytes(lua_State *L, 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 <= 0xff) {\n        hdr[0] = 0xd9;\n        hdr[1] = len;\n        hdrlen = 2;\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(L,buf,hdr,hdrlen);\n    mp_buf_append(L,buf,s,len);\n}\n\n/* we assume IEEE 754 internal format for single and double precision floats. */\nvoid mp_encode_double(lua_State *L, 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(L,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(L,buf,b,9);\n    }\n}\n\nvoid mp_encode_int(lua_State *L, 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] = ((signed 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(L,buf,b,enclen);\n}\n\nvoid mp_encode_array(lua_State *L, 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(L,buf,b,enclen);\n}\n\nvoid mp_encode_map(lua_State *L, 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(L,buf,b,enclen);\n}\n\n/* --------------------------- Lua types encoding --------------------------- */\n\nvoid 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(L,buf,(const unsigned char*)s,len);\n}\n\nvoid mp_encode_lua_bool(lua_State *L, mp_buf *buf) {\n    unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;\n    mp_buf_append(L,buf,&b,1);\n}\n\n/* Lua 5.3 has a built in 64-bit integer type */\nvoid mp_encode_lua_integer(lua_State *L, mp_buf *buf) {\n#if (LUA_VERSION_NUM < 503) && BITS_32\n    lua_Number i = lua_tonumber(L,-1);\n#else\n    lua_Integer i = lua_tointeger(L,-1);\n#endif\n    mp_encode_int(L, buf, (int64_t)i);\n}\n\n/* Lua 5.2 and lower only has 64-bit doubles, so we need to\n * detect if the double may be representable as an int\n * for Lua < 5.3 */\nvoid mp_encode_lua_number(lua_State *L, mp_buf *buf) {\n    lua_Number n = lua_tonumber(L,-1);\n\n    if (IS_INT64_EQUIVALENT(n)) {\n        mp_encode_lua_integer(L, buf);\n    } else {\n        mp_encode_double(L,buf,(double)n);\n    }\n}\n\nvoid mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);\n\n/* Convert a lua table into a message pack list. */\nvoid mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {\n#if LUA_VERSION_NUM < 502\n    size_t len = lua_objlen(L,-1), j;\n#else\n    size_t len = lua_rawlen(L,-1), j;\n#endif\n\n    mp_encode_array(L,buf,len);\n    luaL_checkstack(L, 1, \"in function mp_encode_lua_table_as_array\");\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. */\nvoid 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 hackish for this lib. */\n    luaL_checkstack(L, 3, \"in function mp_encode_lua_table_as_map\");\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(L,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. */\nint table_is_an_array(lua_State *L) {\n    int count = 0, max = 0;\n#if LUA_VERSION_NUM < 503\n    lua_Number n;\n#else\n    lua_Integer n;\n#endif\n\n    /* Stack top on function entry */\n    int stacktop;\n\n    stacktop = lua_gettop(L);\n\n    lua_pushnil(L);\n    while(lua_next(L,-2)) {\n        /* Stack: ... key value */\n        lua_pop(L,1); /* Stack: ... key */\n        /* The <= 0 check is valid here because we're comparing indexes. */\n#if LUA_VERSION_NUM < 503\n        if ((LUA_TNUMBER != lua_type(L,-1)) || (n = lua_tonumber(L, -1)) <= 0 ||\n            !IS_INT_EQUIVALENT(n))\n#else\n        if (!lua_isinteger(L,-1) || (n = lua_tointeger(L, -1)) <= 0)\n#endif\n        {\n            lua_settop(L, stacktop);\n            return 0;\n        }\n        max = (n > max ? n : max);\n        count++;\n    }\n    /* We have the total number of elements in \"count\". Also we have\n     * the max index encountered in \"max\". 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 max==count you are sure\n     * that there are all the keys form 1 to count (both included). */\n    lua_settop(L, stacktop);\n    return max == count;\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. */\nvoid 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\nvoid mp_encode_lua_null(lua_State *L, mp_buf *buf) {\n    unsigned char b[1];\n\n    b[0] = 0xc0;\n    mp_buf_append(L,buf,b,1);\n}\n\nvoid 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 specified 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:\n    #if LUA_VERSION_NUM < 503\n        mp_encode_lua_number(L,buf); break;\n    #else\n        if (lua_isinteger(L, -1)) {\n            mp_encode_lua_integer(L, buf);\n        } else {\n            mp_encode_lua_number(L, buf);\n        }\n        break;\n    #endif\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\n/*\n * Packs all arguments as a stream for multiple upacking later.\n * Returns error if no arguments provided.\n */\nint mp_pack(lua_State *L) {\n    int nargs = lua_gettop(L);\n    int i;\n    mp_buf *buf;\n\n    if (nargs == 0)\n        return luaL_argerror(L, 0, \"MessagePack pack needs input.\");\n\n    if (!lua_checkstack(L, nargs))\n        return luaL_argerror(L, 0, \"Too many arguments for MessagePack pack.\");\n\n    buf = mp_buf_new(L);\n    for(i = 1; i <= nargs; i++) {\n        /* Copy argument i to top of stack for _encode processing;\n         * the encode function pops it from the stack when complete. */\n        luaL_checkstack(L, 1, \"in function mp_check\");\n        lua_pushvalue(L, i);\n\n        mp_encode_lua_type(L,buf,0);\n\n        lua_pushlstring(L,(char*)buf->b,buf->len);\n\n        /* Reuse the buffer for the next operation by\n         * setting its free count to the total buffer size\n         * and the current position to zero. */\n        buf->free += buf->len;\n        buf->len = 0;\n    }\n    mp_buf_free(L, buf);\n\n    /* Concatenate all nargs buffers together */\n    lua_concat(L, nargs);\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    assert(len <= UINT_MAX);\n    int index = 1;\n\n    lua_newtable(L);\n    luaL_checkstack(L, 1, \"in function mp_decode_to_lua_array\");\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    assert(len <= UINT_MAX);\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\n    /* If we return more than 18 elements, we must resize the stack to\n     * fit all our return values.  But, there is no way to\n     * determine how many objects a msgpack will unpack to up front, so\n     * we request a +1 larger stack on each iteration (noop if stack is\n     * big enough, and when stack does require resize it doubles in size) */\n    luaL_checkstack(L, 1,\n        \"too many return values at once; \"\n        \"use unpack_one or unpack_limit instead.\");\n\n    switch(c->p[0]) {\n    case 0xcc:  /* uint 8 */\n        mp_cur_need(c,2);\n        lua_pushunsigned(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_pushinteger(L,(signed 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_pushunsigned(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_pushinteger(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_pushunsigned(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_pushinteger(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_pushunsigned(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#if LUA_VERSION_NUM < 503\n        lua_pushnumber(L,\n#else\n        lua_pushinteger(L,\n#endif\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 0xd9:  /* raw 8 */\n        mp_cur_need(c,2);\n        {\n            size_t l = c->p[1];\n            mp_cur_need(c,2+l);\n            lua_pushlstring(L,(char*)c->p+2,l);\n            mp_cur_consume(c,2+l);\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 = ((size_t)c->p[1] << 24) |\n                       ((size_t)c->p[2] << 16) |\n                       ((size_t)c->p[3] << 8) |\n                       (size_t)c->p[4];\n            mp_cur_consume(c,5);\n            mp_cur_need(c,l);\n            lua_pushlstring(L,(char*)c->p,l);\n            mp_cur_consume(c,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 = ((size_t)c->p[1] << 24) |\n                       ((size_t)c->p[2] << 16) |\n                       ((size_t)c->p[3] << 8) |\n                       (size_t)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 = ((size_t)c->p[1] << 24) |\n                       ((size_t)c->p[2] << 16) |\n                       ((size_t)c->p[3] << 8) |\n                       (size_t)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_pushunsigned(L,c->p[0]);\n            mp_cur_consume(c,1);\n        } else if ((c->p[0] & 0xe0) == 0xe0) {  /* negative fixnum */\n            lua_pushinteger(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\nint mp_unpack_full(lua_State *L, int limit, int offset) {\n    size_t len;\n    const char *s;\n    mp_cur c;\n    int cnt; /* Number of objects unpacked */\n    int decode_all = (!limit && !offset);\n\n    s = luaL_checklstring(L,1,&len); /* if no match, exits */\n\n    if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */\n        return luaL_error(L,\n            \"Invalid request to unpack with offset of %d and limit of %d.\",\n            offset, len);\n    else if (offset > len)\n        return luaL_error(L,\n            \"Start offset %d greater than input length %d.\", offset, len);\n\n    if (decode_all) limit = INT_MAX;\n\n    mp_cur_init(&c,(const unsigned char *)s+offset,len-offset);\n\n    /* We loop over the decode because this could be a stream\n     * of multiple top-level values serialized together */\n    for(cnt = 0; c.left > 0 && cnt < limit; cnt++) {\n        mp_decode_to_lua_type(L,&c);\n\n        if (c.err == MP_CUR_ERROR_EOF) {\n            return luaL_error(L,\"Missing bytes in input.\");\n        } else if (c.err == MP_CUR_ERROR_BADFMT) {\n            return luaL_error(L,\"Bad data format in input.\");\n        }\n    }\n\n    if (!decode_all) {\n        /* c->left is the remaining size of the input buffer.\n         * subtract the entire buffer size from the unprocessed size\n         * to get our next start offset */\n        int offset = len - c.left;\n\n        luaL_checkstack(L, 1, \"in function mp_unpack_full\");\n\n        /* Return offset -1 when we have have processed the entire buffer. */\n        lua_pushinteger(L, c.left == 0 ? -1 : offset);\n        /* Results are returned with the arg elements still\n         * in place. Lua takes care of only returning\n         * elements above the args for us.\n         * In this case, we have one arg on the stack\n         * for this function, so we insert our first return\n         * value at position 2. */\n        lua_insert(L, 2);\n        cnt += 1; /* increase return count by one to make room for offset */\n    }\n\n    return cnt;\n}\n\nint mp_unpack(lua_State *L) {\n    return mp_unpack_full(L, 0, 0);\n}\n\nint mp_unpack_one(lua_State *L) {\n    int offset = luaL_optinteger(L, 2, 0);\n    /* Variable pop because offset may not exist */\n    lua_pop(L, lua_gettop(L)-1);\n    return mp_unpack_full(L, 1, offset);\n}\n\nint mp_unpack_limit(lua_State *L) {\n    int limit = luaL_checkinteger(L, 2);\n    int offset = luaL_optinteger(L, 3, 0);\n    /* Variable pop because offset may not exist */\n    lua_pop(L, lua_gettop(L)-1);\n\n    return mp_unpack_full(L, limit, offset);\n}\n\nint mp_safe(lua_State *L) {\n    int argc, err, total_results;\n\n    argc = lua_gettop(L);\n\n    /* This adds our function to the bottom of the stack\n     * (the \"call this function\" position) */\n    lua_pushvalue(L, lua_upvalueindex(1));\n    lua_insert(L, 1);\n\n    err = lua_pcall(L, argc, LUA_MULTRET, 0);\n    total_results = lua_gettop(L);\n\n    if (!err) {\n        return total_results;\n    } else {\n        lua_pushnil(L);\n        lua_insert(L,-2);\n        return 2;\n    }\n}\n\n/* -------------------------------------------------------------------------- */\nconst struct luaL_Reg cmds[] = {\n    {\"pack\", mp_pack},\n    {\"unpack\", mp_unpack},\n    {\"unpack_one\", mp_unpack_one},\n    {\"unpack_limit\", mp_unpack_limit},\n    {0}\n};\n\nint luaopen_create(lua_State *L) {\n    int i;\n    /* Manually construct our module table instead of\n     * relying on _register or _newlib */\n    lua_newtable(L);\n\n    for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {\n        lua_pushcfunction(L, cmds[i].func);\n        lua_setfield(L, -2, cmds[i].name);\n    }\n\n    /* Add metadata */\n    lua_pushliteral(L, LUACMSGPACK_NAME);\n    lua_setfield(L, -2, \"_NAME\");\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\nLUALIB_API int luaopen_cmsgpack(lua_State *L) {\n    luaopen_create(L);\n\n#if LUA_VERSION_NUM < 502\n    /* Register name globally for 5.1 */\n    lua_pushvalue(L, -1);\n    lua_setglobal(L, LUACMSGPACK_NAME);\n#endif\n\n    return 1;\n}\n\nLUALIB_API int luaopen_cmsgpack_safe(lua_State *L) {\n    int i;\n\n    luaopen_cmsgpack(L);\n\n    /* Wrap all functions in the safe handler */\n    for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {\n        lua_getfield(L, -1, cmds[i].name);\n        lua_pushcclosure(L, mp_safe, 1);\n        lua_setfield(L, -2, cmds[i].name);\n    }\n\n#if LUA_VERSION_NUM < 502\n    /* Register name globally for 5.1 */\n    lua_pushvalue(L, -1);\n    lua_setglobal(L, LUACMSGPACK_SAFE_NAME);\n#endif\n\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": "Chapter02/redis-6.0.3/deps/lua/src/lua_struct.c",
    "content": "/*\n** {======================================================\n** Library for packing/unpacking structures.\n** $Id: struct.c,v 1.7 2018/05/11 22:04:31 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 (lua_State *L, 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      if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))\n        luaL_error(L, \"integral size overflow\");\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(L, fmt, 1);\n    case 'i': case 'I': {\n      int sz = getnum(L, 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(L, 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);\n  luaL_argcheck(L, pos > 0, 3, \"offset must be 1 or greater\");\n  pos--; /* Lua indexes are 1-based, but here we want 0-based for C\n          * pointer math. */\n  int n = 0;  /* number of results */\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    luaL_argcheck(L, size <= ld && pos <= ld - size,\n                   2, \"data string too short\");\n    /* stack space for item + next position */\n    luaL_checkstack(L, 2, \"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++;\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++;\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++;\n        break;\n      }\n      case 'c': {\n        if (size == 0) {\n          if (n == 0 || !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--;\n          luaL_argcheck(L, size <= ld && pos <= ld - size,\n                           2, \"data string too short\");\n        }\n        lua_pushlstring(L, data+pos, size); n++;\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++;\n        break;\n      }\n      default: controloptions(L, opt, &fmt, &h);\n    }\n    pos += size;\n  }\n  lua_pushinteger(L, pos + 1);  /* next position */\n  return n + 1;\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-2018 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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/lua/src/strbuf.c",
    "content": "/* strbuf - String buffer routines\n *\n * Copyright (c) 2010-2012  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\nstatic void 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": "Chapter02/redis-6.0.3/deps/lua/src/strbuf.h",
    "content": "/* strbuf - String buffer routines\n *\n * Copyright (c) 2010-2012  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);\nstatic char *strbuf_empty_ptr(strbuf_t *s);\nstatic void strbuf_extend_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 char *strbuf_empty_ptr(strbuf_t *s)\n{\n    return s->buf + s->length;\n}\n\nstatic inline void strbuf_extend_length(strbuf_t *s, int len)\n{\n    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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/lua/test/hello.lua",
    "content": "-- the first program in every language\n\nio.write(\"Hello world, from \",_VERSION,\"!\\n\")\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/deps/update-jemalloc.sh",
    "content": "#!/bin/bash\nVER=$1\nURL=\"http://www.canonware.com/download/jemalloc/jemalloc-${VER}.tar.bz2\"\necho \"Downloading $URL\"\ncurl $URL > /tmp/jemalloc.tar.bz2\ntar xvjf /tmp/jemalloc.tar.bz2\nrm -rf jemalloc\nmv jemalloc-${VER} jemalloc\necho \"Use git status, add all files and commit changes.\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/redis.conf",
    "content": "# Redis configuration file example.\n#\n# Note that in order to read the configuration file, Redis must be\n# started with the file path as first argument:\n#\n# ./redis-server /path/to/redis.conf\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 servers 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################################## MODULES #####################################\n\n# Load modules at startup. If the server is not able to load modules\n# it will abort. It is possible to use multiple loadmodule directives.\n#\n# loadmodule /path/to/my_module.so\n# loadmodule /path/to/other_module.so\n\n################################## NETWORK #####################################\n\n# By default, if no \"bind\" configuration directive is specified, Redis listens\n# for connections from all the network interfaces available on the server.\n# It is possible to listen to just one or multiple selected interfaces using\n# the \"bind\" configuration directive, followed by one or more IP addresses.\n#\n# Examples:\n#\n# bind 192.168.1.100 10.0.0.1\n# bind 127.0.0.1 ::1\n#\n# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the\n# internet, binding to all the interfaces is dangerous and will expose the\n# instance to everybody on the internet. So by default we uncomment the\n# following bind directive, that will force Redis to listen only into\n# the IPv4 loopback interface address (this means Redis will be able to\n# accept connections only from clients running into the same computer it\n# is running).\n#\n# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES\n# JUST COMMENT THE FOLLOWING LINE.\n# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nbind 127.0.0.1\n\n# Protected mode is a layer of security protection, in order to avoid that\n# Redis instances left open on the internet are accessed and exploited.\n#\n# When protected mode is on and if:\n#\n# 1) The server is not binding explicitly to a set of addresses using the\n#    \"bind\" directive.\n# 2) No password is configured.\n#\n# The server only accepts connections from clients connecting from the\n# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain\n# sockets.\n#\n# By default protected mode is enabled. You should disable it only if\n# you are sure you want clients from other hosts to connect to Redis\n# even if no authentication is configured, nor a specific set of interfaces\n# are explicitly listed using the \"bind\" directive.\nprotected-mode yes\n\n# Accept connections on the specified port, default is 6379 (IANA #815344).\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# Unix socket.\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 700\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 300 seconds, which is the new\n# Redis default starting with Redis 3.2.1.\ntcp-keepalive 300\n\n################################# TLS/SSL #####################################\n\n# By default, TLS/SSL is disabled. To enable it, the \"tls-port\" configuration\n# directive can be used to define TLS-listening ports. To enable TLS on the\n# default port, use:\n#\n# port 0\n# tls-port 6379\n\n# Configure a X.509 certificate and private key to use for authenticating the\n# server to connected clients, masters or cluster peers.  These files should be\n# PEM formatted.\n#\n# tls-cert-file redis.crt \n# tls-key-file redis.key\n\n# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:\n#\n# tls-dh-params-file redis.dh\n\n# Configure a CA certificate(s) bundle or directory to authenticate TLS/SSL\n# clients and peers.  Redis requires an explicit configuration of at least one\n# of these, and will not implicitly use the system wide configuration.\n#\n# tls-ca-cert-file ca.crt\n# tls-ca-cert-dir /etc/ssl/certs\n\n# By default, clients (including replica servers) on a TLS port are required\n# to authenticate using valid client side certificates.\n#\n# It is possible to disable authentication using this directive.\n#\n# tls-auth-clients no\n\n# By default, a Redis replica does not attempt to establish a TLS connection\n# with its master.\n#\n# Use the following directive to enable TLS on replication links.\n#\n# tls-replication yes\n\n# By default, the Redis Cluster bus uses a plain TCP connection. To enable\n# TLS for the bus protocol, use the following directive:\n#\n# tls-cluster yes\n\n# Explicitly specify TLS versions to support. Allowed values are case insensitive\n# and include \"TLSv1\", \"TLSv1.1\", \"TLSv1.2\", \"TLSv1.3\" (OpenSSL >= 1.1.1) \n#\n# tls-protocols TLSv1.2\n\n# Configure allowed ciphers.  See the ciphers(1ssl) manpage for more information\n# about the syntax of this string.\n#\n# Note: this configuration applies only to <= TLSv1.2.\n#\n# tls-ciphers DEFAULT:!MEDIUM\n\n# Configure allowed TLSv1.3 ciphersuites.  See the ciphers(1ssl) manpage for more\n# information about the syntax of this string, and specifically for TLSv1.3\n# ciphersuites.\n#\n# tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256\n\n# When choosing a cipher, use the server's preference instead of the client\n# preference. By default, the server follows the client's preference.\n#\n# tls-prefer-server-ciphers yes\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# If you run Redis from upstart or systemd, Redis can interact with your\n# supervision tree. Options:\n#   supervised no      - no supervision interaction\n#   supervised upstart - signal upstart by putting Redis into SIGSTOP mode\n#   supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET\n#   supervised auto    - detect upstart or systemd method based on\n#                        UPSTART_JOB or NOTIFY_SOCKET environment variables\n# Note: these supervision methods only signal \"process is ready.\"\n#       They do not enable continuous liveness pings back to your supervisor.\nsupervised no\n\n# If a pid file is specified, Redis writes it where specified at startup\n# and removes it at exit.\n#\n# When the server runs non daemonized, no pid file is created if none is\n# specified in the configuration. When the server is daemonized, the pid file\n# is used even if not specified, defaulting to \"/var/run/redis.pid\".\n#\n# Creating a pid file is best effort: if Redis is not able to create it\n# nothing bad happens, the server will start and run normally.\npidfile /var/run/redis_6379.pid\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# By default Redis shows an ASCII art logo only when started to log to the\n# standard output and if the standard output is a TTY. Basically this means\n# that normally a logo is displayed only in interactive sessions.\n#\n# However it is possible to force the pre-4.0 behavior and always show a\n# ASCII art logo in startup logs by setting the following option to yes.\nalways-show-logo yes\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 completely by commenting out all \"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# Remove RDB files used by replication in instances without persistence\n# enabled. By default this option is disabled, however there are environments\n# where for regulations or other security concerns, RDB files persisted on\n# disk by masters in order to feed replicas, or stored on disk by replicas\n# in order to load them for the initial synchronization, should be deleted\n# ASAP. Note that this option ONLY WORKS in instances that have both AOF\n# and RDB persistence disabled, otherwise is completely ignored.\n#\n# An alternative (and sometimes better) way to obtain the same effect is\n# to use diskless replication on both master and replicas instances. However\n# in the case of replicas, diskless is not always an option.\nrdb-del-sync-files no\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-Replica replication. Use replicaof to make a Redis instance a copy of\n# another Redis server. A few things to understand ASAP about Redis replication.\n#\n#   +------------------+      +---------------+\n#   |      Master      | ---> |    Replica    |\n#   | (receive writes) |      |  (exact copy) |\n#   +------------------+      +---------------+\n#\n# 1) Redis replication is asynchronous, but you can configure a master to\n#    stop accepting writes if it appears to be not connected with at least\n#    a given number of replicas.\n# 2) Redis replicas are able to perform a partial resynchronization with the\n#    master if the replication link is lost for a relatively small amount of\n#    time. You may want to configure the replication backlog size (see the next\n#    sections of this file) with a sensible value depending on your needs.\n# 3) Replication is automatic and does not need user intervention. After a\n#    network partition replicas automatically try to reconnect to masters\n#    and resynchronize with them.\n#\n# replicaof <masterip> <masterport>\n\n# If the master is password protected (using the \"requirepass\" configuration\n# directive below) it is possible to tell the replica to authenticate before\n# starting the replication synchronization process, otherwise the master will\n# refuse the replica request.\n#\n# masterauth <master-password>\n#\n# However this is not enough if you are using Redis ACLs (for Redis version\n# 6 or greater), and the default user is not capable of running the PSYNC\n# command and/or other commands needed for replication. In this case it's\n# better to configure a special user to use with replication, and specify the\n# masteruser configuration as such:\n#\n# masteruser <username>\n#\n# When masteruser is specified, the replica will authenticate against its\n# master using the new AUTH form: AUTH <username> <password>.\n\n# When a replica loses its connection with the master, or when the replication\n# is still in progress, the replica can act in two different ways:\n#\n# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica 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 replica-serve-stale-data is set to 'no' the replica will reply with\n#    an error \"SYNC with master in progress\" to all the kind of commands\n#    but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,\n#    SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB,\n#    COMMAND, POST, HOST: and LATENCY.\n#\nreplica-serve-stale-data yes\n\n# You can configure a replica instance to accept writes or not. Writing against\n# a replica instance may be useful to store some ephemeral data (because data\n# written on a replica 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 replicas are read-only.\n#\n# Note: read only replicas 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 replica 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 replicas using 'rename-command' to shadow all the\n# administrative / dangerous commands.\nreplica-read-only yes\n\n# Replication SYNC strategy: disk or socket.\n#\n# New replicas and reconnecting replicas that are not able to continue the\n# replication process just receiving differences, need to do what is called a\n# \"full synchronization\". An RDB file is transmitted from the master to the\n# replicas.\n#\n# The transmission can happen in two different ways:\n#\n# 1) Disk-backed: The Redis master creates a new process that writes the RDB\n#                 file on disk. Later the file is transferred by the parent\n#                 process to the replicas incrementally.\n# 2) Diskless: The Redis master creates a new process that directly writes the\n#              RDB file to replica sockets, without touching the disk at all.\n#\n# With disk-backed replication, while the RDB file is generated, more replicas\n# can be queued and served with the RDB file as soon as the current child\n# producing the RDB file finishes its work. With diskless replication instead\n# once the transfer starts, new replicas arriving will be queued and a new\n# transfer will start when the current one terminates.\n#\n# When diskless replication is used, the master waits a configurable amount of\n# time (in seconds) before starting the transfer in the hope that multiple\n# replicas will arrive and the transfer can be parallelized.\n#\n# With slow disks and fast (large bandwidth) networks, diskless replication\n# works better.\nrepl-diskless-sync no\n\n# When diskless replication is enabled, it is possible to configure the delay\n# the server waits in order to spawn the child that transfers the RDB via socket\n# to the replicas.\n#\n# This is important since once the transfer starts, it is not possible to serve\n# new replicas arriving, that will be queued for the next RDB transfer, so the\n# server waits a delay in order to let more replicas arrive.\n#\n# The delay is specified in seconds, and by default is 5 seconds. To disable\n# it entirely just set it to 0 seconds and the transfer will start ASAP.\nrepl-diskless-sync-delay 5\n\n# -----------------------------------------------------------------------------\n# WARNING: RDB diskless load is experimental. Since in this setup the replica\n# does not immediately store an RDB on disk, it may cause data loss during\n# failovers. RDB diskless load + Redis modules not handling I/O reads may also\n# cause Redis to abort in case of I/O errors during the initial synchronization\n# stage with the master. Use only if your do what you are doing.\n# -----------------------------------------------------------------------------\n#\n# Replica can load the RDB it reads from the replication link directly from the\n# socket, or store the RDB to a file and read that file after it was completely\n# recived from the master.\n#\n# In many cases the disk is slower than the network, and storing and loading\n# the RDB file may increase replication time (and even increase the master's\n# Copy on Write memory and salve buffers).\n# However, parsing the RDB file directly from the socket may mean that we have\n# to flush the contents of the current database before the full rdb was\n# received. For this reason we have the following options:\n#\n# \"disabled\"    - Don't use diskless load (store the rdb file to the disk first)\n# \"on-empty-db\" - Use diskless load only when it is completely safe.\n# \"swapdb\"      - Keep a copy of the current db contents in RAM while parsing\n#                 the data directly from the socket. note that this requires\n#                 sufficient memory, if you don't have it, you risk an OOM kill.\nrepl-diskless-load disabled\n\n# Replicas send PINGs to server in a predefined interval. It's possible to\n# change this interval with the repl_ping_replica_period option. The default\n# value is 10 seconds.\n#\n# repl-ping-replica-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 replica.\n# 2) Master timeout from the point of view of replicas (data, pings).\n# 3) Replica 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-replica-period otherwise a timeout will be detected\n# every time there is low traffic between the master and the replica.\n#\n# repl-timeout 60\n\n# Disable TCP_NODELAY on the replica 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 replicas. But this can add a delay for\n# the data to appear on the replica 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 replica 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 replicas 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# replica data when replicas are disconnected for some time, so that when a\n# replica wants to reconnect again, often a full resync is not needed, but a\n# partial resync is enough, just passing the portion of data the replica\n# missed while disconnected.\n#\n# The bigger the replication backlog, the longer the time the replica 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 replica connected.\n#\n# repl-backlog-size 1mb\n\n# After a master has no longer connected replicas 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 replica disconnected, for\n# the backlog buffer to be freed.\n#\n# Note that replicas never free the backlog for timeout, since they may be\n# promoted to masters later, and should be able to correctly \"partially\n# resynchronize\" with the replicas: hence they should always accumulate backlog.\n#\n# A value of 0 means to never release the backlog.\n#\n# repl-backlog-ttl 3600\n\n# The replica priority is an integer number published by Redis in the INFO\n# output. It is used by Redis Sentinel in order to select a replica to promote\n# into a master if the master is no longer working correctly.\n#\n# A replica with a low priority number is considered better for promotion, so\n# for instance if there are three replicas with priority 10, 100, 25 Sentinel\n# will pick the one with priority 10, that is the lowest.\n#\n# However a special priority of 0 marks the replica as not able to perform the\n# role of master, so a replica with priority of 0 will never be selected by\n# Redis Sentinel for promotion.\n#\n# By default the priority is 100.\nreplica-priority 100\n\n# It is possible for a master to stop accepting writes if there are less than\n# N replicas connected, having a lag less or equal than M seconds.\n#\n# The N replicas 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 replica, that is usually sent every second.\n#\n# This option does not GUARANTEE that N replicas will accept the write, but\n# will limit the window of exposure for lost writes in case not enough replicas\n# are available, to the specified number of seconds.\n#\n# For example to require at least 3 replicas with a lag <= 10 seconds use:\n#\n# min-replicas-to-write 3\n# min-replicas-max-lag 10\n#\n# Setting one or the other to 0 disables the feature.\n#\n# By default min-replicas-to-write is set to 0 (feature disabled) and\n# min-replicas-max-lag is set to 10.\n\n# A Redis master is able to list the address and port of the attached\n# replicas in different ways. For example the \"INFO replication\" section\n# offers this information, which is used, among other tools, by\n# Redis Sentinel in order to discover replica instances.\n# Another place where this info is available is in the output of the\n# \"ROLE\" command of a master.\n#\n# The listed IP and address normally reported by a replica is obtained\n# in the following way:\n#\n#   IP: The address is auto detected by checking the peer address\n#   of the socket used by the replica to connect with the master.\n#\n#   Port: The port is communicated by the replica during the replication\n#   handshake, and is normally the port that the replica is using to\n#   listen for connections.\n#\n# However when port forwarding or Network Address Translation (NAT) is\n# used, the replica may be actually reachable via different IP and port\n# pairs. The following two options can be used by a replica in order to\n# report to its master a specific set of IP and port, so that both INFO\n# and ROLE will report those values.\n#\n# There is no need to use both the options if you need to override just\n# the port or the IP address.\n#\n# replica-announce-ip 5.5.5.5\n# replica-announce-port 1234\n\n############################### KEYS TRACKING #################################\n\n# Redis implements server assisted support for client side caching of values.\n# This is implemented using an invalidation table that remembers, using\n# 16 millions of slots, what clients may have certain subsets of keys. In turn\n# this is used in order to send invalidation messages to clients. Please\n# to understand more about the feature check this page:\n#\n#   https://redis.io/topics/client-side-caching\n#\n# When tracking is enabled for a client, all the read only queries are assumed\n# to be cached: this will force Redis to store information in the invalidation\n# table. When keys are modified, such information is flushed away, and\n# invalidation messages are sent to the clients. However if the workload is\n# heavily dominated by reads, Redis could use more and more memory in order\n# to track the keys fetched by many clients.\n#\n# For this reason it is possible to configure a maximum fill value for the\n# invalidation table. By default it is set to 1M of keys, and once this limit\n# is reached, Redis will start to evict keys in the invalidation table\n# even if they were not modified, just to reclaim memory: this will in turn\n# force the clients to invalidate the cached values. Basically the table\n# maximum size is a trade off between the memory you want to spend server\n# side to track information about who cached what, and the ability of clients\n# to retain cached objects in memory.\n#\n# If you set the value to 0, it means there are no limits, and Redis will\n# retain as many keys as needed in the invalidation table.\n# In the \"stats\" INFO section, you can find information about the number of\n# keys in the invalidation table at every given moment.\n#\n# Note: when key tracking is used in broadcasting mode, no memory is used\n# in the server side so this setting is useless.\n#\n# tracking-table-max-keys 1000000\n\n################################## SECURITY ###################################\n\n# Warning: since Redis is pretty fast an outside user can try up to\n# 1 million passwords per second against a modern box. This means that you\n# should use very strong passwords, otherwise they will be very easy to break.\n# Note that because the password is really a shared secret between the client\n# and the server, and should not be memorized by any human, the password\n# can be easily a long string from /dev/urandom or whatever, so by using a\n# long and unguessable password no brute force attack will be possible.\n\n# Redis ACL users are defined in the following format:\n#\n#   user <username> ... acl rules ...\n#\n# For example:\n#\n#   user worker +@list +@connection ~jobs:* on >ffa9203c493aa99\n#\n# The special username \"default\" is used for new connections. If this user\n# has the \"nopass\" rule, then new connections will be immediately authenticated\n# as the \"default\" user without the need of any password provided via the\n# AUTH command. Otherwise if the \"default\" user is not flagged with \"nopass\"\n# the connections will start in not authenticated state, and will require\n# AUTH (or the HELLO command AUTH option) in order to be authenticated and\n# start to work.\n#\n# The ACL rules that describe what an user can do are the following:\n#\n#  on           Enable the user: it is possible to authenticate as this user.\n#  off          Disable the user: it's no longer possible to authenticate\n#               with this user, however the already authenticated connections\n#               will still work.\n#  +<command>   Allow the execution of that command\n#  -<command>   Disallow the execution of that command\n#  +@<category> Allow the execution of all the commands in such category\n#               with valid categories are like @admin, @set, @sortedset, ...\n#               and so forth, see the full list in the server.c file where\n#               the Redis command table is described and defined.\n#               The special category @all means all the commands, but currently\n#               present in the server, and that will be loaded in the future\n#               via modules.\n#  +<command>|subcommand    Allow a specific subcommand of an otherwise\n#                           disabled command. Note that this form is not\n#                           allowed as negative like -DEBUG|SEGFAULT, but\n#                           only additive starting with \"+\".\n#  allcommands  Alias for +@all. Note that it implies the ability to execute\n#               all the future commands loaded via the modules system.\n#  nocommands   Alias for -@all.\n#  ~<pattern>   Add a pattern of keys that can be mentioned as part of\n#               commands. For instance ~* allows all the keys. The pattern\n#               is a glob-style pattern like the one of KEYS.\n#               It is possible to specify multiple patterns.\n#  allkeys      Alias for ~*\n#  resetkeys    Flush the list of allowed keys patterns.\n#  ><password>  Add this passowrd to the list of valid password for the user.\n#               For example >mypass will add \"mypass\" to the list.\n#               This directive clears the \"nopass\" flag (see later).\n#  <<password>  Remove this password from the list of valid passwords.\n#  nopass       All the set passwords of the user are removed, and the user\n#               is flagged as requiring no password: it means that every\n#               password will work against this user. If this directive is\n#               used for the default user, every new connection will be\n#               immediately authenticated with the default user without\n#               any explicit AUTH command required. Note that the \"resetpass\"\n#               directive will clear this condition.\n#  resetpass    Flush the list of allowed passwords. Moreover removes the\n#               \"nopass\" status. After \"resetpass\" the user has no associated\n#               passwords and there is no way to authenticate without adding\n#               some password (or setting it as \"nopass\" later).\n#  reset        Performs the following actions: resetpass, resetkeys, off,\n#               -@all. The user returns to the same state it has immediately\n#               after its creation.\n#\n# ACL rules can be specified in any order: for instance you can start with\n# passwords, then flags, or key patterns. However note that the additive\n# and subtractive rules will CHANGE MEANING depending on the ordering.\n# For instance see the following example:\n#\n#   user alice on +@all -DEBUG ~* >somepassword\n#\n# This will allow \"alice\" to use all the commands with the exception of the\n# DEBUG command, since +@all added all the commands to the set of the commands\n# alice can use, and later DEBUG was removed. However if we invert the order\n# of two ACL rules the result will be different:\n#\n#   user alice on -DEBUG +@all ~* >somepassword\n#\n# Now DEBUG was removed when alice had yet no commands in the set of allowed\n# commands, later all the commands are added, so the user will be able to\n# execute everything.\n#\n# Basically ACL rules are processed left-to-right.\n#\n# For more information about ACL configuration please refer to\n# the Redis web site at https://redis.io/topics/acl\n\n# ACL LOG\n#\n# The ACL Log tracks failed commands and authentication events associated\n# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked \n# by ACLs. The ACL Log is stored in and consumes memory. There is no limit\n# to its length.You can reclaim memory with ACL LOG RESET or set a maximum\n# length below.\nacllog-max-len 128\n\n# Using an external ACL file\n#\n# Instead of configuring users here in this file, it is possible to use\n# a stand-alone file just listing users. The two methods cannot be mixed:\n# if you configure users here and at the same time you activate the exteranl\n# ACL file, the server will refuse to start.\n#\n# The format of the external ACL user file is exactly the same as the\n# format that is used inside redis.conf to describe users.\n#\n# aclfile /etc/redis/users.acl\n\n# IMPORTANT NOTE: starting with Redis 6 \"requirepass\" is just a compatiblity\n# layer on top of the new ACL system. The option effect will be just setting\n# the password for the default user. Clients will still authenticate using\n# AUTH <password> as usually, or more explicitly with AUTH default <password>\n# if they follow the new protocol: both will work.\n#\n# requirepass foobared\n\n# Command renaming (DEPRECATED).\n#\n# ------------------------------------------------------------------------\n# WARNING: avoid using this option if possible. Instead use ACLs to remove\n# commands from the default user, and put them only in some admin user you\n# create for administrative purposes.\n# ------------------------------------------------------------------------\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 replicas may cause problems.\n\n################################### CLIENTS ####################################\n\n# Set the max number of connected clients at the same time. By default\n# this limit is set to 10000 clients, however if the Redis server is not\n# able to configure the process file limit to allow for the specified limit\n# the max number of allowed clients is set to the current file limit\n# minus 32 (as Redis reserves a few file descriptors for internal uses).\n#\n# Once the limit is reached Redis will close all the new connections sending\n# an error 'max number of clients reached'.\n#\n# maxclients 10000\n\n############################## MEMORY MANAGEMENT ################################\n\n# Set a memory usage limit to 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 or LFU cache, or to\n# set a hard memory limit for an instance (using the 'noeviction' policy).\n#\n# WARNING: If you have replicas attached to an instance with maxmemory on,\n# the size of the output buffers needed to feed the replicas 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 replicas 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 replicas attached it is suggested that you set a lower\n# limit for maxmemory so that there is some free RAM on the system for replica\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 one from the following behaviors:\n#\n# volatile-lru -> Evict using approximated LRU, only keys with an expire set.\n# allkeys-lru -> Evict any key using approximated LRU.\n# volatile-lfu -> Evict using approximated LFU, only keys with an expire set.\n# allkeys-lfu -> Evict any key using approximated LFU.\n# volatile-random -> Remove a random key having 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 evict anything, just return an error on write operations.\n#\n# LRU means Least Recently Used\n# LFU means Least Frequently Used\n#\n# Both LRU, LFU and volatile-ttl are implemented using approximated\n# randomized algorithms.\n#\n# Note: with any of the above policies, Redis will return an error on write\n#       operations, when there are no suitable keys for eviction.\n#\n#       At the date of writing these commands are: set setnx setex append\n#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd\n#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby\n#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby\n#       getset mset msetnx exec sort\n#\n# The default is:\n#\n# maxmemory-policy noeviction\n\n# LRU, LFU 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 more CPU. 3 is faster but not very accurate.\n#\n# maxmemory-samples 5\n\n# Starting from Redis 5, by default a replica will ignore its maxmemory setting\n# (unless it is promoted to master after a failover or manually). It means\n# that the eviction of keys will be just handled by the master, sending the\n# DEL commands to the replica as keys evict in the master side.\n#\n# This behavior ensures that masters and replicas stay consistent, and is usually\n# what you want, however if your replica is writable, or you want the replica\n# to have a different memory setting, and you are sure all the writes performed\n# to the replica are idempotent, then you may change this default (but be sure\n# to understand what you are doing).\n#\n# Note that since the replica by default does not evict, it may end using more\n# memory than the one set via maxmemory (there are certain buffers that may\n# be larger on the replica, or data structures may sometimes take more memory\n# and so forth). So make sure you monitor your replicas and make sure they\n# have enough memory to never hit a real out-of-memory condition before the\n# master hits the configured maxmemory setting.\n#\n# replica-ignore-maxmemory yes\n\n# Redis reclaims expired keys in two ways: upon access when those keys are\n# found to be expired, and also in background, in what is called the\n# \"active expire key\". The key space is slowly and interactively scanned\n# looking for expired keys to reclaim, so that it is possible to free memory\n# of keys that are expired and will never be accessed again in a short time.\n#\n# The default effort of the expire cycle will try to avoid having more than\n# ten percent of expired keys still in memory, and will try to avoid consuming\n# more than 25% of total memory and to add latency to the system. However\n# it is possible to increase the expire \"effort\" that is normally set to\n# \"1\", to a greater value, up to the value \"10\". At its maximum value the\n# system will use more CPU, longer cycles (and technically may introduce\n# more latency), and will tollerate less already expired keys still present\n# in the system. It's a tradeoff betweeen memory, CPU and latecy.\n#\n# active-expire-effort 1\n\n############################# LAZY FREEING ####################################\n\n# Redis has two primitives to delete keys. One is called DEL and is a blocking\n# deletion of the object. It means that the server stops processing new commands\n# in order to reclaim all the memory associated with an object in a synchronous\n# way. If the key deleted is associated with a small object, the time needed\n# in order to execute the DEL command is very small and comparable to most other\n# O(1) or O(log_N) commands in Redis. However if the key is associated with an\n# aggregated value containing millions of elements, the server can block for\n# a long time (even seconds) in order to complete the operation.\n#\n# For the above reasons Redis also offers non blocking deletion primitives\n# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and\n# FLUSHDB commands, in order to reclaim memory in background. Those commands\n# are executed in constant time. Another thread will incrementally free the\n# object in the background as fast as possible.\n#\n# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled.\n# It's up to the design of the application to understand when it is a good\n# idea to use one or the other. However the Redis server sometimes has to\n# delete keys or flush the whole database as a side effect of other operations.\n# Specifically Redis deletes objects independently of a user call in the\n# following scenarios:\n#\n# 1) On eviction, because of the maxmemory and maxmemory policy configurations,\n#    in order to make room for new data, without going over the specified\n#    memory limit.\n# 2) Because of expire: when a key with an associated time to live (see the\n#    EXPIRE command) must be deleted from memory.\n# 3) Because of a side effect of a command that stores data on a key that may\n#    already exist. For example the RENAME command may delete the old key\n#    content when it is replaced with another one. Similarly SUNIONSTORE\n#    or SORT with STORE option may delete existing keys. The SET command\n#    itself removes any old content of the specified key in order to replace\n#    it with the specified string.\n# 4) During replication, when a replica performs a full resynchronization with\n#    its master, the content of the whole database is removed in order to\n#    load the RDB file just transferred.\n#\n# In all the above cases the default is to delete objects in a blocking way,\n# like if DEL was called. However you can configure each case specifically\n# in order to instead release memory in a non-blocking way like if UNLINK\n# was called, using the following configuration directives.\n\nlazyfree-lazy-eviction no\nlazyfree-lazy-expire no\nlazyfree-lazy-server-del no\nreplica-lazy-flush no\n\n# It is also possible, for the case when to replace the user code DEL calls\n# with UNLINK calls is not easy, to modify the default behavior of the DEL\n# command to act exactly like UNLINK, using the following configuration\n# directive:\n\nlazyfree-lazy-user-del no\n\n################################ THREADED I/O #################################\n\n# Redis is mostly single threaded, however there are certain threaded\n# operations such as UNLINK, slow I/O accesses and other things that are\n# performed on side threads.\n#\n# Now it is also possible to handle Redis clients socket reads and writes\n# in different I/O threads. Since especially writing is so slow, normally\n# Redis users use pipelining in order to speedup the Redis performances per\n# core, and spawn multiple instances in order to scale more. Using I/O\n# threads it is possible to easily speedup two times Redis without resorting\n# to pipelining nor sharding of the instance.\n#\n# By default threading is disabled, we suggest enabling it only in machines\n# that have at least 4 or more cores, leaving at least one spare core.\n# Using more than 8 threads is unlikely to help much. We also recommend using\n# threaded I/O only if you actually have performance problems, with Redis\n# instances being able to use a quite big percentage of CPU time, otherwise\n# there is no point in using this feature.\n#\n# So for instance if you have a four cores boxes, try to use 2 or 3 I/O\n# threads, if you have a 8 cores, try to use 6 threads. In order to\n# enable I/O threads use the following configuration directive:\n#\n# io-threads 4\n#\n# Setting io-threads to 1 will just use the main thread as usually.\n# When I/O threads are enabled, we only use threads for writes, that is\n# to thread the write(2) syscall and transfer the client buffers to the\n# socket. However it is also possible to enable threading of reads and\n# protocol parsing using the following configuration directive, by setting\n# it to yes:\n#\n# io-threads-do-reads no\n#\n# Usually threading reads doesn't help much.\n#\n# NOTE 1: This configuration directive cannot be changed at runtime via\n# CONFIG SET. Aso this feature currently does not work when SSL is\n# enabled.\n#\n# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make\n# sure you also run the benchmark itself in threaded mode, using the\n# --threads option to match the number of Redis theads, otherwise you'll not\n# be able to notice the improvements.\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 of waiting 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# An AOF file may be found to be truncated at the end during the Redis\n# startup process, when the AOF data gets loaded back into memory.\n# This may happen when the system where Redis is running\n# crashes, especially when an ext4 filesystem is mounted without the\n# data=ordered option (however this can't happen when Redis itself\n# crashes or aborts but the operating system still works correctly).\n#\n# Redis can either exit with an error when this happens, or load as much\n# data as possible (the default now) and start if the AOF file is found\n# to be truncated at the end. The following option controls this behavior.\n#\n# If aof-load-truncated is set to yes, a truncated AOF file is loaded and\n# the Redis server starts emitting a log to inform the user of the event.\n# Otherwise if the option is set to no, the server aborts with an error\n# and refuses to start. When the option is set to no, the user requires\n# to fix the AOF file using the \"redis-check-aof\" utility before to restart\n# the server.\n#\n# Note that if the AOF file will be found to be corrupted in the middle\n# the server will still exit with an error. This option only applies when\n# Redis will try to read more data from the AOF file but not enough bytes\n# will be found.\naof-load-truncated yes\n\n# When rewriting the AOF file, Redis is able to use an RDB preamble in the\n# AOF file for faster rewrites and recoveries. When this option is turned\n# on the rewritten AOF file is composed of two different stanzas:\n#\n#   [RDB file][AOF tail]\n#\n# When loading Redis recognizes that the AOF file starts with the \"REDIS\"\n# string and loads the prefixed RDB file, and continues loading the AOF\n# tail.\naof-use-rdb-preamble yes\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 exceeds 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 command was\n# already issued by the script but the user doesn'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 do 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# A replica of a failing master will avoid to start a failover if its data\n# looks too old.\n#\n# There is no simple way for a replica to actually have an exact measure of\n# its \"data age\", so the following two checks are performed:\n#\n# 1) If there are multiple replicas able to failover, they exchange messages\n#    in order to try to give an advantage to the replica with the best\n#    replication offset (more data from the master processed).\n#    Replicas will try to get their rank by offset, and apply to the start\n#    of the failover a delay proportional to their rank.\n#\n# 2) Every single replica computes the time of the last interaction with\n#    its master. This can be the last ping or command received (if the master\n#    is still in the \"connected\" state), or the time that elapsed since the\n#    disconnection with the master (if the replication link is currently down).\n#    If the last interaction is too old, the replica will not try to failover\n#    at all.\n#\n# The point \"2\" can be tuned by user. Specifically a replica will not perform\n# the failover if, since the last interaction with the master, the time\n# elapsed is greater than:\n#\n#   (node-timeout * replica-validity-factor) + repl-ping-replica-period\n#\n# So for example if node-timeout is 30 seconds, and the replica-validity-factor\n# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the\n# replica will not try to failover if it was not able to talk with the master\n# for longer than 310 seconds.\n#\n# A large replica-validity-factor may allow replicas with too old data to failover\n# a master, while a too small value may prevent the cluster from being able to\n# elect a replica at all.\n#\n# For maximum availability, it is possible to set the replica-validity-factor\n# to a value of 0, which means, that replicas will always try to failover the\n# master regardless of the last time they interacted with the master.\n# (However they'll always try to apply a delay proportional to their\n# offset rank).\n#\n# Zero is the only value able to guarantee that when all the partitions heal\n# the cluster will always be able to continue.\n#\n# cluster-replica-validity-factor 10\n\n# Cluster replicas are able to migrate to orphaned masters, that are masters\n# that are left without working replicas. 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 replicas.\n#\n# Replicas migrate to orphaned masters only if there are still at least a\n# given number of other working replicas for their old master. This number\n# is the \"migration barrier\". A migration barrier of 1 means that a replica\n# will migrate only if there is at least 1 other working replica for its master\n# and so forth. It usually reflects the number of replicas you want for every\n# master in your cluster.\n#\n# Default is 1 (replicas migrate only if their masters remain with at least\n# one replica). 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# By default Redis Cluster nodes stop accepting queries if they detect there\n# is at least an hash slot uncovered (no available node is serving it).\n# This way if the cluster is partially down (for example a range of hash slots\n# are no longer covered) all the cluster becomes, eventually, unavailable.\n# It automatically returns available as soon as all the slots are covered again.\n#\n# However sometimes you want the subset of the cluster which is working,\n# to continue to accept queries for the part of the key space that is still\n# covered. In order to do so, just set the cluster-require-full-coverage\n# option to no.\n#\n# cluster-require-full-coverage yes\n\n# This option, when set to yes, prevents replicas from trying to failover its\n# master during master failures. However the master can still perform a\n# manual failover, if forced to do so.\n#\n# This is useful in different scenarios, especially in the case of multiple\n# data center operations, where we want one side to never be promoted if not\n# in the case of a total DC failure.\n#\n# cluster-replica-no-failover no\n\n# This option, when set to yes, allows nodes to serve read traffic while the\n# the cluster is in a down state, as long as it believes it owns the slots. \n#\n# This is useful for two cases.  The first case is for when an application \n# doesn't require consistency of data during node failures or network partitions.\n# One example of this is a cache, where as long as the node has the data it\n# should be able to serve it. \n#\n# The second use case is for configurations that don't meet the recommended  \n# three shards but want to enable cluster mode and scale later. A \n# master outage in a 1 or 2 shard configuration causes a read/write outage to the\n# entire cluster without this option set, with it set there is only a write outage.\n# Without a quorum of masters, slot ownership will not change automatically. \n#\n# cluster-allow-reads-when-down no\n\n# In order to setup your cluster make sure to read the documentation\n# available at http://redis.io web site.\n\n########################## CLUSTER DOCKER/NAT support  ########################\n\n# In certain deployments, Redis Cluster nodes address discovery fails, because\n# addresses are NAT-ted or because ports are forwarded (the typical case is\n# Docker and other containers).\n#\n# In order to make Redis Cluster working in such environments, a static\n# configuration where each node knows its public address is needed. The\n# following two options are used for this scope, and are:\n#\n# * cluster-announce-ip\n# * cluster-announce-port\n# * cluster-announce-bus-port\n#\n# Each instruct the node about its address, client port, and cluster message\n# bus port. The information is then published in the header of the bus packets\n# so that other nodes will be able to correctly map the address of the node\n# publishing the information.\n#\n# If the above options are not used, the normal Redis Cluster auto-detection\n# will be used instead.\n#\n# Note that when remapped, the bus port may not be at the fixed offset of\n# clients port + 10000, so you can specify any port and bus-port depending\n# on how they get remapped. If the bus-port is not set, a fixed offset of\n# 10000 will be used as usually.\n#\n# Example:\n#\n# cluster-announce-ip 10.1.1.5\n# cluster-announce-port 6379\n# cluster-announce-bus-port 6380\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################################ LATENCY MONITOR ##############################\n\n# The Redis latency monitoring subsystem samples different operations\n# at runtime in order to collect data related to possible sources of\n# latency of a Redis instance.\n#\n# Via the LATENCY command this information is available to the user that can\n# print graphs and obtain reports.\n#\n# The system only logs operations that were performed in a time equal or\n# greater than the amount of milliseconds specified via the\n# latency-monitor-threshold configuration directive. When its value is set\n# to zero, the latency monitor is turned off.\n#\n# By default latency monitoring is disabled since it is mostly not needed\n# if you don't have latency issues, and collecting data has a performance\n# impact, that while very small, can be measured under big load. Latency\n# monitoring can easily be enabled at runtime using the command\n# \"CONFIG SET latency-monitor-threshold <milliseconds>\" if needed.\nlatency-monitor-threshold 0\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/notifications\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#  t     Stream commands\n#  m     Key-miss events (Note: It is not included in the 'A' class)\n#  A     Alias for g$lshzxet, so that the \"AKE\" string means all the events\n#        (Except key-miss events which are excluded from 'A' due to their\n#         unique nature).\n#\n#  The \"notify-keyspace-events\" takes as argument a string that is composed\n#  of zero or multiple characters. The empty string means that notifications\n#  are disabled.\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############################### GOPHER SERVER #################################\n\n# Redis contains an implementation of the Gopher protocol, as specified in\n# the RFC 1436 (https://www.ietf.org/rfc/rfc1436.txt).\n#\n# The Gopher protocol was very popular in the late '90s. It is an alternative\n# to the web, and the implementation both server and client side is so simple\n# that the Redis server has just 100 lines of code in order to implement this\n# support.\n#\n# What do you do with Gopher nowadays? Well Gopher never *really* died, and\n# lately there is a movement in order for the Gopher more hierarchical content\n# composed of just plain text documents to be resurrected. Some want a simpler\n# internet, others believe that the mainstream internet became too much\n# controlled, and it's cool to create an alternative space for people that\n# want a bit of fresh air.\n#\n# Anyway for the 10nth birthday of the Redis, we gave it the Gopher protocol\n# as a gift.\n#\n# --- HOW IT WORKS? ---\n#\n# The Redis Gopher support uses the inline protocol of Redis, and specifically\n# two kind of inline requests that were anyway illegal: an empty request\n# or any request that starts with \"/\" (there are no Redis commands starting\n# with such a slash). Normal RESP2/RESP3 requests are completely out of the\n# path of the Gopher protocol implementation and are served as usually as well.\n#\n# If you open a connection to Redis when Gopher is enabled and send it\n# a string like \"/foo\", if there is a key named \"/foo\" it is served via the\n# Gopher protocol.\n#\n# In order to create a real Gopher \"hole\" (the name of a Gopher site in Gopher\n# talking), you likely need a script like the following:\n#\n#   https://github.com/antirez/gopher2redis\n#\n# --- SECURITY WARNING ---\n#\n# If you plan to put Redis on the internet in a publicly accessible address\n# to server Gopher pages MAKE SURE TO SET A PASSWORD to the instance.\n# Once a password is set:\n#\n#   1. The Gopher server (when enabled, not by default) will still serve\n#      content via Gopher.\n#   2. However other commands cannot be called before the client will\n#      authenticate.\n#\n# So use the 'requirepass' option to protect your instance.\n#\n# To enable Gopher support uncomment the following line and set\n# the option from no (the default) to yes.\n#\n# gopher-enabled no\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# Lists are also encoded in a special way to save a lot of space.\n# The number of entries allowed per internal list node can be specified\n# as a fixed maximum size or a maximum number of elements.\n# For a fixed maximum size, use -5 through -1, meaning:\n# -5: max size: 64 Kb  <-- not recommended for normal workloads\n# -4: max size: 32 Kb  <-- not recommended\n# -3: max size: 16 Kb  <-- probably not recommended\n# -2: max size: 8 Kb   <-- good\n# -1: max size: 4 Kb   <-- good\n# Positive numbers mean store up to _exactly_ that number of elements\n# per list node.\n# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),\n# but if your use case is unique, adjust the settings as necessary.\nlist-max-ziplist-size -2\n\n# Lists may also be compressed.\n# Compress depth is the number of quicklist ziplist nodes from *each* side of\n# the list to *exclude* from compression.  The head and tail of the list\n# are always uncompressed for fast push/pop operations.  Settings are:\n# 0: disable all list compression\n# 1: depth 1 means \"don't start compressing until after 1 node into the list,\n#    going from either the head or tail\"\n#    So: [head]->node->node->...->node->[tail]\n#    [head], [tail] will always be uncompressed; inner nodes will compress.\n# 2: [head]->[next]->node->node->...->node->[prev]->[tail]\n#    2 here means: don't compress head or head->next or tail->prev or tail,\n#    but compress all nodes between them.\n# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]\n# etc.\nlist-compress-depth 0\n\n# Sets have a special encoding in just one case: when a set is composed\n# of just strings that happen 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# Streams macro node max size / items. The stream data structure is a radix\n# tree of big nodes that encode multiple items inside. Using this configuration\n# it is possible to configure how big a single node can be in bytes, and the\n# maximum number of items it may contain before switching to a new node when\n# appending new stream entries. If any of the following settings are set to\n# zero, the limit is ignored, so for instance it is possible to set just a\n# max entires limit by setting max-bytes to 0 and max-entries to the desired\n# value.\nstream-node-max-bytes 4096\nstream-node-max-entries 100\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# actively rehash 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 from 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 including MONITOR clients\n# replica  -> replica 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 replica clients, since\n# subscribers and replicas 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 replica 256mb 64mb 60\nclient-output-buffer-limit pubsub 32mb 8mb 60\n\n# Client query buffers accumulate new commands. They are limited to a fixed\n# amount by default in order to avoid that a protocol desynchronization (for\n# instance due to a bug in the client) will lead to unbound memory usage in\n# the query buffer. However you can configure it here if you have very special\n# needs, such us huge multi/exec requests or alike.\n#\n# client-query-buffer-limit 1gb\n\n# In the Redis protocol, bulk requests, that are, elements representing single\n# strings, are normally limited ot 512 mb. However you can change this limit\n# here.\n#\n# proto-max-bulk-len 512mb\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 according 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# Normally it is useful to have an HZ value which is proportional to the\n# number of clients connected. This is useful in order, for instance, to\n# avoid too many clients are processed for each background task invocation\n# in order to avoid latency spikes.\n#\n# Since the default HZ value by default is conservatively set to 10, Redis\n# offers, and enables by default, the ability to use an adaptive HZ value\n# which will temporary raise when there are many connected clients.\n#\n# When dynamic HZ is enabled, the actual configured HZ will be used\n# as a baseline, but multiples of the configured HZ value will be actually\n# used as needed once more clients are connected. In this way an idle\n# instance will use very little CPU time while a busy instance will be\n# more responsive.\ndynamic-hz yes\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# When redis saves RDB 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.\nrdb-save-incremental-fsync yes\n\n# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good\n# idea to start with the default settings and only change them after investigating\n# how to improve the performances and how the keys LFU change over time, which\n# is possible to inspect via the OBJECT FREQ command.\n#\n# There are two tunable parameters in the Redis LFU implementation: the\n# counter logarithm factor and the counter decay time. It is important to\n# understand what the two parameters mean before changing them.\n#\n# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis\n# uses a probabilistic increment with logarithmic behavior. Given the value\n# of the old counter, when a key is accessed, the counter is incremented in\n# this way:\n#\n# 1. A random number R between 0 and 1 is extracted.\n# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1).\n# 3. The counter is incremented only if R < P.\n#\n# The default lfu-log-factor is 10. This is a table of how the frequency\n# counter changes with a different number of accesses with different\n# logarithmic factors:\n#\n# +--------+------------+------------+------------+------------+------------+\n# | factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |\n# +--------+------------+------------+------------+------------+------------+\n# | 0      | 104        | 255        | 255        | 255        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n# | 1      | 18         | 49         | 255        | 255        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n# | 10     | 10         | 18         | 142        | 255        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n# | 100    | 8          | 11         | 49         | 143        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n#\n# NOTE: The above table was obtained by running the following commands:\n#\n#   redis-benchmark -n 1000000 incr foo\n#   redis-cli object freq foo\n#\n# NOTE 2: The counter initial value is 5 in order to give new objects a chance\n# to accumulate hits.\n#\n# The counter decay time is the time, in minutes, that must elapse in order\n# for the key counter to be divided by two (or decremented if it has a value\n# less <= 10).\n#\n# The default value for the lfu-decay-time is 1. A Special value of 0 means to\n# decay the counter every time it happens to be scanned.\n#\n# lfu-log-factor 10\n# lfu-decay-time 1\n\n########################### ACTIVE DEFRAGMENTATION #######################\n#\n# What is active defragmentation?\n# -------------------------------\n#\n# Active (online) defragmentation allows a Redis server to compact the\n# spaces left between small allocations and deallocations of data in memory,\n# thus allowing to reclaim back memory.\n#\n# Fragmentation is a natural process that happens with every allocator (but\n# less so with Jemalloc, fortunately) and certain workloads. Normally a server\n# restart is needed in order to lower the fragmentation, or at least to flush\n# away all the data and create it again. However thanks to this feature\n# implemented by Oran Agra for Redis 4.0 this process can happen at runtime\n# in an \"hot\" way, while the server is running.\n#\n# Basically when the fragmentation is over a certain level (see the\n# configuration options below) Redis will start to create new copies of the\n# values in contiguous memory regions by exploiting certain specific Jemalloc\n# features (in order to understand if an allocation is causing fragmentation\n# and to allocate it in a better place), and at the same time, will release the\n# old copies of the data. This process, repeated incrementally for all the keys\n# will cause the fragmentation to drop back to normal values.\n#\n# Important things to understand:\n#\n# 1. This feature is disabled by default, and only works if you compiled Redis\n#    to use the copy of Jemalloc we ship with the source code of Redis.\n#    This is the default with Linux builds.\n#\n# 2. You never need to enable this feature if you don't have fragmentation\n#    issues.\n#\n# 3. Once you experience fragmentation, you can enable this feature when\n#    needed with the command \"CONFIG SET activedefrag yes\".\n#\n# The configuration parameters are able to fine tune the behavior of the\n# defragmentation process. If you are not sure about what they mean it is\n# a good idea to leave the defaults untouched.\n\n# Enabled active defragmentation\n# activedefrag no\n\n# Minimum amount of fragmentation waste to start active defrag\n# active-defrag-ignore-bytes 100mb\n\n# Minimum percentage of fragmentation to start active defrag\n# active-defrag-threshold-lower 10\n\n# Maximum percentage of fragmentation at which we use maximum effort\n# active-defrag-threshold-upper 100\n\n# Minimal effort for defrag in CPU percentage, to be used when the lower\n# threshold is reached\n# active-defrag-cycle-min 1\n\n# Maximal effort for defrag in CPU percentage, to be used when the upper\n# threshold is reached\n# active-defrag-cycle-max 25\n\n# Maximum number of set/hash/zset/list fields that will be processed from\n# the main dictionary scan\n# active-defrag-max-scan-fields 1000\n\n# Jemalloc background thread for purging will be enabled by default\njemalloc-bg-thread yes\n\n# It is possible to pin different threads and processes of Redis to specific\n# CPUs in your system, in order to maximize the performances of the server.\n# This is useful both in order to pin different Redis threads in different\n# CPUs, but also in order to make sure that multiple Redis instances running\n# in the same host will be pinned to different CPUs.\n#\n# Normally you can do this using the \"taskset\" command, however it is also\n# possible to this via Redis configuration directly, both in Linux and FreeBSD.\n#\n# You can pin the server/IO threads, bio threads, aof rewrite child process, and\n# the bgsave child process. The syntax to specify the cpu list is the same as\n# the taskset command:\n#\n# Set redis server/io threads to cpu affinity 0,2,4,6:\n# server_cpulist 0-7:2\n#\n# Set bio threads to cpu affinity 1,3:\n# bio_cpulist 1,3\n#\n# Set aof rewrite child process to cpu affinity 8,9,10,11:\n# aof_rewrite_cpulist 8-11\n#\n# Set bgsave child process to cpu affinity 1,10,11\n# bgsave_cpulist 1,10-11\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/runtest-moduleapi",
    "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\nmake -C tests/modules && \\\n$TCLSH tests/test_helper.tcl \\\n--single unit/moduleapi/commandfilter \\\n--single unit/moduleapi/fork \\\n--single unit/moduleapi/testrdb \\\n--single unit/moduleapi/infotest \\\n--single unit/moduleapi/propagate \\\n--single unit/moduleapi/hooks \\\n--single unit/moduleapi/misc \\\n--single unit/moduleapi/blockonkeys \\\n--single unit/moduleapi/scan \\\n--single unit/moduleapi/datatype \\\n--single unit/moduleapi/auth \\\n\"${@}\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/sentinel.conf",
    "content": "# Example sentinel.conf\n\n# *** IMPORTANT ***\n#\n# By default Sentinel will not be reachable from interfaces different than\n# localhost, either use the 'bind' directive to bind to a list of network\n# interfaces, or disable protected mode with \"protected-mode no\" by\n# adding it to this configuration file.\n#\n# Before doing that MAKE SURE the instance is protected from the outside\n# world via firewalling or other means.\n#\n# For example you may use one of the following:\n#\n# bind 127.0.0.1 192.168.1.1\n#\n# protected-mode no\n\n# port <sentinel-port>\n# The port that this sentinel instance will run on\nport 26379\n\n# By default Redis Sentinel 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-sentinel.pid when\n# daemonized.\ndaemonize no\n\n# When running daemonized, Redis Sentinel writes a pid file in\n# /var/run/redis-sentinel.pid by default. You can specify a custom pid file\n# location here.\npidfile /var/run/redis-sentinel.pid\n\n# Specify the log file name. Also the empty string can be used to force\n# Sentinel 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# sentinel announce-ip <ip>\n# sentinel announce-port <port>\n#\n# The above two configuration directives are useful in environments where,\n# because of NAT, Sentinel is reachable from outside via a non-local address.\n#\n# When announce-ip is provided, the Sentinel will claim the specified IP address\n# in HELLO messages used to gossip its presence, instead of auto-detecting the\n# local address as it usually does.\n#\n# Similarly when announce-port is provided and is valid and non-zero, Sentinel\n# will announce the specified TCP port.\n#\n# The two options don't need to be used together, if only announce-ip is\n# provided, the Sentinel will announce the specified IP and the server port\n# as specified by the \"port\" option. If only announce-port is provided, the\n# Sentinel will announce the auto-detected local IP and the specified port.\n#\n# Example:\n#\n# sentinel announce-ip 1.2.3.4\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 interfere 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# Replicas are auto-discovered, so you don't need to specify replicas in\n# any way. Sentinel itself will rewrite this configuration file adding\n# the replicas using additional configuration options.\n# Also note that the configuration file is rewritten when a\n# replica is promoted to master.\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 replicas.\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 replicas, so it is not\n# possible to set a different password in masters and replicas 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 auth-user <master-name> <username>\n#\n# This is useful in order to authenticate to instances having ACL capabilities,\n# that is, running Redis 6.0 or greater. When just auth-pass is provided the\n# Sentinel instance will authenticate to Redis using the old \"AUTH <pass>\"\n# method. When also an username is provided, it will use \"AUTH <user> <pass>\".\n# In the Redis servers side, the ACL to provide just minimal access to\n# Sentinel instances, should be configured along the following lines:\n#\n#     user sentinel-user >somepassword +client +subscribe +publish \\\n#                        +ping +info +multi +slaveof +config +client +exec on\n\n# sentinel down-after-milliseconds <master-name> <milliseconds>\n#\n# Number of milliseconds the master (or any attached replica 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# requirepass <password>\n#\n# You can configure Sentinel itself to require a password, however when doing\n# so Sentinel will try to authenticate with the same password to all the\n# other Sentinels. So you need to configure all your Sentinels in a given\n# group with the same \"requirepass\" password. Check the following documentation\n# for more info: https://redis.io/topics/sentinel\n\n# sentinel parallel-syncs <master-name> <numreplicas>\n#\n# How many replicas we can reconfigure to point to the new replica simultaneously\n# during the failover. Use a low number if you use the replicas to serve query\n# to avoid that all the replicas 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 replica 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 replica).\n#\n# - The maximum time a failover in progress waits for all the replicas to be\n#   reconfigured as replicas of the new master. However even after this time\n#   the replicas 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 replica\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# SECURITY\n#\n# By default SENTINEL SET will not be able to change the notification-script\n# and client-reconfig-script at runtime. This avoids a trivial security issue\n# where clients can set the script to anything and trigger a failover in order\n# to get the program executed.\n\nsentinel deny-scripts-reconfig yes\n\n# REDIS COMMANDS RENAMING\n#\n# Sometimes the Redis server has certain commands, that are needed for Sentinel\n# to work correctly, renamed to unguessable strings. This is often the case\n# of CONFIG and SLAVEOF in the context of providers that provide Redis as\n# a service, and don't want the customers to reconfigure the instances outside\n# of the administration console.\n#\n# In such case it is possible to tell Sentinel to use different command names\n# instead of the normal ones. For example if the master \"mymaster\", and the\n# associated replicas, have \"CONFIG\" all renamed to \"GUESSME\", I could use:\n#\n# SENTINEL rename-command mymaster CONFIG GUESSME\n#\n# After such configuration is set, every time Sentinel would use CONFIG it will\n# use GUESSME instead. Note that there is no actual need to respect the command\n# case, so writing \"config guessme\" is the same in the example above.\n#\n# SENTINEL SET can also be used in order to perform this configuration at runtime.\n#\n# In order to set a command back to its original name (undo the renaming), it\n# is possible to just rename a command to itsef:\n#\n# SENTINEL rename-command mymaster CONFIG CONFIG\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/.gitignore",
    "content": "*.gcda\n*.gcno\n*.gcov\nredis.info\nlcov-html\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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')\nuname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')\nOPTIMIZATION?=-O2\nDEPENDENCY_TARGETS=hiredis linenoise lua\nNODEPS:=clean distclean\n\n# Default settings\nSTD=-std=c11 -pedantic -DREDIS_STATIC=''\nifneq (,$(findstring clang,$(CC)))\nifneq (,$(findstring FreeBSD,$(uname_S)))\n  STD+=-Wno-c11-extensions\nendif\nendif\nWARN=-Wall -W -Wno-missing-field-initializers\nOPT=$(OPTIMIZATION)\n\nPREFIX?=/usr/local\nINSTALL_BIN=$(PREFIX)/bin\nINSTALL=install\nPKG_CONFIG?=pkg-config\n\n# Default allocator defaults to Jemalloc if it's not an ARM\nMALLOC=libc\nifneq ($(uname_M),armv6l)\nifneq ($(uname_M),armv7l)\nifeq ($(uname_S),Linux)\n\tMALLOC=jemalloc\nendif\nendif\nendif\n\n# To get ARM stack traces if Redis crashes we need a special C flag.\nifneq (,$(filter aarch64 armv,$(uname_M)))\n        CFLAGS+=-funwind-tables\nelse\nifneq (,$(findstring armv,$(uname_M)))\n        CFLAGS+=-funwind-tables\nendif\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\nifeq ($(USE_JEMALLOC),no)\n\tMALLOC=libc\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\n# Linux ARM needs -latomic at linking time\nifneq (,$(filter aarch64 armv,$(uname_M)))\n        FINAL_LIBS+=-latomic\nelse\nifneq (,$(findstring armv,$(uname_M)))\n        FINAL_LIBS+=-latomic\nendif\nendif\n\nifeq ($(uname_S),SunOS)\n\t# SunOS\n        ifneq ($(@@),32bit)\n\t\tCFLAGS+= -m64\n\t\tLDFLAGS+= -m64\n\tendif\n\tDEBUG=-g\n\tDEBUG_FLAGS=-g\n\texport CFLAGS LDFLAGS DEBUG DEBUG_FLAGS\n\tINSTALL=cp -pf\n\tFINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6\n\tFINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt\nelse\nifeq ($(uname_S),Darwin)\n\t# Darwin\n\tFINAL_LIBS+= -ldl\n\tOPENSSL_CFLAGS=-I/usr/local/opt/openssl/include\n\tOPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib\nelse\nifeq ($(uname_S),AIX)\n        # AIX\n        FINAL_LDFLAGS+= -Wl,-bexpall\n        FINAL_LIBS+=-ldl -pthread -lcrypt -lbsd\nelse\nifeq ($(uname_S),OpenBSD)\n\t# OpenBSD\n\tFINAL_LIBS+= -lpthread\n\tifeq ($(USE_BACKTRACE),yes)\n\t    FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/local/include\n\t    FINAL_LDFLAGS+= -L/usr/local/lib\n\t    FINAL_LIBS+= -lexecinfo\n    \tendif\n\nelse\nifeq ($(uname_S),FreeBSD)\n\t# FreeBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\nifeq ($(uname_S),DragonFly)\n\t# FreeBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\nifeq ($(uname_S),OpenBSD)\n\t# OpenBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\nifeq ($(uname_S),NetBSD)\n\t# NetBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\n\t# All the other OSes (notably Linux)\n\tFINAL_LDFLAGS+= -rdynamic\n\tFINAL_LIBS+=-ldl -pthread -lrt\nendif\nendif\nendif\nendif\nendif\nendif\nendif\nendif\n# Include paths to dependencies\nFINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src\n\n# Determine systemd support and/or build preference (defaulting to auto-detection)\nBUILD_WITH_SYSTEMD=no\n# If 'USE_SYSTEMD' in the environment is neither \"no\" nor \"yes\", try to\n# auto-detect libsystemd's presence and link accordingly.\nifneq ($(USE_SYSTEMD),no)\n\tLIBSYSTEMD_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libsystemd && echo $$?)\n# If libsystemd cannot be detected, continue building without support for it\n# (unless a later check tells us otherwise)\nifeq ($(LIBSYSTEMD_PKGCONFIG),0)\n\tBUILD_WITH_SYSTEMD=yes\nendif\nendif\nifeq ($(USE_SYSTEMD),yes)\nifneq ($(LIBSYSTEMD_PKGCONFIG),0)\n$(error USE_SYSTEMD is set to \"$(USE_SYSTEMD)\", but $(PKG_CONFIG) cannot find libsystemd)\nendif\n# Force building with libsystemd\n\tBUILD_WITH_SYSTEMD=yes\nendif\nifeq ($(BUILD_WITH_SYSTEMD),yes)\n\tFINAL_LIBS+=$(shell $(PKG_CONFIG) --libs libsystemd)\n\tFINAL_CFLAGS+= -DHAVE_LIBSYSTEMD\nendif\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 $(FINAL_LIBS)\nendif\n\nifeq ($(BUILD_TLS),yes)\n    FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS)\n    FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)\n    FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto\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 quicklist.o ae.o anet.o dict.o server.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 crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o\nREDIS_CLI_NAME=redis-cli\nREDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o\nREDIS_BENCHMARK_NAME=redis-benchmark\nREDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o\nREDIS_CHECK_RDB_NAME=redis-check-rdb\nREDIS_CHECK_AOF_NAME=redis-check-aof\n\nall: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME)\n\t@echo \"\"\n\t@echo \"Hint: It's a good idea to run 'make test' ;)\"\n\t@echo \"\"\n\nMakefile.dep:\n\t-$(REDIS_CC) -MM *.c > Makefile.dep 2> /dev/null || true\n\nifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))\n-include Makefile.dep\nendif\n\n.PHONY: all\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-check-rdb\n$(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME)\n\t$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME)\n\n# redis-check-aof\n$(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)\n\t$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_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\ndict-benchmark: dict.c zmalloc.c sds.c siphash.c\n\t$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)\n\nDEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)\n-include $(DEP)\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) -MMD -o $@ -c $<\n\nclean:\n\trm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark\n\trm -f $(DEP)\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\ntest-sds: sds.c sds.h\n\t$(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test\n\t/tmp/sds_test\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\nhelgrind:\n\t$(MAKE) OPTIMIZATION=\"-O0\" MALLOC=\"libc\" CFLAGS=\"-D__ATOMIC_VAR_FORCE_SYNC_MACROS\"\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_RDB_NAME) $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)\n\t@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)\n\nuninstall:\n\trm -f $(INSTALL_BIN)/{$(REDIS_SERVER_NAME),$(REDIS_BENCHMARK_NAME),$(REDIS_CLI_NAME),$(REDIS_CHECK_RDB_NAME),$(REDIS_CHECK_AOF_NAME),$(REDIS_SENTINEL_NAME)}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/acl.c",
    "content": "/*\n * Copyright (c) 2018, 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 \"server.h\"\n#include \"sha256.h\"\n#include <fcntl.h>\n#include <ctype.h>\n\n/* =============================================================================\n * Global state for ACLs\n * ==========================================================================*/\n\nrax *Users; /* Table mapping usernames to user structures. */\n\nuser *DefaultUser;  /* Global reference to the default user.\n                       Every new connection is associated to it, if no\n                       AUTH or HELLO is used to authenticate with a\n                       different user. */\n\nlist *UsersToLoad;  /* This is a list of users found in the configuration file\n                       that we'll need to load in the final stage of Redis\n                       initialization, after all the modules are already\n                       loaded. Every list element is a NULL terminated\n                       array of SDS pointers: the first is the user name,\n                       all the remaining pointers are ACL rules in the same\n                       format as ACLSetUser(). */\nlist *ACLLog;       /* Our security log, the user is able to inspect that\n                       using the ACL LOG command .*/\n\nstruct ACLCategoryItem {\n    const char *name;\n    uint64_t flag;\n} ACLCommandCategories[] = {\n    {\"keyspace\", CMD_CATEGORY_KEYSPACE},\n    {\"read\", CMD_CATEGORY_READ},\n    {\"write\", CMD_CATEGORY_WRITE},\n    {\"set\", CMD_CATEGORY_SET},\n    {\"sortedset\", CMD_CATEGORY_SORTEDSET},\n    {\"list\", CMD_CATEGORY_LIST},\n    {\"hash\", CMD_CATEGORY_HASH},\n    {\"string\", CMD_CATEGORY_STRING},\n    {\"bitmap\", CMD_CATEGORY_BITMAP},\n    {\"hyperloglog\", CMD_CATEGORY_HYPERLOGLOG},\n    {\"geo\", CMD_CATEGORY_GEO},\n    {\"stream\", CMD_CATEGORY_STREAM},\n    {\"pubsub\", CMD_CATEGORY_PUBSUB},\n    {\"admin\", CMD_CATEGORY_ADMIN},\n    {\"fast\", CMD_CATEGORY_FAST},\n    {\"slow\", CMD_CATEGORY_SLOW},\n    {\"blocking\", CMD_CATEGORY_BLOCKING},\n    {\"dangerous\", CMD_CATEGORY_DANGEROUS},\n    {\"connection\", CMD_CATEGORY_CONNECTION},\n    {\"transaction\", CMD_CATEGORY_TRANSACTION},\n    {\"scripting\", CMD_CATEGORY_SCRIPTING},\n    {NULL,0} /* Terminator. */\n};\n\nstruct ACLUserFlag {\n    const char *name;\n    uint64_t flag;\n} ACLUserFlags[] = {\n    {\"on\", USER_FLAG_ENABLED},\n    {\"off\", USER_FLAG_DISABLED},\n    {\"allkeys\", USER_FLAG_ALLKEYS},\n    {\"allcommands\", USER_FLAG_ALLCOMMANDS},\n    {\"nopass\", USER_FLAG_NOPASS},\n    {NULL,0} /* Terminator. */\n};\n\nvoid ACLResetSubcommandsForCommand(user *u, unsigned long id);\nvoid ACLResetSubcommands(user *u);\nvoid ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub);\nvoid ACLFreeLogEntry(void *le);\n\n/* The length of the string representation of a hashed password. */\n#define HASH_PASSWORD_LEN SHA256_BLOCK_SIZE*2\n\n/* =============================================================================\n * Helper functions for the rest of the ACL implementation\n * ==========================================================================*/\n\n/* Return zero if strings are the same, non-zero if they are not.\n * The comparison is performed in a way that prevents an attacker to obtain\n * information about the nature of the strings just monitoring the execution\n * time of the function.\n *\n * Note that limiting the comparison length to strings up to 512 bytes we\n * can avoid leaking any information about the password length and any\n * possible branch misprediction related leak.\n */\nint time_independent_strcmp(char *a, char *b) {\n    char bufa[CONFIG_AUTHPASS_MAX_LEN], bufb[CONFIG_AUTHPASS_MAX_LEN];\n    /* The above two strlen perform len(a) + len(b) operations where either\n     * a or b are fixed (our password) length, and the difference is only\n     * relative to the length of the user provided string, so no information\n     * leak is possible in the following two lines of code. */\n    unsigned int alen = strlen(a);\n    unsigned int blen = strlen(b);\n    unsigned int j;\n    int diff = 0;\n\n    /* We can't compare strings longer than our static buffers.\n     * Note that this will never pass the first test in practical circumstances\n     * so there is no info leak. */\n    if (alen > sizeof(bufa) || blen > sizeof(bufb)) return 1;\n\n    memset(bufa,0,sizeof(bufa));        /* Constant time. */\n    memset(bufb,0,sizeof(bufb));        /* Constant time. */\n    /* Again the time of the following two copies is proportional to\n     * len(a) + len(b) so no info is leaked. */\n    memcpy(bufa,a,alen);\n    memcpy(bufb,b,blen);\n\n    /* Always compare all the chars in the two buffers without\n     * conditional expressions. */\n    for (j = 0; j < sizeof(bufa); j++) {\n        diff |= (bufa[j] ^ bufb[j]);\n    }\n    /* Length must be equal as well. */\n    diff |= alen ^ blen;\n    return diff; /* If zero strings are the same. */\n}\n\n/* Given an SDS string, returns the SHA256 hex representation as a\n * new SDS string. */\nsds ACLHashPassword(unsigned char *cleartext, size_t len) {\n    SHA256_CTX ctx;\n    unsigned char hash[SHA256_BLOCK_SIZE];\n    char hex[HASH_PASSWORD_LEN];\n    char *cset = \"0123456789abcdef\";\n\n    sha256_init(&ctx);\n    sha256_update(&ctx,(unsigned char*)cleartext,len);\n    sha256_final(&ctx,hash);\n\n    for (int j = 0; j < SHA256_BLOCK_SIZE; j++) {\n        hex[j*2] = cset[((hash[j]&0xF0)>>4)];\n        hex[j*2+1] = cset[(hash[j]&0xF)];\n    }\n    return sdsnewlen(hex,HASH_PASSWORD_LEN);\n}\n\n/* =============================================================================\n * Low level ACL API\n * ==========================================================================*/\n\n/* Return 1 if the specified string contains spaces or null characters.\n * We do this for usernames and key patterns for simpler rewriting of\n * ACL rules, presentation on ACL list, and to avoid subtle security bugs\n * that may arise from parsing the rules in presence of escapes.\n * The function returns 0 if the string has no spaces. */\nint ACLStringHasSpaces(const char *s, size_t len) {\n    for (size_t i = 0; i < len; i++) {\n        if (isspace(s[i]) || s[i] == 0) return 1;\n    }\n    return 0;\n}\n\n/* Given the category name the command returns the corresponding flag, or\n * zero if there is no match. */\nuint64_t ACLGetCommandCategoryFlagByName(const char *name) {\n    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {\n        if (!strcasecmp(name,ACLCommandCategories[j].name)) {\n            return ACLCommandCategories[j].flag;\n        }\n    }\n    return 0; /* No match. */\n}\n\n/* Method for passwords/pattern comparison used for the user->passwords list\n * so that we can search for items with listSearchKey(). */\nint ACLListMatchSds(void *a, void *b) {\n    return sdscmp(a,b) == 0;\n}\n\n/* Method to free list elements from ACL users password/patterns lists. */\nvoid ACLListFreeSds(void *item) {\n    sdsfree(item);\n}\n\n/* Method to duplicate list elements from ACL users password/patterns lists. */\nvoid *ACLListDupSds(void *item) {\n    return sdsdup(item);\n}\n\n/* Create a new user with the specified name, store it in the list\n * of users (the Users global radix tree), and returns a reference to\n * the structure representing the user.\n *\n * If the user with such name already exists NULL is returned. */\nuser *ACLCreateUser(const char *name, size_t namelen) {\n    if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;\n    user *u = zmalloc(sizeof(*u));\n    u->name = sdsnewlen(name,namelen);\n    u->flags = USER_FLAG_DISABLED;\n    u->allowed_subcommands = NULL;\n    u->passwords = listCreate();\n    u->patterns = listCreate();\n    listSetMatchMethod(u->passwords,ACLListMatchSds);\n    listSetFreeMethod(u->passwords,ACLListFreeSds);\n    listSetDupMethod(u->passwords,ACLListDupSds);\n    listSetMatchMethod(u->patterns,ACLListMatchSds);\n    listSetFreeMethod(u->patterns,ACLListFreeSds);\n    listSetDupMethod(u->patterns,ACLListDupSds);\n    memset(u->allowed_commands,0,sizeof(u->allowed_commands));\n    raxInsert(Users,(unsigned char*)name,namelen,u,NULL);\n    return u;\n}\n\n/* This function should be called when we need an unlinked \"fake\" user\n * we can use in order to validate ACL rules or for other similar reasons.\n * The user will not get linked to the Users radix tree. The returned\n * user should be released with ACLFreeUser() as usually. */\nuser *ACLCreateUnlinkedUser(void) {\n    char username[64];\n    for (int j = 0; ; j++) {\n        snprintf(username,sizeof(username),\"__fakeuser:%d__\",j);\n        user *fakeuser = ACLCreateUser(username,strlen(username));\n        if (fakeuser == NULL) continue;\n        int retval = raxRemove(Users,(unsigned char*) username,\n                               strlen(username),NULL);\n        serverAssert(retval != 0);\n        return fakeuser;\n    }\n}\n\n/* Release the memory used by the user structure. Note that this function\n * will not remove the user from the Users global radix tree. */\nvoid ACLFreeUser(user *u) {\n    sdsfree(u->name);\n    listRelease(u->passwords);\n    listRelease(u->patterns);\n    ACLResetSubcommands(u);\n    zfree(u);\n}\n\n/* When a user is deleted we need to cycle the active\n * connections in order to kill all the pending ones that\n * are authenticated with such user. */\nvoid ACLFreeUserAndKillClients(user *u) {\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client *c = listNodeValue(ln);\n        if (c->user == u) {\n            /* We'll free the conenction asynchronously, so\n             * in theory to set a different user is not needed.\n             * However if there are bugs in Redis, soon or later\n             * this may result in some security hole: it's much\n             * more defensive to set the default user and put\n             * it in non authenticated mode. */\n            c->user = DefaultUser;\n            c->authenticated = 0;\n            freeClientAsync(c);\n        }\n    }\n    ACLFreeUser(u);\n}\n\n/* Copy the user ACL rules from the source user 'src' to the destination\n * user 'dst' so that at the end of the process they'll have exactly the\n * same rules (but the names will continue to be the original ones). */\nvoid ACLCopyUser(user *dst, user *src) {\n    listRelease(dst->passwords);\n    listRelease(dst->patterns);\n    dst->passwords = listDup(src->passwords);\n    dst->patterns = listDup(src->patterns);\n    memcpy(dst->allowed_commands,src->allowed_commands,\n           sizeof(dst->allowed_commands));\n    dst->flags = src->flags;\n    ACLResetSubcommands(dst);\n    /* Copy the allowed subcommands array of array of SDS strings. */\n    if (src->allowed_subcommands) {\n        for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {\n            if (src->allowed_subcommands[j]) {\n                for (int i = 0; src->allowed_subcommands[j][i]; i++)\n                {\n                    ACLAddAllowedSubcommand(dst, j,\n                        src->allowed_subcommands[j][i]);\n                }\n            }\n        }\n    }\n}\n\n/* Free all the users registered in the radix tree 'users' and free the\n * radix tree itself. */\nvoid ACLFreeUsersSet(rax *users) {\n    raxFreeWithCallback(users,(void(*)(void*))ACLFreeUserAndKillClients);\n}\n\n/* Given a command ID, this function set by reference 'word' and 'bit'\n * so that user->allowed_commands[word] will address the right word\n * where the corresponding bit for the provided ID is stored, and\n * so that user->allowed_commands[word]&bit will identify that specific\n * bit. The function returns C_ERR in case the specified ID overflows\n * the bitmap in the user representation. */\nint ACLGetCommandBitCoordinates(uint64_t id, uint64_t *word, uint64_t *bit) {\n    if (id >= USER_COMMAND_BITS_COUNT) return C_ERR;\n    *word = id / sizeof(uint64_t) / 8;\n    *bit = 1ULL << (id % (sizeof(uint64_t) * 8));\n    return C_OK;\n}\n\n/* Check if the specified command bit is set for the specified user.\n * The function returns 1 is the bit is set or 0 if it is not.\n * Note that this function does not check the ALLCOMMANDS flag of the user\n * but just the lowlevel bitmask.\n *\n * If the bit overflows the user internal representation, zero is returned\n * in order to disallow the execution of the command in such edge case. */\nint ACLGetUserCommandBit(user *u, unsigned long id) {\n    uint64_t word, bit;\n    if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0;\n    return (u->allowed_commands[word] & bit) != 0;\n}\n\n/* When +@all or allcommands is given, we set a reserved bit as well that we\n * can later test, to see if the user has the right to execute \"future commands\",\n * that is, commands loaded later via modules. */\nint ACLUserCanExecuteFutureCommands(user *u) {\n    return ACLGetUserCommandBit(u,USER_COMMAND_BITS_COUNT-1);\n}\n\n/* Set the specified command bit for the specified user to 'value' (0 or 1).\n * If the bit overflows the user internal representation, no operation\n * is performed. As a side effect of calling this function with a value of\n * zero, the user flag ALLCOMMANDS is cleared since it is no longer possible\n * to skip the command bit explicit test. */\nvoid ACLSetUserCommandBit(user *u, unsigned long id, int value) {\n    uint64_t word, bit;\n    if (value == 0) u->flags &= ~USER_FLAG_ALLCOMMANDS;\n    if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return;\n    if (value)\n        u->allowed_commands[word] |= bit;\n    else\n        u->allowed_commands[word] &= ~bit;\n}\n\n/* This is like ACLSetUserCommandBit(), but instead of setting the specified\n * ID, it will check all the commands in the category specified as argument,\n * and will set all the bits corresponding to such commands to the specified\n * value. Since the category passed by the user may be non existing, the\n * function returns C_ERR if the category was not found, or C_OK if it was\n * found and the operation was performed. */\nint ACLSetUserCommandBitsForCategory(user *u, const char *category, int value) {\n    uint64_t cflag = ACLGetCommandCategoryFlagByName(category);\n    if (!cflag) return C_ERR;\n    dictIterator *di = dictGetIterator(server.orig_commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        if (cmd->flags & CMD_MODULE) continue; /* Ignore modules commands. */\n        if (cmd->flags & cflag) {\n            ACLSetUserCommandBit(u,cmd->id,value);\n            ACLResetSubcommandsForCommand(u,cmd->id);\n        }\n    }\n    dictReleaseIterator(di);\n    return C_OK;\n}\n\n/* Return the number of commands allowed (on) and denied (off) for the user 'u'\n * in the subset of commands flagged with the specified category name.\n * If the category name is not valid, C_ERR is returned, otherwise C_OK is\n * returned and on and off are populated by reference. */\nint ACLCountCategoryBitsForUser(user *u, unsigned long *on, unsigned long *off,\n                                const char *category)\n{\n    uint64_t cflag = ACLGetCommandCategoryFlagByName(category);\n    if (!cflag) return C_ERR;\n\n    *on = *off = 0;\n    dictIterator *di = dictGetIterator(server.orig_commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        if (cmd->flags & cflag) {\n            if (ACLGetUserCommandBit(u,cmd->id))\n                (*on)++;\n            else\n                (*off)++;\n        }\n    }\n    dictReleaseIterator(di);\n    return C_OK;\n}\n\n/* This function returns an SDS string representing the specified user ACL\n * rules related to command execution, in the same format you could set them\n * back using ACL SETUSER. The function will return just the set of rules needed\n * to recreate the user commands bitmap, without including other user flags such\n * as on/off, passwords and so forth. The returned string always starts with\n * the +@all or -@all rule, depending on the user bitmap, and is followed, if\n * needed, by the other rules needed to narrow or extend what the user can do. */\nsds ACLDescribeUserCommandRules(user *u) {\n    sds rules = sdsempty();\n    int additive;   /* If true we start from -@all and add, otherwise if\n                       false we start from +@all and remove. */\n\n    /* This code is based on a trick: as we generate the rules, we apply\n     * them to a fake user, so that as we go we still know what are the\n     * bit differences we should try to address by emitting more rules. */\n    user fu = {0};\n    user *fakeuser = &fu;\n\n    /* Here we want to understand if we should start with +@all and remove\n     * the commands corresponding to the bits that are not set in the user\n     * commands bitmap, or the contrary. Note that semantically the two are\n     * different. For instance starting with +@all and subtracting, the user\n     * will be able to execute future commands, while -@all and adding will just\n     * allow the user the run the selected commands and/or categories.\n     * How do we test for that? We use the trick of a reserved command ID bit\n     * that is set only by +@all (and its alias \"allcommands\"). */\n    if (ACLUserCanExecuteFutureCommands(u)) {\n        additive = 0;\n        rules = sdscat(rules,\"+@all \");\n        ACLSetUser(fakeuser,\"+@all\",-1);\n    } else {\n        additive = 1;\n        rules = sdscat(rules,\"-@all \");\n        ACLSetUser(fakeuser,\"-@all\",-1);\n    }\n\n    /* Try to add or subtract each category one after the other. Often a\n     * single category will not perfectly match the set of commands into\n     * it, so at the end we do a final pass adding/removing the single commands\n     * needed to make the bitmap exactly match. */\n    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {\n        unsigned long on, off;\n        ACLCountCategoryBitsForUser(u,&on,&off,ACLCommandCategories[j].name);\n        if ((additive && on > off) || (!additive && off > on)) {\n            sds op = sdsnewlen(additive ? \"+@\" : \"-@\", 2);\n            op = sdscat(op,ACLCommandCategories[j].name);\n            ACLSetUser(fakeuser,op,-1);\n            rules = sdscatsds(rules,op);\n            rules = sdscatlen(rules,\" \",1);\n            sdsfree(op);\n        }\n    }\n\n    /* Fix the final ACLs with single commands differences. */\n    dictIterator *di = dictGetIterator(server.orig_commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        int userbit = ACLGetUserCommandBit(u,cmd->id);\n        int fakebit = ACLGetUserCommandBit(fakeuser,cmd->id);\n        if (userbit != fakebit) {\n            rules = sdscatlen(rules, userbit ? \"+\" : \"-\", 1);\n            rules = sdscat(rules,cmd->name);\n            rules = sdscatlen(rules,\" \",1);\n            ACLSetUserCommandBit(fakeuser,cmd->id,userbit);\n        }\n\n        /* Emit the subcommands if there are any. */\n        if (userbit == 0 && u->allowed_subcommands &&\n            u->allowed_subcommands[cmd->id])\n        {\n            for (int j = 0; u->allowed_subcommands[cmd->id][j]; j++) {\n                rules = sdscatlen(rules,\"+\",1);\n                rules = sdscat(rules,cmd->name);\n                rules = sdscatlen(rules,\"|\",1);\n                rules = sdscatsds(rules,u->allowed_subcommands[cmd->id][j]);\n                rules = sdscatlen(rules,\" \",1);\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Trim the final useless space. */\n    sdsrange(rules,0,-2);\n\n    /* This is technically not needed, but we want to verify that now the\n     * predicted bitmap is exactly the same as the user bitmap, and abort\n     * otherwise, because aborting is better than a security risk in this\n     * code path. */\n    if (memcmp(fakeuser->allowed_commands,\n                        u->allowed_commands,\n                        sizeof(u->allowed_commands)) != 0)\n    {\n        serverLog(LL_WARNING,\n            \"CRITICAL ERROR: User ACLs don't match final bitmap: '%s'\",\n            rules);\n        serverPanic(\"No bitmap match in ACLDescribeUserCommandRules()\");\n    }\n    return rules;\n}\n\n/* This is similar to ACLDescribeUserCommandRules(), however instead of\n * describing just the user command rules, everything is described: user\n * flags, keys, passwords and finally the command rules obtained via\n * the ACLDescribeUserCommandRules() function. This is the function we call\n * when we want to rewrite the configuration files describing ACLs and\n * in order to show users with ACL LIST. */\nsds ACLDescribeUser(user *u) {\n    sds res = sdsempty();\n\n    /* Flags. */\n    for (int j = 0; ACLUserFlags[j].flag; j++) {\n        /* Skip the allcommands and allkeys flags because they'll be emitted\n         * later as ~* and +@all. */\n        if (ACLUserFlags[j].flag == USER_FLAG_ALLKEYS ||\n            ACLUserFlags[j].flag == USER_FLAG_ALLCOMMANDS) continue;\n        if (u->flags & ACLUserFlags[j].flag) {\n            res = sdscat(res,ACLUserFlags[j].name);\n            res = sdscatlen(res,\" \",1);\n        }\n    }\n\n    /* Passwords. */\n    listIter li;\n    listNode *ln;\n    listRewind(u->passwords,&li);\n    while((ln = listNext(&li))) {\n        sds thispass = listNodeValue(ln);\n        res = sdscatlen(res,\"#\",1);\n        res = sdscatsds(res,thispass);\n        res = sdscatlen(res,\" \",1);\n    }\n\n    /* Key patterns. */\n    if (u->flags & USER_FLAG_ALLKEYS) {\n        res = sdscatlen(res,\"~* \",3);\n    } else {\n        listRewind(u->patterns,&li);\n        while((ln = listNext(&li))) {\n            sds thispat = listNodeValue(ln);\n            res = sdscatlen(res,\"~\",1);\n            res = sdscatsds(res,thispat);\n            res = sdscatlen(res,\" \",1);\n        }\n    }\n\n    /* Command rules. */\n    sds rules = ACLDescribeUserCommandRules(u);\n    res = sdscatsds(res,rules);\n    sdsfree(rules);\n    return res;\n}\n\n/* Get a command from the original command table, that is not affected\n * by the command renaming operations: we base all the ACL work from that\n * table, so that ACLs are valid regardless of command renaming. */\nstruct redisCommand *ACLLookupCommand(const char *name) {\n    struct redisCommand *cmd;\n    sds sdsname = sdsnew(name);\n    cmd = dictFetchValue(server.orig_commands, sdsname);\n    sdsfree(sdsname);\n    return cmd;\n}\n\n/* Flush the array of allowed subcommands for the specified user\n * and command ID. */\nvoid ACLResetSubcommandsForCommand(user *u, unsigned long id) {\n    if (u->allowed_subcommands && u->allowed_subcommands[id]) {\n        for (int i = 0; u->allowed_subcommands[id][i]; i++)\n            sdsfree(u->allowed_subcommands[id][i]);\n        zfree(u->allowed_subcommands[id]);\n        u->allowed_subcommands[id] = NULL;\n    }\n}\n\n/* Flush the entire table of subcommands. This is useful on +@all, -@all\n * or similar to return back to the minimal memory usage (and checks to do)\n * for the user. */\nvoid ACLResetSubcommands(user *u) {\n    if (u->allowed_subcommands == NULL) return;\n    for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {\n        if (u->allowed_subcommands[j]) {\n            for (int i = 0; u->allowed_subcommands[j][i]; i++)\n                sdsfree(u->allowed_subcommands[j][i]);\n            zfree(u->allowed_subcommands[j]);\n        }\n    }\n    zfree(u->allowed_subcommands);\n    u->allowed_subcommands = NULL;\n}\n\n\n/* Add a subcommand to the list of subcommands for the user 'u' and\n * the command id specified. */\nvoid ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {\n    /* If this is the first subcommand to be configured for\n     * this user, we have to allocate the subcommands array. */\n    if (u->allowed_subcommands == NULL) {\n        u->allowed_subcommands = zcalloc(USER_COMMAND_BITS_COUNT *\n                                 sizeof(sds*));\n    }\n\n    /* We also need to enlarge the allocation pointing to the\n     * null terminated SDS array, to make space for this one.\n     * To start check the current size, and while we are here\n     * make sure the subcommand is not already specified inside. */\n    long items = 0;\n    if (u->allowed_subcommands[id]) {\n        while(u->allowed_subcommands[id][items]) {\n            /* If it's already here do not add it again. */\n            if (!strcasecmp(u->allowed_subcommands[id][items],sub)) return;\n            items++;\n        }\n    }\n\n    /* Now we can make space for the new item (and the null term). */\n    items += 2;\n    u->allowed_subcommands[id] = zrealloc(u->allowed_subcommands[id],\n                                 sizeof(sds)*items);\n    u->allowed_subcommands[id][items-2] = sdsnew(sub);\n    u->allowed_subcommands[id][items-1] = NULL;\n}\n\n/* Set user properties according to the string \"op\". The following\n * is a description of what different strings will do:\n *\n * on           Enable the user: it is possible to authenticate as this user.\n * off          Disable the user: it's no longer possible to authenticate\n *              with this user, however the already authenticated connections\n *              will still work.\n * +<command>   Allow the execution of that command\n * -<command>   Disallow the execution of that command\n * +@<category> Allow the execution of all the commands in such category\n *              with valid categories are like @admin, @set, @sortedset, ...\n *              and so forth, see the full list in the server.c file where\n *              the Redis command table is described and defined.\n *              The special category @all means all the commands, but currently\n *              present in the server, and that will be loaded in the future\n *              via modules.\n * +<command>|subcommand    Allow a specific subcommand of an otherwise\n *                          disabled command. Note that this form is not\n *                          allowed as negative like -DEBUG|SEGFAULT, but\n *                          only additive starting with \"+\".\n * allcommands  Alias for +@all. Note that it implies the ability to execute\n *              all the future commands loaded via the modules system.\n * nocommands   Alias for -@all.\n * ~<pattern>   Add a pattern of keys that can be mentioned as part of\n *              commands. For instance ~* allows all the keys. The pattern\n *              is a glob-style pattern like the one of KEYS.\n *              It is possible to specify multiple patterns.\n * allkeys      Alias for ~*\n * resetkeys    Flush the list of allowed keys patterns.\n * ><password>  Add this password to the list of valid password for the user.\n *              For example >mypass will add \"mypass\" to the list.\n *              This directive clears the \"nopass\" flag (see later).\n * #<hash>      Add this password hash to the list of valid hashes for\n *              the user. This is useful if you have previously computed\n *              the hash, and don't want to store it in plaintext.\n *              This directive clears the \"nopass\" flag (see later).\n * <<password>  Remove this password from the list of valid passwords.\n * !<hash>      Remove this hashed password from the list of valid passwords.\n *              This is useful when you want to remove a password just by\n *              hash without knowing its plaintext version at all.\n * nopass       All the set passwords of the user are removed, and the user\n *              is flagged as requiring no password: it means that every\n *              password will work against this user. If this directive is\n *              used for the default user, every new connection will be\n *              immediately authenticated with the default user without\n *              any explicit AUTH command required. Note that the \"resetpass\"\n *              directive will clear this condition.\n * resetpass    Flush the list of allowed passwords. Moreover removes the\n *              \"nopass\" status. After \"resetpass\" the user has no associated\n *              passwords and there is no way to authenticate without adding\n *              some password (or setting it as \"nopass\" later).\n * reset        Performs the following actions: resetpass, resetkeys, off,\n *              -@all. The user returns to the same state it has immediately\n *              after its creation.\n *\n * The 'op' string must be null terminated. The 'oplen' argument should\n * specify the length of the 'op' string in case the caller requires to pass\n * binary data (for instance the >password form may use a binary password).\n * Otherwise the field can be set to -1 and the function will use strlen()\n * to determine the length.\n *\n * The function returns C_OK if the action to perform was understood because\n * the 'op' string made sense. Otherwise C_ERR is returned if the operation\n * is unknown or has some syntax error.\n *\n * When an error is returned, errno is set to the following values:\n *\n * EINVAL: The specified opcode is not understood or the key pattern is\n *         invalid (contains non allowed characters).\n * ENOENT: The command name or command category provided with + or - is not\n *         known.\n * EBUSY:  The subcommand you want to add is about a command that is currently\n *         fully added.\n * EEXIST: You are adding a key pattern after \"*\" was already added. This is\n *         almost surely an error on the user side.\n * ENODEV: The password you are trying to remove from the user does not exist.\n * EBADMSG: The hash you are trying to add is not a valid hash. \n */\nint ACLSetUser(user *u, const char *op, ssize_t oplen) {\n    if (oplen == -1) oplen = strlen(op);\n    if (!strcasecmp(op,\"on\")) {\n        u->flags |= USER_FLAG_ENABLED;\n        u->flags &= ~USER_FLAG_DISABLED;\n    } else if (!strcasecmp(op,\"off\")) {\n        u->flags |= USER_FLAG_DISABLED;\n        u->flags &= ~USER_FLAG_ENABLED;\n    } else if (!strcasecmp(op,\"allkeys\") ||\n               !strcasecmp(op,\"~*\"))\n    {\n        u->flags |= USER_FLAG_ALLKEYS;\n        listEmpty(u->patterns);\n    } else if (!strcasecmp(op,\"resetkeys\")) {\n        u->flags &= ~USER_FLAG_ALLKEYS;\n        listEmpty(u->patterns);\n    } else if (!strcasecmp(op,\"allcommands\") ||\n               !strcasecmp(op,\"+@all\"))\n    {\n        memset(u->allowed_commands,255,sizeof(u->allowed_commands));\n        u->flags |= USER_FLAG_ALLCOMMANDS;\n        ACLResetSubcommands(u);\n    } else if (!strcasecmp(op,\"nocommands\") ||\n               !strcasecmp(op,\"-@all\"))\n    {\n        memset(u->allowed_commands,0,sizeof(u->allowed_commands));\n        u->flags &= ~USER_FLAG_ALLCOMMANDS;\n        ACLResetSubcommands(u);\n    } else if (!strcasecmp(op,\"nopass\")) {\n        u->flags |= USER_FLAG_NOPASS;\n        listEmpty(u->passwords);\n    } else if (!strcasecmp(op,\"resetpass\")) {\n        u->flags &= ~USER_FLAG_NOPASS;\n        listEmpty(u->passwords);\n    } else if (op[0] == '>' || op[0] == '#') {\n        sds newpass;\n        if (op[0] == '>') {\n            newpass = ACLHashPassword((unsigned char*)op+1,oplen-1);\n        } else {\n            if (oplen != HASH_PASSWORD_LEN + 1) {\n                errno = EBADMSG;\n                return C_ERR;\n            }\n\n            /* Password hashes can only be characters that represent\n             * hexadecimal values, which are numbers and lowercase\n             * characters 'a' through 'f'.\n             */\n            for(int i = 1; i < HASH_PASSWORD_LEN + 1; i++) {\n                char c = op[i];\n                if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {\n                    errno = EBADMSG;\n                    return C_ERR;\n                }\n            }\n            newpass = sdsnewlen(op+1,oplen-1);\n        }\n\n        listNode *ln = listSearchKey(u->passwords,newpass);\n        /* Avoid re-adding the same password multiple times. */\n        if (ln == NULL)\n            listAddNodeTail(u->passwords,newpass);\n        else\n            sdsfree(newpass);\n        u->flags &= ~USER_FLAG_NOPASS;\n    } else if (op[0] == '<' || op[0] == '!') {\n        sds delpass;\n        if (op[0] == '<') {\n            delpass = ACLHashPassword((unsigned char*)op+1,oplen-1);\n        } else {\n            if (oplen != HASH_PASSWORD_LEN + 1) {\n                errno = EBADMSG;\n                return C_ERR;\n            }\n            delpass = sdsnewlen(op+1,oplen-1);\n        }\n        listNode *ln = listSearchKey(u->passwords,delpass);\n        sdsfree(delpass);\n        if (ln) {\n            listDelNode(u->passwords,ln);\n        } else {\n            errno = ENODEV;\n            return C_ERR;\n        }\n    } else if (op[0] == '~') {\n        if (u->flags & USER_FLAG_ALLKEYS) {\n            errno = EEXIST;\n            return C_ERR;\n        }\n        if (ACLStringHasSpaces(op+1,oplen-1)) {\n            errno = EINVAL;\n            return C_ERR;\n        }\n        sds newpat = sdsnewlen(op+1,oplen-1);\n        listNode *ln = listSearchKey(u->patterns,newpat);\n        /* Avoid re-adding the same pattern multiple times. */\n        if (ln == NULL)\n            listAddNodeTail(u->patterns,newpat);\n        else\n            sdsfree(newpat);\n        u->flags &= ~USER_FLAG_ALLKEYS;\n    } else if (op[0] == '+' && op[1] != '@') {\n        if (strchr(op,'|') == NULL) {\n            if (ACLLookupCommand(op+1) == NULL) {\n                errno = ENOENT;\n                return C_ERR;\n            }\n            unsigned long id = ACLGetCommandID(op+1);\n            ACLSetUserCommandBit(u,id,1);\n            ACLResetSubcommandsForCommand(u,id);\n        } else {\n            /* Split the command and subcommand parts. */\n            char *copy = zstrdup(op+1);\n            char *sub = strchr(copy,'|');\n            sub[0] = '\\0';\n            sub++;\n\n            /* Check if the command exists. We can't check the\n             * subcommand to see if it is valid. */\n            if (ACLLookupCommand(copy) == NULL) {\n                zfree(copy);\n                errno = ENOENT;\n                return C_ERR;\n            }\n            unsigned long id = ACLGetCommandID(copy);\n\n            /* The subcommand cannot be empty, so things like DEBUG|\n             * are syntax errors of course. */\n            if (strlen(sub) == 0) {\n                zfree(copy);\n                errno = EINVAL;\n                return C_ERR;\n            }\n\n            /* The command should not be set right now in the command\n             * bitmap, because adding a subcommand of a fully added\n             * command is probably an error on the user side. */\n            if (ACLGetUserCommandBit(u,id) == 1) {\n                zfree(copy);\n                errno = EBUSY;\n                return C_ERR;\n            }\n\n            /* Add the subcommand to the list of valid ones. */\n            ACLAddAllowedSubcommand(u,id,sub);\n\n            /* We have to clear the command bit so that we force the\n             * subcommand check. */\n            ACLSetUserCommandBit(u,id,0);\n            zfree(copy);\n        }\n    } else if (op[0] == '-' && op[1] != '@') {\n        if (ACLLookupCommand(op+1) == NULL) {\n            errno = ENOENT;\n            return C_ERR;\n        }\n        unsigned long id = ACLGetCommandID(op+1);\n        ACLSetUserCommandBit(u,id,0);\n        ACLResetSubcommandsForCommand(u,id);\n    } else if ((op[0] == '+' || op[0] == '-') && op[1] == '@') {\n        int bitval = op[0] == '+' ? 1 : 0;\n        if (ACLSetUserCommandBitsForCategory(u,op+2,bitval) == C_ERR) {\n            errno = ENOENT;\n            return C_ERR;\n        }\n    } else if (!strcasecmp(op,\"reset\")) {\n        serverAssert(ACLSetUser(u,\"resetpass\",-1) == C_OK);\n        serverAssert(ACLSetUser(u,\"resetkeys\",-1) == C_OK);\n        serverAssert(ACLSetUser(u,\"off\",-1) == C_OK);\n        serverAssert(ACLSetUser(u,\"-@all\",-1) == C_OK);\n    } else {\n        errno = EINVAL;\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* Return a description of the error that occurred in ACLSetUser() according to\n * the errno value set by the function on error. */\nchar *ACLSetUserStringError(void) {\n    char *errmsg = \"Wrong format\";\n    if (errno == ENOENT)\n        errmsg = \"Unknown command or category name in ACL\";\n    else if (errno == EINVAL)\n        errmsg = \"Syntax error\";\n    else if (errno == EBUSY)\n        errmsg = \"Adding a subcommand of a command already fully \"\n                 \"added is not allowed. Remove the command to start. \"\n                 \"Example: -DEBUG +DEBUG|DIGEST\";\n    else if (errno == EEXIST)\n        errmsg = \"Adding a pattern after the * pattern (or the \"\n                 \"'allkeys' flag) is not valid and does not have any \"\n                 \"effect. Try 'resetkeys' to start with an empty \"\n                 \"list of patterns\";\n    else if (errno == ENODEV)\n        errmsg = \"The password you are trying to remove from the user does \"\n                 \"not exist\";\n    else if (errno == EBADMSG)\n        errmsg = \"The password hash must be exactly 64 characters and contain \"\n                 \"only lowercase hexadecimal characters\";\n    return errmsg;\n}\n\n/* Initialize the default user, that will always exist for all the process\n * lifetime. */\nvoid ACLInitDefaultUser(void) {\n    DefaultUser = ACLCreateUser(\"default\",7);\n    ACLSetUser(DefaultUser,\"+@all\",-1);\n    ACLSetUser(DefaultUser,\"~*\",-1);\n    ACLSetUser(DefaultUser,\"on\",-1);\n    ACLSetUser(DefaultUser,\"nopass\",-1);\n}\n\n/* Initialization of the ACL subsystem. */\nvoid ACLInit(void) {\n    Users = raxNew();\n    UsersToLoad = listCreate();\n    ACLLog = listCreate();\n    ACLInitDefaultUser();\n    server.requirepass = NULL; /* Only used for backward compatibility. */\n}\n\n/* Check the username and password pair and return C_OK if they are valid,\n * otherwise C_ERR is returned and errno is set to:\n *\n *  EINVAL: if the username-password do not match.\n *  ENONENT: if the specified user does not exist at all.\n */\nint ACLCheckUserCredentials(robj *username, robj *password) {\n    user *u = ACLGetUserByName(username->ptr,sdslen(username->ptr));\n    if (u == NULL) {\n        errno = ENOENT;\n        return C_ERR;\n    }\n\n    /* Disabled users can't login. */\n    if (u->flags & USER_FLAG_DISABLED) {\n        errno = EINVAL;\n        return C_ERR;\n    }\n\n    /* If the user is configured to don't require any password, we\n     * are already fine here. */\n    if (u->flags & USER_FLAG_NOPASS) return C_OK;\n\n    /* Check all the user passwords for at least one to match. */\n    listIter li;\n    listNode *ln;\n    listRewind(u->passwords,&li);\n    sds hashed = ACLHashPassword(password->ptr,sdslen(password->ptr));\n    while((ln = listNext(&li))) {\n        sds thispass = listNodeValue(ln);\n        if (!time_independent_strcmp(hashed, thispass)) {\n            sdsfree(hashed);\n            return C_OK;\n        }\n    }\n    sdsfree(hashed);\n\n    /* If we reached this point, no password matched. */\n    errno = EINVAL;\n    return C_ERR;\n}\n\n/* This is like ACLCheckUserCredentials(), however if the user/pass\n * are correct, the connection is put in authenticated state and the\n * connection user reference is populated.\n *\n * The return value is C_OK or C_ERR with the same meaning as\n * ACLCheckUserCredentials(). */\nint ACLAuthenticateUser(client *c, robj *username, robj *password) {\n    if (ACLCheckUserCredentials(username,password) == C_OK) {\n        c->authenticated = 1;\n        c->user = ACLGetUserByName(username->ptr,sdslen(username->ptr));\n        moduleNotifyUserChanged(c);\n        return C_OK;\n    } else {\n        addACLLogEntry(c,ACL_DENIED_AUTH,0,username->ptr);\n        return C_ERR;\n    }\n}\n\n/* For ACL purposes, every user has a bitmap with the commands that such\n * user is allowed to execute. In order to populate the bitmap, every command\n * should have an assigned ID (that is used to index the bitmap). This function\n * creates such an ID: it uses sequential IDs, reusing the same ID for the same\n * command name, so that a command retains the same ID in case of modules that\n * are unloaded and later reloaded. */\nunsigned long ACLGetCommandID(const char *cmdname) {\n    static rax *map = NULL;\n    static unsigned long nextid = 0;\n\n    sds lowername = sdsnew(cmdname);\n    sdstolower(lowername);\n    if (map == NULL) map = raxNew();\n    void *id = raxFind(map,(unsigned char*)lowername,sdslen(lowername));\n    if (id != raxNotFound) {\n        sdsfree(lowername);\n        return (unsigned long)id;\n    }\n    raxInsert(map,(unsigned char*)lowername,strlen(lowername),\n              (void*)nextid,NULL);\n    sdsfree(lowername);\n    unsigned long thisid = nextid;\n    nextid++;\n\n    /* We never assign the last bit in the user commands bitmap structure,\n     * this way we can later check if this bit is set, understanding if the\n     * current ACL for the user was created starting with a +@all to add all\n     * the possible commands and just subtracting other single commands or\n     * categories, or if, instead, the ACL was created just adding commands\n     * and command categories from scratch, not allowing future commands by\n     * default (loaded via modules). This is useful when rewriting the ACLs\n     * with ACL SAVE. */\n    if (nextid == USER_COMMAND_BITS_COUNT-1) nextid++;\n    return thisid;\n}\n\n/* Return an username by its name, or NULL if the user does not exist. */\nuser *ACLGetUserByName(const char *name, size_t namelen) {\n    void *myuser = raxFind(Users,(unsigned char*)name,namelen);\n    if (myuser == raxNotFound) return NULL;\n    return myuser;\n}\n\n/* Check if the command is ready to be executed in the client 'c', already\n * referenced by c->cmd, and can be executed by this client according to the\n * ACLs associated to the client user c->user.\n *\n * If the user can execute the command ACL_OK is returned, otherwise\n * ACL_DENIED_CMD or ACL_DENIED_KEY is returned: the first in case the\n * command cannot be executed because the user is not allowed to run such\n * command, the second if the command is denied because the user is trying\n * to access keys that are not among the specified patterns. */\nint ACLCheckCommandPerm(client *c, int *keyidxptr) {\n    user *u = c->user;\n    uint64_t id = c->cmd->id;\n\n    /* If there is no associated user, the connection can run anything. */\n    if (u == NULL) return ACL_OK;\n\n    /* Check if the user can execute this command. */\n    if (!(u->flags & USER_FLAG_ALLCOMMANDS) &&\n        c->cmd->proc != authCommand)\n    {\n        /* If the bit is not set we have to check further, in case the\n         * command is allowed just with that specific subcommand. */\n        if (ACLGetUserCommandBit(u,id) == 0) {\n            /* Check if the subcommand matches. */\n            if (c->argc < 2 ||\n                u->allowed_subcommands == NULL ||\n                u->allowed_subcommands[id] == NULL)\n            {\n                return ACL_DENIED_CMD;\n            }\n\n            long subid = 0;\n            while (1) {\n                if (u->allowed_subcommands[id][subid] == NULL)\n                    return ACL_DENIED_CMD;\n                if (!strcasecmp(c->argv[1]->ptr,\n                                u->allowed_subcommands[id][subid]))\n                    break; /* Subcommand match found. Stop here. */\n                subid++;\n            }\n        }\n    }\n\n    /* Check if the user can execute commands explicitly touching the keys\n     * mentioned in the command arguments. */\n    if (!(c->user->flags & USER_FLAG_ALLKEYS) &&\n        (c->cmd->getkeys_proc || c->cmd->firstkey))\n    {\n        int numkeys;\n        int *keyidx = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);\n        for (int j = 0; j < numkeys; j++) {\n            listIter li;\n            listNode *ln;\n            listRewind(u->patterns,&li);\n\n            /* Test this key against every pattern. */\n            int match = 0;\n            while((ln = listNext(&li))) {\n                sds pattern = listNodeValue(ln);\n                size_t plen = sdslen(pattern);\n                int idx = keyidx[j];\n                if (stringmatchlen(pattern,plen,c->argv[idx]->ptr,\n                                   sdslen(c->argv[idx]->ptr),0))\n                {\n                    match = 1;\n                    break;\n                }\n            }\n            if (!match) {\n                if (keyidxptr) *keyidxptr = keyidx[j];\n                getKeysFreeResult(keyidx);\n                return ACL_DENIED_KEY;\n            }\n        }\n        getKeysFreeResult(keyidx);\n    }\n\n    /* If we survived all the above checks, the user can execute the\n     * command. */\n    return ACL_OK;\n}\n\n/* =============================================================================\n * ACL loading / saving functions\n * ==========================================================================*/\n\n/* Given an argument vector describing a user in the form:\n *\n *      user <username> ... ACL rules and flags ...\n *\n * this function validates, and if the syntax is valid, appends\n * the user definition to a list for later loading.\n *\n * The rules are tested for validity and if there obvious syntax errors\n * the function returns C_ERR and does nothing, otherwise C_OK is returned\n * and the user is appended to the list.\n *\n * Note that this function cannot stop in case of commands that are not found\n * and, in that case, the error will be emitted later, because certain\n * commands may be defined later once modules are loaded.\n *\n * When an error is detected and C_ERR is returned, the function populates\n * by reference (if not set to NULL) the argc_err argument with the index\n * of the argv vector that caused the error. */\nint ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {\n    if (argc < 2 || strcasecmp(argv[0],\"user\")) {\n        if (argc_err) *argc_err = 0;\n        return C_ERR;\n    }\n\n    /* Try to apply the user rules in a fake user to see if they\n     * are actually valid. */\n    user *fakeuser = ACLCreateUnlinkedUser();\n\n    for (int j = 2; j < argc; j++) {\n        if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) == C_ERR) {\n            if (errno != ENOENT) {\n                ACLFreeUser(fakeuser);\n                if (argc_err) *argc_err = j;\n                return C_ERR;\n            }\n        }\n    }\n\n    /* Rules look valid, let's append the user to the list. */\n    sds *copy = zmalloc(sizeof(sds)*argc);\n    for (int j = 1; j < argc; j++) copy[j-1] = sdsdup(argv[j]);\n    copy[argc-1] = NULL;\n    listAddNodeTail(UsersToLoad,copy);\n    ACLFreeUser(fakeuser);\n    return C_OK;\n}\n\n/* This function will load the configured users appended to the server\n * configuration via ACLAppendUserForLoading(). On loading errors it will\n * log an error and return C_ERR, otherwise C_OK will be returned. */\nint ACLLoadConfiguredUsers(void) {\n    listIter li;\n    listNode *ln;\n    listRewind(UsersToLoad,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        sds *aclrules = listNodeValue(ln);\n        sds username = aclrules[0];\n\n        if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) {\n            serverLog(LL_WARNING,\"Spaces not allowed in ACL usernames\");\n            return C_ERR;\n        }\n\n        user *u = ACLCreateUser(username,sdslen(username));\n        if (!u) {\n            u = ACLGetUserByName(username,sdslen(username));\n            serverAssert(u != NULL);\n            ACLSetUser(u,\"reset\",-1);\n        }\n\n        /* Load every rule defined for this user. */\n        for (int j = 1; aclrules[j]; j++) {\n            if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {\n                char *errmsg = ACLSetUserStringError();\n                serverLog(LL_WARNING,\"Error loading ACL rule '%s' for \"\n                                     \"the user named '%s': %s\",\n                          aclrules[j],aclrules[0],errmsg);\n                return C_ERR;\n            }\n        }\n\n        /* Having a disabled user in the configuration may be an error,\n         * warn about it without returning any error to the caller. */\n        if (u->flags & USER_FLAG_DISABLED) {\n            serverLog(LL_NOTICE, \"The user '%s' is disabled (there is no \"\n                                 \"'on' modifier in the user description). Make \"\n                                 \"sure this is not a configuration error.\",\n                      aclrules[0]);\n        }\n    }\n    return C_OK;\n}\n\n/* This function loads the ACL from the specified filename: every line\n * is validated and should be either empty or in the format used to specify\n * users in the redis.conf configuration or in the ACL file, that is:\n *\n *  user <username> ... rules ...\n *\n * Note that this function considers comments starting with '#' as errors\n * because the ACL file is meant to be rewritten, and comments would be\n * lost after the rewrite. Yet empty lines are allowed to avoid being too\n * strict.\n *\n * One important part of implementing ACL LOAD, that uses this function, is\n * to avoid ending with broken rules if the ACL file is invalid for some\n * reason, so the function will attempt to validate the rules before loading\n * each user. For every line that will be found broken the function will\n * collect an error message.\n *\n * IMPORTANT: If there is at least a single error, nothing will be loaded\n * and the rules will remain exactly as they were.\n *\n * At the end of the process, if no errors were found in the whole file then\n * NULL is returned. Otherwise an SDS string describing in a single line\n * a description of all the issues found is returned. */\nsds ACLLoadFromFile(const char *filename) {\n    FILE *fp;\n    char buf[1024];\n\n    /* Open the ACL file. */\n    if ((fp = fopen(filename,\"r\")) == NULL) {\n        sds errors = sdscatprintf(sdsempty(),\n            \"Error loading ACLs, opening file '%s': %s\",\n            filename, strerror(errno));\n        return errors;\n    }\n\n    /* Load the whole file as a single string in memory. */\n    sds acls = sdsempty();\n    while(fgets(buf,sizeof(buf),fp) != NULL)\n        acls = sdscat(acls,buf);\n    fclose(fp);\n\n    /* Split the file into lines and attempt to load each line. */\n    int totlines;\n    sds *lines, errors = sdsempty();\n    lines = sdssplitlen(acls,strlen(acls),\"\\n\",1,&totlines);\n    sdsfree(acls);\n\n    /* We need a fake user to validate the rules before making changes\n     * to the real user mentioned in the ACL line. */\n    user *fakeuser = ACLCreateUnlinkedUser();\n\n    /* We do all the loading in a fresh instance of the Users radix tree,\n     * so if there are errors loading the ACL file we can rollback to the\n     * old version. */\n    rax *old_users = Users;\n    user *old_default_user = DefaultUser;\n    Users = raxNew();\n    ACLInitDefaultUser();\n\n    /* Load each line of the file. */\n    for (int i = 0; i < totlines; i++) {\n        sds *argv;\n        int argc;\n        int linenum = i+1;\n\n        lines[i] = sdstrim(lines[i],\" \\t\\r\\n\");\n\n        /* Skip blank lines */\n        if (lines[i][0] == '\\0') continue;\n\n        /* Split into arguments */\n        argv = sdssplitargs(lines[i],&argc);\n        if (argv == NULL) {\n            errors = sdscatprintf(errors,\n                     \"%s:%d: unbalanced quotes in acl line. \",\n                     server.acl_filename, linenum);\n            continue;\n        }\n\n        /* Skip this line if the resulting command vector is empty. */\n        if (argc == 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* The line should start with the \"user\" keyword. */\n        if (strcmp(argv[0],\"user\") || argc < 2) {\n            errors = sdscatprintf(errors,\n                     \"%s:%d should start with user keyword followed \"\n                     \"by the username. \", server.acl_filename,\n                     linenum);\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* Spaces are not allowed in usernames. */\n        if (ACLStringHasSpaces(argv[1],sdslen(argv[1]))) {\n            errors = sdscatprintf(errors,\n                     \"'%s:%d: username '%s' contains invalid characters. \",\n                     server.acl_filename, linenum, argv[1]);\n            continue;\n        }\n\n        /* Try to process the line using the fake user to validate iif\n         * the rules are able to apply cleanly. */\n        ACLSetUser(fakeuser,\"reset\",-1);\n        int j;\n        for (j = 2; j < argc; j++) {\n            if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) != C_OK) {\n                char *errmsg = ACLSetUserStringError();\n                errors = sdscatprintf(errors,\n                         \"%s:%d: %s. \",\n                         server.acl_filename, linenum, errmsg);\n                continue;\n            }\n        }\n\n        /* Apply the rule to the new users set only if so far there\n         * are no errors, otherwise it's useless since we are going\n         * to discard the new users set anyway. */\n        if (sdslen(errors) != 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* We can finally lookup the user and apply the rule. If the\n         * user already exists we always reset it to start. */\n        user *u = ACLCreateUser(argv[1],sdslen(argv[1]));\n        if (!u) {\n            u = ACLGetUserByName(argv[1],sdslen(argv[1]));\n            serverAssert(u != NULL);\n            ACLSetUser(u,\"reset\",-1);\n        }\n\n        /* Note that the same rules already applied to the fake user, so\n         * we just assert that everything goes well: it should. */\n        for (j = 2; j < argc; j++)\n            serverAssert(ACLSetUser(u,argv[j],sdslen(argv[j])) == C_OK);\n\n        sdsfreesplitres(argv,argc);\n    }\n\n    ACLFreeUser(fakeuser);\n    sdsfreesplitres(lines,totlines);\n    DefaultUser = old_default_user; /* This pointer must never change. */\n\n    /* Check if we found errors and react accordingly. */\n    if (sdslen(errors) == 0) {\n        /* The default user pointer is referenced in different places: instead\n         * of replacing such occurrences it is much simpler to copy the new\n         * default user configuration in the old one. */\n        user *new = ACLGetUserByName(\"default\",7);\n        serverAssert(new != NULL);\n        ACLCopyUser(DefaultUser,new);\n        ACLFreeUser(new);\n        raxInsert(Users,(unsigned char*)\"default\",7,DefaultUser,NULL);\n        raxRemove(old_users,(unsigned char*)\"default\",7,NULL);\n        ACLFreeUsersSet(old_users);\n        sdsfree(errors);\n        return NULL;\n    } else {\n        ACLFreeUsersSet(Users);\n        Users = old_users;\n        errors = sdscat(errors,\"WARNING: ACL errors detected, no change to the previously active ACL rules was performed\");\n        return errors;\n    }\n}\n\n/* Generate a copy of the ACLs currently in memory in the specified filename.\n * Returns C_OK on success or C_ERR if there was an error during the I/O.\n * When C_ERR is returned a log is produced with hints about the issue. */\nint ACLSaveToFile(const char *filename) {\n    sds acl = sdsempty();\n    int fd = -1;\n    sds tmpfilename = NULL;\n    int retval = C_ERR;\n\n    /* Let's generate an SDS string containing the new version of the\n     * ACL file. */\n    raxIterator ri;\n    raxStart(&ri,Users);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        user *u = ri.data;\n        /* Return information in the configuration file format. */\n        sds user = sdsnew(\"user \");\n        user = sdscatsds(user,u->name);\n        user = sdscatlen(user,\" \",1);\n        sds descr = ACLDescribeUser(u);\n        user = sdscatsds(user,descr);\n        sdsfree(descr);\n        acl = sdscatsds(acl,user);\n        acl = sdscatlen(acl,\"\\n\",1);\n        sdsfree(user);\n    }\n    raxStop(&ri);\n\n    /* Create a temp file with the new content. */\n    tmpfilename = sdsnew(filename);\n    tmpfilename = sdscatfmt(tmpfilename,\".tmp-%i-%I\",\n        (int)getpid(),(int)mstime());\n    if ((fd = open(tmpfilename,O_WRONLY|O_CREAT,0644)) == -1) {\n        serverLog(LL_WARNING,\"Opening temp ACL file for ACL SAVE: %s\",\n            strerror(errno));\n        goto cleanup;\n    }\n\n    /* Write it. */\n    if (write(fd,acl,sdslen(acl)) != (ssize_t)sdslen(acl)) {\n        serverLog(LL_WARNING,\"Writing ACL file for ACL SAVE: %s\",\n            strerror(errno));\n        goto cleanup;\n    }\n    close(fd); fd = -1;\n\n    /* Let's replace the new file with the old one. */\n    if (rename(tmpfilename,filename) == -1) {\n        serverLog(LL_WARNING,\"Renaming ACL file for ACL SAVE: %s\",\n            strerror(errno));\n        goto cleanup;\n    }\n    sdsfree(tmpfilename); tmpfilename = NULL;\n    retval = C_OK; /* If we reached this point, everything is fine. */\n\ncleanup:\n    if (fd != -1) close(fd);\n    if (tmpfilename) unlink(tmpfilename);\n    sdsfree(tmpfilename);\n    sdsfree(acl);\n    return retval;\n}\n\n/* This function is called once the server is already running, modules are\n * loaded, and we are ready to start, in order to load the ACLs either from\n * the pending list of users defined in redis.conf, or from the ACL file.\n * The function will just exit with an error if the user is trying to mix\n * both the loading methods. */\nvoid ACLLoadUsersAtStartup(void) {\n    if (server.acl_filename[0] != '\\0' && listLength(UsersToLoad) != 0) {\n        serverLog(LL_WARNING,\n            \"Configuring Redis with users defined in redis.conf and at \"\n            \"the same setting an ACL file path is invalid. This setup \"\n            \"is very likely to lead to configuration errors and security \"\n            \"holes, please define either an ACL file or declare users \"\n            \"directly in your redis.conf, but not both.\");\n        exit(1);\n    }\n\n    if (ACLLoadConfiguredUsers() == C_ERR) {\n        serverLog(LL_WARNING,\n            \"Critical error while loading ACLs. Exiting.\");\n        exit(1);\n    }\n\n    if (server.acl_filename[0] != '\\0') {\n        sds errors = ACLLoadFromFile(server.acl_filename);\n        if (errors) {\n            serverLog(LL_WARNING,\n                \"Aborting Redis startup because of ACL errors: %s\", errors);\n            sdsfree(errors);\n            exit(1);\n        }\n    }\n}\n\n/* =============================================================================\n * ACL log\n * ==========================================================================*/\n\n#define ACL_LOG_CTX_TOPLEVEL 0\n#define ACL_LOG_CTX_LUA 1\n#define ACL_LOG_CTX_MULTI 2\n#define ACL_LOG_GROUPING_MAX_TIME_DELTA 60000\n\n/* This structure defines an entry inside the ACL log. */\ntypedef struct ACLLogEntry {\n    uint64_t count;     /* Number of times this happened recently. */\n    int reason;         /* Reason for denying the command. ACL_DENIED_*. */\n    int context;        /* Toplevel, Lua or MULTI/EXEC? ACL_LOG_CTX_*. */\n    sds object;         /* The key name or command name. */\n    sds username;       /* User the client is authenticated with. */\n    mstime_t ctime;     /* Milliseconds time of last update to this entry. */\n    sds cinfo;          /* Client info (last client if updated). */\n} ACLLogEntry;\n\n/* This function will check if ACL entries 'a' and 'b' are similar enough\n * that we should actually update the existing entry in our ACL log instead\n * of creating a new one. */\nint ACLLogMatchEntry(ACLLogEntry *a, ACLLogEntry *b) {\n    if (a->reason != b->reason) return 0;\n    if (a->context != b->context) return 0;\n    mstime_t delta = a->ctime - b->ctime;\n    if (delta < 0) delta = -delta;\n    if (delta > ACL_LOG_GROUPING_MAX_TIME_DELTA) return 0;\n    if (sdscmp(a->object,b->object) != 0) return 0;\n    if (sdscmp(a->username,b->username) != 0) return 0;\n    return 1;\n}\n\n/* Release an ACL log entry. */\nvoid ACLFreeLogEntry(void *leptr) {\n    ACLLogEntry *le = leptr;\n    sdsfree(le->object);\n    sdsfree(le->username);\n    sdsfree(le->cinfo);\n    zfree(le);\n}\n\n/* Adds a new entry in the ACL log, making sure to delete the old entry\n * if we reach the maximum length allowed for the log. This function attempts\n * to find similar entries in the current log in order to bump the counter of\n * the log entry instead of creating many entries for very similar ACL\n * rules issues.\n *\n * The keypos argument is only used when the reason is ACL_DENIED_KEY, since\n * it allows the function to log the key name that caused the problem.\n * Similarly the username is only passed when we failed to authenticate the\n * user with AUTH or HELLO, for the ACL_DENIED_AUTH reason. Otherwise\n * it will just be NULL.\n */\nvoid addACLLogEntry(client *c, int reason, int keypos, sds username) {\n    /* Create a new entry. */\n    struct ACLLogEntry *le = zmalloc(sizeof(*le));\n    le->count = 1;\n    le->reason = reason;\n    le->username = sdsdup(reason == ACL_DENIED_AUTH ? username : c->user->name);\n    le->ctime = mstime();\n\n    switch(reason) {\n    case ACL_DENIED_CMD: le->object = sdsnew(c->cmd->name); break;\n    case ACL_DENIED_KEY: le->object = sdsnew(c->argv[keypos]->ptr); break;\n    case ACL_DENIED_AUTH: le->object = sdsnew(c->argv[0]->ptr); break;\n    default: le->object = sdsempty();\n    }\n\n    client *realclient = c;\n    if (realclient->flags & CLIENT_LUA) realclient = server.lua_caller;\n\n    le->cinfo = catClientInfoString(sdsempty(),realclient);\n    if (c->flags & CLIENT_MULTI) {\n        le->context = ACL_LOG_CTX_MULTI;\n    } else if (c->flags & CLIENT_LUA) {\n        le->context = ACL_LOG_CTX_LUA;\n    } else {\n        le->context = ACL_LOG_CTX_TOPLEVEL;\n    }\n\n    /* Try to match this entry with past ones, to see if we can just\n     * update an existing entry instead of creating a new one. */\n    long toscan = 10; /* Do a limited work trying to find duplicated. */\n    listIter li;\n    listNode *ln;\n    listRewind(ACLLog,&li);\n    ACLLogEntry *match = NULL;\n    while (toscan-- && (ln = listNext(&li)) != NULL) {\n        ACLLogEntry *current = listNodeValue(ln);\n        if (ACLLogMatchEntry(current,le)) {\n            match = current;\n            listDelNode(ACLLog,ln);\n            listAddNodeHead(ACLLog,current);\n            break;\n        }\n    }\n\n    /* If there is a match update the entry, otherwise add it as a\n     * new one. */\n    if (match) {\n        /* We update a few fields of the existing entry and bump the\n         * counter of events for this entry. */\n        sdsfree(match->cinfo);\n        match->cinfo = le->cinfo;\n        match->ctime = le->ctime;\n        match->count++;\n\n        /* Release the old entry. */\n        le->cinfo = NULL;\n        ACLFreeLogEntry(le);\n    } else {\n        /* Add it to our list of entires. We'll have to trim the list\n         * to its maximum size. */\n        listAddNodeHead(ACLLog, le);\n        while(listLength(ACLLog) > server.acllog_max_len) {\n            listNode *ln = listLast(ACLLog);\n            ACLLogEntry *le = listNodeValue(ln);\n            ACLFreeLogEntry(le);\n            listDelNode(ACLLog,ln);\n        }\n    }\n}\n\n/* =============================================================================\n * ACL related commands\n * ==========================================================================*/\n\n/* ACL -- show and modify the configuration of ACL users.\n * ACL HELP\n * ACL LOAD\n * ACL SAVE\n * ACL LIST\n * ACL USERS\n * ACL CAT [<category>]\n * ACL SETUSER <username> ... acl rules ...\n * ACL DELUSER <username> [...]\n * ACL GETUSER <username>\n * ACL GENPASS [<bits>]\n * ACL WHOAMI\n * ACL LOG [<count> | RESET]\n */\nvoid aclCommand(client *c) {\n    char *sub = c->argv[1]->ptr;\n    if (!strcasecmp(sub,\"setuser\") && c->argc >= 3) {\n        sds username = c->argv[2]->ptr;\n        /* Check username validity. */\n        if (ACLStringHasSpaces(username,sdslen(username))) {\n            addReplyErrorFormat(c,\n                \"Usernames can't contain spaces or null characters\");\n            return;\n        }\n\n        /* Create a temporary user to validate and stage all changes against\n         * before applying to an existing user or creating a new user. If all\n         * arguments are valid the user parameters will all be applied together.\n         * If there are any errors then none of the changes will be applied. */\n        user *tempu = ACLCreateUnlinkedUser();\n        user *u = ACLGetUserByName(username,sdslen(username));\n        if (u) ACLCopyUser(tempu, u);\n\n        for (int j = 3; j < c->argc; j++) {\n            if (ACLSetUser(tempu,c->argv[j]->ptr,sdslen(c->argv[j]->ptr)) != C_OK) {\n                char *errmsg = ACLSetUserStringError();\n                addReplyErrorFormat(c,\n                    \"Error in ACL SETUSER modifier '%s': %s\",\n                    (char*)c->argv[j]->ptr, errmsg);\n\n                ACLFreeUser(tempu);\n                return;\n            }\n        }\n\n        /* Overwrite the user with the temporary user we modified above. */\n        if (!u) u = ACLCreateUser(username,sdslen(username));\n        serverAssert(u != NULL);\n        ACLCopyUser(u, tempu);\n        ACLFreeUser(tempu);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(sub,\"deluser\") && c->argc >= 3) {\n        int deleted = 0;\n        for (int j = 2; j < c->argc; j++) {\n            sds username = c->argv[j]->ptr;\n            if (!strcmp(username,\"default\")) {\n                addReplyError(c,\"The 'default' user cannot be removed\");\n                return;\n            }\n        }\n\n        for (int j = 2; j < c->argc; j++) {\n            sds username = c->argv[j]->ptr;\n            user *u;\n            if (raxRemove(Users,(unsigned char*)username,\n                          sdslen(username),\n                          (void**)&u))\n            {\n                ACLFreeUserAndKillClients(u);\n                deleted++;\n            }\n        }\n        addReplyLongLong(c,deleted);\n    } else if (!strcasecmp(sub,\"getuser\") && c->argc == 3) {\n        user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));\n        if (u == NULL) {\n            addReplyNull(c);\n            return;\n        }\n\n        addReplyMapLen(c,4);\n\n        /* Flags */\n        addReplyBulkCString(c,\"flags\");\n        void *deflen = addReplyDeferredLen(c);\n        int numflags = 0;\n        for (int j = 0; ACLUserFlags[j].flag; j++) {\n            if (u->flags & ACLUserFlags[j].flag) {\n                addReplyBulkCString(c,ACLUserFlags[j].name);\n                numflags++;\n            }\n        }\n        setDeferredSetLen(c,deflen,numflags);\n\n        /* Passwords */\n        addReplyBulkCString(c,\"passwords\");\n        addReplyArrayLen(c,listLength(u->passwords));\n        listIter li;\n        listNode *ln;\n        listRewind(u->passwords,&li);\n        while((ln = listNext(&li))) {\n            sds thispass = listNodeValue(ln);\n            addReplyBulkCBuffer(c,thispass,sdslen(thispass));\n        }\n\n        /* Commands */\n        addReplyBulkCString(c,\"commands\");\n        sds cmddescr = ACLDescribeUserCommandRules(u);\n        addReplyBulkSds(c,cmddescr);\n\n        /* Key patterns */\n        addReplyBulkCString(c,\"keys\");\n        if (u->flags & USER_FLAG_ALLKEYS) {\n            addReplyArrayLen(c,1);\n            addReplyBulkCBuffer(c,\"*\",1);\n        } else {\n            addReplyArrayLen(c,listLength(u->patterns));\n            listIter li;\n            listNode *ln;\n            listRewind(u->patterns,&li);\n            while((ln = listNext(&li))) {\n                sds thispat = listNodeValue(ln);\n                addReplyBulkCBuffer(c,thispat,sdslen(thispat));\n            }\n        }\n    } else if ((!strcasecmp(sub,\"list\") || !strcasecmp(sub,\"users\")) &&\n               c->argc == 2)\n    {\n        int justnames = !strcasecmp(sub,\"users\");\n        addReplyArrayLen(c,raxSize(Users));\n        raxIterator ri;\n        raxStart(&ri,Users);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            user *u = ri.data;\n            if (justnames) {\n                addReplyBulkCBuffer(c,u->name,sdslen(u->name));\n            } else {\n                /* Return information in the configuration file format. */\n                sds config = sdsnew(\"user \");\n                config = sdscatsds(config,u->name);\n                config = sdscatlen(config,\" \",1);\n                sds descr = ACLDescribeUser(u);\n                config = sdscatsds(config,descr);\n                sdsfree(descr);\n                addReplyBulkSds(c,config);\n            }\n        }\n        raxStop(&ri);\n    } else if (!strcasecmp(sub,\"whoami\") && c->argc == 2) {\n        if (c->user != NULL) {\n            addReplyBulkCBuffer(c,c->user->name,sdslen(c->user->name));\n        } else {\n            addReplyNull(c);\n        }\n    } else if (server.acl_filename[0] == '\\0' &&\n               (!strcasecmp(sub,\"load\") || !strcasecmp(sub,\"save\")))\n    {\n        addReplyError(c,\"This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.\");\n        return;\n    } else if (!strcasecmp(sub,\"load\") && c->argc == 2) {\n        sds errors = ACLLoadFromFile(server.acl_filename);\n        if (errors == NULL) {\n            addReply(c,shared.ok);\n        } else {\n            addReplyError(c,errors);\n            sdsfree(errors);\n        }\n    } else if (!strcasecmp(sub,\"save\") && c->argc == 2) {\n        if (ACLSaveToFile(server.acl_filename) == C_OK) {\n            addReply(c,shared.ok);\n        } else {\n            addReplyError(c,\"There was an error trying to save the ACLs. \"\n                            \"Please check the server logs for more \"\n                            \"information\");\n        }\n    } else if (!strcasecmp(sub,\"cat\") && c->argc == 2) {\n        void *dl = addReplyDeferredLen(c);\n        int j;\n        for (j = 0; ACLCommandCategories[j].flag != 0; j++)\n            addReplyBulkCString(c,ACLCommandCategories[j].name);\n        setDeferredArrayLen(c,dl,j);\n    } else if (!strcasecmp(sub,\"cat\") && c->argc == 3) {\n        uint64_t cflag = ACLGetCommandCategoryFlagByName(c->argv[2]->ptr);\n        if (cflag == 0) {\n            addReplyErrorFormat(c, \"Unknown category '%s'\", (char*)c->argv[2]->ptr);\n            return;\n        }\n        int arraylen = 0;\n        void *dl = addReplyDeferredLen(c);\n        dictIterator *di = dictGetIterator(server.orig_commands);\n        dictEntry *de;\n        while ((de = dictNext(di)) != NULL) {\n            struct redisCommand *cmd = dictGetVal(de);\n            if (cmd->flags & CMD_MODULE) continue;\n            if (cmd->flags & cflag) {\n                addReplyBulkCString(c,cmd->name);\n                arraylen++;\n            }\n        }\n        dictReleaseIterator(di);\n        setDeferredArrayLen(c,dl,arraylen);\n    } else if (!strcasecmp(sub,\"genpass\") && (c->argc == 2 || c->argc == 3)) {\n        #define GENPASS_MAX_BITS 4096\n        char pass[GENPASS_MAX_BITS/8*2]; /* Hex representation. */\n        long bits = 256; /* By default generate 256 bits passwords. */\n\n        if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&bits,NULL)\n            != C_OK) return;\n\n        if (bits <= 0 || bits > GENPASS_MAX_BITS) {\n            addReplyErrorFormat(c,\n                \"ACL GENPASS argument must be the number of \"\n                \"bits for the output password, a positive number \"\n                \"up to %d\",GENPASS_MAX_BITS);\n            return;\n        }\n\n        long chars = (bits+3)/4; /* Round to number of characters to emit. */\n        getRandomHexChars(pass,chars);\n        addReplyBulkCBuffer(c,pass,chars);\n    } else if (!strcasecmp(sub,\"log\") && (c->argc == 2 || c->argc ==3)) {\n        long count = 10; /* Number of entries to emit by default. */\n\n        /* Parse the only argument that LOG may have: it could be either\n         * the number of entries the user wants to display, or alternatively\n         * the \"RESET\" command in order to flush the old entries. */\n        if (c->argc == 3) {\n            if (!strcasecmp(c->argv[2]->ptr,\"reset\")) {\n                listSetFreeMethod(ACLLog,ACLFreeLogEntry);\n                listEmpty(ACLLog);\n                listSetFreeMethod(ACLLog,NULL);\n                addReply(c,shared.ok);\n                return;\n            } else if (getLongFromObjectOrReply(c,c->argv[2],&count,NULL)\n                       != C_OK)\n            {\n                return;\n            }\n            if (count < 0) count = 0;\n        }\n\n        /* Fix the count according to the number of entries we got. */\n        if ((size_t)count > listLength(ACLLog))\n            count = listLength(ACLLog);\n\n        addReplyArrayLen(c,count);\n        listIter li;\n        listNode *ln;\n        listRewind(ACLLog,&li);\n        mstime_t now = mstime();\n        while (count-- && (ln = listNext(&li)) != NULL) {\n            ACLLogEntry *le = listNodeValue(ln);\n            addReplyMapLen(c,7);\n            addReplyBulkCString(c,\"count\");\n            addReplyLongLong(c,le->count);\n\n            addReplyBulkCString(c,\"reason\");\n            char *reasonstr;\n            switch(le->reason) {\n            case ACL_DENIED_CMD: reasonstr=\"command\"; break;\n            case ACL_DENIED_KEY: reasonstr=\"key\"; break;\n            case ACL_DENIED_AUTH: reasonstr=\"auth\"; break;\n            default: reasonstr=\"unknown\";\n            }\n            addReplyBulkCString(c,reasonstr);\n\n            addReplyBulkCString(c,\"context\");\n            char *ctxstr;\n            switch(le->context) {\n            case ACL_LOG_CTX_TOPLEVEL: ctxstr=\"toplevel\"; break;\n            case ACL_LOG_CTX_MULTI: ctxstr=\"multi\"; break;\n            case ACL_LOG_CTX_LUA: ctxstr=\"lua\"; break;\n            default: ctxstr=\"unknown\";\n            }\n            addReplyBulkCString(c,ctxstr);\n\n            addReplyBulkCString(c,\"object\");\n            addReplyBulkCBuffer(c,le->object,sdslen(le->object));\n            addReplyBulkCString(c,\"username\");\n            addReplyBulkCBuffer(c,le->username,sdslen(le->username));\n            addReplyBulkCString(c,\"age-seconds\");\n            double age = (double)(now - le->ctime)/1000;\n            addReplyDouble(c,age);\n            addReplyBulkCString(c,\"client-info\");\n            addReplyBulkCBuffer(c,le->cinfo,sdslen(le->cinfo));\n        }\n    } else if (!strcasecmp(sub,\"help\")) {\n        const char *help[] = {\n\"LOAD                             -- Reload users from the ACL file.\",\n\"SAVE                             -- Save the current config to the ACL file.\",\n\"LIST                             -- Show user details in config file format.\",\n\"USERS                            -- List all the registered usernames.\",\n\"SETUSER <username> [attribs ...] -- Create or modify a user.\",\n\"GETUSER <username>               -- Get the user details.\",\n\"DELUSER <username> [...]         -- Delete a list of users.\",\n\"CAT                              -- List available categories.\",\n\"CAT <category>                   -- List commands inside category.\",\n\"GENPASS [<bits>]                 -- Generate a secure user password.\",\n\"WHOAMI                           -- Return the current connection username.\",\n\"LOG [<count> | RESET]            -- Show the ACL log entries.\",\nNULL\n        };\n        addReplyHelp(c,help);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\nvoid addReplyCommandCategories(client *c, struct redisCommand *cmd) {\n    int flagcount = 0;\n    void *flaglen = addReplyDeferredLen(c);\n    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {\n        if (cmd->flags & ACLCommandCategories[j].flag) {\n            addReplyStatusFormat(c, \"@%s\", ACLCommandCategories[j].name);\n            flagcount++;\n        }\n    }\n    setDeferredSetLen(c, flaglen, flagcount);\n}\n\n/* AUTH <password>\n * AUTH <username> <password> (Redis >= 6.0 form)\n *\n * When the user is omitted it means that we are trying to authenticate\n * against the default user. */\nvoid authCommand(client *c) {\n    /* Only two or three argument forms are allowed. */\n    if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Handle the two different forms here. The form with two arguments\n     * will just use \"default\" as username. */\n    robj *username, *password;\n    if (c->argc == 2) {\n        /* Mimic the old behavior of giving an error for the two commands\n         * from if no password is configured. */\n        if (DefaultUser->flags & USER_FLAG_NOPASS) {\n            addReplyError(c,\"AUTH <password> called without any password \"\n                            \"configured for the default user. Are you sure \"\n                            \"your configuration is correct?\");\n            return;\n        }\n\n        username = createStringObject(\"default\",7);\n        password = c->argv[1];\n    } else {\n        username = c->argv[1];\n        password = c->argv[2];\n    }\n\n    if (ACLAuthenticateUser(c,username,password) == C_OK) {\n        addReply(c,shared.ok);\n    } else {\n        addReplyError(c,\"-WRONGPASS invalid username-password pair\");\n    }\n\n    /* Free the \"default\" string object we created for the two\n     * arguments form. */\n    if (c->argc == 2) decrRefCount(username);\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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. */\nlist *listCreate(void)\n{\n    struct list *list;\n\n    if ((list = zmalloc(sizeof(*list))) == NULL)\n        return NULL;\n    list->head = list->tail = NULL;\n    list->len = 0;\n    list->dup = NULL;\n    list->free = NULL;\n    list->match = NULL;\n    return list;\n}\n\n/* Remove all the elements from the list without destroying the list itself. */\nvoid listEmpty(list *list)\n{\n    unsigned long len;\n    listNode *current, *next;\n\n    current = list->head;\n    len = list->len;\n    while(len--) {\n        next = current->next;\n        if (list->free) list->free(current->value);\n        zfree(current);\n        current = next;\n    }\n    list->head = list->tail = NULL;\n    list->len = 0;\n}\n\n/* Free the whole list.\n *\n * This function can't fail. */\nvoid listRelease(list *list)\n{\n    listEmpty(list);\n    zfree(list);\n}\n\n/* Add a new node to the list, to head, containing the specified 'value'\n * pointer as value.\n *\n * On error, NULL is returned and no operation is performed (i.e. the\n * list remains unaltered).\n * On success the 'list' pointer you pass to the function is returned. */\nlist *listAddNodeHead(list *list, void *value)\n{\n    listNode *node;\n\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    } else {\n        node->prev = NULL;\n        node->next = list->head;\n        list->head->prev = node;\n        list->head = node;\n    }\n    list->len++;\n    return list;\n}\n\n/* Add a new node to the list, to tail, containing the specified 'value'\n * pointer as value.\n *\n * On error, NULL is returned and no operation is performed (i.e. the\n * list remains unaltered).\n * On success the 'list' pointer you pass to the function is returned. */\nlist *listAddNodeTail(list *list, void *value)\n{\n    listNode *node;\n\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    } else {\n        node->prev = list->tail;\n        node->next = NULL;\n        list->tail->next = node;\n        list->tail = node;\n    }\n    list->len++;\n    return list;\n}\n\nlist *listInsertNode(list *list, listNode *old_node, void *value, int after) {\n    listNode *node;\n\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (after) {\n        node->prev = old_node;\n        node->next = old_node->next;\n        if (list->tail == old_node) {\n            list->tail = node;\n        }\n    } else {\n        node->next = old_node;\n        node->prev = old_node->prev;\n        if (list->head == old_node) {\n            list->head = node;\n        }\n    }\n    if (node->prev != NULL) {\n        node->prev->next = node;\n    }\n    if (node->next != NULL) {\n        node->next->prev = node;\n    }\n    list->len++;\n    return list;\n}\n\n/* Remove the specified node from the specified list.\n * It's up to the caller to free the private value of the node.\n *\n * This function can't fail. */\nvoid listDelNode(list *list, listNode *node)\n{\n    if (node->prev)\n        node->prev->next = node->next;\n    else\n        list->head = node->next;\n    if (node->next)\n        node->next->prev = node->prev;\n    else\n        list->tail = node->prev;\n    if (list->free) list->free(node->value);\n    zfree(node);\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. */\nlistIter *listGetIterator(list *list, int direction)\n{\n    listIter *iter;\n\n    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;\n    if (direction == AL_START_HEAD)\n        iter->next = list->head;\n    else\n        iter->next = list->tail;\n    iter->direction = direction;\n    return iter;\n}\n\n/* Release the iterator memory */\nvoid listReleaseIterator(listIter *iter) {\n    zfree(iter);\n}\n\n/* Create an iterator in the list private iterator structure */\nvoid listRewind(list *list, listIter *li) {\n    li->next = list->head;\n    li->direction = AL_START_HEAD;\n}\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 * */\nlistNode *listNext(listIter *iter)\n{\n    listNode *current = iter->next;\n\n    if (current != NULL) {\n        if (iter->direction == AL_START_HEAD)\n            iter->next = current->next;\n        else\n            iter->next = current->prev;\n    }\n    return current;\n}\n\n/* Duplicate the whole list. On out of memory NULL is returned.\n * On success a copy of the original list is returned.\n *\n * The 'Dup' method set with listSetDupMethod() function is used\n * to copy the node value. Otherwise the same pointer value of\n * the original node is used as value of the copied node.\n *\n * The original list both on success or error is never modified. */\nlist *listDup(list *orig)\n{\n    list *copy;\n    listIter iter;\n    listNode *node;\n\n    if ((copy = listCreate()) == NULL)\n        return NULL;\n    copy->dup = orig->dup;\n    copy->free = orig->free;\n    copy->match = orig->match;\n    listRewind(orig, &iter);\n    while((node = listNext(&iter)) != NULL) {\n        void *value;\n\n        if (copy->dup) {\n            value = copy->dup(node->value);\n            if (value == NULL) {\n                listRelease(copy);\n                return NULL;\n            }\n        } else\n            value = node->value;\n        if (listAddNodeTail(copy, value) == NULL) {\n            listRelease(copy);\n            return NULL;\n        }\n    }\n    return copy;\n}\n\n/* Search the list for a node matching a given key.\n * The match is performed using the 'match' method\n * set with listSetMatchMethod(). If no 'match' method\n * is set, the 'value' pointer of every node is directly\n * compared with the 'key' pointer.\n *\n * On success the first matching node pointer is returned\n * (search starts from head). If no matching node exists\n * NULL is returned. */\nlistNode *listSearchKey(list *list, void *key)\n{\n    listIter iter;\n    listNode *node;\n\n    listRewind(list, &iter);\n    while((node = listNext(&iter)) != NULL) {\n        if (list->match) {\n            if (list->match(node->value, key)) {\n                return node;\n            }\n        } else {\n            if (key == node->value) {\n                return node;\n            }\n        }\n    }\n    return NULL;\n}\n\n/* Return the element at the specified zero-based index\n * where 0 is the head, 1 is the element next to head\n * and so on. Negative integers are used in order to count\n * from the tail, -1 is the last element, -2 the penultimate\n * and so on. If the index is out of range NULL is returned. */\nlistNode *listIndex(list *list, long index) {\n    listNode *n;\n\n    if (index < 0) {\n        index = (-index)-1;\n        n = list->tail;\n        while(index-- && n) n = n->prev;\n    } else {\n        n = list->head;\n        while(index-- && n) n = n->next;\n    }\n    return n;\n}\n\n/* Rotate the list removing the tail node and inserting it to the head. */\nvoid listRotateTailToHead(list *list) {\n    if (listLength(list) <= 1) return;\n\n    /* Detach current tail */\n    listNode *tail = list->tail;\n    list->tail = tail->prev;\n    list->tail->next = NULL;\n    /* Move it as head */\n    list->head->prev = tail;\n    tail->prev = NULL;\n    tail->next = list->head;\n    list->head = tail;\n}\n\n/* Rotate the list removing the head node and inserting it to the tail. */\nvoid listRotateHeadToTail(list *list) {\n    if (listLength(list) <= 1) return;\n\n    listNode *head = list->head;\n    /* Detach current head */\n    list->head = head->next;\n    list->head->prev = NULL;\n    /* Move it as tail */\n    list->tail->next = head;\n    head->next = NULL;\n    head->prev = list->tail;\n    list->tail = head;\n}\n\n/* Add all the elements of the list 'o' at the end of the\n * list 'l'. The list 'other' remains empty but otherwise valid. */\nvoid listJoin(list *l, list *o) {\n    if (o->head)\n        o->head->prev = l->tail;\n\n    if (l->tail)\n        l->tail->next = o->head;\n    else\n        l->head = o->head;\n\n    if (o->tail) l->tail = o->tail;\n    l->len += o->len;\n\n    /* Setup other as an empty list. */\n    o->head = o->tail = NULL;\n    o->len = 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\ntypedef struct listNode {\n    struct listNode *prev;\n    struct listNode *next;\n    void *value;\n} listNode;\n\ntypedef struct listIter {\n    listNode *next;\n    int direction;\n} listIter;\n\ntypedef struct list {\n    listNode *head;\n    listNode *tail;\n    void *(*dup)(void *ptr);\n    void (*free)(void *ptr);\n    int (*match)(void *ptr, void *key);\n    unsigned long len;\n} list;\n\n/* Functions implemented as macros */\n#define listLength(l) ((l)->len)\n#define listFirst(l) ((l)->head)\n#define listLast(l) ((l)->tail)\n#define listPrevNode(n) ((n)->prev)\n#define listNextNode(n) ((n)->next)\n#define listNodeValue(n) ((n)->value)\n\n#define listSetDupMethod(l,m) ((l)->dup = (m))\n#define listSetFreeMethod(l,m) ((l)->free = (m))\n#define listSetMatchMethod(l,m) ((l)->match = (m))\n\n#define listGetDupMethod(l) ((l)->dup)\n#define listGetFreeMethod(l) ((l)->free)\n#define listGetMatchMethod(l) ((l)->match)\n\n/* Prototypes */\nlist *listCreate(void);\nvoid listRelease(list *list);\nvoid listEmpty(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 listRotateTailToHead(list *list);\nvoid listRotateHeadToTail(list *list);\nvoid listJoin(list *l, list *o);\n\n/* Directions for iterators */\n#define AL_START_HEAD 0\n#define AL_START_TAIL 1\n\n#endif /* __ADLIST_H__ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\naeEventLoop *aeCreateEventLoop(int setsize) {\n    aeEventLoop *eventLoop;\n    int i;\n\n    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;\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    eventLoop->setsize = setsize;\n    eventLoop->lastTime = time(NULL);\n    eventLoop->timeEventHead = NULL;\n    eventLoop->timeEventNextId = 0;\n    eventLoop->stop = 0;\n    eventLoop->maxfd = -1;\n    eventLoop->beforesleep = NULL;\n    eventLoop->aftersleep = NULL;\n    eventLoop->flags = 0;\n    if (aeApiCreate(eventLoop) == -1) goto err;\n    /* Events with mask == AE_NONE are not set. So let's initialize the\n     * vector with it. */\n    for (i = 0; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n    return eventLoop;\n\nerr:\n    if (eventLoop) {\n        zfree(eventLoop->events);\n        zfree(eventLoop->fired);\n        zfree(eventLoop);\n    }\n    return NULL;\n}\n\n/* Return the current set size. */\nint aeGetSetSize(aeEventLoop *eventLoop) {\n    return eventLoop->setsize;\n}\n\n/* Tells the next iteration/s of the event processing to set timeout of 0. */\nvoid aeSetDontWait(aeEventLoop *eventLoop, int noWait) {\n    if (noWait)\n        eventLoop->flags |= AE_DONT_WAIT;\n    else\n        eventLoop->flags &= ~AE_DONT_WAIT;\n}\n\n/* Resize the maximum set size of the event loop.\n * If the requested set size is smaller than the current set size, but\n * there is already a file descriptor in use that is >= the requested\n * set size minus one, AE_ERR is returned and the operation is not\n * performed at all.\n *\n * Otherwise AE_OK is returned and the operation is successful. */\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {\n    int i;\n\n    if (setsize == eventLoop->setsize) return AE_OK;\n    if (eventLoop->maxfd >= setsize) return AE_ERR;\n    if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;\n\n    eventLoop->events = 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\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop) {\n    aeApiFree(eventLoop);\n    zfree(eventLoop->events);\n    zfree(eventLoop->fired);\n\n    /* Free the time events list. */\n    aeTimeEvent *next_te, *te = eventLoop->timeEventHead;\n    while (te) {\n        next_te = te->next;\n        zfree(te);\n        te = next_te;\n    }\n    zfree(eventLoop);\n}\n\nvoid aeStop(aeEventLoop *eventLoop) {\n    eventLoop->stop = 1;\n}\n\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData)\n{\n    if (fd >= eventLoop->setsize) {\n        errno = ERANGE;\n        return AE_ERR;\n    }\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    if (aeApiAddEvent(eventLoop, fd, mask) == -1)\n        return AE_ERR;\n    fe->mask |= mask;\n    if (mask & AE_READABLE) fe->rfileProc = proc;\n    if (mask & AE_WRITABLE) fe->wfileProc = proc;\n    fe->clientData = clientData;\n    if (fd > eventLoop->maxfd)\n        eventLoop->maxfd = fd;\n    return AE_OK;\n}\n\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)\n{\n    if (fd >= eventLoop->setsize) return;\n    aeFileEvent *fe = &eventLoop->events[fd];\n    if (fe->mask == AE_NONE) return;\n\n    /* We want to always remove AE_BARRIER if set when AE_WRITABLE\n     * is removed. */\n    if (mask & AE_WRITABLE) mask |= AE_BARRIER;\n\n    aeApiDelEvent(eventLoop, fd, mask);\n    fe->mask = fe->mask & (~mask);\n    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {\n        /* Update the max fd */\n        int j;\n\n        for (j = eventLoop->maxfd-1; j >= 0; j--)\n            if (eventLoop->events[j].mask != AE_NONE) break;\n        eventLoop->maxfd = j;\n    }\n}\n\nint aeGetFileEvents(aeEventLoop *eventLoop, int fd) {\n    if (fd >= eventLoop->setsize) return 0;\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    return fe->mask;\n}\n\nstatic void aeGetTime(long *seconds, long *milliseconds)\n{\n    struct timeval tv;\n\n    gettimeofday(&tv, NULL);\n    *seconds = tv.tv_sec;\n    *milliseconds = tv.tv_usec/1000;\n}\n\nstatic void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {\n    long cur_sec, cur_ms, when_sec, when_ms;\n\n    aeGetTime(&cur_sec, &cur_ms);\n    when_sec = cur_sec + milliseconds/1000;\n    when_ms = cur_ms + milliseconds%1000;\n    if (when_ms >= 1000) {\n        when_sec ++;\n        when_ms -= 1000;\n    }\n    *sec = when_sec;\n    *ms = when_ms;\n}\n\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc)\n{\n    long long id = eventLoop->timeEventNextId++;\n    aeTimeEvent *te;\n\n    te = zmalloc(sizeof(*te));\n    if (te == NULL) return AE_ERR;\n    te->id = id;\n    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);\n    te->timeProc = proc;\n    te->finalizerProc = finalizerProc;\n    te->clientData = clientData;\n    te->prev = NULL;\n    te->next = eventLoop->timeEventHead;\n    te->refcount = 0;\n    if (te->next)\n        te->next->prev = te;\n    eventLoop->timeEventHead = te;\n    return id;\n}\n\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    while(te) {\n        if (te->id == id) {\n            te->id = AE_DELETED_EVENT_ID;\n            return AE_OK;\n        }\n        te = te->next;\n    }\n    return AE_ERR; /* NO event with the specified ID found */\n}\n\n/* Search the first timer to fire.\n * This operation is useful to know how many time the select can be\n * put in sleep without to delay any event.\n * If there are no timers NULL is returned.\n *\n * Note that's O(N) since time events are unsorted.\n * Possible optimizations (not needed by Redis so far, but...):\n * 1) Insert the event in order, so that the nearest is just the head.\n *    Much better but still insertion or deletion of timers is O(N).\n * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).\n */\nstatic aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    aeTimeEvent *nearest = NULL;\n\n    while(te) {\n        if (!nearest || te->when_sec < nearest->when_sec ||\n                (te->when_sec == nearest->when_sec &&\n                 te->when_ms < nearest->when_ms))\n            nearest = te;\n        te = te->next;\n    }\n    return nearest;\n}\n\n/* Process time events */\nstatic int processTimeEvents(aeEventLoop *eventLoop) {\n    int processed = 0;\n    aeTimeEvent *te;\n    long long maxId;\n    time_t now = time(NULL);\n\n    /* If the system clock is moved to the future, and then set back to the\n     * right value, time events may be delayed in a random way. Often this\n     * means that scheduled operations will not be performed soon enough.\n     *\n     * Here we try to detect system clock skews, and force all the time\n     * events to be processed ASAP when this happens: the idea is that\n     * processing events earlier is less dangerous than delaying them\n     * indefinitely, and practice suggests it is. */\n    if (now < eventLoop->lastTime) {\n        te = eventLoop->timeEventHead;\n        while(te) {\n            te->when_sec = 0;\n            te = te->next;\n        }\n    }\n    eventLoop->lastTime = now;\n\n    te = eventLoop->timeEventHead;\n    maxId = eventLoop->timeEventNextId-1;\n    while(te) {\n        long now_sec, now_ms;\n        long long id;\n\n        /* Remove events scheduled for deletion. */\n        if (te->id == AE_DELETED_EVENT_ID) {\n            aeTimeEvent *next = te->next;\n            /* If a reference exists for this timer event,\n             * don't free it. This is currently incremented\n             * for recursive timerProc calls */\n            if (te->refcount) {\n                te = next;\n                continue;\n            }\n            if (te->prev)\n                te->prev->next = te->next;\n            else\n                eventLoop->timeEventHead = te->next;\n            if (te->next)\n                te->next->prev = te->prev;\n            if (te->finalizerProc)\n                te->finalizerProc(eventLoop, te->clientData);\n            zfree(te);\n            te = next;\n            continue;\n        }\n\n        /* Make sure we don't process time events created by time events in\n         * this iteration. Note that this check is currently useless: we always\n         * add new timers on the head, however if we change the implementation\n         * detail, this check may be useful again: we keep it here for future\n         * defense. */\n        if (te->id > maxId) {\n            te = te->next;\n            continue;\n        }\n        aeGetTime(&now_sec, &now_ms);\n        if (now_sec > te->when_sec ||\n            (now_sec == te->when_sec && now_ms >= te->when_ms))\n        {\n            int retval;\n\n            id = te->id;\n            te->refcount++;\n            retval = te->timeProc(eventLoop, id, te->clientData);\n            te->refcount--;\n            processed++;\n            if (retval != AE_NOMORE) {\n                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);\n            } else {\n                te->id = AE_DELETED_EVENT_ID;\n            }\n        }\n        te = te->next;\n    }\n    return processed;\n}\n\n/* Process every pending time event, then every pending file event\n * (that may be registered by time event callbacks just processed).\n * Without special flags the function sleeps until some file event\n * fires, or when the next time event occurs (if any).\n *\n * If flags is 0, the function does nothing and returns.\n * if flags has AE_ALL_EVENTS set, all the kind of events are processed.\n * if flags has AE_FILE_EVENTS set, file events are processed.\n * if flags has AE_TIME_EVENTS set, time events are processed.\n * if flags has AE_DONT_WAIT set the function returns ASAP until all\n * the events that's possible to process without to wait are processed.\n * if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called.\n * if flags has AE_CALL_BEFORE_SLEEP set, the beforesleep callback is called.\n *\n * The function returns the number of events processed. */\nint aeProcessEvents(aeEventLoop *eventLoop, int flags)\n{\n    int processed = 0, numevents;\n\n    /* Nothing to do? return ASAP */\n    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;\n\n    /* Note that we want call select() even if there are no\n     * file events to process as long as we want to process time\n     * events, in order to sleep until the next time event is ready\n     * to fire. */\n    if (eventLoop->maxfd != -1 ||\n        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {\n        int j;\n        aeTimeEvent *shortest = NULL;\n        struct timeval tv, *tvp;\n\n        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))\n            shortest = aeSearchNearestTimer(eventLoop);\n        if (shortest) {\n            long now_sec, now_ms;\n\n            aeGetTime(&now_sec, &now_ms);\n            tvp = &tv;\n\n            /* How many milliseconds we need to wait for the next\n             * time event to fire? */\n            long long ms =\n                (shortest->when_sec - now_sec)*1000 +\n                shortest->when_ms - now_ms;\n\n            if (ms > 0) {\n                tvp->tv_sec = ms/1000;\n                tvp->tv_usec = (ms % 1000)*1000;\n            } else {\n                tvp->tv_sec = 0;\n                tvp->tv_usec = 0;\n            }\n        } else {\n            /* If we have to check for events but need to return\n             * ASAP because of AE_DONT_WAIT we need to set the timeout\n             * to zero */\n            if (flags & AE_DONT_WAIT) {\n                tv.tv_sec = tv.tv_usec = 0;\n                tvp = &tv;\n            } else {\n                /* Otherwise we can block */\n                tvp = NULL; /* wait forever */\n            }\n        }\n\n        if (eventLoop->flags & AE_DONT_WAIT) {\n            tv.tv_sec = tv.tv_usec = 0;\n            tvp = &tv;\n        }\n\n        if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)\n            eventLoop->beforesleep(eventLoop);\n\n        /* Call the multiplexing API, will return only on timeout or when\n         * some event fires. */\n        numevents = aeApiPoll(eventLoop, tvp);\n\n        /* After sleep callback. */\n        if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)\n            eventLoop->aftersleep(eventLoop);\n\n        for (j = 0; j < numevents; j++) {\n            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];\n            int mask = eventLoop->fired[j].mask;\n            int fd = eventLoop->fired[j].fd;\n            int fired = 0; /* Number of events fired for current fd. */\n\n            /* Normally we execute the readable event first, and the writable\n             * event laster. This is useful as sometimes we may be able\n             * to serve the reply of a query immediately after processing the\n             * query.\n             *\n             * However if AE_BARRIER is set in the mask, our application is\n             * asking us to do the reverse: never fire the writable event\n             * after the readable. In such a case, we invert the calls.\n             * This is useful when, for instance, we want to do things\n             * in the beforeSleep() hook, like fsynching a file to disk,\n             * before replying to a client. */\n            int invert = fe->mask & AE_BARRIER;\n\n            /* Note the \"fe->mask & mask & ...\" code: maybe an already\n             * processed event removed an element that fired and we still\n             * didn't processed, so we check if the event is still valid.\n             *\n             * Fire the readable event if the call sequence is not\n             * inverted. */\n            if (!invert && fe->mask & mask & AE_READABLE) {\n                fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n                fired++;\n                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */\n            }\n\n            /* Fire the writable event. */\n            if (fe->mask & mask & AE_WRITABLE) {\n                if (!fired || fe->wfileProc != fe->rfileProc) {\n                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);\n                    fired++;\n                }\n            }\n\n            /* If we have to invert the call, fire the readable event now\n             * after the writable one. */\n            if (invert) {\n                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */\n                if ((fe->mask & mask & AE_READABLE) &&\n                    (!fired || fe->wfileProc != fe->rfileProc))\n                {\n                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n                    fired++;\n                }\n            }\n\n            processed++;\n        }\n    }\n    /* Check time events */\n    if (flags & AE_TIME_EVENTS)\n        processed += processTimeEvents(eventLoop);\n\n    return processed; /* return the number of processed file/time events */\n}\n\n/* Wait for milliseconds until the given file descriptor becomes\n * writable/readable/exception */\nint aeWait(int fd, int mask, long long milliseconds) {\n    struct pollfd pfd;\n    int retmask = 0, retval;\n\n    memset(&pfd, 0, sizeof(pfd));\n    pfd.fd = fd;\n    if (mask & AE_READABLE) pfd.events |= POLLIN;\n    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;\n\n    if ((retval = poll(&pfd, 1, milliseconds))== 1) {\n        if (pfd.revents & POLLIN) retmask |= AE_READABLE;\n        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;\n        if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;\n        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;\n        return retmask;\n    } else {\n        return retval;\n    }\n}\n\nvoid aeMain(aeEventLoop *eventLoop) {\n    eventLoop->stop = 0;\n    while (!eventLoop->stop) {\n        aeProcessEvents(eventLoop, AE_ALL_EVENTS|\n                                   AE_CALL_BEFORE_SLEEP|\n                                   AE_CALL_AFTER_SLEEP);\n    }\n}\n\nchar *aeGetApiName(void) {\n    return aeApiName();\n}\n\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {\n    eventLoop->beforesleep = beforesleep;\n}\n\nvoid aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) {\n    eventLoop->aftersleep = aftersleep;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <time.h>\n\n#define AE_OK 0\n#define AE_ERR -1\n\n#define AE_NONE 0       /* No events registered. */\n#define AE_READABLE 1   /* Fire when descriptor is readable. */\n#define AE_WRITABLE 2   /* Fire when descriptor is writable. */\n#define AE_BARRIER 4    /* With WRITABLE, never fire the event if the\n                           READABLE event already fired in the same event\n                           loop iteration. Useful when you want to persist\n                           things to disk before sending replies, and want\n                           to do that in a group fashion. */\n\n#define AE_FILE_EVENTS (1<<0)\n#define AE_TIME_EVENTS (1<<1)\n#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)\n#define AE_DONT_WAIT (1<<2)\n#define AE_CALL_BEFORE_SLEEP (1<<3)\n#define AE_CALL_AFTER_SLEEP (1<<4)\n\n#define AE_NOMORE -1\n#define AE_DELETED_EVENT_ID -1\n\n/* Macros */\n#define AE_NOTUSED(V) ((void) V)\n\nstruct aeEventLoop;\n\n/* Types and data structures */\ntypedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);\ntypedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);\ntypedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);\ntypedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);\n\n/* File event structure */\ntypedef struct aeFileEvent {\n    int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */\n    aeFileProc *rfileProc;\n    aeFileProc *wfileProc;\n    void *clientData;\n} aeFileEvent;\n\n/* Time event structure */\ntypedef struct aeTimeEvent {\n    long long id; /* time event identifier. */\n    long when_sec; /* seconds */\n    long when_ms; /* milliseconds */\n    aeTimeProc *timeProc;\n    aeEventFinalizerProc *finalizerProc;\n    void *clientData;\n    struct aeTimeEvent *prev;\n    struct aeTimeEvent *next;\n    int refcount; /* refcount to prevent timer events from being\n  \t\t   * freed in recursive time event calls. */\n} aeTimeEvent;\n\n/* A fired event */\ntypedef struct aeFiredEvent {\n    int fd;\n    int mask;\n} aeFiredEvent;\n\n/* State of an event based program */\ntypedef struct aeEventLoop {\n    int maxfd;   /* highest file descriptor currently registered */\n    int setsize; /* max number of file descriptors tracked */\n    long long timeEventNextId;\n    time_t lastTime;     /* Used to detect system clock skew */\n    aeFileEvent *events; /* Registered events */\n    aeFiredEvent *fired; /* Fired events */\n    aeTimeEvent *timeEventHead;\n    int stop;\n    void *apidata; /* This is used for polling API specific data */\n    aeBeforeSleepProc *beforesleep;\n    aeBeforeSleepProc *aftersleep;\n    int flags;\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);\nvoid aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep);\nint aeGetSetSize(aeEventLoop *eventLoop);\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize);\nvoid aeSetDontWait(aeEventLoop *eventLoop, int noWait);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\ntypedef struct aeApiState {\n    int epfd;\n    struct epoll_event *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 epoll_event)*eventLoop->setsize);\n    if (!state->events) {\n        zfree(state);\n        return -1;\n    }\n    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */\n    if (state->epfd == -1) {\n        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 epoll_event)*setsize);\n    return 0;\n}\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\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    /* If the fd was already monitored for some event, we need a MOD\n     * operation. Otherwise we need an ADD operation. */\n    int op = eventLoop->events[fd].mask == AE_NONE ?\n            EPOLL_CTL_ADD : EPOLL_CTL_MOD;\n\n    ee.events = 0;\n    mask |= eventLoop->events[fd].mask; /* Merge old events */\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    int mask = eventLoop->events[fd].mask & (~delmask);\n\n    ee.events = 0;\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (mask != AE_NONE) {\n        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);\n    } else {\n        /* Note, Kernel < 2.6.9 requires a non null event pointer even for\n         * EPOLL_CTL_DEL. */\n        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,\n            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);\n    if (retval > 0) {\n        int j;\n\n        numevents = retval;\n        for (j = 0; j < numevents; j++) {\n            int mask = 0;\n            struct epoll_event *e = state->events+j;\n\n            if (e->events & EPOLLIN) mask |= AE_READABLE;\n            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;\n            if (e->events & EPOLLERR) mask |= AE_WRITABLE|AE_READABLE;\n            if (e->events & EPOLLHUP) mask |= AE_WRITABLE|AE_READABLE;\n            eventLoop->fired[j].fd = e->data.fd;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"epoll\";\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 <sys/select.h>\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": "Chapter02/redis-6.0.3/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 <sys/time.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\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\nint anetSetBlock(char *err, int fd, int non_block) {\n    int flags;\n\n    /* Set the socket blocking (if non_block is zero) or 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\n    if (non_block)\n        flags |= O_NONBLOCK;\n    else\n        flags &= ~O_NONBLOCK;\n\n    if (fcntl(fd, F_SETFL, flags) == -1) {\n        anetSetError(err, \"fcntl(F_SETFL,O_NONBLOCK): %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\nint anetNonBlock(char *err, int fd) {\n    return anetSetBlock(err,fd,1);\n}\n\nint anetBlock(char *err, int fd) {\n    return anetSetBlock(err,fd,0);\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. */\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#else\n    ((void) interval); /* Avoid unused var warning for non Linux systems. */\n#endif\n\n    return ANET_OK;\n}\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\nint anetEnableTcpNoDelay(char *err, int fd)\n{\n    return anetSetTcpNoDelay(err, fd, 1);\n}\n\nint anetDisableTcpNoDelay(char *err, int fd)\n{\n    return anetSetTcpNoDelay(err, fd, 0);\n}\n\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\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/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified\n * number of milliseconds, or disable it if the 'ms' argument is zero. */\nint anetSendTimeout(char *err, int fd, long long ms) {\n    struct timeval tv;\n\n    tv.tv_sec = ms/1000;\n    tv.tv_usec = (ms%1000)*1000;\n    if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {\n        anetSetError(err, \"setsockopt SO_SNDTIMEO: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/* Set the socket receive timeout (SO_RCVTIMEO socket option) to the specified\n * number of milliseconds, or disable it if the 'ms' argument is zero. */\nint anetRecvTimeout(char *err, int fd, long long ms) {\n    struct timeval tv;\n\n    tv.tv_sec = ms/1000;\n    tv.tv_usec = (ms%1000)*1000;\n    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {\n        anetSetError(err, \"setsockopt SO_RCVTIMEO: %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. */\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\nstatic int anetSetReuseAddr(char *err, int fd) {\n    int yes = 1;\n    /* Make sure connection-intensive things like the redis benchmark\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\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#define ANET_CONNECT_NONE 0\n#define ANET_CONNECT_NONBLOCK 1\n#define ANET_CONNECT_BE_BINDING 2 /* Best effort binding. */\nstatic int anetTcpGenericConnect(char *err, const char *addr, int port,\n                                 const 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            {\n                anetSetError(err, \"%s\", gai_strerror(rv));\n                goto error;\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            freeaddrinfo(bservinfo);\n            if (!bound) {\n                anetSetError(err, \"bind: %s\", strerror(errno));\n                goto error;\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    }\n\nend:\n    freeaddrinfo(servinfo);\n\n    /* Handle best effort binding: if a binding address was used, but it is\n     * not possible to create a socket, try again without a binding address. */\n    if (s == ANET_ERR && source_addr && (flags & ANET_CONNECT_BE_BINDING)) {\n        return anetTcpGenericConnect(err,addr,port,NULL,flags);\n    } else {\n        return s;\n    }\n}\n\nint anetTcpConnect(char *err, const char *addr, int port)\n{\n    return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);\n}\n\nint anetTcpNonBlockConnect(char *err, const char *addr, int port)\n{\n    return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);\n}\n\nint anetTcpNonBlockBindConnect(char *err, const char *addr, int port,\n                               const char *source_addr)\n{\n    return anetTcpGenericConnect(err,addr,port,source_addr,\n            ANET_CONNECT_NONBLOCK);\n}\n\nint anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,\n                                         const char *source_addr)\n{\n    return anetTcpGenericConnect(err,addr,port,source_addr,\n            ANET_CONNECT_NONBLOCK|ANET_CONNECT_BE_BINDING);\n}\n\nint anetUnixGenericConnect(char *err, const 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            close(s);\n            return ANET_ERR;\n        }\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\nint anetUnixConnect(char *err, const char *path)\n{\n    return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);\n}\n\nint anetUnixNonBlockConnect(char *err, const 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) */\nint anetRead(int fd, char *buf, int count)\n{\n    ssize_t 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 written before to return\n * (unless error is encountered) */\nint anetWrite(int fd, char *buf, int count)\n{\n    ssize_t 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\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 = -1, 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) s = ANET_ERR;\n        goto end;\n    }\n    if (p == NULL) {\n        anetSetError(err, \"unable to bind socket, errno: %d\", errno);\n        goto error;\n    }\n\nerror:\n    if (s != -1) close(s);\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\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\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\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\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) goto error;\n    if (ip_len == 0) goto error;\n\n    if (sa.ss_family == AF_INET) {\n        struct sockaddr_in *s = (struct sockaddr_in *)&sa;\n        if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);\n        if (port) *port = ntohs(s->sin_port);\n    } else if (sa.ss_family == AF_INET6) {\n        struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;\n        if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);\n        if (port) *port = ntohs(s->sin6_port);\n    } else if (sa.ss_family == AF_UNIX) {\n        if (ip) strncpy(ip,\"/unixsocket\",ip_len);\n        if (port) *port = 0;\n    } else {\n        goto error;\n    }\n    return 0;\n\nerror:\n    if (ip) {\n        if (ip_len >= 2) {\n            ip[0] = '?';\n            ip[1] = '\\0';\n        } else if (ip_len == 1) {\n            ip[0] = '\\0';\n        }\n    }\n    if (port) *port = 0;\n    return -1;\n}\n\n/* Format an IP,port pair into something easy to parse. If IP is IPv6\n * (matches for \":\"), the ip is surrounded by []. IP and port are just\n * separated by colons. This the standard to display addresses within Redis. */\nint anetFormatAddr(char *buf, size_t buf_len, char *ip, int port) {\n    return snprintf(buf,buf_len, strchr(ip,':') ?\n           \"[%s]:%d\" : \"%s:%d\", ip, port);\n}\n\n/* Like anetFormatAddr() but extract ip and port from the socket's peer. */\nint anetFormatPeer(int fd, char *buf, size_t buf_len) {\n    char ip[INET6_ADDRSTRLEN];\n    int port;\n\n    anetPeerToString(fd,ip,sizeof(ip),&port);\n    return anetFormatAddr(buf, buf_len, ip, port);\n}\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\nint anetFormatSock(int fd, char *fmt, size_t fmt_len) {\n    char ip[INET6_ADDRSTRLEN];\n    int port;\n\n    anetSockName(fd,ip,sizeof(ip),&port);\n    return anetFormatAddr(fmt, fmt_len, ip, port);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <sys/types.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) || defined(_AIX)\n#define AF_LOCAL AF_UNIX\n#endif\n\n#ifdef _AIX\n#undef ip_len\n#endif\n\nint anetTcpConnect(char *err, const char *addr, int port);\nint anetTcpNonBlockConnect(char *err, const char *addr, int port);\nint anetTcpNonBlockBindConnect(char *err, const char *addr, int port, const char *source_addr);\nint anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr);\nint anetUnixConnect(char *err, const char *path);\nint anetUnixNonBlockConnect(char *err, const 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 anetBlock(char *err, int fd);\nint anetEnableTcpNoDelay(char *err, int fd);\nint anetDisableTcpNoDelay(char *err, int fd);\nint anetTcpKeepAlive(char *err, int fd);\nint anetSendTimeout(char *err, int fd, long long ms);\nint anetRecvTimeout(char *err, int fd, long long ms);\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);\nint anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port);\nint anetFormatPeer(int fd, char *fmt, size_t fmt_len);\nint anetFormatSock(int fd, char *fmt, size_t fmt_len);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.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#include <sys/param.h>\n\nvoid aofUpdateCurrentSize(void);\nvoid aofClosePipes(void);\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite buffer implementation.\n *\n * The following code implement a simple buffer used in order to accumulate\n * changes while the background process is rewriting the AOF file.\n *\n * We only need to append, but can't just use realloc with a large block\n * because 'huge' reallocs are not always handled as one could expect\n * (via remapping of pages at OS level) but may involve copying data.\n *\n * For this reason we use a list of blocks, every block is\n * AOF_RW_BUF_BLOCK_SIZE bytes.\n * ------------------------------------------------------------------------- */\n\n#define AOF_RW_BUF_BLOCK_SIZE (1024*1024*10)    /* 10 MB per block */\n\ntypedef struct aofrwblock {\n    unsigned long used, free;\n    char buf[AOF_RW_BUF_BLOCK_SIZE];\n} aofrwblock;\n\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. */\nvoid aofRewriteBufferReset(void) {\n    if (server.aof_rewrite_buf_blocks)\n        listRelease(server.aof_rewrite_buf_blocks);\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 rewrite buffer. */\nunsigned long aofRewriteBufferSize(void) {\n    listNode *ln;\n    listIter li;\n    unsigned long size = 0;\n\n    listRewind(server.aof_rewrite_buf_blocks,&li);\n    while((ln = listNext(&li))) {\n        aofrwblock *block = listNodeValue(ln);\n        size += block->used;\n    }\n    return size;\n}\n\n/* Event handler used to send data to the child process doing the AOF\n * rewrite. We send pieces of our AOF differences buffer so that the final\n * write when the child finishes the rewrite will be small. */\nvoid aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {\n    listNode *ln;\n    aofrwblock *block;\n    ssize_t nwritten;\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(privdata);\n    UNUSED(mask);\n\n    while(1) {\n        ln = listFirst(server.aof_rewrite_buf_blocks);\n        block = ln ? ln->value : NULL;\n        if (server.aof_stop_sending_diff || !block) {\n            aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child,\n                              AE_WRITABLE);\n            return;\n        }\n        if (block->used > 0) {\n            nwritten = write(server.aof_pipe_write_data_to_child,\n                             block->buf,block->used);\n            if (nwritten <= 0) return;\n            memmove(block->buf,block->buf+nwritten,block->used-nwritten);\n            block->used -= nwritten;\n            block->free += nwritten;\n        }\n        if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks,ln);\n    }\n}\n\n/* Append data to the AOF rewrite buffer, allocating new blocks if needed. */\nvoid aofRewriteBufferAppend(unsigned char *s, unsigned long len) {\n    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        if (block) {\n            unsigned long thislen = (block->free < len) ? block->free : len;\n            if (thislen) {  /* The current block is not already full. */\n                memcpy(block->buf+block->used, s, thislen);\n                block->used += thislen;\n                block->free -= thislen;\n                s += thislen;\n                len -= thislen;\n            }\n        }\n\n        if (len) { /* First block to allocate, or need another block. */\n            int numblocks;\n\n            block = zmalloc(sizeof(*block));\n            block->free = AOF_RW_BUF_BLOCK_SIZE;\n            block->used = 0;\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            numblocks = listLength(server.aof_rewrite_buf_blocks);\n            if (((numblocks+1) % 10) == 0) {\n                int level = ((numblocks+1) % 100) == 0 ? LL_WARNING :\n                                                         LL_NOTICE;\n                serverLog(level,\"Background AOF buffer size: %lu MB\",\n                    aofRewriteBufferSize()/(1024*1024));\n            }\n        }\n    }\n\n    /* Install a file event to send data to the rewrite child if there is\n     * not one already. */\n    if (aeGetFileEvents(server.el,server.aof_pipe_write_data_to_child) == 0) {\n        aeCreateFileEvent(server.el, server.aof_pipe_write_data_to_child,\n            AE_WRITABLE, aofChildWriteDiffData, NULL);\n    }\n}\n\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. */\nssize_t aofRewriteBufferWrite(int fd) {\n    listNode *ln;\n    listIter li;\n    ssize_t count = 0;\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            nwritten = write(fd,block->buf,block->used);\n            if (nwritten != (ssize_t)block->used) {\n                if (nwritten == 0) errno = EIO;\n                return -1;\n            }\n            count += nwritten;\n        }\n    }\n    return count;\n}\n\n/* ----------------------------------------------------------------------------\n * AOF file implementation\n * ------------------------------------------------------------------------- */\n\n/* Return true if an AOf fsync is currently already in progress in a\n * BIO thread. */\nint aofFsyncInProgress(void) {\n    return bioPendingJobsOfType(BIO_AOF_FSYNC) != 0;\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. */\nvoid aof_background_fsync(int fd) {\n    bioCreateBackgroundJob(BIO_AOF_FSYNC,(void*)(long)fd,NULL,NULL);\n}\n\n/* Kills an AOFRW child process if exists */\nvoid killAppendOnlyChild(void) {\n    int statloc;\n    /* No AOFRW child? return. */\n    if (server.aof_child_pid == -1) return;\n    /* Kill AOFRW child, wait for child exit. */\n    serverLog(LL_NOTICE,\"Killing running AOF rewrite child: %ld\",\n        (long) server.aof_child_pid);\n    if (kill(server.aof_child_pid,SIGUSR1) != -1) {\n        while(wait3(&statloc,0,NULL) != server.aof_child_pid);\n    }\n    /* Reset the buffer accumulating changes while the child saves. */\n    aofRewriteBufferReset();\n    aofRemoveTempFile(server.aof_child_pid);\n    server.aof_child_pid = -1;\n    server.aof_rewrite_time_start = -1;\n    /* Close pipes used for IPC between the two processes. */\n    aofClosePipes();\n    closeChildInfoPipe();\n    updateDictResizePolicy();\n}\n\n/* Called when the user switches from \"appendonly yes\" to \"appendonly no\"\n * at runtime using the CONFIG command. */\nvoid stopAppendOnly(void) {\n    serverAssert(server.aof_state != AOF_OFF);\n    flushAppendOnlyFile(1);\n    redis_fsync(server.aof_fd);\n    close(server.aof_fd);\n\n    server.aof_fd = -1;\n    server.aof_selected_db = -1;\n    server.aof_state = AOF_OFF;\n    server.aof_rewrite_scheduled = 0;\n    killAppendOnlyChild();\n}\n\n/* Called when the user switches from \"appendonly no\" to \"appendonly yes\"\n * at runtime using the CONFIG command. */\nint startAppendOnly(void) {\n    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */\n    int newfd;\n\n    newfd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);\n    serverAssert(server.aof_state == AOF_OFF);\n    if (newfd == -1) {\n        char *cwdp = getcwd(cwd,MAXPATHLEN);\n\n        serverLog(LL_WARNING,\n            \"Redis needs to enable the AOF but can't open the \"\n            \"append only file %s (in server root dir %s): %s\",\n            server.aof_filename,\n            cwdp ? cwdp : \"unknown\",\n            strerror(errno));\n        return C_ERR;\n    }\n    if (hasActiveChildProcess() && server.aof_child_pid == -1) {\n        server.aof_rewrite_scheduled = 1;\n        serverLog(LL_WARNING,\"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.\");\n    } else {\n        /* If there is a pending AOF rewrite, we need to switch it off and\n         * start a new one: the old one cannot be reused because it is not\n         * accumulating the AOF buffer. */\n        if (server.aof_child_pid != -1) {\n            serverLog(LL_WARNING,\"AOF was enabled but there is already an AOF rewriting in background. Stopping background AOF and starting a rewrite now.\");\n            killAppendOnlyChild();\n        }\n        if (rewriteAppendOnlyFileBackground() == C_ERR) {\n            close(newfd);\n            serverLog(LL_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 C_ERR;\n        }\n    }\n    /* We correctly switched on AOF, now wait for the rewrite to be complete\n     * in order to append data on disk. */\n    server.aof_state = AOF_WAIT_REWRITE;\n    server.aof_last_fsync = server.unixtime;\n    server.aof_fd = newfd;\n    return C_OK;\n}\n\n/* This is a wrapper to the write syscall in order to retry on short writes\n * or if the syscall gets interrupted. It could look strange that we retry\n * on short writes given that we are writing to a block device: normally if\n * the first call is short, there is a end-of-space condition, so the next\n * is likely to fail. However apparently in modern systems this is no longer\n * true, and in general it looks just more resilient to retry the write. If\n * there is an actual error condition we'll get it at the next try. */\nssize_t aofWrite(int fd, const char *buf, size_t len) {\n    ssize_t nwritten = 0, totwritten = 0;\n\n    while(len) {\n        nwritten = write(fd, buf, len);\n\n        if (nwritten < 0) {\n            if (errno == EINTR) continue;\n            return totwritten ? totwritten : -1;\n        }\n\n        len -= nwritten;\n        buf += nwritten;\n        totwritten += nwritten;\n    }\n\n    return totwritten;\n}\n\n/* Write the append only file buffer on disk.\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 * About the 'force' argument:\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 * 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 * However if force is set to 1 we'll write regardless of the background\n * fsync. */\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    mstime_t latency;\n\n    if (sdslen(server.aof_buf) == 0) {\n        /* Check if we need to do fsync even the aof buffer is empty,\n         * because previously in AOF_FSYNC_EVERYSEC mode, fsync is\n         * called only when aof buffer is not empty, so if users\n         * stop write commands before fsync called in one second,\n         * the data in page cache cannot be flushed in time. */\n        if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&\n            server.aof_fsync_offset != server.aof_current_size &&\n            server.unixtime > server.aof_last_fsync &&\n            !(sync_in_progress = aofFsyncInProgress())) {\n            goto try_fsync;\n        } else {\n            return;\n        }\n    }\n\n    if (server.aof_fsync == AOF_FSYNC_EVERYSEC)\n        sync_in_progress = aofFsyncInProgress();\n\n    if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {\n        /* With this append fsync policy we do background fsyncing.\n         * If the fsync is still in progress we can try to delay\n         * the write for a couple of seconds. */\n        if (sync_in_progress) {\n            if (server.aof_flush_postponed_start == 0) {\n                /* No previous write postponing, remember that we are\n                 * postponing the flush and return. */\n                server.aof_flush_postponed_start = server.unixtime;\n                return;\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                return;\n            }\n            /* Otherwise fall trough, and go write since we can't wait\n             * over two seconds. */\n            server.aof_delayed_fsync++;\n            serverLog(LL_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    /* 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     * 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    if (server.aof_flush_sleep && sdslen(server.aof_buf)) {\n        usleep(server.aof_flush_sleep);\n    }\n\n    latencyStartMonitor(latency);\n    nwritten = aofWrite(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));\n    latencyEndMonitor(latency);\n    /* We want to capture different events for delayed writes:\n     * when the delay happens with a pending fsync, or with a saving child\n     * active, and when the above two conditions are missing.\n     * We also use an additional event name to save all samples which is\n     * useful for graphing / monitoring purposes. */\n    if (sync_in_progress) {\n        latencyAddSampleIfNeeded(\"aof-write-pending-fsync\",latency);\n    } else if (hasActiveChildProcess()) {\n        latencyAddSampleIfNeeded(\"aof-write-active-child\",latency);\n    } else {\n        latencyAddSampleIfNeeded(\"aof-write-alone\",latency);\n    }\n    latencyAddSampleIfNeeded(\"aof-write\",latency);\n\n    /* We performed the write so reset the postponed flush sentinel to zero. */\n    server.aof_flush_postponed_start = 0;\n\n    if (nwritten != (ssize_t)sdslen(server.aof_buf)) {\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        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        /* Log the AOF write error and record the error code. */\n        if (nwritten == -1) {\n            if (can_log) {\n                serverLog(LL_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                serverLog(LL_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            if (ftruncate(server.aof_fd, server.aof_current_size) == -1) {\n                if (can_log) {\n                    serverLog(LL_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 ftruncate() 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        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 synced on disk. */\n            serverLog(LL_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 = C_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        if (server.aof_last_write_status == C_ERR) {\n            serverLog(LL_WARNING,\n                \"AOF write error looks solved, Redis can write again.\");\n            server.aof_last_write_status = C_OK;\n        }\n    }\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    if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {\n        sdsclear(server.aof_buf);\n    } else {\n        sdsfree(server.aof_buf);\n        server.aof_buf = sdsempty();\n    }\n\ntry_fsync:\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    if (server.aof_no_fsync_on_rewrite && hasActiveChildProcess())\n        return;\n\n    /* Perform the fsync if needed. */\n    if (server.aof_fsync == AOF_FSYNC_ALWAYS) {\n        /* redis_fsync is defined as fdatasync() for Linux in order to avoid\n         * flushing metadata. */\n        latencyStartMonitor(latency);\n        redis_fsync(server.aof_fd); /* Let's try to get this data on the disk */\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"aof-fsync-always\",latency);\n        server.aof_fsync_offset = server.aof_current_size;\n        server.aof_last_fsync = server.unixtime;\n    } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&\n                server.unixtime > server.aof_last_fsync)) {\n        if (!sync_in_progress) {\n            aof_background_fsync(server.aof_fd);\n            server.aof_fsync_offset = server.aof_current_size;\n        }\n        server.aof_last_fsync = server.unixtime;\n    }\n}\n\nsds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) {\n    char buf[32];\n    int len, j;\n    robj *o;\n\n    buf[0] = '*';\n    len = 1+ll2string(buf+1,sizeof(buf)-1,argc);\n    buf[len++] = '\\r';\n    buf[len++] = '\\n';\n    dst = sdscatlen(dst,buf,len);\n\n    for (j = 0; j < argc; j++) {\n        o = getDecodedObject(argv[j]);\n        buf[0] = '$';\n        len = 1+ll2string(buf+1,sizeof(buf)-1,sdslen(o->ptr));\n        buf[len++] = '\\r';\n        buf[len++] = '\\n';\n        dst = sdscatlen(dst,buf,len);\n        dst = sdscatlen(dst,o->ptr,sdslen(o->ptr));\n        dst = sdscatlen(dst,\"\\r\\n\",2);\n        decrRefCount(o);\n    }\n    return dst;\n}\n\n/* Create the sds representation of an PEXPIREAT command, using\n * 'seconds' as time to live and 'cmd' to understand what command\n * we are translating into a PEXPIREAT.\n *\n * This command is used in order to translate EXPIRE and PEXPIRE commands\n * into PEXPIREAT command so that we retain precision in the append only\n * file, and the time is always absolute and not relative. */\nsds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, robj *seconds) {\n    long long when;\n    robj *argv[3];\n\n    /* Make sure we can use strtoll */\n    seconds = getDecodedObject(seconds);\n    when = strtoll(seconds->ptr,NULL,10);\n    /* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT */\n    if (cmd->proc == expireCommand || cmd->proc == setexCommand ||\n        cmd->proc == expireatCommand)\n    {\n        when *= 1000;\n    }\n    /* Convert into absolute time for EXPIRE, PEXPIRE, SETEX, PSETEX */\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == setexCommand || cmd->proc == psetexCommand)\n    {\n        when += mstime();\n    }\n    decrRefCount(seconds);\n\n    argv[0] = createStringObject(\"PEXPIREAT\",9);\n    argv[1] = key;\n    argv[2] = createStringObjectFromLongLong(when);\n    buf = catAppendOnlyGenericCommand(buf, 3, argv);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[2]);\n    return buf;\n}\n\nvoid feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {\n    sds buf = sdsempty();\n    robj *tmpargv[3];\n\n    /* The DB this command was targeting is not the same as the last command\n     * we appended. To issue a SELECT command is needed. */\n    if (dictid != server.aof_selected_db) {\n        char seldb[64];\n\n        snprintf(seldb,sizeof(seldb),\"%d\",dictid);\n        buf = sdscatprintf(buf,\"*2\\r\\n$6\\r\\nSELECT\\r\\n$%lu\\r\\n%s\\r\\n\",\n            (unsigned long)strlen(seldb),seldb);\n        server.aof_selected_db = dictid;\n    }\n\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == expireatCommand) {\n        /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n    } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {\n        /* Translate SETEX/PSETEX to SET and PEXPIREAT */\n        tmpargv[0] = createStringObject(\"SET\",3);\n        tmpargv[1] = argv[1];\n        tmpargv[2] = argv[3];\n        buf = catAppendOnlyGenericCommand(buf,3,tmpargv);\n        decrRefCount(tmpargv[0]);\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n    } else if (cmd->proc == setCommand && argc > 3) {\n        int i;\n        robj *exarg = NULL, *pxarg = NULL;\n        /* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */\n        buf = catAppendOnlyGenericCommand(buf,3,argv);\n        for (i = 3; i < argc; i ++) {\n            if (!strcasecmp(argv[i]->ptr, \"ex\")) exarg = argv[i+1];\n            if (!strcasecmp(argv[i]->ptr, \"px\")) pxarg = argv[i+1];\n        }\n        serverAssert(!(exarg && pxarg));\n        if (exarg)\n            buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],\n                                               exarg);\n        if (pxarg)\n            buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],\n                                               pxarg);\n    } else {\n        /* All the other commands don't need translation or need the\n         * same translation already operated in the command vector\n         * for the replication itself. */\n        buf = catAppendOnlyGenericCommand(buf,argc,argv);\n    }\n\n    /* Append to the AOF buffer. This will be flushed on disk just before\n     * of re-entering the event loop, so before the client will get a\n     * positive reply about the operation performed. */\n    if (server.aof_state == AOF_ON)\n        server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf));\n\n    /* If a background append only file rewriting is in progress we want to\n     * accumulate the differences between the child DB and the current one\n     * in a buffer, so that when the child process will do its work we\n     * can append the differences to the new append only file. */\n    if (server.aof_child_pid != -1)\n        aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));\n\n    sdsfree(buf);\n}\n\n/* ----------------------------------------------------------------------------\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. */\nstruct client *createAOFClient(void) {\n    struct client *c = zmalloc(sizeof(*c));\n\n    selectDb(c,0);\n    c->id = CLIENT_ID_AOF; /* So modules can identify it's the AOF client. */\n    c->conn = NULL;\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 = 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    c->replstate = SLAVE_STATE_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    c->resp = 2;\n    c->user = NULL;\n    listSetFreeMethod(c->reply,freeClientReplyValue);\n    listSetDupMethod(c->reply,dupClientReplyValue);\n    initClientMultiState(c);\n    return c;\n}\n\nvoid freeFakeClientArgv(struct client *c) {\n    int j;\n\n    for (j = 0; j < c->argc; j++)\n        decrRefCount(c->argv[j]);\n    zfree(c->argv);\n}\n\nvoid freeFakeClient(struct client *c) {\n    sdsfree(c->querybuf);\n    listRelease(c->reply);\n    listRelease(c->watched_keys);\n    freeClientMultiState(c);\n    zfree(c);\n}\n\n/* Replay the append log file. On success C_OK is returned. On non fatal\n * error (the append only file is zero-length) C_ERR is returned. On\n * fatal error an error message is logged and the program exists. */\nint loadAppendOnlyFile(char *filename) {\n    struct client *fakeClient;\n    FILE *fp = fopen(filename,\"r\");\n    struct redis_stat sb;\n    int old_aof_state = server.aof_state;\n    long loops = 0;\n    off_t valid_up_to = 0; /* Offset of latest well-formed command loaded. */\n    off_t valid_before_multi = 0; /* Offset before MULTI command loaded. */\n\n    if (fp == NULL) {\n        serverLog(LL_WARNING,\"Fatal error: can't open the append log file for reading: %s\",strerror(errno));\n        exit(1);\n    }\n\n    /* Handle a zero-length AOF file as a special case. An empty AOF file\n     * is a valid AOF because an empty server with AOF enabled will create\n     * a zero length file at startup, that will remain like that if no write\n     * operation is received. */\n    if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {\n        server.aof_current_size = 0;\n        server.aof_fsync_offset = server.aof_current_size;\n        fclose(fp);\n        return C_ERR;\n    }\n\n    /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI\n     * to the same file we're about to read. */\n    server.aof_state = AOF_OFF;\n\n    fakeClient = createAOFClient();\n    startLoadingFile(fp, filename, RDBFLAGS_AOF_PREAMBLE);\n\n    /* Check if this AOF file has an RDB preamble. In that case we need to\n     * load the RDB file and later continue loading the AOF tail. */\n    char sig[5]; /* \"REDIS\" */\n    if (fread(sig,1,5,fp) != 5 || memcmp(sig,\"REDIS\",5) != 0) {\n        /* No RDB preamble, seek back at 0 offset. */\n        if (fseek(fp,0,SEEK_SET) == -1) goto readerr;\n    } else {\n        /* RDB preamble. Pass loading the RDB functions. */\n        rio rdb;\n\n        serverLog(LL_NOTICE,\"Reading RDB preamble from AOF file...\");\n        if (fseek(fp,0,SEEK_SET) == -1) goto readerr;\n        rioInitWithFile(&rdb,fp);\n        if (rdbLoadRio(&rdb,RDBFLAGS_AOF_PREAMBLE,NULL) != C_OK) {\n            serverLog(LL_WARNING,\"Error reading the RDB preamble of the AOF file, AOF loading aborted\");\n            goto readerr;\n        } else {\n            serverLog(LL_NOTICE,\"Reading the remaining AOF tail...\");\n        }\n    }\n\n    /* Read the actual AOF file, in REPL format, command by command. */\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        if (!(loops++ % 1000)) {\n            loadingProgress(ftello(fp));\n            processEventsWhileBlocked();\n            processModuleLoadingProgressEvent(1);\n        }\n\n        if (fgets(buf,sizeof(buf),fp) == NULL) {\n            if (feof(fp))\n                break;\n            else\n                goto readerr;\n        }\n        if (buf[0] != '*') goto fmterr;\n        if (buf[1] == '\\0') goto readerr;\n        argc = atoi(buf+1);\n        if (argc < 1) goto fmterr;\n\n        /* Load the next command in the AOF as our fake client\n         * argv. */\n        argv = zmalloc(sizeof(robj*)*argc);\n        fakeClient->argc = argc;\n        fakeClient->argv = argv;\n\n        for (j = 0; j < argc; j++) {\n            /* Parse the argument len. */\n            char *readres = fgets(buf,sizeof(buf),fp);\n            if (readres == NULL || buf[0] != '$') {\n                fakeClient->argc = j; /* Free up to j-1. */\n                freeFakeClientArgv(fakeClient);\n                if (readres == NULL)\n                    goto readerr;\n                else\n                    goto fmterr;\n            }\n            len = strtol(buf+1,NULL,10);\n\n            /* Read it into a string object. */\n            argsds = sdsnewlen(SDS_NOINIT,len);\n            if (len && fread(argsds,len,1,fp) == 0) {\n                sdsfree(argsds);\n                fakeClient->argc = j; /* Free up to j-1. */\n                freeFakeClientArgv(fakeClient);\n                goto readerr;\n            }\n            argv[j] = createObject(OBJ_STRING,argsds);\n\n            /* Discard CRLF. */\n            if (fread(buf,2,1,fp) == 0) {\n                fakeClient->argc = j+1; /* Free up to j. */\n                freeFakeClientArgv(fakeClient);\n                goto readerr;\n            }\n        }\n\n        /* Command lookup */\n        cmd = lookupCommand(argv[0]->ptr);\n        if (!cmd) {\n            serverLog(LL_WARNING,\n                \"Unknown command '%s' reading the append only file\",\n                (char*)argv[0]->ptr);\n            exit(1);\n        }\n\n        if (cmd == server.multiCommand) valid_before_multi = valid_up_to;\n\n        /* Run the command in the context of a fake client */\n        fakeClient->cmd = fakeClient->lastcmd = cmd;\n        if (fakeClient->flags & CLIENT_MULTI &&\n            fakeClient->cmd->proc != execCommand)\n        {\n            queueMultiCommand(fakeClient);\n        } else {\n            cmd->proc(fakeClient);\n        }\n\n        /* The fake client should not have a reply */\n        serverAssert(fakeClient->bufpos == 0 &&\n                     listLength(fakeClient->reply) == 0);\n\n        /* The fake client should never get blocked */\n        serverAssert((fakeClient->flags & CLIENT_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        freeFakeClientArgv(fakeClient);\n        fakeClient->cmd = NULL;\n        if (server.aof_load_truncated) valid_up_to = ftello(fp);\n        if (server.key_load_delay)\n            usleep(server.key_load_delay);\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, handle it as it was\n     * a short read, even if technically the protocol is correct: we want\n     * to remove the unprocessed tail and continue. */\n    if (fakeClient->flags & CLIENT_MULTI) {\n        serverLog(LL_WARNING,\n            \"Revert incomplete MULTI/EXEC transaction in AOF file\");\n        valid_up_to = valid_before_multi;\n        goto uxeof;\n    }\n\nloaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */\n    fclose(fp);\n    freeFakeClient(fakeClient);\n    server.aof_state = old_aof_state;\n    stopLoading(1);\n    aofUpdateCurrentSize();\n    server.aof_rewrite_base_size = server.aof_current_size;\n    server.aof_fsync_offset = server.aof_current_size;\n    return C_OK;\n\nreaderr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */\n    if (!feof(fp)) {\n        if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */\n        fclose(fp);\n        serverLog(LL_WARNING,\"Unrecoverable error reading the append only file: %s\", strerror(errno));\n        exit(1);\n    }\n\nuxeof: /* Unexpected AOF end of file. */\n    if (server.aof_load_truncated) {\n        serverLog(LL_WARNING,\"!!! Warning: short read while loading the AOF file !!!\");\n        serverLog(LL_WARNING,\"!!! Truncating the AOF at offset %llu !!!\",\n            (unsigned long long) valid_up_to);\n        if (valid_up_to == -1 || truncate(filename,valid_up_to) == -1) {\n            if (valid_up_to == -1) {\n                serverLog(LL_WARNING,\"Last valid command offset is invalid\");\n            } else {\n                serverLog(LL_WARNING,\"Error truncating the AOF file: %s\",\n                    strerror(errno));\n            }\n        } else {\n            /* Make sure the AOF file descriptor points to the end of the\n             * file after the truncate call. */\n            if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) {\n                serverLog(LL_WARNING,\"Can't seek the end of the AOF file: %s\",\n                    strerror(errno));\n            } else {\n                serverLog(LL_WARNING,\n                    \"AOF loaded anyway because aof-load-truncated is enabled\");\n                goto loaded_ok;\n            }\n        }\n    }\n    if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */\n    fclose(fp);\n    serverLog(LL_WARNING,\"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.\");\n    exit(1);\n\nfmterr: /* Format error. */\n    if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */\n    fclose(fp);\n    serverLog(LL_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 server.h dependency. */\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 == OBJ_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        serverPanic(\"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. */\nint rewriteListObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = listTypeLength(o);\n\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklist *list = o->ptr;\n        quicklistIter *li = quicklistGetIterator(list, AL_START_HEAD);\n        quicklistEntry entry;\n\n        while (quicklistNext(li,&entry)) {\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    AOF_REWRITE_ITEMS_PER_CMD : items;\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 (entry.value) {\n                if (rioWriteBulkString(r,(char*)entry.value,entry.sz) == 0) return 0;\n            } else {\n                if (rioWriteBulkLongLong(r,entry.longval) == 0) return 0;\n            }\n            if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        quicklistReleaseIterator(li);\n    } else {\n        serverPanic(\"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. */\nint rewriteSetObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = setTypeSize(o);\n\n    if (o->encoding == OBJ_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 > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        dictIterator *di = dictGetIterator(o->ptr);\n        dictEntry *de;\n\n        while((de = dictNext(di)) != NULL) {\n            sds ele = dictGetKey(de);\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;\n            if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        dictReleaseIterator(di);\n    } else {\n        serverPanic(\"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. */\nint rewriteSortedSetObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = zsetLength(o);\n\n    if (o->encoding == OBJ_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        serverAssert(eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        while (eptr != NULL) {\n            serverAssert(ziplistGet(eptr,&vstr,&vlen,&vll));\n            score = zzlGetScore(sptr);\n\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = o->ptr;\n        dictIterator *di = dictGetIterator(zs->dict);\n        dictEntry *de;\n\n        while((de = dictNext(di)) != NULL) {\n            sds ele = dictGetKey(de);\n            double *score = dictGetVal(de);\n\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;\n            if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        dictReleaseIterator(di);\n    } else {\n        serverPanic(\"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 * The 'hi' argument passes a valid Redis hash iterator.\n * The 'what' filed specifies if to write a key or a value and can be\n * either OBJ_HASH_KEY or OBJ_HASH_VALUE.\n *\n * The function returns 0 on error, non-zero on success. */\nstatic int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n        if (vstr)\n            return rioWriteBulkString(r, (char*)vstr, vlen);\n        else\n            return rioWriteBulkLongLong(r, vll);\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        sds value = hashTypeCurrentFromHashTable(hi, what);\n        return rioWriteBulkString(r, value, sdslen(value));\n    }\n\n    serverPanic(\"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. */\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) != C_ERR) {\n        if (count == 0) {\n            int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                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, OBJ_HASH_KEY) == 0) return 0;\n        if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE) == 0) return 0;\n        if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n        items--;\n    }\n\n    hashTypeReleaseIterator(hi);\n\n    return 1;\n}\n\n/* Helper for rewriteStreamObject() that generates a bulk string into the\n * AOF representing the ID 'id'. */\nint rioWriteBulkStreamID(rio *r,streamID *id) {\n    int retval;\n\n    sds replyid = sdscatfmt(sdsempty(),\"%U-%U\",id->ms,id->seq);\n    retval = rioWriteBulkString(r,replyid,sdslen(replyid));\n    sdsfree(replyid);\n    return retval;\n}\n\n/* Helper for rewriteStreamObject(): emit the XCLAIM needed in order to\n * add the message described by 'nack' having the id 'rawid', into the pending\n * list of the specified consumer. All this in the context of the specified\n * key and group. */\nint rioWriteStreamPendingEntry(rio *r, robj *key, const char *groupname, size_t groupname_len, streamConsumer *consumer, unsigned char *rawid, streamNACK *nack) {\n     /* XCLAIM <key> <group> <consumer> 0 <id> TIME <milliseconds-unix-time>\n               RETRYCOUNT <count> JUSTID FORCE. */\n    streamID id;\n    streamDecodeID(rawid,&id);\n    if (rioWriteBulkCount(r,'*',12) == 0) return 0;\n    if (rioWriteBulkString(r,\"XCLAIM\",6) == 0) return 0;\n    if (rioWriteBulkObject(r,key) == 0) return 0;\n    if (rioWriteBulkString(r,groupname,groupname_len) == 0) return 0;\n    if (rioWriteBulkString(r,consumer->name,sdslen(consumer->name)) == 0) return 0;\n    if (rioWriteBulkString(r,\"0\",1) == 0) return 0;\n    if (rioWriteBulkStreamID(r,&id) == 0) return 0;\n    if (rioWriteBulkString(r,\"TIME\",4) == 0) return 0;\n    if (rioWriteBulkLongLong(r,nack->delivery_time) == 0) return 0;\n    if (rioWriteBulkString(r,\"RETRYCOUNT\",10) == 0) return 0;\n    if (rioWriteBulkLongLong(r,nack->delivery_count) == 0) return 0;\n    if (rioWriteBulkString(r,\"JUSTID\",6) == 0) return 0;\n    if (rioWriteBulkString(r,\"FORCE\",5) == 0) return 0;\n    return 1;\n}\n\n/* Emit the commands needed to rebuild a stream object.\n * The function returns 0 on error, 1 on success. */\nint rewriteStreamObject(rio *r, robj *key, robj *o) {\n    stream *s = o->ptr;\n    streamIterator si;\n    streamIteratorStart(&si,s,NULL,NULL,0);\n    streamID id;\n    int64_t numfields;\n\n    if (s->length) {\n        /* Reconstruct the stream data using XADD commands. */\n        while(streamIteratorGetID(&si,&id,&numfields)) {\n            /* Emit a two elements array for each item. The first is\n             * the ID, the second is an array of field-value pairs. */\n\n            /* Emit the XADD <key> <id> ...fields... command. */\n            if (rioWriteBulkCount(r,'*',3+numfields*2) == 0) return 0;\n            if (rioWriteBulkString(r,\"XADD\",4) == 0) return 0;\n            if (rioWriteBulkObject(r,key) == 0) return 0;\n            if (rioWriteBulkStreamID(r,&id) == 0) return 0;\n            while(numfields--) {\n                unsigned char *field, *value;\n                int64_t field_len, value_len;\n                streamIteratorGetField(&si,&field,&value,&field_len,&value_len);\n                if (rioWriteBulkString(r,(char*)field,field_len) == 0) return 0;\n                if (rioWriteBulkString(r,(char*)value,value_len) == 0) return 0;\n            }\n        }\n    } else {\n        /* Use the XADD MAXLEN 0 trick to generate an empty stream if\n         * the key we are serializing is an empty string, which is possible\n         * for the Stream type. */\n        id.ms = 0; id.seq = 1; \n        if (rioWriteBulkCount(r,'*',7) == 0) return 0;\n        if (rioWriteBulkString(r,\"XADD\",4) == 0) return 0;\n        if (rioWriteBulkObject(r,key) == 0) return 0;\n        if (rioWriteBulkString(r,\"MAXLEN\",6) == 0) return 0;\n        if (rioWriteBulkString(r,\"0\",1) == 0) return 0;\n        if (rioWriteBulkStreamID(r,&id) == 0) return 0;\n        if (rioWriteBulkString(r,\"x\",1) == 0) return 0;\n        if (rioWriteBulkString(r,\"y\",1) == 0) return 0;\n    }\n\n    /* Append XSETID after XADD, make sure lastid is correct,\n     * in case of XDEL lastid. */\n    if (rioWriteBulkCount(r,'*',3) == 0) return 0;\n    if (rioWriteBulkString(r,\"XSETID\",6) == 0) return 0;\n    if (rioWriteBulkObject(r,key) == 0) return 0;\n    if (rioWriteBulkStreamID(r,&s->last_id) == 0) return 0;\n\n\n    /* Create all the stream consumer groups. */\n    if (s->cgroups) {\n        raxIterator ri;\n        raxStart(&ri,s->cgroups);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            streamCG *group = ri.data;\n            /* Emit the XGROUP CREATE in order to create the group. */\n            if (rioWriteBulkCount(r,'*',5) == 0) return 0;\n            if (rioWriteBulkString(r,\"XGROUP\",6) == 0) return 0;\n            if (rioWriteBulkString(r,\"CREATE\",6) == 0) return 0;\n            if (rioWriteBulkObject(r,key) == 0) return 0;\n            if (rioWriteBulkString(r,(char*)ri.key,ri.key_len) == 0) return 0;\n            if (rioWriteBulkStreamID(r,&group->last_id) == 0) return 0;\n\n            /* Generate XCLAIMs for each consumer that happens to\n             * have pending entries. Empty consumers have no semantical\n             * value so they are discarded. */\n            raxIterator ri_cons;\n            raxStart(&ri_cons,group->consumers);\n            raxSeek(&ri_cons,\"^\",NULL,0);\n            while(raxNext(&ri_cons)) {\n                streamConsumer *consumer = ri_cons.data;\n                /* For the current consumer, iterate all the PEL entries\n                 * to emit the XCLAIM protocol. */\n                raxIterator ri_pel;\n                raxStart(&ri_pel,consumer->pel);\n                raxSeek(&ri_pel,\"^\",NULL,0);\n                while(raxNext(&ri_pel)) {\n                    streamNACK *nack = ri_pel.data;\n                    if (rioWriteStreamPendingEntry(r,key,(char*)ri.key,\n                                                   ri.key_len,consumer,\n                                                   ri_pel.key,nack) == 0)\n                    {\n                        return 0;\n                    }\n                }\n                raxStop(&ri_pel);\n            }\n            raxStop(&ri_cons);\n        }\n        raxStop(&ri);\n    }\n\n    streamIteratorStop(&si);\n    return 1;\n}\n\n/* Call the module type callback in order to rewrite a data type\n * that is exported by a module and is not handled by Redis itself.\n * The function returns 0 on error, 1 on success. */\nint rewriteModuleObject(rio *r, robj *key, robj *o) {\n    RedisModuleIO io;\n    moduleValue *mv = o->ptr;\n    moduleType *mt = mv->type;\n    moduleInitIOContext(io,mt,r,key);\n    mt->aof_rewrite(&io,key,mv->value);\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    return io.error ? 0 : 1;\n}\n\n/* This function is called by the child rewriting the AOF file to read\n * the difference accumulated from the parent into a buffer, that is\n * concatenated at the end of the rewrite. */\nssize_t aofReadDiffFromParent(void) {\n    char buf[65536]; /* Default pipe buffer size on most Linux systems. */\n    ssize_t nread, total = 0;\n\n    while ((nread =\n            read(server.aof_pipe_read_data_from_parent,buf,sizeof(buf))) > 0) {\n        server.aof_child_diff = sdscatlen(server.aof_child_diff,buf,nread);\n        total += nread;\n    }\n    return total;\n}\n\nint rewriteAppendOnlyFileRio(rio *aof) {\n    dictIterator *di = NULL;\n    dictEntry *de;\n    size_t processed = 0;\n    int j;\n\n    for (j = 0; j < server.dbnum; j++) {\n        char selectcmd[] = \"*2\\r\\n$6\\r\\nSELECT\\r\\n\";\n        redisDb *db = server.db+j;\n        dict *d = db->dict;\n        if (dictSize(d) == 0) continue;\n        di = dictGetSafeIterator(d);\n\n        /* SELECT the new DB */\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        while((de = dictNext(di)) != NULL) {\n            sds keystr;\n            robj key, *o;\n            long long expiretime;\n\n            keystr = dictGetKey(de);\n            o = dictGetVal(de);\n            initStaticStringObject(key,keystr);\n\n            expiretime = getExpire(db,&key);\n\n            /* Save the key and associated value */\n            if (o->type == OBJ_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 == OBJ_LIST) {\n                if (rewriteListObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_SET) {\n                if (rewriteSetObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_ZSET) {\n                if (rewriteSortedSetObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_HASH) {\n                if (rewriteHashObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_STREAM) {\n                if (rewriteStreamObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_MODULE) {\n                if (rewriteModuleObject(aof,&key,o) == 0) goto werr;\n            } else {\n                serverPanic(\"Unknown object type\");\n            }\n            /* Save the expire time */\n            if (expiretime != -1) {\n                char cmd[]=\"*3\\r\\n$9\\r\\nPEXPIREAT\\r\\n\";\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            /* Read some diff from the parent process from time to time. */\n            if (aof->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES) {\n                processed = aof->processed_bytes;\n                aofReadDiffFromParent();\n            }\n        }\n        dictReleaseIterator(di);\n        di = NULL;\n    }\n    return C_OK;\n\nwerr:\n    if (di) dictReleaseIterator(di);\n    return C_ERR;\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 * 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 AOF_REWRITE_ITEMS_PER_CMD items per time\n * are inserted using a single command. */\nint rewriteAppendOnlyFile(char *filename) {\n    rio aof;\n    FILE *fp;\n    char tmpfile[256];\n    char byte;\n\n    /* Note that we have to use a different temp name here compared to the\n     * one used by rewriteAppendOnlyFileBackground() function. */\n    snprintf(tmpfile,256,\"temp-rewriteaof-%d.aof\", (int) getpid());\n    fp = fopen(tmpfile,\"w\");\n    if (!fp) {\n        serverLog(LL_WARNING, \"Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s\", strerror(errno));\n        return C_ERR;\n    }\n\n    server.aof_child_diff = sdsempty();\n    rioInitWithFile(&aof,fp);\n\n    if (server.aof_rewrite_incremental_fsync)\n        rioSetAutoSync(&aof,REDIS_AUTOSYNC_BYTES);\n\n    startSaving(RDBFLAGS_AOF_PREAMBLE);\n\n    if (server.aof_use_rdb_preamble) {\n        int error;\n        if (rdbSaveRio(&aof,&error,RDBFLAGS_AOF_PREAMBLE,NULL) == C_ERR) {\n            errno = error;\n            goto werr;\n        }\n    } else {\n        if (rewriteAppendOnlyFileRio(&aof) == C_ERR) goto werr;\n    }\n\n    /* Do an initial slow fsync here while the parent is still sending\n     * data, in order to make the next final fsync faster. */\n    if (fflush(fp) == EOF) goto werr;\n    if (fsync(fileno(fp)) == -1) goto werr;\n\n    /* Read again a few times to get more data from the parent.\n     * We can't read forever (the server may receive data from clients\n     * faster than it is able to send data to the child), so we try to read\n     * some more data in a loop as soon as there is a good chance more data\n     * will come. If it looks like we are wasting time, we abort (this\n     * happens after 20 ms without new data). */\n    int nodata = 0;\n    mstime_t start = mstime();\n    while(mstime()-start < 1000 && nodata < 20) {\n        if (aeWait(server.aof_pipe_read_data_from_parent, AE_READABLE, 1) <= 0)\n        {\n            nodata++;\n            continue;\n        }\n        nodata = 0; /* Start counting from zero, we stop on N *contiguous*\n                       timeouts. */\n        aofReadDiffFromParent();\n    }\n\n    /* Ask the master to stop sending diffs. */\n    if (write(server.aof_pipe_write_ack_to_parent,\"!\",1) != 1) goto werr;\n    if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK)\n        goto werr;\n    /* We read the ACK from the server using a 10 seconds timeout. Normally\n     * it should reply ASAP, but just in case we lose its reply, we are sure\n     * the child will eventually get terminated. */\n    if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||\n        byte != '!') goto werr;\n    serverLog(LL_NOTICE,\"Parent agreed to stop sending diffs. Finalizing AOF...\");\n\n    /* Read the final diff if any. */\n    aofReadDiffFromParent();\n\n    /* Write the received diff to the file. */\n    serverLog(LL_NOTICE,\n        \"Concatenating %.2f MB of AOF diff received from parent.\",\n        (double) sdslen(server.aof_child_diff) / (1024*1024));\n    if (rioWrite(&aof,server.aof_child_diff,sdslen(server.aof_child_diff)) == 0)\n        goto werr;\n\n    /* Make sure data will not remain on the OS's output buffers */\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    if (rename(tmpfile,filename) == -1) {\n        serverLog(LL_WARNING,\"Error moving temp append only file on the final destination: %s\", strerror(errno));\n        unlink(tmpfile);\n        stopSaving(0);\n        return C_ERR;\n    }\n    serverLog(LL_NOTICE,\"SYNC append only file rewrite performed\");\n    stopSaving(1);\n    return C_OK;\n\nwerr:\n    serverLog(LL_WARNING,\"Write error writing append only file on disk: %s\", strerror(errno));\n    fclose(fp);\n    unlink(tmpfile);\n    stopSaving(0);\n    return C_ERR;\n}\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite pipes for IPC\n * -------------------------------------------------------------------------- */\n\n/* This event handler is called when the AOF rewriting child sends us a\n * single '!' char to signal we should stop sending buffer diffs. The\n * parent sends a '!' as well to acknowledge. */\nvoid aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {\n    char byte;\n    UNUSED(el);\n    UNUSED(privdata);\n    UNUSED(mask);\n\n    if (read(fd,&byte,1) == 1 && byte == '!') {\n        serverLog(LL_NOTICE,\"AOF rewrite child asks to stop sending diffs.\");\n        server.aof_stop_sending_diff = 1;\n        if (write(server.aof_pipe_write_ack_to_child,\"!\",1) != 1) {\n            /* If we can't send the ack, inform the user, but don't try again\n             * since in the other side the children will use a timeout if the\n             * kernel can't buffer our write, or, the children was\n             * terminated. */\n            serverLog(LL_WARNING,\"Can't send ACK to AOF child: %s\",\n                strerror(errno));\n        }\n    }\n    /* Remove the handler since this can be called only one time during a\n     * rewrite. */\n    aeDeleteFileEvent(server.el,server.aof_pipe_read_ack_from_child,AE_READABLE);\n}\n\n/* Create the pipes used for parent - child process IPC during rewrite.\n * We have a data pipe used to send AOF incremental diffs to the child,\n * and two other pipes used by the children to signal it finished with\n * the rewrite so no more data should be written, and another for the\n * parent to acknowledge it understood this new condition. */\nint aofCreatePipes(void) {\n    int fds[6] = {-1, -1, -1, -1, -1, -1};\n    int j;\n\n    if (pipe(fds) == -1) goto error; /* parent -> children data. */\n    if (pipe(fds+2) == -1) goto error; /* children -> parent ack. */\n    if (pipe(fds+4) == -1) goto error; /* parent -> children ack. */\n    /* Parent -> children data is non blocking. */\n    if (anetNonBlock(NULL,fds[0]) != ANET_OK) goto error;\n    if (anetNonBlock(NULL,fds[1]) != ANET_OK) goto error;\n    if (aeCreateFileEvent(server.el, fds[2], AE_READABLE, aofChildPipeReadable, NULL) == AE_ERR) goto error;\n\n    server.aof_pipe_write_data_to_child = fds[1];\n    server.aof_pipe_read_data_from_parent = fds[0];\n    server.aof_pipe_write_ack_to_parent = fds[3];\n    server.aof_pipe_read_ack_from_child = fds[2];\n    server.aof_pipe_write_ack_to_child = fds[5];\n    server.aof_pipe_read_ack_from_parent = fds[4];\n    server.aof_stop_sending_diff = 0;\n    return C_OK;\n\nerror:\n    serverLog(LL_WARNING,\"Error opening /setting AOF rewrite IPC pipes: %s\",\n        strerror(errno));\n    for (j = 0; j < 6; j++) if(fds[j] != -1) close(fds[j]);\n    return C_ERR;\n}\n\nvoid aofClosePipes(void) {\n    aeDeleteFileEvent(server.el,server.aof_pipe_read_ack_from_child,AE_READABLE);\n    aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child,AE_WRITABLE);\n    close(server.aof_pipe_write_data_to_child);\n    close(server.aof_pipe_read_data_from_parent);\n    close(server.aof_pipe_write_ack_to_parent);\n    close(server.aof_pipe_read_ack_from_child);\n    close(server.aof_pipe_write_ack_to_child);\n    close(server.aof_pipe_read_ack_from_parent);\n}\n\n/* ----------------------------------------------------------------------------\n * AOF background rewrite\n * ------------------------------------------------------------------------- */\n\n/* This is how rewriting of the append only file in background works:\n *\n * 1) The user calls BGREWRITEAOF\n * 2) Redis calls this function, that forks():\n *    2a) the child rewrite the append only file in a temp file.\n *    2b) the parent accumulates differences in server.aof_rewrite_buf.\n * 3) When the child finished '2a' exists.\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 */\nint rewriteAppendOnlyFileBackground(void) {\n    pid_t childpid;\n\n    if (hasActiveChildProcess()) return C_ERR;\n    if (aofCreatePipes() != C_OK) return C_ERR;\n    openChildInfoPipe();\n    if ((childpid = redisFork()) == 0) {\n        char tmpfile[256];\n\n        /* Child */\n        redisSetProcTitle(\"redis-aof-rewrite\");\n        redisSetCpuAffinity(server.aof_rewrite_cpulist);\n        snprintf(tmpfile,256,\"temp-rewriteaof-bg-%d.aof\", (int) getpid());\n        if (rewriteAppendOnlyFile(tmpfile) == C_OK) {\n            sendChildCOWInfo(CHILD_INFO_TYPE_AOF, \"AOF rewrite\");\n            exitFromChild(0);\n        } else {\n            exitFromChild(1);\n        }\n    } else {\n        /* Parent */\n        if (childpid == -1) {\n            closeChildInfoPipe();\n            serverLog(LL_WARNING,\n                \"Can't rewrite append only file in background: fork: %s\",\n                strerror(errno));\n            aofClosePipes();\n            return C_ERR;\n        }\n        serverLog(LL_NOTICE,\n            \"Background append only file rewriting started by pid %d\",childpid);\n        server.aof_rewrite_scheduled = 0;\n        server.aof_rewrite_time_start = time(NULL);\n        server.aof_child_pid = childpid;\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        server.aof_selected_db = -1;\n        replicationScriptCacheFlush();\n        return C_OK;\n    }\n    return C_OK; /* unreached */\n}\n\nvoid bgrewriteaofCommand(client *c) {\n    if (server.aof_child_pid != -1) {\n        addReplyError(c,\"Background append only file rewriting already in progress\");\n    } else if (hasActiveChildProcess()) {\n        server.aof_rewrite_scheduled = 1;\n        addReplyStatus(c,\"Background append only file rewriting scheduled\");\n    } else if (rewriteAppendOnlyFileBackground() == C_OK) {\n        addReplyStatus(c,\"Background append only file rewriting started\");\n    } else {\n        addReplyError(c,\"Can't execute an AOF background rewriting. \"\n                        \"Please check the server logs for more information.\");\n    }\n}\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    snprintf(tmpfile,256,\"temp-rewriteaof-%d.aof\", (int) childpid);\n    unlink(tmpfile);\n}\n\n/* Update the server.aof_current_size field 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. */\nvoid aofUpdateCurrentSize(void) {\n    struct redis_stat sb;\n    mstime_t latency;\n\n    latencyStartMonitor(latency);\n    if (redis_fstat(server.aof_fd,&sb) == -1) {\n        serverLog(LL_WARNING,\"Unable to obtain the AOF file length. stat: %s\",\n            strerror(errno));\n    } else {\n        server.aof_current_size = sb.st_size;\n    }\n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"aof-fstat\",latency);\n}\n\n/* A background append only file rewriting (BGREWRITEAOF) terminated its work.\n * Handle this. */\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        mstime_t latency;\n\n        serverLog(LL_NOTICE,\n            \"Background AOF rewrite terminated with success\");\n\n        /* Flush the differences accumulated by the parent to the\n         * rewritten AOF. */\n        latencyStartMonitor(latency);\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            serverLog(LL_WARNING,\n                \"Unable to open the temporary AOF produced by the child: %s\", strerror(errno));\n            goto cleanup;\n        }\n\n        if (aofRewriteBufferWrite(newfd) == -1) {\n            serverLog(LL_WARNING,\n                \"Error trying to flush the parent diff to the rewritten AOF: %s\", strerror(errno));\n            close(newfd);\n            goto cleanup;\n        }\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"aof-rewrite-diff-write\",latency);\n\n        serverLog(LL_NOTICE,\n            \"Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)\", (double) aofRewriteBufferSize() / (1024*1024));\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         * There are two possible scenarios:\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         * 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         * 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        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        latencyStartMonitor(latency);\n        if (rename(tmpfile,server.aof_filename) == -1) {\n            serverLog(LL_WARNING,\n                \"Error trying to rename the temporary AOF file %s into %s: %s\",\n                tmpfile,\n                server.aof_filename,\n                strerror(errno));\n            close(newfd);\n            if (oldfd != -1) close(oldfd);\n            goto cleanup;\n        }\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"aof-rename\",latency);\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            close(newfd);\n        } else {\n            /* AOF enabled, replace the old fd with the new one. */\n            oldfd = server.aof_fd;\n            server.aof_fd = newfd;\n            if (server.aof_fsync == AOF_FSYNC_ALWAYS)\n                redis_fsync(newfd);\n            else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)\n                aof_background_fsync(newfd);\n            server.aof_selected_db = -1; /* Make sure SELECT is re-issued */\n            aofUpdateCurrentSize();\n            server.aof_rewrite_base_size = server.aof_current_size;\n            server.aof_fsync_offset = 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            sdsfree(server.aof_buf);\n            server.aof_buf = sdsempty();\n        }\n\n        server.aof_lastbgrewrite_status = C_OK;\n\n        serverLog(LL_NOTICE, \"Background AOF rewrite finished successfully\");\n        /* Change state from WAIT_REWRITE to ON if needed */\n        if (server.aof_state == AOF_WAIT_REWRITE)\n            server.aof_state = AOF_ON;\n\n        /* Asynchronously close the overwritten AOF. */\n        if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);\n\n        serverLog(LL_VERBOSE,\n            \"Background AOF rewrite signal handler took %lldus\", ustime()-now);\n    } else if (!bysignal && exitcode != 0) {\n        server.aof_lastbgrewrite_status = C_ERR;\n\n        serverLog(LL_WARNING,\n            \"Background AOF rewrite terminated with error\");\n    } else {\n        /* SIGUSR1 is whitelisted, so we have a way to kill a child without\n         * tirggering an error condition. */\n        if (bysignal != SIGUSR1)\n            server.aof_lastbgrewrite_status = C_ERR;\n\n        serverLog(LL_WARNING,\n            \"Background AOF rewrite terminated by signal %d\", bysignal);\n    }\n\ncleanup:\n    aofClosePipes();\n    aofRewriteBufferReset();\n    aofRemoveTempFile(server.aof_child_pid);\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    /* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */\n    if (server.aof_state == AOF_WAIT_REWRITE)\n        server.aof_rewrite_scheduled = 1;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\nconst char *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": "Chapter02/redis-6.0.3/src/atomicvar.h",
    "content": "/* This file implements atomic counters using __atomic or __sync macros if\n * available, otherwise synchronizing different threads using a mutex.\n *\n * The exported interface is composed of three macros:\n *\n * atomicIncr(var,count) -- Increment the atomic counter\n * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter\n * atomicDecr(var,count) -- Decrement the atomic counter\n * atomicGet(var,dstvar) -- Fetch the atomic counter value\n * atomicSet(var,value)  -- Set the atomic counter value\n *\n * The variable 'var' should also have a declared mutex with the same\n * name and the \"_mutex\" postfix, for instance:\n *\n *  long myvar;\n *  pthread_mutex_t myvar_mutex;\n *  atomicSet(myvar,12345);\n *\n * If atomic primitives are available (tested in config.h) the mutex\n * is not used.\n *\n * Never use return value from the macros, instead use the AtomicGetIncr()\n * if you need to get the current value and increment it atomically, like\n * in the followign example:\n *\n *  long oldvalue;\n *  atomicGetIncr(myvar,oldvalue,1);\n *  doSomethingWith(oldvalue);\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2015, 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 <pthread.h>\n\n#ifndef __ATOMIC_VAR_H\n#define __ATOMIC_VAR_H\n\n/* To test Redis with Helgrind (a Valgrind tool) it is useful to define\n * the following macro, so that __sync macros are used: those can be detected\n * by Helgrind (even if they are less efficient) so that no false positive\n * is reported. */\n// #define __ATOMIC_VAR_FORCE_SYNC_MACROS\n\n#if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)\n/* Implementation using __atomic macros. */\n\n#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \\\n} while(0)\n#define atomicDecr(var,count) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED)\n#define atomicGet(var,dstvar) do { \\\n    dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \\\n} while(0)\n#define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED)\n#define REDIS_ATOMIC_API \"atomic-builtin\"\n\n#elif defined(HAVE_ATOMIC)\n/* Implementation using __sync macros. */\n\n#define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    oldvalue_var = __sync_fetch_and_add(&var,(count)); \\\n} while(0)\n#define atomicDecr(var,count) __sync_sub_and_fetch(&var,(count))\n#define atomicGet(var,dstvar) do { \\\n    dstvar = __sync_sub_and_fetch(&var,0); \\\n} while(0)\n#define atomicSet(var,value) do { \\\n    while(!__sync_bool_compare_and_swap(&var,var,value)); \\\n} while(0)\n#define REDIS_ATOMIC_API \"sync-builtin\"\n\n#else\n/* Implementation using pthread mutex. */\n\n#define atomicIncr(var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var += (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    oldvalue_var = var; \\\n    var += (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicDecr(var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var -= (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicGet(var,dstvar) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    dstvar = var; \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicSet(var,value) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var = value; \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define REDIS_ATOMIC_API \"pthread-mutex\"\n\n#endif\n#endif /* __ATOMIC_VAR_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/bio.c",
    "content": "/* Background I/O service for Redis.\n *\n * This file implements operations that we need to perform in the background.\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 * 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 * 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 waits for new jobs in its queue, and process every job\n * sequentially.\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 * 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 * 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 \"server.h\"\n#include \"bio.h\"\n\nstatic pthread_t bio_threads[BIO_NUM_OPS];\nstatic pthread_mutex_t bio_mutex[BIO_NUM_OPS];\nstatic pthread_cond_t bio_newjob_cond[BIO_NUM_OPS];\nstatic pthread_cond_t bio_step_cond[BIO_NUM_OPS];\nstatic list *bio_jobs[BIO_NUM_OPS];\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. */\nstatic unsigned long long bio_pending[BIO_NUM_OPS];\n\n/* This structure represents a background Job. It is only used locally to this\n * file as the API does not expose the internals at all. */\nstruct bio_job {\n    time_t time; /* Time at which the job was created. */\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    void *arg1, *arg2, *arg3;\n};\n\nvoid *bioProcessBackgroundJobs(void *arg);\nvoid lazyfreeFreeObjectFromBioThread(robj *o);\nvoid lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2);\nvoid lazyfreeFreeSlotsMapFromBioThread(zskiplist *sl);\n\n/* Make sure we have enough stack to perform all the things we do in the\n * main thread. */\n#define REDIS_THREAD_STACK_SIZE (1024*1024*4)\n\n/* Initialize the background system, spawning the thread. */\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    for (j = 0; j < BIO_NUM_OPS; j++) {\n        pthread_mutex_init(&bio_mutex[j],NULL);\n        pthread_cond_init(&bio_newjob_cond[j],NULL);\n        pthread_cond_init(&bio_step_cond[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    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    for (j = 0; j < BIO_NUM_OPS; j++) {\n        void *arg = (void*)(unsigned long) j;\n        if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {\n            serverLog(LL_WARNING,\"Fatal: Can't initialize Background Jobs.\");\n            exit(1);\n        }\n        bio_threads[j] = thread;\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    pthread_mutex_lock(&bio_mutex[type]);\n    listAddNodeTail(bio_jobs[type],job);\n    bio_pending[type]++;\n    pthread_cond_signal(&bio_newjob_cond[type]);\n    pthread_mutex_unlock(&bio_mutex[type]);\n}\n\nvoid *bioProcessBackgroundJobs(void *arg) {\n    struct bio_job *job;\n    unsigned long type = (unsigned long) arg;\n    sigset_t sigset;\n\n    /* Check that the type is within the right interval. */\n    if (type >= BIO_NUM_OPS) {\n        serverLog(LL_WARNING,\n            \"Warning: bio thread started with wrong type %lu\",type);\n        return NULL;\n    }\n\n    switch (type) {\n    case BIO_CLOSE_FILE:\n        redis_set_thread_title(\"bio_close_file\");\n        break;\n    case BIO_AOF_FSYNC:\n        redis_set_thread_title(\"bio_aof_fsync\");\n        break;\n    case BIO_LAZY_FREE:\n        redis_set_thread_title(\"bio_lazy_free\");\n        break;\n    }\n\n    redisSetCpuAffinity(server.bio_cpulist);\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        serverLog(LL_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_newjob_cond[type],&bio_mutex[type]);\n            continue;\n        }\n        /* Pop the job from the queue. */\n        ln = listFirst(bio_jobs[type]);\n        job = ln->value;\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        if (type == BIO_CLOSE_FILE) {\n            close((long)job->arg1);\n        } else if (type == BIO_AOF_FSYNC) {\n            redis_fsync((long)job->arg1);\n        } else if (type == BIO_LAZY_FREE) {\n            /* What we free changes depending on what arguments are set:\n             * arg1 -> free the object at pointer.\n             * arg2 & arg3 -> free two dictionaries (a Redis DB).\n             * only arg3 -> free the skiplist. */\n            if (job->arg1)\n                lazyfreeFreeObjectFromBioThread(job->arg1);\n            else if (job->arg2 && job->arg3)\n                lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);\n            else if (job->arg3)\n                lazyfreeFreeSlotsMapFromBioThread(job->arg3);\n        } else {\n            serverPanic(\"Wrong job type in bioProcessBackgroundJobs().\");\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        listDelNode(bio_jobs[type],ln);\n        bio_pending[type]--;\n\n        /* Unblock threads blocked on bioWaitStepOfType() if any. */\n        pthread_cond_broadcast(&bio_step_cond[type]);\n    }\n}\n\n/* Return the number of pending jobs of the specified type. */\nunsigned long long bioPendingJobsOfType(int type) {\n    unsigned long long val;\n    pthread_mutex_lock(&bio_mutex[type]);\n    val = bio_pending[type];\n    pthread_mutex_unlock(&bio_mutex[type]);\n    return val;\n}\n\n/* If there are pending jobs for the specified type, the function blocks\n * and waits that the next job was processed. Otherwise the function\n * does not block and returns ASAP.\n *\n * The function returns the number of jobs still to process of the\n * requested type.\n *\n * This function is useful when from another thread, we want to wait\n * a bio.c thread to do more work in a blocking way.\n */\nunsigned long long bioWaitStepOfType(int type) {\n    unsigned long long val;\n    pthread_mutex_lock(&bio_mutex[type]);\n    val = bio_pending[type];\n    if (val != 0) {\n        pthread_cond_wait(&bio_step_cond[type],&bio_mutex[type]);\n        val = bio_pending[type];\n    }\n    pthread_mutex_unlock(&bio_mutex[type]);\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 * 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. */\nvoid bioKillThreads(void) {\n    int err, j;\n\n    for (j = 0; j < BIO_NUM_OPS; j++) {\n        if (bio_threads[j] && pthread_cancel(bio_threads[j]) == 0) {\n            if ((err = pthread_join(bio_threads[j],NULL)) != 0) {\n                serverLog(LL_WARNING,\n                    \"Bio thread for job type #%d can be joined: %s\",\n                        j, strerror(err));\n            } else {\n                serverLog(LL_WARNING,\n                    \"Bio thread for job type #%d terminated\",j);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#ifndef __BIO_H\n#define __BIO_H\n\n/* Exported API */\nvoid bioInit(void);\nvoid bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);\nunsigned long long bioPendingJobsOfType(int type);\nunsigned long long bioWaitStepOfType(int type);\ntime_t bioOlderJobOfType(int type);\nvoid bioKillThreads(void);\n\n/* Background job opcodes */\n#define BIO_CLOSE_FILE    0 /* Deferred close(2) syscall. */\n#define BIO_AOF_FSYNC     1 /* Deferred AOF fsync. */\n#define BIO_LAZY_FREE     2 /* Deferred objects freeing. */\n#define BIO_NUM_OPS       3\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n\n/* -----------------------------------------------------------------------------\n * Helpers and low level bit functions.\n * -------------------------------------------------------------------------- */\n\n/* Count number of bits set in the binary array pointed by 's' and long\n * 'count' bytes. The implementation of this function is required to\n * work with a input string length up to 512 MB. */\nsize_t redisPopcount(void *s, long count) {\n    size_t bits = 0;\n    unsigned char *p = s;\n    uint32_t *p4;\n    static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};\n\n    /* Count initial bytes not aligned to 32 bit. */\n    while((unsigned long)p & 3 && count) {\n        bits += bitsinbyte[*p++];\n        count--;\n    }\n\n    /* Count bits 28 bytes at a time */\n    p4 = (uint32_t*)p;\n    while(count>=28) {\n        uint32_t aux1, aux2, aux3, aux4, aux5, aux6, aux7;\n\n        aux1 = *p4++;\n        aux2 = *p4++;\n        aux3 = *p4++;\n        aux4 = *p4++;\n        aux5 = *p4++;\n        aux6 = *p4++;\n        aux7 = *p4++;\n        count -= 28;\n\n        aux1 = aux1 - ((aux1 >> 1) & 0x55555555);\n        aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333);\n        aux2 = aux2 - ((aux2 >> 1) & 0x55555555);\n        aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333);\n        aux3 = aux3 - ((aux3 >> 1) & 0x55555555);\n        aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333);\n        aux4 = aux4 - ((aux4 >> 1) & 0x55555555);\n        aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333);\n        aux5 = aux5 - ((aux5 >> 1) & 0x55555555);\n        aux5 = (aux5 & 0x33333333) + ((aux5 >> 2) & 0x33333333);\n        aux6 = aux6 - ((aux6 >> 1) & 0x55555555);\n        aux6 = (aux6 & 0x33333333) + ((aux6 >> 2) & 0x33333333);\n        aux7 = aux7 - ((aux7 >> 1) & 0x55555555);\n        aux7 = (aux7 & 0x33333333) + ((aux7 >> 2) & 0x33333333);\n        bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) +\n                    ((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) +\n                    ((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) +\n                    ((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) +\n                    ((aux5 + (aux5 >> 4)) & 0x0F0F0F0F) +\n                    ((aux6 + (aux6 >> 4)) & 0x0F0F0F0F) +\n                    ((aux7 + (aux7 >> 4)) & 0x0F0F0F0F))* 0x01010101) >> 24;\n    }\n    /* Count the remaining bytes. */\n    p = (unsigned char*)p4;\n    while(count--) bits += bitsinbyte[*p++];\n    return bits;\n}\n\n/* Return the position of the first bit set to one (if 'bit' is 1) or\n * zero (if 'bit' is 0) in the bitmap starting at 's' and long 'count' bytes.\n *\n * The function is guaranteed to return a value >= 0 if 'bit' is 0 since if\n * no zero bit is found, it returns count*8 assuming the string is zero\n * padded on the right. However if 'bit' is 1 it is possible that there is\n * not a single set bit in the bitmap. In this special case -1 is returned. */\nlong redisBitpos(void *s, unsigned long count, int bit) {\n    unsigned long *l;\n    unsigned char *c;\n    unsigned long skipval, word = 0, one;\n    long pos = 0; /* Position of bit, to return to the caller. */\n    unsigned long j;\n    int found;\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    found = 0;\n    while((unsigned long)c & (sizeof(*l)-1) && count) {\n        if (*c != skipval) {\n            found = 1;\n            break;\n        }\n        c++;\n        count--;\n        pos += 8;\n    }\n\n    /* Skip bits with full word step. */\n    l = (unsigned long*) c;\n    if (!found) {\n        skipval = bit ? 0 : ULONG_MAX;\n        while (count >= sizeof(*l)) {\n            if (*l != skipval) break;\n            l++;\n            count -= sizeof(*l);\n            pos += sizeof(*l)*8;\n        }\n    }\n\n    /* Load bytes into \"word\" considering the first byte as the most significant\n     * (we basically consider it as written in big endian, since we consider the\n     * string as a set of bits from left to right, with the first bit at position\n     * zero.\n     *\n     * Note that the loading is designed to work even when the bytes left\n     * (count) are less than a full word. We pad it with zero on the right. */\n    c = (unsigned char*)l;\n    for (j = 0; j < sizeof(*l); j++) {\n        word <<= 8;\n        if (count) {\n            word |= *c;\n            c++;\n            count--;\n        }\n    }\n\n    /* Special case:\n     * If bits in the string are all zero and we are looking for one,\n     * return -1 to signal that there is not a single \"1\" in the whole\n     * string. This can't happen when we are looking for \"0\" as we assume\n     * that the right of the string is zero padded. */\n    if (bit == 1 && word == 0) return -1;\n\n    /* Last word left, scan bit by bit. The first thing we need is to\n     * have a single \"1\" set in the most significant position in an\n     * unsigned long. We don't know the size of the long so we use a\n     * simple trick. */\n    one = ULONG_MAX; /* All bits set to 1.*/\n    one >>= 1;       /* All bits set to 1 but the MSB. */\n    one = ~one;      /* All bits set to 0 but the MSB. */\n\n    while(one) {\n        if (((one & word) != 0) == bit) return pos;\n        pos++;\n        one >>= 1;\n    }\n\n    /* If we reached this point, there is a bug in the algorithm, since\n     * the case of no match is handled as a special case before. */\n    serverPanic(\"End of redisBitpos() reached.\");\n    return 0; /* Just to avoid warnings. */\n}\n\n/* The following set.*Bitfield and get.*Bitfield functions implement setting\n * and getting arbitrary size (up to 64 bits) signed and unsigned integers\n * at arbitrary positions into a bitmap.\n *\n * The representation considers the bitmap as having the bit number 0 to be\n * the most significant bit of the first byte, and so forth, so for example\n * setting a 5 bits unsigned integer to value 23 at offset 7 into a bitmap\n * previously set to all zeroes, will produce the following representation:\n *\n * +--------+--------+\n * |00000001|01110000|\n * +--------+--------+\n *\n * When offsets and integer sizes are aligned to bytes boundaries, this is the\n * same as big endian, however when such alignment does not exist, its important\n * to also understand how the bits inside a byte are ordered.\n *\n * Note that this format follows the same convention as SETBIT and related\n * commands.\n */\n\nvoid setUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, uint64_t value) {\n    uint64_t byte, bit, byteval, bitval, j;\n\n    for (j = 0; j < bits; j++) {\n        bitval = (value & ((uint64_t)1<<(bits-1-j))) != 0;\n        byte = offset >> 3;\n        bit = 7 - (offset & 0x7);\n        byteval = p[byte];\n        byteval &= ~(1 << bit);\n        byteval |= bitval << bit;\n        p[byte] = byteval & 0xff;\n        offset++;\n    }\n}\n\nvoid setSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, int64_t value) {\n    uint64_t uv = value; /* Casting will add UINT64_MAX + 1 if v is negative. */\n    setUnsignedBitfield(p,offset,bits,uv);\n}\n\nuint64_t getUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {\n    uint64_t byte, bit, byteval, bitval, j, value = 0;\n\n    for (j = 0; j < bits; j++) {\n        byte = offset >> 3;\n        bit = 7 - (offset & 0x7);\n        byteval = p[byte];\n        bitval = (byteval >> bit) & 1;\n        value = (value<<1) | bitval;\n        offset++;\n    }\n    return value;\n}\n\nint64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {\n    int64_t value;\n    union {uint64_t u; int64_t i;} conv;\n\n    /* Converting from unsigned to signed is undefined when the value does\n     * not fit, however here we assume two's complement and the original value\n     * was obtained from signed -> unsigned conversion, so we'll find the\n     * most significant bit set if the original value was negative.\n     *\n     * Note that two's complement is mandatory for exact-width types\n     * according to the C99 standard. */\n    conv.u = getUnsignedBitfield(p,offset,bits);\n    value = conv.i;\n\n    /* If the top significant bit is 1, propagate it to all the\n     * higher bits for two's complement representation of signed\n     * integers. */\n    if (value & ((uint64_t)1 << (bits-1)))\n        value |= ((uint64_t)-1) << bits;\n    return value;\n}\n\n/* The following two functions detect overflow of a value in the context\n * of storing it as an unsigned or signed integer with the specified\n * number of bits. The functions both take the value and a possible increment.\n * If no overflow could happen and the value+increment fit inside the limits,\n * then zero is returned, otherwise in case of overflow, 1 is returned,\n * otherwise in case of underflow, -1 is returned.\n *\n * When non-zero is returned (overflow or underflow), if not NULL, *limit is\n * set to the value the operation should result when an overflow happens,\n * depending on the specified overflow semantics:\n *\n * For BFOVERFLOW_SAT if 1 is returned, *limit it is set maximum value that\n * you can store in that integer. when -1 is returned, *limit is set to the\n * minimum value that an integer of that size can represent.\n *\n * For BFOVERFLOW_WRAP *limit is set by performing the operation in order to\n * \"wrap\" around towards zero for unsigned integers, or towards the most\n * negative number that is possible to represent for signed integers. */\n\n#define BFOVERFLOW_WRAP 0\n#define BFOVERFLOW_SAT 1\n#define BFOVERFLOW_FAIL 2 /* Used by the BITFIELD command implementation. */\n\nint checkUnsignedBitfieldOverflow(uint64_t value, int64_t incr, uint64_t bits, int owtype, uint64_t *limit) {\n    uint64_t max = (bits == 64) ? UINT64_MAX : (((uint64_t)1<<bits)-1);\n    int64_t maxincr = max-value;\n    int64_t minincr = -value;\n\n    if (value > max || (incr > 0 && incr > maxincr)) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = max;\n            }\n        }\n        return 1;\n    } else if (incr < 0 && incr < minincr) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = 0;\n            }\n        }\n        return -1;\n    }\n    return 0;\n\nhandle_wrap:\n    {\n        uint64_t mask = ((uint64_t)-1) << bits;\n        uint64_t res = value+incr;\n\n        res &= ~mask;\n        *limit = res;\n    }\n    return 1;\n}\n\nint checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit) {\n    int64_t max = (bits == 64) ? INT64_MAX : (((int64_t)1<<(bits-1))-1);\n    int64_t min = (-max)-1;\n\n    /* Note that maxincr and minincr could overflow, but we use the values\n     * only after checking 'value' range, so when we use it no overflow\n     * happens. */\n    int64_t maxincr = max-value;\n    int64_t minincr = min-value;\n\n    if (value > max || (bits != 64 && incr > maxincr) || (value >= 0 && incr > 0 && incr > maxincr))\n    {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = max;\n            }\n        }\n        return 1;\n    } else if (value < min || (bits != 64 && incr < minincr) || (value < 0 && incr < 0 && incr < minincr)) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = min;\n            }\n        }\n        return -1;\n    }\n    return 0;\n\nhandle_wrap:\n    {\n        uint64_t mask = ((uint64_t)-1) << bits;\n        uint64_t msb = (uint64_t)1 << (bits-1);\n        uint64_t a = value, b = incr, c;\n        c = a+b; /* Perform addition as unsigned so that's defined. */\n\n        /* If the sign bit is set, propagate to all the higher order\n         * bits, to cap the negative value. If it's clear, mask to\n         * the positive integer limit. */\n        if (c & msb) {\n            c |= mask;\n        } else {\n            c &= ~mask;\n        }\n        *limit = c;\n    }\n    return 1;\n}\n\n/* Debugging function. Just show bits in the specified bitmap. Not used\n * but here for not having to rewrite it when debugging is needed. */\nvoid printBits(unsigned char *p, unsigned long count) {\n    unsigned long j, i, byte;\n\n    for (j = 0; j < count; j++) {\n        byte = p[j];\n        for (i = 0x80; i > 0; i /= 2)\n            printf(\"%c\", (byte & i) ? '1' : '0');\n        printf(\"|\");\n    }\n    printf(\"\\n\");\n}\n\n/* -----------------------------------------------------------------------------\n * Bits related string commands: GETBIT, SETBIT, BITCOUNT, BITOP.\n * -------------------------------------------------------------------------- */\n\n#define BITOP_AND   0\n#define BITOP_OR    1\n#define BITOP_XOR   2\n#define BITOP_NOT   3\n\n#define BITFIELDOP_GET 0\n#define BITFIELDOP_SET 1\n#define BITFIELDOP_INCRBY 2\n\n/* This helper function used by GETBIT / SETBIT parses the bit offset argument\n * making sure an error is returned if it is negative or if it overflows\n * Redis 512 MB limit for the string value.\n *\n * If the 'hash' argument is true, and 'bits is positive, then the command\n * will also parse bit offsets prefixed by \"#\". In such a case the offset\n * is multiplied by 'bits'. This is useful for the BITFIELD command. */\nint getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int bits) {\n    long long loffset;\n    char *err = \"bit offset is not an integer or out of range\";\n    char *p = o->ptr;\n    size_t plen = sdslen(p);\n    int usehash = 0;\n\n    /* Handle #<offset> form. */\n    if (p[0] == '#' && hash && bits > 0) usehash = 1;\n\n    if (string2ll(p+usehash,plen-usehash,&loffset) == 0) {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n\n    /* Adjust the offset by 'bits' for #<offset> form. */\n    if (usehash) loffset *= bits;\n\n    /* Limit offset to 512MB in bytes */\n    if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))\n    {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n\n    *offset = (size_t)loffset;\n    return C_OK;\n}\n\n/* This helper function for BITFIELD parses a bitfield type in the form\n * <sign><bits> where sign is 'u' or 'i' for unsigned and signed, and\n * the bits is a value between 1 and 64. However 64 bits unsigned integers\n * are reported as an error because of current limitations of Redis protocol\n * to return unsigned integer values greater than INT64_MAX.\n *\n * On error C_ERR is returned and an error is sent to the client. */\nint getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {\n    char *p = o->ptr;\n    char *err = \"Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\";\n    long long llbits;\n\n    if (p[0] == 'i') {\n        *sign = 1;\n    } else if (p[0] == 'u') {\n        *sign = 0;\n    } else {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n\n    if ((string2ll(p+1,strlen(p+1),&llbits)) == 0 ||\n        llbits < 1 ||\n        (*sign == 1 && llbits > 64) ||\n        (*sign == 0 && llbits > 63))\n    {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n    *bits = llbits;\n    return C_OK;\n}\n\n/* This is an helper function for commands implementations that need to write\n * bits to a string object. The command creates or pad with zeroes the string\n * so that the 'maxbit' bit can be addressed. The object is finally\n * returned. Otherwise if the key holds a wrong type NULL is returned and\n * an error is sent to the client. */\nrobj *lookupStringForBitCommand(client *c, size_t maxbit) {\n    size_t byte = maxbit >> 3;\n    robj *o = lookupKeyWrite(c->db,c->argv[1]);\n\n    if (o == NULL) {\n        o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        if (checkType(c,o,OBJ_STRING)) return NULL;\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n        o->ptr = sdsgrowzero(o->ptr,byte+1);\n    }\n    return o;\n}\n\n/* Return a pointer to the string object content, and stores its length\n * in 'len'. The user is required to pass (likely stack allocated) buffer\n * 'llbuf' of at least LONG_STR_SIZE bytes. Such a buffer is used in the case\n * the object is integer encoded in order to provide the representation\n * without usign heap allocation.\n *\n * The function returns the pointer to the object array of bytes representing\n * the string it contains, that may be a pointer to 'llbuf' or to the\n * internal object representation. As a side effect 'len' is filled with\n * the length of such buffer.\n *\n * If the source object is NULL the function is guaranteed to return NULL\n * and set 'len' to 0. */\nunsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) {\n    serverAssert(o->type == OBJ_STRING);\n    unsigned char *p = NULL;\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 && o->encoding == OBJ_ENCODING_INT) {\n        p = (unsigned char*) llbuf;\n        if (len) *len = ll2string(llbuf,LONG_STR_SIZE,(long)o->ptr);\n    } else if (o) {\n        p = (unsigned char*) o->ptr;\n        if (len) *len = sdslen(o->ptr);\n    } else {\n        if (len) *len = 0;\n    }\n    return p;\n}\n\n/* SETBIT key offset bitvalue */\nvoid setbitCommand(client *c) {\n    robj *o;\n    char *err = \"bit is not an integer or out of range\";\n    size_t bitoffset;\n    ssize_t byte, bit;\n    int byteval, bitval;\n    long on;\n\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)\n        return;\n\n    if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != C_OK)\n        return;\n\n    /* Bits can only be set or cleared... */\n    if (on & ~1) {\n        addReplyError(c,err);\n        return;\n    }\n\n    if ((o = lookupStringForBitCommand(c,bitoffset)) == NULL) return;\n\n    /* Get current values */\n    byte = bitoffset >> 3;\n    byteval = ((uint8_t*)o->ptr)[byte];\n    bit = 7 - (bitoffset & 0x7);\n    bitval = byteval & (1 << bit);\n\n    /* Update byte with new bit value and return original value */\n    byteval &= ~(1 << bit);\n    byteval |= ((on & 0x1) << bit);\n    ((uint8_t*)o->ptr)[byte] = byteval;\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c, bitval ? shared.cone : shared.czero);\n}\n\n/* GETBIT key offset */\nvoid getbitCommand(client *c) {\n    robj *o;\n    char llbuf[32];\n    size_t bitoffset;\n    size_t byte, bit;\n    size_t bitval = 0;\n\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)\n        return;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n\n    byte = bitoffset >> 3;\n    bit = 7 - (bitoffset & 0x7);\n    if (sdsEncodedObject(o)) {\n        if (byte < sdslen(o->ptr))\n            bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);\n    } else {\n        if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))\n            bitval = llbuf[byte] & (1 << bit);\n    }\n\n    addReply(c, bitval ? shared.cone : shared.czero);\n}\n\n/* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */\nvoid bitopCommand(client *c) {\n    char *opname = c->argv[1]->ptr;\n    robj *o, *targetkey = c->argv[2];\n    unsigned long op, j, numkeys;\n    robj **objects;      /* Array of source objects. */\n    unsigned char **src; /* Array of source strings pointers. */\n    unsigned long *len, maxlen = 0; /* Array of length of src strings,\n                                       and max len. */\n    unsigned long minlen = 0;    /* Min len among the input keys. */\n    unsigned char *res = NULL; /* Resulting string. */\n\n    /* Parse the operation name. */\n    if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,\"and\"))\n        op = BITOP_AND;\n    else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,\"or\"))\n        op = BITOP_OR;\n    else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,\"xor\"))\n        op = BITOP_XOR;\n    else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,\"not\"))\n        op = BITOP_NOT;\n    else {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Sanity check: NOT accepts only a single key argument. */\n    if (op == BITOP_NOT && c->argc != 4) {\n        addReplyError(c,\"BITOP NOT must be called with a single source key.\");\n        return;\n    }\n\n    /* Lookup keys, and store pointers to the string objects into an array. */\n    numkeys = c->argc - 3;\n    src = zmalloc(sizeof(unsigned char*) * numkeys);\n    len = zmalloc(sizeof(long) * numkeys);\n    objects = zmalloc(sizeof(robj*) * numkeys);\n    for (j = 0; j < numkeys; j++) {\n        o = lookupKeyRead(c->db,c->argv[j+3]);\n        /* Handle non-existing keys as empty strings. */\n        if (o == NULL) {\n            objects[j] = NULL;\n            src[j] = NULL;\n            len[j] = 0;\n            minlen = 0;\n            continue;\n        }\n        /* Return an error if one of the keys is not a string. */\n        if (checkType(c,o,OBJ_STRING)) {\n            unsigned long i;\n            for (i = 0; i < j; i++) {\n                if (objects[i])\n                    decrRefCount(objects[i]);\n            }\n            zfree(src);\n            zfree(len);\n            zfree(objects);\n            return;\n        }\n        objects[j] = getDecodedObject(o);\n        src[j] = objects[j]->ptr;\n        len[j] = sdslen(objects[j]->ptr);\n        if (len[j] > maxlen) maxlen = len[j];\n        if (j == 0 || len[j] < minlen) minlen = len[j];\n    }\n\n    /* Compute the bit operation, if at least one string is not empty. */\n    if (maxlen) {\n        res = (unsigned char*) sdsnewlen(NULL,maxlen);\n        unsigned char output, byte;\n        unsigned long i;\n\n        /* Fast path: as far as we have data for all the input bitmaps we\n         * can take a fast path that performs much better than the\n         * vanilla algorithm. On ARM we skip the fast path since it will\n         * result in GCC compiling the code using multiple-words load/store\n         * operations that are not supported even in ARM >= v6. */\n        j = 0;\n        #ifndef USE_ALIGNED_ACCESS\n        if (minlen >= sizeof(unsigned long)*4 && numkeys <= 16) {\n            unsigned long *lp[16];\n            unsigned long *lres = (unsigned long*) res;\n\n            /* Note: sds pointer is always aligned to 8 byte boundary. */\n            memcpy(lp,src,sizeof(unsigned long*)*numkeys);\n            memcpy(res,src[0],minlen);\n\n            /* Different branches per different operations for speed (sorry). */\n            if (op == BITOP_AND) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] &= lp[i][0];\n                        lres[1] &= lp[i][1];\n                        lres[2] &= lp[i][2];\n                        lres[3] &= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_OR) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] |= lp[i][0];\n                        lres[1] |= lp[i][1];\n                        lres[2] |= lp[i][2];\n                        lres[3] |= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_XOR) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] ^= lp[i][0];\n                        lres[1] ^= lp[i][1];\n                        lres[2] ^= lp[i][2];\n                        lres[3] ^= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_NOT) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    lres[0] = ~lres[0];\n                    lres[1] = ~lres[1];\n                    lres[2] = ~lres[2];\n                    lres[3] = ~lres[3];\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            }\n        }\n        #endif\n\n        /* j is set to the next byte to process by the previous loop. */\n        for (; j < maxlen; j++) {\n            output = (len[0] <= j) ? 0 : src[0][j];\n            if (op == BITOP_NOT) output = ~output;\n            for (i = 1; i < numkeys; i++) {\n                byte = (len[i] <= j) ? 0 : src[i][j];\n                switch(op) {\n                case BITOP_AND: output &= byte; break;\n                case BITOP_OR:  output |= byte; break;\n                case BITOP_XOR: output ^= byte; break;\n                }\n            }\n            res[j] = output;\n        }\n    }\n    for (j = 0; j < numkeys; j++) {\n        if (objects[j])\n            decrRefCount(objects[j]);\n    }\n    zfree(src);\n    zfree(len);\n    zfree(objects);\n\n    /* Store the computed value into the target key */\n    if (maxlen) {\n        o = createObject(OBJ_STRING,res);\n        setKey(c,c->db,targetkey,o);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",targetkey,c->db->id);\n        decrRefCount(o);\n    } else if (dbDelete(c->db,targetkey)) {\n        signalModifiedKey(c,c->db,targetkey);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",targetkey,c->db->id);\n    }\n    server.dirty++;\n    addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */\n}\n\n/* BITCOUNT key [start end] */\nvoid bitcountCommand(client *c) {\n    robj *o;\n    long start, end, strlen;\n    unsigned char *p;\n    char llbuf[LONG_STR_SIZE];\n\n    /* Lookup, check for type, and return 0 for non existing keys. */\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n    p = getObjectReadOnlyString(o,&strlen,llbuf);\n\n    /* Parse start/end range if any. */\n    if (c->argc == 4) {\n        if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)\n            return;\n        if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)\n            return;\n        /* Convert negative indexes */\n        if (start < 0 && end < 0 && start > end) {\n            addReply(c,shared.czero);\n            return;\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 (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        addReply(c,shared.czero);\n    } else {\n        long bytes = end-start+1;\n\n        addReplyLongLong(c,redisPopcount(p+start,bytes));\n    }\n}\n\n/* BITPOS key bit [start [end]] */\nvoid bitposCommand(client *c) {\n    robj *o;\n    long bit, start, end, strlen;\n    unsigned char *p;\n    char llbuf[LONG_STR_SIZE];\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) != C_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,OBJ_STRING)) return;\n    p = getObjectReadOnlyString(o,&strlen,llbuf);\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) != C_OK)\n            return;\n        if (c->argc == 5) {\n            if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != C_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\n/* BITFIELD key subcommmand-1 arg ... subcommand-2 arg ... subcommand-N ...\n *\n * Supported subcommands:\n *\n * GET <type> <offset>\n * SET <type> <offset> <value>\n * INCRBY <type> <offset> <increment>\n * OVERFLOW [WRAP|SAT|FAIL]\n */\n\n#define BITFIELD_FLAG_NONE      0\n#define BITFIELD_FLAG_READONLY  (1<<0)\n\nstruct bitfieldOp {\n    uint64_t offset;    /* Bitfield offset. */\n    int64_t i64;        /* Increment amount (INCRBY) or SET value */\n    int opcode;         /* Operation id. */\n    int owtype;         /* Overflow type to use. */\n    int bits;           /* Integer bitfield bits width. */\n    int sign;           /* True if signed, otherwise unsigned op. */\n};\n\n/* This implements both the BITFIELD command and the BITFIELD_RO command\n * when flags is set to BITFIELD_FLAG_READONLY: in this case only the\n * GET subcommand is allowed, other subcommands will return an error. */\nvoid bitfieldGeneric(client *c, int flags) {\n    robj *o;\n    size_t bitoffset;\n    int j, numops = 0, changes = 0;\n    struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */\n    int owtype = BFOVERFLOW_WRAP; /* Overflow type. */\n    int readonly = 1;\n    size_t highest_write_offset = 0;\n\n    for (j = 2; j < c->argc; j++) {\n        int remargs = c->argc-j-1; /* Remaining args other than current. */\n        char *subcmd = c->argv[j]->ptr; /* Current command name. */\n        int opcode; /* Current operation code. */\n        long long i64 = 0;  /* Signed SET value. */\n        int sign = 0; /* Signed or unsigned type? */\n        int bits = 0; /* Bitfield width in bits. */\n\n        if (!strcasecmp(subcmd,\"get\") && remargs >= 2)\n            opcode = BITFIELDOP_GET;\n        else if (!strcasecmp(subcmd,\"set\") && remargs >= 3)\n            opcode = BITFIELDOP_SET;\n        else if (!strcasecmp(subcmd,\"incrby\") && remargs >= 3)\n            opcode = BITFIELDOP_INCRBY;\n        else if (!strcasecmp(subcmd,\"overflow\") && remargs >= 1) {\n            char *owtypename = c->argv[j+1]->ptr;\n            j++;\n            if (!strcasecmp(owtypename,\"wrap\"))\n                owtype = BFOVERFLOW_WRAP;\n            else if (!strcasecmp(owtypename,\"sat\"))\n                owtype = BFOVERFLOW_SAT;\n            else if (!strcasecmp(owtypename,\"fail\"))\n                owtype = BFOVERFLOW_FAIL;\n            else {\n                addReplyError(c,\"Invalid OVERFLOW type specified\");\n                zfree(ops);\n                return;\n            }\n            continue;\n        } else {\n            addReply(c,shared.syntaxerr);\n            zfree(ops);\n            return;\n        }\n\n        /* Get the type and offset arguments, common to all the ops. */\n        if (getBitfieldTypeFromArgument(c,c->argv[j+1],&sign,&bits) != C_OK) {\n            zfree(ops);\n            return;\n        }\n\n        if (getBitOffsetFromArgument(c,c->argv[j+2],&bitoffset,1,bits) != C_OK){\n            zfree(ops);\n            return;\n        }\n\n        if (opcode != BITFIELDOP_GET) {\n            readonly = 0;\n            if (highest_write_offset < bitoffset + bits - 1)\n                highest_write_offset = bitoffset + bits - 1;\n            /* INCRBY and SET require another argument. */\n            if (getLongLongFromObjectOrReply(c,c->argv[j+3],&i64,NULL) != C_OK){\n                zfree(ops);\n                return;\n            }\n        }\n\n        /* Populate the array of operations we'll process. */\n        ops = zrealloc(ops,sizeof(*ops)*(numops+1));\n        ops[numops].offset = bitoffset;\n        ops[numops].i64 = i64;\n        ops[numops].opcode = opcode;\n        ops[numops].owtype = owtype;\n        ops[numops].bits = bits;\n        ops[numops].sign = sign;\n        numops++;\n\n        j += 3 - (opcode == BITFIELDOP_GET);\n    }\n\n    if (readonly) {\n        /* Lookup for read is ok if key doesn't exit, but errors\n         * if it's not a string. */\n        o = lookupKeyRead(c->db,c->argv[1]);\n        if (o != NULL && checkType(c,o,OBJ_STRING)) {\n            zfree(ops);\n            return;\n        }\n    } else {\n        if (flags & BITFIELD_FLAG_READONLY) {\n            zfree(ops);\n            addReplyError(c, \"BITFIELD_RO only supports the GET subcommand\");\n            return;\n        }\n\n        /* Lookup by making room up to the farest bit reached by\n         * this operation. */\n        if ((o = lookupStringForBitCommand(c,\n            highest_write_offset)) == NULL) {\n            zfree(ops);\n            return;\n        }\n    }\n\n    addReplyArrayLen(c,numops);\n\n    /* Actually process the operations. */\n    for (j = 0; j < numops; j++) {\n        struct bitfieldOp *thisop = ops+j;\n\n        /* Execute the operation. */\n        if (thisop->opcode == BITFIELDOP_SET ||\n            thisop->opcode == BITFIELDOP_INCRBY)\n        {\n            /* SET and INCRBY: We handle both with the same code path\n             * for simplicity. SET return value is the previous value so\n             * we need fetch & store as well. */\n\n            /* We need two different but very similar code paths for signed\n             * and unsigned operations, since the set of functions to get/set\n             * the integers and the used variables types are different. */\n            if (thisop->sign) {\n                int64_t oldval, newval, wrapped, retval;\n                int overflow;\n\n                oldval = getSignedBitfield(o->ptr,thisop->offset,\n                        thisop->bits);\n\n                if (thisop->opcode == BITFIELDOP_INCRBY) {\n                    newval = oldval + thisop->i64;\n                    overflow = checkSignedBitfieldOverflow(oldval,\n                            thisop->i64,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = newval;\n                } else {\n                    newval = thisop->i64;\n                    overflow = checkSignedBitfieldOverflow(newval,\n                            0,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = oldval;\n                }\n\n                /* On overflow of type is \"FAIL\", don't write and return\n                 * NULL to signal the condition. */\n                if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {\n                    addReplyLongLong(c,retval);\n                    setSignedBitfield(o->ptr,thisop->offset,\n                                      thisop->bits,newval);\n                } else {\n                    addReplyNull(c);\n                }\n            } else {\n                uint64_t oldval, newval, wrapped, retval;\n                int overflow;\n\n                oldval = getUnsignedBitfield(o->ptr,thisop->offset,\n                        thisop->bits);\n\n                if (thisop->opcode == BITFIELDOP_INCRBY) {\n                    newval = oldval + thisop->i64;\n                    overflow = checkUnsignedBitfieldOverflow(oldval,\n                            thisop->i64,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = newval;\n                } else {\n                    newval = thisop->i64;\n                    overflow = checkUnsignedBitfieldOverflow(newval,\n                            0,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = oldval;\n                }\n                /* On overflow of type is \"FAIL\", don't write and return\n                 * NULL to signal the condition. */\n                if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {\n                    addReplyLongLong(c,retval);\n                    setUnsignedBitfield(o->ptr,thisop->offset,\n                                        thisop->bits,newval);\n                } else {\n                    addReplyNull(c);\n                }\n            }\n            changes++;\n        } else {\n            /* GET */\n            unsigned char buf[9];\n            long strlen = 0;\n            unsigned char *src = NULL;\n            char llbuf[LONG_STR_SIZE];\n\n            if (o != NULL)\n                src = getObjectReadOnlyString(o,&strlen,llbuf);\n\n            /* For GET we use a trick: before executing the operation\n             * copy up to 9 bytes to a local buffer, so that we can easily\n             * execute up to 64 bit operations that are at actual string\n             * object boundaries. */\n            memset(buf,0,9);\n            int i;\n            size_t byte = thisop->offset >> 3;\n            for (i = 0; i < 9; i++) {\n                if (src == NULL || i+byte >= (size_t)strlen) break;\n                buf[i] = src[i+byte];\n            }\n\n            /* Now operate on the copied buffer which is guaranteed\n             * to be zero-padded. */\n            if (thisop->sign) {\n                int64_t val = getSignedBitfield(buf,thisop->offset-(byte*8),\n                                            thisop->bits);\n                addReplyLongLong(c,val);\n            } else {\n                uint64_t val = getUnsignedBitfield(buf,thisop->offset-(byte*8),\n                                            thisop->bits);\n                addReplyLongLong(c,val);\n            }\n        }\n    }\n\n    if (changes) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n        server.dirty += changes;\n    }\n    zfree(ops);\n}\n\nvoid bitfieldCommand(client *c) {\n    bitfieldGeneric(c, BITFIELD_FLAG_NONE);\n}\n\nvoid bitfieldroCommand(client *c) {\n    bitfieldGeneric(c, BITFIELD_FLAG_READONLY);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 * blockClient() set the CLIENT_BLOCKED flag in the client, and set the\n * specified block type 'btype' filed to one of 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 CLIENT_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 CLIENT_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 * If the blocking operation waits for certain keys to change state, the\n * clusterRedirectBlockedClientIfNeeded() function should also be updated.\n */\n\n#include \"server.h\"\n\nint serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);\n\n/* This structure represents the blocked key information that we store\n * in the client structure. Each client blocked on keys, has a\n * client->bpop.keys hash table. The keys of the hash table are Redis\n * keys pointers to 'robj' structures. The value is this structure.\n * The structure has two goals: firstly we store the list node that this\n * client uses to be listed in the database \"blocked clients for this key\"\n * list, so we can later unblock in O(1) without a list scan.\n * Secondly for certain blocking types, we have additional info. Right now\n * the only use for additional info we have is when clients are blocked\n * on streams, as we have to remember the ID it blocked for. */\ntypedef struct bkinfo {\n    listNode *listnode;     /* List node for db->blocking_keys[key] list. */\n    streamID stream_id;     /* Stream ID if we blocked in a stream. */\n} bkinfo;\n\n/* Block a client for the specific operation type. Once the CLIENT_BLOCKED\n * flag is set client query buffer is not longer processed, but accumulated,\n * and will be processed when the client is unblocked. */\nvoid blockClient(client *c, int btype) {\n    c->flags |= CLIENT_BLOCKED;\n    c->btype = btype;\n    server.blocked_clients++;\n    server.blocked_clients_by_type[btype]++;\n    addClientToTimeoutTable(c);\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. */\nvoid processUnblockedClients(void) {\n    listNode *ln;\n    client *c;\n\n    while (listLength(server.unblocked_clients)) {\n        ln = listFirst(server.unblocked_clients);\n        serverAssert(ln != NULL);\n        c = ln->value;\n        listDelNode(server.unblocked_clients,ln);\n        c->flags &= ~CLIENT_UNBLOCKED;\n\n        /* Process remaining data in the input buffer, unless the client\n         * is blocked again. Actually processInputBuffer() checks that the\n         * client is not blocked before to proceed, but things may change and\n         * the code is conceptually more correct this way. */\n        if (!(c->flags & CLIENT_BLOCKED)) {\n            if (c->querybuf && sdslen(c->querybuf) > 0) {\n                processInputBuffer(c);\n            }\n        }\n    }\n}\n\n/* This function will schedule the client for reprocessing at a safe time.\n *\n * This is useful when a client was blocked for some reason (blocking opeation,\n * CLIENT PAUSE, or whatever), because it may end with some accumulated query\n * buffer that needs to be processed ASAP:\n *\n * 1. When a client is blocked, its readable handler is still active.\n * 2. However in this case it only gets data into the query buffer, but the\n *    query is not parsed or executed once there is enough to proceed as\n *    usually (because the client is blocked... so we can't execute commands).\n * 3. When the client is unblocked, without this function, the client would\n *    have to write some query in order for the readable handler to finally\n *    call processQueryBuffer*() on it.\n * 4. With this function instead we can put the client in a queue that will\n *    process it for queries ready to be executed at a safe time.\n */\nvoid queueClientForReprocessing(client *c) {\n    /* The client may already be into the unblocked list because of a previous\n     * blocking operation, don't add back it into the list multiple times. */\n    if (!(c->flags & CLIENT_UNBLOCKED)) {\n        c->flags |= CLIENT_UNBLOCKED;\n        listAddNodeTail(server.unblocked_clients,c);\n    }\n}\n\n/* Unblock a client calling the right function depending on the kind\n * of operation the client is blocking for. */\nvoid unblockClient(client *c) {\n    if (c->btype == BLOCKED_LIST ||\n        c->btype == BLOCKED_ZSET ||\n        c->btype == BLOCKED_STREAM) {\n        unblockClientWaitingData(c);\n    } else if (c->btype == BLOCKED_WAIT) {\n        unblockClientWaitingReplicas(c);\n    } else if (c->btype == BLOCKED_MODULE) {\n        if (moduleClientIsBlockedOnKeys(c)) unblockClientWaitingData(c);\n        unblockClientFromModule(c);\n    } else {\n        serverPanic(\"Unknown btype in unblockClient().\");\n    }\n    /* Clear the flags, and put the client in the unblocked list so that\n     * we'll process new commands in its query buffer ASAP. */\n    server.blocked_clients--;\n    server.blocked_clients_by_type[c->btype]--;\n    c->flags &= ~CLIENT_BLOCKED;\n    c->btype = BLOCKED_NONE;\n    removeClientFromTimeoutTable(c);\n    queueClientForReprocessing(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. After this function is called,\n * unblockClient() will be called with the same client as argument. */\nvoid replyToBlockedClientTimedOut(client *c) {\n    if (c->btype == BLOCKED_LIST ||\n        c->btype == BLOCKED_ZSET ||\n        c->btype == BLOCKED_STREAM) {\n        addReplyNullArray(c);\n    } else if (c->btype == BLOCKED_WAIT) {\n        addReplyLongLong(c,replicationCountAcksByOffset(c->bpop.reploffset));\n    } else if (c->btype == BLOCKED_MODULE) {\n        moduleBlockedClientTimedOut(c);\n    } else {\n        serverPanic(\"Unknown btype in replyToBlockedClientTimedOut().\");\n    }\n}\n\n/* Mass-unblock clients because something changed in the instance that makes\n * blocking no longer safe. For example clients blocked in list operations\n * in an instance which turns from master to slave is unsafe, so this function\n * is called when a master turns into a slave.\n *\n * The semantics is to send an -UNBLOCKED error to the client, disconnecting\n * it at the same time. */\nvoid disconnectAllBlockedClients(void) {\n    listNode *ln;\n    listIter li;\n\n    listRewind(server.clients,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n\n        if (c->flags & CLIENT_BLOCKED) {\n            addReplySds(c,sdsnew(\n                \"-UNBLOCKED force unblock from blocking operation, \"\n                \"instance state changed (master -> replica?)\\r\\n\"));\n            unblockClient(c);\n            c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n        }\n    }\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * when there may be clients blocked on a list key, and there may be new\n * data to fetch (the key is ready). */\nvoid serveClientsBlockedOnListKey(robj *o, readyList *rl) {\n    /* We serve clients in the same order they blocked for\n     * this key, from the first blocked to the last. */\n    dictEntry *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            listNode *clientnode = listFirst(clients);\n            client *receiver = clientnode->value;\n\n            if (receiver->btype != BLOCKED_LIST) {\n                /* Put at the tail, so that at the next call\n                 * we'll not run into it again. */\n                listRotateHeadToTail(clients);\n                continue;\n            }\n\n            robj *dstkey = receiver->bpop.target;\n            int where = (receiver->lastcmd &&\n                         receiver->lastcmd->proc == blpopCommand) ?\n                         LIST_HEAD : LIST_TAIL;\n            robj *value = listTypePop(o,where);\n\n            if (value) {\n                /* Protect receiver->bpop.target, that will be\n                 * freed by the next unblockClient()\n                 * call. */\n                if (dstkey) incrRefCount(dstkey);\n                unblockClient(receiver);\n\n                if (serveClientBlockedOnList(receiver,\n                    rl->key,dstkey,rl->db,value,\n                    where) == C_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                break;\n            }\n        }\n    }\n\n    if (listTypeLength(o) == 0) {\n        dbDelete(rl->db,rl->key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",rl->key,rl->db->id);\n    }\n    /* We don't call signalModifiedKey() as it was already called\n     * when an element was pushed on the list. */\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * when there may be clients blocked on a sorted set key, and there may be new\n * data to fetch (the key is ready). */\nvoid serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {\n    /* We serve clients in the same order they blocked for\n     * this key, from the first blocked to the last. */\n    dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);\n    if (de) {\n        list *clients = dictGetVal(de);\n        int numclients = listLength(clients);\n        unsigned long zcard = zsetLength(o);\n\n        while(numclients-- && zcard) {\n            listNode *clientnode = listFirst(clients);\n            client *receiver = clientnode->value;\n\n            if (receiver->btype != BLOCKED_ZSET) {\n                /* Put at the tail, so that at the next call\n                 * we'll not run into it again. */\n                listRotateHeadToTail(clients);\n                continue;\n            }\n\n            int where = (receiver->lastcmd &&\n                         receiver->lastcmd->proc == bzpopminCommand)\n                         ? ZSET_MIN : ZSET_MAX;\n            unblockClient(receiver);\n            genericZpopCommand(receiver,&rl->key,1,where,1,NULL);\n            zcard--;\n\n            /* Replicate the command. */\n            robj *argv[2];\n            struct redisCommand *cmd = where == ZSET_MIN ?\n                                       server.zpopminCommand :\n                                       server.zpopmaxCommand;\n            argv[0] = createStringObject(cmd->name,strlen(cmd->name));\n            argv[1] = rl->key;\n            incrRefCount(rl->key);\n            propagate(cmd,receiver->db->id,\n                      argv,2,PROPAGATE_AOF|PROPAGATE_REPL);\n            decrRefCount(argv[0]);\n            decrRefCount(argv[1]);\n        }\n    }\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * when there may be clients blocked on a stream key, and there may be new\n * data to fetch (the key is ready). */\nvoid serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {\n    dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);\n    stream *s = o->ptr;\n\n    /* We need to provide the new data arrived on the stream\n     * to all the clients that are waiting for an offset smaller\n     * than the current top item. */\n    if (de) {\n        list *clients = dictGetVal(de);\n        listNode *ln;\n        listIter li;\n        listRewind(clients,&li);\n\n        while((ln = listNext(&li))) {\n            client *receiver = listNodeValue(ln);\n            if (receiver->btype != BLOCKED_STREAM) continue;\n            bkinfo *bki = dictFetchValue(receiver->bpop.keys,rl->key);\n            streamID *gt = &bki->stream_id;\n\n            /* If we blocked in the context of a consumer\n             * group, we need to resolve the group and update the\n             * last ID the client is blocked for: this is needed\n             * because serving other clients in the same consumer\n             * group will alter the \"last ID\" of the consumer\n             * group, and clients blocked in a consumer group are\n             * always blocked for the \">\" ID: we need to deliver\n             * only new messages and avoid unblocking the client\n             * otherwise. */\n            streamCG *group = NULL;\n            if (receiver->bpop.xread_group) {\n                group = streamLookupCG(s,\n                        receiver->bpop.xread_group->ptr);\n                /* If the group was not found, send an error\n                 * to the consumer. */\n                if (!group) {\n                    addReplyError(receiver,\n                        \"-NOGROUP the consumer group this client \"\n                        \"was blocked on no longer exists\");\n                    unblockClient(receiver);\n                    continue;\n                } else {\n                    *gt = group->last_id;\n                }\n            }\n\n            if (streamCompareID(&s->last_id, gt) > 0) {\n                streamID start = *gt;\n                streamIncrID(&start);\n\n                /* Lookup the consumer for the group, if any. */\n                streamConsumer *consumer = NULL;\n                int noack = 0;\n\n                if (group) {\n                    consumer =\n                        streamLookupConsumer(group,\n                                             receiver->bpop.xread_consumer->ptr,\n                                             SLC_NONE);\n                    noack = receiver->bpop.xread_group_noack;\n                }\n\n                /* Emit the two elements sub-array consisting of\n                 * the name of the stream and the data we\n                 * extracted from it. Wrapped in a single-item\n                 * array, since we have just one key. */\n                if (receiver->resp == 2) {\n                    addReplyArrayLen(receiver,1);\n                    addReplyArrayLen(receiver,2);\n                } else {\n                    addReplyMapLen(receiver,1);\n                }\n                addReplyBulk(receiver,rl->key);\n\n                streamPropInfo pi = {\n                    rl->key,\n                    receiver->bpop.xread_group\n                };\n                streamReplyWithRange(receiver,s,&start,NULL,\n                                     receiver->bpop.xread_count,\n                                     0, group, consumer, noack, &pi);\n\n                /* Note that after we unblock the client, 'gt'\n                 * and other receiver->bpop stuff are no longer\n                 * valid, so we must do the setup above before\n                 * this call. */\n                unblockClient(receiver);\n            }\n        }\n    }\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * in order to check if we can serve clients blocked by modules using\n * RM_BlockClientOnKeys(), when the corresponding key was signaled as ready:\n * our goal here is to call the RedisModuleBlockedClient reply() callback to\n * see if the key is really able to serve the client, and in that case,\n * unblock it. */\nvoid serveClientsBlockedOnKeyByModule(readyList *rl) {\n    dictEntry *de;\n\n    /* We serve clients in the same order they blocked for\n     * this key, from the first blocked to the last. */\n    de = dictFind(rl->db->blocking_keys,rl->key);\n    if (de) {\n        list *clients = dictGetVal(de);\n        int numclients = listLength(clients);\n\n        while(numclients--) {\n            listNode *clientnode = listFirst(clients);\n            client *receiver = clientnode->value;\n\n            /* Put at the tail, so that at the next call\n             * we'll not run into it again: clients here may not be\n             * ready to be served, so they'll remain in the list\n             * sometimes. We want also be able to skip clients that are\n             * not blocked for the MODULE type safely. */\n            listRotateHeadToTail(clients);\n\n            if (receiver->btype != BLOCKED_MODULE) continue;\n\n            /* Note that if *this* client cannot be served by this key,\n             * it does not mean that another client that is next into the\n             * list cannot be served as well: they may be blocked by\n             * different modules with different triggers to consider if a key\n             * is ready or not. This means we can't exit the loop but need\n             * to continue after the first failure. */\n            if (!moduleTryServeClientBlockedOnKey(receiver, rl->key)) continue;\n\n            moduleUnblockClient(receiver);\n        }\n    }\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. It handles serving clients blocked in\n * lists, streams, and sorted sets, via a blocking commands.\n *\n * All the keys with at least one client blocked that received at least\n * one new element via some write 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 * This function is normally \"fair\", that is, it will server clients\n * using a FIFO behavior. However this fairness is violated in certain\n * edge cases, that is, when we have clients blocked at the same time\n * in a sorted set and in a list, for the same key (a very odd thing to\n * do client side, indeed!). Because mismatching clients (blocking for\n * a different type compared to the current key type) are moved in the\n * other side of the linked list. However as long as the key starts to\n * be used only for a single type, like virtually any Redis application will\n * do, the function is already fair. */\nvoid handleClientsBlockedOnKeys(void) {\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         * signalKeyAsReady() that may push new elements in server.ready_keys\n         * when handling clients blocked into BRPOPLPUSH. */\n        l = server.ready_keys;\n        server.ready_keys = listCreate();\n\n        while(listLength(l) != 0) {\n            listNode *ln = listFirst(l);\n            readyList *rl = ln->value;\n\n            /* First of all remove this key from db->ready_keys so that\n             * we can safely call signalKeyAsReady() against this key. */\n            dictDelete(rl->db->ready_keys,rl->key);\n\n            /* Even if we are not inside call(), increment the call depth\n             * in order to make sure that keys are expired against a fixed\n             * reference time, and not against the wallclock time. This\n             * way we can lookup an object multiple times (BRPOPLPUSH does\n             * that) without the risk of it being freed in the second\n             * lookup, invalidating the first one.\n             * See https://github.com/antirez/redis/pull/6554. */\n            server.fixed_time_expire++;\n            updateCachedTime(0);\n\n            /* Serve clients blocked on list key. */\n            robj *o = lookupKeyWrite(rl->db,rl->key);\n\n            if (o != NULL) {\n                if (o->type == OBJ_LIST)\n                    serveClientsBlockedOnListKey(o,rl);\n                else if (o->type == OBJ_ZSET)\n                    serveClientsBlockedOnSortedSetKey(o,rl);\n                else if (o->type == OBJ_STREAM)\n                    serveClientsBlockedOnStreamKey(o,rl);\n                /* We want to serve clients blocked on module keys\n                 * regardless of the object type: we don't know what the\n                 * module is trying to accomplish right now. */\n                serveClientsBlockedOnKeyByModule(rl);\n            }\n            server.fixed_time_expire--;\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/* This is how the current blocking lists/sorted sets/streams work, we use\n * BLPOP as example, but the concept is the same for other list ops, sorted\n * sets and XREAD.\n * - If the user calls BLPOP and the key exists and contains a non empty list\n *   then LPOP is called instead. So BLPOP is semantically the same as LPOP\n *   if blocking is not required.\n * - If instead BLPOP is called and the key does not exists or the list is\n *   empty we need to block. In order to do so we remove the notification for\n *   new data to read in the client socket (so that we'll not serve new\n *   requests if the blocking request is not served). Also we put the client\n *   in a dictionary (db->blocking_keys) mapping keys to a list of clients\n *   blocking for this keys.\n * - If a PUSH operation against a key with blocked clients waiting is\n *   performed, we mark this key as \"ready\", and after the current command,\n *   MULTI/EXEC block, or script, is executed, we serve all the clients waiting\n *   for this list, from the one that blocked first, to the last, accordingly\n *   to the number of elements we have in the ready list.\n */\n\n/* Set a client in blocking mode for the specified key (list, zset or stream),\n * with the specified timeout. The 'type' argument is BLOCKED_LIST,\n * BLOCKED_ZSET or BLOCKED_STREAM depending on the kind of operation we are\n * waiting for an empty key in order to awake the client. The client is blocked\n * for all the 'numkeys' keys as in the 'keys' argument. When we block for\n * stream keys, we also provide an array of streamID structures: clients will\n * be unblocked only when items with an ID greater or equal to the specified\n * one is appended to the stream. */\nvoid blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids) {\n    dictEntry *de;\n    list *l;\n    int j;\n\n    c->bpop.timeout = timeout;\n    c->bpop.target = target;\n\n    if (target != NULL) incrRefCount(target);\n\n    for (j = 0; j < numkeys; j++) {\n        /* Allocate our bkinfo structure, associated to each key the client\n         * is blocked for. */\n        bkinfo *bki = zmalloc(sizeof(*bki));\n        if (btype == BLOCKED_STREAM)\n            bki->stream_id = ids[j];\n\n        /* If the key already exists in the dictionary ignore it. */\n        if (dictAdd(c->bpop.keys,keys[j],bki) != DICT_OK) {\n            zfree(bki);\n            continue;\n        }\n        incrRefCount(keys[j]);\n\n        /* And in the other \"side\", to map keys -> clients */\n        de = dictFind(c->db->blocking_keys,keys[j]);\n        if (de == NULL) {\n            int retval;\n\n            /* For every key we take a list of clients blocked for it */\n            l = listCreate();\n            retval = dictAdd(c->db->blocking_keys,keys[j],l);\n            incrRefCount(keys[j]);\n            serverAssertWithInfo(c,keys[j],retval == DICT_OK);\n        } else {\n            l = dictGetVal(de);\n        }\n        listAddNodeTail(l,c);\n        bki->listnode = listLast(l);\n    }\n    blockClient(c,btype);\n}\n\n/* Unblock a client that's waiting in a blocking operation such as BLPOP.\n * You should never call this function directly, but unblockClient() instead. */\nvoid unblockClientWaitingData(client *c) {\n    dictEntry *de;\n    dictIterator *di;\n    list *l;\n\n    serverAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0);\n    di = dictGetIterator(c->bpop.keys);\n    /* The client may wait for multiple keys, so unblock it for every key. */\n    while((de = dictNext(di)) != NULL) {\n        robj *key = dictGetKey(de);\n        bkinfo *bki = dictGetVal(de);\n\n        /* Remove this client from the list of clients waiting for this key. */\n        l = dictFetchValue(c->db->blocking_keys,key);\n        serverAssertWithInfo(c,key,l != NULL);\n        listDelNode(l,bki->listnode);\n        /* If the list is empty we need to remove it to avoid wasting memory */\n        if (listLength(l) == 0)\n            dictDelete(c->db->blocking_keys,key);\n    }\n    dictReleaseIterator(di);\n\n    /* Cleanup the client structure */\n    dictEmpty(c->bpop.keys,NULL);\n    if (c->bpop.target) {\n        decrRefCount(c->bpop.target);\n        c->bpop.target = NULL;\n    }\n    if (c->bpop.xread_group) {\n        decrRefCount(c->bpop.xread_group);\n        decrRefCount(c->bpop.xread_consumer);\n        c->bpop.xread_group = NULL;\n        c->bpop.xread_consumer = NULL;\n    }\n}\n\n/* If the specified key has clients blocked waiting for list pushes, this\n * function will put the key reference into the server.ready_keys list.\n * Note that db->ready_keys is a hash table that allows us to avoid putting\n * the same key again and again in the list in case of multiple pushes\n * made by a script or in the context of MULTI/EXEC.\n *\n * The list will be finally processed by handleClientsBlockedOnKeys() */\nvoid signalKeyAsReady(redisDb *db, robj *key) {\n    readyList *rl;\n\n    /* No clients blocking for this key? No need to queue it. */\n    if (dictFind(db->blocking_keys,key) == NULL) return;\n\n    /* Key was already signaled? No need to queue it again. */\n    if (dictFind(db->ready_keys,key) != NULL) return;\n\n    /* Ok, we need to queue this key into server.ready_keys. */\n    rl = zmalloc(sizeof(*rl));\n    rl->key = key;\n    rl->db = 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    incrRefCount(key);\n    serverAssert(dictAdd(db->ready_keys,key,NULL) == DICT_OK);\n}\n\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/childinfo.c",
    "content": "/*\n * Copyright (c) 2016, 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 \"server.h\"\n#include <unistd.h>\n\n/* Open a child-parent channel used in order to move information about the\n * RDB / AOF saving process from the child to the parent (for instance\n * the amount of copy on write memory used) */\nvoid openChildInfoPipe(void) {\n    if (pipe(server.child_info_pipe) == -1) {\n        /* On error our two file descriptors should be still set to -1,\n         * but we call anyway cloesChildInfoPipe() since can't hurt. */\n        closeChildInfoPipe();\n    } else if (anetNonBlock(NULL,server.child_info_pipe[0]) != ANET_OK) {\n        closeChildInfoPipe();\n    } else {\n        memset(&server.child_info_data,0,sizeof(server.child_info_data));\n    }\n}\n\n/* Close the pipes opened with openChildInfoPipe(). */\nvoid closeChildInfoPipe(void) {\n    if (server.child_info_pipe[0] != -1 ||\n        server.child_info_pipe[1] != -1)\n    {\n        close(server.child_info_pipe[0]);\n        close(server.child_info_pipe[1]);\n        server.child_info_pipe[0] = -1;\n        server.child_info_pipe[1] = -1;\n    }\n}\n\n/* Send COW data to parent. The child should call this function after populating\n * the corresponding fields it want to sent (according to the process type). */\nvoid sendChildInfo(int ptype) {\n    if (server.child_info_pipe[1] == -1) return;\n    server.child_info_data.magic = CHILD_INFO_MAGIC;\n    server.child_info_data.process_type = ptype;\n    ssize_t wlen = sizeof(server.child_info_data);\n    if (write(server.child_info_pipe[1],&server.child_info_data,wlen) != wlen) {\n        /* Nothing to do on error, this will be detected by the other side. */\n    }\n}\n\n/* Receive COW data from parent. */\nvoid receiveChildInfo(void) {\n    if (server.child_info_pipe[0] == -1) return;\n    ssize_t wlen = sizeof(server.child_info_data);\n    if (read(server.child_info_pipe[0],&server.child_info_data,wlen) == wlen &&\n        server.child_info_data.magic == CHILD_INFO_MAGIC)\n    {\n        if (server.child_info_data.process_type == CHILD_INFO_TYPE_RDB) {\n            server.stat_rdb_cow_bytes = server.child_info_data.cow_size;\n        } else if (server.child_info_data.process_type == CHILD_INFO_TYPE_AOF) {\n            server.stat_aof_cow_bytes = server.child_info_data.cow_size;\n        } else if (server.child_info_data.process_type == CHILD_INFO_TYPE_MODULE) {\n            server.stat_module_cow_bytes = server.child_info_data.cow_size;\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.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/stat.h>\n#include <sys/file.h>\n#include <math.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. */\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(connection *conn);\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(const 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);\nsds representClusterNodeFlags(sds ci, uint16_t flags);\nuint64_t clusterGetMaxEpoch(void);\nint clusterBumpConfigEpochWithoutConsensus(void);\nvoid moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len);\n\n/* -----------------------------------------------------------------------------\n * Initialization\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), C_ERR is returned.\n * If the configuration was loaded from the file, C_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 C_ERR;\n        } else {\n            serverLog(LL_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 C_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 C_ERR;\n    }\n\n    /* Parse the file. Note that single lines of the cluster config file can\n     * be really long as they include all the hash slots of the node.\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     * To simplify we allocate 1024+CLUSTER_SLOTS*128 bytes per line. */\n    maxline = 1024+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' || line[0] == '\\0') 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            if (!(argc % 2)) goto fmterr;\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                    serverLog(LL_WARNING,\n                        \"Skipping unknown cluster config variable '%s'\",\n                        argv[j]);\n                }\n            }\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* Regular config lines have at least eight fields */\n        if (argc < 8) {\n            sdsfreesplitres(argv,argc);\n            goto fmterr;\n        }\n\n        /* Create this node if it does not exist */\n        n = clusterLookupNode(argv[0]);\n        if (!n) {\n            n = createClusterNode(argv[0],0);\n            clusterAddNode(n);\n        }\n        /* Address and port */\n        if ((p = strrchr(argv[1],':')) == NULL) {\n            sdsfreesplitres(argv,argc);\n            goto fmterr;\n        }\n        *p = '\\0';\n        memcpy(n->ip,argv[1],strlen(argv[1])+1);\n        char *port = p+1;\n        char *busp = strchr(port,'@');\n        if (busp) {\n            *busp = '\\0';\n            busp++;\n        }\n        n->port = atoi(port);\n        /* In older versions of nodes.conf the \"@busport\" part is missing.\n         * In this case we set it to the default offset of 10000 from the\n         * base port. */\n        n->cport = busp ? atoi(busp) : n->port + CLUSTER_PORT_INCR;\n\n        /* Parse flags */\n        p = s = argv[2];\n        while(p) {\n            p = strchr(s,',');\n            if (p) *p = '\\0';\n            if (!strcasecmp(s,\"myself\")) {\n                serverAssert(server.cluster->myself == NULL);\n                myself = server.cluster->myself = n;\n                n->flags |= CLUSTER_NODE_MYSELF;\n            } else if (!strcasecmp(s,\"master\")) {\n                n->flags |= CLUSTER_NODE_MASTER;\n            } else if (!strcasecmp(s,\"slave\")) {\n                n->flags |= CLUSTER_NODE_SLAVE;\n            } else if (!strcasecmp(s,\"fail?\")) {\n                n->flags |= CLUSTER_NODE_PFAIL;\n            } else if (!strcasecmp(s,\"fail\")) {\n                n->flags |= CLUSTER_NODE_FAIL;\n                n->fail_time = mstime();\n            } else if (!strcasecmp(s,\"handshake\")) {\n                n->flags |= CLUSTER_NODE_HANDSHAKE;\n            } else if (!strcasecmp(s,\"noaddr\")) {\n                n->flags |= CLUSTER_NODE_NOADDR;\n            } else if (!strcasecmp(s,\"nofailover\")) {\n                n->flags |= CLUSTER_NODE_NOFAILOVER;\n            } else if (!strcasecmp(s,\"noflags\")) {\n                /* nothing to do */\n            } else {\n                serverPanic(\"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        if (argv[3][0] != '-') {\n            master = clusterLookupNode(argv[3]);\n            if (!master) {\n                master = createClusterNode(argv[3],0);\n                clusterAddNode(master);\n            }\n            n->slaveof = master;\n            clusterNodeAddSlave(master,n);\n        }\n\n        /* Set ping sent / pong received timestamps */\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->configEpoch = strtoull(argv[6],NULL,10);\n\n        /* Populate hash slots served by this instance. */\n        for (j = 8; j < argc; j++) {\n            int start, stop;\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                serverAssert(p != NULL);\n                *p = '\\0';\n                direction = p[1]; /* Either '>' or '<' */\n                slot = atoi(argv[j]+1);\n                if (slot < 0 || slot >= CLUSTER_SLOTS) {\n                    sdsfreesplitres(argv,argc);\n                    goto fmterr;\n                }\n                p += 3;\n                cn = clusterLookupNode(p);\n                if (!cn) {\n                    cn = createClusterNode(p,0);\n                    clusterAddNode(cn);\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            } else if ((p = strchr(argv[j],'-')) != NULL) {\n                *p = '\\0';\n                start = atoi(argv[j]);\n                stop = atoi(p+1);\n            } else {\n                start = stop = atoi(argv[j]);\n            }\n            if (start < 0 || start >= CLUSTER_SLOTS ||\n                stop < 0 || stop >= CLUSTER_SLOTS)\n            {\n                sdsfreesplitres(argv,argc);\n                goto fmterr;\n            }\n            while(start <= stop) clusterAddSlot(n, start++);\n        }\n\n        sdsfreesplitres(argv,argc);\n    }\n    /* Config sanity check */\n    if (server.cluster->myself == NULL) goto fmterr;\n\n    zfree(line);\n    fclose(fp);\n\n    serverLog(LL_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 C_OK;\n\nfmterr:\n    serverLog(LL_WARNING,\n        \"Unrecoverable error: corrupted cluster config file.\");\n    zfree(line);\n    if (fp) 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. */\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(CLUSTER_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 > (off_t)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\nvoid clusterSaveConfigOrDie(int do_fsync) {\n    if (clusterSaveConfig(do_fsync) == -1) {\n        serverLog(LL_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 C_OK is returned, otherwise an error is logged and\n * the function returns C_ERR to signal a lock was not acquired. */\nint clusterLockConfig(char *filename) {\n/* flock() does not exist on Solaris\n * and a fcntl-based solution won't help, as we constantly re-open that file,\n * which will release _all_ locks anyway\n */\n#if !defined(__sun)\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        serverLog(LL_WARNING,\n            \"Can't open %s in order to acquire a lock: %s\",\n            filename, strerror(errno));\n        return C_ERR;\n    }\n\n    if (flock(fd,LOCK_EX|LOCK_NB) == -1) {\n        if (errno == EWOULDBLOCK) {\n            serverLog(LL_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            serverLog(LL_WARNING,\n                \"Impossible to lock %s: %s\", filename, strerror(errno));\n        }\n        close(fd);\n        return C_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#endif /* __sun */\n\n    return C_OK;\n}\n\n/* Some flags (currently just the NOFAILOVER flag) may need to be updated\n * in the \"myself\" node based on the current configuration of the node,\n * that may change at runtime via CONFIG SET. This function changes the\n * set of flags in myself->flags accordingly. */\nvoid clusterUpdateMyselfFlags(void) {\n    int oldflags = myself->flags;\n    int nofailover = server.cluster_slave_no_failover ?\n                     CLUSTER_NODE_NOFAILOVER : 0;\n    myself->flags &= ~CLUSTER_NODE_NOFAILOVER;\n    myself->flags |= nofailover;\n    if (myself->flags != oldflags) {\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                             CLUSTER_TODO_UPDATE_STATE);\n    }\n}\n\nvoid clusterInit(void) {\n    int saveconf = 0;\n\n    server.cluster = zmalloc(sizeof(clusterState));\n    server.cluster->myself = NULL;\n    server.cluster->currentEpoch = 0;\n    server.cluster->state = 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->cant_failover_reason = CLUSTER_CANT_FAILOVER_NONE;\n    server.cluster->lastVoteEpoch = 0;\n    for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {\n        server.cluster->stats_bus_messages_sent[i] = 0;\n        server.cluster->stats_bus_messages_received[i] = 0;\n    }\n    server.cluster->stats_pfail_nodes = 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) == C_ERR)\n        exit(1);\n\n    /* Load or create a new nodes configuration. */\n    if (clusterLoadConfig(server.cluster_configfile) == C_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,CLUSTER_NODE_MYSELF|CLUSTER_NODE_MASTER);\n        serverLog(LL_NOTICE,\"No cluster configuration found, I'm %.40s\",\n            myself->name);\n        clusterAddNode(myself);\n        saveconf = 1;\n    }\n    if (saveconf) clusterSaveConfigOrDie(1);\n\n    /* We need a listening TCP port for our cluster messaging needs. */\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    int port = server.tls_cluster ? server.tls_port : server.port;\n    if (port > (65535-CLUSTER_PORT_INCR)) {\n        serverLog(LL_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    if (listenToPort(port+CLUSTER_PORT_INCR,\n        server.cfd,&server.cfd_count) == C_ERR)\n    {\n        exit(1);\n    } else {\n        int j;\n\n        for (j = 0; j < server.cfd_count; j++) {\n            if (aeCreateFileEvent(server.el, server.cfd[j], AE_READABLE,\n                clusterAcceptHandler, NULL) == AE_ERR)\n                    serverPanic(\"Unrecoverable error creating Redis Cluster \"\n                                \"file event.\");\n        }\n    }\n\n    /* The slots -> keys map is a radix tree. Initialize it here. */\n    server.cluster->slots_to_keys = raxNew();\n    memset(server.cluster->slots_keys_count,0,\n           sizeof(server.cluster->slots_keys_count));\n\n    /* Set myself->port / cport to my listening ports, we'll just need to\n     * discover the IP address via MEET messages. */\n    myself->port = port;\n    myself->cport = port+CLUSTER_PORT_INCR;\n    if (server.cluster_announce_port)\n        myself->port = server.cluster_announce_port;\n    if (server.cluster_announce_bus_port)\n        myself->cport = server.cluster_announce_bus_port;\n\n    server.cluster->mf_end = 0;\n    resetManualFailover();\n    clusterUpdateMyselfFlags();\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.\n * 8) If the node was a slave, the whole data set is flushed away. */\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        emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);\n    }\n\n    /* Close slots, reset manual failover state. */\n    clusterCloseAllSlots();\n    resetManualFailover();\n\n    /* Unassign all the slots. */\n    for (j = 0; j < 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        serverLog(LL_WARNING, \"configEpoch set to 0 via CLUSTER RESET HARD\");\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, CLUSTER_NAMELEN);\n        dictDelete(server.cluster->nodes,oldname);\n        sdsfree(oldname);\n        getRandomHexChars(myself->name, CLUSTER_NAMELEN);\n        clusterAddNode(myself);\n        serverLog(LL_NOTICE,\"Node hard reset, now I'm %.40s\", myself->name);\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\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->conn = NULL;\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. */\nvoid freeClusterLink(clusterLink *link) {\n    if (link->conn) {\n        connClose(link->conn);\n        link->conn = NULL;\n    }\n    sdsfree(link->sndbuf);\n    sdsfree(link->rcvbuf);\n    if (link->node)\n        link->node->link = NULL;\n    zfree(link);\n}\n\nstatic void clusterConnAcceptHandler(connection *conn) {\n    clusterLink *link;\n\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_VERBOSE,\n                \"Error accepting cluster node connection: %s\", connGetLastError(conn));\n        connClose(conn);\n        return;\n    }\n\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->conn = conn;\n    connSetPrivateData(conn, link);\n\n    /* Register read handler */\n    connSetReadHandler(conn, clusterReadHandler);\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[NET_IP_STR_LEN];\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(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                serverLog(LL_VERBOSE,\n                    \"Error accepting cluster node: %s\", server.neterr);\n            return;\n        }\n\n        connection *conn = server.tls_cluster ? connCreateAcceptedTLS(cfd,1) : connCreateAcceptedSocket(cfd);\n        connNonBlock(conn);\n        connEnableTcpNoDelay(conn);\n\n        /* Use non-blocking I/O for cluster messages. */\n        serverLog(LL_VERBOSE,\"Accepting cluster node connection from %s:%d\", cip, cport);\n\n        /* Accept the connection now.  connAccept() may call our handler directly\n         * or schedule it for later depending on connection implementation.\n         */\n        if (connAccept(conn, clusterConnAcceptHandler) == C_ERR) {\n            if (connGetState(conn) == CONN_STATE_ERROR)\n                serverLog(LL_VERBOSE,\n                        \"Error accepting cluster node connection: %s\",\n                        connGetLastError(conn));\n            connClose(conn);\n            return;\n        }\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). */\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 between {} ? 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 * 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 * The node is created and returned to the user, but it is not automatically\n * added to the nodes hash table. */\nclusterNode *createClusterNode(char *nodename, int flags) {\n    clusterNode *node = zmalloc(sizeof(*node));\n\n    if (nodename)\n        memcpy(node->name, nodename, CLUSTER_NAMELEN);\n    else\n        getRandomHexChars(node->name, CLUSTER_NAMELEN);\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->data_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->cport = 0;\n    node->fail_reports = listCreate();\n    node->voted_time = 0;\n    node->orphaned_time = 0;\n    node->repl_offset_time = 0;\n    node->repl_offset = 0;\n    listSetFreeMethod(node->fail_reports,zfree);\n    return node;\n}\n\n/* This function is called every time we get a failure report from a node.\n * The side effect is to populate the fail_reports list (or to update\n * the timestamp of an existing report).\n *\n * 'failing' is the node that is in failure state according to the\n * 'sender' node.\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. */\nint clusterNodeAddFailureReport(clusterNode *failing, clusterNode *sender) {\n    list *l = failing->fail_reports;\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    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        if (fr->node == sender) {\n            fr->time = mstime();\n            return 0;\n        }\n    }\n\n    /* Otherwise create a new report. */\n    fr = zmalloc(sizeof(*fr));\n    fr->node = sender;\n    fr->time = mstime();\n    listAddNodeTail(l,fr);\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. */\nvoid clusterNodeCleanupFailureReports(clusterNode *node) {\n    list *l = node->fail_reports;\n    listNode *ln;\n    listIter li;\n    clusterNodeFailReport *fr;\n    mstime_t maxtime = server.cluster_node_timeout *\n                     CLUSTER_FAIL_REPORT_VALIDITY_MULT;\n    mstime_t now = mstime();\n\n    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\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 * 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 * The function returns 1 if the failure report was found and removed.\n * Otherwise 0 is returned. */\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    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        if (fr->node == sender) break;\n    }\n    if (!ln) return 0; /* No failure report from this sender. */\n\n    /* Remove the failure report. */\n    listDelNode(l,ln);\n    clusterNodeCleanupFailureReports(node);\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. */\nint clusterNodeFailureReportsCount(clusterNode *node) {\n    clusterNodeCleanupFailureReports(node);\n    return listLength(node->fail_reports);\n}\n\nint clusterNodeRemoveSlave(clusterNode *master, clusterNode *slave) {\n    int j;\n\n    for (j = 0; j < master->numslaves; j++) {\n        if (master->slaves[j] == slave) {\n            if ((j+1) < master->numslaves) {\n                int remaining_slaves = (master->numslaves - j) - 1;\n                memmove(master->slaves+j,master->slaves+(j+1),\n                        (sizeof(*master->slaves) * remaining_slaves));\n            }\n            master->numslaves--;\n            if (master->numslaves == 0)\n                master->flags &= ~CLUSTER_NODE_MIGRATE_TO;\n            return C_OK;\n        }\n    }\n    return C_ERR;\n}\n\nint clusterNodeAddSlave(clusterNode *master, clusterNode *slave) {\n    int j;\n\n    /* If it's already a slave, don't add it again. */\n    for (j = 0; j < master->numslaves; j++)\n        if (master->slaves[j] == slave) return C_ERR;\n    master->slaves = zrealloc(master->slaves,\n        sizeof(clusterNode*)*(master->numslaves+1));\n    master->slaves[master->numslaves] = slave;\n    master->numslaves++;\n    master->flags |= CLUSTER_NODE_MIGRATE_TO;\n    return C_OK;\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/* Low level cleanup of the node structure. Only called by clusterDelNode(). */\nvoid freeClusterNode(clusterNode *n) {\n    sds nodename;\n    int j;\n\n    /* If the node has associated slaves, we have to set\n     * all the slaves->slaveof fields to NULL (unknown). */\n    for (j = 0; j < n->numslaves; j++)\n        n->slaves[j]->slaveof = NULL;\n\n    /* Remove this node from the list of slaves of its master. */\n    if (nodeIsSlave(n) && n->slaveof) clusterNodeRemoveSlave(n->slaveof,n);\n\n    /* Unlink from the set of nodes. */\n    nodename = sdsnewlen(n->name, CLUSTER_NAMELEN);\n    serverAssert(dictDelete(server.cluster->nodes,nodename) == DICT_OK);\n    sdsfree(nodename);\n\n    /* Release link and associated data structures. */\n    if (n->link) freeClusterLink(n->link);\n    listRelease(n->fail_reports);\n    zfree(n->slaves);\n    zfree(n);\n}\n\n/* Add a node to the nodes hash table */\nint clusterAddNode(clusterNode *node) {\n    int retval;\n\n    retval = dictAdd(server.cluster->nodes,\n            sdsnewlen(node->name,CLUSTER_NAMELEN), node);\n    return (retval == DICT_OK) ? C_OK : C_ERR;\n}\n\n/* Remove a node from the cluster. The function performs the high level\n * cleanup, calling freeClusterNode() for the low level cleanup.\n * Here we do the following:\n *\n * 1) Mark all the slots handled by it as unassigned.\n * 2) Remove all the failure reports sent by this node and referenced by\n *    other nodes.\n * 3) Free the node with freeClusterNode() that will in turn remove it\n *    from the hash table and from the list of slaves of its master, if\n *    it is a slave node.\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 < CLUSTER_SLOTS; j++) {\n        if (server.cluster->importing_slots_from[j] == delnode)\n            server.cluster->importing_slots_from[j] = NULL;\n        if (server.cluster->migrating_slots_to[j] == delnode)\n            server.cluster->migrating_slots_to[j] = NULL;\n        if (server.cluster->slots[j] == delnode)\n            clusterDelSlot(j);\n    }\n\n    /* 2) Remove failure reports. */\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) Free the node, unlinking it from the cluster. */\n    freeClusterNode(delnode);\n}\n\n/* Node lookup by name */\nclusterNode *clusterLookupNode(const char *name) {\n    sds s = sdsnewlen(name, 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. */\nvoid clusterRenameNode(clusterNode *node, char *newname) {\n    int retval;\n    sds s = sdsnewlen(node->name, CLUSTER_NAMELEN);\n\n    serverLog(LL_DEBUG,\"Renaming node %.40s into %.40s\",\n        node->name, newname);\n    retval = dictDelete(server.cluster->nodes, s);\n    sdsfree(s);\n    serverAssert(retval == DICT_OK);\n    memcpy(node->name, newname, CLUSTER_NAMELEN);\n    clusterAddNode(node);\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER config epoch handling\n * -------------------------------------------------------------------------- */\n\n/* Return the greatest configEpoch found in the cluster, or the current\n * epoch if greater than any node configEpoch. */\nuint64_t clusterGetMaxEpoch(void) {\n    uint64_t max = 0;\n    dictIterator *di;\n    dictEntry *de;\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/* If this node epoch is zero or is not already the greatest across the\n * cluster (from the POV of the local configuration), this function will:\n *\n * 1) Generate a new config epoch, incrementing the current epoch.\n * 2) Assign the new epoch to this node, WITHOUT any consensus.\n * 3) Persist the configuration on disk before sending packets with the\n *    new configuration.\n *\n * If the new config epoch is generated and assigend, C_OK is returned,\n * otherwise C_ERR is returned (since the node has already the greatest\n * configuration around) and no operation is performed.\n *\n * Important note: this function violates the principle that config epochs\n * should be generated with consensus and should be unique across the cluster.\n * However Redis Cluster uses this auto-generated new config epochs in two\n * cases:\n *\n * 1) When slots are closed after importing. Otherwise resharding would be\n *    too expensive.\n * 2) When CLUSTER FAILOVER is called with options that force a slave to\n *    failover its master even if there is not master majority able to\n *    create a new configuration epoch.\n *\n * Redis Cluster will not explode using this function, even in the case of\n * a collision between this node and another node, generating the same\n * configuration epoch unilaterally, because the config epoch conflict\n * resolution algorithm will eventually move colliding nodes to different\n * config epochs. However using this function may violate the \"last failover\n * wins\" rule, so should only be used with care. */\nint clusterBumpConfigEpochWithoutConsensus(void) {\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_SAVE_CONFIG|\n                             CLUSTER_TODO_FSYNC_CONFIG);\n        serverLog(LL_WARNING,\n            \"New configEpoch set to %llu\",\n            (unsigned long long) myself->configEpoch);\n        return C_OK;\n    } else {\n        return C_ERR;\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,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    serverLog(LL_VERBOSE,\n        \"WARNING: configEpoch collision with node %.40s.\"\n        \" configEpoch set to %llu\",\n        sender->name,\n        (unsigned long long) myself->configEpoch);\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER nodes blacklist\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 CLUSTER_BLACKLIST_TTL).\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 * Currently the 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 * 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#define 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 * However without the cleanup during long uptimes and with some automated\n * node add/removal procedures, entries could accumulate. */\nvoid clusterBlacklistCleanup(void) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(server.cluster->nodes_black_list);\n    while((de = dictNext(di)) != NULL) {\n        int64_t expire = dictGetUnsignedIntegerVal(de);\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. */\nvoid clusterBlacklistAddNode(clusterNode *node) {\n    dictEntry *de;\n    sds id = sdsnewlen(node->name,CLUSTER_NAMELEN);\n\n    clusterBlacklistCleanup();\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    de = dictFind(server.cluster->nodes_black_list,id);\n    dictSetUnsignedIntegerVal(de,time(NULL)+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. */\nint clusterBlacklistExists(char *nodeid) {\n    sds id = sdsnewlen(nodeid,CLUSTER_NAMELEN);\n    int retval;\n\n    clusterBlacklistCleanup();\n    retval = dictFind(server.cluster->nodes_black_list,id) != NULL;\n    sdsfree(id);\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 * 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 * 2) We believe this node is in PFAIL state.\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 * 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 * 1) Either we reach the majority and eventually the FAIL state will propagate\n *    to all the cluster.\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 */\nvoid markNodeAsFailingIfNeeded(clusterNode *node) {\n    int failures;\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    failures = clusterNodeFailureReportsCount(node);\n    /* Also count myself as a voter if I'm a master. */\n    if (nodeIsMaster(myself)) failures++;\n    if (failures < needed_quorum) return; /* No weak agreement from masters. */\n\n    serverLog(LL_NOTICE,\n        \"Marking node %.40s as failing (quorum reached).\", node->name);\n\n    /* Mark the node as failing. */\n    node->flags &= ~CLUSTER_NODE_PFAIL;\n    node->flags |= CLUSTER_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    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. */\nvoid clearNodeFailureIfNeeded(clusterNode *node) {\n    mstime_t now = mstime();\n\n    serverAssert(nodeFailed(node));\n\n    /* For slaves we always clear the FAIL flag if we can contact the\n     * node again. */\n    if (nodeIsSlave(node) || node->numslots == 0) {\n        serverLog(LL_NOTICE,\n            \"Clear FAIL state for node %.40s: %s is reachable again.\",\n                node->name,\n                nodeIsSlave(node) ? \"replica\" : \"master without slots\");\n        node->flags &= ~CLUSTER_NODE_FAIL;\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n    }\n\n    /* If it is a master and...\n     * 1) The FAIL state is old enough.\n     * 2) It is yet serving slots from our point of view (not failed over).\n     * Apparently no one is going to fix these slots, clear the FAIL flag. */\n    if (nodeIsMaster(node) && node->numslots > 0 &&\n        (now - node->fail_time) >\n        (server.cluster_node_timeout * CLUSTER_FAIL_UNDO_TIME_MULT))\n    {\n        serverLog(LL_NOTICE,\n            \"Clear FAIL state for node %.40s: is reachable again and nobody is serving its slots after some time.\",\n                node->name);\n        node->flags &= ~CLUSTER_NODE_FAIL;\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. */\nint clusterHandshakeInProgress(char *ip, int port, int cport) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (!nodeInHandshake(node)) continue;\n        if (!strcasecmp(node->ip,ip) &&\n            node->port == port &&\n            node->cport == cport) break;\n    }\n    dictReleaseIterator(di);\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 * EAGAIN - There is already an handshake in progress for this address.\n * EINVAL - IP or port are not valid. */\nint clusterStartHandshake(char *ip, int port, int cport) {\n    clusterNode *n;\n    char norm_ip[NET_IP_STR_LEN];\n    struct sockaddr_storage sa;\n\n    /* IP sanity check */\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    if (port <= 0 || port > 65535 || cport <= 0 || cport > 65535) {\n        errno = EINVAL;\n        return 0;\n    }\n\n    /* Set norm_ip as the normalized string representation of the node\n     * IP address. */\n    memset(norm_ip,0,NET_IP_STR_LEN);\n    if (sa.ss_family == AF_INET)\n        inet_ntop(AF_INET,\n            (void*)&(((struct sockaddr_in *)&sa)->sin_addr),\n            norm_ip,NET_IP_STR_LEN);\n    else\n        inet_ntop(AF_INET6,\n            (void*)&(((struct sockaddr_in6 *)&sa)->sin6_addr),\n            norm_ip,NET_IP_STR_LEN);\n\n    if (clusterHandshakeInProgress(norm_ip,port,cport)) {\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     * handshake. */\n    n = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE|CLUSTER_NODE_MEET);\n    memcpy(n->ip,norm_ip,sizeof(n->ip));\n    n->port = port;\n    n->cport = cport;\n    clusterAddNode(n);\n    return 1;\n}\n\n/* Process the gossip section of PING or PONG packets.\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. */\nvoid clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {\n    uint16_t count = ntohs(hdr->count);\n    clusterMsgDataGossip *g = (clusterMsgDataGossip*) hdr->data.ping.gossip;\n    clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender);\n\n    while(count--) {\n        uint16_t flags = ntohs(g->flags);\n        clusterNode *node;\n        sds ci;\n\n        if (server.verbosity == LL_DEBUG) {\n            ci = representClusterNodeFlags(sdsempty(), flags);\n            serverLog(LL_DEBUG,\"GOSSIP %.40s %s:%d@%d %s\",\n                g->nodename,\n                g->ip,\n                ntohs(g->port),\n                ntohs(g->cport),\n                ci);\n            sdsfree(ci);\n        }\n\n        /* Update our state accordingly to the gossip sections */\n        node = clusterLookupNode(g->nodename);\n        if (node) {\n            /* We already know this node.\n               Handle failure reports, only when the sender is a master. */\n            if (sender && nodeIsMaster(sender) && node != myself) {\n                if (flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) {\n                    if (clusterNodeAddFailureReport(node,sender)) {\n                        serverLog(LL_VERBOSE,\n                            \"Node %.40s reported node %.40s as not reachable.\",\n                            sender->name, node->name);\n                    }\n                    markNodeAsFailingIfNeeded(node);\n                } else {\n                    if (clusterNodeDelFailureReport(node,sender)) {\n                        serverLog(LL_VERBOSE,\n                            \"Node %.40s reported node %.40s is back online.\",\n                            sender->name, node->name);\n                    }\n                }\n            }\n\n            /* If from our POV the node is up (no failure flags are set),\n             * we have no pending ping for the node, nor we have failure\n             * reports for this node, update the last pong time with the\n             * one we see from the other nodes. */\n            if (!(flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) &&\n                node->ping_sent == 0 &&\n                clusterNodeFailureReportsCount(node) == 0)\n            {\n                mstime_t pongtime = ntohl(g->pong_received);\n                pongtime *= 1000; /* Convert back to milliseconds. */\n\n                /* Replace the pong time with the received one only if\n                 * it's greater than our view but is not in the future\n                 * (with 500 milliseconds tolerance) from the POV of our\n                 * clock. */\n                if (pongtime <= (server.mstime+500) &&\n                    pongtime > node->pong_received)\n                {\n                    node->pong_received = pongtime;\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 of a node that\n             * can talk with this other node, update the address, disconnect\n             * the old link if any, so that we'll attempt to connect with the\n             * new address. */\n            if (node->flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL) &&\n                !(flags & CLUSTER_NODE_NOADDR) &&\n                !(flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) &&\n                (strcasecmp(node->ip,g->ip) ||\n                 node->port != ntohs(g->port) ||\n                 node->cport != ntohs(g->cport)))\n            {\n                if (node->link) freeClusterLink(node->link);\n                memcpy(node->ip,g->ip,NET_IP_STR_LEN);\n                node->port = ntohs(g->port);\n                node->cport = ntohs(g->cport);\n                node->flags &= ~CLUSTER_NODE_NOADDR;\n            }\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             * 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            if (sender &&\n                !(flags & CLUSTER_NODE_NOADDR) &&\n                !clusterBlacklistExists(g->nodename))\n            {\n                clusterStartHandshake(g->ip,ntohs(g->port),ntohs(g->cport));\n            }\n        }\n\n        /* Next node */\n        g++;\n    }\n}\n\n/* IP -> string conversion. 'buf' is supposed to at least be 46 bytes.\n * If 'announced_ip' length is non-zero, it is used instead of extracting\n * the IP from the socket peer address. */\nvoid nodeIp2String(char *buf, clusterLink *link, char *announced_ip) {\n    if (announced_ip[0] != '\\0') {\n        memcpy(buf,announced_ip,NET_IP_STR_LEN);\n        buf[NET_IP_STR_LEN-1] = '\\0'; /* We are not sure the input is sane. */\n    } else {\n        connPeerToString(link->conn, buf, NET_IP_STR_LEN, NULL);\n    }\n}\n\n/* Update the node address to the IP address that can be extracted\n * from link->fd, or if hdr->myip is non empty, to the address the node\n * is announcing us. The port is taken from the packet header as well.\n *\n * If the address or port changed, disconnect the node link so that we'll\n * connect again to the new address.\n *\n * If the ip/port pair are already correct no operation is performed at\n * all.\n *\n * The function returns 0 if the node address is still the same,\n * otherwise 1 is returned. */\nint nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,\n                              clusterMsg *hdr)\n{\n    char ip[NET_IP_STR_LEN] = {0};\n    int port = ntohs(hdr->port);\n    int cport = ntohs(hdr->cport);\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    if (link == node->link) return 0;\n\n    nodeIp2String(ip,link,hdr->myip);\n    if (node->port == port && node->cport == cport &&\n        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    node->cport = cport;\n    if (node->link) freeClusterLink(node->link);\n    node->flags &= ~CLUSTER_NODE_NOADDR;\n    serverLog(LL_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    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. */\nvoid clusterSetNodeAsMaster(clusterNode *n) {\n    if (nodeIsMaster(n)) return;\n\n    if (n->slaveof) {\n        clusterNodeRemoveSlave(n->slaveof,n);\n        if (n != myself) n->flags |= CLUSTER_NODE_MIGRATE_TO;\n    }\n    n->flags &= ~CLUSTER_NODE_SLAVE;\n    n->flags |= CLUSTER_NODE_MASTER;\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 * 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 * The 'sender' is the node for which we received a configuration update.\n * Sometimes it is not actually the \"Sender\" of the information, like in the\n * case we receive the info via an UPDATE packet. */\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[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    curmaster = nodeIsMaster(myself) ? myself : myself->slaveof;\n\n    if (sender == myself) {\n        serverLog(LL_WARNING,\"Discarding UPDATE message about myself.\");\n        return;\n    }\n\n    for (j = 0; j < CLUSTER_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                if (server.cluster->slots[j] == curmaster)\n                    newmaster = sender;\n                clusterDelSlot(j);\n                clusterAddSlot(sender,j);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE|\n                                     CLUSTER_TODO_FSYNC_CONFIG);\n            }\n        }\n    }\n\n    /* After updating the slots configuration, don't do any actual change\n     * in the state of the server if a module disabled Redis Cluster\n     * keys redirections. */\n    if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)\n        return;\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     * 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     * 2) We are a slave and our master is left without slots. We need\n     *    to replicate to the new slots owner. */\n    if (newmaster && curmaster->numslots == 0) {\n        serverLog(LL_WARNING,\n            \"Configuration change detected. Reconfiguring myself \"\n            \"as a replica of %.40s\", sender->name);\n        clusterSetMaster(sender);\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/* 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 * 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). */\nint clusterProcessPacket(clusterLink *link) {\n    clusterMsg *hdr = (clusterMsg*) link->rcvbuf;\n    uint32_t totlen = ntohl(hdr->totlen);\n    uint16_t type = ntohs(hdr->type);\n    mstime_t now = mstime();\n\n    if (type < CLUSTERMSG_TYPE_COUNT)\n        server.cluster->stats_bus_messages_received[type]++;\n    serverLog(LL_DEBUG,\"--- Processing packet of type %d, %lu bytes\",\n        type, (unsigned long) totlen);\n\n    /* Perform sanity checks */\n    if (totlen < 16) return 1; /* At least signature, version, totlen, count. */\n    if (totlen > sdslen(link->rcvbuf)) return 1;\n\n    if (ntohs(hdr->ver) != CLUSTER_PROTO_VER) {\n        /* Can't handle messages of different versions. */\n        return 1;\n    }\n\n    uint16_t flags = ntohs(hdr->flags);\n    uint64_t senderCurrentEpoch = 0, senderConfigEpoch = 0;\n    clusterNode *sender;\n\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                8 +\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    } else if (type == CLUSTERMSG_TYPE_MODULE) {\n        uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n\n        explen += sizeof(clusterMsgDataPublish) -\n                3 + ntohl(hdr->data.module.msg.len);\n        if (totlen != explen) return 1;\n    }\n\n    /* Check if the sender is a known node. Note that for incoming connections\n     * we don't store link->node information, but resolve the node by the\n     * ID in the header each time in the current implementation. */\n    sender = clusterLookupNode(hdr->sender);\n\n    /* Update the last time we saw any data from this node. We\n     * use this in order to avoid detecting a timeout from a node that\n     * is just sending a lot of data in the cluster bus, for instance\n     * because of Pub/Sub. */\n    if (sender) sender->data_received = now;\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 = now;\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            serverLog(LL_WARNING,\n                \"Received replication offset for paused \"\n                \"master manual failover: %lld\",\n                server.cluster->mf_master_offset);\n        }\n    }\n\n    /* Initial processing of PING and MEET requests replying with a PONG. */\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_MEET) {\n        serverLog(LL_DEBUG,\"Ping packet received: %p\", (void*)link->node);\n\n        /* We use incoming MEET messages in order to set the address\n         * for 'myself', since only other cluster nodes will send us\n         * MEET messages on handshakes, when the cluster joins, or\n         * later if we changed address, and those nodes will use our\n         * official address to connect to us. So by obtaining this address\n         * from the socket is a simple way to discover / update our own\n         * address in the cluster without it being hardcoded in the config.\n         *\n         * However if we don't have an address at all, we update the address\n         * even with a normal PING packet. If it's wrong it will be fixed\n         * by MEET later. */\n        if ((type == CLUSTERMSG_TYPE_MEET || myself->ip[0] == '\\0') &&\n            server.cluster_announce_ip == NULL)\n        {\n            char ip[NET_IP_STR_LEN];\n\n            if (connSockName(link->conn,ip,sizeof(ip),NULL) != -1 &&\n                strcmp(ip,myself->ip))\n            {\n                memcpy(myself->ip,ip,NET_IP_STR_LEN);\n                serverLog(LL_WARNING,\"IP address for this node updated to %s\",\n                    myself->ip);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n            }\n        }\n\n        /* Add this node if it is new for us and the msg type is MEET.\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        if (!sender && type == CLUSTERMSG_TYPE_MEET) {\n            clusterNode *node;\n\n            node = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE);\n            nodeIp2String(node->ip,link,hdr->myip);\n            node->port = ntohs(hdr->port);\n            node->cport = ntohs(hdr->cport);\n            clusterAddNode(node);\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n        }\n\n        /* If this is a MEET packet from an unknown node, we still process\n         * the gossip section here since we have to trust the sender because\n         * of the message type. */\n        if (!sender && type == CLUSTERMSG_TYPE_MEET)\n            clusterProcessGossipSection(hdr,link);\n\n        /* Anyway reply with a PONG */\n        clusterSendPing(link,CLUSTERMSG_TYPE_PONG);\n    }\n\n    /* PING, PONG, MEET: process config information. */\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_PONG ||\n        type == CLUSTERMSG_TYPE_MEET)\n    {\n        serverLog(LL_DEBUG,\"%s packet received: %p\",\n            type == CLUSTERMSG_TYPE_PING ? \"ping\" : \"pong\",\n            (void*)link->node);\n        if (link->node) {\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                    serverLog(LL_VERBOSE,\n                        \"Handshake: we already know node %.40s, \"\n                        \"updating the address if needed.\", sender->name);\n                    if (nodeUpdateAddressIfNeeded(sender,link,hdr))\n                    {\n                        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                             CLUSTER_TODO_UPDATE_STATE);\n                    }\n                    /* Free this node as we already have it. This will\n                     * cause the link to be freed as well. */\n                    clusterDelNode(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                clusterRenameNode(link->node, hdr->sender);\n                serverLog(LL_DEBUG,\"Handshake with node %.40s completed.\",\n                    link->node->name);\n                link->node->flags &= ~CLUSTER_NODE_HANDSHAKE;\n                link->node->flags |= flags&(CLUSTER_NODE_MASTER|CLUSTER_NODE_SLAVE);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n            } else if (memcmp(link->node->name,hdr->sender,\n                        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                serverLog(LL_DEBUG,\"PONG contains mismatching sender ID. About node %.40s added %d ms ago, having flags %d\",\n                    link->node->name,\n                    (int)(now-(link->node->ctime)),\n                    link->node->flags);\n                link->node->flags |= CLUSTER_NODE_NOADDR;\n                link->node->ip[0] = '\\0';\n                link->node->port = 0;\n                link->node->cport = 0;\n                freeClusterLink(link);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n                return 0;\n            }\n        }\n\n        /* Copy the CLUSTER_NODE_NOFAILOVER flag from what the sender\n         * announced. This is a dynamic flag that we receive from the\n         * sender, and the latest status must be trusted. We need it to\n         * be propagated because the slave ranking used to understand the\n         * delay of each slave in the voting process, needs to know\n         * what are the instances really competing. */\n        if (sender) {\n            int nofailover = flags & CLUSTER_NODE_NOFAILOVER;\n            sender->flags &= ~CLUSTER_NODE_NOFAILOVER;\n            sender->flags |= nofailover;\n        }\n\n        /* Update the node address if it changed. */\n        if (sender && type == CLUSTERMSG_TYPE_PING &&\n            !nodeInHandshake(sender) &&\n            nodeUpdateAddressIfNeeded(sender,link,hdr))\n        {\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                 CLUSTER_TODO_UPDATE_STATE);\n        }\n\n        /* Update our info about the node */\n        if (link->node && type == CLUSTERMSG_TYPE_PONG) {\n            link->node->pong_received = now;\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             * The FAIL condition is also reversible under specific\n             * conditions detected by clearNodeFailureIfNeeded(). */\n            if (nodeTimedOut(link->node)) {\n                link->node->flags &= ~CLUSTER_NODE_PFAIL;\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE);\n            } else if (nodeFailed(link->node)) {\n                clearNodeFailureIfNeeded(link->node);\n            }\n        }\n\n        /* Check for role switch: slave -> master or master -> slave. */\n        if (sender) {\n            if (!memcmp(hdr->slaveof,CLUSTER_NODE_NULL_NAME,\n                sizeof(hdr->slaveof)))\n            {\n                /* Node is a master. */\n                clusterSetNodeAsMaster(sender);\n            } else {\n                /* Node is a slave. */\n                clusterNode *master = clusterLookupNode(hdr->slaveof);\n\n                if (nodeIsMaster(sender)) {\n                    /* Master turned into a slave! Reconfigure the node. */\n                    clusterDelNodeSlots(sender);\n                    sender->flags &= ~(CLUSTER_NODE_MASTER|\n                                       CLUSTER_NODE_MIGRATE_TO);\n                    sender->flags |= CLUSTER_NODE_SLAVE;\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                if (master && sender->slaveof != master) {\n                    if (sender->slaveof)\n                        clusterNodeRemoveSlave(sender->slaveof,sender);\n                    clusterNodeAddSlave(master,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         * Note: this MUST happen after we update the master/slave state\n         * so that CLUSTER_NODE_MASTER flag will be set. */\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        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         * 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         * A and B are master and slave for slots 1,2,3.\n         * A is partitioned away, B gets promoted.\n         * B is partitioned away, and A returns available.\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        if (sender && dirty_slots) {\n            int j;\n\n            for (j = 0; j < CLUSTER_SLOTS; j++) {\n                if (bitmapTestBit(hdr->myslots,j)) {\n                    if (server.cluster->slots[j] == sender ||\n                        server.cluster->slots[j] == NULL) continue;\n                    if (server.cluster->slots[j]->configEpoch >\n                        senderConfigEpoch)\n                    {\n                        serverLog(LL_VERBOSE,\n                            \"Node %.40s has old slots configuration, sending \"\n                            \"an UPDATE message about %.40s\",\n                                sender->name, server.cluster->slots[j]->name);\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        if (sender) clusterProcessGossipSection(hdr,link);\n    } else if (type == CLUSTERMSG_TYPE_FAIL) {\n        clusterNode *failing;\n\n        if (sender) {\n            failing = clusterLookupNode(hdr->data.fail.about.nodename);\n            if (failing &&\n                !(failing->flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_MYSELF)))\n            {\n                serverLog(LL_NOTICE,\n                    \"FAIL message received from %.40s about %.40s\",\n                    hdr->sender, hdr->data.fail.about.nodename);\n                failing->flags |= CLUSTER_NODE_FAIL;\n                failing->fail_time = now;\n                failing->flags &= ~CLUSTER_NODE_PFAIL;\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE);\n            }\n        } else {\n            serverLog(LL_NOTICE,\n                \"Ignoring FAIL message from unknown node %.40s about %.40s\",\n                hdr->sender, hdr->data.fail.about.nodename);\n        }\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        if (dictSize(server.pubsub_channels) ||\n           listLength(server.pubsub_patterns))\n        {\n            channel_len = ntohl(hdr->data.publish.msg.channel_len);\n            message_len = ntohl(hdr->data.publish.msg.message_len);\n            channel = createStringObject(\n                        (char*)hdr->data.publish.msg.bulk_data,channel_len);\n            message = createStringObject(\n                        (char*)hdr->data.publish.msg.bulk_data+channel_len,\n                        message_len);\n            pubsubPublishMessage(channel,message);\n            decrRefCount(channel);\n            decrRefCount(message);\n        }\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST) {\n        if (!sender) return 1;  /* We don't know that node. */\n        clusterSendFailoverAuthIfNeeded(sender,hdr);\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK) {\n        if (!sender) return 1;  /* We don't know that node. */\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        if (nodeIsMaster(sender) && sender->numslots > 0 &&\n            senderCurrentEpoch >= server.cluster->failover_auth_epoch)\n        {\n            server.cluster->failover_auth_count++;\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    } 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 = now + CLUSTER_MF_TIMEOUT;\n        server.cluster->mf_slave = sender;\n        pauseClients(now+(CLUSTER_MF_TIMEOUT*CLUSTER_MF_PAUSE_MULT));\n        serverLog(LL_WARNING,\"Manual failover requested by replica %.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 = clusterLookupNode(hdr->data.update.nodecfg.nodename);\n        if (!n) return 1;   /* We don't know the reported node. */\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        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 update our\n         * config accordingly. */\n        clusterUpdateSlotsConfigWith(n,reportedConfigEpoch,\n            hdr->data.update.nodecfg.slots);\n    } else if (type == CLUSTERMSG_TYPE_MODULE) {\n        if (!sender) return 1;  /* Protect the module from unknown nodes. */\n        /* We need to route this message back to the right module subscribed\n         * for the right message type. */\n        uint64_t module_id = hdr->data.module.msg.module_id; /* Endian-safe ID */\n        uint32_t len = ntohl(hdr->data.module.msg.len);\n        uint8_t type = hdr->data.module.msg.type;\n        unsigned char *payload = hdr->data.module.msg.bulk_data;\n        moduleCallClusterReceivers(sender->name,module_id,type,payload,len);\n    } else {\n        serverLog(LL_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   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   Instead if the node is a temporary node used to accept a query, we\n   completely free the node on error. */\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. */\nvoid clusterWriteHandler(connection *conn) {\n    clusterLink *link = connGetPrivateData(conn);\n    ssize_t nwritten;\n\n    nwritten = connWrite(conn, link->sndbuf, sdslen(link->sndbuf));\n    if (nwritten <= 0) {\n        serverLog(LL_DEBUG,\"I/O error writing to node link: %s\",\n            (nwritten == -1) ? connGetLastError(conn) : \"short write\");\n        handleLinkIOError(link);\n        return;\n    }\n    sdsrange(link->sndbuf,nwritten,-1);\n    if (sdslen(link->sndbuf) == 0)\n        connSetWriteHandler(link->conn, NULL);\n}\n\n/* A connect handler that gets called when a connection to another node\n * gets established.\n */\nvoid clusterLinkConnectHandler(connection *conn) {\n    clusterLink *link = connGetPrivateData(conn);\n    clusterNode *node = link->node;\n\n    /* Check if connection succeeded */\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_VERBOSE, \"Connection with Node %.40s at %s:%d failed: %s\",\n                node->name, node->ip, node->cport,\n                connGetLastError(conn));\n        freeClusterLink(link);\n        return;\n    }\n\n    /* Register a read handler from now on */\n    connSetReadHandler(conn, clusterReadHandler);\n\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    mstime_t old_ping_sent = node->ping_sent;\n    clusterSendPing(link, node->flags & CLUSTER_NODE_MEET ?\n            CLUSTERMSG_TYPE_MEET : CLUSTERMSG_TYPE_PING);\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    /* We can clear the flag after the first packet is sent.\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    node->flags &= ~CLUSTER_NODE_MEET;\n\n    serverLog(LL_DEBUG,\"Connecting with Node %.40s at %s:%d\",\n            node->name, node->ip, node->cport);\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. */\nvoid clusterReadHandler(connection *conn) {\n    clusterMsg buf[1];\n    ssize_t nread;\n    clusterMsg *hdr;\n    clusterLink *link = connGetPrivateData(conn);\n    unsigned int readlen, rcvbuflen;\n\n    while(1) { /* Read as long as there is data to read. */\n        rcvbuflen = sdslen(link->rcvbuf);\n        if (rcvbuflen < 8) {\n            /* First, obtain the first 8 bytes to get the full message\n             * length. */\n            readlen = 8 - rcvbuflen;\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                    serverLog(LL_WARNING,\n                        \"Bad message length or signature received \"\n                        \"from Cluster bus.\");\n                    handleLinkIOError(link);\n                    return;\n                }\n            }\n            readlen = ntohl(hdr->totlen) - rcvbuflen;\n            if (readlen > sizeof(buf)) readlen = sizeof(buf);\n        }\n\n        nread = connRead(conn,buf,readlen);\n        if (nread == -1 && (connGetState(conn) == CONN_STATE_CONNECTED)) return; /* No more data ready. */\n\n        if (nread <= 0) {\n            /* I/O error... */\n            serverLog(LL_DEBUG,\"I/O error reading from node link: %s\",\n                (nread == 0) ? \"connection closed\" : connGetLastError(conn));\n            handleLinkIOError(link);\n            return;\n        } else {\n            /* Read data and recast the pointer to the new buffer. */\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        if (rcvbuflen >= 8 && rcvbuflen == ntohl(hdr->totlen)) {\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 * 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. */\nvoid clusterSendMessage(clusterLink *link, unsigned char *msg, size_t msglen) {\n    if (sdslen(link->sndbuf) == 0 && msglen != 0)\n        connSetWriteHandlerWithBarrier(link->conn, clusterWriteHandler, 1);\n\n    link->sndbuf = sdscatlen(link->sndbuf, msg, msglen);\n\n    /* Populate sent messages stats. */\n    clusterMsg *hdr = (clusterMsg*) msg;\n    uint16_t type = ntohs(hdr->type);\n    if (type < CLUSTERMSG_TYPE_COUNT)\n        server.cluster->stats_bus_messages_sent[type]++;\n}\n\n/* Send a message to all the nodes that are part of the cluster having\n * a connected link.\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    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (!node->link) continue;\n        if (node->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_HANDSHAKE))\n            continue;\n        clusterSendMessage(node->link,buf,len);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Build the message header. hdr must point to a buffer at least\n * sizeof(clusterMsg) in bytes. */\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     * 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    master = (nodeIsSlave(myself) && myself->slaveof) ?\n              myself->slaveof : myself;\n\n    memset(hdr,0,sizeof(*hdr));\n    hdr->ver = htons(CLUSTER_PROTO_VER);\n    hdr->sig[0] = 'R';\n    hdr->sig[1] = 'C';\n    hdr->sig[2] = 'm';\n    hdr->sig[3] = 'b';\n    hdr->type = htons(type);\n    memcpy(hdr->sender,myself->name,CLUSTER_NAMELEN);\n\n    /* If cluster-announce-ip option is enabled, force the receivers of our\n     * packets to use the specified address for this node. Otherwise if the\n     * first byte is zero, they'll do auto discovery. */\n    memset(hdr->myip,0,NET_IP_STR_LEN);\n    if (server.cluster_announce_ip) {\n        strncpy(hdr->myip,server.cluster_announce_ip,NET_IP_STR_LEN);\n        hdr->myip[NET_IP_STR_LEN-1] = '\\0';\n    }\n\n    /* Handle cluster-announce-port as well. */\n    int port = server.tls_cluster ? server.tls_port : server.port;\n    int announced_port = server.cluster_announce_port ?\n                         server.cluster_announce_port : port;\n    int announced_cport = server.cluster_announce_bus_port ?\n                          server.cluster_announce_bus_port :\n                          (port + CLUSTER_PORT_INCR);\n\n    memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots));\n    memset(hdr->slaveof,0,CLUSTER_NAMELEN);\n    if (myself->slaveof != NULL)\n        memcpy(hdr->slaveof,myself->slaveof->name, CLUSTER_NAMELEN);\n    hdr->port = htons(announced_port);\n    hdr->cport = htons(announced_cport);\n    hdr->flags = htons(myself->flags);\n    hdr->state = server.cluster->state;\n\n    /* Set the currentEpoch and configEpochs. */\n    hdr->currentEpoch = htonu64(server.cluster->currentEpoch);\n    hdr->configEpoch = htonu64(master->configEpoch);\n\n    /* Set the replication offset. */\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    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    hdr->totlen = htonl(totlen);\n    /* For PING, PONG, and MEET, fixing the totlen field is up to the caller. */\n}\n\n/* Return non zero if the node is already present in the gossip section of the\n * message pointed by 'hdr' and having 'count' gossip entries. Otherwise\n * zero is returned. Helper for clusterSendPing(). */\nint clusterNodeIsInGossipSection(clusterMsg *hdr, int count, clusterNode *n) {\n    int j;\n    for (j = 0; j < count; j++) {\n        if (memcmp(hdr->data.ping.gossip[j].nodename,n->name,\n                CLUSTER_NAMELEN) == 0) break;\n    }\n    return j != count;\n}\n\n/* Set the i-th entry of the gossip section in the message pointed by 'hdr'\n * to the info of the specified node 'n'. */\nvoid clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {\n    clusterMsgDataGossip *gossip;\n    gossip = &(hdr->data.ping.gossip[i]);\n    memcpy(gossip->nodename,n->name,CLUSTER_NAMELEN);\n    gossip->ping_sent = htonl(n->ping_sent/1000);\n    gossip->pong_received = htonl(n->pong_received/1000);\n    memcpy(gossip->ip,n->ip,sizeof(n->ip));\n    gossip->port = htons(n->port);\n    gossip->cport = htons(n->cport);\n    gossip->flags = htons(n->flags);\n    gossip->notused1 = 0;\n}\n\n/* Send a PING or PONG packet to the specified node, making sure to add enough\n * gossip informations. */\nvoid clusterSendPing(clusterLink *link, int type) {\n    unsigned char *buf;\n    clusterMsg *hdr;\n    int gossipcount = 0; /* Number of gossip sections added so far. */\n    int wanted; /* Number of gossip sections we want to append if possible. */\n    int totlen; /* Total packet length. */\n    /* freshnodes is the max number of nodes we can hope to append at all:\n     * nodes available minus two (ourself and the node we are sending the\n     * message to). However practically there may be less valid nodes since\n     * nodes in handshake state, disconnected, are not considered. */\n    int freshnodes = dictSize(server.cluster->nodes)-2;\n\n    /* How many gossip sections we want to add? 1/10 of the number of nodes\n     * and anyway at least 3. Why 1/10?\n     *\n     * If we have N masters, with N/10 entries, and we consider that in\n     * node_timeout we exchange with each other node at least 4 packets\n     * (we ping in the worst case in node_timeout/2 time, and we also\n     * receive two pings from the host), we have a total of 8 packets\n     * in the node_timeout*2 falure reports validity time. So we have\n     * that, for a single PFAIL node, we can expect to receive the following\n     * number of failure reports (in the specified window of time):\n     *\n     * PROB * GOSSIP_ENTRIES_PER_PACKET * TOTAL_PACKETS:\n     *\n     * PROB = probability of being featured in a single gossip entry,\n     *        which is 1 / NUM_OF_NODES.\n     * ENTRIES = 10.\n     * TOTAL_PACKETS = 2 * 4 * NUM_OF_MASTERS.\n     *\n     * If we assume we have just masters (so num of nodes and num of masters\n     * is the same), with 1/10 we always get over the majority, and specifically\n     * 80% of the number of nodes, to account for many masters failing at the\n     * same time.\n     *\n     * Since we have non-voting slaves that lower the probability of an entry\n     * to feature our node, we set the number of entries per packet as\n     * 10% of the total nodes we have. */\n    wanted = floor(dictSize(server.cluster->nodes)/10);\n    if (wanted < 3) wanted = 3;\n    if (wanted > freshnodes) wanted = freshnodes;\n\n    /* Include all the nodes in PFAIL state, so that failure reports are\n     * faster to propagate to go from PFAIL to FAIL state. */\n    int pfail_wanted = server.cluster->stats_pfail_nodes;\n\n    /* Compute the maxium totlen to allocate our buffer. We'll fix the totlen\n     * later according to the number of gossip sections we really were able\n     * to put inside the packet. */\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += (sizeof(clusterMsgDataGossip)*(wanted+pfail_wanted));\n    /* Note: clusterBuildMessageHdr() expects the buffer to be always at least\n     * sizeof(clusterMsg) or more. */\n    if (totlen < (int)sizeof(clusterMsg)) totlen = sizeof(clusterMsg);\n    buf = zcalloc(totlen);\n    hdr = (clusterMsg*) buf;\n\n    /* Populate the header. */\n    if (link->node && type == CLUSTERMSG_TYPE_PING)\n        link->node->ping_sent = mstime();\n    clusterBuildMessageHdr(hdr,type);\n\n    /* Populate the gossip fields */\n    int maxiterations = wanted*3;\n    while(freshnodes > 0 && gossipcount < wanted && maxiterations--) {\n        dictEntry *de = dictGetRandomKey(server.cluster->nodes);\n        clusterNode *this = dictGetVal(de);\n\n        /* Don't include this node: the whole packet header is about us\n         * already, so we just gossip about other nodes. */\n        if (this == myself) continue;\n\n        /* PFAIL nodes will be added later. */\n        if (this->flags & CLUSTER_NODE_PFAIL) continue;\n\n        /* In the gossip section don't include:\n         * 1) Nodes in HANDSHAKE state.\n         * 3) Nodes with the NOADDR flag set.\n         * 4) Disconnected nodes if they don't have configured slots.\n         */\n        if (this->flags & (CLUSTER_NODE_HANDSHAKE|CLUSTER_NODE_NOADDR) ||\n            (this->link == NULL && this->numslots == 0))\n        {\n            freshnodes--; /* Tecnically not correct, but saves CPU. */\n            continue;\n        }\n\n        /* Do not add a node we already have. */\n        if (clusterNodeIsInGossipSection(hdr,gossipcount,this)) continue;\n\n        /* Add it */\n        clusterSetGossipEntry(hdr,gossipcount,this);\n        freshnodes--;\n        gossipcount++;\n    }\n\n    /* If there are PFAIL nodes, add them at the end. */\n    if (pfail_wanted) {\n        dictIterator *di;\n        dictEntry *de;\n\n        di = dictGetSafeIterator(server.cluster->nodes);\n        while((de = dictNext(di)) != NULL && pfail_wanted > 0) {\n            clusterNode *node = dictGetVal(de);\n            if (node->flags & CLUSTER_NODE_HANDSHAKE) continue;\n            if (node->flags & CLUSTER_NODE_NOADDR) continue;\n            if (!(node->flags & CLUSTER_NODE_PFAIL)) continue;\n            clusterSetGossipEntry(hdr,gossipcount,node);\n            freshnodes--;\n            gossipcount++;\n            /* We take the count of the slots we allocated, since the\n             * PFAIL stats may not match perfectly with the current number\n             * of PFAIL nodes. */\n            pfail_wanted--;\n        }\n        dictReleaseIterator(di);\n    }\n\n    /* Ready to send... fix the totlen fiend and queue the message in the\n     * output buffer. */\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += (sizeof(clusterMsgDataGossip)*gossipcount);\n    hdr->count = htons(gossipcount);\n    hdr->totlen = htonl(totlen);\n    clusterSendMessage(link,buf,totlen);\n    zfree(buf);\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 * 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 *\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    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\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        clusterSendPing(node->link,CLUSTERMSG_TYPE_PONG);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Send a PUBLISH message.\n *\n * If link is NULL, then the message is broadcasted to the whole cluster. */\nvoid clusterSendPublish(clusterLink *link, robj *channel, robj *message) {\n    unsigned char *payload;\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n    uint32_t channel_len, message_len;\n\n    channel = getDecodedObject(channel);\n    message = getDecodedObject(message);\n    channel_len = sdslen(channel->ptr);\n    message_len = sdslen(message->ptr);\n\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_PUBLISH);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += sizeof(clusterMsgDataPublish) - 8 + 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 = (unsigned char*)buf;\n    } else {\n        payload = zmalloc(totlen);\n        memcpy(payload,hdr,sizeof(*hdr));\n        hdr = (clusterMsg*) payload;\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    if (link)\n        clusterSendMessage(link,payload,totlen);\n    else\n        clusterBroadcastMessage(payload,totlen);\n\n    decrRefCount(channel);\n    decrRefCount(message);\n    if (payload != (unsigned char*)buf) zfree(payload);\n}\n\n/* Send a FAIL message to all the nodes we are able to contact.\n * The FAIL message is sent when we detect that a node is failing\n * (CLUSTER_NODE_PFAIL) and we also receive a gossip confirmation of this:\n * we switch the node state to CLUSTER_NODE_FAIL and ask all the other\n * nodes to do the same ASAP. */\nvoid clusterSendFail(char *nodename) {\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_FAIL);\n    memcpy(hdr->data.fail.about.nodename,nodename,CLUSTER_NAMELEN);\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. */\nvoid clusterSendUpdate(clusterLink *link, clusterNode *node) {\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n\n    if (link == NULL) return;\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_UPDATE);\n    memcpy(hdr->data.update.nodecfg.nodename,node->name,CLUSTER_NAMELEN);\n    hdr->data.update.nodecfg.configEpoch = htonu64(node->configEpoch);\n    memcpy(hdr->data.update.nodecfg.slots,node->slots,sizeof(node->slots));\n    clusterSendMessage(link,(unsigned char*)buf,ntohl(hdr->totlen));\n}\n\n/* Send a MODULE message.\n *\n * If link is NULL, then the message is broadcasted to the whole cluster. */\nvoid clusterSendModule(clusterLink *link, uint64_t module_id, uint8_t type,\n                       unsigned char *payload, uint32_t len) {\n    unsigned char *heapbuf;\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_MODULE);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += sizeof(clusterMsgModule) - 3 + len;\n\n    hdr->data.module.msg.module_id = module_id; /* Already endian adjusted. */\n    hdr->data.module.msg.type = type;\n    hdr->data.module.msg.len = htonl(len);\n    hdr->totlen = htonl(totlen);\n\n    /* Try to use the local buffer if possible */\n    if (totlen < sizeof(buf)) {\n        heapbuf = (unsigned char*)buf;\n    } else {\n        heapbuf = zmalloc(totlen);\n        memcpy(heapbuf,hdr,sizeof(*hdr));\n        hdr = (clusterMsg*) heapbuf;\n    }\n    memcpy(hdr->data.module.msg.bulk_data,payload,len);\n\n    if (link)\n        clusterSendMessage(link,heapbuf,totlen);\n    else\n        clusterBroadcastMessage(heapbuf,totlen);\n\n    if (heapbuf != (unsigned char*)buf) zfree(heapbuf);\n}\n\n/* This function gets a cluster node ID string as target, the same way the nodes\n * addresses are represented in the modules side, resolves the node, and sends\n * the message. If the target is NULL the message is broadcasted.\n *\n * The function returns C_OK if the target is valid, otherwise C_ERR is\n * returned. */\nint clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, unsigned char *payload, uint32_t len) {\n    clusterNode *node = NULL;\n\n    if (target != NULL) {\n        node = clusterLookupNode(target);\n        if (node == NULL || node->link == NULL) return C_ERR;\n    }\n\n    clusterSendModule(target ? node->link : NULL,\n                      module_id, type, payload, len);\n    return C_OK;\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 * -------------------------------------------------------------------------- */\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 * 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. */\nvoid clusterRequestFailoverAuth(void) {\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\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    clusterBroadcastMessage(buf,totlen);\n}\n\n/* Send a FAILOVER_AUTH_ACK message to the specified node. */\nvoid clusterSendFailoverAuth(clusterNode *node) {\n    clusterMsg buf[1];\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,(unsigned char*)buf,totlen);\n}\n\n/* Send a MFSTART message to the specified node. */\nvoid clusterSendMFStart(clusterNode *node) {\n    clusterMsg buf[1];\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,(unsigned char*)buf,totlen);\n}\n\n/* Vote for the node asking for our vote if there are the conditions. */\nvoid clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request) {\n    clusterNode *master = node->slaveof;\n    uint64_t requestCurrentEpoch = ntohu64(request->currentEpoch);\n    uint64_t requestConfigEpoch = ntohu64(request->configEpoch);\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    if (nodeIsSlave(myself) || myself->numslots == 0) return;\n\n    /* Request epoch must be >= our currentEpoch.\n     * Note that it is impossible for it to actually be greater since\n     * our currentEpoch was updated as a side effect of receiving this\n     * request, if the request epoch was greater. */\n    if (requestCurrentEpoch < server.cluster->currentEpoch) {\n        serverLog(LL_WARNING,\n            \"Failover auth denied to %.40s: reqEpoch (%llu) < curEpoch(%llu)\",\n            node->name,\n            (unsigned long long) requestCurrentEpoch,\n            (unsigned long long) server.cluster->currentEpoch);\n        return;\n    }\n\n    /* I already voted for this epoch? Return ASAP. */\n    if (server.cluster->lastVoteEpoch == server.cluster->currentEpoch) {\n        serverLog(LL_WARNING,\n                \"Failover auth denied to %.40s: already voted for epoch %llu\",\n                node->name,\n                (unsigned long long) server.cluster->currentEpoch);\n        return;\n    }\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))\n    {\n        if (nodeIsMaster(node)) {\n            serverLog(LL_WARNING,\n                    \"Failover auth denied to %.40s: it is a master node\",\n                    node->name);\n        } else if (master == NULL) {\n            serverLog(LL_WARNING,\n                    \"Failover auth denied to %.40s: I don't know its master\",\n                    node->name);\n        } else if (!nodeFailed(master)) {\n            serverLog(LL_WARNING,\n                    \"Failover auth denied to %.40s: its master is up\",\n                    node->name);\n        }\n        return;\n    }\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    if (mstime() - node->slaveof->voted_time < server.cluster_node_timeout * 2)\n    {\n        serverLog(LL_WARNING,\n                \"Failover auth denied to %.40s: \"\n                \"can't vote about this master before %lld milliseconds\",\n                node->name,\n                (long long) ((server.cluster_node_timeout*2)-\n                             (mstime() - node->slaveof->voted_time)));\n        return;\n    }\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 < CLUSTER_SLOTS; j++) {\n        if (bitmapTestBit(claimed_slots, j) == 0) continue;\n        if (server.cluster->slots[j] == NULL ||\n            server.cluster->slots[j]->configEpoch <= requestConfigEpoch)\n        {\n            continue;\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        serverLog(LL_WARNING,\n                \"Failover auth denied to %.40s: \"\n                \"slot %d epoch (%llu) > reqEpoch (%llu)\",\n                node->name, j,\n                (unsigned long long) server.cluster->slots[j]->configEpoch,\n                (unsigned long long) requestConfigEpoch);\n        return;\n    }\n\n    /* We can vote for this slave. */\n    server.cluster->lastVoteEpoch = server.cluster->currentEpoch;\n    node->slaveof->voted_time = mstime();\n    clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|CLUSTER_TODO_FSYNC_CONFIG);\n    clusterSendFailoverAuth(node);\n    serverLog(LL_WARNING, \"Failover auth granted to %.40s for epoch %llu\",\n        node->name, (unsigned long long) server.cluster->currentEpoch);\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    serverAssert(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            !nodeCantFailover(master->slaves[j]) &&\n            master->slaves[j]->repl_offset > myoffset) rank++;\n    return rank;\n}\n\n/* This function is called by clusterHandleSlaveFailover() in order to\n * let the slave log why it is not able to failover. Sometimes there are\n * not the conditions, but since the failover function is called again and\n * again, we can't log the same things continuously.\n *\n * This function works by logging only if a given set of conditions are\n * true:\n *\n * 1) The reason for which the failover can't be initiated changed.\n *    The reasons also include a NONE reason we reset the state to\n *    when the slave finds that its master is fine (no FAIL flag).\n * 2) Also, the log is emitted again if the master is still down and\n *    the reason for not failing over is still the same, but more than\n *    CLUSTER_CANT_FAILOVER_RELOG_PERIOD seconds elapsed.\n * 3) Finally, the function only logs if the slave is down for more than\n *    five seconds + NODE_TIMEOUT. This way nothing is logged when a\n *    failover starts in a reasonable time.\n *\n * The function is called with the reason why the slave can't failover\n * which is one of the integer macros CLUSTER_CANT_FAILOVER_*.\n *\n * The function is guaranteed to be called only if 'myself' is a slave. */\nvoid clusterLogCantFailover(int reason) {\n    char *msg;\n    static time_t lastlog_time = 0;\n    mstime_t nolog_fail_time = server.cluster_node_timeout + 5000;\n\n    /* Don't log if we have the same reason for some time. */\n    if (reason == server.cluster->cant_failover_reason &&\n        time(NULL)-lastlog_time < CLUSTER_CANT_FAILOVER_RELOG_PERIOD)\n        return;\n\n    server.cluster->cant_failover_reason = reason;\n\n    /* We also don't emit any log if the master failed no long ago, the\n     * goal of this function is to log slaves in a stalled condition for\n     * a long time. */\n    if (myself->slaveof &&\n        nodeFailed(myself->slaveof) &&\n        (mstime() - myself->slaveof->fail_time) < nolog_fail_time) return;\n\n    switch(reason) {\n    case CLUSTER_CANT_FAILOVER_DATA_AGE:\n        msg = \"Disconnected from master for longer than allowed. \"\n              \"Please check the 'cluster-replica-validity-factor' configuration \"\n              \"option.\";\n        break;\n    case CLUSTER_CANT_FAILOVER_WAITING_DELAY:\n        msg = \"Waiting the delay before I can start a new failover.\";\n        break;\n    case CLUSTER_CANT_FAILOVER_EXPIRED:\n        msg = \"Failover attempt expired.\";\n        break;\n    case CLUSTER_CANT_FAILOVER_WAITING_VOTES:\n        msg = \"Waiting for votes, but majority still not reached.\";\n        break;\n    default:\n        msg = \"Unknown reason code.\";\n        break;\n    }\n    lastlog_time = time(NULL);\n    serverLog(LL_WARNING,\"Currently unable to failover: %s\", msg);\n}\n\n/* This function implements the final part of automatic and manual failovers,\n * where the slave grabs its master's hash slots, and propagates the new\n * configuration.\n *\n * Note that it's up to the caller to be sure that the node got a new\n * configuration epoch already. */\nvoid clusterFailoverReplaceYourMaster(void) {\n    int j;\n    clusterNode *oldmaster = myself->slaveof;\n\n    if (nodeIsMaster(myself) || oldmaster == NULL) return;\n\n    /* 1) Turn this node into a master. */\n    clusterSetNodeAsMaster(myself);\n    replicationUnsetMaster();\n\n    /* 2) Claim all the slots assigned to our master. */\n    for (j = 0; j < CLUSTER_SLOTS; j++) {\n        if (clusterNodeGetSlotBit(oldmaster,j)) {\n            clusterDelSlot(j);\n            clusterAddSlot(myself,j);\n        }\n    }\n\n    /* 3) Update state and save config. */\n    clusterUpdateState();\n    clusterSaveConfigOrDie(1);\n\n    /* 4) Pong all the other nodes so that they can update the state\n     *    accordingly and detect that we switched to master role. */\n    clusterBroadcastPong(CLUSTER_BROADCAST_ALL);\n\n    /* 5) If there was a manual failover in progress, clear the state. */\n    resetManualFailover();\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 * The gaol of this function is:\n * 1) To check if we are able to perform a failover, is our data updated?\n * 2) Try to get elected by masters.\n * 3) Perform the failover informing all the other nodes.\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    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 trying to get voted again).\n     *\n     * Timeout is MAX(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) We don't have the no failover configuration set, and this is\n     *    not a manual failover.\n     * 4) It is serving slots. */\n    if (nodeIsMaster(myself) ||\n        myself->slaveof == NULL ||\n        (!nodeFailed(myself->slaveof) && !manual_failover) ||\n        (server.cluster_slave_no_failover && !manual_failover) ||\n        myself->slaveof->numslots == 0)\n    {\n        /* There are no reasons to failover, so we set the reason why we\n         * are returning without failing over to NONE. */\n        server.cluster->cant_failover_reason = CLUSTER_CANT_FAILOVER_NONE;\n        return;\n    }\n\n    /* Set data_age to the number of seconds we are disconnected from\n     * the master. */\n    if (server.repl_state == REPL_STATE_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    if (data_age > server.cluster_node_timeout)\n        data_age -= server.cluster_node_timeout;\n\n    /* Check if our data is recent enough according to the slave validity\n     * factor configured by the user.\n     *\n     * Check bypassed for manual failovers. */\n    if (server.cluster_slave_validity_factor &&\n        data_age >\n        (((mstime_t)server.repl_ping_slave_period * 1000) +\n         (server.cluster_node_timeout * server.cluster_slave_validity_factor)))\n    {\n        if (!manual_failover) {\n            clusterLogCantFailover(CLUSTER_CANT_FAILOVER_DATA_AGE);\n            return;\n        }\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\t    clusterDoBeforeSleep(CLUSTER_TODO_HANDLE_FAILOVER);\n        }\n        serverLog(LL_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            serverLog(LL_WARNING,\n                \"Replica 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    if (mstime() < server.cluster->failover_auth_time) {\n        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_WAITING_DELAY);\n        return;\n    }\n\n    /* Return ASAP if the election is too old to be valid. */\n    if (auth_age > auth_timeout) {\n        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_EXPIRED);\n        return;\n    }\n\n    /* Ask for votes if needed. */\n    if (server.cluster->failover_auth_sent == 0) {\n        server.cluster->currentEpoch++;\n        server.cluster->failover_auth_epoch = server.cluster->currentEpoch;\n        serverLog(LL_WARNING,\"Starting a failover election for epoch %llu.\",\n            (unsigned long long) server.cluster->currentEpoch);\n        clusterRequestFailoverAuth();\n        server.cluster->failover_auth_sent = 1;\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    if (server.cluster->failover_auth_count >= needed_quorum) {\n        /* We have the quorum, we can finally failover the master. */\n\n        serverLog(LL_WARNING,\n            \"Failover election won: I'm the new master.\");\n\n        /* Update my configEpoch to the epoch of the election. */\n        if (myself->configEpoch < server.cluster->failover_auth_epoch) {\n            myself->configEpoch = server.cluster->failover_auth_epoch;\n            serverLog(LL_WARNING,\n                \"configEpoch set to %llu after successful failover\",\n                (unsigned long long) myself->configEpoch);\n        }\n\n        /* Take responsibility for the cluster slots. */\n        clusterFailoverReplaceYourMaster();\n    } else {\n        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_WAITING_VOTES);\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 != 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: Identify 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     * smallest node ID (the \"candidate slave\").\n     *\n     * Note: this means that eventually a replica migration will occur\n     * since slaves that are reachable again always have their FAIL flag\n     * cleared, so eventually there must be a candidate. At the same time\n     * this does not mean that there are no race conditions possible (two\n     * slaves migrating at the same time), but this is unlikely to\n     * happen, and harmless when happens. */\n    candidate = myself;\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        int okslaves = 0, is_orphaned = 1;\n\n        /* We want to migrate only if this master is working, orphaned, and\n         * used to have slaves or if failed over a master that had slaves\n         * (MIGRATE_TO flag). This way we only migrate to instances that were\n         * supposed to have replicas. */\n        if (nodeIsSlave(node) || nodeFailed(node)) is_orphaned = 0;\n        if (!(node->flags & CLUSTER_NODE_MIGRATE_TO)) is_orphaned = 0;\n\n        /* Check number of working slaves. */\n        if (nodeIsMaster(node)) okslaves = clusterCountNonFailingSlaves(node);\n        if (okslaves > 0) is_orphaned = 0;\n\n        if (is_orphaned) {\n            if (!target && node->numslots > 0) target = node;\n\n            /* Track the starting time of the orphaned condition for this\n             * master. */\n            if (!node->orphaned_time) node->orphaned_time = mstime();\n        } else {\n            node->orphaned_time = 0;\n        }\n\n        /* Check if I'm the slave candidate for the migration: attached\n         * to a master with the maximum number of slaves and with the smallest\n         * node ID. */\n        if (okslaves == max_slaves) {\n            for (j = 0; j < node->numslaves; j++) {\n                if (memcmp(node->slaves[j]->name,\n                           candidate->name,\n                           CLUSTER_NAMELEN) < 0)\n                {\n                    candidate = node->slaves[j];\n                }\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Step 4: perform the migration if there is a target, and if I'm the\n     * candidate, but only if the master is continuously orphaned for a\n     * couple of seconds, so that during failovers, we give some time to\n     * the natural slaves of this instance to advertise their switch from\n     * the old master to the new one. */\n    if (target && candidate == myself &&\n        (mstime()-target->orphaned_time) > CLUSTER_SLAVE_MIGRATION_DELAY &&\n       !(server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_FAILOVER))\n    {\n        serverLog(LL_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 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 * The function can be used both to initialize the manual failover state at\n * startup or to abort a manual failover in progress. */\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        serverLog(LL_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 already 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        serverLog(LL_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 */\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    static unsigned long long iteration = 0;\n    mstime_t handshake_timeout;\n\n    iteration++; /* Number of times this function was called so far. */\n\n    /* We want to take myself->ip in sync with the cluster-announce-ip option.\n     * The option can be set at runtime via CONFIG SET, so we periodically check\n     * if the option changed to reflect this into myself->ip. */\n    {\n        static char *prev_ip = NULL;\n        char *curr_ip = server.cluster_announce_ip;\n        int changed = 0;\n\n        if (prev_ip == NULL && curr_ip != NULL) changed = 1;\n        else if (prev_ip != NULL && curr_ip == NULL) changed = 1;\n        else if (prev_ip && curr_ip && strcmp(prev_ip,curr_ip)) changed = 1;\n\n        if (changed) {\n            if (prev_ip) zfree(prev_ip);\n            prev_ip = curr_ip;\n\n            if (curr_ip) {\n                /* We always take a copy of the previous IP address, by\n                 * duplicating the string. This way later we can check if\n                 * the address really changed. */\n                prev_ip = zstrdup(prev_ip);\n                strncpy(myself->ip,server.cluster_announce_ip,NET_IP_STR_LEN);\n                myself->ip[NET_IP_STR_LEN-1] = '\\0';\n            } else {\n                myself->ip[0] = '\\0'; /* Force autodetection. */\n            }\n        }\n    }\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_timeout = server.cluster_node_timeout;\n    if (handshake_timeout < 1000) handshake_timeout = 1000;\n\n    /* Update myself flags. */\n    clusterUpdateMyselfFlags();\n\n    /* Check if we have disconnected nodes and re-establish the connection.\n     * Also update a few stats while we are here, that can be used to make\n     * better decisions in other part of the code. */\n    di = dictGetSafeIterator(server.cluster->nodes);\n    server.cluster->stats_pfail_nodes = 0;\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        /* Not interested in reconnecting the link with myself or nodes\n         * for which we have no address. */\n        if (node->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_NOADDR)) continue;\n\n        if (node->flags & CLUSTER_NODE_PFAIL)\n            server.cluster->stats_pfail_nodes++;\n\n        /* A Node in HANDSHAKE state has a limited lifespan equal to the\n         * configured node timeout. */\n        if (nodeInHandshake(node) && now - node->ctime > handshake_timeout) {\n            clusterDelNode(node);\n            continue;\n        }\n\n        if (node->link == NULL) {\n            clusterLink *link = createClusterLink(node);\n            link->conn = server.tls_cluster ? connCreateTLS() : connCreateSocket();\n            connSetPrivateData(link->conn, link);\n            if (connConnect(link->conn, node->ip, node->cport, NET_FIRST_BIND_ADDR,\n                        clusterLinkConnectHandler) == -1) {\n                /* We got a synchronous error from connect before\n                 * clusterSendPing() had a chance to be called.\n                 * If node->ping_sent is zero, failure detection can't work,\n                 * so we claim we actually sent a ping now (that will\n                 * be really sent as soon as the link is obtained). */\n                if (node->ping_sent == 0) node->ping_sent = mstime();\n                serverLog(LL_DEBUG, \"Unable to connect to \"\n                    \"Cluster Node [%s]:%d -> %s\", node->ip,\n                    node->cport, server.neterr);\n\n                freeClusterLink(link);\n                continue;\n            }\n            node->link = link;\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    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        for (j = 0; j < 5; j++) {\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            if (this->link == NULL || this->ping_sent != 0) continue;\n            if (this->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_HANDSHAKE))\n                continue;\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        if (min_pong_node) {\n            serverLog(LL_DEBUG,\"Pinging node %.40s\", min_pong_node->name);\n            clusterSendPing(min_pong_node->link, CLUSTERMSG_TYPE_PING);\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\n        if (node->flags &\n            (CLUSTER_NODE_MYSELF|CLUSTER_NODE_NOADDR|CLUSTER_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            /* A master is orphaned if it is serving a non-zero number of\n             * slots, have no working slaves, but used to have at least one\n             * slave, or failed over a master that used to have slaves. */\n            if (okslaves == 0 && node->numslots > 0 &&\n                node->flags & CLUSTER_NODE_MIGRATE_TO)\n            {\n                orphaned_masters++;\n            }\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 not receiving any data for more than half the cluster\n         * timeout, reconnect the link: maybe there is a connection\n         * issue even if the node is alive. */\n        mstime_t ping_delay = now - node->ping_sent;\n        mstime_t data_delay = now - node->data_received;\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            ping_delay > server.cluster_node_timeout/2 &&\n            /* and in such interval we are not seeing any traffic at all. */\n            data_delay > server.cluster_node_timeout/2)\n        {\n            /* Disconnect the link, it will be reconnected automatically. */\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        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        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        if (node->ping_sent == 0) continue;\n\n        /* Check if this node looks unreachable.\n         * Note that if we already received the PONG, then node->ping_sent\n         * is zero, so can't reach this code at all, so we don't risk of\n         * checking for a PONG delay if we didn't sent the PING.\n         *\n         * We also consider every incoming data as proof of liveness, since\n         * our cluster bus link is also used for data: under heavy data\n         * load pong delays are possible. */\n        mstime_t node_delay = (ping_delay < data_delay) ? ping_delay :\n                                                          data_delay;\n\n        if (node_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 & (CLUSTER_NODE_PFAIL|CLUSTER_NODE_FAIL))) {\n                serverLog(LL_DEBUG,\"*** NODE %.40s possibly failing\",\n                    node->name);\n                node->flags |= CLUSTER_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    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        if (!(server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_FAILOVER))\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    if (update_state || server.cluster->state == 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. */\nvoid clusterBeforeSleep(void) {\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    if (server.cluster->todo_before_sleep & CLUSTER_TODO_HANDLE_FAILOVER)\n        clusterHandleSlaveFailover();\n\n    /* Update the cluster state. */\n    if (server.cluster->todo_before_sleep & CLUSTER_TODO_UPDATE_STATE)\n        clusterUpdateState();\n\n    /* Save the config, possibly using fsync. */\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\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. */\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. */\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. */\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/* Return non-zero if there is at least one master with slaves in the cluster.\n * Otherwise zero is returned. Used by clusterNodeSetSlotBit() to set the\n * MIGRATE_TO flag the when a master gets the first slot. */\nint clusterMastersHaveSlaves(void) {\n    dictIterator *di = dictGetSafeIterator(server.cluster->nodes);\n    dictEntry *de;\n    int slaves = 0;\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (nodeIsSlave(node)) continue;\n        slaves += node->numslaves;\n    }\n    dictReleaseIterator(di);\n    return slaves != 0;\n}\n\n/* Set the slot bit and return the old value. */\nint clusterNodeSetSlotBit(clusterNode *n, int slot) {\n    int old = bitmapTestBit(n->slots,slot);\n    bitmapSetBit(n->slots,slot);\n    if (!old) {\n        n->numslots++;\n        /* When a master gets its first slot, even if it has no slaves,\n         * it gets flagged with MIGRATE_TO, that is, the master is a valid\n         * target for replicas migration, if and only if at least one of\n         * the other masters has slaves right now.\n         *\n         * Normally masters are valid targerts of replica migration if:\n         * 1. The used to have slaves (but no longer have).\n         * 2. They are slaves failing over a master that used to have slaves.\n         *\n         * However new masters with slots assigned are considered valid\n         * migration tagets if the rest of the cluster is not a slave-less.\n         *\n         * See https://github.com/antirez/redis/issues/3043 for more info. */\n        if (n->numslots == 1 && clusterMastersHaveSlaves())\n            n->flags |= CLUSTER_NODE_MIGRATE_TO;\n    }\n    return old;\n}\n\n/* Clear the slot bit and return the old value. */\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. */\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 C_OK if the operation ended with success.\n * If the slot is already assigned to another instance this is considered\n * an error and C_ERR is returned. */\nint clusterAddSlot(clusterNode *n, int slot) {\n    if (server.cluster->slots[slot]) return C_ERR;\n    clusterNodeSetSlotBit(n,slot);\n    server.cluster->slots[slot] = n;\n    return C_OK;\n}\n\n/* Delete the specified slot marking it as unassigned.\n * Returns C_OK if the slot was assigned, otherwise if the slot was\n * already unassigned C_ERR is returned. */\nint clusterDelSlot(int slot) {\n    clusterNode *n = server.cluster->slots[slot];\n\n    if (!n) return C_ERR;\n    serverAssert(clusterNodeClearSlotBit(n,slot) == 1);\n    server.cluster->slots[slot] = NULL;\n    return C_OK;\n}\n\n/* Delete all the slots associated with the specified node.\n * The number of deleted slots is returned. */\nint clusterDelNodeSlots(clusterNode *node) {\n    int deleted = 0, j;\n\n    for (j = 0; j < CLUSTER_SLOTS; j++) {\n        if (clusterNodeGetSlotBit(node,j)) {\n            clusterDelSlot(j);\n            deleted++;\n        }\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. */\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. Actually 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 CLUSTER_MAX_REJOIN_DELAY 5000\n#define CLUSTER_MIN_REJOIN_DELAY 500\n#define CLUSTER_WRITABLE_DELAY 2000\n\nvoid clusterUpdateState(void) {\n    int j, new_state;\n    int reachable_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        server.cluster->state == CLUSTER_FAIL &&\n        mstime() - first_call_time < 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    new_state = CLUSTER_OK;\n\n    /* Check if all the slots are covered. */\n    if (server.cluster_require_full_coverage) {\n        for (j = 0; j < CLUSTER_SLOTS; j++) {\n            if (server.cluster->slots[j] == NULL ||\n                server.cluster->slots[j]->flags & (CLUSTER_NODE_FAIL))\n            {\n                new_state = CLUSTER_FAIL;\n                break;\n            }\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 reachable masters having\n     * at least one slot. */\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 & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) == 0)\n                    reachable_masters++;\n            }\n        }\n        dictReleaseIterator(di);\n    }\n\n    /* If we are in a minority partition, change the cluster state\n     * to FAIL. */\n    {\n        int needed_quorum = (server.cluster->size / 2) + 1;\n\n        if (reachable_masters < needed_quorum) {\n            new_state = CLUSTER_FAIL;\n            among_minority_time = mstime();\n        }\n    }\n\n    /* Log a state change */\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 > CLUSTER_MAX_REJOIN_DELAY)\n            rejoin_delay = CLUSTER_MAX_REJOIN_DELAY;\n        if (rejoin_delay < CLUSTER_MIN_REJOIN_DELAY)\n            rejoin_delay = CLUSTER_MIN_REJOIN_DELAY;\n\n        if (new_state == 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        serverLog(LL_WARNING,\"Cluster state changed: %s\",\n            new_state == CLUSTER_OK ? \"ok\" : \"fail\");\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 C_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 C_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, C_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. */\nint verifyClusterConfigWithData(void) {\n    int j;\n    int update_config = 0;\n\n    /* Return ASAP if a module disabled cluster redirections. In that case\n     * every master can store keys about every possible hash slot. */\n    if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)\n        return C_OK;\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    if (nodeIsSlave(myself)) return C_OK;\n\n    /* Make sure we only have keys in DB0. */\n    for (j = 1; j < server.dbnum; j++) {\n        if (dictSize(server.db[j].dict)) return C_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    for (j = 0; j < 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        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 responsibility for it. */\n        if (server.cluster->slots[j] == NULL) {\n            serverLog(LL_WARNING, \"I have keys for unassigned slot %d. \"\n                                    \"Taking responsibility for it.\",j);\n            clusterAddSlot(myself,j);\n        } else {\n            serverLog(LL_WARNING, \"I have keys for slot %d, but the slot is \"\n                                    \"assigned to another node. \"\n                                    \"Setting it to importing state.\",j);\n            server.cluster->importing_slots_from[j] = server.cluster->slots[j];\n        }\n    }\n    if (update_config) clusterSaveConfigOrDie(1);\n    return C_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. */\nvoid clusterSetMaster(clusterNode *n) {\n    serverAssert(n != myself);\n    serverAssert(myself->numslots == 0);\n\n    if (nodeIsMaster(myself)) {\n        myself->flags &= ~(CLUSTER_NODE_MASTER|CLUSTER_NODE_MIGRATE_TO);\n        myself->flags |= CLUSTER_NODE_SLAVE;\n        clusterCloseAllSlots();\n    } else {\n        if (myself->slaveof)\n            clusterNodeRemoveSlave(myself->slaveof,myself);\n    }\n    myself->slaveof = n;\n    clusterNodeAddSlave(n,myself);\n    replicationSetMaster(n->ip, n->port);\n    resetManualFailover();\n}\n\n/* -----------------------------------------------------------------------------\n * Nodes to string representation functions.\n * -------------------------------------------------------------------------- */\n\nstruct redisNodeFlags {\n    uint16_t flag;\n    char *name;\n};\n\nstatic struct redisNodeFlags redisNodeFlagsTable[] = {\n    {CLUSTER_NODE_MYSELF,       \"myself,\"},\n    {CLUSTER_NODE_MASTER,       \"master,\"},\n    {CLUSTER_NODE_SLAVE,        \"slave,\"},\n    {CLUSTER_NODE_PFAIL,        \"fail?,\"},\n    {CLUSTER_NODE_FAIL,         \"fail,\"},\n    {CLUSTER_NODE_HANDSHAKE,    \"handshake,\"},\n    {CLUSTER_NODE_NOADDR,       \"noaddr,\"},\n    {CLUSTER_NODE_NOFAILOVER,   \"nofailover,\"}\n};\n\n/* Concatenate the comma separated list of node flags to the given SDS\n * string 'ci'. */\nsds representClusterNodeFlags(sds ci, uint16_t flags) {\n    size_t orig_len = sdslen(ci);\n    int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);\n    for (i = 0; i < size; i++) {\n        struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;\n        if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);\n    }\n    /* If no flag was added, add the \"noflags\" special flag. */\n    if (sdslen(ci) == orig_len) ci = sdscat(ci,\"noflags,\");\n    sdsIncrLen(ci,-1); /* Remove trailing comma. */\n    return ci;\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. */\nsds clusterGenNodeDescription(clusterNode *node) {\n    int j, start;\n    sds ci;\n\n    /* Node coordinates */\n    ci = sdscatprintf(sdsempty(),\"%.40s %s:%d@%d \",\n        node->name,\n        node->ip,\n        node->port,\n        node->cport);\n\n    /* Flags */\n    ci = representClusterNodeFlags(ci, node->flags);\n\n    /* Slave of... or just \"-\" */\n    if (node->slaveof)\n        ci = sdscatprintf(ci,\" %.40s \",node->slaveof->name);\n    else\n        ci = sdscatlen(ci,\" - \",3);\n\n    /* Latency from the POV of this node, config epoch, 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 & CLUSTER_NODE_MYSELF) ?\n                    \"connected\" : \"disconnected\");\n\n    /* Slots served by this instance */\n    start = -1;\n    for (j = 0; j < 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 == CLUSTER_SLOTS-1)) {\n            if (bit && j == 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 & CLUSTER_NODE_MYSELF) {\n        for (j = 0; j < 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 * 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 * 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. */\nsds clusterGenNodesDescription(int filter) {\n    sds ci = sdsempty(), ni;\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (node->flags & filter) continue;\n        ni = clusterGenNodeDescription(node);\n        ci = sdscatsds(ci,ni);\n        sdsfree(ni);\n        ci = sdscatlen(ci,\"\\n\",1);\n    }\n    dictReleaseIterator(di);\n    return ci;\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER command\n * -------------------------------------------------------------------------- */\n\nconst char *clusterGetMessageTypeString(int type) {\n    switch(type) {\n    case CLUSTERMSG_TYPE_PING: return \"ping\";\n    case CLUSTERMSG_TYPE_PONG: return \"pong\";\n    case CLUSTERMSG_TYPE_MEET: return \"meet\";\n    case CLUSTERMSG_TYPE_FAIL: return \"fail\";\n    case CLUSTERMSG_TYPE_PUBLISH: return \"publish\";\n    case CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST: return \"auth-req\";\n    case CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK: return \"auth-ack\";\n    case CLUSTERMSG_TYPE_UPDATE: return \"update\";\n    case CLUSTERMSG_TYPE_MFSTART: return \"mfstart\";\n    case CLUSTERMSG_TYPE_MODULE: return \"module\";\n    }\n    return \"unknown\";\n}\n\nint getSlotOrReply(client *c, robj *o) {\n    long long slot;\n\n    if (getLongLongFromObject(o,&slot) != C_OK ||\n        slot < 0 || slot >= CLUSTER_SLOTS)\n    {\n        addReplyError(c,\"Invalid or out of range slot\");\n        return -1;\n    }\n    return (int) slot;\n}\n\nvoid clusterReplyMultiBulkSlots(client *c) {\n    /* Format: 1) 1) start slot\n     *            2) end slot\n     *            3) 1) master IP\n     *               2) master port\n     *               3) node ID\n     *            4) 1) replica IP\n     *               2) replica port\n     *               3) node ID\n     *           ... continued until done\n     */\n\n    int num_masters = 0;\n    void *slot_replylen = addReplyDeferredLen(c);\n\n    dictEntry *de;\n    dictIterator *di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        int j = 0, start = -1;\n        int i, nested_elements = 0;\n\n        /* Skip slaves (that are iterated when producing the output of their\n         * master) and  masters not serving any slot. */\n        if (!nodeIsMaster(node) || node->numslots == 0) continue;\n\n        for(i = 0; i < node->numslaves; i++) {\n            if (nodeFailed(node->slaves[i])) continue;\n            nested_elements++;\n        }\n\n        for (j = 0; j < CLUSTER_SLOTS; j++) {\n            int bit, i;\n\n            if ((bit = clusterNodeGetSlotBit(node,j)) != 0) {\n                if (start == -1) start = j;\n            }\n            if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) {\n                addReplyArrayLen(c, nested_elements + 3); /* slots (2) + master addr (1). */\n\n                if (bit && j == CLUSTER_SLOTS-1) j++;\n\n                /* If slot exists in output map, add to it's list.\n                 * else, create a new output map for this slot */\n                if (start == j-1) {\n                    addReplyLongLong(c, start); /* only one slot; low==high */\n                    addReplyLongLong(c, start);\n                } else {\n                    addReplyLongLong(c, start); /* low */\n                    addReplyLongLong(c, j-1);   /* high */\n                }\n                start = -1;\n\n                /* First node reply position is always the master */\n                addReplyArrayLen(c, 3);\n                addReplyBulkCString(c, node->ip);\n                addReplyLongLong(c, node->port);\n                addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);\n\n                /* Remaining nodes in reply are replicas for slot range */\n                for (i = 0; i < node->numslaves; i++) {\n                    /* This loop is copy/pasted from clusterGenNodeDescription()\n                     * with modifications for per-slot node aggregation */\n                    if (nodeFailed(node->slaves[i])) continue;\n                    addReplyArrayLen(c, 3);\n                    addReplyBulkCString(c, node->slaves[i]->ip);\n                    addReplyLongLong(c, node->slaves[i]->port);\n                    addReplyBulkCBuffer(c, node->slaves[i]->name, CLUSTER_NAMELEN);\n                }\n                num_masters++;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n    setDeferredArrayLen(c, slot_replylen, num_masters);\n}\n\nvoid clusterCommand(client *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ADDSLOTS <slot> [slot ...] -- Assign slots to current node.\",\n\"BUMPEPOCH -- Advance the cluster config epoch.\",\n\"COUNT-failure-reports <node-id> -- Return number of failure reports for <node-id>.\",\n\"COUNTKEYSINSLOT <slot> - Return the number of keys in <slot>.\",\n\"DELSLOTS <slot> [slot ...] -- Delete slots information from current node.\",\n\"FAILOVER [force|takeover] -- Promote current replica node to being a master.\",\n\"FORGET <node-id> -- Remove a node from the cluster.\",\n\"GETKEYSINSLOT <slot> <count> -- Return key names stored by current node in a slot.\",\n\"FLUSHSLOTS -- Delete current node own slots information.\",\n\"INFO - Return information about the cluster.\",\n\"KEYSLOT <key> -- Return the hash slot for <key>.\",\n\"MEET <ip> <port> [bus-port] -- Connect nodes into a working cluster.\",\n\"MYID -- Return the node id.\",\n\"NODES -- Return cluster configuration seen by node. Output format:\",\n\"    <id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ... <slot>\",\n\"REPLICATE <node-id> -- Configure current node as replica to <node-id>.\",\n\"RESET [hard|soft] -- Reset current node (default: soft).\",\n\"SET-config-epoch <epoch> - Set config epoch of current node.\",\n\"SETSLOT <slot> (importing|migrating|stable|node <node-id>) -- Set slot state.\",\n\"REPLICAS <node-id> -- Return <node-id> replicas.\",\n\"SAVECONFIG - Force saving cluster configuration on disk.\",\n\"SLOTS -- Return information about slots range mappings. Each range is made of:\",\n\"    start, end, master and replicas IP addresses, ports and ids\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"meet\") && (c->argc == 4 || c->argc == 5)) {\n        /* CLUSTER MEET <ip> <port> [cport] */\n        long long port, cport;\n\n        if (getLongLongFromObject(c->argv[3], &port) != C_OK) {\n            addReplyErrorFormat(c,\"Invalid TCP base port specified: %s\",\n                                (char*)c->argv[3]->ptr);\n            return;\n        }\n\n        if (c->argc == 5) {\n            if (getLongLongFromObject(c->argv[4], &cport) != C_OK) {\n                addReplyErrorFormat(c,\"Invalid TCP bus port specified: %s\",\n                                    (char*)c->argv[4]->ptr);\n                return;\n            }\n        } else {\n            cport = port + CLUSTER_PORT_INCR;\n        }\n\n        if (clusterStartHandshake(c->argv[2]->ptr,port,cport) == 0 &&\n            errno == EINVAL)\n        {\n            addReplyErrorFormat(c,\"Invalid node address specified: %s:%s\",\n                            (char*)c->argv[2]->ptr, (char*)c->argv[3]->ptr);\n        } else {\n            addReply(c,shared.ok);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"nodes\") && c->argc == 2) {\n        /* CLUSTER NODES */\n        sds nodes = clusterGenNodesDescription(0);\n        addReplyVerbatim(c,nodes,sdslen(nodes),\"txt\");\n        sdsfree(nodes);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"myid\") && c->argc == 2) {\n        /* CLUSTER MYID */\n        addReplyBulkCBuffer(c,myself->name, CLUSTER_NAMELEN);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"slots\") && c->argc == 2) {\n        /* CLUSTER SLOTS */\n        clusterReplyMultiBulkSlots(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"flushslots\") && c->argc == 2) {\n        /* CLUSTER FLUSHSLOTS */\n        if (dictSize(server.db[0].dict) != 0) {\n            addReplyError(c,\"DB must be empty to perform CLUSTER FLUSHSLOTS.\");\n            return;\n        }\n        clusterDelNodeSlots(myself);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\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        /* CLUSTER DELSLOTS <slot> [slot] ... */\n        int j, slot;\n        unsigned char *slots = zmalloc(CLUSTER_SLOTS);\n        int del = !strcasecmp(c->argv[1]->ptr,\"delslots\");\n\n        memset(slots,0,CLUSTER_SLOTS);\n        /* Check that all the arguments are parseable and that all the\n         * slots are not already busy. */\n        for (j = 2; j < c->argc; j++) {\n            if ((slot = getSlotOrReply(c,c->argv[j])) == -1) {\n                zfree(slots);\n                return;\n            }\n            if (del && server.cluster->slots[slot] == NULL) {\n                addReplyErrorFormat(c,\"Slot %d is already unassigned\", slot);\n                zfree(slots);\n                return;\n            } else if (!del && server.cluster->slots[slot]) {\n                addReplyErrorFormat(c,\"Slot %d is already busy\", slot);\n                zfree(slots);\n                return;\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        for (j = 0; j < 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                if (server.cluster->importing_slots_from[j])\n                    server.cluster->importing_slots_from[j] = NULL;\n\n                retval = del ? clusterDelSlot(j) :\n                               clusterAddSlot(myself,j);\n                serverAssertWithInfo(c,NULL,retval == C_OK);\n            }\n        }\n        zfree(slots);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\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        if (nodeIsSlave(myself)) {\n            addReplyError(c,\"Please use SETSLOT only with masters.\");\n            return;\n        }\n\n        if ((slot = getSlotOrReply(c,c->argv[2])) == -1) return;\n\n        if (!strcasecmp(c->argv[3]->ptr,\"migrating\") && c->argc == 5) {\n            if (server.cluster->slots[slot] != myself) {\n                addReplyErrorFormat(c,\"I'm not the owner of hash slot %u\",slot);\n                return;\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            server.cluster->migrating_slots_to[slot] = n;\n        } else if (!strcasecmp(c->argv[3]->ptr,\"importing\") && c->argc == 5) {\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            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            server.cluster->importing_slots_from[slot] = n;\n        } else if (!strcasecmp(c->argv[3]->ptr,\"stable\") && c->argc == 4) {\n            /* CLUSTER SETSLOT <SLOT> STABLE */\n            server.cluster->importing_slots_from[slot] = NULL;\n            server.cluster->migrating_slots_to[slot] = NULL;\n        } else if (!strcasecmp(c->argv[3]->ptr,\"node\") && c->argc == 5) {\n            /* CLUSTER SETSLOT <SLOT> NODE <NODE ID> */\n            clusterNode *n = clusterLookupNode(c->argv[4]->ptr);\n\n            if (!n) {\n                addReplyErrorFormat(c,\"Unknown node %s\",\n                    (char*)c->argv[4]->ptr);\n                return;\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            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            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                if (clusterBumpConfigEpochWithoutConsensus() == C_OK) {\n                    serverLog(LL_WARNING,\n                        \"configEpoch updated after importing slot %d\", slot);\n                }\n                server.cluster->importing_slots_from[slot] = NULL;\n            }\n            clusterDelSlot(slot);\n            clusterAddSlot(n,slot);\n        } else {\n            addReplyError(c,\n                \"Invalid CLUSTER SETSLOT action or number of arguments. Try CLUSTER HELP\");\n            return;\n        }\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|CLUSTER_TODO_UPDATE_STATE);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"bumpepoch\") && c->argc == 2) {\n        /* CLUSTER BUMPEPOCH */\n        int retval = clusterBumpConfigEpochWithoutConsensus();\n        sds reply = sdscatprintf(sdsempty(),\"+%s %llu\\r\\n\",\n                (retval == C_OK) ? \"BUMPED\" : \"STILL\",\n                (unsigned long long) myself->configEpoch);\n        addReplySds(c,reply);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"info\") && c->argc == 2) {\n        /* CLUSTER INFO */\n        char *statestr[] = {\"ok\",\"fail\",\"needhelp\"};\n        int slots_assigned = 0, slots_ok = 0, slots_pfail = 0, slots_fail = 0;\n        uint64_t myepoch;\n        int j;\n\n        for (j = 0; j < CLUSTER_SLOTS; j++) {\n            clusterNode *n = server.cluster->slots[j];\n\n            if (n == NULL) continue;\n            slots_assigned++;\n            if (nodeFailed(n)) {\n                slots_fail++;\n            } else if (nodeTimedOut(n)) {\n                slots_pfail++;\n            } else {\n                slots_ok++;\n            }\n        }\n\n        myepoch = (nodeIsSlave(myself) && myself->slaveof) ?\n                  myself->slaveof->configEpoch : myself->configEpoch;\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_my_epoch:%llu\\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            (unsigned long long) myepoch\n        );\n\n        /* Show stats about messages sent and received. */\n        long long tot_msg_sent = 0;\n        long long tot_msg_received = 0;\n\n        for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {\n            if (server.cluster->stats_bus_messages_sent[i] == 0) continue;\n            tot_msg_sent += server.cluster->stats_bus_messages_sent[i];\n            info = sdscatprintf(info,\n                \"cluster_stats_messages_%s_sent:%lld\\r\\n\",\n                clusterGetMessageTypeString(i),\n                server.cluster->stats_bus_messages_sent[i]);\n        }\n        info = sdscatprintf(info,\n            \"cluster_stats_messages_sent:%lld\\r\\n\", tot_msg_sent);\n\n        for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {\n            if (server.cluster->stats_bus_messages_received[i] == 0) continue;\n            tot_msg_received += server.cluster->stats_bus_messages_received[i];\n            info = sdscatprintf(info,\n                \"cluster_stats_messages_%s_received:%lld\\r\\n\",\n                clusterGetMessageTypeString(i),\n                server.cluster->stats_bus_messages_received[i]);\n        }\n        info = sdscatprintf(info,\n            \"cluster_stats_messages_received:%lld\\r\\n\", tot_msg_received);\n\n        /* Produce the reply protocol. */\n        addReplyVerbatim(c,info,sdslen(info),\"txt\");\n        sdsfree(info);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"saveconfig\") && c->argc == 2) {\n        int retval = clusterSaveConfig(1);\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    } else if (!strcasecmp(c->argv[1]->ptr,\"keyslot\") && c->argc == 3) {\n        /* CLUSTER KEYSLOT <key> */\n        sds key = c->argv[2]->ptr;\n\n        addReplyLongLong(c,keyHashSlot(key,sdslen(key)));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"countkeysinslot\") && c->argc == 3) {\n        /* CLUSTER COUNTKEYSINSLOT <slot> */\n        long long slot;\n\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&slot,NULL) != C_OK)\n            return;\n        if (slot < 0 || slot >= CLUSTER_SLOTS) {\n            addReplyError(c,\"Invalid slot\");\n            return;\n        }\n        addReplyLongLong(c,countKeysInSlot(slot));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getkeysinslot\") && c->argc == 4) {\n        /* CLUSTER GETKEYSINSLOT <slot> <count> */\n        long long maxkeys, slot;\n        unsigned int numkeys, j;\n        robj **keys;\n\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&slot,NULL) != C_OK)\n            return;\n        if (getLongLongFromObjectOrReply(c,c->argv[3],&maxkeys,NULL)\n            != C_OK)\n            return;\n        if (slot < 0 || slot >= CLUSTER_SLOTS || maxkeys < 0) {\n            addReplyError(c,\"Invalid slot or number of keys\");\n            return;\n        }\n\n        /* Avoid allocating more than needed in case of large COUNT argument\n         * and smaller actual number of keys. */\n        unsigned int keys_in_slot = countKeysInSlot(slot);\n        if (maxkeys > keys_in_slot) maxkeys = keys_in_slot;\n\n        keys = zmalloc(sizeof(robj*)*maxkeys);\n        numkeys = getKeysInSlot(slot, keys, maxkeys);\n        addReplyArrayLen(c,numkeys);\n        for (j = 0; j < numkeys; j++) {\n            addReplyBulk(c,keys[j]);\n            decrRefCount(keys[j]);\n        }\n        zfree(keys);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"forget\") && c->argc == 3) {\n        /* CLUSTER FORGET <NODE ID> */\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\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        clusterBlacklistAddNode(n);\n        clusterDelNode(n);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|\n                             CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"replicate\") && c->argc == 3) {\n        /* CLUSTER REPLICATE <NODE ID> */\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        if (n == myself) {\n            addReplyError(c,\"Can't replicate myself\");\n            return;\n        }\n\n        /* Can't replicate a slave. */\n        if (nodeIsSlave(n)) {\n            addReplyError(c,\"I can only replicate a master, not a replica.\");\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        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        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\") ||\n                !strcasecmp(c->argv[1]->ptr,\"replicas\")) && c->argc == 3) {\n        /* CLUSTER SLAVES <NODE ID> */\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        addReplyArrayLen(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,\"count-failure-reports\") &&\n               c->argc == 3)\n    {\n        /* CLUSTER COUNT-FAILURE-REPORTS <NODE ID> */\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\n\n        if (!n) {\n            addReplyErrorFormat(c,\"Unknown node %s\", (char*)c->argv[2]->ptr);\n            return;\n        } else {\n            addReplyLongLong(c,clusterNodeFailureReportsCount(n));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"failover\") &&\n               (c->argc == 2 || c->argc == 3))\n    {\n        /* CLUSTER FAILOVER [FORCE|TAKEOVER] */\n        int force = 0, takeover = 0;\n\n        if (c->argc == 3) {\n            if (!strcasecmp(c->argv[2]->ptr,\"force\")) {\n                force = 1;\n            } else if (!strcasecmp(c->argv[2]->ptr,\"takeover\")) {\n                takeover = 1;\n                force = 1; /* Takeover also implies force. */\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n\n        /* Check preconditions. */\n        if (nodeIsMaster(myself)) {\n            addReplyError(c,\"You should send CLUSTER FAILOVER to a replica\");\n            return;\n        } else if (myself->slaveof == NULL) {\n            addReplyError(c,\"I'm a replica but my master is unknown to me\");\n            return;\n        } else if (!force &&\n                   (nodeFailed(myself->slaveof) ||\n                    myself->slaveof->link == NULL))\n        {\n            addReplyError(c,\"Master is down or failed, \"\n                            \"please use CLUSTER FAILOVER FORCE\");\n            return;\n        }\n        resetManualFailover();\n        server.cluster->mf_end = mstime() + CLUSTER_MF_TIMEOUT;\n\n        if (takeover) {\n            /* A takeover does not perform any initial check. It just\n             * generates a new configuration epoch for this node without\n             * consensus, claims the master's slots, and broadcast the new\n             * configuration. */\n            serverLog(LL_WARNING,\"Taking over the master (user request).\");\n            clusterBumpConfigEpochWithoutConsensus();\n            clusterFailoverReplaceYourMaster();\n        } else if (force) {\n            /* If this is a forced failover, we don't need to talk with our\n             * master to agree about the offset. We just failover taking over\n             * it without coordination. */\n            serverLog(LL_WARNING,\"Forced failover user request accepted.\");\n            server.cluster->mf_can_start = 1;\n        } else {\n            serverLog(LL_WARNING,\"Manual failover user request accepted.\");\n            clusterSendMFStart(myself->slaveof);\n        }\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) != C_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            serverLog(LL_WARNING,\n                \"configEpoch set to %llu via CLUSTER SET-CONFIG-EPOCH\",\n                (unsigned long long) myself->configEpoch);\n\n            if (server.cluster->currentEpoch < (uint64_t)epoch)\n                server.cluster->currentEpoch = 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        addReplySubcommandSyntaxError(c);\n        return;\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. */\nvoid createDumpPayload(rio *payload, robj *o, robj *key) {\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    rioInitWithBuffer(payload,sdsempty());\n    serverAssert(rdbSaveObjectType(payload,o));\n    serverAssert(rdbSaveObject(payload,o,key));\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    buf[0] = RDB_VERSION & 0xff;\n    buf[1] = (RDB_VERSION >> 8) & 0xff;\n    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,buf,2);\n\n    /* CRC64 */\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/* Verify that the RDB version of the dump payload matches the one of this Redis\n * instance and that the checksum is ok.\n * If the DUMP payload looks valid C_OK is returned, otherwise C_ERR\n * is returned. */\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    if (len < 10) return C_ERR;\n    footer = p+(len-10);\n\n    /* Verify RDB version */\n    rdbver = (footer[1] << 8) | footer[0];\n    if (rdbver > RDB_VERSION) return C_ERR;\n\n    /* Verify CRC64 */\n    crc = crc64(0,p,len-8);\n    memrev64ifbe(&crc);\n    return (memcmp(&crc,footer+2,8) == 0) ? C_OK : C_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(client *c) {\n    robj *o;\n    rio payload;\n\n    /* Check if the key is here. */\n    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {\n        addReplyNull(c);\n        return;\n    }\n\n    /* Create the DUMP encoded representation. */\n    createDumpPayload(&payload,o,c->argv[1]);\n\n    /* Transfer to the client */\n    addReplyBulkSds(c,payload.io.buffer.ptr);\n    return;\n}\n\n/* RESTORE key ttl serialized-value [REPLACE] */\nvoid restoreCommand(client *c) {\n    long long ttl, lfu_freq = -1, lru_idle = -1, lru_clock = -1;\n    rio payload;\n    int j, type, replace = 0, absttl = 0;\n    robj *obj;\n\n    /* Parse additional options */\n    for (j = 4; j < c->argc; j++) {\n        int additional = c->argc-j-1;\n        if (!strcasecmp(c->argv[j]->ptr,\"replace\")) {\n            replace = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"absttl\")) {\n            absttl = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"idletime\") && additional >= 1 &&\n                   lfu_freq == -1)\n        {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&lru_idle,NULL)\n                    != C_OK) return;\n            if (lru_idle < 0) {\n                addReplyError(c,\"Invalid IDLETIME value, must be >= 0\");\n                return;\n            }\n            lru_clock = LRU_CLOCK();\n            j++; /* Consume additional arg. */\n        } else if (!strcasecmp(c->argv[j]->ptr,\"freq\") && additional >= 1 &&\n                   lru_idle == -1)\n        {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&lfu_freq,NULL)\n                    != C_OK) return;\n            if (lfu_freq < 0 || lfu_freq > 255) {\n                addReplyError(c,\"Invalid FREQ value, must be >= 0 and <= 255\");\n                return;\n            }\n            j++; /* Consume additional arg. */\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Make sure this key does not already exist here... */\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    if (getLongLongFromObjectOrReply(c,c->argv[2],&ttl,NULL) != C_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    if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == C_ERR)\n    {\n        addReplyError(c,\"DUMP payload version or checksum are wrong\");\n        return;\n    }\n\n    rioInitWithBuffer(&payload,c->argv[3]->ptr);\n    if (((type = rdbLoadObjectType(&payload)) == -1) ||\n        ((obj = rdbLoadObject(type,&payload,c->argv[1]->ptr)) == NULL))\n    {\n        addReplyError(c,\"Bad data format\");\n        return;\n    }\n\n    /* Remove the old key if needed. */\n    if (replace) dbDelete(c->db,c->argv[1]);\n\n    /* Create the key and set the TTL if any */\n    dbAdd(c->db,c->argv[1],obj);\n    if (ttl) {\n        if (!absttl) ttl+=mstime();\n        setExpire(c,c->db,c->argv[1],ttl);\n    }\n    objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock,1000);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"restore\",c->argv[1],c->db->id);\n    addReply(c,shared.ok);\n    server.dirty++;\n}\n\n/* MIGRATE socket cache implementation.\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 * 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#define MIGRATE_SOCKET_CACHE_ITEMS 64 /* max num of items in the cache. */\n#define MIGRATE_SOCKET_CACHE_TTL 10 /* close cached sockets after 10 sec. */\n\ntypedef struct migrateCachedSocket {\n    connection *conn;\n    long last_dbid;\n    time_t last_use_time;\n} migrateCachedSocket;\n\n/* Return a migrateCachedSocket containing a TCP socket connected with the\n * target instance, possibly returning a cached one.\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 * If the caller detects an error while using the socket, migrateCloseSocket()\n * should be called so that the connection will be created from scratch\n * the next time. */\nmigrateCachedSocket* migrateGetSocket(client *c, robj *host, robj *port, long timeout) {\n    connection *conn;\n    sds name = sdsempty();\n    migrateCachedSocket *cs;\n\n    /* Check if we have an already cached socket for this ip:port pair. */\n    name = sdscatlen(name,host->ptr,sdslen(host->ptr));\n    name = sdscatlen(name,\":\",1);\n    name = sdscatlen(name,port->ptr,sdslen(port->ptr));\n    cs = dictFetchValue(server.migrate_cached_sockets,name);\n    if (cs) {\n        sdsfree(name);\n        cs->last_use_time = server.unixtime;\n        return cs;\n    }\n\n    /* No cached socket, create one. */\n    if (dictSize(server.migrate_cached_sockets) == MIGRATE_SOCKET_CACHE_ITEMS) {\n        /* Too many items, drop one at random. */\n        dictEntry *de = dictGetRandomKey(server.migrate_cached_sockets);\n        cs = dictGetVal(de);\n        connClose(cs->conn);\n        zfree(cs);\n        dictDelete(server.migrate_cached_sockets,dictGetKey(de));\n    }\n\n    /* Create the socket */\n    conn = server.tls_cluster ? connCreateTLS() : connCreateSocket();\n    if (connBlockingConnect(conn, c->argv[1]->ptr, atoi(c->argv[2]->ptr), timeout)\n            != C_OK) {\n        addReplySds(c,\n            sdsnew(\"-IOERR error or timeout connecting to the client\\r\\n\"));\n        connClose(conn);\n        sdsfree(name);\n        return NULL;\n    }\n    connEnableTcpNoDelay(conn);\n\n    /* Add to the cache and return it to the caller. */\n    cs = zmalloc(sizeof(*cs));\n    cs->conn = conn;\n\n    cs->last_dbid = -1;\n    cs->last_use_time = server.unixtime;\n    dictAdd(server.migrate_cached_sockets,name,cs);\n    return cs;\n}\n\n/* Free a migrate cached connection. */\nvoid migrateCloseSocket(robj *host, robj *port) {\n    sds name = sdsempty();\n    migrateCachedSocket *cs;\n\n    name = sdscatlen(name,host->ptr,sdslen(host->ptr));\n    name = sdscatlen(name,\":\",1);\n    name = sdscatlen(name,port->ptr,sdslen(port->ptr));\n    cs = dictFetchValue(server.migrate_cached_sockets,name);\n    if (!cs) {\n        sdsfree(name);\n        return;\n    }\n\n    connClose(cs->conn);\n    zfree(cs);\n    dictDelete(server.migrate_cached_sockets,name);\n    sdsfree(name);\n}\n\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        if ((server.unixtime - cs->last_use_time) > MIGRATE_SOCKET_CACHE_TTL) {\n            connClose(cs->conn);\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 | AUTH password |\n *         AUTH2 username password]\n *\n * On in the multiple keys form:\n *\n * MIGRATE host port \"\" dbid timeout [COPY | REPLACE | AUTH password |\n *         AUTH2 username password] KEYS key1 key2 ... keyN */\nvoid migrateCommand(client *c) {\n    migrateCachedSocket *cs;\n    int copy = 0, replace = 0, j;\n    char *username = NULL;\n    char *password = NULL;\n    long timeout;\n    long dbid;\n    robj **ov = NULL; /* Objects to migrate. */\n    robj **kv = NULL; /* Key names. */\n    robj **newargv = NULL; /* Used to rewrite the command as DEL ... keys ... */\n    rio cmd, payload;\n    int may_retry = 1;\n    int write_error = 0;\n    int argv_rewritten = 0;\n\n    /* To support the KEYS option we need the following additional state. */\n    int first_key = 3; /* Argument index of the first key. */\n    int num_keys = 1;  /* By default only migrate the 'key' argument. */\n\n    /* Parse additional options */\n    for (j = 6; j < c->argc; j++) {\n        int moreargs = (c->argc-1) - 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 if (!strcasecmp(c->argv[j]->ptr,\"auth\")) {\n            if (!moreargs) {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n            j++;\n            password = c->argv[j]->ptr;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"auth2\")) {\n            if (moreargs < 2) {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n            username = c->argv[++j]->ptr;\n            password = c->argv[++j]->ptr;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"keys\")) {\n            if (sdslen(c->argv[3]->ptr) != 0) {\n                addReplyError(c,\n                    \"When using MIGRATE KEYS option, the key argument\"\n                    \" must be set to the empty string\");\n                return;\n            }\n            first_key = j+1;\n            num_keys = c->argc - j - 1;\n            break; /* All the remaining args are keys. */\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Sanity check */\n    if (getLongFromObjectOrReply(c,c->argv[5],&timeout,NULL) != C_OK ||\n        getLongFromObjectOrReply(c,c->argv[4],&dbid,NULL) != C_OK)\n    {\n        return;\n    }\n    if (timeout <= 0) timeout = 1000;\n\n    /* Check if the keys are here. If at least one key is to migrate, do it\n     * otherwise if all the keys are missing reply with \"NOKEY\" to signal\n     * the caller there was nothing to migrate. We don't return an error in\n     * this case, since often this is due to a normal condition like the key\n     * expiring in the meantime. */\n    ov = zrealloc(ov,sizeof(robj*)*num_keys);\n    kv = zrealloc(kv,sizeof(robj*)*num_keys);\n    int oi = 0;\n\n    for (j = 0; j < num_keys; j++) {\n        if ((ov[oi] = lookupKeyRead(c->db,c->argv[first_key+j])) != NULL) {\n            kv[oi] = c->argv[first_key+j];\n            oi++;\n        }\n    }\n    num_keys = oi;\n    if (num_keys == 0) {\n        zfree(ov); zfree(kv);\n        addReplySds(c,sdsnew(\"+NOKEY\\r\\n\"));\n        return;\n    }\n\ntry_again:\n    write_error = 0;\n\n    /* Connect */\n    cs = migrateGetSocket(c,c->argv[1],c->argv[2],timeout);\n    if (cs == NULL) {\n        zfree(ov); zfree(kv);\n        return; /* error sent to the client by migrateGetSocket() */\n    }\n\n    rioInitWithBuffer(&cmd,sdsempty());\n\n    /* Authentication */\n    if (password) {\n        int arity = username ? 3 : 2;\n        serverAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',arity));\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"AUTH\",4));\n        if (username) {\n            serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,username,\n                                 sdslen(username)));\n        }\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,password,\n            sdslen(password)));\n    }\n\n    /* Send the SELECT command if the current DB is not already selected. */\n    int select = cs->last_dbid != dbid; /* Should we emit SELECT? */\n    if (select) {\n        serverAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"SELECT\",6));\n        serverAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));\n    }\n\n    int non_expired = 0; /* Number of keys that we'll find non expired.\n                            Note that serializing large keys may take some time\n                            so certain keys that were found non expired by the\n                            lookupKey() function, may be expired later. */\n\n    /* Create RESTORE payload and generate the protocol to call the command. */\n    for (j = 0; j < num_keys; j++) {\n        long long ttl = 0;\n        long long expireat = getExpire(c->db,kv[j]);\n\n        if (expireat != -1) {\n            ttl = expireat-mstime();\n            if (ttl < 0) {\n                continue;\n            }\n            if (ttl < 1) ttl = 1;\n        }\n\n        /* Relocate valid (non expired) keys into the array in successive\n         * positions to remove holes created by the keys that were present\n         * in the first lookup but are now expired after the second lookup. */\n        kv[non_expired++] = kv[j];\n\n        serverAssertWithInfo(c,NULL,\n            rioWriteBulkCount(&cmd,'*',replace ? 5 : 4));\n\n        if (server.cluster_enabled)\n            serverAssertWithInfo(c,NULL,\n                rioWriteBulkString(&cmd,\"RESTORE-ASKING\",14));\n        else\n            serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"RESTORE\",7));\n        serverAssertWithInfo(c,NULL,sdsEncodedObject(kv[j]));\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,kv[j]->ptr,\n                sdslen(kv[j]->ptr)));\n        serverAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,ttl));\n\n        /* Emit the payload argument, that is the serialized object using\n         * the DUMP format. */\n        createDumpPayload(&payload,ov[j],kv[j]);\n        serverAssertWithInfo(c,NULL,\n            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        if (replace)\n            serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"REPLACE\",7));\n    }\n\n    /* Fix the actual number of keys we are migrating. */\n    num_keys = non_expired;\n\n    /* Transfer the query to the other node in 64K chunks. */\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 = connSyncWrite(cs->conn,buf+pos,towrite,timeout);\n            if (nwritten != (signed)towrite) {\n                write_error = 1;\n                goto socket_err;\n            }\n            pos += nwritten;\n        }\n    }\n\n    char buf0[1024]; /* Auth reply. */\n    char buf1[1024]; /* Select reply. */\n    char buf2[1024]; /* Restore reply. */\n\n    /* Read the AUTH reply if needed. */\n    if (password && connSyncReadLine(cs->conn, buf0, sizeof(buf0), timeout) <= 0)\n        goto socket_err;\n\n    /* Read the SELECT reply if needed. */\n    if (select && connSyncReadLine(cs->conn, buf1, sizeof(buf1), timeout) <= 0)\n        goto socket_err;\n\n    /* Read the RESTORE replies. */\n    int error_from_target = 0;\n    int socket_error = 0;\n    int del_idx = 1; /* Index of the key argument for the replicated DEL op. */\n\n    /* Allocate the new argument vector that will replace the current command,\n     * to propagate the MIGRATE as a DEL command (if no COPY option was given).\n     * We allocate num_keys+1 because the additional argument is for \"DEL\"\n     * command name itself. */\n    if (!copy) newargv = zmalloc(sizeof(robj*)*(num_keys+1));\n\n    for (j = 0; j < num_keys; j++) {\n        if (connSyncReadLine(cs->conn, buf2, sizeof(buf2), timeout) <= 0) {\n            socket_error = 1;\n            break;\n        }\n        if ((password && buf0[0] == '-') ||\n            (select && buf1[0] == '-') ||\n            buf2[0] == '-')\n        {\n            /* On error assume that last_dbid is no longer valid. */\n            if (!error_from_target) {\n                cs->last_dbid = -1;\n                char *errbuf;\n                if (password && buf0[0] == '-') errbuf = buf0;\n                else if (select && buf1[0] == '-') errbuf = buf1;\n                else errbuf = buf2;\n\n                error_from_target = 1;\n                addReplyErrorFormat(c,\"Target instance replied with error: %s\",\n                    errbuf+1);\n            }\n        } else {\n            if (!copy) {\n                /* No COPY option: remove the local key, signal the change. */\n                dbDelete(c->db,kv[j]);\n                signalModifiedKey(c,c->db,kv[j]);\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",kv[j],c->db->id);\n                server.dirty++;\n\n                /* Populate the argument vector to replace the old one. */\n                newargv[del_idx++] = kv[j];\n                incrRefCount(kv[j]);\n            }\n        }\n    }\n\n    /* On socket error, if we want to retry, do it now before rewriting the\n     * command vector. We only retry if we are sure nothing was processed\n     * and we failed to read the first reply (j == 0 test). */\n    if (!error_from_target && socket_error && j == 0 && may_retry &&\n        errno != ETIMEDOUT)\n    {\n        goto socket_err; /* A retry is guaranteed because of tested conditions.*/\n    }\n\n    /* On socket errors, close the migration socket now that we still have\n     * the original host/port in the ARGV. Later the original command may be\n     * rewritten to DEL and will be too later. */\n    if (socket_error) migrateCloseSocket(c->argv[1],c->argv[2]);\n\n    if (!copy) {\n        /* Translate MIGRATE as DEL for replication/AOF. Note that we do\n         * this only for the keys for which we received an acknowledgement\n         * from the receiving Redis server, by using the del_idx index. */\n        if (del_idx > 1) {\n            newargv[0] = createStringObject(\"DEL\",3);\n            /* Note that the following call takes ownership of newargv. */\n            replaceClientCommandVector(c,del_idx,newargv);\n            argv_rewritten = 1;\n        } else {\n            /* No key transfer acknowledged, no need to rewrite as DEL. */\n            zfree(newargv);\n        }\n        newargv = NULL; /* Make it safe to call zfree() on it in the future. */\n    }\n\n    /* If we are here and a socket error happened, we don't want to retry.\n     * Just signal the problem to the client, but only do it if we did not\n     * already queue a different error reported by the destination server. */\n    if (!error_from_target && socket_error) {\n        may_retry = 0;\n        goto socket_err;\n    }\n\n    if (!error_from_target) {\n        /* Success! Update the last_dbid in migrateCachedSocket, so that we can\n         * avoid SELECT the next time if the target DB is the same. Reply +OK.\n         *\n         * Note: If we reached this point, even if socket_error is true\n         * still the SELECT command succeeded (otherwise the code jumps to\n         * socket_err label. */\n        cs->last_dbid = dbid;\n        addReply(c,shared.ok);\n    } else {\n        /* On error we already sent it in the for loop above, and set\n         * the currently selected socket to -1 to force SELECT the next time. */\n    }\n\n    sdsfree(cmd.io.buffer.ptr);\n    zfree(ov); zfree(kv); zfree(newargv);\n    return;\n\n/* On socket errors we try to close the cached socket and try again.\n * It is very common for the cached socket to get closed, if just reopening\n * it works it's a shame to notify the error to the caller. */\nsocket_err:\n    /* Cleanup we want to perform in both the retry and no retry case.\n     * Note: Closing the migrate socket will also force SELECT next time. */\n    sdsfree(cmd.io.buffer.ptr);\n\n    /* If the command was rewritten as DEL and there was a socket error,\n     * we already closed the socket earlier. While migrateCloseSocket()\n     * is idempotent, the host/port arguments are now gone, so don't do it\n     * again. */\n    if (!argv_rewritten) migrateCloseSocket(c->argv[1],c->argv[2]);\n    zfree(newargv);\n    newargv = NULL; /* This will get reallocated on retry. */\n\n    /* Retry only if it's not a timeout and we never attempted a retry\n     * (or the code jumping here did not set may_retry to zero). */\n    if (errno != ETIMEDOUT && may_retry) {\n        may_retry = 0;\n        goto try_again;\n    }\n\n    /* Cleanup we want to do if no retry is attempted. */\n    zfree(ov); zfree(kv);\n    addReplySds(c,\n        sdscatprintf(sdsempty(),\n            \"-IOERR error or timeout %s to target instance\\r\\n\",\n            write_error ? \"writing\" : \"reading\"));\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 * 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. */\nvoid askingCommand(client *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n    c->flags |= CLIENT_ASKING;\n    addReply(c,shared.ok);\n}\n\n/* The READONLY command is used 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(client *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n    c->flags |= CLIENT_READONLY;\n    addReply(c,shared.ok);\n}\n\n/* The READWRITE command just clears the READONLY command state. */\nvoid readwriteCommand(client *c) {\n    c->flags &= ~CLIENT_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 CLUSTER_REDIR_ASK or\n * CLUSTER_REDIR_MOVED.\n *\n * When the node is 'myself' 'error_code' is set to 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 * CLUSTER_REDIR_CROSS_SLOT if the request contains multiple keys that\n * don't belong to the same hash slot.\n *\n * CLUSTER_REDIR_UNSTABLE if the request contains multiple 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).\n *\n * CLUSTER_REDIR_DOWN_UNBOUND if the request addresses a slot which is\n * not bound to any node. In this case the cluster global state should be\n * already \"down\" but it is fragile to rely on the update of the global state,\n * so we also handle it here.\n *\n * CLUSTER_REDIR_DOWN_STATE and CLUSTER_REDIR_DOWN_RO_STATE if the cluster is\n * down but the user attempts to execute a command that addresses one or more keys. */\nclusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *error_code) {\n    clusterNode *n = NULL;\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    /* Allow any key to be set if a module disabled cluster redirections. */\n    if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)\n        return myself;\n\n    /* Set error code optimistically for the base case. */\n    if (error_code) *error_code = CLUSTER_REDIR_NONE;\n\n    /* Modules can turn off Redis Cluster redirection: this is useful\n     * when writing a module that implements a completely different\n     * distributed system. */\n\n    /* We handle all the cases as if they were EXEC commands, so we have\n     * a common code path for everything */\n    if (cmd->proc == execCommand) {\n        /* If CLIENT_MULTI flag is not set EXEC is just going to return an\n         * error. */\n        if (!(c->flags & CLIENT_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        keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);\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                /* 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\n                /* Error: If a slot is not served, we are in \"cluster down\"\n                 * state. However the state is yet to be updated, so this was\n                 * not trapped earlier in processCommand(). Report the same\n                 * error to the client. */\n                if (n == NULL) {\n                    getKeysFreeResult(keyindex);\n                    if (error_code)\n                        *error_code = CLUSTER_REDIR_DOWN_UNBOUND;\n                    return NULL;\n                }\n\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 = 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 in all the cases. */\n    if (n == NULL) return myself;\n\n    /* Cluster is globally down but we got keys? We only serve the request\n     * if it is a read command and when allow_reads_when_down is enabled. */\n    if (server.cluster->state != CLUSTER_OK) {\n        if (!server.cluster_allow_reads_when_down) {\n            /* The cluster is configured to block commands when the\n             * cluster is down. */\n            if (error_code) *error_code = CLUSTER_REDIR_DOWN_STATE;\n            return NULL;\n        } else if (!(cmd->flags & CMD_READONLY) && !(cmd->proc == evalCommand)\n                && !(cmd->proc == evalShaCommand))\n        {\n            /* The cluster is configured to allow read only commands\n             * but this command is neither readonly, nor EVAL or\n             * EVALSHA. */\n            if (error_code) *error_code = CLUSTER_REDIR_DOWN_RO_STATE;\n            return NULL;\n        } else {\n            /* Fall through and allow the command to be executed:\n             * this happens when server.cluster_allow_reads_when_down is\n             * true and the command is a readonly command or EVAL / EVALSHA. */\n        }\n    }\n\n    /* Return the hashslot by reference. */\n    if (hashslot) *hashslot = slot;\n\n    /* MIGRATE always works in the context of the local node if the slot\n     * is open (migrating or importing state). We need to be able to freely\n     * move keys among instances in this case. */\n    if ((migrating_slot || importing_slot) && cmd->proc == migrateCommand)\n        return myself;\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 = 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 & CLIENT_ASKING || cmd->flags & CMD_ASKING))\n    {\n        if (multiple_keys && missing_keys) {\n            if (error_code) *error_code = 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 & CLIENT_READONLY &&\n        (cmd->flags & CMD_READONLY || cmd->proc == evalCommand ||\n         cmd->proc == evalShaCommand) &&\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 = CLUSTER_REDIR_MOVED;\n    return n;\n}\n\n/* Send the client the right redirection code, according to error_code\n * that should be set to one of CLUSTER_REDIR_* macros.\n *\n * If CLUSTER_REDIR_ASK or CLUSTER_REDIR_MOVED error codes\n * are used, then the node 'n' should not be NULL, but should be the\n * node we want to mention in the redirection. Moreover hashslot should\n * be set to the hash slot that caused the redirection. */\nvoid clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code) {\n    if (error_code == 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 == CLUSTER_REDIR_UNSTABLE) {\n        /* The request spawns multiple 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 if (error_code == CLUSTER_REDIR_DOWN_STATE) {\n        addReplySds(c,sdsnew(\"-CLUSTERDOWN The cluster is down\\r\\n\"));\n    } else if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) {\n        addReplySds(c,sdsnew(\"-CLUSTERDOWN The cluster is down and only accepts read commands\\r\\n\"));\n    } else if (error_code == CLUSTER_REDIR_DOWN_UNBOUND) {\n        addReplySds(c,sdsnew(\"-CLUSTERDOWN Hash slot not served\\r\\n\"));\n    } else if (error_code == CLUSTER_REDIR_MOVED ||\n               error_code == CLUSTER_REDIR_ASK)\n    {\n        addReplySds(c,sdscatprintf(sdsempty(),\n            \"-%s %d %s:%d\\r\\n\",\n            (error_code == CLUSTER_REDIR_ASK) ? \"ASK\" : \"MOVED\",\n            hashslot,n->ip,n->port));\n    } else {\n        serverPanic(\"getNodeByQuery() unknown error.\");\n    }\n}\n\n/* This function is called by the function processing clients incrementally\n * to detect timeouts, in order to handle the following case:\n *\n * 1) A client blocks with BLPOP or similar blocking operation.\n * 2) The master migrates the hash slot elsewhere or turns into a slave.\n * 3) The client may remain blocked forever (or up to the max timeout time)\n *    waiting for a key change that will never happen.\n *\n * If the client is found to be blocked into an hash slot this node no\n * longer handles, the client is sent a redirection error, and the function\n * returns 1. Otherwise 0 is returned and no operation is performed. */\nint clusterRedirectBlockedClientIfNeeded(client *c) {\n    if (c->flags & CLIENT_BLOCKED &&\n        (c->btype == BLOCKED_LIST ||\n         c->btype == BLOCKED_ZSET ||\n         c->btype == BLOCKED_STREAM))\n    {\n        dictEntry *de;\n        dictIterator *di;\n\n        /* If the cluster is down, unblock the client with the right error.\n         * If the cluster is configured to allow reads on cluster down, we\n         * still want to emit this error since a write will be required\n         * to unblock them which may never come.  */\n        if (server.cluster->state == CLUSTER_FAIL) {\n            clusterRedirectClient(c,NULL,0,CLUSTER_REDIR_DOWN_STATE);\n            return 1;\n        }\n\n        /* All keys must belong to the same slot, so check first key only. */\n        di = dictGetIterator(c->bpop.keys);\n        if ((de = dictNext(di)) != NULL) {\n            robj *key = dictGetKey(de);\n            int slot = keyHashSlot((char*)key->ptr, sdslen(key->ptr));\n            clusterNode *node = server.cluster->slots[slot];\n\n            /* We send an error and unblock the client if:\n             * 1) The slot is unassigned, emitting a cluster down error.\n             * 2) The slot is not handled by this node, nor being imported. */\n            if (node != myself &&\n                server.cluster->importing_slots_from[slot] == NULL)\n            {\n                if (node == NULL) {\n                    clusterRedirectClient(c,NULL,0,\n                        CLUSTER_REDIR_DOWN_UNBOUND);\n                } else {\n                    clusterRedirectClient(c,node,slot,\n                        CLUSTER_REDIR_MOVED);\n                }\n                dictReleaseIterator(di);\n                return 1;\n            }\n        }\n        dictReleaseIterator(di);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/cluster.h",
    "content": "#ifndef __CLUSTER_H\n#define __CLUSTER_H\n\n/*-----------------------------------------------------------------------------\n * Redis cluster data structures, defines, exported API.\n *----------------------------------------------------------------------------*/\n\n#define CLUSTER_SLOTS 16384\n#define CLUSTER_OK 0          /* Everything looks ok */\n#define CLUSTER_FAIL 1        /* The cluster can't work */\n#define CLUSTER_NAMELEN 40    /* sha1 hex length */\n#define CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */\n\n/* The following defines are amount of time, sometimes expressed as\n * multiplicators of the node timeout value (when ending with MULT). */\n#define CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */\n#define CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if master is back. */\n#define CLUSTER_FAIL_UNDO_TIME_ADD 10 /* Some additional time. */\n#define CLUSTER_FAILOVER_DELAY 5 /* Seconds */\n#define CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */\n#define CLUSTER_MF_PAUSE_MULT 2 /* Master pause manual failover mult. */\n#define CLUSTER_SLAVE_MIGRATION_DELAY 5000 /* Delay for slave migration. */\n\n/* Redirection errors returned by getNodeByQuery(). */\n#define CLUSTER_REDIR_NONE 0          /* Node can serve the request. */\n#define CLUSTER_REDIR_CROSS_SLOT 1    /* -CROSSSLOT request. */\n#define CLUSTER_REDIR_UNSTABLE 2      /* -TRYAGAIN redirection required */\n#define CLUSTER_REDIR_ASK 3           /* -ASK redirection required. */\n#define CLUSTER_REDIR_MOVED 4         /* -MOVED redirection required. */\n#define CLUSTER_REDIR_DOWN_STATE 5    /* -CLUSTERDOWN, global state. */\n#define CLUSTER_REDIR_DOWN_UNBOUND 6  /* -CLUSTERDOWN, unbound slot. */\n#define CLUSTER_REDIR_DOWN_RO_STATE 7 /* -CLUSTERDOWN, allow reads. */\n\nstruct clusterNode;\n\n/* clusterLink encapsulates everything needed to talk with a remote node. */\ntypedef struct clusterLink {\n    mstime_t ctime;             /* Link creation time */\n    connection *conn;           /* Connection to remote node */\n    sds sndbuf;                 /* Packet send buffer */\n    sds rcvbuf;                 /* Packet reception buffer */\n    struct clusterNode *node;   /* Node related to this link if any, or NULL */\n} clusterLink;\n\n/* Cluster node flags and macros. */\n#define CLUSTER_NODE_MASTER 1     /* The node is a master */\n#define CLUSTER_NODE_SLAVE 2      /* The node is a slave */\n#define CLUSTER_NODE_PFAIL 4      /* Failure? Need acknowledge */\n#define CLUSTER_NODE_FAIL 8       /* The node is believed to be malfunctioning */\n#define CLUSTER_NODE_MYSELF 16    /* This node is myself */\n#define CLUSTER_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */\n#define CLUSTER_NODE_NOADDR   64  /* We don't know the address of this node */\n#define CLUSTER_NODE_MEET 128     /* Send a MEET message to this node */\n#define CLUSTER_NODE_MIGRATE_TO 256 /* Master elegible for replica migration. */\n#define CLUSTER_NODE_NOFAILOVER 512 /* Slave will not try to failver. */\n#define CLUSTER_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#define nodeIsMaster(n) ((n)->flags & CLUSTER_NODE_MASTER)\n#define nodeIsSlave(n) ((n)->flags & CLUSTER_NODE_SLAVE)\n#define nodeInHandshake(n) ((n)->flags & CLUSTER_NODE_HANDSHAKE)\n#define nodeHasAddr(n) (!((n)->flags & CLUSTER_NODE_NOADDR))\n#define nodeWithoutAddr(n) ((n)->flags & CLUSTER_NODE_NOADDR)\n#define nodeTimedOut(n) ((n)->flags & CLUSTER_NODE_PFAIL)\n#define nodeFailed(n) ((n)->flags & CLUSTER_NODE_FAIL)\n#define nodeCantFailover(n) ((n)->flags & CLUSTER_NODE_NOFAILOVER)\n\n/* Reasons why a slave is not able to failover. */\n#define CLUSTER_CANT_FAILOVER_NONE 0\n#define CLUSTER_CANT_FAILOVER_DATA_AGE 1\n#define CLUSTER_CANT_FAILOVER_WAITING_DELAY 2\n#define CLUSTER_CANT_FAILOVER_EXPIRED 3\n#define CLUSTER_CANT_FAILOVER_WAITING_VOTES 4\n#define CLUSTER_CANT_FAILOVER_RELOG_PERIOD (60*5) /* seconds. */\n\n/* clusterState todo_before_sleep flags. */\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/* Message types.\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#define CLUSTERMSG_TYPE_PING 0          /* Ping */\n#define CLUSTERMSG_TYPE_PONG 1          /* Pong (reply to Ping) */\n#define CLUSTERMSG_TYPE_MEET 2          /* Meet \"let's join\" message */\n#define CLUSTERMSG_TYPE_FAIL 3          /* Mark node xxx as failing */\n#define CLUSTERMSG_TYPE_PUBLISH 4       /* Pub/Sub Publish propagation */\n#define CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 5 /* May I failover? */\n#define CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 6     /* Yes, you have my vote */\n#define CLUSTERMSG_TYPE_UPDATE 7        /* Another node slots configuration */\n#define CLUSTERMSG_TYPE_MFSTART 8       /* Pause clients for manual failover */\n#define CLUSTERMSG_TYPE_MODULE 9        /* Module cluster API message. */\n#define CLUSTERMSG_TYPE_COUNT 10        /* Total number of message types. */\n\n/* Flags that a module can set in order to prevent certain Redis Cluster\n * features to be enabled. Useful when implementing a different distributed\n * system on top of Redis Cluster message bus, using modules. */\n#define CLUSTER_MODULE_FLAG_NONE 0\n#define CLUSTER_MODULE_FLAG_NO_FAILOVER (1<<1)\n#define CLUSTER_MODULE_FLAG_NO_REDIRECTION (1<<2)\n\n/* This structure represent elements of node->fail_reports. */\ntypedef struct clusterNodeFailReport {\n    struct clusterNode *node;  /* Node reporting the failure condition. */\n    mstime_t time;             /* Time of the last report from this node. */\n} clusterNodeFailReport;\n\ntypedef struct clusterNode {\n    mstime_t ctime; /* Node object creation time. */\n    char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */\n    int flags;      /* CLUSTER_NODE_... */\n    uint64_t configEpoch; /* Last configEpoch observed for this node */\n    unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */\n    int numslots;   /* Number of slots handled by this node */\n    int numslaves;  /* Number of slave nodes, if this is a master */\n    struct clusterNode **slaves; /* pointers to slave nodes */\n    struct clusterNode *slaveof; /* pointer to the master node. Note that it\n                                    may be NULL even if the node is a slave\n                                    if we don't have the master node in our\n                                    tables. */\n    mstime_t ping_sent;      /* Unix time we sent latest ping */\n    mstime_t pong_received;  /* Unix time we received the pong */\n    mstime_t data_received;  /* Unix time we received any data */\n    mstime_t fail_time;      /* Unix time when FAIL flag was set */\n    mstime_t voted_time;     /* Last time we voted for a slave of this master */\n    mstime_t repl_offset_time;  /* Unix time we received offset for this node */\n    mstime_t orphaned_time;     /* Starting time of orphaned master condition */\n    long long repl_offset;      /* Last known repl offset for this node. */\n    char ip[NET_IP_STR_LEN];  /* Latest known IP address of this node */\n    int port;                   /* Latest known clients port of this node */\n    int cport;                  /* Latest known cluster port of this node. */\n    clusterLink *link;          /* TCP/IP link with this node */\n    list *fail_reports;         /* List of nodes signaling this as failing */\n} clusterNode;\n\ntypedef struct clusterState {\n    clusterNode *myself;  /* This node */\n    uint64_t currentEpoch;\n    int state;            /* CLUSTER_OK, CLUSTER_FAIL, ... */\n    int size;             /* Num of master nodes with at least one slot */\n    dict *nodes;          /* Hash table of name -> clusterNode structures */\n    dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */\n    clusterNode *migrating_slots_to[CLUSTER_SLOTS];\n    clusterNode *importing_slots_from[CLUSTER_SLOTS];\n    clusterNode *slots[CLUSTER_SLOTS];\n    uint64_t slots_keys_count[CLUSTER_SLOTS];\n    rax *slots_to_keys;\n    /* The following fields are used to take the slave state on elections. */\n    mstime_t failover_auth_time; /* Time of previous or next election. */\n    int failover_auth_count;    /* Number of votes received so far. */\n    int failover_auth_sent;     /* True if we already asked for votes. */\n    int failover_auth_rank;     /* This slave rank for current auth request. */\n    uint64_t failover_auth_epoch; /* Epoch of the current election. */\n    int cant_failover_reason;   /* Why a slave is currently not able to\n                                   failover. See the CANT_FAILOVER_* macros. */\n    /* Manual failover state in common. */\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    clusterNode *mf_slave;      /* Slave performing the manual failover. */\n    /* Manual failover state of slave. */\n    long long mf_master_offset; /* Master offset the slave needs to start MF\n                                   or zero if stil not received. */\n    int mf_can_start;           /* If non-zero signal that the manual failover\n                                   can start requesting masters vote. */\n    /* The followign fields are used by masters to take state on elections. */\n    uint64_t lastVoteEpoch;     /* Epoch of the last vote granted. */\n    int todo_before_sleep; /* Things to do in clusterBeforeSleep(). */\n    /* Messages received and sent by type. */\n    long long stats_bus_messages_sent[CLUSTERMSG_TYPE_COUNT];\n    long long stats_bus_messages_received[CLUSTERMSG_TYPE_COUNT];\n    long long stats_pfail_nodes;    /* Number of nodes in PFAIL status,\n                                       excluding nodes without address. */\n} clusterState;\n\n/* Redis cluster messages header */\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. */\ntypedef struct {\n    char nodename[CLUSTER_NAMELEN];\n    uint32_t ping_sent;\n    uint32_t pong_received;\n    char ip[NET_IP_STR_LEN];  /* IP address last time it was seen */\n    uint16_t port;              /* base port last time it was seen */\n    uint16_t cport;             /* cluster port last time it was seen */\n    uint16_t flags;             /* node->flags copy */\n    uint32_t notused1;\n} clusterMsgDataGossip;\n\ntypedef struct {\n    char nodename[CLUSTER_NAMELEN];\n} clusterMsgDataFail;\n\ntypedef struct {\n    uint32_t channel_len;\n    uint32_t message_len;\n    unsigned char bulk_data[8]; /* 8 bytes just as placeholder. */\n} clusterMsgDataPublish;\n\ntypedef struct {\n    uint64_t configEpoch; /* Config epoch of the specified instance. */\n    char nodename[CLUSTER_NAMELEN]; /* Name of the slots owner. */\n    unsigned char slots[CLUSTER_SLOTS/8]; /* Slots bitmap. */\n} clusterMsgDataUpdate;\n\ntypedef struct {\n    uint64_t module_id;     /* ID of the sender module. */\n    uint32_t len;           /* ID of the sender module. */\n    uint8_t type;           /* Type from 0 to 255. */\n    unsigned char bulk_data[3]; /* 3 bytes just as placeholder. */\n} clusterMsgModule;\n\nunion clusterMsgData {\n    /* PING, MEET and PONG */\n    struct {\n        /* Array of N clusterMsgDataGossip structures */\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    /* MODULE */\n    struct {\n        clusterMsgModule msg;\n    } module;\n};\n\n#define CLUSTER_PROTO_VER 1 /* Cluster bus protocol version. */\n\ntypedef struct {\n    char sig[4];        /* Signature \"RCmb\" (Redis Cluster message bus). */\n    uint32_t totlen;    /* Total length of this message */\n    uint16_t ver;       /* Protocol version, currently set to 1. */\n    uint16_t port;      /* TCP base port number. */\n    uint16_t type;      /* Message type */\n    uint16_t count;     /* Only used for some kind of messages. */\n    uint64_t currentEpoch;  /* The epoch accordingly to the sending node. */\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    uint64_t offset;    /* Master replication offset if node is a master or\n                           processed replication offset if node is a slave. */\n    char sender[CLUSTER_NAMELEN]; /* Name of the sender node */\n    unsigned char myslots[CLUSTER_SLOTS/8];\n    char slaveof[CLUSTER_NAMELEN];\n    char myip[NET_IP_STR_LEN];    /* Sender IP, if not all zeroed. */\n    char notused1[34];  /* 34 bytes reserved for future usage. */\n    uint16_t cport;      /* Sender TCP cluster bus port */\n    uint16_t flags;      /* Sender node flags */\n    unsigned char state; /* Cluster state from the POV of the sender */\n    unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */\n    union clusterMsgData data;\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(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);\nint clusterRedirectBlockedClientIfNeeded(client *c);\nvoid clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);\n\n#endif /* __CLUSTER_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"cluster.h\"\n\n#include <fcntl.h>\n#include <sys/stat.h>\n\n/*-----------------------------------------------------------------------------\n * Config file name-value maps.\n *----------------------------------------------------------------------------*/\n\ntypedef struct configEnum {\n    const char *name;\n    const int val;\n} configEnum;\n\nconfigEnum maxmemory_policy_enum[] = {\n    {\"volatile-lru\", MAXMEMORY_VOLATILE_LRU},\n    {\"volatile-lfu\", MAXMEMORY_VOLATILE_LFU},\n    {\"volatile-random\",MAXMEMORY_VOLATILE_RANDOM},\n    {\"volatile-ttl\",MAXMEMORY_VOLATILE_TTL},\n    {\"allkeys-lru\",MAXMEMORY_ALLKEYS_LRU},\n    {\"allkeys-lfu\",MAXMEMORY_ALLKEYS_LFU},\n    {\"allkeys-random\",MAXMEMORY_ALLKEYS_RANDOM},\n    {\"noeviction\",MAXMEMORY_NO_EVICTION},\n    {NULL, 0}\n};\n\nconfigEnum syslog_facility_enum[] = {\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\nconfigEnum loglevel_enum[] = {\n    {\"debug\", LL_DEBUG},\n    {\"verbose\", LL_VERBOSE},\n    {\"notice\", LL_NOTICE},\n    {\"warning\", LL_WARNING},\n    {NULL,0}\n};\n\nconfigEnum supervised_mode_enum[] = {\n    {\"upstart\", SUPERVISED_UPSTART},\n    {\"systemd\", SUPERVISED_SYSTEMD},\n    {\"auto\", SUPERVISED_AUTODETECT},\n    {\"no\", SUPERVISED_NONE},\n    {NULL, 0}\n};\n\nconfigEnum aof_fsync_enum[] = {\n    {\"everysec\", AOF_FSYNC_EVERYSEC},\n    {\"always\", AOF_FSYNC_ALWAYS},\n    {\"no\", AOF_FSYNC_NO},\n    {NULL, 0}\n};\n\nconfigEnum repl_diskless_load_enum[] = {\n    {\"disabled\", REPL_DISKLESS_LOAD_DISABLED},\n    {\"on-empty-db\", REPL_DISKLESS_LOAD_WHEN_DB_EMPTY},\n    {\"swapdb\", REPL_DISKLESS_LOAD_SWAPDB},\n    {NULL, 0}\n};\n\n/* Output buffer limits presets. */\nclientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {\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/* Generic config infrastructure function pointers\n * int is_valid_fn(val, err)\n *     Return 1 when val is valid, and 0 when invalid.\n *     Optionally set err to a static error string.\n * int update_fn(val, prev, err)\n *     This function is called only for CONFIG SET command (not at config file parsing)\n *     It is called after the actual config is applied,\n *     Return 1 for success, and 0 for failure.\n *     Optionally set err to a static error string.\n *     On failure the config change will be reverted.\n */\n\n/* Configuration values that require no special handling to set, get, load or\n * rewrite. */\ntypedef struct boolConfigData {\n    int *config; /* The pointer to the server config this value is stored in */\n    const int default_value; /* The default value of the config on rewrite */\n    int (*is_valid_fn)(int val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(int val, int prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n} boolConfigData;\n\ntypedef struct stringConfigData {\n    char **config; /* Pointer to the server config this value is stored in. */\n    const char *default_value; /* Default value of the config on rewrite. */\n    int (*is_valid_fn)(char* val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(char* val, char* prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n    int convert_empty_to_null; /* Boolean indicating if empty strings should\n                                  be stored as a NULL value. */\n} stringConfigData;\n\ntypedef struct enumConfigData {\n    int *config; /* The pointer to the server config this value is stored in */\n    configEnum *enum_value; /* The underlying enum type this data represents */\n    const int default_value; /* The default value of the config on rewrite */\n    int (*is_valid_fn)(int val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(int val, int prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n} enumConfigData;\n\ntypedef enum numericType {\n    NUMERIC_TYPE_INT,\n    NUMERIC_TYPE_UINT,\n    NUMERIC_TYPE_LONG,\n    NUMERIC_TYPE_ULONG,\n    NUMERIC_TYPE_LONG_LONG,\n    NUMERIC_TYPE_ULONG_LONG,\n    NUMERIC_TYPE_SIZE_T,\n    NUMERIC_TYPE_SSIZE_T,\n    NUMERIC_TYPE_OFF_T,\n    NUMERIC_TYPE_TIME_T,\n} numericType;\n\ntypedef struct numericConfigData {\n    union {\n        int *i;\n        unsigned int *ui;\n        long *l;\n        unsigned long *ul;\n        long long *ll;\n        unsigned long long *ull;\n        size_t *st;\n        ssize_t *sst;\n        off_t *ot;\n        time_t *tt;\n    } config; /* The pointer to the numeric config this value is stored in */\n    int is_memory; /* Indicates if this value can be loaded as a memory value */\n    numericType numeric_type; /* An enum indicating the type of this value */\n    long long lower_bound; /* The lower bound of this numeric value */\n    long long upper_bound; /* The upper bound of this numeric value */\n    const long long default_value; /* The default value of the config on rewrite */\n    int (*is_valid_fn)(long long val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(long long val, long long prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n} numericConfigData;\n\ntypedef union typeData {\n    boolConfigData yesno;\n    stringConfigData string;\n    enumConfigData enumd;\n    numericConfigData numeric;\n} typeData;\n\ntypedef struct typeInterface {\n    /* Called on server start, to init the server with default value */\n    void (*init)(typeData data);\n    /* Called on server start, should return 1 on success, 0 on error and should set err */\n    int (*load)(typeData data, sds *argc, int argv, char **err);\n    /* Called on server startup and CONFIG SET, returns 1 on success, 0 on error\n     * and can set a verbose err string, update is true when called from CONFIG SET */\n    int (*set)(typeData data, sds value, int update, char **err);\n    /* Called on CONFIG GET, required to add output to the client */\n    void (*get)(client *c, typeData data);\n    /* Called on CONFIG REWRITE, required to rewrite the config state */\n    void (*rewrite)(typeData data, const char *name, struct rewriteConfigState *state);\n} typeInterface;\n\ntypedef struct standardConfig {\n    const char *name; /* The user visible name of this config */\n    const char *alias; /* An alias that can also be used for this config */\n    const int modifiable; /* Can this value be updated by CONFIG SET? */\n    typeInterface interface; /* The function pointers that define the type interface */\n    typeData data; /* The type specific data exposed used by the interface */\n} standardConfig;\n\nstandardConfig configs[];\n\n/*-----------------------------------------------------------------------------\n * Enum access functions\n *----------------------------------------------------------------------------*/\n\n/* Get enum value from name. If there is no match INT_MIN is returned. */\nint configEnumGetValue(configEnum *ce, char *name) {\n    while(ce->name != NULL) {\n        if (!strcasecmp(ce->name,name)) return ce->val;\n        ce++;\n    }\n    return INT_MIN;\n}\n\n/* Get enum name from value. If no match is found NULL is returned. */\nconst char *configEnumGetName(configEnum *ce, int val) {\n    while(ce->name != NULL) {\n        if (ce->val == val) return ce->name;\n        ce++;\n    }\n    return NULL;\n}\n\n/* Wrapper for configEnumGetName() returning \"unknown\" instead of NULL if\n * there is no match. */\nconst char *configEnumGetNameOrUnknown(configEnum *ce, int val) {\n    const char *name = configEnumGetName(ce,val);\n    return name ? name : \"unknown\";\n}\n\n/* Used for INFO generation. */\nconst char *evictPolicyToString(void) {\n    return configEnumGetNameOrUnknown(maxmemory_policy_enum,server.maxmemory_policy);\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(void) {\n    zfree(server.saveparams);\n    server.saveparams = NULL;\n    server.saveparamslen = 0;\n}\n\nvoid queueLoadModule(sds path, sds *argv, int argc) {\n    int i;\n    struct moduleLoadQueueEntry *loadmod;\n\n    loadmod = zmalloc(sizeof(struct moduleLoadQueueEntry));\n    loadmod->argv = zmalloc(sizeof(robj*)*argc);\n    loadmod->path = sdsnew(path);\n    loadmod->argc = argc;\n    for (i = 0; i < argc; i++) {\n        loadmod->argv[i] = createRawStringObject(argv[i],sdslen(argv[i]));\n    }\n    listAddNodeTail(server.loadmodule_queue,loadmod);\n}\n\nvoid initConfigValues() {\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        config->interface.init(config->data);\n    }\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        lines[i] = sdstrim(lines[i],\" \\t\\r\\n\");\n\n        /* Skip comments and blank lines */\n        if (lines[i][0] == '#' || lines[i][0] == '\\0') continue;\n\n        /* Split into arguments */\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        if (argc == 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n        sdstolower(argv[0]);\n\n        /* Iterate the configs that are standard */\n        int match = 0;\n        for (standardConfig *config = configs; config->name != NULL; config++) {\n            if ((!strcasecmp(argv[0],config->name) ||\n                (config->alias && !strcasecmp(argv[0],config->alias))))\n            {\n                if (argc != 2) {\n                    err = \"wrong number of arguments\";\n                    goto loaderr;\n                }\n                if (!config->interface.set(config->data, argv[1], 0, &err)) {\n                    goto loaderr;\n                }\n\n                match = 1;\n                break;\n            }\n        }\n\n        if (match) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* Execute config directives */\n        if (!strcasecmp(argv[0],\"bind\") && argc >= 2) {\n            int j, addresses = argc-1;\n\n            if (addresses > CONFIG_BINDADDR_MAX) {\n                err = \"Too many bind addresses specified\"; goto loaderr;\n            }\n            /* Free old bind addresses */\n            for (j = 0; j < server.bindaddr_count; j++) {\n                zfree(server.bindaddr[j]);\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],\"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                serverLog(LL_WARNING,\"Can't chdir to '%s': %s\",\n                    argv[1], strerror(errno));\n                exit(1);\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],\"include\") && argc == 2) {\n            loadServerConfig(argv[1],NULL);\n        } else if ((!strcasecmp(argv[0],\"client-query-buffer-limit\")) && argc == 2) {\n             server.client_max_querybuf_len = memtoll(argv[1],NULL);\n        } else if ((!strcasecmp(argv[0],\"slaveof\") ||\n                    !strcasecmp(argv[0],\"replicaof\")) && argc == 3) {\n            slaveof_linenum = linenum;\n            server.masterhost = sdsnew(argv[1]);\n            server.masterport = atoi(argv[2]);\n            server.repl_state = REPL_STATE_CONNECT;\n        } else if (!strcasecmp(argv[0],\"requirepass\") && argc == 2) {\n            if (strlen(argv[1]) > CONFIG_AUTHPASS_MAX_LEN) {\n                err = \"Password is longer than CONFIG_AUTHPASS_MAX_LEN\";\n                goto loaderr;\n            }\n            /* The old \"requirepass\" directive just translates to setting\n             * a password to the default user. The only thing we do\n             * additionally is to remember the cleartext password in this\n             * case, for backward compatibility with Redis <= 5. */\n            ACLSetUser(DefaultUser,\"resetpass\",-1);\n            sds aclop = sdscatprintf(sdsempty(),\">%s\",argv[1]);\n            ACLSetUser(DefaultUser,aclop,sdslen(aclop));\n            sdsfree(aclop);\n            sdsfree(server.requirepass);\n            server.requirepass = sdsnew(argv[1]);\n        } else if (!strcasecmp(argv[0],\"list-max-ziplist-entries\") && argc == 2){\n            /* DEAD OPTION */\n        } else if (!strcasecmp(argv[0],\"list-max-ziplist-value\") && argc == 2) {\n            /* DEAD OPTION */\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            serverAssert(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-config-file\") && argc == 2) {\n            zfree(server.cluster_configfile);\n            server.cluster_configfile = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"client-output-buffer-limit\") &&\n                   argc == 5)\n        {\n            int class = getClientTypeByName(argv[1]);\n            unsigned long long hard, soft;\n            int soft_seconds;\n\n            if (class == -1 || class == CLIENT_TYPE_MASTER) {\n                err = \"Unrecognized client limit class: the user specified \"\n                \"an invalid one, or 'master' which has no buffer limits.\";\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],\"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],\"user\") && argc >= 2) {\n            int argc_err;\n            if (ACLAppendUserForLoading(argv,argc,&argc_err) == C_ERR) {\n                char buf[1024];\n                char *errmsg = ACLSetUserStringError();\n                snprintf(buf,sizeof(buf),\"Error in user declaration '%s': %s\",\n                    argv[argc_err],errmsg);\n                err = buf;\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"loadmodule\") && argc >= 2) {\n            queueLoadModule(argv[1],&argv[2],argc-2);\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            if (argc != 1) {\n                if (!server.sentinel_mode) {\n                    err = \"sentinel directive while not in sentinel mode\";\n                    goto loaderr;\n                }\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 = \"replicaof 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 (Redis %s) ***\\n\",\n        REDIS_VERSION);\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 * The function appends the additional configuration directives stored\n * in the 'options' string to the config file before loading.\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. */\nvoid loadServerConfig(char *filename, char *options) {\n    sds config = sdsempty();\n    char buf[CONFIG_MAX_LINE+1];\n\n    /* Load the file content */\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                serverLog(LL_WARNING,\n                    \"Fatal error, can't open config file '%s'\", filename);\n                exit(1);\n            }\n        }\n        while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL)\n            config = sdscat(config,buf);\n        if (fp != stdin) fclose(fp);\n    }\n    /* Append the additional options */\n    if (options) {\n        config = sdscat(config,\"\\n\");\n        config = sdscat(config,options);\n    }\n    loadServerConfigFromString(config);\n    sdsfree(config);\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG SET implementation\n *----------------------------------------------------------------------------*/\n\n#define config_set_bool_field(_name,_var) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \\\n        int yn = yesnotoi(o->ptr); \\\n        if (yn == -1) goto badfmt; \\\n        _var = yn;\n\n#define config_set_numerical_field(_name,_var,min,max) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \\\n        if (getLongLongFromObject(o,&ll) == C_ERR) goto badfmt; \\\n        if (min != LLONG_MIN && ll < min) goto badfmt; \\\n        if (max != LLONG_MAX && ll > max) goto badfmt; \\\n        _var = ll;\n\n#define config_set_memory_field(_name,_var) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \\\n        ll = memtoll(o->ptr,&err); \\\n        if (err || ll < 0) goto badfmt; \\\n        _var = ll;\n\n#define config_set_special_field(_name) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) {\n\n#define config_set_special_field_with_alias(_name1,_name2) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name1) || \\\n               !strcasecmp(c->argv[2]->ptr,_name2)) {\n\n#define config_set_else } else\n\nvoid configSetCommand(client *c) {\n    robj *o;\n    long long ll;\n    int err;\n    char *errstr = NULL;\n    serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));\n    serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));\n    o = c->argv[3];\n\n    /* Iterate the configs that are standard */\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        if(config->modifiable && (!strcasecmp(c->argv[2]->ptr,config->name) ||\n            (config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))\n        {\n            if (!config->interface.set(config->data,o->ptr,1,&errstr)) {\n                goto badfmt;\n            }\n            addReply(c,shared.ok);\n            return;\n        }\n    }\n\n    if (0) { /* this starts the config_set macros else-if chain. */\n\n    /* Special fields that can't be handled with general macros. */\n    config_set_special_field(\"requirepass\") {\n        if (sdslen(o->ptr) > CONFIG_AUTHPASS_MAX_LEN) goto badfmt;\n        /* The old \"requirepass\" directive just translates to setting\n         * a password to the default user. The only thing we do\n         * additionally is to remember the cleartext password in this\n         * case, for backward compatibility with Redis <= 5. */\n        ACLSetUser(DefaultUser,\"resetpass\",-1);\n        sds aclop = sdscatprintf(sdsempty(),\">%s\",(char*)o->ptr);\n        ACLSetUser(DefaultUser,aclop,sdslen(aclop));\n        sdsfree(aclop);\n        sdsfree(server.requirepass);\n        server.requirepass = sdsnew(o->ptr);\n    } config_set_special_field(\"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    } config_set_special_field(\"dir\") {\n        if (chdir((char*)o->ptr) == -1) {\n            addReplyErrorFormat(c,\"Changing directory: %s\", strerror(errno));\n            return;\n        }\n    } config_set_special_field(\"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            long val;\n\n            if ((j % 4) == 0) {\n                int class = getClientTypeByName(v[j]);\n                if (class == -1 || class == CLIENT_TYPE_MASTER) {\n                    sdsfreesplitres(v,vlen);\n                    goto badfmt;\n                }\n            } else {\n                val = memtoll(v[j], &err);\n                if (err || 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 = getClientTypeByName(v[j]);\n            hard = memtoll(v[j+1],NULL);\n            soft = memtoll(v[j+2],NULL);\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    } config_set_special_field(\"notify-keyspace-events\") {\n        int flags = keyspaceEventsStringToFlags(o->ptr);\n\n        if (flags == -1) goto badfmt;\n        server.notify_keyspace_events = flags;\n    /* Numerical fields.\n     * config_set_numerical_field(name,var,min,max) */\n    } config_set_numerical_field(\n      \"watchdog-period\",ll,0,INT_MAX) {\n        if (ll)\n            enableWatchdog(ll);\n        else\n            disableWatchdog();\n    /* Memory fields.\n     * config_set_memory_field(name,var) */\n    } config_set_memory_field(\n      \"client-query-buffer-limit\",server.client_max_querybuf_len) {\n    /* Everything else is an error... */\n    } config_set_else {\n        addReplyErrorFormat(c,\"Unsupported CONFIG parameter: %s\",\n            (char*)c->argv[2]->ptr);\n        return;\n    }\n\n    /* On success we just return a generic OK for all the options. */\n    addReply(c,shared.ok);\n    return;\n\nbadfmt: /* Bad format errors */\n    if (errstr) {\n        addReplyErrorFormat(c,\"Invalid argument '%s' for CONFIG SET '%s' - %s\",\n                (char*)o->ptr,\n                (char*)c->argv[2]->ptr,\n                errstr);\n    } else {\n        addReplyErrorFormat(c,\"Invalid argument '%s' for CONFIG SET '%s'\",\n                (char*)o->ptr,\n                (char*)c->argv[2]->ptr);\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG GET implementation\n *----------------------------------------------------------------------------*/\n\n#define config_get_string_field(_name,_var) do { \\\n    if (stringmatch(pattern,_name,1)) { \\\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,1)) { \\\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,1)) { \\\n        ll2string(buf,sizeof(buf),_var); \\\n        addReplyBulkCString(c,_name); \\\n        addReplyBulkCString(c,buf); \\\n        matches++; \\\n    } \\\n} while(0);\n\n\nvoid configGetCommand(client *c) {\n    robj *o = c->argv[2];\n    void *replylen = addReplyDeferredLen(c);\n    char *pattern = o->ptr;\n    char buf[128];\n    int matches = 0;\n    serverAssertWithInfo(c,o,sdsEncodedObject(o));\n\n    /* Iterate the configs that are standard */\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        if (stringmatch(pattern,config->name,1)) {\n            addReplyBulkCString(c,config->name);\n            config->interface.get(c,config->data);\n            matches++;\n        }\n        if (config->alias && stringmatch(pattern,config->alias,1)) {\n            addReplyBulkCString(c,config->alias);\n            config->interface.get(c,config->data);\n            matches++;\n        }\n    }\n\n    /* String values */\n    config_get_string_field(\"logfile\",server.logfile);\n\n    /* Numerical values */\n    config_get_numerical_field(\"client-query-buffer-limit\",server.client_max_querybuf_len);\n    config_get_numerical_field(\"watchdog-period\",server.watchdog_period);\n\n    /* Everything we can't handle with macros follows. */\n\n    if (stringmatch(pattern,\"dir\",1)) {\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,\"save\",1)) {\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,\"client-output-buffer-limit\",1)) {\n        sds buf = sdsempty();\n        int j;\n\n        for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {\n            buf = sdscatprintf(buf,\"%s %llu %llu %ld\",\n                    getClientTypeName(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 != CLIENT_TYPE_OBUF_COUNT-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\",1)) {\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\",1) ||\n        stringmatch(pattern,\"replicaof\",1))\n    {\n        char *optname = stringmatch(pattern,\"slaveof\",1) ?\n                        \"slaveof\" : \"replicaof\";\n        char buf[256];\n\n        addReplyBulkCString(c,optname);\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\",1)) {\n        sds flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);\n\n        addReplyBulkCString(c,\"notify-keyspace-events\");\n        addReplyBulkSds(c,flags);\n        matches++;\n    }\n    if (stringmatch(pattern,\"bind\",1)) {\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    if (stringmatch(pattern,\"requirepass\",1)) {\n        addReplyBulkCString(c,\"requirepass\");\n        sds password = server.requirepass;\n        if (password) {\n            addReplyBulkCBuffer(c,password,sdslen(password));\n        } else {\n            addReplyBulkCString(c,\"\");\n        }\n        matches++;\n    }\n\n    setDeferredMapLen(c,replylen,matches);\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). */\nuint64_t 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, const 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    if (fp == NULL && errno != ENOENT) return NULL;\n\n    char buf[CONFIG_MAX_LINE+1];\n    int linenum = -1;\n    struct rewriteConfigState *state = zmalloc(sizeof(*state));\n    state->option_to_line = dictCreate(&optionToLineDictType,NULL);\n    state->rewritten = dictCreate(&optionSetDictType,NULL);\n    state->numlines = 0;\n    state->lines = NULL;\n    state->has_tail = 0;\n    if (fp == NULL) return state;\n\n    /* Read the old file line by line, populate the state. */\n    while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) {\n        int argc;\n        sds *argv;\n        sds line = sdstrim(sdsnew(buf),\"\\r\\n\\t \");\n\n        linenum++; /* Zero based, so we init at -1 */\n\n        /* Handle comments and empty lines. */\n        if (line[0] == '#' || line[0] == '\\0') {\n            if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))\n                state->has_tail = 1;\n            rewriteConfigAppendLine(state,line);\n            continue;\n        }\n\n        /* Not a comment, split into arguments. */\n        argv = sdssplitargs(line,&argc);\n        if (argv == NULL) {\n            /* Apparently the line is unparsable for some reason, for\n             * instance it may have unbalanced quotes. Load it as a\n             * comment. */\n            sds aux = sdsnew(\"# ??? \");\n            aux = sdscatsds(aux,line);\n            sdsfree(line);\n            rewriteConfigAppendLine(state,aux);\n            continue;\n        }\n\n        sdstolower(argv[0]); /* We only want lowercase config directives. */\n\n        /* Now we populate the state according to the content of this line.\n         * Append the line and populate the option -> line numbers map. */\n        rewriteConfigAppendLine(state,line);\n\n        /* Translate options using the word \"slave\" to the corresponding name\n         * \"replica\", before adding such option to the config name -> lines\n         * mapping. */\n        char *p = strstr(argv[0],\"slave\");\n        if (p) {\n            sds alt = sdsempty();\n            alt = sdscatlen(alt,argv[0],p-argv[0]);;\n            alt = sdscatlen(alt,\"replica\",7);\n            alt = sdscatlen(alt,p+5,strlen(p+5));\n            sdsfree(argv[0]);\n            argv[0] = alt;\n        }\n        rewriteConfigAddLineNumberToOption(state,argv[0],linenum);\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, const 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, const 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, const 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, const char *option, char *value, const 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    /* Set force to zero if the value is set to its default. */\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, const 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. It takes as usually state and option name,\n * and in addition the enumeration array and the default value for the\n * option. */\nvoid rewriteConfigEnumOption(struct rewriteConfigState *state, const char *option, int value, configEnum *ce, int defval) {\n    sds line;\n    const char *name = configEnumGetNameOrUnknown(ce,value);\n    int force = value != defval;\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 user option. */\nvoid rewriteConfigUserOption(struct rewriteConfigState *state) {\n    /* If there is a user file defined we just mark this configuration\n     * directive as processed, so that all the lines containing users\n     * inside the config file gets discarded. */\n    if (server.acl_filename[0] != '\\0') {\n        rewriteConfigMarkAsProcessed(state,\"user\");\n        return;\n    }\n\n    /* Otherwise scan the list of users and rewrite every line. Note that\n     * in case the list here is empty, the effect will just be to comment\n     * all the users directive inside the config file. */\n    raxIterator ri;\n    raxStart(&ri,Users);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        user *u = ri.data;\n        sds line = sdsnew(\"user \");\n        line = sdscatsds(line,u->name);\n        line = sdscatlen(line,\" \",1);\n        sds descr = ACLDescribeUser(u);\n        line = sdscatsds(line,descr);\n        sdsfree(descr);\n        rewriteConfigRewriteLine(state,\"user\",line,1);\n    }\n    raxStop(&ri);\n\n    /* Mark \"user\" as processed in case there are no defined users. */\n    rewriteConfigMarkAsProcessed(state,\"user\");\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, char *option) {\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,option);\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 < CLIENT_TYPE_OBUF_COUNT; 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        char *typename = getClientTypeName(j);\n        if (!strcmp(typename,\"slave\")) typename = \"replica\";\n        line = sdscatprintf(sdsempty(),\"%s %s %s %s %ld\",\n                option, typename, 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/* Rewrite the requirepass option. */\nvoid rewriteConfigRequirepassOption(struct rewriteConfigState *state, char *option) {\n    int force = 1;\n    sds line;\n    sds password = server.requirepass;\n\n    /* If there is no password set, we don't want the requirepass option\n     * to be present in the configuration at all. */\n    if (password == NULL) {\n        rewriteConfigMarkAsProcessed(state,option);\n        return;\n    }\n\n    line = sdsnew(option);\n    line = sdscatlen(line, \" \", 1);\n    line = sdscatsds(line, password);\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            serverLog(LL_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    /* Iterate the configs that are standard */\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        config->interface.rewrite(config->data, config->name, state);\n    }\n\n    rewriteConfigBindOption(state);\n    rewriteConfigOctalOption(state,\"unixsocketperm\",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);\n    rewriteConfigStringOption(state,\"logfile\",server.logfile,CONFIG_DEFAULT_LOGFILE);\n    rewriteConfigSaveOption(state);\n    rewriteConfigUserOption(state);\n    rewriteConfigDirOption(state);\n    rewriteConfigSlaveofOption(state,\"replicaof\");\n    rewriteConfigRequirepassOption(state,\"requirepass\");\n    rewriteConfigBytesOption(state,\"client-query-buffer-limit\",server.client_max_querybuf_len,PROTO_MAX_QUERYBUF_LEN);\n    rewriteConfigStringOption(state,\"cluster-config-file\",server.cluster_configfile,CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);\n    rewriteConfigNotifykeyspaceeventsOption(state);\n    rewriteConfigClientoutputbufferlimitOption(state);\n\n    /* Rewrite Sentinel config if in Sentinel mode. */\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 * Configs that fit one of the major types and require no special handling\n *----------------------------------------------------------------------------*/\n#define LOADBUF_SIZE 256\nstatic char loadbuf[LOADBUF_SIZE];\n\n#define MODIFIABLE_CONFIG 1\n#define IMMUTABLE_CONFIG 0\n\n#define embedCommonConfig(config_name, config_alias, is_modifiable) \\\n    .name = (config_name), \\\n    .alias = (config_alias), \\\n    .modifiable = (is_modifiable),\n\n#define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \\\n    .init = (initfn), \\\n    .set = (setfn), \\\n    .get = (getfn), \\\n    .rewrite = (rewritefn) \\\n},\n\n/* What follows is the generic config types that are supported. To add a new\n * config with one of these types, add it to the standardConfig table with\n * the creation macro for each type.\n *\n * Each type contains the following:\n * * A function defining how to load this type on startup.\n * * A function defining how to update this type on CONFIG SET.\n * * A function defining how to serialize this type on CONFIG SET.\n * * A function defining how to rewrite this type on CONFIG REWRITE.\n * * A Macro defining how to create this type.\n */\n\n/* Bool Configs */\nstatic void boolConfigInit(typeData data) {\n    *data.yesno.config = data.yesno.default_value;\n}\n\nstatic int boolConfigSet(typeData data, sds value, int update, char **err) {\n    int yn = yesnotoi(value);\n    if (yn == -1) {\n        *err = \"argument must be 'yes' or 'no'\";\n        return 0;\n    }\n    if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))\n        return 0;\n    int prev = *(data.yesno.config);\n    *(data.yesno.config) = yn;\n    if (update && data.yesno.update_fn && !data.yesno.update_fn(yn, prev, err)) {\n        *(data.yesno.config) = prev;\n        return 0;\n    }\n    return 1;\n}\n\nstatic void boolConfigGet(client *c, typeData data) {\n    addReplyBulkCString(c, *data.yesno.config ? \"yes\" : \"no\");\n}\n\nstatic void boolConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value);\n}\n\n#define createBoolConfig(name, alias, modifiable, config_addr, default, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \\\n    .data.yesno = { \\\n        .config = &(config_addr), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n    } \\\n}\n\n/* String Configs */\nstatic void stringConfigInit(typeData data) {\n    if (data.string.convert_empty_to_null) {\n        *data.string.config = data.string.default_value ? zstrdup(data.string.default_value) : NULL;\n    } else {\n        *data.string.config = zstrdup(data.string.default_value);\n    }\n}\n\nstatic int stringConfigSet(typeData data, sds value, int update, char **err) {\n    if (data.string.is_valid_fn && !data.string.is_valid_fn(value, err))\n        return 0;\n    char *prev = *data.string.config;\n    if (data.string.convert_empty_to_null) {\n        *data.string.config = value[0] ? zstrdup(value) : NULL;\n    } else {\n        *data.string.config = zstrdup(value);\n    }\n    if (update && data.string.update_fn && !data.string.update_fn(*data.string.config, prev, err)) {\n        zfree(*data.string.config);\n        *data.string.config = prev;\n        return 0;\n    }\n    zfree(prev);\n    return 1;\n}\n\nstatic void stringConfigGet(client *c, typeData data) {\n    addReplyBulkCString(c, *data.string.config ? *data.string.config : \"\");\n}\n\nstatic void stringConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    rewriteConfigStringOption(state, name,*(data.string.config), data.string.default_value);\n}\n\n#define ALLOW_EMPTY_STRING 0\n#define EMPTY_STRING_IS_NULL 1\n\n#define createStringConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \\\n    .data.string = { \\\n        .config = &(config_addr), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n        .convert_empty_to_null = (empty_to_null), \\\n    } \\\n}\n\n/* Enum configs */\nstatic void enumConfigInit(typeData data) {\n    *data.enumd.config = data.enumd.default_value;\n}\n\nstatic int enumConfigSet(typeData data, sds value, int update, char **err) {\n    int enumval = configEnumGetValue(data.enumd.enum_value, value);\n    if (enumval == INT_MIN) {\n        sds enumerr = sdsnew(\"argument must be one of the following: \");\n        configEnum *enumNode = data.enumd.enum_value;\n        while(enumNode->name != NULL) {\n            enumerr = sdscatlen(enumerr, enumNode->name,\n                                strlen(enumNode->name));\n            enumerr = sdscatlen(enumerr, \", \", 2);\n            enumNode++;\n        }\n        sdsrange(enumerr,0,-3); /* Remove final \", \". */\n\n        strncpy(loadbuf, enumerr, LOADBUF_SIZE);\n        loadbuf[LOADBUF_SIZE - 1] = '\\0';\n\n        sdsfree(enumerr);\n        *err = loadbuf;\n        return 0;\n    }\n    if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))\n        return 0;\n    int prev = *(data.enumd.config);\n    *(data.enumd.config) = enumval;\n    if (update && data.enumd.update_fn && !data.enumd.update_fn(enumval, prev, err)) {\n        *(data.enumd.config) = prev;\n        return 0;\n    }\n    return 1;\n}\n\nstatic void enumConfigGet(client *c, typeData data) {\n    addReplyBulkCString(c, configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));\n}\n\nstatic void enumConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value);\n}\n\n#define createEnumConfig(name, alias, modifiable, enum, config_addr, default, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \\\n    .data.enumd = { \\\n        .config = &(config_addr), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n        .enum_value = (enum), \\\n    } \\\n}\n\n/* Gets a 'long long val' and sets it into the union, using a macro to get\n * compile time type check. */\n#define SET_NUMERIC_TYPE(val) \\\n    if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \\\n        *(data.numeric.config.i) = (int) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \\\n        *(data.numeric.config.ui) = (unsigned int) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \\\n        *(data.numeric.config.l) = (long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \\\n        *(data.numeric.config.ul) = (unsigned long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \\\n        *(data.numeric.config.ll) = (long long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \\\n        *(data.numeric.config.ull) = (unsigned long long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \\\n        *(data.numeric.config.st) = (size_t) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \\\n        *(data.numeric.config.sst) = (ssize_t) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \\\n        *(data.numeric.config.ot) = (off_t) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \\\n        *(data.numeric.config.tt) = (time_t) val; \\\n    }\n\n/* Gets a 'long long val' and sets it with the value from the union, using a\n * macro to get compile time type check. */\n#define GET_NUMERIC_TYPE(val) \\\n    if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \\\n        val = *(data.numeric.config.i); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \\\n        val = *(data.numeric.config.ui); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \\\n        val = *(data.numeric.config.l); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \\\n        val = *(data.numeric.config.ul); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \\\n        val = *(data.numeric.config.ll); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \\\n        val = *(data.numeric.config.ull); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \\\n        val = *(data.numeric.config.st); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \\\n        val = *(data.numeric.config.sst); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \\\n        val = *(data.numeric.config.ot); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \\\n        val = *(data.numeric.config.tt); \\\n    }\n\n/* Numeric configs */\nstatic void numericConfigInit(typeData data) {\n    SET_NUMERIC_TYPE(data.numeric.default_value)\n}\n\nstatic int numericBoundaryCheck(typeData data, long long ll, char **err) {\n    if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG ||\n        data.numeric.numeric_type == NUMERIC_TYPE_UINT ||\n        data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {\n        /* Boundary check for unsigned types */\n        unsigned long long ull = ll;\n        unsigned long long upper_bound = data.numeric.upper_bound;\n        unsigned long long lower_bound = data.numeric.lower_bound;\n        if (ull > upper_bound || ull < lower_bound) {\n            snprintf(loadbuf, LOADBUF_SIZE,\n                \"argument must be between %llu and %llu inclusive\",\n                lower_bound,\n                upper_bound);\n            *err = loadbuf;\n            return 0;\n        }\n    } else {\n        /* Boundary check for signed types */\n        if (ll > data.numeric.upper_bound || ll < data.numeric.lower_bound) {\n            snprintf(loadbuf, LOADBUF_SIZE,\n                \"argument must be between %lld and %lld inclusive\",\n                data.numeric.lower_bound,\n                data.numeric.upper_bound);\n            *err = loadbuf;\n            return 0;\n        }\n    }\n    return 1;\n}\n\nstatic int numericConfigSet(typeData data, sds value, int update, char **err) {\n    long long ll, prev = 0;\n    if (data.numeric.is_memory) {\n        int memerr;\n        ll = memtoll(value, &memerr);\n        if (memerr || ll < 0) {\n            *err = \"argument must be a memory value\";\n            return 0;\n        }\n    } else {\n        if (!string2ll(value, sdslen(value),&ll)) {\n            *err = \"argument couldn't be parsed into an integer\" ;\n            return 0;\n        }\n    }\n\n    if (!numericBoundaryCheck(data, ll, err))\n        return 0;\n\n    if (data.numeric.is_valid_fn && !data.numeric.is_valid_fn(ll, err))\n        return 0;\n\n    GET_NUMERIC_TYPE(prev)\n    SET_NUMERIC_TYPE(ll)\n\n    if (update && data.numeric.update_fn && !data.numeric.update_fn(ll, prev, err)) {\n        SET_NUMERIC_TYPE(prev)\n        return 0;\n    }\n    return 1;\n}\n\nstatic void numericConfigGet(client *c, typeData data) {\n    char buf[128];\n    long long value = 0;\n\n    GET_NUMERIC_TYPE(value)\n\n    ll2string(buf, sizeof(buf), value);\n    addReplyBulkCString(c, buf);\n}\n\nstatic void numericConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    long long value = 0;\n\n    GET_NUMERIC_TYPE(value)\n\n    if (data.numeric.is_memory) {\n        rewriteConfigBytesOption(state, name, value, data.numeric.default_value);\n    } else {\n        rewriteConfigNumericalOption(state, name, value, data.numeric.default_value);\n    }\n}\n\n#define INTEGER_CONFIG 0\n#define MEMORY_CONFIG 1\n\n#define embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite) \\\n    .data.numeric = { \\\n        .lower_bound = (lower), \\\n        .upper_bound = (upper), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n        .is_memory = (memory),\n\n#define createIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_INT, \\\n        .config.i = &(config_addr) \\\n    } \\\n}\n\n#define createUIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_UINT, \\\n        .config.ui = &(config_addr) \\\n    } \\\n}\n\n#define createLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_LONG, \\\n        .config.l = &(config_addr) \\\n    } \\\n}\n\n#define createULongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_ULONG, \\\n        .config.ul = &(config_addr) \\\n    } \\\n}\n\n#define createLongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_LONG_LONG, \\\n        .config.ll = &(config_addr) \\\n    } \\\n}\n\n#define createULongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_ULONG_LONG, \\\n        .config.ull = &(config_addr) \\\n    } \\\n}\n\n#define createSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_SIZE_T, \\\n        .config.st = &(config_addr) \\\n    } \\\n}\n\n#define createSSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_SSIZE_T, \\\n        .config.sst = &(config_addr) \\\n    } \\\n}\n\n#define createTimeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_TIME_T, \\\n        .config.tt = &(config_addr) \\\n    } \\\n}\n\n#define createOffTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_OFF_T, \\\n        .config.ot = &(config_addr) \\\n    } \\\n}\n\nstatic int isValidActiveDefrag(int val, char **err) {\n#ifndef HAVE_DEFRAG\n    if (val) {\n        *err = \"Active defragmentation cannot be enabled: it \"\n               \"requires a Redis server compiled with a modified Jemalloc \"\n               \"like the one shipped by default with the Redis source \"\n               \"distribution\";\n        return 0;\n    }\n#else\n    UNUSED(val);\n    UNUSED(err);\n#endif\n    return 1;\n}\n\nstatic int isValidDBfilename(char *val, char **err) {\n    if (!pathIsBaseName(val)) {\n        *err = \"dbfilename can't be a path, just a filename\";\n        return 0;\n    }\n    return 1;\n}\n\nstatic int isValidAOFfilename(char *val, char **err) {\n    if (!pathIsBaseName(val)) {\n        *err = \"appendfilename can't be a path, just a filename\";\n        return 0;\n    }\n    return 1;\n}\n\nstatic int updateHZ(long long val, long long prev, char **err) {\n    UNUSED(prev);\n    UNUSED(err);\n    /* Hz is more an hint from the user, so we accept values out of range\n     * but cap them to reasonable values. */\n    server.config_hz = val;\n    if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;\n    if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;\n    server.hz = server.config_hz;\n    return 1;\n}\n\nstatic int updateJemallocBgThread(int val, int prev, char **err) {\n    UNUSED(prev);\n    UNUSED(err);\n    set_jemalloc_bg_thread(val);\n    return 1;\n}\n\nstatic int updateReplBacklogSize(long long val, long long prev, char **err) {\n    /* resizeReplicationBacklog sets server.repl_backlog_size, and relies on\n     * being able to tell when the size changes, so restore prev becore calling it. */\n    UNUSED(err);\n    server.repl_backlog_size = prev;\n    resizeReplicationBacklog(val);\n    return 1;\n}\n\nstatic int updateMaxmemory(long long val, long long prev, char **err) {\n    UNUSED(prev);\n    UNUSED(err);\n    if (val) {\n        size_t used = zmalloc_used_memory()-freeMemoryGetNotCountedMemory();\n        if ((unsigned long long)val < used) {\n            serverLog(LL_WARNING,\"WARNING: the new maxmemory value set via CONFIG SET (%llu) is smaller than the current memory usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.\", server.maxmemory, used);\n        }\n        freeMemoryIfNeededAndSafe();\n    }\n    return 1;\n}\n\nstatic int updateGoodSlaves(long long val, long long prev, char **err) {\n    UNUSED(val);\n    UNUSED(prev);\n    UNUSED(err);\n    refreshGoodSlavesCount();\n    return 1;\n}\n\nstatic int updateAppendonly(int val, int prev, char **err) {\n    UNUSED(prev);\n    if (val == 0 && server.aof_state != AOF_OFF) {\n        stopAppendOnly();\n    } else if (val && server.aof_state == AOF_OFF) {\n        if (startAppendOnly() == C_ERR) {\n            *err = \"Unable to turn on AOF. Check server logs.\";\n            return 0;\n        }\n    }\n    return 1;\n}\n\nstatic int updateMaxclients(long long val, long long prev, char **err) {\n    /* Try to check if the OS is capable of supporting so many FDs. */\n    if (val > prev) {\n        adjustOpenFilesLimit();\n        if (server.maxclients != val) {\n            static char msg[128];\n            sprintf(msg, \"The operating system is not able to handle the specified number of clients, try with %d\", server.maxclients);\n            *err = msg;\n            if (server.maxclients > prev) {\n                server.maxclients = prev;\n                adjustOpenFilesLimit();\n            }\n            return 0;\n        }\n        if ((unsigned int) aeGetSetSize(server.el) <\n            server.maxclients + CONFIG_FDSET_INCR)\n        {\n            if (aeResizeSetSize(server.el,\n                server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)\n            {\n                *err = \"The event loop API used by Redis is not able to handle the specified number of clients\";\n                return 0;\n            }\n        }\n    }\n    return 1;\n}\n\n#ifdef USE_OPENSSL\nstatic int updateTlsCfg(char *val, char *prev, char **err) {\n    UNUSED(val);\n    UNUSED(prev);\n    UNUSED(err);\n    if (tlsConfigure(&server.tls_ctx_config) == C_ERR) {\n        *err = \"Unable to configure tls-cert-file. Check server logs.\";\n        return 0;\n    }\n    return 1;\n}\nstatic int updateTlsCfgBool(int val, int prev, char **err) {\n    UNUSED(val);\n    UNUSED(prev);\n    return updateTlsCfg(NULL, NULL, err);\n}\n#endif  /* USE_OPENSSL */\n\nstandardConfig configs[] = {\n    /* Bool configs */\n    createBoolConfig(\"rdbchecksum\", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL),\n    createBoolConfig(\"daemonize\", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL),\n    createBoolConfig(\"io-threads-do-reads\", NULL, IMMUTABLE_CONFIG, server.io_threads_do_reads, 0,NULL, NULL), /* Read + parse from threads? */\n    createBoolConfig(\"lua-replicate-commands\", NULL, MODIFIABLE_CONFIG, server.lua_always_replicate_commands, 1, NULL, NULL),\n    createBoolConfig(\"always-show-logo\", NULL, IMMUTABLE_CONFIG, server.always_show_logo, 0, NULL, NULL),\n    createBoolConfig(\"protected-mode\", NULL, MODIFIABLE_CONFIG, server.protected_mode, 1, NULL, NULL),\n    createBoolConfig(\"rdbcompression\", NULL, MODIFIABLE_CONFIG, server.rdb_compression, 1, NULL, NULL),\n    createBoolConfig(\"rdb-del-sync-files\", NULL, MODIFIABLE_CONFIG, server.rdb_del_sync_files, 0, NULL, NULL),\n    createBoolConfig(\"activerehashing\", NULL, MODIFIABLE_CONFIG, server.activerehashing, 1, NULL, NULL),\n    createBoolConfig(\"stop-writes-on-bgsave-error\", NULL, MODIFIABLE_CONFIG, server.stop_writes_on_bgsave_err, 1, NULL, NULL),\n    createBoolConfig(\"dynamic-hz\", NULL, MODIFIABLE_CONFIG, server.dynamic_hz, 1, NULL, NULL), /* Adapt hz to # of clients.*/\n    createBoolConfig(\"lazyfree-lazy-eviction\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_eviction, 0, NULL, NULL),\n    createBoolConfig(\"lazyfree-lazy-expire\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_expire, 0, NULL, NULL),\n    createBoolConfig(\"lazyfree-lazy-server-del\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_server_del, 0, NULL, NULL),\n    createBoolConfig(\"lazyfree-lazy-user-del\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_user_del , 0, NULL, NULL),\n    createBoolConfig(\"repl-disable-tcp-nodelay\", NULL, MODIFIABLE_CONFIG, server.repl_disable_tcp_nodelay, 0, NULL, NULL),\n    createBoolConfig(\"repl-diskless-sync\", NULL, MODIFIABLE_CONFIG, server.repl_diskless_sync, 0, NULL, NULL),\n    createBoolConfig(\"gopher-enabled\", NULL, MODIFIABLE_CONFIG, server.gopher_enabled, 0, NULL, NULL),\n    createBoolConfig(\"aof-rewrite-incremental-fsync\", NULL, MODIFIABLE_CONFIG, server.aof_rewrite_incremental_fsync, 1, NULL, NULL),\n    createBoolConfig(\"no-appendfsync-on-rewrite\", NULL, MODIFIABLE_CONFIG, server.aof_no_fsync_on_rewrite, 0, NULL, NULL),\n    createBoolConfig(\"cluster-require-full-coverage\", NULL, MODIFIABLE_CONFIG, server.cluster_require_full_coverage, 1, NULL, NULL),\n    createBoolConfig(\"rdb-save-incremental-fsync\", NULL, MODIFIABLE_CONFIG, server.rdb_save_incremental_fsync, 1, NULL, NULL),\n    createBoolConfig(\"aof-load-truncated\", NULL, MODIFIABLE_CONFIG, server.aof_load_truncated, 1, NULL, NULL),\n    createBoolConfig(\"aof-use-rdb-preamble\", NULL, MODIFIABLE_CONFIG, server.aof_use_rdb_preamble, 1, NULL, NULL),\n    createBoolConfig(\"cluster-replica-no-failover\", \"cluster-slave-no-failover\", MODIFIABLE_CONFIG, server.cluster_slave_no_failover, 0, NULL, NULL), /* Failover by default. */\n    createBoolConfig(\"replica-lazy-flush\", \"slave-lazy-flush\", MODIFIABLE_CONFIG, server.repl_slave_lazy_flush, 0, NULL, NULL),\n    createBoolConfig(\"replica-serve-stale-data\", \"slave-serve-stale-data\", MODIFIABLE_CONFIG, server.repl_serve_stale_data, 1, NULL, NULL),\n    createBoolConfig(\"replica-read-only\", \"slave-read-only\", MODIFIABLE_CONFIG, server.repl_slave_ro, 1, NULL, NULL),\n    createBoolConfig(\"replica-ignore-maxmemory\", \"slave-ignore-maxmemory\", MODIFIABLE_CONFIG, server.repl_slave_ignore_maxmemory, 1, NULL, NULL),\n    createBoolConfig(\"jemalloc-bg-thread\", NULL, MODIFIABLE_CONFIG, server.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread),\n    createBoolConfig(\"activedefrag\", NULL, MODIFIABLE_CONFIG, server.active_defrag_enabled, 0, isValidActiveDefrag, NULL),\n    createBoolConfig(\"syslog-enabled\", NULL, IMMUTABLE_CONFIG, server.syslog_enabled, 0, NULL, NULL),\n    createBoolConfig(\"cluster-enabled\", NULL, IMMUTABLE_CONFIG, server.cluster_enabled, 0, NULL, NULL),\n    createBoolConfig(\"appendonly\", NULL, MODIFIABLE_CONFIG, server.aof_enabled, 0, NULL, updateAppendonly),\n    createBoolConfig(\"cluster-allow-reads-when-down\", NULL, MODIFIABLE_CONFIG, server.cluster_allow_reads_when_down, 0, NULL, NULL),\n\n\n    /* String Configs */\n    createStringConfig(\"aclfile\", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, \"\", NULL, NULL),\n    createStringConfig(\"unixsocket\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.unixsocket, NULL, NULL, NULL),\n    createStringConfig(\"pidfile\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.pidfile, NULL, NULL, NULL),\n    createStringConfig(\"replica-announce-ip\", \"slave-announce-ip\", MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.slave_announce_ip, NULL, NULL, NULL),\n    createStringConfig(\"masteruser\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masteruser, NULL, NULL, NULL),\n    createStringConfig(\"masterauth\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL),\n    createStringConfig(\"cluster-announce-ip\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_ip, NULL, NULL, NULL),\n    createStringConfig(\"syslog-ident\", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, \"redis\", NULL, NULL),\n    createStringConfig(\"dbfilename\", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, \"dump.rdb\", isValidDBfilename, NULL),\n    createStringConfig(\"appendfilename\", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.aof_filename, \"appendonly.aof\", isValidAOFfilename, NULL),\n    createStringConfig(\"server_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.server_cpulist, NULL, NULL, NULL),\n    createStringConfig(\"bio_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bio_cpulist, NULL, NULL, NULL),\n    createStringConfig(\"aof_rewrite_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.aof_rewrite_cpulist, NULL, NULL, NULL),\n    createStringConfig(\"bgsave_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),\n\n    /* Enum Configs */\n    createEnumConfig(\"supervised\", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),\n    createEnumConfig(\"syslog-facility\", NULL, IMMUTABLE_CONFIG, syslog_facility_enum, server.syslog_facility, LOG_LOCAL0, NULL, NULL),\n    createEnumConfig(\"repl-diskless-load\", NULL, MODIFIABLE_CONFIG, repl_diskless_load_enum, server.repl_diskless_load, REPL_DISKLESS_LOAD_DISABLED, NULL, NULL),\n    createEnumConfig(\"loglevel\", NULL, MODIFIABLE_CONFIG, loglevel_enum, server.verbosity, LL_NOTICE, NULL, NULL),\n    createEnumConfig(\"maxmemory-policy\", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),\n    createEnumConfig(\"appendfsync\", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),\n\n    /* Integer configs */\n    createIntConfig(\"databases\", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, server.dbnum, 16, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"port\", NULL, IMMUTABLE_CONFIG, 0, 65535, server.port, 6379, INTEGER_CONFIG, NULL, NULL), /* TCP port. */\n    createIntConfig(\"io-threads\", NULL, IMMUTABLE_CONFIG, 1, 128, server.io_threads_num, 1, INTEGER_CONFIG, NULL, NULL), /* Single threaded by default */\n    createIntConfig(\"auto-aof-rewrite-percentage\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.aof_rewrite_perc, 100, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"cluster-replica-validity-factor\", \"cluster-slave-validity-factor\", MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_slave_validity_factor, 10, INTEGER_CONFIG, NULL, NULL), /* Slave max data age factor. */\n    createIntConfig(\"list-max-ziplist-size\", NULL, MODIFIABLE_CONFIG, INT_MIN, INT_MAX, server.list_max_ziplist_size, -2, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"tcp-keepalive\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tcpkeepalive, 300, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"cluster-migration-barrier\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_migration_barrier, 1, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"active-defrag-cycle-min\", NULL, MODIFIABLE_CONFIG, 1, 99, server.active_defrag_cycle_min, 1, INTEGER_CONFIG, NULL, NULL), /* Default: 1% CPU min (at lower threshold) */\n    createIntConfig(\"active-defrag-cycle-max\", NULL, MODIFIABLE_CONFIG, 1, 99, server.active_defrag_cycle_max, 25, INTEGER_CONFIG, NULL, NULL), /* Default: 25% CPU max (at upper threshold) */\n    createIntConfig(\"active-defrag-threshold-lower\", NULL, MODIFIABLE_CONFIG, 0, 1000, server.active_defrag_threshold_lower, 10, INTEGER_CONFIG, NULL, NULL), /* Default: don't defrag when fragmentation is below 10% */\n    createIntConfig(\"active-defrag-threshold-upper\", NULL, MODIFIABLE_CONFIG, 0, 1000, server.active_defrag_threshold_upper, 100, INTEGER_CONFIG, NULL, NULL), /* Default: maximum defrag force at 100% fragmentation */\n    createIntConfig(\"lfu-log-factor\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_log_factor, 10, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"lfu-decay-time\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"replica-priority\", \"slave-priority\", MODIFIABLE_CONFIG, 0, INT_MAX, server.slave_priority, 100, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"repl-diskless-sync-delay\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"maxmemory-samples\", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"timeout\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */\n    createIntConfig(\"replica-announce-port\", \"slave-announce-port\", MODIFIABLE_CONFIG, 0, 65535, server.slave_announce_port, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"tcp-backlog\", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, server.tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */\n    createIntConfig(\"cluster-announce-bus-port\", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_bus_port, 0, INTEGER_CONFIG, NULL, NULL), /* Default: Use +10000 offset. */\n    createIntConfig(\"cluster-announce-port\", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.port */\n    createIntConfig(\"repl-timeout\", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_timeout, 60, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"repl-ping-replica-period\", \"repl-ping-slave-period\", MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_ping_slave_period, 10, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"list-compress-depth\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"rdb-key-save-delay\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"key-load-delay\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.key_load_delay, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"active-expire-effort\", NULL, MODIFIABLE_CONFIG, 1, 10, server.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */\n    createIntConfig(\"hz\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),\n    createIntConfig(\"min-replicas-to-write\", \"min-slaves-to-write\", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),\n    createIntConfig(\"min-replicas-max-lag\", \"min-slaves-max-lag\", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves),\n\n    /* Unsigned int configs */\n    createUIntConfig(\"maxclients\", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, server.maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),\n\n    /* Unsigned Long configs */\n    createULongConfig(\"active-defrag-max-scan-fields\", NULL, MODIFIABLE_CONFIG, 1, LONG_MAX, server.active_defrag_max_scan_fields, 1000, INTEGER_CONFIG, NULL, NULL), /* Default: keys with more than 1000 fields will be processed separately */\n    createULongConfig(\"slowlog-max-len\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.slowlog_max_len, 128, INTEGER_CONFIG, NULL, NULL),\n    createULongConfig(\"acllog-max-len\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.acllog_max_len, 128, INTEGER_CONFIG, NULL, NULL),\n\n    /* Long Long configs */\n    createLongLongConfig(\"lua-time-limit\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.lua_time_limit, 5000, INTEGER_CONFIG, NULL, NULL),/* milliseconds */\n    createLongLongConfig(\"cluster-node-timeout\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"slowlog-log-slower-than\", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, server.slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"latency-monitor-threshold\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"proto-max-bulk-len\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */\n    createLongLongConfig(\"stream-node-max-entries\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"repl-backlog-size\", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */\n\n    /* Unsigned Long Long configs */\n    createULongLongConfig(\"maxmemory\", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),\n\n    /* Size_t configs */\n    createSizeTConfig(\"hash-max-ziplist-entries\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_ziplist_entries, 512, INTEGER_CONFIG, NULL, NULL),\n    createSizeTConfig(\"set-max-intset-entries\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.set_max_intset_entries, 512, INTEGER_CONFIG, NULL, NULL),\n    createSizeTConfig(\"zset-max-ziplist-entries\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_entries, 128, INTEGER_CONFIG, NULL, NULL),\n    createSizeTConfig(\"active-defrag-ignore-bytes\", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.active_defrag_ignore_bytes, 100<<20, MEMORY_CONFIG, NULL, NULL), /* Default: don't defrag if frag overhead is below 100mb */\n    createSizeTConfig(\"hash-max-ziplist-value\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"stream-node-max-bytes\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.stream_node_max_bytes, 4096, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"zset-max-ziplist-value\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"hll-sparse-max-bytes\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hll_sparse_max_bytes, 3000, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"tracking-table-max-keys\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.tracking_table_max_keys, 1000000, INTEGER_CONFIG, NULL, NULL), /* Default: 1 million keys max. */\n\n    /* Other configs */\n    createTimeTConfig(\"repl-backlog-ttl\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.repl_backlog_time_limit, 60*60, INTEGER_CONFIG, NULL, NULL), /* Default: 1 hour */\n    createOffTConfig(\"auto-aof-rewrite-min-size\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.aof_rewrite_min_size, 64*1024*1024, MEMORY_CONFIG, NULL, NULL),\n\n#ifdef USE_OPENSSL\n    createIntConfig(\"tls-port\", NULL, IMMUTABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, NULL), /* TCP port. */\n    createBoolConfig(\"tls-cluster\", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, NULL),\n    createBoolConfig(\"tls-replication\", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, NULL),\n    createBoolConfig(\"tls-auth-clients\", NULL, MODIFIABLE_CONFIG, server.tls_auth_clients, 1, NULL, NULL),\n    createBoolConfig(\"tls-prefer-server-ciphers\", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),\n    createStringConfig(\"tls-cert-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-key-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-dh-params-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ca-cert-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ca-cert-dir\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-protocols\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ciphers\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ciphersuites\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, updateTlsCfg),\n#endif\n\n    /* NULL Terminator */\n    {NULL}\n};\n\n/*-----------------------------------------------------------------------------\n * CONFIG command entry point\n *----------------------------------------------------------------------------*/\n\nvoid configCommand(client *c) {\n    /* Only allow CONFIG GET while loading. */\n    if (server.loading && strcasecmp(c->argv[1]->ptr,\"get\")) {\n        addReplyError(c,\"Only CONFIG GET is allowed during loading\");\n        return;\n    }\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"GET <pattern> -- Return parameters matching the glob-like <pattern> and their values.\",\n\"SET <parameter> <value> -- Set parameter to value.\",\n\"RESETSTAT -- Reset statistics reported by INFO.\",\n\"REWRITE -- Rewrite the configuration file.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set\") && c->argc == 4) {\n        configSetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"get\") && c->argc == 3) {\n        configGetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"resetstat\") && c->argc == 2) {\n        resetServerStats();\n        resetCommandTableStats();\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"rewrite\") && c->argc == 2) {\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            serverLog(LL_WARNING,\"CONFIG REWRITE failed: %s\", strerror(errno));\n            addReplyErrorFormat(c,\"Rewriting config file: %s\", strerror(errno));\n        } else {\n            serverLog(LL_WARNING,\"CONFIG REWRITE executed with success.\");\n            addReply(c,shared.ok);\n        }\n    } else {\n        addReplySubcommandSyntaxError(c);\n        return;\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#ifdef __linux__\n#include <linux/version.h>\n#include <features.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#define HAVE_PROC_SOMAXCONN 1\n#endif\n\n/* Test for task_info() */\n#if defined(__APPLE__)\n#define HAVE_TASKINFO 1\n#endif\n\n/* Test for backtrace() */\n#if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__)) || \\\n    defined(__FreeBSD__) || (defined(__OpenBSD__) && defined(USE_BACKTRACE))\\\n || defined(__DragonFly__)\n#define HAVE_BACKTRACE 1\n#endif\n\n/* MSG_NOSIGNAL. */\n#ifdef __linux__\n#define HAVE_MSG_NOSIGNAL 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 redis_fsync to fdatasync() in Linux and fsync() for all the rest */\n#ifdef __linux__\n#define redis_fsync fdatasync\n#else\n#define redis_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#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(__GLIBC__)) || 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 || __powerpc__) && __GNUC__\n#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)\n#if defined(__clang__)\n#define HAVE_ATOMIC\n#endif\n#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))\n#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6))\n#define HAVE_ATOMIC\n#endif\n#endif\n#endif\n\n/* Make sure we can test for ARM just checking for __arm__, since sometimes\n * __arm is defined but __arm__ is not. */\n#if defined(__arm) && !defined(__arm__)\n#define __arm__\n#endif\n#if defined (__aarch64__) && !defined(__arm64__)\n#define __arm64__\n#endif\n\n/* Make sure we can test for SPARC just checking for __sparc__. */\n#if defined(__sparc) && !defined(__sparc__)\n#define __sparc__\n#endif\n\n#if defined(__sparc__) || defined(__arm__)\n#define USE_ALIGNED_ACCESS\n#endif\n\n/* Define for redis_set_thread_title */\n#ifdef __linux__\n#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name)\n#else\n#if (defined __FreeBSD__ || defined __OpenBSD__)\n#include <pthread_np.h>\n#define redis_set_thread_title(name) pthread_set_name_np(pthread_self(), name)\n#elif defined __NetBSD__\n#include <pthread.h>\n#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name, NULL)\n#else\n#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7))\nint pthread_setname_np(const char *name);\n#include <pthread.h>\n#define redis_set_thread_title(name) pthread_setname_np(name)\n#else\n#define redis_set_thread_title(name)\n#endif\n#endif\n#endif\n\n/* Check if we can use setcpuaffinity(). */\n#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__)\n#define USE_SETCPUAFFINITY\nvoid setcpuaffinity(const char *cpulist);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/connection.c",
    "content": "/*\n * Copyright (c) 2019, Redis Labs\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 \"server.h\"\n#include \"connhelpers.h\"\n\n/* The connections module provides a lean abstraction of network connections\n * to avoid direct socket and async event management across the Redis code base.\n *\n * It does NOT provide advanced connection features commonly found in similar\n * libraries such as complete in/out buffer management, throttling, etc. These\n * functions remain in networking.c.\n *\n * The primary goal is to allow transparent handling of TCP and TLS based\n * connections. To do so, connections have the following properties:\n *\n * 1. A connection may live before its corresponding socket exists.  This\n *    allows various context and configuration setting to be handled before\n *    establishing the actual connection.\n * 2. The caller may register/unregister logical read/write handlers to be\n *    called when the connection has data to read from/can accept writes.\n *    These logical handlers may or may not correspond to actual AE events,\n *    depending on the implementation (for TCP they are; for TLS they aren't).\n */\n\nConnectionType CT_Socket;\n\n/* When a connection is created we must know its type already, but the\n * underlying socket may or may not exist:\n *\n * - For accepted connections, it exists as we do not model the listen/accept\n *   part; So caller calls connCreateSocket() followed by connAccept().\n * - For outgoing connections, the socket is created by the connection module\n *   itself; So caller calls connCreateSocket() followed by connConnect(),\n *   which registers a connect callback that fires on connected/error state\n *   (and after any transport level handshake was done).\n *\n * NOTE: An earlier version relied on connections being part of other structs\n * and not independently allocated. This could lead to further optimizations\n * like using container_of(), etc.  However it was discontinued in favor of\n * this approach for these reasons:\n *\n * 1. In some cases conns are created/handled outside the context of the\n * containing struct, in which case it gets a bit awkward to copy them.\n * 2. Future implementations may wish to allocate arbitrary data for the\n * connection.\n * 3. The container_of() approach is anyway risky because connections may\n * be embedded in different structs, not just client.\n */\n\nconnection *connCreateSocket() {\n    connection *conn = zcalloc(sizeof(connection));\n    conn->type = &CT_Socket;\n    conn->fd = -1;\n\n    return conn;\n}\n\n/* Create a new socket-type connection that is already associated with\n * an accepted connection.\n *\n * The socket is not read for I/O until connAccept() was called and\n * invoked the connection-level accept handler.\n */\nconnection *connCreateAcceptedSocket(int fd) {\n    connection *conn = connCreateSocket();\n    conn->fd = fd;\n    conn->state = CONN_STATE_ACCEPTING;\n    return conn;\n}\n\nstatic int connSocketConnect(connection *conn, const char *addr, int port, const char *src_addr,\n        ConnectionCallbackFunc connect_handler) {\n    int fd = anetTcpNonBlockBestEffortBindConnect(NULL,addr,port,src_addr);\n    if (fd == -1) {\n        conn->state = CONN_STATE_ERROR;\n        conn->last_errno = errno;\n        return C_ERR;\n    }\n\n    conn->fd = fd;\n    conn->state = CONN_STATE_CONNECTING;\n\n    conn->conn_handler = connect_handler;\n    aeCreateFileEvent(server.el, conn->fd, AE_WRITABLE,\n            conn->type->ae_handler, conn);\n\n    return C_OK;\n}\n\n/* Returns true if a write handler is registered */\nint connHasWriteHandler(connection *conn) {\n    return conn->write_handler != NULL;\n}\n\n/* Returns true if a read handler is registered */\nint connHasReadHandler(connection *conn) {\n    return conn->read_handler != NULL;\n}\n\n/* Associate a private data pointer with the connection */\nvoid connSetPrivateData(connection *conn, void *data) {\n    conn->private_data = data;\n}\n\n/* Get the associated private data pointer */\nvoid *connGetPrivateData(connection *conn) {\n    return conn->private_data;\n}\n\n/* ------ Pure socket connections ------- */\n\n/* A very incomplete list of implementation-specific calls.  Much of the above shall\n * move here as we implement additional connection types.\n */\n\n/* Close the connection and free resources. */\nstatic void connSocketClose(connection *conn) {\n    if (conn->fd != -1) {\n        aeDeleteFileEvent(server.el,conn->fd,AE_READABLE);\n        aeDeleteFileEvent(server.el,conn->fd,AE_WRITABLE);\n        close(conn->fd);\n        conn->fd = -1;\n    }\n\n    /* If called from within a handler, schedule the close but\n     * keep the connection until the handler returns.\n     */\n    if (connHasRefs(conn)) {\n        conn->flags |= CONN_FLAG_CLOSE_SCHEDULED;\n        return;\n    }\n\n    zfree(conn);\n}\n\nstatic int connSocketWrite(connection *conn, const void *data, size_t data_len) {\n    int ret = write(conn->fd, data, data_len);\n    if (ret < 0 && errno != EAGAIN) {\n        conn->last_errno = errno;\n        conn->state = CONN_STATE_ERROR;\n    }\n\n    return ret;\n}\n\nstatic int connSocketRead(connection *conn, void *buf, size_t buf_len) {\n    int ret = read(conn->fd, buf, buf_len);\n    if (!ret) {\n        conn->state = CONN_STATE_CLOSED;\n    } else if (ret < 0 && errno != EAGAIN) {\n        conn->last_errno = errno;\n        conn->state = CONN_STATE_ERROR;\n    }\n\n    return ret;\n}\n\nstatic int connSocketAccept(connection *conn, ConnectionCallbackFunc accept_handler) {\n    int ret = C_OK;\n\n    if (conn->state != CONN_STATE_ACCEPTING) return C_ERR;\n    conn->state = CONN_STATE_CONNECTED;\n\n    connIncrRefs(conn);\n    if (!callHandler(conn, accept_handler)) ret = C_ERR;\n    connDecrRefs(conn);\n\n    return ret;\n}\n\n/* Register a write handler, to be called when the connection is writable.\n * If NULL, the existing handler is removed.\n *\n * The barrier flag indicates a write barrier is requested, resulting with\n * CONN_FLAG_WRITE_BARRIER set. This will ensure that the write handler is\n * always called before and not after the read handler in a single event\n * loop.\n */\nstatic int connSocketSetWriteHandler(connection *conn, ConnectionCallbackFunc func, int barrier) {\n    if (func == conn->write_handler) return C_OK;\n\n    conn->write_handler = func;\n    if (barrier)\n        conn->flags |= CONN_FLAG_WRITE_BARRIER;\n    else\n        conn->flags &= ~CONN_FLAG_WRITE_BARRIER;\n    if (!conn->write_handler)\n        aeDeleteFileEvent(server.el,conn->fd,AE_WRITABLE);\n    else\n        if (aeCreateFileEvent(server.el,conn->fd,AE_WRITABLE,\n                    conn->type->ae_handler,conn) == AE_ERR) return C_ERR;\n    return C_OK;\n}\n\n/* Register a read handler, to be called when the connection is readable.\n * If NULL, the existing handler is removed.\n */\nstatic int connSocketSetReadHandler(connection *conn, ConnectionCallbackFunc func) {\n    if (func == conn->read_handler) return C_OK;\n\n    conn->read_handler = func;\n    if (!conn->read_handler)\n        aeDeleteFileEvent(server.el,conn->fd,AE_READABLE);\n    else\n        if (aeCreateFileEvent(server.el,conn->fd,\n                    AE_READABLE,conn->type->ae_handler,conn) == AE_ERR) return C_ERR;\n    return C_OK;\n}\n\nstatic const char *connSocketGetLastError(connection *conn) {\n    return strerror(conn->last_errno);\n}\n\nstatic void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask)\n{\n    UNUSED(el);\n    UNUSED(fd);\n    connection *conn = clientData;\n\n    if (conn->state == CONN_STATE_CONNECTING &&\n            (mask & AE_WRITABLE) && conn->conn_handler) {\n\n        if (connGetSocketError(conn)) {\n            conn->last_errno = errno;\n            conn->state = CONN_STATE_ERROR;\n        } else {\n            conn->state = CONN_STATE_CONNECTED;\n        }\n\n        if (!conn->write_handler) aeDeleteFileEvent(server.el,conn->fd,AE_WRITABLE);\n\n        if (!callHandler(conn, conn->conn_handler)) return;\n        conn->conn_handler = NULL;\n    }\n\n    /* Normally we execute the readable event first, and the writable\n     * event later. This is useful as sometimes we may be able\n     * to serve the reply of a query immediately after processing the\n     * query.\n     *\n     * However if WRITE_BARRIER is set in the mask, our application is\n     * asking us to do the reverse: never fire the writable event\n     * after the readable. In such a case, we invert the calls.\n     * This is useful when, for instance, we want to do things\n     * in the beforeSleep() hook, like fsync'ing a file to disk,\n     * before replying to a client. */\n    int invert = conn->flags & CONN_FLAG_WRITE_BARRIER;\n\n    int call_write = (mask & AE_WRITABLE) && conn->write_handler;\n    int call_read = (mask & AE_READABLE) && conn->read_handler;\n\n    /* Handle normal I/O flows */\n    if (!invert && call_read) {\n        if (!callHandler(conn, conn->read_handler)) return;\n    }\n    /* Fire the writable event. */\n    if (call_write) {\n        if (!callHandler(conn, conn->write_handler)) return;\n    }\n    /* If we have to invert the call, fire the readable event now\n     * after the writable one. */\n    if (invert && call_read) {\n        if (!callHandler(conn, conn->read_handler)) return;\n    }\n}\n\nstatic int connSocketBlockingConnect(connection *conn, const char *addr, int port, long long timeout) {\n    int fd = anetTcpNonBlockConnect(NULL,addr,port);\n    if (fd == -1) {\n        conn->state = CONN_STATE_ERROR;\n        conn->last_errno = errno;\n        return C_ERR;\n    }\n\n    if ((aeWait(fd, AE_WRITABLE, timeout) & AE_WRITABLE) == 0) {\n        conn->state = CONN_STATE_ERROR;\n        conn->last_errno = ETIMEDOUT;\n    }\n\n    conn->fd = fd;\n    conn->state = CONN_STATE_CONNECTED;\n    return C_OK;\n}\n\n/* Connection-based versions of syncio.c functions.\n * NOTE: This should ideally be refactored out in favor of pure async work.\n */\n\nstatic ssize_t connSocketSyncWrite(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return syncWrite(conn->fd, ptr, size, timeout);\n}\n\nstatic ssize_t connSocketSyncRead(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return syncRead(conn->fd, ptr, size, timeout);\n}\n\nstatic ssize_t connSocketSyncReadLine(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return syncReadLine(conn->fd, ptr, size, timeout);\n}\n\n\nConnectionType CT_Socket = {\n    .ae_handler = connSocketEventHandler,\n    .close = connSocketClose,\n    .write = connSocketWrite,\n    .read = connSocketRead,\n    .accept = connSocketAccept,\n    .connect = connSocketConnect,\n    .set_write_handler = connSocketSetWriteHandler,\n    .set_read_handler = connSocketSetReadHandler,\n    .get_last_error = connSocketGetLastError,\n    .blocking_connect = connSocketBlockingConnect,\n    .sync_write = connSocketSyncWrite,\n    .sync_read = connSocketSyncRead,\n    .sync_readline = connSocketSyncReadLine\n};\n\n\nint connGetSocketError(connection *conn) {\n    int sockerr = 0;\n    socklen_t errlen = sizeof(sockerr);\n\n    if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)\n        sockerr = errno;\n    return sockerr;\n}\n\nint connPeerToString(connection *conn, char *ip, size_t ip_len, int *port) {\n    return anetPeerToString(conn ? conn->fd : -1, ip, ip_len, port);\n}\n\nint connFormatPeer(connection *conn, char *buf, size_t buf_len) {\n    return anetFormatPeer(conn ? conn->fd : -1, buf, buf_len);\n}\n\nint connSockName(connection *conn, char *ip, size_t ip_len, int *port) {\n    return anetSockName(conn->fd, ip, ip_len, port);\n}\n\nint connBlock(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetBlock(NULL, conn->fd);\n}\n\nint connNonBlock(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetNonBlock(NULL, conn->fd);\n}\n\nint connEnableTcpNoDelay(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetEnableTcpNoDelay(NULL, conn->fd);\n}\n\nint connDisableTcpNoDelay(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetDisableTcpNoDelay(NULL, conn->fd);\n}\n\nint connKeepAlive(connection *conn, int interval) {\n    if (conn->fd == -1) return C_ERR;\n    return anetKeepAlive(NULL, conn->fd, interval);\n}\n\nint connSendTimeout(connection *conn, long long ms) {\n    return anetSendTimeout(NULL, conn->fd, ms);\n}\n\nint connRecvTimeout(connection *conn, long long ms) {\n    return anetRecvTimeout(NULL, conn->fd, ms);\n}\n\nint connGetState(connection *conn) {\n    return conn->state;\n}\n\n/* Return a text that describes the connection, suitable for inclusion\n * in CLIENT LIST and similar outputs.\n *\n * For sockets, we always return \"fd=<fdnum>\" to maintain compatibility.\n */\nconst char *connGetInfo(connection *conn, char *buf, size_t buf_len) {\n    snprintf(buf, buf_len-1, \"fd=%i\", conn->fd);\n    return buf;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/connection.h",
    "content": "\n/*\n * Copyright (c) 2019, Redis Labs\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_CONNECTION_H\n#define __REDIS_CONNECTION_H\n\n#define CONN_INFO_LEN   32\n\nstruct aeEventLoop;\ntypedef struct connection connection;\n\ntypedef enum {\n    CONN_STATE_NONE = 0,\n    CONN_STATE_CONNECTING,\n    CONN_STATE_ACCEPTING,\n    CONN_STATE_CONNECTED,\n    CONN_STATE_CLOSED,\n    CONN_STATE_ERROR\n} ConnectionState;\n\n#define CONN_FLAG_CLOSE_SCHEDULED   (1<<0)      /* Closed scheduled by a handler */\n#define CONN_FLAG_WRITE_BARRIER     (1<<1)      /* Write barrier requested */\n\ntypedef void (*ConnectionCallbackFunc)(struct connection *conn);\n\ntypedef struct ConnectionType {\n    void (*ae_handler)(struct aeEventLoop *el, int fd, void *clientData, int mask);\n    int (*connect)(struct connection *conn, const char *addr, int port, const char *source_addr, ConnectionCallbackFunc connect_handler);\n    int (*write)(struct connection *conn, const void *data, size_t data_len);\n    int (*read)(struct connection *conn, void *buf, size_t buf_len);\n    void (*close)(struct connection *conn);\n    int (*accept)(struct connection *conn, ConnectionCallbackFunc accept_handler);\n    int (*set_write_handler)(struct connection *conn, ConnectionCallbackFunc handler, int barrier);\n    int (*set_read_handler)(struct connection *conn, ConnectionCallbackFunc handler);\n    const char *(*get_last_error)(struct connection *conn);\n    int (*blocking_connect)(struct connection *conn, const char *addr, int port, long long timeout);\n    ssize_t (*sync_write)(struct connection *conn, char *ptr, ssize_t size, long long timeout);\n    ssize_t (*sync_read)(struct connection *conn, char *ptr, ssize_t size, long long timeout);\n    ssize_t (*sync_readline)(struct connection *conn, char *ptr, ssize_t size, long long timeout);\n} ConnectionType;\n\nstruct connection {\n    ConnectionType *type;\n    ConnectionState state;\n    short int flags;\n    short int refs;\n    int last_errno;\n    void *private_data;\n    ConnectionCallbackFunc conn_handler;\n    ConnectionCallbackFunc write_handler;\n    ConnectionCallbackFunc read_handler;\n    int fd;\n};\n\n/* The connection module does not deal with listening and accepting sockets,\n * so we assume we have a socket when an incoming connection is created.\n *\n * The fd supplied should therefore be associated with an already accept()ed\n * socket.\n *\n * connAccept() may directly call accept_handler(), or return and call it\n * at a later time. This behavior is a bit awkward but aims to reduce the need\n * to wait for the next event loop, if no additional handshake is required.\n *\n * IMPORTANT: accept_handler may decide to close the connection, calling connClose().\n * To make this safe, the connection is only marked with CONN_FLAG_CLOSE_SCHEDULED\n * in this case, and connAccept() returns with an error.\n *\n * connAccept() callers must always check the return value and on error (C_ERR)\n * a connClose() must be called.\n */\n\nstatic inline int connAccept(connection *conn, ConnectionCallbackFunc accept_handler) {\n    return conn->type->accept(conn, accept_handler);\n}\n\n/* Establish a connection.  The connect_handler will be called when the connection\n * is established, or if an error has occured.\n *\n * The connection handler will be responsible to set up any read/write handlers\n * as needed.\n *\n * If C_ERR is returned, the operation failed and the connection handler shall\n * not be expected.\n */\nstatic inline int connConnect(connection *conn, const char *addr, int port, const char *src_addr,\n        ConnectionCallbackFunc connect_handler) {\n    return conn->type->connect(conn, addr, port, src_addr, connect_handler);\n}\n\n/* Blocking connect.\n *\n * NOTE: This is implemented in order to simplify the transition to the abstract\n * connections, but should probably be refactored out of cluster.c and replication.c,\n * in favor of a pure async implementation.\n */\nstatic inline int connBlockingConnect(connection *conn, const char *addr, int port, long long timeout) {\n    return conn->type->blocking_connect(conn, addr, port, timeout);\n}\n\n/* Write to connection, behaves the same as write(2).\n *\n * Like write(2), a short write is possible. A -1 return indicates an error.\n *\n * The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use\n * connGetState() to see if the connection state is still CONN_STATE_CONNECTED.\n */\nstatic inline int connWrite(connection *conn, const void *data, size_t data_len) {\n    return conn->type->write(conn, data, data_len);\n}\n\n/* Read from the connection, behaves the same as read(2).\n * \n * Like read(2), a short read is possible.  A return value of 0 will indicate the\n * connection was closed, and -1 will indicate an error.\n *\n * The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use\n * connGetState() to see if the connection state is still CONN_STATE_CONNECTED.\n */\nstatic inline int connRead(connection *conn, void *buf, size_t buf_len) {\n    return conn->type->read(conn, buf, buf_len);\n}\n\n/* Register a write handler, to be called when the connection is writable.\n * If NULL, the existing handler is removed.\n */\nstatic inline int connSetWriteHandler(connection *conn, ConnectionCallbackFunc func) {\n    return conn->type->set_write_handler(conn, func, 0);\n}\n\n/* Register a read handler, to be called when the connection is readable.\n * If NULL, the existing handler is removed.\n */\nstatic inline int connSetReadHandler(connection *conn, ConnectionCallbackFunc func) {\n    return conn->type->set_read_handler(conn, func);\n}\n\n/* Set a write handler, and possibly enable a write barrier, this flag is\n * cleared when write handler is changed or removed.\n * With barroer enabled, we never fire the event if the read handler already\n * fired in the same event loop iteration. Useful when you want to persist\n * things to disk before sending replies, and want to do that in a group fashion. */\nstatic inline int connSetWriteHandlerWithBarrier(connection *conn, ConnectionCallbackFunc func, int barrier) {\n    return conn->type->set_write_handler(conn, func, barrier);\n}\n\nstatic inline void connClose(connection *conn) {\n    conn->type->close(conn);\n}\n\n/* Returns the last error encountered by the connection, as a string.  If no error,\n * a NULL is returned.\n */\nstatic inline const char *connGetLastError(connection *conn) {\n    return conn->type->get_last_error(conn);\n}\n\nstatic inline ssize_t connSyncWrite(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return conn->type->sync_write(conn, ptr, size, timeout);\n}\n\nstatic inline ssize_t connSyncRead(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return conn->type->sync_read(conn, ptr, size, timeout);\n}\n\nstatic inline ssize_t connSyncReadLine(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return conn->type->sync_readline(conn, ptr, size, timeout);\n}\n\nconnection *connCreateSocket();\nconnection *connCreateAcceptedSocket(int fd);\n\nconnection *connCreateTLS();\nconnection *connCreateAcceptedTLS(int fd, int require_auth);\n\nvoid connSetPrivateData(connection *conn, void *data);\nvoid *connGetPrivateData(connection *conn);\nint connGetState(connection *conn);\nint connHasWriteHandler(connection *conn);\nint connHasReadHandler(connection *conn);\nint connGetSocketError(connection *conn);\n\n/* anet-style wrappers to conns */\nint connBlock(connection *conn);\nint connNonBlock(connection *conn);\nint connEnableTcpNoDelay(connection *conn);\nint connDisableTcpNoDelay(connection *conn);\nint connKeepAlive(connection *conn, int interval);\nint connSendTimeout(connection *conn, long long ms);\nint connRecvTimeout(connection *conn, long long ms);\nint connPeerToString(connection *conn, char *ip, size_t ip_len, int *port);\nint connFormatPeer(connection *conn, char *buf, size_t buf_len);\nint connSockName(connection *conn, char *ip, size_t ip_len, int *port);\nconst char *connGetInfo(connection *conn, char *buf, size_t buf_len);\n\n/* Helpers for tls special considerations */\nint tlsHasPendingData();\nint tlsProcessPendingData();\n\n#endif  /* __REDIS_CONNECTION_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/connhelpers.h",
    "content": "\n/*\n * Copyright (c) 2019, Redis Labs\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_CONNHELPERS_H\n#define __REDIS_CONNHELPERS_H\n\n#include \"connection.h\"\n\n/* These are helper functions that are common to different connection\n * implementations (currently sockets in connection.c and TLS in tls.c).\n *\n * Currently helpers implement the mechanisms for invoking connection\n * handlers and tracking connection references, to allow safe destruction\n * of connections from within a handler.\n */\n\n/* Incremenet connection references.\n *\n * Inside a connection handler, we guarantee refs >= 1 so it is always\n * safe to connClose().\n *\n * In other cases where we don't want to prematurely lose the connection,\n * it can go beyond 1 as well; currently it is only done by connAccept().\n */\nstatic inline void connIncrRefs(connection *conn) {\n    conn->refs++;\n}\n\n/* Decrement connection references.\n *\n * Note that this is not intended to provide any automatic free logic!\n * callHandler() takes care of that for the common flows, and anywhere an\n * explicit connIncrRefs() is used, the caller is expected to take care of\n * that.\n */\n\nstatic inline void connDecrRefs(connection *conn) {\n    conn->refs--;\n}\n\nstatic inline int connHasRefs(connection *conn) {\n    return conn->refs;\n}\n\n/* Helper for connection implementations to call handlers:\n * 1. Increment refs to protect the connection.\n * 2. Execute the handler (if set).\n * 3. Decrement refs and perform deferred close, if refs==0.\n */\nstatic inline int callHandler(connection *conn, ConnectionCallbackFunc handler) {\n    connIncrRefs(conn);\n    if (handler) handler(conn);\n    connDecrRefs(conn);\n    if (conn->flags & CONN_FLAG_CLOSE_SCHEDULED) {\n        if (!connHasRefs(conn)) connClose(conn);\n        return 0;\n    }\n    return 1;\n}\n\n#endif  /* __REDIS_CONNHELPERS_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/crc16.c",
    "content": "#include \"server.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": "Chapter02/redis-6.0.3/src/crc16_slottable.h",
    "content": "#ifndef _CRC16_TABLE_H__\n#define _CRC16_TABLE_H__\n\n/* A table of the shortest possible alphanumeric string that is mapped by redis' crc16\n * to any given redis cluster slot. \n * \n * The array indexes are slot numbers, so that given a desired slot, this string is guaranteed\n * to make redis cluster route a request to the shard holding this slot \n */\n\nconst char *crc16_slot_table[] = {\n\"06S\", \"Qi\", \"5L5\", \"4Iu\", \"4gY\", \"460\", \"1Y7\", \"1LV\", \"0QG\", \"ru\", \"7Ok\", \"4ji\", \"4DE\", \"65n\", \"2JH\", \"I8\", \"F9\", \"SX\", \"7nF\", \"4KD\", \n\"4eh\", \"6PK\", \"2ke\", \"1Ng\", \"0Sv\", \"4L\", \"491\", \"4hX\", \"4Ft\", \"5C4\", \"2Hy\", \"09R\", \"021\", \"0cX\", \"4Xv\", \"6mU\", \"6Cy\", \"42R\", \"0Mt\", \"nF\", \n\"cv\", \"1Pe\", \"5kK\", \"6NI\", \"74L\", \"4UF\", \"0nh\", \"MZ\", \"2TJ\", \"0ai\", \"4ZG\", \"6od\", \"6AH\", \"40c\", \"0OE\", \"lw\", \"aG\", \"0Bu\", \"5iz\", \"6Lx\", \n\"5R7\", \"4Ww\", \"0lY\", \"Ok\", \"5n3\", \"4ks\", \"8YE\", \"7g\", \"2KR\", \"1nP\", \"714\", \"64t\", \"69D\", \"4Ho\", \"07I\", \"Ps\", \"2hN\", \"1ML\", \"4fC\", \"7CA\", \n\"avs\", \"4iB\", \"0Rl\", \"5V\", \"2Ic\", \"08H\", \"4Gn\", \"66E\", \"aUo\", \"b4e\", \"05x\", \"RB\", \"8f\", \"8VD\", \"4dr\", \"5a2\", \"4zp\", \"6OS\", \"bl\", \"355\", \n\"0or\", \"1j2\", \"75V\", \"bno\", \"4Yl\", \"6lO\", \"Ap\", \"0bB\", \"0Ln\", \"2yM\", \"6Bc\", \"43H\", \"4xA\", \"6Mb\", \"22D\", \"14\", \"0mC\", \"Nq\", \"6cN\", \"4Vm\", \n\"ban\", \"aDl\", \"CA\", \"14Z\", \"8GG\", \"mm\", \"549\", \"41y\", \"53t\", \"464\", \"1Y3\", \"1LR\", \"06W\", \"Qm\", \"5L1\", \"4Iq\", \"4DA\", \"65j\", \"2JL\", \"1oN\", \n\"0QC\", \"6y\", \"7Oo\", \"4jm\", \"4el\", \"6PO\", \"9x\", \"1Nc\", \"04f\", \"2EM\", \"7nB\", \"bqs\", \"4Fp\", \"5C0\", \"d6F\", \"09V\", \"0Sr\", \"4H\", \"495\", \"bRo\", \n\"aio\", \"42V\", \"0Mp\", \"nB\", \"025\", \"17u\", \"4Xr\", \"6mQ\", \"74H\", \"4UB\", \"0nl\", \"3Kn\", \"cr\", \"1Pa\", \"5kO\", \"6NM\", \"6AL\", \"40g\", \"0OA\", \"ls\", \n\"2TN\", \"0am\", \"4ZC\", \"aEr\", \"5R3\", \"4Ws\", \"18t\", \"Oo\", \"aC\", \"0Bq\", \"bCl\", \"afn\", \"2KV\", \"1nT\", \"5Uz\", \"64p\", \"5n7\", \"4kw\", \"0PY\", \"7c\", \n\"2hJ\", \"1MH\", \"4fG\", \"6Sd\", \"7mi\", \"4Hk\", \"07M\", \"Pw\", \"2Ig\", \"08L\", \"4Gj\", \"66A\", \"7LD\", \"4iF\", \"0Rh\", \"5R\", \"8b\", \"1Oy\", \"4dv\", \"5a6\", \n\"7oX\", \"4JZ\", \"0qt\", \"RF\", \"0ov\", \"LD\", \"4A9\", \"4TX\", \"4zt\", \"6OW\", \"bh\", \"0AZ\", \"z9\", \"oX\", \"6Bg\", \"43L\", \"4Yh\", \"6lK\", \"At\", \"0bF\", \n\"0mG\", \"Nu\", \"6cJ\", \"4Vi\", \"4xE\", \"6Mf\", \"2vH\", \"10\", \"8GC\", \"mi\", \"5p5\", \"4uu\", \"5Kx\", \"4N8\", \"CE\", \"1pV\", \"0QO\", \"6u\", \"7Oc\", \"4ja\", \n\"4DM\", \"65f\", \"3Za\", \"I0\", \"0rS\", \"Qa\", \"68V\", \"b7F\", \"4gQ\", \"468\", \"dSo\", \"285\", \"274\", \"4D\", \"499\", \"4hP\", \"b8G\", \"67W\", \"0h3\", \"09Z\", \n\"F1\", \"SP\", \"7nN\", \"4KL\", \"51I\", \"6PC\", \"9t\", \"1No\", \"21g\", \"1Pm\", \"5kC\", \"6NA\", \"74D\", \"4UN\", \"X3\", \"MR\", \"029\", \"0cP\", \"bbM\", \"79t\", \n\"4c3\", \"42Z\", \"8Dd\", \"nN\", \"aO\", \"8Ke\", \"4yS\", \"4l2\", \"76u\", \"635\", \"0lQ\", \"Oc\", \"BS\", \"W2\", \"4ZO\", \"6ol\", \"7Qa\", \"40k\", \"0OM\", \"2zn\", \n\"69L\", \"4Hg\", \"07A\", \"2Fj\", \"2hF\", \"k6\", \"4fK\", \"6Sh\", \"7Ny\", \"6K9\", \"0PU\", \"7o\", \"2KZ\", \"1nX\", \"4EW\", \"4P6\", \"7oT\", \"4JV\", \"05p\", \"RJ\", \n\"8n\", \"1Ou\", \"4dz\", \"6QY\", \"7LH\", \"4iJ\", \"d7\", \"qV\", \"2Ik\", \"1li\", \"4Gf\", \"66M\", \"4Yd\", \"6lG\", \"Ax\", \"0bJ\", \"z5\", \"oT\", \"6Bk\", \"4wH\", \n\"4zx\", \"aeI\", \"bd\", \"0AV\", \"0oz\", \"LH\", \"4A5\", \"4TT\", \"5Kt\", \"4N4\", \"CI\", \"14R\", \"0NW\", \"me\", \"541\", \"41q\", \"4xI\", \"6Mj\", \"22L\", \"u4\", \n\"0mK\", \"Ny\", \"6cF\", \"4Ve\", \"4DI\", \"65b\", \"2JD\", \"I4\", \"0QK\", \"6q\", \"7Og\", \"4je\", \"4gU\", \"4r4\", \"2iX\", \"1LZ\", \"0rW\", \"Qe\", \"5L9\", \"4Iy\", \n\"4Fx\", \"5C8\", \"0h7\", \"1mw\", \"0Sz\", \"pH\", \"7MV\", \"4hT\", \"4ed\", \"6PG\", \"9p\", \"1Nk\", \"F5\", \"ST\", \"7nJ\", \"4KH\", \"7pH\", \"4UJ\", \"X7\", \"MV\", \n\"cz\", \"1Pi\", \"5kG\", \"6NE\", \"4c7\", \"4vV\", \"0Mx\", \"nJ\", \"0v5\", \"0cT\", \"4Xz\", \"6mY\", \"6bX\", \"5GZ\", \"0lU\", \"Og\", \"aK\", \"0By\", \"4yW\", \"4l6\", \n\"6AD\", \"40o\", \"0OI\", \"2zj\", \"BW\", \"W6\", \"4ZK\", \"6oh\", \"2hB\", \"k2\", \"4fO\", \"6Sl\", \"69H\", \"4Hc\", \"07E\", \"2Fn\", \"d5e\", \"83m\", \"4ES\", \"4P2\", \n\"a0F\", \"bQL\", \"0PQ\", \"7k\", \"8j\", \"1Oq\", \"50W\", \"hbv\", \"7oP\", \"4JR\", \"05t\", \"RN\", \"2Io\", \"08D\", \"4Gb\", \"66I\", \"7LL\", \"4iN\", \"d3\", \"5Z\", \n\"z1\", \"oP\", \"6Bo\", \"43D\", \"5IA\", \"6lC\", \"2Wm\", \"0bN\", \"8ff\", \"LL\", \"4A1\", \"4TP\", \"cPn\", \"aeM\", \"0T3\", \"0AR\", \"0NS\", \"ma\", \"545\", \"41u\", \n\"5Kp\", \"4N0\", \"CM\", \"14V\", \"0mO\", \"2Xl\", \"6cB\", \"4Va\", \"4xM\", \"6Mn\", \"22H\", \"18\", \"04s\", \"SI\", \"7nW\", \"4KU\", \"4ey\", \"6PZ\", \"9m\", \"1Nv\", \n\"e4\", \"pU\", \"7MK\", \"4hI\", \"4Fe\", \"67N\", \"2Hh\", \"09C\", \"06B\", \"Qx\", \"68O\", \"4Id\", \"4gH\", \"6Rk\", \"2iE\", \"j5\", \"0QV\", \"6l\", \"5o8\", \"4jx\", \n\"4DT\", \"4Q5\", \"2JY\", \"82j\", \"BJ\", \"0ax\", \"4ZV\", \"4O7\", \"552\", \"40r\", \"0OT\", \"lf\", \"aV\", \"t7\", \"4yJ\", \"6Li\", \"6bE\", \"4Wf\", \"0lH\", \"Oz\", \n\"2Vj\", \"0cI\", \"4Xg\", \"6mD\", \"6Ch\", \"42C\", \"0Me\", \"nW\", \"cg\", \"1Pt\", \"5kZ\", \"6NX\", \"7pU\", \"4UW\", \"0ny\", \"MK\", \"7LQ\", \"4iS\", \"267\", \"5G\", \n\"0i0\", \"08Y\", \"b9D\", \"66T\", \"7oM\", \"4JO\", \"G2\", \"RS\", \"8w\", \"1Ol\", \"4dc\", \"7Aa\", \"atS\", \"4kb\", \"0PL\", \"7v\", \"2KC\", \"H3\", \"4EN\", \"64e\", \n\"69U\", \"b6E\", \"07X\", \"Pb\", \"dRl\", \"296\", \"4fR\", \"4s3\", \"4xP\", \"4m1\", \"22U\", \"8Jf\", \"0mR\", \"0x3\", \"77v\", \"626\", \"5Km\", \"6no\", \"CP\", \"V1\", \n\"0NN\", \"3kL\", \"7Pb\", \"41h\", \"4za\", \"6OB\", \"20d\", \"0AO\", \"Y0\", \"LQ\", \"6an\", \"4TM\", \"bcN\", \"78w\", \"Aa\", \"0bS\", \"8Eg\", \"oM\", \"4b0\", \"43Y\", \n\"51T\", \"azL\", \"9i\", \"1Nr\", \"04w\", \"SM\", \"7nS\", \"4KQ\", \"4Fa\", \"67J\", \"2Hl\", \"09G\", \"e0\", \"4Y\", \"7MO\", \"4hM\", \"4gL\", \"6Ro\", \"2iA\", \"j1\", \n\"06F\", \"2Gm\", \"68K\", \"5YA\", \"4DP\", \"4Q1\", \"d4f\", \"82n\", \"0QR\", \"6h\", \"a1E\", \"bPO\", \"556\", \"40v\", \"0OP\", \"lb\", \"BN\", \"15U\", \"4ZR\", \"4O3\", \n\"6bA\", \"4Wb\", \"0lL\", \"2Yo\", \"aR\", \"t3\", \"4yN\", \"6Lm\", \"6Cl\", \"42G\", \"0Ma\", \"nS\", \"2Vn\", \"0cM\", \"4Xc\", \"79i\", \"74Y\", \"4US\", \"8ge\", \"MO\", \n\"cc\", \"1Pp\", \"bAL\", \"adN\", \"0i4\", \"1lt\", \"5WZ\", \"66P\", \"7LU\", \"4iW\", \"0Ry\", \"5C\", \"8s\", \"1Oh\", \"4dg\", \"6QD\", \"7oI\", \"4JK\", \"G6\", \"RW\", \n\"2KG\", \"H7\", \"4EJ\", \"64a\", \"7Nd\", \"4kf\", \"0PH\", \"7r\", \"1X8\", \"1MY\", \"4fV\", \"4s7\", \"69Q\", \"4Hz\", \"0sT\", \"Pf\", \"0mV\", \"Nd\", \"5S8\", \"4Vx\", \n\"4xT\", \"4m5\", \"22Q\", \"0Cz\", \"0NJ\", \"mx\", \"7Pf\", \"41l\", \"5Ki\", \"6nk\", \"CT\", \"V5\", \"Y4\", \"LU\", \"6aj\", \"4TI\", \"4ze\", \"6OF\", \"by\", \"0AK\", \n\"2l9\", \"oI\", \"4b4\", \"4wU\", \"4Yy\", \"6lZ\", \"Ae\", \"0bW\", \"0So\", \"4U\", \"7MC\", \"4hA\", \"4Fm\", \"67F\", \"3XA\", \"09K\", \"0ps\", \"SA\", \"aTl\", \"b5f\", \n\"4eq\", \"6PR\", \"9e\", \"8WG\", \"8XF\", \"6d\", \"5o0\", \"4jp\", \"707\", \"65w\", \"1z2\", \"1oS\", \"06J\", \"Qp\", \"68G\", \"4Il\", \"53i\", \"6Rc\", \"2iM\", \"1LO\", \n\"23G\", \"07\", \"4yB\", \"6La\", \"6bM\", \"4Wn\", \"18i\", \"Or\", \"BB\", \"0ap\", \"c4D\", \"aEo\", \"5q2\", \"40z\", \"8FD\", \"ln\", \"co\", \"346\", \"5kR\", \"6NP\", \n\"74U\", \"bol\", \"0nq\", \"MC\", \"2Vb\", \"0cA\", \"4Xo\", \"6mL\", \"7SA\", \"42K\", \"0Mm\", \"2xN\", \"7oE\", \"4JG\", \"05a\", \"2DJ\", \"2jf\", \"1Od\", \"4dk\", \"6QH\", \n\"482\", \"5yz\", \"0Ru\", \"5O\", \"0i8\", \"08Q\", \"4Gw\", \"5B7\", \"5M6\", \"4Hv\", \"07P\", \"Pj\", \"1X4\", \"1MU\", \"4fZ\", \"473\", \"7Nh\", \"4kj\", \"0PD\", \"sv\", \n\"2KK\", \"1nI\", \"4EF\", \"64m\", \"5Ke\", \"6ng\", \"CX\", \"V9\", \"0NF\", \"mt\", \"7Pj\", \"4uh\", \"4xX\", \"4m9\", \"1F6\", \"0Cv\", \"0mZ\", \"Nh\", \"5S4\", \"4Vt\", \n\"4Yu\", \"6lV\", \"Ai\", \"16r\", \"0Lw\", \"oE\", \"4b8\", \"43Q\", \"4zi\", \"6OJ\", \"bu\", \"0AG\", \"Y8\", \"LY\", \"6af\", \"4TE\", \"4Fi\", \"67B\", \"2Hd\", \"09O\", \n\"e8\", \"4Q\", \"7MG\", \"4hE\", \"4eu\", \"6PV\", \"9a\", \"1Nz\", \"0pw\", \"SE\", \"aTh\", \"4KY\", \"4DX\", \"4Q9\", \"1z6\", \"1oW\", \"0QZ\", \"rh\", \"5o4\", \"4jt\", \n\"4gD\", \"6Rg\", \"2iI\", \"j9\", \"06N\", \"Qt\", \"68C\", \"4Ih\", \"6bI\", \"4Wj\", \"0lD\", \"Ov\", \"aZ\", \"03\", \"4yF\", \"6Le\", \"5q6\", \"4tv\", \"0OX\", \"lj\", \n\"BF\", \"0at\", \"4ZZ\", \"6oy\", \"74Q\", \"5Ez\", \"0nu\", \"MG\", \"ck\", \"1Px\", \"5kV\", \"6NT\", \"6Cd\", \"42O\", \"0Mi\", \"2xJ\", \"2Vf\", \"0cE\", \"4Xk\", \"6mH\", \n\"2jb\", \"8VY\", \"4do\", \"6QL\", \"7oA\", \"4JC\", \"05e\", \"2DN\", \"d7E\", \"08U\", \"4Gs\", \"5B3\", \"486\", \"bSl\", \"0Rq\", \"5K\", \"1X0\", \"1MQ\", \"52w\", \"477\", \n\"5M2\", \"4Hr\", \"07T\", \"Pn\", \"2KO\", \"1nM\", \"4EB\", \"64i\", \"7Nl\", \"4kn\", \"8YX\", \"7z\", \"0NB\", \"mp\", \"7Pn\", \"41d\", \"5Ka\", \"6nc\", \"2UM\", \"14G\", \n\"19w\", \"Nl\", \"5S0\", \"4Vp\", \"bBo\", \"agm\", \"1F2\", \"0Cr\", \"0Ls\", \"oA\", \"ahl\", \"43U\", \"4Yq\", \"6lR\", \"Am\", \"16v\", \"0oo\", \"2ZL\", \"6ab\", \"4TA\", \n\"4zm\", \"6ON\", \"bq\", \"0AC\", \"2VY\", \"0cz\", \"4XT\", \"4M5\", \"570\", \"42p\", \"0MV\", \"nd\", \"cT\", \"v5\", \"5ki\", \"6Nk\", \"74n\", \"4Ud\", \"0nJ\", \"Mx\", \n\"By\", \"0aK\", \"4Ze\", \"6oF\", \"6Aj\", \"40A\", \"y4\", \"lU\", \"ae\", \"0BW\", \"4yy\", \"581\", \"4B4\", \"4WU\", \"18R\", \"OI\", \"06q\", \"QK\", \"7lU\", \"4IW\", \n\"53R\", \"6RX\", \"0I4\", \"1Lt\", \"g6\", \"rW\", \"7OI\", \"4jK\", \"4Dg\", \"65L\", \"2Jj\", \"1oh\", \"0pH\", \"Sz\", \"7nd\", \"4Kf\", \"4eJ\", \"6Pi\", \"2kG\", \"h7\", \n\"0ST\", \"4n\", \"7Mx\", \"4hz\", \"4FV\", \"4S7\", \"1x8\", \"09p\", \"4zR\", \"4o3\", \"bN\", \"8Hd\", \"0oP\", \"Lb\", \"75t\", \"604\", \"4YN\", \"6lm\", \"AR\", \"T3\", \n\"0LL\", \"2yo\", \"6BA\", \"43j\", \"4xc\", \"agR\", \"22f\", \"0CM\", \"0ma\", \"NS\", \"6cl\", \"4VO\", \"baL\", \"aDN\", \"Cc\", \"14x\", \"8Ge\", \"mO\", \"7PQ\", \"4uS\", \n\"7NS\", \"4kQ\", \"245\", \"7E\", \"0k2\", \"1nr\", \"coo\", \"64V\", \"69f\", \"4HM\", \"E0\", \"PQ\", \"2hl\", \"1Mn\", \"4fa\", \"6SB\", \"7Lb\", \"5yA\", \"0RN\", \"5t\", \n\"2IA\", \"J1\", \"4GL\", \"66g\", \"aUM\", \"b4G\", \"05Z\", \"0d3\", \"8D\", \"8Vf\", \"4dP\", \"459\", \"574\", \"42t\", \"0MR\", \"0X3\", \"dln\", \"17W\", \"4XP\", \"4M1\", \n\"74j\", \"5EA\", \"0nN\", \"3KL\", \"cP\", \"29\", \"5km\", \"6No\", \"6An\", \"40E\", \"y0\", \"lQ\", \"2Tl\", \"0aO\", \"4Za\", \"6oB\", \"4B0\", \"4WQ\", \"18V\", \"OM\", \n\"aa\", \"0BS\", \"bCN\", \"585\", \"53V\", \"axN\", \"0I0\", \"1Lp\", \"06u\", \"QO\", \"68x\", \"4IS\", \"4Dc\", \"65H\", \"2Jn\", \"1ol\", \"g2\", \"rS\", \"7OM\", \"4jO\", \n\"4eN\", \"6Pm\", \"9Z\", \"h3\", \"04D\", \"2Eo\", \"aTS\", \"4Kb\", \"4FR\", \"4S3\", \"d6d\", \"09t\", \"0SP\", \"4j\", \"a3G\", \"bRM\", \"0oT\", \"Lf\", \"6aY\", \"4Tz\", \n\"4zV\", \"4o7\", \"bJ\", \"0Ax\", \"0LH\", \"oz\", \"6BE\", \"43n\", \"4YJ\", \"6li\", \"AV\", \"T7\", \"0me\", \"NW\", \"6ch\", \"4VK\", \"4xg\", \"6MD\", \"22b\", \"0CI\", \n\"0Ny\", \"mK\", \"7PU\", \"4uW\", \"5KZ\", \"6nX\", \"Cg\", \"1pt\", \"0k6\", \"1nv\", \"4Ey\", \"64R\", \"7NW\", \"4kU\", \"241\", \"7A\", \"2hh\", \"1Mj\", \"4fe\", \"6SF\", \n\"69b\", \"4HI\", \"E4\", \"PU\", \"2IE\", \"J5\", \"4GH\", \"66c\", \"7Lf\", \"4id\", \"0RJ\", \"5p\", \"2jY\", \"8Vb\", \"4dT\", \"4q5\", \"5O8\", \"4Jx\", \"0qV\", \"Rd\", \n\"21E\", \"25\", \"5ka\", \"6Nc\", \"74f\", \"4Ul\", \"0nB\", \"Mp\", \"1f2\", \"0cr\", \"bbo\", \"79V\", \"578\", \"42x\", \"395\", \"nl\", \"am\", \"364\", \"4yq\", \"589\", \n\"76W\", \"bmn\", \"0ls\", \"OA\", \"Bq\", \"0aC\", \"4Zm\", \"6oN\", \"6Ab\", \"40I\", \"0Oo\", \"2zL\", \"0Qm\", \"6W\", \"7OA\", \"4jC\", \"4Do\", \"65D\", \"2Jb\", \"82Q\", \n\"06y\", \"QC\", \"68t\", \"b7d\", \"4gs\", \"5b3\", \"dSM\", \"8UE\", \"8ZD\", \"4f\", \"5m2\", \"4hr\", \"725\", \"67u\", \"1x0\", \"09x\", \"04H\", \"Sr\", \"7nl\", \"4Kn\", \n\"4eB\", \"6Pa\", \"9V\", \"1NM\", \"4YF\", \"6le\", \"AZ\", \"0bh\", \"0LD\", \"ov\", \"6BI\", \"43b\", \"4zZ\", \"6Oy\", \"bF\", \"0At\", \"0oX\", \"Lj\", \"5Q6\", \"4Tv\", \n\"5KV\", \"6nT\", \"Ck\", \"14p\", \"0Nu\", \"mG\", \"7PY\", \"41S\", \"4xk\", \"6MH\", \"22n\", \"0CE\", \"0mi\", \"2XJ\", \"6cd\", \"4VG\", \"69n\", \"4HE\", \"E8\", \"PY\", \n\"2hd\", \"1Mf\", \"4fi\", \"6SJ\", \"ath\", \"4kY\", \"0Pw\", \"7M\", \"2Kx\", \"1nz\", \"4Eu\", \"6pV\", \"5O4\", \"4Jt\", \"05R\", \"Rh\", \"8L\", \"1OW\", \"4dX\", \"451\", \n\"7Lj\", \"4ih\", \"0RF\", \"qt\", \"2II\", \"J9\", \"4GD\", \"66o\", \"74b\", \"4Uh\", \"0nF\", \"Mt\", \"cX\", \"21\", \"5ke\", \"6Ng\", \"5s4\", \"4vt\", \"0MZ\", \"nh\", \n\"1f6\", \"0cv\", \"4XX\", \"4M9\", \"4B8\", \"4WY\", \"0lw\", \"OE\", \"ai\", \"1Rz\", \"4yu\", \"6LV\", \"6Af\", \"40M\", \"y8\", \"lY\", \"Bu\", \"0aG\", \"4Zi\", \"6oJ\", \n\"4Dk\", \"6qH\", \"2Jf\", \"1od\", \"0Qi\", \"6S\", \"7OE\", \"4jG\", \"4gw\", \"5b7\", \"0I8\", \"1Lx\", \"0ru\", \"QG\", \"68p\", \"5Yz\", \"4FZ\", \"67q\", \"1x4\", \"1mU\", \n\"0SX\", \"4b\", \"5m6\", \"4hv\", \"4eF\", \"6Pe\", \"9R\", \"1NI\", \"04L\", \"Sv\", \"7nh\", \"4Kj\", \"8EX\", \"or\", \"6BM\", \"43f\", \"4YB\", \"6la\", \"2WO\", \"0bl\", \n\"8fD\", \"Ln\", \"5Q2\", \"4Tr\", \"cPL\", \"aeo\", \"bB\", \"0Ap\", \"0Nq\", \"mC\", \"ajn\", \"41W\", \"5KR\", \"6nP\", \"Co\", \"14t\", \"0mm\", \"2XN\", \"77I\", \"4VC\", \n\"4xo\", \"6ML\", \"22j\", \"0CA\", \"3xA\", \"1Mb\", \"4fm\", \"6SN\", \"69j\", \"4HA\", \"07g\", \"2FL\", \"d5G\", \"83O\", \"4Eq\", \"64Z\", \"a0d\", \"bQn\", \"0Ps\", \"7I\", \n\"8H\", \"1OS\", \"50u\", \"455\", \"5O0\", \"4Jp\", \"05V\", \"Rl\", \"2IM\", \"08f\", \"5Wa\", \"66k\", \"7Ln\", \"4il\", \"0RB\", \"5x\", \"Bh\", \"0aZ\", \"4Zt\", \"6oW\", \n\"4a9\", \"40P\", \"0Ov\", \"lD\", \"at\", \"0BF\", \"4yh\", \"6LK\", \"6bg\", \"4WD\", \"Z9\", \"OX\", \"2VH\", \"U8\", \"4XE\", \"6mf\", \"6CJ\", \"42a\", \"0MG\", \"nu\", \n\"cE\", \"1PV\", \"5kx\", \"4n8\", \"5P5\", \"4Uu\", \"8gC\", \"Mi\", \"04Q\", \"Sk\", \"5N7\", \"4Kw\", \"51r\", \"442\", \"9O\", \"1NT\", \"0SE\", \"pw\", \"7Mi\", \"4hk\", \n\"4FG\", \"67l\", \"2HJ\", \"09a\", \"3\", \"QZ\", \"68m\", \"4IF\", \"4gj\", \"6RI\", \"2ig\", \"1Le\", \"0Qt\", \"6N\", \"7OX\", \"4jZ\", \"4Dv\", \"5A6\", \"0j9\", \"1oy\", \n\"4xr\", \"6MQ\", \"22w\", \"377\", \"0mp\", \"NB\", \"77T\", \"blm\", \"5KO\", \"6nM\", \"Cr\", \"14i\", \"0Nl\", \"3kn\", \"ajs\", \"41J\", \"4zC\", \"aer\", \"20F\", \"36\", \n\"0oA\", \"Ls\", \"6aL\", \"4To\", \"bcl\", \"78U\", \"AC\", \"0bq\", \"386\", \"oo\", \"5r3\", \"4ws\", \"5l1\", \"4iq\", \"9Kf\", \"5e\", \"1y3\", \"1lR\", \"736\", \"66v\", \n\"7oo\", \"4Jm\", \"05K\", \"Rq\", \"8U\", \"1ON\", \"4dA\", \"6Qb\", \"7NB\", \"bQs\", \"0Pn\", \"7T\", \"2Ka\", \"1nc\", \"4El\", \"64G\", \"69w\", \"b6g\", \"07z\", \"1v2\", \n\"dRN\", \"8TF\", \"4fp\", \"5c0\", \"akm\", \"40T\", \"0Or\", \"1J2\", \"Bl\", \"15w\", \"4Zp\", \"6oS\", \"6bc\", \"5Ga\", \"0ln\", \"2YM\", \"ap\", \"0BB\", \"4yl\", \"6LO\", \n\"6CN\", \"42e\", \"0MC\", \"nq\", \"2VL\", \"0co\", \"4XA\", \"6mb\", \"5P1\", \"4Uq\", \"8gG\", \"Mm\", \"cA\", \"1PR\", \"bAn\", \"adl\", \"51v\", \"446\", \"9K\", \"1NP\", \n\"04U\", \"So\", \"5N3\", \"4Ks\", \"4FC\", \"67h\", \"2HN\", \"09e\", \"0SA\", \"ps\", \"7Mm\", \"4ho\", \"4gn\", \"6RM\", \"2ic\", \"1La\", \"7\", \"2GO\", \"68i\", \"4IB\", \n\"4Dr\", \"5A2\", \"d4D\", \"82L\", \"0Qp\", \"6J\", \"a1g\", \"bPm\", \"0mt\", \"NF\", \"6cy\", \"4VZ\", \"4xv\", \"6MU\", \"0V9\", \"0CX\", \"0Nh\", \"mZ\", \"7PD\", \"41N\", \n\"5KK\", \"6nI\", \"Cv\", \"14m\", \"0oE\", \"Lw\", \"6aH\", \"4Tk\", \"4zG\", \"6Od\", \"20B\", \"32\", \"0LY\", \"ok\", \"5r7\", \"4ww\", \"5Iz\", \"6lx\", \"AG\", \"0bu\", \n\"1y7\", \"1lV\", \"4GY\", \"4R8\", \"5l5\", \"4iu\", \"1Bz\", \"5a\", \"8Q\", \"i8\", \"4dE\", \"6Qf\", \"7ok\", \"4Ji\", \"05O\", \"Ru\", \"2Ke\", \"1ng\", \"4Eh\", \"64C\", \n\"7NF\", \"4kD\", \"f9\", \"7P\", \"2hy\", \"3m9\", \"4ft\", \"5c4\", \"69s\", \"4HX\", \"0sv\", \"PD\", \"23e\", \"0BN\", \"5iA\", \"6LC\", \"6bo\", \"4WL\", \"Z1\", \"OP\", \n\"0t3\", \"0aR\", \"c4f\", \"aEM\", \"4a1\", \"40X\", \"8Ff\", \"lL\", \"cM\", \"8Ig\", \"5kp\", \"4n0\", \"74w\", \"617\", \"0nS\", \"Ma\", \"3Fa\", \"U0\", \"4XM\", \"6mn\", \n\"6CB\", \"42i\", \"0MO\", \"2xl\", \"0SM\", \"4w\", \"7Ma\", \"4hc\", \"4FO\", \"67d\", \"2HB\", \"K2\", \"04Y\", \"Sc\", \"aTN\", \"b5D\", \"4eS\", \"4p2\", \"9G\", \"8We\", \n\"256\", \"6F\", \"7OP\", \"4jR\", \"cnl\", \"65U\", \"0j1\", \"1oq\", \"D3\", \"QR\", \"68e\", \"4IN\", \"4gb\", \"6RA\", \"2io\", \"1Lm\", \"5KG\", \"6nE\", \"Cz\", \"14a\", \n\"x7\", \"mV\", \"7PH\", \"41B\", \"4xz\", \"592\", \"0V5\", \"0CT\", \"0mx\", \"NJ\", \"4C7\", \"4VV\", \"4YW\", \"4L6\", \"AK\", \"0by\", \"0LU\", \"og\", \"563\", \"43s\", \n\"4zK\", \"6Oh\", \"bW\", \"w6\", \"0oI\", \"2Zj\", \"6aD\", \"4Tg\", \"7og\", \"4Je\", \"05C\", \"Ry\", \"2jD\", \"i4\", \"4dI\", \"6Qj\", \"5l9\", \"4iy\", \"0RW\", \"5m\", \n\"2IX\", \"08s\", \"4GU\", \"4R4\", \"7mV\", \"4HT\", \"07r\", \"PH\", \"0H7\", \"1Mw\", \"4fx\", \"5c8\", \"7NJ\", \"4kH\", \"f5\", \"sT\", \"2Ki\", \"1nk\", \"4Ed\", \"64O\", \n\"6bk\", \"4WH\", \"Z5\", \"OT\", \"ax\", \"0BJ\", \"4yd\", \"6LG\", \"4a5\", \"4tT\", \"0Oz\", \"lH\", \"Bd\", \"0aV\", \"4Zx\", \"aEI\", \"5P9\", \"4Uy\", \"0nW\", \"Me\", \n\"cI\", \"1PZ\", \"5kt\", \"4n4\", \"6CF\", \"42m\", \"0MK\", \"ny\", \"2VD\", \"U4\", \"4XI\", \"6mj\", \"4FK\", \"6sh\", \"2HF\", \"K6\", \"0SI\", \"4s\", \"7Me\", \"4hg\", \n\"4eW\", \"4p6\", \"9C\", \"1NX\", \"0pU\", \"Sg\", \"7ny\", \"6k9\", \"4Dz\", \"65Q\", \"0j5\", \"1ou\", \"0Qx\", \"6B\", \"7OT\", \"4jV\", \"4gf\", \"6RE\", \"2ik\", \"1Li\", \n\"D7\", \"QV\", \"68a\", \"4IJ\", \"x3\", \"mR\", \"7PL\", \"41F\", \"5KC\", \"6nA\", \"2Uo\", \"14e\", \"19U\", \"NN\", \"4C3\", \"4VR\", \"bBM\", \"596\", \"0V1\", \"0CP\", \n\"0LQ\", \"oc\", \"567\", \"43w\", \"4YS\", \"4L2\", \"AO\", \"16T\", \"0oM\", \"2Zn\", \"75i\", \"4Tc\", \"4zO\", \"6Ol\", \"bS\", \"w2\", \"8Y\", \"i0\", \"4dM\", \"6Qn\", \n\"7oc\", \"4Ja\", \"05G\", \"2Dl\", \"d7g\", \"08w\", \"4GQ\", \"4R0\", \"a2D\", \"bSN\", \"0RS\", \"5i\", \"0H3\", \"1Ms\", \"52U\", \"ayM\", \"7mR\", \"4HP\", \"07v\", \"PL\", \n\"2Km\", \"1no\", \"5UA\", \"64K\", \"7NN\", \"4kL\", \"f1\", \"7X\", \"5nw\", \"4k7\", \"fJ\", \"0Ex\", \"0kT\", \"Hf\", \"6eY\", \"4Pz\", \"5Mk\", \"6hi\", \"EV\", \"P7\", \n\"0HH\", \"kz\", \"6FE\", \"47n\", \"48o\", \"6ID\", \"26b\", \"0GI\", \"0ie\", \"JW\", \"6gh\", \"4RK\", \"5OZ\", \"6jX\", \"Gg\", \"0dU\", \"0Jy\", \"iK\", \"4d6\", \"4qW\", \n\"4z4\", \"4oU\", \"1DZ\", \"3A\", \"Ye\", \"0zW\", \"4Ay\", \"5D9\", \"6yj\", \"4LI\", \"A4\", \"TU\", \"zy\", \"0YK\", \"4be\", \"6WF\", \"6XG\", \"4md\", \"0VJ\", \"1p\", \n\"2ME\", \"N5\", \"4CH\", \"62c\", \"5K8\", \"4Nx\", \"0uV\", \"Vd\", \"xH\", \"8Rb\", \"5pu\", \"4u5\", \"D\", \"13W\", \"5Lq\", \"4I1\", \"534\", \"46t\", \"0IR\", \"28y\", \n\"gP\", \"69\", \"5om\", \"6Jo\", \"6dC\", \"5AA\", \"0jN\", \"3OL\", \"2Pl\", \"0eO\", \"aT1\", \"6kB\", \"6En\", \"44E\", \"98\", \"hQ\", \"ea\", \"0FS\", \"49u\", \"abL\", \n\"4F0\", \"4SQ\", \"8ag\", \"KM\", \"02u\", \"UO\", \"4X2\", \"4MS\", \"57V\", \"a8F\", \"0M0\", \"0XQ\", \"c2\", \"vS\", \"7KM\", \"4nO\", \"5PB\", \"61H\", \"2Nn\", \"1kl\", \n\"00D\", \"2Ao\", \"6zA\", \"4Ob\", \"4aN\", \"6Tm\", \"yR\", \"l3\", \"0WP\", \"0j\", \"a7G\", \"58W\", \"4BR\", \"4W3\", \"ZN\", \"84l\", \"0kP\", \"Hb\", \"71t\", \"644\", \n\"5ns\", \"4k3\", \"fN\", \"8Ld\", \"0HL\", \"29g\", \"6FA\", \"47j\", \"5Mo\", \"6hm\", \"ER\", \"P3\", \"0ia\", \"JS\", \"6gl\", \"4RO\", \"48k\", \"7Ya\", \"26f\", \"0GM\", \n\"8Ce\", \"iO\", \"4d2\", \"4qS\", \"beL\", \"hYw\", \"Gc\", \"0dQ\", \"Ya\", \"0zS\", \"cko\", \"60V\", \"4z0\", \"4oQ\", \"205\", \"3E\", \"2ll\", \"0YO\", \"4ba\", \"6WB\", \n\"6yn\", \"4LM\", \"A0\", \"TQ\", \"2MA\", \"N1\", \"4CL\", \"62g\", \"6XC\", \"59I\", \"0VN\", \"1t\", \"xL\", \"8Rf\", \"54y\", \"419\", \"aQM\", \"b0G\", \"01Z\", \"3PP\", \n\"530\", \"46p\", \"0IV\", \"jd\", \"DH\", \"0gz\", \"5Lu\", \"4I5\", \"6dG\", \"4Qd\", \"0jJ\", \"Ix\", \"gT\", \"r5\", \"5oi\", \"6Jk\", \"6Ej\", \"44A\", \"0Kg\", \"hU\", \n\"Fy\", \"0eK\", \"5ND\", \"6kF\", \"4F4\", \"4SU\", \"1xZ\", \"KI\", \"ee\", \"0FW\", \"49q\", \"5x9\", \"57R\", \"6VX\", \"0M4\", \"0XU\", \"02q\", \"UK\", \"4X6\", \"4MW\", \n\"5PF\", \"61L\", \"2Nj\", \"1kh\", \"c6\", \"vW\", \"7KI\", \"4nK\", \"4aJ\", \"6Ti\", \"yV\", \"l7\", \"0tH\", \"Wz\", \"6zE\", \"4Of\", \"4BV\", \"4W7\", \"ZJ\", \"0yx\", \n\"0WT\", \"0n\", \"6YY\", \"4lz\", \"5Mc\", \"6ha\", \"2SO\", \"0fl\", \"1Xa\", \"kr\", \"6FM\", \"47f\", \"bDm\", \"aao\", \"fB\", \"0Ep\", \"8bD\", \"Hn\", \"5U2\", \"4Pr\", \n\"5OR\", \"5Z3\", \"Go\", \"10t\", \"0Jq\", \"iC\", \"ann\", \"45W\", \"48g\", \"6IL\", \"ds\", \"0GA\", \"0im\", \"3Lo\", \"73I\", \"4RC\", \"6yb\", \"4LA\", \"03g\", \"2BL\", \n\"zq\", \"0YC\", \"4bm\", \"6WN\", \"a4d\", \"bUn\", \"0Ts\", \"3I\", \"Ym\", \"87O\", \"4Aq\", \"5D1\", \"5K0\", \"4Np\", \"01V\", \"Vl\", \"2nQ\", \"1KS\", \"54u\", \"415\", \n\"6XO\", \"4ml\", \"0VB\", \"1x\", \"2MM\", \"0xn\", \"5Sa\", \"62k\", \"gX\", \"61\", \"5oe\", \"6Jg\", \"6dK\", \"4Qh\", \"0jF\", \"It\", \"L\", \"0gv\", \"5Ly\", \"4I9\", \n\"5w4\", \"4rt\", \"0IZ\", \"jh\", \"ei\", \"1Vz\", \"5mT\", \"5x5\", \"4F8\", \"4SY\", \"0hw\", \"KE\", \"Fu\", \"0eG\", \"5NH\", \"6kJ\", \"6Ef\", \"44M\", \"90\", \"hY\", \n\"0Ui\", \"2S\", \"7KE\", \"4nG\", \"5PJ\", \"6uH\", \"Xw\", \"1kd\", \"0vu\", \"UG\", \"6xx\", \"790\", \"4cw\", \"5f7\", \"0M8\", \"0XY\", \"0WX\", \"0b\", \"5i6\", \"4lv\", \n\"4BZ\", \"63q\", \"ZF\", \"0yt\", \"00L\", \"Wv\", \"6zI\", \"4Oj\", \"4aF\", \"6Te\", \"yZ\", \"0Zh\", \"0HD\", \"kv\", \"6FI\", \"47b\", \"5Mg\", \"6he\", \"EZ\", \"0fh\", \n\"0kX\", \"Hj\", \"5U6\", \"4Pv\", \"7N9\", \"6Ky\", \"fF\", \"0Et\", \"0Ju\", \"iG\", \"6Dx\", \"45S\", \"5OV\", \"5Z7\", \"Gk\", \"0dY\", \"0ii\", \"3Lk\", \"6gd\", \"4RG\", \n\"48c\", \"6IH\", \"dw\", \"0GE\", \"zu\", \"0YG\", \"4bi\", \"6WJ\", \"6yf\", \"4LE\", \"A8\", \"TY\", \"Yi\", \"1jz\", \"4Au\", \"5D5\", \"4z8\", \"4oY\", \"0Tw\", \"3M\", \n\"xD\", \"1KW\", \"54q\", \"411\", \"5K4\", \"4Nt\", \"01R\", \"Vh\", \"2MI\", \"N9\", \"4CD\", \"62o\", \"6XK\", \"4mh\", \"0VF\", \"ut\", \"6dO\", \"4Ql\", \"0jB\", \"Ip\", \n\"25E\", \"65\", \"5oa\", \"6Jc\", \"538\", \"46x\", \"9Pg\", \"jl\", \"H\", \"0gr\", \"bfo\", \"aCm\", \"72W\", \"bin\", \"0hs\", \"KA\", \"em\", \"324\", \"49y\", \"5x1\", \n\"6Eb\", \"44I\", \"94\", \"3nm\", \"Fq\", \"0eC\", \"5NL\", \"6kN\", \"5PN\", \"61D\", \"Xs\", \"86Q\", \"0Um\", \"2W\", \"7KA\", \"4nC\", \"4cs\", \"5f3\", \"39W\", \"8QE\", \n\"02y\", \"UC\", \"aRn\", \"794\", \"765\", \"63u\", \"ZB\", \"0yp\", \"9Ne\", \"0f\", \"5i2\", \"4lr\", \"4aB\", \"6Ta\", \"2oO\", \"0Zl\", \"00H\", \"Wr\", \"6zM\", \"4On\", \n\"5lW\", \"5y6\", \"dj\", \"0GX\", \"0it\", \"JF\", \"6gy\", \"4RZ\", \"5OK\", \"6jI\", \"Gv\", \"0dD\", \"83\", \"iZ\", \"6De\", \"45N\", \"5nf\", \"6Kd\", \"24B\", \"72\", \n\"0kE\", \"Hw\", \"6eH\", \"4Pk\", \"5Mz\", \"6hx\", \"EG\", \"0fu\", \"0HY\", \"kk\", \"5v7\", \"4sw\", \"5h5\", \"4mu\", \"1Fz\", \"1a\", \"2MT\", \"0xw\", \"4CY\", \"4V8\", \n\"7kk\", \"4Ni\", \"01O\", \"Vu\", \"xY\", \"m8\", \"54l\", \"6Uf\", \"6Zg\", \"4oD\", \"b9\", \"3P\", \"Yt\", \"0zF\", \"4Ah\", \"60C\", \"4Y9\", \"4LX\", \"0wv\", \"TD\", \n\"zh\", \"0YZ\", \"4bt\", \"5g4\", \"Fl\", \"11w\", \"5NQ\", \"6kS\", \"aom\", \"44T\", \"0Kr\", \"1N2\", \"ep\", \"0FB\", \"49d\", \"6HO\", \"6fc\", \"5Ca\", \"0hn\", \"3Ml\", \n\"U\", \"0go\", \"bfr\", \"6ib\", \"6GN\", \"46e\", \"0IC\", \"jq\", \"gA\", \"0Ds\", \"bEn\", \"hyU\", \"5T1\", \"4Qq\", \"8cG\", \"Im\", \"00U\", \"Wo\", \"5J3\", \"4Os\", \n\"55v\", \"406\", \"yC\", \"0Zq\", \"0WA\", \"ts\", \"6YL\", \"4lo\", \"4BC\", \"63h\", \"2LN\", \"0ym\", \"02d\", \"2CO\", \"6xa\", \"4MB\", \"4cn\", \"6VM\", \"2mc\", \"1Ha\", \n\"0Up\", \"2J\", \"a5g\", \"bTm\", \"5PS\", \"5E2\", \"Xn\", \"86L\", \"0ip\", \"JB\", \"73T\", \"bhm\", \"48z\", \"5y2\", \"dn\", \"337\", \"87\", \"3on\", \"6Da\", \"45J\", \n\"5OO\", \"6jM\", \"Gr\", \"10i\", \"0kA\", \"Hs\", \"6eL\", \"4Po\", \"5nb\", \"aar\", \"24F\", \"76\", \"8AE\", \"ko\", \"5v3\", \"4ss\", \"bgl\", \"aBn\", \"EC\", \"0fq\", \n\"2MP\", \"0xs\", \"776\", \"62v\", \"5h1\", \"4mq\", \"9Of\", \"1e\", \"2nL\", \"1KN\", \"54h\", \"6Ub\", \"7ko\", \"4Nm\", \"01K\", \"Vq\", \"Yp\", \"0zB\", \"4Al\", \"60G\", \n\"6Zc\", \"bUs\", \"0Tn\", \"3T\", \"zl\", \"8PF\", \"4bp\", \"5g0\", \"aSm\", \"787\", \"03z\", \"1r2\", \"4e9\", \"44P\", \"0Kv\", \"hD\", \"Fh\", \"0eZ\", \"5NU\", \"6kW\", \n\"6fg\", \"4SD\", \"0hj\", \"KX\", \"et\", \"0FF\", \"5mI\", \"6HK\", \"6GJ\", \"46a\", \"0IG\", \"ju\", \"Q\", \"Q8\", \"5Ld\", \"6if\", \"5T5\", \"4Qu\", \"1zz\", \"Ii\", \n\"gE\", \"0Dw\", \"5ox\", \"4j8\", \"55r\", \"402\", \"yG\", \"0Zu\", \"00Q\", \"Wk\", \"5J7\", \"4Ow\", \"4BG\", \"63l\", \"2LJ\", \"0yi\", \"0WE\", \"tw\", \"6YH\", \"4lk\", \n\"4cj\", \"6VI\", \"2mg\", \"0XD\", \"0vh\", \"UZ\", \"6xe\", \"4MF\", \"5PW\", \"5E6\", \"Xj\", \"1ky\", \"0Ut\", \"2N\", \"7KX\", \"4nZ\", \"5OC\", \"6jA\", \"2Qo\", \"0dL\", \n\"1ZA\", \"iR\", \"6Dm\", \"45F\", \"48v\", \"acO\", \"db\", \"0GP\", \"94M\", \"JN\", \"4G3\", \"4RR\", \"5Mr\", \"4H2\", \"EO\", \"12T\", \"0HQ\", \"kc\", \"527\", \"47w\", \n\"5nn\", \"6Kl\", \"fS\", \"s2\", \"0kM\", \"3NO\", \"71i\", \"4Pc\", \"7kc\", \"4Na\", \"01G\", \"3PM\", \"xQ\", \"m0\", \"54d\", \"6Un\", \"a6D\", \"59T\", \"0VS\", \"1i\", \n\"197\", \"85o\", \"4CQ\", \"4V0\", \"4Y1\", \"4LP\", \"03v\", \"TL\", \"0L3\", \"0YR\", \"56U\", \"a9E\", \"6Zo\", \"4oL\", \"b1\", \"3X\", \"2Om\", \"0zN\", \"5QA\", \"60K\", \n\"ex\", \"0FJ\", \"49l\", \"6HG\", \"6fk\", \"4SH\", \"0hf\", \"KT\", \"Fd\", \"0eV\", \"5NY\", \"aAI\", \"4e5\", \"4pT\", \"0Kz\", \"hH\", \"gI\", \"1TZ\", \"5ot\", \"4j4\", \n\"5T9\", \"4Qy\", \"0jW\", \"Ie\", \"DU\", \"Q4\", \"5Lh\", \"6ij\", \"6GF\", \"46m\", \"0IK\", \"jy\", \"0WI\", \"0s\", \"6YD\", \"4lg\", \"4BK\", \"6wh\", \"ZW\", \"O6\", \n\"0tU\", \"Wg\", \"6zX\", \"6o9\", \"4aW\", \"4t6\", \"yK\", \"0Zy\", \"0Ux\", \"2B\", \"7KT\", \"4nV\", \"bzI\", \"61Q\", \"Xf\", \"1ku\", \"02l\", \"UV\", \"6xi\", \"4MJ\", \n\"4cf\", \"6VE\", \"2mk\", \"0XH\", \"0Jd\", \"iV\", \"6Di\", \"45B\", \"5OG\", \"6jE\", \"Gz\", \"0dH\", \"0ix\", \"JJ\", \"4G7\", \"4RV\", \"48r\", \"6IY\", \"df\", \"0GT\", \n\"0HU\", \"kg\", \"523\", \"47s\", \"5Mv\", \"4H6\", \"EK\", \"0fy\", \"0kI\", \"3NK\", \"6eD\", \"4Pg\", \"5nj\", \"6Kh\", \"fW\", \"s6\", \"xU\", \"m4\", \"5ph\", \"6Uj\", \n\"7kg\", \"4Ne\", \"01C\", \"Vy\", \"193\", \"1hZ\", \"4CU\", \"4V4\", \"5h9\", \"4my\", \"0VW\", \"1m\", \"zd\", \"0YV\", \"4bx\", \"5g8\", \"4Y5\", \"4LT\", \"03r\", \"TH\", \n\"Yx\", \"0zJ\", \"4Ad\", \"60O\", \"6Zk\", \"4oH\", \"b5\", \"wT\", \"6fo\", \"4SL\", \"0hb\", \"KP\", \"27e\", \"0FN\", \"49h\", \"6HC\", \"4e1\", \"44X\", \"8Bf\", \"hL\", \n\"0p3\", \"0eR\", \"bdO\", \"aAM\", \"70w\", \"657\", \"0jS\", \"Ia\", \"gM\", \"8Mg\", \"5op\", \"4j0\", \"6GB\", \"46i\", \"0IO\", \"28d\", \"Y\", \"Q0\", \"5Ll\", \"6in\", \n\"4BO\", \"63d\", \"ZS\", \"O2\", \"0WM\", \"0w\", \"7Ia\", \"4lc\", \"4aS\", \"4t2\", \"yO\", \"8Se\", \"00Y\", \"Wc\", \"aPN\", \"b1D\", \"bzM\", \"61U\", \"Xb\", \"1kq\", \n\"216\", \"2F\", \"7KP\", \"4nR\", \"4cb\", \"6VA\", \"2mo\", \"0XL\", \"02h\", \"UR\", \"6xm\", \"4MN\", \"5j7\", \"4ow\", \"0TY\", \"3c\", \"YG\", \"0zu\", \"5Qz\", \"60p\", \n\"6yH\", \"4Lk\", \"03M\", \"Tw\", \"2lJ\", \"0Yi\", \"4bG\", \"6Wd\", \"6Xe\", \"4mF\", \"0Vh\", \"1R\", \"2Mg\", \"0xD\", \"4Cj\", \"62A\", \"7kX\", \"4NZ\", \"0ut\", \"VF\", \n\"xj\", \"1Ky\", \"5pW\", \"5e6\", \"5nU\", \"6KW\", \"fh\", \"0EZ\", \"0kv\", \"HD\", \"4E9\", \"4PX\", \"5MI\", \"6hK\", \"Et\", \"0fF\", \"0Hj\", \"kX\", \"6Fg\", \"47L\", \n\"48M\", \"6If\", \"dY\", \"50\", \"0iG\", \"Ju\", \"6gJ\", \"4Ri\", \"5Ox\", \"4J8\", \"GE\", \"0dw\", \"1Zz\", \"ii\", \"5t5\", \"4qu\", \"02W\", \"Um\", \"5H1\", \"4Mq\", \n\"57t\", \"424\", \"2mP\", \"0Xs\", \"0UC\", \"2y\", \"7Ko\", \"4nm\", \"bzr\", \"61j\", \"2NL\", \"1kN\", \"00f\", \"2AM\", \"6zc\", \"bus\", \"4al\", \"6TO\", \"yp\", \"0ZB\", \n\"0Wr\", \"0H\", \"a7e\", \"58u\", \"4Bp\", \"5G0\", \"Zl\", \"84N\", \"f\", \"13u\", \"5LS\", \"5Y2\", \"amo\", \"46V\", \"0Ip\", \"jB\", \"gr\", \"1Ta\", \"5oO\", \"6JM\", \n\"6da\", \"4QB\", \"0jl\", \"3On\", \"2PN\", \"0em\", \"5Nb\", \"aAr\", \"6EL\", \"44g\", \"0KA\", \"hs\", \"eC\", \"0Fq\", \"49W\", \"abn\", \"5V3\", \"4Ss\", \"8aE\", \"Ko\", \n\"YC\", \"0zq\", \"754\", \"60t\", \"5j3\", \"4os\", \"9Md\", \"3g\", \"2lN\", \"0Ym\", \"4bC\", \"7GA\", \"6yL\", \"4Lo\", \"03I\", \"Ts\", \"2Mc\", \"1ha\", \"4Cn\", \"62E\", \n\"6Xa\", \"4mB\", \"0Vl\", \"1V\", \"xn\", \"8RD\", \"5pS\", \"5e2\", \"aQo\", \"b0e\", \"01x\", \"VB\", \"0kr\", \"1n2\", \"71V\", \"bjo\", \"5nQ\", \"6KS\", \"fl\", \"315\", \n\"0Hn\", \"29E\", \"6Fc\", \"47H\", \"5MM\", \"6hO\", \"Ep\", \"0fB\", \"0iC\", \"Jq\", \"6gN\", \"4Rm\", \"48I\", \"6Ib\", \"26D\", \"54\", \"8CG\", \"im\", \"509\", \"45y\", \n\"ben\", \"hYU\", \"GA\", \"0ds\", \"4cY\", \"420\", \"2mT\", \"0Xw\", \"02S\", \"Ui\", \"5H5\", \"4Mu\", \"5Pd\", \"61n\", \"XY\", \"M8\", \"0UG\", \"vu\", \"7Kk\", \"4ni\", \n\"4ah\", \"6TK\", \"yt\", \"0ZF\", \"B9\", \"WX\", \"6zg\", \"4OD\", \"4Bt\", \"5G4\", \"Zh\", \"0yZ\", \"0Wv\", \"0L\", \"4y9\", \"4lX\", \"6Gy\", \"46R\", \"0It\", \"jF\", \n\"b\", \"0gX\", \"5LW\", \"5Y6\", \"6de\", \"4QF\", \"0jh\", \"IZ\", \"gv\", \"0DD\", \"5oK\", \"6JI\", \"6EH\", \"44c\", \"0KE\", \"hw\", \"2PJ\", \"0ei\", \"5Nf\", \"6kd\", \n\"5V7\", \"4Sw\", \"0hY\", \"Kk\", \"eG\", \"0Fu\", \"49S\", \"6Hx\", \"7ia\", \"4Lc\", \"03E\", \"2Bn\", \"zS\", \"o2\", \"4bO\", \"6Wl\", \"a4F\", \"bUL\", \"0TQ\", \"3k\", \n\"YO\", \"87m\", \"4AS\", \"4T2\", \"7kP\", \"4NR\", \"01t\", \"VN\", \"xb\", \"1Kq\", \"54W\", \"hfv\", \"6Xm\", \"4mN\", \"1FA\", \"1Z\", \"2Mo\", \"0xL\", \"4Cb\", \"62I\", \n\"5MA\", \"6hC\", \"2Sm\", \"0fN\", \"0Hb\", \"kP\", \"6Fo\", \"47D\", \"bDO\", \"aaM\", \"0P3\", \"0ER\", \"8bf\", \"HL\", \"4E1\", \"4PP\", \"5Op\", \"4J0\", \"GM\", \"10V\", \n\"0JS\", \"ia\", \"505\", \"45u\", \"48E\", \"6In\", \"dQ\", \"58\", \"0iO\", \"3LM\", \"6gB\", \"4Ra\", \"0UK\", \"2q\", \"7Kg\", \"4ne\", \"5Ph\", \"61b\", \"XU\", \"M4\", \n\"0vW\", \"Ue\", \"5H9\", \"4My\", \"4cU\", \"4v4\", \"2mX\", \"1HZ\", \"0Wz\", \"tH\", \"4y5\", \"4lT\", \"4Bx\", \"5G8\", \"Zd\", \"0yV\", \"B5\", \"WT\", \"6zk\", \"4OH\", \n\"4ad\", \"6TG\", \"yx\", \"0ZJ\", \"gz\", \"0DH\", \"5oG\", \"6JE\", \"6di\", \"4QJ\", \"0jd\", \"IV\", \"n\", \"0gT\", \"680\", \"6iY\", \"4g7\", \"4rV\", \"0Ix\", \"jJ\", \n\"eK\", \"0Fy\", \"5mv\", \"4h6\", \"6fX\", \"5CZ\", \"0hU\", \"Kg\", \"FW\", \"S6\", \"5Nj\", \"6kh\", \"6ED\", \"44o\", \"0KI\", \"3nK\", \"zW\", \"o6\", \"4bK\", \"6Wh\", \n\"6yD\", \"4Lg\", \"03A\", \"2Bj\", \"YK\", \"0zy\", \"4AW\", \"4T6\", \"6ZX\", \"6O9\", \"0TU\", \"3o\", \"xf\", \"1Ku\", \"54S\", \"6UY\", \"7kT\", \"4NV\", \"01p\", \"VJ\", \n\"2Mk\", \"0xH\", \"4Cf\", \"62M\", \"6Xi\", \"4mJ\", \"0Vd\", \"uV\", \"0Hf\", \"kT\", \"6Fk\", \"4sH\", \"5ME\", \"6hG\", \"Ex\", \"0fJ\", \"0kz\", \"HH\", \"4E5\", \"4PT\", \n\"5nY\", \"aaI\", \"fd\", \"0EV\", \"0JW\", \"ie\", \"501\", \"45q\", \"5Ot\", \"4J4\", \"GI\", \"10R\", \"0iK\", \"Jy\", \"6gF\", \"4Re\", \"48A\", \"6Ij\", \"dU\", \"q4\", \n\"5Pl\", \"61f\", \"XQ\", \"M0\", \"0UO\", \"2u\", \"7Kc\", \"4na\", \"4cQ\", \"428\", \"39u\", \"8Qg\", \"0vS\", \"Ua\", \"aRL\", \"b3F\", \"bxO\", \"63W\", \"0l3\", \"0yR\", \n\"234\", \"0D\", \"4y1\", \"4lP\", \"55I\", \"6TC\", \"2om\", \"0ZN\", \"B1\", \"WP\", \"6zo\", \"4OL\", \"6dm\", \"4QN\", \"1zA\", \"IR\", \"25g\", \"0DL\", \"5oC\", \"6JA\", \n\"4g3\", \"46Z\", \"9PE\", \"jN\", \"j\", \"0gP\", \"684\", \"aCO\", \"72u\", \"675\", \"0hQ\", \"Kc\", \"eO\", \"8Oe\", \"5mr\", \"4h2\", \"7Ua\", \"44k\", \"0KM\", \"3nO\", \n\"FS\", \"S2\", \"5Nn\", \"6kl\", \"4x6\", \"4mW\", \"0Vy\", \"1C\", \"0m4\", \"0xU\", \"5SZ\", \"62P\", \"7kI\", \"4NK\", \"C6\", \"VW\", \"2nj\", \"1Kh\", \"54N\", \"6UD\", \n\"6ZE\", \"4of\", \"0TH\", \"3r\", \"YV\", \"L7\", \"4AJ\", \"60a\", \"6yY\", \"4Lz\", \"0wT\", \"Tf\", \"zJ\", \"0Yx\", \"4bV\", \"4w7\", \"5lu\", \"4i5\", \"dH\", \"0Gz\", \n\"0iV\", \"Jd\", \"5W8\", \"4Rx\", \"5Oi\", \"6jk\", \"GT\", \"R5\", \"0JJ\", \"ix\", \"6DG\", \"45l\", \"5nD\", \"6KF\", \"fy\", \"0EK\", \"0kg\", \"HU\", \"6ej\", \"4PI\", \n\"5MX\", \"5X9\", \"Ee\", \"0fW\", \"1XZ\", \"kI\", \"4f4\", \"4sU\", \"00w\", \"WM\", \"4Z0\", \"4OQ\", \"55T\", \"hgu\", \"ya\", \"0ZS\", \"a0\", \"0Y\", \"6Yn\", \"4lM\", \n\"4Ba\", \"63J\", \"2Ll\", \"0yO\", \"02F\", \"2Cm\", \"6xC\", \"aG0\", \"4cL\", \"6Vo\", \"2mA\", \"n1\", \"0UR\", \"2h\", \"a5E\", \"bTO\", \"5Pq\", \"4U1\", \"XL\", \"86n\", \n\"FN\", \"11U\", \"5Ns\", \"4K3\", \"516\", \"44v\", \"0KP\", \"hb\", \"eR\", \"p3\", \"49F\", \"6Hm\", \"6fA\", \"4Sb\", \"0hL\", \"3MN\", \"w\", \"0gM\", \"5LB\", \"7ya\", \n\"6Gl\", \"46G\", \"0Ia\", \"jS\", \"gc\", \"0DQ\", \"bEL\", \"hyw\", \"4D2\", \"4QS\", \"8ce\", \"IO\", \"0m0\", \"0xQ\", \"byL\", \"62T\", \"4x2\", \"4mS\", \"227\", \"1G\", \n\"2nn\", \"1Kl\", \"54J\", \"7Ea\", \"7kM\", \"4NO\", \"C2\", \"VS\", \"YR\", \"L3\", \"4AN\", \"60e\", \"6ZA\", \"4ob\", \"0TL\", \"3v\", \"zN\", \"8Pd\", \"4bR\", \"4w3\", \n\"aSO\", \"b2E\", \"03X\", \"Tb\", \"0iR\", \"3LP\", \"73v\", \"666\", \"48X\", \"4i1\", \"dL\", \"8Nf\", \"0JN\", \"3oL\", \"6DC\", \"45h\", \"5Om\", \"6jo\", \"GP\", \"R1\", \n\"0kc\", \"HQ\", \"6en\", \"4PM\", \"a09\", \"6KB\", \"24d\", \"0EO\", \"8Ag\", \"kM\", \"4f0\", \"47Y\", \"697\", \"aBL\", \"Ea\", \"0fS\", \"4ay\", \"5d9\", \"ye\", \"0ZW\", \n\"00s\", \"WI\", \"4Z4\", \"4OU\", \"4Be\", \"63N\", \"Zy\", \"0yK\", \"a4\", \"tU\", \"6Yj\", \"4lI\", \"4cH\", \"6Vk\", \"2mE\", \"n5\", \"02B\", \"Ux\", \"6xG\", \"4Md\", \n\"5Pu\", \"4U5\", \"XH\", \"86j\", \"0UV\", \"2l\", \"5k8\", \"4nx\", \"512\", \"44r\", \"0KT\", \"hf\", \"FJ\", \"0ex\", \"5Nw\", \"4K7\", \"6fE\", \"4Sf\", \"0hH\", \"Kz\", \n\"eV\", \"p7\", \"49B\", \"6Hi\", \"6Gh\", \"46C\", \"0Ie\", \"jW\", \"s\", \"0gI\", \"5LF\", \"6iD\", \"4D6\", \"4QW\", \"0jy\", \"IK\", \"gg\", \"0DU\", \"5oZ\", \"6JX\", \n\"7kA\", \"4NC\", \"01e\", \"3Po\", \"xs\", \"8RY\", \"54F\", \"6UL\", \"a6f\", \"59v\", \"0Vq\", \"1K\", \"d3E\", \"85M\", \"4Cs\", \"5F3\", \"5I2\", \"4Lr\", \"03T\", \"Tn\", \n\"zB\", \"0Yp\", \"56w\", \"437\", \"6ZM\", \"4on\", \"1Da\", \"3z\", \"2OO\", \"0zl\", \"4AB\", \"60i\", \"5Oa\", \"6jc\", \"2QM\", \"0dn\", \"0JB\", \"ip\", \"6DO\", \"45d\", \n\"48T\", \"acm\", \"1B2\", \"0Gr\", \"94o\", \"Jl\", \"5W0\", \"4Rp\", \"5MP\", \"5X1\", \"Em\", \"12v\", \"0Hs\", \"kA\", \"all\", \"47U\", \"5nL\", \"6KN\", \"fq\", \"0EC\", \n\"0ko\", \"3Nm\", \"6eb\", \"4PA\", \"a8\", \"0Q\", \"6Yf\", \"4lE\", \"4Bi\", \"63B\", \"Zu\", \"0yG\", \"0tw\", \"WE\", \"4Z8\", \"4OY\", \"4au\", \"5d5\", \"yi\", \"1Jz\", \n\"0UZ\", \"vh\", \"5k4\", \"4nt\", \"5Py\", \"4U9\", \"XD\", \"1kW\", \"02N\", \"Ut\", \"6xK\", \"4Mh\", \"4cD\", \"6Vg\", \"2mI\", \"n9\", \"eZ\", \"43\", \"49N\", \"6He\", \n\"6fI\", \"4Sj\", \"0hD\", \"Kv\", \"FF\", \"0et\", \"7n9\", \"6ky\", \"5u6\", \"4pv\", \"0KX\", \"hj\", \"gk\", \"0DY\", \"5oV\", \"5z7\", \"6dx\", \"5Az\", \"0ju\", \"IG\", \n\"Dw\", \"0gE\", \"5LJ\", \"6iH\", \"6Gd\", \"46O\", \"0Ii\", \"28B\", \"xw\", \"1Kd\", \"54B\", \"6UH\", \"7kE\", \"4NG\", \"01a\", \"3Pk\", \"0m8\", \"0xY\", \"4Cw\", \"5F7\", \n\"6Xx\", \"59r\", \"0Vu\", \"1O\", \"zF\", \"0Yt\", \"4bZ\", \"433\", \"5I6\", \"4Lv\", \"03P\", \"Tj\", \"YZ\", \"0zh\", \"4AF\", \"60m\", \"6ZI\", \"4oj\", \"0TD\", \"wv\", \n\"0JF\", \"it\", \"6DK\", \"4qh\", \"5Oe\", \"6jg\", \"GX\", \"R9\", \"0iZ\", \"Jh\", \"5W4\", \"4Rt\", \"48P\", \"4i9\", \"dD\", \"0Gv\", \"0Hw\", \"kE\", \"4f8\", \"47Q\", \n\"5MT\", \"5X5\", \"Ei\", \"12r\", \"0kk\", \"HY\", \"6ef\", \"4PE\", \"5nH\", \"6KJ\", \"fu\", \"0EG\", \"4Bm\", \"63F\", \"Zq\", \"0yC\", \"0Wo\", \"0U\", \"6Yb\", \"4lA\", \n\"4aq\", \"5d1\", \"ym\", \"8SG\", \"0ts\", \"WA\", \"aPl\", \"b1f\", \"747\", \"61w\", \"2NQ\", \"1kS\", \"9Lg\", \"2d\", \"5k0\", \"4np\", \"57i\", \"6Vc\", \"2mM\", \"0Xn\", \n\"02J\", \"Up\", \"6xO\", \"4Ml\", \"6fM\", \"4Sn\", \"1xa\", \"Kr\", \"27G\", \"47\", \"49J\", \"6Ha\", \"5u2\", \"44z\", \"8BD\", \"hn\", \"FB\", \"0ep\", \"bdm\", \"aAo\", \n\"70U\", \"bkl\", \"0jq\", \"IC\", \"go\", \"306\", \"5oR\", \"5z3\", \"7WA\", \"46K\", \"0Im\", \"28F\", \"Ds\", \"0gA\", \"5LN\", \"6iL\", \"0cY\", \"020\", \"6mT\", \"4Xw\", \n\"42S\", \"6Cx\", \"nG\", \"0Mu\", \"1Pd\", \"cw\", \"6NH\", \"5kJ\", \"4UG\", \"74M\", \"3Kk\", \"0ni\", \"0ah\", \"BZ\", \"6oe\", \"4ZF\", \"40b\", \"6AI\", \"lv\", \"0OD\", \n\"0Bt\", \"aF\", \"6Ly\", \"4yZ\", \"4Wv\", \"5R6\", \"Oj\", \"0lX\", \"Qh\", \"06R\", \"4It\", \"5L4\", \"461\", \"4gX\", \"1LW\", \"1Y6\", \"rt\", \"0QF\", \"4jh\", \"7Oj\", \n\"65o\", \"4DD\", \"I9\", \"2JI\", \"SY\", \"F8\", \"4KE\", \"7nG\", \"6PJ\", \"4ei\", \"1Nf\", \"2kd\", \"4M\", \"0Sw\", \"4hY\", \"490\", \"5C5\", \"4Fu\", \"09S\", \"2Hx\", \n\"6OR\", \"4zq\", \"354\", \"bm\", \"LA\", \"0os\", \"bnn\", \"75W\", \"6lN\", \"4Ym\", \"0bC\", \"Aq\", \"2yL\", \"0Lo\", \"43I\", \"6Bb\", \"6Mc\", \"5ha\", \"15\", \"22E\", \n\"Np\", \"0mB\", \"4Vl\", \"6cO\", \"aDm\", \"bao\", \"1pS\", \"1e2\", \"ml\", \"8GF\", \"41x\", \"548\", \"4kr\", \"5n2\", \"7f\", \"8YD\", \"1nQ\", \"2KS\", \"64u\", \"715\", \n\"4Hn\", \"69E\", \"Pr\", \"07H\", \"1MM\", \"2hO\", \"6Sa\", \"4fB\", \"4iC\", \"7LA\", \"5W\", \"0Rm\", \"08I\", \"2Ib\", \"66D\", \"4Go\", \"b4d\", \"aUn\", \"RC\", \"05y\", \n\"8VE\", \"8g\", \"5a3\", \"4ds\", \"42W\", \"ain\", \"nC\", \"0Mq\", \"17t\", \"024\", \"6mP\", \"4Xs\", \"4UC\", \"74I\", \"3Ko\", \"0nm\", \"8IY\", \"cs\", \"6NL\", \"5kN\", \n\"40f\", \"6AM\", \"lr\", \"8FX\", \"0al\", \"2TO\", \"6oa\", \"4ZB\", \"4Wr\", \"5R2\", \"On\", \"18u\", \"0Bp\", \"aB\", \"afo\", \"bCm\", \"465\", \"53u\", \"1LS\", \"1Y2\", \n\"Ql\", \"06V\", \"4Ip\", \"5L0\", \"65k\", \"5Ta\", \"1oO\", \"2JM\", \"6x\", \"0QB\", \"4jl\", \"7On\", \"6PN\", \"4em\", \"1Nb\", \"9y\", \"2EL\", \"04g\", \"4KA\", \"7nC\", \n\"5C1\", \"4Fq\", \"09W\", \"d6G\", \"4I\", \"0Ss\", \"bRn\", \"494\", \"LE\", \"0ow\", \"4TY\", \"4A8\", \"6OV\", \"4zu\", \"1Qz\", \"bi\", \"oY\", \"z8\", \"43M\", \"6Bf\", \n\"6lJ\", \"4Yi\", \"0bG\", \"Au\", \"Nt\", \"0mF\", \"4Vh\", \"6cK\", \"6Mg\", \"4xD\", \"11\", \"22A\", \"mh\", \"0NZ\", \"4ut\", \"5p4\", \"4N9\", \"5Ky\", \"1pW\", \"CD\", \n\"1nU\", \"2KW\", \"64q\", \"4EZ\", \"4kv\", \"5n6\", \"7b\", \"0PX\", \"1MI\", \"2hK\", \"6Se\", \"4fF\", \"4Hj\", \"69A\", \"Pv\", \"07L\", \"08M\", \"2If\", \"6rH\", \"4Gk\", \n\"4iG\", \"7LE\", \"5S\", \"0Ri\", \"1Ox\", \"8c\", \"5a7\", \"4dw\", \"5Zz\", \"7oY\", \"RG\", \"0qu\", \"1Pl\", \"21f\", \"adR\", \"5kB\", \"4UO\", \"74E\", \"MS\", \"X2\", \n\"0cQ\", \"028\", \"79u\", \"bbL\", \"4vS\", \"4c2\", \"nO\", \"8De\", \"8Kd\", \"aN\", \"4l3\", \"4yR\", \"634\", \"76t\", \"Ob\", \"0lP\", \"W3\", \"BR\", \"6om\", \"4ZN\", \n\"40j\", \"6AA\", \"2zo\", \"0OL\", \"6t\", \"0QN\", \"5zA\", \"7Ob\", \"65g\", \"4DL\", \"I1\", \"2JA\", \"0g3\", \"06Z\", \"b7G\", \"68W\", \"469\", \"4gP\", \"284\", \"dSn\", \n\"4E\", \"275\", \"4hQ\", \"498\", \"67V\", \"b8F\", \"1mr\", \"0h2\", \"SQ\", \"F0\", \"4KM\", \"7nO\", \"6PB\", \"4ea\", \"1Nn\", \"9u\", \"6lF\", \"4Ye\", \"0bK\", \"Ay\", \n\"oU\", \"z4\", \"43A\", \"6Bj\", \"6OZ\", \"4zy\", \"0AW\", \"be\", \"LI\", \"2O9\", \"4TU\", \"4A4\", \"4N5\", \"5Ku\", \"14S\", \"CH\", \"md\", \"0NV\", \"41p\", \"540\", \n\"6Mk\", \"4xH\", \"u5\", \"22M\", \"Nx\", \"0mJ\", \"4Vd\", \"6cG\", \"4Hf\", \"69M\", \"Pz\", \"0sH\", \"k7\", \"2hG\", \"6Si\", \"4fJ\", \"4kz\", \"7Nx\", \"7n\", \"0PT\", \n\"1nY\", \"dqh\", \"4P7\", \"4EV\", \"4JW\", \"7oU\", \"RK\", \"05q\", \"1Ot\", \"8o\", \"6QX\", \"50R\", \"4iK\", \"7LI\", \"qW\", \"d6\", \"08A\", \"2Ij\", \"66L\", \"4Gg\", \n\"4UK\", \"74A\", \"MW\", \"X6\", \"1Ph\", \"21b\", \"6ND\", \"5kF\", \"4vW\", \"4c6\", \"nK\", \"0My\", \"0cU\", \"0v4\", \"6mX\", \"5HZ\", \"4Wz\", \"6bY\", \"Of\", \"0lT\", \n\"0Bx\", \"aJ\", \"4l7\", \"4yV\", \"40n\", \"6AE\", \"lz\", \"0OH\", \"W7\", \"BV\", \"6oi\", \"4ZJ\", \"65c\", \"4DH\", \"I5\", \"2JE\", \"6p\", \"0QJ\", \"4jd\", \"7Of\", \n\"4r5\", \"4gT\", \"280\", \"2iY\", \"Qd\", \"0rV\", \"4Ix\", \"5L8\", \"5C9\", \"4Fy\", \"1mv\", \"0h6\", \"4A\", \"1CZ\", \"4hU\", \"7MW\", \"6PF\", \"4ee\", \"1Nj\", \"9q\", \n\"SU\", \"F4\", \"4KI\", \"7nK\", \"oQ\", \"z0\", \"43E\", \"6Bn\", \"6lB\", \"4Ya\", \"0bO\", \"2Wl\", \"LM\", \"8fg\", \"4TQ\", \"4A0\", \"aeL\", \"cPo\", \"0AS\", \"ba\", \n\"3kP\", \"0NR\", \"41t\", \"544\", \"4N1\", \"5Kq\", \"14W\", \"CL\", \"2Xm\", \"0mN\", \"5FA\", \"6cC\", \"6Mo\", \"4xL\", \"19\", \"22I\", \"k3\", \"2hC\", \"6Sm\", \"4fN\", \n\"4Hb\", \"69I\", \"2Fo\", \"07D\", \"83l\", \"d5d\", \"4P3\", \"4ER\", \"bQM\", \"a0G\", \"7j\", \"0PP\", \"1Op\", \"8k\", \"hbw\", \"50V\", \"4JS\", \"7oQ\", \"RO\", \"05u\", \n\"08E\", \"2In\", \"66H\", \"4Gc\", \"4iO\", \"7LM\", \"qS\", \"d2\", \"0ay\", \"BK\", \"4O6\", \"4ZW\", \"40s\", \"553\", \"lg\", \"0OU\", \"t6\", \"aW\", \"6Lh\", \"4yK\", \n\"4Wg\", \"6bD\", \"2Yj\", \"0lI\", \"0cH\", \"2Vk\", \"6mE\", \"4Xf\", \"42B\", \"6Ci\", \"nV\", \"0Md\", \"1Pu\", \"cf\", \"6NY\", \"bAI\", \"4UV\", \"7pT\", \"MJ\", \"0nx\", \n\"SH\", \"04r\", \"4KT\", \"7nV\", \"azI\", \"4ex\", \"1Nw\", \"9l\", \"pT\", \"e5\", \"4hH\", \"7MJ\", \"67O\", \"4Fd\", \"09B\", \"2Hi\", \"Qy\", \"06C\", \"4Ie\", \"68N\", \n\"6Rj\", \"4gI\", \"j4\", \"2iD\", \"6m\", \"0QW\", \"4jy\", \"5o9\", \"4Q4\", \"4DU\", \"1oZ\", \"2JX\", \"4m0\", \"4xQ\", \"8Jg\", \"22T\", \"Na\", \"0mS\", \"627\", \"77w\", \n\"6nn\", \"5Kl\", \"V0\", \"CQ\", \"3kM\", \"0NO\", \"41i\", \"7Pc\", \"6OC\", \"5jA\", \"0AN\", \"20e\", \"LP\", \"Y1\", \"4TL\", \"6ao\", \"78v\", \"bcO\", \"0bR\", \"0w3\", \n\"oL\", \"8Ef\", \"43X\", \"4b1\", \"4iR\", \"7LP\", \"5F\", \"266\", \"08X\", \"0i1\", \"66U\", \"b9E\", \"4JN\", \"7oL\", \"RR\", \"G3\", \"1Om\", \"8v\", \"6QA\", \"4db\", \n\"4kc\", \"7Na\", \"7w\", \"0PM\", \"H2\", \"2KB\", \"64d\", \"4EO\", \"b6D\", \"69T\", \"Pc\", \"07Y\", \"297\", \"dRm\", \"4s2\", \"4fS\", \"40w\", \"557\", \"lc\", \"0OQ\", \n\"15T\", \"BO\", \"4O2\", \"4ZS\", \"4Wc\", \"76i\", \"2Yn\", \"0lM\", \"t2\", \"aS\", \"6Ll\", \"4yO\", \"42F\", \"6Cm\", \"nR\", \"8Dx\", \"0cL\", \"2Vo\", \"6mA\", \"4Xb\", \n\"4UR\", \"74X\", \"MN\", \"8gd\", \"1Pq\", \"cb\", \"adO\", \"bAM\", \"azM\", \"51U\", \"1Ns\", \"9h\", \"SL\", \"04v\", \"4KP\", \"7nR\", \"67K\", \"5VA\", \"09F\", \"2Hm\", \n\"4X\", \"e1\", \"4hL\", \"7MN\", \"6Rn\", \"4gM\", \"j0\", \"3ya\", \"2Gl\", \"06G\", \"4Ia\", \"68J\", \"4Q0\", \"4DQ\", \"82o\", \"d4g\", \"6i\", \"0QS\", \"bPN\", \"a1D\", \n\"Ne\", \"0mW\", \"4Vy\", \"5S9\", \"4m4\", \"4xU\", \"1SZ\", \"22P\", \"my\", \"0NK\", \"41m\", \"7Pg\", \"6nj\", \"5Kh\", \"V4\", \"CU\", \"LT\", \"Y5\", \"4TH\", \"6ak\", \n\"6OG\", \"4zd\", \"0AJ\", \"bx\", \"oH\", \"0Lz\", \"4wT\", \"4b5\", \"78r\", \"4Yx\", \"0bV\", \"Ad\", \"1lu\", \"0i5\", \"66Q\", \"4Gz\", \"4iV\", \"7LT\", \"5B\", \"0Rx\", \n\"1Oi\", \"8r\", \"6QE\", \"4df\", \"4JJ\", \"7oH\", \"RV\", \"G7\", \"H6\", \"2KF\", \"6ph\", \"4EK\", \"4kg\", \"7Ne\", \"7s\", \"0PI\", \"1MX\", \"1X9\", \"4s6\", \"4fW\", \n\"5XZ\", \"69P\", \"Pg\", \"0sU\", \"06\", \"23F\", \"afr\", \"4yC\", \"4Wo\", \"6bL\", \"Os\", \"0lA\", \"0aq\", \"BC\", \"aEn\", \"c4E\", \"4ts\", \"5q3\", \"lo\", \"8FE\", \n\"347\", \"cn\", \"6NQ\", \"5kS\", \"bom\", \"74T\", \"MB\", \"0np\", \"17i\", \"2Vc\", \"6mM\", \"4Xn\", \"42J\", \"6Ca\", \"2xO\", \"0Ml\", \"4T\", \"0Sn\", \"5xa\", \"7MB\", \n\"67G\", \"4Fl\", \"09J\", \"2Ha\", \"1u2\", \"04z\", \"b5g\", \"aTm\", \"6PS\", \"4ep\", \"8WF\", \"9d\", \"6e\", \"8XG\", \"4jq\", \"5o1\", \"65v\", \"706\", \"1oR\", \"1z3\", \n\"Qq\", \"06K\", \"4Im\", \"68F\", \"6Rb\", \"4gA\", \"1LN\", \"2iL\", \"6nf\", \"5Kd\", \"V8\", \"CY\", \"mu\", \"0NG\", \"41a\", \"7Pk\", \"4m8\", \"4xY\", \"0Cw\", \"1F7\", \n\"Ni\", \"19r\", \"4Vu\", \"5S5\", \"6lW\", \"4Yt\", \"0bZ\", \"Ah\", \"oD\", \"0Lv\", \"43P\", \"4b9\", \"6OK\", \"4zh\", \"0AF\", \"bt\", \"LX\", \"Y9\", \"4TD\", \"6ag\", \n\"4JF\", \"7oD\", \"RZ\", \"0qh\", \"1Oe\", \"2jg\", \"6QI\", \"4dj\", \"4iZ\", \"483\", \"5N\", \"0Rt\", \"08P\", \"0i9\", \"5B6\", \"4Gv\", \"4Hw\", \"5M7\", \"Pk\", \"07Q\", \n\"1MT\", \"1X5\", \"472\", \"52r\", \"4kk\", \"7Ni\", \"sw\", \"0PE\", \"1nH\", \"2KJ\", \"64l\", \"4EG\", \"4Wk\", \"6bH\", \"Ow\", \"0lE\", \"02\", \"23B\", \"6Ld\", \"4yG\", \n\"4tw\", \"5q7\", \"lk\", \"0OY\", \"0au\", \"BG\", \"6ox\", \"5Jz\", \"4UZ\", \"74P\", \"MF\", \"0nt\", \"1Py\", \"cj\", \"6NU\", \"5kW\", \"42N\", \"6Ce\", \"nZ\", \"0Mh\", \n\"0cD\", \"2Vg\", \"6mI\", \"4Xj\", \"67C\", \"4Fh\", \"09N\", \"2He\", \"4P\", \"e9\", \"4hD\", \"7MF\", \"6PW\", \"4et\", \"3n9\", \"2ky\", \"SD\", \"0pv\", \"4KX\", \"7nZ\", \n\"4Q8\", \"4DY\", \"1oV\", \"1z7\", \"6a\", \"1Az\", \"4ju\", \"5o5\", \"6Rf\", \"4gE\", \"j8\", \"2iH\", \"Qu\", \"06O\", \"4Ii\", \"68B\", \"mq\", \"0NC\", \"41e\", \"7Po\", \n\"6nb\", \"bar\", \"14F\", \"2UL\", \"Nm\", \"19v\", \"4Vq\", \"5S1\", \"agl\", \"bBn\", \"0Cs\", \"1F3\", \"1I2\", \"0Lr\", \"43T\", \"ahm\", \"6lS\", \"4Yp\", \"16w\", \"Al\", \n\"2ZM\", \"0on\", \"5Da\", \"6ac\", \"6OO\", \"4zl\", \"0AB\", \"bp\", \"1Oa\", \"8z\", \"6QM\", \"4dn\", \"4JB\", \"aUs\", \"2DO\", \"05d\", \"08T\", \"d7D\", \"5B2\", \"4Gr\", \n\"bSm\", \"487\", \"5J\", \"0Rp\", \"1MP\", \"1X1\", \"476\", \"52v\", \"4Hs\", \"5M3\", \"Po\", \"07U\", \"1nL\", \"2KN\", \"64h\", \"4EC\", \"4ko\", \"7Nm\", \"ss\", \"0PA\", \n\"QJ\", \"06p\", \"4IV\", \"7lT\", \"6RY\", \"4gz\", \"1Lu\", \"0I5\", \"rV\", \"g7\", \"4jJ\", \"7OH\", \"65M\", \"4Df\", \"1oi\", \"2Jk\", \"2Ej\", \"04A\", \"4Kg\", \"7ne\", \n\"6Ph\", \"4eK\", \"h6\", \"2kF\", \"4o\", \"0SU\", \"5xZ\", \"7My\", \"4S6\", \"4FW\", \"09q\", \"1x9\", \"17R\", \"2VX\", \"4M4\", \"4XU\", \"42q\", \"571\", \"ne\", \"0MW\", \n\"v4\", \"cU\", \"6Nj\", \"5kh\", \"4Ue\", \"74o\", \"My\", \"0nK\", \"0aJ\", \"Bx\", \"6oG\", \"4Zd\", \"4tH\", \"6Ak\", \"lT\", \"y5\", \"0BV\", \"ad\", \"580\", \"4yx\", \n\"4WT\", \"4B5\", \"OH\", \"0lz\", \"4kP\", \"7NR\", \"7D\", \"244\", \"1ns\", \"0k3\", \"64W\", \"con\", \"4HL\", \"69g\", \"PP\", \"E1\", \"1Mo\", \"2hm\", \"6SC\", \"52I\", \n\"4ia\", \"7Lc\", \"5u\", \"0RO\", \"J0\", \"3Ya\", \"66f\", \"4GM\", \"b4F\", \"aUL\", \"Ra\", \"0qS\", \"8Vg\", \"8E\", \"458\", \"4dQ\", \"4o2\", \"4zS\", \"8He\", \"bO\", \n\"Lc\", \"0oQ\", \"605\", \"75u\", \"6ll\", \"4YO\", \"T2\", \"AS\", \"2yn\", \"0LM\", \"43k\", \"7Ra\", \"6MA\", \"4xb\", \"0CL\", \"22g\", \"NR\", \"19I\", \"4VN\", \"6cm\", \n\"aDO\", \"baM\", \"14y\", \"Cb\", \"mN\", \"8Gd\", \"41Z\", \"7PP\", \"axO\", \"53W\", \"1Lq\", \"0I1\", \"QN\", \"06t\", \"4IR\", \"68y\", \"65I\", \"4Db\", \"1om\", \"2Jo\", \n\"6Z\", \"g3\", \"4jN\", \"7OL\", \"6Pl\", \"4eO\", \"h2\", \"2kB\", \"2En\", \"04E\", \"4Kc\", \"7na\", \"4S2\", \"4FS\", \"09u\", \"d6e\", \"4k\", \"0SQ\", \"bRL\", \"a3F\", \n\"42u\", \"575\", \"na\", \"0MS\", \"17V\", \"dlo\", \"4M0\", \"4XQ\", \"4Ua\", \"74k\", \"3KM\", \"0nO\", \"28\", \"cQ\", \"6Nn\", \"5kl\", \"40D\", \"6Ao\", \"lP\", \"y1\", \n\"0aN\", \"2Tm\", \"6oC\", \"5JA\", \"4WP\", \"4B1\", \"OL\", \"18W\", \"0BR\", \"0W3\", \"584\", \"bCO\", \"1nw\", \"0k7\", \"64S\", \"4Ex\", \"4kT\", \"7NV\", \"sH\", \"0Pz\", \n\"1Mk\", \"2hi\", \"6SG\", \"4fd\", \"4HH\", \"69c\", \"PT\", \"E5\", \"J4\", \"2ID\", \"66b\", \"4GI\", \"4ie\", \"7Lg\", \"5q\", \"0RK\", \"1OZ\", \"8A\", \"4q4\", \"4dU\", \n\"4Jy\", \"5O9\", \"Re\", \"0qW\", \"Lg\", \"0oU\", \"5DZ\", \"6aX\", \"4o6\", \"4zW\", \"0Ay\", \"bK\", \"2yj\", \"0LI\", \"43o\", \"6BD\", \"6lh\", \"4YK\", \"T6\", \"AW\", \n\"NV\", \"0md\", \"4VJ\", \"6ci\", \"6ME\", \"4xf\", \"0CH\", \"22c\", \"mJ\", \"0Nx\", \"4uV\", \"7PT\", \"6nY\", \"baI\", \"1pu\", \"Cf\", \"6V\", \"0Ql\", \"4jB\", \"aus\", \n\"65E\", \"4Dn\", \"1oa\", \"2Jc\", \"QB\", \"06x\", \"b7e\", \"68u\", \"5b2\", \"4gr\", \"8UD\", \"dSL\", \"4g\", \"8ZE\", \"4hs\", \"5m3\", \"67t\", \"724\", \"09y\", \"1x1\", \n\"Ss\", \"04I\", \"4Ko\", \"7nm\", \"azr\", \"4eC\", \"1NL\", \"9W\", \"24\", \"21D\", \"6Nb\", \"bAr\", \"4Um\", \"74g\", \"Mq\", \"0nC\", \"0cs\", \"1f3\", \"79W\", \"bbn\", \n\"42y\", \"579\", \"nm\", \"394\", \"365\", \"al\", \"588\", \"4yp\", \"bmo\", \"76V\", \"1i2\", \"0lr\", \"0aB\", \"Bp\", \"6oO\", \"4Zl\", \"40H\", \"6Ac\", \"2zM\", \"0On\", \n\"4HD\", \"69o\", \"PX\", \"E9\", \"1Mg\", \"2he\", \"6SK\", \"4fh\", \"4kX\", \"7NZ\", \"7L\", \"0Pv\", \"3N9\", \"2Ky\", \"6pW\", \"4Et\", \"4Ju\", \"5O5\", \"Ri\", \"05S\", \n\"1OV\", \"8M\", \"450\", \"4dY\", \"4ii\", \"7Lk\", \"qu\", \"0RG\", \"J8\", \"2IH\", \"66n\", \"4GE\", \"6ld\", \"4YG\", \"0bi\", \"2WJ\", \"ow\", \"0LE\", \"43c\", \"6BH\", \n\"6Ox\", \"5jz\", \"0Au\", \"bG\", \"Lk\", \"0oY\", \"4Tw\", \"5Q7\", \"6nU\", \"5KW\", \"14q\", \"Cj\", \"mF\", \"0Nt\", \"41R\", \"7PX\", \"6MI\", \"4xj\", \"0CD\", \"22o\", \n\"NZ\", \"0mh\", \"4VF\", \"6ce\", \"65A\", \"4Dj\", \"1oe\", \"2Jg\", \"6R\", \"0Qh\", \"4jF\", \"7OD\", \"5b6\", \"4gv\", \"1Ly\", \"0I9\", \"QF\", \"0rt\", \"4IZ\", \"68q\", \n\"67p\", \"5Vz\", \"1mT\", \"1x5\", \"4c\", \"0SY\", \"4hw\", \"5m7\", \"6Pd\", \"4eG\", \"1NH\", \"9S\", \"Sw\", \"04M\", \"4Kk\", \"7ni\", \"4Ui\", \"74c\", \"Mu\", \"0nG\", \n\"20\", \"cY\", \"6Nf\", \"5kd\", \"4vu\", \"5s5\", \"ni\", \"390\", \"0cw\", \"1f7\", \"4M8\", \"4XY\", \"4WX\", \"4B9\", \"OD\", \"0lv\", \"0BZ\", \"ah\", \"6LW\", \"4yt\", \n\"40L\", \"6Ag\", \"lX\", \"y9\", \"0aF\", \"Bt\", \"6oK\", \"4Zh\", \"1Mc\", \"2ha\", \"6SO\", \"4fl\", \"5Xa\", \"69k\", \"2FM\", \"07f\", \"83N\", \"d5F\", \"6pS\", \"4Ep\", \n\"bQo\", \"a0e\", \"7H\", \"0Pr\", \"1OR\", \"8I\", \"454\", \"50t\", \"4Jq\", \"5O1\", \"Rm\", \"05W\", \"08g\", \"2IL\", \"66j\", \"4GA\", \"4im\", \"7Lo\", \"5y\", \"0RC\", \n\"os\", \"0LA\", \"43g\", \"6BL\", \"78I\", \"4YC\", \"0bm\", \"2WN\", \"Lo\", \"8fE\", \"4Ts\", \"5Q3\", \"aen\", \"cPM\", \"0Aq\", \"bC\", \"mB\", \"0Np\", \"41V\", \"ajo\", \n\"6nQ\", \"5KS\", \"14u\", \"Cn\", \"2XO\", \"0ml\", \"4VB\", \"6ca\", \"6MM\", \"4xn\", \"1Sa\", \"22k\", \"Sj\", \"04P\", \"4Kv\", \"5N6\", \"443\", \"4eZ\", \"1NU\", \"9N\", \n\"pv\", \"0SD\", \"4hj\", \"7Mh\", \"67m\", \"4FF\", \"1mI\", \"2HK\", \"2GJ\", \"2\", \"4IG\", \"68l\", \"6RH\", \"4gk\", \"1Ld\", \"2if\", \"6O\", \"0Qu\", \"5zz\", \"7OY\", \n\"5A7\", \"4Dw\", \"1ox\", \"0j8\", \"15r\", \"Bi\", \"6oV\", \"4Zu\", \"40Q\", \"4a8\", \"lE\", \"0Ow\", \"0BG\", \"au\", \"6LJ\", \"4yi\", \"4WE\", \"6bf\", \"OY\", \"Z8\", \n\"U9\", \"2VI\", \"6mg\", \"4XD\", \"4vh\", \"6CK\", \"nt\", \"0MF\", \"1PW\", \"cD\", \"4n9\", \"5ky\", \"4Ut\", \"5P4\", \"Mh\", \"0nZ\", \"4ip\", \"5l0\", \"5d\", \"9Kg\", \n\"08z\", \"1y2\", \"66w\", \"737\", \"4Jl\", \"7on\", \"Rp\", \"05J\", \"1OO\", \"8T\", \"6Qc\", \"50i\", \"4kA\", \"7NC\", \"7U\", \"0Po\", \"1nb\", \"dqS\", \"64F\", \"4Em\", \n\"b6f\", \"69v\", \"PA\", \"0ss\", \"8TG\", \"dRO\", \"5c1\", \"4fq\", \"6MP\", \"4xs\", \"376\", \"22v\", \"NC\", \"0mq\", \"bll\", \"77U\", \"6nL\", \"5KN\", \"14h\", \"Cs\", \n\"3ko\", \"0Nm\", \"41K\", \"7PA\", \"6Oa\", \"4zB\", \"37\", \"20G\", \"Lr\", \"8fX\", \"4Tn\", \"6aM\", \"78T\", \"bcm\", \"0bp\", \"AB\", \"on\", \"387\", \"43z\", \"5r2\", \n\"447\", \"51w\", \"1NQ\", \"9J\", \"Sn\", \"04T\", \"4Kr\", \"5N2\", \"67i\", \"4FB\", \"09d\", \"2HO\", \"4z\", \"1Ca\", \"4hn\", \"7Ml\", \"6RL\", \"4go\", \"8UY\", \"2ib\", \n\"2GN\", \"6\", \"4IC\", \"68h\", \"5A3\", \"4Ds\", \"82M\", \"d4E\", \"6K\", \"0Qq\", \"bPl\", \"a1f\", \"40U\", \"akl\", \"lA\", \"0Os\", \"15v\", \"Bm\", \"6oR\", \"4Zq\", \n\"4WA\", \"6bb\", \"2YL\", \"0lo\", \"0BC\", \"aq\", \"6LN\", \"4ym\", \"42d\", \"6CO\", \"np\", \"0MB\", \"0cn\", \"2VM\", \"6mc\", \"5Ha\", \"4Up\", \"5P0\", \"Ml\", \"8gF\", \n\"1PS\", \"1E2\", \"adm\", \"bAo\", \"1lW\", \"1y6\", \"4R9\", \"4GX\", \"4it\", \"5l4\", \"qh\", \"0RZ\", \"i9\", \"8P\", \"6Qg\", \"4dD\", \"4Jh\", \"7oj\", \"Rt\", \"05N\", \n\"1nf\", \"2Kd\", \"64B\", \"4Ei\", \"4kE\", \"7NG\", \"7Q\", \"f8\", \"1Mz\", \"2hx\", \"5c5\", \"4fu\", \"4HY\", \"69r\", \"PE\", \"0sw\", \"NG\", \"0mu\", \"5Fz\", \"6cx\", \n\"6MT\", \"4xw\", \"0CY\", \"0V8\", \"3kk\", \"0Ni\", \"41O\", \"7PE\", \"6nH\", \"5KJ\", \"14l\", \"Cw\", \"Lv\", \"0oD\", \"4Tj\", \"6aI\", \"6Oe\", \"4zF\", \"33\", \"bZ\", \n\"oj\", \"0LX\", \"4wv\", \"5r6\", \"6ly\", \"4YZ\", \"0bt\", \"AF\", \"4v\", \"0SL\", \"4hb\", \"awS\", \"67e\", \"4FN\", \"K3\", \"2HC\", \"Sb\", \"04X\", \"b5E\", \"aTO\", \n\"4p3\", \"4eR\", \"8Wd\", \"9F\", \"6G\", \"257\", \"4jS\", \"7OQ\", \"65T\", \"cnm\", \"1op\", \"0j0\", \"QS\", \"D2\", \"4IO\", \"68d\", \"7Ba\", \"4gc\", \"1Ll\", \"2in\", \n\"0BO\", \"23d\", \"6LB\", \"4ya\", \"4WM\", \"6bn\", \"OQ\", \"Z0\", \"0aS\", \"Ba\", \"aEL\", \"c4g\", \"40Y\", \"4a0\", \"lM\", \"8Fg\", \"8If\", \"cL\", \"4n1\", \"5kq\", \n\"616\", \"74v\", \"3KP\", \"0nR\", \"U1\", \"2VA\", \"6mo\", \"4XL\", \"42h\", \"6CC\", \"2xm\", \"0MN\", \"4Jd\", \"7of\", \"Rx\", \"05B\", \"i5\", \"2jE\", \"6Qk\", \"4dH\", \n\"4ix\", \"5l8\", \"5l\", \"0RV\", \"08r\", \"2IY\", \"4R5\", \"4GT\", \"4HU\", \"7mW\", \"PI\", \"07s\", \"1Mv\", \"0H6\", \"5c9\", \"4fy\", \"4kI\", \"7NK\", \"sU\", \"f4\", \n\"1nj\", \"2Kh\", \"64N\", \"4Ee\", \"6nD\", \"5KF\", \"1ph\", \"2Uj\", \"mW\", \"x6\", \"41C\", \"7PI\", \"593\", \"5hZ\", \"0CU\", \"0V4\", \"NK\", \"0my\", \"4VW\", \"4C6\", \n\"4L7\", \"4YV\", \"0bx\", \"AJ\", \"of\", \"0LT\", \"43r\", \"562\", \"6Oi\", \"4zJ\", \"w7\", \"bV\", \"Lz\", \"0oH\", \"4Tf\", \"6aE\", \"67a\", \"4FJ\", \"K7\", \"2HG\", \n\"4r\", \"0SH\", \"4hf\", \"7Md\", \"4p7\", \"4eV\", \"1NY\", \"9B\", \"Sf\", \"0pT\", \"4Kz\", \"7nx\", \"65P\", \"5TZ\", \"1ot\", \"0j4\", \"6C\", \"0Qy\", \"4jW\", \"7OU\", \n\"6RD\", \"4gg\", \"1Lh\", \"2ij\", \"QW\", \"D6\", \"4IK\", \"7lI\", \"4WI\", \"6bj\", \"OU\", \"Z4\", \"0BK\", \"ay\", \"6LF\", \"4ye\", \"4tU\", \"4a4\", \"lI\", \"2o9\", \n\"0aW\", \"Be\", \"6oZ\", \"4Zy\", \"4Ux\", \"5P8\", \"Md\", \"0nV\", \"8Ib\", \"cH\", \"4n5\", \"5ku\", \"42l\", \"6CG\", \"nx\", \"0MJ\", \"U5\", \"2VE\", \"6mk\", \"4XH\", \n\"i1\", \"8X\", \"6Qo\", \"4dL\", \"5ZA\", \"7ob\", \"2Dm\", \"05F\", \"08v\", \"d7f\", \"4R1\", \"4GP\", \"bSO\", \"a2E\", \"5h\", \"0RR\", \"1Mr\", \"0H2\", \"ayL\", \"52T\", \n\"4HQ\", \"69z\", \"PM\", \"07w\", \"1nn\", \"2Kl\", \"64J\", \"4Ea\", \"4kM\", \"7NO\", \"7Y\", \"f0\", \"mS\", \"x2\", \"41G\", \"7PM\", \"aDR\", \"5KB\", \"14d\", \"2Un\", \n\"NO\", \"19T\", \"4VS\", \"4C2\", \"597\", \"bBL\", \"0CQ\", \"0V0\", \"ob\", \"0LP\", \"43v\", \"566\", \"4L3\", \"4YR\", \"16U\", \"AN\", \"2Zo\", \"0oL\", \"4Tb\", \"6aA\", \n\"6Om\", \"4zN\", \"w3\", \"bR\", \"4oT\", \"4z5\", \"wH\", \"0Tz\", \"0zV\", \"Yd\", \"5D8\", \"4Ax\", \"4LH\", \"6yk\", \"TT\", \"A5\", \"0YJ\", \"zx\", \"6WG\", \"4bd\", \n\"4me\", \"6XF\", \"1q\", \"0VK\", \"N4\", \"2MD\", \"62b\", \"4CI\", \"4Ny\", \"5K9\", \"Ve\", \"0uW\", \"1KZ\", \"xI\", \"4u4\", \"5pt\", \"4k6\", \"5nv\", \"0Ey\", \"fK\", \n\"Hg\", \"0kU\", \"641\", \"6eX\", \"6hh\", \"5Mj\", \"P6\", \"EW\", \"29b\", \"0HI\", \"47o\", \"6FD\", \"6IE\", \"48n\", \"0GH\", \"dz\", \"JV\", \"0id\", \"4RJ\", \"6gi\", \n\"6jY\", \"beI\", \"0dT\", \"Gf\", \"iJ\", \"0Jx\", \"4qV\", \"4d7\", \"UN\", \"02t\", \"4MR\", \"4X3\", \"a8G\", \"57W\", \"0XP\", \"0M1\", \"2Z\", \"c3\", \"4nN\", \"7KL\", \n\"61I\", \"5PC\", \"1km\", \"2No\", \"2An\", \"00E\", \"4Oc\", \"7ja\", \"6Tl\", \"4aO\", \"l2\", \"yS\", \"0k\", \"0WQ\", \"58V\", \"a7F\", \"4W2\", \"4BS\", \"84m\", \"ZO\", \n\"13V\", \"E\", \"4I0\", \"5Lp\", \"46u\", \"535\", \"ja\", \"0IS\", \"68\", \"gQ\", \"6Jn\", \"5ol\", \"4Qa\", \"6dB\", \"3OM\", \"0jO\", \"0eN\", \"2Pm\", \"6kC\", \"5NA\", \n\"44D\", \"6Eo\", \"hP\", \"99\", \"0FR\", \"0S3\", \"abM\", \"49t\", \"4SP\", \"4F1\", \"KL\", \"8af\", \"0zR\", \"0o3\", \"60W\", \"ckn\", \"4oP\", \"4z1\", \"3D\", \"204\", \n\"0YN\", \"2lm\", \"6WC\", \"56I\", \"4LL\", \"6yo\", \"TP\", \"A1\", \"N0\", \"903\", \"62f\", \"4CM\", \"4ma\", \"6XB\", \"1u\", \"0VO\", \"8Rg\", \"xM\", \"418\", \"54x\", \n\"b0F\", \"aQL\", \"Va\", \"0uS\", \"Hc\", \"0kQ\", \"645\", \"71u\", \"4k2\", \"5nr\", \"8Le\", \"fO\", \"29f\", \"0HM\", \"47k\", \"7Va\", \"6hl\", \"5Mn\", \"P2\", \"ES\", \n\"JR\", \"1yA\", \"4RN\", \"6gm\", \"6IA\", \"48j\", \"0GL\", \"26g\", \"iN\", \"8Cd\", \"45Z\", \"4d3\", \"hYv\", \"beM\", \"0dP\", \"Gb\", \"6VY\", \"4cz\", \"0XT\", \"0M5\", \n\"UJ\", \"02p\", \"4MV\", \"4X7\", \"61M\", \"5PG\", \"1ki\", \"Xz\", \"vV\", \"c7\", \"4nJ\", \"7KH\", \"6Th\", \"4aK\", \"l6\", \"yW\", \"2Aj\", \"00A\", \"4Og\", \"6zD\", \n\"4W6\", \"4BW\", \"0yy\", \"ZK\", \"0o\", \"0WU\", \"58R\", \"6YX\", \"46q\", \"531\", \"je\", \"0IW\", \"13R\", \"A\", \"4I4\", \"5Lt\", \"4Qe\", \"6dF\", \"Iy\", \"0jK\", \n\"r4\", \"gU\", \"6Jj\", \"5oh\", \"4pH\", \"6Ek\", \"hT\", \"0Kf\", \"0eJ\", \"Fx\", \"6kG\", \"5NE\", \"4ST\", \"4F5\", \"KH\", \"0hz\", \"0FV\", \"ed\", \"5x8\", \"49p\", \n\"bvs\", \"6yc\", \"2BM\", \"03f\", \"0YB\", \"zp\", \"6WO\", \"4bl\", \"bUo\", \"a4e\", \"3H\", \"0Tr\", \"87N\", \"Yl\", \"5D0\", \"4Ap\", \"4Nq\", \"5K1\", \"Vm\", \"01W\", \n\"1KR\", \"xA\", \"414\", \"54t\", \"4mm\", \"6XN\", \"1y\", \"0VC\", \"0xo\", \"2ML\", \"62j\", \"4CA\", \"7xA\", \"5Mb\", \"0fm\", \"2SN\", \"ks\", \"0HA\", \"47g\", \"6FL\", \n\"aan\", \"bDl\", \"0Eq\", \"fC\", \"Ho\", \"8bE\", \"4Ps\", \"5U3\", \"5Z2\", \"5OS\", \"10u\", \"Gn\", \"iB\", \"0Jp\", \"45V\", \"ano\", \"6IM\", \"48f\", \"1Wa\", \"dr\", \n\"3Ln\", \"0il\", \"4RB\", \"6ga\", \"2R\", \"0Uh\", \"4nF\", \"7KD\", \"61A\", \"5PK\", \"1ke\", \"Xv\", \"UF\", \"0vt\", \"4MZ\", \"6xy\", \"5f6\", \"4cv\", \"0XX\", \"0M9\", \n\"0c\", \"0WY\", \"4lw\", \"5i7\", \"63p\", \"5Rz\", \"0yu\", \"ZG\", \"Ww\", \"00M\", \"4Ok\", \"6zH\", \"6Td\", \"4aG\", \"0Zi\", \"2oJ\", \"60\", \"gY\", \"6Jf\", \"5od\", \n\"4Qi\", \"6dJ\", \"Iu\", \"0jG\", \"0gw\", \"M\", \"4I8\", \"5Lx\", \"4ru\", \"5w5\", \"ji\", \"1Yz\", \"0FZ\", \"eh\", \"5x4\", \"5mU\", \"4SX\", \"4F9\", \"KD\", \"0hv\", \n\"0eF\", \"Ft\", \"6kK\", \"5NI\", \"44L\", \"6Eg\", \"hX\", \"91\", \"0YF\", \"zt\", \"6WK\", \"4bh\", \"4LD\", \"6yg\", \"TX\", \"A9\", \"0zZ\", \"Yh\", \"5D4\", \"4At\", \n\"4oX\", \"4z9\", \"3L\", \"0Tv\", \"1KV\", \"xE\", \"410\", \"54p\", \"4Nu\", \"5K5\", \"Vi\", \"01S\", \"N8\", \"2MH\", \"62n\", \"4CE\", \"4mi\", \"6XJ\", \"uu\", \"0VG\", \n\"kw\", \"0HE\", \"47c\", \"6FH\", \"6hd\", \"5Mf\", \"0fi\", \"2SJ\", \"Hk\", \"0kY\", \"4Pw\", \"5U7\", \"6Kx\", \"5nz\", \"0Eu\", \"fG\", \"iF\", \"0Jt\", \"45R\", \"6Dy\", \n\"5Z6\", \"5OW\", \"0dX\", \"Gj\", \"JZ\", \"0ih\", \"4RF\", \"6ge\", \"6II\", \"48b\", \"0GD\", \"dv\", \"61E\", \"5PO\", \"1ka\", \"Xr\", \"2V\", \"0Ul\", \"4nB\", \"aqs\", \n\"5f2\", \"4cr\", \"8QD\", \"39V\", \"UB\", \"02x\", \"795\", \"aRo\", \"63t\", \"764\", \"0yq\", \"ZC\", \"0g\", \"9Nd\", \"4ls\", \"5i3\", \"7DA\", \"4aC\", \"0Zm\", \"2oN\", \n\"Ws\", \"00I\", \"4Oo\", \"6zL\", \"4Qm\", \"6dN\", \"Iq\", \"0jC\", \"64\", \"25D\", \"6Jb\", \"bEr\", \"46y\", \"539\", \"jm\", \"9Pf\", \"0gs\", \"I\", \"aCl\", \"bfn\", \n\"bio\", \"72V\", \"1m2\", \"0hr\", \"325\", \"el\", \"5x0\", \"49x\", \"44H\", \"6Ec\", \"3nl\", \"95\", \"0eB\", \"Fp\", \"6kO\", \"5NM\", \"4mt\", \"5h4\", \"uh\", \"0VZ\", \n\"0xv\", \"2MU\", \"4V9\", \"4CX\", \"4Nh\", \"7kj\", \"Vt\", \"01N\", \"m9\", \"xX\", \"6Ug\", \"54m\", \"4oE\", \"6Zf\", \"3Q\", \"b8\", \"0zG\", \"Yu\", \"60B\", \"4Ai\", \n\"4LY\", \"4Y8\", \"TE\", \"0ww\", \"1Iz\", \"zi\", \"5g5\", \"4bu\", \"5y7\", \"5lV\", \"0GY\", \"dk\", \"JG\", \"0iu\", \"5Bz\", \"6gx\", \"6jH\", \"5OJ\", \"0dE\", \"Gw\", \n\"3ok\", \"82\", \"45O\", \"6Dd\", \"6Ke\", \"5ng\", \"73\", \"fZ\", \"Hv\", \"0kD\", \"4Pj\", \"6eI\", \"6hy\", \"7m9\", \"0ft\", \"EF\", \"kj\", \"0HX\", \"4sv\", \"5v6\", \n\"Wn\", \"00T\", \"4Or\", \"5J2\", \"407\", \"55w\", \"0Zp\", \"yB\", \"0z\", \"1Ga\", \"4ln\", \"6YM\", \"63i\", \"4BB\", \"0yl\", \"2LO\", \"2CN\", \"02e\", \"4MC\", \"7hA\", \n\"6VL\", \"4co\", \"0XA\", \"2mb\", \"2K\", \"0Uq\", \"bTl\", \"a5f\", \"5E3\", \"5PR\", \"86M\", \"Xo\", \"11v\", \"Fm\", \"6kR\", \"5NP\", \"44U\", \"aol\", \"hA\", \"0Ks\", \n\"0FC\", \"eq\", \"6HN\", \"49e\", \"4SA\", \"6fb\", \"3Mm\", \"0ho\", \"0gn\", \"T\", \"6ic\", \"5La\", \"46d\", \"6GO\", \"jp\", \"0IB\", \"0Dr\", \"1A2\", \"hyT\", \"bEo\", \n\"4Qp\", \"5T0\", \"Il\", \"8cF\", \"0xr\", \"2MQ\", \"62w\", \"777\", \"4mp\", \"5h0\", \"1d\", \"9Og\", \"1KO\", \"2nM\", \"6Uc\", \"54i\", \"4Nl\", \"7kn\", \"Vp\", \"01J\", \n\"0zC\", \"Yq\", \"60F\", \"4Am\", \"4oA\", \"6Zb\", \"3U\", \"0To\", \"8PG\", \"zm\", \"5g1\", \"4bq\", \"786\", \"aSl\", \"TA\", \"0ws\", \"JC\", \"0iq\", \"bhl\", \"73U\", \n\"5y3\", \"5lR\", \"336\", \"do\", \"3oo\", \"86\", \"45K\", \"7TA\", \"6jL\", \"5ON\", \"0dA\", \"Gs\", \"Hr\", \"8bX\", \"4Pn\", \"6eM\", \"6Ka\", \"5nc\", \"77\", \"24G\", \n\"kn\", \"8AD\", \"47z\", \"5v2\", \"aBo\", \"bgm\", \"0fp\", \"EB\", \"403\", \"4aZ\", \"0Zt\", \"yF\", \"Wj\", \"00P\", \"4Ov\", \"5J6\", \"63m\", \"4BF\", \"0yh\", \"ZZ\", \n\"tv\", \"0WD\", \"4lj\", \"6YI\", \"6VH\", \"4ck\", \"0XE\", \"2mf\", \"2CJ\", \"02a\", \"4MG\", \"6xd\", \"5E7\", \"5PV\", \"1kx\", \"Xk\", \"2O\", \"0Uu\", \"bTh\", \"7KY\", \n\"44Q\", \"4e8\", \"hE\", \"0Kw\", \"11r\", \"Fi\", \"6kV\", \"5NT\", \"4SE\", \"6ff\", \"KY\", \"0hk\", \"0FG\", \"eu\", \"6HJ\", \"49a\", \"4rh\", \"6GK\", \"jt\", \"0IF\", \n\"Q9\", \"P\", \"6ig\", \"5Le\", \"4Qt\", \"5T4\", \"Ih\", \"0jZ\", \"0Dv\", \"gD\", \"4j9\", \"5oy\", \"aD0\", \"7kb\", \"3PL\", \"01F\", \"m1\", \"xP\", \"6Uo\", \"54e\", \n\"59U\", \"a6E\", \"1h\", \"0VR\", \"85n\", \"196\", \"4V1\", \"4CP\", \"4LQ\", \"4Y0\", \"TM\", \"03w\", \"0YS\", \"za\", \"a9D\", \"56T\", \"4oM\", \"6Zn\", \"3Y\", \"b0\", \n\"0zO\", \"2Ol\", \"60J\", \"4Aa\", \"7za\", \"5OB\", \"0dM\", \"2Qn\", \"iS\", \"0Ja\", \"45G\", \"6Dl\", \"acN\", \"48w\", \"0GQ\", \"dc\", \"JO\", \"94L\", \"4RS\", \"4G2\", \n\"4H3\", \"5Ms\", \"12U\", \"EN\", \"kb\", \"0HP\", \"47v\", \"526\", \"6Km\", \"5no\", \"s3\", \"fR\", \"3NN\", \"0kL\", \"4Pb\", \"6eA\", \"0r\", \"0WH\", \"4lf\", \"6YE\", \n\"63a\", \"4BJ\", \"O7\", \"ZV\", \"Wf\", \"0tT\", \"4Oz\", \"6zY\", \"4t7\", \"4aV\", \"0Zx\", \"yJ\", \"2C\", \"0Uy\", \"4nW\", \"7KU\", \"61P\", \"5PZ\", \"1kt\", \"Xg\", \n\"UW\", \"02m\", \"4MK\", \"6xh\", \"6VD\", \"4cg\", \"0XI\", \"2mj\", \"0FK\", \"ey\", \"6HF\", \"49m\", \"4SI\", \"6fj\", \"KU\", \"0hg\", \"0eW\", \"Fe\", \"6kZ\", \"5NX\", \n\"4pU\", \"4e4\", \"hI\", \"2k9\", \"0Dz\", \"gH\", \"4j5\", \"5ou\", \"4Qx\", \"5T8\", \"Id\", \"0jV\", \"Q5\", \"DT\", \"6ik\", \"5Li\", \"46l\", \"6GG\", \"jx\", \"0IJ\", \n\"m5\", \"xT\", \"6Uk\", \"54a\", \"4Nd\", \"7kf\", \"Vx\", \"01B\", \"0xz\", \"192\", \"4V5\", \"4CT\", \"4mx\", \"5h8\", \"1l\", \"0VV\", \"0YW\", \"ze\", \"5g9\", \"4by\", \n\"4LU\", \"4Y4\", \"TI\", \"03s\", \"0zK\", \"Yy\", \"60N\", \"4Ae\", \"4oI\", \"6Zj\", \"wU\", \"b4\", \"iW\", \"0Je\", \"45C\", \"6Dh\", \"6jD\", \"5OF\", \"0dI\", \"2Qj\", \n\"JK\", \"0iy\", \"4RW\", \"4G6\", \"6IX\", \"48s\", \"0GU\", \"dg\", \"kf\", \"0HT\", \"47r\", \"522\", \"4H7\", \"5Mw\", \"0fx\", \"EJ\", \"Hz\", \"0kH\", \"4Pf\", \"6eE\", \n\"6Ki\", \"5nk\", \"s7\", \"fV\", \"63e\", \"4BN\", \"O3\", \"ZR\", \"0v\", \"0WL\", \"4lb\", \"6YA\", \"4t3\", \"4aR\", \"8Sd\", \"yN\", \"Wb\", \"00X\", \"b1E\", \"aPO\", \n\"61T\", \"bzL\", \"1kp\", \"Xc\", \"2G\", \"217\", \"4nS\", \"7KQ\", \"7Fa\", \"4cc\", \"0XM\", \"2mn\", \"US\", \"02i\", \"4MO\", \"6xl\", \"4SM\", \"6fn\", \"KQ\", \"0hc\", \n\"0FO\", \"27d\", \"6HB\", \"49i\", \"44Y\", \"4e0\", \"hM\", \"8Bg\", \"0eS\", \"Fa\", \"aAL\", \"bdN\", \"656\", \"70v\", \"3OP\", \"0jR\", \"8Mf\", \"gL\", \"4j1\", \"5oq\", \n\"46h\", \"6GC\", \"28e\", \"0IN\", \"Q1\", \"X\", \"6io\", \"5Lm\", \"6KV\", \"5nT\", \"1Uz\", \"fi\", \"HE\", \"0kw\", \"4PY\", \"4E8\", \"6hJ\", \"5MH\", \"0fG\", \"Eu\", \n\"kY\", \"0Hk\", \"47M\", \"6Ff\", \"6Ig\", \"48L\", \"51\", \"dX\", \"Jt\", \"0iF\", \"4Rh\", \"6gK\", \"4J9\", \"5Oy\", \"0dv\", \"GD\", \"ih\", \"0JZ\", \"4qt\", \"5t4\", \n\"4ov\", \"5j6\", \"3b\", \"0TX\", \"0zt\", \"YF\", \"60q\", \"4AZ\", \"4Lj\", \"6yI\", \"Tv\", \"03L\", \"0Yh\", \"zZ\", \"6We\", \"4bF\", \"4mG\", \"6Xd\", \"1S\", \"0Vi\", \n\"0xE\", \"2Mf\", \"6vH\", \"4Ck\", \"bth\", \"7kY\", \"VG\", \"0uu\", \"1Kx\", \"xk\", \"5e7\", \"5pV\", \"13t\", \"g\", \"5Y3\", \"5LR\", \"46W\", \"amn\", \"jC\", \"0Iq\", \n\"0DA\", \"gs\", \"6JL\", \"5oN\", \"4QC\", \"70I\", \"3Oo\", \"0jm\", \"0el\", \"2PO\", \"6ka\", \"5Nc\", \"44f\", \"6EM\", \"hr\", \"8BX\", \"0Fp\", \"eB\", \"abo\", \"49V\", \n\"4Sr\", \"5V2\", \"Kn\", \"8aD\", \"Ul\", \"02V\", \"4Mp\", \"5H0\", \"425\", \"57u\", \"0Xr\", \"2mQ\", \"2x\", \"0UB\", \"4nl\", \"7Kn\", \"61k\", \"5Pa\", \"1kO\", \"2NM\", \n\"2AL\", \"00g\", \"4OA\", \"6zb\", \"6TN\", \"4am\", \"0ZC\", \"yq\", \"0I\", \"0Ws\", \"58t\", \"a7d\", \"5G1\", \"4Bq\", \"84O\", \"Zm\", \"HA\", \"0ks\", \"bjn\", \"71W\", \n\"6KR\", \"5nP\", \"314\", \"fm\", \"29D\", \"0Ho\", \"47I\", \"6Fb\", \"6hN\", \"5ML\", \"0fC\", \"Eq\", \"Jp\", \"0iB\", \"4Rl\", \"6gO\", \"6Ic\", \"48H\", \"55\", \"26E\", \n\"il\", \"8CF\", \"45x\", \"508\", \"hYT\", \"beo\", \"0dr\", \"1a2\", \"0zp\", \"YB\", \"60u\", \"755\", \"4or\", \"5j2\", \"3f\", \"9Me\", \"0Yl\", \"2lO\", \"6Wa\", \"4bB\", \n\"4Ln\", \"6yM\", \"Tr\", \"03H\", \"0xA\", \"2Mb\", \"62D\", \"4Co\", \"4mC\", \"7HA\", \"1W\", \"0Vm\", \"8RE\", \"xo\", \"5e3\", \"54Z\", \"b0d\", \"aQn\", \"VC\", \"01y\", \n\"46S\", \"6Gx\", \"jG\", \"0Iu\", \"0gY\", \"c\", \"5Y7\", \"5LV\", \"4QG\", \"6dd\", \"3Ok\", \"0ji\", \"0DE\", \"gw\", \"6JH\", \"5oJ\", \"44b\", \"6EI\", \"hv\", \"0KD\", \n\"0eh\", \"FZ\", \"6ke\", \"5Ng\", \"4Sv\", \"5V6\", \"Kj\", \"0hX\", \"0Ft\", \"eF\", \"6Hy\", \"49R\", \"421\", \"4cX\", \"0Xv\", \"2mU\", \"Uh\", \"02R\", \"4Mt\", \"5H4\", \n\"61o\", \"5Pe\", \"M9\", \"XX\", \"vt\", \"0UF\", \"4nh\", \"7Kj\", \"6TJ\", \"4ai\", \"0ZG\", \"yu\", \"WY\", \"B8\", \"4OE\", \"6zf\", \"5G5\", \"4Bu\", \"1iz\", \"Zi\", \n\"0M\", \"0Ww\", \"4lY\", \"4y8\", \"6hB\", \"aW1\", \"0fO\", \"2Sl\", \"kQ\", \"0Hc\", \"47E\", \"6Fn\", \"aaL\", \"bDN\", \"0ES\", \"fa\", \"HM\", \"8bg\", \"4PQ\", \"4E0\", \n\"4J1\", \"5Oq\", \"10W\", \"GL\", \"3oP\", \"0JR\", \"45t\", \"504\", \"6Io\", \"48D\", \"59\", \"dP\", \"3LL\", \"0iN\", \"5BA\", \"6gC\", \"4Lb\", \"6yA\", \"2Bo\", \"03D\", \n\"o3\", \"zR\", \"6Wm\", \"4bN\", \"bUM\", \"a4G\", \"3j\", \"0TP\", \"87l\", \"YN\", \"4T3\", \"4AR\", \"4NS\", \"7kQ\", \"VO\", \"01u\", \"1Kp\", \"xc\", \"hfw\", \"54V\", \n\"4mO\", \"6Xl\", \"uS\", \"0Va\", \"0xM\", \"2Mn\", \"62H\", \"4Cc\", \"0DI\", \"25b\", \"6JD\", \"5oF\", \"4QK\", \"6dh\", \"IW\", \"0je\", \"0gU\", \"o\", \"6iX\", \"5LZ\", \n\"4rW\", \"4g6\", \"jK\", \"0Iy\", \"0Fx\", \"eJ\", \"4h7\", \"5mw\", \"4Sz\", \"6fY\", \"Kf\", \"0hT\", \"S7\", \"FV\", \"6ki\", \"5Nk\", \"44n\", \"6EE\", \"hz\", \"0KH\", \n\"2p\", \"0UJ\", \"4nd\", \"7Kf\", \"61c\", \"5Pi\", \"M5\", \"XT\", \"Ud\", \"0vV\", \"4Mx\", \"5H8\", \"4v5\", \"4cT\", \"0Xz\", \"2mY\", \"0A\", \"1GZ\", \"4lU\", \"4y4\", \n\"5G9\", \"4By\", \"0yW\", \"Ze\", \"WU\", \"B4\", \"4OI\", \"6zj\", \"6TF\", \"4ae\", \"0ZK\", \"yy\", \"kU\", \"0Hg\", \"47A\", \"6Fj\", \"6hF\", \"5MD\", \"0fK\", \"Ey\", \n\"HI\", \"2K9\", \"4PU\", \"4E4\", \"6KZ\", \"5nX\", \"0EW\", \"fe\", \"id\", \"0JV\", \"45p\", \"500\", \"4J5\", \"5Ou\", \"0dz\", \"GH\", \"Jx\", \"0iJ\", \"4Rd\", \"6gG\", \n\"6Ik\", \"5li\", \"q5\", \"dT\", \"o7\", \"zV\", \"6Wi\", \"4bJ\", \"4Lf\", \"6yE\", \"Tz\", \"0wH\", \"0zx\", \"YJ\", \"4T7\", \"4AV\", \"4oz\", \"6ZY\", \"3n\", \"0TT\", \n\"1Kt\", \"xg\", \"6UX\", \"54R\", \"4NW\", \"7kU\", \"VK\", \"01q\", \"0xI\", \"2Mj\", \"62L\", \"4Cg\", \"4mK\", \"6Xh\", \"uW\", \"0Ve\", \"4QO\", \"6dl\", \"IS\", \"0ja\", \n\"0DM\", \"25f\", \"7Za\", \"5oB\", \"4rS\", \"4g2\", \"jO\", \"9PD\", \"0gQ\", \"k\", \"aCN\", \"685\", \"674\", \"72t\", \"Kb\", \"0hP\", \"8Od\", \"eN\", \"4h3\", \"49Z\", \n\"44j\", \"6EA\", \"3nN\", \"0KL\", \"S3\", \"FR\", \"6km\", \"5No\", \"61g\", \"5Pm\", \"M1\", \"XP\", \"2t\", \"0UN\", \"ad0\", \"7Kb\", \"429\", \"4cP\", \"8Qf\", \"39t\", \n\"0c3\", \"02Z\", \"b3G\", \"aRM\", \"63V\", \"bxN\", \"0yS\", \"Za\", \"0E\", \"235\", \"4lQ\", \"4y0\", \"6TB\", \"4aa\", \"0ZO\", \"2ol\", \"WQ\", \"B0\", \"4OM\", \"6zn\", \n\"4i4\", \"5lt\", \"1WZ\", \"dI\", \"Je\", \"0iW\", \"4Ry\", \"5W9\", \"6jj\", \"5Oh\", \"R4\", \"GU\", \"iy\", \"0JK\", \"45m\", \"6DF\", \"6KG\", \"5nE\", \"0EJ\", \"fx\", \n\"HT\", \"0kf\", \"4PH\", \"6ek\", \"5X8\", \"5MY\", \"0fV\", \"Ed\", \"kH\", \"0Hz\", \"4sT\", \"4f5\", \"4mV\", \"4x7\", \"1B\", \"0Vx\", \"0xT\", \"0m5\", \"62Q\", \"4Cz\", \n\"4NJ\", \"7kH\", \"VV\", \"C7\", \"1Ki\", \"xz\", \"6UE\", \"54O\", \"4og\", \"6ZD\", \"3s\", \"0TI\", \"L6\", \"YW\", \"6th\", \"4AK\", \"6l9\", \"6yX\", \"Tg\", \"0wU\", \n\"0Yy\", \"zK\", \"4w6\", \"4bW\", \"11T\", \"FO\", \"4K2\", \"5Nr\", \"44w\", \"517\", \"hc\", \"0KQ\", \"p2\", \"eS\", \"6Hl\", \"49G\", \"4Sc\", \"72i\", \"3MO\", \"0hM\", \n\"0gL\", \"v\", \"6iA\", \"5LC\", \"46F\", \"6Gm\", \"jR\", \"1YA\", \"0DP\", \"gb\", \"hyv\", \"bEM\", \"4QR\", \"4D3\", \"IN\", \"8cd\", \"WL\", \"00v\", \"4OP\", \"4Z1\", \n\"hgt\", \"55U\", \"0ZR\", \"0O3\", \"0X\", \"a1\", \"4lL\", \"6Yo\", \"63K\", \"5RA\", \"0yN\", \"2Lm\", \"2Cl\", \"02G\", \"4Ma\", \"6xB\", \"6Vn\", \"4cM\", \"n0\", \"39i\", \n\"2i\", \"0US\", \"bTN\", \"a5D\", \"4U0\", \"5Pp\", \"86o\", \"XM\", \"Ja\", \"0iS\", \"667\", \"73w\", \"4i0\", \"48Y\", \"8Ng\", \"dM\", \"3oM\", \"0JO\", \"45i\", \"6DB\", \n\"6jn\", \"5Ol\", \"R0\", \"GQ\", \"HP\", \"0kb\", \"4PL\", \"6eo\", \"6KC\", \"5nA\", \"0EN\", \"24e\", \"kL\", \"8Af\", \"47X\", \"4f1\", \"aBM\", \"696\", \"0fR\", \"0s3\", \n\"0xP\", \"0m1\", \"62U\", \"byM\", \"4mR\", \"4x3\", \"1F\", \"226\", \"1Km\", \"2no\", \"6UA\", \"54K\", \"4NN\", \"7kL\", \"VR\", \"C3\", \"L2\", \"YS\", \"60d\", \"4AO\", \n\"4oc\", \"7Ja\", \"3w\", \"0TM\", \"8Pe\", \"zO\", \"4w2\", \"4bS\", \"b2D\", \"aSN\", \"Tc\", \"03Y\", \"44s\", \"513\", \"hg\", \"0KU\", \"0ey\", \"FK\", \"4K6\", \"5Nv\", \n\"4Sg\", \"6fD\", \"3MK\", \"0hI\", \"p6\", \"eW\", \"6Hh\", \"49C\", \"46B\", \"6Gi\", \"jV\", \"0Id\", \"0gH\", \"r\", \"6iE\", \"5LG\", \"4QV\", \"4D7\", \"IJ\", \"0jx\", \n\"0DT\", \"gf\", \"6JY\", \"bEI\", \"5d8\", \"4ax\", \"0ZV\", \"yd\", \"WH\", \"00r\", \"4OT\", \"4Z5\", \"63O\", \"4Bd\", \"0yJ\", \"Zx\", \"tT\", \"a5\", \"4lH\", \"6Yk\", \n\"6Vj\", \"4cI\", \"n4\", \"2mD\", \"Uy\", \"02C\", \"4Me\", \"6xF\", \"4U4\", \"5Pt\", \"1kZ\", \"XI\", \"2m\", \"0UW\", \"4ny\", \"5k9\", \"6jb\", \"ber\", \"0do\", \"2QL\", \n\"iq\", \"0JC\", \"45e\", \"6DN\", \"acl\", \"48U\", \"0Gs\", \"dA\", \"Jm\", \"94n\", \"4Rq\", \"5W1\", \"5X0\", \"5MQ\", \"12w\", \"El\", \"1M2\", \"0Hr\", \"47T\", \"alm\", \n\"6KO\", \"5nM\", \"0EB\", \"fp\", \"3Nl\", \"0kn\", \"bjs\", \"6ec\", \"4NB\", \"aQs\", \"3Pn\", \"01d\", \"1Ka\", \"xr\", \"6UM\", \"54G\", \"59w\", \"a6g\", \"1J\", \"0Vp\", \n\"85L\", \"d3D\", \"5F2\", \"4Cr\", \"4Ls\", \"5I3\", \"To\", \"03U\", \"0Yq\", \"zC\", \"436\", \"56v\", \"4oo\", \"6ZL\", \"ws\", \"0TA\", \"0zm\", \"2ON\", \"60h\", \"4AC\", \n\"42\", \"27B\", \"6Hd\", \"49O\", \"4Sk\", \"6fH\", \"Kw\", \"0hE\", \"0eu\", \"FG\", \"6kx\", \"5Nz\", \"4pw\", \"5u7\", \"hk\", \"0KY\", \"0DX\", \"gj\", \"5z6\", \"5oW\", \n\"4QZ\", \"6dy\", \"IF\", \"0jt\", \"0gD\", \"Dv\", \"6iI\", \"5LK\", \"46N\", \"6Ge\", \"jZ\", \"0Ih\", \"0P\", \"a9\", \"4lD\", \"6Yg\", \"63C\", \"4Bh\", \"0yF\", \"Zt\", \n\"WD\", \"0tv\", \"4OX\", \"4Z9\", \"5d4\", \"4at\", \"0ZZ\", \"yh\", \"2a\", \"1Ez\", \"4nu\", \"5k5\", \"4U8\", \"5Px\", \"1kV\", \"XE\", \"Uu\", \"02O\", \"4Mi\", \"6xJ\", \n\"6Vf\", \"4cE\", \"n8\", \"2mH\", \"iu\", \"0JG\", \"45a\", \"6DJ\", \"6jf\", \"5Od\", \"R8\", \"GY\", \"Ji\", \"1yz\", \"4Ru\", \"5W5\", \"4i8\", \"48Q\", \"0Gw\", \"dE\", \n\"kD\", \"0Hv\", \"47P\", \"4f9\", \"5X4\", \"5MU\", \"0fZ\", \"Eh\", \"HX\", \"0kj\", \"4PD\", \"6eg\", \"6KK\", \"5nI\", \"0EF\", \"ft\", \"1Ke\", \"xv\", \"6UI\", \"54C\", \n\"4NF\", \"7kD\", \"VZ\", \"0uh\", \"0xX\", \"0m9\", \"5F6\", \"4Cv\", \"4mZ\", \"6Xy\", \"1N\", \"0Vt\", \"0Yu\", \"zG\", \"432\", \"56r\", \"4Lw\", \"5I7\", \"Tk\", \"03Q\", \n\"0zi\", \"2OJ\", \"60l\", \"4AG\", \"4ok\", \"6ZH\", \"ww\", \"0TE\", \"4So\", \"6fL\", \"Ks\", \"0hA\", \"46\", \"27F\", \"7XA\", \"49K\", \"4ps\", \"5u3\", \"ho\", \"8BE\", \n\"0eq\", \"FC\", \"aAn\", \"bdl\", \"bkm\", \"70T\", \"IB\", \"0jp\", \"307\", \"gn\", \"5z2\", \"5oS\", \"46J\", \"6Ga\", \"28G\", \"0Il\", \"13i\", \"z\", \"6iM\", \"5LO\", \n\"63G\", \"4Bl\", \"0yB\", \"Zp\", \"0T\", \"0Wn\", \"58i\", \"6Yc\", \"5d0\", \"4ap\", \"8SF\", \"yl\", \"1q2\", \"00z\", \"b1g\", \"aPm\", \"61v\", \"746\", \"1kR\", \"XA\", \n\"2e\", \"9Lf\", \"4nq\", \"5k1\", \"6Vb\", \"4cA\", \"0Xo\", \"2mL\", \"Uq\", \"02K\", \"4Mm\", \"6xN\", \"8YG\", \"7e\", \"5n1\", \"4kq\", \"716\", \"64v\", \"2KP\", \"1nR\", \n\"07K\", \"Pq\", \"69F\", \"4Hm\", \"4fA\", \"6Sb\", \"2hL\", \"1MN\", \"0Rn\", \"5T\", \"7LB\", \"5ya\", \"4Gl\", \"66G\", \"2Ia\", \"08J\", \"05z\", \"1t2\", \"aUm\", \"b4g\", \n\"4dp\", \"5a0\", \"8d\", \"8VF\", \"bn\", \"357\", \"4zr\", \"6OQ\", \"75T\", \"bnm\", \"0op\", \"LB\", \"Ar\", \"16i\", \"4Yn\", \"6lM\", \"6Ba\", \"43J\", \"0Ll\", \"2yO\", \n\"22F\", \"16\", \"4xC\", \"agr\", \"6cL\", \"4Vo\", \"0mA\", \"Ns\", \"CC\", \"14X\", \"bal\", \"aDn\", \"5p3\", \"4us\", \"8GE\", \"mo\", \"5L7\", \"4Iw\", \"06Q\", \"Qk\", \n\"1Y5\", \"1LT\", \"53r\", \"462\", \"7Oi\", \"4jk\", \"0QE\", \"rw\", \"2JJ\", \"1oH\", \"4DG\", \"65l\", \"7nD\", \"4KF\", \"0ph\", \"SZ\", \"2kg\", \"1Ne\", \"4ej\", \"6PI\", \n\"493\", \"4hZ\", \"0St\", \"4N\", \"0h9\", \"09P\", \"4Fv\", \"5C6\", \"4Xt\", \"6mW\", \"023\", \"0cZ\", \"0Mv\", \"nD\", \"4c9\", \"42P\", \"5kI\", \"6NK\", \"ct\", \"1Pg\", \n\"X9\", \"MX\", \"74N\", \"4UD\", \"4ZE\", \"6of\", \"BY\", \"W8\", \"0OG\", \"lu\", \"6AJ\", \"40a\", \"4yY\", \"4l8\", \"aE\", \"0Bw\", \"18r\", \"Oi\", \"5R5\", \"4Wu\", \n\"4EY\", \"4P8\", \"2KT\", \"1nV\", \"8YC\", \"7a\", \"5n5\", \"4ku\", \"4fE\", \"6Sf\", \"2hH\", \"k8\", \"07O\", \"Pu\", \"69B\", \"4Hi\", \"4Gh\", \"66C\", \"2Ie\", \"08N\", \n\"d9\", \"5P\", \"7LF\", \"4iD\", \"4dt\", \"5a4\", \"2jy\", \"3o9\", \"0qv\", \"RD\", \"7oZ\", \"4JX\", \"6ay\", \"4TZ\", \"0ot\", \"LF\", \"bj\", \"0AX\", \"4zv\", \"6OU\", \n\"6Be\", \"43N\", \"0Lh\", \"oZ\", \"Av\", \"0bD\", \"4Yj\", \"6lI\", \"6cH\", \"4Vk\", \"0mE\", \"Nw\", \"22B\", \"12\", \"4xG\", \"6Md\", \"5p7\", \"4uw\", \"0NY\", \"mk\", \n\"CG\", \"1pT\", \"5Kz\", \"6nx\", \"1Y1\", \"1LP\", \"53v\", \"466\", \"5L3\", \"4Is\", \"06U\", \"Qo\", \"2JN\", \"1oL\", \"4DC\", \"65h\", \"7Om\", \"4jo\", \"0QA\", \"rs\", \n\"9z\", \"1Na\", \"4en\", \"6PM\", \"aTs\", \"4KB\", \"04d\", \"2EO\", \"d6D\", \"09T\", \"4Fr\", \"5C2\", \"497\", \"bRm\", \"0Sp\", \"4J\", \"0Mr\", \"1H2\", \"aim\", \"42T\", \n\"4Xp\", \"6mS\", \"027\", \"17w\", \"0nn\", \"3Kl\", \"74J\", \"5Ea\", \"5kM\", \"6NO\", \"cp\", \"1Pc\", \"0OC\", \"lq\", \"6AN\", \"40e\", \"4ZA\", \"6ob\", \"2TL\", \"0ao\", \n\"18v\", \"Om\", \"5R1\", \"4Wq\", \"bCn\", \"afl\", \"aA\", \"0Bs\", \"07C\", \"Py\", \"69N\", \"4He\", \"4fI\", \"6Sj\", \"2hD\", \"k4\", \"0PW\", \"7m\", \"5n9\", \"4ky\", \n\"4EU\", \"4P4\", \"2KX\", \"1nZ\", \"05r\", \"RH\", \"7oV\", \"4JT\", \"4dx\", \"5a8\", \"8l\", \"1Ow\", \"d5\", \"qT\", \"7LJ\", \"4iH\", \"4Gd\", \"66O\", \"2Ii\", \"08B\", \n\"Az\", \"0bH\", \"4Yf\", \"6lE\", \"6Bi\", \"43B\", \"z7\", \"oV\", \"bf\", \"0AT\", \"4zz\", \"6OY\", \"4A7\", \"4TV\", \"0ox\", \"LJ\", \"CK\", \"14P\", \"5Kv\", \"4N6\", \n\"543\", \"41s\", \"0NU\", \"mg\", \"22N\", \"u6\", \"4xK\", \"6Mh\", \"6cD\", \"4Vg\", \"0mI\", \"2Xj\", \"7Oa\", \"4jc\", \"0QM\", \"6w\", \"2JB\", \"I2\", \"4DO\", \"65d\", \n\"68T\", \"b7D\", \"06Y\", \"Qc\", \"dSm\", \"287\", \"4gS\", \"4r2\", \"7MP\", \"4hR\", \"276\", \"4F\", \"0h1\", \"09X\", \"b8E\", \"67U\", \"7nL\", \"4KN\", \"F3\", \"SR\", \n\"9v\", \"1Nm\", \"4eb\", \"6PA\", \"5kA\", \"6NC\", \"21e\", \"1Po\", \"X1\", \"MP\", \"74F\", \"4UL\", \"bbO\", \"79v\", \"0v3\", \"0cR\", \"8Df\", \"nL\", \"4c1\", \"42X\", \n\"4yQ\", \"4l0\", \"aM\", \"8Kg\", \"0lS\", \"Oa\", \"76w\", \"637\", \"4ZM\", \"6on\", \"BQ\", \"W0\", \"0OO\", \"2zl\", \"6AB\", \"40i\", \"4fM\", \"6Sn\", \"3xa\", \"k0\", \n\"07G\", \"2Fl\", \"69J\", \"4Ha\", \"4EQ\", \"4P0\", \"d5g\", \"83o\", \"0PS\", \"7i\", \"a0D\", \"bQN\", \"50U\", \"hbt\", \"8h\", \"1Os\", \"05v\", \"RL\", \"7oR\", \"4JP\", \n\"5WA\", \"66K\", \"2Im\", \"08F\", \"d1\", \"5X\", \"7LN\", \"4iL\", \"6Bm\", \"43F\", \"z3\", \"oR\", \"2Wo\", \"0bL\", \"4Yb\", \"6lA\", \"4A3\", \"4TR\", \"8fd\", \"LN\", \n\"bb\", \"0AP\", \"cPl\", \"aeO\", \"547\", \"41w\", \"0NQ\", \"mc\", \"CO\", \"14T\", \"5Kr\", \"4N2\", \"77i\", \"4Vc\", \"0mM\", \"2Xn\", \"22J\", \"u2\", \"4xO\", \"6Ml\", \n\"2JF\", \"I6\", \"4DK\", \"6qh\", \"7Oe\", \"4jg\", \"0QI\", \"6s\", \"1Y9\", \"1LX\", \"4gW\", \"4r6\", \"68P\", \"5YZ\", \"0rU\", \"Qg\", \"0h5\", \"1mu\", \"4Fz\", \"67Q\", \n\"7MT\", \"4hV\", \"0Sx\", \"4B\", \"9r\", \"1Ni\", \"4ef\", \"6PE\", \"7nH\", \"4KJ\", \"F7\", \"SV\", \"X5\", \"MT\", \"74B\", \"4UH\", \"5kE\", \"6NG\", \"cx\", \"1Pk\", \n\"0Mz\", \"nH\", \"4c5\", \"4vT\", \"4Xx\", \"79r\", \"0v7\", \"0cV\", \"0lW\", \"Oe\", \"5R9\", \"4Wy\", \"4yU\", \"4l4\", \"aI\", \"1RZ\", \"0OK\", \"ly\", \"6AF\", \"40m\", \n\"4ZI\", \"6oj\", \"BU\", \"W4\", \"265\", \"5E\", \"488\", \"4iQ\", \"b9F\", \"66V\", \"0i2\", \"1lr\", \"G0\", \"RQ\", \"7oO\", \"4JM\", \"4da\", \"6QB\", \"8u\", \"1On\", \n\"0PN\", \"7t\", \"7Nb\", \"aa0\", \"4EL\", \"64g\", \"2KA\", \"H1\", \"07Z\", \"0f3\", \"69W\", \"b6G\", \"4fP\", \"479\", \"dRn\", \"294\", \"22W\", \"8Jd\", \"4xR\", \"4m3\", \n\"77t\", \"624\", \"0mP\", \"Nb\", \"CR\", \"V3\", \"5Ko\", \"6nm\", \"ajS\", \"41j\", \"0NL\", \"3kN\", \"20f\", \"0AM\", \"4zc\", \"aeR\", \"6al\", \"4TO\", \"Y2\", \"LS\", \n\"Ac\", \"0bQ\", \"bcL\", \"78u\", \"4b2\", \"4wS\", \"8Ee\", \"oO\", \"7nU\", \"4KW\", \"04q\", \"SK\", \"9o\", \"1Nt\", \"51R\", \"6PX\", \"7MI\", \"4hK\", \"e6\", \"pW\", \n\"2Hj\", \"09A\", \"4Fg\", \"67L\", \"68M\", \"4If\", \"0rH\", \"Qz\", \"2iG\", \"j7\", \"4gJ\", \"6Ri\", \"7Ox\", \"4jz\", \"0QT\", \"6n\", \"1z8\", \"1oY\", \"4DV\", \"4Q7\", \n\"4ZT\", \"4O5\", \"BH\", \"0az\", \"0OV\", \"ld\", \"550\", \"40p\", \"4yH\", \"6Lk\", \"aT\", \"t5\", \"0lJ\", \"Ox\", \"6bG\", \"4Wd\", \"4Xe\", \"6mF\", \"2Vh\", \"0cK\", \n\"0Mg\", \"nU\", \"6Cj\", \"42A\", \"5kX\", \"6NZ\", \"ce\", \"1Pv\", \"2N9\", \"MI\", \"7pW\", \"4UU\", \"4Gy\", \"5B9\", \"0i6\", \"1lv\", \"1BZ\", \"5A\", \"7LW\", \"4iU\", \n\"4de\", \"6QF\", \"8q\", \"1Oj\", \"G4\", \"RU\", \"7oK\", \"4JI\", \"4EH\", \"64c\", \"2KE\", \"H5\", \"0PJ\", \"7p\", \"7Nf\", \"4kd\", \"4fT\", \"4s5\", \"2hY\", \"290\", \n\"0sV\", \"Pd\", \"5M8\", \"4Hx\", \"6cY\", \"4Vz\", \"0mT\", \"Nf\", \"1F8\", \"0Cx\", \"4xV\", \"4m7\", \"7Pd\", \"41n\", \"0NH\", \"mz\", \"CV\", \"V7\", \"5Kk\", \"6ni\", \n\"6ah\", \"4TK\", \"Y6\", \"LW\", \"20b\", \"0AI\", \"4zg\", \"6OD\", \"4b6\", \"4wW\", \"0Ly\", \"oK\", \"Ag\", \"0bU\", \"5IZ\", \"6lX\", \"9k\", \"1Np\", \"51V\", \"azN\", \n\"7nQ\", \"4KS\", \"04u\", \"SO\", \"2Hn\", \"09E\", \"4Fc\", \"67H\", \"7MM\", \"4hO\", \"e2\", \"pS\", \"2iC\", \"j3\", \"4gN\", \"6Rm\", \"68I\", \"4Ib\", \"06D\", \"2Go\", \n\"d4d\", \"82l\", \"4DR\", \"4Q3\", \"a1G\", \"bPM\", \"0QP\", \"6j\", \"0OR\", \"0Z3\", \"554\", \"40t\", \"4ZP\", \"4O1\", \"BL\", \"15W\", \"0lN\", \"2Ym\", \"6bC\", \"5GA\", \n\"4yL\", \"6Lo\", \"aP\", \"09\", \"0Mc\", \"nQ\", \"6Cn\", \"42E\", \"4Xa\", \"6mB\", \"2Vl\", \"0cO\", \"8gg\", \"MM\", \"7pS\", \"4UQ\", \"bAN\", \"adL\", \"ca\", \"1Pr\", \n\"G8\", \"RY\", \"7oG\", \"4JE\", \"4di\", \"6QJ\", \"2jd\", \"1Of\", \"0Rw\", \"5M\", \"480\", \"4iY\", \"4Gu\", \"5B5\", \"2Ix\", \"08S\", \"07R\", \"Ph\", \"5M4\", \"4Ht\", \n\"4fX\", \"471\", \"1X6\", \"1MW\", \"0PF\", \"st\", \"7Nj\", \"4kh\", \"4ED\", \"64o\", \"2KI\", \"H9\", \"CZ\", \"14A\", \"5Kg\", \"6ne\", \"7Ph\", \"41b\", \"0ND\", \"mv\", \n\"1F4\", \"0Ct\", \"4xZ\", \"6My\", \"5S6\", \"4Vv\", \"0mX\", \"Nj\", \"Ak\", \"0bY\", \"4Yw\", \"6lT\", \"6Bx\", \"43S\", \"0Lu\", \"oG\", \"bw\", \"0AE\", \"4zk\", \"6OH\", \n\"6ad\", \"4TG\", \"0oi\", \"2ZJ\", \"7MA\", \"4hC\", \"0Sm\", \"4W\", \"2Hb\", \"09I\", \"4Fo\", \"67D\", \"aTn\", \"b5d\", \"04y\", \"SC\", \"9g\", \"8WE\", \"4es\", \"6PP\", \n\"5o2\", \"4jr\", \"8XD\", \"6f\", \"1z0\", \"1oQ\", \"705\", \"65u\", \"68E\", \"4In\", \"06H\", \"Qr\", \"2iO\", \"1LM\", \"4gB\", \"6Ra\", \"5ia\", \"6Lc\", \"23E\", \"05\", \n\"0lB\", \"Op\", \"6bO\", \"4Wl\", \"c4F\", \"aEm\", \"1d2\", \"0ar\", \"8FF\", \"ll\", \"558\", \"40x\", \"5kP\", \"6NR\", \"cm\", \"344\", \"0ns\", \"MA\", \"74W\", \"bon\", \n\"4Xm\", \"6mN\", \"3FA\", \"0cC\", \"0Mo\", \"2xL\", \"6Cb\", \"42I\", \"4dm\", \"6QN\", \"8y\", \"1Ob\", \"05g\", \"2DL\", \"7oC\", \"4JA\", \"4Gq\", \"5B1\", \"d7G\", \"08W\", \n\"0Rs\", \"5I\", \"484\", \"bSn\", \"52u\", \"475\", \"1X2\", \"1MS\", \"07V\", \"Pl\", \"5M0\", \"4Hp\", \"5Ua\", \"64k\", \"2KM\", \"1nO\", \"0PB\", \"7x\", \"7Nn\", \"4kl\", \n\"7Pl\", \"41f\", \"8GX\", \"mr\", \"2UO\", \"14E\", \"5Kc\", \"6na\", \"5S2\", \"4Vr\", \"19u\", \"Nn\", \"1F0\", \"0Cp\", \"bBm\", \"ago\", \"ahn\", \"43W\", \"0Lq\", \"oC\", \n\"Ao\", \"16t\", \"4Ys\", \"6lP\", \"75I\", \"4TC\", \"0om\", \"2ZN\", \"bs\", \"0AA\", \"4zo\", \"6OL\", \"2Hf\", \"09M\", \"4Fk\", \"6sH\", \"7ME\", \"4hG\", \"0Si\", \"4S\", \n\"9c\", \"1Nx\", \"4ew\", \"6PT\", \"7nY\", \"bqh\", \"0pu\", \"SG\", \"1z4\", \"1oU\", \"4DZ\", \"65q\", \"5o6\", \"4jv\", \"0QX\", \"6b\", \"2iK\", \"1LI\", \"4gF\", \"6Re\", \n\"68A\", \"4Ij\", \"06L\", \"Qv\", \"0lF\", \"Ot\", \"6bK\", \"4Wh\", \"4yD\", \"6Lg\", \"aX\", \"01\", \"0OZ\", \"lh\", \"5q4\", \"4tt\", \"4ZX\", \"4O9\", \"BD\", \"0av\", \n\"0nw\", \"ME\", \"74S\", \"4UY\", \"5kT\", \"6NV\", \"ci\", \"1Pz\", \"0Mk\", \"nY\", \"6Cf\", \"42M\", \"4Xi\", \"6mJ\", \"2Vd\", \"0cG\", \"bL\", \"8Hf\", \"4zP\", \"4o1\", \n\"75v\", \"606\", \"0oR\", \"0z3\", \"AP\", \"T1\", \"4YL\", \"6lo\", \"6BC\", \"43h\", \"0LN\", \"2ym\", \"22d\", \"0CO\", \"4xa\", \"6MB\", \"6cn\", \"4VM\", \"0mc\", \"NQ\", \n\"Ca\", \"14z\", \"baN\", \"aDL\", \"7PS\", \"41Y\", \"8Gg\", \"mM\", \"247\", \"7G\", \"7NQ\", \"4kS\", \"com\", \"64T\", \"0k0\", \"1np\", \"E2\", \"PS\", \"69d\", \"4HO\", \n\"4fc\", \"7Ca\", \"2hn\", \"1Ml\", \"0RL\", \"5v\", \"avS\", \"4ib\", \"4GN\", \"66e\", \"2IC\", \"J3\", \"05X\", \"Rb\", \"aUO\", \"b4E\", \"4dR\", \"4q3\", \"8F\", \"8Vd\", \n\"4XV\", \"4M7\", \"1f8\", \"0cx\", \"0MT\", \"nf\", \"572\", \"42r\", \"5kk\", \"6Ni\", \"cV\", \"v7\", \"0nH\", \"Mz\", \"74l\", \"4Uf\", \"4Zg\", \"6oD\", \"2Tj\", \"0aI\", \n\"y6\", \"lW\", \"6Ah\", \"40C\", \"5iZ\", \"583\", \"ag\", \"0BU\", \"0ly\", \"OK\", \"4B6\", \"4WW\", \"7lW\", \"4IU\", \"06s\", \"QI\", \"0I6\", \"1Lv\", \"4gy\", \"5b9\", \n\"7OK\", \"4jI\", \"g4\", \"rU\", \"2Jh\", \"1oj\", \"4De\", \"65N\", \"7nf\", \"4Kd\", \"04B\", \"Sx\", \"2kE\", \"h5\", \"4eH\", \"6Pk\", \"5m8\", \"4hx\", \"0SV\", \"4l\", \n\"2HY\", \"09r\", \"4FT\", \"4S5\", \"5Q8\", \"4Tx\", \"0oV\", \"Ld\", \"bH\", \"0Az\", \"4zT\", \"4o5\", \"6BG\", \"43l\", \"0LJ\", \"ox\", \"AT\", \"T5\", \"4YH\", \"6lk\", \n\"6cj\", \"4VI\", \"0mg\", \"NU\", \"2vh\", \"0CK\", \"4xe\", \"6MF\", \"7PW\", \"4uU\", \"2n9\", \"mI\", \"Ce\", \"1pv\", \"5KX\", \"6nZ\", \"5UZ\", \"64P\", \"0k4\", \"1nt\", \n\"0Py\", \"7C\", \"7NU\", \"4kW\", \"4fg\", \"6SD\", \"2hj\", \"1Mh\", \"E6\", \"PW\", \"7mI\", \"4HK\", \"4GJ\", \"66a\", \"2IG\", \"J7\", \"0RH\", \"5r\", \"7Ld\", \"4if\", \n\"4dV\", \"4q7\", \"8B\", \"1OY\", \"0qT\", \"Rf\", \"7ox\", \"4Jz\", \"0MP\", \"nb\", \"576\", \"42v\", \"4XR\", \"4M3\", \"dll\", \"17U\", \"0nL\", \"3KN\", \"74h\", \"4Ub\", \n\"5ko\", \"6Nm\", \"cR\", \"v3\", \"y2\", \"lS\", \"6Al\", \"40G\", \"4Zc\", \"aER\", \"2Tn\", \"0aM\", \"18T\", \"OO\", \"4B2\", \"4WS\", \"bCL\", \"587\", \"ac\", \"0BQ\", \n\"0I2\", \"1Lr\", \"53T\", \"axL\", \"68z\", \"4IQ\", \"06w\", \"QM\", \"2Jl\", \"1on\", \"4Da\", \"65J\", \"7OO\", \"4jM\", \"g0\", \"6Y\", \"9X\", \"h1\", \"4eL\", \"6Po\", \n\"7nb\", \"aA0\", \"04F\", \"2Em\", \"d6f\", \"09v\", \"4FP\", \"4S1\", \"a3E\", \"bRO\", \"0SR\", \"4h\", \"AX\", \"T9\", \"4YD\", \"6lg\", \"6BK\", \"4wh\", \"0LF\", \"ot\", \n\"bD\", \"0Av\", \"4zX\", \"4o9\", \"5Q4\", \"4Tt\", \"0oZ\", \"Lh\", \"Ci\", \"14r\", \"5KT\", \"6nV\", \"ajh\", \"41Q\", \"0Nw\", \"mE\", \"22l\", \"0CG\", \"4xi\", \"6MJ\", \n\"6cf\", \"4VE\", \"0mk\", \"NY\", \"07a\", \"2FJ\", \"69l\", \"4HG\", \"4fk\", \"6SH\", \"2hf\", \"1Md\", \"0Pu\", \"7O\", \"7NY\", \"bQh\", \"4Ew\", \"6pT\", \"0k8\", \"1nx\", \n\"05P\", \"Rj\", \"5O6\", \"4Jv\", \"4dZ\", \"453\", \"8N\", \"1OU\", \"0RD\", \"qv\", \"7Lh\", \"4ij\", \"4GF\", \"66m\", \"2IK\", \"1lI\", \"5kc\", \"6Na\", \"21G\", \"27\", \n\"8gX\", \"Mr\", \"74d\", \"4Un\", \"bbm\", \"79T\", \"1f0\", \"0cp\", \"397\", \"nn\", \"5s2\", \"42z\", \"4ys\", \"6LP\", \"ao\", \"366\", \"0lq\", \"OC\", \"76U\", \"bml\", \n\"4Zo\", \"6oL\", \"Bs\", \"0aA\", \"0Om\", \"2zN\", \"7QA\", \"40K\", \"7OC\", \"4jA\", \"0Qo\", \"6U\", \"3ZA\", \"1ob\", \"4Dm\", \"65F\", \"68v\", \"b7f\", \"0rs\", \"QA\", \n\"dSO\", \"8UG\", \"4gq\", \"5b1\", \"5m0\", \"4hp\", \"8ZF\", \"4d\", \"1x2\", \"09z\", \"727\", \"67w\", \"7nn\", \"4Kl\", \"04J\", \"Sp\", \"9T\", \"1NO\", \"51i\", \"6Pc\", \n\"6BO\", \"43d\", \"0LB\", \"op\", \"2WM\", \"0bn\", \"5Ia\", \"6lc\", \"5Q0\", \"4Tp\", \"8fF\", \"Ll\", \"1D2\", \"0Ar\", \"cPN\", \"aem\", \"ajl\", \"41U\", \"0Ns\", \"mA\", \n\"Cm\", \"14v\", \"5KP\", \"6nR\", \"6cb\", \"4VA\", \"0mo\", \"2XL\", \"22h\", \"0CC\", \"4xm\", \"6MN\", \"4fo\", \"6SL\", \"2hb\", \"8TY\", \"07e\", \"2FN\", \"69h\", \"4HC\", \n\"4Es\", \"64X\", \"d5E\", \"83M\", \"0Pq\", \"7K\", \"a0f\", \"bQl\", \"50w\", \"457\", \"8J\", \"1OQ\", \"05T\", \"Rn\", \"5O2\", \"4Jr\", \"4GB\", \"66i\", \"2IO\", \"08d\", \n\"1Ba\", \"5z\", \"7Ll\", \"4in\", \"0nD\", \"Mv\", \"7ph\", \"4Uj\", \"5kg\", \"6Ne\", \"cZ\", \"23\", \"0MX\", \"nj\", \"5s6\", \"4vv\", \"4XZ\", \"6my\", \"1f4\", \"0ct\", \n\"0lu\", \"OG\", \"6bx\", \"5Gz\", \"4yw\", \"6LT\", \"ak\", \"0BY\", \"0Oi\", \"2zJ\", \"6Ad\", \"40O\", \"4Zk\", \"6oH\", \"Bw\", \"0aE\", \"2Jd\", \"1of\", \"4Di\", \"65B\", \n\"7OG\", \"4jE\", \"g8\", \"6Q\", \"2ix\", \"1Lz\", \"4gu\", \"5b5\", \"68r\", \"4IY\", \"0rw\", \"QE\", \"1x6\", \"1mW\", \"4FX\", \"4S9\", \"5m4\", \"4ht\", \"0SZ\", \"ph\", \n\"9P\", \"h9\", \"4eD\", \"6Pg\", \"7nj\", \"4Kh\", \"04N\", \"St\", \"22u\", \"375\", \"4xp\", \"598\", \"77V\", \"blo\", \"0mr\", \"1h2\", \"Cp\", \"14k\", \"5KM\", \"6nO\", \n\"7PB\", \"41H\", \"0Nn\", \"3kl\", \"20D\", \"34\", \"4zA\", \"6Ob\", \"6aN\", \"4Tm\", \"0oC\", \"Lq\", \"AA\", \"0bs\", \"bcn\", \"78W\", \"569\", \"43y\", \"384\", \"om\", \n\"9Kd\", \"5g\", \"5l3\", \"4is\", \"734\", \"66t\", \"1y1\", \"08y\", \"05I\", \"Rs\", \"7om\", \"4Jo\", \"4dC\", \"7AA\", \"8W\", \"1OL\", \"0Pl\", \"7V\", \"ats\", \"4kB\", \n\"4En\", \"64E\", \"2Kc\", \"1na\", \"07x\", \"PB\", \"69u\", \"b6e\", \"4fr\", \"5c2\", \"dRL\", \"8TD\", \"4Zv\", \"6oU\", \"Bj\", \"0aX\", \"0Ot\", \"lF\", \"6Ay\", \"40R\", \n\"4yj\", \"6LI\", \"av\", \"0BD\", \"0lh\", \"OZ\", \"6be\", \"4WF\", \"4XG\", \"6md\", \"2VJ\", \"0ci\", \"0ME\", \"nw\", \"6CH\", \"42c\", \"5kz\", \"6Nx\", \"cG\", \"1PT\", \n\"0nY\", \"Mk\", \"5P7\", \"4Uw\", \"5N5\", \"4Ku\", \"04S\", \"Si\", \"9M\", \"1NV\", \"4eY\", \"440\", \"7Mk\", \"4hi\", \"0SG\", \"pu\", \"2HH\", \"K8\", \"4FE\", \"67n\", \n\"68o\", \"4ID\", \"1\", \"QX\", \"2ie\", \"1Lg\", \"4gh\", \"6RK\", \"7OZ\", \"4jX\", \"0Qv\", \"6L\", \"2Jy\", \"3O9\", \"4Dt\", \"5A4\", \"4C9\", \"4VX\", \"0mv\", \"ND\", \n\"22q\", \"0CZ\", \"4xt\", \"6MW\", \"7PF\", \"41L\", \"x9\", \"mX\", \"Ct\", \"14o\", \"5KI\", \"6nK\", \"6aJ\", \"4Ti\", \"0oG\", \"Lu\", \"bY\", \"30\", \"4zE\", \"6Of\", \n\"5r5\", \"4wu\", \"380\", \"oi\", \"AE\", \"0bw\", \"4YY\", \"4L8\", \"5Wz\", \"66p\", \"1y5\", \"1lT\", \"0RY\", \"5c\", \"5l7\", \"4iw\", \"4dG\", \"6Qd\", \"8S\", \"1OH\", \n\"05M\", \"Rw\", \"7oi\", \"4Jk\", \"4Ej\", \"64A\", \"2Kg\", \"1ne\", \"0Ph\", \"7R\", \"7ND\", \"4kF\", \"4fv\", \"5c6\", \"0H9\", \"1My\", \"0st\", \"PF\", \"69q\", \"4HZ\", \n\"0Op\", \"lB\", \"ako\", \"40V\", \"4Zr\", \"6oQ\", \"Bn\", \"15u\", \"0ll\", \"2YO\", \"6ba\", \"4WB\", \"4yn\", \"6LM\", \"ar\", \"1Ra\", \"0MA\", \"ns\", \"6CL\", \"42g\", \n\"4XC\", \"79I\", \"2VN\", \"0cm\", \"8gE\", \"Mo\", \"5P3\", \"4Us\", \"bAl\", \"adn\", \"cC\", \"1PP\", \"9I\", \"1NR\", \"51t\", \"444\", \"5N1\", \"4Kq\", \"04W\", \"Sm\", \n\"2HL\", \"09g\", \"4FA\", \"67j\", \"7Mo\", \"4hm\", \"0SC\", \"4y\", \"2ia\", \"1Lc\", \"4gl\", \"6RO\", \"68k\", \"5Ya\", \"5\", \"2GM\", \"d4F\", \"82N\", \"4Dp\", \"5A0\", \n\"a1e\", \"bPo\", \"0Qr\", \"6H\", \"Cx\", \"14c\", \"5KE\", \"6nG\", \"7PJ\", \"4uH\", \"x5\", \"mT\", \"0V7\", \"0CV\", \"4xx\", \"590\", \"4C5\", \"4VT\", \"0mz\", \"NH\", \n\"AI\", \"16R\", \"4YU\", \"4L4\", \"561\", \"43q\", \"0LW\", \"oe\", \"bU\", \"w4\", \"4zI\", \"6Oj\", \"6aF\", \"4Te\", \"0oK\", \"Ly\", \"05A\", \"2Dj\", \"7oe\", \"4Jg\", \n\"4dK\", \"6Qh\", \"2jF\", \"i6\", \"0RU\", \"5o\", \"7Ly\", \"5yZ\", \"4GW\", \"4R6\", \"1y9\", \"08q\", \"07p\", \"PJ\", \"7mT\", \"4HV\", \"4fz\", \"6SY\", \"0H5\", \"1Mu\", \n\"f7\", \"sV\", \"7NH\", \"4kJ\", \"4Ef\", \"64M\", \"2Kk\", \"1ni\", \"4yb\", \"6LA\", \"23g\", \"0BL\", \"Z3\", \"OR\", \"6bm\", \"4WN\", \"c4d\", \"aEO\", \"Bb\", \"0aP\", \n\"8Fd\", \"lN\", \"4a3\", \"40Z\", \"5kr\", \"4n2\", \"cO\", \"8Ie\", \"0nQ\", \"Mc\", \"74u\", \"615\", \"4XO\", \"6ml\", \"2VB\", \"U2\", \"0MM\", \"2xn\", \"7Sa\", \"42k\", \n\"7Mc\", \"4ha\", \"0SO\", \"4u\", \"3Xa\", \"K0\", \"4FM\", \"67f\", \"aTL\", \"b5F\", \"0pS\", \"Sa\", \"9E\", \"8Wg\", \"4eQ\", \"448\", \"7OR\", \"4jP\", \"254\", \"6D\", \n\"0j3\", \"1os\", \"cnn\", \"65W\", \"68g\", \"4IL\", \"9\", \"QP\", \"2im\", \"1Lo\", \"53I\", \"6RC\", \"7PN\", \"41D\", \"x1\", \"mP\", \"2Um\", \"14g\", \"5KA\", \"6nC\", \n\"4C1\", \"4VP\", \"19W\", \"NL\", \"0V3\", \"0CR\", \"bBO\", \"594\", \"565\", \"43u\", \"0LS\", \"oa\", \"AM\", \"16V\", \"4YQ\", \"4L0\", \"6aB\", \"4Ta\", \"0oO\", \"2Zl\", \n\"bQ\", \"38\", \"4zM\", \"6On\", \"4dO\", \"6Ql\", \"2jB\", \"i2\", \"05E\", \"2Dn\", \"7oa\", \"4Jc\", \"4GS\", \"4R2\", \"d7e\", \"08u\", \"0RQ\", \"5k\", \"a2F\", \"bSL\", \n\"52W\", \"ayO\", \"0H1\", \"1Mq\", \"07t\", \"PN\", \"69y\", \"4HR\", \"4Eb\", \"64I\", \"2Ko\", \"1nm\", \"f3\", \"7Z\", \"7NL\", \"4kN\", \"Z7\", \"OV\", \"6bi\", \"4WJ\", \n\"4yf\", \"6LE\", \"az\", \"0BH\", \"0Ox\", \"lJ\", \"4a7\", \"4tV\", \"4Zz\", \"6oY\", \"Bf\", \"0aT\", \"0nU\", \"Mg\", \"74q\", \"5EZ\", \"5kv\", \"4n6\", \"cK\", \"1PX\", \n\"0MI\", \"2xj\", \"6CD\", \"42o\", \"4XK\", \"6mh\", \"2VF\", \"U6\", \"2HD\", \"K4\", \"4FI\", \"67b\", \"7Mg\", \"4he\", \"0SK\", \"4q\", \"9A\", \"1NZ\", \"4eU\", \"4p4\", \n\"5N9\", \"4Ky\", \"0pW\", \"Se\", \"0j7\", \"1ow\", \"4Dx\", \"5A8\", \"7OV\", \"4jT\", \"0Qz\", \"rH\", \"2ii\", \"1Lk\", \"4gd\", \"6RG\", \"68c\", \"4IH\", \"D5\", \"QT\", \n\"5Ls\", \"4I3\", \"F\", \"13U\", \"0IP\", \"jb\", \"536\", \"46v\", \"5oo\", \"6Jm\", \"gR\", \"r3\", \"0jL\", \"3ON\", \"6dA\", \"4Qb\", \"5NB\", \"aAR\", \"2Pn\", \"0eM\", \n\"0Ka\", \"hS\", \"6El\", \"44G\", \"49w\", \"abN\", \"ec\", \"0FQ\", \"8ae\", \"KO\", \"4F2\", \"4SS\", \"4X0\", \"4MQ\", \"02w\", \"UM\", \"0M2\", \"0XS\", \"57T\", \"a8D\", \n\"7KO\", \"4nM\", \"c0\", \"2Y\", \"2Nl\", \"1kn\", \"aJ1\", \"61J\", \"6zC\", \"aE0\", \"00F\", \"2Am\", \"yP\", \"l1\", \"4aL\", \"6To\", \"a7E\", \"58U\", \"0WR\", \"0h\", \n\"ZL\", \"84n\", \"4BP\", \"4W1\", \"fH\", \"0Ez\", \"5nu\", \"4k5\", \"5U8\", \"4Px\", \"0kV\", \"Hd\", \"ET\", \"P5\", \"5Mi\", \"6hk\", \"6FG\", \"47l\", \"0HJ\", \"kx\", \n\"dy\", \"0GK\", \"48m\", \"6IF\", \"6gj\", \"4RI\", \"0ig\", \"JU\", \"Ge\", \"0dW\", \"5OX\", \"5Z9\", \"4d4\", \"4qU\", \"1ZZ\", \"iI\", \"0Ty\", \"3C\", \"4z6\", \"4oW\", \n\"5QZ\", \"60P\", \"Yg\", \"0zU\", \"A6\", \"TW\", \"6yh\", \"4LK\", \"4bg\", \"6WD\", \"2lj\", \"0YI\", \"0VH\", \"1r\", \"6XE\", \"4mf\", \"4CJ\", \"62a\", \"2MG\", \"N7\", \n\"0uT\", \"Vf\", \"7kx\", \"4Nz\", \"5pw\", \"4u7\", \"xJ\", \"1KY\", \"0IT\", \"jf\", \"532\", \"46r\", \"5Lw\", \"4I7\", \"B\", \"0gx\", \"0jH\", \"Iz\", \"6dE\", \"4Qf\", \n\"5ok\", \"6Ji\", \"gV\", \"r7\", \"0Ke\", \"hW\", \"6Eh\", \"44C\", \"5NF\", \"6kD\", \"2Pj\", \"0eI\", \"0hy\", \"KK\", \"4F6\", \"4SW\", \"49s\", \"6HX\", \"eg\", \"0FU\", \n\"0M6\", \"0XW\", \"4cy\", \"5f9\", \"4X4\", \"4MU\", \"02s\", \"UI\", \"Xy\", \"1kj\", \"5PD\", \"61N\", \"7KK\", \"4nI\", \"c4\", \"vU\", \"yT\", \"l5\", \"4aH\", \"6Tk\", \n\"6zG\", \"4Od\", \"00B\", \"Wx\", \"ZH\", \"0yz\", \"4BT\", \"4W5\", \"5i8\", \"4lx\", \"0WV\", \"0l\", \"71v\", \"646\", \"0kR\", \"3NP\", \"fL\", \"8Lf\", \"5nq\", \"4k1\", \n\"6FC\", \"47h\", \"0HN\", \"29e\", \"EP\", \"P1\", \"5Mm\", \"6ho\", \"6gn\", \"4RM\", \"0ic\", \"JQ\", \"26d\", \"0GO\", \"48i\", \"6IB\", \"4d0\", \"45Y\", \"8Cg\", \"iM\", \n\"Ga\", \"0dS\", \"beN\", \"hYu\", \"ckm\", \"60T\", \"Yc\", \"0zQ\", \"207\", \"3G\", \"4z2\", \"4oS\", \"4bc\", \"7Ga\", \"2ln\", \"0YM\", \"A2\", \"TS\", \"6yl\", \"4LO\", \n\"4CN\", \"62e\", \"2MC\", \"N3\", \"0VL\", \"1v\", \"6XA\", \"4mb\", \"5ps\", \"4u3\", \"xN\", \"8Rd\", \"01X\", \"Vb\", \"aQO\", \"b0E\", \"5og\", \"6Je\", \"gZ\", \"63\", \n\"0jD\", \"Iv\", \"6dI\", \"4Qj\", \"7l9\", \"6iy\", \"N\", \"0gt\", \"0IX\", \"jj\", \"5w6\", \"4rv\", \"5mV\", \"5x7\", \"ek\", \"0FY\", \"0hu\", \"KG\", \"6fx\", \"5Cz\", \n\"5NJ\", \"6kH\", \"Fw\", \"0eE\", \"92\", \"3nk\", \"6Ed\", \"44O\", \"7KG\", \"4nE\", \"c8\", \"2Q\", \"Xu\", \"1kf\", \"5PH\", \"61B\", \"4X8\", \"4MY\", \"0vw\", \"UE\", \n\"2mx\", \"1Hz\", \"4cu\", \"5f5\", \"5i4\", \"4lt\", \"0WZ\", \"th\", \"ZD\", \"0yv\", \"4BX\", \"4W9\", \"6zK\", \"4Oh\", \"00N\", \"Wt\", \"yX\", \"l9\", \"4aD\", \"6Tg\", \n\"2SM\", \"0fn\", \"5Ma\", \"6hc\", \"6FO\", \"47d\", \"0HB\", \"kp\", \"24Y\", \"0Er\", \"bDo\", \"aam\", \"5U0\", \"4Pp\", \"8bF\", \"Hl\", \"Gm\", \"10v\", \"5OP\", \"5Z1\", \n\"anl\", \"45U\", \"0Js\", \"iA\", \"dq\", \"0GC\", \"48e\", \"6IN\", \"6gb\", \"4RA\", \"0io\", \"3Lm\", \"03e\", \"2BN\", \"7iA\", \"4LC\", \"4bo\", \"6WL\", \"zs\", \"0YA\", \n\"0Tq\", \"3K\", \"a4f\", \"bUl\", \"4As\", \"5D3\", \"Yo\", \"87M\", \"01T\", \"Vn\", \"5K2\", \"4Nr\", \"54w\", \"417\", \"xB\", \"1KQ\", \"1Fa\", \"1z\", \"6XM\", \"4mn\", \n\"4CB\", \"62i\", \"2MO\", \"0xl\", \"1za\", \"Ir\", \"6dM\", \"4Qn\", \"5oc\", \"6Ja\", \"25G\", \"67\", \"9Pe\", \"jn\", \"5w2\", \"46z\", \"bfm\", \"aCo\", \"J\", \"0gp\", \n\"0hq\", \"KC\", \"72U\", \"bil\", \"5mR\", \"5x3\", \"eo\", \"326\", \"96\", \"3no\", \"7UA\", \"44K\", \"5NN\", \"6kL\", \"Fs\", \"0eA\", \"Xq\", \"1kb\", \"5PL\", \"61F\", \n\"7KC\", \"4nA\", \"0Uo\", \"2U\", \"39U\", \"8QG\", \"4cq\", \"5f1\", \"aRl\", \"796\", \"0vs\", \"UA\", \"2LQ\", \"0yr\", \"767\", \"63w\", \"5i0\", \"4lp\", \"9Ng\", \"0d\", \n\"2oM\", \"0Zn\", \"55i\", \"6Tc\", \"6zO\", \"4Ol\", \"00J\", \"Wp\", \"6FK\", \"4sh\", \"0HF\", \"kt\", \"EX\", \"P9\", \"5Me\", \"6hg\", \"5U4\", \"4Pt\", \"0kZ\", \"Hh\", \n\"fD\", \"0Ev\", \"5ny\", \"4k9\", \"4d8\", \"45Q\", \"0Jw\", \"iE\", \"Gi\", \"10r\", \"5OT\", \"5Z5\", \"6gf\", \"4RE\", \"0ik\", \"JY\", \"du\", \"0GG\", \"48a\", \"6IJ\", \n\"4bk\", \"6WH\", \"zw\", \"0YE\", \"03a\", \"2BJ\", \"6yd\", \"4LG\", \"4Aw\", \"5D7\", \"Yk\", \"0zY\", \"0Tu\", \"3O\", \"6Zx\", \"bUh\", \"54s\", \"413\", \"xF\", \"1KU\", \n\"01P\", \"Vj\", \"5K6\", \"4Nv\", \"4CF\", \"62m\", \"2MK\", \"0xh\", \"0VD\", \"uv\", \"6XI\", \"4mj\", \"5NS\", \"6kQ\", \"Fn\", \"11u\", \"0Kp\", \"hB\", \"aoo\", \"44V\", \n\"49f\", \"6HM\", \"er\", \"1Va\", \"0hl\", \"3Mn\", \"6fa\", \"4SB\", \"5Lb\", \"7yA\", \"W\", \"0gm\", \"0IA\", \"js\", \"6GL\", \"46g\", \"bEl\", \"hyW\", \"gC\", \"0Dq\", \n\"8cE\", \"Io\", \"5T3\", \"4Qs\", \"5J1\", \"4Oq\", \"00W\", \"Wm\", \"yA\", \"0Zs\", \"55t\", \"404\", \"6YN\", \"4lm\", \"0WC\", \"0y\", \"2LL\", \"0yo\", \"4BA\", \"63j\", \n\"6xc\", \"bws\", \"02f\", \"2CM\", \"2ma\", \"0XB\", \"4cl\", \"6VO\", \"a5e\", \"bTo\", \"0Ur\", \"2H\", \"Xl\", \"86N\", \"5PQ\", \"5E0\", \"dh\", \"0GZ\", \"5lU\", \"5y4\", \n\"4G9\", \"4RX\", \"0iv\", \"JD\", \"Gt\", \"0dF\", \"5OI\", \"6jK\", \"6Dg\", \"45L\", \"81\", \"iX\", \"fY\", \"70\", \"5nd\", \"6Kf\", \"6eJ\", \"4Pi\", \"0kG\", \"Hu\", \n\"EE\", \"0fw\", \"5Mx\", \"4H8\", \"5v5\", \"4su\", \"1Xz\", \"ki\", \"0VY\", \"1c\", \"5h7\", \"4mw\", \"5Sz\", \"62p\", \"2MV\", \"0xu\", \"01M\", \"Vw\", \"7ki\", \"4Nk\", \n\"54n\", \"6Ud\", \"2nJ\", \"1KH\", \"0Th\", \"3R\", \"6Ze\", \"4oF\", \"4Aj\", \"60A\", \"Yv\", \"0zD\", \"0wt\", \"TF\", \"6yy\", \"4LZ\", \"4bv\", \"5g6\", \"zj\", \"0YX\", \n\"0Kt\", \"hF\", \"6Ey\", \"44R\", \"5NW\", \"6kU\", \"Fj\", \"0eX\", \"0hh\", \"KZ\", \"6fe\", \"4SF\", \"49b\", \"6HI\", \"ev\", \"0FD\", \"0IE\", \"jw\", \"6GH\", \"46c\", \n\"5Lf\", \"6id\", \"S\", \"0gi\", \"0jY\", \"Ik\", \"5T7\", \"4Qw\", \"5oz\", \"6Jx\", \"gG\", \"0Du\", \"yE\", \"0Zw\", \"4aY\", \"400\", \"5J5\", \"4Ou\", \"00S\", \"Wi\", \n\"ZY\", \"O8\", \"4BE\", \"63n\", \"6YJ\", \"4li\", \"0WG\", \"tu\", \"2me\", \"0XF\", \"4ch\", \"6VK\", \"6xg\", \"4MD\", \"02b\", \"UX\", \"Xh\", \"3K9\", \"5PU\", \"5E4\", \n\"7KZ\", \"4nX\", \"0Uv\", \"2L\", \"73V\", \"bho\", \"0ir\", \"1l2\", \"dl\", \"335\", \"48x\", \"5y0\", \"6Dc\", \"45H\", \"85\", \"3ol\", \"Gp\", \"0dB\", \"5OM\", \"6jO\", \n\"6eN\", \"4Pm\", \"0kC\", \"Hq\", \"24D\", \"74\", \"bDr\", \"6Kb\", \"529\", \"47y\", \"8AG\", \"km\", \"EA\", \"0fs\", \"bgn\", \"aBl\", \"774\", \"62t\", \"199\", \"0xq\", \n\"9Od\", \"1g\", \"5h3\", \"4ms\", \"54j\", \"7EA\", \"2nN\", \"1KL\", \"01I\", \"Vs\", \"7km\", \"4No\", \"4An\", \"60E\", \"Yr\", \"1ja\", \"0Tl\", \"3V\", \"6Za\", \"4oB\", \n\"4br\", \"5g2\", \"zn\", \"8PD\", \"03x\", \"TB\", \"aSo\", \"785\", \"49n\", \"6HE\", \"ez\", \"0FH\", \"0hd\", \"KV\", \"6fi\", \"4SJ\", \"bdI\", \"6kY\", \"Ff\", \"0eT\", \n\"0Kx\", \"hJ\", \"4e7\", \"4pV\", \"5ov\", \"4j6\", \"gK\", \"0Dy\", \"0jU\", \"Ig\", \"6dX\", \"5AZ\", \"5Lj\", \"6ih\", \"DW\", \"Q6\", \"0II\", \"28b\", \"6GD\", \"46o\", \n\"6YF\", \"4le\", \"0WK\", \"0q\", \"ZU\", \"O4\", \"4BI\", \"63b\", \"5J9\", \"4Oy\", \"0tW\", \"We\", \"yI\", \"1JZ\", \"4aU\", \"4t4\", \"7KV\", \"4nT\", \"0Uz\", \"vH\", \n\"Xd\", \"1kw\", \"5PY\", \"5E8\", \"6xk\", \"4MH\", \"02n\", \"UT\", \"2mi\", \"0XJ\", \"4cd\", \"6VG\", \"2Qm\", \"0dN\", \"5OA\", \"6jC\", \"6Do\", \"45D\", \"89\", \"iP\", \n\"0R3\", \"0GR\", \"48t\", \"acM\", \"4G1\", \"4RP\", \"94O\", \"JL\", \"EM\", \"12V\", \"5Mp\", \"4H0\", \"525\", \"47u\", \"0HS\", \"ka\", \"fQ\", \"78\", \"5nl\", \"6Kn\", \n\"6eB\", \"4Pa\", \"0kO\", \"3NM\", \"01E\", \"3PO\", \"7ka\", \"4Nc\", \"54f\", \"6Ul\", \"xS\", \"m2\", \"0VQ\", \"1k\", \"a6F\", \"59V\", \"4CS\", \"4V2\", \"195\", \"85m\", \n\"03t\", \"TN\", \"4Y3\", \"4LR\", \"56W\", \"a9G\", \"zb\", \"0YP\", \"b3\", \"3Z\", \"6Zm\", \"4oN\", \"4Ab\", \"60I\", \"2Oo\", \"0zL\", \"1xA\", \"KR\", \"6fm\", \"4SN\", \n\"49j\", \"6HA\", \"27g\", \"0FL\", \"8Bd\", \"hN\", \"4e3\", \"44Z\", \"bdM\", \"aAO\", \"Fb\", \"0eP\", \"0jQ\", \"Ic\", \"70u\", \"655\", \"5or\", \"4j2\", \"gO\", \"8Me\", \n\"0IM\", \"28f\", \"7Wa\", \"46k\", \"5Ln\", \"6il\", \"DS\", \"Q2\", \"ZQ\", \"O0\", \"4BM\", \"63f\", \"6YB\", \"4la\", \"0WO\", \"0u\", \"yM\", \"8Sg\", \"4aQ\", \"408\", \n\"aPL\", \"b1F\", \"0tS\", \"Wa\", \"0n3\", \"1ks\", \"bzO\", \"61W\", \"7KR\", \"4nP\", \"214\", \"2D\", \"2mm\", \"0XN\", \"57I\", \"6VC\", \"6xo\", \"4ML\", \"02j\", \"UP\", \n\"6Dk\", \"4qH\", \"0Jf\", \"iT\", \"Gx\", \"0dJ\", \"5OE\", \"6jG\", \"4G5\", \"4RT\", \"0iz\", \"JH\", \"dd\", \"0GV\", \"48p\", \"5y8\", \"521\", \"47q\", \"0HW\", \"ke\", \n\"EI\", \"12R\", \"5Mt\", \"4H4\", \"6eF\", \"4Pe\", \"0kK\", \"Hy\", \"fU\", \"s4\", \"5nh\", \"6Kj\", \"54b\", \"6Uh\", \"xW\", \"m6\", \"01A\", \"3PK\", \"7ke\", \"4Ng\", \n\"4CW\", \"4V6\", \"191\", \"0xy\", \"0VU\", \"1o\", \"6XX\", \"59R\", \"4bz\", \"6WY\", \"zf\", \"0YT\", \"03p\", \"TJ\", \"4Y7\", \"4LV\", \"4Af\", \"60M\", \"Yz\", \"0zH\", \n\"b7\", \"wV\", \"6Zi\", \"4oJ\", \"5H3\", \"4Ms\", \"02U\", \"Uo\", \"2mR\", \"0Xq\", \"57v\", \"426\", \"7Km\", \"4no\", \"0UA\", \"vs\", \"2NN\", \"1kL\", \"5Pb\", \"61h\", \n\"6za\", \"4OB\", \"00d\", \"2AO\", \"yr\", \"1Ja\", \"4an\", \"6TM\", \"a7g\", \"58w\", \"0Wp\", \"0J\", \"Zn\", \"84L\", \"4Br\", \"5G2\", \"5LQ\", \"5Y0\", \"d\", \"13w\", \n\"0Ir\", \"1L2\", \"amm\", \"46T\", \"5oM\", \"6JO\", \"gp\", \"0DB\", \"0jn\", \"3Ol\", \"6dc\", \"5Aa\", \"bdr\", \"6kb\", \"2PL\", \"0eo\", \"0KC\", \"hq\", \"6EN\", \"44e\", \n\"49U\", \"abl\", \"eA\", \"0Fs\", \"8aG\", \"Km\", \"5V1\", \"4Sq\", \"1Dz\", \"3a\", \"5j5\", \"4ou\", \"4AY\", \"4T8\", \"YE\", \"0zw\", \"03O\", \"Tu\", \"6yJ\", \"4Li\", \n\"4bE\", \"6Wf\", \"zY\", \"o8\", \"0Vj\", \"1P\", \"6Xg\", \"4mD\", \"4Ch\", \"62C\", \"2Me\", \"0xF\", \"0uv\", \"VD\", \"7kZ\", \"4NX\", \"5pU\", \"5e4\", \"xh\", \"3k9\", \n\"fj\", \"0EX\", \"5nW\", \"6KU\", \"6ey\", \"4PZ\", \"0kt\", \"HF\", \"Ev\", \"0fD\", \"5MK\", \"6hI\", \"6Fe\", \"47N\", \"0Hh\", \"kZ\", \"26B\", \"52\", \"48O\", \"6Id\", \n\"6gH\", \"4Rk\", \"0iE\", \"Jw\", \"GG\", \"0du\", \"5Oz\", \"6jx\", \"5t7\", \"4qw\", \"0JY\", \"ik\", \"2mV\", \"0Xu\", \"57r\", \"422\", \"5H7\", \"4Mw\", \"02Q\", \"Uk\", \n\"2NJ\", \"1kH\", \"5Pf\", \"61l\", \"7Ki\", \"4nk\", \"0UE\", \"vw\", \"yv\", \"0ZD\", \"4aj\", \"6TI\", \"6ze\", \"4OF\", \"0th\", \"WZ\", \"Zj\", \"0yX\", \"4Bv\", \"5G6\", \n\"6Yy\", \"4lZ\", \"0Wt\", \"0N\", \"0Iv\", \"jD\", \"4g9\", \"46P\", \"5LU\", \"5Y4\", \"Dh\", \"0gZ\", \"0jj\", \"IX\", \"6dg\", \"4QD\", \"5oI\", \"6JK\", \"gt\", \"0DF\", \n\"0KG\", \"hu\", \"6EJ\", \"44a\", \"5Nd\", \"6kf\", \"FY\", \"S8\", \"1xz\", \"Ki\", \"5V5\", \"4Su\", \"49Q\", \"4h8\", \"eE\", \"0Fw\", \"756\", \"60v\", \"YA\", \"0zs\", \n\"9Mf\", \"3e\", \"5j1\", \"4oq\", \"4bA\", \"6Wb\", \"2lL\", \"0Yo\", \"03K\", \"Tq\", \"6yN\", \"4Lm\", \"4Cl\", \"62G\", \"2Ma\", \"0xB\", \"0Vn\", \"1T\", \"6Xc\", \"59i\", \n\"54Y\", \"5e0\", \"xl\", \"8RF\", \"01z\", \"1p2\", \"aQm\", \"b0g\", \"71T\", \"bjm\", \"0kp\", \"HB\", \"fn\", \"317\", \"5nS\", \"6KQ\", \"6Fa\", \"47J\", \"0Hl\", \"29G\", \n\"Er\", \"12i\", \"5MO\", \"6hM\", \"6gL\", \"4Ro\", \"0iA\", \"Js\", \"26F\", \"56\", \"48K\", \"7YA\", \"5t3\", \"4qs\", \"8CE\", \"io\", \"GC\", \"0dq\", \"bel\", \"hYW\", \n\"7Ke\", \"4ng\", \"0UI\", \"2s\", \"XW\", \"M6\", \"5Pj\", \"6uh\", \"6xX\", \"6m9\", \"0vU\", \"Ug\", \"2mZ\", \"0Xy\", \"4cW\", \"4v6\", \"4y7\", \"4lV\", \"0Wx\", \"0B\", \n\"Zf\", \"0yT\", \"4Bz\", \"63Q\", \"6zi\", \"4OJ\", \"B7\", \"WV\", \"yz\", \"0ZH\", \"4af\", \"6TE\", \"5oE\", \"6JG\", \"gx\", \"0DJ\", \"0jf\", \"IT\", \"6dk\", \"4QH\", \n\"5LY\", \"5Y8\", \"l\", \"0gV\", \"0Iz\", \"jH\", \"4g5\", \"4rT\", \"5mt\", \"4h4\", \"eI\", \"1VZ\", \"0hW\", \"Ke\", \"5V9\", \"4Sy\", \"5Nh\", \"6kj\", \"FU\", \"S4\", \n\"0KK\", \"hy\", \"6EF\", \"44m\", \"03G\", \"2Bl\", \"6yB\", \"4La\", \"4bM\", \"6Wn\", \"zQ\", \"o0\", \"0TS\", \"3i\", \"a4D\", \"bUN\", \"4AQ\", \"4T0\", \"YM\", \"87o\", \n\"01v\", \"VL\", \"7kR\", \"4NP\", \"54U\", \"hft\", \"0N3\", \"1Ks\", \"0Vb\", \"1X\", \"6Xo\", \"4mL\", \"5SA\", \"62K\", \"2Mm\", \"0xN\", \"2So\", \"0fL\", \"5MC\", \"6hA\", \n\"6Fm\", \"47F\", \"1XA\", \"kR\", \"fb\", \"0EP\", \"bDM\", \"aaO\", \"4E3\", \"4PR\", \"8bd\", \"HN\", \"GO\", \"10T\", \"5Or\", \"4J2\", \"507\", \"45w\", \"0JQ\", \"ic\", \n\"dS\", \"q2\", \"48G\", \"6Il\", \"73i\", \"4Rc\", \"0iM\", \"3LO\", \"XS\", \"M2\", \"5Pn\", \"61d\", \"7Ka\", \"4nc\", \"0UM\", \"2w\", \"39w\", \"8Qe\", \"4cS\", \"4v2\", \n\"aRN\", \"b3D\", \"02Y\", \"Uc\", \"Zb\", \"0yP\", \"bxM\", \"63U\", \"4y3\", \"4lR\", \"236\", \"0F\", \"2oo\", \"0ZL\", \"4ab\", \"6TA\", \"6zm\", \"4ON\", \"B3\", \"WR\", \n\"0jb\", \"IP\", \"6do\", \"4QL\", \"5oA\", \"6JC\", \"25e\", \"0DN\", \"9PG\", \"jL\", \"4g1\", \"46X\", \"686\", \"aCM\", \"h\", \"0gR\", \"0hS\", \"Ka\", \"72w\", \"677\", \n\"49Y\", \"4h0\", \"eM\", \"8Og\", \"0KO\", \"3nM\", \"6EB\", \"44i\", \"5Nl\", \"6kn\", \"FQ\", \"S0\", \"4bI\", \"6Wj\", \"zU\", \"o4\", \"03C\", \"Ty\", \"6yF\", \"4Le\", \n\"4AU\", \"4T4\", \"YI\", \"1jZ\", \"0TW\", \"3m\", \"5j9\", \"4oy\", \"54Q\", \"5e8\", \"xd\", \"1Kw\", \"01r\", \"VH\", \"7kV\", \"4NT\", \"4Cd\", \"62O\", \"2Mi\", \"0xJ\", \n\"0Vf\", \"uT\", \"6Xk\", \"4mH\", \"6Fi\", \"47B\", \"0Hd\", \"kV\", \"Ez\", \"0fH\", \"5MG\", \"6hE\", \"4E7\", \"4PV\", \"0kx\", \"HJ\", \"ff\", \"0ET\", \"bDI\", \"6KY\", \n\"503\", \"45s\", \"0JU\", \"ig\", \"GK\", \"0dy\", \"5Ov\", \"4J6\", \"6gD\", \"4Rg\", \"0iI\", \"3LK\", \"dW\", \"q6\", \"48C\", \"6Ih\", \"4Z2\", \"4OS\", \"00u\", \"WO\", \n\"yc\", \"0ZQ\", \"55V\", \"hgw\", \"6Yl\", \"4lO\", \"a2\", \"tS\", \"2Ln\", \"0yM\", \"4Bc\", \"63H\", \"6xA\", \"4Mb\", \"02D\", \"2Co\", \"2mC\", \"n3\", \"4cN\", \"6Vm\", \n\"a5G\", \"bTM\", \"0UP\", \"2j\", \"XN\", \"86l\", \"5Ps\", \"4U3\", \"5Nq\", \"4K1\", \"FL\", \"11W\", \"0KR\", \"3nP\", \"514\", \"44t\", \"49D\", \"6Ho\", \"eP\", \"49\", \n\"0hN\", \"3ML\", \"6fC\", \"5CA\", \"aV1\", \"6iB\", \"u\", \"0gO\", \"0Ic\", \"jQ\", \"6Gn\", \"46E\", \"bEN\", \"hyu\", \"ga\", \"0DS\", \"8cg\", \"IM\", \"4D0\", \"4QQ\", \n\"1FZ\", \"1A\", \"4x4\", \"4mU\", \"4Cy\", \"5F9\", \"0m6\", \"0xW\", \"C4\", \"VU\", \"7kK\", \"4NI\", \"54L\", \"6UF\", \"xy\", \"1Kj\", \"0TJ\", \"3p\", \"6ZG\", \"4od\", \n\"4AH\", \"60c\", \"YT\", \"L5\", \"0wV\", \"Td\", \"5I8\", \"4Lx\", \"4bT\", \"4w5\", \"zH\", \"0Yz\", \"dJ\", \"0Gx\", \"5lw\", \"4i7\", \"6gY\", \"4Rz\", \"0iT\", \"Jf\", \n\"GV\", \"R7\", \"5Ok\", \"6ji\", \"6DE\", \"45n\", \"0JH\", \"iz\", \"24b\", \"0EI\", \"5nF\", \"6KD\", \"6eh\", \"4PK\", \"0ke\", \"HW\", \"Eg\", \"0fU\", \"5MZ\", \"6hX\", \n\"4f6\", \"4sW\", \"0Hy\", \"kK\", \"yg\", \"0ZU\", \"55R\", \"6TX\", \"4Z6\", \"4OW\", \"00q\", \"WK\", \"2Lj\", \"0yI\", \"4Bg\", \"63L\", \"6Yh\", \"4lK\", \"a6\", \"tW\", \n\"2mG\", \"n7\", \"4cJ\", \"6Vi\", \"6xE\", \"4Mf\", \"0vH\", \"Uz\", \"XJ\", \"1kY\", \"5Pw\", \"4U7\", \"7Kx\", \"4nz\", \"0UT\", \"2n\", \"0KV\", \"hd\", \"510\", \"44p\", \n\"5Nu\", \"4K5\", \"FH\", \"0ez\", \"0hJ\", \"Kx\", \"6fG\", \"4Sd\", \"5mi\", \"6Hk\", \"eT\", \"p5\", \"0Ig\", \"jU\", \"6Gj\", \"46A\", \"5LD\", \"6iF\", \"q\", \"0gK\", \n\"1zZ\", \"II\", \"4D4\", \"4QU\", \"5oX\", \"5z9\", \"ge\", \"0DW\", \"byN\", \"62V\", \"0m2\", \"0xS\", \"225\", \"1E\", \"4x0\", \"4mQ\", \"54H\", \"6UB\", \"2nl\", \"1Kn\", \n\"C0\", \"VQ\", \"7kO\", \"4NM\", \"4AL\", \"60g\", \"YP\", \"L1\", \"0TN\", \"3t\", \"6ZC\", \"ae0\", \"4bP\", \"439\", \"zL\", \"8Pf\", \"03Z\", \"0b3\", \"aSM\", \"b2G\", \n\"73t\", \"664\", \"0iP\", \"Jb\", \"dN\", \"8Nd\", \"48Z\", \"4i3\", \"6DA\", \"45j\", \"0JL\", \"3oN\", \"GR\", \"R3\", \"5Oo\", \"6jm\", \"6el\", \"4PO\", \"0ka\", \"HS\", \n\"24f\", \"0EM\", \"5nB\", \"aaR\", \"4f2\", \"4sS\", \"8Ae\", \"kO\", \"Ec\", \"0fQ\", \"695\", \"aBN\", \"6Yd\", \"4lG\", \"0Wi\", \"0S\", \"Zw\", \"0yE\", \"4Bk\", \"6wH\", \n\"6zx\", \"buh\", \"0tu\", \"WG\", \"yk\", \"0ZY\", \"4aw\", \"5d7\", \"5k6\", \"4nv\", \"0UX\", \"2b\", \"XF\", \"1kU\", \"741\", \"61q\", \"6xI\", \"4Mj\", \"02L\", \"Uv\", \n\"2mK\", \"0Xh\", \"4cF\", \"6Ve\", \"49L\", \"6Hg\", \"eX\", \"41\", \"0hF\", \"Kt\", \"6fK\", \"4Sh\", \"5Ny\", \"4K9\", \"FD\", \"0ev\", \"0KZ\", \"hh\", \"5u4\", \"4pt\", \n\"5oT\", \"5z5\", \"gi\", \"1Tz\", \"0jw\", \"IE\", \"4D8\", \"4QY\", \"5LH\", \"6iJ\", \"Du\", \"0gG\", \"0Ik\", \"jY\", \"6Gf\", \"46M\", \"01g\", \"3Pm\", \"7kC\", \"4NA\", \n\"54D\", \"6UN\", \"xq\", \"1Kb\", \"0Vs\", \"1I\", \"a6d\", \"59t\", \"4Cq\", \"5F1\", \"d3G\", \"85O\", \"03V\", \"Tl\", \"5I0\", \"4Lp\", \"56u\", \"435\", \"2lQ\", \"0Yr\", \n\"0TB\", \"3x\", \"6ZO\", \"4ol\", \"5Qa\", \"60k\", \"2OM\", \"0zn\", \"2QO\", \"0dl\", \"5Oc\", \"6ja\", \"6DM\", \"45f\", \"1Za\", \"ir\", \"dB\", \"0Gp\", \"48V\", \"aco\", \n\"5W2\", \"4Rr\", \"94m\", \"Jn\", \"Eo\", \"12t\", \"5MR\", \"5X3\", \"aln\", \"47W\", \"0Hq\", \"kC\", \"fs\", \"0EA\", \"5nN\", \"6KL\", \"71I\", \"4PC\", \"0km\", \"3No\", \n\"Zs\", \"0yA\", \"4Bo\", \"63D\", \"7IA\", \"4lC\", \"0Wm\", \"0W\", \"yo\", \"8SE\", \"4as\", \"5d3\", \"aPn\", \"b1d\", \"00y\", \"WC\", \"XB\", \"1kQ\", \"745\", \"61u\", \n\"5k2\", \"4nr\", \"9Le\", \"2f\", \"2mO\", \"0Xl\", \"4cB\", \"6Va\", \"6xM\", \"4Mn\", \"02H\", \"Ur\", \"0hB\", \"Kp\", \"6fO\", \"4Sl\", \"49H\", \"6Hc\", \"27E\", \"45\", \n\"8BF\", \"hl\", \"518\", \"44x\", \"bdo\", \"aAm\", \"2PQ\", \"0er\", \"0js\", \"IA\", \"70W\", \"bkn\", \"5oP\", \"5z1\", \"gm\", \"304\", \"0Io\", \"28D\", \"6Gb\", \"46I\", \n\"5LL\", \"6iN\", \"y\", \"0gC\", \"5pH\", \"6UJ\", \"xu\", \"1Kf\", \"C8\", \"VY\", \"7kG\", \"4NE\", \"4Cu\", \"5F5\", \"2Mx\", \"1hz\", \"0Vw\", \"1M\", \"4x8\", \"4mY\", \n\"4bX\", \"431\", \"zD\", \"0Yv\", \"03R\", \"Th\", \"5I4\", \"4Lt\", \"4AD\", \"60o\", \"YX\", \"L9\", \"0TF\", \"wt\", \"6ZK\", \"4oh\", \"6DI\", \"45b\", \"0JD\", \"iv\", \n\"GZ\", \"0dh\", \"5Og\", \"6je\", \"5W6\", \"4Rv\", \"0iX\", \"Jj\", \"dF\", \"0Gt\", \"48R\", \"6Iy\", \"6Fx\", \"47S\", \"0Hu\", \"kG\", \"Ek\", \"0fY\", \"5MV\", \"5X7\", \n\"6ed\", \"4PG\", \"0ki\", \"3Nk\", \"fw\", \"0EE\", \"5nJ\", \"6KH\", \"356\", \"bo\", \"6OP\", \"4zs\", \"bnl\", \"75U\", \"LC\", \"0oq\", \"0bA\", \"As\", \"6lL\", \"4Yo\", \n\"43K\", \"7RA\", \"2yN\", \"0Lm\", \"17\", \"22G\", \"6Ma\", \"4xB\", \"4Vn\", \"6cM\", \"Nr\", \"19i\", \"14Y\", \"CB\", \"aDo\", \"bam\", \"41z\", \"5p2\", \"mn\", \"8GD\", \n\"7d\", \"8YF\", \"4kp\", \"5n0\", \"64w\", \"717\", \"1nS\", \"2KQ\", \"Pp\", \"07J\", \"4Hl\", \"69G\", \"6Sc\", \"52i\", \"1MO\", \"2hM\", \"5U\", \"0Ro\", \"4iA\", \"7LC\", \n\"66F\", \"4Gm\", \"08K\", \"3YA\", \"RA\", \"0qs\", \"b4f\", \"aUl\", \"5a1\", \"4dq\", \"8VG\", \"8e\", \"6mV\", \"4Xu\", \"17r\", \"022\", \"nE\", \"0Mw\", \"42Q\", \"4c8\", \n\"6NJ\", \"5kH\", \"1Pf\", \"cu\", \"MY\", \"X8\", \"4UE\", \"74O\", \"6og\", \"4ZD\", \"W9\", \"BX\", \"lt\", \"0OF\", \"4th\", \"6AK\", \"4l9\", \"4yX\", \"0Bv\", \"aD\", \n\"Oh\", \"0lZ\", \"4Wt\", \"5R4\", \"4Iv\", \"5L6\", \"Qj\", \"06P\", \"1LU\", \"1Y4\", \"463\", \"4gZ\", \"4jj\", \"7Oh\", \"rv\", \"0QD\", \"1oI\", \"2JK\", \"65m\", \"4DF\", \n\"4KG\", \"7nE\", \"2EJ\", \"04a\", \"1Nd\", \"2kf\", \"6PH\", \"4ek\", \"5xz\", \"492\", \"4O\", \"0Su\", \"09Q\", \"0h8\", \"5C7\", \"4Fw\", \"5Dz\", \"6ax\", \"LG\", \"0ou\", \n\"0AY\", \"bk\", \"6OT\", \"4zw\", \"43O\", \"6Bd\", \"2yJ\", \"0Li\", \"0bE\", \"Aw\", \"6lH\", \"4Yk\", \"4Vj\", \"6cI\", \"Nv\", \"0mD\", \"13\", \"22C\", \"6Me\", \"4xF\", \n\"4uv\", \"5p6\", \"mj\", \"0NX\", \"1pU\", \"CF\", \"6ny\", \"7k9\", \"4P9\", \"4EX\", \"1nW\", \"2KU\", \"sh\", \"0PZ\", \"4kt\", \"5n4\", \"6Sg\", \"4fD\", \"k9\", \"2hI\", \n\"Pt\", \"07N\", \"4Hh\", \"69C\", \"66B\", \"4Gi\", \"08O\", \"2Id\", \"5Q\", \"d8\", \"4iE\", \"7LG\", \"5a5\", \"4du\", \"1Oz\", \"8a\", \"RE\", \"0qw\", \"4JY\", \"aUh\", \n\"nA\", \"0Ms\", \"42U\", \"ail\", \"6mR\", \"4Xq\", \"17v\", \"026\", \"3Km\", \"0no\", \"4UA\", \"74K\", \"6NN\", \"5kL\", \"1Pb\", \"cq\", \"lp\", \"0OB\", \"40d\", \"6AO\", \n\"6oc\", \"5Ja\", \"0an\", \"2TM\", \"Ol\", \"18w\", \"4Wp\", \"5R0\", \"afm\", \"bCo\", \"0Br\", \"1G2\", \"1LQ\", \"1Y0\", \"467\", \"53w\", \"4Ir\", \"5L2\", \"Qn\", \"06T\", \n\"1oM\", \"2JO\", \"65i\", \"4DB\", \"4jn\", \"7Ol\", \"6z\", \"1Aa\", \"8WY\", \"2kb\", \"6PL\", \"4eo\", \"4KC\", \"7nA\", \"2EN\", \"04e\", \"09U\", \"d6E\", \"5C3\", \"4Fs\", \n\"bRl\", \"496\", \"4K\", \"0Sq\", \"0bI\", \"2Wj\", \"6lD\", \"4Yg\", \"43C\", \"6Bh\", \"oW\", \"z6\", \"0AU\", \"bg\", \"6OX\", \"5jZ\", \"4TW\", \"4A6\", \"LK\", \"0oy\", \n\"14Q\", \"CJ\", \"4N7\", \"5Kw\", \"41r\", \"542\", \"mf\", \"0NT\", \"u7\", \"22O\", \"6Mi\", \"4xJ\", \"4Vf\", \"6cE\", \"Nz\", \"0mH\", \"Px\", \"07B\", \"4Hd\", \"69O\", \n\"6Sk\", \"4fH\", \"k5\", \"2hE\", \"7l\", \"0PV\", \"4kx\", \"5n8\", \"4P5\", \"4ET\", \"83j\", \"2KY\", \"RI\", \"05s\", \"4JU\", \"7oW\", \"5a9\", \"4dy\", \"1Ov\", \"8m\", \n\"qU\", \"d4\", \"4iI\", \"7LK\", \"66N\", \"4Ge\", \"08C\", \"2Ih\", \"6NB\", \"a59\", \"1Pn\", \"21d\", \"MQ\", \"X0\", \"4UM\", \"74G\", \"79w\", \"bbN\", \"0cS\", \"0v2\", \n\"nM\", \"8Dg\", \"42Y\", \"4c0\", \"4l1\", \"4yP\", \"8Kf\", \"aL\", \"0y3\", \"0lR\", \"636\", \"76v\", \"6oo\", \"4ZL\", \"W1\", \"BP\", \"2zm\", \"0ON\", \"40h\", \"6AC\", \n\"4jb\", \"auS\", \"6v\", \"0QL\", \"I3\", \"2JC\", \"65e\", \"4DN\", \"b7E\", \"68U\", \"Qb\", \"06X\", \"286\", \"dSl\", \"4r3\", \"4gR\", \"4hS\", \"7MQ\", \"4G\", \"277\", \n\"09Y\", \"0h0\", \"67T\", \"b8D\", \"4KO\", \"7nM\", \"SS\", \"F2\", \"1Nl\", \"9w\", \"azR\", \"4ec\", \"43G\", \"6Bl\", \"oS\", \"z2\", \"0bM\", \"2Wn\", \"78i\", \"4Yc\", \n\"4TS\", \"4A2\", \"LO\", \"8fe\", \"0AQ\", \"bc\", \"aeN\", \"cPm\", \"41v\", \"546\", \"mb\", \"0NP\", \"14U\", \"CN\", \"4N3\", \"5Ks\", \"4Vb\", \"6cA\", \"2Xo\", \"0mL\", \n\"u3\", \"22K\", \"6Mm\", \"4xN\", \"6So\", \"4fL\", \"k1\", \"2hA\", \"2Fm\", \"07F\", \"5XA\", \"69K\", \"4P1\", \"4EP\", \"83n\", \"d5f\", \"7h\", \"0PR\", \"bQO\", \"a0E\", \n\"hbu\", \"50T\", \"1Or\", \"8i\", \"RM\", \"05w\", \"4JQ\", \"7oS\", \"66J\", \"4Ga\", \"08G\", \"2Il\", \"5Y\", \"d0\", \"4iM\", \"7LO\", \"MU\", \"X4\", \"4UI\", \"74C\", \n\"6NF\", \"5kD\", \"1Pj\", \"cy\", \"nI\", \"2m9\", \"4vU\", \"4c4\", \"6mZ\", \"4Xy\", \"0cW\", \"0v6\", \"Od\", \"0lV\", \"4Wx\", \"5R8\", \"4l5\", \"4yT\", \"0Bz\", \"aH\", \n\"lx\", \"0OJ\", \"40l\", \"6AG\", \"6ok\", \"4ZH\", \"W5\", \"BT\", \"I7\", \"2JG\", \"65a\", \"4DJ\", \"4jf\", \"7Od\", \"6r\", \"0QH\", \"1LY\", \"1Y8\", \"4r7\", \"4gV\", \n\"4Iz\", \"68Q\", \"Qf\", \"0rT\", \"1mt\", \"0h4\", \"67P\", \"5VZ\", \"4hW\", \"7MU\", \"4C\", \"0Sy\", \"1Nh\", \"9s\", \"6PD\", \"4eg\", \"4KK\", \"7nI\", \"SW\", \"F6\", \n\"8Je\", \"22V\", \"4m2\", \"4xS\", \"625\", \"77u\", \"Nc\", \"0mQ\", \"V2\", \"CS\", \"6nl\", \"5Kn\", \"41k\", \"7Pa\", \"3kO\", \"0NM\", \"0AL\", \"20g\", \"6OA\", \"4zb\", \n\"4TN\", \"6am\", \"LR\", \"Y3\", \"0bP\", \"Ab\", \"78t\", \"bcM\", \"43Z\", \"4b3\", \"oN\", \"8Ed\", \"5D\", \"264\", \"4iP\", \"489\", \"66W\", \"b9G\", \"08Z\", \"0i3\", \n\"RP\", \"G1\", \"4JL\", \"7oN\", \"6QC\", \"50I\", \"1Oo\", \"8t\", \"7u\", \"0PO\", \"4ka\", \"7Nc\", \"64f\", \"4EM\", \"H0\", \"963\", \"Pa\", \"0sS\", \"b6F\", \"69V\", \n\"478\", \"4fQ\", \"295\", \"dRo\", \"4O4\", \"4ZU\", \"15R\", \"BI\", \"le\", \"0OW\", \"40q\", \"551\", \"6Lj\", \"4yI\", \"t4\", \"aU\", \"Oy\", \"0lK\", \"4We\", \"6bF\", \n\"6mG\", \"4Xd\", \"0cJ\", \"2Vi\", \"nT\", \"0Mf\", \"4vH\", \"6Ck\", \"adI\", \"5kY\", \"1Pw\", \"cd\", \"MH\", \"0nz\", \"4UT\", \"7pV\", \"4KV\", \"7nT\", \"SJ\", \"04p\", \n\"1Nu\", \"9n\", \"6PY\", \"4ez\", \"4hJ\", \"7MH\", \"pV\", \"e7\", \"1mi\", \"2Hk\", \"67M\", \"4Ff\", \"4Ig\", \"68L\", \"2Gj\", \"06A\", \"j6\", \"2iF\", \"6Rh\", \"4gK\", \n\"5zZ\", \"7Oy\", \"6o\", \"0QU\", \"1oX\", \"1z9\", \"4Q6\", \"4DW\", \"5FZ\", \"6cX\", \"Ng\", \"0mU\", \"0Cy\", \"1F9\", \"4m6\", \"4xW\", \"41o\", \"7Pe\", \"3kK\", \"0NI\", \n\"V6\", \"CW\", \"6nh\", \"5Kj\", \"4TJ\", \"6ai\", \"LV\", \"Y7\", \"0AH\", \"bz\", \"6OE\", \"4zf\", \"4wV\", \"4b7\", \"oJ\", \"0Lx\", \"0bT\", \"Af\", \"6lY\", \"4Yz\", \n\"5B8\", \"4Gx\", \"1lw\", \"0i7\", \"qH\", \"0Rz\", \"4iT\", \"7LV\", \"6QG\", \"4dd\", \"1Ok\", \"8p\", \"RT\", \"G5\", \"4JH\", \"7oJ\", \"64b\", \"4EI\", \"H4\", \"2KD\", \n\"7q\", \"0PK\", \"4ke\", \"7Ng\", \"4s4\", \"4fU\", \"1MZ\", \"2hX\", \"Pe\", \"0sW\", \"4Hy\", \"5M9\", \"la\", \"0OS\", \"40u\", \"555\", \"4O0\", \"4ZQ\", \"15V\", \"BM\", \n\"2Yl\", \"0lO\", \"4Wa\", \"6bB\", \"6Ln\", \"4yM\", \"08\", \"aQ\", \"nP\", \"0Mb\", \"42D\", \"6Co\", \"6mC\", \"5HA\", \"0cN\", \"2Vm\", \"ML\", \"8gf\", \"4UP\", \"74Z\", \n\"adM\", \"bAO\", \"1Ps\", \"0U3\", \"1Nq\", \"9j\", \"azO\", \"51W\", \"4KR\", \"7nP\", \"SN\", \"04t\", \"09D\", \"2Ho\", \"67I\", \"4Fb\", \"4hN\", \"7ML\", \"4Z\", \"e3\", \n\"j2\", \"2iB\", \"6Rl\", \"4gO\", \"4Ic\", \"68H\", \"2Gn\", \"06E\", \"82m\", \"d4e\", \"4Q2\", \"4DS\", \"bPL\", \"a1F\", \"6k\", \"0QQ\", \"1pH\", \"2UJ\", \"6nd\", \"5Kf\", \n\"41c\", \"7Pi\", \"mw\", \"0NE\", \"0Cu\", \"1F5\", \"6Mx\", \"5hz\", \"4Vw\", \"5S7\", \"Nk\", \"0mY\", \"0bX\", \"Aj\", \"6lU\", \"4Yv\", \"43R\", \"6By\", \"oF\", \"0Lt\", \n\"0AD\", \"bv\", \"6OI\", \"4zj\", \"4TF\", \"6ae\", \"LZ\", \"0oh\", \"RX\", \"G9\", \"4JD\", \"7oF\", \"6QK\", \"4dh\", \"1Og\", \"2je\", \"5L\", \"0Rv\", \"4iX\", \"481\", \n\"5B4\", \"4Gt\", \"08R\", \"2Iy\", \"Pi\", \"07S\", \"4Hu\", \"5M5\", \"470\", \"4fY\", \"1MV\", \"1X7\", \"su\", \"0PG\", \"4ki\", \"7Nk\", \"64n\", \"4EE\", \"H8\", \"2KH\", \n\"6Lb\", \"4yA\", \"04\", \"23D\", \"Oq\", \"0lC\", \"4Wm\", \"6bN\", \"aEl\", \"c4G\", \"0as\", \"BA\", \"lm\", \"8FG\", \"40y\", \"559\", \"6NS\", \"5kQ\", \"345\", \"cl\", \n\"1k2\", \"0nr\", \"boo\", \"74V\", \"6mO\", \"4Xl\", \"0cB\", \"2Va\", \"2xM\", \"0Mn\", \"42H\", \"6Cc\", \"4hB\", \"aws\", \"4V\", \"0Sl\", \"09H\", \"2Hc\", \"67E\", \"4Fn\", \n\"b5e\", \"aTo\", \"SB\", \"04x\", \"8WD\", \"9f\", \"6PQ\", \"4er\", \"4js\", \"5o3\", \"6g\", \"8XE\", \"1oP\", \"1z1\", \"65t\", \"704\", \"4Io\", \"68D\", \"Qs\", \"06I\", \n\"1LL\", \"2iN\", \"7BA\", \"4gC\", \"41g\", \"7Pm\", \"ms\", \"0NA\", \"14D\", \"2UN\", \"aDr\", \"5Kb\", \"4Vs\", \"5S3\", \"No\", \"19t\", \"0Cq\", \"1F1\", \"agn\", \"bBl\", \n\"43V\", \"aho\", \"oB\", \"0Lp\", \"16u\", \"An\", \"6lQ\", \"4Yr\", \"4TB\", \"6aa\", \"2ZO\", \"0ol\", \"1Qa\", \"br\", \"6OM\", \"4zn\", \"6QO\", \"4dl\", \"1Oc\", \"8x\", \n\"2DM\", \"05f\", \"5Za\", \"7oB\", \"5B0\", \"4Gp\", \"08V\", \"d7F\", \"5H\", \"0Rr\", \"bSo\", \"485\", \"474\", \"52t\", \"1MR\", \"1X3\", \"Pm\", \"07W\", \"4Hq\", \"5M1\", \n\"64j\", \"4EA\", \"1nN\", \"2KL\", \"7y\", \"0PC\", \"4km\", \"7No\", \"Ou\", \"0lG\", \"4Wi\", \"6bJ\", \"6Lf\", \"4yE\", \"00\", \"aY\", \"li\", \"8FC\", \"4tu\", \"5q5\", \n\"4O8\", \"4ZY\", \"0aw\", \"BE\", \"MD\", \"0nv\", \"4UX\", \"74R\", \"6NW\", \"5kU\", \"341\", \"ch\", \"nX\", \"0Mj\", \"42L\", \"6Cg\", \"6mK\", \"4Xh\", \"0cF\", \"2Ve\", \n\"09L\", \"2Hg\", \"67A\", \"4Fj\", \"4hF\", \"7MD\", \"4R\", \"0Sh\", \"1Ny\", \"9b\", \"6PU\", \"4ev\", \"4KZ\", \"7nX\", \"SF\", \"0pt\", \"1oT\", \"1z5\", \"65p\", \"5Tz\", \n\"4jw\", \"5o7\", \"6c\", \"0QY\", \"1LH\", \"2iJ\", \"6Rd\", \"4gG\", \"4Ik\", \"7li\", \"Qw\", \"06M\", \"7F\", \"246\", \"4kR\", \"7NP\", \"64U\", \"col\", \"1nq\", \"0k1\", \n\"PR\", \"E3\", \"4HN\", \"69e\", \"6SA\", \"4fb\", \"1Mm\", \"2ho\", \"5w\", \"0RM\", \"4ic\", \"7La\", \"66d\", \"4GO\", \"J2\", \"2IB\", \"Rc\", \"05Y\", \"b4D\", \"aUN\", \n\"4q2\", \"4dS\", \"8Ve\", \"8G\", \"8Hg\", \"bM\", \"4o0\", \"4zQ\", \"607\", \"75w\", \"La\", \"0oS\", \"T0\", \"AQ\", \"6ln\", \"4YM\", \"43i\", \"6BB\", \"2yl\", \"0LO\", \n\"0CN\", \"22e\", \"6MC\", \"5hA\", \"4VL\", \"6co\", \"NP\", \"0mb\", \"1ps\", \"0u3\", \"aDM\", \"baO\", \"41X\", \"7PR\", \"mL\", \"8Gf\", \"4IT\", \"7lV\", \"QH\", \"06r\", \n\"1Lw\", \"0I7\", \"5b8\", \"4gx\", \"4jH\", \"7OJ\", \"rT\", \"g5\", \"1ok\", \"2Ji\", \"65O\", \"4Dd\", \"4Ke\", \"7ng\", \"Sy\", \"04C\", \"h4\", \"2kD\", \"6Pj\", \"4eI\", \n\"4hy\", \"5m9\", \"4m\", \"0SW\", \"09s\", \"2HX\", \"4S4\", \"4FU\", \"4M6\", \"4XW\", \"0cy\", \"1f9\", \"ng\", \"0MU\", \"42s\", \"573\", \"6Nh\", \"5kj\", \"v6\", \"cW\", \n\"3KK\", \"0nI\", \"4Ug\", \"74m\", \"6oE\", \"4Zf\", \"0aH\", \"Bz\", \"lV\", \"y7\", \"40B\", \"6Ai\", \"582\", \"4yz\", \"0BT\", \"af\", \"OJ\", \"0lx\", \"4WV\", \"4B7\", \n\"64Q\", \"4Ez\", \"1nu\", \"0k5\", \"7B\", \"0Px\", \"4kV\", \"7NT\", \"6SE\", \"4ff\", \"1Mi\", \"2hk\", \"PV\", \"E7\", \"4HJ\", \"69a\", \"6rh\", \"4GK\", \"J6\", \"2IF\", \n\"5s\", \"0RI\", \"4ig\", \"7Le\", \"4q6\", \"4dW\", \"1OX\", \"8C\", \"Rg\", \"0qU\", \"5ZZ\", \"7oy\", \"4Ty\", \"5Q9\", \"Le\", \"0oW\", \"1QZ\", \"bI\", \"4o4\", \"4zU\", \n\"43m\", \"6BF\", \"oy\", \"0LK\", \"T4\", \"AU\", \"6lj\", \"4YI\", \"4VH\", \"6ck\", \"NT\", \"0mf\", \"0CJ\", \"22a\", \"6MG\", \"4xd\", \"4uT\", \"7PV\", \"mH\", \"0Nz\", \n\"1pw\", \"Cd\", \"aDI\", \"5KY\", \"1Ls\", \"0I3\", \"axM\", \"53U\", \"4IP\", \"7lR\", \"QL\", \"06v\", \"1oo\", \"2Jm\", \"65K\", \"5TA\", \"4jL\", \"7ON\", \"6X\", \"g1\", \n\"h0\", \"9Y\", \"6Pn\", \"4eM\", \"4Ka\", \"7nc\", \"2El\", \"04G\", \"09w\", \"d6g\", \"4S0\", \"4FQ\", \"bRN\", \"a3D\", \"4i\", \"0SS\", \"nc\", \"0MQ\", \"42w\", \"577\", \n\"4M2\", \"4XS\", \"17T\", \"dlm\", \"3KO\", \"0nM\", \"4Uc\", \"74i\", \"6Nl\", \"5kn\", \"v2\", \"cS\", \"lR\", \"y3\", \"40F\", \"6Am\", \"6oA\", \"4Zb\", \"0aL\", \"2To\", \n\"ON\", \"18U\", \"4WR\", \"4B3\", \"586\", \"bCM\", \"0BP\", \"ab\", \"PZ\", \"0sh\", \"4HF\", \"69m\", \"6SI\", \"4fj\", \"1Me\", \"2hg\", \"7N\", \"0Pt\", \"4kZ\", \"7NX\", \n\"6pU\", \"4Ev\", \"1ny\", \"0k9\", \"Rk\", \"05Q\", \"4Jw\", \"5O7\", \"452\", \"50r\", \"1OT\", \"8O\", \"qw\", \"0RE\", \"4ik\", \"7Li\", \"66l\", \"4GG\", \"08a\", \"2IJ\", \n\"T8\", \"AY\", \"6lf\", \"4YE\", \"43a\", \"6BJ\", \"ou\", \"0LG\", \"0Aw\", \"bE\", \"4o8\", \"4zY\", \"4Tu\", \"5Q5\", \"Li\", \"8fC\", \"14s\", \"Ch\", \"6nW\", \"5KU\", \n\"41P\", \"7PZ\", \"mD\", \"0Nv\", \"0CF\", \"22m\", \"6MK\", \"4xh\", \"4VD\", \"6cg\", \"NX\", \"0mj\", \"5za\", \"7OB\", \"6T\", \"0Qn\", \"1oc\", \"2Ja\", \"65G\", \"4Dl\", \n\"b7g\", \"68w\", \"1w2\", \"06z\", \"8UF\", \"dSN\", \"5b0\", \"4gp\", \"4hq\", \"5m1\", \"4e\", \"8ZG\", \"1mR\", \"1x3\", \"67v\", \"726\", \"4Km\", \"7no\", \"Sq\", \"04K\", \n\"1NN\", \"9U\", \"6Pb\", \"4eA\", \"adr\", \"5kb\", \"26\", \"21F\", \"Ms\", \"0nA\", \"4Uo\", \"74e\", \"79U\", \"bbl\", \"0cq\", \"1f1\", \"no\", \"396\", \"4vs\", \"5s3\", \n\"6LQ\", \"4yr\", \"367\", \"an\", \"OB\", \"0lp\", \"bmm\", \"76T\", \"6oM\", \"4Zn\", \"15i\", \"Br\", \"2zO\", \"0Ol\", \"40J\", \"6Aa\", \"6SM\", \"4fn\", \"1Ma\", \"2hc\", \n\"2FO\", \"07d\", \"4HB\", \"69i\", \"64Y\", \"4Er\", \"83L\", \"d5D\", \"7J\", \"0Pp\", \"bQm\", \"a0g\", \"456\", \"50v\", \"1OP\", \"8K\", \"Ro\", \"05U\", \"4Js\", \"5O3\", \n\"66h\", \"4GC\", \"08e\", \"2IN\", \"qs\", \"0RA\", \"4io\", \"7Lm\", \"43e\", \"6BN\", \"oq\", \"0LC\", \"0bo\", \"2WL\", \"6lb\", \"4YA\", \"4Tq\", \"5Q1\", \"Lm\", \"8fG\", \n\"0As\", \"bA\", \"ael\", \"cPO\", \"41T\", \"ajm\", \"1K2\", \"0Nr\", \"14w\", \"Cl\", \"6nS\", \"5KQ\", \"5Fa\", \"6cc\", \"2XM\", \"0mn\", \"0CB\", \"22i\", \"6MO\", \"4xl\", \n\"1og\", \"2Je\", \"65C\", \"4Dh\", \"4jD\", \"7OF\", \"6P\", \"g9\", \"3l9\", \"2iy\", \"5b4\", \"4gt\", \"4IX\", \"68s\", \"QD\", \"0rv\", \"1mV\", \"1x7\", \"4S8\", \"4FY\", \n\"4hu\", \"5m5\", \"4a\", \"1Cz\", \"h8\", \"9Q\", \"6Pf\", \"4eE\", \"4Ki\", \"7nk\", \"Su\", \"04O\", \"Mw\", \"0nE\", \"4Uk\", \"74a\", \"6Nd\", \"5kf\", \"22\", \"21B\", \n\"nk\", \"0MY\", \"4vw\", \"5s7\", \"6mx\", \"5Hz\", \"0cu\", \"1f5\", \"OF\", \"0lt\", \"4WZ\", \"6by\", \"6LU\", \"4yv\", \"0BX\", \"aj\", \"lZ\", \"0Oh\", \"40N\", \"6Ae\", \n\"6oI\", \"4Zj\", \"0aD\", \"Bv\", \"5f\", \"9Ke\", \"4ir\", \"5l2\", \"66u\", \"735\", \"08x\", \"1y0\", \"Rr\", \"05H\", \"4Jn\", \"7ol\", \"6Qa\", \"4dB\", \"1OM\", \"8V\", \n\"7W\", \"0Pm\", \"4kC\", \"7NA\", \"64D\", \"4Eo\", \"83Q\", \"2Kb\", \"PC\", \"07y\", \"b6d\", \"69t\", \"5c3\", \"4fs\", \"8TE\", \"dRM\", \"374\", \"22t\", \"599\", \"4xq\", \n\"bln\", \"77W\", \"NA\", \"0ms\", \"14j\", \"Cq\", \"6nN\", \"5KL\", \"41I\", \"7PC\", \"3km\", \"0No\", \"35\", \"20E\", \"6Oc\", \"5ja\", \"4Tl\", \"6aO\", \"Lp\", \"0oB\", \n\"0br\", \"1g2\", \"78V\", \"bco\", \"43x\", \"568\", \"ol\", \"385\", \"4Kt\", \"5N4\", \"Sh\", \"04R\", \"1NW\", \"9L\", \"441\", \"4eX\", \"4hh\", \"7Mj\", \"pt\", \"0SF\", \n\"K9\", \"2HI\", \"67o\", \"4FD\", \"4IE\", \"68n\", \"QY\", \"0\", \"1Lf\", \"2id\", \"6RJ\", \"4gi\", \"4jY\", \"auh\", \"6M\", \"0Qw\", \"1oz\", \"2Jx\", \"5A5\", \"4Du\", \n\"6oT\", \"4Zw\", \"0aY\", \"Bk\", \"lG\", \"0Ou\", \"40S\", \"6Ax\", \"6LH\", \"4yk\", \"0BE\", \"aw\", \"2YJ\", \"0li\", \"4WG\", \"6bd\", \"6me\", \"4XF\", \"0ch\", \"2VK\", \n\"nv\", \"0MD\", \"42b\", \"6CI\", \"6Ny\", \"7K9\", \"1PU\", \"cF\", \"Mj\", \"0nX\", \"4Uv\", \"5P6\", \"66q\", \"4GZ\", \"1lU\", \"1y4\", \"5b\", \"0RX\", \"4iv\", \"5l6\", \n\"6Qe\", \"4dF\", \"1OI\", \"8R\", \"Rv\", \"05L\", \"4Jj\", \"7oh\", \"6pH\", \"4Ek\", \"1nd\", \"2Kf\", \"7S\", \"0Pi\", \"4kG\", \"7NE\", \"5c7\", \"4fw\", \"1Mx\", \"0H8\", \n\"PG\", \"0su\", \"5Xz\", \"69p\", \"4VY\", \"4C8\", \"NE\", \"0mw\", \"1Sz\", \"22p\", \"6MV\", \"4xu\", \"41M\", \"7PG\", \"mY\", \"x8\", \"14n\", \"Cu\", \"6nJ\", \"5KH\", \n\"4Th\", \"6aK\", \"Lt\", \"0oF\", \"31\", \"bX\", \"6Og\", \"4zD\", \"4wt\", \"5r4\", \"oh\", \"0LZ\", \"0bv\", \"AD\", \"4L9\", \"4YX\", \"1NS\", \"9H\", \"445\", \"51u\", \n\"4Kp\", \"5N0\", \"Sl\", \"04V\", \"09f\", \"2HM\", \"67k\", \"5Va\", \"4hl\", \"7Mn\", \"4x\", \"0SB\", \"1Lb\", \"3yA\", \"6RN\", \"4gm\", \"4IA\", \"68j\", \"2GL\", \"4\", \n\"82O\", \"d4G\", \"5A1\", \"4Dq\", \"bPn\", \"a1d\", \"6I\", \"0Qs\", \"lC\", \"0Oq\", \"40W\", \"akn\", \"6oP\", \"4Zs\", \"15t\", \"Bo\", \"2YN\", \"0lm\", \"4WC\", \"76I\", \n\"6LL\", \"4yo\", \"0BA\", \"as\", \"nr\", \"8DX\", \"42f\", \"6CM\", \"6ma\", \"4XB\", \"0cl\", \"2VO\", \"Mn\", \"8gD\", \"4Ur\", \"5P2\", \"ado\", \"bAm\", \"1PQ\", \"cB\", \n\"Rz\", \"0qH\", \"4Jf\", \"7od\", \"6Qi\", \"4dJ\", \"i7\", \"2jG\", \"5n\", \"0RT\", \"4iz\", \"7Lx\", \"4R7\", \"4GV\", \"08p\", \"1y8\", \"PK\", \"07q\", \"4HW\", \"7mU\", \n\"6SX\", \"52R\", \"1Mt\", \"0H4\", \"sW\", \"f6\", \"4kK\", \"7NI\", \"64L\", \"4Eg\", \"1nh\", \"2Kj\", \"14b\", \"Cy\", \"6nF\", \"5KD\", \"41A\", \"7PK\", \"mU\", \"x4\", \n\"0CW\", \"0V6\", \"591\", \"4xy\", \"4VU\", \"4C4\", \"NI\", \"19R\", \"0bz\", \"AH\", \"4L5\", \"4YT\", \"43p\", \"560\", \"od\", \"0LV\", \"w5\", \"bT\", \"6Ok\", \"4zH\", \n\"4Td\", \"6aG\", \"Lx\", \"0oJ\", \"5xA\", \"7Mb\", \"4t\", \"0SN\", \"K1\", \"2HA\", \"67g\", \"4FL\", \"b5G\", \"aTM\", \"0e3\", \"04Z\", \"8Wf\", \"9D\", \"449\", \"4eP\", \n\"4jQ\", \"7OS\", \"6E\", \"255\", \"1or\", \"0j2\", \"65V\", \"cno\", \"4IM\", \"68f\", \"QQ\", \"8\", \"1Ln\", \"2il\", \"6RB\", \"4ga\", \"afR\", \"4yc\", \"0BM\", \"23f\", \n\"OS\", \"Z2\", \"4WO\", \"6bl\", \"aEN\", \"c4e\", \"0aQ\", \"Bc\", \"lO\", \"8Fe\", \"4tS\", \"4a2\", \"4n3\", \"5ks\", \"8Id\", \"cN\", \"Mb\", \"0nP\", \"614\", \"74t\", \n\"6mm\", \"4XN\", \"U3\", \"2VC\", \"2xo\", \"0ML\", \"42j\", \"6CA\", \"6Qm\", \"4dN\", \"i3\", \"8Z\", \"2Do\", \"05D\", \"4Jb\", \"aUS\", \"4R3\", \"4GR\", \"08t\", \"d7d\", \n\"5j\", \"0RP\", \"bSM\", \"a2G\", \"ayN\", \"52V\", \"1Mp\", \"0H0\", \"PO\", \"07u\", \"4HS\", \"69x\", \"64H\", \"4Ec\", \"1nl\", \"2Kn\", \"sS\", \"f2\", \"4kO\", \"7NM\", \n\"41E\", \"7PO\", \"mQ\", \"x0\", \"14f\", \"2Ul\", \"6nB\", \"aQ1\", \"4VQ\", \"4C0\", \"NM\", \"19V\", \"0CS\", \"0V2\", \"595\", \"bBN\", \"43t\", \"564\", \"0Y3\", \"0LR\", \n\"16W\", \"AL\", \"4L1\", \"4YP\", \"5DA\", \"6aC\", \"2Zm\", \"0oN\", \"39\", \"bP\", \"6Oo\", \"4zL\", \"K5\", \"2HE\", \"67c\", \"4FH\", \"4hd\", \"7Mf\", \"4p\", \"0SJ\", \n\"8Wb\", \"2kY\", \"4p5\", \"4eT\", \"4Kx\", \"5N8\", \"Sd\", \"0pV\", \"1ov\", \"0j6\", \"5A9\", \"4Dy\", \"4jU\", \"7OW\", \"6A\", \"1AZ\", \"1Lj\", \"2ih\", \"6RF\", \"4ge\", \n\"4II\", \"68b\", \"QU\", \"D4\", \"OW\", \"Z6\", \"4WK\", \"6bh\", \"6LD\", \"4yg\", \"0BI\", \"23b\", \"lK\", \"0Oy\", \"4tW\", \"4a6\", \"6oX\", \"5JZ\", \"0aU\", \"Bg\", \n\"Mf\", \"0nT\", \"4Uz\", \"74p\", \"4n7\", \"5kw\", \"1PY\", \"cJ\", \"nz\", \"0MH\", \"42n\", \"6CE\", \"6mi\", \"4XJ\", \"U7\", \"2VG\", \"4MP\", \"4X1\", \"UL\", \"02v\", \n\"0XR\", \"0M3\", \"a8E\", \"57U\", \"4nL\", \"7KN\", \"2X\", \"c1\", \"1ko\", \"2Nm\", \"61K\", \"5PA\", \"4Oa\", \"6zB\", \"2Al\", \"00G\", \"l0\", \"yQ\", \"6Tn\", \"4aM\", \n\"58T\", \"a7D\", \"0i\", \"0WS\", \"84o\", \"ZM\", \"4W0\", \"4BQ\", \"4I2\", \"5Lr\", \"13T\", \"G\", \"jc\", \"0IQ\", \"46w\", \"537\", \"6Jl\", \"5on\", \"r2\", \"gS\", \n\"3OO\", \"0jM\", \"4Qc\", \"70i\", \"6kA\", \"5NC\", \"0eL\", \"2Po\", \"hR\", \"8Bx\", \"44F\", \"6Em\", \"abO\", \"49v\", \"0FP\", \"eb\", \"KN\", \"8ad\", \"4SR\", \"4F3\", \n\"3B\", \"0Tx\", \"4oV\", \"4z7\", \"60Q\", \"4Az\", \"0zT\", \"Yf\", \"TV\", \"A7\", \"4LJ\", \"6yi\", \"6WE\", \"4bf\", \"0YH\", \"zz\", \"1s\", \"0VI\", \"4mg\", \"6XD\", \n\"6vh\", \"4CK\", \"N6\", \"2MF\", \"Vg\", \"0uU\", \"6n9\", \"7ky\", \"4u6\", \"5pv\", \"1KX\", \"xK\", \"1UZ\", \"fI\", \"4k4\", \"5nt\", \"4Py\", \"5U9\", \"He\", \"0kW\", \n\"P4\", \"EU\", \"6hj\", \"5Mh\", \"47m\", \"6FF\", \"ky\", \"0HK\", \"0GJ\", \"dx\", \"6IG\", \"48l\", \"4RH\", \"6gk\", \"JT\", \"0if\", \"0dV\", \"Gd\", \"5Z8\", \"5OY\", \n\"4qT\", \"4d5\", \"iH\", \"0Jz\", \"0XV\", \"0M7\", \"5f8\", \"4cx\", \"4MT\", \"4X5\", \"UH\", \"02r\", \"1kk\", \"Xx\", \"61O\", \"5PE\", \"4nH\", \"7KJ\", \"vT\", \"c5\", \n\"l4\", \"yU\", \"6Tj\", \"4aI\", \"4Oe\", \"6zF\", \"Wy\", \"00C\", \"1iZ\", \"ZI\", \"4W4\", \"4BU\", \"4ly\", \"5i9\", \"0m\", \"0WW\", \"jg\", \"0IU\", \"46s\", \"533\", \n\"4I6\", \"5Lv\", \"0gy\", \"C\", \"3OK\", \"0jI\", \"4Qg\", \"6dD\", \"6Jh\", \"5oj\", \"r6\", \"gW\", \"hV\", \"0Kd\", \"44B\", \"6Ei\", \"6kE\", \"5NG\", \"0eH\", \"Fz\", \n\"KJ\", \"0hx\", \"4SV\", \"4F7\", \"6HY\", \"49r\", \"0FT\", \"ef\", \"60U\", \"ckl\", \"0zP\", \"Yb\", \"3F\", \"206\", \"4oR\", \"4z3\", \"6WA\", \"4bb\", \"0YL\", \"2lo\", \n\"TR\", \"A3\", \"4LN\", \"6ym\", \"62d\", \"4CO\", \"N2\", \"2MB\", \"1w\", \"0VM\", \"4mc\", \"7Ha\", \"4u2\", \"54z\", \"8Re\", \"xO\", \"Vc\", \"01Y\", \"b0D\", \"aQN\", \n\"647\", \"71w\", \"Ha\", \"0kS\", \"8Lg\", \"fM\", \"4k0\", \"5np\", \"47i\", \"6FB\", \"29d\", \"0HO\", \"P0\", \"EQ\", \"6hn\", \"5Ml\", \"4RL\", \"6go\", \"JP\", \"0ib\", \n\"0GN\", \"26e\", \"6IC\", \"48h\", \"45X\", \"4d1\", \"iL\", \"8Cf\", \"0dR\", \"0q3\", \"hYt\", \"beO\", \"4nD\", \"7KF\", \"2P\", \"c9\", \"1kg\", \"Xt\", \"61C\", \"5PI\", \n\"4MX\", \"4X9\", \"UD\", \"0vv\", \"0XZ\", \"2my\", \"5f4\", \"4ct\", \"4lu\", \"5i5\", \"0a\", \"1Gz\", \"0yw\", \"ZE\", \"4W8\", \"4BY\", \"4Oi\", \"6zJ\", \"Wu\", \"00O\", \n\"l8\", \"yY\", \"6Tf\", \"4aE\", \"6Jd\", \"5of\", \"62\", \"25B\", \"Iw\", \"0jE\", \"4Qk\", \"6dH\", \"6ix\", \"5Lz\", \"0gu\", \"O\", \"jk\", \"0IY\", \"4rw\", \"5w7\", \n\"5x6\", \"5mW\", \"0FX\", \"ej\", \"KF\", \"0ht\", \"4SZ\", \"6fy\", \"6kI\", \"5NK\", \"0eD\", \"Fv\", \"hZ\", \"93\", \"44N\", \"6Ee\", \"2BO\", \"03d\", \"4LB\", \"6ya\", \n\"6WM\", \"4bn\", \"1Ia\", \"zr\", \"3J\", \"0Tp\", \"bUm\", \"a4g\", \"5D2\", \"4Ar\", \"87L\", \"Yn\", \"Vo\", \"01U\", \"4Ns\", \"5K3\", \"416\", \"54v\", \"1KP\", \"xC\", \n\"us\", \"0VA\", \"4mo\", \"6XL\", \"62h\", \"4CC\", \"0xm\", \"2MN\", \"0fo\", \"2SL\", \"6hb\", \"bgr\", \"47e\", \"6FN\", \"kq\", \"0HC\", \"0Es\", \"fA\", \"aal\", \"bDn\", \n\"4Pq\", \"5U1\", \"Hm\", \"8bG\", \"10w\", \"Gl\", \"5Z0\", \"5OQ\", \"45T\", \"anm\", \"1O2\", \"0Jr\", \"0GB\", \"dp\", \"6IO\", \"48d\", \"5Ba\", \"6gc\", \"3Ll\", \"0in\", \n\"1kc\", \"Xp\", \"61G\", \"5PM\", \"bTs\", \"7KB\", \"2T\", \"0Un\", \"8QF\", \"39T\", \"5f0\", \"4cp\", \"797\", \"aRm\", \"1s2\", \"02z\", \"0ys\", \"ZA\", \"63v\", \"766\", \n\"4lq\", \"5i1\", \"0e\", \"9Nf\", \"0Zo\", \"2oL\", \"6Tb\", \"4aA\", \"4Om\", \"6zN\", \"Wq\", \"00K\", \"Is\", \"0jA\", \"4Qo\", \"6dL\", \"7ZA\", \"5ob\", \"66\", \"25F\", \n\"jo\", \"9Pd\", \"4rs\", \"5w3\", \"aCn\", \"bfl\", \"0gq\", \"K\", \"KB\", \"0hp\", \"bim\", \"72T\", \"5x2\", \"49z\", \"327\", \"en\", \"3nn\", \"97\", \"44J\", \"6Ea\", \n\"6kM\", \"5NO\", \"11i\", \"Fr\", \"6WI\", \"4bj\", \"0YD\", \"zv\", \"TZ\", \"0wh\", \"4LF\", \"6ye\", \"5D6\", \"4Av\", \"0zX\", \"Yj\", \"3N\", \"0Tt\", \"4oZ\", \"6Zy\", \n\"412\", \"54r\", \"1KT\", \"xG\", \"Vk\", \"01Q\", \"4Nw\", \"5K7\", \"62l\", \"4CG\", \"0xi\", \"2MJ\", \"uw\", \"0VE\", \"4mk\", \"6XH\", \"47a\", \"6FJ\", \"ku\", \"0HG\", \n\"P8\", \"EY\", \"6hf\", \"5Md\", \"4Pu\", \"5U5\", \"Hi\", \"8bC\", \"0Ew\", \"fE\", \"4k8\", \"5nx\", \"45P\", \"4d9\", \"iD\", \"0Jv\", \"0dZ\", \"Gh\", \"5Z4\", \"5OU\", \n\"4RD\", \"6gg\", \"JX\", \"0ij\", \"0GF\", \"dt\", \"6IK\", \"5lI\", \"4Op\", \"5J0\", \"Wl\", \"00V\", \"0Zr\", \"2oQ\", \"405\", \"55u\", \"4ll\", \"6YO\", \"0x\", \"0WB\", \n\"0yn\", \"2LM\", \"63k\", \"5Ra\", \"4MA\", \"6xb\", \"2CL\", \"02g\", \"0XC\", \"39I\", \"6VN\", \"4cm\", \"bTn\", \"a5d\", \"2I\", \"0Us\", \"86O\", \"Xm\", \"5E1\", \"5PP\", \n\"6kP\", \"5NR\", \"11t\", \"Fo\", \"hC\", \"0Kq\", \"44W\", \"aon\", \"6HL\", \"49g\", \"0FA\", \"es\", \"3Mo\", \"0hm\", \"4SC\", \"72I\", \"6ia\", \"5Lc\", \"0gl\", \"V\", \n\"jr\", \"1Ya\", \"46f\", \"6GM\", \"hyV\", \"bEm\", \"0Dp\", \"gB\", \"In\", \"8cD\", \"4Qr\", \"5T2\", \"1b\", \"0VX\", \"4mv\", \"5h6\", \"62q\", \"4CZ\", \"0xt\", \"2MW\", \n\"Vv\", \"01L\", \"4Nj\", \"7kh\", \"6Ue\", \"54o\", \"1KI\", \"xZ\", \"3S\", \"0Ti\", \"4oG\", \"6Zd\", \"6tH\", \"4Ak\", \"0zE\", \"Yw\", \"TG\", \"0wu\", \"780\", \"6yx\", \n\"5g7\", \"4bw\", \"0YY\", \"zk\", \"1Wz\", \"di\", \"5y5\", \"5lT\", \"4RY\", \"4G8\", \"JE\", \"0iw\", \"0dG\", \"Gu\", \"6jJ\", \"5OH\", \"45M\", \"6Df\", \"iY\", \"80\", \n\"71\", \"fX\", \"6Kg\", \"5ne\", \"4Ph\", \"6eK\", \"Ht\", \"0kF\", \"0fv\", \"ED\", \"4H9\", \"5My\", \"4st\", \"5v4\", \"kh\", \"0HZ\", \"0Zv\", \"yD\", \"401\", \"4aX\", \n\"4Ot\", \"5J4\", \"Wh\", \"00R\", \"O9\", \"ZX\", \"63o\", \"4BD\", \"4lh\", \"6YK\", \"tt\", \"0WF\", \"0XG\", \"2md\", \"6VJ\", \"4ci\", \"4ME\", \"6xf\", \"UY\", \"02c\", \n\"1kz\", \"Xi\", \"5E5\", \"5PT\", \"4nY\", \"aqh\", \"2M\", \"0Uw\", \"hG\", \"0Ku\", \"44S\", \"6Ex\", \"6kT\", \"5NV\", \"0eY\", \"Fk\", \"3Mk\", \"0hi\", \"4SG\", \"6fd\", \n\"6HH\", \"49c\", \"0FE\", \"ew\", \"jv\", \"0ID\", \"46b\", \"6GI\", \"6ie\", \"5Lg\", \"0gh\", \"R\", \"Ij\", \"0jX\", \"4Qv\", \"5T6\", \"6Jy\", \"7O9\", \"0Dt\", \"gF\", \n\"62u\", \"775\", \"0xp\", \"198\", \"1f\", \"9Oe\", \"4mr\", \"5h2\", \"6Ua\", \"54k\", \"1KM\", \"2nO\", \"Vr\", \"01H\", \"4Nn\", \"7kl\", \"60D\", \"4Ao\", \"0zA\", \"Ys\", \n\"3W\", \"0Tm\", \"4oC\", \"7JA\", \"5g3\", \"4bs\", \"8PE\", \"zo\", \"TC\", \"03y\", \"784\", \"aSn\", \"bhn\", \"73W\", \"JA\", \"0is\", \"334\", \"dm\", \"5y1\", \"48y\", \n\"45I\", \"6Db\", \"3om\", \"84\", \"0dC\", \"Gq\", \"6jN\", \"5OL\", \"4Pl\", \"6eO\", \"Hp\", \"0kB\", \"75\", \"24E\", \"6Kc\", \"5na\", \"47x\", \"528\", \"kl\", \"8AF\", \n\"0fr\", \"1c2\", \"aBm\", \"bgo\", \"4ld\", \"6YG\", \"0p\", \"0WJ\", \"O5\", \"ZT\", \"63c\", \"4BH\", \"4Ox\", \"5J8\", \"Wd\", \"0tV\", \"0Zz\", \"yH\", \"4t5\", \"4aT\", \n\"4nU\", \"7KW\", \"2A\", \"1EZ\", \"1kv\", \"Xe\", \"5E9\", \"5PX\", \"4MI\", \"6xj\", \"UU\", \"02o\", \"0XK\", \"2mh\", \"6VF\", \"4ce\", \"6HD\", \"49o\", \"0FI\", \"27b\", \n\"KW\", \"0he\", \"4SK\", \"6fh\", \"6kX\", \"5NZ\", \"0eU\", \"Fg\", \"hK\", \"0Ky\", \"4pW\", \"4e6\", \"4j7\", \"5ow\", \"0Dx\", \"gJ\", \"If\", \"0jT\", \"4Qz\", \"6dY\", \n\"6ii\", \"5Lk\", \"Q7\", \"DV\", \"jz\", \"0IH\", \"46n\", \"6GE\", \"3PN\", \"01D\", \"4Nb\", \"aQS\", \"6Um\", \"54g\", \"m3\", \"xR\", \"1j\", \"0VP\", \"59W\", \"a6G\", \n\"4V3\", \"4CR\", \"85l\", \"194\", \"TO\", \"03u\", \"4LS\", \"4Y2\", \"a9F\", \"56V\", \"0YQ\", \"zc\", \"wS\", \"b2\", \"4oO\", \"6Zl\", \"60H\", \"4Ac\", \"0zM\", \"2On\", \n\"0dO\", \"2Ql\", \"6jB\", \"aU1\", \"45E\", \"6Dn\", \"iQ\", \"88\", \"0GS\", \"da\", \"acL\", \"48u\", \"4RQ\", \"4G0\", \"JM\", \"94N\", \"12W\", \"EL\", \"4H1\", \"5Mq\", \n\"47t\", \"524\", \"29y\", \"0HR\", \"79\", \"fP\", \"6Ko\", \"5nm\", \"aZ0\", \"6eC\", \"3NL\", \"0kN\", \"O1\", \"ZP\", \"63g\", \"4BL\", \"58I\", \"6YC\", \"0t\", \"0WN\", \n\"8Sf\", \"yL\", \"409\", \"4aP\", \"b1G\", \"aPM\", \"0a3\", \"00Z\", \"1kr\", \"Xa\", \"61V\", \"bzN\", \"4nQ\", \"7KS\", \"2E\", \"215\", \"0XO\", \"2ml\", \"6VB\", \"4ca\", \n\"4MM\", \"6xn\", \"UQ\", \"02k\", \"KS\", \"0ha\", \"4SO\", \"6fl\", \"7Xa\", \"49k\", \"0FM\", \"27f\", \"hO\", \"8Be\", \"4pS\", \"4e2\", \"aAN\", \"bdL\", \"0eQ\", \"Fc\", \n\"Ib\", \"0jP\", \"654\", \"70t\", \"4j3\", \"5os\", \"8Md\", \"gN\", \"28g\", \"0IL\", \"46j\", \"6GA\", \"6im\", \"5Lo\", \"Q3\", \"Z\", \"6Ui\", \"54c\", \"m7\", \"xV\", \n\"Vz\", \"0uH\", \"4Nf\", \"7kd\", \"4V7\", \"4CV\", \"0xx\", \"190\", \"1n\", \"0VT\", \"4mz\", \"6XY\", \"6WX\", \"56R\", \"0YU\", \"zg\", \"TK\", \"03q\", \"4LW\", \"4Y6\", \n\"60L\", \"4Ag\", \"0zI\", \"2Oj\", \"wW\", \"b6\", \"4oK\", \"6Zh\", \"45A\", \"6Dj\", \"iU\", \"0Jg\", \"0dK\", \"Gy\", \"6jF\", \"5OD\", \"4RU\", \"4G4\", \"JI\", \"1yZ\", \n\"0GW\", \"de\", \"5y9\", \"48q\", \"47p\", \"520\", \"kd\", \"0HV\", \"0fz\", \"EH\", \"4H5\", \"5Mu\", \"4Pd\", \"6eG\", \"Hx\", \"0kJ\", \"s5\", \"fT\", \"6Kk\", \"5ni\", \n\"5Y1\", \"5LP\", \"13v\", \"e\", \"jA\", \"0Is\", \"46U\", \"aml\", \"6JN\", \"5oL\", \"0DC\", \"gq\", \"3Om\", \"0jo\", \"4QA\", \"6db\", \"6kc\", \"5Na\", \"0en\", \"2PM\", \n\"hp\", \"0KB\", \"44d\", \"6EO\", \"abm\", \"49T\", \"0Fr\", \"1C2\", \"Kl\", \"8aF\", \"4Sp\", \"5V0\", \"4Mr\", \"5H2\", \"Un\", \"02T\", \"0Xp\", \"2mS\", \"427\", \"57w\", \n\"4nn\", \"7Kl\", \"2z\", \"1Ea\", \"1kM\", \"2NO\", \"61i\", \"5Pc\", \"4OC\", \"7jA\", \"2AN\", \"00e\", \"0ZA\", \"ys\", \"6TL\", \"4ao\", \"58v\", \"a7f\", \"0K\", \"0Wq\", \n\"84M\", \"Zo\", \"5G3\", \"4Bs\", \"0EY\", \"fk\", \"6KT\", \"5nV\", \"bjh\", \"6ex\", \"HG\", \"0ku\", \"0fE\", \"Ew\", \"6hH\", \"5MJ\", \"47O\", \"6Fd\", \"29B\", \"0Hi\", \n\"53\", \"dZ\", \"6Ie\", \"48N\", \"4Rj\", \"6gI\", \"Jv\", \"0iD\", \"0dt\", \"GF\", \"6jy\", \"7o9\", \"4qv\", \"5t6\", \"ij\", \"0JX\", \"wh\", \"0TZ\", \"4ot\", \"5j4\", \n\"4T9\", \"4AX\", \"0zv\", \"YD\", \"Tt\", \"03N\", \"4Lh\", \"6yK\", \"6Wg\", \"4bD\", \"o9\", \"zX\", \"1Q\", \"0Vk\", \"4mE\", \"6Xf\", \"62B\", \"4Ci\", \"0xG\", \"2Md\", \n\"VE\", \"0uw\", \"4NY\", \"aQh\", \"5e5\", \"5pT\", \"1Kz\", \"xi\", \"jE\", \"0Iw\", \"46Q\", \"4g8\", \"5Y5\", \"5LT\", \"13r\", \"a\", \"IY\", \"0jk\", \"4QE\", \"6df\", \n\"6JJ\", \"5oH\", \"0DG\", \"gu\", \"ht\", \"0KF\", \"4ph\", \"6EK\", \"6kg\", \"5Ne\", \"S9\", \"FX\", \"Kh\", \"0hZ\", \"4St\", \"5V4\", \"4h9\", \"49P\", \"0Fv\", \"eD\", \n\"0Xt\", \"2mW\", \"423\", \"4cZ\", \"4Mv\", \"5H6\", \"Uj\", \"02P\", \"1kI\", \"XZ\", \"61m\", \"5Pg\", \"4nj\", \"7Kh\", \"vv\", \"0UD\", \"0ZE\", \"yw\", \"6TH\", \"4ak\", \n\"4OG\", \"6zd\", \"2AJ\", \"00a\", \"0yY\", \"Zk\", \"5G7\", \"4Bw\", \"58r\", \"6Yx\", \"0O\", \"0Wu\", \"bjl\", \"71U\", \"HC\", \"0kq\", \"316\", \"fo\", \"6KP\", \"5nR\", \n\"47K\", \"7VA\", \"29F\", \"0Hm\", \"0fA\", \"Es\", \"6hL\", \"5MN\", \"4Rn\", \"6gM\", \"Jr\", \"1ya\", \"57\", \"26G\", \"6Ia\", \"48J\", \"45z\", \"5t2\", \"in\", \"8CD\", \n\"0dp\", \"GB\", \"hYV\", \"bem\", \"60w\", \"757\", \"0zr\", \"2OQ\", \"3d\", \"9Mg\", \"4op\", \"5j0\", \"6Wc\", \"56i\", \"0Yn\", \"2lM\", \"Tp\", \"03J\", \"4Ll\", \"6yO\", \n\"62F\", \"4Cm\", \"0xC\", \"dwS\", \"1U\", \"0Vo\", \"4mA\", \"6Xb\", \"5e1\", \"54X\", \"8RG\", \"xm\", \"VA\", \"0us\", \"b0f\", \"aQl\", \"6JF\", \"5oD\", \"0DK\", \"gy\", \n\"IU\", \"0jg\", \"4QI\", \"6dj\", \"5Y9\", \"5LX\", \"0gW\", \"m\", \"jI\", \"1YZ\", \"4rU\", \"4g4\", \"4h5\", \"5mu\", \"0Fz\", \"eH\", \"Kd\", \"0hV\", \"4Sx\", \"5V8\", \n\"6kk\", \"5Ni\", \"S5\", \"FT\", \"hx\", \"0KJ\", \"44l\", \"6EG\", \"4nf\", \"7Kd\", \"2r\", \"0UH\", \"M7\", \"XV\", \"61a\", \"5Pk\", \"4Mz\", \"6xY\", \"Uf\", \"0vT\", \n\"0Xx\", \"39r\", \"4v7\", \"4cV\", \"4lW\", \"4y6\", \"0C\", \"0Wy\", \"0yU\", \"Zg\", \"63P\", \"5RZ\", \"4OK\", \"6zh\", \"WW\", \"B6\", \"0ZI\", \"2oj\", \"6TD\", \"4ag\", \n\"0fM\", \"2Sn\", \"7xa\", \"5MB\", \"47G\", \"6Fl\", \"kS\", \"0Ha\", \"0EQ\", \"fc\", \"aaN\", \"bDL\", \"4PS\", \"4E2\", \"HO\", \"8be\", \"10U\", \"GN\", \"4J3\", \"5Os\", \n\"45v\", \"506\", \"ib\", \"0JP\", \"q3\", \"dR\", \"6Im\", \"48F\", \"4Rb\", \"6gA\", \"3LN\", \"0iL\", \"2Bm\", \"03F\", \"aF0\", \"6yC\", \"6Wo\", \"4bL\", \"o1\", \"zP\", \n\"3h\", \"0TR\", \"bUO\", \"a4E\", \"4T1\", \"4AP\", \"87n\", \"YL\", \"VM\", \"01w\", \"4NQ\", \"7kS\", \"hfu\", \"54T\", \"1Kr\", \"xa\", \"1Y\", \"0Vc\", \"4mM\", \"6Xn\", \n\"62J\", \"4Ca\", \"0xO\", \"2Ml\", \"IQ\", \"0jc\", \"4QM\", \"6dn\", \"6JB\", \"a19\", \"0DO\", \"25d\", \"jM\", \"9PF\", \"46Y\", \"4g0\", \"aCL\", \"687\", \"0gS\", \"i\", \n\"3MP\", \"0hR\", \"676\", \"72v\", \"4h1\", \"49X\", \"8Of\", \"eL\", \"3nL\", \"0KN\", \"44h\", \"6EC\", \"6ko\", \"5Nm\", \"S1\", \"FP\", \"M3\", \"XR\", \"61e\", \"5Po\", \n\"4nb\", \"aqS\", \"2v\", \"0UL\", \"8Qd\", \"39v\", \"4v3\", \"4cR\", \"b3E\", \"aRO\", \"Ub\", \"02X\", \"0yQ\", \"Zc\", \"63T\", \"bxL\", \"4lS\", \"4y2\", \"0G\", \"237\", \n\"0ZM\", \"2on\", \"7Da\", \"4ac\", \"4OO\", \"6zl\", \"WS\", \"B2\", \"47C\", \"6Fh\", \"kW\", \"0He\", \"0fI\", \"2Sj\", \"6hD\", \"5MF\", \"4PW\", \"4E6\", \"HK\", \"0ky\", \n\"0EU\", \"fg\", \"6KX\", \"5nZ\", \"45r\", \"502\", \"if\", \"0JT\", \"0dx\", \"GJ\", \"4J7\", \"5Ow\", \"4Rf\", \"6gE\", \"Jz\", \"0iH\", \"q7\", \"dV\", \"6Ii\", \"48B\", \n\"6Wk\", \"4bH\", \"o5\", \"zT\", \"Tx\", \"03B\", \"4Ld\", \"6yG\", \"4T5\", \"4AT\", \"0zz\", \"YH\", \"3l\", \"0TV\", \"4ox\", \"5j8\", \"5e9\", \"54P\", \"1Kv\", \"xe\", \n\"VI\", \"01s\", \"4NU\", \"7kW\", \"62N\", \"4Ce\", \"0xK\", \"2Mh\", \"uU\", \"0Vg\", \"4mI\", \"6Xj\", \"4K0\", \"5Np\", \"11V\", \"FM\", \"ha\", \"0KS\", \"44u\", \"515\", \n\"6Hn\", \"49E\", \"48\", \"eQ\", \"3MM\", \"0hO\", \"4Sa\", \"6fB\", \"6iC\", \"5LA\", \"0gN\", \"t\", \"jP\", \"0Ib\", \"46D\", \"6Go\", \"hyt\", \"bEO\", \"0DR\", \"0Q3\", \n\"IL\", \"8cf\", \"4QP\", \"4D1\", \"4OR\", \"4Z3\", \"WN\", \"00t\", \"0ZP\", \"yb\", \"hgv\", \"55W\", \"4lN\", \"6Ym\", \"0Z\", \"a3\", \"0yL\", \"2Lo\", \"63I\", \"4Bb\", \n\"4Mc\", \"7ha\", \"2Cn\", \"02E\", \"n2\", \"2mB\", \"6Vl\", \"4cO\", \"bTL\", \"a5F\", \"2k\", \"0UQ\", \"86m\", \"XO\", \"4U2\", \"5Pr\", \"0Gy\", \"dK\", \"4i6\", \"5lv\", \n\"5BZ\", \"6gX\", \"Jg\", \"0iU\", \"R6\", \"GW\", \"6jh\", \"5Oj\", \"45o\", \"6DD\", \"3oK\", \"0JI\", \"0EH\", \"fz\", \"6KE\", \"5nG\", \"4PJ\", \"6ei\", \"HV\", \"0kd\", \n\"0fT\", \"Ef\", \"6hY\", \"690\", \"4sV\", \"4f7\", \"kJ\", \"0Hx\", \"uH\", \"0Vz\", \"4mT\", \"4x5\", \"5F8\", \"4Cx\", \"0xV\", \"0m7\", \"VT\", \"C5\", \"4NH\", \"7kJ\", \n\"6UG\", \"54M\", \"1Kk\", \"xx\", \"3q\", \"0TK\", \"4oe\", \"6ZF\", \"60b\", \"4AI\", \"L4\", \"YU\", \"Te\", \"0wW\", \"4Ly\", \"5I9\", \"4w4\", \"4bU\", \"1IZ\", \"zI\", \n\"he\", \"0KW\", \"44q\", \"511\", \"4K4\", \"5Nt\", \"11R\", \"FI\", \"Ky\", \"0hK\", \"4Se\", \"6fF\", \"6Hj\", \"49A\", \"p4\", \"eU\", \"jT\", \"0If\", \"4rH\", \"6Gk\", \n\"6iG\", \"5LE\", \"0gJ\", \"p\", \"IH\", \"0jz\", \"4QT\", \"4D5\", \"5z8\", \"5oY\", \"0DV\", \"gd\", \"0ZT\", \"yf\", \"6TY\", \"4az\", \"4OV\", \"4Z7\", \"WJ\", \"00p\", \n\"0yH\", \"Zz\", \"63M\", \"4Bf\", \"4lJ\", \"6Yi\", \"tV\", \"a7\", \"n6\", \"2mF\", \"6Vh\", \"4cK\", \"4Mg\", \"6xD\", \"2Cj\", \"02A\", \"1kX\", \"XK\", \"4U6\", \"5Pv\", \n\"6N9\", \"7Ky\", \"2o\", \"0UU\", \"665\", \"73u\", \"Jc\", \"0iQ\", \"8Ne\", \"dO\", \"4i2\", \"5lr\", \"45k\", \"7Ta\", \"3oO\", \"0JM\", \"R2\", \"GS\", \"6jl\", \"5On\", \n\"4PN\", \"6em\", \"HR\", \"8bx\", \"0EL\", \"24g\", \"6KA\", \"5nC\", \"47Z\", \"4f3\", \"kN\", \"8Ad\", \"0fP\", \"Eb\", \"aBO\", \"694\", \"62W\", \"byO\", \"0xR\", \"0m3\", \n\"1D\", \"224\", \"4mP\", \"4x1\", \"6UC\", \"54I\", \"1Ko\", \"2nm\", \"VP\", \"C1\", \"4NL\", \"7kN\", \"60f\", \"4AM\", \"L0\", \"YQ\", \"3u\", \"0TO\", \"4oa\", \"6ZB\", \n\"438\", \"4bQ\", \"8Pg\", \"zM\", \"Ta\", \"0wS\", \"b2F\", \"aSL\", \"6Hf\", \"49M\", \"40\", \"eY\", \"Ku\", \"0hG\", \"4Si\", \"6fJ\", \"4K8\", \"5Nx\", \"0ew\", \"FE\", \n\"hi\", \"8BC\", \"4pu\", \"5u5\", \"5z4\", \"5oU\", \"0DZ\", \"gh\", \"ID\", \"0jv\", \"4QX\", \"4D9\", \"6iK\", \"5LI\", \"0gF\", \"Dt\", \"jX\", \"0Ij\", \"46L\", \"6Gg\", \n\"4lF\", \"6Ye\", \"0R\", \"0Wh\", \"0yD\", \"Zv\", \"63A\", \"4Bj\", \"4OZ\", \"6zy\", \"WF\", \"0tt\", \"0ZX\", \"yj\", \"5d6\", \"4av\", \"4nw\", \"5k7\", \"2c\", \"0UY\", \n\"1kT\", \"XG\", \"61p\", \"5Pz\", \"4Mk\", \"6xH\", \"Uw\", \"02M\", \"0Xi\", \"2mJ\", \"6Vd\", \"4cG\", \"0dm\", \"2QN\", \"7zA\", \"5Ob\", \"45g\", \"6DL\", \"is\", \"0JA\", \n\"0Gq\", \"dC\", \"acn\", \"48W\", \"4Rs\", \"5W3\", \"Jo\", \"94l\", \"12u\", \"En\", \"5X2\", \"5MS\", \"47V\", \"alo\", \"kB\", \"0Hp\", \"1Ua\", \"fr\", \"6KM\", \"5nO\", \n\"4PB\", \"6ea\", \"3Nn\", \"0kl\", \"3Pl\", \"01f\", \"bts\", \"7kB\", \"6UO\", \"54E\", \"1Kc\", \"xp\", \"1H\", \"0Vr\", \"59u\", \"a6e\", \"5F0\", \"4Cp\", \"85N\", \"d3F\", \n\"Tm\", \"03W\", \"4Lq\", \"5I1\", \"434\", \"56t\", \"0Ys\", \"zA\", \"3y\", \"0TC\", \"4om\", \"6ZN\", \"60j\", \"4AA\", \"0zo\", \"2OL\", \"Kq\", \"0hC\", \"4Sm\", \"6fN\", \n\"6Hb\", \"49I\", \"44\", \"27D\", \"hm\", \"8BG\", \"44y\", \"519\", \"aAl\", \"bdn\", \"0es\", \"FA\", \"1o2\", \"0jr\", \"bko\", \"70V\", \"5z0\", \"5oQ\", \"305\", \"gl\", \n\"28E\", \"0In\", \"46H\", \"6Gc\", \"6iO\", \"5LM\", \"0gB\", \"x\", \"1ia\", \"Zr\", \"63E\", \"4Bn\", \"4lB\", \"6Ya\", \"0V\", \"0Wl\", \"8SD\", \"yn\", \"5d2\", \"4ar\", \n\"b1e\", \"aPo\", \"WB\", \"00x\", \"1kP\", \"XC\", \"61t\", \"744\", \"4ns\", \"5k3\", \"2g\", \"9Ld\", \"0Xm\", \"2mN\", \"7FA\", \"4cC\", \"4Mo\", \"6xL\", \"Us\", \"02I\", \n\"45c\", \"6DH\", \"iw\", \"0JE\", \"0di\", \"2QJ\", \"6jd\", \"5Of\", \"4Rw\", \"5W7\", \"Jk\", \"0iY\", \"0Gu\", \"dG\", \"6Ix\", \"48S\", \"47R\", \"6Fy\", \"kF\", \"0Ht\", \n\"0fX\", \"Ej\", \"5X6\", \"5MW\", \"4PF\", \"6ee\", \"HZ\", \"0kh\", \"0ED\", \"fv\", \"6KI\", \"5nK\", \"6UK\", \"54A\", \"1Kg\", \"xt\", \"VX\", \"C9\", \"4ND\", \"7kF\", \n\"5F4\", \"4Ct\", \"0xZ\", \"2My\", \"1L\", \"0Vv\", \"4mX\", \"4x9\", \"430\", \"4bY\", \"0Yw\", \"zE\", \"Ti\", \"03S\", \"4Lu\", \"5I5\", \"60n\", \"4AE\", \"L8\", \"YY\", \n\"wu\", \"0TG\", \"4oi\", \"6ZJ\" };\n\n\n#endif\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/crc64.c",
    "content": "/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>\n * Copyright (c) 2020, Amazon Web Services\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 \"crc64.h\"\n#include \"crcspeed.h\"\nstatic uint64_t crc64_table[8][256] = {{0}};\n\n#define POLY UINT64_C(0xad93d23594c935a9)\n/******************** BEGIN GENERATED PYCRC FUNCTIONS ********************/\n/**\n * Generated on Sun Dec 21 14:14:07 2014,\n * by pycrc v0.8.2, https://www.tty1.net/pycrc/\n *\n * LICENSE ON GENERATED CODE:\n * ==========================\n * As of version 0.6, pycrc is released under the terms of the MIT licence.\n * The code generated by pycrc is not considered a substantial portion of the\n * software, therefore the author of pycrc will not claim any copyright on\n * the generated code.\n * ==========================\n *\n * CRC configuration:\n *    Width        = 64\n *    Poly         = 0xad93d23594c935a9\n *    XorIn        = 0xffffffffffffffff\n *    ReflectIn    = True\n *    XorOut       = 0x0000000000000000\n *    ReflectOut   = True\n *    Algorithm    = bit-by-bit-fast\n *\n * Modifications after generation (by matt):\n *   - included finalize step in-line with update for single-call generation\n *   - re-worked some inner variable architectures\n *   - adjusted function parameters to match expected prototypes.\n *****************************************************************************/\n\n/**\n * Reflect all bits of a \\a data word of \\a data_len bytes.\n *\n * \\param data         The data word to be reflected.\n * \\param data_len     The width of \\a data expressed in number of bits.\n * \\return             The reflected data.\n *****************************************************************************/\nstatic inline uint_fast64_t crc_reflect(uint_fast64_t data, size_t data_len) {\n    uint_fast64_t ret = data & 0x01;\n\n    for (size_t i = 1; i < data_len; i++) {\n        data >>= 1;\n        ret = (ret << 1) | (data & 0x01);\n    }\n\n    return ret;\n}\n\n/**\n *  Update the crc value with new data.\n *\n * \\param crc      The current crc value.\n * \\param data     Pointer to a buffer of \\a data_len bytes.\n * \\param data_len Number of bytes in the \\a data buffer.\n * \\return         The updated crc value.\n ******************************************************************************/\nuint64_t _crc64(uint_fast64_t crc, const void *in_data, const uint64_t len) {\n    const uint8_t *data = in_data;\n    unsigned long long bit;\n\n    for (uint64_t offset = 0; offset < len; offset++) {\n        uint8_t c = data[offset];\n        for (uint_fast8_t i = 0x01; i & 0xff; i <<= 1) {\n            bit = crc & 0x8000000000000000;\n            if (c & i) {\n                bit = !bit;\n            }\n\n            crc <<= 1;\n            if (bit) {\n                crc ^= POLY;\n            }\n        }\n\n        crc &= 0xffffffffffffffff;\n    }\n\n    crc = crc & 0xffffffffffffffff;\n    return crc_reflect(crc, 64) ^ 0x0000000000000000;\n}\n\n/******************** END GENERATED PYCRC FUNCTIONS ********************/\n\n/* Initializes the 16KB lookup tables. */\nvoid crc64_init(void) {\n    crcspeed64native_init(_crc64, crc64_table);\n}\n\n/* Compute crc64 */\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {\n    return crcspeed64native(crc64_table, crc, (void *) s, l);\n}\n\n/* Test main */\n#ifdef REDIS_TEST\n#include <stdio.h>\n\n#define UNUSED(x) (void)(x)\nint crc64Test(int argc, char *argv[]) {\n    UNUSED(argc);\n    UNUSED(argv);\n    crc64_init();\n    printf(\"[calcula]: e9c6d914c4b8d9ca == %016\" PRIx64 \"\\n\",\n           (uint64_t)_crc64(0, \"123456789\", 9));\n    printf(\"[64speed]: e9c6d914c4b8d9ca == %016\" PRIx64 \"\\n\",\n           (uint64_t)crc64(0, \"123456789\", 9));\n    char li[] = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed \"\n                \"do eiusmod tempor incididunt ut labore et dolore magna \"\n                \"aliqua. Ut enim ad minim veniam, quis nostrud exercitation \"\n                \"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis \"\n                \"aute irure dolor in reprehenderit in voluptate velit esse \"\n                \"cillum dolore eu fugiat nulla pariatur. Excepteur sint \"\n                \"occaecat cupidatat non proident, sunt in culpa qui officia \"\n                \"deserunt mollit anim id est laborum.\";\n    printf(\"[calcula]: c7794709e69683b3 == %016\" PRIx64 \"\\n\",\n           (uint64_t)_crc64(0, li, sizeof(li)));\n    printf(\"[64speed]: c7794709e69683b3 == %016\" PRIx64 \"\\n\",\n           (uint64_t)crc64(0, li, sizeof(li)));\n    return 0;\n}\n\n#endif\n\n#ifdef REDIS_TEST_MAIN\nint main(int argc, char *argv[]) {\n    return crc64Test(argc, argv);\n}\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/crc64.h",
    "content": "#ifndef CRC64_H\n#define CRC64_H\n\n#include <stdint.h>\n\nvoid crc64_init(void);\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);\n\n#ifdef REDIS_TEST\nint crc64Test(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/crcspeed.c",
    "content": "/*\n * Copyright (C) 2013 Mark Adler\n * Originally by: crc64.c Version 1.4  16 Dec 2013  Mark Adler\n * Modifications by Matt Stancliff <matt@genges.com>:\n *   - removed CRC64-specific behavior\n *   - added generation of lookup tables by parameters\n *   - removed inversion of CRC input/result\n *   - removed automatic initialization in favor of explicit initialization\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the author be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n\n  Mark Adler\n  madler@alumni.caltech.edu\n */\n\n#include \"crcspeed.h\"\n\n/* Fill in a CRC constants table. */\nvoid crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {\n    uint64_t crc;\n\n    /* generate CRCs for all single byte sequences */\n    for (int n = 0; n < 256; n++) {\n        table[0][n] = crcfn(0, &n, 1);\n    }\n\n    /* generate nested CRC table for future slice-by-8 lookup */\n    for (int n = 0; n < 256; n++) {\n        crc = table[0][n];\n        for (int k = 1; k < 8; k++) {\n            crc = table[0][crc & 0xff] ^ (crc >> 8);\n            table[k][n] = crc;\n        }\n    }\n}\n\nvoid crcspeed16little_init(crcfn16 crcfn, uint16_t table[8][256]) {\n    uint16_t crc;\n\n    /* generate CRCs for all single byte sequences */\n    for (int n = 0; n < 256; n++) {\n        table[0][n] = crcfn(0, &n, 1);\n    }\n\n    /* generate nested CRC table for future slice-by-8 lookup */\n    for (int n = 0; n < 256; n++) {\n        crc = table[0][n];\n        for (int k = 1; k < 8; k++) {\n            crc = table[0][(crc >> 8) & 0xff] ^ (crc << 8);\n            table[k][n] = crc;\n        }\n    }\n}\n\n/* Reverse the bytes in a 64-bit word. */\nstatic inline uint64_t rev8(uint64_t a) {\n#if defined(__GNUC__) || defined(__clang__)\n    return __builtin_bswap64(a);\n#else\n    uint64_t m;\n\n    m = UINT64_C(0xff00ff00ff00ff);\n    a = ((a >> 8) & m) | (a & m) << 8;\n    m = UINT64_C(0xffff0000ffff);\n    a = ((a >> 16) & m) | (a & m) << 16;\n    return a >> 32 | a << 32;\n#endif\n}\n\n/* This function is called once to initialize the CRC table for use on a\n   big-endian architecture. */\nvoid crcspeed64big_init(crcfn64 fn, uint64_t big_table[8][256]) {\n    /* Create the little endian table then reverse all the entires. */\n    crcspeed64little_init(fn, big_table);\n    for (int k = 0; k < 8; k++) {\n        for (int n = 0; n < 256; n++) {\n            big_table[k][n] = rev8(big_table[k][n]);\n        }\n    }\n}\n\nvoid crcspeed16big_init(crcfn16 fn, uint16_t big_table[8][256]) {\n    /* Create the little endian table then reverse all the entires. */\n    crcspeed16little_init(fn, big_table);\n    for (int k = 0; k < 8; k++) {\n        for (int n = 0; n < 256; n++) {\n            big_table[k][n] = rev8(big_table[k][n]);\n        }\n    }\n}\n\n/* Calculate a non-inverted CRC multiple bytes at a time on a little-endian\n * architecture. If you need inverted CRC, invert *before* calling and invert\n * *after* calling.\n * 64 bit crc = process 8 bytes at once;\n */\nuint64_t crcspeed64little(uint64_t little_table[8][256], uint64_t crc,\n                          void *buf, size_t len) {\n    unsigned char *next = buf;\n\n    /* process individual bytes until we reach an 8-byte aligned pointer */\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    /* fast middle processing, 8 bytes (aligned!) per loop */\n    while (len >= 8) {\n        crc ^= *(uint64_t *)next;\n        crc = little_table[7][crc & 0xff] ^\n              little_table[6][(crc >> 8) & 0xff] ^\n              little_table[5][(crc >> 16) & 0xff] ^\n              little_table[4][(crc >> 24) & 0xff] ^\n              little_table[3][(crc >> 32) & 0xff] ^\n              little_table[2][(crc >> 40) & 0xff] ^\n              little_table[1][(crc >> 48) & 0xff] ^\n              little_table[0][crc >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    /* process remaining bytes (can't be larger than 8) */\n    while (len) {\n        crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    return crc;\n}\n\nuint16_t crcspeed16little(uint16_t little_table[8][256], uint16_t crc,\n                          void *buf, size_t len) {\n    unsigned char *next = buf;\n\n    /* process individual bytes until we reach an 8-byte aligned pointer */\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);\n        len--;\n    }\n\n    /* fast middle processing, 8 bytes (aligned!) per loop */\n    while (len >= 8) {\n        uint64_t n = *(uint64_t *)next;\n        crc = little_table[7][(n & 0xff) ^ ((crc >> 8) & 0xff)] ^\n              little_table[6][((n >> 8) & 0xff) ^ (crc & 0xff)] ^\n              little_table[5][(n >> 16) & 0xff] ^\n              little_table[4][(n >> 24) & 0xff] ^\n              little_table[3][(n >> 32) & 0xff] ^\n              little_table[2][(n >> 40) & 0xff] ^\n              little_table[1][(n >> 48) & 0xff] ^\n              little_table[0][n >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    /* process remaining bytes (can't be larger than 8) */\n    while (len) {\n        crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);\n        len--;\n    }\n\n    return crc;\n}\n\n/* Calculate a non-inverted CRC eight bytes at a time on a big-endian\n * architecture.\n */\nuint64_t crcspeed64big(uint64_t big_table[8][256], uint64_t crc, void *buf,\n                       size_t len) {\n    unsigned char *next = buf;\n\n    crc = rev8(crc);\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);\n        len--;\n    }\n\n    while (len >= 8) {\n        crc ^= *(uint64_t *)next;\n        crc = big_table[0][crc & 0xff] ^\n              big_table[1][(crc >> 8) & 0xff] ^\n              big_table[2][(crc >> 16) & 0xff] ^\n              big_table[3][(crc >> 24) & 0xff] ^\n              big_table[4][(crc >> 32) & 0xff] ^\n              big_table[5][(crc >> 40) & 0xff] ^\n              big_table[6][(crc >> 48) & 0xff] ^\n              big_table[7][crc >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    while (len) {\n        crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);\n        len--;\n    }\n\n    return rev8(crc);\n}\n\n/* WARNING: Completely untested on big endian architecture.  Possibly broken. */\nuint16_t crcspeed16big(uint16_t big_table[8][256], uint16_t crc_in, void *buf,\n                       size_t len) {\n    unsigned char *next = buf;\n    uint64_t crc = crc_in;\n\n    crc = rev8(crc);\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    while (len >= 8) {\n        uint64_t n = *(uint64_t *)next;\n        crc = big_table[0][(n & 0xff) ^ ((crc >> (56 - 8)) & 0xff)] ^\n              big_table[1][((n >> 8) & 0xff) ^ (crc & 0xff)] ^\n              big_table[2][(n >> 16) & 0xff] ^\n              big_table[3][(n >> 24) & 0xff] ^\n              big_table[4][(n >> 32) & 0xff] ^\n              big_table[5][(n >> 40) & 0xff] ^\n              big_table[6][(n >> 48) & 0xff] ^\n              big_table[7][n >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    while (len) {\n        crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    return rev8(crc);\n}\n\n/* Return the CRC of buf[0..len-1] with initial crc, processing eight bytes\n   at a time using passed-in lookup table.\n   This selects one of two routines depending on the endianess of\n   the architecture. */\nuint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,\n                          size_t len) {\n    uint64_t n = 1;\n\n    return *(char *)&n ? crcspeed64little(table, crc, buf, len)\n                       : crcspeed64big(table, crc, buf, len);\n}\n\nuint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,\n                          size_t len) {\n    uint64_t n = 1;\n\n    return *(char *)&n ? crcspeed16little(table, crc, buf, len)\n                       : crcspeed16big(table, crc, buf, len);\n}\n\n/* Initialize CRC lookup table in architecture-dependent manner. */\nvoid crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]) {\n    uint64_t n = 1;\n\n    *(char *)&n ? crcspeed64little_init(fn, table)\n                : crcspeed64big_init(fn, table);\n}\n\nvoid crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]) {\n    uint64_t n = 1;\n\n    *(char *)&n ? crcspeed16little_init(fn, table)\n                : crcspeed16big_init(fn, table);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/crcspeed.h",
    "content": "/* Copyright (c) 2014, Matt Stancliff <matt@genges.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#ifndef CRCSPEED_H\n#define CRCSPEED_H\n\n#include <inttypes.h>\n#include <stdio.h>\n\ntypedef uint64_t (*crcfn64)(uint64_t, const void *, const uint64_t);\ntypedef uint16_t (*crcfn16)(uint16_t, const void *, const uint64_t);\n\n/* CRC-64 */\nvoid crcspeed64little_init(crcfn64 fn, uint64_t table[8][256]);\nvoid crcspeed64big_init(crcfn64 fn, uint64_t table[8][256]);\nvoid crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]);\n\nuint64_t crcspeed64little(uint64_t table[8][256], uint64_t crc, void *buf,\n                          size_t len);\nuint64_t crcspeed64big(uint64_t table[8][256], uint64_t crc, void *buf,\n                       size_t len);\nuint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,\n                          size_t len);\n\n/* CRC-16 */\nvoid crcspeed16little_init(crcfn16 fn, uint16_t table[8][256]);\nvoid crcspeed16big_init(crcfn16 fn, uint16_t table[8][256]);\nvoid crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]);\n\nuint16_t crcspeed16little(uint16_t table[8][256], uint16_t crc, void *buf,\n                          size_t len);\nuint16_t crcspeed16big(uint16_t table[8][256], uint16_t crc, void *buf,\n                       size_t len);\nuint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,\n                          size_t len);\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"cluster.h\"\n#include \"atomicvar.h\"\n\n#include <signal.h>\n#include <ctype.h>\n\n/*-----------------------------------------------------------------------------\n * C-level DB API\n *----------------------------------------------------------------------------*/\n\nint keyIsExpired(redisDb *db, robj *key);\n\n/* Update LFU when an object is accessed.\n * Firstly, decrement the counter if the decrement time is reached.\n * Then logarithmically increment the counter, and update the access time. */\nvoid updateLFU(robj *val) {\n    unsigned long counter = LFUDecrAndReturn(val);\n    counter = LFULogIncr(counter);\n    val->lru = (LFUGetTimeInMinutes()<<8) | counter;\n}\n\n/* Low level key lookup API, not actually called directly from commands\n * implementations that should instead rely on lookupKeyRead(),\n * lookupKeyWrite() and lookupKeyReadWithFlags(). */\nrobj *lookupKey(redisDb *db, robj *key, int flags) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n    if (de) {\n        robj *val = dictGetVal(de);\n\n        /* Update the access time for the ageing algorithm.\n         * Don't do it if we have a saving child, as this will trigger\n         * a copy on write madness. */\n        if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH)){\n            if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n                updateLFU(val);\n            } else {\n                val->lru = LRU_CLOCK();\n            }\n        }\n        return val;\n    } else {\n        return NULL;\n    }\n}\n\n/* Lookup a key for read operations, or return NULL if the key is not found\n * in the specified DB.\n *\n * As a side effect of calling this function:\n * 1. A key gets expired if it reached it's TTL.\n * 2. The key last access time is updated.\n * 3. The global keys hits/misses stats are updated (reported in INFO).\n * 4. If keyspace notifications are enabled, a \"keymiss\" notification is fired.\n *\n * This API should not be used when we write to the key after obtaining\n * the object linked to the key, but only for read only operations.\n *\n * Flags change the behavior of this command:\n *\n *  LOOKUP_NONE (or zero): no special flags are passed.\n *  LOOKUP_NOTOUCH: don't alter the last access time of the key.\n *\n * Note: this function also returns NULL if the key is logically expired\n * but still existing, in case this is a slave, since this API is called only\n * for read operations. Even if the key expiry is master-driven, we can\n * correctly report a key is expired on slaves even if the master is lagging\n * expiring our key via DELs in the replication link. */\nrobj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {\n    robj *val;\n\n    if (expireIfNeeded(db,key) == 1) {\n        /* Key expired. If we are in the context of a master, expireIfNeeded()\n         * returns 0 only when the key does not exist at all, so it's safe\n         * to return NULL ASAP. */\n        if (server.masterhost == NULL) {\n            server.stat_keyspace_misses++;\n            notifyKeyspaceEvent(NOTIFY_KEY_MISS, \"keymiss\", key, db->id);\n            return NULL;\n        }\n\n        /* However if we are in the context of a slave, expireIfNeeded() will\n         * not really try to expire the key, it only returns information\n         * about the \"logical\" status of the key: key expiring is up to the\n         * master in order to have a consistent view of master's data set.\n         *\n         * However, if the command caller is not the master, and as additional\n         * safety measure, the command invoked is a read-only command, we can\n         * safely return NULL here, and provide a more consistent behavior\n         * to clients accessign expired values in a read-only fashion, that\n         * will say the key as non existing.\n         *\n         * Notably this covers GETs when slaves are used to scale reads. */\n        if (server.current_client &&\n            server.current_client != server.master &&\n            server.current_client->cmd &&\n            server.current_client->cmd->flags & CMD_READONLY)\n        {\n            server.stat_keyspace_misses++;\n            notifyKeyspaceEvent(NOTIFY_KEY_MISS, \"keymiss\", key, db->id);\n            return NULL;\n        }\n    }\n    val = lookupKey(db,key,flags);\n    if (val == NULL) {\n        server.stat_keyspace_misses++;\n        notifyKeyspaceEvent(NOTIFY_KEY_MISS, \"keymiss\", key, db->id);\n    }\n    else\n        server.stat_keyspace_hits++;\n    return val;\n}\n\n/* Like lookupKeyReadWithFlags(), but does not use any flag, which is the\n * common case. */\nrobj *lookupKeyRead(redisDb *db, robj *key) {\n    return lookupKeyReadWithFlags(db,key,LOOKUP_NONE);\n}\n\n/* Lookup a key for write operations, and as a side effect, if needed, expires\n * the key if its TTL is reached.\n *\n * Returns the linked value object if the key exists or NULL if the key\n * does not exist in the specified DB. */\nrobj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) {\n    expireIfNeeded(db,key);\n    return lookupKey(db,key,flags);\n}\n\nrobj *lookupKeyWrite(redisDb *db, robj *key) {\n    return lookupKeyWriteWithFlags(db, key, LOOKUP_NONE);\n}\n\nrobj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {\n    robj *o = lookupKeyRead(c->db, key);\n    if (!o) addReply(c,reply);\n    return o;\n}\n\nrobj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) {\n    robj *o = lookupKeyWrite(c->db, key);\n    if (!o) addReply(c,reply);\n    return o;\n}\n\n/* Add the key to the DB. It's up to the caller to increment the reference\n * counter of the value if needed.\n *\n * The program is aborted if the key already exists. */\nvoid dbAdd(redisDb *db, robj *key, robj *val) {\n    sds copy = sdsdup(key->ptr);\n    int retval = dictAdd(db->dict, copy, val);\n\n    serverAssertWithInfo(NULL,key,retval == DICT_OK);\n    if (val->type == OBJ_LIST ||\n        val->type == OBJ_ZSET ||\n        val->type == OBJ_STREAM)\n        signalKeyAsReady(db, key);\n    if (server.cluster_enabled) slotToKeyAdd(key->ptr);\n}\n\n/* This is a special version of dbAdd() that is used only when loading\n * keys from the RDB file: the key is passed as an SDS string that is\n * retained by the function (and not freed by the caller).\n *\n * Moreover this function will not abort if the key is already busy, to\n * give more control to the caller, nor will signal the key as ready\n * since it is not useful in this context.\n *\n * The function returns 1 if the key was added to the database, taking\n * ownership of the SDS string, otherwise 0 is returned, and is up to the\n * caller to free the SDS string. */\nint dbAddRDBLoad(redisDb *db, sds key, robj *val) {\n    int retval = dictAdd(db->dict, key, val);\n    if (retval != DICT_OK) return 0;\n    if (server.cluster_enabled) slotToKeyAdd(key);\n    return 1;\n}\n\n/* Overwrite an existing key with a new value. Incrementing the reference\n * count of the new value is up to the caller.\n * This function does not modify the expire time of the existing key.\n *\n * The program is aborted if the key was not already present. */\nvoid dbOverwrite(redisDb *db, robj *key, robj *val) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n\n    serverAssertWithInfo(NULL,key,de != NULL);\n    dictEntry auxentry = *de;\n    robj *old = dictGetVal(de);\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        val->lru = old->lru;\n    }\n    dictSetVal(db->dict, de, val);\n\n    if (server.lazyfree_lazy_server_del) {\n        freeObjAsync(old);\n        dictSetVal(db->dict, &auxentry, NULL);\n    }\n\n    dictFreeVal(db->dict, &auxentry);\n}\n\n/* High level Set operation. This function can be used in order to set\n * a key, whatever it was existing or not, to a new object.\n *\n * 1) The ref count of the value object is incremented.\n * 2) clients WATCHing for the destination key notified.\n * 3) The expire time of the key is reset (the key is made persistent),\n *    unless 'keepttl' is true.\n *\n * All the new keys in the database should be created via this interface.\n * The client 'c' argument may be set to NULL if the operation is performed\n * in a context where there is no clear client performing the operation. */\nvoid genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) {\n    if (lookupKeyWrite(db,key) == NULL) {\n        dbAdd(db,key,val);\n    } else {\n        dbOverwrite(db,key,val);\n    }\n    incrRefCount(val);\n    if (!keepttl) removeExpire(db,key);\n    if (signal) signalModifiedKey(c,db,key);\n}\n\n/* Common case for genericSetKey() where the TTL is not retained. */\nvoid setKey(client *c, redisDb *db, robj *key, robj *val) {\n    genericSetKey(c,db,key,val,0,1);\n}\n\n/* Return true if the specified key exists in the specified database.\n * LRU/LFU info is not updated in any way. */\nint dbExists(redisDb *db, robj *key) {\n    return dictFind(db->dict,key->ptr) != NULL;\n}\n\n/* Return a random key, in form of a Redis object.\n * If there are no keys, NULL is returned.\n *\n * The function makes sure to return keys not already expired. */\nrobj *dbRandomKey(redisDb *db) {\n    dictEntry *de;\n    int maxtries = 100;\n    int allvolatile = dictSize(db->dict) == dictSize(db->expires);\n\n    while(1) {\n        sds key;\n        robj *keyobj;\n\n        de = dictGetFairRandomKey(db->dict);\n        if (de == NULL) return NULL;\n\n        key = dictGetKey(de);\n        keyobj = createStringObject(key,sdslen(key));\n        if (dictFind(db->expires,key)) {\n            if (allvolatile && server.masterhost && --maxtries == 0) {\n                /* If the DB is composed only of keys with an expire set,\n                 * it could happen that all the keys are already logically\n                 * expired in the slave, so the function cannot stop because\n                 * expireIfNeeded() is false, nor it can stop because\n                 * dictGetRandomKey() returns NULL (there are keys to return).\n                 * To prevent the infinite loop we do some tries, but if there\n                 * are the conditions for an infinite loop, eventually we\n                 * return a key name that may be already expired. */\n                return keyobj;\n            }\n            if (expireIfNeeded(db,keyobj)) {\n                decrRefCount(keyobj);\n                continue; /* search for another key. This expired. */\n            }\n        }\n        return keyobj;\n    }\n}\n\n/* Delete a key, value, and associated expiration entry if any, from the DB */\nint dbSyncDelete(redisDb *db, robj *key) {\n    /* Deleting an entry from the expires dict will not free the sds of\n     * the key, because it is shared with the main dictionary. */\n    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);\n    if (dictDelete(db->dict,key->ptr) == DICT_OK) {\n        if (server.cluster_enabled) slotToKeyDel(key->ptr);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* This is a wrapper whose behavior depends on the Redis lazy free\n * configuration. Deletes the key synchronously or asynchronously. */\nint dbDelete(redisDb *db, robj *key) {\n    return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key) :\n                                             dbSyncDelete(db,key);\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,OBJ_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    serverAssert(o->type == OBJ_STRING);\n    if (o->refcount != 1 || o->encoding != OBJ_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/* Remove all keys from all the databases in a Redis server.\n * If callback is given the function is called from time to time to\n * signal that work is in progress.\n *\n * The dbnum can be -1 if all the DBs should be flushed, or the specified\n * DB number if we want to flush only a single Redis database number.\n *\n * Flags are be EMPTYDB_NO_FLAGS if no special flags are specified or\n * 1. EMPTYDB_ASYNC if we want the memory to be freed in a different thread.\n * 2. EMPTYDB_BACKUP if we want to empty the backup dictionaries created by\n *    disklessLoadMakeBackups. In that case we only free memory and avoid\n *    firing module events.\n * and the function to return ASAP.\n *\n * On success the fuction returns the number of keys removed from the\n * database(s). Otherwise -1 is returned in the specific case the\n * DB number is out of range, and errno is set to EINVAL. */\nlong long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*)) {\n    int async = (flags & EMPTYDB_ASYNC);\n    int backup = (flags & EMPTYDB_BACKUP); /* Just free the memory, nothing else */\n    RedisModuleFlushInfoV1 fi = {REDISMODULE_FLUSHINFO_VERSION,!async,dbnum};\n    long long removed = 0;\n\n    if (dbnum < -1 || dbnum >= server.dbnum) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    /* Pre-flush actions */\n    if (!backup) {\n        /* Fire the flushdb modules event. */\n        moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,\n                              REDISMODULE_SUBEVENT_FLUSHDB_START,\n                              &fi);\n\n        /* Make sure the WATCHed keys are affected by the FLUSH* commands.\n         * Note that we need to call the function while the keys are still\n         * there. */\n        signalFlushedDb(dbnum);\n    }\n\n    int startdb, enddb;\n    if (dbnum == -1) {\n        startdb = 0;\n        enddb = server.dbnum-1;\n    } else {\n        startdb = enddb = dbnum;\n    }\n\n    for (int j = startdb; j <= enddb; j++) {\n        removed += dictSize(dbarray[j].dict);\n        if (async) {\n            emptyDbAsync(&dbarray[j]);\n        } else {\n            dictEmpty(dbarray[j].dict,callback);\n            dictEmpty(dbarray[j].expires,callback);\n        }\n    }\n\n    /* Post-flush actions */\n    if (!backup) {\n        if (server.cluster_enabled) {\n            if (async) {\n                slotToKeyFlushAsync();\n            } else {\n                slotToKeyFlush();\n            }\n        }\n        if (dbnum == -1) flushSlaveKeysWithExpireList();\n\n        /* Also fire the end event. Note that this event will fire almost\n         * immediately after the start event if the flush is asynchronous. */\n        moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,\n                              REDISMODULE_SUBEVENT_FLUSHDB_END,\n                              &fi);\n    }\n\n    return removed;\n}\n\nlong long emptyDb(int dbnum, int flags, void(callback)(void*)) {\n    return emptyDbGeneric(server.db, dbnum, flags, callback);\n}\n\nint selectDb(client *c, int id) {\n    if (id < 0 || id >= server.dbnum)\n        return C_ERR;\n    c->db = &server.db[id];\n    return C_OK;\n}\n\nlong long dbTotalServerKeyCount() {\n    long long total = 0;\n    int j;\n    for (j = 0; j < server.dbnum; j++) {\n        total += dictSize(server.db[j].dict);\n    }\n    return total;\n}\n\n/*-----------------------------------------------------------------------------\n * Hooks for key space changes.\n *\n * Every time a key in the database is modified the function\n * signalModifiedKey() is called.\n *\n * Every time a DB is flushed the function signalFlushDb() is called.\n *----------------------------------------------------------------------------*/\n\n/* Note that the 'c' argument may be NULL if the key was modified out of\n * a context of a client. */\nvoid signalModifiedKey(client *c, redisDb *db, robj *key) {\n    touchWatchedKey(db,key);\n    trackingInvalidateKey(c,key);\n}\n\nvoid signalFlushedDb(int dbid) {\n    touchWatchedKeysOnFlush(dbid);\n    trackingInvalidateKeysOnFlush(dbid);\n}\n\n/*-----------------------------------------------------------------------------\n * Type agnostic commands operating on the key space\n *----------------------------------------------------------------------------*/\n\n/* Return the set of flags to use for the emptyDb() call for FLUSHALL\n * and FLUSHDB commands.\n *\n * Currently the command just attempts to parse the \"ASYNC\" option. It\n * also checks if the command arity is wrong.\n *\n * On success C_OK is returned and the flags are stored in *flags, otherwise\n * C_ERR is returned and the function sends an error to the client. */\nint getFlushCommandFlags(client *c, int *flags) {\n    /* Parse the optional ASYNC option. */\n    if (c->argc > 1) {\n        if (c->argc > 2 || strcasecmp(c->argv[1]->ptr,\"async\")) {\n            addReply(c,shared.syntaxerr);\n            return C_ERR;\n        }\n        *flags = EMPTYDB_ASYNC;\n    } else {\n        *flags = EMPTYDB_NO_FLAGS;\n    }\n    return C_OK;\n}\n\n/* Flushes the whole server data set. */\nvoid flushAllDataAndResetRDB(int flags) {\n    server.dirty += emptyDb(-1,flags,NULL);\n    if (server.rdb_child_pid != -1) killRDBChild();\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        int saved_dirty = server.dirty;\n        rdbSaveInfo rsi, *rsiptr;\n        rsiptr = rdbPopulateSaveInfo(&rsi);\n        rdbSave(server.rdb_filename,rsiptr);\n        server.dirty = saved_dirty;\n    }\n    server.dirty++;\n#if defined(USE_JEMALLOC)\n    /* jemalloc 5 doesn't release pages back to the OS when there's no traffic.\n     * for large databases, flushdb blocks for long anyway, so a bit more won't\n     * harm and this way the flush and purge will be synchroneus. */\n    if (!(flags & EMPTYDB_ASYNC))\n        jemalloc_purge();\n#endif\n}\n\n/* FLUSHDB [ASYNC]\n *\n * Flushes the currently SELECTed Redis DB. */\nvoid flushdbCommand(client *c) {\n    int flags;\n\n    if (getFlushCommandFlags(c,&flags) == C_ERR) return;\n    server.dirty += emptyDb(c->db->id,flags,NULL);\n    addReply(c,shared.ok);\n#if defined(USE_JEMALLOC)\n    /* jemalloc 5 doesn't release pages back to the OS when there's no traffic.\n     * for large databases, flushdb blocks for long anyway, so a bit more won't\n     * harm and this way the flush and purge will be synchroneus. */\n    if (!(flags & EMPTYDB_ASYNC))\n        jemalloc_purge();\n#endif\n}\n\n/* FLUSHALL [ASYNC]\n *\n * Flushes the whole server data set. */\nvoid flushallCommand(client *c) {\n    int flags;\n    if (getFlushCommandFlags(c,&flags) == C_ERR) return;\n    flushAllDataAndResetRDB(flags);\n    addReply(c,shared.ok);\n}\n\n/* This command implements DEL and LAZYDEL. */\nvoid delGenericCommand(client *c, int lazy) {\n    int numdel = 0, j;\n\n    for (j = 1; j < c->argc; j++) {\n        expireIfNeeded(c->db,c->argv[j]);\n        int deleted  = lazy ? dbAsyncDelete(c->db,c->argv[j]) :\n                              dbSyncDelete(c->db,c->argv[j]);\n        if (deleted) {\n            signalModifiedKey(c,c->db,c->argv[j]);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"del\",c->argv[j],c->db->id);\n            server.dirty++;\n            numdel++;\n        }\n    }\n    addReplyLongLong(c,numdel);\n}\n\nvoid delCommand(client *c) {\n    delGenericCommand(c,server.lazyfree_lazy_user_del);\n}\n\nvoid unlinkCommand(client *c) {\n    delGenericCommand(c,1);\n}\n\n/* EXISTS key1 key2 ... key_N.\n * Return value is the number of keys existing. */\nvoid existsCommand(client *c) {\n    long long count = 0;\n    int j;\n\n    for (j = 1; j < c->argc; j++) {\n        if (lookupKeyRead(c->db,c->argv[j])) count++;\n    }\n    addReplyLongLong(c,count);\n}\n\nvoid selectCommand(client *c) {\n    long id;\n\n    if (getLongFromObjectOrReply(c, c->argv[1], &id,\n        \"invalid DB index\") != C_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    if (selectDb(c,id) == C_ERR) {\n        addReplyError(c,\"DB index is out of range\");\n    } else {\n        addReply(c,shared.ok);\n    }\n}\n\nvoid randomkeyCommand(client *c) {\n    robj *key;\n\n    if ((key = dbRandomKey(c->db)) == NULL) {\n        addReplyNull(c);\n        return;\n    }\n\n    addReplyBulk(c,key);\n    decrRefCount(key);\n}\n\nvoid keysCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n    sds pattern = c->argv[1]->ptr;\n    int plen = sdslen(pattern), allkeys;\n    unsigned long numkeys = 0;\n    void *replylen = addReplyDeferredLen(c);\n\n    di = dictGetSafeIterator(c->db->dict);\n    allkeys = (pattern[0] == '*' && plen == 1);\n    while((de = dictNext(di)) != NULL) {\n        sds key = dictGetKey(de);\n        robj *keyobj;\n\n        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {\n            keyobj = createStringObject(key,sdslen(key));\n            if (!keyIsExpired(c->db,keyobj)) {\n                addReplyBulk(c,keyobj);\n                numkeys++;\n            }\n            decrRefCount(keyobj);\n        }\n    }\n    dictReleaseIterator(di);\n    setDeferredArrayLen(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 == OBJ_SET) {\n        sds keysds = dictGetKey(de);\n        key = createStringObject(keysds,sdslen(keysds));\n    } else if (o->type == OBJ_HASH) {\n        sds sdskey = dictGetKey(de);\n        sds sdsval = dictGetVal(de);\n        key = createStringObject(sdskey,sdslen(sdskey));\n        val = createStringObject(sdsval,sdslen(sdsval));\n    } else if (o->type == OBJ_ZSET) {\n        sds sdskey = dictGetKey(de);\n        key = createStringObject(sdskey,sdslen(sdskey));\n        val = createStringObjectFromLongDouble(*(double*)dictGetVal(de),0);\n    } else {\n        serverPanic(\"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 C_OK. Otherwise return C_ERR and send an error to the\n * client. */\nint parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor) {\n    char *eptr;\n\n    /* Use strtoul() because we need an *unsigned* long, so\n     * getLongLongFromObject() does not cover the whole cursor space. */\n    errno = 0;\n    *cursor = strtoul(o->ptr, &eptr, 10);\n    if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\\0' || errno == ERANGE)\n    {\n        addReplyError(c, \"invalid cursor\");\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* This command implements SCAN, HSCAN and SSCAN commands.\n * If object 'o' is passed, then it must be a Hash, Set or Zset object, otherwise\n * if 'o' is NULL the command will operate on the dictionary associated with\n * the current database.\n *\n * When 'o' is not NULL the function assumes that the first argument in\n * the client arguments vector is a key so it skips it before iterating\n * in order to parse options.\n *\n * In the case of a Hash object the function returns both the field and value\n * of every element on the Hash. */\nvoid scanGenericCommand(client *c, robj *o, unsigned long cursor) {\n    int i, j;\n    list *keys = listCreate();\n    listNode *node, *nextnode;\n    long count = 10;\n    sds pat = NULL;\n    sds typename = NULL;\n    int patlen = 0, 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    serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH ||\n                o->type == OBJ_ZSET);\n\n    /* Set i to the first option argument. The previous one is the cursor. */\n    i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */\n\n    /* Step 1: Parse options. */\n    while (i < c->argc) {\n        j = c->argc - i;\n        if (!strcasecmp(c->argv[i]->ptr, \"count\") && j >= 2) {\n            if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL)\n                != C_OK)\n            {\n                goto cleanup;\n            }\n\n            if (count < 1) {\n                addReply(c,shared.syntaxerr);\n                goto cleanup;\n            }\n\n            i += 2;\n        } else if (!strcasecmp(c->argv[i]->ptr, \"match\") && j >= 2) {\n            pat = c->argv[i+1]->ptr;\n            patlen = sdslen(pat);\n\n            /* The pattern always matches if it is exactly \"*\", so it is\n             * equivalent to disabling it. */\n            use_pattern = !(pat[0] == '*' && patlen == 1);\n\n            i += 2;\n        } else if (!strcasecmp(c->argv[i]->ptr, \"type\") && o == NULL && j >= 2) {\n            /* SCAN for a particular type only applies to the db dict */\n            typename = c->argv[i+1]->ptr;\n            i+= 2;\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\n    /* Handle the case of a hash table. */\n    ht = NULL;\n    if (o == NULL) {\n        ht = c->db->dict;\n    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {\n        ht = o->ptr;\n    } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) {\n        ht = o->ptr;\n        count *= 2; /* We return key / value for this type. */\n    } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = o->ptr;\n        ht = zs->dict;\n        count *= 2; /* We return key / value for this type. */\n    }\n\n    if (ht) {\n        void *privdata[2];\n        /* We set the max number of iterations to ten times the specified\n         * COUNT, so if the hash table is in a pathological state (very\n         * sparsely populated) we avoid to block too much time at the cost\n         * of returning no or very few elements. */\n        long maxiterations = count*10;\n\n        /* We pass two pointers to the callback: the list to which it will\n         * add new elements, and the object containing the dictionary so that\n         * it is possible to fetch more data in a type-dependent way. */\n        privdata[0] = keys;\n        privdata[1] = o;\n        do {\n            cursor = dictScan(ht, cursor, scanCallback, NULL, privdata);\n        } while (cursor &&\n              maxiterations-- &&\n              listLength(keys) < (unsigned long)count);\n    } else if (o->type == OBJ_SET) {\n        int pos = 0;\n        int64_t ll;\n\n        while(intsetGet(o->ptr,pos++,&ll))\n            listAddNodeTail(keys,createStringObjectFromLongLong(ll));\n        cursor = 0;\n    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {\n        unsigned char *p = ziplistIndex(o->ptr,0);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vll;\n\n        while(p) {\n            ziplistGet(p,&vstr,&vlen,&vll);\n            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        serverPanic(\"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[LONG_STR_SIZE];\n                int len;\n\n                serverAssert(kobj->encoding == OBJ_ENCODING_INT);\n                len = ll2string(buf,sizeof(buf),(long)kobj->ptr);\n                if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1;\n            }\n        }\n\n        /* Filter an element if it isn't the type we want. */\n        if (!filter && o == NULL && typename){\n            robj* typecheck = lookupKeyReadWithFlags(c->db, kobj, LOOKUP_NOTOUCH);\n            char* type = getObjectTypeName(typecheck);\n            if (strcasecmp((char*) typename, type)) filter = 1;\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 == OBJ_ZSET || o->type == OBJ_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    addReplyArrayLen(c, 2);\n    addReplyBulkLongLong(c,cursor);\n\n    addReplyArrayLen(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(client *c) {\n    unsigned long cursor;\n    if (parseScanCursorOrReply(c,c->argv[1],&cursor) == C_ERR) return;\n    scanGenericCommand(c,NULL,cursor);\n}\n\nvoid dbsizeCommand(client *c) {\n    addReplyLongLong(c,dictSize(c->db->dict));\n}\n\nvoid lastsaveCommand(client *c) {\n    addReplyLongLong(c,server.lastsave);\n}\n\nchar* getObjectTypeName(robj *o) {\n    char* type;\n    if (o == NULL) {\n        type = \"none\";\n    } else {\n        switch(o->type) {\n        case OBJ_STRING: type = \"string\"; break;\n        case OBJ_LIST: type = \"list\"; break;\n        case OBJ_SET: type = \"set\"; break;\n        case OBJ_ZSET: type = \"zset\"; break;\n        case OBJ_HASH: type = \"hash\"; break;\n        case OBJ_STREAM: type = \"stream\"; break;\n        case OBJ_MODULE: {\n            moduleValue *mv = o->ptr;\n            type = mv->type->name;\n        }; break;\n        default: type = \"unknown\"; break;\n        }\n    }\n    return type;\n}\n\nvoid typeCommand(client *c) {\n    robj *o;\n    o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH);\n    addReplyStatus(c, getObjectTypeName(o));\n}\n\nvoid shutdownCommand(client *c) {\n    int flags = 0;\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    } else if (c->argc == 2) {\n        if (!strcasecmp(c->argv[1]->ptr,\"nosave\")) {\n            flags |= SHUTDOWN_NOSAVE;\n        } else if (!strcasecmp(c->argv[1]->ptr,\"save\")) {\n            flags |= SHUTDOWN_SAVE;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n    /* When SHUTDOWN is called while the server is loading a dataset in\n     * memory we need to make sure no attempt is performed to save\n     * the dataset on shutdown (otherwise it could overwrite the current DB\n     * with half-read data).\n     *\n     * Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */\n    if (server.loading || server.sentinel_mode)\n        flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;\n    if (prepareForShutdown(flags) == C_OK) exit(0);\n    addReplyError(c,\"Errors trying to SHUTDOWN. Check logs.\");\n}\n\nvoid renameGenericCommand(client *c, int nx) {\n    robj *o;\n    long long expire;\n    int samekey = 0;\n\n    /* When source and dest key is the same, no operation is performed,\n     * if the key exists, however we still return an error on unexisting key. */\n    if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) samekey = 1;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)\n        return;\n\n    if (samekey) {\n        addReply(c,nx ? shared.czero : shared.ok);\n        return;\n    }\n\n    incrRefCount(o);\n    expire = getExpire(c->db,c->argv[1]);\n    if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {\n        if (nx) {\n            decrRefCount(o);\n            addReply(c,shared.czero);\n            return;\n        }\n        /* Overwrite: delete the old key before creating the new one\n         * with the same name. */\n        dbDelete(c->db,c->argv[2]);\n    }\n    dbAdd(c->db,c->argv[2],o);\n    if (expire != -1) setExpire(c,c->db,c->argv[2],expire);\n    dbDelete(c->db,c->argv[1]);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    signalModifiedKey(c,c->db,c->argv[2]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"rename_from\",\n        c->argv[1],c->db->id);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"rename_to\",\n        c->argv[2],c->db->id);\n    server.dirty++;\n    addReply(c,nx ? shared.cone : shared.ok);\n}\n\nvoid renameCommand(client *c) {\n    renameGenericCommand(c,0);\n}\n\nvoid renamenxCommand(client *c) {\n    renameGenericCommand(c,1);\n}\n\nvoid moveCommand(client *c) {\n    robj *o;\n    redisDb *src, *dst;\n    int srcid;\n    long long dbid, expire;\n\n    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    src = c->db;\n    srcid = c->db->id;\n\n    if (getLongLongFromObject(c->argv[2],&dbid) == C_ERR ||\n        dbid < INT_MIN || dbid > INT_MAX ||\n        selectDb(c,dbid) == C_ERR)\n    {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n    dst = c->db;\n    selectDb(c,srcid); /* Back to the source DB */\n\n    /* If the user is moving using as target the same\n     * DB as the source DB it is probably an error. */\n    if (src == dst) {\n        addReply(c,shared.sameobjecterr);\n        return;\n    }\n\n    /* Check if the element exists and get a reference */\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (!o) {\n        addReply(c,shared.czero);\n        return;\n    }\n    expire = getExpire(c->db,c->argv[1]);\n\n    /* Return zero if the key already exists in the target DB */\n    if (lookupKeyWrite(dst,c->argv[1]) != NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n    dbAdd(dst,c->argv[1],o);\n    if (expire != -1) setExpire(c,dst,c->argv[1],expire);\n    incrRefCount(o);\n\n    /* OK! key moved, free the entry in the source DB */\n    dbDelete(src,c->argv[1]);\n    signalModifiedKey(c,src,c->argv[1]);\n    signalModifiedKey(c,dst,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"move_from\",c->argv[1],src->id);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"move_to\",c->argv[1],dst->id);\n\n    server.dirty++;\n    addReply(c,shared.cone);\n}\n\n/* Helper function for dbSwapDatabases(): scans the list of keys that have\n * one or more blocked clients for B[LR]POP or other blocking commands\n * and signal the keys as ready if they are of the right type. See the comment\n * where the function is used for more info. */\nvoid scanDatabaseForReadyLists(redisDb *db) {\n    dictEntry *de;\n    dictIterator *di = dictGetSafeIterator(db->blocking_keys);\n    while((de = dictNext(di)) != NULL) {\n        robj *key = dictGetKey(de);\n        robj *value = lookupKey(db,key,LOOKUP_NOTOUCH);\n        if (value && (value->type == OBJ_LIST ||\n                      value->type == OBJ_STREAM ||\n                      value->type == OBJ_ZSET))\n            signalKeyAsReady(db, key);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Swap two databases at runtime so that all clients will magically see\n * the new database even if already connected. Note that the client\n * structure c->db points to a given DB, so we need to be smarter and\n * swap the underlying referenced structures, otherwise we would need\n * to fix all the references to the Redis DB structure.\n *\n * Returns C_ERR if at least one of the DB ids are out of range, otherwise\n * C_OK is returned. */\nint dbSwapDatabases(long id1, long id2) {\n    if (id1 < 0 || id1 >= server.dbnum ||\n        id2 < 0 || id2 >= server.dbnum) return C_ERR;\n    if (id1 == id2) return C_OK;\n    redisDb aux = server.db[id1];\n    redisDb *db1 = &server.db[id1], *db2 = &server.db[id2];\n\n    /* Swap hash tables. Note that we don't swap blocking_keys,\n     * ready_keys and watched_keys, since we want clients to\n     * remain in the same DB they were. */\n    db1->dict = db2->dict;\n    db1->expires = db2->expires;\n    db1->avg_ttl = db2->avg_ttl;\n    db1->expires_cursor = db2->expires_cursor;\n\n    db2->dict = aux.dict;\n    db2->expires = aux.expires;\n    db2->avg_ttl = aux.avg_ttl;\n    db2->expires_cursor = aux.expires_cursor;\n\n    /* Now we need to handle clients blocked on lists: as an effect\n     * of swapping the two DBs, a client that was waiting for list\n     * X in a given DB, may now actually be unblocked if X happens\n     * to exist in the new version of the DB, after the swap.\n     *\n     * However normally we only do this check for efficiency reasons\n     * in dbAdd() when a list is created. So here we need to rescan\n     * the list of clients blocked on lists and signal lists as ready\n     * if needed. */\n    scanDatabaseForReadyLists(db1);\n    scanDatabaseForReadyLists(db2);\n    return C_OK;\n}\n\n/* SWAPDB db1 db2 */\nvoid swapdbCommand(client *c) {\n    long id1, id2;\n\n    /* Not allowed in cluster mode: we have just DB 0 there. */\n    if (server.cluster_enabled) {\n        addReplyError(c,\"SWAPDB is not allowed in cluster mode\");\n        return;\n    }\n\n    /* Get the two DBs indexes. */\n    if (getLongFromObjectOrReply(c, c->argv[1], &id1,\n        \"invalid first DB index\") != C_OK)\n        return;\n\n    if (getLongFromObjectOrReply(c, c->argv[2], &id2,\n        \"invalid second DB index\") != C_OK)\n        return;\n\n    /* Swap... */\n    if (dbSwapDatabases(id1,id2) == C_ERR) {\n        addReplyError(c,\"DB index is out of range\");\n        return;\n    } else {\n        server.dirty++;\n        addReply(c,shared.ok);\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Expires API\n *----------------------------------------------------------------------------*/\n\nint removeExpire(redisDb *db, robj *key) {\n    /* An expire may only be removed if there is a corresponding entry in the\n     * main dict. Otherwise, the key will never be freed. */\n    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n    return dictDelete(db->expires,key->ptr) == DICT_OK;\n}\n\n/* Set an expire to the specified key. If the expire is set in the context\n * of an user calling a command 'c' is the client, otherwise 'c' is set\n * to NULL. The 'when' parameter is the absolute unix time in milliseconds\n * after which the key will no longer be considered valid. */\nvoid setExpire(client *c, redisDb *db, robj *key, long long when) {\n    dictEntry *kde, *de;\n\n    /* Reuse the sds from the main dict in the expire dict */\n    kde = dictFind(db->dict,key->ptr);\n    serverAssertWithInfo(NULL,key,kde != NULL);\n    de = dictAddOrFind(db->expires,dictGetKey(kde));\n    dictSetSignedIntegerVal(de,when);\n\n    int writable_slave = server.masterhost && server.repl_slave_ro == 0;\n    if (c && writable_slave && !(c->flags & CLIENT_MASTER))\n        rememberSlaveKeyWithExpire(db,key);\n}\n\n/* Return the expire time of the specified key, or -1 if no expire\n * is associated with this key (i.e. the key is non volatile) */\nlong long getExpire(redisDb *db, robj *key) {\n    dictEntry *de;\n\n    /* No expire? return ASAP */\n    if (dictSize(db->expires) == 0 ||\n       (de = dictFind(db->expires,key->ptr)) == NULL) return -1;\n\n    /* The entry was found in the expire dict, this means it should also\n     * be present in the main dict (safety check). */\n    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n    return dictGetSignedIntegerVal(de);\n}\n\n/* Propagate expires into slaves and the AOF file.\n * When a key expires in the master, a DEL operation for this key is sent\n * to all the slaves and the AOF file if enabled.\n *\n * This way the key expiry is centralized in one place, and since both\n * AOF and the master->slave link guarantee operation ordering, everything\n * will be consistent even if we allow write operations against expiring\n * keys. */\nvoid propagateExpire(redisDb *db, robj *key, int lazy) {\n    robj *argv[2];\n\n    argv[0] = lazy ? shared.unlink : shared.del;\n    argv[1] = key;\n    incrRefCount(argv[0]);\n    incrRefCount(argv[1]);\n\n    if (server.aof_state != AOF_OFF)\n        feedAppendOnlyFile(server.delCommand,db->id,argv,2);\n    replicationFeedSlaves(server.slaves,db->id,argv,2);\n\n    decrRefCount(argv[0]);\n    decrRefCount(argv[1]);\n}\n\n/* Check if the key is expired. */\nint keyIsExpired(redisDb *db, robj *key) {\n    mstime_t when = getExpire(db,key);\n    mstime_t now;\n\n    if (when < 0) return 0; /* No expire for this key */\n\n    /* Don't expire anything while loading. It will be done later. */\n    if (server.loading) return 0;\n\n    /* If we are in the context of a Lua script, we pretend 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    if (server.lua_caller) {\n        now = server.lua_time_start;\n    }\n    /* If we are in the middle of a command execution, we still want to use\n     * a reference time that does not change: in that case we just use the\n     * cached time, that we update before each call in the call() function.\n     * This way we avoid that commands such as RPOPLPUSH or similar, that\n     * may re-open the same key multiple times, can invalidate an already\n     * open object in a next call, if the next call will see the key expired,\n     * while the first did not. */\n    else if (server.fixed_time_expire > 0) {\n        now = server.mstime;\n    }\n    /* For the other cases, we want to use the most fresh time we have. */\n    else {\n        now = mstime();\n    }\n\n    /* The key expired if the current (virtual or real) time is greater\n     * than the expire time of the key. */\n    return now > when;\n}\n\n/* This function is called when we are going to perform some operation\n * in a given key, but such key may be already logically expired even if\n * it still exists in the database. The main way this function is called\n * is via lookupKey*() family of functions.\n *\n * The behavior of the function depends on the replication role of the\n * instance, because slave instances do not expire keys, they wait\n * for DELs from the master for consistency matters. However even\n * slaves will try to have a coherent return value for the function,\n * so that read commands executed in the slave side will be able to\n * behave like if the key is expired even if still present (because the\n * master has yet to propagate the DEL).\n *\n * In masters as a side effect of finding a key which is expired, such\n * key will be evicted from the database. Also this may trigger the\n * propagation of a DEL/UNLINK command in AOF / replication stream.\n *\n * The return value of the function is 0 if the key is still valid,\n * otherwise the function returns 1 if the key is expired. */\nint expireIfNeeded(redisDb *db, robj *key) {\n    if (!keyIsExpired(db,key)) return 0;\n\n    /* If we are running in the context of a slave, instead of\n     * evicting the expired key from the database, we return ASAP:\n     * the slave key expiration is controlled by the master that will\n     * send us synthesized DEL operations for expired keys.\n     *\n     * Still we try to return the right information to the caller,\n     * that is, 0 if we think the key should be still valid, 1 if\n     * we think the key is expired at this time. */\n    if (server.masterhost != NULL) return 1;\n\n    /* Delete the key */\n    server.stat_expiredkeys++;\n    propagateExpire(db,key,server.lazyfree_lazy_expire);\n    notifyKeyspaceEvent(NOTIFY_EXPIRED,\n        \"expired\",key,db->id);\n    int retval = server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :\n                                               dbSyncDelete(db,key);\n    if (retval) signalModifiedKey(NULL,db,key);\n    return retval;\n}\n\n/* -----------------------------------------------------------------------------\n * API to get key arguments from commands\n * ---------------------------------------------------------------------------*/\n#define MAX_KEYS_BUFFER 256\nstatic int getKeysTempBuffer[MAX_KEYS_BUFFER];\n\n/* The base case is to use the keys position as given in the command table\n * (firstkey, lastkey, step). */\nint *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {\n    int j, i = 0, last, *keys;\n    UNUSED(argv);\n\n    if (cmd->firstkey == 0) {\n        *numkeys = 0;\n        return NULL;\n    }\n\n    last = cmd->lastkey;\n    if (last < 0) last = argc+last;\n\n    int count = ((last - cmd->firstkey)+1);\n    keys = getKeysTempBuffer;\n    if (count > MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int)*count);\n\n    for (j = cmd->firstkey; j <= last; j += cmd->keystep) {\n        if (j >= argc) {\n            /* Modules commands, and standard commands with a not fixed number\n             * of arguments (negative arity parameter) do not have dispatch\n             * time arity checks, so we need to handle the case where the user\n             * passed an invalid number of arguments here. In this case we\n             * return no keys and expect the command implementation to report\n             * an arity or syntax error. */\n            if (cmd->flags & CMD_MODULE || cmd->arity < 0) {\n                getKeysFreeResult(keys);\n                *numkeys = 0;\n                return NULL;\n            } else {\n                serverPanic(\"Redis built-in command declared keys positions not matching the arity requirements.\");\n            }\n        }\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->flags & CMD_MODULE_GETKEYS) {\n        return moduleGetCommandKeysViaAPI(cmd,argv,argc,numkeys);\n    } else if (!(cmd->flags & CMD_MODULE) && 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    if (result != getKeysTempBuffer)\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    UNUSED(cmd);\n\n    num = atoi(argv[2]->ptr);\n    /* Sanity check. Don't return any key if the command is going to\n     * reply with syntax error. */\n    if (num < 1 || 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 = getKeysTempBuffer;\n    if (num+1>MAX_KEYS_BUFFER)\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    UNUSED(cmd);\n\n    num = atoi(argv[2]->ptr);\n    /* Sanity check. Don't return any key if the command is going to\n     * reply with syntax error. */\n    if (num <= 0 || num > (argc-3)) {\n        *numkeys = 0;\n        return NULL;\n    }\n\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int)*num);\n\n    *numkeys = num;\n\n    /* Add all key positions for argv[3...n] to keys[] */\n    for (i = 0; i < num; i++) keys[i] = 3+i;\n\n    return keys;\n}\n\n/* Helper function to extract keys from the SORT command.\n *\n * SORT <sort-key> ... STORE <store-key> ...\n *\n * The first argument of SORT is always a key, however a list of options\n * follow in SQL-alike style. Here we parse just the minimum in order to\n * correctly identify keys in the \"STORE\" option. */\nint *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, j, num, *keys, found_store = 0;\n    UNUSED(cmd);\n\n    num = 0;\n    keys = getKeysTempBuffer; /* Alloc 2 places for the worst case. */\n\n    keys[num++] = 1; /* <sort-key> is always present. */\n\n    /* Search for STORE option. By default we consider options to don't\n     * have arguments, so if we find an unknown option name we scan the\n     * next. However there are options with 1 or 2 arguments, so we\n     * provide a list here in order to skip the right number of args. */\n    struct {\n        char *name;\n        int skip;\n    } skiplist[] = {\n        {\"limit\", 2},\n        {\"get\", 1},\n        {\"by\", 1},\n        {NULL, 0} /* End of elements. */\n    };\n\n    for (i = 2; i < argc; i++) {\n        for (j = 0; skiplist[j].name != NULL; j++) {\n            if (!strcasecmp(argv[i]->ptr,skiplist[j].name)) {\n                i += skiplist[j].skip;\n                break;\n            } else if (!strcasecmp(argv[i]->ptr,\"store\") && i+1 < argc) {\n                /* Note: we don't increment \"num\" here and continue the loop\n                 * to be sure to process the *last* \"STORE\" option if multiple\n                 * ones are provided. This is same behavior as SORT. */\n                found_store = 1;\n                keys[num] = i+1; /* <store-key> */\n                break;\n            }\n        }\n    }\n    *numkeys = num + found_store;\n    return keys;\n}\n\nint *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, first, *keys;\n    UNUSED(cmd);\n\n    /* Assume the obvious form. */\n    first = 3;\n    num = 1;\n\n    /* But check for the extended one with the KEYS option. */\n    if (argc > 6) {\n        for (i = 6; i < argc; i++) {\n            if (!strcasecmp(argv[i]->ptr,\"keys\") &&\n                sdslen(argv[3]->ptr) == 0)\n            {\n                first = i+1;\n                num = argc-first;\n                break;\n            }\n        }\n    }\n\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int)*num);\n\n    for (i = 0; i < num; i++) keys[i] = first+i;\n    *numkeys = num;\n    return keys;\n}\n\n/* Helper function to extract keys from following commands:\n * GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]\n *                             [COUNT count] [STORE key] [STOREDIST key]\n * GEORADIUSBYMEMBER key member radius unit ... options ... */\nint *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, *keys;\n    UNUSED(cmd);\n\n    /* Check for the presence of the stored key in the command */\n    int stored_key = -1;\n    for (i = 5; i < argc; i++) {\n        char *arg = argv[i]->ptr;\n        /* For the case when user specifies both \"store\" and \"storedist\" options, the\n         * second key specified would override the first key. This behavior is kept\n         * the same as in georadiusCommand method.\n         */\n        if ((!strcasecmp(arg, \"store\") || !strcasecmp(arg, \"storedist\")) && ((i+1) < argc)) {\n            stored_key = i+1;\n            i++;\n        }\n    }\n    num = 1 + (stored_key == -1 ? 0 : 1);\n\n    /* Keys in the command come from two places:\n     * argv[1] = key,\n     * argv[5...n] = stored key if present\n     */\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int) * num);\n\n    /* Add all key positions to keys[] */\n    keys[0] = 1;\n    if(num > 1) {\n         keys[1] = stored_key;\n    }\n    *numkeys = num;\n    return keys;\n}\n\n/* LCS ... [KEYS <key1> <key2>] ... */\nint *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)\n{\n    int i;\n    int *keys = getKeysTempBuffer;\n    UNUSED(cmd);\n\n    /* We need to parse the options of the command in order to check for the\n     * \"KEYS\" argument before the \"STRINGS\" argument. */\n    for (i = 1; i < argc; i++) {\n        char *arg = argv[i]->ptr;\n        int moreargs = (argc-1) - i;\n\n        if (!strcasecmp(arg, \"strings\")) {\n            break;\n        } else if (!strcasecmp(arg, \"keys\") && moreargs >= 2) {\n            keys[0] = i+1;\n            keys[1] = i+2;\n            *numkeys = 2;\n            return keys;\n        }\n    }\n    *numkeys = 0;\n    return keys;\n}\n\n/* Helper function to extract keys from memory command.\n * MEMORY USAGE <key> */\nint *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int *keys;\n    UNUSED(cmd);\n\n    if (argc >= 3 && !strcasecmp(argv[1]->ptr,\"usage\")) {\n        keys = getKeysTempBuffer;\n        keys[0] = 2;\n        *numkeys = 1;\n        return keys;\n    }\n    *numkeys = 0;\n    return NULL;\n}\n\n/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]\n *       STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */\nint *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num = 0, *keys;\n    UNUSED(cmd);\n\n    /* We need to parse the options of the command in order to seek the first\n     * \"STREAMS\" string which is actually the option. This is needed because\n     * \"STREAMS\" could also be the name of the consumer group and even the\n     * name of the stream key. */\n    int streams_pos = -1;\n    for (i = 1; i < argc; i++) {\n        char *arg = argv[i]->ptr;\n        if (!strcasecmp(arg, \"block\")) {\n            i++; /* Skip option argument. */\n        } else if (!strcasecmp(arg, \"count\")) {\n            i++; /* Skip option argument. */\n        } else if (!strcasecmp(arg, \"group\")) {\n            i += 2; /* Skip option argument. */\n        } else if (!strcasecmp(arg, \"noack\")) {\n            /* Nothing to do. */\n        } else if (!strcasecmp(arg, \"streams\")) {\n            streams_pos = i;\n            break;\n        } else {\n            break; /* Syntax error. */\n        }\n    }\n    if (streams_pos != -1) num = argc - streams_pos - 1;\n\n    /* Syntax error. */\n    if (streams_pos == -1 || num == 0 || num % 2 != 0) {\n        *numkeys = 0;\n        return NULL;\n    }\n    num /= 2; /* We have half the keys as there are arguments because\n                 there are also the IDs, one per key. */\n\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int) * num);\n\n    for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i;\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 and in other conditions when we need to\n * understand if we have keys for a given hash slot. */\nvoid slotToKeyUpdateKey(sds key, int add) {\n    size_t keylen = sdslen(key);\n    unsigned int hashslot = keyHashSlot(key,keylen);\n    unsigned char buf[64];\n    unsigned char *indexed = buf;\n\n    server.cluster->slots_keys_count[hashslot] += add ? 1 : -1;\n    if (keylen+2 > 64) indexed = zmalloc(keylen+2);\n    indexed[0] = (hashslot >> 8) & 0xff;\n    indexed[1] = hashslot & 0xff;\n    memcpy(indexed+2,key,keylen);\n    if (add) {\n        raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL,NULL);\n    } else {\n        raxRemove(server.cluster->slots_to_keys,indexed,keylen+2,NULL);\n    }\n    if (indexed != buf) zfree(indexed);\n}\n\nvoid slotToKeyAdd(sds key) {\n    slotToKeyUpdateKey(key,1);\n}\n\nvoid slotToKeyDel(sds key) {\n    slotToKeyUpdateKey(key,0);\n}\n\nvoid slotToKeyFlush(void) {\n    raxFree(server.cluster->slots_to_keys);\n    server.cluster->slots_to_keys = raxNew();\n    memset(server.cluster->slots_keys_count,0,\n           sizeof(server.cluster->slots_keys_count));\n}\n\n/* Pupulate the specified array of objects with keys in the specified slot.\n * New objects are returned to represent keys, it's up to the caller to\n * decrement the reference count to release the keys names. */\nunsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count) {\n    raxIterator iter;\n    int j = 0;\n    unsigned char indexed[2];\n\n    indexed[0] = (hashslot >> 8) & 0xff;\n    indexed[1] = hashslot & 0xff;\n    raxStart(&iter,server.cluster->slots_to_keys);\n    raxSeek(&iter,\">=\",indexed,2);\n    while(count-- && raxNext(&iter)) {\n        if (iter.key[0] != indexed[0] || iter.key[1] != indexed[1]) break;\n        keys[j++] = createStringObject((char*)iter.key+2,iter.key_len-2);\n    }\n    raxStop(&iter);\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    raxIterator iter;\n    int j = 0;\n    unsigned char indexed[2];\n\n    indexed[0] = (hashslot >> 8) & 0xff;\n    indexed[1] = hashslot & 0xff;\n    raxStart(&iter,server.cluster->slots_to_keys);\n    while(server.cluster->slots_keys_count[hashslot]) {\n        raxSeek(&iter,\">=\",indexed,2);\n        raxNext(&iter);\n\n        robj *key = createStringObject((char*)iter.key+2,iter.key_len-2);\n        dbDelete(&server.db[0],key);\n        decrRefCount(key);\n        j++;\n    }\n    raxStop(&iter);\n    return j;\n}\n\nunsigned int countKeysInSlot(unsigned int hashslot) {\n    return server.cluster->slots_keys_count[hashslot];\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.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#include <dlfcn.h>\n\n#ifdef HAVE_BACKTRACE\n#include <execinfo.h>\n#ifndef __OpenBSD__\n#include <ucontext.h>\n#else\ntypedef ucontext_t sigcontext_t;\n#endif\n#include <fcntl.h>\n#include \"bio.h\"\n#include <unistd.h>\n#endif /* HAVE_BACKTRACE */\n\n#ifdef __CYGWIN__\n#ifndef SA_ONSTACK\n#define SA_ONSTACK 0x08000000\n#endif\n#endif\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 xorStringObjectDigest(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 mixStringObjectDigest(unsigned char *digest, robj *o) {\n    o = getDecodedObject(o);\n    mixDigest(digest,o->ptr,sdslen(o->ptr));\n    decrRefCount(o);\n}\n\n/* This function computes the digest of a data structure stored in the\n * object 'o'. It is the core of the DEBUG DIGEST command: when taking the\n * digest of a whole dataset, we take the digest of the key and the value\n * pair, and xor all those together.\n *\n * Note that this function does not reset the initial 'digest' passed, it\n * will continue mixing this object digest to anything that was already\n * present. */\nvoid xorObjectDigest(redisDb *db, robj *keyobj, unsigned char *digest, robj *o) {\n    uint32_t aux = htonl(o->type);\n    mixDigest(digest,&aux,sizeof(aux));\n    long long expiretime = getExpire(db,keyobj);\n    char buf[128];\n\n    /* Save the key and associated value */\n    if (o->type == OBJ_STRING) {\n        mixStringObjectDigest(digest,o);\n    } else if (o->type == OBJ_LIST) {\n        listTypeIterator *li = listTypeInitIterator(o,0,LIST_TAIL);\n        listTypeEntry entry;\n        while(listTypeNext(li,&entry)) {\n            robj *eleobj = listTypeGet(&entry);\n            mixStringObjectDigest(digest,eleobj);\n            decrRefCount(eleobj);\n        }\n        listTypeReleaseIterator(li);\n    } else if (o->type == OBJ_SET) {\n        setTypeIterator *si = setTypeInitIterator(o);\n        sds sdsele;\n        while((sdsele = setTypeNextObject(si)) != NULL) {\n            xorDigest(digest,sdsele,sdslen(sdsele));\n            sdsfree(sdsele);\n        }\n        setTypeReleaseIterator(si);\n    } else if (o->type == OBJ_ZSET) {\n        unsigned char eledigest[20];\n\n        if (o->encoding == OBJ_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            serverAssert(eptr != NULL);\n            sptr = ziplistNext(zl,eptr);\n            serverAssert(sptr != NULL);\n\n            while (eptr != NULL) {\n                serverAssert(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 == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = o->ptr;\n            dictIterator *di = dictGetIterator(zs->dict);\n            dictEntry *de;\n\n            while((de = dictNext(di)) != NULL) {\n                sds sdsele = dictGetKey(de);\n                double *score = dictGetVal(de);\n\n                snprintf(buf,sizeof(buf),\"%.17g\",*score);\n                memset(eledigest,0,20);\n                mixDigest(eledigest,sdsele,sdslen(sdsele));\n                mixDigest(eledigest,buf,strlen(buf));\n                xorDigest(digest,eledigest,20);\n            }\n            dictReleaseIterator(di);\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (o->type == OBJ_HASH) {\n        hashTypeIterator *hi = hashTypeInitIterator(o);\n        while (hashTypeNext(hi) != C_ERR) {\n            unsigned char eledigest[20];\n            sds sdsele;\n\n            memset(eledigest,0,20);\n            sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);\n            mixDigest(eledigest,sdsele,sdslen(sdsele));\n            sdsfree(sdsele);\n            sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);\n            mixDigest(eledigest,sdsele,sdslen(sdsele));\n            sdsfree(sdsele);\n            xorDigest(digest,eledigest,20);\n        }\n        hashTypeReleaseIterator(hi);\n    } else if (o->type == OBJ_STREAM) {\n        streamIterator si;\n        streamIteratorStart(&si,o->ptr,NULL,NULL,0);\n        streamID id;\n        int64_t numfields;\n\n        while(streamIteratorGetID(&si,&id,&numfields)) {\n            sds itemid = sdscatfmt(sdsempty(),\"%U.%U\",id.ms,id.seq);\n            mixDigest(digest,itemid,sdslen(itemid));\n            sdsfree(itemid);\n\n            while(numfields--) {\n                unsigned char *field, *value;\n                int64_t field_len, value_len;\n                streamIteratorGetField(&si,&field,&value,\n                                           &field_len,&value_len);\n                mixDigest(digest,field,field_len);\n                mixDigest(digest,value,value_len);\n            }\n        }\n        streamIteratorStop(&si);\n    } else if (o->type == OBJ_MODULE) {\n        RedisModuleDigest md;\n        moduleValue *mv = o->ptr;\n        moduleType *mt = mv->type;\n        moduleInitDigestContext(md);\n        if (mt->digest) {\n            mt->digest(&md,mv->value);\n            xorDigest(digest,md.x,sizeof(md.x));\n        }\n    } else {\n        serverPanic(\"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}\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    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 = dictGetSafeIterator(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\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            xorObjectDigest(db,keyobj,digest,o);\n\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\n#ifdef USE_JEMALLOC\nvoid mallctl_int(client *c, robj **argv, int argc) {\n    int ret;\n    /* start with the biggest size (int64), and if that fails, try smaller sizes (int32, bool) */\n    int64_t old = 0, val;\n    if (argc > 1) {\n        long long ll;\n        if (getLongLongFromObjectOrReply(c, argv[1], &ll, NULL) != C_OK)\n            return;\n        val = ll;\n    }\n    size_t sz = sizeof(old);\n    while (sz > 0) {\n        if ((ret=je_mallctl(argv[0]->ptr, &old, &sz, argc > 1? &val: NULL, argc > 1?sz: 0))) {\n            if (ret==EINVAL) {\n                /* size might be wrong, try a smaller one */\n                sz /= 2;\n#if BYTE_ORDER == BIG_ENDIAN\n                val <<= 8*sz;\n#endif\n                continue;\n            }\n            addReplyErrorFormat(c,\"%s\", strerror(ret));\n            return;\n        } else {\n#if BYTE_ORDER == BIG_ENDIAN\n            old >>= 64 - 8*sz;\n#endif\n            addReplyLongLong(c, old);\n            return;\n        }\n    }\n    addReplyErrorFormat(c,\"%s\", strerror(EINVAL));\n}\n\nvoid mallctl_string(client *c, robj **argv, int argc) {\n    int ret;\n    char *old;\n    size_t sz = sizeof(old);\n    /* for strings, it seems we need to first get the old value, before overriding it. */\n    if ((ret=je_mallctl(argv[0]->ptr, &old, &sz, NULL, 0))) {\n        addReplyErrorFormat(c,\"%s\", strerror(ret));\n        return;\n    }\n    addReplyBulkCString(c, old);\n    if(argc > 1)\n        je_mallctl(argv[0]->ptr, NULL, 0, &argv[1]->ptr, sizeof(char*));\n}\n#endif\n\nvoid debugCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ASSERT -- Crash by assertion failed.\",\n\"CHANGE-REPL-ID -- Change the replication IDs of the instance. Dangerous, should be used only for testing the replication subsystem.\",\n\"CRASH-AND-RECOVER <milliseconds> -- Hard crash and restart after <milliseconds> delay.\",\n\"DIGEST -- Output a hex signature representing the current DB content.\",\n\"DIGEST-VALUE <key-1> ... <key-N>-- Output a hex signature of the values of all the specified keys.\",\n\"DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false]\",\n\"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.\",\n\"LOG <message> -- write message to the server log.\",\n\"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.\",\n\"HTSTATS-KEY <key> -- Like htstats but for the hash table stored as key's value.\",\n\"LOADAOF -- Flush the AOF buffers on disk and reload the AOF in memory.\",\n\"LUA-ALWAYS-REPLICATE-COMMANDS <0|1> -- Setting it to 1 makes Lua replication defaulting to replicating single commands, without the script having to enable effects replication.\",\n\"OBJECT <key> -- Show low level info about key and associated value.\",\n\"OOM -- Crash the server simulating an out-of-memory error.\",\n\"PANIC -- Crash the server simulating a panic.\",\n\"POPULATE <count> [prefix] [size] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix.\",\n\"RELOAD [MERGE] [NOFLUSH] [NOSAVE] -- Save the RDB on disk and reload it back in memory. By default it will save the RDB file and load it back. With the NOFLUSH option the current database is not removed before loading the new one, but conficts in keys will kill the server with an exception. When MERGE is used, conflicting keys will be loaded (the key in the loaded RDB file will win). When NOSAVE is used, the server will not save the current dataset in the RDB file before loading. Use DEBUG RELOAD NOSAVE when you want just to load the RDB file you placed in the Redis working directory in order to replace the current dataset in memory. Use DEBUG RELOAD NOSAVE NOFLUSH MERGE when you want to add what is in the current RDB file placed in the Redis current directory, with the current memory content. Use DEBUG RELOAD when you want to verify Redis is able to persist the current dataset in the RDB file, flush the memory content, and load it back.\",\n\"RESTART -- Graceful restart: save config, db, restart.\",\n\"SDSLEN <key> -- Show low level SDS string info representing key and value.\",\n\"SEGFAULT -- Crash the server with sigsegv.\",\n\"SET-ACTIVE-EXPIRE <0|1> -- Setting it to 0 disables expiring keys in background when they are not accessed (otherwise the Redis behavior). Setting it to 1 reenables back the default.\",\n\"AOF-FLUSH-SLEEP <microsec> -- Server will sleep before flushing the AOF, this is used for testing\",\n\"SLEEP <seconds> -- Stop the server for <seconds>. Decimals allowed.\",\n\"STRUCTSIZE -- Return the size of different Redis core C structures.\",\n\"ZIPLIST <key> -- Show low level info about the ziplist encoding.\",\n\"STRINGMATCH-TEST -- Run a fuzz tester against the stringmatchlen() function.\",\n#ifdef USE_JEMALLOC\n\"MALLCTL <key> [<val>] -- Get or set a malloc tunning integer.\",\n\"MALLCTL-STR <key> [<val>] -- Get or set a malloc tunning string.\",\n#endif\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"segfault\")) {\n        *((char*)-1) = 'x';\n    } else if (!strcasecmp(c->argv[1]->ptr,\"panic\")) {\n        serverPanic(\"DEBUG PANIC called at Unix time %ld\", time(NULL));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"restart\") ||\n               !strcasecmp(c->argv[1]->ptr,\"crash-and-recover\"))\n    {\n        long long delay = 0;\n        if (c->argc >= 3) {\n            if (getLongLongFromObjectOrReply(c, c->argv[2], &delay, NULL)\n                != C_OK) return;\n            if (delay < 0) delay = 0;\n        }\n        int flags = !strcasecmp(c->argv[1]->ptr,\"restart\") ?\n            (RESTART_SERVER_GRACEFULLY|RESTART_SERVER_CONFIG_REWRITE) :\n             RESTART_SERVER_NONE;\n        restartServer(flags,delay);\n        addReplyError(c,\"failed to restart the server. Check server logs.\");\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        serverAssertWithInfo(c,c->argv[0],1 == 2);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"log\") && c->argc == 3) {\n        serverLog(LL_WARNING, \"DEBUG LOG: %s\", (char*)c->argv[2]->ptr);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reload\")) {\n        int flush = 1, save = 1;\n        int flags = RDBFLAGS_NONE;\n\n        /* Parse the additional options that modify the RELOAD\n         * behavior. */\n        for (int j = 2; j < c->argc; j++) {\n            char *opt = c->argv[j]->ptr;\n            if (!strcasecmp(opt,\"MERGE\")) {\n                flags |= RDBFLAGS_ALLOW_DUP;\n            } else if (!strcasecmp(opt,\"NOFLUSH\")) {\n                flush = 0;\n            } else if (!strcasecmp(opt,\"NOSAVE\")) {\n                save = 0;\n            } else {\n                addReplyError(c,\"DEBUG RELOAD only supports the \"\n                                \"MERGE, NOFLUSH and NOSAVE options.\");\n                return;\n            }\n        }\n\n        /* The default beahvior is to save the RDB file before loading\n         * it back. */\n        if (save) {\n            rdbSaveInfo rsi, *rsiptr;\n            rsiptr = rdbPopulateSaveInfo(&rsi);\n            if (rdbSave(server.rdb_filename,rsiptr) != C_OK) {\n                addReply(c,shared.err);\n                return;\n            }\n        }\n\n        /* The default behavior is to remove the current dataset from\n         * memory before loading the RDB file, however when MERGE is\n         * used together with NOFLUSH, we are able to merge two datasets. */\n        if (flush) emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);\n\n        protectClient(c);\n        int ret = rdbLoad(server.rdb_filename,NULL,flags);\n        unprotectClient(c);\n        if (ret != C_OK) {\n            addReplyError(c,\"Error trying to load the RDB dump\");\n            return;\n        }\n        serverLog(LL_WARNING,\"DB reloaded by DEBUG RELOAD\");\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"loadaof\")) {\n        if (server.aof_state != AOF_OFF) flushAppendOnlyFile(1);\n        emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);\n        protectClient(c);\n        int ret = loadAppendOnlyFile(server.aof_filename);\n        unprotectClient(c);\n        if (ret != C_OK) {\n            addReply(c,shared.err);\n            return;\n        }\n        server.dirty = 0; /* Prevent AOF / replication */\n        serverLog(LL_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        char extra[138] = {0};\n        if (val->encoding == OBJ_ENCODING_QUICKLIST) {\n            char *nextra = extra;\n            int remaining = sizeof(extra);\n            quicklist *ql = val->ptr;\n            /* Add number of quicklist nodes */\n            int used = snprintf(nextra, remaining, \" ql_nodes:%lu\", ql->len);\n            nextra += used;\n            remaining -= used;\n            /* Add average quicklist fill factor */\n            double avg = (double)ql->count/ql->len;\n            used = snprintf(nextra, remaining, \" ql_avg_node:%.2f\", avg);\n            nextra += used;\n            remaining -= used;\n            /* Add quicklist fill level / max ziplist size */\n            used = snprintf(nextra, remaining, \" ql_ziplist_max:%d\", ql->fill);\n            nextra += used;\n            remaining -= used;\n            /* Add isCompressed? */\n            int compressed = ql->compress != 0;\n            used = snprintf(nextra, remaining, \" ql_compressed:%d\", compressed);\n            nextra += used;\n            remaining -= used;\n            /* Add total uncompressed size */\n            unsigned long sz = 0;\n            for (quicklistNode *node = ql->head; node; node = node->next) {\n                sz += node->sz;\n            }\n            used = snprintf(nextra, remaining, \" ql_uncompressed_size:%lu\", sz);\n            nextra += used;\n            remaining -= used;\n        }\n\n        addReplyStatusFormat(c,\n            \"Value at:%p refcount:%d \"\n            \"encoding:%s serializedlength:%zu \"\n            \"lru:%d lru_seconds_idle:%llu%s\",\n            (void*)val, val->refcount,\n            strenc, rdbSavedObjectLen(val, c->argv[2]),\n            val->lru, estimateObjectIdleTime(val)/1000, extra);\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 != OBJ_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, key_zmalloc: %lld, \"\n                \"val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld\",\n                (long long) sdslen(key),\n                (long long) sdsavail(key),\n                (long long) sdsZmallocSize(key),\n                (long long) sdslen(val->ptr),\n                (long long) sdsavail(val->ptr),\n                (long long) getStringObjectSdsUsedMemory(val));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"ziplist\") && c->argc == 3) {\n        robj *o;\n\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))\n                == NULL) return;\n\n        if (o->encoding != OBJ_ENCODING_ZIPLIST) {\n            addReplyError(c,\"Not an sds encoded string.\");\n        } else {\n            ziplistRepr(o->ptr);\n            addReplyStatus(c,\"Ziplist structure printed on stdout\");\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"populate\") &&\n               c->argc >= 3 && c->argc <= 5) {\n        long keys, j;\n        robj *key, *val;\n        char buf[128];\n\n        if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)\n            return;\n        dictExpand(c->db->dict,keys);\n        for (j = 0; j < keys; j++) {\n            long valsize = 0;\n            snprintf(buf,sizeof(buf),\"%s:%lu\",\n                (c->argc == 3) ? \"key\" : (char*)c->argv[3]->ptr, j);\n            key = createStringObject(buf,strlen(buf));\n            if (c->argc == 5)\n                if (getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK)\n                    return;\n            if (lookupKeyWrite(c->db,key) != NULL) {\n                decrRefCount(key);\n                continue;\n            }\n            snprintf(buf,sizeof(buf),\"value:%lu\",j);\n            if (valsize==0)\n                val = createStringObject(buf,strlen(buf));\n            else {\n                int buflen = strlen(buf);\n                val = createStringObject(NULL,valsize);\n                memcpy(val->ptr, buf, valsize<=buflen? valsize: buflen);\n            }\n            dbAdd(c->db,key,val);\n            signalModifiedKey(c,c->db,key);\n            decrRefCount(key);\n        }\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"digest\") && c->argc == 2) {\n        /* DEBUG DIGEST (form without keys specified) */\n        unsigned char digest[20];\n        sds d = sdsempty();\n\n        computeDatasetDigest(digest);\n        for (int i = 0; i < 20; i++) d = sdscatprintf(d, \"%02x\",digest[i]);\n        addReplyStatus(c,d);\n        sdsfree(d);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"digest-value\") && c->argc >= 2) {\n        /* DEBUG DIGEST-VALUE key key key ... key. */\n        addReplyArrayLen(c,c->argc-2);\n        for (int j = 2; j < c->argc; j++) {\n            unsigned char digest[20];\n            memset(digest,0,20); /* Start with a clean result */\n            robj *o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);\n            if (o) xorObjectDigest(c->db,c->argv[j],digest,o);\n\n            sds d = sdsempty();\n            for (int i = 0; i < 20; i++) d = sdscatprintf(d, \"%02x\",digest[i]);\n            addReplyStatus(c,d);\n            sdsfree(d);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"protocol\") && c->argc == 3) {\n        /* DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|\n         *                 attrib|push|verbatim|true|false] */\n        char *name = c->argv[2]->ptr;\n        if (!strcasecmp(name,\"string\")) {\n            addReplyBulkCString(c,\"Hello World\");\n        } else if (!strcasecmp(name,\"integer\")) {\n            addReplyLongLong(c,12345);\n        } else if (!strcasecmp(name,\"double\")) {\n            addReplyDouble(c,3.14159265359);\n        } else if (!strcasecmp(name,\"bignum\")) {\n            addReplyProto(c,\"(1234567999999999999999999999999999999\\r\\n\",40);\n        } else if (!strcasecmp(name,\"null\")) {\n            addReplyNull(c);\n        } else if (!strcasecmp(name,\"array\")) {\n            addReplyArrayLen(c,3);\n            for (int j = 0; j < 3; j++) addReplyLongLong(c,j);\n        } else if (!strcasecmp(name,\"set\")) {\n            addReplySetLen(c,3);\n            for (int j = 0; j < 3; j++) addReplyLongLong(c,j);\n        } else if (!strcasecmp(name,\"map\")) {\n            addReplyMapLen(c,3);\n            for (int j = 0; j < 3; j++) {\n                addReplyLongLong(c,j);\n                addReplyBool(c, j == 1);\n            }\n        } else if (!strcasecmp(name,\"attrib\")) {\n            addReplyAttributeLen(c,1);\n            addReplyBulkCString(c,\"key-popularity\");\n            addReplyArrayLen(c,2);\n            addReplyBulkCString(c,\"key:123\");\n            addReplyLongLong(c,90);\n            /* Attributes are not real replies, so a well formed reply should\n             * also have a normal reply type after the attribute. */\n            addReplyBulkCString(c,\"Some real reply following the attribute\");\n        } else if (!strcasecmp(name,\"push\")) {\n            addReplyPushLen(c,2);\n            addReplyBulkCString(c,\"server-cpu-usage\");\n            addReplyLongLong(c,42);\n            /* Push replies are not synchronous replies, so we emit also a\n             * normal reply in order for blocking clients just discarding the\n             * push reply, to actually consume the reply and continue. */\n            addReplyBulkCString(c,\"Some real reply following the push reply\");\n        } else if (!strcasecmp(name,\"true\")) {\n            addReplyBool(c,1);\n        } else if (!strcasecmp(name,\"false\")) {\n            addReplyBool(c,0);\n        } else if (!strcasecmp(name,\"verbatim\")) {\n            addReplyVerbatim(c,\"This is a verbatim\\nstring\",25,\"txt\");\n        } else {\n            addReplyError(c,\"Wrong protocol type name. Please use one of the following: string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false\");\n        }\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,\"aof-flush-sleep\") &&\n               c->argc == 3)\n    {\n        server.aof_flush_sleep = atoi(c->argv[2]->ptr);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"lua-always-replicate-commands\") &&\n               c->argc == 3)\n    {\n        server.lua_always_replicate_commands = atoi(c->argv[2]->ptr);\n        addReply(c,shared.ok);\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 if (!strcasecmp(c->argv[1]->ptr,\"structsize\") && c->argc == 2) {\n        sds sizes = sdsempty();\n        sizes = sdscatprintf(sizes,\"bits:%d \",(sizeof(void*) == 8)?64:32);\n        sizes = sdscatprintf(sizes,\"robj:%d \",(int)sizeof(robj));\n        sizes = sdscatprintf(sizes,\"dictentry:%d \",(int)sizeof(dictEntry));\n        sizes = sdscatprintf(sizes,\"sdshdr5:%d \",(int)sizeof(struct sdshdr5));\n        sizes = sdscatprintf(sizes,\"sdshdr8:%d \",(int)sizeof(struct sdshdr8));\n        sizes = sdscatprintf(sizes,\"sdshdr16:%d \",(int)sizeof(struct sdshdr16));\n        sizes = sdscatprintf(sizes,\"sdshdr32:%d \",(int)sizeof(struct sdshdr32));\n        sizes = sdscatprintf(sizes,\"sdshdr64:%d \",(int)sizeof(struct sdshdr64));\n        addReplyBulkSds(c,sizes);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"htstats\") && c->argc == 3) {\n        long dbid;\n        sds stats = sdsempty();\n        char buf[4096];\n\n        if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) {\n            sdsfree(stats);\n            return;\n        }\n        if (dbid < 0 || dbid >= server.dbnum) {\n            sdsfree(stats);\n            addReplyError(c,\"Out of range database\");\n            return;\n        }\n\n        stats = sdscatprintf(stats,\"[Dictionary HT]\\n\");\n        dictGetStats(buf,sizeof(buf),server.db[dbid].dict);\n        stats = sdscat(stats,buf);\n\n        stats = sdscatprintf(stats,\"[Expires HT]\\n\");\n        dictGetStats(buf,sizeof(buf),server.db[dbid].expires);\n        stats = sdscat(stats,buf);\n\n        addReplyVerbatim(c,stats,sdslen(stats),\"txt\");\n        sdsfree(stats);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"htstats-key\") && c->argc == 3) {\n        robj *o;\n        dict *ht = NULL;\n\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))\n                == NULL) return;\n\n        /* Get the hash table reference from the object, if possible. */\n        switch (o->encoding) {\n        case OBJ_ENCODING_SKIPLIST:\n            {\n                zset *zs = o->ptr;\n                ht = zs->dict;\n            }\n            break;\n        case OBJ_ENCODING_HT:\n            ht = o->ptr;\n            break;\n        }\n\n        if (ht == NULL) {\n            addReplyError(c,\"The value stored at the specified key is not \"\n                            \"represented using an hash table\");\n        } else {\n            char buf[4096];\n            dictGetStats(buf,sizeof(buf),ht);\n            addReplyVerbatim(c,buf,strlen(buf),\"txt\");\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"change-repl-id\") && c->argc == 2) {\n        serverLog(LL_WARNING,\"Changing replication IDs after receiving DEBUG change-repl-id\");\n        changeReplicationId();\n        clearReplicationId2();\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"stringmatch-test\") && c->argc == 2)\n    {\n        stringmatchlen_fuzz_test();\n        addReplyStatus(c,\"Apparently Redis did not crash: test passed\");\n#ifdef USE_JEMALLOC\n    } else if(!strcasecmp(c->argv[1]->ptr,\"mallctl\") && c->argc >= 3) {\n        mallctl_int(c, c->argv+2, c->argc-2);\n        return;\n    } else if(!strcasecmp(c->argv[1]->ptr,\"mallctl-str\") && c->argc >= 3) {\n        mallctl_string(c, c->argv+2, c->argc-2);\n        return;\n#endif\n    } else {\n        addReplySubcommandSyntaxError(c);\n        return;\n    }\n}\n\n/* =========================== Crash handling  ============================== */\n\nvoid _serverAssert(const char *estr, const char *file, int line) {\n    bugReportStart();\n    serverLog(LL_WARNING,\"=== ASSERTION FAILED ===\");\n    serverLog(LL_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    serverLog(LL_WARNING,\"(forcing SIGSEGV to print the bug report.)\");\n#endif\n    *((char*)-1) = 'x';\n}\n\nvoid _serverAssertPrintClientInfo(const client *c) {\n    int j;\n    char conninfo[CONN_INFO_LEN];\n\n    bugReportStart();\n    serverLog(LL_WARNING,\"=== ASSERTION FAILED CLIENT CONTEXT ===\");\n    serverLog(LL_WARNING,\"client->flags = %llu\", (unsigned long long) c->flags);\n    serverLog(LL_WARNING,\"client->conn = %s\", connGetInfo(c->conn, conninfo, sizeof(conninfo)));\n    serverLog(LL_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 == OBJ_STRING && sdsEncodedObject(c->argv[j])) {\n            arg = (char*) c->argv[j]->ptr;\n        } else {\n            snprintf(buf,sizeof(buf),\"Object type: %u, encoding: %u\",\n                c->argv[j]->type, c->argv[j]->encoding);\n            arg = buf;\n        }\n        serverLog(LL_WARNING,\"client->argv[%d] = \\\"%s\\\" (refcount: %d)\",\n            j, arg, c->argv[j]->refcount);\n    }\n}\n\nvoid serverLogObjectDebugInfo(const robj *o) {\n    serverLog(LL_WARNING,\"Object type: %d\", o->type);\n    serverLog(LL_WARNING,\"Object encoding: %d\", o->encoding);\n    serverLog(LL_WARNING,\"Object refcount: %d\", o->refcount);\n    if (o->type == OBJ_STRING && sdsEncodedObject(o)) {\n        serverLog(LL_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            serverLog(LL_WARNING,\"Object raw string content: %s\", repr);\n            sdsfree(repr);\n        }\n    } else if (o->type == OBJ_LIST) {\n        serverLog(LL_WARNING,\"List length: %d\", (int) listTypeLength(o));\n    } else if (o->type == OBJ_SET) {\n        serverLog(LL_WARNING,\"Set size: %d\", (int) setTypeSize(o));\n    } else if (o->type == OBJ_HASH) {\n        serverLog(LL_WARNING,\"Hash size: %d\", (int) hashTypeLength(o));\n    } else if (o->type == OBJ_ZSET) {\n        serverLog(LL_WARNING,\"Sorted set size: %d\", (int) zsetLength(o));\n        if (o->encoding == OBJ_ENCODING_SKIPLIST)\n            serverLog(LL_WARNING,\"Skiplist level: %d\", (int) ((const zset*)o->ptr)->zsl->level);\n    } else if (o->type == OBJ_STREAM) {\n        serverLog(LL_WARNING,\"Stream size: %d\", (int) streamLength(o));\n    }\n}\n\nvoid _serverAssertPrintObject(const robj *o) {\n    bugReportStart();\n    serverLog(LL_WARNING,\"=== ASSERTION FAILED OBJECT CONTEXT ===\");\n    serverLogObjectDebugInfo(o);\n}\n\nvoid _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line) {\n    if (c) _serverAssertPrintClientInfo(c);\n    if (o) _serverAssertPrintObject(o);\n    _serverAssert(estr,file,line);\n}\n\nvoid _serverPanic(const char *file, int line, const char *msg, ...) {\n    va_list ap;\n    va_start(ap,msg);\n    char fmtmsg[256];\n    vsnprintf(fmtmsg,sizeof(fmtmsg),msg,ap);\n    va_end(ap);\n\n    bugReportStart();\n    serverLog(LL_WARNING,\"------------------------------------------------\");\n    serverLog(LL_WARNING,\"!!! Software Failure. Press left mouse button to continue\");\n    serverLog(LL_WARNING,\"Guru Meditation: %s #%s:%d\",fmtmsg,file,line);\n#ifdef HAVE_BACKTRACE\n    serverLog(LL_WARNING,\"(forcing SIGSEGV in order to print the stack trace)\");\n#endif\n    serverLog(LL_WARNING,\"------------------------------------------------\");\n    *((char*)-1) = 'x';\n}\n\nvoid bugReportStart(void) {\n    if (server.bug_report_start == 0) {\n        serverLogRaw(LL_WARNING|LL_RAW,\n        \"\\n\\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\\n\");\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__) || defined(__ILP32__)\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    #elif defined(__arm__) /* Linux ARM */\n    return (void*) uc->uc_mcontext.arm_pc;\n    #elif defined(__aarch64__) /* Linux AArch64 */\n    return (void*) uc->uc_mcontext.pc;\n    #endif\n#elif defined(__FreeBSD__)\n    /* FreeBSD */\n    #if defined(__i386__)\n    return (void*) uc->uc_mcontext.mc_eip;\n    #elif defined(__x86_64__)\n    return (void*) uc->uc_mcontext.mc_rip;\n    #endif\n#elif defined(__OpenBSD__)\n    /* OpenBSD */\n    #if defined(__i386__)\n    return (void*) uc->sc_eip;\n    #elif defined(__x86_64__)\n    return (void*) uc->sc_rip;\n    #endif\n#elif defined(__DragonFly__)\n    return (void*) uc->uc_mcontext.mc_rip;\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            serverLog(LL_WARNING, \"(%08lx) -> %08lx\", addr, val);\n        else\n            serverLog(LL_WARNING, \"(%016lx) -> %016lx\", addr, val);\n    }\n}\n\nvoid logRegisters(ucontext_t *uc) {\n    serverLog(LL_WARNING|LL_RAW, \"\\n------ REGISTERS ------\\n\");\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    serverLog(LL_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    serverLog(LL_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__) || defined(__ILP32__)\n    serverLog(LL_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    serverLog(LL_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    #elif defined(__aarch64__) /* Linux AArch64 */\n    serverLog(LL_WARNING,\n\t      \"\\n\"\n\t      \"X18:%016lx X19:%016lx\\nX20:%016lx X21:%016lx\\n\"\n\t      \"X22:%016lx X23:%016lx\\nX24:%016lx X25:%016lx\\n\"\n\t      \"X26:%016lx X27:%016lx\\nX28:%016lx X29:%016lx\\n\"\n\t      \"X30:%016lx\\n\"\n\t      \"pc:%016lx sp:%016lx\\npstate:%016lx fault_address:%016lx\\n\",\n\t      (unsigned long) uc->uc_mcontext.regs[18],\n\t      (unsigned long) uc->uc_mcontext.regs[19],\n\t      (unsigned long) uc->uc_mcontext.regs[20],\n\t      (unsigned long) uc->uc_mcontext.regs[21],\n\t      (unsigned long) uc->uc_mcontext.regs[22],\n\t      (unsigned long) uc->uc_mcontext.regs[23],\n\t      (unsigned long) uc->uc_mcontext.regs[24],\n\t      (unsigned long) uc->uc_mcontext.regs[25],\n\t      (unsigned long) uc->uc_mcontext.regs[26],\n\t      (unsigned long) uc->uc_mcontext.regs[27],\n\t      (unsigned long) uc->uc_mcontext.regs[28],\n\t      (unsigned long) uc->uc_mcontext.regs[29],\n\t      (unsigned long) uc->uc_mcontext.regs[30],\n\t      (unsigned long) uc->uc_mcontext.pc,\n\t      (unsigned long) uc->uc_mcontext.sp,\n\t      (unsigned long) uc->uc_mcontext.pstate,\n\t      (unsigned long) uc->uc_mcontext.fault_address\n\t\t      );\n\t      logStackContent((void**)uc->uc_mcontext.sp);\n    #elif defined(__arm__) /* Linux ARM */\n    serverLog(LL_WARNING,\n\t      \"\\n\"\n\t      \"R10:%016lx R9 :%016lx\\nR8 :%016lx R7 :%016lx\\n\"\n\t      \"R6 :%016lx R5 :%016lx\\nR4 :%016lx R3 :%016lx\\n\"\n\t      \"R2 :%016lx R1 :%016lx\\nR0 :%016lx EC :%016lx\\n\"\n\t      \"fp: %016lx ip:%016lx\\n\",\n\t      \"pc:%016lx sp:%016lx\\ncpsr:%016lx fault_address:%016lx\\n\",\n\t      (unsigned long) uc->uc_mcontext.arm_r10,\n\t      (unsigned long) uc->uc_mcontext.arm_r9,\n\t      (unsigned long) uc->uc_mcontext.arm_r8,\n\t      (unsigned long) uc->uc_mcontext.arm_r7,\n\t      (unsigned long) uc->uc_mcontext.arm_r6,\n\t      (unsigned long) uc->uc_mcontext.arm_r5,\n\t      (unsigned long) uc->uc_mcontext.arm_r4,\n\t      (unsigned long) uc->uc_mcontext.arm_r3,\n\t      (unsigned long) uc->uc_mcontext.arm_r2,\n\t      (unsigned long) uc->uc_mcontext.arm_r1,\n\t      (unsigned long) uc->uc_mcontext.arm_r0,\n\t      (unsigned long) uc->uc_mcontext.error_code,\n\t      (unsigned long) uc->uc_mcontext.arm_fp,\n\t      (unsigned long) uc->uc_mcontext.arm_ip,\n\t      (unsigned long) uc->uc_mcontext.arm_pc,\n\t      (unsigned long) uc->uc_mcontext.arm_sp,\n\t      (unsigned long) uc->uc_mcontext.arm_cpsr,\n\t      (unsigned long) uc->uc_mcontext.fault_address\n\t\t      );\n\t      logStackContent((void**)uc->uc_mcontext.arm_sp);\n    #endif\n#elif defined(__FreeBSD__)\n    #if defined(__x86_64__)\n    serverLog(LL_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.mc_rax,\n        (unsigned long) uc->uc_mcontext.mc_rbx,\n        (unsigned long) uc->uc_mcontext.mc_rcx,\n        (unsigned long) uc->uc_mcontext.mc_rdx,\n        (unsigned long) uc->uc_mcontext.mc_rdi,\n        (unsigned long) uc->uc_mcontext.mc_rsi,\n        (unsigned long) uc->uc_mcontext.mc_rbp,\n        (unsigned long) uc->uc_mcontext.mc_rsp,\n        (unsigned long) uc->uc_mcontext.mc_r8,\n        (unsigned long) uc->uc_mcontext.mc_r9,\n        (unsigned long) uc->uc_mcontext.mc_r10,\n        (unsigned long) uc->uc_mcontext.mc_r11,\n        (unsigned long) uc->uc_mcontext.mc_r12,\n        (unsigned long) uc->uc_mcontext.mc_r13,\n        (unsigned long) uc->uc_mcontext.mc_r14,\n        (unsigned long) uc->uc_mcontext.mc_r15,\n        (unsigned long) uc->uc_mcontext.mc_rip,\n        (unsigned long) uc->uc_mcontext.mc_rflags,\n        (unsigned long) uc->uc_mcontext.mc_cs\n    );\n    logStackContent((void**)uc->uc_mcontext.mc_rsp);\n    #elif defined(__i386__)\n    serverLog(LL_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.mc_eax,\n        (unsigned long) uc->uc_mcontext.mc_ebx,\n        (unsigned long) uc->uc_mcontext.mc_ebx,\n        (unsigned long) uc->uc_mcontext.mc_edx,\n        (unsigned long) uc->uc_mcontext.mc_edi,\n        (unsigned long) uc->uc_mcontext.mc_esi,\n        (unsigned long) uc->uc_mcontext.mc_ebp,\n        (unsigned long) uc->uc_mcontext.mc_esp,\n        (unsigned long) uc->uc_mcontext.mc_ss,\n        (unsigned long) uc->uc_mcontext.mc_eflags,\n        (unsigned long) uc->uc_mcontext.mc_eip,\n        (unsigned long) uc->uc_mcontext.mc_cs,\n        (unsigned long) uc->uc_mcontext.mc_es,\n        (unsigned long) uc->uc_mcontext.mc_fs,\n        (unsigned long) uc->uc_mcontext.mc_gs\n    );\n    logStackContent((void**)uc->uc_mcontext.mc_esp);\n    #endif\n#elif defined(__OpenBSD__)\n    #if defined(__x86_64__)\n    serverLog(LL_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->sc_rax,\n        (unsigned long) uc->sc_rbx,\n        (unsigned long) uc->sc_rcx,\n        (unsigned long) uc->sc_rdx,\n        (unsigned long) uc->sc_rdi,\n        (unsigned long) uc->sc_rsi,\n        (unsigned long) uc->sc_rbp,\n        (unsigned long) uc->sc_rsp,\n        (unsigned long) uc->sc_r8,\n        (unsigned long) uc->sc_r9,\n        (unsigned long) uc->sc_r10,\n        (unsigned long) uc->sc_r11,\n        (unsigned long) uc->sc_r12,\n        (unsigned long) uc->sc_r13,\n        (unsigned long) uc->sc_r14,\n        (unsigned long) uc->sc_r15,\n        (unsigned long) uc->sc_rip,\n        (unsigned long) uc->sc_rflags,\n        (unsigned long) uc->sc_cs\n    );\n    logStackContent((void**)uc->sc_rsp);\n    #elif defined(__i386__)\n    serverLog(LL_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->sc_eax,\n        (unsigned long) uc->sc_ebx,\n        (unsigned long) uc->sc_ebx,\n        (unsigned long) uc->sc_edx,\n        (unsigned long) uc->sc_edi,\n        (unsigned long) uc->sc_esi,\n        (unsigned long) uc->sc_ebp,\n        (unsigned long) uc->sc_esp,\n        (unsigned long) uc->sc_ss,\n        (unsigned long) uc->sc_eflags,\n        (unsigned long) uc->sc_eip,\n        (unsigned long) uc->sc_cs,\n        (unsigned long) uc->sc_es,\n        (unsigned long) uc->sc_fs,\n        (unsigned long) uc->sc_gs\n    );\n    logStackContent((void**)uc->sc_esp);\n    #endif\n#elif defined(__DragonFly__)\n    serverLog(LL_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.mc_rax,\n        (unsigned long) uc->uc_mcontext.mc_rbx,\n        (unsigned long) uc->uc_mcontext.mc_rcx,\n        (unsigned long) uc->uc_mcontext.mc_rdx,\n        (unsigned long) uc->uc_mcontext.mc_rdi,\n        (unsigned long) uc->uc_mcontext.mc_rsi,\n        (unsigned long) uc->uc_mcontext.mc_rbp,\n        (unsigned long) uc->uc_mcontext.mc_rsp,\n        (unsigned long) uc->uc_mcontext.mc_r8,\n        (unsigned long) uc->uc_mcontext.mc_r9,\n        (unsigned long) uc->uc_mcontext.mc_r10,\n        (unsigned long) uc->uc_mcontext.mc_r11,\n        (unsigned long) uc->uc_mcontext.mc_r12,\n        (unsigned long) uc->uc_mcontext.mc_r13,\n        (unsigned long) uc->uc_mcontext.mc_r14,\n        (unsigned long) uc->uc_mcontext.mc_r15,\n        (unsigned long) uc->uc_mcontext.mc_rip,\n        (unsigned long) uc->uc_mcontext.mc_rflags,\n        (unsigned long) uc->uc_mcontext.mc_cs\n    );\n    logStackContent((void**)uc->uc_mcontext.mc_rsp);\n#else\n    serverLog(LL_WARNING,\n        \"  Dumping of registers not supported for this OS/arch\");\n#endif\n}\n\n/* Return a file descriptor to write directly to the Redis log with the\n * write(2) syscall, that can be used in critical sections of the code\n * where the rest of Redis can't be trusted (for example during the memory\n * test) or when an API call requires a raw fd.\n *\n * Close it with closeDirectLogFiledes(). */\nint openDirectLogFiledes(void) {\n    int log_to_stdout = server.logfile[0] == '\\0';\n    int fd = log_to_stdout ?\n        STDOUT_FILENO :\n        open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);\n    return fd;\n}\n\n/* Used to close what closeDirectLogFiledes() returns. */\nvoid closeDirectLogFiledes(int fd) {\n    int log_to_stdout = server.logfile[0] == '\\0';\n    if (!log_to_stdout) close(fd);\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[101];\n    int trace_size = 0, fd = openDirectLogFiledes();\n\n    if (fd == -1) return; /* If we can't log there is anything to do. */\n\n    /* Generate the stack trace */\n    trace_size = backtrace(trace+1, 100);\n\n    if (getMcontextEip(uc) != NULL) {\n        char *msg1 = \"EIP:\\n\";\n        char *msg2 = \"\\nBacktrace:\\n\";\n        if (write(fd,msg1,strlen(msg1)) == -1) {/* Avoid warning. */};\n        trace[0] = getMcontextEip(uc);\n        backtrace_symbols_fd(trace, 1, fd);\n        if (write(fd,msg2,strlen(msg2)) == -1) {/* Avoid warning. */};\n    }\n\n    /* Write symbols to log file */\n    backtrace_symbols_fd(trace+1, trace_size, fd);\n\n    /* Cleanup */\n    closeDirectLogFiledes(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    client *cc = server.current_client;\n    sds client;\n    int j;\n\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ CURRENT CLIENT INFO ------\\n\");\n    client = catClientInfoString(sdsempty(),cc);\n    serverLog(LL_WARNING|LL_RAW,\"%s\\n\", client);\n    sdsfree(client);\n    for (j = 0; j < cc->argc; j++) {\n        robj *decoded;\n\n        decoded = getDecodedObject(cc->argv[j]);\n        serverLog(LL_WARNING|LL_RAW,\"argv[%d]: '%s'\\n\", j,\n            (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            serverLog(LL_WARNING,\"key '%s' found in DB containing the following object:\", (char*)key->ptr);\n            serverLogObjectDebugInfo(val);\n        }\n        decrRefCount(key);\n    }\n}\n\n#if defined(HAVE_PROC_MAPS)\n\n#define MEMTEST_MAX_REGIONS 128\n\n/* A non destructive memory test executed during segfauls. */\nint memtest_test_linux_anonymous_maps(void) {\n    FILE *fp;\n    char line[1024];\n    char logbuf[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\n    int fd = openDirectLogFiledes();\n    if (!fd) return 0;\n\n    fp = fopen(\"/proc/self/maps\",\"r\");\n    if (!fp) return 0;\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        snprintf(logbuf,sizeof(logbuf),\n            \"*** Preparing to test memory region %lx (%lu bytes)\\n\",\n                (unsigned long) start_vect[regions],\n                (unsigned long) size_vect[regions]);\n        if (write(fd,logbuf,strlen(logbuf)) == -1) { /* Nothing to do. */ }\n        regions++;\n    }\n\n    int errors = 0;\n    for (j = 0; j < regions; j++) {\n        if (write(fd,\".\",1) == -1) { /* Nothing to do. */ }\n        errors += memtest_preserving_test((void*)start_vect[j],size_vect[j],1);\n        if (write(fd, errors ? \"E\" : \"O\",1) == -1) { /* Nothing to do. */ }\n    }\n    if (write(fd,\"\\n\",1) == -1) { /* Nothing to do. */ }\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    closeDirectLogFiledes(fd);\n    return errors;\n}\n#endif\n\n/* Scans the (assumed) x86 code starting at addr, for a max of `len`\n * bytes, searching for E8 (callq) opcodes, and dumping the symbols\n * and the call offset if they appear to be valid. */\nvoid dumpX86Calls(void *addr, size_t len) {\n    size_t j;\n    unsigned char *p = addr;\n    Dl_info info;\n    /* Hash table to best-effort avoid printing the same symbol\n     * multiple times. */\n    unsigned long ht[256] = {0};\n\n    if (len < 5) return;\n    for (j = 0; j < len-4; j++) {\n        if (p[j] != 0xE8) continue; /* Not an E8 CALL opcode. */\n        unsigned long target = (unsigned long)addr+j+5;\n        target += *((int32_t*)(p+j+1));\n        if (dladdr((void*)target, &info) != 0 && info.dli_sname != NULL) {\n            if (ht[target&0xff] != target) {\n                printf(\"Function at 0x%lx is %s\\n\",target,info.dli_sname);\n                ht[target&0xff] = target;\n            }\n            j += 4; /* Skip the 32 bit immediate. */\n        }\n    }\n}\n\nvoid sigsegvHandler(int sig, siginfo_t *info, void *secret) {\n    ucontext_t *uc = (ucontext_t*) secret;\n    void *eip = getMcontextEip(uc);\n    sds infostring, clients;\n    struct sigaction act;\n    UNUSED(info);\n\n    bugReportStart();\n    serverLog(LL_WARNING,\n        \"Redis %s crashed by signal: %d\", REDIS_VERSION, sig);\n    if (eip != NULL) {\n        serverLog(LL_WARNING,\n        \"Crashed running the instruction at: %p\", eip);\n    }\n    if (sig == SIGSEGV || sig == SIGBUS) {\n        serverLog(LL_WARNING,\n        \"Accessing address: %p\", (void*)info->si_addr);\n    }\n    serverLog(LL_WARNING,\n        \"Failed assertion: %s (%s:%d)\", server.assert_failed,\n                        server.assert_file, server.assert_line);\n\n    /* Log the stack trace */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ STACK TRACE ------\\n\");\n    logStackTrace(uc);\n\n    /* Log INFO and CLIENT LIST */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ INFO OUTPUT ------\\n\");\n    infostring = genRedisInfoString(\"all\");\n    serverLogRaw(LL_WARNING|LL_RAW, infostring);\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ CLIENT LIST OUTPUT ------\\n\");\n    clients = getAllClientsInfoString(-1);\n    serverLogRaw(LL_WARNING|LL_RAW, 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    /* Log Modules INFO */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ MODULES INFO OUTPUT ------\\n\");\n    infostring = modulesCollectInfo(sdsempty(), NULL, 1, 0);\n    serverLogRaw(LL_WARNING|LL_RAW, infostring);\n    sdsfree(infostring);\n\n#if defined(HAVE_PROC_MAPS)\n    /* Test memory */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ FAST MEMORY TEST ------\\n\");\n    bioKillThreads();\n    if (memtest_test_linux_anonymous_maps()) {\n        serverLogRaw(LL_WARNING|LL_RAW,\n            \"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\\n\");\n    } else {\n        serverLogRaw(LL_WARNING|LL_RAW,\n            \"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.\\n\");\n    }\n#endif\n\n    if (eip != NULL) {\n        Dl_info info;\n        if (dladdr(eip, &info) != 0) {\n            serverLog(LL_WARNING|LL_RAW,\n                \"\\n------ DUMPING CODE AROUND EIP ------\\n\"\n                \"Symbol: %s (base: %p)\\n\"\n                \"Module: %s (base %p)\\n\"\n                \"$ xxd -r -p /tmp/dump.hex /tmp/dump.bin\\n\"\n                \"$ objdump --adjust-vma=%p -D -b binary -m i386:x86-64 /tmp/dump.bin\\n\"\n                \"------\\n\",\n                info.dli_sname, info.dli_saddr, info.dli_fname, info.dli_fbase,\n                info.dli_saddr);\n            size_t len = (long)eip - (long)info.dli_saddr;\n            unsigned long sz = sysconf(_SC_PAGESIZE);\n            if (len < 1<<13) { /* we don't have functions over 8k (verified) */\n                /* Find the address of the next page, which is our \"safety\"\n                 * limit when dumping. Then try to dump just 128 bytes more\n                 * than EIP if there is room, or stop sooner. */\n                unsigned long next = ((unsigned long)eip + sz) & ~(sz-1);\n                unsigned long end = (unsigned long)eip + 128;\n                if (end > next) end = next;\n                len = end - (unsigned long)info.dli_saddr;\n                serverLogHexDump(LL_WARNING, \"dump of function\",\n                    info.dli_saddr ,len);\n                dumpX86Calls(info.dli_saddr,len);\n            }\n        }\n    }\n\n    serverLogRaw(LL_WARNING|LL_RAW,\n\"\\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\\n\\n\"\n\"       Please report the crash by 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 verify it.\\n\\n\"\n);\n\n    /* free(messages); Don't call free() with possibly corrupted memory. */\n    if (server.daemonize && server.supervised == 0) 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 serverLogHexDump(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    serverLog(level,\"%s (hexdump of %zu bytes):\", descr, len);\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            serverLogRaw(level|LL_RAW,buf);\n            b = buf;\n        }\n    }\n    serverLogRaw(level|LL_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#else\n    (void)secret;\n#endif\n    UNUSED(info);\n    UNUSED(sig);\n\n    serverLogFromHandler(LL_WARNING,\"\\n--- WATCHDOG TIMER EXPIRED ---\");\n#ifdef HAVE_BACKTRACE\n    logStackTrace(uc);\n#else\n    serverLogFromHandler(LL_WARNING,\"Sorry: no support for backtrace().\");\n#endif\n    serverLogFromHandler(LL_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_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": "Chapter02/redis-6.0.3/src/debugmacro.h",
    "content": "/* This file contains debugging macros to be used when investigating issues.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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#define D(...)                                                               \\\n    do {                                                                     \\\n        FILE *fp = fopen(\"/tmp/log.txt\",\"a\");                                \\\n        fprintf(fp,\"%s:%s:%d:\\t\", __FILE__, __func__, __LINE__);             \\\n        fprintf(fp,__VA_ARGS__);                                             \\\n        fprintf(fp,\"\\n\");                                                    \\\n        fclose(fp);                                                          \\\n    } while (0);\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/defrag.c",
    "content": "/* \n * Active memory defragmentation\n * Try to find key / value allocations that need to be re-allocated in order \n * to reduce external fragmentation.\n * We do that by scanning the keyspace and for each pointer we have, we can try to\n * ask the allocator if moving it to a new address will help reduce fragmentation.\n *\n * Copyright (c) 2020, Oran Agra\n * Copyright (c) 2020, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"server.h\"\n#include <time.h>\n#include <assert.h>\n#include <stddef.h>\n\n#ifdef HAVE_DEFRAG\n\n/* this method was added to jemalloc in order to help us understand which\n * pointers are worthwhile moving and which aren't */\nint je_get_defrag_hint(void* ptr, int *bin_util, int *run_util);\n\n/* forward declarations*/\nvoid defragDictBucketCallback(void *privdata, dictEntry **bucketref);\ndictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged);\n\n/* Defrag helper for generic allocations.\n *\n * returns NULL in case the allocatoin wasn't moved.\n * when it returns a non-null value, the old pointer was already released\n * and should NOT be accessed. */\nvoid* activeDefragAlloc(void *ptr) {\n    int bin_util, run_util;\n    size_t size;\n    void *newptr;\n    if(!je_get_defrag_hint(ptr, &bin_util, &run_util)) {\n        server.stat_active_defrag_misses++;\n        return NULL;\n    }\n    /* if this run is more utilized than the average utilization in this bin\n     * (or it is full), skip it. This will eventually move all the allocations\n     * from relatively empty runs into relatively full runs. */\n    if (run_util > bin_util || run_util == 1<<16) {\n        server.stat_active_defrag_misses++;\n        return NULL;\n    }\n    /* move this allocation to a new allocation.\n     * make sure not to use the thread cache. so that we don't get back the same\n     * pointers we try to free */\n    size = zmalloc_size(ptr);\n    newptr = zmalloc_no_tcache(size);\n    memcpy(newptr, ptr, size);\n    zfree_no_tcache(ptr);\n    return newptr;\n}\n\n/*Defrag helper for sds strings\n *\n * returns NULL in case the allocatoin wasn't moved.\n * when it returns a non-null value, the old pointer was already released\n * and should NOT be accessed. */\nsds activeDefragSds(sds sdsptr) {\n    void* ptr = sdsAllocPtr(sdsptr);\n    void* newptr = activeDefragAlloc(ptr);\n    if (newptr) {\n        size_t offset = sdsptr - (char*)ptr;\n        sdsptr = (char*)newptr + offset;\n        return sdsptr;\n    }\n    return NULL;\n}\n\n/* Defrag helper for robj and/or string objects\n *\n * returns NULL in case the allocatoin wasn't moved.\n * when it returns a non-null value, the old pointer was already released\n * and should NOT be accessed. */\nrobj *activeDefragStringOb(robj* ob, long *defragged) {\n    robj *ret = NULL;\n    if (ob->refcount!=1)\n        return NULL;\n\n    /* try to defrag robj (only if not an EMBSTR type (handled below). */\n    if (ob->type!=OBJ_STRING || ob->encoding!=OBJ_ENCODING_EMBSTR) {\n        if ((ret = activeDefragAlloc(ob))) {\n            ob = ret;\n            (*defragged)++;\n        }\n    }\n\n    /* try to defrag string object */\n    if (ob->type == OBJ_STRING) {\n        if(ob->encoding==OBJ_ENCODING_RAW) {\n            sds newsds = activeDefragSds((sds)ob->ptr);\n            if (newsds) {\n                ob->ptr = newsds;\n                (*defragged)++;\n            }\n        } else if (ob->encoding==OBJ_ENCODING_EMBSTR) {\n            /* The sds is embedded in the object allocation, calculate the\n             * offset and update the pointer in the new allocation. */\n            long ofs = (intptr_t)ob->ptr - (intptr_t)ob;\n            if ((ret = activeDefragAlloc(ob))) {\n                ret->ptr = (void*)((intptr_t)ret + ofs);\n                (*defragged)++;\n            }\n        } else if (ob->encoding!=OBJ_ENCODING_INT) {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    return ret;\n}\n\n/* Defrag helper for dictEntries to be used during dict iteration (called on\n * each step). Teturns a stat of how many pointers were moved. */\nlong dictIterDefragEntry(dictIterator *iter) {\n    /* This function is a little bit dirty since it messes with the internals\n     * of the dict and it's iterator, but the benefit is that it is very easy\n     * to use, and require no other chagnes in the dict. */\n    long defragged = 0;\n    dictht *ht;\n    /* Handle the next entry (if there is one), and update the pointer in the\n     * current entry. */\n    if (iter->nextEntry) {\n        dictEntry *newde = activeDefragAlloc(iter->nextEntry);\n        if (newde) {\n            defragged++;\n            iter->nextEntry = newde;\n            iter->entry->next = newde;\n        }\n    }\n    /* handle the case of the first entry in the hash bucket. */\n    ht = &iter->d->ht[iter->table];\n    if (ht->table[iter->index] == iter->entry) {\n        dictEntry *newde = activeDefragAlloc(iter->entry);\n        if (newde) {\n            iter->entry = newde;\n            ht->table[iter->index] = newde;\n            defragged++;\n        }\n    }\n    return defragged;\n}\n\n/* Defrag helper for dict main allocations (dict struct, and hash tables).\n * receives a pointer to the dict* and implicitly updates it when the dict\n * struct itself was moved. Returns a stat of how many pointers were moved. */\nlong dictDefragTables(dict* d) {\n    dictEntry **newtable;\n    long defragged = 0;\n    /* handle the first hash table */\n    newtable = activeDefragAlloc(d->ht[0].table);\n    if (newtable)\n        defragged++, d->ht[0].table = newtable;\n    /* handle the second hash table */\n    if (d->ht[1].table) {\n        newtable = activeDefragAlloc(d->ht[1].table);\n        if (newtable)\n            defragged++, d->ht[1].table = newtable;\n    }\n    return defragged;\n}\n\n/* Internal function used by zslDefrag */\nvoid zslUpdateNode(zskiplist *zsl, zskiplistNode *oldnode, zskiplistNode *newnode, zskiplistNode **update) {\n    int i;\n    for (i = 0; i < zsl->level; i++) {\n        if (update[i]->level[i].forward == oldnode)\n            update[i]->level[i].forward = newnode;\n    }\n    serverAssert(zsl->header!=oldnode);\n    if (newnode->level[0].forward) {\n        serverAssert(newnode->level[0].forward->backward==oldnode);\n        newnode->level[0].forward->backward = newnode;\n    } else {\n        serverAssert(zsl->tail==oldnode);\n        zsl->tail = newnode;\n    }\n}\n\n/* Defrag helper for sorted set.\n * Update the robj pointer, defrag the skiplist struct and return the new score\n * reference. We may not access oldele pointer (not even the pointer stored in\n * the skiplist), as it was already freed. Newele may be null, in which case we\n * only need to defrag the skiplist, but not update the obj pointer.\n * When return value is non-NULL, it is the score reference that must be updated\n * in the dict record. */\ndouble *zslDefrag(zskiplist *zsl, double score, sds oldele, sds newele) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x, *newx;\n    int i;\n    sds ele = newele? newele: oldele;\n\n    /* find the skiplist node referring to the object that was moved,\n     * and all pointers that need to be updated if we'll end up moving the skiplist node. */\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            x->level[i].forward->ele != oldele && /* make sure not to access the\n                                                     ->obj pointer if it matches\n                                                     oldele */\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                sdscmp(x->level[i].forward->ele,ele) < 0)))\n            x = x->level[i].forward;\n        update[i] = x;\n    }\n\n    /* update the robj pointer inside the skip list record. */\n    x = x->level[0].forward;\n    serverAssert(x && score == x->score && x->ele==oldele);\n    if (newele)\n        x->ele = newele;\n\n    /* try to defrag the skiplist record itself */\n    newx = activeDefragAlloc(x);\n    if (newx) {\n        zslUpdateNode(zsl, x, newx, update);\n        return &newx->score;\n    }\n    return NULL;\n}\n\n/* Defrag helpler for sorted set.\n * Defrag a single dict entry key name, and corresponding skiplist struct */\nlong activeDefragZsetEntry(zset *zs, dictEntry *de) {\n    sds newsds;\n    double* newscore;\n    long defragged = 0;\n    sds sdsele = dictGetKey(de);\n    if ((newsds = activeDefragSds(sdsele)))\n        defragged++, de->key = newsds;\n    newscore = zslDefrag(zs->zsl, *(double*)dictGetVal(de), sdsele, newsds);\n    if (newscore) {\n        dictSetVal(zs->dict, de, newscore);\n        defragged++;\n    }\n    return defragged;\n}\n\n#define DEFRAG_SDS_DICT_NO_VAL 0\n#define DEFRAG_SDS_DICT_VAL_IS_SDS 1\n#define DEFRAG_SDS_DICT_VAL_IS_STROB 2\n#define DEFRAG_SDS_DICT_VAL_VOID_PTR 3\n\n/* Defrag a dict with sds key and optional value (either ptr, sds or robj string) */\nlong activeDefragSdsDict(dict* d, int val_type) {\n    dictIterator *di;\n    dictEntry *de;\n    long defragged = 0;\n    di = dictGetIterator(d);\n    while((de = dictNext(di)) != NULL) {\n        sds sdsele = dictGetKey(de), newsds;\n        if ((newsds = activeDefragSds(sdsele)))\n            de->key = newsds, defragged++;\n        /* defrag the value */\n        if (val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) {\n            sdsele = dictGetVal(de);\n            if ((newsds = activeDefragSds(sdsele)))\n                de->v.val = newsds, defragged++;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) {\n            robj *newele, *ele = dictGetVal(de);\n            if ((newele = activeDefragStringOb(ele, &defragged)))\n                de->v.val = newele;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) {\n            void *newptr, *ptr = dictGetVal(de);\n            if ((newptr = activeDefragAlloc(ptr)))\n                de->v.val = newptr, defragged++;\n        }\n        defragged += dictIterDefragEntry(di);\n    }\n    dictReleaseIterator(di);\n    return defragged;\n}\n\n/* Defrag a list of ptr, sds or robj string values */\nlong activeDefragList(list *l, int val_type) {\n    long defragged = 0;\n    listNode *ln, *newln;\n    for (ln = l->head; ln; ln = ln->next) {\n        if ((newln = activeDefragAlloc(ln))) {\n            if (newln->prev)\n                newln->prev->next = newln;\n            else\n                l->head = newln;\n            if (newln->next)\n                newln->next->prev = newln;\n            else\n                l->tail = newln;\n            ln = newln;\n            defragged++;\n        }\n        if (val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) {\n            sds newsds, sdsele = ln->value;\n            if ((newsds = activeDefragSds(sdsele)))\n                ln->value = newsds, defragged++;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) {\n            robj *newele, *ele = ln->value;\n            if ((newele = activeDefragStringOb(ele, &defragged)))\n                ln->value = newele;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) {\n            void *newptr, *ptr = ln->value;\n            if ((newptr = activeDefragAlloc(ptr)))\n                ln->value = newptr, defragged++;\n        }\n    }\n    return defragged;\n}\n\n/* Defrag a list of sds values and a dict with the same sds keys */\nlong activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {\n    long defragged = 0;\n    sds newsds, sdsele;\n    listNode *ln, *newln;\n    dictIterator *di;\n    dictEntry *de;\n    /* Defrag the list and it's sds values */\n    for (ln = l->head; ln; ln = ln->next) {\n        if ((newln = activeDefragAlloc(ln))) {\n            if (newln->prev)\n                newln->prev->next = newln;\n            else\n                l->head = newln;\n            if (newln->next)\n                newln->next->prev = newln;\n            else\n                l->tail = newln;\n            ln = newln;\n            defragged++;\n        }\n        sdsele = ln->value;\n        if ((newsds = activeDefragSds(sdsele))) {\n            /* When defragging an sds value, we need to update the dict key */\n            uint64_t hash = dictGetHash(d, sdsele);\n            replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);\n            ln->value = newsds;\n            defragged++;\n        }\n    }\n\n    /* Defrag the dict values (keys were already handled) */\n    di = dictGetIterator(d);\n    while((de = dictNext(di)) != NULL) {\n        if (dict_val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) {\n            sds newsds, sdsele = dictGetVal(de);\n            if ((newsds = activeDefragSds(sdsele)))\n                de->v.val = newsds, defragged++;\n        } else if (dict_val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) {\n            robj *newele, *ele = dictGetVal(de);\n            if ((newele = activeDefragStringOb(ele, &defragged)))\n                de->v.val = newele, defragged++;\n        } else if (dict_val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) {\n            void *newptr, *ptr = dictGetVal(de);\n            if ((newptr = activeDefragAlloc(ptr)))\n                ln->value = newptr, defragged++;\n        }\n        defragged += dictIterDefragEntry(di);\n    }\n    dictReleaseIterator(di);\n\n    return defragged;\n}\n\n/* Utility function that replaces an old key pointer in the dictionary with a\n * new pointer. Additionally, we try to defrag the dictEntry in that dict.\n * Oldkey mey be a dead pointer and should not be accessed (we get a\n * pre-calculated hash value). Newkey may be null if the key pointer wasn't\n * moved. Return value is the the dictEntry if found, or NULL if not found.\n * NOTE: this is very ugly code, but it let's us avoid the complication of\n * doing a scan on another dict. */\ndictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged) {\n    dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash);\n    if (deref) {\n        dictEntry *de = *deref;\n        dictEntry *newde = activeDefragAlloc(de);\n        if (newde) {\n            de = *deref = newde;\n            (*defragged)++;\n        }\n        if (newkey)\n            de->key = newkey;\n        return de;\n    }\n    return NULL;\n}\n\nlong activeDefragQuickListNode(quicklist *ql, quicklistNode **node_ref) {\n    quicklistNode *newnode, *node = *node_ref;\n    long defragged = 0;\n    unsigned char *newzl;\n    if ((newnode = activeDefragAlloc(node))) {\n        if (newnode->prev)\n            newnode->prev->next = newnode;\n        else\n            ql->head = newnode;\n        if (newnode->next)\n            newnode->next->prev = newnode;\n        else\n            ql->tail = newnode;\n        *node_ref = node = newnode;\n        defragged++;\n    }\n    if ((newzl = activeDefragAlloc(node->zl)))\n        defragged++, node->zl = newzl;\n    return defragged;\n}\n\nlong activeDefragQuickListNodes(quicklist *ql) {\n    quicklistNode *node = ql->head;\n    long defragged = 0;\n    while (node) {\n        defragged += activeDefragQuickListNode(ql, &node);\n        node = node->next;\n    }\n    return defragged;\n}\n\n/* when the value has lots of elements, we want to handle it later and not as\n * oart of the main dictionary scan. this is needed in order to prevent latency\n * spikes when handling large items */\nvoid defragLater(redisDb *db, dictEntry *kde) {\n    sds key = sdsdup(dictGetKey(kde));\n    listAddNodeTail(db->defrag_later, key);\n}\n\n/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */\nlong scanLaterList(robj *ob, unsigned long *cursor, long long endtime, long long *defragged) {\n    quicklist *ql = ob->ptr;\n    quicklistNode *node;\n    long iterations = 0;\n    int bookmark_failed = 0;\n    if (ob->type != OBJ_LIST || ob->encoding != OBJ_ENCODING_QUICKLIST)\n        return 0;\n\n    if (*cursor == 0) {\n        /* if cursor is 0, we start new iteration */\n        node = ql->head;\n    } else {\n        node = quicklistBookmarkFind(ql, \"_AD\");\n        if (!node) {\n            /* if the bookmark was deleted, it means we reached the end. */\n            *cursor = 0;\n            return 0;\n        }\n        node = node->next;\n    }\n\n    (*cursor)++;\n    while (node) {\n        (*defragged) += activeDefragQuickListNode(ql, &node);\n        server.stat_active_defrag_scanned++;\n        if (++iterations > 128 && !bookmark_failed) {\n            if (ustime() > endtime) {\n                if (!quicklistBookmarkCreate(&ql, \"_AD\", node)) {\n                    bookmark_failed = 1;\n                } else {\n                    ob->ptr = ql; /* bookmark creation may have re-allocated the quicklist */\n                    return 1;\n                }\n            }\n            iterations = 0;\n        }\n        node = node->next;\n    }\n    quicklistBookmarkDelete(ql, \"_AD\");\n    *cursor = 0;\n    return bookmark_failed? 1: 0;\n}\n\ntypedef struct {\n    zset *zs;\n    long defragged;\n} scanLaterZsetData;\n\nvoid scanLaterZsetCallback(void *privdata, const dictEntry *_de) {\n    dictEntry *de = (dictEntry*)_de;\n    scanLaterZsetData *data = privdata;\n    data->defragged += activeDefragZsetEntry(data->zs, de);\n    server.stat_active_defrag_scanned++;\n}\n\nlong scanLaterZset(robj *ob, unsigned long *cursor) {\n    if (ob->type != OBJ_ZSET || ob->encoding != OBJ_ENCODING_SKIPLIST)\n        return 0;\n    zset *zs = (zset*)ob->ptr;\n    dict *d = zs->dict;\n    scanLaterZsetData data = {zs, 0};\n    *cursor = dictScan(d, *cursor, scanLaterZsetCallback, defragDictBucketCallback, &data);\n    return data.defragged;\n}\n\nvoid scanLaterSetCallback(void *privdata, const dictEntry *_de) {\n    dictEntry *de = (dictEntry*)_de;\n    long *defragged = privdata;\n    sds sdsele = dictGetKey(de), newsds;\n    if ((newsds = activeDefragSds(sdsele)))\n        (*defragged)++, de->key = newsds;\n    server.stat_active_defrag_scanned++;\n}\n\nlong scanLaterSet(robj *ob, unsigned long *cursor) {\n    long defragged = 0;\n    if (ob->type != OBJ_SET || ob->encoding != OBJ_ENCODING_HT)\n        return 0;\n    dict *d = ob->ptr;\n    *cursor = dictScan(d, *cursor, scanLaterSetCallback, defragDictBucketCallback, &defragged);\n    return defragged;\n}\n\nvoid scanLaterHashCallback(void *privdata, const dictEntry *_de) {\n    dictEntry *de = (dictEntry*)_de;\n    long *defragged = privdata;\n    sds sdsele = dictGetKey(de), newsds;\n    if ((newsds = activeDefragSds(sdsele)))\n        (*defragged)++, de->key = newsds;\n    sdsele = dictGetVal(de);\n    if ((newsds = activeDefragSds(sdsele)))\n        (*defragged)++, de->v.val = newsds;\n    server.stat_active_defrag_scanned++;\n}\n\nlong scanLaterHash(robj *ob, unsigned long *cursor) {\n    long defragged = 0;\n    if (ob->type != OBJ_HASH || ob->encoding != OBJ_ENCODING_HT)\n        return 0;\n    dict *d = ob->ptr;\n    *cursor = dictScan(d, *cursor, scanLaterHashCallback, defragDictBucketCallback, &defragged);\n    return defragged;\n}\n\nlong defragQuicklist(redisDb *db, dictEntry *kde) {\n    robj *ob = dictGetVal(kde);\n    long defragged = 0;\n    quicklist *ql = ob->ptr, *newql;\n    serverAssert(ob->type == OBJ_LIST && ob->encoding == OBJ_ENCODING_QUICKLIST);\n    if ((newql = activeDefragAlloc(ql)))\n        defragged++, ob->ptr = ql = newql;\n    if (ql->len > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else\n        defragged += activeDefragQuickListNodes(ql);\n    return defragged;\n}\n\nlong defragZsetSkiplist(redisDb *db, dictEntry *kde) {\n    robj *ob = dictGetVal(kde);\n    long defragged = 0;\n    zset *zs = (zset*)ob->ptr;\n    zset *newzs;\n    zskiplist *newzsl;\n    dict *newdict;\n    dictEntry *de;\n    struct zskiplistNode *newheader;\n    serverAssert(ob->type == OBJ_ZSET && ob->encoding == OBJ_ENCODING_SKIPLIST);\n    if ((newzs = activeDefragAlloc(zs)))\n        defragged++, ob->ptr = zs = newzs;\n    if ((newzsl = activeDefragAlloc(zs->zsl)))\n        defragged++, zs->zsl = newzsl;\n    if ((newheader = activeDefragAlloc(zs->zsl->header)))\n        defragged++, zs->zsl->header = newheader;\n    if (dictSize(zs->dict) > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else {\n        dictIterator *di = dictGetIterator(zs->dict);\n        while((de = dictNext(di)) != NULL) {\n            defragged += activeDefragZsetEntry(zs, de);\n        }\n        dictReleaseIterator(di);\n    }\n    /* handle the dict struct */\n    if ((newdict = activeDefragAlloc(zs->dict)))\n        defragged++, zs->dict = newdict;\n    /* defrag the dict tables */\n    defragged += dictDefragTables(zs->dict);\n    return defragged;\n}\n\nlong defragHash(redisDb *db, dictEntry *kde) {\n    long defragged = 0;\n    robj *ob = dictGetVal(kde);\n    dict *d, *newd;\n    serverAssert(ob->type == OBJ_HASH && ob->encoding == OBJ_ENCODING_HT);\n    d = ob->ptr;\n    if (dictSize(d) > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else\n        defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_VAL_IS_SDS);\n    /* handle the dict struct */\n    if ((newd = activeDefragAlloc(ob->ptr)))\n        defragged++, ob->ptr = newd;\n    /* defrag the dict tables */\n    defragged += dictDefragTables(ob->ptr);\n    return defragged;\n}\n\nlong defragSet(redisDb *db, dictEntry *kde) {\n    long defragged = 0;\n    robj *ob = dictGetVal(kde);\n    dict *d, *newd;\n    serverAssert(ob->type == OBJ_SET && ob->encoding == OBJ_ENCODING_HT);\n    d = ob->ptr;\n    if (dictSize(d) > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else\n        defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_NO_VAL);\n    /* handle the dict struct */\n    if ((newd = activeDefragAlloc(ob->ptr)))\n        defragged++, ob->ptr = newd;\n    /* defrag the dict tables */\n    defragged += dictDefragTables(ob->ptr);\n    return defragged;\n}\n\n/* Defrag callback for radix tree iterator, called for each node,\n * used in order to defrag the nodes allocations. */\nint defragRaxNode(raxNode **noderef) {\n    raxNode *newnode = activeDefragAlloc(*noderef);\n    if (newnode) {\n        *noderef = newnode;\n        return 1;\n    }\n    return 0;\n}\n\n/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */\nint scanLaterStraemListpacks(robj *ob, unsigned long *cursor, long long endtime, long long *defragged) {\n    static unsigned char last[sizeof(streamID)];\n    raxIterator ri;\n    long iterations = 0;\n    if (ob->type != OBJ_STREAM || ob->encoding != OBJ_ENCODING_STREAM) {\n        *cursor = 0;\n        return 0;\n    }\n\n    stream *s = ob->ptr;\n    raxStart(&ri,s->rax);\n    if (*cursor == 0) {\n        /* if cursor is 0, we start new iteration */\n        defragRaxNode(&s->rax->head);\n        /* assign the iterator node callback before the seek, so that the\n         * initial nodes that are processed till the first item are covered */\n        ri.node_cb = defragRaxNode;\n        raxSeek(&ri,\"^\",NULL,0);\n    } else {\n        /* if cursor is non-zero, we seek to the static 'last' */\n        if (!raxSeek(&ri,\">\", last, sizeof(last))) {\n            *cursor = 0;\n            return 0;\n        }\n        /* assign the iterator node callback after the seek, so that the\n         * initial nodes that are processed till now aren't covered */\n        ri.node_cb = defragRaxNode;\n    }\n\n    (*cursor)++;\n    while (raxNext(&ri)) {\n        void *newdata = activeDefragAlloc(ri.data);\n        if (newdata)\n            raxSetData(ri.node, ri.data=newdata), (*defragged)++;\n        server.stat_active_defrag_scanned++;\n        if (++iterations > 128) {\n            if (ustime() > endtime) {\n                serverAssert(ri.key_len==sizeof(last));\n                memcpy(last,ri.key,ri.key_len);\n                raxStop(&ri);\n                return 1;\n            }\n            iterations = 0;\n        }\n    }\n    raxStop(&ri);\n    *cursor = 0;\n    return 0;\n}\n\n/* optional callback used defrag each rax element (not including the element pointer itself) */\ntypedef void *(raxDefragFunction)(raxIterator *ri, void *privdata, long *defragged);\n\n/* defrag radix tree including:\n * 1) rax struct\n * 2) rax nodes\n * 3) rax entry data (only if defrag_data is specified)\n * 4) call a callback per element, and allow the callback to return a new pointer for the element */\nlong defragRadixTree(rax **raxref, int defrag_data, raxDefragFunction *element_cb, void *element_cb_data) {\n    long defragged = 0;\n    raxIterator ri;\n    rax* rax;\n    if ((rax = activeDefragAlloc(*raxref)))\n        defragged++, *raxref = rax;\n    rax = *raxref;\n    raxStart(&ri,rax);\n    ri.node_cb = defragRaxNode;\n    defragRaxNode(&rax->head);\n    raxSeek(&ri,\"^\",NULL,0);\n    while (raxNext(&ri)) {\n        void *newdata = NULL;\n        if (element_cb)\n            newdata = element_cb(&ri, element_cb_data, &defragged);\n        if (defrag_data && !newdata)\n            newdata = activeDefragAlloc(ri.data);\n        if (newdata)\n            raxSetData(ri.node, ri.data=newdata), defragged++;\n    }\n    raxStop(&ri);\n    return defragged;\n}\n\ntypedef struct {\n    streamCG *cg;\n    streamConsumer *c;\n} PendingEntryContext;\n\nvoid* defragStreamConsumerPendingEntry(raxIterator *ri, void *privdata, long *defragged) {\n    UNUSED(defragged);\n    PendingEntryContext *ctx = privdata;\n    streamNACK *nack = ri->data, *newnack;\n    nack->consumer = ctx->c; /* update nack pointer to consumer */\n    newnack = activeDefragAlloc(nack);\n    if (newnack) {\n        /* update consumer group pointer to the nack */\n        void *prev;\n        raxInsert(ctx->cg->pel, ri->key, ri->key_len, newnack, &prev);\n        serverAssert(prev==nack);\n        /* note: we don't increment 'defragged' that's done by the caller */\n    }\n    return newnack;\n}\n\nvoid* defragStreamConsumer(raxIterator *ri, void *privdata, long *defragged) {\n    streamConsumer *c = ri->data;\n    streamCG *cg = privdata;\n    void *newc = activeDefragAlloc(c);\n    if (newc) {\n        /* note: we don't increment 'defragged' that's done by the caller */\n        c = newc;\n    }\n    sds newsds = activeDefragSds(c->name);\n    if (newsds)\n        (*defragged)++, c->name = newsds;\n    if (c->pel) {\n        PendingEntryContext pel_ctx = {cg, c};\n        *defragged += defragRadixTree(&c->pel, 0, defragStreamConsumerPendingEntry, &pel_ctx);\n    }\n    return newc; /* returns NULL if c was not defragged */\n}\n\nvoid* defragStreamConsumerGroup(raxIterator *ri, void *privdata, long *defragged) {\n    streamCG *cg = ri->data;\n    UNUSED(privdata);\n    if (cg->consumers)\n        *defragged += defragRadixTree(&cg->consumers, 0, defragStreamConsumer, cg);\n    if (cg->pel)\n        *defragged += defragRadixTree(&cg->pel, 0, NULL, NULL);\n    return NULL;\n}\n\nlong defragStream(redisDb *db, dictEntry *kde) {\n    long defragged = 0;\n    robj *ob = dictGetVal(kde);\n    serverAssert(ob->type == OBJ_STREAM && ob->encoding == OBJ_ENCODING_STREAM);\n    stream *s = ob->ptr, *news;\n\n    /* handle the main struct */\n    if ((news = activeDefragAlloc(s)))\n        defragged++, ob->ptr = s = news;\n\n    if (raxSize(s->rax) > server.active_defrag_max_scan_fields) {\n        rax *newrax = activeDefragAlloc(s->rax);\n        if (newrax)\n            defragged++, s->rax = newrax;\n        defragLater(db, kde);\n    } else\n        defragged += defragRadixTree(&s->rax, 1, NULL, NULL);\n\n    if (s->cgroups)\n        defragged += defragRadixTree(&s->cgroups, 1, defragStreamConsumerGroup, NULL);\n    return defragged;\n}\n\n/* for each key we scan in the main dict, this function will attempt to defrag\n * all the various pointers it has. Returns a stat of how many pointers were\n * moved. */\nlong defragKey(redisDb *db, dictEntry *de) {\n    sds keysds = dictGetKey(de);\n    robj *newob, *ob;\n    unsigned char *newzl;\n    long defragged = 0;\n    sds newsds;\n\n    /* Try to defrag the key name. */\n    newsds = activeDefragSds(keysds);\n    if (newsds)\n        defragged++, de->key = newsds;\n    if (dictSize(db->expires)) {\n         /* Dirty code:\n          * I can't search in db->expires for that key after i already released\n          * the pointer it holds it won't be able to do the string compare */\n        uint64_t hash = dictGetHash(db->dict, de->key);\n        replaceSateliteDictKeyPtrAndOrDefragDictEntry(db->expires, keysds, newsds, hash, &defragged);\n    }\n\n    /* Try to defrag robj and / or string value. */\n    ob = dictGetVal(de);\n    if ((newob = activeDefragStringOb(ob, &defragged))) {\n        de->v.val = newob;\n        ob = newob;\n    }\n\n    if (ob->type == OBJ_STRING) {\n        /* Already handled in activeDefragStringOb. */\n    } else if (ob->type == OBJ_LIST) {\n        if (ob->encoding == OBJ_ENCODING_QUICKLIST) {\n            defragged += defragQuicklist(db, de);\n        } else if (ob->encoding == OBJ_ENCODING_ZIPLIST) {\n            if ((newzl = activeDefragAlloc(ob->ptr)))\n                defragged++, ob->ptr = newzl;\n        } else {\n            serverPanic(\"Unknown list encoding\");\n        }\n    } else if (ob->type == OBJ_SET) {\n        if (ob->encoding == OBJ_ENCODING_HT) {\n            defragged += defragSet(db, de);\n        } else if (ob->encoding == OBJ_ENCODING_INTSET) {\n            intset *newis, *is = ob->ptr;\n            if ((newis = activeDefragAlloc(is)))\n                defragged++, ob->ptr = newis;\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (ob->type == OBJ_ZSET) {\n        if (ob->encoding == OBJ_ENCODING_ZIPLIST) {\n            if ((newzl = activeDefragAlloc(ob->ptr)))\n                defragged++, ob->ptr = newzl;\n        } else if (ob->encoding == OBJ_ENCODING_SKIPLIST) {\n            defragged += defragZsetSkiplist(db, de);\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (ob->type == OBJ_HASH) {\n        if (ob->encoding == OBJ_ENCODING_ZIPLIST) {\n            if ((newzl = activeDefragAlloc(ob->ptr)))\n                defragged++, ob->ptr = newzl;\n        } else if (ob->encoding == OBJ_ENCODING_HT) {\n            defragged += defragHash(db, de);\n        } else {\n            serverPanic(\"Unknown hash encoding\");\n        }\n    } else if (ob->type == OBJ_STREAM) {\n        defragged += defragStream(db, de);\n    } else if (ob->type == OBJ_MODULE) {\n        /* Currently defragmenting modules private data types\n         * is not supported. */\n    } else {\n        serverPanic(\"Unknown object type\");\n    }\n    return defragged;\n}\n\n/* Defrag scan callback for the main db dictionary. */\nvoid defragScanCallback(void *privdata, const dictEntry *de) {\n    long defragged = defragKey((redisDb*)privdata, (dictEntry*)de);\n    server.stat_active_defrag_hits += defragged;\n    if(defragged)\n        server.stat_active_defrag_key_hits++;\n    else\n        server.stat_active_defrag_key_misses++;\n    server.stat_active_defrag_scanned++;\n}\n\n/* Defrag scan callback for each hash table bicket,\n * used in order to defrag the dictEntry allocations. */\nvoid defragDictBucketCallback(void *privdata, dictEntry **bucketref) {\n    UNUSED(privdata); /* NOTE: this function is also used by both activeDefragCycle and scanLaterHash, etc. don't use privdata */\n    while(*bucketref) {\n        dictEntry *de = *bucketref, *newde;\n        if ((newde = activeDefragAlloc(de))) {\n            *bucketref = newde;\n        }\n        bucketref = &(*bucketref)->next;\n    }\n}\n\n/* Utility function to get the fragmentation ratio from jemalloc.\n * It is critical to do that by comparing only heap maps that belong to\n * jemalloc, and skip ones the jemalloc keeps as spare. Since we use this\n * fragmentation ratio in order to decide if a defrag action should be taken\n * or not, a false detection can cause the defragmenter to waste a lot of CPU\n * without the possibility of getting any results. */\nfloat getAllocatorFragmentation(size_t *out_frag_bytes) {\n    size_t resident, active, allocated;\n    zmalloc_get_allocator_info(&allocated, &active, &resident);\n    float frag_pct = ((float)active / allocated)*100 - 100;\n    size_t frag_bytes = active - allocated;\n    float rss_pct = ((float)resident / allocated)*100 - 100;\n    size_t rss_bytes = resident - allocated;\n    if(out_frag_bytes)\n        *out_frag_bytes = frag_bytes;\n    serverLog(LL_DEBUG,\n        \"allocated=%zu, active=%zu, resident=%zu, frag=%.0f%% (%.0f%% rss), frag_bytes=%zu (%zu rss)\",\n        allocated, active, resident, frag_pct, rss_pct, frag_bytes, rss_bytes);\n    return frag_pct;\n}\n\n/* We may need to defrag other globals, one small allcation can hold a full allocator run.\n * so although small, it is still important to defrag these */\nlong defragOtherGlobals() {\n    long defragged = 0;\n\n    /* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc.\n     * but we assume most of these are short lived, we only need to defrag allocations\n     * that remain static for a long time */\n    defragged += activeDefragSdsDict(server.lua_scripts, DEFRAG_SDS_DICT_VAL_IS_STROB);\n    defragged += activeDefragSdsListAndDict(server.repl_scriptcache_fifo, server.repl_scriptcache_dict, DEFRAG_SDS_DICT_NO_VAL);\n    return defragged;\n}\n\n/* returns 0 more work may or may not be needed (see non-zero cursor),\n * and 1 if time is up and more work is needed. */\nint defragLaterItem(dictEntry *de, unsigned long *cursor, long long endtime) {\n    if (de) {\n        robj *ob = dictGetVal(de);\n        if (ob->type == OBJ_LIST) {\n            return scanLaterList(ob, cursor, endtime, &server.stat_active_defrag_hits);\n        } else if (ob->type == OBJ_SET) {\n            server.stat_active_defrag_hits += scanLaterSet(ob, cursor);\n        } else if (ob->type == OBJ_ZSET) {\n            server.stat_active_defrag_hits += scanLaterZset(ob, cursor);\n        } else if (ob->type == OBJ_HASH) {\n            server.stat_active_defrag_hits += scanLaterHash(ob, cursor);\n        } else if (ob->type == OBJ_STREAM) {\n            return scanLaterStraemListpacks(ob, cursor, endtime, &server.stat_active_defrag_hits);\n        } else {\n            *cursor = 0; /* object type may have changed since we schedule it for later */\n        }\n    } else {\n        *cursor = 0; /* object may have been deleted already */\n    }\n    return 0;\n}\n\n/* static variables serving defragLaterStep to continue scanning a key from were we stopped last time. */\nstatic sds defrag_later_current_key = NULL;\nstatic unsigned long defrag_later_cursor = 0;\n\n/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */\nint defragLaterStep(redisDb *db, long long endtime) {\n    unsigned int iterations = 0;\n    unsigned long long prev_defragged = server.stat_active_defrag_hits;\n    unsigned long long prev_scanned = server.stat_active_defrag_scanned;\n    long long key_defragged;\n\n    do {\n        /* if we're not continuing a scan from the last call or loop, start a new one */\n        if (!defrag_later_cursor) {\n            listNode *head = listFirst(db->defrag_later);\n\n            /* Move on to next key */\n            if (defrag_later_current_key) {\n                serverAssert(defrag_later_current_key == head->value);\n                listDelNode(db->defrag_later, head);\n                defrag_later_cursor = 0;\n                defrag_later_current_key = NULL;\n            }\n\n            /* stop if we reached the last one. */\n            head = listFirst(db->defrag_later);\n            if (!head)\n                return 0;\n\n            /* start a new key */\n            defrag_later_current_key = head->value;\n            defrag_later_cursor = 0;\n        }\n\n        /* each time we enter this function we need to fetch the key from the dict again (if it still exists) */\n        dictEntry *de = dictFind(db->dict, defrag_later_current_key);\n        key_defragged = server.stat_active_defrag_hits;\n        do {\n            int quit = 0;\n            if (defragLaterItem(de, &defrag_later_cursor, endtime))\n                quit = 1; /* time is up, we didn't finish all the work */\n\n            /* Once in 16 scan iterations, 512 pointer reallocations, or 64 fields\n             * (if we have a lot of pointers in one hash bucket, or rehashing),\n             * check if we reached the time limit. */\n            if (quit || (++iterations > 16 ||\n                            server.stat_active_defrag_hits - prev_defragged > 512 ||\n                            server.stat_active_defrag_scanned - prev_scanned > 64)) {\n                if (quit || ustime() > endtime) {\n                    if(key_defragged != server.stat_active_defrag_hits)\n                        server.stat_active_defrag_key_hits++;\n                    else\n                        server.stat_active_defrag_key_misses++;\n                    return 1;\n                }\n                iterations = 0;\n                prev_defragged = server.stat_active_defrag_hits;\n                prev_scanned = server.stat_active_defrag_scanned;\n            }\n        } while(defrag_later_cursor);\n        if(key_defragged != server.stat_active_defrag_hits)\n            server.stat_active_defrag_key_hits++;\n        else\n            server.stat_active_defrag_key_misses++;\n    } while(1);\n}\n\n#define INTERPOLATE(x, x1, x2, y1, y2) ( (y1) + ((x)-(x1)) * ((y2)-(y1)) / ((x2)-(x1)) )\n#define LIMIT(y, min, max) ((y)<(min)? min: ((y)>(max)? max: (y)))\n\n/* decide if defrag is needed, and at what CPU effort to invest in it */\nvoid computeDefragCycles() {\n    size_t frag_bytes;\n    float frag_pct = getAllocatorFragmentation(&frag_bytes);\n    /* If we're not already running, and below the threshold, exit. */\n    if (!server.active_defrag_running) {\n        if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes)\n            return;\n    }\n\n    /* Calculate the adaptive aggressiveness of the defrag */\n    int cpu_pct = INTERPOLATE(frag_pct,\n            server.active_defrag_threshold_lower,\n            server.active_defrag_threshold_upper,\n            server.active_defrag_cycle_min,\n            server.active_defrag_cycle_max);\n    cpu_pct = LIMIT(cpu_pct,\n            server.active_defrag_cycle_min,\n            server.active_defrag_cycle_max);\n     /* We allow increasing the aggressiveness during a scan, but don't\n      * reduce it. */\n    if (!server.active_defrag_running ||\n        cpu_pct > server.active_defrag_running)\n    {\n        server.active_defrag_running = cpu_pct;\n        serverLog(LL_VERBOSE,\n            \"Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%\",\n            frag_pct, frag_bytes, cpu_pct);\n    }\n}\n\n/* Perform incremental defragmentation work from the serverCron.\n * This works in a similar way to activeExpireCycle, in the sense that\n * we do incremental work across calls. */\nvoid activeDefragCycle(void) {\n    static int current_db = -1;\n    static unsigned long cursor = 0;\n    static redisDb *db = NULL;\n    static long long start_scan, start_stat;\n    unsigned int iterations = 0;\n    unsigned long long prev_defragged = server.stat_active_defrag_hits;\n    unsigned long long prev_scanned = server.stat_active_defrag_scanned;\n    long long start, timelimit, endtime;\n    mstime_t latency;\n    int quit = 0;\n\n    if (!server.active_defrag_enabled) {\n        if (server.active_defrag_running) {\n            /* if active defrag was disabled mid-run, start from fresh next time. */\n            server.active_defrag_running = 0;\n            if (db)\n                listEmpty(db->defrag_later);\n            defrag_later_current_key = NULL;\n            defrag_later_cursor = 0;\n            current_db = -1;\n            cursor = 0;\n            db = NULL;\n        }\n        return;\n    }\n\n    if (hasActiveChildProcess())\n        return; /* Defragging memory while there's a fork will just do damage. */\n\n    /* Once a second, check if we the fragmentation justfies starting a scan\n     * or making it more aggressive. */\n    run_with_period(1000) {\n        computeDefragCycles();\n    }\n    if (!server.active_defrag_running)\n        return;\n\n    /* See activeExpireCycle for how timelimit is handled. */\n    start = ustime();\n    timelimit = 1000000*server.active_defrag_running/server.hz/100;\n    if (timelimit <= 0) timelimit = 1;\n    endtime = start + timelimit;\n    latencyStartMonitor(latency);\n\n    do {\n        /* if we're not continuing a scan from the last call or loop, start a new one */\n        if (!cursor) {\n            /* finish any leftovers from previous db before moving to the next one */\n            if (db && defragLaterStep(db, endtime)) {\n                quit = 1; /* time is up, we didn't finish all the work */\n                break; /* this will exit the function and we'll continue on the next cycle */\n            }\n\n            /* Move on to next database, and stop if we reached the last one. */\n            if (++current_db >= server.dbnum) {\n                /* defrag other items not part of the db / keys */\n                defragOtherGlobals();\n\n                long long now = ustime();\n                size_t frag_bytes;\n                float frag_pct = getAllocatorFragmentation(&frag_bytes);\n                serverLog(LL_VERBOSE,\n                    \"Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu\",\n                    (int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);\n\n                start_scan = now;\n                current_db = -1;\n                cursor = 0;\n                db = NULL;\n                server.active_defrag_running = 0;\n\n                computeDefragCycles(); /* if another scan is needed, start it right away */\n                if (server.active_defrag_running != 0 && ustime() < endtime)\n                    continue;\n                break;\n            }\n            else if (current_db==0) {\n                /* Start a scan from the first database. */\n                start_scan = ustime();\n                start_stat = server.stat_active_defrag_hits;\n            }\n\n            db = &server.db[current_db];\n            cursor = 0;\n        }\n\n        do {\n            /* before scanning the next bucket, see if we have big keys left from the previous bucket to scan */\n            if (defragLaterStep(db, endtime)) {\n                quit = 1; /* time is up, we didn't finish all the work */\n                break; /* this will exit the function and we'll continue on the next cycle */\n            }\n\n            cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db);\n\n            /* Once in 16 scan iterations, 512 pointer reallocations. or 64 keys\n             * (if we have a lot of pointers in one hash bucket or rehasing),\n             * check if we reached the time limit.\n             * But regardless, don't start a new db in this loop, this is because after\n             * the last db we call defragOtherGlobals, which must be done in once cycle */\n            if (!cursor || (++iterations > 16 ||\n                            server.stat_active_defrag_hits - prev_defragged > 512 ||\n                            server.stat_active_defrag_scanned - prev_scanned > 64)) {\n                if (!cursor || ustime() > endtime) {\n                    quit = 1;\n                    break;\n                }\n                iterations = 0;\n                prev_defragged = server.stat_active_defrag_hits;\n                prev_scanned = server.stat_active_defrag_scanned;\n            }\n        } while(cursor && !quit);\n    } while(!quit);\n\n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"active-defrag-cycle\",latency);\n}\n\n#else /* HAVE_DEFRAG */\n\nvoid activeDefragCycle(void) {\n    /* Not implemented yet. */\n}\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 <stdint.h>\n#include <string.h>\n#include <stdarg.h>\n#include <limits.h>\n#include <sys/time.h>\n\n#include \"dict.h\"\n#include \"zmalloc.h\"\n#ifndef DICT_BENCHMARK_MAIN\n#include \"redisassert.h\"\n#else\n#include <assert.h>\n#endif\n\n/* Using dictEnableResize() / dictDisableResize() we make possible to\n * enable/disable resizing of the hash table as needed. This is very important\n * for Redis, as we use copy-on-write and don't want to move too much memory\n * around when there is a child performing saving operations.\n *\n * Note that even when dict_can_resize is set to 0, not all resizes are\n * prevented: a hash table is still allowed to grow if the ratio between\n * the number of elements and the buckets > dict_force_resize_ratio. */\nstatic int dict_can_resize = 1;\nstatic unsigned int dict_force_resize_ratio = 5;\n\n/* -------------------------- private prototypes ---------------------------- */\n\nstatic int _dictExpandIfNeeded(dict *ht);\nstatic unsigned long _dictNextPower(unsigned long size);\nstatic long _dictKeyIndex(dict *ht, const void *key, uint64_t hash, dictEntry **existing);\nstatic int _dictInit(dict *ht, dictType *type, void *privDataPtr);\n\n/* -------------------------- hash functions -------------------------------- */\n\nstatic uint8_t dict_hash_function_seed[16];\n\nvoid dictSetHashFunctionSeed(uint8_t *seed) {\n    memcpy(dict_hash_function_seed,seed,sizeof(dict_hash_function_seed));\n}\n\nuint8_t *dictGetHashFunctionSeed(void) {\n    return dict_hash_function_seed;\n}\n\n/* The default hashing function uses SipHash implementation\n * in siphash.c. */\n\nuint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k);\nuint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k);\n\nuint64_t dictGenHashFunction(const void *key, int len) {\n    return siphash(key,len,dict_hash_function_seed);\n}\n\nuint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) {\n    return siphash_nocase(buf,len,dict_hash_function_seed);\n}\n\n/* ----------------------------- API implementation ------------------------- */\n\n/* Reset a hash table already initialized with ht_init().\n * NOTE: This function should only be called by ht_destroy(). */\nstatic void _dictReset(dictht *ht)\n{\n    ht->table = NULL;\n    ht->size = 0;\n    ht->sizemask = 0;\n    ht->used = 0;\n}\n\n/* Create a new hash table */\ndict *dictCreate(dictType *type,\n        void *privDataPtr)\n{\n    dict *d = zmalloc(sizeof(*d));\n\n    _dictInit(d,type,privDataPtr);\n    return d;\n}\n\n/* Initialize the hash table */\nint _dictInit(dict *d, dictType *type,\n        void *privDataPtr)\n{\n    _dictReset(&d->ht[0]);\n    _dictReset(&d->ht[1]);\n    d->type = type;\n    d->privdata = privDataPtr;\n    d->rehashidx = -1;\n    d->iterators = 0;\n    return DICT_OK;\n}\n\n/* Resize the table to the minimal size that contains all the elements,\n * but with the invariant of a USED/BUCKETS ratio near to <= 1 */\nint dictResize(dict *d)\n{\n    unsigned long minimal;\n\n    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;\n    minimal = d->ht[0].used;\n    if (minimal < DICT_HT_INITIAL_SIZE)\n        minimal = DICT_HT_INITIAL_SIZE;\n    return dictExpand(d, minimal);\n}\n\n/* Expand or create the hash table */\nint dictExpand(dict *d, unsigned long size)\n{\n    /* the size is invalid if it is smaller than the number of\n     * elements already inside the hash table */\n    if (dictIsRehashing(d) || d->ht[0].used > size)\n        return DICT_ERR;\n\n    dictht n; /* the new hash table */\n    unsigned long realsize = _dictNextPower(size);\n\n    /* Rehashing to the same table size is not useful. */\n    if (realsize == d->ht[0].size) return DICT_ERR;\n\n    /* Allocate the new hash table and initialize all pointers to NULL */\n    n.size = realsize;\n    n.sizemask = realsize-1;\n    n.table = 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    if (d->ht[0].table == NULL) {\n        d->ht[0] = n;\n        return DICT_OK;\n    }\n\n    /* Prepare a second hash table for incremental rehashing */\n    d->ht[1] = n;\n    d->rehashidx = 0;\n    return DICT_OK;\n}\n\n/* Performs N steps of incremental rehashing. Returns 1 if there are still\n * keys to move from the old to the new hash table, otherwise 0 is returned.\n *\n * Note that a rehashing step consists in moving a bucket (that may have more\n * than one key as we use chaining) from the old to the new hash table, however\n * since part of the hash table may be composed of empty spaces, it is not\n * guaranteed that this function will rehash even a single bucket, since it\n * will visit at max N*10 empty buckets in total, otherwise the amount of\n * work it does would be unbound and the function may block for a long time. */\nint dictRehash(dict *d, int n) {\n    int empty_visits = n*10; /* Max number of empty buckets to visit. */\n    if (!dictIsRehashing(d)) return 0;\n\n    while(n-- && d->ht[0].used != 0) {\n        dictEntry *de, *nextde;\n\n        /* Note that rehashidx can't overflow as we are sure there are more\n         * elements because ht[0].used != 0 */\n        assert(d->ht[0].size > (unsigned long)d->rehashidx);\n        while(d->ht[0].table[d->rehashidx] == NULL) {\n            d->rehashidx++;\n            if (--empty_visits == 0) return 1;\n        }\n        de = d->ht[0].table[d->rehashidx];\n        /* Move all the keys in this bucket from the old to the new hash HT */\n        while(de) {\n            uint64_t h;\n\n            nextde = de->next;\n            /* Get the index in the new hash table */\n            h = dictHashKey(d, de->key) & d->ht[1].sizemask;\n            de->next = d->ht[1].table[h];\n            d->ht[1].table[h] = de;\n            d->ht[0].used--;\n            d->ht[1].used++;\n            de = nextde;\n        }\n        d->ht[0].table[d->rehashidx] = NULL;\n        d->rehashidx++;\n    }\n\n    /* Check if we already rehashed the whole table... */\n    if (d->ht[0].used == 0) {\n        zfree(d->ht[0].table);\n        d->ht[0] = d->ht[1];\n        _dictReset(&d->ht[1]);\n        d->rehashidx = -1;\n        return 0;\n    }\n\n    /* More to rehash... */\n    return 1;\n}\n\nlong long timeInMilliseconds(void) {\n    struct timeval tv;\n\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);\n}\n\n/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */\nint dictRehashMilliseconds(dict *d, int ms) {\n    long long start = timeInMilliseconds();\n    int rehashes = 0;\n\n    while(dictRehash(d,100)) {\n        rehashes += 100;\n        if (timeInMilliseconds()-start > ms) break;\n    }\n    return rehashes;\n}\n\n/* This function performs just a step of rehashing, and only if there are\n * no safe iterators bound to our hash table. When we have iterators in the\n * middle of a rehashing we can't mess with the two hash tables otherwise\n * some element can be missed or duplicated.\n *\n * This function is called by common lookup or update operations in the\n * dictionary so that the hash table automatically migrates from H1 to H2\n * while it is actively used. */\nstatic void _dictRehashStep(dict *d) {\n    if (d->iterators == 0) dictRehash(d,1);\n}\n\n/* Add an element to the target hash table */\nint dictAdd(dict *d, void *key, void *val)\n{\n    dictEntry *entry = dictAddRaw(d,key,NULL);\n\n    if (!entry) return DICT_ERR;\n    dictSetVal(d, entry, val);\n    return DICT_OK;\n}\n\n/* Low level add or find:\n * This function adds the entry but instead of setting a value returns the\n * dictEntry structure to the user, that will make sure to fill the value\n * field as he wishes.\n *\n * This function is also directly exposed to the user API to be called\n * mainly in order to store non-pointers inside the hash value, example:\n *\n * entry = dictAddRaw(dict,mykey,NULL);\n * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);\n *\n * Return values:\n *\n * If key already exists NULL is returned, and \"*existing\" is populated\n * with the existing entry if existing is not NULL.\n *\n * If key was added, the hash entry is returned to be manipulated by the caller.\n */\ndictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)\n{\n    long index;\n    dictEntry *entry;\n    dictht *ht;\n\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    /* Get the index of the new element, or -1 if\n     * the element already exists. */\n    if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)\n        return NULL;\n\n    /* Allocate the memory and store the new entry.\n     * Insert the element in top, with the assumption that in a database\n     * system it is more likely that recently added entries are accessed\n     * more frequently. */\n    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];\n    entry = zmalloc(sizeof(*entry));\n    entry->next = ht->table[index];\n    ht->table[index] = entry;\n    ht->used++;\n\n    /* Set the hash entry fields. */\n    dictSetKey(d, entry, key);\n    return entry;\n}\n\n/* Add or Overwrite:\n * Add an element, discarding the old value if the key already exists.\n * Return 1 if the key was added from scratch, 0 if there was already an\n * element with such key and dictReplace() just performed a value update\n * operation. */\nint dictReplace(dict *d, void *key, void *val)\n{\n    dictEntry *entry, *existing, auxentry;\n\n    /* Try to add the element. If the key\n     * does not exists dictAdd will succeed. */\n    entry = dictAddRaw(d,key,&existing);\n    if (entry) {\n        dictSetVal(d, entry, val);\n        return 1;\n    }\n\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 = *existing;\n    dictSetVal(d, existing, val);\n    dictFreeVal(d, &auxentry);\n    return 0;\n}\n\n/* Add or Find:\n * dictAddOrFind() is simply a version of dictAddRaw() that always\n * returns the hash entry of the specified key, even if the key already\n * exists and can't be added (in that case the entry of the already\n * existing key is returned.)\n *\n * See dictAddRaw() for more information. */\ndictEntry *dictAddOrFind(dict *d, void *key) {\n    dictEntry *entry, *existing;\n    entry = dictAddRaw(d,key,&existing);\n    return entry ? entry : existing;\n}\n\n/* Search and remove an element. This is an helper function for\n * dictDelete() and dictUnlink(), please check the top comment\n * of those functions. */\nstatic dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {\n    uint64_t h, idx;\n    dictEntry *he, *prevHe;\n    int table;\n\n    if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;\n\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    h = dictHashKey(d, key);\n\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        he = d->ht[table].table[idx];\n        prevHe = NULL;\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key)) {\n                /* Unlink the element from the list */\n                if (prevHe)\n                    prevHe->next = he->next;\n                else\n                    d->ht[table].table[idx] = he->next;\n                if (!nofree) {\n                    dictFreeKey(d, he);\n                    dictFreeVal(d, he);\n                    zfree(he);\n                }\n                d->ht[table].used--;\n                return he;\n            }\n            prevHe = he;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) break;\n    }\n    return NULL; /* not found */\n}\n\n/* Remove an element, returning DICT_OK on success or DICT_ERR if the\n * element was not found. */\nint dictDelete(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,0) ? DICT_OK : DICT_ERR;\n}\n\n/* Remove an element from the table, but without actually releasing\n * the key, value and dictionary entry. The dictionary entry is returned\n * if the element was found (and unlinked from the table), and the user\n * should later call `dictFreeUnlinkedEntry()` with it in order to release it.\n * Otherwise if the key is not found, NULL is returned.\n *\n * This function is useful when we want to remove something from the hash\n * table but want to use its value before actually deleting the entry.\n * Without this function the pattern would require two lookups:\n *\n *  entry = dictFind(...);\n *  // Do something with entry\n *  dictDelete(dictionary,entry);\n *\n * Thanks to this function it is possible to avoid this, and use\n * instead:\n *\n * entry = dictUnlink(dictionary,entry);\n * // Do something with entry\n * dictFreeUnlinkedEntry(entry); // <- This does not need to lookup again.\n */\ndictEntry *dictUnlink(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,1);\n}\n\n/* You need to call this function to really free the entry after a call\n * to dictUnlink(). It's safe to call this function with 'he' = NULL. */\nvoid dictFreeUnlinkedEntry(dict *d, dictEntry *he) {\n    if (he == NULL) return;\n    dictFreeKey(d, he);\n    dictFreeVal(d, he);\n    zfree(he);\n}\n\n/* Destroy an entire dictionary */\nint _dictClear(dict *d, dictht *ht, void(callback)(void *)) {\n    unsigned long i;\n\n    /* Free all the elements */\n    for (i = 0; i < ht->size && ht->used > 0; i++) {\n        dictEntry *he, *nextHe;\n\n        if (callback && (i & 65535) == 0) callback(d->privdata);\n\n        if ((he = ht->table[i]) == NULL) continue;\n        while(he) {\n            nextHe = he->next;\n            dictFreeKey(d, he);\n            dictFreeVal(d, he);\n            zfree(he);\n            ht->used--;\n            he = nextHe;\n        }\n    }\n    /* Free the table and the allocated cache structure */\n    zfree(ht->table);\n    /* Re-initialize the table */\n    _dictReset(ht);\n    return DICT_OK; /* never fails */\n}\n\n/* Clear & Release the hash table */\nvoid dictRelease(dict *d)\n{\n    _dictClear(d,&d->ht[0],NULL);\n    _dictClear(d,&d->ht[1],NULL);\n    zfree(d);\n}\n\ndictEntry *dictFind(dict *d, const void *key)\n{\n    dictEntry *he;\n    uint64_t h, idx, table;\n\n    if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    h = dictHashKey(d, key);\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key))\n                return he;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) return NULL;\n    }\n    return NULL;\n}\n\nvoid *dictFetchValue(dict *d, const void *key) {\n    dictEntry *he;\n\n    he = dictFind(d,key);\n    return he ? dictGetVal(he) : NULL;\n}\n\n/* A fingerprint is a 64 bit number that represents the state of the dictionary\n * at a given time, it's just a few dict properties xored together.\n * When an unsafe iterator is initialized, we get the dict fingerprint, and check\n * the fingerprint again when the iterator is released.\n * If the two fingerprints are different it means that the user of the iterator\n * performed forbidden operations against the dictionary while iterating. */\nlong long dictFingerprint(dict *d) {\n    long long integers[6], hash = 0;\n    int j;\n\n    integers[0] = (long) d->ht[0].table;\n    integers[1] = d->ht[0].size;\n    integers[2] = d->ht[0].used;\n    integers[3] = (long) d->ht[1].table;\n    integers[4] = d->ht[1].size;\n    integers[5] = d->ht[1].used;\n\n    /* We hash N integers by summing every successive integer with the integer\n     * hashing of the previous sum. Basically:\n     *\n     * Result = hash(hash(hash(int1)+int2)+int3) ...\n     *\n     * This way the same set of integers in a different order will (likely) hash\n     * to a different number. */\n    for (j = 0; j < 6; j++) {\n        hash += integers[j];\n        /* For the hashing step we use Tomas Wang's 64 bit integer hash. */\n        hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;\n        hash = hash ^ (hash >> 24);\n        hash = (hash + (hash << 3)) + (hash << 8); // hash * 265\n        hash = hash ^ (hash >> 14);\n        hash = (hash + (hash << 2)) + (hash << 4); // hash * 21\n        hash = hash ^ (hash >> 28);\n        hash = hash + (hash << 31);\n    }\n    return hash;\n}\n\ndictIterator *dictGetIterator(dict *d)\n{\n    dictIterator *iter = 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    return iter;\n}\n\ndictIterator *dictGetSafeIterator(dict *d) {\n    dictIterator *i = dictGetIterator(d);\n\n    i->safe = 1;\n    return i;\n}\n\ndictEntry *dictNext(dictIterator *iter)\n{\n    while (1) {\n        if (iter->entry == NULL) {\n            dictht *ht = &iter->d->ht[iter->table];\n            if (iter->index == -1 && iter->table == 0) {\n                if (iter->safe)\n                    iter->d->iterators++;\n                else\n                    iter->fingerprint = dictFingerprint(iter->d);\n            }\n            iter->index++;\n            if (iter->index >= (long) ht->size) {\n                if (dictIsRehashing(iter->d) && iter->table == 0) {\n                    iter->table++;\n                    iter->index = 0;\n                    ht = &iter->d->ht[1];\n                } else {\n                    break;\n                }\n            }\n            iter->entry = ht->table[iter->index];\n        } else {\n            iter->entry = iter->nextEntry;\n        }\n        if (iter->entry) {\n            /* We need to save the 'next' here, the iterator user\n             * may delete the entry we are returning. */\n            iter->nextEntry = iter->entry->next;\n            return iter->entry;\n        }\n    }\n    return NULL;\n}\n\nvoid dictReleaseIterator(dictIterator *iter)\n{\n    if (!(iter->index == -1 && iter->table == 0)) {\n        if (iter->safe)\n            iter->d->iterators--;\n        else\n            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 */\ndictEntry *dictGetRandomKey(dict *d)\n{\n    dictEntry *he, *orighe;\n    unsigned long h;\n    int listlen, listele;\n\n    if (dictSize(d) == 0) return NULL;\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    if (dictIsRehashing(d)) {\n        do {\n            /* We are sure there are no elements in indexes from 0\n             * to rehashidx-1 */\n            h = d->rehashidx + (random() % (d->ht[0].size +\n                                            d->ht[1].size -\n                                            d->rehashidx));\n            he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :\n                                      d->ht[0].table[h];\n        } while(he == NULL);\n    } else {\n        do {\n            h = random() & d->ht[0].sizemask;\n            he = d->ht[0].table[h];\n        } while(he == NULL);\n    }\n\n    /* Now we found a non empty bucket, but it is a linked\n     * list and we need to get a random element from the list.\n     * The only sane way to do so is counting the elements and\n     * select a random index. */\n    listlen = 0;\n    orighe = he;\n    while(he) {\n        he = he->next;\n        listlen++;\n    }\n    listele = random() % listlen;\n    he = orighe;\n    while(listele--) he = he->next;\n    return he;\n}\n\n/* This function samples the dictionary to return a few keys from random\n * locations.\n *\n * It does not guarantee to return all the keys specified in 'count', nor\n * it does guarantee to return non-duplicated elements, however it will make\n * some effort to do both things.\n *\n * Returned pointers to hash table entries are stored into 'des' that\n * points to an array of dictEntry pointers. The array must have room for\n * at least 'count' elements, that is the argument we pass to the function\n * to tell how many random elements we need.\n *\n * The function returns the number of items stored into 'des', that may\n * be less than 'count' if the hash table has less than 'count' elements\n * inside, or if not enough elements were found in a reasonable amount of\n * steps.\n *\n * Note that this function is not suitable when you need a good distribution\n * of the returned items, but only when you need to \"sample\" a given number\n * of continuous elements to run some kind of algorithm or to produce\n * statistics. However the function is much faster than dictGetRandomKey()\n * at producing N elements. */\nunsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {\n    unsigned long j; /* internal hash table id, 0 or 1. */\n    unsigned long tables; /* 1 or 2 tables? */\n    unsigned long stored = 0, maxsizemask;\n    unsigned long maxsteps;\n\n    if (dictSize(d) < count) count = dictSize(d);\n    maxsteps = count*10;\n\n    /* Try to do a rehashing work proportional to 'count'. */\n    for (j = 0; j < count; j++) {\n        if (dictIsRehashing(d))\n            _dictRehashStep(d);\n        else\n            break;\n    }\n\n    tables = dictIsRehashing(d) ? 2 : 1;\n    maxsizemask = d->ht[0].sizemask;\n    if (tables > 1 && maxsizemask < d->ht[1].sizemask)\n        maxsizemask = d->ht[1].sizemask;\n\n    /* Pick a random point inside the larger table. */\n    unsigned long i = random() & maxsizemask;\n    unsigned long emptylen = 0; /* Continuous empty entries so far. */\n    while(stored < count && maxsteps--) {\n        for (j = 0; j < tables; j++) {\n            /* Invariant of the dict.c rehashing: up to the indexes already\n             * visited in ht[0] during the rehashing, there are no populated\n             * buckets, so we can skip ht[0] for indexes between 0 and idx-1. */\n            if (tables == 2 && j == 0 && i < (unsigned long) d->rehashidx) {\n                /* Moreover, if we are currently out of range in the second\n                 * table, there will be no elements in both tables up to\n                 * the current rehashing index, so we jump if possible.\n                 * (this happens when going from big to small table). */\n                if (i >= d->ht[1].size)\n                    i = d->rehashidx;\n                else\n                    continue;\n            }\n            if (i >= d->ht[j].size) continue; /* Out of range for this table. */\n            dictEntry *he = d->ht[j].table[i];\n\n            /* Count contiguous empty buckets, and jump to other\n             * locations if they reach 'count' (with a minimum of 5). */\n            if (he == NULL) {\n                emptylen++;\n                if (emptylen >= 5 && emptylen > count) {\n                    i = random() & maxsizemask;\n                    emptylen = 0;\n                }\n            } else {\n                emptylen = 0;\n                while (he) {\n                    /* Collect all the elements of the buckets found non\n                     * empty while iterating. */\n                    *des = he;\n                    des++;\n                    he = he->next;\n                    stored++;\n                    if (stored == count) return stored;\n                }\n            }\n        }\n        i = (i+1) & maxsizemask;\n    }\n    return stored;\n}\n\n/* This is like dictGetRandomKey() from the POV of the API, but will do more\n * work to ensure a better distribution of the returned element.\n *\n * This function improves the distribution because the dictGetRandomKey()\n * problem is that it selects a random bucket, then it selects a random\n * element from the chain in the bucket. However elements being in different\n * chain lengths will have different probabilities of being reported. With\n * this function instead what we do is to consider a \"linear\" range of the table\n * that may be constituted of N buckets with chains of different lengths\n * appearing one after the other. Then we report a random element in the range.\n * In this way we smooth away the problem of different chain lenghts. */\n#define GETFAIR_NUM_ENTRIES 15\ndictEntry *dictGetFairRandomKey(dict *d) {\n    dictEntry *entries[GETFAIR_NUM_ENTRIES];\n    unsigned int count = dictGetSomeKeys(d,entries,GETFAIR_NUM_ENTRIES);\n    /* Note that dictGetSomeKeys() may return zero elements in an unlucky\n     * run() even if there are actually elements inside the hash table. So\n     * when we get zero, we call the true dictGetRandomKey() that will always\n     * yeld the element if the hash table has at least one. */\n    if (count == 0) return dictGetRandomKey(d);\n    unsigned int idx = rand() % count;\n    return entries[idx];\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 = CHAR_BIT * sizeof(v); // bit size; must be power of 2\n    unsigned long mask = ~0UL;\n    while ((s >>= 1) > 0) {\n        mask ^= (mask << s);\n        v = ((v >> s) & mask) | ((v << s) & ~mask);\n    }\n    return v;\n}\n\n/* dictScan() is used to iterate over the elements of a dictionary.\n *\n * Iterating works the following way:\n *\n * 1) Initially you call the function using a cursor (v) value of 0.\n * 2) The function performs one step of the iteration, and returns the\n *    new cursor value you must use in the next call.\n * 3) When the returned cursor is 0, the iteration is complete.\n *\n * The function guarantees all elements present in the\n * dictionary get returned between the start and end of the iteration.\n * However it is possible some elements get returned multiple times.\n *\n * For every element returned, the callback argument 'fn' is\n * called with 'privdata' as first argument and the dictionary entry\n * 'de' as second argument.\n *\n * HOW IT WORKS.\n *\n * The iteration algorithm was designed by Pieter Noordhuis.\n * The main idea is to increment a cursor starting from the higher order\n * bits. That is, instead of incrementing the cursor normally, the bits\n * of the cursor are reversed, then the cursor is incremented, and finally\n * the bits are reversed again.\n *\n * This strategy is needed because the hash table may be resized between\n * iteration calls.\n *\n * dict.c hash tables are always power of two in size, and they\n * use chaining, so the position of an element in a given table is given\n * by computing the bitwise AND between Hash(key) and SIZE-1\n * (where SIZE-1 is always the mask that is equivalent to taking the rest\n *  of the division between the Hash of the key and SIZE).\n *\n * For example if the current hash table size is 16, the mask is\n * (in binary) 1111. The position of a key in the hash table will always be\n * the last four bits of the hash output, and so forth.\n *\n * WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?\n *\n * If the hash table grows, elements can go anywhere in one multiple of\n * the old bucket: for example let's say we already iterated with\n * a 4 bit cursor 1100 (the mask is 1111 because hash table size = 16).\n *\n * If the hash table will be resized to 64 elements, then the new mask will\n * be 111111. The new buckets you obtain by substituting in ??1100\n * with either 0 or 1 can be targeted only by keys we already visited\n * when scanning the bucket 1100 in the smaller hash table.\n *\n * By iterating the higher bits first, because of the inverted counter, the\n * cursor does not need to restart if the table size gets bigger. It will\n * continue iterating using cursors without '1100' at the end, and also\n * without any other combination of the final 4 bits already explored.\n *\n * Similarly when the table size shrinks over time, for example going from\n * 16 to 8, if a combination of the lower three bits (the mask for size 8\n * is 111) were already completely explored, it would not be visited again\n * because we are sure we tried, for example, both 0111 and 1111 (all the\n * variations of the higher bit) so we don't need to test it again.\n *\n * WAIT... YOU HAVE *TWO* TABLES DURING REHASHING!\n *\n * Yes, this is true, but we always iterate the smaller table first, then\n * we test all the expansions of the current cursor into the larger\n * table. For example if the current cursor is 101 and we also have a\n * larger table of size 16, we also test (0)101 and (1)101 inside the larger\n * table. This reduces the problem back to having only one table, where\n * the larger one, if it exists, is just an expansion of the smaller one.\n *\n * LIMITATIONS\n *\n * This iterator is completely stateless, and this is a huge advantage,\n * including no additional memory used.\n *\n * The disadvantages resulting from this design are:\n *\n * 1) It is possible we return elements more than once. However this is usually\n *    easy to deal with in the application level.\n * 2) The iterator must return multiple elements per call, as it needs to always\n *    return all the keys chained in a given bucket, and all the expansions, so\n *    we are sure we don't miss keys moving during rehashing.\n * 3) The reverse cursor is somewhat hard to understand at first, but this\n *    comment is supposed to help.\n */\nunsigned long dictScan(dict *d,\n                       unsigned long v,\n                       dictScanFunction *fn,\n                       dictScanBucketFunction* bucketfn,\n                       void *privdata)\n{\n    dictht *t0, *t1;\n    const dictEntry *de, *next;\n    unsigned long m0, m1;\n\n    if (dictSize(d) == 0) return 0;\n\n    /* Having a safe iterator means no rehashing can happen, see _dictRehashStep.\n     * This is needed in case the scan callback tries to do dictFind or alike. */\n    d->iterators++;\n\n    if (!dictIsRehashing(d)) {\n        t0 = &(d->ht[0]);\n        m0 = t0->sizemask;\n\n        /* Emit entries at cursor */\n        if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);\n        de = t0->table[v & m0];\n        while (de) {\n            next = de->next;\n            fn(privdata, de);\n            de = next;\n        }\n\n        /* Set unmasked bits so incrementing the reversed cursor\n         * operates on the masked bits */\n        v |= ~m0;\n\n        /* Increment the reverse cursor */\n        v = rev(v);\n        v++;\n        v = rev(v);\n\n    } else {\n        t0 = &d->ht[0];\n        t1 = &d->ht[1];\n\n        /* Make sure t0 is the smaller and t1 is the bigger table */\n        if (t0->size > t1->size) {\n            t0 = &d->ht[1];\n            t1 = &d->ht[0];\n        }\n\n        m0 = t0->sizemask;\n        m1 = t1->sizemask;\n\n        /* Emit entries at cursor */\n        if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);\n        de = t0->table[v & m0];\n        while (de) {\n            next = de->next;\n            fn(privdata, de);\n            de = next;\n        }\n\n        /* Iterate over indices in larger table that are the expansion\n         * of the index pointed to by the cursor in the smaller table */\n        do {\n            /* Emit entries at cursor */\n            if (bucketfn) bucketfn(privdata, &t1->table[v & m1]);\n            de = t1->table[v & m1];\n            while (de) {\n                next = de->next;\n                fn(privdata, de);\n                de = next;\n            }\n\n            /* Increment the reverse cursor not covered by the smaller mask.*/\n            v |= ~m1;\n            v = rev(v);\n            v++;\n            v = rev(v);\n\n            /* Continue while bits covered by mask difference is non-zero */\n        } while (v & (m0 ^ m1));\n    }\n\n    /* undo the ++ at the top */\n    d->iterators--;\n\n    return v;\n}\n\n/* ------------------------- private functions ------------------------------ */\n\n/* Expand the hash table if needed */\nstatic int _dictExpandIfNeeded(dict *d)\n{\n    /* Incremental rehashing already in progress. Return. */\n    if (dictIsRehashing(d)) return DICT_OK;\n\n    /* If the hash table is empty expand it to the initial size. */\n    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);\n\n    /* If we reached the 1:1 ratio, and we are allowed to resize the hash\n     * table (global setting) or we should avoid it but the ratio between\n     * elements/buckets is over the \"safe\" threshold, we resize doubling\n     * the number of buckets. */\n    if (d->ht[0].used >= d->ht[0].size &&\n        (dict_can_resize ||\n         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))\n    {\n        return dictExpand(d, d->ht[0].used*2);\n    }\n    return DICT_OK;\n}\n\n/* Our hash table capability is a power of two */\nstatic unsigned long _dictNextPower(unsigned long size)\n{\n    unsigned long i = DICT_HT_INITIAL_SIZE;\n\n    if (size >= LONG_MAX) return LONG_MAX + 1LU;\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 * and the optional output parameter may be filled.\n *\n * Note that if we are in the process of rehashing the hash table, the\n * index is always returned in the context of the second (new) hash table. */\nstatic long _dictKeyIndex(dict *d, const void *key, uint64_t hash, dictEntry **existing)\n{\n    unsigned long idx, table;\n    dictEntry *he;\n    if (existing) *existing = NULL;\n\n    /* Expand the hash table if needed */\n    if (_dictExpandIfNeeded(d) == DICT_ERR)\n        return -1;\n    for (table = 0; table <= 1; table++) {\n        idx = hash & d->ht[table].sizemask;\n        /* Search if this slot does not already contain the given key */\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key)) {\n                if (existing) *existing = he;\n                return -1;\n            }\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) break;\n    }\n    return idx;\n}\n\nvoid dictEmpty(dict *d, void(callback)(void*)) {\n    _dictClear(d,&d->ht[0],callback);\n    _dictClear(d,&d->ht[1],callback);\n    d->rehashidx = -1;\n    d->iterators = 0;\n}\n\nvoid dictEnableResize(void) {\n    dict_can_resize = 1;\n}\n\nvoid dictDisableResize(void) {\n    dict_can_resize = 0;\n}\n\nuint64_t dictGetHash(dict *d, const void *key) {\n    return dictHashKey(d, key);\n}\n\n/* Finds the dictEntry reference by using pointer and pre-calculated hash.\n * oldkey is a dead pointer and should not be accessed.\n * the hash value should be provided using dictGetHash.\n * no string / key comparison is performed.\n * return value is the reference to the dictEntry if found, or NULL if not found. */\ndictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash) {\n    dictEntry *he, **heref;\n    unsigned long idx, table;\n\n    if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */\n    for (table = 0; table <= 1; table++) {\n        idx = hash & d->ht[table].sizemask;\n        heref = &d->ht[table].table[idx];\n        he = *heref;\n        while(he) {\n            if (oldptr==he->key)\n                return heref;\n            heref = &he->next;\n            he = *heref;\n        }\n        if (!dictIsRehashing(d)) return NULL;\n    }\n    return NULL;\n}\n\n/* ------------------------------- Debugging ---------------------------------*/\n\n#define DICT_STATS_VECTLEN 50\nsize_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {\n    unsigned long i, slots = 0, chainlen, maxchainlen = 0;\n    unsigned long totchainlen = 0;\n    unsigned long clvector[DICT_STATS_VECTLEN];\n    size_t l = 0;\n\n    if (ht->used == 0) {\n        return snprintf(buf,bufsize,\n            \"No stats available for empty dictionaries\\n\");\n    }\n\n    /* Compute stats. */\n    for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;\n    for (i = 0; i < ht->size; i++) {\n        dictEntry *he;\n\n        if (ht->table[i] == NULL) {\n            clvector[0]++;\n            continue;\n        }\n        slots++;\n        /* For each hash entry on this slot... */\n        chainlen = 0;\n        he = ht->table[i];\n        while(he) {\n            chainlen++;\n            he = he->next;\n        }\n        clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;\n        if (chainlen > maxchainlen) maxchainlen = chainlen;\n        totchainlen += chainlen;\n    }\n\n    /* Generate human readable stats. */\n    l += snprintf(buf+l,bufsize-l,\n        \"Hash table %d stats (%s):\\n\"\n        \" table size: %ld\\n\"\n        \" number of elements: %ld\\n\"\n        \" different slots: %ld\\n\"\n        \" max chain length: %ld\\n\"\n        \" avg chain length (counted): %.02f\\n\"\n        \" avg chain length (computed): %.02f\\n\"\n        \" Chain length distribution:\\n\",\n        tableid, (tableid == 0) ? \"main hash table\" : \"rehashing target\",\n        ht->size, ht->used, slots, maxchainlen,\n        (float)totchainlen/slots, (float)ht->used/slots);\n\n    for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {\n        if (clvector[i] == 0) continue;\n        if (l >= bufsize) break;\n        l += snprintf(buf+l,bufsize-l,\n            \"   %s%ld: %ld (%.02f%%)\\n\",\n            (i == DICT_STATS_VECTLEN-1)?\">= \":\"\",\n            i, clvector[i], ((float)clvector[i]/ht->size)*100);\n    }\n\n    /* Unlike snprintf(), teturn the number of characters actually written. */\n    if (bufsize) buf[bufsize-1] = '\\0';\n    return strlen(buf);\n}\n\nvoid dictGetStats(char *buf, size_t bufsize, dict *d) {\n    size_t l;\n    char *orig_buf = buf;\n    size_t orig_bufsize = bufsize;\n\n    l = _dictGetStatsHt(buf,bufsize,&d->ht[0],0);\n    buf += l;\n    bufsize -= l;\n    if (dictIsRehashing(d) && bufsize > 0) {\n        _dictGetStatsHt(buf,bufsize,&d->ht[1],1);\n    }\n    /* Make sure there is a NULL term at the end. */\n    if (orig_bufsize) orig_buf[orig_bufsize-1] = '\\0';\n}\n\n/* ------------------------------- Benchmark ---------------------------------*/\n\n#ifdef DICT_BENCHMARK_MAIN\n\n#include \"sds.h\"\n\nuint64_t hashCallback(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nint compareCallback(void *privdata, const void *key1, const void *key2) {\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\nvoid freeCallback(void *privdata, void *val) {\n    DICT_NOTUSED(privdata);\n\n    sdsfree(val);\n}\n\ndictType BenchmarkDictType = {\n    hashCallback,\n    NULL,\n    NULL,\n    compareCallback,\n    freeCallback,\n    NULL\n};\n\n#define start_benchmark() start = timeInMilliseconds()\n#define end_benchmark(msg) do { \\\n    elapsed = timeInMilliseconds()-start; \\\n    printf(msg \": %ld items in %lld ms\\n\", count, elapsed); \\\n} while(0);\n\n/* dict-benchmark [count] */\nint main(int argc, char **argv) {\n    long j;\n    long long start, elapsed;\n    dict *dict = dictCreate(&BenchmarkDictType,NULL);\n    long count = 0;\n\n    if (argc == 2) {\n        count = strtol(argv[1],NULL,10);\n    } else {\n        count = 5000000;\n    }\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        int retval = dictAdd(dict,sdsfromlonglong(j),(void*)j);\n        assert(retval == DICT_OK);\n    }\n    end_benchmark(\"Inserting\");\n    assert((long)dictSize(dict) == count);\n\n    /* Wait for rehashing. */\n    while (dictIsRehashing(dict)) {\n        dictRehashMilliseconds(dict,100);\n    }\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(j);\n        dictEntry *de = dictFind(dict,key);\n        assert(de != NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Linear access of existing elements\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(j);\n        dictEntry *de = dictFind(dict,key);\n        assert(de != NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Linear access of existing elements (2nd round)\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(rand() % count);\n        dictEntry *de = dictFind(dict,key);\n        assert(de != NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Random access of existing elements\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(rand() % count);\n        key[0] = 'X';\n        dictEntry *de = dictFind(dict,key);\n        assert(de == NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Accessing missing\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(j);\n        int retval = dictDelete(dict,key);\n        assert(retval == DICT_OK);\n        key[0] += 17; /* Change first number to letter. */\n        retval = dictAdd(dict,key,(void*)j);\n        assert(retval == DICT_OK);\n    }\n    end_benchmark(\"Removing and adding\");\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 * 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#define DICT_OK 0\n#define DICT_ERR 1\n\n/* Unused arguments generate annoying warnings... */\n#define DICT_NOTUSED(V) ((void) V)\n\ntypedef struct dictEntry {\n    void *key;\n    union {\n        void *val;\n        uint64_t u64;\n        int64_t s64;\n        double d;\n    } v;\n    struct dictEntry *next;\n} dictEntry;\n\ntypedef struct dictType {\n    uint64_t (*hashFunction)(const void *key);\n    void *(*keyDup)(void *privdata, const void *key);\n    void *(*valDup)(void *privdata, const void *obj);\n    int (*keyCompare)(void *privdata, const void *key1, const void *key2);\n    void (*keyDestructor)(void *privdata, void *key);\n    void (*valDestructor)(void *privdata, void *obj);\n} dictType;\n\n/* This is our hash table structure. Every dictionary has two of this as we\n * implement incremental rehashing, for the old to the new table. */\ntypedef struct dictht {\n    dictEntry **table;\n    unsigned long size;\n    unsigned long sizemask;\n    unsigned long used;\n} dictht;\n\ntypedef struct dict {\n    dictType *type;\n    void *privdata;\n    dictht ht[2];\n    long rehashidx; /* rehashing not in progress if rehashidx == -1 */\n    unsigned long iterators; /* number of iterators currently running */\n} dict;\n\n/* If safe is set to 1 this is a safe iterator, that means, you can call\n * dictAdd, dictFind, and other functions against the dictionary even while\n * iterating. Otherwise it is a non safe iterator, and only dictNext()\n * should be called while iterating. */\ntypedef struct dictIterator {\n    dict *d;\n    long index;\n    int table, safe;\n    dictEntry *entry, *nextEntry;\n    /* unsafe iterator fingerprint for misuse detection. */\n    long long fingerprint;\n} dictIterator;\n\ntypedef void (dictScanFunction)(void *privdata, const dictEntry *de);\ntypedef void (dictScanBucketFunction)(void *privdata, dictEntry **bucketref);\n\n/* This is the initial size of every hash table */\n#define DICT_HT_INITIAL_SIZE     4\n\n/* ------------------------------- Macros ------------------------------------*/\n#define dictFreeVal(d, entry) \\\n    if ((d)->type->valDestructor) \\\n        (d)->type->valDestructor((d)->privdata, (entry)->v.val)\n\n#define dictSetVal(d, entry, _val_) do { \\\n    if ((d)->type->valDup) \\\n        (entry)->v.val = (d)->type->valDup((d)->privdata, _val_); \\\n    else \\\n        (entry)->v.val = (_val_); \\\n} while(0)\n\n#define dictSetSignedIntegerVal(entry, _val_) \\\n    do { (entry)->v.s64 = _val_; } while(0)\n\n#define dictSetUnsignedIntegerVal(entry, _val_) \\\n    do { (entry)->v.u64 = _val_; } while(0)\n\n#define dictSetDoubleVal(entry, _val_) \\\n    do { (entry)->v.d = _val_; } while(0)\n\n#define dictFreeKey(d, entry) \\\n    if ((d)->type->keyDestructor) \\\n        (d)->type->keyDestructor((d)->privdata, (entry)->key)\n\n#define dictSetKey(d, entry, _key_) do { \\\n    if ((d)->type->keyDup) \\\n        (entry)->key = (d)->type->keyDup((d)->privdata, _key_); \\\n    else \\\n        (entry)->key = (_key_); \\\n} while(0)\n\n#define dictCompareKeys(d, key1, key2) \\\n    (((d)->type->keyCompare) ? \\\n        (d)->type->keyCompare((d)->privdata, key1, key2) : \\\n        (key1) == (key2))\n\n#define dictHashKey(d, key) (d)->type->hashFunction(key)\n#define dictGetKey(he) ((he)->key)\n#define dictGetVal(he) ((he)->v.val)\n#define dictGetSignedIntegerVal(he) ((he)->v.s64)\n#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)\n#define dictGetDoubleVal(he) ((he)->v.d)\n#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)\n#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)\n#define dictIsRehashing(d) ((d)->rehashidx != -1)\n\n/* API */\ndict *dictCreate(dictType *type, void *privDataPtr);\nint dictExpand(dict *d, unsigned long size);\nint dictAdd(dict *d, void *key, void *val);\ndictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing);\ndictEntry *dictAddOrFind(dict *d, void *key);\nint dictReplace(dict *d, void *key, void *val);\nint dictDelete(dict *d, const void *key);\ndictEntry *dictUnlink(dict *ht, const void *key);\nvoid dictFreeUnlinkedEntry(dict *d, dictEntry *he);\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);\ndictEntry *dictGetFairRandomKey(dict *d);\nunsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);\nvoid dictGetStats(char *buf, size_t bufsize, dict *d);\nuint64_t dictGenHashFunction(const void *key, int len);\nuint64_t 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(uint8_t *seed);\nuint8_t *dictGetHashFunctionSeed(void);\nunsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanBucketFunction *bucketfn, void *privdata);\nuint64_t dictGetHash(dict *d, const void *key);\ndictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash);\n\n/* Hash table types */\nextern dictType dictTypeHeapStringCopyKey;\nextern dictType dictTypeHeapStrings;\nextern dictType dictTypeHeapStringCopyKeyValue;\n\n#endif /* __DICT_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 REDIS_TEST\n#include <stdio.h>\n\n#define UNUSED(x) (void)(x)\nint endianconvTest(int argc, char *argv[]) {\n    char buf[32];\n\n    UNUSED(argc);\n    UNUSED(argv);\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": "Chapter02/redis-6.0.3/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 conversion only if the target\n * host is big endian */\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define memrev16ifbe(p) ((void)(0))\n#define memrev32ifbe(p) ((void)(0))\n#define memrev64ifbe(p) ((void)(0))\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#ifdef REDIS_TEST\nint endianconvTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/evict.c",
    "content": "/* Maxmemory directive handling (LRU eviction and other policies).\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2016, 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 \"server.h\"\n#include \"bio.h\"\n#include \"atomicvar.h\"\n\n/* ----------------------------------------------------------------------------\n * Data structures\n * --------------------------------------------------------------------------*/\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 * When an LFU policy is used instead, a reverse frequency indication is used\n * instead of the idle time, so that we still evict by larger value (larger\n * inverse frequency means to evict keys with the least frequent accesses).\n *\n * Empty entries have the key pointer set to NULL. */\n#define EVPOOL_SIZE 16\n#define EVPOOL_CACHED_SDS_SIZE 255\nstruct evictionPoolEntry {\n    unsigned long long idle;    /* Object idle time (inverse frequency for LFU) */\n    sds key;                    /* Key name. */\n    sds cached;                 /* Cached SDS object for key name. */\n    int dbid;                   /* Key DB number. */\n};\n\nstatic struct evictionPoolEntry *EvictionPoolLRU;\n\n/* ----------------------------------------------------------------------------\n * Implementation of eviction, aging and LRU\n * --------------------------------------------------------------------------*/\n\n/* Return the LRU clock, based on the clock resolution. This is a time\n * in a reduced-bits format that can be used to set and check the\n * object->lru field of redisObject structures. */\nunsigned int getLRUClock(void) {\n    return (mstime()/LRU_CLOCK_RESOLUTION) & LRU_CLOCK_MAX;\n}\n\n/* This function is 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 system call. */\nunsigned int LRU_CLOCK(void) {\n    unsigned int lruclock;\n    if (1000/server.hz <= LRU_CLOCK_RESOLUTION) {\n        lruclock = server.lruclock;\n    } else {\n        lruclock = getLRUClock();\n    }\n    return lruclock;\n}\n\n/* Given an object returns the min number of milliseconds the object was never\n * requested, using an approximated LRU algorithm. */\nunsigned long long estimateObjectIdleTime(robj *o) {\n    unsigned long long lruclock = LRU_CLOCK();\n    if (lruclock >= o->lru) {\n        return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;\n    } else {\n        return (lruclock + (LRU_CLOCK_MAX - o->lru)) *\n                    LRU_CLOCK_RESOLUTION;\n    }\n}\n\n/* 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 * The goal of the function is to free enough memory to keep Redis under the\n * configured memory limit.\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 * If all the bytes needed to return back under the limit were freed the\n * function returns C_OK, otherwise C_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 * ------------------------------------------------------------------------\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 EVPOOL_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. */\nvoid evictionPoolAlloc(void) {\n    struct evictionPoolEntry *ep;\n    int j;\n\n    ep = zmalloc(sizeof(*ep)*EVPOOL_SIZE);\n    for (j = 0; j < EVPOOL_SIZE; j++) {\n        ep[j].idle = 0;\n        ep[j].key = NULL;\n        ep[j].cached = sdsnewlen(NULL,EVPOOL_CACHED_SDS_SIZE);\n        ep[j].dbid = 0;\n    }\n    EvictionPoolLRU = 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\nvoid evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {\n    int j, k, count;\n    dictEntry *samples[server.maxmemory_samples];\n\n    count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples);\n    for (j = 0; j < count; j++) {\n        unsigned long long idle;\n        sds key;\n        robj *o;\n        dictEntry *de;\n\n        de = samples[j];\n        key = dictGetKey(de);\n\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 (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {\n            if (sampledict != keydict) de = dictFind(keydict, key);\n            o = dictGetVal(de);\n        }\n\n        /* Calculate the idle time according to the policy. This is called\n         * idle just because the code initially handled LRU, but is in fact\n         * just a score where an higher score means better candidate. */\n        if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {\n            idle = estimateObjectIdleTime(o);\n        } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n            /* When we use an LRU policy, we sort the keys by idle time\n             * so that we expire keys starting from greater idle time.\n             * However when the policy is an LFU one, we have a frequency\n             * estimation, and we want to evict keys with lower frequency\n             * first. So inside the pool we put objects using the inverted\n             * frequency subtracting the actual frequency to the maximum\n             * frequency of 255. */\n            idle = 255-LFUDecrAndReturn(o);\n        } else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {\n            /* In this case the sooner the expire the better. */\n            idle = ULLONG_MAX - (long)dictGetVal(de);\n        } else {\n            serverPanic(\"Unknown eviction policy in evictionPoolPopulate()\");\n        }\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 < EVPOOL_SIZE &&\n               pool[k].key &&\n               pool[k].idle < idle) k++;\n        if (k == 0 && pool[EVPOOL_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 < EVPOOL_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[EVPOOL_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\n                /* Save SDS before overwriting. */\n                sds cached = pool[EVPOOL_SIZE-1].cached;\n                memmove(pool+k+1,pool+k,\n                    sizeof(pool[0])*(EVPOOL_SIZE-k-1));\n                pool[k].cached = cached;\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                sds cached = pool[0].cached; /* Save SDS before overwriting. */\n                if (pool[0].key != pool[0].cached) sdsfree(pool[0].key);\n                memmove(pool,pool+1,sizeof(pool[0])*k);\n                pool[k].cached = cached;\n            }\n        }\n\n        /* Try to reuse the cached SDS string allocated in the pool entry,\n         * because allocating and deallocating this object is costly\n         * (according to the profiler, not my fantasy. Remember:\n         * premature optimizbla bla bla bla. */\n        int klen = sdslen(key);\n        if (klen > EVPOOL_CACHED_SDS_SIZE) {\n            pool[k].key = sdsdup(key);\n        } else {\n            memcpy(pool[k].cached,key,klen+1);\n            sdssetlen(pool[k].cached,klen);\n            pool[k].key = pool[k].cached;\n        }\n        pool[k].idle = idle;\n        pool[k].dbid = dbid;\n    }\n}\n\n/* ----------------------------------------------------------------------------\n * LFU (Least Frequently Used) implementation.\n\n * We have 24 total bits of space in each object in order to implement\n * an LFU (Least Frequently Used) eviction policy, since we re-use the\n * LRU field for this purpose.\n *\n * We split the 24 bits into two fields:\n *\n *          16 bits      8 bits\n *     +----------------+--------+\n *     + Last decr time | LOG_C  |\n *     +----------------+--------+\n *\n * LOG_C is a logarithmic counter that provides an indication of the access\n * frequency. However this field must also be decremented otherwise what used\n * to be a frequently accessed key in the past, will remain ranked like that\n * forever, while we want the algorithm to adapt to access pattern changes.\n *\n * So the remaining 16 bits are used in order to store the \"decrement time\",\n * a reduced-precision Unix time (we take 16 bits of the time converted\n * in minutes since we don't care about wrapping around) where the LOG_C\n * counter is halved if it has an high value, or just decremented if it\n * has a low value.\n *\n * New keys don't start at zero, in order to have the ability to collect\n * some accesses before being trashed away, so they start at COUNTER_INIT_VAL.\n * The logarithmic increment performed on LOG_C takes care of COUNTER_INIT_VAL\n * when incrementing the key, so that keys starting at COUNTER_INIT_VAL\n * (or having a smaller value) have a very high chance of being incremented\n * on access.\n *\n * During decrement, the value of the logarithmic counter is halved if\n * its current value is greater than two times the COUNTER_INIT_VAL, otherwise\n * it is just decremented by one.\n * --------------------------------------------------------------------------*/\n\n/* Return the current time in minutes, just taking the least significant\n * 16 bits. The returned time is suitable to be stored as LDT (last decrement\n * time) for the LFU implementation. */\nunsigned long LFUGetTimeInMinutes(void) {\n    return (server.unixtime/60) & 65535;\n}\n\n/* Given an object last access time, compute the minimum number of minutes\n * that elapsed since the last access. Handle overflow (ldt greater than\n * the current 16 bits minutes time) considering the time as wrapping\n * exactly once. */\nunsigned long LFUTimeElapsed(unsigned long ldt) {\n    unsigned long now = LFUGetTimeInMinutes();\n    if (now >= ldt) return now-ldt;\n    return 65535-ldt+now;\n}\n\n/* Logarithmically increment a counter. The greater is the current counter value\n * the less likely is that it gets really implemented. Saturate it at 255. */\nuint8_t LFULogIncr(uint8_t counter) {\n    if (counter == 255) return 255;\n    double r = (double)rand()/RAND_MAX;\n    double baseval = counter - LFU_INIT_VAL;\n    if (baseval < 0) baseval = 0;\n    double p = 1.0/(baseval*server.lfu_log_factor+1);\n    if (r < p) counter++;\n    return counter;\n}\n\n/* If the object decrement time is reached decrement the LFU counter but\n * do not update LFU fields of the object, we update the access time\n * and counter in an explicit way when the object is really accessed.\n * And we will times halve the counter according to the times of\n * elapsed time than server.lfu_decay_time.\n * Return the object frequency counter.\n *\n * This function is used in order to scan the dataset for the best object\n * to fit: as we check for the candidate, we incrementally decrement the\n * counter of the scanned objects if needed. */\nunsigned long LFUDecrAndReturn(robj *o) {\n    unsigned long ldt = o->lru >> 8;\n    unsigned long counter = o->lru & 255;\n    unsigned long num_periods = server.lfu_decay_time ? LFUTimeElapsed(ldt) / server.lfu_decay_time : 0;\n    if (num_periods)\n        counter = (num_periods > counter) ? 0 : counter - num_periods;\n    return counter;\n}\n\n/* ----------------------------------------------------------------------------\n * The external API for eviction: freeMemroyIfNeeded() is called by the\n * server when there is data to add in order to make space if needed.\n * --------------------------------------------------------------------------*/\n\n/* We don't want to count AOF buffers and slaves output buffers as\n * used memory: the eviction should use mostly data size. This function\n * returns the sum of AOF and slaves buffer. */\nsize_t freeMemoryGetNotCountedMemory(void) {\n    size_t overhead = 0;\n    int slaves = listLength(server.slaves);\n\n    if (slaves) {\n        listIter li;\n        listNode *ln;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = listNodeValue(ln);\n            overhead += getClientOutputBufferMemoryUsage(slave);\n        }\n    }\n    if (server.aof_state != AOF_OFF) {\n        overhead += sdsalloc(server.aof_buf)+aofRewriteBufferSize();\n    }\n    return overhead;\n}\n\n/* Get the memory status from the point of view of the maxmemory directive:\n * if the memory used is under the maxmemory setting then C_OK is returned.\n * Otherwise, if we are over the memory limit, the function returns\n * C_ERR.\n *\n * The function may return additional info via reference, only if the\n * pointers to the respective arguments is not NULL. Certain fields are\n * populated only when C_ERR is returned:\n *\n *  'total'     total amount of bytes used.\n *              (Populated both for C_ERR and C_OK)\n *\n *  'logical'   the amount of memory used minus the slaves/AOF buffers.\n *              (Populated when C_ERR is returned)\n *\n *  'tofree'    the amount of memory that should be released\n *              in order to return back into the memory limits.\n *              (Populated when C_ERR is returned)\n *\n *  'level'     this usually ranges from 0 to 1, and reports the amount of\n *              memory currently used. May be > 1 if we are over the memory\n *              limit.\n *              (Populated both for C_ERR and C_OK)\n */\nint getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level) {\n    size_t mem_reported, mem_used, mem_tofree;\n\n    /* Check if we are over the memory usage limit. If we are not, no need\n     * to subtract the slaves output buffers. We can just return ASAP. */\n    mem_reported = zmalloc_used_memory();\n    if (total) *total = mem_reported;\n\n    /* We may return ASAP if there is no need to compute the level. */\n    int return_ok_asap = !server.maxmemory || mem_reported <= server.maxmemory;\n    if (return_ok_asap && !level) return C_OK;\n\n    /* Remove the size of slaves output buffers and AOF buffer from the\n     * count of used memory. */\n    mem_used = mem_reported;\n    size_t overhead = freeMemoryGetNotCountedMemory();\n    mem_used = (mem_used > overhead) ? mem_used-overhead : 0;\n\n    /* Compute the ratio of memory usage. */\n    if (level) {\n        if (!server.maxmemory) {\n            *level = 0;\n        } else {\n            *level = (float)mem_used / (float)server.maxmemory;\n        }\n    }\n\n    if (return_ok_asap) return C_OK;\n\n    /* Check if we are still over the memory limit. */\n    if (mem_used <= server.maxmemory) return C_OK;\n\n    /* Compute how much memory we need to free. */\n    mem_tofree = mem_used - server.maxmemory;\n\n    if (logical) *logical = mem_used;\n    if (tofree) *tofree = mem_tofree;\n\n    return C_ERR;\n}\n\n/* This function is periodically called to see if there is memory to free\n * according to the current \"maxmemory\" settings. In case we are over the\n * memory limit, the function will try to free some memory to return back\n * under the limit.\n *\n * The function returns C_OK if we are under the memory limit or if we\n * were over the limit, but the attempt to free memory was successful.\n * Otehrwise if we are over the memory limit, but not enough memory\n * was freed to return back under the limit, the function returns C_ERR. */\nint freeMemoryIfNeeded(void) {\n    int keys_freed = 0;\n    /* By default replicas should ignore maxmemory\n     * and just be masters exact copies. */\n    if (server.masterhost && server.repl_slave_ignore_maxmemory) return C_OK;\n\n    size_t mem_reported, mem_tofree, mem_freed;\n    mstime_t latency, eviction_latency, lazyfree_latency;\n    long long delta;\n    int slaves = listLength(server.slaves);\n    int result = C_ERR;\n\n    /* When clients are paused the dataset should be static not just from the\n     * POV of clients not being able to write, but also from the POV of\n     * expires and evictions of keys not being performed. */\n    if (clientsArePaused()) return C_OK;\n    if (getMaxmemoryState(&mem_reported,NULL,&mem_tofree,NULL) == C_OK)\n        return C_OK;\n\n    mem_freed = 0;\n\n    latencyStartMonitor(latency);\n    if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)\n        goto cant_free; /* We need to free memory, but policy forbids. */\n\n    while (mem_freed < mem_tofree) {\n        int j, k, i;\n        static unsigned int next_db = 0;\n        sds bestkey = NULL;\n        int bestdbid;\n        redisDb *db;\n        dict *dict;\n        dictEntry *de;\n\n        if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||\n            server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)\n        {\n            struct evictionPoolEntry *pool = EvictionPoolLRU;\n\n            while(bestkey == NULL) {\n                unsigned long total_keys = 0, keys;\n\n                /* We don't want to make local-db choices when expiring keys,\n                 * so to start populate the eviction pool sampling keys from\n                 * every DB. */\n                for (i = 0; i < server.dbnum; i++) {\n                    db = server.db+i;\n                    dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?\n                            db->dict : db->expires;\n                    if ((keys = dictSize(dict)) != 0) {\n                        evictionPoolPopulate(i, dict, db->dict, pool);\n                        total_keys += keys;\n                    }\n                }\n                if (!total_keys) break; /* No keys to evict. */\n\n                /* Go backward from best to worst element to evict. */\n                for (k = EVPOOL_SIZE-1; k >= 0; k--) {\n                    if (pool[k].key == NULL) continue;\n                    bestdbid = pool[k].dbid;\n\n                    if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {\n                        de = dictFind(server.db[pool[k].dbid].dict,\n                            pool[k].key);\n                    } else {\n                        de = dictFind(server.db[pool[k].dbid].expires,\n                            pool[k].key);\n                    }\n\n                    /* Remove the entry from the pool. */\n                    if (pool[k].key != pool[k].cached)\n                        sdsfree(pool[k].key);\n                    pool[k].key = NULL;\n                    pool[k].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... Iterate again. */\n                    }\n                }\n            }\n        }\n\n        /* volatile-random and allkeys-random policy */\n        else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||\n                 server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)\n        {\n            /* When evicting a random key, we try to evict a key for\n             * each DB, so we use the static 'next_db' variable to\n             * incrementally visit all DBs. */\n            for (i = 0; i < server.dbnum; i++) {\n                j = (++next_db) % server.dbnum;\n                db = server.db+j;\n                dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) ?\n                        db->dict : db->expires;\n                if (dictSize(dict) != 0) {\n                    de = dictGetRandomKey(dict);\n                    bestkey = dictGetKey(de);\n                    bestdbid = j;\n                    break;\n                }\n            }\n        }\n\n        /* Finally remove the selected key. */\n        if (bestkey) {\n            db = server.db+bestdbid;\n            robj *keyobj = createStringObject(bestkey,sdslen(bestkey));\n            propagateExpire(db,keyobj,server.lazyfree_lazy_eviction);\n            /* We compute the amount of memory freed by db*Delete() 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            delta = (long long) zmalloc_used_memory();\n            latencyStartMonitor(eviction_latency);\n            if (server.lazyfree_lazy_eviction)\n                dbAsyncDelete(db,keyobj);\n            else\n                dbSyncDelete(db,keyobj);\n            signalModifiedKey(NULL,db,keyobj);\n            latencyEndMonitor(eviction_latency);\n            latencyAddSampleIfNeeded(\"eviction-del\",eviction_latency);\n            delta -= (long long) zmalloc_used_memory();\n            mem_freed += delta;\n            server.stat_evictedkeys++;\n            notifyKeyspaceEvent(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            /* Normally our stop condition is the ability to release\n             * a fixed, pre-computed amount of memory. However when we\n             * are deleting objects in another thread, it's better to\n             * check, from time to time, if we already reached our target\n             * memory, since the \"mem_freed\" amount is computed only\n             * across the dbAsyncDelete() call, while the thread can\n             * release the memory all the time. */\n            if (server.lazyfree_lazy_eviction && !(keys_freed % 16)) {\n                if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {\n                    /* Let's satisfy our stop condition. */\n                    mem_freed = mem_tofree;\n                }\n            }\n        } else {\n            goto cant_free; /* nothing to free... */\n        }\n    }\n    result = C_OK;\n\ncant_free:\n    /* We are here if we are not able to reclaim memory. There is only one\n     * last thing we can try: check if the lazyfree thread has jobs in queue\n     * and wait... */\n    if (result != C_OK) {\n        latencyStartMonitor(lazyfree_latency);\n        while(bioPendingJobsOfType(BIO_LAZY_FREE)) {\n            if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {\n                result = C_OK;\n                break;\n            }\n            usleep(1000);\n        }\n        latencyEndMonitor(lazyfree_latency);\n        latencyAddSampleIfNeeded(\"eviction-lazyfree\",lazyfree_latency);\n    }\n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"eviction-cycle\",latency);\n    return result;\n}\n\n/* This is a wrapper for freeMemoryIfNeeded() that only really calls the\n * function if right now there are the conditions to do so safely:\n *\n * - There must be no script in timeout condition.\n * - Nor we are loading data right now.\n *\n */\nint freeMemoryIfNeededAndSafe(void) {\n    if (server.lua_timedout || server.loading) return C_OK;\n    return freeMemoryIfNeeded();\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/expire.c",
    "content": "/* Implementation of EXPIRE (keys with fixed time to live).\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2016, 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 \"server.h\"\n\n/*-----------------------------------------------------------------------------\n * Incremental collection of expired keys.\n *\n * When keys are accessed they are expired on-access. However we need a\n * mechanism in order to ensure keys are eventually removed when expired even\n * if no access is performed on them.\n *----------------------------------------------------------------------------*/\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 * 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 * 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. */\nint activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {\n    long long t = dictGetSignedIntegerVal(de);\n    if (now > t) {\n        sds key = dictGetKey(de);\n        robj *keyobj = createStringObject(key,sdslen(key));\n\n        propagateExpire(db,keyobj,server.lazyfree_lazy_expire);\n        if (server.lazyfree_lazy_expire)\n            dbAsyncDelete(db,keyobj);\n        else\n            dbSyncDelete(db,keyobj);\n        notifyKeyspaceEvent(NOTIFY_EXPIRED,\n            \"expired\",keyobj,db->id);\n        trackingInvalidateKey(NULL,keyobj);\n        decrRefCount(keyobj);\n        server.stat_expiredkeys++;\n        return 1;\n    } else {\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 * Every expire cycle tests multiple databases: the next call will start\n * again from the next db, with the exception of exists for time limit: in that\n * case we restart again from the last database we were processing. Anyway\n * no more than CRON_DBS_PER_CALL databases are tested at every iteration.\n *\n * The function can perform more or less work, depending on the \"type\"\n * argument. It can execute a \"fast cycle\" or a \"slow cycle\". The slow\n * cycle is the main way we collect expired cycles: this happens with\n * the \"server.hz\" frequency (usually 10 hertz).\n *\n * However the slow cycle can exit for timeout, since it used too much time.\n * For this reason the function is also invoked to perform a fast cycle\n * at every event loop cycle, in the beforeSleep() function. The fast cycle\n * will try to perform less work, but will do it much more often.\n *\n * The following are the details of the two expire cycles and their stop\n * conditions:\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 * The cycle will also refuse to run at all if the latest slow cycle did not\n * terminate because of a time limit condition.\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 ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC define. In the\n * fast cycle, the check of every database is interrupted once the number\n * of already expired keys in the database is estimated to be lower than\n * a given percentage, in order to avoid doing too much work to gain too\n * little memory.\n *\n * The configured expire \"effort\" will modify the baseline parameters in\n * order to do more work in both the fast and slow expire cycles.\n */\n\n#define ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP 20 /* Keys for each DB loop. */\n#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds. */\n#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* Max % of CPU to use. */\n#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10 /* % of stale keys after which\n                                                   we do extra efforts. */\n\nvoid activeExpireCycle(int type) {\n    /* Adjust the running parameters according to the configured expire\n     * effort. The default effort is 1, and the maximum configurable effort\n     * is 10. */\n    unsigned long\n    effort = server.active_expire_effort-1, /* Rescale from 0 to 9. */\n    config_keys_per_loop = ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP +\n                           ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP/4*effort,\n    config_cycle_fast_duration = ACTIVE_EXPIRE_CYCLE_FAST_DURATION +\n                                 ACTIVE_EXPIRE_CYCLE_FAST_DURATION/4*effort,\n    config_cycle_slow_time_perc = ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC +\n                                  2*effort,\n    config_cycle_acceptable_stale = ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE-\n                                    effort;\n\n    /* This function has some global state in order to continue the work\n     * incrementally across calls. */\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    int j, iteration = 0;\n    int dbs_per_call = CRON_DBS_PER_CALL;\n    long long start = ustime(), timelimit, elapsed;\n\n    /* When clients are paused the dataset should be static not just from the\n     * POV of clients not being able to write, but also from the POV of\n     * expires and evictions of keys not being performed. */\n    if (clientsArePaused()) return;\n\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST) {\n        /* Don't start a fast cycle if the previous cycle did not exit\n         * for time limit, unless the percentage of estimated stale keys is\n         * too high. Also never repeat a fast cycle for the same period\n         * as the fast cycle total duration itself. */\n        if (!timelimit_exit &&\n            server.stat_expired_stale_perc < config_cycle_acceptable_stale)\n            return;\n\n        if (start < last_fast_cycle + (long long)config_cycle_fast_duration*2)\n            return;\n\n        last_fast_cycle = start;\n    }\n\n    /* We usually should test CRON_DBS_PER_CALL per iteration, with\n     * two exceptions:\n     *\n     * 1) Don't test more DBs than we have.\n     * 2) If last time we hit the time limit, we want to scan all DBs\n     * in this iteration, as there is work to do in some DB and we don't want\n     * expired keys to use memory for too much time. */\n    if (dbs_per_call > server.dbnum || timelimit_exit)\n        dbs_per_call = server.dbnum;\n\n    /* We can use at max 'config_cycle_slow_time_perc' percentage of CPU\n     * time per iteration. Since this function gets called with a frequency of\n     * server.hz times per second, the following is the max amount of\n     * microseconds we can spend in this function. */\n    timelimit = config_cycle_slow_time_perc*1000000/server.hz/100;\n    timelimit_exit = 0;\n    if (timelimit <= 0) timelimit = 1;\n\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST)\n        timelimit = config_cycle_fast_duration; /* in microseconds. */\n\n    /* Accumulate some global stats as we expire keys, to have some idea\n     * about the number of keys that are already logically expired, but still\n     * existing inside the database. */\n    long total_sampled = 0;\n    long total_expired = 0;\n\n    for (j = 0; j < dbs_per_call && timelimit_exit == 0; j++) {\n        /* Expired and checked in a single loop. */\n        unsigned long expired, sampled;\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        current_db++;\n\n        /* Continue to expire if at the end of the cycle there are still\n         * a big percentage of keys to expire, compared to the number of keys\n         * we scanned. The percentage, stored in config_cycle_acceptable_stale\n         * is not fixed, but depends on the Redis configured \"expire effort\". */\n        do {\n            unsigned long num, slots;\n            long long now, ttl_sum;\n            int ttl_samples;\n            iteration++;\n\n            /* If there is nothing to expire try next DB ASAP. */\n            if ((num = dictSize(db->expires)) == 0) {\n                db->avg_ttl = 0;\n                break;\n            }\n            slots = dictSlots(db->expires);\n            now = mstime();\n\n            /* When there are less than 1% filled slots, sampling the key\n             * space is expensive, so stop here waiting for better times...\n             * The dictionary will be resized asap. */\n            if (num && slots > DICT_HT_INITIAL_SIZE &&\n                (num*100/slots < 1)) break;\n\n            /* The main collection cycle. Sample random keys among keys\n             * with an expire set, checking for expired ones. */\n            expired = 0;\n            sampled = 0;\n            ttl_sum = 0;\n            ttl_samples = 0;\n\n            if (num > config_keys_per_loop)\n                num = config_keys_per_loop;\n\n            /* Here we access the low level representation of the hash table\n             * for speed concerns: this makes this code coupled with dict.c,\n             * but it hardly changed in ten years.\n             *\n             * Note that certain places of the hash table may be empty,\n             * so we want also a stop condition about the number of\n             * buckets that we scanned. However scanning for free buckets\n             * is very fast: we are in the cache line scanning a sequential\n             * array of NULL pointers, so we can scan a lot more buckets\n             * than keys in the same time. */\n            long max_buckets = num*20;\n            long checked_buckets = 0;\n\n            while (sampled < num && checked_buckets < max_buckets) {\n                for (int table = 0; table < 2; table++) {\n                    if (table == 1 && !dictIsRehashing(db->expires)) break;\n\n                    unsigned long idx = db->expires_cursor;\n                    idx &= db->expires->ht[table].sizemask;\n                    dictEntry *de = db->expires->ht[table].table[idx];\n                    long long ttl;\n\n                    /* Scan the current bucket of the current table. */\n                    checked_buckets++;\n                    while(de) {\n                        /* Get the next entry now since this entry may get\n                         * deleted. */\n                        dictEntry *e = de;\n                        de = de->next;\n\n                        ttl = dictGetSignedIntegerVal(e)-now;\n                        if (activeExpireCycleTryExpire(db,e,now)) expired++;\n                        if (ttl > 0) {\n                            /* We want the average TTL of keys yet\n                             * not expired. */\n                            ttl_sum += ttl;\n                            ttl_samples++;\n                        }\n                        sampled++;\n                    }\n                }\n                db->expires_cursor++;\n            }\n            total_expired += expired;\n            total_sampled += sampled;\n\n            /* Update the average TTL stats for this database. */\n            if (ttl_samples) {\n                long long avg_ttl = ttl_sum/ttl_samples;\n\n                /* Do a simple running average with a few samples.\n                 * We just use the current estimate with a weight of 2%\n                 * and the previous estimate with a weight of 98%. */\n                if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;\n                db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);\n            }\n\n            /* We can't block forever here even if there are many keys to\n             * expire. So after a given amount of milliseconds return to the\n             * caller waiting for the other active expire cycle. */\n            if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */\n                elapsed = ustime()-start;\n                if (elapsed > timelimit) {\n                    timelimit_exit = 1;\n                    server.stat_expired_time_cap_reached_count++;\n                    break;\n                }\n            }\n            /* We don't repeat the cycle for the current database if there are\n             * an acceptable amount of stale keys (logically expired but yet\n             * not reclaimed). */\n        } while (sampled == 0 ||\n                 (expired*100/sampled) > config_cycle_acceptable_stale);\n    }\n\n    elapsed = ustime()-start;\n    server.stat_expire_cycle_time_used += elapsed;\n    latencyAddSampleIfNeeded(\"expire-cycle\",elapsed/1000);\n\n    /* Update our estimate of keys existing but yet to be expired.\n     * Running average with this sample accounting for 5%. */\n    double current_perc;\n    if (total_sampled) {\n        current_perc = (double)total_expired/total_sampled;\n    } else\n        current_perc = 0;\n    server.stat_expired_stale_perc = (current_perc*0.05)+\n                                     (server.stat_expired_stale_perc*0.95);\n}\n\n/*-----------------------------------------------------------------------------\n * Expires of keys created in writable slaves\n *\n * Normally slaves do not process expires: they wait the masters to synthesize\n * DEL operations in order to retain consistency. However writable slaves are\n * an exception: if a key is created in the slave and an expire is assigned\n * to it, we need a way to expire such a key, since the master does not know\n * anything about such a key.\n *\n * In order to do so, we track keys created in the slave side with an expire\n * set, and call the expireSlaveKeys() function from time to time in order to\n * reclaim the keys if they already expired.\n *\n * Note that the use case we are trying to cover here, is a popular one where\n * slaves are put in writable mode in order to compute slow operations in\n * the slave side that are mostly useful to actually read data in a more\n * processed way. Think at sets intersections in a tmp key, with an expire so\n * that it is also used as a cache to avoid intersecting every time.\n *\n * This implementation is currently not perfect but a lot better than leaking\n * the keys as implemented in 3.2.\n *----------------------------------------------------------------------------*/\n\n/* The dictionary where we remember key names and database ID of keys we may\n * want to expire from the slave. Since this function is not often used we\n * don't even care to initialize the database at startup. We'll do it once\n * the feature is used the first time, that is, when rememberSlaveKeyWithExpire()\n * is called.\n *\n * The dictionary has an SDS string representing the key as the hash table\n * key, while the value is a 64 bit unsigned integer with the bits corresponding\n * to the DB where the keys may exist set to 1. Currently the keys created\n * with a DB id > 63 are not expired, but a trivial fix is to set the bitmap\n * to the max 64 bit unsigned value when we know there is a key with a DB\n * ID greater than 63, and check all the configured DBs in such a case. */\ndict *slaveKeysWithExpire = NULL;\n\n/* Check the set of keys created by the master with an expire set in order to\n * check if they should be evicted. */\nvoid expireSlaveKeys(void) {\n    if (slaveKeysWithExpire == NULL ||\n        dictSize(slaveKeysWithExpire) == 0) return;\n\n    int cycles = 0, noexpire = 0;\n    mstime_t start = mstime();\n    while(1) {\n        dictEntry *de = dictGetRandomKey(slaveKeysWithExpire);\n        sds keyname = dictGetKey(de);\n        uint64_t dbids = dictGetUnsignedIntegerVal(de);\n        uint64_t new_dbids = 0;\n\n        /* Check the key against every database corresponding to the\n         * bits set in the value bitmap. */\n        int dbid = 0;\n        while(dbids && dbid < server.dbnum) {\n            if ((dbids & 1) != 0) {\n                redisDb *db = server.db+dbid;\n                dictEntry *expire = dictFind(db->expires,keyname);\n                int expired = 0;\n\n                if (expire &&\n                    activeExpireCycleTryExpire(server.db+dbid,expire,start))\n                {\n                    expired = 1;\n                }\n\n                /* If the key was not expired in this DB, we need to set the\n                 * corresponding bit in the new bitmap we set as value.\n                 * At the end of the loop if the bitmap is zero, it means we\n                 * no longer need to keep track of this key. */\n                if (expire && !expired) {\n                    noexpire++;\n                    new_dbids |= (uint64_t)1 << dbid;\n                }\n            }\n            dbid++;\n            dbids >>= 1;\n        }\n\n        /* Set the new bitmap as value of the key, in the dictionary\n         * of keys with an expire set directly in the writable slave. Otherwise\n         * if the bitmap is zero, we no longer need to keep track of it. */\n        if (new_dbids)\n            dictSetUnsignedIntegerVal(de,new_dbids);\n        else\n            dictDelete(slaveKeysWithExpire,keyname);\n\n        /* Stop conditions: found 3 keys we cna't expire in a row or\n         * time limit was reached. */\n        cycles++;\n        if (noexpire > 3) break;\n        if ((cycles % 64) == 0 && mstime()-start > 1) break;\n        if (dictSize(slaveKeysWithExpire) == 0) break;\n    }\n}\n\n/* Track keys that received an EXPIRE or similar command in the context\n * of a writable slave. */\nvoid rememberSlaveKeyWithExpire(redisDb *db, robj *key) {\n    if (slaveKeysWithExpire == NULL) {\n        static dictType dt = {\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        slaveKeysWithExpire = dictCreate(&dt,NULL);\n    }\n    if (db->id > 63) return;\n\n    dictEntry *de = dictAddOrFind(slaveKeysWithExpire,key->ptr);\n    /* If the entry was just created, set it to a copy of the SDS string\n     * representing the key: we don't want to need to take those keys\n     * in sync with the main DB. The keys will be removed by expireSlaveKeys()\n     * as it scans to find keys to remove. */\n    if (de->key == key->ptr) {\n        de->key = sdsdup(key->ptr);\n        dictSetUnsignedIntegerVal(de,0);\n    }\n\n    uint64_t dbids = dictGetUnsignedIntegerVal(de);\n    dbids |= (uint64_t)1 << db->id;\n    dictSetUnsignedIntegerVal(de,dbids);\n}\n\n/* Return the number of keys we are tracking. */\nsize_t getSlaveKeyWithExpireCount(void) {\n    if (slaveKeysWithExpire == NULL) return 0;\n    return dictSize(slaveKeysWithExpire);\n}\n\n/* Remove the keys in the hash table. We need to do that when data is\n * flushed from the server. We may receive new keys from the master with\n * the same name/db and it is no longer a good idea to expire them.\n *\n * Note: technically we should handle the case of a single DB being flushed\n * but it is not worth it since anyway race conditions using the same set\n * of key names in a wriatable slave and in its master will lead to\n * inconsistencies. This is just a best-effort thing we do. */\nvoid flushSlaveKeysWithExpireList(void) {\n    if (slaveKeysWithExpire) {\n        dictRelease(slaveKeysWithExpire);\n        slaveKeysWithExpire = NULL;\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Expires Commands\n *----------------------------------------------------------------------------*/\n\n/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT\n * and PEXPIREAT. Because the commad second argument may be relative or absolute\n * the \"basetime\" argument is used to signal what the base time is (either 0\n * for *AT variants of the command, or the current time for relative expires).\n *\n * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for\n * the argv[2] parameter. The basetime is always specified in milliseconds. */\nvoid expireGenericCommand(client *c, long long basetime, int unit) {\n    robj *key = c->argv[1], *param = c->argv[2];\n    long long when; /* unix time in milliseconds when the key will expire. */\n\n    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != C_OK)\n        return;\n\n    if (unit == UNIT_SECONDS) when *= 1000;\n    when += basetime;\n\n    /* No key, return zero. */\n    if (lookupKeyWrite(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     * Instead we take the other branch of the IF statement setting an expire\n     * (possibly in the past) and wait for an explicit DEL from the master. */\n    if (when <= mstime() && !server.loading && !server.masterhost) {\n        robj *aux;\n\n        int deleted = server.lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :\n                                                    dbSyncDelete(c->db,key);\n        serverAssertWithInfo(c,key,deleted);\n        server.dirty++;\n\n        /* Replicate/AOF this as an explicit DEL or UNLINK. */\n        aux = server.lazyfree_lazy_expire ? shared.unlink : shared.del;\n        rewriteClientCommandVector(c,2,aux,key);\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n        addReply(c, shared.cone);\n        return;\n    } else {\n        setExpire(c,c->db,key,when);\n        addReply(c,shared.cone);\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"expire\",key,c->db->id);\n        server.dirty++;\n        return;\n    }\n}\n\n/* EXPIRE key seconds */\nvoid expireCommand(client *c) {\n    expireGenericCommand(c,mstime(),UNIT_SECONDS);\n}\n\n/* EXPIREAT key time */\nvoid expireatCommand(client *c) {\n    expireGenericCommand(c,0,UNIT_SECONDS);\n}\n\n/* PEXPIRE key milliseconds */\nvoid pexpireCommand(client *c) {\n    expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);\n}\n\n/* PEXPIREAT key ms_time */\nvoid pexpireatCommand(client *c) {\n    expireGenericCommand(c,0,UNIT_MILLISECONDS);\n}\n\n/* Implements TTL and PTTL */\nvoid ttlGenericCommand(client *c, int output_ms) {\n    long long expire, ttl = -1;\n\n    /* If the key does not exist at all, return -2 */\n    if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == NULL) {\n        addReplyLongLong(c,-2);\n        return;\n    }\n    /* The key exists. Return -1 if it has no expire, or the actual\n     * TTL value otherwise. */\n    expire = getExpire(c->db,c->argv[1]);\n    if (expire != -1) {\n        ttl = expire-mstime();\n        if (ttl < 0) ttl = 0;\n    }\n    if (ttl == -1) {\n        addReplyLongLong(c,-1);\n    } else {\n        addReplyLongLong(c,output_ms ? ttl : ((ttl+500)/1000));\n    }\n}\n\n/* TTL key */\nvoid ttlCommand(client *c) {\n    ttlGenericCommand(c, 0);\n}\n\n/* PTTL key */\nvoid pttlCommand(client *c) {\n    ttlGenericCommand(c, 1);\n}\n\n/* PERSIST key */\nvoid persistCommand(client *c) {\n    if (lookupKeyWrite(c->db,c->argv[1])) {\n        if (removeExpire(c->db,c->argv[1])) {\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"persist\",c->argv[1],c->db->id);\n            addReply(c,shared.cone);\n            server.dirty++;\n        } else {\n            addReply(c,shared.czero);\n        }\n    } else {\n        addReply(c,shared.czero);\n    }\n}\n\n/* TOUCH key1 [key2 key3 ... keyN] */\nvoid touchCommand(client *c) {\n    int touched = 0;\n    for (int j = 1; j < c->argc; j++)\n        if (lookupKeyRead(c->db,c->argv[j]) != NULL) touched++;\n    addReplyLongLong(c,touched);\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#define _DEFAULT_SOURCE\n#endif\n\n#if defined(_AIX)\n#define _ALL_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#if defined(__sun)\n#define _POSIX_C_SOURCE 199506L\n#endif\n\n#define _LARGEFILE_SOURCE\n#define _FILE_OFFSET_BITS 64\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/geo.c",
    "content": "/*\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@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#include \"geo.h\"\n#include \"geohash_helper.h\"\n#include \"debugmacro.h\"\n\n/* Things exported from t_zset.c only for geo.c, since it is the only other\n * part of Redis that requires close zset introspection. */\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);\nint zslValueLteMax(double value, zrangespec *spec);\n\n/* ====================================================================\n * This file implements the following commands:\n *\n *   - geoadd - add coordinates for value to geoset\n *   - georadius - search radius by coordinates in geoset\n *   - georadiusbymember - search radius based on geoset member position\n * ==================================================================== */\n\n/* ====================================================================\n * geoArray implementation\n * ==================================================================== */\n\n/* Create a new array of geoPoints. */\ngeoArray *geoArrayCreate(void) {\n    geoArray *ga = zmalloc(sizeof(*ga));\n    /* It gets allocated on first geoArrayAppend() call. */\n    ga->array = NULL;\n    ga->buckets = 0;\n    ga->used = 0;\n    return ga;\n}\n\n/* Add a new entry and return its pointer so that the caller can populate\n * it with data. */\ngeoPoint *geoArrayAppend(geoArray *ga) {\n    if (ga->used == ga->buckets) {\n        ga->buckets = (ga->buckets == 0) ? 8 : ga->buckets*2;\n        ga->array = zrealloc(ga->array,sizeof(geoPoint)*ga->buckets);\n    }\n    geoPoint *gp = ga->array+ga->used;\n    ga->used++;\n    return gp;\n}\n\n/* Destroy a geoArray created with geoArrayCreate(). */\nvoid geoArrayFree(geoArray *ga) {\n    size_t i;\n    for (i = 0; i < ga->used; i++) sdsfree(ga->array[i].member);\n    zfree(ga->array);\n    zfree(ga);\n}\n\n/* ====================================================================\n * Helpers\n * ==================================================================== */\nint decodeGeohash(double bits, double *xy) {\n    GeoHashBits hash = { .bits = (uint64_t)bits, .step = GEO_STEP_MAX };\n    return geohashDecodeToLongLatWGS84(hash, xy);\n}\n\n/* Input Argument Helper */\n/* Take a pointer to the latitude arg then use the next arg for longitude.\n * On parse error C_ERR is returned, otherwise C_OK. */\nint extractLongLatOrReply(client *c, robj **argv, double *xy) {\n    int i;\n    for (i = 0; i < 2; i++) {\n        if (getDoubleFromObjectOrReply(c, argv[i], xy + i, NULL) !=\n            C_OK) {\n            return C_ERR;\n        }\n    }\n    if (xy[0] < GEO_LONG_MIN || xy[0] > GEO_LONG_MAX ||\n        xy[1] < GEO_LAT_MIN  || xy[1] > GEO_LAT_MAX) {\n        addReplySds(c, sdscatprintf(sdsempty(),\n            \"-ERR invalid longitude,latitude pair %f,%f\\r\\n\",xy[0],xy[1]));\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* Input Argument Helper */\n/* Decode lat/long from a zset member's score.\n * Returns C_OK on successful decoding, otherwise C_ERR is returned. */\nint longLatFromMember(robj *zobj, robj *member, double *xy) {\n    double score = 0;\n\n    if (zsetScore(zobj, member->ptr, &score) == C_ERR) return C_ERR;\n    if (!decodeGeohash(score, xy)) return C_ERR;\n    return C_OK;\n}\n\n/* Check that the unit argument matches one of the known units, and returns\n * the conversion factor to meters (you need to divide meters by the conversion\n * factor to convert to the right unit).\n *\n * If the unit is not valid, an error is reported to the client, and a value\n * less than zero is returned. */\ndouble extractUnitOrReply(client *c, robj *unit) {\n    char *u = unit->ptr;\n\n    if (!strcmp(u, \"m\")) {\n        return 1;\n    } else if (!strcmp(u, \"km\")) {\n        return 1000;\n    } else if (!strcmp(u, \"ft\")) {\n        return 0.3048;\n    } else if (!strcmp(u, \"mi\")) {\n        return 1609.34;\n    } else {\n        addReplyError(c,\n            \"unsupported unit provided. please use m, km, ft, mi\");\n        return -1;\n    }\n}\n\n/* Input Argument Helper.\n * Extract the dinstance from the specified two arguments starting at 'argv'\n * that shouldbe in the form: <number> <unit> and return the dinstance in the\n * specified unit on success. *conversions is populated with the coefficient\n * to use in order to convert meters to the unit.\n *\n * On error a value less than zero is returned. */\ndouble extractDistanceOrReply(client *c, robj **argv,\n                                     double *conversion) {\n    double distance;\n    if (getDoubleFromObjectOrReply(c, argv[0], &distance,\n                                   \"need numeric radius\") != C_OK) {\n        return -1;\n    }\n\n    if (distance < 0) {\n        addReplyError(c,\"radius cannot be negative\");\n        return -1;\n    }\n\n    double to_meters = extractUnitOrReply(c,argv[1]);\n    if (to_meters < 0) {\n        return -1;\n    }\n\n    if (conversion) *conversion = to_meters;\n    return distance * to_meters;\n}\n\n/* The default addReplyDouble has too much accuracy.  We use this\n * for returning location distances. \"5.2145 meters away\" is nicer\n * than \"5.2144992818115 meters away.\" We provide 4 digits after the dot\n * so that the returned value is decently accurate even when the unit is\n * the kilometer. */\nvoid addReplyDoubleDistance(client *c, double d) {\n    char dbuf[128];\n    int dlen = snprintf(dbuf, sizeof(dbuf), \"%.4f\", d);\n    addReplyBulkCBuffer(c, dbuf, dlen);\n}\n\n/* Helper function for geoGetPointsInRange(): given a sorted set score\n * representing a point, and another point (the center of our search) and\n * a radius, appends this entry as a geoPoint into the specified geoArray\n * only if the point is within the search area.\n *\n * returns C_OK if the point is included, or REIDS_ERR if it is outside. */\nint geoAppendIfWithinRadius(geoArray *ga, double lon, double lat, double radius, double score, sds member) {\n    double distance, xy[2];\n\n    if (!decodeGeohash(score,xy)) return C_ERR; /* Can't decode. */\n    /* Note that geohashGetDistanceIfInRadiusWGS84() takes arguments in\n     * reverse order: longitude first, latitude later. */\n    if (!geohashGetDistanceIfInRadiusWGS84(lon,lat, xy[0], xy[1],\n                                           radius, &distance))\n    {\n        return C_ERR;\n    }\n\n    /* Append the new element. */\n    geoPoint *gp = geoArrayAppend(ga);\n    gp->longitude = xy[0];\n    gp->latitude = xy[1];\n    gp->dist = distance;\n    gp->member = member;\n    gp->score = score;\n    return C_OK;\n}\n\n/* Query a Redis sorted set to extract all the elements between 'min' and\n * 'max', appending them into the array of geoPoint structures 'gparray'.\n * The command returns the number of elements added to the array.\n *\n * Elements which are farest than 'radius' from the specified 'x' and 'y'\n * coordinates are not included.\n *\n * The ability of this function to append to an existing set of points is\n * important for good performances because querying by radius is performed\n * using multiple queries to the sorted set, that we later need to sort\n * via qsort. Similarly we need to be able to reject points outside the search\n * radius area ASAP in order to allocate and process more points than needed. */\nint geoGetPointsInRange(robj *zobj, double min, double max, double lon, double lat, double radius, geoArray *ga) {\n    /* minex 0 = include min in range; maxex 1 = exclude max in range */\n    /* That's: min <= val < max */\n    zrangespec range = { .min = min, .max = max, .minex = 0, .maxex = 1 };\n    size_t origincount = ga->used;\n    sds member;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr = NULL;\n        unsigned int vlen = 0;\n        long long vlong = 0;\n        double score = 0;\n\n        if ((eptr = zzlFirstInRange(zl, &range)) == NULL) {\n            /* Nothing exists starting at our min.  No results. */\n            return 0;\n        }\n\n        sptr = ziplistNext(zl, eptr);\n        while (eptr) {\n            score = zzlGetScore(sptr);\n\n            /* If we fell out of range, break. */\n            if (!zslValueLteMax(score, &range))\n                break;\n\n            /* We know the element exists. ziplistGet should always succeed */\n            ziplistGet(eptr, &vstr, &vlen, &vlong);\n            member = (vstr == NULL) ? sdsfromlonglong(vlong) :\n                                      sdsnewlen(vstr,vlen);\n            if (geoAppendIfWithinRadius(ga,lon,lat,radius,score,member)\n                == C_ERR) sdsfree(member);\n            zzlNext(zl, &eptr, &sptr);\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        if ((ln = zslFirstInRange(zsl, &range)) == NULL) {\n            /* Nothing exists starting at our min.  No results. */\n            return 0;\n        }\n\n        while (ln) {\n            sds ele = ln->ele;\n            /* Abort when the node is no longer in range. */\n            if (!zslValueLteMax(ln->score, &range))\n                break;\n\n            ele = sdsdup(ele);\n            if (geoAppendIfWithinRadius(ga,lon,lat,radius,ln->score,ele)\n                == C_ERR) sdsfree(ele);\n            ln = ln->level[0].forward;\n        }\n    }\n    return ga->used - origincount;\n}\n\n/* Compute the sorted set scores min (inclusive), max (exclusive) we should\n * query in order to retrieve all the elements inside the specified area\n * 'hash'. The two scores are returned by reference in *min and *max. */\nvoid scoresOfGeoHashBox(GeoHashBits hash, GeoHashFix52Bits *min, GeoHashFix52Bits *max) {\n    /* We want to compute the sorted set scores that will include all the\n     * elements inside the specified Geohash 'hash', which has as many\n     * bits as specified by hash.step * 2.\n     *\n     * So if step is, for example, 3, and the hash value in binary\n     * is 101010, since our score is 52 bits we want every element which\n     * is in binary: 101010?????????????????????????????????????????????\n     * Where ? can be 0 or 1.\n     *\n     * To get the min score we just use the initial hash value left\n     * shifted enough to get the 52 bit value. Later we increment the\n     * 6 bit prefis (see the hash.bits++ statement), and get the new\n     * prefix: 101011, which we align again to 52 bits to get the maximum\n     * value (which is excluded from the search). So we get everything\n     * between the two following scores (represented in binary):\n     *\n     * 1010100000000000000000000000000000000000000000000000 (included)\n     * and\n     * 1010110000000000000000000000000000000000000000000000 (excluded).\n     */\n    *min = geohashAlign52Bits(hash);\n    hash.bits++;\n    *max = geohashAlign52Bits(hash);\n}\n\n/* Obtain all members between the min/max of this geohash bounding box.\n * Populate a geoArray of GeoPoints by calling geoGetPointsInRange().\n * Return the number of points added to the array. */\nint membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon, double lat, double radius) {\n    GeoHashFix52Bits min, max;\n\n    scoresOfGeoHashBox(hash,&min,&max);\n    return geoGetPointsInRange(zobj, min, max, lon, lat, radius, ga);\n}\n\n/* Search all eight neighbors + self geohash box */\nint membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) {\n    GeoHashBits neighbors[9];\n    unsigned int i, count = 0, last_processed = 0;\n    int debugmsg = 0;\n\n    neighbors[0] = n.hash;\n    neighbors[1] = n.neighbors.north;\n    neighbors[2] = n.neighbors.south;\n    neighbors[3] = n.neighbors.east;\n    neighbors[4] = n.neighbors.west;\n    neighbors[5] = n.neighbors.north_east;\n    neighbors[6] = n.neighbors.north_west;\n    neighbors[7] = n.neighbors.south_east;\n    neighbors[8] = n.neighbors.south_west;\n\n    /* For each neighbor (*and* our own hashbox), get all the matching\n     * members and add them to the potential result list. */\n    for (i = 0; i < sizeof(neighbors) / sizeof(*neighbors); i++) {\n        if (HASHISZERO(neighbors[i])) {\n            if (debugmsg) D(\"neighbors[%d] is zero\",i);\n            continue;\n        }\n\n        /* Debugging info. */\n        if (debugmsg) {\n            GeoHashRange long_range, lat_range;\n            geohashGetCoordRange(&long_range,&lat_range);\n            GeoHashArea myarea = {{0}};\n            geohashDecode(long_range, lat_range, neighbors[i], &myarea);\n\n            /* Dump center square. */\n            D(\"neighbors[%d]:\\n\",i);\n            D(\"area.longitude.min: %f\\n\", myarea.longitude.min);\n            D(\"area.longitude.max: %f\\n\", myarea.longitude.max);\n            D(\"area.latitude.min: %f\\n\", myarea.latitude.min);\n            D(\"area.latitude.max: %f\\n\", myarea.latitude.max);\n            D(\"\\n\");\n        }\n\n        /* When a huge Radius (in the 5000 km range or more) is used,\n         * adjacent neighbors can be the same, leading to duplicated\n         * elements. Skip every range which is the same as the one\n         * processed previously. */\n        if (last_processed &&\n            neighbors[i].bits == neighbors[last_processed].bits &&\n            neighbors[i].step == neighbors[last_processed].step)\n        {\n            if (debugmsg)\n                D(\"Skipping processing of %d, same as previous\\n\",i);\n            continue;\n        }\n        count += membersOfGeoHashBox(zobj, neighbors[i], ga, lon, lat, radius);\n        last_processed = i;\n    }\n    return count;\n}\n\n/* Sort comparators for qsort() */\nstatic int sort_gp_asc(const void *a, const void *b) {\n    const struct geoPoint *gpa = a, *gpb = b;\n    /* We can't do adist - bdist because they are doubles and\n     * the comparator returns an int. */\n    if (gpa->dist > gpb->dist)\n        return 1;\n    else if (gpa->dist == gpb->dist)\n        return 0;\n    else\n        return -1;\n}\n\nstatic int sort_gp_desc(const void *a, const void *b) {\n    return -sort_gp_asc(a, b);\n}\n\n/* ====================================================================\n * Commands\n * ==================================================================== */\n\n/* GEOADD key long lat name [long2 lat2 name2 ... longN latN nameN] */\nvoid geoaddCommand(client *c) {\n    /* Check arguments number for sanity. */\n    if ((c->argc - 2) % 3 != 0) {\n        /* Need an odd number of arguments if we got this far... */\n        addReplyError(c, \"syntax error. Try GEOADD key [x1] [y1] [name1] \"\n                         \"[x2] [y2] [name2] ... \");\n        return;\n    }\n\n    int elements = (c->argc - 2) / 3;\n    int argc = 2+elements*2; /* ZADD key score ele ... */\n    robj **argv = zcalloc(argc*sizeof(robj*));\n    argv[0] = createRawStringObject(\"zadd\",4);\n    argv[1] = c->argv[1]; /* key */\n    incrRefCount(argv[1]);\n\n    /* Create the argument vector to call ZADD in order to add all\n     * the score,value pairs to the requested zset, where score is actually\n     * an encoded version of lat,long. */\n    int i;\n    for (i = 0; i < elements; i++) {\n        double xy[2];\n\n        if (extractLongLatOrReply(c, (c->argv+2)+(i*3),xy) == C_ERR) {\n            for (i = 0; i < argc; i++)\n                if (argv[i]) decrRefCount(argv[i]);\n            zfree(argv);\n            return;\n        }\n\n        /* Turn the coordinates into the score of the element. */\n        GeoHashBits hash;\n        geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash);\n        GeoHashFix52Bits bits = geohashAlign52Bits(hash);\n        robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits));\n        robj *val = c->argv[2 + i * 3 + 2];\n        argv[2+i*2] = score;\n        argv[3+i*2] = val;\n        incrRefCount(val);\n    }\n\n    /* Finally call ZADD that will do the work for us. */\n    replaceClientCommandVector(c,argc,argv);\n    zaddCommand(c);\n}\n\n#define SORT_NONE 0\n#define SORT_ASC 1\n#define SORT_DESC 2\n\n#define RADIUS_COORDS (1<<0)    /* Search around coordinates. */\n#define RADIUS_MEMBER (1<<1)    /* Search around member. */\n#define RADIUS_NOSTORE (1<<2)   /* Do not acceot STORE/STOREDIST option. */\n\n/* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]\n *                               [COUNT count] [STORE key] [STOREDIST key]\n * GEORADIUSBYMEMBER key member radius unit ... options ... */\nvoid georadiusGeneric(client *c, int flags) {\n    robj *key = c->argv[1];\n    robj *storekey = NULL;\n    int storedist = 0; /* 0 for STORE, 1 for STOREDIST. */\n\n    /* Look up the requested zset */\n    robj *zobj = NULL;\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.emptyarray)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET)) {\n        return;\n    }\n\n    /* Find long/lat to use for radius search based on inquiry type */\n    int base_args;\n    double xy[2] = { 0 };\n    if (flags & RADIUS_COORDS) {\n        base_args = 6;\n        if (extractLongLatOrReply(c, c->argv + 2, xy) == C_ERR)\n            return;\n    } else if (flags & RADIUS_MEMBER) {\n        base_args = 5;\n        robj *member = c->argv[2];\n        if (longLatFromMember(zobj, member, xy) == C_ERR) {\n            addReplyError(c, \"could not decode requested zset member\");\n            return;\n        }\n    } else {\n        addReplyError(c, \"Unknown georadius search type\");\n        return;\n    }\n\n    /* Extract radius and units from arguments */\n    double radius_meters = 0, conversion = 1;\n    if ((radius_meters = extractDistanceOrReply(c, c->argv + base_args - 2,\n                                                &conversion)) < 0) {\n        return;\n    }\n\n    /* Discover and populate all optional parameters. */\n    int withdist = 0, withhash = 0, withcoords = 0;\n    int sort = SORT_NONE;\n    long long count = 0;\n    if (c->argc > base_args) {\n        int remaining = c->argc - base_args;\n        for (int i = 0; i < remaining; i++) {\n            char *arg = c->argv[base_args + i]->ptr;\n            if (!strcasecmp(arg, \"withdist\")) {\n                withdist = 1;\n            } else if (!strcasecmp(arg, \"withhash\")) {\n                withhash = 1;\n            } else if (!strcasecmp(arg, \"withcoord\")) {\n                withcoords = 1;\n            } else if (!strcasecmp(arg, \"asc\")) {\n                sort = SORT_ASC;\n            } else if (!strcasecmp(arg, \"desc\")) {\n                sort = SORT_DESC;\n            } else if (!strcasecmp(arg, \"count\") && (i+1) < remaining) {\n                if (getLongLongFromObjectOrReply(c, c->argv[base_args+i+1],\n                    &count, NULL) != C_OK) return;\n                if (count <= 0) {\n                    addReplyError(c,\"COUNT must be > 0\");\n                    return;\n                }\n                i++;\n            } else if (!strcasecmp(arg, \"store\") &&\n                       (i+1) < remaining &&\n                       !(flags & RADIUS_NOSTORE))\n            {\n                storekey = c->argv[base_args+i+1];\n                storedist = 0;\n                i++;\n            } else if (!strcasecmp(arg, \"storedist\") &&\n                       (i+1) < remaining &&\n                       !(flags & RADIUS_NOSTORE))\n            {\n                storekey = c->argv[base_args+i+1];\n                storedist = 1;\n                i++;\n            } else {\n                addReply(c, shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* Trap options not compatible with STORE and STOREDIST. */\n    if (storekey && (withdist || withhash || withcoords)) {\n        addReplyError(c,\n            \"STORE option in GEORADIUS is not compatible with \"\n            \"WITHDIST, WITHHASH and WITHCOORDS options\");\n        return;\n    }\n\n    /* COUNT without ordering does not make much sense, force ASC\n     * ordering if COUNT was specified but no sorting was requested. */\n    if (count != 0 && sort == SORT_NONE) sort = SORT_ASC;\n\n    /* Get all neighbor geohash boxes for our radius search */\n    GeoHashRadius georadius =\n        geohashGetAreasByRadiusWGS84(xy[0], xy[1], radius_meters);\n\n    /* Search the zset for all matching points */\n    geoArray *ga = geoArrayCreate();\n    membersOfAllNeighbors(zobj, georadius, xy[0], xy[1], radius_meters, ga);\n\n    /* If no matching results, the user gets an empty reply. */\n    if (ga->used == 0 && storekey == NULL) {\n        addReply(c,shared.emptyarray);\n        geoArrayFree(ga);\n        return;\n    }\n\n    long result_length = ga->used;\n    long returned_items = (count == 0 || result_length < count) ?\n                          result_length : count;\n    long option_length = 0;\n\n    /* Process [optional] requested sorting */\n    if (sort == SORT_ASC) {\n        qsort(ga->array, result_length, sizeof(geoPoint), sort_gp_asc);\n    } else if (sort == SORT_DESC) {\n        qsort(ga->array, result_length, sizeof(geoPoint), sort_gp_desc);\n    }\n\n    if (storekey == NULL) {\n        /* No target key, return results to user. */\n\n        /* Our options are self-contained nested multibulk replies, so we\n         * only need to track how many of those nested replies we return. */\n        if (withdist)\n            option_length++;\n\n        if (withcoords)\n            option_length++;\n\n        if (withhash)\n            option_length++;\n\n        /* The array len we send is exactly result_length. The result is\n         * either all strings of just zset members  *or* a nested multi-bulk\n         * reply containing the zset member string _and_ all the additional\n         * options the user enabled for this request. */\n        addReplyArrayLen(c, returned_items);\n\n        /* Finally send results back to the caller */\n        int i;\n        for (i = 0; i < returned_items; i++) {\n            geoPoint *gp = ga->array+i;\n            gp->dist /= conversion; /* Fix according to unit. */\n\n            /* If we have options in option_length, return each sub-result\n             * as a nested multi-bulk.  Add 1 to account for result value\n             * itself. */\n            if (option_length)\n                addReplyArrayLen(c, option_length + 1);\n\n            addReplyBulkSds(c,gp->member);\n            gp->member = NULL;\n\n            if (withdist)\n                addReplyDoubleDistance(c, gp->dist);\n\n            if (withhash)\n                addReplyLongLong(c, gp->score);\n\n            if (withcoords) {\n                addReplyArrayLen(c, 2);\n                addReplyHumanLongDouble(c, gp->longitude);\n                addReplyHumanLongDouble(c, gp->latitude);\n            }\n        }\n    } else {\n        /* Target key, create a sorted set with the results. */\n        robj *zobj;\n        zset *zs;\n        int i;\n        size_t maxelelen = 0;\n\n        if (returned_items) {\n            zobj = createZsetObject();\n            zs = zobj->ptr;\n        }\n\n        for (i = 0; i < returned_items; i++) {\n            zskiplistNode *znode;\n            geoPoint *gp = ga->array+i;\n            gp->dist /= conversion; /* Fix according to unit. */\n            double score = storedist ? gp->dist : gp->score;\n            size_t elelen = sdslen(gp->member);\n\n            if (maxelelen < elelen) maxelelen = elelen;\n            znode = zslInsert(zs->zsl,score,gp->member);\n            serverAssert(dictAdd(zs->dict,gp->member,&znode->score) == DICT_OK);\n            gp->member = NULL;\n        }\n\n        if (returned_items) {\n            zsetConvertToZiplistIfNeeded(zobj,maxelelen);\n            setKey(c,c->db,storekey,zobj);\n            decrRefCount(zobj);\n            notifyKeyspaceEvent(NOTIFY_ZSET,\"georadiusstore\",storekey,\n                                c->db->id);\n            server.dirty += returned_items;\n        } else if (dbDelete(c->db,storekey)) {\n            signalModifiedKey(c,c->db,storekey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",storekey,c->db->id);\n            server.dirty++;\n        }\n        addReplyLongLong(c, returned_items);\n    }\n    geoArrayFree(ga);\n}\n\n/* GEORADIUS wrapper function. */\nvoid georadiusCommand(client *c) {\n    georadiusGeneric(c, RADIUS_COORDS);\n}\n\n/* GEORADIUSBYMEMBER wrapper function. */\nvoid georadiusbymemberCommand(client *c) {\n    georadiusGeneric(c, RADIUS_MEMBER);\n}\n\n/* GEORADIUS_RO wrapper function. */\nvoid georadiusroCommand(client *c) {\n    georadiusGeneric(c, RADIUS_COORDS|RADIUS_NOSTORE);\n}\n\n/* GEORADIUSBYMEMBER_RO wrapper function. */\nvoid georadiusbymemberroCommand(client *c) {\n    georadiusGeneric(c, RADIUS_MEMBER|RADIUS_NOSTORE);\n}\n\n/* GEOHASH key ele1 ele2 ... eleN\n *\n * Returns an array with an 11 characters geohash representation of the\n * position of the specified elements. */\nvoid geohashCommand(client *c) {\n    char *geoalphabet= \"0123456789bcdefghjkmnpqrstuvwxyz\";\n    int j;\n\n    /* Look up the requested zset */\n    robj *zobj = lookupKeyRead(c->db, c->argv[1]);\n    if (zobj && checkType(c, zobj, OBJ_ZSET)) return;\n\n    /* Geohash elements one after the other, using a null bulk reply for\n     * missing elements. */\n    addReplyArrayLen(c,c->argc-2);\n    for (j = 2; j < c->argc; j++) {\n        double score;\n        if (!zobj || zsetScore(zobj, c->argv[j]->ptr, &score) == C_ERR) {\n            addReplyNull(c);\n        } else {\n            /* The internal format we use for geocoding is a bit different\n             * than the standard, since we use as initial latitude range\n             * -85,85, while the normal geohashing algorithm uses -90,90.\n             * So we have to decode our position and re-encode using the\n             * standard ranges in order to output a valid geohash string. */\n\n            /* Decode... */\n            double xy[2];\n            if (!decodeGeohash(score,xy)) {\n                addReplyNull(c);\n                continue;\n            }\n\n            /* Re-encode */\n            GeoHashRange r[2];\n            GeoHashBits hash;\n            r[0].min = -180;\n            r[0].max = 180;\n            r[1].min = -90;\n            r[1].max = 90;\n            geohashEncode(&r[0],&r[1],xy[0],xy[1],26,&hash);\n\n            char buf[12];\n            int i;\n            for (i = 0; i < 11; i++) {\n                int idx;\n                if (i == 10) {\n                    /* We have just 52 bits, but the API used to output\n                     * an 11 bytes geohash. For compatibility we assume\n                     * zero. */\n                    idx = 0;\n                } else {\n                    idx = (hash.bits >> (52-((i+1)*5))) & 0x1f;\n                }\n                buf[i] = geoalphabet[idx];\n            }\n            buf[11] = '\\0';\n            addReplyBulkCBuffer(c,buf,11);\n        }\n    }\n}\n\n/* GEOPOS key ele1 ele2 ... eleN\n *\n * Returns an array of two-items arrays representing the x,y position of each\n * element specified in the arguments. For missing elements NULL is returned. */\nvoid geoposCommand(client *c) {\n    int j;\n\n    /* Look up the requested zset */\n    robj *zobj = lookupKeyRead(c->db, c->argv[1]);\n    if (zobj && checkType(c, zobj, OBJ_ZSET)) return;\n\n    /* Report elements one after the other, using a null bulk reply for\n     * missing elements. */\n    addReplyArrayLen(c,c->argc-2);\n    for (j = 2; j < c->argc; j++) {\n        double score;\n        if (!zobj || zsetScore(zobj, c->argv[j]->ptr, &score) == C_ERR) {\n            addReplyNullArray(c);\n        } else {\n            /* Decode... */\n            double xy[2];\n            if (!decodeGeohash(score,xy)) {\n                addReplyNullArray(c);\n                continue;\n            }\n            addReplyArrayLen(c,2);\n            addReplyHumanLongDouble(c,xy[0]);\n            addReplyHumanLongDouble(c,xy[1]);\n        }\n    }\n}\n\n/* GEODIST key ele1 ele2 [unit]\n *\n * Return the distance, in meters by default, otherwise accordig to \"unit\",\n * between points ele1 and ele2. If one or more elements are missing NULL\n * is returned. */\nvoid geodistCommand(client *c) {\n    double to_meter = 1;\n\n    /* Check if there is the unit to extract, otherwise assume meters. */\n    if (c->argc == 5) {\n        to_meter = extractUnitOrReply(c,c->argv[4]);\n        if (to_meter < 0) return;\n    } else if (c->argc > 5) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Look up the requested zset */\n    robj *zobj = NULL;\n    if ((zobj = lookupKeyReadOrReply(c, c->argv[1], shared.null[c->resp]))\n        == NULL || checkType(c, zobj, OBJ_ZSET)) return;\n\n    /* Get the scores. We need both otherwise NULL is returned. */\n    double score1, score2, xyxy[4];\n    if (zsetScore(zobj, c->argv[2]->ptr, &score1) == C_ERR ||\n        zsetScore(zobj, c->argv[3]->ptr, &score2) == C_ERR)\n    {\n        addReplyNull(c);\n        return;\n    }\n\n    /* Decode & compute the distance. */\n    if (!decodeGeohash(score1,xyxy) || !decodeGeohash(score2,xyxy+2))\n        addReplyNull(c);\n    else\n        addReplyDoubleDistance(c,\n            geohashGetDistance(xyxy[0],xyxy[1],xyxy[2],xyxy[3]) / to_meter);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/geo.h",
    "content": "#ifndef __GEO_H__\n#define __GEO_H__\n\n#include \"server.h\"\n\n/* Structures used inside geo.c in order to represent points and array of\n * points on the earth. */\ntypedef struct geoPoint {\n    double longitude;\n    double latitude;\n    double dist;\n    double score;\n    char *member;\n} geoPoint;\n\ntypedef struct geoArray {\n    struct geoPoint *array;\n    size_t buckets;\n    size_t used;\n} geoArray;\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/geohash.c",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n#include \"geohash.h\"\n\n/**\n * Hashing works like this:\n * Divide the world into 4 buckets.  Label each one as such:\n *  -----------------\n *  |       |       |\n *  |       |       |\n *  | 0,1   | 1,1   |\n *  -----------------\n *  |       |       |\n *  |       |       |\n *  | 0,0   | 1,0   |\n *  -----------------\n */\n\n/* Interleave lower bits of x and y, so the bits of x\n * are in the even positions and bits from y in the odd;\n * x and y must initially be less than 2**32 (65536).\n * From:  https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN\n */\nstatic inline uint64_t interleave64(uint32_t xlo, uint32_t ylo) {\n    static const uint64_t B[] = {0x5555555555555555ULL, 0x3333333333333333ULL,\n                                 0x0F0F0F0F0F0F0F0FULL, 0x00FF00FF00FF00FFULL,\n                                 0x0000FFFF0000FFFFULL};\n    static const unsigned int S[] = {1, 2, 4, 8, 16};\n\n    uint64_t x = xlo;\n    uint64_t y = ylo;\n\n    x = (x | (x << S[4])) & B[4];\n    y = (y | (y << S[4])) & B[4];\n\n    x = (x | (x << S[3])) & B[3];\n    y = (y | (y << S[3])) & B[3];\n\n    x = (x | (x << S[2])) & B[2];\n    y = (y | (y << S[2])) & B[2];\n\n    x = (x | (x << S[1])) & B[1];\n    y = (y | (y << S[1])) & B[1];\n\n    x = (x | (x << S[0])) & B[0];\n    y = (y | (y << S[0])) & B[0];\n\n    return x | (y << 1);\n}\n\n/* reverse the interleave process\n * derived from http://stackoverflow.com/questions/4909263\n */\nstatic inline uint64_t deinterleave64(uint64_t interleaved) {\n    static const uint64_t B[] = {0x5555555555555555ULL, 0x3333333333333333ULL,\n                                 0x0F0F0F0F0F0F0F0FULL, 0x00FF00FF00FF00FFULL,\n                                 0x0000FFFF0000FFFFULL, 0x00000000FFFFFFFFULL};\n    static const unsigned int S[] = {0, 1, 2, 4, 8, 16};\n\n    uint64_t x = interleaved;\n    uint64_t y = interleaved >> 1;\n\n    x = (x | (x >> S[0])) & B[0];\n    y = (y | (y >> S[0])) & B[0];\n\n    x = (x | (x >> S[1])) & B[1];\n    y = (y | (y >> S[1])) & B[1];\n\n    x = (x | (x >> S[2])) & B[2];\n    y = (y | (y >> S[2])) & B[2];\n\n    x = (x | (x >> S[3])) & B[3];\n    y = (y | (y >> S[3])) & B[3];\n\n    x = (x | (x >> S[4])) & B[4];\n    y = (y | (y >> S[4])) & B[4];\n\n    x = (x | (x >> S[5])) & B[5];\n    y = (y | (y >> S[5])) & B[5];\n\n    return x | (y << 32);\n}\n\nvoid geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range) {\n    /* These are constraints from EPSG:900913 / EPSG:3785 / OSGEO:41001 */\n    /* We can't geocode at the north/south pole. */\n    long_range->max = GEO_LONG_MAX;\n    long_range->min = GEO_LONG_MIN;\n    lat_range->max = GEO_LAT_MAX;\n    lat_range->min = GEO_LAT_MIN;\n}\n\nint geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,\n                  double longitude, double latitude, uint8_t step,\n                  GeoHashBits *hash) {\n    /* Check basic arguments sanity. */\n    if (hash == NULL || step > 32 || step == 0 ||\n        RANGEPISZERO(lat_range) || RANGEPISZERO(long_range)) return 0;\n\n    /* Return an error when trying to index outside the supported\n     * constraints. */\n    if (longitude > GEO_LONG_MAX || longitude < GEO_LONG_MIN ||\n        latitude > GEO_LAT_MAX || latitude < GEO_LAT_MIN) return 0;\n\n    hash->bits = 0;\n    hash->step = step;\n\n    if (latitude < lat_range->min || latitude > lat_range->max ||\n        longitude < long_range->min || longitude > long_range->max) {\n        return 0;\n    }\n\n    double lat_offset =\n        (latitude - lat_range->min) / (lat_range->max - lat_range->min);\n    double long_offset =\n        (longitude - long_range->min) / (long_range->max - long_range->min);\n\n    /* convert to fixed point based on the step size */\n    lat_offset *= (1ULL << step);\n    long_offset *= (1ULL << step);\n    hash->bits = interleave64(lat_offset, long_offset);\n    return 1;\n}\n\nint geohashEncodeType(double longitude, double latitude, uint8_t step, GeoHashBits *hash) {\n    GeoHashRange r[2] = {{0}};\n    geohashGetCoordRange(&r[0], &r[1]);\n    return geohashEncode(&r[0], &r[1], longitude, latitude, step, hash);\n}\n\nint geohashEncodeWGS84(double longitude, double latitude, uint8_t step,\n                       GeoHashBits *hash) {\n    return geohashEncodeType(longitude, latitude, step, hash);\n}\n\nint geohashDecode(const GeoHashRange long_range, const GeoHashRange lat_range,\n                   const GeoHashBits hash, GeoHashArea *area) {\n    if (HASHISZERO(hash) || NULL == area || RANGEISZERO(lat_range) ||\n        RANGEISZERO(long_range)) {\n        return 0;\n    }\n\n    area->hash = hash;\n    uint8_t step = hash.step;\n    uint64_t hash_sep = deinterleave64(hash.bits); /* hash = [LAT][LONG] */\n\n    double lat_scale = lat_range.max - lat_range.min;\n    double long_scale = long_range.max - long_range.min;\n\n    uint32_t ilato = hash_sep;       /* get lat part of deinterleaved hash */\n    uint32_t ilono = hash_sep >> 32; /* shift over to get long part of hash */\n\n    /* divide by 2**step.\n     * Then, for 0-1 coordinate, multiply times scale and add\n       to the min to get the absolute coordinate. */\n    area->latitude.min =\n        lat_range.min + (ilato * 1.0 / (1ull << step)) * lat_scale;\n    area->latitude.max =\n        lat_range.min + ((ilato + 1) * 1.0 / (1ull << step)) * lat_scale;\n    area->longitude.min =\n        long_range.min + (ilono * 1.0 / (1ull << step)) * long_scale;\n    area->longitude.max =\n        long_range.min + ((ilono + 1) * 1.0 / (1ull << step)) * long_scale;\n\n    return 1;\n}\n\nint geohashDecodeType(const GeoHashBits hash, GeoHashArea *area) {\n    GeoHashRange r[2] = {{0}};\n    geohashGetCoordRange(&r[0], &r[1]);\n    return geohashDecode(r[0], r[1], hash, area);\n}\n\nint geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area) {\n    return geohashDecodeType(hash, area);\n}\n\nint geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy) {\n    if (!xy) return 0;\n    xy[0] = (area->longitude.min + area->longitude.max) / 2;\n    if (xy[0] > GEO_LONG_MAX) xy[0] = GEO_LONG_MAX;\n    if (xy[0] < GEO_LONG_MIN) xy[0] = GEO_LONG_MIN;\n    xy[1] = (area->latitude.min + area->latitude.max) / 2;\n    if (xy[1] > GEO_LAT_MAX) xy[1] = GEO_LAT_MAX;\n    if (xy[1] < GEO_LAT_MIN) xy[1] = GEO_LAT_MIN;\n    return 1;\n}\n\nint geohashDecodeToLongLatType(const GeoHashBits hash, double *xy) {\n    GeoHashArea area = {{0}};\n    if (!xy || !geohashDecodeType(hash, &area))\n        return 0;\n    return geohashDecodeAreaToLongLat(&area, xy);\n}\n\nint geohashDecodeToLongLatWGS84(const GeoHashBits hash, double *xy) {\n    return geohashDecodeToLongLatType(hash, xy);\n}\n\nstatic void geohash_move_x(GeoHashBits *hash, int8_t d) {\n    if (d == 0)\n        return;\n\n    uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaULL;\n    uint64_t y = hash->bits & 0x5555555555555555ULL;\n\n    uint64_t zz = 0x5555555555555555ULL >> (64 - hash->step * 2);\n\n    if (d > 0) {\n        x = x + (zz + 1);\n    } else {\n        x = x | zz;\n        x = x - (zz + 1);\n    }\n\n    x &= (0xaaaaaaaaaaaaaaaaULL >> (64 - hash->step * 2));\n    hash->bits = (x | y);\n}\n\nstatic void geohash_move_y(GeoHashBits *hash, int8_t d) {\n    if (d == 0)\n        return;\n\n    uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaULL;\n    uint64_t y = hash->bits & 0x5555555555555555ULL;\n\n    uint64_t zz = 0xaaaaaaaaaaaaaaaaULL >> (64 - hash->step * 2);\n    if (d > 0) {\n        y = y + (zz + 1);\n    } else {\n        y = y | zz;\n        y = y - (zz + 1);\n    }\n    y &= (0x5555555555555555ULL >> (64 - hash->step * 2));\n    hash->bits = (x | y);\n}\n\nvoid geohashNeighbors(const GeoHashBits *hash, GeoHashNeighbors *neighbors) {\n    neighbors->east = *hash;\n    neighbors->west = *hash;\n    neighbors->north = *hash;\n    neighbors->south = *hash;\n    neighbors->south_east = *hash;\n    neighbors->south_west = *hash;\n    neighbors->north_east = *hash;\n    neighbors->north_west = *hash;\n\n    geohash_move_x(&neighbors->east, 1);\n    geohash_move_y(&neighbors->east, 0);\n\n    geohash_move_x(&neighbors->west, -1);\n    geohash_move_y(&neighbors->west, 0);\n\n    geohash_move_x(&neighbors->south, 0);\n    geohash_move_y(&neighbors->south, -1);\n\n    geohash_move_x(&neighbors->north, 0);\n    geohash_move_y(&neighbors->north, 1);\n\n    geohash_move_x(&neighbors->north_west, -1);\n    geohash_move_y(&neighbors->north_west, 1);\n\n    geohash_move_x(&neighbors->north_east, 1);\n    geohash_move_y(&neighbors->north_east, 1);\n\n    geohash_move_x(&neighbors->south_east, 1);\n    geohash_move_y(&neighbors->south_east, -1);\n\n    geohash_move_x(&neighbors->south_west, -1);\n    geohash_move_y(&neighbors->south_west, -1);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/geohash.h",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef GEOHASH_H_\n#define GEOHASH_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdint.h>\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n#define HASHISZERO(r) (!(r).bits && !(r).step)\n#define RANGEISZERO(r) (!(r).max && !(r).min)\n#define RANGEPISZERO(r) (r == NULL || RANGEISZERO(*r))\n\n#define GEO_STEP_MAX 26 /* 26*2 = 52 bits. */\n\n/* Limits from EPSG:900913 / EPSG:3785 / OSGEO:41001 */\n#define GEO_LAT_MIN -85.05112878\n#define GEO_LAT_MAX 85.05112878\n#define GEO_LONG_MIN -180\n#define GEO_LONG_MAX 180\n\ntypedef enum {\n    GEOHASH_NORTH = 0,\n    GEOHASH_EAST,\n    GEOHASH_WEST,\n    GEOHASH_SOUTH,\n    GEOHASH_SOUTH_WEST,\n    GEOHASH_SOUTH_EAST,\n    GEOHASH_NORT_WEST,\n    GEOHASH_NORT_EAST\n} GeoDirection;\n\ntypedef struct {\n    uint64_t bits;\n    uint8_t step;\n} GeoHashBits;\n\ntypedef struct {\n    double min;\n    double max;\n} GeoHashRange;\n\ntypedef struct {\n    GeoHashBits hash;\n    GeoHashRange longitude;\n    GeoHashRange latitude;\n} GeoHashArea;\n\ntypedef struct {\n    GeoHashBits north;\n    GeoHashBits east;\n    GeoHashBits west;\n    GeoHashBits south;\n    GeoHashBits north_east;\n    GeoHashBits south_east;\n    GeoHashBits north_west;\n    GeoHashBits south_west;\n} GeoHashNeighbors;\n\n/*\n * 0:success\n * -1:failed\n */\nvoid geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range);\nint geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,\n                  double longitude, double latitude, uint8_t step,\n                  GeoHashBits *hash);\nint geohashEncodeType(double longitude, double latitude,\n                      uint8_t step, GeoHashBits *hash);\nint geohashEncodeWGS84(double longitude, double latitude, uint8_t step,\n                       GeoHashBits *hash);\nint geohashDecode(const GeoHashRange long_range, const GeoHashRange lat_range,\n                  const GeoHashBits hash, GeoHashArea *area);\nint geohashDecodeType(const GeoHashBits hash, GeoHashArea *area);\nint geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area);\nint geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy);\nint geohashDecodeToLongLatType(const GeoHashBits hash, double *xy);\nint geohashDecodeToLongLatWGS84(const GeoHashBits hash, double *xy);\nint geohashDecodeToLongLatMercator(const GeoHashBits hash, double *xy);\nvoid geohashNeighbors(const GeoHashBits *hash, GeoHashNeighbors *neighbors);\n\n#if defined(__cplusplus)\n}\n#endif\n#endif /* GEOHASH_H_ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/geohash_helper.c",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* This is a C++ to C conversion from the ardb project.\n * This file started out as:\n * https://github.com/yinqiwen/ardb/blob/d42503/src/geo/geohash_helper.cpp\n */\n\n#include \"fmacros.h\"\n#include \"geohash_helper.h\"\n#include \"debugmacro.h\"\n#include <math.h>\n\n#define D_R (M_PI / 180.0)\n#define R_MAJOR 6378137.0\n#define R_MINOR 6356752.3142\n#define RATIO (R_MINOR / R_MAJOR)\n#define ECCENT (sqrt(1.0 - (RATIO *RATIO)))\n#define COM (0.5 * ECCENT)\n\n/// @brief The usual PI/180 constant\nconst double DEG_TO_RAD = 0.017453292519943295769236907684886;\n/// @brief Earth's quatratic mean radius for WGS-84\nconst double EARTH_RADIUS_IN_METERS = 6372797.560856;\n\nconst double MERCATOR_MAX = 20037726.37;\nconst double MERCATOR_MIN = -20037726.37;\n\nstatic inline double deg_rad(double ang) { return ang * D_R; }\nstatic inline double rad_deg(double ang) { return ang / D_R; }\n\n/* This function is used in order to estimate the step (bits precision)\n * of the 9 search area boxes during radius queries. */\nuint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {\n    if (range_meters == 0) return 26;\n    int step = 1;\n    while (range_meters < MERCATOR_MAX) {\n        range_meters *= 2;\n        step++;\n    }\n    step -= 2; /* Make sure range is included in most of the base cases. */\n\n    /* Wider range torwards the poles... Note: it is possible to do better\n     * than this approximation by computing the distance between meridians\n     * at this latitude, but this does the trick for now. */\n    if (lat > 66 || lat < -66) {\n        step--;\n        if (lat > 80 || lat < -80) step--;\n    }\n\n    /* Frame to valid range. */\n    if (step < 1) step = 1;\n    if (step > 26) step = 26;\n    return step;\n}\n\n/* Return the bounding box of the search area centered at latitude,longitude\n * having a radius of radius_meter. bounds[0] - bounds[2] is the minimum\n * and maxium longitude, while bounds[1] - bounds[3] is the minimum and\n * maximum latitude.\n *\n * This function does not behave correctly with very large radius values, for\n * instance for the coordinates 81.634948934258375 30.561509253718668 and a\n * radius of 7083 kilometers, it reports as bounding boxes:\n *\n * min_lon 7.680495, min_lat -33.119473, max_lon 155.589402, max_lat 94.242491\n *\n * However, for instance, a min_lon of 7.680495 is not correct, because the\n * point -1.27579540014266968 61.33421815228281559 is at less than 7000\n * kilometers away.\n *\n * Since this function is currently only used as an optimization, the\n * optimization is not used for very big radiuses, however the function\n * should be fixed. */\nint geohashBoundingBox(double longitude, double latitude, double radius_meters,\n                       double *bounds) {\n    if (!bounds) return 0;\n\n    bounds[0] = longitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude)));\n    bounds[2] = longitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude)));\n    bounds[1] = latitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS);\n    bounds[3] = latitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS);\n    return 1;\n}\n\n/* Return a set of areas (center + 8) that are able to cover a range query\n * for the specified position and radius. */\nGeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double radius_meters) {\n    GeoHashRange long_range, lat_range;\n    GeoHashRadius radius;\n    GeoHashBits hash;\n    GeoHashNeighbors neighbors;\n    GeoHashArea area;\n    double min_lon, max_lon, min_lat, max_lat;\n    double bounds[4];\n    int steps;\n\n    geohashBoundingBox(longitude, latitude, radius_meters, bounds);\n    min_lon = bounds[0];\n    min_lat = bounds[1];\n    max_lon = bounds[2];\n    max_lat = bounds[3];\n\n    steps = geohashEstimateStepsByRadius(radius_meters,latitude);\n\n    geohashGetCoordRange(&long_range,&lat_range);\n    geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);\n    geohashNeighbors(&hash,&neighbors);\n    geohashDecode(long_range,lat_range,hash,&area);\n\n    /* Check if the step is enough at the limits of the covered area.\n     * Sometimes when the search area is near an edge of the\n     * area, the estimated step is not small enough, since one of the\n     * north / south / west / east square is too near to the search area\n     * to cover everything. */\n    int decrease_step = 0;\n    {\n        GeoHashArea north, south, east, west;\n\n        geohashDecode(long_range, lat_range, neighbors.north, &north);\n        geohashDecode(long_range, lat_range, neighbors.south, &south);\n        geohashDecode(long_range, lat_range, neighbors.east, &east);\n        geohashDecode(long_range, lat_range, neighbors.west, &west);\n\n        if (geohashGetDistance(longitude,latitude,longitude,north.latitude.max)\n            < radius_meters) decrease_step = 1;\n        if (geohashGetDistance(longitude,latitude,longitude,south.latitude.min)\n            < radius_meters) decrease_step = 1;\n        if (geohashGetDistance(longitude,latitude,east.longitude.max,latitude)\n            < radius_meters) decrease_step = 1;\n        if (geohashGetDistance(longitude,latitude,west.longitude.min,latitude)\n            < radius_meters) decrease_step = 1;\n    }\n\n    if (steps > 1 && decrease_step) {\n        steps--;\n        geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);\n        geohashNeighbors(&hash,&neighbors);\n        geohashDecode(long_range,lat_range,hash,&area);\n    }\n\n    /* Exclude the search areas that are useless. */\n    if (steps >= 2) {\n        if (area.latitude.min < min_lat) {\n            GZERO(neighbors.south);\n            GZERO(neighbors.south_west);\n            GZERO(neighbors.south_east);\n        }\n        if (area.latitude.max > max_lat) {\n            GZERO(neighbors.north);\n            GZERO(neighbors.north_east);\n            GZERO(neighbors.north_west);\n        }\n        if (area.longitude.min < min_lon) {\n            GZERO(neighbors.west);\n            GZERO(neighbors.south_west);\n            GZERO(neighbors.north_west);\n        }\n        if (area.longitude.max > max_lon) {\n            GZERO(neighbors.east);\n            GZERO(neighbors.south_east);\n            GZERO(neighbors.north_east);\n        }\n    }\n    radius.hash = hash;\n    radius.neighbors = neighbors;\n    radius.area = area;\n    return radius;\n}\n\nGeoHashRadius geohashGetAreasByRadiusWGS84(double longitude, double latitude,\n                                           double radius_meters) {\n    return geohashGetAreasByRadius(longitude, latitude, radius_meters);\n}\n\nGeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash) {\n    uint64_t bits = hash.bits;\n    bits <<= (52 - hash.step * 2);\n    return bits;\n}\n\n/* Calculate distance using haversin great circle distance formula. */\ndouble geohashGetDistance(double lon1d, double lat1d, double lon2d, double lat2d) {\n    double lat1r, lon1r, lat2r, lon2r, u, v;\n    lat1r = deg_rad(lat1d);\n    lon1r = deg_rad(lon1d);\n    lat2r = deg_rad(lat2d);\n    lon2r = deg_rad(lon2d);\n    u = sin((lat2r - lat1r) / 2);\n    v = sin((lon2r - lon1r) / 2);\n    return 2.0 * EARTH_RADIUS_IN_METERS *\n           asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v));\n}\n\nint geohashGetDistanceIfInRadius(double x1, double y1,\n                                 double x2, double y2, double radius,\n                                 double *distance) {\n    *distance = geohashGetDistance(x1, y1, x2, y2);\n    if (*distance > radius) return 0;\n    return 1;\n}\n\nint geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2,\n                                      double y2, double radius,\n                                      double *distance) {\n    return geohashGetDistanceIfInRadius(x1, y1, x2, y2, radius, distance);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/geohash_helper.h",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef GEOHASH_HELPER_HPP_\n#define GEOHASH_HELPER_HPP_\n\n#include \"geohash.h\"\n\n#define GZERO(s) s.bits = s.step = 0;\n#define GISZERO(s) (!s.bits && !s.step)\n#define GISNOTZERO(s) (s.bits || s.step)\n\ntypedef uint64_t GeoHashFix52Bits;\ntypedef uint64_t GeoHashVarBits;\n\ntypedef struct {\n    GeoHashBits hash;\n    GeoHashArea area;\n    GeoHashNeighbors neighbors;\n} GeoHashRadius;\n\nint GeoHashBitsComparator(const GeoHashBits *a, const GeoHashBits *b);\nuint8_t geohashEstimateStepsByRadius(double range_meters, double lat);\nint geohashBoundingBox(double longitude, double latitude, double radius_meters,\n                        double *bounds);\nGeoHashRadius geohashGetAreasByRadius(double longitude,\n                                      double latitude, double radius_meters);\nGeoHashRadius geohashGetAreasByRadiusWGS84(double longitude, double latitude,\n                                           double radius_meters);\nGeoHashRadius geohashGetAreasByRadiusMercator(double longitude, double latitude,\n                                              double radius_meters);\nGeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash);\ndouble geohashGetDistance(double lon1d, double lat1d,\n                          double lon2d, double lat2d);\nint geohashGetDistanceIfInRadius(double x1, double y1,\n                                 double x2, double y2, double radius,\n                                 double *distance);\nint geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2,\n                                      double y2, double radius,\n                                      double *distance);\n\n#endif /* GEOHASH_HELPER_HPP_ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/gopher.c",
    "content": "/*\n * Copyright (c) 2019, 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 \"server.h\"\n\n/* Emit an item in Gopher directory listing format:\n * <type><descr><TAB><selector><TAB><hostname><TAB><port>\n * If descr or selector are NULL, then the \"(NULL)\" string is used instead. */\nvoid addReplyGopherItem(client *c, const char *type, const char *descr,\n                        const char *selector, const char *hostname, int port)\n{\n    sds item = sdscatfmt(sdsempty(),\"%s%s\\t%s\\t%s\\t%i\\r\\n\",\n                         type, descr,\n                         selector ? selector : \"(NULL)\",\n                         hostname ? hostname : \"(NULL)\",\n                         port);\n    addReplyProto(c,item,sdslen(item));\n    sdsfree(item);\n}\n\n/* This is called by processInputBuffer() when an inline request is processed\n * with Gopher mode enabled, and the request happens to have zero or just one\n * argument. In such case we get the relevant key and reply using the Gopher\n * protocol. */\nvoid processGopherRequest(client *c) {\n    robj *keyname = c->argc == 0 ? createStringObject(\"/\",1) : c->argv[0];\n    robj *o = lookupKeyRead(c->db,keyname);\n\n    /* If there is no such key, return with a Gopher error. */\n    if (o == NULL || o->type != OBJ_STRING) {\n        char *errstr;\n        if (o == NULL)\n            errstr = \"Error: no content at the specified key\";\n        else\n            errstr = \"Error: selected key type is invalid \"\n                     \"for Gopher output\";\n        addReplyGopherItem(c,\"i\",errstr,NULL,NULL,0);\n        addReplyGopherItem(c,\"i\",\"Redis Gopher server\",NULL,NULL,0);\n    } else {\n        addReply(c,o);\n    }\n\n    /* Cleanup, also make sure to emit the final \".CRLF\" line. Note that\n     * the connection will be closed immediately after this because the client\n     * will be flagged with CLIENT_CLOSE_AFTER_REPLY, in accordance with the\n     * Gopher protocol. */\n    if (c->argc == 0) decrRefCount(keyname);\n\n    /* Note that in theory we should terminate the Gopher request with\n     * \".<CR><LF>\" (called Lastline in the RFC) like that:\n     *\n     * addReplyProto(c,\".\\r\\n\",3);\n     *\n     * However after examining the current clients landscape, it's probably\n     * going to do more harm than good for several reasons:\n     *\n     * 1. Clients should not have any issue with missing .<CR><LF> as for\n     *    specification, and in the real world indeed certain servers\n     *    implementations never used to send the terminator.\n     *\n     * 2. Redis does not know if it's serving a text file or a binary file:\n     *    at the same time clients will not remove the \".<CR><LF>\" bytes at\n     *    tne end when downloading a binary file from the server, so adding\n     *    the \"Lastline\" terminator without knowing the content is just\n     *    dangerous.\n     *\n     * 3. The utility gopher2redis.rb that we provide for Redis, and any\n     *    other similar tool you may use as Gopher authoring system for\n     *    Redis, can just add the \"Lastline\" when needed.\n     */\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/help.h",
    "content": "/* Automatically generated by 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    \"cluster\",\n    \"geo\",\n    \"stream\"\n};\n\nstruct commandHelp {\n  char *name;\n  char *params;\n  char *summary;\n  int group;\n  char *since;\n} commandHelp[] = {\n    { \"ACL CAT\",\n    \"[categoryname]\",\n    \"List the ACL categories or the commands inside a category\",\n    9,\n    \"6.0.0\" },\n    { \"ACL DELUSER\",\n    \"username [username ...]\",\n    \"Remove the specified ACL users and the associated rules\",\n    9,\n    \"6.0.0\" },\n    { \"ACL GENPASS\",\n    \"[bits]\",\n    \"Generate a pseudorandom secure password to use for ACL users\",\n    9,\n    \"6.0.0\" },\n    { \"ACL LIST\",\n    \"-\",\n    \"List the current ACL rules in ACL config file format\",\n    9,\n    \"6.0.0\" },\n    { \"ACL LOAD\",\n    \"-\",\n    \"Reload the ACLs from the configured ACL file\",\n    9,\n    \"6.0.0\" },\n    { \"ACL LOG\",\n    \"[count or RESET]\",\n    \"List latest events denied because of ACLs in place\",\n    9,\n    \"6.0.0\" },\n    { \"ACL SAVE\",\n    \"-\",\n    \"Save the current ACL rules in the configured ACL file\",\n    9,\n    \"6.0.0\" },\n    { \"ACL SETUSER\",\n    \"rule [rule ...]\",\n    \"Modify or create the rules for a specific ACL user\",\n    9,\n    \"6.0.0\" },\n    { \"ACL USERS\",\n    \"-\",\n    \"List the username of all the configured ACL rules\",\n    9,\n    \"6.0.0\" },\n    { \"ACL WHOAMI\",\n    \"-\",\n    \"Return the name of the user associated to the current connection\",\n    9,\n    \"6.0.0\" },\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    \"[SCHEDULE]\",\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    { \"BITFIELD\",\n    \"key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]\",\n    \"Perform arbitrary bitfield integer operations on strings\",\n    1,\n    \"3.2.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 an element from a list, push it to another list and return it; or block until one is available\",\n    2,\n    \"2.2.0\" },\n    { \"BZPOPMAX\",\n    \"key [key ...] timeout\",\n    \"Remove and return the member with the highest score from one or more sorted sets, or block until one is available\",\n    4,\n    \"5.0.0\" },\n    { \"BZPOPMIN\",\n    \"key [key ...] timeout\",\n    \"Remove and return the member with the lowest score from one or more sorted sets, or block until one is available\",\n    4,\n    \"5.0.0\" },\n    { \"CLIENT CACHING\",\n    \"YES|NO\",\n    \"Instruct the server about tracking or not keys in the next request\",\n    8,\n    \"6.0.0\" },\n    { \"CLIENT GETNAME\",\n    \"-\",\n    \"Get the current connection name\",\n    8,\n    \"2.6.9\" },\n    { \"CLIENT GETREDIR\",\n    \"-\",\n    \"Get tracking notifications redirection client ID if any\",\n    8,\n    \"6.0.0\" },\n    { \"CLIENT ID\",\n    \"-\",\n    \"Returns the client ID for the current connection\",\n    8,\n    \"5.0.0\" },\n    { \"CLIENT KILL\",\n    \"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [ADDR ip:port] [SKIPME yes/no]\",\n    \"Kill the connection of a client\",\n    8,\n    \"2.4.0\" },\n    { \"CLIENT LIST\",\n    \"[TYPE normal|master|replica|pubsub]\",\n    \"Get the list of client connections\",\n    8,\n    \"2.4.0\" },\n    { \"CLIENT PAUSE\",\n    \"timeout\",\n    \"Stop processing commands from clients for some time\",\n    8,\n    \"2.9.50\" },\n    { \"CLIENT REPLY\",\n    \"ON|OFF|SKIP\",\n    \"Instruct the server whether to reply to commands\",\n    8,\n    \"3.2\" },\n    { \"CLIENT SETNAME\",\n    \"connection-name\",\n    \"Set the current connection name\",\n    8,\n    \"2.6.9\" },\n    { \"CLIENT TRACKING\",\n    \"ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]\",\n    \"Enable or disable server assisted client side caching support\",\n    8,\n    \"6.0.0\" },\n    { \"CLIENT UNBLOCK\",\n    \"client-id [TIMEOUT|ERROR]\",\n    \"Unblock a client blocked in a blocking command from a different connection\",\n    8,\n    \"5.0.0\" },\n    { \"CLUSTER ADDSLOTS\",\n    \"slot [slot ...]\",\n    \"Assign new hash slots to receiving node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER BUMPEPOCH\",\n    \"-\",\n    \"Advance the cluster config epoch\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER COUNT-FAILURE-REPORTS\",\n    \"node-id\",\n    \"Return the number of failure reports active for a given node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER COUNTKEYSINSLOT\",\n    \"slot\",\n    \"Return the number of local keys in the specified hash slot\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER DELSLOTS\",\n    \"slot [slot ...]\",\n    \"Set hash slots as unbound in receiving node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER FAILOVER\",\n    \"[FORCE|TAKEOVER]\",\n    \"Forces a replica to perform a manual failover of its master.\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER FLUSHSLOTS\",\n    \"-\",\n    \"Delete a node's own slots information\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER FORGET\",\n    \"node-id\",\n    \"Remove a node from the nodes table\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER GETKEYSINSLOT\",\n    \"slot count\",\n    \"Return local key names in the specified hash slot\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER INFO\",\n    \"-\",\n    \"Provides info about Redis Cluster node state\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER KEYSLOT\",\n    \"key\",\n    \"Returns the hash slot of the specified key\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER MEET\",\n    \"ip port\",\n    \"Force a node cluster to handshake with another node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER MYID\",\n    \"-\",\n    \"Return the node id\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER NODES\",\n    \"-\",\n    \"Get Cluster config for the node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER REPLICAS\",\n    \"node-id\",\n    \"List replica nodes of the specified master node\",\n    12,\n    \"5.0.0\" },\n    { \"CLUSTER REPLICATE\",\n    \"node-id\",\n    \"Reconfigure a node as a replica of the specified master node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER RESET\",\n    \"[HARD|SOFT]\",\n    \"Reset a Redis Cluster node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SAVECONFIG\",\n    \"-\",\n    \"Forces the node to save cluster state on disk\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SET-CONFIG-EPOCH\",\n    \"config-epoch\",\n    \"Set the configuration epoch in a new node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SETSLOT\",\n    \"slot IMPORTING|MIGRATING|STABLE|NODE [node-id]\",\n    \"Bind a hash slot to a specific node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SLAVES\",\n    \"node-id\",\n    \"List replica nodes of the specified master node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SLOTS\",\n    \"-\",\n    \"Get array of Cluster slot to node mappings\",\n    12,\n    \"3.0.0\" },\n    { \"COMMAND\",\n    \"-\",\n    \"Get array of Redis command details\",\n    9,\n    \"2.8.13\" },\n    { \"COMMAND COUNT\",\n    \"-\",\n    \"Get total number of Redis commands\",\n    9,\n    \"2.8.13\" },\n    { \"COMMAND GETKEYS\",\n    \"-\",\n    \"Extract keys given a full Redis command\",\n    9,\n    \"2.8.13\" },\n    { \"COMMAND INFO\",\n    \"command-name [command-name ...]\",\n    \"Get array of specific Redis command details\",\n    9,\n    \"2.8.13\" },\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 [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    \"[ASYNC]\",\n    \"Remove all keys from all databases\",\n    9,\n    \"1.0.0\" },\n    { \"FLUSHDB\",\n    \"[ASYNC]\",\n    \"Remove all keys from the current database\",\n    9,\n    \"1.0.0\" },\n    { \"GEOADD\",\n    \"key longitude latitude member [longitude latitude member ...]\",\n    \"Add one or more geospatial items in the geospatial index represented using a sorted set\",\n    13,\n    \"3.2.0\" },\n    { \"GEODIST\",\n    \"key member1 member2 [m|km|ft|mi]\",\n    \"Returns the distance between two members of a geospatial index\",\n    13,\n    \"3.2.0\" },\n    { \"GEOHASH\",\n    \"key member [member ...]\",\n    \"Returns members of a geospatial index as standard geohash strings\",\n    13,\n    \"3.2.0\" },\n    { \"GEOPOS\",\n    \"key member [member ...]\",\n    \"Returns longitude and latitude of members of a geospatial index\",\n    13,\n    \"3.2.0\" },\n    { \"GEORADIUS\",\n    \"key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]\",\n    \"Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point\",\n    13,\n    \"3.2.0\" },\n    { \"GEORADIUSBYMEMBER\",\n    \"key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]\",\n    \"Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member\",\n    13,\n    \"3.2.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    { \"HELLO\",\n    \"protover [AUTH username password] [SETNAME clientname]\",\n    \"switch Redis protocol\",\n    8,\n    \"6.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 [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    { \"HSTRLEN\",\n    \"key field\",\n    \"Get the length of the value of a hash field\",\n    5,\n    \"3.2.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    { \"LATENCY DOCTOR\",\n    \"-\",\n    \"Return a human readable latency analysis report.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY GRAPH\",\n    \"event\",\n    \"Return a latency graph for the event.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY HELP\",\n    \"-\",\n    \"Show helpful text about the different subcommands.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY HISTORY\",\n    \"event\",\n    \"Return timestamp-latency samples for the event.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY LATEST\",\n    \"-\",\n    \"Return the latest latency samples for all events.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY RESET\",\n    \"[event]\",\n    \"Reset latency data for one or more events.\",\n    9,\n    \"2.8.13\" },\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 element\",\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    { \"LOLWUT\",\n    \"[VERSION version]\",\n    \"Display some computer art and the Redis version\",\n    9,\n    \"5.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 element [element ...]\",\n    \"Prepend one or multiple elements to a list\",\n    2,\n    \"1.0.0\" },\n    { \"LPUSHX\",\n    \"key element [element ...]\",\n    \"Prepend an element 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 element\",\n    \"Remove elements from a list\",\n    2,\n    \"1.0.0\" },\n    { \"LSET\",\n    \"key index element\",\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    { \"MEMORY DOCTOR\",\n    \"-\",\n    \"Outputs memory problems report\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY HELP\",\n    \"-\",\n    \"Show helpful text about the different subcommands\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY MALLOC-STATS\",\n    \"-\",\n    \"Show allocator internal stats\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY PURGE\",\n    \"-\",\n    \"Ask the allocator to release memory\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY STATS\",\n    \"-\",\n    \"Show memory usage details\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY USAGE\",\n    \"key [SAMPLES count]\",\n    \"Estimate the memory usage of a key\",\n    9,\n    \"4.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] [AUTH password] [KEYS key]\",\n    \"Atomically transfer a key from a Redis instance to another one.\",\n    0,\n    \"2.6.0\" },\n    { \"MODULE LIST\",\n    \"-\",\n    \"List all modules loaded by the server\",\n    9,\n    \"4.0.0\" },\n    { \"MODULE LOAD\",\n    \"path [arg]\",\n    \"Load a module\",\n    9,\n    \"4.0.0\" },\n    { \"MODULE UNLOAD\",\n    \"name\",\n    \"Unload a module\",\n    9,\n    \"4.0.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    \"[message]\",\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    { \"PSYNC\",\n    \"replicationid offset\",\n    \"Internal command used for replication\",\n    9,\n    \"2.8.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    { \"READONLY\",\n    \"-\",\n    \"Enables read queries for a connection to a cluster replica node\",\n    12,\n    \"3.0.0\" },\n    { \"READWRITE\",\n    \"-\",\n    \"Disables read queries for a connection to a cluster replica node\",\n    12,\n    \"3.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    { \"REPLICAOF\",\n    \"host port\",\n    \"Make the server a replica of another instance, or promote it as master.\",\n    9,\n    \"5.0.0\" },\n    { \"RESTORE\",\n    \"key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]\",\n    \"Create a key using the provided serialized value, previously obtained using DUMP.\",\n    0,\n    \"2.6.0\" },\n    { \"ROLE\",\n    \"-\",\n    \"Return the role of the instance in the context of replication\",\n    9,\n    \"2.8.12\" },\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, prepend it to another list and return it\",\n    2,\n    \"1.2.0\" },\n    { \"RPUSH\",\n    \"key element [element ...]\",\n    \"Append one or multiple elements to a list\",\n    2,\n    \"1.0.0\" },\n    { \"RPUSHX\",\n    \"key element [element ...]\",\n    \"Append an element 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] [TYPE type]\",\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 DEBUG\",\n    \"YES|SYNC|NO\",\n    \"Set the debug mode for executed scripts.\",\n    10,\n    \"3.2.0\" },\n    { \"SCRIPT EXISTS\",\n    \"sha1 [sha1 ...]\",\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] [KEEPTTL]\",\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 replica of another instance, or promote it as master. Deprecated starting with Redis 5. Use REPLICAOF instead.\",\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 [count]\",\n    \"Remove and return one or multiple random members 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    { \"STRALGO\",\n    \"LCS algo-specific-argument [algo-specific-argument ...]\",\n    \"Run algorithms (currently LCS) against strings\",\n    1,\n    \"6.0.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    { \"SWAPDB\",\n    \"index1 index2\",\n    \"Swaps two Redis databases\",\n    9,\n    \"4.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    { \"TOUCH\",\n    \"key [key ...]\",\n    \"Alters the last access time of a key(s). Returns the number of existing keys specified.\",\n    0,\n    \"3.2.1\" },\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    { \"UNLINK\",\n    \"key [key ...]\",\n    \"Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.\",\n    0,\n    \"4.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    { \"WAIT\",\n    \"numreplicas timeout\",\n    \"Wait for the synchronous replication of all the write commands sent in the context of the current connection\",\n    0,\n    \"3.0.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    { \"XACK\",\n    \"key group ID [ID ...]\",\n    \"Marks a pending message as correctly processed, effectively removing it from the pending entries list of the consumer group. Return value of the command is the number of messages successfully acknowledged, that is, the IDs we were actually able to resolve in the PEL.\",\n    14,\n    \"5.0.0\" },\n    { \"XADD\",\n    \"key ID field value [field value ...]\",\n    \"Appends a new entry to a stream\",\n    14,\n    \"5.0.0\" },\n    { \"XCLAIM\",\n    \"key group consumer min-idle-time ID [ID ...] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [force] [justid]\",\n    \"Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.\",\n    14,\n    \"5.0.0\" },\n    { \"XDEL\",\n    \"key ID [ID ...]\",\n    \"Removes the specified entries from the stream. Returns the number of items actually deleted, that may be different from the number of IDs passed in case certain IDs do not exist.\",\n    14,\n    \"5.0.0\" },\n    { \"XGROUP\",\n    \"[CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]\",\n    \"Create, destroy, and manage consumer groups.\",\n    14,\n    \"5.0.0\" },\n    { \"XINFO\",\n    \"[CONSUMERS key groupname] [GROUPS key] [STREAM key] [HELP]\",\n    \"Get information on streams and consumer groups\",\n    14,\n    \"5.0.0\" },\n    { \"XLEN\",\n    \"key\",\n    \"Return the number of entires in a stream\",\n    14,\n    \"5.0.0\" },\n    { \"XPENDING\",\n    \"key group [start end count] [consumer]\",\n    \"Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.\",\n    14,\n    \"5.0.0\" },\n    { \"XRANGE\",\n    \"key start end [COUNT count]\",\n    \"Return a range of elements in a stream, with IDs matching the specified IDs interval\",\n    14,\n    \"5.0.0\" },\n    { \"XREAD\",\n    \"[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]\",\n    \"Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.\",\n    14,\n    \"5.0.0\" },\n    { \"XREADGROUP\",\n    \"GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]\",\n    \"Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.\",\n    14,\n    \"5.0.0\" },\n    { \"XREVRANGE\",\n    \"key end start [COUNT count]\",\n    \"Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE\",\n    14,\n    \"5.0.0\" },\n    { \"XTRIM\",\n    \"key MAXLEN [~] count\",\n    \"Trims the stream to (approximately if '~' is passed) a certain size\",\n    14,\n    \"5.0.0\" },\n    { \"ZADD\",\n    \"key [NX|XX] [CH] [INCR] 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    { \"ZPOPMAX\",\n    \"key [count]\",\n    \"Remove and return members with the highest scores in a sorted set\",\n    4,\n    \"5.0.0\" },\n    { \"ZPOPMIN\",\n    \"key [count]\",\n    \"Remove and return members with the lowest scores in a sorted set\",\n    4,\n    \"5.0.0\" },\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    { \"ZREVRANGEBYLEX\",\n    \"key max min [LIMIT offset count]\",\n    \"Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.\",\n    4,\n    \"2.8.9\" },\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": "Chapter02/redis-6.0.3/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 \"server.h\"\n\n#include <stdint.h>\n#include <math.h>\n\n/* The Redis HyperLogLog implementation is based on the following ideas:\n *\n * * The use of a 64 bit hash function as proposed in [1], in order to don't\n *   limited to cardinalities up to 10^9, at the cost of just 1 additional\n *   bit per register.\n * * The use of 16384 6-bit registers for a great level of accuracy, using\n *   a total of 12k per key.\n * * The use of the Redis string data type. No new type is introduced.\n * * No attempt is made to compress the data structure as in [1]. Also the\n *   algorithm used is the original HyperLogLog Algorithm as in [2], with\n *   the only difference that a 64 bit hash function is used, so no correction\n *   is performed for values near 2^32 as in [1].\n *\n * [1] Heule, Nunkesser, Hall: HyperLogLog in Practice: Algorithmic\n *     Engineering of a State of The Art Cardinality Estimation Algorithm.\n *\n * [2] P. Flajolet, Éric Fusy, O. Gandouet, and F. Meunier. Hyperloglog: The\n *     analysis of a near-optimal cardinality estimation algorithm.\n *\n * Redis uses two representations:\n *\n * 1) A \"dense\" representation where every entry is represented by\n *    a 6-bit integer.\n * 2) A \"sparse\" representation using run length compression suitable\n *    for representing HyperLogLogs with many registers set to 0 in\n *    a memory efficient way.\n *\n *\n * HLL header\n * ===\n *\n * Both the dense and sparse representation have a 16 byte header as follows:\n *\n * +------+---+-----+----------+\n * | HYLL | E | N/U | Cardin.  |\n * +------+---+-----+----------+\n *\n * The first 4 bytes are a magic string set to the bytes \"HYLL\".\n * \"E\" is one byte encoding, currently set to HLL_DENSE or\n * HLL_SPARSE. N/U are three not used bytes.\n *\n * The \"Cardin.\" field is a 64 bit integer stored in little endian format\n * with the latest cardinality computed that can be reused if the data\n * structure was not modified since the last computation (this is useful\n * because there are high probabilities that HLLADD operations don't\n * modify the actual data structure and hence the approximated cardinality).\n *\n * When the most significant bit in the most significant byte of the cached\n * cardinality is set, it means that the data structure was modified and\n * we can't reuse the cached value that must be recomputed.\n *\n * Dense representation\n * ===\n *\n * The dense representation used by Redis is the following:\n *\n * +--------+--------+--------+------//      //--+\n * |11000000|22221111|33333322|55444444 ....     |\n * +--------+--------+--------+------//      //--+\n *\n * The 6 bits counters are encoded one after the other starting from the\n * LSB to the MSB, and using the next bytes as needed.\n *\n * Sparse representation\n * ===\n *\n * The sparse representation encodes registers using a run length\n * encoding composed of three opcodes, two using one byte, and one using\n * of two bytes. The opcodes are called ZERO, XZERO and VAL.\n *\n * ZERO opcode is represented as 00xxxxxx. The 6-bit integer represented\n * by the six bits 'xxxxxx', plus 1, means that there are N registers set\n * to 0. This opcode can represent from 1 to 64 contiguous registers set\n * to the value of 0.\n *\n * XZERO opcode is represented by two bytes 01xxxxxx yyyyyyyy. The 14-bit\n * integer represented by the bits 'xxxxxx' as most significant bits and\n * 'yyyyyyyy' as least significant bits, plus 1, means that there are N\n * registers set to 0. This opcode can represent from 0 to 16384 contiguous\n * registers set to the value of 0.\n *\n * VAL opcode is represented as 1vvvvvxx. It contains a 5-bit integer\n * representing the value of a register, and a 2-bit integer representing\n * the number of contiguous registers set to that value 'vvvvv'.\n * To obtain the value and run length, the integers vvvvv and xx must be\n * incremented by one. This opcode can represent values from 1 to 32,\n * repeated from 1 to 4 times.\n *\n * The sparse representation can't represent registers with a value greater\n * than 32, however it is very unlikely that we find such a register in an\n * HLL with a cardinality where the sparse representation is still more\n * memory efficient than the dense representation. When this happens the\n * HLL is converted to the dense representation.\n *\n * The sparse representation is purely positional. For example a sparse\n * representation of an empty HLL is just: XZERO:16384.\n *\n * An HLL having only 3 non-zero registers at position 1000, 1020, 1021\n * respectively set to 2, 3, 3, is represented by the following three\n * opcodes:\n *\n * XZERO:1000 (Registers 0-999 are set to 0)\n * VAL:2,1    (1 register set to value 2, that is register 1000)\n * ZERO:19    (Registers 1001-1019 set to 0)\n * VAL:3,2    (2 registers set to value 3, that is registers 1020,1021)\n * XZERO:15362 (Registers 1022-16383 set to 0)\n *\n * In the example the sparse representation used just 7 bytes instead\n * of 12k in order to represent the HLL registers. In general for low\n * cardinality there is a big win in terms of space efficiency, traded\n * with CPU time since the sparse representation is slower to access:\n *\n * The following table shows average cardinality vs bytes used, 100\n * samples per cardinality (when the set was not representable because\n * of registers with too big value, the dense representation size was used\n * as a sample).\n *\n * 100 267\n * 200 485\n * 300 678\n * 400 859\n * 500 1033\n * 600 1205\n * 700 1375\n * 800 1544\n * 900 1713\n * 1000 1882\n * 2000 3480\n * 3000 4879\n * 4000 6089\n * 5000 7138\n * 6000 8042\n * 7000 8823\n * 8000 9500\n * 9000 10088\n * 10000 10591\n *\n * The dense representation uses 12288 bytes, so there is a big win up to\n * a cardinality of ~2000-3000. For bigger cardinalities the constant times\n * involved in updating the sparse representation is not justified by the\n * memory savings. The exact maximum length of the sparse representation\n * when this implementation switches to the dense representation is\n * configured via the define server.hll_sparse_max_bytes.\n */\n\nstruct hllhdr {\n    char magic[4];      /* \"HYLL\" */\n    uint8_t encoding;   /* HLL_DENSE or HLL_SPARSE. */\n    uint8_t notused[3]; /* Reserved for future use, must be zero. */\n    uint8_t card[8];    /* Cached cardinality, little endian. */\n    uint8_t registers[]; /* Data bytes. */\n};\n\n/* The cached cardinality MSB is used to signal validity of the cached value. */\n#define HLL_INVALIDATE_CACHE(hdr) (hdr)->card[7] |= (1<<7)\n#define HLL_VALID_CACHE(hdr) (((hdr)->card[7] & (1<<7)) == 0)\n\n#define HLL_P 14 /* The greater is P, the smaller the error. */\n#define HLL_Q (64-HLL_P) /* The number of bits of the hash value used for\n                            determining the number of leading zeros. */\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#define HLL_ALPHA_INF 0.721347520444481703680 /* constant for 0.5/ln(2) */\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    #ifdef USE_ALIGNED_ACCESS\n        memcpy(&k,data,sizeof(uint64_t));\n    #else\n        k = *((uint64_t*)data);\n    #endif\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; /* fall-thru */\n    case 6: h ^= (uint64_t)data[5] << 40; /* fall-thru */\n    case 5: h ^= (uint64_t)data[4] << 32; /* fall-thru */\n    case 4: h ^= (uint64_t)data[3] << 24; /* fall-thru */\n    case 3: h ^= (uint64_t)data[2] << 16; /* fall-thru */\n    case 2: h ^= (uint64_t)data[1] << 8; /* fall-thru */\n    case 1: h ^= (uint64_t)data[0];\n            h *= m; /* fall-thru */\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 = Q+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 >>= HLL_P; /* Remove bits used to address the register. */\n    hash |= ((uint64_t)1<<HLL_Q); /* Make sure the loop terminates\n                                     and count will be <= Q+1. */\n    bit = 1;\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/* Low level function to set the dense HLL register at 'index' to the\n * specified value if the current value is smaller than 'count'.\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 hllDenseSet(uint8_t *registers, long index, uint8_t count) {\n    uint8_t oldcount;\n\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/* \"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 * This is just a wrapper to hllDenseSet(), performing the hashing of the\n * element in order to retrieve the index and zero-run count. */\nint hllDenseAdd(uint8_t *registers, unsigned char *ele, size_t elesize) {\n    long index;\n    uint8_t count = hllPatLen(ele,elesize,&index);\n    /* Update the register if this element produced a longer run of zeroes. */\n    return hllDenseSet(registers,index,count);\n}\n\n/* Compute the register histogram in the dense representation. */\nvoid hllDenseRegHisto(uint8_t *registers, int* reghisto) {\n    int j;\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;\n            r1 = (r[0] >> 6 | r[1] << 2) & 63;\n            r2 = (r[1] >> 4 | r[2] << 4) & 63;\n            r3 = (r[2] >> 2) & 63;\n            r4 = r[3] & 63;\n            r5 = (r[3] >> 6 | r[4] << 2) & 63;\n            r6 = (r[4] >> 4 | r[5] << 4) & 63;\n            r7 = (r[5] >> 2) & 63;\n            r8 = r[6] & 63;\n            r9 = (r[6] >> 6 | r[7] << 2) & 63;\n            r10 = (r[7] >> 4 | r[8] << 4) & 63;\n            r11 = (r[8] >> 2) & 63;\n            r12 = r[9] & 63;\n            r13 = (r[9] >> 6 | r[10] << 2) & 63;\n            r14 = (r[10] >> 4 | r[11] << 4) & 63;\n            r15 = (r[11] >> 2) & 63;\n\n            reghisto[r0]++;\n            reghisto[r1]++;\n            reghisto[r2]++;\n            reghisto[r3]++;\n            reghisto[r4]++;\n            reghisto[r5]++;\n            reghisto[r6]++;\n            reghisto[r7]++;\n            reghisto[r8]++;\n            reghisto[r9]++;\n            reghisto[r10]++;\n            reghisto[r11]++;\n            reghisto[r12]++;\n            reghisto[r13]++;\n            reghisto[r14]++;\n            reghisto[r15]++;\n\n            r += 12;\n        }\n    } else {\n        for(j = 0; j < HLL_REGISTERS; j++) {\n            unsigned long reg;\n            HLL_DENSE_GET_REGISTER(reg,registers,j);\n            reghisto[reg]++;\n        }\n    }\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 C_OK if the sparse representation was valid,\n * otherwise C_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 C_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            if ((runlen + idx) > HLL_REGISTERS) break; /* Overflow. */\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 C_ERR;\n    }\n\n    /* Free the old representation and set the new one. */\n    sdsfree(o->ptr);\n    o->ptr = dense;\n    return C_OK;\n}\n\n/* Low level function to set the sparse HLL register at 'index' to the\n * specified value if the current value is smaller than 'count'.\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 hllSparseSet(robj *o, long index, uint8_t count) {\n    struct hllhdr *hdr;\n    uint8_t oldcount, *sparse, *end, *p, *prev, *next;\n    long first, span;\n    long is_zero = 0, is_xzero = 0, is_val = 0, runlen = 0;\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 previous 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 || p >= end) 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 multiple\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) == C_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     * conversion, it will be performed in all the slaves as well. */\n    int dense_retval = hllDenseSet(hdr->registers,index,count);\n    serverAssert(dense_retval == 1);\n    return dense_retval;\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 * This function is actually a wrapper for hllSparseSet(), it only performs\n * the hashshing of the elmenet to obtain the index and zeros run length. */\nint hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) {\n    long index;\n    uint8_t count = hllPatLen(ele,elesize,&index);\n    /* Update the register if this element produced a longer run of zeroes. */\n    return hllSparseSet(o,index,count);\n}\n\n/* Compute the register histogram in the sparse representation. */\nvoid hllSparseRegHisto(uint8_t *sparse, int sparselen, int *invalid, int* reghisto) {\n    int 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            reghisto[0] += runlen;\n            p++;\n        } else if (HLL_SPARSE_IS_XZERO(p)) {\n            runlen = HLL_SPARSE_XZERO_LEN(p);\n            idx += runlen;\n            reghisto[0] += runlen;\n            p += 2;\n        } else {\n            runlen = HLL_SPARSE_VAL_LEN(p);\n            regval = HLL_SPARSE_VAL_VALUE(p);\n            idx += runlen;\n            reghisto[regval] += runlen;\n            p++;\n        }\n    }\n    if (idx != HLL_REGISTERS && invalid) *invalid = 1;\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 hllDenseRegHisto() and hllSparseRegHisto()\n * functions as helpers to compute histogram of register values part of the\n * computation, which is representation-specific, while all the rest is common. */\n\n/* Implements the register histogram calculation for uint8_t data type\n * which is only used internally as speedup for PFCOUNT with multiple keys. */\nvoid hllRawRegHisto(uint8_t *registers, int* reghisto) {\n    uint64_t *word = (uint64_t*) registers;\n    uint8_t *bytes;\n    int j;\n\n    for (j = 0; j < HLL_REGISTERS/8; j++) {\n        if (*word == 0) {\n            reghisto[0] += 8;\n        } else {\n            bytes = (uint8_t*) word;\n            reghisto[bytes[0]]++;\n            reghisto[bytes[1]]++;\n            reghisto[bytes[2]]++;\n            reghisto[bytes[3]]++;\n            reghisto[bytes[4]]++;\n            reghisto[bytes[5]]++;\n            reghisto[bytes[6]]++;\n            reghisto[bytes[7]]++;\n        }\n        word++;\n    }\n}\n\n/* Helper function sigma as defined in\n * \"New cardinality estimation algorithms for HyperLogLog sketches\"\n * Otmar Ertl, arXiv:1702.01284 */\ndouble hllSigma(double x) {\n    if (x == 1.) return INFINITY;\n    double zPrime;\n    double y = 1;\n    double z = x;\n    do {\n        x *= x;\n        zPrime = z;\n        z += x * y;\n        y += y;\n    } while(zPrime != z);\n    return z;\n}\n\n/* Helper function tau as defined in\n * \"New cardinality estimation algorithms for HyperLogLog sketches\"\n * Otmar Ertl, arXiv:1702.01284 */\ndouble hllTau(double x) {\n    if (x == 0. || x == 1.) return 0.;\n    double zPrime;\n    double y = 1.0;\n    double z = 1 - x;\n    do {\n        x = sqrt(x);\n        zPrime = z;\n        y *= 0.5;\n        z -= pow(1 - x, 2)*y;\n    } while(zPrime != z);\n    return z / 3;\n}\n\n/* Return the approximated cardinality of the set based on the harmonic\n * mean of the registers values. 'hdr' points to the start of the SDS\n * representing the String object holding the HLL representation.\n *\n * If the sparse representation of the HLL object is not valid, the integer\n * pointed by 'invalid' is set to non-zero, otherwise it is left untouched.\n *\n * hllCount() supports a special internal-only encoding of HLL_RAW, that\n * is, hdr->registers will point to an uint8_t array of HLL_REGISTERS element.\n * This is useful in order to speedup PFCOUNT when called against multiple\n * keys (no need to work with 6-bit integers encoding). */\nuint64_t hllCount(struct hllhdr *hdr, int *invalid) {\n    double m = HLL_REGISTERS;\n    double E;\n    int j;\n    /* Note that reghisto size could be just HLL_Q+2, becuase HLL_Q+1 is\n     * the maximum frequency of the \"000...1\" sequence the hash function is\n     * able to return. However it is slow to check for sanity of the\n     * input: instead we history array at a safe size: overflows will\n     * just write data to wrong, but correctly allocated, places. */\n    int reghisto[64] = {0};\n\n    /* Compute register histogram */\n    if (hdr->encoding == HLL_DENSE) {\n        hllDenseRegHisto(hdr->registers,reghisto);\n    } else if (hdr->encoding == HLL_SPARSE) {\n        hllSparseRegHisto(hdr->registers,\n                         sdslen((sds)hdr)-HLL_HDR_SIZE,invalid,reghisto);\n    } else if (hdr->encoding == HLL_RAW) {\n        hllRawRegHisto(hdr->registers,reghisto);\n    } else {\n        serverPanic(\"Unknown HyperLogLog encoding in hllCount()\");\n    }\n\n    /* Estimate cardinality form register histogram. See:\n     * \"New cardinality estimation algorithms for HyperLogLog sketches\"\n     * Otmar Ertl, arXiv:1702.01284 */\n    double z = m * hllTau((m-reghisto[HLL_Q+1])/(double)m);\n    for (j = HLL_Q; j >= 1; --j) {\n        z += reghisto[j];\n        z *= 0.5;\n    }\n    z += m * hllSigma(reghisto[0]/(double)m);\n    E = llroundl(HLL_ALPHA_INF*m*m/z);\n\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, C_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                if ((runlen + i) > HLL_REGISTERS) break; /* Overflow. */\n                while(runlen--) {\n                    if (regval > max[i]) max[i] = regval;\n                    i++;\n                }\n                p++;\n            }\n        }\n        if (i != HLL_REGISTERS) return C_ERR;\n    }\n    return C_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    serverAssert((p-(uint8_t*)s) == sparselen);\n\n    /* Create the actual object. */\n    o = createObject(OBJ_STRING,s);\n    hdr = o->ptr;\n    memcpy(hdr->magic,\"HYLL\",4);\n    hdr->encoding = HLL_SPARSE;\n    return o;\n}\n\n/* Check if the object is a String with a valid HLL representation.\n * Return C_OK if this is true, otherwise reply to the client\n * with an error and return C_ERR. */\nint isHLLObjectOrReply(client *c, robj *o) {\n    struct hllhdr *hdr;\n\n    /* Key exists, check type */\n    if (checkType(c,o,OBJ_STRING))\n        return C_ERR; /* Error already sent. */\n\n    if (!sdsEncodedObject(o)) goto invalid;\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 C_OK;\n\ninvalid:\n    addReplySds(c,\n        sdsnew(\"-WRONGTYPE Key is not a valid \"\n               \"HyperLogLog string value.\\r\\n\"));\n    return C_ERR;\n}\n\n/* PFADD var ele ele ele ... ele => :0 or :1 */\nvoid pfaddCommand(client *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) != C_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,c->db,c->argv[1]);\n        notifyKeyspaceEvent(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(client *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) != C_OK) return;\n\n            /* Merge with this HLL with our 'max' HLL by setting max[i]\n             * to MAX(max[i],hll[i]). */\n            if (hllMerge(registers,o) == C_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 = lookupKeyWrite(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) != C_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,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(client *c) {\n    uint8_t max[HLL_REGISTERS];\n    struct hllhdr *hdr;\n    int j;\n    int use_dense = 0; /* Use dense representation as target? */\n\n    /* Compute an HLL with M[i] = MAX(M[i]_j).\n     * We store 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) != C_OK) return;\n\n        /* If at least one involved HLL is dense, use the dense representation\n         * as target ASAP to save time and avoid the conversion step. */\n        hdr = o->ptr;\n        if (hdr->encoding == HLL_DENSE) use_dense = 1;\n\n        /* Merge with this HLL with our 'max' HLL by setting max[i]\n         * to MAX(max[i],hll[i]). */\n        if (hllMerge(max,o) == C_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    /* Convert the destination object to dense representation if at least\n     * one of the inputs was dense. */\n    if (use_dense && hllSparseToDense(o) == C_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    for (j = 0; j < HLL_REGISTERS; j++) {\n        if (max[j] == 0) continue;\n        hdr = o->ptr;\n        switch(hdr->encoding) {\n        case HLL_DENSE: hllDenseSet(hdr->registers,j,max[j]); break;\n        case HLL_SPARSE: hllSparseSet(o,j,max[j]); break;\n        }\n    }\n    hdr = o->ptr; /* o->ptr may be different now, as a side effect of\n                     last hllSparseSet() call. */\n    HLL_INVALIDATE_CACHE(hdr);\n\n    signalModifiedKey(c,c->db,c->argv[1]);\n    /* We generate a PFADD event for PFMERGE for semantical simplicity\n     * since in theory this is a mass-add of elements. */\n    notifyKeyspaceEvent(NOTIFY_STRING,\"pfadd\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c,shared.ok);\n}\n\n/* ========================== Testing / Debugging  ========================== */\n\n/* PFSELFTEST\n * This command performs a self-test of the HLL registers implementation.\n * Something that is not easy to test from within the outside. */\n#define HLL_TEST_CYCLES 1000\nvoid pfselftestCommand(client *c) {\n    unsigned int j, i;\n    sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE);\n    struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2;\n    robj *o = NULL;\n    uint8_t bytecounters[HLL_REGISTERS];\n\n    /* Test 1: access registers.\n     * The test is conceived to test that the different counters of our data\n     * structure are accessible and that setting their values both result in\n     * the correct value to be retained and not affect adjacent values. */\n    for (j = 0; j < HLL_TEST_CYCLES; j++) {\n        /* Set the HLL counters and an array of unsigned byes of the\n         * same size to the same set of random values. */\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            unsigned int r = rand() & HLL_REGISTER_MAX;\n\n            bytecounters[i] = r;\n            HLL_DENSE_SET_REGISTER(hdr->registers,i,r);\n        }\n        /* Check that we are able to retrieve the same values. */\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            unsigned int val;\n\n            HLL_DENSE_GET_REGISTER(val,hdr->registers,i);\n            if (val != bytecounters[i]) {\n                addReplyErrorFormat(c,\n                    \"TESTFAILED Register %d should be %d but is %d\",\n                    i, (int) bytecounters[i], (int) val);\n                goto cleanup;\n            }\n        }\n    }\n\n    /* Test 2: approximation error.\n     * The test adds unique elements and check that the estimated value\n     * is always reasonable bounds.\n     *\n     * We check that the error is smaller than a few times than the expected\n     * standard error, to make it very unlikely for the test to fail because\n     * of a \"bad\" run.\n     *\n     * The test is performed with both dense and sparse HLLs at the same\n     * time also verifying that the computed cardinality is the same. */\n    memset(hdr->registers,0,HLL_DENSE_SIZE-HLL_HDR_SIZE);\n    o = createHLLObject();\n    double relerr = 1.04/sqrt(HLL_REGISTERS);\n    int64_t checkpoint = 1;\n    uint64_t seed = (uint64_t)rand() | (uint64_t)rand() << 32;\n    uint64_t ele;\n    for (j = 1; j <= 10000000; j++) {\n        ele = j ^ seed;\n        hllDenseAdd(hdr->registers,(unsigned char*)&ele,sizeof(ele));\n        hllAdd(o,(unsigned char*)&ele,sizeof(ele));\n\n        /* Make sure that for small cardinalities we use sparse\n         * encoding. */\n        if (j == checkpoint && j < server.hll_sparse_max_bytes/2) {\n            hdr2 = o->ptr;\n            if (hdr2->encoding != HLL_SPARSE) {\n                addReplyError(c, \"TESTFAILED sparse encoding not used\");\n                goto cleanup;\n            }\n        }\n\n        /* Check that dense and sparse representations agree. */\n        if (j == checkpoint && hllCount(hdr,NULL) != hllCount(o->ptr,NULL)) {\n                addReplyError(c, \"TESTFAILED dense/sparse disagree\");\n                goto cleanup;\n        }\n\n        /* Check error. */\n        if (j == checkpoint) {\n            int64_t abserr = checkpoint - (int64_t)hllCount(hdr,NULL);\n            uint64_t maxerr = ceil(relerr*6*checkpoint);\n\n            /* Adjust the max error we expect for cardinality 10\n             * since from time to time it is statistically likely to get\n             * much higher error due to collision, resulting into a false\n             * positive. */\n            if (j == 10) maxerr = 1;\n\n            if (abserr < 0) abserr = -abserr;\n            if (abserr > (int64_t)maxerr) {\n                addReplyErrorFormat(c,\n                    \"TESTFAILED Too big error. card:%llu abserr:%llu\",\n                    (unsigned long long) checkpoint,\n                    (unsigned long long) abserr);\n                goto cleanup;\n            }\n            checkpoint *= 10;\n        }\n    }\n\n    /* Success! */\n    addReply(c,shared.ok);\n\ncleanup:\n    sdsfree(bitcounters);\n    if (o) decrRefCount(o);\n}\n\n/* PFDEBUG <subcommand> <key> ... args ...\n * Different debugging related operations about the HLL implementation. */\nvoid pfdebugCommand(client *c) {\n    char *cmd = c->argv[1]->ptr;\n    struct hllhdr *hdr;\n    robj *o;\n    int j;\n\n    o = lookupKeyWrite(c->db,c->argv[2]);\n    if (o == NULL) {\n        addReplyError(c,\"The specified key does not exist\");\n        return;\n    }\n    if (isHLLObjectOrReply(c,o) != C_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) == C_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        addReplyArrayLen(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            sdsfree(decoded);\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) == C_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": "Chapter02/redis-6.0.3/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#define INTSET_ENC_INT16 (sizeof(int16_t))\n#define INTSET_ENC_INT32 (sizeof(int32_t))\n#define INTSET_ENC_INT64 (sizeof(int64_t))\n\n/* Return the required encoding for the provided value. */\nstatic uint8_t _intsetValueEncoding(int64_t v) {\n    if (v < INT32_MIN || v > INT32_MAX)\n        return INTSET_ENC_INT64;\n    else if (v < INT16_MIN || v > INT16_MAX)\n        return INTSET_ENC_INT32;\n    else\n        return INTSET_ENC_INT16;\n}\n\n/* Return the value at pos, given an encoding. */\nstatic int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {\n    int64_t v64;\n    int32_t v32;\n    int16_t v16;\n\n    if (enc == INTSET_ENC_INT64) {\n        memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));\n        memrev64ifbe(&v64);\n        return v64;\n    } else if (enc == INTSET_ENC_INT32) {\n        memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));\n        memrev32ifbe(&v32);\n        return v32;\n    } else {\n        memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));\n        memrev16ifbe(&v16);\n        return v16;\n    }\n}\n\n/* Return the value at pos, using the configured encoding. */\nstatic int64_t _intsetGet(intset *is, int pos) {\n    return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding));\n}\n\n/* Set the value at pos, using the configured encoding. */\nstatic void _intsetSet(intset *is, int pos, int64_t value) {\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        ((int64_t*)is->contents)[pos] = value;\n        memrev64ifbe(((int64_t*)is->contents)+pos);\n    } else if (encoding == INTSET_ENC_INT32) {\n        ((int32_t*)is->contents)[pos] = value;\n        memrev32ifbe(((int32_t*)is->contents)+pos);\n    } else {\n        ((int16_t*)is->contents)[pos] = value;\n        memrev16ifbe(((int16_t*)is->contents)+pos);\n    }\n}\n\n/* Create an empty intset. */\nintset *intsetNew(void) {\n    intset *is = zmalloc(sizeof(intset));\n    is->encoding = intrev32ifbe(INTSET_ENC_INT16);\n    is->length = 0;\n    return is;\n}\n\n/* Resize the intset */\nstatic intset *intsetResize(intset *is, uint32_t len) {\n    uint32_t size = len*intrev32ifbe(is->encoding);\n    is = zrealloc(is,sizeof(intset)+size);\n    return is;\n}\n\n/* Search for the position of \"value\". Return 1 when the value was found and\n * sets \"pos\" to the position of the value within the intset. Return 0 when\n * the value is not present in the intset and sets \"pos\" to the position\n * where \"value\" can be inserted. */\nstatic uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {\n    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;\n    int64_t cur = -1;\n\n    /* The value can never be found when the set is empty */\n    if (intrev32ifbe(is->length) == 0) {\n        if (pos) *pos = 0;\n        return 0;\n    } else {\n        /* Check for the case where we know we cannot find the value,\n         * but do know the insert position. */\n        if (value > _intsetGet(is,max)) {\n            if (pos) *pos = intrev32ifbe(is->length);\n            return 0;\n        } else if (value < _intsetGet(is,0)) {\n            if (pos) *pos = 0;\n            return 0;\n        }\n    }\n\n    while(max >= min) {\n        mid = ((unsigned int)min + (unsigned int)max) >> 1;\n        cur = _intsetGet(is,mid);\n        if (value > cur) {\n            min = mid+1;\n        } else if (value < cur) {\n            max = mid-1;\n        } else {\n            break;\n        }\n    }\n\n    if (value == cur) {\n        if (pos) *pos = mid;\n        return 1;\n    } else {\n        if (pos) *pos = min;\n        return 0;\n    }\n}\n\n/* Upgrades the intset to a larger encoding and inserts the given integer. */\nstatic intset *intsetUpgradeAndAdd(intset *is, int64_t value) {\n    uint8_t curenc = intrev32ifbe(is->encoding);\n    uint8_t newenc = _intsetValueEncoding(value);\n    int length = intrev32ifbe(is->length);\n    int prepend = value < 0 ? 1 : 0;\n\n    /* First set new encoding and resize */\n    is->encoding = intrev32ifbe(newenc);\n    is = intsetResize(is,intrev32ifbe(is->length)+1);\n\n    /* Upgrade back-to-front so we don't overwrite values.\n     * Note that the \"prepend\" variable is used to make sure we have an empty\n     * space at either the beginning or the end of the intset. */\n    while(length--)\n        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));\n\n    /* Set the value at the beginning or the end. */\n    if (prepend)\n        _intsetSet(is,0,value);\n    else\n        _intsetSet(is,intrev32ifbe(is->length),value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\nstatic void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {\n    void *src, *dst;\n    uint32_t bytes = intrev32ifbe(is->length)-from;\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        src = (int64_t*)is->contents+from;\n        dst = (int64_t*)is->contents+to;\n        bytes *= sizeof(int64_t);\n    } else if (encoding == INTSET_ENC_INT32) {\n        src = (int32_t*)is->contents+from;\n        dst = (int32_t*)is->contents+to;\n        bytes *= sizeof(int32_t);\n    } else {\n        src = (int16_t*)is->contents+from;\n        dst = (int16_t*)is->contents+to;\n        bytes *= sizeof(int16_t);\n    }\n    memmove(dst,src,bytes);\n}\n\n/* Insert an integer in the intset */\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 1;\n\n    /* Upgrade encoding if necessary. If we need to upgrade, we know that\n     * this value should be either appended (if > 0) or prepended (if < 0),\n     * because it lies outside the range of existing values. */\n    if (valenc > intrev32ifbe(is->encoding)) {\n        /* This always succeeds, so we don't need to curry *success. */\n        return intsetUpgradeAndAdd(is,value);\n    } else {\n        /* Abort if the value is already present in the set.\n         * This call will populate \"pos\" with the right position to insert\n         * the value when it cannot be found. */\n        if (intsetSearch(is,value,&pos)) {\n            if (success) *success = 0;\n            return is;\n        }\n\n        is = intsetResize(is,intrev32ifbe(is->length)+1);\n        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);\n    }\n\n    _intsetSet(is,pos,value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\n/* Delete integer from intset */\nintset *intsetRemove(intset *is, int64_t value, int *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 0;\n\n    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {\n        uint32_t len = intrev32ifbe(is->length);\n\n        /* We know we can delete */\n        if (success) *success = 1;\n\n        /* Overwrite value with tail and update length */\n        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);\n        is = intsetResize(is,len-1);\n        is->length = intrev32ifbe(len-1);\n    }\n    return is;\n}\n\n/* Determine whether a value belongs to this set */\nuint8_t intsetFind(intset *is, int64_t value) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);\n}\n\n/* Return random member */\nint64_t intsetRandom(intset *is) {\n    return _intsetGet(is,rand()%intrev32ifbe(is->length));\n}\n\n/* Get the value at the given position. When this position is\n * out of range the function returns 0, when in range it returns 1. */\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {\n    if (pos < intrev32ifbe(is->length)) {\n        *value = _intsetGet(is,pos);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return intset length */\nuint32_t intsetLen(const intset *is) {\n    return intrev32ifbe(is->length);\n}\n\n/* Return intset blob size in bytes. */\nsize_t intsetBlobLen(intset *is) {\n    return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);\n}\n\n#ifdef REDIS_TEST\n#include <sys/time.h>\n#include <time.h>\n\n#if 0\nstatic void intsetRepr(intset *is) {\n    for (uint32_t i = 0; i < intrev32ifbe(is->length); i++) {\n        printf(\"%lld\\n\", (uint64_t)_intsetGet(is,i));\n    }\n    printf(\"\\n\");\n}\n\nstatic void error(char *err) {\n    printf(\"%s\\n\", err);\n    exit(1);\n}\n#endif\n\nstatic void ok(void) {\n    printf(\"OK\\n\");\n}\n\nstatic long long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\n#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))\nstatic void _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\nstatic intset *createSet(int bits, int size) {\n    uint64_t mask = (1<<bits)-1;\n    uint64_t value;\n    intset *is = intsetNew();\n\n    for (int 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\nstatic void checkConsistency(intset *is) {\n    for (uint32_t 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\n#define UNUSED(x) (void)(x)\nint intsetTest(int argc, char **argv) {\n    uint8_t success;\n    int i;\n    intset *is;\n    srand(time(NULL));\n\n    UNUSED(argc);\n    UNUSED(argv);\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) ==\n                    INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(+9223372036854775807ull) ==\n                    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        uint32_t 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\",\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    return 0;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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    uint32_t encoding;\n    uint32_t length;\n    int8_t contents[];\n} intset;\n\nintset *intsetNew(void);\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success);\nintset *intsetRemove(intset *is, int64_t value, int *success);\nuint8_t intsetFind(intset *is, int64_t value);\nint64_t intsetRandom(intset *is);\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);\nuint32_t intsetLen(const intset *is);\nsize_t intsetBlobLen(intset *is);\n\n#ifdef REDIS_TEST\nint intsetTest(int argc, char *argv[]);\n#endif\n\n#endif // __INTSET_H\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/latency.c",
    "content": "/* The latency monitor allows to easily observe the sources of latency\n * in a Redis instance using the LATENCY command. Different latency\n * sources are monitored, like disk I/O, execution of commands, fork\n * system call, and so forth.\n *\n * ----------------------------------------------------------------------------\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 \"server.h\"\n\n/* Dictionary type for latency events. */\nint dictStringKeyCompare(void *privdata, const void *key1, const void *key2) {\n    UNUSED(privdata);\n    return strcmp(key1,key2) == 0;\n}\n\nuint64_t dictStringHash(const void *key) {\n    return dictGenHashFunction(key, strlen(key));\n}\n\nvoid dictVanillaFree(void *privdata, void *val);\n\ndictType latencyTimeSeriesDictType = {\n    dictStringHash,             /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictStringKeyCompare,       /* key compare */\n    dictVanillaFree,            /* key destructor */\n    dictVanillaFree             /* val destructor */\n};\n\n/* ------------------------- Utility functions ------------------------------ */\n\n#ifdef __linux__\n/* Returns 1 if Transparent Huge Pages support is enabled in the kernel.\n * Otherwise (or if we are unable to check) 0 is returned. */\nint THPIsEnabled(void) {\n    char buf[1024];\n\n    FILE *fp = fopen(\"/sys/kernel/mm/transparent_hugepage/enabled\",\"r\");\n    if (!fp) return 0;\n    if (fgets(buf,sizeof(buf),fp) == NULL) {\n        fclose(fp);\n        return 0;\n    }\n    fclose(fp);\n    return (strstr(buf,\"[never]\") == NULL) ? 1 : 0;\n}\n#endif\n\n/* Report the amount of AnonHugePages in smap, in bytes. If the return\n * value of the function is non-zero, the process is being targeted by\n * THP support, and is likely to have memory usage / latency issues. */\nint THPGetAnonHugePagesSize(void) {\n    return zmalloc_get_smap_bytes_by_field(\"AnonHugePages:\",-1);\n}\n\n/* ---------------------------- Latency API --------------------------------- */\n\n/* Latency monitor initialization. We just need to create the dictionary\n * of time series, each time serie is created on demand in order to avoid\n * having a fixed list to maintain. */\nvoid latencyMonitorInit(void) {\n    server.latency_events = dictCreate(&latencyTimeSeriesDictType,NULL);\n}\n\n/* Add the specified sample to the specified time series \"event\".\n * This function is usually called via latencyAddSampleIfNeeded(), that\n * is a macro that only adds the sample if the latency is higher than\n * server.latency_monitor_threshold. */\nvoid latencyAddSample(const char *event, mstime_t latency) {\n    struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);\n    time_t now = time(NULL);\n    int prev;\n\n    /* Create the time series if it does not exist. */\n    if (ts == NULL) {\n        ts = zmalloc(sizeof(*ts));\n        ts->idx = 0;\n        ts->max = 0;\n        memset(ts->samples,0,sizeof(ts->samples));\n        dictAdd(server.latency_events,zstrdup(event),ts);\n    }\n\n    if (latency > ts->max) ts->max = latency;\n\n    /* If the previous sample is in the same second, we update our old sample\n     * if this latency is > of the old one, or just return. */\n    prev = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;\n    if (ts->samples[prev].time == now) {\n        if (latency > ts->samples[prev].latency)\n            ts->samples[prev].latency = latency;\n        return;\n    }\n\n    ts->samples[ts->idx].time = time(NULL);\n    ts->samples[ts->idx].latency = latency;\n\n    ts->idx++;\n    if (ts->idx == LATENCY_TS_LEN) ts->idx = 0;\n}\n\n/* Reset data for the specified event, or all the events data if 'event' is\n * NULL.\n *\n * Note: this is O(N) even when event_to_reset is not NULL because makes\n * the code simpler and we have a small fixed max number of events. */\nint latencyResetEvent(char *event_to_reset) {\n    dictIterator *di;\n    dictEntry *de;\n    int resets = 0;\n\n    di = dictGetSafeIterator(server.latency_events);\n    while((de = dictNext(di)) != NULL) {\n        char *event = dictGetKey(de);\n\n        if (event_to_reset == NULL || strcasecmp(event,event_to_reset) == 0) {\n            dictDelete(server.latency_events, event);\n            resets++;\n        }\n    }\n    dictReleaseIterator(di);\n    return resets;\n}\n\n/* ------------------------ Latency reporting (doctor) ---------------------- */\n\n/* Analyze the samples available for a given event and return a structure\n * populate with different metrics, average, MAD, min, max, and so forth.\n * Check latency.h definition of struct latenctStat for more info.\n * If the specified event has no elements the structure is populate with\n * zero values. */\nvoid analyzeLatencyForEvent(char *event, struct latencyStats *ls) {\n    struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);\n    int j;\n    uint64_t sum;\n\n    ls->all_time_high = ts ? ts->max : 0;\n    ls->avg = 0;\n    ls->min = 0;\n    ls->max = 0;\n    ls->mad = 0;\n    ls->samples = 0;\n    ls->period = 0;\n    if (!ts) return;\n\n    /* First pass, populate everything but the MAD. */\n    sum = 0;\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        if (ts->samples[j].time == 0) continue;\n        ls->samples++;\n        if (ls->samples == 1) {\n            ls->min = ls->max = ts->samples[j].latency;\n        } else {\n            if (ls->min > ts->samples[j].latency)\n                ls->min = ts->samples[j].latency;\n            if (ls->max < ts->samples[j].latency)\n                ls->max = ts->samples[j].latency;\n        }\n        sum += ts->samples[j].latency;\n\n        /* Track the oldest event time in ls->period. */\n        if (ls->period == 0 || ts->samples[j].time < ls->period)\n            ls->period = ts->samples[j].time;\n    }\n\n    /* So far avg is actually the sum of the latencies, and period is\n     * the oldest event time. We need to make the first an average and\n     * the second a range of seconds. */\n    if (ls->samples) {\n        ls->avg = sum / ls->samples;\n        ls->period = time(NULL) - ls->period;\n        if (ls->period == 0) ls->period = 1;\n    }\n\n    /* Second pass, compute MAD. */\n    sum = 0;\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        int64_t delta;\n\n        if (ts->samples[j].time == 0) continue;\n        delta = (int64_t)ls->avg - ts->samples[j].latency;\n        if (delta < 0) delta = -delta;\n        sum += delta;\n    }\n    if (ls->samples) ls->mad = sum / ls->samples;\n}\n\n/* Create a human readable report of latency events for this Redis instance. */\nsds createLatencyReport(void) {\n    sds report = sdsempty();\n    int advise_better_vm = 0;       /* Better virtual machines. */\n    int advise_slowlog_enabled = 0; /* Enable slowlog. */\n    int advise_slowlog_tuning = 0;  /* Reconfigure slowlog. */\n    int advise_slowlog_inspect = 0; /* Check your slowlog. */\n    int advise_disk_contention = 0; /* Try to lower disk contention. */\n    int advise_scheduler = 0;       /* Intrinsic latency. */\n    int advise_data_writeback = 0;  /* data=writeback. */\n    int advise_no_appendfsync = 0;  /* don't fsync during rewrites. */\n    int advise_local_disk = 0;      /* Avoid remote disks. */\n    int advise_ssd = 0;             /* Use an SSD drive. */\n    int advise_write_load_info = 0; /* Print info about AOF and write load. */\n    int advise_hz = 0;              /* Use higher HZ. */\n    int advise_large_objects = 0;   /* Deletion of large objects. */\n    int advise_mass_eviction = 0;   /* Avoid mass eviction of keys. */\n    int advise_relax_fsync_policy = 0; /* appendfsync always is slow. */\n    int advise_disable_thp = 0;     /* AnonHugePages detected. */\n    int advices = 0;\n\n    /* Return ASAP if the latency engine is disabled and it looks like it\n     * was never enabled so far. */\n    if (dictSize(server.latency_events) == 0 &&\n        server.latency_monitor_threshold == 0)\n    {\n        report = sdscat(report,\"I'm sorry, Dave, I can't do that. Latency monitoring is disabled in this Redis instance. You may use \\\"CONFIG SET latency-monitor-threshold <milliseconds>.\\\" in order to enable it. If we weren't in a deep space mission I'd suggest to take a look at http://redis.io/topics/latency-monitor.\\n\");\n        return report;\n    }\n\n    /* Show all the events stats and add for each event some event-related\n     * comment depending on the values. */\n    dictIterator *di;\n    dictEntry *de;\n    int eventnum = 0;\n\n    di = dictGetSafeIterator(server.latency_events);\n    while((de = dictNext(di)) != NULL) {\n        char *event = dictGetKey(de);\n        struct latencyTimeSeries *ts = dictGetVal(de);\n        struct latencyStats ls;\n\n        if (ts == NULL) continue;\n        eventnum++;\n        if (eventnum == 1) {\n            report = sdscat(report,\"Dave, I have observed latency spikes in this Redis instance. You don't mind talking about it, do you Dave?\\n\\n\");\n        }\n        analyzeLatencyForEvent(event,&ls);\n\n        report = sdscatprintf(report,\n            \"%d. %s: %d latency spikes (average %lums, mean deviation %lums, period %.2f sec). Worst all time event %lums.\",\n            eventnum, event,\n            ls.samples,\n            (unsigned long) ls.avg,\n            (unsigned long) ls.mad,\n            (double) ls.period/ls.samples,\n            (unsigned long) ts->max);\n\n        /* Fork */\n        if (!strcasecmp(event,\"fork\")) {\n            char *fork_quality;\n            if (server.stat_fork_rate < 10) {\n                fork_quality = \"terrible\";\n                advise_better_vm = 1;\n                advices++;\n            } else if (server.stat_fork_rate < 25) {\n                fork_quality = \"poor\";\n                advise_better_vm = 1;\n                advices++;\n            } else if (server.stat_fork_rate < 100) {\n                fork_quality = \"good\";\n            } else {\n                fork_quality = \"excellent\";\n            }\n            report = sdscatprintf(report,\n                \" Fork rate is %.2f GB/sec (%s).\", server.stat_fork_rate,\n                fork_quality);\n        }\n\n        /* Potentially commands. */\n        if (!strcasecmp(event,\"command\")) {\n            if (server.slowlog_log_slower_than < 0) {\n                advise_slowlog_enabled = 1;\n                advices++;\n            } else if (server.slowlog_log_slower_than/1000 >\n                       server.latency_monitor_threshold)\n            {\n                advise_slowlog_tuning = 1;\n                advices++;\n            }\n            advise_slowlog_inspect = 1;\n            advise_large_objects = 1;\n            advices += 2;\n        }\n\n        /* fast-command. */\n        if (!strcasecmp(event,\"fast-command\")) {\n            advise_scheduler = 1;\n            advices++;\n        }\n\n        /* AOF and I/O. */\n        if (!strcasecmp(event,\"aof-write-pending-fsync\")) {\n            advise_local_disk = 1;\n            advise_disk_contention = 1;\n            advise_ssd = 1;\n            advise_data_writeback = 1;\n            advices += 4;\n        }\n\n        if (!strcasecmp(event,\"aof-write-active-child\")) {\n            advise_no_appendfsync = 1;\n            advise_data_writeback = 1;\n            advise_ssd = 1;\n            advices += 3;\n        }\n\n        if (!strcasecmp(event,\"aof-write-alone\")) {\n            advise_local_disk = 1;\n            advise_data_writeback = 1;\n            advise_ssd = 1;\n            advices += 3;\n        }\n\n        if (!strcasecmp(event,\"aof-fsync-always\")) {\n            advise_relax_fsync_policy = 1;\n            advices++;\n        }\n\n        if (!strcasecmp(event,\"aof-fstat\") ||\n            !strcasecmp(event,\"rdb-unlik-temp-file\")) {\n            advise_disk_contention = 1;\n            advise_local_disk = 1;\n            advices += 2;\n        }\n\n        if (!strcasecmp(event,\"aof-rewrite-diff-write\") ||\n            !strcasecmp(event,\"aof-rename\")) {\n            advise_write_load_info = 1;\n            advise_data_writeback = 1;\n            advise_ssd = 1;\n            advise_local_disk = 1;\n            advices += 4;\n        }\n\n        /* Expire cycle. */\n        if (!strcasecmp(event,\"expire-cycle\")) {\n            advise_hz = 1;\n            advise_large_objects = 1;\n            advices += 2;\n        }\n\n        /* Eviction cycle. */\n        if (!strcasecmp(event,\"eviction-del\")) {\n            advise_large_objects = 1;\n            advices++;\n        }\n\n        if (!strcasecmp(event,\"eviction-cycle\")) {\n            advise_mass_eviction = 1;\n            advices++;\n        }\n\n        report = sdscatlen(report,\"\\n\",1);\n    }\n    dictReleaseIterator(di);\n\n    /* Add non event based advices. */\n    if (THPGetAnonHugePagesSize() > 0) {\n        advise_disable_thp = 1;\n        advices++;\n    }\n\n    if (eventnum == 0 && advices == 0) {\n        report = sdscat(report,\"Dave, no latency spike was observed during the lifetime of this Redis instance, not in the slightest bit. I honestly think you ought to sit down calmly, take a stress pill, and think things over.\\n\");\n    } else if (eventnum > 0 && advices == 0) {\n        report = sdscat(report,\"\\nWhile there are latency events logged, I'm not able to suggest any easy fix. Please use the Redis community to get some help, providing this report in your help request.\\n\");\n    } else {\n        /* Add all the suggestions accumulated so far. */\n\n        /* Better VM. */\n        report = sdscat(report,\"\\nI have a few advices for you:\\n\\n\");\n        if (advise_better_vm) {\n            report = sdscat(report,\"- If you are using a virtual machine, consider upgrading it with a faster one using an hypervisior that provides less latency during fork() calls. Xen is known to have poor fork() performance. Even in the context of the same VM provider, certain kinds of instances can execute fork faster than others.\\n\");\n        }\n\n        /* Slow log. */\n        if (advise_slowlog_enabled) {\n            report = sdscatprintf(report,\"- There are latency issues with potentially slow commands you are using. Try to enable the Slow Log Redis feature using the command 'CONFIG SET slowlog-log-slower-than %llu'. If the Slow log is disabled Redis is not able to log slow commands execution for you.\\n\", (unsigned long long)server.latency_monitor_threshold*1000);\n        }\n\n        if (advise_slowlog_tuning) {\n            report = sdscatprintf(report,\"- Your current Slow Log configuration only logs events that are slower than your configured latency monitor threshold. Please use 'CONFIG SET slowlog-log-slower-than %llu'.\\n\", (unsigned long long)server.latency_monitor_threshold*1000);\n        }\n\n        if (advise_slowlog_inspect) {\n            report = sdscat(report,\"- Check your Slow Log to understand what are the commands you are running which are too slow to execute. Please check http://redis.io/commands/slowlog for more information.\\n\");\n        }\n\n        /* Intrinsic latency. */\n        if (advise_scheduler) {\n            report = sdscat(report,\"- The system is slow to execute Redis code paths not containing system calls. This usually means the system does not provide Redis CPU time to run for long periods. You should try to:\\n\"\n            \"  1) Lower the system load.\\n\"\n            \"  2) Use a computer / VM just for Redis if you are running other softawre in the same system.\\n\"\n            \"  3) Check if you have a \\\"noisy neighbour\\\" problem.\\n\"\n            \"  4) Check with 'redis-cli --intrinsic-latency 100' what is the intrinsic latency in your system.\\n\"\n            \"  5) Check if the problem is allocator-related by recompiling Redis with MALLOC=libc, if you are using Jemalloc. However this may create fragmentation problems.\\n\");\n        }\n\n        /* AOF / Disk latency. */\n        if (advise_local_disk) {\n            report = sdscat(report,\"- It is strongly advised to use local disks for persistence, especially if you are using AOF. Remote disks provided by platform-as-a-service providers are known to be slow.\\n\");\n        }\n\n        if (advise_ssd) {\n            report = sdscat(report,\"- SSD disks are able to reduce fsync latency, and total time needed for snapshotting and AOF log rewriting (resulting in smaller memory usage and smaller final AOF rewrite buffer flushes). With extremely high write load SSD disks can be a good option. However Redis should perform reasonably with high load using normal disks. Use this advice as a last resort.\\n\");\n        }\n\n        if (advise_data_writeback) {\n            report = sdscat(report,\"- Mounting ext3/4 filesystems with data=writeback can provide a performance boost compared to data=ordered, however this mode of operation provides less guarantees, and sometimes it can happen that after a hard crash the AOF file will have an half-written command at the end and will require to be repaired before Redis restarts.\\n\");\n        }\n\n        if (advise_disk_contention) {\n            report = sdscat(report,\"- Try to lower the disk contention. This is often caused by other disk intensive processes running in the same computer (including other Redis instances).\\n\");\n        }\n\n        if (advise_no_appendfsync) {\n            report = sdscat(report,\"- Assuming from the point of view of data safety this is viable in your environment, you could try to enable the 'no-appendfsync-on-rewrite' option, so that fsync will not be performed while there is a child rewriting the AOF file or producing an RDB file (the moment where there is high disk contention).\\n\");\n        }\n\n        if (advise_relax_fsync_policy && server.aof_fsync == AOF_FSYNC_ALWAYS) {\n            report = sdscat(report,\"- Your fsync policy is set to 'always'. It is very hard to get good performances with such a setup, if possible try to relax the fsync policy to 'onesec'.\\n\");\n        }\n\n        if (advise_write_load_info) {\n            report = sdscat(report,\"- Latency during the AOF atomic rename operation or when the final difference is flushed to the AOF file at the end of the rewrite, sometimes is caused by very high write load, causing the AOF buffer to get very large. If possible try to send less commands to accomplish the same work, or use Lua scripts to group multiple operations into a single EVALSHA call.\\n\");\n        }\n\n        if (advise_hz && server.hz < 100) {\n            report = sdscat(report,\"- In order to make the Redis keys expiring process more incremental, try to set the 'hz' configuration parameter to 100 using 'CONFIG SET hz 100'.\\n\");\n        }\n\n        if (advise_large_objects) {\n            report = sdscat(report,\"- Deleting, expiring or evicting (because of maxmemory policy) large objects is a blocking operation. If you have very large objects that are often deleted, expired, or evicted, try to fragment those objects into multiple smaller objects.\\n\");\n        }\n\n        if (advise_mass_eviction) {\n            report = sdscat(report,\"- Sudden changes to the 'maxmemory' setting via 'CONFIG SET', or allocation of large objects via sets or sorted sets intersections, STORE option of SORT, Redis Cluster large keys migrations (RESTORE command), may create sudden memory pressure forcing the server to block trying to evict keys. \\n\");\n        }\n\n        if (advise_disable_thp) {\n            report = sdscat(report,\"- I detected a non zero amount of anonymous huge pages used by your process. This creates very serious latency events in different conditions, especially when Redis is persisting on disk. To disable THP support use the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled', make sure to also add it into /etc/rc.local so that the command will be executed again after a reboot. Note that even if you have already disabled THP, you still need to restart the Redis process to get rid of the huge pages already created.\\n\");\n        }\n    }\n\n    return report;\n}\n\n/* ---------------------- Latency command implementation -------------------- */\n\n/* latencyCommand() helper to produce a time-delay reply for all the samples\n * in memory for the specified time series. */\nvoid latencyCommandReplyWithSamples(client *c, struct latencyTimeSeries *ts) {\n    void *replylen = addReplyDeferredLen(c);\n    int samples = 0, j;\n\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        int i = (ts->idx + j) % LATENCY_TS_LEN;\n\n        if (ts->samples[i].time == 0) continue;\n        addReplyArrayLen(c,2);\n        addReplyLongLong(c,ts->samples[i].time);\n        addReplyLongLong(c,ts->samples[i].latency);\n        samples++;\n    }\n    setDeferredArrayLen(c,replylen,samples);\n}\n\n/* latencyCommand() helper to produce the reply for the LATEST subcommand,\n * listing the last latency sample for every event type registered so far. */\nvoid latencyCommandReplyWithLatestEvents(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    addReplyArrayLen(c,dictSize(server.latency_events));\n    di = dictGetIterator(server.latency_events);\n    while((de = dictNext(di)) != NULL) {\n        char *event = dictGetKey(de);\n        struct latencyTimeSeries *ts = dictGetVal(de);\n        int last = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;\n\n        addReplyArrayLen(c,4);\n        addReplyBulkCString(c,event);\n        addReplyLongLong(c,ts->samples[last].time);\n        addReplyLongLong(c,ts->samples[last].latency);\n        addReplyLongLong(c,ts->max);\n    }\n    dictReleaseIterator(di);\n}\n\n#define LATENCY_GRAPH_COLS 80\nsds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {\n    int j;\n    struct sequence *seq = createSparklineSequence();\n    sds graph = sdsempty();\n    uint32_t min = 0, max = 0;\n\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        int i = (ts->idx + j) % LATENCY_TS_LEN;\n        int elapsed;\n        char buf[64];\n\n        if (ts->samples[i].time == 0) continue;\n        /* Update min and max. */\n        if (seq->length == 0) {\n            min = max = ts->samples[i].latency;\n        } else {\n            if (ts->samples[i].latency > max) max = ts->samples[i].latency;\n            if (ts->samples[i].latency < min) min = ts->samples[i].latency;\n        }\n        /* Use as label the number of seconds / minutes / hours / days\n         * ago the event happened. */\n        elapsed = time(NULL) - ts->samples[i].time;\n        if (elapsed < 60)\n            snprintf(buf,sizeof(buf),\"%ds\",elapsed);\n        else if (elapsed < 3600)\n            snprintf(buf,sizeof(buf),\"%dm\",elapsed/60);\n        else if (elapsed < 3600*24)\n            snprintf(buf,sizeof(buf),\"%dh\",elapsed/3600);\n        else\n            snprintf(buf,sizeof(buf),\"%dd\",elapsed/(3600*24));\n        sparklineSequenceAddSample(seq,ts->samples[i].latency,buf);\n    }\n\n    graph = sdscatprintf(graph,\n        \"%s - high %lu ms, low %lu ms (all time high %lu ms)\\n\", event,\n        (unsigned long) max, (unsigned long) min, (unsigned long) ts->max);\n    for (j = 0; j < LATENCY_GRAPH_COLS; j++)\n        graph = sdscatlen(graph,\"-\",1);\n    graph = sdscatlen(graph,\"\\n\",1);\n    graph = sparklineRender(graph,seq,LATENCY_GRAPH_COLS,4,SPARKLINE_FILL);\n    freeSparklineSequence(seq);\n    return graph;\n}\n\n/* LATENCY command implementations.\n *\n * LATENCY HISTORY: return time-latency samples for the specified event.\n * LATENCY LATEST: return the latest latency for all the events classes.\n * LATENCY DOCTOR: returns a human readable analysis of instance latency.\n * LATENCY GRAPH: provide an ASCII graph of the latency of the specified event.\n * LATENCY RESET: reset data of a specified event or all the data if no event provided.\n */\nvoid latencyCommand(client *c) {\n    const char *help[] = {\n\"DOCTOR              -- Returns a human readable latency analysis report.\",\n\"GRAPH   <event>     -- Returns an ASCII latency graph for the event class.\",\n\"HISTORY <event>     -- Returns time-latency samples for the event class.\",\n\"LATEST              -- Returns the latest latency samples for all events.\",\n\"RESET   [event ...] -- Resets latency data of one or more event classes.\",\n\"                       (default: reset all data for all event classes)\",\n\"HELP                -- Prints this help.\",\nNULL\n    };\n    struct latencyTimeSeries *ts;\n\n    if (!strcasecmp(c->argv[1]->ptr,\"history\") && c->argc == 3) {\n        /* LATENCY HISTORY <event> */\n        ts = dictFetchValue(server.latency_events,c->argv[2]->ptr);\n        if (ts == NULL) {\n            addReplyArrayLen(c,0);\n        } else {\n            latencyCommandReplyWithSamples(c,ts);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"graph\") && c->argc == 3) {\n        /* LATENCY GRAPH <event> */\n        sds graph;\n        dictEntry *de;\n        char *event;\n\n        de = dictFind(server.latency_events,c->argv[2]->ptr);\n        if (de == NULL) goto nodataerr;\n        ts = dictGetVal(de);\n        event = dictGetKey(de);\n\n        graph = latencyCommandGenSparkeline(event,ts);\n        addReplyVerbatim(c,graph,sdslen(graph),\"txt\");\n        sdsfree(graph);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"latest\") && c->argc == 2) {\n        /* LATENCY LATEST */\n        latencyCommandReplyWithLatestEvents(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"doctor\") && c->argc == 2) {\n        /* LATENCY DOCTOR */\n        sds report = createLatencyReport();\n\n        addReplyVerbatim(c,report,sdslen(report),\"txt\");\n        sdsfree(report);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reset\") && c->argc >= 2) {\n        /* LATENCY RESET */\n        if (c->argc == 2) {\n            addReplyLongLong(c,latencyResetEvent(NULL));\n        } else {\n            int j, resets = 0;\n\n            for (j = 2; j < c->argc; j++)\n                resets += latencyResetEvent(c->argv[j]->ptr);\n            addReplyLongLong(c,resets);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"help\") && c->argc >= 2) {\n        addReplyHelp(c, help);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n    return;\n\nnodataerr:\n    /* Common error when the user asks for an event we have no latency\n     * information about. */\n    addReplyErrorFormat(c,\n        \"No samples available for event '%s'\", (char*) c->argv[2]->ptr);\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/latency.h",
    "content": "/* latency.h -- latency monitor API header file\n * See latency.c for more information.\n *\n * ----------------------------------------------------------------------------\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#ifndef __LATENCY_H\n#define __LATENCY_H\n\n#define LATENCY_TS_LEN 160 /* History length for every monitored event. */\n\n/* Representation of a latency sample: the sampling time and the latency\n * observed in milliseconds. */\nstruct latencySample {\n    int32_t time; /* We don't use time_t to force 4 bytes usage everywhere. */\n    uint32_t latency; /* Latency in milliseconds. */\n};\n\n/* The latency time series for a given event. */\nstruct latencyTimeSeries {\n    int idx; /* Index of the next sample to store. */\n    uint32_t max; /* Max latency observed for this event. */\n    struct latencySample samples[LATENCY_TS_LEN]; /* Latest history. */\n};\n\n/* Latency statistics structure. */\nstruct latencyStats {\n    uint32_t all_time_high; /* Absolute max observed since latest reset. */\n    uint32_t avg;           /* Average of current samples. */\n    uint32_t min;           /* Min of current samples. */\n    uint32_t max;           /* Max of current samples. */\n    uint32_t mad;           /* Mean absolute deviation. */\n    uint32_t samples;       /* Number of non-zero samples. */\n    time_t period;          /* Number of seconds since first event and now. */\n};\n\nvoid latencyMonitorInit(void);\nvoid latencyAddSample(const char *event, mstime_t latency);\nint THPIsEnabled(void);\n\n/* Latency monitoring macros. */\n\n/* Start monitoring an event. We just set the current time. */\n#define latencyStartMonitor(var) if (server.latency_monitor_threshold) { \\\n    var = mstime(); \\\n} else { \\\n    var = 0; \\\n}\n\n/* End monitoring an event, compute the difference with the current time\n * to check the amount of time elapsed. */\n#define latencyEndMonitor(var) if (server.latency_monitor_threshold) { \\\n    var = mstime() - var; \\\n}\n\n/* Add the sample only if the elapsed time is >= to the configured threshold. */\n#define latencyAddSampleIfNeeded(event,var) \\\n    if (server.latency_monitor_threshold && \\\n        (var) >= server.latency_monitor_threshold) \\\n          latencyAddSample((event),(var));\n\n/* Remove time from a nested event. */\n#define latencyRemoveNestedEvent(event_var,nested_var) \\\n    event_var += nested_var;\n\n#endif /* __LATENCY_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lazyfree.c",
    "content": "#include \"server.h\"\n#include \"bio.h\"\n#include \"atomicvar.h\"\n#include \"cluster.h\"\n\nstatic size_t lazyfree_objects = 0;\npthread_mutex_t lazyfree_objects_mutex = PTHREAD_MUTEX_INITIALIZER;\n\n/* Return the number of currently pending objects to free. */\nsize_t lazyfreeGetPendingObjectsCount(void) {\n    size_t aux;\n    atomicGet(lazyfree_objects,aux);\n    return aux;\n}\n\n/* Return the amount of work needed in order to free an object.\n * The return value is not always the actual number of allocations the\n * object is compoesd of, but a number proportional to it.\n *\n * For strings the function always returns 1.\n *\n * For aggregated objects represented by hash tables or other data structures\n * the function just returns the number of elements the object is composed of.\n *\n * Objects composed of single allocations are always reported as having a\n * single item even if they are actually logical composed of multiple\n * elements.\n *\n * For lists the function returns the number of elements in the quicklist\n * representing the list. */\nsize_t lazyfreeGetFreeEffort(robj *obj) {\n    if (obj->type == OBJ_LIST) {\n        quicklist *ql = obj->ptr;\n        return ql->len;\n    } else if (obj->type == OBJ_SET && obj->encoding == OBJ_ENCODING_HT) {\n        dict *ht = obj->ptr;\n        return dictSize(ht);\n    } else if (obj->type == OBJ_ZSET && obj->encoding == OBJ_ENCODING_SKIPLIST){\n        zset *zs = obj->ptr;\n        return zs->zsl->length;\n    } else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) {\n        dict *ht = obj->ptr;\n        return dictSize(ht);\n    } else {\n        return 1; /* Everything else is a single allocation. */\n    }\n}\n\n/* Delete a key, value, and associated expiration entry if any, from the DB.\n * If there are enough allocations to free the value object may be put into\n * a lazy free list instead of being freed synchronously. The lazy free list\n * will be reclaimed in a different bio.c thread. */\n#define LAZYFREE_THRESHOLD 64\nint dbAsyncDelete(redisDb *db, robj *key) {\n    /* Deleting an entry from the expires dict will not free the sds of\n     * the key, because it is shared with the main dictionary. */\n    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);\n\n    /* If the value is composed of a few allocations, to free in a lazy way\n     * is actually just slower... So under a certain limit we just free\n     * the object synchronously. */\n    dictEntry *de = dictUnlink(db->dict,key->ptr);\n    if (de) {\n        robj *val = dictGetVal(de);\n        size_t free_effort = lazyfreeGetFreeEffort(val);\n\n        /* If releasing the object is too much work, do it in the background\n         * by adding the object to the lazy free list.\n         * Note that if the object is shared, to reclaim it now it is not\n         * possible. This rarely happens, however sometimes the implementation\n         * of parts of the Redis core may call incrRefCount() to protect\n         * objects, and then call dbDelete(). In this case we'll fall\n         * through and reach the dictFreeUnlinkedEntry() call, that will be\n         * equivalent to just calling decrRefCount(). */\n        if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) {\n            atomicIncr(lazyfree_objects,1);\n            bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);\n            dictSetVal(db->dict,de,NULL);\n        }\n    }\n\n    /* Release the key-val pair, or just the key if we set the val\n     * field to NULL in order to lazy free it later. */\n    if (de) {\n        dictFreeUnlinkedEntry(db->dict,de);\n        if (server.cluster_enabled) slotToKeyDel(key->ptr);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Free an object, if the object is huge enough, free it in async way. */\nvoid freeObjAsync(robj *o) {\n    size_t free_effort = lazyfreeGetFreeEffort(o);\n    if (free_effort > LAZYFREE_THRESHOLD && o->refcount == 1) {\n        atomicIncr(lazyfree_objects,1);\n        bioCreateBackgroundJob(BIO_LAZY_FREE,o,NULL,NULL);\n    } else {\n        decrRefCount(o);\n    }\n}\n\n/* Empty a Redis DB asynchronously. What the function does actually is to\n * create a new empty set of hash tables and scheduling the old ones for\n * lazy freeing. */\nvoid emptyDbAsync(redisDb *db) {\n    dict *oldht1 = db->dict, *oldht2 = db->expires;\n    db->dict = dictCreate(&dbDictType,NULL);\n    db->expires = dictCreate(&keyptrDictType,NULL);\n    atomicIncr(lazyfree_objects,dictSize(oldht1));\n    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,oldht2);\n}\n\n/* Empty the slots-keys map of Redis CLuster by creating a new empty one\n * and scheduiling the old for lazy freeing. */\nvoid slotToKeyFlushAsync(void) {\n    rax *old = server.cluster->slots_to_keys;\n\n    server.cluster->slots_to_keys = raxNew();\n    memset(server.cluster->slots_keys_count,0,\n           sizeof(server.cluster->slots_keys_count));\n    atomicIncr(lazyfree_objects,old->numele);\n    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,old);\n}\n\n/* Release objects from the lazyfree thread. It's just decrRefCount()\n * updating the count of objects to release. */\nvoid lazyfreeFreeObjectFromBioThread(robj *o) {\n    decrRefCount(o);\n    atomicDecr(lazyfree_objects,1);\n}\n\n/* Release a database from the lazyfree thread. The 'db' pointer is the\n * database which was substitutied with a fresh one in the main thread\n * when the database was logically deleted. 'sl' is a skiplist used by\n * Redis Cluster in order to take the hash slots -> keys mapping. This\n * may be NULL if Redis Cluster is disabled. */\nvoid lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2) {\n    size_t numkeys = dictSize(ht1);\n    dictRelease(ht1);\n    dictRelease(ht2);\n    atomicDecr(lazyfree_objects,numkeys);\n}\n\n/* Release the skiplist mapping Redis Cluster keys to slots in the\n * lazyfree thread. */\nvoid lazyfreeFreeSlotsMapFromBioThread(rax *rt) {\n    size_t len = rt->numele;\n    raxFree(rt);\n    atomicDecr(lazyfree_objects,len);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/listpack.c",
    "content": "/* Listpack -- A lists of strings serialization format\n *\n * This file implements the specification you can find at:\n *\n *  https://github.com/antirez/listpack\n *\n * Copyright (c) 2017, 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#include <limits.h>\n#include <sys/types.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"listpack.h\"\n#include \"listpack_malloc.h\"\n\n#define LP_HDR_SIZE 6       /* 32 bit total len + 16 bit number of elements. */\n#define LP_HDR_NUMELE_UNKNOWN UINT16_MAX\n#define LP_MAX_INT_ENCODING_LEN 9\n#define LP_MAX_BACKLEN_SIZE 5\n#define LP_MAX_ENTRY_BACKLEN 34359738367ULL\n#define LP_ENCODING_INT 0\n#define LP_ENCODING_STRING 1\n\n#define LP_ENCODING_7BIT_UINT 0\n#define LP_ENCODING_7BIT_UINT_MASK 0x80\n#define LP_ENCODING_IS_7BIT_UINT(byte) (((byte)&LP_ENCODING_7BIT_UINT_MASK)==LP_ENCODING_7BIT_UINT)\n\n#define LP_ENCODING_6BIT_STR 0x80\n#define LP_ENCODING_6BIT_STR_MASK 0xC0\n#define LP_ENCODING_IS_6BIT_STR(byte) (((byte)&LP_ENCODING_6BIT_STR_MASK)==LP_ENCODING_6BIT_STR)\n\n#define LP_ENCODING_13BIT_INT 0xC0\n#define LP_ENCODING_13BIT_INT_MASK 0xE0\n#define LP_ENCODING_IS_13BIT_INT(byte) (((byte)&LP_ENCODING_13BIT_INT_MASK)==LP_ENCODING_13BIT_INT)\n\n#define LP_ENCODING_12BIT_STR 0xE0\n#define LP_ENCODING_12BIT_STR_MASK 0xF0\n#define LP_ENCODING_IS_12BIT_STR(byte) (((byte)&LP_ENCODING_12BIT_STR_MASK)==LP_ENCODING_12BIT_STR)\n\n#define LP_ENCODING_16BIT_INT 0xF1\n#define LP_ENCODING_16BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_16BIT_INT(byte) (((byte)&LP_ENCODING_16BIT_INT_MASK)==LP_ENCODING_16BIT_INT)\n\n#define LP_ENCODING_24BIT_INT 0xF2\n#define LP_ENCODING_24BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_24BIT_INT(byte) (((byte)&LP_ENCODING_24BIT_INT_MASK)==LP_ENCODING_24BIT_INT)\n\n#define LP_ENCODING_32BIT_INT 0xF3\n#define LP_ENCODING_32BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_32BIT_INT(byte) (((byte)&LP_ENCODING_32BIT_INT_MASK)==LP_ENCODING_32BIT_INT)\n\n#define LP_ENCODING_64BIT_INT 0xF4\n#define LP_ENCODING_64BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_64BIT_INT(byte) (((byte)&LP_ENCODING_64BIT_INT_MASK)==LP_ENCODING_64BIT_INT)\n\n#define LP_ENCODING_32BIT_STR 0xF0\n#define LP_ENCODING_32BIT_STR_MASK 0xFF\n#define LP_ENCODING_IS_32BIT_STR(byte) (((byte)&LP_ENCODING_32BIT_STR_MASK)==LP_ENCODING_32BIT_STR)\n\n#define LP_EOF 0xFF\n\n#define LP_ENCODING_6BIT_STR_LEN(p) ((p)[0] & 0x3F)\n#define LP_ENCODING_12BIT_STR_LEN(p) ((((p)[0] & 0xF) << 8) | (p)[1])\n#define LP_ENCODING_32BIT_STR_LEN(p) (((uint32_t)(p)[1]<<0) | \\\n                                      ((uint32_t)(p)[2]<<8) | \\\n                                      ((uint32_t)(p)[3]<<16) | \\\n                                      ((uint32_t)(p)[4]<<24))\n\n#define lpGetTotalBytes(p)           (((uint32_t)(p)[0]<<0) | \\\n                                      ((uint32_t)(p)[1]<<8) | \\\n                                      ((uint32_t)(p)[2]<<16) | \\\n                                      ((uint32_t)(p)[3]<<24))\n\n#define lpGetNumElements(p)          (((uint32_t)(p)[4]<<0) | \\\n                                      ((uint32_t)(p)[5]<<8))\n#define lpSetTotalBytes(p,v) do { \\\n    (p)[0] = (v)&0xff; \\\n    (p)[1] = ((v)>>8)&0xff; \\\n    (p)[2] = ((v)>>16)&0xff; \\\n    (p)[3] = ((v)>>24)&0xff; \\\n} while(0)\n\n#define lpSetNumElements(p,v) do { \\\n    (p)[4] = (v)&0xff; \\\n    (p)[5] = ((v)>>8)&0xff; \\\n} while(0)\n\n/* Convert a string into a signed 64 bit integer.\n * The function returns 1 if the string could be parsed into a (non-overflowing)\n * signed 64 bit int, 0 otherwise. The 'value' will be set to the parsed value\n * when the function returns success.\n *\n * Note that this function demands that the string strictly represents\n * a int64 value: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. *\n *\n * -----------------------------------------------------------------------------\n *\n * Credits: this function was adapted from the Redis source code, file\n * \"utils.c\", function string2ll(), and is copyright:\n *\n * Copyright(C) 2011, Pieter Noordhuis\n * Copyright(C) 2011, Salvatore Sanfilippo\n *\n * The function is released under the BSD 3-clause license.\n */\nint lpStringToInt64(const char *s, unsigned long slen, int64_t *value) {\n    const char *p = s;\n    unsigned long plen = 0;\n    int negative = 0;\n    uint64_t 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 > (UINT64_MAX / 10)) /* Overflow. */\n            return 0;\n        v *= 10;\n\n        if (v > (UINT64_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 > ((uint64_t)(-(INT64_MIN+1))+1)) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > INT64_MAX) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = v;\n    }\n    return 1;\n}\n\n/* Create a new, empty listpack.\n * On success the new listpack is returned, otherwise an error is returned. */\nunsigned char *lpNew(void) {\n    unsigned char *lp = lp_malloc(LP_HDR_SIZE+1);\n    if (lp == NULL) return NULL;\n    lpSetTotalBytes(lp,LP_HDR_SIZE+1);\n    lpSetNumElements(lp,0);\n    lp[LP_HDR_SIZE] = LP_EOF;\n    return lp;\n}\n\n/* Free the specified listpack. */\nvoid lpFree(unsigned char *lp) {\n    lp_free(lp);\n}\n\n/* Given an element 'ele' of size 'size', determine if the element can be\n * represented inside the listpack encoded as integer, and returns\n * LP_ENCODING_INT if so. Otherwise returns LP_ENCODING_STR if no integer\n * encoding is possible.\n *\n * If the LP_ENCODING_INT is returned, the function stores the integer encoded\n * representation of the element in the 'intenc' buffer.\n *\n * Regardless of the returned encoding, 'enclen' is populated by reference to\n * the number of bytes that the string or integer encoded element will require\n * in order to be represented. */\nint lpEncodeGetType(unsigned char *ele, uint32_t size, unsigned char *intenc, uint64_t *enclen) {\n    int64_t v;\n    if (lpStringToInt64((const char*)ele, size, &v)) {\n        if (v >= 0 && v <= 127) {\n            /* Single byte 0-127 integer. */\n            intenc[0] = v;\n            *enclen = 1;\n        } else if (v >= -4096 && v <= 4095) {\n            /* 13 bit integer. */\n            if (v < 0) v = ((int64_t)1<<13)+v;\n            intenc[0] = (v>>8)|LP_ENCODING_13BIT_INT;\n            intenc[1] = v&0xff;\n            *enclen = 2;\n        } else if (v >= -32768 && v <= 32767) {\n            /* 16 bit integer. */\n            if (v < 0) v = ((int64_t)1<<16)+v;\n            intenc[0] = LP_ENCODING_16BIT_INT;\n            intenc[1] = v&0xff;\n            intenc[2] = v>>8;\n            *enclen = 3;\n        } else if (v >= -8388608 && v <= 8388607) {\n            /* 24 bit integer. */\n            if (v < 0) v = ((int64_t)1<<24)+v;\n            intenc[0] = LP_ENCODING_24BIT_INT;\n            intenc[1] = v&0xff;\n            intenc[2] = (v>>8)&0xff;\n            intenc[3] = v>>16;\n            *enclen = 4;\n        } else if (v >= -2147483648 && v <= 2147483647) {\n            /* 32 bit integer. */\n            if (v < 0) v = ((int64_t)1<<32)+v;\n            intenc[0] = LP_ENCODING_32BIT_INT;\n            intenc[1] = v&0xff;\n            intenc[2] = (v>>8)&0xff;\n            intenc[3] = (v>>16)&0xff;\n            intenc[4] = v>>24;\n            *enclen = 5;\n        } else {\n            /* 64 bit integer. */\n            uint64_t uv = v;\n            intenc[0] = LP_ENCODING_64BIT_INT;\n            intenc[1] = uv&0xff;\n            intenc[2] = (uv>>8)&0xff;\n            intenc[3] = (uv>>16)&0xff;\n            intenc[4] = (uv>>24)&0xff;\n            intenc[5] = (uv>>32)&0xff;\n            intenc[6] = (uv>>40)&0xff;\n            intenc[7] = (uv>>48)&0xff;\n            intenc[8] = uv>>56;\n            *enclen = 9;\n        }\n        return LP_ENCODING_INT;\n    } else {\n        if (size < 64) *enclen = 1+size;\n        else if (size < 4096) *enclen = 2+size;\n        else *enclen = 5+size;\n        return LP_ENCODING_STRING;\n    }\n}\n\n/* Store a reverse-encoded variable length field, representing the length\n * of the previous element of size 'l', in the target buffer 'buf'.\n * The function returns the number of bytes used to encode it, from\n * 1 to 5. If 'buf' is NULL the function just returns the number of bytes\n * needed in order to encode the backlen. */\nunsigned long lpEncodeBacklen(unsigned char *buf, uint64_t l) {\n    if (l <= 127) {\n        if (buf) buf[0] = l;\n        return 1;\n    } else if (l < 16383) {\n        if (buf) {\n            buf[0] = l>>7;\n            buf[1] = (l&127)|128;\n        }\n        return 2;\n    } else if (l < 2097151) {\n        if (buf) {\n            buf[0] = l>>14;\n            buf[1] = ((l>>7)&127)|128;\n            buf[2] = (l&127)|128;\n        }\n        return 3;\n    } else if (l < 268435455) {\n        if (buf) {\n            buf[0] = l>>21;\n            buf[1] = ((l>>14)&127)|128;\n            buf[2] = ((l>>7)&127)|128;\n            buf[3] = (l&127)|128;\n        }\n        return 4;\n    } else {\n        if (buf) {\n            buf[0] = l>>28;\n            buf[1] = ((l>>21)&127)|128;\n            buf[2] = ((l>>14)&127)|128;\n            buf[3] = ((l>>7)&127)|128;\n            buf[4] = (l&127)|128;\n        }\n        return 5;\n    }\n}\n\n/* Decode the backlen and returns it. If the encoding looks invalid (more than\n * 5 bytes are used), UINT64_MAX is returned to report the problem. */\nuint64_t lpDecodeBacklen(unsigned char *p) {\n    uint64_t val = 0;\n    uint64_t shift = 0;\n    do {\n        val |= (uint64_t)(p[0] & 127) << shift;\n        if (!(p[0] & 128)) break;\n        shift += 7;\n        p--;\n        if (shift > 28) return UINT64_MAX;\n    } while(1);\n    return val;\n}\n\n/* Encode the string element pointed by 's' of size 'len' in the target\n * buffer 's'. The function should be called with 'buf' having always enough\n * space for encoding the string. This is done by calling lpEncodeGetType()\n * before calling this function. */\nvoid lpEncodeString(unsigned char *buf, unsigned char *s, uint32_t len) {\n    if (len < 64) {\n        buf[0] = len | LP_ENCODING_6BIT_STR;\n        memcpy(buf+1,s,len);\n    } else if (len < 4096) {\n        buf[0] = (len >> 8) | LP_ENCODING_12BIT_STR;\n        buf[1] = len & 0xff;\n        memcpy(buf+2,s,len);\n    } else {\n        buf[0] = LP_ENCODING_32BIT_STR;\n        buf[1] = len & 0xff;\n        buf[2] = (len >> 8) & 0xff;\n        buf[3] = (len >> 16) & 0xff;\n        buf[4] = (len >> 24) & 0xff;\n        memcpy(buf+5,s,len);\n    }\n}\n\n/* Return the encoded length of the listpack element pointed by 'p'. If the\n * element encoding is wrong then 0 is returned. */\nuint32_t lpCurrentEncodedSize(unsigned char *p) {\n    if (LP_ENCODING_IS_7BIT_UINT(p[0])) return 1;\n    if (LP_ENCODING_IS_6BIT_STR(p[0])) return 1+LP_ENCODING_6BIT_STR_LEN(p);\n    if (LP_ENCODING_IS_13BIT_INT(p[0])) return 2;\n    if (LP_ENCODING_IS_16BIT_INT(p[0])) return 3;\n    if (LP_ENCODING_IS_24BIT_INT(p[0])) return 4;\n    if (LP_ENCODING_IS_32BIT_INT(p[0])) return 5;\n    if (LP_ENCODING_IS_64BIT_INT(p[0])) return 9;\n    if (LP_ENCODING_IS_12BIT_STR(p[0])) return 2+LP_ENCODING_12BIT_STR_LEN(p);\n    if (LP_ENCODING_IS_32BIT_STR(p[0])) return 5+LP_ENCODING_32BIT_STR_LEN(p);\n    if (p[0] == LP_EOF) return 1;\n    return 0;\n}\n\n/* Skip the current entry returning the next. It is invalid to call this\n * function if the current element is the EOF element at the end of the\n * listpack, however, while this function is used to implement lpNext(),\n * it does not return NULL when the EOF element is encountered. */\nunsigned char *lpSkip(unsigned char *p) {\n    unsigned long entrylen = lpCurrentEncodedSize(p);\n    entrylen += lpEncodeBacklen(NULL,entrylen);\n    p += entrylen;\n    return p;\n}\n\n/* If 'p' points to an element of the listpack, calling lpNext() will return\n * the pointer to the next element (the one on the right), or NULL if 'p'\n * already pointed to the last element of the listpack. */\nunsigned char *lpNext(unsigned char *lp, unsigned char *p) {\n    ((void) lp); /* lp is not used for now. However lpPrev() uses it. */\n    p = lpSkip(p);\n    if (p[0] == LP_EOF) return NULL;\n    return p;\n}\n\n/* If 'p' points to an element of the listpack, calling lpPrev() will return\n * the pointer to the preivous element (the one on the left), or NULL if 'p'\n * already pointed to the first element of the listpack. */\nunsigned char *lpPrev(unsigned char *lp, unsigned char *p) {\n    if (p-lp == LP_HDR_SIZE) return NULL;\n    p--; /* Seek the first backlen byte of the last element. */\n    uint64_t prevlen = lpDecodeBacklen(p);\n    prevlen += lpEncodeBacklen(NULL,prevlen);\n    return p-prevlen+1; /* Seek the first byte of the previous entry. */\n}\n\n/* Return a pointer to the first element of the listpack, or NULL if the\n * listpack has no elements. */\nunsigned char *lpFirst(unsigned char *lp) {\n    lp += LP_HDR_SIZE; /* Skip the header. */\n    if (lp[0] == LP_EOF) return NULL;\n    return lp;\n}\n\n/* Return a pointer to the last element of the listpack, or NULL if the\n * listpack has no elements. */\nunsigned char *lpLast(unsigned char *lp) {\n    unsigned char *p = lp+lpGetTotalBytes(lp)-1; /* Seek EOF element. */\n    return lpPrev(lp,p); /* Will return NULL if EOF is the only element. */\n}\n\n/* Return the number of elements inside the listpack. This function attempts\n * to use the cached value when within range, otherwise a full scan is\n * needed. As a side effect of calling this function, the listpack header\n * could be modified, because if the count is found to be already within\n * the 'numele' header field range, the new value is set. */\nuint32_t lpLength(unsigned char *lp) {\n    uint32_t numele = lpGetNumElements(lp);\n    if (numele != LP_HDR_NUMELE_UNKNOWN) return numele;\n\n    /* Too many elements inside the listpack. We need to scan in order\n     * to get the total number. */\n    uint32_t count = 0;\n    unsigned char *p = lpFirst(lp);\n    while(p) {\n        count++;\n        p = lpNext(lp,p);\n    }\n\n    /* If the count is again within range of the header numele field,\n     * set it. */\n    if (count < LP_HDR_NUMELE_UNKNOWN) lpSetNumElements(lp,count);\n    return count;\n}\n\n/* Return the listpack element pointed by 'p'.\n *\n * The function changes behavior depending on the passed 'intbuf' value.\n * Specifically, if 'intbuf' is NULL:\n *\n * If the element is internally encoded as an integer, the function returns\n * NULL and populates the integer value by reference in 'count'. Otherwise if\n * the element is encoded as a string a pointer to the string (pointing inside\n * the listpack itself) is returned, and 'count' is set to the length of the\n * string.\n *\n * If instead 'intbuf' points to a buffer passed by the caller, that must be\n * at least LP_INTBUF_SIZE bytes, the function always returns the element as\n * it was a string (returning the pointer to the string and setting the\n * 'count' argument to the string length by reference). However if the element\n * is encoded as an integer, the 'intbuf' buffer is used in order to store\n * the string representation.\n *\n * The user should use one or the other form depending on what the value will\n * be used for. If there is immediate usage for an integer value returned\n * by the function, than to pass a buffer (and convert it back to a number)\n * is of course useless.\n *\n * If the function is called against a badly encoded ziplist, so that there\n * is no valid way to parse it, the function returns like if there was an\n * integer encoded with value 12345678900000000 + <unrecognized byte>, this may\n * be an hint to understand that something is wrong. To crash in this case is\n * not sensible because of the different requirements of the application using\n * this lib.\n *\n * Similarly, there is no error returned since the listpack normally can be\n * assumed to be valid, so that would be a very high API cost. However a function\n * in order to check the integrity of the listpack at load time is provided,\n * check lpIsValid(). */\nunsigned char *lpGet(unsigned char *p, int64_t *count, unsigned char *intbuf) {\n    int64_t val;\n    uint64_t uval, negstart, negmax;\n\n    if (LP_ENCODING_IS_7BIT_UINT(p[0])) {\n        negstart = UINT64_MAX; /* 7 bit ints are always positive. */\n        negmax = 0;\n        uval = p[0] & 0x7f;\n    } else if (LP_ENCODING_IS_6BIT_STR(p[0])) {\n        *count = LP_ENCODING_6BIT_STR_LEN(p);\n        return p+1;\n    } else if (LP_ENCODING_IS_13BIT_INT(p[0])) {\n        uval = ((p[0]&0x1f)<<8) | p[1];\n        negstart = (uint64_t)1<<12;\n        negmax = 8191;\n    } else if (LP_ENCODING_IS_16BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8;\n        negstart = (uint64_t)1<<15;\n        negmax = UINT16_MAX;\n    } else if (LP_ENCODING_IS_24BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8 |\n               (uint64_t)p[3]<<16;\n        negstart = (uint64_t)1<<23;\n        negmax = UINT32_MAX>>8;\n    } else if (LP_ENCODING_IS_32BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8 |\n               (uint64_t)p[3]<<16 |\n               (uint64_t)p[4]<<24;\n        negstart = (uint64_t)1<<31;\n        negmax = UINT32_MAX;\n    } else if (LP_ENCODING_IS_64BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8 |\n               (uint64_t)p[3]<<16 |\n               (uint64_t)p[4]<<24 |\n               (uint64_t)p[5]<<32 |\n               (uint64_t)p[6]<<40 |\n               (uint64_t)p[7]<<48 |\n               (uint64_t)p[8]<<56;\n        negstart = (uint64_t)1<<63;\n        negmax = UINT64_MAX;\n    } else if (LP_ENCODING_IS_12BIT_STR(p[0])) {\n        *count = LP_ENCODING_12BIT_STR_LEN(p);\n        return p+2;\n    } else if (LP_ENCODING_IS_32BIT_STR(p[0])) {\n        *count = LP_ENCODING_32BIT_STR_LEN(p);\n        return p+5;\n    } else {\n        uval = 12345678900000000ULL + p[0];\n        negstart = UINT64_MAX;\n        negmax = 0;\n    }\n\n    /* We reach this code path only for integer encodings.\n     * Convert the unsigned value to the signed one using two's complement\n     * rule. */\n    if (uval >= negstart) {\n        /* This three steps conversion should avoid undefined behaviors\n         * in the unsigned -> signed conversion. */\n        uval = negmax-uval;\n        val = uval;\n        val = -val-1;\n    } else {\n        val = uval;\n    }\n\n    /* Return the string representation of the integer or the value itself\n     * depending on intbuf being NULL or not. */\n    if (intbuf) {\n        *count = snprintf((char*)intbuf,LP_INTBUF_SIZE,\"%lld\",(long long)val);\n        return intbuf;\n    } else {\n        *count = val;\n        return NULL;\n    }\n}\n\n/* Insert, delete or replace the specified element 'ele' of length 'len' at\n * the specified position 'p', with 'p' being a listpack element pointer\n * obtained with lpFirst(), lpLast(), lpIndex(), lpNext(), lpPrev() or\n * lpSeek().\n *\n * The element is inserted before, after, or replaces the element pointed\n * by 'p' depending on the 'where' argument, that can be LP_BEFORE, LP_AFTER\n * or LP_REPLACE.\n *\n * If 'ele' is set to NULL, the function removes the element pointed by 'p'\n * instead of inserting one.\n *\n * Returns NULL on out of memory or when the listpack total length would exceed\n * the max allowed size of 2^32-1, otherwise the new pointer to the listpack\n * holding the new element is returned (and the old pointer passed is no longer\n * considered valid)\n *\n * If 'newp' is not NULL, at the end of a successful call '*newp' will be set\n * to the address of the element just added, so that it will be possible to\n * continue an interation with lpNext() and lpPrev().\n *\n * For deletion operations ('ele' set to NULL) 'newp' is set to the next\n * element, on the right of the deleted one, or to NULL if the deleted element\n * was the last one. */\nunsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp) {\n    unsigned char intenc[LP_MAX_INT_ENCODING_LEN];\n    unsigned char backlen[LP_MAX_BACKLEN_SIZE];\n\n    uint64_t enclen; /* The length of the encoded element. */\n\n    /* An element pointer set to NULL means deletion, which is conceptually\n     * replacing the element with a zero-length element. So whatever we\n     * get passed as 'where', set it to LP_REPLACE. */\n    if (ele == NULL) where = LP_REPLACE;\n\n    /* If we need to insert after the current element, we just jump to the\n     * next element (that could be the EOF one) and handle the case of\n     * inserting before. So the function will actually deal with just two\n     * cases: LP_BEFORE and LP_REPLACE. */\n    if (where == LP_AFTER) {\n        p = lpSkip(p);\n        where = LP_BEFORE;\n    }\n\n    /* Store the offset of the element 'p', so that we can obtain its\n     * address again after a reallocation. */\n    unsigned long poff = p-lp;\n\n    /* Calling lpEncodeGetType() results into the encoded version of the\n     * element to be stored into 'intenc' in case it is representable as\n     * an integer: in that case, the function returns LP_ENCODING_INT.\n     * Otherwise if LP_ENCODING_STR is returned, we'll have to call\n     * lpEncodeString() to actually write the encoded string on place later.\n     *\n     * Whatever the returned encoding is, 'enclen' is populated with the\n     * length of the encoded element. */\n    int enctype;\n    if (ele) {\n        enctype = lpEncodeGetType(ele,size,intenc,&enclen);\n    } else {\n        enctype = -1;\n        enclen = 0;\n    }\n\n    /* We need to also encode the backward-parsable length of the element\n     * and append it to the end: this allows to traverse the listpack from\n     * the end to the start. */\n    unsigned long backlen_size = ele ? lpEncodeBacklen(backlen,enclen) : 0;\n    uint64_t old_listpack_bytes = lpGetTotalBytes(lp);\n    uint32_t replaced_len  = 0;\n    if (where == LP_REPLACE) {\n        replaced_len = lpCurrentEncodedSize(p);\n        replaced_len += lpEncodeBacklen(NULL,replaced_len);\n    }\n\n    uint64_t new_listpack_bytes = old_listpack_bytes + enclen + backlen_size\n                                  - replaced_len;\n    if (new_listpack_bytes > UINT32_MAX) return NULL;\n\n    /* We now need to reallocate in order to make space or shrink the\n     * allocation (in case 'when' value is LP_REPLACE and the new element is\n     * smaller). However we do that before memmoving the memory to\n     * make room for the new element if the final allocation will get\n     * larger, or we do it after if the final allocation will get smaller. */\n\n    unsigned char *dst = lp + poff; /* May be updated after reallocation. */\n\n    /* Realloc before: we need more room. */\n    if (new_listpack_bytes > old_listpack_bytes) {\n        if ((lp = lp_realloc(lp,new_listpack_bytes)) == NULL) return NULL;\n        dst = lp + poff;\n    }\n\n    /* Setup the listpack relocating the elements to make the exact room\n     * we need to store the new one. */\n    if (where == LP_BEFORE) {\n        memmove(dst+enclen+backlen_size,dst,old_listpack_bytes-poff);\n    } else { /* LP_REPLACE. */\n        long lendiff = (enclen+backlen_size)-replaced_len;\n        memmove(dst+replaced_len+lendiff,\n                dst+replaced_len,\n                old_listpack_bytes-poff-replaced_len);\n    }\n\n    /* Realloc after: we need to free space. */\n    if (new_listpack_bytes < old_listpack_bytes) {\n        if ((lp = lp_realloc(lp,new_listpack_bytes)) == NULL) return NULL;\n        dst = lp + poff;\n    }\n\n    /* Store the entry. */\n    if (newp) {\n        *newp = dst;\n        /* In case of deletion, set 'newp' to NULL if the next element is\n         * the EOF element. */\n        if (!ele && dst[0] == LP_EOF) *newp = NULL;\n    }\n    if (ele) {\n        if (enctype == LP_ENCODING_INT) {\n            memcpy(dst,intenc,enclen);\n        } else {\n            lpEncodeString(dst,ele,size);\n        }\n        dst += enclen;\n        memcpy(dst,backlen,backlen_size);\n        dst += backlen_size;\n    }\n\n    /* Update header. */\n    if (where != LP_REPLACE || ele == NULL) {\n        uint32_t num_elements = lpGetNumElements(lp);\n        if (num_elements != LP_HDR_NUMELE_UNKNOWN) {\n            if (ele)\n                lpSetNumElements(lp,num_elements+1);\n            else\n                lpSetNumElements(lp,num_elements-1);\n        }\n    }\n    lpSetTotalBytes(lp,new_listpack_bytes);\n\n#if 0\n    /* This code path is normally disabled: what it does is to force listpack\n     * to return *always* a new pointer after performing some modification to\n     * the listpack, even if the previous allocation was enough. This is useful\n     * in order to spot bugs in code using listpacks: by doing so we can find\n     * if the caller forgets to set the new pointer where the listpack reference\n     * is stored, after an update. */\n    unsigned char *oldlp = lp;\n    lp = lp_malloc(new_listpack_bytes);\n    memcpy(lp,oldlp,new_listpack_bytes);\n    if (newp) {\n        unsigned long offset = (*newp)-oldlp;\n        *newp = lp + offset;\n    }\n    /* Make sure the old allocation contains garbage. */\n    memset(oldlp,'A',new_listpack_bytes);\n    lp_free(oldlp);\n#endif\n\n    return lp;\n}\n\n/* Append the specified element 'ele' of length 'len' at the end of the\n * listpack. It is implemented in terms of lpInsert(), so the return value is\n * the same as lpInsert(). */\nunsigned char *lpAppend(unsigned char *lp, unsigned char *ele, uint32_t size) {\n    uint64_t listpack_bytes = lpGetTotalBytes(lp);\n    unsigned char *eofptr = lp + listpack_bytes - 1;\n    return lpInsert(lp,ele,size,eofptr,LP_BEFORE,NULL);\n}\n\n/* Remove the element pointed by 'p', and return the resulting listpack.\n * If 'newp' is not NULL, the next element pointer (to the right of the\n * deleted one) is returned by reference. If the deleted element was the\n * last one, '*newp' is set to NULL. */\nunsigned char *lpDelete(unsigned char *lp, unsigned char *p, unsigned char **newp) {\n    return lpInsert(lp,NULL,0,p,LP_REPLACE,newp);\n}\n\n/* Return the total number of bytes the listpack is composed of. */\nuint32_t lpBytes(unsigned char *lp) {\n    return lpGetTotalBytes(lp);\n}\n\n/* Seek the specified element and returns the pointer to the seeked element.\n * Positive indexes specify the zero-based element to seek from the head to\n * the tail, negative indexes specify elements starting from the tail, where\n * -1 means the last element, -2 the penultimate and so forth. If the index\n * is out of range, NULL is returned. */\nunsigned char *lpSeek(unsigned char *lp, long index) {\n    int forward = 1; /* Seek forward by default. */\n\n    /* We want to seek from left to right or the other way around\n     * depending on the listpack length and the element position.\n     * However if the listpack length cannot be obtained in constant time,\n     * we always seek from left to right. */\n    uint32_t numele = lpGetNumElements(lp);\n    if (numele != LP_HDR_NUMELE_UNKNOWN) {\n        if (index < 0) index = (long)numele+index;\n        if (index < 0) return NULL; /* Index still < 0 means out of range. */\n        if (index >= numele) return NULL; /* Out of range the other side. */\n        /* We want to scan right-to-left if the element we are looking for\n         * is past the half of the listpack. */\n        if (index > numele/2) {\n            forward = 0;\n            /* Left to right scanning always expects a negative index. Convert\n             * our index to negative form. */\n            index -= numele;\n        }\n    } else {\n        /* If the listpack length is unspecified, for negative indexes we\n         * want to always scan left-to-right. */\n        if (index < 0) forward = 0;\n    }\n\n    /* Forward and backward scanning is trivially based on lpNext()/lpPrev(). */\n    if (forward) {\n        unsigned char *ele = lpFirst(lp);\n        while (index > 0 && ele) {\n            ele = lpNext(lp,ele);\n            index--;\n        }\n        return ele;\n    } else {\n        unsigned char *ele = lpLast(lp);\n        while (index < -1 && ele) {\n            ele = lpPrev(lp,ele);\n            index++;\n        }\n        return ele;\n    }\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/listpack.h",
    "content": "/* Listpack -- A lists of strings serialization format\n *\n * This file implements the specification you can find at:\n *\n *  https://github.com/antirez/listpack\n *\n * Copyright (c) 2017, 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 __LISTPACK_H\n#define __LISTPACK_H\n\n#include <stdint.h>\n\n#define LP_INTBUF_SIZE 21 /* 20 digits of -2^63 + 1 null term = 21. */\n\n/* lpInsert() where argument possible values: */\n#define LP_BEFORE 0\n#define LP_AFTER 1\n#define LP_REPLACE 2\n\nunsigned char *lpNew(void);\nvoid lpFree(unsigned char *lp);\nunsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp);\nunsigned char *lpAppend(unsigned char *lp, unsigned char *ele, uint32_t size);\nunsigned char *lpDelete(unsigned char *lp, unsigned char *p, unsigned char **newp);\nuint32_t lpLength(unsigned char *lp);\nunsigned char *lpGet(unsigned char *p, int64_t *count, unsigned char *intbuf);\nunsigned char *lpFirst(unsigned char *lp);\nunsigned char *lpLast(unsigned char *lp);\nunsigned char *lpNext(unsigned char *lp, unsigned char *p);\nunsigned char *lpPrev(unsigned char *lp, unsigned char *p);\nuint32_t lpBytes(unsigned char *lp);\nunsigned char *lpSeek(unsigned char *lp, long index);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/listpack_malloc.h",
    "content": "/* Listpack -- A lists of strings serialization format\n * https://github.com/antirez/listpack\n *\n * Copyright (c) 2017, 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/* Allocator selection.\n *\n * This file is used in order to change the Rax allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#ifndef LISTPACK_ALLOC_H\n#define LISTPACK_ALLOC_H\n#include \"zmalloc.h\"\n#define lp_malloc zmalloc\n#define lp_realloc zrealloc\n#define lp_free zfree\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/localtime.c",
    "content": "/*\n * Copyright (c) 2018, 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 <time.h>\n\n/* This is a safe version of localtime() which contains no locks and is\n * fork() friendly. Even the _r version of localtime() cannot be used safely\n * in Redis. Another thread may be calling localtime() while the main thread\n * forks(). Later when the child process calls localtime() again, for instance\n * in order to log something to the Redis log, it may deadlock: in the copy\n * of the address space of the forked process the lock will never be released.\n *\n * This function takes the timezone 'tz' as argument, and the 'dst' flag is\n * used to check if daylight saving time is currently in effect. The caller\n * of this function should obtain such information calling tzset() ASAP in the\n * main() function to obtain the timezone offset from the 'timezone' global\n * variable. To obtain the daylight information, if it is currently active or not,\n * one trick is to call localtime() in main() ASAP as well, and get the\n * information from the tm_isdst field of the tm structure. However the daylight\n * time may switch in the future for long running processes, so this information\n * should be refreshed at safe times.\n *\n * Note that this function does not work for dates < 1/1/1970, it is solely\n * designed to work with what time(NULL) may return, and to support Redis\n * logging of the dates, it's not really a complete implementation. */\nstatic int is_leap_year(time_t year) {\n    if (year % 4) return 0;         /* A year not divisible by 4 is not leap. */\n    else if (year % 100) return 1;  /* If div by 4 and not 100 is surely leap. */\n    else if (year % 400) return 0;  /* If div by 100 *and* not by 400 is not leap. */\n    else return 1;                  /* If div by 100 and 400 is leap. */\n}\n\nvoid nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst) {\n    const time_t secs_min = 60;\n    const time_t secs_hour = 3600;\n    const time_t secs_day = 3600*24;\n\n    t -= tz;                            /* Adjust for timezone. */\n    t += 3600*dst;                      /* Adjust for daylight time. */\n    time_t days = t / secs_day;         /* Days passed since epoch. */\n    time_t seconds = t % secs_day;      /* Remaining seconds. */\n\n    tmp->tm_isdst = dst;\n    tmp->tm_hour = seconds / secs_hour;\n    tmp->tm_min = (seconds % secs_hour) / secs_min;\n    tmp->tm_sec = (seconds % secs_hour) % secs_min;\n\n    /* 1/1/1970 was a Thursday, that is, day 4 from the POV of the tm structure\n     * where sunday = 0, so to calculate the day of the week we have to add 4\n     * and take the modulo by 7. */\n    tmp->tm_wday = (days+4)%7;\n\n    /* Calculate the current year. */\n    tmp->tm_year = 1970;\n    while(1) {\n        /* Leap years have one day more. */\n        time_t days_this_year = 365 + is_leap_year(tmp->tm_year);\n        if (days_this_year > days) break;\n        days -= days_this_year;\n        tmp->tm_year++;\n    }\n    tmp->tm_yday = days;  /* Number of day of the current year. */\n\n    /* We need to calculate in which month and day of the month we are. To do\n     * so we need to skip days according to how many days there are in each\n     * month, and adjust for the leap year that has one more day in February. */\n    int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};\n    mdays[1] += is_leap_year(tmp->tm_year);\n\n    tmp->tm_mon = 0;\n    while(days >= mdays[tmp->tm_mon]) {\n        days -= mdays[tmp->tm_mon];\n        tmp->tm_mon++;\n    }\n\n    tmp->tm_mday = days+1;  /* Add 1 since our 'days' is zero-based. */\n    tmp->tm_year -= 1900;   /* Surprisingly tm_year is year-1900. */\n}\n\n#ifdef LOCALTIME_TEST_MAIN\n#include <stdio.h>\n\nint main(void) {\n    /* Obtain timezone and daylight info. */\n    tzset(); /* Now 'timezome' global is populated. */\n    time_t t = time(NULL);\n    struct tm *aux = localtime(&t);\n    int daylight_active = aux->tm_isdst;\n\n    struct tm tm;\n    char buf[1024];\n\n    nolocks_localtime(&tm,t,timezone,daylight_active);\n    strftime(buf,sizeof(buf),\"%d %b %H:%M:%S\",&tm);\n    printf(\"[timezone: %d, dl: %d] %s\\n\", (int)timezone, (int)daylight_active, buf);\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lolwut.c",
    "content": "/*\n * Copyright (c) 2018, 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 * This file implements the LOLWUT command. The command should do something\n * fun and interesting, and should be replaced by a new implementation at\n * each new version of Redis.\n */\n\n#include \"server.h\"\n#include \"lolwut.h\"\n#include <math.h>\n\nvoid lolwut5Command(client *c);\nvoid lolwut6Command(client *c);\n\n/* The default target for LOLWUT if no matching version was found.\n * This is what unstable versions of Redis will display. */\nvoid lolwutUnstableCommand(client *c) {\n    sds rendered = sdsnew(\"Redis ver. \");\n    rendered = sdscat(rendered,REDIS_VERSION);\n    rendered = sdscatlen(rendered,\"\\n\",1);\n    addReplyVerbatim(c,rendered,sdslen(rendered),\"txt\");\n    sdsfree(rendered);\n}\n\n/* LOLWUT [VERSION <version>] [... version specific arguments ...] */\nvoid lolwutCommand(client *c) {\n    char *v = REDIS_VERSION;\n    char verstr[64];\n\n    if (c->argc >= 3 && !strcasecmp(c->argv[1]->ptr,\"version\")) {\n        long ver;\n        if (getLongFromObjectOrReply(c,c->argv[2],&ver,NULL) != C_OK) return;\n        snprintf(verstr,sizeof(verstr),\"%u.0.0\",(unsigned int)ver);\n        v = verstr;\n\n        /* Adjust argv/argc to filter the \"VERSION ...\" option, since the\n         * specific LOLWUT version implementations don't know about it\n         * and expect their arguments. */\n        c->argv += 2;\n        c->argc -= 2;\n    }\n\n    if ((v[0] == '5' && v[1] == '.' && v[2] != '9') ||\n        (v[0] == '4' && v[1] == '.' && v[2] == '9'))\n        lolwut5Command(c);\n    else if ((v[0] == '6' && v[1] == '.' && v[2] != '9') ||\n             (v[0] == '5' && v[1] == '.' && v[2] == '9'))\n        lolwut6Command(c);\n    else\n        lolwutUnstableCommand(c);\n\n    /* Fix back argc/argv in case of VERSION argument. */\n    if (v == verstr) {\n        c->argv -= 2;\n        c->argc += 2;\n    }\n}\n\n/* ========================== LOLWUT Canvase ===============================\n * Many LOWUT versions will likely print some computer art to the screen.\n * This is the case with LOLWUT 5 and LOLWUT 6, so here there is a generic\n * canvas implementation that can be reused.  */\n\n/* Allocate and return a new canvas of the specified size. */\nlwCanvas *lwCreateCanvas(int width, int height, int bgcolor) {\n    lwCanvas *canvas = zmalloc(sizeof(*canvas));\n    canvas->width = width;\n    canvas->height = height;\n    canvas->pixels = zmalloc(width*height);\n    memset(canvas->pixels,bgcolor,width*height);\n    return canvas;\n}\n\n/* Free the canvas created by lwCreateCanvas(). */\nvoid lwFreeCanvas(lwCanvas *canvas) {\n    zfree(canvas->pixels);\n    zfree(canvas);\n}\n\n/* Set a pixel to the specified color. Color is 0 or 1, where zero means no\n * dot will be displyed, and 1 means dot will be displayed.\n * Coordinates are arranged so that left-top corner is 0,0. You can write\n * out of the size of the canvas without issues. */\nvoid lwDrawPixel(lwCanvas *canvas, int x, int y, int color) {\n    if (x < 0 || x >= canvas->width ||\n        y < 0 || y >= canvas->height) return;\n    canvas->pixels[x+y*canvas->width] = color;\n}\n\n/* Return the value of the specified pixel on the canvas. */\nint lwGetPixel(lwCanvas *canvas, int x, int y) {\n    if (x < 0 || x >= canvas->width ||\n        y < 0 || y >= canvas->height) return 0;\n    return canvas->pixels[x+y*canvas->width];\n}\n\n/* Draw a line from x1,y1 to x2,y2 using the Bresenham algorithm. */\nvoid lwDrawLine(lwCanvas *canvas, int x1, int y1, int x2, int y2, int color) {\n    int dx = abs(x2-x1);\n    int dy = abs(y2-y1);\n    int sx = (x1 < x2) ? 1 : -1;\n    int sy = (y1 < y2) ? 1 : -1;\n    int err = dx-dy, e2;\n\n    while(1) {\n        lwDrawPixel(canvas,x1,y1,color);\n        if (x1 == x2 && y1 == y2) break;\n        e2 = err*2;\n        if (e2 > -dy) {\n            err -= dy;\n            x1 += sx;\n        }\n        if (e2 < dx) {\n            err += dx;\n            y1 += sy;\n        }\n    }\n}\n\n/* Draw a square centered at the specified x,y coordinates, with the specified\n * rotation angle and size. In order to write a rotated square, we use the\n * trivial fact that the parametric equation:\n *\n *  x = sin(k)\n *  y = cos(k)\n *\n * Describes a circle for values going from 0 to 2*PI. So basically if we start\n * at 45 degrees, that is k = PI/4, with the first point, and then we find\n * the other three points incrementing K by PI/2 (90 degrees), we'll have the\n * points of the square. In order to rotate the square, we just start with\n * k = PI/4 + rotation_angle, and we are done.\n *\n * Of course the vanilla equations above will describe the square inside a\n * circle of radius 1, so in order to draw larger squares we'll have to\n * multiply the obtained coordinates, and then translate them. However this\n * is much simpler than implementing the abstract concept of 2D shape and then\n * performing the rotation/translation transformation, so for LOLWUT it's\n * a good approach. */\nvoid lwDrawSquare(lwCanvas *canvas, int x, int y, float size, float angle, int color) {\n    int px[4], py[4];\n\n    /* Adjust the desired size according to the fact that the square inscribed\n     * into a circle of radius 1 has the side of length SQRT(2). This way\n     * size becomes a simple multiplication factor we can use with our\n     * coordinates to magnify them. */\n    size /= 1.4142135623;\n    size = round(size);\n\n    /* Compute the four points. */\n    float k = M_PI/4 + angle;\n    for (int j = 0; j < 4; j++) {\n        px[j] = round(sin(k) * size + x);\n        py[j] = round(cos(k) * size + y);\n        k += M_PI/2;\n    }\n\n    /* Draw the square. */\n    for (int j = 0; j < 4; j++)\n        lwDrawLine(canvas,px[j],py[j],px[(j+1)%4],py[(j+1)%4],color);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lolwut.h",
    "content": "/*\n * Copyright (c) 2018-2019, 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/* This structure represents our canvas. Drawing functions will take a pointer\n * to a canvas to write to it. Later the canvas can be rendered to a string\n * suitable to be printed on the screen, using unicode Braille characters. */\n\n/* This represents a very simple generic canvas in order to draw stuff.\n * It's up to each LOLWUT versions to translate what they draw to the\n * screen, depending on the result to accomplish. */\n\n#ifndef __LOLWUT_H\n#define __LOLWUT_H\n\ntypedef struct lwCanvas {\n    int width;\n    int height;\n    char *pixels;\n} lwCanvas;\n\n/* Drawing functions implemented inside lolwut.c. */\nlwCanvas *lwCreateCanvas(int width, int height, int bgcolor);\nvoid lwFreeCanvas(lwCanvas *canvas);\nvoid lwDrawPixel(lwCanvas *canvas, int x, int y, int color);\nint lwGetPixel(lwCanvas *canvas, int x, int y);\nvoid lwDrawLine(lwCanvas *canvas, int x1, int y1, int x2, int y2, int color);\nvoid lwDrawSquare(lwCanvas *canvas, int x, int y, float size, float angle, int color);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lolwut5.c",
    "content": "/*\n * Copyright (c) 2018, 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 * This file implements the LOLWUT command. The command should do something\n * fun and interesting, and should be replaced by a new implementation at\n * each new version of Redis.\n */\n\n#include \"server.h\"\n#include \"lolwut.h\"\n#include <math.h>\n\n/* Translate a group of 8 pixels (2x4 vertical rectangle) to the corresponding\n * braille character. The byte should correspond to the pixels arranged as\n * follows, where 0 is the least significant bit, and 7 the most significant\n * bit:\n *\n *   0 3\n *   1 4\n *   2 5\n *   6 7\n *\n * The corresponding utf8 encoded character is set into the three bytes\n * pointed by 'output'.\n */\n#include <stdio.h>\nvoid lwTranslatePixelsGroup(int byte, char *output) {\n    int code = 0x2800 + byte;\n    /* Convert to unicode. This is in the U0800-UFFFF range, so we need to\n     * emit it like this in three bytes:\n     * 1110xxxx 10xxxxxx 10xxxxxx. */\n    output[0] = 0xE0 | (code >> 12);          /* 1110-xxxx */\n    output[1] = 0x80 | ((code >> 6) & 0x3F);  /* 10-xxxxxx */\n    output[2] = 0x80 | (code & 0x3F);         /* 10-xxxxxx */\n}\n\n/* Schotter, the output of LOLWUT of Redis 5, is a computer graphic art piece\n * generated by Georg Nees in the 60s. It explores the relationship between\n * caos and order.\n *\n * The function creates the canvas itself, depending on the columns available\n * in the output display and the number of squares per row and per column\n * requested by the caller. */\nlwCanvas *lwDrawSchotter(int console_cols, int squares_per_row, int squares_per_col) {\n    /* Calculate the canvas size. */\n    int canvas_width = console_cols*2;\n    int padding = canvas_width > 4 ? 2 : 0;\n    float square_side = (float)(canvas_width-padding*2) / squares_per_row;\n    int canvas_height = square_side * squares_per_col + padding*2;\n    lwCanvas *canvas = lwCreateCanvas(canvas_width, canvas_height, 0);\n\n    for (int y = 0; y < squares_per_col; y++) {\n        for (int x = 0; x < squares_per_row; x++) {\n            int sx = x * square_side + square_side/2 + padding;\n            int sy = y * square_side + square_side/2 + padding;\n            /* Rotate and translate randomly as we go down to lower\n             * rows. */\n            float angle = 0;\n            if (y > 1) {\n                float r1 = (float)rand() / RAND_MAX / squares_per_col * y;\n                float r2 = (float)rand() / RAND_MAX / squares_per_col * y;\n                float r3 = (float)rand() / RAND_MAX / squares_per_col * y;\n                if (rand() % 2) r1 = -r1;\n                if (rand() % 2) r2 = -r2;\n                if (rand() % 2) r3 = -r3;\n                angle = r1;\n                sx += r2*square_side/3;\n                sy += r3*square_side/3;\n            }\n            lwDrawSquare(canvas,sx,sy,square_side,angle,1);\n        }\n    }\n\n    return canvas;\n}\n\n/* Converts the canvas to an SDS string representing the UTF8 characters to\n * print to the terminal in order to obtain a graphical representaiton of the\n * logical canvas. The actual returned string will require a terminal that is\n * width/2 large and height/4 tall in order to hold the whole image without\n * overflowing or scrolling, since each Barille character is 2x4. */\nstatic sds renderCanvas(lwCanvas *canvas) {\n    sds text = sdsempty();\n    for (int y = 0; y < canvas->height; y += 4) {\n        for (int x = 0; x < canvas->width; x += 2) {\n            /* We need to emit groups of 8 bits according to a specific\n             * arrangement. See lwTranslatePixelsGroup() for more info. */\n            int byte = 0;\n            if (lwGetPixel(canvas,x,y)) byte |= (1<<0);\n            if (lwGetPixel(canvas,x,y+1)) byte |= (1<<1);\n            if (lwGetPixel(canvas,x,y+2)) byte |= (1<<2);\n            if (lwGetPixel(canvas,x+1,y)) byte |= (1<<3);\n            if (lwGetPixel(canvas,x+1,y+1)) byte |= (1<<4);\n            if (lwGetPixel(canvas,x+1,y+2)) byte |= (1<<5);\n            if (lwGetPixel(canvas,x,y+3)) byte |= (1<<6);\n            if (lwGetPixel(canvas,x+1,y+3)) byte |= (1<<7);\n            char unicode[3];\n            lwTranslatePixelsGroup(byte,unicode);\n            text = sdscatlen(text,unicode,3);\n        }\n        if (y != canvas->height-1) text = sdscatlen(text,\"\\n\",1);\n    }\n    return text;\n}\n\n/* The LOLWUT command:\n *\n * LOLWUT [terminal columns] [squares-per-row] [squares-per-col]\n *\n * By default the command uses 66 columns, 8 squares per row, 12 squares\n * per column.\n */\nvoid lolwut5Command(client *c) {\n    long cols = 66;\n    long squares_per_row = 8;\n    long squares_per_col = 12;\n\n    /* Parse the optional arguments if any. */\n    if (c->argc > 1 &&\n        getLongFromObjectOrReply(c,c->argv[1],&cols,NULL) != C_OK)\n        return;\n\n    if (c->argc > 2 &&\n        getLongFromObjectOrReply(c,c->argv[2],&squares_per_row,NULL) != C_OK)\n        return;\n\n    if (c->argc > 3 &&\n        getLongFromObjectOrReply(c,c->argv[3],&squares_per_col,NULL) != C_OK)\n        return;\n\n    /* Limits. We want LOLWUT to be always reasonably fast and cheap to execute\n     * so we have maximum number of columns, rows, and output resulution. */\n    if (cols < 1) cols = 1;\n    if (cols > 1000) cols = 1000;\n    if (squares_per_row < 1) squares_per_row = 1;\n    if (squares_per_row > 200) squares_per_row = 200;\n    if (squares_per_col < 1) squares_per_col = 1;\n    if (squares_per_col > 200) squares_per_col = 200;\n\n    /* Generate some computer art and reply. */\n    lwCanvas *canvas = lwDrawSchotter(cols,squares_per_row,squares_per_col);\n    sds rendered = renderCanvas(canvas);\n    rendered = sdscat(rendered,\n        \"\\nGeorg Nees - schotter, plotter on paper, 1968. Redis ver. \");\n    rendered = sdscat(rendered,REDIS_VERSION);\n    rendered = sdscatlen(rendered,\"\\n\",1);\n    addReplyVerbatim(c,rendered,sdslen(rendered),\"txt\");\n    sdsfree(rendered);\n    lwFreeCanvas(canvas);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lolwut6.c",
    "content": "/*\n * Copyright (c) 2019, 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 * This file implements the LOLWUT command. The command should do something\n * fun and interesting, and should be replaced by a new implementation at\n * each new version of Redis.\n *\n * Thanks to Michele Hiki Falcone for the original image that ispired\n * the image, part of his game, Plaguemon.\n *\n * Thanks to the Shhh computer art collective for the help in tuning the\n * output to have a better artistic effect.\n */\n\n#include \"server.h\"\n#include \"lolwut.h\"\n\n/* Render the canvas using the four gray levels of the standard color\n * terminal: they match very well to the grayscale display of the gameboy. */\nstatic sds renderCanvas(lwCanvas *canvas) {\n    sds text = sdsempty();\n    for (int y = 0; y < canvas->height; y++) {\n        for (int x = 0; x < canvas->width; x++) {\n            int color = lwGetPixel(canvas,x,y);\n            char *ce; /* Color escape sequence. */\n\n            /* Note that we set both the foreground and background color.\n             * This way we are able to get a more consistent result among\n             * different terminals implementations. */\n            switch(color) {\n            case 0: ce = \"0;30;40m\"; break;    /* Black */\n            case 1: ce = \"0;90;100m\"; break;   /* Gray 1 */\n            case 2: ce = \"0;37;47m\"; break;    /* Gray 2 */\n            case 3: ce = \"0;97;107m\"; break;   /* White */\n            default: ce = \"0;30;40m\"; break;   /* Just for safety. */\n            }\n            text = sdscatprintf(text,\"\\033[%s \\033[0m\",ce);\n        }\n        if (y != canvas->height-1) text = sdscatlen(text,\"\\n\",1);\n    }\n    return text;\n}\n\n/* Draw a skyscraper on the canvas, according to the parameters in the\n * 'skyscraper' structure. Window colors are random and are always one\n * of the two grays. */\nstruct skyscraper {\n    int xoff;       /* X offset. */\n    int width;      /* Pixels width. */\n    int height;     /* Pixels height. */\n    int windows;    /* Draw windows if true. */\n    int color;      /* Color of the skyscraper. */\n};\n\nvoid generateSkyscraper(lwCanvas *canvas, struct skyscraper *si) {\n    int starty = canvas->height-1;\n    int endy = starty - si->height + 1;\n    for (int y = starty; y >= endy; y--) {\n        for (int x = si->xoff; x < si->xoff+si->width; x++) {\n            /* The roof is four pixels less wide. */\n            if (y == endy && (x <= si->xoff+1 || x >= si->xoff+si->width-2))\n                continue;\n            int color = si->color;\n            /* Alter the color if this is a place where we want to\n             * draw a window. We check that we are in the inner part of the\n             * skyscraper, so that windows are far from the borders. */\n            if (si->windows &&\n                x > si->xoff+1 &&\n                x < si->xoff+si->width-2 &&\n                y > endy+1 &&\n                y < starty-1)\n            {\n                /* Calculate the x,y position relative to the start of\n                 * the window area. */\n                int relx = x - (si->xoff+1);\n                int rely = y - (endy+1);\n\n                /* Note that we want the windows to be two pixels wide\n                 * but just one pixel tall, because terminal \"pixels\"\n                 * (characters) are not square. */\n                if (relx/2 % 2 && rely % 2) {\n                    do {\n                        color = 1 + rand() % 2;\n                    } while (color == si->color);\n                    /* Except we want adjacent pixels creating the same\n                     * window to be the same color. */\n                    if (relx % 2) color = lwGetPixel(canvas,x-1,y);\n                }\n            }\n            lwDrawPixel(canvas,x,y,color);\n        }\n    }\n}\n\n/* Generate a skyline inspired by the parallax backgrounds of 8 bit games. */\nvoid generateSkyline(lwCanvas *canvas) {\n    struct skyscraper si;\n\n    /* First draw the background skyscraper without windows, using the\n     * two different grays. We use two passes to make sure that the lighter\n     * ones are always in the background. */\n    for (int color = 2; color >= 1; color--) {\n        si.color = color;\n        for (int offset = -10; offset < canvas->width;) {\n            offset += rand() % 8;\n            si.xoff = offset;\n            si.width = 10 + rand()%9;\n            if (color == 2)\n                si.height = canvas->height/2 + rand()%canvas->height/2;\n            else\n                si.height = canvas->height/2 + rand()%canvas->height/3;\n            si.windows = 0;\n            generateSkyscraper(canvas, &si);\n            if (color == 2)\n                offset += si.width/2;\n            else\n                offset += si.width+1;\n        }\n    }\n\n    /* Now draw the foreground skyscraper with the windows. */\n    si.color = 0;\n    for (int offset = -10; offset < canvas->width;) {\n        offset += rand() % 8;\n        si.xoff = offset;\n        si.width = 5 + rand()%14;\n        if (si.width % 4) si.width += (si.width % 3);\n        si.height = canvas->height/3 + rand()%canvas->height/2;\n        si.windows = 1;\n        generateSkyscraper(canvas, &si);\n        offset += si.width+5;\n    }\n}\n\n/* The LOLWUT 6 command:\n *\n * LOLWUT [columns] [rows]\n *\n * By default the command uses 80 columns, 40 squares per row\n * per column.\n */\nvoid lolwut6Command(client *c) {\n    long cols = 80;\n    long rows = 20;\n\n    /* Parse the optional arguments if any. */\n    if (c->argc > 1 &&\n        getLongFromObjectOrReply(c,c->argv[1],&cols,NULL) != C_OK)\n        return;\n\n    if (c->argc > 2 &&\n        getLongFromObjectOrReply(c,c->argv[2],&rows,NULL) != C_OK)\n        return;\n\n    /* Limits. We want LOLWUT to be always reasonably fast and cheap to execute\n     * so we have maximum number of columns, rows, and output resulution. */\n    if (cols < 1) cols = 1;\n    if (cols > 1000) cols = 1000;\n    if (rows < 1) rows = 1;\n    if (rows > 1000) rows = 1000;\n\n    /* Generate the city skyline and reply. */\n    lwCanvas *canvas = lwCreateCanvas(cols,rows,3);\n    generateSkyline(canvas);\n    sds rendered = renderCanvas(canvas);\n    rendered = sdscat(rendered,\n        \"\\nDedicated to the 8 bit game developers of past and present.\\n\"\n        \"Original 8 bit image from Plaguemon by hikikomori. Redis ver. \");\n    rendered = sdscat(rendered,REDIS_VERSION);\n    rendered = sdscatlen(rendered,\"\\n\",1);\n    addReplyVerbatim(c,rendered,sdslen(rendered),\"txt\");\n    sdsfree(rendered);\n    lwFreeCanvas(canvas);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 22).\n */\n#ifndef HLOG\n# define HLOG 16\n#endif\n\n/*\n * Sacrifice very little compression quality in favour of compression speed.\n * This gives almost the same compression as the default code, and is\n * (very roughly) 15% faster. This is the preferred mode of operation.\n */\n#ifndef VERY_FAST\n# define VERY_FAST 1\n#endif\n\n/*\n * Sacrifice some more compression quality in favour of compression speed.\n * (roughly 1-2% worse compression for large blocks and\n * 9-10% for small, redundant, blocks and >>20% better speed in both cases)\n * In short: when in need for speed, enable this for binary data,\n * possibly disable this for text data.\n */\n#ifndef ULTRA_FAST\n# define ULTRA_FAST 0\n#endif\n\n/*\n * Unconditionally aligning does not cost very much, so do it if unsure\n */\n#ifndef STRICT_ALIGN\n# if !(defined(__i386) || defined (__amd64))\n#  define STRICT_ALIGN 1\n# else\n#  define STRICT_ALIGN 0\n# endif\n#endif\n\n/*\n * You may choose to pre-set the hash table (might be faster on some\n * modern cpus and large (>>64k) blocks, and also makes compression\n * deterministic/repeatable when the configuration otherwise is the same).\n */\n#ifndef INIT_HTAB\n# define INIT_HTAB 0\n#endif\n\n/*\n * Avoid assigning values to errno variable? for some embedding purposes\n * (linux kernel for example), this is necessary. NOTE: this breaks\n * the documentation in lzf.h. Avoiding errno has no speed impact.\n */\n#ifndef AVOID_ERRNO\n# define AVOID_ERRNO 0\n#endif\n\n/*\n * Whether to pass the LZF_STATE variable as argument, or allocate it\n * on the stack. For small-stack environments, define this to 1.\n * NOTE: this breaks the prototype in lzf.h.\n */\n#ifndef LZF_STATE_ARG\n# define LZF_STATE_ARG 0\n#endif\n\n/*\n * Whether to add extra checks for input validity in lzf_decompress\n * and return EINVAL if the input stream has been corrupted. This\n * only shields against overflowing the input buffer and will not\n * detect most corrupted streams.\n * This check is not normally noticeable on modern hardware\n * (<1% slowdown), but might slow down older cpus considerably.\n */\n#ifndef CHECK_INPUT\n# define CHECK_INPUT 1\n#endif\n\n/*\n * Whether to store pointers or offsets inside the hash table. On\n * 64 bit architetcures, pointers take up twice as much space,\n * and might also be slower. Default is to autodetect.\n */\n/*#define LZF_USER_OFFSETS autodetect */\n\n/*****************************************************************************/\n/* nothing should be changed below */\n\n#ifdef __cplusplus\n# include <cstring>\n# include <climits>\nusing namespace std;\n#else\n# include <string.h>\n# include <limits.h>\n#endif\n\n#ifndef LZF_USE_OFFSETS\n# if defined (WIN32)\n#  define LZF_USE_OFFSETS defined(_M_X64)\n# else\n#  if __cplusplus > 199711L\n#   include <cstdint>\n#  else\n#   include <stdint.h>\n#  endif\n#  define LZF_USE_OFFSETS (UINTPTR_MAX > 0xffffffffU)\n# endif\n#endif\n\ntypedef unsigned char u8;\n\n#if LZF_USE_OFFSETS\n# define LZF_HSLOT_BIAS ((const u8 *)in_data)\n  typedef unsigned int LZF_HSLOT;\n#else\n# define LZF_HSLOT_BIAS 0\n  typedef const u8 *LZF_HSLOT;\n#endif\n\ntypedef LZF_HSLOT LZF_STATE[1 << (HLOG)];\n\n#if !STRICT_ALIGN\n/* for unaligned accesses we need a 16 bit datatype. */\n# if USHRT_MAX == 65535\n    typedef unsigned short u16;\n# elif UINT_MAX == 65535\n    typedef unsigned int u16;\n# else\n#  undef STRICT_ALIGN\n#  define STRICT_ALIGN 1\n# endif\n#endif\n\n#if ULTRA_FAST\n# undef VERY_FAST\n#endif\n\n#endif\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lzf_c.c",
    "content": "/*\n * Copyright (c) 2000-2010 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 * the data format is not dependent on the hash function.\n * the hash function might seem strange, just believe me,\n * it works ;)\n */\n#ifndef FRST\n# define FRST(p) (((p[0]) << 8) | p[1])\n# define NEXT(v,p) (((v) << 8) | p[2])\n# if ULTRA_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h  ) & (HSIZE - 1))\n# elif VERY_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# else\n#  define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# endif\n#endif\n/*\n * IDX works because it is very similar to a multiplicative hash, e.g.\n * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1))\n * the latter is also quite fast on newer CPUs, and compresses similarly.\n *\n * the next one is also quite good, albeit slow ;)\n * (int)(cos(h & 0xffffff) * 1e6)\n */\n\n#if 0\n/* original lzv-like hash function, much worse and thus slower */\n# define FRST(p) (p[0] << 5) ^ p[1]\n# define NEXT(v,p) ((v) << 5) ^ p[2]\n# define IDX(h) ((h) & (HSIZE - 1))\n#endif\n\n#define        MAX_LIT        (1 <<  5)\n#define        MAX_OFF        (1 << 13)\n#define        MAX_REF        ((1 << 8) + (1 << 3))\n\n#if __GNUC__ >= 3\n# define expect(expr,value)         __builtin_expect ((expr),(value))\n# define inline                     inline\n#else\n# define expect(expr,value)         (expr)\n# define inline                     static\n#endif\n\n#define expect_false(expr) expect ((expr) != 0, 0)\n#define expect_true(expr)  expect ((expr) != 0, 1)\n\n/*\n * compressed format\n *\n * 000LLLLL <L+1>    ; literal, L+1=1..33 octets\n * LLLooooo oooooooo ; backref L+1=1..7 octets, o+1=1..4096 offset\n * 111ooooo LLLLLLLL oooooooo ; backref L+8 octets, o+1=1..4096 offset\n *\n */\n\nunsigned int\nlzf_compress (const void *const in_data, unsigned int in_len,\n\t      void *out_data, unsigned int out_len\n#if LZF_STATE_ARG\n              , LZF_STATE htab\n#endif\n              )\n{\n#if !LZF_STATE_ARG\n  LZF_STATE htab;\n#endif\n  const u8 *ip = (const u8 *)in_data;\n        u8 *op = (u8 *)out_data;\n  const u8 *in_end  = ip + in_len;\n        u8 *out_end = op + out_len;\n  const u8 *ref;\n\n  /* off requires a type wide enough to hold a general pointer difference.\n   * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only\n   * works for differences within a single object). We also assume that no\n   * no bit pattern traps. Since the only platform that is both non-POSIX\n   * and fails to support both assumptions is windows 64 bit, we make a\n   * special workaround for it.\n   */\n#if defined (WIN32) && defined (_M_X64)\n  unsigned _int64 off; /* workaround for missing POSIX compliance */\n#else\n  unsigned long off;\n#endif\n  unsigned int hval;\n  int lit;\n\n  if (!in_len || !out_len)\n    return 0;\n\n#if INIT_HTAB\n  memset (htab, 0, sizeof (htab));\n#endif\n\n  lit = 0; op++; /* start run */\n\n  hval = FRST (ip);\n  while (ip < in_end - 2)\n    {\n      LZF_HSLOT *hslot;\n\n      hval = NEXT (hval, ip);\n      hslot = htab + IDX (hval);\n      ref = *hslot + LZF_HSLOT_BIAS; *hslot = ip - LZF_HSLOT_BIAS;\n\n      if (1\n#if INIT_HTAB\n          && ref < ip /* the next test will actually take care of this, but this is faster */\n#endif\n          && (off = ip - ref - 1) < MAX_OFF\n          && ref > (u8 *)in_data\n          && ref[2] == ip[2]\n#if STRICT_ALIGN\n          && ((ref[1] << 8) | ref[0]) == ((ip[1] << 8) | ip[0])\n#else\n          && *(u16 *)ref == *(u16 *)ip\n#endif\n        )\n        {\n          /* match found at *ref++ */\n          unsigned int len = 2;\n          unsigned int maxlen = in_end - ip - len;\n          maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;\n\n          if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */\n            if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */\n              return 0;\n\n          op [- lit - 1] = lit - 1; /* stop run */\n          op -= !lit; /* undo run if length is zero */\n\n          for (;;)\n            {\n              if (expect_true (maxlen > 16))\n                {\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                }\n\n              do\n                len++;\n              while (len < maxlen && ref[len] == ip[len]);\n\n              break;\n            }\n\n          len -= 2; /* len is now #octets - 1 */\n          ip++;\n\n          if (len < 7)\n            {\n              *op++ = (off >> 8) + (len << 5);\n            }\n          else\n            {\n              *op++ = (off >> 8) + (  7 << 5);\n              *op++ = len - 7;\n            }\n\n          *op++ = off;\n\n          lit = 0; op++; /* start run */\n\n          ip += len + 1;\n\n          if (expect_false (ip >= in_end - 2))\n            break;\n\n#if ULTRA_FAST || VERY_FAST\n          --ip;\n# if VERY_FAST && !ULTRA_FAST\n          --ip;\n# endif\n          hval = FRST (ip);\n\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n          ip++;\n\n# if VERY_FAST && !ULTRA_FAST\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n          ip++;\n# endif\n#else\n          ip -= len + 1;\n\n          do\n            {\n              hval = NEXT (hval, ip);\n              htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n              ip++;\n            }\n          while (len--);\n#endif\n        }\n      else\n        {\n          /* one more literal byte we must copy */\n          if (expect_false (op >= out_end))\n            return 0;\n\n          lit++; *op++ = *ip++;\n\n          if (expect_false (lit == MAX_LIT))\n            {\n              op [- lit - 1] = lit - 1; /* stop run */\n              lit = 0; op++; /* start run */\n            }\n        }\n    }\n\n  if (op + 3 > out_end) /* at most 3 bytes can be missing here */\n    return 0;\n\n  while (ip < in_end)\n    {\n      lit++; *op++ = *ip++;\n\n      if (expect_false (lit == MAX_LIT))\n        {\n          op [- lit - 1] = lit - 1; /* stop run */\n          lit = 0; op++; /* start run */\n        }\n    }\n\n  op [- lit - 1] = lit - 1; /* end run */\n  op -= !lit; /* undo run if length is zero */\n\n  return op - (u8 *)out_data;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/lzf_d.c",
    "content": "/*\n * Copyright (c) 2000-2010 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#if USE_REP_MOVSB /* small win on amd, big loss on intel */\n#if (__i386 || __amd64) && __GNUC__ >= 3\n# define lzf_movsb(dst, src, len)                \\\n   asm (\"rep movsb\"                              \\\n        : \"=D\" (dst), \"=S\" (src), \"=c\" (len)     \\\n        :  \"0\" (dst),  \"1\" (src),  \"2\" (len));\n#endif\n#endif\n\n#if defined(__GNUC__) && __GNUC__ >= 5\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wimplicit-fallthrough\"\n#endif\nunsigned int\nlzf_decompress (const void *const in_data,  unsigned int in_len,\n                void             *out_data, unsigned int out_len)\n{\n  u8 const *ip = (const u8 *)in_data;\n  u8       *op = (u8 *)out_data;\n  u8 const *const in_end  = ip + in_len;\n  u8       *const out_end = op + out_len;\n\n  do\n    {\n      unsigned int ctrl = *ip++;\n\n      if (ctrl < (1 << 5)) /* literal run */\n        {\n          ctrl++;\n\n          if (op + ctrl > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n#if CHECK_INPUT\n          if (ip + ctrl > in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n\n#ifdef lzf_movsb\n          lzf_movsb (op, ip, ctrl);\n#else\n          switch (ctrl)\n            {\n              case 32: *op++ = *ip++; case 31: *op++ = *ip++; case 30: *op++ = *ip++; case 29: *op++ = *ip++;\n              case 28: *op++ = *ip++; case 27: *op++ = *ip++; case 26: *op++ = *ip++; case 25: *op++ = *ip++;\n              case 24: *op++ = *ip++; case 23: *op++ = *ip++; case 22: *op++ = *ip++; case 21: *op++ = *ip++;\n              case 20: *op++ = *ip++; case 19: *op++ = *ip++; case 18: *op++ = *ip++; case 17: *op++ = *ip++;\n              case 16: *op++ = *ip++; case 15: *op++ = *ip++; case 14: *op++ = *ip++; case 13: *op++ = *ip++;\n              case 12: *op++ = *ip++; case 11: *op++ = *ip++; case 10: *op++ = *ip++; case  9: *op++ = *ip++;\n              case  8: *op++ = *ip++; case  7: *op++ = *ip++; case  6: *op++ = *ip++; case  5: *op++ = *ip++;\n              case  4: *op++ = *ip++; case  3: *op++ = *ip++; case  2: *op++ = *ip++; case  1: *op++ = *ip++;\n            }\n#endif\n        }\n      else /* back reference */\n        {\n          unsigned int len = ctrl >> 5;\n\n          u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;\n\n#if CHECK_INPUT\n          if (ip >= in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n          if (len == 7)\n            {\n              len += *ip++;\n#if CHECK_INPUT\n              if (ip >= in_end)\n                {\n                  SET_ERRNO (EINVAL);\n                  return 0;\n                }\n#endif\n            }\n\n          ref -= *ip++;\n\n          if (op + len + 2 > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n          if (ref < (u8 *)out_data)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n\n#ifdef lzf_movsb\n          len += 2;\n          lzf_movsb (op, ref, len);\n#else\n          switch (len)\n            {\n              default:\n                len += 2;\n\n                if (op >= ref + len)\n                  {\n                    /* disjunct areas */\n                    memcpy (op, ref, len);\n                    op += len;\n                  }\n                else\n                  {\n                    /* overlapping, use octte by octte copying */\n                    do\n                      *op++ = *ref++;\n                    while (--len);\n                  }\n\n                break;\n\n              case 9: *op++ = *ref++; /* fall-thru */\n              case 8: *op++ = *ref++; /* fall-thru */\n              case 7: *op++ = *ref++; /* fall-thru */\n              case 6: *op++ = *ref++; /* fall-thru */\n              case 5: *op++ = *ref++; /* fall-thru */\n              case 4: *op++ = *ref++; /* fall-thru */\n              case 3: *op++ = *ref++; /* fall-thru */\n              case 2: *op++ = *ref++; /* fall-thru */\n              case 1: *op++ = *ref++; /* fall-thru */\n              case 0: *op++ = *ref++; /* two octets more */\n                      *op++ = *ref++; /* fall-thru */\n            }\n#endif\n        }\n    }\n  while (ip < in_end);\n\n  return op - (u8 *)out_data;\n}\n#if defined(__GNUC__) && __GNUC__ >= 5\n#pragma GCC diagnostic pop\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <stdint.h>\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#if defined(__sun)\n#include <stropts.h>\n#endif\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. */\nint memtest_addressing(unsigned long *l, size_t bytes, int interactive) {\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 && interactive)\n            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            if (interactive) {\n                printf(\"\\n*** MEMORY ADDRESSING ERROR: %p contains %lu\\n\",\n                    (void*) p, *p);\n                exit(1);\n            }\n            return 1;\n        }\n        p++;\n        if ((j & 0xffff) == 0 && interactive)\n            memtest_progress_step(j+words,words*2,'A');\n    }\n    return 0;\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.\n *\n * In this test we can't call rand() since the system may be completely\n * unable to handle library calls, so we have to resort to our own\n * PRNG that only uses local state. We use an xorshift* PRNG. */\n#define xorshift64star_next() do { \\\n        rseed ^= rseed >> 12; \\\n        rseed ^= rseed << 25; \\\n        rseed ^= rseed >> 27; \\\n        rout = rseed * UINT64_C(2685821657736338717); \\\n} while(0)\n\nvoid memtest_fill_random(unsigned long *l, size_t bytes, int interactive) {\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    uint64_t rseed = UINT64_C(0xd13133de9afdb566); /* Just a random seed. */\n    uint64_t rout = 0;\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            xorshift64star_next();\n            *l1 = *l2 = (unsigned long) rout;\n            l1 += step;\n            l2 += step;\n            if ((w & 0xffff) == 0 && interactive)\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, int interactive)\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 && interactive)\n                memtest_progress_step(w+iwords*off,words,sym);\n        }\n    }\n}\n\nint memtest_compare(unsigned long *l, size_t bytes, int interactive) {\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            if (interactive) {\n                printf(\"\\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\\n\",\n                    (void*)l1, (void*)l2, *l1, *l2);\n                exit(1);\n            }\n            return 1;\n        }\n        l1 ++;\n        l2 ++;\n        if ((w & 0xffff) == 0 && interactive)\n            memtest_progress_step(w,words,'=');\n    }\n    return 0;\n}\n\nint memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times,\n                          int interactive)\n{\n    int j;\n    int errors = 0;\n\n    for (j = 0; j < times; j++) {\n        if (interactive) memtest_progress_start(\"Compare\",pass);\n        errors += memtest_compare(m,bytes,interactive);\n        if (interactive) memtest_progress_end();\n    }\n    return errors;\n}\n\n/* Test the specified memory. The number of bytes must be multiple of 4096.\n * If interactive is true the program exists with an error and prints\n * ASCII arts to show progresses. Instead when interactive is 0, it can\n * be used as an API call, and returns 1 if memory errors were found or\n * 0 if there were no errors detected. */\nint memtest_test(unsigned long *m, size_t bytes, int passes, int interactive) {\n    int pass = 0;\n    int errors = 0;\n\n    while (pass != passes) {\n        pass++;\n\n        if (interactive) memtest_progress_start(\"Addressing test\",pass);\n        errors += memtest_addressing(m,bytes,interactive);\n        if (interactive) memtest_progress_end();\n\n        if (interactive) memtest_progress_start(\"Random fill\",pass);\n        memtest_fill_random(m,bytes,interactive);\n        if (interactive) memtest_progress_end();\n        errors += memtest_compare_times(m,bytes,pass,4,interactive);\n\n        if (interactive) memtest_progress_start(\"Solid fill\",pass);\n        memtest_fill_value(m,bytes,0,(unsigned long)-1,'S',interactive);\n        if (interactive) memtest_progress_end();\n        errors += memtest_compare_times(m,bytes,pass,4,interactive);\n\n        if (interactive) memtest_progress_start(\"Checkerboard fill\",pass);\n        memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C',interactive);\n        if (interactive) memtest_progress_end();\n        errors += memtest_compare_times(m,bytes,pass,4,interactive);\n    }\n    return errors;\n}\n\n/* A version of memtest_test() that tests memory in small pieces\n * in order to restore the memory content at exit.\n *\n * One problem we have with this approach, is that the cache can avoid\n * real memory accesses, and we can't test big chunks of memory at the\n * same time, because we need to backup them on the stack (the allocator\n * may not be usable or we may be already in an out of memory condition).\n * So what we do is to try to trash the cache with useless memory accesses\n * between the fill and compare cycles. */\n#define MEMTEST_BACKUP_WORDS (1024*(1024/sizeof(long)))\n/* Random accesses of MEMTEST_DECACHE_SIZE are performed at the start and\n * end of the region between fill and compare cycles in order to trash\n * the cache. */\n#define MEMTEST_DECACHE_SIZE (1024*8)\nint memtest_preserving_test(unsigned long *m, size_t bytes, int passes) {\n    unsigned long backup[MEMTEST_BACKUP_WORDS];\n    unsigned long *p = m;\n    unsigned long *end = (unsigned long*) (((unsigned char*)m)+(bytes-MEMTEST_DECACHE_SIZE));\n    size_t left = bytes;\n    int errors = 0;\n\n    if (bytes & 4095) return 0; /* Can't test across 4k page boundaries. */\n    if (bytes < 4096*2) return 0; /* Can't test a single page. */\n\n    while(left) {\n        /* If we have to test a single final page, go back a single page\n         * so that we can test two pages, since the code can't test a single\n         * page but at least two. */\n        if (left == 4096) {\n            left += 4096;\n            p -= 4096/sizeof(unsigned long);\n        }\n\n        int pass = 0;\n        size_t len = (left > sizeof(backup)) ? sizeof(backup) : left;\n\n        /* Always test an even number of pages. */\n        if (len/4096 % 2) len -= 4096;\n\n        memcpy(backup,p,len); /* Backup. */\n        while(pass != passes) {\n            pass++;\n            errors += memtest_addressing(p,len,0);\n            memtest_fill_random(p,len,0);\n            if (bytes >= MEMTEST_DECACHE_SIZE) {\n                memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);\n                memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);\n            }\n            errors += memtest_compare_times(p,len,pass,4,0);\n            memtest_fill_value(p,len,0,(unsigned long)-1,'S',0);\n            if (bytes >= MEMTEST_DECACHE_SIZE) {\n                memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);\n                memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);\n            }\n            errors += memtest_compare_times(p,len,pass,4,0);\n            memtest_fill_value(p,len,ULONG_ONEZERO,ULONG_ZEROONE,'C',0);\n            if (bytes >= MEMTEST_DECACHE_SIZE) {\n                memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);\n                memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);\n            }\n            errors += memtest_compare_times(p,len,pass,4,0);\n        }\n        memcpy(p,backup,len); /* Restore. */\n        left -= len;\n        p += len/sizeof(unsigned long);\n    }\n    return errors;\n}\n\n/* Perform an interactive test allocating the specified number of megabytes. */\nvoid memtest_alloc_and_test(size_t megabytes, int passes) {\n    size_t bytes = megabytes*1024*1024;\n    unsigned long *m = malloc(bytes);\n\n    if (m == NULL) {\n        fprintf(stderr,\"Unable to allocate %zu megabytes: %s\",\n            megabytes, strerror(errno));\n        exit(1);\n    }\n    memtest_test(m,bytes,passes,1);\n    free(m);\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_alloc_and_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": "Chapter02/redis-6.0.3/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`\nif [ -n \"$SOURCE_DATE_EPOCH\" ]; then\n  BUILD_ID=$(date -u -d \"@$SOURCE_DATE_EPOCH\" +%s 2>/dev/null || date -u -r \"$SOURCE_DATE_EPOCH\" +%s 2>/dev/null || date -u +%s)\nfi\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": "Chapter02/redis-6.0.3/src/module.c",
    "content": "/*\n * Copyright (c) 2016, 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 \"server.h\"\n#include \"cluster.h\"\n#include \"rdb.h\"\n#include <dlfcn.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n\n/* --------------------------------------------------------------------------\n * Private data structures used by the modules system. Those are data\n * structures that are never exposed to Redis Modules, if not as void\n * pointers that have an API the module can call with them)\n * -------------------------------------------------------------------------- */\n\ntypedef struct RedisModuleInfoCtx {\n    struct RedisModule *module;\n    const char *requested_section;\n    sds info;           /* info string we collected so far */\n    int sections;       /* number of sections we collected so far */\n    int in_section;     /* indication if we're in an active section or not */\n    int in_dict_field;  /* indication that we're curreintly appending to a dict */\n} RedisModuleInfoCtx;\n\ntypedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);\n\n/* This structure represents a module inside the system. */\nstruct RedisModule {\n    void *handle;   /* Module dlopen() handle. */\n    char *name;     /* Module name. */\n    int ver;        /* Module version. We use just progressive integers. */\n    int apiver;     /* Module API version as requested during initialization.*/\n    list *types;    /* Module data types. */\n    list *usedby;   /* List of modules using APIs from this one. */\n    list *using;    /* List of modules we use some APIs of. */\n    list *filters;  /* List of filters the module has registered. */\n    int in_call;    /* RM_Call() nesting level */\n    int in_hook;    /* Hooks callback nesting level for this module (0 or 1). */\n    int options;    /* Module options and capabilities. */\n    int blocked_clients;         /* Count of RedisModuleBlockedClient in this module. */\n    RedisModuleInfoFunc info_cb; /* Callback for module to add INFO fields. */\n};\ntypedef struct RedisModule RedisModule;\n\n/* This represents a shared API. Shared APIs will be used to populate\n * the server.sharedapi dictionary, mapping names of APIs exported by\n * modules for other modules to use, to their structure specifying the\n * function pointer that can be called. */\nstruct RedisModuleSharedAPI {\n    void *func;\n    RedisModule *module;\n};\ntypedef struct RedisModuleSharedAPI RedisModuleSharedAPI;\n\nstatic dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/\n\n/* Entries in the context->amqueue array, representing objects to free\n * when the callback returns. */\nstruct AutoMemEntry {\n    void *ptr;\n    int type;\n};\n\n/* AutMemEntry type field values. */\n#define REDISMODULE_AM_KEY 0\n#define REDISMODULE_AM_STRING 1\n#define REDISMODULE_AM_REPLY 2\n#define REDISMODULE_AM_FREED 3 /* Explicitly freed by user already. */\n#define REDISMODULE_AM_DICT 4\n#define REDISMODULE_AM_INFO 5\n\n/* The pool allocator block. Redis Modules can allocate memory via this special\n * allocator that will automatically release it all once the callback returns.\n * This means that it can only be used for ephemeral allocations. However\n * there are two advantages for modules to use this API:\n *\n * 1) The memory is automatically released when the callback returns.\n * 2) This allocator is faster for many small allocations since whole blocks\n *    are allocated, and small pieces returned to the caller just advancing\n *    the index of the allocation.\n *\n * Allocations are always rounded to the size of the void pointer in order\n * to always return aligned memory chunks. */\n\n#define REDISMODULE_POOL_ALLOC_MIN_SIZE (1024*8)\n#define REDISMODULE_POOL_ALLOC_ALIGN (sizeof(void*))\n\ntypedef struct RedisModulePoolAllocBlock {\n    uint32_t size;\n    uint32_t used;\n    struct RedisModulePoolAllocBlock *next;\n    char memory[];\n} RedisModulePoolAllocBlock;\n\n/* This structure represents the context in which Redis modules operate.\n * Most APIs module can access, get a pointer to the context, so that the API\n * implementation can hold state across calls, or remember what to free after\n * the call and so forth.\n *\n * Note that not all the context structure is always filled with actual values\n * but only the fields needed in a given context. */\n\nstruct RedisModuleBlockedClient;\n\nstruct RedisModuleCtx {\n    void *getapifuncptr;            /* NOTE: Must be the first field. */\n    struct RedisModule *module;     /* Module reference. */\n    client *client;                 /* Client calling a command. */\n    struct RedisModuleBlockedClient *blocked_client; /* Blocked client for\n                                                        thread safe context. */\n    struct AutoMemEntry *amqueue;   /* Auto memory queue of objects to free. */\n    int amqueue_len;                /* Number of slots in amqueue. */\n    int amqueue_used;               /* Number of used slots in amqueue. */\n    int flags;                      /* REDISMODULE_CTX_... flags. */\n    void **postponed_arrays;        /* To set with RM_ReplySetArrayLength(). */\n    int postponed_arrays_count;     /* Number of entries in postponed_arrays. */\n    void *blocked_privdata;         /* Privdata set when unblocking a client. */\n    RedisModuleString *blocked_ready_key; /* Key ready when the reply callback\n                                             gets called for clients blocked\n                                             on keys. */\n\n    /* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST flag set. */\n    int *keys_pos;\n    int keys_count;\n\n    struct RedisModulePoolAllocBlock *pa_head;\n    redisOpArray saved_oparray;    /* When propagating commands in a callback\n                                      we reallocate the \"also propagate\" op\n                                      array. Here we save the old one to\n                                      restore it later. */\n};\ntypedef struct RedisModuleCtx RedisModuleCtx;\n\n#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, {0}}\n#define REDISMODULE_CTX_MULTI_EMITTED (1<<0)\n#define REDISMODULE_CTX_AUTO_MEMORY (1<<1)\n#define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<2)\n#define REDISMODULE_CTX_BLOCKED_REPLY (1<<3)\n#define REDISMODULE_CTX_BLOCKED_TIMEOUT (1<<4)\n#define REDISMODULE_CTX_THREAD_SAFE (1<<5)\n#define REDISMODULE_CTX_BLOCKED_DISCONNECTED (1<<6)\n#define REDISMODULE_CTX_MODULE_COMMAND_CALL (1<<7)\n\n/* This represents a Redis key opened with RM_OpenKey(). */\nstruct RedisModuleKey {\n    RedisModuleCtx *ctx;\n    redisDb *db;\n    robj *key;      /* Key name object. */\n    robj *value;    /* Value object, or NULL if the key was not found. */\n    void *iter;     /* Iterator. */\n    int mode;       /* Opening mode. */\n\n    /* Zset iterator. */\n    uint32_t ztype;         /* REDISMODULE_ZSET_RANGE_* */\n    zrangespec zrs;         /* Score range. */\n    zlexrangespec zlrs;     /* Lex range. */\n    uint32_t zstart;        /* Start pos for positional ranges. */\n    uint32_t zend;          /* End pos for positional ranges. */\n    void *zcurrent;         /* Zset iterator current node. */\n    int zer;                /* Zset iterator end reached flag\n                               (true if end was reached). */\n};\ntypedef struct RedisModuleKey RedisModuleKey;\n\n/* RedisModuleKey 'ztype' values. */\n#define REDISMODULE_ZSET_RANGE_NONE 0       /* This must always be 0. */\n#define REDISMODULE_ZSET_RANGE_LEX 1\n#define REDISMODULE_ZSET_RANGE_SCORE 2\n#define REDISMODULE_ZSET_RANGE_POS 3\n\n/* Function pointer type of a function representing a command inside\n * a Redis module. */\nstruct RedisModuleBlockedClient;\ntypedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, void **argv, int argc);\ntypedef void (*RedisModuleDisconnectFunc) (RedisModuleCtx *ctx, struct RedisModuleBlockedClient *bc);\n\n/* This struct holds the information about a command registered by a module.*/\nstruct RedisModuleCommandProxy {\n    struct RedisModule *module;\n    RedisModuleCmdFunc func;\n    struct redisCommand *rediscmd;\n};\ntypedef struct RedisModuleCommandProxy RedisModuleCommandProxy;\n\n#define REDISMODULE_REPLYFLAG_NONE 0\n#define REDISMODULE_REPLYFLAG_TOPARSE (1<<0) /* Protocol must be parsed. */\n#define REDISMODULE_REPLYFLAG_NESTED (1<<1)  /* Nested reply object. No proto\n                                                or struct free. */\n\n/* Reply of RM_Call() function. The function is filled in a lazy\n * way depending on the function called on the reply structure. By default\n * only the type, proto and protolen are filled. */\ntypedef struct RedisModuleCallReply {\n    RedisModuleCtx *ctx;\n    int type;       /* REDISMODULE_REPLY_... */\n    int flags;      /* REDISMODULE_REPLYFLAG_...  */\n    size_t len;     /* Len of strings or num of elements of arrays. */\n    char *proto;    /* Raw reply protocol. An SDS string at top-level object. */\n    size_t protolen;/* Length of protocol. */\n    union {\n        const char *str; /* String pointer for string and error replies. This\n                            does not need to be freed, always points inside\n                            a reply->proto buffer of the reply object or, in\n                            case of array elements, of parent reply objects. */\n        long long ll;    /* Reply value for integer reply. */\n        struct RedisModuleCallReply *array; /* Array of sub-reply elements. */\n    } val;\n} RedisModuleCallReply;\n\n/* Structure representing a blocked client. We get a pointer to such\n * an object when blocking from modules. */\ntypedef struct RedisModuleBlockedClient {\n    client *client;  /* Pointer to the blocked client. or NULL if the client\n                        was destroyed during the life of this object. */\n    RedisModule *module;    /* Module blocking the client. */\n    RedisModuleCmdFunc reply_callback; /* Reply callback on normal completion.*/\n    RedisModuleCmdFunc timeout_callback; /* Reply callback on timeout. */\n    RedisModuleDisconnectFunc disconnect_callback; /* Called on disconnection.*/\n    void (*free_privdata)(RedisModuleCtx*,void*);/* privdata cleanup callback.*/\n    void *privdata;     /* Module private data that may be used by the reply\n                           or timeout callback. It is set via the\n                           RedisModule_UnblockClient() API. */\n    client *reply_client;           /* Fake client used to accumulate replies\n                                       in thread safe contexts. */\n    int dbid;           /* Database number selected by the original client. */\n    int blocked_on_keys;    /* If blocked via RM_BlockClientOnKeys(). */\n    int unblocked;          /* Already on the moduleUnblocked list. */\n} RedisModuleBlockedClient;\n\nstatic pthread_mutex_t moduleUnblockedClientsMutex = PTHREAD_MUTEX_INITIALIZER;\nstatic list *moduleUnblockedClients;\n\n/* We need a mutex that is unlocked / relocked in beforeSleep() in order to\n * allow thread safe contexts to execute commands at a safe moment. */\nstatic pthread_mutex_t moduleGIL = PTHREAD_MUTEX_INITIALIZER;\n\n\n/* Function pointer type for keyspace event notification subscriptions from modules. */\ntypedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);\n\n/* Keyspace notification subscriber information.\n * See RM_SubscribeToKeyspaceEvents() for more information. */\ntypedef struct RedisModuleKeyspaceSubscriber {\n    /* The module subscribed to the event */\n    RedisModule *module;\n    /* Notification callback in the module*/\n    RedisModuleNotificationFunc notify_callback;\n    /* A bit mask of the events the module is interested in */\n    int event_mask;\n    /* Active flag set on entry, to avoid reentrant subscribers\n     * calling themselves */\n    int active;\n} RedisModuleKeyspaceSubscriber;\n\n/* The module keyspace notification subscribers list */\nstatic list *moduleKeyspaceSubscribers;\n\n/* Static client recycled for when we need to provide a context with a client\n * in a situation where there is no client to provide. This avoidsallocating\n * a new client per round. For instance this is used in the keyspace\n * notifications, timers and cluster messages callbacks. */\nstatic client *moduleFreeContextReusedClient;\n\n/* Data structures related to the exported dictionary data structure. */\ntypedef struct RedisModuleDict {\n    rax *rax;                       /* The radix tree. */\n} RedisModuleDict;\n\ntypedef struct RedisModuleDictIter {\n    RedisModuleDict *dict;\n    raxIterator ri;\n} RedisModuleDictIter;\n\ntypedef struct RedisModuleCommandFilterCtx {\n    RedisModuleString **argv;\n    int argc;\n} RedisModuleCommandFilterCtx;\n\ntypedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);\n\ntypedef struct RedisModuleCommandFilter {\n    /* The module that registered the filter */\n    RedisModule *module;\n    /* Filter callback function */\n    RedisModuleCommandFilterFunc callback;\n    /* REDISMODULE_CMDFILTER_* flags */\n    int flags;\n} RedisModuleCommandFilter;\n\n/* Registered filters */\nstatic list *moduleCommandFilters;\n\ntypedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);\n\nstatic struct RedisModuleForkInfo {\n    RedisModuleForkDoneHandler done_handler;\n    void* done_handler_user_data;\n} moduleForkInfo = {0};\n\ntypedef struct RedisModuleServerInfoData {\n    rax *rax;                       /* parsed info data. */\n} RedisModuleServerInfoData;\n\n/* Flags for moduleCreateArgvFromUserFormat(). */\n#define REDISMODULE_ARGV_REPLICATE (1<<0)\n#define REDISMODULE_ARGV_NO_AOF (1<<1)\n#define REDISMODULE_ARGV_NO_REPLICAS (1<<2)\n\n/* Determine whether Redis should signalModifiedKey implicitly.\n * In case 'ctx' has no 'module' member (and therefore no module->options),\n * we assume default behavior, that is, Redis signals.\n * (see RM_GetThreadSafeContext) */\n#define SHOULD_SIGNAL_MODIFIED_KEYS(ctx) \\\n    ctx->module? !(ctx->module->options & REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED) : 1\n\n/* Server events hooks data structures and defines: this modules API\n * allow modules to subscribe to certain events in Redis, such as\n * the start and end of an RDB or AOF save, the change of role in replication,\n * and similar other events. */\n\ntypedef struct RedisModuleEventListener {\n    RedisModule *module;\n    RedisModuleEvent event;\n    RedisModuleEventCallback callback;\n} RedisModuleEventListener;\n\nlist *RedisModule_EventListeners; /* Global list of all the active events. */\nunsigned long long ModulesInHooks = 0; /* Total number of modules in hooks\n                                          callbacks right now. */\n\n/* Data structures related to the redis module users */\n\n/* This callback type is called by moduleNotifyUserChanged() every time\n * a user authenticated via the module API is associated with a different\n * user or gets disconnected. */\ntypedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);\n\n/* This is the object returned by RM_CreateModuleUser(). The module API is\n * able to create users, set ACLs to such users, and later authenticate\n * clients using such newly created users. */\ntypedef struct RedisModuleUser {\n    user *user; /* Reference to the real redis user */\n} RedisModuleUser;\n\n\n/* --------------------------------------------------------------------------\n * Prototypes\n * -------------------------------------------------------------------------- */\n\nvoid RM_FreeCallReply(RedisModuleCallReply *reply);\nvoid RM_CloseKey(RedisModuleKey *key);\nvoid autoMemoryCollect(RedisModuleCtx *ctx);\nrobj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap);\nvoid moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx);\nvoid RM_ZsetRangeStop(RedisModuleKey *kp);\nstatic void zsetKeyReset(RedisModuleKey *key);\nvoid RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d);\nvoid RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);\n\n/* --------------------------------------------------------------------------\n * Heap allocation raw functions\n * -------------------------------------------------------------------------- */\n\n/* Use like malloc(). Memory allocated with this function is reported in\n * Redis INFO memory, used for keys eviction according to maxmemory settings\n * and in general is taken into account as memory allocated by Redis.\n * You should avoid using malloc(). */\nvoid *RM_Alloc(size_t bytes) {\n    return zmalloc(bytes);\n}\n\n/* Use like calloc(). Memory allocated with this function is reported in\n * Redis INFO memory, used for keys eviction according to maxmemory settings\n * and in general is taken into account as memory allocated by Redis.\n * You should avoid using calloc() directly. */\nvoid *RM_Calloc(size_t nmemb, size_t size) {\n    return zcalloc(nmemb*size);\n}\n\n/* Use like realloc() for memory obtained with RedisModule_Alloc(). */\nvoid* RM_Realloc(void *ptr, size_t bytes) {\n    return zrealloc(ptr,bytes);\n}\n\n/* Use like free() for memory obtained by RedisModule_Alloc() and\n * RedisModule_Realloc(). However you should never try to free with\n * RedisModule_Free() memory allocated with malloc() inside your module. */\nvoid RM_Free(void *ptr) {\n    zfree(ptr);\n}\n\n/* Like strdup() but returns memory allocated with RedisModule_Alloc(). */\nchar *RM_Strdup(const char *str) {\n    return zstrdup(str);\n}\n\n/* --------------------------------------------------------------------------\n * Pool allocator\n * -------------------------------------------------------------------------- */\n\n/* Release the chain of blocks used for pool allocations. */\nvoid poolAllocRelease(RedisModuleCtx *ctx) {\n    RedisModulePoolAllocBlock *head = ctx->pa_head, *next;\n\n    while(head != NULL) {\n        next = head->next;\n        zfree(head);\n        head = next;\n    }\n    ctx->pa_head = NULL;\n}\n\n/* Return heap allocated memory that will be freed automatically when the\n * module callback function returns. Mostly suitable for small allocations\n * that are short living and must be released when the callback returns\n * anyway. The returned memory is aligned to the architecture word size\n * if at least word size bytes are requested, otherwise it is just\n * aligned to the next power of two, so for example a 3 bytes request is\n * 4 bytes aligned while a 2 bytes request is 2 bytes aligned.\n *\n * There is no realloc style function since when this is needed to use the\n * pool allocator is not a good idea.\n *\n * The function returns NULL if `bytes` is 0. */\nvoid *RM_PoolAlloc(RedisModuleCtx *ctx, size_t bytes) {\n    if (bytes == 0) return NULL;\n    RedisModulePoolAllocBlock *b = ctx->pa_head;\n    size_t left = b ? b->size - b->used : 0;\n\n    /* Fix alignment. */\n    if (left >= bytes) {\n        size_t alignment = REDISMODULE_POOL_ALLOC_ALIGN;\n        while (bytes < alignment && alignment/2 >= bytes) alignment /= 2;\n        if (b->used % alignment)\n            b->used += alignment - (b->used % alignment);\n        left = (b->used > b->size) ? 0 : b->size - b->used;\n    }\n\n    /* Create a new block if needed. */\n    if (left < bytes) {\n        size_t blocksize = REDISMODULE_POOL_ALLOC_MIN_SIZE;\n        if (blocksize < bytes) blocksize = bytes;\n        b = zmalloc(sizeof(*b) + blocksize);\n        b->size = blocksize;\n        b->used = 0;\n        b->next = ctx->pa_head;\n        ctx->pa_head = b;\n    }\n\n    char *retval = b->memory + b->used;\n    b->used += bytes;\n    return retval;\n}\n\n/* --------------------------------------------------------------------------\n * Helpers for modules API implementation\n * -------------------------------------------------------------------------- */\n\n/* Create an empty key of the specified type. 'kp' must point to a key object\n * opened for writing where the .value member is set to NULL because the\n * key was found to be non existing.\n *\n * On success REDISMODULE_OK is returned and the key is populated with\n * the value of the specified type. The function fails and returns\n * REDISMODULE_ERR if:\n *\n * 1) The key is not open for writing.\n * 2) The key is not empty.\n * 3) The specified type is unknown.\n */\nint moduleCreateEmptyKey(RedisModuleKey *key, int type) {\n    robj *obj;\n\n    /* The key must be open for writing and non existing to proceed. */\n    if (!(key->mode & REDISMODULE_WRITE) || key->value)\n        return REDISMODULE_ERR;\n\n    switch(type) {\n    case REDISMODULE_KEYTYPE_LIST:\n        obj = createQuicklistObject();\n        quicklistSetOptions(obj->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n        break;\n    case REDISMODULE_KEYTYPE_ZSET:\n        obj = createZsetZiplistObject();\n        break;\n    case REDISMODULE_KEYTYPE_HASH:\n        obj = createHashObject();\n        break;\n    default: return REDISMODULE_ERR;\n    }\n    dbAdd(key->db,key->key,obj);\n    key->value = obj;\n    return REDISMODULE_OK;\n}\n\n/* This function is called in low-level API implementation functions in order\n * to check if the value associated with the key remained empty after an\n * operation that removed elements from an aggregate data type.\n *\n * If this happens, the key is deleted from the DB and the key object state\n * is set to the right one in order to be targeted again by write operations\n * possibly recreating the key if needed.\n *\n * The function returns 1 if the key value object is found empty and is\n * deleted, otherwise 0 is returned. */\nint moduleDelKeyIfEmpty(RedisModuleKey *key) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL) return 0;\n    int isempty;\n    robj *o = key->value;\n\n    switch(o->type) {\n    case OBJ_LIST: isempty = listTypeLength(o) == 0; break;\n    case OBJ_SET: isempty = setTypeSize(o) == 0; break;\n    case OBJ_ZSET: isempty = zsetLength(o) == 0; break;\n    case OBJ_HASH: isempty = hashTypeLength(o) == 0; break;\n    case OBJ_STREAM: isempty = streamLength(o) == 0; break;\n    default: isempty = 0;\n    }\n\n    if (isempty) {\n        dbDelete(key->db,key->key);\n        key->value = NULL;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Service API exported to modules\n *\n * Note that all the exported APIs are called RM_<funcname> in the core\n * and RedisModule_<funcname> in the module side (defined as function\n * pointers in redismodule.h). In this way the dynamic linker does not\n * mess with our global function pointers, overriding it with the symbols\n * defined in the main executable having the same names.\n * -------------------------------------------------------------------------- */\n\n/* Lookup the requested module API and store the function pointer into the\n * target pointer. The function returns REDISMODULE_ERR if there is no such\n * named API, otherwise REDISMODULE_OK.\n *\n * This function is not meant to be used by modules developer, it is only\n * used implicitly by including redismodule.h. */\nint RM_GetApi(const char *funcname, void **targetPtrPtr) {\n    dictEntry *he = dictFind(server.moduleapi, funcname);\n    if (!he) return REDISMODULE_ERR;\n    *targetPtrPtr = dictGetVal(he);\n    return REDISMODULE_OK;\n}\n\n/* Helper function for when a command callback is called, in order to handle\n * details needed to correctly replicate commands. */\nvoid moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) {\n    client *c = ctx->client;\n\n    /* We don't need to do anything here if the context was never used\n     * in order to propagate commands. */\n    if (!(ctx->flags & REDISMODULE_CTX_MULTI_EMITTED)) return;\n\n    if (c->flags & CLIENT_LUA) return;\n\n    /* Handle the replication of the final EXEC, since whatever a command\n     * emits is always wrapped around MULTI/EXEC. */\n    alsoPropagate(server.execCommand,c->db->id,&shared.exec,1,\n        PROPAGATE_AOF|PROPAGATE_REPL);\n\n    /* If this is not a module command context (but is instead a simple\n     * callback context), we have to handle directly the \"also propagate\"\n     * array and emit it. In a module command call this will be handled\n     * directly by call(). */\n    if (!(ctx->flags & REDISMODULE_CTX_MODULE_COMMAND_CALL) &&\n        server.also_propagate.numops)\n    {\n        for (int j = 0; j < server.also_propagate.numops; j++) {\n            redisOp *rop = &server.also_propagate.ops[j];\n            int target = rop->target;\n            if (target)\n                propagate(rop->cmd,rop->dbid,rop->argv,rop->argc,target);\n        }\n        redisOpArrayFree(&server.also_propagate);\n        /* Restore the previous oparray in case of nexted use of the API. */\n        server.also_propagate = ctx->saved_oparray;\n    }\n}\n\n/* Free the context after the user function was called. */\nvoid moduleFreeContext(RedisModuleCtx *ctx) {\n    moduleHandlePropagationAfterCommandCallback(ctx);\n    autoMemoryCollect(ctx);\n    poolAllocRelease(ctx);\n    if (ctx->postponed_arrays) {\n        zfree(ctx->postponed_arrays);\n        ctx->postponed_arrays_count = 0;\n        serverLog(LL_WARNING,\n            \"API misuse detected in module %s: \"\n            \"RedisModule_ReplyWithArray(REDISMODULE_POSTPONED_ARRAY_LEN) \"\n            \"not matched by the same number of RedisModule_SetReplyArrayLen() \"\n            \"calls.\",\n            ctx->module->name);\n    }\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) freeClient(ctx->client);\n}\n\n/* This Redis command binds the normal Redis command invocation with commands\n * exported by modules. */\nvoid RedisModuleCommandDispatcher(client *c) {\n    RedisModuleCommandProxy *cp = (void*)(unsigned long)c->cmd->getkeys_proc;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n\n    ctx.flags |= REDISMODULE_CTX_MODULE_COMMAND_CALL;\n    ctx.module = cp->module;\n    ctx.client = c;\n    cp->func(&ctx,(void**)c->argv,c->argc);\n    moduleFreeContext(&ctx);\n\n    /* In some cases processMultibulkBuffer uses sdsMakeRoomFor to\n     * expand the query buffer, and in order to avoid a big object copy\n     * the query buffer SDS may be used directly as the SDS string backing\n     * the client argument vectors: sometimes this will result in the SDS\n     * string having unused space at the end. Later if a module takes ownership\n     * of the RedisString, such space will be wasted forever. Inside the\n     * Redis core this is not a problem because tryObjectEncoding() is called\n     * before storing strings in the key space. Here we need to do it\n     * for the module. */\n    for (int i = 0; i < c->argc; i++) {\n        /* Only do the work if the module took ownership of the object:\n         * in that case the refcount is no longer 1. */\n        if (c->argv[i]->refcount > 1)\n            trimStringObjectIfNeeded(c->argv[i]);\n    }\n}\n\n/* This function returns the list of keys, with the same interface as the\n * 'getkeys' function of the native commands, for module commands that exported\n * the \"getkeys-api\" flag during the registration. This is done when the\n * list of keys are not at fixed positions, so that first/last/step cannot\n * be used.\n *\n * In order to accomplish its work, the module command is called, flagging\n * the context in a way that the command can recognize this is a special\n * \"get keys\" call by calling RedisModule_IsKeysPositionRequest(ctx). */\nint *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    RedisModuleCommandProxy *cp = (void*)(unsigned long)cmd->getkeys_proc;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n\n    ctx.module = cp->module;\n    ctx.client = NULL;\n    ctx.flags |= REDISMODULE_CTX_KEYS_POS_REQUEST;\n    cp->func(&ctx,(void**)argv,argc);\n    int *res = ctx.keys_pos;\n    if (numkeys) *numkeys = ctx.keys_count;\n    moduleFreeContext(&ctx);\n    return res;\n}\n\n/* Return non-zero if a module command, that was declared with the\n * flag \"getkeys-api\", is called in a special way to get the keys positions\n * and not to get executed. Otherwise zero is returned. */\nint RM_IsKeysPositionRequest(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) != 0;\n}\n\n/* When a module command is called in order to obtain the position of\n * keys, since it was flagged as \"getkeys-api\" during the registration,\n * the command implementation checks for this special call using the\n * RedisModule_IsKeysPositionRequest() API and uses this function in\n * order to report keys, like in the following example:\n *\n *     if (RedisModule_IsKeysPositionRequest(ctx)) {\n *         RedisModule_KeyAtPos(ctx,1);\n *         RedisModule_KeyAtPos(ctx,2);\n *     }\n *\n *  Note: in the example below the get keys API would not be needed since\n *  keys are at fixed positions. This interface is only used for commands\n *  with a more complex structure. */\nvoid RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {\n    if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST)) return;\n    if (pos <= 0) return;\n    ctx->keys_pos = zrealloc(ctx->keys_pos,sizeof(int)*(ctx->keys_count+1));\n    ctx->keys_pos[ctx->keys_count++] = pos;\n}\n\n/* Helper for RM_CreateCommand(). Turns a string representing command\n * flags into the command flags used by the Redis core.\n *\n * It returns the set of flags, or -1 if unknown flags are found. */\nint64_t commandFlagsFromString(char *s) {\n    int count, j;\n    int64_t flags = 0;\n    sds *tokens = sdssplitlen(s,strlen(s),\" \",1,&count);\n    for (j = 0; j < count; j++) {\n        char *t = tokens[j];\n        if (!strcasecmp(t,\"write\")) flags |= CMD_WRITE;\n        else if (!strcasecmp(t,\"readonly\")) flags |= CMD_READONLY;\n        else if (!strcasecmp(t,\"admin\")) flags |= CMD_ADMIN;\n        else if (!strcasecmp(t,\"deny-oom\")) flags |= CMD_DENYOOM;\n        else if (!strcasecmp(t,\"deny-script\")) flags |= CMD_NOSCRIPT;\n        else if (!strcasecmp(t,\"allow-loading\")) flags |= CMD_LOADING;\n        else if (!strcasecmp(t,\"pubsub\")) flags |= CMD_PUBSUB;\n        else if (!strcasecmp(t,\"random\")) flags |= CMD_RANDOM;\n        else if (!strcasecmp(t,\"allow-stale\")) flags |= CMD_STALE;\n        else if (!strcasecmp(t,\"no-monitor\")) flags |= CMD_SKIP_MONITOR;\n        else if (!strcasecmp(t,\"no-slowlog\")) flags |= CMD_SKIP_SLOWLOG;\n        else if (!strcasecmp(t,\"fast\")) flags |= CMD_FAST;\n        else if (!strcasecmp(t,\"no-auth\")) flags |= CMD_NO_AUTH;\n        else if (!strcasecmp(t,\"getkeys-api\")) flags |= CMD_MODULE_GETKEYS;\n        else if (!strcasecmp(t,\"no-cluster\")) flags |= CMD_MODULE_NO_CLUSTER;\n        else break;\n    }\n    sdsfreesplitres(tokens,count);\n    if (j != count) return -1; /* Some token not processed correctly. */\n    return flags;\n}\n\n/* Register a new command in the Redis server, that will be handled by\n * calling the function pointer 'func' using the RedisModule calling\n * convention. The function returns REDISMODULE_ERR if the specified command\n * name is already busy or a set of invalid flags were passed, otherwise\n * REDISMODULE_OK is returned and the new command is registered.\n *\n * This function must be called during the initialization of the module\n * inside the RedisModule_OnLoad() function. Calling this function outside\n * of the initialization function is not defined.\n *\n * The command function type is the following:\n *\n *      int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);\n *\n * And is supposed to always return REDISMODULE_OK.\n *\n * The set of flags 'strflags' specify the behavior of the command, and should\n * be passed as a C string composed of space separated words, like for\n * example \"write deny-oom\". The set of flags are:\n *\n * * **\"write\"**:     The command may modify the data set (it may also read\n *                    from it).\n * * **\"readonly\"**:  The command returns data from keys but never writes.\n * * **\"admin\"**:     The command is an administrative command (may change\n *                    replication or perform similar tasks).\n * * **\"deny-oom\"**:  The command may use additional memory and should be\n *                    denied during out of memory conditions.\n * * **\"deny-script\"**:   Don't allow this command in Lua scripts.\n * * **\"allow-loading\"**: Allow this command while the server is loading data.\n *                        Only commands not interacting with the data set\n *                        should be allowed to run in this mode. If not sure\n *                        don't use this flag.\n * * **\"pubsub\"**:    The command publishes things on Pub/Sub channels.\n * * **\"random\"**:    The command may have different outputs even starting\n *                    from the same input arguments and key values.\n * * **\"allow-stale\"**: The command is allowed to run on slaves that don't\n *                      serve stale data. Don't use if you don't know what\n *                      this means.\n * * **\"no-monitor\"**: Don't propagate the command on monitor. Use this if\n *                     the command has sensible data among the arguments.\n * * **\"no-slowlog\"**: Don't log this command in the slowlog. Use this if\n *                     the command has sensible data among the arguments.\n * * **\"fast\"**:      The command time complexity is not greater\n *                    than O(log(N)) where N is the size of the collection or\n *                    anything else representing the normal scalability\n *                    issue with the command.\n * * **\"getkeys-api\"**: The command implements the interface to return\n *                      the arguments that are keys. Used when start/stop/step\n *                      is not enough because of the command syntax.\n * * **\"no-cluster\"**: The command should not register in Redis Cluster\n *                     since is not designed to work with it because, for\n *                     example, is unable to report the position of the\n *                     keys, programmatically creates key names, or any\n *                     other reason.\n * * **\"no-auth\"**:    This command can be run by an un-authenticated client.\n *                     Normally this is used by a command that is used\n *                     to authenticate a client. \n */\nint RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {\n    int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0;\n    if (flags == -1) return REDISMODULE_ERR;\n    if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)\n        return REDISMODULE_ERR;\n\n    struct redisCommand *rediscmd;\n    RedisModuleCommandProxy *cp;\n    sds cmdname = sdsnew(name);\n\n    /* Check if the command name is busy. */\n    if (lookupCommand(cmdname) != NULL) {\n        sdsfree(cmdname);\n        return REDISMODULE_ERR;\n    }\n\n    /* Create a command \"proxy\", which is a structure that is referenced\n     * in the command table, so that the generic command that works as\n     * binding between modules and Redis, can know what function to call\n     * and what the module is.\n     *\n     * Note that we use the Redis command table 'getkeys_proc' in order to\n     * pass a reference to the command proxy structure. */\n    cp = zmalloc(sizeof(*cp));\n    cp->module = ctx->module;\n    cp->func = cmdfunc;\n    cp->rediscmd = zmalloc(sizeof(*rediscmd));\n    cp->rediscmd->name = cmdname;\n    cp->rediscmd->proc = RedisModuleCommandDispatcher;\n    cp->rediscmd->arity = -1;\n    cp->rediscmd->flags = flags | CMD_MODULE;\n    cp->rediscmd->getkeys_proc = (redisGetKeysProc*)(unsigned long)cp;\n    cp->rediscmd->firstkey = firstkey;\n    cp->rediscmd->lastkey = lastkey;\n    cp->rediscmd->keystep = keystep;\n    cp->rediscmd->microseconds = 0;\n    cp->rediscmd->calls = 0;\n    dictAdd(server.commands,sdsdup(cmdname),cp->rediscmd);\n    dictAdd(server.orig_commands,sdsdup(cmdname),cp->rediscmd);\n    cp->rediscmd->id = ACLGetCommandID(cmdname); /* ID used for ACL. */\n    return REDISMODULE_OK;\n}\n\n/* Called by RM_Init() to setup the `ctx->module` structure.\n *\n * This is an internal function, Redis modules developers don't need\n * to use it. */\nvoid RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {\n    RedisModule *module;\n\n    if (ctx->module != NULL) return;\n    module = zmalloc(sizeof(*module));\n    module->name = sdsnew((char*)name);\n    module->ver = ver;\n    module->apiver = apiver;\n    module->types = listCreate();\n    module->usedby = listCreate();\n    module->using = listCreate();\n    module->filters = listCreate();\n    module->in_call = 0;\n    module->in_hook = 0;\n    module->options = 0;\n    module->info_cb = 0;\n    ctx->module = module;\n}\n\n/* Return non-zero if the module name is busy.\n * Otherwise zero is returned. */\nint RM_IsModuleNameBusy(const char *name) {\n    sds modulename = sdsnew(name);\n    dictEntry *de = dictFind(modules,modulename);\n    sdsfree(modulename);\n    return de != NULL;\n}\n\n/* Return the current UNIX time in milliseconds. */\nlong long RM_Milliseconds(void) {\n    return mstime();\n}\n\n/* Set flags defining capabilities or behavior bit flags.\n *\n * REDISMODULE_OPTIONS_HANDLE_IO_ERRORS:\n * Generally, modules don't need to bother with this, as the process will just\n * terminate if a read error happens, however, setting this flag would allow\n * repl-diskless-load to work if enabled.\n * The module should use RedisModule_IsIOError after reads, before using the\n * data that was read, and in case of error, propagate it upwards, and also be\n * able to release the partially populated value and all it's allocations. */\nvoid RM_SetModuleOptions(RedisModuleCtx *ctx, int options) {\n    ctx->module->options = options;\n}\n\n/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH\n * and client side caching). */\nint RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) {\n    signalModifiedKey(ctx->client,ctx->client->db,keyname);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Automatic memory management for modules\n * -------------------------------------------------------------------------- */\n\n/* Enable automatic memory management. See API.md for more information.\n *\n * The function must be called as the first function of a command implementation\n * that wants to use automatic memory. */\nvoid RM_AutoMemory(RedisModuleCtx *ctx) {\n    ctx->flags |= REDISMODULE_CTX_AUTO_MEMORY;\n}\n\n/* Add a new object to release automatically when the callback returns. */\nvoid autoMemoryAdd(RedisModuleCtx *ctx, int type, void *ptr) {\n    if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return;\n    if (ctx->amqueue_used == ctx->amqueue_len) {\n        ctx->amqueue_len *= 2;\n        if (ctx->amqueue_len < 16) ctx->amqueue_len = 16;\n        ctx->amqueue = zrealloc(ctx->amqueue,sizeof(struct AutoMemEntry)*ctx->amqueue_len);\n    }\n    ctx->amqueue[ctx->amqueue_used].type = type;\n    ctx->amqueue[ctx->amqueue_used].ptr = ptr;\n    ctx->amqueue_used++;\n}\n\n/* Mark an object as freed in the auto release queue, so that users can still\n * free things manually if they want.\n *\n * The function returns 1 if the object was actually found in the auto memory\n * pool, otherwise 0 is returned. */\nint autoMemoryFreed(RedisModuleCtx *ctx, int type, void *ptr) {\n    if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return 0;\n\n    int count = (ctx->amqueue_used+1)/2;\n    for (int j = 0; j < count; j++) {\n        for (int side = 0; side < 2; side++) {\n            /* For side = 0 check right side of the array, for\n             * side = 1 check the left side instead (zig-zag scanning). */\n            int i = (side == 0) ? (ctx->amqueue_used - 1 - j) : j;\n            if (ctx->amqueue[i].type == type &&\n                ctx->amqueue[i].ptr == ptr)\n            {\n                ctx->amqueue[i].type = REDISMODULE_AM_FREED;\n\n                /* Switch the freed element and the last element, to avoid growing\n                 * the queue unnecessarily if we allocate/free in a loop */\n                if (i != ctx->amqueue_used-1) {\n                    ctx->amqueue[i] = ctx->amqueue[ctx->amqueue_used-1];\n                }\n\n                /* Reduce the size of the queue because we either moved the top\n                 * element elsewhere or freed it */\n                ctx->amqueue_used--;\n                return 1;\n            }\n        }\n    }\n    return 0;\n}\n\n/* Release all the objects in queue. */\nvoid autoMemoryCollect(RedisModuleCtx *ctx) {\n    if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return;\n    /* Clear the AUTO_MEMORY flag from the context, otherwise the functions\n     * we call to free the resources, will try to scan the auto release\n     * queue to mark the entries as freed. */\n    ctx->flags &= ~REDISMODULE_CTX_AUTO_MEMORY;\n    int j;\n    for (j = 0; j < ctx->amqueue_used; j++) {\n        void *ptr = ctx->amqueue[j].ptr;\n        switch(ctx->amqueue[j].type) {\n        case REDISMODULE_AM_STRING: decrRefCount(ptr); break;\n        case REDISMODULE_AM_REPLY: RM_FreeCallReply(ptr); break;\n        case REDISMODULE_AM_KEY: RM_CloseKey(ptr); break;\n        case REDISMODULE_AM_DICT: RM_FreeDict(NULL,ptr); break;\n        case REDISMODULE_AM_INFO: RM_FreeServerInfo(NULL,ptr); break;\n        }\n    }\n    ctx->flags |= REDISMODULE_CTX_AUTO_MEMORY;\n    zfree(ctx->amqueue);\n    ctx->amqueue = NULL;\n    ctx->amqueue_len = 0;\n    ctx->amqueue_used = 0;\n}\n\n/* --------------------------------------------------------------------------\n * String objects APIs\n * -------------------------------------------------------------------------- */\n\n/* Create a new module string object. The returned string must be freed\n * with RedisModule_FreeString(), unless automatic memory is enabled.\n *\n * The string is created by copying the `len` bytes starting\n * at `ptr`. No reference is retained to the passed buffer.\n *\n * The module context 'ctx' is optional and may be NULL if you want to create\n * a string out of the context scope. However in that case, the automatic\n * memory management will not be available, and the string memory must be\n * managed manually. */\nRedisModuleString *RM_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len) {\n    RedisModuleString *o = createStringObject(ptr,len);\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n    return o;\n}\n\n/* Create a new module string object from a printf format and arguments.\n * The returned string must be freed with RedisModule_FreeString(), unless\n * automatic memory is enabled.\n *\n * The string is created using the sds formatter function sdscatvprintf().\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringPrintf(RedisModuleCtx *ctx, const char *fmt, ...) {\n    sds s = sdsempty();\n\n    va_list ap;\n    va_start(ap, fmt);\n    s = sdscatvprintf(s, fmt, ap);\n    va_end(ap);\n\n    RedisModuleString *o = createObject(OBJ_STRING, s);\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n\n    return o;\n}\n\n\n/* Like RedisModule_CreatString(), but creates a string starting from a long long\n * integer instead of taking a buffer and its length.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management.\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll) {\n    char buf[LONG_STR_SIZE];\n    size_t len = ll2string(buf,sizeof(buf),ll);\n    return RM_CreateString(ctx,buf,len);\n}\n\n/* Like RedisModule_CreatString(), but creates a string starting from a double\n * integer instead of taking a buffer and its length.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management. */\nRedisModuleString *RM_CreateStringFromDouble(RedisModuleCtx *ctx, double d) {\n    char buf[128];\n    size_t len = d2string(buf,sizeof(buf),d);\n    return RM_CreateString(ctx,buf,len);\n}\n\n/* Like RedisModule_CreatString(), but creates a string starting from a long\n * double.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management.\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringFromLongDouble(RedisModuleCtx *ctx, long double ld, int humanfriendly) {\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    size_t len = ld2string(buf,sizeof(buf),ld,\n        (humanfriendly ? LD_STR_HUMAN : LD_STR_AUTO));\n    return RM_CreateString(ctx,buf,len);\n}\n\n/* Like RedisModule_CreatString(), but creates a string starting from another\n * RedisModuleString.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management.\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringFromString(RedisModuleCtx *ctx, const RedisModuleString *str) {\n    RedisModuleString *o = dupStringObject(str);\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n    return o;\n}\n\n/* Free a module string object obtained with one of the Redis modules API calls\n * that return new string objects.\n *\n * It is possible to call this function even when automatic memory management\n * is enabled. In that case the string will be released ASAP and removed\n * from the pool of string to release at the end.\n *\n * If the string was created with a NULL context 'ctx', it is also possible to\n * pass ctx as NULL when releasing the string (but passing a context will not\n * create any issue). Strings created with a context should be freed also passing\n * the context, so if you want to free a string out of context later, make sure\n * to create it using a NULL context. */\nvoid RM_FreeString(RedisModuleCtx *ctx, RedisModuleString *str) {\n    decrRefCount(str);\n    if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_STRING,str);\n}\n\n/* Every call to this function, will make the string 'str' requiring\n * an additional call to RedisModule_FreeString() in order to really\n * free the string. Note that the automatic freeing of the string obtained\n * enabling modules automatic memory management counts for one\n * RedisModule_FreeString() call (it is just executed automatically).\n *\n * Normally you want to call this function when, at the same time\n * the following conditions are true:\n *\n * 1) You have automatic memory management enabled.\n * 2) You want to create string objects.\n * 3) Those string objects you create need to live *after* the callback\n *    function(for example a command implementation) creating them returns.\n *\n * Usually you want this in order to store the created string object\n * into your own data structure, for example when implementing a new data\n * type.\n *\n * Note that when memory management is turned off, you don't need\n * any call to RetainString() since creating a string will always result\n * into a string that lives after the callback function returns, if\n * no FreeString() call is performed.\n *\n * It is possible to call this function with a NULL context. */\nvoid RM_RetainString(RedisModuleCtx *ctx, RedisModuleString *str) {\n    if (ctx == NULL || !autoMemoryFreed(ctx,REDISMODULE_AM_STRING,str)) {\n        /* Increment the string reference counting only if we can't\n         * just remove the object from the list of objects that should\n         * be reclaimed. Why we do that, instead of just incrementing\n         * the refcount in any case, and let the automatic FreeString()\n         * call at the end to bring the refcount back at the desired\n         * value? Because this way we ensure that the object refcount\n         * value is 1 (instead of going to 2 to be dropped later to 1)\n         * after the call to this function. This is needed for functions\n         * like RedisModule_StringAppendBuffer() to work. */\n        incrRefCount(str);\n    }\n}\n\n/* Given a string module object, this function returns the string pointer\n * and length of the string. The returned pointer and length should only\n * be used for read only accesses and never modified. */\nconst char *RM_StringPtrLen(const RedisModuleString *str, size_t *len) {\n    if (str == NULL) {\n        const char *errmsg = \"(NULL string reply referenced in module)\";\n        if (len) *len = strlen(errmsg);\n        return errmsg;\n    }\n    if (len) *len = sdslen(str->ptr);\n    return str->ptr;\n}\n\n/* --------------------------------------------------------------------------\n * Higher level string operations\n * ------------------------------------------------------------------------- */\n\n/* Convert the string into a long long integer, storing it at `*ll`.\n * Returns REDISMODULE_OK on success. If the string can't be parsed\n * as a valid, strict long long (no spaces before/after), REDISMODULE_ERR\n * is returned. */\nint RM_StringToLongLong(const RedisModuleString *str, long long *ll) {\n    return string2ll(str->ptr,sdslen(str->ptr),ll) ? REDISMODULE_OK :\n                                                     REDISMODULE_ERR;\n}\n\n/* Convert the string into a double, storing it at `*d`.\n * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is\n * not a valid string representation of a double value. */\nint RM_StringToDouble(const RedisModuleString *str, double *d) {\n    int retval = getDoubleFromObject(str,d);\n    return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Convert the string into a long double, storing it at `*ld`.\n * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is\n * not a valid string representation of a double value. */\nint RM_StringToLongDouble(const RedisModuleString *str, long double *ld) {\n    int retval = string2ld(str->ptr,sdslen(str->ptr),ld);\n    return retval ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Compare two string objects, returning -1, 0 or 1 respectively if\n * a < b, a == b, a > b. Strings are compared byte by byte as two\n * binary blobs without any encoding care / collation attempt. */\nint RM_StringCompare(RedisModuleString *a, RedisModuleString *b) {\n    return compareStringObjects(a,b);\n}\n\n/* Return the (possibly modified in encoding) input 'str' object if\n * the string is unshared, otherwise NULL is returned. */\nRedisModuleString *moduleAssertUnsharedString(RedisModuleString *str) {\n    if (str->refcount != 1) {\n        serverLog(LL_WARNING,\n            \"Module attempted to use an in-place string modify operation \"\n            \"with a string referenced multiple times. Please check the code \"\n            \"for API usage correctness.\");\n        return NULL;\n    }\n    if (str->encoding == OBJ_ENCODING_EMBSTR) {\n        /* Note: here we \"leak\" the additional allocation that was\n         * used in order to store the embedded string in the object. */\n        str->ptr = sdsnewlen(str->ptr,sdslen(str->ptr));\n        str->encoding = OBJ_ENCODING_RAW;\n    } else if (str->encoding == OBJ_ENCODING_INT) {\n        /* Convert the string from integer to raw encoding. */\n        str->ptr = sdsfromlonglong((long)str->ptr);\n        str->encoding = OBJ_ENCODING_RAW;\n    }\n    return str;\n}\n\n/* Append the specified buffer to the string 'str'. The string must be a\n * string created by the user that is referenced only a single time, otherwise\n * REDISMODULE_ERR is returned and the operation is not performed. */\nint RM_StringAppendBuffer(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) {\n    UNUSED(ctx);\n    str = moduleAssertUnsharedString(str);\n    if (str == NULL) return REDISMODULE_ERR;\n    str->ptr = sdscatlen(str->ptr,buf,len);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Reply APIs\n *\n * Most functions always return REDISMODULE_OK so you can use it with\n * 'return' in order to return from the command implementation with:\n *\n *     if (... some condition ...)\n *         return RM_ReplyWithLongLong(ctx,mycount);\n * -------------------------------------------------------------------------- */\n\n/* Send an error about the number of arguments given to the command,\n * citing the command name in the error message.\n *\n * Example:\n *\n *     if (argc != 3) return RedisModule_WrongArity(ctx);\n */\nint RM_WrongArity(RedisModuleCtx *ctx) {\n    addReplyErrorFormat(ctx->client,\n        \"wrong number of arguments for '%s' command\",\n        (char*)ctx->client->argv[0]->ptr);\n    return REDISMODULE_OK;\n}\n\n/* Return the client object the `RM_Reply*` functions should target.\n * Normally this is just `ctx->client`, that is the client that called\n * the module command, however in the case of thread safe contexts there\n * is no directly associated client (since it would not be safe to access\n * the client from a thread), so instead the blocked client object referenced\n * in the thread safe context, has a fake client that we just use to accumulate\n * the replies. Later, when the client is unblocked, the accumulated replies\n * are appended to the actual client.\n *\n * The function returns the client pointer depending on the context, or\n * NULL if there is no potential client. This happens when we are in the\n * context of a thread safe context that was not initialized with a blocked\n * client object. Other contexts without associated clients are the ones\n * initialized to run the timers callbacks. */\nclient *moduleGetReplyClient(RedisModuleCtx *ctx) {\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) {\n        if (ctx->blocked_client)\n            return ctx->blocked_client->reply_client;\n        else\n            return NULL;\n    } else {\n        /* If this is a non thread safe context, just return the client\n         * that is running the command if any. This may be NULL as well\n         * in the case of contexts that are not executed with associated\n         * clients, like timer contexts. */\n        return ctx->client;\n    }\n}\n\n/* Send an integer reply to the client, with the specified long long value.\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyLongLong(c,ll);\n    return REDISMODULE_OK;\n}\n\n/* Reply with an error or simple string (status message). Used to implement\n * ReplyWithSimpleString() and ReplyWithError().\n * The function always returns REDISMODULE_OK. */\nint replyWithStatus(RedisModuleCtx *ctx, const char *msg, char *prefix) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyProto(c,prefix,strlen(prefix));\n    addReplyProto(c,msg,strlen(msg));\n    addReplyProto(c,\"\\r\\n\",2);\n    return REDISMODULE_OK;\n}\n\n/* Reply with the error 'err'.\n *\n * Note that 'err' must contain all the error, including\n * the initial error code. The function only provides the initial \"-\", so\n * the usage is, for example:\n *\n *     RedisModule_ReplyWithError(ctx,\"ERR Wrong Type\");\n *\n * and not just:\n *\n *     RedisModule_ReplyWithError(ctx,\"Wrong Type\");\n *\n * The function always returns REDISMODULE_OK.\n */\nint RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) {\n    return replyWithStatus(ctx,err,\"-\");\n}\n\n/* Reply with a simple string (+... \\r\\n in RESP protocol). This replies\n * are suitable only when sending a small non-binary string with small\n * overhead, like \"OK\" or similar replies.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg) {\n    return replyWithStatus(ctx,msg,\"+\");\n}\n\n/* Reply with an array type of 'len' elements. However 'len' other calls\n * to `ReplyWith*` style functions must follow in order to emit the elements\n * of the array.\n *\n * When producing arrays with a number of element that is not known beforehand\n * the function can be called with the special count\n * REDISMODULE_POSTPONED_ARRAY_LEN, and the actual number of elements can be\n * later set with RedisModule_ReplySetArrayLength() (which will set the\n * latest \"open\" count if there are multiple ones).\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithArray(RedisModuleCtx *ctx, long len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    if (len == REDISMODULE_POSTPONED_ARRAY_LEN) {\n        ctx->postponed_arrays = zrealloc(ctx->postponed_arrays,sizeof(void*)*\n                (ctx->postponed_arrays_count+1));\n        ctx->postponed_arrays[ctx->postponed_arrays_count] =\n            addReplyDeferredLen(c);\n        ctx->postponed_arrays_count++;\n    } else {\n        addReplyArrayLen(c,len);\n    }\n    return REDISMODULE_OK;\n}\n\n/* Reply to the client with a null array, simply null in RESP3 \n * null array in RESP2.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithNullArray(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyNullArray(c);\n    return REDISMODULE_OK;\n}\n\n/* Reply to the client with an empty array. \n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithEmptyArray(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReply(c,shared.emptyarray);\n    return REDISMODULE_OK;\n}\n\n/* When RedisModule_ReplyWithArray() is used with the argument\n * REDISMODULE_POSTPONED_ARRAY_LEN, because we don't know beforehand the number\n * of items we are going to output as elements of the array, this function\n * will take care to set the array length.\n *\n * Since it is possible to have multiple array replies pending with unknown\n * length, this function guarantees to always set the latest array length\n * that was created in a postponed way.\n *\n * For example in order to output an array like [1,[10,20,30]] we\n * could write:\n *\n *      RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n *      RedisModule_ReplyWithLongLong(ctx,1);\n *      RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n *      RedisModule_ReplyWithLongLong(ctx,10);\n *      RedisModule_ReplyWithLongLong(ctx,20);\n *      RedisModule_ReplyWithLongLong(ctx,30);\n *      RedisModule_ReplySetArrayLength(ctx,3); // Set len of 10,20,30 array.\n *      RedisModule_ReplySetArrayLength(ctx,2); // Set len of top array\n *\n * Note that in the above example there is no reason to postpone the array\n * length, since we produce a fixed number of elements, but in the practice\n * the code may use an iterator or other ways of creating the output so\n * that is not easy to calculate in advance the number of elements.\n */\nvoid RM_ReplySetArrayLength(RedisModuleCtx *ctx, long len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return;\n    if (ctx->postponed_arrays_count == 0) {\n        serverLog(LL_WARNING,\n            \"API misuse detected in module %s: \"\n            \"RedisModule_ReplySetArrayLength() called without previous \"\n            \"RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN) \"\n            \"call.\", ctx->module->name);\n            return;\n    }\n    ctx->postponed_arrays_count--;\n    setDeferredArrayLen(c,\n            ctx->postponed_arrays[ctx->postponed_arrays_count],\n            len);\n    if (ctx->postponed_arrays_count == 0) {\n        zfree(ctx->postponed_arrays);\n        ctx->postponed_arrays = NULL;\n    }\n}\n\n/* Reply with a bulk string, taking in input a C buffer pointer and length.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyBulkCBuffer(c,(char*)buf,len);\n    return REDISMODULE_OK;\n}\n\n/* Reply with a bulk string, taking in input a C buffer pointer that is\n * assumed to be null-terminated.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithCString(RedisModuleCtx *ctx, const char *buf) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyBulkCString(c,(char*)buf);\n    return REDISMODULE_OK;\n}\n\n/* Reply with a bulk string, taking in input a RedisModuleString object.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyBulk(c,str);\n    return REDISMODULE_OK;\n}\n\n/* Reply with an empty string.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithEmptyString(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReply(c,shared.emptybulk);\n    return REDISMODULE_OK;\n}\n\n/* Reply with a binary safe string, which should not be escaped or filtered \n * taking in input a C buffer pointer and length.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithVerbatimString(RedisModuleCtx *ctx, const char *buf, size_t len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyVerbatim(c, buf, len, \"txt\");\n    return REDISMODULE_OK;\n}\n\n/* Reply to the client with a NULL.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithNull(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyNull(c);\n    return REDISMODULE_OK;\n}\n\n/* Reply exactly what a Redis command returned us with RedisModule_Call().\n * This function is useful when we use RedisModule_Call() in order to\n * execute some command, as we want to reply to the client exactly the\n * same reply we obtained by the command.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    sds proto = sdsnewlen(reply->proto, reply->protolen);\n    addReplySds(c,proto);\n    return REDISMODULE_OK;\n}\n\n/* Send a string reply obtained converting the double 'd' into a bulk string.\n * This function is basically equivalent to converting a double into\n * a string into a C buffer, and then calling the function\n * RedisModule_ReplyWithStringBuffer() with the buffer and length.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithDouble(RedisModuleCtx *ctx, double d) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyDouble(c,d);\n    return REDISMODULE_OK;\n}\n\n/* Send a string reply obtained converting the long double 'ld' into a bulk\n * string. This function is basically equivalent to converting a long double\n * into a string into a C buffer, and then calling the function\n * RedisModule_ReplyWithStringBuffer() with the buffer and length.\n * The double string uses human readable formatting (see\n * `addReplyHumanLongDouble` in networking.c).\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyHumanLongDouble(c, ld);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Commands replication API\n * -------------------------------------------------------------------------- */\n\n/* Helper function to replicate MULTI the first time we replicate something\n * in the context of a command execution. EXEC will be handled by the\n * RedisModuleCommandDispatcher() function. */\nvoid moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) {\n    /* Skip this if client explicitly wrap the command with MULTI, or if\n     * the module command was called by a script. */\n    if (ctx->client->flags & (CLIENT_MULTI|CLIENT_LUA)) return;\n    /* If we already emitted MULTI return ASAP. */\n    if (ctx->flags & REDISMODULE_CTX_MULTI_EMITTED) return;\n    /* If this is a thread safe context, we do not want to wrap commands\n     * executed into MULTI/EXEC, they are executed as single commands\n     * from an external client in essence. */\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) return;\n    /* If this is a callback context, and not a module command execution\n     * context, we have to setup the op array for the \"also propagate\" API\n     * so that RM_Replicate() will work. */\n    if (!(ctx->flags & REDISMODULE_CTX_MODULE_COMMAND_CALL)) {\n        ctx->saved_oparray = server.also_propagate;\n        redisOpArrayInit(&server.also_propagate);\n    }\n    execCommandPropagateMulti(ctx->client);\n    ctx->flags |= REDISMODULE_CTX_MULTI_EMITTED;\n}\n\n/* Replicate the specified command and arguments to slaves and AOF, as effect\n * of execution of the calling command implementation.\n *\n * The replicated commands are always wrapped into the MULTI/EXEC that\n * contains all the commands replicated in a given module command\n * execution. However the commands replicated with RedisModule_Call()\n * are the first items, the ones replicated with RedisModule_Replicate()\n * will all follow before the EXEC.\n *\n * Modules should try to use one interface or the other.\n *\n * This command follows exactly the same interface of RedisModule_Call(),\n * so a set of format specifiers must be passed, followed by arguments\n * matching the provided format specifiers.\n *\n * Please refer to RedisModule_Call() for more information.\n *\n * Using the special \"A\" and \"R\" modifiers, the caller can exclude either\n * the AOF or the replicas from the propagation of the specified command.\n * Otherwise, by default, the command will be propagated in both channels.\n *\n * ## Note about calling this function from a thread safe context:\n *\n * Normally when you call this function from the callback implementing a\n * module command, or any other callback provided by the Redis Module API,\n * Redis will accumulate all the calls to this function in the context of\n * the callback, and will propagate all the commands wrapped in a MULTI/EXEC\n * transaction. However when calling this function from a threaded safe context\n * that can live an undefined amount of time, and can be locked/unlocked in\n * at will, the behavior is different: MULTI/EXEC wrapper is not emitted\n * and the command specified is inserted in the AOF and replication stream\n * immediately.\n *\n * ## Return value\n *\n * The command returns REDISMODULE_ERR if the format specifiers are invalid\n * or the command name does not belong to a known command. */\nint RM_Replicate(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) {\n    struct redisCommand *cmd;\n    robj **argv = NULL;\n    int argc = 0, flags = 0, j;\n    va_list ap;\n\n    cmd = lookupCommandByCString((char*)cmdname);\n    if (!cmd) return REDISMODULE_ERR;\n\n    /* Create the client and dispatch the command. */\n    va_start(ap, fmt);\n    argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);\n    va_end(ap);\n    if (argv == NULL) return REDISMODULE_ERR;\n\n    /* Select the propagation target. Usually is AOF + replicas, however\n     * the caller can exclude one or the other using the \"A\" or \"R\"\n     * modifiers. */\n    int target = 0;\n    if (!(flags & REDISMODULE_ARGV_NO_AOF)) target |= PROPAGATE_AOF;\n    if (!(flags & REDISMODULE_ARGV_NO_REPLICAS)) target |= PROPAGATE_REPL;\n\n    /* Replicate! When we are in a threaded context, we want to just insert\n     * the replicated command ASAP, since it is not clear when the context\n     * will stop being used, so accumulating stuff does not make much sense,\n     * nor we could easily use the alsoPropagate() API from threads. */\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) {\n        propagate(cmd,ctx->client->db->id,argv,argc,target);\n    } else {\n        moduleReplicateMultiIfNeeded(ctx);\n        alsoPropagate(cmd,ctx->client->db->id,argv,argc,target);\n    }\n\n    /* Release the argv. */\n    for (j = 0; j < argc; j++) decrRefCount(argv[j]);\n    zfree(argv);\n    server.dirty++;\n    return REDISMODULE_OK;\n}\n\n/* This function will replicate the command exactly as it was invoked\n * by the client. Note that this function will not wrap the command into\n * a MULTI/EXEC stanza, so it should not be mixed with other replication\n * commands.\n *\n * Basically this form of replication is useful when you want to propagate\n * the command to the slaves and AOF file exactly as it was called, since\n * the command can just be re-executed to deterministically re-create the\n * new state starting from the old one.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplicateVerbatim(RedisModuleCtx *ctx) {\n    alsoPropagate(ctx->client->cmd,ctx->client->db->id,\n        ctx->client->argv,ctx->client->argc,\n        PROPAGATE_AOF|PROPAGATE_REPL);\n    server.dirty++;\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * DB and Key APIs -- Generic API\n * -------------------------------------------------------------------------- */\n\n/* Return the ID of the current client calling the currently active module\n * command. The returned ID has a few guarantees:\n *\n * 1. The ID is different for each different client, so if the same client\n *    executes a module command multiple times, it can be recognized as\n *    having the same ID, otherwise the ID will be different.\n * 2. The ID increases monotonically. Clients connecting to the server later\n *    are guaranteed to get IDs greater than any past ID previously seen.\n *\n * Valid IDs are from 1 to 2^64-1. If 0 is returned it means there is no way\n * to fetch the ID in the context the function was currently called.\n *\n * After obtaining the ID, it is possible to check if the command execution\n * is actually happening in the context of AOF loading, using this macro:\n *\n *      if (RedisModule_IsAOFClient(RedisModule_GetClientId(ctx)) {\n *          // Handle it differently.\n *      }\n */\nunsigned long long RM_GetClientId(RedisModuleCtx *ctx) {\n    if (ctx->client == NULL) return 0;\n    return ctx->client->id;\n}\n\n/* This is an helper for RM_GetClientInfoById() and other functions: given\n * a client, it populates the client info structure with the appropriate\n * fields depending on the version provided. If the version is not valid\n * then REDISMODULE_ERR is returned. Otherwise the function returns\n * REDISMODULE_OK and the structure pointed by 'ci' gets populated. */\n\nint modulePopulateClientInfoStructure(void *ci, client *client, int structver) {\n    if (structver != 1) return REDISMODULE_ERR;\n\n    RedisModuleClientInfoV1 *ci1 = ci;\n    memset(ci1,0,sizeof(*ci1));\n    ci1->version = structver;\n    if (client->flags & CLIENT_MULTI)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_MULTI;\n    if (client->flags & CLIENT_PUBSUB)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_PUBSUB;\n    if (client->flags & CLIENT_UNIX_SOCKET)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET;\n    if (client->flags & CLIENT_TRACKING)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_TRACKING;\n    if (client->flags & CLIENT_BLOCKED)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_BLOCKED;\n\n    int port;\n    connPeerToString(client->conn,ci1->addr,sizeof(ci1->addr),&port);\n    ci1->port = port;\n    ci1->db = client->db->id;\n    ci1->id = client->id;\n    return REDISMODULE_OK;\n}\n\n/* This is an helper for moduleFireServerEvent() and other functions:\n * It populates the replication info structure with the appropriate\n * fields depending on the version provided. If the version is not valid\n * then REDISMODULE_ERR is returned. Otherwise the function returns\n * REDISMODULE_OK and the structure pointed by 'ri' gets populated. */\nint modulePopulateReplicationInfoStructure(void *ri, int structver) {\n    if (structver != 1) return REDISMODULE_ERR;\n\n    RedisModuleReplicationInfoV1 *ri1 = ri;\n    memset(ri1,0,sizeof(*ri1));\n    ri1->version = structver;\n    ri1->master = server.masterhost==NULL;\n    ri1->masterhost = server.masterhost? server.masterhost: \"\";\n    ri1->masterport = server.masterport;\n    ri1->replid1 = server.replid;\n    ri1->replid2 = server.replid2;\n    ri1->repl1_offset = server.master_repl_offset;\n    ri1->repl2_offset = server.second_replid_offset;\n    return REDISMODULE_OK;\n}\n\n/* Return information about the client with the specified ID (that was\n * previously obtained via the RedisModule_GetClientId() API). If the\n * client exists, REDISMODULE_OK is returned, otherwise REDISMODULE_ERR\n * is returned.\n *\n * When the client exist and the `ci` pointer is not NULL, but points to\n * a structure of type RedisModuleClientInfo, previously initialized with\n * the correct REDISMODULE_CLIENTINFO_INITIALIZER, the structure is populated\n * with the following fields:\n *\n *      uint64_t flags;         // REDISMODULE_CLIENTINFO_FLAG_*\n *      uint64_t id;            // Client ID\n *      char addr[46];          // IPv4 or IPv6 address.\n *      uint16_t port;          // TCP port.\n *      uint16_t db;            // Selected DB.\n *\n * Note: the client ID is useless in the context of this call, since we\n *       already know, however the same structure could be used in other\n *       contexts where we don't know the client ID, yet the same structure\n *       is returned.\n *\n * With flags having the following meaning:\n *\n *     REDISMODULE_CLIENTINFO_FLAG_SSL          Client using SSL connection.\n *     REDISMODULE_CLIENTINFO_FLAG_PUBSUB       Client in Pub/Sub mode.\n *     REDISMODULE_CLIENTINFO_FLAG_BLOCKED      Client blocked in command.\n *     REDISMODULE_CLIENTINFO_FLAG_TRACKING     Client with keys tracking on.\n *     REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET   Client using unix domain socket.\n *     REDISMODULE_CLIENTINFO_FLAG_MULTI        Client in MULTI state.\n *\n * However passing NULL is a way to just check if the client exists in case\n * we are not interested in any additional information.\n *\n * This is the correct usage when we want the client info structure\n * returned:\n *\n *      RedisModuleClientInfo ci = REDISMODULE_CLIENTINFO_INITIALIZER;\n *      int retval = RedisModule_GetClientInfoById(&ci,client_id);\n *      if (retval == REDISMODULE_OK) {\n *          printf(\"Address: %s\\n\", ci.addr);\n *      }\n */\nint RM_GetClientInfoById(void *ci, uint64_t id) {\n    client *client = lookupClientByID(id);\n    if (client == NULL) return REDISMODULE_ERR;\n    if (ci == NULL) return REDISMODULE_OK;\n\n    /* Fill the info structure if passed. */\n    uint64_t structver = ((uint64_t*)ci)[0];\n    return modulePopulateClientInfoStructure(ci,client,structver);\n}\n\n/* Publish a message to subscribers (see PUBLISH command). */\nint RM_PublishMessage(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) {\n    UNUSED(ctx);\n    int receivers = pubsubPublishMessage(channel, message);\n    if (server.cluster_enabled)\n        clusterPropagatePublish(channel, message);\n    return receivers;\n}\n\n/* Return the currently selected DB. */\nint RM_GetSelectedDb(RedisModuleCtx *ctx) {\n    return ctx->client->db->id;\n}\n\n\n/* Return the current context's flags. The flags provide information on the\n * current request context (whether the client is a Lua script or in a MULTI),\n * and about the Redis instance in general, i.e replication and persistence.\n *\n * It is possible to call this function even with a NULL context, however\n * in this case the following flags will not be reported:\n *\n *  * LUA, MULTI, REPLICATED, DIRTY (see below for more info).\n *\n * Available flags and their meaning:\n *\n *  * REDISMODULE_CTX_FLAGS_LUA: The command is running in a Lua script\n *\n *  * REDISMODULE_CTX_FLAGS_MULTI: The command is running inside a transaction\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICATED: The command was sent over the replication\n *    link by the MASTER\n *\n *  * REDISMODULE_CTX_FLAGS_MASTER: The Redis instance is a master\n *\n *  * REDISMODULE_CTX_FLAGS_SLAVE: The Redis instance is a slave\n *\n *  * REDISMODULE_CTX_FLAGS_READONLY: The Redis instance is read-only\n *\n *  * REDISMODULE_CTX_FLAGS_CLUSTER: The Redis instance is in cluster mode\n *\n *  * REDISMODULE_CTX_FLAGS_AOF: The Redis instance has AOF enabled\n *\n *  * REDISMODULE_CTX_FLAGS_RDB: The instance has RDB enabled\n *\n *  * REDISMODULE_CTX_FLAGS_MAXMEMORY:  The instance has Maxmemory set\n *\n *  * REDISMODULE_CTX_FLAGS_EVICT:  Maxmemory is set and has an eviction\n *    policy that may delete keys\n *\n *  * REDISMODULE_CTX_FLAGS_OOM: Redis is out of memory according to the\n *    maxmemory setting.\n *\n *  * REDISMODULE_CTX_FLAGS_OOM_WARNING: Less than 25% of memory remains before\n *                                       reaching the maxmemory level.\n *\n *  * REDISMODULE_CTX_FLAGS_LOADING: Server is loading RDB/AOF\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE: No active link with the master.\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING: The replica is trying to\n *                                                 connect with the master.\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING: Master -> Replica RDB\n *                                                   transfer is in progress.\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE: The replica has an active link\n *                                             with its master. This is the\n *                                             contrary of STALE state.\n *\n *  * REDISMODULE_CTX_FLAGS_ACTIVE_CHILD: There is currently some background\n *                                        process active (RDB, AUX or module).\n */\nint RM_GetContextFlags(RedisModuleCtx *ctx) {\n\n    int flags = 0;\n    /* Client specific flags */\n    if (ctx) {\n        if (ctx->client) {\n            if (ctx->client->flags & CLIENT_LUA)\n             flags |= REDISMODULE_CTX_FLAGS_LUA;\n            if (ctx->client->flags & CLIENT_MULTI)\n             flags |= REDISMODULE_CTX_FLAGS_MULTI;\n            /* Module command recieved from MASTER, is replicated. */\n            if (ctx->client->flags & CLIENT_MASTER)\n             flags |= REDISMODULE_CTX_FLAGS_REPLICATED;\n        }\n\n        /* For DIRTY flags, we need the blocked client if used */\n        client *c = ctx->blocked_client ? ctx->blocked_client->client : ctx->client;\n        if (c && (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC))) {\n            flags |= REDISMODULE_CTX_FLAGS_MULTI_DIRTY;\n        }\n    }\n\n    if (server.cluster_enabled)\n        flags |= REDISMODULE_CTX_FLAGS_CLUSTER;\n\n    if (server.loading)\n        flags |= REDISMODULE_CTX_FLAGS_LOADING;\n\n    /* Maxmemory and eviction policy */\n    if (server.maxmemory > 0) {\n        flags |= REDISMODULE_CTX_FLAGS_MAXMEMORY;\n\n        if (server.maxmemory_policy != MAXMEMORY_NO_EVICTION)\n            flags |= REDISMODULE_CTX_FLAGS_EVICT;\n    }\n\n    /* Persistence flags */\n    if (server.aof_state != AOF_OFF)\n        flags |= REDISMODULE_CTX_FLAGS_AOF;\n    if (server.saveparamslen > 0)\n        flags |= REDISMODULE_CTX_FLAGS_RDB;\n\n    /* Replication flags */\n    if (server.masterhost == NULL) {\n        flags |= REDISMODULE_CTX_FLAGS_MASTER;\n    } else {\n        flags |= REDISMODULE_CTX_FLAGS_SLAVE;\n        if (server.repl_slave_ro)\n            flags |= REDISMODULE_CTX_FLAGS_READONLY;\n\n        /* Replica state flags. */\n        if (server.repl_state == REPL_STATE_CONNECT ||\n            server.repl_state == REPL_STATE_CONNECTING)\n        {\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING;\n        } else if (server.repl_state == REPL_STATE_TRANSFER) {\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING;\n        } else if (server.repl_state == REPL_STATE_CONNECTED) {\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE;\n        }\n\n        if (server.repl_state != REPL_STATE_CONNECTED)\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE;\n    }\n\n    /* OOM flag. */\n    float level;\n    int retval = getMaxmemoryState(NULL,NULL,NULL,&level);\n    if (retval == C_ERR) flags |= REDISMODULE_CTX_FLAGS_OOM;\n    if (level > 0.75) flags |= REDISMODULE_CTX_FLAGS_OOM_WARNING;\n\n    /* Presence of children processes. */\n    if (hasActiveChildProcess()) flags |= REDISMODULE_CTX_FLAGS_ACTIVE_CHILD;\n\n    return flags;\n}\n\n/* Returns true if some client sent the CLIENT PAUSE command to the server or\n * if Redis Cluster is doing a manual failover, and paused tue clients.\n * This is needed when we have a master with replicas, and want to write,\n * without adding further data to the replication channel, that the replicas\n * replication offset, match the one of the master. When this happens, it is\n * safe to failover the master without data loss.\n *\n * However modules may generate traffic by calling RedisModule_Call() with\n * the \"!\" flag, or by calling RedisModule_Replicate(), in a context outside\n * commands execution, for instance in timeout callbacks, threads safe\n * contexts, and so forth. When modules will generate too much traffic, it\n * will be hard for the master and replicas offset to match, because there\n * is more data to send in the replication channel.\n *\n * So modules may want to try to avoid very heavy background work that has\n * the effect of creating data to the replication channel, when this function\n * returns true. This is mostly useful for modules that have background\n * garbage collection tasks, or that do writes and replicate such writes\n * periodically in timer callbacks or other periodic callbacks.\n */\nint RM_AvoidReplicaTraffic() {\n    return clientsArePaused();\n}\n\n/* Change the currently selected DB. Returns an error if the id\n * is out of range.\n *\n * Note that the client will retain the currently selected DB even after\n * the Redis command implemented by the module calling this function\n * returns.\n *\n * If the module command wishes to change something in a different DB and\n * returns back to the original one, it should call RedisModule_GetSelectedDb()\n * before in order to restore the old DB number before returning. */\nint RM_SelectDb(RedisModuleCtx *ctx, int newid) {\n    int retval = selectDb(ctx->client,newid);\n    return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Initialize a RedisModuleKey struct */\nstatic void moduleInitKey(RedisModuleKey *kp, RedisModuleCtx *ctx, robj *keyname, robj *value, int mode){\n    kp->ctx = ctx;\n    kp->db = ctx->client->db;\n    kp->key = keyname;\n    incrRefCount(keyname);\n    kp->value = value;\n    kp->iter = NULL;\n    kp->mode = mode;\n    zsetKeyReset(kp);\n}\n\n/* Return an handle representing a Redis key, so that it is possible\n * to call other APIs with the key handle as argument to perform\n * operations on the key.\n *\n * The return value is the handle representing the key, that must be\n * closed with RM_CloseKey().\n *\n * If the key does not exist and WRITE mode is requested, the handle\n * is still returned, since it is possible to perform operations on\n * a yet not existing key (that will be created, for example, after\n * a list push operation). If the mode is just READ instead, and the\n * key does not exist, NULL is returned. However it is still safe to\n * call RedisModule_CloseKey() and RedisModule_KeyType() on a NULL\n * value. */\nvoid *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {\n    RedisModuleKey *kp;\n    robj *value;\n    int flags = mode & REDISMODULE_OPEN_KEY_NOTOUCH? LOOKUP_NOTOUCH: 0;\n\n    if (mode & REDISMODULE_WRITE) {\n        value = lookupKeyWriteWithFlags(ctx->client->db,keyname, flags);\n    } else {\n        value = lookupKeyReadWithFlags(ctx->client->db,keyname, flags);\n        if (value == NULL) {\n            return NULL;\n        }\n    }\n\n    /* Setup the key handle. */\n    kp = zmalloc(sizeof(*kp));\n    moduleInitKey(kp, ctx, keyname, value, mode);\n    autoMemoryAdd(ctx,REDISMODULE_AM_KEY,kp);\n    return (void*)kp;\n}\n\n/* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */\nstatic void moduleCloseKey(RedisModuleKey *key) {\n    int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);\n    if ((key->mode & REDISMODULE_WRITE) && signal)\n        signalModifiedKey(key->ctx->client,key->db,key->key);\n    /* TODO: if (key->iter) RM_KeyIteratorStop(kp); */\n    RM_ZsetRangeStop(key);\n    decrRefCount(key->key);\n}\n\n/* Close a key handle. */\nvoid RM_CloseKey(RedisModuleKey *key) {\n    if (key == NULL) return;\n    moduleCloseKey(key);\n    autoMemoryFreed(key->ctx,REDISMODULE_AM_KEY,key);\n    zfree(key);\n}\n\n/* Return the type of the key. If the key pointer is NULL then\n * REDISMODULE_KEYTYPE_EMPTY is returned. */\nint RM_KeyType(RedisModuleKey *key) {\n    if (key == NULL || key->value ==  NULL) return REDISMODULE_KEYTYPE_EMPTY;\n    /* We map between defines so that we are free to change the internal\n     * defines as desired. */\n    switch(key->value->type) {\n    case OBJ_STRING: return REDISMODULE_KEYTYPE_STRING;\n    case OBJ_LIST: return REDISMODULE_KEYTYPE_LIST;\n    case OBJ_SET: return REDISMODULE_KEYTYPE_SET;\n    case OBJ_ZSET: return REDISMODULE_KEYTYPE_ZSET;\n    case OBJ_HASH: return REDISMODULE_KEYTYPE_HASH;\n    case OBJ_MODULE: return REDISMODULE_KEYTYPE_MODULE;\n    case OBJ_STREAM: return REDISMODULE_KEYTYPE_STREAM;\n    default: return 0;\n    }\n}\n\n/* Return the length of the value associated with the key.\n * For strings this is the length of the string. For all the other types\n * is the number of elements (just counting keys for hashes).\n *\n * If the key pointer is NULL or the key is empty, zero is returned. */\nsize_t RM_ValueLength(RedisModuleKey *key) {\n    if (key == NULL || key->value == NULL) return 0;\n    switch(key->value->type) {\n    case OBJ_STRING: return stringObjectLen(key->value);\n    case OBJ_LIST: return listTypeLength(key->value);\n    case OBJ_SET: return setTypeSize(key->value);\n    case OBJ_ZSET: return zsetLength(key->value);\n    case OBJ_HASH: return hashTypeLength(key->value);\n    case OBJ_STREAM: return streamLength(key->value);\n    default: return 0;\n    }\n}\n\n/* If the key is open for writing, remove it, and setup the key to\n * accept new writes as an empty key (that will be created on demand).\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing REDISMODULE_ERR is returned. */\nint RM_DeleteKey(RedisModuleKey *key) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value) {\n        dbDelete(key->db,key->key);\n        key->value = NULL;\n    }\n    return REDISMODULE_OK;\n}\n\n/* If the key is open for writing, unlink it (that is delete it in a\n * non-blocking way, not reclaiming memory immediately) and setup the key to\n * accept new writes as an empty key (that will be created on demand).\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing REDISMODULE_ERR is returned. */\nint RM_UnlinkKey(RedisModuleKey *key) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value) {\n        dbAsyncDelete(key->db,key->key);\n        key->value = NULL;\n    }\n    return REDISMODULE_OK;\n}\n\n/* Return the key expire value, as milliseconds of remaining TTL.\n * If no TTL is associated with the key or if the key is empty,\n * REDISMODULE_NO_EXPIRE is returned. */\nmstime_t RM_GetExpire(RedisModuleKey *key) {\n    mstime_t expire = getExpire(key->db,key->key);\n    if (expire == -1 || key->value == NULL) return -1;\n    expire -= mstime();\n    return expire >= 0 ? expire : 0;\n}\n\n/* Set a new expire for the key. If the special expire\n * REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was\n * one (the same as the PERSIST command).\n *\n * Note that the expire must be provided as a positive integer representing\n * the number of milliseconds of TTL the key should have.\n *\n * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if\n * the key was not open for writing or is an empty key. */\nint RM_SetExpire(RedisModuleKey *key, mstime_t expire) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL)\n        return REDISMODULE_ERR;\n    if (expire != REDISMODULE_NO_EXPIRE) {\n        expire += mstime();\n        setExpire(key->ctx->client,key->db,key->key,expire);\n    } else {\n        removeExpire(key->db,key->key);\n    }\n    return REDISMODULE_OK;\n}\n\n/* Performs similar operation to FLUSHALL, and optionally start a new AOF file (if enabled)\n * If restart_aof is true, you must make sure the command that triggered this call is not\n * propagated to the AOF file.\n * When async is set to true, db contents will be freed by a background thread. */\nvoid RM_ResetDataset(int restart_aof, int async) {\n    if (restart_aof && server.aof_state != AOF_OFF) stopAppendOnly();\n    flushAllDataAndResetRDB(async? EMPTYDB_ASYNC: EMPTYDB_NO_FLAGS);\n    if (server.aof_enabled && restart_aof) restartAOFAfterSYNC();\n}\n\n/* Returns the number of keys in the current db. */\nunsigned long long RM_DbSize(RedisModuleCtx *ctx) {\n    return dictSize(ctx->client->db->dict);\n}\n\n/* Returns a name of a random key, or NULL if current db is empty. */\nRedisModuleString *RM_RandomKey(RedisModuleCtx *ctx) {\n    robj *key = dbRandomKey(ctx->client->db);\n    autoMemoryAdd(ctx,REDISMODULE_AM_STRING,key);\n    return key;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for String type\n * -------------------------------------------------------------------------- */\n\n/* If the key is open for writing, set the specified string 'str' as the\n * value of the key, deleting the old value if any.\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing or there is an active iterator, REDISMODULE_ERR is returned. */\nint RM_StringSet(RedisModuleKey *key, RedisModuleString *str) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;\n    RM_DeleteKey(key);\n    genericSetKey(key->ctx->client,key->db,key->key,str,0,0);\n    key->value = str;\n    return REDISMODULE_OK;\n}\n\n/* Prepare the key associated string value for DMA access, and returns\n * a pointer and size (by reference), that the user can use to read or\n * modify the string in-place accessing it directly via pointer.\n *\n * The 'mode' is composed by bitwise OR-ing the following flags:\n *\n *     REDISMODULE_READ -- Read access\n *     REDISMODULE_WRITE -- Write access\n *\n * If the DMA is not requested for writing, the pointer returned should\n * only be accessed in a read-only fashion.\n *\n * On error (wrong type) NULL is returned.\n *\n * DMA access rules:\n *\n * 1. No other key writing function should be called since the moment\n * the pointer is obtained, for all the time we want to use DMA access\n * to read or modify the string.\n *\n * 2. Each time RM_StringTruncate() is called, to continue with the DMA\n * access, RM_StringDMA() should be called again to re-obtain\n * a new pointer and length.\n *\n * 3. If the returned pointer is not NULL, but the length is zero, no\n * byte can be touched (the string is empty, or the key itself is empty)\n * so a RM_StringTruncate() call should be used if there is to enlarge\n * the string, and later call StringDMA() again to get the pointer.\n */\nchar *RM_StringDMA(RedisModuleKey *key, size_t *len, int mode) {\n    /* We need to return *some* pointer for empty keys, we just return\n     * a string literal pointer, that is the advantage to be mapped into\n     * a read only memory page, so the module will segfault if a write\n     * attempt is performed. */\n    char *emptystring = \"<dma-empty-string>\";\n    if (key->value == NULL) {\n        *len = 0;\n        return emptystring;\n    }\n\n    if (key->value->type != OBJ_STRING) return NULL;\n\n    /* For write access, and even for read access if the object is encoded,\n     * we unshare the string (that has the side effect of decoding it). */\n    if ((mode & REDISMODULE_WRITE) || key->value->encoding != OBJ_ENCODING_RAW)\n        key->value = dbUnshareStringValue(key->db, key->key, key->value);\n\n    *len = sdslen(key->value->ptr);\n    return key->value->ptr;\n}\n\n/* If the string is open for writing and is of string type, resize it, padding\n * with zero bytes if the new length is greater than the old one.\n *\n * After this call, RM_StringDMA() must be called again to continue\n * DMA access with the new pointer.\n *\n * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR on\n * error, that is, the key is not open for writing, is not a string\n * or resizing for more than 512 MB is requested.\n *\n * If the key is empty, a string key is created with the new string value\n * unless the new length value requested is zero. */\nint RM_StringTruncate(RedisModuleKey *key, size_t newlen) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_STRING) return REDISMODULE_ERR;\n    if (newlen > 512*1024*1024) return REDISMODULE_ERR;\n\n    /* Empty key and new len set to 0. Just return REDISMODULE_OK without\n     * doing anything. */\n    if (key->value == NULL && newlen == 0) return REDISMODULE_OK;\n\n    if (key->value == NULL) {\n        /* Empty key: create it with the new size. */\n        robj *o = createObject(OBJ_STRING,sdsnewlen(NULL, newlen));\n        genericSetKey(key->ctx->client,key->db,key->key,o,0,0);\n        key->value = o;\n        decrRefCount(o);\n    } else {\n        /* Unshare and resize. */\n        key->value = dbUnshareStringValue(key->db, key->key, key->value);\n        size_t curlen = sdslen(key->value->ptr);\n        if (newlen > curlen) {\n            key->value->ptr = sdsgrowzero(key->value->ptr,newlen);\n        } else if (newlen < curlen) {\n            sdsrange(key->value->ptr,0,newlen-1);\n            /* If the string is too wasteful, reallocate it. */\n            if (sdslen(key->value->ptr) < sdsavail(key->value->ptr))\n                key->value->ptr = sdsRemoveFreeSpace(key->value->ptr);\n        }\n    }\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for List type\n * -------------------------------------------------------------------------- */\n\n/* Push an element into a list, on head or tail depending on 'where' argument.\n * If the key pointer is about an empty key opened for writing, the key\n * is created. On error (key opened for read-only operations or of the wrong\n * type) REDISMODULE_ERR is returned, otherwise REDISMODULE_OK is returned. */\nint RM_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_LIST) return REDISMODULE_ERR;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_LIST);\n    listTypePush(key->value, ele,\n        (where == REDISMODULE_LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL);\n    return REDISMODULE_OK;\n}\n\n/* Pop an element from the list, and returns it as a module string object\n * that the user should be free with RM_FreeString() or by enabling\n * automatic memory. 'where' specifies if the element should be popped from\n * head or tail. The command returns NULL if:\n * 1) The list is empty.\n * 2) The key was not open for writing.\n * 3) The key is not a list. */\nRedisModuleString *RM_ListPop(RedisModuleKey *key, int where) {\n    if (!(key->mode & REDISMODULE_WRITE) ||\n        key->value == NULL ||\n        key->value->type != OBJ_LIST) return NULL;\n    robj *ele = listTypePop(key->value,\n        (where == REDISMODULE_LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL);\n    robj *decoded = getDecodedObject(ele);\n    decrRefCount(ele);\n    moduleDelKeyIfEmpty(key);\n    autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,decoded);\n    return decoded;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for Sorted Set type\n * -------------------------------------------------------------------------- */\n\n/* Conversion from/to public flags of the Modules API and our private flags,\n * so that we have everything decoupled. */\nint RM_ZsetAddFlagsToCoreFlags(int flags) {\n    int retflags = 0;\n    if (flags & REDISMODULE_ZADD_XX) retflags |= ZADD_XX;\n    if (flags & REDISMODULE_ZADD_NX) retflags |= ZADD_NX;\n    return retflags;\n}\n\n/* See previous function comment. */\nint RM_ZsetAddFlagsFromCoreFlags(int flags) {\n    int retflags = 0;\n    if (flags & ZADD_ADDED) retflags |= REDISMODULE_ZADD_ADDED;\n    if (flags & ZADD_UPDATED) retflags |= REDISMODULE_ZADD_UPDATED;\n    if (flags & ZADD_NOP) retflags |= REDISMODULE_ZADD_NOP;\n    return retflags;\n}\n\n/* Add a new element into a sorted set, with the specified 'score'.\n * If the element already exists, the score is updated.\n *\n * A new sorted set is created at value if the key is an empty open key\n * setup for writing.\n *\n * Additional flags can be passed to the function via a pointer, the flags\n * are both used to receive input and to communicate state when the function\n * returns. 'flagsptr' can be NULL if no special flags are used.\n *\n * The input flags are:\n *\n *     REDISMODULE_ZADD_XX: Element must already exist. Do nothing otherwise.\n *     REDISMODULE_ZADD_NX: Element must not exist. Do nothing otherwise.\n *\n * The output flags are:\n *\n *     REDISMODULE_ZADD_ADDED: The new element was added to the sorted set.\n *     REDISMODULE_ZADD_UPDATED: The score of the element was updated.\n *     REDISMODULE_ZADD_NOP: No operation was performed because XX or NX flags.\n *\n * On success the function returns REDISMODULE_OK. On the following errors\n * REDISMODULE_ERR is returned:\n *\n * * The key was not opened for writing.\n * * The key is of the wrong type.\n * * 'score' double value is not a number (NaN).\n */\nint RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) {\n    int flags = 0;\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET);\n    if (flagsptr) flags = RM_ZsetAddFlagsToCoreFlags(*flagsptr);\n    if (zsetAdd(key->value,score,ele->ptr,&flags,NULL) == 0) {\n        if (flagsptr) *flagsptr = 0;\n        return REDISMODULE_ERR;\n    }\n    if (flagsptr) *flagsptr = RM_ZsetAddFlagsFromCoreFlags(flags);\n    return REDISMODULE_OK;\n}\n\n/* This function works exactly like RM_ZsetAdd(), but instead of setting\n * a new score, the score of the existing element is incremented, or if the\n * element does not already exist, it is added assuming the old score was\n * zero.\n *\n * The input and output flags, and the return value, have the same exact\n * meaning, with the only difference that this function will return\n * REDISMODULE_ERR even when 'score' is a valid double number, but adding it\n * to the existing score results into a NaN (not a number) condition.\n *\n * This function has an additional field 'newscore', if not NULL is filled\n * with the new score of the element after the increment, if no error\n * is returned. */\nint RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) {\n    int flags = 0;\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET);\n    if (flagsptr) flags = RM_ZsetAddFlagsToCoreFlags(*flagsptr);\n    flags |= ZADD_INCR;\n    if (zsetAdd(key->value,score,ele->ptr,&flags,newscore) == 0) {\n        if (flagsptr) *flagsptr = 0;\n        return REDISMODULE_ERR;\n    }\n    /* zsetAdd() may signal back that the resulting score is not a number. */\n    if (flagsptr && (*flagsptr & ZADD_NAN)) {\n        *flagsptr = 0;\n        return REDISMODULE_ERR;\n    }\n    if (flagsptr) *flagsptr = RM_ZsetAddFlagsFromCoreFlags(flags);\n    return REDISMODULE_OK;\n}\n\n/* Remove the specified element from the sorted set.\n * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR\n * on one of the following conditions:\n *\n * * The key was not opened for writing.\n * * The key is of the wrong type.\n *\n * The return value does NOT indicate the fact the element was really\n * removed (since it existed) or not, just if the function was executed\n * with success.\n *\n * In order to know if the element was removed, the additional argument\n * 'deleted' must be passed, that populates the integer by reference\n * setting it to 1 or 0 depending on the outcome of the operation.\n * The 'deleted' argument can be NULL if the caller is not interested\n * to know if the element was really removed.\n *\n * Empty keys will be handled correctly by doing nothing. */\nint RM_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (key->value != NULL && zsetDel(key->value,ele->ptr)) {\n        if (deleted) *deleted = 1;\n    } else {\n        if (deleted) *deleted = 0;\n    }\n    return REDISMODULE_OK;\n}\n\n/* On success retrieve the double score associated at the sorted set element\n * 'ele' and returns REDISMODULE_OK. Otherwise REDISMODULE_ERR is returned\n * to signal one of the following conditions:\n *\n * * There is no such element 'ele' in the sorted set.\n * * The key is not a sorted set.\n * * The key is an open empty key.\n */\nint RM_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score) {\n    if (key->value == NULL) return REDISMODULE_ERR;\n    if (key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (zsetScore(key->value,ele->ptr,score) == C_ERR) return REDISMODULE_ERR;\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for Sorted Set iterator\n * -------------------------------------------------------------------------- */\n\nvoid zsetKeyReset(RedisModuleKey *key) {\n    key->ztype = REDISMODULE_ZSET_RANGE_NONE;\n    key->zcurrent = NULL;\n    key->zer = 1;\n}\n\n/* Stop a sorted set iteration. */\nvoid RM_ZsetRangeStop(RedisModuleKey *key) {\n    /* Free resources if needed. */\n    if (key->ztype == REDISMODULE_ZSET_RANGE_LEX)\n        zslFreeLexRange(&key->zlrs);\n    /* Setup sensible values so that misused iteration API calls when an\n     * iterator is not active will result into something more sensible\n     * than crashing. */\n    zsetKeyReset(key);\n}\n\n/* Return the \"End of range\" flag value to signal the end of the iteration. */\nint RM_ZsetRangeEndReached(RedisModuleKey *key) {\n    return key->zer;\n}\n\n/* Helper function for RM_ZsetFirstInScoreRange() and RM_ZsetLastInScoreRange().\n * Setup the sorted set iteration according to the specified score range\n * (see the functions calling it for more info). If 'first' is true the\n * first element in the range is used as a starting point for the iterator\n * otherwise the last. Return REDISMODULE_OK on success otherwise\n * REDISMODULE_ERR. */\nint zsetInitScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex, int first) {\n    if (!key->value || key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n\n    RM_ZsetRangeStop(key);\n    key->ztype = REDISMODULE_ZSET_RANGE_SCORE;\n    key->zer = 0;\n\n    /* Setup the range structure used by the sorted set core implementation\n     * in order to seek at the specified element. */\n    zrangespec *zrs = &key->zrs;\n    zrs->min = min;\n    zrs->max = max;\n    zrs->minex = minex;\n    zrs->maxex = maxex;\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        key->zcurrent = first ? zzlFirstInRange(key->value->ptr,zrs) :\n                                zzlLastInRange(key->value->ptr,zrs);\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = key->value->ptr;\n        zskiplist *zsl = zs->zsl;\n        key->zcurrent = first ? zslFirstInRange(zsl,zrs) :\n                                zslLastInRange(zsl,zrs);\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n    if (key->zcurrent == NULL) key->zer = 1;\n    return REDISMODULE_OK;\n}\n\n/* Setup a sorted set iterator seeking the first element in the specified\n * range. Returns REDISMODULE_OK if the iterator was correctly initialized\n * otherwise REDISMODULE_ERR is returned in the following conditions:\n *\n * 1. The value stored at key is not a sorted set or the key is empty.\n *\n * The range is specified according to the two double values 'min' and 'max'.\n * Both can be infinite using the following two macros:\n *\n * REDISMODULE_POSITIVE_INFINITE for positive infinite value\n * REDISMODULE_NEGATIVE_INFINITE for negative infinite value\n *\n * 'minex' and 'maxex' parameters, if true, respectively setup a range\n * where the min and max value are exclusive (not included) instead of\n * inclusive. */\nint RM_ZsetFirstInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) {\n    return zsetInitScoreRange(key,min,max,minex,maxex,1);\n}\n\n/* Exactly like RedisModule_ZsetFirstInScoreRange() but the last element of\n * the range is selected for the start of the iteration instead. */\nint RM_ZsetLastInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) {\n    return zsetInitScoreRange(key,min,max,minex,maxex,0);\n}\n\n/* Helper function for RM_ZsetFirstInLexRange() and RM_ZsetLastInLexRange().\n * Setup the sorted set iteration according to the specified lexicographical\n * range (see the functions calling it for more info). If 'first' is true the\n * first element in the range is used as a starting point for the iterator\n * otherwise the last. Return REDISMODULE_OK on success otherwise\n * REDISMODULE_ERR.\n *\n * Note that this function takes 'min' and 'max' in the same form of the\n * Redis ZRANGEBYLEX command. */\nint zsetInitLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max, int first) {\n    if (!key->value || key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n\n    RM_ZsetRangeStop(key);\n    key->zer = 0;\n\n    /* Setup the range structure used by the sorted set core implementation\n     * in order to seek at the specified element. */\n    zlexrangespec *zlrs = &key->zlrs;\n    if (zslParseLexRange(min, max, zlrs) == C_ERR) return REDISMODULE_ERR;\n\n    /* Set the range type to lex only after successfully parsing the range,\n     * otherwise we don't want the zlexrangespec to be freed. */\n    key->ztype = REDISMODULE_ZSET_RANGE_LEX;\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        key->zcurrent = first ? zzlFirstInLexRange(key->value->ptr,zlrs) :\n                                zzlLastInLexRange(key->value->ptr,zlrs);\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = key->value->ptr;\n        zskiplist *zsl = zs->zsl;\n        key->zcurrent = first ? zslFirstInLexRange(zsl,zlrs) :\n                                zslLastInLexRange(zsl,zlrs);\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n    if (key->zcurrent == NULL) key->zer = 1;\n\n    return REDISMODULE_OK;\n}\n\n/* Setup a sorted set iterator seeking the first element in the specified\n * lexicographical range. Returns REDISMODULE_OK if the iterator was correctly\n * initialized otherwise REDISMODULE_ERR is returned in the\n * following conditions:\n *\n * 1. The value stored at key is not a sorted set or the key is empty.\n * 2. The lexicographical range 'min' and 'max' format is invalid.\n *\n * 'min' and 'max' should be provided as two RedisModuleString objects\n * in the same format as the parameters passed to the ZRANGEBYLEX command.\n * The function does not take ownership of the objects, so they can be released\n * ASAP after the iterator is setup. */\nint RM_ZsetFirstInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) {\n    return zsetInitLexRange(key,min,max,1);\n}\n\n/* Exactly like RedisModule_ZsetFirstInLexRange() but the last element of\n * the range is selected for the start of the iteration instead. */\nint RM_ZsetLastInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) {\n    return zsetInitLexRange(key,min,max,0);\n}\n\n/* Return the current sorted set element of an active sorted set iterator\n * or NULL if the range specified in the iterator does not include any\n * element. */\nRedisModuleString *RM_ZsetRangeCurrentElement(RedisModuleKey *key, double *score) {\n    RedisModuleString *str;\n\n    if (key->zcurrent == NULL) return NULL;\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr, *sptr;\n        eptr = key->zcurrent;\n        sds ele = ziplistGetObject(eptr);\n        if (score) {\n            sptr = ziplistNext(key->value->ptr,eptr);\n            *score = zzlGetScore(sptr);\n        }\n        str = createObject(OBJ_STRING,ele);\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zskiplistNode *ln = key->zcurrent;\n        if (score) *score = ln->score;\n        str = createStringObject(ln->ele,sdslen(ln->ele));\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n    autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,str);\n    return str;\n}\n\n/* Go to the next element of the sorted set iterator. Returns 1 if there was\n * a next element, 0 if we are already at the latest element or the range\n * does not include any item at all. */\nint RM_ZsetRangeNext(RedisModuleKey *key) {\n    if (!key->ztype || !key->zcurrent) return 0; /* No active iterator. */\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = key->value->ptr;\n        unsigned char *eptr = key->zcurrent;\n        unsigned char *next;\n        next = ziplistNext(zl,eptr); /* Skip element. */\n        if (next) next = ziplistNext(zl,next); /* Skip score. */\n        if (next == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE) {\n                /* Fetch the next element score for the\n                 * range check. */\n                unsigned char *saved_next = next;\n                next = ziplistNext(zl,next); /* Skip next element. */\n                double score = zzlGetScore(next); /* Obtain the next score. */\n                if (!zslValueLteMax(score,&key->zrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n                next = saved_next;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zzlLexValueLteMax(next,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = next;\n            return 1;\n        }\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zskiplistNode *ln = key->zcurrent, *next = ln->level[0].forward;\n        if (next == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE &&\n                !zslValueLteMax(next->score,&key->zrs))\n            {\n                key->zer = 1;\n                return 0;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zslLexValueLteMax(next->ele,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = next;\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n}\n\n/* Go to the previous element of the sorted set iterator. Returns 1 if there was\n * a previous element, 0 if we are already at the first element or the range\n * does not include any item at all. */\nint RM_ZsetRangePrev(RedisModuleKey *key) {\n    if (!key->ztype || !key->zcurrent) return 0; /* No active iterator. */\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = key->value->ptr;\n        unsigned char *eptr = key->zcurrent;\n        unsigned char *prev;\n        prev = ziplistPrev(zl,eptr); /* Go back to previous score. */\n        if (prev) prev = ziplistPrev(zl,prev); /* Back to previous ele. */\n        if (prev == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE) {\n                /* Fetch the previous element score for the\n                 * range check. */\n                unsigned char *saved_prev = prev;\n                prev = ziplistNext(zl,prev); /* Skip element to get the score.*/\n                double score = zzlGetScore(prev); /* Obtain the prev score. */\n                if (!zslValueGteMin(score,&key->zrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n                prev = saved_prev;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zzlLexValueGteMin(prev,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = prev;\n            return 1;\n        }\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zskiplistNode *ln = key->zcurrent, *prev = ln->backward;\n        if (prev == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE &&\n                !zslValueGteMin(prev->score,&key->zrs))\n            {\n                key->zer = 1;\n                return 0;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zslLexValueGteMin(prev->ele,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = prev;\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Key API for Hash type\n * -------------------------------------------------------------------------- */\n\n/* Set the field of the specified hash field to the specified value.\n * If the key is an empty key open for writing, it is created with an empty\n * hash value, in order to set the specified field.\n *\n * The function is variadic and the user must specify pairs of field\n * names and values, both as RedisModuleString pointers (unless the\n * CFIELD option is set, see later). At the end of the field/value-ptr pairs, \n * NULL must be specified as last argument to signal the end of the arguments \n * in the variadic function.\n *\n * Example to set the hash argv[1] to the value argv[2]:\n *\n *      RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],argv[2],NULL);\n *\n * The function can also be used in order to delete fields (if they exist)\n * by setting them to the specified value of REDISMODULE_HASH_DELETE:\n *\n *      RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],\n *                          REDISMODULE_HASH_DELETE,NULL);\n *\n * The behavior of the command changes with the specified flags, that can be\n * set to REDISMODULE_HASH_NONE if no special behavior is needed.\n *\n *     REDISMODULE_HASH_NX: The operation is performed only if the field was not\n *                          already existing in the hash.\n *     REDISMODULE_HASH_XX: The operation is performed only if the field was\n *                          already existing, so that a new value could be\n *                          associated to an existing filed, but no new fields\n *                          are created.\n *     REDISMODULE_HASH_CFIELDS: The field names passed are null terminated C\n *                               strings instead of RedisModuleString objects.\n *\n * Unless NX is specified, the command overwrites the old field value with\n * the new one.\n *\n * When using REDISMODULE_HASH_CFIELDS, field names are reported using\n * normal C strings, so for example to delete the field \"foo\" the following\n * code can be used:\n *\n *      RedisModule_HashSet(key,REDISMODULE_HASH_CFIELDS,\"foo\",\n *                          REDISMODULE_HASH_DELETE,NULL);\n *\n * Return value:\n *\n * The number of fields updated (that may be less than the number of fields\n * specified because of the XX or NX options).\n *\n * In the following case the return value is always zero:\n *\n * * The key was not open for writing.\n * * The key was associated with a non Hash value.\n */\nint RM_HashSet(RedisModuleKey *key, int flags, ...) {\n    va_list ap;\n    if (!(key->mode & REDISMODULE_WRITE)) return 0;\n    if (key->value && key->value->type != OBJ_HASH) return 0;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_HASH);\n\n    int updated = 0;\n    va_start(ap, flags);\n    while(1) {\n        RedisModuleString *field, *value;\n        /* Get the field and value objects. */\n        if (flags & REDISMODULE_HASH_CFIELDS) {\n            char *cfield = va_arg(ap,char*);\n            if (cfield == NULL) break;\n            field = createRawStringObject(cfield,strlen(cfield));\n        } else {\n            field = va_arg(ap,RedisModuleString*);\n            if (field == NULL) break;\n        }\n        value = va_arg(ap,RedisModuleString*);\n\n        /* Handle XX and NX */\n        if (flags & (REDISMODULE_HASH_XX|REDISMODULE_HASH_NX)) {\n            int exists = hashTypeExists(key->value, field->ptr);\n            if (((flags & REDISMODULE_HASH_XX) && !exists) ||\n                ((flags & REDISMODULE_HASH_NX) && exists))\n            {\n                if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field);\n                continue;\n            }\n        }\n\n        /* Handle deletion if value is REDISMODULE_HASH_DELETE. */\n        if (value == REDISMODULE_HASH_DELETE) {\n            updated += hashTypeDelete(key->value, field->ptr);\n            if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field);\n            continue;\n        }\n\n        int low_flags = HASH_SET_COPY;\n        /* If CFIELDS is active, we can pass the ownership of the\n         * SDS object to the low level function that sets the field\n         * to avoid a useless copy. */\n        if (flags & REDISMODULE_HASH_CFIELDS)\n            low_flags |= HASH_SET_TAKE_FIELD;\n\n        robj *argv[2] = {field,value};\n        hashTypeTryConversion(key->value,argv,0,1);\n        updated += hashTypeSet(key->value, field->ptr, value->ptr, low_flags);\n\n        /* If CFIELDS is active, SDS string ownership is now of hashTypeSet(),\n         * however we still have to release the 'field' object shell. */\n        if (flags & REDISMODULE_HASH_CFIELDS) {\n           field->ptr = NULL; /* Prevent the SDS string from being freed. */\n           decrRefCount(field);\n        }\n    }\n    va_end(ap);\n    moduleDelKeyIfEmpty(key);\n    return updated;\n}\n\n/* Get fields from an hash value. This function is called using a variable\n * number of arguments, alternating a field name (as a StringRedisModule\n * pointer) with a pointer to a StringRedisModule pointer, that is set to the\n * value of the field if the field exist, or NULL if the field did not exist.\n * At the end of the field/value-ptr pairs, NULL must be specified as last\n * argument to signal the end of the arguments in the variadic function.\n *\n * This is an example usage:\n *\n *      RedisModuleString *first, *second;\n *      RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first,\n *                      argv[2],&second,NULL);\n *\n * As with RedisModule_HashSet() the behavior of the command can be specified\n * passing flags different than REDISMODULE_HASH_NONE:\n *\n * REDISMODULE_HASH_CFIELD: field names as null terminated C strings.\n *\n * REDISMODULE_HASH_EXISTS: instead of setting the value of the field\n * expecting a RedisModuleString pointer to pointer, the function just\n * reports if the field exists or not and expects an integer pointer\n * as the second element of each pair.\n *\n * Example of REDISMODULE_HASH_CFIELD:\n *\n *      RedisModuleString *username, *hashedpass;\n *      RedisModule_HashGet(mykey,\"username\",&username,\"hp\",&hashedpass, NULL);\n *\n * Example of REDISMODULE_HASH_EXISTS:\n *\n *      int exists;\n *      RedisModule_HashGet(mykey,argv[1],&exists,NULL);\n *\n * The function returns REDISMODULE_OK on success and REDISMODULE_ERR if\n * the key is not an hash value.\n *\n * Memory management:\n *\n * The returned RedisModuleString objects should be released with\n * RedisModule_FreeString(), or by enabling automatic memory management.\n */\nint RM_HashGet(RedisModuleKey *key, int flags, ...) {\n    va_list ap;\n    if (key->value && key->value->type != OBJ_HASH) return REDISMODULE_ERR;\n\n    va_start(ap, flags);\n    while(1) {\n        RedisModuleString *field, **valueptr;\n        int *existsptr;\n        /* Get the field object and the value pointer to pointer. */\n        if (flags & REDISMODULE_HASH_CFIELDS) {\n            char *cfield = va_arg(ap,char*);\n            if (cfield == NULL) break;\n            field = createRawStringObject(cfield,strlen(cfield));\n        } else {\n            field = va_arg(ap,RedisModuleString*);\n            if (field == NULL) break;\n        }\n\n        /* Query the hash for existence or value object. */\n        if (flags & REDISMODULE_HASH_EXISTS) {\n            existsptr = va_arg(ap,int*);\n            if (key->value)\n                *existsptr = hashTypeExists(key->value,field->ptr);\n            else\n                *existsptr = 0;\n        } else {\n            valueptr = va_arg(ap,RedisModuleString**);\n            if (key->value) {\n                *valueptr = hashTypeGetValueObject(key->value,field->ptr);\n                if (*valueptr) {\n                    robj *decoded = getDecodedObject(*valueptr);\n                    decrRefCount(*valueptr);\n                    *valueptr = decoded;\n                }\n                if (*valueptr)\n                    autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,*valueptr);\n            } else {\n                *valueptr = NULL;\n            }\n        }\n\n        /* Cleanup */\n        if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field);\n    }\n    va_end(ap);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Redis <-> Modules generic Call() API\n * -------------------------------------------------------------------------- */\n\n/* Create a new RedisModuleCallReply object. The processing of the reply\n * is lazy, the object is just populated with the raw protocol and later\n * is processed as needed. Initially we just make sure to set the right\n * reply type, which is extremely cheap to do. */\nRedisModuleCallReply *moduleCreateCallReplyFromProto(RedisModuleCtx *ctx, sds proto) {\n    RedisModuleCallReply *reply = zmalloc(sizeof(*reply));\n    reply->ctx = ctx;\n    reply->proto = proto;\n    reply->protolen = sdslen(proto);\n    reply->flags = REDISMODULE_REPLYFLAG_TOPARSE; /* Lazy parsing. */\n    switch(proto[0]) {\n    case '$':\n    case '+': reply->type = REDISMODULE_REPLY_STRING; break;\n    case '-': reply->type = REDISMODULE_REPLY_ERROR; break;\n    case ':': reply->type = REDISMODULE_REPLY_INTEGER; break;\n    case '*': reply->type = REDISMODULE_REPLY_ARRAY; break;\n    default: reply->type = REDISMODULE_REPLY_UNKNOWN; break;\n    }\n    if ((proto[0] == '*' || proto[0] == '$') && proto[1] == '-')\n        reply->type = REDISMODULE_REPLY_NULL;\n    return reply;\n}\n\nvoid moduleParseCallReply_Int(RedisModuleCallReply *reply);\nvoid moduleParseCallReply_BulkString(RedisModuleCallReply *reply);\nvoid moduleParseCallReply_SimpleString(RedisModuleCallReply *reply);\nvoid moduleParseCallReply_Array(RedisModuleCallReply *reply);\n\n/* Do nothing if REDISMODULE_REPLYFLAG_TOPARSE is false, otherwise\n * use the protcol of the reply in reply->proto in order to fill the\n * reply with parsed data according to the reply type. */\nvoid moduleParseCallReply(RedisModuleCallReply *reply) {\n    if (!(reply->flags & REDISMODULE_REPLYFLAG_TOPARSE)) return;\n    reply->flags &= ~REDISMODULE_REPLYFLAG_TOPARSE;\n\n    switch(reply->proto[0]) {\n    case ':': moduleParseCallReply_Int(reply); break;\n    case '$': moduleParseCallReply_BulkString(reply); break;\n    case '-': /* handled by next item. */\n    case '+': moduleParseCallReply_SimpleString(reply); break;\n    case '*': moduleParseCallReply_Array(reply); break;\n    }\n}\n\nvoid moduleParseCallReply_Int(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n\n    string2ll(proto+1,p-proto-1,&reply->val.ll);\n    reply->protolen = p-proto+2;\n    reply->type = REDISMODULE_REPLY_INTEGER;\n}\n\nvoid moduleParseCallReply_BulkString(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n    long long bulklen;\n\n    string2ll(proto+1,p-proto-1,&bulklen);\n    if (bulklen == -1) {\n        reply->protolen = p-proto+2;\n        reply->type = REDISMODULE_REPLY_NULL;\n    } else {\n        reply->val.str = p+2;\n        reply->len = bulklen;\n        reply->protolen = p-proto+2+bulklen+2;\n        reply->type = REDISMODULE_REPLY_STRING;\n    }\n}\n\nvoid moduleParseCallReply_SimpleString(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n\n    reply->val.str = proto+1;\n    reply->len = p-proto-1;\n    reply->protolen = p-proto+2;\n    reply->type = proto[0] == '+' ? REDISMODULE_REPLY_STRING :\n                                    REDISMODULE_REPLY_ERROR;\n}\n\nvoid moduleParseCallReply_Array(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n    long long arraylen, j;\n\n    string2ll(proto+1,p-proto-1,&arraylen);\n    p += 2;\n\n    if (arraylen == -1) {\n        reply->protolen = p-proto;\n        reply->type = REDISMODULE_REPLY_NULL;\n        return;\n    }\n\n    reply->val.array = zmalloc(sizeof(RedisModuleCallReply)*arraylen);\n    reply->len = arraylen;\n    for (j = 0; j < arraylen; j++) {\n        RedisModuleCallReply *ele = reply->val.array+j;\n        ele->flags = REDISMODULE_REPLYFLAG_NESTED |\n                     REDISMODULE_REPLYFLAG_TOPARSE;\n        ele->proto = p;\n        ele->ctx = reply->ctx;\n        moduleParseCallReply(ele);\n        p += ele->protolen;\n    }\n    reply->protolen = p-proto;\n    reply->type = REDISMODULE_REPLY_ARRAY;\n}\n\n/* Free a Call reply and all the nested replies it contains if it's an\n * array. */\nvoid RM_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested){\n    /* Don't free nested replies by default: the user must always free the\n     * toplevel reply. However be gentle and don't crash if the module\n     * misuses the API. */\n    if (!freenested && reply->flags & REDISMODULE_REPLYFLAG_NESTED) return;\n\n    if (!(reply->flags & REDISMODULE_REPLYFLAG_TOPARSE)) {\n        if (reply->type == REDISMODULE_REPLY_ARRAY) {\n            size_t j;\n            for (j = 0; j < reply->len; j++)\n                RM_FreeCallReply_Rec(reply->val.array+j,1);\n            zfree(reply->val.array);\n        }\n    }\n\n    /* For nested replies, we don't free reply->proto (which if not NULL\n     * references the parent reply->proto buffer), nor the structure\n     * itself which is allocated as an array of structures, and is freed\n     * when the array value is released. */\n    if (!(reply->flags & REDISMODULE_REPLYFLAG_NESTED)) {\n        if (reply->proto) sdsfree(reply->proto);\n        zfree(reply);\n    }\n}\n\n/* Wrapper for the recursive free reply function. This is needed in order\n * to have the first level function to return on nested replies, but only\n * if called by the module API. */\nvoid RM_FreeCallReply(RedisModuleCallReply *reply) {\n\n    RedisModuleCtx *ctx = reply->ctx;\n    RM_FreeCallReply_Rec(reply,0);\n    autoMemoryFreed(ctx,REDISMODULE_AM_REPLY,reply);\n}\n\n/* Return the reply type. */\nint RM_CallReplyType(RedisModuleCallReply *reply) {\n    if (!reply) return REDISMODULE_REPLY_UNKNOWN;\n    return reply->type;\n}\n\n/* Return the reply type length, where applicable. */\nsize_t RM_CallReplyLength(RedisModuleCallReply *reply) {\n    moduleParseCallReply(reply);\n    switch(reply->type) {\n    case REDISMODULE_REPLY_STRING:\n    case REDISMODULE_REPLY_ERROR:\n    case REDISMODULE_REPLY_ARRAY:\n        return reply->len;\n    default:\n        return 0;\n    }\n}\n\n/* Return the 'idx'-th nested call reply element of an array reply, or NULL\n * if the reply type is wrong or the index is out of range. */\nRedisModuleCallReply *RM_CallReplyArrayElement(RedisModuleCallReply *reply, size_t idx) {\n    moduleParseCallReply(reply);\n    if (reply->type != REDISMODULE_REPLY_ARRAY) return NULL;\n    if (idx >= reply->len) return NULL;\n    return reply->val.array+idx;\n}\n\n/* Return the long long of an integer reply. */\nlong long RM_CallReplyInteger(RedisModuleCallReply *reply) {\n    moduleParseCallReply(reply);\n    if (reply->type != REDISMODULE_REPLY_INTEGER) return LLONG_MIN;\n    return reply->val.ll;\n}\n\n/* Return the pointer and length of a string or error reply. */\nconst char *RM_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len) {\n    moduleParseCallReply(reply);\n    if (reply->type != REDISMODULE_REPLY_STRING &&\n        reply->type != REDISMODULE_REPLY_ERROR) return NULL;\n    if (len) *len = reply->len;\n    return reply->val.str;\n}\n\n/* Return a new string object from a call reply of type string, error or\n * integer. Otherwise (wrong reply type) return NULL. */\nRedisModuleString *RM_CreateStringFromCallReply(RedisModuleCallReply *reply) {\n    moduleParseCallReply(reply);\n    switch(reply->type) {\n    case REDISMODULE_REPLY_STRING:\n    case REDISMODULE_REPLY_ERROR:\n        return RM_CreateString(reply->ctx,reply->val.str,reply->len);\n    case REDISMODULE_REPLY_INTEGER: {\n        char buf[64];\n        int len = ll2string(buf,sizeof(buf),reply->val.ll);\n        return RM_CreateString(reply->ctx,buf,len);\n        }\n    default: return NULL;\n    }\n}\n\n/* Returns an array of robj pointers, and populates *argc with the number\n * of items, by parsing the format specifier \"fmt\" as described for\n * the RM_Call(), RM_Replicate() and other module APIs.\n *\n * The integer pointed by 'flags' is populated with flags according\n * to special modifiers in \"fmt\". For now only one exists:\n *\n *     \"!\" -> REDISMODULE_ARGV_REPLICATE\n *     \"A\" -> REDISMODULE_ARGV_NO_AOF\n *     \"R\" -> REDISMODULE_ARGV_NO_REPLICAS\n *\n * On error (format specifier error) NULL is returned and nothing is\n * allocated. On success the argument vector is returned. */\nrobj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap) {\n    int argc = 0, argv_size, j;\n    robj **argv = NULL;\n\n    /* As a first guess to avoid useless reallocations, size argv to\n     * hold one argument for each char specifier in 'fmt'. */\n    argv_size = strlen(fmt)+1; /* +1 because of the command name. */\n    argv = zrealloc(argv,sizeof(robj*)*argv_size);\n\n    /* Build the arguments vector based on the format specifier. */\n    argv[0] = createStringObject(cmdname,strlen(cmdname));\n    argc++;\n\n    /* Create the client and dispatch the command. */\n    const char *p = fmt;\n    while(*p) {\n        if (*p == 'c') {\n            char *cstr = va_arg(ap,char*);\n            argv[argc++] = createStringObject(cstr,strlen(cstr));\n        } else if (*p == 's') {\n            robj *obj = va_arg(ap,void*);\n            argv[argc++] = obj;\n            incrRefCount(obj);\n        } else if (*p == 'b') {\n            char *buf = va_arg(ap,char*);\n            size_t len = va_arg(ap,size_t);\n            argv[argc++] = createStringObject(buf,len);\n        } else if (*p == 'l') {\n            long long ll = va_arg(ap,long long);\n            argv[argc++] = createObject(OBJ_STRING,sdsfromlonglong(ll));\n        } else if (*p == 'v') {\n             /* A vector of strings */\n             robj **v = va_arg(ap, void*);\n             size_t vlen = va_arg(ap, size_t);\n\n             /* We need to grow argv to hold the vector's elements.\n              * We resize by vector_len-1 elements, because we held\n              * one element in argv for the vector already */\n             argv_size += vlen-1;\n             argv = zrealloc(argv,sizeof(robj*)*argv_size);\n\n             size_t i = 0;\n             for (i = 0; i < vlen; i++) {\n                 incrRefCount(v[i]);\n                 argv[argc++] = v[i];\n             }\n        } else if (*p == '!') {\n            if (flags) (*flags) |= REDISMODULE_ARGV_REPLICATE;\n        } else if (*p == 'A') {\n            if (flags) (*flags) |= REDISMODULE_ARGV_NO_AOF;\n        } else if (*p == 'R') {\n            if (flags) (*flags) |= REDISMODULE_ARGV_NO_REPLICAS;\n        } else {\n            goto fmterr;\n        }\n        p++;\n    }\n    *argcp = argc;\n    return argv;\n\nfmterr:\n    for (j = 0; j < argc; j++)\n        decrRefCount(argv[j]);\n    zfree(argv);\n    return NULL;\n}\n\n/* Exported API to call any Redis command from modules.\n * On success a RedisModuleCallReply object is returned, otherwise\n * NULL is returned and errno is set to the following values:\n *\n * EBADF: wrong format specifier.\n * EINVAL: wrong command arity.\n * ENOENT: command does not exist.\n * EPERM:  operation in Cluster instance with key in non local slot.\n * EROFS:  operation in Cluster instance when a write command is sent\n *         in a readonly state.\n * ENETDOWN: operation in Cluster instance when cluster is down.\n *\n * This API is documented here: https://redis.io/topics/modules-intro\n */\nRedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) {\n    struct redisCommand *cmd;\n    client *c = NULL;\n    robj **argv = NULL;\n    int argc = 0, flags = 0;\n    va_list ap;\n    RedisModuleCallReply *reply = NULL;\n    int replicate = 0; /* Replicate this command? */\n\n    /* Create the client and dispatch the command. */\n    va_start(ap, fmt);\n    c = createClient(NULL);\n    c->user = NULL; /* Root user. */\n    argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);\n    replicate = flags & REDISMODULE_ARGV_REPLICATE;\n    va_end(ap);\n\n    /* Setup our fake client for command execution. */\n    c->flags |= CLIENT_MODULE;\n    c->db = ctx->client->db;\n    c->argv = argv;\n    c->argc = argc;\n    if (ctx->module) ctx->module->in_call++;\n\n    /* We handle the above format error only when the client is setup so that\n     * we can free it normally. */\n    if (argv == NULL) {\n        errno = EBADF;\n        goto cleanup;\n    }\n\n    /* Call command filters */\n    moduleCallCommandFilters(c);\n\n    /* Lookup command now, after filters had a chance to make modifications\n     * if necessary.\n     */\n    cmd = lookupCommand(c->argv[0]->ptr);\n    if (!cmd) {\n        errno = ENOENT;\n        goto cleanup;\n    }\n    c->cmd = c->lastcmd = cmd;\n\n    /* Basic arity checks. */\n    if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) {\n        errno = EINVAL;\n        goto cleanup;\n    }\n\n    /* If this is a Redis Cluster node, we need to make sure the module is not\n     * trying to access non-local keys, with the exception of commands\n     * received from our master. */\n    if (server.cluster_enabled && !(ctx->client->flags & CLIENT_MASTER)) {\n        int error_code;\n        /* Duplicate relevant flags in the module client. */\n        c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);\n        c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);\n        if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,&error_code) !=\n                           server.cluster->myself)\n        {\n            if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) { \n                errno = EROFS;\n            } else if (error_code == CLUSTER_REDIR_DOWN_STATE) { \n                errno = ENETDOWN;\n            } else {\n                errno = EPERM;\n            }\n            goto cleanup;\n        }\n    }\n\n    /* If we are using single commands replication, we need to wrap what\n     * we propagate into a MULTI/EXEC block, so that it will be atomic like\n     * a Lua script in the context of AOF and slaves. */\n    if (replicate) moduleReplicateMultiIfNeeded(ctx);\n\n    /* Run the command */\n    int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_NOWRAP;\n    if (replicate) {\n        if (!(flags & REDISMODULE_ARGV_NO_AOF))\n            call_flags |= CMD_CALL_PROPAGATE_AOF;\n        if (!(flags & REDISMODULE_ARGV_NO_REPLICAS))\n            call_flags |= CMD_CALL_PROPAGATE_REPL;\n    }\n    call(c,call_flags);\n\n    /* Convert the result of the Redis command into a module reply. */\n    sds proto = sdsnewlen(c->buf,c->bufpos);\n    c->bufpos = 0;\n    while(listLength(c->reply)) {\n        clientReplyBlock *o = listNodeValue(listFirst(c->reply));\n\n        proto = sdscatlen(proto,o->buf,o->used);\n        listDelNode(c->reply,listFirst(c->reply));\n    }\n    reply = moduleCreateCallReplyFromProto(ctx,proto);\n    autoMemoryAdd(ctx,REDISMODULE_AM_REPLY,reply);\n\ncleanup:\n    if (ctx->module) ctx->module->in_call--;\n    freeClient(c);\n    return reply;\n}\n\n/* Return a pointer, and a length, to the protocol returned by the command\n * that returned the reply object. */\nconst char *RM_CallReplyProto(RedisModuleCallReply *reply, size_t *len) {\n    if (reply->proto) *len = sdslen(reply->proto);\n    return reply->proto;\n}\n\n/* --------------------------------------------------------------------------\n * Modules data types\n *\n * When String DMA or using existing data structures is not enough, it is\n * possible to create new data types from scratch and export them to\n * Redis. The module must provide a set of callbacks for handling the\n * new values exported (for example in order to provide RDB saving/loading,\n * AOF rewrite, and so forth). In this section we define this API.\n * -------------------------------------------------------------------------- */\n\n/* Turn a 9 chars name in the specified charset and a 10 bit encver into\n * a single 64 bit unsigned integer that represents this exact module name\n * and version. This final number is called a \"type ID\" and is used when\n * writing module exported values to RDB files, in order to re-associate the\n * value to the right module to load them during RDB loading.\n *\n * If the string is not of the right length or the charset is wrong, or\n * if encver is outside the unsigned 10 bit integer range, 0 is returned,\n * otherwise the function returns the right type ID.\n *\n * The resulting 64 bit integer is composed as follows:\n *\n *     (high order bits) 6|6|6|6|6|6|6|6|6|10 (low order bits)\n *\n * The first 6 bits value is the first character, name[0], while the last\n * 6 bits value, immediately before the 10 bits integer, is name[8].\n * The last 10 bits are the encoding version.\n *\n * Note that a name and encver combo of \"AAAAAAAAA\" and 0, will produce\n * zero as return value, that is the same we use to signal errors, thus\n * this combination is invalid, and also useless since type names should\n * try to be vary to avoid collisions. */\n\nconst char *ModuleTypeNameCharSet =\n             \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n             \"abcdefghijklmnopqrstuvwxyz\"\n             \"0123456789-_\";\n\nuint64_t moduleTypeEncodeId(const char *name, int encver) {\n    /* We use 64 symbols so that we can map each character into 6 bits\n     * of the final output. */\n    const char *cset = ModuleTypeNameCharSet;\n    if (strlen(name) != 9) return 0;\n    if (encver < 0 || encver > 1023) return 0;\n\n    uint64_t id = 0;\n    for (int j = 0; j < 9; j++) {\n        char *p = strchr(cset,name[j]);\n        if (!p) return 0;\n        unsigned long pos = p-cset;\n        id = (id << 6) | pos;\n    }\n    id = (id << 10) | encver;\n    return id;\n}\n\n/* Search, in the list of exported data types of all the modules registered,\n * a type with the same name as the one given. Returns the moduleType\n * structure pointer if such a module is found, or NULL otherwise. */\nmoduleType *moduleTypeLookupModuleByName(const char *name) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        listIter li;\n        listNode *ln;\n\n        listRewind(module->types,&li);\n        while((ln = listNext(&li))) {\n            moduleType *mt = ln->value;\n            if (memcmp(name,mt->name,sizeof(mt->name)) == 0) {\n                dictReleaseIterator(di);\n                return mt;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n    return NULL;\n}\n\n/* Lookup a module by ID, with caching. This function is used during RDB\n * loading. Modules exporting data types should never be able to unload, so\n * our cache does not need to expire. */\n#define MODULE_LOOKUP_CACHE_SIZE 3\n\nmoduleType *moduleTypeLookupModuleByID(uint64_t id) {\n    static struct {\n        uint64_t id;\n        moduleType *mt;\n    } cache[MODULE_LOOKUP_CACHE_SIZE];\n\n    /* Search in cache to start. */\n    int j;\n    for (j = 0; j < MODULE_LOOKUP_CACHE_SIZE && cache[j].mt != NULL; j++)\n        if (cache[j].id == id) return cache[j].mt;\n\n    /* Slow module by module lookup. */\n    moduleType *mt = NULL;\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL && mt == NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        listIter li;\n        listNode *ln;\n\n        listRewind(module->types,&li);\n        while((ln = listNext(&li))) {\n            moduleType *this_mt = ln->value;\n            /* Compare only the 54 bit module identifier and not the\n             * encoding version. */\n            if (this_mt->id >> 10 == id >> 10) {\n                mt = this_mt;\n                break;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Add to cache if possible. */\n    if (mt && j < MODULE_LOOKUP_CACHE_SIZE) {\n        cache[j].id = id;\n        cache[j].mt = mt;\n    }\n    return mt;\n}\n\n/* Turn an (unresolved) module ID into a type name, to show the user an\n * error when RDB files contain module data we can't load.\n * The buffer pointed by 'name' must be 10 bytes at least. The function will\n * fill it with a null terminated module name. */\nvoid moduleTypeNameByID(char *name, uint64_t moduleid) {\n    const char *cset = ModuleTypeNameCharSet;\n\n    name[9] = '\\0';\n    char *p = name+8;\n    moduleid >>= 10;\n    for (int j = 0; j < 9; j++) {\n        *p-- = cset[moduleid & 63];\n        moduleid >>= 6;\n    }\n}\n\n/* Register a new data type exported by the module. The parameters are the\n * following. Please for in depth documentation check the modules API\n * documentation, especially the TYPES.md file.\n *\n * * **name**: A 9 characters data type name that MUST be unique in the Redis\n *   Modules ecosystem. Be creative... and there will be no collisions. Use\n *   the charset A-Z a-z 9-0, plus the two \"-_\" characters. A good\n *   idea is to use, for example `<typename>-<vendor>`. For example\n *   \"tree-AntZ\" may mean \"Tree data structure by @antirez\". To use both\n *   lower case and upper case letters helps in order to prevent collisions.\n * * **encver**: Encoding version, which is, the version of the serialization\n *   that a module used in order to persist data. As long as the \"name\"\n *   matches, the RDB loading will be dispatched to the type callbacks\n *   whatever 'encver' is used, however the module can understand if\n *   the encoding it must load are of an older version of the module.\n *   For example the module \"tree-AntZ\" initially used encver=0. Later\n *   after an upgrade, it started to serialize data in a different format\n *   and to register the type with encver=1. However this module may\n *   still load old data produced by an older version if the rdb_load\n *   callback is able to check the encver value and act accordingly.\n *   The encver must be a positive value between 0 and 1023.\n * * **typemethods_ptr** is a pointer to a RedisModuleTypeMethods structure\n *   that should be populated with the methods callbacks and structure\n *   version, like in the following example:\n *\n *      RedisModuleTypeMethods tm = {\n *          .version = REDISMODULE_TYPE_METHOD_VERSION,\n *          .rdb_load = myType_RDBLoadCallBack,\n *          .rdb_save = myType_RDBSaveCallBack,\n *          .aof_rewrite = myType_AOFRewriteCallBack,\n *          .free = myType_FreeCallBack,\n *\n *          // Optional fields\n *          .digest = myType_DigestCallBack,\n *          .mem_usage = myType_MemUsageCallBack,\n *          .aux_load = myType_AuxRDBLoadCallBack,\n *          .aux_save = myType_AuxRDBSaveCallBack,\n *      }\n *\n * * **rdb_load**: A callback function pointer that loads data from RDB files.\n * * **rdb_save**: A callback function pointer that saves data to RDB files.\n * * **aof_rewrite**: A callback function pointer that rewrites data as commands.\n * * **digest**: A callback function pointer that is used for `DEBUG DIGEST`.\n * * **free**: A callback function pointer that can free a type value.\n * * **aux_save**: A callback function pointer that saves out of keyspace data to RDB files.\n *   'when' argument is either REDISMODULE_AUX_BEFORE_RDB or REDISMODULE_AUX_AFTER_RDB.\n * * **aux_load**: A callback function pointer that loads out of keyspace data from RDB files.\n *   Similar to aux_save, returns REDISMODULE_OK on success, and ERR otherwise.\n *\n * The **digest* and **mem_usage** methods should currently be omitted since\n * they are not yet implemented inside the Redis modules core.\n *\n * Note: the module name \"AAAAAAAAA\" is reserved and produces an error, it\n * happens to be pretty lame as well.\n *\n * If there is already a module registering a type with the same name,\n * and if the module name or encver is invalid, NULL is returned.\n * Otherwise the new type is registered into Redis, and a reference of\n * type RedisModuleType is returned: the caller of the function should store\n * this reference into a gobal variable to make future use of it in the\n * modules type API, since a single module may register multiple types.\n * Example code fragment:\n *\n *      static RedisModuleType *BalancedTreeType;\n *\n *      int RedisModule_OnLoad(RedisModuleCtx *ctx) {\n *          // some code here ...\n *          BalancedTreeType = RM_CreateDataType(...);\n *      }\n */\nmoduleType *RM_CreateDataType(RedisModuleCtx *ctx, const char *name, int encver, void *typemethods_ptr) {\n    uint64_t id = moduleTypeEncodeId(name,encver);\n    if (id == 0) return NULL;\n    if (moduleTypeLookupModuleByName(name) != NULL) return NULL;\n\n    long typemethods_version = ((long*)typemethods_ptr)[0];\n    if (typemethods_version == 0) return NULL;\n\n    struct typemethods {\n        uint64_t version;\n        moduleTypeLoadFunc rdb_load;\n        moduleTypeSaveFunc rdb_save;\n        moduleTypeRewriteFunc aof_rewrite;\n        moduleTypeMemUsageFunc mem_usage;\n        moduleTypeDigestFunc digest;\n        moduleTypeFreeFunc free;\n        struct {\n            moduleTypeAuxLoadFunc aux_load;\n            moduleTypeAuxSaveFunc aux_save;\n            int aux_save_triggers;\n        } v2;\n    } *tms = (struct typemethods*) typemethods_ptr;\n\n    moduleType *mt = zcalloc(sizeof(*mt));\n    mt->id = id;\n    mt->module = ctx->module;\n    mt->rdb_load = tms->rdb_load;\n    mt->rdb_save = tms->rdb_save;\n    mt->aof_rewrite = tms->aof_rewrite;\n    mt->mem_usage = tms->mem_usage;\n    mt->digest = tms->digest;\n    mt->free = tms->free;\n    if (tms->version >= 2) {\n        mt->aux_load = tms->v2.aux_load;\n        mt->aux_save = tms->v2.aux_save;\n        mt->aux_save_triggers = tms->v2.aux_save_triggers;\n    }\n    memcpy(mt->name,name,sizeof(mt->name));\n    listAddNodeTail(ctx->module->types,mt);\n    return mt;\n}\n\n/* If the key is open for writing, set the specified module type object\n * as the value of the key, deleting the old value if any.\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing or there is an active iterator, REDISMODULE_ERR is returned. */\nint RM_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;\n    RM_DeleteKey(key);\n    robj *o = createModuleObject(mt,value);\n    genericSetKey(key->ctx->client,key->db,key->key,o,0,0);\n    decrRefCount(o);\n    key->value = o;\n    return REDISMODULE_OK;\n}\n\n/* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on\n * the key, returns the module type pointer of the value stored at key.\n *\n * If the key is NULL, is not associated with a module type, or is empty,\n * then NULL is returned instead. */\nmoduleType *RM_ModuleTypeGetType(RedisModuleKey *key) {\n    if (key == NULL ||\n        key->value == NULL ||\n        RM_KeyType(key) != REDISMODULE_KEYTYPE_MODULE) return NULL;\n    moduleValue *mv = key->value->ptr;\n    return mv->type;\n}\n\n/* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on\n * the key, returns the module type low-level value stored at key, as\n * it was set by the user via RedisModule_ModuleTypeSet().\n *\n * If the key is NULL, is not associated with a module type, or is empty,\n * then NULL is returned instead. */\nvoid *RM_ModuleTypeGetValue(RedisModuleKey *key) {\n    if (key == NULL ||\n        key->value == NULL ||\n        RM_KeyType(key) != REDISMODULE_KEYTYPE_MODULE) return NULL;\n    moduleValue *mv = key->value->ptr;\n    return mv->value;\n}\n\n/* --------------------------------------------------------------------------\n * RDB loading and saving functions\n * -------------------------------------------------------------------------- */\n\n/* Called when there is a load error in the context of a module. On some\n * modules this cannot be recovered, but if the module declared capability\n * to handle errors, we'll raise a flag rather than exiting. */\nvoid moduleRDBLoadError(RedisModuleIO *io) {\n    if (io->type->module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS) {\n        io->error = 1;\n        return;\n    }\n    serverPanic(\n        \"Error loading data from RDB (short read or EOF). \"\n        \"Read performed by module '%s' about type '%s' \"\n        \"after reading '%llu' bytes of a value \"\n        \"for key named: '%s'.\",\n        io->type->module->name,\n        io->type->name,\n        (unsigned long long)io->bytes,\n        io->key? (char*)io->key->ptr: \"(null)\");\n}\n\n/* Returns 0 if there's at least one registered data type that did not declare\n * REDISMODULE_OPTIONS_HANDLE_IO_ERRORS, in which case diskless loading should\n * be avoided since it could cause data loss. */\nint moduleAllDatatypesHandleErrors() {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        if (listLength(module->types) &&\n            !(module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS))\n        {\n            dictReleaseIterator(di);\n            return 0;\n        }\n    }\n    dictReleaseIterator(di);\n    return 1;\n}\n\n/* Returns true if any previous IO API failed.\n * for Load* APIs the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag must be set with\n * RediModule_SetModuleOptions first. */\nint RM_IsIOError(RedisModuleIO *io) {\n    return io->error;\n}\n\n/* Save an unsigned 64 bit value into the RDB file. This function should only\n * be called in the context of the rdb_save method of modules implementing new\n * data types. */\nvoid RM_SaveUnsigned(RedisModuleIO *io, uint64_t value) {\n    if (io->error) return;\n    /* Save opcode. */\n    int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_UINT);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveLen(io->rio, value);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* Load an unsigned 64 bit value from the RDB file. This function should only\n * be called in the context of the rdb_load method of modules implementing\n * new data types. */\nuint64_t RM_LoadUnsigned(RedisModuleIO *io) {\n    if (io->error) return 0;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_UINT) goto loaderr;\n    }\n    uint64_t value;\n    int retval = rdbLoadLenByRef(io->rio, NULL, &value);\n    if (retval == -1) goto loaderr;\n    return value;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return 0;\n}\n\n/* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */\nvoid RM_SaveSigned(RedisModuleIO *io, int64_t value) {\n    union {uint64_t u; int64_t i;} conv;\n    conv.i = value;\n    RM_SaveUnsigned(io,conv.u);\n}\n\n/* Like RedisModule_LoadUnsigned() but for signed 64 bit values. */\nint64_t RM_LoadSigned(RedisModuleIO *io) {\n    union {uint64_t u; int64_t i;} conv;\n    conv.u = RM_LoadUnsigned(io);\n    return conv.i;\n}\n\n/* In the context of the rdb_save method of a module type, saves a\n * string into the RDB file taking as input a RedisModuleString.\n *\n * The string can be later loaded with RedisModule_LoadString() or\n * other Load family functions expecting a serialized string inside\n * the RDB file. */\nvoid RM_SaveString(RedisModuleIO *io, RedisModuleString *s) {\n    if (io->error) return;\n    /* Save opcode. */\n    ssize_t retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_STRING);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveStringObject(io->rio, s);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* Like RedisModule_SaveString() but takes a raw C pointer and length\n * as input. */\nvoid RM_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len) {\n    if (io->error) return;\n    /* Save opcode. */\n    ssize_t retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_STRING);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveRawString(io->rio, (unsigned char*)str,len);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* Implements RM_LoadString() and RM_LoadStringBuffer() */\nvoid *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) {\n    if (io->error) return NULL;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_STRING) goto loaderr;\n    }\n    void *s = rdbGenericLoadStringObject(io->rio,\n              plain ? RDB_LOAD_PLAIN : RDB_LOAD_NONE, lenptr);\n    if (s == NULL) goto loaderr;\n    return s;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return NULL;\n}\n\n/* In the context of the rdb_load method of a module data type, loads a string\n * from the RDB file, that was previously saved with RedisModule_SaveString()\n * functions family.\n *\n * The returned string is a newly allocated RedisModuleString object, and\n * the user should at some point free it with a call to RedisModule_FreeString().\n *\n * If the data structure does not store strings as RedisModuleString objects,\n * the similar function RedisModule_LoadStringBuffer() could be used instead. */\nRedisModuleString *RM_LoadString(RedisModuleIO *io) {\n    return moduleLoadString(io,0,NULL);\n}\n\n/* Like RedisModule_LoadString() but returns an heap allocated string that\n * was allocated with RedisModule_Alloc(), and can be resized or freed with\n * RedisModule_Realloc() or RedisModule_Free().\n *\n * The size of the string is stored at '*lenptr' if not NULL.\n * The returned string is not automatically NULL terminated, it is loaded\n * exactly as it was stored inisde the RDB file. */\nchar *RM_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr) {\n    return moduleLoadString(io,1,lenptr);\n}\n\n/* In the context of the rdb_save method of a module data type, saves a double\n * value to the RDB file. The double can be a valid number, a NaN or infinity.\n * It is possible to load back the value with RedisModule_LoadDouble(). */\nvoid RM_SaveDouble(RedisModuleIO *io, double value) {\n    if (io->error) return;\n    /* Save opcode. */\n    int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_DOUBLE);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveBinaryDoubleValue(io->rio, value);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* In the context of the rdb_save method of a module data type, loads back the\n * double value saved by RedisModule_SaveDouble(). */\ndouble RM_LoadDouble(RedisModuleIO *io) {\n    if (io->error) return 0;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_DOUBLE) goto loaderr;\n    }\n    double value;\n    int retval = rdbLoadBinaryDoubleValue(io->rio, &value);\n    if (retval == -1) goto loaderr;\n    return value;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return 0;\n}\n\n/* In the context of the rdb_save method of a module data type, saves a float\n * value to the RDB file. The float can be a valid number, a NaN or infinity.\n * It is possible to load back the value with RedisModule_LoadFloat(). */\nvoid RM_SaveFloat(RedisModuleIO *io, float value) {\n    if (io->error) return;\n    /* Save opcode. */\n    int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_FLOAT);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveBinaryFloatValue(io->rio, value);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* In the context of the rdb_save method of a module data type, loads back the\n * float value saved by RedisModule_SaveFloat(). */\nfloat RM_LoadFloat(RedisModuleIO *io) {\n    if (io->error) return 0;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_FLOAT) goto loaderr;\n    }\n    float value;\n    int retval = rdbLoadBinaryFloatValue(io->rio, &value);\n    if (retval == -1) goto loaderr;\n    return value;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return 0;\n}\n\n/* In the context of the rdb_save method of a module data type, saves a long double\n * value to the RDB file. The double can be a valid number, a NaN or infinity.\n * It is possible to load back the value with RedisModule_LoadLongDouble(). */\nvoid RM_SaveLongDouble(RedisModuleIO *io, long double value) {\n    if (io->error) return;\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    /* Long double has different number of bits in different platforms, so we\n     * save it as a string type. */\n    size_t len = ld2string(buf,sizeof(buf),value,LD_STR_HEX);\n    RM_SaveStringBuffer(io,buf,len);\n}\n\n/* In the context of the rdb_save method of a module data type, loads back the\n * long double value saved by RedisModule_SaveLongDouble(). */\nlong double RM_LoadLongDouble(RedisModuleIO *io) {\n    if (io->error) return 0;\n    long double value;\n    size_t len;\n    char* str = RM_LoadStringBuffer(io,&len);\n    if (!str) return 0;\n    string2ld(str,len,&value);\n    RM_Free(str);\n    return value;\n}\n\n/* Iterate over modules, and trigger rdb aux saving for the ones modules types\n * who asked for it. */\nssize_t rdbSaveModulesAux(rio *rdb, int when) {\n    size_t total_written = 0;\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        listIter li;\n        listNode *ln;\n\n        listRewind(module->types,&li);\n        while((ln = listNext(&li))) {\n            moduleType *mt = ln->value;\n            if (!mt->aux_save || !(mt->aux_save_triggers & when))\n                continue;\n            ssize_t ret = rdbSaveSingleModuleAux(rdb, when, mt);\n            if (ret==-1) {\n                dictReleaseIterator(di);\n                return -1;\n            }\n            total_written += ret;\n        }\n    }\n\n    dictReleaseIterator(di);\n    return total_written;\n}\n\n/* --------------------------------------------------------------------------\n * Key digest API (DEBUG DIGEST interface for modules types)\n * -------------------------------------------------------------------------- */\n\n/* Add a new element to the digest. This function can be called multiple times\n * one element after the other, for all the elements that constitute a given\n * data structure. The function call must be followed by the call to\n * `RedisModule_DigestEndSequence` eventually, when all the elements that are\n * always in a given order are added. See the Redis Modules data types\n * documentation for more info. However this is a quick example that uses Redis\n * data types as an example.\n *\n * To add a sequence of unordered elements (for example in the case of a Redis\n * Set), the pattern to use is:\n *\n *     foreach element {\n *         AddElement(element);\n *         EndSequence();\n *     }\n *\n * Because Sets are not ordered, so every element added has a position that\n * does not depend from the other. However if instead our elements are\n * ordered in pairs, like field-value pairs of an Hash, then one should\n * use:\n *\n *     foreach key,value {\n *         AddElement(key);\n *         AddElement(value);\n *         EndSquence();\n *     }\n *\n * Because the key and value will be always in the above order, while instead\n * the single key-value pairs, can appear in any position into a Redis hash.\n *\n * A list of ordered elements would be implemented with:\n *\n *     foreach element {\n *         AddElement(element);\n *     }\n *     EndSequence();\n *\n */\nvoid RM_DigestAddStringBuffer(RedisModuleDigest *md, unsigned char *ele, size_t len) {\n    mixDigest(md->o,ele,len);\n}\n\n/* Like `RedisModule_DigestAddStringBuffer()` but takes a long long as input\n * that gets converted into a string before adding it to the digest. */\nvoid RM_DigestAddLongLong(RedisModuleDigest *md, long long ll) {\n    char buf[LONG_STR_SIZE];\n    size_t len = ll2string(buf,sizeof(buf),ll);\n    mixDigest(md->o,buf,len);\n}\n\n/* See the documentation for `RedisModule_DigestAddElement()`. */\nvoid RM_DigestEndSequence(RedisModuleDigest *md) {\n    xorDigest(md->x,md->o,sizeof(md->o));\n    memset(md->o,0,sizeof(md->o));\n}\n\n/* Decode a serialized representation of a module data type 'mt' from string\n * 'str' and return a newly allocated value, or NULL if decoding failed.\n *\n * This call basically reuses the 'rdb_load' callback which module data types\n * implement in order to allow a module to arbitrarily serialize/de-serialize\n * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.\n *\n * Modules should generally use the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag and\n * make sure the de-serialization code properly checks and handles IO errors\n * (freeing allocated buffers and returning a NULL).\n *\n * If this is NOT done, Redis will handle corrupted (or just truncated) serialized\n * data by producing an error message and terminating the process.\n */\n\nvoid *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType *mt) {\n    rio payload;\n    RedisModuleIO io;\n    void *ret;\n\n    rioInitWithBuffer(&payload, str->ptr);\n    moduleInitIOContext(io,(moduleType *)mt,&payload,NULL);\n\n    /* All RM_Save*() calls always write a version 2 compatible format, so we\n     * need to make sure we read the same.\n     */\n    io.ver = 2;\n    ret = mt->rdb_load(&io,0);\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    return ret;\n}\n\n/* Encode a module data type 'mt' value 'data' into serialized form, and return it\n * as a newly allocated RedisModuleString.\n *\n * This call basically reuses the 'rdb_save' callback which module data types\n * implement in order to allow a module to arbitrarily serialize/de-serialize\n * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.\n */\n\nRedisModuleString *RM_SaveDataTypeToString(RedisModuleCtx *ctx, void *data, const moduleType *mt) {\n    rio payload;\n    RedisModuleIO io;\n\n    rioInitWithBuffer(&payload,sdsempty());\n    moduleInitIOContext(io,(moduleType *)mt,&payload,NULL);\n    mt->rdb_save(&io,data);\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    if (io.error) {\n        return NULL;\n    } else {\n        robj *str = createObject(OBJ_STRING,payload.io.buffer.ptr);\n        if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,str);\n        return str;\n    }\n}\n\n/* --------------------------------------------------------------------------\n * AOF API for modules data types\n * -------------------------------------------------------------------------- */\n\n/* Emits a command into the AOF during the AOF rewriting process. This function\n * is only called in the context of the aof_rewrite method of data types exported\n * by a module. The command works exactly like RedisModule_Call() in the way\n * the parameters are passed, but it does not return anything as the error\n * handling is performed by Redis itself. */\nvoid RM_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) {\n    if (io->error) return;\n    struct redisCommand *cmd;\n    robj **argv = NULL;\n    int argc = 0, flags = 0, j;\n    va_list ap;\n\n    cmd = lookupCommandByCString((char*)cmdname);\n    if (!cmd) {\n        serverLog(LL_WARNING,\n            \"Fatal: AOF method for module data type '%s' tried to \"\n            \"emit unknown command '%s'\",\n            io->type->name, cmdname);\n        io->error = 1;\n        errno = EINVAL;\n        return;\n    }\n\n    /* Emit the arguments into the AOF in Redis protocol format. */\n    va_start(ap, fmt);\n    argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);\n    va_end(ap);\n    if (argv == NULL) {\n        serverLog(LL_WARNING,\n            \"Fatal: AOF method for module data type '%s' tried to \"\n            \"call RedisModule_EmitAOF() with wrong format specifiers '%s'\",\n            io->type->name, fmt);\n        io->error = 1;\n        errno = EINVAL;\n        return;\n    }\n\n    /* Bulk count. */\n    if (!io->error && rioWriteBulkCount(io->rio,'*',argc) == 0)\n        io->error = 1;\n\n    /* Arguments. */\n    for (j = 0; j < argc; j++) {\n        if (!io->error && rioWriteBulkObject(io->rio,argv[j]) == 0)\n            io->error = 1;\n        decrRefCount(argv[j]);\n    }\n    zfree(argv);\n    return;\n}\n\n/* --------------------------------------------------------------------------\n * IO context handling\n * -------------------------------------------------------------------------- */\n\nRedisModuleCtx *RM_GetContextFromIO(RedisModuleIO *io) {\n    if (io->ctx) return io->ctx; /* Can't have more than one... */\n    RedisModuleCtx ctxtemplate = REDISMODULE_CTX_INIT;\n    io->ctx = zmalloc(sizeof(RedisModuleCtx));\n    *(io->ctx) = ctxtemplate;\n    io->ctx->module = io->type->module;\n    io->ctx->client = NULL;\n    return io->ctx;\n}\n\n/* Returns a RedisModuleString with the name of the key currently saving or\n * loading, when an IO data type callback is called.  There is no guarantee\n * that the key name is always available, so this may return NULL.\n */\nconst RedisModuleString *RM_GetKeyNameFromIO(RedisModuleIO *io) {\n    return io->key;\n}\n\n/* Returns a RedisModuleString with the name of the key from RedisModuleKey */\nconst RedisModuleString *RM_GetKeyNameFromModuleKey(RedisModuleKey *key) {\n    return key ? key->key : NULL;\n}\n\n/* --------------------------------------------------------------------------\n * Logging\n * -------------------------------------------------------------------------- */\n\n/* This is the low level function implementing both:\n *\n *      RM_Log()\n *      RM_LogIOError()\n *\n */\nvoid RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_list ap) {\n    char msg[LOG_MAX_LEN];\n    size_t name_len;\n    int level;\n\n    if (!strcasecmp(levelstr,\"debug\")) level = LL_DEBUG;\n    else if (!strcasecmp(levelstr,\"verbose\")) level = LL_VERBOSE;\n    else if (!strcasecmp(levelstr,\"notice\")) level = LL_NOTICE;\n    else if (!strcasecmp(levelstr,\"warning\")) level = LL_WARNING;\n    else level = LL_VERBOSE; /* Default. */\n\n    if (level < server.verbosity) return;\n\n    name_len = snprintf(msg, sizeof(msg),\"<%s> \", module? module->name: \"module\");\n    vsnprintf(msg + name_len, sizeof(msg) - name_len, fmt, ap);\n    serverLogRaw(level,msg);\n}\n\n/* Produces a log message to the standard Redis log, the format accepts\n * printf-alike specifiers, while level is a string describing the log\n * level to use when emitting the log, and must be one of the following:\n *\n * * \"debug\"\n * * \"verbose\"\n * * \"notice\"\n * * \"warning\"\n *\n * If the specified log level is invalid, verbose is used by default.\n * There is a fixed limit to the length of the log line this function is able\n * to emit, this limit is not specified but is guaranteed to be more than\n * a few lines of text.\n *\n * The ctx argument may be NULL if cannot be provided in the context of the\n * caller for instance threads or callbacks, in which case a generic \"module\"\n * will be used instead of the module name.\n */\nvoid RM_Log(RedisModuleCtx *ctx, const char *levelstr, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    RM_LogRaw(ctx? ctx->module: NULL,levelstr,fmt,ap);\n    va_end(ap);\n}\n\n/* Log errors from RDB / AOF serialization callbacks.\n *\n * This function should be used when a callback is returning a critical\n * error to the caller since cannot load or save the data for some\n * critical reason. */\nvoid RM_LogIOError(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    RM_LogRaw(io->type->module,levelstr,fmt,ap);\n    va_end(ap);\n}\n\n/* Redis-like assert function.\n *\n * A failed assertion will shut down the server and produce logging information\n * that looks identical to information generated by Redis itself.\n */\nvoid RM__Assert(const char *estr, const char *file, int line) {\n    _serverAssert(estr, file, line);\n}\n\n/* Allows adding event to the latency monitor to be observed by the LATENCY\n * command. The call is skipped if the latency is smaller than the configured\n * latency-monitor-threshold. */\nvoid RM_LatencyAddSample(const char *event, mstime_t latency) {\n    if (latency >= server.latency_monitor_threshold)\n        latencyAddSample(event, latency);\n}\n\n/* --------------------------------------------------------------------------\n * Blocking clients from modules\n * -------------------------------------------------------------------------- */\n\n/* Readable handler for the awake pipe. We do nothing here, the awake bytes\n * will be actually read in a more appropriate place in the\n * moduleHandleBlockedClients() function that is where clients are actually\n * served. */\nvoid moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n    UNUSED(privdata);\n}\n\n/* This is called from blocked.c in order to unblock a client: may be called\n * for multiple reasons while the client is in the middle of being blocked\n * because the client is terminated, but is also called for cleanup when a\n * client is unblocked in a clean way after replaying.\n *\n * What we do here is just to set the client to NULL in the redis module\n * blocked client handle. This way if the client is terminated while there\n * is a pending threaded operation involving the blocked client, we'll know\n * that the client no longer exists and no reply callback should be called.\n *\n * The structure RedisModuleBlockedClient will be always deallocated when\n * running the list of clients blocked by a module that need to be unblocked. */\nvoid unblockClientFromModule(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n\n    /* Call the disconnection callback if any. Note that\n     * bc->disconnect_callback is set to NULL if the client gets disconnected\n     * by the module itself or because of a timeout, so the callback will NOT\n     * get called if this is not an actual disconnection event. */\n    if (bc->disconnect_callback) {\n        RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n        ctx.blocked_privdata = bc->privdata;\n        ctx.module = bc->module;\n        ctx.client = bc->client;\n        bc->disconnect_callback(&ctx,bc);\n        moduleFreeContext(&ctx);\n    }\n\n    /* If we made it here and client is still blocked it means that the command\n     * timed-out, client was killed or disconnected and disconnect_callback was\n     * not implemented (or it was, but RM_UnblockClient was not called from\n     * within it, as it should).\n     * We must call moduleUnblockClient in order to free privdata and\n     * RedisModuleBlockedClient.\n     *\n     * Note that we only do that for clients that are blocked on keys, for which\n     * the contract is that the module should not call RM_UnblockClient under\n     * normal circumstances.\n     * Clients implementing threads and working with private data should be\n     * aware that calling RM_UnblockClient for every blocked client is their\n     * responsibility, and if they fail to do so memory may leak. Ideally they\n     * should implement the disconnect and timeout callbacks and call\n     * RM_UnblockClient, but any other way is also acceptable. */\n    if (bc->blocked_on_keys && !bc->unblocked)\n        moduleUnblockClient(c);\n\n    bc->client = NULL;\n    /* Reset the client for a new query since, for blocking commands implemented\n     * into modules, we do not it immediately after the command returns (and\n     * the client blocks) in order to be still able to access the argument\n     * vector from callbacks. */\n    resetClient(c);\n}\n\n/* Block a client in the context of a module: this function implements both\n * RM_BlockClient() and RM_BlockClientOnKeys() depending on the fact the\n * keys are passed or not.\n *\n * When not blocking for keys, the keys, numkeys, and privdata parameters are\n * not needed. The privdata in that case must be NULL, since later is\n * RM_UnblockClient() that will provide some private data that the reply\n * callback will receive.\n *\n * Instead when blocking for keys, normally RM_UnblockClient() will not be\n * called (because the client will unblock when the key is modified), so\n * 'privdata' should be provided in that case, so that once the client is\n * unlocked and the reply callback is called, it will receive its associated\n * private data.\n *\n * Even when blocking on keys, RM_UnblockClient() can be called however, but\n * in that case the privdata argument is disregarded, because we pass the\n * reply callback the privdata that is set here while blocking.\n */\nRedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) {\n    client *c = ctx->client;\n    int islua = c->flags & CLIENT_LUA;\n    int ismulti = c->flags & CLIENT_MULTI;\n\n    c->bpop.module_blocked_handle = zmalloc(sizeof(RedisModuleBlockedClient));\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    ctx->module->blocked_clients++;\n\n    /* We need to handle the invalid operation of calling modules blocking\n     * commands from Lua or MULTI. We actually create an already aborted\n     * (client set to NULL) blocked client handle, and actually reply with\n     * an error. */\n    mstime_t timeout = timeout_ms ? (mstime()+timeout_ms) : 0;\n    bc->client = (islua || ismulti) ? NULL : c;\n    bc->module = ctx->module;\n    bc->reply_callback = reply_callback;\n    bc->timeout_callback = timeout_callback;\n    bc->disconnect_callback = NULL; /* Set by RM_SetDisconnectCallback() */\n    bc->free_privdata = free_privdata;\n    bc->privdata = privdata;\n    bc->reply_client = createClient(NULL);\n    bc->reply_client->flags |= CLIENT_MODULE;\n    bc->dbid = c->db->id;\n    bc->blocked_on_keys = keys != NULL;\n    bc->unblocked = 0;\n    c->bpop.timeout = timeout;\n\n    if (islua || ismulti) {\n        c->bpop.module_blocked_handle = NULL;\n        addReplyError(c, islua ?\n            \"Blocking module command called from Lua script\" :\n            \"Blocking module command called from transaction\");\n    } else {\n        if (keys) {\n            blockForKeys(c,BLOCKED_MODULE,keys,numkeys,timeout,NULL,NULL);\n        } else {\n            blockClient(c,BLOCKED_MODULE);\n        }\n    }\n    return bc;\n}\n\n/* This function is called from module.c in order to check if a module\n * blocked for BLOCKED_MODULE and subtype 'on keys' (bc->blocked_on_keys true)\n * can really be unblocked, since the module was able to serve the client.\n * If the callback returns REDISMODULE_OK, then the client can be unblocked,\n * otherwise the client remains blocked and we'll retry again when one of\n * the keys it blocked for becomes \"ready\" again.\n * This function returns 1 if client was served (and should be unblocked) */\nint moduleTryServeClientBlockedOnKey(client *c, robj *key) {\n    int served = 0;\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n\n    /* Protect against re-processing: don't serve clients that are already\n     * in the unblocking list for any reason (including RM_UnblockClient()\n     * explicit call). See #6798. */\n    if (bc->unblocked) return 0;\n\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n    ctx.flags |= REDISMODULE_CTX_BLOCKED_REPLY;\n    ctx.blocked_ready_key = key;\n    ctx.blocked_privdata = bc->privdata;\n    ctx.module = bc->module;\n    ctx.client = bc->client;\n    ctx.blocked_client = bc;\n    if (bc->reply_callback(&ctx,(void**)c->argv,c->argc) == REDISMODULE_OK)\n        served = 1;\n    moduleFreeContext(&ctx);\n    return served;\n}\n\n/* Block a client in the context of a blocking command, returning an handle\n * which will be used, later, in order to unblock the client with a call to\n * RedisModule_UnblockClient(). The arguments specify callback functions\n * and a timeout after which the client is unblocked.\n *\n * The callbacks are called in the following contexts:\n *\n *     reply_callback:  called after a successful RedisModule_UnblockClient()\n *                      call in order to reply to the client and unblock it.\n *\n *     reply_timeout:   called when the timeout is reached in order to send an\n *                      error to the client.\n *\n *     free_privdata:   called in order to free the private data that is passed\n *                      by RedisModule_UnblockClient() call.\n *\n * Note: RedisModule_UnblockClient should be called for every blocked client,\n *       even if client was killed, timed-out or disconnected. Failing to do so\n *       will result in memory leaks.\n */\nRedisModuleBlockedClient *RM_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) {\n    return moduleBlockClient(ctx,reply_callback,timeout_callback,free_privdata,timeout_ms, NULL,0,NULL);\n}\n\n/* This call is similar to RedisModule_BlockClient(), however in this case we\n * don't just block the client, but also ask Redis to unblock it automatically\n * once certain keys become \"ready\", that is, contain more data.\n *\n * Basically this is similar to what a typical Redis command usually does,\n * like BLPOP or ZPOPMAX: the client blocks if it cannot be served ASAP,\n * and later when the key receives new data (a list push for instance), the\n * client is unblocked and served.\n *\n * However in the case of this module API, when the client is unblocked?\n *\n * 1. If you block ok a key of a type that has blocking operations associated,\n *    like a list, a sorted set, a stream, and so forth, the client may be\n *    unblocked once the relevant key is targeted by an operation that normally\n *    unblocks the native blocking operations for that type. So if we block\n *    on a list key, an RPUSH command may unblock our client and so forth.\n * 2. If you are implementing your native data type, or if you want to add new\n *    unblocking conditions in addition to \"1\", you can call the modules API\n *    RedisModule_SignalKeyAsReady().\n *\n * Anyway we can't be sure if the client should be unblocked just because the\n * key is signaled as ready: for instance a successive operation may change the\n * key, or a client in queue before this one can be served, modifying the key\n * as well and making it empty again. So when a client is blocked with\n * RedisModule_BlockClientOnKeys() the reply callback is not called after\n * RM_UnblockCLient() is called, but every time a key is signaled as ready:\n * if the reply callback can serve the client, it returns REDISMODULE_OK\n * and the client is unblocked, otherwise it will return REDISMODULE_ERR\n * and we'll try again later.\n *\n * The reply callback can access the key that was signaled as ready by\n * calling the API RedisModule_GetBlockedClientReadyKey(), that returns\n * just the string name of the key as a RedisModuleString object.\n *\n * Thanks to this system we can setup complex blocking scenarios, like\n * unblocking a client only if a list contains at least 5 items or other\n * more fancy logics.\n *\n * Note that another difference with RedisModule_BlockClient(), is that here\n * we pass the private data directly when blocking the client: it will\n * be accessible later in the reply callback. Normally when blocking with\n * RedisModule_BlockClient() the private data to reply to the client is\n * passed when calling RedisModule_UnblockClient() but here the unblocking\n * is performed by Redis itself, so we need to have some private data before\n * hand. The private data is used to store any information about the specific\n * unblocking operation that you are implementing. Such information will be\n * freed using the free_privdata callback provided by the user.\n *\n * However the reply callback will be able to access the argument vector of\n * the command, so the private data is often not needed.\n *\n * Note: Under normal circumstances RedisModule_UnblockClient should not be\n *       called for clients that are blocked on keys (Either the key will\n *       become ready or a timeout will occur). If for some reason you do want\n *       to call RedisModule_UnblockClient it is possible: Client will be\n *       handled as if it were timed-out (You must implement the timeout\n *       callback in that case).\n */\nRedisModuleBlockedClient *RM_BlockClientOnKeys(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) {\n    return moduleBlockClient(ctx,reply_callback,timeout_callback,free_privdata,timeout_ms, keys,numkeys,privdata);\n}\n\n/* This function is used in order to potentially unblock a client blocked\n * on keys with RedisModule_BlockClientOnKeys(). When this function is called,\n * all the clients blocked for this key will get their reply callback called,\n * and if the callback returns REDISMODULE_OK the client will be unblocked. */\nvoid RM_SignalKeyAsReady(RedisModuleCtx *ctx, RedisModuleString *key) {\n    signalKeyAsReady(ctx->client->db, key);\n}\n\n/* Implements RM_UnblockClient() and moduleUnblockClient(). */\nint moduleUnblockClientByHandle(RedisModuleBlockedClient *bc, void *privdata) {\n    pthread_mutex_lock(&moduleUnblockedClientsMutex);\n    if (!bc->blocked_on_keys) bc->privdata = privdata;\n    bc->unblocked = 1;\n    listAddNodeTail(moduleUnblockedClients,bc);\n    if (write(server.module_blocked_pipe[1],\"A\",1) != 1) {\n        /* Ignore the error, this is best-effort. */\n    }\n    pthread_mutex_unlock(&moduleUnblockedClientsMutex);\n    return REDISMODULE_OK;\n}\n\n/* This API is used by the Redis core to unblock a client that was blocked\n * by a module. */\nvoid moduleUnblockClient(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    moduleUnblockClientByHandle(bc,NULL);\n}\n\n/* Return true if the client 'c' was blocked by a module using\n * RM_BlockClientOnKeys(). */\nint moduleClientIsBlockedOnKeys(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    return bc->blocked_on_keys;\n}\n\n/* Unblock a client blocked by `RedisModule_BlockedClient`. This will trigger\n * the reply callbacks to be called in order to reply to the client.\n * The 'privdata' argument will be accessible by the reply callback, so\n * the caller of this function can pass any value that is needed in order to\n * actually reply to the client.\n *\n * A common usage for 'privdata' is a thread that computes something that\n * needs to be passed to the client, included but not limited some slow\n * to compute reply or some reply obtained via networking.\n *\n * Note 1: this function can be called from threads spawned by the module.\n *\n * Note 2: when we unblock a client that is blocked for keys using\n * the API RedisModule_BlockClientOnKeys(), the privdata argument here is\n * not used, and the reply callback is called with the privdata pointer that\n * was passed when blocking the client.\n *\n * Unblocking a client that was blocked for keys using this API will still\n * require the client to get some reply, so the function will use the\n * \"timeout\" handler in order to do so. */\nint RM_UnblockClient(RedisModuleBlockedClient *bc, void *privdata) {\n    if (bc->blocked_on_keys) {\n        /* In theory the user should always pass the timeout handler as an\n         * argument, but better to be safe than sorry. */\n        if (bc->timeout_callback == NULL) return REDISMODULE_ERR;\n        if (bc->unblocked) return REDISMODULE_OK;\n        if (bc->client) moduleBlockedClientTimedOut(bc->client);\n    }\n    moduleUnblockClientByHandle(bc,privdata);\n    return REDISMODULE_OK;\n}\n\n/* Abort a blocked client blocking operation: the client will be unblocked\n * without firing any callback. */\nint RM_AbortBlock(RedisModuleBlockedClient *bc) {\n    bc->reply_callback = NULL;\n    bc->disconnect_callback = NULL;\n    return RM_UnblockClient(bc,NULL);\n}\n\n/* Set a callback that will be called if a blocked client disconnects\n * before the module has a chance to call RedisModule_UnblockClient()\n *\n * Usually what you want to do there, is to cleanup your module state\n * so that you can call RedisModule_UnblockClient() safely, otherwise\n * the client will remain blocked forever if the timeout is large.\n *\n * Notes:\n *\n * 1. It is not safe to call Reply* family functions here, it is also\n *    useless since the client is gone.\n *\n * 2. This callback is not called if the client disconnects because of\n *    a timeout. In such a case, the client is unblocked automatically\n *    and the timeout callback is called.\n */\nvoid RM_SetDisconnectCallback(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) {\n    bc->disconnect_callback = callback;\n}\n\n/* This function will check the moduleUnblockedClients queue in order to\n * call the reply callback and really unblock the client.\n *\n * Clients end into this list because of calls to RM_UnblockClient(),\n * however it is possible that while the module was doing work for the\n * blocked client, it was terminated by Redis (for timeout or other reasons).\n * When this happens the RedisModuleBlockedClient structure in the queue\n * will have the 'client' field set to NULL. */\nvoid moduleHandleBlockedClients(void) {\n    listNode *ln;\n    RedisModuleBlockedClient *bc;\n\n    pthread_mutex_lock(&moduleUnblockedClientsMutex);\n    /* Here we unblock all the pending clients blocked in modules operations\n     * so we can read every pending \"awake byte\" in the pipe. */\n    char buf[1];\n    while (read(server.module_blocked_pipe[0],buf,1) == 1);\n    while (listLength(moduleUnblockedClients)) {\n        ln = listFirst(moduleUnblockedClients);\n        bc = ln->value;\n        client *c = bc->client;\n        listDelNode(moduleUnblockedClients,ln);\n        pthread_mutex_unlock(&moduleUnblockedClientsMutex);\n\n        /* Release the lock during the loop, as long as we don't\n         * touch the shared list. */\n\n        /* Call the reply callback if the client is valid and we have\n         * any callback. However the callback is not called if the client\n         * was blocked on keys (RM_BlockClientOnKeys()), because we already\n         * called such callback in moduleTryServeClientBlockedOnKey() when\n         * the key was signaled as ready. */\n        if (c && !bc->blocked_on_keys && bc->reply_callback) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.flags |= REDISMODULE_CTX_BLOCKED_REPLY;\n            ctx.blocked_privdata = bc->privdata;\n            ctx.blocked_ready_key = NULL;\n            ctx.module = bc->module;\n            ctx.client = bc->client;\n            ctx.blocked_client = bc;\n            bc->reply_callback(&ctx,(void**)c->argv,c->argc);\n            moduleFreeContext(&ctx);\n        }\n\n        /* Free privdata if any. */\n        if (bc->privdata && bc->free_privdata) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            if (c == NULL)\n                ctx.flags |= REDISMODULE_CTX_BLOCKED_DISCONNECTED;\n            ctx.blocked_privdata = bc->privdata;\n            ctx.module = bc->module;\n            ctx.client = bc->client;\n            bc->free_privdata(&ctx,bc->privdata);\n            moduleFreeContext(&ctx);\n        }\n\n        /* It is possible that this blocked client object accumulated\n         * replies to send to the client in a thread safe context.\n         * We need to glue such replies to the client output buffer and\n         * free the temporary client we just used for the replies. */\n        if (c) AddReplyFromClient(c, bc->reply_client);\n        freeClient(bc->reply_client);\n\n        if (c != NULL) {\n            /* Before unblocking the client, set the disconnect callback\n             * to NULL, because if we reached this point, the client was\n             * properly unblocked by the module. */\n            bc->disconnect_callback = NULL;\n            unblockClient(c);\n            /* Put the client in the list of clients that need to write\n             * if there are pending replies here. This is needed since\n             * during a non blocking command the client may receive output. */\n            if (clientHasPendingReplies(c) &&\n                !(c->flags & CLIENT_PENDING_WRITE))\n            {\n                c->flags |= CLIENT_PENDING_WRITE;\n                listAddNodeHead(server.clients_pending_write,c);\n            }\n        }\n\n        /* Free 'bc' only after unblocking the client, since it is\n         * referenced in the client blocking context, and must be valid\n         * when calling unblockClient(). */\n        bc->module->blocked_clients--;\n        zfree(bc);\n\n        /* Lock again before to iterate the loop. */\n        pthread_mutex_lock(&moduleUnblockedClientsMutex);\n    }\n    pthread_mutex_unlock(&moduleUnblockedClientsMutex);\n}\n\n/* Called when our client timed out. After this function unblockClient()\n * is called, and it will invalidate the blocked client. So this function\n * does not need to do any cleanup. Eventually the module will call the\n * API to unblock the client and the memory will be released. */\nvoid moduleBlockedClientTimedOut(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n    ctx.flags |= REDISMODULE_CTX_BLOCKED_TIMEOUT;\n    ctx.module = bc->module;\n    ctx.client = bc->client;\n    ctx.blocked_client = bc;\n    bc->timeout_callback(&ctx,(void**)c->argv,c->argc);\n    moduleFreeContext(&ctx);\n    /* For timeout events, we do not want to call the disconnect callback,\n     * because the blocked client will be automatically disconnected in\n     * this case, and the user can still hook using the timeout callback. */\n    bc->disconnect_callback = NULL;\n}\n\n/* Return non-zero if a module command was called in order to fill the\n * reply for a blocked client. */\nint RM_IsBlockedReplyRequest(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_BLOCKED_REPLY) != 0;\n}\n\n/* Return non-zero if a module command was called in order to fill the\n * reply for a blocked client that timed out. */\nint RM_IsBlockedTimeoutRequest(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_BLOCKED_TIMEOUT) != 0;\n}\n\n/* Get the private data set by RedisModule_UnblockClient() */\nvoid *RM_GetBlockedClientPrivateData(RedisModuleCtx *ctx) {\n    return ctx->blocked_privdata;\n}\n\n/* Get the key that is ready when the reply callback is called in the context\n * of a client blocked by RedisModule_BlockClientOnKeys(). */\nRedisModuleString *RM_GetBlockedClientReadyKey(RedisModuleCtx *ctx) {\n    return ctx->blocked_ready_key;\n}\n\n/* Get the blocked client associated with a given context.\n * This is useful in the reply and timeout callbacks of blocked clients,\n * before sometimes the module has the blocked client handle references\n * around, and wants to cleanup it. */\nRedisModuleBlockedClient *RM_GetBlockedClientHandle(RedisModuleCtx *ctx) {\n    return ctx->blocked_client;\n}\n\n/* Return true if when the free callback of a blocked client is called,\n * the reason for the client to be unblocked is that it disconnected\n * while it was blocked. */\nint RM_BlockedClientDisconnected(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_BLOCKED_DISCONNECTED) != 0;\n}\n\n/* --------------------------------------------------------------------------\n * Thread Safe Contexts\n * -------------------------------------------------------------------------- */\n\n/* Return a context which can be used inside threads to make Redis context\n * calls with certain modules APIs. If 'bc' is not NULL then the module will\n * be bound to a blocked client, and it will be possible to use the\n * `RedisModule_Reply*` family of functions to accumulate a reply for when the\n * client will be unblocked. Otherwise the thread safe context will be\n * detached by a specific client.\n *\n * To call non-reply APIs, the thread safe context must be prepared with:\n *\n *     RedisModule_ThreadSafeContextLock(ctx);\n *     ... make your call here ...\n *     RedisModule_ThreadSafeContextUnlock(ctx);\n *\n * This is not needed when using `RedisModule_Reply*` functions, assuming\n * that a blocked client was used when the context was created, otherwise\n * no RedisModule_Reply* call should be made at all.\n *\n * TODO: thread safe contexts do not inherit the blocked client\n * selected database. */\nRedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) {\n    RedisModuleCtx *ctx = zmalloc(sizeof(*ctx));\n    RedisModuleCtx empty = REDISMODULE_CTX_INIT;\n    memcpy(ctx,&empty,sizeof(empty));\n    if (bc) {\n        ctx->blocked_client = bc;\n        ctx->module = bc->module;\n    }\n    ctx->flags |= REDISMODULE_CTX_THREAD_SAFE;\n    /* Even when the context is associated with a blocked client, we can't\n     * access it safely from another thread, so we create a fake client here\n     * in order to keep things like the currently selected database and similar\n     * things. */\n    ctx->client = createClient(NULL);\n    if (bc) {\n        selectDb(ctx->client,bc->dbid);\n        if (bc->client) ctx->client->id = bc->client->id;\n    }\n    return ctx;\n}\n\n/* Release a thread safe context. */\nvoid RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {\n    moduleFreeContext(ctx);\n    zfree(ctx);\n}\n\n/* Acquire the server lock before executing a thread safe API call.\n * This is not needed for `RedisModule_Reply*` calls when there is\n * a blocked client connected to the thread safe context. */\nvoid RM_ThreadSafeContextLock(RedisModuleCtx *ctx) {\n    UNUSED(ctx);\n    moduleAcquireGIL();\n}\n\n/* Release the server lock after a thread safe API call was executed. */\nvoid RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) {\n    UNUSED(ctx);\n    moduleReleaseGIL();\n}\n\nvoid moduleAcquireGIL(void) {\n    pthread_mutex_lock(&moduleGIL);\n}\n\nvoid moduleReleaseGIL(void) {\n    pthread_mutex_unlock(&moduleGIL);\n}\n\n\n/* --------------------------------------------------------------------------\n * Module Keyspace Notifications API\n * -------------------------------------------------------------------------- */\n\n/* Subscribe to keyspace notifications. This is a low-level version of the\n * keyspace-notifications API. A module can register callbacks to be notified\n * when keyspce events occur.\n *\n * Notification events are filtered by their type (string events, set events,\n * etc), and the subscriber callback receives only events that match a specific\n * mask of event types.\n *\n * When subscribing to notifications with RedisModule_SubscribeToKeyspaceEvents \n * the module must provide an event type-mask, denoting the events the subscriber\n * is interested in. This can be an ORed mask of any of the following flags:\n *\n *  - REDISMODULE_NOTIFY_GENERIC: Generic commands like DEL, EXPIRE, RENAME\n *  - REDISMODULE_NOTIFY_STRING: String events\n *  - REDISMODULE_NOTIFY_LIST: List events\n *  - REDISMODULE_NOTIFY_SET: Set events\n *  - REDISMODULE_NOTIFY_HASH: Hash events\n *  - REDISMODULE_NOTIFY_ZSET: Sorted Set events\n *  - REDISMODULE_NOTIFY_EXPIRED: Expiration events\n *  - REDISMODULE_NOTIFY_EVICTED: Eviction events\n *  - REDISMODULE_NOTIFY_STREAM: Stream events\n *  - REDISMODULE_NOTIFY_KEYMISS: Key-miss events\n *  - REDISMODULE_NOTIFY_ALL: All events (Excluding REDISMODULE_NOTIFY_KEYMISS)\n *\n * We do not distinguish between key events and keyspace events, and it is up\n * to the module to filter the actions taken based on the key.\n *\n * The subscriber signature is:\n *\n *   int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type,\n *                                       const char *event,\n *                                       RedisModuleString *key);\n *\n * `type` is the event type bit, that must match the mask given at registration\n * time. The event string is the actual command being executed, and key is the\n * relevant Redis key.\n *\n * Notification callback gets executed with a redis context that can not be\n * used to send anything to the client, and has the db number where the event\n * occurred as its selected db number.\n *\n * Notice that it is not necessary to enable notifications in redis.conf for\n * module notifications to work.\n *\n * Warning: the notification callbacks are performed in a synchronous manner,\n * so notification callbacks must to be fast, or they would slow Redis down.\n * If you need to take long actions, use threads to offload them.\n *\n * See https://redis.io/topics/notifications for more information.\n */\nint RM_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc callback) {\n    RedisModuleKeyspaceSubscriber *sub = zmalloc(sizeof(*sub));\n    sub->module = ctx->module;\n    sub->event_mask = types;\n    sub->notify_callback = callback;\n    sub->active = 0;\n\n    listAddNodeTail(moduleKeyspaceSubscribers, sub);\n    return REDISMODULE_OK;\n}\n\n/* Get the configured bitmap of notify-keyspace-events (Could be used\n * for additional filtering in RedisModuleNotificationFunc) */\nint RM_GetNotifyKeyspaceEvents() {\n    return server.notify_keyspace_events;\n}\n\n/* Expose notifyKeyspaceEvent to modules */\nint RM_NotifyKeyspaceEvent(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) {\n    if (!ctx || !ctx->client)\n        return REDISMODULE_ERR;\n    notifyKeyspaceEvent(type, (char *)event, key, ctx->client->db->id);\n    return REDISMODULE_OK;\n}\n\n/* Dispatcher for keyspace notifications to module subscriber functions.\n * This gets called  only if at least one module requested to be notified on\n * keyspace notifications */\nvoid moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid) {\n    /* Don't do anything if there aren't any subscribers */\n    if (listLength(moduleKeyspaceSubscribers) == 0) return;\n\n    listIter li;\n    listNode *ln;\n    listRewind(moduleKeyspaceSubscribers,&li);\n\n    /* Remove irrelevant flags from the type mask */\n    type &= ~(NOTIFY_KEYEVENT | NOTIFY_KEYSPACE);\n\n    while((ln = listNext(&li))) {\n        RedisModuleKeyspaceSubscriber *sub = ln->value;\n        /* Only notify subscribers on events matching they registration,\n         * and avoid subscribers triggering themselves */\n        if ((sub->event_mask & type) && sub->active == 0) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.module = sub->module;\n            ctx.client = moduleFreeContextReusedClient;\n            selectDb(ctx.client, dbid);\n\n            /* mark the handler as active to avoid reentrant loops.\n             * If the subscriber performs an action triggering itself,\n             * it will not be notified about it. */\n            sub->active = 1;\n            sub->notify_callback(&ctx, type, event, key);\n            sub->active = 0;\n            moduleFreeContext(&ctx);\n        }\n    }\n}\n\n/* Unsubscribe any notification subscribers this module has upon unloading */\nvoid moduleUnsubscribeNotifications(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    listRewind(moduleKeyspaceSubscribers,&li);\n    while((ln = listNext(&li))) {\n        RedisModuleKeyspaceSubscriber *sub = ln->value;\n        if (sub->module == module) {\n            listDelNode(moduleKeyspaceSubscribers, ln);\n            zfree(sub);\n        }\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Modules Cluster API\n * -------------------------------------------------------------------------- */\n\n/* The Cluster message callback function pointer type. */\ntypedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len);\n\n/* This structure identifies a registered caller: it must match a given module\n * ID, for a given message type. The callback function is just the function\n * that was registered as receiver. */\ntypedef struct moduleClusterReceiver {\n    uint64_t module_id;\n    RedisModuleClusterMessageReceiver callback;\n    struct RedisModule *module;\n    struct moduleClusterReceiver *next;\n} moduleClusterReceiver;\n\ntypedef struct moduleClusterNodeInfo {\n    int flags;\n    char ip[NET_IP_STR_LEN];\n    int port;\n    char master_id[40]; /* Only if flags & REDISMODULE_NODE_MASTER is true. */\n} mdouleClusterNodeInfo;\n\n/* We have an array of message types: each bucket is a linked list of\n * configured receivers. */\nstatic moduleClusterReceiver *clusterReceivers[UINT8_MAX];\n\n/* Dispatch the message to the right module receiver. */\nvoid moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len) {\n    moduleClusterReceiver *r = clusterReceivers[type];\n    while(r) {\n        if (r->module_id == module_id) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.module = r->module;\n            ctx.client = moduleFreeContextReusedClient;\n            selectDb(ctx.client, 0);\n            r->callback(&ctx,sender_id,type,payload,len);\n            moduleFreeContext(&ctx);\n            return;\n        }\n        r = r->next;\n    }\n}\n\n/* Register a callback receiver for cluster messages of type 'type'. If there\n * was already a registered callback, this will replace the callback function\n * with the one provided, otherwise if the callback is set to NULL and there\n * is already a callback for this function, the callback is unregistered\n * (so this API call is also used in order to delete the receiver). */\nvoid RM_RegisterClusterMessageReceiver(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) {\n    if (!server.cluster_enabled) return;\n\n    uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);\n    moduleClusterReceiver *r = clusterReceivers[type], *prev = NULL;\n    while(r) {\n        if (r->module_id == module_id) {\n            /* Found! Set or delete. */\n            if (callback) {\n                r->callback = callback;\n            } else {\n                /* Delete the receiver entry if the user is setting\n                 * it to NULL. Just unlink the receiver node from the\n                 * linked list. */\n                if (prev)\n                    prev->next = r->next;\n                else\n                    clusterReceivers[type]->next = r->next;\n                zfree(r);\n            }\n            return;\n        }\n        prev = r;\n        r = r->next;\n    }\n\n    /* Not found, let's add it. */\n    if (callback) {\n        r = zmalloc(sizeof(*r));\n        r->module_id = module_id;\n        r->module = ctx->module;\n        r->callback = callback;\n        r->next = clusterReceivers[type];\n        clusterReceivers[type] = r;\n    }\n}\n\n/* Send a message to all the nodes in the cluster if `target` is NULL, otherwise\n * at the specified target, which is a REDISMODULE_NODE_ID_LEN bytes node ID, as\n * returned by the receiver callback or by the nodes iteration functions.\n *\n * The function returns REDISMODULE_OK if the message was successfully sent,\n * otherwise if the node is not connected or such node ID does not map to any\n * known cluster node, REDISMODULE_ERR is returned. */\nint RM_SendClusterMessage(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len) {\n    if (!server.cluster_enabled) return REDISMODULE_ERR;\n    uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);\n    if (clusterSendModuleMessageToTarget(target_id,module_id,type,msg,len) == C_OK)\n        return REDISMODULE_OK;\n    else\n        return REDISMODULE_ERR;\n}\n\n/* Return an array of string pointers, each string pointer points to a cluster\n * node ID of exactly REDISMODULE_NODE_ID_SIZE bytes (without any null term).\n * The number of returned node IDs is stored into `*numnodes`.\n * However if this function is called by a module not running an a Redis\n * instance with Redis Cluster enabled, NULL is returned instead.\n *\n * The IDs returned can be used with RedisModule_GetClusterNodeInfo() in order\n * to get more information about single nodes.\n *\n * The array returned by this function must be freed using the function\n * RedisModule_FreeClusterNodesList().\n *\n * Example:\n *\n *     size_t count, j;\n *     char **ids = RedisModule_GetClusterNodesList(ctx,&count);\n *     for (j = 0; j < count; j++) {\n *         RedisModule_Log(\"notice\",\"Node %.*s\",\n *             REDISMODULE_NODE_ID_LEN,ids[j]);\n *     }\n *     RedisModule_FreeClusterNodesList(ids);\n */\nchar **RM_GetClusterNodesList(RedisModuleCtx *ctx, size_t *numnodes) {\n    UNUSED(ctx);\n\n    if (!server.cluster_enabled) return NULL;\n    size_t count = dictSize(server.cluster->nodes);\n    char **ids = zmalloc((count+1)*REDISMODULE_NODE_ID_LEN);\n    dictIterator *di = dictGetIterator(server.cluster->nodes);\n    dictEntry *de;\n    int j = 0;\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        if (node->flags & (CLUSTER_NODE_NOADDR|CLUSTER_NODE_HANDSHAKE)) continue;\n        ids[j] = zmalloc(REDISMODULE_NODE_ID_LEN);\n        memcpy(ids[j],node->name,REDISMODULE_NODE_ID_LEN);\n        j++;\n    }\n    *numnodes = j;\n    ids[j] = NULL; /* Null term so that FreeClusterNodesList does not need\n                    * to also get the count argument. */\n    dictReleaseIterator(di);\n    return ids;\n}\n\n/* Free the node list obtained with RedisModule_GetClusterNodesList. */\nvoid RM_FreeClusterNodesList(char **ids) {\n    if (ids == NULL) return;\n    for (int j = 0; ids[j]; j++) zfree(ids[j]);\n    zfree(ids);\n}\n\n/* Return this node ID (REDISMODULE_CLUSTER_ID_LEN bytes) or NULL if the cluster\n * is disabled. */\nconst char *RM_GetMyClusterID(void) {\n    if (!server.cluster_enabled) return NULL;\n    return server.cluster->myself->name;\n}\n\n/* Return the number of nodes in the cluster, regardless of their state\n * (handshake, noaddress, ...) so that the number of active nodes may actually\n * be smaller, but not greater than this number. If the instance is not in\n * cluster mode, zero is returned. */\nsize_t RM_GetClusterSize(void) {\n    if (!server.cluster_enabled) return 0;\n    return dictSize(server.cluster->nodes);\n}\n\n/* Populate the specified info for the node having as ID the specified 'id',\n * then returns REDISMODULE_OK. Otherwise if the node ID does not exist from\n * the POV of this local node, REDISMODULE_ERR is returned.\n *\n * The arguments ip, master_id, port and flags can be NULL in case we don't\n * need to populate back certain info. If an ip and master_id (only populated\n * if the instance is a slave) are specified, they point to buffers holding\n * at least REDISMODULE_NODE_ID_LEN bytes. The strings written back as ip\n * and master_id are not null terminated.\n *\n * The list of flags reported is the following:\n *\n * * REDISMODULE_NODE_MYSELF        This node\n * * REDISMODULE_NODE_MASTER        The node is a master\n * * REDISMODULE_NODE_SLAVE         The node is a replica\n * * REDISMODULE_NODE_PFAIL         We see the node as failing\n * * REDISMODULE_NODE_FAIL          The cluster agrees the node is failing\n * * REDISMODULE_NODE_NOFAILOVER    The slave is configured to never failover\n */\n\nclusterNode *clusterLookupNode(const char *name); /* We need access to internals */\n\nint RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) {\n    UNUSED(ctx);\n\n    clusterNode *node = clusterLookupNode(id);\n    if (node == NULL ||\n        node->flags & (CLUSTER_NODE_NOADDR|CLUSTER_NODE_HANDSHAKE))\n    {\n        return REDISMODULE_ERR;\n    }\n\n    if (ip) strncpy(ip,node->ip,NET_IP_STR_LEN);\n\n    if (master_id) {\n        /* If the information is not available, the function will set the\n         * field to zero bytes, so that when the field can't be populated the\n         * function kinda remains predictable. */\n        if (node->flags & CLUSTER_NODE_MASTER && node->slaveof)\n            memcpy(master_id,node->slaveof->name,REDISMODULE_NODE_ID_LEN);\n        else\n            memset(master_id,0,REDISMODULE_NODE_ID_LEN);\n    }\n    if (port) *port = node->port;\n\n    /* As usually we have to remap flags for modules, in order to ensure\n     * we can provide binary compatibility. */\n    if (flags) {\n        *flags = 0;\n        if (node->flags & CLUSTER_NODE_MYSELF) *flags |= REDISMODULE_NODE_MYSELF;\n        if (node->flags & CLUSTER_NODE_MASTER) *flags |= REDISMODULE_NODE_MASTER;\n        if (node->flags & CLUSTER_NODE_SLAVE) *flags |= REDISMODULE_NODE_SLAVE;\n        if (node->flags & CLUSTER_NODE_PFAIL) *flags |= REDISMODULE_NODE_PFAIL;\n        if (node->flags & CLUSTER_NODE_FAIL) *flags |= REDISMODULE_NODE_FAIL;\n        if (node->flags & CLUSTER_NODE_NOFAILOVER) *flags |= REDISMODULE_NODE_NOFAILOVER;\n    }\n    return REDISMODULE_OK;\n}\n\n/* Set Redis Cluster flags in order to change the normal behavior of\n * Redis Cluster, especially with the goal of disabling certain functions.\n * This is useful for modules that use the Cluster API in order to create\n * a different distributed system, but still want to use the Redis Cluster\n * message bus. Flags that can be set:\n *\n *  CLUSTER_MODULE_FLAG_NO_FAILOVER\n *  CLUSTER_MODULE_FLAG_NO_REDIRECTION\n *\n * With the following effects:\n *\n *  NO_FAILOVER: prevent Redis Cluster slaves to failover a failing master.\n *               Also disables the replica migration feature.\n *\n *  NO_REDIRECTION: Every node will accept any key, without trying to perform\n *                  partitioning according to the user Redis Cluster algorithm.\n *                  Slots informations will still be propagated across the\n *                  cluster, but without effects. */\nvoid RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) {\n    UNUSED(ctx);\n    if (flags & REDISMODULE_CLUSTER_FLAG_NO_FAILOVER)\n        server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_FAILOVER;\n    if (flags & REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION)\n        server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_REDIRECTION;\n}\n\n/* --------------------------------------------------------------------------\n * Modules Timers API\n *\n * Module timers are an high precision \"green timers\" abstraction where\n * every module can register even millions of timers without problems, even if\n * the actual event loop will just have a single timer that is used to awake the\n * module timers subsystem in order to process the next event.\n *\n * All the timers are stored into a radix tree, ordered by expire time, when\n * the main Redis event loop timer callback is called, we try to process all\n * the timers already expired one after the other. Then we re-enter the event\n * loop registering a timer that will expire when the next to process module\n * timer will expire.\n *\n * Every time the list of active timers drops to zero, we unregister the\n * main event loop timer, so that there is no overhead when such feature is\n * not used.\n * -------------------------------------------------------------------------- */\n\nstatic rax *Timers;     /* The radix tree of all the timers sorted by expire. */\nlong long aeTimer = -1; /* Main event loop (ae.c) timer identifier. */\n\ntypedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);\n\n/* The timer descriptor, stored as value in the radix tree. */\ntypedef struct RedisModuleTimer {\n    RedisModule *module;                /* Module reference. */\n    RedisModuleTimerProc callback;      /* The callback to invoke on expire. */\n    void *data;                         /* Private data for the callback. */\n    int dbid;                           /* Database number selected by the original client. */\n} RedisModuleTimer;\n\n/* This is the timer handler that is called by the main event loop. We schedule\n * this timer to be called when the nearest of our module timers will expire. */\nint moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n\n    /* To start let's try to fire all the timers already expired. */\n    raxIterator ri;\n    raxStart(&ri,Timers);\n    uint64_t now = ustime();\n    long long next_period = 0;\n    while(1) {\n        raxSeek(&ri,\"^\",NULL,0);\n        if (!raxNext(&ri)) break;\n        uint64_t expiretime;\n        memcpy(&expiretime,ri.key,sizeof(expiretime));\n        expiretime = ntohu64(expiretime);\n        if (now >= expiretime) {\n            RedisModuleTimer *timer = ri.data;\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n\n            ctx.module = timer->module;\n            ctx.client = moduleFreeContextReusedClient;\n            selectDb(ctx.client, timer->dbid);\n            timer->callback(&ctx,timer->data);\n            moduleFreeContext(&ctx);\n            raxRemove(Timers,(unsigned char*)ri.key,ri.key_len,NULL);\n            zfree(timer);\n        } else {\n            next_period = (expiretime-now)/1000; /* Scale to milliseconds. */\n            break;\n        }\n    }\n    raxStop(&ri);\n\n    /* Reschedule the next timer or cancel it. */\n    if (next_period <= 0) next_period = 1;\n    return (raxSize(Timers) > 0) ? next_period : AE_NOMORE;\n}\n\n/* Create a new timer that will fire after `period` milliseconds, and will call\n * the specified function using `data` as argument. The returned timer ID can be\n * used to get information from the timer or to stop it before it fires. */\nRedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) {\n    RedisModuleTimer *timer = zmalloc(sizeof(*timer));\n    timer->module = ctx->module;\n    timer->callback = callback;\n    timer->data = data;\n    timer->dbid = ctx->client ? ctx->client->db->id : 0;\n    uint64_t expiretime = ustime()+period*1000;\n    uint64_t key;\n\n    while(1) {\n        key = htonu64(expiretime);\n        if (raxFind(Timers, (unsigned char*)&key,sizeof(key)) == raxNotFound) {\n            raxInsert(Timers,(unsigned char*)&key,sizeof(key),timer,NULL);\n            break;\n        } else {\n            expiretime++;\n        }\n    }\n\n    /* We need to install the main event loop timer if it's not already\n     * installed, or we may need to refresh its period if we just installed\n     * a timer that will expire sooner than any other else. */\n    if (aeTimer != -1) {\n        raxIterator ri;\n        raxStart(&ri,Timers);\n        raxSeek(&ri,\"^\",NULL,0);\n        raxNext(&ri);\n        if (memcmp(ri.key,&key,sizeof(key)) == 0) {\n            /* This is the first key, we need to re-install the timer according\n             * to the just added event. */\n            aeDeleteTimeEvent(server.el,aeTimer);\n            aeTimer = -1;\n        }\n        raxStop(&ri);\n    }\n\n    /* If we have no main timer (the old one was invalidated, or this is the\n     * first module timer we have), install one. */\n    if (aeTimer == -1)\n        aeTimer = aeCreateTimeEvent(server.el,period,moduleTimerHandler,NULL,NULL);\n\n    return key;\n}\n\n/* Stop a timer, returns REDISMODULE_OK if the timer was found, belonged to the\n * calling module, and was stopped, otherwise REDISMODULE_ERR is returned.\n * If not NULL, the data pointer is set to the value of the data argument when\n * the timer was created. */\nint RM_StopTimer(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) {\n    RedisModuleTimer *timer = raxFind(Timers,(unsigned char*)&id,sizeof(id));\n    if (timer == raxNotFound || timer->module != ctx->module)\n        return REDISMODULE_ERR;\n    if (data) *data = timer->data;\n    raxRemove(Timers,(unsigned char*)&id,sizeof(id),NULL);\n    zfree(timer);\n    return REDISMODULE_OK;\n}\n\n/* Obtain information about a timer: its remaining time before firing\n * (in milliseconds), and the private data pointer associated with the timer.\n * If the timer specified does not exist or belongs to a different module\n * no information is returned and the function returns REDISMODULE_ERR, otherwise\n * REDISMODULE_OK is returned. The arguments remaining or data can be NULL if\n * the caller does not need certain information. */\nint RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) {\n    RedisModuleTimer *timer = raxFind(Timers,(unsigned char*)&id,sizeof(id));\n    if (timer == raxNotFound || timer->module != ctx->module)\n        return REDISMODULE_ERR;\n    if (remaining) {\n        int64_t rem = ntohu64(id)-ustime();\n        if (rem < 0) rem = 0;\n        *remaining = rem/1000; /* Scale to milliseconds. */\n    }\n    if (data) *data = timer->data;\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Modules ACL API\n *\n * Implements a hook into the authentication and authorization within Redis. \n * --------------------------------------------------------------------------*/\n\n/* This function is called when a client's user has changed and invokes the\n * client's user changed callback if it was set. This callback should\n * cleanup any state the module was tracking about this client. \n * \n * A client's user can be changed through the AUTH command, module \n * authentication, and when a client is freed. */\nvoid moduleNotifyUserChanged(client *c) {\n    if (c->auth_callback) {\n        c->auth_callback(c->id, c->auth_callback_privdata);\n\n        /* The callback will fire exactly once, even if the user remains \n         * the same. It is expected to completely clean up the state\n         * so all references are cleared here. */\n        c->auth_callback = NULL;\n        c->auth_callback_privdata = NULL;\n        c->auth_module = NULL;\n    }\n}\n\nvoid revokeClientAuthentication(client *c) {\n    /* Freeing the client would result in moduleNotifyUserChanged() to be\n     * called later, however since we use revokeClientAuthentication() also\n     * in moduleFreeAuthenticatedClients() to implement module unloading, we\n     * do this action ASAP: this way if the module is unloaded, when the client\n     * is eventually freed we don't rely on the module to still exist. */\n    moduleNotifyUserChanged(c);\n\n    c->user = DefaultUser;\n    c->authenticated = 0;\n    freeClientAsync(c);\n}\n\n/* Cleanup all clients that have been authenticated with this module. This\n * is called from onUnload() to give the module a chance to cleanup any\n * resources associated with clients it has authenticated. */\nstatic void moduleFreeAuthenticatedClients(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client *c = listNodeValue(ln);\n        if (!c->auth_module) continue;\n\n        RedisModule *auth_module = (RedisModule *) c->auth_module;\n        if (auth_module == module) { \n            revokeClientAuthentication(c);\n        }\n    }\n}\n\n/* Creates a Redis ACL user that the module can use to authenticate a client.\n * After obtaining the user, the module should set what such user can do\n * using the RM_SetUserACL() function. Once configured, the user\n * can be used in order to authenticate a connection, with the specified\n * ACL rules, using the RedisModule_AuthClientWithUser() function.\n *\n * Note that:\n *\n * * Users created here are not listed by the ACL command.\n * * Users created here are not checked for duplicated name, so it's up to\n *   the module calling this function to take care of not creating users\n *   with the same name.\n * * The created user can be used to authenticate multiple Redis connections.\n *\n * The caller can later free the user using the function\n * RM_FreeModuleUser(). When this function is called, if there are\n * still clients authenticated with this user, they are disconnected.\n * The function to free the user should only be used when the caller really\n * wants to invalidate the user to define a new one with different\n * capabilities. */\nRedisModuleUser *RM_CreateModuleUser(const char *name) {\n    RedisModuleUser *new_user = zmalloc(sizeof(RedisModuleUser));\n    new_user->user = ACLCreateUnlinkedUser();\n\n    /* Free the previous temporarily assigned name to assign the new one */\n    sdsfree(new_user->user->name);\n    new_user->user->name = sdsnew(name);\n    return new_user;\n}\n\n/* Frees a given user and disconnects all of the clients that have been\n * authenticated with it. See RM_CreateModuleUser for detailed usage.*/\nint RM_FreeModuleUser(RedisModuleUser *user) {\n    ACLFreeUserAndKillClients(user->user);\n    zfree(user);\n    return REDISMODULE_OK;\n}\n\n/* Sets the permissions of a user created through the redis module \n * interface. The syntax is the same as ACL SETUSER, so refer to the \n * documentation in acl.c for more information. See RM_CreateModuleUser\n * for detailed usage.\n * \n * Returns REDISMODULE_OK on success and REDISMODULE_ERR on failure\n * and will set an errno describing why the operation failed. */\nint RM_SetModuleUserACL(RedisModuleUser *user, const char* acl) {\n    return ACLSetUser(user->user, acl, -1);\n}\n\n/* Authenticate the client associated with the context with \n * the provided user. Returns REDISMODULE_OK on success and\n * REDISMODULE_ERR on error.\n * \n * This authentication can be tracked with the optional callback and private\n * data fields. The callback will be called whenever the user of the client\n * changes. This callback should be used to cleanup any state that is being\n * kept in the module related to the client authentication. It will only be\n * called once, even when the user hasn't changed, in order to allow for a\n * new callback to be specified. If this authentication does not need to be\n * tracked, pass in NULL for the callback and privdata. \n * \n * If client_id is not NULL, it will be filled with the id of the client\n * that was authenticated. This can be used with the \n * RM_DeauthenticateAndCloseClient() API in order to deauthenticate a \n * previously authenticated client if the authentication is no longer valid. \n * \n * For expensive authentication operations, it is recommended to block the\n * client and do the authentication in the background and then attach the user\n * to the client in a threadsafe context.  */\nstatic int authenticateClientWithUser(RedisModuleCtx *ctx, user *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) {\n    if (user->flags & USER_FLAG_DISABLED) {\n        return REDISMODULE_ERR;\n    }\n\n    moduleNotifyUserChanged(ctx->client);\n\n    ctx->client->user = user;\n    ctx->client->authenticated = 1;\n\n    if (callback) {\n        ctx->client->auth_callback = callback;\n        ctx->client->auth_callback_privdata = privdata;\n        ctx->client->auth_module = ctx->module;\n    }\n\n    if (client_id) {\n        *client_id = ctx->client->id;\n    }\n\n    return REDISMODULE_OK;\n}\n\n\n/* Authenticate the current context's user with the provided redis acl user. \n * Returns REDISMODULE_ERR if the user is disabled.\n * \n * See authenticateClientWithUser for information about callback, client_id,\n * and general usage for authentication. */\nint RM_AuthenticateClientWithUser(RedisModuleCtx *ctx, RedisModuleUser *module_user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) {\n    return authenticateClientWithUser(ctx, module_user->user, callback, privdata, client_id);\n}\n\n/* Authenticate the current context's user with the provided redis acl user. \n * Returns REDISMODULE_ERR if the user is disabled or the user does not exist.\n * \n * See authenticateClientWithUser for information about callback, client_id,\n * and general usage for authentication. */\nint RM_AuthenticateClientWithACLUser(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) {\n    user *acl_user = ACLGetUserByName(name, len);\n\n    if (!acl_user) {\n        return REDISMODULE_ERR;\n    }\n    return authenticateClientWithUser(ctx, acl_user, callback, privdata, client_id);\n}\n\n/* Deauthenticate and close the client. The client resources will not be\n * be immediately freed, but will be cleaned up in a background job. This is \n * the recommended way to deauthenicate a client since most clients can't \n * handle users becomming deauthenticated. Returns REDISMODULE_ERR when the \n * client doesn't exist and REDISMODULE_OK when the operation was successful. \n * \n * The client ID is returned from the RM_AuthenticateClientWithUser and\n * RM_AuthenticateClientWithACLUser APIs, but can be obtained through\n * the CLIENT api or through server events. \n * \n * This function is not thread safe, and must be executed within the context\n * of a command or thread safe context. */\nint RM_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id) {\n    UNUSED(ctx);\n    client *c = lookupClientByID(client_id);\n    if (c == NULL) return REDISMODULE_ERR;\n\n    /* Revoke also marks client to be closed ASAP */\n    revokeClientAuthentication(c);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Modules Dictionary API\n *\n * Implements a sorted dictionary (actually backed by a radix tree) with\n * the usual get / set / del / num-items API, together with an iterator\n * capable of going back and forth.\n * -------------------------------------------------------------------------- */\n\n/* Create a new dictionary. The 'ctx' pointer can be the current module context\n * or NULL, depending on what you want. Please follow the following rules:\n *\n * 1. Use a NULL context if you plan to retain a reference to this dictionary\n *    that will survive the time of the module callback where you created it.\n * 2. Use a NULL context if no context is available at the time you are creating\n *    the dictionary (of course...).\n * 3. However use the current callback context as 'ctx' argument if the\n *    dictionary time to live is just limited to the callback scope. In this\n *    case, if enabled, you can enjoy the automatic memory management that will\n *    reclaim the dictionary memory, as well as the strings returned by the\n *    Next / Prev dictionary iterator calls.\n */\nRedisModuleDict *RM_CreateDict(RedisModuleCtx *ctx) {\n    struct RedisModuleDict *d = zmalloc(sizeof(*d));\n    d->rax = raxNew();\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_DICT,d);\n    return d;\n}\n\n/* Free a dictionary created with RM_CreateDict(). You need to pass the\n * context pointer 'ctx' only if the dictionary was created using the\n * context instead of passing NULL. */\nvoid RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d) {\n    if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_DICT,d);\n    raxFree(d->rax);\n    zfree(d);\n}\n\n/* Return the size of the dictionary (number of keys). */\nuint64_t RM_DictSize(RedisModuleDict *d) {\n    return raxSize(d->rax);\n}\n\n/* Store the specified key into the dictionary, setting its value to the\n * pointer 'ptr'. If the key was added with success, since it did not\n * already exist, REDISMODULE_OK is returned. Otherwise if the key already\n * exists the function returns REDISMODULE_ERR. */\nint RM_DictSetC(RedisModuleDict *d, void *key, size_t keylen, void *ptr) {\n    int retval = raxTryInsert(d->rax,key,keylen,ptr,NULL);\n    return (retval == 1) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictSetC() but will replace the key with the new\n * value if the key already exists. */\nint RM_DictReplaceC(RedisModuleDict *d, void *key, size_t keylen, void *ptr) {\n    int retval = raxInsert(d->rax,key,keylen,ptr,NULL);\n    return (retval == 1) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictSetC() but takes the key as a RedisModuleString. */\nint RM_DictSet(RedisModuleDict *d, RedisModuleString *key, void *ptr) {\n    return RM_DictSetC(d,key->ptr,sdslen(key->ptr),ptr);\n}\n\n/* Like RedisModule_DictReplaceC() but takes the key as a RedisModuleString. */\nint RM_DictReplace(RedisModuleDict *d, RedisModuleString *key, void *ptr) {\n    return RM_DictReplaceC(d,key->ptr,sdslen(key->ptr),ptr);\n}\n\n/* Return the value stored at the specified key. The function returns NULL\n * both in the case the key does not exist, or if you actually stored\n * NULL at key. So, optionally, if the 'nokey' pointer is not NULL, it will\n * be set by reference to 1 if the key does not exist, or to 0 if the key\n * exists. */\nvoid *RM_DictGetC(RedisModuleDict *d, void *key, size_t keylen, int *nokey) {\n    void *res = raxFind(d->rax,key,keylen);\n    if (nokey) *nokey = (res == raxNotFound);\n    return (res == raxNotFound) ? NULL : res;\n}\n\n/* Like RedisModule_DictGetC() but takes the key as a RedisModuleString. */\nvoid *RM_DictGet(RedisModuleDict *d, RedisModuleString *key, int *nokey) {\n    return RM_DictGetC(d,key->ptr,sdslen(key->ptr),nokey);\n}\n\n/* Remove the specified key from the dictionary, returning REDISMODULE_OK if\n * the key was found and delted, or REDISMODULE_ERR if instead there was\n * no such key in the dictionary. When the operation is successful, if\n * 'oldval' is not NULL, then '*oldval' is set to the value stored at the\n * key before it was deleted. Using this feature it is possible to get\n * a pointer to the value (for instance in order to release it), without\n * having to call RedisModule_DictGet() before deleting the key. */\nint RM_DictDelC(RedisModuleDict *d, void *key, size_t keylen, void *oldval) {\n    int retval = raxRemove(d->rax,key,keylen,oldval);\n    return retval ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictDelC() but gets the key as a RedisModuleString. */\nint RM_DictDel(RedisModuleDict *d, RedisModuleString *key, void *oldval) {\n    return RM_DictDelC(d,key->ptr,sdslen(key->ptr),oldval);\n}\n\n/* Return an interator, setup in order to start iterating from the specified\n * key by applying the operator 'op', which is just a string specifying the\n * comparison operator to use in order to seek the first element. The\n * operators avalable are:\n *\n * \"^\"   -- Seek the first (lexicographically smaller) key.\n * \"$\"   -- Seek the last  (lexicographically biffer) key.\n * \">\"   -- Seek the first element greter than the specified key.\n * \">=\"  -- Seek the first element greater or equal than the specified key.\n * \"<\"   -- Seek the first element smaller than the specified key.\n * \"<=\"  -- Seek the first element smaller or equal than the specified key.\n * \"==\"  -- Seek the first element matching exactly the specified key.\n *\n * Note that for \"^\" and \"$\" the passed key is not used, and the user may\n * just pass NULL with a length of 0.\n *\n * If the element to start the iteration cannot be seeked based on the\n * key and operator passed, RedisModule_DictNext() / Prev() will just return\n * REDISMODULE_ERR at the first call, otherwise they'll produce elements.\n */\nRedisModuleDictIter *RM_DictIteratorStartC(RedisModuleDict *d, const char *op, void *key, size_t keylen) {\n    RedisModuleDictIter *di = zmalloc(sizeof(*di));\n    di->dict = d;\n    raxStart(&di->ri,d->rax);\n    raxSeek(&di->ri,op,key,keylen);\n    return di;\n}\n\n/* Exactly like RedisModule_DictIteratorStartC, but the key is passed as a\n * RedisModuleString. */\nRedisModuleDictIter *RM_DictIteratorStart(RedisModuleDict *d, const char *op, RedisModuleString *key) {\n    return RM_DictIteratorStartC(d,op,key->ptr,sdslen(key->ptr));\n}\n\n/* Release the iterator created with RedisModule_DictIteratorStart(). This call\n * is mandatory otherwise a memory leak is introduced in the module. */\nvoid RM_DictIteratorStop(RedisModuleDictIter *di) {\n    raxStop(&di->ri);\n    zfree(di);\n}\n\n/* After its creation with RedisModule_DictIteratorStart(), it is possible to\n * change the currently selected element of the iterator by using this\n * API call. The result based on the operator and key is exactly like\n * the function RedisModule_DictIteratorStart(), however in this case the\n * return value is just REDISMODULE_OK in case the seeked element was found,\n * or REDISMODULE_ERR in case it was not possible to seek the specified\n * element. It is possible to reseek an iterator as many times as you want. */\nint RM_DictIteratorReseekC(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) {\n    return raxSeek(&di->ri,op,key,keylen);\n}\n\n/* Like RedisModule_DictIteratorReseekC() but takes the key as as a\n * RedisModuleString. */\nint RM_DictIteratorReseek(RedisModuleDictIter *di, const char *op, RedisModuleString *key) {\n    return RM_DictIteratorReseekC(di,op,key->ptr,sdslen(key->ptr));\n}\n\n/* Return the current item of the dictionary iterator 'di' and steps to the\n * next element. If the iterator already yield the last element and there\n * are no other elements to return, NULL is returned, otherwise a pointer\n * to a string representing the key is provided, and the '*keylen' length\n * is set by reference (if keylen is not NULL). The '*dataptr', if not NULL\n * is set to the value of the pointer stored at the returned key as auxiliary\n * data (as set by the RedisModule_DictSet API).\n *\n * Usage example:\n *\n *      ... create the iterator here ...\n *      char *key;\n *      void *data;\n *      while((key = RedisModule_DictNextC(iter,&keylen,&data)) != NULL) {\n *          printf(\"%.*s %p\\n\", (int)keylen, key, data);\n *      }\n *\n * The returned pointer is of type void because sometimes it makes sense\n * to cast it to a char* sometimes to an unsigned char* depending on the\n * fact it contains or not binary data, so this API ends being more\n * comfortable to use.\n *\n * The validity of the returned pointer is until the next call to the\n * next/prev iterator step. Also the pointer is no longer valid once the\n * iterator is released. */\nvoid *RM_DictNextC(RedisModuleDictIter *di, size_t *keylen, void **dataptr) {\n    if (!raxNext(&di->ri)) return NULL;\n    if (keylen) *keylen = di->ri.key_len;\n    if (dataptr) *dataptr = di->ri.data;\n    return di->ri.key;\n}\n\n/* This function is exactly like RedisModule_DictNext() but after returning\n * the currently selected element in the iterator, it selects the previous\n * element (laxicographically smaller) instead of the next one. */\nvoid *RM_DictPrevC(RedisModuleDictIter *di, size_t *keylen, void **dataptr) {\n    if (!raxPrev(&di->ri)) return NULL;\n    if (keylen) *keylen = di->ri.key_len;\n    if (dataptr) *dataptr = di->ri.data;\n    return di->ri.key;\n}\n\n/* Like RedisModuleNextC(), but instead of returning an internally allocated\n * buffer and key length, it returns directly a module string object allocated\n * in the specified context 'ctx' (that may be NULL exactly like for the main\n * API RedisModule_CreateString).\n *\n * The returned string object should be deallocated after use, either manually\n * or by using a context that has automatic memory management active. */\nRedisModuleString *RM_DictNext(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) {\n    size_t keylen;\n    void *key = RM_DictNextC(di,&keylen,dataptr);\n    if (key == NULL) return NULL;\n    return RM_CreateString(ctx,key,keylen);\n}\n\n/* Like RedisModule_DictNext() but after returning the currently selected\n * element in the iterator, it selects the previous element (laxicographically\n * smaller) instead of the next one. */\nRedisModuleString *RM_DictPrev(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) {\n    size_t keylen;\n    void *key = RM_DictPrevC(di,&keylen,dataptr);\n    if (key == NULL) return NULL;\n    return RM_CreateString(ctx,key,keylen);\n}\n\n/* Compare the element currently pointed by the iterator to the specified\n * element given by key/keylen, according to the operator 'op' (the set of\n * valid operators are the same valid for RedisModule_DictIteratorStart).\n * If the comparision is successful the command returns REDISMODULE_OK\n * otherwise REDISMODULE_ERR is returned.\n *\n * This is useful when we want to just emit a lexicographical range, so\n * in the loop, as we iterate elements, we can also check if we are still\n * on range.\n *\n * The function returne REDISMODULE_ERR if the iterator reached the\n * end of elements condition as well. */\nint RM_DictCompareC(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) {\n    if (raxEOF(&di->ri)) return REDISMODULE_ERR;\n    int res = raxCompare(&di->ri,op,key,keylen);\n    return res ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictCompareC but gets the key to compare with the current\n * iterator key as a RedisModuleString. */\nint RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *key) {\n    if (raxEOF(&di->ri)) return REDISMODULE_ERR;\n    int res = raxCompare(&di->ri,op,key->ptr,sdslen(key->ptr));\n    return res ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n\n\n\n/* --------------------------------------------------------------------------\n * Modules Info fields\n * -------------------------------------------------------------------------- */\n\nint RM_InfoEndDictField(RedisModuleInfoCtx *ctx);\n\n/* Used to start a new section, before adding any fields. the section name will\n * be prefixed by \"<modulename>_\" and must only include A-Z,a-z,0-9.\n * NULL or empty string indicates the default section (only <modulename>) is used.\n * When return value is REDISMODULE_ERR, the section should and will be skipped. */\nint RM_InfoAddSection(RedisModuleInfoCtx *ctx, char *name) {\n    sds full_name = sdsdup(ctx->module->name);\n    if (name != NULL && strlen(name) > 0)\n        full_name = sdscatfmt(full_name, \"_%s\", name);\n\n    /* Implicitly end dicts, instead of returning an error which is likely un checked. */\n    if (ctx->in_dict_field)\n        RM_InfoEndDictField(ctx);\n\n    /* proceed only if:\n     * 1) no section was requested (emit all)\n     * 2) the module name was requested (emit all)\n     * 3) this specific section was requested. */\n    if (ctx->requested_section) {\n        if (strcasecmp(ctx->requested_section, full_name) &&\n            strcasecmp(ctx->requested_section, ctx->module->name)) {\n            sdsfree(full_name);\n            ctx->in_section = 0;\n            return REDISMODULE_ERR;\n        }\n    }\n    if (ctx->sections++) ctx->info = sdscat(ctx->info,\"\\r\\n\");\n    ctx->info = sdscatfmt(ctx->info, \"# %S\\r\\n\", full_name);\n    ctx->in_section = 1;\n    sdsfree(full_name);\n    return REDISMODULE_OK;\n}\n\n/* Starts a dict field, similar to the ones in INFO KEYSPACE. Use normal\n * RedisModule_InfoAddField* functions to add the items to this field, and\n * terminate with RedisModule_InfoEndDictField. */\nint RM_InfoBeginDictField(RedisModuleInfoCtx *ctx, char *name) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    /* Implicitly end dicts, instead of returning an error which is likely un checked. */\n    if (ctx->in_dict_field)\n        RM_InfoEndDictField(ctx);\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:\",\n        ctx->module->name,\n        name);\n    ctx->in_dict_field = 1;\n    return REDISMODULE_OK;\n}\n\n/* Ends a dict field, see RedisModule_InfoBeginDictField */\nint RM_InfoEndDictField(RedisModuleInfoCtx *ctx) {\n    if (!ctx->in_dict_field)\n        return REDISMODULE_ERR;\n    /* trim the last ',' if found. */\n    if (ctx->info[sdslen(ctx->info)-1]==',')\n        sdsIncrLen(ctx->info, -1);\n    ctx->info = sdscat(ctx->info, \"\\r\\n\");\n    ctx->in_dict_field = 0;\n    return REDISMODULE_OK;\n}\n\n/* Used by RedisModuleInfoFunc to add info fields.\n * Each field will be automatically prefixed by \"<modulename>_\".\n * Field names or values must not include \\r\\n of \":\" */\nint RM_InfoAddFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%S,\",\n            field,\n            (sds)value->ptr);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%S\\r\\n\",\n        ctx->module->name,\n        field,\n        (sds)value->ptr);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%s,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%s\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatprintf(ctx->info,\n            \"%s=%.17g,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatprintf(ctx->info,\n        \"%s_%s:%.17g\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%I,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%I\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%U,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%U\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) {\n    ctx->module->info_cb = cb;\n    return REDISMODULE_OK;\n}\n\nsds modulesCollectInfo(sds info, const char *section, int for_crash_report, int sections) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        if (!module->info_cb)\n            continue;\n        RedisModuleInfoCtx info_ctx = {module, section, info, sections, 0, 0};\n        module->info_cb(&info_ctx, for_crash_report);\n        /* Implicitly end dicts (no way to handle errors, and we must add the newline). */\n        if (info_ctx.in_dict_field)\n            RM_InfoEndDictField(&info_ctx);\n        info = info_ctx.info;\n        sections = info_ctx.sections;\n    }\n    dictReleaseIterator(di);\n    return info;\n}\n\n/* Get information about the server similar to the one that returns from the\n * INFO command. This function takes an optional 'section' argument that may\n * be NULL. The return value holds the output and can be used with\n * RedisModule_ServerInfoGetField and alike to get the individual fields.\n * When done, it needs to be freed with RedisModule_FreeServerInfo or with the\n * automatic memory management mechanism if enabled. */\nRedisModuleServerInfoData *RM_GetServerInfo(RedisModuleCtx *ctx, const char *section) {\n    struct RedisModuleServerInfoData *d = zmalloc(sizeof(*d));\n    d->rax = raxNew();\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_INFO,d);\n    sds info = genRedisInfoString(section);\n    int totlines, i;\n    sds *lines = sdssplitlen(info, sdslen(info), \"\\r\\n\", 2, &totlines);\n    for(i=0; i<totlines; i++) {\n        sds line = lines[i];\n        if (line[0]=='#') continue;\n        char *sep = strchr(line, ':');\n        if (!sep) continue;\n        unsigned char *key = (unsigned char*)line;\n        size_t keylen = (intptr_t)sep-(intptr_t)line;\n        sds val = sdsnewlen(sep+1,sdslen(line)-((intptr_t)sep-(intptr_t)line)-1);\n        if (!raxTryInsert(d->rax,key,keylen,val,NULL))\n            sdsfree(val);\n    }\n    sdsfree(info);\n    sdsfreesplitres(lines,totlines);\n    return d;\n}\n\n/* Free data created with RM_GetServerInfo(). You need to pass the\n * context pointer 'ctx' only if the dictionary was created using the\n * context instead of passing NULL. */\nvoid RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) {\n    if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_INFO,data);\n    raxIterator ri;\n    raxStart(&ri,data->rax);\n    while(1) {\n        raxSeek(&ri,\"^\",NULL,0);\n        if (!raxNext(&ri)) break;\n        raxRemove(data->rax,(unsigned char*)ri.key,ri.key_len,NULL);\n        sdsfree(ri.data);\n    }\n    raxStop(&ri);\n    raxFree(data->rax);\n    zfree(data);\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). You\n * need to pass the context pointer 'ctx' only if you want to use auto memory\n * mechanism to release the returned string. Return value will be NULL if the\n * field was not found. */\nRedisModuleString *RM_ServerInfoGetField(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) {\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) return NULL;\n    RedisModuleString *o = createStringObject(val,sdslen(val));\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n    return o;\n}\n\n/* Similar to RM_ServerInfoGetField, but returns a char* which should not be freed but the caller. */\nconst char *RM_ServerInfoGetFieldC(RedisModuleServerInfoData *data, const char* field) {\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) return NULL;\n    return val;\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). If the\n * field is not found, or is not numerical or out of range, return value will be\n * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */\nlong long RM_ServerInfoGetFieldSigned(RedisModuleServerInfoData *data, const char* field, int *out_err) {\n    long long ll;\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (!string2ll(val,sdslen(val),&ll)) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (out_err) *out_err = REDISMODULE_OK;\n    return ll;\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). If the\n * field is not found, or is not numerical or out of range, return value will be\n * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */\nunsigned long long RM_ServerInfoGetFieldUnsigned(RedisModuleServerInfoData *data, const char* field, int *out_err) {\n    unsigned long long ll;\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (!string2ull(val,&ll)) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (out_err) *out_err = REDISMODULE_OK;\n    return ll;\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). If the\n * field is not found, or is not a double, return value will be 0, and the\n * optional out_err argument will be set to REDISMODULE_ERR. */\ndouble RM_ServerInfoGetFieldDouble(RedisModuleServerInfoData *data, const char* field, int *out_err) {\n    double dbl;\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (!string2d(val,sdslen(val),&dbl)) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (out_err) *out_err = REDISMODULE_OK;\n    return dbl;\n}\n\n/* --------------------------------------------------------------------------\n * Modules utility APIs\n * -------------------------------------------------------------------------- */\n\n/* Return random bytes using SHA1 in counter mode with a /dev/urandom\n * initialized seed. This function is fast so can be used to generate\n * many bytes without any effect on the operating system entropy pool.\n * Currently this function is not thread safe. */\nvoid RM_GetRandomBytes(unsigned char *dst, size_t len) {\n    getRandomBytes(dst,len);\n}\n\n/* Like RedisModule_GetRandomBytes() but instead of setting the string to\n * random bytes the string is set to random characters in the in the\n * hex charset [0-9a-f]. */\nvoid RM_GetRandomHexChars(char *dst, size_t len) {\n    getRandomHexChars(dst,len);\n}\n\n/* --------------------------------------------------------------------------\n * Modules API exporting / importing\n * -------------------------------------------------------------------------- */\n\n/* This function is called by a module in order to export some API with a\n * given name. Other modules will be able to use this API by calling the\n * symmetrical function RM_GetSharedAPI() and casting the return value to\n * the right function pointer.\n *\n * The function will return REDISMODULE_OK if the name is not already taken,\n * otherwise REDISMODULE_ERR will be returned and no operation will be\n * performed.\n *\n * IMPORTANT: the apiname argument should be a string literal with static\n * lifetime. The API relies on the fact that it will always be valid in\n * the future. */\nint RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {\n    RedisModuleSharedAPI *sapi = zmalloc(sizeof(*sapi));\n    sapi->module = ctx->module;\n    sapi->func = func;\n    if (dictAdd(server.sharedapi, (char*)apiname, sapi) != DICT_OK) {\n        zfree(sapi);\n        return REDISMODULE_ERR;\n    }\n    return REDISMODULE_OK;\n}\n\n/* Request an exported API pointer. The return value is just a void pointer\n * that the caller of this function will be required to cast to the right\n * function pointer, so this is a private contract between modules.\n *\n * If the requested API is not available then NULL is returned. Because\n * modules can be loaded at different times with different order, this\n * function calls should be put inside some module generic API registering\n * step, that is called every time a module attempts to execute a\n * command that requires external APIs: if some API cannot be resolved, the\n * command should return an error.\n *\n * Here is an exmaple:\n *\n *     int ... myCommandImplementation() {\n *        if (getExternalAPIs() == 0) {\n *             reply with an error here if we cannot have the APIs\n *        }\n *        // Use the API:\n *        myFunctionPointer(foo);\n *     }\n *\n * And the function registerAPI() is:\n *\n *     int getExternalAPIs(void) {\n *         static int api_loaded = 0;\n *         if (api_loaded != 0) return 1; // APIs already resolved.\n *\n *         myFunctionPointer = RedisModule_GetOtherModuleAPI(\"...\");\n *         if (myFunctionPointer == NULL) return 0;\n *\n *         return 1;\n *     }\n */\nvoid *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) {\n    dictEntry *de = dictFind(server.sharedapi, apiname);\n    if (de == NULL) return NULL;\n    RedisModuleSharedAPI *sapi = dictGetVal(de);\n    if (listSearchKey(sapi->module->usedby,ctx->module) == NULL) {\n        listAddNodeTail(sapi->module->usedby,ctx->module);\n        listAddNodeTail(ctx->module->using,sapi->module);\n    }\n    return sapi->func;\n}\n\n/* Remove all the APIs registered by the specified module. Usually you\n * want this when the module is going to be unloaded. This function\n * assumes that's caller responsibility to make sure the APIs are not\n * used by other modules.\n *\n * The number of unregistered APIs is returned. */\nint moduleUnregisterSharedAPI(RedisModule *module) {\n    int count = 0;\n    dictIterator *di = dictGetSafeIterator(server.sharedapi);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        const char *apiname = dictGetKey(de);\n        RedisModuleSharedAPI *sapi = dictGetVal(de);\n        if (sapi->module == module) {\n            dictDelete(server.sharedapi,apiname);\n            zfree(sapi);\n            count++;\n        }\n    }\n    dictReleaseIterator(di);\n    return count;\n}\n\n/* Remove the specified module as an user of APIs of ever other module.\n * This is usually called when a module is unloaded.\n *\n * Returns the number of modules this module was using APIs from. */\nint moduleUnregisterUsedAPI(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    int count = 0;\n\n    listRewind(module->using,&li);\n    while((ln = listNext(&li))) {\n        RedisModule *used = ln->value;\n        listNode *ln = listSearchKey(used->usedby,module);\n        if (ln) {\n            listDelNode(used->usedby,ln);\n            count++;\n        }\n    }\n    return count;\n}\n\n/* Unregister all filters registered by a module.\n * This is called when a module is being unloaded.\n * \n * Returns the number of filters unregistered. */\nint moduleUnregisterFilters(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    int count = 0;\n\n    listRewind(module->filters,&li);\n    while((ln = listNext(&li))) {\n        RedisModuleCommandFilter *filter = ln->value;\n        listNode *ln = listSearchKey(moduleCommandFilters,filter);\n        if (ln) {\n            listDelNode(moduleCommandFilters,ln);\n            count++;\n        }\n        zfree(filter);\n    }\n    return count;\n}\n\n/* --------------------------------------------------------------------------\n * Module Command Filter API\n * -------------------------------------------------------------------------- */\n\n/* Register a new command filter function.\n *\n * Command filtering makes it possible for modules to extend Redis by plugging\n * into the execution flow of all commands.\n *\n * A registered filter gets called before Redis executes *any* command.  This\n * includes both core Redis commands and commands registered by any module.  The\n * filter applies in all execution paths including:\n *\n * 1. Invocation by a client.\n * 2. Invocation through `RedisModule_Call()` by any module.\n * 3. Invocation through Lua 'redis.call()`.\n * 4. Replication of a command from a master.\n *\n * The filter executes in a special filter context, which is different and more\n * limited than a RedisModuleCtx.  Because the filter affects any command, it\n * must be implemented in a very efficient way to reduce the performance impact\n * on Redis.  All Redis Module API calls that require a valid context (such as\n * `RedisModule_Call()`, `RedisModule_OpenKey()`, etc.) are not supported in a\n * filter context.\n *\n * The `RedisModuleCommandFilterCtx` can be used to inspect or modify the\n * executed command and its arguments.  As the filter executes before Redis\n * begins processing the command, any change will affect the way the command is\n * processed.  For example, a module can override Redis commands this way:\n *\n * 1. Register a `MODULE.SET` command which implements an extended version of\n *    the Redis `SET` command.\n * 2. Register a command filter which detects invocation of `SET` on a specific\n *    pattern of keys.  Once detected, the filter will replace the first\n *    argument from `SET` to `MODULE.SET`.\n * 3. When filter execution is complete, Redis considers the new command name\n *    and therefore executes the module's own command.\n *\n * Note that in the above use case, if `MODULE.SET` itself uses\n * `RedisModule_Call()` the filter will be applied on that call as well.  If\n * that is not desired, the `REDISMODULE_CMDFILTER_NOSELF` flag can be set when\n * registering the filter.\n *\n * The `REDISMODULE_CMDFILTER_NOSELF` flag prevents execution flows that\n * originate from the module's own `RM_Call()` from reaching the filter.  This\n * flag is effective for all execution flows, including nested ones, as long as\n * the execution begins from the module's command context or a thread-safe\n * context that is associated with a blocking command.\n *\n * Detached thread-safe contexts are *not* associated with the module and cannot\n * be protected by this flag.\n *\n * If multiple filters are registered (by the same or different modules), they\n * are executed in the order of registration.\n */\n\nRedisModuleCommandFilter *RM_RegisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback, int flags) {\n    RedisModuleCommandFilter *filter = zmalloc(sizeof(*filter));\n    filter->module = ctx->module;\n    filter->callback = callback;\n    filter->flags = flags;\n\n    listAddNodeTail(moduleCommandFilters, filter);\n    listAddNodeTail(ctx->module->filters, filter);\n    return filter;\n}\n\n/* Unregister a command filter.\n */\nint RM_UnregisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) {\n    listNode *ln;\n\n    /* A module can only remove its own filters */\n    if (filter->module != ctx->module) return REDISMODULE_ERR;\n\n    ln = listSearchKey(moduleCommandFilters,filter);\n    if (!ln) return REDISMODULE_ERR;\n    listDelNode(moduleCommandFilters,ln);\n\n    ln = listSearchKey(ctx->module->filters,filter);\n    if (!ln) return REDISMODULE_ERR;    /* Shouldn't happen */\n    listDelNode(ctx->module->filters,ln);\n\n    zfree(filter);\n\n    return REDISMODULE_OK;\n}\n\nvoid moduleCallCommandFilters(client *c) {\n    if (listLength(moduleCommandFilters) == 0) return;\n\n    listIter li;\n    listNode *ln;\n    listRewind(moduleCommandFilters,&li);\n\n    RedisModuleCommandFilterCtx filter = {\n        .argv = c->argv,\n        .argc = c->argc\n    };\n\n    while((ln = listNext(&li))) {\n        RedisModuleCommandFilter *f = ln->value;\n\n        /* Skip filter if REDISMODULE_CMDFILTER_NOSELF is set and module is\n         * currently processing a command.\n         */\n        if ((f->flags & REDISMODULE_CMDFILTER_NOSELF) && f->module->in_call) continue;\n\n        /* Call filter */\n        f->callback(&filter);\n    }\n\n    c->argv = filter.argv;\n    c->argc = filter.argc;\n}\n\n/* Return the number of arguments a filtered command has.  The number of\n * arguments include the command itself.\n */\nint RM_CommandFilterArgsCount(RedisModuleCommandFilterCtx *fctx)\n{\n    return fctx->argc;\n}\n\n/* Return the specified command argument.  The first argument (position 0) is\n * the command itself, and the rest are user-provided args.\n */\nconst RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *fctx, int pos)\n{\n    if (pos < 0 || pos >= fctx->argc) return NULL;\n    return fctx->argv[pos];\n}\n\n/* Modify the filtered command by inserting a new argument at the specified\n * position.  The specified RedisModuleString argument may be used by Redis\n * after the filter context is destroyed, so it must not be auto-memory\n * allocated, freed or used elsewhere.\n */\n\nint RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)\n{\n    int i;\n\n    if (pos < 0 || pos > fctx->argc) return REDISMODULE_ERR;\n\n    fctx->argv = zrealloc(fctx->argv, (fctx->argc+1)*sizeof(RedisModuleString *));\n    for (i = fctx->argc; i > pos; i--) {\n        fctx->argv[i] = fctx->argv[i-1];\n    }\n    fctx->argv[pos] = arg;\n    fctx->argc++;\n\n    return REDISMODULE_OK;\n}\n\n/* Modify the filtered command by replacing an existing argument with a new one.\n * The specified RedisModuleString argument may be used by Redis after the\n * filter context is destroyed, so it must not be auto-memory allocated, freed\n * or used elsewhere.\n */\n\nint RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)\n{\n    if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR;\n\n    decrRefCount(fctx->argv[pos]);\n    fctx->argv[pos] = arg;\n\n    return REDISMODULE_OK;\n}\n\n/* Modify the filtered command by deleting an argument at the specified\n * position.\n */\nint RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx, int pos)\n{\n    int i;\n    if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR;\n\n    decrRefCount(fctx->argv[pos]);\n    for (i = pos; i < fctx->argc-1; i++) {\n        fctx->argv[i] = fctx->argv[i+1];\n    }\n    fctx->argc--;\n\n    return REDISMODULE_OK;\n}\n\n/* For a given pointer allocated via RedisModule_Alloc() or\n * RedisModule_Realloc(), return the amount of memory allocated for it.\n * Note that this may be different (larger) than the memory we allocated\n * with the allocation calls, since sometimes the underlying allocator\n * will allocate more memory.\n */\nsize_t RM_MallocSize(void* ptr){\n    return zmalloc_size(ptr);\n}\n\n/* Return the a number between 0 to 1 indicating the amount of memory\n * currently used, relative to the Redis \"maxmemory\" configuration.\n *\n * 0 - No memory limit configured.\n * Between 0 and 1 - The percentage of the memory used normalized in 0-1 range.\n * Exactly 1 - Memory limit reached.\n * Greater 1 - More memory used than the configured limit.\n */\nfloat RM_GetUsedMemoryRatio(){\n    float level;\n    getMaxmemoryState(NULL, NULL, NULL, &level);\n    return level;\n}\n\n/* --------------------------------------------------------------------------\n * Scanning keyspace and hashes\n * -------------------------------------------------------------------------- */\n\ntypedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata);\ntypedef struct {\n    RedisModuleCtx *ctx;\n    void* user_data;\n    RedisModuleScanCB fn;\n} ScanCBData;\n\ntypedef struct RedisModuleScanCursor{\n    int cursor;\n    int done;\n}RedisModuleScanCursor;\n\nstatic void moduleScanCallback(void *privdata, const dictEntry *de) {\n    ScanCBData *data = privdata;\n    sds key = dictGetKey(de);\n    robj* val = dictGetVal(de);\n    RedisModuleString *keyname = createObject(OBJ_STRING,sdsdup(key));\n\n    /* Setup the key handle. */\n    RedisModuleKey kp = {0};\n    moduleInitKey(&kp, data->ctx, keyname, val, REDISMODULE_READ);\n\n    data->fn(data->ctx, keyname, &kp, data->user_data);\n\n    moduleCloseKey(&kp);\n    decrRefCount(keyname);\n}\n\n/* Create a new cursor to be used with RedisModule_Scan */\nRedisModuleScanCursor *RM_ScanCursorCreate() {\n    RedisModuleScanCursor* cursor = zmalloc(sizeof(*cursor));\n    cursor->cursor = 0;\n    cursor->done = 0;\n    return cursor;\n}\n\n/* Restart an existing cursor. The keys will be rescanned. */\nvoid RM_ScanCursorRestart(RedisModuleScanCursor *cursor) {\n    cursor->cursor = 0;\n    cursor->done = 0;\n}\n\n/* Destroy the cursor struct. */\nvoid RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) {\n    zfree(cursor);\n}\n\n/* Scan API that allows a module to scan all the keys and value in\n * the selected db.\n *\n * Callback for scan implementation.\n * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname,\n *                    RedisModuleKey *key, void *privdata);\n * ctx - the redis module context provided to for the scan.\n * keyname - owned by the caller and need to be retained if used after this\n * function.\n *\n * key - holds info on the key and value, it is provided as best effort, in\n * some cases it might be NULL, in which case the user should (can) use\n * RedisModule_OpenKey (and CloseKey too).\n * when it is provided, it is owned by the caller and will be free when the\n * callback returns.\n *\n * privdata - the user data provided to RedisModule_Scan.\n *\n * The way it should be used:\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      while(RedisModule_Scan(ctx, c, callback, privateData));\n *      RedisModule_ScanCursorDestroy(c);\n *\n * It is also possible to use this API from another thread while the lock\n * is acquired durring the actuall call to RM_Scan:\n *\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      RedisModule_ThreadSafeContextLock(ctx);\n *      while(RedisModule_Scan(ctx, c, callback, privateData)){\n *          RedisModule_ThreadSafeContextUnlock(ctx);\n *          // do some background job\n *          RedisModule_ThreadSafeContextLock(ctx);\n *      }\n *      RedisModule_ScanCursorDestroy(c);\n *\n * The function will return 1 if there are more elements to scan and\n * 0 otherwise, possibly setting errno if the call failed.\n *\n * It is also possible to restart and existing cursor using RM_CursorRestart.\n *\n * IMPORTANT: This API is very similar to the Redis SCAN command from the\n * point of view of the guarantees it provides. This means that the API\n * may report duplicated keys, but guarantees to report at least one time\n * every key that was there from the start to the end of the scanning process.\n *\n * NOTE: If you do database changes within the callback, you should be aware\n * that the internal state of the database may change. For instance it is safe\n * to delete or modify the current key, but may not be safe to delete any\n * other key.\n * Moreover playing with the Redis keyspace while iterating may have the\n * effect of returning more duplicates. A safe pattern is to store the keys\n * names you want to modify elsewhere, and perform the actions on the keys\n * later when the iteration is complete. Howerver this can cost a lot of\n * memory, so it may make sense to just operate on the current key when\n * possible during the iteration, given that this is safe. */\nint RM_Scan(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) {\n    if (cursor->done) {\n        errno = ENOENT;\n        return 0;\n    }\n    int ret = 1;\n    ScanCBData data = { ctx, privdata, fn };\n    cursor->cursor = dictScan(ctx->client->db->dict, cursor->cursor, moduleScanCallback, NULL, &data);\n    if (cursor->cursor == 0) {\n        cursor->done = 1;\n        ret = 0;\n    }\n    errno = 0;\n    return ret;\n}\n\ntypedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata);\ntypedef struct {\n    RedisModuleKey *key;\n    void* user_data;\n    RedisModuleScanKeyCB fn;\n} ScanKeyCBData;\n\nstatic void moduleScanKeyCallback(void *privdata, const dictEntry *de) {\n    ScanKeyCBData *data = privdata;\n    sds key = dictGetKey(de);\n    robj *o = data->key->value;\n    robj *field = createStringObject(key, sdslen(key));\n    robj *value = NULL;\n    if (o->type == OBJ_SET) {\n        value = NULL;\n    } else if (o->type == OBJ_HASH) {\n        sds val = dictGetVal(de);\n        value = createStringObject(val, sdslen(val));\n    } else if (o->type == OBJ_ZSET) {\n        double *val = (double*)dictGetVal(de);\n        value = createStringObjectFromLongDouble(*val, 0);\n    }\n\n    data->fn(data->key, field, value, data->user_data);\n    decrRefCount(field);\n    if (value) decrRefCount(value);\n}\n\n/* Scan api that allows a module to scan the elements in a hash, set or sorted set key\n *\n * Callback for scan implementation.\n * void scan_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata);\n * - key - the redis key context provided to for the scan.\n * - field - field name, owned by the caller and need to be retained if used\n *   after this function.\n * - value - value string or NULL for set type, owned by the caller and need to\n *   be retained if used after this function.\n * - privdata - the user data provided to RedisModule_ScanKey.\n *\n * The way it should be used:\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      RedisModuleKey *key = RedisModule_OpenKey(...)\n *      while(RedisModule_ScanKey(key, c, callback, privateData));\n *      RedisModule_CloseKey(key);\n *      RedisModule_ScanCursorDestroy(c);\n *\n * It is also possible to use this API from another thread while the lock is acquired durring\n * the actuall call to RM_Scan, and re-opening the key each time:\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      RedisModule_ThreadSafeContextLock(ctx);\n *      RedisModuleKey *key = RedisModule_OpenKey(...)\n *      while(RedisModule_ScanKey(ctx, c, callback, privateData)){\n *          RedisModule_CloseKey(key);\n *          RedisModule_ThreadSafeContextUnlock(ctx);\n *          // do some background job\n *          RedisModule_ThreadSafeContextLock(ctx);\n *          RedisModuleKey *key = RedisModule_OpenKey(...)\n *      }\n *      RedisModule_CloseKey(key);\n *      RedisModule_ScanCursorDestroy(c);\n *\n * The function will return 1 if there are more elements to scan and 0 otherwise,\n * possibly setting errno if the call failed.\n * It is also possible to restart and existing cursor using RM_CursorRestart.\n *\n * NOTE: Certain operations are unsafe while iterating the object. For instance\n * while the API guarantees to return at least one time all the elements that\n * are present in the data structure consistently from the start to the end\n * of the iteration (see HSCAN and similar commands documentation), the more\n * you play with the elements, the more duplicates you may get. In general\n * deleting the current element of the data structure is safe, while removing\n * the key you are iterating is not safe. */\nint RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) {\n    if (key == NULL || key->value == NULL) {\n        errno = EINVAL;\n        return 0;\n    }\n    dict *ht = NULL;\n    robj *o = key->value;\n    if (o->type == OBJ_SET) {\n        if (o->encoding == OBJ_ENCODING_HT)\n            ht = o->ptr;\n    } else if (o->type == OBJ_HASH) {\n        if (o->encoding == OBJ_ENCODING_HT)\n            ht = o->ptr;\n    } else if (o->type == OBJ_ZSET) {\n        if (o->encoding == OBJ_ENCODING_SKIPLIST)\n            ht = ((zset *)o->ptr)->dict;\n    } else {\n        errno = EINVAL;\n        return 0;\n    }\n    if (cursor->done) {\n        errno = ENOENT;\n        return 0;\n    }\n    int ret = 1;\n    if (ht) {\n        ScanKeyCBData data = { key, privdata, fn };\n        cursor->cursor = dictScan(ht, cursor->cursor, moduleScanKeyCallback, NULL, &data);\n        if (cursor->cursor == 0) {\n            cursor->done = 1;\n            ret = 0;\n        }\n    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_INTSET) {\n        int pos = 0;\n        int64_t ll;\n        while(intsetGet(o->ptr,pos++,&ll)) {\n            robj *field = createStringObjectFromLongLong(ll);\n            fn(key, field, NULL, privdata);\n            decrRefCount(field);\n        }\n        cursor->cursor = 1;\n        cursor->done = 1;\n        ret = 0;\n    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {\n        unsigned char *p = ziplistIndex(o->ptr,0);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vll;\n        while(p) {\n            ziplistGet(p,&vstr,&vlen,&vll);\n            robj *field = (vstr != NULL) ?\n                createStringObject((char*)vstr,vlen) :\n                createStringObjectFromLongLong(vll);\n            p = ziplistNext(o->ptr,p);\n            ziplistGet(p,&vstr,&vlen,&vll);\n            robj *value = (vstr != NULL) ?\n                createStringObject((char*)vstr,vlen) :\n                createStringObjectFromLongLong(vll);\n            fn(key, field, value, privdata);\n            p = ziplistNext(o->ptr,p);\n            decrRefCount(field);\n            decrRefCount(value);\n        }\n        cursor->cursor = 1;\n        cursor->done = 1;\n        ret = 0;\n    }\n    errno = 0;\n    return ret;\n}\n\n\n/* --------------------------------------------------------------------------\n * Module fork API\n * -------------------------------------------------------------------------- */\n\n/* Create a background child process with the current frozen snaphost of the\n * main process where you can do some processing in the background without\n * affecting / freezing the traffic and no need for threads and GIL locking.\n * Note that Redis allows for only one concurrent fork.\n * When the child wants to exit, it should call RedisModule_ExitFromChild.\n * If the parent wants to kill the child it should call RedisModule_KillForkChild\n * The done handler callback will be executed on the parent process when the\n * child existed (but not when killed)\n * Return: -1 on failure, on success the parent process will get a positive PID\n * of the child, and the child process will get 0.\n */\nint RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) {\n    pid_t childpid;\n    if (hasActiveChildProcess()) {\n        return -1;\n    }\n\n    openChildInfoPipe();\n    if ((childpid = redisFork()) == 0) {\n        /* Child */\n        redisSetProcTitle(\"redis-module-fork\");\n    } else if (childpid == -1) {\n        closeChildInfoPipe();\n        serverLog(LL_WARNING,\"Can't fork for module: %s\", strerror(errno));\n    } else {\n        /* Parent */\n        server.module_child_pid = childpid;\n        moduleForkInfo.done_handler = cb;\n        moduleForkInfo.done_handler_user_data = user_data;\n        serverLog(LL_VERBOSE, \"Module fork started pid: %d \", childpid);\n    }\n    return childpid;\n}\n\n/* Call from the child process when you want to terminate it.\n * retcode will be provided to the done handler executed on the parent process.\n */\nint RM_ExitFromChild(int retcode) {\n    sendChildCOWInfo(CHILD_INFO_TYPE_MODULE, \"Module fork\");\n    exitFromChild(retcode);\n    return REDISMODULE_OK;\n}\n\n/* Kill the active module forked child, if there is one active and the\n * pid matches, and returns C_OK. Otherwise if there is no active module\n * child or the pid does not match, return C_ERR without doing anything. */\nint TerminateModuleForkChild(int child_pid, int wait) {\n    /* Module child should be active and pid should match. */\n    if (server.module_child_pid == -1 ||\n        server.module_child_pid != child_pid) return C_ERR;\n\n    int statloc;\n    serverLog(LL_VERBOSE,\"Killing running module fork child: %ld\",\n        (long) server.module_child_pid);\n    if (kill(server.module_child_pid,SIGUSR1) != -1 && wait) {\n        while(wait4(server.module_child_pid,&statloc,0,NULL) !=\n              server.module_child_pid);\n    }\n    /* Reset the buffer accumulating changes while the child saves. */\n    server.module_child_pid = -1;\n    moduleForkInfo.done_handler = NULL;\n    moduleForkInfo.done_handler_user_data = NULL;\n    closeChildInfoPipe();\n    updateDictResizePolicy();\n    return C_OK;\n}\n\n/* Can be used to kill the forked child process from the parent process.\n * child_pid whould be the return value of RedisModule_Fork. */\nint RM_KillForkChild(int child_pid) {\n    /* Kill module child, wait for child exit. */\n    if (TerminateModuleForkChild(child_pid,1) == C_OK)\n        return REDISMODULE_OK;\n    else\n        return REDISMODULE_ERR;\n}\n\nvoid ModuleForkDoneHandler(int exitcode, int bysignal) {\n    serverLog(LL_NOTICE,\n        \"Module fork exited pid: %d, retcode: %d, bysignal: %d\",\n        server.module_child_pid, exitcode, bysignal);\n    if (moduleForkInfo.done_handler) {\n        moduleForkInfo.done_handler(exitcode, bysignal,\n            moduleForkInfo.done_handler_user_data);\n    }\n    server.module_child_pid = -1;\n    moduleForkInfo.done_handler = NULL;\n    moduleForkInfo.done_handler_user_data = NULL;\n}\n\n/* --------------------------------------------------------------------------\n * Server hooks implementation\n * -------------------------------------------------------------------------- */\n\n/* Register to be notified, via a callback, when the specified server event\n * happens. The callback is called with the event as argument, and an additional\n * argument which is a void pointer and should be cased to a specific type\n * that is event-specific (but many events will just use NULL since they do not\n * have additional information to pass to the callback).\n *\n * If the callback is NULL and there was a previous subscription, the module\n * will be unsubscribed. If there was a previous subscription and the callback\n * is not null, the old callback will be replaced with the new one.\n *\n * The callback must be of this type:\n *\n *  int (*RedisModuleEventCallback)(RedisModuleCtx *ctx,\n *                                  RedisModuleEvent eid,\n *                                  uint64_t subevent,\n *                                  void *data);\n *\n * The 'ctx' is a normal Redis module context that the callback can use in\n * order to call other modules APIs. The 'eid' is the event itself, this\n * is only useful in the case the module subscribed to multiple events: using\n * the 'id' field of this structure it is possible to check if the event\n * is one of the events we registered with this callback. The 'subevent' field\n * depends on the event that fired.\n *\n * Finally the 'data' pointer may be populated, only for certain events, with\n * more relevant data.\n *\n * Here is a list of events you can use as 'eid' and related sub events:\n *\n *      RedisModuleEvent_ReplicationRoleChanged\n *\n *          This event is called when the instance switches from master\n *          to replica or the other way around, however the event is\n *          also called when the replica remains a replica but starts to\n *          replicate with a different master.\n *\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER\n *              REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA\n *\n *          The 'data' field can be casted by the callback to a\n *          RedisModuleReplicationInfo structure with the following fields:\n *\n *              int master; // true if master, false if replica\n *              char *masterhost; // master instance hostname for NOW_REPLICA\n *              int masterport; // master instance port for NOW_REPLICA\n *              char *replid1; // Main replication ID\n *              char *replid2; // Secondary replication ID\n *              uint64_t repl1_offset; // Main replication offset\n *              uint64_t repl2_offset; // Offset of replid2 validity\n *\n *      RedisModuleEvent_Persistence\n *\n *          This event is called when RDB saving or AOF rewriting starts\n *          and ends. The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START\n *              REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START\n *              REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START\n *              REDISMODULE_SUBEVENT_PERSISTENCE_ENDED\n *              REDISMODULE_SUBEVENT_PERSISTENCE_FAILED\n *\n *          The above events are triggered not just when the user calls the\n *          relevant commands like BGSAVE, but also when a saving operation\n *          or AOF rewriting occurs because of internal server triggers.\n *          The SYNC_RDB_START sub events are happening in the forground due to\n *          SAVE command, FLUSHALL, or server shutdown, and the other RDB and\n *          AOF sub events are executed in a background fork child, so any\n *          action the module takes can only affect the generated AOF or RDB,\n *          but will not be reflected in the parent process and affect connected\n *          clients and commands. Also note that the AOF_START sub event may end\n *          up saving RDB content in case of an AOF with rdb-preamble.\n *\n *      RedisModuleEvent_FlushDB\n *\n *          The FLUSHALL, FLUSHDB or an internal flush (for instance\n *          because of replication, after the replica synchronization)\n *          happened. The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_FLUSHDB_START\n *              REDISMODULE_SUBEVENT_FLUSHDB_END\n *\n *          The data pointer can be casted to a RedisModuleFlushInfo\n *          structure with the following fields:\n *\n *              int32_t async;  // True if the flush is done in a thread.\n *                                 See for instance FLUSHALL ASYNC.\n *                                 In this case the END callback is invoked\n *                                 immediately after the database is put\n *                                 in the free list of the thread.\n *              int32_t dbnum;  // Flushed database number, -1 for all the DBs\n *                                 in the case of the FLUSHALL operation.\n *\n *          The start event is called *before* the operation is initated, thus\n *          allowing the callback to call DBSIZE or other operation on the\n *          yet-to-free keyspace.\n *\n *      RedisModuleEvent_Loading\n *\n *          Called on loading operations: at startup when the server is\n *          started, but also after a first synchronization when the\n *          replica is loading the RDB file from the master.\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_LOADING_RDB_START\n *              REDISMODULE_SUBEVENT_LOADING_AOF_START\n *              REDISMODULE_SUBEVENT_LOADING_REPL_START\n *              REDISMODULE_SUBEVENT_LOADING_ENDED\n *              REDISMODULE_SUBEVENT_LOADING_FAILED\n *\n *          Note that AOF loading may start with an RDB data in case of\n *          rdb-preamble, in which case you'll only recieve an AOF_START event.\n *\n *\n *      RedisModuleEvent_ClientChange\n *\n *          Called when a client connects or disconnects.\n *          The data pointer can be casted to a RedisModuleClientInfo\n *          structure, documented in RedisModule_GetClientInfoById().\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED\n *              REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED\n *\n *      RedisModuleEvent_Shutdown\n *\n *          The server is shutting down. No subevents are available.\n *\n *  RedisModuleEvent_ReplicaChange\n *\n *          This event is called when the instance (that can be both a\n *          master or a replica) get a new online replica, or lose a\n *          replica since it gets disconnected.\n *          The following sub events are availble:\n *\n *              REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE\n *              REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE\n *\n *          No additional information is available so far: future versions\n *          of Redis will have an API in order to enumerate the replicas\n *          connected and their state.\n *\n *  RedisModuleEvent_CronLoop\n *\n *          This event is called every time Redis calls the serverCron()\n *          function in order to do certain bookkeeping. Modules that are\n *          required to do operations from time to time may use this callback.\n *          Normally Redis calls this function 10 times per second, but\n *          this changes depending on the \"hz\" configuration.\n *          No sub events are available.\n *\n *          The data pointer can be casted to a RedisModuleCronLoop\n *          structure with the following fields:\n *\n *              int32_t hz;  // Approximate number of events per second.\n *\n *  RedisModuleEvent_MasterLinkChange\n *\n *          This is called for replicas in order to notify when the\n *          replication link becomes functional (up) with our master,\n *          or when it goes down. Note that the link is not considered\n *          up when we just connected to the master, but only if the\n *          replication is happening correctly.\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_MASTER_LINK_UP\n *              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN\n *\n *  RedisModuleEvent_ModuleChange\n *\n *          This event is called when a new module is loaded or one is unloaded.\n *          The following sub events are availble:\n *\n *              REDISMODULE_SUBEVENT_MODULE_LOADED\n *              REDISMODULE_SUBEVENT_MODULE_UNLOADED\n *\n *          The data pointer can be casted to a RedisModuleModuleChange\n *          structure with the following fields:\n *\n *              const char* module_name;  // Name of module loaded or unloaded.\n *              int32_t module_version;  // Module version.\n *\n *  RedisModuleEvent_LoadingProgress\n *\n *          This event is called repeatedly called while an RDB or AOF file\n *          is being loaded.\n *          The following sub events are availble:\n *\n *              REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB\n *              REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF\n *\n *          The data pointer can be casted to a RedisModuleLoadingProgress\n *          structure with the following fields:\n *\n *              int32_t hz;  // Approximate number of events per second.\n *              int32_t progress;  // Approximate progress between 0 and 1024,\n *                                    or -1 if unknown.\n *\n * The function returns REDISMODULE_OK if the module was successfully subscrived\n * for the specified event. If the API is called from a wrong context then\n * REDISMODULE_ERR is returned. */\nint RM_SubscribeToServerEvent(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) {\n    RedisModuleEventListener *el;\n\n    /* Protect in case of calls from contexts without a module reference. */\n    if (ctx->module == NULL) return REDISMODULE_ERR;\n\n    /* Search an event matching this module and event ID. */\n    listIter li;\n    listNode *ln;\n    listRewind(RedisModule_EventListeners,&li);\n    while((ln = listNext(&li))) {\n        el = ln->value;\n        if (el->module == ctx->module && el->event.id == event.id)\n            break; /* Matching event found. */\n    }\n\n    /* Modify or remove the event listener if we already had one. */\n    if (ln) {\n        if (callback == NULL) {\n            listDelNode(RedisModule_EventListeners,ln);\n            zfree(el);\n        } else {\n            el->callback = callback; /* Update the callback with the new one. */\n        }\n        return REDISMODULE_OK;\n    }\n\n    /* No event found, we need to add a new one. */\n    el = zmalloc(sizeof(*el));\n    el->module = ctx->module;\n    el->event = event;\n    el->callback = callback;\n    listAddNodeTail(RedisModule_EventListeners,el);\n    return REDISMODULE_OK;\n}\n\n/* This is called by the Redis internals every time we want to fire an\n * event that can be interceppted by some module. The pointer 'data' is useful\n * in order to populate the event-specific structure when needed, in order\n * to return the structure with more information to the callback.\n *\n * 'eid' and 'subid' are just the main event ID and the sub event associated\n * with the event, depending on what exactly happened. */\nvoid moduleFireServerEvent(uint64_t eid, int subid, void *data) {\n    /* Fast path to return ASAP if there is nothing to do, avoiding to\n     * setup the iterator and so forth: we want this call to be extremely\n     * cheap if there are no registered modules. */\n    if (listLength(RedisModule_EventListeners) == 0) return;\n\n    listIter li;\n    listNode *ln;\n    listRewind(RedisModule_EventListeners,&li);\n    while((ln = listNext(&li))) {\n        RedisModuleEventListener *el = ln->value;\n        if (el->event.id == eid) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.module = el->module;\n\n            if (ModulesInHooks == 0) {\n                ctx.client = moduleFreeContextReusedClient;\n            } else {\n                ctx.client = createClient(NULL);\n                ctx.client->flags |= CLIENT_MODULE;\n                ctx.client->user = NULL; /* Root user. */\n            }\n\n            void *moduledata = NULL;\n            RedisModuleClientInfoV1 civ1;\n            RedisModuleReplicationInfoV1 riv1;\n            RedisModuleModuleChangeV1 mcv1;\n            /* Start at DB zero by default when calling the handler. It's\n             * up to the specific event setup to change it when it makes\n             * sense. For instance for FLUSHDB events we select the correct\n             * DB automatically. */\n            selectDb(ctx.client, 0);\n\n            /* Event specific context and data pointer setup. */\n            if (eid == REDISMODULE_EVENT_CLIENT_CHANGE) {\n                modulePopulateClientInfoStructure(&civ1,data,\n                                                  el->event.dataver);\n                moduledata = &civ1;\n            } else if (eid == REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED) {\n                modulePopulateReplicationInfoStructure(&riv1,el->event.dataver);\n                moduledata = &riv1;\n            } else if (eid == REDISMODULE_EVENT_FLUSHDB) {\n                moduledata = data;\n                RedisModuleFlushInfoV1 *fi = data;\n                if (fi->dbnum != -1)\n                    selectDb(ctx.client, fi->dbnum);\n            } else if (eid == REDISMODULE_EVENT_MODULE_CHANGE) {\n                RedisModule *m = data;\n                if (m == el->module)\n                    continue;\n                mcv1.version = REDISMODULE_MODULE_CHANGE_VERSION;\n                mcv1.module_name = m->name;\n                mcv1.module_version = m->ver;\n                moduledata = &mcv1;\n            } else if (eid == REDISMODULE_EVENT_LOADING_PROGRESS) {\n                moduledata = data;\n            } else if (eid == REDISMODULE_EVENT_CRON_LOOP) {\n                moduledata = data;\n            }\n\n            ModulesInHooks++;\n            el->module->in_hook++;\n            el->callback(&ctx,el->event,subid,moduledata);\n            el->module->in_hook--;\n            ModulesInHooks--;\n\n            if (ModulesInHooks != 0) freeClient(ctx.client);\n            moduleFreeContext(&ctx);\n        }\n    }\n}\n\n/* Remove all the listeners for this module: this is used before unloading\n * a module. */\nvoid moduleUnsubscribeAllServerEvents(RedisModule *module) {\n    RedisModuleEventListener *el;\n    listIter li;\n    listNode *ln;\n    listRewind(RedisModule_EventListeners,&li);\n\n    while((ln = listNext(&li))) {\n        el = ln->value;\n        if (el->module == module) {\n            listDelNode(RedisModule_EventListeners,ln);\n            zfree(el);\n        }\n    }\n}\n\nvoid processModuleLoadingProgressEvent(int is_aof) {\n    long long now = ustime();\n    static long long next_event = 0;\n    if (now >= next_event) {\n        /* Fire the loading progress modules end event. */\n        int progress = -1;\n        if (server.loading_total_bytes)\n            progress = (server.loading_total_bytes<<10) / server.loading_total_bytes;\n        RedisModuleFlushInfoV1 fi = {REDISMODULE_LOADING_PROGRESS_VERSION,\n                                     server.hz,\n                                     progress};\n        moduleFireServerEvent(REDISMODULE_EVENT_LOADING_PROGRESS,\n                              is_aof?\n                                REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF:\n                                REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB,\n                              &fi);\n        /* decide when the next event should fire. */\n        next_event = now + 1000000 / server.hz;\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Modules API internals\n * -------------------------------------------------------------------------- */\n\n/* server.moduleapi dictionary type. Only uses plain C strings since\n * this gets queries from modules. */\n\nuint64_t dictCStringKeyHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, strlen((char*)key));\n}\n\nint dictCStringKeyCompare(void *privdata, const void *key1, const void *key2) {\n    UNUSED(privdata);\n    return strcmp(key1,key2) == 0;\n}\n\ndictType moduleAPIDictType = {\n    dictCStringKeyHash,        /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictCStringKeyCompare,     /* key compare */\n    NULL,                      /* key destructor */\n    NULL                       /* val destructor */\n};\n\nint moduleRegisterApi(const char *funcname, void *funcptr) {\n    return dictAdd(server.moduleapi, (char*)funcname, funcptr);\n}\n\n#define REGISTER_API(name) \\\n    moduleRegisterApi(\"RedisModule_\" #name, (void *)(unsigned long)RM_ ## name)\n\n/* Global initialization at Redis startup. */\nvoid moduleRegisterCoreAPI(void);\n\nvoid moduleInitModulesSystem(void) {\n    moduleUnblockedClients = listCreate();\n    server.loadmodule_queue = listCreate();\n    modules = dictCreate(&modulesDictType,NULL);\n\n    /* Set up the keyspace notification susbscriber list and static client */\n    moduleKeyspaceSubscribers = listCreate();\n    moduleFreeContextReusedClient = createClient(NULL);\n    moduleFreeContextReusedClient->flags |= CLIENT_MODULE;\n    moduleFreeContextReusedClient->user = NULL; /* root user. */\n\n    /* Set up filter list */\n    moduleCommandFilters = listCreate();\n\n    moduleRegisterCoreAPI();\n    if (pipe(server.module_blocked_pipe) == -1) {\n        serverLog(LL_WARNING,\n            \"Can't create the pipe for module blocking commands: %s\",\n            strerror(errno));\n        exit(1);\n    }\n    /* Make the pipe non blocking. This is just a best effort aware mechanism\n     * and we do not want to block not in the read nor in the write half. */\n    anetNonBlock(NULL,server.module_blocked_pipe[0]);\n    anetNonBlock(NULL,server.module_blocked_pipe[1]);\n\n    /* Create the timers radix tree. */\n    Timers = raxNew();\n\n    /* Setup the event listeners data structures. */\n    RedisModule_EventListeners = listCreate();\n\n    /* Our thread-safe contexts GIL must start with already locked:\n     * it is just unlocked when it's safe. */\n    pthread_mutex_lock(&moduleGIL);\n}\n\n/* Load all the modules in the server.loadmodule_queue list, which is\n * populated by `loadmodule` directives in the configuration file.\n * We can't load modules directly when processing the configuration file\n * because the server must be fully initialized before loading modules.\n *\n * The function aborts the server on errors, since to start with missing\n * modules is not considered sane: clients may rely on the existence of\n * given commands, loading AOF also may need some modules to exist, and\n * if this instance is a slave, it must understand commands from master. */\nvoid moduleLoadFromQueue(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(server.loadmodule_queue,&li);\n    while((ln = listNext(&li))) {\n        struct moduleLoadQueueEntry *loadmod = ln->value;\n        if (moduleLoad(loadmod->path,(void **)loadmod->argv,loadmod->argc)\n            == C_ERR)\n        {\n            serverLog(LL_WARNING,\n                \"Can't load module from %s: server aborting\",\n                loadmod->path);\n            exit(1);\n        }\n    }\n}\n\nvoid moduleFreeModuleStructure(struct RedisModule *module) {\n    listRelease(module->types);\n    listRelease(module->filters);\n    listRelease(module->usedby);\n    listRelease(module->using);\n    sdsfree(module->name);\n    zfree(module);\n}\n\nvoid moduleUnregisterCommands(struct RedisModule *module) {\n    /* Unregister all the commands registered by this module. */\n    dictIterator *di = dictGetSafeIterator(server.commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        if (cmd->proc == RedisModuleCommandDispatcher) {\n            RedisModuleCommandProxy *cp =\n                (void*)(unsigned long)cmd->getkeys_proc;\n            sds cmdname = cp->rediscmd->name;\n            if (cp->module == module) {\n                dictDelete(server.commands,cmdname);\n                dictDelete(server.orig_commands,cmdname);\n                sdsfree(cmdname);\n                zfree(cp->rediscmd);\n                zfree(cp);\n            }\n        }\n    }\n    dictReleaseIterator(di);\n}\n\n/* Load a module and initialize it. On success C_OK is returned, otherwise\n * C_ERR is returned. */\nint moduleLoad(const char *path, void **module_argv, int module_argc) {\n    int (*onload)(void *, void **, int);\n    void *handle;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n    ctx.client = moduleFreeContextReusedClient;\n    selectDb(ctx.client, 0);\n\n    struct stat st;\n    if (stat(path, &st) == 0)\n    {   // this check is best effort\n        if (!(st.st_mode & (S_IXUSR  | S_IXGRP | S_IXOTH))) {\n            serverLog(LL_WARNING, \"Module %s failed to load: It does not have execute permissions.\", path);\n            return C_ERR;\n        }\n    }\n\n    handle = dlopen(path,RTLD_NOW|RTLD_LOCAL);\n    if (handle == NULL) {\n        serverLog(LL_WARNING, \"Module %s failed to load: %s\", path, dlerror());\n        return C_ERR;\n    }\n    onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,\"RedisModule_OnLoad\");\n    if (onload == NULL) {\n        dlclose(handle);\n        serverLog(LL_WARNING,\n            \"Module %s does not export RedisModule_OnLoad() \"\n            \"symbol. Module not loaded.\",path);\n        return C_ERR;\n    }\n    if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) {\n        if (ctx.module) {\n            moduleUnregisterCommands(ctx.module);\n            moduleUnregisterSharedAPI(ctx.module);\n            moduleUnregisterUsedAPI(ctx.module);\n            moduleFreeModuleStructure(ctx.module);\n        }\n        dlclose(handle);\n        serverLog(LL_WARNING,\n            \"Module %s initialization failed. Module not loaded\",path);\n        return C_ERR;\n    }\n\n    /* Redis module loaded! Register it. */\n    dictAdd(modules,ctx.module->name,ctx.module);\n    ctx.module->blocked_clients = 0;\n    ctx.module->handle = handle;\n    serverLog(LL_NOTICE,\"Module '%s' loaded from %s\",ctx.module->name,path);\n    /* Fire the loaded modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_MODULE_CHANGE,\n                          REDISMODULE_SUBEVENT_MODULE_LOADED,\n                          ctx.module);\n\n    moduleFreeContext(&ctx);\n    return C_OK;\n}\n\n\n/* Unload the module registered with the specified name. On success\n * C_OK is returned, otherwise C_ERR is returned and errno is set\n * to the following values depending on the type of error:\n *\n * * ENONET: No such module having the specified name.\n * * EBUSY: The module exports a new data type and can only be reloaded. */\nint moduleUnload(sds name) {\n    struct RedisModule *module = dictFetchValue(modules,name);\n\n    if (module == NULL) {\n        errno = ENOENT;\n        return REDISMODULE_ERR;\n    } else if (listLength(module->types)) {\n        errno = EBUSY;\n        return REDISMODULE_ERR;\n    } else if (listLength(module->usedby)) {\n        errno = EPERM;\n        return REDISMODULE_ERR;\n    } else if (module->blocked_clients) {\n        errno = EAGAIN;\n        return REDISMODULE_ERR;\n    }\n\n    /* Give module a chance to clean up. */\n    int (*onunload)(void *);\n    onunload = (int (*)(void *))(unsigned long) dlsym(module->handle, \"RedisModule_OnUnload\");\n    if (onunload) {\n        RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n        ctx.module = module;\n        ctx.client = moduleFreeContextReusedClient;\n        int unload_status = onunload((void*)&ctx);\n        moduleFreeContext(&ctx);\n\n        if (unload_status == REDISMODULE_ERR) {\n            serverLog(LL_WARNING, \"Module %s OnUnload failed.  Unload canceled.\", name);\n            errno = ECANCELED;\n            return REDISMODULE_ERR;\n        }\n    }\n\n    moduleFreeAuthenticatedClients(module);\n    moduleUnregisterCommands(module);\n    moduleUnregisterSharedAPI(module);\n    moduleUnregisterUsedAPI(module);\n    moduleUnregisterFilters(module);\n\n    /* Remove any notification subscribers this module might have */\n    moduleUnsubscribeNotifications(module);\n    moduleUnsubscribeAllServerEvents(module);\n\n    /* Unload the dynamic library. */\n    if (dlclose(module->handle) == -1) {\n        char *error = dlerror();\n        if (error == NULL) error = \"Unknown error\";\n        serverLog(LL_WARNING,\"Error when trying to close the %s module: %s\",\n            module->name, error);\n    }\n\n    /* Fire the unloaded modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_MODULE_CHANGE,\n                          REDISMODULE_SUBEVENT_MODULE_UNLOADED,\n                          module);\n\n    /* Remove from list of modules. */\n    serverLog(LL_NOTICE,\"Module %s unloaded\",module->name);\n    dictDelete(modules,module->name);\n    module->name = NULL; /* The name was already freed by dictDelete(). */\n    moduleFreeModuleStructure(module);\n\n    return REDISMODULE_OK;\n}\n\n/* Helper function for the MODULE and HELLO command: send the list of the\n * loaded modules to the client. */\nvoid addReplyLoadedModules(client *c) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    addReplyArrayLen(c,dictSize(modules));\n    while ((de = dictNext(di)) != NULL) {\n        sds name = dictGetKey(de);\n        struct RedisModule *module = dictGetVal(de);\n        addReplyMapLen(c,2);\n        addReplyBulkCString(c,\"name\");\n        addReplyBulkCBuffer(c,name,sdslen(name));\n        addReplyBulkCString(c,\"ver\");\n        addReplyLongLong(c,module->ver);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Helper for genModulesInfoString(): given a list of modules, return\n * am SDS string in the form \"[modulename|modulename2|...]\" */\nsds genModulesInfoStringRenderModulesList(list *l) {\n    listIter li;\n    listNode *ln;\n    listRewind(l,&li);\n    sds output = sdsnew(\"[\");\n    while((ln = listNext(&li))) {\n        RedisModule *module = ln->value;\n        output = sdscat(output,module->name);\n    }\n    output = sdstrim(output,\"|\");\n    output = sdscat(output,\"]\");\n    return output;\n}\n\n/* Helper for genModulesInfoString(): render module options as an SDS string. */\nsds genModulesInfoStringRenderModuleOptions(struct RedisModule *module) {\n    sds output = sdsnew(\"[\");\n    if (module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS)\n        output = sdscat(output,\"handle-io-errors|\");\n    output = sdstrim(output,\"|\");\n    output = sdscat(output,\"]\");\n    return output;\n}\n\n\n/* Helper function for the INFO command: adds loaded modules as to info's\n * output.\n *\n * After the call, the passed sds info string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds genModulesInfoString(sds info) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        sds name = dictGetKey(de);\n        struct RedisModule *module = dictGetVal(de);\n\n        sds usedby = genModulesInfoStringRenderModulesList(module->usedby);\n        sds using = genModulesInfoStringRenderModulesList(module->using);\n        sds options = genModulesInfoStringRenderModuleOptions(module);\n        info = sdscatfmt(info,\n            \"module:name=%S,ver=%i,api=%i,filters=%i,\"\n            \"usedby=%S,using=%S,options=%S\\r\\n\",\n                name, module->ver, module->apiver,\n                (int)listLength(module->filters), usedby, using, options);\n        sdsfree(usedby);\n        sdsfree(using);\n        sdsfree(options);\n    }\n    dictReleaseIterator(di);\n    return info;\n}\n\n/* Redis MODULE command.\n *\n * MODULE LOAD <path> [args...] */\nvoid moduleCommand(client *c) {\n    char *subcmd = c->argv[1]->ptr;\n    if (c->argc == 2 && !strcasecmp(subcmd,\"help\")) {\n        const char *help[] = {\n\"LIST -- Return a list of loaded modules.\",\n\"LOAD <path> [arg ...] -- Load a module library from <path>.\",\n\"UNLOAD <name> -- Unload a module.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else\n    if (!strcasecmp(subcmd,\"load\") && c->argc >= 3) {\n        robj **argv = NULL;\n        int argc = 0;\n\n        if (c->argc > 3) {\n            argc = c->argc - 3;\n            argv = &c->argv[3];\n        }\n\n        if (moduleLoad(c->argv[2]->ptr,(void **)argv,argc) == C_OK)\n            addReply(c,shared.ok);\n        else\n            addReplyError(c,\n                \"Error loading the extension. Please check the server logs.\");\n    } else if (!strcasecmp(subcmd,\"unload\") && c->argc == 3) {\n        if (moduleUnload(c->argv[2]->ptr) == C_OK)\n            addReply(c,shared.ok);\n        else {\n            char *errmsg;\n            switch(errno) {\n            case ENOENT:\n                errmsg = \"no such module with that name\";\n                break;\n            case EBUSY:\n                errmsg = \"the module exports one or more module-side data \"\n                         \"types, can't unload\";\n                break;\n            case EPERM:\n                errmsg = \"the module exports APIs used by other modules. \"\n                         \"Please unload them first and try again\";\n                break;\n            case EAGAIN:\n                errmsg = \"the module has blocked clients. \"\n                         \"Please wait them unblocked and try again\";\n                break;\n            default:\n                errmsg = \"operation not possible.\";\n                break;\n            }\n            addReplyErrorFormat(c,\"Error unloading module: %s\",errmsg);\n        }\n    } else if (!strcasecmp(subcmd,\"list\") && c->argc == 2) {\n        addReplyLoadedModules(c);\n    } else {\n        addReplySubcommandSyntaxError(c);\n        return;\n    }\n}\n\n/* Return the number of registered modules. */\nsize_t moduleCount(void) {\n    return dictSize(modules);\n}\n\n/* Set the key last access time for LRU based eviction. not relevent if the\n * servers's maxmemory policy is LFU based. Value is idle time in milliseconds.\n * returns REDISMODULE_OK if the LRU was updated, REDISMODULE_ERR otherwise. */\nint RM_SetLRU(RedisModuleKey *key, mstime_t lru_idle) {\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (objectSetLRUOrLFU(key->value, -1, lru_idle, lru_idle>=0 ? LRU_CLOCK() : 0, 1))\n        return REDISMODULE_OK;\n    return REDISMODULE_ERR;\n}\n\n/* Gets the key last access time.\n * Value is idletime in milliseconds or -1 if the server's eviction policy is\n * LFU based.\n * returns REDISMODULE_OK if when key is valid. */\nint RM_GetLRU(RedisModuleKey *key, mstime_t *lru_idle) {\n    *lru_idle = -1;\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU)\n        return REDISMODULE_OK;\n    *lru_idle = estimateObjectIdleTime(key->value);\n    return REDISMODULE_OK;\n}\n\n/* Set the key access frequency. only relevant if the server's maxmemory policy\n * is LFU based.\n * The frequency is a logarithmic counter that provides an indication of\n * the access frequencyonly (must be <= 255).\n * returns REDISMODULE_OK if the LFU was updated, REDISMODULE_ERR otherwise. */\nint RM_SetLFU(RedisModuleKey *key, long long lfu_freq) {\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (objectSetLRUOrLFU(key->value, lfu_freq, -1, 0, 1))\n        return REDISMODULE_OK;\n    return REDISMODULE_ERR;\n}\n\n/* Gets the key access frequency or -1 if the server's eviction policy is not\n * LFU based.\n * returns REDISMODULE_OK if when key is valid. */\nint RM_GetLFU(RedisModuleKey *key, long long *lfu_freq) {\n    *lfu_freq = -1;\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU)\n        *lfu_freq = LFUDecrAndReturn(key->value);\n    return REDISMODULE_OK;\n}\n\n/* Replace the value assigned to a module type.\n *\n * The key must be open for writing, have an existing value, and have a moduleType\n * that matches the one specified by the caller.\n *\n * Unlike RM_ModuleTypeSetValue() which will free the old value, this function\n * simply swaps the old value with the new value.\n *\n * The function returns REDISMODULE_OK on success, REDISMODULE_ERR on errors\n * such as:\n *\n * 1. Key is not opened for writing.\n * 2. Key is not a module data type key.\n * 3. Key is a module datatype other than 'mt'.\n *\n * If old_value is non-NULL, the old value is returned by reference.\n */\nint RM_ModuleTypeReplaceValue(RedisModuleKey *key, moduleType *mt, void *new_value, void **old_value) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->iter)\n        return REDISMODULE_ERR;\n    if (!key->value || key->value->type != OBJ_MODULE)\n        return REDISMODULE_ERR;\n\n    moduleValue *mv = key->value->ptr;\n    if (mv->type != mt)\n        return REDISMODULE_ERR;\n\n    if (old_value)\n        *old_value = mv->value;\n    mv->value = new_value;\n\n    return REDISMODULE_OK;\n}\n\n/* Register all the APIs we export. Keep this function at the end of the\n * file so that's easy to seek it to add new entries. */\nvoid moduleRegisterCoreAPI(void) {\n    server.moduleapi = dictCreate(&moduleAPIDictType,NULL);\n    server.sharedapi = dictCreate(&moduleAPIDictType,NULL);\n    REGISTER_API(Alloc);\n    REGISTER_API(Calloc);\n    REGISTER_API(Realloc);\n    REGISTER_API(Free);\n    REGISTER_API(Strdup);\n    REGISTER_API(CreateCommand);\n    REGISTER_API(SetModuleAttribs);\n    REGISTER_API(IsModuleNameBusy);\n    REGISTER_API(WrongArity);\n    REGISTER_API(ReplyWithLongLong);\n    REGISTER_API(ReplyWithError);\n    REGISTER_API(ReplyWithSimpleString);\n    REGISTER_API(ReplyWithArray);\n    REGISTER_API(ReplyWithNullArray);\n    REGISTER_API(ReplyWithEmptyArray);\n    REGISTER_API(ReplySetArrayLength);\n    REGISTER_API(ReplyWithString);\n    REGISTER_API(ReplyWithEmptyString);\n    REGISTER_API(ReplyWithVerbatimString);\n    REGISTER_API(ReplyWithStringBuffer);\n    REGISTER_API(ReplyWithCString);\n    REGISTER_API(ReplyWithNull);\n    REGISTER_API(ReplyWithCallReply);\n    REGISTER_API(ReplyWithDouble);\n    REGISTER_API(ReplyWithLongDouble);\n    REGISTER_API(GetSelectedDb);\n    REGISTER_API(SelectDb);\n    REGISTER_API(OpenKey);\n    REGISTER_API(CloseKey);\n    REGISTER_API(KeyType);\n    REGISTER_API(ValueLength);\n    REGISTER_API(ListPush);\n    REGISTER_API(ListPop);\n    REGISTER_API(StringToLongLong);\n    REGISTER_API(StringToDouble);\n    REGISTER_API(StringToLongDouble);\n    REGISTER_API(Call);\n    REGISTER_API(CallReplyProto);\n    REGISTER_API(FreeCallReply);\n    REGISTER_API(CallReplyInteger);\n    REGISTER_API(CallReplyType);\n    REGISTER_API(CallReplyLength);\n    REGISTER_API(CallReplyArrayElement);\n    REGISTER_API(CallReplyStringPtr);\n    REGISTER_API(CreateStringFromCallReply);\n    REGISTER_API(CreateString);\n    REGISTER_API(CreateStringFromLongLong);\n    REGISTER_API(CreateStringFromDouble);\n    REGISTER_API(CreateStringFromLongDouble);\n    REGISTER_API(CreateStringFromString);\n    REGISTER_API(CreateStringPrintf);\n    REGISTER_API(FreeString);\n    REGISTER_API(StringPtrLen);\n    REGISTER_API(AutoMemory);\n    REGISTER_API(Replicate);\n    REGISTER_API(ReplicateVerbatim);\n    REGISTER_API(DeleteKey);\n    REGISTER_API(UnlinkKey);\n    REGISTER_API(StringSet);\n    REGISTER_API(StringDMA);\n    REGISTER_API(StringTruncate);\n    REGISTER_API(SetExpire);\n    REGISTER_API(GetExpire);\n    REGISTER_API(ResetDataset);\n    REGISTER_API(DbSize);\n    REGISTER_API(RandomKey);\n    REGISTER_API(ZsetAdd);\n    REGISTER_API(ZsetIncrby);\n    REGISTER_API(ZsetScore);\n    REGISTER_API(ZsetRem);\n    REGISTER_API(ZsetRangeStop);\n    REGISTER_API(ZsetFirstInScoreRange);\n    REGISTER_API(ZsetLastInScoreRange);\n    REGISTER_API(ZsetFirstInLexRange);\n    REGISTER_API(ZsetLastInLexRange);\n    REGISTER_API(ZsetRangeCurrentElement);\n    REGISTER_API(ZsetRangeNext);\n    REGISTER_API(ZsetRangePrev);\n    REGISTER_API(ZsetRangeEndReached);\n    REGISTER_API(HashSet);\n    REGISTER_API(HashGet);\n    REGISTER_API(IsKeysPositionRequest);\n    REGISTER_API(KeyAtPos);\n    REGISTER_API(GetClientId);\n    REGISTER_API(GetContextFlags);\n    REGISTER_API(AvoidReplicaTraffic);\n    REGISTER_API(PoolAlloc);\n    REGISTER_API(CreateDataType);\n    REGISTER_API(ModuleTypeSetValue);\n    REGISTER_API(ModuleTypeReplaceValue);\n    REGISTER_API(ModuleTypeGetType);\n    REGISTER_API(ModuleTypeGetValue);\n    REGISTER_API(IsIOError);\n    REGISTER_API(SetModuleOptions);\n    REGISTER_API(SignalModifiedKey);\n    REGISTER_API(SaveUnsigned);\n    REGISTER_API(LoadUnsigned);\n    REGISTER_API(SaveSigned);\n    REGISTER_API(LoadSigned);\n    REGISTER_API(SaveString);\n    REGISTER_API(SaveStringBuffer);\n    REGISTER_API(LoadString);\n    REGISTER_API(LoadStringBuffer);\n    REGISTER_API(SaveDouble);\n    REGISTER_API(LoadDouble);\n    REGISTER_API(SaveFloat);\n    REGISTER_API(LoadFloat);\n    REGISTER_API(SaveLongDouble);\n    REGISTER_API(LoadLongDouble);\n    REGISTER_API(SaveDataTypeToString);\n    REGISTER_API(LoadDataTypeFromString);\n    REGISTER_API(EmitAOF);\n    REGISTER_API(Log);\n    REGISTER_API(LogIOError);\n    REGISTER_API(_Assert);\n    REGISTER_API(LatencyAddSample);\n    REGISTER_API(StringAppendBuffer);\n    REGISTER_API(RetainString);\n    REGISTER_API(StringCompare);\n    REGISTER_API(GetContextFromIO);\n    REGISTER_API(GetKeyNameFromIO);\n    REGISTER_API(GetKeyNameFromModuleKey);\n    REGISTER_API(BlockClient);\n    REGISTER_API(UnblockClient);\n    REGISTER_API(IsBlockedReplyRequest);\n    REGISTER_API(IsBlockedTimeoutRequest);\n    REGISTER_API(GetBlockedClientPrivateData);\n    REGISTER_API(AbortBlock);\n    REGISTER_API(Milliseconds);\n    REGISTER_API(GetThreadSafeContext);\n    REGISTER_API(FreeThreadSafeContext);\n    REGISTER_API(ThreadSafeContextLock);\n    REGISTER_API(ThreadSafeContextUnlock);\n    REGISTER_API(DigestAddStringBuffer);\n    REGISTER_API(DigestAddLongLong);\n    REGISTER_API(DigestEndSequence);\n    REGISTER_API(NotifyKeyspaceEvent);\n    REGISTER_API(GetNotifyKeyspaceEvents);\n    REGISTER_API(SubscribeToKeyspaceEvents);\n    REGISTER_API(RegisterClusterMessageReceiver);\n    REGISTER_API(SendClusterMessage);\n    REGISTER_API(GetClusterNodeInfo);\n    REGISTER_API(GetClusterNodesList);\n    REGISTER_API(FreeClusterNodesList);\n    REGISTER_API(CreateTimer);\n    REGISTER_API(StopTimer);\n    REGISTER_API(GetTimerInfo);\n    REGISTER_API(GetMyClusterID);\n    REGISTER_API(GetClusterSize);\n    REGISTER_API(GetRandomBytes);\n    REGISTER_API(GetRandomHexChars);\n    REGISTER_API(BlockedClientDisconnected);\n    REGISTER_API(SetDisconnectCallback);\n    REGISTER_API(GetBlockedClientHandle);\n    REGISTER_API(SetClusterFlags);\n    REGISTER_API(CreateDict);\n    REGISTER_API(FreeDict);\n    REGISTER_API(DictSize);\n    REGISTER_API(DictSetC);\n    REGISTER_API(DictReplaceC);\n    REGISTER_API(DictSet);\n    REGISTER_API(DictReplace);\n    REGISTER_API(DictGetC);\n    REGISTER_API(DictGet);\n    REGISTER_API(DictDelC);\n    REGISTER_API(DictDel);\n    REGISTER_API(DictIteratorStartC);\n    REGISTER_API(DictIteratorStart);\n    REGISTER_API(DictIteratorStop);\n    REGISTER_API(DictIteratorReseekC);\n    REGISTER_API(DictIteratorReseek);\n    REGISTER_API(DictNextC);\n    REGISTER_API(DictPrevC);\n    REGISTER_API(DictNext);\n    REGISTER_API(DictPrev);\n    REGISTER_API(DictCompareC);\n    REGISTER_API(DictCompare);\n    REGISTER_API(ExportSharedAPI);\n    REGISTER_API(GetSharedAPI);\n    REGISTER_API(RegisterCommandFilter);\n    REGISTER_API(UnregisterCommandFilter);\n    REGISTER_API(CommandFilterArgsCount);\n    REGISTER_API(CommandFilterArgGet);\n    REGISTER_API(CommandFilterArgInsert);\n    REGISTER_API(CommandFilterArgReplace);\n    REGISTER_API(CommandFilterArgDelete);\n    REGISTER_API(Fork);\n    REGISTER_API(ExitFromChild);\n    REGISTER_API(KillForkChild);\n    REGISTER_API(RegisterInfoFunc);\n    REGISTER_API(InfoAddSection);\n    REGISTER_API(InfoBeginDictField);\n    REGISTER_API(InfoEndDictField);\n    REGISTER_API(InfoAddFieldString);\n    REGISTER_API(InfoAddFieldCString);\n    REGISTER_API(InfoAddFieldDouble);\n    REGISTER_API(InfoAddFieldLongLong);\n    REGISTER_API(InfoAddFieldULongLong);\n    REGISTER_API(GetServerInfo);\n    REGISTER_API(FreeServerInfo);\n    REGISTER_API(ServerInfoGetField);\n    REGISTER_API(ServerInfoGetFieldC);\n    REGISTER_API(ServerInfoGetFieldSigned);\n    REGISTER_API(ServerInfoGetFieldUnsigned);\n    REGISTER_API(ServerInfoGetFieldDouble);\n    REGISTER_API(GetClientInfoById);\n    REGISTER_API(PublishMessage);\n    REGISTER_API(SubscribeToServerEvent);\n    REGISTER_API(SetLRU);\n    REGISTER_API(GetLRU);\n    REGISTER_API(SetLFU);\n    REGISTER_API(GetLFU);\n    REGISTER_API(BlockClientOnKeys);\n    REGISTER_API(SignalKeyAsReady);\n    REGISTER_API(GetBlockedClientReadyKey);\n    REGISTER_API(GetUsedMemoryRatio);\n    REGISTER_API(MallocSize);\n    REGISTER_API(ScanCursorCreate);\n    REGISTER_API(ScanCursorDestroy);\n    REGISTER_API(ScanCursorRestart);\n    REGISTER_API(Scan);\n    REGISTER_API(ScanKey);\n    REGISTER_API(CreateModuleUser);\n    REGISTER_API(SetModuleUserACL);\n    REGISTER_API(FreeModuleUser);\n    REGISTER_API(DeauthenticateAndCloseClient);\n    REGISTER_API(AuthenticateClientWithACLUser);\n    REGISTER_API(AuthenticateClientWithUser);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/.gitignore",
    "content": "*.so\n*.xo\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/Makefile",
    "content": "\n# find the OS\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\n# Compile flags for linux / osx\nifeq ($(uname_S),Linux)\n\tSHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -shared\nelse\n\tSHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup\nendif\n\n.SUFFIXES: .c .so .xo .o\n\nall: helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so hellohook.so helloacl.so\n\n.c.xo:\n\t$(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@\n\nhelloworld.xo: ../redismodule.h\n\nhelloworld.so: helloworld.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellotype.xo: ../redismodule.h\n\nhellotype.so: hellotype.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhelloblock.xo: ../redismodule.h\n\nhelloblock.so: helloblock.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lpthread -lc\n\nhellocluster.xo: ../redismodule.h\n\nhellocluster.so: hellocluster.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellotimer.xo: ../redismodule.h\n\nhellotimer.so: hellotimer.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellodict.xo: ../redismodule.h\n\nhellodict.so: hellodict.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellohook.xo: ../redismodule.h\n\nhellohook.so: hellohook.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhelloacl.xo: ../redismodule.h\n\nhelloacl.so: helloacl.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\ntestmodule.xo: ../redismodule.h\n\ntestmodule.so: testmodule.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nclean:\n\trm -rf *.xo *.so\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/gendoc.rb",
    "content": "# gendoc.rb -- Converts the top-comments inside module.c to modules API\n#              reference documentation in markdown format.\n\n# Convert the C comment to markdown\ndef markdown(s)\n    s = s.gsub(/\\*\\/$/,\"\")\n    s = s.gsub(/^ \\* {0,1}/,\"\")\n    s = s.gsub(/^\\/\\* /,\"\")\n    s.chop! while s[-1] == \"\\n\" || s[-1] == \" \"\n    lines = s.split(\"\\n\")\n    newlines = []\n    lines.each{|l|\n        if l[0] != ' '\n            l = l.gsub(/RM_[A-z()]+/){|x| \"`#{x}`\"}\n            l = l.gsub(/RedisModule_[A-z()]+/){|x| \"`#{x}`\"}\n            l = l.gsub(/REDISMODULE_[A-z]+/){|x| \"`#{x}`\"}\n        end\n        newlines << l\n    }\n    return newlines.join(\"\\n\")\nend\n\n# Given the source code array and the index at which an exported symbol was\n# detected, extracts and outputs the documentation.\ndef docufy(src,i)\n    m = /RM_[A-z0-9]+/.match(src[i])\n    name = m[0]\n    name = name.sub(\"RM_\",\"RedisModule_\")\n    proto = src[i].sub(\"{\",\"\").strip+\";\\n\"\n    proto = proto.sub(\"RM_\",\"RedisModule_\")\n    puts \"## `#{name}`\\n\\n\"\n    puts \"    #{proto}\\n\"\n    comment = \"\"\n    while true\n        i = i-1\n        comment = src[i]+comment\n        break if src[i] =~ /\\/\\*/\n    end\n    comment = markdown(comment)\n    puts comment+\"\\n\\n\"\nend\n\nputs \"# Modules API reference\\n\\n\"\nsrc = File.open(\"../module.c\").to_a\nsrc.each_with_index{|line,i|\n    if line =~ /RM_/ && line[0] != ' ' && line[0] != '#' && line[0] != '/'\n        if src[i-1] =~ /\\*\\//\n            docufy(src,i)\n        end\n    end\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/helloacl.c",
    "content": "/* ACL API example - An example for performing custom synchronous and\n * asynchronous password authentication.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright 2019 Amazon.com, Inc. or its affiliates.\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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <pthread.h>\n#include <unistd.h>\n\n// A simple global user\nstatic RedisModuleUser *global;\nstatic uint64_t global_auth_client_id = 0;\n\n/* HELLOACL.REVOKE \n * Synchronously revoke access from a user. */\nint RevokeCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (global_auth_client_id) {\n        RedisModule_DeauthenticateAndCloseClient(ctx, global_auth_client_id);\n        return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n    } else {\n        return RedisModule_ReplyWithError(ctx, \"Global user currently not used\");    \n    }\n}\n\n/* HELLOACL.RESET \n * Synchronously delete and re-create a module user. */\nint ResetCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_FreeModuleUser(global);\n    global = RedisModule_CreateModuleUser(\"global\");\n    RedisModule_SetModuleUserACL(global, \"allcommands\");\n    RedisModule_SetModuleUserACL(global, \"allkeys\");\n    RedisModule_SetModuleUserACL(global, \"on\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* Callback handler for user changes, use this to notify a module of \n * changes to users authenticated by the module */\nvoid HelloACL_UserChanged(uint64_t client_id, void *privdata) {\n    REDISMODULE_NOT_USED(privdata);\n    REDISMODULE_NOT_USED(client_id);\n    global_auth_client_id = 0;\n}\n\n/* HELLOACL.AUTHGLOBAL \n * Synchronously assigns a module user to the current context. */\nint AuthGlobalCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (global_auth_client_id) {\n        return RedisModule_ReplyWithError(ctx, \"Global user currently used\");    \n    }\n\n    RedisModule_AuthenticateClientWithUser(ctx, global, HelloACL_UserChanged, NULL, &global_auth_client_id);\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n#define TIMEOUT_TIME 1000\n\n/* Reply callback for auth command HELLOACL.AUTHASYNC */\nint HelloACL_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    size_t length;\n\n    RedisModuleString *user_string = RedisModule_GetBlockedClientPrivateData(ctx);\n    const char *name = RedisModule_StringPtrLen(user_string, &length);\n\n    if (RedisModule_AuthenticateClientWithACLUser(ctx, name, length, NULL, NULL, NULL) == \n            REDISMODULE_ERR) {\n        return RedisModule_ReplyWithError(ctx, \"Invalid Username or password\");    \n    }\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* Timeout callback for auth command HELLOACL.AUTHASYNC */\nint HelloACL_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\n/* Private data frees data for HELLOACL.AUTHASYNC command. */\nvoid HelloACL_FreeData(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_FreeString(NULL, privdata);\n}\n\n/* Background authentication can happen here. */\nvoid *HelloACL_ThreadMain(void *args) {\n    void **targs = args;\n    RedisModuleBlockedClient *bc = targs[0];\n    RedisModuleString *user = targs[1];\n    RedisModule_Free(targs);\n\n    RedisModule_UnblockClient(bc,user);\n    return NULL;\n}\n\n/* HELLOACL.AUTHASYNC \n * Asynchronously assigns an ACL user to the current context. */\nint AuthAsyncCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    pthread_t tid;\n    RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, HelloACL_Reply, HelloACL_Timeout, HelloACL_FreeData, TIMEOUT_TIME);\n    \n\n    void **targs = RedisModule_Alloc(sizeof(void*)*2);\n    targs[0] = bc;\n    targs[1] = RedisModule_CreateStringFromString(NULL, argv[1]);\n\n    if (pthread_create(&tid, NULL, HelloACL_ThreadMain, targs) != 0) {\n        RedisModule_AbortBlock(bc);\n        return RedisModule_ReplyWithError(ctx, \"-ERR Can't start thread\");\n    }\n\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"helloacl\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.reset\",\n        ResetCommand_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.revoke\",\n        RevokeCommand_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.authglobal\",\n        AuthGlobalCommand_RedisCommand,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.authasync\",\n        AuthAsyncCommand_RedisCommand,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    global = RedisModule_CreateModuleUser(\"global\");\n    RedisModule_SetModuleUserACL(global, \"allcommands\");\n    RedisModule_SetModuleUserACL(global, \"allkeys\");\n    RedisModule_SetModuleUserACL(global, \"on\");\n\n    global_auth_client_id = 0;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/helloblock.c",
    "content": "/* Helloblock module -- An example of blocking command implementation\n * with threads.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <pthread.h>\n#include <unistd.h>\n\n/* Reply callback for blocking command HELLO.BLOCK */\nint HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    int *myint = RedisModule_GetBlockedClientPrivateData(ctx);\n    return RedisModule_ReplyWithLongLong(ctx,*myint);\n}\n\n/* Timeout callback for blocking command HELLO.BLOCK */\nint HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx,\"Request timedout\");\n}\n\n/* Private data freeing callback for HELLO.BLOCK command. */\nvoid HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_Free(privdata);\n}\n\n/* The thread entry point that actually executes the blocking part\n * of the command HELLO.BLOCK. */\nvoid *HelloBlock_ThreadMain(void *arg) {\n    void **targ = arg;\n    RedisModuleBlockedClient *bc = targ[0];\n    long long delay = (unsigned long)targ[1];\n    RedisModule_Free(targ);\n\n    sleep(delay);\n    int *r = RedisModule_Alloc(sizeof(int));\n    *r = rand();\n    RedisModule_UnblockClient(bc,r);\n    return NULL;\n}\n\n/* An example blocked client disconnection callback.\n *\n * Note that in the case of the HELLO.BLOCK command, the blocked client is now\n * owned by the thread calling sleep(). In this specific case, there is not\n * much we can do, however normally we could instead implement a way to\n * signal the thread that the client disconnected, and sleep the specified\n * amount of seconds with a while loop calling sleep(1), so that once we\n * detect the client disconnection, we can terminate the thread ASAP. */\nvoid HelloBlock_Disconnected(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc) {\n    RedisModule_Log(ctx,\"warning\",\"Blocked client %p disconnected!\",\n        (void*)bc);\n\n    /* Here you should cleanup your state / threads, and if possible\n     * call RedisModule_UnblockClient(), or notify the thread that will\n     * call the function ASAP. */\n}\n\n/* HELLO.BLOCK <delay> <timeout> -- Block for <count> seconds, then reply with\n * a random number. Timeout is the command timeout, so that you can test\n * what happens when the delay is greater than the timeout. */\nint HelloBlock_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n    long long delay;\n    long long timeout;\n\n    if (RedisModule_StringToLongLong(argv[1],&delay) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    pthread_t tid;\n    RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout);\n\n    /* Here we set a disconnection handler, however since this module will\n     * block in sleep() in a thread, there is not much we can do in the\n     * callback, so this is just to show you the API. */\n    RedisModule_SetDisconnectCallback(bc,HelloBlock_Disconnected);\n\n    /* Now that we setup a blocking client, we need to pass the control\n     * to the thread. However we need to pass arguments to the thread:\n     * the delay and a reference to the blocked client handle. */\n    void **targ = RedisModule_Alloc(sizeof(void*)*2);\n    targ[0] = bc;\n    targ[1] = (void*)(unsigned long) delay;\n\n    if (pthread_create(&tid,NULL,HelloBlock_ThreadMain,targ) != 0) {\n        RedisModule_AbortBlock(bc);\n        return RedisModule_ReplyWithError(ctx,\"-ERR Can't start thread\");\n    }\n    return REDISMODULE_OK;\n}\n\n/* The thread entry point that actually executes the blocking part\n * of the command HELLO.KEYS.\n *\n * Note: this implementation is very simple on purpose, so no duplicated\n * keys (returned by SCAN) are filtered. However adding such a functionality\n * would be trivial just using any data structure implementing a dictionary\n * in order to filter the duplicated items. */\nvoid *HelloKeys_ThreadMain(void *arg) {\n    RedisModuleBlockedClient *bc = arg;\n    RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bc);\n    long long cursor = 0;\n    size_t replylen = 0;\n\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    do {\n        RedisModule_ThreadSafeContextLock(ctx);\n        RedisModuleCallReply *reply = RedisModule_Call(ctx,\n            \"SCAN\",\"l\",(long long)cursor);\n        RedisModule_ThreadSafeContextUnlock(ctx);\n\n        RedisModuleCallReply *cr_cursor =\n            RedisModule_CallReplyArrayElement(reply,0);\n        RedisModuleCallReply *cr_keys =\n            RedisModule_CallReplyArrayElement(reply,1);\n\n        RedisModuleString *s = RedisModule_CreateStringFromCallReply(cr_cursor);\n        RedisModule_StringToLongLong(s,&cursor);\n        RedisModule_FreeString(ctx,s);\n\n        size_t items = RedisModule_CallReplyLength(cr_keys);\n        for (size_t j = 0; j < items; j++) {\n            RedisModuleCallReply *ele =\n                RedisModule_CallReplyArrayElement(cr_keys,j);\n            RedisModule_ReplyWithCallReply(ctx,ele);\n            replylen++;\n        }\n        RedisModule_FreeCallReply(reply);\n    } while (cursor != 0);\n    RedisModule_ReplySetArrayLength(ctx,replylen);\n\n    RedisModule_FreeThreadSafeContext(ctx);\n    RedisModule_UnblockClient(bc,NULL);\n    return NULL;\n}\n\n/* HELLO.KEYS -- Return all the keys in the current database without blocking\n * the server. The keys do not represent a point-in-time state so only the keys\n * that were in the database from the start to the end are guaranteed to be\n * there. */\nint HelloKeys_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    if (argc != 1) return RedisModule_WrongArity(ctx);\n\n    pthread_t tid;\n\n    /* Note that when blocking the client we do not set any callback: no\n     * timeout is possible since we passed '0', nor we need a reply callback\n     * because we'll use the thread safe context to accumulate a reply. */\n    RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,NULL,NULL,NULL,0);\n\n    /* Now that we setup a blocking client, we need to pass the control\n     * to the thread. However we need to pass arguments to the thread:\n     * the reference to the blocked client handle. */\n    if (pthread_create(&tid,NULL,HelloKeys_ThreadMain,bc) != 0) {\n        RedisModule_AbortBlock(bc);\n        return RedisModule_ReplyWithError(ctx,\"-ERR Can't start thread\");\n    }\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"helloblock\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.block\",\n        HelloBlock_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"hello.keys\",\n        HelloKeys_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/hellocluster.c",
    "content": "/* Helloworld cluster -- A ping/pong cluster API example.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2018, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n#define MSGTYPE_PING 1\n#define MSGTYPE_PONG 2\n\n/* HELLOCLUSTER.PINGALL */\nint PingallCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_SendClusterMessage(ctx,NULL,MSGTYPE_PING,(unsigned char*)\"Hey\",3);\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* HELLOCLUSTER.LIST */\nint ListCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    size_t numnodes;\n    char **ids = RedisModule_GetClusterNodesList(ctx,&numnodes);\n    if (ids == NULL) {\n        return RedisModule_ReplyWithError(ctx,\"Cluster not enabled\");\n    }\n\n    RedisModule_ReplyWithArray(ctx,numnodes);\n    for (size_t j = 0; j < numnodes; j++) {\n        int port;\n        RedisModule_GetClusterNodeInfo(ctx,ids[j],NULL,NULL,&port,NULL);\n        RedisModule_ReplyWithArray(ctx,2);\n        RedisModule_ReplyWithStringBuffer(ctx,ids[j],REDISMODULE_NODE_ID_LEN);\n        RedisModule_ReplyWithLongLong(ctx,port);\n    }\n    RedisModule_FreeClusterNodesList(ids);\n    return REDISMODULE_OK;\n}\n\n/* Callback for message MSGTYPE_PING */\nvoid PingReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {\n    RedisModule_Log(ctx,\"notice\",\"PING (type %d) RECEIVED from %.*s: '%.*s'\",\n        type,REDISMODULE_NODE_ID_LEN,sender_id,(int)len, payload);\n    RedisModule_SendClusterMessage(ctx,NULL,MSGTYPE_PONG,(unsigned char*)\"Ohi!\",4);\n    RedisModule_Call(ctx, \"INCR\", \"c\", \"pings_received\");\n}\n\n/* Callback for message MSGTYPE_PONG. */\nvoid PongReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {\n    RedisModule_Log(ctx,\"notice\",\"PONG (type %d) RECEIVED from %.*s: '%.*s'\",\n        type,REDISMODULE_NODE_ID_LEN,sender_id,(int)len, payload);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellocluster\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellocluster.pingall\",\n        PingallCommand_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellocluster.list\",\n        ListCommand_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    /* Disable Redis Cluster sharding and redirections. This way every node\n     * will be able to access every possible key, regardless of the hash slot.\n     * This way the PING message handler will be able to increment a specific\n     * variable. Normally you do that in order for the distributed system\n     * you create as a module to have total freedom in the keyspace\n     * manipulation. */\n    RedisModule_SetClusterFlags(ctx,REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION);\n\n    /* Register our handlers for different message types. */\n    RedisModule_RegisterClusterMessageReceiver(ctx,MSGTYPE_PING,PingReceiver);\n    RedisModule_RegisterClusterMessageReceiver(ctx,MSGTYPE_PONG,PongReceiver);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/hellodict.c",
    "content": "/* Hellodict -- An example of modules dictionary API\n *\n * This module implements a volatile key-value store on top of the\n * dictionary exported by the Redis modules API.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2018, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\nstatic RedisModuleDict *Keyspace;\n\n/* HELLODICT.SET <key> <value>\n *\n * Set the specified key to the specified value. */\nint cmd_SET(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n    RedisModule_DictSet(Keyspace,argv[1],argv[2]);\n    /* We need to keep a reference to the value stored at the key, otherwise\n     * it would be freed when this callback returns. */\n    RedisModule_RetainString(NULL,argv[2]);\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* HELLODICT.GET <key>\n *\n * Return the value of the specified key, or a null reply if the key\n * is not defined. */\nint cmd_GET(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n    RedisModuleString *val = RedisModule_DictGet(Keyspace,argv[1],NULL);\n    if (val == NULL) {\n        return RedisModule_ReplyWithNull(ctx);\n    } else {\n        return RedisModule_ReplyWithString(ctx, val);\n    }\n}\n\n/* HELLODICT.KEYRANGE <startkey> <endkey> <count>\n *\n * Return a list of matching keys, lexicographically between startkey\n * and endkey. No more than 'count' items are emitted. */\nint cmd_KEYRANGE(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    /* Parse the count argument. */\n    long long count;\n    if (RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    /* Seek the iterator. */\n    RedisModuleDictIter *iter = RedisModule_DictIteratorStart(\n        Keyspace, \">=\", argv[1]);\n\n    /* Reply with the matching items. */\n    char *key;\n    size_t keylen;\n    long long replylen = 0; /* Keep track of the amitted array len. */\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    while((key = RedisModule_DictNextC(iter,&keylen,NULL)) != NULL) {\n        if (replylen >= count) break;\n        if (RedisModule_DictCompare(iter,\"<=\",argv[2]) == REDISMODULE_ERR)\n            break;\n        RedisModule_ReplyWithStringBuffer(ctx,key,keylen);\n        replylen++;\n    }\n    RedisModule_ReplySetArrayLength(ctx,replylen);\n\n    /* Cleanup. */\n    RedisModule_DictIteratorStop(iter);\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellodict\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellodict.set\",\n        cmd_SET,\"write deny-oom\",1,1,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellodict.get\",\n        cmd_GET,\"readonly\",1,1,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellodict.keyrange\",\n        cmd_KEYRANGE,\"readonly\",1,1,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    /* Create our global dictionray. Here we'll set our keys and values. */\n    Keyspace = RedisModule_CreateDict(NULL);\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/hellohook.c",
    "content": "/* Server hooks API example\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2019, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n/* Client state change callback. */\nvoid clientChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(ctx);\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleClientInfo *ci = data;\n    printf(\"Client %s event for client #%llu %s:%d\\n\",\n        (sub == REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED) ?\n            \"connection\" : \"disconnection\",\n        ci->id,ci->addr,ci->port);\n}\n\nvoid flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(ctx);\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleFlushInfo *fi = data;\n    if (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) {\n        if (fi->dbnum != -1) {\n            RedisModuleCallReply *reply;\n            reply = RedisModule_Call(ctx,\"DBSIZE\",\"\");\n            long long numkeys = RedisModule_CallReplyInteger(reply);\n            printf(\"FLUSHDB event of database %d started (%lld keys in DB)\\n\",\n                fi->dbnum, numkeys);\n            RedisModule_FreeCallReply(reply);\n        } else {\n            printf(\"FLUSHALL event started\\n\");\n        }\n    } else {\n        if (fi->dbnum != -1) {\n            printf(\"FLUSHDB event of database %d ended\\n\",fi->dbnum);\n        } else {\n            printf(\"FLUSHALL event ended\\n\");\n        }\n    }\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellohook\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ClientChange, clientChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_FlushDB, flushdbCallback);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/hellotimer.c",
    "content": "/* Timer API example -- Register and handle timer events\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2018, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n/* Timer callback. */\nvoid timerHandler(RedisModuleCtx *ctx, void *data) {\n    REDISMODULE_NOT_USED(ctx);\n    printf(\"Fired %s!\\n\", data);\n    RedisModule_Free(data);\n}\n\n/* HELLOTIMER.TIMER*/\nint TimerCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    for (int j = 0; j < 10; j++) {\n        int delay = rand() % 5000;\n        char *buf = RedisModule_Alloc(256);\n        snprintf(buf,256,\"After %d\", delay);\n        RedisModuleTimerID tid = RedisModule_CreateTimer(ctx,delay,timerHandler,buf);\n        REDISMODULE_NOT_USED(tid);\n    }\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellotimer\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotimer.timer\",\n        TimerCommand_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/hellotype.c",
    "content": "/* This file implements a new module native data type called \"HELLOTYPE\".\n * The data structure implemented is a very simple ordered linked list of\n * 64 bit integers, in order to have something that is real world enough, but\n * at the same time, extremely simple to understand, to show how the API\n * works, how a new data type is created, and how to write basic methods\n * for RDB loading, saving and AOF rewriting.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n#include <stdint.h>\n\nstatic RedisModuleType *HelloType;\n\n/* ========================== Internal data structure  =======================\n * This is just a linked list of 64 bit integers where elements are inserted\n * in-place, so it's ordered. There is no pop/push operation but just insert\n * because it is enough to show the implementation of new data types without\n * making things complex. */\n\nstruct HelloTypeNode {\n    int64_t value;\n    struct HelloTypeNode *next;\n};\n\nstruct HelloTypeObject {\n    struct HelloTypeNode *head;\n    size_t len; /* Number of elements added. */\n};\n\nstruct HelloTypeObject *createHelloTypeObject(void) {\n    struct HelloTypeObject *o;\n    o = RedisModule_Alloc(sizeof(*o));\n    o->head = NULL;\n    o->len = 0;\n    return o;\n}\n\nvoid HelloTypeInsert(struct HelloTypeObject *o, int64_t ele) {\n    struct HelloTypeNode *next = o->head, *newnode, *prev = NULL;\n\n    while(next && next->value < ele) {\n        prev = next;\n        next = next->next;\n    }\n    newnode = RedisModule_Alloc(sizeof(*newnode));\n    newnode->value = ele;\n    newnode->next = next;\n    if (prev) {\n        prev->next = newnode;\n    } else {\n        o->head = newnode;\n    }\n    o->len++;\n}\n\nvoid HelloTypeReleaseObject(struct HelloTypeObject *o) {\n    struct HelloTypeNode *cur, *next;\n    cur = o->head;\n    while(cur) {\n        next = cur->next;\n        RedisModule_Free(cur);\n        cur = next;\n    }\n    RedisModule_Free(o);\n}\n\n/* ========================= \"hellotype\" type commands ======================= */\n\n/* HELLOTYPE.INSERT key value */\nint HelloTypeInsert_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long value;\n    if ((RedisModule_StringToLongLong(argv[2],&value) != REDISMODULE_OK)) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid value: must be a signed 64 bit integer\");\n    }\n\n    /* Create an empty value object if the key is currently empty. */\n    struct HelloTypeObject *hto;\n    if (type == REDISMODULE_KEYTYPE_EMPTY) {\n        hto = createHelloTypeObject();\n        RedisModule_ModuleTypeSetValue(key,HelloType,hto);\n    } else {\n        hto = RedisModule_ModuleTypeGetValue(key);\n    }\n\n    /* Insert the new element. */\n    HelloTypeInsert(hto,value);\n    RedisModule_SignalKeyAsReady(ctx,argv[1]);\n\n    RedisModule_ReplyWithLongLong(ctx,hto->len);\n    RedisModule_ReplicateVerbatim(ctx);\n    return REDISMODULE_OK;\n}\n\n/* HELLOTYPE.RANGE key first count */\nint HelloTypeRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long first, count;\n    if (RedisModule_StringToLongLong(argv[2],&first) != REDISMODULE_OK ||\n        RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK ||\n        first < 0 || count < 0)\n    {\n        return RedisModule_ReplyWithError(ctx,\n            \"ERR invalid first or count parameters\");\n    }\n\n    struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key);\n    struct HelloTypeNode *node = hto ? hto->head : NULL;\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    long long arraylen = 0;\n    while(node && count--) {\n        RedisModule_ReplyWithLongLong(ctx,node->value);\n        arraylen++;\n        node = node->next;\n    }\n    RedisModule_ReplySetArrayLength(ctx,arraylen);\n    return REDISMODULE_OK;\n}\n\n/* HELLOTYPE.LEN key */\nint HelloTypeLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_ReplyWithLongLong(ctx,hto ? hto->len : 0);\n    return REDISMODULE_OK;\n}\n\n/* ====================== Example of a blocking command ==================== */\n\n/* Reply callback for blocking command HELLOTYPE.BRANGE, this will get\n * called when the key we blocked for is ready: we need to check if we\n * can really serve the client, and reply OK or ERR accordingly. */\nint HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_READ);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_MODULE ||\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        RedisModule_CloseKey(key);\n        return REDISMODULE_ERR;\n    }\n\n    /* In case the key is able to serve our blocked client, let's directly\n     * use our original command implementation to make this example simpler. */\n    RedisModule_CloseKey(key);\n    return HelloTypeRange_RedisCommand(ctx,argv,argc-1);\n}\n\n/* Timeout callback for blocking command HELLOTYPE.BRANGE */\nint HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx,\"Request timedout\");\n}\n\n/* Private data freeing callback for HELLOTYPE.BRANGE command. */\nvoid HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_Free(privdata);\n}\n\n/* HELLOTYPE.BRANGE key first count timeout -- This is a blocking verison of\n * the RANGE operation, in order to show how to use the API\n * RedisModule_BlockClientOnKeys(). */\nint HelloTypeBRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 5) return RedisModule_WrongArity(ctx);\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    /* Parse the timeout before even trying to serve the client synchronously,\n     * so that we always fail ASAP on syntax errors. */\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[4],&timeout) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\n            \"ERR invalid timeout parameter\");\n    }\n\n    /* Can we serve the reply synchronously? */\n    if (type != REDISMODULE_KEYTYPE_EMPTY) {\n        return HelloTypeRange_RedisCommand(ctx,argv,argc-1);\n    }\n\n    /* Otherwise let's block on the key. */\n    void *privdata = RedisModule_Alloc(100);\n    RedisModule_BlockClientOnKeys(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout,argv+1,1,privdata);\n    return REDISMODULE_OK;\n}\n\n/* ========================== \"hellotype\" type methods ======================= */\n\nvoid *HelloTypeRdbLoad(RedisModuleIO *rdb, int encver) {\n    if (encver != 0) {\n        /* RedisModule_Log(\"warning\",\"Can't load data with version %d\", encver);*/\n        return NULL;\n    }\n    uint64_t elements = RedisModule_LoadUnsigned(rdb);\n    struct HelloTypeObject *hto = createHelloTypeObject();\n    while(elements--) {\n        int64_t ele = RedisModule_LoadSigned(rdb);\n        HelloTypeInsert(hto,ele);\n    }\n    return hto;\n}\n\nvoid HelloTypeRdbSave(RedisModuleIO *rdb, void *value) {\n    struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    RedisModule_SaveUnsigned(rdb,hto->len);\n    while(node) {\n        RedisModule_SaveSigned(rdb,node->value);\n        node = node->next;\n    }\n}\n\nvoid HelloTypeAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) {\n    struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    while(node) {\n        RedisModule_EmitAOF(aof,\"HELLOTYPE.INSERT\",\"sl\",key,node->value);\n        node = node->next;\n    }\n}\n\n/* The goal of this function is to return the amount of memory used by\n * the HelloType value. */\nsize_t HelloTypeMemUsage(const void *value) {\n    const struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    return sizeof(*hto) + sizeof(*node)*hto->len;\n}\n\nvoid HelloTypeFree(void *value) {\n    HelloTypeReleaseObject(value);\n}\n\nvoid HelloTypeDigest(RedisModuleDigest *md, void *value) {\n    struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    while(node) {\n        RedisModule_DigestAddLongLong(md,node->value);\n        node = node->next;\n    }\n    RedisModule_DigestEndSequence(md);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellotype\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    RedisModuleTypeMethods tm = {\n        .version = REDISMODULE_TYPE_METHOD_VERSION,\n        .rdb_load = HelloTypeRdbLoad,\n        .rdb_save = HelloTypeRdbSave,\n        .aof_rewrite = HelloTypeAofRewrite,\n        .mem_usage = HelloTypeMemUsage,\n        .free = HelloTypeFree,\n        .digest = HelloTypeDigest\n    };\n\n    HelloType = RedisModule_CreateDataType(ctx,\"hellotype\",0,&tm);\n    if (HelloType == NULL) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.insert\",\n        HelloTypeInsert_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.range\",\n        HelloTypeRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.len\",\n        HelloTypeLen_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.brange\",\n        HelloTypeBRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/helloworld.c",
    "content": "/* Helloworld module -- A few examples of the Redis Modules API in the form\n * of commands showing how to accomplish common tasks.\n *\n * This module does not do anything useful, if not for a few commands. The\n * examples are designed in order to show the API.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n/* HELLO.SIMPLE is among the simplest commands you can implement.\n * It just returns the currently selected DB id, a functionality which is\n * missing in Redis. The command uses two important API calls: one to\n * fetch the currently selected DB, the other in order to send the client\n * an integer reply as response. */\nint HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModule_ReplyWithLongLong(ctx,RedisModule_GetSelectedDb(ctx));\n    return REDISMODULE_OK;\n}\n\n/* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API\n * where you can \"open\" keys, make low level operations, create new keys by\n * pushing elements into non-existing keys, and so forth.\n *\n * You'll find this command to be roughly as fast as the actual RPUSH\n * command. */\nint HelloPushNative_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    RedisModule_ListPush(key,REDISMODULE_LIST_TAIL,argv[2]);\n    size_t newlen = RedisModule_ValueLength(key);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithLongLong(ctx,newlen);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.PUSH.CALL implements RPUSH using an higher level approach, calling\n * a Redis command instead of working with the key in a low level way. This\n * approach is useful when you need to call Redis commands that are not\n * available as low level APIs, or when you don't need the maximum speed\n * possible but instead prefer implementation simplicity. */\nint HelloPushCall_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    RedisModuleCallReply *reply;\n\n    reply = RedisModule_Call(ctx,\"RPUSH\",\"ss\",argv[1],argv[2]);\n    long long len = RedisModule_CallReplyInteger(reply);\n    RedisModule_FreeCallReply(reply);\n    RedisModule_ReplyWithLongLong(ctx,len);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.PUSH.CALL2\n * This is exaxctly as HELLO.PUSH.CALL, but shows how we can reply to the\n * client using directly a reply object that Call() returned. */\nint HelloPushCall2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    RedisModuleCallReply *reply;\n\n    reply = RedisModule_Call(ctx,\"RPUSH\",\"ss\",argv[1],argv[2]);\n    RedisModule_ReplyWithCallReply(ctx,reply);\n    RedisModule_FreeCallReply(reply);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LIST.SUM.LEN returns the total length of all the items inside\n * a Redis list, by using the high level Call() API.\n * This command is an example of the array reply access. */\nint HelloListSumLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    RedisModuleCallReply *reply;\n\n    reply = RedisModule_Call(ctx,\"LRANGE\",\"sll\",argv[1],(long long)0,(long long)-1);\n    size_t strlen = 0;\n    size_t items = RedisModule_CallReplyLength(reply);\n    size_t j;\n    for (j = 0; j < items; j++) {\n        RedisModuleCallReply *ele = RedisModule_CallReplyArrayElement(reply,j);\n        strlen += RedisModule_CallReplyLength(ele);\n    }\n    RedisModule_FreeCallReply(reply);\n    RedisModule_ReplyWithLongLong(ctx,strlen);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LIST.SPLICE srclist dstlist count\n * Moves 'count' elements from the tail of 'srclist' to the head of\n * 'dstlist'. If less than count elements are available, it moves as much\n * elements as possible. */\nint HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    /* Src and dst key must be empty or lists. */\n    if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||\n        (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))\n    {\n        RedisModule_CloseKey(srckey);\n        RedisModule_CloseKey(dstkey);\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long count;\n    if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||\n        (count < 0)) {\n        RedisModule_CloseKey(srckey);\n        RedisModule_CloseKey(dstkey);\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    while(count-- > 0) {\n        RedisModuleString *ele;\n\n        ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);\n        if (ele == NULL) break;\n        RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);\n        RedisModule_FreeString(ctx,ele);\n    }\n\n    size_t len = RedisModule_ValueLength(srckey);\n    RedisModule_CloseKey(srckey);\n    RedisModule_CloseKey(dstkey);\n    RedisModule_ReplyWithLongLong(ctx,len);\n    return REDISMODULE_OK;\n}\n\n/* Like the HELLO.LIST.SPLICE above, but uses automatic memory management\n * in order to avoid freeing stuff. */\nint HelloListSpliceAuto_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    RedisModule_AutoMemory(ctx);\n\n    RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    /* Src and dst key must be empty or lists. */\n    if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||\n        (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long count;\n    if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||\n        (count < 0))\n    {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    while(count-- > 0) {\n        RedisModuleString *ele;\n\n        ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);\n        if (ele == NULL) break;\n        RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);\n    }\n\n    size_t len = RedisModule_ValueLength(srckey);\n    RedisModule_ReplyWithLongLong(ctx,len);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.RAND.ARRAY <count>\n * Shows how to generate arrays as commands replies.\n * It just outputs <count> random numbers. */\nint HelloRandArray_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n    long long count;\n    if (RedisModule_StringToLongLong(argv[1],&count) != REDISMODULE_OK ||\n        count < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n\n    /* To reply with an array, we call RedisModule_ReplyWithArray() followed\n     * by other \"count\" calls to other reply functions in order to generate\n     * the elements of the array. */\n    RedisModule_ReplyWithArray(ctx,count);\n    while(count--) RedisModule_ReplyWithLongLong(ctx,rand());\n    return REDISMODULE_OK;\n}\n\n/* This is a simple command to test replication. Because of the \"!\" modified\n * in the RedisModule_Call() call, the two INCRs get replicated.\n * Also note how the ECHO is replicated in an unexpected position (check\n * comments the function implementation). */\nint HelloRepl1_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModule_AutoMemory(ctx);\n\n    /* This will be replicated *after* the two INCR statements, since\n     * the Call() replication has precedence, so the actual replication\n     * stream will be:\n     *\n     * MULTI\n     * INCR foo\n     * INCR bar\n     * ECHO c foo\n     * EXEC\n     */\n    RedisModule_Replicate(ctx,\"ECHO\",\"c\",\"foo\");\n\n    /* Using the \"!\" modifier we replicate the command if it\n     * modified the dataset in some way. */\n    RedisModule_Call(ctx,\"INCR\",\"c!\",\"foo\");\n    RedisModule_Call(ctx,\"INCR\",\"c!\",\"bar\");\n\n    RedisModule_ReplyWithLongLong(ctx,0);\n\n    return REDISMODULE_OK;\n}\n\n/* Another command to show replication. In this case, we call\n * RedisModule_ReplicateVerbatim() to mean we want just the command to be\n * propagated to slaves / AOF exactly as it was called by the user.\n *\n * This command also shows how to work with string objects.\n * It takes a list, and increments all the elements (that must have\n * a numerical value) by 1, returning the sum of all the elements\n * as reply.\n *\n * Usage: HELLO.REPL2 <list-key> */\nint HelloRepl2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST)\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n\n    size_t listlen = RedisModule_ValueLength(key);\n    long long sum = 0;\n\n    /* Rotate and increment. */\n    while(listlen--) {\n        RedisModuleString *ele = RedisModule_ListPop(key,REDISMODULE_LIST_TAIL);\n        long long val;\n        if (RedisModule_StringToLongLong(ele,&val) != REDISMODULE_OK) val = 0;\n        val++;\n        sum += val;\n        RedisModuleString *newele = RedisModule_CreateStringFromLongLong(ctx,val);\n        RedisModule_ListPush(key,REDISMODULE_LIST_HEAD,newele);\n    }\n    RedisModule_ReplyWithLongLong(ctx,sum);\n    RedisModule_ReplicateVerbatim(ctx);\n    return REDISMODULE_OK;\n}\n\n/* This is an example of strings DMA access. Given a key containing a string\n * it toggles the case of each character from lower to upper case or the\n * other way around.\n *\n * No automatic memory management is used in this example (for the sake\n * of variety).\n *\n * HELLO.TOGGLE.CASE key */\nint HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    int keytype = RedisModule_KeyType(key);\n    if (keytype != REDISMODULE_KEYTYPE_STRING &&\n        keytype != REDISMODULE_KEYTYPE_EMPTY)\n    {\n        RedisModule_CloseKey(key);\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    if (keytype == REDISMODULE_KEYTYPE_STRING) {\n        size_t len, j;\n        char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);\n        for (j = 0; j < len; j++) {\n            if (isupper(s[j])) {\n                s[j] = tolower(s[j]);\n            } else {\n                s[j] = toupper(s[j]);\n            }\n        }\n    }\n\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    RedisModule_ReplicateVerbatim(ctx);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.MORE.EXPIRE key milliseconds.\n *\n * If they key has already an associated TTL, extends it by \"milliseconds\"\n * milliseconds. Otherwise no operation is performed. */\nint HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    mstime_t addms, expire;\n\n    if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid expire time\");\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    expire = RedisModule_GetExpire(key);\n    if (expire != REDISMODULE_NO_EXPIRE) {\n        expire += addms;\n        RedisModule_SetExpire(key,expire);\n    }\n    return RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n}\n\n/* HELLO.ZSUMRANGE key startscore endscore\n * Return the sum of all the scores elements between startscore and endscore.\n *\n * The computation is performed two times, one time from start to end and\n * another time backward. The two scores, returned as a two element array,\n * should match.*/\nint HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    double score_start, score_end;\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK ||\n        RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK)\n    {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid range\");\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    double scoresum_a = 0;\n    double scoresum_b = 0;\n\n    RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0);\n    while(!RedisModule_ZsetRangeEndReached(key)) {\n        double score;\n        RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);\n        RedisModule_FreeString(ctx,ele);\n        scoresum_a += score;\n        RedisModule_ZsetRangeNext(key);\n    }\n    RedisModule_ZsetRangeStop(key);\n\n    RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0);\n    while(!RedisModule_ZsetRangeEndReached(key)) {\n        double score;\n        RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);\n        RedisModule_FreeString(ctx,ele);\n        scoresum_b += score;\n        RedisModule_ZsetRangePrev(key);\n    }\n\n    RedisModule_ZsetRangeStop(key);\n\n    RedisModule_CloseKey(key);\n\n    RedisModule_ReplyWithArray(ctx,2);\n    RedisModule_ReplyWithDouble(ctx,scoresum_a);\n    RedisModule_ReplyWithDouble(ctx,scoresum_b);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LEXRANGE key min_lex max_lex min_age max_age\n * This command expects a sorted set stored at key in the following form:\n * - All the elements have score 0.\n * - Elements are pairs of \"<name>:<age>\", for example \"Anna:52\".\n * The command will return all the sorted set items that are lexicographically\n * between the specified range (using the same format as ZRANGEBYLEX)\n * and having an age between min_age and max_age. */\nint HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 6) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"invalid range\");\n    }\n\n    int arraylen = 0;\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    while(!RedisModule_ZsetRangeEndReached(key)) {\n        double score;\n        RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);\n        RedisModule_ReplyWithString(ctx,ele);\n        RedisModule_FreeString(ctx,ele);\n        RedisModule_ZsetRangeNext(key);\n        arraylen++;\n    }\n    RedisModule_ZsetRangeStop(key);\n    RedisModule_ReplySetArrayLength(ctx,arraylen);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.HCOPY key srcfield dstfield\n * This is just an example command that sets the hash field dstfield to the\n * same value of srcfield. If srcfield does not exist no operation is\n * performed.\n *\n * The command returns 1 if the copy is performed (srcfield exists) otherwise\n * 0 is returned. */\nint HelloHCopy_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_HASH &&\n        type != REDISMODULE_KEYTYPE_EMPTY)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    /* Get the old field value. */\n    RedisModuleString *oldval;\n    RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&oldval,NULL);\n    if (oldval) {\n        RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[3],oldval,NULL);\n    }\n    RedisModule_ReplyWithLongLong(ctx,oldval != NULL);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LEFTPAD str len ch\n * This is an implementation of the infamous LEFTPAD function, that\n * was at the center of an issue with the npm modules system in March 2016.\n *\n * LEFTPAD is a good example of using a Redis Modules API called\n * \"pool allocator\", that was a famous way to allocate memory in yet another\n * open source project, the Apache web server.\n *\n * The concept is very simple: there is memory that is useful to allocate\n * only in the context of serving a request, and must be freed anyway when\n * the callback implementing the command returns. So in that case the module\n * does not need to retain a reference to these allocations, it is just\n * required to free the memory before returning. When this is the case the\n * module can call RedisModule_PoolAlloc() instead, that works like malloc()\n * but will automatically free the memory when the module callback returns.\n *\n * Note that PoolAlloc() does not necessarily require AutoMemory to be\n * active. */\nint HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    long long padlen;\n\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) ||\n        (padlen< 0)) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid padding length\");\n    }\n    size_t strlen, chlen;\n    const char *str = RedisModule_StringPtrLen(argv[1], &strlen);\n    const char *ch = RedisModule_StringPtrLen(argv[3], &chlen);\n\n    /* If the string is already larger than the target len, just return\n     * the string itself. */\n    if (strlen >= (size_t)padlen)\n        return RedisModule_ReplyWithString(ctx,argv[1]);\n\n    /* Padding must be a single character in this simple implementation. */\n    if (chlen != 1)\n        return RedisModule_ReplyWithError(ctx,\n            \"ERR padding must be a single char\");\n\n    /* Here we use our pool allocator, for our throw-away allocation. */\n    padlen -= strlen;\n    char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen);\n    for (long long j = 0; j < padlen; j++) buf[j] = *ch;\n    memcpy(buf+padlen,str,strlen);\n\n    RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen);\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (RedisModule_Init(ctx,\"helloworld\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    /* Log the list of parameters passing loading the module. */\n    for (int j = 0; j < argc; j++) {\n        const char *s = RedisModule_StringPtrLen(argv[j],NULL);\n        printf(\"Module loaded with ARGV[%d] = %s\\n\", j, s);\n    }\n\n    if (RedisModule_CreateCommand(ctx,\"hello.simple\",\n        HelloSimple_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.push.native\",\n        HelloPushNative_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.push.call\",\n        HelloPushCall_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.push.call2\",\n        HelloPushCall2_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.list.sum.len\",\n        HelloListSumLen_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.list.splice\",\n        HelloListSplice_RedisCommand,\"write deny-oom\",1,2,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.list.splice.auto\",\n        HelloListSpliceAuto_RedisCommand,\n        \"write deny-oom\",1,2,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.rand.array\",\n        HelloRandArray_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.repl1\",\n        HelloRepl1_RedisCommand,\"write\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.repl2\",\n        HelloRepl2_RedisCommand,\"write\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.toggle.case\",\n        HelloToggleCase_RedisCommand,\"write\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.more.expire\",\n        HelloMoreExpire_RedisCommand,\"write\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.zsumrange\",\n        HelloZsumRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.lexrange\",\n        HelloLexRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.hcopy\",\n        HelloHCopy_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.leftpad\",\n        HelloLeftPad_RedisCommand,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/modules/testmodule.c",
    "content": "/* Module designed to test the Redis modules subsystem.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <string.h>\n\n/* --------------------------------- Helpers -------------------------------- */\n\n/* Return true if the reply and the C null term string matches. */\nint TestMatchReply(RedisModuleCallReply *reply, char *str) {\n    RedisModuleString *mystr;\n    mystr = RedisModule_CreateStringFromCallReply(reply);\n    if (!mystr) return 0;\n    const char *ptr = RedisModule_StringPtrLen(mystr,NULL);\n    return strcmp(ptr,str) == 0;\n}\n\n/* ------------------------------- Test units ------------------------------- */\n\n/* TEST.CALL -- Test Call() API. */\nint TestCall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_AutoMemory(ctx);\n    RedisModuleCallReply *reply;\n\n    RedisModule_Call(ctx,\"DEL\",\"c\",\"mylist\");\n    RedisModuleString *mystr = RedisModule_CreateString(ctx,\"foo\",3);\n    RedisModule_Call(ctx,\"RPUSH\",\"csl\",\"mylist\",mystr,(long long)1234);\n    reply = RedisModule_Call(ctx,\"LRANGE\",\"ccc\",\"mylist\",\"0\",\"-1\");\n    long long items = RedisModule_CallReplyLength(reply);\n    if (items != 2) goto fail;\n\n    RedisModuleCallReply *item0, *item1;\n\n    item0 = RedisModule_CallReplyArrayElement(reply,0);\n    item1 = RedisModule_CallReplyArrayElement(reply,1);\n    if (!TestMatchReply(item0,\"foo\")) goto fail;\n    if (!TestMatchReply(item1,\"1234\")) goto fail;\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n\nfail:\n    RedisModule_ReplyWithSimpleString(ctx,\"ERR\");\n    return REDISMODULE_OK;\n}\n\n/* TEST.STRING.APPEND -- Test appending to an existing string object. */\nint TestStringAppend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleString *s = RedisModule_CreateString(ctx,\"foo\",3);\n    RedisModule_StringAppendBuffer(ctx,s,\"bar\",3);\n    RedisModule_ReplyWithString(ctx,s);\n    RedisModule_FreeString(ctx,s);\n    return REDISMODULE_OK;\n}\n\n/* TEST.STRING.APPEND.AM -- Test append with retain when auto memory is on. */\nint TestStringAppendAM(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_AutoMemory(ctx);\n    RedisModuleString *s = RedisModule_CreateString(ctx,\"foo\",3);\n    RedisModule_RetainString(ctx,s);\n    RedisModule_StringAppendBuffer(ctx,s,\"bar\",3);\n    RedisModule_ReplyWithString(ctx,s);\n    RedisModule_FreeString(ctx,s);\n    return REDISMODULE_OK;\n}\n\n/* TEST.STRING.PRINTF -- Test string formatting. */\nint TestStringPrintf(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx);\n    if (argc < 3) {\n        return RedisModule_WrongArity(ctx);\n    }\n    RedisModuleString *s = RedisModule_CreateStringPrintf(ctx,\n        \"Got %d args. argv[1]: %s, argv[2]: %s\",\n        argc,\n        RedisModule_StringPtrLen(argv[1], NULL),\n        RedisModule_StringPtrLen(argv[2], NULL)\n    );\n\n    RedisModule_ReplyWithString(ctx,s);\n\n    return REDISMODULE_OK;\n}\n\nint failTest(RedisModuleCtx *ctx, const char *msg) {\n    RedisModule_ReplyWithError(ctx, msg);\n    return REDISMODULE_ERR;\n}\n\nint TestUnlink(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx);\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleKey *k = RedisModule_OpenKey(ctx, RedisModule_CreateStringPrintf(ctx, \"unlinked\"), REDISMODULE_WRITE | REDISMODULE_READ);\n    if (!k) return failTest(ctx, \"Could not create key\");\n\n    if (REDISMODULE_ERR == RedisModule_StringSet(k, RedisModule_CreateStringPrintf(ctx, \"Foobar\"))) {\n        return failTest(ctx, \"Could not set string value\");\n    }\n\n    RedisModuleCallReply *rep = RedisModule_Call(ctx, \"EXISTS\", \"c\", \"unlinked\");\n    if (!rep || RedisModule_CallReplyInteger(rep) != 1) {\n        return failTest(ctx, \"Key does not exist before unlink\");\n    }\n\n    if (REDISMODULE_ERR == RedisModule_UnlinkKey(k)) {\n        return failTest(ctx, \"Could not unlink key\");\n    }\n\n    rep = RedisModule_Call(ctx, \"EXISTS\", \"c\", \"unlinked\");\n    if (!rep || RedisModule_CallReplyInteger(rep) != 0) {\n        return failTest(ctx, \"Could not verify key to be unlinked\");\n    }\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n\n}\n\nint NotifyCallback(RedisModuleCtx *ctx, int type, const char *event,\n                   RedisModuleString *key) {\n  /* Increment a counter on the notifications: for each key notified we\n   * increment a counter */\n  RedisModule_Log(ctx, \"notice\", \"Got event type %d, event %s, key %s\", type,\n                  event, RedisModule_StringPtrLen(key, NULL));\n\n  RedisModule_Call(ctx, \"HINCRBY\", \"csc\", \"notifications\", key, \"1\");\n  return REDISMODULE_OK;\n}\n\n/* TEST.NOTIFICATIONS -- Test Keyspace Notifications. */\nint TestNotifications(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n#define FAIL(msg, ...)                                                                       \\\n    {                                                                                        \\\n        RedisModule_Log(ctx, \"warning\", \"Failed NOTIFY Test. Reason: \" #msg, ##__VA_ARGS__); \\\n        goto err;                                                                            \\\n    }\n    RedisModule_Call(ctx, \"FLUSHDB\", \"\");\n\n    RedisModule_Call(ctx, \"SET\", \"cc\", \"foo\", \"bar\");\n    RedisModule_Call(ctx, \"SET\", \"cc\", \"foo\", \"baz\");\n    RedisModule_Call(ctx, \"SADD\", \"cc\", \"bar\", \"x\");\n    RedisModule_Call(ctx, \"SADD\", \"cc\", \"bar\", \"y\");\n\n    RedisModule_Call(ctx, \"HSET\", \"ccc\", \"baz\", \"x\", \"y\");\n    /* LPUSH should be ignored and not increment any counters */\n    RedisModule_Call(ctx, \"LPUSH\", \"cc\", \"l\", \"y\");\n    RedisModule_Call(ctx, \"LPUSH\", \"cc\", \"l\", \"y\");\n\n    /* Miss some keys intentionally so we will get a \"keymiss\" notification. */\n    RedisModule_Call(ctx, \"GET\", \"c\", \"nosuchkey\");\n    RedisModule_Call(ctx, \"SMEMBERS\", \"c\", \"nosuchkey\");\n\n    size_t sz;\n    const char *rep;\n    RedisModuleCallReply *r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"foo\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for foo\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '2') {\n            FAIL(\"Got reply '%s'. expected '2'\", RedisModule_CallReplyStringPtr(r, NULL));\n        }\n    }\n\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"bar\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for bar\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '2') {\n            FAIL(\"Got reply '%s'. expected '2'\", rep);\n        }\n    }\n\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"baz\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for baz\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '1') {\n            FAIL(\"Got reply '%.*s'. expected '1'\", sz, rep);\n        }\n    }\n    /* For l we expect nothing since we didn't subscribe to list events */\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"l\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_NULL) {\n        FAIL(\"Wrong reply for l\");\n    }\n\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"nosuchkey\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for nosuchkey\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '2') {\n            FAIL(\"Got reply '%.*s'. expected '2'\", sz, rep);\n        }\n    }\n\n    RedisModule_Call(ctx, \"FLUSHDB\", \"\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\nerr:\n    RedisModule_Call(ctx, \"FLUSHDB\", \"\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"ERR\");\n}\n\n/* TEST.CTXFLAGS -- Test GetContextFlags. */\nint TestCtxFlags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argc);\n    REDISMODULE_NOT_USED(argv);\n\n    RedisModule_AutoMemory(ctx);\n\n    int ok = 1;\n    const char *errString = NULL;\n#undef FAIL\n#define FAIL(msg)        \\\n    {                    \\\n        ok = 0;          \\\n        errString = msg; \\\n        goto end;        \\\n    }\n\n    int flags = RedisModule_GetContextFlags(ctx);\n    if (flags == 0) {\n        FAIL(\"Got no flags\");\n    }\n\n    if (flags & REDISMODULE_CTX_FLAGS_LUA) FAIL(\"Lua flag was set\");\n    if (flags & REDISMODULE_CTX_FLAGS_MULTI) FAIL(\"Multi flag was set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_AOF) FAIL(\"AOF Flag was set\")\n    /* Enable AOF to test AOF flags */\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"appendonly\", \"yes\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_AOF)) FAIL(\"AOF Flag not set after config set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_RDB) FAIL(\"RDB Flag was set\");\n    /* Enable RDB to test RDB flags */\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"save\", \"900 1\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_RDB)) FAIL(\"RDB Flag was not set after config set\");\n\n    if (!(flags & REDISMODULE_CTX_FLAGS_MASTER)) FAIL(\"Master flag was not set\");\n    if (flags & REDISMODULE_CTX_FLAGS_SLAVE) FAIL(\"Slave flag was set\");\n    if (flags & REDISMODULE_CTX_FLAGS_READONLY) FAIL(\"Read-only flag was set\");\n    if (flags & REDISMODULE_CTX_FLAGS_CLUSTER) FAIL(\"Cluster flag was set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_MAXMEMORY) FAIL(\"Maxmemory flag was set\");\n\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory\", \"100000000\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_MAXMEMORY))\n        FAIL(\"Maxmemory flag was not set after config set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_EVICT) FAIL(\"Eviction flag was set\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory-policy\", \"allkeys-lru\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_EVICT)) FAIL(\"Eviction flag was not set after config set\");\n\nend:\n    /* Revert config changes */\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"appendonly\", \"no\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"save\", \"\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory\", \"0\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory-policy\", \"noeviction\");\n\n    if (!ok) {\n        RedisModule_Log(ctx, \"warning\", \"Failed CTXFLAGS Test. Reason: %s\", errString);\n        return RedisModule_ReplyWithSimpleString(ctx, \"ERR\");\n    }\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* ----------------------------- Test framework ----------------------------- */\n\n/* Return 1 if the reply matches the specified string, otherwise log errors\n * in the server log and return 0. */\nint TestAssertStringReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) {\n    RedisModuleString *mystr, *expected;\n\n    if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) {\n        RedisModule_Log(ctx,\"warning\",\"Unexpected reply type %d\",\n            RedisModule_CallReplyType(reply));\n        return 0;\n    }\n    mystr = RedisModule_CreateStringFromCallReply(reply);\n    expected = RedisModule_CreateString(ctx,str,len);\n    if (RedisModule_StringCompare(mystr,expected) != 0) {\n        const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL);\n        const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL);\n        RedisModule_Log(ctx,\"warning\",\n            \"Unexpected string reply '%s' (instead of '%s')\",\n            mystr_ptr, expected_ptr);\n        return 0;\n    }\n    return 1;\n}\n\n/* Return 1 if the reply matches the specified integer, otherwise log errors\n * in the server log and return 0. */\nint TestAssertIntegerReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, long long expected) {\n    if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_INTEGER) {\n        RedisModule_Log(ctx,\"warning\",\"Unexpected reply type %d\",\n            RedisModule_CallReplyType(reply));\n        return 0;\n    }\n    long long val = RedisModule_CallReplyInteger(reply);\n    if (val != expected) {\n        RedisModule_Log(ctx,\"warning\",\n            \"Unexpected integer reply '%lld' (instead of '%lld')\",\n            val, expected);\n        return 0;\n    }\n    return 1;\n}\n\n#define T(name,...) \\\n    do { \\\n        RedisModule_Log(ctx,\"warning\",\"Testing %s\", name); \\\n        reply = RedisModule_Call(ctx,name,__VA_ARGS__); \\\n    } while (0);\n\n/* TEST.IT -- Run all the tests. */\nint TestIt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_AutoMemory(ctx);\n    RedisModuleCallReply *reply;\n\n    /* Make sure the DB is empty before to proceed. */\n    T(\"dbsize\",\"\");\n    if (!TestAssertIntegerReply(ctx,reply,0)) goto fail;\n\n    T(\"ping\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"PONG\",4)) goto fail;\n\n    T(\"test.call\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    T(\"test.ctxflags\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    T(\"test.string.append\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"foobar\",6)) goto fail;\n\n    T(\"test.unlink\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    T(\"test.string.append.am\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"foobar\",6)) goto fail;\n\n    T(\"test.string.printf\", \"cc\", \"foo\", \"bar\");\n    if (!TestAssertStringReply(ctx,reply,\"Got 3 args. argv[1]: foo, argv[2]: bar\",38)) goto fail;\n\n    T(\"test.notify\", \"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    RedisModule_ReplyWithSimpleString(ctx,\"ALL TESTS PASSED\");\n    return REDISMODULE_OK;\n\nfail:\n    RedisModule_ReplyWithSimpleString(ctx,\n        \"SOME TEST NOT PASSED! Check server logs\");\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"test\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.call\",\n        TestCall,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.string.append\",\n        TestStringAppend,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.string.append.am\",\n        TestStringAppendAM,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.string.printf\",\n        TestStringPrintf,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.ctxflags\",\n        TestCtxFlags,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.unlink\",\n        TestUnlink,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.it\",\n        TestIt,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModule_SubscribeToKeyspaceEvents(ctx,\n                                            REDISMODULE_NOTIFY_HASH |\n                                            REDISMODULE_NOTIFY_SET |\n                                            REDISMODULE_NOTIFY_STRING |\n                                            REDISMODULE_NOTIFY_KEY_MISS,\n                                        NotifyCallback);\n    if (RedisModule_CreateCommand(ctx,\"test.notify\",\n        TestNotifications,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n\n/* ================================ MULTI/EXEC ============================== */\n\n/* Client state initialization for MULTI/EXEC */\nvoid initClientMultiState(client *c) {\n    c->mstate.commands = NULL;\n    c->mstate.count = 0;\n    c->mstate.cmd_flags = 0;\n}\n\n/* Release all the resources associated with MULTI/EXEC state */\nvoid freeClientMultiState(client *c) {\n    int j;\n\n    for (j = 0; j < c->mstate.count; j++) {\n        int i;\n        multiCmd *mc = c->mstate.commands+j;\n\n        for (i = 0; i < mc->argc; i++)\n            decrRefCount(mc->argv[i]);\n        zfree(mc->argv);\n    }\n    zfree(c->mstate.commands);\n}\n\n/* Add a new command into the MULTI commands queue */\nvoid queueMultiCommand(client *c) {\n    multiCmd *mc;\n    int j;\n\n    c->mstate.commands = zrealloc(c->mstate.commands,\n            sizeof(multiCmd)*(c->mstate.count+1));\n    mc = c->mstate.commands+c->mstate.count;\n    mc->cmd = c->cmd;\n    mc->argc = c->argc;\n    mc->argv = 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    c->mstate.count++;\n    c->mstate.cmd_flags |= c->cmd->flags;\n}\n\nvoid discardTransaction(client *c) {\n    freeClientMultiState(c);\n    initClientMultiState(c);\n    c->flags &= ~(CLIENT_MULTI|CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC);\n    unwatchAllKeys(c);\n}\n\n/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.\n * Should be called every time there is an error while queueing a command. */\nvoid flagTransaction(client *c) {\n    if (c->flags & CLIENT_MULTI)\n        c->flags |= CLIENT_DIRTY_EXEC;\n}\n\nvoid multiCommand(client *c) {\n    if (c->flags & CLIENT_MULTI) {\n        addReplyError(c,\"MULTI calls can not be nested\");\n        return;\n    }\n    c->flags |= CLIENT_MULTI;\n    addReply(c,shared.ok);\n}\n\nvoid discardCommand(client *c) {\n    if (!(c->flags & CLIENT_MULTI)) {\n        addReplyError(c,\"DISCARD without MULTI\");\n        return;\n    }\n    discardTransaction(c);\n    addReply(c,shared.ok);\n}\n\n/* Send a MULTI command to all the slaves and AOF file. Check the execCommand\n * implementation for more information. */\nvoid execCommandPropagateMulti(client *c) {\n    propagate(server.multiCommand,c->db->id,&shared.multi,1,\n              PROPAGATE_AOF|PROPAGATE_REPL);\n}\n\nvoid execCommandPropagateExec(client *c) {\n    propagate(server.execCommand,c->db->id,&shared.exec,1,\n              PROPAGATE_AOF|PROPAGATE_REPL);\n}\n\nvoid execCommand(client *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    int was_master = server.masterhost == NULL;\n\n    if (!(c->flags & CLIENT_MULTI)) {\n        addReplyError(c,\"EXEC without MULTI\");\n        return;\n    }\n\n    /* Check if we need to abort the EXEC because:\n     * 1) Some WATCHed key was touched.\n     * 2) There was a previous error while queueing commands.\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    if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC)) {\n        addReply(c, c->flags & CLIENT_DIRTY_EXEC ? shared.execaborterr :\n                                                   shared.nullarray[c->resp]);\n        discardTransaction(c);\n        goto handle_monitor;\n    }\n\n    /* If there are write commands inside the transaction, and this is a read\n     * only slave, we want to send an error. This happens when the transaction\n     * was initiated when the instance was a master or a writable replica and\n     * then the configuration changed (for example instance was turned into\n     * a replica). */\n    if (!server.loading && server.masterhost && server.repl_slave_ro &&\n        !(c->flags & CLIENT_MASTER) && c->mstate.cmd_flags & CMD_WRITE)\n    {\n        addReplyError(c,\n            \"Transaction contains write commands but instance \"\n            \"is now a read-only replica. EXEC aborted.\");\n        discardTransaction(c);\n        goto handle_monitor;\n    }\n\n    /* Exec all the queued commands */\n    unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */\n    orig_argv = c->argv;\n    orig_argc = c->argc;\n    orig_cmd = c->cmd;\n    addReplyArrayLen(c,c->mstate.count);\n    for (j = 0; j < c->mstate.count; j++) {\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 command which\n         * is not readonly nor an administrative one.\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        if (!must_propagate &&\n            !server.loading &&\n            !(c->cmd->flags & (CMD_READONLY|CMD_ADMIN)))\n        {\n            execCommandPropagateMulti(c);\n            must_propagate = 1;\n        }\n\n        int acl_keypos;\n        int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);\n        if (acl_retval != ACL_OK) {\n            addACLLogEntry(c,acl_retval,acl_keypos,NULL);\n            addReplyErrorFormat(c,\n                \"-NOPERM ACLs rules changed between the moment the \"\n                \"transaction was accumulated and the EXEC call. \"\n                \"This command is no longer allowed for the \"\n                \"following reason: %s\",\n                (acl_retval == ACL_DENIED_CMD) ?\n                \"no permission to execute the command or subcommand\" :\n                \"no permission to touch the specified keys\");\n        } else {\n            call(c,server.loading ? CMD_CALL_NONE : CMD_CALL_FULL);\n        }\n\n        /* Commands may alter argc/argv, restore mstate. */\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    c->argv = orig_argv;\n    c->argc = orig_argc;\n    c->cmd = orig_cmd;\n    discardTransaction(c);\n\n    /* Make sure the EXEC command will be propagated as well if MULTI\n     * was already propagated. */\n    if (must_propagate) {\n        int is_master = server.masterhost == NULL;\n        server.dirty++;\n        /* If inside the MULTI/EXEC block this instance was suddenly\n         * switched from master to slave (using the SLAVEOF command), the\n         * initial MULTI was propagated into the replication backlog, but the\n         * rest was not. We need to make sure to at least terminate the\n         * backlog with the final EXEC. */\n        if (server.repl_backlog && was_master && !is_master) {\n            char *execcmd = \"*1\\r\\n$4\\r\\nEXEC\\r\\n\";\n            feedReplicationBacklog(execcmd,strlen(execcmd));\n        }\n    }\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 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 * Also every client contains a list of WATCHed keys so that's possible to\n * un-watch such keys when the client is freed or when UNWATCH is called. */\n\n/* In the client->watched_keys list we need to use watchedKey structures\n * as in order to identify a key in Redis we need both the key name and the\n * DB */\ntypedef struct watchedKey {\n    robj *key;\n    redisDb *db;\n} watchedKey;\n\n/* Watch for the specified key */\nvoid watchForKey(client *c, robj *key) {\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    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    /* This key is not already watched in this DB. Let's add it */\n    clients = dictFetchValue(c->db->watched_keys,key);\n    if (!clients) {\n        clients = listCreate();\n        dictAdd(c->db->watched_keys,key,clients);\n        incrRefCount(key);\n    }\n    listAddNodeTail(clients,c);\n    /* Add the new key to the list of keys watched by this client */\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. */\nvoid unwatchAllKeys(client *c) {\n    listIter li;\n    listNode *ln;\n\n    if (listLength(c->watched_keys) == 0) return;\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        wk = listNodeValue(ln);\n        clients = dictFetchValue(wk->db->watched_keys, wk->key);\n        serverAssertWithInfo(c,NULL,clients != NULL);\n        listDelNode(clients,listSearchKey(clients,c));\n        /* Kill the entry at all if this was the only client */\n        if (listLength(clients) == 0)\n            dictDelete(wk->db->watched_keys, wk->key);\n        /* Remove this watched key from the client->watched list */\n        listDelNode(c->watched_keys,ln);\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. */\nvoid touchWatchedKey(redisDb *db, robj *key) {\n    list *clients;\n    listIter li;\n    listNode *ln;\n\n    if (dictSize(db->watched_keys) == 0) return;\n    clients = dictFetchValue(db->watched_keys, key);\n    if (!clients) return;\n\n    /* Mark all the clients watching this key as CLIENT_DIRTY_CAS */\n    /* Check if we are already watching for this key */\n    listRewind(clients,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n\n        c->flags |= CLIENT_DIRTY_CAS;\n    }\n}\n\n/* On FLUSHDB or FLUSHALL all the watched keys that are present before the\n * flush but will be deleted as effect of the flushing operation should\n * be touched. \"dbid\" is the DB that's getting the flush. -1 if it is\n * a FLUSHALL operation (all the DBs flushed). */\nvoid touchWatchedKeysOnFlush(int dbid) {\n    listIter li1, li2;\n    listNode *ln;\n\n    /* For every client, check all the waited keys */\n    listRewind(server.clients,&li1);\n    while((ln = listNext(&li1))) {\n        client *c = listNodeValue(ln);\n        listRewind(c->watched_keys,&li2);\n        while((ln = listNext(&li2))) {\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            if (dbid == -1 || wk->db->id == dbid) {\n                if (dictFind(wk->db->dict, wk->key->ptr) != NULL)\n                    c->flags |= CLIENT_DIRTY_CAS;\n            }\n        }\n    }\n}\n\nvoid watchCommand(client *c) {\n    int j;\n\n    if (c->flags & CLIENT_MULTI) {\n        addReplyError(c,\"WATCH inside MULTI is not allowed\");\n        return;\n    }\n    for (j = 1; j < c->argc; j++)\n        watchForKey(c,c->argv[j]);\n    addReply(c,shared.ok);\n}\n\nvoid unwatchCommand(client *c) {\n    unwatchAllKeys(c);\n    c->flags &= (~CLIENT_DIRTY_CAS);\n    addReply(c,shared.ok);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"atomicvar.h\"\n#include <sys/socket.h>\n#include <sys/uio.h>\n#include <math.h>\n#include <ctype.h>\n\nstatic void setProtocolError(const char *errstr, client *c);\nint postponeClientRead(client *c);\nint ProcessingEventsWhileBlocked = 0; /* See processEventsWhileBlocked(). */\n\n/* Return the size consumed from the allocator, for the specified SDS string,\n * including internal fragmentation. This function is used in order to compute\n * the client output buffer size. */\nsize_t sdsZmallocSize(sds s) {\n    void *sh = sdsAllocPtr(s);\n    return zmalloc_size(sh);\n}\n\n/* Return the amount of memory used by the sds string at object->ptr\n * for a string object. */\nsize_t getStringObjectSdsUsedMemory(robj *o) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    switch(o->encoding) {\n    case OBJ_ENCODING_RAW: return sdsZmallocSize(o->ptr);\n    case OBJ_ENCODING_EMBSTR: return zmalloc_size(o)-sizeof(robj);\n    default: return 0; /* Just integer encoding for now. */\n    }\n}\n\n/* Client.reply list dup and free methods. */\nvoid *dupClientReplyValue(void *o) {\n    clientReplyBlock *old = o;\n    clientReplyBlock *buf = zmalloc(sizeof(clientReplyBlock) + old->size);\n    memcpy(buf, o, sizeof(clientReplyBlock) + old->size);\n    return buf;\n}\n\nvoid freeClientReplyValue(void *o) {\n    zfree(o);\n}\n\nint listMatchObjects(void *a, void *b) {\n    return equalStringObjects(a,b);\n}\n\n/* This function links the client to the global linked list of clients.\n * unlinkClient() does the opposite, among other things. */\nvoid linkClient(client *c) {\n    listAddNodeTail(server.clients,c);\n    /* Note that we remember the linked list node where the client is stored,\n     * this way removing the client in unlinkClient() will not require\n     * a linear scan, but just a constant time operation. */\n    c->client_list_node = listLast(server.clients);\n    uint64_t id = htonu64(c->id);\n    raxInsert(server.clients_index,(unsigned char*)&id,sizeof(id),c,NULL);\n}\n\nclient *createClient(connection *conn) {\n    client *c = zmalloc(sizeof(client));\n\n    /* passing NULL as conn it is possible to create a non connected client.\n     * This is useful since all the commands needs to be executed\n     * in the context of a client. When commands are executed in other\n     * contexts (for instance a Lua script) we need a non connected client. */\n    if (conn) {\n        connNonBlock(conn);\n        connEnableTcpNoDelay(conn);\n        if (server.tcpkeepalive)\n            connKeepAlive(conn,server.tcpkeepalive);\n        connSetReadHandler(conn, readQueryFromClient);\n        connSetPrivateData(conn, c);\n    }\n\n    selectDb(c,0);\n    uint64_t client_id = ++server.next_client_id;\n    c->id = client_id;\n    c->resp = 2;\n    c->conn = conn;\n    c->name = NULL;\n    c->bufpos = 0;\n    c->qb_pos = 0;\n    c->querybuf = sdsempty();\n    c->pending_querybuf = sdsempty();\n    c->querybuf_peak = 0;\n    c->reqtype = 0;\n    c->argc = 0;\n    c->argv = NULL;\n    c->cmd = c->lastcmd = NULL;\n    c->user = DefaultUser;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n    c->sentlen = 0;\n    c->flags = 0;\n    c->ctime = c->lastinteraction = server.unixtime;\n    /* If the default user does not require authentication, the user is\n     * directly authenticated. */\n    c->authenticated = (c->user->flags & USER_FLAG_NOPASS) &&\n                       !(c->user->flags & USER_FLAG_DISABLED);\n    c->replstate = REPL_STATE_NONE;\n    c->repl_put_online_on_ack = 0;\n    c->reploff = 0;\n    c->read_reploff = 0;\n    c->repl_ack_off = 0;\n    c->repl_ack_time = 0;\n    c->slave_listening_port = 0;\n    c->slave_ip[0] = '\\0';\n    c->slave_capa = SLAVE_CAPA_NONE;\n    c->reply = listCreate();\n    c->reply_bytes = 0;\n    c->obuf_soft_limit_reached_time = 0;\n    listSetFreeMethod(c->reply,freeClientReplyValue);\n    listSetDupMethod(c->reply,dupClientReplyValue);\n    c->btype = BLOCKED_NONE;\n    c->bpop.timeout = 0;\n    c->bpop.keys = dictCreate(&objectKeyHeapPointerValueDictType,NULL);\n    c->bpop.target = NULL;\n    c->bpop.xread_group = NULL;\n    c->bpop.xread_consumer = NULL;\n    c->bpop.xread_group_noack = 0;\n    c->bpop.numreplicas = 0;\n    c->bpop.reploffset = 0;\n    c->woff = 0;\n    c->watched_keys = listCreate();\n    c->pubsub_channels = dictCreate(&objectKeyPointerValueDictType,NULL);\n    c->pubsub_patterns = listCreate();\n    c->peerid = NULL;\n    c->client_list_node = NULL;\n    c->client_tracking_redirection = 0;\n    c->client_tracking_prefixes = NULL;\n    c->client_cron_last_memory_usage = 0;\n    c->client_cron_last_memory_type = CLIENT_TYPE_NORMAL;\n    c->auth_callback = NULL;\n    c->auth_callback_privdata = NULL;\n    c->auth_module = NULL;\n    listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);\n    listSetMatchMethod(c->pubsub_patterns,listMatchObjects);\n    if (conn) linkClient(c);\n    initClientMultiState(c);\n    return c;\n}\n\n/* This funciton puts the client in the queue of clients that should write\n * their output buffers to the socket. Note that it does not *yet* install\n * the write handler, to start clients are put in a queue of clients that need\n * to write, so we try to do that before returning in the event loop (see the\n * handleClientsWithPendingWrites() function).\n * If we fail and there is more data to write, compared to what the socket\n * buffers can hold, then we'll really install the handler. */\nvoid clientInstallWriteHandler(client *c) {\n    /* Schedule the client to write the output buffers to the socket only\n     * if not already done and, for slaves, if the slave can actually receive\n     * writes at this stage. */\n    if (!(c->flags & CLIENT_PENDING_WRITE) &&\n        (c->replstate == REPL_STATE_NONE ||\n         (c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack)))\n    {\n        /* Here instead of installing the write handler, we just flag the\n         * client and put it into a list of clients that have something\n         * to write to the socket. This way before re-entering the event\n         * loop, we can try to directly write to the client sockets avoiding\n         * a system call. We'll only really install the write handler if\n         * we'll not be able to write the whole reply at once. */\n        c->flags |= CLIENT_PENDING_WRITE;\n        listAddNodeHead(server.clients_pending_write,c);\n    }\n}\n\n/* This function is called every time we are going to transmit new data\n * to the client. The behavior is the following:\n *\n * If the client should receive new data (normal clients will) the function\n * returns C_OK, and make sure to install the write handler in our event\n * loop so that when the socket is writable new data gets written.\n *\n * If the client should not receive new data, because it is a fake client\n * (used to load AOF in memory), a master or because the setup of the write\n * handler failed, the function returns C_ERR.\n *\n * The function may return C_OK without actually installing the write\n * event handler in the following cases:\n *\n * 1) The event handler should already be installed since the output buffer\n *    already contains something.\n * 2) The client is a slave but not yet online, so we want to just accumulate\n *    writes in the buffer but not actually sending them yet.\n *\n * Typically gets called every time a reply is built, before adding more\n * data to the clients output buffers. If the function returns C_ERR no\n * data should be appended to the output buffers. */\nint prepareClientToWrite(client *c) {\n    /* If it's the Lua client we always return ok without installing any\n     * handler since there is no socket at all. */\n    if (c->flags & (CLIENT_LUA|CLIENT_MODULE)) return C_OK;\n\n    /* CLIENT REPLY OFF / SKIP handling: don't send replies. */\n    if (c->flags & (CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP)) return C_ERR;\n\n    /* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag\n     * is set. */\n    if ((c->flags & CLIENT_MASTER) &&\n        !(c->flags & CLIENT_MASTER_FORCE_REPLY)) return C_ERR;\n\n    if (!c->conn) return C_ERR; /* Fake client for AOF loading. */\n\n    /* Schedule the client to write the output buffers to the socket, unless\n     * it should already be setup to do so (it has already pending data). */\n    if (!clientHasPendingReplies(c)) clientInstallWriteHandler(c);\n\n    /* Authorize the caller to queue in the output buffer of this client. */\n    return C_OK;\n}\n\n/* -----------------------------------------------------------------------------\n * Low level functions to add more data to output buffers.\n * -------------------------------------------------------------------------- */\n\nint _addReplyToBuffer(client *c, const char *s, size_t len) {\n    size_t available = sizeof(c->buf)-c->bufpos;\n\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return C_OK;\n\n    /* If there already are entries in the reply list, we cannot\n     * add anything more to the static buffer. */\n    if (listLength(c->reply) > 0) return C_ERR;\n\n    /* Check that the buffer has enough space available for this string. */\n    if (len > available) return C_ERR;\n\n    memcpy(c->buf+c->bufpos,s,len);\n    c->bufpos+=len;\n    return C_OK;\n}\n\nvoid _addReplyProtoToList(client *c, const char *s, size_t len) {\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;\n\n    listNode *ln = listLast(c->reply);\n    clientReplyBlock *tail = ln? listNodeValue(ln): NULL;\n\n    /* Note that 'tail' may be NULL even if we have a tail node, becuase when\n     * addDeferredMultiBulkLength() is used, it sets a dummy node to NULL just\n     * fo fill it later, when the size of the bulk length is set. */\n\n    /* Append to tail string when possible. */\n    if (tail) {\n        /* Copy the part we can fit into the tail, and leave the rest for a\n         * new node */\n        size_t avail = tail->size - tail->used;\n        size_t copy = avail >= len? len: avail;\n        memcpy(tail->buf + tail->used, s, copy);\n        tail->used += copy;\n        s += copy;\n        len -= copy;\n    }\n    if (len) {\n        /* Create a new node, make sure it is allocated to at\n         * least PROTO_REPLY_CHUNK_BYTES */\n        size_t size = len < PROTO_REPLY_CHUNK_BYTES? PROTO_REPLY_CHUNK_BYTES: len;\n        tail = zmalloc(size + sizeof(clientReplyBlock));\n        /* take over the allocation's internal fragmentation */\n        tail->size = zmalloc_usable(tail) - sizeof(clientReplyBlock);\n        tail->used = len;\n        memcpy(tail->buf, s, len);\n        listAddNodeTail(c->reply, tail);\n        c->reply_bytes += tail->size;\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\n/* Add the object 'obj' string representation to the client output buffer. */\nvoid addReply(client *c, robj *obj) {\n    if (prepareClientToWrite(c) != C_OK) return;\n\n    if (sdsEncodedObject(obj)) {\n        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)\n            _addReplyProtoToList(c,obj->ptr,sdslen(obj->ptr));\n    } else if (obj->encoding == OBJ_ENCODING_INT) {\n        /* For integer encoded strings we just convert it into a string\n         * using our optimized function, and attach the resulting string\n         * to the output buffer. */\n        char buf[32];\n        size_t len = ll2string(buf,sizeof(buf),(long)obj->ptr);\n        if (_addReplyToBuffer(c,buf,len) != C_OK)\n            _addReplyProtoToList(c,buf,len);\n    } else {\n        serverPanic(\"Wrong obj->encoding in addReply()\");\n    }\n}\n\n/* Add the SDS 's' string to the client output buffer, as a side effect\n * the SDS string is freed. */\nvoid addReplySds(client *c, sds s) {\n    if (prepareClientToWrite(c) != C_OK) {\n        /* The caller expects the sds to be free'd. */\n        sdsfree(s);\n        return;\n    }\n    if (_addReplyToBuffer(c,s,sdslen(s)) != C_OK)\n        _addReplyProtoToList(c,s,sdslen(s));\n    sdsfree(s);\n}\n\n/* This low level function just adds whatever protocol you send it to the\n * client buffer, trying the static buffer initially, and using the string\n * of objects if not possible.\n *\n * It is efficient because does not create an SDS object nor an Redis object\n * if not needed. The object will only be created by calling\n * _addReplyProtoToList() if we fail to extend the existing tail object\n * in the list of objects. */\nvoid addReplyProto(client *c, const char *s, size_t len) {\n    if (prepareClientToWrite(c) != C_OK) return;\n    if (_addReplyToBuffer(c,s,len) != C_OK)\n        _addReplyProtoToList(c,s,len);\n}\n\n/* Low level function called by the addReplyError...() functions.\n * It emits the protocol for a Redis error, in the form:\n *\n * -ERRORCODE Error Message<CR><LF>\n *\n * If the error code is already passed in the string 's', the error\n * code provided is used, otherwise the string \"-ERR \" for the generic\n * error code is automatically added. */\nvoid addReplyErrorLength(client *c, const char *s, size_t len) {\n    /* If the string already starts with \"-...\" then the error code\n     * is provided by the caller. Otherwise we use \"-ERR\". */\n    if (!len || s[0] != '-') addReplyProto(c,\"-ERR \",5);\n    addReplyProto(c,s,len);\n    addReplyProto(c,\"\\r\\n\",2);\n\n    /* Sometimes it could be normal that a slave replies to a master with\n     * an error and this function gets called. Actually the error will never\n     * be sent because addReply*() against master clients has no effect...\n     * A notable example is:\n     *\n     *    EVAL 'redis.call(\"incr\",KEYS[1]); redis.call(\"nonexisting\")' 1 x\n     *\n     * Where the master must propagate the first change even if the second\n     * will produce an error. However it is useful to log such events since\n     * they are rare and may hint at errors in a script or a bug in Redis. */\n    int ctype = getClientType(c);\n    if (ctype == CLIENT_TYPE_MASTER || ctype == CLIENT_TYPE_SLAVE || c->id == CLIENT_ID_AOF) {\n        char *to, *from;\n\n        if (c->id == CLIENT_ID_AOF) {\n            to = \"AOF-loading-client\";\n            from = \"server\";\n        } else if (ctype == CLIENT_TYPE_MASTER) {\n            to = \"master\";\n            from = \"replica\";\n        } else {\n            to = \"replica\";\n            from = \"master\";\n        }\n\n        char *cmdname = c->lastcmd ? c->lastcmd->name : \"<unknown>\";\n        serverLog(LL_WARNING,\"== CRITICAL == This %s is sending an error \"\n                             \"to its %s: '%s' after processing the command \"\n                             \"'%s'\", from, to, s, cmdname);\n        if (ctype == CLIENT_TYPE_MASTER && server.repl_backlog &&\n            server.repl_backlog_histlen > 0)\n        {\n            long long dumplen = 256;\n            if (server.repl_backlog_histlen < dumplen)\n                dumplen = server.repl_backlog_histlen;\n\n            /* Identify the first byte to dump. */\n            long long idx =\n              (server.repl_backlog_idx + (server.repl_backlog_size - dumplen)) %\n               server.repl_backlog_size;\n\n            /* Scan the circular buffer to collect 'dumplen' bytes. */\n            sds dump = sdsempty();\n            while(dumplen) {\n                long long thislen =\n                    ((server.repl_backlog_size - idx) < dumplen) ?\n                    (server.repl_backlog_size - idx) : dumplen;\n\n                dump = sdscatrepr(dump,server.repl_backlog+idx,thislen);\n                dumplen -= thislen;\n                idx = 0;\n            }\n\n            /* Finally log such bytes: this is vital debugging info to\n             * understand what happened. */\n            serverLog(LL_WARNING,\"Latest backlog is: '%s'\", dump);\n            sdsfree(dump);\n        }\n        server.stat_unexpected_error_replies++;\n    }\n}\n\nvoid addReplyError(client *c, const char *err) {\n    addReplyErrorLength(c,err,strlen(err));\n}\n\nvoid addReplyErrorFormat(client *c, const char *fmt, ...) {\n    size_t l, j;\n    va_list ap;\n    va_start(ap,fmt);\n    sds s = sdscatvprintf(sdsempty(),fmt,ap);\n    va_end(ap);\n    /* Make sure there are no newlines in the string, otherwise invalid protocol\n     * is emitted. */\n    l = sdslen(s);\n    for (j = 0; j < l; j++) {\n        if (s[j] == '\\r' || s[j] == '\\n') s[j] = ' ';\n    }\n    addReplyErrorLength(c,s,sdslen(s));\n    sdsfree(s);\n}\n\nvoid addReplyStatusLength(client *c, const char *s, size_t len) {\n    addReplyProto(c,\"+\",1);\n    addReplyProto(c,s,len);\n    addReplyProto(c,\"\\r\\n\",2);\n}\n\nvoid addReplyStatus(client *c, const char *status) {\n    addReplyStatusLength(c,status,strlen(status));\n}\n\nvoid addReplyStatusFormat(client *c, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap,fmt);\n    sds s = sdscatvprintf(sdsempty(),fmt,ap);\n    va_end(ap);\n    addReplyStatusLength(c,s,sdslen(s));\n    sdsfree(s);\n}\n\n/* Sometimes we are forced to create a new reply node, and we can't append to\n * the previous one, when that happens, we wanna try to trim the unused space\n * at the end of the last reply node which we won't use anymore. */\nvoid trimReplyUnusedTailSpace(client *c) {\n    listNode *ln = listLast(c->reply);\n    clientReplyBlock *tail = ln? listNodeValue(ln): NULL;\n\n    /* Note that 'tail' may be NULL even if we have a tail node, becuase when\n     * addDeferredMultiBulkLength() is used */\n    if (!tail) return;\n\n    /* We only try to trim the space is relatively high (more than a 1/4 of the\n     * allocation), otherwise there's a high chance realloc will NOP.\n     * Also, to avoid large memmove which happens as part of realloc, we only do\n     * that if the used part is small.  */\n    if (tail->size - tail->used > tail->size / 4 &&\n        tail->used < PROTO_REPLY_CHUNK_BYTES)\n    {\n        size_t old_size = tail->size;\n        tail = zrealloc(tail, tail->used + sizeof(clientReplyBlock));\n        /* take over the allocation's internal fragmentation (at least for\n         * memory usage tracking) */\n        tail->size = zmalloc_usable(tail) - sizeof(clientReplyBlock);\n        c->reply_bytes += tail->size - old_size;\n        listNodeValue(ln) = tail;\n    }\n}\n\n/* Adds an empty object to the reply list that will contain the multi bulk\n * length, which is not known when this function is called. */\nvoid *addReplyDeferredLen(client *c) {\n    /* Note that we install the write event here even if the object is not\n     * ready to be sent, since we are sure that before returning to the\n     * event loop setDeferredAggregateLen() will be called. */\n    if (prepareClientToWrite(c) != C_OK) return NULL;\n    trimReplyUnusedTailSpace(c);\n    listAddNodeTail(c->reply,NULL); /* NULL is our placeholder. */\n    return listLast(c->reply);\n}\n\n/* Populate the length object and try gluing it to the next chunk. */\nvoid setDeferredAggregateLen(client *c, void *node, long length, char prefix) {\n    listNode *ln = (listNode*)node;\n    clientReplyBlock *next;\n    char lenstr[128];\n    size_t lenstr_len = sprintf(lenstr, \"%c%ld\\r\\n\", prefix, length);\n\n    /* Abort when *node is NULL: when the client should not accept writes\n     * we return NULL in addReplyDeferredLen() */\n    if (node == NULL) return;\n    serverAssert(!listNodeValue(ln));\n\n    /* Normally we fill this dummy NULL node, added by addReplyDeferredLen(),\n     * with a new buffer structure containing the protocol needed to specify\n     * the length of the array following. However sometimes when there is\n     * little memory to move, we may instead remove this NULL node, and prefix\n     * our protocol in the node immediately after to it, in order to save a\n     * write(2) syscall later. Conditions needed to do it:\n     *\n     * - The next node is non-NULL,\n     * - It has enough room already allocated\n     * - And not too large (avoid large memmove) */\n    if (ln->next != NULL && (next = listNodeValue(ln->next)) &&\n        next->size - next->used >= lenstr_len &&\n        next->used < PROTO_REPLY_CHUNK_BYTES * 4) {\n        memmove(next->buf + lenstr_len, next->buf, next->used);\n        memcpy(next->buf, lenstr, lenstr_len);\n        next->used += lenstr_len;\n        listDelNode(c->reply,ln);\n    } else {\n        /* Create a new node */\n        clientReplyBlock *buf = zmalloc(lenstr_len + sizeof(clientReplyBlock));\n        /* Take over the allocation's internal fragmentation */\n        buf->size = zmalloc_usable(buf) - sizeof(clientReplyBlock);\n        buf->used = lenstr_len;\n        memcpy(buf->buf, lenstr, lenstr_len);\n        listNodeValue(ln) = buf;\n        c->reply_bytes += buf->size;\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\nvoid setDeferredArrayLen(client *c, void *node, long length) {\n    setDeferredAggregateLen(c,node,length,'*');\n}\n\nvoid setDeferredMapLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '%';\n    if (c->resp == 2) length *= 2;\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\nvoid setDeferredSetLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '~';\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\nvoid setDeferredAttributeLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '|';\n    if (c->resp == 2) length *= 2;\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\nvoid setDeferredPushLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '>';\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\n/* Add a double as a bulk reply */\nvoid addReplyDouble(client *c, double d) {\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        if (c->resp == 2) {\n            addReplyBulkCString(c, d > 0 ? \"inf\" : \"-inf\");\n        } else {\n            addReplyProto(c, d > 0 ? \",inf\\r\\n\" : \",-inf\\r\\n\",\n                              d > 0 ? 6 : 7);\n        }\n    } else {\n        char dbuf[MAX_LONG_DOUBLE_CHARS+3],\n             sbuf[MAX_LONG_DOUBLE_CHARS+32];\n        int dlen, slen;\n        if (c->resp == 2) {\n            dlen = snprintf(dbuf,sizeof(dbuf),\"%.17g\",d);\n            slen = snprintf(sbuf,sizeof(sbuf),\"$%d\\r\\n%s\\r\\n\",dlen,dbuf);\n            addReplyProto(c,sbuf,slen);\n        } else {\n            dlen = snprintf(dbuf,sizeof(dbuf),\",%.17g\\r\\n\",d);\n            addReplyProto(c,dbuf,dlen);\n        }\n    }\n}\n\n/* Add a long double as a bulk reply, but uses a human readable formatting\n * of the double instead of exposing the crude behavior of doubles to the\n * dear user. */\nvoid addReplyHumanLongDouble(client *c, long double d) {\n    if (c->resp == 2) {\n        robj *o = createStringObjectFromLongDouble(d,1);\n        addReplyBulk(c,o);\n        decrRefCount(o);\n    } else {\n        char buf[MAX_LONG_DOUBLE_CHARS];\n        int len = ld2string(buf,sizeof(buf),d,LD_STR_HUMAN);\n        addReplyProto(c,\",\",1);\n        addReplyProto(c,buf,len);\n        addReplyProto(c,\"\\r\\n\",2);\n    }\n}\n\n/* Add a long long as integer reply or bulk len / multi bulk count.\n * Basically this is used to output <prefix><long long><crlf>. */\nvoid addReplyLongLongWithPrefix(client *c, long long ll, char prefix) {\n    char buf[128];\n    int len;\n\n    /* Things like $3\\r\\n or *2\\r\\n are emitted very often by the protocol\n     * so we have a few shared objects to use if the integer is small\n     * like it is most of the times. */\n    if (prefix == '*' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {\n        addReply(c,shared.mbulkhdr[ll]);\n        return;\n    } else if (prefix == '$' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {\n        addReply(c,shared.bulkhdr[ll]);\n        return;\n    }\n\n    buf[0] = prefix;\n    len = ll2string(buf+1,sizeof(buf)-1,ll);\n    buf[len+1] = '\\r';\n    buf[len+2] = '\\n';\n    addReplyProto(c,buf,len+3);\n}\n\nvoid addReplyLongLong(client *c, long long ll) {\n    if (ll == 0)\n        addReply(c,shared.czero);\n    else if (ll == 1)\n        addReply(c,shared.cone);\n    else\n        addReplyLongLongWithPrefix(c,ll,':');\n}\n\nvoid addReplyAggregateLen(client *c, long length, int prefix) {\n    if (prefix == '*' && length < OBJ_SHARED_BULKHDR_LEN)\n        addReply(c,shared.mbulkhdr[length]);\n    else\n        addReplyLongLongWithPrefix(c,length,prefix);\n}\n\nvoid addReplyArrayLen(client *c, long length) {\n    addReplyAggregateLen(c,length,'*');\n}\n\nvoid addReplyMapLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '%';\n    if (c->resp == 2) length *= 2;\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplySetLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '~';\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplyAttributeLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '|';\n    if (c->resp == 2) length *= 2;\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplyPushLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '>';\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplyNull(client *c) {\n    if (c->resp == 2) {\n        addReplyProto(c,\"$-1\\r\\n\",5);\n    } else {\n        addReplyProto(c,\"_\\r\\n\",3);\n    }\n}\n\nvoid addReplyBool(client *c, int b) {\n    if (c->resp == 2) {\n        addReply(c, b ? shared.cone : shared.czero);\n    } else {\n        addReplyProto(c, b ? \"#t\\r\\n\" : \"#f\\r\\n\",4);\n    }\n}\n\n/* A null array is a concept that no longer exists in RESP3. However\n * RESP2 had it, so API-wise we have this call, that will emit the correct\n * RESP2 protocol, however for RESP3 the reply will always be just the\n * Null type \"_\\r\\n\". */\nvoid addReplyNullArray(client *c) {\n    if (c->resp == 2) {\n        addReplyProto(c,\"*-1\\r\\n\",5);\n    } else {\n        addReplyProto(c,\"_\\r\\n\",3);\n    }\n}\n\n/* Create the length prefix of a bulk reply, example: $2234 */\nvoid addReplyBulkLen(client *c, robj *obj) {\n    size_t len = stringObjectLen(obj);\n\n    if (len < OBJ_SHARED_BULKHDR_LEN)\n        addReply(c,shared.bulkhdr[len]);\n    else\n        addReplyLongLongWithPrefix(c,len,'$');\n}\n\n/* Add a Redis Object as a bulk reply */\nvoid addReplyBulk(client *c, robj *obj) {\n    addReplyBulkLen(c,obj);\n    addReply(c,obj);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C buffer as bulk reply */\nvoid addReplyBulkCBuffer(client *c, const void *p, size_t len) {\n    addReplyLongLongWithPrefix(c,len,'$');\n    addReplyProto(c,p,len);\n    addReply(c,shared.crlf);\n}\n\n/* Add sds to reply (takes ownership of sds and frees it) */\nvoid addReplyBulkSds(client *c, sds s)  {\n    addReplyLongLongWithPrefix(c,sdslen(s),'$');\n    addReplySds(c,s);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C null term string as bulk reply */\nvoid addReplyBulkCString(client *c, const char *s) {\n    if (s == NULL) {\n        addReplyNull(c);\n    } else {\n        addReplyBulkCBuffer(c,s,strlen(s));\n    }\n}\n\n/* Add a long long as a bulk reply */\nvoid addReplyBulkLongLong(client *c, long long ll) {\n    char buf[64];\n    int len;\n\n    len = ll2string(buf,64,ll);\n    addReplyBulkCBuffer(c,buf,len);\n}\n\n/* Reply with a verbatim type having the specified extension.\n *\n * The 'ext' is the \"extension\" of the file, actually just a three\n * character type that describes the format of the verbatim string.\n * For instance \"txt\" means it should be interpreted as a text only\n * file by the receiver, \"md \" as markdown, and so forth. Only the\n * three first characters of the extension are used, and if the\n * provided one is shorter than that, the remaining is filled with\n * spaces. */\nvoid addReplyVerbatim(client *c, const char *s, size_t len, const char *ext) {\n    if (c->resp == 2) {\n        addReplyBulkCBuffer(c,s,len);\n    } else {\n        char buf[32];\n        size_t preflen = snprintf(buf,sizeof(buf),\"=%zu\\r\\nxxx:\",len+4);\n        char *p = buf+preflen-4;\n        for (int i = 0; i < 3; i++) {\n            if (*ext == '\\0') {\n                p[i] = ' ';\n            } else {\n                p[i] = *ext++;\n            }\n        }\n        addReplyProto(c,buf,preflen);\n        addReplyProto(c,s,len);\n        addReplyProto(c,\"\\r\\n\",2);\n    }\n}\n\n/* Add an array of C strings as status replies with a heading.\n * This function is typically invoked by from commands that support\n * subcommands in response to the 'help' subcommand. The help array\n * is terminated by NULL sentinel. */\nvoid addReplyHelp(client *c, const char **help) {\n    sds cmd = sdsnew((char*) c->argv[0]->ptr);\n    void *blenp = addReplyDeferredLen(c);\n    int blen = 0;\n\n    sdstoupper(cmd);\n    addReplyStatusFormat(c,\n        \"%s <subcommand> arg arg ... arg. Subcommands are:\",cmd);\n    sdsfree(cmd);\n\n    while (help[blen]) addReplyStatus(c,help[blen++]);\n\n    blen++;  /* Account for the header line(s). */\n    setDeferredArrayLen(c,blenp,blen);\n}\n\n/* Add a suggestive error reply.\n * This function is typically invoked by from commands that support\n * subcommands in response to an unknown subcommand or argument error. */\nvoid addReplySubcommandSyntaxError(client *c) {\n    sds cmd = sdsnew((char*) c->argv[0]->ptr);\n    sdstoupper(cmd);\n    addReplyErrorFormat(c,\n        \"Unknown subcommand or wrong number of arguments for '%s'. Try %s HELP.\",\n        (char*)c->argv[1]->ptr,cmd);\n    sdsfree(cmd);\n}\n\n/* Append 'src' client output buffers into 'dst' client output buffers. \n * This function clears the output buffers of 'src' */\nvoid AddReplyFromClient(client *dst, client *src) {\n    if (prepareClientToWrite(dst) != C_OK)\n        return;\n    addReplyProto(dst,src->buf, src->bufpos);\n    if (listLength(src->reply))\n        listJoin(dst->reply,src->reply);\n    dst->reply_bytes += src->reply_bytes;\n    src->reply_bytes = 0;\n    src->bufpos = 0;\n}\n\n/* Copy 'src' client output buffers into 'dst' client output buffers.\n * The function takes care of freeing the old output buffers of the\n * destination client. */\nvoid copyClientOutputBuffer(client *dst, client *src) {\n    listRelease(dst->reply);\n    dst->sentlen = 0;\n    dst->reply = listDup(src->reply);\n    memcpy(dst->buf,src->buf,src->bufpos);\n    dst->bufpos = src->bufpos;\n    dst->reply_bytes = src->reply_bytes;\n}\n\n/* Return true if the specified client has pending reply buffers to write to\n * the socket. */\nint clientHasPendingReplies(client *c) {\n    return c->bufpos || listLength(c->reply);\n}\n\nvoid clientAcceptHandler(connection *conn) {\n    client *c = connGetPrivateData(conn);\n\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_WARNING,\n                \"Error accepting a client connection: %s\",\n                connGetLastError(conn));\n        freeClientAsync(c);\n        return;\n    }\n\n    /* If the server is running in protected mode (the default) and there\n     * is no password set, nor a specific interface is bound, we don't accept\n     * requests from non loopback interfaces. Instead we try to explain the\n     * user what to do to fix it if needed. */\n    if (server.protected_mode &&\n        server.bindaddr_count == 0 &&\n        DefaultUser->flags & USER_FLAG_NOPASS &&\n        !(c->flags & CLIENT_UNIX_SOCKET))\n    {\n        char cip[NET_IP_STR_LEN+1] = { 0 };\n        connPeerToString(conn, cip, sizeof(cip)-1, NULL);\n\n        if (strcmp(cip,\"127.0.0.1\") && strcmp(cip,\"::1\")) {\n            char *err =\n                \"-DENIED Redis is running in protected mode because protected \"\n                \"mode is enabled, no bind address was specified, no \"\n                \"authentication password is requested to clients. In this mode \"\n                \"connections are only accepted from the loopback interface. \"\n                \"If you want to connect from external computers to Redis you \"\n                \"may adopt one of the following solutions: \"\n                \"1) Just disable protected mode sending the command \"\n                \"'CONFIG SET protected-mode no' from the loopback interface \"\n                \"by connecting to Redis from the same host the server is \"\n                \"running, however MAKE SURE Redis is not publicly accessible \"\n                \"from internet if you do so. Use CONFIG REWRITE to make this \"\n                \"change permanent. \"\n                \"2) Alternatively you can just disable the protected mode by \"\n                \"editing the Redis configuration file, and setting the protected \"\n                \"mode option to 'no', and then restarting the server. \"\n                \"3) If you started the server manually just for testing, restart \"\n                \"it with the '--protected-mode no' option. \"\n                \"4) Setup a bind address or an authentication password. \"\n                \"NOTE: You only need to do one of the above things in order for \"\n                \"the server to start accepting connections from the outside.\\r\\n\";\n            if (connWrite(c->conn,err,strlen(err)) == -1) {\n                /* Nothing to do, Just to avoid the warning... */\n            }\n            server.stat_rejected_conn++;\n            freeClientAsync(c);\n            return;\n        }\n    }\n\n    server.stat_numconnections++;\n    moduleFireServerEvent(REDISMODULE_EVENT_CLIENT_CHANGE,\n                          REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED,\n                          c);\n}\n\n#define MAX_ACCEPTS_PER_CALL 1000\nstatic void acceptCommonHandler(connection *conn, int flags, char *ip) {\n    client *c;\n    UNUSED(ip);\n\n    /* Admission control will happen before a client is created and connAccept()\n     * called, because we don't want to even start transport-level negotiation\n     * if rejected.\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         * Note that for TLS connections, no handshake was done yet so nothing is written\n         * and the connection will just drop.\n         */\n        if (connWrite(conn,err,strlen(err)) == -1) {\n            /* Nothing to do, Just to avoid the warning... */\n        }\n        server.stat_rejected_conn++;\n        connClose(conn);\n        return;\n    }\n\n    /* Create connection and client */\n    if ((c = createClient(conn)) == NULL) {\n        char conninfo[100];\n        serverLog(LL_WARNING,\n            \"Error registering fd event for the new client: %s (conn: %s)\",\n            connGetLastError(conn),\n            connGetInfo(conn, conninfo, sizeof(conninfo)));\n        connClose(conn); /* May be already closed, just ignore errors */\n        return;\n    }\n\n    /* Last chance to keep flags */\n    c->flags |= flags;\n\n    /* Initiate accept.\n     *\n     * Note that connAccept() is free to do two things here:\n     * 1. Call clientAcceptHandler() immediately;\n     * 2. Schedule a future call to clientAcceptHandler().\n     *\n     * Because of that, we must do nothing else afterwards.\n     */\n    if (connAccept(conn, clientAcceptHandler) == C_ERR) {\n        char conninfo[100];\n        if (connGetState(conn) == CONN_STATE_ERROR)\n            serverLog(LL_WARNING,\n                    \"Error accepting a client connection: %s (conn: %s)\",\n                    connGetLastError(conn), connGetInfo(conn, conninfo, sizeof(conninfo)));\n        freeClient(connGetPrivateData(conn));\n        return;\n    }\n}\n\nvoid acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;\n    char cip[NET_IP_STR_LEN];\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(privdata);\n\n    while(max--) {\n        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                serverLog(LL_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        serverLog(LL_VERBOSE,\"Accepted %s:%d\", cip, cport);\n        acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip);\n    }\n}\n\nvoid acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;\n    char cip[NET_IP_STR_LEN];\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(privdata);\n\n    while(max--) {\n        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                serverLog(LL_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        serverLog(LL_VERBOSE,\"Accepted %s:%d\", cip, cport);\n        acceptCommonHandler(connCreateAcceptedTLS(cfd, server.tls_auth_clients),0,cip);\n    }\n}\n\nvoid acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cfd, max = MAX_ACCEPTS_PER_CALL;\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(privdata);\n\n    while(max--) {\n        cfd = anetUnixAccept(server.neterr, fd);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                serverLog(LL_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        serverLog(LL_VERBOSE,\"Accepted connection to %s\", server.unixsocket);\n        acceptCommonHandler(connCreateAcceptedSocket(cfd),CLIENT_UNIX_SOCKET,NULL);\n    }\n}\n\nstatic void freeClientArgv(client *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. */\nvoid disconnectSlaves(void) {\n    while (listLength(server.slaves)) {\n        listNode *ln = listFirst(server.slaves);\n        freeClient((client*)ln->value);\n    }\n}\n\n/* Remove the specified client from global lists where the client could\n * be referenced, not including the Pub/Sub channels.\n * This is used by freeClient() and replicationCacheMaster(). */\nvoid unlinkClient(client *c) {\n    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    /* Certain operations must be done only if the client has an active connection.\n     * If the client was already unlinked or if it's a \"fake client\" the\n     * conn is already set to NULL. */\n    if (c->conn) {\n        /* Remove from the list of active clients. */\n        if (c->client_list_node) {\n            uint64_t id = htonu64(c->id);\n            raxRemove(server.clients_index,(unsigned char*)&id,sizeof(id),NULL);\n            listDelNode(server.clients,c->client_list_node);\n            c->client_list_node = NULL;\n        }\n\n        /* Check if this is a replica waiting for diskless replication (rdb pipe),\n         * in which case it needs to be cleaned from that list */\n        if (c->flags & CLIENT_SLAVE &&\n            c->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&\n            server.rdb_pipe_conns)\n        {\n            int i;\n            for (i=0; i < server.rdb_pipe_numconns; i++) {\n                if (server.rdb_pipe_conns[i] == c->conn) {\n                    rdbPipeWriteHandlerConnRemoved(c->conn);\n                    server.rdb_pipe_conns[i] = NULL;\n                    break;\n                }\n            }\n        }\n        connClose(c->conn);\n        c->conn = NULL;\n    }\n\n    /* Remove from the list of pending writes if needed. */\n    if (c->flags & CLIENT_PENDING_WRITE) {\n        ln = listSearchKey(server.clients_pending_write,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.clients_pending_write,ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n    }\n\n    /* Remove from the list of pending reads if needed. */\n    if (c->flags & CLIENT_PENDING_READ) {\n        ln = listSearchKey(server.clients_pending_read,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.clients_pending_read,ln);\n        c->flags &= ~CLIENT_PENDING_READ;\n    }\n\n    /* When client was just unblocked because of a blocking operation,\n     * remove it from the list of unblocked clients. */\n    if (c->flags & CLIENT_UNBLOCKED) {\n        ln = listSearchKey(server.unblocked_clients,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.unblocked_clients,ln);\n        c->flags &= ~CLIENT_UNBLOCKED;\n    }\n\n    /* Clear the tracking status. */\n    if (c->flags & CLIENT_TRACKING) disableTracking(c);\n}\n\nvoid freeClient(client *c) {\n    listNode *ln;\n\n    /* If a client is protected, yet we need to free it right now, make sure\n     * to at least use asynchronous freeing. */\n    if (c->flags & CLIENT_PROTECTED) {\n        freeClientAsync(c);\n        return;\n    }\n\n    /* For connected clients, call the disconnection event of modules hooks. */\n    if (c->conn) {\n        moduleFireServerEvent(REDISMODULE_EVENT_CLIENT_CHANGE,\n                              REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED,\n                              c);\n    }\n\n    /* Notify module system that this client auth status changed. */\n    moduleNotifyUserChanged(c);\n\n    /* If this client was scheduled for async freeing we need to remove it\n     * from the queue. Note that we need to do this here, because later\n     * we may call replicationCacheMaster() and the client should already\n     * be removed from the list of clients to free. */\n    if (c->flags & CLIENT_CLOSE_ASAP) {\n        ln = listSearchKey(server.clients_to_close,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.clients_to_close,ln);\n    }\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 & CLIENT_MASTER) {\n        serverLog(LL_WARNING,\"Connection with master lost.\");\n        if (!(c->flags & (CLIENT_PROTOCOL_ERROR|CLIENT_BLOCKED))) {\n            c->flags &= ~(CLIENT_CLOSE_ASAP|CLIENT_CLOSE_AFTER_REPLY);\n            replicationCacheMaster(c);\n            return;\n        }\n    }\n\n    /* Log link disconnection with slave */\n    if (getClientType(c) == CLIENT_TYPE_SLAVE) {\n        serverLog(LL_WARNING,\"Connection with replica %s lost.\",\n            replicationGetSlaveName(c));\n    }\n\n    /* Free the query buffer */\n    sdsfree(c->querybuf);\n    sdsfree(c->pending_querybuf);\n    c->querybuf = NULL;\n\n    /* Deallocate structures used to block on blocking ops. */\n    if (c->flags & CLIENT_BLOCKED) unblockClient(c);\n    dictRelease(c->bpop.keys);\n\n    /* UNWATCH all the keys */\n    unwatchAllKeys(c);\n    listRelease(c->watched_keys);\n\n    /* Unsubscribe from all the pubsub channels */\n    pubsubUnsubscribeAllChannels(c,0);\n    pubsubUnsubscribeAllPatterns(c,0);\n    dictRelease(c->pubsub_channels);\n    listRelease(c->pubsub_patterns);\n\n    /* Free data structures. */\n    listRelease(c->reply);\n    freeClientArgv(c);\n\n    /* Unlink the client: this will close the socket, remove the I/O\n     * handlers, and remove references of the client from different\n     * places where active clients may be referenced. */\n    unlinkClient(c);\n\n    /* Master/slave cleanup Case 1:\n     * we lost the connection with a slave. */\n    if (c->flags & CLIENT_SLAVE) {\n        if (c->replstate == SLAVE_STATE_SEND_BULK) {\n            if (c->repldbfd != -1) close(c->repldbfd);\n            if (c->replpreamble) sdsfree(c->replpreamble);\n        }\n        list *l = (c->flags & CLIENT_MONITOR) ? server.monitors : server.slaves;\n        ln = listSearchKey(l,c);\n        serverAssert(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 (getClientType(c) == CLIENT_TYPE_SLAVE && listLength(server.slaves) == 0)\n            server.repl_no_slaves_since = server.unixtime;\n        refreshGoodSlavesCount();\n        /* Fire the replica change modules event. */\n        if (c->replstate == SLAVE_STATE_ONLINE)\n            moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,\n                                  REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE,\n                                  NULL);\n    }\n\n    /* Master/slave cleanup Case 2:\n     * we lost the connection with the master. */\n    if (c->flags & CLIENT_MASTER) replicationHandleMasterDisconnection();\n\n   /* Remove the contribution that this client gave to our\n     * incrementally computed memory usage. */\n    server.stat_clients_type_memory[c->client_cron_last_memory_type] -=\n        c->client_cron_last_memory_usage;\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    zfree(c->argv);\n    freeClientMultiState(c);\n    sdsfree(c->peerid);\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. */\nvoid freeClientAsync(client *c) {\n    /* We need to handle concurrent access to the server.clients_to_close list\n     * only in the freeClientAsync() function, since it's the only function that\n     * may access the list while Redis uses I/O threads. All the other accesses\n     * are in the context of the main thread while the other threads are\n     * idle. */\n    if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;\n    c->flags |= CLIENT_CLOSE_ASAP;\n    if (server.io_threads_num == 1) {\n        /* no need to bother with locking if there's just one thread (the main thread) */\n        listAddNodeTail(server.clients_to_close,c);\n        return;\n    }\n    static pthread_mutex_t async_free_queue_mutex = PTHREAD_MUTEX_INITIALIZER;\n    pthread_mutex_lock(&async_free_queue_mutex);\n    listAddNodeTail(server.clients_to_close,c);\n    pthread_mutex_unlock(&async_free_queue_mutex);\n}\n\n/* Free the clietns marked as CLOSE_ASAP, return the number of clients\n * freed. */\nint freeClientsInAsyncFreeQueue(void) {\n    int freed = listLength(server.clients_to_close);\n    while (listLength(server.clients_to_close)) {\n        listNode *ln = listFirst(server.clients_to_close);\n        client *c = listNodeValue(ln);\n\n        c->flags &= ~CLIENT_CLOSE_ASAP;\n        freeClient(c);\n        listDelNode(server.clients_to_close,ln);\n    }\n    return freed;\n}\n\n/* Return a client by ID, or NULL if the client ID is not in the set\n * of registered clients. Note that \"fake clients\", created with -1 as FD,\n * are not registered clients. */\nclient *lookupClientByID(uint64_t id) {\n    id = htonu64(id);\n    client *c = raxFind(server.clients_index,(unsigned char*)&id,sizeof(id));\n    return (c == raxNotFound) ? NULL : c;\n}\n\n/* Write data in output buffers to client. Return C_OK if the client\n * is still valid after the call, C_ERR if it was freed because of some\n * error.  If handler_installed is set, it will attempt to clear the\n * write event.\n *\n * This function is called by threads, but always with handler_installed\n * set to 0. So when handler_installed is set to 0 the function must be\n * thread safe. */\nint writeToClient(client *c, int handler_installed) {\n    ssize_t nwritten = 0, totwritten = 0;\n    size_t objlen;\n    clientReplyBlock *o;\n\n    while(clientHasPendingReplies(c)) {\n        if (c->bufpos > 0) {\n            nwritten = connWrite(c->conn,c->buf+c->sentlen,c->bufpos-c->sentlen);\n            if (nwritten <= 0) break;\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If the buffer was sent, set bufpos to zero to continue with\n             * the remainder of the reply. */\n            if ((int)c->sentlen == c->bufpos) {\n                c->bufpos = 0;\n                c->sentlen = 0;\n            }\n        } else {\n            o = listNodeValue(listFirst(c->reply));\n            objlen = o->used;\n\n            if (objlen == 0) {\n                c->reply_bytes -= o->size;\n                listDelNode(c->reply,listFirst(c->reply));\n                continue;\n            }\n\n            nwritten = connWrite(c->conn, o->buf + c->sentlen, objlen - c->sentlen);\n            if (nwritten <= 0) break;\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If we fully sent the object on head go to the next one */\n            if (c->sentlen == objlen) {\n                c->reply_bytes -= o->size;\n                listDelNode(c->reply,listFirst(c->reply));\n                c->sentlen = 0;\n                /* If there are no longer objects in the list, we expect\n                 * the count of reply bytes to be exactly zero. */\n                if (listLength(c->reply) == 0)\n                    serverAssert(c->reply_bytes == 0);\n            }\n        }\n        /* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT\n         * bytes, in a single threaded server it's a good idea to serve\n         * other clients as well, even if a very large request comes from\n         * super fast link that is always able to accept data (in real world\n         * scenario think about 'KEYS *' against the loopback interface).\n         *\n         * However if we are over the maxmemory limit we ignore that and\n         * just deliver as much data as it is possible to deliver.\n         *\n         * Moreover, we also send as much as possible if the client is\n         * a slave or a monitor (otherwise, on high-speed traffic, the\n         * replication/output buffer will grow indefinitely) */\n        if (totwritten > NET_MAX_WRITES_PER_EVENT &&\n            (server.maxmemory == 0 ||\n             zmalloc_used_memory() < server.maxmemory) &&\n            !(c->flags & CLIENT_SLAVE)) break;\n    }\n    server.stat_net_output_bytes += totwritten;\n    if (nwritten == -1) {\n        if (connGetState(c->conn) == CONN_STATE_CONNECTED) {\n            nwritten = 0;\n        } else {\n            serverLog(LL_VERBOSE,\n                \"Error writing to client: %s\", connGetLastError(c->conn));\n            freeClientAsync(c);\n            return C_ERR;\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 & CLIENT_MASTER)) c->lastinteraction = server.unixtime;\n    }\n    if (!clientHasPendingReplies(c)) {\n        c->sentlen = 0;\n        /* Note that writeToClient() is called in a threaded way, but\n         * adDeleteFileEvent() is not thread safe: however writeToClient()\n         * is always called with handler_installed set to 0 from threads\n         * so we are fine. */\n        if (handler_installed) connSetWriteHandler(c->conn, NULL);\n\n        /* Close connection after entire reply has been sent. */\n        if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {\n            freeClientAsync(c);\n            return C_ERR;\n        }\n    }\n    return C_OK;\n}\n\n/* Write event handler. Just send data to the client. */\nvoid sendReplyToClient(connection *conn) {\n    client *c = connGetPrivateData(conn);\n    writeToClient(c,1);\n}\n\n/* This function is called just before entering the event loop, in the hope\n * we can just write the replies to the client output buffer without any\n * need to use a syscall in order to install the writable event handler,\n * get it called, and so forth. */\nint handleClientsWithPendingWrites(void) {\n    listIter li;\n    listNode *ln;\n    int processed = listLength(server.clients_pending_write);\n\n    listRewind(server.clients_pending_write,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n        listDelNode(server.clients_pending_write,ln);\n\n        /* If a client is protected, don't do anything,\n         * that may trigger write error or recreate handler. */\n        if (c->flags & CLIENT_PROTECTED) continue;\n\n        /* Try to write buffers to the client socket. */\n        if (writeToClient(c,0) == C_ERR) continue;\n\n        /* If after the synchronous writes above we still have data to\n         * output to the client, we need to install the writable handler. */\n        if (clientHasPendingReplies(c)) {\n            int ae_barrier = 0;\n            /* For the fsync=always policy, we want that a given FD is never\n             * served for reading and writing in the same event loop iteration,\n             * so that in the middle of receiving the query, and serving it\n             * to the client, we'll call beforeSleep() that will do the\n             * actual fsync of AOF to disk. the write barrier ensures that. */\n            if (server.aof_state == AOF_ON &&\n                server.aof_fsync == AOF_FSYNC_ALWAYS)\n            {\n                ae_barrier = 1;\n            }\n            if (connSetWriteHandlerWithBarrier(c->conn, sendReplyToClient, ae_barrier) == C_ERR) {\n                freeClientAsync(c);\n            }\n        }\n    }\n    return processed;\n}\n\n/* resetClient prepare the client to process the next command */\nvoid resetClient(client *c) {\n    redisCommandProc *prevcmd = c->cmd ? c->cmd->proc : NULL;\n\n    freeClientArgv(c);\n    c->reqtype = 0;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n\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 & CLIENT_MULTI) && prevcmd != askingCommand)\n        c->flags &= ~CLIENT_ASKING;\n\n    /* We do the same for the CACHING command as well. It also affects\n     * the next command or transaction executed, in a way very similar\n     * to ASKING. */\n    if (!(c->flags & CLIENT_MULTI) && prevcmd != clientCommand)\n        c->flags &= ~CLIENT_TRACKING_CACHING;\n\n    /* Remove the CLIENT_REPLY_SKIP flag if any so that the reply\n     * to the next command will be sent, but set the flag if the command\n     * we just processed was \"CLIENT REPLY SKIP\". */\n    c->flags &= ~CLIENT_REPLY_SKIP;\n    if (c->flags & CLIENT_REPLY_SKIP_NEXT) {\n        c->flags |= CLIENT_REPLY_SKIP;\n        c->flags &= ~CLIENT_REPLY_SKIP_NEXT;\n    }\n}\n\n/* This funciton is used when we want to re-enter the event loop but there\n * is the risk that the client we are dealing with will be freed in some\n * way. This happens for instance in:\n *\n * * DEBUG RELOAD and similar.\n * * When a Lua script is in -BUSY state.\n *\n * So the function will protect the client by doing two things:\n *\n * 1) It removes the file events. This way it is not possible that an\n *    error is signaled on the socket, freeing the client.\n * 2) Moreover it makes sure that if the client is freed in a different code\n *    path, it is not really released, but only marked for later release. */\nvoid protectClient(client *c) {\n    c->flags |= CLIENT_PROTECTED;\n    connSetReadHandler(c->conn,NULL);\n    connSetWriteHandler(c->conn,NULL);\n}\n\n/* This will undo the client protection done by protectClient() */\nvoid unprotectClient(client *c) {\n    if (c->flags & CLIENT_PROTECTED) {\n        c->flags &= ~CLIENT_PROTECTED;\n        connSetReadHandler(c->conn,readQueryFromClient);\n        if (clientHasPendingReplies(c)) clientInstallWriteHandler(c);\n    }\n}\n\n/* Like processMultibulkBuffer(), but for the inline protocol instead of RESP,\n * this function consumes the client query buffer and creates a command ready\n * to be executed inside the client structure. Returns C_OK if the command\n * is ready to be executed, or C_ERR if there is still protocol to read to\n * have a well formed command. The function also returns C_ERR when there is\n * a protocol error: in such a case the client structure is setup to reply\n * with the error and close the connection. */\nint processInlineBuffer(client *c) {\n    char *newline;\n    int argc, j, linefeed_chars = 1;\n    sds *argv, aux;\n    size_t querylen;\n\n    /* Search for end of line */\n    newline = strchr(c->querybuf+c->qb_pos,'\\n');\n\n    /* Nothing to do without a \\r\\n */\n    if (newline == NULL) {\n        if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {\n            addReplyError(c,\"Protocol error: too big inline request\");\n            setProtocolError(\"too big inline request\",c);\n        }\n        return C_ERR;\n    }\n\n    /* Handle the \\r\\n case. */\n    if (newline && newline != c->querybuf+c->qb_pos && *(newline-1) == '\\r')\n        newline--, linefeed_chars++;\n\n    /* Split the input buffer up to the \\r\\n */\n    querylen = newline-(c->querybuf+c->qb_pos);\n    aux = sdsnewlen(c->querybuf+c->qb_pos,querylen);\n    argv = sdssplitargs(aux,&argc);\n    sdsfree(aux);\n    if (argv == NULL) {\n        addReplyError(c,\"Protocol error: unbalanced quotes in request\");\n        setProtocolError(\"unbalanced quotes in inline request\",c);\n        return C_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 && getClientType(c) == CLIENT_TYPE_SLAVE)\n        c->repl_ack_time = server.unixtime;\n\n    /* Move querybuffer position to the next query in the buffer. */\n    c->qb_pos += querylen+linefeed_chars;\n\n    /* Setup argv array on client structure */\n    if (argc) {\n        if (c->argv) zfree(c->argv);\n        c->argv = zmalloc(sizeof(robj*)*argc);\n    }\n\n    /* Create redis objects for all arguments. */\n    for (c->argc = 0, j = 0; j < argc; j++) {\n        c->argv[c->argc] = createObject(OBJ_STRING,argv[j]);\n        c->argc++;\n    }\n    zfree(argv);\n    return C_OK;\n}\n\n/* Helper function. Record protocol erro details in server log,\n * and set the client as CLIENT_CLOSE_AFTER_REPLY and\n * CLIENT_PROTOCOL_ERROR. */\n#define PROTO_DUMP_LEN 128\nstatic void setProtocolError(const char *errstr, client *c) {\n    if (server.verbosity <= LL_VERBOSE) {\n        sds client = catClientInfoString(sdsempty(),c);\n\n        /* Sample some protocol to given an idea about what was inside. */\n        char buf[256];\n        if (sdslen(c->querybuf)-c->qb_pos < PROTO_DUMP_LEN) {\n            snprintf(buf,sizeof(buf),\"Query buffer during protocol error: '%s'\", c->querybuf+c->qb_pos);\n        } else {\n            snprintf(buf,sizeof(buf),\"Query buffer during protocol error: '%.*s' (... more %zu bytes ...) '%.*s'\", PROTO_DUMP_LEN/2, c->querybuf+c->qb_pos, sdslen(c->querybuf)-c->qb_pos-PROTO_DUMP_LEN, PROTO_DUMP_LEN/2, c->querybuf+sdslen(c->querybuf)-PROTO_DUMP_LEN/2);\n        }\n\n        /* Remove non printable chars. */\n        char *p = buf;\n        while (*p != '\\0') {\n            if (!isprint(*p)) *p = '.';\n            p++;\n        }\n\n        /* Log all the client and protocol info. */\n        serverLog(LL_VERBOSE,\n            \"Protocol error (%s) from client: %s. %s\", errstr, client, buf);\n        sdsfree(client);\n    }\n    c->flags |= (CLIENT_CLOSE_AFTER_REPLY|CLIENT_PROTOCOL_ERROR);\n}\n\n/* Process the query buffer for client 'c', setting up the client argument\n * vector for command execution. Returns C_OK if after running the function\n * the client has a well-formed ready to be processed command, otherwise\n * C_ERR if there is still to read more buffer to get the full command.\n * The function also returns C_ERR when there is a protocol error: in such a\n * case the client structure is setup to reply with the error and close\n * the connection.\n *\n * This function is called if processInputBuffer() detects that the next\n * command is in RESP format, so the first byte in the command is found\n * to be '*'. Otherwise for inline commands processInlineBuffer() is called. */\nint processMultibulkBuffer(client *c) {\n    char *newline = NULL;\n    int ok;\n    long long ll;\n\n    if (c->multibulklen == 0) {\n        /* The client should have been reset */\n        serverAssertWithInfo(c,NULL,c->argc == 0);\n\n        /* Multi bulk length cannot be read without a \\r\\n */\n        newline = strchr(c->querybuf+c->qb_pos,'\\r');\n        if (newline == NULL) {\n            if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {\n                addReplyError(c,\"Protocol error: too big mbulk count string\");\n                setProtocolError(\"too big mbulk count string\",c);\n            }\n            return C_ERR;\n        }\n\n        /* Buffer should also contain \\n */\n        if (newline-(c->querybuf+c->qb_pos) > (ssize_t)(sdslen(c->querybuf)-c->qb_pos-2))\n            return C_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        serverAssertWithInfo(c,NULL,c->querybuf[c->qb_pos] == '*');\n        ok = string2ll(c->querybuf+1+c->qb_pos,newline-(c->querybuf+1+c->qb_pos),&ll);\n        if (!ok || ll > 1024*1024) {\n            addReplyError(c,\"Protocol error: invalid multibulk length\");\n            setProtocolError(\"invalid mbulk count\",c);\n            return C_ERR;\n        }\n\n        c->qb_pos = (newline-c->querybuf)+2;\n\n        if (ll <= 0) return C_OK;\n\n        c->multibulklen = ll;\n\n        /* Setup argv array on client structure */\n        if (c->argv) zfree(c->argv);\n        c->argv = zmalloc(sizeof(robj*)*c->multibulklen);\n    }\n\n    serverAssertWithInfo(c,NULL,c->multibulklen > 0);\n    while(c->multibulklen) {\n        /* Read bulk length if unknown */\n        if (c->bulklen == -1) {\n            newline = strchr(c->querybuf+c->qb_pos,'\\r');\n            if (newline == NULL) {\n                if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {\n                    addReplyError(c,\n                        \"Protocol error: too big bulk count string\");\n                    setProtocolError(\"too big bulk count string\",c);\n                    return C_ERR;\n                }\n                break;\n            }\n\n            /* Buffer should also contain \\n */\n            if (newline-(c->querybuf+c->qb_pos) > (ssize_t)(sdslen(c->querybuf)-c->qb_pos-2))\n                break;\n\n            if (c->querybuf[c->qb_pos] != '$') {\n                addReplyErrorFormat(c,\n                    \"Protocol error: expected '$', got '%c'\",\n                    c->querybuf[c->qb_pos]);\n                setProtocolError(\"expected $ but got something else\",c);\n                return C_ERR;\n            }\n\n            ok = string2ll(c->querybuf+c->qb_pos+1,newline-(c->querybuf+c->qb_pos+1),&ll);\n            if (!ok || ll < 0 || ll > server.proto_max_bulk_len) {\n                addReplyError(c,\"Protocol error: invalid bulk length\");\n                setProtocolError(\"invalid bulk length\",c);\n                return C_ERR;\n            }\n\n            c->qb_pos = newline-c->querybuf+2;\n            if (ll >= PROTO_MBULK_BIG_ARG) {\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                 *\n                 * But only when the data we have not parsed is less than\n                 * or equal to ll+2. If the data length is greater than\n                 * ll+2, trimming querybuf is just a waste of time, because\n                 * at this time the querybuf contains not only our bulk. */\n                if (sdslen(c->querybuf)-c->qb_pos <= (size_t)ll+2) {\n                    sdsrange(c->querybuf,c->qb_pos,-1);\n                    c->qb_pos = 0;\n                    /* Hint the sds library about the amount of bytes this string is\n                     * going to contain. */\n                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2);\n                }\n            }\n            c->bulklen = ll;\n        }\n\n        /* Read bulk argument */\n        if (sdslen(c->querybuf)-c->qb_pos < (size_t)(c->bulklen+2)) {\n            /* Not enough data (+2 == trailing \\r\\n) */\n            break;\n        } else {\n            /* Optimization: if the buffer contains JUST our bulk element\n             * instead of creating a new object by *copying* the sds we\n             * just use the current sds string. */\n            if (c->qb_pos == 0 &&\n                c->bulklen >= PROTO_MBULK_BIG_ARG &&\n                sdslen(c->querybuf) == (size_t)(c->bulklen+2))\n            {\n                c->argv[c->argc++] = createObject(OBJ_STRING,c->querybuf);\n                sdsIncrLen(c->querybuf,-2); /* remove CRLF */\n                /* Assume that if we saw a fat argument we'll see another one\n                 * likely... */\n                c->querybuf = sdsnewlen(SDS_NOINIT,c->bulklen+2);\n                sdsclear(c->querybuf);\n            } else {\n                c->argv[c->argc++] =\n                    createStringObject(c->querybuf+c->qb_pos,c->bulklen);\n                c->qb_pos += c->bulklen+2;\n            }\n            c->bulklen = -1;\n            c->multibulklen--;\n        }\n    }\n\n    /* We're done when c->multibulk == 0 */\n    if (c->multibulklen == 0) return C_OK;\n\n    /* Still not ready to process the command */\n    return C_ERR;\n}\n\n/* Perform necessary tasks after a command was executed:\n *\n * 1. The client is reset unless there are reasons to avoid doing it.\n * 2. In the case of master clients, the replication offset is updated.\n * 3. Propagate commands we got from our master to replicas down the line. */\nvoid commandProcessed(client *c) {\n    int cmd_is_ping = c->cmd && c->cmd->proc == pingCommand;\n    long long prev_offset = c->reploff;\n    if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {\n        /* Update the applied replication offset of our master. */\n        c->reploff = c->read_reploff - sdslen(c->querybuf) + c->qb_pos;\n    }\n\n    /* Don't reset the client structure for clients blocked in a\n     * module blocking command, so that the reply callback will\n     * still be able to access the client argv and argc field.\n     * The client will be reset in unblockClientFromModule(). */\n    if (!(c->flags & CLIENT_BLOCKED) ||\n        c->btype != BLOCKED_MODULE)\n    {\n        resetClient(c);\n    }\n\n    /* If the client is a master we need to compute the difference\n     * between the applied offset before and after processing the buffer,\n     * to understand how much of the replication stream was actually\n     * applied to the master state: this quantity, and its corresponding\n     * part of the replication stream, will be propagated to the\n     * sub-replicas and to the replication backlog. */\n    if (c->flags & CLIENT_MASTER) {\n        long long applied = c->reploff - prev_offset;\n        long long prev_master_repl_meaningful_offset = server.master_repl_meaningful_offset;\n        if (applied) {\n            replicationFeedSlavesFromMasterStream(server.slaves,\n                    c->pending_querybuf, applied);\n            sdsrange(c->pending_querybuf,applied,-1);\n        }\n        /* The server.master_repl_meaningful_offset variable represents\n         * the offset of the replication stream without the pending PINGs. */\n        if (cmd_is_ping)\n            server.master_repl_meaningful_offset = prev_master_repl_meaningful_offset;\n    }\n}\n\n/* This function calls processCommand(), but also performs a few sub tasks\n * for the client that are useful in that context:\n *\n * 1. It sets the current client to the client 'c'.\n * 2. calls commandProcessed() if the command was handled.\n *\n * The function returns C_ERR in case the client was freed as a side effect\n * of processing the command, otherwise C_OK is returned. */\nint processCommandAndResetClient(client *c) {\n    int deadclient = 0;\n    server.current_client = c;\n    if (processCommand(c) == C_OK) {\n        commandProcessed(c);\n    }\n    if (server.current_client == NULL) deadclient = 1;\n    server.current_client = NULL;\n    /* freeMemoryIfNeeded may flush slave output buffers. This may\n     * result into a slave, that may be the active client, to be\n     * freed. */\n    return deadclient ? C_ERR : C_OK;\n}\n\n/* This function is called every time, in the client structure 'c', there is\n * more query buffer to process, because we read more data from the socket\n * or because a client was blocked and later reactivated, so there could be\n * pending query buffer, already representing a full command, to process. */\nvoid processInputBuffer(client *c) {\n    /* Keep processing while there is something in the input buffer */\n    while(c->qb_pos < sdslen(c->querybuf)) {\n        /* Return if clients are paused. */\n        if (!(c->flags & CLIENT_SLAVE) && clientsArePaused()) break;\n\n        /* Immediately abort if the client is in the middle of something. */\n        if (c->flags & CLIENT_BLOCKED) break;\n\n        /* Don't process more buffers from clients that have already pending\n         * commands to execute in c->argv. */\n        if (c->flags & CLIENT_PENDING_COMMAND) break;\n\n        /* Don't process input from the master while there is a busy script\n         * condition on the slave. We want just to accumulate the replication\n         * stream (instead of replying -BUSY like we do with other clients) and\n         * later resume the processing. */\n        if (server.lua_timedout && c->flags & CLIENT_MASTER) break;\n\n        /* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is\n         * written to the client. Make sure to not let the reply grow after\n         * this flag has been set (i.e. don't process more commands).\n         *\n         * The same applies for clients we want to terminate ASAP. */\n        if (c->flags & (CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP)) break;\n\n        /* Determine request type when unknown. */\n        if (!c->reqtype) {\n            if (c->querybuf[c->qb_pos] == '*') {\n                c->reqtype = PROTO_REQ_MULTIBULK;\n            } else {\n                c->reqtype = PROTO_REQ_INLINE;\n            }\n        }\n\n        if (c->reqtype == PROTO_REQ_INLINE) {\n            if (processInlineBuffer(c) != C_OK) break;\n            /* If the Gopher mode and we got zero or one argument, process\n             * the request in Gopher mode. */\n            if (server.gopher_enabled &&\n                ((c->argc == 1 && ((char*)(c->argv[0]->ptr))[0] == '/') ||\n                  c->argc == 0))\n            {\n                processGopherRequest(c);\n                resetClient(c);\n                c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n                break;\n            }\n        } else if (c->reqtype == PROTO_REQ_MULTIBULK) {\n            if (processMultibulkBuffer(c) != C_OK) break;\n        } else {\n            serverPanic(\"Unknown request type\");\n        }\n\n        /* Multibulk processing could see a <= 0 length. */\n        if (c->argc == 0) {\n            resetClient(c);\n        } else {\n            /* If we are in the context of an I/O thread, we can't really\n             * execute the command here. All we can do is to flag the client\n             * as one that needs to process the command. */\n            if (c->flags & CLIENT_PENDING_READ) {\n                c->flags |= CLIENT_PENDING_COMMAND;\n                break;\n            }\n\n            /* We are finally ready to execute the command. */\n            if (processCommandAndResetClient(c) == C_ERR) {\n                /* If the client is no longer valid, we avoid exiting this\n                 * loop and trimming the client buffer later. So we return\n                 * ASAP in that case. */\n                return;\n            }\n        }\n    }\n\n    /* Trim to pos */\n    if (c->qb_pos) {\n        sdsrange(c->querybuf,c->qb_pos,-1);\n        c->qb_pos = 0;\n    }\n}\n\nvoid readQueryFromClient(connection *conn) {\n    client *c = connGetPrivateData(conn);\n    int nread, readlen;\n    size_t qblen;\n\n    /* Check if we want to read from the client later when exiting from\n     * the event loop. This is the case if threaded I/O is enabled. */\n    if (postponeClientRead(c)) return;\n\n    readlen = PROTO_IOBUF_LEN;\n    /* If this is a multi bulk request, and we are processing a bulk reply\n     * that is large enough, try to maximize the probability that the query\n     * buffer contains exactly the SDS string representing the object, even\n     * at the risk of requiring more read(2) calls. This way the function\n     * processMultiBulkBuffer() can avoid copying buffers to create the\n     * Redis Object representing the argument. */\n    if (c->reqtype == PROTO_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1\n        && c->bulklen >= PROTO_MBULK_BIG_ARG)\n    {\n        ssize_t remaining = (size_t)(c->bulklen+2)-sdslen(c->querybuf);\n\n        /* Note that the 'remaining' variable may be zero in some edge case,\n         * for example once we resume a blocked client after CLIENT PAUSE. */\n        if (remaining > 0 && remaining < readlen) readlen = remaining;\n    }\n\n    qblen = sdslen(c->querybuf);\n    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;\n    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);\n    nread = connRead(c->conn, c->querybuf+qblen, readlen);\n    if (nread == -1) {\n        if (connGetState(conn) == CONN_STATE_CONNECTED) {\n            return;\n        } else {\n            serverLog(LL_VERBOSE, \"Reading from client: %s\",connGetLastError(c->conn));\n            freeClientAsync(c);\n            return;\n        }\n    } else if (nread == 0) {\n        serverLog(LL_VERBOSE, \"Client closed connection\");\n        freeClientAsync(c);\n        return;\n    } else if (c->flags & CLIENT_MASTER) {\n        /* Append the query buffer to the pending (not applied) buffer\n         * of the master. We'll use this buffer later in order to have a\n         * copy of the string applied by the last command executed. */\n        c->pending_querybuf = sdscatlen(c->pending_querybuf,\n                                        c->querybuf+qblen,nread);\n    }\n\n    sdsIncrLen(c->querybuf,nread);\n    c->lastinteraction = server.unixtime;\n    if (c->flags & CLIENT_MASTER) c->read_reploff += nread;\n    server.stat_net_input_bytes += nread;\n    if (sdslen(c->querybuf) > server.client_max_querybuf_len) {\n        sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();\n\n        bytes = sdscatrepr(bytes,c->querybuf,64);\n        serverLog(LL_WARNING,\"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)\", ci, bytes);\n        sdsfree(ci);\n        sdsfree(bytes);\n        freeClientAsync(c);\n        return;\n    }\n\n    /* There is more data in the client input buffer, continue parsing it\n     * in case to check if there is a full command to execute. */\n     processInputBuffer(c);\n}\n\nvoid getClientsMaxBuffers(unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer) {\n    client *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/* A Redis \"Peer ID\" is a colon separated ip:port pair.\n * For IPv4 it's in the form x.y.z.k:port, example: \"127.0.0.1:1234\".\n * For IPv6 addresses we use [] around the IP part, like in \"[::1]:1234\".\n * For Unix sockets we use path:0, like in \"/tmp/redis:0\".\n *\n * A Peer ID always fits inside a buffer of NET_PEER_ID_LEN bytes, including\n * the null term.\n *\n * On failure the function still populates 'peerid' with the \"?:0\" string\n * in case you want to relax error checking or need to display something\n * anyway (see anetPeerToString implementation for more info). */\nvoid genClientPeerId(client *client, char *peerid,\n                            size_t peerid_len) {\n    if (client->flags & CLIENT_UNIX_SOCKET) {\n        /* Unix socket client. */\n        snprintf(peerid,peerid_len,\"%s:0\",server.unixsocket);\n    } else {\n        /* TCP client. */\n        connFormatPeer(client->conn,peerid,peerid_len);\n    }\n}\n\n/* This function returns the client peer id, by creating and caching it\n * if client->peerid is NULL, otherwise returning the cached value.\n * The Peer ID never changes during the life of the client, however it\n * is expensive to compute. */\nchar *getClientPeerId(client *c) {\n    char peerid[NET_PEER_ID_LEN];\n\n    if (c->peerid == NULL) {\n        genClientPeerId(c,peerid,sizeof(peerid));\n        c->peerid = sdsnew(peerid);\n    }\n    return c->peerid;\n}\n\n/* Concatenate a string representing the state of a client in an human\n * readable format, into the sds string 's'. */\nsds catClientInfoString(sds s, client *client) {\n    char flags[16], events[3], conninfo[CONN_INFO_LEN], *p;\n\n    p = flags;\n    if (client->flags & CLIENT_SLAVE) {\n        if (client->flags & CLIENT_MONITOR)\n            *p++ = 'O';\n        else\n            *p++ = 'S';\n    }\n    if (client->flags & CLIENT_MASTER) *p++ = 'M';\n    if (client->flags & CLIENT_PUBSUB) *p++ = 'P';\n    if (client->flags & CLIENT_MULTI) *p++ = 'x';\n    if (client->flags & CLIENT_BLOCKED) *p++ = 'b';\n    if (client->flags & CLIENT_TRACKING) *p++ = 't';\n    if (client->flags & CLIENT_TRACKING_BROKEN_REDIR) *p++ = 'R';\n    if (client->flags & CLIENT_DIRTY_CAS) *p++ = 'd';\n    if (client->flags & CLIENT_CLOSE_AFTER_REPLY) *p++ = 'c';\n    if (client->flags & CLIENT_UNBLOCKED) *p++ = 'u';\n    if (client->flags & CLIENT_CLOSE_ASAP) *p++ = 'A';\n    if (client->flags & CLIENT_UNIX_SOCKET) *p++ = 'U';\n    if (client->flags & CLIENT_READONLY) *p++ = 'r';\n    if (p == flags) *p++ = 'N';\n    *p++ = '\\0';\n\n    p = events;\n    if (client->conn) {\n        if (connHasReadHandler(client->conn)) *p++ = 'r';\n        if (connHasWriteHandler(client->conn)) *p++ = 'w';\n    }\n    *p = '\\0';\n    return sdscatfmt(s,\n        \"id=%U addr=%s %s 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 user=%s\",\n        (unsigned long long) client->id,\n        getClientPeerId(client),\n        connGetInfo(client->conn, conninfo, sizeof(conninfo)),\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 & CLIENT_MULTI) ? client->mstate.count : -1,\n        (unsigned long long) sdslen(client->querybuf),\n        (unsigned long long) sdsavail(client->querybuf),\n        (unsigned long long) client->bufpos,\n        (unsigned long long) listLength(client->reply),\n        (unsigned long long) getClientOutputBufferMemoryUsage(client),\n        events,\n        client->lastcmd ? client->lastcmd->name : \"NULL\",\n        client->user ? client->user->name : \"(superuser)\");\n}\n\nsds getAllClientsInfoString(int type) {\n    listNode *ln;\n    listIter li;\n    client *client;\n    sds o = sdsnewlen(SDS_NOINIT,200*listLength(server.clients));\n    sdsclear(o);\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client = listNodeValue(ln);\n        if (type != -1 && getClientType(client) != type) continue;\n        o = catClientInfoString(o,client);\n        o = sdscatlen(o,\"\\n\",1);\n    }\n    return o;\n}\n\n/* This function implements CLIENT SETNAME, including replying to the\n * user with an error if the charset is wrong (in that case C_ERR is\n * returned). If the function succeeeded C_OK is returned, and it's up\n * to the caller to send a reply if needed.\n *\n * Setting an empty string as name has the effect of unsetting the\n * currently set name: the client will remain unnamed.\n *\n * This function is also used to implement the HELLO SETNAME option. */\nint clientSetNameOrReply(client *c, robj *name) {\n    int len = sdslen(name->ptr);\n    char *p = name->ptr;\n\n    /* Setting the client name to an empty string actually removes\n     * the current name. */\n    if (len == 0) {\n        if (c->name) decrRefCount(c->name);\n        c->name = NULL;\n        return C_OK;\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 (int 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 C_ERR;\n        }\n    }\n    if (c->name) decrRefCount(c->name);\n    c->name = name;\n    incrRefCount(name);\n    return C_OK;\n}\n\nvoid clientCommand(client *c) {\n    listNode *ln;\n    listIter li;\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ID                     -- Return the ID of the current connection.\",\n\"GETNAME                -- Return the name of the current connection.\",\n\"KILL <ip:port>         -- Kill connection made from <ip:port>.\",\n\"KILL <option> <value> [option value ...] -- Kill connections. Options are:\",\n\"     ADDR <ip:port>                      -- Kill connection made from <ip:port>\",\n\"     TYPE (normal|master|replica|pubsub) -- Kill connections by type.\",\n\"     USER <username>   -- Kill connections authenticated with such user.\",\n\"     SKIPME (yes|no)   -- Skip killing current connection (default: yes).\",\n\"LIST [options ...]     -- Return information about client connections. Options:\",\n\"     TYPE (normal|master|replica|pubsub) -- Return clients of specified type.\",\n\"PAUSE <timeout>        -- Suspend all Redis clients for <timout> milliseconds.\",\n\"REPLY (on|off|skip)    -- Control the replies sent to the current connection.\",\n\"SETNAME <name>         -- Assign the name <name> to the current connection.\",\n\"UNBLOCK <clientid> [TIMEOUT|ERROR] -- Unblock the specified blocked client.\",\n\"TRACKING (on|off) [REDIRECT <id>] [BCAST] [PREFIX first] [PREFIX second] [OPTIN] [OPTOUT]... -- Enable client keys tracking for client side caching.\",\n\"GETREDIR               -- Return the client ID we are redirecting to when tracking is enabled.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"id\") && c->argc == 2) {\n        /* CLIENT ID */\n        addReplyLongLong(c,c->id);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"list\")) {\n        /* CLIENT LIST */\n        int type = -1;\n        if (c->argc == 4 && !strcasecmp(c->argv[2]->ptr,\"type\")) {\n            type = getClientTypeByName(c->argv[3]->ptr);\n            if (type == -1) {\n                addReplyErrorFormat(c,\"Unknown client type '%s'\",\n                    (char*) c->argv[3]->ptr);\n                return;\n             }\n        } else if (c->argc != 2) {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n        sds o = getAllClientsInfoString(type);\n        addReplyVerbatim(c,o,sdslen(o),\"txt\");\n        sdsfree(o);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reply\") && c->argc == 3) {\n        /* CLIENT REPLY ON|OFF|SKIP */\n        if (!strcasecmp(c->argv[2]->ptr,\"on\")) {\n            c->flags &= ~(CLIENT_REPLY_SKIP|CLIENT_REPLY_OFF);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"off\")) {\n            c->flags |= CLIENT_REPLY_OFF;\n        } else if (!strcasecmp(c->argv[2]->ptr,\"skip\")) {\n            if (!(c->flags & CLIENT_REPLY_OFF))\n                c->flags |= CLIENT_REPLY_SKIP_NEXT;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"kill\")) {\n        /* CLIENT KILL <ip:port>\n         * CLIENT KILL <option> [value] ... <option> [value] */\n        char *addr = NULL;\n        user *user = NULL;\n        int type = -1;\n        uint64_t id = 0;\n        int skipme = 1;\n        int killed = 0, close_this_client = 0;\n\n        if (c->argc == 3) {\n            /* Old style syntax: CLIENT KILL <addr> */\n            addr = c->argv[2]->ptr;\n            skipme = 0; /* With the old form, you can kill yourself. */\n        } else if (c->argc > 3) {\n            int i = 2; /* Next option index. */\n\n            /* New style syntax: parse options. */\n            while(i < c->argc) {\n                int moreargs = c->argc > i+1;\n\n                if (!strcasecmp(c->argv[i]->ptr,\"id\") && moreargs) {\n                    long long tmp;\n\n                    if (getLongLongFromObjectOrReply(c,c->argv[i+1],&tmp,NULL)\n                        != C_OK) return;\n                    id = tmp;\n                } else if (!strcasecmp(c->argv[i]->ptr,\"type\") && moreargs) {\n                    type = getClientTypeByName(c->argv[i+1]->ptr);\n                    if (type == -1) {\n                        addReplyErrorFormat(c,\"Unknown client type '%s'\",\n                            (char*) c->argv[i+1]->ptr);\n                        return;\n                    }\n                } else if (!strcasecmp(c->argv[i]->ptr,\"addr\") && moreargs) {\n                    addr = c->argv[i+1]->ptr;\n                } else if (!strcasecmp(c->argv[i]->ptr,\"user\") && moreargs) {\n                    user = ACLGetUserByName(c->argv[i+1]->ptr,\n                                            sdslen(c->argv[i+1]->ptr));\n                    if (user == NULL) {\n                        addReplyErrorFormat(c,\"No such user '%s'\",\n                            (char*) c->argv[i+1]->ptr);\n                        return;\n                    }\n                } else if (!strcasecmp(c->argv[i]->ptr,\"skipme\") && moreargs) {\n                    if (!strcasecmp(c->argv[i+1]->ptr,\"yes\")) {\n                        skipme = 1;\n                    } else if (!strcasecmp(c->argv[i+1]->ptr,\"no\")) {\n                        skipme = 0;\n                    } else {\n                        addReply(c,shared.syntaxerr);\n                        return;\n                    }\n                } else {\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                i += 2;\n            }\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n\n        /* Iterate clients killing all the matching clients. */\n        listRewind(server.clients,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            client *client = listNodeValue(ln);\n            if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;\n            if (type != -1 && getClientType(client) != type) continue;\n            if (id != 0 && client->id != id) continue;\n            if (user && client->user != user) continue;\n            if (c == client && skipme) continue;\n\n            /* Kill it. */\n            if (c == client) {\n                close_this_client = 1;\n            } else {\n                freeClient(client);\n            }\n            killed++;\n        }\n\n        /* Reply according to old/new format. */\n        if (c->argc == 3) {\n            if (killed == 0)\n                addReplyError(c,\"No such client\");\n            else\n                addReply(c,shared.ok);\n        } else {\n            addReplyLongLong(c,killed);\n        }\n\n        /* If this client has to be closed, flag it as CLOSE_AFTER_REPLY\n         * only after we queued the reply to its output buffers. */\n        if (close_this_client) c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"unblock\") && (c->argc == 3 ||\n                                                          c->argc == 4))\n    {\n        /* CLIENT UNBLOCK <id> [timeout|error] */\n        long long id;\n        int unblock_error = 0;\n\n        if (c->argc == 4) {\n            if (!strcasecmp(c->argv[3]->ptr,\"timeout\")) {\n                unblock_error = 0;\n            } else if (!strcasecmp(c->argv[3]->ptr,\"error\")) {\n                unblock_error = 1;\n            } else {\n                addReplyError(c,\n                    \"CLIENT UNBLOCK reason should be TIMEOUT or ERROR\");\n                return;\n            }\n        }\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&id,NULL)\n            != C_OK) return;\n        struct client *target = lookupClientByID(id);\n        if (target && target->flags & CLIENT_BLOCKED) {\n            if (unblock_error)\n                addReplyError(target,\n                    \"-UNBLOCKED client unblocked via CLIENT UNBLOCK\");\n            else\n                replyToBlockedClientTimedOut(target);\n            unblockClient(target);\n            addReply(c,shared.cone);\n        } else {\n            addReply(c,shared.czero);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"setname\") && c->argc == 3) {\n        /* CLIENT SETNAME */\n        if (clientSetNameOrReply(c,c->argv[2]) == C_OK)\n            addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getname\") && c->argc == 2) {\n        /* CLIENT GETNAME */\n        if (c->name)\n            addReplyBulk(c,c->name);\n        else\n            addReplyNull(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"pause\") && c->argc == 3) {\n        /* CLIENT PAUSE */\n        long long duration;\n\n        if (getTimeoutFromObjectOrReply(c,c->argv[2],&duration,\n                UNIT_MILLISECONDS) != C_OK) return;\n        pauseClients(duration);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"tracking\") && c->argc >= 3) {\n        /* CLIENT TRACKING (on|off) [REDIRECT <id>] [BCAST] [PREFIX first]\n         *                          [PREFIX second] [OPTIN] [OPTOUT] ... */\n        long long redir = 0;\n        uint64_t options = 0;\n        robj **prefix = NULL;\n        size_t numprefix = 0;\n\n        /* Parse the options. */\n        for (int j = 3; j < c->argc; j++) {\n            int moreargs = (c->argc-1) - j;\n\n            if (!strcasecmp(c->argv[j]->ptr,\"redirect\") && moreargs) {\n                j++;\n                if (redir != 0) {\n                    addReplyError(c,\"A client can only redirect to a single \"\n                                    \"other client\");\n                    zfree(prefix);\n                    return;\n                }\n\n                if (getLongLongFromObjectOrReply(c,c->argv[j],&redir,NULL) !=\n                    C_OK)\n                {\n                    zfree(prefix);\n                    return;\n                }\n                /* We will require the client with the specified ID to exist\n                 * right now, even if it is possible that it gets disconnected\n                 * later. Still a valid sanity check. */\n                if (lookupClientByID(redir) == NULL) {\n                    addReplyError(c,\"The client ID you want redirect to \"\n                                    \"does not exist\");\n                    zfree(prefix);\n                    return;\n                }\n            } else if (!strcasecmp(c->argv[j]->ptr,\"bcast\")) {\n                options |= CLIENT_TRACKING_BCAST;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"optin\")) {\n                options |= CLIENT_TRACKING_OPTIN;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"optout\")) {\n                options |= CLIENT_TRACKING_OPTOUT;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"noloop\")) {\n                options |= CLIENT_TRACKING_NOLOOP;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"prefix\") && moreargs) {\n                j++;\n                prefix = zrealloc(prefix,sizeof(robj*)*(numprefix+1));\n                prefix[numprefix++] = c->argv[j];\n            } else {\n                zfree(prefix);\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n\n        /* Options are ok: enable or disable the tracking for this client. */\n        if (!strcasecmp(c->argv[2]->ptr,\"on\")) {\n            /* Before enabling tracking, make sure options are compatible\n             * among each other and with the current state of the client. */\n            if (!(options & CLIENT_TRACKING_BCAST) && numprefix) {\n                addReplyError(c,\n                    \"PREFIX option requires BCAST mode to be enabled\");\n                zfree(prefix);\n                return;\n            }\n\n            if (c->flags & CLIENT_TRACKING) {\n                int oldbcast = !!(c->flags & CLIENT_TRACKING_BCAST);\n                int newbcast = !!(options & CLIENT_TRACKING_BCAST);\n                if (oldbcast != newbcast) {\n                    addReplyError(c,\n                    \"You can't switch BCAST mode on/off before disabling \"\n                    \"tracking for this client, and then re-enabling it with \"\n                    \"a different mode.\");\n                    zfree(prefix);\n                    return;\n                }\n            }\n\n            if (options & CLIENT_TRACKING_BCAST &&\n                options & (CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT))\n            {\n                addReplyError(c,\n                \"OPTIN and OPTOUT are not compatible with BCAST\");\n                zfree(prefix);\n                return;\n            }\n\n            if (options & CLIENT_TRACKING_OPTIN && options & CLIENT_TRACKING_OPTOUT)\n            {\n                addReplyError(c,\n                \"You can't specify both OPTIN mode and OPTOUT mode\");\n                zfree(prefix);\n                return;\n            }\n\n            if ((options & CLIENT_TRACKING_OPTIN && c->flags & CLIENT_TRACKING_OPTOUT) ||\n                (options & CLIENT_TRACKING_OPTOUT && c->flags & CLIENT_TRACKING_OPTIN))\n            {\n                addReplyError(c,\n                \"You can't switch OPTIN/OPTOUT mode before disabling \"\n                \"tracking for this client, and then re-enabling it with \"\n                \"a different mode.\");\n                zfree(prefix);\n                return;\n            }\n\n            enableTracking(c,redir,options,prefix,numprefix);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"off\")) {\n            disableTracking(c);\n        } else {\n            zfree(prefix);\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n        zfree(prefix);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"caching\") && c->argc >= 3) {\n        if (!(c->flags & CLIENT_TRACKING)) {\n            addReplyError(c,\"CLIENT CACHING can be called only when the \"\n                            \"client is in tracking mode with OPTIN or \"\n                            \"OPTOUT mode enabled\");\n            return;\n        }\n\n        char *opt = c->argv[2]->ptr;\n        if (!strcasecmp(opt,\"yes\")) {\n            if (c->flags & CLIENT_TRACKING_OPTIN) {\n                c->flags |= CLIENT_TRACKING_CACHING;\n            } else {\n                addReplyError(c,\"CLIENT CACHING YES is only valid when tracking is enabled in OPTIN mode.\");\n                return;\n            }\n        } else if (!strcasecmp(opt,\"no\")) {\n            if (c->flags & CLIENT_TRACKING_OPTOUT) {\n                c->flags |= CLIENT_TRACKING_CACHING;\n            } else {\n                addReplyError(c,\"CLIENT CACHING NO is only valid when tracking is enabled in OPTOUT mode.\");\n                return;\n            }\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n\n        /* Common reply for when we succeeded. */\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getredir\") && c->argc == 2) {\n        /* CLIENT GETREDIR */\n        if (c->flags & CLIENT_TRACKING) {\n            addReplyLongLong(c,c->client_tracking_redirection);\n        } else {\n            addReplyLongLong(c,-1);\n        }\n    } else {\n        addReplyErrorFormat(c, \"Unknown subcommand or wrong number of arguments for '%s'. Try CLIENT HELP\", (char*)c->argv[1]->ptr);\n    }\n}\n\n/* HELLO <protocol-version> [AUTH <user> <password>] [SETNAME <name>] */\nvoid helloCommand(client *c) {\n    long long ver;\n\n    if (getLongLongFromObject(c->argv[1],&ver) != C_OK ||\n        ver < 2 || ver > 3)\n    {\n        addReplyError(c,\"-NOPROTO unsupported protocol version\");\n        return;\n    }\n\n    for (int j = 2; j < c->argc; j++) {\n        int moreargs = (c->argc-1) - j;\n        const char *opt = c->argv[j]->ptr;\n        if (!strcasecmp(opt,\"AUTH\") && moreargs >= 2) {\n            if (ACLAuthenticateUser(c, c->argv[j+1], c->argv[j+2]) == C_ERR) {\n                addReplyError(c,\"-WRONGPASS invalid username-password pair\");\n                return;\n            }\n            j += 2;\n        } else if (!strcasecmp(opt,\"SETNAME\") && moreargs) {\n            if (clientSetNameOrReply(c, c->argv[j+1]) == C_ERR) return;\n            j++;\n        } else {\n            addReplyErrorFormat(c,\"Syntax error in HELLO option '%s'\",opt);\n            return;\n        }\n    }\n\n    /* At this point we need to be authenticated to continue. */\n    if (!c->authenticated) {\n        addReplyError(c,\"-NOAUTH HELLO must be called with the client already \"\n                        \"authenticated, otherwise the HELLO AUTH <user> <pass> \"\n                        \"option can be used to authenticate the client and \"\n                        \"select the RESP protocol version at the same time\");\n        return;\n    }\n\n    /* Let's switch to the specified RESP mode. */\n    c->resp = ver;\n    addReplyMapLen(c,6 + !server.sentinel_mode);\n\n    addReplyBulkCString(c,\"server\");\n    addReplyBulkCString(c,\"redis\");\n\n    addReplyBulkCString(c,\"version\");\n    addReplyBulkCString(c,REDIS_VERSION);\n\n    addReplyBulkCString(c,\"proto\");\n    addReplyLongLong(c,3);\n\n    addReplyBulkCString(c,\"id\");\n    addReplyLongLong(c,c->id);\n\n    addReplyBulkCString(c,\"mode\");\n    if (server.sentinel_mode) addReplyBulkCString(c,\"sentinel\");\n    else if (server.cluster_enabled) addReplyBulkCString(c,\"cluster\");\n    else addReplyBulkCString(c,\"standalone\");\n\n    if (!server.sentinel_mode) {\n        addReplyBulkCString(c,\"role\");\n        addReplyBulkCString(c,server.masterhost ? \"replica\" : \"master\");\n    }\n\n    addReplyBulkCString(c,\"modules\");\n    addReplyLoadedModules(c);\n}\n\n/* This callback is bound to POST and \"Host:\" command names. Those are not\n * really commands, but are used in security attacks in order to talk to\n * Redis instances via HTTP, with a technique called \"cross protocol scripting\"\n * which exploits the fact that services like Redis will discard invalid\n * HTTP headers and will process what follows.\n *\n * As a protection against this attack, Redis will terminate the connection\n * when a POST or \"Host:\" header is seen, and will log the event from\n * time to time (to avoid creating a DOS as a result of too many logs). */\nvoid securityWarningCommand(client *c) {\n    static time_t logged_time;\n    time_t now = time(NULL);\n\n    if (labs(now-logged_time) > 60) {\n        serverLog(LL_WARNING,\"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.\");\n        logged_time = now;\n    }\n    freeClientAsync(c);\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. */\nvoid rewriteClientCommandVector(client *c, int argc, ...) {\n    va_list ap;\n    int j;\n    robj **argv; /* The new argument vector */\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    for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]);\n    zfree(c->argv);\n    /* Replace argv and argc with our new versions. */\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    serverAssertWithInfo(c,NULL,c->cmd != NULL);\n    va_end(ap);\n}\n\n/* Completely replace the client command vector with the provided one. */\nvoid replaceClientCommandVector(client *c, int argc, robj **argv) {\n    freeClientArgv(c);\n    zfree(c->argv);\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    serverAssertWithInfo(c,NULL,c->cmd != NULL);\n}\n\n/* Rewrite a single item in the command vector.\n * The new val ref count is incremented, and the old decremented.\n *\n * It is possible to specify an argument over the current size of the\n * argument vector: in this case the array of objects gets reallocated\n * and c->argc set to the max value. However it's up to the caller to\n *\n * 1. Make sure there are no \"holes\" and all the arguments are set.\n * 2. If the original argument vector was longer than the one we\n *    want to end with, it's up to the caller to set c->argc and\n *    free the no longer used objects on c->argv. */\nvoid rewriteClientCommandArgument(client *c, int i, robj *newval) {\n    robj *oldval;\n\n    if (i >= c->argc) {\n        c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1));\n        c->argc = i+1;\n        c->argv[i] = NULL;\n    }\n    oldval = c->argv[i];\n    c->argv[i] = newval;\n    incrRefCount(newval);\n    if (oldval) 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        serverAssertWithInfo(c,NULL,c->cmd != NULL);\n    }\n}\n\n/* This function returns the number of bytes that Redis is\n * using to store the reply still not read by the client.\n *\n * Note: this function is very fast so can be called as many time as\n * the caller wishes. The main usage of this function currently is\n * enforcing the client output length limits. */\nunsigned long getClientOutputBufferMemoryUsage(client *c) {\n    unsigned long list_item_size = sizeof(listNode) + sizeof(clientReplyBlock);\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 * The function will return one of the following:\n * CLIENT_TYPE_NORMAL -> Normal client\n * CLIENT_TYPE_SLAVE  -> Slave\n * CLIENT_TYPE_PUBSUB -> Client subscribed to Pub/Sub channels\n * CLIENT_TYPE_MASTER -> The client representing our replication master.\n */\nint getClientType(client *c) {\n    if (c->flags & CLIENT_MASTER) return CLIENT_TYPE_MASTER;\n    /* Even though MONITOR clients are marked as replicas, we\n     * want the expose them as normal clients. */\n    if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR))\n        return CLIENT_TYPE_SLAVE;\n    if (c->flags & CLIENT_PUBSUB) return CLIENT_TYPE_PUBSUB;\n    return CLIENT_TYPE_NORMAL;\n}\n\nint getClientTypeByName(char *name) {\n    if (!strcasecmp(name,\"normal\")) return CLIENT_TYPE_NORMAL;\n    else if (!strcasecmp(name,\"slave\")) return CLIENT_TYPE_SLAVE;\n    else if (!strcasecmp(name,\"replica\")) return CLIENT_TYPE_SLAVE;\n    else if (!strcasecmp(name,\"pubsub\")) return CLIENT_TYPE_PUBSUB;\n    else if (!strcasecmp(name,\"master\")) return CLIENT_TYPE_MASTER;\n    else return -1;\n}\n\nchar *getClientTypeName(int class) {\n    switch(class) {\n    case CLIENT_TYPE_NORMAL: return \"normal\";\n    case CLIENT_TYPE_SLAVE:  return \"slave\";\n    case CLIENT_TYPE_PUBSUB: return \"pubsub\";\n    case CLIENT_TYPE_MASTER: return \"master\";\n    default:                       return NULL;\n    }\n}\n\n/* The function checks if the client reached output buffer soft or hard\n * limit, and also update the state needed to check the soft limit as\n * a side effect.\n *\n * Return value: non-zero if the client reached the soft or the hard limit.\n *               Otherwise zero is returned. */\nint checkClientOutputBufferLimits(client *c) {\n    int soft = 0, hard = 0, class;\n    unsigned long used_mem = getClientOutputBufferMemoryUsage(c);\n\n    class = getClientType(c);\n    /* For the purpose of output buffer limiting, masters are handled\n     * like normal clients. */\n    if (class == CLIENT_TYPE_MASTER) class = CLIENT_TYPE_NORMAL;\n\n    if (server.client_obuf_limits[class].hard_limit_bytes &&\n        used_mem >= server.client_obuf_limits[class].hard_limit_bytes)\n        hard = 1;\n    if (server.client_obuf_limits[class].soft_limit_bytes &&\n        used_mem >= server.client_obuf_limits[class].soft_limit_bytes)\n        soft = 1;\n\n    /* We need to check if the soft limit is reached continuously for the\n     * specified amount of seconds. */\n    if (soft) {\n        if (c->obuf_soft_limit_reached_time == 0) {\n            c->obuf_soft_limit_reached_time = server.unixtime;\n            soft = 0; /* First time we see the soft limit reached */\n        } else {\n            time_t elapsed = server.unixtime - c->obuf_soft_limit_reached_time;\n\n            if (elapsed <=\n                server.client_obuf_limits[class].soft_limit_seconds) {\n                soft = 0; /* The client still did not reached the max number of\n                             seconds for the soft limit to be considered\n                             reached. */\n            }\n        }\n    } else {\n        c->obuf_soft_limit_reached_time = 0;\n    }\n    return soft || hard;\n}\n\n/* Asynchronously close a client if soft or hard limit is reached on the\n * output buffer size. The caller can check if the client will be closed\n * checking if the client CLIENT_CLOSE_ASAP flag is set.\n *\n * Note: we need to close the client asynchronously because this function is\n * called from contexts where the client can't be freed safely, i.e. from the\n * lower level functions pushing data inside the client output buffers. */\nvoid asyncCloseClientOnOutputBufferLimitReached(client *c) {\n    if (!c->conn) return; /* It is unsafe to free fake clients. */\n    serverAssert(c->reply_bytes < SIZE_MAX-(1024*64));\n    if (c->reply_bytes == 0 || c->flags & CLIENT_CLOSE_ASAP) return;\n    if (checkClientOutputBufferLimits(c)) {\n        sds client = catClientInfoString(sdsempty(),c);\n\n        freeClientAsync(c);\n        serverLog(LL_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 * This is also called by SHUTDOWN for a best-effort attempt to send\n * slaves the latest writes. */\nvoid flushSlavesOutputBuffers(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = listNodeValue(ln);\n        int can_receive_writes = connHasWriteHandler(slave->conn) ||\n                                 (slave->flags & CLIENT_PENDING_WRITE);\n\n        /* We don't want to send the pending data to the replica in a few\n         * cases:\n         *\n         * 1. For some reason there is neither the write handler installed\n         *    nor the client is flagged as to have pending writes: for some\n         *    reason this replica may not be set to receive data. This is\n         *    just for the sake of defensive programming.\n         *\n         * 2. The put_online_on_ack flag is true. To know why we don't want\n         *    to send data to the replica in this case, please grep for the\n         *    flag for this flag.\n         *\n         * 3. Obviously if the slave is not ONLINE.\n         */\n        if (slave->replstate == SLAVE_STATE_ONLINE &&\n            can_receive_writes &&\n            !slave->repl_put_online_on_ack &&\n            clientHasPendingReplies(slave))\n        {\n            writeToClient(slave,0);\n        }\n    }\n}\n\n/* Pause clients up to the specified unixtime (in ms). While clients\n * are paused no command is processed from clients, so the data set can't\n * change during that time.\n *\n * However while this function pauses normal and Pub/Sub clients, slaves are\n * still served, so this function can be used on server upgrades where it is\n * required that slaves process the latest bytes from the replication stream\n * before being turned to masters.\n *\n * This function is also internally used by Redis Cluster for the manual\n * failover procedure implemented by CLUSTER FAILOVER.\n *\n * The function always succeed, even if there is already a pause in progress.\n * In such a case, the pause is extended if the duration is more than the\n * time left for the previous duration. However if the duration is smaller\n * than the time left for the previous pause, no change is made to the\n * left duration. */\nvoid pauseClients(mstime_t end) {\n    if (!server.clients_paused || end > server.clients_pause_end_time)\n        server.clients_pause_end_time = end;\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. */\nint clientsArePaused(void) {\n    if (server.clients_paused &&\n        server.clients_pause_end_time < server.mstime)\n    {\n        listNode *ln;\n        listIter li;\n        client *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            /* Don't touch slaves and blocked clients.\n             * The latter pending requests will be processed when unblocked. */\n            if (c->flags & (CLIENT_SLAVE|CLIENT_BLOCKED)) continue;\n            queueClientForReprocessing(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 4 times as long as we receive acknowledge that\n * some event was processed, in order to go forward with the accept, read,\n * write, close sequence needed to serve a client.\n *\n * The function returns the total number of events processed. */\nvoid processEventsWhileBlocked(void) {\n    int iterations = 4; /* See the function top-comment. */\n\n    /* Note: when we are processing events while blocked (for instance during\n     * busy Lua scripts), we set a global flag. When such flag is set, we\n     * avoid handling the read part of clients using threaded I/O.\n     * See https://github.com/antirez/redis/issues/6988 for more info. */\n    ProcessingEventsWhileBlocked = 1;\n    while (iterations--) {\n        long long startval = server.events_processed_while_blocked;\n        long long ae_events = aeProcessEvents(server.el,\n            AE_FILE_EVENTS|AE_DONT_WAIT|\n            AE_CALL_BEFORE_SLEEP|AE_CALL_AFTER_SLEEP);\n        /* Note that server.events_processed_while_blocked will also get\n         * incremeted by callbacks called by the event loop handlers. */\n        server.events_processed_while_blocked += ae_events;\n        long long events = server.events_processed_while_blocked - startval;\n        if (!events) break;\n    }\n    ProcessingEventsWhileBlocked = 0;\n}\n\n/* ==========================================================================\n * Threaded I/O\n * ========================================================================== */\n\nint tio_debug = 0;\n\n#define IO_THREADS_MAX_NUM 128\n#define IO_THREADS_OP_READ 0\n#define IO_THREADS_OP_WRITE 1\n\npthread_t io_threads[IO_THREADS_MAX_NUM];\npthread_mutex_t io_threads_mutex[IO_THREADS_MAX_NUM];\n_Atomic unsigned long io_threads_pending[IO_THREADS_MAX_NUM];\nint io_threads_active;  /* Are the threads currently spinning waiting I/O? */\nint io_threads_op;      /* IO_THREADS_OP_WRITE or IO_THREADS_OP_READ. */\n\n/* This is the list of clients each thread will serve when threaded I/O is\n * used. We spawn io_threads_num-1 threads, since one is the main thread\n * itself. */\nlist *io_threads_list[IO_THREADS_MAX_NUM];\n\nvoid *IOThreadMain(void *myid) {\n    /* The ID is the thread number (from 0 to server.iothreads_num-1), and is\n     * used by the thread to just manipulate a single sub-array of clients. */\n    long id = (unsigned long)myid;\n    char thdname[16];\n\n    snprintf(thdname, sizeof(thdname), \"io_thd_%ld\", id);\n    redis_set_thread_title(thdname);\n    redisSetCpuAffinity(server.server_cpulist);\n\n    while(1) {\n        /* Wait for start */\n        for (int j = 0; j < 1000000; j++) {\n            if (io_threads_pending[id] != 0) break;\n        }\n\n        /* Give the main thread a chance to stop this thread. */\n        if (io_threads_pending[id] == 0) {\n            pthread_mutex_lock(&io_threads_mutex[id]);\n            pthread_mutex_unlock(&io_threads_mutex[id]);\n            continue;\n        }\n\n        serverAssert(io_threads_pending[id] != 0);\n\n        if (tio_debug) printf(\"[%ld] %d to handle\\n\", id, (int)listLength(io_threads_list[id]));\n\n        /* Process: note that the main thread will never touch our list\n         * before we drop the pending count to 0. */\n        listIter li;\n        listNode *ln;\n        listRewind(io_threads_list[id],&li);\n        while((ln = listNext(&li))) {\n            client *c = listNodeValue(ln);\n            if (io_threads_op == IO_THREADS_OP_WRITE) {\n                writeToClient(c,0);\n            } else if (io_threads_op == IO_THREADS_OP_READ) {\n                readQueryFromClient(c->conn);\n            } else {\n                serverPanic(\"io_threads_op value is unknown\");\n            }\n        }\n        listEmpty(io_threads_list[id]);\n        io_threads_pending[id] = 0;\n\n        if (tio_debug) printf(\"[%ld] Done\\n\", id);\n    }\n}\n\n/* Initialize the data structures needed for threaded I/O. */\nvoid initThreadedIO(void) {\n    io_threads_active = 0; /* We start with threads not active. */\n\n    /* Don't spawn any thread if the user selected a single thread:\n     * we'll handle I/O directly from the main thread. */\n    if (server.io_threads_num == 1) return;\n\n    if (server.io_threads_num > IO_THREADS_MAX_NUM) {\n        serverLog(LL_WARNING,\"Fatal: too many I/O threads configured. \"\n                             \"The maximum number is %d.\", IO_THREADS_MAX_NUM);\n        exit(1);\n    }\n\n    /* Spawn and initialize the I/O threads. */\n    for (int i = 0; i < server.io_threads_num; i++) {\n        /* Things we do for all the threads including the main thread. */\n        io_threads_list[i] = listCreate();\n        if (i == 0) continue; /* Thread 0 is the main thread. */\n\n        /* Things we do only for the additional threads. */\n        pthread_t tid;\n        pthread_mutex_init(&io_threads_mutex[i],NULL);\n        io_threads_pending[i] = 0;\n        pthread_mutex_lock(&io_threads_mutex[i]); /* Thread will be stopped. */\n        if (pthread_create(&tid,NULL,IOThreadMain,(void*)(long)i) != 0) {\n            serverLog(LL_WARNING,\"Fatal: Can't initialize IO thread.\");\n            exit(1);\n        }\n        io_threads[i] = tid;\n    }\n}\n\nvoid startThreadedIO(void) {\n    if (tio_debug) { printf(\"S\"); fflush(stdout); }\n    if (tio_debug) printf(\"--- STARTING THREADED IO ---\\n\");\n    serverAssert(io_threads_active == 0);\n    for (int j = 1; j < server.io_threads_num; j++)\n        pthread_mutex_unlock(&io_threads_mutex[j]);\n    io_threads_active = 1;\n}\n\nvoid stopThreadedIO(void) {\n    /* We may have still clients with pending reads when this function\n     * is called: handle them before stopping the threads. */\n    handleClientsWithPendingReadsUsingThreads();\n    if (tio_debug) { printf(\"E\"); fflush(stdout); }\n    if (tio_debug) printf(\"--- STOPPING THREADED IO [R%d] [W%d] ---\\n\",\n        (int) listLength(server.clients_pending_read),\n        (int) listLength(server.clients_pending_write));\n    serverAssert(io_threads_active == 1);\n    for (int j = 1; j < server.io_threads_num; j++)\n        pthread_mutex_lock(&io_threads_mutex[j]);\n    io_threads_active = 0;\n}\n\n/* This function checks if there are not enough pending clients to justify\n * taking the I/O threads active: in that case I/O threads are stopped if\n * currently active. We track the pending writes as a measure of clients\n * we need to handle in parallel, however the I/O threading is disabled\n * globally for reads as well if we have too little pending clients.\n *\n * The function returns 0 if the I/O threading should be used becuase there\n * are enough active threads, otherwise 1 is returned and the I/O threads\n * could be possibly stopped (if already active) as a side effect. */\nint stopThreadedIOIfNeeded(void) {\n    int pending = listLength(server.clients_pending_write);\n\n    /* Return ASAP if IO threads are disabled (single threaded mode). */\n    if (server.io_threads_num == 1) return 1;\n\n    if (pending < (server.io_threads_num*2)) {\n        if (io_threads_active) stopThreadedIO();\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nint handleClientsWithPendingWritesUsingThreads(void) {\n    int processed = listLength(server.clients_pending_write);\n    if (processed == 0) return 0; /* Return ASAP if there are no clients. */\n\n    /* If I/O threads are disabled or we have few clients to serve, don't\n     * use I/O threads, but thejboring synchronous code. */\n    if (server.io_threads_num == 1 || stopThreadedIOIfNeeded()) {\n        return handleClientsWithPendingWrites();\n    }\n\n    /* Start threads if needed. */\n    if (!io_threads_active) startThreadedIO();\n\n    if (tio_debug) printf(\"%d TOTAL WRITE pending clients\\n\", processed);\n\n    /* Distribute the clients across N different lists. */\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients_pending_write,&li);\n    int item_id = 0;\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n        int target_id = item_id % server.io_threads_num;\n        listAddNodeTail(io_threads_list[target_id],c);\n        item_id++;\n    }\n\n    /* Give the start condition to the waiting threads, by setting the\n     * start condition atomic var. */\n    io_threads_op = IO_THREADS_OP_WRITE;\n    for (int j = 1; j < server.io_threads_num; j++) {\n        int count = listLength(io_threads_list[j]);\n        io_threads_pending[j] = count;\n    }\n\n    /* Also use the main thread to process a slice of clients. */\n    listRewind(io_threads_list[0],&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        writeToClient(c,0);\n    }\n    listEmpty(io_threads_list[0]);\n\n    /* Wait for all the other threads to end their work. */\n    while(1) {\n        unsigned long pending = 0;\n        for (int j = 1; j < server.io_threads_num; j++)\n            pending += io_threads_pending[j];\n        if (pending == 0) break;\n    }\n    if (tio_debug) printf(\"I/O WRITE All threads finshed\\n\");\n\n    /* Run the list of clients again to install the write handler where\n     * needed. */\n    listRewind(server.clients_pending_write,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n\n        /* Install the write handler if there are pending writes in some\n         * of the clients. */\n        if (clientHasPendingReplies(c) &&\n                connSetWriteHandler(c->conn, sendReplyToClient) == AE_ERR)\n        {\n            freeClientAsync(c);\n        }\n    }\n    listEmpty(server.clients_pending_write);\n    return processed;\n}\n\n/* Return 1 if we want to handle the client read later using threaded I/O.\n * This is called by the readable handler of the event loop.\n * As a side effect of calling this function the client is put in the\n * pending read clients and flagged as such. */\nint postponeClientRead(client *c) {\n    if (io_threads_active &&\n        server.io_threads_do_reads &&\n        !ProcessingEventsWhileBlocked &&\n        !(c->flags & (CLIENT_MASTER|CLIENT_SLAVE|CLIENT_PENDING_READ)))\n    {\n        c->flags |= CLIENT_PENDING_READ;\n        listAddNodeHead(server.clients_pending_read,c);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* When threaded I/O is also enabled for the reading + parsing side, the\n * readable handler will just put normal clients into a queue of clients to\n * process (instead of serving them synchronously). This function runs\n * the queue using the I/O threads, and process them in order to accumulate\n * the reads in the buffers, and also parse the first command available\n * rendering it in the client structures. */\nint handleClientsWithPendingReadsUsingThreads(void) {\n    if (!io_threads_active || !server.io_threads_do_reads) return 0;\n    int processed = listLength(server.clients_pending_read);\n    if (processed == 0) return 0;\n\n    if (tio_debug) printf(\"%d TOTAL READ pending clients\\n\", processed);\n\n    /* Distribute the clients across N different lists. */\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients_pending_read,&li);\n    int item_id = 0;\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        int target_id = item_id % server.io_threads_num;\n        listAddNodeTail(io_threads_list[target_id],c);\n        item_id++;\n    }\n\n    /* Give the start condition to the waiting threads, by setting the\n     * start condition atomic var. */\n    io_threads_op = IO_THREADS_OP_READ;\n    for (int j = 1; j < server.io_threads_num; j++) {\n        int count = listLength(io_threads_list[j]);\n        io_threads_pending[j] = count;\n    }\n\n    /* Also use the main thread to process a slice of clients. */\n    listRewind(io_threads_list[0],&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        readQueryFromClient(c->conn);\n    }\n    listEmpty(io_threads_list[0]);\n\n    /* Wait for all the other threads to end their work. */\n    while(1) {\n        unsigned long pending = 0;\n        for (int j = 1; j < server.io_threads_num; j++)\n            pending += io_threads_pending[j];\n        if (pending == 0) break;\n    }\n    if (tio_debug) printf(\"I/O READ All threads finshed\\n\");\n\n    /* Run the list of clients again to process the new buffers. */\n    while(listLength(server.clients_pending_read)) {\n        ln = listFirst(server.clients_pending_read);\n        client *c = listNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_READ;\n        listDelNode(server.clients_pending_read,ln);\n\n        if (c->flags & CLIENT_PENDING_COMMAND) {\n            c->flags &= ~CLIENT_PENDING_COMMAND;\n            if (processCommandAndResetClient(c) == C_ERR) {\n                /* If the client is no longer valid, we avoid\n                 * processing the client later. So we just go\n                 * to the next. */\n                continue;\n            }\n        }\n        processInputBuffer(c);\n    }\n    return processed;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n\n/* This file implements keyspace events notification via Pub/Sub and\n * described at https://redis.io/topics/notifications. */\n\n/* Turn a string representing notification classes into an integer\n * representing notification classes flags xored.\n *\n * The function returns -1 if the input contains characters not mapping to\n * any class. */\nint keyspaceEventsStringToFlags(char *classes) {\n    char *p = classes;\n    int c, flags = 0;\n\n    while((c = *p++) != '\\0') {\n        switch(c) {\n        case 'A': flags |= NOTIFY_ALL; break;\n        case 'g': flags |= NOTIFY_GENERIC; break;\n        case '$': flags |= NOTIFY_STRING; break;\n        case 'l': flags |= NOTIFY_LIST; break;\n        case 's': flags |= NOTIFY_SET; break;\n        case 'h': flags |= NOTIFY_HASH; break;\n        case 'z': flags |= NOTIFY_ZSET; break;\n        case 'x': flags |= NOTIFY_EXPIRED; break;\n        case 'e': flags |= NOTIFY_EVICTED; break;\n        case 'K': flags |= NOTIFY_KEYSPACE; break;\n        case 'E': flags |= NOTIFY_KEYEVENT; break;\n        case 't': flags |= NOTIFY_STREAM; break;\n        case 'm': flags |= NOTIFY_KEY_MISS; break;\n        default: return -1;\n        }\n    }\n    return flags;\n}\n\n/* This function does exactly the revese of the function above: it gets\n * as input an integer with the xored flags and returns a string representing\n * the selected classes. The string returned is an sds string that needs to\n * be released with sdsfree(). */\nsds keyspaceEventsFlagsToString(int flags) {\n    sds res;\n\n    res = sdsempty();\n    if ((flags & NOTIFY_ALL) == NOTIFY_ALL) {\n        res = sdscatlen(res,\"A\",1);\n    } else {\n        if (flags & NOTIFY_GENERIC) res = sdscatlen(res,\"g\",1);\n        if (flags & NOTIFY_STRING) res = sdscatlen(res,\"$\",1);\n        if (flags & NOTIFY_LIST) res = sdscatlen(res,\"l\",1);\n        if (flags & NOTIFY_SET) res = sdscatlen(res,\"s\",1);\n        if (flags & NOTIFY_HASH) res = sdscatlen(res,\"h\",1);\n        if (flags & NOTIFY_ZSET) res = sdscatlen(res,\"z\",1);\n        if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,\"x\",1);\n        if (flags & NOTIFY_EVICTED) res = sdscatlen(res,\"e\",1);\n        if (flags & NOTIFY_STREAM) res = sdscatlen(res,\"t\",1);\n    }\n    if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,\"K\",1);\n    if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,\"E\",1);\n    if (flags & NOTIFY_KEY_MISS) res = sdscatlen(res,\"m\",1);\n    return res;\n}\n\n/* The API provided to the rest of the Redis core is a simple function:\n *\n * notifyKeyspaceEvent(char *event, robj *key, int dbid);\n *\n * 'event' is a C string representing the event name.\n * 'key' is a Redis object representing the key name.\n * 'dbid' is the database ID where the key lives.  */\nvoid notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {\n    sds chan;\n    robj *chanobj, *eventobj;\n    int len = -1;\n    char buf[24];\n\n    /* If any modules are interested in events, notify the module system now.\n     * This bypasses the notifications configuration, but the module engine\n     * will only call event subscribers if the event type matches the types\n     * they are interested in. */\n     moduleNotifyKeyspaceEvent(type, event, key, dbid);\n\n    /* If notifications for this class of events are off, return ASAP. */\n    if (!(server.notify_keyspace_events & type)) return;\n\n    eventobj = createStringObject(event,strlen(event));\n\n    /* __keyspace@<db>__:<key> <event> notifications. */\n    if (server.notify_keyspace_events & NOTIFY_KEYSPACE) {\n        chan = sdsnewlen(\"__keyspace@\",11);\n        len = ll2string(buf,sizeof(buf),dbid);\n        chan = sdscatlen(chan, buf, len);\n        chan = sdscatlen(chan, \"__:\", 3);\n        chan = sdscatsds(chan, key->ptr);\n        chanobj = createObject(OBJ_STRING, chan);\n        pubsubPublishMessage(chanobj, eventobj);\n        decrRefCount(chanobj);\n    }\n\n    /* __keyevent@<db>__:<event> <key> notifications. */\n    if (server.notify_keyspace_events & NOTIFY_KEYEVENT) {\n        chan = sdsnewlen(\"__keyevent@\",11);\n        if (len == -1) len = ll2string(buf,sizeof(buf),dbid);\n        chan = sdscatlen(chan, buf, len);\n        chan = sdscatlen(chan, \"__:\", 3);\n        chan = sdscatsds(chan, eventobj->ptr);\n        chanobj = createObject(OBJ_STRING, chan);\n        pubsubPublishMessage(chanobj, key);\n        decrRefCount(chanobj);\n    }\n    decrRefCount(eventobj);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include <math.h>\n#include <ctype.h>\n\n#ifdef __CYGWIN__\n#define strtold(a,b) ((long double)strtod((a),(b)))\n#endif\n\n/* ===================== Creation and parsing of objects ==================== */\n\nrobj *createObject(int type, void *ptr) {\n    robj *o = zmalloc(sizeof(*o));\n    o->type = type;\n    o->encoding = OBJ_ENCODING_RAW;\n    o->ptr = ptr;\n    o->refcount = 1;\n\n    /* Set the LRU to the current lruclock (minutes resolution), or\n     * alternatively the LFU counter. */\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;\n    } else {\n        o->lru = LRU_CLOCK();\n    }\n    return o;\n}\n\n/* Set a special refcount in the object to make it \"shared\":\n * incrRefCount and decrRefCount() will test for this special refcount\n * and will not touch the object. This way it is free to access shared\n * objects such as small integers from different threads without any\n * mutex.\n *\n * A common patter to create shared objects:\n *\n * robj *myobject = makeObjectShared(createObject(...));\n *\n */\nrobj *makeObjectShared(robj *o) {\n    serverAssert(o->refcount == 1);\n    o->refcount = OBJ_SHARED_REFCOUNT;\n    return o;\n}\n\n/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain\n * string object where o->ptr points to a proper sds string. */\nrobj *createRawStringObject(const char *ptr, size_t len) {\n    return createObject(OBJ_STRING, sdsnewlen(ptr,len));\n}\n\n/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is\n * an object where the sds string is actually an unmodifiable string\n * allocated in the same chunk as the object itself. */\nrobj *createEmbeddedStringObject(const char *ptr, size_t len) {\n    robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);\n    struct sdshdr8 *sh = (void*)(o+1);\n\n    o->type = OBJ_STRING;\n    o->encoding = OBJ_ENCODING_EMBSTR;\n    o->ptr = sh+1;\n    o->refcount = 1;\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;\n    } else {\n        o->lru = LRU_CLOCK();\n    }\n\n    sh->len = len;\n    sh->alloc = len;\n    sh->flags = SDS_TYPE_8;\n    if (ptr == SDS_NOINIT)\n        sh->buf[len] = '\\0';\n    else 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 * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is\n * used.\n *\n * The current limit of 44 is chosen so that the biggest string object\n * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */\n#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44\nrobj *createStringObject(const char *ptr, size_t len) {\n    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)\n        return createEmbeddedStringObject(ptr,len);\n    else\n        return createRawStringObject(ptr,len);\n}\n\n/* Create a string object from a long long value. When possible returns a\n * shared integer object, or at least an integer encoded one.\n *\n * If valueobj is non zero, the function avoids returning a a shared\n * integer, because the object is going to be used as value in the Redis key\n * space (for instance when the INCR command is used), so we want LFU/LRU\n * values specific for each key. */\nrobj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {\n    robj *o;\n\n    if (server.maxmemory == 0 ||\n        !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))\n    {\n        /* If the maxmemory policy permits, we can still return shared integers\n         * even if valueobj is true. */\n        valueobj = 0;\n    }\n\n    if (value >= 0 && value < OBJ_SHARED_INTEGERS && valueobj == 0) {\n        incrRefCount(shared.integers[value]);\n        o = shared.integers[value];\n    } else {\n        if (value >= LONG_MIN && value <= LONG_MAX) {\n            o = createObject(OBJ_STRING, NULL);\n            o->encoding = OBJ_ENCODING_INT;\n            o->ptr = (void*)((long)value);\n        } else {\n            o = createObject(OBJ_STRING,sdsfromlonglong(value));\n        }\n    }\n    return o;\n}\n\n/* Wrapper for createStringObjectFromLongLongWithOptions() always demanding\n * to create a shared object if possible. */\nrobj *createStringObjectFromLongLong(long long value) {\n    return createStringObjectFromLongLongWithOptions(value,0);\n}\n\n/* Wrapper for createStringObjectFromLongLongWithOptions() avoiding a shared\n * object when LFU/LRU info are needed, that is, when the object is used\n * as a value in the key space, and Redis is configured to evict based on\n * LFU/LRU. */\nrobj *createStringObjectFromLongLongForValue(long long value) {\n    return createStringObjectFromLongLongWithOptions(value,1);\n}\n\n/* Create a string object from a long double. If humanfriendly is non-zero\n * it does not use exponential format and trims trailing zeroes at the end,\n * however this results in loss of precision. Otherwise exp format is used\n * and the output of snprintf() is not modified.\n *\n * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */\nrobj *createStringObjectFromLongDouble(long double value, int humanfriendly) {\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    int len = ld2string(buf,sizeof(buf),value,humanfriendly? LD_STR_HUMAN: LD_STR_AUTO);\n    return createStringObject(buf,len);\n}\n\n/* Duplicate a string object, with the guarantee that the returned object\n * has the same encoding as the original one.\n *\n * This function also guarantees that duplicating a small integer object\n * (or a string object that contains a representation of a small integer)\n * will always result in a fresh object that is unshared (refcount == 1).\n *\n * The resulting object always has refcount set to 1. */\nrobj *dupStringObject(const robj *o) {\n    robj *d;\n\n    serverAssert(o->type == OBJ_STRING);\n\n    switch(o->encoding) {\n    case OBJ_ENCODING_RAW:\n        return createRawStringObject(o->ptr,sdslen(o->ptr));\n    case OBJ_ENCODING_EMBSTR:\n        return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));\n    case OBJ_ENCODING_INT:\n        d = createObject(OBJ_STRING, NULL);\n        d->encoding = OBJ_ENCODING_INT;\n        d->ptr = o->ptr;\n        return d;\n    default:\n        serverPanic(\"Wrong encoding.\");\n        break;\n    }\n}\n\nrobj *createQuicklistObject(void) {\n    quicklist *l = quicklistCreate();\n    robj *o = createObject(OBJ_LIST,l);\n    o->encoding = OBJ_ENCODING_QUICKLIST;\n    return o;\n}\n\nrobj *createZiplistObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_LIST,zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createSetObject(void) {\n    dict *d = dictCreate(&setDictType,NULL);\n    robj *o = createObject(OBJ_SET,d);\n    o->encoding = OBJ_ENCODING_HT;\n    return o;\n}\n\nrobj *createIntsetObject(void) {\n    intset *is = intsetNew();\n    robj *o = createObject(OBJ_SET,is);\n    o->encoding = OBJ_ENCODING_INTSET;\n    return o;\n}\n\nrobj *createHashObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_HASH, zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createZsetObject(void) {\n    zset *zs = zmalloc(sizeof(*zs));\n    robj *o;\n\n    zs->dict = dictCreate(&zsetDictType,NULL);\n    zs->zsl = zslCreate();\n    o = createObject(OBJ_ZSET,zs);\n    o->encoding = OBJ_ENCODING_SKIPLIST;\n    return o;\n}\n\nrobj *createZsetZiplistObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_ZSET,zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createStreamObject(void) {\n    stream *s = streamNew();\n    robj *o = createObject(OBJ_STREAM,s);\n    o->encoding = OBJ_ENCODING_STREAM;\n    return o;\n}\n\nrobj *createModuleObject(moduleType *mt, void *value) {\n    moduleValue *mv = zmalloc(sizeof(*mv));\n    mv->type = mt;\n    mv->value = value;\n    return createObject(OBJ_MODULE,mv);\n}\n\nvoid freeStringObject(robj *o) {\n    if (o->encoding == OBJ_ENCODING_RAW) {\n        sdsfree(o->ptr);\n    }\n}\n\nvoid freeListObject(robj *o) {\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistRelease(o->ptr);\n    } else {\n        serverPanic(\"Unknown list encoding type\");\n    }\n}\n\nvoid freeSetObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n    case OBJ_ENCODING_INTSET:\n        zfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown set encoding type\");\n    }\n}\n\nvoid freeZsetObject(robj *o) {\n    zset *zs;\n    switch (o->encoding) {\n    case OBJ_ENCODING_SKIPLIST:\n        zs = o->ptr;\n        dictRelease(zs->dict);\n        zslFree(zs->zsl);\n        zfree(zs);\n        break;\n    case OBJ_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid freeHashObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n    case OBJ_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown hash encoding type\");\n        break;\n    }\n}\n\nvoid freeModuleObject(robj *o) {\n    moduleValue *mv = o->ptr;\n    mv->type->free(mv->value);\n    zfree(mv);\n}\n\nvoid freeStreamObject(robj *o) {\n    freeStream(o->ptr);\n}\n\nvoid incrRefCount(robj *o) {\n    if (o->refcount < OBJ_FIRST_SPECIAL_REFCOUNT) {\n        o->refcount++;\n    } else {\n        if (o->refcount == OBJ_SHARED_REFCOUNT) {\n            /* Nothing to do: this refcount is immutable. */\n        } else if (o->refcount == OBJ_STATIC_REFCOUNT) {\n            serverPanic(\"You tried to retain an object allocated in the stack\");\n        }\n    }\n}\n\nvoid decrRefCount(robj *o) {\n    if (o->refcount == 1) {\n        switch(o->type) {\n        case OBJ_STRING: freeStringObject(o); break;\n        case OBJ_LIST: freeListObject(o); break;\n        case OBJ_SET: freeSetObject(o); break;\n        case OBJ_ZSET: freeZsetObject(o); break;\n        case OBJ_HASH: freeHashObject(o); break;\n        case OBJ_MODULE: freeModuleObject(o); break;\n        case OBJ_STREAM: freeStreamObject(o); break;\n        default: serverPanic(\"Unknown object type\"); break;\n        }\n        zfree(o);\n    } else {\n        if (o->refcount <= 0) serverPanic(\"decrRefCount against refcount <= 0\");\n        if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;\n    }\n}\n\n/* This variant of decrRefCount() gets its argument as void, and is useful\n * as free method in data structures that expect a 'void free_object(void*)'\n * prototype for the free method. */\nvoid decrRefCountVoid(void *o) {\n    decrRefCount(o);\n}\n\n/* This function set the ref count to zero without freeing the object.\n * It is useful in order to pass a new object to functions incrementing\n * the ref count of the received object. Example:\n *\n *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));\n *\n * Otherwise you need to resort to the less elegant pattern:\n *\n *    *obj = createObject(...);\n *    functionThatWillIncrementRefCount(obj);\n *    decrRefCount(obj);\n */\nrobj *resetRefCount(robj *obj) {\n    obj->refcount = 0;\n    return obj;\n}\n\nint checkType(client *c, robj *o, int type) {\n    if (o->type != type) {\n        addReply(c,shared.wrongtypeerr);\n        return 1;\n    }\n    return 0;\n}\n\nint isSdsRepresentableAsLongLong(sds s, long long *llval) {\n    return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;\n}\n\nint isObjectRepresentableAsLongLong(robj *o, long long *llval) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    if (o->encoding == OBJ_ENCODING_INT) {\n        if (llval) *llval = (long) o->ptr;\n        return C_OK;\n    } else {\n        return isSdsRepresentableAsLongLong(o->ptr,llval);\n    }\n}\n\n/* Optimize the SDS string inside the string object to require little space,\n * in case there is more than 10% of free space at the end of the SDS\n * string. This happens because SDS strings tend to overallocate to avoid\n * wasting too much time in allocations when appending to the string. */\nvoid trimStringObjectIfNeeded(robj *o) {\n    if (o->encoding == OBJ_ENCODING_RAW &&\n        sdsavail(o->ptr) > sdslen(o->ptr)/10)\n    {\n        o->ptr = sdsRemoveFreeSpace(o->ptr);\n    }\n}\n\n/* Try to encode a string object in order to save space */\nrobj *tryObjectEncoding(robj *o) {\n    long value;\n    sds s = o->ptr;\n    size_t len;\n\n    /* Make sure this is a string object, the only type we encode\n     * in this function. Other types use encoded memory efficient\n     * representations but are handled by the commands implementing\n     * the type. */\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n\n    /* We try some specialized encoding only for objects that are\n     * RAW or EMBSTR encoded, in other words objects that are still\n     * in represented by an actually array of chars. */\n    if (!sdsEncodedObject(o)) return o;\n\n    /* It's 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     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 20 chars is not\n     * representable as a 32 nor 64 bit integer. */\n    len = sdslen(s);\n    if (len <= 20 && 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            !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) &&\n            value >= 0 &&\n            value < OBJ_SHARED_INTEGERS)\n        {\n            decrRefCount(o);\n            incrRefCount(shared.integers[value]);\n            return shared.integers[value];\n        } else {\n            if (o->encoding == OBJ_ENCODING_RAW) {\n                sdsfree(o->ptr);\n                o->encoding = OBJ_ENCODING_INT;\n                o->ptr = (void*) value;\n                return o;\n            } else if (o->encoding == OBJ_ENCODING_EMBSTR) {\n                decrRefCount(o);\n                return createStringObjectFromLongLongForValue(value);\n            }\n        }\n    }\n\n    /* If the string is small and is still RAW encoded,\n     * try the EMBSTR encoding which is more efficient.\n     * In this representation the object and the SDS string are allocated\n     * in the same chunk of memory to save space and cache misses. */\n    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {\n        robj *emb;\n\n        if (o->encoding == OBJ_ENCODING_EMBSTR) return o;\n        emb = createEmbeddedStringObject(s,sdslen(s));\n        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     * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */\n    trimStringObjectIfNeeded(o);\n\n    /* Return the original object. */\n    return o;\n}\n\n/* Get a decoded version of an encoded object (returned as a new object).\n * If the object is already raw-encoded just increment the ref count. */\nrobj *getDecodedObject(robj *o) {\n    robj *dec;\n\n    if (sdsEncodedObject(o)) {\n        incrRefCount(o);\n        return o;\n    }\n    if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {\n        char buf[32];\n\n        ll2string(buf,32,(long)o->ptr);\n        dec = createStringObject(buf,strlen(buf));\n        return dec;\n    } else {\n        serverPanic(\"Unknown encoding type\");\n    }\n}\n\n/* Compare two string objects via strcmp() or strcoll() depending on flags.\n * Note that the objects may be integer-encoded. In such a case we\n * use ll2string() to get a string representation of the numbers on the stack\n * and compare the strings, it's much faster than calling getDecodedObject().\n *\n * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison\n * is used. */\n\n#define REDIS_COMPARE_BINARY (1<<0)\n#define REDIS_COMPARE_COLL (1<<1)\n\nint compareStringObjectsWithFlags(robj *a, robj *b, int flags) {\n    serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING);\n    char bufa[128], bufb[128], *astr, *bstr;\n    size_t alen, blen, minlen;\n\n    if (a == b) return 0;\n    if (sdsEncodedObject(a)) {\n        astr = a->ptr;\n        alen = sdslen(astr);\n    } else {\n        alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);\n        astr = bufa;\n    }\n    if (sdsEncodedObject(b)) {\n        bstr = b->ptr;\n        blen = sdslen(bstr);\n    } else {\n        blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);\n        bstr = bufb;\n    }\n    if (flags & REDIS_COMPARE_COLL) {\n        return strcoll(astr,bstr);\n    } else {\n        int cmp;\n\n        minlen = (alen < blen) ? alen : blen;\n        cmp = memcmp(astr,bstr,minlen);\n        if (cmp == 0) return alen-blen;\n        return cmp;\n    }\n}\n\n/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */\nint compareStringObjects(robj *a, robj *b) {\n    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);\n}\n\n/* Wrapper for compareStringObjectsWithFlags() using collation. */\nint collateStringObjects(robj *a, robj *b) {\n    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);\n}\n\n/* Equal string objects return 1 if the two objects are the same from the\n * point of view of a string comparison, otherwise 0 is returned. Note that\n * this function is faster then checking for (compareStringObject(a,b) == 0)\n * because it can perform some more optimization. */\nint equalStringObjects(robj *a, robj *b) {\n    if (a->encoding == OBJ_ENCODING_INT &&\n        b->encoding == OBJ_ENCODING_INT){\n        /* If both strings are integer encoded just check if the stored\n         * long is the same. */\n        return a->ptr == b->ptr;\n    } else {\n        return compareStringObjects(a,b) == 0;\n    }\n}\n\nsize_t stringObjectLen(robj *o) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    if (sdsEncodedObject(o)) {\n        return sdslen(o->ptr);\n    } else {\n        return sdigits10((long)o->ptr);\n    }\n}\n\nint getDoubleFromObject(const robj *o, double *target) {\n    double value;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            if (!string2d(o->ptr, sdslen(o->ptr), &value))\n                return C_ERR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg) {\n    double value;\n    if (getDoubleFromObject(o, &value) != C_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongDoubleFromObject(robj *o, long double *target) {\n    long double value;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            if (!string2ld(o->ptr, sdslen(o->ptr), &value))\n                return C_ERR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg) {\n    long double value;\n    if (getLongDoubleFromObject(o, &value) != C_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongLongFromObject(robj *o, long long *target) {\n    long long value;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    if (target) *target = value;\n    return C_OK;\n}\n\nint getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {\n    long long value;\n    if (getLongLongFromObject(o, &value) != C_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 C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {\n    long long value;\n\n    if (getLongLongFromObjectOrReply(c, o, &value, msg) != C_OK) return C_ERR;\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 C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nchar *strEncoding(int encoding) {\n    switch(encoding) {\n    case OBJ_ENCODING_RAW: return \"raw\";\n    case OBJ_ENCODING_INT: return \"int\";\n    case OBJ_ENCODING_HT: return \"hashtable\";\n    case OBJ_ENCODING_QUICKLIST: return \"quicklist\";\n    case OBJ_ENCODING_ZIPLIST: return \"ziplist\";\n    case OBJ_ENCODING_INTSET: return \"intset\";\n    case OBJ_ENCODING_SKIPLIST: return \"skiplist\";\n    case OBJ_ENCODING_EMBSTR: return \"embstr\";\n    default: return \"unknown\";\n    }\n}\n\n/* =========================== Memory introspection ========================= */\n\n\n/* This is an helper function with the goal of estimating the memory\n * size of a radix tree that is used to store Stream IDs.\n *\n * Note: to guess the size of the radix tree is not trivial, so we\n * approximate it considering 16 bytes of data overhead for each\n * key (the ID), and then adding the number of bare nodes, plus some\n * overhead due by the data and child pointers. This secret recipe\n * was obtained by checking the average radix tree created by real\n * workloads, and then adjusting the constants to get numbers that\n * more or less match the real memory usage.\n *\n * Actually the number of nodes and keys may be different depending\n * on the insertion speed and thus the ability of the radix tree\n * to compress prefixes. */\nsize_t streamRadixTreeMemoryUsage(rax *rax) {\n    size_t size;\n    size = rax->numele * sizeof(streamID);\n    size += rax->numnodes * sizeof(raxNode);\n    /* Add a fixed overhead due to the aux data pointer, children, ... */\n    size += rax->numnodes * sizeof(long)*30;\n    return size;\n}\n\n/* Returns the size in bytes consumed by the key's value in RAM.\n * Note that the returned value is just an approximation, especially in the\n * case of aggregated data types where only \"sample_size\" elements\n * are checked and averaged to estimate the total size. */\n#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */\nsize_t objectComputeSize(robj *o, size_t sample_size) {\n    sds ele, ele2;\n    dict *d;\n    dictIterator *di;\n    struct dictEntry *de;\n    size_t asize = 0, elesize = 0, samples = 0;\n\n    if (o->type == OBJ_STRING) {\n        if(o->encoding == OBJ_ENCODING_INT) {\n            asize = sizeof(*o);\n        } else if(o->encoding == OBJ_ENCODING_RAW) {\n            asize = sdsAllocSize(o->ptr)+sizeof(*o);\n        } else if(o->encoding == OBJ_ENCODING_EMBSTR) {\n            asize = sdslen(o->ptr)+2+sizeof(*o);\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    } else if (o->type == OBJ_LIST) {\n        if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n            quicklist *ql = o->ptr;\n            quicklistNode *node = ql->head;\n            asize = sizeof(*o)+sizeof(quicklist);\n            do {\n                elesize += sizeof(quicklistNode)+ziplistBlobLen(node->zl);\n                samples++;\n            } while ((node = node->next) && samples < sample_size);\n            asize += (double)elesize/samples*ql->len;\n        } else if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            asize = sizeof(*o)+ziplistBlobLen(o->ptr);\n        } else {\n            serverPanic(\"Unknown list encoding\");\n        }\n    } else if (o->type == OBJ_SET) {\n        if (o->encoding == OBJ_ENCODING_HT) {\n            d = o->ptr;\n            di = dictGetIterator(d);\n            asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));\n            while((de = dictNext(di)) != NULL && samples < sample_size) {\n                ele = dictGetKey(de);\n                elesize += sizeof(struct dictEntry) + sdsAllocSize(ele);\n                samples++;\n            }\n            dictReleaseIterator(di);\n            if (samples) asize += (double)elesize/samples*dictSize(d);\n        } else if (o->encoding == OBJ_ENCODING_INTSET) {\n            intset *is = o->ptr;\n            asize = sizeof(*o)+sizeof(*is)+is->encoding*is->length;\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (o->type == OBJ_ZSET) {\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            asize = sizeof(*o)+(ziplistBlobLen(o->ptr));\n        } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {\n            d = ((zset*)o->ptr)->dict;\n            zskiplist *zsl = ((zset*)o->ptr)->zsl;\n            zskiplistNode *znode = zsl->header->level[0].forward;\n            asize = sizeof(*o)+sizeof(zset)+sizeof(zskiplist)+sizeof(dict)+\n                    (sizeof(struct dictEntry*)*dictSlots(d))+\n                    zmalloc_size(zsl->header);\n            while(znode != NULL && samples < sample_size) {\n                elesize += sdsAllocSize(znode->ele);\n                elesize += sizeof(struct dictEntry) + zmalloc_size(znode);\n                samples++;\n                znode = znode->level[0].forward;\n            }\n            if (samples) asize += (double)elesize/samples*dictSize(d);\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (o->type == OBJ_HASH) {\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            asize = sizeof(*o)+(ziplistBlobLen(o->ptr));\n        } else if (o->encoding == OBJ_ENCODING_HT) {\n            d = o->ptr;\n            di = dictGetIterator(d);\n            asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));\n            while((de = dictNext(di)) != NULL && samples < sample_size) {\n                ele = dictGetKey(de);\n                ele2 = dictGetVal(de);\n                elesize += sdsAllocSize(ele) + sdsAllocSize(ele2);\n                elesize += sizeof(struct dictEntry);\n                samples++;\n            }\n            dictReleaseIterator(di);\n            if (samples) asize += (double)elesize/samples*dictSize(d);\n        } else {\n            serverPanic(\"Unknown hash encoding\");\n        }\n    } else if (o->type == OBJ_STREAM) {\n        stream *s = o->ptr;\n        asize = sizeof(*o);\n        asize += streamRadixTreeMemoryUsage(s->rax);\n\n        /* Now we have to add the listpacks. The last listpack is often non\n         * complete, so we estimate the size of the first N listpacks, and\n         * use the average to compute the size of the first N-1 listpacks, and\n         * finally add the real size of the last node. */\n        raxIterator ri;\n        raxStart(&ri,s->rax);\n        raxSeek(&ri,\"^\",NULL,0);\n        size_t lpsize = 0, samples = 0;\n        while(samples < sample_size && raxNext(&ri)) {\n            unsigned char *lp = ri.data;\n            lpsize += lpBytes(lp);\n            samples++;\n        }\n        if (s->rax->numele <= samples) {\n            asize += lpsize;\n        } else {\n            if (samples) lpsize /= samples; /* Compute the average. */\n            asize += lpsize * (s->rax->numele-1);\n            /* No need to check if seek succeeded, we enter this branch only\n             * if there are a few elements in the radix tree. */\n            raxSeek(&ri,\"$\",NULL,0);\n            raxNext(&ri);\n            asize += lpBytes(ri.data);\n        }\n        raxStop(&ri);\n\n        /* Consumer groups also have a non trivial memory overhead if there\n         * are many consumers and many groups, let's count at least the\n         * overhead of the pending entries in the groups and consumers\n         * PELs. */\n        if (s->cgroups) {\n            raxStart(&ri,s->cgroups);\n            raxSeek(&ri,\"^\",NULL,0);\n            while(raxNext(&ri)) {\n                streamCG *cg = ri.data;\n                asize += sizeof(*cg);\n                asize += streamRadixTreeMemoryUsage(cg->pel);\n                asize += sizeof(streamNACK)*raxSize(cg->pel);\n\n                /* For each consumer we also need to add the basic data\n                 * structures and the PEL memory usage. */\n                raxIterator cri;\n                raxStart(&cri,cg->consumers);\n                raxSeek(&cri,\"^\",NULL,0);\n                while(raxNext(&cri)) {\n                    streamConsumer *consumer = cri.data;\n                    asize += sizeof(*consumer);\n                    asize += sdslen(consumer->name);\n                    asize += streamRadixTreeMemoryUsage(consumer->pel);\n                    /* Don't count NACKs again, they are shared with the\n                     * consumer group PEL. */\n                }\n                raxStop(&cri);\n            }\n            raxStop(&ri);\n        }\n    } else if (o->type == OBJ_MODULE) {\n        moduleValue *mv = o->ptr;\n        moduleType *mt = mv->type;\n        if (mt->mem_usage != NULL) {\n            asize = mt->mem_usage(mv->value);\n        } else {\n            asize = 0;\n        }\n    } else {\n        serverPanic(\"Unknown object type\");\n    }\n    return asize;\n}\n\n/* Release data obtained with getMemoryOverheadData(). */\nvoid freeMemoryOverheadData(struct redisMemOverhead *mh) {\n    zfree(mh->db);\n    zfree(mh);\n}\n\n/* Return a struct redisMemOverhead filled with memory overhead\n * information used for the MEMORY OVERHEAD and INFO command. The returned\n * structure pointer should be freed calling freeMemoryOverheadData(). */\nstruct redisMemOverhead *getMemoryOverheadData(void) {\n    int j;\n    size_t mem_total = 0;\n    size_t mem = 0;\n    size_t zmalloc_used = zmalloc_used_memory();\n    struct redisMemOverhead *mh = zcalloc(sizeof(*mh));\n\n    mh->total_allocated = zmalloc_used;\n    mh->startup_allocated = server.initial_memory_usage;\n    mh->peak_allocated = server.stat_peak_memory;\n    mh->total_frag =\n        (float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.zmalloc_used;\n    mh->total_frag_bytes =\n        server.cron_malloc_stats.process_rss - server.cron_malloc_stats.zmalloc_used;\n    mh->allocator_frag =\n        (float)server.cron_malloc_stats.allocator_active / server.cron_malloc_stats.allocator_allocated;\n    mh->allocator_frag_bytes =\n        server.cron_malloc_stats.allocator_active - server.cron_malloc_stats.allocator_allocated;\n    mh->allocator_rss =\n        (float)server.cron_malloc_stats.allocator_resident / server.cron_malloc_stats.allocator_active;\n    mh->allocator_rss_bytes =\n        server.cron_malloc_stats.allocator_resident - server.cron_malloc_stats.allocator_active;\n    mh->rss_extra =\n        (float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.allocator_resident;\n    mh->rss_extra_bytes =\n        server.cron_malloc_stats.process_rss - server.cron_malloc_stats.allocator_resident;\n\n    mem_total += server.initial_memory_usage;\n\n    mem = 0;\n    if (server.repl_backlog)\n        mem += zmalloc_size(server.repl_backlog);\n    mh->repl_backlog = mem;\n    mem_total += mem;\n\n    /* Computing the memory used by the clients would be O(N) if done\n     * here online. We use our values computed incrementally by\n     * clientsCronTrackClientsMemUsage(). */\n    mh->clients_slaves = server.stat_clients_type_memory[CLIENT_TYPE_SLAVE];\n    mh->clients_normal = server.stat_clients_type_memory[CLIENT_TYPE_MASTER]+\n                         server.stat_clients_type_memory[CLIENT_TYPE_PUBSUB]+\n                         server.stat_clients_type_memory[CLIENT_TYPE_NORMAL];\n    mem_total += mh->clients_slaves;\n    mem_total += mh->clients_normal;\n\n    mem = 0;\n    if (server.aof_state != AOF_OFF) {\n        mem += sdsalloc(server.aof_buf);\n        mem += aofRewriteBufferSize();\n    }\n    mh->aof_buffer = mem;\n    mem_total+=mem;\n\n    mem = server.lua_scripts_mem;\n    mem += dictSize(server.lua_scripts) * sizeof(dictEntry) +\n        dictSlots(server.lua_scripts) * sizeof(dictEntry*);\n    mem += dictSize(server.repl_scriptcache_dict) * sizeof(dictEntry) +\n        dictSlots(server.repl_scriptcache_dict) * sizeof(dictEntry*);\n    if (listLength(server.repl_scriptcache_fifo) > 0) {\n        mem += listLength(server.repl_scriptcache_fifo) * (sizeof(listNode) + \n            sdsZmallocSize(listNodeValue(listFirst(server.repl_scriptcache_fifo))));\n    }\n    mh->lua_caches = mem;\n    mem_total+=mem;\n\n    for (j = 0; j < server.dbnum; j++) {\n        redisDb *db = server.db+j;\n        long long keyscount = dictSize(db->dict);\n        if (keyscount==0) continue;\n\n        mh->total_keys += keyscount;\n        mh->db = zrealloc(mh->db,sizeof(mh->db[0])*(mh->num_dbs+1));\n        mh->db[mh->num_dbs].dbid = j;\n\n        mem = dictSize(db->dict) * sizeof(dictEntry) +\n              dictSlots(db->dict) * sizeof(dictEntry*) +\n              dictSize(db->dict) * sizeof(robj);\n        mh->db[mh->num_dbs].overhead_ht_main = mem;\n        mem_total+=mem;\n\n        mem = dictSize(db->expires) * sizeof(dictEntry) +\n              dictSlots(db->expires) * sizeof(dictEntry*);\n        mh->db[mh->num_dbs].overhead_ht_expires = mem;\n        mem_total+=mem;\n\n        mh->num_dbs++;\n    }\n\n    mh->overhead_total = mem_total;\n    mh->dataset = zmalloc_used - mem_total;\n    mh->peak_perc = (float)zmalloc_used*100/mh->peak_allocated;\n\n    /* Metrics computed after subtracting the startup memory from\n     * the total memory. */\n    size_t net_usage = 1;\n    if (zmalloc_used > mh->startup_allocated)\n        net_usage = zmalloc_used - mh->startup_allocated;\n    mh->dataset_perc = (float)mh->dataset*100/net_usage;\n    mh->bytes_per_key = mh->total_keys ? (net_usage / mh->total_keys) : 0;\n\n    return mh;\n}\n\n/* Helper for \"MEMORY allocator-stats\", used as a callback for the jemalloc\n * stats output. */\nvoid inputCatSds(void *result, const char *str) {\n    /* result is actually a (sds *), so re-cast it here */\n    sds *info = (sds *)result;\n    *info = sdscat(*info, str);\n}\n\n/* This implements MEMORY DOCTOR. An human readable analysis of the Redis\n * memory condition. */\nsds getMemoryDoctorReport(void) {\n    int empty = 0;          /* Instance is empty or almost empty. */\n    int big_peak = 0;       /* Memory peak is much larger than used mem. */\n    int high_frag = 0;      /* High fragmentation. */\n    int high_alloc_frag = 0;/* High allocator fragmentation. */\n    int high_proc_rss = 0;  /* High process rss overhead. */\n    int high_alloc_rss = 0; /* High rss overhead. */\n    int big_slave_buf = 0;  /* Slave buffers are too big. */\n    int big_client_buf = 0; /* Client buffers are too big. */\n    int many_scripts = 0;   /* Script cache has too many scripts. */\n    int num_reports = 0;\n    struct redisMemOverhead *mh = getMemoryOverheadData();\n\n    if (mh->total_allocated < (1024*1024*5)) {\n        empty = 1;\n        num_reports++;\n    } else {\n        /* Peak is > 150% of current used memory? */\n        if (((float)mh->peak_allocated / mh->total_allocated) > 1.5) {\n            big_peak = 1;\n            num_reports++;\n        }\n\n        /* Fragmentation is higher than 1.4 and 10MB ?*/\n        if (mh->total_frag > 1.4 && mh->total_frag_bytes > 10<<20) {\n            high_frag = 1;\n            num_reports++;\n        }\n\n        /* External fragmentation is higher than 1.1 and 10MB? */\n        if (mh->allocator_frag > 1.1 && mh->allocator_frag_bytes > 10<<20) {\n            high_alloc_frag = 1;\n            num_reports++;\n        }\n\n        /* Allocator rss is higher than 1.1 and 10MB ? */\n        if (mh->allocator_rss > 1.1 && mh->allocator_rss_bytes > 10<<20) {\n            high_alloc_rss = 1;\n            num_reports++;\n        }\n\n        /* Non-Allocator rss is higher than 1.1 and 10MB ? */\n        if (mh->rss_extra > 1.1 && mh->rss_extra_bytes > 10<<20) {\n            high_proc_rss = 1;\n            num_reports++;\n        }\n\n        /* Clients using more than 200k each average? */\n        long numslaves = listLength(server.slaves);\n        long numclients = listLength(server.clients)-numslaves;\n        if (mh->clients_normal / numclients > (1024*200)) {\n            big_client_buf = 1;\n            num_reports++;\n        }\n\n        /* Slaves using more than 10 MB each? */\n        if (numslaves > 0 && mh->clients_slaves / numslaves > (1024*1024*10)) {\n            big_slave_buf = 1;\n            num_reports++;\n        }\n\n        /* Too many scripts are cached? */\n        if (dictSize(server.lua_scripts) > 1000) {\n            many_scripts = 1;\n            num_reports++;\n        }\n    }\n\n    sds s;\n    if (num_reports == 0) {\n        s = sdsnew(\n        \"Hi Sam, I can't find any memory issue in your instance. \"\n        \"I can only account for what occurs on this base.\\n\");\n    } else if (empty == 1) {\n        s = sdsnew(\n        \"Hi Sam, this instance is empty or is using very little memory, \"\n        \"my issues detector can't be used in these conditions. \"\n        \"Please, leave for your mission on Earth and fill it with some data. \"\n        \"The new Sam and I will be back to our programming as soon as I \"\n        \"finished rebooting.\\n\");\n    } else {\n        s = sdsnew(\"Sam, I detected a few issues in this Redis instance memory implants:\\n\\n\");\n        if (big_peak) {\n            s = sdscat(s,\" * Peak memory: In the past this instance used more than 150% the memory that is currently using. The allocator is normally not able to release memory after a peak, so you can expect to see a big fragmentation ratio, however this is actually harmless and is only due to the memory peak, and if the Redis instance Resident Set Size (RSS) is currently bigger than expected, the memory will be used as soon as you fill the Redis instance with more data. If the memory peak was only occasional and you want to try to reclaim memory, please try the MEMORY PURGE command, otherwise the only other option is to shutdown and restart the instance.\\n\\n\");\n        }\n        if (high_frag) {\n            s = sdscatprintf(s,\" * High total RSS: This instance has a memory fragmentation and RSS overhead greater than 1.4 (this means that the Resident Set Size of the Redis process is much larger than the sum of the logical allocations Redis performed). This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. If the problem is a large peak memory, then there is no issue. Otherwise, make sure you are using the Jemalloc allocator and not the default libc malloc. Note: The currently used allocator is \\\"%s\\\".\\n\\n\", ZMALLOC_LIB);\n        }\n        if (high_alloc_frag) {\n            s = sdscatprintf(s,\" * High allocator fragmentation: This instance has an allocator external fragmentation greater than 1.1. This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. You can try enabling 'activedefrag' config option.\\n\\n\");\n        }\n        if (high_alloc_rss) {\n            s = sdscatprintf(s,\" * High allocator RSS overhead: This instance has an RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the allocator is much larger than the sum what the allocator actually holds). This problem is usually due to a large peak memory (check if there is a peak memory entry above in the report), you can try the MEMORY PURGE command to reclaim it.\\n\\n\");\n        }\n        if (high_proc_rss) {\n            s = sdscatprintf(s,\" * High process RSS overhead: This instance has non-allocator RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the Redis process is much larger than the RSS the allocator holds). This problem may be due to Lua scripts or Modules.\\n\\n\");\n        }\n        if (big_slave_buf) {\n            s = sdscat(s,\" * Big replica buffers: The replica output buffers in this instance are greater than 10MB for each replica (on average). This likely means that there is some replica instance that is struggling receiving data, either because it is too slow or because of networking issues. As a result, data piles on the master output buffers. Please try to identify what replica is not receiving data correctly and why. You can use the INFO output in order to check the replicas delays and the CLIENT LIST command to check the output buffers of each replica.\\n\\n\");\n        }\n        if (big_client_buf) {\n            s = sdscat(s,\" * Big client buffers: The clients output buffers in this instance are greater than 200K per client (on average). This may result from different causes, like Pub/Sub clients subscribed to channels bot not receiving data fast enough, so that data piles on the Redis instance output buffer, or clients sending commands with large replies or very large sequences of commands in the same pipeline. Please use the CLIENT LIST command in order to investigate the issue if it causes problems in your instance, or to understand better why certain clients are using a big amount of memory.\\n\\n\");\n        }\n        if (many_scripts) {\n            s = sdscat(s,\" * Many scripts: There seem to be many cached scripts in this instance (more than 1000). This may be because scripts are generated and `EVAL`ed, instead of being parameterized (with KEYS and ARGV), `SCRIPT LOAD`ed and `EVALSHA`ed. Unless `SCRIPT FLUSH` is called periodically, the scripts' caches may end up consuming most of your memory.\\n\\n\");\n        }\n        s = sdscat(s,\"I'm here to keep you safe, Sam. I want to help you.\\n\");\n    }\n    freeMemoryOverheadData(mh);\n    return s;\n}\n\n/* Set the object LRU/LFU depending on server.maxmemory_policy.\n * The lfu_freq arg is only relevant if policy is MAXMEMORY_FLAG_LFU.\n * The lru_idle and lru_clock args are only relevant if policy\n * is MAXMEMORY_FLAG_LRU.\n * Either or both of them may be <0, in that case, nothing is set. */\nint objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,\n                       long long lru_clock, int lru_multiplier) {\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        if (lfu_freq >= 0) {\n            serverAssert(lfu_freq <= 255);\n            val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;\n            return 1;\n        }\n    } else if (lru_idle >= 0) {\n        /* Provided LRU idle time is in seconds. Scale\n         * according to the LRU clock resolution this Redis\n         * instance was compiled with (normally 1000 ms, so the\n         * below statement will expand to lru_idle*1000/1000. */\n        lru_idle = lru_idle*lru_multiplier/LRU_CLOCK_RESOLUTION;\n        long lru_abs = lru_clock - lru_idle; /* Absolute access time. */\n        /* If the LRU field underflows (since LRU it is a wrapping\n         * clock), the best we can do is to provide a large enough LRU\n         * that is half-way in the circlular LRU clock we use: this way\n         * the computed idle time for this object will stay high for quite\n         * some time. */\n        if (lru_abs < 0)\n            lru_abs = (lru_clock+(LRU_CLOCK_MAX/2)) % LRU_CLOCK_MAX;\n        val->lru = lru_abs;\n        return 1;\n    }\n    return 0;\n}\n\n/* ======================= The OBJECT and MEMORY commands =================== */\n\n/* This is a helper function for the OBJECT command. We need to lookup keys\n * without any modification of LRU or other parameters. */\nrobj *objectCommandLookup(client *c, robj *key) {\n    dictEntry *de;\n\n    if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;\n    return (robj*) dictGetVal(de);\n}\n\nrobj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {\n    robj *o = objectCommandLookup(c,key);\n\n    if (!o) addReply(c, reply);\n    return o;\n}\n\n/* Object command allows to inspect the internals of an Redis Object.\n * Usage: OBJECT <refcount|encoding|idletime|freq> <key> */\nvoid objectCommand(client *c) {\n    robj *o;\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.\",\n\"FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.\",\n\"IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.\",\n\"REFCOUNT <key> -- Return the number of references of the value associated with the specified key.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"refcount\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        addReplyLongLong(c,o->refcount);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"encoding\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        addReplyBulkCString(c,strEncoding(o->encoding));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"idletime\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n            addReplyError(c,\"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.\");\n            return;\n        }\n        addReplyLongLong(c,estimateObjectIdleTime(o)/1000);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"freq\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        if (!(server.maxmemory_policy & MAXMEMORY_FLAG_LFU)) {\n            addReplyError(c,\"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.\");\n            return;\n        }\n        /* LFUDecrAndReturn should be called\n         * in case of the key has not been accessed for a long time,\n         * because we update the access time only\n         * when the key is read or overwritten. */\n        addReplyLongLong(c,LFUDecrAndReturn(o));\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\n/* The memory command will eventually be a complete interface for the\n * memory introspection capabilities of Redis.\n *\n * Usage: MEMORY usage <key> */\nvoid memoryCommand(client *c) {\n    if (!strcasecmp(c->argv[1]->ptr,\"help\") && c->argc == 2) {\n        const char *help[] = {\n\"DOCTOR - Return memory problems reports.\",\n\"MALLOC-STATS -- Return internal statistics report from the memory allocator.\",\n\"PURGE -- Attempt to purge dirty pages for reclamation by the allocator.\",\n\"STATS -- Return information about the memory usage of the server.\",\n\"USAGE <key> [SAMPLES <count>] -- Return memory in bytes used by <key> and its value. Nested values are sampled up to <count> times (default: 5).\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"usage\") && c->argc >= 3) {\n        dictEntry *de;\n        long long samples = OBJ_COMPUTE_SIZE_DEF_SAMPLES;\n        for (int j = 3; j < c->argc; j++) {\n            if (!strcasecmp(c->argv[j]->ptr,\"samples\") &&\n                j+1 < c->argc)\n            {\n                if (getLongLongFromObjectOrReply(c,c->argv[j+1],&samples,NULL)\n                     == C_ERR) return;\n                if (samples < 0) {\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                if (samples == 0) samples = LLONG_MAX;;\n                j++; /* skip option argument. */\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n        if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {\n            addReplyNull(c);\n            return;\n        }\n        size_t usage = objectComputeSize(dictGetVal(de),samples);\n        usage += sdsAllocSize(dictGetKey(de));\n        usage += sizeof(dictEntry);\n        addReplyLongLong(c,usage);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"stats\") && c->argc == 2) {\n        struct redisMemOverhead *mh = getMemoryOverheadData();\n\n        addReplyMapLen(c,25+mh->num_dbs);\n\n        addReplyBulkCString(c,\"peak.allocated\");\n        addReplyLongLong(c,mh->peak_allocated);\n\n        addReplyBulkCString(c,\"total.allocated\");\n        addReplyLongLong(c,mh->total_allocated);\n\n        addReplyBulkCString(c,\"startup.allocated\");\n        addReplyLongLong(c,mh->startup_allocated);\n\n        addReplyBulkCString(c,\"replication.backlog\");\n        addReplyLongLong(c,mh->repl_backlog);\n\n        addReplyBulkCString(c,\"clients.slaves\");\n        addReplyLongLong(c,mh->clients_slaves);\n\n        addReplyBulkCString(c,\"clients.normal\");\n        addReplyLongLong(c,mh->clients_normal);\n\n        addReplyBulkCString(c,\"aof.buffer\");\n        addReplyLongLong(c,mh->aof_buffer);\n\n        addReplyBulkCString(c,\"lua.caches\");\n        addReplyLongLong(c,mh->lua_caches);\n\n        for (size_t j = 0; j < mh->num_dbs; j++) {\n            char dbname[32];\n            snprintf(dbname,sizeof(dbname),\"db.%zd\",mh->db[j].dbid);\n            addReplyBulkCString(c,dbname);\n            addReplyMapLen(c,2);\n\n            addReplyBulkCString(c,\"overhead.hashtable.main\");\n            addReplyLongLong(c,mh->db[j].overhead_ht_main);\n\n            addReplyBulkCString(c,\"overhead.hashtable.expires\");\n            addReplyLongLong(c,mh->db[j].overhead_ht_expires);\n        }\n\n        addReplyBulkCString(c,\"overhead.total\");\n        addReplyLongLong(c,mh->overhead_total);\n\n        addReplyBulkCString(c,\"keys.count\");\n        addReplyLongLong(c,mh->total_keys);\n\n        addReplyBulkCString(c,\"keys.bytes-per-key\");\n        addReplyLongLong(c,mh->bytes_per_key);\n\n        addReplyBulkCString(c,\"dataset.bytes\");\n        addReplyLongLong(c,mh->dataset);\n\n        addReplyBulkCString(c,\"dataset.percentage\");\n        addReplyDouble(c,mh->dataset_perc);\n\n        addReplyBulkCString(c,\"peak.percentage\");\n        addReplyDouble(c,mh->peak_perc);\n\n        addReplyBulkCString(c,\"allocator.allocated\");\n        addReplyLongLong(c,server.cron_malloc_stats.allocator_allocated);\n\n        addReplyBulkCString(c,\"allocator.active\");\n        addReplyLongLong(c,server.cron_malloc_stats.allocator_active);\n\n        addReplyBulkCString(c,\"allocator.resident\");\n        addReplyLongLong(c,server.cron_malloc_stats.allocator_resident);\n\n        addReplyBulkCString(c,\"allocator-fragmentation.ratio\");\n        addReplyDouble(c,mh->allocator_frag);\n\n        addReplyBulkCString(c,\"allocator-fragmentation.bytes\");\n        addReplyLongLong(c,mh->allocator_frag_bytes);\n\n        addReplyBulkCString(c,\"allocator-rss.ratio\");\n        addReplyDouble(c,mh->allocator_rss);\n\n        addReplyBulkCString(c,\"allocator-rss.bytes\");\n        addReplyLongLong(c,mh->allocator_rss_bytes);\n\n        addReplyBulkCString(c,\"rss-overhead.ratio\");\n        addReplyDouble(c,mh->rss_extra);\n\n        addReplyBulkCString(c,\"rss-overhead.bytes\");\n        addReplyLongLong(c,mh->rss_extra_bytes);\n\n        addReplyBulkCString(c,\"fragmentation\"); /* this is the total RSS overhead, including fragmentation */\n        addReplyDouble(c,mh->total_frag); /* it is kept here for backwards compatibility */\n\n        addReplyBulkCString(c,\"fragmentation.bytes\");\n        addReplyLongLong(c,mh->total_frag_bytes);\n\n        freeMemoryOverheadData(mh);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"malloc-stats\") && c->argc == 2) {\n#if defined(USE_JEMALLOC)\n        sds info = sdsempty();\n        je_malloc_stats_print(inputCatSds, &info, NULL);\n        addReplyVerbatim(c,info,sdslen(info),\"txt\");\n        sdsfree(info);\n#else\n        addReplyBulkCString(c,\"Stats not supported for the current allocator\");\n#endif\n    } else if (!strcasecmp(c->argv[1]->ptr,\"doctor\") && c->argc == 2) {\n        sds report = getMemoryDoctorReport();\n        addReplyVerbatim(c,report,sdslen(report),\"txt\");\n        sdsfree(report);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"purge\") && c->argc == 2) {\n        if (jemalloc_purge() == 0)\n            addReply(c, shared.ok);\n        else\n            addReplyError(c, \"Error purging dirty pages\");\n    } else {\n        addReplyErrorFormat(c, \"Unknown subcommand or wrong number of arguments for '%s'. Try MEMORY HELP\", (char*)c->argv[1]->ptr);\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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 \"server.h\"\n\nint clientSubscriptionsCount(client *c);\n\n/*-----------------------------------------------------------------------------\n * Pubsub client replies API\n *----------------------------------------------------------------------------*/\n\n/* Send a pubsub message of type \"message\" to the client.\n * Normally 'msg' is a Redis object containing the string to send as\n * message. However if the caller sets 'msg' as NULL, it will be able\n * to send a special message (for instance an Array type) by using the\n * addReply*() API family. */\nvoid addReplyPubsubMessage(client *c, robj *channel, robj *msg) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.messagebulk);\n    addReplyBulk(c,channel);\n    if (msg) addReplyBulk(c,msg);\n}\n\n/* Send a pubsub message of type \"pmessage\" to the client. The difference\n * with the \"message\" type delivered by addReplyPubsubMessage() is that\n * this message format also includes the pattern that matched the message. */\nvoid addReplyPubsubPatMessage(client *c, robj *pat, robj *channel, robj *msg) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[4]);\n    else\n        addReplyPushLen(c,4);\n    addReply(c,shared.pmessagebulk);\n    addReplyBulk(c,pat);\n    addReplyBulk(c,channel);\n    addReplyBulk(c,msg);\n}\n\n/* Send the pubsub subscription notification to the client. */\nvoid addReplyPubsubSubscribed(client *c, robj *channel) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.subscribebulk);\n    addReplyBulk(c,channel);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/* Send the pubsub unsubscription notification to the client.\n * Channel can be NULL: this is useful when the client sends a mass\n * unsubscribe command but there are no channels to unsubscribe from: we\n * still send a notification. */\nvoid addReplyPubsubUnsubscribed(client *c, robj *channel) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.unsubscribebulk);\n    if (channel)\n        addReplyBulk(c,channel);\n    else\n        addReplyNull(c);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/* Send the pubsub pattern subscription notification to the client. */\nvoid addReplyPubsubPatSubscribed(client *c, robj *pattern) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.psubscribebulk);\n    addReplyBulk(c,pattern);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/* Send the pubsub pattern unsubscription notification to the client.\n * Pattern can be NULL: this is useful when the client sends a mass\n * punsubscribe command but there are no pattern to unsubscribe from: we\n * still send a notification. */\nvoid addReplyPubsubPatUnsubscribed(client *c, robj *pattern) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.punsubscribebulk);\n    if (pattern)\n        addReplyBulk(c,pattern);\n    else\n        addReplyNull(c);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/*-----------------------------------------------------------------------------\n * Pubsub low level API\n *----------------------------------------------------------------------------*/\n\nvoid freePubsubPattern(void *p) {\n    pubsubPattern *pat = p;\n\n    decrRefCount(pat->pattern);\n    zfree(pat);\n}\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/* Return the number of channels + patterns a client is subscribed to. */\nint clientSubscriptionsCount(client *c) {\n    return dictSize(c->pubsub_channels)+\n           listLength(c->pubsub_patterns);\n}\n\n/* Subscribe a client to a channel. Returns 1 if the operation succeeded, or\n * 0 if the client was already subscribed to that channel. */\nint pubsubSubscribeChannel(client *c, robj *channel) {\n    dictEntry *de;\n    list *clients = NULL;\n    int retval = 0;\n\n    /* Add the channel to the client -> channels hash table */\n    if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {\n        retval = 1;\n        incrRefCount(channel);\n        /* Add the client to the channel -> list of clients hash table */\n        de = dictFind(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        listAddNodeTail(clients,c);\n    }\n    /* Notify the client */\n    addReplyPubsubSubscribed(c,channel);\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. */\nint pubsubUnsubscribeChannel(client *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    incrRefCount(channel); /* channel may be just a pointer to the same object\n                            we have in the hash tables. Protect it... */\n    if (dictDelete(c->pubsub_channels,channel) == DICT_OK) {\n        retval = 1;\n        /* Remove the client from the channel -> clients list hash table */\n        de = dictFind(server.pubsub_channels,channel);\n        serverAssertWithInfo(c,NULL,de != NULL);\n        clients = dictGetVal(de);\n        ln = listSearchKey(clients,c);\n        serverAssertWithInfo(c,NULL,ln != NULL);\n        listDelNode(clients,ln);\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    /* Notify the client */\n    if (notify) addReplyPubsubUnsubscribed(c,channel);\n    decrRefCount(channel); /* it is finally safe to release it */\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. */\nint pubsubSubscribePattern(client *c, robj *pattern) {\n    dictEntry *de;\n    list *clients;\n    int retval = 0;\n\n    if (listSearchKey(c->pubsub_patterns,pattern) == NULL) {\n        retval = 1;\n        pubsubPattern *pat;\n        listAddNodeTail(c->pubsub_patterns,pattern);\n        incrRefCount(pattern);\n        pat = zmalloc(sizeof(*pat));\n        pat->pattern = getDecodedObject(pattern);\n        pat->client = c;\n        listAddNodeTail(server.pubsub_patterns,pat);\n        /* Add the client to the pattern -> list of clients hash table */\n        de = dictFind(server.pubsub_patterns_dict,pattern);\n        if (de == NULL) {\n            clients = listCreate();\n            dictAdd(server.pubsub_patterns_dict,pattern,clients);\n            incrRefCount(pattern);\n        } else {\n            clients = dictGetVal(de);\n        }\n        listAddNodeTail(clients,c);\n    }\n    /* Notify the client */\n    addReplyPubsubPatSubscribed(c,pattern);\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. */\nint pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {\n    dictEntry *de;\n    list *clients;\n    listNode *ln;\n    pubsubPattern pat;\n    int retval = 0;\n\n    incrRefCount(pattern); /* Protect the object. May be the same we remove */\n    if ((ln = listSearchKey(c->pubsub_patterns,pattern)) != NULL) {\n        retval = 1;\n        listDelNode(c->pubsub_patterns,ln);\n        pat.client = c;\n        pat.pattern = pattern;\n        ln = listSearchKey(server.pubsub_patterns,&pat);\n        listDelNode(server.pubsub_patterns,ln);\n        /* Remove the client from the pattern -> clients list hash table */\n        de = dictFind(server.pubsub_patterns_dict,pattern);\n        serverAssertWithInfo(c,NULL,de != NULL);\n        clients = dictGetVal(de);\n        ln = listSearchKey(clients,c);\n        serverAssertWithInfo(c,NULL,ln != NULL);\n        listDelNode(clients,ln);\n        if (listLength(clients) == 0) {\n            /* Free the list and associated hash entry at all if this was\n             * the latest client. */\n            dictDelete(server.pubsub_patterns_dict,pattern);\n        }\n    }\n    /* Notify the client */\n    if (notify) addReplyPubsubPatUnsubscribed(c,pattern);\n    decrRefCount(pattern);\n    return retval;\n}\n\n/* Unsubscribe from all the channels. Return the number of channels the\n * client was subscribed to. */\nint pubsubUnsubscribeAllChannels(client *c, int notify) {\n    dictIterator *di = dictGetSafeIterator(c->pubsub_channels);\n    dictEntry *de;\n    int count = 0;\n\n    while((de = dictNext(di)) != NULL) {\n        robj *channel = dictGetKey(de);\n\n        count += pubsubUnsubscribeChannel(c,channel,notify);\n    }\n    /* We were subscribed to nothing? Still reply to the client. */\n    if (notify && count == 0) addReplyPubsubUnsubscribed(c,NULL);\n    dictReleaseIterator(di);\n    return count;\n}\n\n/* Unsubscribe from all the patterns. Return the number of patterns the\n * client was subscribed from. */\nint pubsubUnsubscribeAllPatterns(client *c, int notify) {\n    listNode *ln;\n    listIter li;\n    int count = 0;\n\n    listRewind(c->pubsub_patterns,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        robj *pattern = ln->value;\n\n        count += pubsubUnsubscribePattern(c,pattern,notify);\n    }\n    if (notify && count == 0) addReplyPubsubPatUnsubscribed(c,NULL);\n    return count;\n}\n\n/* Publish a message */\nint pubsubPublishMessage(robj *channel, robj *message) {\n    int receivers = 0;\n    dictEntry *de;\n    dictIterator *di;\n    listNode *ln;\n    listIter li;\n\n    /* Send to clients listening for that channel */\n    de = dictFind(server.pubsub_channels,channel);\n    if (de) {\n        list *list = dictGetVal(de);\n        listNode *ln;\n        listIter li;\n\n        listRewind(list,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            client *c = ln->value;\n            addReplyPubsubMessage(c,channel,message);\n            receivers++;\n        }\n    }\n    /* Send to clients listening to matching channels */\n    di = dictGetIterator(server.pubsub_patterns_dict);\n    if (di) {\n        channel = getDecodedObject(channel);\n        while((de = dictNext(di)) != NULL) {\n            robj *pattern = dictGetKey(de);\n            list *clients = dictGetVal(de);\n            if (!stringmatchlen((char*)pattern->ptr,\n                                sdslen(pattern->ptr),\n                                (char*)channel->ptr,\n                                sdslen(channel->ptr),0)) continue;\n\n            listRewind(clients,&li);\n            while ((ln = listNext(&li)) != NULL) {\n                client *c = listNodeValue(ln);\n                addReplyPubsubPatMessage(c,pattern,channel,message);\n                receivers++;\n            }\n        }\n        decrRefCount(channel);\n        dictReleaseIterator(di);\n    }\n    return receivers;\n}\n\n/*-----------------------------------------------------------------------------\n * Pubsub commands implementation\n *----------------------------------------------------------------------------*/\n\nvoid subscribeCommand(client *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribeChannel(c,c->argv[j]);\n    c->flags |= CLIENT_PUBSUB;\n}\n\nvoid unsubscribeCommand(client *c) {\n    if (c->argc == 1) {\n        pubsubUnsubscribeAllChannels(c,1);\n    } else {\n        int j;\n\n        for (j = 1; j < c->argc; j++)\n            pubsubUnsubscribeChannel(c,c->argv[j],1);\n    }\n    if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;\n}\n\nvoid psubscribeCommand(client *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribePattern(c,c->argv[j]);\n    c->flags |= CLIENT_PUBSUB;\n}\n\nvoid punsubscribeCommand(client *c) {\n    if (c->argc == 1) {\n        pubsubUnsubscribeAllPatterns(c,1);\n    } else {\n        int j;\n\n        for (j = 1; j < c->argc; j++)\n            pubsubUnsubscribePattern(c,c->argv[j],1);\n    }\n    if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;\n}\n\nvoid publishCommand(client *c) {\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,PROPAGATE_REPL);\n    addReplyLongLong(c,receivers);\n}\n\n/* PUBSUB command for Pub/Sub introspection. */\nvoid pubsubCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"CHANNELS [<pattern>] -- Return the currently active channels matching a pattern (default: all).\",\n\"NUMPAT -- Return number of subscriptions to patterns.\",\n\"NUMSUB [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none).\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"channels\") &&\n        (c->argc == 2 || c->argc == 3))\n    {\n        /* PUBSUB CHANNELS [<pattern>] */\n        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;\n        dictIterator *di = dictGetIterator(server.pubsub_channels);\n        dictEntry *de;\n        long mblen = 0;\n        void *replylen;\n\n        replylen = addReplyDeferredLen(c);\n        while((de = dictNext(di)) != NULL) {\n            robj *cobj = dictGetKey(de);\n            sds channel = cobj->ptr;\n\n            if (!pat || stringmatchlen(pat, sdslen(pat),\n                                       channel, sdslen(channel),0))\n            {\n                addReplyBulk(c,cobj);\n                mblen++;\n            }\n        }\n        dictReleaseIterator(di);\n        setDeferredArrayLen(c,replylen,mblen);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"numsub\") && c->argc >= 2) {\n        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */\n        int j;\n\n        addReplyArrayLen(c,(c->argc-2)*2);\n        for (j = 2; j < c->argc; j++) {\n            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);\n\n            addReplyBulk(c,c->argv[j]);\n            addReplyLongLong(c,l ? listLength(l) : 0);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"numpat\") && c->argc == 2) {\n        /* PUBSUB NUMPAT */\n        addReplyLongLong(c,listLength(server.pubsub_patterns));\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/quicklist.c",
    "content": "/* quicklist.c - A doubly linked list of ziplists\n *\n * Copyright (c) 2014, Matt Stancliff <matt@genges.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 start the above copyright notice,\n *     this quicklist of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this quicklist 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 <string.h> /* for memcpy */\n#include \"quicklist.h\"\n#include \"zmalloc.h\"\n#include \"ziplist.h\"\n#include \"util.h\" /* for ll2string */\n#include \"lzf.h\"\n\n#if defined(REDIS_TEST) || defined(REDIS_TEST_VERBOSE)\n#include <stdio.h> /* for printf (debug printing), snprintf (genstr) */\n#endif\n\n#ifndef REDIS_STATIC\n#define REDIS_STATIC static\n#endif\n\n/* Optimization levels for size-based filling */\nstatic const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536};\n\n/* Maximum size in bytes of any multi-element ziplist.\n * Larger values will live in their own isolated ziplists. */\n#define SIZE_SAFETY_LIMIT 8192\n\n/* Minimum ziplist size in bytes for attempting compression. */\n#define MIN_COMPRESS_BYTES 48\n\n/* Minimum size reduction in bytes to store compressed quicklistNode data.\n * This also prevents us from storing compression if the compression\n * resulted in a larger size than the original data. */\n#define MIN_COMPRESS_IMPROVE 8\n\n/* If not verbose testing, remove all debug printing. */\n#ifndef REDIS_TEST_VERBOSE\n#define D(...)\n#else\n#define D(...)                                                                 \\\n    do {                                                                       \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(__VA_ARGS__);                                                   \\\n        printf(\"\\n\");                                                          \\\n    } while (0);\n#endif\n\n/* Bookmarks forward declarations */\n#define QL_MAX_BM ((1 << QL_BM_BITS)-1)\nquicklistBookmark *_quicklistBookmarkFindByName(quicklist *ql, const char *name);\nquicklistBookmark *_quicklistBookmarkFindByNode(quicklist *ql, quicklistNode *node);\nvoid _quicklistBookmarkDelete(quicklist *ql, quicklistBookmark *bm);\n\n/* Simple way to give quicklistEntry structs default values with one call. */\n#define initEntry(e)                                                           \\\n    do {                                                                       \\\n        (e)->zi = (e)->value = NULL;                                           \\\n        (e)->longval = -123456789;                                             \\\n        (e)->quicklist = NULL;                                                 \\\n        (e)->node = NULL;                                                      \\\n        (e)->offset = 123456789;                                               \\\n        (e)->sz = 0;                                                           \\\n    } while (0)\n\n#if __GNUC__ >= 3\n#define likely(x) __builtin_expect(!!(x), 1)\n#define unlikely(x) __builtin_expect(!!(x), 0)\n#else\n#define likely(x) (x)\n#define unlikely(x) (x)\n#endif\n\n/* Create a new quicklist.\n * Free with quicklistRelease(). */\nquicklist *quicklistCreate(void) {\n    struct quicklist *quicklist;\n\n    quicklist = zmalloc(sizeof(*quicklist));\n    quicklist->head = quicklist->tail = NULL;\n    quicklist->len = 0;\n    quicklist->count = 0;\n    quicklist->compress = 0;\n    quicklist->fill = -2;\n    quicklist->bookmark_count = 0;\n    return quicklist;\n}\n\n#define COMPRESS_MAX ((1 << QL_COMP_BITS)-1)\nvoid quicklistSetCompressDepth(quicklist *quicklist, int compress) {\n    if (compress > COMPRESS_MAX) {\n        compress = COMPRESS_MAX;\n    } else if (compress < 0) {\n        compress = 0;\n    }\n    quicklist->compress = compress;\n}\n\n#define FILL_MAX ((1 << (QL_FILL_BITS-1))-1)\nvoid quicklistSetFill(quicklist *quicklist, int fill) {\n    if (fill > FILL_MAX) {\n        fill = FILL_MAX;\n    } else if (fill < -5) {\n        fill = -5;\n    }\n    quicklist->fill = fill;\n}\n\nvoid quicklistSetOptions(quicklist *quicklist, int fill, int depth) {\n    quicklistSetFill(quicklist, fill);\n    quicklistSetCompressDepth(quicklist, depth);\n}\n\n/* Create a new quicklist with some default parameters. */\nquicklist *quicklistNew(int fill, int compress) {\n    quicklist *quicklist = quicklistCreate();\n    quicklistSetOptions(quicklist, fill, compress);\n    return quicklist;\n}\n\nREDIS_STATIC quicklistNode *quicklistCreateNode(void) {\n    quicklistNode *node;\n    node = zmalloc(sizeof(*node));\n    node->zl = NULL;\n    node->count = 0;\n    node->sz = 0;\n    node->next = node->prev = NULL;\n    node->encoding = QUICKLIST_NODE_ENCODING_RAW;\n    node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST;\n    node->recompress = 0;\n    return node;\n}\n\n/* Return cached quicklist count */\nunsigned long quicklistCount(const quicklist *ql) { return ql->count; }\n\n/* Free entire quicklist. */\nvoid quicklistRelease(quicklist *quicklist) {\n    unsigned long len;\n    quicklistNode *current, *next;\n\n    current = quicklist->head;\n    len = quicklist->len;\n    while (len--) {\n        next = current->next;\n\n        zfree(current->zl);\n        quicklist->count -= current->count;\n\n        zfree(current);\n\n        quicklist->len--;\n        current = next;\n    }\n    quicklistBookmarksClear(quicklist);\n    zfree(quicklist);\n}\n\n/* Compress the ziplist in 'node' and update encoding details.\n * Returns 1 if ziplist compressed successfully.\n * Returns 0 if compression failed or if ziplist too small to compress. */\nREDIS_STATIC int __quicklistCompressNode(quicklistNode *node) {\n#ifdef REDIS_TEST\n    node->attempted_compress = 1;\n#endif\n\n    /* Don't bother compressing small values */\n    if (node->sz < MIN_COMPRESS_BYTES)\n        return 0;\n\n    quicklistLZF *lzf = zmalloc(sizeof(*lzf) + node->sz);\n\n    /* Cancel if compression fails or doesn't compress small enough */\n    if (((lzf->sz = lzf_compress(node->zl, node->sz, lzf->compressed,\n                                 node->sz)) == 0) ||\n        lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) {\n        /* lzf_compress aborts/rejects compression if value not compressable. */\n        zfree(lzf);\n        return 0;\n    }\n    lzf = zrealloc(lzf, sizeof(*lzf) + lzf->sz);\n    zfree(node->zl);\n    node->zl = (unsigned char *)lzf;\n    node->encoding = QUICKLIST_NODE_ENCODING_LZF;\n    node->recompress = 0;\n    return 1;\n}\n\n/* Compress only uncompressed nodes. */\n#define quicklistCompressNode(_node)                                           \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_RAW) {     \\\n            __quicklistCompressNode((_node));                                  \\\n        }                                                                      \\\n    } while (0)\n\n/* Uncompress the ziplist in 'node' and update encoding details.\n * Returns 1 on successful decode, 0 on failure to decode. */\nREDIS_STATIC int __quicklistDecompressNode(quicklistNode *node) {\n#ifdef REDIS_TEST\n    node->attempted_compress = 0;\n#endif\n\n    void *decompressed = zmalloc(node->sz);\n    quicklistLZF *lzf = (quicklistLZF *)node->zl;\n    if (lzf_decompress(lzf->compressed, lzf->sz, decompressed, node->sz) == 0) {\n        /* Someone requested decompress, but we can't decompress.  Not good. */\n        zfree(decompressed);\n        return 0;\n    }\n    zfree(lzf);\n    node->zl = decompressed;\n    node->encoding = QUICKLIST_NODE_ENCODING_RAW;\n    return 1;\n}\n\n/* Decompress only compressed nodes. */\n#define quicklistDecompressNode(_node)                                         \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) {     \\\n            __quicklistDecompressNode((_node));                                \\\n        }                                                                      \\\n    } while (0)\n\n/* Force node to not be immediately re-compresable */\n#define quicklistDecompressNodeForUse(_node)                                   \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) {     \\\n            __quicklistDecompressNode((_node));                                \\\n            (_node)->recompress = 1;                                           \\\n        }                                                                      \\\n    } while (0)\n\n/* Extract the raw LZF data from this quicklistNode.\n * Pointer to LZF data is assigned to '*data'.\n * Return value is the length of compressed LZF data. */\nsize_t quicklistGetLzf(const quicklistNode *node, void **data) {\n    quicklistLZF *lzf = (quicklistLZF *)node->zl;\n    *data = lzf->compressed;\n    return lzf->sz;\n}\n\n#define quicklistAllowsCompression(_ql) ((_ql)->compress != 0)\n\n/* Force 'quicklist' to meet compression guidelines set by compress depth.\n * The only way to guarantee interior nodes get compressed is to iterate\n * to our \"interior\" compress depth then compress the next node we find.\n * If compress depth is larger than the entire list, we return immediately. */\nREDIS_STATIC void __quicklistCompress(const quicklist *quicklist,\n                                      quicklistNode *node) {\n    /* If length is less than our compress depth (from both sides),\n     * we can't compress anything. */\n    if (!quicklistAllowsCompression(quicklist) ||\n        quicklist->len < (unsigned int)(quicklist->compress * 2))\n        return;\n\n#if 0\n    /* Optimized cases for small depth counts */\n    if (quicklist->compress == 1) {\n        quicklistNode *h = quicklist->head, *t = quicklist->tail;\n        quicklistDecompressNode(h);\n        quicklistDecompressNode(t);\n        if (h != node && t != node)\n            quicklistCompressNode(node);\n        return;\n    } else if (quicklist->compress == 2) {\n        quicklistNode *h = quicklist->head, *hn = h->next, *hnn = hn->next;\n        quicklistNode *t = quicklist->tail, *tp = t->prev, *tpp = tp->prev;\n        quicklistDecompressNode(h);\n        quicklistDecompressNode(hn);\n        quicklistDecompressNode(t);\n        quicklistDecompressNode(tp);\n        if (h != node && hn != node && t != node && tp != node) {\n            quicklistCompressNode(node);\n        }\n        if (hnn != t) {\n            quicklistCompressNode(hnn);\n        }\n        if (tpp != h) {\n            quicklistCompressNode(tpp);\n        }\n        return;\n    }\n#endif\n\n    /* Iterate until we reach compress depth for both sides of the list.a\n     * Note: because we do length checks at the *top* of this function,\n     *       we can skip explicit null checks below. Everything exists. */\n    quicklistNode *forward = quicklist->head;\n    quicklistNode *reverse = quicklist->tail;\n    int depth = 0;\n    int in_depth = 0;\n    while (depth++ < quicklist->compress) {\n        quicklistDecompressNode(forward);\n        quicklistDecompressNode(reverse);\n\n        if (forward == node || reverse == node)\n            in_depth = 1;\n\n        if (forward == reverse)\n            return;\n\n        forward = forward->next;\n        reverse = reverse->prev;\n    }\n\n    if (!in_depth)\n        quicklistCompressNode(node);\n\n    if (depth > 2) {\n        /* At this point, forward and reverse are one node beyond depth */\n        quicklistCompressNode(forward);\n        quicklistCompressNode(reverse);\n    }\n}\n\n#define quicklistCompress(_ql, _node)                                          \\\n    do {                                                                       \\\n        if ((_node)->recompress)                                               \\\n            quicklistCompressNode((_node));                                    \\\n        else                                                                   \\\n            __quicklistCompress((_ql), (_node));                               \\\n    } while (0)\n\n/* If we previously used quicklistDecompressNodeForUse(), just recompress. */\n#define quicklistRecompressOnly(_ql, _node)                                    \\\n    do {                                                                       \\\n        if ((_node)->recompress)                                               \\\n            quicklistCompressNode((_node));                                    \\\n    } while (0)\n\n/* Insert 'new_node' after 'old_node' if 'after' is 1.\n * Insert 'new_node' before 'old_node' if 'after' is 0.\n * Note: 'new_node' is *always* uncompressed, so if we assign it to\n *       head or tail, we do not need to uncompress it. */\nREDIS_STATIC void __quicklistInsertNode(quicklist *quicklist,\n                                        quicklistNode *old_node,\n                                        quicklistNode *new_node, int after) {\n    if (after) {\n        new_node->prev = old_node;\n        if (old_node) {\n            new_node->next = old_node->next;\n            if (old_node->next)\n                old_node->next->prev = new_node;\n            old_node->next = new_node;\n        }\n        if (quicklist->tail == old_node)\n            quicklist->tail = new_node;\n    } else {\n        new_node->next = old_node;\n        if (old_node) {\n            new_node->prev = old_node->prev;\n            if (old_node->prev)\n                old_node->prev->next = new_node;\n            old_node->prev = new_node;\n        }\n        if (quicklist->head == old_node)\n            quicklist->head = new_node;\n    }\n    /* If this insert creates the only element so far, initialize head/tail. */\n    if (quicklist->len == 0) {\n        quicklist->head = quicklist->tail = new_node;\n    }\n\n    if (old_node)\n        quicklistCompress(quicklist, old_node);\n\n    quicklist->len++;\n}\n\n/* Wrappers for node inserting around existing node. */\nREDIS_STATIC void _quicklistInsertNodeBefore(quicklist *quicklist,\n                                             quicklistNode *old_node,\n                                             quicklistNode *new_node) {\n    __quicklistInsertNode(quicklist, old_node, new_node, 0);\n}\n\nREDIS_STATIC void _quicklistInsertNodeAfter(quicklist *quicklist,\n                                            quicklistNode *old_node,\n                                            quicklistNode *new_node) {\n    __quicklistInsertNode(quicklist, old_node, new_node, 1);\n}\n\nREDIS_STATIC int\n_quicklistNodeSizeMeetsOptimizationRequirement(const size_t sz,\n                                               const int fill) {\n    if (fill >= 0)\n        return 0;\n\n    size_t offset = (-fill) - 1;\n    if (offset < (sizeof(optimization_level) / sizeof(*optimization_level))) {\n        if (sz <= optimization_level[offset]) {\n            return 1;\n        } else {\n            return 0;\n        }\n    } else {\n        return 0;\n    }\n}\n\n#define sizeMeetsSafetyLimit(sz) ((sz) <= SIZE_SAFETY_LIMIT)\n\nREDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node,\n                                           const int fill, const size_t sz) {\n    if (unlikely(!node))\n        return 0;\n\n    int ziplist_overhead;\n    /* size of previous offset */\n    if (sz < 254)\n        ziplist_overhead = 1;\n    else\n        ziplist_overhead = 5;\n\n    /* size of forward offset */\n    if (sz < 64)\n        ziplist_overhead += 1;\n    else if (likely(sz < 16384))\n        ziplist_overhead += 2;\n    else\n        ziplist_overhead += 5;\n\n    /* new_sz overestimates if 'sz' encodes to an integer type */\n    unsigned int new_sz = node->sz + sz + ziplist_overhead;\n    if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill)))\n        return 1;\n    else if (!sizeMeetsSafetyLimit(new_sz))\n        return 0;\n    else if ((int)node->count < fill)\n        return 1;\n    else\n        return 0;\n}\n\nREDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a,\n                                          const quicklistNode *b,\n                                          const int fill) {\n    if (!a || !b)\n        return 0;\n\n    /* approximate merged ziplist size (- 11 to remove one ziplist\n     * header/trailer) */\n    unsigned int merge_sz = a->sz + b->sz - 11;\n    if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill)))\n        return 1;\n    else if (!sizeMeetsSafetyLimit(merge_sz))\n        return 0;\n    else if ((int)(a->count + b->count) <= fill)\n        return 1;\n    else\n        return 0;\n}\n\n#define quicklistNodeUpdateSz(node)                                            \\\n    do {                                                                       \\\n        (node)->sz = ziplistBlobLen((node)->zl);                               \\\n    } while (0)\n\n/* Add new entry to head node of quicklist.\n *\n * Returns 0 if used existing head.\n * Returns 1 if new head created. */\nint quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {\n    quicklistNode *orig_head = quicklist->head;\n    if (likely(\n            _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {\n        quicklist->head->zl =\n            ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);\n        quicklistNodeUpdateSz(quicklist->head);\n    } else {\n        quicklistNode *node = quicklistCreateNode();\n        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n\n        quicklistNodeUpdateSz(node);\n        _quicklistInsertNodeBefore(quicklist, quicklist->head, node);\n    }\n    quicklist->count++;\n    quicklist->head->count++;\n    return (orig_head != quicklist->head);\n}\n\n/* Add new entry to tail node of quicklist.\n *\n * Returns 0 if used existing tail.\n * Returns 1 if new tail created. */\nint quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {\n    quicklistNode *orig_tail = quicklist->tail;\n    if (likely(\n            _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {\n        quicklist->tail->zl =\n            ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);\n        quicklistNodeUpdateSz(quicklist->tail);\n    } else {\n        quicklistNode *node = quicklistCreateNode();\n        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);\n\n        quicklistNodeUpdateSz(node);\n        _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);\n    }\n    quicklist->count++;\n    quicklist->tail->count++;\n    return (orig_tail != quicklist->tail);\n}\n\n/* Create new node consisting of a pre-formed ziplist.\n * Used for loading RDBs where entire ziplists have been stored\n * to be retrieved later. */\nvoid quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl) {\n    quicklistNode *node = quicklistCreateNode();\n\n    node->zl = zl;\n    node->count = ziplistLen(node->zl);\n    node->sz = ziplistBlobLen(zl);\n\n    _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);\n    quicklist->count += node->count;\n}\n\n/* Append all values of ziplist 'zl' individually into 'quicklist'.\n *\n * This allows us to restore old RDB ziplists into new quicklists\n * with smaller ziplist sizes than the saved RDB ziplist.\n *\n * Returns 'quicklist' argument. Frees passed-in ziplist 'zl' */\nquicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,\n                                            unsigned char *zl) {\n    unsigned char *value;\n    unsigned int sz;\n    long long longval;\n    char longstr[32] = {0};\n\n    unsigned char *p = ziplistIndex(zl, 0);\n    while (ziplistGet(p, &value, &sz, &longval)) {\n        if (!value) {\n            /* Write the longval as a string so we can re-add it */\n            sz = ll2string(longstr, sizeof(longstr), longval);\n            value = (unsigned char *)longstr;\n        }\n        quicklistPushTail(quicklist, value, sz);\n        p = ziplistNext(zl, p);\n    }\n    zfree(zl);\n    return quicklist;\n}\n\n/* Create new (potentially multi-node) quicklist from a single existing ziplist.\n *\n * Returns new quicklist.  Frees passed-in ziplist 'zl'. */\nquicklist *quicklistCreateFromZiplist(int fill, int compress,\n                                      unsigned char *zl) {\n    return quicklistAppendValuesFromZiplist(quicklistNew(fill, compress), zl);\n}\n\n#define quicklistDeleteIfEmpty(ql, n)                                          \\\n    do {                                                                       \\\n        if ((n)->count == 0) {                                                 \\\n            __quicklistDelNode((ql), (n));                                     \\\n            (n) = NULL;                                                        \\\n        }                                                                      \\\n    } while (0)\n\nREDIS_STATIC void __quicklistDelNode(quicklist *quicklist,\n                                     quicklistNode *node) {\n    /* Update the bookmark if any */\n    quicklistBookmark *bm = _quicklistBookmarkFindByNode(quicklist, node);\n    if (bm) {\n        bm->node = node->next;\n        /* if the bookmark was to the last node, delete it. */\n        if (!bm->node)\n            _quicklistBookmarkDelete(quicklist, bm);\n    }\n\n    if (node->next)\n        node->next->prev = node->prev;\n    if (node->prev)\n        node->prev->next = node->next;\n\n    if (node == quicklist->tail) {\n        quicklist->tail = node->prev;\n    }\n\n    if (node == quicklist->head) {\n        quicklist->head = node->next;\n    }\n\n    /* If we deleted a node within our compress depth, we\n     * now have compressed nodes needing to be decompressed. */\n    __quicklistCompress(quicklist, NULL);\n\n    quicklist->count -= node->count;\n\n    zfree(node->zl);\n    zfree(node);\n    quicklist->len--;\n}\n\n/* Delete one entry from list given the node for the entry and a pointer\n * to the entry in the node.\n *\n * Note: quicklistDelIndex() *requires* uncompressed nodes because you\n *       already had to get *p from an uncompressed node somewhere.\n *\n * Returns 1 if the entire node was deleted, 0 if node still exists.\n * Also updates in/out param 'p' with the next offset in the ziplist. */\nREDIS_STATIC int quicklistDelIndex(quicklist *quicklist, quicklistNode *node,\n                                   unsigned char **p) {\n    int gone = 0;\n\n    node->zl = ziplistDelete(node->zl, p);\n    node->count--;\n    if (node->count == 0) {\n        gone = 1;\n        __quicklistDelNode(quicklist, node);\n    } else {\n        quicklistNodeUpdateSz(node);\n    }\n    quicklist->count--;\n    /* If we deleted the node, the original node is no longer valid */\n    return gone ? 1 : 0;\n}\n\n/* Delete one element represented by 'entry'\n *\n * 'entry' stores enough metadata to delete the proper position in\n * the correct ziplist in the correct quicklist node. */\nvoid quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry) {\n    quicklistNode *prev = entry->node->prev;\n    quicklistNode *next = entry->node->next;\n    int deleted_node = quicklistDelIndex((quicklist *)entry->quicklist,\n                                         entry->node, &entry->zi);\n\n    /* after delete, the zi is now invalid for any future usage. */\n    iter->zi = NULL;\n\n    /* If current node is deleted, we must update iterator node and offset. */\n    if (deleted_node) {\n        if (iter->direction == AL_START_HEAD) {\n            iter->current = next;\n            iter->offset = 0;\n        } else if (iter->direction == AL_START_TAIL) {\n            iter->current = prev;\n            iter->offset = -1;\n        }\n    }\n    /* else if (!deleted_node), no changes needed.\n     * we already reset iter->zi above, and the existing iter->offset\n     * doesn't move again because:\n     *   - [1, 2, 3] => delete offset 1 => [1, 3]: next element still offset 1\n     *   - [1, 2, 3] => delete offset 0 => [2, 3]: next element still offset 0\n     *  if we deleted the last element at offet N and now\n     *  length of this ziplist is N-1, the next call into\n     *  quicklistNext() will jump to the next node. */\n}\n\n/* Replace quicklist entry at offset 'index' by 'data' with length 'sz'.\n *\n * Returns 1 if replace happened.\n * Returns 0 if replace failed and no changes happened. */\nint quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,\n                            int sz) {\n    quicklistEntry entry;\n    if (likely(quicklistIndex(quicklist, index, &entry))) {\n        /* quicklistIndex provides an uncompressed node */\n        entry.node->zl = ziplistDelete(entry.node->zl, &entry.zi);\n        entry.node->zl = ziplistInsert(entry.node->zl, entry.zi, data, sz);\n        quicklistNodeUpdateSz(entry.node);\n        quicklistCompress(quicklist, entry.node);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Given two nodes, try to merge their ziplists.\n *\n * This helps us not have a quicklist with 3 element ziplists if\n * our fill factor can handle much higher levels.\n *\n * Note: 'a' must be to the LEFT of 'b'.\n *\n * After calling this function, both 'a' and 'b' should be considered\n * unusable.  The return value from this function must be used\n * instead of re-using any of the quicklistNode input arguments.\n *\n * Returns the input node picked to merge against or NULL if\n * merging was not possible. */\nREDIS_STATIC quicklistNode *_quicklistZiplistMerge(quicklist *quicklist,\n                                                   quicklistNode *a,\n                                                   quicklistNode *b) {\n    D(\"Requested merge (a,b) (%u, %u)\", a->count, b->count);\n\n    quicklistDecompressNode(a);\n    quicklistDecompressNode(b);\n    if ((ziplistMerge(&a->zl, &b->zl))) {\n        /* We merged ziplists! Now remove the unused quicklistNode. */\n        quicklistNode *keep = NULL, *nokeep = NULL;\n        if (!a->zl) {\n            nokeep = a;\n            keep = b;\n        } else if (!b->zl) {\n            nokeep = b;\n            keep = a;\n        }\n        keep->count = ziplistLen(keep->zl);\n        quicklistNodeUpdateSz(keep);\n\n        nokeep->count = 0;\n        __quicklistDelNode(quicklist, nokeep);\n        quicklistCompress(quicklist, keep);\n        return keep;\n    } else {\n        /* else, the merge returned NULL and nothing changed. */\n        return NULL;\n    }\n}\n\n/* Attempt to merge ziplists within two nodes on either side of 'center'.\n *\n * We attempt to merge:\n *   - (center->prev->prev, center->prev)\n *   - (center->next, center->next->next)\n *   - (center->prev, center)\n *   - (center, center->next)\n */\nREDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist,\n                                       quicklistNode *center) {\n    int fill = quicklist->fill;\n    quicklistNode *prev, *prev_prev, *next, *next_next, *target;\n    prev = prev_prev = next = next_next = target = NULL;\n\n    if (center->prev) {\n        prev = center->prev;\n        if (center->prev->prev)\n            prev_prev = center->prev->prev;\n    }\n\n    if (center->next) {\n        next = center->next;\n        if (center->next->next)\n            next_next = center->next->next;\n    }\n\n    /* Try to merge prev_prev and prev */\n    if (_quicklistNodeAllowMerge(prev, prev_prev, fill)) {\n        _quicklistZiplistMerge(quicklist, prev_prev, prev);\n        prev_prev = prev = NULL; /* they could have moved, invalidate them. */\n    }\n\n    /* Try to merge next and next_next */\n    if (_quicklistNodeAllowMerge(next, next_next, fill)) {\n        _quicklistZiplistMerge(quicklist, next, next_next);\n        next = next_next = NULL; /* they could have moved, invalidate them. */\n    }\n\n    /* Try to merge center node and previous node */\n    if (_quicklistNodeAllowMerge(center, center->prev, fill)) {\n        target = _quicklistZiplistMerge(quicklist, center->prev, center);\n        center = NULL; /* center could have been deleted, invalidate it. */\n    } else {\n        /* else, we didn't merge here, but target needs to be valid below. */\n        target = center;\n    }\n\n    /* Use result of center merge (or original) to merge with next node. */\n    if (_quicklistNodeAllowMerge(target, target->next, fill)) {\n        _quicklistZiplistMerge(quicklist, target, target->next);\n    }\n}\n\n/* Split 'node' into two parts, parameterized by 'offset' and 'after'.\n *\n * The 'after' argument controls which quicklistNode gets returned.\n * If 'after'==1, returned node has elements after 'offset'.\n *                input node keeps elements up to 'offset', including 'offset'.\n * If 'after'==0, returned node has elements up to 'offset', including 'offset'.\n *                input node keeps elements after 'offset'.\n *\n * If 'after'==1, returned node will have elements _after_ 'offset'.\n *                The returned node will have elements [OFFSET+1, END].\n *                The input node keeps elements [0, OFFSET].\n *\n * If 'after'==0, returned node will keep elements up to and including 'offset'.\n *                The returned node will have elements [0, OFFSET].\n *                The input node keeps elements [OFFSET+1, END].\n *\n * The input node keeps all elements not taken by the returned node.\n *\n * Returns newly created node or NULL if split not possible. */\nREDIS_STATIC quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset,\n                                                int after) {\n    size_t zl_sz = node->sz;\n\n    quicklistNode *new_node = quicklistCreateNode();\n    new_node->zl = zmalloc(zl_sz);\n\n    /* Copy original ziplist so we can split it */\n    memcpy(new_node->zl, node->zl, zl_sz);\n\n    /* -1 here means \"continue deleting until the list ends\" */\n    int orig_start = after ? offset + 1 : 0;\n    int orig_extent = after ? -1 : offset;\n    int new_start = after ? 0 : offset;\n    int new_extent = after ? offset + 1 : -1;\n\n    D(\"After %d (%d); ranges: [%d, %d], [%d, %d]\", after, offset, orig_start,\n      orig_extent, new_start, new_extent);\n\n    node->zl = ziplistDeleteRange(node->zl, orig_start, orig_extent);\n    node->count = ziplistLen(node->zl);\n    quicklistNodeUpdateSz(node);\n\n    new_node->zl = ziplistDeleteRange(new_node->zl, new_start, new_extent);\n    new_node->count = ziplistLen(new_node->zl);\n    quicklistNodeUpdateSz(new_node);\n\n    D(\"After split lengths: orig (%d), new (%d)\", node->count, new_node->count);\n    return new_node;\n}\n\n/* Insert a new entry before or after existing entry 'entry'.\n *\n * If after==1, the new value is inserted after 'entry', otherwise\n * the new value is inserted before 'entry'. */\nREDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry,\n                                   void *value, const size_t sz, int after) {\n    int full = 0, at_tail = 0, at_head = 0, full_next = 0, full_prev = 0;\n    int fill = quicklist->fill;\n    quicklistNode *node = entry->node;\n    quicklistNode *new_node = NULL;\n\n    if (!node) {\n        /* we have no reference node, so let's create only node in the list */\n        D(\"No node given!\");\n        new_node = quicklistCreateNode();\n        new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n        __quicklistInsertNode(quicklist, NULL, new_node, after);\n        new_node->count++;\n        quicklist->count++;\n        return;\n    }\n\n    /* Populate accounting flags for easier boolean checks later */\n    if (!_quicklistNodeAllowInsert(node, fill, sz)) {\n        D(\"Current node is full with count %d with requested fill %lu\",\n          node->count, fill);\n        full = 1;\n    }\n\n    if (after && (entry->offset == node->count)) {\n        D(\"At Tail of current ziplist\");\n        at_tail = 1;\n        if (!_quicklistNodeAllowInsert(node->next, fill, sz)) {\n            D(\"Next node is full too.\");\n            full_next = 1;\n        }\n    }\n\n    if (!after && (entry->offset == 0)) {\n        D(\"At Head\");\n        at_head = 1;\n        if (!_quicklistNodeAllowInsert(node->prev, fill, sz)) {\n            D(\"Prev node is full too.\");\n            full_prev = 1;\n        }\n    }\n\n    /* Now determine where and how to insert the new element */\n    if (!full && after) {\n        D(\"Not full, inserting after current position.\");\n        quicklistDecompressNodeForUse(node);\n        unsigned char *next = ziplistNext(node->zl, entry->zi);\n        if (next == NULL) {\n            node->zl = ziplistPush(node->zl, value, sz, ZIPLIST_TAIL);\n        } else {\n            node->zl = ziplistInsert(node->zl, next, value, sz);\n        }\n        node->count++;\n        quicklistNodeUpdateSz(node);\n        quicklistRecompressOnly(quicklist, node);\n    } else if (!full && !after) {\n        D(\"Not full, inserting before current position.\");\n        quicklistDecompressNodeForUse(node);\n        node->zl = ziplistInsert(node->zl, entry->zi, value, sz);\n        node->count++;\n        quicklistNodeUpdateSz(node);\n        quicklistRecompressOnly(quicklist, node);\n    } else if (full && at_tail && node->next && !full_next && after) {\n        /* If we are: at tail, next has free space, and inserting after:\n         *   - insert entry at head of next node. */\n        D(\"Full and tail, but next isn't full; inserting next node head\");\n        new_node = node->next;\n        quicklistDecompressNodeForUse(new_node);\n        new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_HEAD);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        quicklistRecompressOnly(quicklist, new_node);\n    } else if (full && at_head && node->prev && !full_prev && !after) {\n        /* If we are: at head, previous has free space, and inserting before:\n         *   - insert entry at tail of previous node. */\n        D(\"Full and head, but prev isn't full, inserting prev node tail\");\n        new_node = node->prev;\n        quicklistDecompressNodeForUse(new_node);\n        new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_TAIL);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        quicklistRecompressOnly(quicklist, new_node);\n    } else if (full && ((at_tail && node->next && full_next && after) ||\n                        (at_head && node->prev && full_prev && !after))) {\n        /* If we are: full, and our prev/next is full, then:\n         *   - create new node and attach to quicklist */\n        D(\"\\tprovisioning new node...\");\n        new_node = quicklistCreateNode();\n        new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        __quicklistInsertNode(quicklist, node, new_node, after);\n    } else if (full) {\n        /* else, node is full we need to split it. */\n        /* covers both after and !after cases */\n        D(\"\\tsplitting node...\");\n        quicklistDecompressNodeForUse(node);\n        new_node = _quicklistSplitNode(node, entry->offset, after);\n        new_node->zl = ziplistPush(new_node->zl, value, sz,\n                                   after ? ZIPLIST_HEAD : ZIPLIST_TAIL);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        __quicklistInsertNode(quicklist, node, new_node, after);\n        _quicklistMergeNodes(quicklist, node);\n    }\n\n    quicklist->count++;\n}\n\nvoid quicklistInsertBefore(quicklist *quicklist, quicklistEntry *entry,\n                           void *value, const size_t sz) {\n    _quicklistInsert(quicklist, entry, value, sz, 0);\n}\n\nvoid quicklistInsertAfter(quicklist *quicklist, quicklistEntry *entry,\n                          void *value, const size_t sz) {\n    _quicklistInsert(quicklist, entry, value, sz, 1);\n}\n\n/* Delete a range of elements from the quicklist.\n *\n * elements may span across multiple quicklistNodes, so we\n * have to be careful about tracking where we start and end.\n *\n * Returns 1 if entries were deleted, 0 if nothing was deleted. */\nint quicklistDelRange(quicklist *quicklist, const long start,\n                      const long count) {\n    if (count <= 0)\n        return 0;\n\n    unsigned long extent = count; /* range is inclusive of start position */\n\n    if (start >= 0 && extent > (quicklist->count - start)) {\n        /* if requesting delete more elements than exist, limit to list size. */\n        extent = quicklist->count - start;\n    } else if (start < 0 && extent > (unsigned long)(-start)) {\n        /* else, if at negative offset, limit max size to rest of list. */\n        extent = -start; /* c.f. LREM -29 29; just delete until end. */\n    }\n\n    quicklistEntry entry;\n    if (!quicklistIndex(quicklist, start, &entry))\n        return 0;\n\n    D(\"Quicklist delete request for start %ld, count %ld, extent: %ld\", start,\n      count, extent);\n    quicklistNode *node = entry.node;\n\n    /* iterate over next nodes until everything is deleted. */\n    while (extent) {\n        quicklistNode *next = node->next;\n\n        unsigned long del;\n        int delete_entire_node = 0;\n        if (entry.offset == 0 && extent >= node->count) {\n            /* If we are deleting more than the count of this node, we\n             * can just delete the entire node without ziplist math. */\n            delete_entire_node = 1;\n            del = node->count;\n        } else if (entry.offset >= 0 && extent >= node->count) {\n            /* If deleting more nodes after this one, calculate delete based\n             * on size of current node. */\n            del = node->count - entry.offset;\n        } else if (entry.offset < 0) {\n            /* If offset is negative, we are in the first run of this loop\n             * and we are deleting the entire range\n             * from this start offset to end of list.  Since the Negative\n             * offset is the number of elements until the tail of the list,\n             * just use it directly as the deletion count. */\n            del = -entry.offset;\n\n            /* If the positive offset is greater than the remaining extent,\n             * we only delete the remaining extent, not the entire offset.\n             */\n            if (del > extent)\n                del = extent;\n        } else {\n            /* else, we are deleting less than the extent of this node, so\n             * use extent directly. */\n            del = extent;\n        }\n\n        D(\"[%ld]: asking to del: %ld because offset: %d; (ENTIRE NODE: %d), \"\n          \"node count: %u\",\n          extent, del, entry.offset, delete_entire_node, node->count);\n\n        if (delete_entire_node) {\n            __quicklistDelNode(quicklist, node);\n        } else {\n            quicklistDecompressNodeForUse(node);\n            node->zl = ziplistDeleteRange(node->zl, entry.offset, del);\n            quicklistNodeUpdateSz(node);\n            node->count -= del;\n            quicklist->count -= del;\n            quicklistDeleteIfEmpty(quicklist, node);\n            if (node)\n                quicklistRecompressOnly(quicklist, node);\n        }\n\n        extent -= del;\n\n        node = next;\n\n        entry.offset = 0;\n    }\n    return 1;\n}\n\n/* Passthrough to ziplistCompare() */\nint quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len) {\n    return ziplistCompare(p1, p2, p2_len);\n}\n\n/* Returns a quicklist iterator 'iter'. After the initialization every\n * call to quicklistNext() will return the next element of the quicklist. */\nquicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction) {\n    quicklistIter *iter;\n\n    iter = zmalloc(sizeof(*iter));\n\n    if (direction == AL_START_HEAD) {\n        iter->current = quicklist->head;\n        iter->offset = 0;\n    } else if (direction == AL_START_TAIL) {\n        iter->current = quicklist->tail;\n        iter->offset = -1;\n    }\n\n    iter->direction = direction;\n    iter->quicklist = quicklist;\n\n    iter->zi = NULL;\n\n    return iter;\n}\n\n/* Initialize an iterator at a specific offset 'idx' and make the iterator\n * return nodes in 'direction' direction. */\nquicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,\n                                         const int direction,\n                                         const long long idx) {\n    quicklistEntry entry;\n\n    if (quicklistIndex(quicklist, idx, &entry)) {\n        quicklistIter *base = quicklistGetIterator(quicklist, direction);\n        base->zi = NULL;\n        base->current = entry.node;\n        base->offset = entry.offset;\n        return base;\n    } else {\n        return NULL;\n    }\n}\n\n/* Release iterator.\n * If we still have a valid current node, then re-encode current node. */\nvoid quicklistReleaseIterator(quicklistIter *iter) {\n    if (iter->current)\n        quicklistCompress(iter->quicklist, iter->current);\n\n    zfree(iter);\n}\n\n/* Get next element in iterator.\n *\n * Note: You must NOT insert into the list while iterating over it.\n * You *may* delete from the list while iterating using the\n * quicklistDelEntry() function.\n * If you insert into the quicklist while iterating, you should\n * re-create the iterator after your addition.\n *\n * iter = quicklistGetIterator(quicklist,<direction>);\n * quicklistEntry entry;\n * while (quicklistNext(iter, &entry)) {\n *     if (entry.value)\n *          [[ use entry.value with entry.sz ]]\n *     else\n *          [[ use entry.longval ]]\n * }\n *\n * Populates 'entry' with values for this iteration.\n * Returns 0 when iteration is complete or if iteration not possible.\n * If return value is 0, the contents of 'entry' are not valid.\n */\nint quicklistNext(quicklistIter *iter, quicklistEntry *entry) {\n    initEntry(entry);\n\n    if (!iter) {\n        D(\"Returning because no iter!\");\n        return 0;\n    }\n\n    entry->quicklist = iter->quicklist;\n    entry->node = iter->current;\n\n    if (!iter->current) {\n        D(\"Returning because current node is NULL\")\n        return 0;\n    }\n\n    unsigned char *(*nextFn)(unsigned char *, unsigned char *) = NULL;\n    int offset_update = 0;\n\n    if (!iter->zi) {\n        /* If !zi, use current index. */\n        quicklistDecompressNodeForUse(iter->current);\n        iter->zi = ziplistIndex(iter->current->zl, iter->offset);\n    } else {\n        /* else, use existing iterator offset and get prev/next as necessary. */\n        if (iter->direction == AL_START_HEAD) {\n            nextFn = ziplistNext;\n            offset_update = 1;\n        } else if (iter->direction == AL_START_TAIL) {\n            nextFn = ziplistPrev;\n            offset_update = -1;\n        }\n        iter->zi = nextFn(iter->current->zl, iter->zi);\n        iter->offset += offset_update;\n    }\n\n    entry->zi = iter->zi;\n    entry->offset = iter->offset;\n\n    if (iter->zi) {\n        /* Populate value from existing ziplist position */\n        ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);\n        return 1;\n    } else {\n        /* We ran out of ziplist entries.\n         * Pick next node, update offset, then re-run retrieval. */\n        quicklistCompress(iter->quicklist, iter->current);\n        if (iter->direction == AL_START_HEAD) {\n            /* Forward traversal */\n            D(\"Jumping to start of next node\");\n            iter->current = iter->current->next;\n            iter->offset = 0;\n        } else if (iter->direction == AL_START_TAIL) {\n            /* Reverse traversal */\n            D(\"Jumping to end of previous node\");\n            iter->current = iter->current->prev;\n            iter->offset = -1;\n        }\n        iter->zi = NULL;\n        return quicklistNext(iter, entry);\n    }\n}\n\n/* Duplicate the quicklist.\n * On success a copy of the original quicklist is returned.\n *\n * The original quicklist both on success or error is never modified.\n *\n * Returns newly allocated quicklist. */\nquicklist *quicklistDup(quicklist *orig) {\n    quicklist *copy;\n\n    copy = quicklistNew(orig->fill, orig->compress);\n\n    for (quicklistNode *current = orig->head; current;\n         current = current->next) {\n        quicklistNode *node = quicklistCreateNode();\n\n        if (current->encoding == QUICKLIST_NODE_ENCODING_LZF) {\n            quicklistLZF *lzf = (quicklistLZF *)current->zl;\n            size_t lzf_sz = sizeof(*lzf) + lzf->sz;\n            node->zl = zmalloc(lzf_sz);\n            memcpy(node->zl, current->zl, lzf_sz);\n        } else if (current->encoding == QUICKLIST_NODE_ENCODING_RAW) {\n            node->zl = zmalloc(current->sz);\n            memcpy(node->zl, current->zl, current->sz);\n        }\n\n        node->count = current->count;\n        copy->count += node->count;\n        node->sz = current->sz;\n        node->encoding = current->encoding;\n\n        _quicklistInsertNodeAfter(copy, copy->tail, node);\n    }\n\n    /* copy->count must equal orig->count here */\n    return copy;\n}\n\n/* Populate 'entry' with the element at the specified zero-based index\n * where 0 is the head, 1 is the element next to head\n * and so on. Negative integers are used in order to count\n * from the tail, -1 is the last element, -2 the penultimate\n * and so on. If the index is out of range 0 is returned.\n *\n * Returns 1 if element found\n * Returns 0 if element not found */\nint quicklistIndex(const quicklist *quicklist, const long long idx,\n                   quicklistEntry *entry) {\n    quicklistNode *n;\n    unsigned long long accum = 0;\n    unsigned long long index;\n    int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */\n\n    initEntry(entry);\n    entry->quicklist = quicklist;\n\n    if (!forward) {\n        index = (-idx) - 1;\n        n = quicklist->tail;\n    } else {\n        index = idx;\n        n = quicklist->head;\n    }\n\n    if (index >= quicklist->count)\n        return 0;\n\n    while (likely(n)) {\n        if ((accum + n->count) > index) {\n            break;\n        } else {\n            D(\"Skipping over (%p) %u at accum %lld\", (void *)n, n->count,\n              accum);\n            accum += n->count;\n            n = forward ? n->next : n->prev;\n        }\n    }\n\n    if (!n)\n        return 0;\n\n    D(\"Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu\", (void *)n,\n      accum, index, index - accum, (-index) - 1 + accum);\n\n    entry->node = n;\n    if (forward) {\n        /* forward = normal head-to-tail offset. */\n        entry->offset = index - accum;\n    } else {\n        /* reverse = need negative offset for tail-to-head, so undo\n         * the result of the original if (index < 0) above. */\n        entry->offset = (-index) - 1 + accum;\n    }\n\n    quicklistDecompressNodeForUse(entry->node);\n    entry->zi = ziplistIndex(entry->node->zl, entry->offset);\n    ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);\n    /* The caller will use our result, so we don't re-compress here.\n     * The caller can recompress or delete the node as needed. */\n    return 1;\n}\n\n/* Rotate quicklist by moving the tail element to the head. */\nvoid quicklistRotate(quicklist *quicklist) {\n    if (quicklist->count <= 1)\n        return;\n\n    /* First, get the tail entry */\n    unsigned char *p = ziplistIndex(quicklist->tail->zl, -1);\n    unsigned char *value;\n    long long longval;\n    unsigned int sz;\n    char longstr[32] = {0};\n    ziplistGet(p, &value, &sz, &longval);\n\n    /* If value found is NULL, then ziplistGet populated longval instead */\n    if (!value) {\n        /* Write the longval as a string so we can re-add it */\n        sz = ll2string(longstr, sizeof(longstr), longval);\n        value = (unsigned char *)longstr;\n    }\n\n    /* Add tail entry to head (must happen before tail is deleted). */\n    quicklistPushHead(quicklist, value, sz);\n\n    /* If quicklist has only one node, the head ziplist is also the\n     * tail ziplist and PushHead() could have reallocated our single ziplist,\n     * which would make our pre-existing 'p' unusable. */\n    if (quicklist->len == 1) {\n        p = ziplistIndex(quicklist->tail->zl, -1);\n    }\n\n    /* Remove tail entry. */\n    quicklistDelIndex(quicklist, quicklist->tail, &p);\n}\n\n/* pop from quicklist and return result in 'data' ptr.  Value of 'data'\n * is the return value of 'saver' function pointer if the data is NOT a number.\n *\n * If the quicklist element is a long long, then the return value is returned in\n * 'sval'.\n *\n * Return value of 0 means no elements available.\n * Return value of 1 means check 'data' and 'sval' for values.\n * If 'data' is set, use 'data' and 'sz'.  Otherwise, use 'sval'. */\nint quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,\n                       unsigned int *sz, long long *sval,\n                       void *(*saver)(unsigned char *data, unsigned int sz)) {\n    unsigned char *p;\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    int pos = (where == QUICKLIST_HEAD) ? 0 : -1;\n\n    if (quicklist->count == 0)\n        return 0;\n\n    if (data)\n        *data = NULL;\n    if (sz)\n        *sz = 0;\n    if (sval)\n        *sval = -123456789;\n\n    quicklistNode *node;\n    if (where == QUICKLIST_HEAD && quicklist->head) {\n        node = quicklist->head;\n    } else if (where == QUICKLIST_TAIL && quicklist->tail) {\n        node = quicklist->tail;\n    } else {\n        return 0;\n    }\n\n    p = ziplistIndex(node->zl, pos);\n    if (ziplistGet(p, &vstr, &vlen, &vlong)) {\n        if (vstr) {\n            if (data)\n                *data = saver(vstr, vlen);\n            if (sz)\n                *sz = vlen;\n        } else {\n            if (data)\n                *data = NULL;\n            if (sval)\n                *sval = vlong;\n        }\n        quicklistDelIndex(quicklist, node, &p);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return a malloc'd copy of data passed in */\nREDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) {\n    unsigned char *vstr;\n    if (data) {\n        vstr = zmalloc(sz);\n        memcpy(vstr, data, sz);\n        return vstr;\n    }\n    return NULL;\n}\n\n/* Default pop function\n *\n * Returns malloc'd value from quicklist */\nint quicklistPop(quicklist *quicklist, int where, unsigned char **data,\n                 unsigned int *sz, long long *slong) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    if (quicklist->count == 0)\n        return 0;\n    int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong,\n                                 _quicklistSaver);\n    if (data)\n        *data = vstr;\n    if (slong)\n        *slong = vlong;\n    if (sz)\n        *sz = vlen;\n    return ret;\n}\n\n/* Wrapper to allow argument-based switching between HEAD/TAIL pop */\nvoid quicklistPush(quicklist *quicklist, void *value, const size_t sz,\n                   int where) {\n    if (where == QUICKLIST_HEAD) {\n        quicklistPushHead(quicklist, value, sz);\n    } else if (where == QUICKLIST_TAIL) {\n        quicklistPushTail(quicklist, value, sz);\n    }\n}\n\n/* Create or update a bookmark in the list which will be updated to the next node\n * automatically when the one referenced gets deleted.\n * Returns 1 on success (creation of new bookmark or override of an existing one).\n * Returns 0 on failure (reached the maximum supported number of bookmarks).\n * NOTE: use short simple names, so that string compare on find is quick.\n * NOTE: bookmakrk creation may re-allocate the quicklist, so the input pointer\n         may change and it's the caller responsibilty to update the reference.\n */\nint quicklistBookmarkCreate(quicklist **ql_ref, const char *name, quicklistNode *node) {\n    quicklist *ql = *ql_ref;\n    if (ql->bookmark_count >= QL_MAX_BM)\n        return 0;\n    quicklistBookmark *bm = _quicklistBookmarkFindByName(ql, name);\n    if (bm) {\n        bm->node = node;\n        return 1;\n    }\n    ql = zrealloc(ql, sizeof(quicklist) + (ql->bookmark_count+1) * sizeof(quicklistBookmark));\n    *ql_ref = ql;\n    ql->bookmarks[ql->bookmark_count].node = node;\n    ql->bookmarks[ql->bookmark_count].name = zstrdup(name);\n    ql->bookmark_count++;\n    return 1;\n}\n\n/* Find the quicklist node referenced by a named bookmark.\n * When the bookmarked node is deleted the bookmark is updated to the next node,\n * and if that's the last node, the bookmark is deleted (so find returns NULL). */\nquicklistNode *quicklistBookmarkFind(quicklist *ql, const char *name) {\n    quicklistBookmark *bm = _quicklistBookmarkFindByName(ql, name);\n    if (!bm) return NULL;\n    return bm->node;\n}\n\n/* Delete a named bookmark.\n * returns 0 if bookmark was not found, and 1 if deleted.\n * Note that the bookmark memory is not freed yet, and is kept for future use. */\nint quicklistBookmarkDelete(quicklist *ql, const char *name) {\n    quicklistBookmark *bm = _quicklistBookmarkFindByName(ql, name);\n    if (!bm)\n        return 0;\n    _quicklistBookmarkDelete(ql, bm);\n    return 1;\n}\n\nquicklistBookmark *_quicklistBookmarkFindByName(quicklist *ql, const char *name) {\n    unsigned i;\n    for (i=0; i<ql->bookmark_count; i++) {\n        if (!strcmp(ql->bookmarks[i].name, name)) {\n            return &ql->bookmarks[i];\n        }\n    }\n    return NULL;\n}\n\nquicklistBookmark *_quicklistBookmarkFindByNode(quicklist *ql, quicklistNode *node) {\n    unsigned i;\n    for (i=0; i<ql->bookmark_count; i++) {\n        if (ql->bookmarks[i].node == node) {\n            return &ql->bookmarks[i];\n        }\n    }\n    return NULL;\n}\n\nvoid _quicklistBookmarkDelete(quicklist *ql, quicklistBookmark *bm) {\n    int index = bm - ql->bookmarks;\n    zfree(bm->name);\n    ql->bookmark_count--;\n    memmove(bm, bm+1, (ql->bookmark_count - index)* sizeof(*bm));\n    /* NOTE: We do not shrink (realloc) the quicklist yet (to avoid resonance,\n     * it may be re-used later (a call to realloc may NOP). */\n}\n\nvoid quicklistBookmarksClear(quicklist *ql) {\n    while (ql->bookmark_count)\n        zfree(ql->bookmarks[--ql->bookmark_count].name);\n    /* NOTE: We do not shrink (realloc) the quick list. main use case for this\n     * function is just before releasing the allocation. */\n}\n\n/* The rest of this file is test cases and test helpers. */\n#ifdef REDIS_TEST\n#include <stdint.h>\n#include <sys/time.h>\n\n#define assert(_e)                                                             \\\n    do {                                                                       \\\n        if (!(_e)) {                                                           \\\n            printf(\"\\n\\n=== ASSERTION FAILED ===\\n\");                          \\\n            printf(\"==> %s:%d '%s' is not true\\n\", __FILE__, __LINE__, #_e);   \\\n            err++;                                                             \\\n        }                                                                      \\\n    } while (0)\n\n#define yell(str, ...) printf(\"ERROR! \" str \"\\n\\n\", __VA_ARGS__)\n\n#define OK printf(\"\\tOK\\n\")\n\n#define ERROR                                                                  \\\n    do {                                                                       \\\n        printf(\"\\tERROR!\\n\");                                                  \\\n        err++;                                                                 \\\n    } while (0)\n\n#define ERR(x, ...)                                                            \\\n    do {                                                                       \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(\"ERROR! \" x \"\\n\", __VA_ARGS__);                                 \\\n        err++;                                                                 \\\n    } while (0)\n\n#define TEST(name) printf(\"test — %s\\n\", name);\n#define TEST_DESC(name, ...) printf(\"test — \" name \"\\n\", __VA_ARGS__);\n\n#define QL_TEST_VERBOSE 0\n\n#define UNUSED(x) (void)(x)\nstatic void ql_info(quicklist *ql) {\n#if QL_TEST_VERBOSE\n    printf(\"Container length: %lu\\n\", ql->len);\n    printf(\"Container size: %lu\\n\", ql->count);\n    if (ql->head)\n        printf(\"\\t(zsize head: %d)\\n\", ziplistLen(ql->head->zl));\n    if (ql->tail)\n        printf(\"\\t(zsize tail: %d)\\n\", ziplistLen(ql->tail->zl));\n    printf(\"\\n\");\n#else\n    UNUSED(ql);\n#endif\n}\n\n/* Return the UNIX time in microseconds */\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\n/* Return the UNIX time in milliseconds */\nstatic long long mstime(void) { return ustime() / 1000; }\n\n/* Iterate over an entire quicklist.\n * Print the list if 'print' == 1.\n *\n * Returns physical count of elements found by iterating over the list. */\nstatic int _itrprintr(quicklist *ql, int print, int forward) {\n    quicklistIter *iter =\n        quicklistGetIterator(ql, forward ? AL_START_HEAD : AL_START_TAIL);\n    quicklistEntry entry;\n    int i = 0;\n    int p = 0;\n    quicklistNode *prev = NULL;\n    while (quicklistNext(iter, &entry)) {\n        if (entry.node != prev) {\n            /* Count the number of list nodes too */\n            p++;\n            prev = entry.node;\n        }\n        if (print) {\n            printf(\"[%3d (%2d)]: [%.*s] (%lld)\\n\", i, p, entry.sz,\n                   (char *)entry.value, entry.longval);\n        }\n        i++;\n    }\n    quicklistReleaseIterator(iter);\n    return i;\n}\nstatic int itrprintr(quicklist *ql, int print) {\n    return _itrprintr(ql, print, 1);\n}\n\nstatic int itrprintr_rev(quicklist *ql, int print) {\n    return _itrprintr(ql, print, 0);\n}\n\n#define ql_verify(a, b, c, d, e)                                               \\\n    do {                                                                       \\\n        err += _ql_verify((a), (b), (c), (d), (e));                            \\\n    } while (0)\n\n/* Verify list metadata matches physical list contents. */\nstatic int _ql_verify(quicklist *ql, uint32_t len, uint32_t count,\n                      uint32_t head_count, uint32_t tail_count) {\n    int errors = 0;\n\n    ql_info(ql);\n    if (len != ql->len) {\n        yell(\"quicklist length wrong: expected %d, got %u\", len, ql->len);\n        errors++;\n    }\n\n    if (count != ql->count) {\n        yell(\"quicklist count wrong: expected %d, got %lu\", count, ql->count);\n        errors++;\n    }\n\n    int loopr = itrprintr(ql, 0);\n    if (loopr != (int)ql->count) {\n        yell(\"quicklist cached count not match actual count: expected %lu, got \"\n             \"%d\",\n             ql->count, loopr);\n        errors++;\n    }\n\n    int rloopr = itrprintr_rev(ql, 0);\n    if (loopr != rloopr) {\n        yell(\"quicklist has different forward count than reverse count!  \"\n             \"Forward count is %d, reverse count is %d.\",\n             loopr, rloopr);\n        errors++;\n    }\n\n    if (ql->len == 0 && !errors) {\n        OK;\n        return errors;\n    }\n\n    if (ql->head && head_count != ql->head->count &&\n        head_count != ziplistLen(ql->head->zl)) {\n        yell(\"quicklist head count wrong: expected %d, \"\n             \"got cached %d vs. actual %d\",\n             head_count, ql->head->count, ziplistLen(ql->head->zl));\n        errors++;\n    }\n\n    if (ql->tail && tail_count != ql->tail->count &&\n        tail_count != ziplistLen(ql->tail->zl)) {\n        yell(\"quicklist tail count wrong: expected %d, \"\n             \"got cached %u vs. actual %d\",\n             tail_count, ql->tail->count, ziplistLen(ql->tail->zl));\n        errors++;\n    }\n\n    if (quicklistAllowsCompression(ql)) {\n        quicklistNode *node = ql->head;\n        unsigned int low_raw = ql->compress;\n        unsigned int high_raw = ql->len - ql->compress;\n\n        for (unsigned int at = 0; at < ql->len; at++, node = node->next) {\n            if (node && (at < low_raw || at >= high_raw)) {\n                if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {\n                    yell(\"Incorrect compression: node %d is \"\n                         \"compressed at depth %d ((%u, %u); total \"\n                         \"nodes: %u; size: %u; recompress: %d)\",\n                         at, ql->compress, low_raw, high_raw, ql->len, node->sz,\n                         node->recompress);\n                    errors++;\n                }\n            } else {\n                if (node->encoding != QUICKLIST_NODE_ENCODING_LZF &&\n                    !node->attempted_compress) {\n                    yell(\"Incorrect non-compression: node %d is NOT \"\n                         \"compressed at depth %d ((%u, %u); total \"\n                         \"nodes: %u; size: %u; recompress: %d; attempted: %d)\",\n                         at, ql->compress, low_raw, high_raw, ql->len, node->sz,\n                         node->recompress, node->attempted_compress);\n                    errors++;\n                }\n            }\n        }\n    }\n\n    if (!errors)\n        OK;\n    return errors;\n}\n\n/* Generate new string concatenating integer i against string 'prefix' */\nstatic char *genstr(char *prefix, int i) {\n    static char result[64] = {0};\n    snprintf(result, sizeof(result), \"%s%d\", prefix, i);\n    return result;\n}\n\n/* main test, but callable from other files */\nint quicklistTest(int argc, char *argv[]) {\n    UNUSED(argc);\n    UNUSED(argv);\n\n    unsigned int err = 0;\n    int optimize_start =\n        -(int)(sizeof(optimization_level) / sizeof(*optimization_level));\n\n    printf(\"Starting optimization offset at: %d\\n\", optimize_start);\n\n    int options[] = {0, 1, 2, 3, 4, 5, 6, 10};\n    size_t option_count = sizeof(options) / sizeof(*options);\n    long long runtime[option_count];\n\n    for (int _i = 0; _i < (int)option_count; _i++) {\n        printf(\"Testing Option %d\\n\", options[_i]);\n        long long start = mstime();\n\n        TEST(\"create list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"add to tail of empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushTail(ql, \"hello\", 6);\n            /* 1 for head and 1 for tail because 1 node = head = tail */\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"add to head of empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"hello\", 6);\n            /* 1 for head and 1 for tail because 1 node = head = tail */\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        for (int f = optimize_start; f < 32; f++) {\n            TEST_DESC(\"add to tail 5x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 5; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i), 32);\n                if (ql->count != 5)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 1, 5, 5, 5);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 32; f++) {\n            TEST_DESC(\"add to head 5x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 5; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 32);\n                if (ql->count != 5)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 1, 5, 5, 5);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 512; f++) {\n            TEST_DESC(\"add to tail 500x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i), 64);\n                if (ql->count != 500)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 16, 500, 32, 20);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 512; f++) {\n            TEST_DESC(\"add to head 500x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 32);\n                if (ql->count != 500)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 16, 500, 20, 32);\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"rotate empty\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistRotate(ql);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        for (int f = optimize_start; f < 32; f++) {\n            TEST(\"rotate one val once\") {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushHead(ql, \"hello\", 6);\n                quicklistRotate(ql);\n                /* Ignore compression verify because ziplist is\n                 * too small to compress. */\n                ql_verify(ql, 1, 1, 1, 1);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 3; f++) {\n            TEST_DESC(\"rotate 500 val 5000 times at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushHead(ql, \"900\", 3);\n                quicklistPushHead(ql, \"7000\", 4);\n                quicklistPushHead(ql, \"-1200\", 5);\n                quicklistPushHead(ql, \"42\", 2);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 64);\n                ql_info(ql);\n                for (int i = 0; i < 5000; i++) {\n                    ql_info(ql);\n                    quicklistRotate(ql);\n                }\n                if (f == 1)\n                    ql_verify(ql, 504, 504, 1, 1);\n                else if (f == 2)\n                    ql_verify(ql, 252, 504, 2, 2);\n                else if (f == 32)\n                    ql_verify(ql, 16, 504, 32, 24);\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"pop empty\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPop(ql, QUICKLIST_HEAD, NULL, NULL, NULL);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop 1 string from 1\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            char *populate = genstr(\"hello\", 331);\n            quicklistPushHead(ql, populate, 32);\n            unsigned char *data;\n            unsigned int sz;\n            long long lv;\n            ql_info(ql);\n            quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n            assert(data != NULL);\n            assert(sz == 32);\n            if (strcmp(populate, (char *)data))\n                ERR(\"Pop'd value (%.*s) didn't equal original value (%s)\", sz,\n                    data, populate);\n            zfree(data);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop head 1 number from 1\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"55513\", 5);\n            unsigned char *data;\n            unsigned int sz;\n            long long lv;\n            ql_info(ql);\n            quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n            assert(data == NULL);\n            assert(lv == 55513);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop head 500 from 500\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_info(ql);\n            for (int i = 0; i < 500; i++) {\n                unsigned char *data;\n                unsigned int sz;\n                long long lv;\n                int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n                assert(ret == 1);\n                assert(data != NULL);\n                assert(sz == 32);\n                if (strcmp(genstr(\"hello\", 499 - i), (char *)data))\n                    ERR(\"Pop'd value (%.*s) didn't equal original value (%s)\",\n                        sz, data, genstr(\"hello\", 499 - i));\n                zfree(data);\n            }\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop head 5000 from 500\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            for (int i = 0; i < 5000; i++) {\n                unsigned char *data;\n                unsigned int sz;\n                long long lv;\n                int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n                if (i < 500) {\n                    assert(ret == 1);\n                    assert(data != NULL);\n                    assert(sz == 32);\n                    if (strcmp(genstr(\"hello\", 499 - i), (char *)data))\n                        ERR(\"Pop'd value (%.*s) didn't equal original value \"\n                            \"(%s)\",\n                            sz, data, genstr(\"hello\", 499 - i));\n                    zfree(data);\n                } else {\n                    assert(ret == 0);\n                }\n            }\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"iterate forward over 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);\n            quicklistEntry entry;\n            int i = 499, count = 0;\n            while (quicklistNext(iter, &entry)) {\n                char *h = genstr(\"hello\", i);\n                if (strcmp((char *)entry.value, h))\n                    ERR(\"value [%s] didn't match [%s] at position %d\",\n                        entry.value, h, i);\n                i--;\n                count++;\n            }\n            if (count != 500)\n                ERR(\"Didn't iterate over exactly 500 elements (%d)\", i);\n            ql_verify(ql, 16, 500, 20, 32);\n            quicklistReleaseIterator(iter);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"iterate reverse over 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL);\n            quicklistEntry entry;\n            int i = 0;\n            while (quicklistNext(iter, &entry)) {\n                char *h = genstr(\"hello\", i);\n                if (strcmp((char *)entry.value, h))\n                    ERR(\"value [%s] didn't match [%s] at position %d\",\n                        entry.value, h, i);\n                i++;\n            }\n            if (i != 500)\n                ERR(\"Didn't iterate over exactly 500 elements (%d)\", i);\n            ql_verify(ql, 16, 500, 20, 32);\n            quicklistReleaseIterator(iter);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert before with 0 elements\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertBefore(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert after with 0 elements\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertAfter(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert after 1 element\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"hello\", 6);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertAfter(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 2, 2, 2);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert before 1 element\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"hello\", 6);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertAfter(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 2, 2, 2);\n            quicklistRelease(ql);\n        }\n\n        for (int f = optimize_start; f < 12; f++) {\n            TEST_DESC(\"insert once in elements while iterating at fill %d at \"\n                      \"compress %d\\n\",\n                      f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushTail(ql, \"abc\", 3);\n                quicklistSetFill(ql, 1);\n                quicklistPushTail(ql, \"def\", 3); /* force to unique node */\n                quicklistSetFill(ql, f);\n                quicklistPushTail(ql, \"bob\", 3); /* force to reset for +3 */\n                quicklistPushTail(ql, \"foo\", 3);\n                quicklistPushTail(ql, \"zoo\", 3);\n\n                itrprintr(ql, 0);\n                /* insert \"bar\" before \"bob\" while iterating over list. */\n                quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);\n                quicklistEntry entry;\n                while (quicklistNext(iter, &entry)) {\n                    if (!strncmp((char *)entry.value, \"bob\", 3)) {\n                        /* Insert as fill = 1 so it spills into new node. */\n                        quicklistInsertBefore(ql, &entry, \"bar\", 3);\n                        break; /* didn't we fix insert-while-iterating? */\n                    }\n                }\n                itrprintr(ql, 0);\n\n                /* verify results */\n                quicklistIndex(ql, 0, &entry);\n                if (strncmp((char *)entry.value, \"abc\", 3))\n                    ERR(\"Value 0 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 1, &entry);\n                if (strncmp((char *)entry.value, \"def\", 3))\n                    ERR(\"Value 1 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 2, &entry);\n                if (strncmp((char *)entry.value, \"bar\", 3))\n                    ERR(\"Value 2 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 3, &entry);\n                if (strncmp((char *)entry.value, \"bob\", 3))\n                    ERR(\"Value 3 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 4, &entry);\n                if (strncmp((char *)entry.value, \"foo\", 3))\n                    ERR(\"Value 4 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 5, &entry);\n                if (strncmp((char *)entry.value, \"zoo\", 3))\n                    ERR(\"Value 5 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistReleaseIterator(iter);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 1024; f++) {\n            TEST_DESC(\n                \"insert [before] 250 new in middle of 500 elements at fill\"\n                \" %d at compress %d\",\n                f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i), 32);\n                for (int i = 0; i < 250; i++) {\n                    quicklistEntry entry;\n                    quicklistIndex(ql, 250, &entry);\n                    quicklistInsertBefore(ql, &entry, genstr(\"abc\", i), 32);\n                }\n                if (f == 32)\n                    ql_verify(ql, 25, 750, 32, 20);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 1024; f++) {\n            TEST_DESC(\"insert [after] 250 new in middle of 500 elements at \"\n                      \"fill %d at compress %d\",\n                      f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 32);\n                for (int i = 0; i < 250; i++) {\n                    quicklistEntry entry;\n                    quicklistIndex(ql, 250, &entry);\n                    quicklistInsertAfter(ql, &entry, genstr(\"abc\", i), 32);\n                }\n\n                if (ql->count != 750)\n                    ERR(\"List size not 750, but rather %ld\", ql->count);\n\n                if (f == 32)\n                    ql_verify(ql, 26, 750, 20, 32);\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"duplicate empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklist *copy = quicklistDup(ql);\n            ql_verify(copy, 0, 0, 0, 0);\n            quicklistRelease(ql);\n            quicklistRelease(copy);\n        }\n\n        TEST(\"duplicate list of 1 element\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, genstr(\"hello\", 3), 32);\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklist *copy = quicklistDup(ql);\n            ql_verify(copy, 1, 1, 1, 1);\n            quicklistRelease(ql);\n            quicklistRelease(copy);\n        }\n\n        TEST(\"duplicate list of 500\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_verify(ql, 16, 500, 20, 32);\n\n            quicklist *copy = quicklistDup(ql);\n            ql_verify(copy, 16, 500, 20, 32);\n            quicklistRelease(ql);\n            quicklistRelease(copy);\n        }\n\n        for (int f = optimize_start; f < 512; f++) {\n            TEST_DESC(\"index 1,200 from 500 list at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                quicklistIndex(ql, 1, &entry);\n                if (!strcmp((char *)entry.value, \"hello2\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistIndex(ql, 200, &entry);\n                if (!strcmp((char *)entry.value, \"hello201\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistRelease(ql);\n            }\n\n            TEST_DESC(\"index -1,-2 from 500 list at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                quicklistIndex(ql, -1, &entry);\n                if (!strcmp((char *)entry.value, \"hello500\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistIndex(ql, -2, &entry);\n                if (!strcmp((char *)entry.value, \"hello499\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistRelease(ql);\n            }\n\n            TEST_DESC(\"index -100 from 500 list at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                quicklistIndex(ql, -100, &entry);\n                if (!strcmp((char *)entry.value, \"hello401\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistRelease(ql);\n            }\n\n            TEST_DESC(\"index too big +1 from 50 list at fill %d at compress %d\",\n                      f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 50; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                if (quicklistIndex(ql, 50, &entry))\n                    ERR(\"Index found at 50 with 50 list: %.*s\", entry.sz,\n                        entry.value);\n                else\n                    OK;\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"delete range empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistDelRange(ql, 5, 20);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete range of entire node in list of one node\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 32; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_verify(ql, 1, 32, 32, 32);\n            quicklistDelRange(ql, 0, 32);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete range of entire node with overflow counts\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 32; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_verify(ql, 1, 32, 32, 32);\n            quicklistDelRange(ql, 0, 128);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete middle 100 of 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 16, 500, 32, 20);\n            quicklistDelRange(ql, 200, 100);\n            ql_verify(ql, 14, 400, 32, 20);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete negative 1 from 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 16, 500, 32, 20);\n            quicklistDelRange(ql, -1, 1);\n            ql_verify(ql, 16, 499, 32, 19);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete negative 1 from 500 list with overflow counts\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 16, 500, 32, 20);\n            quicklistDelRange(ql, -1, 128);\n            ql_verify(ql, 16, 499, 32, 19);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete negative 100 from 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            quicklistDelRange(ql, -100, 100);\n            ql_verify(ql, 13, 400, 32, 16);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete -10 count 5 from 50 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 50; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 2, 50, 32, 18);\n            quicklistDelRange(ql, -10, 5);\n            ql_verify(ql, 2, 45, 32, 13);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"numbers only list read\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushTail(ql, \"1111\", 4);\n            quicklistPushTail(ql, \"2222\", 4);\n            quicklistPushTail(ql, \"3333\", 4);\n            quicklistPushTail(ql, \"4444\", 4);\n            ql_verify(ql, 1, 4, 4, 4);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            if (entry.longval != 1111)\n                ERR(\"Not 1111, %lld\", entry.longval);\n            quicklistIndex(ql, 1, &entry);\n            if (entry.longval != 2222)\n                ERR(\"Not 2222, %lld\", entry.longval);\n            quicklistIndex(ql, 2, &entry);\n            if (entry.longval != 3333)\n                ERR(\"Not 3333, %lld\", entry.longval);\n            quicklistIndex(ql, 3, &entry);\n            if (entry.longval != 4444)\n                ERR(\"Not 4444, %lld\", entry.longval);\n            if (quicklistIndex(ql, 4, &entry))\n                ERR(\"Index past elements: %lld\", entry.longval);\n            quicklistIndex(ql, -1, &entry);\n            if (entry.longval != 4444)\n                ERR(\"Not 4444 (reverse), %lld\", entry.longval);\n            quicklistIndex(ql, -2, &entry);\n            if (entry.longval != 3333)\n                ERR(\"Not 3333 (reverse), %lld\", entry.longval);\n            quicklistIndex(ql, -3, &entry);\n            if (entry.longval != 2222)\n                ERR(\"Not 2222 (reverse), %lld\", entry.longval);\n            quicklistIndex(ql, -4, &entry);\n            if (entry.longval != 1111)\n                ERR(\"Not 1111 (reverse), %lld\", entry.longval);\n            if (quicklistIndex(ql, -5, &entry))\n                ERR(\"Index past elements (reverse), %lld\", entry.longval);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"numbers larger list read\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            char num[32];\n            long long nums[5000];\n            for (int i = 0; i < 5000; i++) {\n                nums[i] = -5157318210846258176 + i;\n                int sz = ll2string(num, sizeof(num), nums[i]);\n                quicklistPushTail(ql, num, sz);\n            }\n            quicklistPushTail(ql, \"xxxxxxxxxxxxxxxxxxxx\", 20);\n            quicklistEntry entry;\n            for (int i = 0; i < 5000; i++) {\n                quicklistIndex(ql, i, &entry);\n                if (entry.longval != nums[i])\n                    ERR(\"[%d] Not longval %lld but rather %lld\", i, nums[i],\n                        entry.longval);\n                entry.longval = 0xdeadbeef;\n            }\n            quicklistIndex(ql, 5000, &entry);\n            if (strncmp((char *)entry.value, \"xxxxxxxxxxxxxxxxxxxx\", 20))\n                ERR(\"String val not match: %s\", entry.value);\n            ql_verify(ql, 157, 5001, 32, 9);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"numbers larger list read B\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushTail(ql, \"99\", 2);\n            quicklistPushTail(ql, \"98\", 2);\n            quicklistPushTail(ql, \"xxxxxxxxxxxxxxxxxxxx\", 20);\n            quicklistPushTail(ql, \"96\", 2);\n            quicklistPushTail(ql, \"95\", 2);\n            quicklistReplaceAtIndex(ql, 1, \"foo\", 3);\n            quicklistReplaceAtIndex(ql, -1, \"bar\", 3);\n            quicklistRelease(ql);\n            OK;\n        }\n\n        for (int f = optimize_start; f < 16; f++) {\n            TEST_DESC(\"lrem test at fill %d at compress %d\", f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char *words[] = {\"abc\", \"foo\", \"bar\",  \"foobar\", \"foobared\",\n                                 \"zap\", \"bar\", \"test\", \"foo\"};\n                char *result[] = {\"abc\", \"foo\",  \"foobar\", \"foobared\",\n                                  \"zap\", \"test\", \"foo\"};\n                char *resultB[] = {\"abc\",      \"foo\", \"foobar\",\n                                   \"foobared\", \"zap\", \"test\"};\n                for (int i = 0; i < 9; i++)\n                    quicklistPushTail(ql, words[i], strlen(words[i]));\n\n                /* lrem 0 bar */\n                quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);\n                quicklistEntry entry;\n                int i = 0;\n                while (quicklistNext(iter, &entry)) {\n                    if (quicklistCompare(entry.zi, (unsigned char *)\"bar\", 3)) {\n                        quicklistDelEntry(iter, &entry);\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                /* check result of lrem 0 bar */\n                iter = quicklistGetIterator(ql, AL_START_HEAD);\n                i = 0;\n                int ok = 1;\n                while (quicklistNext(iter, &entry)) {\n                    /* Result must be: abc, foo, foobar, foobared, zap, test,\n                     * foo */\n                    if (strncmp((char *)entry.value, result[i], entry.sz)) {\n                        ERR(\"No match at position %d, got %.*s instead of %s\",\n                            i, entry.sz, entry.value, result[i]);\n                        ok = 0;\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                quicklistPushTail(ql, \"foo\", 3);\n\n                /* lrem -2 foo */\n                iter = quicklistGetIterator(ql, AL_START_TAIL);\n                i = 0;\n                int del = 2;\n                while (quicklistNext(iter, &entry)) {\n                    if (quicklistCompare(entry.zi, (unsigned char *)\"foo\", 3)) {\n                        quicklistDelEntry(iter, &entry);\n                        del--;\n                    }\n                    if (!del)\n                        break;\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                /* check result of lrem -2 foo */\n                /* (we're ignoring the '2' part and still deleting all foo\n                 * because\n                 * we only have two foo) */\n                iter = quicklistGetIterator(ql, AL_START_TAIL);\n                i = 0;\n                size_t resB = sizeof(resultB) / sizeof(*resultB);\n                while (quicklistNext(iter, &entry)) {\n                    /* Result must be: abc, foo, foobar, foobared, zap, test,\n                     * foo */\n                    if (strncmp((char *)entry.value, resultB[resB - 1 - i],\n                                entry.sz)) {\n                        ERR(\"No match at position %d, got %.*s instead of %s\",\n                            i, entry.sz, entry.value, resultB[resB - 1 - i]);\n                        ok = 0;\n                    }\n                    i++;\n                }\n\n                quicklistReleaseIterator(iter);\n                /* final result of all tests */\n                if (ok)\n                    OK;\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 16; f++) {\n            TEST_DESC(\"iterate reverse + delete at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushTail(ql, \"abc\", 3);\n                quicklistPushTail(ql, \"def\", 3);\n                quicklistPushTail(ql, \"hij\", 3);\n                quicklistPushTail(ql, \"jkl\", 3);\n                quicklistPushTail(ql, \"oop\", 3);\n\n                quicklistEntry entry;\n                quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL);\n                int i = 0;\n                while (quicklistNext(iter, &entry)) {\n                    if (quicklistCompare(entry.zi, (unsigned char *)\"hij\", 3)) {\n                        quicklistDelEntry(iter, &entry);\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                if (i != 5)\n                    ERR(\"Didn't iterate 5 times, iterated %d times.\", i);\n\n                /* Check results after deletion of \"hij\" */\n                iter = quicklistGetIterator(ql, AL_START_HEAD);\n                i = 0;\n                char *vals[] = {\"abc\", \"def\", \"jkl\", \"oop\"};\n                while (quicklistNext(iter, &entry)) {\n                    if (!quicklistCompare(entry.zi, (unsigned char *)vals[i],\n                                          3)) {\n                        ERR(\"Value at %d didn't match %s\\n\", i, vals[i]);\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 800; f++) {\n            TEST_DESC(\"iterator at index test at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 760; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n\n                quicklistEntry entry;\n                quicklistIter *iter =\n                    quicklistGetIteratorAtIdx(ql, AL_START_HEAD, 437);\n                int i = 437;\n                while (quicklistNext(iter, &entry)) {\n                    if (entry.longval != nums[i])\n                        ERR(\"Expected %lld, but got %lld\", entry.longval,\n                            nums[i]);\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test A at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 32; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 1, 32, 32, 32);\n                /* ltrim 25 53 (keep [25,32] inclusive = 7 remaining) */\n                quicklistDelRange(ql, 0, 25);\n                quicklistDelRange(ql, 0, 0);\n                quicklistEntry entry;\n                for (int i = 0; i < 7; i++) {\n                    quicklistIndex(ql, i, &entry);\n                    if (entry.longval != nums[25 + i])\n                        ERR(\"Deleted invalid range!  Expected %lld but got \"\n                            \"%lld\",\n                            entry.longval, nums[25 + i]);\n                }\n                if (f == 32)\n                    ql_verify(ql, 1, 7, 7, 7);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test B at fill %d at compress %d\", f,\n                      options[_i]) {\n                /* Force-disable compression because our 33 sequential\n                 * integers don't compress and the check always fails. */\n                quicklist *ql = quicklistNew(f, QUICKLIST_NOCOMPRESS);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 2, 33, 32, 1);\n                /* ltrim 5 16 (keep [5,16] inclusive = 12 remaining) */\n                quicklistDelRange(ql, 0, 5);\n                quicklistDelRange(ql, -16, 16);\n                if (f == 32)\n                    ql_verify(ql, 1, 12, 12, 12);\n                quicklistEntry entry;\n                quicklistIndex(ql, 0, &entry);\n                if (entry.longval != 5)\n                    ERR(\"A: longval not 5, but %lld\", entry.longval);\n                else\n                    OK;\n                quicklistIndex(ql, -1, &entry);\n                if (entry.longval != 16)\n                    ERR(\"B! got instead: %lld\", entry.longval);\n                else\n                    OK;\n                quicklistPushTail(ql, \"bobobob\", 7);\n                quicklistIndex(ql, -1, &entry);\n                if (strncmp((char *)entry.value, \"bobobob\", 7))\n                    ERR(\"Tail doesn't match bobobob, it's %.*s instead\",\n                        entry.sz, entry.value);\n                for (int i = 0; i < 12; i++) {\n                    quicklistIndex(ql, i, &entry);\n                    if (entry.longval != nums[5 + i])\n                        ERR(\"Deleted invalid range!  Expected %lld but got \"\n                            \"%lld\",\n                            entry.longval, nums[5 + i]);\n                }\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test C at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 2, 33, 32, 1);\n                /* ltrim 3 3 (keep [3,3] inclusive = 1 remaining) */\n                quicklistDelRange(ql, 0, 3);\n                quicklistDelRange(ql, -29,\n                                  4000); /* make sure not loop forever */\n                if (f == 32)\n                    ql_verify(ql, 1, 1, 1, 1);\n                quicklistEntry entry;\n                quicklistIndex(ql, 0, &entry);\n                if (entry.longval != -5157318210846258173)\n                    ERROR;\n                else\n                    OK;\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test D at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 2, 33, 32, 1);\n                quicklistDelRange(ql, -12, 3);\n                if (ql->count != 30)\n                    ERR(\"Didn't delete exactly three elements!  Count is: %lu\",\n                        ql->count);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 72; f++) {\n            TEST_DESC(\"create quicklist from ziplist at fill %d at compress %d\",\n                      f, options[_i]) {\n                unsigned char *zl = ziplistNew();\n                long long nums[64];\n                char num[64];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    zl =\n                        ziplistPush(zl, (unsigned char *)num, sz, ZIPLIST_TAIL);\n                }\n                for (int i = 0; i < 33; i++) {\n                    zl = ziplistPush(zl, (unsigned char *)genstr(\"hello\", i),\n                                     32, ZIPLIST_TAIL);\n                }\n                quicklist *ql = quicklistCreateFromZiplist(f, options[_i], zl);\n                if (f == 1)\n                    ql_verify(ql, 66, 66, 1, 1);\n                else if (f == 32)\n                    ql_verify(ql, 3, 66, 32, 2);\n                else if (f == 66)\n                    ql_verify(ql, 1, 66, 66, 66);\n                quicklistRelease(ql);\n            }\n        }\n\n        long long stop = mstime();\n        runtime[_i] = stop - start;\n    }\n\n    /* Run a longer test of compression depth outside of primary test loop. */\n    int list_sizes[] = {250, 251, 500, 999, 1000};\n    long long start = mstime();\n    for (int list = 0; list < (int)(sizeof(list_sizes) / sizeof(*list_sizes));\n         list++) {\n        for (int f = optimize_start; f < 128; f++) {\n            for (int depth = 1; depth < 40; depth++) {\n                /* skip over many redundant test cases */\n                TEST_DESC(\"verify specific compression of interior nodes with \"\n                          \"%d list \"\n                          \"at fill %d at compress %d\",\n                          list_sizes[list], f, depth) {\n                    quicklist *ql = quicklistNew(f, depth);\n                    for (int i = 0; i < list_sizes[list]; i++) {\n                        quicklistPushTail(ql, genstr(\"hello TAIL\", i + 1), 64);\n                        quicklistPushHead(ql, genstr(\"hello HEAD\", i + 1), 64);\n                    }\n\n                    quicklistNode *node = ql->head;\n                    unsigned int low_raw = ql->compress;\n                    unsigned int high_raw = ql->len - ql->compress;\n\n                    for (unsigned int at = 0; at < ql->len;\n                         at++, node = node->next) {\n                        if (at < low_raw || at >= high_raw) {\n                            if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {\n                                ERR(\"Incorrect compression: node %d is \"\n                                    \"compressed at depth %d ((%u, %u); total \"\n                                    \"nodes: %u; size: %u)\",\n                                    at, depth, low_raw, high_raw, ql->len,\n                                    node->sz);\n                            }\n                        } else {\n                            if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) {\n                                ERR(\"Incorrect non-compression: node %d is NOT \"\n                                    \"compressed at depth %d ((%u, %u); total \"\n                                    \"nodes: %u; size: %u; attempted: %d)\",\n                                    at, depth, low_raw, high_raw, ql->len,\n                                    node->sz, node->attempted_compress);\n                            }\n                        }\n                    }\n                    quicklistRelease(ql);\n                }\n            }\n        }\n    }\n    long long stop = mstime();\n\n    printf(\"\\n\");\n    for (size_t i = 0; i < option_count; i++)\n        printf(\"Test Loop %02d: %0.2f seconds.\\n\", options[i],\n               (float)runtime[i] / 1000);\n    printf(\"Compressions: %0.2f seconds.\\n\", (float)(stop - start) / 1000);\n    printf(\"\\n\");\n\n    TEST(\"bookmark get updated to next item\") {\n        quicklist *ql = quicklistNew(1, 0);\n        quicklistPushTail(ql, \"1\", 1);\n        quicklistPushTail(ql, \"2\", 1);\n        quicklistPushTail(ql, \"3\", 1);\n        quicklistPushTail(ql, \"4\", 1);\n        quicklistPushTail(ql, \"5\", 1);\n        assert(ql->len==5);\n        /* add two bookmarks, one pointing to the node before the last. */\n        assert(quicklistBookmarkCreate(&ql, \"_dummy\", ql->head->next));\n        assert(quicklistBookmarkCreate(&ql, \"_test\", ql->tail->prev));\n        /* test that the bookmark returns the right node, delete it and see that the bookmark points to the last node */\n        assert(quicklistBookmarkFind(ql, \"_test\") == ql->tail->prev);\n        assert(quicklistDelRange(ql, -2, 1));\n        assert(quicklistBookmarkFind(ql, \"_test\") == ql->tail);\n        /* delete the last node, and see that the bookmark was deleted. */\n        assert(quicklistDelRange(ql, -1, 1));\n        assert(quicklistBookmarkFind(ql, \"_test\") == NULL);\n        /* test that other bookmarks aren't affected */\n        assert(quicklistBookmarkFind(ql, \"_dummy\") == ql->head->next);\n        assert(quicklistBookmarkFind(ql, \"_missing\") == NULL);\n        assert(ql->len==3);\n        quicklistBookmarksClear(ql); /* for coverage */\n        assert(quicklistBookmarkFind(ql, \"_dummy\") == NULL);\n        quicklistRelease(ql);\n    }\n\n    TEST(\"bookmark limit\") {\n        int i;\n        quicklist *ql = quicklistNew(1, 0);\n        quicklistPushHead(ql, \"1\", 1);\n        for (i=0; i<QL_MAX_BM; i++)\n            assert(quicklistBookmarkCreate(&ql, genstr(\"\",i), ql->head));\n        /* when all bookmarks are used, creation fails */\n        assert(!quicklistBookmarkCreate(&ql, \"_test\", ql->head));\n        /* delete one and see that we can now create another */\n        assert(quicklistBookmarkDelete(ql, \"0\"));\n        assert(quicklistBookmarkCreate(&ql, \"_test\", ql->head));\n        /* delete one and see that the rest survive */\n        assert(quicklistBookmarkDelete(ql, \"_test\"));\n        for (i=1; i<QL_MAX_BM; i++)\n            assert(quicklistBookmarkFind(ql, genstr(\"\",i)) == ql->head);\n        /* make sure the deleted ones are indeed gone */\n        assert(!quicklistBookmarkFind(ql, \"0\"));\n        assert(!quicklistBookmarkFind(ql, \"_test\"));\n        quicklistRelease(ql);\n    }\n\n    if (!err)\n        printf(\"ALL TESTS PASSED!\\n\");\n    else\n        ERR(\"Sorry, not all tests passed!  In fact, %d tests failed.\", err);\n\n    return err;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/quicklist.h",
    "content": "/* quicklist.h - A generic doubly linked quicklist implementation\n *\n * Copyright (c) 2014, Matt Stancliff <matt@genges.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 quicklist of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this quicklist 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> // for UINTPTR_MAX\n\n#ifndef __QUICKLIST_H__\n#define __QUICKLIST_H__\n\n/* Node, quicklist, and Iterator are the only data structures used currently. */\n\n/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.\n * We use bit fields keep the quicklistNode at 32 bytes.\n * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).\n * encoding: 2 bits, RAW=1, LZF=2.\n * container: 2 bits, NONE=1, ZIPLIST=2.\n * recompress: 1 bit, bool, true if node is temporarry decompressed for usage.\n * attempted_compress: 1 bit, boolean, used for verifying during testing.\n * extra: 10 bits, free for future use; pads out the remainder of 32 bits */\ntypedef struct quicklistNode {\n    struct quicklistNode *prev;\n    struct quicklistNode *next;\n    unsigned char *zl;\n    unsigned int sz;             /* ziplist size in bytes */\n    unsigned int count : 16;     /* count of items in ziplist */\n    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */\n    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */\n    unsigned int recompress : 1; /* was this node previous compressed? */\n    unsigned int attempted_compress : 1; /* node can't compress; too small */\n    unsigned int extra : 10; /* more bits to steal for future usage */\n} quicklistNode;\n\n/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.\n * 'sz' is byte length of 'compressed' field.\n * 'compressed' is LZF data with total (compressed) length 'sz'\n * NOTE: uncompressed length is stored in quicklistNode->sz.\n * When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */\ntypedef struct quicklistLZF {\n    unsigned int sz; /* LZF size in bytes*/\n    char compressed[];\n} quicklistLZF;\n\n/* Bookmarks are padded with realloc at the end of of the quicklist struct.\n * They should only be used for very big lists if thousands of nodes were the\n * excess memory usage is negligible, and there's a real need to iterate on them\n * in portions.\n * When not used, they don't add any memory overhead, but when used and then\n * deleted, some overhead remains (to avoid resonance).\n * The number of bookmarks used should be kept to minimum since it also adds\n * overhead on node deletion (searching for a bookmark to update). */\ntypedef struct quicklistBookmark {\n    quicklistNode *node;\n    char *name;\n} quicklistBookmark;\n\n#if UINTPTR_MAX == 0xffffffff\n/* 32-bit */\n#   define QL_FILL_BITS 14\n#   define QL_COMP_BITS 14\n#   define QL_BM_BITS 4\n#elif UINTPTR_MAX == 0xffffffffffffffff\n/* 64-bit */\n#   define QL_FILL_BITS 16\n#   define QL_COMP_BITS 16\n#   define QL_BM_BITS 4 /* we can encode more, but we rather limit the user\n                           since they cause performance degradation. */\n#else\n#   error unknown arch bits count\n#endif\n\n/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.\n * 'count' is the number of total entries.\n * 'len' is the number of quicklist nodes.\n * 'compress' is: -1 if compression disabled, otherwise it's the number\n *                of quicklistNodes to leave uncompressed at ends of quicklist.\n * 'fill' is the user-requested (or default) fill factor.\n * 'bookmakrs are an optional feature that is used by realloc this struct,\n *      so that they don't consume memory when not used. */\ntypedef struct quicklist {\n    quicklistNode *head;\n    quicklistNode *tail;\n    unsigned long count;        /* total count of all entries in all ziplists */\n    unsigned long len;          /* number of quicklistNodes */\n    int fill : QL_FILL_BITS;              /* fill factor for individual nodes */\n    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */\n    unsigned int bookmark_count: QL_BM_BITS;\n    quicklistBookmark bookmarks[];\n} quicklist;\n\ntypedef struct quicklistIter {\n    const quicklist *quicklist;\n    quicklistNode *current;\n    unsigned char *zi;\n    long offset; /* offset in current ziplist */\n    int direction;\n} quicklistIter;\n\ntypedef struct quicklistEntry {\n    const quicklist *quicklist;\n    quicklistNode *node;\n    unsigned char *zi;\n    unsigned char *value;\n    long long longval;\n    unsigned int sz;\n    int offset;\n} quicklistEntry;\n\n#define QUICKLIST_HEAD 0\n#define QUICKLIST_TAIL -1\n\n/* quicklist node encodings */\n#define QUICKLIST_NODE_ENCODING_RAW 1\n#define QUICKLIST_NODE_ENCODING_LZF 2\n\n/* quicklist compression disable */\n#define QUICKLIST_NOCOMPRESS 0\n\n/* quicklist container formats */\n#define QUICKLIST_NODE_CONTAINER_NONE 1\n#define QUICKLIST_NODE_CONTAINER_ZIPLIST 2\n\n#define quicklistNodeIsCompressed(node)                                        \\\n    ((node)->encoding == QUICKLIST_NODE_ENCODING_LZF)\n\n/* Prototypes */\nquicklist *quicklistCreate(void);\nquicklist *quicklistNew(int fill, int compress);\nvoid quicklistSetCompressDepth(quicklist *quicklist, int depth);\nvoid quicklistSetFill(quicklist *quicklist, int fill);\nvoid quicklistSetOptions(quicklist *quicklist, int fill, int depth);\nvoid quicklistRelease(quicklist *quicklist);\nint quicklistPushHead(quicklist *quicklist, void *value, const size_t sz);\nint quicklistPushTail(quicklist *quicklist, void *value, const size_t sz);\nvoid quicklistPush(quicklist *quicklist, void *value, const size_t sz,\n                   int where);\nvoid quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl);\nquicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,\n                                            unsigned char *zl);\nquicklist *quicklistCreateFromZiplist(int fill, int compress,\n                                      unsigned char *zl);\nvoid quicklistInsertAfter(quicklist *quicklist, quicklistEntry *node,\n                          void *value, const size_t sz);\nvoid quicklistInsertBefore(quicklist *quicklist, quicklistEntry *node,\n                           void *value, const size_t sz);\nvoid quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry);\nint quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,\n                            int sz);\nint quicklistDelRange(quicklist *quicklist, const long start, const long stop);\nquicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction);\nquicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,\n                                         int direction, const long long idx);\nint quicklistNext(quicklistIter *iter, quicklistEntry *node);\nvoid quicklistReleaseIterator(quicklistIter *iter);\nquicklist *quicklistDup(quicklist *orig);\nint quicklistIndex(const quicklist *quicklist, const long long index,\n                   quicklistEntry *entry);\nvoid quicklistRewind(quicklist *quicklist, quicklistIter *li);\nvoid quicklistRewindTail(quicklist *quicklist, quicklistIter *li);\nvoid quicklistRotate(quicklist *quicklist);\nint quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,\n                       unsigned int *sz, long long *sval,\n                       void *(*saver)(unsigned char *data, unsigned int sz));\nint quicklistPop(quicklist *quicklist, int where, unsigned char **data,\n                 unsigned int *sz, long long *slong);\nunsigned long quicklistCount(const quicklist *ql);\nint quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len);\nsize_t quicklistGetLzf(const quicklistNode *node, void **data);\n\n/* bookmarks */\nint quicklistBookmarkCreate(quicklist **ql_ref, const char *name, quicklistNode *node);\nint quicklistBookmarkDelete(quicklist *ql, const char *name);\nquicklistNode *quicklistBookmarkFind(quicklist *ql, const char *name);\nvoid quicklistBookmarksClear(quicklist *ql);\n\n#ifdef REDIS_TEST\nint quicklistTest(int argc, char *argv[]);\n#endif\n\n/* Directions for iterators */\n#define AL_START_HEAD 0\n#define AL_START_TAIL 1\n\n#endif /* __QUICKLIST_H__ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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(void);\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(void) {\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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/src/rax.c",
    "content": "/* Rax -- A radix tree implementation.\n *\n * Version 1.2 -- 7 February 2019\n *\n * Copyright (c) 2017-2019, 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 <string.h>\n#include <assert.h>\n#include <stdio.h>\n#include <errno.h>\n#include <math.h>\n#include \"rax.h\"\n\n#ifndef RAX_MALLOC_INCLUDE\n#define RAX_MALLOC_INCLUDE \"rax_malloc.h\"\n#endif\n\n#include RAX_MALLOC_INCLUDE\n\n/* This is a special pointer that is guaranteed to never have the same value\n * of a radix tree node. It's used in order to report \"not found\" error without\n * requiring the function to have multiple return values. */\nvoid *raxNotFound = (void*)\"rax-not-found-pointer\";\n\n/* -------------------------------- Debugging ------------------------------ */\n\nvoid raxDebugShowNode(const char *msg, raxNode *n);\n\n/* Turn debugging messages on/off by compiling with RAX_DEBUG_MSG macro on.\n * When RAX_DEBUG_MSG is defined by default Rax operations will emit a lot\n * of debugging info to the standard output, however you can still turn\n * debugging on/off in order to enable it only when you suspect there is an\n * operation causing a bug using the function raxSetDebugMsg(). */\n#ifdef RAX_DEBUG_MSG\n#define debugf(...)                                                            \\\n    if (raxDebugMsg) {                                                         \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(__VA_ARGS__);                                                   \\\n        fflush(stdout);                                                        \\\n    }\n\n#define debugnode(msg,n) raxDebugShowNode(msg,n)\n#else\n#define debugf(...)\n#define debugnode(msg,n)\n#endif\n\n/* By default log debug info if RAX_DEBUG_MSG is defined. */\nstatic int raxDebugMsg = 1;\n\n/* When debug messages are enabled, turn them on/off dynamically. By\n * default they are enabled. Set the state to 0 to disable, and 1 to\n * re-enable. */\nvoid raxSetDebugMsg(int onoff) {\n    raxDebugMsg = onoff;\n}\n\n/* ------------------------- raxStack functions --------------------------\n * The raxStack is a simple stack of pointers that is capable of switching\n * from using a stack-allocated array to dynamic heap once a given number of\n * items are reached. It is used in order to retain the list of parent nodes\n * while walking the radix tree in order to implement certain operations that\n * need to navigate the tree upward.\n * ------------------------------------------------------------------------- */\n\n/* Initialize the stack. */\nstatic inline void raxStackInit(raxStack *ts) {\n    ts->stack = ts->static_items;\n    ts->items = 0;\n    ts->maxitems = RAX_STACK_STATIC_ITEMS;\n    ts->oom = 0;\n}\n\n/* Push an item into the stack, returns 1 on success, 0 on out of memory. */\nstatic inline int raxStackPush(raxStack *ts, void *ptr) {\n    if (ts->items == ts->maxitems) {\n        if (ts->stack == ts->static_items) {\n            ts->stack = rax_malloc(sizeof(void*)*ts->maxitems*2);\n            if (ts->stack == NULL) {\n                ts->stack = ts->static_items;\n                ts->oom = 1;\n                errno = ENOMEM;\n                return 0;\n            }\n            memcpy(ts->stack,ts->static_items,sizeof(void*)*ts->maxitems);\n        } else {\n            void **newalloc = rax_realloc(ts->stack,sizeof(void*)*ts->maxitems*2);\n            if (newalloc == NULL) {\n                ts->oom = 1;\n                errno = ENOMEM;\n                return 0;\n            }\n            ts->stack = newalloc;\n        }\n        ts->maxitems *= 2;\n    }\n    ts->stack[ts->items] = ptr;\n    ts->items++;\n    return 1;\n}\n\n/* Pop an item from the stack, the function returns NULL if there are no\n * items to pop. */\nstatic inline void *raxStackPop(raxStack *ts) {\n    if (ts->items == 0) return NULL;\n    ts->items--;\n    return ts->stack[ts->items];\n}\n\n/* Return the stack item at the top of the stack without actually consuming\n * it. */\nstatic inline void *raxStackPeek(raxStack *ts) {\n    if (ts->items == 0) return NULL;\n    return ts->stack[ts->items-1];\n}\n\n/* Free the stack in case we used heap allocation. */\nstatic inline void raxStackFree(raxStack *ts) {\n    if (ts->stack != ts->static_items) rax_free(ts->stack);\n}\n\n/* ----------------------------------------------------------------------------\n * Radix tree implementation\n * --------------------------------------------------------------------------*/\n\n/* Return the padding needed in the characters section of a node having size\n * 'nodesize'. The padding is needed to store the child pointers to aligned\n * addresses. Note that we add 4 to the node size because the node has a four\n * bytes header. */\n#define raxPadding(nodesize) ((sizeof(void*)-((nodesize+4) % sizeof(void*))) & (sizeof(void*)-1))\n\n/* Return the pointer to the last child pointer in a node. For the compressed\n * nodes this is the only child pointer. */\n#define raxNodeLastChildPtr(n) ((raxNode**) ( \\\n    ((char*)(n)) + \\\n    raxNodeCurrentLength(n) - \\\n    sizeof(raxNode*) - \\\n    (((n)->iskey && !(n)->isnull) ? sizeof(void*) : 0) \\\n))\n\n/* Return the pointer to the first child pointer. */\n#define raxNodeFirstChildPtr(n) ((raxNode**) ( \\\n    (n)->data + \\\n    (n)->size + \\\n    raxPadding((n)->size)))\n\n/* Return the current total size of the node. Note that the second line\n * computes the padding after the string of characters, needed in order to\n * save pointers to aligned addresses. */\n#define raxNodeCurrentLength(n) ( \\\n    sizeof(raxNode)+(n)->size+ \\\n    raxPadding((n)->size)+ \\\n    ((n)->iscompr ? sizeof(raxNode*) : sizeof(raxNode*)*(n)->size)+ \\\n    (((n)->iskey && !(n)->isnull)*sizeof(void*)) \\\n)\n\n/* Allocate a new non compressed node with the specified number of children.\n * If datafiled is true, the allocation is made large enough to hold the\n * associated data pointer.\n * Returns the new node pointer. On out of memory NULL is returned. */\nraxNode *raxNewNode(size_t children, int datafield) {\n    size_t nodesize = sizeof(raxNode)+children+raxPadding(children)+\n                      sizeof(raxNode*)*children;\n    if (datafield) nodesize += sizeof(void*);\n    raxNode *node = rax_malloc(nodesize);\n    if (node == NULL) return NULL;\n    node->iskey = 0;\n    node->isnull = 0;\n    node->iscompr = 0;\n    node->size = children;\n    return node;\n}\n\n/* Allocate a new rax and return its pointer. On out of memory the function\n * returns NULL. */\nrax *raxNew(void) {\n    rax *rax = rax_malloc(sizeof(*rax));\n    if (rax == NULL) return NULL;\n    rax->numele = 0;\n    rax->numnodes = 1;\n    rax->head = raxNewNode(0,0);\n    if (rax->head == NULL) {\n        rax_free(rax);\n        return NULL;\n    } else {\n        return rax;\n    }\n}\n\n/* realloc the node to make room for auxiliary data in order\n * to store an item in that node. On out of memory NULL is returned. */\nraxNode *raxReallocForData(raxNode *n, void *data) {\n    if (data == NULL) return n; /* No reallocation needed, setting isnull=1 */\n    size_t curlen = raxNodeCurrentLength(n);\n    return rax_realloc(n,curlen+sizeof(void*));\n}\n\n/* Set the node auxiliary data to the specified pointer. */\nvoid raxSetData(raxNode *n, void *data) {\n    n->iskey = 1;\n    if (data != NULL) {\n        n->isnull = 0;\n        void **ndata = (void**)\n            ((char*)n+raxNodeCurrentLength(n)-sizeof(void*));\n        memcpy(ndata,&data,sizeof(data));\n    } else {\n        n->isnull = 1;\n    }\n}\n\n/* Get the node auxiliary data. */\nvoid *raxGetData(raxNode *n) {\n    if (n->isnull) return NULL;\n    void **ndata =(void**)((char*)n+raxNodeCurrentLength(n)-sizeof(void*));\n    void *data;\n    memcpy(&data,ndata,sizeof(data));\n    return data;\n}\n\n/* Add a new child to the node 'n' representing the character 'c' and return\n * its new pointer, as well as the child pointer by reference. Additionally\n * '***parentlink' is populated with the raxNode pointer-to-pointer of where\n * the new child was stored, which is useful for the caller to replace the\n * child pointer if it gets reallocated.\n *\n * On success the new parent node pointer is returned (it may change because\n * of the realloc, so the caller should discard 'n' and use the new value).\n * On out of memory NULL is returned, and the old node is still valid. */\nraxNode *raxAddChild(raxNode *n, unsigned char c, raxNode **childptr, raxNode ***parentlink) {\n    assert(n->iscompr == 0);\n\n    size_t curlen = raxNodeCurrentLength(n);\n    n->size++;\n    size_t newlen = raxNodeCurrentLength(n);\n    n->size--; /* For now restore the orignal size. We'll update it only on\n                  success at the end. */\n\n    /* Alloc the new child we will link to 'n'. */\n    raxNode *child = raxNewNode(0,0);\n    if (child == NULL) return NULL;\n\n    /* Make space in the original node. */\n    raxNode *newn = rax_realloc(n,newlen);\n    if (newn == NULL) {\n        rax_free(child);\n        return NULL;\n    }\n    n = newn;\n\n    /* After the reallocation, we have up to 8/16 (depending on the system\n     * pointer size, and the required node padding) bytes at the end, that is,\n     * the additional char in the 'data' section, plus one pointer to the new\n     * child, plus the padding needed in order to store addresses into aligned\n     * locations.\n     *\n     * So if we start with the following node, having \"abde\" edges.\n     *\n     * Note:\n     * - We assume 4 bytes pointer for simplicity.\n     * - Each space below corresponds to one byte\n     *\n     * [HDR*][abde][Aptr][Bptr][Dptr][Eptr]|AUXP|\n     *\n     * After the reallocation we need: 1 byte for the new edge character\n     * plus 4 bytes for a new child pointer (assuming 32 bit machine).\n     * However after adding 1 byte to the edge char, the header + the edge\n     * characters are no longer aligned, so we also need 3 bytes of padding.\n     * In total the reallocation will add 1+4+3 bytes = 8 bytes:\n     *\n     * (Blank bytes are represented by \".\")\n     *\n     * [HDR*][abde][Aptr][Bptr][Dptr][Eptr]|AUXP|[....][....]\n     *\n     * Let's find where to insert the new child in order to make sure\n     * it is inserted in-place lexicographically. Assuming we are adding\n     * a child \"c\" in our case pos will be = 2 after the end of the following\n     * loop. */\n    int pos;\n    for (pos = 0; pos < n->size; pos++) {\n        if (n->data[pos] > c) break;\n    }\n\n    /* Now, if present, move auxiliary data pointer at the end\n     * so that we can mess with the other data without overwriting it.\n     * We will obtain something like that:\n     *\n     * [HDR*][abde][Aptr][Bptr][Dptr][Eptr][....][....]|AUXP|\n     */\n    unsigned char *src, *dst;\n    if (n->iskey && !n->isnull) {\n        src = ((unsigned char*)n+curlen-sizeof(void*));\n        dst = ((unsigned char*)n+newlen-sizeof(void*));\n        memmove(dst,src,sizeof(void*));\n    }\n\n    /* Compute the \"shift\", that is, how many bytes we need to move the\n     * pointers section forward because of the addition of the new child\n     * byte in the string section. Note that if we had no padding, that\n     * would be always \"1\", since we are adding a single byte in the string\n     * section of the node (where now there is \"abde\" basically).\n     *\n     * However we have padding, so it could be zero, or up to 8.\n     *\n     * Another way to think at the shift is, how many bytes we need to\n     * move child pointers forward *other than* the obvious sizeof(void*)\n     * needed for the additional pointer itself. */\n    size_t shift = newlen - curlen - sizeof(void*);\n\n    /* We said we are adding a node with edge 'c'. The insertion\n     * point is between 'b' and 'd', so the 'pos' variable value is\n     * the index of the first child pointer that we need to move forward\n     * to make space for our new pointer.\n     *\n     * To start, move all the child pointers after the insertion point\n     * of shift+sizeof(pointer) bytes on the right, to obtain:\n     *\n     * [HDR*][abde][Aptr][Bptr][....][....][Dptr][Eptr]|AUXP|\n     */\n    src = n->data+n->size+\n          raxPadding(n->size)+\n          sizeof(raxNode*)*pos;\n    memmove(src+shift+sizeof(raxNode*),src,sizeof(raxNode*)*(n->size-pos));\n\n    /* Move the pointers to the left of the insertion position as well. Often\n     * we don't need to do anything if there was already some padding to use. In\n     * that case the final destination of the pointers will be the same, however\n     * in our example there was no pre-existing padding, so we added one byte\n     * plus thre bytes of padding. After the next memmove() things will look\n     * like thata:\n     *\n     * [HDR*][abde][....][Aptr][Bptr][....][Dptr][Eptr]|AUXP|\n     */\n    if (shift) {\n        src = (unsigned char*) raxNodeFirstChildPtr(n);\n        memmove(src+shift,src,sizeof(raxNode*)*pos);\n    }\n\n    /* Now make the space for the additional char in the data section,\n     * but also move the pointers before the insertion point to the right\n     * by shift bytes, in order to obtain the following:\n     *\n     * [HDR*][ab.d][e...][Aptr][Bptr][....][Dptr][Eptr]|AUXP|\n     */\n    src = n->data+pos;\n    memmove(src+1,src,n->size-pos);\n\n    /* We can now set the character and its child node pointer to get:\n     *\n     * [HDR*][abcd][e...][Aptr][Bptr][....][Dptr][Eptr]|AUXP|\n     * [HDR*][abcd][e...][Aptr][Bptr][Cptr][Dptr][Eptr]|AUXP|\n     */\n    n->data[pos] = c;\n    n->size++;\n    src = (unsigned char*) raxNodeFirstChildPtr(n);\n    raxNode **childfield = (raxNode**)(src+sizeof(raxNode*)*pos);\n    memcpy(childfield,&child,sizeof(child));\n    *childptr = child;\n    *parentlink = childfield;\n    return n;\n}\n\n/* Turn the node 'n', that must be a node without any children, into a\n * compressed node representing a set of nodes linked one after the other\n * and having exactly one child each. The node can be a key or not: this\n * property and the associated value if any will be preserved.\n *\n * The function also returns a child node, since the last node of the\n * compressed chain cannot be part of the chain: it has zero children while\n * we can only compress inner nodes with exactly one child each. */\nraxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **child) {\n    assert(n->size == 0 && n->iscompr == 0);\n    void *data = NULL; /* Initialized only to avoid warnings. */\n    size_t newsize;\n\n    debugf(\"Compress node: %.*s\\n\", (int)len,s);\n\n    /* Allocate the child to link to this node. */\n    *child = raxNewNode(0,0);\n    if (*child == NULL) return NULL;\n\n    /* Make space in the parent node. */\n    newsize = sizeof(raxNode)+len+raxPadding(len)+sizeof(raxNode*);\n    if (n->iskey) {\n        data = raxGetData(n); /* To restore it later. */\n        if (!n->isnull) newsize += sizeof(void*);\n    }\n    raxNode *newn = rax_realloc(n,newsize);\n    if (newn == NULL) {\n        rax_free(*child);\n        return NULL;\n    }\n    n = newn;\n\n    n->iscompr = 1;\n    n->size = len;\n    memcpy(n->data,s,len);\n    if (n->iskey) raxSetData(n,data);\n    raxNode **childfield = raxNodeLastChildPtr(n);\n    memcpy(childfield,child,sizeof(*child));\n    return n;\n}\n\n/* Low level function that walks the tree looking for the string\n * 's' of 'len' bytes. The function returns the number of characters\n * of the key that was possible to process: if the returned integer\n * is the same as 'len', then it means that the node corresponding to the\n * string was found (however it may not be a key in case the node->iskey is\n * zero or if simply we stopped in the middle of a compressed node, so that\n * 'splitpos' is non zero).\n *\n * Otherwise if the returned integer is not the same as 'len', there was an\n * early stop during the tree walk because of a character mismatch.\n *\n * The node where the search ended (because the full string was processed\n * or because there was an early stop) is returned by reference as\n * '*stopnode' if the passed pointer is not NULL. This node link in the\n * parent's node is returned as '*plink' if not NULL. Finally, if the\n * search stopped in a compressed node, '*splitpos' returns the index\n * inside the compressed node where the search ended. This is useful to\n * know where to split the node for insertion.\n *\n * Note that when we stop in the middle of a compressed node with\n * a perfect match, this function will return a length equal to the\n * 'len' argument (all the key matched), and will return a *splitpos which is\n * always positive (that will represent the index of the character immediately\n * *after* the last match in the current compressed node).\n *\n * When instead we stop at a compressed node and *splitpos is zero, it\n * means that the current node represents the key (that is, none of the\n * compressed node characters are needed to represent the key, just all\n * its parents nodes). */\nstatic inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) {\n    raxNode *h = rax->head;\n    raxNode **parentlink = &rax->head;\n\n    size_t i = 0; /* Position in the string. */\n    size_t j = 0; /* Position in the node children (or bytes if compressed).*/\n    while(h->size && i < len) {\n        debugnode(\"Lookup current node\",h);\n        unsigned char *v = h->data;\n\n        if (h->iscompr) {\n            for (j = 0; j < h->size && i < len; j++, i++) {\n                if (v[j] != s[i]) break;\n            }\n            if (j != h->size) break;\n        } else {\n            /* Even when h->size is large, linear scan provides good\n             * performances compared to other approaches that are in theory\n             * more sounding, like performing a binary search. */\n            for (j = 0; j < h->size; j++) {\n                if (v[j] == s[i]) break;\n            }\n            if (j == h->size) break;\n            i++;\n        }\n\n        if (ts) raxStackPush(ts,h); /* Save stack of parent nodes. */\n        raxNode **children = raxNodeFirstChildPtr(h);\n        if (h->iscompr) j = 0; /* Compressed node only child is at index 0. */\n        memcpy(&h,children+j,sizeof(h));\n        parentlink = children+j;\n        j = 0; /* If the new node is compressed and we do not\n                  iterate again (since i == l) set the split\n                  position to 0 to signal this node represents\n                  the searched key. */\n    }\n    debugnode(\"Lookup stop node is\",h);\n    if (stopnode) *stopnode = h;\n    if (plink) *plink = parentlink;\n    if (splitpos && h->iscompr) *splitpos = j;\n    return i;\n}\n\n/* Insert the element 's' of size 'len', setting as auxiliary data\n * the pointer 'data'. If the element is already present, the associated\n * data is updated (only if 'overwrite' is set to 1), and 0 is returned,\n * otherwise the element is inserted and 1 is returned. On out of memory the\n * function returns 0 as well but sets errno to ENOMEM, otherwise errno will\n * be set to 0.\n */\nint raxGenericInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old, int overwrite) {\n    size_t i;\n    int j = 0; /* Split position. If raxLowWalk() stops in a compressed\n                  node, the index 'j' represents the char we stopped within the\n                  compressed node, that is, the position where to split the\n                  node for insertion. */\n    raxNode *h, **parentlink;\n\n    debugf(\"### Insert %.*s with value %p\\n\", (int)len, s, data);\n    i = raxLowWalk(rax,s,len,&h,&parentlink,&j,NULL);\n\n    /* If i == len we walked following the whole string. If we are not\n     * in the middle of a compressed node, the string is either already\n     * inserted or this middle node is currently not a key, but can represent\n     * our key. We have just to reallocate the node and make space for the\n     * data pointer. */\n    if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) {\n        debugf(\"### Insert: node representing key exists\\n\");\n        /* Make space for the value pointer if needed. */\n        if (!h->iskey || (h->isnull && overwrite)) {\n            h = raxReallocForData(h,data);\n            if (h) memcpy(parentlink,&h,sizeof(h));\n        }\n        if (h == NULL) {\n            errno = ENOMEM;\n            return 0;\n        }\n\n        /* Update the existing key if there is already one. */\n        if (h->iskey) {\n            if (old) *old = raxGetData(h);\n            if (overwrite) raxSetData(h,data);\n            errno = 0;\n            return 0; /* Element already exists. */\n        }\n\n        /* Otherwise set the node as a key. Note that raxSetData()\n         * will set h->iskey. */\n        raxSetData(h,data);\n        rax->numele++;\n        return 1; /* Element inserted. */\n    }\n\n    /* If the node we stopped at is a compressed node, we need to\n     * split it before to continue.\n     *\n     * Splitting a compressed node have a few possible cases.\n     * Imagine that the node 'h' we are currently at is a compressed\n     * node contaning the string \"ANNIBALE\" (it means that it represents\n     * nodes A -> N -> N -> I -> B -> A -> L -> E with the only child\n     * pointer of this node pointing at the 'E' node, because remember that\n     * we have characters at the edges of the graph, not inside the nodes\n     * themselves.\n     *\n     * In order to show a real case imagine our node to also point to\n     * another compressed node, that finally points at the node without\n     * children, representing 'O':\n     *\n     *     \"ANNIBALE\" -> \"SCO\" -> []\n     *\n     * When inserting we may face the following cases. Note that all the cases\n     * require the insertion of a non compressed node with exactly two\n     * children, except for the last case which just requires splitting a\n     * compressed node.\n     *\n     * 1) Inserting \"ANNIENTARE\"\n     *\n     *               |B| -> \"ALE\" -> \"SCO\" -> []\n     *     \"ANNI\" -> |-|\n     *               |E| -> (... continue algo ...) \"NTARE\" -> []\n     *\n     * 2) Inserting \"ANNIBALI\"\n     *\n     *                  |E| -> \"SCO\" -> []\n     *     \"ANNIBAL\" -> |-|\n     *                  |I| -> (... continue algo ...) []\n     *\n     * 3) Inserting \"AGO\" (Like case 1, but set iscompr = 0 into original node)\n     *\n     *            |N| -> \"NIBALE\" -> \"SCO\" -> []\n     *     |A| -> |-|\n     *            |G| -> (... continue algo ...) |O| -> []\n     *\n     * 4) Inserting \"CIAO\"\n     *\n     *     |A| -> \"NNIBALE\" -> \"SCO\" -> []\n     *     |-|\n     *     |C| -> (... continue algo ...) \"IAO\" -> []\n     *\n     * 5) Inserting \"ANNI\"\n     *\n     *     \"ANNI\" -> \"BALE\" -> \"SCO\" -> []\n     *\n     * The final algorithm for insertion covering all the above cases is as\n     * follows.\n     *\n     * ============================= ALGO 1 =============================\n     *\n     * For the above cases 1 to 4, that is, all cases where we stopped in\n     * the middle of a compressed node for a character mismatch, do:\n     *\n     * Let $SPLITPOS be the zero-based index at which, in the\n     * compressed node array of characters, we found the mismatching\n     * character. For example if the node contains \"ANNIBALE\" and we add\n     * \"ANNIENTARE\" the $SPLITPOS is 4, that is, the index at which the\n     * mismatching character is found.\n     *\n     * 1. Save the current compressed node $NEXT pointer (the pointer to the\n     *    child element, that is always present in compressed nodes).\n     *\n     * 2. Create \"split node\" having as child the non common letter\n     *    at the compressed node. The other non common letter (at the key)\n     *    will be added later as we continue the normal insertion algorithm\n     *    at step \"6\".\n     *\n     * 3a. IF $SPLITPOS == 0:\n     *     Replace the old node with the split node, by copying the auxiliary\n     *     data if any. Fix parent's reference. Free old node eventually\n     *     (we still need its data for the next steps of the algorithm).\n     *\n     * 3b. IF $SPLITPOS != 0:\n     *     Trim the compressed node (reallocating it as well) in order to\n     *     contain $splitpos characters. Change chilid pointer in order to link\n     *     to the split node. If new compressed node len is just 1, set\n     *     iscompr to 0 (layout is the same). Fix parent's reference.\n     *\n     * 4a. IF the postfix len (the length of the remaining string of the\n     *     original compressed node after the split character) is non zero,\n     *     create a \"postfix node\". If the postfix node has just one character\n     *     set iscompr to 0, otherwise iscompr to 1. Set the postfix node\n     *     child pointer to $NEXT.\n     *\n     * 4b. IF the postfix len is zero, just use $NEXT as postfix pointer.\n     *\n     * 5. Set child[0] of split node to postfix node.\n     *\n     * 6. Set the split node as the current node, set current index at child[1]\n     *    and continue insertion algorithm as usually.\n     *\n     * ============================= ALGO 2 =============================\n     *\n     * For case 5, that is, if we stopped in the middle of a compressed\n     * node but no mismatch was found, do:\n     *\n     * Let $SPLITPOS be the zero-based index at which, in the\n     * compressed node array of characters, we stopped iterating because\n     * there were no more keys character to match. So in the example of\n     * the node \"ANNIBALE\", addig the string \"ANNI\", the $SPLITPOS is 4.\n     *\n     * 1. Save the current compressed node $NEXT pointer (the pointer to the\n     *    child element, that is always present in compressed nodes).\n     *\n     * 2. Create a \"postfix node\" containing all the characters from $SPLITPOS\n     *    to the end. Use $NEXT as the postfix node child pointer.\n     *    If the postfix node length is 1, set iscompr to 0.\n     *    Set the node as a key with the associated value of the new\n     *    inserted key.\n     *\n     * 3. Trim the current node to contain the first $SPLITPOS characters.\n     *    As usually if the new node length is just 1, set iscompr to 0.\n     *    Take the iskey / associated value as it was in the orignal node.\n     *    Fix the parent's reference.\n     *\n     * 4. Set the postfix node as the only child pointer of the trimmed\n     *    node created at step 1.\n     */\n\n    /* ------------------------- ALGORITHM 1 --------------------------- */\n    if (h->iscompr && i != len) {\n        debugf(\"ALGO 1: Stopped at compressed node %.*s (%p)\\n\",\n            h->size, h->data, (void*)h);\n        debugf(\"Still to insert: %.*s\\n\", (int)(len-i), s+i);\n        debugf(\"Splitting at %d: '%c'\\n\", j, ((char*)h->data)[j]);\n        debugf(\"Other (key) letter is '%c'\\n\", s[i]);\n\n        /* 1: Save next pointer. */\n        raxNode **childfield = raxNodeLastChildPtr(h);\n        raxNode *next;\n        memcpy(&next,childfield,sizeof(next));\n        debugf(\"Next is %p\\n\", (void*)next);\n        debugf(\"iskey %d\\n\", h->iskey);\n        if (h->iskey) {\n            debugf(\"key value is %p\\n\", raxGetData(h));\n        }\n\n        /* Set the length of the additional nodes we will need. */\n        size_t trimmedlen = j;\n        size_t postfixlen = h->size - j - 1;\n        int split_node_is_key = !trimmedlen && h->iskey && !h->isnull;\n        size_t nodesize;\n\n        /* 2: Create the split node. Also allocate the other nodes we'll need\n         *    ASAP, so that it will be simpler to handle OOM. */\n        raxNode *splitnode = raxNewNode(1, split_node_is_key);\n        raxNode *trimmed = NULL;\n        raxNode *postfix = NULL;\n\n        if (trimmedlen) {\n            nodesize = sizeof(raxNode)+trimmedlen+raxPadding(trimmedlen)+\n                       sizeof(raxNode*);\n            if (h->iskey && !h->isnull) nodesize += sizeof(void*);\n            trimmed = rax_malloc(nodesize);\n        }\n\n        if (postfixlen) {\n            nodesize = sizeof(raxNode)+postfixlen+raxPadding(postfixlen)+\n                       sizeof(raxNode*);\n            postfix = rax_malloc(nodesize);\n        }\n\n        /* OOM? Abort now that the tree is untouched. */\n        if (splitnode == NULL ||\n            (trimmedlen && trimmed == NULL) ||\n            (postfixlen && postfix == NULL))\n        {\n            rax_free(splitnode);\n            rax_free(trimmed);\n            rax_free(postfix);\n            errno = ENOMEM;\n            return 0;\n        }\n        splitnode->data[0] = h->data[j];\n\n        if (j == 0) {\n            /* 3a: Replace the old node with the split node. */\n            if (h->iskey) {\n                void *ndata = raxGetData(h);\n                raxSetData(splitnode,ndata);\n            }\n            memcpy(parentlink,&splitnode,sizeof(splitnode));\n        } else {\n            /* 3b: Trim the compressed node. */\n            trimmed->size = j;\n            memcpy(trimmed->data,h->data,j);\n            trimmed->iscompr = j > 1 ? 1 : 0;\n            trimmed->iskey = h->iskey;\n            trimmed->isnull = h->isnull;\n            if (h->iskey && !h->isnull) {\n                void *ndata = raxGetData(h);\n                raxSetData(trimmed,ndata);\n            }\n            raxNode **cp = raxNodeLastChildPtr(trimmed);\n            memcpy(cp,&splitnode,sizeof(splitnode));\n            memcpy(parentlink,&trimmed,sizeof(trimmed));\n            parentlink = cp; /* Set parentlink to splitnode parent. */\n            rax->numnodes++;\n        }\n\n        /* 4: Create the postfix node: what remains of the original\n         * compressed node after the split. */\n        if (postfixlen) {\n            /* 4a: create a postfix node. */\n            postfix->iskey = 0;\n            postfix->isnull = 0;\n            postfix->size = postfixlen;\n            postfix->iscompr = postfixlen > 1;\n            memcpy(postfix->data,h->data+j+1,postfixlen);\n            raxNode **cp = raxNodeLastChildPtr(postfix);\n            memcpy(cp,&next,sizeof(next));\n            rax->numnodes++;\n        } else {\n            /* 4b: just use next as postfix node. */\n            postfix = next;\n        }\n\n        /* 5: Set splitnode first child as the postfix node. */\n        raxNode **splitchild = raxNodeLastChildPtr(splitnode);\n        memcpy(splitchild,&postfix,sizeof(postfix));\n\n        /* 6. Continue insertion: this will cause the splitnode to\n         * get a new child (the non common character at the currently\n         * inserted key). */\n        rax_free(h);\n        h = splitnode;\n    } else if (h->iscompr && i == len) {\n    /* ------------------------- ALGORITHM 2 --------------------------- */\n        debugf(\"ALGO 2: Stopped at compressed node %.*s (%p) j = %d\\n\",\n            h->size, h->data, (void*)h, j);\n\n        /* Allocate postfix & trimmed nodes ASAP to fail for OOM gracefully. */\n        size_t postfixlen = h->size - j;\n        size_t nodesize = sizeof(raxNode)+postfixlen+raxPadding(postfixlen)+\n                          sizeof(raxNode*);\n        if (data != NULL) nodesize += sizeof(void*);\n        raxNode *postfix = rax_malloc(nodesize);\n\n        nodesize = sizeof(raxNode)+j+raxPadding(j)+sizeof(raxNode*);\n        if (h->iskey && !h->isnull) nodesize += sizeof(void*);\n        raxNode *trimmed = rax_malloc(nodesize);\n\n        if (postfix == NULL || trimmed == NULL) {\n            rax_free(postfix);\n            rax_free(trimmed);\n            errno = ENOMEM;\n            return 0;\n        }\n\n        /* 1: Save next pointer. */\n        raxNode **childfield = raxNodeLastChildPtr(h);\n        raxNode *next;\n        memcpy(&next,childfield,sizeof(next));\n\n        /* 2: Create the postfix node. */\n        postfix->size = postfixlen;\n        postfix->iscompr = postfixlen > 1;\n        postfix->iskey = 1;\n        postfix->isnull = 0;\n        memcpy(postfix->data,h->data+j,postfixlen);\n        raxSetData(postfix,data);\n        raxNode **cp = raxNodeLastChildPtr(postfix);\n        memcpy(cp,&next,sizeof(next));\n        rax->numnodes++;\n\n        /* 3: Trim the compressed node. */\n        trimmed->size = j;\n        trimmed->iscompr = j > 1;\n        trimmed->iskey = 0;\n        trimmed->isnull = 0;\n        memcpy(trimmed->data,h->data,j);\n        memcpy(parentlink,&trimmed,sizeof(trimmed));\n        if (h->iskey) {\n            void *aux = raxGetData(h);\n            raxSetData(trimmed,aux);\n        }\n\n        /* Fix the trimmed node child pointer to point to\n         * the postfix node. */\n        cp = raxNodeLastChildPtr(trimmed);\n        memcpy(cp,&postfix,sizeof(postfix));\n\n        /* Finish! We don't need to continue with the insertion\n         * algorithm for ALGO 2. The key is already inserted. */\n        rax->numele++;\n        rax_free(h);\n        return 1; /* Key inserted. */\n    }\n\n    /* We walked the radix tree as far as we could, but still there are left\n     * chars in our string. We need to insert the missing nodes. */\n    while(i < len) {\n        raxNode *child;\n\n        /* If this node is going to have a single child, and there\n         * are other characters, so that that would result in a chain\n         * of single-childed nodes, turn it into a compressed node. */\n        if (h->size == 0 && len-i > 1) {\n            debugf(\"Inserting compressed node\\n\");\n            size_t comprsize = len-i;\n            if (comprsize > RAX_NODE_MAX_SIZE)\n                comprsize = RAX_NODE_MAX_SIZE;\n            raxNode *newh = raxCompressNode(h,s+i,comprsize,&child);\n            if (newh == NULL) goto oom;\n            h = newh;\n            memcpy(parentlink,&h,sizeof(h));\n            parentlink = raxNodeLastChildPtr(h);\n            i += comprsize;\n        } else {\n            debugf(\"Inserting normal node\\n\");\n            raxNode **new_parentlink;\n            raxNode *newh = raxAddChild(h,s[i],&child,&new_parentlink);\n            if (newh == NULL) goto oom;\n            h = newh;\n            memcpy(parentlink,&h,sizeof(h));\n            parentlink = new_parentlink;\n            i++;\n        }\n        rax->numnodes++;\n        h = child;\n    }\n    raxNode *newh = raxReallocForData(h,data);\n    if (newh == NULL) goto oom;\n    h = newh;\n    if (!h->iskey) rax->numele++;\n    raxSetData(h,data);\n    memcpy(parentlink,&h,sizeof(h));\n    return 1; /* Element inserted. */\n\noom:\n    /* This code path handles out of memory after part of the sub-tree was\n     * already modified. Set the node as a key, and then remove it. However we\n     * do that only if the node is a terminal node, otherwise if the OOM\n     * happened reallocating a node in the middle, we don't need to free\n     * anything. */\n    if (h->size == 0) {\n        h->isnull = 1;\n        h->iskey = 1;\n        rax->numele++; /* Compensate the next remove. */\n        assert(raxRemove(rax,s,i,NULL) != 0);\n    }\n    errno = ENOMEM;\n    return 0;\n}\n\n/* Overwriting insert. Just a wrapper for raxGenericInsert() that will\n * update the element if there is already one for the same key. */\nint raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {\n    return raxGenericInsert(rax,s,len,data,old,1);\n}\n\n/* Non overwriting insert function: this if an element with the same key\n * exists, the value is not updated and the function returns 0.\n * This is a just a wrapper for raxGenericInsert(). */\nint raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {\n    return raxGenericInsert(rax,s,len,data,old,0);\n}\n\n/* Find a key in the rax, returns raxNotFound special void pointer value\n * if the item was not found, otherwise the value associated with the\n * item is returned. */\nvoid *raxFind(rax *rax, unsigned char *s, size_t len) {\n    raxNode *h;\n\n    debugf(\"### Lookup: %.*s\\n\", (int)len, s);\n    int splitpos = 0;\n    size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,NULL);\n    if (i != len || (h->iscompr && splitpos != 0) || !h->iskey)\n        return raxNotFound;\n    return raxGetData(h);\n}\n\n/* Return the memory address where the 'parent' node stores the specified\n * 'child' pointer, so that the caller can update the pointer with another\n * one if needed. The function assumes it will find a match, otherwise the\n * operation is an undefined behavior (it will continue scanning the\n * memory without any bound checking). */\nraxNode **raxFindParentLink(raxNode *parent, raxNode *child) {\n    raxNode **cp = raxNodeFirstChildPtr(parent);\n    raxNode *c;\n    while(1) {\n        memcpy(&c,cp,sizeof(c));\n        if (c == child) break;\n        cp++;\n    }\n    return cp;\n}\n\n/* Low level child removal from node. The new node pointer (after the child\n * removal) is returned. Note that this function does not fix the pointer\n * of the parent node in its parent, so this task is up to the caller.\n * The function never fails for out of memory. */\nraxNode *raxRemoveChild(raxNode *parent, raxNode *child) {\n    debugnode(\"raxRemoveChild before\", parent);\n    /* If parent is a compressed node (having a single child, as for definition\n     * of the data structure), the removal of the child consists into turning\n     * it into a normal node without children. */\n    if (parent->iscompr) {\n        void *data = NULL;\n        if (parent->iskey) data = raxGetData(parent);\n        parent->isnull = 0;\n        parent->iscompr = 0;\n        parent->size = 0;\n        if (parent->iskey) raxSetData(parent,data);\n        debugnode(\"raxRemoveChild after\", parent);\n        return parent;\n    }\n\n    /* Otherwise we need to scan for the child pointer and memmove()\n     * accordingly.\n     *\n     * 1. To start we seek the first element in both the children\n     *    pointers and edge bytes in the node. */\n    raxNode **cp = raxNodeFirstChildPtr(parent);\n    raxNode **c = cp;\n    unsigned char *e = parent->data;\n\n    /* 2. Search the child pointer to remove inside the array of children\n     *    pointers. */\n    while(1) {\n        raxNode *aux;\n        memcpy(&aux,c,sizeof(aux));\n        if (aux == child) break;\n        c++;\n        e++;\n    }\n\n    /* 3. Remove the edge and the pointer by memmoving the remaining children\n     *    pointer and edge bytes one position before. */\n    int taillen = parent->size - (e - parent->data) - 1;\n    debugf(\"raxRemoveChild tail len: %d\\n\", taillen);\n    memmove(e,e+1,taillen);\n\n    /* Compute the shift, that is the amount of bytes we should move our\n     * child pointers to the left, since the removal of one edge character\n     * and the corresponding padding change, may change the layout.\n     * We just check if in the old version of the node there was at the\n     * end just a single byte and all padding: in that case removing one char\n     * will remove a whole sizeof(void*) word. */\n    size_t shift = ((parent->size+4) % sizeof(void*)) == 1 ? sizeof(void*) : 0;\n\n    /* Move the children pointers before the deletion point. */\n    if (shift)\n        memmove(((char*)cp)-shift,cp,(parent->size-taillen-1)*sizeof(raxNode**));\n\n    /* Move the remaining \"tail\" pointers at the right position as well. */\n    size_t valuelen = (parent->iskey && !parent->isnull) ? sizeof(void*) : 0;\n    memmove(((char*)c)-shift,c+1,taillen*sizeof(raxNode**)+valuelen);\n\n    /* 4. Update size. */\n    parent->size--;\n\n    /* realloc the node according to the theoretical memory usage, to free\n     * data if we are over-allocating right now. */\n    raxNode *newnode = rax_realloc(parent,raxNodeCurrentLength(parent));\n    if (newnode) {\n        debugnode(\"raxRemoveChild after\", newnode);\n    }\n    /* Note: if rax_realloc() fails we just return the old address, which\n     * is valid. */\n    return newnode ? newnode : parent;\n}\n\n/* Remove the specified item. Returns 1 if the item was found and\n * deleted, 0 otherwise. */\nint raxRemove(rax *rax, unsigned char *s, size_t len, void **old) {\n    raxNode *h;\n    raxStack ts;\n\n    debugf(\"### Delete: %.*s\\n\", (int)len, s);\n    raxStackInit(&ts);\n    int splitpos = 0;\n    size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,&ts);\n    if (i != len || (h->iscompr && splitpos != 0) || !h->iskey) {\n        raxStackFree(&ts);\n        return 0;\n    }\n    if (old) *old = raxGetData(h);\n    h->iskey = 0;\n    rax->numele--;\n\n    /* If this node has no children, the deletion needs to reclaim the\n     * no longer used nodes. This is an iterative process that needs to\n     * walk the three upward, deleting all the nodes with just one child\n     * that are not keys, until the head of the rax is reached or the first\n     * node with more than one child is found. */\n\n    int trycompress = 0; /* Will be set to 1 if we should try to optimize the\n                            tree resulting from the deletion. */\n\n    if (h->size == 0) {\n        debugf(\"Key deleted in node without children. Cleanup needed.\\n\");\n        raxNode *child = NULL;\n        while(h != rax->head) {\n            child = h;\n            debugf(\"Freeing child %p [%.*s] key:%d\\n\", (void*)child,\n                (int)child->size, (char*)child->data, child->iskey);\n            rax_free(child);\n            rax->numnodes--;\n            h = raxStackPop(&ts);\n             /* If this node has more then one child, or actually holds\n              * a key, stop here. */\n            if (h->iskey || (!h->iscompr && h->size != 1)) break;\n        }\n        if (child) {\n            debugf(\"Unlinking child %p from parent %p\\n\",\n                (void*)child, (void*)h);\n            raxNode *new = raxRemoveChild(h,child);\n            if (new != h) {\n                raxNode *parent = raxStackPeek(&ts);\n                raxNode **parentlink;\n                if (parent == NULL) {\n                    parentlink = &rax->head;\n                } else {\n                    parentlink = raxFindParentLink(parent,h);\n                }\n                memcpy(parentlink,&new,sizeof(new));\n            }\n\n            /* If after the removal the node has just a single child\n             * and is not a key, we need to try to compress it. */\n            if (new->size == 1 && new->iskey == 0) {\n                trycompress = 1;\n                h = new;\n            }\n        }\n    } else if (h->size == 1) {\n        /* If the node had just one child, after the removal of the key\n         * further compression with adjacent nodes is pontentially possible. */\n        trycompress = 1;\n    }\n\n    /* Don't try node compression if our nodes pointers stack is not\n     * complete because of OOM while executing raxLowWalk() */\n    if (trycompress && ts.oom) trycompress = 0;\n\n    /* Recompression: if trycompress is true, 'h' points to a radix tree node\n     * that changed in a way that could allow to compress nodes in this\n     * sub-branch. Compressed nodes represent chains of nodes that are not\n     * keys and have a single child, so there are two deletion events that\n     * may alter the tree so that further compression is needed:\n     *\n     * 1) A node with a single child was a key and now no longer is a key.\n     * 2) A node with two children now has just one child.\n     *\n     * We try to navigate upward till there are other nodes that can be\n     * compressed, when we reach the upper node which is not a key and has\n     * a single child, we scan the chain of children to collect the\n     * compressable part of the tree, and replace the current node with the\n     * new one, fixing the child pointer to reference the first non\n     * compressable node.\n     *\n     * Example of case \"1\". A tree stores the keys \"FOO\" = 1 and\n     * \"FOOBAR\" = 2:\n     *\n     *\n     * \"FOO\" -> \"BAR\" -> [] (2)\n     *           (1)\n     *\n     * After the removal of \"FOO\" the tree can be compressed as:\n     *\n     * \"FOOBAR\" -> [] (2)\n     *\n     *\n     * Example of case \"2\". A tree stores the keys \"FOOBAR\" = 1 and\n     * \"FOOTER\" = 2:\n     *\n     *          |B| -> \"AR\" -> [] (1)\n     * \"FOO\" -> |-|\n     *          |T| -> \"ER\" -> [] (2)\n     *\n     * After the removal of \"FOOTER\" the resulting tree is:\n     *\n     * \"FOO\" -> |B| -> \"AR\" -> [] (1)\n     *\n     * That can be compressed into:\n     *\n     * \"FOOBAR\" -> [] (1)\n     */\n    if (trycompress) {\n        debugf(\"After removing %.*s:\\n\", (int)len, s);\n        debugnode(\"Compression may be needed\",h);\n        debugf(\"Seek start node\\n\");\n\n        /* Try to reach the upper node that is compressible.\n         * At the end of the loop 'h' will point to the first node we\n         * can try to compress and 'parent' to its parent. */\n        raxNode *parent;\n        while(1) {\n            parent = raxStackPop(&ts);\n            if (!parent || parent->iskey ||\n                (!parent->iscompr && parent->size != 1)) break;\n            h = parent;\n            debugnode(\"Going up to\",h);\n        }\n        raxNode *start = h; /* Compression starting node. */\n\n        /* Scan chain of nodes we can compress. */\n        size_t comprsize = h->size;\n        int nodes = 1;\n        while(h->size != 0) {\n            raxNode **cp = raxNodeLastChildPtr(h);\n            memcpy(&h,cp,sizeof(h));\n            if (h->iskey || (!h->iscompr && h->size != 1)) break;\n            /* Stop here if going to the next node would result into\n             * a compressed node larger than h->size can hold. */\n            if (comprsize + h->size > RAX_NODE_MAX_SIZE) break;\n            nodes++;\n            comprsize += h->size;\n        }\n        if (nodes > 1) {\n            /* If we can compress, create the new node and populate it. */\n            size_t nodesize =\n                sizeof(raxNode)+comprsize+raxPadding(comprsize)+sizeof(raxNode*);\n            raxNode *new = rax_malloc(nodesize);\n            /* An out of memory here just means we cannot optimize this\n             * node, but the tree is left in a consistent state. */\n            if (new == NULL) {\n                raxStackFree(&ts);\n                return 1;\n            }\n            new->iskey = 0;\n            new->isnull = 0;\n            new->iscompr = 1;\n            new->size = comprsize;\n            rax->numnodes++;\n\n            /* Scan again, this time to populate the new node content and\n             * to fix the new node child pointer. At the same time we free\n             * all the nodes that we'll no longer use. */\n            comprsize = 0;\n            h = start;\n            while(h->size != 0) {\n                memcpy(new->data+comprsize,h->data,h->size);\n                comprsize += h->size;\n                raxNode **cp = raxNodeLastChildPtr(h);\n                raxNode *tofree = h;\n                memcpy(&h,cp,sizeof(h));\n                rax_free(tofree); rax->numnodes--;\n                if (h->iskey || (!h->iscompr && h->size != 1)) break;\n            }\n            debugnode(\"New node\",new);\n\n            /* Now 'h' points to the first node that we still need to use,\n             * so our new node child pointer will point to it. */\n            raxNode **cp = raxNodeLastChildPtr(new);\n            memcpy(cp,&h,sizeof(h));\n\n            /* Fix parent link. */\n            if (parent) {\n                raxNode **parentlink = raxFindParentLink(parent,start);\n                memcpy(parentlink,&new,sizeof(new));\n            } else {\n                rax->head = new;\n            }\n\n            debugf(\"Compressed %d nodes, %d total bytes\\n\",\n                nodes, (int)comprsize);\n        }\n    }\n    raxStackFree(&ts);\n    return 1;\n}\n\n/* This is the core of raxFree(): performs a depth-first scan of the\n * tree and releases all the nodes found. */\nvoid raxRecursiveFree(rax *rax, raxNode *n, void (*free_callback)(void*)) {\n    debugnode(\"free traversing\",n);\n    int numchildren = n->iscompr ? 1 : n->size;\n    raxNode **cp = raxNodeLastChildPtr(n);\n    while(numchildren--) {\n        raxNode *child;\n        memcpy(&child,cp,sizeof(child));\n        raxRecursiveFree(rax,child,free_callback);\n        cp--;\n    }\n    debugnode(\"free depth-first\",n);\n    if (free_callback && n->iskey && !n->isnull)\n        free_callback(raxGetData(n));\n    rax_free(n);\n    rax->numnodes--;\n}\n\n/* Free a whole radix tree, calling the specified callback in order to\n * free the auxiliary data. */\nvoid raxFreeWithCallback(rax *rax, void (*free_callback)(void*)) {\n    raxRecursiveFree(rax,rax->head,free_callback);\n    assert(rax->numnodes == 0);\n    rax_free(rax);\n}\n\n/* Free a whole radix tree. */\nvoid raxFree(rax *rax) {\n    raxFreeWithCallback(rax,NULL);\n}\n\n/* ------------------------------- Iterator --------------------------------- */\n\n/* Initialize a Rax iterator. This call should be performed a single time\n * to initialize the iterator, and must be followed by a raxSeek() call,\n * otherwise the raxPrev()/raxNext() functions will just return EOF. */\nvoid raxStart(raxIterator *it, rax *rt) {\n    it->flags = RAX_ITER_EOF; /* No crash if the iterator is not seeked. */\n    it->rt = rt;\n    it->key_len = 0;\n    it->key = it->key_static_string;\n    it->key_max = RAX_ITER_STATIC_LEN;\n    it->data = NULL;\n    it->node_cb = NULL;\n    raxStackInit(&it->stack);\n}\n\n/* Append characters at the current key string of the iterator 'it'. This\n * is a low level function used to implement the iterator, not callable by\n * the user. Returns 0 on out of memory, otherwise 1 is returned. */\nint raxIteratorAddChars(raxIterator *it, unsigned char *s, size_t len) {\n    if (it->key_max < it->key_len+len) {\n        unsigned char *old = (it->key == it->key_static_string) ? NULL :\n                                                                  it->key;\n        size_t new_max = (it->key_len+len)*2;\n        it->key = rax_realloc(old,new_max);\n        if (it->key == NULL) {\n            it->key = (!old) ? it->key_static_string : old;\n            errno = ENOMEM;\n            return 0;\n        }\n        if (old == NULL) memcpy(it->key,it->key_static_string,it->key_len);\n        it->key_max = new_max;\n    }\n    /* Use memmove since there could be an overlap between 's' and\n     * it->key when we use the current key in order to re-seek. */\n    memmove(it->key+it->key_len,s,len);\n    it->key_len += len;\n    return 1;\n}\n\n/* Remove the specified number of chars from the right of the current\n * iterator key. */\nvoid raxIteratorDelChars(raxIterator *it, size_t count) {\n    it->key_len -= count;\n}\n\n/* Do an iteration step towards the next element. At the end of the step the\n * iterator key will represent the (new) current key. If it is not possible\n * to step in the specified direction since there are no longer elements, the\n * iterator is flagged with RAX_ITER_EOF.\n *\n * If 'noup' is true the function starts directly scanning for the next\n * lexicographically smaller children, and the current node is already assumed\n * to be the parent of the last key node, so the first operation to go back to\n * the parent will be skipped. This option is used by raxSeek() when\n * implementing seeking a non existing element with the \">\" or \"<\" options:\n * the starting node is not a key in that particular case, so we start the scan\n * from a node that does not represent the key set.\n *\n * The function returns 1 on success or 0 on out of memory. */\nint raxIteratorNextStep(raxIterator *it, int noup) {\n    if (it->flags & RAX_ITER_EOF) {\n        return 1;\n    } else if (it->flags & RAX_ITER_JUST_SEEKED) {\n        it->flags &= ~RAX_ITER_JUST_SEEKED;\n        return 1;\n    }\n\n    /* Save key len, stack items and the node where we are currently\n     * so that on iterator EOF we can restore the current key and state. */\n    size_t orig_key_len = it->key_len;\n    size_t orig_stack_items = it->stack.items;\n    raxNode *orig_node = it->node;\n\n    while(1) {\n        int children = it->node->iscompr ? 1 : it->node->size;\n        if (!noup && children) {\n            debugf(\"GO DEEPER\\n\");\n            /* Seek the lexicographically smaller key in this subtree, which\n             * is the first one found always going torwards the first child\n             * of every successive node. */\n            if (!raxStackPush(&it->stack,it->node)) return 0;\n            raxNode **cp = raxNodeFirstChildPtr(it->node);\n            if (!raxIteratorAddChars(it,it->node->data,\n                it->node->iscompr ? it->node->size : 1)) return 0;\n            memcpy(&it->node,cp,sizeof(it->node));\n            /* Call the node callback if any, and replace the node pointer\n             * if the callback returns true. */\n            if (it->node_cb && it->node_cb(&it->node))\n                memcpy(cp,&it->node,sizeof(it->node));\n            /* For \"next\" step, stop every time we find a key along the\n             * way, since the key is lexicograhically smaller compared to\n             * what follows in the sub-children. */\n            if (it->node->iskey) {\n                it->data = raxGetData(it->node);\n                return 1;\n            }\n        } else {\n            /* If we finished exporing the previous sub-tree, switch to the\n             * new one: go upper until a node is found where there are\n             * children representing keys lexicographically greater than the\n             * current key. */\n            while(1) {\n                int old_noup = noup;\n\n                /* Already on head? Can't go up, iteration finished. */\n                if (!noup && it->node == it->rt->head) {\n                    it->flags |= RAX_ITER_EOF;\n                    it->stack.items = orig_stack_items;\n                    it->key_len = orig_key_len;\n                    it->node = orig_node;\n                    return 1;\n                }\n                /* If there are no children at the current node, try parent's\n                 * next child. */\n                unsigned char prevchild = it->key[it->key_len-1];\n                if (!noup) {\n                    it->node = raxStackPop(&it->stack);\n                } else {\n                    noup = 0;\n                }\n                /* Adjust the current key to represent the node we are\n                 * at. */\n                int todel = it->node->iscompr ? it->node->size : 1;\n                raxIteratorDelChars(it,todel);\n\n                /* Try visiting the next child if there was at least one\n                 * additional child. */\n                if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) {\n                    raxNode **cp = raxNodeFirstChildPtr(it->node);\n                    int i = 0;\n                    while (i < it->node->size) {\n                        debugf(\"SCAN NEXT %c\\n\", it->node->data[i]);\n                        if (it->node->data[i] > prevchild) break;\n                        i++;\n                        cp++;\n                    }\n                    if (i != it->node->size) {\n                        debugf(\"SCAN found a new node\\n\");\n                        raxIteratorAddChars(it,it->node->data+i,1);\n                        if (!raxStackPush(&it->stack,it->node)) return 0;\n                        memcpy(&it->node,cp,sizeof(it->node));\n                        /* Call the node callback if any, and replace the node\n                         * pointer if the callback returns true. */\n                        if (it->node_cb && it->node_cb(&it->node))\n                            memcpy(cp,&it->node,sizeof(it->node));\n                        if (it->node->iskey) {\n                            it->data = raxGetData(it->node);\n                            return 1;\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n    }\n}\n\n/* Seek the greatest key in the subtree at the current node. Return 0 on\n * out of memory, otherwise 1. This is an helper function for different\n * iteration functions below. */\nint raxSeekGreatest(raxIterator *it) {\n    while(it->node->size) {\n        if (it->node->iscompr) {\n            if (!raxIteratorAddChars(it,it->node->data,\n                it->node->size)) return 0;\n        } else {\n            if (!raxIteratorAddChars(it,it->node->data+it->node->size-1,1))\n                return 0;\n        }\n        raxNode **cp = raxNodeLastChildPtr(it->node);\n        if (!raxStackPush(&it->stack,it->node)) return 0;\n        memcpy(&it->node,cp,sizeof(it->node));\n    }\n    return 1;\n}\n\n/* Like raxIteratorNextStep() but implements an iteration step moving\n * to the lexicographically previous element. The 'noup' option has a similar\n * effect to the one of raxIteratorNextStep(). */\nint raxIteratorPrevStep(raxIterator *it, int noup) {\n    if (it->flags & RAX_ITER_EOF) {\n        return 1;\n    } else if (it->flags & RAX_ITER_JUST_SEEKED) {\n        it->flags &= ~RAX_ITER_JUST_SEEKED;\n        return 1;\n    }\n\n    /* Save key len, stack items and the node where we are currently\n     * so that on iterator EOF we can restore the current key and state. */\n    size_t orig_key_len = it->key_len;\n    size_t orig_stack_items = it->stack.items;\n    raxNode *orig_node = it->node;\n\n    while(1) {\n        int old_noup = noup;\n\n        /* Already on head? Can't go up, iteration finished. */\n        if (!noup && it->node == it->rt->head) {\n            it->flags |= RAX_ITER_EOF;\n            it->stack.items = orig_stack_items;\n            it->key_len = orig_key_len;\n            it->node = orig_node;\n            return 1;\n        }\n\n        unsigned char prevchild = it->key[it->key_len-1];\n        if (!noup) {\n            it->node = raxStackPop(&it->stack);\n        } else {\n            noup = 0;\n        }\n\n        /* Adjust the current key to represent the node we are\n         * at. */\n        int todel = it->node->iscompr ? it->node->size : 1;\n        raxIteratorDelChars(it,todel);\n\n        /* Try visiting the prev child if there is at least one\n         * child. */\n        if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) {\n            raxNode **cp = raxNodeLastChildPtr(it->node);\n            int i = it->node->size-1;\n            while (i >= 0) {\n                debugf(\"SCAN PREV %c\\n\", it->node->data[i]);\n                if (it->node->data[i] < prevchild) break;\n                i--;\n                cp--;\n            }\n            /* If we found a new subtree to explore in this node,\n             * go deeper following all the last children in order to\n             * find the key lexicographically greater. */\n            if (i != -1) {\n                debugf(\"SCAN found a new node\\n\");\n                /* Enter the node we just found. */\n                if (!raxIteratorAddChars(it,it->node->data+i,1)) return 0;\n                if (!raxStackPush(&it->stack,it->node)) return 0;\n                memcpy(&it->node,cp,sizeof(it->node));\n                /* Seek sub-tree max. */\n                if (!raxSeekGreatest(it)) return 0;\n            }\n        }\n\n        /* Return the key: this could be the key we found scanning a new\n         * subtree, or if we did not find a new subtree to explore here,\n         * before giving up with this node, check if it's a key itself. */\n        if (it->node->iskey) {\n            it->data = raxGetData(it->node);\n            return 1;\n        }\n    }\n}\n\n/* Seek an iterator at the specified element.\n * Return 0 if the seek failed for syntax error or out of memory. Otherwise\n * 1 is returned. When 0 is returned for out of memory, errno is set to\n * the ENOMEM value. */\nint raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {\n    int eq = 0, lt = 0, gt = 0, first = 0, last = 0;\n\n    it->stack.items = 0; /* Just resetting. Intialized by raxStart(). */\n    it->flags |= RAX_ITER_JUST_SEEKED;\n    it->flags &= ~RAX_ITER_EOF;\n    it->key_len = 0;\n    it->node = NULL;\n\n    /* Set flags according to the operator used to perform the seek. */\n    if (op[0] == '>') {\n        gt = 1;\n        if (op[1] == '=') eq = 1;\n    } else if (op[0] == '<') {\n        lt = 1;\n        if (op[1] == '=') eq = 1;\n    } else if (op[0] == '=') {\n        eq = 1;\n    } else if (op[0] == '^') {\n        first = 1;\n    } else if (op[0] == '$') {\n        last = 1;\n    } else {\n        errno = 0;\n        return 0; /* Error. */\n    }\n\n    /* If there are no elements, set the EOF condition immediately and\n     * return. */\n    if (it->rt->numele == 0) {\n        it->flags |= RAX_ITER_EOF;\n        return 1;\n    }\n\n    if (first) {\n        /* Seeking the first key greater or equal to the empty string\n         * is equivalent to seeking the smaller key available. */\n        return raxSeek(it,\">=\",NULL,0);\n    }\n\n    if (last) {\n        /* Find the greatest key taking always the last child till a\n         * final node is found. */\n        it->node = it->rt->head;\n        if (!raxSeekGreatest(it)) return 0;\n        assert(it->node->iskey);\n        it->data = raxGetData(it->node);\n        return 1;\n    }\n\n    /* We need to seek the specified key. What we do here is to actually\n     * perform a lookup, and later invoke the prev/next key code that\n     * we already use for iteration. */\n    int splitpos = 0;\n    size_t i = raxLowWalk(it->rt,ele,len,&it->node,NULL,&splitpos,&it->stack);\n\n    /* Return OOM on incomplete stack info. */\n    if (it->stack.oom) return 0;\n\n    if (eq && i == len && (!it->node->iscompr || splitpos == 0) &&\n        it->node->iskey)\n    {\n        /* We found our node, since the key matches and we have an\n         * \"equal\" condition. */\n        if (!raxIteratorAddChars(it,ele,len)) return 0; /* OOM. */\n        it->data = raxGetData(it->node);\n    } else if (lt || gt) {\n        /* Exact key not found or eq flag not set. We have to set as current\n         * key the one represented by the node we stopped at, and perform\n         * a next/prev operation to seek. To reconstruct the key at this node\n         * we start from the parent and go to the current node, accumulating\n         * the characters found along the way. */\n        if (!raxStackPush(&it->stack,it->node)) return 0;\n        for (size_t j = 1; j < it->stack.items; j++) {\n            raxNode *parent = it->stack.stack[j-1];\n            raxNode *child = it->stack.stack[j];\n            if (parent->iscompr) {\n                if (!raxIteratorAddChars(it,parent->data,parent->size))\n                    return 0;\n            } else {\n                raxNode **cp = raxNodeFirstChildPtr(parent);\n                unsigned char *p = parent->data;\n                while(1) {\n                    raxNode *aux;\n                    memcpy(&aux,cp,sizeof(aux));\n                    if (aux == child) break;\n                    cp++;\n                    p++;\n                }\n                if (!raxIteratorAddChars(it,p,1)) return 0;\n            }\n        }\n        raxStackPop(&it->stack);\n\n        /* We need to set the iterator in the correct state to call next/prev\n         * step in order to seek the desired element. */\n        debugf(\"After initial seek: i=%d len=%d key=%.*s\\n\",\n            (int)i, (int)len, (int)it->key_len, it->key);\n        if (i != len && !it->node->iscompr) {\n            /* If we stopped in the middle of a normal node because of a\n             * mismatch, add the mismatching character to the current key\n             * and call the iterator with the 'noup' flag so that it will try\n             * to seek the next/prev child in the current node directly based\n             * on the mismatching character. */\n            if (!raxIteratorAddChars(it,ele+i,1)) return 0;\n            debugf(\"Seek normal node on mismatch: %.*s\\n\",\n                (int)it->key_len, (char*)it->key);\n\n            it->flags &= ~RAX_ITER_JUST_SEEKED;\n            if (lt && !raxIteratorPrevStep(it,1)) return 0;\n            if (gt && !raxIteratorNextStep(it,1)) return 0;\n            it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */\n        } else if (i != len && it->node->iscompr) {\n            debugf(\"Compressed mismatch: %.*s\\n\",\n                (int)it->key_len, (char*)it->key);\n            /* In case of a mismatch within a compressed node. */\n            int nodechar = it->node->data[splitpos];\n            int keychar = ele[i];\n            it->flags &= ~RAX_ITER_JUST_SEEKED;\n            if (gt) {\n                /* If the key the compressed node represents is greater\n                 * than our seek element, continue forward, otherwise set the\n                 * state in order to go back to the next sub-tree. */\n                if (nodechar > keychar) {\n                    if (!raxIteratorNextStep(it,0)) return 0;\n                } else {\n                    if (!raxIteratorAddChars(it,it->node->data,it->node->size))\n                        return 0;\n                    if (!raxIteratorNextStep(it,1)) return 0;\n                }\n            }\n            if (lt) {\n                /* If the key the compressed node represents is smaller\n                 * than our seek element, seek the greater key in this\n                 * subtree, otherwise set the state in order to go back to\n                 * the previous sub-tree. */\n                if (nodechar < keychar) {\n                    if (!raxSeekGreatest(it)) return 0;\n                    it->data = raxGetData(it->node);\n                } else {\n                    if (!raxIteratorAddChars(it,it->node->data,it->node->size))\n                        return 0;\n                    if (!raxIteratorPrevStep(it,1)) return 0;\n                }\n            }\n            it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */\n        } else {\n            debugf(\"No mismatch: %.*s\\n\",\n                (int)it->key_len, (char*)it->key);\n            /* If there was no mismatch we are into a node representing the\n             * key, (but which is not a key or the seek operator does not\n             * include 'eq'), or we stopped in the middle of a compressed node\n             * after processing all the key. Continue iterating as this was\n             * a legitimate key we stopped at. */\n            it->flags &= ~RAX_ITER_JUST_SEEKED;\n            if (it->node->iscompr && it->node->iskey && splitpos && lt) {\n                /* If we stopped in the middle of a compressed node with\n                 * perfect match, and the condition is to seek a key \"<\" than\n                 * the specified one, then if this node is a key it already\n                 * represents our match. For instance we may have nodes:\n                 *\n                 * \"f\" -> \"oobar\" = 1 -> \"\" = 2\n                 *\n                 * Representing keys \"f\" = 1, \"foobar\" = 2. A seek for\n                 * the key < \"foo\" will stop in the middle of the \"oobar\"\n                 * node, but will be our match, representing the key \"f\".\n                 *\n                 * So in that case, we don't seek backward. */\n                it->data = raxGetData(it->node);\n            } else {\n                if (gt && !raxIteratorNextStep(it,0)) return 0;\n                if (lt && !raxIteratorPrevStep(it,0)) return 0;\n            }\n            it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */\n        }\n    } else {\n        /* If we are here just eq was set but no match was found. */\n        it->flags |= RAX_ITER_EOF;\n        return 1;\n    }\n    return 1;\n}\n\n/* Go to the next element in the scope of the iterator 'it'.\n * If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is\n * returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */\nint raxNext(raxIterator *it) {\n    if (!raxIteratorNextStep(it,0)) {\n        errno = ENOMEM;\n        return 0;\n    }\n    if (it->flags & RAX_ITER_EOF) {\n        errno = 0;\n        return 0;\n    }\n    return 1;\n}\n\n/* Go to the previous element in the scope of the iterator 'it'.\n * If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is\n * returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */\nint raxPrev(raxIterator *it) {\n    if (!raxIteratorPrevStep(it,0)) {\n        errno = ENOMEM;\n        return 0;\n    }\n    if (it->flags & RAX_ITER_EOF) {\n        errno = 0;\n        return 0;\n    }\n    return 1;\n}\n\n/* Perform a random walk starting in the current position of the iterator.\n * Return 0 if the tree is empty or on out of memory. Otherwise 1 is returned\n * and the iterator is set to the node reached after doing a random walk\n * of 'steps' steps. If the 'steps' argument is 0, the random walk is performed\n * using a random number of steps between 1 and two times the logarithm of\n * the number of elements.\n *\n * NOTE: if you use this function to generate random elements from the radix\n * tree, expect a disappointing distribution. A random walk produces good\n * random elements if the tree is not sparse, however in the case of a radix\n * tree certain keys will be reported much more often than others. At least\n * this function should be able to expore every possible element eventually. */\nint raxRandomWalk(raxIterator *it, size_t steps) {\n    if (it->rt->numele == 0) {\n        it->flags |= RAX_ITER_EOF;\n        return 0;\n    }\n\n    if (steps == 0) {\n        size_t fle = 1+floor(log(it->rt->numele));\n        fle *= 2;\n        steps = 1 + rand() % fle;\n    }\n\n    raxNode *n = it->node;\n    while(steps > 0 || !n->iskey) {\n        int numchildren = n->iscompr ? 1 : n->size;\n        int r = rand() % (numchildren+(n != it->rt->head));\n\n        if (r == numchildren) {\n            /* Go up to parent. */\n            n = raxStackPop(&it->stack);\n            int todel = n->iscompr ? n->size : 1;\n            raxIteratorDelChars(it,todel);\n        } else {\n            /* Select a random child. */\n            if (n->iscompr) {\n                if (!raxIteratorAddChars(it,n->data,n->size)) return 0;\n            } else {\n                if (!raxIteratorAddChars(it,n->data+r,1)) return 0;\n            }\n            raxNode **cp = raxNodeFirstChildPtr(n)+r;\n            if (!raxStackPush(&it->stack,n)) return 0;\n            memcpy(&n,cp,sizeof(n));\n        }\n        if (n->iskey) steps--;\n    }\n    it->node = n;\n    it->data = raxGetData(it->node);\n    return 1;\n}\n\n/* Compare the key currently pointed by the iterator to the specified\n * key according to the specified operator. Returns 1 if the comparison is\n * true, otherwise 0 is returned. */\nint raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len) {\n    int eq = 0, lt = 0, gt = 0;\n\n    if (op[0] == '=' || op[1] == '=') eq = 1;\n    if (op[0] == '>') gt = 1;\n    else if (op[0] == '<') lt = 1;\n    else if (op[1] != '=') return 0; /* Syntax error. */\n\n    size_t minlen = key_len < iter->key_len ? key_len : iter->key_len;\n    int cmp = memcmp(iter->key,key,minlen);\n\n    /* Handle == */\n    if (lt == 0 && gt == 0) return cmp == 0 && key_len == iter->key_len;\n\n    /* Handle >, >=, <, <= */\n    if (cmp == 0) {\n        /* Same prefix: longer wins. */\n        if (eq && key_len == iter->key_len) return 1;\n        else if (lt) return iter->key_len < key_len;\n        else if (gt) return iter->key_len > key_len;\n        else return 0; /* Avoid warning, just 'eq' is handled before. */\n    } else if (cmp > 0) {\n        return gt ? 1 : 0;\n    } else /* (cmp < 0) */ {\n        return lt ? 1 : 0;\n    }\n}\n\n/* Free the iterator. */\nvoid raxStop(raxIterator *it) {\n    if (it->key != it->key_static_string) rax_free(it->key);\n    raxStackFree(&it->stack);\n}\n\n/* Return if the iterator is in an EOF state. This happens when raxSeek()\n * failed to seek an appropriate element, so that raxNext() or raxPrev()\n * will return zero, or when an EOF condition was reached while iterating\n * with raxNext() and raxPrev(). */\nint raxEOF(raxIterator *it) {\n    return it->flags & RAX_ITER_EOF;\n}\n\n/* Return the number of elements inside the radix tree. */\nuint64_t raxSize(rax *rax) {\n    return rax->numele;\n}\n\n/* ----------------------------- Introspection ------------------------------ */\n\n/* This function is mostly used for debugging and learning purposes.\n * It shows an ASCII representation of a tree on standard output, outling\n * all the nodes and the contained keys.\n *\n * The representation is as follow:\n *\n *  \"foobar\" (compressed node)\n *  [abc] (normal node with three children)\n *  [abc]=0x12345678 (node is a key, pointing to value 0x12345678)\n *  [] (a normal empty node)\n *\n *  Children are represented in new idented lines, each children prefixed by\n *  the \"`-(x)\" string, where \"x\" is the edge byte.\n *\n *  [abc]\n *   `-(a) \"ladin\"\n *   `-(b) [kj]\n *   `-(c) []\n *\n *  However when a node has a single child the following representation\n *  is used instead:\n *\n *  [abc] -> \"ladin\" -> []\n */\n\n/* The actual implementation of raxShow(). */\nvoid raxRecursiveShow(int level, int lpad, raxNode *n) {\n    char s = n->iscompr ? '\"' : '[';\n    char e = n->iscompr ? '\"' : ']';\n\n    int numchars = printf(\"%c%.*s%c\", s, n->size, n->data, e);\n    if (n->iskey) {\n        numchars += printf(\"=%p\",raxGetData(n));\n    }\n\n    int numchildren = n->iscompr ? 1 : n->size;\n    /* Note that 7 and 4 magic constants are the string length\n     * of \" `-(x) \" and \" -> \" respectively. */\n    if (level) {\n        lpad += (numchildren > 1) ? 7 : 4;\n        if (numchildren == 1) lpad += numchars;\n    }\n    raxNode **cp = raxNodeFirstChildPtr(n);\n    for (int i = 0; i < numchildren; i++) {\n        char *branch = \" `-(%c) \";\n        if (numchildren > 1) {\n            printf(\"\\n\");\n            for (int j = 0; j < lpad; j++) putchar(' ');\n            printf(branch,n->data[i]);\n        } else {\n            printf(\" -> \");\n        }\n        raxNode *child;\n        memcpy(&child,cp,sizeof(child));\n        raxRecursiveShow(level+1,lpad,child);\n        cp++;\n    }\n}\n\n/* Show a tree, as outlined in the comment above. */\nvoid raxShow(rax *rax) {\n    raxRecursiveShow(0,0,rax->head);\n    putchar('\\n');\n}\n\n/* Used by debugnode() macro to show info about a given node. */\nvoid raxDebugShowNode(const char *msg, raxNode *n) {\n    if (raxDebugMsg == 0) return;\n    printf(\"%s: %p [%.*s] key:%d size:%d children:\",\n        msg, (void*)n, (int)n->size, (char*)n->data, n->iskey, n->size);\n    int numcld = n->iscompr ? 1 : n->size;\n    raxNode **cldptr = raxNodeLastChildPtr(n) - (numcld-1);\n    while(numcld--) {\n        raxNode *child;\n        memcpy(&child,cldptr,sizeof(child));\n        cldptr++;\n        printf(\"%p \", (void*)child);\n    }\n    printf(\"\\n\");\n    fflush(stdout);\n}\n\n/* Touch all the nodes of a tree returning a check sum. This is useful\n * in order to make Valgrind detect if there is something wrong while\n * reading the data structure.\n *\n * This function was used in order to identify Rax bugs after a big refactoring\n * using this technique:\n *\n * 1. The rax-test is executed using Valgrind, adding a printf() so that for\n *    the fuzz tester we see what iteration in the loop we are in.\n * 2. After every modification of the radix tree made by the fuzz tester\n *    in rax-test.c, we add a call to raxTouch().\n * 3. Now as soon as an operation will corrupt the tree, raxTouch() will\n *    detect it (via Valgrind) immediately. We can add more calls to narrow\n *    the state.\n * 4. At this point a good idea is to enable Rax debugging messages immediately\n *    before the moment the tree is corrupted, to see what happens.\n */\nunsigned long raxTouch(raxNode *n) {\n    debugf(\"Touching %p\\n\", (void*)n);\n    unsigned long sum = 0;\n    if (n->iskey) {\n        sum += (unsigned long)raxGetData(n);\n    }\n\n    int numchildren = n->iscompr ? 1 : n->size;\n    raxNode **cp = raxNodeFirstChildPtr(n);\n    int count = 0;\n    for (int i = 0; i < numchildren; i++) {\n        if (numchildren > 1) {\n            sum += (long)n->data[i];\n        }\n        raxNode *child;\n        memcpy(&child,cp,sizeof(child));\n        if (child == (void*)0x65d1760) count++;\n        if (count > 1) exit(1);\n        sum += raxTouch(child);\n        cp++;\n    }\n    return sum;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/rax.h",
    "content": "/* Rax -- A radix tree implementation.\n *\n * Copyright (c) 2017-2018, 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 RAX_H\n#define RAX_H\n\n#include <stdint.h>\n\n/* Representation of a radix tree as implemented in this file, that contains\n * the strings \"foo\", \"foobar\" and \"footer\" after the insertion of each\n * word. When the node represents a key inside the radix tree, we write it\n * between [], otherwise it is written between ().\n *\n * This is the vanilla representation:\n *\n *              (f) \"\"\n *                \\\n *                (o) \"f\"\n *                  \\\n *                  (o) \"fo\"\n *                    \\\n *                  [t   b] \"foo\"\n *                  /     \\\n *         \"foot\" (e)     (a) \"foob\"\n *                /         \\\n *      \"foote\" (r)         (r) \"fooba\"\n *              /             \\\n *    \"footer\" []             [] \"foobar\"\n *\n * However, this implementation implements a very common optimization where\n * successive nodes having a single child are \"compressed\" into the node\n * itself as a string of characters, each representing a next-level child,\n * and only the link to the node representing the last character node is\n * provided inside the representation. So the above representation is turend\n * into:\n *\n *                  [\"foo\"] \"\"\n *                     |\n *                  [t   b] \"foo\"\n *                  /     \\\n *        \"foot\" (\"er\")    (\"ar\") \"foob\"\n *                 /          \\\n *       \"footer\" []          [] \"foobar\"\n *\n * However this optimization makes the implementation a bit more complex.\n * For instance if a key \"first\" is added in the above radix tree, a\n * \"node splitting\" operation is needed, since the \"foo\" prefix is no longer\n * composed of nodes having a single child one after the other. This is the\n * above tree and the resulting node splitting after this event happens:\n *\n *\n *                    (f) \"\"\n *                    /\n *                 (i o) \"f\"\n *                 /   \\\n *    \"firs\"  (\"rst\")  (o) \"fo\"\n *              /        \\\n *    \"first\" []       [t   b] \"foo\"\n *                     /     \\\n *           \"foot\" (\"er\")    (\"ar\") \"foob\"\n *                    /          \\\n *          \"footer\" []          [] \"foobar\"\n *\n * Similarly after deletion, if a new chain of nodes having a single child\n * is created (the chain must also not include nodes that represent keys),\n * it must be compressed back into a single node.\n *\n */\n\n#define RAX_NODE_MAX_SIZE ((1<<29)-1)\ntypedef struct raxNode {\n    uint32_t iskey:1;     /* Does this node contain a key? */\n    uint32_t isnull:1;    /* Associated value is NULL (don't store it). */\n    uint32_t iscompr:1;   /* Node is compressed. */\n    uint32_t size:29;     /* Number of children, or compressed string len. */\n    /* Data layout is as follows:\n     *\n     * If node is not compressed we have 'size' bytes, one for each children\n     * character, and 'size' raxNode pointers, point to each child node.\n     * Note how the character is not stored in the children but in the\n     * edge of the parents:\n     *\n     * [header iscompr=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?)\n     *\n     * if node is compressed (iscompr bit is 1) the node has 1 children.\n     * In that case the 'size' bytes of the string stored immediately at\n     * the start of the data section, represent a sequence of successive\n     * nodes linked one after the other, for which only the last one in\n     * the sequence is actually represented as a node, and pointed to by\n     * the current compressed node.\n     *\n     * [header iscompr=1][xyz][z-ptr](value-ptr?)\n     *\n     * Both compressed and not compressed nodes can represent a key\n     * with associated data in the radix tree at any level (not just terminal\n     * nodes).\n     *\n     * If the node has an associated key (iskey=1) and is not NULL\n     * (isnull=0), then after the raxNode pointers poiting to the\n     * children, an additional value pointer is present (as you can see\n     * in the representation above as \"value-ptr\" field).\n     */\n    unsigned char data[];\n} raxNode;\n\ntypedef struct rax {\n    raxNode *head;\n    uint64_t numele;\n    uint64_t numnodes;\n} rax;\n\n/* Stack data structure used by raxLowWalk() in order to, optionally, return\n * a list of parent nodes to the caller. The nodes do not have a \"parent\"\n * field for space concerns, so we use the auxiliary stack when needed. */\n#define RAX_STACK_STATIC_ITEMS 32\ntypedef struct raxStack {\n    void **stack; /* Points to static_items or an heap allocated array. */\n    size_t items, maxitems; /* Number of items contained and total space. */\n    /* Up to RAXSTACK_STACK_ITEMS items we avoid to allocate on the heap\n     * and use this static array of pointers instead. */\n    void *static_items[RAX_STACK_STATIC_ITEMS];\n    int oom; /* True if pushing into this stack failed for OOM at some point. */\n} raxStack;\n\n/* Optional callback used for iterators and be notified on each rax node,\n * including nodes not representing keys. If the callback returns true\n * the callback changed the node pointer in the iterator structure, and the\n * iterator implementation will have to replace the pointer in the radix tree\n * internals. This allows the callback to reallocate the node to perform\n * very special operations, normally not needed by normal applications.\n *\n * This callback is used to perform very low level analysis of the radix tree\n * structure, scanning each possible node (but the root node), or in order to\n * reallocate the nodes to reduce the allocation fragmentation (this is the\n * Redis application for this callback).\n *\n * This is currently only supported in forward iterations (raxNext) */\ntypedef int (*raxNodeCallback)(raxNode **noderef);\n\n/* Radix tree iterator state is encapsulated into this data structure. */\n#define RAX_ITER_STATIC_LEN 128\n#define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current\n                                       element for the first iteration and\n                                       clear the flag. */\n#define RAX_ITER_EOF (1<<1)    /* End of iteration reached. */\n#define RAX_ITER_SAFE (1<<2)   /* Safe iterator, allows operations while\n                                  iterating. But it is slower. */\ntypedef struct raxIterator {\n    int flags;\n    rax *rt;                /* Radix tree we are iterating. */\n    unsigned char *key;     /* The current string. */\n    void *data;             /* Data associated to this key. */\n    size_t key_len;         /* Current key length. */\n    size_t key_max;         /* Max key len the current key buffer can hold. */\n    unsigned char key_static_string[RAX_ITER_STATIC_LEN];\n    raxNode *node;          /* Current node. Only for unsafe iteration. */\n    raxStack stack;         /* Stack used for unsafe iteration. */\n    raxNodeCallback node_cb; /* Optional node callback. Normally set to NULL. */\n} raxIterator;\n\n/* A special pointer returned for not found items. */\nextern void *raxNotFound;\n\n/* Exported API. */\nrax *raxNew(void);\nint raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);\nint raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);\nint raxRemove(rax *rax, unsigned char *s, size_t len, void **old);\nvoid *raxFind(rax *rax, unsigned char *s, size_t len);\nvoid raxFree(rax *rax);\nvoid raxFreeWithCallback(rax *rax, void (*free_callback)(void*));\nvoid raxStart(raxIterator *it, rax *rt);\nint raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len);\nint raxNext(raxIterator *it);\nint raxPrev(raxIterator *it);\nint raxRandomWalk(raxIterator *it, size_t steps);\nint raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len);\nvoid raxStop(raxIterator *it);\nint raxEOF(raxIterator *it);\nvoid raxShow(rax *rax);\nuint64_t raxSize(rax *rax);\nunsigned long raxTouch(raxNode *n);\nvoid raxSetDebugMsg(int onoff);\n\n/* Internal API. May be used by the node callback in order to access rax nodes\n * in a low level way, so this function is exported as well. */\nvoid raxSetData(raxNode *n, void *data);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/rax_malloc.h",
    "content": "/* Rax -- A radix tree implementation.\n *\n * Copyright (c) 2017, 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/* Allocator selection.\n *\n * This file is used in order to change the Rax allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#ifndef RAX_ALLOC_H\n#define RAX_ALLOC_H\n#include \"zmalloc.h\"\n#define rax_malloc zmalloc\n#define rax_realloc zrealloc\n#define rax_free zfree\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"lzf.h\"    /* LZF compression library */\n#include \"zipmap.h\"\n#include \"endianconv.h\"\n#include \"stream.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#include <sys/param.h>\n\n/* This macro is called when the internal RDB stracture is corrupt */\n#define rdbExitReportCorruptRDB(...) rdbReportError(1, __LINE__,__VA_ARGS__)\n/* This macro is called when RDB read failed (possibly a short read) */\n#define rdbReportReadError(...) rdbReportError(0, __LINE__,__VA_ARGS__)\n\nchar* rdbFileBeingLoaded = NULL; /* used for rdb checking on read error */\nextern int rdbCheckMode;\nvoid rdbCheckError(const char *fmt, ...);\nvoid rdbCheckSetError(const char *fmt, ...);\n\nvoid rdbReportError(int corruption_error, int linenum, char *reason, ...) {\n    va_list ap;\n    char msg[1024];\n    int len;\n\n    len = snprintf(msg,sizeof(msg),\n        \"Internal error in RDB reading offset %llu, function at rdb.c:%d -> \",\n        (unsigned long long)server.loading_loaded_bytes, linenum);\n    va_start(ap,reason);\n    vsnprintf(msg+len,sizeof(msg)-len,reason,ap);\n    va_end(ap);\n\n    if (!rdbCheckMode) {\n        if (rdbFileBeingLoaded || corruption_error) {\n            serverLog(LL_WARNING, \"%s\", msg);\n            char *argv[2] = {\"\",rdbFileBeingLoaded};\n            redis_check_rdb_main(2,argv,NULL);\n        } else {\n            serverLog(LL_WARNING, \"%s. Failure loading rdb format from socket, assuming connection error, resuming operation.\", msg);\n            return;\n        }\n    } else {\n        rdbCheckError(\"%s\",msg);\n    }\n    serverLog(LL_WARNING, \"Terminating server after rdb file reading failure.\");\n    exit(1);\n}\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\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 * 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. */\nint rdbLoadType(rio *rdb) {\n    unsigned char type;\n    if (rioRead(rdb,&type,1) == 0) return -1;\n    return type;\n}\n\n/* This is only used to load old databases stored with the RDB_OPCODE_EXPIRETIME\n * opcode. New versions of Redis store using the RDB_OPCODE_EXPIRETIME_MS\n * opcode. On error -1 is returned, however this could be a valid time, so\n * to check for loading errors the caller should call rioGetReadError() after\n * calling this function. */\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\nint rdbSaveMillisecondTime(rio *rdb, long long t) {\n    int64_t t64 = (int64_t) t;\n    memrev64ifbe(&t64); /* Store in little endian. */\n    return rdbWriteRaw(rdb,&t64,8);\n}\n\n/* This function loads a time from the RDB file. It gets the version of the\n * RDB because, unfortunately, before Redis 5 (RDB version 9), the function\n * failed to convert data to/from little endian, so RDB files with keys having\n * expires could not be shared between big endian and little endian systems\n * (because the expire time will be totally wrong). The fix for this is just\n * to call memrev64ifbe(), however if we fix this for all the RDB versions,\n * this call will introduce an incompatibility for big endian systems:\n * after upgrading to Redis version 5 they will no longer be able to load their\n * own old RDB files. Because of that, we instead fix the function only for new\n * RDB versions, and load older RDB versions as we used to do in the past,\n * allowing big endian systems to load their own old RDB files.\n *\n * On I/O error the function returns LLONG_MAX, however if this is also a\n * valid stored value, the caller should use rioGetReadError() to check for\n * errors after calling this function. */\nlong long rdbLoadMillisecondTime(rio *rdb, int rdbver) {\n    int64_t t64;\n    if (rioRead(rdb,&t64,8) == 0) return LLONG_MAX;\n    if (rdbver >= 9) /* Check the top comment of this function. */\n        memrev64ifbe(&t64); /* Convert in big endian if the system is BE. */\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 RDB_* definitions for more information\n * on the types of encoding. */\nint rdbSaveLen(rio *rdb, uint64_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)|(RDB_6BITLEN<<6);\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        nwritten = 1;\n    } else if (len < (1<<14)) {\n        /* Save a 14 bit len */\n        buf[0] = ((len>>8)&0xFF)|(RDB_14BITLEN<<6);\n        buf[1] = len&0xFF;\n        if (rdbWriteRaw(rdb,buf,2) == -1) return -1;\n        nwritten = 2;\n    } else if (len <= UINT32_MAX) {\n        /* Save a 32 bit len */\n        buf[0] = RDB_32BITLEN;\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        uint32_t len32 = htonl(len);\n        if (rdbWriteRaw(rdb,&len32,4) == -1) return -1;\n        nwritten = 1+4;\n    } else {\n        /* Save a 64 bit len */\n        buf[0] = RDB_64BITLEN;\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        len = htonu64(len);\n        if (rdbWriteRaw(rdb,&len,8) == -1) return -1;\n        nwritten = 1+8;\n    }\n    return nwritten;\n}\n\n\n/* Load an encoded length. If the loaded length is a normal length as stored\n * with rdbSaveLen(), the read length is set to '*lenptr'. If instead the\n * loaded length describes a special encoding that follows, then '*isencoded'\n * is set to 1 and the encoding format is stored at '*lenptr'.\n *\n * See the RDB_ENC_* definitions in rdb.h for more information on special\n * encodings.\n *\n * The function returns -1 on error, 0 on success. */\nint rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr) {\n    unsigned char buf[2];\n    int type;\n\n    if (isencoded) *isencoded = 0;\n    if (rioRead(rdb,buf,1) == 0) return -1;\n    type = (buf[0]&0xC0)>>6;\n    if (type == RDB_ENCVAL) {\n        /* Read a 6 bit encoding type. */\n        if (isencoded) *isencoded = 1;\n        *lenptr = buf[0]&0x3F;\n    } else if (type == RDB_6BITLEN) {\n        /* Read a 6 bit len. */\n        *lenptr = buf[0]&0x3F;\n    } else if (type == RDB_14BITLEN) {\n        /* Read a 14 bit len. */\n        if (rioRead(rdb,buf+1,1) == 0) return -1;\n        *lenptr = ((buf[0]&0x3F)<<8)|buf[1];\n    } else if (buf[0] == RDB_32BITLEN) {\n        /* Read a 32 bit len. */\n        uint32_t len;\n        if (rioRead(rdb,&len,4) == 0) return -1;\n        *lenptr = ntohl(len);\n    } else if (buf[0] == RDB_64BITLEN) {\n        /* Read a 64 bit len. */\n        uint64_t len;\n        if (rioRead(rdb,&len,8) == 0) return -1;\n        *lenptr = ntohu64(len);\n    } else {\n        rdbExitReportCorruptRDB(\n            \"Unknown length encoding %d in rdbLoadLen()\",type);\n        return -1; /* Never reached. */\n    }\n    return 0;\n}\n\n/* This is like rdbLoadLenByRef() but directly returns the value read\n * from the RDB stream, signaling an error by returning RDB_LENERR\n * (since it is a too large count to be applicable in any Redis data\n * structure). */\nuint64_t rdbLoadLen(rio *rdb, int *isencoded) {\n    uint64_t len;\n\n    if (rdbLoadLenByRef(rdb,isencoded,&len) == -1) return RDB_LENERR;\n    return len;\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. */\nint rdbEncodeInteger(long long value, unsigned char *enc) {\n    if (value >= -(1<<7) && value <= (1<<7)-1) {\n        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT8;\n        enc[1] = value&0xFF;\n        return 2;\n    } else if (value >= -(1<<15) && value <= (1<<15)-1) {\n        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT16;\n        enc[1] = value&0xFF;\n        enc[2] = (value>>8)&0xFF;\n        return 3;\n    } else if (value >= -((long long)1<<31) && value <= ((long long)1<<31)-1) {\n        enc[0] = (RDB_ENCVAL<<6)|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    } else {\n        return 0;\n    }\n}\n\n/* Loads an integer-encoded object with the specified encoding type \"enctype\".\n * The returned value changes according to the flags, see\n * rdbGenericLoadStringObject() for more info. */\nvoid *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr) {\n    int plain = flags & RDB_LOAD_PLAIN;\n    int sds = flags & RDB_LOAD_SDS;\n    int encode = flags & RDB_LOAD_ENC;\n    unsigned char enc[4];\n    long long val;\n\n    if (enctype == RDB_ENC_INT8) {\n        if (rioRead(rdb,enc,1) == 0) return NULL;\n        val = (signed char)enc[0];\n    } else if (enctype == 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 == 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        rdbExitReportCorruptRDB(\"Unknown RDB integer encoding type %d\",enctype);\n        return NULL; /* Never reached. */\n    }\n    if (plain || sds) {\n        char buf[LONG_STR_SIZE], *p;\n        int len = ll2string(buf,sizeof(buf),val);\n        if (lenptr) *lenptr = len;\n        p = plain ? zmalloc(len) : sdsnewlen(SDS_NOINIT,len);\n        memcpy(p,buf,len);\n        return p;\n    } else if (encode) {\n        return createStringObjectFromLongLongForValue(val);\n    } else {\n        return createObject(OBJ_STRING,sdsfromlonglong(val));\n    }\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 */\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    value = strtoll(s, &endptr, 10);\n    if (endptr[0] != '\\0') return 0;\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    if (strlen(buf) != len || memcmp(buf,s,len)) return 0;\n\n    return rdbEncodeInteger(value,enc);\n}\n\nssize_t rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,\n                       size_t original_len) {\n    unsigned char byte;\n    ssize_t n, nwritten = 0;\n\n    /* Data compressed! Let's save it on disk */\n    byte = (RDB_ENCVAL<<6)|RDB_ENC_LZF;\n    if ((n = rdbWriteRaw(rdb,&byte,1)) == -1) goto writeerr;\n    nwritten += n;\n\n    if ((n = rdbSaveLen(rdb,compress_len)) == -1) goto writeerr;\n    nwritten += n;\n\n    if ((n = rdbSaveLen(rdb,original_len)) == -1) goto writeerr;\n    nwritten += n;\n\n    if ((n = rdbWriteRaw(rdb,data,compress_len)) == -1) goto writeerr;\n    nwritten += n;\n\n    return nwritten;\n\nwriteerr:\n    return -1;\n}\n\nssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {\n    size_t comprlen, outlen;\n    void *out;\n\n    /* We require at least four bytes compression for this to be worth it */\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    ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);\n    zfree(out);\n    return nwritten;\n}\n\n/* Load an LZF compressed string in RDB format. The returned value\n * changes according to 'flags'. For more info check the\n * rdbGenericLoadStringObject() function. */\nvoid *rdbLoadLzfStringObject(rio *rdb, int flags, size_t *lenptr) {\n    int plain = flags & RDB_LOAD_PLAIN;\n    int sds = flags & RDB_LOAD_SDS;\n    uint64_t len, clen;\n    unsigned char *c = NULL;\n    char *val = NULL;\n\n    if ((clen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n    if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n    if ((c = zmalloc(clen)) == NULL) goto err;\n\n    /* Allocate our target according to the uncompressed size. */\n    if (plain) {\n        val = zmalloc(len);\n    } else {\n        val = sdsnewlen(SDS_NOINIT,len);\n    }\n    if (lenptr) *lenptr = len;\n\n    /* Load the compressed representation and uncompress it to target. */\n    if (rioRead(rdb,c,clen) == 0) goto err;\n    if (lzf_decompress(c,clen,val,len) == 0) {\n        rdbExitReportCorruptRDB(\"Invalid LZF compressed string\");\n    }\n    zfree(c);\n\n    if (plain || sds) {\n        return val;\n    } else {\n        return createObject(OBJ_STRING,val);\n    }\nerr:\n    zfree(c);\n    if (plain)\n        zfree(val);\n    else\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 */\nssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {\n    int enclen;\n    ssize_t n, nwritten = 0;\n\n    /* Try integer encoding */\n    if (len <= 11) {\n        unsigned char buf[5];\n        if ((enclen = rdbTryIntegerEncoding((char*)s,len,buf)) > 0) {\n            if (rdbWriteRaw(rdb,buf,enclen) == -1) return -1;\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    if (server.rdb_compression && len > 20) {\n        n = rdbSaveLzfStringObject(rdb,s,len);\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    /* Store verbatim */\n    if ((n = rdbSaveLen(rdb,len)) == -1) return -1;\n    nwritten += n;\n    if (len > 0) {\n        if (rdbWriteRaw(rdb,s,len) == -1) return -1;\n        nwritten += len;\n    }\n    return nwritten;\n}\n\n/* Save a long long value as either an encoded string or a string. */\nssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) {\n    unsigned char buf[32];\n    ssize_t n, nwritten = 0;\n    int enclen = rdbEncodeInteger(value,buf);\n    if (enclen > 0) {\n        return rdbWriteRaw(rdb,buf,enclen);\n    } else {\n        /* Encode as string */\n        enclen = ll2string((char*)buf,32,value);\n        serverAssert(enclen < 32);\n        if ((n = rdbSaveLen(rdb,enclen)) == -1) return -1;\n        nwritten += n;\n        if ((n = rdbWriteRaw(rdb,buf,enclen)) == -1) return -1;\n        nwritten += n;\n    }\n    return nwritten;\n}\n\n/* Like rdbSaveRawString() gets a Redis object instead. */\nssize_t rdbSaveStringObject(rio *rdb, robj *obj) {\n    /* Avoid to decode the object, then encode it again, if the\n     * object is already integer encoded. */\n    if (obj->encoding == OBJ_ENCODING_INT) {\n        return rdbSaveLongLongAsStringObject(rdb,(long)obj->ptr);\n    } else {\n        serverAssertWithInfo(NULL,obj,sdsEncodedObject(obj));\n        return rdbSaveRawString(rdb,obj->ptr,sdslen(obj->ptr));\n    }\n}\n\n/* Load a string object from an RDB file according to flags:\n *\n * RDB_LOAD_NONE (no flags): load an RDB object, unencoded.\n * RDB_LOAD_ENC: If the returned type is a Redis object, try to\n *               encode it in a special way to be more memory\n *               efficient. When this flag is passed the function\n *               no longer guarantees that obj->ptr is an SDS string.\n * RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc()\n *                 instead of a Redis object with an sds in it.\n * RDB_LOAD_SDS: Return an SDS string instead of a Redis object.\n *\n * On I/O error NULL is returned.\n */\nvoid *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr) {\n    int encode = flags & RDB_LOAD_ENC;\n    int plain = flags & RDB_LOAD_PLAIN;\n    int sds = flags & RDB_LOAD_SDS;\n    int isencoded;\n    uint64_t len;\n\n    len = rdbLoadLen(rdb,&isencoded);\n    if (isencoded) {\n        switch(len) {\n        case RDB_ENC_INT8:\n        case RDB_ENC_INT16:\n        case RDB_ENC_INT32:\n            return rdbLoadIntegerObject(rdb,len,flags,lenptr);\n        case RDB_ENC_LZF:\n            return rdbLoadLzfStringObject(rdb,flags,lenptr);\n        default:\n            rdbExitReportCorruptRDB(\"Unknown RDB string encoding type %d\",len);\n            return NULL; /* Never reached. */\n        }\n    }\n\n    if (len == RDB_LENERR) return NULL;\n    if (plain || sds) {\n        void *buf = plain ? zmalloc(len) : sdsnewlen(SDS_NOINIT,len);\n        if (lenptr) *lenptr = len;\n        if (len && rioRead(rdb,buf,len) == 0) {\n            if (plain)\n                zfree(buf);\n            else\n                sdsfree(buf);\n            return NULL;\n        }\n        return buf;\n    } else {\n        robj *o = encode ? createStringObject(SDS_NOINIT,len) :\n                           createRawStringObject(SDS_NOINIT,len);\n        if (len && rioRead(rdb,o->ptr,len) == 0) {\n            decrRefCount(o);\n            return NULL;\n        }\n        return o;\n    }\n}\n\nrobj *rdbLoadStringObject(rio *rdb) {\n    return rdbGenericLoadStringObject(rdb,RDB_LOAD_NONE,NULL);\n}\n\nrobj *rdbLoadEncodedStringObject(rio *rdb) {\n    return rdbGenericLoadStringObject(rdb,RDB_LOAD_ENC,NULL);\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 * This 8 bit integer has special values in order to specify the following\n * conditions:\n * 253: not a number\n * 254: + inf\n * 255: - inf\n */\nint rdbSaveDoubleValue(rio *rdb, double val) {\n    unsigned char buf[128];\n    int len;\n\n    if (isnan(val)) {\n        buf[0] = 253;\n        len = 1;\n    } else if (!isfinite(val)) {\n        len = 1;\n        buf[0] = (val < 0) ? 255 : 254;\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    return rdbWriteRaw(rdb,buf,len);\n}\n\n/* For information about double serialization check rdbSaveDoubleValue() */\nint rdbLoadDoubleValue(rio *rdb, double *val) {\n    char buf[256];\n    unsigned char len;\n\n    if (rioRead(rdb,&len,1) == 0) return -1;\n    switch(len) {\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    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/* Saves a double for RDB 8 or greater, where IE754 binary64 format is assumed.\n * We just make sure the integer is always stored in little endian, otherwise\n * the value is copied verbatim from memory to disk.\n *\n * Return -1 on error, the size of the serialized value on success. */\nint rdbSaveBinaryDoubleValue(rio *rdb, double val) {\n    memrev64ifbe(&val);\n    return rdbWriteRaw(rdb,&val,sizeof(val));\n}\n\n/* Loads a double from RDB 8 or greater. See rdbSaveBinaryDoubleValue() for\n * more info. On error -1 is returned, otherwise 0. */\nint rdbLoadBinaryDoubleValue(rio *rdb, double *val) {\n    if (rioRead(rdb,val,sizeof(*val)) == 0) return -1;\n    memrev64ifbe(val);\n    return 0;\n}\n\n/* Like rdbSaveBinaryDoubleValue() but single precision. */\nint rdbSaveBinaryFloatValue(rio *rdb, float val) {\n    memrev32ifbe(&val);\n    return rdbWriteRaw(rdb,&val,sizeof(val));\n}\n\n/* Like rdbLoadBinaryDoubleValue() but single precision. */\nint rdbLoadBinaryFloatValue(rio *rdb, float *val) {\n    if (rioRead(rdb,val,sizeof(*val)) == 0) return -1;\n    memrev32ifbe(val);\n    return 0;\n}\n\n/* Save the object type of object \"o\". */\nint rdbSaveObjectType(rio *rdb, robj *o) {\n    switch (o->type) {\n    case OBJ_STRING:\n        return rdbSaveType(rdb,RDB_TYPE_STRING);\n    case OBJ_LIST:\n        if (o->encoding == OBJ_ENCODING_QUICKLIST)\n            return rdbSaveType(rdb,RDB_TYPE_LIST_QUICKLIST);\n        else\n            serverPanic(\"Unknown list encoding\");\n    case OBJ_SET:\n        if (o->encoding == OBJ_ENCODING_INTSET)\n            return rdbSaveType(rdb,RDB_TYPE_SET_INTSET);\n        else if (o->encoding == OBJ_ENCODING_HT)\n            return rdbSaveType(rdb,RDB_TYPE_SET);\n        else\n            serverPanic(\"Unknown set encoding\");\n    case OBJ_ZSET:\n        if (o->encoding == OBJ_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,RDB_TYPE_ZSET_ZIPLIST);\n        else if (o->encoding == OBJ_ENCODING_SKIPLIST)\n            return rdbSaveType(rdb,RDB_TYPE_ZSET_2);\n        else\n            serverPanic(\"Unknown sorted set encoding\");\n    case OBJ_HASH:\n        if (o->encoding == OBJ_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,RDB_TYPE_HASH_ZIPLIST);\n        else if (o->encoding == OBJ_ENCODING_HT)\n            return rdbSaveType(rdb,RDB_TYPE_HASH);\n        else\n            serverPanic(\"Unknown hash encoding\");\n    case OBJ_STREAM:\n        return rdbSaveType(rdb,RDB_TYPE_STREAM_LISTPACKS);\n    case OBJ_MODULE:\n        return rdbSaveType(rdb,RDB_TYPE_MODULE_2);\n    default:\n        serverPanic(\"Unknown object type\");\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/* This helper function serializes a consumer group Pending Entries List (PEL)\n * into the RDB file. The 'nacks' argument tells the function if also persist\n * the informations about the not acknowledged message, or if to persist\n * just the IDs: this is useful because for the global consumer group PEL\n * we serialized the NACKs as well, but when serializing the local consumer\n * PELs we just add the ID, that will be resolved inside the global PEL to\n * put a reference to the same structure. */\nssize_t rdbSaveStreamPEL(rio *rdb, rax *pel, int nacks) {\n    ssize_t n, nwritten = 0;\n\n    /* Number of entries in the PEL. */\n    if ((n = rdbSaveLen(rdb,raxSize(pel))) == -1) return -1;\n    nwritten += n;\n\n    /* Save each entry. */\n    raxIterator ri;\n    raxStart(&ri,pel);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        /* We store IDs in raw form as 128 big big endian numbers, like\n         * they are inside the radix tree key. */\n        if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) return -1;\n        nwritten += n;\n\n        if (nacks) {\n            streamNACK *nack = ri.data;\n            if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1)\n                return -1;\n            nwritten += n;\n            if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) return -1;\n            nwritten += n;\n            /* We don't save the consumer name: we'll save the pending IDs\n             * for each consumer in the consumer PEL, and resolve the consumer\n             * at loading time. */\n        }\n    }\n    raxStop(&ri);\n    return nwritten;\n}\n\n/* Serialize the consumers of a stream consumer group into the RDB. Helper\n * function for the stream data type serialization. What we do here is to\n * persist the consumer metadata, and it's PEL, for each consumer. */\nsize_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {\n    ssize_t n, nwritten = 0;\n\n    /* Number of consumers in this consumer group. */\n    if ((n = rdbSaveLen(rdb,raxSize(cg->consumers))) == -1) return -1;\n    nwritten += n;\n\n    /* Save each consumer. */\n    raxIterator ri;\n    raxStart(&ri,cg->consumers);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        streamConsumer *consumer = ri.data;\n\n        /* Consumer name. */\n        if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;\n        nwritten += n;\n\n        /* Last seen time. */\n        if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1)\n            return -1;\n        nwritten += n;\n\n        /* Consumer PEL, without the ACKs (see last parameter of the function\n         * passed with value of 0), at loading time we'll lookup the ID\n         * in the consumer group global PEL and will put a reference in the\n         * consumer local PEL. */\n        if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1)\n            return -1;\n        nwritten += n;\n    }\n    raxStop(&ri);\n    return nwritten;\n}\n\n/* Save a Redis object.\n * Returns -1 on error, number of bytes written on success. */\nssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) {\n    ssize_t n = 0, nwritten = 0;\n\n    if (o->type == OBJ_STRING) {\n        /* Save a string value */\n        if ((n = rdbSaveStringObject(rdb,o)) == -1) return -1;\n        nwritten += n;\n    } else if (o->type == OBJ_LIST) {\n        /* Save a list value */\n        if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n            quicklist *ql = o->ptr;\n            quicklistNode *node = ql->head;\n\n            if ((n = rdbSaveLen(rdb,ql->len)) == -1) return -1;\n            nwritten += n;\n\n            while(node) {\n                if (quicklistNodeIsCompressed(node)) {\n                    void *data;\n                    size_t compress_len = quicklistGetLzf(node, &data);\n                    if ((n = rdbSaveLzfBlob(rdb,data,compress_len,node->sz)) == -1) return -1;\n                    nwritten += n;\n                } else {\n                    if ((n = rdbSaveRawString(rdb,node->zl,node->sz)) == -1) return -1;\n                    nwritten += n;\n                }\n                node = node->next;\n            }\n        } else {\n            serverPanic(\"Unknown list encoding\");\n        }\n    } else if (o->type == OBJ_SET) {\n        /* Save a set value */\n        if (o->encoding == OBJ_ENCODING_HT) {\n            dict *set = o->ptr;\n            dictIterator *di = dictGetIterator(set);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize(set))) == -1) {\n                dictReleaseIterator(di);\n                return -1;\n            }\n            nwritten += n;\n\n            while((de = dictNext(di)) != NULL) {\n                sds ele = dictGetKey(de);\n                if ((n = rdbSaveRawString(rdb,(unsigned char*)ele,sdslen(ele)))\n                    == -1)\n                {\n                    dictReleaseIterator(di);\n                    return -1;\n                }\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n        } else if (o->encoding == OBJ_ENCODING_INTSET) {\n            size_t l = intsetBlobLen((intset*)o->ptr);\n\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (o->type == OBJ_ZSET) {\n        /* Save a sorted set value */\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = o->ptr;\n            zskiplist *zsl = zs->zsl;\n\n            if ((n = rdbSaveLen(rdb,zsl->length)) == -1) return -1;\n            nwritten += n;\n\n            /* We save the skiplist elements from the greatest to the smallest\n             * (that's trivial since the elements are already ordered in the\n             * skiplist): this improves the load process, since the next loaded\n             * element will always be the smaller, so adding to the skiplist\n             * will always immediately stop at the head, making the insertion\n             * O(1) instead of O(log(N)). */\n            zskiplistNode *zn = zsl->tail;\n            while (zn != NULL) {\n                if ((n = rdbSaveRawString(rdb,\n                    (unsigned char*)zn->ele,sdslen(zn->ele))) == -1)\n                {\n                    return -1;\n                }\n                nwritten += n;\n                if ((n = rdbSaveBinaryDoubleValue(rdb,zn->score)) == -1)\n                    return -1;\n                nwritten += n;\n                zn = zn->backward;\n            }\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (o->type == OBJ_HASH) {\n        /* Save a hash value */\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n\n        } else if (o->encoding == OBJ_ENCODING_HT) {\n            dictIterator *di = dictGetIterator(o->ptr);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize((dict*)o->ptr))) == -1) {\n                dictReleaseIterator(di);\n                return -1;\n            }\n            nwritten += n;\n\n            while((de = dictNext(di)) != NULL) {\n                sds field = dictGetKey(de);\n                sds value = dictGetVal(de);\n\n                if ((n = rdbSaveRawString(rdb,(unsigned char*)field,\n                        sdslen(field))) == -1)\n                {\n                    dictReleaseIterator(di);\n                    return -1;\n                }\n                nwritten += n;\n                if ((n = rdbSaveRawString(rdb,(unsigned char*)value,\n                        sdslen(value))) == -1)\n                {\n                    dictReleaseIterator(di);\n                    return -1;\n                }\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n        } else {\n            serverPanic(\"Unknown hash encoding\");\n        }\n    } else if (o->type == OBJ_STREAM) {\n        /* Store how many listpacks we have inside the radix tree. */\n        stream *s = o->ptr;\n        rax *rax = s->rax;\n        if ((n = rdbSaveLen(rdb,raxSize(rax))) == -1) return -1;\n        nwritten += n;\n\n        /* Serialize all the listpacks inside the radix tree as they are,\n         * when loading back, we'll use the first entry of each listpack\n         * to insert it back into the radix tree. */\n        raxIterator ri;\n        raxStart(&ri,rax);\n        raxSeek(&ri,\"^\",NULL,0);\n        while (raxNext(&ri)) {\n            unsigned char *lp = ri.data;\n            size_t lp_bytes = lpBytes(lp);\n            if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;\n            nwritten += n;\n            if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) return -1;\n            nwritten += n;\n        }\n        raxStop(&ri);\n\n        /* Save the number of elements inside the stream. We cannot obtain\n         * this easily later, since our macro nodes should be checked for\n         * number of items: not a great CPU / space tradeoff. */\n        if ((n = rdbSaveLen(rdb,s->length)) == -1) return -1;\n        nwritten += n;\n        /* Save the last entry ID. */\n        if ((n = rdbSaveLen(rdb,s->last_id.ms)) == -1) return -1;\n        nwritten += n;\n        if ((n = rdbSaveLen(rdb,s->last_id.seq)) == -1) return -1;\n        nwritten += n;\n\n        /* The consumer groups and their clients are part of the stream\n         * type, so serialize every consumer group. */\n\n        /* Save the number of groups. */\n        size_t num_cgroups = s->cgroups ? raxSize(s->cgroups) : 0;\n        if ((n = rdbSaveLen(rdb,num_cgroups)) == -1) return -1;\n        nwritten += n;\n\n        if (num_cgroups) {\n            /* Serialize each consumer group. */\n            raxStart(&ri,s->cgroups);\n            raxSeek(&ri,\"^\",NULL,0);\n            while(raxNext(&ri)) {\n                streamCG *cg = ri.data;\n\n                /* Save the group name. */\n                if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1)\n                    return -1;\n                nwritten += n;\n\n                /* Last ID. */\n                if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) return -1;\n                nwritten += n;\n                if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) return -1;\n                nwritten += n;\n\n                /* Save the global PEL. */\n                if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) return -1;\n                nwritten += n;\n\n                /* Save the consumers of this group. */\n                if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) return -1;\n                nwritten += n;\n            }\n            raxStop(&ri);\n        }\n    } else if (o->type == OBJ_MODULE) {\n        /* Save a module-specific value. */\n        RedisModuleIO io;\n        moduleValue *mv = o->ptr;\n        moduleType *mt = mv->type;\n\n        /* Write the \"module\" identifier as prefix, so that we'll be able\n         * to call the right module during loading. */\n        int retval = rdbSaveLen(rdb,mt->id);\n        if (retval == -1) return -1;\n        io.bytes += retval;\n\n        /* Then write the module-specific representation + EOF marker. */\n        moduleInitIOContext(io,mt,rdb,key);\n        mt->rdb_save(&io,mv->value);\n        retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);\n        if (retval == -1)\n            io.error = 1;\n        else\n            io.bytes += retval;\n\n        if (io.ctx) {\n            moduleFreeContext(io.ctx);\n            zfree(io.ctx);\n        }\n        return io.error ? -1 : (ssize_t)io.bytes;\n    } else {\n        serverPanic(\"Unknown object type\");\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. */\nsize_t rdbSavedObjectLen(robj *o, robj *key) {\n    ssize_t len = rdbSaveObject(NULL,o,key);\n    serverAssertWithInfo(NULL,o,len != -1);\n    return len;\n}\n\n/* Save a key-value pair, with expire time, type, key, value.\n * On error -1 is returned.\n * On success if the key was actually saved 1 is returned, otherwise 0\n * is returned (the key was already expired). */\nint rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) {\n    int savelru = server.maxmemory_policy & MAXMEMORY_FLAG_LRU;\n    int savelfu = server.maxmemory_policy & MAXMEMORY_FLAG_LFU;\n\n    /* Save the expire time */\n    if (expiretime != -1) {\n        if (rdbSaveType(rdb,RDB_OPCODE_EXPIRETIME_MS) == -1) return -1;\n        if (rdbSaveMillisecondTime(rdb,expiretime) == -1) return -1;\n    }\n\n    /* Save the LRU info. */\n    if (savelru) {\n        uint64_t idletime = estimateObjectIdleTime(val);\n        idletime /= 1000; /* Using seconds is enough and requires less space.*/\n        if (rdbSaveType(rdb,RDB_OPCODE_IDLE) == -1) return -1;\n        if (rdbSaveLen(rdb,idletime) == -1) return -1;\n    }\n\n    /* Save the LFU info. */\n    if (savelfu) {\n        uint8_t buf[1];\n        buf[0] = LFUDecrAndReturn(val);\n        /* We can encode this in exactly two bytes: the opcode and an 8\n         * bit counter, since the frequency is logarithmic with a 0-255 range.\n         * Note that we do not store the halving time because to reset it\n         * a single time when loading does not affect the frequency much. */\n        if (rdbSaveType(rdb,RDB_OPCODE_FREQ) == -1) return -1;\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n    }\n\n    /* Save type, key, value */\n    if (rdbSaveObjectType(rdb,val) == -1) return -1;\n    if (rdbSaveStringObject(rdb,key) == -1) return -1;\n    if (rdbSaveObject(rdb,val,key) == -1) return -1;\n\n    /* Delay return if required (for testing) */\n    if (server.rdb_key_save_delay)\n        usleep(server.rdb_key_save_delay);\n\n    return 1;\n}\n\n/* Save an AUX field. */\nssize_t rdbSaveAuxField(rio *rdb, void *key, size_t keylen, void *val, size_t vallen) {\n    ssize_t ret, len = 0;\n    if ((ret = rdbSaveType(rdb,RDB_OPCODE_AUX)) == -1) return -1;\n    len += ret;\n    if ((ret = rdbSaveRawString(rdb,key,keylen)) == -1) return -1;\n    len += ret;\n    if ((ret = rdbSaveRawString(rdb,val,vallen)) == -1) return -1;\n    len += ret;\n    return len;\n}\n\n/* Wrapper for rdbSaveAuxField() used when key/val length can be obtained\n * with strlen(). */\nssize_t rdbSaveAuxFieldStrStr(rio *rdb, char *key, char *val) {\n    return rdbSaveAuxField(rdb,key,strlen(key),val,strlen(val));\n}\n\n/* Wrapper for strlen(key) + integer type (up to long long range). */\nssize_t rdbSaveAuxFieldStrInt(rio *rdb, char *key, long long val) {\n    char buf[LONG_STR_SIZE];\n    int vlen = ll2string(buf,sizeof(buf),val);\n    return rdbSaveAuxField(rdb,key,strlen(key),buf,vlen);\n}\n\n/* Save a few default AUX fields with information about the RDB generated. */\nint rdbSaveInfoAuxFields(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {\n    int redis_bits = (sizeof(void*) == 8) ? 64 : 32;\n    int aof_preamble = (rdbflags & RDBFLAGS_AOF_PREAMBLE) != 0;\n\n    /* Add a few fields about the state when the RDB was created. */\n    if (rdbSaveAuxFieldStrStr(rdb,\"redis-ver\",REDIS_VERSION) == -1) return -1;\n    if (rdbSaveAuxFieldStrInt(rdb,\"redis-bits\",redis_bits) == -1) return -1;\n    if (rdbSaveAuxFieldStrInt(rdb,\"ctime\",time(NULL)) == -1) return -1;\n    if (rdbSaveAuxFieldStrInt(rdb,\"used-mem\",zmalloc_used_memory()) == -1) return -1;\n\n    /* Handle saving options that generate aux fields. */\n    if (rsi) {\n        if (rdbSaveAuxFieldStrInt(rdb,\"repl-stream-db\",rsi->repl_stream_db)\n            == -1) return -1;\n        if (rdbSaveAuxFieldStrStr(rdb,\"repl-id\",server.replid)\n            == -1) return -1;\n        if (rdbSaveAuxFieldStrInt(rdb,\"repl-offset\",server.master_repl_offset)\n            == -1) return -1;\n    }\n    if (rdbSaveAuxFieldStrInt(rdb,\"aof-preamble\",aof_preamble) == -1) return -1;\n    return 1;\n}\n\nssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt) {\n    /* Save a module-specific aux value. */\n    RedisModuleIO io;\n    int retval = rdbSaveType(rdb, RDB_OPCODE_MODULE_AUX);\n\n    /* Write the \"module\" identifier as prefix, so that we'll be able\n     * to call the right module during loading. */\n    retval = rdbSaveLen(rdb,mt->id);\n    if (retval == -1) return -1;\n    io.bytes += retval;\n\n    /* write the 'when' so that we can provide it on loading. add a UINT opcode\n     * for backwards compatibility, everything after the MT needs to be prefixed\n     * by an opcode. */\n    retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_UINT);\n    if (retval == -1) return -1;\n    io.bytes += retval;\n    retval = rdbSaveLen(rdb,when);\n    if (retval == -1) return -1;\n    io.bytes += retval;\n\n    /* Then write the module-specific representation + EOF marker. */\n    moduleInitIOContext(io,mt,rdb,NULL);\n    mt->aux_save(&io,when);\n    retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);\n    if (retval == -1)\n        io.error = 1;\n    else\n        io.bytes += retval;\n\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    if (io.error)\n        return -1;\n    return io.bytes;\n}\n\n/* Produces a dump of the database in RDB format sending it to the specified\n * Redis I/O channel. On success C_OK is returned, otherwise C_ERR\n * is returned and part of the output, or all the output, can be\n * missing because of I/O errors.\n *\n * When the function returns C_ERR and if 'error' is not NULL, the\n * integer pointed by 'error' is set to the value of errno just after the I/O\n * error. */\nint rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) {\n    dictIterator *di = NULL;\n    dictEntry *de;\n    char magic[10];\n    int j;\n    uint64_t cksum;\n    size_t processed = 0;\n\n    if (server.rdb_checksum)\n        rdb->update_cksum = rioGenericUpdateChecksum;\n    snprintf(magic,sizeof(magic),\"REDIS%04d\",RDB_VERSION);\n    if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;\n    if (rdbSaveInfoAuxFields(rdb,rdbflags,rsi) == -1) goto werr;\n    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_BEFORE_RDB) == -1) goto werr;\n\n    for (j = 0; j < server.dbnum; j++) {\n        redisDb *db = server.db+j;\n        dict *d = db->dict;\n        if (dictSize(d) == 0) continue;\n        di = dictGetSafeIterator(d);\n\n        /* Write the SELECT DB opcode */\n        if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;\n        if (rdbSaveLen(rdb,j) == -1) goto werr;\n\n        /* Write the RESIZE DB opcode. */\n        uint64_t db_size, expires_size;\n        db_size = dictSize(db->dict);\n        expires_size = dictSize(db->expires);\n        if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;\n        if (rdbSaveLen(rdb,db_size) == -1) goto werr;\n        if (rdbSaveLen(rdb,expires_size) == -1) goto werr;\n\n        /* Iterate this DB writing every entry */\n        while((de = dictNext(di)) != NULL) {\n            sds keystr = dictGetKey(de);\n            robj key, *o = dictGetVal(de);\n            long long expire;\n\n            initStaticStringObject(key,keystr);\n            expire = getExpire(db,&key);\n            if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;\n\n            /* When this RDB is produced as part of an AOF rewrite, move\n             * accumulated diff from parent to child while rewriting in\n             * order to have a smaller final write. */\n            if (rdbflags & RDBFLAGS_AOF_PREAMBLE &&\n                rdb->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES)\n            {\n                processed = rdb->processed_bytes;\n                aofReadDiffFromParent();\n            }\n        }\n        dictReleaseIterator(di);\n        di = NULL; /* So that we don't release it again on error. */\n    }\n\n    /* If we are storing the replication information on disk, persist\n     * the script cache as well: on successful PSYNC after a restart, we need\n     * to be able to process any EVALSHA inside the replication backlog the\n     * master will send us. */\n    if (rsi && dictSize(server.lua_scripts)) {\n        di = dictGetIterator(server.lua_scripts);\n        while((de = dictNext(di)) != NULL) {\n            robj *body = dictGetVal(de);\n            if (rdbSaveAuxField(rdb,\"lua\",3,body->ptr,sdslen(body->ptr)) == -1)\n                goto werr;\n        }\n        dictReleaseIterator(di);\n        di = NULL; /* So that we don't release it again on error. */\n    }\n\n    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_AFTER_RDB) == -1) goto werr;\n\n    /* EOF opcode */\n    if (rdbSaveType(rdb,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    cksum = rdb->cksum;\n    memrev64ifbe(&cksum);\n    if (rioWrite(rdb,&cksum,8) == 0) goto werr;\n    return C_OK;\n\nwerr:\n    if (error) *error = errno;\n    if (di) dictReleaseIterator(di);\n    return C_ERR;\n}\n\n/* This is just a wrapper to rdbSaveRio() that additionally adds a prefix\n * and a suffix to the generated RDB dump. The prefix is:\n *\n * $EOF:<40 bytes unguessable hex string>\\r\\n\n *\n * While the suffix is the 40 bytes hex string we announced in the prefix.\n * This way processes receiving the payload can understand when it ends\n * without doing any processing of the content. */\nint rdbSaveRioWithEOFMark(rio *rdb, int *error, rdbSaveInfo *rsi) {\n    char eofmark[RDB_EOF_MARK_SIZE];\n\n    startSaving(RDBFLAGS_REPLICATION);\n    getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);\n    if (error) *error = 0;\n    if (rioWrite(rdb,\"$EOF:\",5) == 0) goto werr;\n    if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;\n    if (rioWrite(rdb,\"\\r\\n\",2) == 0) goto werr;\n    if (rdbSaveRio(rdb,error,RDBFLAGS_NONE,rsi) == C_ERR) goto werr;\n    if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;\n    stopSaving(1);\n    return C_OK;\n\nwerr: /* Write error. */\n    /* Set 'error' only if not already set by rdbSaveRio() call. */\n    if (error && *error == 0) *error = errno;\n    stopSaving(0);\n    return C_ERR;\n}\n\n/* Save the DB on disk. Return C_ERR on error, C_OK on success. */\nint rdbSave(char *filename, rdbSaveInfo *rsi) {\n    char tmpfile[256];\n    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */\n    FILE *fp;\n    rio rdb;\n    int error = 0;\n\n    snprintf(tmpfile,256,\"temp-%d.rdb\", (int) getpid());\n    fp = fopen(tmpfile,\"w\");\n    if (!fp) {\n        char *cwdp = getcwd(cwd,MAXPATHLEN);\n        serverLog(LL_WARNING,\n            \"Failed opening the RDB file %s (in server root dir %s) \"\n            \"for saving: %s\",\n            filename,\n            cwdp ? cwdp : \"unknown\",\n            strerror(errno));\n        return C_ERR;\n    }\n\n    rioInitWithFile(&rdb,fp);\n    startSaving(RDBFLAGS_NONE);\n\n    if (server.rdb_save_incremental_fsync)\n        rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);\n\n    if (rdbSaveRio(&rdb,&error,RDBFLAGS_NONE,rsi) == C_ERR) {\n        errno = error;\n        goto werr;\n    }\n\n    /* Make sure data will not remain on the OS's output buffers */\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    if (rename(tmpfile,filename) == -1) {\n        char *cwdp = getcwd(cwd,MAXPATHLEN);\n        serverLog(LL_WARNING,\n            \"Error moving temp DB file %s on the final \"\n            \"destination %s (in server root dir %s): %s\",\n            tmpfile,\n            filename,\n            cwdp ? cwdp : \"unknown\",\n            strerror(errno));\n        unlink(tmpfile);\n        stopSaving(0);\n        return C_ERR;\n    }\n\n    serverLog(LL_NOTICE,\"DB saved on disk\");\n    server.dirty = 0;\n    server.lastsave = time(NULL);\n    server.lastbgsave_status = C_OK;\n    stopSaving(1);\n    return C_OK;\n\nwerr:\n    serverLog(LL_WARNING,\"Write error saving DB on disk: %s\", strerror(errno));\n    fclose(fp);\n    unlink(tmpfile);\n    stopSaving(0);\n    return C_ERR;\n}\n\nint rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {\n    pid_t childpid;\n\n    if (hasActiveChildProcess()) return C_ERR;\n\n    server.dirty_before_bgsave = server.dirty;\n    server.lastbgsave_try = time(NULL);\n    openChildInfoPipe();\n\n    if ((childpid = redisFork()) == 0) {\n        int retval;\n\n        /* Child */\n        redisSetProcTitle(\"redis-rdb-bgsave\");\n        redisSetCpuAffinity(server.bgsave_cpulist);\n        retval = rdbSave(filename,rsi);\n        if (retval == C_OK) {\n            sendChildCOWInfo(CHILD_INFO_TYPE_RDB, \"RDB\");\n        }\n        exitFromChild((retval == C_OK) ? 0 : 1);\n    } else {\n        /* Parent */\n        if (childpid == -1) {\n            closeChildInfoPipe();\n            server.lastbgsave_status = C_ERR;\n            serverLog(LL_WARNING,\"Can't save in background: fork: %s\",\n                strerror(errno));\n            return C_ERR;\n        }\n        serverLog(LL_NOTICE,\"Background saving started by pid %d\",childpid);\n        server.rdb_save_time_start = time(NULL);\n        server.rdb_child_pid = childpid;\n        server.rdb_child_type = RDB_CHILD_TYPE_DISK;\n        return C_OK;\n    }\n    return C_OK; /* unreached */\n}\n\nvoid rdbRemoveTempFile(pid_t childpid) {\n    char tmpfile[256];\n\n    snprintf(tmpfile,sizeof(tmpfile),\"temp-%d.rdb\", (int) childpid);\n    unlink(tmpfile);\n}\n\n/* This function is called by rdbLoadObject() when the code is in RDB-check\n * mode and we find a module value of type 2 that can be parsed without\n * the need of the actual module. The value is parsed for errors, finally\n * a dummy redis object is returned just to conform to the API. */\nrobj *rdbLoadCheckModuleValue(rio *rdb, char *modulename) {\n    uint64_t opcode;\n    while((opcode = rdbLoadLen(rdb,NULL)) != RDB_MODULE_OPCODE_EOF) {\n        if (opcode == RDB_MODULE_OPCODE_SINT ||\n            opcode == RDB_MODULE_OPCODE_UINT)\n        {\n            uint64_t len;\n            if (rdbLoadLenByRef(rdb,NULL,&len) == -1) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading integer from module %s value\", modulename);\n            }\n        } else if (opcode == RDB_MODULE_OPCODE_STRING) {\n            robj *o = rdbGenericLoadStringObject(rdb,RDB_LOAD_NONE,NULL);\n            if (o == NULL) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading string from module %s value\", modulename);\n            }\n            decrRefCount(o);\n        } else if (opcode == RDB_MODULE_OPCODE_FLOAT) {\n            float val;\n            if (rdbLoadBinaryFloatValue(rdb,&val) == -1) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading float from module %s value\", modulename);\n            }\n        } else if (opcode == RDB_MODULE_OPCODE_DOUBLE) {\n            double val;\n            if (rdbLoadBinaryDoubleValue(rdb,&val) == -1) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading double from module %s value\", modulename);\n            }\n        }\n    }\n    return createStringObject(\"module-dummy-value\",18);\n}\n\n/* Load a Redis object of the specified type from the specified file.\n * On success a newly allocated object is returned, otherwise NULL. */\nrobj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {\n    robj *o = NULL, *ele, *dec;\n    uint64_t len;\n    unsigned int i;\n\n    if (rdbtype == RDB_TYPE_STRING) {\n        /* Read string value */\n        if ((o = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;\n        o = tryObjectEncoding(o);\n    } else if (rdbtype == RDB_TYPE_LIST) {\n        /* Read list value */\n        if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n\n        o = createQuicklistObject();\n        quicklistSetOptions(o->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n\n        /* Load every single element of the list */\n        while(len--) {\n            if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            dec = getDecodedObject(ele);\n            size_t len = sdslen(dec->ptr);\n            quicklistPushTail(o->ptr, dec->ptr, len);\n            decrRefCount(dec);\n            decrRefCount(ele);\n        }\n    } else if (rdbtype == RDB_TYPE_SET) {\n        /* Read Set value */\n        if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n\n        /* Use a regular set when there are too many entries. */\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 set */\n        for (i = 0; i < len; i++) {\n            long long llval;\n            sds sdsele;\n\n            if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n\n            if (o->encoding == OBJ_ENCODING_INTSET) {\n                /* Fetch integer value from element. */\n                if (isSdsRepresentableAsLongLong(sdsele,&llval) == C_OK) {\n                    o->ptr = intsetAdd(o->ptr,llval,NULL);\n                } else {\n                    setTypeConvert(o,OBJ_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            if (o->encoding == OBJ_ENCODING_HT) {\n                dictAdd((dict*)o->ptr,sdsele,NULL);\n            } else {\n                sdsfree(sdsele);\n            }\n        }\n    } else if (rdbtype == RDB_TYPE_ZSET_2 || rdbtype == RDB_TYPE_ZSET) {\n        /* Read list/set value. */\n        uint64_t zsetlen;\n        size_t maxelelen = 0;\n        zset *zs;\n\n        if ((zsetlen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n        o = createZsetObject();\n        zs = o->ptr;\n\n        if (zsetlen > DICT_HT_INITIAL_SIZE)\n            dictExpand(zs->dict,zsetlen);\n\n        /* Load every single element of the sorted set. */\n        while(zsetlen--) {\n            sds sdsele;\n            double score;\n            zskiplistNode *znode;\n\n            if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n\n            if (rdbtype == RDB_TYPE_ZSET_2) {\n                if (rdbLoadBinaryDoubleValue(rdb,&score) == -1) {\n                    decrRefCount(o);\n                    sdsfree(sdsele);\n                    return NULL;\n                }\n            } else {\n                if (rdbLoadDoubleValue(rdb,&score) == -1) {\n                    decrRefCount(o);\n                    sdsfree(sdsele);\n                    return NULL;\n                }\n            }\n\n            /* Don't care about integer-encoded strings. */\n            if (sdslen(sdsele) > maxelelen) maxelelen = sdslen(sdsele);\n\n            znode = zslInsert(zs->zsl,score,sdsele);\n            dictAdd(zs->dict,sdsele,&znode->score);\n        }\n\n        /* Convert *after* loading, since sorted sets are not stored ordered. */\n        if (zsetLength(o) <= server.zset_max_ziplist_entries &&\n            maxelelen <= server.zset_max_ziplist_value)\n                zsetConvert(o,OBJ_ENCODING_ZIPLIST);\n    } else if (rdbtype == RDB_TYPE_HASH) {\n        uint64_t len;\n        int ret;\n        sds field, value;\n\n        len = rdbLoadLen(rdb, NULL);\n        if (len == RDB_LENERR) return NULL;\n\n        o = createHashObject();\n\n        /* Too many entries? Use a hash table. */\n        if (len > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n\n        /* Load every field and value into the ziplist */\n        while (o->encoding == OBJ_ENCODING_ZIPLIST && len > 0) {\n            len--;\n            /* Load raw strings */\n            if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                sdsfree(field);\n                decrRefCount(o);\n                return NULL;\n            }\n\n            /* Add pair to ziplist */\n            o->ptr = ziplistPush(o->ptr, (unsigned char*)field,\n                    sdslen(field), ZIPLIST_TAIL);\n            o->ptr = ziplistPush(o->ptr, (unsigned char*)value,\n                    sdslen(value), ZIPLIST_TAIL);\n\n            /* Convert to hash table if size threshold is exceeded */\n            if (sdslen(field) > server.hash_max_ziplist_value ||\n                sdslen(value) > server.hash_max_ziplist_value)\n            {\n                sdsfree(field);\n                sdsfree(value);\n                hashTypeConvert(o, OBJ_ENCODING_HT);\n                break;\n            }\n            sdsfree(field);\n            sdsfree(value);\n        }\n\n        if (o->encoding == OBJ_ENCODING_HT && len > DICT_HT_INITIAL_SIZE)\n            dictExpand(o->ptr,len);\n\n        /* Load remaining fields and values into the hash table */\n        while (o->encoding == OBJ_ENCODING_HT && len > 0) {\n            len--;\n            /* Load encoded strings */\n            if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                sdsfree(field);\n                decrRefCount(o);\n                return NULL;\n            }\n\n            /* Add pair to hash table */\n            ret = dictAdd((dict*)o->ptr, field, value);\n            if (ret == DICT_ERR) {\n                rdbExitReportCorruptRDB(\"Duplicate keys detected\");\n            }\n        }\n\n        /* All pairs should be read by now */\n        serverAssert(len == 0);\n    } else if (rdbtype == RDB_TYPE_LIST_QUICKLIST) {\n        if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n        o = createQuicklistObject();\n        quicklistSetOptions(o->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n\n        while (len--) {\n            unsigned char *zl =\n                rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);\n            if (zl == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            quicklistAppendZiplist(o->ptr, zl);\n        }\n    } else if (rdbtype == RDB_TYPE_HASH_ZIPMAP  ||\n               rdbtype == RDB_TYPE_LIST_ZIPLIST ||\n               rdbtype == RDB_TYPE_SET_INTSET   ||\n               rdbtype == RDB_TYPE_ZSET_ZIPLIST ||\n               rdbtype == RDB_TYPE_HASH_ZIPLIST)\n    {\n        unsigned char *encoded =\n            rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);\n        if (encoded == NULL) return NULL;\n        o = createObject(OBJ_STRING,encoded); /* Obj type fixed below. */\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        switch(rdbtype) {\n            case 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                    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                    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                    o->ptr = zl;\n                    o->type = OBJ_HASH;\n                    o->encoding = OBJ_ENCODING_ZIPLIST;\n\n                    if (hashTypeLength(o) > server.hash_max_ziplist_entries ||\n                        maxlen > server.hash_max_ziplist_value)\n                    {\n                        hashTypeConvert(o, OBJ_ENCODING_HT);\n                    }\n                }\n                break;\n            case RDB_TYPE_LIST_ZIPLIST:\n                o->type = OBJ_LIST;\n                o->encoding = OBJ_ENCODING_ZIPLIST;\n                listTypeConvert(o,OBJ_ENCODING_QUICKLIST);\n                break;\n            case RDB_TYPE_SET_INTSET:\n                o->type = OBJ_SET;\n                o->encoding = OBJ_ENCODING_INTSET;\n                if (intsetLen(o->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(o,OBJ_ENCODING_HT);\n                break;\n            case RDB_TYPE_ZSET_ZIPLIST:\n                o->type = OBJ_ZSET;\n                o->encoding = OBJ_ENCODING_ZIPLIST;\n                if (zsetLength(o) > server.zset_max_ziplist_entries)\n                    zsetConvert(o,OBJ_ENCODING_SKIPLIST);\n                break;\n            case RDB_TYPE_HASH_ZIPLIST:\n                o->type = OBJ_HASH;\n                o->encoding = OBJ_ENCODING_ZIPLIST;\n                if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n                    hashTypeConvert(o, OBJ_ENCODING_HT);\n                break;\n            default:\n                /* totally unreachable */\n                rdbExitReportCorruptRDB(\"Unknown RDB encoding type %d\",rdbtype);\n                break;\n        }\n    } else if (rdbtype == RDB_TYPE_STREAM_LISTPACKS) {\n        o = createStreamObject();\n        stream *s = o->ptr;\n        uint64_t listpacks = rdbLoadLen(rdb,NULL);\n        if (listpacks == RDB_LENERR) {\n            rdbReportReadError(\"Stream listpacks len loading failed.\");\n            decrRefCount(o);\n            return NULL;\n        }\n\n        while(listpacks--) {\n            /* Get the master ID, the one we'll use as key of the radix tree\n             * node: the entries inside the listpack itself are delta-encoded\n             * relatively to this ID. */\n            sds nodekey = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL);\n            if (nodekey == NULL) {\n                rdbReportReadError(\"Stream master ID loading failed: invalid encoding or I/O error.\");\n                decrRefCount(o);\n                return NULL;\n            }\n            if (sdslen(nodekey) != sizeof(streamID)) {\n                rdbExitReportCorruptRDB(\"Stream node key entry is not the \"\n                                        \"size of a stream ID\");\n            }\n\n            /* Load the listpack. */\n            unsigned char *lp =\n                rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);\n            if (lp == NULL) {\n                rdbReportReadError(\"Stream listpacks loading failed.\");\n                sdsfree(nodekey);\n                decrRefCount(o);\n                return NULL;\n            }\n            unsigned char *first = lpFirst(lp);\n            if (first == NULL) {\n                /* Serialized listpacks should never be empty, since on\n                 * deletion we should remove the radix tree key if the\n                 * resulting listpack is empty. */\n                rdbExitReportCorruptRDB(\"Empty listpack inside stream\");\n            }\n\n            /* Insert the key in the radix tree. */\n            int retval = raxInsert(s->rax,\n                (unsigned char*)nodekey,sizeof(streamID),lp,NULL);\n            sdsfree(nodekey);\n            if (!retval)\n                rdbExitReportCorruptRDB(\"Listpack re-added with existing key\");\n        }\n        /* Load total number of items inside the stream. */\n        s->length = rdbLoadLen(rdb,NULL);\n\n        /* Load the last entry ID. */\n        s->last_id.ms = rdbLoadLen(rdb,NULL);\n        s->last_id.seq = rdbLoadLen(rdb,NULL);\n\n        if (rioGetReadError(rdb)) {\n            rdbReportReadError(\"Stream object metadata loading failed.\");\n            decrRefCount(o);\n            return NULL;\n        }\n\n        /* Consumer groups loading */\n        uint64_t cgroups_count = rdbLoadLen(rdb,NULL);\n        if (cgroups_count == RDB_LENERR) {\n            rdbReportReadError(\"Stream cgroup count loading failed.\");\n            decrRefCount(o);\n            return NULL;\n        }\n        while(cgroups_count--) {\n            /* Get the consumer group name and ID. We can then create the\n             * consumer group ASAP and populate its structure as\n             * we read more data. */\n            streamID cg_id;\n            sds cgname = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL);\n            if (cgname == NULL) {\n                rdbReportReadError(\n                    \"Error reading the consumer group name from Stream\");\n                decrRefCount(o);\n                return NULL;\n            }\n\n            cg_id.ms = rdbLoadLen(rdb,NULL);\n            cg_id.seq = rdbLoadLen(rdb,NULL);\n            if (rioGetReadError(rdb)) {\n                rdbReportReadError(\"Stream cgroup ID loading failed.\");\n                sdsfree(cgname);\n                decrRefCount(o);\n                return NULL;\n            }\n\n            streamCG *cgroup = streamCreateCG(s,cgname,sdslen(cgname),&cg_id);\n            if (cgroup == NULL)\n                rdbExitReportCorruptRDB(\"Duplicated consumer group name %s\",\n                                         cgname);\n            sdsfree(cgname);\n\n            /* Load the global PEL for this consumer group, however we'll\n             * not yet populate the NACK structures with the message\n             * owner, since consumers for this group and their messages will\n             * be read as a next step. So for now leave them not resolved\n             * and later populate it. */\n            uint64_t pel_size = rdbLoadLen(rdb,NULL);\n            if (pel_size == RDB_LENERR) {\n                rdbReportReadError(\"Stream PEL size loading failed.\");\n                decrRefCount(o);\n                return NULL;\n            }\n            while(pel_size--) {\n                unsigned char rawid[sizeof(streamID)];\n                if (rioRead(rdb,rawid,sizeof(rawid)) == 0) {\n                    rdbReportReadError(\"Stream PEL ID loading failed.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n                streamNACK *nack = streamCreateNACK(NULL);\n                nack->delivery_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);\n                nack->delivery_count = rdbLoadLen(rdb,NULL);\n                if (rioGetReadError(rdb)) {\n                    rdbReportReadError(\"Stream PEL NACK loading failed.\");\n                    decrRefCount(o);\n                    streamFreeNACK(nack);\n                    return NULL;\n                }\n                if (!raxInsert(cgroup->pel,rawid,sizeof(rawid),nack,NULL))\n                    rdbExitReportCorruptRDB(\"Duplicated gobal PEL entry \"\n                                            \"loading stream consumer group\");\n            }\n\n            /* Now that we loaded our global PEL, we need to load the\n             * consumers and their local PELs. */\n            uint64_t consumers_num = rdbLoadLen(rdb,NULL);\n            if (consumers_num == RDB_LENERR) {\n                rdbReportReadError(\"Stream consumers num loading failed.\");\n                decrRefCount(o);\n                return NULL;\n            }\n            while(consumers_num--) {\n                sds cname = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL);\n                if (cname == NULL) {\n                    rdbReportReadError(\n                        \"Error reading the consumer name from Stream group.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n                streamConsumer *consumer =\n                    streamLookupConsumer(cgroup,cname,SLC_NONE);\n                sdsfree(cname);\n                consumer->seen_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);\n                if (rioGetReadError(rdb)) {\n                    rdbReportReadError(\"Stream short read reading seen time.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n\n                /* Load the PEL about entries owned by this specific\n                 * consumer. */\n                pel_size = rdbLoadLen(rdb,NULL);\n                if (pel_size == RDB_LENERR) {\n                    rdbReportReadError(\n                        \"Stream consumer PEL num loading failed.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n                while(pel_size--) {\n                    unsigned char rawid[sizeof(streamID)];\n                    if (rioRead(rdb,rawid,sizeof(rawid)) == 0) {\n                        rdbReportReadError(\n                            \"Stream short read reading PEL streamID.\");\n                        decrRefCount(o);\n                        return NULL;\n                    }\n                    streamNACK *nack = raxFind(cgroup->pel,rawid,sizeof(rawid));\n                    if (nack == raxNotFound)\n                        rdbExitReportCorruptRDB(\"Consumer entry not found in \"\n                                                \"group global PEL\");\n\n                    /* Set the NACK consumer, that was left to NULL when\n                     * loading the global PEL. Then set the same shared\n                     * NACK structure also in the consumer-specific PEL. */\n                    nack->consumer = consumer;\n                    if (!raxInsert(consumer->pel,rawid,sizeof(rawid),nack,NULL))\n                        rdbExitReportCorruptRDB(\"Duplicated consumer PEL entry \"\n                                                \" loading a stream consumer \"\n                                                \"group\");\n                }\n            }\n        }\n    } else if (rdbtype == RDB_TYPE_MODULE || rdbtype == RDB_TYPE_MODULE_2) {\n        uint64_t moduleid = rdbLoadLen(rdb,NULL);\n        if (rioGetReadError(rdb)) {\n            rdbReportReadError(\"Short read module id\");\n            return NULL;\n        }\n        moduleType *mt = moduleTypeLookupModuleByID(moduleid);\n        char name[10];\n\n        if (rdbCheckMode && rdbtype == RDB_TYPE_MODULE_2) {\n            moduleTypeNameByID(name,moduleid);\n            return rdbLoadCheckModuleValue(rdb,name);\n        }\n\n        if (mt == NULL) {\n            moduleTypeNameByID(name,moduleid);\n            serverLog(LL_WARNING,\"The RDB file contains module data I can't load: no matching module '%s'\", name);\n            exit(1);\n        }\n        RedisModuleIO io;\n        robj keyobj;\n        initStaticStringObject(keyobj,key);\n        moduleInitIOContext(io,mt,rdb,&keyobj);\n        io.ver = (rdbtype == RDB_TYPE_MODULE) ? 1 : 2;\n        /* Call the rdb_load method of the module providing the 10 bit\n         * encoding version in the lower 10 bits of the module ID. */\n        void *ptr = mt->rdb_load(&io,moduleid&1023);\n        if (io.ctx) {\n            moduleFreeContext(io.ctx);\n            zfree(io.ctx);\n        }\n\n        /* Module v2 serialization has an EOF mark at the end. */\n        if (io.ver == 2) {\n            uint64_t eof = rdbLoadLen(rdb,NULL);\n            if (eof == RDB_LENERR) {\n                o = createModuleObject(mt,ptr); /* creating just in order to easily destroy */\n                decrRefCount(o);\n                return NULL;\n            }\n            if (eof != RDB_MODULE_OPCODE_EOF) {\n                serverLog(LL_WARNING,\"The RDB file contains module data for the module '%s' that is not terminated by the proper module value EOF marker\", name);\n                exit(1);\n            }\n        }\n\n        if (ptr == NULL) {\n            moduleTypeNameByID(name,moduleid);\n            serverLog(LL_WARNING,\"The RDB file contains module data for the module type '%s', that the responsible module is not able to load. Check for modules log above for additional clues.\", name);\n            exit(1);\n        }\n        o = createModuleObject(mt,ptr);\n    } else {\n        rdbReportReadError(\"Unknown RDB encoding type %d\",rdbtype);\n        return NULL;\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. */\nvoid startLoading(size_t size, int rdbflags) {\n    /* Load the DB */\n    server.loading = 1;\n    server.loading_start_time = time(NULL);\n    server.loading_loaded_bytes = 0;\n    server.loading_total_bytes = size;\n\n    /* Fire the loading modules start event. */\n    int subevent;\n    if (rdbflags & RDBFLAGS_AOF_PREAMBLE)\n        subevent = REDISMODULE_SUBEVENT_LOADING_AOF_START;\n    else if(rdbflags & RDBFLAGS_REPLICATION)\n        subevent = REDISMODULE_SUBEVENT_LOADING_REPL_START;\n    else\n        subevent = REDISMODULE_SUBEVENT_LOADING_RDB_START;\n    moduleFireServerEvent(REDISMODULE_EVENT_LOADING,subevent,NULL);\n}\n\n/* Mark that we are loading in the global state and setup the fields\n * needed to provide loading stats.\n * 'filename' is optional and used for rdb-check on error */\nvoid startLoadingFile(FILE *fp, char* filename, int rdbflags) {\n    struct stat sb;\n    if (fstat(fileno(fp), &sb) == -1)\n        sb.st_size = 0;\n    rdbFileBeingLoaded = filename;\n    startLoading(sb.st_size, rdbflags);\n}\n\n/* Refresh the loading progress info */\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 */\nvoid stopLoading(int success) {\n    server.loading = 0;\n    rdbFileBeingLoaded = NULL;\n\n    /* Fire the loading modules end event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_LOADING,\n                          success?\n                            REDISMODULE_SUBEVENT_LOADING_ENDED:\n                            REDISMODULE_SUBEVENT_LOADING_FAILED,\n                          NULL);\n}\n\nvoid startSaving(int rdbflags) {\n    /* Fire the persistence modules end event. */\n    int subevent;\n    if (rdbflags & RDBFLAGS_AOF_PREAMBLE)\n        subevent = REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START;\n    else if (getpid()!=server.pid)\n        subevent = REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START;\n    else\n        subevent = REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START;\n    moduleFireServerEvent(REDISMODULE_EVENT_PERSISTENCE,subevent,NULL);\n}\n\nvoid stopSaving(int success) {\n    /* Fire the persistence modules end event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_PERSISTENCE,\n                          success?\n                            REDISMODULE_SUBEVENT_PERSISTENCE_ENDED:\n                            REDISMODULE_SUBEVENT_PERSISTENCE_FAILED,\n                          NULL);\n}\n\n/* Track loading progress in order to serve client's from time to time\n   and if needed calculate rdb checksum  */\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(0);\n        if (server.masterhost && server.repl_state == REPL_STATE_TRANSFER)\n            replicationSendNewlineToMaster();\n        loadingProgress(r->processed_bytes);\n        processEventsWhileBlocked();\n        processModuleLoadingProgressEvent(0);\n    }\n}\n\n/* Load an RDB file from the rio stream 'rdb'. On success C_OK is returned,\n * otherwise C_ERR is returned and 'errno' is set accordingly. */\nint rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {\n    uint64_t dbid;\n    int type, rdbver;\n    redisDb *db = server.db+0;\n    char buf[1024];\n\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    if (memcmp(buf,\"REDIS\",5) != 0) {\n        serverLog(LL_WARNING,\"Wrong signature trying to load DB from file\");\n        errno = EINVAL;\n        return C_ERR;\n    }\n    rdbver = atoi(buf+5);\n    if (rdbver < 1 || rdbver > RDB_VERSION) {\n        serverLog(LL_WARNING,\"Can't handle RDB format version %d\",rdbver);\n        errno = EINVAL;\n        return C_ERR;\n    }\n\n    /* Key-specific attributes, set by opcodes before the key type. */\n    long long lru_idle = -1, lfu_freq = -1, expiretime = -1, now = mstime();\n    long long lru_clock = LRU_CLOCK();\n\n    while(1) {\n        sds key;\n        robj *val;\n\n        /* Read type. */\n        if ((type = rdbLoadType(rdb)) == -1) goto eoferr;\n\n        /* Handle special types. */\n        if (type == RDB_OPCODE_EXPIRETIME) {\n            /* EXPIRETIME: load an expire associated with the next key\n             * to load. Note that after loading an expire we need to\n             * load the actual type, and continue. */\n            expiretime = rdbLoadTime(rdb);\n            expiretime *= 1000;\n            if (rioGetReadError(rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EXPIRETIME_MS) {\n            /* EXPIRETIME_MS: milliseconds precision expire times introduced\n             * with RDB v3. Like EXPIRETIME but no with more precision. */\n            expiretime = rdbLoadMillisecondTime(rdb,rdbver);\n            if (rioGetReadError(rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_FREQ) {\n            /* FREQ: LFU frequency. */\n            uint8_t byte;\n            if (rioRead(rdb,&byte,1) == 0) goto eoferr;\n            lfu_freq = byte;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_IDLE) {\n            /* IDLE: LRU idle time. */\n            uint64_t qword;\n            if ((qword = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr;\n            lru_idle = qword;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EOF) {\n            /* EOF: End of file, exit the main loop. */\n            break;\n        } else if (type == RDB_OPCODE_SELECTDB) {\n            /* SELECTDB: Select the specified database. */\n            if ((dbid = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr;\n            if (dbid >= (unsigned)server.dbnum) {\n                serverLog(LL_WARNING,\n                    \"FATAL: Data file was created with a Redis \"\n                    \"server configured to handle more than %d \"\n                    \"databases. Exiting\\n\", server.dbnum);\n                exit(1);\n            }\n            db = server.db+dbid;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_RESIZEDB) {\n            /* RESIZEDB: Hint about the size of the keys in the currently\n             * selected data base, in order to avoid useless rehashing. */\n            uint64_t db_size, expires_size;\n            if ((db_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            if ((expires_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            dictExpand(db->dict,db_size);\n            dictExpand(db->expires,expires_size);\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_AUX) {\n            /* AUX: generic string-string fields. Use to add state to RDB\n             * which is backward compatible. Implementations of RDB loading\n             * are requierd to skip AUX fields they don't understand.\n             *\n             * An AUX field is composed of two strings: key and value. */\n            robj *auxkey, *auxval;\n            if ((auxkey = rdbLoadStringObject(rdb)) == NULL) goto eoferr;\n            if ((auxval = rdbLoadStringObject(rdb)) == NULL) goto eoferr;\n\n            if (((char*)auxkey->ptr)[0] == '%') {\n                /* All the fields with a name staring with '%' are considered\n                 * information fields and are logged at startup with a log\n                 * level of NOTICE. */\n                serverLog(LL_NOTICE,\"RDB '%s': %s\",\n                    (char*)auxkey->ptr,\n                    (char*)auxval->ptr);\n            } else if (!strcasecmp(auxkey->ptr,\"repl-stream-db\")) {\n                if (rsi) rsi->repl_stream_db = atoi(auxval->ptr);\n            } else if (!strcasecmp(auxkey->ptr,\"repl-id\")) {\n                if (rsi && sdslen(auxval->ptr) == CONFIG_RUN_ID_SIZE) {\n                    memcpy(rsi->repl_id,auxval->ptr,CONFIG_RUN_ID_SIZE+1);\n                    rsi->repl_id_is_set = 1;\n                }\n            } else if (!strcasecmp(auxkey->ptr,\"repl-offset\")) {\n                if (rsi) rsi->repl_offset = strtoll(auxval->ptr,NULL,10);\n            } else if (!strcasecmp(auxkey->ptr,\"lua\")) {\n                /* Load the script back in memory. */\n                if (luaCreateFunction(NULL,server.lua,auxval) == NULL) {\n                    rdbExitReportCorruptRDB(\n                        \"Can't load Lua script from RDB file! \"\n                        \"BODY: %s\", auxval->ptr);\n                }\n            } else if (!strcasecmp(auxkey->ptr,\"redis-ver\")) {\n                serverLog(LL_NOTICE,\"Loading RDB produced by version %s\",\n                    (char*)auxval->ptr);\n            } else if (!strcasecmp(auxkey->ptr,\"ctime\")) {\n                time_t age = time(NULL)-strtol(auxval->ptr,NULL,10);\n                if (age < 0) age = 0;\n                serverLog(LL_NOTICE,\"RDB age %ld seconds\",\n                    (unsigned long) age);\n            } else if (!strcasecmp(auxkey->ptr,\"used-mem\")) {\n                long long usedmem = strtoll(auxval->ptr,NULL,10);\n                serverLog(LL_NOTICE,\"RDB memory usage when created %.2f Mb\",\n                    (double) usedmem / (1024*1024));\n            } else if (!strcasecmp(auxkey->ptr,\"aof-preamble\")) {\n                long long haspreamble = strtoll(auxval->ptr,NULL,10);\n                if (haspreamble) serverLog(LL_NOTICE,\"RDB has an AOF tail\");\n            } else if (!strcasecmp(auxkey->ptr,\"redis-bits\")) {\n                /* Just ignored. */\n            } else {\n                /* We ignore fields we don't understand, as by AUX field\n                 * contract. */\n                serverLog(LL_DEBUG,\"Unrecognized RDB AUX field: '%s'\",\n                    (char*)auxkey->ptr);\n            }\n\n            decrRefCount(auxkey);\n            decrRefCount(auxval);\n            continue; /* Read type again. */\n        } else if (type == RDB_OPCODE_MODULE_AUX) {\n            /* Load module data that is not related to the Redis key space.\n             * Such data can be potentially be stored both before and after the\n             * RDB keys-values section. */\n            uint64_t moduleid = rdbLoadLen(rdb,NULL);\n            int when_opcode = rdbLoadLen(rdb,NULL);\n            int when = rdbLoadLen(rdb,NULL);\n            if (rioGetReadError(rdb)) goto eoferr;\n            if (when_opcode != RDB_MODULE_OPCODE_UINT)\n                rdbReportReadError(\"bad when_opcode\");\n            moduleType *mt = moduleTypeLookupModuleByID(moduleid);\n            char name[10];\n            moduleTypeNameByID(name,moduleid);\n\n            if (!rdbCheckMode && mt == NULL) {\n                /* Unknown module. */\n                serverLog(LL_WARNING,\"The RDB file contains AUX module data I can't load: no matching module '%s'\", name);\n                exit(1);\n            } else if (!rdbCheckMode && mt != NULL) {\n                if (!mt->aux_load) {\n                    /* Module doesn't support AUX. */\n                    serverLog(LL_WARNING,\"The RDB file contains module AUX data, but the module '%s' doesn't seem to support it.\", name);\n                    exit(1);\n                }\n\n                RedisModuleIO io;\n                moduleInitIOContext(io,mt,rdb,NULL);\n                io.ver = 2;\n                /* Call the rdb_load method of the module providing the 10 bit\n                 * encoding version in the lower 10 bits of the module ID. */\n                if (mt->aux_load(&io,moduleid&1023, when) != REDISMODULE_OK || io.error) {\n                    moduleTypeNameByID(name,moduleid);\n                    serverLog(LL_WARNING,\"The RDB file contains module AUX data for the module type '%s', that the responsible module is not able to load. Check for modules log above for additional clues.\", name);\n                    exit(1);\n                }\n                if (io.ctx) {\n                    moduleFreeContext(io.ctx);\n                    zfree(io.ctx);\n                }\n                uint64_t eof = rdbLoadLen(rdb,NULL);\n                if (eof != RDB_MODULE_OPCODE_EOF) {\n                    serverLog(LL_WARNING,\"The RDB file contains module AUX data for the module '%s' that is not terminated by the proper module value EOF marker\", name);\n                    exit(1);\n                }\n                continue;\n            } else {\n                /* RDB check mode. */\n                robj *aux = rdbLoadCheckModuleValue(rdb,name);\n                decrRefCount(aux);\n                continue; /* Read next opcode. */\n            }\n        }\n\n        /* Read key */\n        if ((key = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL)\n            goto eoferr;\n        /* Read value */\n        if ((val = rdbLoadObject(type,rdb,key)) == NULL) {\n            sdsfree(key);\n            goto eoferr;\n        }\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         * Similarly if the RDB is the preamble of an AOF file, we want to\n         * load all the keys as they are, since the log of operations later\n         * assume to work in an exact keyspace state. */\n        if (iAmMaster() &&\n            !(rdbflags&RDBFLAGS_AOF_PREAMBLE) &&\n            expiretime != -1 && expiretime < now)\n        {\n            sdsfree(key);\n            decrRefCount(val);\n        } else {\n            robj keyobj;\n\n            /* Add the new object in the hash table */\n            int added = dbAddRDBLoad(db,key,val);\n            if (!added) {\n                if (rdbflags & RDBFLAGS_ALLOW_DUP) {\n                    /* This flag is useful for DEBUG RELOAD special modes.\n                     * When it's set we allow new keys to replace the current\n                     * keys with the same name. */\n                    initStaticStringObject(keyobj,key);\n                    dbSyncDelete(db,&keyobj);\n                    dbAddRDBLoad(db,key,val);\n                } else {\n                    serverLog(LL_WARNING,\n                        \"RDB has duplicated key '%s' in DB %d\",key,db->id);\n                    serverPanic(\"Duplicated key found in RDB file\");\n                }\n            }\n\n            /* Set the expire time if needed */\n            if (expiretime != -1) {\n                initStaticStringObject(keyobj,key);\n                setExpire(NULL,db,&keyobj,expiretime);\n            }\n\n            /* Set usage information (for eviction). */\n            objectSetLRUOrLFU(val,lfu_freq,lru_idle,lru_clock,1000);\n        }\n\n        /* Loading the database more slowly is useful in order to test\n         * certain edge cases. */\n        if (server.key_load_delay) usleep(server.key_load_delay);\n\n        /* Reset the state that is key-specified and is populated by\n         * opcodes before the key, so that we start from scratch again. */\n        expiretime = -1;\n        lfu_freq = -1;\n        lru_idle = -1;\n    }\n    /* Verify the checksum if RDB version is >= 5 */\n    if (rdbver >= 5) {\n        uint64_t cksum, expected = rdb->cksum;\n\n        if (rioRead(rdb,&cksum,8) == 0) goto eoferr;\n        if (server.rdb_checksum) {\n            memrev64ifbe(&cksum);\n            if (cksum == 0) {\n                serverLog(LL_WARNING,\"RDB file was saved with checksum disabled: no check performed.\");\n            } else if (cksum != expected) {\n                serverLog(LL_WARNING,\"Wrong RDB checksum expected: (%llx) but \"\n                    \"got (%llx). Aborting now.\",\n                        (unsigned long long)expected,\n                        (unsigned long long)cksum);\n                rdbExitReportCorruptRDB(\"RDB CRC error\");\n            }\n        }\n    }\n    return C_OK;\n\n    /* Unexpected end of file is handled here calling rdbReportReadError():\n     * this will in turn either abort Redis in most cases, or if we are loading\n     * the RDB file from a socket during initial SYNC (diskless replica mode),\n     * we'll report the error to the caller, so that we can retry. */\neoferr:\n    serverLog(LL_WARNING,\n        \"Short read or OOM loading DB. Unrecoverable error, aborting now.\");\n    rdbReportReadError(\"Unexpected EOF reading RDB file\");\n    return C_ERR;\n}\n\n/* Like rdbLoadRio() but takes a filename instead of a rio stream. The\n * filename is open for reading and a rio stream object created in order\n * to do the actual loading. Moreover the ETA displayed in the INFO\n * output is initialized and finalized.\n *\n * If you pass an 'rsi' structure initialied with RDB_SAVE_OPTION_INIT, the\n * loading code will fiil the information fields in the structure. */\nint rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags) {\n    FILE *fp;\n    rio rdb;\n    int retval;\n\n    if ((fp = fopen(filename,\"r\")) == NULL) return C_ERR;\n    startLoadingFile(fp, filename,rdbflags);\n    rioInitWithFile(&rdb,fp);\n    retval = rdbLoadRio(&rdb,rdbflags,rsi);\n    fclose(fp);\n    stopLoading(retval==C_OK);\n    return retval;\n}\n\n/* A background saving child (BGSAVE) terminated its work. Handle this.\n * This function covers the case of actual BGSAVEs. */\nvoid backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {\n    if (!bysignal && exitcode == 0) {\n        serverLog(LL_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 = C_OK;\n    } else if (!bysignal && exitcode != 0) {\n        serverLog(LL_WARNING, \"Background saving error\");\n        server.lastbgsave_status = C_ERR;\n    } else {\n        mstime_t latency;\n\n        serverLog(LL_WARNING,\n            \"Background saving terminated by signal %d\", bysignal);\n        latencyStartMonitor(latency);\n        rdbRemoveTempFile(server.rdb_child_pid);\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"rdb-unlink-temp-file\",latency);\n        /* SIGUSR1 is whitelisted, so we have a way to kill a child without\n         * tirggering an error condition. */\n        if (bysignal != SIGUSR1)\n            server.lastbgsave_status = C_ERR;\n    }\n    server.rdb_child_pid = -1;\n    server.rdb_child_type = RDB_CHILD_TYPE_NONE;\n    server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;\n    server.rdb_save_time_start = -1;\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    updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? C_OK : C_ERR, RDB_CHILD_TYPE_DISK);\n}\n\n/* A background saving child (BGSAVE) terminated its work. Handle this.\n * This function covers the case of RDB -> Slaves socket transfers for\n * diskless replication. */\nvoid backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {\n    if (!bysignal && exitcode == 0) {\n        serverLog(LL_NOTICE,\n            \"Background RDB transfer terminated with success\");\n    } else if (!bysignal && exitcode != 0) {\n        serverLog(LL_WARNING, \"Background transfer error\");\n    } else {\n        serverLog(LL_WARNING,\n            \"Background transfer terminated by signal %d\", bysignal);\n    }\n    server.rdb_child_pid = -1;\n    server.rdb_child_type = RDB_CHILD_TYPE_NONE;\n    server.rdb_save_time_start = -1;\n\n    updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? C_OK : C_ERR, RDB_CHILD_TYPE_SOCKET);\n}\n\n/* When a background RDB saving/transfer terminates, call the right handler. */\nvoid backgroundSaveDoneHandler(int exitcode, int bysignal) {\n    switch(server.rdb_child_type) {\n    case RDB_CHILD_TYPE_DISK:\n        backgroundSaveDoneHandlerDisk(exitcode,bysignal);\n        break;\n    case RDB_CHILD_TYPE_SOCKET:\n        backgroundSaveDoneHandlerSocket(exitcode,bysignal);\n        break;\n    default:\n        serverPanic(\"Unknown RDB child type.\");\n        break;\n    }\n}\n\n/* Kill the RDB saving child using SIGUSR1 (so that the parent will know\n * the child did not exit for an error, but because we wanted), and performs\n * the cleanup needed. */\nvoid killRDBChild(void) {\n    kill(server.rdb_child_pid,SIGUSR1);\n    rdbRemoveTempFile(server.rdb_child_pid);\n    closeChildInfoPipe();\n    updateDictResizePolicy();\n}\n\n/* Spawn an RDB child that writes the RDB to the sockets of the slaves\n * that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */\nint rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {\n    listNode *ln;\n    listIter li;\n    pid_t childpid;\n    int pipefds[2];\n\n    if (hasActiveChildProcess()) return C_ERR;\n\n    /* Even if the previous fork child exited, don't start a new one until we\n     * drained the pipe. */\n    if (server.rdb_pipe_conns) return C_ERR;\n\n    /* Before to fork, create a pipe that is used to transfer the rdb bytes to\n     * the parent, we can't let it write directly to the sockets, since in case\n     * of TLS we must let the parent handle a continuous TLS state when the\n     * child terminates and parent takes over. */\n    if (pipe(pipefds) == -1) return C_ERR;\n    server.rdb_pipe_read = pipefds[0];\n    server.rdb_pipe_write = pipefds[1];\n    anetNonBlock(NULL, server.rdb_pipe_read);\n\n    /* Collect the connections of the replicas we want to transfer\n     * the RDB to, which are i WAIT_BGSAVE_START state. */\n    server.rdb_pipe_conns = zmalloc(sizeof(connection *)*listLength(server.slaves));\n    server.rdb_pipe_numconns = 0;\n    server.rdb_pipe_numconns_writing = 0;\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n            server.rdb_pipe_conns[server.rdb_pipe_numconns++] = slave->conn;\n            replicationSetupSlaveForFullResync(slave,getPsyncInitialOffset());\n        }\n    }\n\n    /* Create the child process. */\n    openChildInfoPipe();\n    if ((childpid = redisFork()) == 0) {\n        /* Child */\n        int retval;\n        rio rdb;\n\n        rioInitWithFd(&rdb,server.rdb_pipe_write);\n\n        redisSetProcTitle(\"redis-rdb-to-slaves\");\n        redisSetCpuAffinity(server.bgsave_cpulist);\n\n        retval = rdbSaveRioWithEOFMark(&rdb,NULL,rsi);\n        if (retval == C_OK && rioFlush(&rdb) == 0)\n            retval = C_ERR;\n\n        if (retval == C_OK) {\n            sendChildCOWInfo(CHILD_INFO_TYPE_RDB, \"RDB\");\n        }\n\n        rioFreeFd(&rdb);\n        close(server.rdb_pipe_write); /* wake up the reader, tell it we're done. */\n        exitFromChild((retval == C_OK) ? 0 : 1);\n    } else {\n        /* Parent */\n        if (childpid == -1) {\n            serverLog(LL_WARNING,\"Can't save in background: fork: %s\",\n                strerror(errno));\n\n            /* Undo the state change. The caller will perform cleanup on\n             * all the slaves in BGSAVE_START state, but an early call to\n             * replicationSetupSlaveForFullResync() turned it into BGSAVE_END */\n            listRewind(server.slaves,&li);\n            while((ln = listNext(&li))) {\n                client *slave = ln->value;\n                if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {\n                    slave->replstate = SLAVE_STATE_WAIT_BGSAVE_START;\n                }\n            }\n            close(server.rdb_pipe_write);\n            close(server.rdb_pipe_read);\n            zfree(server.rdb_pipe_conns);\n            server.rdb_pipe_conns = NULL;\n            server.rdb_pipe_numconns = 0;\n            server.rdb_pipe_numconns_writing = 0;\n            closeChildInfoPipe();\n        } else {\n            serverLog(LL_NOTICE,\"Background RDB transfer started by pid %d\",\n                childpid);\n            server.rdb_save_time_start = time(NULL);\n            server.rdb_child_pid = childpid;\n            server.rdb_child_type = RDB_CHILD_TYPE_SOCKET;\n            close(server.rdb_pipe_write); /* close write in parent so that it can detect the close on the child. */\n            if (aeCreateFileEvent(server.el, server.rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {\n                serverPanic(\"Unrecoverable error creating server.rdb_pipe_read file event.\");\n            }\n        }\n        return (childpid == -1) ? C_ERR : C_OK;\n    }\n    return C_OK; /* Unreached. */\n}\n\nvoid saveCommand(client *c) {\n    if (server.rdb_child_pid != -1) {\n        addReplyError(c,\"Background save already in progress\");\n        return;\n    }\n    rdbSaveInfo rsi, *rsiptr;\n    rsiptr = rdbPopulateSaveInfo(&rsi);\n    if (rdbSave(server.rdb_filename,rsiptr) == C_OK) {\n        addReply(c,shared.ok);\n    } else {\n        addReply(c,shared.err);\n    }\n}\n\n/* BGSAVE [SCHEDULE] */\nvoid bgsaveCommand(client *c) {\n    int schedule = 0;\n\n    /* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite\n     * is in progress. Instead of returning an error a BGSAVE gets scheduled. */\n    if (c->argc > 1) {\n        if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"schedule\")) {\n            schedule = 1;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    rdbSaveInfo rsi, *rsiptr;\n    rsiptr = rdbPopulateSaveInfo(&rsi);\n\n    if (server.rdb_child_pid != -1) {\n        addReplyError(c,\"Background save already in progress\");\n    } else if (hasActiveChildProcess()) {\n        if (schedule) {\n            server.rdb_bgsave_scheduled = 1;\n            addReplyStatus(c,\"Background saving scheduled\");\n        } else {\n            addReplyError(c,\n            \"Another child process is active (AOF?): can't BGSAVE right now. \"\n            \"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever \"\n            \"possible.\");\n        }\n    } else if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK) {\n        addReplyStatus(c,\"Background saving started\");\n    } else {\n        addReply(c,shared.err);\n    }\n}\n\n/* Populate the rdbSaveInfo structure used to persist the replication\n * information inside the RDB file. Currently the structure explicitly\n * contains just the currently selected DB from the master stream, however\n * if the rdbSave*() family functions receive a NULL rsi structure also\n * the Replication ID/offset is not saved. The function popultes 'rsi'\n * that is normally stack-allocated in the caller, returns the populated\n * pointer if the instance has a valid master client, otherwise NULL\n * is returned, and the RDB saving will not persist any replication related\n * information. */\nrdbSaveInfo *rdbPopulateSaveInfo(rdbSaveInfo *rsi) {\n    rdbSaveInfo rsi_init = RDB_SAVE_INFO_INIT;\n    *rsi = rsi_init;\n\n    /* If the instance is a master, we can populate the replication info\n     * only when repl_backlog is not NULL. If the repl_backlog is NULL,\n     * it means that the instance isn't in any replication chains. In this\n     * scenario the replication info is useless, because when a slave\n     * connects to us, the NULL repl_backlog will trigger a full\n     * synchronization, at the same time we will use a new replid and clear\n     * replid2. */\n    if (!server.masterhost && server.repl_backlog) {\n        /* Note that when server.slaveseldb is -1, it means that this master\n         * didn't apply any write commands after a full synchronization.\n         * So we can let repl_stream_db be 0, this allows a restarted slave\n         * to reload replication ID/offset, it's safe because the next write\n         * command must generate a SELECT statement. */\n        rsi->repl_stream_db = server.slaveseldb == -1 ? 0 : server.slaveseldb;\n        return rsi;\n    }\n\n    /* If the instance is a slave we need a connected master\n     * in order to fetch the currently selected DB. */\n    if (server.master) {\n        rsi->repl_stream_db = server.master->db->id;\n        return rsi;\n    }\n\n    /* If we have a cached master we can use it in order to populate the\n     * replication selected DB info inside the RDB file: the slave can\n     * increment the master_repl_offset only from data arriving from the\n     * master, so if we are disconnected the offset in the cached master\n     * is valid. */\n    if (server.cached_master) {\n        rsi->repl_stream_db = server.cached_master->db->id;\n        return rsi;\n    }\n    return NULL;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 __RDB_H\n#define __RDB_H\n\n#include <stdio.h>\n#include \"rio.h\"\n\n/* TBD: include only necessary headers. */\n#include \"server.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#define RDB_VERSION 9\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|XXXXXX => if the two MSB are 00 the len is the 6 bits of this byte\n * 01|XXXXXX XXXXXXXX =>  01, the len is 14 byes, 6 bits + 8 bits of next byte\n * 10|000000 [32 bit integer] => A full 32 bit len in net byte order will follow\n * 10|000001 [64 bit integer] => A full 64 bit len in net byte order will follow\n * 11|OBKIND this means: specially encoded object will follow. The six bits\n *           number specify the kind of object that follows.\n *           See the RDB_ENC_* defines.\n *\n * Lengths up to 63 are stored using a single byte, most DB keys, and may\n * values, will fit inside. */\n#define RDB_6BITLEN 0\n#define RDB_14BITLEN 1\n#define RDB_32BITLEN 0x80\n#define RDB_64BITLEN 0x81\n#define RDB_ENCVAL 3\n#define RDB_LENERR UINT64_MAX\n\n/* When a length of a string object stored on disk has the first two bits\n * set, the remaining six bits specify a special encoding for the object\n * accordingly to the following defines: */\n#define RDB_ENC_INT8 0        /* 8 bit signed integer */\n#define RDB_ENC_INT16 1       /* 16 bit signed integer */\n#define RDB_ENC_INT32 2       /* 32 bit signed integer */\n#define RDB_ENC_LZF 3         /* string compressed with FASTLZ */\n\n/* Map object types to RDB object types. Macros starting with OBJ_ are for\n * memory storage and may change. Instead RDB types must be fixed because\n * we store them on disk. */\n#define RDB_TYPE_STRING 0\n#define RDB_TYPE_LIST   1\n#define RDB_TYPE_SET    2\n#define RDB_TYPE_ZSET   3\n#define RDB_TYPE_HASH   4\n#define RDB_TYPE_ZSET_2 5 /* ZSET version 2 with doubles stored in binary. */\n#define RDB_TYPE_MODULE 6\n#define RDB_TYPE_MODULE_2 7 /* Module value with annotations for parsing without\n                               the generating module being loaded. */\n/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */\n\n/* Object types for encoded objects. */\n#define RDB_TYPE_HASH_ZIPMAP    9\n#define RDB_TYPE_LIST_ZIPLIST  10\n#define RDB_TYPE_SET_INTSET    11\n#define RDB_TYPE_ZSET_ZIPLIST  12\n#define RDB_TYPE_HASH_ZIPLIST  13\n#define RDB_TYPE_LIST_QUICKLIST 14\n#define RDB_TYPE_STREAM_LISTPACKS 15\n/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */\n\n/* Test if a type is an object type. */\n#define rdbIsObjectType(t) ((t >= 0 && t <= 7) || (t >= 9 && t <= 15))\n\n/* Special RDB opcodes (saved/loaded with rdbSaveType/rdbLoadType). */\n#define RDB_OPCODE_MODULE_AUX 247   /* Module auxiliary data. */\n#define RDB_OPCODE_IDLE       248   /* LRU idle time. */\n#define RDB_OPCODE_FREQ       249   /* LFU frequency. */\n#define RDB_OPCODE_AUX        250   /* RDB aux field. */\n#define RDB_OPCODE_RESIZEDB   251   /* Hash table resize hint. */\n#define RDB_OPCODE_EXPIRETIME_MS 252    /* Expire time in milliseconds. */\n#define RDB_OPCODE_EXPIRETIME 253       /* Old expire time in seconds. */\n#define RDB_OPCODE_SELECTDB   254   /* DB number of the following keys. */\n#define RDB_OPCODE_EOF        255   /* End of the RDB file. */\n\n/* Module serialized values sub opcodes */\n#define RDB_MODULE_OPCODE_EOF   0   /* End of module value. */\n#define RDB_MODULE_OPCODE_SINT  1   /* Signed integer. */\n#define RDB_MODULE_OPCODE_UINT  2   /* Unsigned integer. */\n#define RDB_MODULE_OPCODE_FLOAT 3   /* Float. */\n#define RDB_MODULE_OPCODE_DOUBLE 4  /* Double. */\n#define RDB_MODULE_OPCODE_STRING 5  /* String. */\n\n/* rdbLoad...() functions flags. */\n#define RDB_LOAD_NONE   0\n#define RDB_LOAD_ENC    (1<<0)\n#define RDB_LOAD_PLAIN  (1<<1)\n#define RDB_LOAD_SDS    (1<<2)\n\n/* flags on the purpose of rdb save or load */\n#define RDBFLAGS_NONE 0                 /* No special RDB loading. */\n#define RDBFLAGS_AOF_PREAMBLE (1<<0)    /* Load/save the RDB as AOF preamble. */\n#define RDBFLAGS_REPLICATION (1<<1)     /* Load/save for SYNC. */\n#define RDBFLAGS_ALLOW_DUP (1<<2)       /* Allow duplicated keys when loading.*/\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, uint64_t len);\nint rdbSaveMillisecondTime(rio *rdb, long long t);\nlong long rdbLoadMillisecondTime(rio *rdb, int rdbver);\nuint64_t rdbLoadLen(rio *rdb, int *isencoded);\nint rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr);\nint rdbSaveObjectType(rio *rdb, robj *o);\nint rdbLoadObjectType(rio *rdb);\nint rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags);\nint rdbSaveBackground(char *filename, rdbSaveInfo *rsi);\nint rdbSaveToSlavesSockets(rdbSaveInfo *rsi);\nvoid rdbRemoveTempFile(pid_t childpid);\nint rdbSave(char *filename, rdbSaveInfo *rsi);\nssize_t rdbSaveObject(rio *rdb, robj *o, robj *key);\nsize_t rdbSavedObjectLen(robj *o, robj *key);\nrobj *rdbLoadObject(int type, rio *rdb, sds key);\nvoid backgroundSaveDoneHandler(int exitcode, int bysignal);\nint rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime);\nssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt);\nrobj *rdbLoadStringObject(rio *rdb);\nssize_t rdbSaveStringObject(rio *rdb, robj *obj);\nssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len);\nvoid *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr);\nint rdbSaveBinaryDoubleValue(rio *rdb, double val);\nint rdbLoadBinaryDoubleValue(rio *rdb, double *val);\nint rdbSaveBinaryFloatValue(rio *rdb, float val);\nint rdbLoadBinaryFloatValue(rio *rdb, float *val);\nint rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi);\nint rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi);\nrdbSaveInfo *rdbPopulateSaveInfo(rdbSaveInfo *rsi);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <math.h>\n#include <pthread.h>\n\n#include <sds.h> /* Use hiredis sds. */\n#include \"ae.h\"\n#include \"hiredis.h\"\n#include \"adlist.h\"\n#include \"dict.h\"\n#include \"zmalloc.h\"\n#include \"atomicvar.h\"\n#include \"crc16_slottable.h\"\n\n#define UNUSED(V) ((void) V)\n#define RANDPTR_INITIAL_SIZE 8\n#define MAX_LATENCY_PRECISION 3\n#define MAX_THREADS 500\n#define CLUSTER_SLOTS 16384\n\n#define CLIENT_GET_EVENTLOOP(c) \\\n    (c->thread_id >= 0 ? config.threads[c->thread_id]->el : config.el)\n\nstruct benchmarkThread;\nstruct clusterNode;\nstruct redisConfig;\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    int showerrors;\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    char *auth;\n    const char *user;\n    int precision;\n    int num_threads;\n    struct benchmarkThread **threads;\n    int cluster_mode;\n    int cluster_node_count;\n    struct clusterNode **cluster_nodes;\n    struct redisConfig *redis_config;\n    int is_fetching_slots;\n    int is_updating_slots;\n    int slots_last_update;\n    int enable_tracking;\n    /* Thread mutexes to be used as fallbacks by atomicvar.h */\n    pthread_mutex_t requests_issued_mutex;\n    pthread_mutex_t requests_finished_mutex;\n    pthread_mutex_t liveclients_mutex;\n    pthread_mutex_t is_fetching_slots_mutex;\n    pthread_mutex_t is_updating_slots_mutex;\n    pthread_mutex_t updating_slots_mutex;\n    pthread_mutex_t slots_last_update_mutex;\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    char **stagptr;         /* Pointers to slot hashtags (cluster mode only) */\n    size_t staglen;         /* Number of pointers in client->stagptr */\n    size_t stagfree;        /* Number of unused pointers in client->stagptr */\n    size_t written;         /* Bytes of 'obuf' already written */\n    long long start;        /* Start time of a request */\n    long long latency;      /* Request latency */\n    int pending;            /* Number of pending requests (replies to consume) */\n    int prefix_pending;     /* If non-zero, number of pending prefix commands. Commands\n                               such as auth and select are prefixed to the pipeline of\n                               benchmark commands and discarded after the first send. */\n    int prefixlen;          /* Size in bytes of the pending prefix commands */\n    int thread_id;\n    struct clusterNode *cluster_node;\n    int slots_last_update;\n} *client;\n\n/* Threads. */\n\ntypedef struct benchmarkThread {\n    int index;\n    pthread_t thread;\n    aeEventLoop *el;\n} benchmarkThread;\n\n/* Cluster. */\ntypedef struct clusterNode {\n    char *ip;\n    int port;\n    sds name;\n    int flags;\n    sds replicate;  /* Master ID if node is a slave */\n    int *slots;\n    int slots_count;\n    int current_slot_index;\n    int *updated_slots;         /* Used by updateClusterSlotsConfiguration */\n    int updated_slots_count;    /* Used by updateClusterSlotsConfiguration */\n    int replicas_count;\n    sds *migrating; /* An array of sds where even strings are slots and odd\n                     * strings are the destination node IDs. */\n    sds *importing; /* An array of sds where even strings are slots and odd\n                     * strings are the source node IDs. */\n    int migrating_count; /* Length of the migrating array (migrating slots*2) */\n    int importing_count; /* Length of the importing array (importing slots*2) */\n    struct redisConfig *redis_config;\n} clusterNode;\n\ntypedef struct redisConfig {\n    sds save;\n    sds appendonly;\n} redisConfig;\n\n/* Prototypes */\nstatic void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nstatic void createMissingClients(client c);\nstatic benchmarkThread *createBenchmarkThread(int index);\nstatic void freeBenchmarkThread(benchmarkThread *thread);\nstatic void freeBenchmarkThreads();\nstatic void *execBenchmarkThread(void *ptr);\nstatic clusterNode *createClusterNode(char *ip, int port);\nstatic redisConfig *getRedisConfig(const char *ip, int port,\n                                   const char *hostsocket);\nstatic void freeRedisConfig(redisConfig *cfg);\nstatic int fetchClusterSlotsConfiguration(client c);\nstatic void updateClusterSlotsConfiguration();\nint showThroughput(struct aeEventLoop *eventLoop, long long id,\n                   void *clientData);\n\n/* Dict callbacks */\nstatic uint64_t dictSdsHash(const void *key);\nstatic int dictSdsKeyCompare(void *privdata, const void *key1,\n    const void *key2);\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 uint64_t dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nstatic int 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/* _serverAssert is needed by dict */\nvoid _serverAssert(const char *estr, const char *file, int line) {\n    fprintf(stderr, \"=== ASSERTION FAILED ===\");\n    fprintf(stderr, \"==> %s:%d '%s' is not true\",file,line,estr);\n    *((char*)-1) = 'x';\n}\n\nstatic redisConfig *getRedisConfig(const char *ip, int port,\n                                   const char *hostsocket)\n{\n    redisConfig *cfg = zcalloc(sizeof(*cfg));\n    if (!cfg) return NULL;\n    redisContext *c = NULL;\n    redisReply *reply = NULL, *sub_reply = NULL;\n    if (hostsocket == NULL)\n        c = redisConnect(ip, port);\n    else\n        c = redisConnectUnix(hostsocket);\n    if (c == NULL || c->err) {\n        fprintf(stderr,\"Could not connect to Redis at \");\n        char *err = (c != NULL ? c->errstr : \"\");\n        if (hostsocket == NULL) fprintf(stderr,\"%s:%d: %s\\n\",ip,port,err);\n        else fprintf(stderr,\"%s: %s\\n\",hostsocket,err);\n        goto fail;\n    }\n\n    if(config.auth) {\n        void *authReply = NULL;\n        if (config.user == NULL)\n            redisAppendCommand(c, \"AUTH %s\", config.auth);\n        else\n            redisAppendCommand(c, \"AUTH %s %s\", config.user, config.auth);\n        if (REDIS_OK != redisGetReply(c, &authReply)) goto fail;\n        if (reply) freeReplyObject(reply);\n        reply = ((redisReply *) authReply);\n        if (reply->type == REDIS_REPLY_ERROR) {\n            fprintf(stderr, \"ERROR: %s\\n\", reply->str);\n            goto fail;\n        }\n    }\n\n    redisAppendCommand(c, \"CONFIG GET %s\", \"save\");\n    redisAppendCommand(c, \"CONFIG GET %s\", \"appendonly\");\n    int i = 0;\n    void *r = NULL;\n    for (; i < 2; i++) {\n        int res = redisGetReply(c, &r);\n        if (reply) freeReplyObject(reply);\n        reply = res == REDIS_OK ? ((redisReply *) r) : NULL;\n        if (res != REDIS_OK || !r) goto fail;\n        if (reply->type == REDIS_REPLY_ERROR) {\n            fprintf(stderr, \"ERROR: %s\\n\", reply->str);\n            goto fail;\n        }\n        if (reply->type != REDIS_REPLY_ARRAY || reply->elements < 2) goto fail;\n        sub_reply = reply->element[1];\n        char *value = sub_reply->str;\n        if (!value) value = \"\";\n        switch (i) {\n        case 0: cfg->save = sdsnew(value); break;\n        case 1: cfg->appendonly = sdsnew(value); break;\n        }\n    }\n    freeReplyObject(reply);\n    redisFree(c);\n    return cfg;\nfail:\n    fprintf(stderr, \"ERROR: failed to fetch CONFIG from \");\n    if (hostsocket == NULL) fprintf(stderr, \"%s:%d\\n\", ip, port);\n    else fprintf(stderr, \"%s\\n\", hostsocket);\n    freeReplyObject(reply);\n    redisFree(c);\n    zfree(cfg);\n    return NULL;\n}\nstatic void freeRedisConfig(redisConfig *cfg) {\n    if (cfg->save) sdsfree(cfg->save);\n    if (cfg->appendonly) sdsfree(cfg->appendonly);\n    zfree(cfg);\n}\n\nstatic void freeClient(client c) {\n    aeEventLoop *el = CLIENT_GET_EVENTLOOP(c);\n    listNode *ln;\n    aeDeleteFileEvent(el,c->context->fd,AE_WRITABLE);\n    aeDeleteFileEvent(el,c->context->fd,AE_READABLE);\n    if (c->thread_id >= 0) {\n        int requests_finished = 0;\n        atomicGet(config.requests_finished, requests_finished);\n        if (requests_finished >= config.requests) {\n            aeStop(el);\n        }\n    }\n    redisFree(c->context);\n    sdsfree(c->obuf);\n    zfree(c->randptr);\n    zfree(c->stagptr);\n    zfree(c);\n    if (config.num_threads) pthread_mutex_lock(&(config.liveclients_mutex));\n    config.liveclients--;\n    ln = listSearchKey(config.clients,c);\n    assert(ln != NULL);\n    listDelNode(config.clients,ln);\n    if (config.num_threads) pthread_mutex_unlock(&(config.liveclients_mutex));\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    aeEventLoop *el = CLIENT_GET_EVENTLOOP(c);\n    aeDeleteFileEvent(el,c->context->fd,AE_WRITABLE);\n    aeDeleteFileEvent(el,c->context->fd,AE_READABLE);\n    aeCreateFileEvent(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 = 0;\n        if (config.randomkeys_keyspacelen != 0)\n            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 setClusterKeyHashTag(client c) {\n    assert(c->thread_id >= 0);\n    clusterNode *node = c->cluster_node;\n    assert(node);\n    assert(node->current_slot_index < node->slots_count);\n    int is_updating_slots = 0;\n    atomicGet(config.is_updating_slots, is_updating_slots);\n    /* If updateClusterSlotsConfiguration is updating the slots array,\n     * call updateClusterSlotsConfiguration is order to block the thread\n     * since the mutex is locked. When the slots will be updated by the\n     * thread that's actually performing the update, the execution of\n     * updateClusterSlotsConfiguration won't actually do anything, since\n     * the updated_slots_count array will be already NULL. */\n    if (is_updating_slots) updateClusterSlotsConfiguration();\n    int slot = node->slots[node->current_slot_index];\n    const char *tag = crc16_slot_table[slot];\n    int taglen = strlen(tag);\n    size_t i;\n    for (i = 0; i < c->staglen; i++) {\n        char *p = c->stagptr[i] + 1;\n        p[0] = tag[0];\n        p[1] = (taglen >= 2 ? tag[1] : '}');\n        p[2] = (taglen == 3 ? tag[2] : '}');\n    }\n}\n\nstatic void clientDone(client c) {\n    int requests_finished = 0;\n    atomicGet(config.requests_finished, requests_finished);\n    if (requests_finished >= config.requests) {\n        freeClient(c);\n        if (!config.num_threads && config.el) aeStop(config.el);\n        return;\n    }\n    if (config.keepalive) {\n        resetClient(c);\n    } else {\n        if (config.num_threads) pthread_mutex_lock(&(config.liveclients_mutex));\n        config.liveclients--;\n        createMissingClients(c);\n        config.liveclients++;\n        if (config.num_threads)\n            pthread_mutex_unlock(&(config.liveclients_mutex));\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    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Calculate latency only for the first read event. This means that the\n     * server already sent the reply and we need to parse it. Parsing overhead\n     * is not part of the latency, so calculate it only once, here. */\n    if (c->latency < 0) c->latency = 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                redisReply *r = reply;\n                int is_err = (r->type == REDIS_REPLY_ERROR);\n\n                if (is_err && config.showerrors) {\n                    /* TODO: static lasterr_time not thread-safe */\n                    static time_t lasterr_time = 0;\n                    time_t now = time(NULL);\n                    if (lasterr_time != now) {\n                        lasterr_time = now;\n                        if (c->cluster_node) {\n                            printf(\"Error from server %s:%d: %s\\n\",\n                                   c->cluster_node->ip,\n                                   c->cluster_node->port,\n                                   r->str);\n                        } else printf(\"Error from server: %s\\n\", r->str);\n                    }\n                }\n\n                /* Try to update slots configuration if reply error is\n                 * MOVED/ASK/CLUSTERDOWN and the key(s) used by the command\n                 * contain(s) the slot hash tag. */\n                if (is_err && c->cluster_node && c->staglen) {\n                    int fetch_slots = 0, do_wait = 0;\n                    if (!strncmp(r->str,\"MOVED\",5) || !strncmp(r->str,\"ASK\",3))\n                        fetch_slots = 1;\n                    else if (!strncmp(r->str,\"CLUSTERDOWN\",11)) {\n                        /* Usually the cluster is able to recover itself after\n                         * a CLUSTERDOWN error, so try to sleep one second\n                         * before requesting the new configuration. */\n                        fetch_slots = 1;\n                        do_wait = 1;\n                        printf(\"Error from server %s:%d: %s\\n\",\n                               c->cluster_node->ip,\n                               c->cluster_node->port,\n                               r->str);\n                    }\n                    if (do_wait) sleep(1);\n                    if (fetch_slots && !fetchClusterSlotsConfiguration(c))\n                        exit(1);\n                }\n\n                freeReplyObject(reply);\n                /* This is an OK for prefix commands such as auth and select.*/\n                if (c->prefix_pending > 0) {\n                    c->prefix_pending--;\n                    c->pending--;\n                    /* Discard prefix commands on first response.*/\n                    if (c->prefixlen > 0) {\n                        size_t j;\n                        sdsrange(c->obuf, c->prefixlen, -1);\n                        /* We also need to fix the pointers to the strings\n                        * we need to randomize. */\n                        for (j = 0; j < c->randlen; j++)\n                            c->randptr[j] -= c->prefixlen;\n                        c->prefixlen = 0;\n                    }\n                    continue;\n                }\n                int requests_finished = 0;\n                atomicGetIncr(config.requests_finished, requests_finished, 1);\n                if (requests_finished < config.requests)\n                    config.latency[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    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Initialize request when nothing was written. */\n    if (c->written == 0) {\n        /* Enforce upper bound to number of requests. */\n        int requests_issued = 0;\n        atomicGetIncr(config.requests_issued, requests_issued, 1);\n        if (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        if (config.cluster_mode && c->staglen > 0) setClusterKeyHashTag(c);\n        atomicGet(config.slots_last_update, c->slots_last_update);\n        c->start = ustime();\n        c->latency = -1;\n    }\n    if (sdslen(c->obuf) > c->written) {\n        void *ptr = c->obuf+c->written;\n        ssize_t 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(el,c->context->fd,AE_WRITABLE);\n            aeCreateFileEvent(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, prefix commands are applied if needed.*/\nstatic client createClient(char *cmd, size_t len, client from, int thread_id) {\n    int j;\n    int is_cluster_client = (config.cluster_mode && thread_id >= 0);\n    client c = zmalloc(sizeof(struct _client));\n\n    const char *ip = NULL;\n    int port = 0;\n    c->cluster_node = NULL;\n    if (config.hostsocket == NULL || is_cluster_client) {\n        if (!is_cluster_client) {\n            ip = config.hostip;\n            port = config.hostport;\n        } else {\n            int node_idx = 0;\n            if (config.num_threads < config.cluster_node_count)\n                node_idx = config.liveclients % config.cluster_node_count;\n            else\n                node_idx = thread_id % config.cluster_node_count;\n            clusterNode *node = config.cluster_nodes[node_idx];\n            assert(node != NULL);\n            ip = (const char *) node->ip;\n            port = node->port;\n            c->cluster_node = node;\n        }\n        c->context = redisConnectNonBlock(ip,port);\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 || is_cluster_client)\n            fprintf(stderr,\"%s:%d: %s\\n\",ip,port,c->context->errstr);\n        else\n            fprintf(stderr,\"%s: %s\\n\",config.hostsocket,c->context->errstr);\n        exit(1);\n    }\n    c->thread_id = thread_id;\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    /* Prefix the request buffer with AUTH and/or SELECT commands, if applicable.\n     * These commands are discarded after the first response, so if the client is\n     * reused the commands will not be used again. */\n    c->prefix_pending = 0;\n    if (config.auth) {\n        char *buf = NULL;\n        int len;\n        if (config.user == NULL)\n            len = redisFormatCommand(&buf, \"AUTH %s\", config.auth);\n        else\n            len = redisFormatCommand(&buf, \"AUTH %s %s\",\n                                     config.user, config.auth);\n        c->obuf = sdscatlen(c->obuf, buf, len);\n        free(buf);\n        c->prefix_pending++;\n    }\n\n    if (config.enable_tracking) {\n        char *buf = NULL;\n        int len = redisFormatCommand(&buf, \"CLIENT TRACKING on\");\n        c->obuf = sdscatlen(c->obuf, buf, len);\n        free(buf);\n        c->prefix_pending++;\n    }\n\n    /* If a DB number different than zero is selected, prefix our request\n     * buffer with the SELECT command, that will be discarded the first\n     * time the replies are received, so if the client is reused the\n     * SELECT command will not be used again. */\n    if (config.dbnum != 0 && !is_cluster_client) {\n        c->obuf = sdscatprintf(c->obuf,\"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n            (int)sdslen(config.dbnumstr),config.dbnumstr);\n        c->prefix_pending++;\n    }\n    c->prefixlen = sdslen(c->obuf);\n    /* Append the request itself. */\n    if (from) {\n        c->obuf = sdscatlen(c->obuf,\n            from->obuf+from->prefixlen,\n            sdslen(from->obuf)-from->prefixlen);\n    } else {\n        for (j = 0; j < config.pipeline; j++)\n            c->obuf = sdscatlen(c->obuf,cmd,len);\n    }\n\n    c->written = 0;\n    c->pending = config.pipeline+c->prefix_pending;\n    c->randptr = NULL;\n    c->randlen = 0;\n    c->stagptr = NULL;\n    c->staglen = 0;\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 < (int)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->prefixlen - from->prefixlen;\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    /* If cluster mode is enabled, set slot hashtags pointers. */\n    if (config.cluster_mode) {\n        if (from) {\n            c->staglen = from->staglen;\n            c->stagfree = 0;\n            c->stagptr = zmalloc(sizeof(char*)*c->staglen);\n            /* copy the offsets. */\n            for (j = 0; j < (int)c->staglen; j++) {\n                c->stagptr[j] = c->obuf + (from->stagptr[j]-from->obuf);\n                /* Adjust for the different select prefix length. */\n                c->stagptr[j] += c->prefixlen - from->prefixlen;\n            }\n        } else {\n            char *p = c->obuf;\n\n            c->staglen = 0;\n            c->stagfree = RANDPTR_INITIAL_SIZE;\n            c->stagptr = zmalloc(sizeof(char*)*c->stagfree);\n            while ((p = strstr(p,\"{tag}\")) != NULL) {\n                if (c->stagfree == 0) {\n                    c->stagptr = zrealloc(c->stagptr,\n                                          sizeof(char*) * c->staglen*2);\n                    c->stagfree += c->staglen;\n                }\n                c->stagptr[c->staglen++] = p;\n                c->stagfree--;\n                p += 5; /* 12 is strlen(\"{tag}\"). */\n            }\n        }\n    }\n    aeEventLoop *el = NULL;\n    if (thread_id < 0) el = config.el;\n    else {\n        benchmarkThread *thread = config.threads[thread_id];\n        el = thread->el;\n    }\n    if (config.idlemode == 0)\n        aeCreateFileEvent(el,c->context->fd,AE_WRITABLE,writeHandler,c);\n    listAddNodeTail(config.clients,c);\n    atomicIncr(config.liveclients, 1);\n    atomicGet(config.slots_last_update, c->slots_last_update);\n    return c;\n}\n\nstatic void createMissingClients(client c) {\n    int n = 0;\n    while(config.liveclients < config.numclients) {\n        int thread_id = -1;\n        if (config.num_threads)\n            thread_id = config.liveclients % config.num_threads;\n        createClient(NULL,0,c,thread_id);\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 int ipow(int base, int exp) {\n    int result = 1;\n    while (exp) {\n        if (exp & 1) result *= base;\n        exp /= 2;\n        base *= base;\n    }\n    return result;\n}\n\nstatic void showLatencyReport(void) {\n    int i, curlat = 0;\n    int usbetweenlat = ipow(10, MAX_LATENCY_PRECISION-config.precision);\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        if (config.cluster_mode) {\n            printf(\"  cluster mode: yes (%d masters)\\n\",\n                   config.cluster_node_count);\n            int m ;\n            for (m = 0; m < config.cluster_node_count; m++) {\n                clusterNode *node =  config.cluster_nodes[m];\n                redisConfig *cfg = node->redis_config;\n                if (cfg == NULL) continue;\n                printf(\"  node [%d] configuration:\\n\",m );\n                printf(\"    save: %s\\n\",\n                    sdslen(cfg->save) ? cfg->save : \"NONE\");\n                printf(\"    appendonly: %s\\n\", cfg->appendonly);\n            }\n        } else {\n            if (config.redis_config) {\n                printf(\"  host configuration \\\"save\\\": %s\\n\",\n                       config.redis_config->save);\n                printf(\"  host configuration \\\"appendonly\\\": %s\\n\",\n                       config.redis_config->appendonly);\n            }\n        }\n        printf(\"  multi-thread: %s\\n\", (config.num_threads ? \"yes\" : \"no\"));\n        if (config.num_threads)\n            printf(\"  threads: %d\\n\", config.num_threads);\n\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]/usbetweenlat != curlat ||\n                i == (config.requests-1))\n            {\n                /* After the 2 milliseconds latency to have percentages split\n                 * by decimals will just add a lot of noise to the output. */\n                if (config.latency[i] >= 2000) {\n                    config.precision = 0;\n                    usbetweenlat = ipow(10,\n                        MAX_LATENCY_PRECISION-config.precision);\n                }\n\n                curlat = config.latency[i]/usbetweenlat;\n                perc = ((float)(i+1)*100)/config.requests;\n                printf(\"%.2f%% <= %.*f milliseconds\\n\", perc, config.precision,\n                    curlat/pow(10.0, config.precision));\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 initBenchmarkThreads() {\n    int i;\n    if (config.threads) freeBenchmarkThreads();\n    config.threads = zmalloc(config.num_threads * sizeof(benchmarkThread*));\n    for (i = 0; i < config.num_threads; i++) {\n        benchmarkThread *thread = createBenchmarkThread(i);\n        config.threads[i] = thread;\n    }\n}\n\nstatic void startBenchmarkThreads() {\n    int i;\n    for (i = 0; i < config.num_threads; i++) {\n        benchmarkThread *t = config.threads[i];\n        if (pthread_create(&(t->thread), NULL, execBenchmarkThread, t)){\n            fprintf(stderr, \"FATAL: Failed to start thread %d.\\n\", i);\n            exit(1);\n        }\n    }\n    for (i = 0; i < config.num_threads; i++)\n        pthread_join(config.threads[i]->thread, NULL);\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    if (config.num_threads) initBenchmarkThreads();\n\n    int thread_id = config.num_threads > 0 ? 0 : -1;\n    c = createClient(cmd,len,NULL,thread_id);\n    createMissingClients(c);\n\n    config.start = mstime();\n    if (!config.num_threads) aeMain(config.el);\n    else startBenchmarkThreads();\n    config.totlatency = mstime()-config.start;\n\n    showLatencyReport();\n    freeAllClients();\n    if (config.threads) freeBenchmarkThreads();\n}\n\n/* Thread functions. */\n\nstatic benchmarkThread *createBenchmarkThread(int index) {\n    benchmarkThread *thread = zmalloc(sizeof(*thread));\n    if (thread == NULL) return NULL;\n    thread->index = index;\n    thread->el = aeCreateEventLoop(1024*10);\n    aeCreateTimeEvent(thread->el,1,showThroughput,NULL,NULL);\n    return thread;\n}\n\nstatic void freeBenchmarkThread(benchmarkThread *thread) {\n    if (thread->el) aeDeleteEventLoop(thread->el);\n    zfree(thread);\n}\n\nstatic void freeBenchmarkThreads() {\n    int i = 0;\n    for (; i < config.num_threads; i++) {\n        benchmarkThread *thread = config.threads[i];\n        if (thread) freeBenchmarkThread(thread);\n    }\n    zfree(config.threads);\n    config.threads = NULL;\n}\n\nstatic void *execBenchmarkThread(void *ptr) {\n    benchmarkThread *thread = (benchmarkThread *) ptr;\n    aeMain(thread->el);\n    return NULL;\n}\n\n/* Cluster helper functions. */\n\nstatic clusterNode *createClusterNode(char *ip, int port) {\n    clusterNode *node = zmalloc(sizeof(*node));\n    if (!node) return NULL;\n    node->ip = ip;\n    node->port = port;\n    node->name = NULL;\n    node->flags = 0;\n    node->replicate = NULL;\n    node->replicas_count = 0;\n    node->slots = zmalloc(CLUSTER_SLOTS * sizeof(int));\n    node->slots_count = 0;\n    node->current_slot_index = 0;\n    node->updated_slots = NULL;\n    node->updated_slots_count = 0;\n    node->migrating = NULL;\n    node->importing = NULL;\n    node->migrating_count = 0;\n    node->importing_count = 0;\n    node->redis_config = NULL;\n    return node;\n}\n\nstatic void freeClusterNode(clusterNode *node) {\n    int i;\n    if (node->name) sdsfree(node->name);\n    if (node->replicate) sdsfree(node->replicate);\n    if (node->migrating != NULL) {\n        for (i = 0; i < node->migrating_count; i++) sdsfree(node->migrating[i]);\n        zfree(node->migrating);\n    }\n    if (node->importing != NULL) {\n        for (i = 0; i < node->importing_count; i++) sdsfree(node->importing[i]);\n        zfree(node->importing);\n    }\n    /* If the node is not the reference node, that uses the address from\n     * config.hostip and config.hostport, then the node ip has been\n     * allocated by fetchClusterConfiguration, so it must be freed. */\n    if (node->ip && strcmp(node->ip, config.hostip) != 0) sdsfree(node->ip);\n    if (node->redis_config != NULL) freeRedisConfig(node->redis_config);\n    zfree(node->slots);\n    zfree(node);\n}\n\nstatic void freeClusterNodes() {\n    int i = 0;\n    for (; i < config.cluster_node_count; i++) {\n        clusterNode *n = config.cluster_nodes[i];\n        if (n) freeClusterNode(n);\n    }\n    zfree(config.cluster_nodes);\n    config.cluster_nodes = NULL;\n}\n\nstatic clusterNode **addClusterNode(clusterNode *node) {\n    int count = config.cluster_node_count + 1;\n    config.cluster_nodes = zrealloc(config.cluster_nodes,\n                                    count * sizeof(*node));\n    if (!config.cluster_nodes) return NULL;\n    config.cluster_nodes[config.cluster_node_count++] = node;\n    return config.cluster_nodes;\n}\n\nstatic int fetchClusterConfiguration() {\n    int success = 1;\n    redisContext *ctx = NULL;\n    redisReply *reply =  NULL;\n    if (config.hostsocket == NULL)\n        ctx = redisConnect(config.hostip,config.hostport);\n    else\n        ctx = redisConnectUnix(config.hostsocket);\n    if (ctx->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,\n                    ctx->errstr);\n        } else fprintf(stderr,\"%s: %s\\n\",config.hostsocket,ctx->errstr);\n        exit(1);\n    }\n    clusterNode *firstNode = createClusterNode((char *) config.hostip,\n                                               config.hostport);\n    if (!firstNode) {success = 0; goto cleanup;}\n    reply = redisCommand(ctx, \"CLUSTER NODES\");\n    success = (reply != NULL);\n    if (!success) goto cleanup;\n    success = (reply->type != REDIS_REPLY_ERROR);\n    if (!success) {\n        if (config.hostsocket == NULL) {\n            fprintf(stderr, \"Cluster node %s:%d replied with error:\\n%s\\n\",\n                    config.hostip, config.hostport, reply->str);\n        } else {\n            fprintf(stderr, \"Cluster node %s replied with error:\\n%s\\n\",\n                    config.hostsocket, reply->str);\n        }\n        goto cleanup;\n    }\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *name = NULL, *addr = NULL, *flags = NULL, *master_id = NULL;\n        int i = 0;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            switch(i++){\n            case 0: name = token; break;\n            case 1: addr = token; break;\n            case 2: flags = token; break;\n            case 3: master_id = token; break;\n            }\n            if (i == 8) break; // Slots\n        }\n        if (!flags) {\n            fprintf(stderr, \"Invalid CLUSTER NODES reply: missing flags.\\n\");\n            success = 0;\n            goto cleanup;\n        }\n        int myself = (strstr(flags, \"myself\") != NULL);\n        int is_replica = (strstr(flags, \"slave\") != NULL ||\n                         (master_id != NULL && master_id[0] != '-'));\n        if (is_replica) continue;\n        if (addr == NULL) {\n            fprintf(stderr, \"Invalid CLUSTER NODES reply: missing addr.\\n\");\n            success = 0;\n            goto cleanup;\n        }\n        clusterNode *node = NULL;\n        char *ip = NULL;\n        int port = 0;\n        char *paddr = strchr(addr, ':');\n        if (paddr != NULL) {\n            *paddr = '\\0';\n            ip = addr;\n            addr = paddr + 1;\n            /* If internal bus is specified, then just drop it. */\n            if ((paddr = strchr(addr, '@')) != NULL) *paddr = '\\0';\n            port = atoi(addr);\n        }\n        if (myself) {\n            node = firstNode;\n            if (node->ip == NULL && ip != NULL) {\n                node->ip = ip;\n                node->port = port;\n            }\n        } else {\n            node = createClusterNode(sdsnew(ip), port);\n        }\n        if (node == NULL) {\n            success = 0;\n            goto cleanup;\n        }\n        if (name != NULL) node->name = sdsnew(name);\n        if (i == 8) {\n            int remaining = strlen(line);\n            while (remaining > 0) {\n                p = strchr(line, ' ');\n                if (p == NULL) p = line + remaining;\n                remaining -= (p - line);\n\n                char *slotsdef = line;\n                *p = '\\0';\n                if (remaining) {\n                    line = p + 1;\n                    remaining--;\n                } else line = p;\n                char *dash = NULL;\n                if (slotsdef[0] == '[') {\n                    slotsdef++;\n                    if ((p = strstr(slotsdef, \"->-\"))) { // Migrating\n                        *p = '\\0';\n                        p += 3;\n                        char *closing_bracket = strchr(p, ']');\n                        if (closing_bracket) *closing_bracket = '\\0';\n                        sds slot = sdsnew(slotsdef);\n                        sds dst = sdsnew(p);\n                        node->migrating_count += 2;\n                        node->migrating =\n                            zrealloc(node->migrating,\n                                (node->migrating_count * sizeof(sds)));\n                        node->migrating[node->migrating_count - 2] =\n                            slot;\n                        node->migrating[node->migrating_count - 1] =\n                            dst;\n                    }  else if ((p = strstr(slotsdef, \"-<-\"))) {//Importing\n                        *p = '\\0';\n                        p += 3;\n                        char *closing_bracket = strchr(p, ']');\n                        if (closing_bracket) *closing_bracket = '\\0';\n                        sds slot = sdsnew(slotsdef);\n                        sds src = sdsnew(p);\n                        node->importing_count += 2;\n                        node->importing = zrealloc(node->importing,\n                            (node->importing_count * sizeof(sds)));\n                        node->importing[node->importing_count - 2] =\n                            slot;\n                        node->importing[node->importing_count - 1] =\n                            src;\n                    }\n                } else if ((dash = strchr(slotsdef, '-')) != NULL) {\n                    p = dash;\n                    int start, stop;\n                    *p = '\\0';\n                    start = atoi(slotsdef);\n                    stop = atoi(p + 1);\n                    while (start <= stop) {\n                        int slot = start++;\n                        node->slots[node->slots_count++] = slot;\n                    }\n                } else if (p > slotsdef) {\n                    int slot = atoi(slotsdef);\n                    node->slots[node->slots_count++] = slot;\n                }\n            }\n        }\n        if (node->slots_count == 0) {\n            printf(\"WARNING: master node %s:%d has no slots, skipping...\\n\",\n                   node->ip, node->port);\n            continue;\n        }\n        if (!addClusterNode(node)) {\n            success = 0;\n            goto cleanup;\n        }\n    }\ncleanup:\n    if (ctx) redisFree(ctx);\n    if (!success) {\n        if (config.cluster_nodes) freeClusterNodes();\n    }\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Request the current cluster slots configuration by calling CLUSTER SLOTS\n * and atomically update the slots after a successful reply. */\nstatic int fetchClusterSlotsConfiguration(client c) {\n    UNUSED(c);\n    int success = 1, is_fetching_slots = 0, last_update = 0;\n    size_t i;\n    atomicGet(config.slots_last_update, last_update);\n    if (c->slots_last_update < last_update) {\n        c->slots_last_update = last_update;\n        return -1;\n    }\n    redisReply *reply = NULL;\n    atomicGetIncr(config.is_fetching_slots, is_fetching_slots, 1);\n    if (is_fetching_slots) return -1; //TODO: use other codes || errno ?\n    atomicSet(config.is_fetching_slots, 1);\n    if (config.showerrors)\n        printf(\"Cluster slots configuration changed, fetching new one...\\n\");\n    const char *errmsg = \"Failed to update cluster slots configuration\";\n    static dictType dtype = {\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    /* printf(\"[%d] fetchClusterSlotsConfiguration\\n\", c->thread_id); */\n    dict *masters = dictCreate(&dtype, NULL);\n    redisContext *ctx = NULL;\n    for (i = 0; i < (size_t) config.cluster_node_count; i++) {\n        clusterNode *node = config.cluster_nodes[i];\n        assert(node->ip != NULL);\n        assert(node->name != NULL);\n        assert(node->port);\n        /* Use first node as entry point to connect to. */\n        if (ctx == NULL) {\n            ctx = redisConnect(node->ip, node->port);\n            if (!ctx || ctx->err) {\n                success = 0;\n                if (ctx && ctx->err)\n                    fprintf(stderr, \"REDIS CONNECTION ERROR: %s\\n\", ctx->errstr);\n                goto cleanup;\n            }\n        }\n        if (node->updated_slots != NULL)\n            zfree(node->updated_slots);\n        node->updated_slots = NULL;\n        node->updated_slots_count = 0;\n        dictReplace(masters, node->name, node) ;\n    }\n    reply = redisCommand(ctx, \"CLUSTER SLOTS\");\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        success = 0;\n        if (reply)\n            fprintf(stderr,\"%s\\nCLUSTER SLOTS ERROR: %s\\n\",errmsg,reply->str);\n        goto cleanup;\n    }\n    assert(reply->type == REDIS_REPLY_ARRAY);\n    for (i = 0; i < reply->elements; i++) {\n        redisReply *r = reply->element[i];\n        assert(r->type == REDIS_REPLY_ARRAY);\n        assert(r->elements >= 3);\n        int from, to, slot;\n        from = r->element[0]->integer;\n        to = r->element[1]->integer;\n        redisReply *nr =  r->element[2];\n        assert(nr->type == REDIS_REPLY_ARRAY && nr->elements >= 3);\n        assert(nr->element[2]->str != NULL);\n        sds name =  sdsnew(nr->element[2]->str);\n        dictEntry *entry = dictFind(masters, name);\n        if (entry == NULL) {\n            success = 0;\n            fprintf(stderr, \"%s: could not find node with ID %s in current \"\n                            \"configuration.\\n\", errmsg, name);\n            if (name) sdsfree(name);\n            goto cleanup;\n        }\n        sdsfree(name);\n        clusterNode *node = dictGetVal(entry);\n        if (node->updated_slots == NULL)\n            node->updated_slots = zcalloc(CLUSTER_SLOTS * sizeof(int));\n        for (slot = from; slot <= to; slot++)\n            node->updated_slots[node->updated_slots_count++] = slot;\n    }\n    updateClusterSlotsConfiguration();\ncleanup:\n    freeReplyObject(reply);\n    redisFree(ctx);\n    dictRelease(masters);\n    atomicSet(config.is_fetching_slots, 0);\n    return success;\n}\n\n/* Atomically update the new slots configuration. */\nstatic void updateClusterSlotsConfiguration() {\n    pthread_mutex_lock(&config.is_updating_slots_mutex);\n    atomicSet(config.is_updating_slots, 1);\n    int i;\n    for (i = 0; i < config.cluster_node_count; i++) {\n        clusterNode *node = config.cluster_nodes[i];\n        if (node->updated_slots != NULL) {\n            int *oldslots = node->slots;\n            node->slots = node->updated_slots;\n            node->slots_count = node->updated_slots_count;\n            node->current_slot_index = 0;\n            node->updated_slots = NULL;\n            node->updated_slots_count = 0;\n            zfree(oldslots);\n        }\n    }\n    atomicSet(config.is_updating_slots, 0);\n    atomicIncr(config.slots_last_update, 1);\n    pthread_mutex_unlock(&config.is_updating_slots_mutex);\n}\n\n/* Returns number of consumed options. */\nint parseOptions(int argc, const char **argv) {\n    int i;\n    int lastarg;\n    int exit_status = 1;\n\n    for (i = 1; i < argc; i++) {\n        lastarg = (i == (argc-1));\n\n        if (!strcmp(argv[i],\"-c\")) {\n            if (lastarg) goto invalid;\n            config.numclients = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-n\")) {\n            if (lastarg) goto invalid;\n            config.requests = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-k\")) {\n            if (lastarg) goto invalid;\n            config.keepalive = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-h\")) {\n            if (lastarg) goto invalid;\n            config.hostip = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-p\")) {\n            if (lastarg) goto invalid;\n            config.hostport = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-s\")) {\n            if (lastarg) goto invalid;\n            config.hostsocket = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-a\") ) {\n            if (lastarg) goto invalid;\n            config.auth = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"--user\")) {\n            if (lastarg) goto invalid;\n            config.user = 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            const char *next = argv[++i], *p = next;\n            if (*p == '-') {\n                p++;\n                if (*p < '0' || *p > '9') goto invalid;\n            }\n            config.randomkeys = 1;\n            config.randomkeys_keyspacelen = atoi(next);\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],\"-e\")) {\n            config.showerrors = 1;\n        } else if (!strcmp(argv[i],\"-t\")) {\n            if (lastarg) goto invalid;\n            /* We get the list of tests to run as a string in the form\n             * get,set,lrange,...,test_N. Then we add a comma before and\n             * after the string in order to make sure that searching\n             * for \",testname,\" will always get a match if the test is\n             * enabled. */\n            config.tests = sdsnew(\",\");\n            config.tests = sdscat(config.tests,(char*)argv[++i]);\n            config.tests = sdscat(config.tests,\",\");\n            sdstolower(config.tests);\n        } else if (!strcmp(argv[i],\"--dbnum\")) {\n            if (lastarg) goto invalid;\n            config.dbnum = atoi(argv[++i]);\n            config.dbnumstr = sdsfromlonglong(config.dbnum);\n        } else if (!strcmp(argv[i],\"--precision\")) {\n            if (lastarg) goto invalid;\n            config.precision = atoi(argv[++i]);\n            if (config.precision < 0) config.precision = 0;\n            if (config.precision > MAX_LATENCY_PRECISION) config.precision = MAX_LATENCY_PRECISION;\n        } else if (!strcmp(argv[i],\"--threads\")) {\n             if (lastarg) goto invalid;\n             config.num_threads = atoi(argv[++i]);\n             if (config.num_threads > MAX_THREADS) {\n                printf(\"WARNING: too many threads, limiting threads to %d.\\n\",\n                       MAX_THREADS);\n                config.num_threads = MAX_THREADS;\n             } else if (config.num_threads < 0) config.num_threads = 0;\n        } else if (!strcmp(argv[i],\"--cluster\")) {\n            config.cluster_mode = 1;\n        } else if (!strcmp(argv[i],\"--enable-tracking\")) {\n            config.enable_tracking = 1;\n        } else if (!strcmp(argv[i],\"--help\")) {\n            exit_status = 0;\n            goto usage;\n        } else {\n            /* Assume the user meant to provide an option when the arg starts\n             * with a dash. We're done otherwise and should use the remainder\n             * as the command and arguments for running the benchmark. */\n            if (argv[i][0] == '-') goto invalid;\n            return i;\n        }\n    }\n\n    return i;\n\ninvalid:\n    printf(\"Invalid option \\\"%s\\\" or option argument missing\\n\\n\",argv[i]);\n\nusage:\n    printf(\n\"Usage: 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\" -a <password>      Password for Redis Auth\\n\"\n\" --user <username>  Used to send ACL style 'AUTH username pass'. Needs -a.\\n\"\n\" -c <clients>       Number of parallel connections (default 50)\\n\"\n\" -n <requests>      Total number of requests (default 100000)\\n\"\n\" -d <size>          Data size of SET/GET value in bytes (default 3)\\n\"\n\" --dbnum <db>       SELECT the specified db number (default 0)\\n\"\n\" --threads <num>    Enable multi-thread mode.\\n\"\n\" --cluster          Enable cluster mode.\\n\"\n\" --enable-tracking  Send CLIENT TRACKING on before starting benchmark.\\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\" -e                 If server replies with errors, show them on stdout.\\n\"\n\"                    (no more than 1 error per second is displayed)\\n\"\n\" -q                 Quiet. Just show query/sec values\\n\"\n\" --precision        Number of decimal places to display in latency output (default 0)\\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    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n    int liveclients = 0;\n    int requests_finished = 0;\n    atomicGet(config.liveclients, liveclients);\n    atomicGet(config.requests_finished, requests_finished);\n\n    if (liveclients == 0 && requests_finished != config.requests) {\n        fprintf(stderr,\"All clients disconnected... aborting.\\n\");\n        exit(1);\n    }\n    if (config.num_threads && requests_finished >= config.requests) {\n        aeStop(eventLoop);\n        return AE_NOMORE;\n    }\n    if (config.csv) return 250;\n    if (config.idlemode == 1) {\n        printf(\"clients: %d\\r\", config.liveclients);\n        fflush(stdout);\n\treturn 250;\n    }\n    float dt = (float)(mstime()-config.start)/1000.0;\n    float rps = (float)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 = 100000;\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.showerrors = 0;\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    config.auth = NULL;\n    config.precision = 1;\n    config.num_threads = 0;\n    config.threads = NULL;\n    config.cluster_mode = 0;\n    config.cluster_node_count = 0;\n    config.cluster_nodes = NULL;\n    config.redis_config = NULL;\n    config.is_fetching_slots = 0;\n    config.is_updating_slots = 0;\n    config.slots_last_update = 0;\n    config.enable_tracking = 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.cluster_mode) {\n        /* Fetch cluster configuration. */\n        if (!fetchClusterConfiguration() || !config.cluster_nodes) {\n            if (!config.hostsocket) {\n                fprintf(stderr, \"Failed to fetch cluster configuration from \"\n                                \"%s:%d\\n\", config.hostip, config.hostport);\n            } else {\n                fprintf(stderr, \"Failed to fetch cluster configuration from \"\n                                \"%s\\n\", config.hostsocket);\n            }\n            exit(1);\n        }\n        if (config.cluster_node_count <= 1) {\n            fprintf(stderr, \"Invalid cluster: %d node(s).\\n\",\n                    config.cluster_node_count);\n            exit(1);\n        }\n        printf(\"Cluster has %d master nodes:\\n\\n\", config.cluster_node_count);\n        int i = 0;\n        for (; i < config.cluster_node_count; i++) {\n            clusterNode *node = config.cluster_nodes[i];\n            if (!node) {\n                fprintf(stderr, \"Invalid cluster node #%d\\n\", i);\n                exit(1);\n            }\n            printf(\"Master %d: \", i);\n            if (node->name) printf(\"%s \", node->name);\n            printf(\"%s:%d\\n\", node->ip, node->port);\n            node->redis_config = getRedisConfig(node->ip, node->port, NULL);\n            if (node->redis_config == NULL) {\n                fprintf(stderr, \"WARN: could not fetch node CONFIG %s:%d\\n\",\n                        node->ip, node->port);\n            }\n        }\n        printf(\"\\n\");\n        /* Automatically set thread number to node count if not specified\n         * by the user. */\n        if (config.num_threads == 0)\n            config.num_threads = config.cluster_node_count;\n    } else {\n        config.redis_config =\n            getRedisConfig(config.hostip, config.hostport, config.hostsocket);\n        if (config.redis_config == NULL)\n            fprintf(stderr, \"WARN: could not fetch server CONFIG\\n\");\n    }\n\n    if (config.num_threads > 0) {\n        pthread_mutex_init(&(config.requests_issued_mutex), NULL);\n        pthread_mutex_init(&(config.requests_finished_mutex), NULL);\n        pthread_mutex_init(&(config.liveclients_mutex), NULL);\n        pthread_mutex_init(&(config.is_fetching_slots_mutex), NULL);\n        pthread_mutex_init(&(config.is_updating_slots_mutex), NULL);\n        pthread_mutex_init(&(config.updating_slots_mutex), NULL);\n        pthread_mutex_init(&(config.slots_last_update_mutex), NULL);\n    }\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        int thread_id = -1, use_threads = (config.num_threads > 0);\n        if (use_threads) {\n            thread_id = 0;\n            initBenchmarkThreads();\n        }\n        c = createClient(\"\",0,NULL,thread_id); /* will never receive a reply */\n        createMissingClients(c);\n        if (use_threads) startBenchmarkThreads();\n        else 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        if (config.redis_config != NULL) freeRedisConfig(config.redis_config);\n        return 0;\n    }\n\n    /* Run default benchmark suite. */\n    data = zmalloc(config.datasize+1);\n    do {\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:{tag}:__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:{tag}:__rand_int__\");\n            benchmark(\"GET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"incr\")) {\n            len = redisFormatCommand(&cmd,\"INCR counter:{tag}:__rand_int__\");\n            benchmark(\"INCR\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpush\")) {\n            len = redisFormatCommand(&cmd,\"LPUSH mylist:{tag} %s\",data);\n            benchmark(\"LPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"rpush\")) {\n            len = redisFormatCommand(&cmd,\"RPUSH mylist:{tag} %s\",data);\n            benchmark(\"RPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpop\")) {\n            len = redisFormatCommand(&cmd,\"LPOP mylist:{tag}\");\n            benchmark(\"LPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"rpop\")) {\n            len = redisFormatCommand(&cmd,\"RPOP mylist:{tag}\");\n            benchmark(\"RPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"sadd\")) {\n            len = redisFormatCommand(&cmd,\n                \"SADD myset:{tag} element:__rand_int__\");\n            benchmark(\"SADD\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hset\")) {\n            len = redisFormatCommand(&cmd,\n                \"HSET myhash:{tag}:__rand_int__ element:__rand_int__ %s\",data);\n            benchmark(\"HSET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"spop\")) {\n            len = redisFormatCommand(&cmd,\"SPOP myset:{tag}\");\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:{tag} %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:{tag} 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:{tag} 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:{tag} 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:{tag} 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:{tag}:__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    if (config.redis_config != NULL) freeRedisConfig(config.redis_config);\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include <sys/stat.h>\n\n#define ERROR(...) { \\\n    char __buf[1024]; \\\n    snprintf(__buf, sizeof(__buf), __VA_ARGS__); \\\n    snprintf(error, sizeof(error), \"0x%16llx: %s\", (long long)epos, __buf); \\\n}\n\nstatic char error[1044];\nstatic off_t epos;\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\nint readLong(FILE *fp, char prefix, long *target) {\n    char buf[128], *eptr;\n    epos = ftello(fp);\n    if (fgets(buf,sizeof(buf),fp) == NULL) {\n        return 0;\n    }\n    if (buf[0] != prefix) {\n        ERROR(\"Expected prefix '%c', got: '%c'\",prefix,buf[0]);\n        return 0;\n    }\n    *target = strtol(buf+1,&eptr,10);\n    return consumeNewline(eptr);\n}\n\nint readBytes(FILE *fp, char *target, long length) {\n    long real;\n    epos = ftello(fp);\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    return 1;\n}\n\nint readString(FILE *fp, char** target) {\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    *target = (char*)zmalloc(len);\n    if (!readBytes(fp,*target,len)) {\n        return 0;\n    }\n    if (!consumeNewline(*target+len-2)) {\n        return 0;\n    }\n    (*target)[len-2] = '\\0';\n    return 1;\n}\n\nint readArgc(FILE *fp, long *target) {\n    return readLong(fp,'*',target);\n}\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        if (!multi) pos = ftello(fp);\n        if (!readArgc(fp, &argc)) break;\n\n        for (i = 0; i < argc; i++) {\n            if (!readString(fp,&str)) break;\n            if (i == 0) {\n                if (strcasecmp(str, \"multi\") == 0) {\n                    if (multi++) {\n                        ERROR(\"Unexpected MULTI\");\n                        break;\n                    }\n                } else if (strcasecmp(str, \"exec\") == 0) {\n                    if (--multi) {\n                        ERROR(\"Unexpected EXEC\");\n                        break;\n                    }\n                }\n            }\n            zfree(str);\n        }\n\n        /* Stop if the loop did not finish */\n        if (i < argc) {\n            if (str) zfree(str);\n            break;\n        }\n    }\n\n    if (feof(fp) && multi && strlen(error) == 0) {\n        ERROR(\"Reached EOF before reading EXEC for MULTI\");\n    }\n    if (strlen(error) > 0) {\n        printf(\"%s\\n\", error);\n    }\n    return pos;\n}\n\nint redis_check_aof_main(int argc, char **argv) {\n    char *filename;\n    int fix = 0;\n\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    FILE *fp = fopen(filename,\"r+\");\n    if (fp == NULL) {\n        printf(\"Cannot open file: %s\\n\", filename);\n        exit(1);\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    off_t size = sb.st_size;\n    if (size == 0) {\n        printf(\"Empty file: %s\\n\", filename);\n        exit(1);\n    }\n\n    /* This AOF file may have an RDB preamble. Check this to start, and if this\n     * is the case, start processing the RDB part. */\n    if (size >= 8) {    /* There must be at least room for the RDB header. */\n        char sig[5];\n        int has_preamble = fread(sig,sizeof(sig),1,fp) == 1 &&\n                            memcmp(sig,\"REDIS\",sizeof(sig)) == 0;\n        rewind(fp);\n        if (has_preamble) {\n            printf(\"The AOF appears to start with an RDB preamble.\\n\"\n                   \"Checking the RDB preamble to start:\\n\");\n            if (redis_check_rdb_main(argc,argv,fp) == C_ERR) {\n                printf(\"RDB preamble of AOF file is not sane, aborting.\\n\");\n                exit(1);\n            } else {\n                printf(\"RDB preamble is OK, proceeding with AOF tail...\\n\");\n            }\n        }\n    }\n\n    off_t pos = process(fp);\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    if (diff > 0) {\n        if (fix) {\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            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        } else {\n            printf(\"AOF is not valid. \"\n                   \"Use the --fix option to try fixing it.\\n\");\n            exit(1);\n        }\n    } else {\n        printf(\"AOF is valid\\n\");\n    }\n\n    fclose(fp);\n    exit(0);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/redis-check-rdb.c",
    "content": "/*\n * Copyright (c) 2016, 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 \"server.h\"\n#include \"rdb.h\"\n\n#include <stdarg.h>\n\nvoid createSharedObjects(void);\nvoid rdbLoadProgressCallback(rio *r, const void *buf, size_t len);\nint rdbCheckMode = 0;\n\nstruct {\n    rio *rio;\n    robj *key;                      /* Current key we are reading. */\n    int key_type;                   /* Current key type if != -1. */\n    unsigned long keys;             /* Number of keys processed. */\n    unsigned long expires;          /* Number of keys with an expire. */\n    unsigned long already_expired;  /* Number of keys already expired. */\n    int doing;                      /* The state while reading the RDB. */\n    int error_set;                  /* True if error is populated. */\n    char error[1024];\n} rdbstate;\n\n/* At every loading step try to remember what we were about to do, so that\n * we can log this information when an error is encountered. */\n#define RDB_CHECK_DOING_START 0\n#define RDB_CHECK_DOING_READ_TYPE 1\n#define RDB_CHECK_DOING_READ_EXPIRE 2\n#define RDB_CHECK_DOING_READ_KEY 3\n#define RDB_CHECK_DOING_READ_OBJECT_VALUE 4\n#define RDB_CHECK_DOING_CHECK_SUM 5\n#define RDB_CHECK_DOING_READ_LEN 6\n#define RDB_CHECK_DOING_READ_AUX 7\n\nchar *rdb_check_doing_string[] = {\n    \"start\",\n    \"read-type\",\n    \"read-expire\",\n    \"read-key\",\n    \"read-object-value\",\n    \"check-sum\",\n    \"read-len\",\n    \"read-aux\"\n};\n\nchar *rdb_type_string[] = {\n    \"string\",\n    \"list-linked\",\n    \"set-hashtable\",\n    \"zset-v1\",\n    \"hash-hashtable\",\n    \"zset-v2\",\n    \"module-value\",\n    \"\",\"\",\n    \"hash-zipmap\",\n    \"list-ziplist\",\n    \"set-intset\",\n    \"zset-ziplist\",\n    \"hash-ziplist\",\n    \"quicklist\",\n    \"stream\"\n};\n\n/* Show a few stats collected into 'rdbstate' */\nvoid rdbShowGenericInfo(void) {\n    printf(\"[info] %lu keys read\\n\", rdbstate.keys);\n    printf(\"[info] %lu expires\\n\", rdbstate.expires);\n    printf(\"[info] %lu already expired\\n\", rdbstate.already_expired);\n}\n\n/* Called on RDB errors. Provides details about the RDB and the offset\n * we were when the error was detected. */\nvoid rdbCheckError(const char *fmt, ...) {\n    char msg[1024];\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(msg, sizeof(msg), fmt, ap);\n    va_end(ap);\n\n    printf(\"--- RDB ERROR DETECTED ---\\n\");\n    printf(\"[offset %llu] %s\\n\",\n        (unsigned long long) (rdbstate.rio ?\n            rdbstate.rio->processed_bytes : 0), msg);\n    printf(\"[additional info] While doing: %s\\n\",\n        rdb_check_doing_string[rdbstate.doing]);\n    if (rdbstate.key)\n        printf(\"[additional info] Reading key '%s'\\n\",\n            (char*)rdbstate.key->ptr);\n    if (rdbstate.key_type != -1)\n        printf(\"[additional info] Reading type %d (%s)\\n\",\n            rdbstate.key_type,\n            ((unsigned)rdbstate.key_type <\n             sizeof(rdb_type_string)/sizeof(char*)) ?\n                rdb_type_string[rdbstate.key_type] : \"unknown\");\n    rdbShowGenericInfo();\n}\n\n/* Print informations during RDB checking. */\nvoid rdbCheckInfo(const char *fmt, ...) {\n    char msg[1024];\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(msg, sizeof(msg), fmt, ap);\n    va_end(ap);\n\n    printf(\"[offset %llu] %s\\n\",\n        (unsigned long long) (rdbstate.rio ?\n            rdbstate.rio->processed_bytes : 0), msg);\n}\n\n/* Used inside rdb.c in order to log specific errors happening inside\n * the RDB loading internals. */\nvoid rdbCheckSetError(const char *fmt, ...) {\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(rdbstate.error, sizeof(rdbstate.error), fmt, ap);\n    va_end(ap);\n    rdbstate.error_set = 1;\n}\n\n/* During RDB check we setup a special signal handler for memory violations\n * and similar conditions, so that we can log the offending part of the RDB\n * if the crash is due to broken content. */\nvoid rdbCheckHandleCrash(int sig, siginfo_t *info, void *secret) {\n    UNUSED(sig);\n    UNUSED(info);\n    UNUSED(secret);\n\n    rdbCheckError(\"Server crash checking the specified RDB file!\");\n    exit(1);\n}\n\nvoid rdbCheckSetupSignals(void) {\n    struct sigaction act;\n\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;\n    act.sa_sigaction = rdbCheckHandleCrash;\n    sigaction(SIGSEGV, &act, NULL);\n    sigaction(SIGBUS, &act, NULL);\n    sigaction(SIGFPE, &act, NULL);\n    sigaction(SIGILL, &act, NULL);\n}\n\n/* Check the specified RDB file. Return 0 if the RDB looks sane, otherwise\n * 1 is returned.\n * The file is specified as a filename in 'rdbfilename' if 'fp' is not NULL,\n * otherwise the already open file 'fp' is checked. */\nint redis_check_rdb(char *rdbfilename, FILE *fp) {\n    uint64_t dbid;\n    int type, rdbver;\n    char buf[1024];\n    long long expiretime, now = mstime();\n    static rio rdb; /* Pointed by global struct riostate. */\n\n    int closefile = (fp == NULL);\n    if (fp == NULL && (fp = fopen(rdbfilename,\"r\")) == NULL) return 1;\n\n    rioInitWithFile(&rdb,fp);\n    rdbstate.rio = &rdb;\n    rdb.update_cksum = rdbLoadProgressCallback;\n    if (rioRead(&rdb,buf,9) == 0) goto eoferr;\n    buf[9] = '\\0';\n    if (memcmp(buf,\"REDIS\",5) != 0) {\n        rdbCheckError(\"Wrong signature trying to load DB from file\");\n        goto err;\n    }\n    rdbver = atoi(buf+5);\n    if (rdbver < 1 || rdbver > RDB_VERSION) {\n        rdbCheckError(\"Can't handle RDB format version %d\",rdbver);\n        goto err;\n    }\n\n    expiretime = -1;\n    startLoadingFile(fp, rdbfilename, RDBFLAGS_NONE);\n    while(1) {\n        robj *key, *val;\n\n        /* Read type. */\n        rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;\n        if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;\n\n        /* Handle special types. */\n        if (type == RDB_OPCODE_EXPIRETIME) {\n            rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;\n            /* EXPIRETIME: load an expire associated with the next key\n             * to load. Note that after loading an expire we need to\n             * load the actual type, and continue. */\n            expiretime = rdbLoadTime(&rdb);\n            expiretime *= 1000;\n            if (rioGetReadError(&rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EXPIRETIME_MS) {\n            /* EXPIRETIME_MS: milliseconds precision expire times introduced\n             * with RDB v3. Like EXPIRETIME but no with more precision. */\n            rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;\n            expiretime = rdbLoadMillisecondTime(&rdb, rdbver);\n            if (rioGetReadError(&rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_FREQ) {\n            /* FREQ: LFU frequency. */\n            uint8_t byte;\n            if (rioRead(&rdb,&byte,1) == 0) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_IDLE) {\n            /* IDLE: LRU idle time. */\n            if (rdbLoadLen(&rdb,NULL) == RDB_LENERR) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EOF) {\n            /* EOF: End of file, exit the main loop. */\n            break;\n        } else if (type == RDB_OPCODE_SELECTDB) {\n            /* SELECTDB: Select the specified database. */\n            rdbstate.doing = RDB_CHECK_DOING_READ_LEN;\n            if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            rdbCheckInfo(\"Selecting DB ID %d\", dbid);\n            continue; /* Read type again. */\n        } else if (type == RDB_OPCODE_RESIZEDB) {\n            /* RESIZEDB: Hint about the size of the keys in the currently\n             * selected data base, in order to avoid useless rehashing. */\n            uint64_t db_size, expires_size;\n            rdbstate.doing = RDB_CHECK_DOING_READ_LEN;\n            if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            continue; /* Read type again. */\n        } else if (type == RDB_OPCODE_AUX) {\n            /* AUX: generic string-string fields. Use to add state to RDB\n             * which is backward compatible. Implementations of RDB loading\n             * are requierd to skip AUX fields they don't understand.\n             *\n             * An AUX field is composed of two strings: key and value. */\n            robj *auxkey, *auxval;\n            rdbstate.doing = RDB_CHECK_DOING_READ_AUX;\n            if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n            if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n\n            rdbCheckInfo(\"AUX FIELD %s = '%s'\",\n                (char*)auxkey->ptr, (char*)auxval->ptr);\n            decrRefCount(auxkey);\n            decrRefCount(auxval);\n            continue; /* Read type again. */\n        } else {\n            if (!rdbIsObjectType(type)) {\n                rdbCheckError(\"Invalid object type: %d\", type);\n                goto err;\n            }\n            rdbstate.key_type = type;\n        }\n\n        /* Read key */\n        rdbstate.doing = RDB_CHECK_DOING_READ_KEY;\n        if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n        rdbstate.key = key;\n        rdbstate.keys++;\n        /* Read value */\n        rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;\n        if ((val = rdbLoadObject(type,&rdb,key->ptr)) == NULL) goto eoferr;\n        /* Check if the key already expired. */\n        if (expiretime != -1 && expiretime < now)\n            rdbstate.already_expired++;\n        if (expiretime != -1) rdbstate.expires++;\n        rdbstate.key = NULL;\n        decrRefCount(key);\n        decrRefCount(val);\n        rdbstate.key_type = -1;\n        expiretime = -1;\n    }\n    /* Verify the checksum if RDB version is >= 5 */\n    if (rdbver >= 5 && server.rdb_checksum) {\n        uint64_t cksum, expected = rdb.cksum;\n\n        rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM;\n        if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;\n        memrev64ifbe(&cksum);\n        if (cksum == 0) {\n            rdbCheckInfo(\"RDB file was saved with checksum disabled: no check performed.\");\n        } else if (cksum != expected) {\n            rdbCheckError(\"RDB CRC error\");\n            goto err;\n        } else {\n            rdbCheckInfo(\"Checksum OK\");\n        }\n    }\n\n    if (closefile) fclose(fp);\n    stopLoading(1);\n    return 0;\n\neoferr: /* unexpected end of file is handled here with a fatal exit */\n    if (rdbstate.error_set) {\n        rdbCheckError(rdbstate.error);\n    } else {\n        rdbCheckError(\"Unexpected EOF reading RDB file\");\n    }\nerr:\n    if (closefile) fclose(fp);\n    stopLoading(0);\n    return 1;\n}\n\n/* RDB check main: called form redis.c when Redis is executed with the\n * redis-check-rdb alias, on during RDB loading errors.\n *\n * The function works in two ways: can be called with argc/argv as a\n * standalone executable, or called with a non NULL 'fp' argument if we\n * already have an open file to check. This happens when the function\n * is used to check an RDB preamble inside an AOF file.\n *\n * When called with fp = NULL, the function never returns, but exits with the\n * status code according to success (RDB is sane) or error (RDB is corrupted).\n * Otherwise if called with a non NULL fp, the function returns C_OK or\n * C_ERR depending on the success or failure. */\nint redis_check_rdb_main(int argc, char **argv, FILE *fp) {\n    if (argc != 2 && fp == NULL) {\n        fprintf(stderr, \"Usage: %s <rdb-file-name>\\n\", argv[0]);\n        exit(1);\n    }\n    /* In order to call the loading functions we need to create the shared\n     * integer objects, however since this function may be called from\n     * an already initialized Redis instance, check if we really need to. */\n    if (shared.integers[0] == NULL)\n        createSharedObjects();\n    server.loading_process_events_interval_bytes = 0;\n    rdbCheckMode = 1;\n    rdbCheckInfo(\"Checking RDB file %s\", argv[1]);\n    rdbCheckSetupSignals();\n    int retval = redis_check_rdb(argv[1],fp);\n    if (retval == 0) {\n        rdbCheckInfo(\"\\\\o/ RDB looks OK! \\\\o/\");\n        rdbShowGenericInfo();\n    }\n    if (fp) return (retval == 0) ? C_OK : C_ERR;\n    exit(retval);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 <signal.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#include <math.h>\n\n#include <hiredis.h>\n#ifdef USE_OPENSSL\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <hiredis_ssl.h>\n#endif\n#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */\n#include \"dict.h\"\n#include \"adlist.h\"\n#include \"zmalloc.h\"\n#include \"linenoise.h\"\n#include \"help.h\"\n#include \"anet.h\"\n#include \"ae.h\"\n\n#define UNUSED(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#define REDIS_CLI_HISTFILE_ENV \"REDISCLI_HISTFILE\"\n#define REDIS_CLI_HISTFILE_DEFAULT \".rediscli_history\"\n#define REDIS_CLI_RCFILE_ENV \"REDISCLI_RCFILE\"\n#define REDIS_CLI_RCFILE_DEFAULT \".redisclirc\"\n#define REDIS_CLI_AUTH_ENV \"REDISCLI_AUTH\"\n\n#define CLUSTER_MANAGER_SLOTS               16384\n#define CLUSTER_MANAGER_MIGRATE_TIMEOUT     60000\n#define CLUSTER_MANAGER_MIGRATE_PIPELINE    10\n#define CLUSTER_MANAGER_REBALANCE_THRESHOLD 2\n\n#define CLUSTER_MANAGER_INVALID_HOST_ARG \\\n    \"[ERR] Invalid arguments: you need to pass either a valid \" \\\n    \"address (ie. 120.0.0.1:7000) or space separated IP \" \\\n    \"and port (ie. 120.0.0.1 7000)\\n\"\n#define CLUSTER_MANAGER_MODE() (config.cluster_manager_command.name != NULL)\n#define CLUSTER_MANAGER_MASTERS_COUNT(nodes, replicas) (nodes/(replicas + 1))\n#define CLUSTER_MANAGER_COMMAND(n,...) \\\n        (redisCommand(n->context, __VA_ARGS__))\n\n#define CLUSTER_MANAGER_NODE_ARRAY_FREE(array) zfree(array->alloc)\n\n#define CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err) \\\n    clusterManagerLogErr(\"Node %s:%d replied with error:\\n%s\\n\", \\\n                         n->ip, n->port, err);\n\n#define clusterManagerLogInfo(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_INFO,__VA_ARGS__)\n\n#define clusterManagerLogErr(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_ERR,__VA_ARGS__)\n\n#define clusterManagerLogWarn(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_WARN,__VA_ARGS__)\n\n#define clusterManagerLogOk(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_SUCCESS,__VA_ARGS__)\n\n#define CLUSTER_MANAGER_FLAG_MYSELF     1 << 0\n#define CLUSTER_MANAGER_FLAG_SLAVE      1 << 1\n#define CLUSTER_MANAGER_FLAG_FRIEND     1 << 2\n#define CLUSTER_MANAGER_FLAG_NOADDR     1 << 3\n#define CLUSTER_MANAGER_FLAG_DISCONNECT 1 << 4\n#define CLUSTER_MANAGER_FLAG_FAIL       1 << 5\n\n#define CLUSTER_MANAGER_CMD_FLAG_FIX            1 << 0\n#define CLUSTER_MANAGER_CMD_FLAG_SLAVE          1 << 1\n#define CLUSTER_MANAGER_CMD_FLAG_YES            1 << 2\n#define CLUSTER_MANAGER_CMD_FLAG_AUTOWEIGHTS    1 << 3\n#define CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER    1 << 4\n#define CLUSTER_MANAGER_CMD_FLAG_SIMULATE       1 << 5\n#define CLUSTER_MANAGER_CMD_FLAG_REPLACE        1 << 6\n#define CLUSTER_MANAGER_CMD_FLAG_COPY           1 << 7\n#define CLUSTER_MANAGER_CMD_FLAG_COLOR          1 << 8\n#define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS   1 << 9\n#define CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS 1 << 10\n\n#define CLUSTER_MANAGER_OPT_GETFRIENDS  1 << 0\n#define CLUSTER_MANAGER_OPT_COLD        1 << 1\n#define CLUSTER_MANAGER_OPT_UPDATE      1 << 2\n#define CLUSTER_MANAGER_OPT_QUIET       1 << 6\n#define CLUSTER_MANAGER_OPT_VERBOSE     1 << 7\n\n#define CLUSTER_MANAGER_LOG_LVL_INFO    1\n#define CLUSTER_MANAGER_LOG_LVL_WARN    2\n#define CLUSTER_MANAGER_LOG_LVL_ERR     3\n#define CLUSTER_MANAGER_LOG_LVL_SUCCESS 4\n\n#define CLUSTER_JOIN_CHECK_AFTER        20\n\n#define LOG_COLOR_BOLD      \"29;1m\"\n#define LOG_COLOR_RED       \"31;1m\"\n#define LOG_COLOR_GREEN     \"32;1m\"\n#define LOG_COLOR_YELLOW    \"33;1m\"\n#define LOG_COLOR_RESET     \"0m\"\n\n/* cliConnect() flags. */\n#define CC_FORCE (1<<0)         /* Re-connect if already connected. */\n#define CC_QUIET (1<<1)         /* Don't log connecting errors. */\n\n/* --latency-dist palettes. */\nint spectrum_palette_color_size = 19;\nint spectrum_palette_color[] = {0,233,234,235,237,239,241,243,245,247,144,143,142,184,226,214,208,202,196};\n\nint spectrum_palette_mono_size = 13;\nint spectrum_palette_mono[] = {0,233,234,235,237,239,241,243,245,247,249,251,253};\n\n/* The actual palette in use. */\nint *spectrum_palette;\nint spectrum_palette_size;\n\n/* Dict Helpers */\n\nstatic uint64_t dictSdsHash(const void *key);\nstatic int dictSdsKeyCompare(void *privdata, const void *key1,\n    const void *key2);\nstatic void dictSdsDestructor(void *privdata, void *val);\nstatic void dictListDestructor(void *privdata, void *val);\n\n/* Cluster Manager Command Info */\ntypedef struct clusterManagerCommand {\n    char *name;\n    int argc;\n    char **argv;\n    int flags;\n    int replicas;\n    char *from;\n    char *to;\n    char **weight;\n    int weight_argc;\n    char *master_id;\n    int slots;\n    int timeout;\n    int pipeline;\n    float threshold;\n    char *backup_dir;\n} clusterManagerCommand;\n\nstatic void createClusterManagerCommand(char *cmdname, int argc, char **argv);\n\n\nstatic redisContext *context;\nstatic struct config {\n    char *hostip;\n    int hostport;\n    char *hostsocket;\n    int tls;\n    char *sni;\n    char *cacert;\n    char *cacertdir;\n    char *cert;\n    char *key;\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_dist_mode;\n    int latency_history;\n    int lru_test_mode;\n    long long lru_test_sample_size;\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 memkeys;\n    unsigned memkeys_samples;\n    int hotkeys;\n    int stdinarg; /* get last arg from stdin. (-x option) */\n    char *auth;\n    int askpass;\n    char *user;\n    int output; /* output mode, see OUTPUT_* defines */\n    sds mb_delim;\n    char prompt[128];\n    char *eval;\n    int eval_ldb;\n    int eval_ldb_sync;  /* Ask for synchronous mode of the Lua debugger. */\n    int eval_ldb_end;   /* Lua debugging session ended. */\n    int enable_ldb_on_eval; /* Handle manual SCRIPT DEBUG + EVAL commands. */\n    int last_cmd_type;\n    int verbose;\n    clusterManagerCommand cluster_manager_command;\n    int no_auth_warning;\n    int resp3;\n} config;\n\n/* User preferences. */\nstatic struct pref {\n    int hints;\n} pref;\n\nstatic volatile sig_atomic_t force_cancel_loop = 0;\nstatic void usage(void);\nstatic void slaveMode(void);\nchar *redisGitSHA1(void);\nchar *redisGitDirty(void);\nstatic int cliConnect(int force);\n\nstatic char *getInfoField(char *info, char *field);\nstatic long getLongInfoField(char *info, char *field);\n\n/*------------------------------------------------------------------------------\n * Utility functions\n *--------------------------------------------------------------------------- */\n\nuint16_t crc16(const char *buf, int len);\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    if (config.eval_ldb) return;\n\n    sds prompt = sdsempty();\n    if (config.hostsocket != NULL) {\n        prompt = sdscatfmt(prompt,\"redis %s\",config.hostsocket);\n    } else {\n        char addr[256];\n        anetFormatAddr(addr, sizeof(addr), config.hostip, config.hostport);\n        prompt = sdscatlen(prompt,addr,strlen(addr));\n    }\n\n    /* Add [dbnum] if needed */\n    if (config.dbnum != 0)\n        prompt = sdscatfmt(prompt,\"[%i]\",config.dbnum);\n\n    /* Copy the prompt in the static buffer. */\n    prompt = sdscatlen(prompt,\"> \",2);\n    snprintf(config.prompt,sizeof(config.prompt),\"%s\",prompt);\n    sdsfree(prompt);\n}\n\n/* Return the name of the dotfile for the specified 'dotfilename'.\n * Normally it just concatenates user $HOME to the file specified\n * in 'dotfilename'. However if the environment varialbe 'envoverride'\n * is set, its value is taken as the path.\n *\n * The function returns NULL (if the file is /dev/null or cannot be\n * obtained for some error), or an SDS string that must be freed by\n * the user. */\nstatic sds getDotfilePath(char *envoverride, char *dotfilename) {\n    char *path = NULL;\n    sds dotPath = NULL;\n\n    /* Check the env for a dotfile override. */\n    path = getenv(envoverride);\n    if (path != NULL && *path != '\\0') {\n        if (!strcmp(\"/dev/null\", path)) {\n            return NULL;\n        }\n\n        /* If the env is set, return it. */\n        dotPath = sdsnew(path);\n    } else {\n        char *home = getenv(\"HOME\");\n        if (home != NULL && *home != '\\0') {\n            /* If no override is set use $HOME/<dotfilename>. */\n            dotPath = sdscatprintf(sdsempty(), \"%s/%s\", home, dotfilename);\n        }\n    }\n    return dotPath;\n}\n\n/* URL-style percent decoding. */\n#define isHexChar(c) (isdigit(c) || (c >= 'a' && c <= 'f'))\n#define decodeHexChar(c) (isdigit(c) ? c - '0' : c - 'a' + 10)\n#define decodeHex(h, l) ((decodeHexChar(h) << 4) + decodeHexChar(l))\n\nstatic sds percentDecode(const char *pe, size_t len) {\n    const char *end = pe + len;\n    sds ret = sdsempty();\n    const char *curr = pe;\n\n    while (curr < end) {\n        if (*curr == '%') {\n            if ((end - curr) < 2) {\n                fprintf(stderr, \"Incomplete URI encoding\\n\");\n                exit(1);\n            }\n\n            char h = tolower(*(++curr));\n            char l = tolower(*(++curr));\n            if (!isHexChar(h) || !isHexChar(l)) {\n                fprintf(stderr, \"Illegal character in URI encoding\\n\");\n                exit(1);\n            }\n            char c = decodeHex(h, l);\n            ret = sdscatlen(ret, &c, 1);\n            curr++;\n        } else {\n            ret = sdscatlen(ret, curr++, 1);\n        }\n    }\n\n    return ret;\n}\n\n/* Parse a URI and extract the server connection information.\n * URI scheme is based on the the provisional specification[1] excluding support\n * for query parameters. Valid URIs are:\n *   scheme:    \"redis://\"\n *   authority: [[<username> \":\"] <password> \"@\"] [<hostname> [\":\" <port>]]\n *   path:      [\"/\" [<db>]]\n *\n *  [1]: https://www.iana.org/assignments/uri-schemes/prov/redis */\nstatic void parseRedisUri(const char *uri) {\n\n    const char *scheme = \"redis://\";\n    const char *curr = uri;\n    const char *end = uri + strlen(uri);\n    const char *userinfo, *username, *port, *host, *path;\n\n    /* URI must start with a valid scheme. */\n    if (strncasecmp(scheme, curr, strlen(scheme))) {\n        fprintf(stderr,\"Invalid URI scheme\\n\");\n        exit(1);\n    }\n    curr += strlen(scheme);\n    if (curr == end) return;\n\n    /* Extract user info. */\n    if ((userinfo = strchr(curr,'@'))) {\n        if ((username = strchr(curr, ':')) && username < userinfo) {\n            /* If provided, username is ignored. */\n            curr = username + 1;\n        }\n\n        config.auth = percentDecode(curr, userinfo - curr);\n        curr = userinfo + 1;\n    }\n    if (curr == end) return;\n\n    /* Extract host and port. */\n    path = strchr(curr, '/');\n    if (*curr != '/') {\n        host = path ? path - 1 : end;\n        if ((port = strchr(curr, ':'))) {\n            config.hostport = atoi(port + 1);\n            host = port - 1;\n        }\n        config.hostip = sdsnewlen(curr, host - curr + 1);\n    }\n    curr = path ? path + 1 : end;\n    if (curr == end) return;\n\n    /* Extract database number. */\n    config.dbnum = atoi(curr);\n}\n\nstatic uint64_t dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nstatic int 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\nstatic void dictSdsDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    sdsfree(val);\n}\n\nvoid dictListDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    listRelease((list*)val);\n}\n\n/* _serverAssert is needed by dict */\nvoid _serverAssert(const char *estr, const char *file, int line) {\n    fprintf(stderr, \"=== ASSERTION FAILED ===\");\n    fprintf(stderr, \"==> %s:%d '%s' is not true\",file,line,estr);\n    *((char*)-1) = 'x';\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(void) {\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(void) {\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 = zmalloc(sizeof(helpEntry)*len);\n\n    for (i = 0; i < groupslen; i++) {\n        tmp.argc = 1;\n        tmp.argv = zmalloc(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/* cliInitHelp() setups the helpEntries array with the command and group\n * names from the help.h file. However the Redis instance we are connecting\n * to may support more commands, so this function integrates the previous\n * entries with additional entries obtained using the COMMAND command\n * available in recent versions of Redis. */\nstatic void cliIntegrateHelp(void) {\n    if (cliConnect(CC_QUIET) == REDIS_ERR) return;\n\n    redisReply *reply = redisCommand(context, \"COMMAND\");\n    if(reply == NULL || reply->type != REDIS_REPLY_ARRAY) return;\n\n    /* Scan the array reported by COMMAND and fill only the entries that\n     * don't already match what we have. */\n    for (size_t j = 0; j < reply->elements; j++) {\n        redisReply *entry = reply->element[j];\n        if (entry->type != REDIS_REPLY_ARRAY || entry->elements < 4 ||\n            entry->element[0]->type != REDIS_REPLY_STRING ||\n            entry->element[1]->type != REDIS_REPLY_INTEGER ||\n            entry->element[3]->type != REDIS_REPLY_INTEGER) return;\n        char *cmdname = entry->element[0]->str;\n        int i;\n\n        for (i = 0; i < helpEntriesLen; i++) {\n            helpEntry *he = helpEntries+i;\n            if (!strcasecmp(he->argv[0],cmdname))\n                break;\n        }\n        if (i != helpEntriesLen) continue;\n\n        helpEntriesLen++;\n        helpEntries = zrealloc(helpEntries,sizeof(helpEntry)*helpEntriesLen);\n        helpEntry *new = helpEntries+(helpEntriesLen-1);\n\n        new->argc = 1;\n        new->argv = zmalloc(sizeof(sds));\n        new->argv[0] = sdsnew(cmdname);\n        new->full = new->argv[0];\n        new->type = CLI_HELP_COMMAND;\n        sdstoupper(new->argv[0]);\n\n        struct commandHelp *ch = zmalloc(sizeof(*ch));\n        ch->name = new->argv[0];\n        ch->params = sdsempty();\n        int args = llabs(entry->element[1]->integer);\n        args--; /* Remove the command name itself. */\n        if (entry->element[3]->integer == 1) {\n            ch->params = sdscat(ch->params,\"key \");\n            args--;\n        }\n        while(args-- > 0) ch->params = sdscat(ch->params,\"arg \");\n        if (entry->element[1]->integer < 0)\n            ch->params = sdscat(ch->params,\"...options...\");\n        ch->summary = \"Help not available\";\n        ch->group = 0;\n        ch->since = \"not known\";\n        new->org = ch;\n    }\n    freeReplyObject(reply);\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(void) {\n    sds version = cliVersion();\n    printf(\n        \"redis-cli %s\\n\"\n        \"To get help about Redis commands type:\\n\"\n        \"      \\\"help @<group>\\\" to get a list of commands in <group>\\n\"\n        \"      \\\"help <command>\\\" for help on <command>\\n\"\n        \"      \\\"help <tab>\\\" to get a list of possible help topics\\n\"\n        \"      \\\"quit\\\" to exit\\n\"\n        \"\\n\"\n        \"To set redis-cli preferences:\\n\"\n        \"      \\\":set hints\\\" enable online hints\\n\"\n        \"      \\\":set nohints\\\" disable online hints\\n\"\n        \"Set your preferences in ~/.redisclirc\\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\n/* Linenoise completion callback. */\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/* Linenoise hints callback. */\nstatic char *hintsCallback(const char *buf, int *color, int *bold) {\n    if (!pref.hints) return NULL;\n\n    int i, argc, buflen = strlen(buf);\n    sds *argv = sdssplitargs(buf,&argc);\n    int endspace = buflen && isspace(buf[buflen-1]);\n\n    /* Check if the argument list is empty and return ASAP. */\n    if (argc == 0) {\n        sdsfreesplitres(argv,argc);\n        return NULL;\n    }\n\n    for (i = 0; i < helpEntriesLen; i++) {\n        if (!(helpEntries[i].type & CLI_HELP_COMMAND)) continue;\n\n        if (strcasecmp(argv[0],helpEntries[i].full) == 0 ||\n            strcasecmp(buf,helpEntries[i].full) == 0)\n        {\n            *color = 90;\n            *bold = 0;\n            sds hint = sdsnew(helpEntries[i].org->params);\n\n            /* Remove arguments from the returned hint to show only the\n             * ones the user did not yet typed. */\n            int toremove = argc-1;\n            while(toremove > 0 && sdslen(hint)) {\n                if (hint[0] == '[') break;\n                if (hint[0] == ' ') toremove--;\n                sdsrange(hint,1,-1);\n            }\n\n            /* Add an initial space if needed. */\n            if (!endspace) {\n                sds newhint = sdsnewlen(\" \",1);\n                newhint = sdscatsds(newhint,hint);\n                sdsfree(hint);\n                hint = newhint;\n            }\n\n            sdsfreesplitres(argv,argc);\n            return hint;\n        }\n    }\n    sdsfreesplitres(argv,argc);\n    return NULL;\n}\n\nstatic void freeHintsCallback(void *ptr) {\n    sdsfree(ptr);\n}\n\n/*------------------------------------------------------------------------------\n * Networking / parsing\n *--------------------------------------------------------------------------- */\n\n/* Send AUTH command to the server */\nstatic int cliAuth(void) {\n    redisReply *reply;\n    if (config.auth == NULL) return REDIS_OK;\n\n    if (config.user == NULL)\n        reply = redisCommand(context,\"AUTH %s\",config.auth);\n    else\n        reply = redisCommand(context,\"AUTH %s %s\",config.user,config.auth);\n    if (reply != NULL) {\n        if (reply->type == REDIS_REPLY_ERROR)\n            fprintf(stderr,\"Warning: AUTH failed\\n\");\n        freeReplyObject(reply);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\n/* Send SELECT dbnum to the server */\nstatic int cliSelect(void) {\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        int result = REDIS_OK;\n        if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;\n        freeReplyObject(reply);\n        return result;\n    }\n    return REDIS_ERR;\n}\n\n/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if\n * not building with TLS support.\n */\nstatic int cliSecureConnection(redisContext *c, const char **err) {\n#ifdef USE_OPENSSL\n    static SSL_CTX *ssl_ctx = NULL;\n\n    if (!ssl_ctx) {\n        ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n        if (!ssl_ctx) {\n            *err = \"Failed to create SSL_CTX\";\n            goto error;\n        }\n\n        SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);\n        SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);\n\n        if (config.cacert || config.cacertdir) {\n            if (!SSL_CTX_load_verify_locations(ssl_ctx, config.cacert, config.cacertdir)) {\n                *err = \"Invalid CA Certificate File/Directory\";\n                goto error;\n            }\n        } else {\n            if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {\n                *err = \"Failed to use default CA paths\";\n                goto error;\n            }\n        }\n\n        if (config.cert && !SSL_CTX_use_certificate_chain_file(ssl_ctx, config.cert)) {\n            *err = \"Invalid client certificate\";\n            goto error;\n        }\n\n        if (config.key && !SSL_CTX_use_PrivateKey_file(ssl_ctx, config.key, SSL_FILETYPE_PEM)) {\n            *err = \"Invalid private key\";\n            goto error;\n        }\n    }\n\n    SSL *ssl = SSL_new(ssl_ctx);\n    if (!ssl) {\n        *err = \"Failed to create SSL object\";\n        return REDIS_ERR;\n    }\n\n    if (config.sni && !SSL_set_tlsext_host_name(ssl, config.sni)) {\n        *err = \"Failed to configure SNI\";\n        SSL_free(ssl);\n        return REDIS_ERR;\n    }\n\n    return redisInitiateSSL(c, ssl);\n\nerror:\n    SSL_CTX_free(ssl_ctx);\n    ssl_ctx = NULL;\n    return REDIS_ERR;\n#else\n    (void) c;\n    (void) err;\n    return REDIS_OK;\n#endif\n}\n\n/* Select RESP3 mode if redis-cli was started with the -3 option.  */\nstatic int cliSwitchProto(void) {\n    redisReply *reply;\n    if (config.resp3 == 0) return REDIS_OK;\n\n    reply = redisCommand(context,\"HELLO 3\");\n    if (reply != NULL) {\n        int result = REDIS_OK;\n        if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;\n        freeReplyObject(reply);\n        return result;\n    }\n    return REDIS_ERR;\n}\n\n/* Connect to the server. It is possible to pass certain flags to the function:\n *      CC_FORCE: The connection is performed even if there is already\n *                a connected socket.\n *      CC_QUIET: Don't print errors if connection fails. */\nstatic int cliConnect(int flags) {\n    if (context == NULL || flags & CC_FORCE) {\n        if (context != NULL) {\n            redisFree(context);\n        }\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 && config.tls) {\n            const char *err = NULL;\n            if (cliSecureConnection(context, &err) == REDIS_ERR && err) {\n                fprintf(stderr, \"Could not negotiate a TLS connection: %s\\n\", err);\n                context = NULL;\n                redisFree(context);\n                return REDIS_ERR;\n            }\n        }\n\n        if (context->err) {\n            if (!(flags & CC_QUIET)) {\n                fprintf(stderr,\"Could not connect to Redis at \");\n                if (config.hostsocket == NULL)\n                    fprintf(stderr,\"%s:%d: %s\\n\",\n                        config.hostip,config.hostport,context->errstr);\n                else\n                    fprintf(stderr,\"%s: %s\\n\",\n                        config.hostsocket,context->errstr);\n            }\n            redisFree(context);\n            context = NULL;\n            return REDIS_ERR;\n        }\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, select the right DB, switch to RESP3 if needed. */\n        if (cliAuth() != REDIS_OK)\n            return REDIS_ERR;\n        if (cliSelect() != REDIS_OK)\n            return REDIS_ERR;\n        if (cliSwitchProto() != REDIS_OK)\n            return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic void cliPrintContextError(void) {\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_DOUBLE:\n        out = sdscatprintf(out,\"(double) %s\\n\",r->str);\n    break;\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        /* If you are producing output for the standard output we want\n        * a more interesting output with quoted characters and so forth,\n        * unless it's a verbatim string type. */\n        if (r->type == REDIS_REPLY_STRING) {\n            out = sdscatrepr(out,r->str,r->len);\n            out = sdscat(out,\"\\n\");\n        } else {\n            out = sdscatlen(out,r->str,r->len);\n            out = sdscat(out,\"\\n\");\n        }\n    break;\n    case REDIS_REPLY_NIL:\n        out = sdscat(out,\"(nil)\\n\");\n    break;\n    case REDIS_REPLY_BOOL:\n        out = sdscat(out,r->integer ? \"(true)\\n\" : \"(false)\\n\");\n    break;\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n        if (r->elements == 0) {\n            if (r->type == REDIS_REPLY_ARRAY)\n                out = sdscat(out,\"(empty array)\\n\");\n            else if (r->type == REDIS_REPLY_MAP)\n                out = sdscat(out,\"(empty hash)\\n\");\n            else if (r->type == REDIS_REPLY_SET)\n                out = sdscat(out,\"(empty set)\\n\");\n            else\n                out = sdscat(out,\"(empty aggregate type)\\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            if (r->type == REDIS_REPLY_MAP) i /= 2;\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            char numsep;\n            if (r->type == REDIS_REPLY_SET) numsep = '~';\n            else if (r->type == REDIS_REPLY_MAP) numsep = '#';\n            else numsep = ')';\n            snprintf(_prefixfmt,sizeof(_prefixfmt),\"%%s%%%ud%c \",idxlen,numsep);\n\n            for (i = 0; i < r->elements; i++) {\n                unsigned int human_idx = (r->type == REDIS_REPLY_MAP) ?\n                                         i/2 : i;\n                human_idx++; /* Make it 1-based. */\n\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,human_idx);\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                /* For maps, format the value as well. */\n                if (r->type == REDIS_REPLY_MAP) {\n                    i++;\n                    sdsrange(out,0,-2);\n                    out = sdscat(out,\" => \");\n                    tmp = cliFormatReplyTTY(r->element[i],_prefix);\n                    out = sdscatlen(out,tmp,sdslen(tmp));\n                    sdsfree(tmp);\n                }\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\nint isColorTerm(void) {\n    char *t = getenv(\"TERM\");\n    return t != NULL && strstr(t,\"xterm\") != NULL;\n}\n\n/* Helper  function for sdsCatColorizedLdbReply() appending colorize strings\n * to an SDS string. */\nsds sdscatcolor(sds o, char *s, size_t len, char *color) {\n    if (!isColorTerm()) return sdscatlen(o,s,len);\n\n    int bold = strstr(color,\"bold\") != NULL;\n    int ccode = 37; /* Defaults to white. */\n    if (strstr(color,\"red\")) ccode = 31;\n    else if (strstr(color,\"green\")) ccode = 32;\n    else if (strstr(color,\"yellow\")) ccode = 33;\n    else if (strstr(color,\"blue\")) ccode = 34;\n    else if (strstr(color,\"magenta\")) ccode = 35;\n    else if (strstr(color,\"cyan\")) ccode = 36;\n    else if (strstr(color,\"white\")) ccode = 37;\n\n    o = sdscatfmt(o,\"\\033[%i;%i;49m\",bold,ccode);\n    o = sdscatlen(o,s,len);\n    o = sdscat(o,\"\\033[0m\");\n    return o;\n}\n\n/* Colorize Lua debugger status replies according to the prefix they\n * have. */\nsds sdsCatColorizedLdbReply(sds o, char *s, size_t len) {\n    char *color = \"white\";\n\n    if (strstr(s,\"<debug>\")) color = \"bold\";\n    if (strstr(s,\"<redis>\")) color = \"green\";\n    if (strstr(s,\"<reply>\")) color = \"cyan\";\n    if (strstr(s,\"<error>\")) color = \"red\";\n    if (strstr(s,\"<hint>\")) color = \"bold\";\n    if (strstr(s,\"<value>\") || strstr(s,\"<retval>\")) color = \"magenta\";\n    if (len > 4 && isdigit(s[3])) {\n        if (s[1] == '>') color = \"yellow\"; /* Current line. */\n        else if (s[2] == '#') color = \"bold\"; /* Break point. */\n    }\n    return sdscatcolor(o,s,len,color);\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    case REDIS_REPLY_VERB:\n        if (r->type == REDIS_REPLY_STATUS && config.eval_ldb) {\n            /* The Lua debugger replies with arrays of simple (status)\n             * strings. We colorize the output for more fun if this\n             * is a debugging session. */\n\n            /* Detect the end of a debugging session. */\n            if (strstr(r->str,\"<endsession>\") == r->str) {\n                config.enable_ldb_on_eval = 0;\n                config.eval_ldb = 0;\n                config.eval_ldb_end = 1; /* Signal the caller session ended. */\n                config.output = OUTPUT_STANDARD;\n                cliRefreshPrompt();\n            } else {\n                out = sdsCatColorizedLdbReply(out,r->str,r->len);\n            }\n        } else {\n            out = sdscatlen(out,r->str,r->len);\n        }\n        break;\n    case REDIS_REPLY_BOOL:\n        out = sdscat(out,r->integer ? \"(true)\" : \"(false)\");\n    break;\n    case REDIS_REPLY_INTEGER:\n        out = sdscatprintf(out,\"%lld\",r->integer);\n        break;\n    case REDIS_REPLY_DOUBLE:\n        out = sdscatprintf(out,\"%s\",r->str);\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    case REDIS_REPLY_MAP:\n        for (i = 0; i < r->elements; i += 2) {\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            out = sdscatlen(out,\" \",1);\n            tmp = cliFormatReplyRaw(r->element[i+1]);\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_DOUBLE:\n        out = sdscatprintf(out,\"%s\",r->str);\n        break;\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        out = sdscatrepr(out,r->str,r->len);\n    break;\n    case REDIS_REPLY_NIL:\n        out = sdscat(out,\"NULL\");\n    break;\n    case REDIS_REPLY_BOOL:\n        out = sdscat(out,r->integer ? \"true\" : \"false\");\n    break;\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP: /* CSV has no map type, just output flat list. */\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            redisFree(context);\n            context = NULL;\n            return REDIS_OK;\n        }\n        if (config.interactive) {\n            /* Filter cases where we should reconnect */\n            if (context->err == REDIS_ERR_IO &&\n                (errno == ECONNRESET || errno == EPIPE))\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    config.last_cmd_type = reply->type;\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 = strrchr(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, long repeat) {\n    char *command = argv[0];\n    size_t *argvlen;\n    int j, output_raw;\n\n    if (!config.eval_ldb && /* In debugging mode, let's pass \"help\" to Redis. */\n        (!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        !strcasecmp(command,\"lolwut\") ||\n        (argc >= 2 && !strcasecmp(command,\"debug\") &&\n                       !strcasecmp(argv[1],\"htstats\")) ||\n        (argc >= 2 && !strcasecmp(command,\"debug\") &&\n                       !strcasecmp(argv[1],\"htstats-key\")) ||\n        (argc >= 2 && !strcasecmp(command,\"memory\") &&\n                      (!strcasecmp(argv[1],\"malloc-stats\") ||\n                       !strcasecmp(argv[1],\"doctor\"))) ||\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        (argc == 3 && !strcasecmp(command,\"latency\") &&\n                       !strcasecmp(argv[1],\"graph\")) ||\n        (argc == 2 && !strcasecmp(command,\"latency\") &&\n                       !strcasecmp(argv[1],\"doctor\")) ||\n        /* Format PROXY INFO command for Redis Cluster Proxy:\n         * https://github.com/artix75/redis-cluster-proxy */\n        (argc >= 2 && !strcasecmp(command,\"proxy\") &&\n                       !strcasecmp(argv[1],\"info\")))\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    /* When the user manually calls SCRIPT DEBUG, setup the activation of\n     * debugging mode on the next eval if needed. */\n    if (argc == 3 && !strcasecmp(argv[0],\"script\") &&\n                     !strcasecmp(argv[1],\"debug\"))\n    {\n        if (!strcasecmp(argv[2],\"yes\") || !strcasecmp(argv[2],\"sync\")) {\n            config.enable_ldb_on_eval = 1;\n        } else {\n            config.enable_ldb_on_eval = 0;\n        }\n    }\n\n    /* Actually activate LDB on EVAL if needed. */\n    if (!strcasecmp(command,\"eval\") && config.enable_ldb_on_eval) {\n        config.eval_ldb = 1;\n        config.output = OUTPUT_RAW;\n    }\n\n    /* Setup argument length */\n    argvlen = zmalloc(argc*sizeof(size_t));\n    for (j = 0; j < argc; j++)\n        argvlen[j] = sdslen(argv[j]);\n\n    /* Negative repeat is allowed and causes infinite loop,\n       works well with the interval option. */\n    while(repeat < 0 || repeat-- > 0) {\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 replica output mode...  (press Ctrl-C to quit)\\n\");\n            slaveMode();\n            config.slave_mode = 0;\n            zfree(argvlen);\n            return REDIS_ERR;  /* Error = slaveMode lost connection to master */\n        }\n\n        if (cliReadReply(output_raw) != REDIS_OK) {\n            zfree(argvlen);\n            return REDIS_ERR;\n        } else {\n            /* Store database number when SELECT was successfully executed. */\n            if (!strcasecmp(command,\"select\") && argc == 2 && config.last_cmd_type != REDIS_REPLY_ERROR) {\n                config.dbnum = atoi(argv[1]);\n                cliRefreshPrompt();\n            } else if (!strcasecmp(command,\"auth\") && (argc == 2 || argc == 3))\n            {\n                cliSelect();\n            }\n        }\n        if (config.cluster_reissue_command){\n            /* If we need to reissue the command, break to prevent a\n               further 'repeat' number of dud interations */\n            break;\n        }\n        if (config.interval) usleep(config.interval);\n        fflush(stdout); /* Make it grep friendly */\n    }\n\n    zfree(argvlen);\n    return REDIS_OK;\n}\n\n/* Send a command reconnecting the link if needed. */\nstatic redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {\n    redisReply *reply = NULL;\n    int tries = 0;\n    va_list ap;\n\n    assert(!c->err);\n    while(reply == NULL) {\n        while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {\n            printf(\"\\r\\x1b[0K\"); /* Cursor to left edge + clear line. */\n            printf(\"Reconnecting... %d\\r\", ++tries);\n            fflush(stdout);\n\n            redisFree(c);\n            c = redisConnect(config.hostip,config.hostport);\n            if (!c->err && config.tls) {\n                const char *err = NULL;\n                if (cliSecureConnection(c, &err) == REDIS_ERR && err) {\n                    fprintf(stderr, \"TLS Error: %s\\n\", err);\n                    exit(1);\n                }\n            }\n            usleep(1000000);\n        }\n\n        va_start(ap,fmt);\n        reply = redisvCommand(c,fmt,ap);\n        va_end(ap);\n\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(\"\\r\\x1b[0K\"); /* Cursor to left edge + clear line. */\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], \"--no-auth-warning\")) {\n            config.no_auth_warning = 1;\n        } else if (!strcmp(argv[i], \"--askpass\")) {\n            config.askpass = 1;\n        } else if ((!strcmp(argv[i],\"-a\") || !strcmp(argv[i],\"--pass\"))\n                   && !lastarg)\n        {\n            config.auth = argv[++i];\n        } else if (!strcmp(argv[i],\"--user\") && !lastarg) {\n            config.user = argv[++i];\n        } else if (!strcmp(argv[i],\"-u\") && !lastarg) {\n            parseRedisUri(argv[++i]);\n        } else if (!strcmp(argv[i],\"--raw\")) {\n            config.output = OUTPUT_RAW;\n        } else if (!strcmp(argv[i],\"--no-raw\")) {\n            config.output = OUTPUT_STANDARD;\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-dist\")) {\n            config.latency_dist_mode = 1;\n        } else if (!strcmp(argv[i],\"--mono\")) {\n            spectrum_palette = spectrum_palette_mono;\n            spectrum_palette_size = spectrum_palette_mono_size;\n        } else if (!strcmp(argv[i],\"--latency-history\")) {\n            config.latency_mode = 1;\n            config.latency_history = 1;\n        } else if (!strcmp(argv[i],\"--lru-test\") && !lastarg) {\n            config.lru_test_mode = 1;\n            config.lru_test_sample_size = strtoll(argv[++i],NULL,10);\n        } else if (!strcmp(argv[i],\"--slave\")) {\n            config.slave_mode = 1;\n        } else if (!strcmp(argv[i],\"--replica\")) {\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],\"--memkeys\")) {\n            config.memkeys = 1;\n            config.memkeys_samples = 0; /* use redis default */\n        } else if (!strcmp(argv[i],\"--memkeys-samples\")) {\n            config.memkeys = 1;\n            config.memkeys_samples = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--hotkeys\")) {\n            config.hotkeys = 1;\n        } else if (!strcmp(argv[i],\"--eval\") && !lastarg) {\n            config.eval = argv[++i];\n        } else if (!strcmp(argv[i],\"--ldb\")) {\n            config.eval_ldb = 1;\n            config.output = OUTPUT_RAW;\n        } else if (!strcmp(argv[i],\"--ldb-sync-mode\")) {\n            config.eval_ldb = 1;\n            config.eval_ldb_sync = 1;\n            config.output = OUTPUT_RAW;\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],\"--verbose\")) {\n            config.verbose = 1;\n        } else if (!strcmp(argv[i],\"--cluster\") && !lastarg) {\n            if (CLUSTER_MANAGER_MODE()) usage();\n            char *cmd = argv[++i];\n            int j = i;\n            while (j < argc && argv[j][0] != '-') j++;\n            if (j > i) j--;\n            createClusterManagerCommand(cmd, j - i, argv + i + 1);\n            i = j;\n        } else if (!strcmp(argv[i],\"--cluster\") && lastarg) {\n            usage();\n        } else if (!strcmp(argv[i],\"--cluster-replicas\") && !lastarg) {\n            config.cluster_manager_command.replicas = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-master-id\") && !lastarg) {\n            config.cluster_manager_command.master_id = argv[++i];\n        } else if (!strcmp(argv[i],\"--cluster-from\") && !lastarg) {\n            config.cluster_manager_command.from = argv[++i];\n        } else if (!strcmp(argv[i],\"--cluster-to\") && !lastarg) {\n            config.cluster_manager_command.to = argv[++i];\n        } else if (!strcmp(argv[i],\"--cluster-weight\") && !lastarg) {\n            if (config.cluster_manager_command.weight != NULL) {\n                fprintf(stderr, \"WARNING: you cannot use --cluster-weight \"\n                                \"more than once.\\n\"\n                                \"You can set more weights by adding them \"\n                                \"as a space-separated list, ie:\\n\"\n                                \"--cluster-weight n1=w n2=w\\n\");\n                exit(1);\n            }\n            int widx = i + 1;\n            char **weight = argv + widx;\n            int wargc = 0;\n            for (; widx < argc; widx++) {\n                if (strstr(argv[widx], \"--\") == argv[widx]) break;\n                if (strchr(argv[widx], '=') == NULL) break;\n                wargc++;\n            }\n            if (wargc > 0) {\n                config.cluster_manager_command.weight = weight;\n                config.cluster_manager_command.weight_argc = wargc;\n                i += wargc;\n            }\n        } else if (!strcmp(argv[i],\"--cluster-slots\") && !lastarg) {\n            config.cluster_manager_command.slots = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-timeout\") && !lastarg) {\n            config.cluster_manager_command.timeout = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-pipeline\") && !lastarg) {\n            config.cluster_manager_command.pipeline = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-threshold\") && !lastarg) {\n            config.cluster_manager_command.threshold = atof(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-yes\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_YES;\n        } else if (!strcmp(argv[i],\"--cluster-simulate\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_SIMULATE;\n        } else if (!strcmp(argv[i],\"--cluster-replace\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_REPLACE;\n        } else if (!strcmp(argv[i],\"--cluster-copy\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_COPY;\n        } else if (!strcmp(argv[i],\"--cluster-slave\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_SLAVE;\n        } else if (!strcmp(argv[i],\"--cluster-use-empty-masters\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;\n        } else if (!strcmp(argv[i],\"--cluster-search-multiple-owners\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;\n        } else if (!strcmp(argv[i],\"--cluster-fix-with-unreachable-masters\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;\n#ifdef USE_OPENSSL\n        } else if (!strcmp(argv[i],\"--tls\")) {\n            config.tls = 1;\n        } else if (!strcmp(argv[i],\"--sni\") && !lastarg) {\n            config.sni = argv[++i];\n        } else if (!strcmp(argv[i],\"--cacertdir\") && !lastarg) {\n            config.cacertdir = argv[++i];\n        } else if (!strcmp(argv[i],\"--cacert\") && !lastarg) {\n            config.cacert = argv[++i];\n        } else if (!strcmp(argv[i],\"--cert\") && !lastarg) {\n            config.cert = argv[++i];\n        } else if (!strcmp(argv[i],\"--key\") && !lastarg) {\n            config.key = argv[++i];\n#endif\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 if (!strcmp(argv[i],\"-3\")) {\n            config.resp3 = 1;\n        } else if (CLUSTER_MANAGER_MODE() && argv[i][0] != '-') {\n            if (config.cluster_manager_command.argc == 0) {\n                int j = i + 1;\n                while (j < argc && argv[j][0] != '-') j++;\n                int cmd_argc = j - i;\n                config.cluster_manager_command.argc = cmd_argc;\n                config.cluster_manager_command.argv = argv + i;\n                if (cmd_argc > 1) i = j - 1;\n            }\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\n    /* --ldb requires --eval. */\n    if (config.eval_ldb && config.eval == NULL) {\n        fprintf(stderr,\"Options --ldb and --ldb-sync-mode require --eval.\\n\");\n        fprintf(stderr,\"Try %s --help for more information.\\n\", argv[0]);\n        exit(1);\n    }\n\n    if (!config.no_auth_warning && config.auth != NULL) {\n        fputs(\"Warning: Using a password with '-a' or '-u' option on the command\"\n              \" line interface may not be safe.\\n\", stderr);\n    }\n\n    return i;\n}\n\nstatic void parseEnv() {\n    /* Set auth from env, but do not overwrite CLI arguments if passed */\n    char *auth = getenv(REDIS_CLI_AUTH_ENV);\n    if (auth != NULL && config.auth == NULL) {\n        config.auth = auth;\n    }\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(void) {\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\"                     You can also use the \" REDIS_CLI_AUTH_ENV \" environment\\n\"\n\"                     variable to pass this password more safely\\n\"\n\"                     (if both are used, this argument takes predecence).\\n\"\n\"  --user <username>  Used to send ACL style 'AUTH username pass'. Needs -a.\\n\"\n\"  --pass <password>  Alias of -a for consistency with the new --user option.\\n\"\n\"  --askpass          Force user to input password with mask from STDIN.\\n\"\n\"                     If this argument is used, '-a' and \" REDIS_CLI_AUTH_ENV \"\\n\"\n\"                     environment variable will be ignored.\\n\"\n\"  -u <uri>           Server URI.\\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\"  -3                 Start session in RESP3 protocol mode.\\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#ifdef USE_OPENSSL\n\"  --tls              Establish a secure TLS connection.\\n\"\n\"  --sni <host>       Server name indication for TLS.\\n\"\n\"  --cacert <file>    CA Certificate file to verify with.\\n\"\n\"  --cacertdir <dir>  Directory where trusted CA certificates are stored.\\n\"\n\"                     If neither cacert nor cacertdir are specified, the default\\n\"\n\"                     system-wide trusted root certs configuration will apply.\\n\"\n\"  --cert <file>      Client certificate to authenticate with.\\n\"\n\"  --key <file>       Private key file to authenticate with.\\n\"\n#endif\n\"  --raw              Use raw formatting for replies (default when STDOUT is\\n\"\n\"                     not a tty).\\n\"\n\"  --no-raw           Force formatted output even when STDOUT is not a tty.\\n\"\n\"  --csv              Output in CSV format.\\n\"\n\"  --stat             Print rolling stats about server: mem, clients, ...\\n\"\n\"  --latency          Enter a special mode continuously sampling latency.\\n\"\n\"                     If you use this mode in an interactive session it runs\\n\"\n\"                     forever displaying real-time stats. Otherwise if --raw or\\n\"\n\"                     --csv is specified, or if you redirect the output to a non\\n\"\n\"                     TTY, it samples the latency for 1 second (you can use\\n\"\n\"                     -i to change the interval), then produces a single output\\n\"\n\"                     and exits.\\n\",version);\n\n    fprintf(stderr,\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\"  --latency-dist     Shows latency as a spectrum, requires xterm 256 colors.\\n\"\n\"                     Default time interval is 1 sec. Change it using -i.\\n\"\n\"  --lru-test <keys>  Simulate a cache workload with an 80-20 distribution.\\n\"\n\"  --replica          Simulate a replica 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    REDIS_CLI_DEFAULT_PIPE_TIMEOUT);\n    fprintf(stderr,\n\"  --bigkeys          Sample Redis keys looking for keys with many elements (complexity).\\n\"\n\"  --memkeys          Sample Redis keys looking for keys consuming a lot of memory.\\n\"\n\"  --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.\\n\"\n\"                     And define number of key elements to sample\\n\"\n\"  --hotkeys          Sample Redis keys looking for hot keys.\\n\"\n\"                     only works when maxmemory-policy is *lfu.\\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\"  --ldb              Used with --eval enable the Redis Lua debugger.\\n\"\n\"  --ldb-sync-mode    Like --ldb but uses the synchronous Lua debugger, in\\n\"\n\"                     this mode the server is blocked and script changes are\\n\"\n\"                     not rolled back from the server memory.\\n\"\n\"  --cluster <command> [args...] [opts...]\\n\"\n\"                     Cluster Manager command and arguments (see below).\\n\"\n\"  --verbose          Verbose mode.\\n\"\n\"  --no-auth-warning  Don't show warning message when using password on command\\n\"\n\"                     line interface.\\n\"\n\"  --help             Output this help and exit.\\n\"\n\"  --version          Output version and exit.\\n\"\n\"\\n\");\n    /* Using another fprintf call to avoid -Woverlength-strings compile warning */\n    fprintf(stderr,\n\"Cluster Manager Commands:\\n\"\n\"  Use --cluster help to list all available cluster manager commands.\\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\"and settings.\\n\"\n\"\\n\");\n    sdsfree(version);\n    exit(1);\n}\n\nstatic int confirmWithYes(char *msg) {\n    printf(\"%s (type 'yes' to accept): \", msg);\n    fflush(stdout);\n    char buf[4];\n    int nread = read(fileno(stdin),buf,4);\n    buf[3] = '\\0';\n    return (nread != 0 && !strcmp(\"yes\", buf));\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\nstatic int issueCommandRepeat(int argc, char **argv, long repeat) {\n    while (1) {\n        config.cluster_reissue_command = 0;\n        if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {\n            cliConnect(CC_FORCE);\n\n            /* If we still cannot send the command print error.\n             * We'll try to reconnect the next time. */\n            if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {\n                cliPrintContextError();\n                return REDIS_ERR;\n            }\n        }\n        /* Issue the command again if we got redirected in cluster mode */\n        if (config.cluster_mode && config.cluster_reissue_command) {\n            cliConnect(CC_FORCE);\n        } else {\n            break;\n        }\n    }\n    return REDIS_OK;\n}\n\nstatic int issueCommand(int argc, char **argv) {\n    return issueCommandRepeat(argc, argv, config.repeat);\n}\n\n/* Split the user provided command into multiple SDS arguments.\n * This function normally uses sdssplitargs() from sds.c which is able\n * to understand \"quoted strings\", escapes and so forth. However when\n * we are in Lua debugging mode and the \"eval\" command is used, we want\n * the remaining Lua script (after \"e \" or \"eval \") to be passed verbatim\n * as a single big argument. */\nstatic sds *cliSplitArgs(char *line, int *argc) {\n    if (config.eval_ldb && (strstr(line,\"eval \") == line ||\n                            strstr(line,\"e \") == line))\n    {\n        sds *argv = sds_malloc(sizeof(sds)*2);\n        *argc = 2;\n        int len = strlen(line);\n        int elen = line[1] == ' ' ? 2 : 5; /* \"e \" or \"eval \"? */\n        argv[0] = sdsnewlen(line,elen-1);\n        argv[1] = sdsnewlen(line+elen,len-elen);\n        return argv;\n    } else {\n        return sdssplitargs(line,argc);\n    }\n}\n\n/* Set the CLI preferences. This function is invoked when an interactive\n * \":command\" is called, or when reading ~/.redisclirc file, in order to\n * set user preferences. */\nvoid cliSetPreferences(char **argv, int argc, int interactive) {\n    if (!strcasecmp(argv[0],\":set\") && argc >= 2) {\n        if (!strcasecmp(argv[1],\"hints\")) pref.hints = 1;\n        else if (!strcasecmp(argv[1],\"nohints\")) pref.hints = 0;\n        else {\n            printf(\"%sunknown redis-cli preference '%s'\\n\",\n                interactive ? \"\" : \".redisclirc: \",\n                argv[1]);\n        }\n    } else {\n        printf(\"%sunknown redis-cli internal command '%s'\\n\",\n            interactive ? \"\" : \".redisclirc: \",\n            argv[0]);\n    }\n}\n\n/* Load the ~/.redisclirc file if any. */\nvoid cliLoadPreferences(void) {\n    sds rcfile = getDotfilePath(REDIS_CLI_RCFILE_ENV,REDIS_CLI_RCFILE_DEFAULT);\n    if (rcfile == NULL) return;\n    FILE *fp = fopen(rcfile,\"r\");\n    char buf[1024];\n\n    if (fp) {\n        while(fgets(buf,sizeof(buf),fp) != NULL) {\n            sds *argv;\n            int argc;\n\n            argv = sdssplitargs(buf,&argc);\n            if (argc > 0) cliSetPreferences(argv,argc,0);\n            sdsfreesplitres(argv,argc);\n        }\n        fclose(fp);\n    }\n    sdsfree(rcfile);\n}\n\nstatic void repl(void) {\n    sds historyfile = NULL;\n    int history = 0;\n    char *line;\n    int argc;\n    sds *argv;\n\n    /* Initialize the help and, if possible, use the COMMAND command in order\n     * to retrieve missing entries. */\n    cliInitHelp();\n    cliIntegrateHelp();\n\n    config.interactive = 1;\n    linenoiseSetMultiLine(1);\n    linenoiseSetCompletionCallback(completionCallback);\n    linenoiseSetHintsCallback(hintsCallback);\n    linenoiseSetFreeHintsCallback(freeHintsCallback);\n\n    /* Only use history and load the rc file when stdin is a tty. */\n    if (isatty(fileno(stdin))) {\n        historyfile = getDotfilePath(REDIS_CLI_HISTFILE_ENV,REDIS_CLI_HISTFILE_DEFAULT);\n        //keep in-memory history always regardless if history file can be determined\n        history = 1;\n        if (historyfile != NULL) {\n            linenoiseHistoryLoad(historyfile);\n        }\n        cliLoadPreferences();\n    }\n\n    cliRefreshPrompt();\n    while((line = linenoise(context ? config.prompt : \"not connected> \")) != NULL) {\n        if (line[0] != '\\0') {\n            long repeat = 1;\n            int skipargs = 0;\n            char *endptr = NULL;\n\n            argv = cliSplitArgs(line,&argc);\n\n            /* check if we have a repeat command option and\n             * need to skip the first arg */\n            if (argv && argc > 0) {\n                errno = 0;\n                repeat = strtol(argv[0], &endptr, 10);\n                if (argc > 1 && *endptr == '\\0') {\n                    if (errno == ERANGE || errno == EINVAL || repeat <= 0) {\n                        fputs(\"Invalid redis-cli repeat command option value.\\n\", stdout);\n                        sdsfreesplitres(argv, argc);\n                        linenoiseFree(line);\n                        continue;\n                    }\n                    skipargs = 1;\n                } else {\n                    repeat = 1;\n                }\n            }\n\n            /* Won't save auth or acl setuser commands in history file */\n            int dangerous = 0;\n            if (argv && argc > 0) {\n                if (!strcasecmp(argv[skipargs], \"auth\")) {\n                    dangerous = 1;\n                } else if (skipargs+1 < argc &&\n                           !strcasecmp(argv[skipargs], \"acl\") &&\n                           !strcasecmp(argv[skipargs+1], \"setuser\"))\n                {\n                    dangerous = 1;\n                }\n            }\n\n            if (!dangerous) {\n                if (history) linenoiseHistoryAdd(line);\n                if (historyfile) linenoiseHistorySave(historyfile);\n            }\n\n            if (argv == NULL) {\n                printf(\"Invalid argument(s)\\n\");\n                linenoiseFree(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 (argv[0][0] == ':') {\n                    cliSetPreferences(argv,argc,1);\n                    sdsfreesplitres(argv,argc);\n                    linenoiseFree(line);\n                    continue;\n                } else if (strcasecmp(argv[0],\"restart\") == 0) {\n                    if (config.eval) {\n                        config.eval_ldb = 1;\n                        config.output = OUTPUT_RAW;\n                        sdsfreesplitres(argv,argc);\n                        linenoiseFree(line);\n                        return; /* Return to evalMode to restart the session. */\n                    } else {\n                        printf(\"Use 'restart' only in Lua debugging mode.\");\n                    }\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(CC_FORCE);\n                } else if (argc == 1 && !strcasecmp(argv[0],\"clear\")) {\n                    linenoiseClearScreen();\n                } else {\n                    long long start_time = mstime(), elapsed;\n\n                    issueCommandRepeat(argc-skipargs, argv+skipargs, repeat);\n\n                    /* If our debugging session ended, show the EVAL final\n                     * reply. */\n                    if (config.eval_ldb_end) {\n                        config.eval_ldb_end = 0;\n                        cliReadReply(0);\n                        printf(\"\\n(Lua debugging session ended%s)\\n\\n\",\n                            config.eval_ldb_sync ? \"\" :\n                            \" -- dataset changes rolled back\");\n                    }\n\n                    elapsed = mstime()-start_time;\n                    if (elapsed >= 500 &&\n                        config.output == OUTPUT_STANDARD)\n                    {\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        linenoiseFree(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 = issueCommand(argc+1, argv);\n    } else {\n        retval = issueCommand(argc, argv);\n    }\n    return retval;\n}\n\n/*------------------------------------------------------------------------------\n * Eval mode\n *--------------------------------------------------------------------------- */\n\nstatic int evalMode(int argc, char **argv) {\n    sds script = NULL;\n    FILE *fp;\n    char buf[1024];\n    size_t nread;\n    char **argv2;\n    int j, got_comma, keys;\n    int retval = REDIS_OK;\n\n    while(1) {\n        if (config.eval_ldb) {\n            printf(\n            \"Lua debugging session started, please use:\\n\"\n            \"quit    -- End the session.\\n\"\n            \"restart -- Restart the script in debug mode again.\\n\"\n            \"help    -- Show Lua script debugging commands.\\n\\n\"\n            );\n        }\n\n        sdsfree(script);\n        script = sdsempty();\n        got_comma = 0;\n        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        /* If we are debugging a script, enable the Lua debugger. */\n        if (config.eval_ldb) {\n            redisReply *reply = redisCommand(context,\n                    config.eval_ldb_sync ?\n                    \"SCRIPT DEBUG sync\": \"SCRIPT DEBUG yes\");\n            if (reply) freeReplyObject(reply);\n        }\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        int eval_ldb = config.eval_ldb; /* Save it, may be reverteed. */\n        retval = issueCommand(argc+3-got_comma, argv2);\n        if (eval_ldb) {\n            if (!config.eval_ldb) {\n                /* If the debugging session ended immediately, there was an\n                 * error compiling the script. Show it and they don't enter\n                 * the REPL at all. */\n                printf(\"Eval debugging session can't start:\\n\");\n                cliReadReply(0);\n                break; /* Return to the caller. */\n            } else {\n                strncpy(config.prompt,\"lua debugger> \",sizeof(config.prompt));\n                repl();\n                /* Restart the session if repl() returned. */\n                cliConnect(CC_FORCE);\n                printf(\"\\n\");\n            }\n        } else {\n            break; /* Return to the caller. */\n        }\n    }\n    return retval;\n}\n\n/*------------------------------------------------------------------------------\n * Cluster Manager\n *--------------------------------------------------------------------------- */\n\n/* The Cluster Manager global structure */\nstatic struct clusterManager {\n    list *nodes;    /* List of nodes in the configuration. */\n    list *errors;\n    int unreachable_masters;    /* Masters we are not able to reach. */\n} cluster_manager;\n\n/* Used by clusterManagerFixSlotsCoverage */\ndict *clusterManagerUncoveredSlots = NULL;\n\ntypedef struct clusterManagerNode {\n    redisContext *context;\n    sds name;\n    char *ip;\n    int port;\n    uint64_t current_epoch;\n    time_t ping_sent;\n    time_t ping_recv;\n    int flags;\n    list *flags_str; /* Flags string representations */\n    sds replicate;  /* Master ID if node is a slave */\n    int dirty;      /* Node has changes that can be flushed */\n    uint8_t slots[CLUSTER_MANAGER_SLOTS];\n    int slots_count;\n    int replicas_count;\n    list *friends;\n    sds *migrating; /* An array of sds where even strings are slots and odd\n                     * strings are the destination node IDs. */\n    sds *importing; /* An array of sds where even strings are slots and odd\n                     * strings are the source node IDs. */\n    int migrating_count; /* Length of the migrating array (migrating slots*2) */\n    int importing_count; /* Length of the importing array (importing slots*2) */\n    float weight;   /* Weight used by rebalance */\n    int balance;    /* Used by rebalance */\n} clusterManagerNode;\n\n/* Data structure used to represent a sequence of cluster nodes. */\ntypedef struct clusterManagerNodeArray {\n    clusterManagerNode **nodes; /* Actual nodes array */\n    clusterManagerNode **alloc; /* Pointer to the allocated memory */\n    int len;                    /* Actual length of the array */\n    int count;                  /* Non-NULL nodes count */\n} clusterManagerNodeArray;\n\n/* Used for the reshard table. */\ntypedef struct clusterManagerReshardTableItem {\n    clusterManagerNode *source;\n    int slot;\n} clusterManagerReshardTableItem;\n\n/* Info about a cluster internal link. */\n\ntypedef struct clusterManagerLink {\n    sds node_name;\n    sds node_addr;\n    int connected;\n    int handshaking;\n} clusterManagerLink;\n\nstatic dictType clusterManagerDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* key destructor */\n    dictSdsDestructor          /* val destructor */\n};\n\nstatic dictType clusterManagerLinkDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    dictSdsDestructor,         /* key destructor */\n    dictListDestructor         /* val destructor */\n};\n\ntypedef int clusterManagerCommandProc(int argc, char **argv);\ntypedef int (*clusterManagerOnReplyError)(redisReply *reply,\n    clusterManagerNode *n, int bulk_idx);\n\n/* Cluster Manager helper functions */\n\nstatic clusterManagerNode *clusterManagerNewNode(char *ip, int port);\nstatic clusterManagerNode *clusterManagerNodeByName(const char *name);\nstatic clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char *n);\nstatic void clusterManagerNodeResetSlots(clusterManagerNode *node);\nstatic int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err);\nstatic void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,\n                                                   char *err);\nstatic int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,\n                                      char **err);\nstatic int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts);\nstatic int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err);\nstatic int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,\n    int ip_count, clusterManagerNode ***offending, int *offending_len);\nstatic void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,\n    int ip_count);\nstatic sds clusterManagerNodeInfo(clusterManagerNode *node, int indent);\nstatic void clusterManagerShowNodes(void);\nstatic void clusterManagerShowClusterInfo(void);\nstatic int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err);\nstatic void clusterManagerWaitForClusterJoin(void);\nstatic int clusterManagerCheckCluster(int quiet);\nstatic void clusterManagerLog(int level, const char* fmt, ...);\nstatic int clusterManagerIsConfigConsistent(void);\nstatic dict *clusterManagerGetLinkStatus(void);\nstatic void clusterManagerOnError(sds err);\nstatic void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,\n                                        int len);\nstatic void clusterManagerNodeArrayReset(clusterManagerNodeArray *array);\nstatic void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,\n                                         clusterManagerNode **nodeptr);\nstatic void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,\n                                       clusterManagerNode *node);\n\n/* Cluster Manager commands. */\n\nstatic int clusterManagerCommandCreate(int argc, char **argv);\nstatic int clusterManagerCommandAddNode(int argc, char **argv);\nstatic int clusterManagerCommandDeleteNode(int argc, char **argv);\nstatic int clusterManagerCommandInfo(int argc, char **argv);\nstatic int clusterManagerCommandCheck(int argc, char **argv);\nstatic int clusterManagerCommandFix(int argc, char **argv);\nstatic int clusterManagerCommandReshard(int argc, char **argv);\nstatic int clusterManagerCommandRebalance(int argc, char **argv);\nstatic int clusterManagerCommandSetTimeout(int argc, char **argv);\nstatic int clusterManagerCommandImport(int argc, char **argv);\nstatic int clusterManagerCommandCall(int argc, char **argv);\nstatic int clusterManagerCommandHelp(int argc, char **argv);\nstatic int clusterManagerCommandBackup(int argc, char **argv);\n\ntypedef struct clusterManagerCommandDef {\n    char *name;\n    clusterManagerCommandProc *proc;\n    int arity;\n    char *args;\n    char *options;\n} clusterManagerCommandDef;\n\nclusterManagerCommandDef clusterManagerCommands[] = {\n    {\"create\", clusterManagerCommandCreate, -2, \"host1:port1 ... hostN:portN\",\n     \"replicas <arg>\"},\n    {\"check\", clusterManagerCommandCheck, -1, \"host:port\",\n     \"search-multiple-owners\"},\n    {\"info\", clusterManagerCommandInfo, -1, \"host:port\", NULL},\n    {\"fix\", clusterManagerCommandFix, -1, \"host:port\",\n     \"search-multiple-owners,fix-with-unreachable-masters\"},\n    {\"reshard\", clusterManagerCommandReshard, -1, \"host:port\",\n     \"from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,\"\n     \"replace\"},\n    {\"rebalance\", clusterManagerCommandRebalance, -1, \"host:port\",\n     \"weight <node1=w1...nodeN=wN>,use-empty-masters,\"\n     \"timeout <arg>,simulate,pipeline <arg>,threshold <arg>,replace\"},\n    {\"add-node\", clusterManagerCommandAddNode, 2,\n     \"new_host:new_port existing_host:existing_port\", \"slave,master-id <arg>\"},\n    {\"del-node\", clusterManagerCommandDeleteNode, 2, \"host:port node_id\",NULL},\n    {\"call\", clusterManagerCommandCall, -2,\n        \"host:port command arg arg .. arg\", NULL},\n    {\"set-timeout\", clusterManagerCommandSetTimeout, 2,\n     \"host:port milliseconds\", NULL},\n    {\"import\", clusterManagerCommandImport, 1, \"host:port\",\n     \"from <arg>,copy,replace\"},\n    {\"backup\", clusterManagerCommandBackup, 2,  \"host:port backup_directory\",\n     NULL},\n    {\"help\", clusterManagerCommandHelp, 0, NULL, NULL}\n};\n\nstatic void getRDB(clusterManagerNode *node);\n\nstatic void createClusterManagerCommand(char *cmdname, int argc, char **argv) {\n    clusterManagerCommand *cmd = &config.cluster_manager_command;\n    cmd->name = cmdname;\n    cmd->argc = argc;\n    cmd->argv = argc ? argv : NULL;\n    if (isColorTerm()) cmd->flags |= CLUSTER_MANAGER_CMD_FLAG_COLOR;\n}\n\n\nstatic clusterManagerCommandProc *validateClusterManagerCommand(void) {\n    int i, commands_count = sizeof(clusterManagerCommands) /\n                            sizeof(clusterManagerCommandDef);\n    clusterManagerCommandProc *proc = NULL;\n    char *cmdname = config.cluster_manager_command.name;\n    int argc = config.cluster_manager_command.argc;\n    for (i = 0; i < commands_count; i++) {\n        clusterManagerCommandDef cmddef = clusterManagerCommands[i];\n        if (!strcmp(cmddef.name, cmdname)) {\n            if ((cmddef.arity > 0 && argc != cmddef.arity) ||\n                (cmddef.arity < 0 && argc < (cmddef.arity * -1))) {\n                fprintf(stderr, \"[ERR] Wrong number of arguments for \"\n                                \"specified --cluster sub command\\n\");\n                return NULL;\n            }\n            proc = cmddef.proc;\n        }\n    }\n    if (!proc) fprintf(stderr, \"Unknown --cluster subcommand\\n\");\n    return proc;\n}\n\nstatic int parseClusterNodeAddress(char *addr, char **ip_ptr, int *port_ptr,\n                                   int *bus_port_ptr)\n{\n    char *c = strrchr(addr, '@');\n    if (c != NULL) {\n        *c = '\\0';\n        if (bus_port_ptr != NULL)\n            *bus_port_ptr = atoi(c + 1);\n    }\n    c = strrchr(addr, ':');\n    if (c != NULL) {\n        *c = '\\0';\n        *ip_ptr = addr;\n        *port_ptr = atoi(++c);\n    } else return 0;\n    return 1;\n}\n\n/* Get host ip and port from command arguments. If only one argument has\n * been provided it must be in the form of 'ip:port', elsewhere\n * the first argument must be the ip and the second one the port.\n * If host and port can be detected, it returns 1 and it stores host and\n * port into variables referenced by'ip_ptr' and 'port_ptr' pointers,\n * elsewhere it returns 0. */\nstatic int getClusterHostFromCmdArgs(int argc, char **argv,\n                                     char **ip_ptr, int *port_ptr) {\n    int port = 0;\n    char *ip = NULL;\n    if (argc == 1) {\n        char *addr = argv[0];\n        if (!parseClusterNodeAddress(addr, &ip, &port, NULL)) return 0;\n    } else {\n        ip = argv[0];\n        port = atoi(argv[1]);\n    }\n    if (!ip || !port) return 0;\n    else {\n        *ip_ptr = ip;\n        *port_ptr = port;\n    }\n    return 1;\n}\n\nstatic void freeClusterManagerNodeFlags(list *flags) {\n    listIter li;\n    listNode *ln;\n    listRewind(flags, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        sds flag = ln->value;\n        sdsfree(flag);\n    }\n    listRelease(flags);\n}\n\nstatic void freeClusterManagerNode(clusterManagerNode *node) {\n    if (node->context != NULL) redisFree(node->context);\n    if (node->friends != NULL) {\n        listIter li;\n        listNode *ln;\n        listRewind(node->friends,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *fn = ln->value;\n            freeClusterManagerNode(fn);\n        }\n        listRelease(node->friends);\n        node->friends = NULL;\n    }\n    if (node->name != NULL) sdsfree(node->name);\n    if (node->replicate != NULL) sdsfree(node->replicate);\n    if ((node->flags & CLUSTER_MANAGER_FLAG_FRIEND) && node->ip)\n        sdsfree(node->ip);\n    int i;\n    if (node->migrating != NULL) {\n        for (i = 0; i < node->migrating_count; i++) sdsfree(node->migrating[i]);\n        zfree(node->migrating);\n    }\n    if (node->importing != NULL) {\n        for (i = 0; i < node->importing_count; i++) sdsfree(node->importing[i]);\n        zfree(node->importing);\n    }\n    if (node->flags_str != NULL) {\n        freeClusterManagerNodeFlags(node->flags_str);\n        node->flags_str = NULL;\n    }\n    zfree(node);\n}\n\nstatic void freeClusterManager(void) {\n    listIter li;\n    listNode *ln;\n    if (cluster_manager.nodes != NULL) {\n        listRewind(cluster_manager.nodes,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            freeClusterManagerNode(n);\n        }\n        listRelease(cluster_manager.nodes);\n        cluster_manager.nodes = NULL;\n    }\n    if (cluster_manager.errors != NULL) {\n        listRewind(cluster_manager.errors,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            sds err = ln->value;\n            sdsfree(err);\n        }\n        listRelease(cluster_manager.errors);\n        cluster_manager.errors = NULL;\n    }\n    if (clusterManagerUncoveredSlots != NULL)\n        dictRelease(clusterManagerUncoveredSlots);\n}\n\nstatic clusterManagerNode *clusterManagerNewNode(char *ip, int port) {\n    clusterManagerNode *node = zmalloc(sizeof(*node));\n    node->context = NULL;\n    node->name = NULL;\n    node->ip = ip;\n    node->port = port;\n    node->current_epoch = 0;\n    node->ping_sent = 0;\n    node->ping_recv = 0;\n    node->flags = 0;\n    node->flags_str = NULL;\n    node->replicate = NULL;\n    node->dirty = 0;\n    node->friends = NULL;\n    node->migrating = NULL;\n    node->importing = NULL;\n    node->migrating_count = 0;\n    node->importing_count = 0;\n    node->replicas_count = 0;\n    node->weight = 1.0f;\n    node->balance = 0;\n    clusterManagerNodeResetSlots(node);\n    return node;\n}\n\nstatic sds clusterManagerGetNodeRDBFilename(clusterManagerNode *node) {\n    assert(config.cluster_manager_command.backup_dir);\n    sds filename = sdsnew(config.cluster_manager_command.backup_dir);\n    if (filename[sdslen(filename) - 1] != '/')\n        filename = sdscat(filename, \"/\");\n    filename = sdscatprintf(filename, \"redis-node-%s-%d-%s.rdb\", node->ip,\n                            node->port, node->name);\n    return filename;\n}\n\n/* Check whether reply is NULL or its type is REDIS_REPLY_ERROR. In the\n * latest case, if the 'err' arg is not NULL, it gets allocated with a copy\n * of reply error (it's up to the caller function to free it), elsewhere\n * the error is directly printed. */\nstatic int clusterManagerCheckRedisReply(clusterManagerNode *n,\n                                         redisReply *r, char **err)\n{\n    int is_err = 0;\n    if (!r || (is_err = (r->type == REDIS_REPLY_ERROR))) {\n        if (is_err) {\n            if (err != NULL) {\n                *err = zmalloc((r->len + 1) * sizeof(char));\n                strcpy(*err, r->str);\n            } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, r->str);\n        }\n        return 0;\n    }\n    return 1;\n}\n\n/* Call MULTI command on a cluster node. */\nstatic int clusterManagerStartTransaction(clusterManagerNode *node) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"MULTI\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Call EXEC command on a cluster node. */\nstatic int clusterManagerExecTransaction(clusterManagerNode *node,\n                                         clusterManagerOnReplyError onerror)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"EXEC\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (success) {\n        if (reply->type != REDIS_REPLY_ARRAY) {\n            success = 0;\n            goto cleanup;\n        }\n        size_t i;\n        for (i = 0; i < reply->elements; i++) {\n            redisReply *r = reply->element[i];\n            char *err = NULL;\n            success = clusterManagerCheckRedisReply(node, r, &err);\n            if (!success && onerror) success = onerror(r, node, i);\n            if (err) {\n                if (!success)\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                zfree(err);\n            }\n            if (!success) break;\n        }\n    }\ncleanup:\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerNodeConnect(clusterManagerNode *node) {\n    if (node->context) redisFree(node->context);\n    node->context = redisConnect(node->ip, node->port);\n    if (!node->context->err && config.tls) {\n        const char *err = NULL;\n        if (cliSecureConnection(node->context, &err) == REDIS_ERR && err) {\n            fprintf(stderr,\"TLS Error: %s\\n\", err);\n            redisFree(node->context);\n            node->context = NULL;\n            return 0;\n        }\n    }\n    if (node->context->err) {\n        fprintf(stderr,\"Could not connect to Redis at \");\n        fprintf(stderr,\"%s:%d: %s\\n\", node->ip, node->port,\n                node->context->errstr);\n        redisFree(node->context);\n        node->context = NULL;\n        return 0;\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, node->context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);\n    if (config.auth) {\n        redisReply *reply;\n        if (config.user == NULL)\n            reply = redisCommand(node->context,\"AUTH %s\", config.auth);\n        else\n            reply = redisCommand(node->context,\"AUTH %s %s\",\n                                 config.user,config.auth);\n        int ok = clusterManagerCheckRedisReply(node, reply, NULL);\n        if (reply != NULL) freeReplyObject(reply);\n        if (!ok) return 0;\n    }\n    return 1;\n}\n\nstatic void clusterManagerRemoveNodeFromList(list *nodelist,\n                                             clusterManagerNode *node) {\n    listIter li;\n    listNode *ln;\n    listRewind(nodelist, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        if (node == ln->value) {\n            listDelNode(nodelist, ln);\n            break;\n        }\n    }\n}\n\n/* Return the node with the specified name (ID) or NULL. */\nstatic clusterManagerNode *clusterManagerNodeByName(const char *name) {\n    if (cluster_manager.nodes == NULL) return NULL;\n    clusterManagerNode *found = NULL;\n    sds lcname = sdsempty();\n    lcname = sdscpy(lcname, name);\n    sdstolower(lcname);\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->name && !sdscmp(n->name, lcname)) {\n            found = n;\n            break;\n        }\n    }\n    sdsfree(lcname);\n    return found;\n}\n\n/* Like clusterManagerNodeByName but the specified name can be just the first\n * part of the node ID as long as the prefix in unique across the\n * cluster.\n */\nstatic clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char*name)\n{\n    if (cluster_manager.nodes == NULL) return NULL;\n    clusterManagerNode *found = NULL;\n    sds lcname = sdsempty();\n    lcname = sdscpy(lcname, name);\n    sdstolower(lcname);\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->name &&\n            strstr(n->name, lcname) == n->name) {\n            found = n;\n            break;\n        }\n    }\n    sdsfree(lcname);\n    return found;\n}\n\nstatic void clusterManagerNodeResetSlots(clusterManagerNode *node) {\n    memset(node->slots, 0, sizeof(node->slots));\n    node->slots_count = 0;\n}\n\n/* Call \"INFO\" redis command on the specified node and return the reply. */\nstatic redisReply *clusterManagerGetNodeRedisInfo(clusterManagerNode *node,\n                                                  char **err)\n{\n    redisReply *info = CLUSTER_MANAGER_COMMAND(node, \"INFO\");\n    if (err != NULL) *err = NULL;\n    if (info == NULL) return NULL;\n    if (info->type == REDIS_REPLY_ERROR) {\n        if (err != NULL) {\n            *err = zmalloc((info->len + 1) * sizeof(char));\n            strcpy(*err, info->str);\n        }\n        freeReplyObject(info);\n        return  NULL;\n    }\n    return info;\n}\n\nstatic int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err) {\n    redisReply *info = clusterManagerGetNodeRedisInfo(node, err);\n    if (info == NULL) return 0;\n    int is_cluster = (int) getLongInfoField(info->str, \"cluster_enabled\");\n    freeReplyObject(info);\n    return is_cluster;\n}\n\n/* Checks whether the node is empty. Node is considered not-empty if it has\n * some key or if it already knows other nodes */\nstatic int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err) {\n    redisReply *info = clusterManagerGetNodeRedisInfo(node, err);\n    int is_empty = 1;\n    if (info == NULL) return 0;\n    if (strstr(info->str, \"db0:\") != NULL) {\n        is_empty = 0;\n        goto result;\n    }\n    freeReplyObject(info);\n    info = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER INFO\");\n    if (err != NULL) *err = NULL;\n    if (!clusterManagerCheckRedisReply(node, info, err)) {\n        is_empty = 0;\n        goto result;\n    }\n    long known_nodes = getLongInfoField(info->str, \"cluster_known_nodes\");\n    is_empty = (known_nodes == 1);\nresult:\n    freeReplyObject(info);\n    return is_empty;\n}\n\n/* Return the anti-affinity score, which is a measure of the amount of\n * violations of anti-affinity in the current cluster layout, that is, how\n * badly the masters and slaves are distributed in the different IP\n * addresses so that slaves of the same master are not in the master\n * host and are also in different hosts.\n *\n * The score is calculated as follows:\n *\n * SAME_AS_MASTER = 10000 * each slave in the same IP of its master.\n * SAME_AS_SLAVE  = 1 * each slave having the same IP as another slave\n                      of the same master.\n * FINAL_SCORE = SAME_AS_MASTER + SAME_AS_SLAVE\n *\n * So a greater score means a worse anti-affinity level, while zero\n * means perfect anti-affinity.\n *\n * The anti affinity optimizator will try to get a score as low as\n * possible. Since we do not want to sacrifice the fact that slaves should\n * not be in the same host as the master, we assign 10000 times the score\n * to this violation, so that we'll optimize for the second factor only\n * if it does not impact the first one.\n *\n * The ipnodes argument is an array of clusterManagerNodeArray, one for\n * each IP, while ip_count is the total number of IPs in the configuration.\n *\n * The function returns the above score, and the list of\n * offending slaves can be stored into the 'offending' argument,\n * so that the optimizer can try changing the configuration of the\n * slaves violating the anti-affinity goals. */\nstatic int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,\n    int ip_count, clusterManagerNode ***offending, int *offending_len)\n{\n    int score = 0, i, j;\n    int node_len = cluster_manager.nodes->len;\n    clusterManagerNode **offending_p = NULL;\n    if (offending != NULL) {\n        *offending = zcalloc(node_len * sizeof(clusterManagerNode*));\n        offending_p = *offending;\n    }\n    /* For each set of nodes in the same host, split by\n     * related nodes (masters and slaves which are involved in\n     * replication of each other) */\n    for (i = 0; i < ip_count; i++) {\n        clusterManagerNodeArray *node_array = &(ipnodes[i]);\n        dict *related = dictCreate(&clusterManagerDictType, NULL);\n        char *ip = NULL;\n        for (j = 0; j < node_array->len; j++) {\n            clusterManagerNode *node = node_array->nodes[j];\n            if (node == NULL) continue;\n            if (!ip) ip = node->ip;\n            sds types;\n            /* We always use the Master ID as key. */\n            sds key = (!node->replicate ? node->name : node->replicate);\n            assert(key != NULL);\n            dictEntry *entry = dictFind(related, key);\n            if (entry) types = sdsdup((sds) dictGetVal(entry));\n            else types = sdsempty();\n            /* Master type 'm' is always set as the first character of the\n             * types string. */\n            if (!node->replicate) types = sdscatprintf(types, \"m%s\", types);\n            else types = sdscat(types, \"s\");\n            dictReplace(related, key, types);\n        }\n        /* Now it's trivial to check, for each related group having the\n         * same host, what is their local score. */\n        dictIterator *iter = dictGetIterator(related);\n        dictEntry *entry;\n        while ((entry = dictNext(iter)) != NULL) {\n            sds types = (sds) dictGetVal(entry);\n            sds name = (sds) dictGetKey(entry);\n            int typeslen = sdslen(types);\n            if (typeslen < 2) continue;\n            if (types[0] == 'm') score += (10000 * (typeslen - 1));\n            else score += (1 * typeslen);\n            if (offending == NULL) continue;\n            /* Populate the list of offending nodes. */\n            listIter li;\n            listNode *ln;\n            listRewind(cluster_manager.nodes, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (n->replicate == NULL) continue;\n                if (!strcmp(n->replicate, name) && !strcmp(n->ip, ip)) {\n                    *(offending_p++) = n;\n                    if (offending_len != NULL) (*offending_len)++;\n                    break;\n                }\n            }\n        }\n        //if (offending_len != NULL) *offending_len = offending_p - *offending;\n        dictReleaseIterator(iter);\n        dictRelease(related);\n    }\n    return score;\n}\n\nstatic void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,\n    int ip_count)\n{\n    clusterManagerNode **offenders = NULL;\n    int score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count,\n                                                   NULL, NULL);\n    if (score == 0) goto cleanup;\n    clusterManagerLogInfo(\">>> Trying to optimize slaves allocation \"\n                          \"for anti-affinity\\n\");\n    int node_len = cluster_manager.nodes->len;\n    int maxiter = 500 * node_len; // Effort is proportional to cluster size...\n    srand(time(NULL));\n    while (maxiter > 0) {\n        int offending_len = 0;\n        if (offenders != NULL) {\n            zfree(offenders);\n            offenders = NULL;\n        }\n        score = clusterManagerGetAntiAffinityScore(ipnodes,\n                                                   ip_count,\n                                                   &offenders,\n                                                   &offending_len);\n        if (score == 0) break; // Optimal anti affinity reached\n        /* We'll try to randomly swap a slave's assigned master causing\n         * an affinity problem with another random slave, to see if we\n         * can improve the affinity. */\n        int rand_idx = rand() % offending_len;\n        clusterManagerNode *first = offenders[rand_idx],\n                           *second = NULL;\n        clusterManagerNode **other_replicas = zcalloc((node_len - 1) *\n                                                      sizeof(*other_replicas));\n        int other_replicas_count = 0;\n        listIter li;\n        listNode *ln;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n != first && n->replicate != NULL)\n                other_replicas[other_replicas_count++] = n;\n        }\n        if (other_replicas_count == 0) {\n            zfree(other_replicas);\n            break;\n        }\n        rand_idx = rand() % other_replicas_count;\n        second = other_replicas[rand_idx];\n        char *first_master = first->replicate,\n             *second_master = second->replicate;\n        first->replicate = second_master, first->dirty = 1;\n        second->replicate = first_master, second->dirty = 1;\n        int new_score = clusterManagerGetAntiAffinityScore(ipnodes,\n                                                           ip_count,\n                                                           NULL, NULL);\n        /* If the change actually makes thing worse, revert. Otherwise\n         * leave as it is because the best solution may need a few\n         * combined swaps. */\n        if (new_score > score) {\n            first->replicate = first_master;\n            second->replicate = second_master;\n        }\n        zfree(other_replicas);\n        maxiter--;\n    }\n    score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL, NULL);\n    char *msg;\n    int perfect = (score == 0);\n    int log_level = (perfect ? CLUSTER_MANAGER_LOG_LVL_SUCCESS :\n                               CLUSTER_MANAGER_LOG_LVL_WARN);\n    if (perfect) msg = \"[OK] Perfect anti-affinity obtained!\";\n    else if (score >= 10000)\n        msg = (\"[WARNING] Some slaves are in the same host as their master\");\n    else\n        msg=(\"[WARNING] Some slaves of the same master are in the same host\");\n    clusterManagerLog(log_level, \"%s\\n\", msg);\ncleanup:\n    zfree(offenders);\n}\n\n/* Return a representable string of the node's flags */\nstatic sds clusterManagerNodeFlagString(clusterManagerNode *node) {\n    sds flags = sdsempty();\n    if (!node->flags_str) return flags;\n    int empty = 1;\n    listIter li;\n    listNode *ln;\n    listRewind(node->flags_str, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        sds flag = ln->value;\n        if (strcmp(flag, \"myself\") == 0) continue;\n        if (!empty) flags = sdscat(flags, \",\");\n        flags = sdscatfmt(flags, \"%S\", flag);\n        empty = 0;\n    }\n    return flags;\n}\n\n/* Return a representable string of the node's slots */\nstatic sds clusterManagerNodeSlotsString(clusterManagerNode *node) {\n    sds slots = sdsempty();\n    int first_range_idx = -1, last_slot_idx = -1, i;\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        int has_slot = node->slots[i];\n        if (has_slot) {\n            if (first_range_idx == -1) {\n                if (sdslen(slots)) slots = sdscat(slots, \",\");\n                first_range_idx = i;\n                slots = sdscatfmt(slots, \"[%u\", i);\n            }\n            last_slot_idx = i;\n        } else {\n            if (last_slot_idx >= 0) {\n                if (first_range_idx == last_slot_idx)\n                    slots = sdscat(slots, \"]\");\n                else slots = sdscatfmt(slots, \"-%u]\", last_slot_idx);\n            }\n            last_slot_idx = -1;\n            first_range_idx = -1;\n        }\n    }\n    if (last_slot_idx >= 0) {\n        if (first_range_idx == last_slot_idx) slots = sdscat(slots, \"]\");\n        else slots = sdscatfmt(slots, \"-%u]\", last_slot_idx);\n    }\n    return slots;\n}\n\nstatic sds clusterManagerNodeGetJSON(clusterManagerNode *node,\n                                     unsigned long error_count)\n{\n    sds json = sdsempty();\n    sds replicate = sdsempty();\n    if (node->replicate)\n        replicate = sdscatprintf(replicate, \"\\\"%s\\\"\", node->replicate);\n    else\n        replicate = sdscat(replicate, \"null\");\n    sds slots = clusterManagerNodeSlotsString(node);\n    sds flags = clusterManagerNodeFlagString(node);\n    char *p = slots;\n    while ((p = strchr(p, '-')) != NULL)\n        *(p++) = ',';\n    json = sdscatprintf(json,\n        \"  {\\n\"\n        \"    \\\"name\\\": \\\"%s\\\",\\n\"\n        \"    \\\"host\\\": \\\"%s\\\",\\n\"\n        \"    \\\"port\\\": %d,\\n\"\n        \"    \\\"replicate\\\": %s,\\n\"\n        \"    \\\"slots\\\": [%s],\\n\"\n        \"    \\\"slots_count\\\": %d,\\n\"\n        \"    \\\"flags\\\": \\\"%s\\\",\\n\"\n        \"    \\\"current_epoch\\\": %llu\",\n        node->name,\n        node->ip,\n        node->port,\n        replicate,\n        slots,\n        node->slots_count,\n        flags,\n        (unsigned long long)node->current_epoch\n    );\n    if (error_count > 0) {\n        json = sdscatprintf(json, \",\\n    \\\"cluster_errors\\\": %lu\",\n                            error_count);\n    }\n    if (node->migrating_count > 0 && node->migrating != NULL) {\n        int i = 0;\n        sds migrating = sdsempty();\n        for (; i < node->migrating_count; i += 2) {\n            sds slot = node->migrating[i];\n            sds dest = node->migrating[i + 1];\n            if (slot && dest) {\n                if (sdslen(migrating) > 0) migrating = sdscat(migrating, \",\");\n                migrating = sdscatfmt(migrating, \"\\\"%S\\\": \\\"%S\\\"\", slot, dest);\n            }\n        }\n        if (sdslen(migrating) > 0)\n            json = sdscatfmt(json, \",\\n    \\\"migrating\\\": {%S}\", migrating);\n        sdsfree(migrating);\n    }\n    if (node->importing_count > 0 && node->importing != NULL) {\n        int i = 0;\n        sds importing = sdsempty();\n        for (; i < node->importing_count; i += 2) {\n            sds slot = node->importing[i];\n            sds from = node->importing[i + 1];\n            if (slot && from) {\n                if (sdslen(importing) > 0) importing = sdscat(importing, \",\");\n                importing = sdscatfmt(importing, \"\\\"%S\\\": \\\"%S\\\"\", slot, from);\n            }\n        }\n        if (sdslen(importing) > 0)\n            json = sdscatfmt(json, \",\\n    \\\"importing\\\": {%S}\", importing);\n        sdsfree(importing);\n    }\n    json = sdscat(json, \"\\n  }\");\n    sdsfree(replicate);\n    sdsfree(slots);\n    sdsfree(flags);\n    return json;\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). */\nstatic unsigned int clusterManagerKeyHashSlot(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 between {} ? 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/* Return a string representation of the cluster node. */\nstatic sds clusterManagerNodeInfo(clusterManagerNode *node, int indent) {\n    sds info = sdsempty();\n    sds spaces = sdsempty();\n    int i;\n    for (i = 0; i < indent; i++) spaces = sdscat(spaces, \" \");\n    if (indent) info = sdscat(info, spaces);\n    int is_master = !(node->flags & CLUSTER_MANAGER_FLAG_SLAVE);\n    char *role = (is_master ? \"M\" : \"S\");\n    sds slots = NULL;\n    if (node->dirty && node->replicate != NULL)\n        info = sdscatfmt(info, \"S: %S %s:%u\", node->name, node->ip, node->port);\n    else {\n        slots = clusterManagerNodeSlotsString(node);\n        sds flags = clusterManagerNodeFlagString(node);\n        info = sdscatfmt(info, \"%s: %S %s:%u\\n\"\n                               \"%s   slots:%S (%u slots) \"\n                               \"%S\",\n                               role, node->name, node->ip, node->port, spaces,\n                               slots, node->slots_count, flags);\n        sdsfree(slots);\n        sdsfree(flags);\n    }\n    if (node->replicate != NULL)\n        info = sdscatfmt(info, \"\\n%s   replicates %S\", spaces, node->replicate);\n    else if (node->replicas_count)\n        info = sdscatfmt(info, \"\\n%s   %U additional replica(s)\",\n                         spaces, node->replicas_count);\n    sdsfree(spaces);\n    return info;\n}\n\nstatic void clusterManagerShowNodes(void) {\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        sds info = clusterManagerNodeInfo(node, 0);\n        printf(\"%s\\n\", (char *) info);\n        sdsfree(info);\n    }\n}\n\nstatic void clusterManagerShowClusterInfo(void) {\n    int masters = 0;\n    int keys = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        if (!(node->flags & CLUSTER_MANAGER_FLAG_SLAVE)) {\n            if (!node->name) continue;\n            int replicas = 0;\n            int dbsize = -1;\n            char name[9];\n            memcpy(name, node->name, 8);\n            name[8] = '\\0';\n            listIter ri;\n            listNode *rn;\n            listRewind(cluster_manager.nodes, &ri);\n            while ((rn = listNext(&ri)) != NULL) {\n                clusterManagerNode *n = rn->value;\n                if (n == node || !(n->flags & CLUSTER_MANAGER_FLAG_SLAVE))\n                    continue;\n                if (n->replicate && !strcmp(n->replicate, node->name))\n                    replicas++;\n            }\n            redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"DBSIZE\");\n            if (reply != NULL && reply->type == REDIS_REPLY_INTEGER)\n                dbsize = reply->integer;\n            if (dbsize < 0) {\n                char *err = \"\";\n                if (reply != NULL && reply->type == REDIS_REPLY_ERROR)\n                    err = reply->str;\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                if (reply != NULL) freeReplyObject(reply);\n                return;\n            };\n            if (reply != NULL) freeReplyObject(reply);\n            printf(\"%s:%d (%s...) -> %d keys | %d slots | %d slaves.\\n\",\n                   node->ip, node->port, name, dbsize,\n                   node->slots_count, replicas);\n            masters++;\n            keys += dbsize;\n        }\n    }\n    clusterManagerLogOk(\"[OK] %d keys in %d masters.\\n\", keys, masters);\n    float keys_per_slot = keys / (float) CLUSTER_MANAGER_SLOTS;\n    printf(\"%.2f keys per slot on average.\\n\", keys_per_slot);\n}\n\n/* Flush dirty slots configuration of the node by calling CLUSTER ADDSLOTS */\nstatic int clusterManagerAddSlots(clusterManagerNode *node, char**err)\n{\n    redisReply *reply = NULL;\n    void *_reply = NULL;\n    int success = 1;\n    /* First two args are used for the command itself. */\n    int argc = node->slots_count + 2;\n    sds *argv = zmalloc(argc * sizeof(*argv));\n    size_t *argvlen = zmalloc(argc * sizeof(*argvlen));\n    argv[0] = \"CLUSTER\";\n    argv[1] = \"ADDSLOTS\";\n    argvlen[0] = 7;\n    argvlen[1] = 8;\n    *err = NULL;\n    int i, argv_idx = 2;\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        if (argv_idx >= argc) break;\n        if (node->slots[i]) {\n            argv[argv_idx] = sdsfromlonglong((long long) i);\n            argvlen[argv_idx] = sdslen(argv[argv_idx]);\n            argv_idx++;\n        }\n    }\n    if (!argv_idx) {\n        success = 0;\n        goto cleanup;\n    }\n    redisAppendCommandArgv(node->context,argc,(const char**)argv,argvlen);\n    if (redisGetReply(node->context, &_reply) != REDIS_OK) {\n        success = 0;\n        goto cleanup;\n    }\n    reply = (redisReply*) _reply;\n    success = clusterManagerCheckRedisReply(node, reply, err);\ncleanup:\n    zfree(argvlen);\n    if (argv != NULL) {\n        for (i = 2; i < argc; i++) sdsfree(argv[i]);\n        zfree(argv);\n    }\n    if (reply != NULL) freeReplyObject(reply);\n    return success;\n}\n\n/* Get the node the slot is assigned to from the point of view of node *n.\n * If the slot is unassigned or if the reply is an error, return NULL.\n * Use the **err argument in order to check wether the slot is unassigned\n * or the reply resulted in an error. */\nstatic clusterManagerNode *clusterManagerGetSlotOwner(clusterManagerNode *n,\n                                                      int slot, char **err)\n{\n    assert(slot >= 0 && slot < CLUSTER_MANAGER_SLOTS);\n    clusterManagerNode *owner = NULL;\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER SLOTS\");\n    if (clusterManagerCheckRedisReply(n, reply, err)) {\n        assert(reply->type == REDIS_REPLY_ARRAY);\n        size_t i;\n        for (i = 0; i < reply->elements; i++) {\n            redisReply *r = reply->element[i];\n            assert(r->type == REDIS_REPLY_ARRAY && r->elements >= 3);\n            int from, to;\n            from = r->element[0]->integer;\n            to = r->element[1]->integer;\n            if (slot < from || slot > to) continue;\n            redisReply *nr =  r->element[2];\n            assert(nr->type == REDIS_REPLY_ARRAY && nr->elements >= 2);\n            char *name = NULL;\n            if (nr->elements >= 3)\n                name =  nr->element[2]->str;\n            if (name != NULL)\n                owner = clusterManagerNodeByName(name);\n            else {\n                char *ip = nr->element[0]->str;\n                assert(ip != NULL);\n                int port = (int) nr->element[1]->integer;\n                listIter li;\n                listNode *ln;\n                listRewind(cluster_manager.nodes, &li);\n                while ((ln = listNext(&li)) != NULL) {\n                    clusterManagerNode *nd = ln->value;\n                    if (strcmp(nd->ip, ip) == 0 && port == nd->port) {\n                        owner = nd;\n                        break;\n                    }\n                }\n            }\n            if (owner) break;\n        }\n    }\n    if (reply) freeReplyObject(reply);\n    return owner;\n}\n\n/* Set slot status to \"importing\" or \"migrating\" */\nstatic int clusterManagerSetSlot(clusterManagerNode *node1,\n                                 clusterManagerNode *node2,\n                                 int slot, const char *status, char **err) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node1, \"CLUSTER \"\n                                                \"SETSLOT %d %s %s\",\n                                                slot, status,\n                                                (char *) node2->name);\n    if (err != NULL) *err = NULL;\n    if (!reply) return 0;\n    int success = 1;\n    if (reply->type == REDIS_REPLY_ERROR) {\n        success = 0;\n        if (err != NULL) {\n            *err = zmalloc((reply->len + 1) * sizeof(char));\n            strcpy(*err, reply->str);\n        } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(node1, reply->str);\n        goto cleanup;\n    }\ncleanup:\n    freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerClearSlotStatus(clusterManagerNode *node, int slot) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER SETSLOT %d %s\", slot, \"STABLE\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerDelSlot(clusterManagerNode *node, int slot,\n                                 int ignore_unassigned_err)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER DELSLOTS %d\", slot);\n    char *err = NULL;\n    int success = clusterManagerCheckRedisReply(node, reply, &err);\n    if (!success && reply && reply->type == REDIS_REPLY_ERROR &&\n        ignore_unassigned_err)\n    {\n        char *get_owner_err = NULL;\n        clusterManagerNode *assigned_to =\n            clusterManagerGetSlotOwner(node, slot, &get_owner_err);\n        if (!assigned_to) {\n            if (get_owner_err == NULL) success = 1;\n            else {\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, get_owner_err);\n                zfree(get_owner_err);\n            }\n        }\n    }\n    if (!success && err != NULL) {\n        CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n        zfree(err);\n    }\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerAddSlot(clusterManagerNode *node, int slot) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER ADDSLOTS %d\", slot);\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic signed int clusterManagerCountKeysInSlot(clusterManagerNode *node,\n                                                int slot)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n    int count = -1;\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (success && reply->type == REDIS_REPLY_INTEGER) count = reply->integer;\n    if (reply) freeReplyObject(reply);\n    return count;\n}\n\nstatic int clusterManagerBumpEpoch(clusterManagerNode *node) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER BUMPEPOCH\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Callback used by clusterManagerSetSlotOwner transaction. It should ignore\n * errors except for ADDSLOTS errors.\n * Return 1 if the error should be ignored. */\nstatic int clusterManagerOnSetOwnerErr(redisReply *reply,\n    clusterManagerNode *n, int bulk_idx)\n{\n    UNUSED(reply);\n    UNUSED(n);\n    /* Only raise error when ADDSLOTS fail (bulk_idx == 1). */\n    return (bulk_idx != 1);\n}\n\nstatic int clusterManagerSetSlotOwner(clusterManagerNode *owner,\n                                      int slot,\n                                      int do_clear)\n{\n    int success = clusterManagerStartTransaction(owner);\n    if (!success) return 0;\n    /* Ensure the slot is not already assigned. */\n    clusterManagerDelSlot(owner, slot, 1);\n    /* Add the slot and bump epoch. */\n    clusterManagerAddSlot(owner, slot);\n    if (do_clear) clusterManagerClearSlotStatus(owner, slot);\n    clusterManagerBumpEpoch(owner);\n    success = clusterManagerExecTransaction(owner, clusterManagerOnSetOwnerErr);\n    return success;\n}\n\n/* Get the hash for the values of the specified keys in *keys_reply for the\n * specified nodes *n1 and *n2, by calling DEBUG DIGEST-VALUE redis command\n * on both nodes. Every key with same name on both nodes but having different\n * values will be added to the *diffs list. Return 0 in case of reply\n * error. */\nstatic int clusterManagerCompareKeysValues(clusterManagerNode *n1,\n                                          clusterManagerNode *n2,\n                                          redisReply *keys_reply,\n                                          list *diffs)\n{\n    size_t i, argc = keys_reply->elements + 2;\n    static const char *hash_zero = \"0000000000000000000000000000000000000000\";\n    char **argv = zcalloc(argc * sizeof(char *));\n    size_t  *argv_len = zcalloc(argc * sizeof(size_t));\n    argv[0] = \"DEBUG\";\n    argv_len[0] = 5;\n    argv[1] = \"DIGEST-VALUE\";\n    argv_len[1] = 12;\n    for (i = 0; i < keys_reply->elements; i++) {\n        redisReply *entry = keys_reply->element[i];\n        int idx = i + 2;\n        argv[idx] = entry->str;\n        argv_len[idx] = entry->len;\n    }\n    int success = 0;\n    void *_reply1 = NULL, *_reply2 = NULL;\n    redisReply *r1 = NULL, *r2 = NULL;\n    redisAppendCommandArgv(n1->context,argc, (const char**)argv,argv_len);\n    success = (redisGetReply(n1->context, &_reply1) == REDIS_OK);\n    if (!success) goto cleanup;\n    r1 = (redisReply *) _reply1;\n    redisAppendCommandArgv(n2->context,argc, (const char**)argv,argv_len);\n    success = (redisGetReply(n2->context, &_reply2) == REDIS_OK);\n    if (!success) goto cleanup;\n    r2 = (redisReply *) _reply2;\n    success = (r1->type != REDIS_REPLY_ERROR && r2->type != REDIS_REPLY_ERROR);\n    if (r1->type == REDIS_REPLY_ERROR) {\n        CLUSTER_MANAGER_PRINT_REPLY_ERROR(n1, r1->str);\n        success = 0;\n    }\n    if (r2->type == REDIS_REPLY_ERROR) {\n        CLUSTER_MANAGER_PRINT_REPLY_ERROR(n2, r2->str);\n        success = 0;\n    }\n    if (!success) goto cleanup;\n    assert(keys_reply->elements == r1->elements &&\n           keys_reply->elements == r2->elements);\n    for (i = 0; i < keys_reply->elements; i++) {\n        char *key = keys_reply->element[i]->str;\n        char *hash1 = r1->element[i]->str;\n        char *hash2 = r2->element[i]->str;\n        /* Ignore keys that don't exist in both nodes. */\n        if (strcmp(hash1, hash_zero) == 0 || strcmp(hash2, hash_zero) == 0)\n            continue;\n        if (strcmp(hash1, hash2) != 0) listAddNodeTail(diffs, key);\n    }\ncleanup:\n    if (r1) freeReplyObject(r1);\n    if (r2) freeReplyObject(r2);\n    zfree(argv);\n    zfree(argv_len);\n    return success;\n}\n\n/* Migrate keys taken from reply->elements. It returns the reply from the\n * MIGRATE command, or NULL if something goes wrong. If the argument 'dots'\n * is not NULL, a dot will be printed for every migrated key. */\nstatic redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,\n                                                    clusterManagerNode *target,\n                                                    redisReply *reply,\n                                                    int replace, int timeout,\n                                                    char *dots)\n{\n    redisReply *migrate_reply = NULL;\n    char **argv = NULL;\n    size_t *argv_len = NULL;\n    int c = (replace ? 8 : 7);\n    if (config.auth) c += 2;\n    size_t argc = c + reply->elements;\n    size_t i, offset = 6; // Keys Offset\n    argv = zcalloc(argc * sizeof(char *));\n    argv_len = zcalloc(argc * sizeof(size_t));\n    char portstr[255];\n    char timeoutstr[255];\n    snprintf(portstr, 10, \"%d\", target->port);\n    snprintf(timeoutstr, 10, \"%d\", timeout);\n    argv[0] = \"MIGRATE\";\n    argv_len[0] = 7;\n    argv[1] = target->ip;\n    argv_len[1] = strlen(target->ip);\n    argv[2] = portstr;\n    argv_len[2] = strlen(portstr);\n    argv[3] = \"\";\n    argv_len[3] = 0;\n    argv[4] = \"0\";\n    argv_len[4] = 1;\n    argv[5] = timeoutstr;\n    argv_len[5] = strlen(timeoutstr);\n    if (replace) {\n        argv[offset] = \"REPLACE\";\n        argv_len[offset] = 7;\n        offset++;\n    }\n    if (config.auth) {\n        argv[offset] = \"AUTH\";\n        argv_len[offset] = 4;\n        offset++;\n        argv[offset] = config.auth;\n        argv_len[offset] = strlen(config.auth);\n        offset++;\n    }\n    argv[offset] = \"KEYS\";\n    argv_len[offset] = 4;\n    offset++;\n    for (i = 0; i < reply->elements; i++) {\n        redisReply *entry = reply->element[i];\n        size_t idx = i + offset;\n        assert(entry->type == REDIS_REPLY_STRING);\n        argv[idx] = (char *) sdsnewlen(entry->str, entry->len);\n        argv_len[idx] = entry->len;\n        if (dots) dots[i] = '.';\n    }\n    if (dots) dots[reply->elements] = '\\0';\n    void *_reply = NULL;\n    redisAppendCommandArgv(source->context,argc,\n                           (const char**)argv,argv_len);\n    int success = (redisGetReply(source->context, &_reply) == REDIS_OK);\n    for (i = 0; i < reply->elements; i++) sdsfree(argv[i + offset]);\n    if (!success) goto cleanup;\n    migrate_reply = (redisReply *) _reply;\ncleanup:\n    zfree(argv);\n    zfree(argv_len);\n    return migrate_reply;\n}\n\n/* Migrate all keys in the given slot from source to target.*/\nstatic int clusterManagerMigrateKeysInSlot(clusterManagerNode *source,\n                                           clusterManagerNode *target,\n                                           int slot, int timeout,\n                                           int pipeline, int verbose,\n                                           char **err)\n{\n    int success = 1;\n    int do_fix = config.cluster_manager_command.flags &\n                 CLUSTER_MANAGER_CMD_FLAG_FIX;\n    int do_replace = config.cluster_manager_command.flags &\n                     CLUSTER_MANAGER_CMD_FLAG_REPLACE;\n    while (1) {\n        char *dots = NULL;\n        redisReply *reply = NULL, *migrate_reply = NULL;\n        reply = CLUSTER_MANAGER_COMMAND(source, \"CLUSTER \"\n                                        \"GETKEYSINSLOT %d %d\", slot,\n                                        pipeline);\n        success = (reply != NULL);\n        if (!success) return 0;\n        if (reply->type == REDIS_REPLY_ERROR) {\n            success = 0;\n            if (err != NULL) {\n                *err = zmalloc((reply->len + 1) * sizeof(char));\n                strcpy(*err, reply->str);\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(source, *err);\n            }\n            goto next;\n        }\n        assert(reply->type == REDIS_REPLY_ARRAY);\n        size_t count = reply->elements;\n        if (count == 0) {\n            freeReplyObject(reply);\n            break;\n        }\n        if (verbose) dots = zmalloc((count+1) * sizeof(char));\n        /* Calling MIGRATE command. */\n        migrate_reply = clusterManagerMigrateKeysInReply(source, target,\n                                                         reply, 0, timeout,\n                                                         dots);\n        if (migrate_reply == NULL) goto next;\n        if (migrate_reply->type == REDIS_REPLY_ERROR) {\n            int is_busy = strstr(migrate_reply->str, \"BUSYKEY\") != NULL;\n            int not_served = 0;\n            if (!is_busy) {\n                /* Check if the slot is unassigned (not served) in the\n                 * source node's configuration. */\n                char *get_owner_err = NULL;\n                clusterManagerNode *served_by =\n                    clusterManagerGetSlotOwner(source, slot, &get_owner_err);\n                if (!served_by) {\n                    if (get_owner_err == NULL) not_served = 1;\n                    else {\n                        CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,\n                                                          get_owner_err);\n                        zfree(get_owner_err);\n                    }\n                }\n            }\n            /* Try to handle errors. */\n            if (is_busy || not_served) {\n                /* If the key's slot is not served, try to assign slot\n                 * to the target node. */\n                if (do_fix && not_served) {\n                    clusterManagerLogWarn(\"*** Slot was not served, setting \"\n                                          \"owner to node %s:%d.\\n\",\n                                          target->ip, target->port);\n                    clusterManagerSetSlot(source, target, slot, \"node\", NULL);\n                }\n                /* If the key already exists in the target node (BUSYKEY),\n                 * check whether its value is the same in both nodes.\n                 * In case of equal values, retry migration with the\n                 * REPLACE option.\n                 * In case of different values:\n                 *  - If the migration is requested by the fix command, stop\n                 *    and warn the user.\n                 *  - In other cases (ie. reshard), proceed only if the user\n                 *    launched the command with the --cluster-replace option.*/\n                if (is_busy) {\n                    clusterManagerLogWarn(\"\\n*** Target key exists\\n\");\n                    if (!do_replace) {\n                        clusterManagerLogWarn(\"*** Checking key values on \"\n                                              \"both nodes...\\n\");\n                        list *diffs = listCreate();\n                        success = clusterManagerCompareKeysValues(source,\n                            target, reply, diffs);\n                        if (!success) {\n                            clusterManagerLogErr(\"*** Value check failed!\\n\");\n                            listRelease(diffs);\n                            goto next;\n                        }\n                        if (listLength(diffs) > 0) {\n                            success = 0;\n                            clusterManagerLogErr(\n                                \"*** Found %d key(s) in both source node and \"\n                                \"target node having different values.\\n\"\n                                \"    Source node: %s:%d\\n\"\n                                \"    Target node: %s:%d\\n\"\n                                \"    Keys(s):\\n\",\n                                listLength(diffs),\n                                source->ip, source->port,\n                                target->ip, target->port);\n                            listIter dli;\n                            listNode *dln;\n                            listRewind(diffs, &dli);\n                            while((dln = listNext(&dli)) != NULL) {\n                                char *k = dln->value;\n                                clusterManagerLogErr(\"    - %s\\n\", k);\n                            }\n                            clusterManagerLogErr(\"Please fix the above key(s) \"\n                                                 \"manually and try again \"\n                                                 \"or relaunch the command \\n\"\n                                                 \"with --cluster-replace \"\n                                                 \"option to force key \"\n                                                 \"overriding.\\n\");\n                            listRelease(diffs);\n                            goto next;\n                        }\n                        listRelease(diffs);\n                    }\n                    clusterManagerLogWarn(\"*** Replacing target keys...\\n\");\n                }\n                freeReplyObject(migrate_reply);\n                migrate_reply = clusterManagerMigrateKeysInReply(source,\n                                                                 target,\n                                                                 reply,\n                                                                 is_busy,\n                                                                 timeout,\n                                                                 NULL);\n                success = (migrate_reply != NULL &&\n                           migrate_reply->type != REDIS_REPLY_ERROR);\n            } else success = 0;\n            if (!success) {\n                if (migrate_reply != NULL) {\n                    if (err) {\n                        *err = zmalloc((migrate_reply->len + 1) * sizeof(char));\n                        strcpy(*err, migrate_reply->str);\n                    }\n                    printf(\"\\n\");\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,\n                                                      migrate_reply->str);\n                }\n                goto next;\n            }\n        }\n        if (verbose) {\n            printf(\"%s\", dots);\n            fflush(stdout);\n        }\nnext:\n        if (reply != NULL) freeReplyObject(reply);\n        if (migrate_reply != NULL) freeReplyObject(migrate_reply);\n        if (dots) zfree(dots);\n        if (!success) break;\n    }\n    return success;\n}\n\n/* Move slots between source and target nodes using MIGRATE.\n *\n * Options:\n * CLUSTER_MANAGER_OPT_VERBOSE -- Print a dot for every moved key.\n * CLUSTER_MANAGER_OPT_COLD    -- Move keys without opening slots /\n *                                reconfiguring the nodes.\n * CLUSTER_MANAGER_OPT_UPDATE  -- Update node->slots for source/target nodes.\n * CLUSTER_MANAGER_OPT_QUIET   -- Don't print info messages.\n*/\nstatic int clusterManagerMoveSlot(clusterManagerNode *source,\n                                  clusterManagerNode *target,\n                                  int slot, int opts,  char**err)\n{\n    if (!(opts & CLUSTER_MANAGER_OPT_QUIET)) {\n        printf(\"Moving slot %d from %s:%d to %s:%d: \", slot, source->ip,\n               source->port, target->ip, target->port);\n        fflush(stdout);\n    }\n    if (err != NULL) *err = NULL;\n    int pipeline = config.cluster_manager_command.pipeline,\n        timeout = config.cluster_manager_command.timeout,\n        print_dots = (opts & CLUSTER_MANAGER_OPT_VERBOSE),\n        option_cold = (opts & CLUSTER_MANAGER_OPT_COLD),\n        success = 1;\n    if (!option_cold) {\n        success = clusterManagerSetSlot(target, source, slot,\n                                        \"importing\", err);\n        if (!success) return 0;\n        success = clusterManagerSetSlot(source, target, slot,\n                                        \"migrating\", err);\n        if (!success) return 0;\n    }\n    success = clusterManagerMigrateKeysInSlot(source, target, slot, timeout,\n                                              pipeline, print_dots, err);\n    if (!(opts & CLUSTER_MANAGER_OPT_QUIET)) printf(\"\\n\");\n    if (!success) return 0;\n    /* Set the new node as the owner of the slot in all the known nodes. */\n    if (!option_cold) {\n        listIter li;\n        listNode *ln;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER \"\n                                                    \"SETSLOT %d %s %s\",\n                                                    slot, \"node\",\n                                                    target->name);\n            success = (r != NULL);\n            if (!success) return 0;\n            if (r->type == REDIS_REPLY_ERROR) {\n                success = 0;\n                if (err != NULL) {\n                    *err = zmalloc((r->len + 1) * sizeof(char));\n                    strcpy(*err, r->str);\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, *err);\n                }\n            }\n            freeReplyObject(r);\n            if (!success) return 0;\n        }\n    }\n    /* Update the node logical config */\n    if (opts & CLUSTER_MANAGER_OPT_UPDATE) {\n        source->slots[slot] = 0;\n        target->slots[slot] = 1;\n    }\n    return 1;\n}\n\n/* Flush the dirty node configuration by calling replicate for slaves or\n * adding the slots defined in the masters. */\nstatic int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err) {\n    if (!node->dirty) return 0;\n    redisReply *reply = NULL;\n    int is_err = 0, success = 1;\n    if (err != NULL) *err = NULL;\n    if (node->replicate != NULL) {\n        reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER REPLICATE %s\",\n                                        node->replicate);\n        if (reply == NULL || (is_err = (reply->type == REDIS_REPLY_ERROR))) {\n            if (is_err && err != NULL) {\n                *err = zmalloc((reply->len + 1) * sizeof(char));\n                strcpy(*err, reply->str);\n            }\n            success = 0;\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            goto cleanup;\n        }\n    } else {\n        int added = clusterManagerAddSlots(node, err);\n        if (!added || *err != NULL) success = 0;\n    }\n    node->dirty = 0;\ncleanup:\n    if (reply != NULL) freeReplyObject(reply);\n    return success;\n}\n\n/* Wait until the cluster configuration is consistent. */\nstatic void clusterManagerWaitForClusterJoin(void) {\n    printf(\"Waiting for the cluster to join\\n\");\n    int counter = 0,\n        check_after = CLUSTER_JOIN_CHECK_AFTER +\n                      (int)(listLength(cluster_manager.nodes) * 0.15f);\n    while(!clusterManagerIsConfigConsistent()) {\n        printf(\".\");\n        fflush(stdout);\n        sleep(1);\n        if (++counter > check_after) {\n            dict *status = clusterManagerGetLinkStatus();\n            dictIterator *iter = NULL;\n            if (status != NULL && dictSize(status) > 0) {\n                printf(\"\\n\");\n                clusterManagerLogErr(\"Warning: %d node(s) may \"\n                                     \"be unreachable\\n\", dictSize(status));\n                iter = dictGetIterator(status);\n                dictEntry *entry;\n                while ((entry = dictNext(iter)) != NULL) {\n                    sds nodeaddr = (sds) dictGetKey(entry);\n                    char *node_ip = NULL;\n                    int node_port = 0, node_bus_port = 0;\n                    list *from = (list *) dictGetVal(entry);\n                    if (parseClusterNodeAddress(nodeaddr, &node_ip,\n                        &node_port, &node_bus_port) && node_bus_port) {\n                        clusterManagerLogErr(\" - The port %d of node %s may \"\n                                             \"be unreachable from:\\n\",\n                                             node_bus_port, node_ip);\n                    } else {\n                        clusterManagerLogErr(\" - Node %s may be unreachable \"\n                                             \"from:\\n\", nodeaddr);\n                    }\n                    listIter li;\n                    listNode *ln;\n                    listRewind(from, &li);\n                    while ((ln = listNext(&li)) != NULL) {\n                        sds from_addr = ln->value;\n                        clusterManagerLogErr(\"   %s\\n\", from_addr);\n                        sdsfree(from_addr);\n                    }\n                    clusterManagerLogErr(\"Cluster bus ports must be reachable \"\n                                         \"by every node.\\nRemember that \"\n                                         \"cluster bus ports are different \"\n                                         \"from standard instance ports.\\n\");\n                    listEmpty(from);\n                }\n            }\n            if (iter != NULL) dictReleaseIterator(iter);\n            if (status != NULL) dictRelease(status);\n            counter = 0;\n        }\n    }\n    printf(\"\\n\");\n}\n\n/* Load node's cluster configuration by calling \"CLUSTER NODES\" command.\n * Node's configuration (name, replicate, slots, ...) is then updated.\n * If CLUSTER_MANAGER_OPT_GETFRIENDS flag is set into 'opts' argument,\n * and node already knows other nodes, the node's friends list is populated\n * with the other nodes info. */\nstatic int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,\n                                      char **err)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER NODES\");\n    int success = 1;\n    *err = NULL;\n    if (!clusterManagerCheckRedisReply(node, reply, err)) {\n        success = 0;\n        goto cleanup;\n    }\n    int getfriends = (opts & CLUSTER_MANAGER_OPT_GETFRIENDS);\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *name = NULL, *addr = NULL, *flags = NULL, *master_id = NULL,\n             *ping_sent = NULL, *ping_recv = NULL, *config_epoch = NULL,\n             *link_status = NULL;\n        UNUSED(link_status);\n        int i = 0;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            switch(i++){\n            case 0: name = token; break;\n            case 1: addr = token; break;\n            case 2: flags = token; break;\n            case 3: master_id = token; break;\n            case 4: ping_sent = token; break;\n            case 5: ping_recv = token; break;\n            case 6: config_epoch = token; break;\n            case 7: link_status = token; break;\n            }\n            if (i == 8) break; // Slots\n        }\n        if (!flags) {\n            success = 0;\n            goto cleanup;\n        }\n        int myself = (strstr(flags, \"myself\") != NULL);\n        clusterManagerNode *currentNode = NULL;\n        if (myself) {\n            node->flags |= CLUSTER_MANAGER_FLAG_MYSELF;\n            currentNode = node;\n            clusterManagerNodeResetSlots(node);\n            if (i == 8) {\n                int remaining = strlen(line);\n                while (remaining > 0) {\n                    p = strchr(line, ' ');\n                    if (p == NULL) p = line + remaining;\n                    remaining -= (p - line);\n\n                    char *slotsdef = line;\n                    *p = '\\0';\n                    if (remaining) {\n                        line = p + 1;\n                        remaining--;\n                    } else line = p;\n                    char *dash = NULL;\n                    if (slotsdef[0] == '[') {\n                        slotsdef++;\n                        if ((p = strstr(slotsdef, \"->-\"))) { // Migrating\n                            *p = '\\0';\n                            p += 3;\n                            char *closing_bracket = strchr(p, ']');\n                            if (closing_bracket) *closing_bracket = '\\0';\n                            sds slot = sdsnew(slotsdef);\n                            sds dst = sdsnew(p);\n                            node->migrating_count += 2;\n                            node->migrating = zrealloc(node->migrating,\n                                (node->migrating_count * sizeof(sds)));\n                            node->migrating[node->migrating_count - 2] =\n                                slot;\n                            node->migrating[node->migrating_count - 1] =\n                                dst;\n                        }  else if ((p = strstr(slotsdef, \"-<-\"))) {//Importing\n                            *p = '\\0';\n                            p += 3;\n                            char *closing_bracket = strchr(p, ']');\n                            if (closing_bracket) *closing_bracket = '\\0';\n                            sds slot = sdsnew(slotsdef);\n                            sds src = sdsnew(p);\n                            node->importing_count += 2;\n                            node->importing = zrealloc(node->importing,\n                                (node->importing_count * sizeof(sds)));\n                            node->importing[node->importing_count - 2] =\n                                slot;\n                            node->importing[node->importing_count - 1] =\n                                src;\n                        }\n                    } else if ((dash = strchr(slotsdef, '-')) != NULL) {\n                        p = dash;\n                        int start, stop;\n                        *p = '\\0';\n                        start = atoi(slotsdef);\n                        stop = atoi(p + 1);\n                        node->slots_count += (stop - (start - 1));\n                        while (start <= stop) node->slots[start++] = 1;\n                    } else if (p > slotsdef) {\n                        node->slots[atoi(slotsdef)] = 1;\n                        node->slots_count++;\n                    }\n                }\n            }\n            node->dirty = 0;\n        } else if (!getfriends) {\n            if (!(node->flags & CLUSTER_MANAGER_FLAG_MYSELF)) continue;\n            else break;\n        } else {\n            if (addr == NULL) {\n                fprintf(stderr, \"Error: invalid CLUSTER NODES reply\\n\");\n                success = 0;\n                goto cleanup;\n            }\n            char *c = strrchr(addr, '@');\n            if (c != NULL) *c = '\\0';\n            c = strrchr(addr, ':');\n            if (c == NULL) {\n                fprintf(stderr, \"Error: invalid CLUSTER NODES reply\\n\");\n                success = 0;\n                goto cleanup;\n            }\n            *c = '\\0';\n            int port = atoi(++c);\n            currentNode = clusterManagerNewNode(sdsnew(addr), port);\n            currentNode->flags |= CLUSTER_MANAGER_FLAG_FRIEND;\n            if (node->friends == NULL) node->friends = listCreate();\n            listAddNodeTail(node->friends, currentNode);\n        }\n        if (name != NULL) {\n            if (currentNode->name) sdsfree(currentNode->name);\n            currentNode->name = sdsnew(name);\n        }\n        if (currentNode->flags_str != NULL)\n            freeClusterManagerNodeFlags(currentNode->flags_str);\n        currentNode->flags_str = listCreate();\n        int flag_len;\n        while ((flag_len = strlen(flags)) > 0) {\n            sds flag = NULL;\n            char *fp = strchr(flags, ',');\n            if (fp) {\n                *fp = '\\0';\n                flag = sdsnew(flags);\n                flags = fp + 1;\n            } else {\n                flag = sdsnew(flags);\n                flags += flag_len;\n            }\n            if (strcmp(flag, \"noaddr\") == 0)\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_NOADDR;\n            else if (strcmp(flag, \"disconnected\") == 0)\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_DISCONNECT;\n            else if (strcmp(flag, \"fail\") == 0)\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_FAIL;\n            else if (strcmp(flag, \"slave\") == 0) {\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_SLAVE;\n                if (master_id != NULL) {\n                    if (currentNode->replicate) sdsfree(currentNode->replicate);\n                    currentNode->replicate = sdsnew(master_id);\n                }\n            }\n            listAddNodeTail(currentNode->flags_str, flag);\n        }\n        if (config_epoch != NULL)\n            currentNode->current_epoch = atoll(config_epoch);\n        if (ping_sent != NULL) currentNode->ping_sent = atoll(ping_sent);\n        if (ping_recv != NULL) currentNode->ping_recv = atoll(ping_recv);\n        if (!getfriends && myself) break;\n    }\ncleanup:\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Retrieves info about the cluster using argument 'node' as the starting\n * point. All nodes will be loaded inside the cluster_manager.nodes list.\n * Warning: if something goes wrong, it will free the starting node before\n * returning 0. */\nstatic int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts) {\n    if (node->context == NULL && !clusterManagerNodeConnect(node)) {\n        freeClusterManagerNode(node);\n        return 0;\n    }\n    opts |= CLUSTER_MANAGER_OPT_GETFRIENDS;\n    char *e = NULL;\n    if (!clusterManagerNodeIsCluster(node, &e)) {\n        clusterManagerPrintNotClusterNodeError(node, e);\n        if (e) zfree(e);\n        freeClusterManagerNode(node);\n        return 0;\n    }\n    e = NULL;\n    if (!clusterManagerNodeLoadInfo(node, opts, &e)) {\n        if (e) {\n            CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, e);\n            zfree(e);\n        }\n        freeClusterManagerNode(node);\n        return 0;\n    }\n    listIter li;\n    listNode *ln;\n    if (cluster_manager.nodes != NULL) {\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL)\n            freeClusterManagerNode((clusterManagerNode *) ln->value);\n        listRelease(cluster_manager.nodes);\n    }\n    cluster_manager.nodes = listCreate();\n    listAddNodeTail(cluster_manager.nodes, node);\n    if (node->friends != NULL) {\n        listRewind(node->friends, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *friend = ln->value;\n            if (!friend->ip || !friend->port) goto invalid_friend;\n            if (!friend->context && !clusterManagerNodeConnect(friend))\n                goto invalid_friend;\n            e = NULL;\n            if (clusterManagerNodeLoadInfo(friend, 0, &e)) {\n                if (friend->flags & (CLUSTER_MANAGER_FLAG_NOADDR |\n                                     CLUSTER_MANAGER_FLAG_DISCONNECT |\n                                     CLUSTER_MANAGER_FLAG_FAIL))\n                {\n                    goto invalid_friend;\n                }\n                listAddNodeTail(cluster_manager.nodes, friend);\n            } else {\n                clusterManagerLogErr(\"[ERR] Unable to load info for \"\n                                     \"node %s:%d\\n\",\n                                     friend->ip, friend->port);\n                goto invalid_friend;\n            }\n            continue;\ninvalid_friend:\n            if (!(friend->flags & CLUSTER_MANAGER_FLAG_SLAVE))\n                cluster_manager.unreachable_masters++;\n            freeClusterManagerNode(friend);\n        }\n        listRelease(node->friends);\n        node->friends = NULL;\n    }\n    // Count replicas for each node\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->replicate != NULL) {\n            clusterManagerNode *master = clusterManagerNodeByName(n->replicate);\n            if (master == NULL) {\n                clusterManagerLogWarn(\"*** WARNING: %s:%d claims to be \"\n                                      \"slave of unknown node ID %s.\\n\",\n                                      n->ip, n->port, n->replicate);\n            } else master->replicas_count++;\n        }\n    }\n    return 1;\n}\n\n/* Compare functions used by various sorting operations. */\nint clusterManagerSlotCompare(const void *slot1, const void *slot2) {\n    const char **i1 = (const char **)slot1;\n    const char **i2 = (const char **)slot2;\n    return strcmp(*i1, *i2);\n}\n\nint clusterManagerSlotCountCompareDesc(const void *n1, const void *n2) {\n    clusterManagerNode *node1 = *((clusterManagerNode **) n1);\n    clusterManagerNode *node2 = *((clusterManagerNode **) n2);\n    return node2->slots_count - node1->slots_count;\n}\n\nint clusterManagerCompareNodeBalance(const void *n1, const void *n2) {\n    clusterManagerNode *node1 = *((clusterManagerNode **) n1);\n    clusterManagerNode *node2 = *((clusterManagerNode **) n2);\n    return node1->balance - node2->balance;\n}\n\nstatic sds clusterManagerGetConfigSignature(clusterManagerNode *node) {\n    sds signature = NULL;\n    int node_count = 0, i = 0, name_len = 0;\n    char **node_configs = NULL;\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER NODES\");\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR)\n        goto cleanup;\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        i = 0;\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *nodename = NULL;\n        int tot_size = 0;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            if (i == 0) {\n                nodename = token;\n                tot_size = (p - token);\n                name_len = tot_size++; // Make room for ':' in tot_size\n            }\n            if (++i == 8) break;\n        }\n        if (i != 8) continue;\n        if (nodename == NULL) continue;\n        int remaining = strlen(line);\n        if (remaining == 0) continue;\n        char **slots = NULL;\n        int c = 0;\n        while (remaining > 0) {\n            p = strchr(line, ' ');\n            if (p == NULL) p = line + remaining;\n            int size = (p - line);\n            remaining -= size;\n            tot_size += size;\n            char *slotsdef = line;\n            *p = '\\0';\n            if (remaining) {\n                line = p + 1;\n                remaining--;\n            } else line = p;\n            if (slotsdef[0] != '[') {\n                c++;\n                slots = zrealloc(slots, (c * sizeof(char *)));\n                slots[c - 1] = slotsdef;\n            }\n        }\n        if (c > 0) {\n            if (c > 1)\n                qsort(slots, c, sizeof(char *), clusterManagerSlotCompare);\n            node_count++;\n            node_configs =\n                zrealloc(node_configs, (node_count * sizeof(char *)));\n            /* Make room for '|' separators. */\n            tot_size += (sizeof(char) * (c - 1));\n            char *cfg = zmalloc((sizeof(char) * tot_size) + 1);\n            memcpy(cfg, nodename, name_len);\n            char *sp = cfg + name_len;\n            *(sp++) = ':';\n            for (i = 0; i < c; i++) {\n                if (i > 0) *(sp++) = ',';\n                int slen = strlen(slots[i]);\n                memcpy(sp, slots[i], slen);\n                sp += slen;\n            }\n            *(sp++) = '\\0';\n            node_configs[node_count - 1] = cfg;\n        }\n        zfree(slots);\n    }\n    if (node_count > 0) {\n        if (node_count > 1) {\n            qsort(node_configs, node_count, sizeof(char *),\n                  clusterManagerSlotCompare);\n        }\n        signature = sdsempty();\n        for (i = 0; i < node_count; i++) {\n            if (i > 0) signature = sdscatprintf(signature, \"%c\", '|');\n            signature = sdscatfmt(signature, \"%s\", node_configs[i]);\n        }\n    }\ncleanup:\n    if (reply != NULL) freeReplyObject(reply);\n    if (node_configs != NULL) {\n        for (i = 0; i < node_count; i++) zfree(node_configs[i]);\n        zfree(node_configs);\n    }\n    return signature;\n}\n\nstatic int clusterManagerIsConfigConsistent(void) {\n    if (cluster_manager.nodes == NULL) return 0;\n    int consistent = (listLength(cluster_manager.nodes) <= 1);\n    // If the Cluster has only one node, it's always consistent\n    if (consistent) return 1;\n    sds first_cfg = NULL;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        sds cfg = clusterManagerGetConfigSignature(node);\n        if (cfg == NULL) {\n            consistent = 0;\n            break;\n        }\n        if (first_cfg == NULL) first_cfg = cfg;\n        else {\n            consistent = !sdscmp(first_cfg, cfg);\n            sdsfree(cfg);\n            if (!consistent) break;\n        }\n    }\n    if (first_cfg != NULL) sdsfree(first_cfg);\n    return consistent;\n}\n\nstatic list *clusterManagerGetDisconnectedLinks(clusterManagerNode *node) {\n    list *links = NULL;\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER NODES\");\n    if (!clusterManagerCheckRedisReply(node, reply, NULL)) goto cleanup;\n    links = listCreate();\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        int i = 0;\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *nodename = NULL, *addr = NULL, *flags = NULL, *link_status = NULL;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            if (i == 0) nodename = token;\n            else if (i == 1) addr = token;\n            else if (i == 2) flags = token;\n            else if (i == 7) link_status = token;\n            else if (i == 8) break;\n            i++;\n        }\n        if (i == 7) link_status = line;\n        if (nodename == NULL || addr == NULL || flags == NULL ||\n            link_status == NULL) continue;\n        if (strstr(flags, \"myself\") != NULL) continue;\n        int disconnected = ((strstr(flags, \"disconnected\") != NULL) ||\n                            (strstr(link_status, \"disconnected\")));\n        int handshaking = (strstr(flags, \"handshake\") != NULL);\n        if (disconnected || handshaking) {\n            clusterManagerLink *link = zmalloc(sizeof(*link));\n            link->node_name = sdsnew(nodename);\n            link->node_addr = sdsnew(addr);\n            link->connected = 0;\n            link->handshaking = handshaking;\n            listAddNodeTail(links, link);\n        }\n    }\ncleanup:\n    if (reply != NULL) freeReplyObject(reply);\n    return links;\n}\n\n/* Check for disconnected cluster links. It returns a dict whose keys\n * are the unreachable node addresses and the values are lists of\n * node addresses that cannot reach the unreachable node. */\nstatic dict *clusterManagerGetLinkStatus(void) {\n    if (cluster_manager.nodes == NULL) return NULL;\n    dict *status = dictCreate(&clusterManagerLinkDictType, NULL);\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        list *links = clusterManagerGetDisconnectedLinks(node);\n        if (links) {\n            listIter lli;\n            listNode *lln;\n            listRewind(links, &lli);\n            while ((lln = listNext(&lli)) != NULL) {\n                clusterManagerLink *link = lln->value;\n                list *from = NULL;\n                dictEntry *entry = dictFind(status, link->node_addr);\n                if (entry) from = dictGetVal(entry);\n                else {\n                    from = listCreate();\n                    dictAdd(status, sdsdup(link->node_addr), from);\n                }\n                sds myaddr = sdsempty();\n                myaddr = sdscatfmt(myaddr, \"%s:%u\", node->ip, node->port);\n                listAddNodeTail(from, myaddr);\n                sdsfree(link->node_name);\n                sdsfree(link->node_addr);\n                zfree(link);\n            }\n            listRelease(links);\n        }\n    }\n    return status;\n}\n\n/* Add the error string to cluster_manager.errors and print it. */\nstatic void clusterManagerOnError(sds err) {\n    if (cluster_manager.errors == NULL)\n        cluster_manager.errors = listCreate();\n    listAddNodeTail(cluster_manager.errors, err);\n    clusterManagerLogErr(\"%s\\n\", (char *) err);\n}\n\n/* Check the slots coverage of the cluster. The 'all_slots' argument must be\n * and array of 16384 bytes. Every covered slot will be set to 1 in the\n * 'all_slots' array. The function returns the total number if covered slots.*/\nstatic int clusterManagerGetCoveredSlots(char *all_slots) {\n    if (cluster_manager.nodes == NULL) return 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    int totslots = 0, i;\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n            if (node->slots[i] && !all_slots[i]) {\n                all_slots[i] = 1;\n                totslots++;\n            }\n        }\n    }\n    return totslots;\n}\n\nstatic void clusterManagerPrintSlotsList(list *slots) {\n    clusterManagerNode n = {0};\n    listIter li;\n    listNode *ln;\n    listRewind(slots, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        int slot = atoi(ln->value);\n        if (slot >= 0 && slot < CLUSTER_MANAGER_SLOTS)\n            n.slots[slot] = 1;\n    }\n    sds nodeslist = clusterManagerNodeSlotsString(&n);\n    printf(\"%s\\n\", nodeslist);\n    sdsfree(nodeslist);\n}\n\n/* Return the node, among 'nodes' with the greatest number of keys\n * in the specified slot. */\nstatic clusterManagerNode * clusterManagerGetNodeWithMostKeysInSlot(list *nodes,\n                                                                    int slot,\n                                                                    char **err)\n{\n    clusterManagerNode *node = NULL;\n    int numkeys = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(nodes, &li);\n    if (err) *err = NULL;\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n            continue;\n        redisReply *r =\n            CLUSTER_MANAGER_COMMAND(n, \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n        int success = clusterManagerCheckRedisReply(n, r, err);\n        if (success) {\n            if (r->integer > numkeys || node == NULL) {\n                numkeys = r->integer;\n                node = n;\n            }\n        }\n        if (r != NULL) freeReplyObject(r);\n        /* If the reply contains errors */\n        if (!success) {\n            if (err != NULL && *err != NULL)\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err);\n            node = NULL;\n            break;\n        }\n    }\n    return node;\n}\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\nstatic clusterManagerNode *clusterManagerNodeWithLeastReplicas() {\n    clusterManagerNode *node = NULL;\n    int lowest_count = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        if (node == NULL || n->replicas_count < lowest_count) {\n            node = n;\n            lowest_count = n->replicas_count;\n        }\n    }\n    return node;\n}\n\n/* This function returns a random master node, return NULL if none */\n\nstatic clusterManagerNode *clusterManagerNodeMasterRandom() {\n    int master_count = 0;\n    int idx;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        master_count++;\n    }\n\n    srand(time(NULL));\n    idx = rand() % master_count;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        if (!idx--) {\n            return n;\n        }\n    }\n    /* Can not be reached */\n    return NULL;\n}\n\nstatic int clusterManagerFixSlotsCoverage(char *all_slots) {\n    int force_fix = config.cluster_manager_command.flags &\n                    CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;\n\n    if (cluster_manager.unreachable_masters > 0 && !force_fix) {\n        clusterManagerLogWarn(\"*** Fixing slots coverage with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\\n\", cluster_manager.unreachable_masters);\n        exit(1);\n    }\n\n    int i, fixed = 0;\n    list *none = NULL, *single = NULL, *multi = NULL;\n    clusterManagerLogInfo(\">>> Fixing slots coverage...\\n\");\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        int covered = all_slots[i];\n        if (!covered) {\n            sds slot = sdsfromlonglong((long long) i);\n            list *slot_nodes = listCreate();\n            sds slot_nodes_str = sdsempty();\n            listIter li;\n            listNode *ln;\n            listRewind(cluster_manager.nodes, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n                    continue;\n                redisReply *reply = CLUSTER_MANAGER_COMMAND(n,\n                    \"CLUSTER GETKEYSINSLOT %d %d\", i, 1);\n                if (!clusterManagerCheckRedisReply(n, reply, NULL)) {\n                    fixed = -1;\n                    if (reply) freeReplyObject(reply);\n                    goto cleanup;\n                }\n                assert(reply->type == REDIS_REPLY_ARRAY);\n                if (reply->elements > 0) {\n                    listAddNodeTail(slot_nodes, n);\n                    if (listLength(slot_nodes) > 1)\n                        slot_nodes_str = sdscat(slot_nodes_str, \", \");\n                    slot_nodes_str = sdscatfmt(slot_nodes_str,\n                                               \"%s:%u\", n->ip, n->port);\n                }\n                freeReplyObject(reply);\n            }\n            sdsfree(slot_nodes_str);\n            dictAdd(clusterManagerUncoveredSlots, slot, slot_nodes);\n        }\n    }\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    none = listCreate();\n    single = listCreate();\n    multi = listCreate();\n    dictIterator *iter = dictGetIterator(clusterManagerUncoveredSlots);\n    dictEntry *entry;\n    while ((entry = dictNext(iter)) != NULL) {\n        sds slot = (sds) dictGetKey(entry);\n        list *nodes = (list *) dictGetVal(entry);\n        switch (listLength(nodes)){\n        case 0: listAddNodeTail(none, slot); break;\n        case 1: listAddNodeTail(single, slot); break;\n        default: listAddNodeTail(multi, slot); break;\n        }\n    }\n    dictReleaseIterator(iter);\n\n    /*  Handle case \"1\": keys in no node. */\n    if (listLength(none) > 0) {\n        printf(\"The following uncovered slots have no keys \"\n               \"across the cluster:\\n\");\n        clusterManagerPrintSlotsList(none);\n        if (confirmWithYes(\"Fix these slots by covering with a random node?\")){\n            listIter li;\n            listNode *ln;\n            listRewind(none, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                sds slot = ln->value;\n                int s = atoi(slot);\n                clusterManagerNode *n = clusterManagerNodeMasterRandom();\n                clusterManagerLogInfo(\">>> Covering slot %s with %s:%d\\n\",\n                                      slot, n->ip, n->port);\n                if (!clusterManagerSetSlotOwner(n, s, 0)) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n                 * info into the node struct, in order to keep it synced */\n                n->slots[s] = 1;\n                fixed++;\n            }\n        }\n    }\n\n    /*  Handle case \"2\": keys only in one node. */\n    if (listLength(single) > 0) {\n        printf(\"The following uncovered slots have keys in just one node:\\n\");\n        clusterManagerPrintSlotsList(single);\n        if (confirmWithYes(\"Fix these slots by covering with those nodes?\")){\n            listIter li;\n            listNode *ln;\n            listRewind(single, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                sds slot = ln->value;\n                int s = atoi(slot);\n                dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);\n                assert(entry != NULL);\n                list *nodes = (list *) dictGetVal(entry);\n                listNode *fn = listFirst(nodes);\n                assert(fn != NULL);\n                clusterManagerNode *n = fn->value;\n                clusterManagerLogInfo(\">>> Covering slot %s with %s:%d\\n\",\n                                      slot, n->ip, n->port);\n                if (!clusterManagerSetSlotOwner(n, s, 0)) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n                 * info into the node struct, in order to keep it synced */\n                n->slots[atoi(slot)] = 1;\n                fixed++;\n            }\n        }\n    }\n\n    /* Handle case \"3\": keys in multiple nodes. */\n    if (listLength(multi) > 0) {\n        printf(\"The following uncovered slots have keys in multiple nodes:\\n\");\n        clusterManagerPrintSlotsList(multi);\n        if (confirmWithYes(\"Fix these slots by moving keys \"\n                           \"into a single node?\")) {\n            listIter li;\n            listNode *ln;\n            listRewind(multi, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                sds slot = ln->value;\n                dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);\n                assert(entry != NULL);\n                list *nodes = (list *) dictGetVal(entry);\n                int s = atoi(slot);\n                clusterManagerNode *target =\n                    clusterManagerGetNodeWithMostKeysInSlot(nodes, s, NULL);\n                if (target == NULL) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                clusterManagerLogInfo(\">>> Covering slot %s moving keys \"\n                                      \"to %s:%d\\n\", slot,\n                                      target->ip, target->port);\n                if (!clusterManagerSetSlotOwner(target, s, 1)) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n                 * info into the node struct, in order to keep it synced */\n                target->slots[atoi(slot)] = 1;\n                listIter nli;\n                listNode *nln;\n                listRewind(nodes, &nli);\n                while ((nln = listNext(&nli)) != NULL) {\n                    clusterManagerNode *src = nln->value;\n                    if (src == target) continue;\n                    /* Assign the slot to target node in the source node. */\n                    if (!clusterManagerSetSlot(src, target, s, \"NODE\", NULL))\n                        fixed = -1;\n                    if (fixed < 0) goto cleanup;\n                    /* Set the source node in 'importing' state\n                     * (even if we will actually migrate keys away)\n                     * in order to avoid receiving redirections\n                     * for MIGRATE. */\n                    if (!clusterManagerSetSlot(src, target, s,\n                                               \"IMPORTING\", NULL)) fixed = -1;\n                    if (fixed < 0) goto cleanup;\n                    int opts = CLUSTER_MANAGER_OPT_VERBOSE |\n                               CLUSTER_MANAGER_OPT_COLD;\n                    if (!clusterManagerMoveSlot(src, target, s, opts, NULL)) {\n                        fixed = -1;\n                        goto cleanup;\n                    }\n                    if (!clusterManagerClearSlotStatus(src, s))\n                        fixed = -1;\n                    if (fixed < 0) goto cleanup;\n                }\n                fixed++;\n            }\n        }\n    }\ncleanup:\n    if (none) listRelease(none);\n    if (single) listRelease(single);\n    if (multi) listRelease(multi);\n    return fixed;\n}\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. */\nstatic int clusterManagerFixOpenSlot(int slot) {\n    int force_fix = config.cluster_manager_command.flags &\n                    CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;\n\n    if (cluster_manager.unreachable_masters > 0 && !force_fix) {\n        clusterManagerLogWarn(\"*** Fixing open slots with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\\n\", cluster_manager.unreachable_masters);\n        exit(1);\n    }\n\n    clusterManagerLogInfo(\">>> Fixing open slot %d\\n\", slot);\n    /* Try to obtain the current slot owner, according to the current\n     * nodes configuration. */\n    int success = 1;\n    list *owners = listCreate();    /* List of nodes claiming some ownership.\n                                       it could be stating in the configuration\n                                       to have the node ownership, or just\n                                       holding keys for such slot. */\n    list *migrating = listCreate();\n    list *importing = listCreate();\n    sds migrating_str = sdsempty();\n    sds importing_str = sdsempty();\n    clusterManagerNode *owner = NULL; /* The obvious slot owner if any. */\n\n    /* Iterate all the nodes, looking for potential owners of this slot. */\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        if (n->slots[slot]) {\n            listAddNodeTail(owners, n);\n        } else {\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n,\n                \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (success && r->integer > 0) {\n                clusterManagerLogWarn(\"*** Found keys about slot %d \"\n                                      \"in non-owner node %s:%d!\\n\", slot,\n                                      n->ip, n->port);\n                listAddNodeTail(owners, n);\n            }\n            if (r) freeReplyObject(r);\n            if (!success) goto cleanup;\n        }\n    }\n\n    /* If we have only a single potential owner for this slot,\n     * set it as \"owner\". */\n    if (listLength(owners) == 1) owner = listFirst(owners)->value;\n\n    /* Scan the list of nodes again, in order to populate the\n     * list of nodes in importing or migrating state for\n     * this slot. */\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        int is_migrating = 0, is_importing = 0;\n        if (n->migrating) {\n            for (int i = 0; i < n->migrating_count; i += 2) {\n                sds migrating_slot = n->migrating[i];\n                if (atoi(migrating_slot) == slot) {\n                    char *sep = (listLength(migrating) == 0 ? \"\" : \",\");\n                    migrating_str = sdscatfmt(migrating_str, \"%s%s:%u\",\n                                              sep, n->ip, n->port);\n                    listAddNodeTail(migrating, n);\n                    is_migrating = 1;\n                    break;\n                }\n            }\n        }\n        if (!is_migrating && n->importing) {\n            for (int i = 0; i < n->importing_count; i += 2) {\n                sds importing_slot = n->importing[i];\n                if (atoi(importing_slot) == slot) {\n                    char *sep = (listLength(importing) == 0 ? \"\" : \",\");\n                    importing_str = sdscatfmt(importing_str, \"%s%s:%u\",\n                                              sep, n->ip, n->port);\n                    listAddNodeTail(importing, n);\n                    is_importing = 1;\n                    break;\n                }\n            }\n        }\n\n        /* If the node is neither migrating nor importing and it's not\n         * the owner, then is added to the importing list in case\n         * it has keys in the slot. */\n        if (!is_migrating && !is_importing && n != owner) {\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n,\n                \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (success && r->integer > 0) {\n                clusterManagerLogWarn(\"*** Found keys about slot %d \"\n                                      \"in node %s:%d!\\n\", slot, n->ip,\n                                      n->port);\n                char *sep = (listLength(importing) == 0 ? \"\" : \",\");\n                importing_str = sdscatfmt(importing_str, \"%s%S:%u\",\n                                          sep, n->ip, n->port);\n                listAddNodeTail(importing, n);\n            }\n            if (r) freeReplyObject(r);\n            if (!success) goto cleanup;\n        }\n    }\n    if (sdslen(migrating_str) > 0)\n        printf(\"Set as migrating in: %s\\n\", migrating_str);\n    if (sdslen(importing_str) > 0)\n        printf(\"Set as importing in: %s\\n\", importing_str);\n\n    /* If there is no slot owner, set as owner the node with the biggest\n     * number of keys, among the set of migrating / importing nodes. */\n    if (owner == NULL) {\n        clusterManagerLogInfo(\">>> No single clear owner for the slot, \"\n                              \"selecting an owner by # of keys...\\n\");\n        owner = clusterManagerGetNodeWithMostKeysInSlot(cluster_manager.nodes,\n                                                        slot, NULL);\n        // If we still don't have an owner, we can't fix it.\n        if (owner == NULL) {\n            clusterManagerLogErr(\"[ERR] Can't select a slot owner. \"\n                                 \"Impossible to fix.\\n\");\n            success = 0;\n            goto cleanup;\n        }\n\n        // Use ADDSLOTS to assign the slot.\n        clusterManagerLogWarn(\"*** Configuring %s:%d as the slot owner\\n\",\n                              owner->ip, owner->port);\n        success = clusterManagerClearSlotStatus(owner, slot);\n        if (!success) goto cleanup;\n        success = clusterManagerSetSlotOwner(owner, slot, 0);\n        if (!success) goto cleanup;\n        /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n         * info into the node struct, in order to keep it synced */\n        owner->slots[slot] = 1;\n        /* Make sure this information will propagate. Not strictly needed\n         * since there is no past owner, so all the other nodes will accept\n         * whatever epoch this node will claim the slot with. */\n        success = clusterManagerBumpEpoch(owner);\n        if (!success) goto cleanup;\n        /* Remove the owner from the list of migrating/importing\n         * nodes. */\n        clusterManagerRemoveNodeFromList(migrating, owner);\n        clusterManagerRemoveNodeFromList(importing, owner);\n    }\n\n    /* If there are multiple owners of the slot, we need to fix it\n     * so that a single node is the owner and all the other nodes\n     * are in importing state. Later the fix can be handled by one\n     * of the base cases above.\n     *\n     * Note that this case also covers multiple nodes having the slot\n     * in migrating state, since migrating is a valid state only for\n     * slot owners. */\n    if (listLength(owners) > 1) {\n        /* Owner cannot be NULL at this point, since if there are more owners,\n         * the owner has been set in the previous condition (owner == NULL). */\n        assert(owner != NULL);\n        listRewind(owners, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n == owner) continue;\n            success = clusterManagerDelSlot(n, slot, 1);\n            if (!success) goto cleanup;\n            n->slots[slot] = 0;\n            /* Assign the slot to the owner in the node 'n' configuration.' */\n            success = clusterManagerSetSlot(n, owner, slot, \"node\", NULL);\n            if (!success) goto cleanup;\n            success = clusterManagerSetSlot(n, owner, slot, \"importing\", NULL);\n            if (!success) goto cleanup;\n            /* Avoid duplicates. */\n            clusterManagerRemoveNodeFromList(importing, n);\n            listAddNodeTail(importing, n);\n            /* Ensure that the node is not in the migrating list. */\n            clusterManagerRemoveNodeFromList(migrating, n);\n        }\n    }\n    int move_opts = CLUSTER_MANAGER_OPT_VERBOSE;\n\n    /* Case 1: The slot is in migrating state in one node, and in\n     *         importing state in 1 node. That's trivial to address. */\n    if (listLength(migrating) == 1 && listLength(importing) == 1) {\n        clusterManagerNode *src = listFirst(migrating)->value;\n        clusterManagerNode *dst = listFirst(importing)->value;\n        clusterManagerLogInfo(\">>> Case 1: Moving slot %d from \"\n                              \"%s:%d to %s:%d\\n\", slot,\n                              src->ip, src->port, dst->ip, dst->port);\n        move_opts |= CLUSTER_MANAGER_OPT_UPDATE;\n        success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);\n    }\n\n    /* Case 2: There are multiple nodes that claim the slot as importing,\n     * they probably got keys about the slot after a restart so opened\n     * the slot. In this case we just move all the keys to the owner\n     * according to the configuration. */\n    else if (listLength(migrating) == 0 && listLength(importing) > 0) {\n        clusterManagerLogInfo(\">>> Case 2: Moving all the %d slot keys to its \"\n                              \"owner %s:%d\\n\", slot, owner->ip, owner->port);\n        move_opts |= CLUSTER_MANAGER_OPT_COLD;\n        listRewind(importing, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n == owner) continue;\n            success = clusterManagerMoveSlot(n, owner, slot, move_opts, NULL);\n            if (!success) goto cleanup;\n            clusterManagerLogInfo(\">>> Setting %d as STABLE in \"\n                                  \"%s:%d\\n\", slot, n->ip, n->port);\n            success = clusterManagerClearSlotStatus(n, slot);\n            if (!success) goto cleanup;\n        }\n        /* Since the slot has been moved in \"cold\" mode, ensure that all the\n         * other nodes update their own configuration about the slot itself. */\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n == owner) continue;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n            success = clusterManagerSetSlot(n, owner, slot, \"NODE\", NULL);\n            if (!success) goto cleanup;\n        }\n    }\n\n    /* Case 3: The slot is in migrating state in one node but multiple\n     * other nodes claim to be in importing state and don't have any key in\n     * the slot. We search for the importing node having the same ID as\n     * the destination node of the migrating node.\n     * In that case we move the slot from the migrating node to this node and\n     * we close the importing states on all the other importing nodes.\n     * If no importing node has the same ID as the destination node of the\n     * migrating node, the slot's state is closed on both the migrating node\n     * and the importing nodes. */\n    else if (listLength(migrating) == 1 && listLength(importing) > 1) {\n        int try_to_fix = 1;\n        clusterManagerNode *src = listFirst(migrating)->value;\n        clusterManagerNode *dst = NULL;\n        sds target_id = NULL;\n        for (int i = 0; i < src->migrating_count; i += 2) {\n            sds migrating_slot = src->migrating[i];\n            if (atoi(migrating_slot) == slot) {\n                target_id = src->migrating[i + 1];\n                break;\n            }\n        }\n        assert(target_id != NULL);\n        listIter li;\n        listNode *ln;\n        listRewind(importing, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            int count = clusterManagerCountKeysInSlot(n, slot);\n            if (count > 0) {\n                try_to_fix = 0;\n                break;\n            }\n            if (strcmp(n->name, target_id) == 0) dst = n;\n        }\n        if (!try_to_fix) goto unhandled_case;\n        if (dst != NULL) {\n            clusterManagerLogInfo(\">>> Case 3: Moving slot %d from %s:%d to \"\n                                  \"%s:%d and closing it on all the other \"\n                                  \"importing nodes.\\n\",\n                                  slot, src->ip, src->port,\n                                  dst->ip, dst->port);\n            /* Move the slot to the destination node. */\n            success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);\n            if (!success) goto cleanup;\n            /* Close slot on all the other importing nodes. */\n            listRewind(importing, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (dst == n) continue;\n                success = clusterManagerClearSlotStatus(n, slot);\n                if (!success) goto cleanup;\n            }\n        } else {\n            clusterManagerLogInfo(\">>> Case 3: Closing slot %d on both \"\n                                  \"migrating and importing nodes.\\n\", slot);\n            /* Close the slot on both the migrating node and the importing\n             * nodes. */\n            success = clusterManagerClearSlotStatus(src, slot);\n            if (!success) goto cleanup;\n            listRewind(importing, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                success = clusterManagerClearSlotStatus(n, slot);\n                if (!success) goto cleanup;\n            }\n        }\n    } else {\n        int try_to_close_slot = (listLength(importing) == 0 &&\n                                 listLength(migrating) == 1);\n        if (try_to_close_slot) {\n            clusterManagerNode *n = listFirst(migrating)->value;\n            if (!owner || owner != n) {\n                redisReply *r = CLUSTER_MANAGER_COMMAND(n,\n                    \"CLUSTER GETKEYSINSLOT %d %d\", slot, 10);\n                success = clusterManagerCheckRedisReply(n, r, NULL);\n                if (r) {\n                    if (success) try_to_close_slot = (r->elements == 0);\n                    freeReplyObject(r);\n                }\n                if (!success) goto cleanup;\n            }\n        }\n        /* Case 4: There are no slots claiming to be in importing state, but\n         * there is a migrating node that actually don't have any key or is the\n         * slot owner. We can just close the slot, probably a reshard\n         * interrupted in the middle. */\n        if (try_to_close_slot) {\n            clusterManagerNode *n = listFirst(migrating)->value;\n            clusterManagerLogInfo(\">>> Case 4: Closing slot %d on %s:%d\\n\",\n                                  slot, n->ip, n->port);\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER SETSLOT %d %s\",\n                                                    slot, \"STABLE\");\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (r) freeReplyObject(r);\n            if (!success) goto cleanup;\n        } else {\nunhandled_case:\n            success = 0;\n            clusterManagerLogErr(\"[ERR] Sorry, redis-cli can't fix this slot \"\n                                 \"yet (work in progress). Slot is set as \"\n                                 \"migrating in %s, as importing in %s, \"\n                                 \"owner is %s:%d\\n\", migrating_str,\n                                 importing_str, owner->ip, owner->port);\n        }\n    }\ncleanup:\n    listRelease(owners);\n    listRelease(migrating);\n    listRelease(importing);\n    sdsfree(migrating_str);\n    sdsfree(importing_str);\n    return success;\n}\n\nstatic int clusterManagerFixMultipleSlotOwners(int slot, list *owners) {\n    clusterManagerLogInfo(\">>> Fixing multiple owners for slot %d...\\n\", slot);\n    int success = 0;\n    assert(listLength(owners) > 1);\n    clusterManagerNode *owner = clusterManagerGetNodeWithMostKeysInSlot(owners,\n                                                                        slot,\n                                                                        NULL);\n    if (!owner) owner = listFirst(owners)->value;\n    clusterManagerLogInfo(\">>> Setting slot %d owner: %s:%d\\n\",\n                          slot, owner->ip, owner->port);\n    /* Set the slot owner. */\n    if (!clusterManagerSetSlotOwner(owner, slot, 0)) return 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    /* Update configuration in all the other master nodes by assigning the slot\n     * itself to the new owner, and by eventually migrating keys if the node\n     * has keys for the slot. */\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n == owner) continue;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        int count = clusterManagerCountKeysInSlot(n, slot);\n        success = (count >= 0);\n        if (!success) break;\n        clusterManagerDelSlot(n, slot, 1);\n        if (!clusterManagerSetSlot(n, owner, slot, \"node\", NULL)) return 0;\n        if (count > 0) {\n            int opts = CLUSTER_MANAGER_OPT_VERBOSE |\n                       CLUSTER_MANAGER_OPT_COLD;\n            success = clusterManagerMoveSlot(n, owner, slot, opts, NULL);\n            if (!success) break;\n        }\n    }\n    return success;\n}\n\nstatic int clusterManagerCheckCluster(int quiet) {\n    listNode *ln = listFirst(cluster_manager.nodes);\n    if (!ln) return 0;\n    clusterManagerNode *node = ln->value;\n    clusterManagerLogInfo(\">>> Performing Cluster Check (using node %s:%d)\\n\",\n                          node->ip, node->port);\n    int result = 1, consistent = 0;\n    int do_fix = config.cluster_manager_command.flags &\n                 CLUSTER_MANAGER_CMD_FLAG_FIX;\n    if (!quiet) clusterManagerShowNodes();\n    consistent = clusterManagerIsConfigConsistent();\n    if (!consistent) {\n        sds err = sdsnew(\"[ERR] Nodes don't agree about configuration!\");\n        clusterManagerOnError(err);\n        result = 0;\n    } else {\n        clusterManagerLogOk(\"[OK] All nodes agree about slots \"\n                            \"configuration.\\n\");\n    }\n    /* Check open slots */\n    clusterManagerLogInfo(\">>> Check for open slots...\\n\");\n    listIter li;\n    listRewind(cluster_manager.nodes, &li);\n    int i;\n    dict *open_slots = NULL;\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->migrating != NULL) {\n            if (open_slots == NULL)\n                open_slots = dictCreate(&clusterManagerDictType, NULL);\n            sds errstr = sdsempty();\n            errstr = sdscatprintf(errstr,\n                                \"[WARNING] Node %s:%d has slots in \"\n                                \"migrating state \",\n                                n->ip,\n                                n->port);\n            for (i = 0; i < n->migrating_count; i += 2) {\n                sds slot = n->migrating[i];\n                dictReplace(open_slots, slot, sdsdup(n->migrating[i + 1]));\n                char *fmt = (i > 0 ? \",%S\" : \"%S\");\n                errstr = sdscatfmt(errstr, fmt, slot);\n            }\n            errstr = sdscat(errstr, \".\");\n            clusterManagerOnError(errstr);\n        }\n        if (n->importing != NULL) {\n            if (open_slots == NULL)\n                open_slots = dictCreate(&clusterManagerDictType, NULL);\n            sds errstr = sdsempty();\n            errstr = sdscatprintf(errstr,\n                                \"[WARNING] Node %s:%d has slots in \"\n                                \"importing state \",\n                                n->ip,\n                                n->port);\n            for (i = 0; i < n->importing_count; i += 2) {\n                sds slot = n->importing[i];\n                dictReplace(open_slots, slot, sdsdup(n->importing[i + 1]));\n                char *fmt = (i > 0 ? \",%S\" : \"%S\");\n                errstr = sdscatfmt(errstr, fmt, slot);\n            }\n            errstr = sdscat(errstr, \".\");\n            clusterManagerOnError(errstr);\n        }\n    }\n    if (open_slots != NULL) {\n        result = 0;\n        dictIterator *iter = dictGetIterator(open_slots);\n        dictEntry *entry;\n        sds errstr = sdsnew(\"[WARNING] The following slots are open: \");\n        i = 0;\n        while ((entry = dictNext(iter)) != NULL) {\n            sds slot = (sds) dictGetKey(entry);\n            char *fmt = (i++ > 0 ? \",%S\" : \"%S\");\n            errstr = sdscatfmt(errstr, fmt, slot);\n        }\n        clusterManagerLogErr(\"%s.\\n\", (char *) errstr);\n        sdsfree(errstr);\n        if (do_fix) {\n            /* Fix open slots. */\n            dictReleaseIterator(iter);\n            iter = dictGetIterator(open_slots);\n            while ((entry = dictNext(iter)) != NULL) {\n                sds slot = (sds) dictGetKey(entry);\n                result = clusterManagerFixOpenSlot(atoi(slot));\n                if (!result) break;\n            }\n        }\n        dictReleaseIterator(iter);\n        dictRelease(open_slots);\n    }\n    clusterManagerLogInfo(\">>> Check slots coverage...\\n\");\n    char slots[CLUSTER_MANAGER_SLOTS];\n    memset(slots, 0, CLUSTER_MANAGER_SLOTS);\n    int coverage = clusterManagerGetCoveredSlots(slots);\n    if (coverage == CLUSTER_MANAGER_SLOTS) {\n        clusterManagerLogOk(\"[OK] All %d slots covered.\\n\",\n                            CLUSTER_MANAGER_SLOTS);\n    } else {\n        sds err = sdsempty();\n        err = sdscatprintf(err, \"[ERR] Not all %d slots are \"\n                                \"covered by nodes.\\n\",\n                                CLUSTER_MANAGER_SLOTS);\n        clusterManagerOnError(err);\n        result = 0;\n        if (do_fix/* && result*/) {\n            dictType dtype = clusterManagerDictType;\n            dtype.keyDestructor = dictSdsDestructor;\n            dtype.valDestructor = dictListDestructor;\n            clusterManagerUncoveredSlots = dictCreate(&dtype, NULL);\n            int fixed = clusterManagerFixSlotsCoverage(slots);\n            if (fixed > 0) result = 1;\n        }\n    }\n    int search_multiple_owners = config.cluster_manager_command.flags &\n                                 CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;\n    if (search_multiple_owners) {\n        /* Check whether there are multiple owners, even when slots are\n         * fully covered and there are no open slots. */\n        clusterManagerLogInfo(\">>> Check for multiple slot owners...\\n\");\n        int slot = 0, slots_with_multiple_owners = 0;\n        for (; slot < CLUSTER_MANAGER_SLOTS; slot++) {\n            listIter li;\n            listNode *ln;\n            listRewind(cluster_manager.nodes, &li);\n            list *owners = listCreate();\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n                if (n->slots[slot]) listAddNodeTail(owners, n);\n                else {\n                    /* Nodes having keys for the slot will be considered\n                     * owners too. */\n                    int count = clusterManagerCountKeysInSlot(n, slot);\n                    if (count > 0) listAddNodeTail(owners, n);\n                }\n            }\n            if (listLength(owners) > 1) {\n                result = 0;\n                clusterManagerLogErr(\"[WARNING] Slot %d has %d owners:\\n\",\n                                     slot, listLength(owners));\n                listRewind(owners, &li);\n                while ((ln = listNext(&li)) != NULL) {\n                    clusterManagerNode *n = ln->value;\n                    clusterManagerLogErr(\"    %s:%d\\n\", n->ip, n->port);\n                }\n                slots_with_multiple_owners++;\n                if (do_fix) {\n                    result = clusterManagerFixMultipleSlotOwners(slot, owners);\n                    if (!result) {\n                        clusterManagerLogErr(\"Failed to fix multiple owners \"\n                                             \"for slot %d\\n\", slot);\n                        listRelease(owners);\n                        break;\n                    } else slots_with_multiple_owners--;\n                }\n            }\n            listRelease(owners);\n        }\n        if (slots_with_multiple_owners == 0)\n            clusterManagerLogOk(\"[OK] No multiple owners found.\\n\");\n    }\n    return result;\n}\n\nstatic clusterManagerNode *clusterNodeForResharding(char *id,\n                                                    clusterManagerNode *target,\n                                                    int *raise_err)\n{\n    clusterManagerNode *node = NULL;\n    const char *invalid_node_msg = \"*** The specified node (%s) is not known \"\n                                   \"or not a master, please retry.\\n\";\n    node = clusterManagerNodeByName(id);\n    *raise_err = 0;\n    if (!node || node->flags & CLUSTER_MANAGER_FLAG_SLAVE) {\n        clusterManagerLogErr(invalid_node_msg, id);\n        *raise_err = 1;\n        return NULL;\n    } else if (node != NULL && target != NULL) {\n        if (!strcmp(node->name, target->name)) {\n            clusterManagerLogErr( \"*** It is not possible to use \"\n                                  \"the target node as \"\n                                  \"source node.\\n\");\n            return NULL;\n        }\n    }\n    return node;\n}\n\nstatic list *clusterManagerComputeReshardTable(list *sources, int numslots) {\n    list *moved = listCreate();\n    int src_count = listLength(sources), i = 0, tot_slots = 0, j;\n    clusterManagerNode **sorted = zmalloc(src_count * sizeof(*sorted));\n    listIter li;\n    listNode *ln;\n    listRewind(sources, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        tot_slots += node->slots_count;\n        sorted[i++] = node;\n    }\n    qsort(sorted, src_count, sizeof(clusterManagerNode *),\n          clusterManagerSlotCountCompareDesc);\n    for (i = 0; i < src_count; i++) {\n        clusterManagerNode *node = sorted[i];\n        float n = ((float) numslots / tot_slots * node->slots_count);\n        if (i == 0) n = ceil(n);\n        else n = floor(n);\n        int max = (int) n, count = 0;\n        for (j = 0; j < CLUSTER_MANAGER_SLOTS; j++) {\n            int slot = node->slots[j];\n            if (!slot) continue;\n            if (count >= max || (int)listLength(moved) >= numslots) break;\n            clusterManagerReshardTableItem *item = zmalloc(sizeof(*item));\n            item->source = node;\n            item->slot = j;\n            listAddNodeTail(moved, item);\n            count++;\n        }\n    }\n    zfree(sorted);\n    return moved;\n}\n\nstatic void clusterManagerShowReshardTable(list *table) {\n    listIter li;\n    listNode *ln;\n    listRewind(table, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerReshardTableItem *item = ln->value;\n        clusterManagerNode *n = item->source;\n        printf(\"    Moving slot %d from %s\\n\", item->slot, (char *) n->name);\n    }\n}\n\nstatic void clusterManagerReleaseReshardTable(list *table) {\n    if (table != NULL) {\n        listIter li;\n        listNode *ln;\n        listRewind(table, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerReshardTableItem *item = ln->value;\n            zfree(item);\n        }\n        listRelease(table);\n    }\n}\n\nstatic void clusterManagerLog(int level, const char* fmt, ...) {\n    int use_colors =\n        (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COLOR);\n    if (use_colors) {\n        printf(\"\\033[\");\n        switch (level) {\n        case CLUSTER_MANAGER_LOG_LVL_INFO: printf(LOG_COLOR_BOLD); break;\n        case CLUSTER_MANAGER_LOG_LVL_WARN: printf(LOG_COLOR_YELLOW); break;\n        case CLUSTER_MANAGER_LOG_LVL_ERR: printf(LOG_COLOR_RED); break;\n        case CLUSTER_MANAGER_LOG_LVL_SUCCESS: printf(LOG_COLOR_GREEN); break;\n        default: printf(LOG_COLOR_RESET); break;\n        }\n    }\n    va_list ap;\n    va_start(ap, fmt);\n    vprintf(fmt, ap);\n    va_end(ap);\n    if (use_colors) printf(\"\\033[\" LOG_COLOR_RESET);\n}\n\nstatic void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,\n                                        int alloc_len)\n{\n    array->nodes = zcalloc(alloc_len * sizeof(clusterManagerNode*));\n    array->alloc = array->nodes;\n    array->len = alloc_len;\n    array->count = 0;\n}\n\n/* Reset array->nodes to the original array allocation and re-count non-NULL\n * nodes. */\nstatic void clusterManagerNodeArrayReset(clusterManagerNodeArray *array) {\n    if (array->nodes > array->alloc) {\n        array->len = array->nodes - array->alloc;\n        array->nodes = array->alloc;\n        array->count = 0;\n        int i = 0;\n        for(; i < array->len; i++) {\n            if (array->nodes[i] != NULL) array->count++;\n        }\n    }\n}\n\n/* Shift array->nodes and store the shifted node into 'nodeptr'. */\nstatic void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,\n                                         clusterManagerNode **nodeptr)\n{\n    assert(array->nodes < (array->nodes + array->len));\n    /* If the first node to be shifted is not NULL, decrement count. */\n    if (*array->nodes != NULL) array->count--;\n    /* Store the first node to be shifted into 'nodeptr'. */\n    *nodeptr = *array->nodes;\n    /* Shift the nodes array and decrement length. */\n    array->nodes++;\n    array->len--;\n}\n\nstatic void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,\n                                       clusterManagerNode *node)\n{\n    assert(array->nodes < (array->nodes + array->len));\n    assert(node != NULL);\n    assert(array->count < array->len);\n    array->nodes[array->count++] = node;\n}\n\nstatic void clusterManagerPrintNotEmptyNodeError(clusterManagerNode *node,\n                                                 char *err)\n{\n    char *msg;\n    if (err) msg = err;\n    else {\n        msg = \"is not empty. Either the node already knows other \"\n              \"nodes (check with CLUSTER NODES) or contains some \"\n              \"key in database 0.\";\n    }\n    clusterManagerLogErr(\"[ERR] Node %s:%d %s\\n\", node->ip, node->port, msg);\n}\n\nstatic void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,\n                                                   char *err)\n{\n    char *msg = (err ? err : \"is not configured as a cluster node.\");\n    clusterManagerLogErr(\"[ERR] Node %s:%d %s\\n\", node->ip, node->port, msg);\n}\n\n/* Execute redis-cli in Cluster Manager mode */\nstatic void clusterManagerMode(clusterManagerCommandProc *proc) {\n    int argc = config.cluster_manager_command.argc;\n    char **argv = config.cluster_manager_command.argv;\n    cluster_manager.nodes = NULL;\n    if (!proc(argc, argv)) goto cluster_manager_err;\n    freeClusterManager();\n    exit(0);\ncluster_manager_err:\n    freeClusterManager();\n    sdsfree(config.hostip);\n    sdsfree(config.mb_delim);\n    exit(1);\n}\n\n/* Cluster Manager Commands */\n\nstatic int clusterManagerCommandCreate(int argc, char **argv) {\n    int i, j, success = 1;\n    cluster_manager.nodes = listCreate();\n    for (i = 0; i < argc; i++) {\n        char *addr = argv[i];\n        char *c = strrchr(addr, '@');\n        if (c != NULL) *c = '\\0';\n        c = strrchr(addr, ':');\n        if (c == NULL) {\n            fprintf(stderr, \"Invalid address format: %s\\n\", addr);\n            return 0;\n        }\n        *c = '\\0';\n        char *ip = addr;\n        int port = atoi(++c);\n        clusterManagerNode *node = clusterManagerNewNode(ip, port);\n        if (!clusterManagerNodeConnect(node)) {\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        char *err = NULL;\n        if (!clusterManagerNodeIsCluster(node, &err)) {\n            clusterManagerPrintNotClusterNodeError(node, err);\n            if (err) zfree(err);\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        err = NULL;\n        if (!clusterManagerNodeLoadInfo(node, 0, &err)) {\n            if (err) {\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                zfree(err);\n            }\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        err = NULL;\n        if (!clusterManagerNodeIsEmpty(node, &err)) {\n            clusterManagerPrintNotEmptyNodeError(node, err);\n            if (err) zfree(err);\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        listAddNodeTail(cluster_manager.nodes, node);\n    }\n    int node_len = cluster_manager.nodes->len;\n    int replicas = config.cluster_manager_command.replicas;\n    int masters_count = CLUSTER_MANAGER_MASTERS_COUNT(node_len, replicas);\n    if (masters_count < 3) {\n        clusterManagerLogErr(\n            \"*** ERROR: Invalid configuration for cluster creation.\\n\"\n            \"*** Redis Cluster requires at least 3 master nodes.\\n\"\n            \"*** This is not possible with %d nodes and %d replicas per node.\",\n            node_len, replicas);\n        clusterManagerLogErr(\"\\n*** At least %d nodes are required.\\n\",\n                             3 * (replicas + 1));\n        return 0;\n    }\n    clusterManagerLogInfo(\">>> Performing hash slots allocation \"\n                          \"on %d nodes...\\n\", node_len);\n    int interleaved_len = 0, ip_count = 0;\n    clusterManagerNode **interleaved = zcalloc(node_len*sizeof(**interleaved));\n    char **ips = zcalloc(node_len * sizeof(char*));\n    clusterManagerNodeArray *ip_nodes = zcalloc(node_len * sizeof(*ip_nodes));\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        int found = 0;\n        for (i = 0; i < ip_count; i++) {\n            char *ip = ips[i];\n            if (!strcmp(ip, n->ip)) {\n                found = 1;\n                break;\n            }\n        }\n        if (!found) {\n            ips[ip_count++] = n->ip;\n        }\n        clusterManagerNodeArray *node_array = &(ip_nodes[i]);\n        if (node_array->nodes == NULL)\n            clusterManagerNodeArrayInit(node_array, node_len);\n        clusterManagerNodeArrayAdd(node_array, n);\n    }\n    while (interleaved_len < node_len) {\n        for (i = 0; i < ip_count; i++) {\n            clusterManagerNodeArray *node_array = &(ip_nodes[i]);\n            if (node_array->count > 0) {\n                clusterManagerNode *n = NULL;\n                clusterManagerNodeArrayShift(node_array, &n);\n                interleaved[interleaved_len++] = n;\n            }\n        }\n    }\n    clusterManagerNode **masters = interleaved;\n    interleaved += masters_count;\n    interleaved_len -= masters_count;\n    float slots_per_node = CLUSTER_MANAGER_SLOTS / (float) masters_count;\n    long first = 0;\n    float cursor = 0.0f;\n    for (i = 0; i < masters_count; i++) {\n        clusterManagerNode *master = masters[i];\n        long last = lround(cursor + slots_per_node - 1);\n        if (last > CLUSTER_MANAGER_SLOTS || i == (masters_count - 1))\n            last = CLUSTER_MANAGER_SLOTS - 1;\n        if (last < first) last = first;\n        printf(\"Master[%d] -> Slots %lu - %lu\\n\", i, first, last);\n        master->slots_count = 0;\n        for (j = first; j <= last; j++) {\n            master->slots[j] = 1;\n            master->slots_count++;\n        }\n        master->dirty = 1;\n        first = last + 1;\n        cursor += slots_per_node;\n    }\n\n    /* Rotating the list sometimes helps to get better initial\n     * anti-affinity before the optimizer runs. */\n    clusterManagerNode *first_node = interleaved[0];\n    for (i = 0; i < (interleaved_len - 1); i++)\n        interleaved[i] = interleaved[i + 1];\n    interleaved[interleaved_len - 1] = first_node;\n    int assign_unused = 0, available_count = interleaved_len;\nassign_replicas:\n    for (i = 0; i < masters_count; i++) {\n        clusterManagerNode *master = masters[i];\n        int assigned_replicas = 0;\n        while (assigned_replicas < replicas) {\n            if (available_count == 0) break;\n            clusterManagerNode *found = NULL, *slave = NULL;\n            int firstNodeIdx = -1;\n            for (j = 0; j < interleaved_len; j++) {\n                clusterManagerNode *n = interleaved[j];\n                if (n == NULL) continue;\n                if (strcmp(n->ip, master->ip)) {\n                    found = n;\n                    interleaved[j] = NULL;\n                    break;\n                }\n                if (firstNodeIdx < 0) firstNodeIdx = j;\n            }\n            if (found) slave = found;\n            else if (firstNodeIdx >= 0) {\n                slave = interleaved[firstNodeIdx];\n                interleaved_len -= (interleaved - (interleaved + firstNodeIdx));\n                interleaved += (firstNodeIdx + 1);\n            }\n            if (slave != NULL) {\n                assigned_replicas++;\n                available_count--;\n                if (slave->replicate) sdsfree(slave->replicate);\n                slave->replicate = sdsnew(master->name);\n                slave->dirty = 1;\n            } else break;\n            printf(\"Adding replica %s:%d to %s:%d\\n\", slave->ip, slave->port,\n                   master->ip, master->port);\n            if (assign_unused) break;\n        }\n    }\n    if (!assign_unused && available_count > 0) {\n        assign_unused = 1;\n        printf(\"Adding extra replicas...\\n\");\n        goto assign_replicas;\n    }\n    for (i = 0; i < ip_count; i++) {\n        clusterManagerNodeArray *node_array = ip_nodes + i;\n        clusterManagerNodeArrayReset(node_array);\n    }\n    clusterManagerOptimizeAntiAffinity(ip_nodes, ip_count);\n    clusterManagerShowNodes();\n    if (confirmWithYes(\"Can I set the above configuration?\")) {\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            char *err = NULL;\n            int flushed = clusterManagerFlushNodeConfig(node, &err);\n            if (!flushed && node->dirty && !node->replicate) {\n                if (err != NULL) {\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                    zfree(err);\n                }\n                success = 0;\n                goto cleanup;\n            } else if (err != NULL) zfree(err);\n        }\n        clusterManagerLogInfo(\">>> Nodes configuration updated\\n\");\n        clusterManagerLogInfo(\">>> Assign a different config epoch to \"\n                              \"each node\\n\");\n        int config_epoch = 1;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            redisReply *reply = NULL;\n            reply = CLUSTER_MANAGER_COMMAND(node,\n                                            \"cluster set-config-epoch %d\",\n                                            config_epoch++);\n            if (reply != NULL) freeReplyObject(reply);\n        }\n        clusterManagerLogInfo(\">>> Sending CLUSTER MEET messages to join \"\n                              \"the cluster\\n\");\n        clusterManagerNode *first = NULL;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            if (first == NULL) {\n                first = node;\n                continue;\n            }\n            redisReply *reply = NULL;\n            reply = CLUSTER_MANAGER_COMMAND(node, \"cluster meet %s %d\",\n                                            first->ip, first->port);\n            int is_err = 0;\n            if (reply != NULL) {\n                if ((is_err = reply->type == REDIS_REPLY_ERROR))\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, reply->str);\n                freeReplyObject(reply);\n            } else {\n                is_err = 1;\n                fprintf(stderr, \"Failed to send CLUSTER MEET command.\\n\");\n            }\n            if (is_err) {\n                success = 0;\n                goto cleanup;\n            }\n        }\n        /* Give one second for the join to start, in order to avoid that\n         * waiting for cluster join will find all the nodes agree about\n         * the config as they are still empty with unassigned slots. */\n        sleep(1);\n        clusterManagerWaitForClusterJoin();\n        /* Useful for the replicas */\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            if (!node->dirty) continue;\n            char *err = NULL;\n            int flushed = clusterManagerFlushNodeConfig(node, &err);\n            if (!flushed && !node->replicate) {\n                if (err != NULL) {\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                    zfree(err);\n                }\n                success = 0;\n                goto cleanup;\n            }\n        }\n        // Reset Nodes\n        listRewind(cluster_manager.nodes, &li);\n        clusterManagerNode *first_node = NULL;\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            if (!first_node) first_node = node;\n            else freeClusterManagerNode(node);\n        }\n        listEmpty(cluster_manager.nodes);\n        if (!clusterManagerLoadInfoFromNode(first_node, 0)) {\n            success = 0;\n            goto cleanup;\n        }\n        clusterManagerCheckCluster(0);\n    }\ncleanup:\n    /* Free everything */\n    zfree(masters);\n    zfree(ips);\n    for (i = 0; i < node_len; i++) {\n        clusterManagerNodeArray *node_array = ip_nodes + i;\n        CLUSTER_MANAGER_NODE_ARRAY_FREE(node_array);\n    }\n    zfree(ip_nodes);\n    return success;\n}\n\nstatic int clusterManagerCommandAddNode(int argc, char **argv) {\n    int success = 1;\n    redisReply *reply = NULL;\n    char *ref_ip = NULL, *ip = NULL;\n    int ref_port = 0, port = 0;\n    if (!getClusterHostFromCmdArgs(argc - 1, argv + 1, &ref_ip, &ref_port))\n        goto invalid_args;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port))\n        goto invalid_args;\n    clusterManagerLogInfo(\">>> Adding node %s:%d to cluster %s:%d\\n\", ip, port,\n                          ref_ip, ref_port);\n    // Check the existing cluster\n    clusterManagerNode *refnode = clusterManagerNewNode(ref_ip, ref_port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    if (!clusterManagerCheckCluster(0)) return 0;\n\n    /* If --cluster-master-id was specified, try to resolve it now so that we\n     * abort before starting with the node configuration. */\n    clusterManagerNode *master_node = NULL;\n    if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVE) {\n        char *master_id = config.cluster_manager_command.master_id;\n        if (master_id != NULL) {\n            master_node = clusterManagerNodeByName(master_id);\n            if (master_node == NULL) {\n                clusterManagerLogErr(\"[ERR] No such master ID %s\\n\", master_id);\n                return 0;\n            }\n        } else {\n            master_node = clusterManagerNodeWithLeastReplicas();\n            assert(master_node != NULL);\n            printf(\"Automatically selected master %s:%d\\n\", master_node->ip,\n                   master_node->port);\n        }\n    }\n\n    // Add the new node\n    clusterManagerNode *new_node = clusterManagerNewNode(ip, port);\n    int added = 0;\n    if (!clusterManagerNodeConnect(new_node)) {\n        clusterManagerLogErr(\"[ERR] Sorry, can't connect to node %s:%d\\n\",\n                             ip, port);\n        success = 0;\n        goto cleanup;\n    }\n    char *err = NULL;\n    if (!(success = clusterManagerNodeIsCluster(new_node, &err))) {\n        clusterManagerPrintNotClusterNodeError(new_node, err);\n        if (err) zfree(err);\n        goto cleanup;\n    }\n    if (!clusterManagerNodeLoadInfo(new_node, 0, &err)) {\n        if (err) {\n            CLUSTER_MANAGER_PRINT_REPLY_ERROR(new_node, err);\n            zfree(err);\n        }\n        success = 0;\n        goto cleanup;\n    }\n    if (!(success = clusterManagerNodeIsEmpty(new_node, &err))) {\n        clusterManagerPrintNotEmptyNodeError(new_node, err);\n        if (err) zfree(err);\n        goto cleanup;\n    }\n    clusterManagerNode *first = listFirst(cluster_manager.nodes)->value;\n    listAddNodeTail(cluster_manager.nodes, new_node);\n    added = 1;\n\n    // Send CLUSTER MEET command to the new node\n    clusterManagerLogInfo(\">>> Send CLUSTER MEET to node %s:%d to make it \"\n                          \"join the cluster.\\n\", ip, port);\n    reply = CLUSTER_MANAGER_COMMAND(new_node, \"CLUSTER MEET %s %d\",\n                                    first->ip, first->port);\n    if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))\n        goto cleanup;\n\n    /* Additional configuration is needed if the node is added as a slave. */\n    if (master_node) {\n        sleep(1);\n        clusterManagerWaitForClusterJoin();\n        clusterManagerLogInfo(\">>> Configure node as replica of %s:%d.\\n\",\n                              master_node->ip, master_node->port);\n        freeReplyObject(reply);\n        reply = CLUSTER_MANAGER_COMMAND(new_node, \"CLUSTER REPLICATE %s\",\n                                        master_node->name);\n        if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))\n            goto cleanup;\n    }\n    clusterManagerLogOk(\"[OK] New node added correctly.\\n\");\ncleanup:\n    if (!added && new_node) freeClusterManagerNode(new_node);\n    if (reply) freeReplyObject(reply);\n    return success;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandDeleteNode(int argc, char **argv) {\n    UNUSED(argc);\n    int success = 1;\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    char *node_id = argv[1];\n    clusterManagerLogInfo(\">>> Removing node %s from cluster %s:%d\\n\",\n                          node_id, ip, port);\n    clusterManagerNode *ref_node = clusterManagerNewNode(ip, port);\n    clusterManagerNode *node = NULL;\n\n    // Load cluster information\n    if (!clusterManagerLoadInfoFromNode(ref_node, 0)) return 0;\n\n    // Check if the node exists and is not empty\n    node = clusterManagerNodeByName(node_id);\n    if (node == NULL) {\n        clusterManagerLogErr(\"[ERR] No such node ID %s\\n\", node_id);\n        return 0;\n    }\n    if (node->slots_count != 0) {\n        clusterManagerLogErr(\"[ERR] Node %s:%d is not empty! Reshard data \"\n                             \"away and try again.\\n\", node->ip, node->port);\n        return 0;\n    }\n\n    // Send CLUSTER FORGET to all the nodes but the node to remove\n    clusterManagerLogInfo(\">>> Sending CLUSTER FORGET messages to the \"\n                          \"cluster...\\n\");\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n == node) continue;\n        if (n->replicate && !strcasecmp(n->replicate, node_id)) {\n            // Reconfigure the slave to replicate with some other node\n            clusterManagerNode *master = clusterManagerNodeWithLeastReplicas();\n            assert(master != NULL);\n            clusterManagerLogInfo(\">>> %s:%d as replica of %s:%d\\n\",\n                                  n->ip, n->port, master->ip, master->port);\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER REPLICATE %s\",\n                                                    master->name);\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (r) freeReplyObject(r);\n            if (!success) return 0;\n        }\n        redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER FORGET %s\",\n                                                node_id);\n        success = clusterManagerCheckRedisReply(n, r, NULL);\n        if (r) freeReplyObject(r);\n        if (!success) return 0;\n    }\n\n    /* Finally send CLUSTER RESET to the node. */\n    clusterManagerLogInfo(\">>> Sending CLUSTER RESET SOFT to the \"\n                          \"deleted node.\\n\");\n    redisReply *r = redisCommand(node->context, \"CLUSTER RESET %s\", \"SOFT\");\n    success = clusterManagerCheckRedisReply(node, r, NULL);\n    if (r) freeReplyObject(r);\n    return success;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandInfo(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    clusterManagerShowClusterInfo();\n    return 1;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandCheck(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    clusterManagerShowClusterInfo();\n    return clusterManagerCheckCluster(0);\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandFix(int argc, char **argv) {\n    config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_FIX;\n    return clusterManagerCommandCheck(argc, argv);\n}\n\nstatic int clusterManagerCommandReshard(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    clusterManagerCheckCluster(0);\n    if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {\n        fflush(stdout);\n        fprintf(stderr,\n                \"*** Please fix your cluster problems before resharding\\n\");\n        return 0;\n    }\n    int slots = config.cluster_manager_command.slots;\n    if (!slots) {\n        while (slots <= 0 || slots > CLUSTER_MANAGER_SLOTS) {\n            printf(\"How many slots do you want to move (from 1 to %d)? \",\n                   CLUSTER_MANAGER_SLOTS);\n            fflush(stdout);\n            char buf[6];\n            int nread = read(fileno(stdin),buf,6);\n            if (nread <= 0) continue;\n            int last_idx = nread - 1;\n            if (buf[last_idx] != '\\n') {\n                int ch;\n                while ((ch = getchar()) != '\\n' && ch != EOF) {}\n            }\n            buf[last_idx] = '\\0';\n            slots = atoi(buf);\n        }\n    }\n    char buf[255];\n    char *to = config.cluster_manager_command.to,\n         *from = config.cluster_manager_command.from;\n    while (to == NULL) {\n        printf(\"What is the receiving node ID? \");\n        fflush(stdout);\n        int nread = read(fileno(stdin),buf,255);\n        if (nread <= 0) continue;\n        int last_idx = nread - 1;\n        if (buf[last_idx] != '\\n') {\n            int ch;\n            while ((ch = getchar()) != '\\n' && ch != EOF) {}\n        }\n        buf[last_idx] = '\\0';\n        if (strlen(buf) > 0) to = buf;\n    }\n    int raise_err = 0;\n    clusterManagerNode *target = clusterNodeForResharding(to, NULL, &raise_err);\n    if (target == NULL) return 0;\n    list *sources = listCreate();\n    list *table = NULL;\n    int all = 0, result = 1;\n    if (from == NULL) {\n        printf(\"Please enter all the source node IDs.\\n\");\n        printf(\"  Type 'all' to use all the nodes as source nodes for \"\n               \"the hash slots.\\n\");\n        printf(\"  Type 'done' once you entered all the source nodes IDs.\\n\");\n        while (1) {\n            printf(\"Source node #%lu: \", listLength(sources) + 1);\n            fflush(stdout);\n            int nread = read(fileno(stdin),buf,255);\n            if (nread <= 0) continue;\n            int last_idx = nread - 1;\n            if (buf[last_idx] != '\\n') {\n                int ch;\n                while ((ch = getchar()) != '\\n' && ch != EOF) {}\n            }\n            buf[last_idx] = '\\0';\n            if (!strcmp(buf, \"done\")) break;\n            else if (!strcmp(buf, \"all\")) {\n                all = 1;\n                break;\n            } else {\n                clusterManagerNode *src =\n                    clusterNodeForResharding(buf, target, &raise_err);\n                if (src != NULL) listAddNodeTail(sources, src);\n                else if (raise_err) {\n                    result = 0;\n                    goto cleanup;\n                }\n            }\n        }\n    } else {\n        char *p;\n        while((p = strchr(from, ',')) != NULL) {\n            *p = '\\0';\n            if (!strcmp(from, \"all\")) {\n                all = 1;\n                break;\n            } else {\n                clusterManagerNode *src =\n                    clusterNodeForResharding(from, target, &raise_err);\n                if (src != NULL) listAddNodeTail(sources, src);\n                else if (raise_err) {\n                    result = 0;\n                    goto cleanup;\n                }\n            }\n            from = p + 1;\n        }\n        /* Check if there's still another source to process. */\n        if (!all && strlen(from) > 0) {\n            if (!strcmp(from, \"all\")) all = 1;\n            if (!all) {\n                clusterManagerNode *src =\n                    clusterNodeForResharding(from, target, &raise_err);\n                if (src != NULL) listAddNodeTail(sources, src);\n                else if (raise_err) {\n                    result = 0;\n                    goto cleanup;\n                }\n            }\n        }\n    }\n    listIter li;\n    listNode *ln;\n    if (all) {\n        listEmpty(sources);\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n                continue;\n            if (!sdscmp(n->name, target->name)) continue;\n            listAddNodeTail(sources, n);\n        }\n    }\n    if (listLength(sources) == 0) {\n        fprintf(stderr, \"*** No source nodes given, operation aborted.\\n\");\n        result = 0;\n        goto cleanup;\n    }\n    printf(\"\\nReady to move %d slots.\\n\", slots);\n    printf(\"  Source nodes:\\n\");\n    listRewind(sources, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *src = ln->value;\n        sds info = clusterManagerNodeInfo(src, 4);\n        printf(\"%s\\n\", info);\n        sdsfree(info);\n    }\n    printf(\"  Destination node:\\n\");\n    sds info = clusterManagerNodeInfo(target, 4);\n    printf(\"%s\\n\", info);\n    sdsfree(info);\n    table = clusterManagerComputeReshardTable(sources, slots);\n    printf(\"  Resharding plan:\\n\");\n    clusterManagerShowReshardTable(table);\n    if (!(config.cluster_manager_command.flags &\n          CLUSTER_MANAGER_CMD_FLAG_YES))\n    {\n        printf(\"Do you want to proceed with the proposed \"\n               \"reshard plan (yes/no)? \");\n        fflush(stdout);\n        char buf[4];\n        int nread = read(fileno(stdin),buf,4);\n        buf[3] = '\\0';\n        if (nread <= 0 || strcmp(\"yes\", buf) != 0) {\n            result = 0;\n            goto cleanup;\n        }\n    }\n    int opts = CLUSTER_MANAGER_OPT_VERBOSE;\n    listRewind(table, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerReshardTableItem *item = ln->value;\n        char *err = NULL;\n        result = clusterManagerMoveSlot(item->source, target, item->slot,\n                                        opts, &err);\n        if (!result) {\n            if (err != NULL) {\n                //clusterManagerLogErr(\"\\n%s\\n\", err);\n                zfree(err);\n            }\n            goto cleanup;\n        }\n    }\ncleanup:\n    listRelease(sources);\n    clusterManagerReleaseReshardTable(table);\n    return result;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandRebalance(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    clusterManagerNode **weightedNodes = NULL;\n    list *involved = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    int result = 1, i;\n    if (config.cluster_manager_command.weight != NULL) {\n        for (i = 0; i < config.cluster_manager_command.weight_argc; i++) {\n            char *name = config.cluster_manager_command.weight[i];\n            char *p = strchr(name, '=');\n            if (p == NULL) {\n                result = 0;\n                goto cleanup;\n            }\n            *p = '\\0';\n            float w = atof(++p);\n            clusterManagerNode *n = clusterManagerNodeByAbbreviatedName(name);\n            if (n == NULL) {\n                clusterManagerLogErr(\"*** No such master node %s\\n\", name);\n                result = 0;\n                goto cleanup;\n            }\n            n->weight = w;\n        }\n    }\n    float total_weight = 0;\n    int nodes_involved = 0;\n    int use_empty = config.cluster_manager_command.flags &\n                    CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;\n    involved = listCreate();\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    /* Compute the total cluster weight. */\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n            continue;\n        if (!use_empty && n->slots_count == 0) {\n            n->weight = 0;\n            continue;\n        }\n        total_weight += n->weight;\n        nodes_involved++;\n        listAddNodeTail(involved, n);\n    }\n    weightedNodes = zmalloc(nodes_involved * sizeof(clusterManagerNode *));\n    if (weightedNodes == NULL) goto cleanup;\n    /* Check cluster, only proceed if it looks sane. */\n    clusterManagerCheckCluster(1);\n    if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {\n        clusterManagerLogErr(\"*** Please fix your cluster problems \"\n                             \"before rebalancing\\n\");\n        result = 0;\n        goto cleanup;\n    }\n    /* Calculate the slots balance for each node. It's the number of\n     * slots the node should lose (if positive) or gain (if negative)\n     * in order to be balanced. */\n    int threshold_reached = 0, total_balance = 0;\n    float threshold = config.cluster_manager_command.threshold;\n    i = 0;\n    listRewind(involved, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        weightedNodes[i++] = n;\n        int expected = (int) (((float)CLUSTER_MANAGER_SLOTS / total_weight) *\n                        n->weight);\n        n->balance = n->slots_count - expected;\n        total_balance += n->balance;\n        /* Compute the percentage of difference between the\n         * expected number of slots and the real one, to see\n         * if it's over the threshold specified by the user. */\n        int over_threshold = 0;\n        if (threshold > 0) {\n            if (n->slots_count > 0) {\n                float err_perc = fabs((100-(100.0*expected/n->slots_count)));\n                if (err_perc > threshold) over_threshold = 1;\n            } else if (expected > 1) {\n                over_threshold = 1;\n            }\n        }\n        if (over_threshold) threshold_reached = 1;\n    }\n    if (!threshold_reached) {\n        clusterManagerLogWarn(\"*** No rebalancing needed! \"\n                             \"All nodes are within the %.2f%% threshold.\\n\",\n                             config.cluster_manager_command.threshold);\n        goto cleanup;\n    }\n    /* Because of rounding, it is possible that the balance of all nodes\n     * summed does not give 0. Make sure that nodes that have to provide\n     * slots are always matched by nodes receiving slots. */\n    while (total_balance > 0) {\n        listRewind(involved, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->balance <= 0 && total_balance > 0) {\n                n->balance--;\n                total_balance--;\n            }\n        }\n    }\n    /* Sort nodes by their slots balance. */\n    qsort(weightedNodes, nodes_involved, sizeof(clusterManagerNode *),\n          clusterManagerCompareNodeBalance);\n    clusterManagerLogInfo(\">>> Rebalancing across %d nodes. \"\n                          \"Total weight = %.2f\\n\",\n                          nodes_involved, total_weight);\n    if (config.verbose) {\n        for (i = 0; i < nodes_involved; i++) {\n            clusterManagerNode *n = weightedNodes[i];\n            printf(\"%s:%d balance is %d slots\\n\", n->ip, n->port, n->balance);\n        }\n    }\n    /* Now we have at the start of the 'sn' array nodes that should get\n     * slots, at the end nodes that must give slots.\n     * We take two indexes, one at the start, and one at the end,\n     * incrementing or decrementing the indexes accordingly til we\n     * find nodes that need to get/provide slots. */\n    int dst_idx = 0;\n    int src_idx = nodes_involved - 1;\n    int simulate = config.cluster_manager_command.flags &\n                   CLUSTER_MANAGER_CMD_FLAG_SIMULATE;\n    while (dst_idx < src_idx) {\n        clusterManagerNode *dst = weightedNodes[dst_idx];\n        clusterManagerNode *src = weightedNodes[src_idx];\n        int db = abs(dst->balance);\n        int sb = abs(src->balance);\n        int numslots = (db < sb ? db : sb);\n        if (numslots > 0) {\n            printf(\"Moving %d slots from %s:%d to %s:%d\\n\", numslots,\n                                                            src->ip,\n                                                            src->port,\n                                                            dst->ip,\n                                                            dst->port);\n            /* Actually move the slots. */\n            list *lsrc = listCreate(), *table = NULL;\n            listAddNodeTail(lsrc, src);\n            table = clusterManagerComputeReshardTable(lsrc, numslots);\n            listRelease(lsrc);\n            int table_len = (int) listLength(table);\n            if (!table || table_len != numslots) {\n                clusterManagerLogErr(\"*** Assertion failed: Reshard table \"\n                                     \"!= number of slots\");\n                result = 0;\n                goto end_move;\n            }\n            if (simulate) {\n                for (i = 0; i < table_len; i++) printf(\"#\");\n            } else {\n                int opts = CLUSTER_MANAGER_OPT_QUIET |\n                           CLUSTER_MANAGER_OPT_UPDATE;\n                listRewind(table, &li);\n                while ((ln = listNext(&li)) != NULL) {\n                    clusterManagerReshardTableItem *item = ln->value;\n                    result = clusterManagerMoveSlot(item->source,\n                                                    dst,\n                                                    item->slot,\n                                                    opts, NULL);\n                    if (!result) goto end_move;\n                    printf(\"#\");\n                    fflush(stdout);\n                }\n\n            }\n            printf(\"\\n\");\nend_move:\n            clusterManagerReleaseReshardTable(table);\n            if (!result) goto cleanup;\n        }\n        /* Update nodes balance. */\n        dst->balance += numslots;\n        src->balance -= numslots;\n        if (dst->balance == 0) dst_idx++;\n        if (src->balance == 0) src_idx --;\n    }\ncleanup:\n    if (involved != NULL) listRelease(involved);\n    if (weightedNodes != NULL) zfree(weightedNodes);\n    return result;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandSetTimeout(int argc, char **argv) {\n    UNUSED(argc);\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    int timeout = atoi(argv[1]);\n    if (timeout < 100) {\n        fprintf(stderr, \"Setting a node timeout of less than 100 \"\n                \"milliseconds is a bad idea.\\n\");\n        return 0;\n    }\n    // Load cluster information\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    int ok_count = 0, err_count = 0;\n\n    clusterManagerLogInfo(\">>> Reconfiguring node timeout in every \"\n                          \"cluster node...\\n\");\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        char *err = NULL;\n        redisReply *reply = CLUSTER_MANAGER_COMMAND(n, \"CONFIG %s %s %d\",\n                                                    \"SET\",\n                                                    \"cluster-node-timeout\",\n                                                    timeout);\n        if (reply == NULL) goto reply_err;\n        int ok = clusterManagerCheckRedisReply(n, reply, &err);\n        freeReplyObject(reply);\n        if (!ok) goto reply_err;\n        reply = CLUSTER_MANAGER_COMMAND(n, \"CONFIG %s\", \"REWRITE\");\n        if (reply == NULL) goto reply_err;\n        ok = clusterManagerCheckRedisReply(n, reply, &err);\n        freeReplyObject(reply);\n        if (!ok) goto reply_err;\n        clusterManagerLogWarn(\"*** New timeout set for %s:%d\\n\", n->ip,\n                              n->port);\n        ok_count++;\n        continue;\nreply_err:;\n        int need_free = 0;\n        if (err == NULL) err = \"\";\n        else need_free = 1;\n        clusterManagerLogErr(\"ERR setting node-timeot for %s:%d: %s\\n\", n->ip,\n                             n->port, err);\n        if (need_free) zfree(err);\n        err_count++;\n    }\n    clusterManagerLogInfo(\">>> New node timeout set. %d OK, %d ERR.\\n\",\n                          ok_count, err_count);\n    return 1;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandImport(int argc, char **argv) {\n    int success = 1;\n    int port = 0, src_port = 0;\n    char *ip = NULL, *src_ip = NULL;\n    char *invalid_args_msg = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) {\n        invalid_args_msg = CLUSTER_MANAGER_INVALID_HOST_ARG;\n        goto invalid_args;\n    }\n    if (config.cluster_manager_command.from == NULL) {\n        invalid_args_msg = \"[ERR] Option '--cluster-from' is required for \"\n                           \"subcommand 'import'.\\n\";\n        goto invalid_args;\n    }\n    char *src_host[] = {config.cluster_manager_command.from};\n    if (!getClusterHostFromCmdArgs(1, src_host, &src_ip, &src_port)) {\n        invalid_args_msg = \"[ERR] Invalid --cluster-from host. You need to \"\n                           \"pass a valid address (ie. 120.0.0.1:7000).\\n\";\n        goto invalid_args;\n    }\n    clusterManagerLogInfo(\">>> Importing data from %s:%d to cluster %s:%d\\n\",\n                          src_ip, src_port, ip, port);\n\n    clusterManagerNode *refnode = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    if (!clusterManagerCheckCluster(0)) return 0;\n    char *reply_err = NULL;\n    redisReply *src_reply = NULL;\n    // Connect to the source node.\n    redisContext *src_ctx = redisConnect(src_ip, src_port);\n    if (src_ctx->err) {\n        success = 0;\n        fprintf(stderr,\"Could not connect to Redis at %s:%d: %s.\\n\", src_ip,\n                src_port, src_ctx->errstr);\n        goto cleanup;\n    }\n    src_reply = reconnectingRedisCommand(src_ctx, \"INFO\");\n    if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {\n        if (src_reply && src_reply->str) reply_err = src_reply->str;\n        success = 0;\n        goto cleanup;\n    }\n    if (getLongInfoField(src_reply->str, \"cluster_enabled\")) {\n        clusterManagerLogErr(\"[ERR] The source node should not be a \"\n                             \"cluster node.\\n\");\n        success = 0;\n        goto cleanup;\n    }\n    freeReplyObject(src_reply);\n    src_reply = reconnectingRedisCommand(src_ctx, \"DBSIZE\");\n    if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {\n        if (src_reply && src_reply->str) reply_err = src_reply->str;\n        success = 0;\n        goto cleanup;\n    }\n    int size = src_reply->integer, i;\n    clusterManagerLogWarn(\"*** Importing %d keys from DB 0\\n\", size);\n\n    // Build a slot -> node map\n    clusterManagerNode  *slots_map[CLUSTER_MANAGER_SLOTS];\n    memset(slots_map, 0, sizeof(slots_map));\n    listIter li;\n    listNode *ln;\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n            if (n->slots_count == 0) continue;\n            if (n->slots[i]) {\n                slots_map[i] = n;\n                break;\n            }\n        }\n    }\n\n    char cmdfmt[50] = \"MIGRATE %s %d %s %d %d\";\n    if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COPY)\n        strcat(cmdfmt, \" %s\");\n    if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_REPLACE)\n        strcat(cmdfmt, \" %s\");\n\n    /* Use SCAN to iterate over the keys, migrating to the\n     * right node as needed. */\n    int cursor = -999, timeout = config.cluster_manager_command.timeout;\n    while (cursor != 0) {\n        if (cursor < 0) cursor = 0;\n        freeReplyObject(src_reply);\n        src_reply = reconnectingRedisCommand(src_ctx, \"SCAN %d COUNT %d\",\n                                             cursor, 1000);\n        if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {\n            if (src_reply && src_reply->str) reply_err = src_reply->str;\n            success = 0;\n            goto cleanup;\n        }\n        assert(src_reply->type == REDIS_REPLY_ARRAY);\n        assert(src_reply->elements >= 2);\n        assert(src_reply->element[1]->type == REDIS_REPLY_ARRAY);\n        if (src_reply->element[0]->type == REDIS_REPLY_STRING)\n            cursor = atoi(src_reply->element[0]->str);\n        else if (src_reply->element[0]->type == REDIS_REPLY_INTEGER)\n            cursor = src_reply->element[0]->integer;\n        int keycount = src_reply->element[1]->elements;\n        for (i = 0; i < keycount; i++) {\n            redisReply *kr = src_reply->element[1]->element[i];\n            assert(kr->type == REDIS_REPLY_STRING);\n            char *key = kr->str;\n            uint16_t slot = clusterManagerKeyHashSlot(key, kr->len);\n            clusterManagerNode *target = slots_map[slot];\n            printf(\"Migrating %s to %s:%d: \", key, target->ip, target->port);\n            redisReply *r = reconnectingRedisCommand(src_ctx, cmdfmt,\n                                                     target->ip, target->port,\n                                                     key, 0, timeout,\n                                                     \"COPY\", \"REPLACE\");\n            if (!r || r->type == REDIS_REPLY_ERROR) {\n                if (r && r->str) {\n                    clusterManagerLogErr(\"Source %s:%d replied with \"\n                                         \"error:\\n%s\\n\", src_ip, src_port,\n                                         r->str);\n                }\n                success = 0;\n            }\n            freeReplyObject(r);\n            if (!success) goto cleanup;\n            clusterManagerLogOk(\"OK\\n\");\n        }\n    }\ncleanup:\n    if (reply_err)\n        clusterManagerLogErr(\"Source %s:%d replied with error:\\n%s\\n\",\n                             src_ip, src_port, reply_err);\n    if (src_ctx) redisFree(src_ctx);\n    if (src_reply) freeReplyObject(src_reply);\n    return success;\ninvalid_args:\n    fprintf(stderr, \"%s\", invalid_args_msg);\n    return 0;\n}\n\nstatic int clusterManagerCommandCall(int argc, char **argv) {\n    int port = 0, i;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *refnode = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    argc--;\n    argv++;\n    size_t *argvlen = zmalloc(argc*sizeof(size_t));\n    clusterManagerLogInfo(\">>> Calling\");\n    for (i = 0; i < argc; i++) {\n        argvlen[i] = strlen(argv[i]);\n        printf(\" %s\", argv[i]);\n    }\n    printf(\"\\n\");\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (!n->context && !clusterManagerNodeConnect(n)) continue;\n        redisReply *reply = NULL;\n        redisAppendCommandArgv(n->context, argc, (const char **) argv, argvlen);\n        int status = redisGetReply(n->context, (void **)(&reply));\n        if (status != REDIS_OK || reply == NULL )\n            printf(\"%s:%d: Failed!\\n\", n->ip, n->port);\n        else {\n            sds formatted_reply = cliFormatReplyRaw(reply);\n            printf(\"%s:%d: %s\\n\", n->ip, n->port, (char *) formatted_reply);\n            sdsfree(formatted_reply);\n        }\n        if (reply != NULL) freeReplyObject(reply);\n    }\n    zfree(argvlen);\n    return 1;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandBackup(int argc, char **argv) {\n    UNUSED(argc);\n    int success = 1, port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *refnode = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    int no_issues = clusterManagerCheckCluster(0);\n    int cluster_errors_count = (no_issues ? 0 :\n                                listLength(cluster_manager.errors));\n    config.cluster_manager_command.backup_dir = argv[1];\n    /* TODO: check if backup_dir is a valid directory. */\n    sds json = sdsnew(\"[\\n\");\n    int first_node = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        if (!first_node) first_node = 1;\n        else json = sdscat(json, \",\\n\");\n        clusterManagerNode *node = ln->value;\n        sds node_json = clusterManagerNodeGetJSON(node, cluster_errors_count);\n        json = sdscat(json, node_json);\n        sdsfree(node_json);\n        if (node->replicate)\n            continue;\n        clusterManagerLogInfo(\">>> Node %s:%d -> Saving RDB...\\n\",\n                              node->ip, node->port);\n        fflush(stdout);\n        getRDB(node);\n    }\n    json = sdscat(json, \"\\n]\");\n    sds jsonpath = sdsnew(config.cluster_manager_command.backup_dir);\n    if (jsonpath[sdslen(jsonpath) - 1] != '/')\n        jsonpath = sdscat(jsonpath, \"/\");\n    jsonpath = sdscat(jsonpath, \"nodes.json\");\n    fflush(stdout);\n    clusterManagerLogInfo(\"Saving cluster configuration to: %s\\n\", jsonpath);\n    FILE *out = fopen(jsonpath, \"w+\");\n    if (!out) {\n        clusterManagerLogErr(\"Could not save nodes to: %s\\n\", jsonpath);\n        success = 0;\n        goto cleanup;\n    }\n    fputs(json, out);\n    fclose(out);\ncleanup:\n    sdsfree(json);\n    sdsfree(jsonpath);\n    if (success) {\n        if (!no_issues) {\n            clusterManagerLogWarn(\"*** Cluster seems to have some problems, \"\n                                  \"please be aware of it if you're going \"\n                                  \"to restore this backup.\\n\");\n        }\n        clusterManagerLogOk(\"[OK] Backup created into: %s\\n\",\n                            config.cluster_manager_command.backup_dir);\n    } else clusterManagerLogOk(\"[ERR] Failed to back cluster!\\n\");\n    return success;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandHelp(int argc, char **argv) {\n    UNUSED(argc);\n    UNUSED(argv);\n    int commands_count = sizeof(clusterManagerCommands) /\n                         sizeof(clusterManagerCommandDef);\n    int i = 0, j;\n    fprintf(stderr, \"Cluster Manager Commands:\\n\");\n    int padding = 15;\n    for (; i < commands_count; i++) {\n        clusterManagerCommandDef *def = &(clusterManagerCommands[i]);\n        int namelen = strlen(def->name), padlen = padding - namelen;\n        fprintf(stderr, \"  %s\", def->name);\n        for (j = 0; j < padlen; j++) fprintf(stderr, \" \");\n        fprintf(stderr, \"%s\\n\", (def->args ? def->args : \"\"));\n        if (def->options != NULL) {\n            int optslen = strlen(def->options);\n            char *p = def->options, *eos = p + optslen;\n            char *comma = NULL;\n            while ((comma = strchr(p, ',')) != NULL) {\n                int deflen = (int)(comma - p);\n                char buf[255];\n                memcpy(buf, p, deflen);\n                buf[deflen] = '\\0';\n                for (j = 0; j < padding; j++) fprintf(stderr, \" \");\n                fprintf(stderr, \"  --cluster-%s\\n\", buf);\n                p = comma + 1;\n                if (p >= eos) break;\n            }\n            if (p < eos) {\n                for (j = 0; j < padding; j++) fprintf(stderr, \" \");\n                fprintf(stderr, \"  --cluster-%s\\n\", p);\n            }\n        }\n    }\n    fprintf(stderr, \"\\nFor check, fix, reshard, del-node, set-timeout you \"\n                    \"can specify the host and port of any working node in \"\n                    \"the cluster.\\n\\n\");\n    return 0;\n}\n\n/*------------------------------------------------------------------------------\n * Latency and latency history modes\n *--------------------------------------------------------------------------- */\n\nstatic void latencyModePrint(long long min, long long max, double avg, long long count) {\n    if (config.output == OUTPUT_STANDARD) {\n        printf(\"min: %lld, max: %lld, avg: %.2f (%lld samples)\",\n                min, max, avg, count);\n        fflush(stdout);\n    } else if (config.output == OUTPUT_CSV) {\n        printf(\"%lld,%lld,%.2f,%lld\\n\", min, max, avg, count);\n    } else if (config.output == OUTPUT_RAW) {\n        printf(\"%lld %lld %.2f %lld\\n\", min, max, avg, count);\n    }\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    /* Set a default for the interval in case of --latency option\n     * with --raw, --csv or when it is redirected to non tty. */\n    if (config.interval == 0) {\n        config.interval = 1000;\n    } else {\n        config.interval /= 1000; /* We need to convert to milliseconds. */\n    }\n\n    if (!context) exit(1);\n    while(1) {\n        start = mstime();\n        reply = reconnectingRedisCommand(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\n        if (config.output == OUTPUT_STANDARD) {\n            printf(\"\\x1b[0G\\x1b[2K\"); /* Clear the line. */\n            latencyModePrint(min,max,avg,count);\n        } else {\n            if (config.latency_history) {\n                latencyModePrint(min,max,avg,count);\n            } else if (mstime()-history_start > config.interval) {\n                latencyModePrint(min,max,avg,count);\n                exit(0);\n            }\n        }\n\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 * Latency distribution mode -- requires 256 colors xterm\n *--------------------------------------------------------------------------- */\n\n#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */\n\n/* Structure to store samples distribution. */\nstruct distsamples {\n    long long max;   /* Max latency to fit into this interval (usec). */\n    long long count; /* Number of samples in this interval. */\n    int character;   /* Associated character in visualization. */\n};\n\n/* Helper function for latencyDistMode(). Performs the spectrum visualization\n * of the collected samples targeting an xterm 256 terminal.\n *\n * Takes an array of distsamples structures, ordered from smaller to bigger\n * 'max' value. Last sample max must be 0, to mean that it olds all the\n * samples greater than the previous one, and is also the stop sentinel.\n *\n * \"tot' is the total number of samples in the different buckets, so it\n * is the SUM(samples[i].conut) for i to 0 up to the max sample.\n *\n * As a side effect the function sets all the buckets count to 0. */\nvoid showLatencyDistSamples(struct distsamples *samples, long long tot) {\n    int j;\n\n     /* We convert samples into a index inside the palette\n     * proportional to the percentage a given bucket represents.\n     * This way intensity of the different parts of the spectrum\n     * don't change relative to the number of requests, which avoids to\n     * pollute the visualization with non-latency related info. */\n    printf(\"\\033[38;5;0m\"); /* Set foreground color to black. */\n    for (j = 0; ; j++) {\n        int coloridx =\n            ceil((float) samples[j].count / tot * (spectrum_palette_size-1));\n        int color = spectrum_palette[coloridx];\n        printf(\"\\033[48;5;%dm%c\", (int)color, samples[j].character);\n        samples[j].count = 0;\n        if (samples[j].max == 0) break; /* Last sample. */\n    }\n    printf(\"\\033[0m\\n\");\n    fflush(stdout);\n}\n\n/* Show the legend: different buckets values and colors meaning, so\n * that the spectrum is more easily readable. */\nvoid showLatencyDistLegend(void) {\n    int j;\n\n    printf(\"---------------------------------------------\\n\");\n    printf(\". - * #          .01 .125 .25 .5 milliseconds\\n\");\n    printf(\"1,2,3,...,9      from 1 to 9     milliseconds\\n\");\n    printf(\"A,B,C,D,E        10,20,30,40,50  milliseconds\\n\");\n    printf(\"F,G,H,I,J        .1,.2,.3,.4,.5       seconds\\n\");\n    printf(\"K,L,M,N,O,P,Q,?  1,2,4,8,16,30,60,>60 seconds\\n\");\n    printf(\"From 0 to 100%%: \");\n    for (j = 0; j < spectrum_palette_size; j++) {\n        printf(\"\\033[48;5;%dm \", spectrum_palette[j]);\n    }\n    printf(\"\\033[0m\\n\");\n    printf(\"---------------------------------------------\\n\");\n}\n\nstatic void latencyDistMode(void) {\n    redisReply *reply;\n    long long start, latency, count = 0;\n    long long history_interval =\n        config.interval ? config.interval/1000 :\n                          LATENCY_DIST_DEFAULT_INTERVAL;\n    long long history_start = ustime();\n    int j, outputs = 0;\n\n    struct distsamples samples[] = {\n        /* We use a mostly logarithmic scale, with certain linear intervals\n         * which are more interesting than others, like 1-10 milliseconds\n         * range. */\n        {10,0,'.'},         /* 0.01 ms */\n        {125,0,'-'},        /* 0.125 ms */\n        {250,0,'*'},        /* 0.25 ms */\n        {500,0,'#'},        /* 0.5 ms */\n        {1000,0,'1'},       /* 1 ms */\n        {2000,0,'2'},       /* 2 ms */\n        {3000,0,'3'},       /* 3 ms */\n        {4000,0,'4'},       /* 4 ms */\n        {5000,0,'5'},       /* 5 ms */\n        {6000,0,'6'},       /* 6 ms */\n        {7000,0,'7'},       /* 7 ms */\n        {8000,0,'8'},       /* 8 ms */\n        {9000,0,'9'},       /* 9 ms */\n        {10000,0,'A'},      /* 10 ms */\n        {20000,0,'B'},      /* 20 ms */\n        {30000,0,'C'},      /* 30 ms */\n        {40000,0,'D'},      /* 40 ms */\n        {50000,0,'E'},      /* 50 ms */\n        {100000,0,'F'},     /* 0.1 s */\n        {200000,0,'G'},     /* 0.2 s */\n        {300000,0,'H'},     /* 0.3 s */\n        {400000,0,'I'},     /* 0.4 s */\n        {500000,0,'J'},     /* 0.5 s */\n        {1000000,0,'K'},    /* 1 s */\n        {2000000,0,'L'},    /* 2 s */\n        {4000000,0,'M'},    /* 4 s */\n        {8000000,0,'N'},    /* 8 s */\n        {16000000,0,'O'},   /* 16 s */\n        {30000000,0,'P'},   /* 30 s */\n        {60000000,0,'Q'},   /* 1 minute */\n        {0,0,'?'},          /* > 1 minute */\n    };\n\n    if (!context) exit(1);\n    while(1) {\n        start = ustime();\n        reply = reconnectingRedisCommand(context,\"PING\");\n        if (reply == NULL) {\n            fprintf(stderr,\"\\nI/O error\\n\");\n            exit(1);\n        }\n        latency = ustime()-start;\n        freeReplyObject(reply);\n        count++;\n\n        /* Populate the relevant bucket. */\n        for (j = 0; ; j++) {\n            if (samples[j].max == 0 || latency <= samples[j].max) {\n                samples[j].count++;\n                break;\n            }\n        }\n\n        /* From time to time show the spectrum. */\n        if (count && (ustime()-history_start)/1000 > history_interval) {\n            if ((outputs++ % 20) == 0)\n                showLatencyDistLegend();\n            showLatencyDistSamples(samples,count);\n            history_start = ustime();\n            count = 0;\n        }\n        usleep(LATENCY_SAMPLE_RATE * 1000);\n    }\n}\n\n/*------------------------------------------------------------------------------\n * Slave mode\n *--------------------------------------------------------------------------- */\n\n#define RDB_EOF_MARK_SIZE 40\n\nvoid sendReplconf(const char* arg1, const char* arg2) {\n    printf(\"sending REPLCONF %s %s\\n\", arg1, arg2);\n    redisReply *reply = redisCommand(context, \"REPLCONF %s %s\", arg1, arg2);\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, \"REPLCONF %s error: %s\\n\", arg1, reply->str);\n        /* non fatal, old versions may not support it */\n    }\n    freeReplyObject(reply);\n}\n\nvoid sendCapa() {\n    sendReplconf(\"capa\", \"eof\");\n}\n\n/* Sends SYNC and reads the number of bytes in the payload. Used both by\n * slaveMode() and getRDB().\n * returns 0 in case an EOF marker is used. */\nunsigned long long sendSync(int fd, char *out_eof) {\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    if (strncmp(buf+1,\"EOF:\",4) == 0 && strlen(buf+5) >= RDB_EOF_MARK_SIZE) {\n        memcpy(out_eof, buf+5, RDB_EOF_MARK_SIZE);\n        return 0;\n    }\n    return strtoull(buf+1,NULL,10);\n}\n\nstatic void slaveMode(void) {\n    int fd = context->fd;\n    static char eofmark[RDB_EOF_MARK_SIZE];\n    static char lastbytes[RDB_EOF_MARK_SIZE];\n    static int usemark = 0;\n    unsigned long long payload = sendSync(fd, eofmark);\n    char buf[1024];\n    int original_output = config.output;\n\n    if (payload == 0) {\n        payload = ULLONG_MAX;\n        memset(lastbytes,0,RDB_EOF_MARK_SIZE);\n        usemark = 1;\n        fprintf(stderr,\"SYNC with master, discarding \"\n                       \"bytes of bulk transfer until EOF marker...\\n\");\n    } else {\n        fprintf(stderr,\"SYNC with master, discarding %llu \"\n                       \"bytes of bulk transfer...\\n\", payload);\n    }\n\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        if (usemark) {\n            /* Update the last bytes array, and check if it matches our delimiter.*/\n            if (nread >= RDB_EOF_MARK_SIZE) {\n                memcpy(lastbytes,buf+nread-RDB_EOF_MARK_SIZE,RDB_EOF_MARK_SIZE);\n            } else {\n                int rem = RDB_EOF_MARK_SIZE-nread;\n                memmove(lastbytes,lastbytes+nread,rem);\n                memcpy(lastbytes+rem,buf,nread);\n            }\n            if (memcmp(lastbytes,eofmark,RDB_EOF_MARK_SIZE) == 0)\n                break;\n        }\n    }\n\n    if (usemark) {\n        unsigned long long offset = ULLONG_MAX - payload;\n        fprintf(stderr,\"SYNC done after %llu bytes. Logging commands from master.\\n\", offset);\n        /* put the slave online */\n        sleep(1);\n        sendReplconf(\"ACK\", \"0\");\n    } else\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(clusterManagerNode *node) {\n    int s, fd;\n    char *filename;\n    if (node != NULL) {\n        assert(node->context);\n        s = node->context->fd;\n        filename = clusterManagerGetNodeRDBFilename(node);\n    } else {\n        s = context->fd;\n        filename = config.rdb_filename;\n    }\n    static char eofmark[RDB_EOF_MARK_SIZE];\n    static char lastbytes[RDB_EOF_MARK_SIZE];\n    static int usemark = 0;\n    unsigned long long payload = sendSync(s, eofmark);\n    char buf[4096];\n\n    if (payload == 0) {\n        payload = ULLONG_MAX;\n        memset(lastbytes,0,RDB_EOF_MARK_SIZE);\n        usemark = 1;\n        fprintf(stderr,\"SYNC sent to master, writing bytes of bulk transfer \"\n                \"until EOF marker to '%s'\\n\", filename);\n    } else {\n        fprintf(stderr,\"SYNC sent to master, writing %llu bytes to '%s'\\n\",\n            payload, filename);\n    }\n\n    /* Write to file. */\n    if (!strcmp(filename,\"-\")) {\n        fd = STDOUT_FILENO;\n    } else {\n        fd = open(filename, O_CREAT|O_WRONLY, 0644);\n        if (fd == -1) {\n            fprintf(stderr, \"Error opening '%s': %s\\n\", 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                (nwritten == -1) ? strerror(errno) : \"short write\");\n            exit(1);\n        }\n        payload -= nread;\n\n        if (usemark) {\n            /* Update the last bytes array, and check if it matches our delimiter.*/\n            if (nread >= RDB_EOF_MARK_SIZE) {\n                memcpy(lastbytes,buf+nread-RDB_EOF_MARK_SIZE,RDB_EOF_MARK_SIZE);\n            } else {\n                int rem = RDB_EOF_MARK_SIZE-nread;\n                memmove(lastbytes,lastbytes+nread,rem);\n                memcpy(lastbytes+rem,buf,nread);\n            }\n            if (memcmp(lastbytes,eofmark,RDB_EOF_MARK_SIZE) == 0)\n                break;\n        }\n    }\n    if (usemark) {\n        payload = ULLONG_MAX - payload - RDB_EOF_MARK_SIZE;\n        if (ftruncate(fd, payload) == -1)\n            fprintf(stderr,\"ftruncate failed: %s.\\n\", strerror(errno));\n        fprintf(stderr,\"Transfer finished with success after %llu bytes\\n\", payload);\n    } else {\n        fprintf(stderr,\"Transfer finished with success.\\n\");\n    }\n    close(s); /* Close the file descriptor ASAP as fsync() may take time. */\n    fsync(fd);\n    close(fd);\n    fprintf(stderr,\"Transfer finished with success.\\n\");\n    if (node) {\n        sdsfree(filename);\n        return;\n    }\n    exit(0);\n}\n\n/*------------------------------------------------------------------------------\n * Bulk import (pipe) mode\n *--------------------------------------------------------------------------- */\n\n#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)\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            int read_error = 0;\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                    read_error = 1;\n                    break;\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            /* Abort on read errors. We abort here because it is important\n             * to consume replies even after a read error: this way we can\n             * show a potential problem to the user. */\n            if (read_error) exit(1);\n        }\n\n        /* Handle the writable state: we can send protocol to the server. */\n        if (mask & AE_WRITABLE) {\n            ssize_t loop_nwritten = 0;\n\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                    loop_nwritten += 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) ||\n                    loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) 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\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 = strtoull(reply->element[0]->str, NULL, 10);\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\ntypedef struct {\n    char *name;\n    char *sizecmd;\n    char *sizeunit;\n    unsigned long long biggest;\n    unsigned long long count;\n    unsigned long long totalsize;\n    sds biggest_key;\n} typeinfo;\n\ntypeinfo type_string = { \"string\", \"STRLEN\", \"bytes\" };\ntypeinfo type_list = { \"list\", \"LLEN\", \"items\" };\ntypeinfo type_set = { \"set\", \"SCARD\", \"members\" };\ntypeinfo type_hash = { \"hash\", \"HLEN\", \"fields\" };\ntypeinfo type_zset = { \"zset\", \"ZCARD\", \"members\" };\ntypeinfo type_stream = { \"stream\", \"XLEN\", \"entries\" };\ntypeinfo type_other = { \"other\", NULL, \"?\" };\n\nstatic typeinfo* typeinfo_add(dict *types, char* name, typeinfo* type_template) {\n    typeinfo *info = zmalloc(sizeof(typeinfo));\n    *info = *type_template;\n    info->name = sdsnew(name);\n    dictAdd(types, info->name, info);\n    return info;\n}\n\nvoid type_free(void* priv_data, void* val) {\n    typeinfo *info = val;\n    UNUSED(priv_data);\n    if (info->biggest_key)\n        sdsfree(info->biggest_key);\n    sdsfree(info->name);\n    zfree(info);\n}\n\nstatic dictType typeinfoDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* key destructor (owned by the value)*/\n    type_free                  /* val destructor */\n};\n\nstatic void getKeyTypes(dict *types_dict, redisReply *keys, typeinfo **types) {\n    redisReply *reply;\n    unsigned 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            if(reply->type == REDIS_REPLY_ERROR) {\n                fprintf(stderr, \"TYPE returned an error: %s\\n\", reply->str);\n            } else {\n                fprintf(stderr,\n                    \"Invalid reply type (%d) for TYPE on key '%s'!\\n\",\n                    reply->type, keys->element[i]->str);\n            }\n            exit(1);\n        }\n\n        sds typereply = sdsnew(reply->str);\n        dictEntry *de = dictFind(types_dict, typereply);\n        sdsfree(typereply);\n        typeinfo *type = NULL;\n        if (de)\n            type = dictGetVal(de);\n        else if (strcmp(reply->str, \"none\")) /* create new types for modules, (but not for deleted keys) */\n            type = typeinfo_add(types_dict, reply->str, &type_other);\n        types[i] = type;\n        freeReplyObject(reply);\n    }\n}\n\nstatic void getKeySizes(redisReply *keys, typeinfo **types,\n                        unsigned long long *sizes, int memkeys,\n                        unsigned memkeys_samples)\n{\n    redisReply *reply;\n    unsigned int i;\n\n    /* Pipeline size commands */\n    for(i=0;i<keys->elements;i++) {\n        /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */\n        if(!types[i] || (!types[i]->sizecmd && !memkeys))\n            continue;\n\n        if (!memkeys)\n            redisAppendCommand(context, \"%s %s\",\n                types[i]->sizecmd, keys->element[i]->str);\n        else if (memkeys_samples==0)\n            redisAppendCommand(context, \"%s %s %s\",\n                \"MEMORY\", \"USAGE\", keys->element[i]->str);\n        else\n            redisAppendCommand(context, \"%s %s %s SAMPLES %u\",\n                \"MEMORY\", \"USAGE\", keys->element[i]->str, memkeys_samples);\n    }\n\n    /* Retrieve sizes */\n    for(i=0;i<keys->elements;i++) {\n        /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */\n        if(!types[i] || (!types[i]->sizecmd && !memkeys)) {\n            sizes[i] = 0;\n            continue;\n        }\n\n        /* Retrieve 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                !memkeys? types[i]->sizecmd: \"MEMORY USAGE\",\n                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(int memkeys, unsigned memkeys_samples) {\n    unsigned long long sampled = 0, total_keys, totlen=0, *sizes=NULL, it=0;\n    redisReply *reply, *keys;\n    unsigned int arrsize=0, i;\n    dictIterator *di;\n    dictEntry *de;\n    typeinfo **types = NULL;\n    double pct;\n\n    dict *types_dict = dictCreate(&typeinfoDictType, NULL);\n    typeinfo_add(types_dict, \"string\", &type_string);\n    typeinfo_add(types_dict, \"list\", &type_list);\n    typeinfo_add(types_dict, \"set\", &type_set);\n    typeinfo_add(types_dict, \"hash\", &type_hash);\n    typeinfo_add(types_dict, \"zset\", &type_zset);\n    typeinfo_add(types_dict, \"stream\", &type_stream);\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    /* 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(typeinfo*)*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        /* Retrieve types and then sizes */\n        getKeyTypes(types_dict, keys, types);\n        getKeySizes(keys, types, sizes, memkeys, memkeys_samples);\n\n        /* Now update our stats */\n        for(i=0;i<keys->elements;i++) {\n            typeinfo *type = types[i];\n            /* Skip keys that disappeared between SCAN and TYPE */\n            if(!type)\n                continue;\n\n            type->totalsize += sizes[i];\n            type->count++;\n            totlen += keys->element[i]->len;\n            sampled++;\n\n            if(type->biggest<sizes[i]) {\n                printf(\n                   \"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\\n\",\n                   pct, type->name, keys->element[i]->str, sizes[i],\n                   !memkeys? type->sizeunit: \"bytes\");\n\n                /* Keep track of biggest key name for this type */\n                if (type->biggest_key)\n                    sdsfree(type->biggest_key);\n                type->biggest_key = sdsnew(keys->element[i]->str);\n                if(!type->biggest_key) {\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                type->biggest = 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    di = dictGetIterator(types_dict);\n    while ((de = dictNext(di))) {\n        typeinfo *type = dictGetVal(de);\n        if(type->biggest_key) {\n            printf(\"Biggest %6s found '%s' has %llu %s\\n\", type->name, type->biggest_key,\n               type->biggest, !memkeys? type->sizeunit: \"bytes\");\n        }\n    }\n    dictReleaseIterator(di);\n\n    printf(\"\\n\");\n\n    di = dictGetIterator(types_dict);\n    while ((de = dictNext(di))) {\n        typeinfo *type = dictGetVal(de);\n        printf(\"%llu %ss with %llu %s (%05.2f%% of keys, avg size %.2f)\\n\",\n           type->count, type->name, type->totalsize, !memkeys? type->sizeunit: \"bytes\",\n           sampled ? 100 * (double)type->count/sampled : 0,\n           type->count ? (double)type->totalsize/type->count : 0);\n    }\n    dictReleaseIterator(di);\n\n    dictRelease(types_dict);\n\n    /* Success! */\n    exit(0);\n}\n\nstatic void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {\n    redisReply *reply;\n    unsigned int i;\n\n    /* Pipeline OBJECT freq commands */\n    for(i=0;i<keys->elements;i++) {\n        redisAppendCommand(context, \"OBJECT freq %s\", keys->element[i]->str);\n    }\n\n    /* Retrieve freqs */\n    for(i=0;i<keys->elements;i++) {\n        if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {\n            fprintf(stderr, \"Error getting freq 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            if(reply->type == REDIS_REPLY_ERROR) {\n                fprintf(stderr, \"Error: %s\\n\", reply->str);\n                exit(1);\n            } else {\n                fprintf(stderr, \"Warning: OBJECT freq on '%s' failed (may have been deleted)\\n\", keys->element[i]->str);\n                freqs[i] = 0;\n            }\n        } else {\n            freqs[i] = reply->integer;\n        }\n        freeReplyObject(reply);\n    }\n}\n\n#define HOTKEYS_SAMPLE 16\nstatic void findHotKeys(void) {\n    redisReply *keys, *reply;\n    unsigned long long counters[HOTKEYS_SAMPLE] = {0};\n    sds hotkeys[HOTKEYS_SAMPLE] = {NULL};\n    unsigned long long sampled = 0, total_keys, *freqs = NULL, it = 0;\n    unsigned int arrsize = 0, i, k;\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 hot 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    /* 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 freqs array if we need to */\n        if(keys->elements > arrsize) {\n            freqs = zrealloc(freqs, sizeof(unsigned long long)*keys->elements);\n\n            if(!freqs) {\n                fprintf(stderr, \"Failed to allocate storage for keys!\\n\");\n                exit(1);\n            }\n\n            arrsize = keys->elements;\n        }\n\n        getKeyFreqs(keys, freqs);\n\n        /* Now update our stats */\n        for(i=0;i<keys->elements;i++) {\n            sampled++;\n            /* Update overall progress */\n            if(sampled % 1000000 == 0) {\n                printf(\"[%05.2f%%] Sampled %llu keys so far\\n\", pct, sampled);\n            }\n\n            /* Use eviction pool here */\n            k = 0;\n            while (k < HOTKEYS_SAMPLE && freqs[i] > counters[k]) k++;\n            if (k == 0) continue;\n            k--;\n            if (k == 0 || counters[k] == 0) {\n                sdsfree(hotkeys[k]);\n            } else {\n                sdsfree(hotkeys[0]);\n                memmove(counters,counters+1,sizeof(counters[0])*k);\n                memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);\n            }\n            counters[k] = freqs[i];\n            hotkeys[k] = sdsnew(keys->element[i]->str);\n            printf(\n               \"[%05.2f%%] Hot key '%s' found so far with counter %llu\\n\",\n               pct, keys->element[i]->str, freqs[i]);\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 (freqs) zfree(freqs);\n\n    /* We're done */\n    printf(\"\\n-------- summary -------\\n\\n\");\n\n    printf(\"Sampled %llu keys in the keyspace!\\n\", sampled);\n\n    for (i=1; i<= HOTKEYS_SAMPLE; i++) {\n        k = HOTKEYS_SAMPLE - i;\n        if(counters[k]>0) {\n            printf(\"hot key found with counter: %llu\\tkeyname: %s\\n\", counters[k], hotkeys[k]);\n            sdsfree(hotkeys[k]);\n        }\n    }\n\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 = zmalloc(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    zfree(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,\"%lldB\",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(void) {\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 = reconnectingRedisCommand(context,\"INFO\");\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        /* Requests */\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        aux |= getLongInfoField(reply->str,\"loading\") << 2;\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        case 4:\n            printf(\"LOAD\");\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(void) {\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            unsigned 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 * LRU test mode\n *--------------------------------------------------------------------------- */\n\n/* Return an integer from min to max (both inclusive) using a power-law\n * distribution, depending on the value of alpha: the greater the alpha\n * the more bias towards lower values.\n *\n * With alpha = 6.2 the output follows the 80-20 rule where 20% of\n * the returned numbers will account for 80% of the frequency. */\nlong long powerLawRand(long long min, long long max, double alpha) {\n    double pl, r;\n\n    max += 1;\n    r = ((double)rand()) / RAND_MAX;\n    pl = pow(\n        ((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),\n        (1.0/(alpha+1)));\n    return (max-1-(long long)pl)+min;\n}\n\n/* Generates a key name among a set of lru_test_sample_size keys, using\n * an 80-20 distribution. */\nvoid LRUTestGenKey(char *buf, size_t buflen) {\n    snprintf(buf, buflen, \"lru:%lld\",\n        powerLawRand(1, config.lru_test_sample_size, 6.2));\n}\n\n#define LRU_CYCLE_PERIOD 1000 /* 1000 milliseconds. */\n#define LRU_CYCLE_PIPELINE_SIZE 250\nstatic void LRUTestMode(void) {\n    redisReply *reply;\n    char key[128];\n    long long start_cycle;\n    int j;\n\n    srand(time(NULL)^getpid());\n    while(1) {\n        /* Perform cycles of 1 second with 50% writes and 50% reads.\n         * We use pipelining batching writes / reads N times per cycle in order\n         * to fill the target instance easily. */\n        start_cycle = mstime();\n        long long hits = 0, misses = 0;\n        while(mstime() - start_cycle < LRU_CYCLE_PERIOD) {\n            /* Write cycle. */\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {\n                char val[6];\n                val[5] = '\\0';\n                for (int i = 0; i < 5; i++) val[i] = 'A'+rand()%('z'-'A');\n                LRUTestGenKey(key,sizeof(key));\n                redisAppendCommand(context, \"SET %s %s\",key,val);\n            }\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)\n                redisGetReply(context, (void**)&reply);\n\n            /* Read cycle. */\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {\n                LRUTestGenKey(key,sizeof(key));\n                redisAppendCommand(context, \"GET %s\",key);\n            }\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {\n                if (redisGetReply(context, (void**)&reply) == REDIS_OK) {\n                    switch(reply->type) {\n                        case REDIS_REPLY_ERROR:\n                            printf(\"%s\\n\", reply->str);\n                            break;\n                        case REDIS_REPLY_NIL:\n                            misses++;\n                            break;\n                        default:\n                            hits++;\n                            break;\n                    }\n                }\n            }\n\n            if (context->err) {\n                fprintf(stderr,\"I/O error during LRU test\\n\");\n                exit(1);\n            }\n        }\n        /* Print stats. */\n        printf(\n            \"%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\\n\",\n            hits+misses,\n            hits, (double)hits/(hits+misses)*100,\n            misses, (double)misses/(hits+misses)*100);\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 intrinsicLatencyModeStop(int s) {\n    UNUSED(s);\n    force_cancel_loop = 1;\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    signal(SIGINT, intrinsicLatencyModeStop);\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        double avg_us = (double)run_time/runs;\n        double avg_ns = avg_us * 1e3;\n        if (force_cancel_loop || end > test_end) {\n            printf(\"\\n%lld total runs \"\n                \"(avg latency: \"\n                \"%.4f microseconds / %.2f nanoseconds per run).\\n\",\n                runs, avg_us, avg_ns);\n            printf(\"Worst run took %.0fx longer than the average latency.\\n\",\n                max_latency / avg_us);\n            exit(0);\n        }\n    }\n}\n\nstatic sds askPassword() {\n    linenoiseMaskModeEnable();\n    sds auth = linenoise(\"Please input password: \");\n    linenoiseMaskModeDisable();\n    return auth;\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_dist_mode = 0;\n    config.latency_history = 0;\n    config.lru_test_mode = 0;\n    config.lru_test_sample_size = 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.hotkeys = 0;\n    config.stdinarg = 0;\n    config.auth = NULL;\n    config.askpass = 0;\n    config.user = NULL;\n    config.eval = NULL;\n    config.eval_ldb = 0;\n    config.eval_ldb_end = 0;\n    config.eval_ldb_sync = 0;\n    config.enable_ldb_on_eval = 0;\n    config.last_cmd_type = -1;\n    config.verbose = 0;\n    config.no_auth_warning = 0;\n    config.cluster_manager_command.name = NULL;\n    config.cluster_manager_command.argc = 0;\n    config.cluster_manager_command.argv = NULL;\n    config.cluster_manager_command.flags = 0;\n    config.cluster_manager_command.replicas = 0;\n    config.cluster_manager_command.from = NULL;\n    config.cluster_manager_command.to = NULL;\n    config.cluster_manager_command.weight = NULL;\n    config.cluster_manager_command.weight_argc = 0;\n    config.cluster_manager_command.slots = 0;\n    config.cluster_manager_command.timeout = CLUSTER_MANAGER_MIGRATE_TIMEOUT;\n    config.cluster_manager_command.pipeline = CLUSTER_MANAGER_MIGRATE_PIPELINE;\n    config.cluster_manager_command.threshold =\n        CLUSTER_MANAGER_REBALANCE_THRESHOLD;\n    config.cluster_manager_command.backup_dir = NULL;\n    pref.hints = 1;\n\n    spectrum_palette = spectrum_palette_color;\n    spectrum_palette_size = spectrum_palette_color_size;\n\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\n    firstarg = parseOptions(argc,argv);\n    argc -= firstarg;\n    argv += firstarg;\n\n    parseEnv();\n\n    if (config.askpass) {\n        config.auth = askPassword();\n    }\n\n#ifdef USE_OPENSSL\n    if (config.tls) {\n        ERR_load_crypto_strings();\n        SSL_load_error_strings();\n        SSL_library_init();\n    }\n#endif\n\n    /* Cluster Manager mode */\n    if (CLUSTER_MANAGER_MODE()) {\n        clusterManagerCommandProc *proc = validateClusterManagerCommand();\n        if (!proc) {\n            sdsfree(config.hostip);\n            sdsfree(config.mb_delim);\n            exit(1);\n        }\n        clusterManagerMode(proc);\n    }\n\n    /* Latency mode */\n    if (config.latency_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        latencyMode();\n    }\n\n    /* Latency distribution mode */\n    if (config.latency_dist_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        latencyDistMode();\n    }\n\n    /* Slave mode */\n    if (config.slave_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        sendCapa();\n        slaveMode();\n    }\n\n    /* Get RDB mode. */\n    if (config.getrdb_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        sendCapa();\n        getRDB(NULL);\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(0, 0);\n    }\n\n    /* Find large keys */\n    if (config.memkeys) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        findBigKeys(1, config.memkeys_samples);\n    }\n\n    /* Find hot keys */\n    if (config.hotkeys) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        findHotKeys();\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    /* LRU test mode */\n    if (config.lru_test_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        LRUTestMode();\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        /* Ignore SIGPIPE in interactive mode to force a reconnect */\n        signal(SIGPIPE, SIG_IGN);\n\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\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/redis-trib.rb",
    "content": "#!/usr/bin/env ruby\n\ndef colorized(str, color)\n    return str if !(ENV['TERM'] || '')[\"xterm\"]\n    color_code = {\n        white: 29,\n        bold: '29;1',\n        black: 30,\n        red: 31,\n        green: 32,\n        yellow: 33,\n        blue: 34,\n        magenta: 35,\n        cyan: 36,\n        gray: 37\n    }[color]\n    return str if !color_code\n    \"\\033[#{color_code}m#{str}\\033[0m\"\nend\n\nclass String\n\n    %w(white bold black red green yellow blue magenta cyan gray).each{|color|\n        color = :\"#{color}\"\n        define_method(color){\n            colorized(self, color)\n        }\n    }\n\nend\n\nCOMMANDS = %w(create check info fix reshard rebalance add-node \n              del-node set-timeout call import help)\n\nALLOWED_OPTIONS={\n    \"create\" => {\"replicas\" => true},\n    \"add-node\" => {\"slave\" => false, \"master-id\" => true},\n    \"import\" => {\"from\" => :required, \"copy\" => false, \"replace\" => false},\n    \"reshard\" => {\"from\" => true, \"to\" => true, \"slots\" => true, \"yes\" => false, \"timeout\" => true, \"pipeline\" => true},\n    \"rebalance\" => {\"weight\" => [], \"auto-weights\" => false, \"use-empty-masters\" => false, \"timeout\" => true, \"simulate\" => false, \"pipeline\" => true, \"threshold\" => true},\n    \"fix\" => {\"timeout\" => 0},\n}\n\ndef parse_options(cmd)\n    cmd = cmd.downcase\n    idx = 0\n    options = {}\n    args = []\n    while (arg = ARGV.shift)\n        if arg[0..1] == \"--\"\n            option = arg[2..-1]\n\n            # --verbose is a global option\n            if option == \"--verbose\"\n                options['verbose'] = true\n                next\n            end\n            if ALLOWED_OPTIONS[cmd] == nil || \n               ALLOWED_OPTIONS[cmd][option] == nil\n                next\n            end\n            if ALLOWED_OPTIONS[cmd][option] != false\n                value = ARGV.shift\n                next if !value\n            else\n                value = true\n            end\n\n            # If the option is set to [], it's a multiple arguments\n            # option. We just queue every new value into an array.\n            if ALLOWED_OPTIONS[cmd][option] == []\n                options[option] = [] if !options[option]\n                options[option] << value\n            else\n                options[option] = value\n            end\n        else\n            next if arg[0,1] == '-'\n            args << arg\n        end\n    end\n\n    return options,args\nend\n\ndef command_example(cmd, args, opts)\n    cmd = \"redis-cli --cluster #{cmd}\"\n    args.each{|a| \n        a = a.to_s\n        a = a.inspect if a[' ']\n        cmd << \" #{a}\"\n    }\n    opts.each{|opt, val|\n        opt = \" --cluster-#{opt.downcase}\"\n        if val != true\n            val = val.join(' ') if val.is_a? Array\n            opt << \" #{val}\"\n        end\n        cmd << opt\n    }\n    cmd\nend\n\n$command = ARGV.shift\n$opts, $args = parse_options($command) if $command\n\nputs \"WARNING: redis-trib.rb is not longer available!\".yellow\nputs \"You should use #{'redis-cli'.bold} instead.\"\nputs ''\nputs \"All commands and features belonging to redis-trib.rb \"+\n     \"have been moved\\nto redis-cli.\"\nputs \"In order to use them you should call redis-cli with the #{'--cluster'.bold}\"\nputs \"option followed by the subcommand name, arguments and options.\"\nputs ''\nputs \"Use the following syntax:\"\nputs \"redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS]\".bold\nputs ''\nputs \"Example:\"\nif $command\n    example = command_example $command, $args, $opts\nelse\n    example = \"redis-cli --cluster info 127.0.0.1:7000\"\nend\nputs example.bold\nputs ''\nputs \"To get help about all subcommands, type:\"\nputs \"redis-cli --cluster help\".bold\nputs ''\nexit 1\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/redisassert.h",
    "content": "/* redisassert.h -- Drop in replacements 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 : (_serverAssert(#_e,__FILE__,__LINE__),_exit(1)))\n#define panic(...) _serverPanic(__FILE__,__LINE__,__VA_ARGS__),_exit(1)\n\nvoid _serverAssert(char *estr, char *file, int line);\nvoid _serverPanic(const char *file, int line, const char *msg, ...);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/redismodule.h",
    "content": "#ifndef REDISMODULE_H\n#define REDISMODULE_H\n\n#include <sys/types.h>\n#include <stdint.h>\n#include <stdio.h>\n\n/* ---------------- Defines common between core and modules --------------- */\n\n/* Error status return values. */\n#define REDISMODULE_OK 0\n#define REDISMODULE_ERR 1\n\n/* API versions. */\n#define REDISMODULE_APIVER_1 1\n\n/* API flags and constants */\n#define REDISMODULE_READ (1<<0)\n#define REDISMODULE_WRITE (1<<1)\n\n/* RedisModule_OpenKey extra flags for the 'mode' argument.\n * Avoid touching the LRU/LFU of the key when opened. */\n#define REDISMODULE_OPEN_KEY_NOTOUCH (1<<16)\n\n#define REDISMODULE_LIST_HEAD 0\n#define REDISMODULE_LIST_TAIL 1\n\n/* Key types. */\n#define REDISMODULE_KEYTYPE_EMPTY 0\n#define REDISMODULE_KEYTYPE_STRING 1\n#define REDISMODULE_KEYTYPE_LIST 2\n#define REDISMODULE_KEYTYPE_HASH 3\n#define REDISMODULE_KEYTYPE_SET 4\n#define REDISMODULE_KEYTYPE_ZSET 5\n#define REDISMODULE_KEYTYPE_MODULE 6\n#define REDISMODULE_KEYTYPE_STREAM 7\n\n/* Reply types. */\n#define REDISMODULE_REPLY_UNKNOWN -1\n#define REDISMODULE_REPLY_STRING 0\n#define REDISMODULE_REPLY_ERROR 1\n#define REDISMODULE_REPLY_INTEGER 2\n#define REDISMODULE_REPLY_ARRAY 3\n#define REDISMODULE_REPLY_NULL 4\n\n/* Postponed array length. */\n#define REDISMODULE_POSTPONED_ARRAY_LEN -1\n\n/* Expire */\n#define REDISMODULE_NO_EXPIRE -1\n\n/* Sorted set API flags. */\n#define REDISMODULE_ZADD_XX      (1<<0)\n#define REDISMODULE_ZADD_NX      (1<<1)\n#define REDISMODULE_ZADD_ADDED   (1<<2)\n#define REDISMODULE_ZADD_UPDATED (1<<3)\n#define REDISMODULE_ZADD_NOP     (1<<4)\n\n/* Hash API flags. */\n#define REDISMODULE_HASH_NONE       0\n#define REDISMODULE_HASH_NX         (1<<0)\n#define REDISMODULE_HASH_XX         (1<<1)\n#define REDISMODULE_HASH_CFIELDS    (1<<2)\n#define REDISMODULE_HASH_EXISTS     (1<<3)\n\n/* Context Flags: Info about the current context returned by\n * RM_GetContextFlags(). */\n\n/* The command is running in the context of a Lua script */\n#define REDISMODULE_CTX_FLAGS_LUA (1<<0)\n/* The command is running inside a Redis transaction */\n#define REDISMODULE_CTX_FLAGS_MULTI (1<<1)\n/* The instance is a master */\n#define REDISMODULE_CTX_FLAGS_MASTER (1<<2)\n/* The instance is a slave */\n#define REDISMODULE_CTX_FLAGS_SLAVE (1<<3)\n/* The instance is read-only (usually meaning it's a slave as well) */\n#define REDISMODULE_CTX_FLAGS_READONLY (1<<4)\n/* The instance is running in cluster mode */\n#define REDISMODULE_CTX_FLAGS_CLUSTER (1<<5)\n/* The instance has AOF enabled */\n#define REDISMODULE_CTX_FLAGS_AOF (1<<6)\n/* The instance has RDB enabled */\n#define REDISMODULE_CTX_FLAGS_RDB (1<<7)\n/* The instance has Maxmemory set */\n#define REDISMODULE_CTX_FLAGS_MAXMEMORY (1<<8)\n/* Maxmemory is set and has an eviction policy that may delete keys */\n#define REDISMODULE_CTX_FLAGS_EVICT (1<<9)\n/* Redis is out of memory according to the maxmemory flag. */\n#define REDISMODULE_CTX_FLAGS_OOM (1<<10)\n/* Less than 25% of memory available according to maxmemory. */\n#define REDISMODULE_CTX_FLAGS_OOM_WARNING (1<<11)\n/* The command was sent over the replication link. */\n#define REDISMODULE_CTX_FLAGS_REPLICATED (1<<12)\n/* Redis is currently loading either from AOF or RDB. */\n#define REDISMODULE_CTX_FLAGS_LOADING (1<<13)\n/* The replica has no link with its master, note that\n * there is the inverse flag as well:\n *\n *  REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE\n *\n * The two flags are exclusive, one or the other can be set. */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE (1<<14)\n/* The replica is trying to connect with the master.\n * (REPL_STATE_CONNECT and REPL_STATE_CONNECTING states) */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING (1<<15)\n/* THe replica is receiving an RDB file from its master. */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING (1<<16)\n/* The replica is online, receiving updates from its master. */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE (1<<17)\n/* There is currently some background process active. */\n#define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18)\n/* The next EXEC will fail due to dirty CAS (touched keys). */\n#define REDISMODULE_CTX_FLAGS_MULTI_DIRTY (1<<19)\n\n/* Keyspace changes notification classes. Every class is associated with a\n * character for configuration purposes.\n * NOTE: These have to be in sync with NOTIFY_* in server.h */\n#define REDISMODULE_NOTIFY_KEYSPACE (1<<0)    /* K */\n#define REDISMODULE_NOTIFY_KEYEVENT (1<<1)    /* E */\n#define REDISMODULE_NOTIFY_GENERIC (1<<2)     /* g */\n#define REDISMODULE_NOTIFY_STRING (1<<3)      /* $ */\n#define REDISMODULE_NOTIFY_LIST (1<<4)        /* l */\n#define REDISMODULE_NOTIFY_SET (1<<5)         /* s */\n#define REDISMODULE_NOTIFY_HASH (1<<6)        /* h */\n#define REDISMODULE_NOTIFY_ZSET (1<<7)        /* z */\n#define REDISMODULE_NOTIFY_EXPIRED (1<<8)     /* x */\n#define REDISMODULE_NOTIFY_EVICTED (1<<9)     /* e */\n#define REDISMODULE_NOTIFY_STREAM (1<<10)     /* t */\n#define REDISMODULE_NOTIFY_KEY_MISS (1<<11)   /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */\n#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM)      /* A */\n\n\n/* A special pointer that we can use between the core and the module to signal\n * field deletion, and that is impossible to be a valid pointer. */\n#define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1)\n\n/* Error messages. */\n#define REDISMODULE_ERRORMSG_WRONGTYPE \"WRONGTYPE Operation against a key holding the wrong kind of value\"\n\n#define REDISMODULE_POSITIVE_INFINITE (1.0/0.0)\n#define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0)\n\n/* Cluster API defines. */\n#define REDISMODULE_NODE_ID_LEN 40\n#define REDISMODULE_NODE_MYSELF     (1<<0)\n#define REDISMODULE_NODE_MASTER     (1<<1)\n#define REDISMODULE_NODE_SLAVE      (1<<2)\n#define REDISMODULE_NODE_PFAIL      (1<<3)\n#define REDISMODULE_NODE_FAIL       (1<<4)\n#define REDISMODULE_NODE_NOFAILOVER (1<<5)\n\n#define REDISMODULE_CLUSTER_FLAG_NONE 0\n#define REDISMODULE_CLUSTER_FLAG_NO_FAILOVER (1<<1)\n#define REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION (1<<2)\n\n#define REDISMODULE_NOT_USED(V) ((void) V)\n\n/* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */\n#define REDISMODULE_AUX_BEFORE_RDB (1<<0)\n#define REDISMODULE_AUX_AFTER_RDB (1<<1)\n\n/* This type represents a timer handle, and is returned when a timer is\n * registered and used in order to invalidate a timer. It's just a 64 bit\n * number, because this is how each timer is represented inside the radix tree\n * of timers that are going to expire, sorted by expire time. */\ntypedef uint64_t RedisModuleTimerID;\n\n/* CommandFilter Flags */\n\n/* Do filter RedisModule_Call() commands initiated by module itself. */\n#define REDISMODULE_CMDFILTER_NOSELF    (1<<0)\n\n/* Declare that the module can handle errors with RedisModule_SetModuleOptions. */\n#define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS    (1<<0)\n/* When set, Redis will not call RedisModule_SignalModifiedKey(), implicitly in\n * RedisModule_CloseKey, and the module needs to do that when manually when keys\n * are modified from the user's sperspective, to invalidate WATCH. */\n#define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1)\n\n/* Server events definitions. */\n#define REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED 0\n#define REDISMODULE_EVENT_PERSISTENCE 1\n#define REDISMODULE_EVENT_FLUSHDB 2\n#define REDISMODULE_EVENT_LOADING 3\n#define REDISMODULE_EVENT_CLIENT_CHANGE 4\n#define REDISMODULE_EVENT_SHUTDOWN 5\n#define REDISMODULE_EVENT_REPLICA_CHANGE 6\n#define REDISMODULE_EVENT_MASTER_LINK_CHANGE 7\n#define REDISMODULE_EVENT_CRON_LOOP 8\n#define REDISMODULE_EVENT_MODULE_CHANGE 9\n#define REDISMODULE_EVENT_LOADING_PROGRESS 10\n\ntypedef struct RedisModuleEvent {\n    uint64_t id;        /* REDISMODULE_EVENT_... defines. */\n    uint64_t dataver;   /* Version of the structure we pass as 'data'. */\n} RedisModuleEvent;\n\nstruct RedisModuleCtx;\ntypedef void (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data);\n\nstatic const RedisModuleEvent\n    RedisModuleEvent_ReplicationRoleChanged = {\n        REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,\n        1\n    },\n    RedisModuleEvent_Persistence = {\n        REDISMODULE_EVENT_PERSISTENCE,\n        1\n    },\n    RedisModuleEvent_FlushDB = {\n        REDISMODULE_EVENT_FLUSHDB,\n        1\n    },\n    RedisModuleEvent_Loading = {\n        REDISMODULE_EVENT_LOADING,\n        1\n    },\n    RedisModuleEvent_ClientChange = {\n        REDISMODULE_EVENT_CLIENT_CHANGE,\n        1\n    },\n    RedisModuleEvent_Shutdown = {\n        REDISMODULE_EVENT_SHUTDOWN,\n        1\n    },\n    RedisModuleEvent_ReplicaChange = {\n        REDISMODULE_EVENT_REPLICA_CHANGE,\n        1\n    },\n    RedisModuleEvent_CronLoop = {\n        REDISMODULE_EVENT_CRON_LOOP,\n        1\n    },\n    RedisModuleEvent_MasterLinkChange = {\n        REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n        1\n    },\n    RedisModuleEvent_ModuleChange = {\n        REDISMODULE_EVENT_MODULE_CHANGE,\n        1\n    },\n    RedisModuleEvent_LoadingProgress = {\n        REDISMODULE_EVENT_LOADING_PROGRESS,\n        1\n    };\n\n/* Those are values that are used for the 'subevent' callback argument. */\n#define REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START 0\n#define REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START 1\n#define REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START 2\n#define REDISMODULE_SUBEVENT_PERSISTENCE_ENDED 3\n#define REDISMODULE_SUBEVENT_PERSISTENCE_FAILED 4\n\n#define REDISMODULE_SUBEVENT_LOADING_RDB_START 0\n#define REDISMODULE_SUBEVENT_LOADING_AOF_START 1\n#define REDISMODULE_SUBEVENT_LOADING_REPL_START 2\n#define REDISMODULE_SUBEVENT_LOADING_ENDED 3\n#define REDISMODULE_SUBEVENT_LOADING_FAILED 4\n\n#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0\n#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1\n\n#define REDISMODULE_SUBEVENT_MASTER_LINK_UP 0\n#define REDISMODULE_SUBEVENT_MASTER_LINK_DOWN 1\n\n#define REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE 0\n#define REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE 1\n\n#define REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER 0\n#define REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA 1\n\n#define REDISMODULE_SUBEVENT_FLUSHDB_START 0\n#define REDISMODULE_SUBEVENT_FLUSHDB_END 1\n\n#define REDISMODULE_SUBEVENT_MODULE_LOADED 0\n#define REDISMODULE_SUBEVENT_MODULE_UNLOADED 1\n\n#define REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB 0\n#define REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF 1\n\n/* RedisModuleClientInfo flags. */\n#define REDISMODULE_CLIENTINFO_FLAG_SSL (1<<0)\n#define REDISMODULE_CLIENTINFO_FLAG_PUBSUB (1<<1)\n#define REDISMODULE_CLIENTINFO_FLAG_BLOCKED (1<<2)\n#define REDISMODULE_CLIENTINFO_FLAG_TRACKING (1<<3)\n#define REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET (1<<4)\n#define REDISMODULE_CLIENTINFO_FLAG_MULTI (1<<5)\n\n/* Here we take all the structures that the module pass to the core\n * and the other way around. Notably the list here contains the structures\n * used by the hooks API RedisModule_RegisterToServerEvent().\n *\n * The structures always start with a 'version' field. This is useful\n * when we want to pass a reference to the structure to the core APIs,\n * for the APIs to fill the structure. In that case, the structure 'version'\n * field is initialized before passing it to the core, so that the core is\n * able to cast the pointer to the appropriate structure version. In this\n * way we obtain ABI compatibility.\n *\n * Here we'll list all the structure versions in case they evolve over time,\n * however using a define, we'll make sure to use the last version as the\n * public name for the module to use. */\n\n#define REDISMODULE_CLIENTINFO_VERSION 1\ntypedef struct RedisModuleClientInfo {\n    uint64_t version;       /* Version of this structure for ABI compat. */\n    uint64_t flags;         /* REDISMODULE_CLIENTINFO_FLAG_* */\n    uint64_t id;            /* Client ID. */\n    char addr[46];          /* IPv4 or IPv6 address. */\n    uint16_t port;          /* TCP port. */\n    uint16_t db;            /* Selected DB. */\n} RedisModuleClientInfoV1;\n\n#define RedisModuleClientInfo RedisModuleClientInfoV1\n\n#define REDISMODULE_REPLICATIONINFO_VERSION 1\ntypedef struct RedisModuleReplicationInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int master;             /* true if master, false if replica */\n    char *masterhost;       /* master instance hostname for NOW_REPLICA */\n    int masterport;         /* master instance port for NOW_REPLICA */\n    char *replid1;          /* Main replication ID */\n    char *replid2;          /* Secondary replication ID */\n    uint64_t repl1_offset;  /* Main replication offset */\n    uint64_t repl2_offset;  /* Offset of replid2 validity */\n} RedisModuleReplicationInfoV1;\n\n#define RedisModuleReplicationInfo RedisModuleReplicationInfoV1\n\n#define REDISMODULE_FLUSHINFO_VERSION 1\ntypedef struct RedisModuleFlushInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int32_t sync;           /* Synchronous or threaded flush?. */\n    int32_t dbnum;          /* Flushed database number, -1 for ALL. */\n} RedisModuleFlushInfoV1;\n\n#define RedisModuleFlushInfo RedisModuleFlushInfoV1\n\n#define REDISMODULE_MODULE_CHANGE_VERSION 1\ntypedef struct RedisModuleModuleChange {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    const char* module_name;/* Name of module loaded or unloaded. */\n    int32_t module_version; /* Module version. */\n} RedisModuleModuleChangeV1;\n\n#define RedisModuleModuleChange RedisModuleModuleChangeV1\n\n#define REDISMODULE_CRON_LOOP_VERSION 1\ntypedef struct RedisModuleCronLoopInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int32_t hz;             /* Approximate number of events per second. */\n} RedisModuleCronLoopV1;\n\n#define RedisModuleCronLoop RedisModuleCronLoopV1\n\n#define REDISMODULE_LOADING_PROGRESS_VERSION 1\ntypedef struct RedisModuleLoadingProgressInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int32_t hz;             /* Approximate number of events per second. */\n    int32_t progress;       /* Approximate progress between 0 and 1024, or -1\n                             * if unknown. */\n} RedisModuleLoadingProgressV1;\n\n#define RedisModuleLoadingProgress RedisModuleLoadingProgressV1\n\n/* ------------------------- End of common defines ------------------------ */\n\n#ifndef REDISMODULE_CORE\n\ntypedef long long mstime_t;\n\n/* Incomplete structures for compiler checks but opaque access. */\ntypedef struct RedisModuleCtx RedisModuleCtx;\ntypedef struct RedisModuleKey RedisModuleKey;\ntypedef struct RedisModuleString RedisModuleString;\ntypedef struct RedisModuleCallReply RedisModuleCallReply;\ntypedef struct RedisModuleIO RedisModuleIO;\ntypedef struct RedisModuleType RedisModuleType;\ntypedef struct RedisModuleDigest RedisModuleDigest;\ntypedef struct RedisModuleBlockedClient RedisModuleBlockedClient;\ntypedef struct RedisModuleClusterInfo RedisModuleClusterInfo;\ntypedef struct RedisModuleDict RedisModuleDict;\ntypedef struct RedisModuleDictIter RedisModuleDictIter;\ntypedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;\ntypedef struct RedisModuleCommandFilter RedisModuleCommandFilter;\ntypedef struct RedisModuleInfoCtx RedisModuleInfoCtx;\ntypedef struct RedisModuleServerInfoData RedisModuleServerInfoData;\ntypedef struct RedisModuleScanCursor RedisModuleScanCursor;\ntypedef struct RedisModuleUser RedisModuleUser;\n\ntypedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);\ntypedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc);\ntypedef int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);\ntypedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);\ntypedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);\ntypedef int (*RedisModuleTypeAuxLoadFunc)(RedisModuleIO *rdb, int encver, int when);\ntypedef void (*RedisModuleTypeAuxSaveFunc)(RedisModuleIO *rdb, int when);\ntypedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);\ntypedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value);\ntypedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);\ntypedef void (*RedisModuleTypeFreeFunc)(void *value);\ntypedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len);\ntypedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);\ntypedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);\ntypedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);\ntypedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);\ntypedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata);\ntypedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata);\ntypedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);\n\n#define REDISMODULE_TYPE_METHOD_VERSION 2\ntypedef struct RedisModuleTypeMethods {\n    uint64_t version;\n    RedisModuleTypeLoadFunc rdb_load;\n    RedisModuleTypeSaveFunc rdb_save;\n    RedisModuleTypeRewriteFunc aof_rewrite;\n    RedisModuleTypeMemUsageFunc mem_usage;\n    RedisModuleTypeDigestFunc digest;\n    RedisModuleTypeFreeFunc free;\n    RedisModuleTypeAuxLoadFunc aux_load;\n    RedisModuleTypeAuxSaveFunc aux_save;\n    int aux_save_triggers;\n} RedisModuleTypeMethods;\n\n#define REDISMODULE_GET_API(name) \\\n    RedisModule_GetApi(\"RedisModule_\" #name, ((void **)&RedisModule_ ## name))\n\n#define REDISMODULE_API_FUNC(x) (*x)\n\n\nvoid *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes);\nvoid *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes);\nvoid REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr);\nvoid *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size);\nchar *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str);\nint REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *);\nint REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep);\nvoid REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver);\nint REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name);\nint REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll);\nint REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid);\nvoid *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode);\nvoid REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp);\nint REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp);\nsize_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp);\nint REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where);\nRedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);\nconst char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply);\nint REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply);\nlong long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply);\nsize_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply);\nRedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);\n#ifdef __GNUC__\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));\n#else\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);\n#endif\nvoid REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str);\nconst char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply);\nint REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll);\nint REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d);\nint REDISMODULE_API_FUNC(RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d);\nvoid REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);\nint REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx);\nconst char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply);\nint REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str);\nchar *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode);\nint REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen);\nmstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire);\nvoid REDISMODULE_API_FUNC(RedisModule_ResetDataset)(int restart_aof, int async);\nunsigned long long REDISMODULE_API_FUNC(RedisModule_DbSize)(RedisModuleCtx *ctx);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_RandomKey)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr);\nint REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore);\nint REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted);\nvoid REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);\nint REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);\nint REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);\nint REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...);\nint REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...);\nint REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos);\nunsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_GetClientInfoById)(void *ci, uint64_t id);\nint REDISMODULE_API_FUNC(RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message);\nint REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_AvoidReplicaTraffic)();\nvoid *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes);\nRedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods);\nint REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);\nint REDISMODULE_API_FUNC(RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value);\nRedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);\nvoid *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_IsIOError)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options);\nint REDISMODULE_API_FUNC(RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);\nuint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);\nint64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io);\nchar *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value);\ndouble REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);\nfloat REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value);\nlong double REDISMODULE_API_FUNC(RedisModule_LoadLongDouble)(RedisModuleIO *io);\nvoid *REDISMODULE_API_FUNC(RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt);\n#ifdef __GNUC__\nvoid REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));\nvoid REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));\n#else\nvoid REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);\nvoid REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);\n#endif\nvoid REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);\nvoid REDISMODULE_API_FUNC(RedisModule_LatencyAddSample)(const char *event, mstime_t latency);\nint REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str);\nint REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b);\nRedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io);\nconst RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromIO)(RedisModuleIO *io);\nconst RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key);\nlong long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void);\nvoid REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele);\nvoid REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md);\nRedisModuleDict *REDISMODULE_API_FUNC(RedisModule_CreateDict)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d);\nuint64_t REDISMODULE_API_FUNC(RedisModule_DictSize)(RedisModuleDict *d);\nint REDISMODULE_API_FUNC(RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);\nint REDISMODULE_API_FUNC(RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);\nint REDISMODULE_API_FUNC(RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr);\nint REDISMODULE_API_FUNC(RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey);\nint REDISMODULE_API_FUNC(RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval);\nint REDISMODULE_API_FUNC(RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval);\nRedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen);\nRedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key);\nvoid REDISMODULE_API_FUNC(RedisModule_DictIteratorStop)(RedisModuleDictIter *di);\nint REDISMODULE_API_FUNC(RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);\nint REDISMODULE_API_FUNC(RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);\nint REDISMODULE_API_FUNC(RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);\nint REDISMODULE_API_FUNC(RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);\nint REDISMODULE_API_FUNC(RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name);\nint REDISMODULE_API_FUNC(RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name);\nint REDISMODULE_API_FUNC(RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value);\nRedisModuleServerInfoData *REDISMODULE_API_FUNC(RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field);\nconst char *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field);\nlong long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);\nunsigned long long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);\ndouble REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err);\nint REDISMODULE_API_FUNC(RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback);\nint REDISMODULE_API_FUNC(RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle);\nint REDISMODULE_API_FUNC(RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle);\nint REDISMODULE_API_FUNC(RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq);\nint REDISMODULE_API_FUNC(RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq);\nRedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata);\nvoid REDISMODULE_API_FUNC(RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx);\nRedisModuleScanCursor *REDISMODULE_API_FUNC(RedisModule_ScanCursorCreate)();\nvoid REDISMODULE_API_FUNC(RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor);\nvoid REDISMODULE_API_FUNC(RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor);\nint REDISMODULE_API_FUNC(RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata);\nint REDISMODULE_API_FUNC(RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata);\n/* Experimental APIs */\n#ifdef REDISMODULE_EXPERIMENTAL_API\n#define REDISMODULE_EXPERIMENTAL_API_VERSION 3\nRedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms);\nint REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata);\nint REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx);\nvoid *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx);\nRedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc);\nRedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb);\nint REDISMODULE_API_FUNC(RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);\nint REDISMODULE_API_FUNC(RedisModule_GetNotifyKeyspaceEvents)();\nint REDISMODULE_API_FUNC(RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback);\nint REDISMODULE_API_FUNC(RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len);\nint REDISMODULE_API_FUNC(RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags);\nchar **REDISMODULE_API_FUNC(RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeClusterNodesList)(char **ids);\nRedisModuleTimerID REDISMODULE_API_FUNC(RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data);\nint REDISMODULE_API_FUNC(RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data);\nint REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data);\nconst char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void);\nsize_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(void);\nvoid REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback);\nvoid REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);\nint REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func);\nvoid *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname);\nRedisModuleCommandFilter *REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags);\nint REDISMODULE_API_FUNC(RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx);\nconst RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos);\nint REDISMODULE_API_FUNC(RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data);\nint REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode);\nint REDISMODULE_API_FUNC(RedisModule_KillForkChild)(int child_pid);\nfloat REDISMODULE_API_FUNC(RedisModule_GetUsedMemoryRatio)();\nsize_t REDISMODULE_API_FUNC(RedisModule_MallocSize)(void* ptr);\nRedisModuleUser *REDISMODULE_API_FUNC(RedisModule_CreateModuleUser)(const char *name);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeModuleUser)(RedisModuleUser *user);\nint REDISMODULE_API_FUNC(RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl);\nint REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);\nint REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);\nvoid REDISMODULE_API_FUNC(RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id);\n#endif\n\n#define RedisModule_IsAOFClient(id) ((id) == CLIENT_ID_AOF)\n\n/* This is included inline inside each Redis module. */\nstatic int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));\nstatic int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {\n    void *getapifuncptr = ((void**)ctx)[0];\n    RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr;\n    REDISMODULE_GET_API(Alloc);\n    REDISMODULE_GET_API(Calloc);\n    REDISMODULE_GET_API(Free);\n    REDISMODULE_GET_API(Realloc);\n    REDISMODULE_GET_API(Strdup);\n    REDISMODULE_GET_API(CreateCommand);\n    REDISMODULE_GET_API(SetModuleAttribs);\n    REDISMODULE_GET_API(IsModuleNameBusy);\n    REDISMODULE_GET_API(WrongArity);\n    REDISMODULE_GET_API(ReplyWithLongLong);\n    REDISMODULE_GET_API(ReplyWithError);\n    REDISMODULE_GET_API(ReplyWithSimpleString);\n    REDISMODULE_GET_API(ReplyWithArray);\n    REDISMODULE_GET_API(ReplyWithNullArray);\n    REDISMODULE_GET_API(ReplyWithEmptyArray);\n    REDISMODULE_GET_API(ReplySetArrayLength);\n    REDISMODULE_GET_API(ReplyWithStringBuffer);\n    REDISMODULE_GET_API(ReplyWithCString);\n    REDISMODULE_GET_API(ReplyWithString);\n    REDISMODULE_GET_API(ReplyWithEmptyString);\n    REDISMODULE_GET_API(ReplyWithVerbatimString);\n    REDISMODULE_GET_API(ReplyWithNull);\n    REDISMODULE_GET_API(ReplyWithCallReply);\n    REDISMODULE_GET_API(ReplyWithDouble);\n    REDISMODULE_GET_API(ReplyWithLongDouble);\n    REDISMODULE_GET_API(GetSelectedDb);\n    REDISMODULE_GET_API(SelectDb);\n    REDISMODULE_GET_API(OpenKey);\n    REDISMODULE_GET_API(CloseKey);\n    REDISMODULE_GET_API(KeyType);\n    REDISMODULE_GET_API(ValueLength);\n    REDISMODULE_GET_API(ListPush);\n    REDISMODULE_GET_API(ListPop);\n    REDISMODULE_GET_API(StringToLongLong);\n    REDISMODULE_GET_API(StringToDouble);\n    REDISMODULE_GET_API(StringToLongDouble);\n    REDISMODULE_GET_API(Call);\n    REDISMODULE_GET_API(CallReplyProto);\n    REDISMODULE_GET_API(FreeCallReply);\n    REDISMODULE_GET_API(CallReplyInteger);\n    REDISMODULE_GET_API(CallReplyType);\n    REDISMODULE_GET_API(CallReplyLength);\n    REDISMODULE_GET_API(CallReplyArrayElement);\n    REDISMODULE_GET_API(CallReplyStringPtr);\n    REDISMODULE_GET_API(CreateStringFromCallReply);\n    REDISMODULE_GET_API(CreateString);\n    REDISMODULE_GET_API(CreateStringFromLongLong);\n    REDISMODULE_GET_API(CreateStringFromDouble);\n    REDISMODULE_GET_API(CreateStringFromLongDouble);\n    REDISMODULE_GET_API(CreateStringFromString);\n    REDISMODULE_GET_API(CreateStringPrintf);\n    REDISMODULE_GET_API(FreeString);\n    REDISMODULE_GET_API(StringPtrLen);\n    REDISMODULE_GET_API(AutoMemory);\n    REDISMODULE_GET_API(Replicate);\n    REDISMODULE_GET_API(ReplicateVerbatim);\n    REDISMODULE_GET_API(DeleteKey);\n    REDISMODULE_GET_API(UnlinkKey);\n    REDISMODULE_GET_API(StringSet);\n    REDISMODULE_GET_API(StringDMA);\n    REDISMODULE_GET_API(StringTruncate);\n    REDISMODULE_GET_API(GetExpire);\n    REDISMODULE_GET_API(SetExpire);\n    REDISMODULE_GET_API(ResetDataset);\n    REDISMODULE_GET_API(DbSize);\n    REDISMODULE_GET_API(RandomKey);\n    REDISMODULE_GET_API(ZsetAdd);\n    REDISMODULE_GET_API(ZsetIncrby);\n    REDISMODULE_GET_API(ZsetScore);\n    REDISMODULE_GET_API(ZsetRem);\n    REDISMODULE_GET_API(ZsetRangeStop);\n    REDISMODULE_GET_API(ZsetFirstInScoreRange);\n    REDISMODULE_GET_API(ZsetLastInScoreRange);\n    REDISMODULE_GET_API(ZsetFirstInLexRange);\n    REDISMODULE_GET_API(ZsetLastInLexRange);\n    REDISMODULE_GET_API(ZsetRangeCurrentElement);\n    REDISMODULE_GET_API(ZsetRangeNext);\n    REDISMODULE_GET_API(ZsetRangePrev);\n    REDISMODULE_GET_API(ZsetRangeEndReached);\n    REDISMODULE_GET_API(HashSet);\n    REDISMODULE_GET_API(HashGet);\n    REDISMODULE_GET_API(IsKeysPositionRequest);\n    REDISMODULE_GET_API(KeyAtPos);\n    REDISMODULE_GET_API(GetClientId);\n    REDISMODULE_GET_API(GetContextFlags);\n    REDISMODULE_GET_API(AvoidReplicaTraffic);\n    REDISMODULE_GET_API(PoolAlloc);\n    REDISMODULE_GET_API(CreateDataType);\n    REDISMODULE_GET_API(ModuleTypeSetValue);\n    REDISMODULE_GET_API(ModuleTypeReplaceValue);\n    REDISMODULE_GET_API(ModuleTypeGetType);\n    REDISMODULE_GET_API(ModuleTypeGetValue);\n    REDISMODULE_GET_API(IsIOError);\n    REDISMODULE_GET_API(SetModuleOptions);\n    REDISMODULE_GET_API(SignalModifiedKey);\n    REDISMODULE_GET_API(SaveUnsigned);\n    REDISMODULE_GET_API(LoadUnsigned);\n    REDISMODULE_GET_API(SaveSigned);\n    REDISMODULE_GET_API(LoadSigned);\n    REDISMODULE_GET_API(SaveString);\n    REDISMODULE_GET_API(SaveStringBuffer);\n    REDISMODULE_GET_API(LoadString);\n    REDISMODULE_GET_API(LoadStringBuffer);\n    REDISMODULE_GET_API(SaveDouble);\n    REDISMODULE_GET_API(LoadDouble);\n    REDISMODULE_GET_API(SaveFloat);\n    REDISMODULE_GET_API(LoadFloat);\n    REDISMODULE_GET_API(SaveLongDouble);\n    REDISMODULE_GET_API(LoadLongDouble);\n    REDISMODULE_GET_API(SaveDataTypeToString);\n    REDISMODULE_GET_API(LoadDataTypeFromString);\n    REDISMODULE_GET_API(EmitAOF);\n    REDISMODULE_GET_API(Log);\n    REDISMODULE_GET_API(LogIOError);\n    REDISMODULE_GET_API(_Assert);\n    REDISMODULE_GET_API(LatencyAddSample);\n    REDISMODULE_GET_API(StringAppendBuffer);\n    REDISMODULE_GET_API(RetainString);\n    REDISMODULE_GET_API(StringCompare);\n    REDISMODULE_GET_API(GetContextFromIO);\n    REDISMODULE_GET_API(GetKeyNameFromIO);\n    REDISMODULE_GET_API(GetKeyNameFromModuleKey);\n    REDISMODULE_GET_API(Milliseconds);\n    REDISMODULE_GET_API(DigestAddStringBuffer);\n    REDISMODULE_GET_API(DigestAddLongLong);\n    REDISMODULE_GET_API(DigestEndSequence);\n    REDISMODULE_GET_API(CreateDict);\n    REDISMODULE_GET_API(FreeDict);\n    REDISMODULE_GET_API(DictSize);\n    REDISMODULE_GET_API(DictSetC);\n    REDISMODULE_GET_API(DictReplaceC);\n    REDISMODULE_GET_API(DictSet);\n    REDISMODULE_GET_API(DictReplace);\n    REDISMODULE_GET_API(DictGetC);\n    REDISMODULE_GET_API(DictGet);\n    REDISMODULE_GET_API(DictDelC);\n    REDISMODULE_GET_API(DictDel);\n    REDISMODULE_GET_API(DictIteratorStartC);\n    REDISMODULE_GET_API(DictIteratorStart);\n    REDISMODULE_GET_API(DictIteratorStop);\n    REDISMODULE_GET_API(DictIteratorReseekC);\n    REDISMODULE_GET_API(DictIteratorReseek);\n    REDISMODULE_GET_API(DictNextC);\n    REDISMODULE_GET_API(DictPrevC);\n    REDISMODULE_GET_API(DictNext);\n    REDISMODULE_GET_API(DictPrev);\n    REDISMODULE_GET_API(DictCompare);\n    REDISMODULE_GET_API(DictCompareC);\n    REDISMODULE_GET_API(RegisterInfoFunc);\n    REDISMODULE_GET_API(InfoAddSection);\n    REDISMODULE_GET_API(InfoBeginDictField);\n    REDISMODULE_GET_API(InfoEndDictField);\n    REDISMODULE_GET_API(InfoAddFieldString);\n    REDISMODULE_GET_API(InfoAddFieldCString);\n    REDISMODULE_GET_API(InfoAddFieldDouble);\n    REDISMODULE_GET_API(InfoAddFieldLongLong);\n    REDISMODULE_GET_API(InfoAddFieldULongLong);\n    REDISMODULE_GET_API(GetServerInfo);\n    REDISMODULE_GET_API(FreeServerInfo);\n    REDISMODULE_GET_API(ServerInfoGetField);\n    REDISMODULE_GET_API(ServerInfoGetFieldC);\n    REDISMODULE_GET_API(ServerInfoGetFieldSigned);\n    REDISMODULE_GET_API(ServerInfoGetFieldUnsigned);\n    REDISMODULE_GET_API(ServerInfoGetFieldDouble);\n    REDISMODULE_GET_API(GetClientInfoById);\n    REDISMODULE_GET_API(PublishMessage);\n    REDISMODULE_GET_API(SubscribeToServerEvent);\n    REDISMODULE_GET_API(SetLRU);\n    REDISMODULE_GET_API(GetLRU);\n    REDISMODULE_GET_API(SetLFU);\n    REDISMODULE_GET_API(GetLFU);\n    REDISMODULE_GET_API(BlockClientOnKeys);\n    REDISMODULE_GET_API(SignalKeyAsReady);\n    REDISMODULE_GET_API(GetBlockedClientReadyKey);\n    REDISMODULE_GET_API(ScanCursorCreate);\n    REDISMODULE_GET_API(ScanCursorRestart);\n    REDISMODULE_GET_API(ScanCursorDestroy);\n    REDISMODULE_GET_API(Scan);\n    REDISMODULE_GET_API(ScanKey);\n\n#ifdef REDISMODULE_EXPERIMENTAL_API\n    REDISMODULE_GET_API(GetThreadSafeContext);\n    REDISMODULE_GET_API(FreeThreadSafeContext);\n    REDISMODULE_GET_API(ThreadSafeContextLock);\n    REDISMODULE_GET_API(ThreadSafeContextUnlock);\n    REDISMODULE_GET_API(BlockClient);\n    REDISMODULE_GET_API(UnblockClient);\n    REDISMODULE_GET_API(IsBlockedReplyRequest);\n    REDISMODULE_GET_API(IsBlockedTimeoutRequest);\n    REDISMODULE_GET_API(GetBlockedClientPrivateData);\n    REDISMODULE_GET_API(GetBlockedClientHandle);\n    REDISMODULE_GET_API(AbortBlock);\n    REDISMODULE_GET_API(SetDisconnectCallback);\n    REDISMODULE_GET_API(SubscribeToKeyspaceEvents);\n    REDISMODULE_GET_API(NotifyKeyspaceEvent);\n    REDISMODULE_GET_API(GetNotifyKeyspaceEvents);\n    REDISMODULE_GET_API(BlockedClientDisconnected);\n    REDISMODULE_GET_API(RegisterClusterMessageReceiver);\n    REDISMODULE_GET_API(SendClusterMessage);\n    REDISMODULE_GET_API(GetClusterNodeInfo);\n    REDISMODULE_GET_API(GetClusterNodesList);\n    REDISMODULE_GET_API(FreeClusterNodesList);\n    REDISMODULE_GET_API(CreateTimer);\n    REDISMODULE_GET_API(StopTimer);\n    REDISMODULE_GET_API(GetTimerInfo);\n    REDISMODULE_GET_API(GetMyClusterID);\n    REDISMODULE_GET_API(GetClusterSize);\n    REDISMODULE_GET_API(GetRandomBytes);\n    REDISMODULE_GET_API(GetRandomHexChars);\n    REDISMODULE_GET_API(SetClusterFlags);\n    REDISMODULE_GET_API(ExportSharedAPI);\n    REDISMODULE_GET_API(GetSharedAPI);\n    REDISMODULE_GET_API(RegisterCommandFilter);\n    REDISMODULE_GET_API(UnregisterCommandFilter);\n    REDISMODULE_GET_API(CommandFilterArgsCount);\n    REDISMODULE_GET_API(CommandFilterArgGet);\n    REDISMODULE_GET_API(CommandFilterArgInsert);\n    REDISMODULE_GET_API(CommandFilterArgReplace);\n    REDISMODULE_GET_API(CommandFilterArgDelete);\n    REDISMODULE_GET_API(Fork);\n    REDISMODULE_GET_API(ExitFromChild);\n    REDISMODULE_GET_API(KillForkChild);\n    REDISMODULE_GET_API(GetUsedMemoryRatio);\n    REDISMODULE_GET_API(MallocSize);\n    REDISMODULE_GET_API(CreateModuleUser);\n    REDISMODULE_GET_API(FreeModuleUser);\n    REDISMODULE_GET_API(SetModuleUserACL);\n    REDISMODULE_GET_API(DeauthenticateAndCloseClient);\n    REDISMODULE_GET_API(AuthenticateClientWithACLUser);\n    REDISMODULE_GET_API(AuthenticateClientWithUser);\n#endif\n\n    if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;\n    RedisModule_SetModuleAttribs(ctx,name,ver,apiver);\n    return REDISMODULE_OK;\n}\n\n#define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1)))\n\n#else\n\n/* Things only defined for the modules core, not exported to modules\n * including this file. */\n#define RedisModuleString robj\n\n#endif /* REDISMODULE_CORE */\n#endif /* REDISMOUDLE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <stdio.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\n/* Return a cached value of the build string in order to avoid recomputing\n * and converting it in hex every time: this string is shown in the INFO\n * output that should be fast. */\nchar *redisBuildIdString(void) {\n    static char buf[32];\n    static int cached = 0;\n    if (!cached) {\n        snprintf(buf,sizeof(buf),\"%llx\",(unsigned long long) redisBuildId());\n        cached = 1;\n    }\n    return buf;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/replication.c",
    "content": "/* Asynchronous replication 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\n#include \"server.h\"\n#include \"cluster.h\"\n#include \"bio.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\nlong long adjustMeaningfulReplOffset();\nvoid replicationDiscardCachedMaster(void);\nvoid replicationResurrectCachedMaster(connection *conn);\nvoid replicationSendAck(void);\nvoid putSlaveOnline(client *slave);\nint cancelReplicationHandshake(void);\n\n/* We take a global flag to remember if this instance generated an RDB\n * because of replication, so that we can remove the RDB file in case\n * the instance is configured to have no persistence. */\nint RDBGeneratedByReplication = 0;\n\n/* --------------------------- Utility functions ---------------------------- */\n\n/* Return the pointer to a string representing the slave ip:listening_port\n * pair. Mostly useful for logging, since we want to log a slave using its\n * IP address and its listening port which is more clear for the user, for\n * example: \"Closing connection with replica 10.1.2.3:6380\". */\nchar *replicationGetSlaveName(client *c) {\n    static char buf[NET_PEER_ID_LEN];\n    char ip[NET_IP_STR_LEN];\n\n    ip[0] = '\\0';\n    buf[0] = '\\0';\n    if (c->slave_ip[0] != '\\0' ||\n        connPeerToString(c->conn,ip,sizeof(ip),NULL) != -1)\n    {\n        /* Note that the 'ip' buffer is always larger than 'c->slave_ip' */\n        if (c->slave_ip[0] != '\\0') memcpy(ip,c->slave_ip,sizeof(c->slave_ip));\n\n        if (c->slave_listening_port)\n            anetFormatAddr(buf,sizeof(buf),ip,c->slave_listening_port);\n        else\n            snprintf(buf,sizeof(buf),\"%s:<unknown-replica-port>\",ip);\n    } else {\n        snprintf(buf,sizeof(buf),\"client id #%llu\",\n            (unsigned long long) c->id);\n    }\n    return buf;\n}\n\n/* Plain unlink() can block for quite some time in order to actually apply\n * the file deletion to the filesystem. This call removes the file in a\n * background thread instead. We actually just do close() in the thread,\n * by using the fact that if there is another instance of the same file open,\n * the foreground unlink() will not really do anything, and deleting the\n * file will only happen once the last reference is lost. */\nint bg_unlink(const char *filename) {\n    int fd = open(filename,O_RDONLY|O_NONBLOCK);\n    if (fd == -1) {\n        /* Can't open the file? Fall back to unlinking in the main thread. */\n        return unlink(filename);\n    } else {\n        /* The following unlink() will not do anything since file\n         * is still open. */\n        int retval = unlink(filename);\n        if (retval == -1) {\n            /* If we got an unlink error, we just return it, closing the\n             * new reference we have to the file. */\n            int old_errno = errno;\n            close(fd);  /* This would overwrite our errno. So we saved it. */\n            errno = old_errno;\n            return -1;\n        }\n        bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)fd,NULL,NULL);\n        return 0; /* Success. */\n    }\n}\n\n/* ---------------------------------- MASTER -------------------------------- */\n\nvoid createReplicationBacklog(void) {\n    serverAssert(server.repl_backlog == NULL);\n    server.repl_backlog = zmalloc(server.repl_backlog_size);\n    server.repl_backlog_histlen = 0;\n    server.repl_backlog_idx = 0;\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    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). */\nvoid resizeReplicationBacklog(long long newsize) {\n    if (newsize < CONFIG_REPL_BACKLOG_MIN_SIZE)\n        newsize = CONFIG_REPL_BACKLOG_MIN_SIZE;\n    if (server.repl_backlog_size == newsize) return;\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        zfree(server.repl_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 empty. */\n        server.repl_backlog_off = server.master_repl_offset+1;\n    }\n}\n\nvoid freeReplicationBacklog(void) {\n    serverAssert(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 offset. */\nvoid feedReplicationBacklog(void *ptr, size_t len) {\n    unsigned char *p = ptr;\n\n    server.master_repl_offset += len;\n    server.master_repl_meaningful_offset = server.master_repl_offset;\n\n    /* This is a circular buffer, so write as much data we can at every\n     * iteration and rewind the \"idx\" index if we reach the limit. */\n    while(len) {\n        size_t thislen = server.repl_backlog_size - server.repl_backlog_idx;\n        if (thislen > len) thislen = len;\n        memcpy(server.repl_backlog+server.repl_backlog_idx,p,thislen);\n        server.repl_backlog_idx += thislen;\n        if (server.repl_backlog_idx == server.repl_backlog_size)\n            server.repl_backlog_idx = 0;\n        len -= thislen;\n        p += thislen;\n        server.repl_backlog_histlen += thislen;\n    }\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    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. */\nvoid feedReplicationBacklogWithObject(robj *o) {\n    char llstr[LONG_STR_SIZE];\n    void *p;\n    size_t len;\n\n    if (o->encoding == OBJ_ENCODING_INT) {\n        len = ll2string(llstr,sizeof(llstr),(long)o->ptr);\n        p = llstr;\n    } else {\n        len = sdslen(o->ptr);\n        p = o->ptr;\n    }\n    feedReplicationBacklog(p,len);\n}\n\n/* Propagate write commands to slaves, and populate the replication backlog\n * as well. This function is used if the instance is a master: we use\n * the commands received by our clients in order to create the replication\n * stream. Instead if the instance is a slave and has sub-slaves attached,\n * we use replicationFeedSlavesFromMaster() */\nvoid replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {\n    listNode *ln;\n    listIter li;\n    int j, len;\n    char llstr[LONG_STR_SIZE];\n\n    /* If the instance is not a top level master, return ASAP: we'll just proxy\n     * the stream of data we receive from our master instead, in order to\n     * propagate *identical* replication stream. In this way this slave can\n     * advertise the same replication ID as the master (since it shares the\n     * master replication history and has the same backlog and offsets). */\n    if (server.masterhost != NULL) return;\n\n    /* If there aren't slaves, and there is no backlog buffer to populate,\n     * we can return ASAP. */\n    if (server.repl_backlog == NULL && listLength(slaves) == 0) return;\n\n    /* We can't have slaves attached and no backlog. */\n    serverAssert(!(listLength(slaves) != 0 && server.repl_backlog == NULL));\n\n    /* Send SELECT command to every slave if needed. */\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 < PROTO_SHARED_SELECT_CMDS) {\n            selectcmd = shared.select[dictid];\n        } else {\n            int dictid_len;\n\n            dictid_len = ll2string(llstr,sizeof(llstr),dictid);\n            selectcmd = createObject(OBJ_STRING,\n                sdscatprintf(sdsempty(),\n                \"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n                dictid_len, llstr));\n        }\n\n        /* Add the SELECT command into the backlog. */\n        if (server.repl_backlog) feedReplicationBacklogWithObject(selectcmd);\n\n        /* Send it to slaves. */\n        listRewind(slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n            addReply(slave,selectcmd);\n        }\n\n        if (dictid < 0 || dictid >= PROTO_SHARED_SELECT_CMDS)\n            decrRefCount(selectcmd);\n    }\n    server.slaveseldb = dictid;\n\n    /* Write the command to the replication backlog if any. */\n    if (server.repl_backlog) {\n        char aux[LONG_STR_SIZE+3];\n\n        /* Add the multi bulk reply length. */\n        aux[0] = '*';\n        len = ll2string(aux+1,sizeof(aux)-1,argc);\n        aux[len+1] = '\\r';\n        aux[len+2] = '\\n';\n        feedReplicationBacklog(aux,len+3);\n\n        for (j = 0; j < argc; j++) {\n            long objlen = stringObjectLen(argv[j]);\n\n            /* We need to feed the buffer with the object as a bulk reply\n             * not just as a plain string, so create the $..CRLF payload len\n             * and add the final CRLF */\n            aux[0] = '$';\n            len = ll2string(aux+1,sizeof(aux)-1,objlen);\n            aux[len+1] = '\\r';\n            aux[len+2] = '\\n';\n            feedReplicationBacklog(aux,len+3);\n            feedReplicationBacklogWithObject(argv[j]);\n            feedReplicationBacklog(aux+len+1,2);\n        }\n    }\n\n    /* Write the command to every slave. */\n    listRewind(slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        /* Don't feed slaves that are still waiting for BGSAVE to start. */\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n\n        /* Feed slaves that are waiting for the initial SYNC (so these commands\n         * are queued in the output buffer until the initial SYNC completes),\n         * or are already in sync with the master. */\n\n        /* Add the multi bulk length. */\n        addReplyArrayLen(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/* This function is used in order to proxy what we receive from our master\n * to our sub-slaves. */\n#include <ctype.h>\nvoid replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen) {\n    listNode *ln;\n    listIter li;\n\n    /* Debugging: this is handy to see the stream sent from master\n     * to slaves. Disabled with if(0). */\n    if (0) {\n        printf(\"%zu:\",buflen);\n        for (size_t j = 0; j < buflen; j++) {\n            printf(\"%c\", isprint(buf[j]) ? buf[j] : '.');\n        }\n        printf(\"\\n\");\n    }\n\n    if (server.repl_backlog) feedReplicationBacklog(buf,buflen);\n    listRewind(slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        /* Don't feed slaves that are still waiting for BGSAVE to start. */\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n        addReplyProto(slave,buf,buflen);\n    }\n}\n\nvoid replicationFeedMonitors(client *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    gettimeofday(&tv,NULL);\n    cmdrepr = sdscatprintf(cmdrepr,\"%ld.%06ld \",(long)tv.tv_sec,(long)tv.tv_usec);\n    if (c->flags & CLIENT_LUA) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d lua] \",dictid);\n    } else if (c->flags & CLIENT_UNIX_SOCKET) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d unix:%s] \",dictid,server.unixsocket);\n    } else {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d %s] \",dictid,getClientPeerId(c));\n    }\n\n    for (j = 0; j < argc; j++) {\n        if (argv[j]->encoding == OBJ_ENCODING_INT) {\n            cmdrepr = sdscatprintf(cmdrepr, \"\\\"%ld\\\"\", (long)argv[j]->ptr);\n        } else {\n            cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr,\n                        sdslen(argv[j]->ptr));\n        }\n        if (j != argc-1)\n            cmdrepr = sdscatlen(cmdrepr,\" \",1);\n    }\n    cmdrepr = sdscatlen(cmdrepr,\"\\r\\n\",2);\n    cmdobj = createObject(OBJ_STRING,cmdrepr);\n\n    listRewind(monitors,&li);\n    while((ln = listNext(&li))) {\n        client *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. */\nlong long addReplyReplicationBacklog(client *c, long long offset) {\n    long long j, skip, len;\n\n    serverLog(LL_DEBUG, \"[PSYNC] Replica request offset: %lld\", offset);\n\n    if (server.repl_backlog_histlen == 0) {\n        serverLog(LL_DEBUG, \"[PSYNC] Backlog history len is zero\");\n        return 0;\n    }\n\n    serverLog(LL_DEBUG, \"[PSYNC] Backlog size: %lld\",\n             server.repl_backlog_size);\n    serverLog(LL_DEBUG, \"[PSYNC] First byte: %lld\",\n             server.repl_backlog_off);\n    serverLog(LL_DEBUG, \"[PSYNC] History len: %lld\",\n             server.repl_backlog_histlen);\n    serverLog(LL_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    serverLog(LL_DEBUG, \"[PSYNC] Skipping: %lld\", skip);\n\n    /* Point j to the oldest byte, that is actually 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    serverLog(LL_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    serverLog(LL_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        serverLog(LL_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/* Return the offset to provide as reply to the PSYNC command received\n * from the slave. The returned value is only valid immediately after\n * the BGSAVE process started and before executing any other command\n * from clients. */\nlong long getPsyncInitialOffset(void) {\n    return server.master_repl_offset;\n}\n\n/* Send a FULLRESYNC reply in the specific case of a full resynchronization,\n * as a side effect setup the slave for a full sync in different ways:\n *\n * 1) Remember, into the slave client structure, the replication offset\n *    we sent here, so that if new slaves will later attach to the same\n *    background RDB saving process (by duplicating this client output\n *    buffer), we can get the right offset from this slave.\n * 2) Set the replication state of the slave to WAIT_BGSAVE_END so that\n *    we start accumulating differences from this point.\n * 3) Force the replication stream to re-emit a SELECT statement so\n *    the new slave incremental differences will start selecting the\n *    right database number.\n *\n * Normally this function should be called immediately after a successful\n * BGSAVE for replication was started, or when there is one already in\n * progress that we attached our slave to. */\nint replicationSetupSlaveForFullResync(client *slave, long long offset) {\n    char buf[128];\n    int buflen;\n\n    slave->psync_initial_offset = offset;\n    slave->replstate = SLAVE_STATE_WAIT_BGSAVE_END;\n    /* We are going to accumulate the incremental changes for this\n     * slave as well. Set slaveseldb to -1 in order to force to re-emit\n     * a SELECT statement in the replication stream. */\n    server.slaveseldb = -1;\n\n    /* Don't send this reply to slaves that approached us with\n     * the old SYNC command. */\n    if (!(slave->flags & CLIENT_PRE_PSYNC)) {\n        buflen = snprintf(buf,sizeof(buf),\"+FULLRESYNC %s %lld\\r\\n\",\n                          server.replid,offset);\n        if (connWrite(slave->conn,buf,buflen) != buflen) {\n            freeClientAsync(slave);\n            return C_ERR;\n        }\n    }\n    return C_OK;\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 C_OK, otherwise C_ERR is returned and we proceed\n * with the usual full resync. */\nint masterTryPartialResynchronization(client *c) {\n    long long psync_offset, psync_len;\n    char *master_replid = c->argv[1]->ptr;\n    char buf[128];\n    int buflen;\n\n    /* Parse the replication offset asked by the slave. Go to full sync\n     * on parse error: this should never happen but we try to handle\n     * it in a robust way compared to aborting. */\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&psync_offset,NULL) !=\n       C_OK) goto need_full_resync;\n\n    /* Is the replication ID of this master the same advertised by the wannabe\n     * slave via PSYNC? If the replication ID changed this master has a\n     * different replication history, and there is no way to continue.\n     *\n     * Note that there are two potentially valid replication IDs: the ID1\n     * and the ID2. The ID2 however is only valid up to a specific offset. */\n    if (strcasecmp(master_replid, server.replid) &&\n        (strcasecmp(master_replid, server.replid2) ||\n         psync_offset > server.second_replid_offset))\n    {\n        /* Run id \"?\" is used by slaves that want to force a full resync. */\n        if (master_replid[0] != '?') {\n            if (strcasecmp(master_replid, server.replid) &&\n                strcasecmp(master_replid, server.replid2))\n            {\n                serverLog(LL_NOTICE,\"Partial resynchronization not accepted: \"\n                    \"Replication ID mismatch (Replica asked for '%s', my \"\n                    \"replication IDs are '%s' and '%s')\",\n                    master_replid, server.replid, server.replid2);\n            } else {\n                serverLog(LL_NOTICE,\"Partial resynchronization not accepted: \"\n                    \"Requested offset for second ID was %lld, but I can reply \"\n                    \"up to %lld\", psync_offset, server.second_replid_offset);\n            }\n        } else {\n            serverLog(LL_NOTICE,\"Full resync requested by replica %s\",\n                replicationGetSlaveName(c));\n        }\n        goto need_full_resync;\n    }\n\n    /* We still have the data our slave is asking for? */\n    if (!server.repl_backlog ||\n        psync_offset < server.repl_backlog_off ||\n        psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen))\n    {\n        serverLog(LL_NOTICE,\n            \"Unable to partial resync with replica %s for lack of backlog (Replica request was: %lld).\", replicationGetSlaveName(c), psync_offset);\n        if (psync_offset > server.master_repl_offset) {\n            serverLog(LL_WARNING,\n                \"Warning: replica %s tried to PSYNC with an offset that is greater than the master replication offset.\", replicationGetSlaveName(c));\n        }\n        goto need_full_resync;\n    }\n\n    /* If we reached this point, we are able to perform a partial resync:\n     * 1) Set client state to make it a slave.\n     * 2) Inform the client we can continue with +CONTINUE\n     * 3) Send the backlog data (from the offset to the end) to the slave. */\n    c->flags |= CLIENT_SLAVE;\n    c->replstate = SLAVE_STATE_ONLINE;\n    c->repl_ack_time = server.unixtime;\n    c->repl_put_online_on_ack = 0;\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     * empty so this write will never fail actually. */\n    if (c->slave_capa & SLAVE_CAPA_PSYNC2) {\n        buflen = snprintf(buf,sizeof(buf),\"+CONTINUE %s\\r\\n\", server.replid);\n    } else {\n        buflen = snprintf(buf,sizeof(buf),\"+CONTINUE\\r\\n\");\n    }\n    if (connWrite(c->conn,buf,buflen) != buflen) {\n        freeClientAsync(c);\n        return C_OK;\n    }\n    psync_len = addReplyReplicationBacklog(c,psync_offset);\n    serverLog(LL_NOTICE,\n        \"Partial resynchronization request from %s accepted. Sending %lld bytes of backlog starting from offset %lld.\",\n            replicationGetSlaveName(c),\n            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    refreshGoodSlavesCount();\n\n    /* Fire the replica change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,\n                          REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE,\n                          NULL);\n\n    return C_OK; /* The caller can return, no full resync needed. */\n\nneed_full_resync:\n    /* We need a full resync for some reason... Note that we can't\n     * reply to PSYNC right now if a full SYNC is needed. The reply\n     * must include the master offset at the time the RDB file we transfer\n     * is generated, so we need to delay the reply to that moment. */\n    return C_ERR;\n}\n\n/* Start a BGSAVE for replication goals, which is, selecting the disk or\n * socket target depending on the configuration, and making sure that\n * the script cache is flushed before to start.\n *\n * The mincapa argument is the bitwise AND among all the slaves capabilities\n * of the slaves waiting for this BGSAVE, so represents the slave capabilities\n * all the slaves support. Can be tested via SLAVE_CAPA_* macros.\n *\n * Side effects, other than starting a BGSAVE:\n *\n * 1) Handle the slaves in WAIT_START state, by preparing them for a full\n *    sync if the BGSAVE was successfully started, or sending them an error\n *    and dropping them from the list of slaves.\n *\n * 2) Flush the Lua scripting script cache if the BGSAVE was actually\n *    started.\n *\n * Returns C_OK on success or C_ERR otherwise. */\nint startBgsaveForReplication(int mincapa) {\n    int retval;\n    int socket_target = server.repl_diskless_sync && (mincapa & SLAVE_CAPA_EOF);\n    listIter li;\n    listNode *ln;\n\n    serverLog(LL_NOTICE,\"Starting BGSAVE for SYNC with target: %s\",\n        socket_target ? \"replicas sockets\" : \"disk\");\n\n    rdbSaveInfo rsi, *rsiptr;\n    rsiptr = rdbPopulateSaveInfo(&rsi);\n    /* Only do rdbSave* when rsiptr is not NULL,\n     * otherwise slave will miss repl-stream-db. */\n    if (rsiptr) {\n        if (socket_target)\n            retval = rdbSaveToSlavesSockets(rsiptr);\n        else\n            retval = rdbSaveBackground(server.rdb_filename,rsiptr);\n    } else {\n        serverLog(LL_WARNING,\"BGSAVE for replication: replication information not available, can't generate the RDB file right now. Try later.\");\n        retval = C_ERR;\n    }\n\n    /* If we succeeded to start a BGSAVE with disk target, let's remember\n     * this fact, so that we can later delete the file if needed. Note\n     * that we don't set the flag to 1 if the feature is disabled, otherwise\n     * it would never be cleared: the file is not deleted. This way if\n     * the user enables it later with CONFIG SET, we are fine. */\n    if (retval == C_OK && !socket_target && server.rdb_del_sync_files)\n        RDBGeneratedByReplication = 1;\n\n    /* If we failed to BGSAVE, remove the slaves waiting for a full\n     * resynchronization from the list of slaves, inform them with\n     * an error about what happened, close the connection ASAP. */\n    if (retval == C_ERR) {\n        serverLog(LL_WARNING,\"BGSAVE for replication failed\");\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n                slave->replstate = REPL_STATE_NONE;\n                slave->flags &= ~CLIENT_SLAVE;\n                listDelNode(server.slaves,ln);\n                addReplyError(slave,\n                    \"BGSAVE failed, replication can't continue\");\n                slave->flags |= CLIENT_CLOSE_AFTER_REPLY;\n            }\n        }\n        return retval;\n    }\n\n    /* If the target is socket, rdbSaveToSlavesSockets() already setup\n     * the slaves for a full resync. Otherwise for disk target do it now.*/\n    if (!socket_target) {\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n                    replicationSetupSlaveForFullResync(slave,\n                            getPsyncInitialOffset());\n            }\n        }\n    }\n\n    /* Flush the script cache, since we need that slave differences are\n     * accumulated without requiring slaves to match our cached scripts. */\n    if (retval == C_OK) replicationScriptCacheFlush();\n    return retval;\n}\n\n/* SYNC and PSYNC command implemenation. */\nvoid syncCommand(client *c) {\n    /* ignore SYNC if already slave or in monitor mode */\n    if (c->flags & CLIENT_SLAVE) return;\n\n    /* Refuse SYNC requests if we are a slave but the link with our master\n     * is not ok... */\n    if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED) {\n        addReplySds(c,sdsnew(\"-NOMASTERLINK Can't SYNC while not connected with my master\\r\\n\"));\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    if (clientHasPendingReplies(c)) {\n        addReplyError(c,\"SYNC and PSYNC are invalid with pending output\");\n        return;\n    }\n\n    serverLog(LL_NOTICE,\"Replica %s asks for synchronization\",\n        replicationGetSlaveName(c));\n\n    /* Try a partial resynchronization if this is a PSYNC command.\n     * If it fails, we continue with usual full resynchronization, however\n     * when this happens masterTryPartialResynchronization() already\n     * replied with:\n     *\n     * +FULLRESYNC <replid> <offset>\n     *\n     * So the slave knows the new replid and offset to try a PSYNC later\n     * if the connection with the master is lost. */\n    if (!strcasecmp(c->argv[0]->ptr,\"psync\")) {\n        if (masterTryPartialResynchronization(c) == C_OK) {\n            server.stat_sync_partial_ok++;\n            return; /* No full resync needed, return. */\n        } else {\n            char *master_replid = c->argv[1]->ptr;\n\n            /* Increment stats for failed PSYNCs, but only if the\n             * replid 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_replid[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        c->flags |= CLIENT_PRE_PSYNC;\n    }\n\n    /* Full resynchronization. */\n    server.stat_sync_full++;\n\n    /* Setup the slave as one waiting for BGSAVE to start. The following code\n     * paths will change the state if we handle the slave differently. */\n    c->replstate = SLAVE_STATE_WAIT_BGSAVE_START;\n    if (server.repl_disable_tcp_nodelay)\n        connDisableTcpNoDelay(c->conn); /* Non critical if it fails. */\n    c->repldbfd = -1;\n    c->flags |= CLIENT_SLAVE;\n    listAddNodeTail(server.slaves,c);\n\n    /* Create the replication backlog if needed. */\n    if (listLength(server.slaves) == 1 && server.repl_backlog == NULL) {\n        /* When we create the backlog from scratch, we always use a new\n         * replication ID and clear the ID2, since there is no valid\n         * past history. */\n        changeReplicationId();\n        clearReplicationId2();\n        createReplicationBacklog();\n    }\n\n    /* CASE 1: BGSAVE is in progress, with disk target. */\n    if (server.rdb_child_pid != -1 &&\n        server.rdb_child_type == RDB_CHILD_TYPE_DISK)\n    {\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        client *slave;\n        listNode *ln;\n        listIter li;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) break;\n        }\n        /* To attach this slave, we check that it has at least all the\n         * capabilities of the slave that triggered the current BGSAVE. */\n        if (ln && ((c->slave_capa & slave->slave_capa) == slave->slave_capa)) {\n            /* Perfect, the server is already registering differences for\n             * another slave. Set the right state, and copy the buffer. */\n            copyClientOutputBuffer(c,slave);\n            replicationSetupSlaveForFullResync(c,slave->psync_initial_offset);\n            serverLog(LL_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            serverLog(LL_NOTICE,\"Can't attach the replica to the current BGSAVE. Waiting for next BGSAVE for SYNC\");\n        }\n\n    /* CASE 2: BGSAVE is in progress, with socket target. */\n    } else if (server.rdb_child_pid != -1 &&\n               server.rdb_child_type == RDB_CHILD_TYPE_SOCKET)\n    {\n        /* There is an RDB child process but it is writing directly to\n         * children sockets. We need to wait for the next BGSAVE\n         * in order to synchronize. */\n        serverLog(LL_NOTICE,\"Current BGSAVE has socket target. Waiting for next BGSAVE for SYNC\");\n\n    /* CASE 3: There is no BGSAVE is progress. */\n    } else {\n        if (server.repl_diskless_sync && (c->slave_capa & SLAVE_CAPA_EOF)) {\n            /* Diskless replication RDB child is created inside\n             * replicationCron() since we want to delay its start a\n             * few seconds to wait for more slaves to arrive. */\n            if (server.repl_diskless_sync_delay)\n                serverLog(LL_NOTICE,\"Delay next BGSAVE for diskless SYNC\");\n        } else {\n            /* Target is disk (or the slave is not capable of supporting\n             * diskless replication) and we don't have a BGSAVE in progress,\n             * let's start one. */\n            if (!hasActiveChildProcess()) {\n                startBgsaveForReplication(c->slave_capa);\n            } else {\n                serverLog(LL_NOTICE,\n                    \"No BGSAVE in progress, but another BG operation is active. \"\n                    \"BGSAVE for replication delayed\");\n            }\n        }\n    }\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 * Currently the only use of this command is to communicate to the master\n * what is the listening port of the Slave redis instance, so that the\n * master can accurately list slaves and their listening ports in\n * the INFO output.\n *\n * In the future the same command can be used in order to configure\n * the replication to initiate an incremental replication instead of a\n * full resync. */\nvoid replconfCommand(client *c) {\n    int j;\n\n    if ((c->argc % 2) == 0) {\n        /* Number of arguments must be odd to make sure that every\n         * option has a corresponding value. */\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Process every option-value pair. */\n    for (j = 1; j < c->argc; j+=2) {\n        if (!strcasecmp(c->argv[j]->ptr,\"listening-port\")) {\n            long port;\n\n            if ((getLongFromObjectOrReply(c,c->argv[j+1],\n                    &port,NULL) != C_OK))\n                return;\n            c->slave_listening_port = port;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"ip-address\")) {\n            sds ip = c->argv[j+1]->ptr;\n            if (sdslen(ip) < sizeof(c->slave_ip)) {\n                memcpy(c->slave_ip,ip,sdslen(ip)+1);\n            } else {\n                addReplyErrorFormat(c,\"REPLCONF ip-address provided by \"\n                    \"replica instance is too long: %zd bytes\", sdslen(ip));\n                return;\n            }\n        } else if (!strcasecmp(c->argv[j]->ptr,\"capa\")) {\n            /* Ignore capabilities not understood by this master. */\n            if (!strcasecmp(c->argv[j+1]->ptr,\"eof\"))\n                c->slave_capa |= SLAVE_CAPA_EOF;\n            else if (!strcasecmp(c->argv[j+1]->ptr,\"psync2\"))\n                c->slave_capa |= SLAVE_CAPA_PSYNC2;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"ack\")) {\n            /* REPLCONF ACK is used by slave to inform the master the amount\n             * of replication stream that it processed so far. It is an\n             * internal only command that normal clients should never use. */\n            long long offset;\n\n            if (!(c->flags & CLIENT_SLAVE)) return;\n            if ((getLongLongFromObject(c->argv[j+1], &offset) != C_OK))\n                return;\n            if (offset > c->repl_ack_off)\n                c->repl_ack_off = offset;\n            c->repl_ack_time = server.unixtime;\n            /* If this was a diskless replication, we need to really put\n             * the slave online when the first ACK is received (which\n             * confirms slave is online and ready to get more data). This\n             * allows for simpler and less CPU intensive EOF detection\n             * when streaming RDB files. */\n            if (c->repl_put_online_on_ack && c->replstate == SLAVE_STATE_ONLINE)\n                putSlaveOnline(c);\n            /* Note: this command does not reply anything! */\n            return;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"getack\")) {\n            /* REPLCONF GETACK is used in order to request an ACK ASAP\n             * to the slave. */\n            if (server.masterhost && server.master) replicationSendAck();\n            return;\n        } else {\n            addReplyErrorFormat(c,\"Unrecognized REPLCONF option: %s\",\n                (char*)c->argv[j]->ptr);\n            return;\n        }\n    }\n    addReply(c,shared.ok);\n}\n\n/* This function puts a replica in the online state, and should be called just\n * after a replica received the RDB file for the initial synchronization, and\n * we are finally ready to send the incremental stream of commands.\n *\n * It does a few things:\n *\n * 1) Put the slave in ONLINE state. Note that the function may also be called\n *    for a replicas that are already in ONLINE state, but having the flag\n *    repl_put_online_on_ack set to true: we still have to install the write\n *    handler in that case. This function will take care of that.\n * 2) Make sure the writable event is re-installed, since calling the SYNC\n *    command disables it, so that we can accumulate output buffer without\n *    sending it to the replica.\n * 3) Update the count of \"good replicas\". */\nvoid putSlaveOnline(client *slave) {\n    slave->replstate = SLAVE_STATE_ONLINE;\n    slave->repl_put_online_on_ack = 0;\n    slave->repl_ack_time = server.unixtime; /* Prevent false timeout. */\n    if (connSetWriteHandler(slave->conn, sendReplyToClient) == C_ERR) {\n        serverLog(LL_WARNING,\"Unable to register writable event for replica bulk transfer: %s\", strerror(errno));\n        freeClient(slave);\n        return;\n    }\n    refreshGoodSlavesCount();\n    /* Fire the replica change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,\n                          REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE,\n                          NULL);\n    serverLog(LL_NOTICE,\"Synchronization with replica %s succeeded\",\n        replicationGetSlaveName(slave));\n}\n\n/* We call this function periodically to remove an RDB file that was\n * generated because of replication, in an instance that is otherwise\n * without any persistence. We don't want instances without persistence\n * to take RDB files around, this violates certain policies in certain\n * environments. */\nvoid removeRDBUsedToSyncReplicas(void) {\n    /* If the feature is disabled, return ASAP but also clear the\n     * RDBGeneratedByReplication flag in case it was set. Otherwise if the\n     * feature was enabled, but gets disabled later with CONFIG SET, the\n     * flag may remain set to one: then next time the feature is re-enabled\n     * via CONFIG SET we have have it set even if no RDB was generated\n     * because of replication recently. */\n    if (!server.rdb_del_sync_files) {\n        RDBGeneratedByReplication = 0;\n        return;\n    }\n\n    if (allPersistenceDisabled() && RDBGeneratedByReplication) {\n        client *slave;\n        listNode *ln;\n        listIter li;\n\n        int delrdb = 1;\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||\n                slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END ||\n                slave->replstate == SLAVE_STATE_SEND_BULK)\n            {\n                delrdb = 0;\n                break; /* No need to check the other replicas. */\n            }\n        }\n        if (delrdb) {\n            struct stat sb;\n            if (lstat(server.rdb_filename,&sb) != -1) {\n                RDBGeneratedByReplication = 0;\n                serverLog(LL_NOTICE,\n                    \"Removing the RDB file used to feed replicas \"\n                    \"in a persistence-less instance\");\n                bg_unlink(server.rdb_filename);\n            }\n        }\n    }\n}\n\nvoid sendBulkToSlave(connection *conn) {\n    client *slave = connGetPrivateData(conn);\n    char buf[PROTO_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 = connWrite(conn,slave->replpreamble,sdslen(slave->replpreamble));\n        if (nwritten == -1) {\n            serverLog(LL_VERBOSE,\n                \"Write error sending RDB preamble to replica: %s\",\n                connGetLastError(conn));\n            freeClient(slave);\n            return;\n        }\n        server.stat_net_output_bytes += nwritten;\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 transferred, send the RDB bulk data. */\n    lseek(slave->repldbfd,slave->repldboff,SEEK_SET);\n    buflen = read(slave->repldbfd,buf,PROTO_IOBUF_LEN);\n    if (buflen <= 0) {\n        serverLog(LL_WARNING,\"Read error sending DB to replica: %s\",\n            (buflen == 0) ? \"premature EOF\" : strerror(errno));\n        freeClient(slave);\n        return;\n    }\n    if ((nwritten = connWrite(conn,buf,buflen)) == -1) {\n        if (connGetState(conn) != CONN_STATE_CONNECTED) {\n            serverLog(LL_WARNING,\"Write error sending DB to replica: %s\",\n                connGetLastError(conn));\n            freeClient(slave);\n        }\n        return;\n    }\n    slave->repldboff += nwritten;\n    server.stat_net_output_bytes += nwritten;\n    if (slave->repldboff == slave->repldbsize) {\n        close(slave->repldbfd);\n        slave->repldbfd = -1;\n        connSetWriteHandler(slave->conn,NULL);\n        putSlaveOnline(slave);\n    }\n}\n\n/* Remove one write handler from the list of connections waiting to be writable\n * during rdb pipe transfer. */\nvoid rdbPipeWriteHandlerConnRemoved(struct connection *conn) {\n    if (!connHasWriteHandler(conn))\n        return;\n    connSetWriteHandler(conn, NULL);\n    server.rdb_pipe_numconns_writing--;\n    /* if there are no more writes for now for this conn, or write error: */\n    if (server.rdb_pipe_numconns_writing == 0) {\n        if (aeCreateFileEvent(server.el, server.rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {\n            serverPanic(\"Unrecoverable error creating server.rdb_pipe_read file event.\");\n        }\n    }\n}\n\n/* Called in diskless master during transfer of data from the rdb pipe, when\n * the replica becomes writable again. */\nvoid rdbPipeWriteHandler(struct connection *conn) {\n    serverAssert(server.rdb_pipe_bufflen>0);\n    client *slave = connGetPrivateData(conn);\n    int nwritten;\n    if ((nwritten = connWrite(conn, server.rdb_pipe_buff + slave->repldboff,\n                              server.rdb_pipe_bufflen - slave->repldboff)) == -1)\n    {\n        if (connGetState(conn) == CONN_STATE_CONNECTED)\n            return; /* equivalent to EAGAIN */\n        serverLog(LL_WARNING,\"Write error sending DB to replica: %s\",\n            connGetLastError(conn));\n        freeClient(slave);\n        return;\n    } else {\n        slave->repldboff += nwritten;\n        server.stat_net_output_bytes += nwritten;\n        if (slave->repldboff < server.rdb_pipe_bufflen)\n            return; /* more data to write.. */\n    }\n    rdbPipeWriteHandlerConnRemoved(conn);\n}\n\n/* When the the pipe serving diskless rdb transfer is drained (write end was\n * closed), we can clean up all the temporary variables, and cleanup after the\n * fork child. */\nvoid RdbPipeCleanup() {\n    close(server.rdb_pipe_read);\n    zfree(server.rdb_pipe_conns);\n    server.rdb_pipe_conns = NULL;\n    server.rdb_pipe_numconns = 0;\n    server.rdb_pipe_numconns_writing = 0;\n    zfree(server.rdb_pipe_buff);\n    server.rdb_pipe_buff = NULL;\n    server.rdb_pipe_bufflen = 0;\n\n    /* Since we're avoiding to detect the child exited as long as the pipe is\n     * not drained, so now is the time to check. */\n    checkChildrenDone();\n}\n\n/* Called in diskless master, when there's data to read from the child's rdb pipe */\nvoid rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask) {\n    UNUSED(mask);\n    UNUSED(clientData);\n    UNUSED(eventLoop);\n    int i;\n    if (!server.rdb_pipe_buff)\n        server.rdb_pipe_buff = zmalloc(PROTO_IOBUF_LEN);\n    serverAssert(server.rdb_pipe_numconns_writing==0);\n\n    while (1) {\n        server.rdb_pipe_bufflen = read(fd, server.rdb_pipe_buff, PROTO_IOBUF_LEN);\n        if (server.rdb_pipe_bufflen < 0) {\n            if (errno == EAGAIN || errno == EWOULDBLOCK)\n                return;\n            serverLog(LL_WARNING,\"Diskless rdb transfer, read error sending DB to replicas: %s\", strerror(errno));\n            for (i=0; i < server.rdb_pipe_numconns; i++) {\n                connection *conn = server.rdb_pipe_conns[i];\n                if (!conn)\n                    continue;\n                client *slave = connGetPrivateData(conn);\n                freeClient(slave);\n                server.rdb_pipe_conns[i] = NULL;\n            }\n            killRDBChild();\n            return;\n        }\n\n        if (server.rdb_pipe_bufflen == 0) {\n            /* EOF - write end was closed. */\n            int stillUp = 0;\n            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE);\n            for (i=0; i < server.rdb_pipe_numconns; i++)\n            {\n                connection *conn = server.rdb_pipe_conns[i];\n                if (!conn)\n                    continue;\n                stillUp++;\n            }\n            serverLog(LL_WARNING,\"Diskless rdb transfer, done reading from pipe, %d replicas still up.\", stillUp);\n            RdbPipeCleanup();\n            return;\n        }\n\n        int stillAlive = 0;\n        for (i=0; i < server.rdb_pipe_numconns; i++)\n        {\n            int nwritten;\n            connection *conn = server.rdb_pipe_conns[i];\n            if (!conn)\n                continue;\n\n            client *slave = connGetPrivateData(conn);\n            if ((nwritten = connWrite(conn, server.rdb_pipe_buff, server.rdb_pipe_bufflen)) == -1) {\n                if (connGetState(conn) != CONN_STATE_CONNECTED) {\n                    serverLog(LL_WARNING,\"Diskless rdb transfer, write error sending DB to replica: %s\",\n                        connGetLastError(conn));\n                    freeClient(slave);\n                    server.rdb_pipe_conns[i] = NULL;\n                    continue;\n                }\n                /* An error and still in connected state, is equivalent to EAGAIN */\n                slave->repldboff = 0;\n            } else {\n                slave->repldboff = nwritten;\n                server.stat_net_output_bytes += nwritten;\n            }\n            /* If we were unable to write all the data to one of the replicas,\n             * setup write handler (and disable pipe read handler, below) */\n            if (nwritten != server.rdb_pipe_bufflen) {\n                server.rdb_pipe_numconns_writing++;\n                connSetWriteHandler(conn, rdbPipeWriteHandler);\n            }\n            stillAlive++;\n        }\n\n        if (stillAlive == 0) {\n            serverLog(LL_WARNING,\"Diskless rdb transfer, last replica dropped, killing fork child.\");\n            killRDBChild();\n            RdbPipeCleanup();\n        }\n        /*  Remove the pipe read handler if at least one write handler was set. */\n        if (server.rdb_pipe_numconns_writing || stillAlive == 0) {\n            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE);\n            break;\n        }\n    }\n}\n\n/* This function is called at the end of every background saving,\n * or when the replication RDB transfer strategy is modified from\n * disk to socket or the other way around.\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, and\n * to schedule a new BGSAVE if there are slaves that attached while a\n * BGSAVE was in progress, but it was not a good one for replication (no\n * other slave was accumulating differences).\n *\n * The argument bgsaveerr is C_OK if the background saving succeeded\n * otherwise C_ERR is passed to the function.\n * The 'type' argument is the type of the child that terminated\n * (if it had a disk or socket target). */\nvoid updateSlavesWaitingBgsave(int bgsaveerr, int type) {\n    listNode *ln;\n    int startbgsave = 0;\n    int mincapa = -1;\n    listIter li;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n            startbgsave = 1;\n            mincapa = (mincapa == -1) ? slave->slave_capa :\n                                        (mincapa & slave->slave_capa);\n        } else if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {\n            struct redis_stat buf;\n\n            /* If this was an RDB on disk save, we have to prepare to send\n             * the RDB from disk to the slave socket. Otherwise if this was\n             * already an RDB -> Slaves socket transfer, used in the case of\n             * diskless replication, our work is trivial, we can just put\n             * the slave online. */\n            if (type == RDB_CHILD_TYPE_SOCKET) {\n                serverLog(LL_NOTICE,\n                    \"Streamed RDB transfer with replica %s succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming\",\n                        replicationGetSlaveName(slave));\n                /* Note: we wait for a REPLCONF ACK message from the replica in\n                 * order to really put it online (install the write handler\n                 * so that the accumulated data can be transferred). However\n                 * we change the replication state ASAP, since our slave\n                 * is technically online now.\n                 *\n                 * So things work like that:\n                 *\n                 * 1. We end trasnferring the RDB file via socket.\n                 * 2. The replica is put ONLINE but the write handler\n                 *    is not installed.\n                 * 3. The replica however goes really online, and pings us\n                 *    back via REPLCONF ACK commands.\n                 * 4. Now we finally install the write handler, and send\n                 *    the buffers accumulated so far to the replica.\n                 *\n                 * But why we do that? Because the replica, when we stream\n                 * the RDB directly via the socket, must detect the RDB\n                 * EOF (end of file), that is a special random string at the\n                 * end of the RDB (for streamed RDBs we don't know the length\n                 * in advance). Detecting such final EOF string is much\n                 * simpler and less CPU intensive if no more data is sent\n                 * after such final EOF. So we don't want to glue the end of\n                 * the RDB trasfer with the start of the other replication\n                 * data. */\n                slave->replstate = SLAVE_STATE_ONLINE;\n                slave->repl_put_online_on_ack = 1;\n                slave->repl_ack_time = server.unixtime; /* Timeout otherwise. */\n            } else {\n                if (bgsaveerr != C_OK) {\n                    freeClient(slave);\n                    serverLog(LL_WARNING,\"SYNC failed. BGSAVE child returned an error\");\n                    continue;\n                }\n                if ((slave->repldbfd = open(server.rdb_filename,O_RDONLY)) == -1 ||\n                    redis_fstat(slave->repldbfd,&buf) == -1) {\n                    freeClient(slave);\n                    serverLog(LL_WARNING,\"SYNC failed. Can't open/stat DB after BGSAVE: %s\", strerror(errno));\n                    continue;\n                }\n                slave->repldboff = 0;\n                slave->repldbsize = buf.st_size;\n                slave->replstate = SLAVE_STATE_SEND_BULK;\n                slave->replpreamble = sdscatprintf(sdsempty(),\"$%lld\\r\\n\",\n                    (unsigned long long) slave->repldbsize);\n\n                connSetWriteHandler(slave->conn,NULL);\n                if (connSetWriteHandler(slave->conn,sendBulkToSlave) == C_ERR) {\n                    freeClient(slave);\n                    continue;\n                }\n            }\n        }\n    }\n    if (startbgsave) startBgsaveForReplication(mincapa);\n}\n\n/* Change the current instance replication ID with a new, random one.\n * This will prevent successful PSYNCs between this master and other\n * slaves, so the command should be called when something happens that\n * alters the current story of the dataset. */\nvoid changeReplicationId(void) {\n    getRandomHexChars(server.replid,CONFIG_RUN_ID_SIZE);\n    server.replid[CONFIG_RUN_ID_SIZE] = '\\0';\n}\n\n/* Clear (invalidate) the secondary replication ID. This happens, for\n * example, after a full resynchronization, when we start a new replication\n * history. */\nvoid clearReplicationId2(void) {\n    memset(server.replid2,'0',sizeof(server.replid));\n    server.replid2[CONFIG_RUN_ID_SIZE] = '\\0';\n    server.second_replid_offset = -1;\n}\n\n/* Use the current replication ID / offset as secondary replication\n * ID, and change the current one in order to start a new history.\n * This should be used when an instance is switched from slave to master\n * so that it can serve PSYNC requests performed using the master\n * replication ID. */\nvoid shiftReplicationId(void) {\n    memcpy(server.replid2,server.replid,sizeof(server.replid));\n    /* We set the second replid offset to the master offset + 1, since\n     * the slave will ask for the first byte it has not yet received, so\n     * we need to add one to the offset: for example if, as a slave, we are\n     * sure we have the same history as the master for 50 bytes, after we\n     * are turned into a master, we can accept a PSYNC request with offset\n     * 51, since the slave asking has the same history up to the 50th\n     * byte, and is asking for the new bytes starting at offset 51. */\n    server.second_replid_offset = server.master_repl_offset+1;\n    changeReplicationId();\n    serverLog(LL_WARNING,\"Setting secondary replication ID to %s, valid up to offset: %lld. New replication ID is %s\", server.replid2, server.second_replid_offset, server.replid);\n}\n\n/* ----------------------------------- SLAVE -------------------------------- */\n\n/* Returns 1 if the given replication state is a handshake state,\n * 0 otherwise. */\nint slaveIsInHandshakeState(void) {\n    return server.repl_state >= REPL_STATE_RECEIVE_PONG &&\n           server.repl_state <= REPL_STATE_RECEIVE_PSYNC;\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 entirely 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        /* Pinging back in this stage is best-effort. */\n        if (server.repl_transfer_s) connWrite(server.repl_transfer_s, \"\\n\", 1);\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    UNUSED(privdata);\n    replicationSendNewlineToMaster();\n}\n\n/* Once we have a link with the master and the synchroniziation was\n * performed, this function materializes the master client we store\n * at server.master, starting from the specified file descriptor. */\nvoid replicationCreateMasterClient(connection *conn, int dbid) {\n    server.master = createClient(conn);\n    if (conn)\n        connSetReadHandler(server.master->conn, readQueryFromClient);\n    server.master->flags |= CLIENT_MASTER;\n    server.master->authenticated = 1;\n    server.master->reploff = server.master_initial_offset;\n    server.master->read_reploff = server.master->reploff;\n    server.master->user = NULL; /* This client can do everything. */\n    memcpy(server.master->replid, server.master_replid,\n        sizeof(server.master_replid));\n    /* If master offset is set to -1, this master is old and is not\n     * PSYNC capable, so we flag it accordingly. */\n    if (server.master->reploff == -1)\n        server.master->flags |= CLIENT_PRE_PSYNC;\n    if (dbid != -1) selectDb(server.master,dbid);\n}\n\n/* This function will try to re-enable the AOF file after the\n * master-replica synchronization: if it fails after multiple attempts\n * the replica cannot be considered reliable and exists with an\n * error. */\nvoid restartAOFAfterSYNC() {\n    unsigned int tries, max_tries = 10;\n    for (tries = 0; tries < max_tries; ++tries) {\n        if (startAppendOnly() == C_OK) break;\n        serverLog(LL_WARNING,\n            \"Failed enabling the AOF after successful master synchronization! \"\n            \"Trying it again in one second.\");\n        sleep(1);\n    }\n    if (tries == max_tries) {\n        serverLog(LL_WARNING,\n            \"FATAL: this replica instance finished the synchronization with \"\n            \"its master, but the AOF can't be turned on. Exiting now.\");\n        exit(1);\n    }\n}\n\nstatic int useDisklessLoad() {\n    /* compute boolean decision to use diskless load */\n    int enabled = server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB ||\n           (server.repl_diskless_load == REPL_DISKLESS_LOAD_WHEN_DB_EMPTY && dbTotalServerKeyCount()==0);\n    /* Check all modules handle read errors, otherwise it's not safe to use diskless load. */\n    if (enabled && !moduleAllDatatypesHandleErrors()) {\n        serverLog(LL_WARNING,\n            \"Skipping diskless-load because there are modules that don't handle read errors.\");\n        enabled = 0;\n    }\n    return enabled;\n}\n\n/* Helper function for readSyncBulkPayload() to make backups of the current\n * DBs before socket-loading the new ones. The backups may be restored later\n * or freed by disklessLoadRestoreBackups(). */\nredisDb *disklessLoadMakeBackups(void) {\n    redisDb *backups = zmalloc(sizeof(redisDb)*server.dbnum);\n    for (int i=0; i<server.dbnum; i++) {\n        backups[i] = server.db[i];\n        server.db[i].dict = dictCreate(&dbDictType,NULL);\n        server.db[i].expires = dictCreate(&keyptrDictType,NULL);\n    }\n    return backups;\n}\n\n/* Helper function for readSyncBulkPayload(): when replica-side diskless\n * database loading is used, Redis makes a backup of the existing databases\n * before loading the new ones from the socket.\n *\n * If the socket loading went wrong, we want to restore the old backups\n * into the server databases. This function does just that in the case\n * the 'restore' argument (the number of DBs to replace) is non-zero.\n *\n * When instead the loading succeeded we want just to free our old backups,\n * in that case the funciton will do just that when 'restore' is 0. */\nvoid disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags)\n{\n    if (restore) {\n        /* Restore. */\n        emptyDbGeneric(server.db,-1,empty_db_flags,replicationEmptyDbCallback);\n        for (int i=0; i<server.dbnum; i++) {\n            dictRelease(server.db[i].dict);\n            dictRelease(server.db[i].expires);\n            server.db[i] = backup[i];\n        }\n    } else {\n        /* Delete (Pass EMPTYDB_BACKUP in order to avoid firing module events) . */\n        emptyDbGeneric(backup,-1,empty_db_flags|EMPTYDB_BACKUP,replicationEmptyDbCallback);\n        for (int i=0; i<server.dbnum; i++) {\n            dictRelease(backup[i].dict);\n            dictRelease(backup[i].expires);\n        }\n    }\n    zfree(backup);\n}\n\n/* Asynchronously read the SYNC payload we receive from a master */\n#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */\nvoid readSyncBulkPayload(connection *conn) {\n    char buf[PROTO_IOBUF_LEN];\n    ssize_t nread, readlen, nwritten;\n    int use_diskless_load = useDisklessLoad();\n    redisDb *diskless_load_backup = NULL;\n    int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :\n                                                        EMPTYDB_NO_FLAGS;\n    off_t left;\n\n    /* Static vars used to hold the EOF mark, and the last bytes received\n     * form the server: when they match, we reached the end of the transfer. */\n    static char eofmark[CONFIG_RUN_ID_SIZE];\n    static char lastbytes[CONFIG_RUN_ID_SIZE];\n    static int usemark = 0;\n\n    /* If repl_transfer_size == -1 we still have to read the bulk length\n     * from the master reply. */\n    if (server.repl_transfer_size == -1) {\n        if (connSyncReadLine(conn,buf,1024,server.repl_syncio_timeout*1000) == -1) {\n            serverLog(LL_WARNING,\n                \"I/O error reading bulk count from MASTER: %s\",\n                strerror(errno));\n            goto error;\n        }\n\n        if (buf[0] == '-') {\n            serverLog(LL_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            server.repl_transfer_lastio = server.unixtime;\n            return;\n        } else if (buf[0] != '$') {\n            serverLog(LL_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        /* There are two possible forms for the bulk payload. One is the\n         * usual $<count> bulk format. The other is used for diskless transfers\n         * when the master does not know beforehand the size of the file to\n         * transfer. In the latter case, the following format is used:\n         *\n         * $EOF:<40 bytes delimiter>\n         *\n         * At the end of the file the announced delimiter is transmitted. The\n         * delimiter is long and random enough that the probability of a\n         * collision with the actual file content can be ignored. */\n        if (strncmp(buf+1,\"EOF:\",4) == 0 && strlen(buf+5) >= CONFIG_RUN_ID_SIZE) {\n            usemark = 1;\n            memcpy(eofmark,buf+5,CONFIG_RUN_ID_SIZE);\n            memset(lastbytes,0,CONFIG_RUN_ID_SIZE);\n            /* Set any repl_transfer_size to avoid entering this code path\n             * at the next call. */\n            server.repl_transfer_size = 0;\n            serverLog(LL_NOTICE,\n                \"MASTER <-> REPLICA sync: receiving streamed RDB from master with EOF %s\",\n                use_diskless_load? \"to parser\":\"to disk\");\n        } else {\n            usemark = 0;\n            server.repl_transfer_size = strtol(buf+1,NULL,10);\n            serverLog(LL_NOTICE,\n                \"MASTER <-> REPLICA sync: receiving %lld bytes from master %s\",\n                (long long) server.repl_transfer_size,\n                use_diskless_load? \"to parser\":\"to disk\");\n        }\n        return;\n    }\n\n    if (!use_diskless_load) {\n        /* Read the data from the socket, store it to a file and search\n         * for the EOF. */\n        if (usemark) {\n            readlen = sizeof(buf);\n        } else {\n            left = server.repl_transfer_size - server.repl_transfer_read;\n            readlen = (left < (signed)sizeof(buf)) ? left : (signed)sizeof(buf);\n        }\n\n        nread = connRead(conn,buf,readlen);\n        if (nread <= 0) {\n            serverLog(LL_WARNING,\"I/O error trying to sync with MASTER: %s\",\n                (nread == -1) ? strerror(errno) : \"connection lost\");\n            cancelReplicationHandshake();\n            return;\n        }\n        server.stat_net_input_bytes += nread;\n\n        /* When a mark is used, we want to detect EOF asap in order to avoid\n         * writing the EOF mark into the file... */\n        int eof_reached = 0;\n\n        if (usemark) {\n            /* Update the last bytes array, and check if it matches our\n             * delimiter. */\n            if (nread >= CONFIG_RUN_ID_SIZE) {\n                memcpy(lastbytes,buf+nread-CONFIG_RUN_ID_SIZE,\n                       CONFIG_RUN_ID_SIZE);\n            } else {\n                int rem = CONFIG_RUN_ID_SIZE-nread;\n                memmove(lastbytes,lastbytes+nread,rem);\n                memcpy(lastbytes+rem,buf,nread);\n            }\n            if (memcmp(lastbytes,eofmark,CONFIG_RUN_ID_SIZE) == 0)\n                eof_reached = 1;\n        }\n\n        /* Update the last I/O time for the replication transfer (used in\n         * order to detect timeouts during replication), and write what we\n         * got from the socket to the dump file on disk. */\n        server.repl_transfer_lastio = server.unixtime;\n        if ((nwritten = write(server.repl_transfer_fd,buf,nread)) != nread) {\n            serverLog(LL_WARNING,\n                \"Write error or short write writing to the DB dump file \"\n                \"needed for MASTER <-> REPLICA synchronization: %s\",\n                (nwritten == -1) ? strerror(errno) : \"short write\");\n            goto error;\n        }\n        server.repl_transfer_read += nread;\n\n        /* Delete the last 40 bytes from the file if we reached EOF. */\n        if (usemark && eof_reached) {\n            if (ftruncate(server.repl_transfer_fd,\n                server.repl_transfer_read - CONFIG_RUN_ID_SIZE) == -1)\n            {\n                serverLog(LL_WARNING,\n                    \"Error truncating the RDB file received from the master \"\n                    \"for SYNC: %s\", strerror(errno));\n                goto error;\n            }\n        }\n\n        /* Sync data on disk from time to time, otherwise at the end of the\n         * transfer we may suffer a big delay as the memory buffers are copied\n         * into the actual disk. */\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        if (!usemark) {\n            if (server.repl_transfer_read == server.repl_transfer_size)\n                eof_reached = 1;\n        }\n\n        /* If the transfer is yet not complete, we need to read more, so\n         * return ASAP and wait for the handler to be called again. */\n        if (!eof_reached) return;\n    }\n\n    /* We reach this point in one of the following cases:\n     *\n     * 1. The replica is using diskless replication, that is, it reads data\n     *    directly from the socket to the Redis memory, without using\n     *    a temporary RDB file on disk. In that case we just block and\n     *    read everything from the socket.\n     *\n     * 2. Or when we are done reading from the socket to the RDB file, in\n     *    such case we want just to read the RDB file in memory. */\n    serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Flushing old data\");\n\n    /* We need to stop any AOF rewriting child before flusing and parsing\n     * the RDB, otherwise we'll create a copy-on-write disaster. */\n    if (server.aof_state != AOF_OFF) stopAppendOnly();\n\n    /* When diskless RDB loading is used by replicas, it may be configured\n     * in order to save the current DB instead of throwing it away,\n     * so that we can restore it in case of failed transfer. */\n    if (use_diskless_load &&\n        server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB)\n    {\n        /* Create a backup of server.db[] and initialize to empty\n         * dictionaries */\n        diskless_load_backup = disklessLoadMakeBackups();\n    }\n    /* We call to emptyDb even in case of REPL_DISKLESS_LOAD_SWAPDB\n     * (Where disklessLoadMakeBackups left server.db empty) because we\n     * want to execute all the auxiliary logic of emptyDb (Namely,\n     * fire module events) */\n    emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);\n\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    connSetReadHandler(conn, NULL);\n    serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Loading DB in memory\");\n    rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;\n    if (use_diskless_load) {\n        rio rdb;\n        rioInitWithConn(&rdb,conn,server.repl_transfer_size);\n\n        /* Put the socket in blocking mode to simplify RDB transfer.\n         * We'll restore it when the RDB is received. */\n        connBlock(conn);\n        connRecvTimeout(conn, server.repl_timeout*1000);\n        startLoading(server.repl_transfer_size, RDBFLAGS_REPLICATION);\n\n        if (rdbLoadRio(&rdb,RDBFLAGS_REPLICATION,&rsi) != C_OK) {\n            /* RDB loading failed. */\n            stopLoading(0);\n            serverLog(LL_WARNING,\n                \"Failed trying to load the MASTER synchronization DB \"\n                \"from socket\");\n            cancelReplicationHandshake();\n            rioFreeConn(&rdb, NULL);\n            if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {\n                /* Restore the backed up databases. */\n                disklessLoadRestoreBackups(diskless_load_backup,1,\n                                           empty_db_flags);\n            } else {\n                /* Remove the half-loaded data in case we started with\n                 * an empty replica. */\n                emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);\n            }\n\n            /* Note that there's no point in restarting the AOF on SYNC\n             * failure, it'll be restarted when sync succeeds or the replica\n             * gets promoted. */\n            return;\n        }\n        stopLoading(1);\n\n        /* RDB loading succeeded if we reach this point. */\n        if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {\n            /* Delete the backup databases we created before starting to load\n             * the new RDB. Now the RDB was loaded with success so the old\n             * data is useless. */\n            disklessLoadRestoreBackups(diskless_load_backup,0,empty_db_flags);\n        }\n\n        /* Verify the end mark is correct. */\n        if (usemark) {\n            if (!rioRead(&rdb,buf,CONFIG_RUN_ID_SIZE) ||\n                memcmp(buf,eofmark,CONFIG_RUN_ID_SIZE) != 0)\n            {\n                serverLog(LL_WARNING,\"Replication stream EOF marker is broken\");\n                cancelReplicationHandshake();\n                rioFreeConn(&rdb, NULL);\n                return;\n            }\n        }\n\n        /* Cleanup and restore the socket to the original state to continue\n         * with the normal replication. */\n        rioFreeConn(&rdb, NULL);\n        connNonBlock(conn);\n        connRecvTimeout(conn,0);\n    } else {\n        /* Ensure background save doesn't overwrite synced data */\n        if (server.rdb_child_pid != -1) {\n            serverLog(LL_NOTICE,\n                \"Replica is about to load the RDB file received from the \"\n                \"master, but there is a pending RDB child running. \"\n                \"Killing process %ld and removing its temp file to avoid \"\n                \"any race\",\n                    (long) server.rdb_child_pid);\n            killRDBChild();\n        }\n\n        /* Rename rdb like renaming rewrite aof asynchronously. */\n        int old_rdb_fd = open(server.rdb_filename,O_RDONLY|O_NONBLOCK);\n        if (rename(server.repl_transfer_tmpfile,server.rdb_filename) == -1) {\n            serverLog(LL_WARNING,\n                \"Failed trying to rename the temp DB into %s in \"\n                \"MASTER <-> REPLICA synchronization: %s\",\n                server.rdb_filename, strerror(errno));\n            cancelReplicationHandshake();\n            if (old_rdb_fd != -1) close(old_rdb_fd);\n            return;\n        }\n        /* Close old rdb asynchronously. */\n        if (old_rdb_fd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)old_rdb_fd,NULL,NULL);\n\n        if (rdbLoad(server.rdb_filename,&rsi,RDBFLAGS_REPLICATION) != C_OK) {\n            serverLog(LL_WARNING,\n                \"Failed trying to load the MASTER synchronization \"\n                \"DB from disk\");\n            cancelReplicationHandshake();\n            if (server.rdb_del_sync_files && allPersistenceDisabled()) {\n                serverLog(LL_NOTICE,\"Removing the RDB file obtained from \"\n                                    \"the master. This replica has persistence \"\n                                    \"disabled\");\n                bg_unlink(server.rdb_filename);\n            }\n            /* Note that there's no point in restarting the AOF on sync failure,\n               it'll be restarted when sync succeeds or replica promoted. */\n            return;\n        }\n\n        /* Cleanup. */\n        if (server.rdb_del_sync_files && allPersistenceDisabled()) {\n            serverLog(LL_NOTICE,\"Removing the RDB file obtained from \"\n                                \"the master. This replica has persistence \"\n                                \"disabled\");\n            bg_unlink(server.rdb_filename);\n        }\n\n        zfree(server.repl_transfer_tmpfile);\n        close(server.repl_transfer_fd);\n        server.repl_transfer_fd = -1;\n        server.repl_transfer_tmpfile = NULL;\n    }\n\n    /* Final setup of the connected slave <- master link */\n    replicationCreateMasterClient(server.repl_transfer_s,rsi.repl_stream_db);\n    server.repl_state = REPL_STATE_CONNECTED;\n    server.repl_down_since = 0;\n\n    /* Fire the master link modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                          REDISMODULE_SUBEVENT_MASTER_LINK_UP,\n                          NULL);\n\n    /* After a full resynchroniziation we use the replication ID and\n     * offset of the master. The secondary ID / offset are cleared since\n     * we are starting a new history. */\n    memcpy(server.replid,server.master->replid,sizeof(server.replid));\n    server.master_repl_offset = server.master->reploff;\n    server.master_repl_meaningful_offset = server.master->reploff;\n    clearReplicationId2();\n\n    /* Let's create the replication backlog if needed. Slaves need to\n     * accumulate the backlog regardless of the fact they have sub-slaves\n     * or not, in order to behave correctly if they are promoted to\n     * masters after a failover. */\n    if (server.repl_backlog == NULL) createReplicationBacklog();\n    serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Finished with success\");\n\n    if (server.supervised_mode == SUPERVISED_SYSTEMD) {\n        redisCommunicateSystemd(\"STATUS=MASTER <-> REPLICA sync: Finished with success. Ready to accept connections.\\n\");\n        redisCommunicateSystemd(\"READY=1\\n\");\n    }\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    if (server.aof_enabled) restartAOFAfterSYNC();\n    return;\n\nerror:\n    cancelReplicationHandshake();\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#define SYNC_CMD_READ (1<<0)\n#define SYNC_CMD_WRITE (1<<1)\n#define SYNC_CMD_FULL (SYNC_CMD_READ|SYNC_CMD_WRITE)\nchar *sendSynchronousCommand(int flags, connection *conn, ...) {\n\n    /* Create the command to send to the master, we use redis binary\n     * protocol to make sure correct arguments are sent. This function\n     * is not safe for all binary data. */\n    if (flags & SYNC_CMD_WRITE) {\n        char *arg;\n        va_list ap;\n        sds cmd = sdsempty();\n        sds cmdargs = sdsempty();\n        size_t argslen = 0;\n        va_start(ap,conn);\n\n        while(1) {\n            arg = va_arg(ap, char*);\n            if (arg == NULL) break;\n\n            cmdargs = sdscatprintf(cmdargs,\"$%zu\\r\\n%s\\r\\n\",strlen(arg),arg);\n            argslen++;\n        }\n\n        va_end(ap);\n\n        cmd = sdscatprintf(cmd,\"*%zu\\r\\n\",argslen);\n        cmd = sdscatsds(cmd,cmdargs);\n        sdsfree(cmdargs);\n\n        /* Transfer command to the server. */\n        if (connSyncWrite(conn,cmd,sdslen(cmd),server.repl_syncio_timeout*1000)\n            == -1)\n        {\n            sdsfree(cmd);\n            return sdscatprintf(sdsempty(),\"-Writing to master: %s\",\n                    connGetLastError(conn));\n        }\n        sdsfree(cmd);\n    }\n\n    /* Read the reply from the server. */\n    if (flags & SYNC_CMD_READ) {\n        char buf[256];\n\n        if (connSyncReadLine(conn,buf,sizeof(buf),server.repl_syncio_timeout*1000)\n            == -1)\n        {\n            return sdscatprintf(sdsempty(),\"-Reading from master: %s\",\n                    strerror(errno));\n        }\n        server.repl_transfer_lastio = server.unixtime;\n        return sdsnew(buf);\n    }\n    return NULL;\n}\n\n/* Try a partial resynchronization with the master if we are about to reconnect.\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 * This function is designed to be called from syncWithMaster(), so the\n * following assumptions are made:\n *\n * 1) We pass the function an already connected socket \"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 *\n * The function is split in two halves: if read_reply is 0, the function\n * writes the PSYNC command on the socket, and a new function call is\n * needed, with read_reply set to 1, in order to read the reply of the\n * command. This is useful in order to support non blocking operations, so\n * that we write, return into the event loop, and read when there are data.\n *\n * When read_reply is 0 the function returns PSYNC_WRITE_ERR if there\n * was a write error, or PSYNC_WAIT_REPLY to signal we need another call\n * with read_reply set to 1. However even when read_reply is set to 1\n * the function may return PSYNC_WAIT_REPLY again to signal there were\n * insufficient data to read to complete its work. We should re-enter\n * into the event loop and wait in such a case.\n *\n * The function returns:\n *\n * PSYNC_CONTINUE: If the PSYNC command succeeded and we can continue.\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_NOT_SUPPORTED: If the server does not understand PSYNC at all and\n *                      the caller should fall back to SYNC.\n * PSYNC_WRITE_ERROR: There was an error writing the command to the socket.\n * PSYNC_WAIT_REPLY: Call again the function with read_reply set to 1.\n * PSYNC_TRY_LATER: Master is currently in a transient error condition.\n *\n * Notable side effects:\n *\n * 1) As a side effect of the function call the function removes the readable\n *    event handler from \"fd\", unless the return value is PSYNC_WAIT_REPLY.\n * 2) server.master_initial_offset is set to the right value according\n *    to the master reply. This will be used to populate the 'server.master'\n *    structure replication offset.\n */\n\n#define PSYNC_WRITE_ERROR 0\n#define PSYNC_WAIT_REPLY 1\n#define PSYNC_CONTINUE 2\n#define PSYNC_FULLRESYNC 3\n#define PSYNC_NOT_SUPPORTED 4\n#define PSYNC_TRY_LATER 5\nint slaveTryPartialResynchronization(connection *conn, int read_reply) {\n    char *psync_replid;\n    char psync_offset[32];\n    sds reply;\n\n    /* Writing half */\n    if (!read_reply) {\n        /* Initially set 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.master_initial_offset = -1;\n\n        if (server.cached_master) {\n            psync_replid = server.cached_master->replid;\n            snprintf(psync_offset,sizeof(psync_offset),\"%lld\", server.cached_master->reploff+1);\n            serverLog(LL_NOTICE,\"Trying a partial resynchronization (request %s:%s).\", psync_replid, psync_offset);\n        } else {\n            serverLog(LL_NOTICE,\"Partial resynchronization not possible (no cached master)\");\n            psync_replid = \"?\";\n            memcpy(psync_offset,\"-1\",3);\n        }\n\n        /* Issue the PSYNC command */\n        reply = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"PSYNC\",psync_replid,psync_offset,NULL);\n        if (reply != NULL) {\n            serverLog(LL_WARNING,\"Unable to send PSYNC to master: %s\",reply);\n            sdsfree(reply);\n            connSetReadHandler(conn, NULL);\n            return PSYNC_WRITE_ERROR;\n        }\n        return PSYNC_WAIT_REPLY;\n    }\n\n    /* Reading half */\n    reply = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n    if (sdslen(reply) == 0) {\n        /* The master may send empty newlines after it receives PSYNC\n         * and before to reply, just to keep the connection alive. */\n        sdsfree(reply);\n        return PSYNC_WAIT_REPLY;\n    }\n\n    connSetReadHandler(conn, NULL);\n\n    if (!strncmp(reply,\"+FULLRESYNC\",11)) {\n        char *replid = NULL, *offset = NULL;\n\n        /* FULL RESYNC, parse the reply in order to extract the run id\n         * and the replication offset. */\n        replid = strchr(reply,' ');\n        if (replid) {\n            replid++;\n            offset = strchr(replid,' ');\n            if (offset) offset++;\n        }\n        if (!replid || !offset || (offset-replid-1) != CONFIG_RUN_ID_SIZE) {\n            serverLog(LL_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             * replid to make sure next PSYNCs will fail. */\n            memset(server.master_replid,0,CONFIG_RUN_ID_SIZE+1);\n        } else {\n            memcpy(server.master_replid, replid, offset-replid-1);\n            server.master_replid[CONFIG_RUN_ID_SIZE] = '\\0';\n            server.master_initial_offset = strtoll(offset,NULL,10);\n            serverLog(LL_NOTICE,\"Full resync from master: %s:%lld\",\n                server.master_replid,\n                server.master_initial_offset);\n        }\n        /* We are going to full resync, discard the cached master structure. */\n        replicationDiscardCachedMaster();\n        sdsfree(reply);\n        return PSYNC_FULLRESYNC;\n    }\n\n    if (!strncmp(reply,\"+CONTINUE\",9)) {\n        /* Partial resync was accepted. */\n        serverLog(LL_NOTICE,\n            \"Successful partial resynchronization with master.\");\n\n        /* Check the new replication ID advertised by the master. If it\n         * changed, we need to set the new ID as primary ID, and set or\n         * secondary ID as the old master ID up to the current offset, so\n         * that our sub-slaves will be able to PSYNC with us after a\n         * disconnection. */\n        char *start = reply+10;\n        char *end = reply+9;\n        while(end[0] != '\\r' && end[0] != '\\n' && end[0] != '\\0') end++;\n        if (end-start == CONFIG_RUN_ID_SIZE) {\n            char new[CONFIG_RUN_ID_SIZE+1];\n            memcpy(new,start,CONFIG_RUN_ID_SIZE);\n            new[CONFIG_RUN_ID_SIZE] = '\\0';\n\n            if (strcmp(new,server.cached_master->replid)) {\n                /* Master ID changed. */\n                serverLog(LL_WARNING,\"Master replication ID changed to %s\",new);\n\n                /* Set the old ID as our ID2, up to the current offset+1. */\n                memcpy(server.replid2,server.cached_master->replid,\n                    sizeof(server.replid2));\n                server.second_replid_offset = server.master_repl_offset+1;\n\n                /* Update the cached master ID and our own primary ID to the\n                 * new one. */\n                memcpy(server.replid,new,sizeof(server.replid));\n                memcpy(server.cached_master->replid,new,sizeof(server.replid));\n\n                /* Disconnect all the sub-slaves: they need to be notified. */\n                disconnectSlaves();\n            }\n        }\n\n        /* Setup the replication to continue. */\n        sdsfree(reply);\n        replicationResurrectCachedMaster(conn);\n\n        /* If this instance was restarted and we read the metadata to\n         * PSYNC from the persistence file, our replication backlog could\n         * be still not initialized. Create it. */\n        if (server.repl_backlog == NULL) createReplicationBacklog();\n        return PSYNC_CONTINUE;\n    }\n\n    /* If we reach this point we received either an error (since the master does\n     * not understand PSYNC or because it is in a special state and cannot\n     * serve our request), or an unexpected reply from the master.\n     *\n     * Return PSYNC_NOT_SUPPORTED on errors we don't understand, otherwise\n     * return PSYNC_TRY_LATER if we believe this is a transient error. */\n\n    if (!strncmp(reply,\"-NOMASTERLINK\",13) ||\n        !strncmp(reply,\"-LOADING\",8))\n    {\n        serverLog(LL_NOTICE,\n            \"Master is currently unable to PSYNC \"\n            \"but should be in the future: %s\", reply);\n        sdsfree(reply);\n        return PSYNC_TRY_LATER;\n    }\n\n    if (strncmp(reply,\"-ERR\",4)) {\n        /* If it's not an error, log the unexpected event. */\n        serverLog(LL_WARNING,\n            \"Unexpected reply to PSYNC from master: %s\", reply);\n    } else {\n        serverLog(LL_NOTICE,\n            \"Master does not support PSYNC or is in \"\n            \"error state (reply: %s)\", reply);\n    }\n    sdsfree(reply);\n    replicationDiscardCachedMaster();\n    return PSYNC_NOT_SUPPORTED;\n}\n\n/* This handler fires when the non blocking connect was able to\n * establish a connection with the master. */\nvoid syncWithMaster(connection *conn) {\n    char tmpfile[256], *err = NULL;\n    int dfd = -1, maxtries = 5;\n    int psync_result;\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    if (server.repl_state == REPL_STATE_NONE) {\n        connClose(conn);\n        return;\n    }\n\n    /* Check for errors in the socket: after a non blocking connect() we\n     * may find that the socket is in error state. */\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_WARNING,\"Error condition on socket for SYNC: %s\",\n                connGetLastError(conn));\n        goto error;\n    }\n\n    /* Send a PING to check the master is able to reply without errors. */\n    if (server.repl_state == REPL_STATE_CONNECTING) {\n        serverLog(LL_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        connSetReadHandler(conn, syncWithMaster);\n        connSetWriteHandler(conn, NULL);\n        server.repl_state = REPL_STATE_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        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"PING\",NULL);\n        if (err) goto write_error;\n        return;\n    }\n\n    /* Receive the PONG command. */\n    if (server.repl_state == REPL_STATE_RECEIVE_PONG) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\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        if (err[0] != '+' &&\n            strncmp(err,\"-NOAUTH\",7) != 0 &&\n            strncmp(err,\"-ERR operation not permitted\",28) != 0)\n        {\n            serverLog(LL_WARNING,\"Error reply to PING from master: '%s'\",err);\n            sdsfree(err);\n            goto error;\n        } else {\n            serverLog(LL_NOTICE,\n                \"Master replied to PING, replication can continue...\");\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_AUTH;\n    }\n\n    /* AUTH with the master if required. */\n    if (server.repl_state == REPL_STATE_SEND_AUTH) {\n        if (server.masteruser && server.masterauth) {\n            err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"AUTH\",\n                                         server.masteruser,server.masterauth,NULL);\n            if (err) goto write_error;\n            server.repl_state = REPL_STATE_RECEIVE_AUTH;\n            return;\n        } else if (server.masterauth) {\n            err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"AUTH\",server.masterauth,NULL);\n            if (err) goto write_error;\n            server.repl_state = REPL_STATE_RECEIVE_AUTH;\n            return;\n        } else {\n            server.repl_state = REPL_STATE_SEND_PORT;\n        }\n    }\n\n    /* Receive AUTH reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_AUTH) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        if (err[0] == '-') {\n            serverLog(LL_WARNING,\"Unable to AUTH to MASTER: %s\",err);\n            sdsfree(err);\n            goto error;\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_PORT;\n    }\n\n    /* Set the slave port, so that Master's INFO command can list the\n     * slave listening port correctly. */\n    if (server.repl_state == REPL_STATE_SEND_PORT) {\n        int port;\n        if (server.slave_announce_port) port = server.slave_announce_port;\n        else if (server.tls_replication && server.tls_port) port = server.tls_port;\n        else port = server.port;\n        sds portstr = sdsfromlonglong(port);\n        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"REPLCONF\",\n                \"listening-port\",portstr, NULL);\n        sdsfree(portstr);\n        if (err) goto write_error;\n        sdsfree(err);\n        server.repl_state = REPL_STATE_RECEIVE_PORT;\n        return;\n    }\n\n    /* Receive REPLCONF listening-port reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_PORT) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF listening-port. */\n        if (err[0] == '-') {\n            serverLog(LL_NOTICE,\"(Non critical) Master does not understand \"\n                                \"REPLCONF listening-port: %s\", err);\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_IP;\n    }\n\n    /* Skip REPLCONF ip-address if there is no slave-announce-ip option set. */\n    if (server.repl_state == REPL_STATE_SEND_IP &&\n        server.slave_announce_ip == NULL)\n    {\n            server.repl_state = REPL_STATE_SEND_CAPA;\n    }\n\n    /* Set the slave ip, so that Master's INFO command can list the\n     * slave IP address port correctly in case of port forwarding or NAT. */\n    if (server.repl_state == REPL_STATE_SEND_IP) {\n        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"REPLCONF\",\n                \"ip-address\",server.slave_announce_ip, NULL);\n        if (err) goto write_error;\n        sdsfree(err);\n        server.repl_state = REPL_STATE_RECEIVE_IP;\n        return;\n    }\n\n    /* Receive REPLCONF ip-address reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_IP) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF listening-port. */\n        if (err[0] == '-') {\n            serverLog(LL_NOTICE,\"(Non critical) Master does not understand \"\n                                \"REPLCONF ip-address: %s\", err);\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_CAPA;\n    }\n\n    /* Inform the master of our (slave) capabilities.\n     *\n     * EOF: supports EOF-style RDB transfer for diskless replication.\n     * PSYNC2: supports PSYNC v2, so understands +CONTINUE <new repl ID>.\n     *\n     * The master will ignore capabilities it does not understand. */\n    if (server.repl_state == REPL_STATE_SEND_CAPA) {\n        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"REPLCONF\",\n                \"capa\",\"eof\",\"capa\",\"psync2\",NULL);\n        if (err) goto write_error;\n        sdsfree(err);\n        server.repl_state = REPL_STATE_RECEIVE_CAPA;\n        return;\n    }\n\n    /* Receive CAPA reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_CAPA) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF capa. */\n        if (err[0] == '-') {\n            serverLog(LL_NOTICE,\"(Non critical) Master does not understand \"\n                                  \"REPLCONF capa: %s\", err);\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_PSYNC;\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    if (server.repl_state == REPL_STATE_SEND_PSYNC) {\n        if (slaveTryPartialResynchronization(conn,0) == PSYNC_WRITE_ERROR) {\n            err = sdsnew(\"Write error sending the PSYNC command.\");\n            goto write_error;\n        }\n        server.repl_state = REPL_STATE_RECEIVE_PSYNC;\n        return;\n    }\n\n    /* If reached this point, we should be in REPL_STATE_RECEIVE_PSYNC. */\n    if (server.repl_state != REPL_STATE_RECEIVE_PSYNC) {\n        serverLog(LL_WARNING,\"syncWithMaster(): state machine error, \"\n                             \"state should be RECEIVE_PSYNC but is %d\",\n                             server.repl_state);\n        goto error;\n    }\n\n    psync_result = slaveTryPartialResynchronization(conn,1);\n    if (psync_result == PSYNC_WAIT_REPLY) return; /* Try again later... */\n\n    /* If the master is in an transient error, we should try to PSYNC\n     * from scratch later, so go to the error path. This happens when\n     * the server is loading the dataset or is not connected with its\n     * master and so forth. */\n    if (psync_result == PSYNC_TRY_LATER) goto error;\n\n    /* Note: if PSYNC does not return WAIT_REPLY, it will take care of\n     * uninstalling the read handler from the file descriptor. */\n\n    if (psync_result == PSYNC_CONTINUE) {\n        serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.\");\n        if (server.supervised_mode == SUPERVISED_SYSTEMD) {\n            redisCommunicateSystemd(\"STATUS=MASTER <-> REPLICA sync: Partial Resynchronization accepted. Ready to accept connections.\\n\");\n            redisCommunicateSystemd(\"READY=1\\n\");\n        }\n        return;\n    }\n\n    /* PSYNC failed or is not supported: we want our slaves to resync with us\n     * as well, if we have any sub-slaves. The master may transfer us an\n     * entirely different data set and we have no way to incrementally feed\n     * our slaves after that. */\n    disconnectSlaves(); /* Force our slaves to resync with us as well. */\n    freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */\n\n    /* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC\n     * and the server.master_replid and master_initial_offset are\n     * already populated. */\n    if (psync_result == PSYNC_NOT_SUPPORTED) {\n        serverLog(LL_NOTICE,\"Retrying with SYNC...\");\n        if (connSyncWrite(conn,\"SYNC\\r\\n\",6,server.repl_syncio_timeout*1000) == -1) {\n            serverLog(LL_WARNING,\"I/O error writing to MASTER: %s\",\n                strerror(errno));\n            goto error;\n        }\n    }\n\n    /* Prepare a suitable temp file for bulk transfer */\n    if (!useDisklessLoad()) {\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            serverLog(LL_WARNING,\"Opening the temp file needed for MASTER <-> REPLICA synchronization: %s\",strerror(errno));\n            goto error;\n        }\n        server.repl_transfer_tmpfile = zstrdup(tmpfile);\n        server.repl_transfer_fd = dfd;\n    }\n\n    /* Setup the non blocking download of the bulk file. */\n    if (connSetReadHandler(conn, readSyncBulkPayload)\n            == C_ERR)\n    {\n        char conninfo[CONN_INFO_LEN];\n        serverLog(LL_WARNING,\n            \"Can't create readable event for SYNC: %s (%s)\",\n            strerror(errno), connGetInfo(conn, conninfo, sizeof(conninfo)));\n        goto error;\n    }\n\n    server.repl_state = REPL_STATE_TRANSFER;\n    server.repl_transfer_size = -1;\n    server.repl_transfer_read = 0;\n    server.repl_transfer_last_fsync_off = 0;\n    server.repl_transfer_lastio = server.unixtime;\n    return;\n\nerror:\n    if (dfd != -1) close(dfd);\n    connClose(conn);\n    server.repl_transfer_s = NULL;\n    if (server.repl_transfer_fd != -1)\n        close(server.repl_transfer_fd);\n    if (server.repl_transfer_tmpfile)\n        zfree(server.repl_transfer_tmpfile);\n    server.repl_transfer_tmpfile = NULL;\n    server.repl_transfer_fd = -1;\n    server.repl_state = REPL_STATE_CONNECT;\n    return;\n\nwrite_error: /* Handle sendSynchronousCommand(SYNC_CMD_WRITE) errors. */\n    serverLog(LL_WARNING,\"Sending command to master in replication handshake: %s\", err);\n    sdsfree(err);\n    goto error;\n}\n\nint connectWithMaster(void) {\n    server.repl_transfer_s = server.tls_replication ? connCreateTLS() : connCreateSocket();\n    if (connConnect(server.repl_transfer_s, server.masterhost, server.masterport,\n                NET_FIRST_BIND_ADDR, syncWithMaster) == C_ERR) {\n        serverLog(LL_WARNING,\"Unable to connect to MASTER: %s\",\n                connGetLastError(server.repl_transfer_s));\n        connClose(server.repl_transfer_s);\n        server.repl_transfer_s = NULL;\n        return C_ERR;\n    }\n\n\n    server.repl_transfer_lastio = server.unixtime;\n    server.repl_state = REPL_STATE_CONNECTING;\n    return C_OK;\n}\n\n/* This function can be called when a non blocking connection is currently\n * in progress to undo it.\n * Never call this function directly, use cancelReplicationHandshake() instead.\n */\nvoid undoConnectWithMaster(void) {\n    connClose(server.repl_transfer_s);\n    server.repl_transfer_s = NULL;\n}\n\n/* Abort the async download of the bulk dataset while SYNC-ing with master.\n * Never call this function directly, use cancelReplicationHandshake() instead.\n */\nvoid replicationAbortSyncTransfer(void) {\n    serverAssert(server.repl_state == REPL_STATE_TRANSFER);\n    undoConnectWithMaster();\n    if (server.repl_transfer_fd!=-1) {\n        close(server.repl_transfer_fd);\n        unlink(server.repl_transfer_tmpfile);\n        zfree(server.repl_transfer_tmpfile);\n        server.repl_transfer_tmpfile = NULL;\n        server.repl_transfer_fd = -1;\n    }\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 * If there was a replication handshake in progress 1 is returned and\n * the replication state (server.repl_state) set to REPL_STATE_CONNECT.\n *\n * Otherwise zero is returned and no operation is perforemd at all. */\nint cancelReplicationHandshake(void) {\n    if (server.repl_state == REPL_STATE_TRANSFER) {\n        replicationAbortSyncTransfer();\n        server.repl_state = REPL_STATE_CONNECT;\n    } else if (server.repl_state == REPL_STATE_CONNECTING ||\n               slaveIsInHandshakeState())\n    {\n        undoConnectWithMaster();\n        server.repl_state = REPL_STATE_CONNECT;\n    } else {\n        return 0;\n    }\n    return 1;\n}\n\n/* Set replication to the specified master address and port. */\nvoid replicationSetMaster(char *ip, int port) {\n    int was_master = server.masterhost == NULL;\n\n    sdsfree(server.masterhost);\n    server.masterhost = sdsnew(ip);\n    server.masterport = port;\n    if (server.master) {\n        freeClient(server.master);\n    }\n    disconnectAllBlockedClients(); /* Clients blocked in master, now slave. */\n\n    /* Force our slaves to resync with us as well. They may hopefully be able\n     * to partially resync with us, but we can notify the replid change. */\n    disconnectSlaves();\n    cancelReplicationHandshake();\n    /* Before destroying our master state, create a cached master using\n     * our own parameters, to later PSYNC with the new master. */\n    if (was_master) {\n        replicationDiscardCachedMaster();\n        replicationCacheMasterUsingMyself();\n    }\n\n    /* Fire the role change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,\n                          REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA,\n                          NULL);\n\n    /* Fire the master link modules event. */\n    if (server.repl_state == REPL_STATE_CONNECTED)\n        moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN,\n                              NULL);\n\n    server.repl_state = REPL_STATE_CONNECT;\n}\n\n/* Cancel replication, setting the instance as a master itself. */\nvoid replicationUnsetMaster(void) {\n    if (server.masterhost == NULL) return; /* Nothing to do. */\n\n    /* Fire the master link modules event. */\n    if (server.repl_state == REPL_STATE_CONNECTED)\n        moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN,\n                              NULL);\n\n    sdsfree(server.masterhost);\n    server.masterhost = NULL;\n    /* When a slave is turned into a master, the current replication ID\n     * (that was inherited from the master at synchronization time) is\n     * used as secondary ID up to the current offset, and a new replication\n     * ID is created to continue with a new replication history. */\n    shiftReplicationId();\n    if (server.master) freeClient(server.master);\n    replicationDiscardCachedMaster();\n    cancelReplicationHandshake();\n    /* Disconnecting all the slaves is required: we need to inform slaves\n     * of the replication ID change (see shiftReplicationId() call). However\n     * the slaves will be able to partially resync with us, so it will be\n     * a very fast reconnection. */\n    disconnectSlaves();\n    server.repl_state = REPL_STATE_NONE;\n\n    /* We need to make sure the new master will start the replication stream\n     * with a SELECT statement. This is forced after a full resync, but\n     * with PSYNC version 2, there is no need for full resync after a\n     * master switch. */\n    server.slaveseldb = -1;\n\n    /* Once we turn from slave to master, we consider the starting time without\n     * slaves (that is used to count the replication backlog time to live) as\n     * starting from now. Otherwise the backlog will be freed after a\n     * failover if slaves do not connect immediately. */\n    server.repl_no_slaves_since = server.unixtime;\n\n    /* Fire the role change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,\n                          REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER,\n                          NULL);\n\n    /* Restart the AOF subsystem in case we shut it down during a sync when\n     * we were still a slave. */\n    if (server.aof_enabled && server.aof_state == AOF_OFF) restartAOFAfterSYNC();\n}\n\n/* This function is called when the slave lose the connection with the\n * master into an unexpected way. */\nvoid replicationHandleMasterDisconnection(void) {\n    /* Fire the master link modules event. */\n    if (server.repl_state == REPL_STATE_CONNECTED)\n        moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN,\n                              NULL);\n\n    server.master = NULL;\n    server.repl_state = REPL_STATE_CONNECT;\n    server.repl_down_since = server.unixtime;\n    /* We lost connection with our master, don't disconnect slaves yet,\n     * maybe we'll be able to PSYNC with our master later. We'll disconnect\n     * the slaves only if we'll have to do a full resync with our master. */\n}\n\nvoid replicaofCommand(client *c) {\n    /* SLAVEOF is not allowed in cluster mode as replication is automatically\n     * configured using the current address of the master node. */\n    if (server.cluster_enabled) {\n        addReplyError(c,\"REPLICAOF 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    if (!strcasecmp(c->argv[1]->ptr,\"no\") &&\n        !strcasecmp(c->argv[2]->ptr,\"one\")) {\n        if (server.masterhost) {\n            replicationUnsetMaster();\n            sds client = catClientInfoString(sdsempty(),c);\n            serverLog(LL_NOTICE,\"MASTER MODE enabled (user request from '%s')\",\n                client);\n            sdsfree(client);\n        }\n    } else {\n        long port;\n\n        if (c->flags & CLIENT_SLAVE)\n        {\n            /* If a client is already a replica they cannot run this command,\n             * because it involves flushing all replicas (including this\n             * client) */\n            addReplyError(c, \"Command is not valid when client is a replica.\");\n            return;\n        }\n\n        if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != C_OK))\n            return;\n\n        /* Check if we are already attached to the specified slave */\n        if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr)\n            && server.masterport == port) {\n            serverLog(LL_NOTICE,\"REPLICAOF would result into synchronization \"\n                                \"with the master we are already connected \"\n                                \"with. No operation performed.\");\n            addReplySds(c,sdsnew(\"+OK Already connected to specified \"\n                                 \"master\\r\\n\"));\n            return;\n        }\n        /* There was no previous master or the user specified a different one,\n         * we can continue. */\n        replicationSetMaster(c->argv[1]->ptr, port);\n        sds client = catClientInfoString(sdsempty(),c);\n        serverLog(LL_NOTICE,\"REPLICAOF %s:%d enabled (user request from '%s')\",\n            server.masterhost, server.masterport, client);\n        sdsfree(client);\n    }\n    addReply(c,shared.ok);\n}\n\n/* ROLE command: provide information about the role of the instance\n * (master or slave) and additional information related to replication\n * in an easy to process format. */\nvoid roleCommand(client *c) {\n    if (server.masterhost == NULL) {\n        listIter li;\n        listNode *ln;\n        void *mbcount;\n        int slaves = 0;\n\n        addReplyArrayLen(c,3);\n        addReplyBulkCBuffer(c,\"master\",6);\n        addReplyLongLong(c,server.master_repl_offset);\n        mbcount = addReplyDeferredLen(c);\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n            char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;\n\n            if (slaveip[0] == '\\0') {\n                if (connPeerToString(slave->conn,ip,sizeof(ip),NULL) == -1)\n                    continue;\n                slaveip = ip;\n            }\n            if (slave->replstate != SLAVE_STATE_ONLINE) continue;\n            addReplyArrayLen(c,3);\n            addReplyBulkCString(c,slaveip);\n            addReplyBulkLongLong(c,slave->slave_listening_port);\n            addReplyBulkLongLong(c,slave->repl_ack_off);\n            slaves++;\n        }\n        setDeferredArrayLen(c,mbcount,slaves);\n    } else {\n        char *slavestate = NULL;\n\n        addReplyArrayLen(c,5);\n        addReplyBulkCBuffer(c,\"slave\",5);\n        addReplyBulkCString(c,server.masterhost);\n        addReplyLongLong(c,server.masterport);\n        if (slaveIsInHandshakeState()) {\n            slavestate = \"handshake\";\n        } else {\n            switch(server.repl_state) {\n            case REPL_STATE_NONE: slavestate = \"none\"; break;\n            case REPL_STATE_CONNECT: slavestate = \"connect\"; break;\n            case REPL_STATE_CONNECTING: slavestate = \"connecting\"; break;\n            case REPL_STATE_TRANSFER: slavestate = \"sync\"; break;\n            case REPL_STATE_CONNECTED: slavestate = \"connected\"; break;\n            default: slavestate = \"unknown\"; break;\n            }\n        }\n        addReplyBulkCString(c,slavestate);\n        addReplyLongLong(c,server.master ? server.master->reploff : -1);\n    }\n}\n\n/* Send a REPLCONF ACK command to the master to inform it about the current\n * processed offset. If we are not connected with a master, the command has\n * no effects. */\nvoid replicationSendAck(void) {\n    client *c = server.master;\n\n    if (c != NULL) {\n        c->flags |= CLIENT_MASTER_FORCE_REPLY;\n        addReplyArrayLen(c,3);\n        addReplyBulkCString(c,\"REPLCONF\");\n        addReplyBulkCString(c,\"ACK\");\n        addReplyBulkLongLong(c,c->reploff);\n        c->flags &= ~CLIENT_MASTER_FORCE_REPLY;\n    }\n}\n\n/* ---------------------- MASTER CACHING FOR PSYNC -------------------------- */\n\n/* In order to implement partial synchronization we need to be able to cache\n * our master's client structure after a transient disconnection.\n * It is cached into server.cached_master and flushed away using the following\n * functions. */\n\n/* This function is called by freeClient() in order to cache the master\n * client structure instead of destroying it. freeClient() will return\n * ASAP after this function returns, so every action needed to avoid problems\n * with a client that is really \"suspended\" has to be done by this function.\n *\n * The other functions that will deal with the cached master are:\n *\n * replicationDiscardCachedMaster() that will make sure to kill the client\n * as for some reason we don't want to use it in the future.\n *\n * replicationResurrectCachedMaster() that is used after a successful PSYNC\n * handshake in order to reactivate the cached master.\n */\nvoid replicationCacheMaster(client *c) {\n    serverAssert(server.master != NULL && server.cached_master == NULL);\n    serverLog(LL_NOTICE,\"Caching the disconnected master state.\");\n\n    /* Unlink the client from the server structures. */\n    unlinkClient(c);\n\n    /* Reset the master client so that's ready to accept new commands:\n     * we want to discard te non processed query buffers and non processed\n     * offsets, including pending transactions, already populated arguments,\n     * pending outputs to the master. */\n    sdsclear(server.master->querybuf);\n    sdsclear(server.master->pending_querybuf);\n\n    /* Adjust reploff and read_reploff to the last meaningful offset we\n     * executed. This is the offset the replica will use for future PSYNC. */\n    server.master->reploff = adjustMeaningfulReplOffset();\n    server.master->read_reploff = server.master->reploff;\n    if (c->flags & CLIENT_MULTI) discardTransaction(c);\n    listEmpty(c->reply);\n    c->sentlen = 0;\n    c->reply_bytes = 0;\n    c->bufpos = 0;\n    resetClient(c);\n\n    /* Save the master. Server.master will be set to null later by\n     * replicationHandleMasterDisconnection(). */\n    server.cached_master = server.master;\n\n    /* Invalidate the Peer ID cache. */\n    if (c->peerid) {\n        sdsfree(c->peerid);\n        c->peerid = NULL;\n    }\n\n    /* Caching the master happens instead of the actual freeClient() call,\n     * so make sure to adjust the replication state. This function will\n     * also set server.master to NULL. */\n    replicationHandleMasterDisconnection();\n}\n\n/* If the \"meaningful\" offset, that is the offset without the final PINGs\n * in the stream, is different than the last offset, use it instead:\n * often when the master is no longer reachable, replicas will never\n * receive the PINGs, however the master will end with an incremented\n * offset because of the PINGs and will not be able to incrementally\n * PSYNC with the new master.\n * This function trims the replication backlog when needed, and returns\n * the offset to be used for future partial sync. */\nlong long adjustMeaningfulReplOffset() {\n    if (server.master_repl_offset > server.master_repl_meaningful_offset) {\n        long long delta = server.master_repl_offset -\n                          server.master_repl_meaningful_offset;\n        serverLog(LL_NOTICE,\n            \"Using the meaningful offset %lld instead of %lld to exclude \"\n            \"the final PINGs (%lld bytes difference)\",\n                server.master_repl_meaningful_offset,\n                server.master_repl_offset,\n                delta);\n        server.master_repl_offset = server.master_repl_meaningful_offset;\n        if (server.repl_backlog_histlen <= delta) {\n            server.repl_backlog_histlen = 0;\n            server.repl_backlog_idx = 0;\n        } else {\n            server.repl_backlog_histlen -= delta;\n            server.repl_backlog_idx =\n                (server.repl_backlog_idx + (server.repl_backlog_size - delta)) %\n                server.repl_backlog_size;\n        }\n    }\n    return server.master_repl_offset;\n}\n\n/* This function is called when a master is turend into a slave, in order to\n * create from scratch a cached master for the new client, that will allow\n * to PSYNC with the slave that was promoted as the new master after a\n * failover.\n *\n * Assuming this instance was previously the master instance of the new master,\n * the new master will accept its replication ID, and potentiall also the\n * current offset if no data was lost during the failover. So we use our\n * current replication ID and offset in order to synthesize a cached master. */\nvoid replicationCacheMasterUsingMyself(void) {\n    serverLog(LL_NOTICE,\n        \"Before turning into a replica, using my own master parameters \"\n        \"to synthesize a cached master: I may be able to synchronize with \"\n        \"the new master with just a partial transfer.\");\n\n    /* This will be used to populate the field server.master->reploff\n     * by replicationCreateMasterClient(). We'll later set the created\n     * master as server.cached_master, so the replica will use such\n     * offset for PSYNC. */\n    server.master_initial_offset = adjustMeaningfulReplOffset();\n\n    /* The master client we create can be set to any DBID, because\n     * the new master will start its replication stream with SELECT. */\n    replicationCreateMasterClient(NULL,-1);\n\n    /* Use our own ID / offset. */\n    memcpy(server.master->replid, server.replid, sizeof(server.replid));\n\n    /* Set as cached master. */\n    unlinkClient(server.master);\n    server.cached_master = server.master;\n    server.master = NULL;\n}\n\n/* Free a cached master, called when there are no longer the conditions for\n * a partial resync on reconnection. */\nvoid replicationDiscardCachedMaster(void) {\n    if (server.cached_master == NULL) return;\n\n    serverLog(LL_NOTICE,\"Discarding previously cached master state.\");\n    server.cached_master->flags &= ~CLIENT_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 * This function 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. */\nvoid replicationResurrectCachedMaster(connection *conn) {\n    server.master = server.cached_master;\n    server.cached_master = NULL;\n    server.master->conn = conn;\n    connSetPrivateData(server.master->conn, server.master);\n    server.master->flags &= ~(CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP);\n    server.master->authenticated = 1;\n    server.master->lastinteraction = server.unixtime;\n    server.repl_state = REPL_STATE_CONNECTED;\n    server.repl_down_since = 0;\n\n    /* Re-add to the list of clients. */\n    linkClient(server.master);\n    if (connSetReadHandler(server.master->conn, readQueryFromClient)) {\n        serverLog(LL_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 (clientHasPendingReplies(server.master)) {\n        if (connSetWriteHandler(server.master->conn, sendReplyToClient)) {\n            serverLog(LL_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 * If the option is active, the server will prevent writes if there are not\n * enough connected slaves with the specified lag (or less). */\nvoid refreshGoodSlavesCount(void) {\n    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        client *slave = ln->value;\n        time_t lag = server.unixtime - slave->repl_ack_time;\n\n        if (slave->replstate == SLAVE_STATE_ONLINE &&\n            lag <= server.repl_min_slaves_max_lag) good++;\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 * 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 * 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 * This is how the system works:\n *\n * 1) Every time a new slave connects, we flush the whole script cache.\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 * 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 * 4) On SCRIPT FLUSH command, we replicate the command to all the slaves\n *    and at the same time flush the script cache.\n * 5) When the last slave disconnects, flush the cache.\n * 6) We handle SCRIPT LOAD as well since that's how scripts are loaded\n *    in the master sometimes.\n */\n\n/* Initialize the script cache, only called at startup. */\nvoid replicationScriptCacheInit(void) {\n    server.repl_scriptcache_size = 10000;\n    server.repl_scriptcache_dict = dictCreate(&replScriptCacheDictType,NULL);\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 * 1) Every time a new slave reconnects to this master and performs a\n *    full SYNC (PSYNC does not require flushing).\n * 2) Every time an AOF rewrite is performed.\n * 3) Every time we are left without slaves at all, and AOF is off, in order\n *    to reclaim otherwise unused memory.\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. */\nvoid replicationScriptCacheAdd(sds sha1) {\n    int retval;\n    sds key = sdsdup(sha1);\n\n    /* Evict oldest. */\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        serverAssert(retval == DICT_OK);\n        listDelNode(server.repl_scriptcache_fifo,ln);\n    }\n\n    /* Add current. */\n    retval = dictAdd(server.repl_scriptcache_dict,key,NULL);\n    listAddNodeHead(server.repl_scriptcache_fifo,key);\n    serverAssert(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. */\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        client *slave = ln->value;\n\n        if (slave->replstate != SLAVE_STATE_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(client *c) {\n    mstime_t timeout;\n    long numreplicas, ackreplicas;\n    long long offset = c->woff;\n\n    if (server.masterhost) {\n        addReplyError(c,\"WAIT cannot be used with replica instances. Please also note that since Redis 4.0 if a replica is configured to be writable (which is not the default) writes to replicas are just local and are not propagated.\");\n        return;\n    }\n\n    /* Argument parsing. */\n    if (getLongFromObjectOrReply(c,c->argv[1],&numreplicas,NULL) != C_OK)\n        return;\n    if (getTimeoutFromObjectOrReply(c,c->argv[2],&timeout,UNIT_MILLISECONDS)\n        != C_OK) return;\n\n    /* First try without blocking at all. */\n    ackreplicas = replicationCountAcksByOffset(c->woff);\n    if (ackreplicas >= numreplicas || c->flags & CLIENT_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,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(client *c) {\n    listNode *ln = listSearchKey(server.clients_waiting_acks,c);\n    serverAssert(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        client *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 function, called 1 time per second. */\nvoid replicationCron(void) {\n    static long long replication_cron_loops = 0;\n\n    /* Non blocking connection timeout? */\n    if (server.masterhost &&\n        (server.repl_state == REPL_STATE_CONNECTING ||\n         slaveIsInHandshakeState()) &&\n         (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)\n    {\n        serverLog(LL_WARNING,\"Timeout connecting to the MASTER...\");\n        cancelReplicationHandshake();\n    }\n\n    /* Bulk transfer I/O timeout? */\n    if (server.masterhost && server.repl_state == REPL_STATE_TRANSFER &&\n        (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)\n    {\n        serverLog(LL_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        cancelReplicationHandshake();\n    }\n\n    /* Timed out master when we are an already connected slave? */\n    if (server.masterhost && server.repl_state == REPL_STATE_CONNECTED &&\n        (time(NULL)-server.master->lastinteraction) > server.repl_timeout)\n    {\n        serverLog(LL_WARNING,\"MASTER timeout: no data nor PING received...\");\n        freeClient(server.master);\n    }\n\n    /* Check if we should connect to a MASTER */\n    if (server.repl_state == REPL_STATE_CONNECT) {\n        serverLog(LL_NOTICE,\"Connecting to MASTER %s:%d\",\n            server.masterhost, server.masterport);\n        if (connectWithMaster() == C_OK) {\n            serverLog(LL_NOTICE,\"MASTER <-> REPLICA 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    if (server.masterhost && server.master &&\n        !(server.master->flags & CLIENT_PRE_PSYNC))\n        replicationSendAck();\n\n    /* If we have attached slaves, PING them from time to time.\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    listIter li;\n    listNode *ln;\n    robj *ping_argv[1];\n\n    /* First, send PING according to ping_slave_period. */\n    if ((replication_cron_loops % server.repl_ping_slave_period) == 0 &&\n        listLength(server.slaves))\n    {\n        /* Note that we don't send the PING if the clients are paused during\n         * a Redis Cluster manual failover: the PING we send will otherwise\n         * alter the replication offsets of master and slave, and will no longer\n         * match the one stored into 'mf_master_offset' state. */\n        int manual_failover_in_progress =\n            server.cluster_enabled &&\n            server.cluster->mf_end &&\n            clientsArePaused();\n\n        if (!manual_failover_in_progress) {\n            long long before_ping = server.master_repl_meaningful_offset;\n            ping_argv[0] = createStringObject(\"PING\",4);\n            replicationFeedSlaves(server.slaves, server.slaveseldb,\n                ping_argv, 1);\n            decrRefCount(ping_argv[0]);\n            /* The server.master_repl_meaningful_offset variable represents\n             * the offset of the replication stream without the pending PINGs.\n             * This is useful to set the right replication offset for PSYNC\n             * when the master is turned into a replica. Otherwise pending\n             * PINGs may not allow it to perform an incremental sync with the\n             * new master. */\n            server.master_repl_meaningful_offset = before_ping;\n        }\n    }\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     * Also send the a newline to all the chained slaves we have, if we lost\n     * connection from our master, to keep the slaves aware that their\n     * master is online. This is needed since sub-slaves only receive proxied\n     * data from top-level masters, so there is no explicit pinging in order\n     * to avoid altering the replication offsets. This special out of band\n     * pings (newlines) can be sent, they will have no effect in the offset.\n     *\n     * The newline will be ignored by the slave but will refresh the\n     * last interaction timer preventing a timeout. In this case we ignore the\n     * ping period and refresh the connection once per second since certain\n     * timeouts are set at a few seconds (example: PSYNC response). */\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        int is_presync =\n            (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||\n            (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&\n             server.rdb_child_type != RDB_CHILD_TYPE_SOCKET));\n\n        if (is_presync) {\n            connWrite(slave->conn, \"\\n\", 1);\n        }\n    }\n\n    /* Disconnect timedout slaves. */\n    if (listLength(server.slaves)) {\n        listIter li;\n        listNode *ln;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n\n            if (slave->replstate != SLAVE_STATE_ONLINE) continue;\n            if (slave->flags & CLIENT_PRE_PSYNC) continue;\n            if ((server.unixtime - slave->repl_ack_time) > server.repl_timeout)\n            {\n                serverLog(LL_WARNING, \"Disconnecting timedout replica: %s\",\n                    replicationGetSlaveName(slave));\n                freeClient(slave);\n            }\n        }\n    }\n\n    /* If this is a master without attached slaves and there is a replication\n     * backlog active, in order to reclaim memory we can free it after some\n     * (configured) time. Note that this cannot be done for slaves: slaves\n     * without sub-slaves attached should still accumulate data into the\n     * backlog, in order to reply to PSYNC queries if they are turned into\n     * masters after a failover. */\n    if (listLength(server.slaves) == 0 && server.repl_backlog_time_limit &&\n        server.repl_backlog && server.masterhost == NULL)\n    {\n        time_t idle = server.unixtime - server.repl_no_slaves_since;\n\n        if (idle > server.repl_backlog_time_limit) {\n            /* When we free the backlog, we always use a new\n             * replication ID and clear the ID2. This is needed\n             * because when there is no backlog, the master_repl_offset\n             * is not updated, but we would still retain our replication\n             * ID, leading to the following problem:\n             *\n             * 1. We are a master instance.\n             * 2. Our slave is promoted to master. It's repl-id-2 will\n             *    be the same as our repl-id.\n             * 3. We, yet as master, receive some updates, that will not\n             *    increment the master_repl_offset.\n             * 4. Later we are turned into a slave, connect to the new\n             *    master that will accept our PSYNC request by second\n             *    replication ID, but there will be data inconsistency\n             *    because we received writes. */\n            changeReplicationId();\n            clearReplicationId2();\n            freeReplicationBacklog();\n            serverLog(LL_NOTICE,\n                \"Replication backlog freed after %d seconds \"\n                \"without connected replicas.\",\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    if (listLength(server.slaves) == 0 &&\n        server.aof_state == AOF_OFF &&\n        listLength(server.repl_scriptcache_fifo) != 0)\n    {\n        replicationScriptCacheFlush();\n    }\n\n    /* Start a BGSAVE good for replication if we have slaves in\n     * WAIT_BGSAVE_START state.\n     *\n     * In case of diskless replication, we make sure to wait the specified\n     * number of seconds (according to configuration) so that other slaves\n     * have the time to arrive before we start streaming. */\n    if (!hasActiveChildProcess()) {\n        time_t idle, max_idle = 0;\n        int slaves_waiting = 0;\n        int mincapa = -1;\n        listNode *ln;\n        listIter li;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n                idle = server.unixtime - slave->lastinteraction;\n                if (idle > max_idle) max_idle = idle;\n                slaves_waiting++;\n                mincapa = (mincapa == -1) ? slave->slave_capa :\n                                            (mincapa & slave->slave_capa);\n            }\n        }\n\n        if (slaves_waiting &&\n            (!server.repl_diskless_sync ||\n             max_idle > server.repl_diskless_sync_delay))\n        {\n            /* Start the BGSAVE. The called function may start a\n             * BGSAVE with socket target or disk target depending on the\n             * configuration and slaves capabilities. */\n            startBgsaveForReplication(mincapa);\n        }\n    }\n\n    /* Remove the RDB file used for replication if Redis is not running\n     * with any persistence. */\n    removeRDBUsedToSyncReplicas();\n\n    /* Refresh the number of slaves with lag <= min-slaves-max-lag. */\n    refreshGoodSlavesCount();\n    replication_cron_loops++; /* Incremented with frequency 1 HZ. */\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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. 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 * A rio object provides the following methods:\n *  read: read from stream.\n *  write: write to stream.\n *  tell: get the current offset.\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 * ----------------------------------------------------------------------------\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 \"server.h\"\n\n/* ------------------------- Buffer I/O implementation ----------------------- */\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioBufferWrite(rio *r, const void *buf, size_t len) {\n    r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);\n    r->io.buffer.pos += len;\n    return 1;\n}\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioBufferRead(rio *r, void *buf, size_t len) {\n    if (sdslen(r->io.buffer.ptr)-r->io.buffer.pos < len)\n        return 0; /* not enough buffer to return len bytes. */\n    memcpy(buf,r->io.buffer.ptr+r->io.buffer.pos,len);\n    r->io.buffer.pos += len;\n    return 1;\n}\n\n/* Returns read/write position in buffer. */\nstatic off_t rioBufferTell(rio *r) {\n    return r->io.buffer.pos;\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioBufferFlush(rio *r) {\n    UNUSED(r);\n    return 1; /* Nothing to do, our write just appends to the buffer. */\n}\n\nstatic const rio rioBufferIO = {\n    rioBufferRead,\n    rioBufferWrite,\n    rioBufferTell,\n    rioBufferFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\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/* --------------------- Stdio file pointer implementation ------------------- */\n\n/* Returns 1 or 0 for success/failure. */\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    if (r->io.file.autosync &&\n        r->io.file.buffered >= r->io.file.autosync)\n    {\n        fflush(r->io.file.fp);\n        redis_fsync(fileno(r->io.file.fp));\n        r->io.file.buffered = 0;\n    }\n    return retval;\n}\n\n/* Returns 1 or 0 for success/failure. */\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. */\nstatic off_t rioFileTell(rio *r) {\n    return ftello(r->io.file.fp);\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioFileFlush(rio *r) {\n    return (fflush(r->io.file.fp) == 0) ? 1 : 0;\n}\n\nstatic const rio rioFileIO = {\n    rioFileRead,\n    rioFileWrite,\n    rioFileTell,\n    rioFileFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\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/* ------------------- Connection implementation -------------------\n * We use this RIO implemetnation when reading an RDB file directly from\n * the connection to the memory via rdbLoadRio(), thus this implementation\n * only implements reading from a connection that is, normally,\n * just a socket. */\n\nstatic size_t rioConnWrite(rio *r, const void *buf, size_t len) {\n    UNUSED(r);\n    UNUSED(buf);\n    UNUSED(len);\n    return 0; /* Error, this target does not yet support writing. */\n}\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioConnRead(rio *r, void *buf, size_t len) {\n    size_t avail = sdslen(r->io.conn.buf)-r->io.conn.pos;\n\n    /* If the buffer is too small for the entire request: realloc. */\n    if (sdslen(r->io.conn.buf) + sdsavail(r->io.conn.buf) < len)\n        r->io.conn.buf = sdsMakeRoomFor(r->io.conn.buf, len - sdslen(r->io.conn.buf));\n\n    /* If the remaining unused buffer is not large enough: memmove so that we\n     * can read the rest. */\n    if (len > avail && sdsavail(r->io.conn.buf) < len - avail) {\n        sdsrange(r->io.conn.buf, r->io.conn.pos, -1);\n        r->io.conn.pos = 0;\n    }\n\n    /* If we don't already have all the data in the sds, read more */\n    while (len > sdslen(r->io.conn.buf) - r->io.conn.pos) {\n        size_t buffered = sdslen(r->io.conn.buf) - r->io.conn.pos;\n        size_t toread = len - buffered;\n        /* Read either what's missing, or PROTO_IOBUF_LEN, the bigger of\n         * the two. */\n        if (toread < PROTO_IOBUF_LEN) toread = PROTO_IOBUF_LEN;\n        if (toread > sdsavail(r->io.conn.buf)) toread = sdsavail(r->io.conn.buf);\n        if (r->io.conn.read_limit != 0 &&\n            r->io.conn.read_so_far + buffered + toread > r->io.conn.read_limit)\n        {\n            if (r->io.conn.read_limit >= r->io.conn.read_so_far - buffered)\n                toread = r->io.conn.read_limit - r->io.conn.read_so_far - buffered;\n            else {\n                errno = EOVERFLOW;\n                return 0;\n            }\n        }\n        int retval = connRead(r->io.conn.conn,\n                          (char*)r->io.conn.buf + sdslen(r->io.conn.buf),\n                          toread);\n        if (retval <= 0) {\n            if (errno == EWOULDBLOCK) errno = ETIMEDOUT;\n            return 0;\n        }\n        sdsIncrLen(r->io.conn.buf, retval);\n    }\n\n    memcpy(buf, (char*)r->io.conn.buf + r->io.conn.pos, len);\n    r->io.conn.read_so_far += len;\n    r->io.conn.pos += len;\n    return len;\n}\n\n/* Returns read/write position in file. */\nstatic off_t rioConnTell(rio *r) {\n    return r->io.conn.read_so_far;\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioConnFlush(rio *r) {\n    /* Our flush is implemented by the write method, that recognizes a\n     * buffer set to NULL with a count of zero as a flush request. */\n    return rioConnWrite(r,NULL,0);\n}\n\nstatic const rio rioConnIO = {\n    rioConnRead,\n    rioConnWrite,\n    rioConnTell,\n    rioConnFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\n};\n\n/* Create an RIO that implements a buffered read from an fd\n * read_limit argument stops buffering when the reaching the limit. */\nvoid rioInitWithConn(rio *r, connection *conn, size_t read_limit) {\n    *r = rioConnIO;\n    r->io.conn.conn = conn;\n    r->io.conn.pos = 0;\n    r->io.conn.read_limit = read_limit;\n    r->io.conn.read_so_far = 0;\n    r->io.conn.buf = sdsnewlen(NULL, PROTO_IOBUF_LEN);\n    sdsclear(r->io.conn.buf);\n}\n\n/* Release the RIO tream. Optionally returns the unread buffered data\n * when the SDS pointer 'remaining' is passed. */\nvoid rioFreeConn(rio *r, sds *remaining) {\n    if (remaining && (size_t)r->io.conn.pos < sdslen(r->io.conn.buf)) {\n        if (r->io.conn.pos > 0) sdsrange(r->io.conn.buf, r->io.conn.pos, -1);\n        *remaining = r->io.conn.buf;\n    } else {\n        sdsfree(r->io.conn.buf);\n        if (remaining) *remaining = NULL;\n    }\n    r->io.conn.buf = NULL;\n}\n\n/* ------------------- File descriptor implementation ------------------\n * This target is used to write the RDB file to pipe, when the master just\n * streams the data to the replicas without creating an RDB on-disk image\n * (diskless replication option).\n * It only implements writes. */\n\n/* Returns 1 or 0 for success/failure.\n *\n * When buf is NULL and len is 0, the function performs a flush operation\n * if there is some pending buffer, so this function is also used in order\n * to implement rioFdFlush(). */\nstatic size_t rioFdWrite(rio *r, const void *buf, size_t len) {\n    ssize_t retval;\n    unsigned char *p = (unsigned char*) buf;\n    int doflush = (buf == NULL && len == 0);\n\n    /* For small writes, we rather keep the data in user-space buffer, and flush\n     * it only when it grows. however for larger writes, we prefer to flush\n     * any pre-existing buffer, and write the new one directly without reallocs\n     * and memory copying. */\n    if (len > PROTO_IOBUF_LEN) {\n        /* First, flush any pre-existing buffered data. */\n        if (sdslen(r->io.fd.buf)) {\n            if (rioFdWrite(r, NULL, 0) == 0)\n                return 0;\n        }\n        /* Write the new data, keeping 'p' and 'len' from the input. */\n    } else {\n        if (len) {\n            r->io.fd.buf = sdscatlen(r->io.fd.buf,buf,len);\n            if (sdslen(r->io.fd.buf) > PROTO_IOBUF_LEN)\n                doflush = 1;\n            if (!doflush)\n                return 1;\n        }\n        /* Flusing the buffered data. set 'p' and 'len' accordintly. */\n        p = (unsigned char*) r->io.fd.buf;\n        len = sdslen(r->io.fd.buf);\n    }\n\n    size_t nwritten = 0;\n    while(nwritten != len) {\n        retval = write(r->io.fd.fd,p+nwritten,len-nwritten);\n        if (retval <= 0) {\n            /* With blocking io, which is the sole user of this\n             * rio target, EWOULDBLOCK is returned only because of\n             * the SO_SNDTIMEO socket option, so we translate the error\n             * into one more recognizable by the user. */\n            if (retval == -1 && errno == EWOULDBLOCK) errno = ETIMEDOUT;\n            return 0; /* error. */\n        }\n        nwritten += retval;\n    }\n\n    r->io.fd.pos += len;\n    sdsclear(r->io.fd.buf);\n    return 1;\n}\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioFdRead(rio *r, void *buf, size_t len) {\n    UNUSED(r);\n    UNUSED(buf);\n    UNUSED(len);\n    return 0; /* Error, this target does not support reading. */\n}\n\n/* Returns read/write position in file. */\nstatic off_t rioFdTell(rio *r) {\n    return r->io.fd.pos;\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioFdFlush(rio *r) {\n    /* Our flush is implemented by the write method, that recognizes a\n     * buffer set to NULL with a count of zero as a flush request. */\n    return rioFdWrite(r,NULL,0);\n}\n\nstatic const rio rioFdIO = {\n    rioFdRead,\n    rioFdWrite,\n    rioFdTell,\n    rioFdFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\n};\n\nvoid rioInitWithFd(rio *r, int fd) {\n    *r = rioFdIO;\n    r->io.fd.fd = fd;\n    r->io.fd.pos = 0;\n    r->io.fd.buf = sdsempty();\n}\n\n/* release the rio stream. */\nvoid rioFreeFd(rio *r) {\n    sdsfree(r->io.fd.buf);\n}\n\n/* ---------------------------- Generic functions ---------------------------- */\n\n/* This function can be installed both in memory and file streams when checksum\n * computation is needed. */\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 * By default this is set to zero that means no automatic file sync is\n * performed.\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. */\nvoid rioSetAutoSync(rio *r, off_t bytes) {\n    if(r->write != rioFileIO.write) return;\n    r->io.file.autosync = bytes;\n}\n\n/* --------------------------- Higher level interface --------------------------\n *\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/* Write multi bulk count in the format: \"*<count>\\r\\n\". */\nsize_t rioWriteBulkCount(rio *r, char prefix, long count) {\n    char cbuf[128];\n    int clen;\n\n    cbuf[0] = prefix;\n    clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,count);\n    cbuf[clen++] = '\\r';\n    cbuf[clen++] = '\\n';\n    if (rioWrite(r,cbuf,clen) == 0) return 0;\n    return clen;\n}\n\n/* Write binary-safe string in the format: \"$<count>\\r\\n<payload>\\r\\n\". */\nsize_t rioWriteBulkString(rio *r, const char *buf, size_t len) {\n    size_t nwritten;\n\n    if ((nwritten = rioWriteBulkCount(r,'$',len)) == 0) return 0;\n    if (len > 0 && rioWrite(r,buf,len) == 0) return 0;\n    if (rioWrite(r,\"\\r\\n\",2) == 0) return 0;\n    return nwritten+len+2;\n}\n\n/* Write a long long value in format: \"$<count>\\r\\n<payload>\\r\\n\". */\nsize_t rioWriteBulkLongLong(rio *r, long long l) {\n    char lbuf[32];\n    unsigned int llen;\n\n    llen = ll2string(lbuf,sizeof(lbuf),l);\n    return rioWriteBulkString(r,lbuf,llen);\n}\n\n/* Write a double value in the format: \"$<count>\\r\\n<payload>\\r\\n\" */\nsize_t rioWriteBulkDouble(rio *r, double d) {\n    char dbuf[128];\n    unsigned int dlen;\n\n    dlen = snprintf(dbuf,sizeof(dbuf),\"%.17g\",d);\n    return rioWriteBulkString(r,dbuf,dlen);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/rio.h",
    "content": "/*\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2019, 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#include \"connection.h\"\n\n#define RIO_FLAG_READ_ERROR (1<<0)\n#define RIO_FLAG_WRITE_ERROR (1<<1)\n\nstruct _rio {\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    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    int (*flush)(struct _rio *);\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    void (*update_cksum)(struct _rio *, const void *buf, size_t len);\n\n    /* The current checksum and flags (see RIO_FLAG_*) */\n    uint64_t cksum, flags;\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        /* In-memory buffer target. */\n        struct {\n            sds ptr;\n            off_t pos;\n        } buffer;\n        /* Stdio file pointer target. */\n        struct {\n            FILE *fp;\n            off_t buffered; /* Bytes written since last fsync. */\n            off_t autosync; /* fsync after 'autosync' bytes written. */\n        } file;\n        /* Connection object (used to read from socket) */\n        struct {\n            connection *conn;   /* Connection */\n            off_t pos;    /* pos in buf that was returned */\n            sds buf;      /* buffered data */\n            size_t read_limit;  /* don't allow to buffer/read more than that */\n            size_t read_so_far; /* amount of data read from the rio (not buffered) */\n        } conn;\n        /* FD target (used to write to pipe). */\n        struct {\n            int fd;       /* File descriptor. */\n            off_t pos;\n            sds buf;\n        } fd;\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\nstatic inline size_t rioWrite(rio *r, const void *buf, size_t len) {\n    if (r->flags & RIO_FLAG_WRITE_ERROR) return 0;\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            r->flags |= RIO_FLAG_WRITE_ERROR;\n            return 0;\n        }\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\nstatic inline size_t rioRead(rio *r, void *buf, size_t len) {\n    if (r->flags & RIO_FLAG_READ_ERROR) return 0;\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            r->flags |= RIO_FLAG_READ_ERROR;\n            return 0;\n        }\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\nstatic inline off_t rioTell(rio *r) {\n    return r->tell(r);\n}\n\nstatic inline int rioFlush(rio *r) {\n    return r->flush(r);\n}\n\n/* This function allows to know if there was a read error in any past\n * operation, since the rio stream was created or since the last call\n * to rioClearError(). */\nstatic inline int rioGetReadError(rio *r) {\n    return (r->flags & RIO_FLAG_READ_ERROR) != 0;\n}\n\n/* Like rioGetReadError() but for write errors. */\nstatic inline int rioGetWriteError(rio *r) {\n    return (r->flags & RIO_FLAG_WRITE_ERROR) != 0;\n}\n\nstatic inline void rioClearErrors(rio *r) {\n    r->flags &= ~(RIO_FLAG_READ_ERROR|RIO_FLAG_WRITE_ERROR);\n}\n\nvoid rioInitWithFile(rio *r, FILE *fp);\nvoid rioInitWithBuffer(rio *r, sds s);\nvoid rioInitWithConn(rio *r, connection *conn, size_t read_limit);\nvoid rioInitWithFd(rio *r, int fd);\n\nvoid rioFreeFd(rio *r);\nvoid rioFreeConn(rio *r, sds* out_remainingBufferedData);\n\nsize_t rioWriteBulkCount(rio *r, char prefix, long 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\nstruct redisObject;\nint rioWriteBulkObject(rio *r, struct redisObject *obj);\n\nvoid rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);\nvoid rioSetAutoSync(rio *r, off_t bytes);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"sha1.h\"\n#include \"rand.h\"\n#include \"cluster.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_Aggregate(lua_State *lua, char *reply, int atype);\nchar *redisProtocolToLuaType_Null(lua_State *lua, char *reply);\nchar *redisProtocolToLuaType_Bool(lua_State *lua, char *reply, int tf);\nchar *redisProtocolToLuaType_Double(lua_State *lua, char *reply);\nint redis_math_random (lua_State *L);\nint redis_math_randomseed (lua_State *L);\nvoid ldbInit(void);\nvoid ldbDisable(client *c);\nvoid ldbEnable(client *c);\nvoid evalGenericCommandWithDebugging(client *c, int evalsha);\nvoid luaLdbLineHook(lua_State *lua, lua_Debug *ar);\nvoid ldbLog(sds entry);\nvoid ldbLogRedisReply(char *reply);\nsds ldbCatStackValue(sds s, lua_State *lua, int idx);\n\n/* Debugger shared state is stored inside this global structure. */\n#define LDB_BREAKPOINTS_MAX 64  /* Max number of breakpoints. */\n#define LDB_MAX_LEN_DEFAULT 256 /* Default len limit for replies / var dumps. */\nstruct ldbState {\n    connection *conn; /* Connection of the debugging client. */\n    int active; /* Are we debugging EVAL right now? */\n    int forked; /* Is this a fork()ed debugging session? */\n    list *logs; /* List of messages to send to the client. */\n    list *traces; /* Messages about Redis commands executed since last stop.*/\n    list *children; /* All forked debugging sessions pids. */\n    int bp[LDB_BREAKPOINTS_MAX]; /* An array of breakpoints line numbers. */\n    int bpcount; /* Number of valid entries inside bp. */\n    int step;   /* Stop at next line ragardless of breakpoints. */\n    int luabp;  /* Stop at next line because redis.breakpoint() was called. */\n    sds *src;   /* Lua script source code split by line. */\n    int lines;  /* Number of lines in 'src'. */\n    int currentline;    /* Current line number. */\n    sds cbuf;   /* Debugger client command buffer. */\n    size_t maxlen;  /* Max var dump / reply length. */\n    int maxlen_hint_sent; /* Did we already hint about \"set maxlen\"? */\n} ldb;\n\n/* ---------------------------------------------------------------------------\n * Utility functions.\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 * 'digest' should point to a 41 bytes buffer: 40 for SHA1 converted into an\n * hexadecimal number, plus 1 byte for null term. */\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 * Redis reply to Lua type conversion functions.\n * ------------------------------------------------------------------------- */\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 * 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. The script\n * is like a normal client that bypasses all the slow I/O paths.\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 * Errors are returned as a table with a single 'err' field set to the\n * error string.\n */\n\nchar *redisProtocolToLuaType(lua_State *lua, char* reply) {\n    char *p = reply;\n\n    switch(*p) {\n    case ':': p = redisProtocolToLuaType_Int(lua,reply); break;\n    case '$': p = redisProtocolToLuaType_Bulk(lua,reply); break;\n    case '+': p = redisProtocolToLuaType_Status(lua,reply); break;\n    case '-': p = redisProtocolToLuaType_Error(lua,reply); break;\n    case '*': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;\n    case '%': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;\n    case '~': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;\n    case '_': p = redisProtocolToLuaType_Null(lua,reply); break;\n    case '#': p = redisProtocolToLuaType_Bool(lua,reply,p[1]); break;\n    case ',': p = redisProtocolToLuaType_Double(lua,reply); break;\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_Aggregate(lua_State *lua, char *reply, int atype) {\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    if (server.lua_client->resp == 2 || atype == '*') {\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    } else if (server.lua_client->resp == 3) {\n        /* Here we handle only Set and Map replies in RESP3 mode, since arrays\n         * follow the above RESP2 code path. Note that those are represented\n         * as a table with the \"map\" or \"set\" field populated with the actual\n         * table representing the set or the map type. */\n        p += 2;\n        lua_newtable(lua);\n        lua_pushstring(lua,atype == '%' ? \"map\" : \"set\");\n        lua_newtable(lua);\n        for (j = 0; j < mbulklen; j++) {\n            p = redisProtocolToLuaType(lua,p);\n            if (atype == '%') {\n                p = redisProtocolToLuaType(lua,p);\n            } else {\n                lua_pushboolean(lua,1);\n            }\n            lua_settable(lua,-3);\n        }\n        lua_settable(lua,-3);\n    }\n    return p;\n}\n\nchar *redisProtocolToLuaType_Null(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    lua_pushnil(lua);\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_Bool(lua_State *lua, char *reply, int tf) {\n    char *p = strchr(reply+1,'\\r');\n    lua_pushboolean(lua,tf == 't');\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_Double(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    char buf[MAX_LONG_DOUBLE_CHARS+1];\n    size_t len = p-reply-1;\n    double d;\n\n    if (len <= MAX_LONG_DOUBLE_CHARS) {\n        memcpy(buf,reply+1,len);\n        buf[len] = '\\0';\n        d = strtod(buf,NULL); /* We expect a valid representation. */\n    } else {\n        d = 0;\n    }\n\n    lua_newtable(lua);\n    lua_pushstring(lua,\"double\");\n    lua_pushnumber(lua,d);\n    lua_settable(lua,-3);\n    return p+2;\n}\n\n/* This function is used in order to push an error on the Lua stack in the\n * format used by redis.pcall to return errors, which is a lua table\n * with a single \"err\" field set to the error string. Note that this\n * table is never a valid reply by proper commands, since the returned\n * tables are otherwise always indexed by integers, never by strings. */\nvoid luaPushError(lua_State *lua, char *error) {\n    lua_Debug dbg;\n\n    /* If debugging is active and in step mode, log errors resulting from\n     * Redis commands. */\n    if (ldb.active && ldb.step) {\n        ldbLog(sdscatprintf(sdsempty(),\"<error> %s\",error));\n    }\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/* In case the error set into the Lua stack by luaPushError() was generated\n * by the non-error-trapping version of redis.pcall(), which is redis.call(),\n * this function will raise the Lua error so that the execution of the\n * script will be halted. */\nint luaRaiseError(lua_State *lua) {\n    lua_pushstring(lua,\"err\");\n    lua_gettable(lua,-2);\n    return lua_error(lua);\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 * The array is sorted using table.sort itself, and assuming all the\n * list elements are strings. */\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 reply to Redis reply conversion functions.\n * ------------------------------------------------------------------------- */\n\n/* Reply to client 'c' converting the top element in the Lua stack to a\n * Redis reply. As a side effect the element is consumed from the stack.  */\nvoid luaReplyToRedisReply(client *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        if (server.lua_client->resp == 2)\n            addReply(c,lua_toboolean(lua,-1) ? shared.cone :\n                                               shared.null[c->resp]);\n        else\n            addReplyBool(c,lua_toboolean(lua,-1));\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'\n         * field. */\n\n        /* Handle error reply. */\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        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle status reply. */\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,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle double reply. */\n        lua_pushstring(lua,\"double\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TNUMBER) {\n            addReplyDouble(c,lua_tonumber(lua,-1));\n            lua_pop(lua,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle map reply. */\n        lua_pushstring(lua,\"map\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TTABLE) {\n            int maplen = 0;\n            void *replylen = addReplyDeferredLen(c);\n            lua_pushnil(lua); /* Use nil to start iteration. */\n            while (lua_next(lua,-2)) {\n                /* Stack now: table, key, value */\n                luaReplyToRedisReply(c, lua); /* Return value. */\n                lua_pushvalue(lua,-1);        /* Dup key before consuming. */\n                luaReplyToRedisReply(c, lua); /* Return key. */\n                /* Stack now: table, key. */\n                maplen++;\n            }\n            setDeferredMapLen(c,replylen,maplen);\n            lua_pop(lua,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle set reply. */\n        lua_pushstring(lua,\"set\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TTABLE) {\n            int setlen = 0;\n            void *replylen = addReplyDeferredLen(c);\n            lua_pushnil(lua); /* Use nil to start iteration. */\n            while (lua_next(lua,-2)) {\n                /* Stack now: table, key, true */\n                lua_pop(lua,1);               /* Discard the boolean value. */\n                lua_pushvalue(lua,-1);        /* Dup key before consuming. */\n                luaReplyToRedisReply(c, lua); /* Return key. */\n                /* Stack now: table, key. */\n                setlen++;\n            }\n            setDeferredSetLen(c,replylen,setlen);\n            lua_pop(lua,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle the array reply. */\n        void *replylen = addReplyDeferredLen(c);\n        int j = 1, mbulklen = 0;\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        setDeferredArrayLen(c,replylen,mbulklen);\n        break;\n    default:\n        addReplyNull(c);\n    }\n    lua_pop(lua,1);\n}\n\n/* ---------------------------------------------------------------------------\n * Lua redis.* functions implementations.\n * ------------------------------------------------------------------------- */\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    client *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 size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE];\n    static int inuse = 0;   /* Recursive calls detection. */\n\n    /* By using Lua debug hooks it is possible to trigger a recursive call\n     * to luaRedisGenericCommand(), which normally should never happen.\n     * To make this function reentrant is futile and makes it slower, but\n     * we should at least detect such a misuse, and abort. */\n    if (inuse) {\n        char *recursion_warning =\n            \"luaRedisGenericCommand() recursive call detected. \"\n            \"Are you doing funny stuff with Lua debug hooks?\";\n        serverLog(LL_WARNING,\"%s\",recursion_warning);\n        luaPushError(lua,recursion_warning);\n        return 1;\n    }\n    inuse++;\n\n    /* Require at least one argument */\n    if (argc == 0) {\n        luaPushError(lua,\n            \"Please specify at least one argument for redis.call()\");\n        inuse--;\n        return raise_error ? luaRaiseError(lua) : 1;\n    }\n\n    /* Build the arguments vector */\n    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        char dbuf[64];\n\n        if (lua_type(lua,j+1) == LUA_TNUMBER) {\n            /* We can't use lua_tolstring() for number -> string conversion\n             * since Lua uses a format specifier that loses precision. */\n            lua_Number num = lua_tonumber(lua,j+1);\n\n            obj_len = snprintf(dbuf,sizeof(dbuf),\"%.17g\",(double)num);\n            obj_s = dbuf;\n        } else {\n            obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);\n            if (obj_s == NULL) break; /* Not a string. */\n        }\n\n        /* Try to use a cached object. */\n        if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] &&\n            cached_objects_len[j] >= obj_len)\n        {\n            sds s = cached_objects[j]->ptr;\n            argv[j] = cached_objects[j];\n            cached_objects[j] = NULL;\n            memcpy(s,obj_s,obj_len+1);\n            sdssetlen(s, 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    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        inuse--;\n        return raise_error ? luaRaiseError(lua) : 1;\n    }\n\n    /* Setup our fake client for command execution */\n    c->argv = argv;\n    c->argc = argc;\n    c->user = server.lua_caller->user;\n\n    /* Process module hooks */\n    moduleCallCommandFilters(c);\n    argv = c->argv;\n    argc = c->argc;\n\n    /* Log the command if debugging is active. */\n    if (ldb.active && ldb.step) {\n        sds cmdlog = sdsnew(\"<redis>\");\n        for (j = 0; j < c->argc; j++) {\n            if (j == 10) {\n                cmdlog = sdscatprintf(cmdlog,\" ... (%d more)\",\n                    c->argc-j-1);\n                break;\n            } else {\n                cmdlog = sdscatlen(cmdlog,\" \",1);\n                cmdlog = sdscatsds(cmdlog,c->argv[j]->ptr);\n            }\n        }\n        ldbLog(cmdlog);\n    }\n\n    /* Command lookup */\n    cmd = lookupCommand(argv[0]->ptr);\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    c->cmd = c->lastcmd = cmd;\n\n    /* There are commands that are not allowed inside scripts. */\n    if (cmd->flags & CMD_NOSCRIPT) {\n        luaPushError(lua, \"This Redis command is not allowed from scripts\");\n        goto cleanup;\n    }\n\n    /* Check the ACLs. */\n    int acl_keypos;\n    int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);\n    if (acl_retval != ACL_OK) {\n        addACLLogEntry(c,acl_retval,acl_keypos,NULL);\n        if (acl_retval == ACL_DENIED_CMD)\n            luaPushError(lua, \"The user executing the script can't run this \"\n                              \"command or subcommand\");\n        else\n            luaPushError(lua, \"The user executing the script can't access \"\n                              \"at least one of the keys mentioned in the \"\n                              \"command arguments\");\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    if (cmd->flags & CMD_WRITE) {\n        int deny_write_type = writeCommandsDeniedByDiskError();\n        if (server.lua_random_dirty && !server.lua_replicate_commands) {\n            luaPushError(lua,\n                \"Write commands not allowed after non deterministic commands. Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode.\");\n            goto cleanup;\n        } else if (server.masterhost && server.repl_slave_ro &&\n                   !server.loading &&\n                   !(server.lua_caller->flags & CLIENT_MASTER))\n        {\n            luaPushError(lua, shared.roslaveerr->ptr);\n            goto cleanup;\n        } else if (deny_write_type != DISK_ERROR_TYPE_NONE) {\n            if (deny_write_type == DISK_ERROR_TYPE_RDB) {\n                luaPushError(lua, shared.bgsaveerr->ptr);\n            } else {\n                sds aof_write_err = sdscatfmt(sdsempty(),\n                    \"-MISCONF Errors writing to the AOF file: %s\\r\\n\",\n                    strerror(server.aof_last_write_errno));\n                luaPushError(lua, aof_write_err);\n                sdsfree(aof_write_err);\n            }\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    if (server.maxmemory &&             /* Maxmemory is actually enabled. */\n        !server.loading &&              /* Don't care about mem if loading. */\n        !server.masterhost &&           /* Slave must execute the script. */\n        server.lua_write_dirty == 0 &&  /* Script had no side effects so far. */\n        server.lua_oom &&               /* Detected OOM when script start. */\n        (cmd->flags & CMD_DENYOOM))\n    {\n        luaPushError(lua, shared.oomerr->ptr);\n        goto cleanup;\n    }\n\n    if (cmd->flags & CMD_RANDOM) server.lua_random_dirty = 1;\n    if (cmd->flags & CMD_WRITE) server.lua_write_dirty = 1;\n\n    /* If this is a Redis Cluster node, we need to make sure Lua is not\n     * trying to access non-local keys, with the exception of commands\n     * received from our master or when loading the AOF back in memory. */\n    if (server.cluster_enabled && !server.loading &&\n        !(server.lua_caller->flags & CLIENT_MASTER))\n    {\n        int error_code;\n        /* Duplicate relevant flags in the lua client. */\n        c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);\n        c->flags |= server.lua_caller->flags & (CLIENT_READONLY|CLIENT_ASKING);\n        if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,&error_code) !=\n                           server.cluster->myself)\n        {\n            if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) { \n                luaPushError(lua,\n                    \"Lua script attempted to execute a write command while the \"\n                    \"cluster is down and readonly\");\n            } else if (error_code == CLUSTER_REDIR_DOWN_STATE) { \n                luaPushError(lua,\n                    \"Lua script attempted to execute a command while the \"\n                    \"cluster is down\");\n            } else {\n                luaPushError(lua,\n                    \"Lua script attempted to access a non local key in a \"\n                    \"cluster node\");\n            }\n\n            goto cleanup;\n        }\n    }\n\n    /* If we are using single commands replication, we need to wrap what\n     * we propagate into a MULTI/EXEC block, so that it will be atomic like\n     * a Lua script in the context of AOF and slaves. */\n    if (server.lua_replicate_commands &&\n        !server.lua_multi_emitted &&\n        !(server.lua_caller->flags & CLIENT_MULTI) &&\n        server.lua_write_dirty &&\n        server.lua_repl != PROPAGATE_NONE)\n    {\n        execCommandPropagateMulti(server.lua_caller);\n        server.lua_multi_emitted = 1;\n        /* Now we are in the MULTI context, the lua_client should be\n         * flag as CLIENT_MULTI. */\n        c->flags |= CLIENT_MULTI;\n    }\n\n    /* Run the command */\n    int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS;\n    if (server.lua_replicate_commands) {\n        /* Set flags according to redis.set_repl() settings. */\n        if (server.lua_repl & PROPAGATE_AOF)\n            call_flags |= CMD_CALL_PROPAGATE_AOF;\n        if (server.lua_repl & PROPAGATE_REPL)\n            call_flags |= CMD_CALL_PROPAGATE_REPL;\n    }\n    call(c,call_flags);\n\n    /* Convert the result of the Redis command into a suitable Lua type.\n     * The first thing we need is to create a single string from the client\n     * output buffers. */\n    if (listLength(c->reply) == 0 && c->bufpos < PROTO_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            clientReplyBlock *o = listNodeValue(listFirst(c->reply));\n\n            reply = sdscatlen(reply,o->buf,o->used);\n            listDelNode(c->reply,listFirst(c->reply));\n        }\n    }\n    if (raise_error && reply[0] != '-') raise_error = 0;\n    redisProtocolToLuaType(lua,reply);\n\n    /* If the debugger is active, log the reply from Redis. */\n    if (ldb.active && ldb.step)\n        ldbLogRedisReply(reply);\n\n    /* Sort the output array if needed, assuming it is a non-null multi bulk\n     * reply as expected. */\n    if ((cmd->flags & CMD_SORT_FOR_SCRIPT) &&\n        (server.lua_replicate_commands == 0) &&\n        (reply[0] == '*' && reply[1] != '-')) {\n            luaSortArray(lua);\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 == OBJ_ENCODING_RAW ||\n             o->encoding == OBJ_ENCODING_EMBSTR) &&\n            sdslen(o->ptr) <= LUA_CMD_OBJCACHE_MAX_LEN)\n        {\n            sds s = o->ptr;\n            if (cached_objects[j]) decrRefCount(cached_objects[j]);\n            cached_objects[j] = o;\n            cached_objects_len[j] = sdsalloc(s);\n        } else {\n            decrRefCount(o);\n        }\n    }\n\n    if (c->argv != argv) {\n        zfree(c->argv);\n        argv = NULL;\n        argv_size = 0;\n    }\n\n    c->user = NULL;\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        inuse--;\n        return luaRaiseError(lua);\n    }\n    inuse--;\n    return 1;\n}\n\n/* redis.call() */\nint luaRedisCallCommand(lua_State *lua) {\n    return luaRedisGenericCommand(lua,1);\n}\n\n/* redis.pcall() */\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        lua_pushstring(lua, \"wrong number of arguments\");\n        return lua_error(lua);\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\n/* redis.error_reply() */\nint luaRedisErrorReplyCommand(lua_State *lua) {\n    return luaRedisReturnSingleFieldTable(lua,\"err\");\n}\n\n/* redis.status_reply() */\nint luaRedisStatusReplyCommand(lua_State *lua) {\n    return luaRedisReturnSingleFieldTable(lua,\"ok\");\n}\n\n/* redis.replicate_commands()\n *\n * Turn on single commands replication if the script never called\n * a write command so far, and returns true. Otherwise if the script\n * already started to write, returns false and stick to whole scripts\n * replication, which is our default. */\nint luaRedisReplicateCommandsCommand(lua_State *lua) {\n    if (server.lua_write_dirty) {\n        lua_pushboolean(lua,0);\n    } else {\n        server.lua_replicate_commands = 1;\n        /* When we switch to single commands replication, we can provide\n         * different math.random() sequences at every call, which is what\n         * the user normally expects. */\n        redisSrand48(rand());\n        lua_pushboolean(lua,1);\n    }\n    return 1;\n}\n\n/* redis.breakpoint()\n *\n * Allows to stop execution during a debuggign session from within\n * the Lua code implementation, like if a breakpoint was set in the code\n * immediately after the function. */\nint luaRedisBreakpointCommand(lua_State *lua) {\n    if (ldb.active) {\n        ldb.luabp = 1;\n        lua_pushboolean(lua,1);\n    } else {\n        lua_pushboolean(lua,0);\n    }\n    return 1;\n}\n\n/* redis.debug()\n *\n * Log a string message into the output console.\n * Can take multiple arguments that will be separated by commas.\n * Nothing is returned to the caller. */\nint luaRedisDebugCommand(lua_State *lua) {\n    if (!ldb.active) return 0;\n    int argc = lua_gettop(lua);\n    sds log = sdscatprintf(sdsempty(),\"<debug> line %d: \", ldb.currentline);\n    while(argc--) {\n        log = ldbCatStackValue(log,lua,-1 - argc);\n        if (argc != 0) log = sdscatlen(log,\", \",2);\n    }\n    ldbLog(log);\n    return 0;\n}\n\n/* redis.set_repl()\n *\n * Set the propagation of write commands executed in the context of the\n * script to on/off for AOF and slaves. */\nint luaRedisSetReplCommand(lua_State *lua) {\n    int argc = lua_gettop(lua);\n    int flags;\n\n    if (server.lua_replicate_commands == 0) {\n        lua_pushstring(lua, \"You can set the replication behavior only after turning on single commands replication with redis.replicate_commands().\");\n        return lua_error(lua);\n    } else if (argc != 1) {\n        lua_pushstring(lua, \"redis.set_repl() requires two arguments.\");\n        return lua_error(lua);\n    }\n\n    flags = lua_tonumber(lua,-1);\n    if ((flags & ~(PROPAGATE_AOF|PROPAGATE_REPL)) != 0) {\n        lua_pushstring(lua, \"Invalid replication flags. Use REPL_AOF, REPL_REPLICA, REPL_ALL or REPL_NONE.\");\n        return lua_error(lua);\n    }\n    server.lua_repl = flags;\n    return 0;\n}\n\n/* redis.log() */\nint luaLogCommand(lua_State *lua) {\n    int j, argc = lua_gettop(lua);\n    int level;\n    sds log;\n\n    if (argc < 2) {\n        lua_pushstring(lua, \"redis.log() requires two arguments or more.\");\n        return lua_error(lua);\n    } else if (!lua_isnumber(lua,-argc)) {\n        lua_pushstring(lua, \"First argument must be a number (log level).\");\n        return lua_error(lua);\n    }\n    level = lua_tonumber(lua,-argc);\n    if (level < LL_DEBUG || level > LL_WARNING) {\n        lua_pushstring(lua, \"Invalid debug level.\");\n        return lua_error(lua);\n    }\n    if (level < server.verbosity) return 0;\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    serverLogRaw(level,log);\n    sdsfree(log);\n    return 0;\n}\n\n/* redis.setresp() */\nint luaSetResp(lua_State *lua) {\n    int argc = lua_gettop(lua);\n\n    if (argc != 1) {\n        lua_pushstring(lua, \"redis.setresp() requires one argument.\");\n        return lua_error(lua);\n    }\n\n    int resp = lua_tonumber(lua,-argc);\n    if (resp != 2 && resp != 3) {\n        lua_pushstring(lua, \"RESP version must be 2 or 3.\");\n        return lua_error(lua);\n    }\n\n    server.lua_client->resp = resp;\n    return 0;\n}\n\n/* ---------------------------------------------------------------------------\n * Lua engine initialization and reset.\n * ------------------------------------------------------------------------- */\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);\nLUALIB_API int (luaopen_bit) (lua_State *L);\n\nvoid luaLoadLibraries(lua_State *lua) {\n    luaLoadLib(lua, \"\", luaopen_base);\n    luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);\n    luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);\n    luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);\n    luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug);\n    luaLoadLib(lua, \"cjson\", luaopen_cjson);\n    luaLoadLib(lua, \"struct\", luaopen_struct);\n    luaLoadLib(lua, \"cmsgpack\", luaopen_cmsgpack);\n    luaLoadLib(lua, \"bit\", luaopen_bit);\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. */\nvoid luaRemoveUnsupportedFunctions(lua_State *lua) {\n    lua_pushnil(lua);\n    lua_setglobal(lua,\"loadfile\");\n    lua_pushnil(lua);\n    lua_setglobal(lua,\"dofile\");\n}\n\n/* This function installs metamethods in the global table _G that prevent\n * the creation of globals accidentally.\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. */\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 dbg=debug\\n\";\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 dbg.getinfo(2) then\\n\";\n    s[j++]=\"    local w = dbg.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 dbg.getinfo(2) and dbg.getinfo(2, \\\"S\\\").what ~= \\\"C\\\" then\\n\";\n    s[j++]=\"    error(\\\"Script attempted to access nonexistent 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++]=\"debug = nil\\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 * This function is called the first time at server startup with\n * the 'setup' argument set to 1.\n *\n * It can be called again multiple times during the lifetime of the Redis\n * process, with 'setup' set to 0, and following a scriptingRelease() call,\n * in order to reset the Lua scripting environment.\n *\n * However it is simpler to just call scriptingReset() that does just that. */\nvoid scriptingInit(int setup) {\n    lua_State *lua = lua_open();\n\n    if (setup) {\n        server.lua_client = NULL;\n        server.lua_caller = NULL;\n        server.lua_cur_script = NULL;\n        server.lua_timedout = 0;\n        ldbInit();\n    }\n\n    luaLoadLibraries(lua);\n    luaRemoveUnsupportedFunctions(lua);\n\n    /* Initialize a dictionary we use to map SHAs to scripts.\n     * This is useful for replication, as we need to replicate EVALSHA\n     * as EVAL, so we need to remember the associated script. */\n    server.lua_scripts = dictCreate(&shaScriptObjectDictType,NULL);\n    server.lua_scripts_mem = 0;\n\n    /* Register the redis commands table and fields */\n    lua_newtable(lua);\n\n    /* redis.call */\n    lua_pushstring(lua,\"call\");\n    lua_pushcfunction(lua,luaRedisCallCommand);\n    lua_settable(lua,-3);\n\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    lua_pushstring(lua,\"log\");\n    lua_pushcfunction(lua,luaLogCommand);\n    lua_settable(lua,-3);\n\n    /* redis.setresp */\n    lua_pushstring(lua,\"setresp\");\n    lua_pushcfunction(lua,luaSetResp);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_DEBUG\");\n    lua_pushnumber(lua,LL_DEBUG);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_VERBOSE\");\n    lua_pushnumber(lua,LL_VERBOSE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_NOTICE\");\n    lua_pushnumber(lua,LL_NOTICE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_WARNING\");\n    lua_pushnumber(lua,LL_WARNING);\n    lua_settable(lua,-3);\n\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    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    /* redis.replicate_commands */\n    lua_pushstring(lua, \"replicate_commands\");\n    lua_pushcfunction(lua, luaRedisReplicateCommandsCommand);\n    lua_settable(lua, -3);\n\n    /* redis.set_repl and associated flags. */\n    lua_pushstring(lua,\"set_repl\");\n    lua_pushcfunction(lua,luaRedisSetReplCommand);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_NONE\");\n    lua_pushnumber(lua,PROPAGATE_NONE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_AOF\");\n    lua_pushnumber(lua,PROPAGATE_AOF);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_SLAVE\");\n    lua_pushnumber(lua,PROPAGATE_REPL);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_REPLICA\");\n    lua_pushnumber(lua,PROPAGATE_REPL);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_ALL\");\n    lua_pushnumber(lua,PROPAGATE_AOF|PROPAGATE_REPL);\n    lua_settable(lua,-3);\n\n    /* redis.breakpoint */\n    lua_pushstring(lua,\"breakpoint\");\n    lua_pushcfunction(lua,luaRedisBreakpointCommand);\n    lua_settable(lua,-3);\n\n    /* redis.debug */\n    lua_pushstring(lua,\"debug\");\n    lua_pushcfunction(lua,luaRedisDebugCommand);\n    lua_settable(lua,-3);\n\n    /* Finally set the table as 'redis' global var. */\n    lua_setglobal(lua,\"redis\");\n\n    /* Replace math.random and math.randomseed with our implementations. */\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        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        char *errh_func =       \"local dbg = debug\\n\"\n                                \"function __redis__err__handler(err)\\n\"\n                                \"  local i = dbg.getinfo(2,'nSl')\\n\"\n                                \"  if i and i.what == 'C' then\\n\"\n                                \"    i = dbg.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     * Note: there is no need to create it again when this function is called\n     * by scriptingReset(). */\n    if (server.lua_client == NULL) {\n        server.lua_client = createClient(NULL);\n        server.lua_client->flags |= CLIENT_LUA;\n    }\n\n    /* Lua beginners often 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    scriptingEnableGlobalsProtection(lua);\n\n    server.lua = lua;\n}\n\n/* Release resources related to Lua scripting.\n * This function is used in order to reset the scripting environment. */\nvoid scriptingRelease(void) {\n    dictRelease(server.lua_scripts);\n    server.lua_scripts_mem = 0;\n    lua_close(server.lua);\n}\n\nvoid scriptingReset(void) {\n    scriptingRelease();\n    scriptingInit(0);\n}\n\n/* Set an array of Redis String Objects as a Lua array (table) stored into a\n * global variable. */\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/* ---------------------------------------------------------------------------\n * Redis provided math.random\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/* 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 * EVAL and SCRIPT commands implementation\n * ------------------------------------------------------------------------- */\n\n/* Define a Lua function with the specified body.\n * The function name will be generated in the following form:\n *\n *   f_<hex sha1 sum>\n *\n * The function increments the reference count of the 'body' object as a\n * side effect of a successful call.\n *\n * On success a pointer to an SDS string representing the function SHA1 of the\n * just added function is returned (and will be valid until the next call\n * to scriptingReset() function), otherwise NULL is returned.\n *\n * The function handles the fact of being called with a script that already\n * exists, and in such a case, it behaves like in the success case.\n *\n * If 'c' is not NULL, on error the client is informed with an appropriate\n * error describing the nature of the problem and the Lua interpreter error. */\nsds luaCreateFunction(client *c, lua_State *lua, robj *body) {\n    char funcname[43];\n    dictEntry *de;\n\n    funcname[0] = 'f';\n    funcname[1] = '_';\n    sha1hex(funcname+2,body->ptr,sdslen(body->ptr));\n\n    sds sha = sdsnewlen(funcname+2,40);\n    if ((de = dictFind(server.lua_scripts,sha)) != NULL) {\n        sdsfree(sha);\n        return dictGetKey(de);\n    }\n\n    sds funcdef = sdsempty();\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,\"\\nend\",4);\n\n    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),\"@user_script\")) {\n        if (c != NULL) {\n            addReplyErrorFormat(c,\n                \"Error compiling script (new function): %s\\n\",\n                lua_tostring(lua,-1));\n        }\n        lua_pop(lua,1);\n        sdsfree(sha);\n        sdsfree(funcdef);\n        return NULL;\n    }\n    sdsfree(funcdef);\n\n    if (lua_pcall(lua,0,0,0)) {\n        if (c != NULL) {\n            addReplyErrorFormat(c,\"Error running script (new function): %s\\n\",\n                lua_tostring(lua,-1));\n        }\n        lua_pop(lua,1);\n        sdsfree(sha);\n        return NULL;\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    int retval = dictAdd(server.lua_scripts,sha,body);\n    serverAssertWithInfo(c ? c : server.lua_client,NULL,retval == DICT_OK);\n    server.lua_scripts_mem += sdsZmallocSize(sha) + getStringObjectSdsUsedMemory(body);\n    incrRefCount(body);\n    return sha;\n}\n\n/* This is the Lua script \"count\" hook that we use to detect scripts timeout. */\nvoid luaMaskCountHook(lua_State *lua, lua_Debug *ar) {\n    long long elapsed = mstime() - server.lua_time_start;\n    UNUSED(ar);\n    UNUSED(lua);\n\n    /* Set the timeout condition if not already set and the maximum\n     * execution time was reached. */\n    if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {\n        serverLog(LL_WARNING,\n            \"Lua slow script detected: still in execution after %lld milliseconds. \"\n            \"You can try killing the script using the SCRIPT KILL command. \"\n            \"Script SHA1 is: %s\",\n            elapsed, server.lua_cur_script);\n        server.lua_timedout = 1;\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        protectClient(server.lua_caller);\n    }\n    if (server.lua_timedout) processEventsWhileBlocked();\n    if (server.lua_kill) {\n        serverLog(LL_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\nvoid prepareLuaClient(void) {\n    /* Select the right DB in the context of the Lua client */\n    selectDb(server.lua_client,server.lua_caller->db->id);\n    server.lua_client->resp = 2; /* Default is RESP2, scripts can change it. */\n\n    /* If we are in MULTI context, flag Lua client as CLIENT_MULTI. */\n    if (server.lua_caller->flags & CLIENT_MULTI) {\n        server.lua_client->flags |= CLIENT_MULTI;\n    }\n}\n\nvoid resetLuaClient(void) {\n    /* After the script done, remove the MULTI state. */\n    server.lua_client->flags &= ~CLIENT_MULTI;\n}\n\nvoid evalGenericCommand(client *c, int evalsha) {\n    lua_State *lua = server.lua;\n    char funcname[43];\n    long long numkeys;\n    long long initial_server_dirty = server.dirty;\n    int delhook = 0, err;\n\n    /* When we replicate whole scripts, we want the same PRNG sequence at\n     * every call so that our PRNG is not affected by external state. */\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     * Thanks to this flag we'll raise an error every time a write command\n     * is called after a random command was used. */\n    server.lua_random_dirty = 0;\n    server.lua_write_dirty = 0;\n    server.lua_replicate_commands = server.lua_always_replicate_commands;\n    server.lua_multi_emitted = 0;\n    server.lua_repl = PROPAGATE_AOF|PROPAGATE_REPL;\n\n    /* Get the number of arguments that are keys */\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != C_OK)\n        return;\n    if (numkeys > (c->argc - 3)) {\n        addReplyError(c,\"Number of keys can't be greater than number of args\");\n        return;\n    } else if (numkeys < 0) {\n        addReplyError(c,\"Number of keys can't be negative\");\n        return;\n    }\n\n    /* We obtain the script SHA1, then check if this function is already\n     * defined into the Lua state */\n    funcname[0] = 'f';\n    funcname[1] = '_';\n    if (!evalsha) {\n        /* Hash the code if this is an EVAL call */\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        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_getglobal(lua, funcname);\n    if (lua_isnil(lua,-1)) {\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        if (evalsha) {\n            lua_pop(lua,1); /* remove the error handler from the stack. */\n            addReply(c, shared.noscripterr);\n            return;\n        }\n        if (luaCreateFunction(c,lua,c->argv[1]) == NULL) {\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 NULL. */\n            return;\n        }\n        /* Now the following is guaranteed to return non nil */\n        lua_getglobal(lua, funcname);\n        serverAssert(!lua_isnil(lua,-1));\n    }\n\n    /* Populate the argv and keys table accordingly to the arguments that\n     * EVAL received. */\n    luaSetGlobalArray(lua,\"KEYS\",c->argv+3,numkeys);\n    luaSetGlobalArray(lua,\"ARGV\",c->argv+3+numkeys,c->argc-3-numkeys);\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     * We set the hook only if the time limit is enabled as the hook will\n     * make the Lua script execution slower.\n     *\n     * If we are debugging, we set instead a \"line\" hook so that the\n     * debugger is call-back at every line executed by the script. */\n    server.lua_caller = c;\n    server.lua_cur_script = funcname + 2;\n    server.lua_time_start = mstime();\n    server.lua_kill = 0;\n    if (server.lua_time_limit > 0 && ldb.active == 0) {\n        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);\n        delhook = 1;\n    } else if (ldb.active) {\n        lua_sethook(server.lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000);\n        delhook = 1;\n    }\n\n    prepareLuaClient();\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    err = lua_pcall(lua,0,1,-2);\n\n    resetLuaClient();\n\n    /* Perform some cleanup that we need to do both on error and success. */\n    if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */\n    if (server.lua_timedout) {\n        server.lua_timedout = 0;\n        /* Restore the client that was protected when the script timeout\n         * was detected. */\n        unprotectClient(c);\n        if (server.masterhost && server.master)\n            queueClientForReprocessing(server.master);\n    }\n    server.lua_caller = NULL;\n    server.lua_cur_script = NULL;\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    if (err) {\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        luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */\n        lua_pop(lua,1); /* Remove the error handler. */\n    }\n\n    /* If we are using single commands replication, emit EXEC if there\n     * was at least a write. */\n    if (server.lua_replicate_commands) {\n        preventCommandPropagation(c);\n        if (server.lua_multi_emitted) {\n            execCommandPropagateExec(c);\n        }\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     * 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     * 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    if (evalsha && !server.lua_replicate_commands) {\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            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);\n\n            replicationScriptCacheAdd(c->argv[1]->ptr);\n            serverAssertWithInfo(c,NULL,script != NULL);\n\n            /* If the script did not produce any changes in the dataset we want\n             * just to replicate it as SCRIPT LOAD, otherwise we risk running\n             * an aborted script on slaves (that may then produce results there)\n             * or just running a CPU costly read-only script on the slaves. */\n            if (server.dirty == initial_server_dirty) {\n                rewriteClientCommandVector(c,3,\n                    resetRefCount(createStringObject(\"SCRIPT\",6)),\n                    resetRefCount(createStringObject(\"LOAD\",4)),\n                    script);\n            } else {\n                rewriteClientCommandArgument(c,0,\n                    resetRefCount(createStringObject(\"EVAL\",4)));\n                rewriteClientCommandArgument(c,1,script);\n            }\n            forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF);\n        }\n    }\n}\n\nvoid evalCommand(client *c) {\n    if (!(c->flags & CLIENT_LUA_DEBUG))\n        evalGenericCommand(c,0);\n    else\n        evalGenericCommandWithDebugging(c,0);\n}\n\nvoid evalShaCommand(client *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    if (!(c->flags & CLIENT_LUA_DEBUG))\n        evalGenericCommand(c,1);\n    else {\n        addReplyError(c,\"Please use EVAL instead of EVALSHA for debugging\");\n        return;\n    }\n}\n\nvoid scriptCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"DEBUG (yes|sync|no) -- Set the debug mode for subsequent scripts executed.\",\n\"EXISTS <sha1> [<sha1> ...] -- Return information about the existence of the scripts in the script cache.\",\n\"FLUSH -- Flush the Lua scripts cache. Very dangerous on replicas.\",\n\"KILL -- Kill the currently executing Lua script.\",\n\"LOAD <script> -- Load a script into the scripts cache, without executing it.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else 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    } else if (c->argc >= 2 && !strcasecmp(c->argv[1]->ptr,\"exists\")) {\n        int j;\n\n        addReplyArrayLen(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    } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,\"load\")) {\n        sds sha = luaCreateFunction(c,server.lua,c->argv[2]);\n        if (sha == NULL) return; /* The error was sent by luaCreateFunction(). */\n        addReplyBulkCBuffer(c,sha,40);\n        forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF);\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_caller->flags & CLIENT_MASTER) {\n            addReplySds(c,sdsnew(\"-UNKILLABLE The busy script was sent by a master instance in the context of replication and cannot be killed.\\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    } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,\"debug\")) {\n        if (clientHasPendingReplies(c)) {\n            addReplyError(c,\"SCRIPT DEBUG must be called outside a pipeline\");\n            return;\n        }\n        if (!strcasecmp(c->argv[2]->ptr,\"no\")) {\n            ldbDisable(c);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"yes\")) {\n            ldbEnable(c);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"sync\")) {\n            ldbEnable(c);\n            addReply(c,shared.ok);\n            c->flags |= CLIENT_LUA_DEBUG_SYNC;\n        } else {\n            addReplyError(c,\"Use SCRIPT DEBUG yes/sync/no\");\n            return;\n        }\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\n/* ---------------------------------------------------------------------------\n * LDB: Redis Lua debugging facilities\n * ------------------------------------------------------------------------- */\n\n/* Initialize Lua debugger data structures. */\nvoid ldbInit(void) {\n    ldb.conn = NULL;\n    ldb.active = 0;\n    ldb.logs = listCreate();\n    listSetFreeMethod(ldb.logs,(void (*)(void*))sdsfree);\n    ldb.children = listCreate();\n    ldb.src = NULL;\n    ldb.lines = 0;\n    ldb.cbuf = sdsempty();\n}\n\n/* Remove all the pending messages in the specified list. */\nvoid ldbFlushLog(list *log) {\n    listNode *ln;\n\n    while((ln = listFirst(log)) != NULL)\n        listDelNode(log,ln);\n}\n\n/* Enable debug mode of Lua scripts for this client. */\nvoid ldbEnable(client *c) {\n    c->flags |= CLIENT_LUA_DEBUG;\n    ldbFlushLog(ldb.logs);\n    ldb.conn = c->conn;\n    ldb.step = 1;\n    ldb.bpcount = 0;\n    ldb.luabp = 0;\n    sdsfree(ldb.cbuf);\n    ldb.cbuf = sdsempty();\n    ldb.maxlen = LDB_MAX_LEN_DEFAULT;\n    ldb.maxlen_hint_sent = 0;\n}\n\n/* Exit debugging mode from the POV of client. This function is not enough\n * to properly shut down a client debugging session, see ldbEndSession()\n * for more information. */\nvoid ldbDisable(client *c) {\n    c->flags &= ~(CLIENT_LUA_DEBUG|CLIENT_LUA_DEBUG_SYNC);\n}\n\n/* Append a log entry to the specified LDB log. */\nvoid ldbLog(sds entry) {\n    listAddNodeTail(ldb.logs,entry);\n}\n\n/* A version of ldbLog() which prevents producing logs greater than\n * ldb.maxlen. The first time the limit is reached an hint is generated\n * to inform the user that reply trimming can be disabled using the\n * debugger \"maxlen\" command. */\nvoid ldbLogWithMaxLen(sds entry) {\n    int trimmed = 0;\n    if (ldb.maxlen && sdslen(entry) > ldb.maxlen) {\n        sdsrange(entry,0,ldb.maxlen-1);\n        entry = sdscatlen(entry,\" ...\",4);\n        trimmed = 1;\n    }\n    ldbLog(entry);\n    if (trimmed && ldb.maxlen_hint_sent == 0) {\n        ldb.maxlen_hint_sent = 1;\n        ldbLog(sdsnew(\n        \"<hint> The above reply was trimmed. Use 'maxlen 0' to disable trimming.\"));\n    }\n}\n\n/* Send ldb.logs to the debugging client as a multi-bulk reply\n * consisting of simple strings. Log entries which include newlines have them\n * replaced with spaces. The entries sent are also consumed. */\nvoid ldbSendLogs(void) {\n    sds proto = sdsempty();\n    proto = sdscatfmt(proto,\"*%i\\r\\n\", (int)listLength(ldb.logs));\n    while(listLength(ldb.logs)) {\n        listNode *ln = listFirst(ldb.logs);\n        proto = sdscatlen(proto,\"+\",1);\n        sdsmapchars(ln->value,\"\\r\\n\",\"  \",2);\n        proto = sdscatsds(proto,ln->value);\n        proto = sdscatlen(proto,\"\\r\\n\",2);\n        listDelNode(ldb.logs,ln);\n    }\n    if (connWrite(ldb.conn,proto,sdslen(proto)) == -1) {\n        /* Avoid warning. We don't check the return value of write()\n         * since the next read() will catch the I/O error and will\n         * close the debugging session. */\n    }\n    sdsfree(proto);\n}\n\n/* Start a debugging session before calling EVAL implementation.\n * The techique we use is to capture the client socket file descriptor,\n * in order to perform direct I/O with it from within Lua hooks. This\n * way we don't have to re-enter Redis in order to handle I/O.\n *\n * The function returns 1 if the caller should proceed to call EVAL,\n * and 0 if instead the caller should abort the operation (this happens\n * for the parent in a forked session, since it's up to the children\n * to continue, or when fork returned an error).\n *\n * The caller should call ldbEndSession() only if ldbStartSession()\n * returned 1. */\nint ldbStartSession(client *c) {\n    ldb.forked = (c->flags & CLIENT_LUA_DEBUG_SYNC) == 0;\n    if (ldb.forked) {\n        pid_t cp = redisFork();\n        if (cp == -1) {\n            addReplyError(c,\"Fork() failed: can't run EVAL in debugging mode.\");\n            return 0;\n        } else if (cp == 0) {\n            /* Child. Let's ignore important signals handled by the parent. */\n            struct sigaction act;\n            sigemptyset(&act.sa_mask);\n            act.sa_flags = 0;\n            act.sa_handler = SIG_IGN;\n            sigaction(SIGTERM, &act, NULL);\n            sigaction(SIGINT, &act, NULL);\n\n            /* Log the creation of the child and close the listening\n             * socket to make sure if the parent crashes a reset is sent\n             * to the clients. */\n            serverLog(LL_WARNING,\"Redis forked for debugging eval\");\n        } else {\n            /* Parent */\n            listAddNodeTail(ldb.children,(void*)(unsigned long)cp);\n            freeClientAsync(c); /* Close the client in the parent side. */\n            return 0;\n        }\n    } else {\n        serverLog(LL_WARNING,\n            \"Redis synchronous debugging eval session started\");\n    }\n\n    /* Setup our debugging session. */\n    connBlock(ldb.conn);\n    connSendTimeout(ldb.conn,5000);\n    ldb.active = 1;\n\n    /* First argument of EVAL is the script itself. We split it into different\n     * lines since this is the way the debugger accesses the source code. */\n    sds srcstring = sdsdup(c->argv[1]->ptr);\n    size_t srclen = sdslen(srcstring);\n    while(srclen && (srcstring[srclen-1] == '\\n' ||\n                     srcstring[srclen-1] == '\\r'))\n    {\n        srcstring[--srclen] = '\\0';\n    }\n    sdssetlen(srcstring,srclen);\n    ldb.src = sdssplitlen(srcstring,sdslen(srcstring),\"\\n\",1,&ldb.lines);\n    sdsfree(srcstring);\n    return 1;\n}\n\n/* End a debugging session after the EVAL call with debugging enabled\n * returned. */\nvoid ldbEndSession(client *c) {\n    /* Emit the remaining logs and an <endsession> mark. */\n    ldbLog(sdsnew(\"<endsession>\"));\n    ldbSendLogs();\n\n    /* If it's a fork()ed session, we just exit. */\n    if (ldb.forked) {\n        writeToClient(c,0);\n        serverLog(LL_WARNING,\"Lua debugging session child exiting\");\n        exitFromChild(0);\n    } else {\n        serverLog(LL_WARNING,\n            \"Redis synchronous debugging eval session ended\");\n    }\n\n    /* Otherwise let's restore client's state. */\n    connNonBlock(ldb.conn);\n    connSendTimeout(ldb.conn,0);\n\n    /* Close the client connectin after sending the final EVAL reply\n     * in order to signal the end of the debugging session. */\n    c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n\n    /* Cleanup. */\n    sdsfreesplitres(ldb.src,ldb.lines);\n    ldb.lines = 0;\n    ldb.active = 0;\n}\n\n/* If the specified pid is among the list of children spawned for\n * forked debugging sessions, it is removed from the children list.\n * If the pid was found non-zero is returned. */\nint ldbRemoveChild(pid_t pid) {\n    listNode *ln = listSearchKey(ldb.children,(void*)(unsigned long)pid);\n    if (ln) {\n        listDelNode(ldb.children,ln);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return the number of children we still did not receive termination\n * acknowledge via wait() in the parent process. */\nint ldbPendingChildren(void) {\n    return listLength(ldb.children);\n}\n\n/* Kill all the forked sessions. */\nvoid ldbKillForkedSessions(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(ldb.children,&li);\n    while((ln = listNext(&li))) {\n        pid_t pid = (unsigned long) ln->value;\n        serverLog(LL_WARNING,\"Killing debugging session %ld\",(long)pid);\n        kill(pid,SIGKILL);\n    }\n    listRelease(ldb.children);\n    ldb.children = listCreate();\n}\n\n/* Wrapper for EVAL / EVALSHA that enables debugging, and makes sure\n * that when EVAL returns, whatever happened, the session is ended. */\nvoid evalGenericCommandWithDebugging(client *c, int evalsha) {\n    if (ldbStartSession(c)) {\n        evalGenericCommand(c,evalsha);\n        ldbEndSession(c);\n    } else {\n        ldbDisable(c);\n    }\n}\n\n/* Return a pointer to ldb.src source code line, considering line to be\n * one-based, and returning a special string for out of range lines. */\nchar *ldbGetSourceLine(int line) {\n    int idx = line-1;\n    if (idx < 0 || idx >= ldb.lines) return \"<out of range source code line>\";\n    return ldb.src[idx];\n}\n\n/* Return true if there is a breakpoint in the specified line. */\nint ldbIsBreakpoint(int line) {\n    int j;\n\n    for (j = 0; j < ldb.bpcount; j++)\n        if (ldb.bp[j] == line) return 1;\n    return 0;\n}\n\n/* Add the specified breakpoint. Ignore it if we already reached the max.\n * Returns 1 if the breakpoint was added (or was already set). 0 if there is\n * no space for the breakpoint or if the line is invalid. */\nint ldbAddBreakpoint(int line) {\n    if (line <= 0 || line > ldb.lines) return 0;\n    if (!ldbIsBreakpoint(line) && ldb.bpcount != LDB_BREAKPOINTS_MAX) {\n        ldb.bp[ldb.bpcount++] = line;\n        return 1;\n    }\n    return 0;\n}\n\n/* Remove the specified breakpoint, returning 1 if the operation was\n * performed or 0 if there was no such breakpoint. */\nint ldbDelBreakpoint(int line) {\n    int j;\n\n    for (j = 0; j < ldb.bpcount; j++) {\n        if (ldb.bp[j] == line) {\n            ldb.bpcount--;\n            memmove(ldb.bp+j,ldb.bp+j+1,ldb.bpcount-j);\n            return 1;\n        }\n    }\n    return 0;\n}\n\n/* Expect a valid multi-bulk command in the debugging client query buffer.\n * On success the command is parsed and returned as an array of SDS strings,\n * otherwise NULL is returned and there is to read more buffer. */\nsds *ldbReplParseCommand(int *argcp) {\n    sds *argv = NULL;\n    int argc = 0;\n    if (sdslen(ldb.cbuf) == 0) return NULL;\n\n    /* Working on a copy is simpler in this case. We can modify it freely\n     * for the sake of simpler parsing. */\n    sds copy = sdsdup(ldb.cbuf);\n    char *p = copy;\n\n    /* This Redis protocol parser is a joke... just the simplest thing that\n     * works in this context. It is also very forgiving regarding broken\n     * protocol. */\n\n    /* Seek and parse *<count>\\r\\n. */\n    p = strchr(p,'*'); if (!p) goto protoerr;\n    char *plen = p+1; /* Multi bulk len pointer. */\n    p = strstr(p,\"\\r\\n\"); if (!p) goto protoerr;\n    *p = '\\0'; p += 2;\n    *argcp = atoi(plen);\n    if (*argcp <= 0 || *argcp > 1024) goto protoerr;\n\n    /* Parse each argument. */\n    argv = zmalloc(sizeof(sds)*(*argcp));\n    argc = 0;\n    while(argc < *argcp) {\n        if (*p != '$') goto protoerr;\n        plen = p+1; /* Bulk string len pointer. */\n        p = strstr(p,\"\\r\\n\"); if (!p) goto protoerr;\n        *p = '\\0'; p += 2;\n        int slen = atoi(plen); /* Length of this arg. */\n        if (slen <= 0 || slen > 1024) goto protoerr;\n        argv[argc++] = sdsnewlen(p,slen);\n        p += slen; /* Skip the already parsed argument. */\n        if (p[0] != '\\r' || p[1] != '\\n') goto protoerr;\n        p += 2; /* Skip \\r\\n. */\n    }\n    sdsfree(copy);\n    return argv;\n\nprotoerr:\n    sdsfreesplitres(argv,argc);\n    sdsfree(copy);\n    return NULL;\n}\n\n/* Log the specified line in the Lua debugger output. */\nvoid ldbLogSourceLine(int lnum) {\n    char *line = ldbGetSourceLine(lnum);\n    char *prefix;\n    int bp = ldbIsBreakpoint(lnum);\n    int current = ldb.currentline == lnum;\n\n    if (current && bp)\n        prefix = \"->#\";\n    else if (current)\n        prefix = \"-> \";\n    else if (bp)\n        prefix = \"  #\";\n    else\n        prefix = \"   \";\n    sds thisline = sdscatprintf(sdsempty(),\"%s%-3d %s\", prefix, lnum, line);\n    ldbLog(thisline);\n}\n\n/* Implement the \"list\" command of the Lua debugger. If around is 0\n * the whole file is listed, otherwise only a small portion of the file\n * around the specified line is shown. When a line number is specified\n * the amonut of context (lines before/after) is specified via the\n * 'context' argument. */\nvoid ldbList(int around, int context) {\n    int j;\n\n    for (j = 1; j <= ldb.lines; j++) {\n        if (around != 0 && abs(around-j) > context) continue;\n        ldbLogSourceLine(j);\n    }\n}\n\n/* Append an human readable representation of the Lua value at position 'idx'\n * on the stack of the 'lua' state, to the SDS string passed as argument.\n * The new SDS string with the represented value attached is returned.\n * Used in order to implement ldbLogStackValue().\n *\n * The element is not automatically removed from the stack, nor it is\n * converted to a different type. */\n#define LDB_MAX_VALUES_DEPTH (LUA_MINSTACK/2)\nsds ldbCatStackValueRec(sds s, lua_State *lua, int idx, int level) {\n    int t = lua_type(lua,idx);\n\n    if (level++ == LDB_MAX_VALUES_DEPTH)\n        return sdscat(s,\"<max recursion level reached! Nested table?>\");\n\n    switch(t) {\n    case LUA_TSTRING:\n        {\n        size_t strl;\n        char *strp = (char*)lua_tolstring(lua,idx,&strl);\n        s = sdscatrepr(s,strp,strl);\n        }\n        break;\n    case LUA_TBOOLEAN:\n        s = sdscat(s,lua_toboolean(lua,idx) ? \"true\" : \"false\");\n        break;\n    case LUA_TNUMBER:\n        s = sdscatprintf(s,\"%g\",(double)lua_tonumber(lua,idx));\n        break;\n    case LUA_TNIL:\n        s = sdscatlen(s,\"nil\",3);\n        break;\n    case LUA_TTABLE:\n        {\n        int expected_index = 1; /* First index we expect in an array. */\n        int is_array = 1; /* Will be set to null if check fails. */\n        /* Note: we create two representations at the same time, one\n         * assuming the table is an array, one assuming it is not. At the\n         * end we know what is true and select the right one. */\n        sds repr1 = sdsempty();\n        sds repr2 = sdsempty();\n        lua_pushnil(lua); /* The first key to start the iteration is nil. */\n        while (lua_next(lua,idx-1)) {\n            /* Test if so far the table looks like an array. */\n            if (is_array &&\n                (lua_type(lua,-2) != LUA_TNUMBER ||\n                 lua_tonumber(lua,-2) != expected_index)) is_array = 0;\n            /* Stack now: table, key, value */\n            /* Array repr. */\n            repr1 = ldbCatStackValueRec(repr1,lua,-1,level);\n            repr1 = sdscatlen(repr1,\"; \",2);\n            /* Full repr. */\n            repr2 = sdscatlen(repr2,\"[\",1);\n            repr2 = ldbCatStackValueRec(repr2,lua,-2,level);\n            repr2 = sdscatlen(repr2,\"]=\",2);\n            repr2 = ldbCatStackValueRec(repr2,lua,-1,level);\n            repr2 = sdscatlen(repr2,\"; \",2);\n            lua_pop(lua,1); /* Stack: table, key. Ready for next iteration. */\n            expected_index++;\n        }\n        /* Strip the last \" ;\" from both the representations. */\n        if (sdslen(repr1)) sdsrange(repr1,0,-3);\n        if (sdslen(repr2)) sdsrange(repr2,0,-3);\n        /* Select the right one and discard the other. */\n        s = sdscatlen(s,\"{\",1);\n        s = sdscatsds(s,is_array ? repr1 : repr2);\n        s = sdscatlen(s,\"}\",1);\n        sdsfree(repr1);\n        sdsfree(repr2);\n        }\n        break;\n    case LUA_TFUNCTION:\n    case LUA_TUSERDATA:\n    case LUA_TTHREAD:\n    case LUA_TLIGHTUSERDATA:\n        {\n        const void *p = lua_topointer(lua,idx);\n        char *typename = \"unknown\";\n        if (t == LUA_TFUNCTION) typename = \"function\";\n        else if (t == LUA_TUSERDATA) typename = \"userdata\";\n        else if (t == LUA_TTHREAD) typename = \"thread\";\n        else if (t == LUA_TLIGHTUSERDATA) typename = \"light-userdata\";\n        s = sdscatprintf(s,\"\\\"%s@%p\\\"\",typename,p);\n        }\n        break;\n    default:\n        s = sdscat(s,\"\\\"<unknown-lua-type>\\\"\");\n        break;\n    }\n    return s;\n}\n\n/* Higher level wrapper for ldbCatStackValueRec() that just uses an initial\n * recursion level of '0'. */\nsds ldbCatStackValue(sds s, lua_State *lua, int idx) {\n    return ldbCatStackValueRec(s,lua,idx,0);\n}\n\n/* Produce a debugger log entry representing the value of the Lua object\n * currently on the top of the stack. The element is ot popped nor modified.\n * Check ldbCatStackValue() for the actual implementation. */\nvoid ldbLogStackValue(lua_State *lua, char *prefix) {\n    sds s = sdsnew(prefix);\n    s = ldbCatStackValue(s,lua,-1);\n    ldbLogWithMaxLen(s);\n}\n\nchar *ldbRedisProtocolToHuman_Int(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Bulk(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Status(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_MultiBulk(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Set(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Map(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Null(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Bool(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Double(sds *o, char *reply);\n\n/* Get Redis protocol from 'reply' and appends it in human readable form to\n * the passed SDS string 'o'.\n *\n * Note that the SDS string is passed by reference (pointer of pointer to\n * char*) so that we can return a modified pointer, as for SDS semantics. */\nchar *ldbRedisProtocolToHuman(sds *o, char *reply) {\n    char *p = reply;\n    switch(*p) {\n    case ':': p = ldbRedisProtocolToHuman_Int(o,reply); break;\n    case '$': p = ldbRedisProtocolToHuman_Bulk(o,reply); break;\n    case '+': p = ldbRedisProtocolToHuman_Status(o,reply); break;\n    case '-': p = ldbRedisProtocolToHuman_Status(o,reply); break;\n    case '*': p = ldbRedisProtocolToHuman_MultiBulk(o,reply); break;\n    case '~': p = ldbRedisProtocolToHuman_Set(o,reply); break;\n    case '%': p = ldbRedisProtocolToHuman_Map(o,reply); break;\n    case '_': p = ldbRedisProtocolToHuman_Null(o,reply); break;\n    case '#': p = ldbRedisProtocolToHuman_Bool(o,reply); break;\n    case ',': p = ldbRedisProtocolToHuman_Double(o,reply); break;\n    }\n    return p;\n}\n\n/* The following functions are helpers for ldbRedisProtocolToHuman(), each\n * take care of a given Redis return type. */\n\nchar *ldbRedisProtocolToHuman_Int(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    *o = sdscatlen(*o,reply+1,p-reply-1);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_Bulk(sds *o, 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        *o = sdscatlen(*o,\"NULL\",4);\n        return p+2;\n    } else {\n        *o = sdscatrepr(*o,p+2,bulklen);\n        return p+2+bulklen+2;\n    }\n}\n\nchar *ldbRedisProtocolToHuman_Status(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n\n    *o = sdscatrepr(*o,reply,p-reply);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_MultiBulk(sds *o, 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        *o = sdscatlen(*o,\"NULL\",4);\n        return p;\n    }\n    *o = sdscatlen(*o,\"[\",1);\n    for (j = 0; j < mbulklen; j++) {\n        p = ldbRedisProtocolToHuman(o,p);\n        if (j != mbulklen-1) *o = sdscatlen(*o,\",\",1);\n    }\n    *o = sdscatlen(*o,\"]\",1);\n    return p;\n}\n\nchar *ldbRedisProtocolToHuman_Set(sds *o, 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    *o = sdscatlen(*o,\"~(\",2);\n    for (j = 0; j < mbulklen; j++) {\n        p = ldbRedisProtocolToHuman(o,p);\n        if (j != mbulklen-1) *o = sdscatlen(*o,\",\",1);\n    }\n    *o = sdscatlen(*o,\")\",1);\n    return p;\n}\n\nchar *ldbRedisProtocolToHuman_Map(sds *o, 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    *o = sdscatlen(*o,\"{\",1);\n    for (j = 0; j < mbulklen; j++) {\n        p = ldbRedisProtocolToHuman(o,p);\n        *o = sdscatlen(*o,\" => \",4);\n        p = ldbRedisProtocolToHuman(o,p);\n        if (j != mbulklen-1) *o = sdscatlen(*o,\",\",1);\n    }\n    *o = sdscatlen(*o,\"}\",1);\n    return p;\n}\n\nchar *ldbRedisProtocolToHuman_Null(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    *o = sdscatlen(*o,\"(null)\",6);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_Bool(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    if (reply[1] == 't')\n        *o = sdscatlen(*o,\"#true\",5);\n    else\n        *o = sdscatlen(*o,\"#false\",6);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_Double(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    *o = sdscatlen(*o,\"(double) \",9);\n    *o = sdscatlen(*o,reply+1,p-reply-1);\n    return p+2;\n}\n\n/* Log a Redis reply as debugger output, in an human readable format.\n * If the resulting string is longer than 'len' plus a few more chars\n * used as prefix, it gets truncated. */\nvoid ldbLogRedisReply(char *reply) {\n    sds log = sdsnew(\"<reply> \");\n    ldbRedisProtocolToHuman(&log,reply);\n    ldbLogWithMaxLen(log);\n}\n\n/* Implements the \"print <var>\" command of the Lua debugger. It scans for Lua\n * var \"varname\" starting from the current stack frame up to the top stack\n * frame. The first matching variable is printed. */\nvoid ldbPrint(lua_State *lua, char *varname) {\n    lua_Debug ar;\n\n    int l = 0; /* Stack level. */\n    while (lua_getstack(lua,l,&ar) != 0) {\n        l++;\n        const char *name;\n        int i = 1; /* Variable index. */\n        while((name = lua_getlocal(lua,&ar,i)) != NULL) {\n            i++;\n            if (strcmp(varname,name) == 0) {\n                ldbLogStackValue(lua,\"<value> \");\n                lua_pop(lua,1);\n                return;\n            } else {\n                lua_pop(lua,1); /* Discard the var name on the stack. */\n            }\n        }\n    }\n\n    /* Let's try with global vars in two selected cases */\n    if (!strcmp(varname,\"ARGV\") || !strcmp(varname,\"KEYS\")) {\n        lua_getglobal(lua, varname);\n        ldbLogStackValue(lua,\"<value> \");\n        lua_pop(lua,1);\n    } else {\n        ldbLog(sdsnew(\"No such variable.\"));\n    }\n}\n\n/* Implements the \"print\" command (without arguments) of the Lua debugger.\n * Prints all the variables in the current stack frame. */\nvoid ldbPrintAll(lua_State *lua) {\n    lua_Debug ar;\n    int vars = 0;\n\n    if (lua_getstack(lua,0,&ar) != 0) {\n        const char *name;\n        int i = 1; /* Variable index. */\n        while((name = lua_getlocal(lua,&ar,i)) != NULL) {\n            i++;\n            if (!strstr(name,\"(*temporary)\")) {\n                sds prefix = sdscatprintf(sdsempty(),\"<value> %s = \",name);\n                ldbLogStackValue(lua,prefix);\n                sdsfree(prefix);\n                vars++;\n            }\n            lua_pop(lua,1);\n        }\n    }\n\n    if (vars == 0) {\n        ldbLog(sdsnew(\"No local variables in the current context.\"));\n    }\n}\n\n/* Implements the break command to list, add and remove breakpoints. */\nvoid ldbBreak(sds *argv, int argc) {\n    if (argc == 1) {\n        if (ldb.bpcount == 0) {\n            ldbLog(sdsnew(\"No breakpoints set. Use 'b <line>' to add one.\"));\n            return;\n        } else {\n            ldbLog(sdscatfmt(sdsempty(),\"%i breakpoints set:\",ldb.bpcount));\n            int j;\n            for (j = 0; j < ldb.bpcount; j++)\n                ldbLogSourceLine(ldb.bp[j]);\n        }\n    } else {\n        int j;\n        for (j = 1; j < argc; j++) {\n            char *arg = argv[j];\n            long line;\n            if (!string2l(arg,sdslen(arg),&line)) {\n                ldbLog(sdscatfmt(sdsempty(),\"Invalid argument:'%s'\",arg));\n            } else {\n                if (line == 0) {\n                    ldb.bpcount = 0;\n                    ldbLog(sdsnew(\"All breakpoints removed.\"));\n                } else if (line > 0) {\n                    if (ldb.bpcount == LDB_BREAKPOINTS_MAX) {\n                        ldbLog(sdsnew(\"Too many breakpoints set.\"));\n                    } else if (ldbAddBreakpoint(line)) {\n                        ldbList(line,1);\n                    } else {\n                        ldbLog(sdsnew(\"Wrong line number.\"));\n                    }\n                } else if (line < 0) {\n                    if (ldbDelBreakpoint(-line))\n                        ldbLog(sdsnew(\"Breakpoint removed.\"));\n                    else\n                        ldbLog(sdsnew(\"No breakpoint in the specified line.\"));\n                }\n            }\n        }\n    }\n}\n\n/* Implements the Lua debugger \"eval\" command. It just compiles the user\n * passed fragment of code and executes it, showing the result left on\n * the stack. */\nvoid ldbEval(lua_State *lua, sds *argv, int argc) {\n    /* Glue the script together if it is composed of multiple arguments. */\n    sds code = sdsjoinsds(argv+1,argc-1,\" \",1);\n    sds expr = sdscatsds(sdsnew(\"return \"),code);\n\n    /* Try to compile it as an expression, prepending \"return \". */\n    if (luaL_loadbuffer(lua,expr,sdslen(expr),\"@ldb_eval\")) {\n        lua_pop(lua,1);\n        /* Failed? Try as a statement. */\n        if (luaL_loadbuffer(lua,code,sdslen(code),\"@ldb_eval\")) {\n            ldbLog(sdscatfmt(sdsempty(),\"<error> %s\",lua_tostring(lua,-1)));\n            lua_pop(lua,1);\n            sdsfree(code);\n            sdsfree(expr);\n            return;\n        }\n    }\n\n    /* Call it. */\n    sdsfree(code);\n    sdsfree(expr);\n    if (lua_pcall(lua,0,1,0)) {\n        ldbLog(sdscatfmt(sdsempty(),\"<error> %s\",lua_tostring(lua,-1)));\n        lua_pop(lua,1);\n        return;\n    }\n    ldbLogStackValue(lua,\"<retval> \");\n    lua_pop(lua,1);\n}\n\n/* Implement the debugger \"redis\" command. We use a trick in order to make\n * the implementation very simple: we just call the Lua redis.call() command\n * implementation, with ldb.step enabled, so as a side effect the Redis command\n * and its reply are logged. */\nvoid ldbRedis(lua_State *lua, sds *argv, int argc) {\n    int j, saved_rc = server.lua_replicate_commands;\n\n    lua_getglobal(lua,\"redis\");\n    lua_pushstring(lua,\"call\");\n    lua_gettable(lua,-2);       /* Stack: redis, redis.call */\n    for (j = 1; j < argc; j++)\n        lua_pushlstring(lua,argv[j],sdslen(argv[j]));\n    ldb.step = 1;               /* Force redis.call() to log. */\n    server.lua_replicate_commands = 1;\n    lua_pcall(lua,argc-1,1,0);  /* Stack: redis, result */\n    ldb.step = 0;               /* Disable logging. */\n    server.lua_replicate_commands = saved_rc;\n    lua_pop(lua,2);             /* Discard the result and clean the stack. */\n}\n\n/* Implements \"trace\" command of the Lua debugger. It just prints a backtrace\n * querying Lua starting from the current callframe back to the outer one. */\nvoid ldbTrace(lua_State *lua) {\n    lua_Debug ar;\n    int level = 0;\n\n    while(lua_getstack(lua,level,&ar)) {\n        lua_getinfo(lua,\"Snl\",&ar);\n        if(strstr(ar.short_src,\"user_script\") != NULL) {\n            ldbLog(sdscatprintf(sdsempty(),\"%s %s:\",\n                (level == 0) ? \"In\" : \"From\",\n                ar.name ? ar.name : \"top level\"));\n            ldbLogSourceLine(ar.currentline);\n        }\n        level++;\n    }\n    if (level == 0) {\n        ldbLog(sdsnew(\"<error> Can't retrieve Lua stack.\"));\n    }\n}\n\n/* Impleemnts the debugger \"maxlen\" command. It just queries or sets the\n * ldb.maxlen variable. */\nvoid ldbMaxlen(sds *argv, int argc) {\n    if (argc == 2) {\n        int newval = atoi(argv[1]);\n        ldb.maxlen_hint_sent = 1; /* User knows about this command. */\n        if (newval != 0 && newval <= 60) newval = 60;\n        ldb.maxlen = newval;\n    }\n    if (ldb.maxlen) {\n        ldbLog(sdscatprintf(sdsempty(),\"<value> replies are truncated at %d bytes.\",(int)ldb.maxlen));\n    } else {\n        ldbLog(sdscatprintf(sdsempty(),\"<value> replies are unlimited.\"));\n    }\n}\n\n/* Read debugging commands from client.\n * Return C_OK if the debugging session is continuing, otherwise\n * C_ERR if the client closed the connection or is timing out. */\nint ldbRepl(lua_State *lua) {\n    sds *argv;\n    int argc;\n\n    /* We continue processing commands until a command that should return\n     * to the Lua interpreter is found. */\n    while(1) {\n        while((argv = ldbReplParseCommand(&argc)) == NULL) {\n            char buf[1024];\n            int nread = connRead(ldb.conn,buf,sizeof(buf));\n            if (nread <= 0) {\n                /* Make sure the script runs without user input since the\n                 * client is no longer connected. */\n                ldb.step = 0;\n                ldb.bpcount = 0;\n                return C_ERR;\n            }\n            ldb.cbuf = sdscatlen(ldb.cbuf,buf,nread);\n        }\n\n        /* Flush the old buffer. */\n        sdsfree(ldb.cbuf);\n        ldb.cbuf = sdsempty();\n\n        /* Execute the command. */\n        if (!strcasecmp(argv[0],\"h\") || !strcasecmp(argv[0],\"help\")) {\nldbLog(sdsnew(\"Redis Lua debugger help:\"));\nldbLog(sdsnew(\"[h]elp               Show this help.\"));\nldbLog(sdsnew(\"[s]tep               Run current line and stop again.\"));\nldbLog(sdsnew(\"[n]ext               Alias for step.\"));\nldbLog(sdsnew(\"[c]continue          Run till next breakpoint.\"));\nldbLog(sdsnew(\"[l]list              List source code around current line.\"));\nldbLog(sdsnew(\"[l]list [line]       List source code around [line].\"));\nldbLog(sdsnew(\"                     line = 0 means: current position.\"));\nldbLog(sdsnew(\"[l]list [line] [ctx] In this form [ctx] specifies how many lines\"));\nldbLog(sdsnew(\"                     to show before/after [line].\"));\nldbLog(sdsnew(\"[w]hole              List all source code. Alias for 'list 1 1000000'.\"));\nldbLog(sdsnew(\"[p]rint              Show all the local variables.\"));\nldbLog(sdsnew(\"[p]rint <var>        Show the value of the specified variable.\"));\nldbLog(sdsnew(\"                     Can also show global vars KEYS and ARGV.\"));\nldbLog(sdsnew(\"[b]reak              Show all breakpoints.\"));\nldbLog(sdsnew(\"[b]reak <line>       Add a breakpoint to the specified line.\"));\nldbLog(sdsnew(\"[b]reak -<line>      Remove breakpoint from the specified line.\"));\nldbLog(sdsnew(\"[b]reak 0            Remove all breakpoints.\"));\nldbLog(sdsnew(\"[t]race              Show a backtrace.\"));\nldbLog(sdsnew(\"[e]eval <code>       Execute some Lua code (in a different callframe).\"));\nldbLog(sdsnew(\"[r]edis <cmd>        Execute a Redis command.\"));\nldbLog(sdsnew(\"[m]axlen [len]       Trim logged Redis replies and Lua var dumps to len.\"));\nldbLog(sdsnew(\"                     Specifying zero as <len> means unlimited.\"));\nldbLog(sdsnew(\"[a]bort              Stop the execution of the script. In sync\"));\nldbLog(sdsnew(\"                     mode dataset changes will be retained.\"));\nldbLog(sdsnew(\"\"));\nldbLog(sdsnew(\"Debugger functions you can call from Lua scripts:\"));\nldbLog(sdsnew(\"redis.debug()        Produce logs in the debugger console.\"));\nldbLog(sdsnew(\"redis.breakpoint()   Stop execution like if there was a breakpoing.\"));\nldbLog(sdsnew(\"                     in the next line of code.\"));\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"s\") || !strcasecmp(argv[0],\"step\") ||\n                   !strcasecmp(argv[0],\"n\") || !strcasecmp(argv[0],\"next\")) {\n            ldb.step = 1;\n            break;\n        } else if (!strcasecmp(argv[0],\"c\") || !strcasecmp(argv[0],\"continue\")){\n            break;\n        } else if (!strcasecmp(argv[0],\"t\") || !strcasecmp(argv[0],\"trace\")) {\n            ldbTrace(lua);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"m\") || !strcasecmp(argv[0],\"maxlen\")) {\n            ldbMaxlen(argv,argc);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"b\") || !strcasecmp(argv[0],\"break\")) {\n            ldbBreak(argv,argc);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"e\") || !strcasecmp(argv[0],\"eval\")) {\n            ldbEval(lua,argv,argc);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"a\") || !strcasecmp(argv[0],\"abort\")) {\n            lua_pushstring(lua, \"script aborted for user request\");\n            lua_error(lua);\n        } else if (argc > 1 &&\n                   (!strcasecmp(argv[0],\"r\") || !strcasecmp(argv[0],\"redis\"))) {\n            ldbRedis(lua,argv,argc);\n            ldbSendLogs();\n        } else if ((!strcasecmp(argv[0],\"p\") || !strcasecmp(argv[0],\"print\"))) {\n            if (argc == 2)\n                ldbPrint(lua,argv[1]);\n            else\n                ldbPrintAll(lua);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"l\") || !strcasecmp(argv[0],\"list\")){\n            int around = ldb.currentline, ctx = 5;\n            if (argc > 1) {\n                int num = atoi(argv[1]);\n                if (num > 0) around = num;\n            }\n            if (argc > 2) ctx = atoi(argv[2]);\n            ldbList(around,ctx);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"w\") || !strcasecmp(argv[0],\"whole\")){\n            ldbList(1,1000000);\n            ldbSendLogs();\n        } else {\n            ldbLog(sdsnew(\"<error> Unknown Redis Lua debugger command or \"\n                          \"wrong number of arguments.\"));\n            ldbSendLogs();\n        }\n\n        /* Free the command vector. */\n        sdsfreesplitres(argv,argc);\n    }\n\n    /* Free the current command argv if we break inside the while loop. */\n    sdsfreesplitres(argv,argc);\n    return C_OK;\n}\n\n/* This is the core of our Lua debugger, called each time Lua is about\n * to start executing a new line. */\nvoid luaLdbLineHook(lua_State *lua, lua_Debug *ar) {\n    lua_getstack(lua,0,ar);\n    lua_getinfo(lua,\"Sl\",ar);\n    ldb.currentline = ar->currentline;\n\n    int bp = ldbIsBreakpoint(ldb.currentline) || ldb.luabp;\n    int timeout = 0;\n\n    /* Events outside our script are not interesting. */\n    if(strstr(ar->short_src,\"user_script\") == NULL) return;\n\n    /* Check if a timeout occurred. */\n    if (ar->event == LUA_HOOKCOUNT && ldb.step == 0 && bp == 0) {\n        mstime_t elapsed = mstime() - server.lua_time_start;\n        mstime_t timelimit = server.lua_time_limit ?\n                             server.lua_time_limit : 5000;\n        if (elapsed >= timelimit) {\n            timeout = 1;\n            ldb.step = 1;\n        } else {\n            return; /* No timeout, ignore the COUNT event. */\n        }\n    }\n\n    if (ldb.step || bp) {\n        char *reason = \"step over\";\n        if (bp) reason = ldb.luabp ? \"redis.breakpoint() called\" :\n                                     \"break point\";\n        else if (timeout) reason = \"timeout reached, infinite loop?\";\n        ldb.step = 0;\n        ldb.luabp = 0;\n        ldbLog(sdscatprintf(sdsempty(),\n            \"* Stopped at %d, stop reason = %s\",\n            ldb.currentline, reason));\n        ldbLogSourceLine(ldb.currentline);\n        ldbSendLogs();\n        if (ldbRepl(lua) == C_ERR && timeout) {\n            /* If the client closed the connection and we have a timeout\n             * connection, let's kill the script otherwise the process\n             * will remain blocked indefinitely. */\n            lua_pushstring(lua, \"timeout during Lua debugging with client closing connection\");\n            lua_error(lua);\n        }\n        server.lua_time_start = mstime();\n    }\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sds.c",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include <limits.h>\n#include \"sds.h\"\n#include \"sdsalloc.h\"\n\nconst char *SDS_NOINIT = \"SDS_NOINIT\";\n\nstatic inline int sdsHdrSize(char type) {\n    switch(type&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return sizeof(struct sdshdr5);\n        case SDS_TYPE_8:\n            return sizeof(struct sdshdr8);\n        case SDS_TYPE_16:\n            return sizeof(struct sdshdr16);\n        case SDS_TYPE_32:\n            return sizeof(struct sdshdr32);\n        case SDS_TYPE_64:\n            return sizeof(struct sdshdr64);\n    }\n    return 0;\n}\n\nstatic inline char sdsReqType(size_t string_size) {\n    if (string_size < 1<<5)\n        return SDS_TYPE_5;\n    if (string_size < 1<<8)\n        return SDS_TYPE_8;\n    if (string_size < 1<<16)\n        return SDS_TYPE_16;\n#if (LONG_MAX == LLONG_MAX)\n    if (string_size < 1ll<<32)\n        return SDS_TYPE_32;\n    return SDS_TYPE_64;\n#else\n    return SDS_TYPE_32;\n#endif\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 * If SDS_NOINIT is used, the buffer is left uninitialized;\n *\n * The string is always null-termined (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3);\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    void *sh;\n    sds s;\n    char type = sdsReqType(initlen);\n    /* Empty strings are usually created in order to append. Use type 8\n     * since type 5 is not good at this. */\n    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;\n    int hdrlen = sdsHdrSize(type);\n    unsigned char *fp; /* flags pointer. */\n\n    sh = s_malloc(hdrlen+initlen+1);\n    if (sh == NULL) return NULL;\n    if (init==SDS_NOINIT)\n        init = NULL;\n    else if (!init)\n        memset(sh, 0, hdrlen+initlen+1);\n    s = (char*)sh+hdrlen;\n    fp = ((unsigned char*)s)-1;\n    switch(type) {\n        case SDS_TYPE_5: {\n            *fp = type | (initlen << SDS_TYPE_BITS);\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n    }\n    if (initlen && init)\n        memcpy(s, init, initlen);\n    s[initlen] = '\\0';\n    return s;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null terminated C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    s_free((char*)s-sdsHdrSize(s[-1]));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    size_t reallen = strlen(s);\n    sdssetlen(s, reallen);\n}\n\n/* Modify an sds string in-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    sdssetlen(s, 0);\n    s[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n    void *sh, *newsh;\n    size_t avail = sdsavail(s);\n    size_t len, newlen;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n\n    /* Return ASAP if there is enough space left. */\n    if (avail >= addlen) return s;\n\n    len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n\n    type = sdsReqType(newlen);\n\n    /* Don't use type 5: the user is appending to the string and type 5 is\n     * not able to remember empty space, so sdsMakeRoomFor() must be called\n     * at every appending operation. */\n    if (type == SDS_TYPE_5) type = SDS_TYPE_8;\n\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        /* Since the header size changes, need to move the string forward,\n         * and can't use realloc */\n        newsh = s_malloc(hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, newlen);\n    return s;\n}\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    void *sh, *newsh;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);\n    size_t len = sdslen(s);\n    size_t avail = sdsavail(s);\n    sh = (char*)s-oldhdrlen;\n\n    /* Return ASAP if there is no space left. */\n    if (avail == 0) return s;\n\n    /* Check what would be the minimum SDS header that is just good enough to\n     * fit this string. */\n    type = sdsReqType(len);\n    hdrlen = sdsHdrSize(type);\n\n    /* If the type is the same, or at least a large enough type is still\n     * required, we just realloc(), letting the allocator to do the copy\n     * only if really needed. Otherwise if the change is huge, we manually\n     * reallocate the string to use the different header type. */\n    if (oldtype==type || type > SDS_TYPE_8) {\n        newsh = s_realloc(sh, oldhdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+oldhdrlen;\n    } else {\n        newsh = s_malloc(hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, len);\n    return s;\n}\n\n/* Return the total size of the allocation of the specified sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    size_t alloc = sdsalloc(s);\n    return sdsHdrSize(s[-1])+alloc+1;\n}\n\n/* Return the pointer of the actual SDS allocation (normally SDS strings\n * are referenced by the start of the string buffer). */\nvoid *sdsAllocPtr(sds s) {\n    return (void*) (s-sdsHdrSize(s[-1]));\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, ssize_t incr) {\n    unsigned char flags = s[-1];\n    size_t len;\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            unsigned char *fp = ((unsigned char*)s)-1;\n            unsigned char oldlen = SDS_TYPE_5_LEN(flags);\n            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));\n            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);\n            len = oldlen+incr;\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        default: len = 0; /* Just to avoid compilation warnings. */\n    }\n    s[len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    size_t curlen = sdslen(s);\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    memcpy(s+curlen, t, len);\n    sdssetlen(s, curlen+len);\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    if (sdsalloc(s) < len) {\n        s = sdsMakeRoomFor(s,len-sdslen(s));\n        if (s == NULL) return NULL;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-termined string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the length of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[SDS_LLSTR_SIZE];\n    int len = sdsll2str(buf,value);\n\n    return sdsnewlen(buf,len);\n}\n\n/* Like sdscatprintf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char staticbuf[1024], *buf = staticbuf, *t;\n    size_t buflen = strlen(fmt)*2;\n\n    /* We try to start using a static buffer for speed.\n     * If not possible we revert to heap allocation. */\n    if (buflen > sizeof(staticbuf)) {\n        buf = s_malloc(buflen);\n        if (buf == NULL) return NULL;\n    } else {\n        buflen = sizeof(staticbuf);\n    }\n\n    /* Try with buffers two times bigger every time we fail to\n     * fit the string in the current buffer size. */\n    while(1) {\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        va_end(cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) s_free(buf);\n            buflen *= 2;\n            buf = s_malloc(buflen);\n            if (buf == NULL) return NULL;\n            continue;\n        }\n        break;\n    }\n\n    /* Finally concat the obtained string to the SDS string and return it. */\n    t = sdscat(s, buf);\n    if (buf != staticbuf) s_free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b).\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    size_t initlen = sdslen(s);\n    const char *f = fmt;\n    long i;\n    va_list ap;\n\n    /* To avoid continuous reallocations, let's start with a buffer that\n     * can hold at least two times the format string itself. It's not the\n     * best heuristic but seems to work in practice. */\n    s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);\n    va_start(ap,fmt);\n    f = fmt;    /* Next format specifier byte to process. */\n    i = initlen; /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        size_t l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sdsavail(s)==0) {\n            s = sdsMakeRoomFor(s,1);\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sdsavail(s) < l) {\n                    s = sdsMakeRoomFor(s,l);\n                }\n                memcpy(s+i,str,l);\n                sdsinclen(s,l);\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else\n                    unum = va_arg(ap,unsigned long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sdsinclen(s,1);\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sdsinclen(s,1);\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n}\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"Aa. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"HelloWorld\".\n */\nsds sdstrim(sds s, const char *cset) {\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > sp && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (s != sp) memmove(s, sp, len);\n    s[len] = '\\0';\n    sdssetlen(s,len);\n    return s;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nvoid sdsrange(sds s, ssize_t start, ssize_t end) {\n    size_t newlen, len = sdslen(s);\n\n    if (len == 0) return;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (ssize_t)len) {\n            newlen = 0;\n        } else if (end >= (ssize_t)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(s, s+start, newlen);\n    s[newlen] = 0;\n    sdssetlen(s,newlen);\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    size_t 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    size_t len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     positive if s1 > s2.\n *     negative if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0);\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, ssize_t len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5;\n    long start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = s_malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = s_realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        s_free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    s_free(tokens);\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\nint is_hex_digit(char c) {\n    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||\n           (c >= 'A' && c <= 'F');\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             is_hex_digit(*(p+2)) &&\n                                             is_hex_digit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            vector = s_realloc(vector,((*argc)+1)*sizeof(char*));\n            vector[*argc] = current;\n            (*argc)++;\n            current = NULL;\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = s_malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    s_free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscat(join,sep);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n/* Wrappers to the allocators used by SDS. Note that SDS will actually\n * just use the macros defined into sdsalloc.h in order to avoid to pay\n * the overhead of function calls. Here we define these wrappers only for\n * the programs SDS is linked to, if they want to touch the SDS internals\n * even if they use a different allocator. */\nvoid *sds_malloc(size_t size) { return s_malloc(size); }\nvoid *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }\nvoid sds_free(void *ptr) { s_free(ptr); }\n\n#if defined(SDS_TEST_MAIN)\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.h\"\n\n#define UNUSED(x) (void)(x)\nint sdsTest(void) {\n    {\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"Hello %s World %I,%I--\", \"Hi!\", LLONG_MIN,LLONG_MAX);\n        test_cond(\"sdscatfmt() seems working in the base case\",\n            sdslen(x) == 60 &&\n            memcmp(x,\"--Hello Hi! World -9223372036854775808,\"\n                     \"9223372036854775807--\",60) == 0)\n        printf(\"[%s]\\n\",x);\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"%u,%U--\", UINT_MAX, ULLONG_MAX);\n        test_cond(\"sdscatfmt() seems working with unsigned numbers\",\n            sdslen(x) == 35 &&\n            memcmp(x,\"--4294967295,18446744073709551615--\",35) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" x\");\n        test_cond(\"sdstrim() works when all chars match\",\n            sdslen(x) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" \");\n        test_cond(\"sdstrim() works when a single char remains\",\n            sdslen(x) == 1 && x[0] == 'x')\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            unsigned int oldfree;\n            char *p;\n            int step = 10, j, i;\n\n            sdsfree(x);\n            sdsfree(y);\n            x = sdsnew(\"0\");\n            test_cond(\"sdsnew() free/len buffers\", sdslen(x) == 1 && sdsavail(x) == 0);\n\n            /* Run the test a few times in order to hit the first two\n             * SDS header types. */\n            for (i = 0; i < 10; i++) {\n                int oldlen = sdslen(x);\n                x = sdsMakeRoomFor(x,step);\n                int type = x[-1]&SDS_TYPE_MASK;\n\n                test_cond(\"sdsMakeRoomFor() len\", sdslen(x) == oldlen);\n                if (type != SDS_TYPE_5) {\n                    test_cond(\"sdsMakeRoomFor() free\", sdsavail(x) >= step);\n                    oldfree = sdsavail(x);\n                }\n                p = x+oldlen;\n                for (j = 0; j < step; j++) {\n                    p[j] = 'A'+j;\n                }\n                sdsIncrLen(x,step);\n            }\n            test_cond(\"sdsMakeRoomFor() content\",\n                memcmp(\"0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ\",x,101) == 0);\n            test_cond(\"sdsMakeRoomFor() final length\",sdslen(x)==101);\n\n            sdsfree(x);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n\n#ifdef SDS_TEST_MAIN\nint main(void) {\n    return sdsTest();\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sds.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\nextern const char *SDS_NOINIT;\n\n#include <sys/types.h>\n#include <stdarg.h>\n#include <stdint.h>\n\ntypedef char *sds;\n\n/* Note: sdshdr5 is never used, we just access the flags byte directly.\n * However is here to document the layout of type 5 SDS strings. */\nstruct __attribute__ ((__packed__)) sdshdr5 {\n    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr8 {\n    uint8_t len; /* used */\n    uint8_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr16 {\n    uint16_t len; /* used */\n    uint16_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr32 {\n    uint32_t len; /* used */\n    uint32_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr64 {\n    uint64_t len; /* used */\n    uint64_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\n\n#define SDS_TYPE_5  0\n#define SDS_TYPE_8  1\n#define SDS_TYPE_16 2\n#define SDS_TYPE_32 3\n#define SDS_TYPE_64 4\n#define SDS_TYPE_MASK 7\n#define SDS_TYPE_BITS 3\n#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));\n#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))\n#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)\n\nstatic inline size_t sdslen(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->len;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->len;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->len;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->len;\n    }\n    return 0;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            return 0;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            return sh->alloc - sh->len;\n        }\n    }\n    return 0;\n}\n\nstatic inline void sdssetlen(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len = newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len = newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len = newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len = newlen;\n            break;\n    }\n}\n\nstatic inline void sdsinclen(sds s, size_t inc) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len += inc;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len += inc;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len += inc;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len += inc;\n            break;\n    }\n}\n\n/* sdsalloc() = sdsavail() + sdslen() */\nstatic inline size_t sdsalloc(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->alloc;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->alloc;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->alloc;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->alloc;\n    }\n    return 0;\n}\n\nstatic inline void sdssetalloc(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            /* Nothing to do, this type has no total allocation info. */\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->alloc = newlen;\n            break;\n    }\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nsds sdstrim(sds s, const char *cset);\nvoid sdsrange(sds s, ssize_t start, ssize_t end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, ssize_t incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\nvoid *sdsAllocPtr(sds s);\n\n/* Export the allocator used by SDS to the program using SDS.\n * Sometimes the program SDS is linked to, may use a different set of\n * allocators, but may want to allocate or free things that SDS will\n * respectively free or allocate. */\nvoid *sds_malloc(size_t size);\nvoid *sds_realloc(void *ptr, size_t size);\nvoid sds_free(void *ptr);\n\n#ifdef REDIS_TEST\nint sdsTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sdsalloc.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* SDS allocator selection.\n *\n * This file is used in order to change the SDS allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#ifndef __SDS_ALLOC_H__\n#define __SDS_ALLOC_H__\n\n#include \"zmalloc.h\"\n#define s_malloc zmalloc\n#define s_realloc zrealloc\n#define s_free zfree\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"hiredis.h\"\n#ifdef USE_OPENSSL\n#include \"openssl/ssl.h\"\n#include \"hiredis_ssl.h\"\n#endif\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#ifdef USE_OPENSSL\nextern SSL_CTX *redis_tls_ctx;\n#endif\n\n#define REDIS_SENTINEL_PORT 26379\n\n/* ======================== Sentinel global state =========================== */\n\n/* Address object, used to describe an ip:port pair. */\ntypedef struct sentinelAddr {\n    char *ip;\n    int port;\n} sentinelAddr;\n\n/* A Sentinel Redis Instance object is monitoring. */\n#define SRI_MASTER  (1<<0)\n#define SRI_SLAVE   (1<<1)\n#define SRI_SENTINEL (1<<2)\n#define SRI_S_DOWN (1<<3)   /* Subjectively down (no quorum). */\n#define SRI_O_DOWN (1<<4)   /* Objectively down (confirmed by others). */\n#define SRI_MASTER_DOWN (1<<5) /* A Sentinel with this flag set thinks that\n                                   its master is down. */\n#define SRI_FAILOVER_IN_PROGRESS (1<<6) /* Failover is in progress for\n                                           this master. */\n#define SRI_PROMOTED (1<<7)            /* Slave selected for promotion. */\n#define SRI_RECONF_SENT (1<<8)     /* SLAVEOF <newmaster> sent. */\n#define SRI_RECONF_INPROG (1<<9)   /* Slave synchronization in progress. */\n#define SRI_RECONF_DONE (1<<10)     /* Slave synchronized with new master. */\n#define SRI_FORCE_FAILOVER (1<<11)  /* Force failover with master up. */\n#define SRI_SCRIPT_KILL_SENT (1<<12) /* SCRIPT KILL already sent on -BUSY */\n\n/* Note: times are in milliseconds. */\n#define SENTINEL_INFO_PERIOD 10000\n#define SENTINEL_PING_PERIOD 1000\n#define SENTINEL_ASK_PERIOD 1000\n#define SENTINEL_PUBLISH_PERIOD 2000\n#define SENTINEL_DEFAULT_DOWN_AFTER 30000\n#define SENTINEL_HELLO_CHANNEL \"__sentinel__:hello\"\n#define SENTINEL_TILT_TRIGGER 2000\n#define SENTINEL_TILT_PERIOD (SENTINEL_PING_PERIOD*30)\n#define SENTINEL_DEFAULT_SLAVE_PRIORITY 100\n#define SENTINEL_SLAVE_RECONF_TIMEOUT 10000\n#define SENTINEL_DEFAULT_PARALLEL_SYNCS 1\n#define SENTINEL_MIN_LINK_RECONNECT_PERIOD 15000\n#define SENTINEL_DEFAULT_FAILOVER_TIMEOUT (60*3*1000)\n#define SENTINEL_MAX_PENDING_COMMANDS 100\n#define SENTINEL_ELECTION_TIMEOUT 10000\n#define SENTINEL_MAX_DESYNC 1000\n#define SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG 1\n\n/* Failover machine different states. */\n#define SENTINEL_FAILOVER_STATE_NONE 0  /* No failover in progress. */\n#define SENTINEL_FAILOVER_STATE_WAIT_START 1  /* Wait for failover_start_time*/\n#define SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* Select slave to promote */\n#define SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* Slave -> Master */\n#define SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* Wait slave to change role */\n#define SENTINEL_FAILOVER_STATE_RECONF_SLAVES 5 /* SLAVEOF newmaster */\n#define SENTINEL_FAILOVER_STATE_UPDATE_CONFIG 6 /* Monitor promoted slave. */\n\n#define SENTINEL_MASTER_LINK_STATUS_UP 0\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#define SENTINEL_NO_FLAGS 0\n#define SENTINEL_GENERATE_EVENT (1<<16)\n#define SENTINEL_LEADER (1<<17)\n#define SENTINEL_OBSERVER (1<<18)\n\n/* Script execution flags and limits. */\n#define SENTINEL_SCRIPT_NONE 0\n#define SENTINEL_SCRIPT_RUNNING 1\n#define SENTINEL_SCRIPT_MAX_QUEUE 256\n#define SENTINEL_SCRIPT_MAX_RUNNING 16\n#define SENTINEL_SCRIPT_MAX_RUNTIME 60000 /* 60 seconds max exec time. */\n#define SENTINEL_SCRIPT_MAX_RETRY 10\n#define SENTINEL_SCRIPT_RETRY_DELAY 30000 /* 30 seconds between retries. */\n\n/* SENTINEL SIMULATE-FAILURE command flags. */\n#define SENTINEL_SIMFAILURE_NONE 0\n#define SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION (1<<0)\n#define SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION (1<<1)\n\n/* The link to a sentinelRedisInstance. When we have the same set of Sentinels\n * monitoring many masters, we have different instances representing the\n * same Sentinels, one per master, and we need to share the hiredis connections\n * among them. Oherwise if 5 Sentinels are monitoring 100 masters we create\n * 500 outgoing connections instead of 5.\n *\n * So this structure represents a reference counted link in terms of the two\n * hiredis connections for commands and Pub/Sub, and the fields needed for\n * failure detection, since the ping/pong time are now local to the link: if\n * the link is available, the instance is avaialbe. This way we don't just\n * have 5 connections instead of 500, we also send 5 pings instead of 500.\n *\n * Links are shared only for Sentinels: master and slave instances have\n * a link with refcount = 1, always. */\ntypedef struct instanceLink {\n    int refcount;          /* Number of sentinelRedisInstance owners. */\n    int disconnected;      /* Non-zero if we need to reconnect cc or pc. */\n    int pending_commands;  /* Number of commands sent waiting for a reply. */\n    redisAsyncContext *cc; /* Hiredis context for commands. */\n    redisAsyncContext *pc; /* Hiredis context for Pub / Sub. */\n    mstime_t cc_conn_time; /* cc connection time. */\n    mstime_t pc_conn_time; /* pc connection time. */\n    mstime_t pc_last_activity; /* Last time we received any message. */\n    mstime_t last_avail_time; /* Last time the instance replied to ping with\n                                 a reply we consider valid. */\n    mstime_t act_ping_time;   /* Time at which the last pending ping (no pong\n                                 received after it) was sent. This field is\n                                 set to 0 when a pong is received, and set again\n                                 to the current time if the value is 0 and a new\n                                 ping is sent. */\n    mstime_t last_ping_time;  /* Time at which we sent the last ping. This is\n                                 only used to avoid sending too many pings\n                                 during failure. Idle time is computed using\n                                 the act_ping_time field. */\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    mstime_t last_reconn_time;  /* Last reconnection attempt performed when\n                                   the link was down. */\n} instanceLink;\n\ntypedef struct sentinelRedisInstance {\n    int flags;      /* See SRI_... defines */\n    char *name;     /* Master name from the point of view of this sentinel. */\n    char *runid;    /* Run ID of this instance, or unique ID if is a Sentinel.*/\n    uint64_t config_epoch;  /* Configuration epoch. */\n    sentinelAddr *addr; /* Master host. */\n    instanceLink *link; /* Link to the instance, may be shared for Sentinels. */\n    mstime_t last_pub_time;   /* Last time we sent hello via Pub/Sub. */\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    mstime_t last_master_down_reply_time; /* Time of last reply to\n                                             SENTINEL is-master-down command. */\n    mstime_t s_down_since_time; /* Subjectively down since time. */\n    mstime_t o_down_since_time; /* Objectively down since time. */\n    mstime_t down_after_period; /* Consider it down after that period. */\n    mstime_t info_refresh;  /* Time at which we received INFO output from it. */\n    dict *renamed_commands;     /* Commands renamed in this instance:\n                                   Sentinel will use the alternative commands\n                                   mapped on this table to send things like\n                                   SLAVEOF, CONFING, INFO, ... */\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    int role_reported;\n    mstime_t role_reported_time;\n    mstime_t slave_conf_change_time; /* Last time slave master addr changed. */\n\n    /* Master specific. */\n    dict *sentinels;    /* Other sentinels monitoring the same master. */\n    dict *slaves;       /* Slaves for this master instance. */\n    unsigned int quorum;/* Number of sentinels that need to agree on failure. */\n    int parallel_syncs; /* How many slaves to reconfigure at same time. */\n    char *auth_pass;    /* Password to use for AUTH against master & replica. */\n    char *auth_user;    /* Username for ACLs AUTH against master & replica. */\n\n    /* Slave specific. */\n    mstime_t master_link_down_time; /* Slave replication link down time. */\n    int slave_priority; /* Slave priority according to its INFO output. */\n    mstime_t slave_reconf_sent_time; /* Time at which we sent SLAVE OF <new> */\n    struct sentinelRedisInstance *master; /* Master instance if it's slave. */\n    char *slave_master_host;    /* Master host as reported by INFO */\n    int slave_master_port;      /* Master port as reported by INFO */\n    int slave_master_link_status; /* Master link status as reported by INFO */\n    unsigned long long slave_repl_offset; /* Slave replication offset. */\n    /* Failover */\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    uint64_t leader_epoch; /* Epoch of the 'leader' field. */\n    uint64_t failover_epoch; /* Epoch of the currently started failover. */\n    int failover_state; /* See SENTINEL_FAILOVER_STATE_* defines. */\n    mstime_t failover_state_change_time;\n    mstime_t failover_start_time;   /* Last failover attempt start time. */\n    mstime_t failover_timeout;      /* Max time to refresh failover state. */\n    mstime_t failover_delay_logged; /* For what failover_start_time value we\n                                       logged the failover delay. */\n    struct sentinelRedisInstance *promoted_slave; /* Promoted slave instance. */\n    /* Scripts executed to notify admin or reconfigure clients: when they\n     * are set to NULL no script is executed. */\n    char *notification_script;\n    char *client_reconfig_script;\n    sds info; /* cached INFO output */\n} sentinelRedisInstance;\n\n/* Main state. */\nstruct sentinelState {\n    char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */\n    uint64_t current_epoch;         /* Current epoch. */\n    dict *masters;      /* Dictionary of master sentinelRedisInstances.\n                           Key is the instance name, value is the\n                           sentinelRedisInstance structure pointer. */\n    int tilt;           /* Are we in TILT mode? */\n    int running_scripts;    /* Number of scripts in execution right now. */\n    mstime_t tilt_start_time;       /* When TITL started. */\n    mstime_t previous_time;         /* Last time we ran the time handler. */\n    list *scripts_queue;            /* Queue of user scripts to execute. */\n    char *announce_ip;  /* IP addr that is gossiped to other sentinels if\n                           not NULL. */\n    int announce_port;  /* Port that is gossiped to other sentinels if\n                           non zero. */\n    unsigned long simfailure_flags; /* Failures simulation. */\n    int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script\n                                  paths at runtime? */\n} sentinel;\n\n/* A script execution job. */\ntypedef struct sentinelScriptJob {\n    int flags;              /* Script job flags: SENTINEL_SCRIPT_* */\n    int retry_num;          /* Number of times we tried to execute it. */\n    char **argv;            /* Arguments to call the script. */\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    pid_t pid;              /* Script execution pid. */\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\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    zfree(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 C_ERR;\n\n    /* Create container for context and r/w events */\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    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 C_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 instanceLinkConnectionError(const 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);\nint sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master);\nsentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, char *ip, int port, char *runid);\nvoid sentinelSimFailureCrash(void);\n\n/* ========================= Dictionary types =============================== */\n\nuint64_t dictSdsHash(const void *key);\nuint64_t dictSdsCaseHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\nint dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);\nvoid releaseSentinelRedisInstance(sentinelRedisInstance *ri);\n\nvoid dictInstancesValDestructor (void *privdata, void *obj) {\n    UNUSED(privdata);\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. */\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. */\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/* Instance renamed commands table. */\ndictType renamedCommandsDictType = {\n    dictSdsCaseHash,           /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCaseCompare,     /* key compare */\n    dictSdsDestructor,         /* key destructor */\n    dictSdsDestructor          /* val destructor */\n};\n\n/* =========================== Initialization =============================== */\n\nvoid sentinelCommand(client *c);\nvoid sentinelInfoCommand(client *c);\nvoid sentinelSetCommand(client *c);\nvoid sentinelPublishCommand(client *c);\nvoid sentinelRoleCommand(client *c);\n\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    {\"role\",sentinelRoleCommand,1,\"ok-loading\",0,NULL,0,0,0,0,0},\n    {\"client\",clientCommand,-2,\"read-only no-script\",0,NULL,0,0,0,0,0},\n    {\"shutdown\",shutdownCommand,-1,\"\",0,NULL,0,0,0,0,0},\n    {\"auth\",authCommand,2,\"no-auth no-script ok-loading ok-stale fast\",0,NULL,0,0,0,0,0},\n    {\"hello\",helloCommand,-2,\"no-auth no-script fast\",0,NULL,0,0,0,0,0}\n};\n\n/* This function overwrites a few normal Redis config default with Sentinel\n * specific defaults. */\nvoid initSentinelConfig(void) {\n    server.port = REDIS_SENTINEL_PORT;\n    server.protected_mode = 0; /* Sentinel must be exposed. */\n}\n\n/* Perform the Sentinel mode initialization. */\nvoid initSentinel(void) {\n    unsigned int j;\n\n    /* Remove usual Redis commands from the command table, then just add\n     * the SENTINEL command. */\n    dictEmpty(server.commands,NULL);\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        serverAssert(retval == DICT_OK);\n\n        /* Translate the command string flags description into an actual\n         * set of flags. */\n        if (populateCommandTableParseFlags(cmd,cmd->sflags) == C_ERR)\n            serverPanic(\"Unsupported command flag\");\n    }\n\n    /* Initialize various data structures. */\n    sentinel.current_epoch = 0;\n    sentinel.masters = dictCreate(&instancesDictType,NULL);\n    sentinel.tilt = 0;\n    sentinel.tilt_start_time = 0;\n    sentinel.previous_time = mstime();\n    sentinel.running_scripts = 0;\n    sentinel.scripts_queue = listCreate();\n    sentinel.announce_ip = NULL;\n    sentinel.announce_port = 0;\n    sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;\n    sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;\n    memset(sentinel.myid,0,sizeof(sentinel.myid));\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. */\nvoid sentinelIsRunning(void) {\n    int j;\n\n    if (server.configfile == NULL) {\n        serverLog(LL_WARNING,\n            \"Sentinel started without a config file. Exiting...\");\n        exit(1);\n    } else if (access(server.configfile,W_OK) == -1) {\n        serverLog(LL_WARNING,\n            \"Sentinel config file %s is not writable: %s. Exiting...\",\n            server.configfile,strerror(errno));\n        exit(1);\n    }\n\n    /* If this Sentinel has yet no ID set in the configuration file, we\n     * pick a random one and persist the config on disk. From now on this\n     * will be this Sentinel ID across restarts. */\n    for (j = 0; j < CONFIG_RUN_ID_SIZE; j++)\n        if (sentinel.myid[j] != 0) break;\n\n    if (j == CONFIG_RUN_ID_SIZE) {\n        /* Pick ID and persist the config. */\n        getRandomHexChars(sentinel.myid,CONFIG_RUN_ID_SIZE);\n        sentinelFlushConfig();\n    }\n\n    /* Log its ID to make debugging of issues simpler. */\n    serverLog(LL_WARNING,\"Sentinel ID is %s\", sentinel.myid);\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 * On error NULL is returned and errno is set to:\n *  ENOENT: Can't resolve the hostname.\n *  EINVAL: Invalid port number.\n */\nsentinelAddr *createSentinelAddr(char *hostname, int port) {\n    char ip[NET_IP_STR_LEN];\n    sentinelAddr *sa;\n\n    if (port < 0 || port > 65535) {\n        errno = EINVAL;\n        return NULL;\n    }\n    if (anetResolve(NULL,hostname,ip,sizeof(ip)) == ANET_ERR) {\n        errno = ENOENT;\n        return NULL;\n    }\n    sa = zmalloc(sizeof(*sa));\n    sa->ip = sdsnew(ip);\n    sa->port = port;\n    return sa;\n}\n\n/* Return a duplicate of the source address. */\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. */\nvoid releaseSentinelAddr(sentinelAddr *sa) {\n    sdsfree(sa->ip);\n    zfree(sa);\n}\n\n/* Return non-zero if two addresses are equal. */\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 * 'level' is the log level for logging. Only LL_WARNING events will trigger\n * the execution of the user notification script.\n *\n * 'type' is the message type, also used as a pub/sub channel name.\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 * The remaining arguments are printf-alike.\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 *  <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 *  @ <master name> <master ip> <master port>\n *\n *  Any other specifier after \"%@\" is processed by printf itself.\n */\nvoid sentinelEvent(int level, char *type, sentinelRedisInstance *ri,\n                   const char *fmt, ...) {\n    va_list ap;\n    char msg[LOG_MAX_LEN];\n    robj *channel, *payload;\n\n    /* Handle %@ */\n    if (fmt[0] == '%' && fmt[1] == '@') {\n        sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ?\n                                         NULL : ri->master;\n\n        if (master) {\n            snprintf(msg, sizeof(msg), \"%s %s %s %d @ %s %s %d\",\n                sentinelRedisInstanceTypeStr(ri),\n                ri->name, ri->addr->ip, ri->addr->port,\n                master->name, master->addr->ip, master->addr->port);\n        } else {\n            snprintf(msg, sizeof(msg), \"%s %s %s %d\",\n                sentinelRedisInstanceTypeStr(ri),\n                ri->name, ri->addr->ip, ri->addr->port);\n        }\n        fmt += 2;\n    } else {\n        msg[0] = '\\0';\n    }\n\n    /* Use vsprintf for the rest of the formatting if any. */\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    if (level >= server.verbosity)\n        serverLog(level,\"%s %s\",type,msg);\n\n    /* Publish the message via Pub/Sub if it's not a debugging one. */\n    if (level != LL_DEBUG) {\n        channel = createStringObject(type,strlen(type));\n        payload = createStringObject(msg,strlen(msg));\n        pubsubPublishMessage(channel,payload);\n        decrRefCount(channel);\n        decrRefCount(payload);\n    }\n\n    /* Call the notification script if applicable. */\n    if (level == LL_WARNING && ri != NULL) {\n        sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ?\n                                         ri : ri->master;\n        if (master && 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. */\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(LL_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. */\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#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    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    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    listAddNodeTail(sentinel.scripts_queue,sj);\n\n    /* Remove the oldest non running script if we already hit the limit. */\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            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        serverAssert(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). */\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. */\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    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        if (sj->flags & SENTINEL_SCRIPT_RUNNING) continue;\n\n        /* Skip if it's a retry, but not enough time has elapsed. */\n        if (sj->start_time && sj->start_time > now) continue;\n\n        sj->flags |= SENTINEL_SCRIPT_RUNNING;\n        sj->start_time = mstime();\n        sj->retry_num++;\n        pid = fork();\n\n        if (pid == -1) {\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(LL_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            /* 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            sentinel.running_scripts++;\n            sj->pid = pid;\n            sentinelEvent(LL_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. */\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. */\nvoid sentinelCollectTerminatedScripts(void) {\n    int statloc;\n    pid_t pid;\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        if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);\n        sentinelEvent(LL_DEBUG,\"-script-child\",NULL,\"%ld %d %d\",\n            (long)pid, exitcode, bysignal);\n\n        ln = sentinelGetScriptListNodeByPid(pid);\n        if (ln == NULL) {\n            serverLog(LL_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            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            if (bysignal || exitcode != 0) {\n                sentinelEvent(LL_WARNING,\"-script-error\",NULL,\n                              \"%s %d %d\", sj->argv[0], bysignal, exitcode);\n            }\n            listDelNode(sentinel.scripts_queue,ln);\n            sentinelReleaseScriptJob(sj);\n        }\n        sentinel.running_scripts--;\n    }\n}\n\n/* Kill scripts in timeout, they'll be collected by the\n * sentinelCollectTerminatedScripts() function. */\nvoid sentinelKillTimedoutScripts(void) {\n    listNode *ln;\n    listIter li;\n    mstime_t now = mstime();\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 &&\n            (now - sj->start_time) > SENTINEL_SCRIPT_MAX_RUNTIME)\n        {\n            sentinelEvent(LL_WARNING,\"-script-timeout\",NULL,\"%s %ld\",\n                sj->argv[0], (long)sj->pid);\n            kill(sj->pid,SIGKILL);\n        }\n    }\n}\n\n/* Implements SENTINEL PENDING-SCRIPTS command. */\nvoid sentinelPendingScriptsCommand(client *c) {\n    listNode *ln;\n    listIter li;\n\n    addReplyArrayLen(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        addReplyMapLen(c,5);\n\n        addReplyBulkCString(c,\"argv\");\n        while (sj->argv[j]) j++;\n        addReplyArrayLen(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 * <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 * <state> is currently always \"failover\".\n * <role> is either \"leader\" or \"observer\".\n *\n * from/to fields are respectively master -> promoted slave addresses for\n * \"start\" and \"end\". */\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    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/* =============================== instanceLink ============================= */\n\n/* Create a not yet connected link object. */\ninstanceLink *createInstanceLink(void) {\n    instanceLink *link = zmalloc(sizeof(*link));\n\n    link->refcount = 1;\n    link->disconnected = 1;\n    link->pending_commands = 0;\n    link->cc = NULL;\n    link->pc = NULL;\n    link->cc_conn_time = 0;\n    link->pc_conn_time = 0;\n    link->last_reconn_time = 0;\n    link->pc_last_activity = 0;\n    /* We set the act_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    link->act_ping_time = mstime();\n    link->last_ping_time = 0;\n    link->last_avail_time = mstime();\n    link->last_pong_time = mstime();\n    return link;\n}\n\n/* Disconnect an hiredis connection in the context of an instance link. */\nvoid instanceLinkCloseConnection(instanceLink *link, redisAsyncContext *c) {\n    if (c == NULL) return;\n\n    if (link->cc == c) {\n        link->cc = NULL;\n        link->pending_commands = 0;\n    }\n    if (link->pc == c) link->pc = NULL;\n    c->data = NULL;\n    link->disconnected = 1;\n    redisAsyncFree(c);\n}\n\n/* Decrement the refcount of a link object, if it drops to zero, actually\n * free it and return NULL. Otherwise don't do anything and return the pointer\n * to the object.\n *\n * If we are not going to free the link and ri is not NULL, we rebind all the\n * pending requests in link->cc (hiredis connection for commands) to a\n * callback that will just ignore them. This is useful to avoid processing\n * replies for an instance that no longer exists. */\ninstanceLink *releaseInstanceLink(instanceLink *link, sentinelRedisInstance *ri)\n{\n    serverAssert(link->refcount > 0);\n    link->refcount--;\n    if (link->refcount != 0) {\n        if (ri && ri->link->cc) {\n            /* This instance may have pending callbacks in the hiredis async\n             * context, having as 'privdata' the instance that we are going to\n             * free. Let's rewrite the callback list, directly exploiting\n             * hiredis internal data structures, in order to bind them with\n             * a callback that will ignore the reply at all. */\n            redisCallback *cb;\n            redisCallbackList *callbacks = &link->cc->replies;\n\n            cb = callbacks->head;\n            while(cb) {\n                if (cb->privdata == ri) {\n                    cb->fn = sentinelDiscardReplyCallback;\n                    cb->privdata = NULL; /* Not strictly needed. */\n                }\n                cb = cb->next;\n            }\n        }\n        return link; /* Other active users. */\n    }\n\n    instanceLinkCloseConnection(link,link->cc);\n    instanceLinkCloseConnection(link,link->pc);\n    zfree(link);\n    return NULL;\n}\n\n/* This function will attempt to share the instance link we already have\n * for the same Sentinel in the context of a different master, with the\n * instance we are passing as argument.\n *\n * This way multiple Sentinel objects that refer all to the same physical\n * Sentinel instance but in the context of different masters will use\n * a single connection, will send a single PING per second for failure\n * detection and so forth.\n *\n * Return C_OK if a matching Sentinel was found in the context of a\n * different master and sharing was performed. Otherwise C_ERR\n * is returned. */\nint sentinelTryConnectionSharing(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_SENTINEL);\n    dictIterator *di;\n    dictEntry *de;\n\n    if (ri->runid == NULL) return C_ERR; /* No way to identify it. */\n    if (ri->link->refcount > 1) return C_ERR; /* Already shared. */\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *master = dictGetVal(de), *match;\n        /* We want to share with the same physical Sentinel referenced\n         * in other masters, so skip our master. */\n        if (master == ri->master) continue;\n        match = getSentinelRedisInstanceByAddrAndRunID(master->sentinels,\n                                                       NULL,0,ri->runid);\n        if (match == NULL) continue; /* No match. */\n        if (match == ri) continue; /* Should never happen but... safer. */\n\n        /* We identified a matching Sentinel, great! Let's free our link\n         * and use the one of the matching Sentinel. */\n        releaseInstanceLink(ri->link,NULL);\n        ri->link = match->link;\n        match->link->refcount++;\n        return C_OK;\n    }\n    dictReleaseIterator(di);\n    return C_ERR;\n}\n\n/* When we detect a Sentinel to switch address (reporting a different IP/port\n * pair in Hello messages), let's update all the matching Sentinels in the\n * context of other masters as well and disconnect the links, so that everybody\n * will be updated.\n *\n * Return the number of updated Sentinel addresses. */\nint sentinelUpdateSentinelAddressInAllMasters(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_SENTINEL);\n    dictIterator *di;\n    dictEntry *de;\n    int reconfigured = 0;\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *master = dictGetVal(de), *match;\n        match = getSentinelRedisInstanceByAddrAndRunID(master->sentinels,\n                                                       NULL,0,ri->runid);\n        /* If there is no match, this master does not know about this\n         * Sentinel, try with the next one. */\n        if (match == NULL) continue;\n\n        /* Disconnect the old links if connected. */\n        if (match->link->cc != NULL)\n            instanceLinkCloseConnection(match->link,match->link->cc);\n        if (match->link->pc != NULL)\n            instanceLinkCloseConnection(match->link,match->link->pc);\n\n        if (match == ri) continue; /* Address already updated for it. */\n\n        /* Update the address of the matching Sentinel by copying the address\n         * of the Sentinel object that received the address update. */\n        releaseSentinelAddr(match->addr);\n        match->addr = dupSentinelAddr(ri->addr);\n        reconfigured++;\n    }\n    dictReleaseIterator(di);\n    if (reconfigured)\n        sentinelEvent(LL_NOTICE,\"+sentinel-address-update\", ri,\n                    \"%@ %d additional matching instances\", reconfigured);\n    return reconfigured;\n}\n\n/* This function is called when an hiredis connection reported an error.\n * We set it to NULL and mark the link as disconnected so that it will be\n * reconnected again.\n *\n * Note: we don't free the hiredis context as hiredis will do it for us\n * for async connections. */\nvoid instanceLinkConnectionError(const redisAsyncContext *c) {\n    instanceLink *link = c->data;\n    int pubsub;\n\n    if (!link) return;\n\n    pubsub = (link->pc == c);\n    if (pubsub)\n        link->pc = NULL;\n    else\n        link->cc = NULL;\n    link->disconnected = 1;\n}\n\n/* Hiredis connection established / disconnected callbacks. We need them\n * just to cleanup our link state. */\nvoid sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) {\n    if (status != C_OK) instanceLinkConnectionError(c);\n}\n\nvoid sentinelDisconnectCallback(const redisAsyncContext *c, int status) {\n    UNUSED(status);\n    instanceLinkConnectionError(c);\n}\n\n/* ========================== sentinelRedisInstance ========================= */\n\n/* Create a redis instance, the following fields must be populated by the\n * caller if needed:\n * runid: set to NULL but will be populated once INFO output is received.\n * info_refresh: is set to 0 to mean that we never received INFO so far.\n *\n * If SRI_MASTER is set into initial flags the instance is added to\n * sentinel.masters table.\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 * If the instance is a slave or sentinel, the name parameter is ignored and\n * is created automatically as 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 * The function may also fail and return NULL with errno set to EBUSY if\n * a master with the same name, a slave with the same address, or a sentinel\n * with the same ID already exists. */\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[NET_PEER_ID_LEN], *sdsname;\n\n    serverAssert(flags & (SRI_MASTER|SRI_SLAVE|SRI_SENTINEL));\n    serverAssert((flags & SRI_MASTER) || master != NULL);\n\n    /* Check address validity. */\n    addr = createSentinelAddr(hostname,port);\n    if (addr == NULL) return NULL;\n\n    /* For slaves use ip:port as name. */\n    if (flags & SRI_SLAVE) {\n        anetFormatAddr(slavename, sizeof(slavename), 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    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        releaseSentinelAddr(addr);\n        sdsfree(sdsname);\n        errno = EBUSY;\n        return NULL;\n    }\n\n    /* Create the instance object. */\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    ri->flags = flags;\n    ri->name = sdsname;\n    ri->runid = NULL;\n    ri->config_epoch = 0;\n    ri->addr = addr;\n    ri->link = createInstanceLink();\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->auth_user = 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    ri->renamed_commands = dictCreate(&renamedCommandsDictType,NULL);\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    ri->info = 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    dictAdd(table, ri->name, ri);\n    return ri;\n}\n\n/* Release this instance and all its slaves, sentinels, hiredis connections.\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. */\nvoid releaseSentinelRedisInstance(sentinelRedisInstance *ri) {\n    /* Release all its slaves or sentinels if any. */\n    dictRelease(ri->sentinels);\n    dictRelease(ri->slaves);\n\n    /* Disconnect the instance. */\n    releaseInstanceLink(ri->link,ri);\n\n    /* Free other resources. */\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    sdsfree(ri->auth_user);\n    sdsfree(ri->info);\n    releaseSentinelAddr(ri->addr);\n    dictRelease(ri->renamed_commands);\n\n    /* Clear state into the master if needed. */\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. */\nsentinelRedisInstance *sentinelRedisInstanceLookupSlave(\n                sentinelRedisInstance *ri, char *ip, int port)\n{\n    sds key;\n    sentinelRedisInstance *slave;\n    char buf[NET_PEER_ID_LEN];\n\n    serverAssert(ri->flags & SRI_MASTER);\n    anetFormatAddr(buf,sizeof(buf),ip,port);\n    key = sdsnew(buf);\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. */\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 remove the Sentinel with the specified ID from the\n * specified master.\n *\n * If \"runid\" is NULL the function returns ASAP.\n *\n * This function is useful because on Sentinels address switch, we want to\n * remove our old entry and add a new one for the same ID but with the new\n * address.\n *\n * The function returns 1 if the matching Sentinel was removed, otherwise\n * 0 if there was no Sentinel with this ID. */\nint removeMatchingSentinelFromMaster(sentinelRedisInstance *master, char *runid) {\n    dictIterator *di;\n    dictEntry *de;\n    int removed = 0;\n\n    if (runid == NULL) return 0;\n\n    di = dictGetSafeIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (ri->runid && strcmp(ri->runid,runid) == 0) {\n            dictDelete(master->sentinels,ri->name);\n            removed++;\n        }\n    }\n    dictReleaseIterator(di);\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 or ip can be NULL. In such a case the search is performed only\n * by the non-NULL field. */\nsentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, char *ip, int port, char *runid) {\n    dictIterator *di;\n    dictEntry *de;\n    sentinelRedisInstance *instance = NULL;\n\n    serverAssert(ip || runid);   /* User must pass at least one search param. */\n    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (runid && !ri->runid) continue;\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    return instance;\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. */\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. */\nvoid sentinelDelFlagsToDictOfRedisInstances(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/* Reset the state of a monitored master:\n * 1) Remove all slaves.\n * 2) Remove all sentinels.\n * 3) Remove most of the flags resulting from runtime operations.\n * 4) Reset timers to their default value. For example after a reset it will be\n *    possible to failover again the same master ASAP, without waiting the\n *    failover timeout delay.\n * 5) In the process of doing this undo the failover if in progress.\n * 6) Disconnect the connections with the master (will reconnect automatically).\n */\n\n#define SENTINEL_RESET_NO_SENTINELS (1<<0)\nvoid sentinelResetMaster(sentinelRedisInstance *ri, int flags) {\n    serverAssert(ri->flags & SRI_MASTER);\n    dictRelease(ri->slaves);\n    ri->slaves = dictCreate(&instancesDictType,NULL);\n    if (!(flags & SENTINEL_RESET_NO_SENTINELS)) {\n        dictRelease(ri->sentinels);\n        ri->sentinels = dictCreate(&instancesDictType,NULL);\n    }\n    instanceLinkCloseConnection(ri->link,ri->link->cc);\n    instanceLinkCloseConnection(ri->link,ri->link->pc);\n    ri->flags &= SRI_MASTER;\n    if (ri->leader) {\n        sdsfree(ri->leader);\n        ri->leader = NULL;\n    }\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = 0;\n    ri->failover_start_time = 0; /* We can failover again ASAP. */\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->link->act_ping_time = mstime();\n    ri->link->last_ping_time = 0;\n    ri->link->last_avail_time = mstime();\n    ri->link->last_pong_time = mstime();\n    ri->role_reported_time = mstime();\n    ri->role_reported = SRI_MASTER;\n    if (flags & SENTINEL_GENERATE_EVENT)\n        sentinelEvent(LL_WARNING,\"+reset-master\",ri,\"%@\");\n}\n\n/* Call sentinelResetMaster() on every master with a name matching the specified\n * pattern. */\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 * This is used to handle the +switch-master event.\n *\n * The function returns C_ERR if the address can't be resolved for some\n * reason. Otherwise C_OK is returned.  */\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    newaddr = createSentinelAddr(ip,port);\n    if (newaddr == NULL) return C_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    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        if (sentinelAddrIsEqual(slave->addr,newaddr)) continue;\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    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    sentinelResetMaster(master,SENTINEL_RESET_NO_SENTINELS);\n    oldaddr = master->addr;\n    master->addr = newaddr;\n    master->o_down_since_time = 0;\n    master->s_down_since_time = 0;\n\n    /* Add slaves back. */\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        releaseSentinelAddr(slaves[j]);\n        if (slave) sentinelEvent(LL_NOTICE,\"+slave\",slave,\"%@\");\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    releaseSentinelAddr(oldaddr);\n    sentinelFlushConfig();\n    return C_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. */\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. */\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\nchar *sentinelGetInstanceTypeString(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 is used in order to send commands to Redis instances: the\n * commands we send from Sentinel may be renamed, a common case is a master\n * with CONFIG and SLAVEOF commands renamed for security concerns. In that\n * case we check the ri->renamed_command table (or if the instance is a slave,\n * we check the one of the master), and map the command that we should send\n * to the set of renamed commads. However, if the command was not renamed,\n * we just return \"command\" itself. */\nchar *sentinelInstanceMapCommand(sentinelRedisInstance *ri, char *command) {\n    sds sc = sdsnew(command);\n    if (ri->master) ri = ri->master;\n    char *retval = dictFetchValue(ri->renamed_commands, sc);\n    sdsfree(sc);\n    return retval ? retval : command;\n}\n\n/* ============================ Config handling ============================= */\nchar *sentinelHandleConfiguration(char **argv, int argc) {\n    sentinelRedisInstance *ri;\n\n    if (!strcasecmp(argv[0],\"monitor\") && argc == 5) {\n        /* monitor <name> <host> <port> <quorum> */\n        int quorum = atoi(argv[4]);\n\n        if (quorum <= 0) return \"Quorum must be 1 or greater.\";\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    } else if (!strcasecmp(argv[0],\"down-after-milliseconds\") && argc == 3) {\n        /* down-after-milliseconds <name> <milliseconds> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->down_after_period = atoi(argv[2]);\n        if (ri->down_after_period <= 0)\n            return \"negative or zero time parameter.\";\n        sentinelPropagateDownAfterPeriod(ri);\n    } else if (!strcasecmp(argv[0],\"failover-timeout\") && argc == 3) {\n        /* failover-timeout <name> <milliseconds> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->failover_timeout = atoi(argv[2]);\n        if (ri->failover_timeout <= 0)\n            return \"negative or zero time parameter.\";\n    } else if (!strcasecmp(argv[0],\"parallel-syncs\") && argc == 3) {\n        /* parallel-syncs <name> <milliseconds> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->parallel_syncs = atoi(argv[2]);\n    } else if (!strcasecmp(argv[0],\"notification-script\") && argc == 3) {\n        /* notification-script <name> <path> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        if (access(argv[2],X_OK) == -1)\n            return \"Notification script seems non existing or non executable.\";\n        ri->notification_script = sdsnew(argv[2]);\n    } else if (!strcasecmp(argv[0],\"client-reconfig-script\") && argc == 3) {\n        /* client-reconfig-script <name> <path> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        if (access(argv[2],X_OK) == -1)\n            return \"Client reconfiguration script seems non existing or \"\n                   \"non executable.\";\n        ri->client_reconfig_script = sdsnew(argv[2]);\n    } else if (!strcasecmp(argv[0],\"auth-pass\") && argc == 3) {\n        /* auth-pass <name> <password> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->auth_pass = sdsnew(argv[2]);\n    } else if (!strcasecmp(argv[0],\"auth-user\") && argc == 3) {\n        /* auth-user <name> <username> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->auth_user = sdsnew(argv[2]);\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    } else if (!strcasecmp(argv[0],\"myid\") && argc == 2) {\n        if (strlen(argv[1]) != CONFIG_RUN_ID_SIZE)\n            return \"Malformed Sentinel id in myid option.\";\n        memcpy(sentinel.myid,argv[1],CONFIG_RUN_ID_SIZE);\n    } else if (!strcasecmp(argv[0],\"config-epoch\") && argc == 3) {\n        /* config-epoch <name> <epoch> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\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    } 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    } else if ((!strcasecmp(argv[0],\"known-slave\") ||\n                !strcasecmp(argv[0],\"known-replica\")) && argc == 4)\n    {\n        sentinelRedisInstance *slave;\n\n        /* known-replica <name> <ip> <port> */\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 replica.\";\n        }\n    } else if (!strcasecmp(argv[0],\"known-sentinel\") &&\n               (argc == 4 || argc == 5)) {\n        sentinelRedisInstance *si;\n\n        if (argc == 5) { /* Ignore the old form without runid. */\n            /* known-sentinel <name> <ip> <port> [runid] */\n            ri = sentinelGetMasterByName(argv[1]);\n            if (!ri) return \"No such master with specified name.\";\n            if ((si = createSentinelRedisInstance(argv[4],SRI_SENTINEL,argv[2],\n                        atoi(argv[3]), ri->quorum, ri)) == NULL)\n            {\n                return \"Wrong hostname or port for sentinel.\";\n            }\n            si->runid = sdsnew(argv[4]);\n            sentinelTryConnectionSharing(si);\n        }\n    } else if (!strcasecmp(argv[0],\"rename-command\") && argc == 4) {\n        /* rename-command <name> <command> <renamed-command> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        sds oldcmd = sdsnew(argv[2]);\n        sds newcmd = sdsnew(argv[3]);\n        if (dictAdd(ri->renamed_commands,oldcmd,newcmd) != DICT_OK) {\n            sdsfree(oldcmd);\n            sdsfree(newcmd);\n            return \"Same command renamed multiple times with rename-command.\";\n        }\n    } else if (!strcasecmp(argv[0],\"announce-ip\") && argc == 2) {\n        /* announce-ip <ip-address> */\n        if (strlen(argv[1]))\n            sentinel.announce_ip = sdsnew(argv[1]);\n    } else if (!strcasecmp(argv[0],\"announce-port\") && argc == 2) {\n        /* announce-port <port> */\n        sentinel.announce_port = atoi(argv[1]);\n    } else if (!strcasecmp(argv[0],\"deny-scripts-reconfig\") && argc == 2) {\n        /* deny-scripts-reconfig <yes|no> */\n        if ((sentinel.deny_scripts_reconfig = yesnotoi(argv[1])) == -1) {\n            return \"Please specify yes or no for the \"\n                   \"deny-scripts-reconfig options.\";\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. */\nvoid rewriteConfigSentinelOption(struct rewriteConfigState *state) {\n    dictIterator *di, *di2;\n    dictEntry *de;\n    sds line;\n\n    /* sentinel unique ID. */\n    line = sdscatprintf(sdsempty(), \"sentinel myid %s\", sentinel.myid);\n    rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n\n    /* sentinel deny-scripts-reconfig. */\n    line = sdscatprintf(sdsempty(), \"sentinel deny-scripts-reconfig %s\",\n        sentinel.deny_scripts_reconfig ? \"yes\" : \"no\");\n    rewriteConfigRewriteLine(state,\"sentinel\",line,\n        sentinel.deny_scripts_reconfig != SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG);\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 & auth-user */\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        if (master->auth_user) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel auth-user %s %s\",\n                master->name, master->auth_user);\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-replica %s %s %d\",\n                master->name, slave_addr->ip, slave_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            if (ri->runid == NULL) continue;\n            line = sdscatprintf(sdsempty(),\n                \"sentinel known-sentinel %s %s %d %s\",\n                master->name, ri->addr->ip, ri->addr->port, ri->runid);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n        dictReleaseIterator(di2);\n\n        /* sentinel rename-command */\n        di2 = dictGetIterator(master->renamed_commands);\n        while((de = dictNext(di2)) != NULL) {\n            sds oldname = dictGetKey(de);\n            sds newname = dictGetVal(de);\n            line = sdscatprintf(sdsempty(),\n                \"sentinel rename-command %s %s %s\",\n                master->name, oldname, newname);\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    /* sentinel announce-ip. */\n    if (sentinel.announce_ip) {\n        line = sdsnew(\"sentinel announce-ip \");\n        line = sdscatrepr(line, sentinel.announce_ip, sdslen(sentinel.announce_ip));\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n    }\n\n    /* sentinel announce-port. */\n    if (sentinel.announce_port) {\n        line = sdscatprintf(sdsempty(),\"sentinel announce-port %d\",\n                            sentinel.announce_port);\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n    }\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 * Before returning the function calls fsync() against the generated\n * configuration file to make sure changes are committed to disk.\n *\n * On failure the function logs a warning on the Redis log. */\nvoid sentinelFlushConfig(void) {\n    int fd = -1;\n    int saved_hz = server.hz;\n    int rewrite_status;\n\n    server.hz = CONFIG_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    serverLog(LL_WARNING,\"WARNING: Sentinel was not able to save the new configuration on disk!!!: %s\", strerror(errno));\n}\n\n/* ====================== hiredis connection handling ======================= */\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 * In case this Sentinel requires a password as well, via the \"requirepass\"\n * configuration directive, we assume we should use the local password in\n * order to authenticate when connecting with the other Sentinels as well.\n * So basically all the Sentinels share the same password and use it to\n * authenticate reciprocally.\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. */\nvoid sentinelSendAuthIfNeeded(sentinelRedisInstance *ri, redisAsyncContext *c) {\n    char *auth_pass = NULL;\n    char *auth_user = NULL;\n\n    if (ri->flags & SRI_MASTER) {\n        auth_pass = ri->auth_pass;\n        auth_user = ri->auth_user;\n    } else if (ri->flags & SRI_SLAVE) {\n        auth_pass = ri->master->auth_pass;\n        auth_user = ri->master->auth_user;\n    } else if (ri->flags & SRI_SENTINEL) {\n        auth_pass = server.requirepass;\n        auth_user = NULL;\n    }\n\n    if (auth_pass && auth_user == NULL) {\n        if (redisAsyncCommand(c, sentinelDiscardReplyCallback, ri, \"%s %s\",\n            sentinelInstanceMapCommand(ri,\"AUTH\"),\n            auth_pass) == C_OK) ri->link->pending_commands++;\n    } else if (auth_pass && auth_user) {\n        /* If we also have an username, use the ACL-style AUTH command\n         * with two arguments, username and password. */\n        if (redisAsyncCommand(c, sentinelDiscardReplyCallback, ri, \"%s %s %s\",\n            sentinelInstanceMapCommand(ri,\"AUTH\"),\n            auth_user, auth_pass) == C_OK) ri->link->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. */\nvoid sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char *type) {\n    char name[64];\n\n    snprintf(name,sizeof(name),\"sentinel-%.8s-%s\",sentinel.myid,type);\n    if (redisAsyncCommand(c, sentinelDiscardReplyCallback, ri,\n        \"%s SETNAME %s\",\n        sentinelInstanceMapCommand(ri,\"CLIENT\"),\n        name) == C_OK)\n    {\n        ri->link->pending_commands++;\n    }\n}\n\nstatic int instanceLinkNegotiateTLS(redisAsyncContext *context) {\n#ifndef USE_OPENSSL\n    (void) context;\n#else\n    if (!redis_tls_ctx) return C_ERR;\n    SSL *ssl = SSL_new(redis_tls_ctx);\n    if (!ssl) return C_ERR;\n\n    if (redisInitiateSSL(&context->c, ssl) == REDIS_ERR) return C_ERR;\n#endif\n    return C_OK;\n}\n\n/* Create the async connections for the instance link if the link\n * is disconnected. Note that link->disconnected is true even if just\n * one of the two links (commands and pub/sub) is missing. */\nvoid sentinelReconnectInstance(sentinelRedisInstance *ri) {\n    if (ri->link->disconnected == 0) return;\n    if (ri->addr->port == 0) return; /* port == 0 means invalid address. */\n    instanceLink *link = ri->link;\n    mstime_t now = mstime();\n\n    if (now - ri->link->last_reconn_time < SENTINEL_PING_PERIOD) return;\n    ri->link->last_reconn_time = now;\n\n    /* Commands connection. */\n    if (link->cc == NULL) {\n        link->cc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,NET_FIRST_BIND_ADDR);\n        if (!link->cc->err && server.tls_replication &&\n                (instanceLinkNegotiateTLS(link->cc) == C_ERR)) {\n            sentinelEvent(LL_DEBUG,\"-cmd-link-reconnection\",ri,\"%@ #Failed to initialize TLS\");\n            instanceLinkCloseConnection(link,link->cc);\n        } else if (link->cc->err) {\n            sentinelEvent(LL_DEBUG,\"-cmd-link-reconnection\",ri,\"%@ #%s\",\n                link->cc->errstr);\n            instanceLinkCloseConnection(link,link->cc);\n        } else {\n            link->pending_commands = 0;\n            link->cc_conn_time = mstime();\n            link->cc->data = link;\n            redisAeAttach(server.el,link->cc);\n            redisAsyncSetConnectCallback(link->cc,\n                    sentinelLinkEstablishedCallback);\n            redisAsyncSetDisconnectCallback(link->cc,\n                    sentinelDisconnectCallback);\n            sentinelSendAuthIfNeeded(ri,link->cc);\n            sentinelSetClientName(ri,link->cc,\"cmd\");\n\n            /* Send a PING ASAP when reconnecting. */\n            sentinelSendPing(ri);\n        }\n    }\n    /* Pub / Sub */\n    if ((ri->flags & (SRI_MASTER|SRI_SLAVE)) && link->pc == NULL) {\n        link->pc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,NET_FIRST_BIND_ADDR);\n        if (!link->pc->err && server.tls_replication &&\n                (instanceLinkNegotiateTLS(link->pc) == C_ERR)) {\n            sentinelEvent(LL_DEBUG,\"-pubsub-link-reconnection\",ri,\"%@ #Failed to initialize TLS\");\n        } else if (link->pc->err) {\n            sentinelEvent(LL_DEBUG,\"-pubsub-link-reconnection\",ri,\"%@ #%s\",\n                link->pc->errstr);\n            instanceLinkCloseConnection(link,link->pc);\n        } else {\n            int retval;\n\n            link->pc_conn_time = mstime();\n            link->pc->data = link;\n            redisAeAttach(server.el,link->pc);\n            redisAsyncSetConnectCallback(link->pc,\n                    sentinelLinkEstablishedCallback);\n            redisAsyncSetDisconnectCallback(link->pc,\n                    sentinelDisconnectCallback);\n            sentinelSendAuthIfNeeded(ri,link->pc);\n            sentinelSetClientName(ri,link->pc,\"pubsub\");\n            /* Now we subscribe to the Sentinels \"Hello\" channel. */\n            retval = redisAsyncCommand(link->pc,\n                sentinelReceiveHelloMessages, ri, \"%s %s\",\n                sentinelInstanceMapCommand(ri,\"SUBSCRIBE\"),\n                SENTINEL_HELLO_CHANNEL);\n            if (retval != C_OK) {\n                /* If we can't subscribe, the Pub/Sub connection is useless\n                 * and we can simply disconnect it and try again. */\n                instanceLinkCloseConnection(link,link->pc);\n                return;\n            }\n        }\n    }\n    /* Clear the disconnected status only if we have both the connections\n     * (or just the commands connection if this is a sentinel instance). */\n    if (link->cc && (ri->flags & SRI_SENTINEL || link->pc))\n        link->disconnected = 0;\n}\n\n/* ======================== Redis instances pinging  ======================== */\n\n/* Return true if master looks \"sane\", that is:\n * 1) It is actually a master in the current configuration.\n * 2) It reports itself as a master.\n * 3) It is not SDOWN or ODOWN.\n * 4) We obtained last INFO no more than two times the INFO period time ago. */\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. */\nvoid sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {\n    sds *lines;\n    int numlines, j;\n    int role = 0;\n\n    /* cache full INFO output for instance */\n    sdsfree(ri->info);\n    ri->info = sdsnew(info);\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    ri->master_link_down_time = 0;\n\n    /* Process line by line. */\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        if (sdslen(l) >= 47 && !memcmp(l,\"run_id:\",7)) {\n            if (ri->runid == NULL) {\n                ri->runid = sdsnewlen(l+7,40);\n            } else {\n                if (strncmp(ri->runid,l+7,40) != 0) {\n                    sentinelEvent(LL_NOTICE,\"+reboot\",ri,\"%@\");\n                    sdsfree(ri->runid);\n                    ri->runid = sdsnewlen(l+7,40);\n                }\n            }\n        }\n\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            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(LL_NOTICE,\"+slave\",slave,\"%@\");\n                    sentinelFlushConfig();\n                }\n            }\n        }\n\n        /* master_link_down_since_seconds:<seconds> */\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        if (!memcmp(l,\"role:master\",11)) role = SRI_MASTER;\n        else if (!memcmp(l,\"role:slave\",10)) role = SRI_SLAVE;\n\n        if (role == SRI_SLAVE) {\n            /* master_host:<host> */\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            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            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            if (sdslen(l) >= 15 && !memcmp(l,\"slave_priority:\",15))\n                ri->slave_priority = atoi(l+15);\n\n            /* slave_repl_offset:<offset> */\n            if (sdslen(l) >= 18 && !memcmp(l,\"slave_repl_offset:\",18))\n                ri->slave_repl_offset = strtoull(l+18,NULL,10);\n        }\n    }\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    /* 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(LL_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    if (sentinel.tilt) return;\n\n    /* Handle master -> slave role switch. */\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    }\n\n    /* Handle slave -> master role switch. */\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        if ((ri->flags & SRI_PROMOTED) &&\n            (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            ri->master->config_epoch = ri->master->failover_epoch;\n            ri->master->failover_state = SENTINEL_FAILOVER_STATE_RECONF_SLAVES;\n            ri->master->failover_state_change_time = mstime();\n            sentinelFlushConfig();\n            sentinelEvent(LL_WARNING,\"+promoted-slave\",ri,\"%@\");\n            if (sentinel.simfailure_flags &\n                SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION)\n                sentinelSimFailureCrash();\n            sentinelEvent(LL_WARNING,\"+failover-state-reconf-slaves\",\n                ri->master,\"%@\");\n            sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER,\n                \"start\",ri->master->addr,ri->addr);\n            sentinelForceHelloUpdateForMaster(ri->master);\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            mstime_t wait_time = SENTINEL_PUBLISH_PERIOD*4;\n\n            if (!(ri->flags & SRI_PROMOTED) &&\n                 sentinelMasterLooksSane(ri->master) &&\n                 sentinelRedisInstanceNoDownFor(ri,wait_time) &&\n                 mstime() - ri->role_reported_time > wait_time)\n            {\n                int retval = sentinelSendSlaveOf(ri,\n                        ri->master->addr->ip,\n                        ri->master->addr->port);\n                if (retval == C_OK)\n                    sentinelEvent(LL_NOTICE,\"+convert-to-slave\",ri,\"%@\");\n            }\n        }\n    }\n\n    /* Handle slaves replicating to a different master address. */\n    if ((ri->flags & SRI_SLAVE) &&\n        role == SRI_SLAVE &&\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        if (sentinelMasterLooksSane(ri->master) &&\n            sentinelRedisInstanceNoDownFor(ri,wait_time) &&\n            mstime() - ri->slave_conf_change_time > wait_time)\n        {\n            int retval = sentinelSendSlaveOf(ri,\n                    ri->master->addr->ip,\n                    ri->master->addr->port);\n            if (retval == C_OK)\n                sentinelEvent(LL_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    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        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(LL_NOTICE,\"+slave-reconf-inprog\",ri,\"%@\");\n        }\n\n        /* SRI_RECONF_INPROG -> SRI_RECONF_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(LL_NOTICE,\"+slave-reconf-done\",ri,\"%@\");\n        }\n    }\n}\n\nvoid sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    if (r->type == REDIS_REPLY_STRING)\n        sentinelRefreshInstanceInfo(ri,r->str);\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. */\nvoid sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    instanceLink *link = c->data;\n    UNUSED(reply);\n    UNUSED(privdata);\n\n    if (link) link->pending_commands--;\n}\n\nvoid sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    if (r->type == REDIS_REPLY_STATUS ||\n        r->type == REDIS_REPLY_ERROR) {\n        /* Update the \"instance available\" field only if this is an\n         * acceptable reply. */\n        if (strncmp(r->str,\"PONG\",4) == 0 ||\n            strncmp(r->str,\"LOADING\",7) == 0 ||\n            strncmp(r->str,\"MASTERDOWN\",10) == 0)\n        {\n            link->last_avail_time = mstime();\n            link->act_ping_time = 0; /* Flag the pong as received. */\n        } else {\n            /* Send a SCRIPT KILL command if the instance appears to be\n             * down because of a busy script. */\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->link->cc,\n                        sentinelDiscardReplyCallback, ri,\n                        \"%s KILL\",\n                        sentinelInstanceMapCommand(ri,\"SCRIPT\")) == C_OK)\n                {\n                    ri->link->pending_commands++;\n                }\n                ri->flags |= SRI_SCRIPT_KILL_SENT;\n            }\n        }\n    }\n    link->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. */\nvoid sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    /* Only update pub_time if we actually published our message. Otherwise\n     * we'll retry again in 100 milliseconds. */\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 * If the master name specified in the message is not known, the message is\n * discarded. */\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        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        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            /* If not, remove all the sentinels that have the same runid\n             * because there was an address change, and add the same Sentinel\n             * with the new address back. */\n            removed = removeMatchingSentinelFromMaster(master,token[2]);\n            if (removed) {\n                sentinelEvent(LL_NOTICE,\"+sentinel-address-switch\",master,\n                    \"%@ ip %s port %d for %s\", token[0],port,token[2]);\n            } else {\n                /* Check if there is another Sentinel with the same address this\n                 * new one is reporting. What we do if this happens is to set its\n                 * port to 0, to signal the address is invalid. We'll update it\n                 * later if we get an HELLO message. */\n                sentinelRedisInstance *other =\n                    getSentinelRedisInstanceByAddrAndRunID(\n                        master->sentinels, token[0],port,NULL);\n                if (other) {\n                    sentinelEvent(LL_NOTICE,\"+sentinel-invalid-addr\",other,\"%@\");\n                    other->addr->port = 0; /* It means: invalid address. */\n                    sentinelUpdateSentinelAddressInAllMasters(other);\n                }\n            }\n\n            /* Add the new sentinel. */\n            si = createSentinelRedisInstance(token[2],SRI_SENTINEL,\n                            token[0],port,master->quorum,master);\n\n            if (si) {\n                if (!removed) sentinelEvent(LL_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                sentinelTryConnectionSharing(si);\n                if (removed) sentinelUpdateSentinelAddressInAllMasters(si);\n                sentinelFlushConfig();\n            }\n        }\n\n        /* Update local current_epoch if received current_epoch is greater.*/\n        if (current_epoch > sentinel.current_epoch) {\n            sentinel.current_epoch = current_epoch;\n            sentinelFlushConfig();\n            sentinelEvent(LL_WARNING,\"+new-epoch\",master,\"%llu\",\n                (unsigned long long) sentinel.current_epoch);\n        }\n\n        /* Update master info if received configuration is newer. */\n        if (si && 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(LL_WARNING,\"+config-update-from\",si,\"%@\");\n                sentinelEvent(LL_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        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. */\nvoid sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    redisReply *r;\n    UNUSED(c);\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    ri->link->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    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    if (strstr(r->element[2]->str,sentinel.myid) != 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 configuration for this\n * master, and to advertise the existence of this Sentinel at the same time.\n *\n * The message has the following format:\n *\n * sentinel_ip,sentinel_port,sentinel_runid,current_epoch,\n * master_name,master_ip,master_port,master_config_epoch.\n *\n * Returns C_OK if the PUBLISH was queued correctly, otherwise\n * C_ERR is returned. */\nint sentinelSendHello(sentinelRedisInstance *ri) {\n    char ip[NET_IP_STR_LEN];\n    char payload[NET_IP_STR_LEN+1024];\n    int retval;\n    char *announce_ip;\n    int announce_port;\n    sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master;\n    sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master);\n\n    if (ri->link->disconnected) return C_ERR;\n\n    /* Use the specified announce address if specified, otherwise try to\n     * obtain our own IP address. */\n    if (sentinel.announce_ip) {\n        announce_ip = sentinel.announce_ip;\n    } else {\n        if (anetSockName(ri->link->cc->c.fd,ip,sizeof(ip),NULL) == -1)\n            return C_ERR;\n        announce_ip = ip;\n    }\n    if (sentinel.announce_port) announce_port = sentinel.announce_port;\n    else if (server.tls_replication && server.tls_port) announce_port = server.tls_port;\n    else announce_port = server.port;\n\n    /* Format and send the Hello message. */\n    snprintf(payload,sizeof(payload),\n        \"%s,%d,%s,%llu,\" /* Info about this sentinel. */\n        \"%s,%s,%d,%llu\", /* Info about current master. */\n        announce_ip, announce_port, sentinel.myid,\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    retval = redisAsyncCommand(ri->link->cc,\n        sentinelPublishReplyCallback, ri, \"%s %s %s\",\n        sentinelInstanceMapCommand(ri,\"PUBLISH\"),\n        SENTINEL_HELLO_CHANNEL,payload);\n    if (retval != C_OK) return C_ERR;\n    ri->link->pending_commands++;\n    return C_OK;\n}\n\n/* Reset last_pub_time in all the instances in the specified dictionary\n * in order to force the delivery of an Hello update ASAP. */\nvoid sentinelForceHelloUpdateDictOfRedisInstances(dict *instances) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        if (ri->last_pub_time >= (SENTINEL_PUBLISH_PERIOD+1))\n            ri->last_pub_time -= (SENTINEL_PUBLISH_PERIOD+1);\n    }\n    dictReleaseIterator(di);\n}\n\n/* This function forces the delivery of an \"Hello\" message (see\n * sentinelSendHello() top comment for further information) to all the Redis\n * and Sentinel instances related to the specified 'master'.\n *\n * It is technically not needed since we send an update to every instance\n * with a period of SENTINEL_PUBLISH_PERIOD milliseconds, however when a\n * Sentinel upgrades a configuration it is a good idea to deliever an update\n * to the other Sentinels ASAP. */\nint sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master) {\n    if (!(master->flags & SRI_MASTER)) return C_ERR;\n    if (master->last_pub_time >= (SENTINEL_PUBLISH_PERIOD+1))\n        master->last_pub_time -= (SENTINEL_PUBLISH_PERIOD+1);\n    sentinelForceHelloUpdateDictOfRedisInstances(master->sentinels);\n    sentinelForceHelloUpdateDictOfRedisInstances(master->slaves);\n    return C_OK;\n}\n\n/* Send a PING to the specified instance and refresh the act_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. */\nint sentinelSendPing(sentinelRedisInstance *ri) {\n    int retval = redisAsyncCommand(ri->link->cc,\n        sentinelPingReplyCallback, ri, \"%s\",\n        sentinelInstanceMapCommand(ri,\"PING\"));\n    if (retval == C_OK) {\n        ri->link->pending_commands++;\n        ri->link->last_ping_time = mstime();\n        /* We update the active ping time only if we received the pong for\n         * the previous ping, otherwise we are technically waiting since the\n         * first ping that did not receive a reply. */\n        if (ri->link->act_ping_time == 0)\n            ri->link->act_ping_time = ri->link->last_ping_time;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\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    if (ri->link->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    if (ri->link->pending_commands >=\n        SENTINEL_MAX_PENDING_COMMANDS * ri->link->refcount) 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     *\n     * Similarly we monitor the INFO output more often if the slave reports\n     * to be disconnected from the master, so that we can have a fresh\n     * disconnection time figure. */\n    if ((ri->flags & SRI_SLAVE) &&\n        ((ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS)) ||\n         (ri->master_link_down_time != 0)))\n    {\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    /* Send INFO to masters and slaves, not sentinels. */\n    if ((ri->flags & SRI_SENTINEL) == 0 &&\n        (ri->info_refresh == 0 ||\n        (now - ri->info_refresh) > info_period))\n    {\n        retval = redisAsyncCommand(ri->link->cc,\n            sentinelInfoReplyCallback, ri, \"%s\",\n            sentinelInstanceMapCommand(ri,\"INFO\"));\n        if (retval == C_OK) ri->link->pending_commands++;\n    }\n\n    /* Send PING to all the three kinds of instances. */\n    if ((now - ri->link->last_pong_time) > ping_period &&\n               (now - ri->link->last_ping_time) > ping_period/2) {\n        sentinelSendPing(ri);\n    }\n\n    /* PUBLISH hello messages to all the three kinds of instances. */\n    if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) {\n        sentinelSendHello(ri);\n    }\n}\n\n/* =========================== SENTINEL command ============================= */\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. */\nvoid addReplySentinelRedisInstance(client *c, sentinelRedisInstance *ri) {\n    char *flags = sdsempty();\n    void *mbl;\n    int fields = 0;\n\n    mbl = addReplyDeferredLen(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->link->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,\"link-pending-commands\");\n    addReplyBulkLongLong(c,ri->link->pending_commands);\n    fields++;\n\n    addReplyBulkCString(c,\"link-refcount\");\n    addReplyBulkLongLong(c,ri->link->refcount);\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->link->act_ping_time ? (mstime() - ri->link->act_ping_time) : 0);\n    fields++;\n\n    addReplyBulkCString(c,\"last-ok-ping-reply\");\n    addReplyBulkLongLong(c,mstime() - ri->link->last_avail_time);\n    fields++;\n\n    addReplyBulkCString(c,\"last-ping-reply\");\n    addReplyBulkLongLong(c,mstime() - ri->link->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    setDeferredMapLen(c,mbl,fields);\n}\n\n/* Output a number of instances contained inside a dictionary as\n * Redis protocol. */\nvoid addReplyDictOfRedisInstances(client *c, dict *instances) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(instances);\n    addReplyArrayLen(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. */\nsentinelRedisInstance *sentinelGetMasterByNameOrReplyError(client *c,\n                        robj *name)\n{\n    sentinelRedisInstance *ri;\n\n    ri = dictFetchValue(sentinel.masters,name->ptr);\n    if (!ri) {\n        addReplyError(c,\"No such master with that name\");\n        return NULL;\n    }\n    return ri;\n}\n\n#define SENTINEL_ISQR_OK 0\n#define SENTINEL_ISQR_NOQUORUM (1<<0)\n#define SENTINEL_ISQR_NOAUTH (1<<1)\nint sentinelIsQuorumReachable(sentinelRedisInstance *master, int *usableptr) {\n    dictIterator *di;\n    dictEntry *de;\n    int usable = 1; /* Number of usable Sentinels. Init to 1 to count myself. */\n    int result = SENTINEL_ISQR_OK;\n    int voters = dictSize(master->sentinels)+1; /* Known Sentinels + myself. */\n\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (ri->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;\n        usable++;\n    }\n    dictReleaseIterator(di);\n\n    if (usable < (int)master->quorum) result |= SENTINEL_ISQR_NOQUORUM;\n    if (usable < voters/2+1) result |= SENTINEL_ISQR_NOAUTH;\n    if (usableptr) *usableptr = usable;\n    return result;\n}\n\nvoid sentinelCommand(client *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               !strcasecmp(c->argv[1]->ptr,\"replicas\"))\n    {\n        /* SENTINEL REPLICAS <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         *\n         * Arguments:\n         *\n         * ip and port are the ip and port of the master we want to be\n         * checked by Sentinel. Note that the command will not check by\n         * name but just by master, in theory different Sentinels may monitor\n         * differnet masters with the same name.\n         *\n         * current-epoch is needed in order to understand if we are allowed\n         * to vote for a failover leader or not. Each Sentinel can vote just\n         * one time per epoch.\n         *\n         * runid is \"*\" if we are not seeking for a vote from the Sentinel\n         * in order to elect the failover leader. Otherwise it is set to the\n         * runid we want the Sentinel to vote if it did not already voted.\n         */\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) != C_OK ||\n            getLongLongFromObjectOrReply(c,c->argv[4],&req_epoch,NULL)\n                                                              != C_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        addReplyArrayLen(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            addReplyNullArray(c);\n        } else {\n            sentinelAddr *addr = sentinelGetCurrentMasterAddress(ri);\n\n            addReplyArrayLen(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 replica to promote\\r\\n\"));\n            return;\n        }\n        serverLog(LL_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 ip[NET_IP_STR_LEN];\n\n        if (c->argc != 6) goto numargserr;\n        if (getLongFromObjectOrReply(c,c->argv[5],&quorum,\"Invalid quorum\")\n            != C_OK) return;\n        if (getLongFromObjectOrReply(c,c->argv[4],&port,\"Invalid port\")\n            != C_OK) return;\n\n        if (quorum <= 0) {\n            addReplyError(c, \"Quorum must be 1 or greater.\");\n            return;\n        }\n\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,ip,sizeof(ip)) == 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(LL_WARNING,\"+monitor\",ri,\"%@ quorum %d\",ri->quorum);\n            addReply(c,shared.ok);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"flushconfig\")) {\n        if (c->argc != 2) goto numargserr;\n        sentinelFlushConfig();\n        addReply(c,shared.ok);\n        return;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"remove\")) {\n        /* SENTINEL REMOVE <name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n            == NULL) return;\n        sentinelEvent(LL_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,\"ckquorum\")) {\n        /* SENTINEL CKQUORUM <name> */\n        sentinelRedisInstance *ri;\n        int usable;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n            == NULL) return;\n        int result = sentinelIsQuorumReachable(ri,&usable);\n        if (result == SENTINEL_ISQR_OK) {\n            addReplySds(c, sdscatfmt(sdsempty(),\n                \"+OK %i usable Sentinels. Quorum and failover authorization \"\n                \"can be reached\\r\\n\",usable));\n        } else {\n            sds e = sdscatfmt(sdsempty(),\n                \"-NOQUORUM %i usable Sentinels. \",usable);\n            if (result & SENTINEL_ISQR_NOQUORUM)\n                e = sdscat(e,\"Not enough available Sentinels to reach the\"\n                             \" specified quorum for this master\");\n            if (result & SENTINEL_ISQR_NOAUTH) {\n                if (result & SENTINEL_ISQR_NOQUORUM) e = sdscat(e,\". \");\n                e = sdscat(e, \"Not enough available Sentinels to reach the\"\n                              \" majority and authorize a failover\");\n            }\n            e = sdscat(e,\"\\r\\n\");\n            addReplySds(c,e);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set\")) {\n        if (c->argc < 3) goto numargserr;\n        sentinelSetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"info-cache\")) {\n        /* SENTINEL INFO-CACHE <name> */\n        if (c->argc < 2) goto numargserr;\n        mstime_t now = mstime();\n\n        /* Create an ad-hoc dictionary type so that we can iterate\n         * a dictionary composed of just the master groups the user\n         * requested. */\n        dictType copy_keeper = instancesDictType;\n        copy_keeper.valDestructor = NULL;\n        dict *masters_local = sentinel.masters;\n        if (c->argc > 2) {\n            masters_local = dictCreate(&copy_keeper, NULL);\n\n            for (int i = 2; i < c->argc; i++) {\n                sentinelRedisInstance *ri;\n                ri = sentinelGetMasterByName(c->argv[i]->ptr);\n                if (!ri) continue; /* ignore non-existing names */\n                dictAdd(masters_local, ri->name, ri);\n            }\n        }\n\n        /* Reply format:\n         *   1.) master name\n         *   2.) 1.) info from master\n         *       2.) info from replica\n         *       ...\n         *   3.) other master name\n         *   ...\n         */\n        addReplyArrayLen(c,dictSize(masters_local) * 2);\n\n        dictIterator  *di;\n        dictEntry *de;\n        di = dictGetIterator(masters_local);\n        while ((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n            addReplyBulkCBuffer(c,ri->name,strlen(ri->name));\n            addReplyArrayLen(c,dictSize(ri->slaves) + 1); /* +1 for self */\n            addReplyArrayLen(c,2);\n            addReplyLongLong(c, now - ri->info_refresh);\n            if (ri->info)\n                addReplyBulkCBuffer(c,ri->info,sdslen(ri->info));\n            else\n                addReplyNull(c);\n\n            dictIterator *sdi;\n            dictEntry *sde;\n            sdi = dictGetIterator(ri->slaves);\n            while ((sde = dictNext(sdi)) != NULL) {\n                sentinelRedisInstance *sri = dictGetVal(sde);\n                addReplyArrayLen(c,2);\n                addReplyLongLong(c, now - sri->info_refresh);\n                if (sri->info)\n                    addReplyBulkCBuffer(c,sri->info,sdslen(sri->info));\n                else\n                    addReplyNull(c);\n            }\n            dictReleaseIterator(sdi);\n        }\n        dictReleaseIterator(di);\n        if (masters_local != sentinel.masters) dictRelease(masters_local);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"simulate-failure\")) {\n        /* SENTINEL SIMULATE-FAILURE <flag> <flag> ... <flag> */\n        int j;\n\n        sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;\n        for (j = 2; j < c->argc; j++) {\n            if (!strcasecmp(c->argv[j]->ptr,\"crash-after-election\")) {\n                sentinel.simfailure_flags |=\n                    SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION;\n                serverLog(LL_WARNING,\"Failure simulation: this Sentinel \"\n                    \"will crash after being successfully elected as failover \"\n                    \"leader\");\n            } else if (!strcasecmp(c->argv[j]->ptr,\"crash-after-promotion\")) {\n                sentinel.simfailure_flags |=\n                    SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION;\n                serverLog(LL_WARNING,\"Failure simulation: this Sentinel \"\n                    \"will crash after promoting the selected replica to master\");\n            } else if (!strcasecmp(c->argv[j]->ptr,\"help\")) {\n                addReplyArrayLen(c,2);\n                addReplyBulkCString(c,\"crash-after-election\");\n                addReplyBulkCString(c,\"crash-after-promotion\");\n            } else {\n                addReplyError(c,\"Unknown failure simulation specified\");\n                return;\n            }\n        }\n        addReply(c,shared.ok);\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#define info_section_from_redis(section_name) do { \\\n    if (defsections || allsections || !strcasecmp(section,section_name)) { \\\n        sds redissection; \\\n        if (sections++) info = sdscat(info,\"\\r\\n\"); \\\n        redissection = genRedisInfoString(section_name); \\\n        info = sdscatlen(info,redissection,sdslen(redissection)); \\\n        sdsfree(redissection); \\\n    } \\\n} while(0)\n\n/* SENTINEL INFO [section] */\nvoid sentinelInfoCommand(client *c) {\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    int defsections = 0, allsections = 0;\n    char *section = c->argc == 2 ? c->argv[1]->ptr : NULL;\n    if (section) {\n        allsections = !strcasecmp(section,\"all\");\n        defsections = !strcasecmp(section,\"default\");\n    } else {\n        defsections = 1;\n    }\n\n    int sections = 0;\n    sds info = sdsempty();\n\n    info_section_from_redis(\"server\");\n    info_section_from_redis(\"clients\");\n    info_section_from_redis(\"cpu\");\n    info_section_from_redis(\"stats\");\n\n    if (defsections || allsections || !strcasecmp(section,\"sentinel\")) {\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            \"sentinel_simulate_failure_flags:%lu\\r\\n\",\n            dictSize(sentinel.masters),\n            sentinel.tilt,\n            sentinel.running_scripts,\n            listLength(sentinel.scripts_queue),\n            sentinel.simfailure_flags);\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    addReplyBulkSds(c, info);\n}\n\n/* Implements Sentinel version of the ROLE command. The output is\n * \"sentinel\" and the list of currently monitored master names. */\nvoid sentinelRoleCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    addReplyArrayLen(c,2);\n    addReplyBulkCBuffer(c,\"sentinel\",8);\n    addReplyArrayLen(c,dictSize(sentinel.masters));\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        addReplyBulkCString(c,ri->name);\n    }\n    dictReleaseIterator(di);\n}\n\n/* SENTINEL SET <mastername> [<option> <value> ...] */\nvoid sentinelSetCommand(client *c) {\n    sentinelRedisInstance *ri;\n    int j, changes = 0;\n    int badarg = 0; /* Bad argument position for error reporting. */\n    char *option;\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++) {\n        int moreargs = (c->argc-1) - j;\n        option = c->argv[j]->ptr;\n        long long ll;\n        int old_j = j; /* Used to know what to log as an event. */\n\n        if (!strcasecmp(option,\"down-after-milliseconds\") && moreargs > 0) {\n            /* down-after-millisecodns <milliseconds> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->down_after_period = ll;\n            sentinelPropagateDownAfterPeriod(ri);\n            changes++;\n        } else if (!strcasecmp(option,\"failover-timeout\") && moreargs > 0) {\n            /* failover-timeout <milliseconds> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->failover_timeout = ll;\n            changes++;\n        } else if (!strcasecmp(option,\"parallel-syncs\") && moreargs > 0) {\n            /* parallel-syncs <milliseconds> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->parallel_syncs = ll;\n            changes++;\n        } else if (!strcasecmp(option,\"notification-script\") && moreargs > 0) {\n            /* notification-script <path> */\n            char *value = c->argv[++j]->ptr;\n            if (sentinel.deny_scripts_reconfig) {\n                addReplyError(c,\n                    \"Reconfiguration of scripts path is denied for \"\n                    \"security reasons. Check the deny-scripts-reconfig \"\n                    \"configuration directive in your Sentinel configuration\");\n                return;\n            }\n\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\") && moreargs > 0) {\n            /* client-reconfig-script <path> */\n            char *value = c->argv[++j]->ptr;\n            if (sentinel.deny_scripts_reconfig) {\n                addReplyError(c,\n                    \"Reconfiguration of scripts path is denied for \"\n                    \"security reasons. Check the deny-scripts-reconfig \"\n                    \"configuration directive in your Sentinel configuration\");\n                return;\n            }\n\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\") && moreargs > 0) {\n            /* auth-pass <password> */\n            char *value = c->argv[++j]->ptr;\n            sdsfree(ri->auth_pass);\n            ri->auth_pass = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n        } else if (!strcasecmp(option,\"auth-user\") && moreargs > 0) {\n            /* auth-user <username> */\n            char *value = c->argv[++j]->ptr;\n            sdsfree(ri->auth_user);\n            ri->auth_user = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n        } else if (!strcasecmp(option,\"quorum\") && moreargs > 0) {\n            /* quorum <count> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->quorum = ll;\n            changes++;\n        } else if (!strcasecmp(option,\"rename-command\") && moreargs > 1) {\n            /* rename-command <oldname> <newname> */\n            sds oldname = c->argv[++j]->ptr;\n            sds newname = c->argv[++j]->ptr;\n\n            if ((sdslen(oldname) == 0) || (sdslen(newname) == 0)) {\n                badarg = sdslen(newname) ? j-1 : j;\n                goto badfmt;\n            }\n\n            /* Remove any older renaming for this command. */\n            dictDelete(ri->renamed_commands,oldname);\n\n            /* If the target name is the same as the source name there\n             * is no need to add an entry mapping to itself. */\n            if (!dictSdsKeyCaseCompare(NULL,oldname,newname)) {\n                oldname = sdsdup(oldname);\n                newname = sdsdup(newname);\n                dictAdd(ri->renamed_commands,oldname,newname);\n            }\n            changes++;\n        } else {\n            addReplyErrorFormat(c,\"Unknown option or number of arguments for \"\n                                  \"SENTINEL SET '%s'\", option);\n            if (changes) sentinelFlushConfig();\n            return;\n        }\n\n        /* Log the event. */\n        int numargs = j-old_j+1;\n        switch(numargs) {\n        case 2:\n            sentinelEvent(LL_WARNING,\"+set\",ri,\"%@ %s %s\",c->argv[old_j]->ptr,\n                                                          c->argv[old_j+1]->ptr);\n            break;\n        case 3:\n            sentinelEvent(LL_WARNING,\"+set\",ri,\"%@ %s %s %s\",c->argv[old_j]->ptr,\n                                                             c->argv[old_j+1]->ptr,\n                                                             c->argv[old_j+2]->ptr);\n            break;\n        default:\n            sentinelEvent(LL_WARNING,\"+set\",ri,\"%@ %s\",c->argv[old_j]->ptr);\n            break;\n        }\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        (char*)c->argv[badarg]->ptr,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(client *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? */\nvoid sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {\n    mstime_t elapsed = 0;\n\n    if (ri->link->act_ping_time)\n        elapsed = mstime() - ri->link->act_ping_time;\n    else if (ri->link->disconnected)\n        elapsed = mstime() - ri->link->last_avail_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     * 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    if (ri->link->cc &&\n        (mstime() - ri->link->cc_conn_time) >\n        SENTINEL_MIN_LINK_RECONNECT_PERIOD &&\n        ri->link->act_ping_time != 0 && /* There is a pending ping... */\n        /* The pending ping is delayed, and we did not receive\n         * error replies as well. */\n        (mstime() - ri->link->act_ping_time) > (ri->down_after_period/2) &&\n        (mstime() - ri->link->last_pong_time) > (ri->down_after_period/2))\n    {\n        instanceLinkCloseConnection(ri->link,ri->link->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    if (ri->link->pc &&\n        (mstime() - ri->link->pc_conn_time) >\n         SENTINEL_MIN_LINK_RECONNECT_PERIOD &&\n        (mstime() - ri->link->pc_last_activity) > (SENTINEL_PUBLISH_PERIOD*3))\n    {\n        instanceLinkCloseConnection(ri->link,ri->link->pc);\n    }\n\n    /* Update the SDOWN flag. We believe the instance is SDOWN if:\n     *\n     * 1) It is not replying.\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    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            sentinelEvent(LL_WARNING,\"+sdown\",ri,\"%@\");\n            ri->s_down_since_time = mstime();\n            ri->flags |= SRI_S_DOWN;\n        }\n    } else {\n        /* Is subjectively up */\n        if (ri->flags & SRI_S_DOWN) {\n            sentinelEvent(LL_WARNING,\"-sdown\",ri,\"%@\");\n            ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT);\n        }\n    }\n}\n\n/* Is this instance down according to the configured quorum?\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 * However messages can be delayed so there are no strong guarantees about\n * N instances agreeing at the same time about the down state. */\nvoid sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    unsigned int quorum = 0, odown = 0;\n\n    if (master->flags & SRI_S_DOWN) {\n        /* Is down for enough sentinels? */\n        quorum = 1; /* the current sentinel. */\n        /* Count all the other sentinels. */\n        di = dictGetIterator(master->sentinels);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n\n            if (ri->flags & SRI_MASTER_DOWN) quorum++;\n        }\n        dictReleaseIterator(di);\n        if (quorum >= master->quorum) odown = 1;\n    }\n\n    /* Set the flag accordingly to the outcome. */\n    if (odown) {\n        if ((master->flags & SRI_O_DOWN) == 0) {\n            sentinelEvent(LL_WARNING,\"+odown\",master,\"%@ #quorum %d/%d\",\n                quorum, master->quorum);\n            master->flags |= SRI_O_DOWN;\n            master->o_down_since_time = mstime();\n        }\n    } else {\n        if (master->flags & SRI_O_DOWN) {\n            sentinelEvent(LL_WARNING,\"-odown\",master,\"%@\");\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. */\nvoid sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    /* Ignore every error or unexpected reply.\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        ri->last_master_down_reply_time = mstime();\n        if (r->element[0]->integer == 1) {\n            ri->flags |= SRI_MASTER_DOWN;\n        } else {\n            ri->flags &= ~SRI_MASTER_DOWN;\n        }\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            if ((long long)ri->leader_epoch != r->element[2]->integer)\n                serverLog(LL_WARNING,\n                    \"%s voted for %s %llu\", ri->name,\n                    r->element[1]->str,\n                    (unsigned long long) r->element[2]->integer);\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#define SENTINEL_ASK_FORCED (1<<0)\nvoid sentinelAskMasterStateToOtherSentinels(sentinelRedisInstance *master, int flags) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        mstime_t elapsed = mstime() - ri->last_master_down_reply_time;\n        char port[32];\n        int retval;\n\n        /* If the master state from other sentinel is too old, we clear it. */\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         * 1) We believe it is down, or there is a failover in progress.\n         * 2) Sentinel is connected.\n         * 3) We did not receive the info within SENTINEL_ASK_PERIOD ms. */\n        if ((master->flags & SRI_S_DOWN) == 0) continue;\n        if (ri->link->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        ll2string(port,sizeof(port),master->addr->port);\n        retval = redisAsyncCommand(ri->link->cc,\n                    sentinelReceiveIsMasterDownReply, ri,\n                    \"%s is-master-down-by-addr %s %s %llu %s\",\n                    sentinelInstanceMapCommand(ri,\"SENTINEL\"),\n                    master->addr->ip, port,\n                    sentinel.current_epoch,\n                    (master->failover_state > SENTINEL_FAILOVER_STATE_NONE) ?\n                    sentinel.myid : \"*\");\n        if (retval == C_OK) ri->link->pending_commands++;\n    }\n    dictReleaseIterator(di);\n}\n\n/* =============================== FAILOVER ================================= */\n\n/* Crash because of user request via SENTINEL simulate-failure command. */\nvoid sentinelSimFailureCrash(void) {\n    serverLog(LL_WARNING,\n        \"Sentinel CRASH because of SENTINEL simulate-failure\");\n    exit(99);\n}\n\n/* Vote for the sentinel with 'req_runid' or return the old vote if already\n * voted for the specified 'req_epoch' or one greater.\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. */\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(LL_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(LL_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,sentinel.myid))\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\nstruct sentinelLeader {\n    char *runid;\n    unsigned long votes;\n};\n\n/* Helper function for sentinelGetLeader, increment the counter\n * relative to the specified runid. */\nint sentinelLeaderIncr(dict *counters, char *runid) {\n    dictEntry *existing, *de;\n    uint64_t oldval;\n\n    de = dictAddRaw(counters,runid,&existing);\n    if (existing) {\n        oldval = dictGetUnsignedIntegerVal(existing);\n        dictSetUnsignedIntegerVal(existing,oldval+1);\n        return oldval+1;\n    } else {\n        serverAssert(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 * To be a leader for a given epoch, we should have the majority of\n * the Sentinels we know (ever seen since the last SENTINEL RESET) that\n * reported the same instance as leader for the same 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    serverAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS));\n    counters = dictCreate(&leaderVotesDictType,NULL);\n\n    voters = dictSize(master->sentinels)+1; /* All the other sentinels and me.*/\n\n    /* Count other sentinels votes */\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        if (ri->leader != NULL && ri->leader_epoch == sentinel.current_epoch)\n            sentinelLeaderIncr(counters,ri->leader);\n    }\n    dictReleaseIterator(di);\n\n    /* Check what's the winner. For the winner to win, it needs two conditions:\n     * 1) Absolute majority between voters (50% + 1).\n     * 2) And anyway at least master->quorum votes. */\n    di = dictGetIterator(counters);\n    while((de = dictNext(di)) != NULL) {\n        uint64_t votes = dictGetUnsignedIntegerVal(de);\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    if (winner)\n        myvote = sentinelVoteLeader(master,epoch,winner,&leader_epoch);\n    else\n        myvote = sentinelVoteLeader(master,epoch,sentinel.myid,&leader_epoch);\n\n    if (myvote && leader_epoch == epoch) {\n        uint64_t votes = sentinelLeaderIncr(counters,myvote);\n\n        if (votes > max_votes) {\n            max_votes = votes;\n            winner = myvote;\n        }\n    }\n\n    voters_quorum = voters/2+1;\n    if (winner && (max_votes < voters_quorum || max_votes < master->quorum))\n        winner = NULL;\n\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 * If Host is NULL the function sends \"SLAVEOF NO ONE\".\n *\n * The command returns C_OK if the SLAVEOF command was accepted for\n * (later) delivery otherwise C_ERR. The command replies are just\n * discarded. */\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 is NULL we send SLAVEOF NO ONE that will turn the instance\n     * into a master. */\n    if (host == NULL) {\n        host = \"NO\";\n        memcpy(portstr,\"ONE\",4);\n    }\n\n    /* In order to send SLAVEOF in a safe way, we send a transaction performing\n     * the following tasks:\n     * 1) Reconfigure the instance according to the specified host/port params.\n     * 2) Rewrite the configuration.\n     * 3) Disconnect all clients (but this one sending the commnad) in order\n     *    to trigger the ask-master-on-reconnection protocol for connected\n     *    clients.\n     *\n     * Note that we don't check the replies returned by commands, since we\n     * will observe instead the effects in the next INFO output. */\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s\",\n        sentinelInstanceMapCommand(ri,\"MULTI\"));\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s %s %s\",\n        sentinelInstanceMapCommand(ri,\"SLAVEOF\"),\n        host, portstr);\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s REWRITE\",\n        sentinelInstanceMapCommand(ri,\"CONFIG\"));\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    /* CLIENT KILL TYPE <type> is only supported starting from Redis 2.8.12,\n     * however sending it to an instance not understanding this command is not\n     * an issue because CLIENT is variadic command, so Redis will not\n     * recognized as a syntax error, and the transaction will not fail (but\n     * only the unsupported command will fail). */\n    for (int type = 0; type < 2; type++) {\n        retval = redisAsyncCommand(ri->link->cc,\n            sentinelDiscardReplyCallback, ri, \"%s KILL TYPE %s\",\n            sentinelInstanceMapCommand(ri,\"CLIENT\"),\n            type == 0 ? \"normal\" : \"pubsub\");\n        if (retval == C_ERR) return retval;\n        ri->link->pending_commands++;\n    }\n\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s\",\n        sentinelInstanceMapCommand(ri,\"EXEC\"));\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    return C_OK;\n}\n\n/* Setup the master state to start a failover. */\nvoid sentinelStartFailover(sentinelRedisInstance *master) {\n    serverAssert(master->flags & SRI_MASTER);\n\n    master->failover_state = SENTINEL_FAILOVER_STATE_WAIT_START;\n    master->flags |= SRI_FAILOVER_IN_PROGRESS;\n    master->failover_epoch = ++sentinel.current_epoch;\n    sentinelEvent(LL_WARNING,\"+new-epoch\",master,\"%llu\",\n        (unsigned long long) sentinel.current_epoch);\n    sentinelEvent(LL_WARNING,\"+try-failover\",master,\"%@\");\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 * 1) Master must be in ODOWN condition.\n * 2) No failover already in progress.\n * 3) No failover already attempted recently.\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 * Return non-zero if a failover was started. */\nint sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) {\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            serverLog(LL_WARNING,\n                \"Next failover delay: I will not start a failover before %s\",\n                ctimebuf);\n        }\n        return 0;\n    }\n\n    sentinelStartFailover(master);\n    return 1;\n}\n\n/* Select a suitable slave to promote. The current algorithm only uses\n * the following parameters:\n *\n * 1) None of the following conditions: S_DOWN, O_DOWN, DISCONNECTED.\n * 2) Last time the slave replied to ping no more than 5 times the PING period.\n * 3) info_refresh not older than 3 times the INFO refresh period.\n * 4) master_link_down_time no more than:\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 *    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 *    Anyway we'll select the best slave according to replication offset.\n * 5) Slave priority can't be zero, otherwise the slave is discarded.\n *\n * Among all the slaves matching the above conditions we select the slave\n * with, in order of sorting key:\n *\n * - lower slave_priority.\n * - bigger processed replication offset.\n * - lexicographically smaller runid.\n *\n * Basically if runid is the same, the slave that processed more commands\n * from the master is selected.\n *\n * The function returns the pointer to the selected slave, otherwise\n * NULL if no suitable slave was found.\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. */\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    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 from the master). */\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; /* a > b */\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    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\nsentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) {\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    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    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n        mstime_t info_validity_time;\n\n        if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;\n        if (slave->link->disconnected) continue;\n        if (mstime() - slave->link->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        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        if (mstime() - slave->info_refresh > info_validity_time) continue;\n        if (slave->master_link_down_time > max_master_down_time) continue;\n        instance[instances++] = slave;\n    }\n    dictReleaseIterator(di);\n    if (instances) {\n        qsort(instance,instances,sizeof(sentinelRedisInstance*),\n            compareSlavesForPromotion);\n        selected = instance[0];\n    }\n    zfree(instance);\n    return selected;\n}\n\n/* ---------------- Failover state machine implementation ------------------- */\nvoid sentinelFailoverWaitStart(sentinelRedisInstance *ri) {\n    char *leader;\n    int isleader;\n\n    /* Check if we are the leader for the failover epoch. */\n    leader = sentinelGetLeader(ri, ri->failover_epoch);\n    isleader = leader && strcasecmp(leader,sentinel.myid) == 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    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        if (election_timeout > ri->failover_timeout)\n            election_timeout = ri->failover_timeout;\n        /* Abort the failover if I'm not the leader after some time. */\n        if (mstime() - ri->failover_start_time > election_timeout) {\n            sentinelEvent(LL_WARNING,\"-failover-abort-not-elected\",ri,\"%@\");\n            sentinelAbortFailover(ri);\n        }\n        return;\n    }\n    sentinelEvent(LL_WARNING,\"+elected-leader\",ri,\"%@\");\n    if (sentinel.simfailure_flags & SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION)\n        sentinelSimFailureCrash();\n    ri->failover_state = SENTINEL_FAILOVER_STATE_SELECT_SLAVE;\n    ri->failover_state_change_time = mstime();\n    sentinelEvent(LL_WARNING,\"+failover-state-select-slave\",ri,\"%@\");\n}\n\nvoid sentinelFailoverSelectSlave(sentinelRedisInstance *ri) {\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    if (slave == NULL) {\n        sentinelEvent(LL_WARNING,\"-failover-abort-no-good-slave\",ri,\"%@\");\n        sentinelAbortFailover(ri);\n    } else {\n        sentinelEvent(LL_WARNING,\"+selected-slave\",slave,\"%@\");\n        slave->flags |= SRI_PROMOTED;\n        ri->promoted_slave = slave;\n        ri->failover_state = SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE;\n        ri->failover_state_change_time = mstime();\n        sentinelEvent(LL_NOTICE,\"+failover-state-send-slaveof-noone\",\n            slave, \"%@\");\n    }\n}\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    if (ri->promoted_slave->link->disconnected) {\n        if (mstime() - ri->failover_state_change_time > ri->failover_timeout) {\n            sentinelEvent(LL_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     * 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    retval = sentinelSendSlaveOf(ri->promoted_slave,NULL,0);\n    if (retval != C_OK) return;\n    sentinelEvent(LL_NOTICE, \"+failover-state-wait-promotion\",\n        ri->promoted_slave,\"%@\");\n    ri->failover_state = SENTINEL_FAILOVER_STATE_WAIT_PROMOTION;\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. */\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(LL_WARNING,\"-failover-abort-slave-timeout\",ri,\"%@\");\n        sentinelAbortFailover(ri);\n    }\n}\n\nvoid sentinelFailoverDetectEnd(sentinelRedisInstance *master) {\n    int not_reconfigured = 0, timeout = 0;\n    dictIterator *di;\n    dictEntry *de;\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    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    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue;\n        if (slave->flags & SRI_S_DOWN) continue;\n        not_reconfigured++;\n    }\n    dictReleaseIterator(di);\n\n    /* Force end of failover on timeout. */\n    if (elapsed > master->failover_timeout) {\n        not_reconfigured = 0;\n        timeout = 1;\n        sentinelEvent(LL_WARNING,\"+failover-end-for-timeout\",master,\"%@\");\n    }\n\n    if (not_reconfigured == 0) {\n        sentinelEvent(LL_WARNING,\"+failover-end\",master,\"%@\");\n        master->failover_state = SENTINEL_FAILOVER_STATE_UPDATE_CONFIG;\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        di = dictGetIterator(master->slaves);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *slave = dictGetVal(de);\n            int retval;\n\n            if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE|SRI_RECONF_SENT)) continue;\n            if (slave->link->disconnected) continue;\n\n            retval = sentinelSendSlaveOf(slave,\n                    master->promoted_slave->addr->ip,\n                    master->promoted_slave->addr->port);\n            if (retval == C_OK) {\n                sentinelEvent(LL_NOTICE,\"+slave-reconf-sent-be\",slave,\"%@\");\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. */\nvoid sentinelFailoverReconfNextSlave(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    int in_progress = 0;\n\n    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG))\n            in_progress++;\n    }\n    dictReleaseIterator(di);\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        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            sentinelEvent(LL_NOTICE,\"-slave-reconf-sent-timeout\",slave,\"%@\");\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        if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG)) continue;\n        if (slave->link->disconnected) continue;\n\n        /* Send SLAVEOF <new master>. */\n        retval = sentinelSendSlaveOf(slave,\n                master->promoted_slave->addr->ip,\n                master->promoted_slave->addr->port);\n        if (retval == C_OK) {\n            slave->flags |= SRI_RECONF_SENT;\n            slave->slave_reconf_sent_time = mstime();\n            sentinelEvent(LL_NOTICE,\"+slave-reconf-sent\",slave,\"%@\");\n            in_progress++;\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Check if all the slaves are reconfigured and handle timeout. */\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. */\nvoid sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) {\n    sentinelRedisInstance *ref = master->promoted_slave ?\n                                 master->promoted_slave : master;\n\n    sentinelEvent(LL_WARNING,\"+switch-master\",master,\"%s %s %d %s %d\",\n        master->name, master->addr->ip, master->addr->port,\n        ref->addr->ip, ref->addr->port);\n\n    sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port);\n}\n\nvoid sentinelFailoverStateMachine(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_MASTER);\n\n    if (!(ri->flags & SRI_FAILOVER_IN_PROGRESS)) return;\n\n    switch(ri->failover_state) {\n        case SENTINEL_FAILOVER_STATE_WAIT_START:\n            sentinelFailoverWaitStart(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_SELECT_SLAVE:\n            sentinelFailoverSelectSlave(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE:\n            sentinelFailoverSendSlaveOfNoOne(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION:\n            sentinelFailoverWaitPromotion(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_RECONF_SLAVES:\n            sentinelFailoverReconfNextSlave(ri);\n            break;\n    }\n}\n\n/* Abort a failover in progress:\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). */\nvoid sentinelAbortFailover(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_FAILOVER_IN_PROGRESS);\n    serverAssert(ri->failover_state <= SENTINEL_FAILOVER_STATE_WAIT_PROMOTION);\n\n    ri->flags &= ~(SRI_FAILOVER_IN_PROGRESS|SRI_FORCE_FAILOVER);\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = mstime();\n    if (ri->promoted_slave) {\n        ri->promoted_slave->flags &= ~SRI_PROMOTED;\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. */\nvoid sentinelHandleRedisInstance(sentinelRedisInstance *ri) {\n    /* ========== MONITORING HALF ============ */\n    /* Every kind of instance */\n    sentinelReconnectInstance(ri);\n    sentinelSendPeriodicCommands(ri);\n\n    /* ============== ACTING HALF ============= */\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    if (sentinel.tilt) {\n        if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;\n        sentinel.tilt = 0;\n        sentinelEvent(LL_WARNING,\"-tilt\",NULL,\"#tilt mode exited\");\n    }\n\n    /* Every kind of instance */\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    if (ri->flags & SRI_MASTER) {\n        sentinelCheckObjectivelyDown(ri);\n        if (sentinelStartFailoverIfNeeded(ri))\n            sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);\n        sentinelFailoverStateMachine(ri);\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. */\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    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        sentinelHandleRedisInstance(ri);\n        if (ri->flags & SRI_MASTER) {\n            sentinelHandleDictOfRedisInstances(ri->slaves);\n            sentinelHandleDictOfRedisInstances(ri->sentinels);\n            if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) {\n                switch_to_promoted = ri;\n            }\n        }\n    }\n    if (switch_to_promoted)\n        sentinelFailoverSwitchToPromotedSlave(switch_to_promoted);\n    dictReleaseIterator(di);\n}\n\n/* This function checks if we need to enter the TITL mode.\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 * 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 * 1) The Sentiel 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 * 2) The system clock was altered significantly.\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 * During TILT time we still collect information, we just do not act. */\nvoid sentinelCheckTiltCondition(void) {\n    mstime_t now = mstime();\n    mstime_t delta = now - sentinel.previous_time;\n\n    if (delta < 0 || delta > SENTINEL_TILT_TRIGGER) {\n        sentinel.tilt = 1;\n        sentinel.tilt_start_time = mstime();\n        sentinelEvent(LL_WARNING,\"+tilt\",NULL,\"#tilt mode entered\");\n    }\n    sentinel.previous_time = mstime();\n}\n\nvoid sentinelTimer(void) {\n    sentinelCheckTiltCondition();\n    sentinelHandleDictOfRedisInstances(sentinel.masters);\n    sentinelRunPendingScripts();\n    sentinelCollectTerminatedScripts();\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 = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/server.c",
    "content": "/*\n * Copyright (c) 2009-2016, 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 \"server.h\"\n#include \"cluster.h\"\n#include \"slowlog.h\"\n#include \"bio.h\"\n#include \"latency.h\"\n#include \"atomicvar.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 <sys/un.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#include <sys/socket.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 */\nvolatile unsigned long lru_clock; /* Server global current LRU time. */\n\n/* Our command table.\n *\n * Every entry is composed of the following fields:\n *\n * name:        A string representing the command name.\n *\n * function:    Pointer to the C function implementing the command.\n *\n * arity:       Number of arguments, it is possible to use -N to say >= N\n *\n * sflags:      Command flags as string. See below for a table of flags.\n *\n * flags:       Flags as bitmask. Computed by Redis using the 'sflags' field.\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 *\n * first_key_index: First argument that is a key\n *\n * last_key_index: Last argument that is a key\n *\n * key_step:    Step to get all the keys from first to last argument.\n *              For instance in MSET the step is two since arguments\n *              are key,val,key,val,...\n *\n * microseconds: Microseconds of total execution time for this command.\n *\n * calls:       Total number of calls of this command.\n *\n * id:          Command bit identifier for ACLs or other goals.\n *\n * The flags, microseconds and calls fields are computed by Redis and should\n * always be set to zero.\n *\n * Command flags are expressed using space separated strings, that are turned\n * into actual flags by the populateCommandTable() function.\n *\n * This is the meaning of the flags:\n *\n * write:       Write command (may modify the key space).\n *\n * read-only:   All the non special commands just reading from keys without\n *              changing the content, or returning other informations like\n *              the TIME command. Special commands such administrative commands\n *              or transaction related commands (multi, exec, discard, ...)\n *              are not flagged as read-only commands, since they affect the\n *              server or the connection in other ways.\n *\n * use-memory:  May increase memory usage once called. Don't allow if out\n *              of memory.\n *\n * admin:       Administrative command, like SAVE or SHUTDOWN.\n *\n * pub-sub:     Pub/Sub related command.\n *\n * no-script:   Command not allowed in scripts.\n *\n * random:      Random command. Command is not deterministic, that is, the same\n *              command with the same arguments, with the same key space, may\n *              have different results. For instance SPOP and RANDOMKEY are\n *              two random commands.\n *\n * to-sort:     Sort command output array if called from script, so that the\n *              output is deterministic. When this flag is used (not always\n *              possible), then the \"random\" flag is not needed.\n *\n * ok-loading:  Allow the command while loading the database.\n *\n * ok-stale:    Allow the command while a slave has stale data but is not\n *              allowed to serve this data. Normally no command is accepted\n *              in this condition but just a few.\n *\n * no-monitor:  Do not automatically propagate the command on MONITOR.\n *\n * no-slowlog:  Do not automatically propagate the command to the slowlog.\n *\n * cluster-asking: Perform an implicit ASKING for this command, so the\n *              command will be accepted in cluster mode if the slot is marked\n *              as 'importing'.\n *\n * fast:        Fast command: O(1) or O(log(N)) command that should never\n *              delay its execution as long as the kernel scheduler is giving\n *              us time. Note that commands that may trigger a DEL as a side\n *              effect (like SET) are not fast commands.\n *\n * The following additional flags are only used in order to put commands\n * in a specific ACL category. Commands can have multiple ACL categories.\n *\n * @keyspace, @read, @write, @set, @sortedset, @list, @hash, @string, @bitmap,\n * @hyperloglog, @stream, @admin, @fast, @slow, @pubsub, @blocking, @dangerous,\n * @connection, @transaction, @scripting, @geo.\n *\n * Note that:\n *\n * 1) The read-only flag implies the @read ACL category.\n * 2) The write flag implies the @write ACL category.\n * 3) The fast flag implies the @fast ACL category.\n * 4) The admin flag implies the @admin and @dangerous ACL category.\n * 5) The pub-sub flag implies the @pubsub ACL category.\n * 6) The lack of fast flag implies the @slow ACL category.\n * 7) The non obvious \"keyspace\" category includes the commands\n *    that interact with keys without having anything to do with\n *    specific data structures, such as: DEL, RENAME, MOVE, SELECT,\n *    TYPE, EXPIRE*, PEXPIRE*, TTL, PTTL, ...\n */\n\nstruct redisCommand redisCommandTable[] = {\n    {\"module\",moduleCommand,-2,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"get\",getCommand,2,\n     \"read-only fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* Note that we can't flag set as fast, since it may perform an\n     * implicit DEL of a large key. */\n    {\"set\",setCommand,-3,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"setnx\",setnxCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"setex\",setexCommand,4,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"psetex\",psetexCommand,4,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"append\",appendCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"strlen\",strlenCommand,2,\n     \"read-only fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"del\",delCommand,-2,\n     \"write @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"unlink\",unlinkCommand,-2,\n     \"write fast @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"exists\",existsCommand,-2,\n     \"read-only fast @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"setbit\",setbitCommand,4,\n     \"write use-memory @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"getbit\",getbitCommand,3,\n     \"read-only fast @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bitfield\",bitfieldCommand,-2,\n     \"write use-memory @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bitfield_ro\",bitfieldroCommand,-2,\n     \"read-only fast @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"setrange\",setrangeCommand,4,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"getrange\",getrangeCommand,4,\n     \"read-only @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"substr\",getrangeCommand,4,\n     \"read-only @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"incr\",incrCommand,2,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"decr\",decrCommand,2,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"mget\",mgetCommand,-2,\n     \"read-only fast @string\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"rpush\",rpushCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lpush\",lpushCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"rpushx\",rpushxCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lpushx\",lpushxCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"linsert\",linsertCommand,5,\n     \"write use-memory @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"rpop\",rpopCommand,2,\n     \"write fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lpop\",lpopCommand,2,\n     \"write fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"brpop\",brpopCommand,-3,\n     \"write no-script @list @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"brpoplpush\",brpoplpushCommand,4,\n     \"write use-memory no-script @list @blocking\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"blpop\",blpopCommand,-3,\n     \"write no-script @list @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"llen\",llenCommand,2,\n     \"read-only fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lindex\",lindexCommand,3,\n     \"read-only @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lset\",lsetCommand,4,\n     \"write use-memory @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lrange\",lrangeCommand,4,\n     \"read-only @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"ltrim\",ltrimCommand,4,\n     \"write @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lrem\",lremCommand,4,\n     \"write @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"rpoplpush\",rpoplpushCommand,3,\n     \"write use-memory @list\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"sadd\",saddCommand,-3,\n     \"write use-memory fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"srem\",sremCommand,-3,\n     \"write fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"smove\",smoveCommand,4,\n     \"write fast @set\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"sismember\",sismemberCommand,3,\n     \"read-only fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"scard\",scardCommand,2,\n     \"read-only fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"spop\",spopCommand,-2,\n     \"write random fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"srandmember\",srandmemberCommand,-2,\n     \"read-only random @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"sinter\",sinterCommand,-2,\n     \"read-only to-sort @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sinterstore\",sinterstoreCommand,-3,\n     \"write use-memory @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sunion\",sunionCommand,-2,\n     \"read-only to-sort @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sunionstore\",sunionstoreCommand,-3,\n     \"write use-memory @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sdiff\",sdiffCommand,-2,\n     \"read-only to-sort @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sdiffstore\",sdiffstoreCommand,-3,\n     \"write use-memory @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"smembers\",sinterCommand,2,\n     \"read-only to-sort @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"sscan\",sscanCommand,-3,\n     \"read-only random @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zadd\",zaddCommand,-4,\n     \"write use-memory fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zincrby\",zincrbyCommand,4,\n     \"write use-memory fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrem\",zremCommand,-3,\n     \"write fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zremrangebyscore\",zremrangebyscoreCommand,4,\n     \"write @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zremrangebyrank\",zremrangebyrankCommand,4,\n     \"write @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zremrangebylex\",zremrangebylexCommand,4,\n     \"write @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zunionstore\",zunionstoreCommand,-4,\n     \"write use-memory @sortedset\",\n     0,zunionInterGetKeys,0,0,0,0,0,0},\n\n    {\"zinterstore\",zinterstoreCommand,-4,\n     \"write use-memory @sortedset\",\n     0,zunionInterGetKeys,0,0,0,0,0,0},\n\n    {\"zrange\",zrangeCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrangebyscore\",zrangebyscoreCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrangebyscore\",zrevrangebyscoreCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrangebylex\",zrangebylexCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrangebylex\",zrevrangebylexCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zcount\",zcountCommand,4,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zlexcount\",zlexcountCommand,4,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrange\",zrevrangeCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zcard\",zcardCommand,2,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zscore\",zscoreCommand,3,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrank\",zrankCommand,3,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrank\",zrevrankCommand,3,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zscan\",zscanCommand,-3,\n     \"read-only random @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zpopmin\",zpopminCommand,-2,\n     \"write fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zpopmax\",zpopmaxCommand,-2,\n     \"write fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bzpopmin\",bzpopminCommand,-3,\n     \"write no-script fast @sortedset @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"bzpopmax\",bzpopmaxCommand,-3,\n     \"write no-script fast @sortedset @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"hset\",hsetCommand,-4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hsetnx\",hsetnxCommand,4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hget\",hgetCommand,3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hmset\",hsetCommand,-4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hmget\",hmgetCommand,-3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hincrby\",hincrbyCommand,4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hincrbyfloat\",hincrbyfloatCommand,4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hdel\",hdelCommand,-3,\n     \"write fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hlen\",hlenCommand,2,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hstrlen\",hstrlenCommand,3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hkeys\",hkeysCommand,2,\n     \"read-only to-sort @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hvals\",hvalsCommand,2,\n     \"read-only to-sort @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hgetall\",hgetallCommand,2,\n     \"read-only random @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hexists\",hexistsCommand,3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hscan\",hscanCommand,-3,\n     \"read-only random @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"incrby\",incrbyCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"decrby\",decrbyCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"incrbyfloat\",incrbyfloatCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"getset\",getsetCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"mset\",msetCommand,-3,\n     \"write use-memory @string\",\n     0,NULL,1,-1,2,0,0,0},\n\n    {\"msetnx\",msetnxCommand,-3,\n     \"write use-memory @string\",\n     0,NULL,1,-1,2,0,0,0},\n\n    {\"randomkey\",randomkeyCommand,1,\n     \"read-only random @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"select\",selectCommand,2,\n     \"ok-loading fast ok-stale @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"swapdb\",swapdbCommand,3,\n     \"write fast @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"move\",moveCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* Like for SET, we can't mark rename as a fast command because\n     * overwriting the target key may result in an implicit slow DEL. */\n    {\"rename\",renameCommand,3,\n     \"write @keyspace\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"renamenx\",renamenxCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"expire\",expireCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"expireat\",expireatCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"pexpire\",pexpireCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"pexpireat\",pexpireatCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"keys\",keysCommand,2,\n     \"read-only to-sort @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"scan\",scanCommand,-2,\n     \"read-only random @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"dbsize\",dbsizeCommand,1,\n     \"read-only fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"auth\",authCommand,-2,\n     \"no-auth no-script ok-loading ok-stale fast no-monitor no-slowlog @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    /* We don't allow PING during loading since in Redis PING is used as\n     * failure detection, and a loading server is considered to be\n     * not available. */\n    {\"ping\",pingCommand,-1,\n     \"ok-stale fast @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"echo\",echoCommand,2,\n     \"read-only fast @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"save\",saveCommand,1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"bgsave\",bgsaveCommand,-1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"bgrewriteaof\",bgrewriteaofCommand,1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"shutdown\",shutdownCommand,-1,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"lastsave\",lastsaveCommand,1,\n     \"read-only random fast ok-loading ok-stale @admin @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"type\",typeCommand,2,\n     \"read-only fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"multi\",multiCommand,1,\n     \"no-script fast ok-loading ok-stale @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"exec\",execCommand,1,\n     \"no-script no-monitor no-slowlog ok-loading ok-stale @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"discard\",discardCommand,1,\n     \"no-script fast ok-loading ok-stale @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"sync\",syncCommand,1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"psync\",syncCommand,3,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"replconf\",replconfCommand,-1,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"flushdb\",flushdbCommand,-1,\n     \"write @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"flushall\",flushallCommand,-1,\n     \"write @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"sort\",sortCommand,-2,\n     \"write use-memory @list @set @sortedset @dangerous\",\n     0,sortGetKeys,1,1,1,0,0,0},\n\n    {\"info\",infoCommand,-1,\n     \"ok-loading ok-stale random @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"monitor\",monitorCommand,1,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"ttl\",ttlCommand,2,\n     \"read-only fast random @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"touch\",touchCommand,-2,\n     \"read-only fast @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"pttl\",pttlCommand,2,\n     \"read-only fast random @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"persist\",persistCommand,2,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"slaveof\",replicaofCommand,3,\n     \"admin no-script ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"replicaof\",replicaofCommand,3,\n     \"admin no-script ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"role\",roleCommand,1,\n     \"ok-loading ok-stale no-script fast read-only @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"debug\",debugCommand,-2,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"config\",configCommand,-2,\n     \"admin ok-loading ok-stale no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"subscribe\",subscribeCommand,-2,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"unsubscribe\",unsubscribeCommand,-1,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"psubscribe\",psubscribeCommand,-2,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"punsubscribe\",punsubscribeCommand,-1,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"publish\",publishCommand,3,\n     \"pub-sub ok-loading ok-stale fast\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"pubsub\",pubsubCommand,-2,\n     \"pub-sub ok-loading ok-stale random\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"watch\",watchCommand,-2,\n     \"no-script fast @transaction\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"unwatch\",unwatchCommand,1,\n     \"no-script fast @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"cluster\",clusterCommand,-2,\n     \"admin ok-stale random\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"restore\",restoreCommand,-4,\n     \"write use-memory @keyspace @dangerous\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"restore-asking\",restoreCommand,-4,\n    \"write use-memory cluster-asking @keyspace @dangerous\",\n    0,NULL,1,1,1,0,0,0},\n\n    {\"migrate\",migrateCommand,-6,\n     \"write random @keyspace @dangerous\",\n     0,migrateGetKeys,0,0,0,0,0,0},\n\n    {\"asking\",askingCommand,1,\n     \"fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"readonly\",readonlyCommand,1,\n     \"fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"readwrite\",readwriteCommand,1,\n     \"fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"dump\",dumpCommand,2,\n     \"read-only random @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"object\",objectCommand,-2,\n     \"read-only random @keyspace\",\n     0,NULL,2,2,1,0,0,0},\n\n    {\"memory\",memoryCommand,-2,\n     \"random read-only\",\n     0,memoryGetKeys,0,0,0,0,0,0},\n\n    {\"client\",clientCommand,-2,\n     \"admin no-script random ok-loading ok-stale @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"hello\",helloCommand,-2,\n     \"no-auth no-script fast no-monitor ok-loading ok-stale no-slowlog @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    /* EVAL can modify the dataset, however it is not flagged as a write\n     * command since we do the check while running commands from Lua. */\n    {\"eval\",evalCommand,-3,\n     \"no-script @scripting\",\n     0,evalGetKeys,0,0,0,0,0,0},\n\n    {\"evalsha\",evalShaCommand,-3,\n     \"no-script @scripting\",\n     0,evalGetKeys,0,0,0,0,0,0},\n\n    {\"slowlog\",slowlogCommand,-2,\n     \"admin random ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"script\",scriptCommand,-2,\n     \"no-script @scripting\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"time\",timeCommand,1,\n     \"read-only random fast ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"bitop\",bitopCommand,-4,\n     \"write use-memory @bitmap\",\n     0,NULL,2,-1,1,0,0,0},\n\n    {\"bitcount\",bitcountCommand,-2,\n     \"read-only @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bitpos\",bitposCommand,-3,\n     \"read-only @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"wait\",waitCommand,3,\n     \"no-script @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"command\",commandCommand,-1,\n     \"ok-loading ok-stale random @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"geoadd\",geoaddCommand,-5,\n     \"write use-memory @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* GEORADIUS has store options that may write. */\n    {\"georadius\",georadiusCommand,-6,\n     \"write @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"georadius_ro\",georadiusroCommand,-6,\n     \"read-only @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"georadiusbymember\",georadiusbymemberCommand,-5,\n     \"write @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"georadiusbymember_ro\",georadiusbymemberroCommand,-5,\n     \"read-only @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"geohash\",geohashCommand,-2,\n     \"read-only @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"geopos\",geoposCommand,-2,\n     \"read-only @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"geodist\",geodistCommand,-4,\n     \"read-only @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"pfselftest\",pfselftestCommand,1,\n     \"admin @hyperloglog\",\n      0,NULL,0,0,0,0,0,0},\n\n    {\"pfadd\",pfaddCommand,-2,\n     \"write use-memory fast @hyperloglog\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* Technically speaking PFCOUNT may change the key since it changes the\n     * final bytes in the HyperLogLog representation. However in this case\n     * we claim that the representation, even if accessible, is an internal\n     * affair, and the command is semantically read only. */\n    {\"pfcount\",pfcountCommand,-2,\n     \"read-only @hyperloglog\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"pfmerge\",pfmergeCommand,-2,\n     \"write use-memory @hyperloglog\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"pfdebug\",pfdebugCommand,-3,\n     \"admin write\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"xadd\",xaddCommand,-5,\n     \"write use-memory fast random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xrange\",xrangeCommand,-4,\n     \"read-only @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xrevrange\",xrevrangeCommand,-4,\n     \"read-only @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xlen\",xlenCommand,2,\n     \"read-only fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xread\",xreadCommand,-4,\n     \"read-only @stream @blocking\",\n     0,xreadGetKeys,1,1,1,0,0,0},\n\n    {\"xreadgroup\",xreadCommand,-7,\n     \"write @stream @blocking\",\n     0,xreadGetKeys,1,1,1,0,0,0},\n\n    {\"xgroup\",xgroupCommand,-2,\n     \"write use-memory @stream\",\n     0,NULL,2,2,1,0,0,0},\n\n    {\"xsetid\",xsetidCommand,3,\n     \"write use-memory fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xack\",xackCommand,-4,\n     \"write fast random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xpending\",xpendingCommand,-3,\n     \"read-only random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xclaim\",xclaimCommand,-6,\n     \"write random fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xinfo\",xinfoCommand,-2,\n     \"read-only random @stream\",\n     0,NULL,2,2,1,0,0,0},\n\n    {\"xdel\",xdelCommand,-3,\n     \"write fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xtrim\",xtrimCommand,-2,\n     \"write random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"post\",securityWarningCommand,-1,\n     \"ok-loading ok-stale read-only\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"host:\",securityWarningCommand,-1,\n     \"ok-loading ok-stale read-only\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"latency\",latencyCommand,-2,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"lolwut\",lolwutCommand,-1,\n     \"read-only fast\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"acl\",aclCommand,-2,\n     \"admin no-script no-slowlog ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"stralgo\",stralgoCommand,-2,\n     \"read-only @string\",\n     0,lcsGetKeys,0,0,0,0,0,0}\n};\n\n/*============================ Utility functions ============================ */\n\n/* We use a private localtime implementation which is fork-safe. The logging\n * function of Redis may be called from other threads. */\nvoid nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst);\n\n/* Low level logging. To use only for very big messages, otherwise\n * serverLog() is to prefer. */\nvoid serverLogRaw(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 & LL_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        int role_char;\n        pid_t pid = getpid();\n\n        gettimeofday(&tv,NULL);\n        struct tm tm;\n        nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active);\n        off = strftime(buf,sizeof(buf),\"%d %b %Y %H:%M:%S.\",&tm);\n        snprintf(buf+off,sizeof(buf)-off,\"%03d\",(int)tv.tv_usec/1000);\n        if (server.sentinel_mode) {\n            role_char = 'X'; /* Sentinel. */\n        } else if (pid != server.pid) {\n            role_char = 'C'; /* RDB / AOF writing child. */\n        } else {\n            role_char = (server.masterhost ? 'S':'M'); /* Slave or Master. */\n        }\n        fprintf(fp,\"%d:%c %s %c %s\\n\",\n            (int)getpid(),role_char, 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 serverLogRaw() 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 serverLog(int level, const char *fmt, ...) {\n    va_list ap;\n    char msg[LOG_MAX_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    serverLogRaw(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 serverLog(). */\nvoid serverLogFromHandler(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,buf,strlen(buf)) == -1) goto err;\n    if (write(fd,\":signal-handler (\",17) == -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 */\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 */\nmstime_t 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 redis 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 dictObjectDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    if (val == NULL) return; /* Lazy freeing will set value 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\nuint64_t dictObjHash(const void *key) {\n    const robj *o = key;\n    return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));\n}\n\nuint64_t dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nuint64_t 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 == OBJ_ENCODING_INT &&\n        o2->encoding == OBJ_ENCODING_INT)\n            return o1->ptr == o2->ptr;\n\n    /* Due to OBJ_STATIC_REFCOUNT, we avoid calling getDecodedObject() without\n     * good reasons, because it would incrRefCount() the object, which\n     * is invalid. So we check to make sure dictFind() works with static\n     * objects as well. */\n    if (o1->refcount != OBJ_STATIC_REFCOUNT) o1 = getDecodedObject(o1);\n    if (o2->refcount != OBJ_STATIC_REFCOUNT) o2 = getDecodedObject(o2);\n    cmp = dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);\n    if (o1->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o1);\n    if (o2->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o2);\n    return cmp;\n}\n\nuint64_t 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 == OBJ_ENCODING_INT) {\n            char buf[32];\n            int len;\n\n            len = ll2string(buf,32,(long)o->ptr);\n            return dictGenHashFunction((unsigned char*)buf, len);\n        } else {\n            uint64_t 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/* Generic hash table type where keys are Redis Objects, Values\n * dummy pointers. */\ndictType objectKeyPointerValueDictType = {\n    dictEncObjHash,            /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictEncObjKeyCompare,      /* key compare */\n    dictObjectDestructor,      /* key destructor */\n    NULL                       /* val destructor */\n};\n\n/* Like objectKeyPointerValueDictType(), but values can be destroyed, if\n * not NULL, calling zfree(). */\ndictType objectKeyHeapPointerValueDictType = {\n    dictEncObjHash,            /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictEncObjKeyCompare,      /* key compare */\n    dictObjectDestructor,      /* key destructor */\n    dictVanillaFree            /* val destructor */\n};\n\n/* Set dictionary type. Keys are SDS strings, values are ot used. */\ndictType setDictType = {\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/* Sorted sets hash (note: a skiplist is used in addition to the hash table) */\ndictType zsetDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* Note: SDS string shared & freed by skiplist */\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    dictObjectDestructor   /* 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    dictObjectDestructor        /* val destructor */\n};\n\n/* Db->expires */\ndictType keyptrDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    NULL,                       /* key destructor */\n    NULL                        /* val destructor */\n};\n\n/* 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    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictSdsDestructor           /* val destructor */\n};\n\n/* Keylist hash table type has unencoded redis objects as keys and\n * lists as values. It's used for blocking operations (BLPOP) and to\n * map swapped keys to a list of clients waiting for this keys to be loaded. */\ndictType keylistDictType = {\n    dictObjHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictObjKeyCompare,          /* key compare */\n    dictObjectDestructor,       /* key destructor */\n    dictListDestructor          /* val destructor */\n};\n\n/* 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/* 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 modulesDictType = {\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 > DICT_HT_INITIAL_SIZE &&\n            (used*100/size < HASHTABLE_MIN_FILL));\n}\n\n/* If the percentage of used slots in the HT reaches HASHTABLE_MIN_FILL\n * we resize the hash table to save memory */\nvoid 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 * The function returns 1 if some rehashing was performed, otherwise 0\n * is returned. */\nint incrementallyRehash(int dbid) {\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    /* 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    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 (!hasActiveChildProcess())\n        dictEnableResize();\n    else\n        dictDisableResize();\n}\n\n/* Return true if there are no active children processes doing RDB saving,\n * AOF rewriting, or some side process spawned by a loaded module. */\nint hasActiveChildProcess() {\n    return server.rdb_child_pid != -1 ||\n           server.aof_child_pid != -1 ||\n           server.module_child_pid != -1;\n}\n\n/* Return true if this instance has persistence completely turned off:\n * both RDB and AOF are disabled. */\nint allPersistenceDisabled(void) {\n    return server.saveparamslen == 0 && server.aof_state == AOF_OFF;\n}\n\n/* ======================= Cron: called every 100 ms ======================== */\n\n/* Add a sample to the operations per second array of samples. */\nvoid trackInstantaneousMetric(int metric, long long current_reading) {\n    long long t = mstime() - server.inst_metric[metric].last_sample_time;\n    long long ops = current_reading -\n                    server.inst_metric[metric].last_sample_count;\n    long long ops_sec;\n\n    ops_sec = t > 0 ? (ops*1000/t) : 0;\n\n    server.inst_metric[metric].samples[server.inst_metric[metric].idx] =\n        ops_sec;\n    server.inst_metric[metric].idx++;\n    server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;\n    server.inst_metric[metric].last_sample_time = mstime();\n    server.inst_metric[metric].last_sample_count = current_reading;\n}\n\n/* Return the mean of all the samples. */\nlong long getInstantaneousMetric(int metric) {\n    int j;\n    long long sum = 0;\n\n    for (j = 0; j < STATS_METRIC_SAMPLES; j++)\n        sum += server.inst_metric[metric].samples[j];\n    return sum / STATS_METRIC_SAMPLES;\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 * The function always returns 0 as it never terminates the client. */\nint clientsCronResizeQueryBuffer(client *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     * 1) Query buffer is > BIG_ARG and too big for latest peak.\n     * 2) Query buffer is > BIG_ARG and client is idle. */\n    if (querybuf_size > PROTO_MBULK_BIG_ARG &&\n         ((querybuf_size/(c->querybuf_peak+1)) > 2 ||\n          idletime > 2))\n    {\n        /* Only resize the query buffer if it is actually wasting\n         * at least a few kbytes. */\n        if (sdsavail(c->querybuf) > 1024*4) {\n            c->querybuf = sdsRemoveFreeSpace(c->querybuf);\n        }\n    }\n    /* Reset the peak again to capture the peak memory usage in the next\n     * cycle. */\n    c->querybuf_peak = 0;\n\n    /* Clients representing masters also use a \"pending query buffer\" that\n     * is the yet not applied part of the stream we are reading. Such buffer\n     * also needs resizing from time to time, otherwise after a very large\n     * transfer (a huge value or a big MIGRATE operation) it will keep using\n     * a lot of memory. */\n    if (c->flags & CLIENT_MASTER) {\n        /* There are two conditions to resize the pending query buffer:\n         * 1) Pending Query buffer is > LIMIT_PENDING_QUERYBUF.\n         * 2) Used length is smaller than pending_querybuf_size/2 */\n        size_t pending_querybuf_size = sdsAllocSize(c->pending_querybuf);\n        if(pending_querybuf_size > LIMIT_PENDING_QUERYBUF &&\n           sdslen(c->pending_querybuf) < (pending_querybuf_size/2))\n        {\n            c->pending_querybuf = sdsRemoveFreeSpace(c->pending_querybuf);\n        }\n    }\n    return 0;\n}\n\n/* This function is used in order to track clients using the biggest amount\n * of memory in the latest few seconds. This way we can provide such information\n * in the INFO output (clients section), without having to do an O(N) scan for\n * all the clients.\n *\n * This is how it works. We have an array of CLIENTS_PEAK_MEM_USAGE_SLOTS slots\n * where we track, for each, the biggest client output and input buffers we\n * saw in that slot. Every slot correspond to one of the latest seconds, since\n * the array is indexed by doing UNIXTIME % CLIENTS_PEAK_MEM_USAGE_SLOTS.\n *\n * When we want to know what was recently the peak memory usage, we just scan\n * such few slots searching for the maximum value. */\n#define CLIENTS_PEAK_MEM_USAGE_SLOTS 8\nsize_t ClientsPeakMemInput[CLIENTS_PEAK_MEM_USAGE_SLOTS];\nsize_t ClientsPeakMemOutput[CLIENTS_PEAK_MEM_USAGE_SLOTS];\n\nint clientsCronTrackExpansiveClients(client *c) {\n    size_t in_usage = sdsAllocSize(c->querybuf);\n    size_t out_usage = getClientOutputBufferMemoryUsage(c);\n    int i = server.unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS;\n    int zeroidx = (i+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS;\n\n    /* Always zero the next sample, so that when we switch to that second, we'll\n     * only register samples that are greater in that second without considering\n     * the history of such slot.\n     *\n     * Note: our index may jump to any random position if serverCron() is not\n     * called for some reason with the normal frequency, for instance because\n     * some slow command is called taking multiple seconds to execute. In that\n     * case our array may end containing data which is potentially older\n     * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem\n     * since here we want just to track if \"recently\" there were very expansive\n     * clients from the POV of memory usage. */\n    ClientsPeakMemInput[zeroidx] = 0;\n    ClientsPeakMemOutput[zeroidx] = 0;\n\n    /* Track the biggest values observed so far in this slot. */\n    if (in_usage > ClientsPeakMemInput[i]) ClientsPeakMemInput[i] = in_usage;\n    if (out_usage > ClientsPeakMemOutput[i]) ClientsPeakMemOutput[i] = out_usage;\n\n    return 0; /* This function never terminates the client. */\n}\n\n/* Iterating all the clients in getMemoryOverheadData() is too slow and\n * in turn would make the INFO command too slow. So we perform this\n * computation incrementally and track the (not instantaneous but updated\n * to the second) total memory used by clients using clinetsCron() in\n * a more incremental way (depending on server.hz). */\nint clientsCronTrackClientsMemUsage(client *c) {\n    size_t mem = 0;\n    int type = getClientType(c);\n    mem += getClientOutputBufferMemoryUsage(c);\n    mem += sdsAllocSize(c->querybuf);\n    mem += sizeof(client);\n    /* Now that we have the memory used by the client, remove the old\n     * value from the old categoty, and add it back. */\n    server.stat_clients_type_memory[c->client_cron_last_memory_type] -=\n        c->client_cron_last_memory_usage;\n    server.stat_clients_type_memory[type] += mem;\n    /* Remember what we added and where, to remove it next time. */\n    c->client_cron_last_memory_usage = mem;\n    c->client_cron_last_memory_type = type;\n    return 0;\n}\n\n/* Return the max samples in the memory usage of clients tracked by\n * the function clientsCronTrackExpansiveClients(). */\nvoid getExpansiveClientsInfo(size_t *in_usage, size_t *out_usage) {\n    size_t i = 0, o = 0;\n    for (int j = 0; j < CLIENTS_PEAK_MEM_USAGE_SLOTS; j++) {\n        if (ClientsPeakMemInput[j] > i) i = ClientsPeakMemInput[j];\n        if (ClientsPeakMemOutput[j] > o) o = ClientsPeakMemOutput[j];\n    }\n    *in_usage = i;\n    *out_usage = o;\n}\n\n/* This function is called by serverCron() and is used in order to perform\n * operations on clients that are important to perform constantly. For instance\n * we use this function in order to disconnect clients after a timeout, including\n * clients blocked in some blocking command with a non-zero timeout.\n *\n * The function makes some effort to process all the clients every second, even\n * if this cannot be strictly guaranteed, since serverCron() may be called with\n * an actual frequency lower than server.hz in case of latency events like slow\n * commands.\n *\n * It is very important for this function, and the functions it calls, to be\n * very fast: sometimes Redis has tens of hundreds of connected clients, and the\n * default server.hz value is 10, so sometimes here we need to process thousands\n * of clients per second, turning this function into a source of latency.\n */\n#define CLIENTS_CRON_MIN_ITERATIONS 5\nvoid clientsCron(void) {\n    /* Try to process at least numclients/server.hz of clients\n     * per call. Since normally (if there are no big latency events) this\n     * function is called server.hz times per second, in the average case we\n     * process all the clients in 1 second. */\n    int numclients = listLength(server.clients);\n    int iterations = numclients/server.hz;\n    mstime_t now = mstime();\n\n    /* Process at least a few clients while we are at it, even if we need\n     * to process less than CLIENTS_CRON_MIN_ITERATIONS to meet our contract\n     * of processing each client once per second. */\n    if (iterations < CLIENTS_CRON_MIN_ITERATIONS)\n        iterations = (numclients < CLIENTS_CRON_MIN_ITERATIONS) ?\n                     numclients : CLIENTS_CRON_MIN_ITERATIONS;\n\n    while(listLength(server.clients) && iterations--) {\n        client *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        listRotateTailToHead(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        if (clientsCronHandleTimeout(c,now)) continue;\n        if (clientsCronResizeQueryBuffer(c)) continue;\n        if (clientsCronTrackExpansiveClients(c)) continue;\n        if (clientsCronTrackClientsMemUsage(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. */\nvoid databasesCron(void) {\n    /* Expire keys by random sampling. Not required for slaves\n     * as master will synthesize DELs for us. */\n    if (server.active_expire_enabled) {\n        if (iAmMaster()) {\n            activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);\n        } else {\n            expireSlaveKeys();\n        }\n    }\n\n    /* Defrag keys gradually. */\n    activeDefragCycle();\n\n    /* Perform hash tables rehashing if needed, but only if there are no\n     * other processes saving the DB on disk. Otherwise rehashing is bad\n     * as will cause a lot of copy-on-write of memory pages. */\n    if (!hasActiveChildProcess()) {\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        int dbs_per_call = CRON_DBS_PER_CALL;\n        int j;\n\n        /* Don't test more DBs than we have. */\n        if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;\n\n        /* Resize */\n        for (j = 0; j < dbs_per_call; j++) {\n            tryResizeHashTables(resize_db % server.dbnum);\n            resize_db++;\n        }\n\n        /* Rehash */\n        if (server.activerehashing) {\n            for (j = 0; j < dbs_per_call; j++) {\n                int work_done = incrementallyRehash(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                } else {\n                    /* If this db didn't need rehash, we'll try the next one. */\n                    rehash_db++;\n                    rehash_db %= server.dbnum;\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).\n *\n * This function should be fast because it is called at every command execution\n * in call(), so it is possible to decide if to update the daylight saving\n * info or not using the 'update_daylight_info' argument. Normally we update\n * such info only when calling this function from serverCron() but not when\n * calling it from call(). */\nvoid updateCachedTime(int update_daylight_info) {\n    server.ustime = ustime();\n    server.mstime = server.ustime / 1000;\n    server.unixtime = server.mstime / 1000;\n\n    /* To get information about daylight saving time, we need to call\n     * localtime_r and cache the result. However calling localtime_r in this\n     * context is safe since we will never fork() while here, in the main\n     * thread. The logging function will call a thread safe version of\n     * localtime that has no locks. */\n    if (update_daylight_info) {\n        struct tm tm;\n        time_t ut = server.unixtime;\n        localtime_r(&ut,&tm);\n        server.daylight_active = tm.tm_isdst;\n    }\n}\n\nvoid checkChildrenDone(void) {\n    int statloc;\n    pid_t pid;\n\n    /* If we have a diskless rdb child (note that we support only one concurrent\n     * child), we want to avoid collecting it's exit status and acting on it\n     * as long as we didn't finish to drain the pipe, since then we're at risk\n     * of starting a new fork and a new pipe before we're done with the previous\n     * one. */\n    if (server.rdb_child_pid != -1 && server.rdb_pipe_conns)\n        return;\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        /* sigKillChildHandler catches the signal and calls exit(), but we\n         * must make sure not to flag lastbgsave_status, etc incorrectly.\n         * We could directly terminate the child process via SIGUSR1\n         * without handling it, but in this case Valgrind will log an\n         * annoying error. */\n        if (exitcode == SERVER_CHILD_NOERROR_RETVAL) {\n            bysignal = SIGUSR1;\n            exitcode = 1;\n        }\n\n        if (pid == -1) {\n            serverLog(LL_WARNING,\"wait3() returned an error: %s. \"\n                \"rdb_child_pid = %d, aof_child_pid = %d, module_child_pid = %d\",\n                strerror(errno),\n                (int) server.rdb_child_pid,\n                (int) server.aof_child_pid,\n                (int) server.module_child_pid);\n        } else if (pid == server.rdb_child_pid) {\n            backgroundSaveDoneHandler(exitcode,bysignal);\n            if (!bysignal && exitcode == 0) receiveChildInfo();\n        } else if (pid == server.aof_child_pid) {\n            backgroundRewriteDoneHandler(exitcode,bysignal);\n            if (!bysignal && exitcode == 0) receiveChildInfo();\n        } else if (pid == server.module_child_pid) {\n            ModuleForkDoneHandler(exitcode,bysignal);\n            if (!bysignal && exitcode == 0) receiveChildInfo();\n        } else {\n            if (!ldbRemoveChild(pid)) {\n                serverLog(LL_WARNING,\n                    \"Warning, detected child with unmatched pid: %ld\",\n                    (long)pid);\n            }\n        }\n        updateDictResizePolicy();\n        closeChildInfoPipe();\n    }\n}\n\n/* This is our timer interrupt, called server.hz times per second.\n * Here is where we do a number of things that need to be done asynchronously.\n * For instance:\n *\n * - Active expired keys collection (it is also performed in a lazy way on\n *   lookup).\n * - Software watchdog.\n * - Update some statistic.\n * - Incremental rehashing of the DBs hash tables.\n * - Triggering BGSAVE / AOF rewrite, and handling of terminated children.\n * - Clients timeout of different kinds.\n * - Replication reconnection.\n * - Many more...\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\nint serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    int j;\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(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(1);\n\n    server.hz = server.config_hz;\n    /* Adapt the server.hz value to the number of configured clients. If we have\n     * many clients, we want to call serverCron() with an higher frequency. */\n    if (server.dynamic_hz) {\n        while (listLength(server.clients) / server.hz >\n               MAX_CLIENTS_PER_CLOCK_TICK)\n        {\n            server.hz *= 2;\n            if (server.hz > CONFIG_MAX_HZ) {\n                server.hz = CONFIG_MAX_HZ;\n                break;\n            }\n        }\n    }\n\n    run_with_period(100) {\n        trackInstantaneousMetric(STATS_METRIC_COMMAND,server.stat_numcommands);\n        trackInstantaneousMetric(STATS_METRIC_NET_INPUT,\n                server.stat_net_input_bytes);\n        trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT,\n                server.stat_net_output_bytes);\n    }\n\n    /* We have just 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     * Note that you can change the resolution altering the\n     * LRU_CLOCK_RESOLUTION define. */\n    server.lruclock = getLRUClock();\n\n    /* Record the max memory used since the server was started. */\n    if (zmalloc_used_memory() > server.stat_peak_memory)\n        server.stat_peak_memory = zmalloc_used_memory();\n\n    run_with_period(100) {\n        /* Sample the RSS and other metrics here since this is a relatively slow call.\n         * We must sample the zmalloc_used at the same time we take the rss, otherwise\n         * the frag ratio calculate may be off (ratio of two samples at different times) */\n        server.cron_malloc_stats.process_rss = zmalloc_get_rss();\n        server.cron_malloc_stats.zmalloc_used = zmalloc_used_memory();\n        /* Sampling the allcator info can be slow too.\n         * The fragmentation ratio it'll show is potentically more accurate\n         * it excludes other RSS pages such as: shared libraries, LUA and other non-zmalloc\n         * allocations, and allocator reserved pages that can be pursed (all not actual frag) */\n        zmalloc_get_allocator_info(&server.cron_malloc_stats.allocator_allocated,\n                                   &server.cron_malloc_stats.allocator_active,\n                                   &server.cron_malloc_stats.allocator_resident);\n        /* in case the allocator isn't providing these stats, fake them so that\n         * fragmention info still shows some (inaccurate metrics) */\n        if (!server.cron_malloc_stats.allocator_resident) {\n            /* LUA memory isn't part of zmalloc_used, but it is part of the process RSS,\n             * so we must desuct it in order to be able to calculate correct\n             * \"allocator fragmentation\" ratio */\n            size_t lua_memory = lua_gc(server.lua,LUA_GCCOUNT,0)*1024LL;\n            server.cron_malloc_stats.allocator_resident = server.cron_malloc_stats.process_rss - lua_memory;\n        }\n        if (!server.cron_malloc_stats.allocator_active)\n            server.cron_malloc_stats.allocator_active = server.cron_malloc_stats.allocator_resident;\n        if (!server.cron_malloc_stats.allocator_allocated)\n            server.cron_malloc_stats.allocator_allocated = server.cron_malloc_stats.zmalloc_used;\n    }\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    if (server.shutdown_asap) {\n        if (prepareForShutdown(SHUTDOWN_NOFLAGS) == C_OK) exit(0);\n        serverLog(LL_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    run_with_period(5000) {\n        for (j = 0; j < server.dbnum; j++) {\n            long long size, used, vkeys;\n\n            size = dictSlots(server.db[j].dict);\n            used = dictSize(server.db[j].dict);\n            vkeys = dictSize(server.db[j].expires);\n            if (used || vkeys) {\n                serverLog(LL_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    if (!server.sentinel_mode) {\n        run_with_period(5000) {\n            serverLog(LL_DEBUG,\n                \"%lu clients connected (%lu replicas), %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    clientsCron();\n\n    /* Handle background operations on Redis databases. */\n    databasesCron();\n\n    /* Start a scheduled AOF rewrite if this was requested by the user while\n     * a BGSAVE was in progress. */\n    if (!hasActiveChildProcess() &&\n        server.aof_rewrite_scheduled)\n    {\n        rewriteAppendOnlyFileBackground();\n    }\n\n    /* Check if a background saving or AOF rewrite in progress terminated. */\n    if (hasActiveChildProcess() || ldbPendingChildren())\n    {\n        checkChildrenDone();\n    } else {\n        /* If there is not a background saving/rewrite in progress check if\n         * we have to save/rewrite now. */\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             * CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed. */\n            if (server.dirty >= sp->changes &&\n                server.unixtime-server.lastsave > sp->seconds &&\n                (server.unixtime-server.lastbgsave_try >\n                 CONFIG_BGSAVE_RETRY_DELAY ||\n                 server.lastbgsave_status == C_OK))\n            {\n                serverLog(LL_NOTICE,\"%d changes in %d seconds. Saving...\",\n                    sp->changes, (int)sp->seconds);\n                rdbSaveInfo rsi, *rsiptr;\n                rsiptr = rdbPopulateSaveInfo(&rsi);\n                rdbSaveBackground(server.rdb_filename,rsiptr);\n                break;\n            }\n        }\n\n        /* Trigger an AOF rewrite if needed. */\n        if (server.aof_state == AOF_ON &&\n            !hasActiveChildProcess() &&\n            server.aof_rewrite_perc &&\n            server.aof_current_size > server.aof_rewrite_min_size)\n        {\n            long long base = server.aof_rewrite_base_size ?\n                server.aof_rewrite_base_size : 1;\n            long long growth = (server.aof_current_size*100/base) - 100;\n            if (growth >= server.aof_rewrite_perc) {\n                serverLog(LL_NOTICE,\"Starting automatic rewriting of AOF on %lld%% growth\",growth);\n                rewriteAppendOnlyFileBackground();\n            }\n        }\n    }\n\n\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 == C_ERR)\n            flushAppendOnlyFile(0);\n    }\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,\n     * detect transfer failures, start background RDB transfers and so forth. */\n    run_with_period(1000) replicationCron();\n\n    /* Run the Redis Cluster cron. */\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    if (server.sentinel_mode) sentinelTimer();\n\n    /* Cleanup expired MIGRATE cached sockets. */\n    run_with_period(1000) {\n        migrateCloseTimedoutSockets();\n    }\n\n    /* Stop the I/O threads if we don't have enough pending work. */\n    stopThreadedIOIfNeeded();\n\n    /* Resize tracking keys table if needed. This is also done at every\n     * command execution, but we want to be sure that if the last command\n     * executed changes the value via CONFIG SET, the server will perform\n     * the operation even if completely idle. */\n    if (server.tracking_clients) trackingLimitUsedSlots();\n\n    /* Start a scheduled BGSAVE if the corresponding flag is set. This is\n     * useful when we are forced to postpone a BGSAVE because an AOF\n     * rewrite is in progress.\n     *\n     * Note: this code must be after the replicationCron() call above so\n     * make sure when refactoring this file to keep this order. This is useful\n     * because we want to give priority to RDB savings for replication. */\n    if (!hasActiveChildProcess() &&\n        server.rdb_bgsave_scheduled &&\n        (server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||\n         server.lastbgsave_status == C_OK))\n    {\n        rdbSaveInfo rsi, *rsiptr;\n        rsiptr = rdbPopulateSaveInfo(&rsi);\n        if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK)\n            server.rdb_bgsave_scheduled = 0;\n    }\n\n    /* Fire the cron loop modules event. */\n    RedisModuleCronLoopV1 ei = {REDISMODULE_CRON_LOOP_VERSION,server.hz};\n    moduleFireServerEvent(REDISMODULE_EVENT_CRON_LOOP,\n                          0,\n                          &ei);\n\n    server.cronloops++;\n    return 1000/server.hz;\n}\n\nextern int ProcessingEventsWhileBlocked;\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 *\n * Note: This function is (currently) called from two functions:\n * 1. aeMain - The main server loop\n * 2. processEventsWhileBlocked - Process clients during RDB/AOF load\n *\n * If it was called from processEventsWhileBlocked we don't want\n * to perform all actions (For example, we don't want to expire\n * keys), but we do need to perform some actions.\n *\n * The most important is freeClientsInAsyncFreeQueue but we also\n * call some other low-risk functions. */\nvoid beforeSleep(struct aeEventLoop *eventLoop) {\n    UNUSED(eventLoop);\n\n    /* Just call a subset of vital functions in case we are re-entering\n     * the event loop from processEventsWhileBlocked(). Note that in this\n     * case we keep track of the number of events we are processing, since\n     * processEventsWhileBlocked() wants to stop ASAP if there are no longer\n     * events to handle. */\n    if (ProcessingEventsWhileBlocked) {\n        uint64_t processed = 0;\n        processed += handleClientsWithPendingReadsUsingThreads();\n        processed += tlsProcessPendingData();\n        processed += handleClientsWithPendingWrites();\n        processed += freeClientsInAsyncFreeQueue();\n        server.events_processed_while_blocked += processed;\n        return;\n    }\n\n    /* Handle precise timeouts of blocked clients. */\n    handleBlockedClientsTimeout();\n\n    /* We should handle pending reads clients ASAP after event loop. */\n    handleClientsWithPendingReadsUsingThreads();\n\n    /* Handle TLS pending data. (must be done before flushAppendOnlyFile) */\n    tlsProcessPendingData();\n\n    /* If tls still has pending unread data don't sleep at all. */\n    aeSetDontWait(server.el, tlsHasPendingData());\n\n    /* Call the Redis Cluster before sleep function. Note that this function\n     * may change the state of Redis Cluster (from ok to fail or vice versa),\n     * so it's a good idea to call it before serving the unblocked clients\n     * later in this function. */\n    if (server.cluster_enabled) clusterBeforeSleep();\n\n    /* Run a fast expire cycle (the called function will return\n     * ASAP if a fast cycle is not needed). */\n    if (server.active_expire_enabled && server.masterhost == NULL)\n        activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);\n\n    /* Unblock all the clients blocked for synchronous replication\n     * in WAIT. */\n    if (listLength(server.clients_waiting_acks))\n        processClientsWaitingReplicas();\n\n    /* Check if there are clients unblocked by modules that implement\n     * blocking commands. */\n    if (moduleCount()) moduleHandleBlockedClients();\n\n    /* Try to process pending commands for clients that were just unblocked. */\n    if (listLength(server.unblocked_clients))\n        processUnblockedClients();\n\n    /* Send all the slaves an ACK request if at least one client blocked\n     * during the previous event loop iteration. Note that we do this after\n     * processUnblockedClients(), so if there are multiple pipelined WAITs\n     * and the just unblocked WAIT gets blocked again, we don't have to wait\n     * a server cron cycle in absence of other event loop events. See #6623. */\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    /* Send the invalidation messages to clients participating to the\n     * client side caching protocol in broadcasting (BCAST) mode. */\n    trackingBroadcastInvalidationMessages();\n\n    /* Write the AOF buffer on disk */\n    flushAppendOnlyFile(0);\n\n    /* Handle writes with pending output buffers. */\n    handleClientsWithPendingWritesUsingThreads();\n\n    /* Close clients that need to be closed asynchronous */\n    freeClientsInAsyncFreeQueue();\n\n    /* Before we are going to sleep, let the threads access the dataset by\n     * releasing the GIL. Redis main thread will not touch anything at this\n     * time. */\n    if (moduleCount()) moduleReleaseGIL();\n}\n\n/* This function is called immadiately after the event loop multiplexing\n * API returned, and the control is going to soon return to Redis by invoking\n * the different events callbacks. */\nvoid afterSleep(struct aeEventLoop *eventLoop) {\n    UNUSED(eventLoop);\n\n    if (!ProcessingEventsWhileBlocked) {\n        if (moduleCount()) moduleAcquireGIL();\n    }\n}\n\n/* =========================== Server initialization ======================== */\n\nvoid createSharedObjects(void) {\n    int j;\n\n    shared.crlf = createObject(OBJ_STRING,sdsnew(\"\\r\\n\"));\n    shared.ok = createObject(OBJ_STRING,sdsnew(\"+OK\\r\\n\"));\n    shared.err = createObject(OBJ_STRING,sdsnew(\"-ERR\\r\\n\"));\n    shared.emptybulk = createObject(OBJ_STRING,sdsnew(\"$0\\r\\n\\r\\n\"));\n    shared.czero = createObject(OBJ_STRING,sdsnew(\":0\\r\\n\"));\n    shared.cone = createObject(OBJ_STRING,sdsnew(\":1\\r\\n\"));\n    shared.emptyarray = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.pong = createObject(OBJ_STRING,sdsnew(\"+PONG\\r\\n\"));\n    shared.queued = createObject(OBJ_STRING,sdsnew(\"+QUEUED\\r\\n\"));\n    shared.emptyscan = createObject(OBJ_STRING,sdsnew(\"*2\\r\\n$1\\r\\n0\\r\\n*0\\r\\n\"));\n    shared.wrongtypeerr = createObject(OBJ_STRING,sdsnew(\n        \"-WRONGTYPE Operation against a key holding the wrong kind of value\\r\\n\"));\n    shared.nokeyerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR no such key\\r\\n\"));\n    shared.syntaxerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR syntax error\\r\\n\"));\n    shared.sameobjecterr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR source and destination objects are the same\\r\\n\"));\n    shared.outofrangeerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR index out of range\\r\\n\"));\n    shared.noscripterr = createObject(OBJ_STRING,sdsnew(\n        \"-NOSCRIPT No matching script. Please use EVAL.\\r\\n\"));\n    shared.loadingerr = createObject(OBJ_STRING,sdsnew(\n        \"-LOADING Redis is loading the dataset in memory\\r\\n\"));\n    shared.slowscripterr = createObject(OBJ_STRING,sdsnew(\n        \"-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\\r\\n\"));\n    shared.masterdownerr = createObject(OBJ_STRING,sdsnew(\n        \"-MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'.\\r\\n\"));\n    shared.bgsaveerr = createObject(OBJ_STRING,sdsnew(\n        \"-MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.\\r\\n\"));\n    shared.roslaveerr = createObject(OBJ_STRING,sdsnew(\n        \"-READONLY You can't write against a read only replica.\\r\\n\"));\n    shared.noautherr = createObject(OBJ_STRING,sdsnew(\n        \"-NOAUTH Authentication required.\\r\\n\"));\n    shared.oomerr = createObject(OBJ_STRING,sdsnew(\n        \"-OOM command not allowed when used memory > 'maxmemory'.\\r\\n\"));\n    shared.execaborterr = createObject(OBJ_STRING,sdsnew(\n        \"-EXECABORT Transaction discarded because of previous errors.\\r\\n\"));\n    shared.noreplicaserr = createObject(OBJ_STRING,sdsnew(\n        \"-NOREPLICAS Not enough good replicas to write.\\r\\n\"));\n    shared.busykeyerr = createObject(OBJ_STRING,sdsnew(\n        \"-BUSYKEY Target key name already exists.\\r\\n\"));\n    shared.space = createObject(OBJ_STRING,sdsnew(\" \"));\n    shared.colon = createObject(OBJ_STRING,sdsnew(\":\"));\n    shared.plus = createObject(OBJ_STRING,sdsnew(\"+\"));\n\n    /* The shared NULL depends on the protocol version. */\n    shared.null[0] = NULL;\n    shared.null[1] = NULL;\n    shared.null[2] = createObject(OBJ_STRING,sdsnew(\"$-1\\r\\n\"));\n    shared.null[3] = createObject(OBJ_STRING,sdsnew(\"_\\r\\n\"));\n\n    shared.nullarray[0] = NULL;\n    shared.nullarray[1] = NULL;\n    shared.nullarray[2] = createObject(OBJ_STRING,sdsnew(\"*-1\\r\\n\"));\n    shared.nullarray[3] = createObject(OBJ_STRING,sdsnew(\"_\\r\\n\"));\n\n    shared.emptymap[0] = NULL;\n    shared.emptymap[1] = NULL;\n    shared.emptymap[2] = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.emptymap[3] = createObject(OBJ_STRING,sdsnew(\"%0\\r\\n\"));\n\n    shared.emptyset[0] = NULL;\n    shared.emptyset[1] = NULL;\n    shared.emptyset[2] = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.emptyset[3] = createObject(OBJ_STRING,sdsnew(\"~0\\r\\n\"));\n\n    for (j = 0; j < PROTO_SHARED_SELECT_CMDS; j++) {\n        char dictid_str[64];\n        int dictid_len;\n\n        dictid_len = ll2string(dictid_str,sizeof(dictid_str),j);\n        shared.select[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\n                \"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n                dictid_len, dictid_str));\n    }\n    shared.messagebulk = createStringObject(\"$7\\r\\nmessage\\r\\n\",13);\n    shared.pmessagebulk = createStringObject(\"$8\\r\\npmessage\\r\\n\",14);\n    shared.subscribebulk = createStringObject(\"$9\\r\\nsubscribe\\r\\n\",15);\n    shared.unsubscribebulk = createStringObject(\"$11\\r\\nunsubscribe\\r\\n\",18);\n    shared.psubscribebulk = createStringObject(\"$10\\r\\npsubscribe\\r\\n\",17);\n    shared.punsubscribebulk = createStringObject(\"$12\\r\\npunsubscribe\\r\\n\",19);\n    shared.del = createStringObject(\"DEL\",3);\n    shared.unlink = createStringObject(\"UNLINK\",6);\n    shared.rpop = createStringObject(\"RPOP\",4);\n    shared.lpop = createStringObject(\"LPOP\",4);\n    shared.lpush = createStringObject(\"LPUSH\",5);\n    shared.rpoplpush = createStringObject(\"RPOPLPUSH\",9);\n    shared.zpopmin = createStringObject(\"ZPOPMIN\",7);\n    shared.zpopmax = createStringObject(\"ZPOPMAX\",7);\n    shared.multi = createStringObject(\"MULTI\",5);\n    shared.exec = createStringObject(\"EXEC\",4);\n    for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {\n        shared.integers[j] =\n            makeObjectShared(createObject(OBJ_STRING,(void*)(long)j));\n        shared.integers[j]->encoding = OBJ_ENCODING_INT;\n    }\n    for (j = 0; j < OBJ_SHARED_BULKHDR_LEN; j++) {\n        shared.mbulkhdr[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\"*%d\\r\\n\",j));\n        shared.bulkhdr[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\"$%d\\r\\n\",j));\n    }\n    /* The following two shared objects, minstring and maxstrings, are not\n     * actually used for their value but as a special object meaning\n     * respectively the minimum possible string and the maximum possible\n     * string in string comparisons for the ZRANGEBYLEX command. */\n    shared.minstring = sdsnew(\"minstring\");\n    shared.maxstring = sdsnew(\"maxstring\");\n}\n\nvoid initServerConfig(void) {\n    int j;\n\n    updateCachedTime(1);\n    getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE);\n    server.runid[CONFIG_RUN_ID_SIZE] = '\\0';\n    changeReplicationId();\n    clearReplicationId2();\n    server.hz = CONFIG_DEFAULT_HZ; /* Initialize it ASAP, even if it may get\n                                      updated later after loading the config.\n                                      This value may be used before the server\n                                      is initialized. */\n    server.timezone = getTimeZone(); /* Initialized by tzset(). */\n    server.configfile = NULL;\n    server.executable = NULL;\n    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;\n    server.bindaddr_count = 0;\n    server.unixsocketperm = CONFIG_DEFAULT_UNIX_SOCKET_PERM;\n    server.ipfd_count = 0;\n    server.tlsfd_count = 0;\n    server.sofd = -1;\n    server.active_expire_enabled = 1;\n    server.client_max_querybuf_len = PROTO_MAX_QUERYBUF_LEN;\n    server.saveparams = NULL;\n    server.loading = 0;\n    server.logfile = zstrdup(CONFIG_DEFAULT_LOGFILE);\n    server.aof_state = AOF_OFF;\n    server.aof_rewrite_base_size = 0;\n    server.aof_rewrite_scheduled = 0;\n    server.aof_flush_sleep = 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 = C_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.pidfile = NULL;\n    server.active_defrag_running = 0;\n    server.notify_keyspace_events = 0;\n    server.blocked_clients = 0;\n    memset(server.blocked_clients_by_type,0,\n           sizeof(server.blocked_clients_by_type));\n    server.shutdown_asap = 0;\n    server.cluster_configfile = zstrdup(CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);\n    server.cluster_module_flags = CLUSTER_MODULE_FLAG_NONE;\n    server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL);\n    server.next_client_id = 1; /* Client IDs, start from 1 .*/\n    server.loading_process_events_interval_bytes = (1024*1024*2);\n\n    server.lruclock = getLRUClock();\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    server.masterauth = NULL;\n    server.masterhost = NULL;\n    server.masterport = 6379;\n    server.master = NULL;\n    server.cached_master = NULL;\n    server.master_initial_offset = -1;\n    server.repl_state = REPL_STATE_NONE;\n    server.repl_transfer_tmpfile = NULL;\n    server.repl_transfer_fd = -1;\n    server.repl_transfer_s = NULL;\n    server.repl_syncio_timeout = CONFIG_REPL_SYNCIO_TIMEOUT;\n    server.repl_down_since = 0; /* Never connected, repl is down since EVER. */\n    server.master_repl_offset = 0;\n    server.master_repl_meaningful_offset = 0;\n\n    /* Replication partial resync backlog */\n    server.repl_backlog = NULL;\n    server.repl_backlog_histlen = 0;\n    server.repl_backlog_idx = 0;\n    server.repl_backlog_off = 0;\n    server.repl_no_slaves_since = time(NULL);\n\n    /* Client output buffer limits */\n    for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++)\n        server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];\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    /* 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    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    server.zpopminCommand = lookupCommandByCString(\"zpopmin\");\n    server.zpopmaxCommand = lookupCommandByCString(\"zpopmax\");\n    server.sremCommand = lookupCommandByCString(\"srem\");\n    server.execCommand = lookupCommandByCString(\"exec\");\n    server.expireCommand = lookupCommandByCString(\"expire\");\n    server.pexpireCommand = lookupCommandByCString(\"pexpire\");\n    server.xclaimCommand = lookupCommandByCString(\"xclaim\");\n    server.xgroupCommand = lookupCommandByCString(\"xgroup\");\n    server.rpoplpushCommand = lookupCommandByCString(\"rpoplpush\");\n\n    /* Debugging */\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    /* By default we want scripts to be always replicated by effects\n     * (single commands executed by the script), and not by sending the\n     * script to the slave / AOF. This is the new way starting from\n     * Redis 5. However it is possible to revert it via redis.conf. */\n    server.lua_always_replicate_commands = 1;\n\n    initConfigValues();\n}\n\nextern char **environ;\n\n/* Restart the server, executing the same executable that started this\n * instance, with the same arguments and configuration file.\n *\n * The function is designed to directly call execve() so that the new\n * server instance will retain the PID of the previous one.\n *\n * The list of flags, that may be bitwise ORed together, alter the\n * behavior of this function:\n *\n * RESTART_SERVER_NONE              No flags.\n * RESTART_SERVER_GRACEFULLY        Do a proper shutdown before restarting.\n * RESTART_SERVER_CONFIG_REWRITE    Rewrite the config file before restarting.\n *\n * On success the function does not return, because the process turns into\n * a different process. On error C_ERR is returned. */\nint restartServer(int flags, mstime_t delay) {\n    int j;\n\n    /* Check if we still have accesses to the executable that started this\n     * server instance. */\n    if (access(server.executable,X_OK) == -1) {\n        serverLog(LL_WARNING,\"Can't restart: this process has no \"\n                             \"permissions to execute %s\", server.executable);\n        return C_ERR;\n    }\n\n    /* Config rewriting. */\n    if (flags & RESTART_SERVER_CONFIG_REWRITE &&\n        server.configfile &&\n        rewriteConfig(server.configfile) == -1)\n    {\n        serverLog(LL_WARNING,\"Can't restart: configuration rewrite process \"\n                             \"failed\");\n        return C_ERR;\n    }\n\n    /* Perform a proper shutdown. */\n    if (flags & RESTART_SERVER_GRACEFULLY &&\n        prepareForShutdown(SHUTDOWN_NOFLAGS) != C_OK)\n    {\n        serverLog(LL_WARNING,\"Can't restart: error preparing for shutdown\");\n        return C_ERR;\n    }\n\n    /* Close all file descriptors, with the exception of stdin, stdout, strerr\n     * which are useful if we restart a Redis server which is not daemonized. */\n    for (j = 3; j < (int)server.maxclients + 1024; j++) {\n        /* Test the descriptor validity before closing it, otherwise\n         * Valgrind issues a warning on close(). */\n        if (fcntl(j,F_GETFD) != -1) close(j);\n    }\n\n    /* Execute the server with the original command line. */\n    if (delay) usleep(delay*1000);\n    zfree(server.exec_argv[0]);\n    server.exec_argv[0] = zstrdup(server.executable);\n    execve(server.executable,server.exec_argv,environ);\n\n    /* If an error occurred here, there is nothing we can do, but exit. */\n    _exit(1);\n\n    return C_ERR; /* Never reached. */\n}\n\n/* This function will try to raise the max number of open files accordingly to\n * the configured max number of clients. It also reserves a number of file\n * descriptors (CONFIG_MIN_RESERVED_FDS) for extra operations of\n * persistence, listening sockets, log files and so forth.\n *\n * If it will not be possible to set the limit accordingly to the configured\n * max number of clients, the function will do the reverse setting\n * server.maxclients to the value that we can actually handle. */\nvoid adjustOpenFilesLimit(void) {\n    rlim_t maxfiles = server.maxclients+CONFIG_MIN_RESERVED_FDS;\n    struct rlimit limit;\n\n    if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {\n        serverLog(LL_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-CONFIG_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 bestlimit;\n            int setrlimit_error = 0;\n\n            /* Try to set the file limit to match 'maxfiles' or at least\n             * to the higher value supported less than maxfiles. */\n            bestlimit = maxfiles;\n            while(bestlimit > oldlimit) {\n                rlim_t decr_step = 16;\n\n                limit.rlim_cur = bestlimit;\n                limit.rlim_max = bestlimit;\n                if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;\n                setrlimit_error = errno;\n\n                /* We failed to set file limit to 'bestlimit'. Try with a\n                 * smaller limit decrementing by a few FDs per iteration. */\n                if (bestlimit < decr_step) break;\n                bestlimit -= decr_step;\n            }\n\n            /* Assume that the limit we get initially is still valid if\n             * our last try was even lower. */\n            if (bestlimit < oldlimit) bestlimit = oldlimit;\n\n            if (bestlimit < maxfiles) {\n                unsigned int old_maxclients = server.maxclients;\n                server.maxclients = bestlimit-CONFIG_MIN_RESERVED_FDS;\n                /* maxclients is unsigned so may overflow: in order\n                 * to check if maxclients is now logically less than 1\n                 * we test indirectly via bestlimit. */\n                if (bestlimit <= CONFIG_MIN_RESERVED_FDS) {\n                    serverLog(LL_WARNING,\"Your current 'ulimit -n' \"\n                        \"of %llu is not enough for the server to start. \"\n                        \"Please increase your open file limit to at least \"\n                        \"%llu. Exiting.\",\n                        (unsigned long long) oldlimit,\n                        (unsigned long long) maxfiles);\n                    exit(1);\n                }\n                serverLog(LL_WARNING,\"You requested maxclients of %d \"\n                    \"requiring at least %llu max file descriptors.\",\n                    old_maxclients,\n                    (unsigned long long) maxfiles);\n                serverLog(LL_WARNING,\"Server can't set maximum open files \"\n                    \"to %llu because of OS error: %s.\",\n                    (unsigned long long) maxfiles, strerror(setrlimit_error));\n                serverLog(LL_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) bestlimit, server.maxclients);\n            } else {\n                serverLog(LL_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/* Check that server.tcp_backlog can be actually enforced in Linux according\n * to the value of /proc/sys/net/core/somaxconn, or warn about it. */\nvoid checkTcpBacklogSettings(void) {\n#ifdef HAVE_PROC_SOMAXCONN\n    FILE *fp = fopen(\"/proc/sys/net/core/somaxconn\",\"r\");\n    char buf[1024];\n    if (!fp) return;\n    if (fgets(buf,sizeof(buf),fp) != NULL) {\n        int somaxconn = atoi(buf);\n        if (somaxconn > 0 && somaxconn < server.tcp_backlog) {\n            serverLog(LL_WARNING,\"WARNING: The TCP backlog setting of %d cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of %d.\", server.tcp_backlog, somaxconn);\n        }\n    }\n    fclose(fp);\n#endif\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 C_OK.\n *\n * On error the function returns C_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            int unsupported = 0;\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            } else if (errno == EAFNOSUPPORT) {\n                unsupported++;\n                serverLog(LL_WARNING,\"Not listening to IPv6: unsupported\");\n            }\n\n            if (*count == 1 || unsupported) {\n                /* Bind the IPv4 address as well. */\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                } else if (errno == EAFNOSUPPORT) {\n                    unsupported++;\n                    serverLog(LL_WARNING,\"Not listening to IPv4: unsupported\");\n                }\n            }\n            /* Exit the loop if we were able to bind * on IPv4 and 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 + unsupported == 2) 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            serverLog(LL_WARNING,\n                \"Could not create server TCP listening socket %s:%d: %s\",\n                server.bindaddr[j] ? server.bindaddr[j] : \"*\",\n                port, server.neterr);\n                if (errno == ENOPROTOOPT     || errno == EPROTONOSUPPORT ||\n                    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||\n                    errno == EAFNOSUPPORT    || errno == EADDRNOTAVAIL)\n                    continue;\n            return C_ERR;\n        }\n        anetNonBlock(NULL,fds[*count]);\n        (*count)++;\n    }\n    return C_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    int j;\n\n    server.stat_numcommands = 0;\n    server.stat_numconnections = 0;\n    server.stat_expiredkeys = 0;\n    server.stat_expired_stale_perc = 0;\n    server.stat_expired_time_cap_reached_count = 0;\n    server.stat_expire_cycle_time_used = 0;\n    server.stat_evictedkeys = 0;\n    server.stat_keyspace_misses = 0;\n    server.stat_keyspace_hits = 0;\n    server.stat_active_defrag_hits = 0;\n    server.stat_active_defrag_misses = 0;\n    server.stat_active_defrag_key_hits = 0;\n    server.stat_active_defrag_key_misses = 0;\n    server.stat_active_defrag_scanned = 0;\n    server.stat_fork_time = 0;\n    server.stat_fork_rate = 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    for (j = 0; j < STATS_METRIC_COUNT; j++) {\n        server.inst_metric[j].idx = 0;\n        server.inst_metric[j].last_sample_time = mstime();\n        server.inst_metric[j].last_sample_count = 0;\n        memset(server.inst_metric[j].samples,0,\n            sizeof(server.inst_metric[j].samples));\n    }\n    server.stat_net_input_bytes = 0;\n    server.stat_net_output_bytes = 0;\n    server.stat_unexpected_error_replies = 0;\n    server.aof_delayed_fsync = 0;\n}\n\nvoid initServer(void) {\n    int j;\n\n    signal(SIGHUP, SIG_IGN);\n    signal(SIGPIPE, SIG_IGN);\n    setupSignalHandlers();\n\n    if (server.syslog_enabled) {\n        openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,\n            server.syslog_facility);\n    }\n\n    /* Initialization after setting defaults from the config system. */\n    server.aof_state = server.aof_enabled ? AOF_ON : AOF_OFF;\n    server.hz = server.config_hz;\n    server.pid = getpid();\n    server.current_client = NULL;\n    server.fixed_time_expire = 0;\n    server.clients = listCreate();\n    server.clients_index = raxNew();\n    server.clients_to_close = listCreate();\n    server.slaves = listCreate();\n    server.monitors = listCreate();\n    server.clients_pending_write = listCreate();\n    server.clients_pending_read = listCreate();\n    server.clients_timeout_table = raxNew();\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    server.events_processed_while_blocked = 0;\n    server.system_memory_size = zmalloc_get_memory_size();\n\n    if (server.tls_port && tlsConfigure(&server.tls_ctx_config) == C_ERR) {\n        serverLog(LL_WARNING, \"Failed to configure TLS. Check logs for more info.\");\n        exit(1);\n    }\n\n    createSharedObjects();\n    adjustOpenFilesLimit();\n    server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);\n    if (server.el == NULL) {\n        serverLog(LL_WARNING,\n            \"Failed creating the event loop. Error message: '%s'\",\n            strerror(errno));\n        exit(1);\n    }\n    server.db = zmalloc(sizeof(redisDb)*server.dbnum);\n\n    /* Open the TCP listening socket for the user commands. */\n    if (server.port != 0 &&\n        listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)\n        exit(1);\n    if (server.tls_port != 0 &&\n        listenToPort(server.tls_port,server.tlsfd,&server.tlsfd_count) == C_ERR)\n        exit(1);\n\n    /* Open the listening Unix domain socket. */\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            serverLog(LL_WARNING, \"Opening Unix 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.tlsfd_count == 0 && server.sofd < 0) {\n        serverLog(LL_WARNING, \"Configured to not listen anywhere, exiting.\");\n        exit(1);\n    }\n\n    /* Create the Redis databases, and initialize other internal state. */\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].expires_cursor = 0;\n        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);\n        server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);\n        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);\n        server.db[j].id = j;\n        server.db[j].avg_ttl = 0;\n        server.db[j].defrag_later = listCreate();\n        listSetFreeMethod(server.db[j].defrag_later,(void (*)(void*))sdsfree);\n    }\n    evictionPoolAlloc(); /* Initialize the LRU keys pool. */\n    server.pubsub_channels = dictCreate(&keylistDictType,NULL);\n    server.pubsub_patterns = listCreate();\n    server.pubsub_patterns_dict = dictCreate(&keylistDictType,NULL);\n    listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);\n    listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);\n    server.cronloops = 0;\n    server.rdb_child_pid = -1;\n    server.aof_child_pid = -1;\n    server.module_child_pid = -1;\n    server.rdb_child_type = RDB_CHILD_TYPE_NONE;\n    server.rdb_pipe_conns = NULL;\n    server.rdb_pipe_numconns = 0;\n    server.rdb_pipe_numconns_writing = 0;\n    server.rdb_pipe_buff = NULL;\n    server.rdb_pipe_bufflen = 0;\n    server.rdb_bgsave_scheduled = 0;\n    server.child_info_pipe[0] = -1;\n    server.child_info_pipe[1] = -1;\n    server.child_info_data.magic = 0;\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.stat_rdb_cow_bytes = 0;\n    server.stat_aof_cow_bytes = 0;\n    server.stat_module_cow_bytes = 0;\n    for (int j = 0; j < CLIENT_TYPE_COUNT; j++)\n        server.stat_clients_type_memory[j] = 0;\n    server.cron_malloc_stats.zmalloc_used = 0;\n    server.cron_malloc_stats.process_rss = 0;\n    server.cron_malloc_stats.allocator_allocated = 0;\n    server.cron_malloc_stats.allocator_active = 0;\n    server.cron_malloc_stats.allocator_resident = 0;\n    server.lastbgsave_status = C_OK;\n    server.aof_last_write_status = C_OK;\n    server.aof_last_write_errno = 0;\n    server.repl_good_slaves_count = 0;\n\n    /* Create the timer callback, this is our way to process many background\n     * operations incrementally, like clients timeout, eviction of unaccessed\n     * expired keys and so forth. */\n    if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {\n        serverPanic(\"Can't create event loop timers.\");\n        exit(1);\n    }\n\n    /* Create an event handler for accepting new connections in TCP and Unix\n     * domain sockets. */\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                serverPanic(\n                    \"Unrecoverable error creating server.ipfd file event.\");\n            }\n    }\n    for (j = 0; j < server.tlsfd_count; j++) {\n        if (aeCreateFileEvent(server.el, server.tlsfd[j], AE_READABLE,\n            acceptTLSHandler,NULL) == AE_ERR)\n            {\n                serverPanic(\n                    \"Unrecoverable error creating server.tlsfd file event.\");\n            }\n    }\n    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,\n        acceptUnixHandler,NULL) == AE_ERR) serverPanic(\"Unrecoverable error creating server.sofd file event.\");\n\n\n    /* Register a readable event for the pipe used to awake the event loop\n     * when a blocked client in a module needs attention. */\n    if (aeCreateFileEvent(server.el, server.module_blocked_pipe[0], AE_READABLE,\n        moduleBlockedClientPipeReadable,NULL) == AE_ERR) {\n            serverPanic(\n                \"Error registering the readable event for the module \"\n                \"blocked clients subsystem.\");\n    }\n\n    /* Register before and after sleep handlers (note this needs to be done\n     * before loading persistence since it is used by processEventsWhileBlocked. */\n    aeSetBeforeSleepProc(server.el,beforeSleep);\n    aeSetAfterSleepProc(server.el,afterSleep);\n\n    /* Open the AOF file if needed. */\n    if (server.aof_state == 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            serverLog(LL_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    if (server.arch_bits == 32 && server.maxmemory == 0) {\n        serverLog(LL_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 = MAXMEMORY_NO_EVICTION;\n    }\n\n    if (server.cluster_enabled) clusterInit();\n    replicationScriptCacheInit();\n    scriptingInit(1);\n    slowlogInit();\n    latencyMonitorInit();\n}\n\n/* Some steps in server initialization need to be done last (after modules\n * are loaded).\n * Specifically, creation of threads due to a race bug in ld.so, in which\n * Thread Local Storage initialization collides with dlopen call.\n * see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */\nvoid InitServerLast() {\n    bioInit();\n    initThreadedIO();\n    set_jemalloc_bg_thread(server.jemalloc_bg_thread);\n    server.initial_memory_usage = zmalloc_used_memory();\n}\n\n/* Parse the flags string description 'strflags' and set them to the\n * command 'c'. If the flags are all valid C_OK is returned, otherwise\n * C_ERR is returned (yet the recognized flags are set in the command). */\nint populateCommandTableParseFlags(struct redisCommand *c, char *strflags) {\n    int argc;\n    sds *argv;\n\n    /* Split the line into arguments for processing. */\n    argv = sdssplitargs(strflags,&argc);\n    if (argv == NULL) return C_ERR;\n\n    for (int j = 0; j < argc; j++) {\n        char *flag = argv[j];\n        if (!strcasecmp(flag,\"write\")) {\n            c->flags |= CMD_WRITE|CMD_CATEGORY_WRITE;\n        } else if (!strcasecmp(flag,\"read-only\")) {\n            c->flags |= CMD_READONLY|CMD_CATEGORY_READ;\n        } else if (!strcasecmp(flag,\"use-memory\")) {\n            c->flags |= CMD_DENYOOM;\n        } else if (!strcasecmp(flag,\"admin\")) {\n            c->flags |= CMD_ADMIN|CMD_CATEGORY_ADMIN|CMD_CATEGORY_DANGEROUS;\n        } else if (!strcasecmp(flag,\"pub-sub\")) {\n            c->flags |= CMD_PUBSUB|CMD_CATEGORY_PUBSUB;\n        } else if (!strcasecmp(flag,\"no-script\")) {\n            c->flags |= CMD_NOSCRIPT;\n        } else if (!strcasecmp(flag,\"random\")) {\n            c->flags |= CMD_RANDOM;\n        } else if (!strcasecmp(flag,\"to-sort\")) {\n            c->flags |= CMD_SORT_FOR_SCRIPT;\n        } else if (!strcasecmp(flag,\"ok-loading\")) {\n            c->flags |= CMD_LOADING;\n        } else if (!strcasecmp(flag,\"ok-stale\")) {\n            c->flags |= CMD_STALE;\n        } else if (!strcasecmp(flag,\"no-monitor\")) {\n            c->flags |= CMD_SKIP_MONITOR;\n        } else if (!strcasecmp(flag,\"no-slowlog\")) {\n            c->flags |= CMD_SKIP_SLOWLOG;\n        } else if (!strcasecmp(flag,\"cluster-asking\")) {\n            c->flags |= CMD_ASKING;\n        } else if (!strcasecmp(flag,\"fast\")) {\n            c->flags |= CMD_FAST | CMD_CATEGORY_FAST;\n        } else if (!strcasecmp(flag,\"no-auth\")) {\n            c->flags |= CMD_NO_AUTH;\n        } else {\n            /* Parse ACL categories here if the flag name starts with @. */\n            uint64_t catflag;\n            if (flag[0] == '@' &&\n                (catflag = ACLGetCommandCategoryFlagByName(flag+1)) != 0)\n            {\n                c->flags |= catflag;\n            } else {\n                sdsfreesplitres(argv,argc);\n                return C_ERR;\n            }\n        }\n    }\n    /* If it's not @fast is @slow in this binary world. */\n    if (!(c->flags & CMD_CATEGORY_FAST)) c->flags |= CMD_CATEGORY_SLOW;\n\n    sdsfreesplitres(argv,argc);\n    return C_OK;\n}\n\n/* Populates the Redis Command Table starting from the hard coded list\n * we have on top of redis.c file. */\nvoid populateCommandTable(void) {\n    int j;\n    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n\n    for (j = 0; j < numcommands; j++) {\n        struct redisCommand *c = redisCommandTable+j;\n        int retval1, retval2;\n\n        /* Translate the command string flags description into an actual\n         * set of flags. */\n        if (populateCommandTableParseFlags(c,c->sflags) == C_ERR)\n            serverPanic(\"Unsupported command flag\");\n\n        c->id = ACLGetCommandID(c->name); /* Assign the ID used for ACL. */\n        retval1 = dictAdd(server.commands, sdsnew(c->name), c);\n        /* Populate an additional dictionary that will be unaffected\n         * by rename-command statements in redis.conf. */\n        retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);\n        serverAssert(retval1 == DICT_OK && retval2 == DICT_OK);\n    }\n}\n\nvoid resetCommandTableStats(void) {\n    struct redisCommand *c;\n    dictEntry *de;\n    dictIterator *di;\n\n    di = dictGetSafeIterator(server.commands);\n    while((de = dictNext(di)) != NULL) {\n        c = (struct redisCommand *) dictGetVal(de);\n        c->microseconds = 0;\n        c->calls = 0;\n    }\n    dictReleaseIterator(di);\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\nstruct redisCommand *lookupCommand(sds name) {\n    return dictFetchValue(server.commands, name);\n}\n\nstruct redisCommand *lookupCommandByCString(char *s) {\n    struct redisCommand *cmd;\n    sds name = sdsnew(s);\n\n    cmd = dictFetchValue(server.commands, name);\n    sdsfree(name);\n    return cmd;\n}\n\n/* Lookup the command in the current table, if not found also check in\n * the original table containing the original command names unaffected by\n * redis.conf rename-command statement.\n *\n * This is used by functions rewriting the argument vector such as\n * rewriteClientCommandVector() in order to set client->cmd pointer\n * correctly even if the command was renamed. */\nstruct redisCommand *lookupCommandOrOriginal(sds name) {\n    struct redisCommand *cmd = dictFetchValue(server.commands, name);\n\n    if (!cmd) cmd = dictFetchValue(server.orig_commands,name);\n    return cmd;\n}\n\n/* Propagate the specified command (in the context of the specified database id)\n * to AOF and Slaves.\n *\n * flags are an xor between:\n * + PROPAGATE_NONE (no propagation of command at all)\n * + PROPAGATE_AOF (propagate into the AOF file if is enabled)\n * + PROPAGATE_REPL (propagate into the replication link)\n *\n * This should not be used inside commands implementation since it will not\n * wrap the resulting commands in MULTI/EXEC. Use instead alsoPropagate(),\n * preventCommandPropagation(), forceCommandPropagation().\n *\n * However for functions that need to (also) propagate out of the context of a\n * command execution, for example when serving a blocked client, you\n * want to use propagate().\n */\nvoid propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n               int flags)\n{\n    if (server.aof_state != AOF_OFF && flags & PROPAGATE_AOF)\n        feedAppendOnlyFile(cmd,dbid,argv,argc);\n    if (flags & PROPAGATE_REPL)\n        replicationFeedSlaves(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.\n *\n * 'cmd' must be a pointer to the Redis command to replicate, dbid is the\n * database ID the command should be propagated into.\n * Arguments of the command to propagte are passed as an array of redis\n * objects pointers of len 'argc', using the 'argv' vector.\n *\n * The function does not take a reference to the passed 'argv' vector,\n * so it is up to the caller to release the passed argv (but it is usually\n * stack allocated).  The function autoamtically increments ref count of\n * passed objects, so the caller does not need to. */\nvoid alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n                   int target)\n{\n    robj **argvcopy;\n    int j;\n\n    if (server.loading) return; /* No propagation during loading. */\n\n    argvcopy = zmalloc(sizeof(robj*)*argc);\n    for (j = 0; j < argc; j++) {\n        argvcopy[j] = argv[j];\n        incrRefCount(argv[j]);\n    }\n    redisOpArrayAppend(&server.also_propagate,cmd,dbid,argvcopy,argc,target);\n}\n\n/* It is possible to call the function forceCommandPropagation() inside a\n * Redis command implementation in order to to force the propagation of a\n * specific command execution into AOF / Replication. */\nvoid forceCommandPropagation(client *c, int flags) {\n    if (flags & PROPAGATE_REPL) c->flags |= CLIENT_FORCE_REPL;\n    if (flags & PROPAGATE_AOF) c->flags |= CLIENT_FORCE_AOF;\n}\n\n/* Avoid that the executed command is propagated at all. This way we\n * are free to just propagate what we want using the alsoPropagate()\n * API. */\nvoid preventCommandPropagation(client *c) {\n    c->flags |= CLIENT_PREVENT_PROP;\n}\n\n/* AOF specific version of preventCommandPropagation(). */\nvoid preventCommandAOF(client *c) {\n    c->flags |= CLIENT_PREVENT_AOF_PROP;\n}\n\n/* Replication specific version of preventCommandPropagation(). */\nvoid preventCommandReplication(client *c) {\n    c->flags |= CLIENT_PREVENT_REPL_PROP;\n}\n\n/* Call() is the core of Redis execution of a command.\n *\n * The following flags can be passed:\n * CMD_CALL_NONE        No flags.\n * CMD_CALL_SLOWLOG     Check command speed and log in the slow log if needed.\n * CMD_CALL_STATS       Populate command stats.\n * CMD_CALL_PROPAGATE_AOF   Append command to AOF if it modified the dataset\n *                          or if the client flags are forcing propagation.\n * CMD_CALL_PROPAGATE_REPL  Send command to slaves if it modified the dataset\n *                          or if the client flags are forcing propagation.\n * CMD_CALL_PROPAGATE   Alias for PROPAGATE_AOF|PROPAGATE_REPL.\n * CMD_CALL_FULL        Alias for SLOWLOG|STATS|PROPAGATE.\n *\n * The exact propagation behavior depends on the client flags.\n * Specifically:\n *\n * 1. If the client flags CLIENT_FORCE_AOF or CLIENT_FORCE_REPL are set\n *    and assuming the corresponding CMD_CALL_PROPAGATE_AOF/REPL is set\n *    in the call flags, then the command is propagated even if the\n *    dataset was not affected by the command.\n * 2. If the client flags CLIENT_PREVENT_REPL_PROP or CLIENT_PREVENT_AOF_PROP\n *    are set, the propagation into AOF or to slaves is not performed even\n *    if the command modified the dataset.\n *\n * Note that regardless of the client flags, if CMD_CALL_PROPAGATE_AOF\n * or CMD_CALL_PROPAGATE_REPL are not set, then respectively AOF or\n * slaves propagation will never occur.\n *\n * Client flags are modified by the implementation of a given command\n * using the following API:\n *\n * forceCommandPropagation(client *c, int flags);\n * preventCommandPropagation(client *c);\n * preventCommandAOF(client *c);\n * preventCommandReplication(client *c);\n *\n */\nvoid call(client *c, int flags) {\n    long long dirty;\n    ustime_t start, duration;\n    int client_old_flags = c->flags;\n    struct redisCommand *real_cmd = c->cmd;\n\n    server.fixed_time_expire++;\n\n    /* Send the command to clients in MONITOR mode if applicable.\n     * Administrative commands are considered too dangerous to be shown. */\n    if (listLength(server.monitors) &&\n        !server.loading &&\n        !(c->cmd->flags & (CMD_SKIP_MONITOR|CMD_ADMIN)))\n    {\n        replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);\n    }\n\n    /* Initialization: clear the flags that must be set by the command on\n     * demand, and initialize the array for additional commands propagation. */\n    c->flags &= ~(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n    redisOpArray prev_also_propagate = server.also_propagate;\n    redisOpArrayInit(&server.also_propagate);\n\n    /* Call the command. */\n    dirty = server.dirty;\n    updateCachedTime(0);\n    start = server.ustime;\n    c->cmd->proc(c);\n    duration = ustime()-start;\n    dirty = server.dirty-dirty;\n    if (dirty < 0) dirty = 0;\n\n    /* When EVAL is called loading the AOF we don't want commands called\n     * from Lua to go into the slowlog or to populate statistics. */\n    if (server.loading && c->flags & CLIENT_LUA)\n        flags &= ~(CMD_CALL_SLOWLOG | CMD_CALL_STATS);\n\n    /* If the caller is Lua, we want to force the EVAL caller to propagate\n     * the script if the command flag or client flag are forcing the\n     * propagation. */\n    if (c->flags & CLIENT_LUA && server.lua_caller) {\n        if (c->flags & CLIENT_FORCE_REPL)\n            server.lua_caller->flags |= CLIENT_FORCE_REPL;\n        if (c->flags & CLIENT_FORCE_AOF)\n            server.lua_caller->flags |= CLIENT_FORCE_AOF;\n    }\n\n    /* Log the command into the Slow log if needed, and populate the\n     * per-command statistics that we show in INFO commandstats. */\n    if (flags & CMD_CALL_SLOWLOG && !(c->cmd->flags & CMD_SKIP_SLOWLOG)) {\n        char *latency_event = (c->cmd->flags & CMD_FAST) ?\n                              \"fast-command\" : \"command\";\n        latencyAddSampleIfNeeded(latency_event,duration/1000);\n        slowlogPushEntryIfNeeded(c,c->argv,c->argc,duration);\n    }\n\n    if (flags & CMD_CALL_STATS) {\n        /* use the real command that was executed (cmd and lastamc) may be\n         * different, in case of MULTI-EXEC or re-written commands such as\n         * EXPIRE, GEOADD, etc. */\n        real_cmd->microseconds += duration;\n        real_cmd->calls++;\n    }\n\n    /* Propagate the command into the AOF and replication link */\n    if (flags & CMD_CALL_PROPAGATE &&\n        (c->flags & CLIENT_PREVENT_PROP) != CLIENT_PREVENT_PROP)\n    {\n        int propagate_flags = PROPAGATE_NONE;\n\n        /* Check if the command operated changes in the data set. If so\n         * set for replication / AOF propagation. */\n        if (dirty) propagate_flags |= (PROPAGATE_AOF|PROPAGATE_REPL);\n\n        /* If the client forced AOF / replication of the command, set\n         * the flags regardless of the command effects on the data set. */\n        if (c->flags & CLIENT_FORCE_REPL) propagate_flags |= PROPAGATE_REPL;\n        if (c->flags & CLIENT_FORCE_AOF) propagate_flags |= PROPAGATE_AOF;\n\n        /* However prevent AOF / replication propagation if the command\n         * implementations called preventCommandPropagation() or similar,\n         * or if we don't have the call() flags to do so. */\n        if (c->flags & CLIENT_PREVENT_REPL_PROP ||\n            !(flags & CMD_CALL_PROPAGATE_REPL))\n                propagate_flags &= ~PROPAGATE_REPL;\n        if (c->flags & CLIENT_PREVENT_AOF_PROP ||\n            !(flags & CMD_CALL_PROPAGATE_AOF))\n                propagate_flags &= ~PROPAGATE_AOF;\n\n        /* Call propagate() only if at least one of AOF / replication\n         * propagation is needed. Note that modules commands handle replication\n         * in an explicit way, so we never replicate them automatically. */\n        if (propagate_flags != PROPAGATE_NONE && !(c->cmd->flags & CMD_MODULE))\n            propagate(c->cmd,c->db->id,c->argv,c->argc,propagate_flags);\n    }\n\n    /* Restore the old replication flags, since call() can be executed\n     * recursively. */\n    c->flags &= ~(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n    c->flags |= client_old_flags &\n        (CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n\n    /* Handle the alsoPropagate() API to handle commands that want to propagate\n     * multiple separated commands. Note that alsoPropagate() is not affected\n     * by CLIENT_PREVENT_PROP flag. */\n    if (server.also_propagate.numops) {\n        int j;\n        redisOp *rop;\n\n        if (flags & CMD_CALL_PROPAGATE) {\n            int multi_emitted = 0;\n            /* Wrap the commands in server.also_propagate array,\n             * but don't wrap it if we are already in MULTI context,\n             * in case the nested MULTI/EXEC.\n             *\n             * And if the array contains only one command, no need to\n             * wrap it, since the single command is atomic. */\n            if (server.also_propagate.numops > 1 &&\n                !(c->cmd->flags & CMD_MODULE) &&\n                !(c->flags & CLIENT_MULTI) &&\n                !(flags & CMD_CALL_NOWRAP))\n            {\n                execCommandPropagateMulti(c);\n                multi_emitted = 1;\n            }\n\n            for (j = 0; j < server.also_propagate.numops; j++) {\n                rop = &server.also_propagate.ops[j];\n                int target = rop->target;\n                /* Whatever the command wish is, we honor the call() flags. */\n                if (!(flags&CMD_CALL_PROPAGATE_AOF)) target &= ~PROPAGATE_AOF;\n                if (!(flags&CMD_CALL_PROPAGATE_REPL)) target &= ~PROPAGATE_REPL;\n                if (target)\n                    propagate(rop->cmd,rop->dbid,rop->argv,rop->argc,target);\n            }\n\n            if (multi_emitted) {\n                execCommandPropagateExec(c);\n            }\n        }\n        redisOpArrayFree(&server.also_propagate);\n    }\n    server.also_propagate = prev_also_propagate;\n\n    /* If the client has keys tracking enabled for client side caching,\n     * make sure to remember the keys it fetched via this command. */\n    if (c->cmd->flags & CMD_READONLY) {\n        client *caller = (c->flags & CLIENT_LUA && server.lua_caller) ?\n                            server.lua_caller : c;\n        if (caller->flags & CLIENT_TRACKING &&\n            !(caller->flags & CLIENT_TRACKING_BCAST))\n        {\n            trackingRememberKeys(caller);\n        }\n    }\n\n    server.fixed_time_expire--;\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 * If C_OK is returned the client is still alive and valid and\n * other operations can be performed by the caller. Otherwise\n * if C_ERR is returned the client was destroyed (i.e. after QUIT). */\nint processCommand(client *c) {\n    moduleCallCommandFilters(c);\n\n    /* The QUIT command is handled separately. Normal command procs will\n     * go through checking for replication and QUIT will cause trouble\n     * when FORCE_REPLICATION is enabled and would be implemented in\n     * a regular command proc. */\n    if (!strcasecmp(c->argv[0]->ptr,\"quit\")) {\n        addReply(c,shared.ok);\n        c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n        return C_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    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);\n    if (!c->cmd) {\n        flagTransaction(c);\n        sds args = sdsempty();\n        int i;\n        for (i=1; i < c->argc && sdslen(args) < 128; i++)\n            args = sdscatprintf(args, \"`%.*s`, \", 128-(int)sdslen(args), (char*)c->argv[i]->ptr);\n        addReplyErrorFormat(c,\"unknown command `%s`, with args beginning with: %s\",\n            (char*)c->argv[0]->ptr, args);\n        sdsfree(args);\n        return C_OK;\n    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||\n               (c->argc < -c->cmd->arity)) {\n        flagTransaction(c);\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return C_OK;\n    }\n\n    /* Check if the user is authenticated. This check is skipped in case\n     * the default user is flagged as \"nopass\" and is active. */\n    int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) ||\n                          (DefaultUser->flags & USER_FLAG_DISABLED)) &&\n                        !c->authenticated;\n    if (auth_required) {\n        /* AUTH and HELLO and no auth modules are valid even in\n         * non-authenticated state. */\n        if (!(c->cmd->flags & CMD_NO_AUTH)) {\n            flagTransaction(c);\n            addReply(c,shared.noautherr);\n            return C_OK;\n        }\n    }\n\n    /* Check if the user can run this command according to the current\n     * ACLs. */\n    int acl_keypos;\n    int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);\n    if (acl_retval != ACL_OK) {\n        addACLLogEntry(c,acl_retval,acl_keypos,NULL);\n        flagTransaction(c);\n        if (acl_retval == ACL_DENIED_CMD)\n            addReplyErrorFormat(c,\n                \"-NOPERM this user has no permissions to run \"\n                \"the '%s' command or its subcommand\", c->cmd->name);\n        else\n            addReplyErrorFormat(c,\n                \"-NOPERM this user has no permissions to access \"\n                \"one of the keys used as arguments\");\n        return C_OK;\n    }\n\n    /* If cluster is enabled perform the cluster redirection here.\n     * However we don't perform the redirection if:\n     * 1) The sender of this command is our master.\n     * 2) The command has no key arguments. */\n    if (server.cluster_enabled &&\n        !(c->flags & CLIENT_MASTER) &&\n        !(c->flags & CLIENT_LUA &&\n          server.lua_caller->flags & CLIENT_MASTER) &&\n        !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0 &&\n          c->cmd->proc != execCommand))\n    {\n        int hashslot;\n        int error_code;\n        clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,\n                                        &hashslot,&error_code);\n        if (n == NULL || n != server.cluster->myself) {\n            if (c->cmd->proc == execCommand) {\n                discardTransaction(c);\n            } else {\n                flagTransaction(c);\n            }\n            clusterRedirectClient(c,n,hashslot,error_code);\n            return C_OK;\n        }\n    }\n\n    /* Handle the maxmemory directive.\n     *\n     * Note that we do not want to reclaim memory if we are here re-entering\n     * the event loop since there is a busy Lua script running in timeout\n     * condition, to avoid mixing the propagation of scripts with the\n     * propagation of DELs due to eviction. */\n    if (server.maxmemory && !server.lua_timedout) {\n        int out_of_memory = freeMemoryIfNeededAndSafe() == C_ERR;\n        /* freeMemoryIfNeeded may flush slave output buffers. This may result\n         * into a slave, that may be the active client, to be freed. */\n        if (server.current_client == NULL) return C_ERR;\n\n        /* It was impossible to free enough memory, and the command the client\n         * is trying to execute is denied during OOM conditions or the client\n         * is in MULTI/EXEC context? Error. */\n        if (out_of_memory &&\n            (c->cmd->flags & CMD_DENYOOM ||\n             (c->flags & CLIENT_MULTI &&\n              c->cmd->proc != execCommand &&\n              c->cmd->proc != discardCommand)))\n        {\n            flagTransaction(c);\n            addReply(c, shared.oomerr);\n            return C_OK;\n        }\n\n        /* Save out_of_memory result at script start, otherwise if we check OOM\n         * untill first write within script, memory used by lua stack and\n         * arguments might interfere. */\n        if (c->cmd->proc == evalCommand || c->cmd->proc == evalShaCommand) {\n            server.lua_oom = out_of_memory;\n        }\n    }\n\n    /* Make sure to use a reasonable amount of memory for client side\n     * caching metadata. */\n    if (server.tracking_clients) trackingLimitUsedSlots();\n\n    /* Don't accept write commands if there are problems persisting on disk\n     * and if this is a master instance. */\n    int deny_write_type = writeCommandsDeniedByDiskError();\n    if (deny_write_type != DISK_ERROR_TYPE_NONE &&\n        server.masterhost == NULL &&\n        (c->cmd->flags & CMD_WRITE ||\n         c->cmd->proc == pingCommand))\n    {\n        flagTransaction(c);\n        if (deny_write_type == DISK_ERROR_TYPE_RDB)\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 C_OK;\n    }\n\n    /* Don't accept write commands if there are not enough good slaves and\n     * user configured the min-slaves-to-write option. */\n    if (server.masterhost == NULL &&\n        server.repl_min_slaves_to_write &&\n        server.repl_min_slaves_max_lag &&\n        c->cmd->flags & 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 C_OK;\n    }\n\n    /* Don't accept write commands if this is a read only slave. But\n     * accept write commands if this is our master. */\n    if (server.masterhost && server.repl_slave_ro &&\n        !(c->flags & CLIENT_MASTER) &&\n        c->cmd->flags & CMD_WRITE)\n    {\n        flagTransaction(c);\n        addReply(c, shared.roslaveerr);\n        return C_OK;\n    }\n\n    /* Only allow a subset of commands in the context of Pub/Sub if the\n     * connection is in RESP2 mode. With RESP3 there are no limits. */\n    if ((c->flags & CLIENT_PUBSUB && c->resp == 2) &&\n        c->cmd->proc != pingCommand &&\n        c->cmd->proc != subscribeCommand &&\n        c->cmd->proc != unsubscribeCommand &&\n        c->cmd->proc != psubscribeCommand &&\n        c->cmd->proc != punsubscribeCommand) {\n        addReplyErrorFormat(c,\n            \"Can't execute '%s': only (P)SUBSCRIBE / \"\n            \"(P)UNSUBSCRIBE / PING / QUIT are allowed in this context\",\n            c->cmd->name);\n        return C_OK;\n    }\n\n    /* Only allow commands with flag \"t\", such as INFO, SLAVEOF and so on,\n     * when slave-serve-stale-data is no and we are a slave with a broken\n     * link with master. */\n    if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED &&\n        server.repl_serve_stale_data == 0 &&\n        !(c->cmd->flags & CMD_STALE))\n    {\n        flagTransaction(c);\n        addReply(c, shared.masterdownerr);\n        return C_OK;\n    }\n\n    /* Loading DB? Return an error if the command has not the\n     * CMD_LOADING flag. */\n    if (server.loading && !(c->cmd->flags & CMD_LOADING)) {\n        addReply(c, shared.loadingerr);\n        return C_OK;\n    }\n\n    /* Lua script too slow? Only allow a limited number of commands.\n     * Note that we need to allow the transactions commands, otherwise clients\n     * sending a transaction with pipelining without error checking, may have\n     * the MULTI plus a few initial commands refused, then the timeout\n     * condition resolves, and the bottom-half of the transaction gets\n     * executed, see Github PR #7022. */\n    if (server.lua_timedout &&\n          c->cmd->proc != authCommand &&\n          c->cmd->proc != helloCommand &&\n          c->cmd->proc != replconfCommand &&\n          c->cmd->proc != multiCommand &&\n          c->cmd->proc != execCommand &&\n          c->cmd->proc != discardCommand &&\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 C_OK;\n    }\n\n    /* Exec the command */\n    if (c->flags & CLIENT_MULTI &&\n        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&\n        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)\n    {\n        queueMultiCommand(c);\n        addReply(c,shared.queued);\n    } else {\n        call(c,CMD_CALL_FULL);\n        c->woff = server.master_repl_offset;\n        if (listLength(server.ready_keys))\n            handleClientsBlockedOnKeys();\n    }\n    return C_OK;\n}\n\n/*================================== Shutdown =============================== */\n\n/* Close listening sockets. Also unlink the unix domain socket if\n * unlink_unix_socket is non-zero. */\nvoid closeListeningSockets(int unlink_unix_socket) {\n    int j;\n\n    for (j = 0; j < server.ipfd_count; j++) close(server.ipfd[j]);\n    for (j = 0; j < server.tlsfd_count; j++) close(server.tlsfd[j]);\n    if (server.sofd != -1) close(server.sofd);\n    if (server.cluster_enabled)\n        for (j = 0; j < server.cfd_count; j++) close(server.cfd[j]);\n    if (unlink_unix_socket && server.unixsocket) {\n        serverLog(LL_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 & SHUTDOWN_SAVE;\n    int nosave = flags & SHUTDOWN_NOSAVE;\n\n    serverLog(LL_WARNING,\"User requested shutdown...\");\n    if (server.supervised_mode == SUPERVISED_SYSTEMD)\n        redisCommunicateSystemd(\"STOPPING=1\\n\");\n\n    /* Kill all the Lua debugger forked sessions. */\n    ldbKillForkedSessions();\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    if (server.rdb_child_pid != -1) {\n        serverLog(LL_WARNING,\"There is a child saving an .rdb. Killing it!\");\n        killRDBChild();\n    }\n\n    /* Kill module child if there is one. */\n    if (server.module_child_pid != -1) {\n        serverLog(LL_WARNING,\"There is a module fork child. Killing it!\");\n        TerminateModuleForkChild(server.module_child_pid,0);\n    }\n\n    if (server.aof_state != 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            /* If we have AOF enabled but haven't written the AOF yet, don't\n             * shutdown or else the dataset will be lost. */\n            if (server.aof_state == AOF_WAIT_REWRITE) {\n                serverLog(LL_WARNING, \"Writing initial AOF, can't exit.\");\n                return C_ERR;\n            }\n            serverLog(LL_WARNING,\n                \"There is a child rewriting the AOF. Killing it!\");\n            killAppendOnlyChild();\n        }\n        /* Append only file: flush buffers and fsync() the AOF at exit */\n        serverLog(LL_NOTICE,\"Calling fsync() on the AOF file.\");\n        flushAppendOnlyFile(1);\n        redis_fsync(server.aof_fd);\n    }\n\n    /* Create a new RDB file before exiting. */\n    if ((server.saveparamslen > 0 && !nosave) || save) {\n        serverLog(LL_NOTICE,\"Saving the final RDB snapshot before exiting.\");\n        if (server.supervised_mode == SUPERVISED_SYSTEMD)\n            redisCommunicateSystemd(\"STATUS=Saving the final RDB snapshot\\n\");\n        /* Snapshotting. Perform a SYNC SAVE and exit */\n        rdbSaveInfo rsi, *rsiptr;\n        rsiptr = rdbPopulateSaveInfo(&rsi);\n        if (rdbSave(server.rdb_filename,rsiptr) != C_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            serverLog(LL_WARNING,\"Error trying to save the DB, can't exit.\");\n            if (server.supervised_mode == SUPERVISED_SYSTEMD)\n                redisCommunicateSystemd(\"STATUS=Error trying to save the DB, can't exit.\\n\");\n            return C_ERR;\n        }\n    }\n\n    /* Fire the shutdown modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_SHUTDOWN,0,NULL);\n\n    /* Remove the pid file if possible and needed. */\n    if (server.daemonize || server.pidfile) {\n        serverLog(LL_NOTICE,\"Removing the pid file.\");\n        unlink(server.pidfile);\n    }\n\n    /* Best effort flush of slave output buffers, so that we hopefully\n     * send them pending writes. */\n    flushSlavesOutputBuffers();\n\n    /* Close the listening sockets. Apparently this allows faster restarts. */\n    closeListeningSockets(1);\n    serverLog(LL_WARNING,\"%s is now ready to exit, bye bye...\",\n        server.sentinel_mode ? \"Sentinel\" : \"Redis\");\n    return C_OK;\n}\n\n/*================================== Commands =============================== */\n\n/* Sometimes Redis cannot accept write commands because there is a perstence\n * error with the RDB or AOF file, and Redis is configured in order to stop\n * accepting writes in such situation. This function returns if such a\n * condition is active, and the type of the condition.\n *\n * Function return values:\n *\n * DISK_ERROR_TYPE_NONE:    No problems, we can accept writes.\n * DISK_ERROR_TYPE_AOF:     Don't accept writes: AOF errors.\n * DISK_ERROR_TYPE_RDB:     Don't accept writes: RDB errors.\n */\nint writeCommandsDeniedByDiskError(void) {\n    if (server.stop_writes_on_bgsave_err &&\n        server.saveparamslen > 0 &&\n        server.lastbgsave_status == C_ERR)\n    {\n        return DISK_ERROR_TYPE_RDB;\n    } else if (server.aof_state != AOF_OFF &&\n               server.aof_last_write_status == C_ERR)\n    {\n        return DISK_ERROR_TYPE_AOF;\n    } else {\n        return DISK_ERROR_TYPE_NONE;\n    }\n}\n\n/* The PING command. It works in a different way if the client is in\n * in Pub/Sub mode. */\nvoid pingCommand(client *c) {\n    /* The command takes zero or one arguments. */\n    if (c->argc > 2) {\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return;\n    }\n\n    if (c->flags & CLIENT_PUBSUB && c->resp == 2) {\n        addReply(c,shared.mbulkhdr[2]);\n        addReplyBulkCBuffer(c,\"pong\",4);\n        if (c->argc == 1)\n            addReplyBulkCBuffer(c,\"\",0);\n        else\n            addReplyBulk(c,c->argv[1]);\n    } else {\n        if (c->argc == 1)\n            addReply(c,shared.pong);\n        else\n            addReplyBulk(c,c->argv[1]);\n    }\n}\n\nvoid echoCommand(client *c) {\n    addReplyBulk(c,c->argv[1]);\n}\n\nvoid timeCommand(client *c) {\n    struct timeval tv;\n\n    /* gettimeofday() can only fail if &tv is a bad address so we\n     * don't check for errors. */\n    gettimeofday(&tv,NULL);\n    addReplyArrayLen(c,2);\n    addReplyBulkLongLong(c,tv.tv_sec);\n    addReplyBulkLongLong(c,tv.tv_usec);\n}\n\n/* Helper function for addReplyCommand() to output flags. */\nint addReplyCommandFlag(client *c, struct redisCommand *cmd, int f, char *reply) {\n    if (cmd->flags & f) {\n        addReplyStatus(c, reply);\n        return 1;\n    }\n    return 0;\n}\n\n/* Output the representation of a Redis command. Used by the COMMAND command. */\nvoid addReplyCommand(client *c, struct redisCommand *cmd) {\n    if (!cmd) {\n        addReplyNull(c);\n    } else {\n        /* We are adding: command name, arg count, flags, first, last, offset, categories */\n        addReplyArrayLen(c, 7);\n        addReplyBulkCString(c, cmd->name);\n        addReplyLongLong(c, cmd->arity);\n\n        int flagcount = 0;\n        void *flaglen = addReplyDeferredLen(c);\n        flagcount += addReplyCommandFlag(c,cmd,CMD_WRITE, \"write\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_READONLY, \"readonly\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_DENYOOM, \"denyoom\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_ADMIN, \"admin\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_PUBSUB, \"pubsub\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_NOSCRIPT, \"noscript\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_RANDOM, \"random\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SORT_FOR_SCRIPT,\"sort_for_script\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_LOADING, \"loading\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_STALE, \"stale\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_MONITOR, \"skip_monitor\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_SLOWLOG, \"skip_slowlog\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_ASKING, \"asking\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_FAST, \"fast\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_NO_AUTH, \"no_auth\");\n        if ((cmd->getkeys_proc && !(cmd->flags & CMD_MODULE)) ||\n            cmd->flags & CMD_MODULE_GETKEYS)\n        {\n            addReplyStatus(c, \"movablekeys\");\n            flagcount += 1;\n        }\n        setDeferredSetLen(c, flaglen, flagcount);\n\n        addReplyLongLong(c, cmd->firstkey);\n        addReplyLongLong(c, cmd->lastkey);\n        addReplyLongLong(c, cmd->keystep);\n\n        addReplyCommandCategories(c,cmd);\n    }\n}\n\n/* COMMAND <subcommand> <args> */\nvoid commandCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"(no subcommand) -- Return details about all Redis commands.\",\n\"COUNT -- Return the total number of commands in this Redis server.\",\n\"GETKEYS <full-command> -- Return the keys from a full Redis command.\",\n\"INFO [command-name ...] -- Return details about multiple Redis commands.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (c->argc == 1) {\n        addReplyArrayLen(c, dictSize(server.commands));\n        di = dictGetIterator(server.commands);\n        while ((de = dictNext(di)) != NULL) {\n            addReplyCommand(c, dictGetVal(de));\n        }\n        dictReleaseIterator(di);\n    } else if (!strcasecmp(c->argv[1]->ptr, \"info\")) {\n        int i;\n        addReplyArrayLen(c, c->argc-2);\n        for (i = 2; i < c->argc; i++) {\n            addReplyCommand(c, dictFetchValue(server.commands, c->argv[i]->ptr));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr, \"count\") && c->argc == 2) {\n        addReplyLongLong(c, dictSize(server.commands));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getkeys\") && c->argc >= 3) {\n        struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);\n        int *keys, numkeys, j;\n\n        if (!cmd) {\n            addReplyError(c,\"Invalid command specified\");\n            return;\n        } else if (cmd->getkeys_proc == NULL && cmd->firstkey == 0) {\n            addReplyError(c,\"The command has no key arguments\");\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        if (!keys) {\n            addReplyError(c,\"Invalid arguments specified for command\");\n        } else {\n            addReplyArrayLen(c,numkeys);\n            for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);\n            getKeysFreeResult(keys);\n        }\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\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    } else if (n < (1024*1024)) {\n        d = (double)n/(1024);\n        sprintf(s,\"%.2fK\",d);\n    } else if (n < (1024LL*1024*1024)) {\n        d = (double)n/(1024*1024);\n        sprintf(s,\"%.2fM\",d);\n    } else if (n < (1024LL*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024);\n        sprintf(s,\"%.2fG\",d);\n    } else if (n < (1024LL*1024*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024*1024);\n        sprintf(s,\"%.2fT\",d);\n    } else if (n < (1024LL*1024*1024*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024*1024*1024);\n        sprintf(s,\"%.2fP\",d);\n    } else {\n        /* Let's hope we never need this */\n        sprintf(s,\"%lluB\",n);\n    }\n}\n\n/* 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(const char *section) {\n    sds info = sdsempty();\n    time_t uptime = server.unixtime-server.stat_starttime;\n    int j;\n    struct rusage self_ru, c_ru;\n    int allsections = 0, defsections = 0, everything = 0, modules = 0;\n    int sections = 0;\n\n    if (section == NULL) section = \"default\";\n    allsections = strcasecmp(section,\"all\") == 0;\n    defsections = strcasecmp(section,\"default\") == 0;\n    everything = strcasecmp(section,\"everything\") == 0;\n    modules = strcasecmp(section,\"modules\") == 0;\n    if (everything) allsections = 1;\n\n    getrusage(RUSAGE_SELF, &self_ru);\n    getrusage(RUSAGE_CHILDREN, &c_ru);\n\n    /* Server */\n    if (allsections || defsections || !strcasecmp(section,\"server\")) {\n        static int call_uname = 1;\n        static struct utsname name;\n        char *mode;\n\n        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 = sdscatfmt(info,\n            \"# Server\\r\\n\"\n            \"redis_version:%s\\r\\n\"\n            \"redis_git_sha1:%s\\r\\n\"\n            \"redis_git_dirty:%i\\r\\n\"\n            \"redis_build_id:%s\\r\\n\"\n            \"redis_mode:%s\\r\\n\"\n            \"os:%s %s %s\\r\\n\"\n            \"arch_bits:%i\\r\\n\"\n            \"multiplexing_api:%s\\r\\n\"\n            \"atomicvar_api:%s\\r\\n\"\n            \"gcc_version:%i.%i.%i\\r\\n\"\n            \"process_id:%I\\r\\n\"\n            \"run_id:%s\\r\\n\"\n            \"tcp_port:%i\\r\\n\"\n            \"uptime_in_seconds:%I\\r\\n\"\n            \"uptime_in_days:%I\\r\\n\"\n            \"hz:%i\\r\\n\"\n            \"configured_hz:%i\\r\\n\"\n            \"lru_clock:%u\\r\\n\"\n            \"executable:%s\\r\\n\"\n            \"config_file:%s\\r\\n\",\n            REDIS_VERSION,\n            redisGitSHA1(),\n            strtol(redisGitDirty(),NULL,10) > 0,\n            redisBuildIdString(),\n            mode,\n            name.sysname, name.release, name.machine,\n            server.arch_bits,\n            aeGetApiName(),\n            REDIS_ATOMIC_API,\n#ifdef __GNUC__\n            __GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__,\n#else\n            0,0,0,\n#endif\n            (int64_t) getpid(),\n            server.runid,\n            server.port ? server.port : server.tls_port,\n            (int64_t)uptime,\n            (int64_t)(uptime/(3600*24)),\n            server.hz,\n            server.config_hz,\n            server.lruclock,\n            server.executable ? server.executable : \"\",\n            server.configfile ? server.configfile : \"\");\n    }\n\n    /* Clients */\n    if (allsections || defsections || !strcasecmp(section,\"clients\")) {\n        size_t maxin, maxout;\n        getExpansiveClientsInfo(&maxin,&maxout);\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Clients\\r\\n\"\n            \"connected_clients:%lu\\r\\n\"\n            \"client_recent_max_input_buffer:%zu\\r\\n\"\n            \"client_recent_max_output_buffer:%zu\\r\\n\"\n            \"blocked_clients:%d\\r\\n\"\n            \"tracking_clients:%d\\r\\n\"\n            \"clients_in_timeout_table:%llu\\r\\n\",\n            listLength(server.clients)-listLength(server.slaves),\n            maxin, maxout,\n            server.blocked_clients,\n            server.tracking_clients,\n            (unsigned long long) raxSize(server.clients_timeout_table));\n    }\n\n    /* Memory */\n    if (allsections || defsections || !strcasecmp(section,\"memory\")) {\n        char hmem[64];\n        char peak_hmem[64];\n        char total_system_hmem[64];\n        char used_memory_lua_hmem[64];\n        char used_memory_scripts_hmem[64];\n        char used_memory_rss_hmem[64];\n        char maxmemory_hmem[64];\n        size_t zmalloc_used = zmalloc_used_memory();\n        size_t total_system_mem = server.system_memory_size;\n        const char *evict_policy = evictPolicyToString();\n        long long memory_lua = server.lua ? (long long)lua_gc(server.lua,LUA_GCCOUNT,0)*1024 : 0;\n        struct redisMemOverhead *mh = getMemoryOverheadData();\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        bytesToHuman(total_system_hmem,total_system_mem);\n        bytesToHuman(used_memory_lua_hmem,memory_lua);\n        bytesToHuman(used_memory_scripts_hmem,mh->lua_caches);\n        bytesToHuman(used_memory_rss_hmem,server.cron_malloc_stats.process_rss);\n        bytesToHuman(maxmemory_hmem,server.maxmemory);\n\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Memory\\r\\n\"\n            \"used_memory:%zu\\r\\n\"\n            \"used_memory_human:%s\\r\\n\"\n            \"used_memory_rss:%zu\\r\\n\"\n            \"used_memory_rss_human:%s\\r\\n\"\n            \"used_memory_peak:%zu\\r\\n\"\n            \"used_memory_peak_human:%s\\r\\n\"\n            \"used_memory_peak_perc:%.2f%%\\r\\n\"\n            \"used_memory_overhead:%zu\\r\\n\"\n            \"used_memory_startup:%zu\\r\\n\"\n            \"used_memory_dataset:%zu\\r\\n\"\n            \"used_memory_dataset_perc:%.2f%%\\r\\n\"\n            \"allocator_allocated:%zu\\r\\n\"\n            \"allocator_active:%zu\\r\\n\"\n            \"allocator_resident:%zu\\r\\n\"\n            \"total_system_memory:%lu\\r\\n\"\n            \"total_system_memory_human:%s\\r\\n\"\n            \"used_memory_lua:%lld\\r\\n\"\n            \"used_memory_lua_human:%s\\r\\n\"\n            \"used_memory_scripts:%lld\\r\\n\"\n            \"used_memory_scripts_human:%s\\r\\n\"\n            \"number_of_cached_scripts:%lu\\r\\n\"\n            \"maxmemory:%lld\\r\\n\"\n            \"maxmemory_human:%s\\r\\n\"\n            \"maxmemory_policy:%s\\r\\n\"\n            \"allocator_frag_ratio:%.2f\\r\\n\"\n            \"allocator_frag_bytes:%zu\\r\\n\"\n            \"allocator_rss_ratio:%.2f\\r\\n\"\n            \"allocator_rss_bytes:%zd\\r\\n\"\n            \"rss_overhead_ratio:%.2f\\r\\n\"\n            \"rss_overhead_bytes:%zd\\r\\n\"\n            \"mem_fragmentation_ratio:%.2f\\r\\n\"\n            \"mem_fragmentation_bytes:%zd\\r\\n\"\n            \"mem_not_counted_for_evict:%zu\\r\\n\"\n            \"mem_replication_backlog:%zu\\r\\n\"\n            \"mem_clients_slaves:%zu\\r\\n\"\n            \"mem_clients_normal:%zu\\r\\n\"\n            \"mem_aof_buffer:%zu\\r\\n\"\n            \"mem_allocator:%s\\r\\n\"\n            \"active_defrag_running:%d\\r\\n\"\n            \"lazyfree_pending_objects:%zu\\r\\n\",\n            zmalloc_used,\n            hmem,\n            server.cron_malloc_stats.process_rss,\n            used_memory_rss_hmem,\n            server.stat_peak_memory,\n            peak_hmem,\n            mh->peak_perc,\n            mh->overhead_total,\n            mh->startup_allocated,\n            mh->dataset,\n            mh->dataset_perc,\n            server.cron_malloc_stats.allocator_allocated,\n            server.cron_malloc_stats.allocator_active,\n            server.cron_malloc_stats.allocator_resident,\n            (unsigned long)total_system_mem,\n            total_system_hmem,\n            memory_lua,\n            used_memory_lua_hmem,\n            (long long) mh->lua_caches,\n            used_memory_scripts_hmem,\n            dictSize(server.lua_scripts),\n            server.maxmemory,\n            maxmemory_hmem,\n            evict_policy,\n            mh->allocator_frag,\n            mh->allocator_frag_bytes,\n            mh->allocator_rss,\n            mh->allocator_rss_bytes,\n            mh->rss_extra,\n            mh->rss_extra_bytes,\n            mh->total_frag,       /* This is the total RSS overhead, including\n                                     fragmentation, but not just it. This field\n                                     (and the next one) is named like that just\n                                     for backward compatibility. */\n            mh->total_frag_bytes,\n            freeMemoryGetNotCountedMemory(),\n            mh->repl_backlog,\n            mh->clients_slaves,\n            mh->clients_normal,\n            mh->aof_buffer,\n            ZMALLOC_LIB,\n            server.active_defrag_running,\n            lazyfreeGetPendingObjectsCount()\n        );\n        freeMemoryOverheadData(mh);\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            \"rdb_last_cow_size:%zu\\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            \"aof_last_cow_size:%zu\\r\\n\"\n            \"module_fork_in_progress:%d\\r\\n\"\n            \"module_fork_last_cow_size:%zu\\r\\n\",\n            server.loading,\n            server.dirty,\n            server.rdb_child_pid != -1,\n            (intmax_t)server.lastsave,\n            (server.lastbgsave_status == C_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.stat_rdb_cow_bytes,\n            server.aof_state != 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 == C_OK) ? \"ok\" : \"err\",\n            (server.aof_last_write_status == C_OK) ? \"ok\" : \"err\",\n            server.stat_aof_cow_bytes,\n            server.module_child_pid != -1,\n            server.stat_module_cow_bytes);\n\n        if (server.aof_enabled) {\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(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+1)) * 100;\n\n            elapsed = time(NULL)-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+1);\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            \"total_net_input_bytes:%lld\\r\\n\"\n            \"total_net_output_bytes:%lld\\r\\n\"\n            \"instantaneous_input_kbps:%.2f\\r\\n\"\n            \"instantaneous_output_kbps:%.2f\\r\\n\"\n            \"rejected_connections:%lld\\r\\n\"\n            \"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            \"expired_stale_perc:%.2f\\r\\n\"\n            \"expired_time_cap_reached_count:%lld\\r\\n\"\n            \"expire_cycle_cpu_milliseconds:%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            \"slave_expires_tracked_keys:%zu\\r\\n\"\n            \"active_defrag_hits:%lld\\r\\n\"\n            \"active_defrag_misses:%lld\\r\\n\"\n            \"active_defrag_key_hits:%lld\\r\\n\"\n            \"active_defrag_key_misses:%lld\\r\\n\"\n            \"tracking_total_keys:%lld\\r\\n\"\n            \"tracking_total_items:%lld\\r\\n\"\n            \"tracking_total_prefixes:%lld\\r\\n\"\n            \"unexpected_error_replies:%lld\\r\\n\",\n            server.stat_numconnections,\n            server.stat_numcommands,\n            getInstantaneousMetric(STATS_METRIC_COMMAND),\n            server.stat_net_input_bytes,\n            server.stat_net_output_bytes,\n            (float)getInstantaneousMetric(STATS_METRIC_NET_INPUT)/1024,\n            (float)getInstantaneousMetric(STATS_METRIC_NET_OUTPUT)/1024,\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_expired_stale_perc*100,\n            server.stat_expired_time_cap_reached_count,\n            server.stat_expire_cycle_time_used/1000,\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            getSlaveKeyWithExpireCount(),\n            server.stat_active_defrag_hits,\n            server.stat_active_defrag_misses,\n            server.stat_active_defrag_key_hits,\n            server.stat_active_defrag_key_misses,\n            (unsigned long long) trackingGetTotalKeys(),\n            (unsigned long long) trackingGetTotalItems(),\n            (unsigned long long) trackingGetTotalPrefixes(),\n            server.stat_unexpected_error_replies);\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 == REPL_STATE_CONNECTED) ?\n                    \"up\" : \"down\",\n                server.master ?\n                ((int)(server.unixtime-server.master->lastinteraction)) : -1,\n                server.repl_state == REPL_STATE_TRANSFER,\n                slave_repl_offset\n            );\n\n            if (server.repl_state == REPL_STATE_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 != REPL_STATE_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                client *slave = listNodeValue(ln);\n                char *state = NULL;\n                char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;\n                int port;\n                long lag = 0;\n\n                if (slaveip[0] == '\\0') {\n                    if (connPeerToString(slave->conn,ip,sizeof(ip),&port) == -1)\n                        continue;\n                    slaveip = ip;\n                }\n                switch(slave->replstate) {\n                case SLAVE_STATE_WAIT_BGSAVE_START:\n                case SLAVE_STATE_WAIT_BGSAVE_END:\n                    state = \"wait_bgsave\";\n                    break;\n                case SLAVE_STATE_SEND_BULK:\n                    state = \"send_bulk\";\n                    break;\n                case SLAVE_STATE_ONLINE:\n                    state = \"online\";\n                    break;\n                }\n                if (state == NULL) continue;\n                if (slave->replstate == SLAVE_STATE_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,slaveip,slave->slave_listening_port,state,\n                    slave->repl_ack_off, lag);\n                slaveid++;\n            }\n        }\n        info = sdscatprintf(info,\n            \"master_replid:%s\\r\\n\"\n            \"master_replid2:%s\\r\\n\"\n            \"master_repl_offset:%lld\\r\\n\"\n            \"master_repl_meaningful_offset:%lld\\r\\n\"\n            \"second_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.replid,\n            server.replid2,\n            server.master_repl_offset,\n            server.master_repl_meaningful_offset,\n            server.second_replid_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:%ld.%06ld\\r\\n\"\n        \"used_cpu_user:%ld.%06ld\\r\\n\"\n        \"used_cpu_sys_children:%ld.%06ld\\r\\n\"\n        \"used_cpu_user_children:%ld.%06ld\\r\\n\",\n        (long)self_ru.ru_stime.tv_sec, (long)self_ru.ru_stime.tv_usec,\n        (long)self_ru.ru_utime.tv_sec, (long)self_ru.ru_utime.tv_usec,\n        (long)c_ru.ru_stime.tv_sec, (long)c_ru.ru_stime.tv_usec,\n        (long)c_ru.ru_utime.tv_sec, (long)c_ru.ru_utime.tv_usec);\n    }\n\n    /* Modules */\n    if (allsections || defsections || !strcasecmp(section,\"modules\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\"# Modules\\r\\n\");\n        info = genModulesInfoString(info);\n    }\n\n    /* Command statistics */\n    if (allsections || !strcasecmp(section,\"commandstats\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info, \"# Commandstats\\r\\n\");\n\n        struct redisCommand *c;\n        dictEntry *de;\n        dictIterator *di;\n        di = dictGetSafeIterator(server.commands);\n        while((de = dictNext(di)) != NULL) {\n            c = (struct redisCommand *) dictGetVal(de);\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        dictReleaseIterator(di);\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\n    /* Get info from modules.\n     * if user asked for \"everything\" or \"modules\", or a specific section\n     * that's not found yet. */\n    if (everything || modules ||\n        (!allsections && !defsections && sections==0)) {\n        info = modulesCollectInfo(info,\n                                  everything || modules ? NULL: section,\n                                  0, /* not a crash report */\n                                  sections);\n    }\n    return info;\n}\n\nvoid infoCommand(client *c) {\n    char *section = c->argc == 2 ? c->argv[1]->ptr : \"default\";\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    sds info = genRedisInfoString(section);\n    addReplyVerbatim(c,info,sdslen(info),\"txt\");\n    sdsfree(info);\n}\n\nvoid monitorCommand(client *c) {\n    /* ignore MONITOR if already slave or in monitor mode */\n    if (c->flags & CLIENT_SLAVE) return;\n\n    c->flags |= (CLIENT_SLAVE|CLIENT_MONITOR);\n    listAddNodeTail(server.monitors,c);\n    addReply(c,shared.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 linuxMemoryWarnings(void) {\n    if (linuxOvercommitMemoryValue() == 0) {\n        serverLog(LL_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    if (THPIsEnabled()) {\n        serverLog(LL_WARNING,\"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.\");\n    }\n}\n#endif /* __linux__ */\n\nvoid createPidFile(void) {\n    /* If pidfile requested, but no pidfile defined, use\n     * default pidfile path */\n    if (!server.pidfile) server.pidfile = zstrdup(CONFIG_DEFAULT_PID_FILE);\n\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(void) {\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(void) {\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 --replicaof 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;\n\n    if (server.cluster_enabled) mode = \"cluster\";\n    else if (server.sentinel_mode) mode = \"sentinel\";\n    else mode = \"standalone\";\n\n    /* Show the ASCII logo if: log file is stdout AND stdout is a\n     * tty AND syslog logging is disabled. Also show logo if the user\n     * forced us to do so via redis.conf. */\n    int show_logo = ((!server.syslog_enabled &&\n                      server.logfile[0] == '\\0' &&\n                      isatty(fileno(stdout))) ||\n                     server.always_show_logo);\n\n    if (!show_logo) {\n        serverLog(LL_NOTICE,\n            \"Running mode=%s, port=%d.\",\n            mode, server.port ? server.port : server.tls_port\n        );\n    } else {\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 ? server.port : server.tls_port,\n            (long) getpid()\n        );\n        serverLogRaw(LL_NOTICE|LL_RAW,buf);\n    }\n    zfree(buf);\n}\n\nstatic void sigShutdownHandler(int sig) {\n    char *msg;\n\n    switch (sig) {\n    case SIGINT:\n        msg = \"Received SIGINT scheduling shutdown...\";\n        break;\n    case SIGTERM:\n        msg = \"Received SIGTERM scheduling shutdown...\";\n        break;\n    default:\n        msg = \"Received shutdown signal, scheduling shutdown...\";\n    };\n\n    /* SIGINT is often delivered via Ctrl+C in an interactive session.\n     * If we receive the signal the second time, we interpret this as\n     * the user really wanting to quit ASAP without waiting to persist\n     * on disk. */\n    if (server.shutdown_asap && sig == SIGINT) {\n        serverLogFromHandler(LL_WARNING, \"You insist... exiting now.\");\n        rdbRemoveTempFile(getpid());\n        exit(1); /* Exit with an error since this was not a clean shutdown. */\n    } else if (server.loading) {\n        serverLogFromHandler(LL_WARNING, \"Received shutdown signal during loading, exiting now.\");\n        exit(0);\n    }\n\n    serverLogFromHandler(LL_WARNING, msg);\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 = sigShutdownHandler;\n    sigaction(SIGTERM, &act, NULL);\n    sigaction(SIGINT, &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\n/* This is the signal handler for children process. It is currently useful\n * in order to track the SIGUSR1, that we send to a child in order to terminate\n * it in a clean way, without the parent detecting an error and stop\n * accepting writes because of a write error condition. */\nstatic void sigKillChildHandler(int sig) {\n    UNUSED(sig);\n    serverLogFromHandler(LL_WARNING, \"Received SIGUSR1 in child, exiting now.\");\n    exitFromChild(SERVER_CHILD_NOERROR_RETVAL);\n}\n\nvoid setupChildSignalHandlers(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 = sigKillChildHandler;\n    sigaction(SIGUSR1, &act, NULL);\n    return;\n}\n\nint redisFork() {\n    int childpid;\n    long long start = ustime();\n    if ((childpid = fork()) == 0) {\n        /* Child */\n        closeListeningSockets(0);\n        setupChildSignalHandlers();\n    } else {\n        /* Parent */\n        server.stat_fork_time = ustime()-start;\n        server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */\n        latencyAddSampleIfNeeded(\"fork\",server.stat_fork_time/1000);\n        if (childpid == -1) {\n            return -1;\n        }\n        updateDictResizePolicy();\n    }\n    return childpid;\n}\n\nvoid sendChildCOWInfo(int ptype, char *pname) {\n    size_t private_dirty = zmalloc_get_private_dirty(-1);\n\n    if (private_dirty) {\n        serverLog(LL_NOTICE,\n            \"%s: %zu MB of memory used by copy-on-write\",\n            pname, private_dirty/(1024*1024));\n    }\n\n    server.child_info_data.cow_size = private_dirty;\n    sendChildInfo(ptype);\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] contains \"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    long long start = ustime();\n    if (server.aof_state == AOF_ON) {\n        if (loadAppendOnlyFile(server.aof_filename) == C_OK)\n            serverLog(LL_NOTICE,\"DB loaded from append only file: %.3f seconds\",(float)(ustime()-start)/1000000);\n    } else {\n        rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;\n        if (rdbLoad(server.rdb_filename,&rsi,RDBFLAGS_NONE) == C_OK) {\n            serverLog(LL_NOTICE,\"DB loaded from disk: %.3f seconds\",\n                (float)(ustime()-start)/1000000);\n\n            /* Restore the replication ID / offset from the RDB file. */\n            if ((server.masterhost ||\n                (server.cluster_enabled &&\n                nodeIsSlave(server.cluster->myself))) &&\n                rsi.repl_id_is_set &&\n                rsi.repl_offset != -1 &&\n                /* Note that older implementations may save a repl_stream_db\n                 * of -1 inside the RDB file in a wrong way, see more\n                 * information in function rdbPopulateSaveInfo. */\n                rsi.repl_stream_db != -1)\n            {\n                memcpy(server.replid,rsi.repl_id,sizeof(server.replid));\n                server.master_repl_offset = rsi.repl_offset;\n                server.master_repl_meaningful_offset = rsi.repl_offset;\n                /* If we are a slave, create a cached master from this\n                 * information, in order to allow partial resynchronizations\n                 * with masters. */\n                replicationCacheMasterUsingMyself();\n                selectDb(server.cached_master,rsi.repl_stream_db);\n            }\n        } else if (errno != ENOENT) {\n            serverLog(LL_WARNING,\"Fatal error loading the DB: %s. Exiting.\",strerror(errno));\n            exit(1);\n        }\n    }\n}\n\nvoid redisOutOfMemoryHandler(size_t allocation_size) {\n    serverLog(LL_WARNING,\"Out Of Memory allocating %zu bytes!\",\n        allocation_size);\n    serverPanic(\"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 ? server.port : server.tls_port,\n        server_mode);\n#else\n    UNUSED(title);\n#endif\n}\n\nvoid redisSetCpuAffinity(const char *cpulist) {\n#ifdef USE_SETCPUAFFINITY\n    setcpuaffinity(cpulist);\n#else\n    UNUSED(cpulist);\n#endif\n}\n\n/*\n * Check whether systemd or upstart have been used to start redis.\n */\n\nint redisSupervisedUpstart(void) {\n    const char *upstart_job = getenv(\"UPSTART_JOB\");\n\n    if (!upstart_job) {\n        serverLog(LL_WARNING,\n                \"upstart supervision requested, but UPSTART_JOB not found\");\n        return 0;\n    }\n\n    serverLog(LL_NOTICE, \"supervised by upstart, will stop to signal readiness\");\n    raise(SIGSTOP);\n    unsetenv(\"UPSTART_JOB\");\n    return 1;\n}\n\nint redisCommunicateSystemd(const char *sd_notify_msg) {\n    const char *notify_socket = getenv(\"NOTIFY_SOCKET\");\n    if (!notify_socket) {\n        serverLog(LL_WARNING,\n                \"systemd supervision requested, but NOTIFY_SOCKET not found\");\n    }\n\n    #ifdef HAVE_LIBSYSTEMD\n    (void) sd_notify(0, sd_notify_msg);\n    #else\n    UNUSED(sd_notify_msg);\n    #endif\n    return 0;\n}\n\nint redisIsSupervised(int mode) {\n    if (mode == SUPERVISED_AUTODETECT) {\n        const char *upstart_job = getenv(\"UPSTART_JOB\");\n        const char *notify_socket = getenv(\"NOTIFY_SOCKET\");\n\n        if (upstart_job) {\n            redisSupervisedUpstart();\n        } else if (notify_socket) {\n            server.supervised_mode = SUPERVISED_SYSTEMD;\n            serverLog(LL_WARNING,\n                \"WARNING auto-supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit.\");\n            return redisCommunicateSystemd(\"STATUS=Redis is loading...\\n\");\n        }\n    } else if (mode == SUPERVISED_UPSTART) {\n        return redisSupervisedUpstart();\n    } else if (mode == SUPERVISED_SYSTEMD) {\n        serverLog(LL_WARNING,\n            \"WARNING supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit.\");\n        return redisCommunicateSystemd(\"STATUS=Redis is loading...\\n\");\n    }\n\n    return 0;\n}\n\nint iAmMaster(void) {\n    return ((!server.cluster_enabled && server.masterhost == NULL) ||\n            (server.cluster_enabled && nodeIsMaster(server.cluster->myself)));\n}\n\n\nint main(int argc, char **argv) {\n    struct timeval tv;\n    int j;\n\n#ifdef REDIS_TEST\n    if (argc == 3 && !strcasecmp(argv[1], \"test\")) {\n        if (!strcasecmp(argv[2], \"ziplist\")) {\n            return ziplistTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"quicklist\")) {\n            quicklistTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"intset\")) {\n            return intsetTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"zipmap\")) {\n            return zipmapTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"sha1test\")) {\n            return sha1Test(argc, argv);\n        } else if (!strcasecmp(argv[2], \"util\")) {\n            return utilTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"endianconv\")) {\n            return endianconvTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"crc64\")) {\n            return crc64Test(argc, argv);\n        } else if (!strcasecmp(argv[2], \"zmalloc\")) {\n            return zmalloc_test(argc, argv);\n        }\n\n        return -1; /* test not found */\n    }\n#endif\n\n    /* We need to initialize our libraries, and the server configuration. */\n#ifdef INIT_SETPROCTITLE_REPLACEMENT\n    spt_init(argc, argv);\n#endif\n    setlocale(LC_COLLATE,\"\");\n    tzset(); /* Populates 'timezone' global. */\n    zmalloc_set_oom_handler(redisOutOfMemoryHandler);\n    srand(time(NULL)^getpid());\n    gettimeofday(&tv,NULL);\n    crc64_init();\n\n    uint8_t hashseed[16];\n    getRandomBytes(hashseed,sizeof(hashseed));\n    dictSetHashFunctionSeed(hashseed);\n    server.sentinel_mode = checkForSentinelMode(argc,argv);\n    initServerConfig();\n    ACLInit(); /* The ACL subsystem must be initialized ASAP because the\n                  basic networking code and client creation depends on it. */\n    moduleInitModulesSystem();\n    tlsInit();\n\n    /* Store the executable path and arguments in a safe place in order\n     * to be able to restart the server later. */\n    server.executable = getAbsolutePath(argv[0]);\n    server.exec_argv = zmalloc(sizeof(char*)*(argc+1));\n    server.exec_argv[argc] = NULL;\n    for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]);\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    if (server.sentinel_mode) {\n        initSentinelConfig();\n        initSentinel();\n    }\n\n    /* Check if we need to start in redis-check-rdb/aof mode. We just execute\n     * the program main. However the program is part of the Redis executable\n     * so that we can easily execute an RDB check on loading errors. */\n    if (strstr(argv[0],\"redis-check-rdb\") != NULL)\n        redis_check_rdb_main(argc,argv,NULL);\n    else if (strstr(argv[0],\"redis-check-aof\") != NULL)\n        redis_check_aof_main(argc,argv);\n\n    if (argc >= 2) {\n        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        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        if (argv[j][0] != '-' || argv[j][1] != '-') {\n            configfile = argv[j];\n            server.configfile = getAbsolutePath(configfile);\n            /* Replace the config file in server.exec_argv with\n             * its absolute path. */\n            zfree(server.exec_argv[j]);\n            server.exec_argv[j] = zstrdup(server.configfile);\n            j++;\n        }\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        while(j != argc) {\n            if (argv[j][0] == '-' && argv[j][1] == '-') {\n                /* Option name */\n                if (!strcmp(argv[j], \"--check-rdb\")) {\n                    /* Argument has no options, need to skip for parsing. */\n                    j++;\n                    continue;\n                }\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 (server.sentinel_mode && configfile && *configfile == '-') {\n            serverLog(LL_WARNING,\n                \"Sentinel config from STDIN not allowed.\");\n            serverLog(LL_WARNING,\n                \"Sentinel needs config file on disk to save state.  Exiting...\");\n            exit(1);\n        }\n        resetServerSaveParams();\n        loadServerConfig(configfile,options);\n        sdsfree(options);\n    }\n\n    serverLog(LL_WARNING, \"oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo\");\n    serverLog(LL_WARNING,\n        \"Redis version=%s, bits=%d, commit=%s, modified=%d, pid=%d, just started\",\n            REDIS_VERSION,\n            (sizeof(long) == 8) ? 64 : 32,\n            redisGitSHA1(),\n            strtol(redisGitDirty(),NULL,10) > 0,\n            (int)getpid());\n\n    if (argc == 1) {\n        serverLog(LL_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    } else {\n        serverLog(LL_WARNING, \"Configuration loaded\");\n    }\n\n    server.supervised = redisIsSupervised(server.supervised_mode);\n    int background = server.daemonize && !server.supervised;\n    if (background) daemonize();\n\n    initServer();\n    if (background || server.pidfile) createPidFile();\n    redisSetProcTitle(argv[0]);\n    redisAsciiArt();\n    checkTcpBacklogSettings();\n\n    if (!server.sentinel_mode) {\n        /* Things not needed when running in Sentinel mode. */\n        serverLog(LL_WARNING,\"Server initialized\");\n    #ifdef __linux__\n        linuxMemoryWarnings();\n    #endif\n        moduleLoadFromQueue();\n        ACLLoadUsersAtStartup();\n        InitServerLast();\n        loadDataFromDisk();\n        if (server.cluster_enabled) {\n            if (verifyClusterConfigWithData() == C_ERR) {\n                serverLog(LL_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        if (server.ipfd_count > 0 || server.tlsfd_count > 0)\n            serverLog(LL_NOTICE,\"Ready to accept connections\");\n        if (server.sofd > 0)\n            serverLog(LL_NOTICE,\"The server is now ready to accept connections at %s\", server.unixsocket);\n        if (server.supervised_mode == SUPERVISED_SYSTEMD) {\n            if (!server.masterhost) {\n                redisCommunicateSystemd(\"STATUS=Ready to accept connections\\n\");\n                redisCommunicateSystemd(\"READY=1\\n\");\n            } else {\n                redisCommunicateSystemd(\"STATUS=Waiting for MASTER <-> REPLICA sync\\n\");\n            }\n        }\n    } else {\n        InitServerLast();\n        sentinelIsRunning();\n    }\n\n    /* Warning the user about suspicious maxmemory setting. */\n    if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {\n        serverLog(LL_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    redisSetCpuAffinity(server.server_cpulist);\n    aeMain(server.el);\n    aeDeleteEventLoop(server.el);\n    return 0;\n}\n\n/* The End */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/server.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#include \"solarisfixes.h\"\n#include \"rio.h\"\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 <sys/socket.h>\n#include <lua.h>\n#include <signal.h>\n\n#ifdef HAVE_LIBSYSTEMD\n#include <systemd/sd-daemon.h>\n#endif\n\ntypedef long long mstime_t; /* millisecond time type. */\ntypedef long long ustime_t; /* microsecond time type. */\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#include \"latency.h\" /* Latency monitor API */\n#include \"sparkline.h\" /* ASCII graphs API */\n#include \"quicklist.h\"  /* Lists are encoded as linked lists of\n                           N-elements flat arrays */\n#include \"rax.h\"     /* Radix tree */\n#include \"connection.h\" /* Connection abstraction */\n\n#define REDISMODULE_CORE 1\n#include \"redismodule.h\"    /* Redis modules API defines. */\n\n/* Following includes allow test functions to be called from Redis main() */\n#include \"zipmap.h\"\n#include \"sha1.h\"\n#include \"endianconv.h\"\n#include \"crc64.h\"\n\n/* Error codes */\n#define C_OK                    0\n#define C_ERR                   -1\n\n/* Static server configuration */\n#define CONFIG_DEFAULT_HZ        10             /* Time interrupt calls/sec. */\n#define CONFIG_MIN_HZ            1\n#define CONFIG_MAX_HZ            500\n#define MAX_CLIENTS_PER_CLOCK_TICK 200          /* HZ is adapted based on that. */\n#define CONFIG_MAX_LINE    1024\n#define CRON_DBS_PER_CALL 16\n#define NET_MAX_WRITES_PER_EVENT (1024*64)\n#define PROTO_SHARED_SELECT_CMDS 10\n#define OBJ_SHARED_INTEGERS 10000\n#define OBJ_SHARED_BULKHDR_LEN 32\n#define LOG_MAX_LEN    1024 /* Default maximum length of syslog messages.*/\n#define AOF_REWRITE_ITEMS_PER_CMD 64\n#define AOF_READ_DIFF_INTERVAL_BYTES (1024*10)\n#define CONFIG_AUTHPASS_MAX_LEN 512\n#define CONFIG_RUN_ID_SIZE 40\n#define RDB_EOF_MARK_SIZE 40\n#define CONFIG_REPL_BACKLOG_MIN_SIZE (1024*16)          /* 16k */\n#define CONFIG_BGSAVE_RETRY_DELAY 5 /* Wait a few secs before trying again. */\n#define CONFIG_DEFAULT_PID_FILE \"/var/run/redis.pid\"\n#define CONFIG_DEFAULT_CLUSTER_CONFIG_FILE \"nodes.conf\"\n#define CONFIG_DEFAULT_UNIX_SOCKET_PERM 0\n#define CONFIG_DEFAULT_LOGFILE \"\"\n#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */\n#define NET_PEER_ID_LEN (NET_IP_STR_LEN+32) /* Must be enough for ip:port */\n#define CONFIG_BINDADDR_MAX 16\n#define CONFIG_MIN_RESERVED_FDS 32\n\n#define ACTIVE_EXPIRE_CYCLE_SLOW 0\n#define ACTIVE_EXPIRE_CYCLE_FAST 1\n\n/* Children process will exit with this status code to signal that the\n * process terminated without an error: this is useful in order to kill\n * a saving child (RDB or AOF one), without triggering in the parent the\n * write protection that is normally turned on on write errors.\n * Usually children that are terminated with SIGUSR1 will exit with this\n * special code. */\n#define SERVER_CHILD_NOERROR_RETVAL    255\n\n/* Instantaneous metrics tracking. */\n#define STATS_METRIC_SAMPLES 16     /* Number of samples per metric. */\n#define STATS_METRIC_COMMAND 0      /* Number of commands executed. */\n#define STATS_METRIC_NET_INPUT 1    /* Bytes read to network .*/\n#define STATS_METRIC_NET_OUTPUT 2   /* Bytes written to network. */\n#define STATS_METRIC_COUNT 3\n\n/* Protocol and I/O related defines */\n#define PROTO_MAX_QUERYBUF_LEN  (1024*1024*1024) /* 1GB max query buffer. */\n#define PROTO_IOBUF_LEN         (1024*16)  /* Generic I/O buffer size */\n#define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */\n#define PROTO_INLINE_MAX_SIZE   (1024*64) /* Max size of inline reads */\n#define PROTO_MBULK_BIG_ARG     (1024*32)\n#define LONG_STR_SIZE      21          /* Bytes needed for long -> str + '\\0' */\n#define REDIS_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */\n\n#define LIMIT_PENDING_QUERYBUF (4*1024*1024) /* 4mb */\n\n/* When configuring the server eventloop, we setup it so that the total number\n * of file descriptors we can handle are server.maxclients + RESERVED_FDS +\n * a few more to stay safe. Since RESERVED_FDS defaults to 32, we add 96\n * in order to make sure of not over provisioning more than 128 fds. */\n#define CONFIG_FDSET_INCR (CONFIG_MIN_RESERVED_FDS+96)\n\n/* Hash table parameters */\n#define HASHTABLE_MIN_FILL        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#define CMD_WRITE (1ULL<<0)            /* \"write\" flag */\n#define CMD_READONLY (1ULL<<1)         /* \"read-only\" flag */\n#define CMD_DENYOOM (1ULL<<2)          /* \"use-memory\" flag */\n#define CMD_MODULE (1ULL<<3)           /* Command exported by module. */\n#define CMD_ADMIN (1ULL<<4)            /* \"admin\" flag */\n#define CMD_PUBSUB (1ULL<<5)           /* \"pub-sub\" flag */\n#define CMD_NOSCRIPT (1ULL<<6)         /* \"no-script\" flag */\n#define CMD_RANDOM (1ULL<<7)           /* \"random\" flag */\n#define CMD_SORT_FOR_SCRIPT (1ULL<<8)  /* \"to-sort\" flag */\n#define CMD_LOADING (1ULL<<9)          /* \"ok-loading\" flag */\n#define CMD_STALE (1ULL<<10)           /* \"ok-stale\" flag */\n#define CMD_SKIP_MONITOR (1ULL<<11)    /* \"no-monitor\" flag */\n#define CMD_SKIP_SLOWLOG (1ULL<<12)    /* \"no-slowlog\" flag */\n#define CMD_ASKING (1ULL<<13)          /* \"cluster-asking\" flag */\n#define CMD_FAST (1ULL<<14)            /* \"fast\" flag */\n#define CMD_NO_AUTH (1ULL<<15)         /* \"no-auth\" flag */\n\n/* Command flags used by the module system. */\n#define CMD_MODULE_GETKEYS (1ULL<<16)  /* Use the modules getkeys interface. */\n#define CMD_MODULE_NO_CLUSTER (1ULL<<17) /* Deny on Redis Cluster. */\n\n/* Command flags that describe ACLs categories. */\n#define CMD_CATEGORY_KEYSPACE (1ULL<<18)\n#define CMD_CATEGORY_READ (1ULL<<19)\n#define CMD_CATEGORY_WRITE (1ULL<<20)\n#define CMD_CATEGORY_SET (1ULL<<21)\n#define CMD_CATEGORY_SORTEDSET (1ULL<<22)\n#define CMD_CATEGORY_LIST (1ULL<<23)\n#define CMD_CATEGORY_HASH (1ULL<<24)\n#define CMD_CATEGORY_STRING (1ULL<<25)\n#define CMD_CATEGORY_BITMAP (1ULL<<26)\n#define CMD_CATEGORY_HYPERLOGLOG (1ULL<<27)\n#define CMD_CATEGORY_GEO (1ULL<<28)\n#define CMD_CATEGORY_STREAM (1ULL<<29)\n#define CMD_CATEGORY_PUBSUB (1ULL<<30)\n#define CMD_CATEGORY_ADMIN (1ULL<<31)\n#define CMD_CATEGORY_FAST (1ULL<<32)\n#define CMD_CATEGORY_SLOW (1ULL<<33)\n#define CMD_CATEGORY_BLOCKING (1ULL<<34)\n#define CMD_CATEGORY_DANGEROUS (1ULL<<35)\n#define CMD_CATEGORY_CONNECTION (1ULL<<36)\n#define CMD_CATEGORY_TRANSACTION (1ULL<<37)\n#define CMD_CATEGORY_SCRIPTING (1ULL<<38)\n\n/* AOF states */\n#define AOF_OFF 0             /* AOF is off */\n#define AOF_ON 1              /* AOF is on */\n#define AOF_WAIT_REWRITE 2    /* AOF waits rewrite to start appending */\n\n/* Client flags */\n#define CLIENT_SLAVE (1<<0)   /* This client is a repliaca */\n#define CLIENT_MASTER (1<<1)  /* This client is a master */\n#define CLIENT_MONITOR (1<<2) /* This client is a slave monitor, see MONITOR */\n#define CLIENT_MULTI (1<<3)   /* This client is in a MULTI context */\n#define CLIENT_BLOCKED (1<<4) /* The client is waiting in a blocking operation */\n#define CLIENT_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */\n#define CLIENT_CLOSE_AFTER_REPLY (1<<6) /* Close after writing entire reply. */\n#define CLIENT_UNBLOCKED (1<<7) /* This client was unblocked and is stored in\n                                  server.unblocked_clients */\n#define CLIENT_LUA (1<<8) /* This is a non connected client used by Lua */\n#define CLIENT_ASKING (1<<9)     /* Client issued the ASKING command */\n#define CLIENT_CLOSE_ASAP (1<<10)/* Close this client ASAP */\n#define CLIENT_UNIX_SOCKET (1<<11) /* Client connected via Unix domain socket */\n#define CLIENT_DIRTY_EXEC (1<<12)  /* EXEC will fail for errors while queueing */\n#define CLIENT_MASTER_FORCE_REPLY (1<<13)  /* Queue replies even if is master */\n#define CLIENT_FORCE_AOF (1<<14)   /* Force AOF propagation of current cmd. */\n#define CLIENT_FORCE_REPL (1<<15)  /* Force replication of current cmd. */\n#define CLIENT_PRE_PSYNC (1<<16)   /* Instance don't understand PSYNC. */\n#define CLIENT_READONLY (1<<17)    /* Cluster client is in read-only state. */\n#define CLIENT_PUBSUB (1<<18)      /* Client is in Pub/Sub mode. */\n#define CLIENT_PREVENT_AOF_PROP (1<<19)  /* Don't propagate to AOF. */\n#define CLIENT_PREVENT_REPL_PROP (1<<20)  /* Don't propagate to slaves. */\n#define CLIENT_PREVENT_PROP (CLIENT_PREVENT_AOF_PROP|CLIENT_PREVENT_REPL_PROP)\n#define CLIENT_PENDING_WRITE (1<<21) /* Client has output to send but a write\n                                        handler is yet not installed. */\n#define CLIENT_REPLY_OFF (1<<22)   /* Don't send replies to client. */\n#define CLIENT_REPLY_SKIP_NEXT (1<<23)  /* Set CLIENT_REPLY_SKIP for next cmd */\n#define CLIENT_REPLY_SKIP (1<<24)  /* Don't send just this reply. */\n#define CLIENT_LUA_DEBUG (1<<25)  /* Run EVAL in debug mode. */\n#define CLIENT_LUA_DEBUG_SYNC (1<<26)  /* EVAL debugging without fork() */\n#define CLIENT_MODULE (1<<27) /* Non connected client used by some module. */\n#define CLIENT_PROTECTED (1<<28) /* Client should not be freed for now. */\n#define CLIENT_PENDING_READ (1<<29) /* The client has pending reads and was put\n                                       in the list of clients we can read\n                                       from. */\n#define CLIENT_PENDING_COMMAND (1<<30) /* Used in threaded I/O to signal after\n                                          we return single threaded that the\n                                          client has already pending commands\n                                          to be executed. */\n#define CLIENT_TRACKING (1ULL<<31) /* Client enabled keys tracking in order to\n                                   perform client side caching. */\n#define CLIENT_TRACKING_BROKEN_REDIR (1ULL<<32) /* Target client is invalid. */\n#define CLIENT_TRACKING_BCAST (1ULL<<33) /* Tracking in BCAST mode. */\n#define CLIENT_TRACKING_OPTIN (1ULL<<34)  /* Tracking in opt-in mode. */\n#define CLIENT_TRACKING_OPTOUT (1ULL<<35) /* Tracking in opt-out mode. */\n#define CLIENT_TRACKING_CACHING (1ULL<<36) /* CACHING yes/no was given,\n                                              depending on optin/optout mode. */\n#define CLIENT_TRACKING_NOLOOP (1ULL<<37) /* Don't send invalidation messages\n                                             about writes performed by myself.*/\n#define CLIENT_IN_TO_TABLE (1ULL<<38) /* This client is in the timeout table. */\n#define CLIENT_PROTOCOL_ERROR (1ULL<<39) /* Protocol error chatting with it. */\n\n/* Client block type (btype field in client structure)\n * if CLIENT_BLOCKED flag is set. */\n#define BLOCKED_NONE 0    /* Not blocked, no CLIENT_BLOCKED flag set. */\n#define BLOCKED_LIST 1    /* BLPOP & co. */\n#define BLOCKED_WAIT 2    /* WAIT for synchronous replication. */\n#define BLOCKED_MODULE 3  /* Blocked by a loadable module. */\n#define BLOCKED_STREAM 4  /* XREAD. */\n#define BLOCKED_ZSET 5    /* BZPOP et al. */\n#define BLOCKED_NUM 6     /* Number of blocked states. */\n\n/* Client request types */\n#define PROTO_REQ_INLINE 1\n#define PROTO_REQ_MULTIBULK 2\n\n/* Client classes for client limits, currently used only for\n * the max-client-output-buffer limit implementation. */\n#define CLIENT_TYPE_NORMAL 0 /* Normal req-reply clients + MONITORs */\n#define CLIENT_TYPE_SLAVE 1  /* Slaves. */\n#define CLIENT_TYPE_PUBSUB 2 /* Clients subscribed to PubSub channels. */\n#define CLIENT_TYPE_MASTER 3 /* Master. */\n#define CLIENT_TYPE_COUNT 4  /* Total number of client types. */\n#define CLIENT_TYPE_OBUF_COUNT 3 /* Number of clients to expose to output\n                                    buffer configuration. Just the first\n                                    three: normal, slave, pubsub. */\n\n/* Slave replication state. Used in server.repl_state for slaves to remember\n * what to do next. */\n#define REPL_STATE_NONE 0 /* No active replication */\n#define REPL_STATE_CONNECT 1 /* Must connect to master */\n#define REPL_STATE_CONNECTING 2 /* Connecting to master */\n/* --- Handshake states, must be ordered --- */\n#define REPL_STATE_RECEIVE_PONG 3 /* Wait for PING reply */\n#define REPL_STATE_SEND_AUTH 4 /* Send AUTH to master */\n#define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */\n#define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */\n#define REPL_STATE_RECEIVE_PORT 7 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_IP 8 /* Send REPLCONF ip-address */\n#define REPL_STATE_RECEIVE_IP 9 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_CAPA 10 /* Send REPLCONF capa */\n#define REPL_STATE_RECEIVE_CAPA 11 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_PSYNC 12 /* Send PSYNC */\n#define REPL_STATE_RECEIVE_PSYNC 13 /* Wait for PSYNC reply */\n/* --- End of handshake states --- */\n#define REPL_STATE_TRANSFER 14 /* Receiving .rdb from master */\n#define REPL_STATE_CONNECTED 15 /* Connected to master */\n\n/* State of slaves from the POV of the master. Used in client->replstate.\n * In SEND_BULK and ONLINE state the slave receives new updates\n * in its output queue. In the WAIT_BGSAVE states instead the server is waiting\n * to start the next background saving in order to send updates to it. */\n#define SLAVE_STATE_WAIT_BGSAVE_START 6 /* We need to produce a new RDB file. */\n#define SLAVE_STATE_WAIT_BGSAVE_END 7 /* Waiting RDB file creation to finish. */\n#define SLAVE_STATE_SEND_BULK 8 /* Sending RDB file to slave. */\n#define SLAVE_STATE_ONLINE 9 /* RDB file transmitted, sending just updates. */\n\n/* Slave capabilities. */\n#define SLAVE_CAPA_NONE 0\n#define SLAVE_CAPA_EOF (1<<0)    /* Can parse the RDB EOF streaming format. */\n#define SLAVE_CAPA_PSYNC2 (1<<1) /* Supports PSYNC2 protocol. */\n\n/* Synchronous read timeout - slave side */\n#define CONFIG_REPL_SYNCIO_TIMEOUT 5\n\n/* List related stuff */\n#define LIST_HEAD 0\n#define LIST_TAIL 1\n#define ZSET_MIN 0\n#define ZSET_MAX 1\n\n/* Sort operations */\n#define SORT_OP_GET 0\n\n/* Log levels */\n#define LL_DEBUG 0\n#define LL_VERBOSE 1\n#define LL_NOTICE 2\n#define LL_WARNING 3\n#define LL_RAW (1<<10) /* Modifier to log without timestamp */\n\n/* Supervision options */\n#define SUPERVISED_NONE 0\n#define SUPERVISED_AUTODETECT 1\n#define SUPERVISED_SYSTEMD 2\n#define SUPERVISED_UPSTART 3\n\n/* Anti-warning macro... */\n#define UNUSED(V) ((void) V)\n\n#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 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\n/* Replication diskless load defines */\n#define REPL_DISKLESS_LOAD_DISABLED 0\n#define REPL_DISKLESS_LOAD_WHEN_DB_EMPTY 1\n#define REPL_DISKLESS_LOAD_SWAPDB 2\n\n/* Sets operations codes */\n#define SET_OP_UNION 0\n#define SET_OP_DIFF 1\n#define SET_OP_INTER 2\n\n/* Redis maxmemory strategies. Instead of using just incremental number\n * for this defines, we use a set of flags so that testing for certain\n * properties common to multiple policies is faster. */\n#define MAXMEMORY_FLAG_LRU (1<<0)\n#define MAXMEMORY_FLAG_LFU (1<<1)\n#define MAXMEMORY_FLAG_ALLKEYS (1<<2)\n#define MAXMEMORY_FLAG_NO_SHARED_INTEGERS \\\n    (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)\n\n#define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU)\n#define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU)\n#define MAXMEMORY_VOLATILE_TTL (2<<8)\n#define MAXMEMORY_VOLATILE_RANDOM (3<<8)\n#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_ALLKEYS)\n#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU|MAXMEMORY_FLAG_ALLKEYS)\n#define MAXMEMORY_ALLKEYS_RANDOM ((6<<8)|MAXMEMORY_FLAG_ALLKEYS)\n#define MAXMEMORY_NO_EVICTION (7<<8)\n\n/* Units */\n#define UNIT_SECONDS 0\n#define UNIT_MILLISECONDS 1\n\n/* SHUTDOWN flags */\n#define SHUTDOWN_NOFLAGS 0      /* No flags. */\n#define SHUTDOWN_SAVE 1         /* Force SAVE on SHUTDOWN even if no save\n                                   points are configured. */\n#define SHUTDOWN_NOSAVE 2       /* Don't SAVE on SHUTDOWN. */\n\n/* Command call flags, see call() function */\n#define CMD_CALL_NONE 0\n#define CMD_CALL_SLOWLOG (1<<0)\n#define CMD_CALL_STATS (1<<1)\n#define CMD_CALL_PROPAGATE_AOF (1<<2)\n#define CMD_CALL_PROPAGATE_REPL (1<<3)\n#define CMD_CALL_PROPAGATE (CMD_CALL_PROPAGATE_AOF|CMD_CALL_PROPAGATE_REPL)\n#define CMD_CALL_FULL (CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_PROPAGATE)\n#define CMD_CALL_NOWRAP (1<<4)  /* Don't wrap also propagate array into\n                                   MULTI/EXEC: the caller will handle it.  */\n\n/* Command propagation flags, see propagate() function */\n#define PROPAGATE_NONE 0\n#define PROPAGATE_AOF 1\n#define PROPAGATE_REPL 2\n\n/* RDB active child save type. */\n#define RDB_CHILD_TYPE_NONE 0\n#define RDB_CHILD_TYPE_DISK 1     /* RDB is written to disk. */\n#define RDB_CHILD_TYPE_SOCKET 2   /* RDB is written to slave socket. */\n\n/* Keyspace changes notification classes. Every class is associated with a\n * character for configuration purposes. */\n#define NOTIFY_KEYSPACE (1<<0)    /* K */\n#define NOTIFY_KEYEVENT (1<<1)    /* E */\n#define NOTIFY_GENERIC (1<<2)     /* g */\n#define NOTIFY_STRING (1<<3)      /* $ */\n#define NOTIFY_LIST (1<<4)        /* l */\n#define NOTIFY_SET (1<<5)         /* s */\n#define NOTIFY_HASH (1<<6)        /* h */\n#define NOTIFY_ZSET (1<<7)        /* z */\n#define NOTIFY_EXPIRED (1<<8)     /* x */\n#define NOTIFY_EVICTED (1<<9)     /* e */\n#define NOTIFY_STREAM (1<<10)     /* t */\n#define NOTIFY_KEY_MISS (1<<11)   /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */\n#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM) /* A flag */\n\n/* Get the first bind addr or NULL */\n#define NET_FIRST_BIND_ADDR (server.bindaddr_count ? server.bindaddr[0] : NULL)\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 serverAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_serverAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))\n#define serverAssert(_e) ((_e)?(void)0 : (_serverAssert(#_e,__FILE__,__LINE__),_exit(1)))\n#define serverPanic(...) _serverPanic(__FILE__,__LINE__,__VA_ARGS__),_exit(1)\n\n/*-----------------------------------------------------------------------------\n * Data types\n *----------------------------------------------------------------------------*/\n\n/* A redis object, that is a type able to hold a string / list / set */\n\n/* The actual Redis Object */\n#define OBJ_STRING 0    /* String object. */\n#define OBJ_LIST 1      /* List object. */\n#define OBJ_SET 2       /* Set object. */\n#define OBJ_ZSET 3      /* Sorted set object. */\n#define OBJ_HASH 4      /* Hash object. */\n\n/* The \"module\" object type is a special one that signals that the object\n * is one directly managed by a Redis module. In this case the value points\n * to a moduleValue struct, which contains the object value (which is only\n * handled by the module itself) and the RedisModuleType struct which lists\n * function pointers in order to serialize, deserialize, AOF-rewrite and\n * free the object.\n *\n * Inside the RDB file, module types are encoded as OBJ_MODULE followed\n * by a 64 bit module type ID, which has a 54 bits module-specific signature\n * in order to dispatch the loading to the right module, plus a 10 bits\n * encoding version. */\n#define OBJ_MODULE 5    /* Module object. */\n#define OBJ_STREAM 6    /* Stream object. */\n\n/* Extract encver / signature from a module type ID. */\n#define REDISMODULE_TYPE_ENCVER_BITS 10\n#define REDISMODULE_TYPE_ENCVER_MASK ((1<<REDISMODULE_TYPE_ENCVER_BITS)-1)\n#define REDISMODULE_TYPE_ENCVER(id) (id & REDISMODULE_TYPE_ENCVER_MASK)\n#define REDISMODULE_TYPE_SIGN(id) ((id & ~((uint64_t)REDISMODULE_TYPE_ENCVER_MASK)) >>REDISMODULE_TYPE_ENCVER_BITS)\n\n/* Bit flags for moduleTypeAuxSaveFunc */\n#define REDISMODULE_AUX_BEFORE_RDB (1<<0)\n#define REDISMODULE_AUX_AFTER_RDB (1<<1)\n\nstruct RedisModule;\nstruct RedisModuleIO;\nstruct RedisModuleDigest;\nstruct RedisModuleCtx;\nstruct redisObject;\n\n/* Each module type implementation should export a set of methods in order\n * to serialize and deserialize the value in the RDB file, rewrite the AOF\n * log, create the digest for \"DEBUG DIGEST\", and free the value when a key\n * is deleted. */\ntypedef void *(*moduleTypeLoadFunc)(struct RedisModuleIO *io, int encver);\ntypedef void (*moduleTypeSaveFunc)(struct RedisModuleIO *io, void *value);\ntypedef int (*moduleTypeAuxLoadFunc)(struct RedisModuleIO *rdb, int encver, int when);\ntypedef void (*moduleTypeAuxSaveFunc)(struct RedisModuleIO *rdb, int when);\ntypedef void (*moduleTypeRewriteFunc)(struct RedisModuleIO *io, struct redisObject *key, void *value);\ntypedef void (*moduleTypeDigestFunc)(struct RedisModuleDigest *digest, void *value);\ntypedef size_t (*moduleTypeMemUsageFunc)(const void *value);\ntypedef void (*moduleTypeFreeFunc)(void *value);\n\n/* A callback that is called when the client authentication changes. This\n * needs to be exposed since you can't cast a function pointer to (void *) */\ntypedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);\n\n\n/* The module type, which is referenced in each value of a given type, defines\n * the methods and links to the module exporting the type. */\ntypedef struct RedisModuleType {\n    uint64_t id; /* Higher 54 bits of type ID + 10 lower bits of encoding ver. */\n    struct RedisModule *module;\n    moduleTypeLoadFunc rdb_load;\n    moduleTypeSaveFunc rdb_save;\n    moduleTypeRewriteFunc aof_rewrite;\n    moduleTypeMemUsageFunc mem_usage;\n    moduleTypeDigestFunc digest;\n    moduleTypeFreeFunc free;\n    moduleTypeAuxLoadFunc aux_load;\n    moduleTypeAuxSaveFunc aux_save;\n    int aux_save_triggers;\n    char name[10]; /* 9 bytes name + null term. Charset: A-Z a-z 0-9 _- */\n} moduleType;\n\n/* In Redis objects 'robj' structures of type OBJ_MODULE, the value pointer\n * is set to the following structure, referencing the moduleType structure\n * in order to work with the value, and at the same time providing a raw\n * pointer to the value, as created by the module commands operating with\n * the module type.\n *\n * So for example in order to free such a value, it is possible to use\n * the following code:\n *\n *  if (robj->type == OBJ_MODULE) {\n *      moduleValue *mt = robj->ptr;\n *      mt->type->free(mt->value);\n *      zfree(mt); // We need to release this in-the-middle struct as well.\n *  }\n */\ntypedef struct moduleValue {\n    moduleType *type;\n    void *value;\n} moduleValue;\n\n/* This is a wrapper for the 'rio' streams used inside rdb.c in Redis, so that\n * the user does not have to take the total count of the written bytes nor\n * to care about error conditions. */\ntypedef struct RedisModuleIO {\n    size_t bytes;       /* Bytes read / written so far. */\n    rio *rio;           /* Rio stream. */\n    moduleType *type;   /* Module type doing the operation. */\n    int error;          /* True if error condition happened. */\n    int ver;            /* Module serialization version: 1 (old),\n                         * 2 (current version with opcodes annotation). */\n    struct RedisModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/\n    struct redisObject *key;    /* Optional name of key processed */\n} RedisModuleIO;\n\n/* Macro to initialize an IO context. Note that the 'ver' field is populated\n * inside rdb.c according to the version of the value to load. */\n#define moduleInitIOContext(iovar,mtype,rioptr,keyptr) do { \\\n    iovar.rio = rioptr; \\\n    iovar.type = mtype; \\\n    iovar.bytes = 0; \\\n    iovar.error = 0; \\\n    iovar.ver = 0; \\\n    iovar.key = keyptr; \\\n    iovar.ctx = NULL; \\\n} while(0);\n\n/* This is a structure used to export DEBUG DIGEST capabilities to Redis\n * modules. We want to capture both the ordered and unordered elements of\n * a data structure, so that a digest can be created in a way that correctly\n * reflects the values. See the DEBUG DIGEST command implementation for more\n * background. */\ntypedef struct RedisModuleDigest {\n    unsigned char o[20];    /* Ordered elements. */\n    unsigned char x[20];    /* Xored elements. */\n} RedisModuleDigest;\n\n/* Just start with a digest composed of all zero bytes. */\n#define moduleInitDigestContext(mdvar) do { \\\n    memset(mdvar.o,0,sizeof(mdvar.o)); \\\n    memset(mdvar.x,0,sizeof(mdvar.x)); \\\n} while(0);\n\n/* Objects encoding. Some kind of objects like Strings and Hashes can be\n * internally represented in multiple ways. The 'encoding' field of the object\n * is set to one of this fields for this object. */\n#define OBJ_ENCODING_RAW 0     /* Raw representation */\n#define OBJ_ENCODING_INT 1     /* Encoded as integer */\n#define OBJ_ENCODING_HT 2      /* Encoded as hash table */\n#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */\n#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */\n#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */\n#define OBJ_ENCODING_INTSET 6  /* Encoded as intset */\n#define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */\n#define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */\n#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */\n#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */\n\n#define LRU_BITS 24\n#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */\n#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */\n\n#define OBJ_SHARED_REFCOUNT INT_MAX     /* Global object never destroyed. */\n#define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */\n#define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT\ntypedef struct redisObject {\n    unsigned type:4;\n    unsigned encoding:4;\n    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or\n                            * LFU data (least significant 8 bits frequency\n                            * and most significant 16 bits access time). */\n    int refcount;\n    void *ptr;\n} robj;\n\n/* The a string name for an object's type as listed above\n * Native types are checked against the OBJ_STRING, OBJ_LIST, OBJ_* defines,\n * and Module types have their registered name returned. */\nchar *getObjectTypeName(robj*);\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 = OBJ_STATIC_REFCOUNT; \\\n    _var.type = OBJ_STRING; \\\n    _var.encoding = OBJ_ENCODING_RAW; \\\n    _var.ptr = _ptr; \\\n} while(0)\n\nstruct evictionPoolEntry; /* Defined in evict.c */\n\n/* This structure is used in order to represent the output buffer of a client,\n * which is actually a linked list of blocks like that, that is: client->reply. */\ntypedef struct clientReplyBlock {\n    size_t size, used;\n    char buf[];\n} clientReplyBlock;\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    dict *dict;                 /* The keyspace for this DB */\n    dict *expires;              /* Timeout of keys with a timeout set */\n    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/\n    dict *ready_keys;           /* Blocked keys that received a PUSH */\n    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */\n    int id;                     /* Database ID */\n    long long avg_ttl;          /* Average TTL, just for stats */\n    unsigned long expires_cursor; /* Cursor of the active expire cycle. */\n    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */\n} redisDb;\n\n/* Client MULTI/EXEC state */\ntypedef struct multiCmd {\n    robj **argv;\n    int argc;\n    struct redisCommand *cmd;\n} multiCmd;\n\ntypedef struct multiState {\n    multiCmd *commands;     /* Array of MULTI commands */\n    int count;              /* Total number of MULTI commands */\n    int cmd_flags;          /* The accumulated command flags OR-ed together.\n                               So if at least a command has a given flag, it\n                               will be set in this field. */\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. */\ntypedef struct blockingState {\n    /* Generic fields. */\n    mstime_t timeout;       /* Blocking operation timeout. If UNIX current time\n                             * is > timeout then the operation timed out. */\n\n    /* BLOCKED_LIST, BLOCKED_ZSET and BLOCKED_STREAM */\n    dict *keys;             /* The keys we are waiting to terminate a blocking\n                             * operation such as BLPOP or XREAD. Or NULL. */\n    robj *target;           /* The key that should receive the element,\n                             * for BRPOPLPUSH. */\n\n    /* BLOCK_STREAM */\n    size_t xread_count;     /* XREAD COUNT option. */\n    robj *xread_group;      /* XREADGROUP group name. */\n    robj *xread_consumer;   /* XREADGROUP consumer name. */\n    mstime_t xread_retry_time, xread_retry_ttl;\n    int xread_group_noack;\n\n    /* BLOCKED_WAIT */\n    int numreplicas;        /* Number of replicas we are waiting for ACK. */\n    long long reploffset;   /* Replication offset to reach. */\n\n    /* BLOCKED_MODULE */\n    void *module_blocked_handle; /* RedisModuleBlockedClient structure.\n                                    which is opaque for the Redis core, only\n                                    handled in module.c. */\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. */\ntypedef struct readyList {\n    redisDb *db;\n    robj *key;\n} readyList;\n\n/* This structure represents a Redis user. This is useful for ACLs, the\n * user is associated to the connection after the connection is authenticated.\n * If there is no associated user, the connection uses the default user. */\n#define USER_COMMAND_BITS_COUNT 1024    /* The total number of command bits\n                                           in the user structure. The last valid\n                                           command ID we can set in the user\n                                           is USER_COMMAND_BITS_COUNT-1. */\n#define USER_FLAG_ENABLED (1<<0)        /* The user is active. */\n#define USER_FLAG_DISABLED (1<<1)       /* The user is disabled. */\n#define USER_FLAG_ALLKEYS (1<<2)        /* The user can mention any key. */\n#define USER_FLAG_ALLCOMMANDS (1<<3)    /* The user can run all commands. */\n#define USER_FLAG_NOPASS      (1<<4)    /* The user requires no password, any\n                                           provided password will work. For the\n                                           default user, this also means that\n                                           no AUTH is needed, and every\n                                           connection is immediately\n                                           authenticated. */\ntypedef struct {\n    sds name;       /* The username as an SDS string. */\n    uint64_t flags; /* See USER_FLAG_* */\n\n    /* The bit in allowed_commands is set if this user has the right to\n     * execute this command. In commands having subcommands, if this bit is\n     * set, then all the subcommands are also available.\n     *\n     * If the bit for a given command is NOT set and the command has\n     * subcommands, Redis will also check allowed_subcommands in order to\n     * understand if the command can be executed. */\n    uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64];\n\n    /* This array points, for each command ID (corresponding to the command\n     * bit set in allowed_commands), to an array of SDS strings, terminated by\n     * a NULL pointer, with all the sub commands that can be executed for\n     * this command. When no subcommands matching is used, the field is just\n     * set to NULL to avoid allocating USER_COMMAND_BITS_COUNT pointers. */\n    sds **allowed_subcommands;\n    list *passwords; /* A list of SDS valid passwords for this user. */\n    list *patterns;  /* A list of allowed key patterns. If this field is NULL\n                        the user cannot mention any key in a command, unless\n                        the flag ALLKEYS is set in the user. */\n} user;\n\n/* With multiplexing we need to take per-client state.\n * Clients are taken in a linked list. */\n\n#define CLIENT_ID_AOF (UINT64_MAX) /* Reserved ID for the AOF client. If you\n                                      need more reserved IDs use UINT64_MAX-1,\n                                      -2, ... and so forth. */\n\ntypedef struct client {\n    uint64_t id;            /* Client incremental unique ID. */\n    connection *conn;\n    int resp;               /* RESP protocol version. Can be 2 or 3. */\n    redisDb *db;            /* Pointer to currently SELECTed DB. */\n    robj *name;             /* As set by CLIENT SETNAME. */\n    sds querybuf;           /* Buffer we use to accumulate client queries. */\n    size_t qb_pos;          /* The position we have read in querybuf. */\n    sds pending_querybuf;   /* If this client is flagged as master, this buffer\n                               represents the yet not applied portion of the\n                               replication stream that we are receiving from\n                               the master. */\n    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size. */\n    int argc;               /* Num of arguments of current command. */\n    robj **argv;            /* Arguments of current command. */\n    struct redisCommand *cmd, *lastcmd;  /* Last command executed. */\n    user *user;             /* User associated with this connection. If the\n                               user is set to NULL the connection can do\n                               anything (admin). */\n    int reqtype;            /* Request protocol type: PROTO_REQ_* */\n    int multibulklen;       /* Number of multi bulk arguments left to read. */\n    long bulklen;           /* Length of bulk argument in multi bulk request. */\n    list *reply;            /* List of reply objects to send to the client. */\n    unsigned long long reply_bytes; /* Tot bytes of objects in reply list. */\n    size_t sentlen;         /* Amount of bytes already sent in the current\n                               buffer or object being sent. */\n    time_t ctime;           /* Client creation time. */\n    time_t lastinteraction; /* Time of the last interaction, used for timeout */\n    time_t obuf_soft_limit_reached_time;\n    uint64_t flags;         /* Client flags: CLIENT_* macros. */\n    int authenticated;      /* Needed when the default user requires auth. */\n    int replstate;          /* Replication state if this is a slave. */\n    int repl_put_online_on_ack; /* Install slave write handler on first ACK. */\n    int repldbfd;           /* Replication DB file descriptor. */\n    off_t repldboff;        /* Replication DB file offset. */\n    off_t repldbsize;       /* Replication DB file size. */\n    sds replpreamble;       /* Replication DB preamble. */\n    long long read_reploff; /* Read replication offset if this is a master. */\n    long long reploff;      /* Applied replication offset if this is a master. */\n    long long repl_ack_off; /* Replication ack offset, if this is a slave. */\n    long long repl_ack_time;/* Replication ack time, if this is a slave. */\n    long long psync_initial_offset; /* FULLRESYNC reply offset other slaves\n                                       copying this slave output buffer\n                                       should use. */\n    char replid[CONFIG_RUN_ID_SIZE+1]; /* Master replication ID (if master). */\n    int slave_listening_port; /* As configured with: SLAVECONF listening-port */\n    char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */\n    int slave_capa;         /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */\n    multiState mstate;      /* MULTI/EXEC state */\n    int btype;              /* Type of blocking op if CLIENT_BLOCKED. */\n    blockingState bpop;     /* blocking state */\n    long long woff;         /* Last write global replication offset. */\n    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS */\n    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) */\n    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) */\n    sds peerid;             /* Cached peer ID. */\n    listNode *client_list_node; /* list node in client list */\n    RedisModuleUserChangedFunc auth_callback; /* Module callback to execute\n                                               * when the authenticated user\n                                               * changes. */\n    void *auth_callback_privdata; /* Private data that is passed when the auth\n                                   * changed callback is executed. Opaque for\n                                   * Redis Core. */\n    void *auth_module;      /* The module that owns the callback, which is used\n                             * to disconnect the client if the module is\n                             * unloaded for cleanup. Opaque for Redis Core.*/\n\n    /* If this client is in tracking mode and this field is non zero,\n     * invalidation messages for keys fetched by this client will be send to\n     * the specified client ID. */\n    uint64_t client_tracking_redirection;\n    rax *client_tracking_prefixes; /* A dictionary of prefixes we are already\n                                      subscribed to in BCAST mode, in the\n                                      context of client side caching. */\n    /* In clientsCronTrackClientsMemUsage() we track the memory usage of\n     * each client and add it to the sum of all the clients of a given type,\n     * however we need to remember what was the old contribution of each\n     * client, and in which categoty the client was, in order to remove it\n     * before adding it the new value. */\n    uint64_t client_cron_last_memory_usage;\n    int      client_cron_last_memory_type;\n    /* Response buffer */\n    int bufpos;\n    char buf[PROTO_REPLY_CHUNK_BYTES];\n} client;\n\nstruct saveparam {\n    time_t seconds;\n    int changes;\n};\n\nstruct moduleLoadQueueEntry {\n    sds path;\n    int argc;\n    robj **argv;\n};\n\nstruct sharedObjectsStruct {\n    robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,\n    *colon, *queued, *null[4], *nullarray[4], *emptymap[4], *emptyset[4],\n    *emptyarray, *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, *unlink,\n    *rpop, *lpop, *lpush, *rpoplpush, *zpopmin, *zpopmax, *emptyscan,\n    *multi, *exec,\n    *select[PROTO_SHARED_SELECT_CMDS],\n    *integers[OBJ_SHARED_INTEGERS],\n    *mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* \"*<value>\\r\\n\" */\n    *bulkhdr[OBJ_SHARED_BULKHDR_LEN];  /* \"$<value>\\r\\n\" */\n    sds minstring, maxstring;\n};\n\n/* ZSETs use a specialized version of Skiplists */\ntypedef struct zskiplistNode {\n    sds ele;\n    double score;\n    struct zskiplistNode *backward;\n    struct zskiplistLevel {\n        struct zskiplistNode *forward;\n        unsigned long span;\n    } level[];\n} zskiplistNode;\n\ntypedef struct zskiplist {\n    struct zskiplistNode *header, *tail;\n    unsigned long length;\n    int level;\n} zskiplist;\n\ntypedef struct zset {\n    dict *dict;\n    zskiplist *zsl;\n} zset;\n\ntypedef struct clientBufferLimitsConfig {\n    unsigned long long hard_limit_bytes;\n    unsigned long long soft_limit_bytes;\n    time_t soft_limit_seconds;\n} clientBufferLimitsConfig;\n\nextern clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT];\n\n/* The redisOp structure defines a Redis Operation, that is an instance of\n * a command with an argument vector, database ID, propagation target\n * (PROPAGATE_*), and command pointer.\n *\n * Currently only used to additionally propagate more commands to AOF/Replication\n * after the propagation of the executed command. */\ntypedef struct redisOp {\n    robj **argv;\n    int argc, dbid, target;\n    struct redisCommand *cmd;\n} redisOp;\n\n/* Defines an array of Redis operations. There is an API to add to this\n * structure in a easy way.\n *\n * redisOpArrayInit();\n * redisOpArrayAppend();\n * redisOpArrayFree();\n */\ntypedef struct redisOpArray {\n    redisOp *ops;\n    int numops;\n} redisOpArray;\n\n/* This structure is returned by the getMemoryOverheadData() function in\n * order to return memory overhead information. */\nstruct redisMemOverhead {\n    size_t peak_allocated;\n    size_t total_allocated;\n    size_t startup_allocated;\n    size_t repl_backlog;\n    size_t clients_slaves;\n    size_t clients_normal;\n    size_t aof_buffer;\n    size_t lua_caches;\n    size_t overhead_total;\n    size_t dataset;\n    size_t total_keys;\n    size_t bytes_per_key;\n    float dataset_perc;\n    float peak_perc;\n    float total_frag;\n    ssize_t total_frag_bytes;\n    float allocator_frag;\n    ssize_t allocator_frag_bytes;\n    float allocator_rss;\n    ssize_t allocator_rss_bytes;\n    float rss_extra;\n    size_t rss_extra_bytes;\n    size_t num_dbs;\n    struct {\n        size_t dbid;\n        size_t overhead_ht_main;\n        size_t overhead_ht_expires;\n    } *db;\n};\n\n/* This structure can be optionally passed to RDB save/load functions in\n * order to implement additional functionalities, by storing and loading\n * metadata to the RDB file.\n *\n * Currently the only use is to select a DB at load time, useful in\n * replication in order to make sure that chained slaves (slaves of slaves)\n * select the correct DB and are able to accept the stream coming from the\n * top-level master. */\ntypedef struct rdbSaveInfo {\n    /* Used saving and loading. */\n    int repl_stream_db;  /* DB to select in server.master client. */\n\n    /* Used only loading. */\n    int repl_id_is_set;  /* True if repl_id field is set. */\n    char repl_id[CONFIG_RUN_ID_SIZE+1];     /* Replication ID. */\n    long long repl_offset;                  /* Replication offset. */\n} rdbSaveInfo;\n\n#define RDB_SAVE_INFO_INIT {-1,0,\"000000000000000000000000000000\",-1}\n\nstruct malloc_stats {\n    size_t zmalloc_used;\n    size_t process_rss;\n    size_t allocator_allocated;\n    size_t allocator_active;\n    size_t allocator_resident;\n};\n\n/*-----------------------------------------------------------------------------\n * TLS Context Configuration\n *----------------------------------------------------------------------------*/\n\ntypedef struct redisTLSContextConfig {\n    char *cert_file;\n    char *key_file;\n    char *dh_params_file;\n    char *ca_cert_file;\n    char *ca_cert_dir;\n    char *protocols;\n    char *ciphers;\n    char *ciphersuites;\n    int prefer_server_ciphers;\n} redisTLSContextConfig;\n\n/*-----------------------------------------------------------------------------\n * Global server state\n *----------------------------------------------------------------------------*/\n\nstruct clusterState;\n\n/* AIX defines hz to __hz, we don't use this define and in order to allow\n * Redis build on AIX we need to undef it. */\n#ifdef _AIX\n#undef hz\n#endif\n\n#define CHILD_INFO_MAGIC 0xC17DDA7A12345678LL\n#define CHILD_INFO_TYPE_RDB 0\n#define CHILD_INFO_TYPE_AOF 1\n#define CHILD_INFO_TYPE_MODULE 3\n\nstruct redisServer {\n    /* General */\n    pid_t pid;                  /* Main process pid. */\n    char *configfile;           /* Absolute config file path, or NULL */\n    char *executable;           /* Absolute executable file path. */\n    char **exec_argv;           /* Executable argv vector (copy). */\n    int dynamic_hz;             /* Change hz value depending on # of clients. */\n    int config_hz;              /* Configured HZ value. May be different than\n                                   the actual 'hz' field value if dynamic-hz\n                                   is enabled. */\n    int hz;                     /* serverCron() calls frequency in hertz */\n    redisDb *db;\n    dict *commands;             /* Command table */\n    dict *orig_commands;        /* Command table before command renaming. */\n    aeEventLoop *el;\n    _Atomic unsigned int lruclock; /* Clock for LRU eviction */\n    int shutdown_asap;          /* SHUTDOWN needed ASAP */\n    int activerehashing;        /* Incremental rehash in serverCron() */\n    int active_defrag_running;  /* Active defragmentation running (holds current scan aggressiveness) */\n    char *pidfile;              /* PID file path */\n    int arch_bits;              /* 32 or 64 depending on sizeof(long) */\n    int cronloops;              /* Number of times the cron function run */\n    char runid[CONFIG_RUN_ID_SIZE+1];  /* ID always different at every exec. */\n    int sentinel_mode;          /* True if this instance is a Sentinel. */\n    size_t initial_memory_usage; /* Bytes used after initialization. */\n    int always_show_logo;       /* Show logo even for non-stdout logging. */\n    /* Modules */\n    dict *moduleapi;            /* Exported core APIs dictionary for modules. */\n    dict *sharedapi;            /* Like moduleapi but containing the APIs that\n                                   modules share with each other. */\n    list *loadmodule_queue;     /* List of modules to load at startup. */\n    int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a\n                                   client blocked on a module command needs\n                                   to be processed. */\n    pid_t module_child_pid;     /* PID of module child */\n    /* Networking */\n    int port;                   /* TCP listening port */\n    int tls_port;               /* TLS listening port */\n    int tcp_backlog;            /* TCP listen() backlog */\n    char *bindaddr[CONFIG_BINDADDR_MAX]; /* Addresses we should bind to */\n    int bindaddr_count;         /* Number of addresses in server.bindaddr[] */\n    char *unixsocket;           /* UNIX socket path */\n    mode_t unixsocketperm;      /* UNIX socket permission */\n    int ipfd[CONFIG_BINDADDR_MAX]; /* TCP socket file descriptors */\n    int ipfd_count;             /* Used slots in ipfd[] */\n    int tlsfd[CONFIG_BINDADDR_MAX]; /* TLS socket file descriptors */\n    int tlsfd_count;            /* Used slots in tlsfd[] */\n    int sofd;                   /* Unix socket file descriptor */\n    int cfd[CONFIG_BINDADDR_MAX];/* Cluster bus listening socket */\n    int cfd_count;              /* Used slots in cfd[] */\n    list *clients;              /* List of active clients */\n    list *clients_to_close;     /* Clients to close asynchronously */\n    list *clients_pending_write; /* There is to write or install handler. */\n    list *clients_pending_read;  /* Client has pending read socket buffers. */\n    list *slaves, *monitors;    /* List of slaves and MONITORs */\n    client *current_client;     /* Current client executing the command. */\n    rax *clients_timeout_table; /* Radix tree for blocked clients timeouts. */\n    long fixed_time_expire;     /* If > 0, expire keys against server.mstime. */\n    rax *clients_index;         /* Active clients dictionary by client ID. */\n    int clients_paused;         /* True if clients are currently paused */\n    mstime_t clients_pause_end_time; /* Time when we undo clients_paused */\n    char neterr[ANET_ERR_LEN];   /* Error buffer for anet.c */\n    dict *migrate_cached_sockets;/* MIGRATE cached sockets */\n    _Atomic uint64_t next_client_id; /* Next client unique ID. Incremental. */\n    int protected_mode;         /* Don't accept external connections. */\n    int gopher_enabled;         /* If true the server will reply to gopher\n                                   queries. Will still serve RESP2 queries. */\n    int io_threads_num;         /* Number of IO threads to use. */\n    int io_threads_do_reads;    /* Read and parse from IO threads? */\n    long long events_processed_while_blocked; /* processEventsWhileBlocked() */\n\n    /* RDB / AOF loading information */\n    int loading;                /* We are loading data from disk if true */\n    off_t loading_total_bytes;\n    off_t loading_loaded_bytes;\n    time_t loading_start_time;\n    off_t loading_process_events_interval_bytes;\n    /* Fast pointers to often looked up command */\n    struct redisCommand *delCommand, *multiCommand, *lpushCommand,\n                        *lpopCommand, *rpopCommand, *zpopminCommand,\n                        *zpopmaxCommand, *sremCommand, *execCommand,\n                        *expireCommand, *pexpireCommand, *xclaimCommand,\n                        *xgroupCommand, *rpoplpushCommand;\n    /* Fields used only for stats */\n    time_t stat_starttime;          /* Server start time */\n    long long stat_numcommands;     /* Number of processed commands */\n    long long stat_numconnections;  /* Number of connections received */\n    long long stat_expiredkeys;     /* Number of expired keys */\n    double stat_expired_stale_perc; /* Percentage of keys probably expired */\n    long long stat_expired_time_cap_reached_count; /* Early expire cylce stops.*/\n    long long stat_expire_cycle_time_used; /* Cumulative microseconds used. */\n    long long stat_evictedkeys;     /* Number of evicted keys (maxmemory) */\n    long long stat_keyspace_hits;   /* Number of successful lookups of keys */\n    long long stat_keyspace_misses; /* Number of failed lookups of keys */\n    long long stat_active_defrag_hits;      /* number of allocations moved */\n    long long stat_active_defrag_misses;    /* number of allocations scanned but not moved */\n    long long stat_active_defrag_key_hits;  /* number of keys with moved allocations */\n    long long stat_active_defrag_key_misses;/* number of keys scanned and not moved */\n    long long stat_active_defrag_scanned;   /* number of dictEntries scanned */\n    size_t stat_peak_memory;        /* Max used memory record */\n    long long stat_fork_time;       /* Time needed to perform latest fork() */\n    double stat_fork_rate;          /* Fork rate in GB/sec. */\n    long long stat_rejected_conn;   /* Clients rejected because of maxclients */\n    long long stat_sync_full;       /* Number of full resyncs with slaves. */\n    long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */\n    long long stat_sync_partial_err;/* Number of unaccepted PSYNC requests. */\n    list *slowlog;                  /* SLOWLOG list of commands */\n    long long slowlog_entry_id;     /* SLOWLOG current entry ID */\n    long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */\n    unsigned long slowlog_max_len;     /* SLOWLOG max number of items logged */\n    struct malloc_stats cron_malloc_stats; /* sampled in serverCron(). */\n    _Atomic long long stat_net_input_bytes; /* Bytes read from network. */\n    _Atomic long long stat_net_output_bytes; /* Bytes written to network. */\n    size_t stat_rdb_cow_bytes;      /* Copy on write bytes during RDB saving. */\n    size_t stat_aof_cow_bytes;      /* Copy on write bytes during AOF rewrite. */\n    size_t stat_module_cow_bytes;   /* Copy on write bytes during module fork. */\n    uint64_t stat_clients_type_memory[CLIENT_TYPE_COUNT];/* Mem usage by type */\n    long long stat_unexpected_error_replies; /* Number of unexpected (aof-loading, replica to master, etc.) error replies */\n    /* The following two are used to track instantaneous metrics, like\n     * number of operations per second, network traffic. */\n    struct {\n        long long last_sample_time; /* Timestamp of last sample in ms */\n        long long last_sample_count;/* Count in last sample */\n        long long samples[STATS_METRIC_SAMPLES];\n        int idx;\n    } inst_metric[STATS_METRIC_COUNT];\n    /* Configuration */\n    int verbosity;                  /* Loglevel in redis.conf */\n    int maxidletime;                /* Client timeout in seconds */\n    int tcpkeepalive;               /* Set SO_KEEPALIVE if non-zero. */\n    int active_expire_enabled;      /* Can be disabled for testing purposes. */\n    int active_expire_effort;       /* From 1 (default) to 10, active effort. */\n    int active_defrag_enabled;\n    int jemalloc_bg_thread;         /* Enable jemalloc background thread */\n    size_t active_defrag_ignore_bytes; /* minimum amount of fragmentation waste to start active defrag */\n    int active_defrag_threshold_lower; /* minimum percentage of fragmentation to start active defrag */\n    int active_defrag_threshold_upper; /* maximum percentage of fragmentation at which we use maximum effort */\n    int active_defrag_cycle_min;       /* minimal effort for defrag in CPU percentage */\n    int active_defrag_cycle_max;       /* maximal effort for defrag in CPU percentage */\n    unsigned long active_defrag_max_scan_fields; /* maximum number of fields of set/hash/zset/list to process from within the main dict scan */\n    _Atomic size_t client_max_querybuf_len; /* Limit for client query buffer length */\n    int dbnum;                      /* Total number of configured DBs */\n    int supervised;                 /* 1 if supervised, 0 otherwise. */\n    int supervised_mode;            /* See SUPERVISED_* */\n    int daemonize;                  /* True if running as a daemon */\n    clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT];\n    /* AOF persistence */\n    int aof_enabled;                /* AOF configuration */\n    int aof_state;                  /* AOF_(ON|OFF|WAIT_REWRITE) */\n    int aof_fsync;                  /* Kind of fsync() policy */\n    char *aof_filename;             /* Name of the AOF file */\n    int aof_no_fsync_on_rewrite;    /* Don't fsync if a rewrite is in prog. */\n    int aof_rewrite_perc;           /* Rewrite AOF if % growth is > M and... */\n    off_t aof_rewrite_min_size;     /* the AOF file is at least N bytes. */\n    off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */\n    off_t aof_current_size;         /* AOF current size. */\n    off_t aof_fsync_offset;         /* AOF offset which is already synced to disk. */\n    int aof_flush_sleep;            /* Micros to sleep before flush. (used by tests) */\n    int aof_rewrite_scheduled;      /* Rewrite once BGSAVE terminates. */\n    pid_t aof_child_pid;            /* PID if rewriting process */\n    list *aof_rewrite_buf_blocks;   /* Hold changes during an AOF rewrite. */\n    sds aof_buf;      /* AOF buffer, written before entering the event loop */\n    int aof_fd;       /* File descriptor of currently selected AOF file */\n    int aof_selected_db; /* Currently selected DB in AOF */\n    time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */\n    time_t aof_last_fsync;            /* UNIX time of last fsync() */\n    time_t aof_rewrite_time_last;   /* Time used by last AOF rewrite run. */\n    time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */\n    int aof_lastbgrewrite_status;   /* C_OK or C_ERR */\n    unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */\n    int aof_rewrite_incremental_fsync;/* fsync incrementally while aof rewriting? */\n    int rdb_save_incremental_fsync;   /* fsync incrementally while rdb saving? */\n    int aof_last_write_status;      /* C_OK or C_ERR */\n    int aof_last_write_errno;       /* Valid if aof_last_write_status is ERR */\n    int aof_load_truncated;         /* Don't stop on unexpected AOF EOF. */\n    int aof_use_rdb_preamble;       /* Use RDB preamble on AOF rewrites. */\n    /* AOF pipes used to communicate between parent and child during rewrite. */\n    int aof_pipe_write_data_to_child;\n    int aof_pipe_read_data_from_parent;\n    int aof_pipe_write_ack_to_parent;\n    int aof_pipe_read_ack_from_child;\n    int aof_pipe_write_ack_to_child;\n    int aof_pipe_read_ack_from_parent;\n    int aof_stop_sending_diff;     /* If true stop sending accumulated diffs\n                                      to child process. */\n    sds aof_child_diff;             /* AOF diff accumulator child side. */\n    /* RDB persistence */\n    long long dirty;                /* Changes to DB from the last save */\n    long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */\n    pid_t rdb_child_pid;            /* PID of RDB saving child */\n    struct saveparam *saveparams;   /* Save points array for RDB */\n    int saveparamslen;              /* Number of saving points */\n    char *rdb_filename;             /* Name of RDB file */\n    int rdb_compression;            /* Use compression in RDB? */\n    int rdb_checksum;               /* Use RDB checksum? */\n    int rdb_del_sync_files;         /* Remove RDB files used only for SYNC if\n                                       the instance does not use persistence. */\n    time_t lastsave;                /* Unix time of last successful save */\n    time_t lastbgsave_try;          /* Unix time of last attempted bgsave */\n    time_t rdb_save_time_last;      /* Time used by last RDB save run. */\n    time_t rdb_save_time_start;     /* Current RDB save start time. */\n    int rdb_bgsave_scheduled;       /* BGSAVE when possible if true. */\n    int rdb_child_type;             /* Type of save by active child. */\n    int lastbgsave_status;          /* C_OK or C_ERR */\n    int stop_writes_on_bgsave_err;  /* Don't allow writes if can't BGSAVE */\n    int rdb_pipe_write;             /* RDB pipes used to transfer the rdb */\n    int rdb_pipe_read;              /* data to the parent process in diskless repl. */\n    connection **rdb_pipe_conns;    /* Connections which are currently the */\n    int rdb_pipe_numconns;          /* target of diskless rdb fork child. */\n    int rdb_pipe_numconns_writing;  /* Number of rdb conns with pending writes. */\n    char *rdb_pipe_buff;            /* In diskless replication, this buffer holds data */\n    int rdb_pipe_bufflen;           /* that was read from the the rdb pipe. */\n    int rdb_key_save_delay;         /* Delay in microseconds between keys while\n                                     * writing the RDB. (for testings) */\n    int key_load_delay;             /* Delay in microseconds between keys while\n                                     * loading aof or rdb. (for testings) */\n    /* Pipe and data structures for child -> parent info sharing. */\n    int child_info_pipe[2];         /* Pipe used to write the child_info_data. */\n    struct {\n        int process_type;           /* AOF or RDB child? */\n        size_t cow_size;            /* Copy on write size. */\n        unsigned long long magic;   /* Magic value to make sure data is valid. */\n    } child_info_data;\n    /* Propagation of commands in AOF / replication */\n    redisOpArray also_propagate;    /* Additional command to propagate. */\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    /* Replication (master) */\n    char replid[CONFIG_RUN_ID_SIZE+1];  /* My current replication ID. */\n    char replid2[CONFIG_RUN_ID_SIZE+1]; /* replid inherited from master*/\n    long long master_repl_offset;   /* My current replication offset */\n    long long master_repl_meaningful_offset; /* Offset minus latest PINGs. */\n    long long second_replid_offset; /* Accept offsets up to this for replid2. */\n    int slaveseldb;                 /* Last SELECTed DB in replication output */\n    int repl_ping_slave_period;     /* Master pings the slave every N seconds */\n    char *repl_backlog;             /* Replication backlog for partial syncs */\n    long long repl_backlog_size;    /* Backlog circular buffer size */\n    long long repl_backlog_histlen; /* Backlog actual data length */\n    long long repl_backlog_idx;     /* Backlog circular buffer current offset,\n                                       that is the next byte will'll write to.*/\n    long long repl_backlog_off;     /* Replication \"master offset\" of first\n                                       byte in the replication backlog buffer.*/\n    time_t repl_backlog_time_limit; /* Time without slaves after the backlog\n                                       gets released. */\n    time_t repl_no_slaves_since;    /* We have no slaves since that time.\n                                       Only valid if server.slaves len is 0. */\n    int repl_min_slaves_to_write;   /* Min number of slaves to write. */\n    int repl_min_slaves_max_lag;    /* Max lag of <count> slaves to write. */\n    int repl_good_slaves_count;     /* Number of slaves with lag <= max_lag. */\n    int repl_diskless_sync;         /* Master send RDB to slaves sockets directly. */\n    int repl_diskless_load;         /* Slave parse RDB directly from the socket.\n                                     * see REPL_DISKLESS_LOAD_* enum */\n    int repl_diskless_sync_delay;   /* Delay to start a diskless repl BGSAVE. */\n    /* Replication (slave) */\n    char *masteruser;               /* AUTH with this user and masterauth with master */\n    char *masterauth;               /* AUTH with this password with master */\n    char *masterhost;               /* Hostname of master */\n    int masterport;                 /* Port of master */\n    int repl_timeout;               /* Timeout after N seconds of master idle */\n    client *master;     /* Client that is master for this slave */\n    client *cached_master; /* Cached master to be reused for PSYNC. */\n    int repl_syncio_timeout; /* Timeout for synchronous I/O calls */\n    int repl_state;          /* Replication status if the instance is a slave */\n    off_t repl_transfer_size; /* Size of RDB to read from master during sync. */\n    off_t repl_transfer_read; /* Amount of RDB read from master during sync. */\n    off_t repl_transfer_last_fsync_off; /* Offset when we fsync-ed last time. */\n    connection *repl_transfer_s;     /* Slave -> Master SYNC connection */\n    int repl_transfer_fd;    /* Slave -> Master SYNC temp file descriptor */\n    char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */\n    time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */\n    int repl_serve_stale_data; /* Serve stale data when link is down? */\n    int repl_slave_ro;          /* Slave is read only? */\n    int repl_slave_ignore_maxmemory;    /* If true slaves do not evict. */\n    time_t repl_down_since; /* Unix time at which link with master went down */\n    int repl_disable_tcp_nodelay;   /* Disable TCP_NODELAY after SYNC? */\n    int slave_priority;             /* Reported in INFO and used by Sentinel. */\n    int slave_announce_port;        /* Give the master this listening port. */\n    char *slave_announce_ip;        /* Give the master this ip address. */\n    /* The following two fields is where we store master PSYNC replid/offset\n     * while the PSYNC is in progress. At the end we'll copy the fields into\n     * the server->master client structure. */\n    char master_replid[CONFIG_RUN_ID_SIZE+1];  /* Master PSYNC runid. */\n    long long master_initial_offset;           /* Master PSYNC offset. */\n    int repl_slave_lazy_flush;          /* Lazy FLUSHALL before loading DB? */\n    /* Replication script cache. */\n    dict *repl_scriptcache_dict;        /* SHA1 all slaves are aware of. */\n    list *repl_scriptcache_fifo;        /* First in, first out LRU eviction. */\n    unsigned int repl_scriptcache_size; /* Max number of elements. */\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    unsigned 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    int lfu_log_factor;             /* LFU logarithmic counter factor. */\n    int lfu_decay_time;             /* LFU counter decay factor. */\n    long long proto_max_bulk_len;   /* Protocol bulk length maximum size. */\n    /* Blocked clients */\n    unsigned int blocked_clients;   /* # of clients executing a blocking cmd.*/\n    unsigned int blocked_clients_by_type[BLOCKED_NUM];\n    list *unblocked_clients; /* list of clients to unblock before next loop */\n    list *ready_keys;        /* List of readyList structures for BLPOP & co */\n    /* Client side caching. */\n    unsigned int tracking_clients;  /* # of clients with tracking enabled.*/\n    size_t tracking_table_max_keys; /* Max number of keys in tracking table. */\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    /* Zip structure config, see redis.conf for more information  */\n    size_t hash_max_ziplist_entries;\n    size_t hash_max_ziplist_value;\n    size_t set_max_intset_entries;\n    size_t zset_max_ziplist_entries;\n    size_t zset_max_ziplist_value;\n    size_t hll_sparse_max_bytes;\n    size_t stream_node_max_bytes;\n    long long stream_node_max_entries;\n    /* List parameters */\n    int list_max_ziplist_size;\n    int list_compress_depth;\n    /* time cache */\n    _Atomic time_t unixtime;    /* Unix time sampled every cron cycle. */\n    time_t timezone;            /* Cached timezone. As set by tzset(). */\n    int daylight_active;        /* Currently in daylight saving time. */\n    mstime_t mstime;            /* 'unixtime' in milliseconds. */\n    ustime_t ustime;            /* 'unixtime' in microseconds. */\n    /* Pubsub */\n    dict *pubsub_channels;  /* Map channels to list of subscribed clients */\n    list *pubsub_patterns;  /* A list of pubsub_patterns */\n    dict *pubsub_patterns_dict;  /* A dict of pubsub_patterns */\n    int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an\n                                   xor of NOTIFY_... flags. */\n    /* Cluster */\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    int cluster_migration_barrier; /* Cluster replicas migration barrier. */\n    int cluster_slave_validity_factor; /* Slave max data age for failover. */\n    int cluster_require_full_coverage; /* If true, put the cluster down if\n                                          there is at least an uncovered slot.*/\n    int cluster_slave_no_failover;  /* Prevent slave from starting a failover\n                                       if the master is in failure state. */\n    char *cluster_announce_ip;  /* IP address to announce on cluster bus. */\n    int cluster_announce_port;     /* base port to announce on cluster bus. */\n    int cluster_announce_bus_port; /* bus port to announce on cluster bus. */\n    int cluster_module_flags;      /* Set of flags that Redis modules are able\n                                      to set in order to suppress certain\n                                      native Redis Cluster features. Check the\n                                      REDISMODULE_CLUSTER_FLAG_*. */\n    int cluster_allow_reads_when_down; /* Are reads allowed when the cluster\n                                        is down? */\n    /* Scripting */\n    lua_State *lua; /* The Lua interpreter. We use just one for all clients */\n    client *lua_client;   /* The \"fake client\" to query Redis from Lua */\n    client *lua_caller;   /* The client running EVAL right now, or NULL */\n    char* lua_cur_script; /* SHA1 of the script currently running, or NULL */\n    dict *lua_scripts;         /* A dictionary of SHA1 -> Lua scripts */\n    unsigned long long lua_scripts_mem;  /* Cached scripts' memory + oh */\n    mstime_t lua_time_limit;  /* Script timeout in milliseconds */\n    mstime_t lua_time_start;  /* Start time of script, milliseconds time */\n    int lua_write_dirty;  /* True if a write command was called during the\n                             execution of the current script. */\n    int lua_random_dirty; /* True if a random command was called during the\n                             execution of the current script. */\n    int lua_replicate_commands; /* True if we are doing single commands repl. */\n    int lua_multi_emitted;/* True if we already proagated MULTI. */\n    int lua_repl;         /* Script replication flags for redis.set_repl(). */\n    int lua_timedout;     /* True if we reached the time limit for script\n                             execution. */\n    int lua_kill;         /* Kill the script if true. */\n    int lua_always_replicate_commands; /* Default replication type. */\n    int lua_oom;          /* OOM detected when script start? */\n    /* Lazy free */\n    int lazyfree_lazy_eviction;\n    int lazyfree_lazy_expire;\n    int lazyfree_lazy_server_del;\n    int lazyfree_lazy_user_del;\n    /* Latency monitor */\n    long long latency_monitor_threshold;\n    dict *latency_events;\n    /* ACLs */\n    char *acl_filename;     /* ACL Users file. NULL if not configured. */\n    unsigned long acllog_max_len; /* Maximum length of the ACL LOG list. */\n    sds requirepass;        /* Remember the cleartext password set with the\n                               old \"requirepass\" directive for backward\n                               compatibility with Redis <= 5. */\n    /* Assert & bug reporting */\n    const char *assert_failed;\n    const 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    /* System hardware info */\n    size_t system_memory_size;  /* Total memory in system as reported by OS */\n    /* TLS Configuration */\n    int tls_cluster;\n    int tls_replication;\n    int tls_auth_clients;\n    redisTLSContextConfig tls_ctx_config;\n    /* cpu affinity */\n    char *server_cpulist; /* cpu affinity list of redis server main/io thread. */\n    char *bio_cpulist; /* cpu affinity list of bio thread. */\n    char *aof_rewrite_cpulist; /* cpu affinity list of aof rewrite process. */\n    char *bgsave_cpulist; /* cpu affinity list of bgsave process. */\n};\n\ntypedef struct pubsubPattern {\n    client *client;\n    robj *pattern;\n} pubsubPattern;\n\ntypedef void redisCommandProc(client *c);\ntypedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nstruct redisCommand {\n    char *name;\n    redisCommandProc *proc;\n    int arity;\n    char *sflags;   /* Flags as string representation, one char per flag. */\n    uint64_t flags; /* The actual flags, obtained from the 'sflags' field. */\n    /* Use a function to determine keys arguments in a command line.\n     * Used for Redis Cluster redirect. */\n    redisGetKeysProc *getkeys_proc;\n    /* What keys should be loaded in background when calling this command? */\n    int firstkey; /* The first argument that's a key (0 = no keys) */\n    int lastkey;  /* The last argument that's a key */\n    int keystep;  /* The step between first and last key */\n    long long microseconds, calls;\n    int id;     /* Command ID. This is a progressive ID starting from 0 that\n                   is assigned at runtime, and is used in order to check\n                   ACLs. A connection is able to execute a given command if\n                   the user associated to the connection has this command\n                   bit set in the bitmap of allowed commands. */\n};\n\nstruct redisFunctionSym {\n    char *name;\n    unsigned long pointer;\n};\n\ntypedef struct _redisSortObject {\n    robj *obj;\n    union {\n        double score;\n        robj *cmpobj;\n    } u;\n} redisSortObject;\n\ntypedef struct _redisSortOperation {\n    int type;\n    robj *pattern;\n} redisSortOperation;\n\n/* Structure to hold list iteration abstraction. */\ntypedef struct {\n    robj *subject;\n    unsigned char encoding;\n    unsigned char direction; /* Iteration direction */\n    quicklistIter *iter;\n} listTypeIterator;\n\n/* Structure for an entry while iterating over a list. */\ntypedef struct {\n    listTypeIterator *li;\n    quicklistEntry entry; /* Entry in quicklist */\n} listTypeEntry;\n\n/* Structure to hold set iteration abstraction. */\ntypedef struct {\n    robj *subject;\n    int encoding;\n    int ii; /* intset iterator */\n    dictIterator *di;\n} setTypeIterator;\n\n/* Structure to hold hash iteration abstraction. Note that iteration over\n * hashes involves both fields and values. Because it is possible that\n * not both are required, store pointers in the iterator to avoid\n * unnecessary memory allocation for fields/values. */\ntypedef struct {\n    robj *subject;\n    int encoding;\n\n    unsigned char *fptr, *vptr;\n\n    dictIterator *di;\n    dictEntry *de;\n} hashTypeIterator;\n\n#include \"stream.h\"  /* Stream data type header file. */\n\n#define OBJ_HASH_KEY 1\n#define OBJ_HASH_VALUE 2\n\n/*-----------------------------------------------------------------------------\n * Extern declarations\n *----------------------------------------------------------------------------*/\n\nextern struct redisServer server;\nextern struct sharedObjectsStruct shared;\nextern dictType objectKeyPointerValueDictType;\nextern dictType objectKeyHeapPointerValueDictType;\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;\nextern dictType keyptrDictType;\nextern dictType modulesDictType;\n\n/*-----------------------------------------------------------------------------\n * Functions prototypes\n *----------------------------------------------------------------------------*/\n\n/* Modules */\nvoid moduleInitModulesSystem(void);\nint moduleLoad(const char *path, void **argv, int argc);\nvoid moduleLoadFromQueue(void);\nint *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nmoduleType *moduleTypeLookupModuleByID(uint64_t id);\nvoid moduleTypeNameByID(char *name, uint64_t moduleid);\nvoid moduleFreeContext(struct RedisModuleCtx *ctx);\nvoid unblockClientFromModule(client *c);\nvoid moduleHandleBlockedClients(void);\nvoid moduleBlockedClientTimedOut(client *c);\nvoid moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask);\nsize_t moduleCount(void);\nvoid moduleAcquireGIL(void);\nvoid moduleReleaseGIL(void);\nvoid moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);\nvoid moduleCallCommandFilters(client *c);\nvoid ModuleForkDoneHandler(int exitcode, int bysignal);\nint TerminateModuleForkChild(int child_pid, int wait);\nssize_t rdbSaveModulesAux(rio *rdb, int when);\nint moduleAllDatatypesHandleErrors();\nsds modulesCollectInfo(sds info, const char *section, int for_crash_report, int sections);\nvoid moduleFireServerEvent(uint64_t eid, int subid, void *data);\nvoid processModuleLoadingProgressEvent(int is_aof);\nint moduleTryServeClientBlockedOnKey(client *c, robj *key);\nvoid moduleUnblockClient(client *c);\nint moduleClientIsBlockedOnKeys(client *c);\nvoid moduleNotifyUserChanged(client *c);\n\n/* Utils */\nlong long ustime(void);\nlong long mstime(void);\nvoid getRandomHexChars(char *p, size_t len);\nvoid getRandomBytes(unsigned char *p, size_t 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);\nint redisCommunicateSystemd(const char *sd_notify_msg);\nvoid redisSetCpuAffinity(const char *cpulist);\n\n/* networking.c -- Networking and Client related operations */\nclient *createClient(connection *conn);\nvoid closeTimedoutClients(void);\nvoid freeClient(client *c);\nvoid freeClientAsync(client *c);\nvoid resetClient(client *c);\nvoid sendReplyToClient(connection *conn);\nvoid *addReplyDeferredLen(client *c);\nvoid setDeferredArrayLen(client *c, void *node, long length);\nvoid setDeferredMapLen(client *c, void *node, long length);\nvoid setDeferredSetLen(client *c, void *node, long length);\nvoid setDeferredAttributeLen(client *c, void *node, long length);\nvoid setDeferredPushLen(client *c, void *node, long length);\nvoid processInputBuffer(client *c);\nvoid processGopherRequest(client *c);\nvoid acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid readQueryFromClient(connection *conn);\nvoid addReplyNull(client *c);\nvoid addReplyNullArray(client *c);\nvoid addReplyBool(client *c, int b);\nvoid addReplyVerbatim(client *c, const char *s, size_t len, const char *ext);\nvoid addReplyProto(client *c, const char *s, size_t len);\nvoid AddReplyFromClient(client *c, client *src);\nvoid addReplyBulk(client *c, robj *obj);\nvoid addReplyBulkCString(client *c, const char *s);\nvoid addReplyBulkCBuffer(client *c, const void *p, size_t len);\nvoid addReplyBulkLongLong(client *c, long long ll);\nvoid addReply(client *c, robj *obj);\nvoid addReplySds(client *c, sds s);\nvoid addReplyBulkSds(client *c, sds s);\nvoid addReplyError(client *c, const char *err);\nvoid addReplyStatus(client *c, const char *status);\nvoid addReplyDouble(client *c, double d);\nvoid addReplyHumanLongDouble(client *c, long double d);\nvoid addReplyLongLong(client *c, long long ll);\nvoid addReplyArrayLen(client *c, long length);\nvoid addReplyMapLen(client *c, long length);\nvoid addReplySetLen(client *c, long length);\nvoid addReplyAttributeLen(client *c, long length);\nvoid addReplyPushLen(client *c, long length);\nvoid addReplyHelp(client *c, const char **help);\nvoid addReplySubcommandSyntaxError(client *c);\nvoid addReplyLoadedModules(client *c);\nvoid copyClientOutputBuffer(client *dst, client *src);\nsize_t sdsZmallocSize(sds s);\nsize_t getStringObjectSdsUsedMemory(robj *o);\nvoid freeClientReplyValue(void *o);\nvoid *dupClientReplyValue(void *o);\nvoid getClientsMaxBuffers(unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer);\nchar *getClientPeerId(client *client);\nsds catClientInfoString(sds s, client *client);\nsds getAllClientsInfoString(int type);\nvoid rewriteClientCommandVector(client *c, int argc, ...);\nvoid rewriteClientCommandArgument(client *c, int i, robj *newval);\nvoid replaceClientCommandVector(client *c, int argc, robj **argv);\nunsigned long getClientOutputBufferMemoryUsage(client *c);\nint freeClientsInAsyncFreeQueue(void);\nvoid asyncCloseClientOnOutputBufferLimitReached(client *c);\nint getClientType(client *c);\nint getClientTypeByName(char *name);\nchar *getClientTypeName(int class);\nvoid flushSlavesOutputBuffers(void);\nvoid disconnectSlaves(void);\nint listenToPort(int port, int *fds, int *count);\nvoid pauseClients(mstime_t duration);\nint clientsArePaused(void);\nvoid processEventsWhileBlocked(void);\nint handleClientsWithPendingWrites(void);\nint handleClientsWithPendingWritesUsingThreads(void);\nint handleClientsWithPendingReadsUsingThreads(void);\nint stopThreadedIOIfNeeded(void);\nint clientHasPendingReplies(client *c);\nvoid unlinkClient(client *c);\nint writeToClient(client *c, int handler_installed);\nvoid linkClient(client *c);\nvoid protectClient(client *c);\nvoid unprotectClient(client *c);\nvoid initThreadedIO(void);\nclient *lookupClientByID(uint64_t id);\n\n#ifdef __GNUC__\nvoid addReplyErrorFormat(client *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\nvoid addReplyStatusFormat(client *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid addReplyErrorFormat(client *c, const char *fmt, ...);\nvoid addReplyStatusFormat(client *c, const char *fmt, ...);\n#endif\n\n/* Client side caching (tracking mode) */\nvoid enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **prefix, size_t numprefix);\nvoid disableTracking(client *c);\nvoid trackingRememberKeys(client *c);\nvoid trackingInvalidateKey(client *c, robj *keyobj);\nvoid trackingInvalidateKeysOnFlush(int dbid);\nvoid trackingLimitUsedSlots(void);\nuint64_t trackingGetTotalItems(void);\nuint64_t trackingGetTotalKeys(void);\nuint64_t trackingGetTotalPrefixes(void);\nvoid trackingBroadcastInvalidationMessages(void);\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(const robj *subject);\nlistTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction);\nvoid listTypeReleaseIterator(listTypeIterator *li);\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry);\nrobj *listTypeGet(listTypeEntry *entry);\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where);\nint listTypeEqual(listTypeEntry *entry, robj *o);\nvoid listTypeDelete(listTypeIterator *iter, listTypeEntry *entry);\nvoid listTypeConvert(robj *subject, int enc);\nvoid unblockClientWaitingData(client *c);\nvoid popGenericCommand(client *c, int where);\n\n/* MULTI/EXEC/WATCH... */\nvoid unwatchAllKeys(client *c);\nvoid initClientMultiState(client *c);\nvoid freeClientMultiState(client *c);\nvoid queueMultiCommand(client *c);\nvoid touchWatchedKey(redisDb *db, robj *key);\nvoid touchWatchedKeysOnFlush(int dbid);\nvoid discardTransaction(client *c);\nvoid flagTransaction(client *c);\nvoid execCommandPropagateMulti(client *c);\nvoid execCommandPropagateExec(client *c);\n\n/* Redis object implementation */\nvoid decrRefCount(robj *o);\nvoid decrRefCountVoid(void *o);\nvoid incrRefCount(robj *o);\nrobj *makeObjectShared(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(const char *ptr, size_t len);\nrobj *createRawStringObject(const char *ptr, size_t len);\nrobj *createEmbeddedStringObject(const char *ptr, size_t len);\nrobj *dupStringObject(const robj *o);\nint isSdsRepresentableAsLongLong(sds s, long long *llval);\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 *createStringObjectFromLongLongForValue(long long value);\nrobj *createStringObjectFromLongDouble(long double value, int humanfriendly);\nrobj *createQuicklistObject(void);\nrobj *createZiplistObject(void);\nrobj *createSetObject(void);\nrobj *createIntsetObject(void);\nrobj *createHashObject(void);\nrobj *createZsetObject(void);\nrobj *createZsetZiplistObject(void);\nrobj *createStreamObject(void);\nrobj *createModuleObject(moduleType *mt, void *value);\nint getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg);\nint checkType(client *c, robj *o, int type);\nint getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg);\nint getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg);\nint getDoubleFromObject(const robj *o, double *target);\nint getLongLongFromObject(robj *o, long long *target);\nint getLongDoubleFromObject(robj *o, long double *target);\nint getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg);\nchar *strEncoding(int encoding);\nint compareStringObjects(robj *a, robj *b);\nint collateStringObjects(robj *a, robj *b);\nint equalStringObjects(robj *a, robj *b);\nunsigned long long estimateObjectIdleTime(robj *o);\nvoid trimStringObjectIfNeeded(robj *o);\n#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_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 replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen);\nvoid replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc);\nvoid updateSlavesWaitingBgsave(int bgsaveerr, int type);\nvoid replicationCron(void);\nvoid replicationHandleMasterDisconnection(void);\nvoid replicationCacheMaster(client *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(client *c);\nint replicationCountAcksByOffset(long long offset);\nvoid replicationSendNewlineToMaster(void);\nlong long replicationGetSlaveOffset(void);\nchar *replicationGetSlaveName(client *c);\nlong long getPsyncInitialOffset(void);\nint replicationSetupSlaveForFullResync(client *slave, long long offset);\nvoid changeReplicationId(void);\nvoid clearReplicationId2(void);\nvoid chopReplicationBacklog(void);\nvoid replicationCacheMasterUsingMyself(void);\nvoid feedReplicationBacklog(void *ptr, size_t len);\nvoid rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);\nvoid rdbPipeWriteHandlerConnRemoved(struct connection *conn);\n\n/* Generic persistence functions */\nvoid startLoadingFile(FILE* fp, char* filename, int rdbflags);\nvoid startLoading(size_t size, int rdbflags);\nvoid loadingProgress(off_t pos);\nvoid stopLoading(int success);\nvoid startSaving(int rdbflags);\nvoid stopSaving(int success);\nint allPersistenceDisabled(void);\n\n#define DISK_ERROR_TYPE_AOF 1       /* Don't accept writes: AOF errors. */\n#define DISK_ERROR_TYPE_RDB 2       /* Don't accept writes: RDB errors. */\n#define DISK_ERROR_TYPE_NONE 0      /* No problems, we can accept writes. */\nint writeCommandsDeniedByDiskError(void);\n\n/* RDB persistence */\n#include \"rdb.h\"\nvoid killRDBChild(void);\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);\nssize_t aofReadDiffFromParent(void);\nvoid killAppendOnlyChild(void);\nvoid restartAOFAfterSYNC();\n\n/* Child info */\nvoid openChildInfoPipe(void);\nvoid closeChildInfoPipe(void);\nvoid sendChildInfo(int process_type);\nvoid receiveChildInfo(void);\n\n/* Fork helpers */\nint redisFork();\nint hasActiveChildProcess();\nvoid sendChildCOWInfo(int ptype, char *pname);\n\n/* acl.c -- Authentication related prototypes. */\nextern rax *Users;\nextern user *DefaultUser;\nvoid ACLInit(void);\n/* Return values for ACLCheckUserCredentials(). */\n#define ACL_OK 0\n#define ACL_DENIED_CMD 1\n#define ACL_DENIED_KEY 2\n#define ACL_DENIED_AUTH 3 /* Only used for ACL LOG entries. */\nint ACLCheckUserCredentials(robj *username, robj *password);\nint ACLAuthenticateUser(client *c, robj *username, robj *password);\nunsigned long ACLGetCommandID(const char *cmdname);\nuser *ACLGetUserByName(const char *name, size_t namelen);\nint ACLCheckCommandPerm(client *c, int *keyidxptr);\nint ACLSetUser(user *u, const char *op, ssize_t oplen);\nsds ACLDefaultUserFirstPassword(void);\nuint64_t ACLGetCommandCategoryFlagByName(const char *name);\nint ACLAppendUserForLoading(sds *argv, int argc, int *argc_err);\nchar *ACLSetUserStringError(void);\nint ACLLoadConfiguredUsers(void);\nsds ACLDescribeUser(user *u);\nvoid ACLLoadUsersAtStartup(void);\nvoid addReplyCommandCategories(client *c, struct redisCommand *cmd);\nuser *ACLCreateUnlinkedUser();\nvoid ACLFreeUserAndKillClients(user *u);\nvoid addACLLogEntry(client *c, int reason, int keypos, sds username);\n\n/* Sorted sets data type */\n\n/* Input flags. */\n#define ZADD_NONE 0\n#define ZADD_INCR (1<<0)    /* Increment the score instead of setting it. */\n#define ZADD_NX (1<<1)      /* Don't touch elements not already existing. */\n#define ZADD_XX (1<<2)      /* Only touch elements already existing. */\n\n/* Output flags. */\n#define ZADD_NOP (1<<3)     /* Operation not performed because of conditionals.*/\n#define ZADD_NAN (1<<4)     /* Only touch elements already existing. */\n#define ZADD_ADDED (1<<5)   /* The element was new and was added. */\n#define ZADD_UPDATED (1<<6) /* The element already existed, score updated. */\n\n/* Flags only used by the ZADD command but not by zsetAdd() API: */\n#define ZADD_CH (1<<16)      /* Return num of elements added or updated. */\n\n/* Struct to hold a inclusive/exclusive range spec by score comparison. */\ntypedef struct {\n    double min, max;\n    int minex, maxex; /* are min or max exclusive? */\n} zrangespec;\n\n/* Struct to hold an inclusive/exclusive range spec by lexicographic comparison. */\ntypedef struct {\n    sds 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, sds ele);\nunsigned char *zzlInsert(unsigned char *zl, sds ele, double score);\nint zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node);\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 char *zzlFirstInRange(unsigned char *zl, zrangespec *range);\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range);\nunsigned long zsetLength(const robj *zobj);\nvoid zsetConvert(robj *zobj, int encoding);\nvoid zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen);\nint zsetScore(robj *zobj, sds member, double *score);\nunsigned long zslGetRank(zskiplist *zsl, double score, sds o);\nint zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore);\nlong zsetRank(robj *zobj, sds ele, int reverse);\nint zsetDel(robj *zobj, sds ele);\nvoid genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey, robj *countarg);\nsds ziplistGetObject(unsigned char *sptr);\nint zslValueGteMin(double value, zrangespec *spec);\nint zslValueLteMax(double value, zrangespec *spec);\nvoid zslFreeLexRange(zlexrangespec *spec);\nint zslParseLexRange(robj *min, robj *max, zlexrangespec *spec);\nunsigned char *zzlFirstInLexRange(unsigned char *zl, zlexrangespec *range);\nunsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range);\nzskiplistNode *zslFirstInLexRange(zskiplist *zsl, zlexrangespec *range);\nzskiplistNode *zslLastInLexRange(zskiplist *zsl, zlexrangespec *range);\nint zzlLexValueGteMin(unsigned char *p, zlexrangespec *spec);\nint zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec);\nint zslLexValueGteMin(sds value, zlexrangespec *spec);\nint zslLexValueLteMax(sds value, zlexrangespec *spec);\n\n/* Core functions */\nint getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level);\nsize_t freeMemoryGetNotCountedMemory();\nint freeMemoryIfNeeded(void);\nint freeMemoryIfNeededAndSafe(void);\nint processCommand(client *c);\nvoid setupSignalHandlers(void);\nstruct redisCommand *lookupCommand(sds name);\nstruct redisCommand *lookupCommandByCString(char *s);\nstruct redisCommand *lookupCommandOrOriginal(sds name);\nvoid call(client *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 redisOpArrayInit(redisOpArray *oa);\nvoid redisOpArrayFree(redisOpArray *oa);\nvoid forceCommandPropagation(client *c, int flags);\nvoid preventCommandPropagation(client *c);\nvoid preventCommandAOF(client *c);\nvoid preventCommandReplication(client *c);\nint prepareForShutdown();\n#ifdef __GNUC__\nvoid serverLog(int level, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid serverLog(int level, const char *fmt, ...);\n#endif\nvoid serverLogRaw(int level, const char *msg);\nvoid serverLogFromHandler(int level, const char *msg);\nvoid usage(void);\nvoid updateDictResizePolicy(void);\nint htNeedsResize(dict *dict);\nvoid populateCommandTable(void);\nvoid resetCommandTableStats(void);\nvoid adjustOpenFilesLimit(void);\nvoid closeListeningSockets(int unlink_unix_socket);\nvoid updateCachedTime(int update_daylight_info);\nvoid resetServerStats(void);\nvoid activeDefragCycle(void);\nunsigned int getLRUClock(void);\nunsigned int LRU_CLOCK(void);\nconst char *evictPolicyToString(void);\nstruct redisMemOverhead *getMemoryOverheadData(void);\nvoid freeMemoryOverheadData(struct redisMemOverhead *mh);\nvoid checkChildrenDone(void);\n\n#define RESTART_SERVER_NONE 0\n#define RESTART_SERVER_GRACEFULLY (1<<0)     /* Do proper shutdown. */\n#define RESTART_SERVER_CONFIG_REWRITE (1<<1) /* CONFIG REWRITE before restart.*/\nint restartServer(int flags, mstime_t delay);\n\n/* Set data type */\nrobj *setTypeCreate(sds value);\nint setTypeAdd(robj *subject, sds value);\nint setTypeRemove(robj *subject, sds value);\nint setTypeIsMember(robj *subject, sds value);\nsetTypeIterator *setTypeInitIterator(robj *subject);\nvoid setTypeReleaseIterator(setTypeIterator *si);\nint setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele);\nsds setTypeNextObject(setTypeIterator *si);\nint setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele);\nunsigned long setTypeRandomElements(robj *set, unsigned long count, robj *aux_set);\nunsigned long setTypeSize(const robj *subject);\nvoid setTypeConvert(robj *subject, int enc);\n\n/* Hash data type */\n#define HASH_SET_TAKE_FIELD (1<<0)\n#define HASH_SET_TAKE_VALUE (1<<1)\n#define HASH_SET_COPY 0\n\nvoid hashTypeConvert(robj *o, int enc);\nvoid hashTypeTryConversion(robj *subject, robj **argv, int start, int end);\nint hashTypeExists(robj *o, sds key);\nint hashTypeDelete(robj *o, sds key);\nunsigned long hashTypeLength(const 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);\nsds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what);\nvoid hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll);\nsds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what);\nrobj *hashTypeLookupWriteOrCreate(client *c, robj *key);\nrobj *hashTypeGetValueObject(robj *o, sds field);\nint hashTypeSet(robj *o, sds field, sds value, int flags);\n\n/* Pub / Sub */\nint pubsubUnsubscribeAllChannels(client *c, int notify);\nint pubsubUnsubscribeAllPatterns(client *c, int notify);\nvoid freePubsubPattern(void *p);\nint listMatchPubsubPattern(void *a, void *b);\nint pubsubPublishMessage(robj *channel, robj *message);\nvoid addReplyPubsubMessage(client *c, robj *channel, robj *msg);\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(void);\nstruct rewriteConfigState; /* Forward declaration to export API. */\nvoid rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force);\nint rewriteConfig(char *path);\nvoid initConfigValues();\n\n/* db.c -- Keyspace access API */\nint removeExpire(redisDb *db, robj *key);\nvoid propagateExpire(redisDb *db, robj *key, int lazy);\nint expireIfNeeded(redisDb *db, robj *key);\nlong long getExpire(redisDb *db, robj *key);\nvoid setExpire(client *c, redisDb *db, robj *key, long long when);\nrobj *lookupKey(redisDb *db, robj *key, int flags);\nrobj *lookupKeyRead(redisDb *db, robj *key);\nrobj *lookupKeyWrite(redisDb *db, robj *key);\nrobj *lookupKeyReadOrReply(client *c, robj *key, robj *reply);\nrobj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);\nrobj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);\nrobj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags);\nrobj *objectCommandLookup(client *c, robj *key);\nrobj *objectCommandLookupOrReply(client *c, robj *key, robj *reply);\nint objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,\n                       long long lru_clock, int lru_multiplier);\n#define LOOKUP_NONE 0\n#define LOOKUP_NOTOUCH (1<<0)\nvoid dbAdd(redisDb *db, robj *key, robj *val);\nint dbAddRDBLoad(redisDb *db, sds key, robj *val);\nvoid dbOverwrite(redisDb *db, robj *key, robj *val);\nvoid genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal);\nvoid setKey(client *c, redisDb *db, robj *key, robj *val);\nint dbExists(redisDb *db, robj *key);\nrobj *dbRandomKey(redisDb *db);\nint dbSyncDelete(redisDb *db, robj *key);\nint dbDelete(redisDb *db, robj *key);\nrobj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);\n\n#define EMPTYDB_NO_FLAGS 0      /* No flags. */\n#define EMPTYDB_ASYNC (1<<0)    /* Reclaim memory in another thread. */\n#define EMPTYDB_BACKUP (1<<2)   /* DB array is a backup for REPL_DISKLESS_LOAD_SWAPDB. */\nlong long emptyDb(int dbnum, int flags, void(callback)(void*));\nlong long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*));\nvoid flushAllDataAndResetRDB(int flags);\nlong long dbTotalServerKeyCount();\n\nint selectDb(client *c, int id);\nvoid signalModifiedKey(client *c, 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(client *c, robj *o, unsigned long cursor);\nint parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor);\nvoid slotToKeyAdd(sds key);\nvoid slotToKeyDel(sds key);\nvoid slotToKeyFlush(void);\nint dbAsyncDelete(redisDb *db, robj *key);\nvoid emptyDbAsync(redisDb *db);\nvoid slotToKeyFlushAsync(void);\nsize_t lazyfreeGetPendingObjectsCount(void);\nvoid freeObjAsync(robj *o);\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);\nint *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *lcsGetKeys(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);\nint clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, unsigned char *payload, uint32_t len);\n\n/* Sentinel */\nvoid initSentinelConfig(void);\nvoid initSentinel(void);\nvoid sentinelTimer(void);\nchar *sentinelHandleConfiguration(char **argv, int argc);\nvoid sentinelIsRunning(void);\n\n/* redis-check-rdb & aof */\nint redis_check_rdb(char *rdbfilename, FILE *fp);\nint redis_check_rdb_main(int argc, char **argv, FILE *fp);\nint redis_check_aof_main(int argc, char **argv);\n\n/* Scripting */\nvoid scriptingInit(int setup);\nint ldbRemoveChild(pid_t pid);\nvoid ldbKillForkedSessions(void);\nint ldbPendingChildren(void);\nsds luaCreateFunction(client *c, lua_State *lua, robj *body);\n\n/* Blocked clients */\nvoid processUnblockedClients(void);\nvoid blockClient(client *c, int btype);\nvoid unblockClient(client *c);\nvoid queueClientForReprocessing(client *c);\nvoid replyToBlockedClientTimedOut(client *c);\nint getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit);\nvoid disconnectAllBlockedClients(void);\nvoid handleClientsBlockedOnKeys(void);\nvoid signalKeyAsReady(redisDb *db, robj *key);\nvoid blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids);\n\n/* timeout.c -- Blocked clients timeout and connections timeout. */\nvoid addClientToTimeoutTable(client *c);\nvoid removeClientFromTimeoutTable(client *c);\nvoid handleBlockedClientsTimeout(void);\nint clientsCronHandleTimeout(client *c, mstime_t now_ms);\n\n/* expire.c -- Handling of expired keys */\nvoid activeExpireCycle(int type);\nvoid expireSlaveKeys(void);\nvoid rememberSlaveKeyWithExpire(redisDb *db, robj *key);\nvoid flushSlaveKeysWithExpireList(void);\nsize_t getSlaveKeyWithExpireCount(void);\n\n/* evict.c -- maxmemory handling and LRU eviction. */\nvoid evictionPoolAlloc(void);\n#define LFU_INIT_VAL 5\nunsigned long LFUGetTimeInMinutes(void);\nuint8_t LFULogIncr(uint8_t value);\nunsigned long LFUDecrAndReturn(robj *o);\n\n/* Keys hashing / comparison functions for dict.c hash tables. */\nuint64_t dictSdsHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\nvoid dictSdsDestructor(void *privdata, void *val);\n\n/* Git SHA1 */\nchar *redisGitSHA1(void);\nchar *redisGitDirty(void);\nuint64_t redisBuildId(void);\nchar *redisBuildIdString(void);\n\n/* Commands prototypes */\nvoid authCommand(client *c);\nvoid pingCommand(client *c);\nvoid echoCommand(client *c);\nvoid commandCommand(client *c);\nvoid setCommand(client *c);\nvoid setnxCommand(client *c);\nvoid setexCommand(client *c);\nvoid psetexCommand(client *c);\nvoid getCommand(client *c);\nvoid delCommand(client *c);\nvoid unlinkCommand(client *c);\nvoid existsCommand(client *c);\nvoid setbitCommand(client *c);\nvoid getbitCommand(client *c);\nvoid bitfieldCommand(client *c);\nvoid bitfieldroCommand(client *c);\nvoid setrangeCommand(client *c);\nvoid getrangeCommand(client *c);\nvoid incrCommand(client *c);\nvoid decrCommand(client *c);\nvoid incrbyCommand(client *c);\nvoid decrbyCommand(client *c);\nvoid incrbyfloatCommand(client *c);\nvoid selectCommand(client *c);\nvoid swapdbCommand(client *c);\nvoid randomkeyCommand(client *c);\nvoid keysCommand(client *c);\nvoid scanCommand(client *c);\nvoid dbsizeCommand(client *c);\nvoid lastsaveCommand(client *c);\nvoid saveCommand(client *c);\nvoid bgsaveCommand(client *c);\nvoid bgrewriteaofCommand(client *c);\nvoid shutdownCommand(client *c);\nvoid moveCommand(client *c);\nvoid renameCommand(client *c);\nvoid renamenxCommand(client *c);\nvoid lpushCommand(client *c);\nvoid rpushCommand(client *c);\nvoid lpushxCommand(client *c);\nvoid rpushxCommand(client *c);\nvoid linsertCommand(client *c);\nvoid lpopCommand(client *c);\nvoid rpopCommand(client *c);\nvoid llenCommand(client *c);\nvoid lindexCommand(client *c);\nvoid lrangeCommand(client *c);\nvoid ltrimCommand(client *c);\nvoid typeCommand(client *c);\nvoid lsetCommand(client *c);\nvoid saddCommand(client *c);\nvoid sremCommand(client *c);\nvoid smoveCommand(client *c);\nvoid sismemberCommand(client *c);\nvoid scardCommand(client *c);\nvoid spopCommand(client *c);\nvoid srandmemberCommand(client *c);\nvoid sinterCommand(client *c);\nvoid sinterstoreCommand(client *c);\nvoid sunionCommand(client *c);\nvoid sunionstoreCommand(client *c);\nvoid sdiffCommand(client *c);\nvoid sdiffstoreCommand(client *c);\nvoid sscanCommand(client *c);\nvoid syncCommand(client *c);\nvoid flushdbCommand(client *c);\nvoid flushallCommand(client *c);\nvoid sortCommand(client *c);\nvoid lremCommand(client *c);\nvoid rpoplpushCommand(client *c);\nvoid infoCommand(client *c);\nvoid mgetCommand(client *c);\nvoid monitorCommand(client *c);\nvoid expireCommand(client *c);\nvoid expireatCommand(client *c);\nvoid pexpireCommand(client *c);\nvoid pexpireatCommand(client *c);\nvoid getsetCommand(client *c);\nvoid ttlCommand(client *c);\nvoid touchCommand(client *c);\nvoid pttlCommand(client *c);\nvoid persistCommand(client *c);\nvoid replicaofCommand(client *c);\nvoid roleCommand(client *c);\nvoid debugCommand(client *c);\nvoid msetCommand(client *c);\nvoid msetnxCommand(client *c);\nvoid zaddCommand(client *c);\nvoid zincrbyCommand(client *c);\nvoid zrangeCommand(client *c);\nvoid zrangebyscoreCommand(client *c);\nvoid zrevrangebyscoreCommand(client *c);\nvoid zrangebylexCommand(client *c);\nvoid zrevrangebylexCommand(client *c);\nvoid zcountCommand(client *c);\nvoid zlexcountCommand(client *c);\nvoid zrevrangeCommand(client *c);\nvoid zcardCommand(client *c);\nvoid zremCommand(client *c);\nvoid zscoreCommand(client *c);\nvoid zremrangebyscoreCommand(client *c);\nvoid zremrangebylexCommand(client *c);\nvoid zpopminCommand(client *c);\nvoid zpopmaxCommand(client *c);\nvoid bzpopminCommand(client *c);\nvoid bzpopmaxCommand(client *c);\nvoid multiCommand(client *c);\nvoid execCommand(client *c);\nvoid discardCommand(client *c);\nvoid blpopCommand(client *c);\nvoid brpopCommand(client *c);\nvoid brpoplpushCommand(client *c);\nvoid appendCommand(client *c);\nvoid strlenCommand(client *c);\nvoid zrankCommand(client *c);\nvoid zrevrankCommand(client *c);\nvoid hsetCommand(client *c);\nvoid hsetnxCommand(client *c);\nvoid hgetCommand(client *c);\nvoid hmsetCommand(client *c);\nvoid hmgetCommand(client *c);\nvoid hdelCommand(client *c);\nvoid hlenCommand(client *c);\nvoid hstrlenCommand(client *c);\nvoid zremrangebyrankCommand(client *c);\nvoid zunionstoreCommand(client *c);\nvoid zinterstoreCommand(client *c);\nvoid zscanCommand(client *c);\nvoid hkeysCommand(client *c);\nvoid hvalsCommand(client *c);\nvoid hgetallCommand(client *c);\nvoid hexistsCommand(client *c);\nvoid hscanCommand(client *c);\nvoid configCommand(client *c);\nvoid hincrbyCommand(client *c);\nvoid hincrbyfloatCommand(client *c);\nvoid subscribeCommand(client *c);\nvoid unsubscribeCommand(client *c);\nvoid psubscribeCommand(client *c);\nvoid punsubscribeCommand(client *c);\nvoid publishCommand(client *c);\nvoid pubsubCommand(client *c);\nvoid watchCommand(client *c);\nvoid unwatchCommand(client *c);\nvoid clusterCommand(client *c);\nvoid restoreCommand(client *c);\nvoid migrateCommand(client *c);\nvoid askingCommand(client *c);\nvoid readonlyCommand(client *c);\nvoid readwriteCommand(client *c);\nvoid dumpCommand(client *c);\nvoid objectCommand(client *c);\nvoid memoryCommand(client *c);\nvoid clientCommand(client *c);\nvoid helloCommand(client *c);\nvoid evalCommand(client *c);\nvoid evalShaCommand(client *c);\nvoid scriptCommand(client *c);\nvoid timeCommand(client *c);\nvoid bitopCommand(client *c);\nvoid bitcountCommand(client *c);\nvoid bitposCommand(client *c);\nvoid replconfCommand(client *c);\nvoid waitCommand(client *c);\nvoid geoencodeCommand(client *c);\nvoid geodecodeCommand(client *c);\nvoid georadiusbymemberCommand(client *c);\nvoid georadiusbymemberroCommand(client *c);\nvoid georadiusCommand(client *c);\nvoid georadiusroCommand(client *c);\nvoid geoaddCommand(client *c);\nvoid geohashCommand(client *c);\nvoid geoposCommand(client *c);\nvoid geodistCommand(client *c);\nvoid pfselftestCommand(client *c);\nvoid pfaddCommand(client *c);\nvoid pfcountCommand(client *c);\nvoid pfmergeCommand(client *c);\nvoid pfdebugCommand(client *c);\nvoid latencyCommand(client *c);\nvoid moduleCommand(client *c);\nvoid securityWarningCommand(client *c);\nvoid xaddCommand(client *c);\nvoid xrangeCommand(client *c);\nvoid xrevrangeCommand(client *c);\nvoid xlenCommand(client *c);\nvoid xreadCommand(client *c);\nvoid xgroupCommand(client *c);\nvoid xsetidCommand(client *c);\nvoid xackCommand(client *c);\nvoid xpendingCommand(client *c);\nvoid xclaimCommand(client *c);\nvoid xinfoCommand(client *c);\nvoid xdelCommand(client *c);\nvoid xtrimCommand(client *c);\nvoid lolwutCommand(client *c);\nvoid aclCommand(client *c);\nvoid stralgoCommand(client *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 _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line);\nvoid _serverAssert(const char *estr, const char *file, int line);\nvoid _serverPanic(const char *file, int line, const char *msg, ...);\nvoid bugReportStart(void);\nvoid serverLogObjectDebugInfo(const robj *o);\nvoid sigsegvHandler(int sig, siginfo_t *info, void *secret);\nsds genRedisInfoString(const char *section);\nsds genModulesInfoString(sds info);\nvoid enableWatchdog(int period);\nvoid disableWatchdog(void);\nvoid watchdogScheduleSignal(int period);\nvoid serverLogHexDump(int level, char *descr, void *value, size_t len);\nint memtest_preserving_test(unsigned long *m, size_t bytes, int passes);\nvoid mixDigest(unsigned char *digest, void *ptr, size_t len);\nvoid xorDigest(unsigned char *digest, void *ptr, size_t len);\nint populateCommandTableParseFlags(struct redisCommand *c, char *strflags);\n\n/* TLS stuff */\nvoid tlsInit(void);\nint tlsConfigure(redisTLSContextConfig *ctx_config);\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\nint iAmMaster(void);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/setcpuaffinity.c",
    "content": "/* ==========================================================================\n * setcpuaffinity.c - Linux/BSD setcpuaffinity.\n * --------------------------------------------------------------------------\n * Copyright (C) 2020  zhenwei pi\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#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#ifdef __linux__\n#include <sched.h>\n#endif\n#ifdef __FreeBSD__\n#include <sys/param.h>\n#include <sys/cpuset.h>\n#endif\n#ifdef __NetBSD__\n#include <pthread.h>\n#include <sched.h>\n#endif\n#include \"config.h\"\n\n#ifdef USE_SETCPUAFFINITY\nstatic const char *next_token(const char *q,  int sep) {\n    if (q)\n        q = strchr(q, sep);\n    if (q)\n        q++;\n\n    return q;\n}\n\nstatic int next_num(const char *str, char **end, int *result) {\n    if (!str || *str == '\\0' || !isdigit(*str))\n        return -1;\n\n    *result = strtoul(str, end, 10);\n    if (str == *end)\n        return -1;\n\n    return 0;\n}\n\n/* set current thread cpu affinity to cpu list, this function works like\n * taskset command (actually cpulist parsing logic reference to util-linux).\n * example of this function: \"0,2,3\", \"0,2-3\", \"0-20:2\". */\nvoid setcpuaffinity(const char *cpulist) {\n    const char *p, *q;\n    char *end = NULL;\n#ifdef __linux__\n    cpu_set_t cpuset;\n#endif\n#ifdef __FreeBSD__\n    cpuset_t cpuset;\n#endif\n#ifdef __NetBSD__\n    cpuset_t *cpuset;\n#endif\n\n    if (!cpulist)\n        return;\n\n#ifndef __NetBSD__\n    CPU_ZERO(&cpuset);\n#else\n    cpuset = cpuset_create();\n#endif\n\n    q = cpulist;\n    while (p = q, q = next_token(q, ','), p) {\n        int a, b, s;\n        const char *c1, *c2;\n\n        if (next_num(p, &end, &a) != 0)\n            return;\n\n        b = a;\n        s = 1;\n        p = end;\n\n        c1 = next_token(p, '-');\n        c2 = next_token(p, ',');\n\n        if (c1 != NULL && (c2 == NULL || c1 < c2)) {\n            if (next_num(c1, &end, &b) != 0)\n                return;\n\n            c1 = end && *end ? next_token(end, ':') : NULL;\n            if (c1 != NULL && (c2 == NULL || c1 < c2)) {\n                if (next_num(c1, &end, &s) != 0)\n                    return;\n\n                if (s == 0)\n                    return;\n            }\n        }\n\n        if ((a > b))\n            return;\n\n        while (a <= b) {\n#ifndef __NetBSD__\n            CPU_SET(a, &cpuset);\n#else\n            cpuset_set(a, cpuset);\n#endif\n            a += s;\n        }\n    }\n\n    if (end && *end)\n        return;\n\n#ifdef __linux__\n    sched_setaffinity(0, sizeof(cpuset), &cpuset);\n#endif\n#ifdef __FreeBSD__\n    cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset);\n#endif\n#ifdef __NetBSD__\n    pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset);\n    cpuset_destroy(cpuset);\n#endif\n}\n\n#endif /* USE_SETCPUAFFINITY */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__)\n#define HAVE_SETPROCTITLE 1\n#else\n#define HAVE_SETPROCTITLE 0\n#endif\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": "Chapter02/redis-6.0.3/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 <stdint.h>\n#include \"solarisfixes.h\"\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(uint32_t state[5], const unsigned char buffer[64])\n{\n    uint32_t a, b, c, d, e;\n    typedef union {\n        unsigned char c[64];\n        uint32_t l[16];\n    } CHAR64LONG16;\n#ifdef SHA1HANDSOFF\n    CHAR64LONG16 block[1];  /* use array to appear as a pointer */\n    memcpy(block, buffer, 64);\n#else\n    /* The following had better never be used because it causes the\n     * pointer-to-const buffer to be cast into a pointer to non-const.\n     * And the result is written through.  I threw a \"const\" in, hoping\n     * this will cause a diagnostic.\n     */\n    CHAR64LONG16* block = (const CHAR64LONG16*)buffer;\n#endif\n    /* Copy context->state[] to working vars */\n    a = state[0];\n    b = state[1];\n    c = state[2];\n    d = state[3];\n    e = state[4];\n    /* 4 rounds of 20 operations each. Loop unrolled. */\n    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);\n    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);\n    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);\n    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);\n    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);\n    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);\n    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);\n    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);\n    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);\n    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);\n    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);\n    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);\n    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);\n    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);\n    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);\n    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);\n    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);\n    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);\n    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);\n    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);\n    /* Add the working vars back into context.state[] */\n    state[0] += a;\n    state[1] += b;\n    state[2] += c;\n    state[3] += d;\n    state[4] += e;\n    /* Wipe variables */\n    a = b = c = d = e = 0;\n#ifdef SHA1HANDSOFF\n    memset(block, '\\0', sizeof(block));\n#endif\n}\n\n\n/* SHA1Init - Initialize new context */\n\nvoid SHA1Init(SHA1_CTX* context)\n{\n    /* SHA1 initialization constants */\n    context->state[0] = 0x67452301;\n    context->state[1] = 0xEFCDAB89;\n    context->state[2] = 0x98BADCFE;\n    context->state[3] = 0x10325476;\n    context->state[4] = 0xC3D2E1F0;\n    context->count[0] = context->count[1] = 0;\n}\n\n\n/* Run your data through this. */\n\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)\n{\n    uint32_t i, j;\n\n    j = context->count[0];\n    if ((context->count[0] += len << 3) < j)\n        context->count[1]++;\n    context->count[1] += (len>>29);\n    j = (j >> 3) & 63;\n    if ((j + len) > 63) {\n        memcpy(&context->buffer[j], data, (i = 64-j));\n        SHA1Transform(context->state, context->buffer);\n        for ( ; i + 63 < len; i += 64) {\n            SHA1Transform(context->state, &data[i]);\n        }\n        j = 0;\n    }\n    else i = 0;\n    memcpy(&context->buffer[j], &data[i], len - i);\n}\n\n\n/* Add padding and return the message digest. */\n\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context)\n{\n    unsigned i;\n    unsigned char finalcount[8];\n    unsigned char c;\n\n#if 0\t/* untested \"improvement\" by DHR */\n    /* Convert context->count to a sequence of bytes\n     * in finalcount.  Second element first, but\n     * big-endian order within element.\n     * But we do it all backwards.\n     */\n    unsigned char *fcp = &finalcount[8];\n\n    for (i = 0; i < 2; i++)\n       {\n        uint32_t t = context->count[i];\n        int j;\n\n        for (j = 0; j < 4; t >>= 8, j++)\n\t          *--fcp = (unsigned char) t;\n    }\n#else\n    for (i = 0; i < 8; i++) {\n        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]\n         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */\n    }\n#endif\n    c = 0200;\n    SHA1Update(context, &c, 1);\n    while ((context->count[0] & 504) != 448) {\n\tc = 0000;\n        SHA1Update(context, &c, 1);\n    }\n    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */\n    for (i = 0; i < 20; i++) {\n        digest[i] = (unsigned char)\n         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);\n    }\n    /* Wipe variables */\n    memset(context, '\\0', sizeof(*context));\n    memset(&finalcount, '\\0', sizeof(finalcount));\n}\n/* ================ end of sha1.c ================ */\n\n#ifdef REDIS_TEST\n#define BUFSIZE 4096\n\n#define UNUSED(x) (void)(x)\nint sha1Test(int argc, char **argv)\n{\n    SHA1_CTX ctx;\n    unsigned char hash[20], buf[BUFSIZE];\n    int i;\n\n    UNUSED(argc);\n    UNUSED(argv);\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#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sha1.h",
    "content": "#ifndef SHA1_H\n#define SHA1_H\n/* ================ sha1.h ================ */\n/*\nSHA-1 in C\nBy Steve Reid <steve@edmweb.com>\n100% Public Domain\n*/\n\ntypedef struct {\n    uint32_t state[5];\n    uint32_t count[2];\n    unsigned char buffer[64];\n} SHA1_CTX;\n\nvoid SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);\nvoid SHA1Init(SHA1_CTX* context);\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context);\n\n#ifdef REDIS_TEST\nint sha1Test(int argc, char **argv);\n#endif\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sha256.c",
    "content": "/*********************************************************************\n* Filename:   sha256.c\n* Author:     Brad Conte (brad AT bradconte.com)\n* Copyright:\n* Disclaimer: This code is presented \"as is\" without any guarantees.\n* Details:    Implementation of the SHA-256 hashing algorithm.\n              SHA-256 is one of the three algorithms in the SHA2\n              specification. The others, SHA-384 and SHA-512, are not\n              offered in this implementation.\n              Algorithm specification can be found here:\n               * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf\n              This implementation uses little endian byte order.\n*********************************************************************/\n\n/*************************** HEADER FILES ***************************/\n#include <stdlib.h>\n#include <string.h>\n#include \"sha256.h\"\n\n/****************************** MACROS ******************************/\n#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))\n#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))\n\n#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n\n/**************************** VARIABLES *****************************/\nstatic const WORD k[64] = {\n\t0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n\t0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n\t0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n\t0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n\t0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n\t0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n\t0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n\t0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n};\n\n/*********************** FUNCTION DEFINITIONS ***********************/\nvoid sha256_transform(SHA256_CTX *ctx, const BYTE data[])\n{\n\tWORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n\n\tfor (i = 0, j = 0; i < 16; ++i, j += 4)\n\t\tm[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n\tfor ( ; i < 64; ++i)\n\t\tm[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n\n\ta = ctx->state[0];\n\tb = ctx->state[1];\n\tc = ctx->state[2];\n\td = ctx->state[3];\n\te = ctx->state[4];\n\tf = ctx->state[5];\n\tg = ctx->state[6];\n\th = ctx->state[7];\n\n\tfor (i = 0; i < 64; ++i) {\n\t\tt1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];\n\t\tt2 = EP0(a) + MAJ(a,b,c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + t1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = t1 + t2;\n\t}\n\n\tctx->state[0] += a;\n\tctx->state[1] += b;\n\tctx->state[2] += c;\n\tctx->state[3] += d;\n\tctx->state[4] += e;\n\tctx->state[5] += f;\n\tctx->state[6] += g;\n\tctx->state[7] += h;\n}\n\nvoid sha256_init(SHA256_CTX *ctx)\n{\n\tctx->datalen = 0;\n\tctx->bitlen = 0;\n\tctx->state[0] = 0x6a09e667;\n\tctx->state[1] = 0xbb67ae85;\n\tctx->state[2] = 0x3c6ef372;\n\tctx->state[3] = 0xa54ff53a;\n\tctx->state[4] = 0x510e527f;\n\tctx->state[5] = 0x9b05688c;\n\tctx->state[6] = 0x1f83d9ab;\n\tctx->state[7] = 0x5be0cd19;\n}\n\nvoid sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)\n{\n\tWORD i;\n\n\tfor (i = 0; i < len; ++i) {\n\t\tctx->data[ctx->datalen] = data[i];\n\t\tctx->datalen++;\n\t\tif (ctx->datalen == 64) {\n\t\t\tsha256_transform(ctx, ctx->data);\n\t\t\tctx->bitlen += 512;\n\t\t\tctx->datalen = 0;\n\t\t}\n\t}\n}\n\nvoid sha256_final(SHA256_CTX *ctx, BYTE hash[])\n{\n\tWORD i;\n\n\ti = ctx->datalen;\n\n\t// Pad whatever data is left in the buffer.\n\tif (ctx->datalen < 56) {\n\t\tctx->data[i++] = 0x80;\n\t\twhile (i < 56)\n\t\t\tctx->data[i++] = 0x00;\n\t}\n\telse {\n\t\tctx->data[i++] = 0x80;\n\t\twhile (i < 64)\n\t\t\tctx->data[i++] = 0x00;\n\t\tsha256_transform(ctx, ctx->data);\n\t\tmemset(ctx->data, 0, 56);\n\t}\n\n\t// Append to the padding the total message's length in bits and transform.\n\tctx->bitlen += ctx->datalen * 8;\n\tctx->data[63] = ctx->bitlen;\n\tctx->data[62] = ctx->bitlen >> 8;\n\tctx->data[61] = ctx->bitlen >> 16;\n\tctx->data[60] = ctx->bitlen >> 24;\n\tctx->data[59] = ctx->bitlen >> 32;\n\tctx->data[58] = ctx->bitlen >> 40;\n\tctx->data[57] = ctx->bitlen >> 48;\n\tctx->data[56] = ctx->bitlen >> 56;\n\tsha256_transform(ctx, ctx->data);\n\n\t// Since this implementation uses little endian byte ordering and SHA uses big endian,\n\t// reverse all the bytes when copying the final state to the output hash.\n\tfor (i = 0; i < 4; ++i) {\n\t\thash[i]      = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 4]  = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 8]  = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n\t}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sha256.h",
    "content": "/*********************************************************************\n* Filename:   sha256.h\n* Author:     Brad Conte (brad AT bradconte.com)\n* Copyright:\n* Disclaimer: This code is presented \"as is\" without any guarantees.\n* Details:    Defines the API for the corresponding SHA1 implementation.\n*********************************************************************/\n\n#ifndef SHA256_H\n#define SHA256_H\n\n/*************************** HEADER FILES ***************************/\n#include <stddef.h>\n#include <stdint.h>\n\n/****************************** MACROS ******************************/\n#define SHA256_BLOCK_SIZE 32            // SHA256 outputs a 32 byte digest\n\n/**************************** DATA TYPES ****************************/\ntypedef uint8_t BYTE;   // 8-bit byte\ntypedef uint32_t WORD;  // 32-bit word\n\ntypedef struct {\n\tBYTE data[64];\n\tWORD datalen;\n\tunsigned long long bitlen;\n\tWORD state[8];\n} SHA256_CTX;\n\n/*********************** FUNCTION DECLARATIONS **********************/\nvoid sha256_init(SHA256_CTX *ctx);\nvoid sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);\nvoid sha256_final(SHA256_CTX *ctx, BYTE hash[]);\n\n#endif   // SHA256_H\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/siphash.c",
    "content": "/*\n   SipHash reference C implementation\n\n   Copyright (c) 2012-2016 Jean-Philippe Aumasson\n   <jeanphilippe.aumasson@gmail.com>\n   Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>\n   Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>\n\n   To the extent possible under law, the author(s) have dedicated all copyright\n   and related and neighboring rights to this software to the public domain\n   worldwide. This software is distributed without any warranty.\n\n   You should have received a copy of the CC0 Public Domain Dedication along\n   with this software. If not, see\n   <http://creativecommons.org/publicdomain/zero/1.0/>.\n\n   ----------------------------------------------------------------------------\n\n   This version was modified by Salvatore Sanfilippo <antirez@gmail.com>\n   in the following ways:\n\n   1. We use SipHash 1-2. This is not believed to be as strong as the\n      suggested 2-4 variant, but AFAIK there are not trivial attacks\n      against this reduced-rounds version, and it runs at the same speed\n      as Murmurhash2 that we used previously, why the 2-4 variant slowed\n      down Redis by a 4% figure more or less.\n   2. Hard-code rounds in the hope the compiler can optimize it more\n      in this raw from. Anyway we always want the standard 2-4 variant.\n   3. Modify the prototype and implementation so that the function directly\n      returns an uint64_t value, the hash itself, instead of receiving an\n      output buffer. This also means that the output size is set to 8 bytes\n      and the 16 bytes output code handling was removed.\n   4. Provide a case insensitive variant to be used when hashing strings that\n      must be considered identical by the hash table regardless of the case.\n      If we don't have directly a case insensitive hash function, we need to\n      perform a text transformation in some temporary buffer, which is costly.\n   5. Remove debugging code.\n   6. Modified the original test.c file to be a stand-alone function testing\n      the function in the new form (returing an uint64_t) using just the\n      relevant test vector.\n */\n#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n#include <ctype.h>\n\n/* Fast tolower() alike function that does not care about locale\n * but just returns a-z insetad of A-Z. */\nint siptlw(int c) {\n    if (c >= 'A' && c <= 'Z') {\n        return c+('a'-'A');\n    } else {\n        return c;\n    }\n}\n\n/* Test of the CPU is Little Endian and supports not aligned accesses.\n * Two interesting conditions to speedup the function that happen to be\n * in most of x86 servers. */\n#if defined(__X86_64__) || defined(__x86_64__) || defined (__i386__) \\\n\t|| defined (__aarch64__) || defined (__arm64__)\n#define UNALIGNED_LE_CPU\n#endif\n\n#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))\n\n#define U32TO8_LE(p, v)                                                        \\\n    (p)[0] = (uint8_t)((v));                                                   \\\n    (p)[1] = (uint8_t)((v) >> 8);                                              \\\n    (p)[2] = (uint8_t)((v) >> 16);                                             \\\n    (p)[3] = (uint8_t)((v) >> 24);\n\n#define U64TO8_LE(p, v)                                                        \\\n    U32TO8_LE((p), (uint32_t)((v)));                                           \\\n    U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));\n\n#ifdef UNALIGNED_LE_CPU\n#define U8TO64_LE(p) (*((uint64_t*)(p)))\n#else\n#define U8TO64_LE(p)                                                           \\\n    (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |                        \\\n     ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) |                 \\\n     ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) |                 \\\n     ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))\n#endif\n\n#define U8TO64_LE_NOCASE(p)                                                    \\\n    (((uint64_t)(siptlw((p)[0]))) |                                           \\\n     ((uint64_t)(siptlw((p)[1])) << 8) |                                      \\\n     ((uint64_t)(siptlw((p)[2])) << 16) |                                     \\\n     ((uint64_t)(siptlw((p)[3])) << 24) |                                     \\\n     ((uint64_t)(siptlw((p)[4])) << 32) |                                              \\\n     ((uint64_t)(siptlw((p)[5])) << 40) |                                              \\\n     ((uint64_t)(siptlw((p)[6])) << 48) |                                              \\\n     ((uint64_t)(siptlw((p)[7])) << 56))\n\n#define SIPROUND                                                               \\\n    do {                                                                       \\\n        v0 += v1;                                                              \\\n        v1 = ROTL(v1, 13);                                                     \\\n        v1 ^= v0;                                                              \\\n        v0 = ROTL(v0, 32);                                                     \\\n        v2 += v3;                                                              \\\n        v3 = ROTL(v3, 16);                                                     \\\n        v3 ^= v2;                                                              \\\n        v0 += v3;                                                              \\\n        v3 = ROTL(v3, 21);                                                     \\\n        v3 ^= v0;                                                              \\\n        v2 += v1;                                                              \\\n        v1 = ROTL(v1, 17);                                                     \\\n        v1 ^= v2;                                                              \\\n        v2 = ROTL(v2, 32);                                                     \\\n    } while (0)\n\nuint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) {\n#ifndef UNALIGNED_LE_CPU\n    uint64_t hash;\n    uint8_t *out = (uint8_t*) &hash;\n#endif\n    uint64_t v0 = 0x736f6d6570736575ULL;\n    uint64_t v1 = 0x646f72616e646f6dULL;\n    uint64_t v2 = 0x6c7967656e657261ULL;\n    uint64_t v3 = 0x7465646279746573ULL;\n    uint64_t k0 = U8TO64_LE(k);\n    uint64_t k1 = U8TO64_LE(k + 8);\n    uint64_t m;\n    const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));\n    const int left = inlen & 7;\n    uint64_t b = ((uint64_t)inlen) << 56;\n    v3 ^= k1;\n    v2 ^= k0;\n    v1 ^= k1;\n    v0 ^= k0;\n\n    for (; in != end; in += 8) {\n        m = U8TO64_LE(in);\n        v3 ^= m;\n\n        SIPROUND;\n\n        v0 ^= m;\n    }\n\n    switch (left) {\n    case 7: b |= ((uint64_t)in[6]) << 48; /* fall-thru */\n    case 6: b |= ((uint64_t)in[5]) << 40; /* fall-thru */\n    case 5: b |= ((uint64_t)in[4]) << 32; /* fall-thru */\n    case 4: b |= ((uint64_t)in[3]) << 24; /* fall-thru */\n    case 3: b |= ((uint64_t)in[2]) << 16; /* fall-thru */\n    case 2: b |= ((uint64_t)in[1]) << 8; /* fall-thru */\n    case 1: b |= ((uint64_t)in[0]); break;\n    case 0: break;\n    }\n\n    v3 ^= b;\n\n    SIPROUND;\n\n    v0 ^= b;\n    v2 ^= 0xff;\n\n    SIPROUND;\n    SIPROUND;\n\n    b = v0 ^ v1 ^ v2 ^ v3;\n#ifndef UNALIGNED_LE_CPU\n    U64TO8_LE(out, b);\n    return hash;\n#else\n    return b;\n#endif\n}\n\nuint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)\n{\n#ifndef UNALIGNED_LE_CPU\n    uint64_t hash;\n    uint8_t *out = (uint8_t*) &hash;\n#endif\n    uint64_t v0 = 0x736f6d6570736575ULL;\n    uint64_t v1 = 0x646f72616e646f6dULL;\n    uint64_t v2 = 0x6c7967656e657261ULL;\n    uint64_t v3 = 0x7465646279746573ULL;\n    uint64_t k0 = U8TO64_LE(k);\n    uint64_t k1 = U8TO64_LE(k + 8);\n    uint64_t m;\n    const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));\n    const int left = inlen & 7;\n    uint64_t b = ((uint64_t)inlen) << 56;\n    v3 ^= k1;\n    v2 ^= k0;\n    v1 ^= k1;\n    v0 ^= k0;\n\n    for (; in != end; in += 8) {\n        m = U8TO64_LE_NOCASE(in);\n        v3 ^= m;\n\n        SIPROUND;\n\n        v0 ^= m;\n    }\n\n    switch (left) {\n    case 7: b |= ((uint64_t)siptlw(in[6])) << 48; /* fall-thru */\n    case 6: b |= ((uint64_t)siptlw(in[5])) << 40; /* fall-thru */\n    case 5: b |= ((uint64_t)siptlw(in[4])) << 32; /* fall-thru */\n    case 4: b |= ((uint64_t)siptlw(in[3])) << 24; /* fall-thru */\n    case 3: b |= ((uint64_t)siptlw(in[2])) << 16; /* fall-thru */\n    case 2: b |= ((uint64_t)siptlw(in[1])) << 8; /* fall-thru */\n    case 1: b |= ((uint64_t)siptlw(in[0])); break;\n    case 0: break;\n    }\n\n    v3 ^= b;\n\n    SIPROUND;\n\n    v0 ^= b;\n    v2 ^= 0xff;\n\n    SIPROUND;\n    SIPROUND;\n\n    b = v0 ^ v1 ^ v2 ^ v3;\n#ifndef UNALIGNED_LE_CPU\n    U64TO8_LE(out, b);\n    return hash;\n#else\n    return b;\n#endif\n}\n\n\n/* --------------------------------- TEST ------------------------------------ */\n\n#ifdef SIPHASH_TEST\n\nconst uint8_t vectors_sip64[64][8] = {\n    { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },\n    { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },\n    { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },\n    { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },\n    { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },\n    { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },\n    { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },\n    { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },\n    { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },\n    { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },\n    { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },\n    { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },\n    { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },\n    { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },\n    { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },\n    { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },\n    { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },\n    { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },\n    { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },\n    { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },\n    { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },\n    { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },\n    { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },\n    { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },\n    { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },\n    { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },\n    { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },\n    { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },\n    { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },\n    { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },\n    { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },\n    { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },\n    { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },\n    { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },\n    { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },\n    { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },\n    { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },\n    { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },\n    { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },\n    { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },\n    { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },\n    { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },\n    { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },\n    { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },\n    { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },\n    { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },\n    { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },\n    { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },\n    { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },\n    { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },\n    { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },\n    { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },\n    { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },\n    { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },\n    { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },\n    { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },\n    { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },\n    { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },\n    { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },\n    { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },\n    { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },\n    { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },\n    { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },\n    { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, },\n};\n\n\n/* Test siphash using a test vector. Returns 0 if the function passed\n * all the tests, otherwise 1 is returned.\n *\n * IMPORTANT: The test vector is for SipHash 2-4. Before running\n * the test revert back the siphash() function to 2-4 rounds since\n * now it uses 1-2 rounds. */\nint siphash_test(void) {\n    uint8_t in[64], k[16];\n    int i;\n    int fails = 0;\n\n    for (i = 0; i < 16; ++i)\n        k[i] = i;\n\n    for (i = 0; i < 64; ++i) {\n        in[i] = i;\n        uint64_t hash = siphash(in, i, k);\n        const uint8_t *v = NULL;\n        v = (uint8_t *)vectors_sip64;\n        if (memcmp(&hash, v + (i * 8), 8)) {\n            /* printf(\"fail for %d bytes\\n\", i); */\n            fails++;\n        }\n    }\n\n    /* Run a few basic tests with the case insensitive version. */\n    uint64_t h1, h2;\n    h1 = siphash((uint8_t*)\"hello world\",11,(uint8_t*)\"1234567812345678\");\n    h2 = siphash_nocase((uint8_t*)\"hello world\",11,(uint8_t*)\"1234567812345678\");\n    if (h1 != h2) fails++;\n\n    h1 = siphash((uint8_t*)\"hello world\",11,(uint8_t*)\"1234567812345678\");\n    h2 = siphash_nocase((uint8_t*)\"HELLO world\",11,(uint8_t*)\"1234567812345678\");\n    if (h1 != h2) fails++;\n\n    h1 = siphash((uint8_t*)\"HELLO world\",11,(uint8_t*)\"1234567812345678\");\n    h2 = siphash_nocase((uint8_t*)\"HELLO world\",11,(uint8_t*)\"1234567812345678\");\n    if (h1 == h2) fails++;\n\n    if (!fails) return 0;\n    return 1;\n}\n\nint main(void) {\n    if (siphash_test() == 0) {\n        printf(\"SipHash test: OK\\n\");\n        return 0;\n    } else {\n        printf(\"SipHash test: FAILED\\n\");\n        return 1;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 * 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 * 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 * 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 \"server.h\"\n#include \"slowlog.h\"\n\n/* Create a new slowlog entry.\n * Incrementing the ref count of all the objects retained is up to\n * this function. */\nslowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long duration) {\n    slowlogEntry *se = zmalloc(sizeof(*se));\n    int j, slargc = argc;\n\n    if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;\n    se->argc = slargc;\n    se->argv = 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        if (slargc != argc && j == slargc-1) {\n            se->argv[j] = createObject(OBJ_STRING,\n                sdscatprintf(sdsempty(),\"... (%d more arguments)\",\n                argc-slargc+1));\n        } else {\n            /* Trim too long strings as well... */\n            if (argv[j]->type == OBJ_STRING &&\n                sdsEncodedObject(argv[j]) &&\n                sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)\n            {\n                sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);\n\n                s = sdscatprintf(s,\"... (%lu more bytes)\",\n                    (unsigned long)\n                    sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);\n                se->argv[j] = createObject(OBJ_STRING,s);\n            } else if (argv[j]->refcount == OBJ_SHARED_REFCOUNT) {\n                se->argv[j] = argv[j];\n            } else {\n                /* Here we need to dupliacate the string objects composing the\n                 * argument vector of the command, because those may otherwise\n                 * end shared with string objects stored into keys. Having\n                 * shared objects between any part of Redis, and the data\n                 * structure holding the data, is a problem: FLUSHALL ASYNC\n                 * may release the shared string object and create a race. */\n                se->argv[j] = dupStringObject(argv[j]);\n            }\n        }\n    }\n    se->time = time(NULL);\n    se->duration = duration;\n    se->id = server.slowlog_entry_id++;\n    se->peerid = sdsnew(getClientPeerId(c));\n    se->cname = c->name ? sdsnew(c->name->ptr) : sdsempty();\n    return se;\n}\n\n/* Free a slow log entry. The argument is void so that the prototype of this\n * function matches the one of the 'free' method of adlist.c.\n *\n * This function will take care to release all the retained object. */\nvoid slowlogFreeEntry(void *septr) {\n    slowlogEntry *se = septr;\n    int j;\n\n    for (j = 0; j < se->argc; j++)\n        decrRefCount(se->argv[j]);\n    zfree(se->argv);\n    sdsfree(se->peerid);\n    sdsfree(se->cname);\n    zfree(se);\n}\n\n/* Initialize the slow log. This function should be called a single time\n * at server startup. */\nvoid slowlogInit(void) {\n    server.slowlog = listCreate();\n    server.slowlog_entry_id = 0;\n    listSetFreeMethod(server.slowlog,slowlogFreeEntry);\n}\n\n/* Push a new entry into the slow log.\n * This function will make sure to trim the slow log accordingly to the\n * configured max length. */\nvoid slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration) {\n    if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */\n    if (duration >= server.slowlog_log_slower_than)\n        listAddNodeHead(server.slowlog,\n                        slowlogCreateEntry(c,argv,argc,duration));\n\n    /* Remove old entries if needed. */\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. */\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. */\nvoid slowlogCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"GET [count] -- Return top entries from the slowlog (default: 10).\"\n\"    Entries are made of:\",\n\"    id, timestamp, time in microseconds, arguments array, client IP and port, client name\",\n\"LEN -- Return the length of the slowlog.\",\n\"RESET -- Reset the slowlog.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"reset\")) {\n        slowlogReset();\n        addReply(c,shared.ok);\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"len\")) {\n        addReplyLongLong(c,listLength(server.slowlog));\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) != C_OK)\n            return;\n\n        listRewind(server.slowlog,&li);\n        totentries = addReplyDeferredLen(c);\n        while(count-- && (ln = listNext(&li))) {\n            int j;\n\n            se = ln->value;\n            addReplyArrayLen(c,6);\n            addReplyLongLong(c,se->id);\n            addReplyLongLong(c,se->time);\n            addReplyLongLong(c,se->duration);\n            addReplyArrayLen(c,se->argc);\n            for (j = 0; j < se->argc; j++)\n                addReplyBulk(c,se->argv[j]);\n            addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid));\n            addReplyBulkCBuffer(c,se->cname,sdslen(se->cname));\n            sent++;\n        }\n        setDeferredArrayLen(c,totentries,sent);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#ifndef __SLOWLOG_H__\n#define __SLOWLOG_H__\n\n#define SLOWLOG_ENTRY_MAX_ARGC 32\n#define SLOWLOG_ENTRY_MAX_STRING 128\n\n/* This structure defines an entry inside the slow log list */\ntypedef struct slowlogEntry {\n    robj **argv;\n    int argc;\n    long long id;       /* Unique entry identifier. */\n    long long duration; /* Time spent by the query, in microseconds. */\n    time_t time;        /* Unix time at which the query was executed. */\n    sds cname;          /* Client name. */\n    sds peerid;         /* Client network address. */\n} slowlogEntry;\n\n/* Exported API */\nvoid slowlogInit(void);\nvoid slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration);\n\n/* Exported commands */\nvoid slowlogCommand(client *c);\n\n#endif /* __SLOWLOG_H__ */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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(__sun)\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\n#endif /* __sun */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include \"pqsort.h\" /* Partial qsort for SORT+LIMIT */\n#include <math.h> /* isnan() */\n\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);\n\nredisSortOperation *createSortOperation(int type, robj *pattern) {\n    redisSortOperation *so = zmalloc(sizeof(*so));\n    so->type = type;\n    so->pattern = pattern;\n    return so;\n}\n\n/* Return the value associated to the key with a name obtained using\n * the following rules:\n *\n * 1) The first occurrence of '*' in 'pattern' is substituted with '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 *\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 *\n * The returned object will always have its refcount increased by 1\n * when it is non-NULL. */\nrobj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag) {\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    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 = getDecodedObject(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    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    if ((f = strstr(p+1, \"->\")) != NULL && *(f+2) != '\\0') {\n        fieldlen = sdslen(spat)-(f-spat)-2;\n        fieldobj = createStringObject(f+2,fieldlen);\n    } else {\n        fieldlen = 0;\n    }\n\n    /* Perform the '*' substitution. */\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    if (!writeflag)\n        o = lookupKeyRead(db,keyobj);\n    else\n        o = lookupKeyWrite(db,keyobj);\n    if (o == NULL) goto noobj;\n\n    if (fieldobj) {\n        if (o->type != OBJ_HASH) goto noobj;\n\n        /* Retrieve value from hash by the field name. The returend object\n         * is a new object with refcount already incremented. */\n        o = hashTypeGetValueObject(o, fieldobj->ptr);\n    } else {\n        if (o->type != OBJ_STRING) goto noobj;\n\n        /* Every object that this function returns needs to have its refcount\n         * increased. sortCommand decreases it again. */\n        incrRefCount(o);\n    }\n    decrRefCount(keyobj);\n    if (fieldobj) decrRefCount(fieldobj);\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 */\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        /* Numeric sorting. Here it's trivial as we precomputed scores */\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            cmp = compareStringObjects(so1->obj,so2->obj);\n        }\n    } else {\n        /* Alphanumeric sorting */\n        if (server.sort_bypattern) {\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                if (server.sort_store) {\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                    cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);\n                }\n            }\n        } else {\n            /* Compare elements directly. */\n            if (server.sort_store) {\n                cmp = compareStringObjects(so1->obj,so2->obj);\n            } else {\n                cmp = collateStringObjects(so1->obj,so2->obj);\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(client *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_conversion_error = 0;\n    int syntax_error = 0;\n    robj *sortval, *sortby = NULL, *storekey = NULL;\n    redisSortObject *vector; /* Resulting vector to sort */\n\n    /* Create a list of operations to perform for every sorted element.\n     * Operations can be GET */\n    operations = listCreate();\n    listSetFreeMethod(operations,zfree);\n    j = 2; /* options start at argv[2] */\n\n    /* The SORT command has an SQL-alike syntax, parse it */\n    while(j < c->argc) {\n        int leftargs = c->argc-j-1;\n        if (!strcasecmp(c->argv[j]->ptr,\"asc\")) {\n            desc = 0;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"desc\")) {\n            desc = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"alpha\")) {\n            alpha = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"limit\") && leftargs >= 2) {\n            if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL)\n                 != C_OK) ||\n                (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL)\n                 != C_OK))\n            {\n                syntax_error++;\n                break;\n            }\n            j+=2;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"store\") && leftargs >= 1) {\n            storekey = c->argv[j+1];\n            j++;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"by\") && leftargs >= 1) {\n            sortby = c->argv[j+1];\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            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        } else if (!strcasecmp(c->argv[j]->ptr,\"get\") && leftargs >= 1) {\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                SORT_OP_GET,c->argv[j+1]));\n            getop++;\n            j++;\n        } else {\n            addReply(c,shared.syntaxerr);\n            syntax_error++;\n            break;\n        }\n        j++;\n    }\n\n    /* Handle syntax errors set during options parsing. */\n    if (syntax_error) {\n        listRelease(operations);\n        return;\n    }\n\n    /* Lookup the key to sort. It must be of the right types */\n    if (storekey)\n        sortval = lookupKeyRead(c->db,c->argv[1]);\n    else\n        sortval = lookupKeyWrite(c->db,c->argv[1]);\n    if (sortval && sortval->type != OBJ_SET &&\n                   sortval->type != OBJ_LIST &&\n                   sortval->type != OBJ_ZSET)\n    {\n        listRelease(operations);\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\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    if (sortval)\n        incrRefCount(sortval);\n    else\n        sortval = createQuicklistObject();\n\n\n    /* When sorting a set with no sort specified, we must sort the output\n     * so the result is consistent across scripting and replication.\n     *\n     * The other types (list, sorted set) will retain their native order\n     * even if no sort order is requested, so they remain stable across\n     * scripting and replication. */\n    if (dontsort &&\n        sortval->type == OBJ_SET &&\n        (storekey || c->flags & CLIENT_LUA))\n    {\n        /* Force ALPHA sorting */\n        dontsort = 0;\n        alpha = 1;\n        sortby = NULL;\n    }\n\n    /* Destructively convert encoded sorted sets for SORT. */\n    if (sortval->type == OBJ_ZSET)\n        zsetConvert(sortval, OBJ_ENCODING_SKIPLIST);\n\n    /* Objtain the length of the object to sort. */\n    switch(sortval->type) {\n    case OBJ_LIST: vectorlen = listTypeLength(sortval); break;\n    case OBJ_SET: vectorlen =  setTypeSize(sortval); break;\n    case OBJ_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;\n    default: vectorlen = 0; serverPanic(\"Bad SORT type\"); /* Avoid GCC warning */\n    }\n\n    /* Perform LIMIT start,count sanity checking. */\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    /* Whenever possible, we load elements into the output array in a more\n     * direct way. This is possible if:\n     *\n     * 1) The object to sort is a sorted set or a list (internally sorted).\n     * 2) There is nothing to sort as dontsort is true (BY <constant string>).\n     *\n     * In this special case, if we have a LIMIT option that actually reduces\n     * the number of elements to fetch, we also optimize to just load the\n     * range we are interested in and allocating a vector that is big enough\n     * for the selected range length. */\n    if ((sortval->type == OBJ_ZSET || sortval->type == OBJ_LIST) &&\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    vector = zmalloc(sizeof(redisSortObject)*vectorlen);\n    j = 0;\n\n    if (sortval->type == OBJ_LIST && dontsort) {\n        /* Special handling for a list, if 'dontsort' is true.\n         * This makes sure we return elements in the list 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        if (end >= start) {\n            listTypeIterator *li;\n            listTypeEntry entry;\n            li = listTypeInitIterator(sortval,\n                    desc ? (long)(listTypeLength(sortval) - start - 1) : start,\n                    desc ? LIST_HEAD : LIST_TAIL);\n\n            while(j < vectorlen && 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            /* Fix start/end: output code is not aware of this optimization. */\n            end -= start;\n            start = 0;\n        }\n    } else if (sortval->type == OBJ_LIST) {\n        listTypeIterator *li = listTypeInitIterator(sortval,0,LIST_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    } else if (sortval->type == OBJ_SET) {\n        setTypeIterator *si = setTypeInitIterator(sortval);\n        sds sdsele;\n        while((sdsele = setTypeNextObject(si)) != NULL) {\n            vector[j].obj = createObject(OBJ_STRING,sdsele);\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        setTypeReleaseIterator(si);\n    } else if (sortval->type == OBJ_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        zset *zs = sortval->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n        sds sdsele;\n        int rangelen = vectorlen;\n\n        /* Check if starting point is trivial, before doing log(N) lookup. */\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        while(rangelen--) {\n            serverAssertWithInfo(c,sortval,ln != NULL);\n            sdsele = ln->ele;\n            vector[j].obj = createStringObject(sdsele,sdslen(sdsele));\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        /* Fix start/end: output code is not aware of this optimization. */\n        end -= start;\n        start = 0;\n    } else if (sortval->type == OBJ_ZSET) {\n        dict *set = ((zset*)sortval->ptr)->dict;\n        dictIterator *di;\n        dictEntry *setele;\n        sds sdsele;\n        di = dictGetIterator(set);\n        while((setele = dictNext(di)) != NULL) {\n            sdsele =  dictGetKey(setele);\n            vector[j].obj = createStringObject(sdsele,sdslen(sdsele));\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        dictReleaseIterator(di);\n    } else {\n        serverPanic(\"Unknown type\");\n    }\n    serverAssertWithInfo(c,sortval,j == vectorlen);\n\n    /* Now it's time to load the right scores in the sorting vector */\n    if (!dontsort) {\n        for (j = 0; j < vectorlen; j++) {\n            robj *byval;\n            if (sortby) {\n                /* lookup value to sort by */\n                byval = lookupKeyByPattern(c->db,sortby,vector[j].obj,storekey!=NULL);\n                if (!byval) continue;\n            } else {\n                /* use object itself to sort by */\n                byval = vector[j].obj;\n            }\n\n            if (alpha) {\n                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);\n            } else {\n                if (sdsEncodedObject(byval)) {\n                    char *eptr;\n\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_conversion_error = 1;\n                    }\n                } else if (byval->encoding == OBJ_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                    vector[j].u.score = (long)byval->ptr;\n                } else {\n                    serverAssertWithInfo(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        server.sort_desc = desc;\n        server.sort_alpha = alpha;\n        server.sort_bypattern = sortby ? 1 : 0;\n        server.sort_store = storekey ? 1 : 0;\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    outputlen = getop ? getop*(end-start+1) : end-start+1;\n    if (int_conversion_error) {\n        addReplyError(c,\"One or more scores can't be converted into double\");\n    } else if (storekey == NULL) {\n        /* STORE option not specified, sent the sorting result to client */\n        addReplyArrayLen(c,outputlen);\n        for (j = start; j <= end; j++) {\n            listNode *ln;\n            listIter li;\n\n            if (!getop) addReplyBulk(c,vector[j].obj);\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,storekey!=NULL);\n\n                if (sop->type == SORT_OP_GET) {\n                    if (!val) {\n                        addReplyNull(c);\n                    } else {\n                        addReplyBulk(c,val);\n                        decrRefCount(val);\n                    }\n                } else {\n                    /* Always fails */\n                    serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET);\n                }\n            }\n        }\n    } else {\n        robj *sobj = createQuicklistObject();\n\n        /* STORE option specified, set the sorting result as a List object */\n        for (j = start; j <= end; j++) {\n            listNode *ln;\n            listIter li;\n\n            if (!getop) {\n                listTypePush(sobj,vector[j].obj,LIST_TAIL);\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,storekey!=NULL);\n\n                    if (sop->type == SORT_OP_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,LIST_TAIL);\n                        decrRefCount(val);\n                    } else {\n                        /* Always fails */\n                        serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET);\n                    }\n                }\n            }\n        }\n        if (outputlen) {\n            setKey(c,c->db,storekey,sobj);\n            notifyKeyspaceEvent(NOTIFY_LIST,\"sortstore\",storekey,\n                                c->db->id);\n            server.dirty += outputlen;\n        } else if (dbDelete(c->db,storekey)) {\n            signalModifiedKey(c,c->db,storekey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",storekey,c->db->id);\n            server.dirty++;\n        }\n        decrRefCount(sobj);\n        addReplyLongLong(c,outputlen);\n    }\n\n    /* Cleanup */\n    for (j = 0; j < vectorlen; j++)\n        decrRefCount(vector[j].obj);\n\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": "Chapter02/redis-6.0.3/src/sparkline.c",
    "content": "/* sparkline.c -- ASCII Sparklines\n * This code is modified from http://github.com/antirez/aspark and adapted\n * in order to return SDS strings instead of outputting directly to\n * the terminal.\n *\n * ---------------------------------------------------------------------------\n *\n * Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@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 *\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 \"server.h\"\n\n#include <math.h>\n\n/* This is the charset used to display the graphs, but multiple rows are used\n * to increase the resolution. */\nstatic char charset[] = \"_-`\";\nstatic char charset_fill[] = \"_o#\";\nstatic int charset_len = sizeof(charset)-1;\nstatic int label_margin_top = 1;\n\n/* ----------------------------------------------------------------------------\n * Sequences are arrays of samples we use to represent data to turn\n * into sparklines. This is the API in order to generate a sparkline:\n *\n * struct sequence *seq = createSparklineSequence();\n * sparklineSequenceAddSample(seq, 10, NULL);\n * sparklineSequenceAddSample(seq, 20, NULL);\n * sparklineSequenceAddSample(seq, 30, \"last sample label\");\n * sds output = sparklineRender(sdsempty(), seq, 80, 4, SPARKLINE_FILL);\n * freeSparklineSequence(seq);\n * ------------------------------------------------------------------------- */\n\n/* Create a new sequence. */\nstruct sequence *createSparklineSequence(void) {\n    struct sequence *seq = zmalloc(sizeof(*seq));\n    seq->length = 0;\n    seq->samples = NULL;\n    return seq;\n}\n\n/* Add a new sample into a sequence. */\nvoid sparklineSequenceAddSample(struct sequence *seq, double value, char *label) {\n    label = (label == NULL || label[0] == '\\0') ? NULL : zstrdup(label);\n    if (seq->length == 0) {\n        seq->min = seq->max = value;\n    } else {\n        if (value < seq->min) seq->min = value;\n        else if (value > seq->max) seq->max = value;\n    }\n    seq->samples = zrealloc(seq->samples,sizeof(struct sample)*(seq->length+1));\n    seq->samples[seq->length].value = value;\n    seq->samples[seq->length].label = label;\n    seq->length++;\n    if (label) seq->labels++;\n}\n\n/* Free a sequence. */\nvoid freeSparklineSequence(struct sequence *seq) {\n    int j;\n\n    for (j = 0; j < seq->length; j++)\n        zfree(seq->samples[j].label);\n    zfree(seq->samples);\n    zfree(seq);\n}\n\n/* ----------------------------------------------------------------------------\n * ASCII rendering of sequence\n * ------------------------------------------------------------------------- */\n\n/* Render part of a sequence, so that render_sequence() call call this function\n * with differnent parts in order to create the full output without overflowing\n * the current terminal columns. */\nsds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) {\n    int j;\n    double relmax = seq->max - seq->min;\n    int steps = charset_len*rows;\n    int row = 0;\n    char *chars = zmalloc(len);\n    int loop = 1;\n    int opt_fill = flags & SPARKLINE_FILL;\n    int opt_log = flags & SPARKLINE_LOG_SCALE;\n\n    if (opt_log) {\n        relmax = log(relmax+1);\n    } else if (relmax == 0) {\n        relmax = 1;\n    }\n\n    while(loop) {\n        loop = 0;\n        memset(chars,' ',len);\n        for (j = 0; j < len; j++) {\n            struct sample *s = &seq->samples[j+offset];\n            double relval = s->value - seq->min;\n            int step;\n\n            if (opt_log) relval = log(relval+1);\n            step = (int) (relval*steps)/relmax;\n            if (step < 0) step = 0;\n            if (step >= steps) step = steps-1;\n\n            if (row < rows) {\n                /* Print the character needed to create the sparkline */\n                int charidx = step-((rows-row-1)*charset_len);\n                loop = 1;\n                if (charidx >= 0 && charidx < charset_len) {\n                    chars[j] = opt_fill ? charset_fill[charidx] :\n                                          charset[charidx];\n                } else if(opt_fill && charidx >= charset_len) {\n                    chars[j] = '|';\n                }\n            } else {\n                /* Labels spacing */\n                if (seq->labels && row-rows < label_margin_top) {\n                    loop = 1;\n                    break;\n                }\n                /* Print the label if needed. */\n                if (s->label) {\n                    int label_len = strlen(s->label);\n                    int label_char = row - rows - label_margin_top;\n\n                    if (label_len > label_char) {\n                        loop = 1;\n                        chars[j] = s->label[label_char];\n                    }\n                }\n            }\n        }\n        if (loop) {\n            row++;\n            output = sdscatlen(output,chars,len);\n            output = sdscatlen(output,\"\\n\",1);\n        }\n    }\n    zfree(chars);\n    return output;\n}\n\n/* Turn a sequence into its ASCII representation */\nsds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags) {\n    int j;\n\n    for (j = 0; j < seq->length; j += columns) {\n        int sublen = (seq->length-j) < columns ? (seq->length-j) : columns;\n\n        if (j != 0) output = sdscatlen(output,\"\\n\",1);\n        output = sparklineRenderRange(output, seq, rows, j, sublen, flags);\n    }\n    return output;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/sparkline.h",
    "content": "/* sparkline.h -- ASCII Sparklines header file\n *\n * ---------------------------------------------------------------------------\n *\n * Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@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 *\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 __SPARKLINE_H\n#define __SPARKLINE_H\n\n/* A sequence is represented of many \"samples\" */\nstruct sample {\n    double value;\n    char *label;\n};\n\nstruct sequence {\n    int length;\n    int labels;\n    struct sample *samples;\n    double min, max;\n};\n\n#define SPARKLINE_NO_FLAGS 0\n#define SPARKLINE_FILL 1      /* Fill the area under the curve. */\n#define SPARKLINE_LOG_SCALE 2 /* Use logarithmic scale. */\n\nstruct sequence *createSparklineSequence(void);\nvoid sparklineSequenceAddSample(struct sequence *seq, double value, char *label);\nvoid freeSparklineSequence(struct sequence *seq);\nsds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags);\nsds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags);\n\n#endif /* __SPARKLINE_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/stream.h",
    "content": "#ifndef STREAM_H\n#define STREAM_H\n\n#include \"rax.h\"\n#include \"listpack.h\"\n\n/* Stream item ID: a 128 bit number composed of a milliseconds time and\n * a sequence counter. IDs generated in the same millisecond (or in a past\n * millisecond if the clock jumped backward) will use the millisecond time\n * of the latest generated ID and an incremented sequence. */\ntypedef struct streamID {\n    uint64_t ms;        /* Unix time in milliseconds. */\n    uint64_t seq;       /* Sequence number. */\n} streamID;\n\ntypedef struct stream {\n    rax *rax;               /* The radix tree holding the stream. */\n    uint64_t length;        /* Number of elements inside this stream. */\n    streamID last_id;       /* Zero if there are yet no items. */\n    rax *cgroups;           /* Consumer groups dictionary: name -> streamCG */\n} stream;\n\n/* We define an iterator to iterate stream items in an abstract way, without\n * caring about the radix tree + listpack representation. Technically speaking\n * the iterator is only used inside streamReplyWithRange(), so could just\n * be implemented inside the function, but practically there is the AOF\n * rewriting code that also needs to iterate the stream to emit the XADD\n * commands. */\ntypedef struct streamIterator {\n    stream *stream;         /* The stream we are iterating. */\n    streamID master_id;     /* ID of the master entry at listpack head. */\n    uint64_t master_fields_count;       /* Master entries # of fields. */\n    unsigned char *master_fields_start; /* Master entries start in listpack. */\n    unsigned char *master_fields_ptr;   /* Master field to emit next. */\n    int entry_flags;                    /* Flags of entry we are emitting. */\n    int rev;                /* True if iterating end to start (reverse). */\n    uint64_t start_key[2];  /* Start key as 128 bit big endian. */\n    uint64_t end_key[2];    /* End key as 128 bit big endian. */\n    raxIterator ri;         /* Rax iterator. */\n    unsigned char *lp;      /* Current listpack. */\n    unsigned char *lp_ele;  /* Current listpack cursor. */\n    unsigned char *lp_flags; /* Current entry flags pointer. */\n    /* Buffers used to hold the string of lpGet() when the element is\n     * integer encoded, so that there is no string representation of the\n     * element inside the listpack itself. */\n    unsigned char field_buf[LP_INTBUF_SIZE];\n    unsigned char value_buf[LP_INTBUF_SIZE];\n} streamIterator;\n\n/* Consumer group. */\ntypedef struct streamCG {\n    streamID last_id;       /* Last delivered (not acknowledged) ID for this\n                               group. Consumers that will just ask for more\n                               messages will served with IDs > than this. */\n    rax *pel;               /* Pending entries list. This is a radix tree that\n                               has every message delivered to consumers (without\n                               the NOACK option) that was yet not acknowledged\n                               as processed. The key of the radix tree is the\n                               ID as a 64 bit big endian number, while the\n                               associated value is a streamNACK structure.*/\n    rax *consumers;         /* A radix tree representing the consumers by name\n                               and their associated representation in the form\n                               of streamConsumer structures. */\n} streamCG;\n\n/* A specific consumer in a consumer group.  */\ntypedef struct streamConsumer {\n    mstime_t seen_time;         /* Last time this consumer was active. */\n    sds name;                   /* Consumer name. This is how the consumer\n                                   will be identified in the consumer group\n                                   protocol. Case sensitive. */\n    rax *pel;                   /* Consumer specific pending entries list: all\n                                   the pending messages delivered to this\n                                   consumer not yet acknowledged. Keys are\n                                   big endian message IDs, while values are\n                                   the same streamNACK structure referenced\n                                   in the \"pel\" of the conumser group structure\n                                   itself, so the value is shared. */\n} streamConsumer;\n\n/* Pending (yet not acknowledged) message in a consumer group. */\ntypedef struct streamNACK {\n    mstime_t delivery_time;     /* Last time this message was delivered. */\n    uint64_t delivery_count;    /* Number of times this message was delivered.*/\n    streamConsumer *consumer;   /* The consumer this message was delivered to\n                                   in the last delivery. */\n} streamNACK;\n\n/* Stream propagation informations, passed to functions in order to propagate\n * XCLAIM commands to AOF and slaves. */\ntypedef struct streamPropInfo {\n    robj *keyname;\n    robj *groupname;\n} streamPropInfo;\n\n/* Prototypes of exported APIs. */\nstruct client;\n\n/* Flags for streamLookupConsumer */\n#define SLC_NONE      0\n#define SLC_NOCREAT   (1<<0) /* Do not create the consumer if it doesn't exist */\n#define SLC_NOREFRESH (1<<1) /* Do not update consumer's seen-time */\n\nstream *streamNew(void);\nvoid freeStream(stream *s);\nunsigned long streamLength(const robj *subject);\nsize_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end, size_t count, int rev, streamCG *group, streamConsumer *consumer, int flags, streamPropInfo *spi);\nvoid streamIteratorStart(streamIterator *si, stream *s, streamID *start, streamID *end, int rev);\nint streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields);\nvoid streamIteratorGetField(streamIterator *si, unsigned char **fieldptr, unsigned char **valueptr, int64_t *fieldlen, int64_t *valuelen);\nvoid streamIteratorStop(streamIterator *si);\nstreamCG *streamLookupCG(stream *s, sds groupname);\nstreamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags);\nstreamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id);\nstreamNACK *streamCreateNACK(streamConsumer *consumer);\nvoid streamDecodeID(void *buf, streamID *id);\nint streamCompareID(streamID *a, streamID *b);\nvoid streamFreeNACK(streamNACK *na);\nvoid streamIncrID(streamID *id);\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.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 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 > SYNCIO__RESOLUTION) ?\n                          remaining : 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 > SYNCIO__RESOLUTION) ?\n                          remaining : 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        size--;\n    }\n    return nread;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.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. Note that we only check string encoded objects\n * as their string length can be queried in constant time. */\nvoid hashTypeTryConversion(robj *o, robj **argv, int start, int end) {\n    int i;\n\n    if (o->encoding != OBJ_ENCODING_ZIPLIST) return;\n\n    for (i = start; i <= end; i++) {\n        if (sdsEncodedObject(argv[i]) &&\n            sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)\n        {\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n            break;\n        }\n    }\n}\n\n/* Get the value from a ziplist encoded hash, identified by field.\n * Returns -1 when the field cannot be found. */\nint hashTypeGetFromZiplist(robj *o, sds 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    serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);\n\n    zl = o->ptr;\n    fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n    if (fptr != NULL) {\n        fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);\n        if (fptr != NULL) {\n            /* Grab pointer to the value (fptr points to the field) */\n            vptr = ziplistNext(zl, fptr);\n            serverAssert(vptr != NULL);\n        }\n    }\n\n    if (vptr != NULL) {\n        ret = ziplistGet(vptr, vstr, vlen, vll);\n        serverAssert(ret);\n        return 0;\n    }\n\n    return -1;\n}\n\n/* Get the value from a hash table encoded hash, identified by field.\n * Returns NULL when the field cannot be found, otherwise the SDS value\n * is returned. */\nsds hashTypeGetFromHashTable(robj *o, sds field) {\n    dictEntry *de;\n\n    serverAssert(o->encoding == OBJ_ENCODING_HT);\n\n    de = dictFind(o->ptr, field);\n    if (de == NULL) return NULL;\n    return dictGetVal(de);\n}\n\n/* Higher level function of hashTypeGet*() that returns the hash value\n * associated with the specified field. If the field is found C_OK\n * is returned, otherwise C_ERR. The returned object is returned by\n * reference in either *vstr and *vlen if it's returned in string form,\n * or stored in *vll if it's returned as a number.\n *\n * If *vll is populated *vstr is set to NULL, so the caller\n * can always check the function return by checking the return value\n * for C_OK and checking if vll (or vstr) is NULL. */\nint hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        *vstr = NULL;\n        if (hashTypeGetFromZiplist(o, field, vstr, vlen, vll) == 0)\n            return C_OK;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        sds value;\n        if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {\n            *vstr = (unsigned char*) value;\n            *vlen = sdslen(value);\n            return C_OK;\n        }\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return C_ERR;\n}\n\n/* Like hashTypeGetValue() but returns a Redis object, which is useful for\n * interaction with the hash type outside t_hash.c.\n * The function returns NULL if the field is not found in the hash. Otherwise\n * a newly allocated string object with the value is returned. */\nrobj *hashTypeGetValueObject(robj *o, sds field) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vll;\n\n    if (hashTypeGetValue(o,field,&vstr,&vlen,&vll) == C_ERR) return NULL;\n    if (vstr) return createStringObject((char*)vstr,vlen);\n    else return createStringObjectFromLongLong(vll);\n}\n\n/* Higher level function using hashTypeGet*() to return the length of the\n * object associated with the requested field, or 0 if the field does not\n * exist. */\nsize_t hashTypeGetValueLength(robj *o, sds field) {\n    size_t len = 0;\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)\n            len = vstr ? vlen : sdigits10(vll);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        sds aux;\n\n        if ((aux = hashTypeGetFromHashTable(o, field)) != NULL)\n            len = sdslen(aux);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return len;\n}\n\n/* Test if the specified field exists in the given hash. Returns 1 if the field\n * exists, and 0 when it doesn't. */\nint hashTypeExists(robj *o, sds field) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        if (hashTypeGetFromHashTable(o, field) != NULL) return 1;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return 0;\n}\n\n/* Add a new field, overwrite the old with the new value if it already exists.\n * Return 0 on insert and 1 on update.\n *\n * By default, the key and value SDS strings are copied if needed, so the\n * caller retains ownership of the strings passed. However this behavior\n * can be effected by passing appropriate flags (possibly bitwise OR-ed):\n *\n * HASH_SET_TAKE_FIELD -- The SDS field ownership passes to the function.\n * HASH_SET_TAKE_VALUE -- The SDS value ownership passes to the function.\n *\n * When the flags are used the caller does not need to release the passed\n * SDS string(s). It's up to the function to use the string to create a new\n * entry or to free the SDS string before returning to the caller.\n *\n * HASH_SET_COPY corresponds to no flags passed, and means the default\n * semantics of copying the values if needed.\n *\n */\n#define HASH_SET_TAKE_FIELD (1<<0)\n#define HASH_SET_TAKE_VALUE (1<<1)\n#define HASH_SET_COPY 0\nint hashTypeSet(robj *o, sds field, sds value, int flags) {\n    int update = 0;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr, *vptr;\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);\n            if (fptr != NULL) {\n                /* Grab pointer to the value (fptr points to the field) */\n                vptr = ziplistNext(zl, fptr);\n                serverAssert(vptr != NULL);\n                update = 1;\n\n                /* Delete value */\n                zl = ziplistDelete(zl, &vptr);\n\n                /* Insert new value */\n                zl = ziplistInsert(zl, vptr, (unsigned char*)value,\n                        sdslen(value));\n            }\n        }\n\n        if (!update) {\n            /* Push new field/value pair onto the tail of the ziplist */\n            zl = ziplistPush(zl, (unsigned char*)field, sdslen(field),\n                    ZIPLIST_TAIL);\n            zl = ziplistPush(zl, (unsigned char*)value, sdslen(value),\n                    ZIPLIST_TAIL);\n        }\n        o->ptr = zl;\n\n        /* Check if the ziplist needs to be converted to a hash table */\n        if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictFind(o->ptr,field);\n        if (de) {\n            sdsfree(dictGetVal(de));\n            if (flags & HASH_SET_TAKE_VALUE) {\n                dictGetVal(de) = value;\n                value = NULL;\n            } else {\n                dictGetVal(de) = sdsdup(value);\n            }\n            update = 1;\n        } else {\n            sds f,v;\n            if (flags & HASH_SET_TAKE_FIELD) {\n                f = field;\n                field = NULL;\n            } else {\n                f = sdsdup(field);\n            }\n            if (flags & HASH_SET_TAKE_VALUE) {\n                v = value;\n                value = NULL;\n            } else {\n                v = sdsdup(value);\n            }\n            dictAdd(o->ptr,f,v);\n        }\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n\n    /* Free SDS strings we did not referenced elsewhere if the flags\n     * want this function to be responsible. */\n    if (flags & HASH_SET_TAKE_FIELD && field) sdsfree(field);\n    if (flags & HASH_SET_TAKE_VALUE && value) sdsfree(value);\n    return update;\n}\n\n/* Delete an element from a hash.\n * Return 1 on deleted and 0 on not found. */\nint hashTypeDelete(robj *o, sds field) {\n    int deleted = 0;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr;\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);\n            if (fptr != NULL) {\n                zl = ziplistDelete(zl,&fptr); /* Delete the key. */\n                zl = ziplistDelete(zl,&fptr); /* Delete the value. */\n                o->ptr = zl;\n                deleted = 1;\n            }\n        }\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        if (dictDelete((dict*)o->ptr, field) == C_OK) {\n            deleted = 1;\n\n            /* Always check if the dictionary needs a resize after a delete. */\n            if (htNeedsResize(o->ptr)) dictResize(o->ptr);\n        }\n\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return deleted;\n}\n\n/* Return the number of elements in a hash. */\nunsigned long hashTypeLength(const robj *o) {\n    unsigned long length = ULONG_MAX;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        length = ziplistLen(o->ptr) / 2;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        length = dictSize((const dict*)o->ptr);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return length;\n}\n\nhashTypeIterator *hashTypeInitIterator(robj *subject) {\n    hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));\n    hi->subject = subject;\n    hi->encoding = subject->encoding;\n\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        hi->fptr = NULL;\n        hi->vptr = NULL;\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        hi->di = dictGetIterator(subject->ptr);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return hi;\n}\n\nvoid hashTypeReleaseIterator(hashTypeIterator *hi) {\n    if (hi->encoding == OBJ_ENCODING_HT)\n        dictReleaseIterator(hi->di);\n    zfree(hi);\n}\n\n/* Move to the next entry in the hash. Return C_OK when the next entry\n * could be found and C_ERR when the iterator reaches the end. */\nint hashTypeNext(hashTypeIterator *hi) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl;\n        unsigned char *fptr, *vptr;\n\n        zl = hi->subject->ptr;\n        fptr = hi->fptr;\n        vptr = hi->vptr;\n\n        if (fptr == NULL) {\n            /* Initialize cursor */\n            serverAssert(vptr == NULL);\n            fptr = ziplistIndex(zl, 0);\n        } else {\n            /* Advance cursor */\n            serverAssert(vptr != NULL);\n            fptr = ziplistNext(zl, vptr);\n        }\n        if (fptr == NULL) return C_ERR;\n\n        /* Grab pointer to the value (fptr points to the field) */\n        vptr = ziplistNext(zl, fptr);\n        serverAssert(vptr != NULL);\n\n        /* fptr, vptr now point to the first or next pair */\n        hi->fptr = fptr;\n        hi->vptr = vptr;\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        if ((hi->de = dictNext(hi->di)) == NULL) return C_ERR;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return C_OK;\n}\n\n/* Get the field or value at iterator cursor, for an iterator on a hash value\n * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */\nvoid hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,\n                                unsigned char **vstr,\n                                unsigned int *vlen,\n                                long long *vll)\n{\n    int ret;\n\n    serverAssert(hi->encoding == OBJ_ENCODING_ZIPLIST);\n\n    if (what & OBJ_HASH_KEY) {\n        ret = ziplistGet(hi->fptr, vstr, vlen, vll);\n        serverAssert(ret);\n    } else {\n        ret = ziplistGet(hi->vptr, vstr, vlen, vll);\n        serverAssert(ret);\n    }\n}\n\n/* Get the field or value at iterator cursor, for an iterator on a hash value\n * encoded as a hash table. Prototype is similar to\n * `hashTypeGetFromHashTable`. */\nsds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what) {\n    serverAssert(hi->encoding == OBJ_ENCODING_HT);\n\n    if (what & OBJ_HASH_KEY) {\n        return dictGetKey(hi->de);\n    } else {\n        return dictGetVal(hi->de);\n    }\n}\n\n/* Higher level function of hashTypeCurrent*() that returns the hash value\n * at current iterator position.\n *\n * The returned element is returned by reference in either *vstr and *vlen if\n * it's returned in string form, or stored in *vll if it's returned as\n * a number.\n *\n * If *vll is populated *vstr is set to NULL, so the caller\n * can always check the function return by checking the return value\n * type checking if vstr == NULL. */\nvoid hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        *vstr = NULL;\n        hashTypeCurrentFromZiplist(hi, what, vstr, vlen, vll);\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        sds ele = hashTypeCurrentFromHashTable(hi, what);\n        *vstr = (unsigned char*) ele;\n        *vlen = sdslen(ele);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\n/* Return the key or value at the current iterator position as a new\n * SDS string. */\nsds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vll;\n\n    hashTypeCurrentObject(hi,what,&vstr,&vlen,&vll);\n    if (vstr) return sdsnewlen(vstr,vlen);\n    return sdsfromlonglong(vll);\n}\n\nrobj *hashTypeLookupWriteOrCreate(client *c, robj *key) {\n    robj *o = lookupKeyWrite(c->db,key);\n    if (o == NULL) {\n        o = createHashObject();\n        dbAdd(c->db,key,o);\n    } else {\n        if (o->type != OBJ_HASH) {\n            addReply(c,shared.wrongtypeerr);\n            return NULL;\n        }\n    }\n    return o;\n}\n\nvoid hashTypeConvertZiplist(robj *o, int enc) {\n    serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);\n\n    if (enc == OBJ_ENCODING_ZIPLIST) {\n        /* Nothing to do... */\n\n    } else if (enc == OBJ_ENCODING_HT) {\n        hashTypeIterator *hi;\n        dict *dict;\n        int ret;\n\n        hi = hashTypeInitIterator(o);\n        dict = dictCreate(&hashDictType, NULL);\n\n        while (hashTypeNext(hi) != C_ERR) {\n            sds key, value;\n\n            key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);\n            value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);\n            ret = dictAdd(dict, key, value);\n            if (ret != DICT_OK) {\n                serverLogHexDump(LL_WARNING,\"ziplist with dup elements dump\",\n                    o->ptr,ziplistBlobLen(o->ptr));\n                serverPanic(\"Ziplist corruption detected\");\n            }\n        }\n        hashTypeReleaseIterator(hi);\n        zfree(o->ptr);\n        o->encoding = OBJ_ENCODING_HT;\n        o->ptr = dict;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hashTypeConvert(robj *o, int enc) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        hashTypeConvertZiplist(o, enc);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        serverPanic(\"Not implemented\");\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Hash type commands\n *----------------------------------------------------------------------------*/\n\nvoid hsetnxCommand(client *c) {\n    robj *o;\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    hashTypeTryConversion(o,c->argv,2,3);\n\n    if (hashTypeExists(o, c->argv[2]->ptr)) {\n        addReply(c, shared.czero);\n    } else {\n        hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);\n        addReply(c, shared.cone);\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n        server.dirty++;\n    }\n}\n\nvoid hsetCommand(client *c) {\n    int i, created = 0;\n    robj *o;\n\n    if ((c->argc % 2) == 1) {\n        addReplyError(c,\"wrong number of arguments for HMSET\");\n        return;\n    }\n\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    hashTypeTryConversion(o,c->argv,2,c->argc-1);\n\n    for (i = 2; i < c->argc; i += 2)\n        created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);\n\n    /* HMSET (deprecated) and HSET return value is different. */\n    char *cmdname = c->argv[0]->ptr;\n    if (cmdname[1] == 's' || cmdname[1] == 'S') {\n        /* HSET */\n        addReplyLongLong(c, created);\n    } else {\n        /* HMSET */\n        addReply(c, shared.ok);\n    }\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n    server.dirty++;\n}\n\nvoid hincrbyCommand(client *c) {\n    long long value, incr, oldvalue;\n    robj *o;\n    sds new;\n    unsigned char *vstr;\n    unsigned int vlen;\n\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {\n        if (vstr) {\n            if (string2ll((char*)vstr,vlen,&value) == 0) {\n                addReplyError(c,\"hash value is not an integer\");\n                return;\n            }\n        } /* Else hashTypeGetValue() already stored it into &value */\n    } else {\n        value = 0;\n    }\n\n    oldvalue = value;\n    if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||\n        (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {\n        addReplyError(c,\"increment or decrement would overflow\");\n        return;\n    }\n    value += incr;\n    new = sdsfromlonglong(value);\n    hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);\n    addReplyLongLong(c,value);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hincrby\",c->argv[1],c->db->id);\n    server.dirty++;\n}\n\nvoid hincrbyfloatCommand(client *c) {\n    long double value, incr;\n    long long ll;\n    robj *o;\n    sds new;\n    unsigned char *vstr;\n    unsigned int vlen;\n\n    if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {\n        if (vstr) {\n            if (string2ld((char*)vstr,vlen,&value) == 0) {\n                addReplyError(c,\"hash value is not a float\");\n                return;\n            }\n        } else {\n            value = (long double)ll;\n        }\n    } else {\n        value = 0;\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    char buf[MAX_LONG_DOUBLE_CHARS];\n    int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);\n    new = sdsnewlen(buf,len);\n    hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);\n    addReplyBulkCBuffer(c,buf,len);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hincrbyfloat\",c->argv[1],c->db->id);\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    robj *aux, *newobj;\n    aux = createStringObject(\"HSET\",4);\n    newobj = createRawStringObject(buf,len);\n    rewriteClientCommandArgument(c,0,aux);\n    decrRefCount(aux);\n    rewriteClientCommandArgument(c,3,newobj);\n    decrRefCount(newobj);\n}\n\nstatic void addHashFieldToReply(client *c, robj *o, sds field) {\n    int ret;\n\n    if (o == NULL) {\n        addReplyNull(c);\n        return;\n    }\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);\n        if (ret < 0) {\n            addReplyNull(c);\n        } else {\n            if (vstr) {\n                addReplyBulkCBuffer(c, vstr, vlen);\n            } else {\n                addReplyBulkLongLong(c, vll);\n            }\n        }\n\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        sds value = hashTypeGetFromHashTable(o, field);\n        if (value == NULL)\n            addReplyNull(c);\n        else\n            addReplyBulkCBuffer(c, value, sdslen(value));\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hgetCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    addHashFieldToReply(c, o, c->argv[2]->ptr);\n}\n\nvoid hmgetCommand(client *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    o = lookupKeyRead(c->db, c->argv[1]);\n    if (o != NULL && o->type != OBJ_HASH) {\n        addReply(c, shared.wrongtypeerr);\n        return;\n    }\n\n    addReplyArrayLen(c, c->argc-2);\n    for (i = 2; i < c->argc; i++) {\n        addHashFieldToReply(c, o, c->argv[i]->ptr);\n    }\n}\n\nvoid hdelCommand(client *c) {\n    robj *o;\n    int j, deleted = 0, keyremoved = 0;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        if (hashTypeDelete(o,c->argv[j]->ptr)) {\n            deleted++;\n            if (hashTypeLength(o) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_HASH,\"hdel\",c->argv[1],c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\nvoid hlenCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    addReplyLongLong(c,hashTypeLength(o));\n}\n\nvoid hstrlenCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n    addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]->ptr));\n}\n\nstatic void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n        if (vstr)\n            addReplyBulkCBuffer(c, vstr, vlen);\n        else\n            addReplyBulkLongLong(c, vll);\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        sds value = hashTypeCurrentFromHashTable(hi, what);\n        addReplyBulkCBuffer(c, value, sdslen(value));\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid genericHgetallCommand(client *c, int flags) {\n    robj *o;\n    hashTypeIterator *hi;\n    int length, count = 0;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymap[c->resp]))\n        == NULL || checkType(c,o,OBJ_HASH)) return;\n\n    /* We return a map if the user requested keys and values, like in the\n     * HGETALL case. Otherwise to use a flat array makes more sense. */\n    length = hashTypeLength(o);\n    if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {\n        addReplyMapLen(c, length);\n    } else {\n        addReplyArrayLen(c, length);\n    }\n\n    hi = hashTypeInitIterator(o);\n    while (hashTypeNext(hi) != C_ERR) {\n        if (flags & OBJ_HASH_KEY) {\n            addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);\n            count++;\n        }\n        if (flags & OBJ_HASH_VALUE) {\n            addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);\n            count++;\n        }\n    }\n\n    hashTypeReleaseIterator(hi);\n\n    /* Make sure we returned the right number of elements. */\n    if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= 2;\n    serverAssert(count == length);\n}\n\nvoid hkeysCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_KEY);\n}\n\nvoid hvalsCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_VALUE);\n}\n\nvoid hgetallCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);\n}\n\nvoid hexistsCommand(client *c) {\n    robj *o;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    addReply(c, hashTypeExists(o,c->argv[2]->ptr) ? shared.cone : shared.czero);\n}\n\nvoid hscanCommand(client *c) {\n    robj *o;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n    scanGenericCommand(c,o,cursor);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n\n/*-----------------------------------------------------------------------------\n * List API\n *----------------------------------------------------------------------------*/\n\n/* The function pushes an element to the specified list object 'subject',\n * at head or tail position as specified by 'where'.\n *\n * There is no need for the caller to increment the refcount of 'value' as\n * the function takes care of it if needed. */\nvoid listTypePush(robj *subject, robj *value, int where) {\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        int pos = (where == LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;\n        value = getDecodedObject(value);\n        size_t len = sdslen(value->ptr);\n        quicklistPush(subject->ptr, value->ptr, len, pos);\n        decrRefCount(value);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid *listPopSaver(unsigned char *data, unsigned int sz) {\n    return createStringObject((char*)data,sz);\n}\n\nrobj *listTypePop(robj *subject, int where) {\n    long long vlong;\n    robj *value = NULL;\n\n    int ql_where = where == LIST_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL;\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value,\n                               NULL, &vlong, listPopSaver)) {\n            if (!value)\n                value = createStringObjectFromLongLong(vlong);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return value;\n}\n\nunsigned long listTypeLength(const robj *subject) {\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        return quicklistCount(subject->ptr);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Initialize an iterator at the specified index. */\nlistTypeIterator *listTypeInitIterator(robj *subject, long index,\n                                       unsigned char direction) {\n    listTypeIterator *li = zmalloc(sizeof(listTypeIterator));\n    li->subject = subject;\n    li->encoding = subject->encoding;\n    li->direction = direction;\n    li->iter = NULL;\n    /* LIST_HEAD means start at TAIL and move *towards* head.\n     * LIST_TAIL means start at HEAD and move *towards tail. */\n    int iter_direction =\n        direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD;\n    if (li->encoding == OBJ_ENCODING_QUICKLIST) {\n        li->iter = quicklistGetIteratorAtIdx(li->subject->ptr,\n                                             iter_direction, index);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return li;\n}\n\n/* Clean up the iterator. */\nvoid listTypeReleaseIterator(listTypeIterator *li) {\n    zfree(li->iter);\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. */\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry) {\n    /* Protect from converting when iterating */\n    serverAssert(li->subject->encoding == li->encoding);\n\n    entry->li = li;\n    if (li->encoding == OBJ_ENCODING_QUICKLIST) {\n        return quicklistNext(li->iter, &entry->entry);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return 0;\n}\n\n/* Return entry or NULL at the current position of the iterator. */\nrobj *listTypeGet(listTypeEntry *entry) {\n    robj *value = NULL;\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        if (entry->entry.value) {\n            value = createStringObject((char *)entry->entry.value,\n                                       entry->entry.sz);\n        } else {\n            value = createStringObjectFromLongLong(entry->entry.longval);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return value;\n}\n\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        value = getDecodedObject(value);\n        sds str = value->ptr;\n        size_t len = sdslen(str);\n        if (where == LIST_TAIL) {\n            quicklistInsertAfter((quicklist *)entry->entry.quicklist,\n                                 &entry->entry, str, len);\n        } else if (where == LIST_HEAD) {\n            quicklistInsertBefore((quicklist *)entry->entry.quicklist,\n                                  &entry->entry, str, len);\n        }\n        decrRefCount(value);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Compare the given object with the entry at the current position. */\nint listTypeEqual(listTypeEntry *entry, robj *o) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        serverAssertWithInfo(NULL,o,sdsEncodedObject(o));\n        return quicklistCompare(entry->entry.zi,o->ptr,sdslen(o->ptr));\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Delete the element pointed to. */\nvoid listTypeDelete(listTypeIterator *iter, listTypeEntry *entry) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistDelEntry(iter->iter, &entry->entry);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Create a quicklist from a single ziplist */\nvoid listTypeConvert(robj *subject, int enc) {\n    serverAssertWithInfo(NULL,subject,subject->type==OBJ_LIST);\n    serverAssertWithInfo(NULL,subject,subject->encoding==OBJ_ENCODING_ZIPLIST);\n\n    if (enc == OBJ_ENCODING_QUICKLIST) {\n        size_t zlen = server.list_max_ziplist_size;\n        int depth = server.list_compress_depth;\n        subject->ptr = quicklistCreateFromZiplist(zlen, depth, subject->ptr);\n        subject->encoding = OBJ_ENCODING_QUICKLIST;\n    } else {\n        serverPanic(\"Unsupported list conversion\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * List Commands\n *----------------------------------------------------------------------------*/\n\nvoid pushGenericCommand(client *c, int where) {\n    int j, pushed = 0;\n    robj *lobj = lookupKeyWrite(c->db,c->argv[1]);\n\n    if (lobj && lobj->type != OBJ_LIST) {\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        if (!lobj) {\n            lobj = createQuicklistObject();\n            quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,\n                                server.list_compress_depth);\n            dbAdd(c->db,c->argv[1],lobj);\n        }\n        listTypePush(lobj,c->argv[j],where);\n        pushed++;\n    }\n    addReplyLongLong(c, (lobj ? listTypeLength(lobj) : 0));\n    if (pushed) {\n        char *event = (where == LIST_HEAD) ? \"lpush\" : \"rpush\";\n\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n    }\n    server.dirty += pushed;\n}\n\nvoid lpushCommand(client *c) {\n    pushGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpushCommand(client *c) {\n    pushGenericCommand(c,LIST_TAIL);\n}\n\nvoid pushxGenericCommand(client *c, int where) {\n    int j, pushed = 0;\n    robj *subject;\n\n    if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,subject,OBJ_LIST)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        listTypePush(subject,c->argv[j],where);\n        pushed++;\n    }\n\n    addReplyLongLong(c,listTypeLength(subject));\n\n    if (pushed) {\n        char *event = (where == LIST_HEAD) ? \"lpush\" : \"rpush\";\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n    }\n    server.dirty += pushed;\n}\n\nvoid lpushxCommand(client *c) {\n    pushxGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpushxCommand(client *c) {\n    pushxGenericCommand(c,LIST_TAIL);\n}\n\nvoid linsertCommand(client *c) {\n    int where;\n    robj *subject;\n    listTypeIterator *iter;\n    listTypeEntry entry;\n    int inserted = 0;\n\n    if (strcasecmp(c->argv[2]->ptr,\"after\") == 0) {\n        where = LIST_TAIL;\n    } else if (strcasecmp(c->argv[2]->ptr,\"before\") == 0) {\n        where = LIST_HEAD;\n    } else {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,subject,OBJ_LIST)) return;\n\n    /* Seek pivot from head to tail */\n    iter = listTypeInitIterator(subject,0,LIST_TAIL);\n    while (listTypeNext(iter,&entry)) {\n        if (listTypeEqual(&entry,c->argv[3])) {\n            listTypeInsert(&entry,c->argv[4],where);\n            inserted = 1;\n            break;\n        }\n    }\n    listTypeReleaseIterator(iter);\n\n    if (inserted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,\"linsert\",\n                            c->argv[1],c->db->id);\n        server.dirty++;\n    } else {\n        /* Notify client of a failed insert */\n        addReplyLongLong(c,-1);\n        return;\n    }\n\n    addReplyLongLong(c,listTypeLength(subject));\n}\n\nvoid llenCommand(client *c) {\n    robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n    addReplyLongLong(c,listTypeLength(o));\n}\n\nvoid lindexCommand(client *c) {\n    robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n    long index;\n    robj *value = NULL;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))\n        return;\n\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistEntry entry;\n        if (quicklistIndex(o->ptr, index, &entry)) {\n            if (entry.value) {\n                value = createStringObject((char*)entry.value,entry.sz);\n            } else {\n                value = createStringObjectFromLongLong(entry.longval);\n            }\n            addReplyBulk(c,value);\n            decrRefCount(value);\n        } else {\n            addReplyNull(c);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid lsetCommand(client *c) {\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n    long index;\n    robj *value = c->argv[3];\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))\n        return;\n\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklist *ql = o->ptr;\n        int replaced = quicklistReplaceAtIndex(ql, index,\n                                               value->ptr, sdslen(value->ptr));\n        if (!replaced) {\n            addReply(c,shared.outofrangeerr);\n        } else {\n            addReply(c,shared.ok);\n            signalModifiedKey(c,c->db,c->argv[1]);\n            notifyKeyspaceEvent(NOTIFY_LIST,\"lset\",c->argv[1],c->db->id);\n            server.dirty++;\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid popGenericCommand(client *c, int where) {\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp]);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n\n    robj *value = listTypePop(o,where);\n    if (value == NULL) {\n        addReplyNull(c);\n    } else {\n        char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n\n        addReplyBulk(c,value);\n        decrRefCount(value);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n        if (listTypeLength(o) == 0) {\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                c->argv[1],c->db->id);\n            dbDelete(c->db,c->argv[1]);\n        }\n        signalModifiedKey(c,c->db,c->argv[1]);\n        server.dirty++;\n    }\n}\n\nvoid lpopCommand(client *c) {\n    popGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpopCommand(client *c) {\n    popGenericCommand(c,LIST_TAIL);\n}\n\nvoid lrangeCommand(client *c) {\n    robj *o;\n    long start, end, llen, rangelen;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == NULL\n         || checkType(c,o,OBJ_LIST)) return;\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        addReply(c,shared.emptyarray);\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    addReplyArrayLen(c,rangelen);\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        listTypeIterator *iter = listTypeInitIterator(o, start, LIST_TAIL);\n\n        while(rangelen--) {\n            listTypeEntry entry;\n            listTypeNext(iter, &entry);\n            quicklistEntry *qe = &entry.entry;\n            if (qe->value) {\n                addReplyBulkCBuffer(c,qe->value,qe->sz);\n            } else {\n                addReplyBulkLongLong(c,qe->longval);\n            }\n        }\n        listTypeReleaseIterator(iter);\n    } else {\n        serverPanic(\"List encoding is not QUICKLIST!\");\n    }\n}\n\nvoid ltrimCommand(client *c) {\n    robj *o;\n    long start, end, llen, ltrim, rtrim;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||\n        checkType(c,o,OBJ_LIST)) return;\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        /* Out of range start or start > end result in empty list */\n        ltrim = llen;\n        rtrim = 0;\n    } else {\n        if (end >= llen) end = llen-1;\n        ltrim = start;\n        rtrim = llen-end-1;\n    }\n\n    /* Remove list elements to perform the trim */\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistDelRange(o->ptr,0,ltrim);\n        quicklistDelRange(o->ptr,-rtrim,rtrim);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n\n    notifyKeyspaceEvent(NOTIFY_LIST,\"ltrim\",c->argv[1],c->db->id);\n    if (listTypeLength(o) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n    signalModifiedKey(c,c->db,c->argv[1]);\n    server.dirty++;\n    addReply(c,shared.ok);\n}\n\nvoid lremCommand(client *c) {\n    robj *subject, *obj;\n    obj = c->argv[3];\n    long toremove;\n    long removed = 0;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != C_OK))\n        return;\n\n    subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);\n    if (subject == NULL || checkType(c,subject,OBJ_LIST)) return;\n\n    listTypeIterator *li;\n    if (toremove < 0) {\n        toremove = -toremove;\n        li = listTypeInitIterator(subject,-1,LIST_HEAD);\n    } else {\n        li = listTypeInitIterator(subject,0,LIST_TAIL);\n    }\n\n    listTypeEntry entry;\n    while (listTypeNext(li,&entry)) {\n        if (listTypeEqual(&entry,obj)) {\n            listTypeDelete(li, &entry);\n            server.dirty++;\n            removed++;\n            if (toremove && removed == toremove) break;\n        }\n    }\n    listTypeReleaseIterator(li);\n\n    if (removed) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,\"lrem\",c->argv[1],c->db->id);\n    }\n\n    if (listTypeLength(subject) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    addReplyLongLong(c,removed);\n}\n\n/* This is the semantic of this command:\n *  RPOPLPUSH srclist dstlist:\n *    IF LLEN(srclist) > 0\n *      element = RPOP srclist\n *      LPUSH dstlist element\n *      RETURN element\n *    ELSE\n *      RETURN nil\n *    END\n *  END\n *\n * The idea is to be able to get an element from a list in a reliable way\n * since the element is not just returned but pushed against another list\n * as well. This command was originally proposed by Ezra Zygmuntowicz.\n */\n\nvoid rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value) {\n    /* Create the list if the key does not exist */\n    if (!dstobj) {\n        dstobj = createQuicklistObject();\n        quicklistSetOptions(dstobj->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n        dbAdd(c->db,dstkey,dstobj);\n    }\n    signalModifiedKey(c,c->db,dstkey);\n    listTypePush(dstobj,value,LIST_HEAD);\n    notifyKeyspaceEvent(NOTIFY_LIST,\"lpush\",dstkey,c->db->id);\n    /* Always send the pushed value to the client. */\n    addReplyBulk(c,value);\n}\n\nvoid rpoplpushCommand(client *c) {\n    robj *sobj, *value;\n    if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp]))\n        == NULL || checkType(c,sobj,OBJ_LIST)) return;\n\n    if (listTypeLength(sobj) == 0) {\n        /* This may only happen after loading very old RDB files. Recent\n         * versions of Redis delete keys of empty lists. */\n        addReplyNull(c);\n    } else {\n        robj *dobj = lookupKeyWrite(c->db,c->argv[2]);\n        robj *touchedkey = c->argv[1];\n\n        if (dobj && checkType(c,dobj,OBJ_LIST)) return;\n        value = listTypePop(sobj,LIST_TAIL);\n        /* We saved touched key, and protect it, since rpoplpushHandlePush\n         * may change the client command argument vector (it does not\n         * currently). */\n        incrRefCount(touchedkey);\n        rpoplpushHandlePush(c,c->argv[2],dobj,value);\n\n        /* listTypePop returns an object with its refcount incremented */\n        decrRefCount(value);\n\n        /* Delete the source list when it is empty */\n        notifyKeyspaceEvent(NOTIFY_LIST,\"rpop\",touchedkey,c->db->id);\n        if (listTypeLength(sobj) == 0) {\n            dbDelete(c->db,touchedkey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                touchedkey,c->db->id);\n        }\n        signalModifiedKey(c,c->db,touchedkey);\n        decrRefCount(touchedkey);\n        server.dirty++;\n        if (c->cmd->proc == brpoplpushCommand) {\n            rewriteClientCommandVector(c,3,shared.rpoplpush,c->argv[1],c->argv[2]);\n        }\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Blocking POP operations\n *----------------------------------------------------------------------------*/\n\n/* This is a helper function for handleClientsBlockedOnKeys(). It's work\n * is to serve a specific client (receiver) that is blocked on 'key'\n * in the context of the specified 'db', doing the following:\n *\n * 1) Provide the client with the 'value' element.\n * 2) If the dstkey is not NULL (we are serving a BRPOPLPUSH) also push the\n *    'value' element on the destination list (the LPUSH side of the command).\n * 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into\n *    the AOF and replication channel.\n *\n * The argument 'where' is LIST_TAIL or LIST_HEAD, and indicates if the\n * 'value' element was popped from the head (BLPOP) or tail (BRPOP) so that\n * we can propagate the command properly.\n *\n * The function returns C_OK if we are able to serve the client, otherwise\n * C_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. */\nint serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where)\n{\n    robj *argv[3];\n\n    if (dstkey == NULL) {\n        /* Propagate the [LR]POP operation. */\n        argv[0] = (where == LIST_HEAD) ? shared.lpop :\n                                          shared.rpop;\n        argv[1] = key;\n        propagate((where == LIST_HEAD) ?\n            server.lpopCommand : server.rpopCommand,\n            db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL);\n\n        /* BRPOP/BLPOP */\n        addReplyArrayLen(receiver,2);\n        addReplyBulk(receiver,key);\n        addReplyBulk(receiver,value);\n\n        /* Notify event. */\n        char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n        notifyKeyspaceEvent(NOTIFY_LIST,event,key,receiver->db->id);\n    } else {\n        /* BRPOPLPUSH */\n        robj *dstobj =\n            lookupKeyWrite(receiver->db,dstkey);\n        if (!(dstobj &&\n             checkType(receiver,dstobj,OBJ_LIST)))\n        {\n            rpoplpushHandlePush(receiver,dstkey,dstobj,\n                value);\n            /* Propagate the RPOPLPUSH operation. */\n            argv[0] = shared.rpoplpush;\n            argv[1] = key;\n            argv[2] = dstkey;\n            propagate(server.rpoplpushCommand,\n                db->id,argv,3,\n                PROPAGATE_AOF|\n                PROPAGATE_REPL);\n\n            /* Notify event (\"lpush\" was notified by rpoplpushHandlePush). */\n            notifyKeyspaceEvent(NOTIFY_LIST,\"rpop\",key,receiver->db->id);\n        } else {\n            /* BRPOPLPUSH failed because of wrong\n             * destination type. */\n            return C_ERR;\n        }\n    }\n    return C_OK;\n}\n\n/* Blocking RPOP/LPOP */\nvoid blockingPopGenericCommand(client *c, int where) {\n    robj *o;\n    mstime_t timeout;\n    int j;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)\n        != C_OK) return;\n\n    for (j = 1; j < c->argc-1; j++) {\n        o = lookupKeyWrite(c->db,c->argv[j]);\n        if (o != NULL) {\n            if (o->type != OBJ_LIST) {\n                addReply(c,shared.wrongtypeerr);\n                return;\n            } else {\n                if (listTypeLength(o) != 0) {\n                    /* Non empty list, this is like a non normal [LR]POP. */\n                    char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n                    robj *value = listTypePop(o,where);\n                    serverAssert(value != NULL);\n\n                    addReplyArrayLen(c,2);\n                    addReplyBulk(c,c->argv[j]);\n                    addReplyBulk(c,value);\n                    decrRefCount(value);\n                    notifyKeyspaceEvent(NOTIFY_LIST,event,\n                                        c->argv[j],c->db->id);\n                    if (listTypeLength(o) == 0) {\n                        dbDelete(c->db,c->argv[j]);\n                        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                            c->argv[j],c->db->id);\n                    }\n                    signalModifiedKey(c,c->db,c->argv[j]);\n                    server.dirty++;\n\n                    /* Replicate it as an [LR]POP instead of B[LR]POP. */\n                    rewriteClientCommandVector(c,2,\n                        (where == LIST_HEAD) ? shared.lpop : shared.rpop,\n                        c->argv[j]);\n                    return;\n                }\n            }\n        }\n    }\n\n    /* If we are inside a MULTI/EXEC and the list is empty the only thing\n     * we can do is treating it as a timeout (even with timeout 0). */\n    if (c->flags & CLIENT_MULTI) {\n        addReplyNullArray(c);\n        return;\n    }\n\n    /* If the list is empty or the key does not exists we must block */\n    blockForKeys(c,BLOCKED_LIST,c->argv + 1,c->argc - 2,timeout,NULL,NULL);\n}\n\nvoid blpopCommand(client *c) {\n    blockingPopGenericCommand(c,LIST_HEAD);\n}\n\nvoid brpopCommand(client *c) {\n    blockingPopGenericCommand(c,LIST_TAIL);\n}\n\nvoid brpoplpushCommand(client *c) {\n    mstime_t timeout;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout,UNIT_SECONDS)\n        != C_OK) return;\n\n    robj *key = lookupKeyWrite(c->db, c->argv[1]);\n\n    if (key == NULL) {\n        if (c->flags & CLIENT_MULTI) {\n            /* Blocking against an empty list in a multi state\n             * returns immediately. */\n            addReplyNull(c);\n        } else {\n            /* The list is empty and the client blocks. */\n            blockForKeys(c,BLOCKED_LIST,c->argv + 1,1,timeout,c->argv[2],NULL);\n        }\n    } else {\n        if (key->type != OBJ_LIST) {\n            addReply(c, shared.wrongtypeerr);\n        } else {\n            /* The list exists and has elements, so\n             * the regular rpoplpushCommand is executed. */\n            serverAssertWithInfo(c,key,listTypeLength(key) > 0);\n            rpoplpushCommand(c);\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n\n/*-----------------------------------------------------------------------------\n * Set Commands\n *----------------------------------------------------------------------------*/\n\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,\n                              robj *dstkey, int op);\n\n/* Factory method to return a set that *can* hold \"value\". When the object has\n * an integer-encodable value, an intset will be returned. Otherwise a regular\n * hash table. */\nrobj *setTypeCreate(sds value) {\n    if (isSdsRepresentableAsLongLong(value,NULL) == C_OK)\n        return createIntsetObject();\n    return createSetObject();\n}\n\n/* Add the specified value into a set.\n *\n * If the value was already member of the set, nothing is done and 0 is\n * returned, otherwise the new element is added and 1 is returned. */\nint setTypeAdd(robj *subject, sds value) {\n    long long llval;\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        dict *ht = subject->ptr;\n        dictEntry *de = dictAddRaw(ht,value,NULL);\n        if (de) {\n            dictSetKey(ht,de,sdsdup(value));\n            dictSetVal(ht,de,NULL);\n            return 1;\n        }\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {\n            uint8_t success = 0;\n            subject->ptr = intsetAdd(subject->ptr,llval,&success);\n            if (success) {\n                /* Convert to regular set when the intset contains\n                 * too many entries. */\n                if (intsetLen(subject->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(subject,OBJ_ENCODING_HT);\n                return 1;\n            }\n        } else {\n            /* Failed to get integer from object, convert to regular set. */\n            setTypeConvert(subject,OBJ_ENCODING_HT);\n\n            /* The set *was* an intset and this value is not integer\n             * encodable, so dictAdd should always work. */\n            serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nint setTypeRemove(robj *setobj, sds value) {\n    long long llval;\n    if (setobj->encoding == OBJ_ENCODING_HT) {\n        if (dictDelete(setobj->ptr,value) == DICT_OK) {\n            if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);\n            return 1;\n        }\n    } else if (setobj->encoding == OBJ_ENCODING_INTSET) {\n        if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {\n            int success;\n            setobj->ptr = intsetRemove(setobj->ptr,llval,&success);\n            if (success) return 1;\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nint setTypeIsMember(robj *subject, sds value) {\n    long long llval;\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        return dictFind((dict*)subject->ptr,value) != NULL;\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {\n            return intsetFind((intset*)subject->ptr,llval);\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nsetTypeIterator *setTypeInitIterator(robj *subject) {\n    setTypeIterator *si = zmalloc(sizeof(setTypeIterator));\n    si->subject = subject;\n    si->encoding = subject->encoding;\n    if (si->encoding == OBJ_ENCODING_HT) {\n        si->di = dictGetIterator(subject->ptr);\n    } else if (si->encoding == OBJ_ENCODING_INTSET) {\n        si->ii = 0;\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return si;\n}\n\nvoid setTypeReleaseIterator(setTypeIterator *si) {\n    if (si->encoding == OBJ_ENCODING_HT)\n        dictReleaseIterator(si->di);\n    zfree(si);\n}\n\n/* Move to the next entry in the set. Returns the object at the current\n * position.\n *\n * Since set elements can be internally be stored as SDS strings 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 * (sdsele) or (llele) accordingly.\n *\n * Note that both the sdsele and llele pointers should be passed and cannot\n * be NULL since the function will try to defensively populate the non\n * used field with values which are easy to trap if misused.\n *\n * When there are no longer elements -1 is returned. */\nint setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele) {\n    if (si->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictNext(si->di);\n        if (de == NULL) return -1;\n        *sdsele = dictGetKey(de);\n        *llele = -123456789; /* Not needed. Defensive. */\n    } else if (si->encoding == OBJ_ENCODING_INTSET) {\n        if (!intsetGet(si->subject->ptr,si->ii++,llele))\n            return -1;\n        *sdsele = NULL; /* Not needed. Defensive. */\n    } else {\n        serverPanic(\"Wrong set encoding in setTypeNext\");\n    }\n    return si->encoding;\n}\n\n/* The not copy on write friendly version but easy to use version\n * of setTypeNext() is setTypeNextObject(), returning new SDS\n * strings. So if you don't retain a pointer to this object you should call\n * sdsfree() against it.\n *\n * This function is the way to go for write operations where COW is not\n * an issue. */\nsds setTypeNextObject(setTypeIterator *si) {\n    int64_t intele;\n    sds sdsele;\n    int encoding;\n\n    encoding = setTypeNext(si,&sdsele,&intele);\n    switch(encoding) {\n        case -1:    return NULL;\n        case OBJ_ENCODING_INTSET:\n            return sdsfromlonglong(intele);\n        case OBJ_ENCODING_HT:\n            return sdsdup(sdsele);\n        default:\n            serverPanic(\"Unsupported encoding\");\n    }\n    return NULL; /* just to suppress warnings */\n}\n\n/* Return random element from a non empty set.\n * The returned element can be a int64_t value if the set is encoded\n * as an \"intset\" blob of integers, or an SDS string if the set\n * is a regular set.\n *\n * The caller provides both pointers to be populated with the right\n * object. The return value of the function is the object->encoding\n * field of the object and is used by the caller to check if the\n * int64_t pointer or the redis object pointer was populated.\n *\n * Note that both the sdsele and llele pointers should be passed and cannot\n * be NULL since the function will try to defensively populate the non\n * used field with values which are easy to trap if misused. */\nint setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele) {\n    if (setobj->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictGetFairRandomKey(setobj->ptr);\n        *sdsele = dictGetKey(de);\n        *llele = -123456789; /* Not needed. Defensive. */\n    } else if (setobj->encoding == OBJ_ENCODING_INTSET) {\n        *llele = intsetRandom(setobj->ptr);\n        *sdsele = NULL; /* Not needed. Defensive. */\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return setobj->encoding;\n}\n\nunsigned long setTypeSize(const robj *subject) {\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        return dictSize((const dict*)subject->ptr);\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        return intsetLen((const intset*)subject->ptr);\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n}\n\n/* Convert the set to specified encoding. The resulting dict (when converting\n * to a hash table) is presized to hold the number of elements in the original\n * set. */\nvoid setTypeConvert(robj *setobj, int enc) {\n    setTypeIterator *si;\n    serverAssertWithInfo(NULL,setobj,setobj->type == OBJ_SET &&\n                             setobj->encoding == OBJ_ENCODING_INTSET);\n\n    if (enc == OBJ_ENCODING_HT) {\n        int64_t intele;\n        dict *d = dictCreate(&setDictType,NULL);\n        sds element;\n\n        /* Presize the dict to avoid rehashing */\n        dictExpand(d,intsetLen(setobj->ptr));\n\n        /* To add the elements we extract integers and create redis objects */\n        si = setTypeInitIterator(setobj);\n        while (setTypeNext(si,&element,&intele) != -1) {\n            element = sdsfromlonglong(intele);\n            serverAssert(dictAdd(d,element,NULL) == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n\n        setobj->encoding = OBJ_ENCODING_HT;\n        zfree(setobj->ptr);\n        setobj->ptr = d;\n    } else {\n        serverPanic(\"Unsupported set conversion\");\n    }\n}\n\nvoid saddCommand(client *c) {\n    robj *set;\n    int j, added = 0;\n\n    set = lookupKeyWrite(c->db,c->argv[1]);\n    if (set == NULL) {\n        set = setTypeCreate(c->argv[2]->ptr);\n        dbAdd(c->db,c->argv[1],set);\n    } else {\n        if (set->type != OBJ_SET) {\n            addReply(c,shared.wrongtypeerr);\n            return;\n        }\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        if (setTypeAdd(set,c->argv[j]->ptr)) added++;\n    }\n    if (added) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_SET,\"sadd\",c->argv[1],c->db->id);\n    }\n    server.dirty += added;\n    addReplyLongLong(c,added);\n}\n\nvoid sremCommand(client *c) {\n    robj *set;\n    int j, deleted = 0, keyremoved = 0;\n\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        if (setTypeRemove(set,c->argv[j]->ptr)) {\n            deleted++;\n            if (setTypeSize(set) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\nvoid smoveCommand(client *c) {\n    robj *srcset, *dstset, *ele;\n    srcset = lookupKeyWrite(c->db,c->argv[1]);\n    dstset = lookupKeyWrite(c->db,c->argv[2]);\n    ele = c->argv[3];\n\n    /* If the source key does not exist return 0 */\n    if (srcset == NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* If the source key has the wrong type, or the destination key\n     * is set and has the wrong type, return with an error. */\n    if (checkType(c,srcset,OBJ_SET) ||\n        (dstset && checkType(c,dstset,OBJ_SET))) return;\n\n    /* If srcset and dstset are equal, SMOVE is a no-op */\n    if (srcset == dstset) {\n        addReply(c,setTypeIsMember(srcset,ele->ptr) ?\n            shared.cone : shared.czero);\n        return;\n    }\n\n    /* If the element cannot be removed from the src set, return 0. */\n    if (!setTypeRemove(srcset,ele->ptr)) {\n        addReply(c,shared.czero);\n        return;\n    }\n    notifyKeyspaceEvent(NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n\n    /* Remove the src set from the database when empty */\n    if (setTypeSize(srcset) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    /* Create the destination set when it doesn't exist */\n    if (!dstset) {\n        dstset = setTypeCreate(ele->ptr);\n        dbAdd(c->db,c->argv[2],dstset);\n    }\n\n    signalModifiedKey(c,c->db,c->argv[1]);\n    signalModifiedKey(c,c->db,c->argv[2]);\n    server.dirty++;\n\n    /* An extra key has changed when ele was successfully added to dstset */\n    if (setTypeAdd(dstset,ele->ptr)) {\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_SET,\"sadd\",c->argv[2],c->db->id);\n    }\n    addReply(c,shared.cone);\n}\n\nvoid sismemberCommand(client *c) {\n    robj *set;\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n\n    if (setTypeIsMember(set,c->argv[2]->ptr))\n        addReply(c,shared.cone);\n    else\n        addReply(c,shared.czero);\n}\n\nvoid scardCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_SET)) return;\n\n    addReplyLongLong(c,setTypeSize(o));\n}\n\n/* Handle the \"SPOP key <count>\" variant. The normal version of the\n * command is handled by the spopCommand() function itself. */\n\n/* How many times bigger should be the set compared to the remaining size\n * for us to use the \"create new set\" strategy? Read later in the\n * implementation for more info. */\n#define SPOP_MOVE_STRATEGY_MUL 5\n\nvoid spopWithCountCommand(client *c) {\n    long l;\n    unsigned long count, size;\n    robj *set;\n\n    /* Get the count argument */\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;\n    if (l >= 0) {\n        count = (unsigned long) l;\n    } else {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n\n    /* Make sure a key with the name inputted exists, and that it's type is\n     * indeed a set. Otherwise, return nil */\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.emptyset[c->resp]))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n\n    /* If count is zero, serve an empty set ASAP to avoid special\n     * cases later. */\n    if (count == 0) {\n        addReply(c,shared.emptyset[c->resp]);\n        return;\n    }\n\n    size = setTypeSize(set);\n\n    /* Generate an SPOP keyspace notification */\n    notifyKeyspaceEvent(NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n    server.dirty += count;\n\n    /* CASE 1:\n     * The number of requested elements is greater than or equal to\n     * the number of elements inside the set: simply return the whole set. */\n    if (count >= size) {\n        /* We just return the entire set */\n        sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION);\n\n        /* Delete the set as it is now empty */\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n\n        /* Propagate this command as an DEL operation */\n        rewriteClientCommandVector(c,2,shared.del,c->argv[1]);\n        signalModifiedKey(c,c->db,c->argv[1]);\n        server.dirty++;\n        return;\n    }\n\n    /* Case 2 and 3 require to replicate SPOP as a set of SREM commands.\n     * Prepare our replication argument vector. Also send the array length\n     * which is common to both the code paths. */\n    robj *propargv[3];\n    propargv[0] = createStringObject(\"SREM\",4);\n    propargv[1] = c->argv[1];\n    addReplySetLen(c,count);\n\n    /* Common iteration vars. */\n    sds sdsele;\n    robj *objele;\n    int encoding;\n    int64_t llele;\n    unsigned long remaining = size-count; /* Elements left after SPOP. */\n\n    /* If we are here, the number of requested elements is less than the\n     * number of elements inside the set. Also we are sure that count < size.\n     * Use two different strategies.\n     *\n     * CASE 2: The number of elements to return is small compared to the\n     * set size. We can just extract random elements and return them to\n     * the set. */\n    if (remaining*SPOP_MOVE_STRATEGY_MUL > count) {\n        while(count--) {\n            /* Emit and remove. */\n            encoding = setTypeRandomElement(set,&sdsele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n                objele = createStringObjectFromLongLong(llele);\n                set->ptr = intsetRemove(set->ptr,llele,NULL);\n            } else {\n                addReplyBulkCBuffer(c,sdsele,sdslen(sdsele));\n                objele = createStringObject(sdsele,sdslen(sdsele));\n                setTypeRemove(set,sdsele);\n            }\n\n            /* Replicate/AOF this command as an SREM operation */\n            propargv[2] = objele;\n            alsoPropagate(server.sremCommand,c->db->id,propargv,3,\n                PROPAGATE_AOF|PROPAGATE_REPL);\n            decrRefCount(objele);\n        }\n    } else {\n    /* CASE 3: The number of elements to return is very big, approaching\n     * the size of the set itself. After some time extracting random elements\n     * from such a set becomes computationally expensive, so we use\n     * a different strategy, we extract random elements that we don't\n     * want to return (the elements that will remain part of the set),\n     * creating a new set as we do this (that will be stored as the original\n     * set). Then we return the elements left in the original set and\n     * release it. */\n        robj *newset = NULL;\n\n        /* Create a new set with just the remaining elements. */\n        while(remaining--) {\n            encoding = setTypeRandomElement(set,&sdsele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                sdsele = sdsfromlonglong(llele);\n            } else {\n                sdsele = sdsdup(sdsele);\n            }\n            if (!newset) newset = setTypeCreate(sdsele);\n            setTypeAdd(newset,sdsele);\n            setTypeRemove(set,sdsele);\n            sdsfree(sdsele);\n        }\n\n        /* Transfer the old set to the client. */\n        setTypeIterator *si;\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&sdsele,&llele)) != -1) {\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n                objele = createStringObjectFromLongLong(llele);\n            } else {\n                addReplyBulkCBuffer(c,sdsele,sdslen(sdsele));\n                objele = createStringObject(sdsele,sdslen(sdsele));\n            }\n\n            /* Replicate/AOF this command as an SREM operation */\n            propargv[2] = objele;\n            alsoPropagate(server.sremCommand,c->db->id,propargv,3,\n                PROPAGATE_AOF|PROPAGATE_REPL);\n            decrRefCount(objele);\n        }\n        setTypeReleaseIterator(si);\n\n        /* Assign the new set as the key value. */\n        dbOverwrite(c->db,c->argv[1],newset);\n    }\n\n    /* Don't propagate the command itself even if we incremented the\n     * dirty counter. We don't want to propagate an SPOP command since\n     * we propagated the command as a set of SREMs operations using\n     * the alsoPropagate() API. */\n    decrRefCount(propargv[0]);\n    preventCommandPropagation(c);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    server.dirty++;\n}\n\nvoid spopCommand(client *c) {\n    robj *set, *ele, *aux;\n    sds sdsele;\n    int64_t llele;\n    int encoding;\n\n    if (c->argc == 3) {\n        spopWithCountCommand(c);\n        return;\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Make sure a key with the name inputted exists, and that it's type is\n     * indeed a set */\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp]))\n         == NULL || checkType(c,set,OBJ_SET)) return;\n\n    /* Get a random element from the set */\n    encoding = setTypeRandomElement(set,&sdsele,&llele);\n\n    /* Remove the element from the set */\n    if (encoding == OBJ_ENCODING_INTSET) {\n        ele = createStringObjectFromLongLong(llele);\n        set->ptr = intsetRemove(set->ptr,llele,NULL);\n    } else {\n        ele = createStringObject(sdsele,sdslen(sdsele));\n        setTypeRemove(set,ele->ptr);\n    }\n\n    notifyKeyspaceEvent(NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n\n    /* Replicate/AOF this command as an SREM operation */\n    aux = createStringObject(\"SREM\",4);\n    rewriteClientCommandVector(c,3,aux,c->argv[1],ele);\n    decrRefCount(aux);\n\n    /* Add the element to the reply */\n    addReplyBulk(c,ele);\n    decrRefCount(ele);\n\n    /* Delete the set if it's empty */\n    if (setTypeSize(set) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    /* Set has been modified */\n    signalModifiedKey(c,c->db,c->argv[1]);\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/* How many times bigger should be the set compared to the requested size\n * for us to don't use the \"remove elements\" strategy? Read later in the\n * implementation for more info. */\n#define SRANDMEMBER_SUB_STRATEGY_MUL 3\n\nvoid srandmemberWithCountCommand(client *c) {\n    long l;\n    unsigned long count, size;\n    int uniq = 1;\n    robj *set;\n    sds ele;\n    int64_t llele;\n    int encoding;\n\n    dict *d;\n\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;\n    if (l >= 0) {\n        count = (unsigned long) l;\n    } else {\n        /* A negative count means: return the same elements multiple times\n         * (i.e. don't remove the extracted element after every extraction). */\n        count = -l;\n        uniq = 0;\n    }\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyset[c->resp]))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n    size = setTypeSize(set);\n\n    /* If count is zero, serve it ASAP to avoid special cases later. */\n    if (count == 0) {\n        addReply(c,shared.emptyset[c->resp]);\n        return;\n    }\n\n    /* CASE 1: The count was negative, so the extraction method is just:\n     * \"return N random elements\" sampling the whole set every time.\n     * This case is trivial and can be served without auxiliary data\n     * structures. */\n    if (!uniq) {\n        addReplySetLen(c,count);\n        while(count--) {\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n            } else {\n                addReplyBulkCBuffer(c,ele,sdslen(ele));\n            }\n        }\n        return;\n    }\n\n    /* CASE 2:\n     * The number of requested elements is greater than the number of\n     * elements inside the set: simply return the whole set. */\n    if (count >= size) {\n        sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION);\n        return;\n    }\n\n    /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */\n    d = dictCreate(&objectKeyPointerValueDictType,NULL);\n\n    /* CASE 3:\n     * The number of elements inside the set is not greater than\n     * SRANDMEMBER_SUB_STRATEGY_MUL times the number of requested elements.\n     * In this case we create a set from scratch with all the elements, and\n     * subtract random elements to reach the requested number of elements.\n     *\n     * This is done because if the number of requsted elements is just\n     * a bit less than the number of elements in the set, the natural approach\n     * used into CASE 3 is highly inefficient. */\n    if (count*SRANDMEMBER_SUB_STRATEGY_MUL > size) {\n        setTypeIterator *si;\n\n        /* Add all the elements into the temporary dictionary. */\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&ele,&llele)) != -1) {\n            int retval = DICT_ERR;\n\n            if (encoding == OBJ_ENCODING_INTSET) {\n                retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);\n            } else {\n                retval = dictAdd(d,createStringObject(ele,sdslen(ele)),NULL);\n            }\n            serverAssert(retval == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n        serverAssert(dictSize(d) == size);\n\n        /* Remove random elements to reach the right count. */\n        while(size > count) {\n            dictEntry *de;\n\n            de = dictGetRandomKey(d);\n            dictDelete(d,dictGetKey(de));\n            size--;\n        }\n    }\n\n    /* CASE 4: We have a big set compared to the requested number of elements.\n     * In this case we can simply get random elements from the set and add\n     * to the temporary set, trying to eventually get enough unique elements\n     * to reach the specified count. */\n    else {\n        unsigned long added = 0;\n        robj *objele;\n\n        while(added < count) {\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                objele = createStringObjectFromLongLong(llele);\n            } else {\n                objele = createStringObject(ele,sdslen(ele));\n            }\n            /* Try to add the object to the dictionary. If it already exists\n             * free it, otherwise increment the number of objects we have\n             * in the result dictionary. */\n            if (dictAdd(d,objele,NULL) == DICT_OK)\n                added++;\n            else\n                decrRefCount(objele);\n        }\n    }\n\n    /* CASE 3 & 4: send the result to the user. */\n    {\n        dictIterator *di;\n        dictEntry *de;\n\n        addReplySetLen(c,count);\n        di = dictGetIterator(d);\n        while((de = dictNext(di)) != NULL)\n            addReplyBulk(c,dictGetKey(de));\n        dictReleaseIterator(di);\n        dictRelease(d);\n    }\n}\n\nvoid srandmemberCommand(client *c) {\n    robj *set;\n    sds ele;\n    int64_t llele;\n    int encoding;\n\n    if (c->argc == 3) {\n        srandmemberWithCountCommand(c);\n        return;\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n\n    encoding = setTypeRandomElement(set,&ele,&llele);\n    if (encoding == OBJ_ENCODING_INTSET) {\n        addReplyBulkLongLong(c,llele);\n    } else {\n        addReplyBulkCBuffer(c,ele,sdslen(ele));\n    }\n}\n\nint qsortCompareSetsByCardinality(const void *s1, const void *s2) {\n    if (setTypeSize(*(robj**)s1) > setTypeSize(*(robj**)s2)) return 1;\n    if (setTypeSize(*(robj**)s1) < setTypeSize(*(robj**)s2)) return -1;\n    return 0;\n}\n\n/* This is used by SDIFF and in this case we can receive NULL that should\n * be handled as empty sets. */\nint qsortCompareSetsByRevCardinality(const void *s1, const void *s2) {\n    robj *o1 = *(robj**)s1, *o2 = *(robj**)s2;\n    unsigned long first = o1 ? setTypeSize(o1) : 0;\n    unsigned long second = o2 ? setTypeSize(o2) : 0;\n\n    if (first < second) return 1;\n    if (first > second) return -1;\n    return 0;\n}\n\nvoid sinterGenericCommand(client *c, robj **setkeys,\n                          unsigned long setnum, robj *dstkey) {\n    robj **sets = zmalloc(sizeof(robj*)*setnum);\n    setTypeIterator *si;\n    robj *dstset = NULL;\n    sds elesds;\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        robj *setobj = dstkey ?\n            lookupKeyWrite(c->db,setkeys[j]) :\n            lookupKeyRead(c->db,setkeys[j]);\n        if (!setobj) {\n            zfree(sets);\n            if (dstkey) {\n                if (dbDelete(c->db,dstkey)) {\n                    signalModifiedKey(c,c->db,dstkey);\n                    server.dirty++;\n                }\n                addReply(c,shared.czero);\n            } else {\n                addReply(c,shared.emptyset[c->resp]);\n            }\n            return;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            zfree(sets);\n            return;\n        }\n        sets[j] = setobj;\n    }\n    /* Sort sets from the smallest to largest, this will improve our\n     * algorithm's performance */\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    if (!dstkey) {\n        replylen = addReplyDeferredLen(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    si = setTypeInitIterator(sets[0]);\n    while((encoding = setTypeNext(si,&elesds,&intobj)) != -1) {\n        for (j = 1; j < setnum; j++) {\n            if (sets[j] == sets[0]) continue;\n            if (encoding == OBJ_ENCODING_INTSET) {\n                /* intset with intset is simple... and fast */\n                if (sets[j]->encoding == OBJ_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 == OBJ_ENCODING_HT) {\n                    elesds = sdsfromlonglong(intobj);\n                    if (!setTypeIsMember(sets[j],elesds)) {\n                        sdsfree(elesds);\n                        break;\n                    }\n                    sdsfree(elesds);\n                }\n            } else if (encoding == OBJ_ENCODING_HT) {\n                if (!setTypeIsMember(sets[j],elesds)) {\n                    break;\n                }\n            }\n        }\n\n        /* Only take action when all sets contain the member */\n        if (j == setnum) {\n            if (!dstkey) {\n                if (encoding == OBJ_ENCODING_HT)\n                    addReplyBulkCBuffer(c,elesds,sdslen(elesds));\n                else\n                    addReplyBulkLongLong(c,intobj);\n                cardinality++;\n            } else {\n                if (encoding == OBJ_ENCODING_INTSET) {\n                    elesds = sdsfromlonglong(intobj);\n                    setTypeAdd(dstset,elesds);\n                    sdsfree(elesds);\n                } else {\n                    setTypeAdd(dstset,elesds);\n                }\n            }\n        }\n    }\n    setTypeReleaseIterator(si);\n\n    if (dstkey) {\n        /* Store the resulting set into the target, if the intersection\n         * is not an empty set. */\n        int deleted = dbDelete(c->db,dstkey);\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(NOTIFY_SET,\"sinterstore\",\n                dstkey,c->db->id);\n        } else {\n            decrRefCount(dstset);\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n        signalModifiedKey(c,c->db,dstkey);\n        server.dirty++;\n    } else {\n        setDeferredSetLen(c,replylen,cardinality);\n    }\n    zfree(sets);\n}\n\nvoid sinterCommand(client *c) {\n    sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);\n}\n\nvoid sinterstoreCommand(client *c) {\n    sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);\n}\n\n#define SET_OP_UNION 0\n#define SET_OP_DIFF 1\n#define SET_OP_INTER 2\n\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,\n                              robj *dstkey, int op) {\n    robj **sets = zmalloc(sizeof(robj*)*setnum);\n    setTypeIterator *si;\n    robj *dstset = NULL;\n    sds ele;\n    int j, cardinality = 0;\n    int diff_algo = 1;\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        if (!setobj) {\n            sets[j] = NULL;\n            continue;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            zfree(sets);\n            return;\n        }\n        sets[j] = setobj;\n    }\n\n    /* Select what DIFF algorithm to use.\n     *\n     * Algorithm 1 is O(N*M) where N is the size of the element first set\n     * and M the total number of sets.\n     *\n     * Algorithm 2 is O(N) where N is the total number of elements in all\n     * the sets.\n     *\n     * We compute what is the best bet with the current input here. */\n    if (op == SET_OP_DIFF && sets[0]) {\n        long long algo_one_work = 0, algo_two_work = 0;\n\n        for (j = 0; j < setnum; j++) {\n            if (sets[j] == NULL) continue;\n\n            algo_one_work += setTypeSize(sets[0]);\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        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            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    dstset = createIntsetObject();\n\n    if (op == SET_OP_UNION) {\n        /* Union is trivial, just add every element of every set to the\n         * temporary set. */\n        for (j = 0; j < setnum; j++) {\n            if (!sets[j]) continue; /* non existing keys are like empty sets */\n\n            si = setTypeInitIterator(sets[j]);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                if (setTypeAdd(dstset,ele)) cardinality++;\n                sdsfree(ele);\n            }\n            setTypeReleaseIterator(si);\n        }\n    } else if (op == SET_OP_DIFF && sets[0] && diff_algo == 1) {\n        /* DIFF Algorithm 1:\n         *\n         * We perform the diff by iterating all the elements of the first set,\n         * and only adding it to the target set if the element does not exist\n         * into all the other sets.\n         *\n         * This way we perform at max N*M operations, where N is the size of\n         * the first set, and M the number of sets. */\n        si = setTypeInitIterator(sets[0]);\n        while((ele = setTypeNextObject(si)) != NULL) {\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            if (j == setnum) {\n                /* There is no other set with this element. Add it. */\n                setTypeAdd(dstset,ele);\n                cardinality++;\n            }\n            sdsfree(ele);\n        }\n        setTypeReleaseIterator(si);\n    } else if (op == SET_OP_DIFF && sets[0] && diff_algo == 2) {\n        /* DIFF Algorithm 2:\n         *\n         * Add all the elements of the first set to the auxiliary set.\n         * Then remove all the elements of all the next sets from it.\n         *\n         * This is O(N) where N is the sum of all the elements in every\n         * set. */\n        for (j = 0; j < setnum; j++) {\n            if (!sets[j]) continue; /* non existing keys are like empty sets */\n\n            si = setTypeInitIterator(sets[j]);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                if (j == 0) {\n                    if (setTypeAdd(dstset,ele)) cardinality++;\n                } else {\n                    if (setTypeRemove(dstset,ele)) cardinality--;\n                }\n                sdsfree(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    if (!dstkey) {\n        addReplySetLen(c,cardinality);\n        si = setTypeInitIterator(dstset);\n        while((ele = setTypeNextObject(si)) != NULL) {\n            addReplyBulkCBuffer(c,ele,sdslen(ele));\n            sdsfree(ele);\n        }\n        setTypeReleaseIterator(si);\n        server.lazyfree_lazy_server_del ? freeObjAsync(dstset) :\n                                          decrRefCount(dstset);\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        int deleted = dbDelete(c->db,dstkey);\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(NOTIFY_SET,\n                op == SET_OP_UNION ? \"sunionstore\" : \"sdiffstore\",\n                dstkey,c->db->id);\n        } else {\n            decrRefCount(dstset);\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n        signalModifiedKey(c,c->db,dstkey);\n        server.dirty++;\n    }\n    zfree(sets);\n}\n\nvoid sunionCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_UNION);\n}\n\nvoid sunionstoreCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_UNION);\n}\n\nvoid sdiffCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_DIFF);\n}\n\nvoid sdiffstoreCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_DIFF);\n}\n\nvoid sscanCommand(client *c) {\n    robj *set;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n    scanGenericCommand(c,set,cursor);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/t_stream.c",
    "content": "/*\n * Copyright (c) 2017, 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 \"server.h\"\n#include \"endianconv.h\"\n#include \"stream.h\"\n\n#define STREAM_BYTES_PER_LISTPACK 2048\n\n/* Every stream item inside the listpack, has a flags field that is used to\n * mark the entry as deleted, or having the same field as the \"master\"\n * entry at the start of the listpack> */\n#define STREAM_ITEM_FLAG_NONE 0             /* No special flags. */\n#define STREAM_ITEM_FLAG_DELETED (1<<0)     /* Entry is deleted. Skip it. */\n#define STREAM_ITEM_FLAG_SAMEFIELDS (1<<1)  /* Same fields as master entry. */\n\nvoid streamFreeCG(streamCG *cg);\nvoid streamFreeNACK(streamNACK *na);\nsize_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer);\n\n/* -----------------------------------------------------------------------\n * Low level stream encoding: a radix tree of listpacks.\n * ----------------------------------------------------------------------- */\n\n/* Create a new stream data structure. */\nstream *streamNew(void) {\n    stream *s = zmalloc(sizeof(*s));\n    s->rax = raxNew();\n    s->length = 0;\n    s->last_id.ms = 0;\n    s->last_id.seq = 0;\n    s->cgroups = NULL; /* Created on demand to save memory when not used. */\n    return s;\n}\n\n/* Free a stream, including the listpacks stored inside the radix tree. */\nvoid freeStream(stream *s) {\n    raxFreeWithCallback(s->rax,(void(*)(void*))lpFree);\n    if (s->cgroups)\n        raxFreeWithCallback(s->cgroups,(void(*)(void*))streamFreeCG);\n    zfree(s);\n}\n\n/* Return the length of a stream. */\nunsigned long streamLength(const robj *subject) {\n    stream *s = subject->ptr;\n    return s->length;\n}\n\n/* Set 'id' to be its successor streamID */\nvoid streamIncrID(streamID *id) {\n    if (id->seq == UINT64_MAX) {\n        if (id->ms == UINT64_MAX) {\n            /* Special case where 'id' is the last possible streamID... */\n            id->ms = id->seq = 0;\n        } else {\n            id->ms++;\n            id->seq = 0;\n        }\n    } else {\n        id->seq++;\n    }\n}\n\n/* Generate the next stream item ID given the previous one. If the current\n * milliseconds Unix time is greater than the previous one, just use this\n * as time part and start with sequence part of zero. Otherwise we use the\n * previous time (and never go backward) and increment the sequence. */\nvoid streamNextID(streamID *last_id, streamID *new_id) {\n    uint64_t ms = mstime();\n    if (ms > last_id->ms) {\n        new_id->ms = ms;\n        new_id->seq = 0;\n    } else {\n        *new_id = *last_id;\n        streamIncrID(new_id);\n    }\n}\n\n/* This is just a wrapper for lpAppend() to directly use a 64 bit integer\n * instead of a string. */\nunsigned char *lpAppendInteger(unsigned char *lp, int64_t value) {\n    char buf[LONG_STR_SIZE];\n    int slen = ll2string(buf,sizeof(buf),value);\n    return lpAppend(lp,(unsigned char*)buf,slen);\n}\n\n/* This is just a wrapper for lpReplace() to directly use a 64 bit integer\n * instead of a string to replace the current element. The function returns\n * the new listpack as return value, and also updates the current cursor\n * by updating '*pos'. */\nunsigned char *lpReplaceInteger(unsigned char *lp, unsigned char **pos, int64_t value) {\n    char buf[LONG_STR_SIZE];\n    int slen = ll2string(buf,sizeof(buf),value);\n    return lpInsert(lp, (unsigned char*)buf, slen, *pos, LP_REPLACE, pos);\n}\n\n/* This is a wrapper function for lpGet() to directly get an integer value\n * from the listpack (that may store numbers as a string), converting\n * the string if needed. */\nint64_t lpGetInteger(unsigned char *ele) {\n    int64_t v;\n    unsigned char *e = lpGet(ele,&v,NULL);\n    if (e == NULL) return v;\n    /* The following code path should never be used for how listpacks work:\n     * they should always be able to store an int64_t value in integer\n     * encoded form. However the implementation may change. */\n    long long ll;\n    int retval = string2ll((char*)e,v,&ll);\n    serverAssert(retval != 0);\n    v = ll;\n    return v;\n}\n\n/* Debugging function to log the full content of a listpack. Useful\n * for development and debugging. */\nvoid streamLogListpackContent(unsigned char *lp) {\n    unsigned char *p = lpFirst(lp);\n    while(p) {\n        unsigned char buf[LP_INTBUF_SIZE];\n        int64_t v;\n        unsigned char *ele = lpGet(p,&v,buf);\n        serverLog(LL_WARNING,\"- [%d] '%.*s'\", (int)v, (int)v, ele);\n        p = lpNext(lp,p);\n    }\n}\n\n/* Convert the specified stream entry ID as a 128 bit big endian number, so\n * that the IDs can be sorted lexicographically. */\nvoid streamEncodeID(void *buf, streamID *id) {\n    uint64_t e[2];\n    e[0] = htonu64(id->ms);\n    e[1] = htonu64(id->seq);\n    memcpy(buf,e,sizeof(e));\n}\n\n/* This is the reverse of streamEncodeID(): the decoded ID will be stored\n * in the 'id' structure passed by reference. The buffer 'buf' must point\n * to a 128 bit big-endian encoded ID. */\nvoid streamDecodeID(void *buf, streamID *id) {\n    uint64_t e[2];\n    memcpy(e,buf,sizeof(e));\n    id->ms = ntohu64(e[0]);\n    id->seq = ntohu64(e[1]);\n}\n\n/* Compare two stream IDs. Return -1 if a < b, 0 if a == b, 1 if a > b. */\nint streamCompareID(streamID *a, streamID *b) {\n    if (a->ms > b->ms) return 1;\n    else if (a->ms < b->ms) return -1;\n    /* The ms part is the same. Check the sequence part. */\n    else if (a->seq > b->seq) return 1;\n    else if (a->seq < b->seq) return -1;\n    /* Everything is the same: IDs are equal. */\n    return 0;\n}\n\n/* Adds a new item into the stream 's' having the specified number of\n * field-value pairs as specified in 'numfields' and stored into 'argv'.\n * Returns the new entry ID populating the 'added_id' structure.\n *\n * If 'use_id' is not NULL, the ID is not auto-generated by the function,\n * but instead the passed ID is used to add the new entry. In this case\n * adding the entry may fail as specified later in this comment.\n *\n * The function returns C_OK if the item was added, this is always true\n * if the ID was generated by the function. However the function may return\n * C_ERR if an ID was given via 'use_id', but adding it failed since the\n * current top ID is greater or equal. */\nint streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_id, streamID *use_id) {\n    \n    /* Generate the new entry ID. */\n    streamID id;\n    if (use_id)\n        id = *use_id;\n    else\n        streamNextID(&s->last_id,&id);\n\n    /* Check that the new ID is greater than the last entry ID\n     * or return an error. Automatically generated IDs might\n     * overflow (and wrap-around) when incrementing the sequence \n       part. */\n    if (streamCompareID(&id,&s->last_id) <= 0) return C_ERR;\n\n    /* Add the new entry. */\n    raxIterator ri;\n    raxStart(&ri,s->rax);\n    raxSeek(&ri,\"$\",NULL,0);\n\n    size_t lp_bytes = 0;        /* Total bytes in the tail listpack. */\n    unsigned char *lp = NULL;   /* Tail listpack pointer. */\n\n    /* Get a reference to the tail node listpack. */\n    if (raxNext(&ri)) {\n        lp = ri.data;\n        lp_bytes = lpBytes(lp);\n    }\n    raxStop(&ri);\n\n    /* We have to add the key into the radix tree in lexicographic order,\n     * to do so we consider the ID as a single 128 bit number written in\n     * big endian, so that the most significant bytes are the first ones. */\n    uint64_t rax_key[2];    /* Key in the radix tree containing the listpack.*/\n    streamID master_id;     /* ID of the master entry in the listpack. */\n\n    /* Create a new listpack and radix tree node if needed. Note that when\n     * a new listpack is created, we populate it with a \"master entry\". This\n     * is just a set of fields that is taken as references in order to compress\n     * the stream entries that we'll add inside the listpack.\n     *\n     * Note that while we use the first added entry fields to create\n     * the master entry, the first added entry is NOT represented in the master\n     * entry, which is a stand alone object. But of course, the first entry\n     * will compress well because it's used as reference.\n     *\n     * The master entry is composed like in the following example:\n     *\n     * +-------+---------+------------+---------+--/--+---------+---------+-+\n     * | count | deleted | num-fields | field_1 | field_2 | ... | field_N |0|\n     * +-------+---------+------------+---------+--/--+---------+---------+-+\n     *\n     * count and deleted just represent respectively the total number of\n     * entries inside the listpack that are valid, and marked as deleted\n     * (deleted flag in the entry flags set). So the total number of items\n     * actually inside the listpack (both deleted and not) is count+deleted.\n     *\n     * The real entries will be encoded with an ID that is just the\n     * millisecond and sequence difference compared to the key stored at\n     * the radix tree node containing the listpack (delta encoding), and\n     * if the fields of the entry are the same as the master entry fields, the\n     * entry flags will specify this fact and the entry fields and number\n     * of fields will be omitted (see later in the code of this function).\n     *\n     * The \"0\" entry at the end is the same as the 'lp-count' entry in the\n     * regular stream entries (see below), and marks the fact that there are\n     * no more entries, when we scan the stream from right to left. */\n\n    /* First of all, check if we can append to the current macro node or\n     * if we need to switch to the next one. 'lp' will be set to NULL if\n     * the current node is full. */\n    if (lp != NULL) {\n        if (server.stream_node_max_bytes &&\n            lp_bytes >= server.stream_node_max_bytes)\n        {\n            lp = NULL;\n        } else if (server.stream_node_max_entries) {\n            int64_t count = lpGetInteger(lpFirst(lp));\n            if (count >= server.stream_node_max_entries) lp = NULL;\n        }\n    }\n\n    int flags = STREAM_ITEM_FLAG_NONE;\n    if (lp == NULL || lp_bytes >= server.stream_node_max_bytes) {\n        master_id = id;\n        streamEncodeID(rax_key,&id);\n        /* Create the listpack having the master entry ID and fields. */\n        lp = lpNew();\n        lp = lpAppendInteger(lp,1); /* One item, the one we are adding. */\n        lp = lpAppendInteger(lp,0); /* Zero deleted so far. */\n        lp = lpAppendInteger(lp,numfields);\n        for (int64_t i = 0; i < numfields; i++) {\n            sds field = argv[i*2]->ptr;\n            lp = lpAppend(lp,(unsigned char*)field,sdslen(field));\n        }\n        lp = lpAppendInteger(lp,0); /* Master entry zero terminator. */\n        raxInsert(s->rax,(unsigned char*)&rax_key,sizeof(rax_key),lp,NULL);\n        /* The first entry we insert, has obviously the same fields of the\n         * master entry. */\n        flags |= STREAM_ITEM_FLAG_SAMEFIELDS;\n    } else {\n        serverAssert(ri.key_len == sizeof(rax_key));\n        memcpy(rax_key,ri.key,sizeof(rax_key));\n\n        /* Read the master ID from the radix tree key. */\n        streamDecodeID(rax_key,&master_id);\n        unsigned char *lp_ele = lpFirst(lp);\n\n        /* Update count and skip the deleted fields. */\n        int64_t count = lpGetInteger(lp_ele);\n        lp = lpReplaceInteger(lp,&lp_ele,count+1);\n        lp_ele = lpNext(lp,lp_ele); /* seek deleted. */\n        lp_ele = lpNext(lp,lp_ele); /* seek master entry num fields. */\n\n        /* Check if the entry we are adding, have the same fields\n         * as the master entry. */\n        int64_t master_fields_count = lpGetInteger(lp_ele);\n        lp_ele = lpNext(lp,lp_ele);\n        if (numfields == master_fields_count) {\n            int64_t i;\n            for (i = 0; i < master_fields_count; i++) {\n                sds field = argv[i*2]->ptr;\n                int64_t e_len;\n                unsigned char buf[LP_INTBUF_SIZE];\n                unsigned char *e = lpGet(lp_ele,&e_len,buf);\n                /* Stop if there is a mismatch. */\n                if (sdslen(field) != (size_t)e_len ||\n                    memcmp(e,field,e_len) != 0) break;\n                lp_ele = lpNext(lp,lp_ele);\n            }\n            /* All fields are the same! We can compress the field names\n             * setting a single bit in the flags. */\n            if (i == master_fields_count) flags |= STREAM_ITEM_FLAG_SAMEFIELDS;\n        }\n    }\n\n    /* Populate the listpack with the new entry. We use the following\n     * encoding:\n     *\n     * +-----+--------+----------+-------+-------+-/-+-------+-------+--------+\n     * |flags|entry-id|num-fields|field-1|value-1|...|field-N|value-N|lp-count|\n     * +-----+--------+----------+-------+-------+-/-+-------+-------+--------+\n     *\n     * However if the SAMEFIELD flag is set, we have just to populate\n     * the entry with the values, so it becomes:\n     *\n     * +-----+--------+-------+-/-+-------+--------+\n     * |flags|entry-id|value-1|...|value-N|lp-count|\n     * +-----+--------+-------+-/-+-------+--------+\n     *\n     * The entry-id field is actually two separated fields: the ms\n     * and seq difference compared to the master entry.\n     *\n     * The lp-count field is a number that states the number of listpack pieces\n     * that compose the entry, so that it's possible to travel the entry\n     * in reverse order: we can just start from the end of the listpack, read\n     * the entry, and jump back N times to seek the \"flags\" field to read\n     * the stream full entry. */\n    lp = lpAppendInteger(lp,flags);\n    lp = lpAppendInteger(lp,id.ms - master_id.ms);\n    lp = lpAppendInteger(lp,id.seq - master_id.seq);\n    if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS))\n        lp = lpAppendInteger(lp,numfields);\n    for (int64_t i = 0; i < numfields; i++) {\n        sds field = argv[i*2]->ptr, value = argv[i*2+1]->ptr;\n        if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS))\n            lp = lpAppend(lp,(unsigned char*)field,sdslen(field));\n        lp = lpAppend(lp,(unsigned char*)value,sdslen(value));\n    }\n    /* Compute and store the lp-count field. */\n    int64_t lp_count = numfields;\n    lp_count += 3; /* Add the 3 fixed fields flags + ms-diff + seq-diff. */\n    if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS)) {\n        /* If the item is not compressed, it also has the fields other than\n         * the values, and an additional num-fileds field. */\n        lp_count += numfields+1;\n    }\n    lp = lpAppendInteger(lp,lp_count);\n\n    /* Insert back into the tree in order to update the listpack pointer. */\n    if (ri.data != lp)\n        raxInsert(s->rax,(unsigned char*)&rax_key,sizeof(rax_key),lp,NULL);\n    s->length++;\n    s->last_id = id;\n    if (added_id) *added_id = id;\n    return C_OK;\n}\n\n/* Trim the stream 's' to have no more than maxlen elements, and return the\n * number of elements removed from the stream. The 'approx' option, if non-zero,\n * specifies that the trimming must be performed in a approximated way in\n * order to maximize performances. This means that the stream may contain\n * more elements than 'maxlen', and elements are only removed if we can remove\n * a *whole* node of the radix tree. The elements are removed from the head\n * of the stream (older elements).\n *\n * The function may return zero if:\n *\n * 1) The stream is already shorter or equal to the specified max length.\n * 2) The 'approx' option is true and the head node had not enough elements\n *    to be deleted, leaving the stream with a number of elements >= maxlen.\n */\nint64_t streamTrimByLength(stream *s, size_t maxlen, int approx) {\n    if (s->length <= maxlen) return 0;\n\n    raxIterator ri;\n    raxStart(&ri,s->rax);\n    raxSeek(&ri,\"^\",NULL,0);\n\n    int64_t deleted = 0;\n    while(s->length > maxlen && raxNext(&ri)) {\n        unsigned char *lp = ri.data, *p = lpFirst(lp);\n        int64_t entries = lpGetInteger(p);\n\n        /* Check if we can remove the whole node, and still have at\n         * least maxlen elements. */\n        if (s->length - entries >= maxlen) {\n            lpFree(lp);\n            raxRemove(s->rax,ri.key,ri.key_len,NULL);\n            raxSeek(&ri,\">=\",ri.key,ri.key_len);\n            s->length -= entries;\n            deleted += entries;\n            continue;\n        }\n\n        /* If we cannot remove a whole element, and approx is true,\n         * stop here. */\n        if (approx) break;\n\n        /* Otherwise, we have to mark single entries inside the listpack\n         * as deleted. We start by updating the entries/deleted counters. */\n        int64_t to_delete = s->length - maxlen;\n        serverAssert(to_delete < entries);\n        lp = lpReplaceInteger(lp,&p,entries-to_delete);\n        p = lpNext(lp,p); /* Seek deleted field. */\n        int64_t marked_deleted = lpGetInteger(p);\n        lp = lpReplaceInteger(lp,&p,marked_deleted+to_delete);\n        p = lpNext(lp,p); /* Seek num-of-fields in the master entry. */\n\n        /* Skip all the master fields. */\n        int64_t master_fields_count = lpGetInteger(p);\n        p = lpNext(lp,p); /* Seek the first field. */\n        for (int64_t j = 0; j < master_fields_count; j++)\n            p = lpNext(lp,p); /* Skip all master fields. */\n        p = lpNext(lp,p); /* Skip the zero master entry terminator. */\n\n        /* 'p' is now pointing to the first entry inside the listpack.\n         * We have to run entry after entry, marking entries as deleted\n         * if they are already not deleted. */\n        while(p) {\n            int flags = lpGetInteger(p);\n            int to_skip;\n\n            /* Mark the entry as deleted. */\n            if (!(flags & STREAM_ITEM_FLAG_DELETED)) {\n                flags |= STREAM_ITEM_FLAG_DELETED;\n                lp = lpReplaceInteger(lp,&p,flags);\n                deleted++;\n                s->length--;\n                if (s->length <= maxlen) break; /* Enough entries deleted. */\n            }\n\n            p = lpNext(lp,p); /* Skip ID ms delta. */\n            p = lpNext(lp,p); /* Skip ID seq delta. */\n            p = lpNext(lp,p); /* Seek num-fields or values (if compressed). */\n            if (flags & STREAM_ITEM_FLAG_SAMEFIELDS) {\n                to_skip = master_fields_count;\n            } else {\n                to_skip = lpGetInteger(p);\n                to_skip = 1+(to_skip*2);\n            }\n\n            while(to_skip--) p = lpNext(lp,p); /* Skip the whole entry. */\n            p = lpNext(lp,p); /* Skip the final lp-count field. */\n        }\n\n        /* Here we should perform garbage collection in case at this point\n         * there are too many entries deleted inside the listpack. */\n        entries -= to_delete;\n        marked_deleted += to_delete;\n        if (entries + marked_deleted > 10 && marked_deleted > entries/2) {\n            /* TODO: perform a garbage collection. */\n        }\n\n        /* Update the listpack with the new pointer. */\n        raxInsert(s->rax,ri.key,ri.key_len,lp,NULL);\n\n        break; /* If we are here, there was enough to delete in the current\n                  node, so no need to go to the next node. */\n    }\n\n    raxStop(&ri);\n    return deleted;\n}\n\n/* Initialize the stream iterator, so that we can call iterating functions\n * to get the next items. This requires a corresponding streamIteratorStop()\n * at the end. The 'rev' parameter controls the direction. If it's zero the\n * iteration is from the start to the end element (inclusive), otherwise\n * if rev is non-zero, the iteration is reversed.\n *\n * Once the iterator is initialized, we iterate like this:\n *\n *  streamIterator myiterator;\n *  streamIteratorStart(&myiterator,...);\n *  int64_t numfields;\n *  while(streamIteratorGetID(&myiterator,&ID,&numfields)) {\n *      while(numfields--) {\n *          unsigned char *key, *value;\n *          size_t key_len, value_len;\n *          streamIteratorGetField(&myiterator,&key,&value,&key_len,&value_len);\n *\n *          ... do what you want with key and value ...\n *      }\n *  }\n *  streamIteratorStop(&myiterator); */\nvoid streamIteratorStart(streamIterator *si, stream *s, streamID *start, streamID *end, int rev) {\n    /* Initialize the iterator and translates the iteration start/stop\n     * elements into a 128 big big-endian number. */\n    if (start) {\n        streamEncodeID(si->start_key,start);\n    } else {\n        si->start_key[0] = 0;\n        si->start_key[1] = 0;\n    }\n\n    if (end) {\n        streamEncodeID(si->end_key,end);\n    } else {\n        si->end_key[0] = UINT64_MAX;\n        si->end_key[1] = UINT64_MAX;\n    }\n\n    /* Seek the correct node in the radix tree. */\n    raxStart(&si->ri,s->rax);\n    if (!rev) {\n        if (start && (start->ms || start->seq)) {\n            raxSeek(&si->ri,\"<=\",(unsigned char*)si->start_key,\n                    sizeof(si->start_key));\n            if (raxEOF(&si->ri)) raxSeek(&si->ri,\"^\",NULL,0);\n        } else {\n            raxSeek(&si->ri,\"^\",NULL,0);\n        }\n    } else {\n        if (end && (end->ms || end->seq)) {\n            raxSeek(&si->ri,\"<=\",(unsigned char*)si->end_key,\n                    sizeof(si->end_key));\n            if (raxEOF(&si->ri)) raxSeek(&si->ri,\"$\",NULL,0);\n        } else {\n            raxSeek(&si->ri,\"$\",NULL,0);\n        }\n    }\n    si->stream = s;\n    si->lp = NULL; /* There is no current listpack right now. */\n    si->lp_ele = NULL; /* Current listpack cursor. */\n    si->rev = rev;  /* Direction, if non-zero reversed, from end to start. */\n}\n\n/* Return 1 and store the current item ID at 'id' if there are still\n * elements within the iteration range, otherwise return 0 in order to\n * signal the iteration terminated. */\nint streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields) {\n    while(1) { /* Will stop when element > stop_key or end of radix tree. */\n        /* If the current listpack is set to NULL, this is the start of the\n         * iteration or the previous listpack was completely iterated.\n         * Go to the next node. */\n        if (si->lp == NULL || si->lp_ele == NULL) {\n            if (!si->rev && !raxNext(&si->ri)) return 0;\n            else if (si->rev && !raxPrev(&si->ri)) return 0;\n            serverAssert(si->ri.key_len == sizeof(streamID));\n            /* Get the master ID. */\n            streamDecodeID(si->ri.key,&si->master_id);\n            /* Get the master fields count. */\n            si->lp = si->ri.data;\n            si->lp_ele = lpFirst(si->lp);           /* Seek items count */\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek deleted count. */\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek num fields. */\n            si->master_fields_count = lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek first field. */\n            si->master_fields_start = si->lp_ele;\n            /* We are now pointing to the first field of the master entry.\n             * We need to seek either the first or the last entry depending\n             * on the direction of the iteration. */\n            if (!si->rev) {\n                /* If we are iterating in normal order, skip the master fields\n                 * to seek the first actual entry. */\n                for (uint64_t i = 0; i < si->master_fields_count; i++)\n                    si->lp_ele = lpNext(si->lp,si->lp_ele);\n            } else {\n                /* If we are iterating in reverse direction, just seek the\n                 * last part of the last entry in the listpack (that is, the\n                 * fields count). */\n                si->lp_ele = lpLast(si->lp);\n            }\n        } else if (si->rev) {\n            /* If we are iterating in the reverse order, and this is not\n             * the first entry emitted for this listpack, then we already\n             * emitted the current entry, and have to go back to the previous\n             * one. */\n            int lp_count = lpGetInteger(si->lp_ele);\n            while(lp_count--) si->lp_ele = lpPrev(si->lp,si->lp_ele);\n            /* Seek lp-count of prev entry. */\n            si->lp_ele = lpPrev(si->lp,si->lp_ele);\n        }\n\n        /* For every radix tree node, iterate the corresponding listpack,\n         * returning elements when they are within range. */\n        while(1) {\n            if (!si->rev) {\n                /* If we are going forward, skip the previous entry\n                 * lp-count field (or in case of the master entry, the zero\n                 * term field) */\n                si->lp_ele = lpNext(si->lp,si->lp_ele);\n                if (si->lp_ele == NULL) break;\n            } else {\n                /* If we are going backward, read the number of elements this\n                 * entry is composed of, and jump backward N times to seek\n                 * its start. */\n                int64_t lp_count = lpGetInteger(si->lp_ele);\n                if (lp_count == 0) { /* We reached the master entry. */\n                    si->lp = NULL;\n                    si->lp_ele = NULL;\n                    break;\n                }\n                while(lp_count--) si->lp_ele = lpPrev(si->lp,si->lp_ele);\n            }\n\n            /* Get the flags entry. */\n            si->lp_flags = si->lp_ele;\n            int flags = lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek ID. */\n\n            /* Get the ID: it is encoded as difference between the master\n             * ID and this entry ID. */\n            *id = si->master_id;\n            id->ms += lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele);\n            id->seq += lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele);\n            unsigned char buf[sizeof(streamID)];\n            streamEncodeID(buf,id);\n\n            /* The number of entries is here or not depending on the\n             * flags. */\n            if (flags & STREAM_ITEM_FLAG_SAMEFIELDS) {\n                *numfields = si->master_fields_count;\n            } else {\n                *numfields = lpGetInteger(si->lp_ele);\n                si->lp_ele = lpNext(si->lp,si->lp_ele);\n            }\n\n            /* If current >= start, and the entry is not marked as\n             * deleted, emit it. */\n            if (!si->rev) {\n                if (memcmp(buf,si->start_key,sizeof(streamID)) >= 0 &&\n                    !(flags & STREAM_ITEM_FLAG_DELETED))\n                {\n                    if (memcmp(buf,si->end_key,sizeof(streamID)) > 0)\n                        return 0; /* We are already out of range. */\n                    si->entry_flags = flags;\n                    if (flags & STREAM_ITEM_FLAG_SAMEFIELDS)\n                        si->master_fields_ptr = si->master_fields_start;\n                    return 1; /* Valid item returned. */\n                }\n            } else {\n                if (memcmp(buf,si->end_key,sizeof(streamID)) <= 0 &&\n                    !(flags & STREAM_ITEM_FLAG_DELETED))\n                {\n                    if (memcmp(buf,si->start_key,sizeof(streamID)) < 0)\n                        return 0; /* We are already out of range. */\n                    si->entry_flags = flags;\n                    if (flags & STREAM_ITEM_FLAG_SAMEFIELDS)\n                        si->master_fields_ptr = si->master_fields_start;\n                    return 1; /* Valid item returned. */\n                }\n            }\n\n            /* If we do not emit, we have to discard if we are going\n             * forward, or seek the previous entry if we are going\n             * backward. */\n            if (!si->rev) {\n                int64_t to_discard = (flags & STREAM_ITEM_FLAG_SAMEFIELDS) ?\n                                      *numfields : *numfields*2;\n                for (int64_t i = 0; i < to_discard; i++)\n                    si->lp_ele = lpNext(si->lp,si->lp_ele);\n            } else {\n                int64_t prev_times = 4; /* flag + id ms + id seq + one more to\n                                           go back to the previous entry \"count\"\n                                           field. */\n                /* If the entry was not flagged SAMEFIELD we also read the\n                 * number of fields, so go back one more. */\n                if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS)) prev_times++;\n                while(prev_times--) si->lp_ele = lpPrev(si->lp,si->lp_ele);\n            }\n        }\n\n        /* End of listpack reached. Try the next/prev radix tree node. */\n    }\n}\n\n/* Get the field and value of the current item we are iterating. This should\n * be called immediately after streamIteratorGetID(), and for each field\n * according to the number of fields returned by streamIteratorGetID().\n * The function populates the field and value pointers and the corresponding\n * lengths by reference, that are valid until the next iterator call, assuming\n * no one touches the stream meanwhile. */\nvoid streamIteratorGetField(streamIterator *si, unsigned char **fieldptr, unsigned char **valueptr, int64_t *fieldlen, int64_t *valuelen) {\n    if (si->entry_flags & STREAM_ITEM_FLAG_SAMEFIELDS) {\n        *fieldptr = lpGet(si->master_fields_ptr,fieldlen,si->field_buf);\n        si->master_fields_ptr = lpNext(si->lp,si->master_fields_ptr);\n    } else {\n        *fieldptr = lpGet(si->lp_ele,fieldlen,si->field_buf);\n        si->lp_ele = lpNext(si->lp,si->lp_ele);\n    }\n    *valueptr = lpGet(si->lp_ele,valuelen,si->value_buf);\n    si->lp_ele = lpNext(si->lp,si->lp_ele);\n}\n\n/* Remove the current entry from the stream: can be called after the\n * GetID() API or after any GetField() call, however we need to iterate\n * a valid entry while calling this function. Moreover the function\n * requires the entry ID we are currently iterating, that was previously\n * returned by GetID().\n *\n * Note that after calling this function, next calls to GetField() can't\n * be performed: the entry is now deleted. Instead the iterator will\n * automatically re-seek to the next entry, so the caller should continue\n * with GetID(). */\nvoid streamIteratorRemoveEntry(streamIterator *si, streamID *current) {\n    unsigned char *lp = si->lp;\n    int64_t aux;\n\n    /* We do not really delete the entry here. Instead we mark it as\n     * deleted flagging it, and also incrementing the count of the\n     * deleted entries in the listpack header.\n     *\n     * We start flagging: */\n    int flags = lpGetInteger(si->lp_flags);\n    flags |= STREAM_ITEM_FLAG_DELETED;\n    lp = lpReplaceInteger(lp,&si->lp_flags,flags);\n\n    /* Change the valid/deleted entries count in the master entry. */\n    unsigned char *p = lpFirst(lp);\n    aux = lpGetInteger(p);\n\n    if (aux == 1) {\n        /* If this is the last element in the listpack, we can remove the whole\n         * node. */\n        lpFree(lp);\n        raxRemove(si->stream->rax,si->ri.key,si->ri.key_len,NULL);\n    } else {\n        /* In the base case we alter the counters of valid/deleted entries. */\n        lp = lpReplaceInteger(lp,&p,aux-1);\n        p = lpNext(lp,p); /* Seek deleted field. */\n        aux = lpGetInteger(p);\n        lp = lpReplaceInteger(lp,&p,aux+1);\n\n        /* Update the listpack with the new pointer. */\n        if (si->lp != lp)\n            raxInsert(si->stream->rax,si->ri.key,si->ri.key_len,lp,NULL);\n    }\n\n    /* Update the number of entries counter. */\n    si->stream->length--;\n\n    /* Re-seek the iterator to fix the now messed up state. */\n    streamID start, end;\n    if (si->rev) {\n        streamDecodeID(si->start_key,&start);\n        end = *current;\n    } else {\n        start = *current;\n        streamDecodeID(si->end_key,&end);\n    }\n    streamIteratorStop(si);\n    streamIteratorStart(si,si->stream,&start,&end,si->rev);\n\n    /* TODO: perform a garbage collection here if the ration between\n     * deleted and valid goes over a certain limit. */\n}\n\n/* Stop the stream iterator. The only cleanup we need is to free the rax\n * iterator, since the stream iterator itself is supposed to be stack\n * allocated. */\nvoid streamIteratorStop(streamIterator *si) {\n    raxStop(&si->ri);\n}\n\n/* Delete the specified item ID from the stream, returning 1 if the item\n * was deleted 0 otherwise (if it does not exist). */\nint streamDeleteItem(stream *s, streamID *id) {\n    int deleted = 0;\n    streamIterator si;\n    streamIteratorStart(&si,s,id,id,0);\n    streamID myid;\n    int64_t numfields;\n    if (streamIteratorGetID(&si,&myid,&numfields)) {\n        streamIteratorRemoveEntry(&si,&myid);\n        deleted = 1;\n    }\n    streamIteratorStop(&si);\n    return deleted;\n}\n\n/* Get the last valid (non-tombstone) streamID of 's'. */\nvoid streamLastValidID(stream *s, streamID *maxid)\n{\n    streamIterator si;\n    streamIteratorStart(&si,s,NULL,NULL,1);\n    int64_t numfields;\n    streamIteratorGetID(&si,maxid,&numfields);\n    streamIteratorStop(&si);\n}\n\n/* Emit a reply in the client output buffer by formatting a Stream ID\n * in the standard <ms>-<seq> format, using the simple string protocol\n * of REPL. */\nvoid addReplyStreamID(client *c, streamID *id) {\n    sds replyid = sdscatfmt(sdsempty(),\"%U-%U\",id->ms,id->seq);\n    addReplyBulkSds(c,replyid);\n}\n\n/* Similar to the above function, but just creates an object, usually useful\n * for replication purposes to create arguments. */\nrobj *createObjectFromStreamID(streamID *id) {\n    return createObject(OBJ_STRING, sdscatfmt(sdsempty(),\"%U-%U\",\n                        id->ms,id->seq));\n}\n\n/* As a result of an explicit XCLAIM or XREADGROUP command, new entries\n * are created in the pending list of the stream and consumers. We need\n * to propagate this changes in the form of XCLAIM commands. */\nvoid streamPropagateXCLAIM(client *c, robj *key, streamCG *group, robj *groupname, robj *id, streamNACK *nack) {\n    /* We need to generate an XCLAIM that will work in a idempotent fashion:\n     *\n     * XCLAIM <key> <group> <consumer> 0 <id> TIME <milliseconds-unix-time>\n     *        RETRYCOUNT <count> FORCE JUSTID LASTID <id>.\n     *\n     * Note that JUSTID is useful in order to avoid that XCLAIM will do\n     * useless work in the slave side, trying to fetch the stream item. */\n    robj *argv[14];\n    argv[0] = createStringObject(\"XCLAIM\",6);\n    argv[1] = key;\n    argv[2] = groupname;\n    argv[3] = createStringObject(nack->consumer->name,sdslen(nack->consumer->name));\n    argv[4] = createStringObjectFromLongLong(0);\n    argv[5] = id;\n    argv[6] = createStringObject(\"TIME\",4);\n    argv[7] = createStringObjectFromLongLong(nack->delivery_time);\n    argv[8] = createStringObject(\"RETRYCOUNT\",10);\n    argv[9] = createStringObjectFromLongLong(nack->delivery_count);\n    argv[10] = createStringObject(\"FORCE\",5);\n    argv[11] = createStringObject(\"JUSTID\",6);\n    argv[12] = createStringObject(\"LASTID\",6);\n    argv[13] = createObjectFromStreamID(&group->last_id);\n\n    /* We use progagate() because this code path is not always called from\n     * the command execution context. Moreover this will just alter the\n     * consumer group state, and we don't need MULTI/EXEC wrapping because\n     * there is no message state cross-message atomicity required. */\n    propagate(server.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[3]);\n    decrRefCount(argv[4]);\n    decrRefCount(argv[6]);\n    decrRefCount(argv[7]);\n    decrRefCount(argv[8]);\n    decrRefCount(argv[9]);\n    decrRefCount(argv[10]);\n    decrRefCount(argv[11]);\n    decrRefCount(argv[12]);\n    decrRefCount(argv[13]);\n}\n\n/* We need this when we want to propoagate the new last-id of a consumer group\n * that was consumed by XREADGROUP with the NOACK option: in that case we can't\n * propagate the last ID just using the XCLAIM LASTID option, so we emit\n *\n *  XGROUP SETID <key> <groupname> <id>\n */\nvoid streamPropagateGroupID(client *c, robj *key, streamCG *group, robj *groupname) {\n    robj *argv[5];\n    argv[0] = createStringObject(\"XGROUP\",6);\n    argv[1] = createStringObject(\"SETID\",5);\n    argv[2] = key;\n    argv[3] = groupname;\n    argv[4] = createObjectFromStreamID(&group->last_id);\n\n    /* We use progagate() because this code path is not always called from\n     * the command execution context. Moreover this will just alter the\n     * consumer group state, and we don't need MULTI/EXEC wrapping because\n     * there is no message state cross-message atomicity required. */\n    propagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[1]);\n    decrRefCount(argv[4]);\n}\n\n/* Send the stream items in the specified range to the client 'c'. The range\n * the client will receive is between start and end inclusive, if 'count' is\n * non zero, no more than 'count' elements are sent.\n *\n * The 'end' pointer can be NULL to mean that we want all the elements from\n * 'start' till the end of the stream. If 'rev' is non zero, elements are\n * produced in reversed order from end to start.\n *\n * The function returns the number of entries emitted.\n *\n * If group and consumer are not NULL, the function performs additional work:\n * 1. It updates the last delivered ID in the group in case we are\n *    sending IDs greater than the current last ID.\n * 2. If the requested IDs are already assigned to some other consumer, the\n *    function will not return it to the client.\n * 3. An entry in the pending list will be created for every entry delivered\n *    for the first time to this consumer.\n *\n * The behavior may be modified passing non-zero flags:\n *\n * STREAM_RWR_NOACK: Do not create PEL entries, that is, the point \"3\" above\n *                   is not performed.\n * STREAM_RWR_RAWENTRIES: Do not emit array boundaries, but just the entries,\n *                        and return the number of entries emitted as usually.\n *                        This is used when the function is just used in order\n *                        to emit data and there is some higher level logic.\n *\n * The final argument 'spi' (stream propagation info pointer) is a structure\n * filled with information needed to propagate the command execution to AOF\n * and slaves, in the case a consumer group was passed: we need to generate\n * XCLAIM commands to create the pending list into AOF/slaves in that case.\n *\n * If 'spi' is set to NULL no propagation will happen even if the group was\n * given, but currently such a feature is never used by the code base that\n * will always pass 'spi' and propagate when a group is passed.\n *\n * Note that this function is recursive in certain cases. When it's called\n * with a non NULL group and consumer argument, it may call\n * streamReplyWithRangeFromConsumerPEL() in order to get entries from the\n * consumer pending entries list. However such a function will then call\n * streamReplyWithRange() in order to emit single entries (found in the\n * PEL by ID) to the client. This is the use case for the STREAM_RWR_RAWENTRIES\n * flag.\n */\n#define STREAM_RWR_NOACK (1<<0)         /* Do not create entries in the PEL. */\n#define STREAM_RWR_RAWENTRIES (1<<1)    /* Do not emit protocol for array\n                                           boundaries, just the entries. */\n#define STREAM_RWR_HISTORY (1<<2)       /* Only serve consumer local PEL. */\nsize_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end, size_t count, int rev, streamCG *group, streamConsumer *consumer, int flags, streamPropInfo *spi) {\n    void *arraylen_ptr = NULL;\n    size_t arraylen = 0;\n    streamIterator si;\n    int64_t numfields;\n    streamID id;\n    int propagate_last_id = 0;\n    int noack = flags & STREAM_RWR_NOACK;\n\n    /* If the client is asking for some history, we serve it using a\n     * different function, so that we return entries *solely* from its\n     * own PEL. This ensures each consumer will always and only see\n     * the history of messages delivered to it and not yet confirmed\n     * as delivered. */\n    if (group && (flags & STREAM_RWR_HISTORY)) {\n        return streamReplyWithRangeFromConsumerPEL(c,s,start,end,count,\n                                                   consumer);\n    }\n\n    if (!(flags & STREAM_RWR_RAWENTRIES))\n        arraylen_ptr = addReplyDeferredLen(c);\n    streamIteratorStart(&si,s,start,end,rev);\n    while(streamIteratorGetID(&si,&id,&numfields)) {\n        /* Update the group last_id if needed. */\n        if (group && streamCompareID(&id,&group->last_id) > 0) {\n            group->last_id = id;\n            /* Group last ID should be propagated only if NOACK was\n             * specified, otherwise the last id will be included\n             * in the propagation of XCLAIM itself. */\n            if (noack) propagate_last_id = 1;\n        }\n\n        /* Emit a two elements array for each item. The first is\n         * the ID, the second is an array of field-value pairs. */\n        addReplyArrayLen(c,2);\n        addReplyStreamID(c,&id);\n\n        addReplyArrayLen(c,numfields*2);\n\n        /* Emit the field-value pairs. */\n        while(numfields--) {\n            unsigned char *key, *value;\n            int64_t key_len, value_len;\n            streamIteratorGetField(&si,&key,&value,&key_len,&value_len);\n            addReplyBulkCBuffer(c,key,key_len);\n            addReplyBulkCBuffer(c,value,value_len);\n        }\n\n        /* If a group is passed, we need to create an entry in the\n         * PEL (pending entries list) of this group *and* this consumer.\n         *\n         * Note that we cannot be sure about the fact the message is not\n         * already owned by another consumer, because the admin is able\n         * to change the consumer group last delivered ID using the\n         * XGROUP SETID command. So if we find that there is already\n         * a NACK for the entry, we need to associate it to the new\n         * consumer. */\n        if (group && !noack) {\n            unsigned char buf[sizeof(streamID)];\n            streamEncodeID(buf,&id);\n\n            /* Try to add a new NACK. Most of the time this will work and\n             * will not require extra lookups. We'll fix the problem later\n             * if we find that there is already a entry for this ID. */\n            streamNACK *nack = streamCreateNACK(consumer);\n            int group_inserted =\n                raxTryInsert(group->pel,buf,sizeof(buf),nack,NULL);\n            int consumer_inserted =\n                raxTryInsert(consumer->pel,buf,sizeof(buf),nack,NULL);\n\n            /* Now we can check if the entry was already busy, and\n             * in that case reassign the entry to the new consumer,\n             * or update it if the consumer is the same as before. */\n            if (group_inserted == 0) {\n                streamFreeNACK(nack);\n                nack = raxFind(group->pel,buf,sizeof(buf));\n                serverAssert(nack != raxNotFound);\n                raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);\n                /* Update the consumer and NACK metadata. */\n                nack->consumer = consumer;\n                nack->delivery_time = mstime();\n                nack->delivery_count = 1;\n                /* Add the entry in the new consumer local PEL. */\n                raxInsert(consumer->pel,buf,sizeof(buf),nack,NULL);\n            } else if (group_inserted == 1 && consumer_inserted == 0) {\n                serverPanic(\"NACK half-created. Should not be possible.\");\n            }\n\n            /* Propagate as XCLAIM. */\n            if (spi) {\n                robj *idarg = createObjectFromStreamID(&id);\n                streamPropagateXCLAIM(c,spi->keyname,group,spi->groupname,idarg,nack);\n                decrRefCount(idarg);\n            }\n        }\n\n        arraylen++;\n        if (count && count == arraylen) break;\n    }\n\n    if (spi && propagate_last_id)\n        streamPropagateGroupID(c,spi->keyname,group,spi->groupname);\n\n    streamIteratorStop(&si);\n    if (arraylen_ptr) setDeferredArrayLen(c,arraylen_ptr,arraylen);\n    return arraylen;\n}\n\n/* This is an helper function for streamReplyWithRange() when called with\n * group and consumer arguments, but with a range that is referring to already\n * delivered messages. In this case we just emit messages that are already\n * in the history of the consumer, fetching the IDs from its PEL.\n *\n * Note that this function does not have a 'rev' argument because it's not\n * possible to iterate in reverse using a group. Basically this function\n * is only called as a result of the XREADGROUP command.\n *\n * This function is more expensive because it needs to inspect the PEL and then\n * seek into the radix tree of the messages in order to emit the full message\n * to the client. However clients only reach this code path when they are\n * fetching the history of already retrieved messages, which is rare. */\nsize_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer) {\n    raxIterator ri;\n    unsigned char startkey[sizeof(streamID)];\n    unsigned char endkey[sizeof(streamID)];\n    streamEncodeID(startkey,start);\n    if (end) streamEncodeID(endkey,end);\n\n    size_t arraylen = 0;\n    void *arraylen_ptr = addReplyDeferredLen(c);\n    raxStart(&ri,consumer->pel);\n    raxSeek(&ri,\">=\",startkey,sizeof(startkey));\n    while(raxNext(&ri) && (!count || arraylen < count)) {\n        if (end && memcmp(ri.key,end,ri.key_len) > 0) break;\n        streamID thisid;\n        streamDecodeID(ri.key,&thisid);\n        if (streamReplyWithRange(c,s,&thisid,&thisid,1,0,NULL,NULL,\n                                 STREAM_RWR_RAWENTRIES,NULL) == 0)\n        {\n            /* Note that we may have a not acknowledged entry in the PEL\n             * about a message that's no longer here because was removed\n             * by the user by other means. In that case we signal it emitting\n             * the ID but then a NULL entry for the fields. */\n            addReplyArrayLen(c,2);\n            addReplyStreamID(c,&thisid);\n            addReplyNullArray(c);\n        } else {\n            streamNACK *nack = ri.data;\n            nack->delivery_time = mstime();\n            nack->delivery_count++;\n        }\n        arraylen++;\n    }\n    raxStop(&ri);\n    setDeferredArrayLen(c,arraylen_ptr,arraylen);\n    return arraylen;\n}\n\n/* -----------------------------------------------------------------------\n * Stream commands implementation\n * ----------------------------------------------------------------------- */\n\n/* Look the stream at 'key' and return the corresponding stream object.\n * The function creates a key setting it to an empty stream if needed. */\nrobj *streamTypeLookupWriteOrCreate(client *c, robj *key) {\n    robj *o = lookupKeyWrite(c->db,key);\n    if (o == NULL) {\n        o = createStreamObject();\n        dbAdd(c->db,key,o);\n    } else {\n        if (o->type != OBJ_STREAM) {\n            addReply(c,shared.wrongtypeerr);\n            return NULL;\n        }\n    }\n    return o;\n}\n\n/* Parse a stream ID in the format given by clients to Redis, that is\n * <ms>-<seq>, and converts it into a streamID structure. If\n * the specified ID is invalid C_ERR is returned and an error is reported\n * to the client, otherwise C_OK is returned. The ID may be in incomplete\n * form, just stating the milliseconds time part of the stream. In such a case\n * the missing part is set according to the value of 'missing_seq' parameter.\n *\n * The IDs \"-\" and \"+\" specify respectively the minimum and maximum IDs\n * that can be represented. If 'strict' is set to 1, \"-\" and \"+\" will be\n * treated as an invalid ID.\n *\n * If 'c' is set to NULL, no reply is sent to the client. */\nint streamGenericParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq, int strict) {\n    char buf[128];\n    if (sdslen(o->ptr) > sizeof(buf)-1) goto invalid;\n    memcpy(buf,o->ptr,sdslen(o->ptr)+1);\n\n    if (strict && (buf[0] == '-' || buf[0] == '+') && buf[1] == '\\0')\n        goto invalid;\n\n    /* Handle the \"-\" and \"+\" special cases. */\n    if (buf[0] == '-' && buf[1] == '\\0') {\n        id->ms = 0;\n        id->seq = 0;\n        return C_OK;\n    } else if (buf[0] == '+' && buf[1] == '\\0') {\n        id->ms = UINT64_MAX;\n        id->seq = UINT64_MAX;\n        return C_OK;\n    }\n\n    /* Parse <ms>-<seq> form. */\n    char *dot = strchr(buf,'-');\n    if (dot) *dot = '\\0';\n    unsigned long long ms, seq;\n    if (string2ull(buf,&ms) == 0) goto invalid;\n    if (dot && string2ull(dot+1,&seq) == 0) goto invalid;\n    if (!dot) seq = missing_seq;\n    id->ms = ms;\n    id->seq = seq;\n    return C_OK;\n\ninvalid:\n    if (c) addReplyError(c,\"Invalid stream ID specified as stream \"\n                           \"command argument\");\n    return C_ERR;\n}\n\n/* Wrapper for streamGenericParseIDOrReply() with 'strict' argument set to\n * 0, to be used when - and + are acceptable IDs. */\nint streamParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {\n    return streamGenericParseIDOrReply(c,o,id,missing_seq,0);\n}\n\n/* Wrapper for streamGenericParseIDOrReply() with 'strict' argument set to\n * 1, to be used when we want to return an error if the special IDs + or -\n * are provided. */\nint streamParseStrictIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {\n    return streamGenericParseIDOrReply(c,o,id,missing_seq,1);\n}\n\n/* We propagate MAXLEN ~ <count> as MAXLEN = <resulting-len-of-stream>\n * otherwise trimming is no longer determinsitic on replicas / AOF. */\nvoid streamRewriteApproxMaxlen(client *c, stream *s, int maxlen_arg_idx) {\n    robj *maxlen_obj = createStringObjectFromLongLong(s->length);\n    robj *equal_obj = createStringObject(\"=\",1);\n\n    rewriteClientCommandArgument(c,maxlen_arg_idx,maxlen_obj);\n    rewriteClientCommandArgument(c,maxlen_arg_idx-1,equal_obj);\n\n    decrRefCount(equal_obj);\n    decrRefCount(maxlen_obj);\n}\n\n/* XADD key [MAXLEN [~|=] <count>] <ID or *> [field value] [field value] ... */\nvoid xaddCommand(client *c) {\n    streamID id;\n    int id_given = 0; /* Was an ID different than \"*\" specified? */\n    long long maxlen = -1;  /* If left to -1 no trimming is performed. */\n    int approx_maxlen = 0;  /* If 1 only delete whole radix tree nodes, so\n                               the maxium length is not applied verbatim. */\n    int maxlen_arg_idx = 0; /* Index of the count in MAXLEN, for rewriting. */\n\n    /* Parse options. */\n    int i = 2; /* This is the first argument position where we could\n                  find an option, or the ID. */\n    for (; i < c->argc; i++) {\n        int moreargs = (c->argc-1) - i; /* Number of additional arguments. */\n        char *opt = c->argv[i]->ptr;\n        if (opt[0] == '*' && opt[1] == '\\0') {\n            /* This is just a fast path for the common case of auto-ID\n             * creation. */\n            break;\n        } else if (!strcasecmp(opt,\"maxlen\") && moreargs) {\n            approx_maxlen = 0;\n            char *next = c->argv[i+1]->ptr;\n            /* Check for the form MAXLEN ~ <count>. */\n            if (moreargs >= 2 && next[0] == '~' && next[1] == '\\0') {\n                approx_maxlen = 1;\n                i++;\n            } else if (moreargs >= 2 && next[0] == '=' && next[1] == '\\0') {\n                i++;\n            }\n            if (getLongLongFromObjectOrReply(c,c->argv[i+1],&maxlen,NULL)\n                != C_OK) return;\n\n            if (maxlen < 0) {\n                addReplyError(c,\"The MAXLEN argument must be >= 0.\");\n                return;\n            }\n            i++;\n            maxlen_arg_idx = i;\n        } else {\n            /* If we are here is a syntax error or a valid ID. */\n            if (streamParseStrictIDOrReply(c,c->argv[i],&id,0) != C_OK) return;\n            id_given = 1;\n            break;\n        }\n    }\n    int field_pos = i+1;\n\n    /* Check arity. */\n    if ((c->argc - field_pos) < 2 || ((c->argc-field_pos) % 2) == 1) {\n        addReplyError(c,\"wrong number of arguments for XADD\");\n        return;\n    }\n\n    /* Return ASAP if minimal ID (0-0) was given so we avoid possibly creating\n     * a new stream and have streamAppendItem fail, leaving an empty key in the\n     * database. */\n    if (id_given && id.ms == 0 && id.seq == 0) {\n        addReplyError(c,\"The ID specified in XADD must be greater than 0-0\");\n        return;\n    }\n\n    /* Lookup the stream at key. */\n    robj *o;\n    stream *s;\n    if ((o = streamTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    s = o->ptr;\n\n    /* Return ASAP if the stream has reached the last possible ID */\n    if (s->last_id.ms == UINT64_MAX && s->last_id.seq == UINT64_MAX) {\n        addReplyError(c,\"The stream has exhausted the last possible ID, \"\n                        \"unable to add more items\");\n        return;\n    }\n\n    /* Append using the low level function and return the ID. */\n    if (streamAppendItem(s,c->argv+field_pos,(c->argc-field_pos)/2,\n        &id, id_given ? &id : NULL)\n        == C_ERR)\n    {\n        addReplyError(c,\"The ID specified in XADD is equal or smaller than the \"\n                        \"target stream top item\");\n        return;\n    }\n    addReplyStreamID(c,&id);\n\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STREAM,\"xadd\",c->argv[1],c->db->id);\n    server.dirty++;\n\n    if (maxlen >= 0) {\n        /* Notify xtrim event if needed. */\n        if (streamTrimByLength(s,maxlen,approx_maxlen)) {\n            notifyKeyspaceEvent(NOTIFY_STREAM,\"xtrim\",c->argv[1],c->db->id);\n        }\n        if (approx_maxlen) streamRewriteApproxMaxlen(c,s,maxlen_arg_idx);\n    }\n\n    /* Let's rewrite the ID argument with the one actually generated for\n     * AOF/replication propagation. */\n    robj *idarg = createObjectFromStreamID(&id);\n    rewriteClientCommandArgument(c,i,idarg);\n    decrRefCount(idarg);\n\n    /* We need to signal to blocked clients that there is new data on this\n     * stream. */\n    if (server.blocked_clients_by_type[BLOCKED_STREAM])\n        signalKeyAsReady(c->db, c->argv[1]);\n}\n\n/* XRANGE/XREVRANGE actual implementation. */\nvoid xrangeGenericCommand(client *c, int rev) {\n    robj *o;\n    stream *s;\n    streamID startid, endid;\n    long long count = -1;\n    robj *startarg = rev ? c->argv[3] : c->argv[2];\n    robj *endarg = rev ? c->argv[2] : c->argv[3];\n\n    if (streamParseIDOrReply(c,startarg,&startid,0) == C_ERR) return;\n    if (streamParseIDOrReply(c,endarg,&endid,UINT64_MAX) == C_ERR) return;\n\n    /* Parse the COUNT option if any. */\n    if (c->argc > 4) {\n        for (int j = 4; j < c->argc; j++) {\n            int additional = c->argc-j-1;\n            if (strcasecmp(c->argv[j]->ptr,\"COUNT\") == 0 && additional >= 1) {\n                if (getLongLongFromObjectOrReply(c,c->argv[j+1],&count,NULL)\n                    != C_OK) return;\n                if (count < 0) count = 0;\n                j++; /* Consume additional arg. */\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* Return the specified range to the user. */\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == NULL ||\n         checkType(c,o,OBJ_STREAM)) return;\n\n    s = o->ptr;\n\n    if (count == 0) {\n        addReplyNullArray(c);\n    } else {\n        if (count == -1) count = 0;\n        streamReplyWithRange(c,s,&startid,&endid,count,rev,NULL,NULL,0,NULL);\n    }\n}\n\n/* XRANGE key start end [COUNT <n>] */\nvoid xrangeCommand(client *c) {\n    xrangeGenericCommand(c,0);\n}\n\n/* XREVRANGE key end start [COUNT <n>] */\nvoid xrevrangeCommand(client *c) {\n    xrangeGenericCommand(c,1);\n}\n\n/* XLEN */\nvoid xlenCommand(client *c) {\n    robj *o;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL\n        || checkType(c,o,OBJ_STREAM)) return;\n    stream *s = o->ptr;\n    addReplyLongLong(c,s->length);\n}\n\n/* XREAD [BLOCK <milliseconds>] [COUNT <count>] STREAMS key_1 key_2 ... key_N\n *       ID_1 ID_2 ... ID_N\n *\n * This function also implements the XREAD-GROUP command, which is like XREAD\n * but accepting the [GROUP group-name consumer-name] additional option.\n * This is useful because while XREAD is a read command and can be called\n * on slaves, XREAD-GROUP is not. */\n#define XREAD_BLOCKED_DEFAULT_COUNT 1000\nvoid xreadCommand(client *c) {\n    long long timeout = -1; /* -1 means, no BLOCK argument given. */\n    long long count = 0;\n    int streams_count = 0;\n    int streams_arg = 0;\n    int noack = 0;          /* True if NOACK option was specified. */\n    #define STREAMID_STATIC_VECTOR_LEN 8\n    streamID static_ids[STREAMID_STATIC_VECTOR_LEN];\n    streamID *ids = static_ids;\n    streamCG **groups = NULL;\n    int xreadgroup = sdslen(c->argv[0]->ptr) == 10; /* XREAD or XREADGROUP? */\n    robj *groupname = NULL;\n    robj *consumername = NULL;\n\n    /* Parse arguments. */\n    for (int i = 1; i < c->argc; i++) {\n        int moreargs = c->argc-i-1;\n        char *o = c->argv[i]->ptr;\n        if (!strcasecmp(o,\"BLOCK\") && moreargs) {\n            if (c->flags & CLIENT_LUA) {\n                /* There is no sense to use BLOCK option within LUA */\n                addReplyErrorFormat(c, \"%s command is not allowed with BLOCK option from scripts\", (char *)c->argv[0]->ptr);\n                return;\n            }\n            i++;\n            if (getTimeoutFromObjectOrReply(c,c->argv[i],&timeout,\n                UNIT_MILLISECONDS) != C_OK) return;\n        } else if (!strcasecmp(o,\"COUNT\") && moreargs) {\n            i++;\n            if (getLongLongFromObjectOrReply(c,c->argv[i],&count,NULL) != C_OK)\n                return;\n            if (count < 0) count = 0;\n        } else if (!strcasecmp(o,\"STREAMS\") && moreargs) {\n            streams_arg = i+1;\n            streams_count = (c->argc-streams_arg);\n            if ((streams_count % 2) != 0) {\n                addReplyError(c,\"Unbalanced XREAD list of streams: \"\n                                \"for each stream key an ID or '$' must be \"\n                                \"specified.\");\n                return;\n            }\n            streams_count /= 2; /* We have two arguments for each stream. */\n            break;\n        } else if (!strcasecmp(o,\"GROUP\") && moreargs >= 2) {\n            if (!xreadgroup) {\n                addReplyError(c,\"The GROUP option is only supported by \"\n                                \"XREADGROUP. You called XREAD instead.\");\n                return;\n            }\n            groupname = c->argv[i+1];\n            consumername = c->argv[i+2];\n            i += 2;\n        } else if (!strcasecmp(o,\"NOACK\")) {\n            if (!xreadgroup) {\n                addReplyError(c,\"The NOACK option is only supported by \"\n                                \"XREADGROUP. You called XREAD instead.\");\n                return;\n            }\n            noack = 1;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* STREAMS option is mandatory. */\n    if (streams_arg == 0) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* If the user specified XREADGROUP then it must also\n     * provide the GROUP option. */\n    if (xreadgroup && groupname == NULL) {\n        addReplyError(c,\"Missing GROUP option for XREADGROUP\");\n        return;\n    }\n\n    /* Parse the IDs and resolve the group name. */\n    if (streams_count > STREAMID_STATIC_VECTOR_LEN)\n        ids = zmalloc(sizeof(streamID)*streams_count);\n    if (groupname) groups = zmalloc(sizeof(streamCG*)*streams_count);\n\n    for (int i = streams_arg + streams_count; i < c->argc; i++) {\n        /* Specifying \"$\" as last-known-id means that the client wants to be\n         * served with just the messages that will arrive into the stream\n         * starting from now. */\n        int id_idx = i - streams_arg - streams_count;\n        robj *key = c->argv[i-streams_count];\n        robj *o = lookupKeyRead(c->db,key);\n        if (o && checkType(c,o,OBJ_STREAM)) goto cleanup;\n        streamCG *group = NULL;\n\n        /* If a group was specified, than we need to be sure that the\n         * key and group actually exist. */\n        if (groupname) {\n            if (o == NULL ||\n                (group = streamLookupCG(o->ptr,groupname->ptr)) == NULL)\n            {\n                addReplyErrorFormat(c, \"-NOGROUP No such key '%s' or consumer \"\n                                       \"group '%s' in XREADGROUP with GROUP \"\n                                       \"option\",\n                                    (char*)key->ptr,(char*)groupname->ptr);\n                goto cleanup;\n            }\n            groups[id_idx] = group;\n        }\n\n        if (strcmp(c->argv[i]->ptr,\"$\") == 0) {\n            if (xreadgroup) {\n                addReplyError(c,\"The $ ID is meaningless in the context of \"\n                                \"XREADGROUP: you want to read the history of \"\n                                \"this consumer by specifying a proper ID, or \"\n                                \"use the > ID to get new messages. The $ ID would \"\n                                \"just return an empty result set.\");\n                goto cleanup;\n            }\n            if (o) {\n                stream *s = o->ptr;\n                ids[id_idx] = s->last_id;\n            } else {\n                ids[id_idx].ms = 0;\n                ids[id_idx].seq = 0;\n            }\n            continue;\n        } else if (strcmp(c->argv[i]->ptr,\">\") == 0) {\n            if (!xreadgroup) {\n                addReplyError(c,\"The > ID can be specified only when calling \"\n                                \"XREADGROUP using the GROUP <group> \"\n                                \"<consumer> option.\");\n                goto cleanup;\n            }\n            /* We use just the maximum ID to signal this is a \">\" ID, anyway\n             * the code handling the blocking clients will have to update the\n             * ID later in order to match the changing consumer group last ID. */\n            ids[id_idx].ms = UINT64_MAX;\n            ids[id_idx].seq = UINT64_MAX;\n            continue;\n        }\n        if (streamParseStrictIDOrReply(c,c->argv[i],ids+id_idx,0) != C_OK)\n            goto cleanup;\n    }\n\n    /* Try to serve the client synchronously. */\n    size_t arraylen = 0;\n    void *arraylen_ptr = NULL;\n    for (int i = 0; i < streams_count; i++) {\n        robj *o = lookupKeyRead(c->db,c->argv[streams_arg+i]);\n        if (o == NULL) continue;\n        stream *s = o->ptr;\n        streamID *gt = ids+i; /* ID must be greater than this. */\n        int serve_synchronously = 0;\n        int serve_history = 0; /* True for XREADGROUP with ID != \">\". */\n\n        /* Check if there are the conditions to serve the client\n         * synchronously. */\n        if (groups) {\n            /* If the consumer is blocked on a group, we always serve it\n             * synchronously (serving its local history) if the ID specified\n             * was not the special \">\" ID. */\n            if (gt->ms != UINT64_MAX ||\n                gt->seq != UINT64_MAX)\n            {\n                serve_synchronously = 1;\n                serve_history = 1;\n            } else if (s->length) {\n                /* We also want to serve a consumer in a consumer group\n                 * synchronously in case the group top item delivered is smaller\n                 * than what the stream has inside. */\n                streamID maxid, *last = &groups[i]->last_id;\n                streamLastValidID(s, &maxid);\n                if (streamCompareID(&maxid, last) > 0) {\n                    serve_synchronously = 1;\n                    *gt = *last;\n                }\n            }\n        } else if (s->length) {\n            /* For consumers without a group, we serve synchronously if we can\n             * actually provide at least one item from the stream. */\n            streamID maxid;\n            streamLastValidID(s, &maxid);\n            if (streamCompareID(&maxid, gt) > 0) {\n                serve_synchronously = 1;\n            }\n        }\n\n        if (serve_synchronously) {\n            arraylen++;\n            if (arraylen == 1) arraylen_ptr = addReplyDeferredLen(c);\n            /* streamReplyWithRange() handles the 'start' ID as inclusive,\n             * so start from the next ID, since we want only messages with\n             * IDs greater than start. */\n            streamID start = *gt;\n            streamIncrID(&start);\n\n            /* Emit the two elements sub-array consisting of the name\n             * of the stream and the data we extracted from it. */\n            if (c->resp == 2) addReplyArrayLen(c,2);\n            addReplyBulk(c,c->argv[streams_arg+i]);\n            streamConsumer *consumer = NULL;\n            if (groups) consumer = streamLookupConsumer(groups[i],\n                                                        consumername->ptr,\n                                                        SLC_NONE);\n            streamPropInfo spi = {c->argv[i+streams_arg],groupname};\n            int flags = 0;\n            if (noack) flags |= STREAM_RWR_NOACK;\n            if (serve_history) flags |= STREAM_RWR_HISTORY;\n            streamReplyWithRange(c,s,&start,NULL,count,0,\n                                 groups ? groups[i] : NULL,\n                                 consumer, flags, &spi);\n            if (groups) server.dirty++;\n        }\n    }\n\n     /* We replied synchronously! Set the top array len and return to caller. */\n    if (arraylen) {\n        if (c->resp == 2)\n            setDeferredArrayLen(c,arraylen_ptr,arraylen);\n        else\n            setDeferredMapLen(c,arraylen_ptr,arraylen);\n        goto cleanup;\n    }\n\n    /* Block if needed. */\n    if (timeout != -1) {\n        /* If we are inside a MULTI/EXEC and the list is empty the only thing\n         * we can do is treating it as a timeout (even with timeout 0). */\n        if (c->flags & CLIENT_MULTI) {\n            addReplyNullArray(c);\n            goto cleanup;\n        }\n        blockForKeys(c, BLOCKED_STREAM, c->argv+streams_arg, streams_count,\n                     timeout, NULL, ids);\n        /* If no COUNT is given and we block, set a relatively small count:\n         * in case the ID provided is too low, we do not want the server to\n         * block just to serve this client a huge stream of messages. */\n        c->bpop.xread_count = count ? count : XREAD_BLOCKED_DEFAULT_COUNT;\n\n        /* If this is a XREADGROUP + GROUP we need to remember for which\n         * group and consumer name we are blocking, so later when one of the\n         * keys receive more data, we can call streamReplyWithRange() passing\n         * the right arguments. */\n        if (groupname) {\n            incrRefCount(groupname);\n            incrRefCount(consumername);\n            c->bpop.xread_group = groupname;\n            c->bpop.xread_consumer = consumername;\n            c->bpop.xread_group_noack = noack;\n        } else {\n            c->bpop.xread_group = NULL;\n            c->bpop.xread_consumer = NULL;\n        }\n        goto cleanup;\n    }\n\n    /* No BLOCK option, nor any stream we can serve. Reply as with a\n     * timeout happened. */\n    addReplyNullArray(c);\n    /* Continue to cleanup... */\n\ncleanup: /* Cleanup. */\n\n    /* The command is propagated (in the READGROUP form) as a side effect\n     * of calling lower level APIs. So stop any implicit propagation. */\n    preventCommandPropagation(c);\n    if (ids != static_ids) zfree(ids);\n    zfree(groups);\n}\n\n/* -----------------------------------------------------------------------\n * Low level implementation of consumer groups\n * ----------------------------------------------------------------------- */\n\n/* Create a NACK entry setting the delivery count to 1 and the delivery\n * time to the current time. The NACK consumer will be set to the one\n * specified as argument of the function. */\nstreamNACK *streamCreateNACK(streamConsumer *consumer) {\n    streamNACK *nack = zmalloc(sizeof(*nack));\n    nack->delivery_time = mstime();\n    nack->delivery_count = 1;\n    nack->consumer = consumer;\n    return nack;\n}\n\n/* Free a NACK entry. */\nvoid streamFreeNACK(streamNACK *na) {\n    zfree(na);\n}\n\n/* Free a consumer and associated data structures. Note that this function\n * will not reassign the pending messages associated with this consumer\n * nor will delete them from the stream, so when this function is called\n * to delete a consumer, and not when the whole stream is destroyed, the caller\n * should do some work before. */\nvoid streamFreeConsumer(streamConsumer *sc) {\n    raxFree(sc->pel); /* No value free callback: the PEL entries are shared\n                         between the consumer and the main stream PEL. */\n    sdsfree(sc->name);\n    zfree(sc);\n}\n\n/* Create a new consumer group in the context of the stream 's', having the\n * specified name and last server ID. If a consumer group with the same name\n * already existed NULL is returned, otherwise the pointer to the consumer\n * group is returned. */\nstreamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id) {\n    if (s->cgroups == NULL) s->cgroups = raxNew();\n    if (raxFind(s->cgroups,(unsigned char*)name,namelen) != raxNotFound)\n        return NULL;\n\n    streamCG *cg = zmalloc(sizeof(*cg));\n    cg->pel = raxNew();\n    cg->consumers = raxNew();\n    cg->last_id = *id;\n    raxInsert(s->cgroups,(unsigned char*)name,namelen,cg,NULL);\n    return cg;\n}\n\n/* Free a consumer group and all its associated data. */\nvoid streamFreeCG(streamCG *cg) {\n    raxFreeWithCallback(cg->pel,(void(*)(void*))streamFreeNACK);\n    raxFreeWithCallback(cg->consumers,(void(*)(void*))streamFreeConsumer);\n    zfree(cg);\n}\n\n/* Lookup the consumer group in the specified stream and returns its\n * pointer, otherwise if there is no such group, NULL is returned. */\nstreamCG *streamLookupCG(stream *s, sds groupname) {\n    if (s->cgroups == NULL) return NULL;\n    streamCG *cg = raxFind(s->cgroups,(unsigned char*)groupname,\n                           sdslen(groupname));\n    return (cg == raxNotFound) ? NULL : cg;\n}\n\n/* Lookup the consumer with the specified name in the group 'cg': if the\n * consumer does not exist it is automatically created as a side effect\n * of calling this function, otherwise its last seen time is updated and\n * the existing consumer reference returned. */\nstreamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags) {\n    int create = !(flags & SLC_NOCREAT);\n    int refresh = !(flags & SLC_NOREFRESH);\n    streamConsumer *consumer = raxFind(cg->consumers,(unsigned char*)name,\n                               sdslen(name));\n    if (consumer == raxNotFound) {\n        if (!create) return NULL;\n        consumer = zmalloc(sizeof(*consumer));\n        consumer->name = sdsdup(name);\n        consumer->pel = raxNew();\n        raxInsert(cg->consumers,(unsigned char*)name,sdslen(name),\n                  consumer,NULL);\n    }\n    if (refresh) consumer->seen_time = mstime();\n    return consumer;\n}\n\n/* Delete the consumer specified in the consumer group 'cg'. The consumer\n * may have pending messages: they are removed from the PEL, and the number\n * of pending messages \"lost\" is returned. */\nuint64_t streamDelConsumer(streamCG *cg, sds name) {\n    streamConsumer *consumer =\n        streamLookupConsumer(cg,name,SLC_NOCREAT|SLC_NOREFRESH);\n    if (consumer == NULL) return 0;\n\n    uint64_t retval = raxSize(consumer->pel);\n\n    /* Iterate all the consumer pending messages, deleting every corresponding\n     * entry from the global entry. */\n    raxIterator ri;\n    raxStart(&ri,consumer->pel);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        streamNACK *nack = ri.data;\n        raxRemove(cg->pel,ri.key,ri.key_len,NULL);\n        streamFreeNACK(nack);\n    }\n    raxStop(&ri);\n\n    /* Deallocate the consumer. */\n    raxRemove(cg->consumers,(unsigned char*)name,sdslen(name),NULL);\n    streamFreeConsumer(consumer);\n    return retval;\n}\n\n/* -----------------------------------------------------------------------\n * Consumer groups commands\n * ----------------------------------------------------------------------- */\n\n/* XGROUP CREATE <key> <groupname> <id or $> [MKSTREAM]\n * XGROUP SETID <key> <groupname> <id or $>\n * XGROUP DESTROY <key> <groupname>\n * XGROUP DELCONSUMER <key> <groupname> <consumername> */\nvoid xgroupCommand(client *c) {\n    const char *help[] = {\n\"CREATE      <key> <groupname> <id or $> [opt] -- Create a new consumer group.\",\n\"            option MKSTREAM: create the empty stream if it does not exist.\",\n\"SETID       <key> <groupname> <id or $>  -- Set the current group ID.\",\n\"DESTROY     <key> <groupname>            -- Remove the specified group.\",\n\"DELCONSUMER <key> <groupname> <consumer> -- Remove the specified consumer.\",\n\"HELP                                     -- Prints this help.\",\nNULL\n    };\n    stream *s = NULL;\n    sds grpname = NULL;\n    streamCG *cg = NULL;\n    char *opt = c->argv[1]->ptr; /* Subcommand name. */\n    int mkstream = 0;\n    robj *o;\n\n    /* CREATE has an MKSTREAM option that creates the stream if it\n     * does not exist. */\n    if (c->argc == 6 && !strcasecmp(opt,\"CREATE\")) {\n        if (strcasecmp(c->argv[5]->ptr,\"MKSTREAM\")) {\n            addReplySubcommandSyntaxError(c);\n            return;\n        }\n        mkstream = 1;\n        grpname = c->argv[3]->ptr;\n    }\n\n    /* Everything but the \"HELP\" option requires a key and group name. */\n    if (c->argc >= 4) {\n        o = lookupKeyWrite(c->db,c->argv[2]);\n        if (o) {\n            if (checkType(c,o,OBJ_STREAM)) return;\n            s = o->ptr;\n        }\n        grpname = c->argv[3]->ptr;\n    }\n\n    /* Check for missing key/group. */\n    if (c->argc >= 4 && !mkstream) {\n        /* At this point key must exist, or there is an error. */\n        if (s == NULL) {\n            addReplyError(c,\n                \"The XGROUP subcommand requires the key to exist. \"\n                \"Note that for CREATE you may want to use the MKSTREAM \"\n                \"option to create an empty stream automatically.\");\n            return;\n        }\n\n        /* Certain subcommands require the group to exist. */\n        if ((cg = streamLookupCG(s,grpname)) == NULL &&\n            (!strcasecmp(opt,\"SETID\") ||\n             !strcasecmp(opt,\"DELCONSUMER\")))\n        {\n            addReplyErrorFormat(c, \"-NOGROUP No such consumer group '%s' \"\n                                   \"for key name '%s'\",\n                                   (char*)grpname, (char*)c->argv[2]->ptr);\n            return;\n        }\n    }\n\n    /* Dispatch the different subcommands. */\n    if (!strcasecmp(opt,\"CREATE\") && (c->argc == 5 || c->argc == 6)) {\n        streamID id;\n        if (!strcmp(c->argv[4]->ptr,\"$\")) {\n            if (s) {\n                id = s->last_id;\n            } else {\n                id.ms = 0;\n                id.seq = 0;\n            }\n        } else if (streamParseStrictIDOrReply(c,c->argv[4],&id,0) != C_OK) {\n            return;\n        }\n\n        /* Handle the MKSTREAM option now that the command can no longer fail. */\n        if (s == NULL) {\n            serverAssert(mkstream);\n            o = createStreamObject();\n            dbAdd(c->db,c->argv[2],o);\n            s = o->ptr;\n        }\n\n        streamCG *cg = streamCreateCG(s,grpname,sdslen(grpname),&id);\n        if (cg) {\n            addReply(c,shared.ok);\n            server.dirty++;\n            notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-create\",\n                                c->argv[2],c->db->id);\n        } else {\n            addReplySds(c,\n                sdsnew(\"-BUSYGROUP Consumer Group name already exists\\r\\n\"));\n        }\n    } else if (!strcasecmp(opt,\"SETID\") && c->argc == 5) {\n        streamID id;\n        if (!strcmp(c->argv[4]->ptr,\"$\")) {\n            id = s->last_id;\n        } else if (streamParseIDOrReply(c,c->argv[4],&id,0) != C_OK) {\n            return;\n        }\n        cg->last_id = id;\n        addReply(c,shared.ok);\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-setid\",c->argv[2],c->db->id);\n    } else if (!strcasecmp(opt,\"DESTROY\") && c->argc == 4) {\n        if (cg) {\n            raxRemove(s->cgroups,(unsigned char*)grpname,sdslen(grpname),NULL);\n            streamFreeCG(cg);\n            addReply(c,shared.cone);\n            server.dirty++;\n            notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-destroy\",\n                                c->argv[2],c->db->id);\n            /* We want to unblock any XREADGROUP consumers with -NOGROUP. */\n            signalKeyAsReady(c->db,c->argv[2]);\n        } else {\n            addReply(c,shared.czero);\n        }\n    } else if (!strcasecmp(opt,\"DELCONSUMER\") && c->argc == 5) {\n        /* Delete the consumer and returns the number of pending messages\n         * that were yet associated with such a consumer. */\n        long long pending = streamDelConsumer(cg,c->argv[4]->ptr);\n        addReplyLongLong(c,pending);\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-delconsumer\",\n                            c->argv[2],c->db->id);\n    } else if (!strcasecmp(opt,\"HELP\")) {\n        addReplyHelp(c, help);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\n/* XSETID <stream> <groupname> <id>\n *\n * Set the internal \"last ID\" of a stream. */\nvoid xsetidCommand(client *c) {\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);\n    if (o == NULL || checkType(c,o,OBJ_STREAM)) return;\n\n    stream *s = o->ptr;\n    streamID id;\n    if (streamParseStrictIDOrReply(c,c->argv[2],&id,0) != C_OK) return;\n\n    /* If the stream has at least one item, we want to check that the user\n     * is setting a last ID that is equal or greater than the current top\n     * item, otherwise the fundamental ID monotonicity assumption is violated. */\n    if (s->length > 0) {\n        streamID maxid;\n        streamLastValidID(s,&maxid);\n\n        if (streamCompareID(&id,&maxid) < 0) {\n            addReplyError(c,\"The ID specified in XSETID is smaller than the \"\n                            \"target stream top item\");\n            return;\n        }\n    }\n    s->last_id = id;\n    addReply(c,shared.ok);\n    server.dirty++;\n    notifyKeyspaceEvent(NOTIFY_STREAM,\"xsetid\",c->argv[1],c->db->id);\n}\n\n/* XACK <key> <group> <id> <id> ... <id>\n *\n * Acknowledge a message as processed. In practical terms we just check the\n * pendine entries list (PEL) of the group, and delete the PEL entry both from\n * the group and the consumer (pending messages are referenced in both places).\n *\n * Return value of the command is the number of messages successfully\n * acknowledged, that is, the IDs we were actually able to resolve in the PEL.\n */\nvoid xackCommand(client *c) {\n    streamCG *group = NULL;\n    robj *o = lookupKeyRead(c->db,c->argv[1]);\n    if (o) {\n        if (checkType(c,o,OBJ_STREAM)) return; /* Type error. */\n        group = streamLookupCG(o->ptr,c->argv[2]->ptr);\n    }\n\n    /* No key or group? Nothing to ack. */\n    if (o == NULL || group == NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* Start parsing the IDs, so that we abort ASAP if there is a syntax\n     * error: the return value of this command cannot be an error in case\n     * the client successfully acknowledged some messages, so it should be\n     * executed in a \"all or nothing\" fashion. */\n    for (int j = 3; j < c->argc; j++) {\n        streamID id;\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;\n    }\n\n    int acknowledged = 0;\n    for (int j = 3; j < c->argc; j++) {\n        streamID id;\n        unsigned char buf[sizeof(streamID)];\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK)\n            serverPanic(\"StreamID invalid after check. Should not be possible.\");\n        streamEncodeID(buf,&id);\n\n        /* Lookup the ID in the group PEL: it will have a reference to the\n         * NACK structure that will have a reference to the consumer, so that\n         * we are able to remove the entry from both PELs. */\n        streamNACK *nack = raxFind(group->pel,buf,sizeof(buf));\n        if (nack != raxNotFound) {\n            raxRemove(group->pel,buf,sizeof(buf),NULL);\n            raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);\n            streamFreeNACK(nack);\n            acknowledged++;\n            server.dirty++;\n        }\n    }\n    addReplyLongLong(c,acknowledged);\n}\n\n/* XPENDING <key> <group> [<start> <stop> <count> [<consumer>]]\n *\n * If start and stop are omitted, the command just outputs information about\n * the amount of pending messages for the key/group pair, together with\n * the minimum and maxium ID of pending messages.\n *\n * If start and stop are provided instead, the pending messages are returned\n * with informations about the current owner, number of deliveries and last\n * delivery time and so forth. */\nvoid xpendingCommand(client *c) {\n    int justinfo = c->argc == 3; /* Without the range just outputs general\n                                    informations about the PEL. */\n    robj *key = c->argv[1];\n    robj *groupname = c->argv[2];\n    robj *consumername = (c->argc == 7) ? c->argv[6] : NULL;\n    streamID startid, endid;\n    long long count;\n\n    /* Start and stop, and the consumer, can be omitted. */\n    if (c->argc != 3 && c->argc != 6 && c->argc != 7) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Parse start/end/count arguments ASAP if needed, in order to report\n     * syntax errors before any other error. */\n    if (c->argc >= 6) {\n        if (getLongLongFromObjectOrReply(c,c->argv[5],&count,NULL) == C_ERR)\n            return;\n        if (count < 0) count = 0;\n        if (streamParseIDOrReply(c,c->argv[3],&startid,0) == C_ERR)\n            return;\n        if (streamParseIDOrReply(c,c->argv[4],&endid,UINT64_MAX) == C_ERR)\n            return;\n    }\n\n    /* Lookup the key and the group inside the stream. */\n    robj *o = lookupKeyRead(c->db,c->argv[1]);\n    streamCG *group;\n\n    if (o && checkType(c,o,OBJ_STREAM)) return;\n    if (o == NULL ||\n        (group = streamLookupCG(o->ptr,groupname->ptr)) == NULL)\n    {\n        addReplyErrorFormat(c, \"-NOGROUP No such key '%s' or consumer \"\n                               \"group '%s'\",\n                               (char*)key->ptr,(char*)groupname->ptr);\n        return;\n    }\n\n    /* XPENDING <key> <group> variant. */\n    if (justinfo) {\n        addReplyArrayLen(c,4);\n        /* Total number of messages in the PEL. */\n        addReplyLongLong(c,raxSize(group->pel));\n        /* First and last IDs. */\n        if (raxSize(group->pel) == 0) {\n            addReplyNull(c); /* Start. */\n            addReplyNull(c); /* End. */\n            addReplyNullArray(c); /* Clients. */\n        } else {\n            /* Start. */\n            raxIterator ri;\n            raxStart(&ri,group->pel);\n            raxSeek(&ri,\"^\",NULL,0);\n            raxNext(&ri);\n            streamDecodeID(ri.key,&startid);\n            addReplyStreamID(c,&startid);\n\n            /* End. */\n            raxSeek(&ri,\"$\",NULL,0);\n            raxNext(&ri);\n            streamDecodeID(ri.key,&endid);\n            addReplyStreamID(c,&endid);\n            raxStop(&ri);\n\n            /* Consumers with pending messages. */\n            raxStart(&ri,group->consumers);\n            raxSeek(&ri,\"^\",NULL,0);\n            void *arraylen_ptr = addReplyDeferredLen(c);\n            size_t arraylen = 0;\n            while(raxNext(&ri)) {\n                streamConsumer *consumer = ri.data;\n                if (raxSize(consumer->pel) == 0) continue;\n                addReplyArrayLen(c,2);\n                addReplyBulkCBuffer(c,ri.key,ri.key_len);\n                addReplyBulkLongLong(c,raxSize(consumer->pel));\n                arraylen++;\n            }\n            setDeferredArrayLen(c,arraylen_ptr,arraylen);\n            raxStop(&ri);\n        }\n    }\n    /* XPENDING <key> <group> <start> <stop> <count> [<consumer>] variant. */\n    else {\n        streamConsumer *consumer = NULL;\n        if (consumername) {\n            consumer = streamLookupConsumer(group,\n                                            consumername->ptr,\n                                            SLC_NOCREAT|SLC_NOREFRESH);\n\n            /* If a consumer name was mentioned but it does not exist, we can\n             * just return an empty array. */\n            if (consumer == NULL) {\n                addReplyArrayLen(c,0);\n                return;\n            }\n        }\n\n        rax *pel = consumer ? consumer->pel : group->pel;\n        unsigned char startkey[sizeof(streamID)];\n        unsigned char endkey[sizeof(streamID)];\n        raxIterator ri;\n        mstime_t now = mstime();\n\n        streamEncodeID(startkey,&startid);\n        streamEncodeID(endkey,&endid);\n        raxStart(&ri,pel);\n        raxSeek(&ri,\">=\",startkey,sizeof(startkey));\n        void *arraylen_ptr = addReplyDeferredLen(c);\n        size_t arraylen = 0;\n\n        while(count && raxNext(&ri) && memcmp(ri.key,endkey,ri.key_len) <= 0) {\n            streamNACK *nack = ri.data;\n\n            arraylen++;\n            count--;\n            addReplyArrayLen(c,4);\n\n            /* Entry ID. */\n            streamID id;\n            streamDecodeID(ri.key,&id);\n            addReplyStreamID(c,&id);\n\n            /* Consumer name. */\n            addReplyBulkCBuffer(c,nack->consumer->name,\n                                sdslen(nack->consumer->name));\n\n            /* Milliseconds elapsed since last delivery. */\n            mstime_t elapsed = now - nack->delivery_time;\n            if (elapsed < 0) elapsed = 0;\n            addReplyLongLong(c,elapsed);\n\n            /* Number of deliveries. */\n            addReplyLongLong(c,nack->delivery_count);\n        }\n        raxStop(&ri);\n        setDeferredArrayLen(c,arraylen_ptr,arraylen);\n    }\n}\n\n/* XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2>\n *        [IDLE <milliseconds>] [TIME <mstime>] [RETRYCOUNT <count>]\n *        [FORCE] [JUSTID]\n *\n * Gets ownership of one or multiple messages in the Pending Entries List\n * of a given stream consumer group.\n *\n * If the message ID (among the specified ones) exists, and its idle\n * time greater or equal to <min-idle-time>, then the message new owner\n * becomes the specified <consumer>. If the minimum idle time specified\n * is zero, messages are claimed regardless of their idle time.\n *\n * All the messages that cannot be found inside the pending entries list\n * are ignored, but in case the FORCE option is used. In that case we\n * create the NACK (representing a not yet acknowledged message) entry in\n * the consumer group PEL.\n *\n * This command creates the consumer as side effect if it does not yet\n * exists. Moreover the command reset the idle time of the message to 0,\n * even if by using the IDLE or TIME options, the user can control the\n * new idle time.\n *\n * The options at the end can be used in order to specify more attributes\n * to set in the representation of the pending message:\n *\n * 1. IDLE <ms>:\n *      Set the idle time (last time it was delivered) of the message.\n *      If IDLE is not specified, an IDLE of 0 is assumed, that is,\n *      the time count is reset because the message has now a new\n *      owner trying to process it.\n *\n * 2. TIME <ms-unix-time>:\n *      This is the same as IDLE but instead of a relative amount of\n *      milliseconds, it sets the idle time to a specific unix time\n *      (in milliseconds). This is useful in order to rewrite the AOF\n *      file generating XCLAIM commands.\n *\n * 3. RETRYCOUNT <count>:\n *      Set the retry counter to the specified value. This counter is\n *      incremented every time a message is delivered again. Normally\n *      XCLAIM does not alter this counter, which is just served to clients\n *      when the XPENDING command is called: this way clients can detect\n *      anomalies, like messages that are never processed for some reason\n *      after a big number of delivery attempts.\n *\n * 4. FORCE:\n *      Creates the pending message entry in the PEL even if certain\n *      specified IDs are not already in the PEL assigned to a different\n *      client. However the message must be exist in the stream, otherwise\n *      the IDs of non existing messages are ignored.\n *\n * 5. JUSTID:\n *      Return just an array of IDs of messages successfully claimed,\n *      without returning the actual message.\n *\n * 6. LASTID <id>:\n *      Update the consumer group last ID with the specified ID if the\n *      current last ID is smaller than the provided one.\n *      This is used for replication / AOF, so that when we read from a\n *      consumer group, the XCLAIM that gets propagated to give ownership\n *      to the consumer, is also used in order to update the group current\n *      ID.\n *\n * The command returns an array of messages that the user\n * successfully claimed, so that the caller is able to understand\n * what messages it is now in charge of. */\nvoid xclaimCommand(client *c) {\n    streamCG *group = NULL;\n    robj *o = lookupKeyRead(c->db,c->argv[1]);\n    long long minidle; /* Minimum idle time argument. */\n    long long retrycount = -1;   /* -1 means RETRYCOUNT option not given. */\n    mstime_t deliverytime = -1;  /* -1 means IDLE/TIME options not given. */\n    int force = 0;\n    int justid = 0;\n\n    if (o) {\n        if (checkType(c,o,OBJ_STREAM)) return; /* Type error. */\n        group = streamLookupCG(o->ptr,c->argv[2]->ptr);\n    }\n\n    /* No key or group? Send an error given that the group creation\n     * is mandatory. */\n    if (o == NULL || group == NULL) {\n        addReplyErrorFormat(c,\"-NOGROUP No such key '%s' or \"\n                              \"consumer group '%s'\", (char*)c->argv[1]->ptr,\n                              (char*)c->argv[2]->ptr);\n        return;\n    }\n\n    if (getLongLongFromObjectOrReply(c,c->argv[4],&minidle,\n        \"Invalid min-idle-time argument for XCLAIM\")\n        != C_OK) return;\n    if (minidle < 0) minidle = 0;\n\n    /* Start parsing the IDs, so that we abort ASAP if there is a syntax\n     * error: the return value of this command cannot be an error in case\n     * the client successfully claimed some message, so it should be\n     * executed in a \"all or nothing\" fashion. */\n    int j;\n    for (j = 5; j < c->argc; j++) {\n        streamID id;\n        if (streamParseStrictIDOrReply(NULL,c->argv[j],&id,0) != C_OK) break;\n    }\n    int last_id_arg = j-1; /* Next time we iterate the IDs we now the range. */\n\n    /* If we stopped because some IDs cannot be parsed, perhaps they\n     * are trailing options. */\n    mstime_t now = mstime();\n    streamID last_id = {0,0};\n    int propagate_last_id = 0;\n    for (; j < c->argc; j++) {\n        int moreargs = (c->argc-1) - j; /* Number of additional arguments. */\n        char *opt = c->argv[j]->ptr;\n        if (!strcasecmp(opt,\"FORCE\")) {\n            force = 1;\n        } else if (!strcasecmp(opt,\"JUSTID\")) {\n            justid = 1;\n        } else if (!strcasecmp(opt,\"IDLE\") && moreargs) {\n            j++;\n            if (getLongLongFromObjectOrReply(c,c->argv[j],&deliverytime,\n                \"Invalid IDLE option argument for XCLAIM\")\n                != C_OK) return;\n            deliverytime = now - deliverytime;\n        } else if (!strcasecmp(opt,\"TIME\") && moreargs) {\n            j++;\n            if (getLongLongFromObjectOrReply(c,c->argv[j],&deliverytime,\n                \"Invalid TIME option argument for XCLAIM\")\n                != C_OK) return;\n        } else if (!strcasecmp(opt,\"RETRYCOUNT\") && moreargs) {\n            j++;\n            if (getLongLongFromObjectOrReply(c,c->argv[j],&retrycount,\n                \"Invalid RETRYCOUNT option argument for XCLAIM\")\n                != C_OK) return;\n        } else if (!strcasecmp(opt,\"LASTID\") && moreargs) {\n            j++;\n            if (streamParseStrictIDOrReply(c,c->argv[j],&last_id,0) != C_OK) return;\n        } else {\n            addReplyErrorFormat(c,\"Unrecognized XCLAIM option '%s'\",opt);\n            return;\n        }\n    }\n\n    if (streamCompareID(&last_id,&group->last_id) > 0) {\n        group->last_id = last_id;\n        propagate_last_id = 1;\n    }\n\n    if (deliverytime != -1) {\n        /* If a delivery time was passed, either with IDLE or TIME, we\n         * do some sanity check on it, and set the deliverytime to now\n         * (which is a sane choice usually) if the value is bogus.\n         * To raise an error here is not wise because clients may compute\n         * the idle time doing some math starting from their local time,\n         * and this is not a good excuse to fail in case, for instance,\n         * the computer time is a bit in the future from our POV. */\n        if (deliverytime < 0 || deliverytime > now) deliverytime = now;\n    } else {\n        /* If no IDLE/TIME option was passed, we want the last delivery\n         * time to be now, so that the idle time of the message will be\n         * zero. */\n        deliverytime = now;\n    }\n\n    /* Do the actual claiming. */\n    streamConsumer *consumer = NULL;\n    void *arraylenptr = addReplyDeferredLen(c);\n    size_t arraylen = 0;\n    for (int j = 5; j <= last_id_arg; j++) {\n        streamID id;\n        unsigned char buf[sizeof(streamID)];\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK)\n            serverPanic(\"StreamID invalid after check. Should not be possible.\");\n        streamEncodeID(buf,&id);\n\n        /* Lookup the ID in the group PEL. */\n        streamNACK *nack = raxFind(group->pel,buf,sizeof(buf));\n\n        /* If FORCE is passed, let's check if at least the entry\n         * exists in the Stream. In such case, we'll crate a new\n         * entry in the PEL from scratch, so that XCLAIM can also\n         * be used to create entries in the PEL. Useful for AOF\n         * and replication of consumer groups. */\n        if (force && nack == raxNotFound) {\n            streamIterator myiterator;\n            streamIteratorStart(&myiterator,o->ptr,&id,&id,0);\n            int64_t numfields;\n            int found = 0;\n            streamID item_id;\n            if (streamIteratorGetID(&myiterator,&item_id,&numfields)) found = 1;\n            streamIteratorStop(&myiterator);\n\n            /* Item must exist for us to create a NACK for it. */\n            if (!found) continue;\n\n            /* Create the NACK. */\n            nack = streamCreateNACK(NULL);\n            raxInsert(group->pel,buf,sizeof(buf),nack,NULL);\n        }\n\n        if (nack != raxNotFound) {\n            /* We need to check if the minimum idle time requested\n             * by the caller is satisfied by this entry.\n             *\n             * Note that the nack could be created by FORCE, in this\n             * case there was no pre-existing entry and minidle should\n             * be ignored, but in that case nick->consumer is NULL. */\n            if (nack->consumer && minidle) {\n                mstime_t this_idle = now - nack->delivery_time;\n                if (this_idle < minidle) continue;\n            }\n            /* Remove the entry from the old consumer.\n             * Note that nack->consumer is NULL if we created the\n             * NACK above because of the FORCE option. */\n            if (nack->consumer)\n                raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);\n            /* Update the consumer and idle time. */\n            if (consumer == NULL)\n                consumer = streamLookupConsumer(group,c->argv[3]->ptr,SLC_NONE);\n            nack->consumer = consumer;\n            nack->delivery_time = deliverytime;\n            /* Set the delivery attempts counter if given, otherwise\n             * autoincrement unless JUSTID option provided */\n            if (retrycount >= 0) {\n                nack->delivery_count = retrycount;\n            } else if (!justid) {\n                nack->delivery_count++;\n            }\n            /* Add the entry in the new consumer local PEL. */\n            raxInsert(consumer->pel,buf,sizeof(buf),nack,NULL);\n            /* Send the reply for this entry. */\n            if (justid) {\n                addReplyStreamID(c,&id);\n            } else {\n                size_t emitted = streamReplyWithRange(c,o->ptr,&id,&id,1,0,\n                                    NULL,NULL,STREAM_RWR_RAWENTRIES,NULL);\n                if (!emitted) addReplyNull(c);\n            }\n            arraylen++;\n\n            /* Propagate this change. */\n            streamPropagateXCLAIM(c,c->argv[1],group,c->argv[2],c->argv[j],nack);\n            propagate_last_id = 0; /* Will be propagated by XCLAIM itself. */\n            server.dirty++;\n        }\n    }\n    if (propagate_last_id) {\n        streamPropagateGroupID(c,c->argv[1],group,c->argv[2]);\n        server.dirty++;\n    }\n    setDeferredArrayLen(c,arraylenptr,arraylen);\n    preventCommandPropagation(c);\n}\n\n\n/* XDEL <key> [<ID1> <ID2> ... <IDN>]\n *\n * Removes the specified entries from the stream. Returns the number\n * of items actually deleted, that may be different from the number\n * of IDs passed in case certain IDs do not exist. */\nvoid xdelCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL\n        || checkType(c,o,OBJ_STREAM)) return;\n    stream *s = o->ptr;\n\n    /* We need to sanity check the IDs passed to start. Even if not\n     * a big issue, it is not great that the command is only partially\n     * executed because at some point an invalid ID is parsed. */\n    streamID id;\n    for (int j = 2; j < c->argc; j++) {\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;\n    }\n\n    /* Actually apply the command. */\n    int deleted = 0;\n    for (int j = 2; j < c->argc; j++) {\n        streamParseStrictIDOrReply(c,c->argv[j],&id,0); /* Retval already checked. */\n        deleted += streamDeleteItem(s,&id);\n    }\n\n    /* Propagate the write if needed. */\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xdel\",c->argv[1],c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\n/* General form: XTRIM <key> [... options ...]\n *\n * List of options:\n *\n * MAXLEN [~|=] <count>     -- Trim so that the stream will be capped at\n *                             the specified length. Use ~ before the\n *                             count in order to demand approximated trimming\n *                             (like XADD MAXLEN option).\n */\n\n#define TRIM_STRATEGY_NONE 0\n#define TRIM_STRATEGY_MAXLEN 1\nvoid xtrimCommand(client *c) {\n    robj *o;\n\n    /* If the key does not exist, we are ok returning zero, that is, the\n     * number of elements removed from the stream. */\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL\n        || checkType(c,o,OBJ_STREAM)) return;\n    stream *s = o->ptr;\n\n    /* Argument parsing. */\n    int trim_strategy = TRIM_STRATEGY_NONE;\n    long long maxlen = -1;  /* If left to -1 no trimming is performed. */\n    int approx_maxlen = 0;  /* If 1 only delete whole radix tree nodes, so\n                               the maxium length is not applied verbatim. */\n    int maxlen_arg_idx = 0; /* Index of the count in MAXLEN, for rewriting. */\n\n    /* Parse options. */\n    int i = 2; /* Start of options. */\n    for (; i < c->argc; i++) {\n        int moreargs = (c->argc-1) - i; /* Number of additional arguments. */\n        char *opt = c->argv[i]->ptr;\n        if (!strcasecmp(opt,\"maxlen\") && moreargs) {\n            approx_maxlen = 0;\n            trim_strategy = TRIM_STRATEGY_MAXLEN;\n            char *next = c->argv[i+1]->ptr;\n            /* Check for the form MAXLEN ~ <count>. */\n            if (moreargs >= 2 && next[0] == '~' && next[1] == '\\0') {\n                approx_maxlen = 1;\n                i++;\n            } else if (moreargs >= 2 && next[0] == '=' && next[1] == '\\0') {\n                i++;\n            }\n            if (getLongLongFromObjectOrReply(c,c->argv[i+1],&maxlen,NULL)\n                != C_OK) return;\n\n            if (maxlen < 0) {\n                addReplyError(c,\"The MAXLEN argument must be >= 0.\");\n                return;\n            }\n            i++;\n            maxlen_arg_idx = i;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Perform the trimming. */\n    int64_t deleted = 0;\n    if (trim_strategy == TRIM_STRATEGY_MAXLEN) {\n        deleted = streamTrimByLength(s,maxlen,approx_maxlen);\n    } else {\n        addReplyError(c,\"XTRIM called without an option to trim the stream\");\n        return;\n    }\n\n    /* Propagate the write if needed. */\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xtrim\",c->argv[1],c->db->id);\n        server.dirty += deleted;\n        if (approx_maxlen) streamRewriteApproxMaxlen(c,s,maxlen_arg_idx);\n    }\n    addReplyLongLong(c,deleted);\n}\n\n/* Helper function for xinfoCommand.\n * Handles the variants of XINFO STREAM */\nvoid xinfoReplyWithStreamInfo(client *c, stream *s) {\n    int full = 1;\n    long long count = 10; /* Default COUNT is 10 so we don't block the server */\n    robj **optv = c->argv + 3; /* Options start after XINFO STREAM <key> */\n    int optc = c->argc - 3;\n\n    /* Parse options. */\n    if (optc == 0) {\n        full = 0;\n    } else {\n        /* Valid options are [FULL] or [FULL COUNT <count>] */\n        if (optc != 1 && optc != 3) {\n            addReplySubcommandSyntaxError(c);\n            return;\n        }\n\n        /* First option must be \"FULL\" */\n        if (strcasecmp(optv[0]->ptr,\"full\")) {\n            addReplySubcommandSyntaxError(c);\n            return;\n        }\n\n        if (optc == 3) {\n            /* First option must be \"FULL\" */\n            if (strcasecmp(optv[1]->ptr,\"count\")) {\n                addReplySubcommandSyntaxError(c);\n                return;\n            }\n            if (getLongLongFromObjectOrReply(c,optv[2],&count,NULL) == C_ERR)\n                return;\n            if (count < 0) count = 10;\n        }\n    }\n\n    addReplyMapLen(c,full ? 6 : 7);\n    addReplyBulkCString(c,\"length\");\n    addReplyLongLong(c,s->length);\n    addReplyBulkCString(c,\"radix-tree-keys\");\n    addReplyLongLong(c,raxSize(s->rax));\n    addReplyBulkCString(c,\"radix-tree-nodes\");\n    addReplyLongLong(c,s->rax->numnodes);\n    addReplyBulkCString(c,\"last-generated-id\");\n    addReplyStreamID(c,&s->last_id);\n\n    if (!full) {\n        /* XINFO STREAM <key> */\n\n        addReplyBulkCString(c,\"groups\");\n        addReplyLongLong(c,s->cgroups ? raxSize(s->cgroups) : 0);\n\n        /* To emit the first/last entry we use streamReplyWithRange(). */\n        int emitted;\n        streamID start, end;\n        start.ms = start.seq = 0;\n        end.ms = end.seq = UINT64_MAX;\n        addReplyBulkCString(c,\"first-entry\");\n        emitted = streamReplyWithRange(c,s,&start,&end,1,0,NULL,NULL,\n                                       STREAM_RWR_RAWENTRIES,NULL);\n        if (!emitted) addReplyNull(c);\n        addReplyBulkCString(c,\"last-entry\");\n        emitted = streamReplyWithRange(c,s,&start,&end,1,1,NULL,NULL,\n                                       STREAM_RWR_RAWENTRIES,NULL);\n        if (!emitted) addReplyNull(c);\n    } else {\n        /* XINFO STREAM <key> FULL [COUNT <count>] */\n\n        /* Stream entries */\n        addReplyBulkCString(c,\"entries\");\n        streamReplyWithRange(c,s,NULL,NULL,count,0,NULL,NULL,0,NULL);\n\n        /* Consumer groups */\n        addReplyBulkCString(c,\"groups\");\n        if (s->cgroups == NULL) {\n            addReplyArrayLen(c,0);\n        } else {\n            addReplyArrayLen(c,raxSize(s->cgroups));\n            raxIterator ri_cgroups;\n            raxStart(&ri_cgroups,s->cgroups);\n            raxSeek(&ri_cgroups,\"^\",NULL,0);\n            while(raxNext(&ri_cgroups)) {\n                streamCG *cg = ri_cgroups.data;\n                addReplyMapLen(c,5);\n\n                /* Name */\n                addReplyBulkCString(c,\"name\");\n                addReplyBulkCBuffer(c,ri_cgroups.key,ri_cgroups.key_len);\n\n                /* Last delivered ID */\n                addReplyBulkCString(c,\"last-delivered-id\");\n                addReplyStreamID(c,&cg->last_id);\n\n                /* Group PEL count */\n                addReplyBulkCString(c,\"pel-count\");\n                addReplyLongLong(c,raxSize(cg->pel));\n\n                /* Group PEL */\n                addReplyBulkCString(c,\"pending\");\n                long long arraylen_cg_pel = 0;\n                void *arrayptr_cg_pel = addReplyDeferredLen(c);\n                raxIterator ri_cg_pel;\n                raxStart(&ri_cg_pel,cg->pel);\n                raxSeek(&ri_cg_pel,\"^\",NULL,0);\n                while(raxNext(&ri_cg_pel) && (!count || arraylen_cg_pel < count)) {\n                    streamNACK *nack = ri_cg_pel.data;\n                    addReplyArrayLen(c,4);\n\n                    /* Entry ID. */\n                    streamID id;\n                    streamDecodeID(ri_cg_pel.key,&id);\n                    addReplyStreamID(c,&id);\n\n                    /* Consumer name. */\n                    addReplyBulkCBuffer(c,nack->consumer->name,\n                                        sdslen(nack->consumer->name));\n\n                    /* Last delivery. */\n                    addReplyLongLong(c,nack->delivery_time);\n\n                    /* Number of deliveries. */\n                    addReplyLongLong(c,nack->delivery_count);\n\n                    arraylen_cg_pel++;\n                }\n                setDeferredArrayLen(c,arrayptr_cg_pel,arraylen_cg_pel);\n                raxStop(&ri_cg_pel);\n\n                /* Consumers */\n                addReplyBulkCString(c,\"consumers\");\n                addReplyArrayLen(c,raxSize(cg->consumers));\n                raxIterator ri_consumers;\n                raxStart(&ri_consumers,cg->consumers);\n                raxSeek(&ri_consumers,\"^\",NULL,0);\n                while(raxNext(&ri_consumers)) {\n                    streamConsumer *consumer = ri_consumers.data;\n                    addReplyMapLen(c,4);\n\n                    /* Consumer name */\n                    addReplyBulkCString(c,\"name\");\n                    addReplyBulkCBuffer(c,consumer->name,sdslen(consumer->name));\n\n                    /* Seen-time */\n                    addReplyBulkCString(c,\"seen-time\");\n                    addReplyLongLong(c,consumer->seen_time);\n\n                    /* Consumer PEL count */\n                    addReplyBulkCString(c,\"pel-count\");\n                    addReplyLongLong(c,raxSize(consumer->pel));\n\n                    /* Consumer PEL */\n                    addReplyBulkCString(c,\"pending\");\n                    long long arraylen_cpel = 0;\n                    void *arrayptr_cpel = addReplyDeferredLen(c);\n                    raxIterator ri_cpel;\n                    raxStart(&ri_cpel,consumer->pel);\n                    raxSeek(&ri_cpel,\"^\",NULL,0);\n                    while(raxNext(&ri_cpel) && (!count || arraylen_cpel < count)) {\n                        streamNACK *nack = ri_cpel.data;\n                        addReplyArrayLen(c,3);\n\n                        /* Entry ID. */\n                        streamID id;\n                        streamDecodeID(ri_cpel.key,&id);\n                        addReplyStreamID(c,&id);\n\n                        /* Last delivery. */\n                        addReplyLongLong(c,nack->delivery_time);\n\n                        /* Number of deliveries. */\n                        addReplyLongLong(c,nack->delivery_count);\n\n                        arraylen_cpel++;\n                    }\n                    setDeferredArrayLen(c,arrayptr_cpel,arraylen_cpel);\n                    raxStop(&ri_cpel);\n                }\n                raxStop(&ri_consumers);\n            }\n            raxStop(&ri_cgroups);\n        }\n    }\n}\n\n/* XINFO CONSUMERS <key> <group>\n * XINFO GROUPS <key>\n * XINFO STREAM <key> [FULL [COUNT <count>]]\n * XINFO HELP. */\nvoid xinfoCommand(client *c) {\n    const char *help[] = {\n\"CONSUMERS <key> <groupname>         -- Show consumer groups of group <groupname>.\",\n\"GROUPS <key>                        -- Show the stream consumer groups.\",\n\"STREAM <key> [FULL [COUNT <count>]] -- Show information about the stream.\",\n\"                                       FULL will return the full state of the stream,\",\n\"                                            including all entries, groups, consumers and PELs.\",\n\"                                            It's possible to show only the first stream/PEL entries\",\n\"                                            by using the COUNT modifier (Default is 10)\",\n\"HELP                                -- Print this help.\",\nNULL\n    };\n    stream *s = NULL;\n    char *opt;\n    robj *key;\n\n    /* HELP is special. Handle it ASAP. */\n    if (!strcasecmp(c->argv[1]->ptr,\"HELP\")) {\n        addReplyHelp(c, help);\n        return;\n    } else if (c->argc < 3) {\n        addReplyError(c,\"syntax error, try 'XINFO HELP'\");\n        return;\n    }\n\n    /* With the exception of HELP handled before any other sub commands, all\n     * the ones are in the form of \"<subcommand> <key>\". */\n    opt = c->argv[1]->ptr;\n    key = c->argv[2];\n\n    /* Lookup the key now, this is common for all the subcommands but HELP. */\n    robj *o = lookupKeyWriteOrReply(c,key,shared.nokeyerr);\n    if (o == NULL || checkType(c,o,OBJ_STREAM)) return;\n    s = o->ptr;\n\n    /* Dispatch the different subcommands. */\n    if (!strcasecmp(opt,\"CONSUMERS\") && c->argc == 4) {\n        /* XINFO CONSUMERS <key> <group>. */\n        streamCG *cg = streamLookupCG(s,c->argv[3]->ptr);\n        if (cg == NULL) {\n            addReplyErrorFormat(c, \"-NOGROUP No such consumer group '%s' \"\n                                   \"for key name '%s'\",\n                                   (char*)c->argv[3]->ptr, (char*)key->ptr);\n            return;\n        }\n\n        addReplyArrayLen(c,raxSize(cg->consumers));\n        raxIterator ri;\n        raxStart(&ri,cg->consumers);\n        raxSeek(&ri,\"^\",NULL,0);\n        mstime_t now = mstime();\n        while(raxNext(&ri)) {\n            streamConsumer *consumer = ri.data;\n            mstime_t idle = now - consumer->seen_time;\n            if (idle < 0) idle = 0;\n\n            addReplyMapLen(c,3);\n            addReplyBulkCString(c,\"name\");\n            addReplyBulkCBuffer(c,consumer->name,sdslen(consumer->name));\n            addReplyBulkCString(c,\"pending\");\n            addReplyLongLong(c,raxSize(consumer->pel));\n            addReplyBulkCString(c,\"idle\");\n            addReplyLongLong(c,idle);\n        }\n        raxStop(&ri);\n    } else if (!strcasecmp(opt,\"GROUPS\") && c->argc == 3) {\n        /* XINFO GROUPS <key>. */\n        if (s->cgroups == NULL) {\n            addReplyArrayLen(c,0);\n            return;\n        }\n\n        addReplyArrayLen(c,raxSize(s->cgroups));\n        raxIterator ri;\n        raxStart(&ri,s->cgroups);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            streamCG *cg = ri.data;\n            addReplyMapLen(c,4);\n            addReplyBulkCString(c,\"name\");\n            addReplyBulkCBuffer(c,ri.key,ri.key_len);\n            addReplyBulkCString(c,\"consumers\");\n            addReplyLongLong(c,raxSize(cg->consumers));\n            addReplyBulkCString(c,\"pending\");\n            addReplyLongLong(c,raxSize(cg->pel));\n            addReplyBulkCString(c,\"last-delivered-id\");\n            addReplyStreamID(c,&cg->last_id);\n        }\n        raxStop(&ri);\n    } else if (!strcasecmp(opt,\"STREAM\")) {\n        /* XINFO STREAM <key> [FULL [COUNT <count>]]. */\n        xinfoReplyWithStreamInfo(c,s);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"server.h\"\n#include <math.h> /* isnan(), isinf() */\n\n/*-----------------------------------------------------------------------------\n * String Commands\n *----------------------------------------------------------------------------*/\n\nstatic int checkStringLength(client *c, long long size) {\n    if (size > 512*1024*1024) {\n        addReplyError(c,\"string exceeds maximum allowed size (512MB)\");\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* The setGenericCommand() function implements the SET operation with different\n * options and variants. This function is called in order to implement the\n * following commands: SET, SETEX, PSETEX, SETNX.\n *\n * 'flags' changes the behavior of the command (NX or XX, see below).\n *\n * 'expire' represents an expire to set in form of a Redis object as passed\n * by the user. It is interpreted according to the specified 'unit'.\n *\n * 'ok_reply' and 'abort_reply' is what the function will reply to the client\n * if the operation is performed, or when it is not because of NX or\n * XX flags.\n *\n * If ok_reply is NULL \"+OK\" is used.\n * If abort_reply is NULL, \"$-1\" is used. */\n\n#define OBJ_SET_NO_FLAGS 0\n#define OBJ_SET_NX (1<<0)          /* Set if key not exists. */\n#define OBJ_SET_XX (1<<1)          /* Set if key exists. */\n#define OBJ_SET_EX (1<<2)          /* Set if time in seconds is given */\n#define OBJ_SET_PX (1<<3)          /* Set if time in ms in given */\n#define OBJ_SET_KEEPTTL (1<<4)     /* Set and keep the ttl */\n\nvoid setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {\n    long long milliseconds = 0; /* initialized to avoid any harmness warning */\n\n    if (expire) {\n        if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK)\n            return;\n        if (milliseconds <= 0) {\n            addReplyErrorFormat(c,\"invalid expire time in %s\",c->cmd->name);\n            return;\n        }\n        if (unit == UNIT_SECONDS) milliseconds *= 1000;\n    }\n\n    if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||\n        (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))\n    {\n        addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);\n        return;\n    }\n    genericSetKey(c,c->db,key,val,flags & OBJ_SET_KEEPTTL,1);\n    server.dirty++;\n    if (expire) setExpire(c,c->db,key,mstime()+milliseconds);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"set\",key,c->db->id);\n    if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,\n        \"expire\",key,c->db->id);\n    addReply(c, ok_reply ? ok_reply : shared.ok);\n}\n\n/* SET key value [NX] [XX] [KEEPTTL] [EX <seconds>] [PX <milliseconds>] */\nvoid setCommand(client *c) {\n    int j;\n    robj *expire = NULL;\n    int unit = UNIT_SECONDS;\n    int flags = OBJ_SET_NO_FLAGS;\n\n    for (j = 3; j < c->argc; j++) {\n        char *a = c->argv[j]->ptr;\n        robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];\n\n        if ((a[0] == 'n' || a[0] == 'N') &&\n            (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n            !(flags & OBJ_SET_XX))\n        {\n            flags |= OBJ_SET_NX;\n        } else if ((a[0] == 'x' || a[0] == 'X') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_NX))\n        {\n            flags |= OBJ_SET_XX;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"KEEPTTL\") &&\n                   !(flags & OBJ_SET_EX) && !(flags & OBJ_SET_PX))\n        {\n            flags |= OBJ_SET_KEEPTTL;\n        } else if ((a[0] == 'e' || a[0] == 'E') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_KEEPTTL) &&\n                   !(flags & OBJ_SET_PX) && next)\n        {\n            flags |= OBJ_SET_EX;\n            unit = UNIT_SECONDS;\n            expire = next;\n            j++;\n        } else if ((a[0] == 'p' || a[0] == 'P') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_KEEPTTL) &&\n                   !(flags & OBJ_SET_EX) && next)\n        {\n            flags |= OBJ_SET_PX;\n            unit = UNIT_MILLISECONDS;\n            expire = next;\n            j++;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);\n}\n\nvoid setnxCommand(client *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,OBJ_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);\n}\n\nvoid setexCommand(client *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);\n}\n\nvoid psetexCommand(client *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);\n}\n\nint getGenericCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL)\n        return C_OK;\n\n    if (o->type != OBJ_STRING) {\n        addReply(c,shared.wrongtypeerr);\n        return C_ERR;\n    } else {\n        addReplyBulk(c,o);\n        return C_OK;\n    }\n}\n\nvoid getCommand(client *c) {\n    getGenericCommand(c);\n}\n\nvoid getsetCommand(client *c) {\n    if (getGenericCommand(c) == C_ERR) return;\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setKey(c,c->db,c->argv[1],c->argv[2]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[1],c->db->id);\n    server.dirty++;\n}\n\nvoid setrangeCommand(client *c) {\n    robj *o;\n    long offset;\n    sds value = c->argv[3]->ptr;\n\n    if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != C_OK)\n        return;\n\n    if (offset < 0) {\n        addReplyError(c,\"offset is out of range\");\n        return;\n    }\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n        /* Return 0 when setting nothing on a non-existing string */\n        if (sdslen(value) == 0) {\n            addReply(c,shared.czero);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        if (checkStringLength(c,offset+sdslen(value)) != C_OK)\n            return;\n\n        o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        size_t olen;\n\n        /* Key exists, check type */\n        if (checkType(c,o,OBJ_STRING))\n            return;\n\n        /* Return existing string length when setting nothing */\n        olen = stringObjectLen(o);\n        if (sdslen(value) == 0) {\n            addReplyLongLong(c,olen);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        if (checkStringLength(c,offset+sdslen(value)) != C_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    if (sdslen(value) > 0) {\n        o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));\n        memcpy((char*)o->ptr+offset,value,sdslen(value));\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\n            \"setrange\",c->argv[1],c->db->id);\n        server.dirty++;\n    }\n    addReplyLongLong(c,sdslen(o->ptr));\n}\n\nvoid getrangeCommand(client *c) {\n    robj *o;\n    long long start, end;\n    char *str, llbuf[32];\n    size_t strlen;\n\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)\n        return;\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)\n        return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n\n    if (o->encoding == OBJ_ENCODING_INT) {\n        str = llbuf;\n        strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);\n    } else {\n        str = o->ptr;\n        strlen = sdslen(str);\n    }\n\n    /* Convert negative indexes */\n    if (start < 0 && end < 0 && start > end) {\n        addReply(c,shared.emptybulk);\n        return;\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 long long)end >= strlen) end = strlen-1;\n\n    /* Precondition: end >= 0 && end < strlen, so the only condition where\n     * nothing can be returned is: start > end. */\n    if (start > end || strlen == 0) {\n        addReply(c,shared.emptybulk);\n    } else {\n        addReplyBulkCBuffer(c,(char*)str+start,end-start+1);\n    }\n}\n\nvoid mgetCommand(client *c) {\n    int j;\n\n    addReplyArrayLen(c,c->argc-1);\n    for (j = 1; j < c->argc; j++) {\n        robj *o = lookupKeyRead(c->db,c->argv[j]);\n        if (o == NULL) {\n            addReplyNull(c);\n        } else {\n            if (o->type != OBJ_STRING) {\n                addReplyNull(c);\n            } else {\n                addReplyBulk(c,o);\n            }\n        }\n    }\n}\n\nvoid msetGenericCommand(client *c, int nx) {\n    int j;\n\n    if ((c->argc % 2) == 0) {\n        addReplyError(c,\"wrong number of arguments for MSET\");\n        return;\n    }\n\n    /* Handle the NX flag. The MSETNX semantic is to return zero and don't\n     * set anything if at least one key alerady exists. */\n    if (nx) {\n        for (j = 1; j < c->argc; j += 2) {\n            if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {\n                addReply(c, shared.czero);\n                return;\n            }\n        }\n    }\n\n    for (j = 1; j < c->argc; j += 2) {\n        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);\n        setKey(c,c->db,c->argv[j],c->argv[j+1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[j],c->db->id);\n    }\n    server.dirty += (c->argc-1)/2;\n    addReply(c, nx ? shared.cone : shared.ok);\n}\n\nvoid msetCommand(client *c) {\n    msetGenericCommand(c,0);\n}\n\nvoid msetnxCommand(client *c) {\n    msetGenericCommand(c,1);\n}\n\nvoid incrDecrCommand(client *c, long long incr) {\n    long long value, oldvalue;\n    robj *o, *new;\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o != NULL && checkType(c,o,OBJ_STRING)) return;\n    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;\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    value += incr;\n\n    if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&\n        (value < 0 || value >= OBJ_SHARED_INTEGERS) &&\n        value >= LONG_MIN && value <= LONG_MAX)\n    {\n        new = o;\n        o->ptr = (void*)((long)value);\n    } else {\n        new = createStringObjectFromLongLongForValue(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,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"incrby\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c,shared.colon);\n    addReply(c,new);\n    addReply(c,shared.crlf);\n}\n\nvoid incrCommand(client *c) {\n    incrDecrCommand(c,1);\n}\n\nvoid decrCommand(client *c) {\n    incrDecrCommand(c,-1);\n}\n\nvoid incrbyCommand(client *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;\n    incrDecrCommand(c,incr);\n}\n\nvoid decrbyCommand(client *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;\n    incrDecrCommand(c,-incr);\n}\n\nvoid incrbyfloatCommand(client *c) {\n    long double incr, value;\n    robj *o, *new, *aux1, *aux2;\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o != NULL && checkType(c,o,OBJ_STRING)) return;\n    if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != C_OK ||\n        getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != C_OK)\n        return;\n\n    value += incr;\n    if (isnan(value) || isinf(value)) {\n        addReplyError(c,\"increment would produce NaN or Infinity\");\n        return;\n    }\n    new = createStringObjectFromLongDouble(value,1);\n    if (o)\n        dbOverwrite(c->db,c->argv[1],new);\n    else\n        dbAdd(c->db,c->argv[1],new);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"incrbyfloat\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReplyBulk(c,new);\n\n    /* Always replicate INCRBYFLOAT as a SET command with the final value\n     * in order to make sure that differences in float precision or formatting\n     * will not create differences in replicas or after an AOF restart. */\n    aux1 = createStringObject(\"SET\",3);\n    rewriteClientCommandArgument(c,0,aux1);\n    decrRefCount(aux1);\n    rewriteClientCommandArgument(c,2,new);\n    aux2 = createStringObject(\"KEEPTTL\",7);\n    rewriteClientCommandArgument(c,3,aux2);\n    decrRefCount(aux2);\n}\n\nvoid appendCommand(client *c) {\n    size_t totlen;\n    robj *o, *append;\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n        /* Create the key */\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        /* Key exists, check type */\n        if (checkType(c,o,OBJ_STRING))\n            return;\n\n        /* \"append\" is an argument, so always an sds */\n        append = c->argv[2];\n        totlen = stringObjectLen(o)+sdslen(append->ptr);\n        if (checkStringLength(c,totlen) != C_OK)\n            return;\n\n        /* Append the value */\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n        o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));\n        totlen = sdslen(o->ptr);\n    }\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"append\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReplyLongLong(c,totlen);\n}\n\nvoid strlenCommand(client *c) {\n    robj *o;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n    addReplyLongLong(c,stringObjectLen(o));\n}\n\n\n/* STRALGO -- Implement complex algorithms on strings.\n *\n * STRALGO <algorithm> ... arguments ... */\nvoid stralgoLCS(client *c);     /* This implements the LCS algorithm. */\nvoid stralgoCommand(client *c) {\n    /* Select the algorithm. */\n    if (!strcasecmp(c->argv[1]->ptr,\"lcs\")) {\n        stralgoLCS(c);\n    } else {\n        addReply(c,shared.syntaxerr);\n    }\n}\n\n/* STRALGO <algo> [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN]\n *     STRINGS <string> <string> | KEYS <keya> <keyb>\n */\nvoid stralgoLCS(client *c) {\n    uint32_t i, j;\n    long long minmatchlen = 0;\n    sds a = NULL, b = NULL;\n    int getlen = 0, getidx = 0, withmatchlen = 0;\n    robj *obja = NULL, *objb = NULL;\n\n    for (j = 2; j < (uint32_t)c->argc; j++) {\n        char *opt = c->argv[j]->ptr;\n        int moreargs = (c->argc-1) - j;\n\n        if (!strcasecmp(opt,\"IDX\")) {\n            getidx = 1;\n        } else if (!strcasecmp(opt,\"LEN\")) {\n            getlen = 1;\n        } else if (!strcasecmp(opt,\"WITHMATCHLEN\")) {\n            withmatchlen = 1;\n        } else if (!strcasecmp(opt,\"MINMATCHLEN\") && moreargs) {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&minmatchlen,NULL)\n                != C_OK) return;\n            if (minmatchlen < 0) minmatchlen = 0;\n            j++;\n        } else if (!strcasecmp(opt,\"STRINGS\")) {\n            if (a != NULL) {\n                addReplyError(c,\"Either use STRINGS or KEYS\");\n                return;\n            }\n            a = c->argv[j+1]->ptr;\n            b = c->argv[j+2]->ptr;\n            j += 2;\n        } else if (!strcasecmp(opt,\"KEYS\")) {\n            if (a != NULL) {\n                addReplyError(c,\"Either use STRINGS or KEYS\");\n                return;\n            }\n            obja = lookupKeyRead(c->db,c->argv[j+1]);\n            objb = lookupKeyRead(c->db,c->argv[j+2]);\n            obja = obja ? getDecodedObject(obja) : createStringObject(\"\",0);\n            objb = objb ? getDecodedObject(objb) : createStringObject(\"\",0);\n            a = obja->ptr;\n            b = objb->ptr;\n            j += 2;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Complain if the user passed ambiguous parameters. */\n    if (a == NULL) {\n        addReplyError(c,\"Please specify two strings: \"\n                        \"STRINGS or KEYS options are mandatory\");\n        return;\n    } else if (getlen && getidx) {\n        addReplyError(c,\n            \"If you want both the length and indexes, please \"\n            \"just use IDX.\");\n        return;\n    }\n\n    /* Compute the LCS using the vanilla dynamic programming technique of\n     * building a table of LCS(x,y) substrings. */\n    uint32_t alen = sdslen(a);\n    uint32_t blen = sdslen(b);\n\n    /* Setup an uint32_t array to store at LCS[i,j] the length of the\n     * LCS A0..i-1, B0..j-1. Note that we have a linear array here, so\n     * we index it as LCS[j+(blen+1)*j] */\n    uint32_t *lcs = zmalloc((alen+1)*(blen+1)*sizeof(uint32_t));\n    #define LCS(A,B) lcs[(B)+((A)*(blen+1))]\n\n    /* Start building the LCS table. */\n    for (uint32_t i = 0; i <= alen; i++) {\n        for (uint32_t j = 0; j <= blen; j++) {\n            if (i == 0 || j == 0) {\n                /* If one substring has length of zero, the\n                 * LCS length is zero. */\n                LCS(i,j) = 0;\n            } else if (a[i-1] == b[j-1]) {\n                /* The len LCS (and the LCS itself) of two\n                 * sequences with the same final character, is the\n                 * LCS of the two sequences without the last char\n                 * plus that last char. */\n                LCS(i,j) = LCS(i-1,j-1)+1;\n            } else {\n                /* If the last character is different, take the longest\n                 * between the LCS of the first string and the second\n                 * minus the last char, and the reverse. */\n                uint32_t lcs1 = LCS(i-1,j);\n                uint32_t lcs2 = LCS(i,j-1);\n                LCS(i,j) = lcs1 > lcs2 ? lcs1 : lcs2;\n            }\n        }\n    }\n\n    /* Store the actual LCS string in \"result\" if needed. We create\n     * it backward, but the length is already known, we store it into idx. */\n    uint32_t idx = LCS(alen,blen);\n    sds result = NULL;        /* Resulting LCS string. */\n    void *arraylenptr = NULL; /* Deffered length of the array for IDX. */\n    uint32_t arange_start = alen, /* alen signals that values are not set. */\n             arange_end = 0,\n             brange_start = 0,\n             brange_end = 0;\n\n    /* Do we need to compute the actual LCS string? Allocate it in that case. */\n    int computelcs = getidx || !getlen;\n    if (computelcs) result = sdsnewlen(SDS_NOINIT,idx);\n\n    /* Start with a deferred array if we have to emit the ranges. */\n    uint32_t arraylen = 0;  /* Number of ranges emitted in the array. */\n    if (getidx) {\n        addReplyMapLen(c,2);\n        addReplyBulkCString(c,\"matches\");\n        arraylenptr = addReplyDeferredLen(c);\n    }\n\n    i = alen, j = blen;\n    while (computelcs && i > 0 && j > 0) {\n        int emit_range = 0;\n        if (a[i-1] == b[j-1]) {\n            /* If there is a match, store the character and reduce\n             * the indexes to look for a new match. */\n            result[idx-1] = a[i-1];\n\n            /* Track the current range. */\n            if (arange_start == alen) {\n                arange_start = i-1;\n                arange_end = i-1;\n                brange_start = j-1;\n                brange_end = j-1;\n            } else {\n                /* Let's see if we can extend the range backward since\n                 * it is contiguous. */\n                if (arange_start == i && brange_start == j) {\n                    arange_start--;\n                    brange_start--;\n                } else {\n                    emit_range = 1;\n                }\n            }\n            /* Emit the range if we matched with the first byte of\n             * one of the two strings. We'll exit the loop ASAP. */\n            if (arange_start == 0 || brange_start == 0) emit_range = 1;\n            idx--; i--; j--;\n        } else {\n            /* Otherwise reduce i and j depending on the largest\n             * LCS between, to understand what direction we need to go. */\n            uint32_t lcs1 = LCS(i-1,j);\n            uint32_t lcs2 = LCS(i,j-1);\n            if (lcs1 > lcs2)\n                i--;\n            else\n                j--;\n            if (arange_start != alen) emit_range = 1;\n        }\n\n        /* Emit the current range if needed. */\n        uint32_t match_len = arange_end - arange_start + 1;\n        if (emit_range) {\n            if (minmatchlen == 0 || match_len >= minmatchlen) {\n                if (arraylenptr) {\n                    addReplyArrayLen(c,2+withmatchlen);\n                    addReplyArrayLen(c,2);\n                    addReplyLongLong(c,arange_start);\n                    addReplyLongLong(c,arange_end);\n                    addReplyArrayLen(c,2);\n                    addReplyLongLong(c,brange_start);\n                    addReplyLongLong(c,brange_end);\n                    if (withmatchlen) addReplyLongLong(c,match_len);\n                    arraylen++;\n                }\n            }\n            arange_start = alen; /* Restart at the next match. */\n        }\n    }\n\n    /* Signal modified key, increment dirty, ... */\n\n    /* Reply depending on the given options. */\n    if (arraylenptr) {\n        addReplyBulkCString(c,\"len\");\n        addReplyLongLong(c,LCS(alen,blen));\n        setDeferredArrayLen(c,arraylenptr,arraylen);\n    } else if (getlen) {\n        addReplyLongLong(c,LCS(alen,blen));\n    } else {\n        addReplyBulkSds(c,result);\n        result = NULL;\n    }\n\n    /* Cleanup. */\n    if (obja) decrRefCount(obja);\n    if (objb) decrRefCount(objb);\n    sdsfree(result);\n    zfree(lcs);\n    return;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 * 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 * Note that the SDS string representing the element is the same in both\n * the hash table and skiplist in order to save memory. What we do in order\n * to manage the shared SDS string more easily is to free the SDS string\n * only in zslFreeNode(). The dictionary has no value free method set.\n * So we should always remove an element from the dictionary, and later from\n * the skiplist.\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 * a) this implementation allows for repeated scores.\n * b) the comparison is not just by key (our 'score') but by satellite data.\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#include \"server.h\"\n#include <math.h>\n\n/*-----------------------------------------------------------------------------\n * Skiplist implementation of the low level API\n *----------------------------------------------------------------------------*/\n\nint zslLexValueGteMin(sds value, zlexrangespec *spec);\nint zslLexValueLteMax(sds value, zlexrangespec *spec);\n\n/* Create a skiplist node with the specified number of levels.\n * The SDS string 'ele' is referenced by the node after the call. */\nzskiplistNode *zslCreateNode(int level, double score, sds ele) {\n    zskiplistNode *zn =\n        zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));\n    zn->score = score;\n    zn->ele = ele;\n    return zn;\n}\n\n/* Create a new skiplist. */\nzskiplist *zslCreate(void) {\n    int j;\n    zskiplist *zsl;\n\n    zsl = zmalloc(sizeof(*zsl));\n    zsl->level = 1;\n    zsl->length = 0;\n    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);\n    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {\n        zsl->header->level[j].forward = NULL;\n        zsl->header->level[j].span = 0;\n    }\n    zsl->header->backward = NULL;\n    zsl->tail = NULL;\n    return zsl;\n}\n\n/* Free the specified skiplist node. The referenced SDS string representation\n * of the element is freed too, unless node->ele is set to NULL before calling\n * this function. */\nvoid zslFreeNode(zskiplistNode *node) {\n    sdsfree(node->ele);\n    zfree(node);\n}\n\n/* Free a whole skiplist. */\nvoid zslFree(zskiplist *zsl) {\n    zskiplistNode *node = zsl->header->level[0].forward, *next;\n\n    zfree(zsl->header);\n    while(node) {\n        next = node->level[0].forward;\n        zslFreeNode(node);\n        node = next;\n    }\n    zfree(zsl);\n}\n\n/* Returns a random level for the new skiplist node we are going to create.\n * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL\n * (both inclusive), with a powerlaw-alike distribution where higher\n * levels are less likely to be returned. */\nint zslRandomLevel(void) {\n    int level = 1;\n    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))\n        level += 1;\n    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;\n}\n\n/* Insert a new node in the skiplist. Assumes the element does not already\n * exist (up to the caller to enforce that). The skiplist takes ownership\n * of the passed SDS string 'ele'. */\nzskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned int rank[ZSKIPLIST_MAXLEVEL];\n    int i, level;\n\n    serverAssert(!isnan(score));\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* store rank that is crossed to reach the insert position */\n        rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];\n        while (x->level[i].forward &&\n                (x->level[i].forward->score < score ||\n                    (x->level[i].forward->score == score &&\n                    sdscmp(x->level[i].forward->ele,ele) < 0)))\n        {\n            rank[i] += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n    /* we assume the element is not already inside, since we allow duplicated\n     * scores, reinserting the same element should never happen since the\n     * caller of zslInsert() should test in the hash table if the element is\n     * already inside or not. */\n    level = zslRandomLevel();\n    if (level > zsl->level) {\n        for (i = zsl->level; i < level; i++) {\n            rank[i] = 0;\n            update[i] = zsl->header;\n            update[i]->level[i].span = zsl->length;\n        }\n        zsl->level = level;\n    }\n    x = zslCreateNode(level,score,ele);\n    for (i = 0; i < level; i++) {\n        x->level[i].forward = update[i]->level[i].forward;\n        update[i]->level[i].forward = x;\n\n        /* update span covered by update[i] as x is inserted here */\n        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);\n        update[i]->level[i].span = (rank[0] - rank[i]) + 1;\n    }\n\n    /* increment span for untouched levels */\n    for (i = level; i < zsl->level; i++) {\n        update[i]->level[i].span++;\n    }\n\n    x->backward = (update[0] == zsl->header) ? NULL : update[0];\n    if (x->level[0].forward)\n        x->level[0].forward->backward = x;\n    else\n        zsl->tail = x;\n    zsl->length++;\n    return x;\n}\n\n/* Internal function used by zslDelete, zslDeleteRangeByScore and\n * zslDeleteRangeByRank. */\nvoid zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {\n    int i;\n    for (i = 0; i < zsl->level; i++) {\n        if (update[i]->level[i].forward == x) {\n            update[i]->level[i].span += x->level[i].span - 1;\n            update[i]->level[i].forward = x->level[i].forward;\n        } else {\n            update[i]->level[i].span -= 1;\n        }\n    }\n    if (x->level[0].forward) {\n        x->level[0].forward->backward = x->backward;\n    } else {\n        zsl->tail = x->backward;\n    }\n    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)\n        zsl->level--;\n    zsl->length--;\n}\n\n/* Delete an element with matching score/element from the skiplist.\n * The function returns 1 if the node was found and deleted, otherwise\n * 0 is returned.\n *\n * If 'node' is NULL the deleted node is freed by zslFreeNode(), otherwise\n * it is not freed (but just unlinked) and *node is set to the node pointer,\n * so that it is possible for the caller to reuse the node (including the\n * referenced SDS string at node->ele). */\nint zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n                (x->level[i].forward->score < score ||\n                    (x->level[i].forward->score == score &&\n                     sdscmp(x->level[i].forward->ele,ele) < 0)))\n        {\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n    /* We may have multiple elements with the same score, what we need\n     * is to find the element with both the right score and object. */\n    x = x->level[0].forward;\n    if (x && score == x->score && sdscmp(x->ele,ele) == 0) {\n        zslDeleteNode(zsl, x, update);\n        if (!node)\n            zslFreeNode(x);\n        else\n            *node = x;\n        return 1;\n    }\n    return 0; /* not found */\n}\n\n/* Update the score of an elmenent inside the sorted set skiplist.\n * Note that the element must exist and must match 'score'.\n * This function does not update the score in the hash table side, the\n * caller should take care of it.\n *\n * Note that this function attempts to just update the node, in case after\n * the score update, the node would be exactly at the same position.\n * Otherwise the skiplist is modified by removing and re-adding a new\n * element, which is more costly.\n *\n * The function returns the updated element skiplist node pointer. */\nzskiplistNode *zslUpdateScore(zskiplist *zsl, double curscore, sds ele, double newscore) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    int i;\n\n    /* We need to seek to element to update to start: this is useful anyway,\n     * we'll have to update or remove it. */\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n                (x->level[i].forward->score < curscore ||\n                    (x->level[i].forward->score == curscore &&\n                     sdscmp(x->level[i].forward->ele,ele) < 0)))\n        {\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n\n    /* Jump to our element: note that this function assumes that the\n     * element with the matching score exists. */\n    x = x->level[0].forward;\n    serverAssert(x && curscore == x->score && sdscmp(x->ele,ele) == 0);\n\n    /* If the node, after the score update, would be still exactly\n     * at the same position, we can just update the score without\n     * actually removing and re-inserting the element in the skiplist. */\n    if ((x->backward == NULL || x->backward->score < newscore) &&\n        (x->level[0].forward == NULL || x->level[0].forward->score > newscore))\n    {\n        x->score = newscore;\n        return x;\n    }\n\n    /* No way to reuse the old node: we need to remove and insert a new\n     * one at a different place. */\n    zslDeleteNode(zsl, x, update);\n    zskiplistNode *newnode = zslInsert(zsl,newscore,x->ele);\n    /* We reused the old node x->ele SDS string, free the node now\n     * since zslInsert created a new one. */\n    x->ele = NULL;\n    zslFreeNode(x);\n    return newnode;\n}\n\nint zslValueGteMin(double value, zrangespec *spec) {\n    return spec->minex ? (value > spec->min) : (value >= spec->min);\n}\n\nint zslValueLteMax(double value, zrangespec *spec) {\n    return spec->maxex ? (value < spec->max) : (value <= spec->max);\n}\n\n/* Returns if there is a part of the zset is in range. */\nint zslIsInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n\n    /* Test for ranges that will always be empty. */\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n    x = zsl->tail;\n    if (x == NULL || !zslValueGteMin(x->score,range))\n        return 0;\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslValueLteMax(x->score,range))\n        return 0;\n    return 1;\n}\n\n/* Find the first node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *OUT* of range. */\n        while (x->level[i].forward &&\n            !zslValueGteMin(x->level[i].forward->score,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so the next node cannot be NULL. */\n    x = x->level[0].forward;\n    serverAssert(x != NULL);\n\n    /* Check if score <= max. */\n    if (!zslValueLteMax(x->score,range)) return NULL;\n    return x;\n}\n\n/* Find the last node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *IN* range. */\n        while (x->level[i].forward &&\n            zslValueLteMax(x->level[i].forward->score,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so this node cannot be NULL. */\n    serverAssert(x != NULL);\n\n    /* Check if score >= min. */\n    if (!zslValueGteMin(x->score,range)) return NULL;\n    return x;\n}\n\n/* Delete all the elements with score between min and max from the skiplist.\n * Min and max are inclusive, so a score >= min || score <= max is deleted.\n * Note that this function takes the reference to the hash table view of the\n * sorted set, in order to remove the elements from the hash table too. */\nunsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long removed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (range->minex ?\n            x->level[i].forward->score <= range->min :\n            x->level[i].forward->score < range->min))\n                x = x->level[i].forward;\n        update[i] = x;\n    }\n\n    /* Current node is the last with score < or <= min. */\n    x = x->level[0].forward;\n\n    /* Delete nodes while in range. */\n    while (x &&\n           (range->maxex ? x->score < range->max : x->score <= range->max))\n    {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->ele);\n        zslFreeNode(x); /* Here is where x->ele is actually released. */\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->ele,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->ele,range)) {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->ele);\n        zslFreeNode(x); /* Here is where x->ele is actually released. */\n        removed++;\n        x = next;\n    }\n    return removed;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n * Start and end are inclusive. Note that start and end need to be 1-based */\nunsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long traversed = 0, removed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (traversed + x->level[i].span) < start) {\n            traversed += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n\n    traversed++;\n    x = x->level[0].forward;\n    while (x && traversed <= end) {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->ele);\n        zslFreeNode(x);\n        removed++;\n        traversed++;\n        x = next;\n    }\n    return removed;\n}\n\n/* Find the rank for an element by both score and key.\n * Returns 0 when the element cannot be found, rank otherwise.\n * Note that the rank is 1-based due to the span of zsl->header to the\n * first element. */\nunsigned long zslGetRank(zskiplist *zsl, double score, sds ele) {\n    zskiplistNode *x;\n    unsigned long rank = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                sdscmp(x->level[i].forward->ele,ele) <= 0))) {\n            rank += x->level[i].span;\n            x = x->level[i].forward;\n        }\n\n        /* x might be equal to zsl->header, so test if obj is non-NULL */\n        if (x->ele && sdscmp(x->ele,ele) == 0) {\n            return rank;\n        }\n    }\n    return 0;\n}\n\n/* Finds an element by its rank. The rank argument needs to be 1-based. */\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {\n    zskiplistNode *x;\n    unsigned long traversed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (traversed + x->level[i].span) <= rank)\n        {\n            traversed += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        if (traversed == rank) {\n            return x;\n        }\n    }\n    return NULL;\n}\n\n/* Populate the rangespec according to the objects min and max. */\nstatic int zslParseRange(robj *min, robj *max, zrangespec *spec) {\n    char *eptr;\n    spec->minex = spec->maxex = 0;\n\n    /* Parse the min-max interval. If one of the values is prefixed\n     * by the \"(\" character, it's considered \"open\". For instance\n     * ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max\n     * ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */\n    if (min->encoding == OBJ_ENCODING_INT) {\n        spec->min = (long)min->ptr;\n    } else {\n        if (((char*)min->ptr)[0] == '(') {\n            spec->min = strtod((char*)min->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return C_ERR;\n            spec->minex = 1;\n        } else {\n            spec->min = strtod((char*)min->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return C_ERR;\n        }\n    }\n    if (max->encoding == OBJ_ENCODING_INT) {\n        spec->max = (long)max->ptr;\n    } else {\n        if (((char*)max->ptr)[0] == '(') {\n            spec->max = strtod((char*)max->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return C_ERR;\n            spec->maxex = 1;\n        } else {\n            spec->max = strtod((char*)max->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return C_ERR;\n        }\n    }\n\n    return C_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 comparison, and ex will be set to 0 or 1\n  * respectively if the item is exclusive or inclusive. C_OK will be\n  * returned.\n  *\n  * If the string is not a valid range C_ERR is returned, and the value\n  * of *dest and *ex is undefined. */\nint zslParseLexRangeItem(robj *item, sds *dest, int *ex) {\n    char *c = item->ptr;\n\n    switch(c[0]) {\n    case '+':\n        if (c[1] != '\\0') return C_ERR;\n        *ex = 1;\n        *dest = shared.maxstring;\n        return C_OK;\n    case '-':\n        if (c[1] != '\\0') return C_ERR;\n        *ex = 1;\n        *dest = shared.minstring;\n        return C_OK;\n    case '(':\n        *ex = 1;\n        *dest = sdsnewlen(c+1,sdslen(c)-1);\n        return C_OK;\n    case '[':\n        *ex = 0;\n        *dest = sdsnewlen(c+1,sdslen(c)-1);\n        return C_OK;\n    default:\n        return C_ERR;\n    }\n}\n\n/* Free a lex range structure, must be called only after zelParseLexRange()\n * populated the structure with success (C_OK returned). */\nvoid zslFreeLexRange(zlexrangespec *spec) {\n    if (spec->min != shared.minstring &&\n        spec->min != shared.maxstring) sdsfree(spec->min);\n    if (spec->max != shared.minstring &&\n        spec->max != shared.maxstring) sdsfree(spec->max);\n}\n\n/* Populate the lex rangespec according to the objects min and max.\n *\n * Return C_OK on success. On error C_ERR is returned.\n * When OK is returned the structure must be freed with zslFreeLexRange(),\n * otherwise no release is needed. */\nint zslParseLexRange(robj *min, robj *max, zlexrangespec *spec) {\n    /* The range can't be valid if objects are integer encoded.\n     * Every item must start with ( or [. */\n    if (min->encoding == OBJ_ENCODING_INT ||\n        max->encoding == OBJ_ENCODING_INT) return C_ERR;\n\n    spec->min = spec->max = NULL;\n    if (zslParseLexRangeItem(min, &spec->min, &spec->minex) == C_ERR ||\n        zslParseLexRangeItem(max, &spec->max, &spec->maxex) == C_ERR) {\n        zslFreeLexRange(spec);\n        return C_ERR;\n    } else {\n        return C_OK;\n    }\n}\n\n/* This is just a wrapper to sdscmp() that is able to\n * handle shared.minstring and shared.maxstring as the equivalent of\n * -inf and +inf for strings */\nint sdscmplex(sds a, sds b) {\n    if (a == b) return 0;\n    if (a == shared.minstring || b == shared.maxstring) return -1;\n    if (a == shared.maxstring || b == shared.minstring) return 1;\n    return sdscmp(a,b);\n}\n\nint zslLexValueGteMin(sds value, zlexrangespec *spec) {\n    return spec->minex ?\n        (sdscmplex(value,spec->min) > 0) :\n        (sdscmplex(value,spec->min) >= 0);\n}\n\nint zslLexValueLteMax(sds value, zlexrangespec *spec) {\n    return spec->maxex ?\n        (sdscmplex(value,spec->max) < 0) :\n        (sdscmplex(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    int cmp = sdscmplex(range->min,range->max);\n    if (cmp > 0 || (cmp == 0 && (range->minex || range->maxex)))\n        return 0;\n    x = zsl->tail;\n    if (x == NULL || !zslLexValueGteMin(x->ele,range))\n        return 0;\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslLexValueLteMax(x->ele,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->ele,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    serverAssert(x != NULL);\n\n    /* Check if score <= max. */\n    if (!zslLexValueLteMax(x->ele,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->ele,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so this node cannot be NULL. */\n    serverAssert(x != NULL);\n\n    /* Check if score >= min. */\n    if (!zslLexValueGteMin(x->ele,range)) return NULL;\n    return x;\n}\n\n/*-----------------------------------------------------------------------------\n * Ziplist-backed sorted set API\n *----------------------------------------------------------------------------*/\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    serverAssert(sptr != NULL);\n    serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));\n\n    if (vstr) {\n        memcpy(buf,vstr,vlen);\n        buf[vlen] = '\\0';\n        score = strtod(buf,NULL);\n    } else {\n        score = vlong;\n    }\n\n    return score;\n}\n\n/* Return a ziplist element as an SDS string. */\nsds ziplistGetObject(unsigned char *sptr) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    serverAssert(sptr != NULL);\n    serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));\n\n    if (vstr) {\n        return sdsnewlen((char*)vstr,vlen);\n    } else {\n        return sdsfromlonglong(vlong);\n    }\n}\n\n/* Compare element in sorted set with given element. */\nint zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    unsigned char vbuf[32];\n    int minlen, cmp;\n\n    serverAssert(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    minlen = (vlen < clen) ? vlen : clen;\n    cmp = memcmp(vstr,cstr,minlen);\n    if (cmp == 0) return vlen-clen;\n    return cmp;\n}\n\nunsigned int zzlLength(unsigned char *zl) {\n    return ziplistLen(zl)/2;\n}\n\n/* Move to next entry based on the values in eptr and sptr. Both are set to\n * NULL when there is no next entry. */\nvoid zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    serverAssert(*eptr != NULL && *sptr != NULL);\n\n    _eptr = ziplistNext(zl,*sptr);\n    if (_eptr != NULL) {\n        _sptr = ziplistNext(zl,_eptr);\n        serverAssert(_sptr != NULL);\n    } else {\n        /* No next entry. */\n        _sptr = NULL;\n    }\n\n    *eptr = _eptr;\n    *sptr = _sptr;\n}\n\n/* Move to the previous entry based on the values in eptr and sptr. Both are\n * set to NULL when there is no next entry. */\nvoid zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    serverAssert(*eptr != NULL && *sptr != NULL);\n\n    _sptr = ziplistPrev(zl,*eptr);\n    if (_sptr != NULL) {\n        _eptr = ziplistPrev(zl,_sptr);\n        serverAssert(_eptr != NULL);\n    } else {\n        /* No previous entry. */\n        _eptr = NULL;\n    }\n\n    *eptr = _eptr;\n    *sptr = _sptr;\n}\n\n/* Returns if there is a part of the zset is in range. Should only be used\n * internally by zzlFirstInRange and zzlLastInRange. */\nint zzlIsInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *p;\n    double score;\n\n    /* Test for ranges that will always be empty. */\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n\n    p = ziplistIndex(zl,-1); /* Last score. */\n    if (p == NULL) return 0; /* Empty sorted set */\n    score = zzlGetScore(p);\n    if (!zslValueGteMin(score,range))\n        return 0;\n\n    p = ziplistIndex(zl,1); /* First score. */\n    serverAssert(p != NULL);\n    score = zzlGetScore(p);\n    if (!zslValueLteMax(score,range))\n        return 0;\n\n    return 1;\n}\n\n/* Find pointer to the first element contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double score;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueGteMin(score,range)) {\n            /* Check if score <= max. */\n            if (zslValueLteMax(score,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    return NULL;\n}\n\n/* Find pointer to the last element contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;\n    double score;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueLteMax(score,range)) {\n            /* Check if score >= min. */\n            if (zslValueGteMin(score,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to previous element by moving to the score of previous element.\n         * When this returns NULL, we know there also is no element. */\n        sptr = ziplistPrev(zl,eptr);\n        if (sptr != NULL)\n            serverAssert((eptr = ziplistPrev(zl,sptr)) != NULL);\n        else\n            eptr = NULL;\n    }\n\n    return NULL;\n}\n\nint zzlLexValueGteMin(unsigned char *p, zlexrangespec *spec) {\n    sds value = ziplistGetObject(p);\n    int res = zslLexValueGteMin(value,spec);\n    sdsfree(value);\n    return res;\n}\n\nint zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec) {\n    sds value = ziplistGetObject(p);\n    int res = zslLexValueLteMax(value,spec);\n    sdsfree(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    int cmp = sdscmplex(range->min,range->max);\n    if (cmp > 0 || (cmp == 0 && (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    serverAssert(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        serverAssert(sptr != NULL);\n        eptr = ziplistNext(zl,sptr); /* Next element. */\n    }\n\n    return NULL;\n}\n\n/* Find pointer to the last element contained in the specified lex range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInLexRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        if (zzlLexValueLteMax(eptr,range)) {\n            /* Check if score >= min. */\n            if (zzlLexValueGteMin(eptr,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to previous element by moving to the score of previous element.\n         * When this returns NULL, we know there also is no element. */\n        sptr = ziplistPrev(zl,eptr);\n        if (sptr != NULL)\n            serverAssert((eptr = ziplistPrev(zl,sptr)) != NULL);\n        else\n            eptr = NULL;\n    }\n\n    return NULL;\n}\n\nunsigned char *zzlFind(unsigned char *zl, sds ele, double *score) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        if (ziplistCompare(eptr,(unsigned char*)ele,sdslen(ele))) {\n            /* Matching element, pull out score. */\n            if (score != NULL) *score = zzlGetScore(sptr);\n            return eptr;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\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. */\nunsigned char *zzlDelete(unsigned char *zl, unsigned char *eptr) {\n    unsigned char *p = eptr;\n\n    /* TODO: add function to ziplist API to delete N elements from offset. */\n    zl = ziplistDelete(zl,&p);\n    zl = ziplistDelete(zl,&p);\n    return zl;\n}\n\nunsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, sds ele, double score) {\n    unsigned char *sptr;\n    char scorebuf[128];\n    int scorelen;\n    size_t offset;\n\n    scorelen = d2string(scorebuf,sizeof(scorebuf),score);\n    if (eptr == NULL) {\n        zl = ziplistPush(zl,(unsigned char*)ele,sdslen(ele),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)scorebuf,scorelen,ZIPLIST_TAIL);\n    } else {\n        /* Keep offset relative to zl, as it might be re-allocated. */\n        offset = eptr-zl;\n        zl = ziplistInsert(zl,eptr,(unsigned char*)ele,sdslen(ele));\n        eptr = zl+offset;\n\n        /* Insert score after the element. */\n        serverAssert((sptr = ziplistNext(zl,eptr)) != NULL);\n        zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);\n    }\n    return zl;\n}\n\n/* Insert (element,score) pair in ziplist. This function assumes the element is\n * not yet present in the list. */\nunsigned char *zzlInsert(unsigned char *zl, sds ele, double score) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double s;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n        s = zzlGetScore(sptr);\n\n        if (s > score) {\n            /* First element with score larger than score for element to be\n             * inserted. This means we should take its spot in the list to\n             * maintain ordering. */\n            zl = zzlInsertAt(zl,eptr,ele,score);\n            break;\n        } else if (s == score) {\n            /* Ensure lexicographical ordering for elements. */\n            if (zzlCompareElements(eptr,(unsigned char*)ele,sdslen(ele)) > 0) {\n                zl = zzlInsertAt(zl,eptr,ele,score);\n                break;\n            }\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    /* Push on tail of list when it was not yet inserted. */\n    if (eptr == NULL)\n        zl = zzlInsertAt(zl,NULL,ele,score);\n    return zl;\n}\n\nunsigned char *zzlDeleteRangeByScore(unsigned char *zl, zrangespec *range, unsigned long *deleted) {\n    unsigned char *eptr, *sptr;\n    double score;\n    unsigned long num = 0;\n\n    if (deleted != NULL) *deleted = 0;\n\n    eptr = zzlFirstInRange(zl,range);\n    if (eptr == NULL) return zl;\n\n    /* When the tail of the ziplist is deleted, eptr will point to the sentinel\n     * byte and ziplistNext will return NULL. */\n    while ((sptr = ziplistNext(zl,eptr)) != NULL) {\n        score = zzlGetScore(sptr);\n        if (zslValueLteMax(score,range)) {\n            /* Delete both the element and the score. */\n            zl = ziplistDelete(zl,&eptr);\n            zl = ziplistDelete(zl,&eptr);\n            num++;\n        } else {\n            /* No longer in range. */\n            break;\n        }\n    }\n\n    if (deleted != NULL) *deleted = num;\n    return zl;\n}\n\nunsigned char *zzlDeleteRangeByLex(unsigned char *zl, zlexrangespec *range, unsigned long *deleted) {\n    unsigned char *eptr, *sptr;\n    unsigned long num = 0;\n\n    if (deleted != NULL) *deleted = 0;\n\n    eptr = zzlFirstInLexRange(zl,range);\n    if (eptr == NULL) return zl;\n\n    /* When the tail of the ziplist is deleted, eptr will point to the sentinel\n     * byte and ziplistNext will return NULL. */\n    while ((sptr = ziplistNext(zl,eptr)) != NULL) {\n        if (zzlLexValueLteMax(eptr,range)) {\n            /* Delete both the element and the score. */\n            zl = ziplistDelete(zl,&eptr);\n            zl = ziplistDelete(zl,&eptr);\n            num++;\n        } else {\n            /* No longer in range. */\n            break;\n        }\n    }\n\n    if (deleted != NULL) *deleted = num;\n    return zl;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n * Start and end are inclusive. Note that start and end need to be 1-based */\nunsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsigned int end, unsigned long *deleted) {\n    unsigned int num = (end-start)+1;\n    if (deleted) *deleted = num;\n    zl = ziplistDeleteRange(zl,2*(start-1),2*num);\n    return zl;\n}\n\n/*-----------------------------------------------------------------------------\n * Common sorted set API\n *----------------------------------------------------------------------------*/\n\nunsigned long zsetLength(const robj *zobj) {\n    unsigned long length = 0;\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        length = zzlLength(zobj->ptr);\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        length = ((const zset*)zobj->ptr)->zsl->length;\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return length;\n}\n\nvoid zsetConvert(robj *zobj, int encoding) {\n    zset *zs;\n    zskiplistNode *node, *next;\n    sds ele;\n    double score;\n\n    if (zobj->encoding == encoding) return;\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        if (encoding != OBJ_ENCODING_SKIPLIST)\n            serverPanic(\"Unknown target encoding\");\n\n        zs = zmalloc(sizeof(*zs));\n        zs->dict = dictCreate(&zsetDictType,NULL);\n        zs->zsl = zslCreate();\n\n        eptr = ziplistIndex(zl,0);\n        serverAssertWithInfo(NULL,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(NULL,zobj,sptr != NULL);\n\n        while (eptr != NULL) {\n            score = zzlGetScore(sptr);\n            serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n            if (vstr == NULL)\n                ele = sdsfromlonglong(vlong);\n            else\n                ele = sdsnewlen((char*)vstr,vlen);\n\n            node = zslInsert(zs->zsl,score,ele);\n            serverAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        zfree(zobj->ptr);\n        zobj->ptr = zs;\n        zobj->encoding = OBJ_ENCODING_SKIPLIST;\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        unsigned char *zl = ziplistNew();\n\n        if (encoding != OBJ_ENCODING_ZIPLIST)\n            serverPanic(\"Unknown target encoding\");\n\n        /* Approach similar to zslFree(), since we want to free the skiplist at\n         * the same time as creating the ziplist. */\n        zs = zobj->ptr;\n        dictRelease(zs->dict);\n        node = zs->zsl->header->level[0].forward;\n        zfree(zs->zsl->header);\n        zfree(zs->zsl);\n\n        while (node) {\n            zl = zzlInsertAt(zl,NULL,node->ele,node->score);\n            next = node->level[0].forward;\n            zslFreeNode(node);\n            node = next;\n        }\n\n        zfree(zs);\n        zobj->ptr = zl;\n        zobj->encoding = OBJ_ENCODING_ZIPLIST;\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\n/* Convert the sorted set object into a ziplist if it is not already a ziplist\n * and if the number of elements and the maximum element size is within the\n * expected ranges. */\nvoid zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return;\n    zset *zset = zobj->ptr;\n\n    if (zset->zsl->length <= server.zset_max_ziplist_entries &&\n        maxelelen <= server.zset_max_ziplist_value)\n            zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);\n}\n\n/* Return (by reference) the score of the specified member of the sorted set\n * storing it into *score. If the element does not exist C_ERR is returned\n * otherwise C_OK is returned and *score is correctly populated.\n * If 'zobj' or 'member' is NULL, C_ERR is returned. */\nint zsetScore(robj *zobj, sds member, double *score) {\n    if (!zobj || !member) return C_ERR;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        if (zzlFind(zobj->ptr, member, score) == NULL) return C_ERR;\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de = dictFind(zs->dict, member);\n        if (de == NULL) return C_ERR;\n        *score = *(double*)dictGetVal(de);\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return C_OK;\n}\n\n/* Add a new element or update the score of an existing element in a sorted\n * set, regardless of its encoding.\n *\n * The set of flags change the command behavior. They are passed with an integer\n * pointer since the function will clear the flags and populate them with\n * other flags to indicate different conditions.\n *\n * The input flags are the following:\n *\n * ZADD_INCR: Increment the current element score by 'score' instead of updating\n *            the current element score. If the element does not exist, we\n *            assume 0 as previous score.\n * ZADD_NX:   Perform the operation only if the element does not exist.\n * ZADD_XX:   Perform the operation only if the element already exist.\n *\n * When ZADD_INCR is used, the new score of the element is stored in\n * '*newscore' if 'newscore' is not NULL.\n *\n * The returned flags are the following:\n *\n * ZADD_NAN:     The resulting score is not a number.\n * ZADD_ADDED:   The element was added (not present before the call).\n * ZADD_UPDATED: The element score was updated.\n * ZADD_NOP:     No operation was performed because of NX or XX.\n *\n * Return value:\n *\n * The function returns 1 on success, and sets the appropriate flags\n * ADDED or UPDATED to signal what happened during the operation (note that\n * none could be set if we re-added an element using the same score it used\n * to have, or in the case a zero increment is used).\n *\n * The function returns 0 on error, currently only when the increment\n * produces a NAN condition, or when the 'score' value is NAN since the\n * start.\n *\n * The command as a side effect of adding a new element may convert the sorted\n * set internal encoding from ziplist to hashtable+skiplist.\n *\n * Memory management of 'ele':\n *\n * The function does not take ownership of the 'ele' SDS string, but copies\n * it if needed. */\nint zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {\n    /* Turn options into simple to check vars. */\n    int incr = (*flags & ZADD_INCR) != 0;\n    int nx = (*flags & ZADD_NX) != 0;\n    int xx = (*flags & ZADD_XX) != 0;\n    *flags = 0; /* We'll return our response flags. */\n    double curscore;\n\n    /* NaN as input is an error regardless of all the other parameters. */\n    if (isnan(score)) {\n        *flags = ZADD_NAN;\n        return 0;\n    }\n\n    /* Update the sorted set according to its encoding. */\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr;\n\n        if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {\n            /* NX? Return, same element already exists. */\n            if (nx) {\n                *flags |= ZADD_NOP;\n                return 1;\n            }\n\n            /* Prepare the score for the increment if needed. */\n            if (incr) {\n                score += curscore;\n                if (isnan(score)) {\n                    *flags |= ZADD_NAN;\n                    return 0;\n                }\n                if (newscore) *newscore = score;\n            }\n\n            /* Remove and re-insert when score changed. */\n            if (score != curscore) {\n                zobj->ptr = zzlDelete(zobj->ptr,eptr);\n                zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n                *flags |= ZADD_UPDATED;\n            }\n            return 1;\n        } else if (!xx) {\n            /* Optimize: check if the element is too large or the list\n             * becomes too long *before* executing zzlInsert. */\n            zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n            if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries ||\n                sdslen(ele) > server.zset_max_ziplist_value)\n                zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);\n            if (newscore) *newscore = score;\n            *flags |= ZADD_ADDED;\n            return 1;\n        } else {\n            *flags |= ZADD_NOP;\n            return 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplistNode *znode;\n        dictEntry *de;\n\n        de = dictFind(zs->dict,ele);\n        if (de != NULL) {\n            /* NX? Return, same element already exists. */\n            if (nx) {\n                *flags |= ZADD_NOP;\n                return 1;\n            }\n            curscore = *(double*)dictGetVal(de);\n\n            /* Prepare the score for the increment if needed. */\n            if (incr) {\n                score += curscore;\n                if (isnan(score)) {\n                    *flags |= ZADD_NAN;\n                    return 0;\n                }\n                if (newscore) *newscore = score;\n            }\n\n            /* Remove and re-insert when score changes. */\n            if (score != curscore) {\n                znode = zslUpdateScore(zs->zsl,curscore,ele,score);\n                /* Note that we did not removed the original element from\n                 * the hash table representing the sorted set, so we just\n                 * update the score. */\n                dictGetVal(de) = &znode->score; /* Update score ptr. */\n                *flags |= ZADD_UPDATED;\n            }\n            return 1;\n        } else if (!xx) {\n            ele = sdsdup(ele);\n            znode = zslInsert(zs->zsl,score,ele);\n            serverAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);\n            *flags |= ZADD_ADDED;\n            if (newscore) *newscore = score;\n            return 1;\n        } else {\n            *flags |= ZADD_NOP;\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return 0; /* Never reached. */\n}\n\n/* Delete the element 'ele' from the sorted set, returning 1 if the element\n * existed and was deleted, 0 otherwise (the element was not there). */\nint zsetDel(robj *zobj, sds ele) {\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr;\n\n        if ((eptr = zzlFind(zobj->ptr,ele,NULL)) != NULL) {\n            zobj->ptr = zzlDelete(zobj->ptr,eptr);\n            return 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de;\n        double score;\n\n        de = dictUnlink(zs->dict,ele);\n        if (de != NULL) {\n            /* Get the score in order to delete from the skiplist later. */\n            score = *(double*)dictGetVal(de);\n\n            /* Delete from the hash table and later from the skiplist.\n             * Note that the order is important: deleting from the skiplist\n             * actually releases the SDS string representing the element,\n             * which is shared between the skiplist and the hash table, so\n             * we need to delete from the skiplist as the final step. */\n            dictFreeUnlinkedEntry(zs->dict,de);\n\n            /* Delete from skiplist. */\n            int retval = zslDelete(zs->zsl,score,ele,NULL);\n            serverAssert(retval);\n\n            if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return 0; /* No such element found. */\n}\n\n/* Given a sorted set object returns the 0-based rank of the object or\n * -1 if the object does not exist.\n *\n * For rank we mean the position of the element in the sorted collection\n * of elements. So the first element has rank 0, the second rank 1, and so\n * forth up to length-1 elements.\n *\n * If 'reverse' is false, the rank is returned considering as first element\n * the one with the lowest score. Otherwise if 'reverse' is non-zero\n * the rank is computed considering as element with rank 0 the one with\n * the highest score. */\nlong zsetRank(robj *zobj, sds ele, int reverse) {\n    unsigned long llen;\n    unsigned long rank;\n\n    llen = zsetLength(zobj);\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        eptr = ziplistIndex(zl,0);\n        serverAssert(eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        rank = 1;\n        while(eptr != NULL) {\n            if (ziplistCompare(eptr,(unsigned char*)ele,sdslen(ele)))\n                break;\n            rank++;\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        if (eptr != NULL) {\n            if (reverse)\n                return llen-rank;\n            else\n                return rank-1;\n        } else {\n            return -1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        dictEntry *de;\n        double score;\n\n        de = dictFind(zs->dict,ele);\n        if (de != NULL) {\n            score = *(double*)dictGetVal(de);\n            rank = zslGetRank(zsl,score,ele);\n            /* Existing elements always have a rank. */\n            serverAssert(rank != 0);\n            if (reverse)\n                return llen-rank;\n            else\n                return rank-1;\n        } else {\n            return -1;\n        }\n    } else {\n        serverPanic(\"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(client *c, int flags) {\n    static char *nanerr = \"resulting score is not a number (NaN)\";\n    robj *key = c->argv[1];\n    robj *zobj;\n    sds ele;\n    double score = 0, *scores = NULL;\n    int j, elements;\n    int scoreidx = 0;\n    /* The following vars are used in order to track what the command actually\n     * did during the execution, to reply to the client and to trigger the\n     * notification of keyspace change. */\n    int added = 0;      /* Number of new elements added. */\n    int updated = 0;    /* Number of elements with updated score. */\n    int processed = 0;  /* Number of elements processed, may remain zero with\n                           options like XX. */\n\n    /* Parse options. At the end 'scoreidx' is set to the argument position\n     * of the score of the first score-element pair. */\n    scoreidx = 2;\n    while(scoreidx < c->argc) {\n        char *opt = c->argv[scoreidx]->ptr;\n        if (!strcasecmp(opt,\"nx\")) flags |= ZADD_NX;\n        else if (!strcasecmp(opt,\"xx\")) flags |= ZADD_XX;\n        else if (!strcasecmp(opt,\"ch\")) flags |= ZADD_CH;\n        else if (!strcasecmp(opt,\"incr\")) flags |= ZADD_INCR;\n        else break;\n        scoreidx++;\n    }\n\n    /* Turn options into simple to check vars. */\n    int incr = (flags & ZADD_INCR) != 0;\n    int nx = (flags & ZADD_NX) != 0;\n    int xx = (flags & ZADD_XX) != 0;\n    int ch = (flags & ZADD_CH) != 0;\n\n    /* After the options, we expect to have an even number of args, since\n     * we expect any number of score-element pairs. */\n    elements = c->argc-scoreidx;\n    if (elements % 2 || !elements) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    elements /= 2; /* Now this holds the number of score-element pairs. */\n\n    /* Check for incompatible options. */\n    if (nx && xx) {\n        addReplyError(c,\n            \"XX and NX options at the same time are not compatible\");\n        return;\n    }\n\n    if (incr && elements > 1) {\n        addReplyError(c,\n            \"INCR option supports a single increment-element pair\");\n        return;\n    }\n\n    /* Start parsing all the scores, we need to emit any syntax error\n     * before executing additions to the sorted set, as the command should\n     * either execute fully or nothing at all. */\n    scores = zmalloc(sizeof(double)*elements);\n    for (j = 0; j < elements; j++) {\n        if (getDoubleFromObjectOrReply(c,c->argv[scoreidx+j*2],&scores[j],NULL)\n            != C_OK) goto cleanup;\n    }\n\n    /* Lookup the key and create the sorted set if does not exist. */\n    zobj = lookupKeyWrite(c->db,key);\n    if (zobj == NULL) {\n        if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */\n        if (server.zset_max_ziplist_entries == 0 ||\n            server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr))\n        {\n            zobj = createZsetObject();\n        } else {\n            zobj = createZsetZiplistObject();\n        }\n        dbAdd(c->db,key,zobj);\n    } else {\n        if (zobj->type != OBJ_ZSET) {\n            addReply(c,shared.wrongtypeerr);\n            goto cleanup;\n        }\n    }\n\n    for (j = 0; j < elements; j++) {\n        double newscore;\n        score = scores[j];\n        int retflags = flags;\n\n        ele = c->argv[scoreidx+1+j*2]->ptr;\n        int retval = zsetAdd(zobj, score, ele, &retflags, &newscore);\n        if (retval == 0) {\n            addReplyError(c,nanerr);\n            goto cleanup;\n        }\n        if (retflags & ZADD_ADDED) added++;\n        if (retflags & ZADD_UPDATED) updated++;\n        if (!(retflags & ZADD_NOP)) processed++;\n        score = newscore;\n    }\n    server.dirty += (added+updated);\n\nreply_to_client:\n    if (incr) { /* ZINCRBY or INCR option. */\n        if (processed)\n            addReplyDouble(c,score);\n        else\n            addReplyNull(c);\n    } else { /* ZADD. */\n        addReplyLongLong(c,ch ? added+updated : added);\n    }\n\ncleanup:\n    zfree(scores);\n    if (added || updated) {\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_ZSET,\n            incr ? \"zincr\" : \"zadd\", key, c->db->id);\n    }\n}\n\nvoid zaddCommand(client *c) {\n    zaddGenericCommand(c,ZADD_NONE);\n}\n\nvoid zincrbyCommand(client *c) {\n    zaddGenericCommand(c,ZADD_INCR);\n}\n\nvoid zremCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int deleted = 0, keyremoved = 0, j;\n\n    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        if (zsetDel(zobj,c->argv[j]->ptr)) deleted++;\n        if (zsetLength(zobj) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n            break;\n        }\n    }\n\n    if (deleted) {\n        notifyKeyspaceEvent(NOTIFY_ZSET,\"zrem\",key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n        signalModifiedKey(c,c->db,key);\n        server.dirty += deleted;\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(client *c, int rangetype) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int keyremoved = 0;\n    unsigned long deleted = 0;\n    zrangespec range;\n    zlexrangespec lexrange;\n    long start, end, llen;\n\n    /* Step 1: Parse the range. */\n    if (rangetype == ZRANGE_RANK) {\n        if ((getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK) ||\n            (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK))\n            return;\n    } else if (rangetype == ZRANGE_SCORE) {\n        if (zslParseRange(c->argv[2],c->argv[3],&range) != C_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) != C_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,OBJ_ZSET)) goto cleanup;\n\n    if (rangetype == ZRANGE_RANK) {\n        /* Sanitize indexes. */\n        llen = zsetLength(zobj);\n        if (start < 0) start = llen+start;\n        if (end < 0) end = llen+end;\n        if (start < 0) start = 0;\n\n        /* Invariant: start >= 0, so this test will be true when end < 0.\n         * The range is empty when start > end or start >= length. */\n        if (start > end || start >= llen) {\n            addReply(c,shared.czero);\n            goto cleanup;\n        }\n        if (end >= llen) end = llen-1;\n    }\n\n    /* Step 3: Perform the range deletion operation. */\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        switch(rangetype) {\n        case ZRANGE_RANK:\n            zobj->ptr = zzlDeleteRangeByRank(zobj->ptr,start+1,end+1,&deleted);\n            break;\n        case ZRANGE_SCORE:\n            zobj->ptr = zzlDeleteRangeByScore(zobj->ptr,&range,&deleted);\n            break;\n        case ZRANGE_LEX:\n            zobj->ptr = zzlDeleteRangeByLex(zobj->ptr,&lexrange,&deleted);\n            break;\n        }\n        if (zzlLength(zobj->ptr) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        switch(rangetype) {\n        case ZRANGE_RANK:\n            deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);\n            break;\n        case ZRANGE_SCORE:\n            deleted = zslDeleteRangeByScore(zs->zsl,&range,zs->dict);\n            break;\n        case ZRANGE_LEX:\n            deleted = zslDeleteRangeByLex(zs->zsl,&lexrange,zs->dict);\n            break;\n        }\n        if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n        if (dictSize(zs->dict) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    /* Step 4: Notifications and reply. */\n    if (deleted) {\n        char *event[3] = {\"zremrangebyrank\",\"zremrangebyscore\",\"zremrangebylex\"};\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_ZSET,event[rangetype],key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n    }\n    server.dirty += deleted;\n    addReplyLongLong(c,deleted);\n\ncleanup:\n    if (rangetype == ZRANGE_LEX) zslFreeLexRange(&lexrange);\n}\n\nvoid zremrangebyrankCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_RANK);\n}\n\nvoid zremrangebyscoreCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_SCORE);\n}\n\nvoid zremrangebylexCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_LEX);\n}\n\ntypedef struct {\n    robj *subject;\n    int type; /* Set, sorted set */\n    int encoding;\n    double weight;\n\n    union {\n        /* Set iterators. */\n        union _iterset {\n            struct {\n                intset *is;\n                int ii;\n            } is;\n            struct {\n                dict *dict;\n                dictIterator *di;\n                dictEntry *de;\n            } ht;\n        } set;\n\n        /* Sorted set iterators. */\n        union _iterzset {\n            struct {\n                unsigned char *zl;\n                unsigned char *eptr, *sptr;\n            } zl;\n            struct {\n                zset *zs;\n                zskiplistNode *node;\n            } sl;\n        } zset;\n    } iter;\n} zsetopsrc;\n\n\n/* Use dirty flags for pointers that need to be cleaned up in the next\n * iteration over the zsetopval. The dirty flag for the long long value is\n * special, since long long values don't need cleanup. Instead, it means that\n * we already checked that \"ell\" holds a long long, or tried to convert another\n * representation into a long long value. When this was successful,\n * OPVAL_VALID_LL is set as well. */\n#define OPVAL_DIRTY_SDS 1\n#define OPVAL_DIRTY_LL 2\n#define OPVAL_VALID_LL 4\n\n/* Store value retrieved from the iterator. */\ntypedef struct {\n    int flags;\n    unsigned char _buf[32]; /* Private buffer. */\n    sds ele;\n    unsigned char *estr;\n    unsigned int elen;\n    long long ell;\n    double score;\n} zsetopval;\n\ntypedef union _iterset iterset;\ntypedef union _iterzset iterzset;\n\nvoid zuiInitIterator(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            it->is.is = op->subject->ptr;\n            it->is.ii = 0;\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            it->ht.dict = op->subject->ptr;\n            it->ht.di = dictGetIterator(op->subject->ptr);\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            it->zl.zl = op->subject->ptr;\n            it->zl.eptr = ziplistIndex(it->zl.zl,0);\n            if (it->zl.eptr != NULL) {\n                it->zl.sptr = ziplistNext(it->zl.zl,it->zl.eptr);\n                serverAssert(it->zl.sptr != NULL);\n            }\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            it->sl.zs = op->subject->ptr;\n            it->sl.node = it->sl.zs->zsl->header->level[0].forward;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nvoid zuiClearIterator(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            UNUSED(it); /* skip */\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dictReleaseIterator(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            UNUSED(it); /* skip */\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            UNUSED(it); /* skip */\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nunsigned long zuiLength(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == OBJ_SET) {\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            return intsetLen(op->subject->ptr);\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            return dictSize(ht);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            return zzlLength(op->subject->ptr);\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            return zs->zsl->length;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\n/* Check if the current value is valid. If so, store it in the passed structure\n * and move to the next element. If not valid, this means we have reached the\n * end of the structure and can abort. */\nint zuiNext(zsetopsrc *op, zsetopval *val) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (val->flags & OPVAL_DIRTY_SDS)\n        sdsfree(val->ele);\n\n    memset(val,0,sizeof(zsetopval));\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            int64_t ell;\n\n            if (!intsetGet(it->is.is,it->is.ii,&ell))\n                return 0;\n            val->ell = ell;\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->is.ii++;\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            if (it->ht.de == NULL)\n                return 0;\n            val->ele = dictGetKey(it->ht.de);\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            /* No need to check both, but better be explicit. */\n            if (it->zl.eptr == NULL || it->zl.sptr == NULL)\n                return 0;\n            serverAssert(ziplistGet(it->zl.eptr,&val->estr,&val->elen,&val->ell));\n            val->score = zzlGetScore(it->zl.sptr);\n\n            /* Move to next element. */\n            zzlNext(it->zl.zl,&it->zl.eptr,&it->zl.sptr);\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            if (it->sl.node == NULL)\n                return 0;\n            val->ele = it->sl.node->ele;\n            val->score = it->sl.node->score;\n\n            /* Move to next element. */\n            it->sl.node = it->sl.node->level[0].forward;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n    return 1;\n}\n\nint zuiLongLongFromValue(zsetopval *val) {\n    if (!(val->flags & OPVAL_DIRTY_LL)) {\n        val->flags |= OPVAL_DIRTY_LL;\n\n        if (val->ele != NULL) {\n            if (string2ll(val->ele,sdslen(val->ele),&val->ell))\n                val->flags |= OPVAL_VALID_LL;\n        } else if (val->estr != NULL) {\n            if (string2ll((char*)val->estr,val->elen,&val->ell))\n                val->flags |= OPVAL_VALID_LL;\n        } else {\n            /* The long long was already set, flag as valid. */\n            val->flags |= OPVAL_VALID_LL;\n        }\n    }\n    return val->flags & OPVAL_VALID_LL;\n}\n\nsds zuiSdsFromValue(zsetopval *val) {\n    if (val->ele == NULL) {\n        if (val->estr != NULL) {\n            val->ele = sdsnewlen((char*)val->estr,val->elen);\n        } else {\n            val->ele = sdsfromlonglong(val->ell);\n        }\n        val->flags |= OPVAL_DIRTY_SDS;\n    }\n    return val->ele;\n}\n\n/* This is different from zuiSdsFromValue since returns a new SDS string\n * which is up to the caller to free. */\nsds zuiNewSdsFromValue(zsetopval *val) {\n    if (val->flags & OPVAL_DIRTY_SDS) {\n        /* We have already one to return! */\n        sds ele = val->ele;\n        val->flags &= ~OPVAL_DIRTY_SDS;\n        val->ele = NULL;\n        return ele;\n    } else if (val->ele) {\n        return sdsdup(val->ele);\n    } else if (val->estr) {\n        return sdsnewlen((char*)val->estr,val->elen);\n    } else {\n        return sdsfromlonglong(val->ell);\n    }\n}\n\nint zuiBufferFromValue(zsetopval *val) {\n    if (val->estr == NULL) {\n        if (val->ele != NULL) {\n            val->elen = sdslen(val->ele);\n            val->estr = (unsigned char*)val->ele;\n        } else {\n            val->elen = ll2string((char*)val->_buf,sizeof(val->_buf),val->ell);\n            val->estr = val->_buf;\n        }\n    }\n    return 1;\n}\n\n/* Find value pointed to by val in the source pointer to by op. When found,\n * return 1 and store its score in target. Return 0 otherwise. */\nint zuiFind(zsetopsrc *op, zsetopval *val, double *score) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == OBJ_SET) {\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            if (zuiLongLongFromValue(val) &&\n                intsetFind(op->subject->ptr,val->ell))\n            {\n                *score = 1.0;\n                return 1;\n            } else {\n                return 0;\n            }\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            zuiSdsFromValue(val);\n            if (dictFind(ht,val->ele) != NULL) {\n                *score = 1.0;\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        zuiSdsFromValue(val);\n\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            if (zzlFind(op->subject->ptr,val->ele,score) != NULL) {\n                /* Score is already set by zzlFind. */\n                return 1;\n            } else {\n                return 0;\n            }\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            dictEntry *de;\n            if ((de = dictFind(zs->dict,val->ele)) != NULL) {\n                *score = *(double*)dictGetVal(de);\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nint zuiCompareByCardinality(const void *s1, const void *s2) {\n    unsigned long first = zuiLength((zsetopsrc*)s1);\n    unsigned long second = zuiLength((zsetopsrc*)s2);\n    if (first > second) return 1;\n    if (first < second) return -1;\n    return 0;\n}\n\n#define REDIS_AGGR_SUM 1\n#define REDIS_AGGR_MIN 2\n#define REDIS_AGGR_MAX 3\n#define zunionInterDictValue(_e) (dictGetVal(_e) == NULL ? 1.0 : *(double*)dictGetVal(_e))\n\ninline static void zunionInterAggregate(double *target, double val, int aggregate) {\n    if (aggregate == REDIS_AGGR_SUM) {\n        *target = *target + val;\n        /* The result of adding two doubles is NaN when one variable\n         * is +inf and the other is -inf. When these numbers are added,\n         * we maintain the convention of the result being 0.0. */\n        if (isnan(*target)) *target = 0.0;\n    } else if (aggregate == REDIS_AGGR_MIN) {\n        *target = val < *target ? val : *target;\n    } else if (aggregate == REDIS_AGGR_MAX) {\n        *target = val > *target ? val : *target;\n    } else {\n        /* safety net */\n        serverPanic(\"Unknown ZUNION/INTER aggregate type\");\n    }\n}\n\nuint64_t dictSdsHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\n\ndictType setAccumulatorDictType = {\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\nvoid zunionInterGenericCommand(client *c, robj *dstkey, int op) {\n    int i, j;\n    long setnum;\n    int aggregate = REDIS_AGGR_SUM;\n    zsetopsrc *src;\n    zsetopval zval;\n    sds tmp;\n    size_t maxelelen = 0;\n    robj *dstobj;\n    zset *dstzset;\n    zskiplistNode *znode;\n    int touched = 0;\n\n    /* expect setnum input keys to be given */\n    if ((getLongFromObjectOrReply(c, c->argv[2], &setnum, NULL) != C_OK))\n        return;\n\n    if (setnum < 1) {\n        addReplyError(c,\n            \"at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE\");\n        return;\n    }\n\n    /* test if the expected number of keys would overflow */\n    if (setnum > c->argc-3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* read keys to be used for input */\n    src = zcalloc(sizeof(zsetopsrc) * setnum);\n    for (i = 0, j = 3; i < setnum; i++, j++) {\n        robj *obj = lookupKeyWrite(c->db,c->argv[j]);\n        if (obj != NULL) {\n            if (obj->type != OBJ_ZSET && obj->type != OBJ_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        } else {\n            src[i].subject = NULL;\n        }\n\n        /* Default all weights to 1. */\n        src[i].weight = 1.0;\n    }\n\n    /* parse optional extra arguments */\n    if (j < c->argc) {\n        int remaining = c->argc - j;\n\n        while (remaining) {\n            if (remaining >= (setnum + 1) &&\n                !strcasecmp(c->argv[j]->ptr,\"weights\"))\n            {\n                j++; remaining--;\n                for (i = 0; i < setnum; i++, j++, remaining--) {\n                    if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,\n                            \"weight value is not a float\") != C_OK)\n                    {\n                        zfree(src);\n                        return;\n                    }\n                }\n            } else if (remaining >= 2 &&\n                       !strcasecmp(c->argv[j]->ptr,\"aggregate\"))\n            {\n                j++; remaining--;\n                if (!strcasecmp(c->argv[j]->ptr,\"sum\")) {\n                    aggregate = REDIS_AGGR_SUM;\n                } else if (!strcasecmp(c->argv[j]->ptr,\"min\")) {\n                    aggregate = REDIS_AGGR_MIN;\n                } else if (!strcasecmp(c->argv[j]->ptr,\"max\")) {\n                    aggregate = REDIS_AGGR_MAX;\n                } else {\n                    zfree(src);\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                j++; remaining--;\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    qsort(src,setnum,sizeof(zsetopsrc),zuiCompareByCardinality);\n\n    dstobj = createZsetObject();\n    dstzset = dstobj->ptr;\n    memset(&zval, 0, sizeof(zval));\n\n    if (op == SET_OP_INTER) {\n        /* Skip everything if the smallest input is empty. */\n        if (zuiLength(&src[0]) > 0) {\n            /* Precondition: as src[0] is non-empty and the inputs are ordered\n             * by size, all src[i > 0] are non-empty too. */\n            zuiInitIterator(&src[0]);\n            while (zuiNext(&src[0],&zval)) {\n                double score, value;\n\n                score = src[0].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                for (j = 1; j < setnum; j++) {\n                    /* It is not safe to access the zset we are\n                     * iterating, so explicitly check for equal object. */\n                    if (src[j].subject == src[0].subject) {\n                        value = zval.score*src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    } else if (zuiFind(&src[j],&zval,&value)) {\n                        value *= src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    } else {\n                        break;\n                    }\n                }\n\n                /* Only continue when present in every input. */\n                if (j == setnum) {\n                    tmp = zuiNewSdsFromValue(&zval);\n                    znode = zslInsert(dstzset->zsl,score,tmp);\n                    dictAdd(dstzset->dict,tmp,&znode->score);\n                    if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp);\n                }\n            }\n            zuiClearIterator(&src[0]);\n        }\n    } else if (op == SET_OP_UNION) {\n        dict *accumulator = dictCreate(&setAccumulatorDictType,NULL);\n        dictIterator *di;\n        dictEntry *de, *existing;\n        double score;\n\n        if (setnum) {\n            /* Our union is at least as large as the largest set.\n             * Resize the dictionary ASAP to avoid useless rehashing. */\n            dictExpand(accumulator,zuiLength(&src[setnum-1]));\n        }\n\n        /* Step 1: Create a dictionary of elements -> aggregated-scores\n         * by iterating one sorted set after the other. */\n        for (i = 0; i < setnum; i++) {\n            if (zuiLength(&src[i]) == 0) continue;\n\n            zuiInitIterator(&src[i]);\n            while (zuiNext(&src[i],&zval)) {\n                /* Initialize value */\n                score = src[i].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                /* Search for this element in the accumulating dictionary. */\n                de = dictAddRaw(accumulator,zuiSdsFromValue(&zval),&existing);\n                /* If we don't have it, we need to create a new entry. */\n                if (!existing) {\n                    tmp = zuiNewSdsFromValue(&zval);\n                    /* Remember the longest single element encountered,\n                     * to understand if it's possible to convert to ziplist\n                     * at the end. */\n                     if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp);\n                    /* Update the element with its initial score. */\n                    dictSetKey(accumulator, de, tmp);\n                    dictSetDoubleVal(de,score);\n                } else {\n                    /* Update the score with the score of the new instance\n                     * of the element found in the current sorted set.\n                     *\n                     * Here we access directly the dictEntry double\n                     * value inside the union as it is a big speedup\n                     * compared to using the getDouble/setDouble API. */\n                    zunionInterAggregate(&existing->v.d,score,aggregate);\n                }\n            }\n            zuiClearIterator(&src[i]);\n        }\n\n        /* Step 2: convert the dictionary into the final sorted set. */\n        di = dictGetIterator(accumulator);\n\n        /* We now are aware of the final size of the resulting sorted set,\n         * let's resize the dictionary embedded inside the sorted set to the\n         * right size, in order to save rehashing time. */\n        dictExpand(dstzset->dict,dictSize(accumulator));\n\n        while((de = dictNext(di)) != NULL) {\n            sds ele = dictGetKey(de);\n            score = dictGetDoubleVal(de);\n            znode = zslInsert(dstzset->zsl,score,ele);\n            dictAdd(dstzset->dict,ele,&znode->score);\n        }\n        dictReleaseIterator(di);\n        dictRelease(accumulator);\n    } else {\n        serverPanic(\"Unknown operator\");\n    }\n\n    if (dbDelete(c->db,dstkey))\n        touched = 1;\n    if (dstzset->zsl->length) {\n        zsetConvertToZiplistIfNeeded(dstobj,maxelelen);\n        dbAdd(c->db,dstkey,dstobj);\n        addReplyLongLong(c,zsetLength(dstobj));\n        signalModifiedKey(c,c->db,dstkey);\n        notifyKeyspaceEvent(NOTIFY_ZSET,\n            (op == SET_OP_UNION) ? \"zunionstore\" : \"zinterstore\",\n            dstkey,c->db->id);\n        server.dirty++;\n    } else {\n        decrRefCount(dstobj);\n        addReply(c,shared.czero);\n        if (touched) {\n            signalModifiedKey(c,c->db,dstkey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",dstkey,c->db->id);\n            server.dirty++;\n        }\n    }\n    zfree(src);\n}\n\nvoid zunionstoreCommand(client *c) {\n    zunionInterGenericCommand(c,c->argv[1], SET_OP_UNION);\n}\n\nvoid zinterstoreCommand(client *c) {\n    zunionInterGenericCommand(c,c->argv[1], SET_OP_INTER);\n}\n\nvoid zrangeGenericCommand(client *c, int reverse) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int withscores = 0;\n    long start;\n    long end;\n    long llen;\n    long rangelen;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;\n\n    if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,\"withscores\")) {\n        withscores = 1;\n    } else if (c->argc >= 5) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptyarray)) == NULL\n         || checkType(c,zobj,OBJ_ZSET)) return;\n\n    /* Sanitize indexes. */\n    llen = zsetLength(zobj);\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        addReply(c,shared.emptyarray);\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. RESP3 clients\n     * will receive sub arrays with score->element, while RESP2 returned\n     * a flat array. */\n    if (withscores && c->resp == 2)\n        addReplyArrayLen(c, rangelen*2);\n    else\n        addReplyArrayLen(c, rangelen);\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        if (reverse)\n            eptr = ziplistIndex(zl,-2-(2*start));\n        else\n            eptr = ziplistIndex(zl,2*start);\n\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        while (rangelen--) {\n            serverAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            if (vstr == NULL)\n                addReplyBulkLongLong(c,vlong);\n            else\n                addReplyBulkCBuffer(c,vstr,vlen);\n            if (withscores) addReplyDouble(c,zzlGetScore(sptr));\n\n            if (reverse)\n                zzlPrev(zl,&eptr,&sptr);\n            else\n                zzlNext(zl,&eptr,&sptr);\n        }\n\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n        sds ele;\n\n        /* Check if starting point is trivial, before doing log(N) lookup. */\n        if (reverse) {\n            ln = zsl->tail;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,llen-start);\n        } else {\n            ln = zsl->header->level[0].forward;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,start+1);\n        }\n\n        while(rangelen--) {\n            serverAssertWithInfo(c,zobj,ln != NULL);\n            ele = ln->ele;\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            addReplyBulkCBuffer(c,ele,sdslen(ele));\n            if (withscores) addReplyDouble(c,ln->score);\n            ln = reverse ? ln->backward : ln->level[0].forward;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid zrangeCommand(client *c) {\n    zrangeGenericCommand(c,0);\n}\n\nvoid zrevrangeCommand(client *c) {\n    zrangeGenericCommand(c,1);\n}\n\n/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE. */\nvoid genericZrangebyscoreCommand(client *c, int reverse) {\n    zrangespec range;\n    robj *key = c->argv[1];\n    robj *zobj;\n    long offset = 0, limit = -1;\n    int withscores = 0;\n    unsigned long rangelen = 0;\n    void *replylen = NULL;\n    int minidx, maxidx;\n\n    /* Parse the range arguments. */\n    if (reverse) {\n        /* Range is given as [max,min] */\n        maxidx = 2; minidx = 3;\n    } else {\n        /* Range is given as [min,max] */\n        minidx = 2; maxidx = 3;\n    }\n\n    if (zslParseRange(c->argv[minidx],c->argv[maxidx],&range) != C_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    /* Parse optional extra arguments. Note that ZCOUNT will exactly have\n     * 4 arguments, so we'll never enter the following code path. */\n    if (c->argc > 4) {\n        int remaining = c->argc - 4;\n        int pos = 4;\n\n        while (remaining) {\n            if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,\"withscores\")) {\n                pos++; remaining--;\n                withscores = 1;\n            } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,\"limit\")) {\n                if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL)\n                        != C_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL)\n                        != C_OK))\n                {\n                    return;\n                }\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    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptyarray)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n        double score;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            eptr = zzlLastInRange(zl,&range);\n        } else {\n            eptr = zzlFirstInRange(zl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (eptr == NULL) {\n            addReply(c,shared.emptyarray);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addReplyDeferredLen(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        while (eptr && limit--) {\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslValueGteMin(score,&range)) break;\n            } else {\n                if (!zslValueLteMax(score,&range)) break;\n            }\n\n            /* We know the element exists, so ziplistGet should always\n             * succeed */\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            rangelen++;\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            if (vstr == NULL) {\n                addReplyBulkLongLong(c,vlong);\n            } else {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            }\n            if (withscores) addReplyDouble(c,score);\n\n            /* Move to next node */\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            ln = zslLastInRange(zsl,&range);\n        } else {\n            ln = zslFirstInRange(zsl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (ln == NULL) {\n            addReply(c,shared.emptyarray);\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 = addReplyDeferredLen(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (ln && offset--) {\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n\n        while (ln && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslValueGteMin(ln->score,&range)) break;\n            } else {\n                if (!zslValueLteMax(ln->score,&range)) break;\n            }\n\n            rangelen++;\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            addReplyBulkCBuffer(c,ln->ele,sdslen(ln->ele));\n            if (withscores) addReplyDouble(c,ln->score);\n\n            /* Move to next node */\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    if (withscores && c->resp == 2) rangelen *= 2;\n    setDeferredArrayLen(c, replylen, rangelen);\n}\n\nvoid zrangebyscoreCommand(client *c) {\n    genericZrangebyscoreCommand(c,0);\n}\n\nvoid zrevrangebyscoreCommand(client *c) {\n    genericZrangebyscoreCommand(c,1);\n}\n\nvoid zcountCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zrangespec range;\n    unsigned long count = 0;\n\n    /* Parse the range arguments */\n    if (zslParseRange(c->argv[2],c->argv[3],&range) != C_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    /* Lookup the sorted set */\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET)) return;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        double score;\n\n        /* Use the first element in range as the starting point */\n        eptr = zzlFirstInRange(zl,&range);\n\n        /* No \"first\" element */\n        if (eptr == NULL) {\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        sptr = ziplistNext(zl,eptr);\n        score = zzlGetScore(sptr);\n        serverAssertWithInfo(c,zobj,zslValueLteMax(score,&range));\n\n        /* Iterate over elements in range */\n        while (eptr) {\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            if (!zslValueLteMax(score,&range)) {\n                break;\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *zn;\n        unsigned long rank;\n\n        /* Find first element in range */\n        zn = zslFirstInRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        if (zn != NULL) {\n            rank = zslGetRank(zsl, zn->score, zn->ele);\n            count = (zsl->length - (rank - 1));\n\n            /* Find last element in range */\n            zn = zslLastInRange(zsl, &range);\n\n            /* Use rank of last element, if any, to determine the actual count */\n            if (zn != NULL) {\n                rank = zslGetRank(zsl, zn->score, zn->ele);\n                count -= (zsl->length - rank);\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    addReplyLongLong(c, count);\n}\n\nvoid zlexcountCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zlexrangespec range;\n    unsigned long count = 0;\n\n    /* Parse the range arguments */\n    if (zslParseLexRange(c->argv[2],c->argv[3],&range) != C_OK) {\n        addReplyError(c,\"min or max not valid string range item\");\n        return;\n    }\n\n    /* Lookup the sorted set */\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        /* Use the first element in range as the starting point */\n        eptr = zzlFirstInLexRange(zl,&range);\n\n        /* No \"first\" element */\n        if (eptr == NULL) {\n            zslFreeLexRange(&range);\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(c,zobj,zzlLexValueLteMax(eptr,&range));\n\n        /* Iterate over elements in range */\n        while (eptr) {\n            /* Abort when the node is no longer in range. */\n            if (!zzlLexValueLteMax(eptr,&range)) {\n                break;\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *zn;\n        unsigned long rank;\n\n        /* Find first element in range */\n        zn = zslFirstInLexRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        if (zn != NULL) {\n            rank = zslGetRank(zsl, zn->score, zn->ele);\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->ele);\n                count -= (zsl->length - rank);\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    addReplyLongLong(c, count);\n}\n\n/* This command implements ZRANGEBYLEX, ZREVRANGEBYLEX. */\nvoid genericZrangebylexCommand(client *c, int reverse) {\n    zlexrangespec range;\n    robj *key = c->argv[1];\n    robj *zobj;\n    long offset = 0, limit = -1;\n    unsigned long rangelen = 0;\n    void *replylen = NULL;\n    int minidx, maxidx;\n\n    /* Parse the range arguments. */\n    if (reverse) {\n        /* Range is given as [max,min] */\n        maxidx = 2; minidx = 3;\n    } else {\n        /* Range is given as [min,max] */\n        minidx = 2; maxidx = 3;\n    }\n\n    if (zslParseLexRange(c->argv[minidx],c->argv[maxidx],&range) != C_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) != C_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != C_OK)) {\n                    zslFreeLexRange(&range);\n                    return;\n                }\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.emptyarray)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            eptr = zzlLastInLexRange(zl,&range);\n        } else {\n            eptr = zzlFirstInLexRange(zl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (eptr == NULL) {\n            addReply(c,shared.emptyarray);\n            zslFreeLexRange(&range);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addReplyDeferredLen(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        while (eptr && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zzlLexValueGteMin(eptr,&range)) break;\n            } else {\n                if (!zzlLexValueLteMax(eptr,&range)) break;\n            }\n\n            /* We know the element exists, so ziplistGet should always\n             * succeed. */\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            rangelen++;\n            if (vstr == NULL) {\n                addReplyBulkLongLong(c,vlong);\n            } else {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            }\n\n            /* Move to next node */\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            ln = zslLastInLexRange(zsl,&range);\n        } else {\n            ln = zslFirstInLexRange(zsl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (ln == NULL) {\n            addReply(c,shared.emptyarray);\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 = addReplyDeferredLen(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->ele,&range)) break;\n            } else {\n                if (!zslLexValueLteMax(ln->ele,&range)) break;\n            }\n\n            rangelen++;\n            addReplyBulkCBuffer(c,ln->ele,sdslen(ln->ele));\n\n            /* Move to next node */\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    setDeferredArrayLen(c, replylen, rangelen);\n}\n\nvoid zrangebylexCommand(client *c) {\n    genericZrangebylexCommand(c,0);\n}\n\nvoid zrevrangebylexCommand(client *c) {\n    genericZrangebylexCommand(c,1);\n}\n\nvoid zcardCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    addReplyLongLong(c,zsetLength(zobj));\n}\n\nvoid zscoreCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    double score;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    if (zsetScore(zobj,c->argv[2]->ptr,&score) == C_ERR) {\n        addReplyNull(c);\n    } else {\n        addReplyDouble(c,score);\n    }\n}\n\nvoid zrankGenericCommand(client *c, int reverse) {\n    robj *key = c->argv[1];\n    robj *ele = c->argv[2];\n    robj *zobj;\n    long rank;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    serverAssertWithInfo(c,ele,sdsEncodedObject(ele));\n    rank = zsetRank(zobj,ele->ptr,reverse);\n    if (rank >= 0) {\n        addReplyLongLong(c,rank);\n    } else {\n        addReplyNull(c);\n    }\n}\n\nvoid zrankCommand(client *c) {\n    zrankGenericCommand(c, 0);\n}\n\nvoid zrevrankCommand(client *c) {\n    zrankGenericCommand(c, 1);\n}\n\nvoid zscanCommand(client *c) {\n    robj *o;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,o,OBJ_ZSET)) return;\n    scanGenericCommand(c,o,cursor);\n}\n\n/* This command implements the generic zpop operation, used by:\n * ZPOPMIN, ZPOPMAX, BZPOPMIN and BZPOPMAX. This function is also used\n * inside blocked.c in the unblocking stage of BZPOPMIN and BZPOPMAX.\n *\n * If 'emitkey' is true also the key name is emitted, useful for the blocking\n * behavior of BZPOP[MIN|MAX], since we can block into multiple keys.\n *\n * The synchronous version instead does not need to emit the key, but may\n * use the 'count' argument to return multiple items if available. */\nvoid genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey, robj *countarg) {\n    int idx;\n    robj *key = NULL;\n    robj *zobj = NULL;\n    sds ele;\n    double score;\n    long count = 1;\n\n    /* If a count argument as passed, parse it or return an error. */\n    if (countarg) {\n        if (getLongFromObjectOrReply(c,countarg,&count,NULL) != C_OK)\n            return;\n        if (count <= 0) {\n            addReply(c,shared.emptyarray);\n            return;\n        }\n    }\n\n    /* Check type and break on the first error, otherwise identify candidate. */\n    idx = 0;\n    while (idx < keyc) {\n        key = keyv[idx++];\n        zobj = lookupKeyWrite(c->db,key);\n        if (!zobj) continue;\n        if (checkType(c,zobj,OBJ_ZSET)) return;\n        break;\n    }\n\n    /* No candidate for zpopping, return empty. */\n    if (!zobj) {\n        addReply(c,shared.emptyarray);\n        return;\n    }\n\n    void *arraylen_ptr = addReplyDeferredLen(c);\n    long arraylen = 0;\n\n    /* We emit the key only for the blocking variant. */\n    if (emitkey) addReplyBulk(c,key);\n\n    /* Remove the element. */\n    do {\n        if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n            unsigned char *zl = zobj->ptr;\n            unsigned char *eptr, *sptr;\n            unsigned char *vstr;\n            unsigned int vlen;\n            long long vlong;\n\n            /* Get the first or last element in the sorted set. */\n            eptr = ziplistIndex(zl,where == ZSET_MAX ? -2 : 0);\n            serverAssertWithInfo(c,zobj,eptr != NULL);\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n            if (vstr == NULL)\n                ele = sdsfromlonglong(vlong);\n            else\n                ele = sdsnewlen(vstr,vlen);\n\n            /* Get the score. */\n            sptr = ziplistNext(zl,eptr);\n            serverAssertWithInfo(c,zobj,sptr != NULL);\n            score = zzlGetScore(sptr);\n        } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = zobj->ptr;\n            zskiplist *zsl = zs->zsl;\n            zskiplistNode *zln;\n\n            /* Get the first or last element in the sorted set. */\n            zln = (where == ZSET_MAX ? zsl->tail :\n                                       zsl->header->level[0].forward);\n\n            /* There must be an element in the sorted set. */\n            serverAssertWithInfo(c,zobj,zln != NULL);\n            ele = sdsdup(zln->ele);\n            score = zln->score;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n\n        serverAssertWithInfo(c,zobj,zsetDel(zobj,ele));\n        server.dirty++;\n\n        if (arraylen == 0) { /* Do this only for the first iteration. */\n            char *events[2] = {\"zpopmin\",\"zpopmax\"};\n            notifyKeyspaceEvent(NOTIFY_ZSET,events[where],key,c->db->id);\n            signalModifiedKey(c,c->db,key);\n        }\n\n        addReplyBulkCBuffer(c,ele,sdslen(ele));\n        addReplyDouble(c,score);\n        sdsfree(ele);\n        arraylen += 2;\n\n        /* Remove the key, if indeed needed. */\n        if (zsetLength(zobj) == 0) {\n            dbDelete(c->db,key);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n            break;\n        }\n    } while(--count);\n\n    setDeferredArrayLen(c,arraylen_ptr,arraylen + (emitkey != 0));\n}\n\n/* ZPOPMIN key [<count>] */\nvoid zpopminCommand(client *c) {\n    if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    genericZpopCommand(c,&c->argv[1],1,ZSET_MIN,0,\n        c->argc == 3 ? c->argv[2] : NULL);\n}\n\n/* ZMAXPOP key [<count>] */\nvoid zpopmaxCommand(client *c) {\n    if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    genericZpopCommand(c,&c->argv[1],1,ZSET_MAX,0,\n        c->argc == 3 ? c->argv[2] : NULL);\n}\n\n/* BZPOPMIN / BZPOPMAX actual implementation. */\nvoid blockingGenericZpopCommand(client *c, int where) {\n    robj *o;\n    mstime_t timeout;\n    int j;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)\n        != C_OK) return;\n\n    for (j = 1; j < c->argc-1; j++) {\n        o = lookupKeyWrite(c->db,c->argv[j]);\n        if (o != NULL) {\n            if (o->type != OBJ_ZSET) {\n                addReply(c,shared.wrongtypeerr);\n                return;\n            } else {\n                if (zsetLength(o) != 0) {\n                    /* Non empty zset, this is like a normal ZPOP[MIN|MAX]. */\n                    genericZpopCommand(c,&c->argv[j],1,where,1,NULL);\n                    /* Replicate it as an ZPOP[MIN|MAX] instead of BZPOP[MIN|MAX]. */\n                    rewriteClientCommandVector(c,2,\n                        where == ZSET_MAX ? shared.zpopmax : shared.zpopmin,\n                        c->argv[j]);\n                    return;\n                }\n            }\n        }\n    }\n\n    /* If we are inside a MULTI/EXEC and the zset is empty the only thing\n     * we can do is treating it as a timeout (even with timeout 0). */\n    if (c->flags & CLIENT_MULTI) {\n        addReplyNullArray(c);\n        return;\n    }\n\n    /* If the keys do not exist we must block */\n    blockForKeys(c,BLOCKED_ZSET,c->argv + 1,c->argc - 2,timeout,NULL,NULL);\n}\n\n// BZPOPMIN key [key ...] timeout\nvoid bzpopminCommand(client *c) {\n    blockingGenericZpopCommand(c,ZSET_MIN);\n}\n\n// BZPOPMAX key [key ...] timeout\nvoid bzpopmaxCommand(client *c) {\n    blockingGenericZpopCommand(c,ZSET_MAX);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/src/timeout.c",
    "content": "/* Copyright (c) 2009-2020, 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 \"server.h\"\n#include \"cluster.h\"\n\n/* ========================== Clients timeouts ============================= */\n\n/* Check if this blocked client timedout (does nothing if the client is\n * not blocked right now). If so send a reply, unblock it, and return 1.\n * Otherwise 0 is returned and no operation is performed. */\nint checkBlockedClientTimeout(client *c, mstime_t now) {\n    if (c->flags & CLIENT_BLOCKED &&\n        c->bpop.timeout != 0\n        && c->bpop.timeout < now)\n    {\n        /* Handle blocking operation specific timeout. */\n        replyToBlockedClientTimedOut(c);\n        unblockClient(c);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Check for timeouts. Returns non-zero if the client was terminated.\n * The function gets the current time in milliseconds as argument since\n * it gets called multiple times in a loop, so calling gettimeofday() for\n * each iteration would be costly without any actual gain. */\nint clientsCronHandleTimeout(client *c, mstime_t now_ms) {\n    time_t now = now_ms/1000;\n\n    if (server.maxidletime &&\n        /* This handles the idle clients connection timeout if set. */\n        !(c->flags & CLIENT_SLAVE) &&   /* No timeout for slaves and monitors */\n        !(c->flags & CLIENT_MASTER) &&  /* No timeout for masters */\n        !(c->flags & CLIENT_BLOCKED) && /* No timeout for BLPOP */\n        !(c->flags & CLIENT_PUBSUB) &&  /* No timeout for Pub/Sub clients */\n        (now - c->lastinteraction > server.maxidletime))\n    {\n        serverLog(LL_VERBOSE,\"Closing idle client\");\n        freeClient(c);\n        return 1;\n    } else if (c->flags & CLIENT_BLOCKED) {\n        /* Cluster: handle unblock & redirect of clients blocked\n         * into keys no longer served by this server. */\n        if (server.cluster_enabled) {\n            if (clusterRedirectBlockedClientIfNeeded(c))\n                unblockClient(c);\n        }\n    }\n    return 0;\n}\n\n/* For blocked clients timeouts we populate a radix tree of 128 bit keys\n * composed as such:\n *\n *  [8 byte big endian expire time]+[8 byte client ID]\n *\n * We don't do any cleanup in the Radix tree: when we run the clients that\n * reached the timeout already, if they are no longer existing or no longer\n * blocked with such timeout, we just go forward.\n *\n * Every time a client blocks with a timeout, we add the client in\n * the tree. In beforeSleep() we call handleBlockedClientsTimeout() to run\n * the tree and unblock the clients. */\n\n#define CLIENT_ST_KEYLEN 16    /* 8 bytes mstime + 8 bytes client ID. */\n\n/* Given client ID and timeout, write the resulting radix tree key in buf. */\nvoid encodeTimeoutKey(unsigned char *buf, uint64_t timeout, client *c) {\n    timeout = htonu64(timeout);\n    memcpy(buf,&timeout,sizeof(timeout));\n    memcpy(buf+8,&c,sizeof(c));\n    if (sizeof(c) == 4) memset(buf+12,0,4); /* Zero padding for 32bit target. */\n}\n\n/* Given a key encoded with encodeTimeoutKey(), resolve the fields and write\n * the timeout into *toptr and the client pointer into *cptr. */\nvoid decodeTimeoutKey(unsigned char *buf, uint64_t *toptr, client **cptr) {\n    memcpy(toptr,buf,sizeof(*toptr));\n    *toptr = ntohu64(*toptr);\n    memcpy(cptr,buf+8,sizeof(*cptr));\n}\n\n/* Add the specified client id / timeout as a key in the radix tree we use\n * to handle blocked clients timeouts. The client is not added to the list\n * if its timeout is zero (block forever). */\nvoid addClientToTimeoutTable(client *c) {\n    if (c->bpop.timeout == 0) return;\n    uint64_t timeout = c->bpop.timeout;\n    unsigned char buf[CLIENT_ST_KEYLEN];\n    encodeTimeoutKey(buf,timeout,c);\n    if (raxTryInsert(server.clients_timeout_table,buf,sizeof(buf),NULL,NULL))\n        c->flags |= CLIENT_IN_TO_TABLE;\n}\n\n/* Remove the client from the table when it is unblocked for reasons\n * different than timing out. */\nvoid removeClientFromTimeoutTable(client *c) {\n    if (!(c->flags & CLIENT_IN_TO_TABLE)) return;\n    c->flags &= ~CLIENT_IN_TO_TABLE;\n    uint64_t timeout = c->bpop.timeout;\n    unsigned char buf[CLIENT_ST_KEYLEN];\n    encodeTimeoutKey(buf,timeout,c);\n    raxRemove(server.clients_timeout_table,buf,sizeof(buf),NULL);\n}\n\n/* This function is called in beforeSleep() in order to unblock clients\n * that are waiting in blocking operations with a timeout set. */\nvoid handleBlockedClientsTimeout(void) {\n    if (raxSize(server.clients_timeout_table) == 0) return;\n    uint64_t now = mstime();\n    raxIterator ri;\n    raxStart(&ri,server.clients_timeout_table);\n    raxSeek(&ri,\"^\",NULL,0);\n\n    while(raxNext(&ri)) {\n        uint64_t timeout;\n        client *c;\n        decodeTimeoutKey(ri.key,&timeout,&c);\n        if (timeout >= now) break; /* All the timeouts are in the future. */\n        c->flags &= ~CLIENT_IN_TO_TABLE;\n        checkBlockedClientTimeout(c,now);\n        raxRemove(server.clients_timeout_table,ri.key,ri.key_len,NULL);\n        raxSeek(&ri,\"^\",NULL,0);\n    }\n}\n\n/* Get a timeout value from an object and store it into 'timeout'.\n * The final timeout is always stored as milliseconds as a time where the\n * timeout will expire, however the parsing is performed according to\n * the 'unit' that can be seconds or milliseconds.\n *\n * Note that if the timeout is zero (usually from the point of view of\n * commands API this means no timeout) the value stored into 'timeout'\n * is zero. */\nint getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit) {\n    long long tval;\n    long double ftval;\n\n    if (unit == UNIT_SECONDS) {\n        if (getLongDoubleFromObjectOrReply(c,object,&ftval,\n            \"timeout is not a float or out of range\") != C_OK)\n            return C_ERR;\n        tval = (long long) (ftval * 1000.0);\n    } else {\n        if (getLongLongFromObjectOrReply(c,object,&tval,\n            \"timeout is not an integer or out of range\") != C_OK)\n            return C_ERR;\n    }\n\n    if (tval < 0) {\n        addReplyError(c,\"timeout is negative\");\n        return C_ERR;\n    }\n\n    if (tval > 0) {\n        tval += mstime();\n    }\n    *timeout = tval;\n\n    return C_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/tls.c",
    "content": "/*\n * Copyright (c) 2019, Redis Labs\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 \"server.h\"\n#include \"connhelpers.h\"\n#include \"adlist.h\"\n\n#ifdef USE_OPENSSL\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <openssl/rand.h>\n\n#define REDIS_TLS_PROTO_TLSv1       (1<<0)\n#define REDIS_TLS_PROTO_TLSv1_1     (1<<1)\n#define REDIS_TLS_PROTO_TLSv1_2     (1<<2)\n#define REDIS_TLS_PROTO_TLSv1_3     (1<<3)\n\n/* Use safe defaults */\n#ifdef TLS1_3_VERSION\n#define REDIS_TLS_PROTO_DEFAULT     (REDIS_TLS_PROTO_TLSv1_2|REDIS_TLS_PROTO_TLSv1_3)\n#else\n#define REDIS_TLS_PROTO_DEFAULT     (REDIS_TLS_PROTO_TLSv1_2)\n#endif\n\nextern ConnectionType CT_Socket;\n\nSSL_CTX *redis_tls_ctx;\n\nstatic int parseProtocolsConfig(const char *str) {\n    int i, count = 0;\n    int protocols = 0;\n\n    if (!str) return REDIS_TLS_PROTO_DEFAULT;\n    sds *tokens = sdssplitlen(str, strlen(str), \" \", 1, &count);\n\n    if (!tokens) { \n        serverLog(LL_WARNING, \"Invalid tls-protocols configuration string\");\n        return -1;\n    }\n    for (i = 0; i < count; i++) {\n        if (!strcasecmp(tokens[i], \"tlsv1\")) protocols |= REDIS_TLS_PROTO_TLSv1;\n        else if (!strcasecmp(tokens[i], \"tlsv1.1\")) protocols |= REDIS_TLS_PROTO_TLSv1_1;\n        else if (!strcasecmp(tokens[i], \"tlsv1.2\")) protocols |= REDIS_TLS_PROTO_TLSv1_2;\n        else if (!strcasecmp(tokens[i], \"tlsv1.3\")) {\n#ifdef TLS1_3_VERSION\n            protocols |= REDIS_TLS_PROTO_TLSv1_3;\n#else\n            serverLog(LL_WARNING, \"TLSv1.3 is specified in tls-protocols but not supported by OpenSSL.\");\n            protocols = -1;\n            break;\n#endif\n        } else {\n            serverLog(LL_WARNING, \"Invalid tls-protocols specified. \"\n                    \"Use a combination of 'TLSv1', 'TLSv1.1', 'TLSv1.2' and 'TLSv1.3'.\");\n            protocols = -1;\n            break;\n        }\n    }\n    sdsfreesplitres(tokens, count);\n\n    return protocols;\n}\n\n/* list of connections with pending data already read from the socket, but not\n * served to the reader yet. */\nstatic list *pending_list = NULL;\n\n/**\n * OpenSSL global initialization and locking handling callbacks.\n * Note that this is only required for OpenSSL < 1.1.0.\n */\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#define USE_CRYPTO_LOCKS\n#endif\n\n#ifdef USE_CRYPTO_LOCKS\n\nstatic pthread_mutex_t *openssl_locks;\n\nstatic void sslLockingCallback(int mode, int lock_id, const char *f, int line) {\n    pthread_mutex_t *mt = openssl_locks + lock_id;\n\n    if (mode & CRYPTO_LOCK) {\n        pthread_mutex_lock(mt);\n    } else {\n        pthread_mutex_unlock(mt);\n    }\n\n    (void)f;\n    (void)line;\n}\n\nstatic void initCryptoLocks(void) {\n    unsigned i, nlocks;\n    if (CRYPTO_get_locking_callback() != NULL) {\n        /* Someone already set the callback before us. Don't destroy it! */\n        return;\n    }\n    nlocks = CRYPTO_num_locks();\n    openssl_locks = zmalloc(sizeof(*openssl_locks) * nlocks);\n    for (i = 0; i < nlocks; i++) {\n        pthread_mutex_init(openssl_locks + i, NULL);\n    }\n    CRYPTO_set_locking_callback(sslLockingCallback);\n}\n#endif /* USE_CRYPTO_LOCKS */\n\nvoid tlsInit(void) {\n    ERR_load_crypto_strings();\n    SSL_load_error_strings();\n    SSL_library_init();\n\n#ifdef USE_CRYPTO_LOCKS\n    initCryptoLocks();\n#endif\n\n    if (!RAND_poll()) {\n        serverLog(LL_WARNING, \"OpenSSL: Failed to seed random number generator.\");\n    }\n\n    pending_list = listCreate();\n\n    /* Server configuration */\n    server.tls_auth_clients = 1;    /* Secure by default */\n}\n\n/* Attempt to configure/reconfigure TLS. This operation is atomic and will\n * leave the SSL_CTX unchanged if fails.\n */\nint tlsConfigure(redisTLSContextConfig *ctx_config) {\n    char errbuf[256];\n    SSL_CTX *ctx = NULL;\n\n    if (!ctx_config->cert_file) {\n        serverLog(LL_WARNING, \"No tls-cert-file configured!\");\n        goto error;\n    }\n\n    if (!ctx_config->key_file) {\n        serverLog(LL_WARNING, \"No tls-key-file configured!\");\n        goto error;\n    }\n\n    if (!ctx_config->ca_cert_file && !ctx_config->ca_cert_dir) {\n        serverLog(LL_WARNING, \"Either tls-ca-cert-file or tls-ca-cert-dir must be configured!\");\n        goto error;\n    }\n\n    ctx = SSL_CTX_new(SSLv23_method());\n\n    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);\n    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);\n\n#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS\n    SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);\n#endif\n\n    int protocols = parseProtocolsConfig(ctx_config->protocols);\n    if (protocols == -1) goto error;\n\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1_1))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);\n#ifdef SSL_OP_NO_TLSv1_2\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1_2))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);\n#endif\n#ifdef SSL_OP_NO_TLSv1_3\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1_3))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3);\n#endif\n\n#ifdef SSL_OP_NO_COMPRESSION\n    SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);\n#endif\n\n#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION\n    SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);\n#endif\n\n    if (ctx_config->prefer_server_ciphers)\n        SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);\n\n    SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);\n#if defined(SSL_CTX_set_ecdh_auto)\n    SSL_CTX_set_ecdh_auto(ctx, 1);\n#endif\n\n    if (SSL_CTX_use_certificate_file(ctx, ctx_config->cert_file, SSL_FILETYPE_PEM) <= 0) {\n        ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n        serverLog(LL_WARNING, \"Failed to load certificate: %s: %s\", ctx_config->cert_file, errbuf);\n        goto error;\n    }\n        \n    if (SSL_CTX_use_PrivateKey_file(ctx, ctx_config->key_file, SSL_FILETYPE_PEM) <= 0) {\n        ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n        serverLog(LL_WARNING, \"Failed to load private key: %s: %s\", ctx_config->key_file, errbuf);\n        goto error;\n    }\n    \n    if (SSL_CTX_load_verify_locations(ctx, ctx_config->ca_cert_file, ctx_config->ca_cert_dir) <= 0) {\n        ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n        serverLog(LL_WARNING, \"Failed to configure CA certificate(s) file/directory: %s\", errbuf);\n        goto error;\n    }\n\n    if (ctx_config->dh_params_file) {\n        FILE *dhfile = fopen(ctx_config->dh_params_file, \"r\");\n        DH *dh = NULL;\n        if (!dhfile) {\n            serverLog(LL_WARNING, \"Failed to load %s: %s\", ctx_config->dh_params_file, strerror(errno));\n            goto error;\n        }\n\n        dh = PEM_read_DHparams(dhfile, NULL, NULL, NULL);\n        fclose(dhfile);\n        if (!dh) {\n            serverLog(LL_WARNING, \"%s: failed to read DH params.\", ctx_config->dh_params_file);\n            goto error;\n        }\n\n        if (SSL_CTX_set_tmp_dh(ctx, dh) <= 0) {\n            ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n            serverLog(LL_WARNING, \"Failed to load DH params file: %s: %s\", ctx_config->dh_params_file, errbuf);\n            DH_free(dh);\n            goto error;\n        }\n\n        DH_free(dh);\n    }\n\n    if (ctx_config->ciphers && !SSL_CTX_set_cipher_list(ctx, ctx_config->ciphers)) {\n        serverLog(LL_WARNING, \"Failed to configure ciphers: %s\", ctx_config->ciphers);\n        goto error;\n    }\n\n#ifdef TLS1_3_VERSION\n    if (ctx_config->ciphersuites && !SSL_CTX_set_ciphersuites(ctx, ctx_config->ciphersuites)) {\n        serverLog(LL_WARNING, \"Failed to configure ciphersuites: %s\", ctx_config->ciphersuites);\n        goto error;\n    }\n#endif\n\n    SSL_CTX_free(redis_tls_ctx);\n    redis_tls_ctx = ctx;\n\n    return C_OK;\n\nerror:\n    if (ctx) SSL_CTX_free(ctx);\n    return C_ERR;\n}\n\n#ifdef TLS_DEBUGGING\n#define TLSCONN_DEBUG(fmt, ...) \\\n    serverLog(LL_DEBUG, \"TLSCONN: \" fmt, __VA_ARGS__)\n#else\n#define TLSCONN_DEBUG(fmt, ...)\n#endif\n\nConnectionType CT_TLS;\n\n/* Normal socket connections have a simple events/handler correlation.\n *\n * With TLS connections we need to handle cases where during a logical read\n * or write operation, the SSL library asks to block for the opposite\n * socket operation.\n *\n * When this happens, we need to do two things:\n * 1. Make sure we register for the even.\n * 2. Make sure we know which handler needs to execute when the\n *    event fires.  That is, if we notify the caller of a write operation\n *    that it blocks, and SSL asks for a read, we need to trigger the\n *    write handler again on the next read event.\n *\n */\n\ntypedef enum {\n    WANT_READ = 1,\n    WANT_WRITE\n} WantIOType;\n\n#define TLS_CONN_FLAG_READ_WANT_WRITE   (1<<0)\n#define TLS_CONN_FLAG_WRITE_WANT_READ   (1<<1)\n#define TLS_CONN_FLAG_FD_SET            (1<<2)\n\ntypedef struct tls_connection {\n    connection c;\n    int flags;\n    SSL *ssl;\n    char *ssl_error;\n    listNode *pending_list_node;\n} tls_connection;\n\nconnection *connCreateTLS(void) {\n    tls_connection *conn = zcalloc(sizeof(tls_connection));\n    conn->c.type = &CT_TLS;\n    conn->c.fd = -1;\n    conn->ssl = SSL_new(redis_tls_ctx);\n    return (connection *) conn;\n}\n\nconnection *connCreateAcceptedTLS(int fd, int require_auth) {\n    tls_connection *conn = (tls_connection *) connCreateTLS();\n    conn->c.fd = fd;\n    conn->c.state = CONN_STATE_ACCEPTING;\n\n    if (!require_auth) {\n        /* We still verify certificates if provided, but don't require them.\n         */\n        SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, NULL);\n    }\n\n    SSL_set_fd(conn->ssl, conn->c.fd);\n    SSL_set_accept_state(conn->ssl);\n\n    return (connection *) conn;\n}\n\nstatic void tlsEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask);\n\n/* Process the return code received from OpenSSL>\n * Update the want parameter with expected I/O.\n * Update the connection's error state if a real error has occured.\n * Returns an SSL error code, or 0 if no further handling is required.\n */\nstatic int handleSSLReturnCode(tls_connection *conn, int ret_value, WantIOType *want) {\n    if (ret_value <= 0) {\n        int ssl_err = SSL_get_error(conn->ssl, ret_value);\n        switch (ssl_err) {\n            case SSL_ERROR_WANT_WRITE:\n                *want = WANT_WRITE;\n                return 0;\n            case SSL_ERROR_WANT_READ:\n                *want = WANT_READ;\n                return 0;\n            case SSL_ERROR_SYSCALL:\n                conn->c.last_errno = errno;\n                if (conn->ssl_error) zfree(conn->ssl_error);\n                conn->ssl_error = errno ? zstrdup(strerror(errno)) : NULL;\n                break;\n            default:\n                /* Error! */\n                conn->c.last_errno = 0;\n                if (conn->ssl_error) zfree(conn->ssl_error);\n                conn->ssl_error = zmalloc(512);\n                ERR_error_string_n(ERR_get_error(), conn->ssl_error, 512);\n                break;\n        }\n\n        return ssl_err;\n    }\n\n    return 0;\n}\n\nvoid registerSSLEvent(tls_connection *conn, WantIOType want) {\n    int mask = aeGetFileEvents(server.el, conn->c.fd);\n\n    switch (want) {\n        case WANT_READ:\n            if (mask & AE_WRITABLE) aeDeleteFileEvent(server.el, conn->c.fd, AE_WRITABLE);\n            if (!(mask & AE_READABLE)) aeCreateFileEvent(server.el, conn->c.fd, AE_READABLE,\n                        tlsEventHandler, conn);\n            break;\n        case WANT_WRITE:\n            if (mask & AE_READABLE) aeDeleteFileEvent(server.el, conn->c.fd, AE_READABLE);\n            if (!(mask & AE_WRITABLE)) aeCreateFileEvent(server.el, conn->c.fd, AE_WRITABLE,\n                        tlsEventHandler, conn);\n            break;\n        default:\n            serverAssert(0);\n            break;\n    }\n}\n\nvoid updateSSLEvent(tls_connection *conn) {\n    int mask = aeGetFileEvents(server.el, conn->c.fd);\n    int need_read = conn->c.read_handler || (conn->flags & TLS_CONN_FLAG_WRITE_WANT_READ);\n    int need_write = conn->c.write_handler || (conn->flags & TLS_CONN_FLAG_READ_WANT_WRITE);\n\n    if (need_read && !(mask & AE_READABLE))\n        aeCreateFileEvent(server.el, conn->c.fd, AE_READABLE, tlsEventHandler, conn);\n    if (!need_read && (mask & AE_READABLE))\n        aeDeleteFileEvent(server.el, conn->c.fd, AE_READABLE);\n\n    if (need_write && !(mask & AE_WRITABLE))\n        aeCreateFileEvent(server.el, conn->c.fd, AE_WRITABLE, tlsEventHandler, conn);\n    if (!need_write && (mask & AE_WRITABLE))\n        aeDeleteFileEvent(server.el, conn->c.fd, AE_WRITABLE);\n}\n\nstatic void tlsHandleEvent(tls_connection *conn, int mask) {\n    int ret;\n\n    TLSCONN_DEBUG(\"tlsEventHandler(): fd=%d, state=%d, mask=%d, r=%d, w=%d, flags=%d\",\n            fd, conn->c.state, mask, conn->c.read_handler != NULL, conn->c.write_handler != NULL,\n            conn->flags);\n\n    ERR_clear_error();\n\n    switch (conn->c.state) {\n        case CONN_STATE_CONNECTING:\n            if (connGetSocketError((connection *) conn)) {\n                conn->c.last_errno = errno;\n                conn->c.state = CONN_STATE_ERROR;\n            } else {\n                if (!(conn->flags & TLS_CONN_FLAG_FD_SET)) {\n                    SSL_set_fd(conn->ssl, conn->c.fd);\n                    conn->flags |= TLS_CONN_FLAG_FD_SET;\n                }\n                ret = SSL_connect(conn->ssl);\n                if (ret <= 0) {\n                    WantIOType want = 0;\n                    if (!handleSSLReturnCode(conn, ret, &want)) {\n                        registerSSLEvent(conn, want);\n\n                        /* Avoid hitting UpdateSSLEvent, which knows nothing\n                         * of what SSL_connect() wants and instead looks at our\n                         * R/W handlers.\n                         */\n                        return;\n                    }\n\n                    /* If not handled, it's an error */\n                    conn->c.state = CONN_STATE_ERROR;\n                } else {\n                    conn->c.state = CONN_STATE_CONNECTED;\n                }\n            }\n\n            if (!callHandler((connection *) conn, conn->c.conn_handler)) return;\n            conn->c.conn_handler = NULL;\n            break;\n        case CONN_STATE_ACCEPTING:\n            ret = SSL_accept(conn->ssl);\n            if (ret <= 0) {\n                WantIOType want = 0;\n                if (!handleSSLReturnCode(conn, ret, &want)) {\n                    /* Avoid hitting UpdateSSLEvent, which knows nothing\n                     * of what SSL_connect() wants and instead looks at our\n                     * R/W handlers.\n                     */\n                    registerSSLEvent(conn, want);\n                    return;\n                }\n\n                /* If not handled, it's an error */\n                conn->c.state = CONN_STATE_ERROR;\n            } else {\n                conn->c.state = CONN_STATE_CONNECTED;\n            }\n\n            if (!callHandler((connection *) conn, conn->c.conn_handler)) return;\n            conn->c.conn_handler = NULL;\n            break;\n        case CONN_STATE_CONNECTED:\n        {\n            int call_read = ((mask & AE_READABLE) && conn->c.read_handler) ||\n                ((mask & AE_WRITABLE) && (conn->flags & TLS_CONN_FLAG_READ_WANT_WRITE));\n            int call_write = ((mask & AE_WRITABLE) && conn->c.write_handler) ||\n                ((mask & AE_READABLE) && (conn->flags & TLS_CONN_FLAG_WRITE_WANT_READ));\n\n            /* Normally we execute the readable event first, and the writable\n             * event laster. This is useful as sometimes we may be able\n             * to serve the reply of a query immediately after processing the\n             * query.\n             *\n             * However if WRITE_BARRIER is set in the mask, our application is\n             * asking us to do the reverse: never fire the writable event\n             * after the readable. In such a case, we invert the calls.\n             * This is useful when, for instance, we want to do things\n             * in the beforeSleep() hook, like fsynching a file to disk,\n             * before replying to a client. */\n            int invert = conn->c.flags & CONN_FLAG_WRITE_BARRIER;\n\n            if (!invert && call_read) {\n                conn->flags &= ~TLS_CONN_FLAG_READ_WANT_WRITE;\n                if (!callHandler((connection *) conn, conn->c.read_handler)) return;\n            }\n\n            /* Fire the writable event. */\n            if (call_write) {\n                conn->flags &= ~TLS_CONN_FLAG_WRITE_WANT_READ;\n                if (!callHandler((connection *) conn, conn->c.write_handler)) return;\n            }\n\n            /* If we have to invert the call, fire the readable event now\n             * after the writable one. */\n            if (invert && call_read) {\n                conn->flags &= ~TLS_CONN_FLAG_READ_WANT_WRITE;\n                if (!callHandler((connection *) conn, conn->c.read_handler)) return;\n            }\n\n            /* If SSL has pending that, already read from the socket, we're at\n             * risk of not calling the read handler again, make sure to add it\n             * to a list of pending connection that should be handled anyway. */\n            if ((mask & AE_READABLE)) {\n                if (SSL_pending(conn->ssl) > 0) {\n                    if (!conn->pending_list_node) {\n                        listAddNodeTail(pending_list, conn);\n                        conn->pending_list_node = listLast(pending_list);\n                    }\n                } else if (conn->pending_list_node) {\n                    listDelNode(pending_list, conn->pending_list_node);\n                    conn->pending_list_node = NULL;\n                }\n            }\n\n            break;\n        }\n        default:\n            break;\n    }\n\n    updateSSLEvent(conn);\n}\n\nstatic void tlsEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask) {\n    UNUSED(el);\n    UNUSED(fd);\n    tls_connection *conn = clientData;\n    tlsHandleEvent(conn, mask);\n}\n\nstatic void connTLSClose(connection *conn_) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    if (conn->ssl) {\n        SSL_free(conn->ssl);\n        conn->ssl = NULL;\n    }\n\n    if (conn->ssl_error) {\n        zfree(conn->ssl_error);\n        conn->ssl_error = NULL;\n    }\n\n    if (conn->pending_list_node) {\n        listDelNode(pending_list, conn->pending_list_node);\n        conn->pending_list_node = NULL;\n    }\n\n    CT_Socket.close(conn_);\n}\n\nstatic int connTLSAccept(connection *_conn, ConnectionCallbackFunc accept_handler) {\n    tls_connection *conn = (tls_connection *) _conn;\n    int ret;\n\n    if (conn->c.state != CONN_STATE_ACCEPTING) return C_ERR;\n    ERR_clear_error();\n\n    /* Try to accept */\n    conn->c.conn_handler = accept_handler;\n    ret = SSL_accept(conn->ssl);\n\n    if (ret <= 0) {\n        WantIOType want = 0;\n        if (!handleSSLReturnCode(conn, ret, &want)) {\n            registerSSLEvent(conn, want);   /* We'll fire back */\n            return C_OK;\n        } else {\n            conn->c.state = CONN_STATE_ERROR;\n            return C_ERR;\n        }\n    }\n\n    conn->c.state = CONN_STATE_CONNECTED;\n    if (!callHandler((connection *) conn, conn->c.conn_handler)) return C_OK;\n    conn->c.conn_handler = NULL;\n\n    return C_OK;\n}\n\nstatic int connTLSConnect(connection *conn_, const char *addr, int port, const char *src_addr, ConnectionCallbackFunc connect_handler) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    if (conn->c.state != CONN_STATE_NONE) return C_ERR;\n    ERR_clear_error();\n\n    /* Initiate Socket connection first */\n    if (CT_Socket.connect(conn_, addr, port, src_addr, connect_handler) == C_ERR) return C_ERR;\n\n    /* Return now, once the socket is connected we'll initiate\n     * TLS connection from the event handler.\n     */\n    return C_OK;\n}\n\nstatic int connTLSWrite(connection *conn_, const void *data, size_t data_len) {\n    tls_connection *conn = (tls_connection *) conn_;\n    int ret, ssl_err;\n\n    if (conn->c.state != CONN_STATE_CONNECTED) return -1;\n    ERR_clear_error();\n    ret = SSL_write(conn->ssl, data, data_len);\n\n    if (ret <= 0) {\n        WantIOType want = 0;\n        if (!(ssl_err = handleSSLReturnCode(conn, ret, &want))) {\n            if (want == WANT_READ) conn->flags |= TLS_CONN_FLAG_WRITE_WANT_READ;\n            updateSSLEvent(conn);\n            errno = EAGAIN;\n            return -1;\n        } else {\n            if (ssl_err == SSL_ERROR_ZERO_RETURN ||\n                    ((ssl_err == SSL_ERROR_SYSCALL && !errno))) {\n                conn->c.state = CONN_STATE_CLOSED;\n                return 0;\n            } else {\n                conn->c.state = CONN_STATE_ERROR;\n                return -1;\n            }\n        }\n    }\n\n    return ret;\n}\n\nstatic int connTLSRead(connection *conn_, void *buf, size_t buf_len) {\n    tls_connection *conn = (tls_connection *) conn_;\n    int ret;\n    int ssl_err;\n\n    if (conn->c.state != CONN_STATE_CONNECTED) return -1;\n    ERR_clear_error();\n    ret = SSL_read(conn->ssl, buf, buf_len);\n    if (ret <= 0) {\n        WantIOType want = 0;\n        if (!(ssl_err = handleSSLReturnCode(conn, ret, &want))) {\n            if (want == WANT_WRITE) conn->flags |= TLS_CONN_FLAG_READ_WANT_WRITE;\n            updateSSLEvent(conn);\n\n            errno = EAGAIN;\n            return -1;\n        } else {\n            if (ssl_err == SSL_ERROR_ZERO_RETURN ||\n                    ((ssl_err == SSL_ERROR_SYSCALL) && !errno)) {\n                conn->c.state = CONN_STATE_CLOSED;\n                return 0;\n            } else {\n                conn->c.state = CONN_STATE_ERROR;\n                return -1;\n            }\n        }\n    }\n\n    return ret;\n}\n\nstatic const char *connTLSGetLastError(connection *conn_) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    if (conn->ssl_error) return conn->ssl_error;\n    return NULL;\n}\n\nint connTLSSetWriteHandler(connection *conn, ConnectionCallbackFunc func, int barrier) {\n    conn->write_handler = func;\n    if (barrier)\n        conn->flags |= CONN_FLAG_WRITE_BARRIER;\n    else\n        conn->flags &= ~CONN_FLAG_WRITE_BARRIER;\n    updateSSLEvent((tls_connection *) conn);\n    return C_OK;\n}\n\nint connTLSSetReadHandler(connection *conn, ConnectionCallbackFunc func) {\n    conn->read_handler = func;\n    updateSSLEvent((tls_connection *) conn);\n    return C_OK;\n}\n\nstatic void setBlockingTimeout(tls_connection *conn, long long timeout) {\n    anetBlock(NULL, conn->c.fd);\n    anetSendTimeout(NULL, conn->c.fd, timeout);\n    anetRecvTimeout(NULL, conn->c.fd, timeout);\n}\n\nstatic void unsetBlockingTimeout(tls_connection *conn) {\n    anetNonBlock(NULL, conn->c.fd);\n    anetSendTimeout(NULL, conn->c.fd, 0);\n    anetRecvTimeout(NULL, conn->c.fd, 0);\n}\n\nstatic int connTLSBlockingConnect(connection *conn_, const char *addr, int port, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n    int ret;\n\n    if (conn->c.state != CONN_STATE_NONE) return C_ERR;\n\n    /* Initiate socket blocking connect first */\n    if (CT_Socket.blocking_connect(conn_, addr, port, timeout) == C_ERR) return C_ERR;\n\n    /* Initiate TLS connection now.  We set up a send/recv timeout on the socket,\n     * which means the specified timeout will not be enforced accurately. */\n    SSL_set_fd(conn->ssl, conn->c.fd);\n    setBlockingTimeout(conn, timeout);\n\n    if ((ret = SSL_connect(conn->ssl)) <= 0) {\n        conn->c.state = CONN_STATE_ERROR;\n        return C_ERR;\n    }\n    unsetBlockingTimeout(conn);\n\n    conn->c.state = CONN_STATE_CONNECTED;\n    return C_OK;\n}\n\nstatic ssize_t connTLSSyncWrite(connection *conn_, char *ptr, ssize_t size, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    setBlockingTimeout(conn, timeout);\n    SSL_clear_mode(conn->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);\n    int ret = SSL_write(conn->ssl, ptr, size);\n    SSL_set_mode(conn->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);\n    unsetBlockingTimeout(conn);\n\n    return ret;\n}\n\nstatic ssize_t connTLSSyncRead(connection *conn_, char *ptr, ssize_t size, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    setBlockingTimeout(conn, timeout);\n    int ret = SSL_read(conn->ssl, ptr, size);\n    unsetBlockingTimeout(conn);\n\n    return ret;\n}\n\nstatic ssize_t connTLSSyncReadLine(connection *conn_, char *ptr, ssize_t size, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n    ssize_t nread = 0;\n\n    setBlockingTimeout(conn, timeout);\n\n    size--;\n    while(size) {\n        char c;\n\n        if (SSL_read(conn->ssl,&c,1) <= 0) {\n            nread = -1;\n            goto exit;\n        }\n        if (c == '\\n') {\n            *ptr = '\\0';\n            if (nread && *(ptr-1) == '\\r') *(ptr-1) = '\\0';\n            goto exit;\n        } else {\n            *ptr++ = c;\n            *ptr = '\\0';\n            nread++;\n        }\n        size--;\n    }\nexit:\n    unsetBlockingTimeout(conn);\n    return nread;\n}\n\nConnectionType CT_TLS = {\n    .ae_handler = tlsEventHandler,\n    .accept = connTLSAccept,\n    .connect = connTLSConnect,\n    .blocking_connect = connTLSBlockingConnect,\n    .read = connTLSRead,\n    .write = connTLSWrite,\n    .close = connTLSClose,\n    .set_write_handler = connTLSSetWriteHandler,\n    .set_read_handler = connTLSSetReadHandler,\n    .get_last_error = connTLSGetLastError,\n    .sync_write = connTLSSyncWrite,\n    .sync_read = connTLSSyncRead,\n    .sync_readline = connTLSSyncReadLine,\n};\n\nint tlsHasPendingData() {\n    if (!pending_list)\n        return 0;\n    return listLength(pending_list) > 0;\n}\n\nint tlsProcessPendingData() {\n    listIter li;\n    listNode *ln;\n\n    int processed = listLength(pending_list);\n    listRewind(pending_list,&li);\n    while((ln = listNext(&li))) {\n        tls_connection *conn = listNodeValue(ln);\n        tlsHandleEvent(conn, AE_READABLE);\n    }\n    return processed;\n}\n\n#else   /* USE_OPENSSL */\n\nvoid tlsInit(void) {\n}\n\nint tlsConfigure(redisTLSContextConfig *ctx_config) {\n    UNUSED(ctx_config);\n    return C_OK;\n}\n\nconnection *connCreateTLS(void) { \n    return NULL;\n}\n\nconnection *connCreateAcceptedTLS(int fd, int require_auth) {\n    UNUSED(fd);\n    UNUSED(require_auth);\n\n    return NULL;\n}\n\nint tlsHasPendingData() {\n    return 0;\n}\n\nint tlsProcessPendingData() {\n    return 0;\n}\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/tracking.c",
    "content": "/* tracking.c - Client side caching: keys tracking and invalidation\n *\n * Copyright (c) 2019, 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 \"server.h\"\n\n/* The tracking table is constituted by a radix tree of keys, each pointing\n * to a radix tree of client IDs, used to track the clients that may have\n * certain keys in their local, client side, cache.\n *\n * When a client enables tracking with \"CLIENT TRACKING on\", each key served to\n * the client is remembered in the table mapping the keys to the client IDs.\n * Later, when a key is modified, all the clients that may have local copy\n * of such key will receive an invalidation message.\n *\n * Clients will normally take frequently requested objects in memory, removing\n * them when invalidation messages are received. */\nrax *TrackingTable = NULL;\nrax *PrefixTable = NULL;\nuint64_t TrackingTableTotalItems = 0; /* Total number of IDs stored across\n                                         the whole tracking table. This gives\n                                         an hint about the total memory we\n                                         are using server side for CSC. */\nrobj *TrackingChannelName;\n\n/* This is the structure that we have as value of the PrefixTable, and\n * represents the list of keys modified, and the list of clients that need\n * to be notified, for a given prefix. */\ntypedef struct bcastState {\n    rax *keys;      /* Keys modified in the current event loop cycle. */\n    rax *clients;   /* Clients subscribed to the notification events for this\n                       prefix. */\n} bcastState;\n\n/* Remove the tracking state from the client 'c'. Note that there is not much\n * to do for us here, if not to decrement the counter of the clients in\n * tracking mode, because we just store the ID of the client in the tracking\n * table, so we'll remove the ID reference in a lazy way. Otherwise when a\n * client with many entries in the table is removed, it would cost a lot of\n * time to do the cleanup. */\nvoid disableTracking(client *c) {\n    /* If this client is in broadcasting mode, we need to unsubscribe it\n     * from all the prefixes it is registered to. */\n    if (c->flags & CLIENT_TRACKING_BCAST) {\n        raxIterator ri;\n        raxStart(&ri,c->client_tracking_prefixes);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            bcastState *bs = raxFind(PrefixTable,ri.key,ri.key_len);\n            serverAssert(bs != raxNotFound);\n            raxRemove(bs->clients,(unsigned char*)&c,sizeof(c),NULL);\n            /* Was it the last client? Remove the prefix from the\n             * table. */\n            if (raxSize(bs->clients) == 0) {\n                raxFree(bs->clients);\n                raxFree(bs->keys);\n                zfree(bs);\n                raxRemove(PrefixTable,ri.key,ri.key_len,NULL);\n            }\n        }\n        raxStop(&ri);\n        raxFree(c->client_tracking_prefixes);\n        c->client_tracking_prefixes = NULL;\n    }\n\n    /* Clear flags and adjust the count. */\n    if (c->flags & CLIENT_TRACKING) {\n        server.tracking_clients--;\n        c->flags &= ~(CLIENT_TRACKING|CLIENT_TRACKING_BROKEN_REDIR|\n                      CLIENT_TRACKING_BCAST|CLIENT_TRACKING_OPTIN|\n                      CLIENT_TRACKING_OPTOUT|CLIENT_TRACKING_CACHING|\n                      CLIENT_TRACKING_NOLOOP);\n    }\n}\n\n/* Set the client 'c' to track the prefix 'prefix'. If the client 'c' is\n * already registered for the specified prefix, no operation is performed. */\nvoid enableBcastTrackingForPrefix(client *c, char *prefix, size_t plen) {\n    bcastState *bs = raxFind(PrefixTable,(unsigned char*)prefix,sdslen(prefix));\n    /* If this is the first client subscribing to such prefix, create\n     * the prefix in the table. */\n    if (bs == raxNotFound) {\n        bs = zmalloc(sizeof(*bs));\n        bs->keys = raxNew();\n        bs->clients = raxNew();\n        raxInsert(PrefixTable,(unsigned char*)prefix,plen,bs,NULL);\n    }\n    if (raxTryInsert(bs->clients,(unsigned char*)&c,sizeof(c),NULL,NULL)) {\n        if (c->client_tracking_prefixes == NULL)\n            c->client_tracking_prefixes = raxNew();\n        raxInsert(c->client_tracking_prefixes,\n                  (unsigned char*)prefix,plen,NULL,NULL);\n    }\n}\n\n/* Enable the tracking state for the client 'c', and as a side effect allocates\n * the tracking table if needed. If the 'redirect_to' argument is non zero, the\n * invalidation messages for this client will be sent to the client ID\n * specified by the 'redirect_to' argument. Note that if such client will\n * eventually get freed, we'll send a message to the original client to\n * inform it of the condition. Multiple clients can redirect the invalidation\n * messages to the same client ID. */\nvoid enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **prefix, size_t numprefix) {\n    if (!(c->flags & CLIENT_TRACKING)) server.tracking_clients++;\n    c->flags |= CLIENT_TRACKING;\n    c->flags &= ~(CLIENT_TRACKING_BROKEN_REDIR|CLIENT_TRACKING_BCAST|\n                  CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT|\n                  CLIENT_TRACKING_NOLOOP);\n    c->client_tracking_redirection = redirect_to;\n\n    /* This may be the first client we ever enable. Crete the tracking\n     * table if it does not exist. */\n    if (TrackingTable == NULL) {\n        TrackingTable = raxNew();\n        PrefixTable = raxNew();\n        TrackingChannelName = createStringObject(\"__redis__:invalidate\",20);\n    }\n\n    /* For broadcasting, set the list of prefixes in the client. */\n    if (options & CLIENT_TRACKING_BCAST) {\n        c->flags |= CLIENT_TRACKING_BCAST;\n        if (numprefix == 0) enableBcastTrackingForPrefix(c,\"\",0);\n        for (size_t j = 0; j < numprefix; j++) {\n            sds sdsprefix = prefix[j]->ptr;\n            enableBcastTrackingForPrefix(c,sdsprefix,sdslen(sdsprefix));\n        }\n    }\n\n    /* Set the remaining flags that don't need any special handling. */\n    c->flags |= options & (CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT|\n                           CLIENT_TRACKING_NOLOOP);\n}\n\n/* This function is called after the execution of a readonly command in the\n * case the client 'c' has keys tracking enabled and the tracking is not\n * in BCAST mode. It will populate the tracking invalidation table according\n * to the keys the user fetched, so that Redis will know what are the clients\n * that should receive an invalidation message with certain groups of keys\n * are modified. */\nvoid trackingRememberKeys(client *c) {\n    /* Return if we are in optin/out mode and the right CACHING command\n     * was/wasn't given in order to modify the default behavior. */\n    uint64_t optin = c->flags & CLIENT_TRACKING_OPTIN;\n    uint64_t optout = c->flags & CLIENT_TRACKING_OPTOUT;\n    uint64_t caching_given = c->flags & CLIENT_TRACKING_CACHING;\n    if ((optin && !caching_given) || (optout && caching_given)) return;\n\n    int numkeys;\n    int *keys = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);\n    if (keys == NULL) return;\n\n    for(int j = 0; j < numkeys; j++) {\n        int idx = keys[j];\n        sds sdskey = c->argv[idx]->ptr;\n        rax *ids = raxFind(TrackingTable,(unsigned char*)sdskey,sdslen(sdskey));\n        if (ids == raxNotFound) {\n            ids = raxNew();\n            int inserted = raxTryInsert(TrackingTable,(unsigned char*)sdskey,\n                                        sdslen(sdskey),ids, NULL);\n            serverAssert(inserted == 1);\n        }\n        if (raxTryInsert(ids,(unsigned char*)&c->id,sizeof(c->id),NULL,NULL))\n            TrackingTableTotalItems++;\n    }\n    getKeysFreeResult(keys);\n}\n\n/* Given a key name, this function sends an invalidation message in the\n * proper channel (depending on RESP version: PubSub or Push message) and\n * to the proper client (in case fo redirection), in the context of the\n * client 'c' with tracking enabled.\n *\n * In case the 'proto' argument is non zero, the function will assume that\n * 'keyname' points to a buffer of 'keylen' bytes already expressed in the\n * form of Redis RESP protocol, representing an array of keys to send\n * to the client as value of the invalidation. This is used in BCAST mode\n * in order to optimized the implementation to use less CPU time. */\nvoid sendTrackingMessage(client *c, char *keyname, size_t keylen, int proto) {\n    int using_redirection = 0;\n    if (c->client_tracking_redirection) {\n        client *redir = lookupClientByID(c->client_tracking_redirection);\n        if (!redir) {\n            /* We need to signal to the original connection that we\n             * are unable to send invalidation messages to the redirected\n             * connection, because the client no longer exist. */\n            if (c->resp > 2) {\n                addReplyPushLen(c,3);\n                addReplyBulkCBuffer(c,\"tracking-redir-broken\",21);\n                addReplyLongLong(c,c->client_tracking_redirection);\n            }\n            return;\n        }\n        c = redir;\n        using_redirection = 1;\n    }\n\n    /* Only send such info for clients in RESP version 3 or more. However\n     * if redirection is active, and the connection we redirect to is\n     * in Pub/Sub mode, we can support the feature with RESP 2 as well,\n     * by sending Pub/Sub messages in the __redis__:invalidate channel. */\n    if (c->resp > 2) {\n        addReplyPushLen(c,2);\n        addReplyBulkCBuffer(c,\"invalidate\",10);\n    } else if (using_redirection && c->flags & CLIENT_PUBSUB) {\n        /* We use a static object to speedup things, however we assume\n         * that addReplyPubsubMessage() will not take a reference. */\n        addReplyPubsubMessage(c,TrackingChannelName,NULL);\n    } else {\n        /* If are here, the client is not using RESP3, nor is\n         * redirecting to another client. We can't send anything to\n         * it since RESP2 does not support push messages in the same\n         * connection. */\n        return;\n    }\n\n    /* Send the \"value\" part, which is the array of keys. */\n    if (proto) {\n        addReplyProto(c,keyname,keylen);\n    } else {\n        addReplyArrayLen(c,1);\n        addReplyBulkCBuffer(c,keyname,keylen);\n    }\n}\n\n/* This function is called when a key is modified in Redis and in the case\n * we have at least one client with the BCAST mode enabled.\n * Its goal is to set the key in the right broadcast state if the key\n * matches one or more prefixes in the prefix table. Later when we\n * return to the event loop, we'll send invalidation messages to the\n * clients subscribed to each prefix. */\nvoid trackingRememberKeyToBroadcast(client *c, char *keyname, size_t keylen) {\n    raxIterator ri;\n    raxStart(&ri,PrefixTable);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        if (ri.key_len > keylen) continue;\n        if (ri.key_len != 0 && memcmp(ri.key,keyname,ri.key_len) != 0)\n            continue;\n        bcastState *bs = ri.data;\n        /* We insert the client pointer as associated value in the radix\n         * tree. This way we know who was the client that did the last\n         * change to the key, and can avoid sending the notification in the\n         * case the client is in NOLOOP mode. */\n        raxTryInsert(bs->keys,(unsigned char*)keyname,keylen,c,NULL);\n    }\n    raxStop(&ri);\n}\n\n/* This function is called from signalModifiedKey() or other places in Redis\n * when a key changes value. In the context of keys tracking, our task here is\n * to send a notification to every client that may have keys about such caching\n * slot.\n *\n * Note that 'c' may be NULL in case the operation was performed outside the\n * context of a client modifying the database (for instance when we delete a\n * key because of expire).\n *\n * The last argument 'bcast' tells the function if it should also schedule\n * the key for broadcasting to clients in BCAST mode. This is the case when\n * the function is called from the Redis core once a key is modified, however\n * we also call the function in order to evict keys in the key table in case\n * of memory pressure: in that case the key didn't really change, so we want\n * just to notify the clients that are in the table for this key, that would\n * otherwise miss the fact we are no longer tracking the key for them. */\nvoid trackingInvalidateKeyRaw(client *c, char *key, size_t keylen, int bcast) {\n    if (TrackingTable == NULL) return;\n\n    if (bcast && raxSize(PrefixTable) > 0)\n        trackingRememberKeyToBroadcast(c,key,keylen);\n\n    rax *ids = raxFind(TrackingTable,(unsigned char*)key,keylen);\n    if (ids == raxNotFound) return;\n\n    raxIterator ri;\n    raxStart(&ri,ids);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        uint64_t id;\n        memcpy(&id,ri.key,sizeof(id));\n        client *target = lookupClientByID(id);\n        /* Note that if the client is in BCAST mode, we don't want to\n         * send invalidation messages that were pending in the case\n         * previously the client was not in BCAST mode. This can happen if\n         * TRACKING is enabled normally, and then the client switches to\n         * BCAST mode. */\n        if (target == NULL ||\n            !(target->flags & CLIENT_TRACKING)||\n            target->flags & CLIENT_TRACKING_BCAST)\n        {\n            continue;\n        }\n\n        /* If the client enabled the NOLOOP mode, don't send notifications\n         * about keys changed by the client itself. */\n        if (target->flags & CLIENT_TRACKING_NOLOOP &&\n            target == c)\n        {\n            continue;\n        }\n\n        sendTrackingMessage(target,key,keylen,0);\n    }\n    raxStop(&ri);\n\n    /* Free the tracking table: we'll create the radix tree and populate it\n     * again if more keys will be modified in this caching slot. */\n    TrackingTableTotalItems -= raxSize(ids);\n    raxFree(ids);\n    raxRemove(TrackingTable,(unsigned char*)key,keylen,NULL);\n}\n\n/* Wrapper (the one actually called across the core) to pass the key\n * as object. */\nvoid trackingInvalidateKey(client *c, robj *keyobj) {\n    trackingInvalidateKeyRaw(c,keyobj->ptr,sdslen(keyobj->ptr),1);\n}\n\n/* This function is called when one or all the Redis databases are flushed\n * (dbid == -1 in case of FLUSHALL). Caching keys are not specific for\n * each DB but are global: currently what we do is send a special\n * notification to clients with tracking enabled, invalidating the caching\n * key \"\", which means, \"all the keys\", in order to avoid flooding clients\n * with many invalidation messages for all the keys they may hold.\n */\nvoid freeTrackingRadixTree(void *rt) {\n    raxFree(rt);\n}\n\nvoid trackingInvalidateKeysOnFlush(int dbid) {\n    if (server.tracking_clients) {\n        listNode *ln;\n        listIter li;\n        listRewind(server.clients,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            client *c = listNodeValue(ln);\n            if (c->flags & CLIENT_TRACKING) {\n                sendTrackingMessage(c,\"\",1,0);\n            }\n        }\n    }\n\n    /* In case of FLUSHALL, reclaim all the memory used by tracking. */\n    if (dbid == -1 && TrackingTable) {\n        raxFreeWithCallback(TrackingTable,freeTrackingRadixTree);\n        TrackingTable = raxNew();\n        TrackingTableTotalItems = 0;\n    }\n}\n\n/* Tracking forces Redis to remember information about which client may have\n * certain keys. In workloads where there are a lot of reads, but keys are\n * hardly modified, the amount of information we have to remember server side\n * could be a lot, with the number of keys being totally not bound.\n *\n * So Redis allows the user to configure a maximum number of keys for the\n * invalidation table. This function makes sure that we don't go over the\n * specified fill rate: if we are over, we can just evict informations about\n * a random key, and send invalidation messages to clients like if the key was\n * modified. */\nvoid trackingLimitUsedSlots(void) {\n    static unsigned int timeout_counter = 0;\n    if (TrackingTable == NULL) return;\n    if (server.tracking_table_max_keys == 0) return; /* No limits set. */\n    size_t max_keys = server.tracking_table_max_keys;\n    if (raxSize(TrackingTable) <= max_keys) {\n        timeout_counter = 0;\n        return; /* Limit not reached. */\n    }\n\n    /* We have to invalidate a few keys to reach the limit again. The effort\n     * we do here is proportional to the number of times we entered this\n     * function and found that we are still over the limit. */\n    int effort = 100 * (timeout_counter+1);\n\n    /* We just remove one key after another by using a random walk. */\n    raxIterator ri;\n    raxStart(&ri,TrackingTable);\n    while(effort > 0) {\n        effort--;\n        raxSeek(&ri,\"^\",NULL,0);\n        raxRandomWalk(&ri,0);\n        if (raxEOF(&ri)) break;\n        trackingInvalidateKeyRaw(NULL,(char*)ri.key,ri.key_len,0);\n        if (raxSize(TrackingTable) <= max_keys) {\n            timeout_counter = 0;\n            raxStop(&ri);\n            return; /* Return ASAP: we are again under the limit. */\n        }\n    }\n\n    /* If we reach this point, we were not able to go under the configured\n     * limit using the maximum effort we had for this run. */\n    raxStop(&ri);\n    timeout_counter++;\n}\n\n/* Generate Redis protocol for an array containing all the key names\n * in the 'keys' radix tree. If the client is not NULL, the list will not\n * include keys that were modified the last time by this client, in order\n * to implement the NOLOOP option.\n *\n * If the resultin array would be empty, NULL is returned instead. */\nsds trackingBuildBroadcastReply(client *c, rax *keys) {\n    raxIterator ri;\n    uint64_t count;\n\n    if (c == NULL) {\n        count = raxSize(keys);\n    } else {\n        count = 0;\n        raxStart(&ri,keys);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            if (ri.data != c) count++;\n        }\n        raxStop(&ri);\n\n        if (count == 0) return NULL;\n    }\n\n    /* Create the array reply with the list of keys once, then send\n    * it to all the clients subscribed to this prefix. */\n    char buf[32];\n    size_t len = ll2string(buf,sizeof(buf),count);\n    sds proto = sdsempty();\n    proto = sdsMakeRoomFor(proto,count*15);\n    proto = sdscatlen(proto,\"*\",1);\n    proto = sdscatlen(proto,buf,len);\n    proto = sdscatlen(proto,\"\\r\\n\",2);\n    raxStart(&ri,keys);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        if (c && ri.data == c) continue;\n        len = ll2string(buf,sizeof(buf),ri.key_len);\n        proto = sdscatlen(proto,\"$\",1);\n        proto = sdscatlen(proto,buf,len);\n        proto = sdscatlen(proto,\"\\r\\n\",2);\n        proto = sdscatlen(proto,ri.key,ri.key_len);\n        proto = sdscatlen(proto,\"\\r\\n\",2);\n    }\n    raxStop(&ri);\n    return proto;\n}\n\n/* This function will run the prefixes of clients in BCAST mode and\n * keys that were modified about each prefix, and will send the\n * notifications to each client in each prefix. */\nvoid trackingBroadcastInvalidationMessages(void) {\n    raxIterator ri, ri2;\n\n    /* Return ASAP if there is nothing to do here. */\n    if (TrackingTable == NULL || !server.tracking_clients) return;\n\n    raxStart(&ri,PrefixTable);\n    raxSeek(&ri,\"^\",NULL,0);\n\n    /* For each prefix... */\n    while(raxNext(&ri)) {\n        bcastState *bs = ri.data;\n\n        if (raxSize(bs->keys)) {\n            /* Generate the common protocol for all the clients that are\n             * not using the NOLOOP option. */\n            sds proto = trackingBuildBroadcastReply(NULL,bs->keys);\n\n            /* Send this array of keys to every client in the list. */\n            raxStart(&ri2,bs->clients);\n            raxSeek(&ri2,\"^\",NULL,0);\n            while(raxNext(&ri2)) {\n                client *c;\n                memcpy(&c,ri2.key,sizeof(c));\n                if (c->flags & CLIENT_TRACKING_NOLOOP) {\n                    /* This client may have certain keys excluded. */\n                    sds adhoc = trackingBuildBroadcastReply(c,bs->keys);\n                    if (adhoc) {\n                        sendTrackingMessage(c,adhoc,sdslen(adhoc),1);\n                        sdsfree(adhoc);\n                    }\n                } else {\n                    sendTrackingMessage(c,proto,sdslen(proto),1);\n                }\n            }\n            raxStop(&ri2);\n\n            /* Clean up: we can remove everything from this state, because we\n             * want to only track the new keys that will be accumulated starting\n             * from now. */\n            sdsfree(proto);\n        }\n        raxFree(bs->keys);\n        bs->keys = raxNew();\n    }\n    raxStop(&ri);\n}\n\n/* This is just used in order to access the amount of used slots in the\n * tracking table. */\nuint64_t trackingGetTotalItems(void) {\n    return TrackingTableTotalItems;\n}\n\nuint64_t trackingGetTotalKeys(void) {\n    if (TrackingTable == NULL) return 0;\n    return raxSize(TrackingTable);\n}\n\nuint64_t trackingGetTotalPrefixes(void) {\n    if (PrefixTable == NULL) return 0;\n    return raxSize(PrefixTable);\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <stdint.h>\n#include <errno.h>\n#include <time.h>\n\n#include \"util.h\"\n#include \"sha256.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 && stringLen) {\n        switch(pattern[0]) {\n        case '*':\n            while (patternLen && 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            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] == '\\\\' && patternLen >= 2) {\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 (patternLen >= 3 && pattern[1] == '-') {\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/* Fuzz stringmatchlen() trying to crash it with bad input. */\nint stringmatchlen_fuzz_test(void) {\n    char str[32];\n    char pat[32];\n    int cycles = 10000000;\n    int total_matches = 0;\n    while(cycles--) {\n        int strlen = rand() % sizeof(str);\n        int patlen = rand() % sizeof(pat);\n        for (int j = 0; j < strlen; j++) str[j] = rand() % 128;\n        for (int j = 0; j < patlen; j++) pat[j] = rand() % 128;\n        total_matches += stringmatchlen(pat, patlen, str, strlen, 0);\n    }\n    return total_matches;\n}\n\n/* Convert a string representing an amount of memory into the number of\n * bytes, so for instance memtoll(\"1Gb\") will return 1073741824 that is\n * (1024*1024*1024).\n *\n * On parsing error, if *err is not NULL, it's set to 1, otherwise it's\n * set to 0. On error the function return value is 0, regardless of the\n * fact 'err' is NULL or not. */\nlong long memtoll(const char *p, int *err) {\n    const char *u;\n    char buf[128];\n    long mul; /* unit multiplier */\n    long long val;\n    unsigned int digits;\n\n    if (err) *err = 0;\n\n    /* Search the first non digit character. */\n    u = p;\n    if (*u == '-') u++;\n    while(*u && isdigit(*u)) u++;\n    if (*u == '\\0' || !strcasecmp(u,\"b\")) {\n        mul = 1;\n    } else if (!strcasecmp(u,\"k\")) {\n        mul = 1000;\n    } else if (!strcasecmp(u,\"kb\")) {\n        mul = 1024;\n    } else if (!strcasecmp(u,\"m\")) {\n        mul = 1000*1000;\n    } else if (!strcasecmp(u,\"mb\")) {\n        mul = 1024*1024;\n    } else if (!strcasecmp(u,\"g\")) {\n        mul = 1000L*1000*1000;\n    } else if (!strcasecmp(u,\"gb\")) {\n        mul = 1024L*1024*1024;\n    } else {\n        if (err) *err = 1;\n        return 0;\n    }\n\n    /* Copy the digits into a buffer, we'll use strtoll() to convert\n     * the digit (without the unit) into a number. */\n    digits = u-p;\n    if (digits >= sizeof(buf)) {\n        if (err) *err = 1;\n        return 0;\n    }\n    memcpy(buf,p,digits);\n    buf[digits] = '\\0';\n\n    char *endptr;\n    errno = 0;\n    val = strtoll(buf,&endptr,10);\n    if ((val == 0 && errno == EINVAL) || *endptr != '\\0') {\n        if (err) *err = 1;\n        return 0;\n    }\n    return val*mul;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * See ll2string() for more information. */\nuint32_t digits10(uint64_t v) {\n    if (v < 10) return 1;\n    if (v < 100) return 2;\n    if (v < 1000) return 3;\n    if (v < 1000000000000UL) {\n        if (v < 100000000UL) {\n            if (v < 1000000) {\n                if (v < 10000) return 4;\n                return 5 + (v >= 100000);\n            }\n            return 7 + (v >= 10000000UL);\n        }\n        if (v < 10000000000UL) {\n            return 9 + (v >= 1000000000UL);\n        }\n        return 11 + (v >= 100000000000UL);\n    }\n    return 12 + digits10(v / 1000000000000UL);\n}\n\n/* Like digits10() but for signed values. */\nuint32_t sdigits10(int64_t v) {\n    if (v < 0) {\n        /* Abs value of LLONG_MIN requires special handling. */\n        uint64_t uv = (v != LLONG_MIN) ?\n                      (uint64_t)-v : ((uint64_t) LLONG_MAX)+1;\n        return digits10(uv)+1; /* +1 for the minus. */\n    } else {\n        return digits10(v);\n    }\n}\n\n/* Convert a long long into a string. Returns the number of\n * characters needed to represent the number.\n * If the buffer is not big enough to store the string, 0 is returned.\n *\n * Based on the following article (that apparently does not provide a\n * novel approach but only publicizes an already used technique):\n *\n * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920\n *\n * Modified in order to handle signed integers since the original code was\n * designed for unsigned integers. */\nint ll2string(char *dst, size_t dstlen, long long svalue) {\n    static const char digits[201] =\n        \"0001020304050607080910111213141516171819\"\n        \"2021222324252627282930313233343536373839\"\n        \"4041424344454647484950515253545556575859\"\n        \"6061626364656667686970717273747576777879\"\n        \"8081828384858687888990919293949596979899\";\n    int negative;\n    unsigned long long value;\n\n    /* The main loop works with 64bit unsigned integers for simplicity, so\n     * we convert the number here and remember if it is negative. */\n    if (svalue < 0) {\n        if (svalue != LLONG_MIN) {\n            value = -svalue;\n        } else {\n            value = ((unsigned long long) LLONG_MAX)+1;\n        }\n        negative = 1;\n    } else {\n        value = svalue;\n        negative = 0;\n    }\n\n    /* Check length. */\n    uint32_t const length = digits10(value)+negative;\n    if (length >= dstlen) return 0;\n\n    /* Null term. */\n    uint32_t next = length;\n    dst[next] = '\\0';\n    next--;\n    while (value >= 100) {\n        int const i = (value % 100) * 2;\n        value /= 100;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n        next -= 2;\n    }\n\n    /* Handle last 1-2 digits. */\n    if (value < 10) {\n        dst[next] = '0' + (uint32_t) value;\n    } else {\n        int i = (uint32_t) value * 2;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n    }\n\n    /* Add sign. */\n    if (negative) dst[0] = '-';\n    return length;\n}\n\n/* Convert a string into a long long. Returns 1 if the string could be parsed\n * into a (non-overflowing) long long, 0 otherwise. The value will be set to\n * the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a long long: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. */\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    /* A zero length string is not a valid number. */\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    /* Handle negative numbers: just set a flag and continue like if it\n     * was a positive number. Later convert into negative. */\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 {\n        return 0;\n    }\n\n    /* Parse all the other digits, checking for overflow at every step. */\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    /* Convert to negative if needed, and do the final overflow check when\n     * converting from unsigned long long to long long. */\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/* Helper function to convert a string to an unsigned long long value.\n * The function attempts to use the faster string2ll() function inside\n * Redis: if it fails, strtoull() is used instead. The function returns\n * 1 if the conversion happened successfully or 0 if the number is\n * invalid or out of range. */\nint string2ull(const char *s, unsigned long long *value) {\n    long long ll;\n    if (string2ll(s,strlen(s),&ll)) {\n        if (ll < 0) return 0; /* Negative values are out of range. */\n        *value = ll;\n        return 1;\n    }\n    errno = 0;\n    char *endptr = NULL;\n    *value = strtoull(s,&endptr,10);\n    if (errno == EINVAL || errno == ERANGE || !(*s != '\\0' && *endptr == '\\0'))\n        return 0; /* strtoull() failed. */\n    return 1; /* Conversion done! */\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 string into a double. Returns 1 if the string could be parsed\n * into a (non-overflowing) double, 0 otherwise. The value will be set to\n * the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a double: no spaces or other characters before or after the string\n * representing the number are accepted. */\nint string2ld(const char *s, size_t slen, long double *dp) {\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    long double value;\n    char *eptr;\n\n    if (slen == 0 || slen >= sizeof(buf)) return 0;\n    memcpy(buf,s,slen);\n    buf[slen] = '\\0';\n\n    errno = 0;\n    value = strtold(buf, &eptr);\n    if (isspace(buf[0]) || eptr[0] != '\\0' ||\n        (size_t)(eptr-buf) != slen ||\n        (errno == ERANGE &&\n            (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||\n        errno == EINVAL ||\n        isnan(value))\n        return 0;\n\n    if (dp) *dp = value;\n    return 1;\n}\n\n/* Convert a string into a double. Returns 1 if the string could be parsed\n * into a (non-overflowing) double, 0 otherwise. The value will be set to\n * the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a double: no spaces or other characters before or after the string\n * representing the number are accepted. */\nint string2d(const char *s, size_t slen, double *dp) {\n    errno = 0;\n    char *eptr;\n    *dp = strtod(s, &eptr);\n    if (slen == 0 ||\n        isspace(((const char*)s)[0]) ||\n        (size_t)(eptr-(char*)s) != slen ||\n        (errno == ERANGE &&\n            (*dp == HUGE_VAL || *dp == -HUGE_VAL || *dp == 0)) ||\n        isnan(*dp))\n        return 0;\n    return 1;\n}\n\n/* Convert a double to a string representation. Returns the number of bytes\n * required. The representation should always be parsable by strtod(3).\n * This function does not support human-friendly formatting like ld2string\n * does. It is intended mainly to be used inside t_zset.c when writing scores\n * into a ziplist representing a sorted set. */\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/* Create a string object from a long double.\n * If mode is humanfriendly it does not use exponential format and trims trailing\n * zeroes at the end (may result in loss of precision).\n * If mode is default exp format is used and the output of snprintf()\n * is not modified (may result in loss of precision).\n * If mode is hex hexadecimal format is used (no loss of precision)\n *\n * The function returns the length of the string or zero if there was not\n * enough buffer room to store it. */\nint ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {\n    size_t l = 0;\n\n    if (isinf(value)) {\n        /* Libc in odd systems (Hi Solaris!) will format infinite in a\n         * different way, so better to handle it in an explicit way. */\n        if (len < 5) return 0; /* No room. 5 is \"-inf\\0\" */\n        if (value > 0) {\n            memcpy(buf,\"inf\",3);\n            l = 3;\n        } else {\n            memcpy(buf,\"-inf\",4);\n            l = 4;\n        }\n    } else {\n        switch (mode) {\n        case LD_STR_AUTO:\n            l = snprintf(buf,len,\"%.17Lg\",value);\n            if (l+1 > len) return 0; /* No room. */\n            break;\n        case LD_STR_HEX:\n            l = snprintf(buf,len,\"%La\",value);\n            if (l+1 > len) return 0; /* No room. */\n            break;\n        case LD_STR_HUMAN:\n            /* We use 17 digits precision since with 128 bit floats that precision\n             * after rounding is able to represent most small decimal numbers in a\n             * way that is \"non surprising\" for the user (that is, most small\n             * decimal numbers will be represented in a way that when converted\n             * back into a string are exactly the same as what the user typed.) */\n            l = snprintf(buf,len,\"%.17Lf\",value);\n            if (l+1 > len) return 0; /* No room. */\n            /* Now remove trailing zeroes after the '.' */\n            if (strchr(buf,'.') != NULL) {\n                char *p = buf+l-1;\n                while(*p == '0') {\n                    p--;\n                    l--;\n                }\n                if (*p == '.') l--;\n            }\n            if (l == 2 && buf[0] == '-' && buf[1] == '0') {\n                buf[0] = '0';\n                l = 1;\n            }\n            break;\n        default: return 0; /* Invalid mode. */\n        }\n    }\n    buf[l] = '\\0';\n    return l;\n}\n\n/* Get random bytes, attempts to get an initial seed from /dev/urandom and\n * the uses a one way hash function in counter mode to generate a random\n * stream. However if /dev/urandom is not available, a weaker seed is used.\n *\n * This function is not thread safe, since the state is global. */\nvoid getRandomBytes(unsigned char *p, size_t len) {\n    /* Global state. */\n    static int seed_initialized = 0;\n    static unsigned char seed[64]; /* 512 bit internal block size. */\n    static uint64_t counter = 0; /* The counter we hash with the seed. */\n\n    if (!seed_initialized) {\n        /* Initialize a seed and use SHA1 in counter mode, where we hash\n         * the same seed with a progressive counter. For the goals of this\n         * function we just need non-colliding strings, there are no\n         * cryptographic security needs. */\n        FILE *fp = fopen(\"/dev/urandom\",\"r\");\n        if (fp == NULL || fread(seed,sizeof(seed),1,fp) != 1) {\n            /* Revert to a weaker seed, and in this case reseed again\n             * at every call.*/\n            for (unsigned int j = 0; j < sizeof(seed); j++) {\n                struct timeval tv;\n                gettimeofday(&tv,NULL);\n                pid_t pid = getpid();\n                seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp;\n            }\n        } else {\n            seed_initialized = 1;\n        }\n        if (fp) fclose(fp);\n    }\n\n    while(len) {\n        /* This implements SHA256-HMAC. */\n        unsigned char digest[SHA256_BLOCK_SIZE];\n        unsigned char kxor[64];\n        unsigned int copylen =\n            len > SHA256_BLOCK_SIZE ? SHA256_BLOCK_SIZE : len;\n\n        /* IKEY: key xored with 0x36. */\n        memcpy(kxor,seed,sizeof(kxor));\n        for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x36;\n\n        /* Obtain HASH(IKEY||MESSAGE). */\n        SHA256_CTX ctx;\n        sha256_init(&ctx);\n        sha256_update(&ctx,kxor,sizeof(kxor));\n        sha256_update(&ctx,(unsigned char*)&counter,sizeof(counter));\n        sha256_final(&ctx,digest);\n\n        /* OKEY: key xored with 0x5c. */\n        memcpy(kxor,seed,sizeof(kxor));\n        for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x5C;\n\n        /* Obtain HASH(OKEY || HASH(IKEY||MESSAGE)). */\n        sha256_init(&ctx);\n        sha256_update(&ctx,kxor,sizeof(kxor));\n        sha256_update(&ctx,digest,SHA256_BLOCK_SIZE);\n        sha256_final(&ctx,digest);\n\n        /* Increment the counter for the next iteration. */\n        counter++;\n\n        memcpy(p,digest,copylen);\n        len -= copylen;\n        p += copylen;\n    }\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, size_t len) {\n    char *charset = \"0123456789abcdef\";\n    size_t j;\n\n    getRandomBytes((unsigned char*)p,len);\n    for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F];\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 \"../\" appearing 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/*\n * Gets the proper timezone in a more portable fashion\n * i.e timezone variables are linux specific.\n */\n\nunsigned long getTimeZone(void) {\n#ifdef __linux__\n    return timezone;\n#else\n    struct timeval tv;\n    struct timezone tz;\n\n    gettimeofday(&tv, &tz);\n\n    return tz.tz_minuteswest * 60UL;\n#endif\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 REDIS_TEST\n#include <assert.h>\n\nstatic void 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\nstatic void 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\nstatic void test_ll2string(void) {\n    char buf[32];\n    long long v;\n    int sz;\n\n    v = 0;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 1);\n    assert(!strcmp(buf, \"0\"));\n\n    v = -1;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 2);\n    assert(!strcmp(buf, \"-1\"));\n\n    v = 99;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 2);\n    assert(!strcmp(buf, \"99\"));\n\n    v = -99;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 3);\n    assert(!strcmp(buf, \"-99\"));\n\n    v = -2147483648;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 11);\n    assert(!strcmp(buf, \"-2147483648\"));\n\n    v = LLONG_MIN;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 20);\n    assert(!strcmp(buf, \"-9223372036854775808\"));\n\n    v = LLONG_MAX;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 19);\n    assert(!strcmp(buf, \"9223372036854775807\"));\n}\n\n#define UNUSED(x) (void)(x)\nint utilTest(int argc, char **argv) {\n    UNUSED(argc);\n    UNUSED(argv);\n\n    test_string2ll();\n    test_string2l();\n    test_ll2string();\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 <stdint.h>\n#include \"sds.h\"\n\n/* The maximum number of characters needed to represent a long double\n * as a string (long double has a huge range).\n * This should be the size of the buffer given to ld2string */\n#define MAX_LONG_DOUBLE_CHARS 5*1024\n\n/* long double to string convertion options */\ntypedef enum {\n    LD_STR_AUTO,     /* %.17Lg */\n    LD_STR_HUMAN,    /* %.17Lf + Trimming of trailing zeros */\n    LD_STR_HEX       /* %La */\n} ld2string_mode;\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);\nint stringmatchlen_fuzz_test(void);\nlong long memtoll(const char *p, int *err);\nuint32_t digits10(uint64_t v);\nuint32_t sdigits10(int64_t v);\nint ll2string(char *s, size_t len, long long value);\nint string2ll(const char *s, size_t slen, long long *value);\nint string2ull(const char *s, unsigned long long *value);\nint string2l(const char *s, size_t slen, long *value);\nint string2ld(const char *s, size_t slen, long double *dp);\nint string2d(const char *s, size_t slen, double *dp);\nint d2string(char *buf, size_t len, double value);\nint ld2string(char *buf, size_t len, long double value, ld2string_mode mode);\nsds getAbsolutePath(char *filename);\nunsigned long getTimeZone(void);\nint pathIsBaseName(char *path);\n\n#ifdef REDIS_TEST\nint utilTest(int argc, char **argv);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/src/version.h",
    "content": "#define REDIS_VERSION \"6.0.3\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/ziplist.c",
    "content": "/* The ziplist is a specially encoded dually linked list that is designed\n * to be very memory efficient. It stores both strings and integer values,\n * where integers are encoded as actual integers instead of a series of\n * characters. It allows push and pop operations on either side of the list\n * in O(1) time. However, because every operation requires a reallocation of\n * the memory used by the ziplist, the actual complexity is related to the\n * amount of memory used by the ziplist.\n *\n * ----------------------------------------------------------------------------\n *\n * ZIPLIST OVERALL LAYOUT\n * ======================\n *\n * The general layout of the ziplist is as follows:\n *\n * <zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>\n *\n * NOTE: all fields are stored in little endian, if not specified otherwise.\n *\n * <uint32_t zlbytes> is an unsigned integer to hold the number of bytes that\n * the ziplist occupies, including the four bytes of the zlbytes field itself.\n * This value needs to be stored to be able to resize the entire structure\n * without the need to traverse it first.\n *\n * <uint32_t zltail> is the offset to the last entry in the list. This allows\n * a pop operation on the far side of the list without the need for full\n * traversal.\n *\n * <uint16_t zllen> is the number of entries. When there are more than\n * 2^16-2 entries, this value is set to 2^16-1 and we need to traverse the\n * entire list to know how many items it holds.\n *\n * <uint8_t zlend> is a special entry representing the end of the ziplist.\n * Is encoded as a single byte equal to 255. No other normal entry starts\n * with a byte set to the value of 255.\n *\n * ZIPLIST ENTRIES\n * ===============\n *\n * Every entry in the ziplist is prefixed by metadata 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 entry encoding is\n * provided. It represents the entry type, integer or string, and in the case\n * of strings it also represents the length of the string payload.\n * So a complete entry is stored like this:\n *\n * <prevlen> <encoding> <entry-data>\n *\n * Sometimes the encoding represents the entry itself, like for small integers\n * as we'll see later. In such a case the <entry-data> part is missing, and we\n * could have just:\n *\n * <prevlen> <encoding>\n *\n * The length of the previous entry, <prevlen>, is encoded in the following way:\n * If this length is smaller than 254 bytes, it will only consume a single\n * byte representing the length as an unsinged 8 bit integer. When the length\n * is greater than or equal to 254, it will consume 5 bytes. The first byte is\n * set to 254 (FE) to indicate a larger value is following. The remaining 4\n * bytes take the length of the previous entry as value.\n *\n * So practically an entry is encoded in the following way:\n *\n * <prevlen from 0 to 253> <encoding> <entry>\n *\n * Or alternatively if the previous entry length is greater than 253 bytes\n * the following encoding is used:\n *\n * 0xFE <4 bytes unsigned little endian prevlen> <encoding> <entry>\n *\n * The encoding field of the entry depends on the content of the\n * entry. When the entry is a string, the first 2 bits of the encoding first\n * byte will hold the type of encoding used to store the length of the string,\n * followed by the actual length of the string. When the entry is an integer\n * the first 2 bits are both set to 1. The following 2 bits are used to specify\n * what kind of integer will be stored after this header. An overview of the\n * different types and encodings is as follows. The first byte is always enough\n * to determine the kind of entry.\n *\n * |00pppppp| - 1 byte\n *      String value with length less than or equal to 63 bytes (6 bits).\n *      \"pppppp\" represents the unsigned 6 bit length.\n * |01pppppp|qqqqqqqq| - 2 bytes\n *      String value with length less than or equal to 16383 bytes (14 bits).\n *      IMPORTANT: The 14 bit number is stored in big endian.\n * |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes\n *      String value with length greater than or equal to 16384 bytes.\n *      Only the 4 bytes following the first byte represents the length\n *      up to 32^2-1. The 6 lower bits of the first byte are not used and\n *      are set to zero.\n *      IMPORTANT: The 32 bit number is stored in big endian.\n * |11000000| - 3 bytes\n *      Integer encoded as int16_t (2 bytes).\n * |11010000| - 5 bytes\n *      Integer encoded as int32_t (4 bytes).\n * |11100000| - 9 bytes\n *      Integer encoded as int64_t (8 bytes).\n * |11110000| - 4 bytes\n *      Integer encoded as 24 bit signed (3 bytes).\n * |11111110| - 2 bytes\n *      Integer encoded as 8 bit signed (1 byte).\n * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer.\n *      Unsigned integer from 0 to 12. The encoded value is actually from\n *      1 to 13 because 0000 and 1111 can not be used, so 1 should be\n *      subtracted from the encoded 4 bit value to obtain the right value.\n * |11111111| - End of ziplist special entry.\n *\n * Like for the ziplist header, all the integers are represented in little\n * endian byte order, even when this code is compiled in big endian systems.\n *\n * EXAMPLES OF ACTUAL ZIPLISTS\n * ===========================\n *\n * The following is a ziplist containing the two elements representing\n * the strings \"2\" and \"5\". It is composed of 15 bytes, that we visually\n * split into sections:\n *\n *  [0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]\n *        |             |          |       |       |     |\n *     zlbytes        zltail    entries   \"2\"     \"5\"   end\n *\n * The first 4 bytes represent the number 15, that is the number of bytes\n * the whole ziplist is composed of. The second 4 bytes are the offset\n * at which the last ziplist entry is found, that is 12, in fact the\n * last entry, that is \"5\", is at offset 12 inside the ziplist.\n * The next 16 bit integer represents the number of elements inside the\n * ziplist, its value is 2 since there are just two elements inside.\n * Finally \"00 f3\" is the first entry representing the number 2. It is\n * composed of the previous entry length, which is zero because this is\n * our first entry, and the byte F3 which corresponds to the encoding\n * |1111xxxx| with xxxx between 0001 and 1101. We need to remove the \"F\"\n * higher order bits 1111, and subtract 1 from the \"3\", so the entry value\n * is \"2\". The next entry has a prevlen of 02, since the first entry is\n * composed of exactly two bytes. The entry itself, F6, is encoded exactly\n * like the first entry, and 6-1 = 5, so the value of the entry is 5.\n * Finally the special entry FF signals the end of the ziplist.\n *\n * Adding another element to the above string with the value \"Hello World\"\n * allows us to show how the ziplist encodes small strings. We'll just show\n * the hex dump of the entry itself. Imagine the bytes as following the\n * entry that stores \"5\" in the ziplist above:\n *\n * [02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]\n *\n * The first byte, 02, is the length of the previous entry. The next\n * byte represents the encoding in the pattern |00pppppp| that means\n * that the entry is a string of length <pppppp>, so 0B means that\n * an 11 bytes string follows. From the third byte (48) to the last (64)\n * there are just the ASCII characters for \"Hello World\".\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2017, 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#define ZIP_END 255         /* Special \"end of ziplist\" entry. */\n#define ZIP_BIG_PREVLEN 254 /* Max number of bytes of the previous entry, for\n                               the \"prevlen\" field prefixing each entry, to be\n                               represented with just a single byte. Otherwise\n                               it is represented as FF AA BB CC DD, where\n                               AA BB CC DD are a 4 bytes unsigned integer\n                               representing the previous entry len. */\n\n/* Different encoding/length possibilities */\n#define ZIP_STR_MASK 0xc0\n#define ZIP_INT_MASK 0x30\n#define ZIP_STR_06B (0 << 6)\n#define ZIP_STR_14B (1 << 6)\n#define ZIP_STR_32B (2 << 6)\n#define ZIP_INT_16B (0xc0 | 0<<4)\n#define ZIP_INT_32B (0xc0 | 1<<4)\n#define ZIP_INT_64B (0xc0 | 2<<4)\n#define ZIP_INT_24B (0xc0 | 3<<4)\n#define ZIP_INT_8B 0xfe\n\n/* 4 bit integer immediate encoding |1111xxxx| with xxxx between\n * 0001 and 1101. */\n#define ZIP_INT_IMM_MASK 0x0f   /* Mask to extract the 4 bits value. To add\n                                   one is needed to reconstruct the value. */\n#define ZIP_INT_IMM_MIN 0xf1    /* 11110001 */\n#define ZIP_INT_IMM_MAX 0xfd    /* 11111101 */\n\n#define INT24_MAX 0x7fffff\n#define INT24_MIN (-INT24_MAX - 1)\n\n/* Macro to determine if the entry is a string. String entries never start\n * with \"11\" as most significant bits of the first byte. */\n#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)\n\n/* Utility macros.*/\n\n/* Return total bytes a ziplist is composed of. */\n#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))\n\n/* Return the offset of the last item inside the ziplist. */\n#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))\n\n/* Return the length of a ziplist, or UINT16_MAX if the length cannot be\n * determined without scanning the whole ziplist. */\n#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))\n\n/* The size of a ziplist header: two 32 bit integers for the total\n * bytes count and last item offset. One 16 bit integer for the number\n * of items field. */\n#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))\n\n/* Size of the \"end of ziplist\" entry. Just one byte. */\n#define ZIPLIST_END_SIZE        (sizeof(uint8_t))\n\n/* Return the pointer to the first entry of a ziplist. */\n#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)\n\n/* Return the pointer to the last entry of a ziplist, using the\n * last entry offset inside the ziplist header. */\n#define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))\n\n/* Return the pointer to the last byte of a ziplist, which is, the\n * end of ziplist FF entry. */\n#define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)\n\n/* Increment the number of items field in the ziplist header. Note that this\n * macro should never overflow the unsigned 16 bit integer, since entries are\n * always pushed one at a time. When UINT16_MAX is reached we want the count\n * to stay there to signal that a full scan is needed to get the number of\n * items inside the ziplist. */\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/* We use this function to receive information about a ziplist entry.\n * Note that this is not how the data is actually encoded, is just what we\n * get filled by a function in order to operate more easily. */\ntypedef struct zlentry {\n    unsigned int prevrawlensize; /* Bytes used to encode the previous entry len*/\n    unsigned int prevrawlen;     /* Previous entry len. */\n    unsigned int lensize;        /* Bytes used to encode this entry type/len.\n                                    For example strings have a 1, 2 or 5 bytes\n                                    header. Integers always use a single byte.*/\n    unsigned int len;            /* Bytes used to represent the actual entry.\n                                    For strings this is just the string length\n                                    while for integers it is 1, 2, 3, 4, 8 or\n                                    0 (for 4 bit immediate) depending on the\n                                    number range. */\n    unsigned int headersize;     /* prevrawlensize + lensize. */\n    unsigned char encoding;      /* Set to ZIP_STR_* or ZIP_INT_* depending on\n                                    the entry encoding. However for 4 bits\n                                    immediate integers this can assume a range\n                                    of values and must be range-checked. */\n    unsigned char *p;            /* Pointer to the very start of the entry, that\n                                    is, this points to prev-entry-len field. */\n} zlentry;\n\n#define ZIPLIST_ENTRY_ZERO(zle) { \\\n    (zle)->prevrawlensize = (zle)->prevrawlen = 0; \\\n    (zle)->lensize = (zle)->len = (zle)->headersize = 0; \\\n    (zle)->encoding = 0; \\\n    (zle)->p = NULL; \\\n}\n\n/* Extract the encoding from the byte pointed by 'ptr' and set it into\n * 'encoding' field of the zlentry structure. */\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'. */\nunsigned int zipIntSize(unsigned char encoding) {\n    switch(encoding) {\n    case ZIP_INT_8B:  return 1;\n    case ZIP_INT_16B: return 2;\n    case ZIP_INT_24B: return 3;\n    case ZIP_INT_32B: return 4;\n    case ZIP_INT_64B: return 8;\n    }\n    if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)\n        return 0; /* 4 bit immediate */\n    panic(\"Invalid integer encoding 0x%02X\", encoding);\n    return 0;\n}\n\n/* Write the encoidng header of the entry in 'p'. If p is NULL it just returns\n * the amount of bytes required to encode such a length. Arguments:\n *\n * 'encoding' is the encoding we are using for the entry. It could be\n * ZIP_INT_* or ZIP_STR_* or between ZIP_INT_IMM_MIN and ZIP_INT_IMM_MAX\n * for single-byte small immediate integers.\n *\n * 'rawlen' is only used for ZIP_STR_* encodings and is the length of the\n * srting that this entry represents.\n *\n * The function returns the number of bytes used by the encoding/length\n * header stored in 'p'. */\nunsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, unsigned int rawlen) {\n    unsigned char len = 1, buf[5];\n\n    if (ZIP_IS_STR(encoding)) {\n        /* Although encoding is given it may not be set for strings,\n         * so we determine it here using the raw length. */\n        if (rawlen <= 0x3f) {\n            if (!p) return len;\n            buf[0] = ZIP_STR_06B | rawlen;\n        } else if (rawlen <= 0x3fff) {\n            len += 1;\n            if (!p) return len;\n            buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);\n            buf[1] = rawlen & 0xff;\n        } else {\n            len += 4;\n            if (!p) return len;\n            buf[0] = ZIP_STR_32B;\n            buf[1] = (rawlen >> 24) & 0xff;\n            buf[2] = (rawlen >> 16) & 0xff;\n            buf[3] = (rawlen >> 8) & 0xff;\n            buf[4] = rawlen & 0xff;\n        }\n    } else {\n        /* Implies integer encoding, so length is always 1. */\n        if (!p) return len;\n        buf[0] = encoding;\n    }\n\n    /* Store this length at p. */\n    memcpy(p,buf,len);\n    return len;\n}\n\n/* Decode the entry encoding type and data length (string length for strings,\n * number of bytes used for the integer for integer entries) encoded in 'ptr'.\n * The 'encoding' variable will hold the entry encoding, the 'lensize'\n * variable will hold the number of bytes required to encode the entry\n * length, and the 'len' variable will hold the entry length. */\n#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do {                    \\\n    ZIP_ENTRY_ENCODING((ptr), (encoding));                                     \\\n    if ((encoding) < ZIP_STR_MASK) {                                           \\\n        if ((encoding) == ZIP_STR_06B) {                                       \\\n            (lensize) = 1;                                                     \\\n            (len) = (ptr)[0] & 0x3f;                                           \\\n        } else if ((encoding) == ZIP_STR_14B) {                                \\\n            (lensize) = 2;                                                     \\\n            (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1];                       \\\n        } else if ((encoding) == ZIP_STR_32B) {                                \\\n            (lensize) = 5;                                                     \\\n            (len) = ((ptr)[1] << 24) |                                         \\\n                    ((ptr)[2] << 16) |                                         \\\n                    ((ptr)[3] <<  8) |                                         \\\n                    ((ptr)[4]);                                                \\\n        } else {                                                               \\\n            panic(\"Invalid string encoding 0x%02X\", (encoding));               \\\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\". This only\n * uses the larger encoding (required in __ziplistCascadeUpdate). */\nint zipStorePrevEntryLengthLarge(unsigned char *p, unsigned int len) {\n    if (p != NULL) {\n        p[0] = ZIP_BIG_PREVLEN;\n        memcpy(p+1,&len,sizeof(len));\n        memrev32ifbe(p+1);\n    }\n    return 1+sizeof(len);\n}\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. */\nunsigned int zipStorePrevEntryLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return (len < ZIP_BIG_PREVLEN) ? 1 : sizeof(len)+1;\n    } else {\n        if (len < ZIP_BIG_PREVLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            return zipStorePrevEntryLengthLarge(p,len);\n        }\n    }\n}\n\n/* Return the number of bytes used to encode the length of the previous\n * entry. The length is returned by setting the var 'prevlensize'. */\n#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do {                          \\\n    if ((ptr)[0] < ZIP_BIG_PREVLEN) {                                          \\\n        (prevlensize) = 1;                                                     \\\n    } else {                                                                   \\\n        (prevlensize) = 5;                                                     \\\n    }                                                                          \\\n} while(0);\n\n/* Return the length of the previous element, and the number of bytes that\n * are used in order to encode the previous element length.\n * 'ptr' must point to the prevlen prefix of an entry (that encodes the\n * length of the previous entry in order to navigate the elements backward).\n * The length of the previous entry is stored in 'prevlen', the number of\n * bytes needed to encode the previous entry length are stored in\n * 'prevlensize'. */\n#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do {                     \\\n    ZIP_DECODE_PREVLENSIZE(ptr, prevlensize);                                  \\\n    if ((prevlensize) == 1) {                                                  \\\n        (prevlen) = (ptr)[0];                                                  \\\n    } else if ((prevlensize) == 5) {                                           \\\n        assert(sizeof((prevlen)) == 4);                                        \\\n        memcpy(&(prevlen), ((char*)(ptr)) + 1, 4);                             \\\n        memrev32ifbe(&prevlen);                                                \\\n    }                                                                          \\\n} while(0);\n\n/* Given a pointer 'p' to the prevlen info that prefixes an entry, this\n * function returns the difference in number of bytes needed to encode\n * the prevlen if the previous entry changes of size.\n *\n * So if A is the number of bytes used right now to encode the 'prevlen'\n * field.\n *\n * And B is the number of bytes that are needed in order to encode the\n * 'prevlen' if the previous element will be updated to one of size 'len'.\n *\n * Then the function returns B - A\n *\n * So the function returns a positive number if more space is needed,\n * a negative number if less space is needed, or zero if the same space\n * is needed. */\nint zipPrevLenByteDiff(unsigned char *p, unsigned int len) {\n    unsigned int prevlensize;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    return zipStorePrevEntryLength(NULL, len) - prevlensize;\n}\n\n/* Return the total number of bytes used by the entry pointed to by 'p'. */\nunsigned int zipRawEntryLength(unsigned char *p) {\n    unsigned int prevlensize, encoding, lensize, len;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n    return prevlensize + lensize + len;\n}\n\n/* Check if string pointed to by 'entry' can be encoded as an integer.\n * Stores the integer value in 'v' and its encoding in 'encoding'. */\nint zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {\n    long long value;\n\n    if (entrylen >= 32 || entrylen == 0) return 0;\n    if (string2ll((char*)entry,entrylen,&value)) {\n        /* Great, the string can be encoded. Check what's the smallest\n         * of our encoding types that can hold this value. */\n        if (value >= 0 && value <= 12) {\n            *encoding = ZIP_INT_IMM_MIN+value;\n        } else if (value >= INT8_MIN && value <= INT8_MAX) {\n            *encoding = ZIP_INT_8B;\n        } else if (value >= INT16_MIN && value <= INT16_MAX) {\n            *encoding = ZIP_INT_16B;\n        } else if (value >= INT24_MIN && value <= INT24_MAX) {\n            *encoding = ZIP_INT_24B;\n        } else if (value >= INT32_MIN && value <= INT32_MAX) {\n            *encoding = ZIP_INT_32B;\n        } else {\n            *encoding = ZIP_INT_64B;\n        }\n        *v = value;\n        return 1;\n    }\n    return 0;\n}\n\n/* Store integer 'value' at 'p', encoded as 'encoding' */\nvoid zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64;\n    if (encoding == ZIP_INT_8B) {\n        ((int8_t*)p)[0] = (int8_t)value;\n    } else if (encoding == ZIP_INT_16B) {\n        i16 = value;\n        memcpy(p,&i16,sizeof(i16));\n        memrev16ifbe(p);\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = value<<8;\n        memrev32ifbe(&i32);\n        memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));\n    } else if (encoding == ZIP_INT_32B) {\n        i32 = value;\n        memcpy(p,&i32,sizeof(i32));\n        memrev32ifbe(p);\n    } else if (encoding == ZIP_INT_64B) {\n        i64 = value;\n        memcpy(p,&i64,sizeof(i64));\n        memrev64ifbe(p);\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        /* Nothing to do, the value is stored in the encoding itself. */\n    } else {\n        assert(NULL);\n    }\n}\n\n/* Read integer encoded as 'encoding' from 'p' */\nint64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64, ret = 0;\n    if (encoding == ZIP_INT_8B) {\n        ret = ((int8_t*)p)[0];\n    } else if (encoding == ZIP_INT_16B) {\n        memcpy(&i16,p,sizeof(i16));\n        memrev16ifbe(&i16);\n        ret = i16;\n    } else if (encoding == ZIP_INT_32B) {\n        memcpy(&i32,p,sizeof(i32));\n        memrev32ifbe(&i32);\n        ret = i32;\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = 0;\n        memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t));\n        memrev32ifbe(&i32);\n        ret = i32>>8;\n    } else if (encoding == ZIP_INT_64B) {\n        memcpy(&i64,p,sizeof(i64));\n        memrev64ifbe(&i64);\n        ret = i64;\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        ret = (encoding & ZIP_INT_IMM_MASK)-1;\n    } else {\n        assert(NULL);\n    }\n    return ret;\n}\n\n/* Return a struct with all information about an entry. */\nvoid zipEntry(unsigned char *p, zlentry *e) {\n\n    ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);\n    ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);\n    e->headersize = e->prevrawlensize + e->lensize;\n    e->p = p;\n}\n\n/* Create a new empty ziplist. */\nunsigned char *ziplistNew(void) {\n    unsigned int bytes = ZIPLIST_HEADER_SIZE+ZIPLIST_END_SIZE;\n    unsigned char *zl = zmalloc(bytes);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);\n    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);\n    ZIPLIST_LENGTH(zl) = 0;\n    zl[bytes-1] = ZIP_END;\n    return zl;\n}\n\n/* Resize the ziplist. */\nunsigned char *ziplistResize(unsigned char *zl, unsigned int len) {\n    zl = zrealloc(zl,len);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(len);\n    zl[len-1] = ZIP_END;\n    return zl;\n}\n\n/* When an entry is inserted, we need to set the prevlen field of the next\n * entry to equal the length of the inserted entry. It can occur that this\n * length cannot be encoded in 1 byte and the next entry needs to be grow\n * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,\n * because this only happens when an entry is already being inserted (which\n * causes a realloc and memmove). However, encoding the prevlen may require\n * that this entry is grown as well. This effect may cascade throughout\n * the ziplist when there are consecutive entries with a size close to\n * ZIP_BIG_PREVLEN, so we need to check that the prevlen can be encoded in\n * every consecutive entry.\n *\n * Note that this effect can also happen in reverse, where the bytes required\n * to encode the prevlen field can shrink. This effect is deliberately ignored,\n * because it can cause a \"flapping\" effect where a chain prevlen fields is\n * first grown and then shrunk again after consecutive inserts. Rather, the\n * field is allowed to stay larger than necessary, because a large prevlen\n * field implies the ziplist is holding large entries anyway.\n *\n * The pointer \"p\" points to the first entry that does NOT need to be\n * updated, i.e. consecutive fields MAY need an update. */\nunsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;\n    size_t offset, noffset, extra;\n    unsigned char *np;\n    zlentry cur, next;\n\n    while (p[0] != ZIP_END) {\n        zipEntry(p, &cur);\n        rawlen = cur.headersize + cur.len;\n        rawlensize = zipStorePrevEntryLength(NULL,rawlen);\n\n        /* Abort if there is no next entry. */\n        if (p[rawlen] == ZIP_END) break;\n        zipEntry(p+rawlen, &next);\n\n        /* Abort when \"prevlen\" has not changed. */\n        if (next.prevrawlen == rawlen) break;\n\n        if (next.prevrawlensize < rawlensize) {\n            /* The \"prevlen\" field of \"next\" needs more bytes to hold\n             * the raw length of \"cur\". */\n            offset = p-zl;\n            extra = rawlensize-next.prevrawlensize;\n            zl = ziplistResize(zl,curlen+extra);\n            p = zl+offset;\n\n            /* Current pointer and offset for next element. */\n            np = p+rawlen;\n            noffset = np-zl;\n\n            /* Update tail offset when next element is not the tail element. */\n            if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);\n            }\n\n            /* Move the tail to the back. */\n            memmove(np+rawlensize,\n                np+next.prevrawlensize,\n                curlen-noffset-next.prevrawlensize-1);\n            zipStorePrevEntryLength(np,rawlen);\n\n            /* Advance the cursor */\n            p += rawlen;\n            curlen += extra;\n        } else {\n            if (next.prevrawlensize > rawlensize) {\n                /* This would result in shrinking, which we want to avoid.\n                 * So, set \"rawlen\" in the available bytes. */\n                zipStorePrevEntryLengthLarge(p+rawlen,rawlen);\n            } else {\n                zipStorePrevEntryLength(p+rawlen,rawlen);\n            }\n\n            /* Stop here, as the raw length of \"next\" has not changed. */\n            break;\n        }\n    }\n    return zl;\n}\n\n/* Delete \"num\" entries, starting at \"p\". Returns pointer to the ziplist. */\nunsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {\n    unsigned int i, totlen, deleted = 0;\n    size_t offset;\n    int nextdiff = 0;\n    zlentry first, tail;\n\n    zipEntry(p, &first);\n    for (i = 0; p[0] != ZIP_END && i < num; i++) {\n        p += zipRawEntryLength(p);\n        deleted++;\n    }\n\n    totlen = p-first.p; /* Bytes taken by the element(s) to delete. */\n    if (totlen > 0) {\n        if (p[0] != ZIP_END) {\n            /* Storing `prevrawlen` in this entry may increase or decrease the\n             * number of bytes required compare to the current `prevrawlen`.\n             * There always is room to store this, because it was previously\n             * stored by an entry that is now being deleted. */\n            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);\n\n            /* Note that there is always space when p jumps backward: if\n             * the new previous entry is large, one of the deleted elements\n             * had a 5 bytes prevlen header, so there is for sure at least\n             * 5 bytes free and we need just 4. */\n            p -= nextdiff;\n            zipStorePrevEntryLength(p,first.prevrawlen);\n\n            /* Update offset for tail */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);\n\n            /* When the tail contains more than one entry, we need to take\n             * \"nextdiff\" in account as well. Otherwise, a change in the\n             * size of prevlen doesn't have an effect on the *tail* offset. */\n            zipEntry(p, &tail);\n            if (p[tail.headersize+tail.len] != ZIP_END) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                   intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n            }\n\n            /* Move tail to the front of the ziplist */\n            memmove(first.p,p,\n                intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);\n        } else {\n            /* The entire tail was deleted. No need to move memory. */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe((first.p-zl)-first.prevrawlen);\n        }\n\n        /* Resize and update length */\n        offset = first.p-zl;\n        zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);\n        ZIPLIST_INCR_LENGTH(zl,-deleted);\n        p = zl+offset;\n\n        /* When nextdiff != 0, the raw length of the next entry has changed, so\n         * we need to cascade the update throughout the ziplist */\n        if (nextdiff != 0)\n            zl = __ziplistCascadeUpdate(zl,p);\n    }\n    return zl;\n}\n\n/* Insert item at \"p\". */\nunsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;\n    unsigned int prevlensize, prevlen = 0;\n    size_t offset;\n    int nextdiff = 0;\n    unsigned char encoding = 0;\n    long long value = 123456789; /* initialized to avoid warning. Using a value\n                                    that is easy to see if for some reason\n                                    we use it uninitialized. */\n    zlentry tail;\n\n    /* Find out prevlen for the entry that is inserted. */\n    if (p[0] != ZIP_END) {\n        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n    } else {\n        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);\n        if (ptail[0] != ZIP_END) {\n            prevlen = zipRawEntryLength(ptail);\n        }\n    }\n\n    /* See if the entry can be encoded */\n    if (zipTryEncoding(s,slen,&value,&encoding)) {\n        /* 'encoding' is set to the appropriate integer encoding */\n        reqlen = zipIntSize(encoding);\n    } else {\n        /* 'encoding' is untouched, however zipStoreEntryEncoding will use the\n         * string length to figure out how to encode it. */\n        reqlen = slen;\n    }\n    /* We need space for both the length of the previous entry and\n     * the length of the payload. */\n    reqlen += zipStorePrevEntryLength(NULL,prevlen);\n    reqlen += zipStoreEntryEncoding(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    int forcelarge = 0;\n    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;\n    if (nextdiff == -4 && reqlen < 4) {\n        nextdiff = 0;\n        forcelarge = 1;\n    }\n\n    /* Store offset because a realloc may change the address of zl. */\n    offset = p-zl;\n    zl = ziplistResize(zl,curlen+reqlen+nextdiff);\n    p = zl+offset;\n\n    /* Apply memory move when necessary and update tail offset. */\n    if (p[0] != ZIP_END) {\n        /* Subtract one because of the ZIP_END bytes */\n        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);\n\n        /* Encode this entry's raw length in the next entry. */\n        if (forcelarge)\n            zipStorePrevEntryLengthLarge(p+reqlen,reqlen);\n        else\n            zipStorePrevEntryLength(p+reqlen,reqlen);\n\n        /* Update offset for tail */\n        ZIPLIST_TAIL_OFFSET(zl) =\n            intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);\n\n        /* When the tail contains more than one entry, we need to take\n         * \"nextdiff\" in account as well. Otherwise, a change in the\n         * size of prevlen doesn't have an effect on the *tail* offset. */\n        zipEntry(p+reqlen, &tail);\n        if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n        }\n    } else {\n        /* This element will be the new tail. */\n        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);\n    }\n\n    /* When nextdiff != 0, the raw length of the next entry has changed, so\n     * we need to cascade the update throughout the ziplist */\n    if (nextdiff != 0) {\n        offset = p-zl;\n        zl = __ziplistCascadeUpdate(zl,p+reqlen);\n        p = zl+offset;\n    }\n\n    /* Write the entry */\n    p += zipStorePrevEntryLength(p,prevlen);\n    p += zipStoreEntryEncoding(p,encoding,slen);\n    if (ZIP_IS_STR(encoding)) {\n        memcpy(p,s,slen);\n    } else {\n        zipSaveInteger(p,value,encoding);\n    }\n    ZIPLIST_INCR_LENGTH(zl,1);\n    return zl;\n}\n\n/* Merge ziplists 'first' and 'second' by appending 'second' to 'first'.\n *\n * NOTE: The larger ziplist is reallocated to contain the new merged ziplist.\n * Either 'first' or 'second' can be used for the result.  The parameter not\n * used will be free'd and set to NULL.\n *\n * After calling this function, the input parameters are no longer valid since\n * they are changed and free'd in-place.\n *\n * The result ziplist is the contents of 'first' followed by 'second'.\n *\n * On failure: returns NULL if the merge is impossible.\n * On success: returns the merged ziplist (which is expanded version of either\n * 'first' or 'second', also frees the other unused input ziplist, and sets the\n * input ziplist argument equal to newly reallocated ziplist return value. */\nunsigned char *ziplistMerge(unsigned char **first, unsigned char **second) {\n    /* If any params are null, we can't merge, so NULL. */\n    if (first == NULL || *first == NULL || second == NULL || *second == NULL)\n        return NULL;\n\n    /* Can't merge same list into itself. */\n    if (*first == *second)\n        return NULL;\n\n    size_t first_bytes = intrev32ifbe(ZIPLIST_BYTES(*first));\n    size_t first_len = intrev16ifbe(ZIPLIST_LENGTH(*first));\n\n    size_t second_bytes = intrev32ifbe(ZIPLIST_BYTES(*second));\n    size_t second_len = intrev16ifbe(ZIPLIST_LENGTH(*second));\n\n    int append;\n    unsigned char *source, *target;\n    size_t target_bytes, source_bytes;\n    /* Pick the largest ziplist so we can resize easily in-place.\n     * We must also track if we are now appending or prepending to\n     * the target ziplist. */\n    if (first_len >= second_len) {\n        /* retain first, append second to first. */\n        target = *first;\n        target_bytes = first_bytes;\n        source = *second;\n        source_bytes = second_bytes;\n        append = 1;\n    } else {\n        /* else, retain second, prepend first to second. */\n        target = *second;\n        target_bytes = second_bytes;\n        source = *first;\n        source_bytes = first_bytes;\n        append = 0;\n    }\n\n    /* Calculate final bytes (subtract one pair of metadata) */\n    size_t zlbytes = first_bytes + second_bytes -\n                     ZIPLIST_HEADER_SIZE - ZIPLIST_END_SIZE;\n    size_t zllength = first_len + second_len;\n\n    /* Combined zl length should be limited within UINT16_MAX */\n    zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX;\n\n    /* Save offset positions before we start ripping memory apart. */\n    size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first));\n    size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second));\n\n    /* Extend target to new zlbytes then append or prepend source. */\n    target = zrealloc(target, zlbytes);\n    if (append) {\n        /* append == appending to target */\n        /* Copy source after target (copying over original [END]):\n         *   [TARGET - END, SOURCE - HEADER] */\n        memcpy(target + target_bytes - ZIPLIST_END_SIZE,\n               source + ZIPLIST_HEADER_SIZE,\n               source_bytes - ZIPLIST_HEADER_SIZE);\n    } else {\n        /* !append == prepending to target */\n        /* Move target *contents* exactly size of (source - [END]),\n         * then copy source into vacataed space (source - [END]):\n         *   [SOURCE - END, TARGET - HEADER] */\n        memmove(target + source_bytes - ZIPLIST_END_SIZE,\n                target + ZIPLIST_HEADER_SIZE,\n                target_bytes - ZIPLIST_HEADER_SIZE);\n        memcpy(target, source, source_bytes - ZIPLIST_END_SIZE);\n    }\n\n    /* Update header metadata. */\n    ZIPLIST_BYTES(target) = intrev32ifbe(zlbytes);\n    ZIPLIST_LENGTH(target) = intrev16ifbe(zllength);\n    /* New tail offset is:\n     *   + N bytes of first ziplist\n     *   - 1 byte for [END] of first ziplist\n     *   + M bytes for the offset of the original tail of the second ziplist\n     *   - J bytes for HEADER because second_offset keeps no header. */\n    ZIPLIST_TAIL_OFFSET(target) = intrev32ifbe(\n                                   (first_bytes - ZIPLIST_END_SIZE) +\n                                   (second_offset - ZIPLIST_HEADER_SIZE));\n\n    /* __ziplistCascadeUpdate just fixes the prev length values until it finds a\n     * correct prev length value (then it assumes the rest of the list is okay).\n     * We tell CascadeUpdate to start at the first ziplist's tail element to fix\n     * the merge seam. */\n    target = __ziplistCascadeUpdate(target, target+first_offset);\n\n    /* Now free and NULL out what we didn't realloc */\n    if (append) {\n        zfree(*second);\n        *second = NULL;\n        *first = target;\n    } else {\n        zfree(*first);\n        *first = NULL;\n        *second = target;\n    }\n    return target;\n}\n\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {\n    unsigned char *p;\n    p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);\n    return __ziplistInsert(zl,p,s,slen);\n}\n\n/* Returns an offset to use for iterating with ziplistNext. When the given\n * index is negative, the list is traversed back to front. When the list\n * doesn't contain an element at the provided index, NULL is returned. */\nunsigned char *ziplistIndex(unsigned char *zl, int index) {\n    unsigned char *p;\n    unsigned int prevlensize, prevlen = 0;\n    if (index < 0) {\n        index = (-index)-1;\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        if (p[0] != ZIP_END) {\n            ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n            while (prevlen > 0 && index--) {\n                p -= prevlen;\n                ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n            }\n        }\n    } else {\n        p = ZIPLIST_ENTRY_HEAD(zl);\n        while (p[0] != ZIP_END && index--) {\n            p += zipRawEntryLength(p);\n        }\n    }\n    return (p[0] == ZIP_END || index > 0) ? NULL : p;\n}\n\n/* Return pointer to next entry in ziplist.\n *\n * zl is the pointer to the ziplist\n * p is the pointer to the current element\n *\n * The element after 'p' is returned, otherwise NULL if we are at the end. */\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {\n    ((void) zl);\n\n    /* \"p\" could be equal to ZIP_END, caused by ziplistDelete,\n     * and we should return NULL. Otherwise, we should return NULL\n     * when the *next* element is ZIP_END (there is no next entry). */\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    p += zipRawEntryLength(p);\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    return p;\n}\n\n/* Return pointer to previous entry in ziplist. */\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {\n    unsigned int prevlensize, prevlen = 0;\n\n    /* Iterating backwards from ZIP_END should return the tail. When \"p\" is\n     * equal to the first element of the list, we're already at the head,\n     * and should return NULL. */\n    if (p[0] == ZIP_END) {\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        return (p[0] == ZIP_END) ? NULL : p;\n    } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {\n        return NULL;\n    } else {\n        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n        assert(prevlen > 0);\n        return p-prevlen;\n    }\n}\n\n/* Get entry pointed to by 'p' and store in either '*sstr' or 'sval' depending\n * on the encoding of the entry. '*sstr' is always set to NULL to be able\n * to find out whether the string pointer or the integer value was set.\n * Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */\nunsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {\n    zlentry entry;\n    if (p == NULL || p[0] == ZIP_END) return 0;\n    if (sstr) *sstr = NULL;\n\n    zipEntry(p, &entry);\n    if (ZIP_IS_STR(entry.encoding)) {\n        if (sstr) {\n            *slen = entry.len;\n            *sstr = p+entry.headersize;\n        }\n    } else {\n        if (sval) {\n            *sval = zipLoadInteger(p+entry.headersize,entry.encoding);\n        }\n    }\n    return 1;\n}\n\n/* Insert an entry at \"p\". */\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    return __ziplistInsert(zl,p,s,slen);\n}\n\n/* Delete a single entry from the ziplist, pointed to by *p.\n * Also update *p in place, to be able to iterate over the\n * ziplist, while deleting entries. */\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {\n    size_t offset = *p-zl;\n    zl = __ziplistDelete(zl,*p,1);\n\n    /* Store pointer to current element in p, because ziplistDelete will\n     * do a realloc which might result in a different \"zl\"-pointer.\n     * When the delete direction is back to front, we might delete the last\n     * entry and end up with \"p\" pointing to ZIP_END, so check this. */\n    *p = zl+offset;\n    return zl;\n}\n\n/* Delete a range of entries from the ziplist. */\nunsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num) {\n    unsigned char *p = ziplistIndex(zl,index);\n    return (p == NULL) ? zl : __ziplistDelete(zl,p,num);\n}\n\n/* Compare entry pointer to by 'p' with 'sstr' of length 'slen'. */\n/* Return 1 if equal. */\nunsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {\n    zlentry entry;\n    unsigned char sencoding;\n    long long zval, sval;\n    if (p[0] == ZIP_END) return 0;\n\n    zipEntry(p, &entry);\n    if (ZIP_IS_STR(entry.encoding)) {\n        /* Raw compare */\n        if (entry.len == slen) {\n            return memcmp(p+entry.headersize,sstr,slen) == 0;\n        } else {\n            return 0;\n        }\n    } else {\n        /* Try to compare encoded values. Don't compare encoding because\n         * different implementations may encoded integers differently. */\n        if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {\n          zval = zipLoadInteger(p+entry.headersize,entry.encoding);\n          return zval == sval;\n        }\n    }\n    return 0;\n}\n\n/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries\n * between every comparison. Returns NULL when the field could not be found. */\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {\n    int skipcnt = 0;\n    unsigned char vencoding = 0;\n    long long vll = 0;\n\n    while (p[0] != ZIP_END) {\n        unsigned int prevlensize, encoding, lensize, len;\n        unsigned char *q;\n\n        ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n        ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n        q = p + prevlensize + lensize;\n\n        if (skipcnt == 0) {\n            /* Compare current entry with specified entry */\n            if (ZIP_IS_STR(encoding)) {\n                if (len == vlen && memcmp(q, vstr, vlen) == 0) {\n                    return p;\n                }\n            } else {\n                /* Find out if the searched field can be encoded. Note that\n                 * we do it only the first time, once done vencoding is set\n                 * to non-zero and vll is set to the integer value. */\n                if (vencoding == 0) {\n                    if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {\n                        /* If the entry can't be encoded we set it to\n                         * UCHAR_MAX so that we don't retry again the next\n                         * time. */\n                        vencoding = UCHAR_MAX;\n                    }\n                    /* Must be non-zero by now */\n                    assert(vencoding);\n                }\n\n                /* Compare current entry with specified entry, do it only\n                 * if vencoding != UCHAR_MAX because if there is no encoding\n                 * possible for the field it can't be a valid integer. */\n                if (vencoding != UCHAR_MAX) {\n                    long long ll = zipLoadInteger(q, encoding);\n                    if (ll == vll) {\n                        return p;\n                    }\n                }\n            }\n\n            /* Reset skip count */\n            skipcnt = skip;\n        } else {\n            /* Skip entry */\n            skipcnt--;\n        }\n\n        /* Move to next entry */\n        p = q + len;\n    }\n\n    return NULL;\n}\n\n/* Return length of ziplist. */\nunsigned int ziplistLen(unsigned char *zl) {\n    unsigned int len = 0;\n    if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {\n        len = intrev16ifbe(ZIPLIST_LENGTH(zl));\n    } else {\n        unsigned char *p = zl+ZIPLIST_HEADER_SIZE;\n        while (*p != ZIP_END) {\n            p += zipRawEntryLength(p);\n            len++;\n        }\n\n        /* Re-store length if small enough */\n        if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);\n    }\n    return len;\n}\n\n/* Return ziplist blob size in bytes. */\nsize_t ziplistBlobLen(unsigned char *zl) {\n    return intrev32ifbe(ZIPLIST_BYTES(zl));\n}\n\nvoid ziplistRepr(unsigned char *zl) {\n    unsigned char *p;\n    int index = 0;\n    zlentry entry;\n\n    printf(\n        \"{total bytes %d} \"\n        \"{num entries %u}\\n\"\n        \"{tail offset %u}\\n\",\n        intrev32ifbe(ZIPLIST_BYTES(zl)),\n        intrev16ifbe(ZIPLIST_LENGTH(zl)),\n        intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));\n    p = ZIPLIST_ENTRY_HEAD(zl);\n    while(*p != ZIP_END) {\n        zipEntry(p, &entry);\n        printf(\n            \"{\\n\"\n                \"\\taddr 0x%08lx,\\n\"\n                \"\\tindex %2d,\\n\"\n                \"\\toffset %5ld,\\n\"\n                \"\\thdr+entry len: %5u,\\n\"\n                \"\\thdr len%2u,\\n\"\n                \"\\tprevrawlen: %5u,\\n\"\n                \"\\tprevrawlensize: %2u,\\n\"\n                \"\\tpayload %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        printf(\"\\tbytes: \");\n        for (unsigned int i = 0; i < entry.headersize+entry.len; i++) {\n            printf(\"%02x|\",p[i]);\n        }\n        printf(\"\\n\");\n        p += entry.headersize;\n        if (ZIP_IS_STR(entry.encoding)) {\n            printf(\"\\t[str]\");\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(\"\\t[int]%lld\", (long long) zipLoadInteger(p,entry.encoding));\n        }\n        printf(\"\\n}\\n\");\n        p += entry.len;\n        index++;\n    }\n    printf(\"{end}\\n\\n\");\n}\n\n#ifdef REDIS_TEST\n#include <sys/time.h>\n#include \"adlist.h\"\n#include \"sds.h\"\n\n#define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }\n\nstatic unsigned char *createList() {\n    unsigned char *zl = ziplistNew();\n    zl = ziplistPush(zl, (unsigned char*)\"foo\", 3, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"quux\", 4, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"hello\", 5, ZIPLIST_HEAD);\n    zl = ziplistPush(zl, (unsigned char*)\"1024\", 4, ZIPLIST_TAIL);\n    return zl;\n}\n\nstatic unsigned char *createIntList() {\n    unsigned char *zl = ziplistNew();\n    char buf[32];\n\n    sprintf(buf, \"100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"128000\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"-100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"4294967296\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"much much longer non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    return zl;\n}\n\nstatic long long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\nstatic void stress(int pos, int num, int maxsize, int dnum) {\n    int i,j,k;\n    unsigned char *zl;\n    char posstr[2][5] = { \"HEAD\", \"TAIL\" };\n    long long start;\n    for (i = 0; i < maxsize; i+=dnum) {\n        zl = ziplistNew();\n        for (j = 0; j < i; j++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,ZIPLIST_TAIL);\n        }\n\n        /* Do num times a push+pop from pos */\n        start = usec();\n        for (k = 0; k < num; k++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,pos);\n            zl = ziplistDeleteRange(zl,0,1);\n        }\n        printf(\"List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\\n\",\n            i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start);\n        zfree(zl);\n    }\n}\n\nstatic unsigned char *pop(unsigned char *zl, int where) {\n    unsigned char *p, *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);\n    if (ziplistGet(p,&vstr,&vlen,&vlong)) {\n        if (where == ZIPLIST_HEAD)\n            printf(\"Pop head: \");\n        else\n            printf(\"Pop tail: \");\n\n        if (vstr) {\n            if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror(\"fwrite\");\n        }\n        else {\n            printf(\"%lld\", vlong);\n        }\n\n        printf(\"\\n\");\n        return ziplistDelete(zl,&p);\n    } else {\n        printf(\"ERROR: Could not pop\\n\");\n        exit(1);\n    }\n}\n\nstatic int randstring(char *target, unsigned int min, unsigned int max) {\n    int p = 0;\n    int len = min+rand()%(max-min+1);\n    int minval, maxval;\n    switch(rand() % 3) {\n    case 0:\n        minval = 0;\n        maxval = 255;\n    break;\n    case 1:\n        minval = 48;\n        maxval = 122;\n    break;\n    case 2:\n        minval = 48;\n        maxval = 52;\n    break;\n    default:\n        assert(NULL);\n    }\n\n    while(p < len)\n        target[p++] = minval+rand()%(maxval-minval+1);\n    return len;\n}\n\nstatic void verify(unsigned char *zl, zlentry *e) {\n    int len = ziplistLen(zl);\n    zlentry _e;\n\n    ZIPLIST_ENTRY_ZERO(&_e);\n\n    for (int i = 0; i < len; i++) {\n        memset(&e[i], 0, sizeof(zlentry));\n        zipEntry(ziplistIndex(zl, i), &e[i]);\n\n        memset(&_e, 0, sizeof(zlentry));\n        zipEntry(ziplistIndex(zl, -len+i), &_e);\n\n        assert(memcmp(&e[i], &_e, sizeof(zlentry)) == 0);\n    }\n}\n\nint ziplistTest(int argc, char **argv) {\n    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    zfree(zl);\n\n    zl = createList();\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_HEAD);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zfree(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        zfree(zl);\n    }\n\n    printf(\"Get element at index 4 (out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Get element at index -1 (last element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -1\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Get element at index -4 (first element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -4\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Get element at index -5 (reverse out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -5);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate list from 0 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 0);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate list from 1 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate list from 2 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 2);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate starting out of range:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate from back to front:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate from back to front, deleting all items:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            zl = ziplistDelete(zl,&p);\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Delete inclusive range 0,0:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 1);\n        ziplistRepr(zl);\n        zfree(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        zfree(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        zfree(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        zfree(zl);\n    }\n\n    printf(\"Delete with num overflow:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 5);\n        ziplistRepr(zl);\n        zfree(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        zfree(zl);\n    }\n\n    printf(\"Regression test for >255 byte strings:\\n\");\n    {\n        char v1[257] = {0}, v2[257] = {0};\n        memset(v1,'x',256);\n        memset(v2,'y',256);\n        zl = ziplistNew();\n        zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL);\n\n        /* Pop values again and compare their value. */\n        p = ziplistIndex(zl,0);\n        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        zfree(zl);\n    }\n\n    printf(\"Regression test deleting next to last entries:\\n\");\n    {\n        char v[3][257] = {{0}};\n        zlentry e[3] = {{.prevrawlensize = 0, .prevrawlen = 0, .lensize = 0,\n                         .len = 0, .headersize = 0, .encoding = 0, .p = NULL}};\n        size_t i;\n\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            memset(v[i], 'a' + i, sizeof(v[0]));\n        }\n\n        v[0][256] = '\\0';\n        v[1][  1] = '\\0';\n        v[2][256] = '\\0';\n\n        zl = ziplistNew();\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL);\n        }\n\n        verify(zl, e);\n\n        assert(e[0].prevrawlensize == 1);\n        assert(e[1].prevrawlensize == 5);\n        assert(e[2].prevrawlensize == 1);\n\n        /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */\n        unsigned char *p = e[1].p;\n        zl = ziplistDelete(zl, &p);\n\n        verify(zl, e);\n\n        assert(e[0].prevrawlensize == 1);\n        assert(e[1].prevrawlensize == 5);\n\n        printf(\"SUCCESS\\n\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Create long list and check indices:\\n\");\n    {\n        zl = ziplistNew();\n        char buf[32];\n        int i,len;\n        for (i = 0; i < 1000; i++) {\n            len = sprintf(buf,\"%d\",i);\n            zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);\n        }\n        for (i = 0; i < 1000; i++) {\n            p = ziplistIndex(zl,i);\n            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        zfree(zl);\n    }\n\n    printf(\"Compare strings with ziplist entries:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Merge test:\\n\");\n    {\n        /* create list gives us: [hello, foo, quux, 1024] */\n        zl = createList();\n        unsigned char *zl2 = createList();\n\n        unsigned char *zl3 = ziplistNew();\n        unsigned char *zl4 = ziplistNew();\n\n        if (ziplistMerge(&zl4, &zl4)) {\n            printf(\"ERROR: Allowed merging of one ziplist into itself.\\n\");\n            return 1;\n        }\n\n        /* Merge two empty ziplists, get empty result back. */\n        zl4 = ziplistMerge(&zl3, &zl4);\n        ziplistRepr(zl4);\n        if (ziplistLen(zl4)) {\n            printf(\"ERROR: Merging two empty ziplists created entries.\\n\");\n            return 1;\n        }\n        zfree(zl4);\n\n        zl2 = ziplistMerge(&zl, &zl2);\n        /* merge gives us: [hello, foo, quux, 1024, hello, foo, quux, 1024] */\n        ziplistRepr(zl2);\n\n        if (ziplistLen(zl2) != 8) {\n            printf(\"ERROR: Merged length not 8, but: %u\\n\", ziplistLen(zl2));\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,4);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,7);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Stress with random payloads of different encoding:\\n\");\n    {\n        int i,j,len,where;\n        unsigned char *p;\n        char buf[1024];\n        int buflen;\n        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,(void (*)(void*))sdsfree);\n            len = rand() % 256;\n\n            /* Create lists */\n            for (j = 0; j < len; j++) {\n                where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;\n                if (rand() % 2) {\n                    buflen = randstring(buf,1,sizeof(buf)-1);\n                } else {\n                    switch(rand() % 3) {\n                    case 0:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) >> 20);\n                        break;\n                    case 1:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()));\n                        break;\n                    case 2:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) << 20);\n                        break;\n                    default:\n                        assert(NULL);\n                    }\n                }\n\n                /* Add to ziplist */\n                zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);\n\n                /* Add to reference list */\n                if (where == ZIPLIST_HEAD) {\n                    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#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#ifndef _ZIPLIST_H\n#define _ZIPLIST_H\n\n#define ZIPLIST_HEAD 0\n#define ZIPLIST_TAIL 1\n\nunsigned char *ziplistNew(void);\nunsigned char *ziplistMerge(unsigned char **first, unsigned char **second);\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);\nunsigned char *ziplistIndex(unsigned char *zl, int index);\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p);\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p);\nunsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval);\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);\nunsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num);\nunsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);\nunsigned int ziplistLen(unsigned char *zl);\nsize_t ziplistBlobLen(unsigned char *zl);\nvoid ziplistRepr(unsigned char *zl);\n\n#ifdef REDIS_TEST\nint ziplistTest(int argc, char *argv[]);\n#endif\n\n#endif /* _ZIPLIST_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/src/zipmap.c",
    "content": "/* String -> String Map data structure optimized for size.\n * This file implements a data structure mapping strings to other strings\n * implementing an O(n) lookup data structure designed to be very memory\n * efficient.\n *\n * The Redis Hash type uses this data structure for hashes composed of a small\n * number of elements, to switch to a hash table once a given number of\n * elements is reached.\n *\n * Given that many times Redis Hashes are used to represent objects composed\n * of few fields, this is a very big win in terms of used memory.\n *\n * --------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Memory layout of a zipmap, for the map \"foo\" => \"bar\", \"hello\" => \"world\":\n *\n * <zmlen><len>\"foo\"<len><free>\"bar\"<len>\"hello\"<len><free>\"world\"\n *\n * <zmlen> is 1 byte length that holds the current size of the zipmap.\n * When the zipmap length is greater than or equal to 254, this value\n * is not used and the zipmap needs to be traversed to find out the length.\n *\n * <len> is the length of the following string (key or value).\n * <len> lengths are encoded in a single value or in a 5 bytes value.\n * If the first byte value (as an unsigned 8 bit value) is between 0 and\n * 253, it's a single-byte length. If it is 254 then a four bytes unsigned\n * integer follows (in the host byte ordering). A value of 255 is used to\n * signal the end of the hash.\n *\n * <free> is the number of free unused bytes after the string, resulting\n * from modification of values associated to a key. For instance if \"foo\"\n * is set to \"bar\", and later \"foo\" will be set to \"hi\", it will have a\n * free byte to use if the value will enlarge again later, or even in\n * order to add a key/value pair if it fits.\n *\n * <free> is always an unsigned 8 bit number, because if after an\n * update operation there are more than a few free bytes, the zipmap will be\n * reallocated to make sure it is as small as possible.\n *\n * The most compact representation of the above two elements hash is actually:\n *\n * \"\\x02\\x03foo\\x03\\x00bar\\x05hello\\x05\\x00world\\xff\"\n *\n * Note that because keys and values are prefixed length \"objects\",\n * the lookup will take O(N) where N is the number of elements\n * in the zipmap and *not* the number of bytes needed to represent the zipmap.\n * This lowers the constant times considerably.\n */\n\n#include <stdio.h>\n#include <string.h>\n#include \"zmalloc.h\"\n#include \"endianconv.h\"\n\n#define ZIPMAP_BIGLEN 254\n#define ZIPMAP_END 255\n\n/* The following defines the max value for the <free> field described in the\n * comments above, that is, the max number of trailing bytes in a value. */\n#define ZIPMAP_VALUE_MAX_FREE 4\n\n/* The following macro returns the number of bytes needed to encode the length\n * for the integer value _l, that is, 1 byte for lengths < ZIPMAP_BIGLEN and\n * 5 bytes for all the other lengths. */\n#define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)\n\n/* Create a new empty zipmap. */\nunsigned char *zipmapNew(void) {\n    unsigned char *zm = zmalloc(2);\n\n    zm[0] = 0; /* Length */\n    zm[1] = ZIPMAP_END;\n    return zm;\n}\n\n/* Decode the encoded length pointed by 'p' */\nstatic unsigned int zipmapDecodeLength(unsigned char *p) {\n    unsigned int len = *p;\n\n    if (len < ZIPMAP_BIGLEN) return len;\n    memcpy(&len,p+1,sizeof(unsigned int));\n    memrev32ifbe(&len);\n    return len;\n}\n\n/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns\n * the amount of bytes required to encode such a length. */\nstatic unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return ZIPMAP_LEN_BYTES(len);\n    } else {\n        if (len < ZIPMAP_BIGLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            p[0] = ZIPMAP_BIGLEN;\n            memcpy(p+1,&len,sizeof(len));\n            memrev32ifbe(p+1);\n            return 1+sizeof(len);\n        }\n    }\n}\n\n/* Search for a matching key, returning a pointer to the entry inside the\n * zipmap. Returns NULL if the key is not found.\n *\n * If NULL is returned, and totlen is not NULL, it is set to the entire\n * size of the zimap, so that the calling function will be able to\n * reallocate the original zipmap to make room for more entries. */\nstatic unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {\n    unsigned char *p = zm+1, *k = NULL;\n    unsigned int l,llen;\n\n    while(*p != ZIPMAP_END) {\n        unsigned char free;\n\n        /* Match or skip the key */\n        l = zipmapDecodeLength(p);\n        llen = zipmapEncodeLength(NULL,l);\n        if (key != NULL && k == NULL && l == klen && !memcmp(p+llen,key,l)) {\n            /* Only return when the user doesn't care\n             * for the total length of the zipmap. */\n            if (totlen != NULL) {\n                k = p;\n            } else {\n                return p;\n            }\n        }\n        p += llen+l;\n        /* Skip the value as well */\n        l = zipmapDecodeLength(p);\n        p += zipmapEncodeLength(NULL,l);\n        free = p[0];\n        p += l+1+free; /* +1 to skip the free byte */\n    }\n    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;\n    return k;\n}\n\nstatic unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) {\n    unsigned int l;\n\n    l = klen+vlen+3;\n    if (klen >= ZIPMAP_BIGLEN) l += 4;\n    if (vlen >= ZIPMAP_BIGLEN) l += 4;\n    return l;\n}\n\n/* Return the total amount used by a key (encoded length + payload) */\nstatic unsigned int zipmapRawKeyLength(unsigned char *p) {\n    unsigned int l = zipmapDecodeLength(p);\n    return zipmapEncodeLength(NULL,l) + l;\n}\n\n/* Return the total amount used by a value\n * (encoded length + single byte free count + payload) */\nstatic unsigned int zipmapRawValueLength(unsigned char *p) {\n    unsigned int l = zipmapDecodeLength(p);\n    unsigned int used;\n\n    used = zipmapEncodeLength(NULL,l);\n    used += p[used] + 1 + l;\n    return used;\n}\n\n/* If 'p' points to a key, this function returns the total amount of\n * bytes used to store this entry (entry = key + associated value + trailing\n * free space if any). */\nstatic unsigned int zipmapRawEntryLength(unsigned char *p) {\n    unsigned int l = zipmapRawKeyLength(p);\n    return l + zipmapRawValueLength(p+l);\n}\n\nstatic inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) {\n    zm = zrealloc(zm, len);\n    zm[len-1] = ZIPMAP_END;\n    return zm;\n}\n\n/* Set key to value, creating the key if it does not already exist.\n * If 'update' is not NULL, *update is set to 1 if the key was\n * already preset, otherwise to 0. */\nunsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) {\n    unsigned int zmlen, offset;\n    unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);\n    unsigned int empty, vempty;\n    unsigned char *p;\n\n    freelen = reqlen;\n    if (update) *update = 0;\n    p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p == NULL) {\n        /* Key not found: enlarge */\n        zm = zipmapResize(zm, zmlen+reqlen);\n        p = zm+zmlen-1;\n        zmlen = zmlen+reqlen;\n\n        /* Increase zipmap length (this is an insert) */\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;\n    } else {\n        /* Key found. Is there enough space for the new value? */\n        /* Compute the total length: */\n        if (update) *update = 1;\n        freelen = zipmapRawEntryLength(p);\n        if (freelen < reqlen) {\n            /* Store the offset of this key within the current zipmap, so\n             * it can be resized. Then, move the tail backwards so this\n             * pair fits at the current position. */\n            offset = p-zm;\n            zm = zipmapResize(zm, zmlen-freelen+reqlen);\n            p = zm+offset;\n\n            /* The +1 in the number of bytes to be moved is caused by the\n             * end-of-zipmap byte. Note: the *original* zmlen is used. */\n            memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n            zmlen = zmlen-freelen+reqlen;\n            freelen = reqlen;\n        }\n    }\n\n    /* We now have a suitable block where the key/value entry can\n     * be written. If there is too much free space, move the tail\n     * of the zipmap a few bytes to the front and shrink the zipmap,\n     * as we want zipmaps to be very space efficient. */\n    empty = freelen-reqlen;\n    if (empty >= ZIPMAP_VALUE_MAX_FREE) {\n        /* First, move the tail <empty> bytes to the front, then resize\n         * the zipmap to be <empty> bytes smaller. */\n        offset = p-zm;\n        memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n        zmlen -= empty;\n        zm = zipmapResize(zm, zmlen);\n        p = zm+offset;\n        vempty = 0;\n    } else {\n        vempty = empty;\n    }\n\n    /* Just write the key + value and we are done. */\n    /* Key: */\n    p += zipmapEncodeLength(p,klen);\n    memcpy(p,key,klen);\n    p += klen;\n    /* Value: */\n    p += zipmapEncodeLength(p,vlen);\n    *p++ = vempty;\n    memcpy(p,val,vlen);\n    return zm;\n}\n\n/* Remove the specified key. If 'deleted' is not NULL the pointed integer is\n * set to 0 if the key was not found, to 1 if it was found and deleted. */\nunsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) {\n    unsigned int zmlen, freelen;\n    unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p) {\n        freelen = zipmapRawEntryLength(p);\n        memmove(p, p+freelen, zmlen-((p-zm)+freelen+1));\n        zm = zipmapResize(zm, zmlen-freelen);\n\n        /* Decrease zipmap length */\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;\n\n        if (deleted) *deleted = 1;\n    } else {\n        if (deleted) *deleted = 0;\n    }\n    return zm;\n}\n\n/* Call before iterating through elements via zipmapNext() */\nunsigned char *zipmapRewind(unsigned char *zm) {\n    return zm+1;\n}\n\n/* This function is used to iterate through all the zipmap elements.\n * In the first call the first argument is the pointer to the zipmap + 1.\n * In the next calls what zipmapNext returns is used as first argument.\n * Example:\n *\n * unsigned char *i = zipmapRewind(my_zipmap);\n * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {\n *     printf(\"%d bytes key at $p\\n\", klen, key);\n *     printf(\"%d bytes value at $p\\n\", vlen, value);\n * }\n */\nunsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {\n    if (zm[0] == ZIPMAP_END) return NULL;\n    if (key) {\n        *key = zm;\n        *klen = zipmapDecodeLength(zm);\n        *key += ZIPMAP_LEN_BYTES(*klen);\n    }\n    zm += zipmapRawKeyLength(zm);\n    if (value) {\n        *value = zm+1;\n        *vlen = zipmapDecodeLength(zm);\n        *value += ZIPMAP_LEN_BYTES(*vlen);\n    }\n    zm += zipmapRawValueLength(zm);\n    return zm;\n}\n\n/* Search a key and retrieve the pointer and len of the associated value.\n * If the key is found the function returns 1, otherwise 0. */\nint zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) {\n    unsigned char *p;\n\n    if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0;\n    p += zipmapRawKeyLength(p);\n    *vlen = zipmapDecodeLength(p);\n    *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;\n    return 1;\n}\n\n/* Return 1 if the key exists, otherwise 0 is returned. */\nint zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen) {\n    return zipmapLookupRaw(zm,key,klen,NULL) != NULL;\n}\n\n/* Return the number of entries inside a zipmap */\nunsigned int zipmapLen(unsigned char *zm) {\n    unsigned int len = 0;\n    if (zm[0] < ZIPMAP_BIGLEN) {\n        len = zm[0];\n    } else {\n        unsigned char *p = zipmapRewind(zm);\n        while((p = zipmapNext(p,NULL,NULL,NULL,NULL)) != NULL) len++;\n\n        /* Re-store length if small enough */\n        if (len < ZIPMAP_BIGLEN) zm[0] = len;\n    }\n    return len;\n}\n\n/* Return the raw size in bytes of a zipmap, so that we can serialize\n * the zipmap on disk (or everywhere is needed) just writing the returned\n * amount of bytes of the C array starting at the zipmap pointer. */\nsize_t zipmapBlobLen(unsigned char *zm) {\n    unsigned int totlen;\n    zipmapLookupRaw(zm,NULL,0,&totlen);\n    return totlen;\n}\n\n#ifdef REDIS_TEST\nstatic void 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\n#define UNUSED(x) (void)(x)\nint zipmapTest(int argc, char *argv[]) {\n    unsigned char *zm;\n\n    UNUSED(argc);\n    UNUSED(argv);\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": "Chapter02/redis-6.0.3/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#ifdef REDIS_TEST\nint zipmapTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#include <stdint.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#include \"atomicvar.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#define mallocx(size,flags) je_mallocx(size,flags)\n#define dallocx(ptr,flags) je_dallocx(ptr,flags)\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    atomicIncr(used_memory,__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    atomicDecr(used_memory,__n); \\\n} while(0)\n\nstatic size_t used_memory = 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\n/* Allocation and free functions that bypass the thread cache\n * and go straight to the allocator arena bins.\n * Currently implemented only for jemalloc. Used for online defragmentation. */\n#ifdef HAVE_DEFRAG\nvoid *zmalloc_no_tcache(size_t size) {\n    void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);\n    if (!ptr) zmalloc_oom_handler(size);\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n}\n\nvoid zfree_no_tcache(void *ptr) {\n    if (ptr == NULL) return;\n    update_zmalloc_stat_free(zmalloc_size(ptr));\n    dallocx(ptr, MALLOCX_TCACHE_NONE);\n}\n#endif\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 (size == 0 && ptr != NULL) {\n        zfree(ptr);\n        return NULL;\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+PREFIX_SIZE);\n    update_zmalloc_stat_alloc(size+PREFIX_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}\nsize_t zmalloc_usable(void *ptr) {\n    return zmalloc_size(ptr)-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    atomicGet(used_memory,um);\n    return um;\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#elif defined(__FreeBSD__)\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <sys/user.h>\n#include <unistd.h>\n\nsize_t zmalloc_get_rss(void) {\n    struct kinfo_proc info;\n    size_t infolen = sizeof(info);\n    int mib[4];\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PID;\n    mib[3] = getpid();\n\n    if (sysctl(mib, 4, &info, &infolen, NULL, 0) == 0)\n        return (size_t)info.ki_rssize;\n\n    return 0L;\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#if defined(USE_JEMALLOC)\n\nint zmalloc_get_allocator_info(size_t *allocated,\n                               size_t *active,\n                               size_t *resident) {\n    uint64_t epoch = 1;\n    size_t sz;\n    *allocated = *resident = *active = 0;\n    /* Update the statistics cached by mallctl. */\n    sz = sizeof(epoch);\n    je_mallctl(\"epoch\", &epoch, &sz, &epoch, sz);\n    sz = sizeof(size_t);\n    /* Unlike RSS, this does not include RSS from shared libraries and other non\n     * heap mappings. */\n    je_mallctl(\"stats.resident\", resident, &sz, NULL, 0);\n    /* Unlike resident, this doesn't not include the pages jemalloc reserves\n     * for re-use (purge will clean that). */\n    je_mallctl(\"stats.active\", active, &sz, NULL, 0);\n    /* Unlike zmalloc_used_memory, this matches the stats.resident by taking\n     * into account all allocations done by this process (not only zmalloc). */\n    je_mallctl(\"stats.allocated\", allocated, &sz, NULL, 0);\n    return 1;\n}\n\nvoid set_jemalloc_bg_thread(int enable) {\n    /* let jemalloc do purging asynchronously, required when there's no traffic \n     * after flushdb */\n    char val = !!enable;\n    je_mallctl(\"background_thread\", NULL, 0, &val, 1);\n}\n\nint jemalloc_purge() {\n    /* return all unused (reserved) pages to the OS */\n    char tmp[32];\n    unsigned narenas = 0;\n    size_t sz = sizeof(unsigned);\n    if (!je_mallctl(\"arenas.narenas\", &narenas, &sz, NULL, 0)) {\n        sprintf(tmp, \"arena.%d.purge\", narenas);\n        if (!je_mallctl(tmp, NULL, 0, NULL, 0))\n            return 0;\n    }\n    return -1;\n}\n\n#else\n\nint zmalloc_get_allocator_info(size_t *allocated,\n                               size_t *active,\n                               size_t *resident) {\n    *allocated = *resident = *active = 0;\n    return 1;\n}\n\nvoid set_jemalloc_bg_thread(int enable) {\n    ((void)(enable));\n}\n\nint jemalloc_purge() {\n    return 0;\n}\n\n#endif\n\n#if defined(__APPLE__)\n/* For proc_pidinfo() used later in zmalloc_get_smap_bytes_by_field().\n * Note that this file cannot be included in zmalloc.h because it includes\n * a Darwin queue.h file where there is a \"LIST_HEAD\" macro (!) defined\n * conficting with Redis user code. */\n#include <libproc.h>\n#endif\n\n/* Get the sum of the specified field (converted form kb to bytes) in\n * /proc/self/smaps. The field must be specified with trailing \":\" as it\n * apperas in the smaps output.\n *\n * If a pid is specified, the information is extracted for such a pid,\n * otherwise if pid is -1 the information is reported is about the\n * current process.\n *\n * Example: zmalloc_get_smap_bytes_by_field(\"Rss:\",-1);\n */\n#if defined(HAVE_PROC_SMAPS)\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {\n    char line[1024];\n    size_t bytes = 0;\n    int flen = strlen(field);\n    FILE *fp;\n\n    if (pid == -1) {\n        fp = fopen(\"/proc/self/smaps\",\"r\");\n    } else {\n        char filename[128];\n        snprintf(filename,sizeof(filename),\"/proc/%ld/smaps\",pid);\n        fp = fopen(filename,\"r\");\n    }\n\n    if (!fp) return 0;\n    while(fgets(line,sizeof(line),fp) != NULL) {\n        if (strncmp(line,field,flen) == 0) {\n            char *p = strchr(line,'k');\n            if (p) {\n                *p = '\\0';\n                bytes += strtol(line+flen,NULL,10) * 1024;\n            }\n        }\n    }\n    fclose(fp);\n    return bytes;\n}\n#else\n/* Get sum of the specified field from libproc api call.\n * As there are per page value basis we need to convert\n * them accordingly.\n *\n * Note that AnonHugePages is a no-op as THP feature\n * is not supported in this platform\n */\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {\n#if defined(__APPLE__)\n    struct proc_regioninfo pri;\n    if (proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, PROC_PIDREGIONINFO_SIZE) ==\n\tPROC_PIDREGIONINFO_SIZE) {\n\tif (!strcmp(field, \"Private_Dirty:\")) {\n            return (size_t)pri.pri_pages_dirtied * 4096;\n\t} else if (!strcmp(field, \"Rss:\")) {\n            return (size_t)pri.pri_pages_resident * 4096;\n\t} else if (!strcmp(field, \"AnonHugePages:\")) {\n            return 0;\n\t}\n    }\n    return 0;\n#endif\n    ((void) field);\n    ((void) pid);\n    return 0;\n}\n#endif\n\nsize_t zmalloc_get_private_dirty(long pid) {\n    return zmalloc_get_smap_bytes_by_field(\"Private_Dirty:\",pid);\n}\n\n/* Returns the size of physical memory (RAM) in bytes.\n * It looks ugly, but this is the cleanest way to achieve cross platform results.\n * Cleaned up from:\n *\n * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system\n *\n * Note that this function:\n * 1) Was released under the following CC attribution license:\n *    http://creativecommons.org/licenses/by/3.0/deed.en_US.\n * 2) Was originally implemented by David Robert Nadeau.\n * 3) Was modified for Redis by Matt Stancliff.\n * 4) This note exists in order to comply with the original license.\n */\nsize_t zmalloc_get_memory_size(void) {\n#if defined(__unix__) || defined(__unix) || defined(unix) || \\\n    (defined(__APPLE__) && defined(__MACH__))\n#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_MEMSIZE)\n    mib[1] = HW_MEMSIZE;            /* OSX. --------------------- */\n#elif defined(HW_PHYSMEM64)\n    mib[1] = HW_PHYSMEM64;          /* NetBSD, OpenBSD. --------- */\n#endif\n    int64_t size = 0;               /* 64-bit */\n    size_t len = sizeof(size);\n    if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n\n#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)\n    /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */\n    return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);\n\n#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))\n    /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_REALMEM)\n    mib[1] = HW_REALMEM;        /* FreeBSD. ----------------- */\n#elif defined(HW_PHYSMEM)\n    mib[1] = HW_PHYSMEM;        /* Others. ------------------ */\n#endif\n    unsigned int size = 0;      /* 32-bit */\n    size_t len = sizeof(size);\n    if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n#else\n    return 0L;          /* Unknown method to get the data. */\n#endif\n#else\n    return 0L;          /* Unknown OS. */\n#endif\n}\n\n#ifdef REDIS_TEST\n#define UNUSED(x) ((void)(x))\nint zmalloc_test(int argc, char **argv) {\n    void *ptr;\n\n    UNUSED(argc);\n    UNUSED(argv);\n    printf(\"Initial used memory: %zu\\n\", zmalloc_used_memory());\n    ptr = zmalloc(123);\n    printf(\"Allocated 123 bytes; used: %zu\\n\", zmalloc_used_memory());\n    ptr = zrealloc(ptr, 456);\n    printf(\"Reallocated to 456 bytes; used: %zu\\n\", zmalloc_used_memory());\n    zfree(ptr);\n    printf(\"Freed pointer; used: %zu\\n\", zmalloc_used_memory());\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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#ifdef __GLIBC__\n#include <malloc.h>\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) malloc_usable_size(p)\n#endif\n#endif\n\n/* We can enable the Redis defrag capabilities only if we are using Jemalloc\n * and the version used is our special version modified for Redis having\n * the ability to return per-allocation fragmentation hints. */\n#if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)\n#define HAVE_DEFRAG\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_set_oom_handler(void (*oom_handler)(size_t));\nsize_t zmalloc_get_rss(void);\nint zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);\nvoid set_jemalloc_bg_thread(int enable);\nint jemalloc_purge();\nsize_t zmalloc_get_private_dirty(long pid);\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid);\nsize_t zmalloc_get_memory_size(void);\nvoid zlibc_free(void *ptr);\n\n#ifdef HAVE_DEFRAG\nvoid zfree_no_tcache(void *ptr);\nvoid *zmalloc_no_tcache(size_t size);\n#endif\n\n#ifndef HAVE_MALLOC_SIZE\nsize_t zmalloc_size(void *ptr);\nsize_t zmalloc_usable(void *ptr);\n#else\n#define zmalloc_usable(p) zmalloc_size(p)\n#endif\n\n#ifdef REDIS_TEST\nint zmalloc_test(int argc, char **argv);\n#endif\n\n#endif /* __ZMALLOC_H */\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/assets/default.conf",
    "content": "# Redis configuration for testing.\n\nalways-show-logo yes\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\nlatency-monitor-threshold 1\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": "Chapter02/redis-6.0.3/tests/cluster/cluster.tcl",
    "content": "# Cluster-specific test functions.\n#\n# Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This software 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# Get a specific node by ID by parsing the CLUSTER NODES output\n# of the instance Number 'instance_id'\nproc get_node_by_id {instance_id node_id} {\n    set nodes [get_cluster_nodes $instance_id]\n    foreach n $nodes {\n        if {[dict get $n id] eq $node_id} {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\n# Search the first node starting from ID $first that is not\n# already configured as a slave.\nproc cluster_find_available_slave {first} {\n    foreach_redis_id id {\n        if {$id < $first} continue\n        if {[instance_is_killed redis $id]} continue\n        set me [get_myself $id]\n        if {[dict get $me slaveof] eq {-}} {return $id}\n    }\n    fail \"No available slaves\"\n}\n\n# Add 'slaves' slaves to a cluster composed of 'masters' masters.\n# It assumes that masters are allocated sequentially from instance ID 0\n# to N-1.\nproc cluster_allocate_slaves {masters slaves} {\n    for {set j 0} {$j < $slaves} {incr j} {\n        set master_id [expr {$j % $masters}]\n        set slave_id [cluster_find_available_slave $masters]\n        set master_myself [get_myself $master_id]\n        R $slave_id cluster replicate [dict get $master_myself id]\n    }\n}\n\n# Create a cluster composed of the specified number of masters and slaves.\nproc create_cluster {masters slaves} {\n    cluster_allocate_slots $masters\n    if {$slaves} {\n        cluster_allocate_slaves $masters $slaves\n    }\n    assert_cluster_state ok\n}\n\n# Set the cluster node-timeout to all the reachalbe nodes.\nproc set_cluster_node_timeout {to} {\n    foreach_redis_id id {\n        catch {R $id CONFIG SET cluster-node-timeout $to}\n    }\n}\n\n# Check if the cluster is writable and readable. Use node \"id\"\n# as a starting point to talk with the cluster.\nproc cluster_write_test {id} {\n    set prefix [randstring 20 20 alpha]\n    set port [get_instance_attrib redis $id 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 $prefix.$j\n    }\n    for {set j 0} {$j < 100} {incr j} {\n        assert {[$cluster get key.$j] eq \"$prefix.$j\"}\n    }\n    $cluster close\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/run.tcl",
    "content": "# Cluster test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This software 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.\nset ::tlsdir \"../../tls\"\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    end_tests\n}\n\nif {[catch main e]} {\n    puts $::errorInfo\n    if {$::pause_on_error} pause_on_error\n    cleanup\n    exit 1\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"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 \"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    cluster_write_test 0\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/01-faildet.tcl",
    "content": "# Check the basic monitoring and failover capabilities.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster should start ok\" {\n    assert_cluster_state ok\n}\n\ntest \"Killing two slave nodes\" {\n    kill_instance redis 5\n    kill_instance redis 6\n}\n\ntest \"Cluster should be still up\" {\n    assert_cluster_state ok\n}\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\n# Note: the only slave of instance 0 is already down so no\n# failover is possible, that would change the state back to ok.\ntest \"Cluster should be down now\" {\n    assert_cluster_state fail\n}\n\ntest \"Restarting master node\" {\n    restart_instance redis 0\n}\n\ntest \"Cluster should be up again\" {\n    assert_cluster_state ok\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/02-failover.tcl",
    "content": "# Check the basic monitoring and failover capabilities.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance #5 is now a master\" {\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Restarting the previously killed master node\" {\n    restart_instance redis 0\n}\n\ntest \"Instance #0 gets converted into a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave}\n    } else {\n        fail \"Old master was not converted into slave\"\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/03-failover-loop.tcl",
    "content": "# Failover stress test.\n# In this test a different node is killed in a loop for N\n# iterations. The test checks that certain properties\n# are preseved across iterations.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\nset iterations 20\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\n\nwhile {[incr iterations -1]} {\n    set tokill [randomInt 10]\n    set other [expr {($tokill+1)%10}] ; # Some other instance.\n    set key [randstring 20 20 alpha]\n    set val [randstring 20 20 alpha]\n    set role [RI $tokill role]\n    if {$role eq {master}} {\n        set slave {}\n        set myid [dict get [get_myself $tokill] id]\n        foreach_redis_id id {\n            if {$id == $tokill} continue\n            if {[dict get [get_myself $id] slaveof] eq $myid} {\n                set slave $id\n            }\n        }\n        if {$slave eq {}} {\n            fail \"Unable to retrieve slave's ID for master #$tokill\"\n        }\n    }\n\n    puts \"--- Iteration $iterations ---\"\n\n    if {$role eq {master}} {\n        test \"Wait for slave of #$tokill to sync\" {\n            wait_for_condition 1000 50 {\n                [string match {*state=online*} [RI $tokill slave0]]\n            } else {\n                fail \"Slave of node #$tokill is not ok\"\n            }\n        }\n        set slave_config_epoch [CI $slave cluster_my_epoch]\n    }\n\n    test \"Cluster is writable before failover\" {\n        for {set i 0} {$i < 100} {incr i} {\n            catch {$cluster set $key:$i $val:$i} err\n            assert {$err eq {OK}}\n        }\n        # Wait for the write to propagate to the slave if we\n        # are going to kill a master.\n        if {$role eq {master}} {\n            R $tokill wait 1 20000\n        }\n    }\n\n    test \"Killing node #$tokill\" {\n        kill_instance redis $tokill\n    }\n\n    if {$role eq {master}} {\n        test \"Wait failover by #$slave with old epoch $slave_config_epoch\" {\n            wait_for_condition 1000 50 {\n                [CI $slave cluster_my_epoch] > $slave_config_epoch\n            } else {\n                fail \"No failover detected, epoch is still [CI $slave cluster_my_epoch]\"\n            }\n        }\n    }\n\n    test \"Cluster should eventually be up again\" {\n        assert_cluster_state ok\n    }\n\n    test \"Cluster is writable again\" {\n        for {set i 0} {$i < 100} {incr i} {\n            catch {$cluster set $key:$i:2 $val:$i:2} err\n            assert {$err eq {OK}}\n        }\n    }\n\n    test \"Restarting node #$tokill\" {\n        restart_instance redis $tokill\n    }\n\n    test \"Instance #$tokill is now a slave\" {\n        wait_for_condition 1000 50 {\n            [RI $tokill role] eq {slave}\n        } else {\n            fail \"Restarted instance is not a slave\"\n        }\n    }\n\n    test \"We can read back the value we set before\" {\n        for {set i 0} {$i < 100} {incr i} {\n            catch {$cluster get $key:$i} err\n            assert {$err eq \"$val:$i\"}\n            catch {$cluster get $key:$i:2} err\n            assert {$err eq \"$val:$i:2\"}\n        }\n    }\n}\n\ntest \"Post condition: current_epoch >= my_epoch everywhere\" {\n    foreach_redis_id id {\n        assert {[CI $id cluster_current_epoch] >= [CI $id cluster_my_epoch]}\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/04-resharding.tcl",
    "content": "# Failover stress test.\n# In this test a different node is killed in a loop for N\n# iterations. The test checks that certain properties\n# are preseved across iterations.\n\nsource \"../tests/includes/init-tests.tcl\"\nsource \"../../../tests/support/cli.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Enable AOF in all the instances\" {\n    foreach_redis_id id {\n        R $id config set appendonly yes\n        # We use \"appendfsync no\" because it's fast but also guarantees that\n        # write(2) is performed before replying to client.\n        R $id config set appendfsync no\n    }\n\n    foreach_redis_id id {\n        wait_for_condition 1000 500 {\n            [RI $id aof_rewrite_in_progress] == 0 &&\n            [RI $id aof_enabled] == 1\n        } else {\n            fail \"Failed to enable AOF on instance #$id\"\n        }\n    }\n}\n\n# Return nno-zero if the specified PID is about a process still in execution,\n# otherwise 0 is returned.\nproc process_is_running {pid} {\n    # PS should return with an error if PID is non existing,\n    # and catch will return non-zero. We want to return non-zero if\n    # the PID exists, so we invert the return value with expr not operator.\n    expr {![catch {exec ps -p $pid}]}\n}\n\n# Our resharding test performs the following actions:\n#\n# - N commands are sent to the cluster in the course of the test.\n# - Every command selects a random key from key:0 to key:MAX-1.\n# - The operation RPUSH key <randomvalue> is perforemd.\n# - Tcl remembers into an array all the values pushed to each list.\n# - After N/2 commands, the resharding process is started in background.\n# - The test continues while the resharding is in progress.\n# - At the end of the test, we wait for the resharding process to stop.\n# - Finally the keys are checked to see if they contain the value they should.\n\nset numkeys 50000\nset numops 200000\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\ncatch {unset content}\narray set content {}\nset tribpid {}\n\ntest \"Cluster consistency during live resharding\" {\n    set ele 0\n    for {set j 0} {$j < $numops} {incr j} {\n        # Trigger the resharding once we execute half the ops.\n        if {$tribpid ne {} &&\n            ($j % 10000) == 0 &&\n            ![process_is_running $tribpid]} {\n            set tribpid {}\n        }\n\n        if {$j >= $numops/2 && $tribpid eq {}} {\n            puts -nonewline \"...Starting resharding...\"\n            flush stdout\n            set target [dict get [get_myself [randomInt 5]] id]\n            set tribpid [lindex [exec \\\n                ../../../src/redis-cli --cluster reshard \\\n                127.0.0.1:[get_instance_attrib redis 0 port] \\\n                --cluster-from all \\\n                --cluster-to $target \\\n                --cluster-slots 100 \\\n                --cluster-yes \\\n                {*}[rediscli_tls_config \"../../../tests\"] \\\n                | [info nameofexecutable] \\\n                ../tests/helpers/onlydots.tcl \\\n                &] 0]\n        }\n\n        # Write random data to random list.\n        set listid [randomInt $numkeys]\n        set key \"key:$listid\"\n        incr ele\n        # We write both with Lua scripts and with plain commands.\n        # This way we are able to stress Lua -> Redis command invocation\n        # as well, that has tests to prevent Lua to write into wrong\n        # hash slots.\n        if {$listid % 2} {\n            $cluster rpush $key $ele\n        } else {\n            $cluster eval {redis.call(\"rpush\",KEYS[1],ARGV[1])} 1 $key $ele\n        }\n        lappend content($key) $ele\n\n        if {($j % 1000) == 0} {\n            puts -nonewline W; flush stdout\n        }\n    }\n\n    # Wait for the resharding process to end\n    wait_for_condition 1000 500 {\n        [process_is_running $tribpid] == 0\n    } else {\n        fail \"Resharding is not terminating after some time.\"\n    }\n\n}\n\ntest \"Verify $numkeys keys for consistency with logical content\" {\n    # Check that the Redis Cluster content matches our logical content.\n    foreach {key value} [array get content] {\n        if {[$cluster lrange $key 0 -1] ne $value} {\n            fail \"Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]\"\n        }\n    }\n}\n\ntest \"Crash and restart all the instances\" {\n    foreach_redis_id id {\n        kill_instance redis $id\n        restart_instance redis $id\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Verify $numkeys keys after the crash & restart\" {\n    # Check that the Redis Cluster content matches our logical content.\n    foreach {key value} [array get content] {\n        if {[$cluster lrange $key 0 -1] ne $value} {\n            fail \"Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]\"\n        }\n    }\n}\n\ntest \"Disable AOF in all the instances\" {\n    foreach_redis_id id {\n        R $id config set appendonly no\n    }\n}\n\ntest \"Verify slaves consistency\" {\n    set verified_masters 0\n    foreach_redis_id id {\n        set role [R $id role]\n        lassign $role myrole myoffset slaves\n        if {$myrole eq {slave}} continue\n        set masterport [get_instance_attrib redis $id port]\n        set masterdigest [R $id debug digest]\n        foreach_redis_id sid {\n            set srole [R $sid role]\n            if {[lindex $srole 0] eq {master}} continue\n            if {[lindex $srole 2] != $masterport} continue\n            wait_for_condition 1000 500 {\n                [R $sid debug digest] eq $masterdigest\n            } else {\n                fail \"Master and slave data digest are different\"\n            }\n            incr verified_masters\n        }\n    }\n    assert {$verified_masters >= 5}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/05-slave-selection.tcl",
    "content": "# Slave selection test\n# Check the algorithm trying to pick the slave with the most complete history.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 10 slaves, so that we have 2\n# slaves for each master.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 10\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"The first master has actually two slaves\" {\n    assert {[llength [lindex [R 0 role] 2]] == 2}\n}\n\ntest {Slaves of #0 are instance #5 and #10 as expected} {\n    set port0 [get_instance_attrib redis 0 port]\n    assert {[lindex [R 5 role] 2] == $port0}\n    assert {[lindex [R 10 role] 2] == $port0}\n}\n\ntest \"Instance #5 and #10 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up} &&\n        [RI 10 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 or #10 master link status is not up\"\n    }\n}\n\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\n\ntest \"Slaves are both able to receive and acknowledge writes\" {\n    for {set j 0} {$j < 100} {incr j} {\n        $cluster set $j $j\n    }\n    assert {[R 0 wait 2 60000] == 2}\n}\n\ntest \"Write data while slave #10 is paused and can't receive it\" {\n    # Stop the slave with a multi/exec transaction so that the master will\n    # be killed as soon as it can accept writes again.\n    R 10 multi\n    R 10 debug sleep 10\n    R 10 client kill 127.0.0.1:$port0\n    R 10 deferred 1\n    R 10 exec\n\n    # Write some data the slave can't receive.\n    for {set j 0} {$j < 100} {incr j} {\n        $cluster set $j $j\n    }\n\n    # Prevent the master from accepting new slaves.\n    # Use a large pause value since we'll kill it anyway.\n    R 0 CLIENT PAUSE 60000\n\n    # Wait for the slave to return available again\n    R 10 deferred 0\n    assert {[R 10 read] eq {OK OK}}\n\n    # Kill the master so that a reconnection will not be possible.\n    kill_instance redis 0\n}\n\ntest \"Wait for instance #5 (and not #10) to turn into a master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 role] eq {master}\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Wait for the node #10 to return alive before ending the test\" {\n    R 10 ping\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Node #10 should eventually replicate node #5\" {\n    set port5 [get_instance_attrib redis 5 port]\n    wait_for_condition 1000 50 {\n        ([lindex [R 10 role] 2] == $port5) &&\n        ([lindex [R 10 role] 3] eq {connected})\n    } else {\n        fail \"#10 didn't became slave of #5\"\n    }\n}\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 3 master and 15 slaves, so that we have 5\n# slaves for eatch master.\ntest \"Create a 3 nodes cluster\" {\n    create_cluster 3 15\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"The first master has actually 5 slaves\" {\n    assert {[llength [lindex [R 0 role] 2]] == 5}\n}\n\ntest {Slaves of #0 are instance #3, #6, #9, #12 and #15 as expected} {\n    set port0 [get_instance_attrib redis 0 port]\n    assert {[lindex [R 3 role] 2] == $port0}\n    assert {[lindex [R 6 role] 2] == $port0}\n    assert {[lindex [R 9 role] 2] == $port0}\n    assert {[lindex [R 12 role] 2] == $port0}\n    assert {[lindex [R 15 role] 2] == $port0}\n}\n\ntest {Instance #3, #6, #9, #12 and #15 synced with the master} {\n    wait_for_condition 1000 50 {\n        [RI 3 master_link_status] eq {up} &&\n        [RI 6 master_link_status] eq {up} &&\n        [RI 9 master_link_status] eq {up} &&\n        [RI 12 master_link_status] eq {up} &&\n        [RI 15 master_link_status] eq {up}\n    } else {\n        fail \"Instance #3 or #6 or #9 or #12 or #15 master link status is not up\"\n    }\n}\n\nproc master_detected {instances} {\n    foreach instance [dict keys $instances] {\n        if {[RI $instance role] eq {master}} {\n            return true\n        }\n    }\n\n    return false\n}\n\ntest \"New Master down consecutively\" {\n    set instances [dict create 0 1 3 1 6 1 9 1 12 1 15 1]\n\n    set loops [expr {[dict size $instances]-1}]\n    for {set i 0} {$i < $loops} {incr i} {\n        set master_id -1\n        foreach instance [dict keys $instances] {\n            if {[RI $instance role] eq {master}} {\n                set master_id $instance\n                break;\n            }\n        }\n\n        if {$master_id eq -1} {\n            fail \"no master detected, #loop $i\"\n        }\n\n        set instances [dict remove $instances $master_id]\n\n        kill_instance redis $master_id\n        wait_for_condition 1000 50 {\n            [master_detected $instances]\n        } else {\n            failover \"No failover detected when master $master_id fails\"\n        }\n\n        assert_cluster_state ok\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/06-slave-stop-cond.tcl",
    "content": "# Slave stop condition test\n# Check that if there is a disconnection time limit, the slave will not try\n# to failover its master.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 5 slaves.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"The first master has actually one slave\" {\n    assert {[llength [lindex [R 0 role] 2]] == 1}\n}\n\ntest {Slaves of #0 is instance #5 as expected} {\n    set port0 [get_instance_attrib redis 0 port]\n    assert {[lindex [R 5 role] 2] == $port0}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"Lower the slave validity factor of #5 to the value of 2\" {\n    assert {[R 5 config set cluster-slave-validity-factor 2] eq {OK}}\n}\n\ntest \"Break master-slave link and prevent further reconnections\" {\n    # Stop the slave with a multi/exec transaction so that the master will\n    # be killed as soon as it can accept writes again.\n    R 5 multi\n    R 5 client kill 127.0.0.1:$port0\n    # here we should sleep 6 or more seconds (node_timeout * slave_validity)\n    # but the actual validity time is actually incremented by the\n    # repl-ping-slave-period value which is 10 seconds by default. So we\n    # need to wait more than 16 seconds.\n    R 5 debug sleep 20\n    R 5 deferred 1\n    R 5 exec\n\n    # Prevent the master from accepting new slaves.\n    # Use a large pause value since we'll kill it anyway.\n    R 0 CLIENT PAUSE 60000\n\n    # Wait for the slave to return available again\n    R 5 deferred 0\n    assert {[R 5 read] eq {OK OK}}\n\n    # Kill the master so that a reconnection will not be possible.\n    kill_instance redis 0\n}\n\ntest \"Slave #5 is reachable and alive\" {\n    assert {[R 5 ping] eq {PONG}}\n}\n\ntest \"Slave #5 should not be able to failover\" {\n    after 10000\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Cluster should be down\" {\n    assert_cluster_state fail\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/07-replica-migration.tcl",
    "content": "# Replica migration test.\n# Check that orphaned masters are joined by replicas of masters having\n# multiple replicas attached, according to the migration barrier settings.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 10 slaves, so that we have 2\n# slaves for each master.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 10\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Each master should have two replicas attached\" {\n    foreach_redis_id id {\n        if {$id < 5} {\n            wait_for_condition 1000 50 {\n                [llength [lindex [R 0 role] 2]] == 2\n            } else {\n                fail \"Master #$id does not have 2 slaves as expected\"\n            }\n        }\n    }\n}\n\ntest \"Killing all the slaves of master #0 and #1\" {\n    kill_instance redis 5\n    kill_instance redis 10\n    kill_instance redis 6\n    kill_instance redis 11\n    after 4000\n}\n\nforeach_redis_id id {\n    if {$id < 5} {\n        test \"Master #$id should have at least one replica\" {\n            wait_for_condition 1000 50 {\n                [llength [lindex [R $id role] 2]] >= 1\n            } else {\n                fail \"Master #$id has no replicas\"\n            }\n        }\n    }\n}\n\n# Now test the migration to a master which used to be a slave, after\n# a failver.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 10 slaves, so that we have 2\n# slaves for each master.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 10\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Kill slave #7 of master #2. Only slave left is #12 now\" {\n    kill_instance redis 7\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing master node #2, #12 should failover\" {\n    kill_instance redis 2\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance 12 is now a master without slaves\" {\n    assert {[RI 12 role] eq {master}}\n}\n\n# The remaining instance is now without slaves. Some other slave\n# should migrate to it.\n\ntest \"Master #12 should get at least one migrated replica\" {\n    wait_for_condition 1000 50 {\n        [llength [lindex [R 12 role] 2]] >= 1\n    } else {\n        fail \"Master #12 has no replicas\"\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/08-update-msg.tcl",
    "content": "# Test UPDATE messages sent by other nodes when the currently authorirative\n# master is unavaialble. The test is performed in the following steps:\n#\n# 1) Master goes down.\n# 2) Slave failover and becomes new master.\n# 3) New master is partitoned away.\n# 4) Old master returns.\n# 5) At this point we expect the old master to turn into a slave ASAP because\n#    of the UPDATE messages it will receive from the other nodes when its\n#    configuration will be found to be outdated.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance #5 is now a master\" {\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Killing the new master #5\" {\n    kill_instance redis 5\n}\n\ntest \"Cluster should be down now\" {\n    assert_cluster_state fail\n}\n\ntest \"Restarting the old master node\" {\n    restart_instance redis 0\n}\n\ntest \"Instance #0 gets converted into a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave}\n    } else {\n        fail \"Old master was not converted into slave\"\n    }\n}\n\ntest \"Restarting the new master node\" {\n    restart_instance redis 5\n}\n\ntest \"Cluster is up again\" {\n    assert_cluster_state ok\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/09-pubsub.tcl",
    "content": "# Test PUBLISH propagation across the cluster.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\nproc test_cluster_publish {instance instances} {\n    # Subscribe all the instances but the one we use to send.\n    for {set j 0} {$j < $instances} {incr j} {\n        if {$j != $instance} {\n            R $j deferred 1\n            R $j subscribe testchannel\n            R $j read; # Read the subscribe reply\n        }\n    }\n\n    set data [randomValue]\n    R $instance PUBLISH testchannel $data\n\n    # Read the message back from all the nodes.\n    for {set j 0} {$j < $instances} {incr j} {\n        if {$j != $instance} {\n            set msg [R $j read]\n            assert {$data eq [lindex $msg 2]}\n            R $j unsubscribe testchannel\n            R $j read; # Read the unsubscribe reply\n            R $j deferred 0\n        }\n    }\n}\n\ntest \"Test publishing to master\" {\n    test_cluster_publish 0 10\n}\n\ntest \"Test publishing to slave\" {\n    test_cluster_publish 5 10\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/10-manual-failover.tcl",
    "content": "# Check the manual failover\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\nset numkeys 50000\nset numops 10000\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\ncatch {unset content}\narray set content {}\n\ntest \"Send CLUSTER FAILOVER to #5, during load\" {\n    for {set j 0} {$j < $numops} {incr j} {\n        # Write random data to random list.\n        set listid [randomInt $numkeys]\n        set key \"key:$listid\"\n        set ele [randomValue]\n        # We write both with Lua scripts and with plain commands.\n        # This way we are able to stress Lua -> Redis command invocation\n        # as well, that has tests to prevent Lua to write into wrong\n        # hash slots.\n        if {$listid % 2} {\n            $cluster rpush $key $ele\n        } else {\n           $cluster eval {redis.call(\"rpush\",KEYS[1],ARGV[1])} 1 $key $ele\n        }\n        lappend content($key) $ele\n\n        if {($j % 1000) == 0} {\n            puts -nonewline W; flush stdout\n        }\n\n        if {$j == $numops/2} {R 5 cluster failover}\n    }\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance #5 is now a master\" {\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Verify $numkeys keys for consistency with logical content\" {\n    # Check that the Redis Cluster content matches our logical content.\n    foreach {key value} [array get content] {\n        assert {[$cluster lrange $key 0 -1] eq $value}\n    }\n}\n\ntest \"Instance #0 gets converted into a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave}\n    } else {\n        fail \"Old master was not converted into slave\"\n    }\n}\n\n## Check that manual failover does not happen if we can't talk with the master.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"Make instance #0 unreachable without killing it\" {\n    R 0 deferred 1\n    R 0 DEBUG SLEEP 10\n}\n\ntest \"Send CLUSTER FAILOVER to instance #5\" {\n    R 5 cluster failover\n}\n\ntest \"Instance #5 is still a slave after some time (no failover)\" {\n    after 5000\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Wait for instance #0 to return back alive\" {\n    R 0 deferred 0\n    assert {[R 0 read] eq {OK}}\n}\n\n## Check with \"force\" failover happens anyway.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"Make instance #0 unreachable without killing it\" {\n    R 0 deferred 1\n    R 0 DEBUG SLEEP 10\n}\n\ntest \"Send CLUSTER FAILOVER to instance #5\" {\n    R 5 cluster failover force\n}\n\ntest \"Instance #5 is a master after some time\" {\n    wait_for_condition 1000 50 {\n        [RI 5 role] eq {master}\n    } else {\n        fail \"Instance #5 is not a master after some time regardless of FORCE\"\n    }\n}\n\ntest \"Wait for instance #0 to return back alive\" {\n    R 0 deferred 0\n    assert {[R 0 read] eq {OK}}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/11-manual-takeover.tcl",
    "content": "# Manual takeover test\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Killing majority of master nodes\" {\n    kill_instance redis 0\n    kill_instance redis 1\n    kill_instance redis 2\n}\n\ntest \"Cluster should eventually be down\" {\n    assert_cluster_state fail\n}\n\ntest \"Use takeover to bring slaves back\" {\n    R 5 cluster failover takeover\n    R 6 cluster failover takeover\n    R 7 cluster failover takeover\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 4\n}\n\ntest \"Instance #5, #6, #7 are now masters\" {\n    assert {[RI 5 role] eq {master}}\n    assert {[RI 6 role] eq {master}}\n    assert {[RI 7 role] eq {master}}\n}\n\ntest \"Restarting the previously killed master nodes\" {\n    restart_instance redis 0\n    restart_instance redis 1\n    restart_instance redis 2\n}\n\ntest \"Instance #0, #1, #2 gets converted into a slaves\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave} && [RI 1 role] eq {slave} && [RI 2 role] eq {slave}\n    } else {\n        fail \"Old masters not converted into slaves\"\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/12-replica-migration-2.tcl",
    "content": "# Replica migration test #2.\n#\n# Check that the status of master that can be targeted by replica migration\n# is acquired again, after being getting slots again, in a cluster where the\n# other masters have slaves.\n\nsource \"../tests/includes/init-tests.tcl\"\nsource \"../../../tests/support/cli.tcl\"\n\n# Create a cluster with 5 master and 15 slaves, to make sure there are no\n# empty masters and make rebalancing simpler to handle during the test.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 15\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Each master should have at least two replicas attached\" {\n    foreach_redis_id id {\n        if {$id < 5} {\n            wait_for_condition 1000 50 {\n                [llength [lindex [R 0 role] 2]] >= 2\n            } else {\n                fail \"Master #$id does not have 2 slaves as expected\"\n            }\n        }\n    }\n}\n\nset master0_id [dict get [get_myself 0] id]\ntest \"Resharding all the master #0 slots away from it\" {\n    set output [exec \\\n        ../../../src/redis-cli --cluster rebalance \\\n        127.0.0.1:[get_instance_attrib redis 0 port] \\\n        {*}[rediscli_tls_config \"../../../tests\"] \\\n        --cluster-weight ${master0_id}=0 >@ stdout ]\n\n}\n\ntest \"Master #0 should lose its replicas\" {\n    wait_for_condition 1000 50 {\n        [llength [lindex [R 0 role] 2]] == 0\n    } else {\n        fail \"Master #0 still has replicas\"\n    }\n}\n\ntest \"Resharding back some slot to master #0\" {\n    # Wait for the cluster config to propagate before attempting a\n    # new resharding.\n    after 10000\n    set output [exec \\\n        ../../../src/redis-cli --cluster rebalance \\\n        127.0.0.1:[get_instance_attrib redis 0 port] \\\n        {*}[rediscli_tls_config \"../../../tests\"] \\\n        --cluster-weight ${master0_id}=.01 \\\n        --cluster-use-empty-masters  >@ stdout]\n}\n\ntest \"Master #0 should re-acquire one or more replicas\" {\n    wait_for_condition 1000 50 {\n        [llength [lindex [R 0 role] 2]] >= 1\n    } else {\n        fail \"Master #0 has no has replicas\"\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/13-no-failover-option.tcl",
    "content": "# Check that the no-failover option works\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n\n    # Configure it to never failover the master\n    R 5 CONFIG SET cluster-slave-no-failover yes\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"The nofailover flag is propagated\" {\n    set slave5_id [dict get [get_myself 5] id]\n\n    foreach_redis_id id {\n        wait_for_condition 1000 50 {\n            [has_flag [get_node_by_id $id $slave5_id] nofailover]\n        } else {\n            fail \"Instance $id can't see the nofailover flag of slave\"\n        }\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\ntest \"Cluster should be still down after some time\" {\n    after 10000\n    assert_cluster_state fail\n}\n\ntest \"Instance #5 is still a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Restarting the previously killed master node\" {\n    restart_instance redis 0\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/14-consistency-check.tcl",
    "content": "source \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster should start ok\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\nproc find_non_empty_master {} {\n    set master_id_no {}\n    foreach_redis_id id {\n        if {[RI $id role] eq {master} && [R $id dbsize] > 0} {\n            set master_id_no $id\n        }\n    }\n    return $master_id_no\n}\n\nproc get_one_of_my_replica {id} {\n    set replica_port [lindex [lindex [lindex [R $id role] 2] 0] 1]\n    set replica_id_num [get_instance_id_by_port redis $replica_port]\n    return $replica_id_num\n}\n\nproc cluster_write_keys_with_expire {id ttl} {\n    set prefix [randstring 20 20 alpha]\n    set port [get_instance_attrib redis $id port]\n    set cluster [redis_cluster 127.0.0.1:$port]\n    for {set j 100} {$j < 200} {incr j} {\n        $cluster setex key_expire.$j $ttl $prefix.$j\n    }\n    $cluster close\n}\n\nproc test_slave_load_expired_keys {aof} {\n    test \"Slave expired keys is loaded when restarted: appendonly=$aof\" {\n        set master_id [find_non_empty_master]\n        set replica_id [get_one_of_my_replica $master_id]\n\n        set master_dbsize [R $master_id dbsize]\n        set slave_dbsize [R $replica_id dbsize]\n        assert_equal $master_dbsize $slave_dbsize\n\n        set data_ttl 5\n        cluster_write_keys_with_expire $master_id $data_ttl\n        after 100\n        set replica_dbsize_1 [R $replica_id dbsize]\n        assert {$replica_dbsize_1  > $slave_dbsize}\n\n        R $replica_id config set appendonly $aof\n        R $replica_id config rewrite\n\n        set start_time [clock seconds]\n        set end_time [expr $start_time+$data_ttl+2]\n        R $replica_id save\n        set replica_dbsize_2 [R $replica_id dbsize]\n        assert {$replica_dbsize_2  > $slave_dbsize}\n        kill_instance redis $replica_id\n\n        set master_port [get_instance_attrib redis $master_id port]\n        exec ../../../src/redis-cli -h 127.0.0.1 -p $master_port debug sleep [expr $data_ttl+3] > /dev/null &\n\n        while {[clock seconds] <= $end_time} {\n            #wait for $data_ttl seconds\n        }\n        restart_instance redis $replica_id\n\n        wait_for_condition 200 50 {\n            [R $replica_id ping] eq {PONG}\n        } else {\n            fail \"replica #$replica_id not started\"\n        }\n\n        set replica_dbsize_3 [R $replica_id dbsize]\n        assert {$replica_dbsize_3  > $slave_dbsize}\n    }\n}\n\ntest_slave_load_expired_keys no\nafter 5000\ntest_slave_load_expired_keys yes\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/15-cluster-slots.tcl",
    "content": "source \"../tests/includes/init-tests.tcl\"\n\nproc cluster_allocate_mixedSlots {n} {\n    set slot 16383\n    while {$slot >= 0} {\n        set node [expr {$slot % $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\nproc create_cluster_with_mixedSlot {masters slaves} {\n    cluster_allocate_mixedSlots $masters\n    if {$slaves} {\n        cluster_allocate_slaves $masters $slaves\n    }\n    assert_cluster_state ok\n}\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster_with_mixedSlot 5 15\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"client do not break when cluster slot\" {\n    R 0 config set client-output-buffer-limit \"normal 33554432 16777216 60\"\n    if { [catch {R 0 cluster slots}] } {\n        fail \"output overflow when cluster slots\"\n    }\n}"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tests/helpers/onlydots.tcl",
    "content": "# Read the standard input and only shows dots in the output, filtering out\n# all the other characters. Designed to avoid bufferization so that when\n# we get the output of redis-trib and want to show just the dots, we'll see\n# the dots as soon as redis-trib will output them.\n\nfconfigure stdin -buffering none\n\nwhile 1 {\n    set c [read stdin 1]\n    if {$c eq {}} {\n        exit 0; # EOF\n    } elseif {$c eq {.}} {\n        puts -nonewline .\n        flush stdout\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\ntest \"Cluster nodes are reachable\" {\n    foreach_redis_id id {\n        # Every node should be reachable.\n        wait_for_condition 1000 50 {\n            ([catch {R $id ping} ping_reply] == 0) &&\n            ($ping_reply eq {PONG})\n        } else {\n            catch {R $id ping} err\n            fail \"Node #$id keeps replying '$err' to PING.\"\n        }\n    }\n}\n\ntest \"Cluster nodes hard reset\" {\n    foreach_redis_id id {\n        if {$::valgrind} {\n            set node_timeout 10000\n        } else {\n            set node_timeout 3000\n        }\n        catch {R $id flushall} ; # May fail for readonly slaves.\n        R $id MULTI\n        R $id cluster reset hard\n        R $id cluster set-config-epoch [expr {$id+1}]\n        R $id EXEC\n        R $id config set cluster-node-timeout $node_timeout\n        R $id config set cluster-slave-validity-factor 10\n        R $id config rewrite\n    }\n}\n\ntest \"Cluster Join and auto-discovery test\" {\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"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/cluster/tmp/.gitignore",
    "content": "redis_*\nsentinel_*\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/helpers/bg_block_op.tcl",
    "content": "source tests/support/redis.tcl\nsource tests/support/util.tcl\n\nset ::tlsdir \"tests/tls\"\n\n# This function sometimes writes sometimes blocking-reads from lists/sorted\n# sets. There are multiple processes like this executing at the same time\n# so that we have some chance to trap some corner condition if there is\n# a regression. For this to happen it is important that we narrow the key\n# space to just a few elements, and balance the operations so that it is\n# unlikely that lists and zsets just get more data without ever causing\n# blocking.\nproc bg_block_op {host port db ops tls} {\n    set r [redis $host $port 0 $tls]\n    $r select $db\n\n    for {set j 0} {$j < $ops} {incr j} {\n\n        # List side\n        set k list_[randomInt 10]\n        set k2 list_[randomInt 10]\n        set v [randomValue]\n\n        randpath {\n            randpath {\n                $r rpush $k $v\n            } {\n                $r lpush $k $v\n            }\n        } {\n            $r blpop $k 2\n        } {\n            $r blpop $k $k2 2\n        }\n\n        # Zset side\n        set k zset_[randomInt 10]\n        set k2 zset_[randomInt 10]\n        set v1 [randomValue]\n        set v2 [randomValue]\n\n        randpath {\n            $r zadd $k [randomInt 10000] $v\n        } {\n            $r zadd $k [randomInt 10000] $v [randomInt 10000] $v2\n        } {\n            $r bzpopmin $k 2\n        } {\n            $r bzpopmax $k 2\n        }\n    }\n}\n\nbg_block_op [lindex $argv 0] [lindex $argv 1] [lindex $argv 2] [lindex $argv 3] [lindex $argv 4]\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/helpers/bg_complex_data.tcl",
    "content": "source tests/support/redis.tcl\nsource tests/support/util.tcl\n\nset ::tlsdir \"tests/tls\"\n\nproc bg_complex_data {host port db ops tls} {\n    set r [redis $host $port 0 $tls]\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] [lindex $argv 4]\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/helpers/gen_write_load.tcl",
    "content": "source tests/support/redis.tcl\n\nset ::tlsdir \"tests/tls\"\n\nproc gen_write_load {host port seconds tls} {\n    set start_time [clock seconds]\n    set r [redis $host $port 1 $tls]\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] [lindex $argv 3]\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 software 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 ::valgrind 0\nset ::tls 0\nset ::pause_on_error 0\nset ::simulate_error 0\nset ::failed 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# Execute the specified instance of the server specified by 'type', using\n# the provided configuration file. Returns the PID of the process.\nproc exec_instance {type cfgfile} {\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\n    if {$::valgrind} {\n        set pid [exec valgrind --track-origins=yes --suppressions=../../../src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full ../../../src/${prgname} $cfgfile &]\n    } else {\n        set pid [exec ../../../src/${prgname} $cfgfile &]\n    }\n    return $pid\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        if {$::tls} {\n            puts $cfg \"tls-port $port\"\n            puts $cfg \"tls-replication yes\"\n            puts $cfg \"tls-cluster yes\"\n            puts $cfg \"port 0\"\n            puts $cfg [format \"tls-cert-file %s/../../tls/redis.crt\" [pwd]]\n            puts $cfg [format \"tls-key-file %s/../../tls/redis.key\" [pwd]]\n            puts $cfg [format \"tls-dh-params-file %s/../../tls/redis.dh\" [pwd]]\n            puts $cfg [format \"tls-ca-cert-file %s/../../tls/ca.crt\" [pwd]]\n            puts $cfg \"loglevel debug\"\n        } else {\n            puts $cfg \"port $port\"\n        }\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        set pid [exec_instance $type $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        set link [redis 127.0.0.1 $port 0 $::tls]\n        $link reconnect 1\n        lappend ::${type}_instances [list \\\n            pid $pid \\\n            host 127.0.0.1 \\\n            port $port \\\n            link $link \\\n        ]\n    }\n}\n\nproc log_crashes {} {\n    set start_pattern {*REDIS BUG REPORT START*}\n    set logs [glob */log.txt]\n    foreach log $logs {\n        set fd [open $log]\n        set found 0\n        while {[gets $fd line] >= 0} {\n            if {[string match $start_pattern $line]} {\n                puts \"\\n*** Crash report found in $log ***\"\n                set found 1\n            }\n            if {$found} {puts $line}\n        }\n    }\n}\n\nproc cleanup {} {\n    puts \"Cleaning up...\"\n    log_crashes\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    incr ::failed\n    puts \"WARNING: Aborting the test.\"\n    puts \">>>>>>>> $msg\"\n    if {$::pause_on_error} pause_on_error\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 {--valgrind}} {\n            set ::valgrind 1\n        } elseif {$opt eq {--tls}} {\n            package require tls 1.6\n            ::tls::init \\\n                -cafile \"$::tlsdir/ca.crt\" \\\n                -certfile \"$::tlsdir/redis.crt\" \\\n                -keyfile \"$::tlsdir/redis.key\"\n            set ::tls 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 \"--valgrind              Run with valgrind.\"\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-redis-logs}} {\n            set count 10\n            if {[lindex $argv 1] ne {}} {set count [lindex $argv 1]}\n            foreach_redis_id id {\n                puts \"=== REDIS $id ====\"\n                puts [exec tail -$count redis_$id/log.txt]\n                puts \"---------------------\\n\"\n            }\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 \"show-redis-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        incr ::failed\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\n# Check memory leaks when running on OSX using the \"leaks\" utility.\nproc check_leaks instance_types {\n    if {[string match {*Darwin*} [exec uname -a]]} {\n        puts -nonewline \"Testing for memory leaks...\"; flush stdout\n        foreach type $instance_types {\n            foreach_instance_id [set ::${type}_instances] id {\n                if {[instance_is_killed $type $id]} continue\n                set pid [get_instance_attrib $type $id 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                } else {\n                    puts -nonewline \"$type/$pid \"\n                    flush stdout\n                }\n                if {![string match {*0 leaks*} $output]} {\n                    puts [colorstr red \"=== MEMORY LEAK DETECTED ===\"]\n                    puts \"Instance type $type, ID $id:\"\n                    puts $output\n                    puts \"===\"\n                    incr ::failed\n                }\n            }\n        }\n        puts \"\"\n    }\n}\n\n# Execute all the units inside the 'tests' directory.\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        check_leaks {redis sentinel}\n    }\n}\n\n# Print a message and exists with 0 / 1 according to zero or more failures.\nproc end_tests {} {\n    if {$::failed == 0} {\n        puts \"GOOD! No errors.\"\n        exit 0\n    } else {\n        puts \"WARNING $::failed test(s) failed.\"\n        exit 1\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    set port [get_instance_attrib $type $id port]\n\n    if {$pid == -1} {\n        error \"You tried to kill $type $id twice.\"\n    }\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    # Wait for the port it was using to be available again, so that's not\n    # an issue to start a new server ASAP with the same port.\n    set retry 10\n    while {[incr retry -1]} {\n        set port_is_free [catch {set s [socket 127.0.01 $port]}]\n        if {$port_is_free} break\n        catch {close $s}\n        after 1000\n    }\n    if {$retry == 0} {\n        error \"Port $port does not return available after killing instance.\"\n    }\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    expr {$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    set pid [exec_instance $type $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 #$id: ping timeout\"\n    }\n\n    # Connect with it with a fresh link\n    set link [redis 127.0.0.1 $port 0 $::tls]\n    $link reconnect 1\n    set_instance_attrib $type $id link $link\n\n    # Make sure the instance is not loading the dataset when this\n    # function returns.\n    while 1 {\n        catch {[$link ping]} retval\n        if {[string match {*LOADING*} $retval]} {\n            after 100\n            continue\n        } else {\n            break\n        }\n    }\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/aof-race.tcl",
    "content": "set defaults { appendonly {yes} appendfilename {appendonly.aof} aof-use-rdb-preamble {no} }\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] 0 $::tls]\n        set bench [open \"|src/redis-benchmark -q -s [srv unixsocket] -c 20 -n 20000 incr foo\" \"r+\"]\n\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] 0 $::tls]\n        assert_equal 20000 [$client get foo]\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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    ## Server can start when aof-load-truncated is set to yes and AOF\n    ## is truncated, with an incomplete MULTI block.\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 aof-load-truncated yes] {\n        test \"Unfinished MULTI: Server should start if load-truncated is yes\" {\n            assert_equal 1 [is_alive $srv]\n        }\n    }\n\n    ## Should also start with truncated AOF without incomplete MULTI block.\n    create_aof {\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [string range [formatCommand incr foo] 0 end-1]\n    }\n\n    start_server_aof [list dir $server_path aof-load-truncated yes] {\n        test \"Short read: Server should start if load-truncated is yes\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]\n\n        wait_for_condition 50 100 {\n            [catch {$client ping} e] == 0\n        } else {\n            fail \"Loading DB is taking too much time.\"\n        }\n\n        test \"Truncated AOF loaded: we expect foo to be equal to 5\" {\n            assert {[$client get foo] eq \"5\"}\n        }\n\n        test \"Append a new command after loading an incomplete AOF\" {\n            $client incr foo\n        }\n    }\n\n    # Now the AOF file is expected to be correct\n    start_server_aof [list dir $server_path aof-load-truncated yes] {\n        test \"Short read + command: Server should start\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]\n\n        wait_for_condition 50 100 {\n            [catch {$client ping} e] == 0\n        } else {\n            fail \"Loading DB is taking too much time.\"\n        }\n\n        test \"Truncated AOF loaded: we expect foo to be equal to 6 now\" {\n            assert {[$client get foo] eq \"6\"}\n        }\n    }\n\n    ## Test that the server exits when the AOF contains a format error\n    create_aof {\n        append_to_aof [formatCommand set foo hello]\n        append_to_aof \"!!!\"\n        append_to_aof [formatCommand set foo hello]\n    }\n\n    start_server_aof [list dir $server_path aof-load-truncated yes] {\n        test \"Bad format: 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 -1 < [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 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 aof-load-truncated no] {\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 -1 < [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 aof-load-truncated no] {\n        test \"Short read: 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 -1 < [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 aof-load-truncated no] {\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 parseable\" {\n            set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\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 aof-load-truncated no] {\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] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\n            assert_equal 1 [$client scard set]\n        }\n    }\n\n    ## Uses the alsoPropagate() API.\n    create_aof {\n        append_to_aof [formatCommand sadd set foo]\n        append_to_aof [formatCommand sadd set bar]\n        append_to_aof [formatCommand sadd set gah]\n        append_to_aof [formatCommand spop set 2]\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] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\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 aof-load-truncated no] {\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] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\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    start_server {overrides {appendonly {yes} appendfilename {appendonly.aof} appendfsync always}} {\n        test {AOF fsync always barrier issue} {\n            set rd [redis_deferring_client]\n            # Set a sleep when aof is flushed, so that we have a chance to look\n            # at the aof size and detect if the response of an incr command\n            # arrives before the data was written (and hopefully fsynced)\n            # We create a big reply, which will hopefully not have room in the\n            # socket buffers, and will install a write handler, then we sleep\n            # a big and issue the incr command, hoping that the last portion of\n            # the output buffer write, and the processing of the incr will happen\n            # in the same event loop cycle.\n            # Since the socket buffers and timing are unpredictable, we fuzz this\n            # test with slightly different sizes and sleeps a few times.\n            for {set i 0} {$i < 10} {incr i} {\n                r debug aof-flush-sleep 0\n                r del x\n                r setrange x [expr {int(rand()*5000000)+10000000}] x\n                r debug aof-flush-sleep 500000\n                set aof [file join [lindex [r config get dir] 1] appendonly.aof]\n                set size1 [file size $aof]\n                $rd get x\n                after [expr {int(rand()*30)}]\n                $rd incr new_value\n                $rd read\n                $rd read\n                set size2 [file size $aof]\n                assert {$size1 != $size2}\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/block-repl.tcl",
    "content": "# Test replication of blocking lists and zset operations.\n# Unlike stream operations such operations are \"pop\" style, so they consume\n# the list or sorted set, and must be replicated correctly.\n\nproc start_bg_block_op {host port db ops tls} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/bg_block_op.tcl $host $port $db $ops $tls &\n}\n\nproc stop_bg_block_op {handle} {\n    catch {exec /bin/kill -9 $handle}\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        set load_handle0 [start_bg_block_op $master_host $master_port 9 100000 $::tls]\n        set load_handle1 [start_bg_block_op $master_host $master_port 9 100000 $::tls]\n        set load_handle2 [start_bg_block_op $master_host $master_port 9 100000 $::tls]\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 blocking lists and sorted sets operations} {\n            after 25000\n            stop_bg_block_op $load_handle0\n            stop_bg_block_op $load_handle1\n            stop_bg_block_op $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\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 - Replica 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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/tests/integration/logging.tcl",
    "content": "set server_path [tmpdir server.log]\nset system_name [string tolower [exec uname -s]]\n\nif {$system_name eq {linux} || $system_name eq {darwin}} {\n    start_server [list overrides [list dir $server_path]] {\n        test \"Server is able to generate a stack trace on selected systems\" {\n            r config set watchdog-period 200\n            r debug sleep 1\n            set pattern \"*debugCommand*\"\n            set retry 10\n            while {$retry} {\n                set result [exec tail -100 < [srv 0 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 stack trace not found into log file\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/psync2-pingoff.tcl",
    "content": "# Test the meaningful offset implementation to make sure masters\n# are able to PSYNC with replicas even if the replication stream\n# has pending PINGs at the end.\n\nstart_server {tags {\"psync2\"}} {\nstart_server {} {\n    # Config\n    set debug_msg 0                 ; # Enable additional debug messages\n\n    for {set j 0} {$j < 2} {incr j} {\n        set R($j) [srv [expr 0-$j] client]\n        set R_host($j) [srv [expr 0-$j] host]\n        set R_port($j) [srv [expr 0-$j] port]\n        $R($j) CONFIG SET repl-ping-replica-period 1\n        if {$debug_msg} {puts \"Log file: [srv [expr 0-$j] stdout]\"}\n    }\n\n    # Setup replication\n    test \"PSYNC2 meaningful offset: setup\" {\n        $R(1) replicaof $R_host(0) $R_port(0)\n        $R(0) set foo bar\n        wait_for_condition 50 1000 {\n            [status $R(1) master_link_status] == \"up\" &&\n            [$R(0) dbsize] == 1 && [$R(1) dbsize] == 1\n        } else {\n            fail \"Replicas not replicating from master\"\n        }\n    }\n\n    test \"PSYNC2 meaningful offset: write and wait replication\" {\n        $R(0) INCR counter\n        $R(0) INCR counter\n        $R(0) INCR counter\n        wait_for_condition 50 1000 {\n            [$R(0) GET counter] eq [$R(1) GET counter]\n        } else {\n            fail \"Master and replica don't agree about counter\"\n        }\n    }\n\n    # In this test we'll make sure the replica will get stuck, but with\n    # an active connection: this way the master will continue to send PINGs\n    # every second (we modified the PING period earlier)\n    test \"PSYNC2 meaningful offset: pause replica and promote it\" {\n        $R(1) MULTI\n        $R(1) DEBUG SLEEP 5\n        $R(1) SLAVEOF NO ONE\n        $R(1) EXEC\n        $R(1) ping ; # Wait for it to return back available\n    }\n\n    test \"Make the old master a replica of the new one and check conditions\" {\n        set sync_partial [status $R(1) sync_partial_ok]\n        assert {$sync_partial == 0}\n        $R(0) REPLICAOF $R_host(1) $R_port(1)\n        wait_for_condition 50 1000 {\n            [status $R(1) sync_partial_ok] == 1\n        } else {\n            fail \"The new master was not able to partial sync\"\n        }\n    }\n}}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/psync2-reg.tcl",
    "content": "# Issue 3899 regression test.\n# We create a chain of three instances: master -> slave -> slave2\n# and continuously break the link while traffic is generated by\n# redis-benchmark. At the end we check that the data is the same\n# everywhere.\n\nstart_server {tags {\"psync2\"}} {\nstart_server {} {\nstart_server {} {\n    # Config\n    set debug_msg 0                 ; # Enable additional debug messages\n\n    set no_exit 0                   ; # Do not exit at end of the test\n\n    set duration 20                 ; # Total test seconds\n\n    for {set j 0} {$j < 3} {incr j} {\n        set R($j) [srv [expr 0-$j] client]\n        set R_host($j) [srv [expr 0-$j] host]\n        set R_port($j) [srv [expr 0-$j] port]\n        set R_unixsocket($j) [srv [expr 0-$j] unixsocket]\n        if {$debug_msg} {puts \"Log file: [srv [expr 0-$j] stdout]\"}\n    }\n\n    # Setup the replication and backlog parameters\n    test \"PSYNC2 #3899 regression: setup\" {\n        $R(1) slaveof $R_host(0) $R_port(0)\n        $R(2) slaveof $R_host(0) $R_port(0)\n        $R(0) set foo bar\n        wait_for_condition 50 1000 {\n            [status $R(1) master_link_status] == \"up\" &&\n            [status $R(2) master_link_status] == \"up\" &&\n            [$R(1) dbsize] == 1 &&\n            [$R(2) dbsize] == 1\n        } else {\n            fail \"Replicas not replicating from master\"\n        }\n        $R(0) config set repl-backlog-size 10mb\n        $R(1) config set repl-backlog-size 10mb\n    }\n\n    set cycle_start_time [clock milliseconds]\n    set bench_pid [exec src/redis-benchmark -s $R_unixsocket(0) -n 10000000 -r 1000 incr __rand_int__ > /dev/null &]\n    while 1 {\n        set elapsed [expr {[clock milliseconds]-$cycle_start_time}]\n        if {$elapsed > $duration*1000} break\n        if {rand() < .05} {\n            test \"PSYNC2 #3899 regression: kill first replica\" {\n                $R(1) client kill type master\n            }\n        }\n        if {rand() < .05} {\n            test \"PSYNC2 #3899 regression: kill chained replica\" {\n                $R(2) client kill type master\n            }\n        }\n        after 100\n    }\n    exec kill -9 $bench_pid\n\n    if {$debug_msg} {\n        for {set j 0} {$j < 100} {incr j} {\n            if {\n                [$R(0) debug digest] == [$R(1) debug digest] &&\n                [$R(1) debug digest] == [$R(2) debug digest]\n            } break\n            puts [$R(0) debug digest]\n            puts [$R(1) debug digest]\n            puts [$R(2) debug digest]\n            after 1000\n        }\n    }\n\n    test \"PSYNC2 #3899 regression: verify consistency\" {\n        wait_for_condition 50 1000 {\n            ([$R(0) debug digest] eq [$R(1) debug digest]) &&\n            ([$R(1) debug digest] eq [$R(2) debug digest])\n        } else {\n            fail \"The three instances have different data sets\"\n        }\n    }\n}}}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/psync2.tcl",
    "content": "start_server {tags {\"psync2\"}} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\n    set master_id 0                 ; # Current master\n    set start_time [clock seconds]  ; # Test start time\n    set counter_value 0             ; # Current value of the Redis counter \"x\"\n\n    # Config\n    set debug_msg 0                 ; # Enable additional debug messages\n\n    set no_exit 0                   ; # Do not exit at end of the test\n\n    set duration 20                 ; # Total test seconds\n\n    set genload 1                   ; # Load master with writes at every cycle\n\n    set genload_time 5000           ; # Writes duration time in ms\n\n    set disconnect 1                ; # Break replication link between random\n                                      # master and slave instances while the\n                                      # master is loaded with writes.\n\n    set disconnect_period 1000      ; # Disconnect repl link every N ms.\n\n    for {set j 0} {$j < 5} {incr j} {\n        set R($j) [srv [expr 0-$j] client]\n        set R_host($j) [srv [expr 0-$j] host]\n        set R_port($j) [srv [expr 0-$j] port]\n        if {$debug_msg} {puts \"Log file: [srv [expr 0-$j] stdout]\"}\n    }\n\n    set cycle 1\n    while {([clock seconds]-$start_time) < $duration} {\n        test \"PSYNC2: --- CYCLE $cycle ---\" {}\n        incr cycle\n\n        # Create a random replication layout.\n        # Start with switching master (this simulates a failover).\n\n        # 1) Select the new master.\n        set master_id [randomInt 5]\n        set used [list $master_id]\n        test \"PSYNC2: \\[NEW LAYOUT\\] Set #$master_id as master\" {\n            $R($master_id) slaveof no one\n            $R($master_id) config set repl-ping-replica-period 1 ;# increse the chance that random ping will cause issues\n            if {$counter_value == 0} {\n                $R($master_id) set x $counter_value\n            }\n        }\n\n        # 2) Attach all the slaves to a random instance\n        while {[llength $used] != 5} {\n            while 1 {\n                set slave_id [randomInt 5]\n                if {[lsearch -exact $used $slave_id] == -1} break\n            }\n            set rand [randomInt [llength $used]]\n            set mid [lindex $used $rand]\n            set master_host $R_host($mid)\n            set master_port $R_port($mid)\n\n            test \"PSYNC2: Set #$slave_id to replicate from #$mid\" {\n                $R($slave_id) slaveof $master_host $master_port\n            }\n            lappend used $slave_id\n        }\n\n        # Wait for replicas to sync. so next loop won't get -LOADING error\n        wait_for_condition 50 1000 {\n            [status $R([expr {($master_id+1)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+2)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+3)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+4)%5}]) master_link_status] == \"up\"\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n\n        # 3) Increment the counter and wait for all the instances\n        # to converge.\n        test \"PSYNC2: cluster is consistent after failover\" {\n            $R($master_id) incr x; incr counter_value\n            for {set j 0} {$j < 5} {incr j} {\n                wait_for_condition 50 1000 {\n                    [$R($j) get x] == $counter_value\n                } else {\n                    fail \"Instance #$j x variable is inconsistent\"\n                }\n            }\n        }\n\n        # 4) Generate load while breaking the connection of random\n        # slave-master pairs.\n        test \"PSYNC2: generate load while killing replication links\" {\n            set t [clock milliseconds]\n            set next_break [expr {$t+$disconnect_period}]\n            while {[clock milliseconds]-$t < $genload_time} {\n                if {$genload} {\n                    $R($master_id) incr x; incr counter_value\n                }\n                if {[clock milliseconds] == $next_break} {\n                    set next_break \\\n                        [expr {[clock milliseconds]+$disconnect_period}]\n                    set slave_id [randomInt 5]\n                    if {$disconnect} {\n                        $R($slave_id) client kill type master\n                        if {$debug_msg} {\n                            puts \"+++ Breaking link for replica #$slave_id\"\n                        }\n                    }\n                }\n            }\n        }\n\n        # 5) Increment the counter and wait for all the instances\n        set x [$R($master_id) get x]\n        test \"PSYNC2: cluster is consistent after load (x = $x)\" {\n            for {set j 0} {$j < 5} {incr j} {\n                wait_for_condition 50 1000 {\n                    [$R($j) get x] == $counter_value\n                } else {\n                    fail \"Instance #$j x variable is inconsistent\"\n                }\n            }\n        }\n\n        # wait for all the slaves to be in sync with the master, due to pings, we have to re-sample the master constantly too\n        wait_for_condition 500 100 {\n            [status $R($master_id) master_repl_offset] == [status $R(0) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(1) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(2) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(3) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(4) master_repl_offset]\n        } else {\n            for {set j 0} {$j < 5} {incr j} {\n                puts \"$j: sync_full: [status $R($j) sync_full]\"\n                puts \"$j: id1      : [status $R($j) master_replid]:[status $R($j) master_repl_offset]\"\n                puts \"$j: id2      : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]\"\n                puts \"$j: backlog  : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]\"\n                puts \"---\"\n            }\n            fail \"Slaves are not in sync with the master after too long time.\"\n        }\n\n        # Put down the old master so that it cannot generate more\n        # replication stream, this way in the next master switch, the time at\n        # which we move slaves away is not important, each will have full\n        # history (otherwise PINGs will make certain slaves have more history),\n        # and sometimes a full resync will be needed.\n        $R($master_id) slaveof 127.0.0.1 0 ;# We use port zero to make it fail.\n\n        if {$debug_msg} {\n            for {set j 0} {$j < 5} {incr j} {\n                puts \"$j: sync_full: [status $R($j) sync_full]\"\n                puts \"$j: id1      : [status $R($j) master_replid]:[status $R($j) master_repl_offset]\"\n                puts \"$j: id2      : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]\"\n                puts \"$j: backlog  : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]\"\n                puts \"---\"\n            }\n        }\n\n        test \"PSYNC2: total sum of full synchronizations is exactly 4\" {\n            set sum 0\n            for {set j 0} {$j < 5} {incr j} {\n                incr sum [status $R($j) sync_full]\n            }\n            assert {$sum == 4}\n        }\n\n        # Limit anyway the maximum number of cycles. This is useful when the\n        # test is skipped via --only option of the test suite. In that case\n        # we don't want to see many seconds of this test being just skipped.\n        if {$cycle > 50} break\n    }\n\n    test \"PSYNC2: Bring the master back again for next test\" {\n        $R($master_id) slaveof no one\n        set master_host $R_host($master_id)\n        set master_port $R_port($master_id)\n        for {set j 0} {$j < 5} {incr j} {\n            if {$j == $master_id} continue\n            $R($j) slaveof $master_host $master_port\n        }\n\n        # Wait for replicas to sync. it is not enough to just wait for connected_slaves==4\n        # since we might do the check before the master realized that they're disconnected\n        wait_for_condition 50 1000 {\n            [status $R($master_id) connected_slaves] == 4 &&\n            [status $R([expr {($master_id+1)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+2)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+3)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+4)%5}]) master_link_status] == \"up\"\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n    }\n\n    test \"PSYNC2: Partial resync after restart using RDB aux fields\" {\n        # Pick a random slave\n        set slave_id [expr {($master_id+1)%5}]\n        set sync_count [status $R($master_id) sync_full]\n        set sync_partial [status $R($master_id) sync_partial_ok]\n        set sync_partial_err [status $R($master_id) sync_partial_err]\n        catch {\n            $R($slave_id) config rewrite\n            $R($slave_id) debug restart\n        }\n        # note: just waiting for connected_slaves==4 has a race condition since\n        # we might do the check before the master realized that the slave disconnected\n        wait_for_condition 50 1000 {\n            [status $R($master_id) sync_partial_ok] == $sync_partial + 1\n        } else {\n            puts \"prev sync_full: $sync_count\"\n            puts \"prev sync_partial_ok: $sync_partial\"\n            puts \"prev sync_partial_err: $sync_partial_err\"\n            puts [$R($master_id) info stats]\n            fail \"Replica didn't partial sync\"\n        }\n        set new_sync_count [status $R($master_id) sync_full]\n        assert {$sync_count == $new_sync_count}\n    }\n\n    test \"PSYNC2: Replica RDB restart with EVALSHA in backlog issue #4483\" {\n        # Pick a random slave\n        set slave_id [expr {($master_id+1)%5}]\n        set sync_count [status $R($master_id) sync_full]\n\n        # Make sure to replicate the first EVAL while the salve is online\n        # so that it's part of the scripts the master believes it's safe\n        # to propagate as EVALSHA.\n        $R($master_id) EVAL {return redis.call(\"incr\",\"__mycounter\")} 0\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n\n        # Wait for the two to sync\n        wait_for_condition 50 1000 {\n            [$R($master_id) debug digest] == [$R($slave_id) debug digest]\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n\n        # Prevent the slave from receiving master updates, and at\n        # the same time send a new script several times to the\n        # master, so that we'll end with EVALSHA into the backlog.\n        $R($slave_id) slaveof 127.0.0.1 0\n\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n\n        catch {\n            $R($slave_id) config rewrite\n            $R($slave_id) debug restart\n        }\n\n        # Reconfigure the slave correctly again, when it's back online.\n        set retry 50\n        while {$retry} {\n            if {[catch {\n                $R($slave_id) slaveof $master_host $master_port\n            }]} {\n                after 1000\n            } else {\n                break\n            }\n            incr retry -1\n        }\n\n        # The master should be back at 4 slaves eventually\n        wait_for_condition 50 1000 {\n            [status $R($master_id) connected_slaves] == 4\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n        set new_sync_count [status $R($master_id) sync_full]\n        assert {$sync_count == $new_sync_count}\n\n        # However if the slave started with the full state of the\n        # scripting engine, we should now have the same digest.\n        wait_for_condition 50 1000 {\n            [$R($master_id) debug digest] == [$R($slave_id) debug digest]\n        } else {\n            fail \"Debug digest mismatch between master and replica in post-restart handshake\"\n        }\n    }\n\n    if {$no_exit} {\n        while 1 { puts -nonewline .; flush stdout; after 1000}\n    }\n\n}}}}}\n\nstart_server {tags {\"psync2\"}} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\n    test {pings at the end of replication stream are ignored for psync} {\n        set master [srv -4 client]\n        set master_host [srv -4 host]\n        set master_port [srv -4 port]\n        set replica1 [srv -3 client]\n        set replica2 [srv -2 client]\n        set replica3 [srv -1 client]\n        set replica4 [srv -0 client]\n\n        $replica1 replicaof $master_host $master_port\n        $replica2 replicaof $master_host $master_port\n        $replica3 replicaof $master_host $master_port\n        $replica4 replicaof $master_host $master_port\n        wait_for_condition 50 1000 {\n            [status $master connected_slaves] == 4\n        } else {\n            fail \"replicas didn't connect\"\n        }\n\n        $master incr x\n        wait_for_condition 50 1000 {\n            [$replica1 get x] == 1 && [$replica2 get x] == 1 &&\n            [$replica3 get x] == 1 && [$replica4 get x] == 1\n        } else {\n            fail \"replicas didn't get incr\"\n        }\n\n        # disconnect replica1 and replica2\n        # and wait for the master to send a ping to replica3 and replica4\n        $replica1 replicaof no one\n        $replica2 replicaof 127.0.0.1 1 ;# we can't promote it to master since that will cycle the replication id\n        $master config set repl-ping-replica-period 1\n        after 1500\n\n        # make everyone sync from the replica1 that didn't get the last ping from the old master\n        # replica4 will keep syncing from the old master which now syncs from replica1\n        # and replica2 will re-connect to the old master (which went back in time)\n        set new_master_host [srv -3 host]\n        set new_master_port [srv -3 port]\n        $replica3 replicaof $new_master_host $new_master_port\n        $master replicaof $new_master_host $new_master_port\n        $replica2 replicaof $master_host $master_port\n        wait_for_condition 50 1000 {\n            [status $replica2 master_link_status] == \"up\" &&\n            [status $replica3 master_link_status] == \"up\" &&\n            [status $replica4 master_link_status] == \"up\" &&\n            [status $master master_link_status] == \"up\"\n        } else {\n            fail \"replicas didn't connect\"\n        }\n\n        # make sure replication is still alive and kicking\n        $replica1 incr x\n        wait_for_condition 50 1000 {\n            [$replica2 get x] == 2 &&\n            [$replica3 get x] == 2 &&\n            [$replica4 get x] == 2 &&\n            [$master get x] == 2\n        } else {\n            fail \"replicas didn't get incr\"\n        }\n\n        # make sure there are full syncs other than the initial ones\n        assert_equal [status $master sync_full] 4\n        assert_equal [status $replica1 sync_full] 0\n        assert_equal [status $replica2 sync_full] 0\n        assert_equal [status $replica3 sync_full] 0\n        assert_equal [status $replica4 sync_full] 0\n\n        # force psync\n        $master client kill type master\n        $replica2 client kill type master\n        $replica3 client kill type master\n        $replica4 client kill type master\n\n        # make sure replication is still alive and kicking\n        $replica1 incr x\n        wait_for_condition 50 1000 {\n            [$replica2 get x] == 3 &&\n            [$replica3 get x] == 3 &&\n            [$replica4 get x] == 3 &&\n            [$master get x] == 3\n        } else {\n            fail \"replicas didn't get incr\"\n        }\n\n        # make sure there are full syncs other than the initial ones\n        assert_equal [status $master sync_full] 4\n        assert_equal [status $replica1 sync_full] 0\n        assert_equal [status $replica2 sync_full] 0\n        assert_equal [status $replica3 sync_full] 0\n        assert_equal [status $replica4 sync_full] 0\n}\n}}}}}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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  } {\"0\",\"compressible\",\"string\",\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\"0\",\"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\"0\",\"hash_zipped\",\"hash\",\"a\",\"1\",\"b\",\"2\",\"c\",\"3\",\n\"0\",\"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\"0\",\"list_zipped\",\"list\",\"1\",\"2\",\"3\",\"a\",\"b\",\"c\",\"100000\",\"6000000000\",\n\"0\",\"number\",\"string\",\"10\"\n\"0\",\"set\",\"set\",\"1\",\"100000\",\"2\",\"3\",\"6000000000\",\"a\",\"b\",\"c\",\n\"0\",\"set_zipped_1\",\"set\",\"1\",\"2\",\"3\",\"4\",\n\"0\",\"set_zipped_2\",\"set\",\"100000\",\"200000\",\"300000\",\"400000\",\n\"0\",\"set_zipped_3\",\"set\",\"1000000000\",\"2000000000\",\"3000000000\",\"4000000000\",\"5000000000\",\"6000000000\",\n\"0\",\"string\",\"string\",\"Hello World\"\n\"0\",\"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\"0\",\"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\nstart_server [list overrides [list \"dir\" $server_path]] {\n    test {Test RDB stream encoding} {\n        for {set j 0} {$j < 1000} {incr j} {\n            if {rand() < 0.9} {\n                r xadd stream * foo $j\n            } else {\n                r xadd stream * bar $j\n            }\n        }\n        r xgroup create stream mygroup 0\n        r xreadgroup GROUP mygroup Alice COUNT 1 STREAMS stream >\n        set digest [r debug digest]\n        r debug reload\n        set newdigest [r debug digest]\n        assert {$digest eq $newdigest}\n        r del stream\n    }\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 -1 < [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 {*CRC error*} \\\n                [exec tail -10 < [dict get $srv stdout]]]\n        } else {\n            fail \"Server started even if RDB was corrupted!\"\n        }\n    }\n}\n\nstart_server {} {\n    test {Test FLUSHALL aborts bgsave} {\n        r config set rdb-key-save-delay 1000\n        r debug populate 1000\n        r bgsave\n        assert_equal [s rdb_bgsave_in_progress] 1\n        r flushall\n        after 200\n        assert_equal [s rdb_bgsave_in_progress] 0\n        # make sure the server is still writable\n        r set x xx\n    }\n}\n\ntest {client freed during loading} {\n    start_server [list overrides [list key-load-delay 10 rdbcompression no]] {\n        # create a big rdb that will take long to load. it is important\n        # for keys to be big since the server processes events only once in 2mb.\n        # 100mb of rdb, 100k keys will load in more than 1 second\n        r debug populate 100000 key 1000\n\n        catch {\n            r debug restart\n        }\n\n        set stdout [srv 0 stdout]\n        while 1 {\n            # check that the new server actually started and is ready for connections\n            if {[exec grep -i \"Server initialized\" | wc -l < $stdout] > 1} {\n                break\n            }\n            after 10\n        }\n        # make sure it's still loading\n        assert_equal [s loading] 1\n\n        # connect and disconnect 10 clients\n        set clients {}\n        for {set j 0} {$j < 10} {incr j} {\n            lappend clients [redis_deferring_client]\n        }\n        foreach rd $clients {\n            $rd debug log bla\n        }\n        foreach rd $clients {\n            $rd read\n        }\n        foreach rd $clients {\n            $rd close\n        }\n\n        # make sure the server freed the clients\n        wait_for_condition 100 100 {\n            [s connected_clients] < 3\n        } else {\n            fail \"clients didn't disconnect\"\n        }\n\n        # make sure it's still loading\n        assert_equal [s loading] 1\n\n        # no need to keep waiting for loading to complete\n        exec kill [srv 0 pid]\n    }\n}"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/redis-cli.tcl",
    "content": "source tests/support/cli.tcl\n\nstart_server {tags {\"cli\"}} {\n    proc open_cli {} {\n        set ::env(TERM) dumb\n        set cmdline [rediscli [srv port] \"-n 9\"]\n        set fd [open \"|$cmdline\" \"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 [rediscli [srv port] [list -n 9 {*}$args]]\n        foreach {key value} $args {\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": "Chapter02/redis-6.0.3/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            wait_for_condition 50 100 {\n                [s -1 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n        }\n\n        test {If min-slaves-to-write is honored, write is accepted} {\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 10\n            r set foo 12345\n            wait_for_condition 50 100 {\n                [r -1 get foo] eq {12345}\n            } else {\n                fail \"Write did not reached replica\"\n            }\n        }\n\n        test {No write if min-slaves-to-write is < attached slaves} {\n            r config set min-slaves-to-write 2\n            r config set min-slaves-max-lag 10\n            catch {r set foo 12345} err\n            set err\n        } {NOREPLICAS*}\n\n        test {If min-slaves-to-write is honored, write is accepted (again)} {\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 10\n            r set foo 12345\n            wait_for_condition 50 100 {\n                [r -1 get foo] eq {12345}\n            } else {\n                fail \"Write did not reached replica\"\n            }\n        }\n\n        test {No write if min-slaves-max-lag is > of the slave lag} {\n            r -1 deferred 1\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 2\n            r -1 debug sleep 6\n            assert {[r set foo 12345] eq {OK}}\n            after 4000\n            catch {r set foo 12345} err\n            assert {[r -1 read] eq {OK}}\n            r -1 deferred 0\n            set err\n        } {NOREPLICAS*}\n\n        test {min-slaves-to-write is ignored by slaves} {\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 10\n            r -1 config set min-slaves-to-write 1\n            r -1 config set min-slaves-max-lag 10\n            r set foo aaabbb\n            wait_for_condition 50 100 {\n                [r -1 get foo] eq {aaabbb}\n            } else {\n                fail \"Write did not reached replica\"\n            }\n        }\n\n        # Fix parameters for the next test to work\n        r config set min-slaves-to-write 0\n        r -1 config set min-slaves-to-write 0\n        r flushall\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 - Replica 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": "Chapter02/redis-6.0.3/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 - Replica 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        test {Slave is able to evict keys created in writable slaves} {\n            r -1 select 5\n            assert {[r -1 dbsize] == 0}\n            r -1 config set slave-read-only no\n            r -1 set key1 1 ex 5\n            r -1 set key2 2 ex 5\n            r -1 set key3 3 ex 5\n            assert {[r -1 dbsize] == 3}\n            after 6000\n            r -1 dbsize\n        } {0}\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 - Replica inconsistency\"\n                puts \"Run diff -u against /tmp/repldump*.txt for more info\"\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        test {SLAVE can reload \"lua\" AUX RDB fields of duplicated scripts} {\n            # Force a Slave full resynchronization\n            r debug change-repl-id\n            r -1 client kill type master\n\n            # Check that after a full resync the slave can still load\n            # correctly the RDB file: such file will contain \"lua\" AUX\n            # sections with scripts already in the memory of the master.\n\n            wait_for_condition 500 100 {\n                [s -1 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n\n            wait_for_condition 50 100 {\n                [r debug digest] eq [r -1 debug digest]\n            } else {\n                fail \"DEBUG DIGEST mismatch after full SYNC with many scripts\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/replication-4.tcl",
    "content": "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        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 - Replica 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        test {Replication of SPOP command -- alsoPropagate() API} {\n            $master del myset\n            set size [expr 1+[randomInt 100]]\n            set content {}\n            for {set j 0} {$j < $size} {incr j} {\n                lappend content [randomValue]\n            }\n            $master sadd myset {*}$content\n\n            set count [randomInt 100]\n            set result [$master spop myset $count]\n\n            wait_for_condition 50 100 {\n                [$master debug digest] eq [$slave debug digest]\n            } else {\n                fail \"SPOP replication inconsistency\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/replication-psync.tcl",
    "content": "# 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 specify backlog size, ttl, delay before reconnection, test duration\n# in seconds, and an additional condition to verify at the end.\n#\n# If reconnect is > 0, the test actually try to break the connection and\n# reconnect with the master, otherwise just the initial synchronization is\n# checked for consistency.\nproc test_psync {descr duration backlog_size backlog_ttl delay cond mdl sdl reconnect} {\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            $master config set repl-diskless-sync $mdl\n            $master config set repl-diskless-sync-delay 1\n            $slave config set repl-diskless-load $sdl\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 {Slave should be able to synchronize with the master} {\n                $slave slaveof $master_host $master_port\n                wait_for_condition 50 100 {\n                    [lindex [r role] 0] eq {slave} &&\n                    [lindex [r role] 3] eq {connected}\n                } else {\n                    fail \"Replication not started.\"\n                }\n            }\n\n            # Check that the background clients are actually writing.\n            test {Detect write load to master} {\n                wait_for_condition 50 1000 {\n                    [$master dbsize] > 100\n                } else {\n                    fail \"Can't detect write load from background clients.\"\n                }\n            }\n\n            test \"Test replication partial resync: $descr (diskless: $mdl, $sdl, reconnect: $reconnect)\" {\n                # Now while the clients are writing data, break the maste-slave\n                # link multiple times.\n                if ($reconnect) {\n                    for {set j 0} {$j < $duration*10} {incr j} {\n                        after 100\n                        # catch {puts \"MASTER [$master dbsize] keys, REPLICA [$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                }\n                stop_bg_complex_data $load_handle0\n                stop_bg_complex_data $load_handle1\n                stop_bg_complex_data $load_handle2\n\n                # Wait for the slave to reach the \"online\"\n                # state from the POV of the master.\n                set retry 5000\n                while {$retry} {\n                    set info [$master info]\n                    if {[string match {*slave0:*state=online*} $info]} {\n                        break\n                    } else {\n                        incr retry -1\n                        after 100\n                    }\n                }\n                if {$retry == 0} {\n                    error \"assertion:Slave not correctly synchronized\"\n                }\n\n                # Wait that slave acknowledge it is online so\n                # we are sure that DBSIZE and DEBUG DIGEST will not\n                # fail because of timing issues. (-LOADING error)\n                wait_for_condition 5000 100 {\n                    [lindex [$slave role] 3] eq {connected}\n                } else {\n                    fail \"Slave still not connected after some time\"\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                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 - Replica 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\nforeach mdl {no yes} {\n    foreach sdl {disabled swapdb} {\n        test_psync {no reconnection, just sync} 6 1000000 3600 0 {\n        } $mdl $sdl 0\n\n        test_psync {ok psync} 6 100000000 3600 0 {\n        assert {[s -1 sync_partial_ok] > 0}\n        } $mdl $sdl 1\n\n        test_psync {no backlog} 6 100 3600 0.5 {\n        assert {[s -1 sync_partial_err] > 0}\n        } $mdl $sdl 1\n\n        test_psync {ok after delay} 3 100000000 3600 3 {\n        assert {[s -1 sync_partial_ok] > 0}\n        } $mdl $sdl 1\n\n        test_psync {backlog expired} 3 100000000 1 3 {\n        assert {[s -1 sync_partial_err] > 0}\n        } $mdl $sdl 1\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/integration/replication.tcl",
    "content": "proc log_file_matches {log pattern} {\n    set fp [open $log r]\n    set content [read $fp]\n    close $fp\n    string match $pattern $content\n}\n\nstart_server {tags {\"repl\"}} {\n    set slave [srv 0 client]\n    set slave_host [srv 0 host]\n    set slave_port [srv 0 port]\n    set slave_log [srv 0 stdout]\n    start_server {} {\n        set master [srv 0 client]\n        set master_host [srv 0 host]\n        set master_port [srv 0 port]\n\n        # Configure the master in order to hang waiting for the BGSAVE\n        # operation, so that the slave remains in the handshake state.\n        $master config set repl-diskless-sync yes\n        $master config set repl-diskless-sync-delay 1000\n\n        # Use a short replication timeout on the slave, so that if there\n        # are no bugs the timeout is triggered in a reasonable amount\n        # of time.\n        $slave config set repl-timeout 5\n\n        # Start the replication process...\n        $slave slaveof $master_host $master_port\n\n        test {Slave enters handshake} {\n            wait_for_condition 50 1000 {\n                [string match *handshake* [$slave role]]\n            } else {\n                fail \"Replica does not enter handshake state\"\n            }\n        }\n\n        # But make the master unable to send\n        # the periodic newlines to refresh the connection. The slave\n        # should detect the timeout.\n        $master debug sleep 10\n\n        test {Slave is able to detect timeout during handshake} {\n            wait_for_condition 50 1000 {\n                [log_file_matches $slave_log \"*Timeout connecting to the MASTER*\"]\n            } else {\n                fail \"Replica is not able to detect timeout\"\n            }\n        }\n    }\n}\n\nstart_server {tags {\"repl\"}} {\n    set A [srv 0 client]\n    set A_host [srv 0 host]\n    set A_port [srv 0 port]\n    start_server {} {\n        set B [srv 0 client]\n        set B_host [srv 0 host]\n        set B_port [srv 0 port]\n\n        test {Set instance A as slave of B} {\n            $A slaveof $B_host $B_port\n            wait_for_condition 50 100 {\n                [lindex [$A role] 0] eq {slave} &&\n                [string match {*master_link_status:up*} [$A info replication]]\n            } else {\n                fail \"Can't turn the instance into a replica\"\n            }\n        }\n\n        test {INCRBYFLOAT replication, should not remove expire} {\n            r set test 1 EX 100\n            r incrbyfloat test 0.1\n            after 1000\n            assert_equal [$A debug digest] [$B debug digest]\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                [$A debug digest] eq [$B debug digest]\n            } else {\n                fail \"Master and replica have different digest: [$A debug digest] VS [$B 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 [$A debug digest] [$B debug digest]\n        }\n\n        test {BLPOP followed by role change, issue #2473} {\n            set rd [redis_deferring_client]\n            $rd blpop foo 0 ; # Block while B is a master\n\n            # Turn B into master of A\n            $A slaveof no one\n            $B slaveof $A_host $A_port\n            wait_for_condition 50 100 {\n                [lindex [$B role] 0] eq {slave} &&\n                [string match {*master_link_status:up*} [$B info replication]]\n            } else {\n                fail \"Can't turn the instance into a replica\"\n            }\n\n            # Push elements into the \"foo\" list of the new replica.\n            # If the client is still attached to the instance, we'll get\n            # a desync between the two instances.\n            $A rpush foo a b c\n            after 100\n\n            wait_for_condition 50 100 {\n                [$A debug digest] eq [$B debug digest] &&\n                [$A lrange foo 0 -1] eq {a b c} &&\n                [$B lrange foo 0 -1] eq {a b c}\n            } else {\n                fail \"Master and replica have different digest: [$A debug digest] VS [$B debug digest]\"\n            }\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 \"replica\"} {\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 replica\"\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        test {ROLE in master reports master with a slave} {\n            set res [r -1 role]\n            lassign $res role offset slaves\n            assert {$role eq {master}}\n            assert {$offset > 0}\n            assert {[llength $slaves] == 1}\n            lassign [lindex $slaves 0] master_host master_port slave_offset\n            assert {$slave_offset <= $offset}\n        }\n\n        test {ROLE in slave reports slave in connected state} {\n            set res [r role]\n            lassign $res role master_host master_port slave_state slave_offset\n            assert {$role eq {slave}}\n            assert {$slave_state eq {connected}}\n        }\n    }\n}\n\nforeach mdl {no yes} {\n    foreach sdl {disabled swapdb} {\n        start_server {tags {\"repl\"}} {\n            set master [srv 0 client]\n            $master config set repl-diskless-sync $mdl\n            $master config set repl-diskless-sync-delay 1\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n            set slaves {}\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 replicas at the same time (issue #141), master diskless=$mdl, replica diskless=$sdl\" {\n                            # start load handles only inside the test, so that the test can be skipped\n                            set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000000]\n                            set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000000]\n                            set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000000]\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                            after 5000 ;# wait for some data to accumulate so that we have RDB part for the fork\n\n                            # Send SLAVEOF commands to slaves\n                            [lindex $slaves 0] config set repl-diskless-load $sdl\n                            [lindex $slaves 1] config set repl-diskless-load $sdl\n                            [lindex $slaves 2] config set repl-diskless-load $sdl\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\"\n                            # state from the POV of the master.\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                            # Wait that slaves acknowledge they are online so\n                            # we are sure that DBSIZE and DEBUG DIGEST will not\n                            # fail because of timing issues.\n                            wait_for_condition 500 100 {\n                                [lindex [[lindex $slaves 0] role] 3] eq {connected} &&\n                                [lindex [[lindex $slaves 1] role] 3] eq {connected} &&\n                                [lindex [[lindex $slaves 2] role] 3] eq {connected}\n                            } else {\n                                fail \"Slaves still not connected after some time\"\n                            }\n\n                            # Stop the write load\n                            stop_bg_complex_data $load_handle0\n                            stop_bg_complex_data $load_handle1\n                            stop_bg_complex_data $load_handle2\n                            stop_write_load $load_handle3\n                            stop_write_load $load_handle4\n\n                            # Make sure that slaves and master have same\n                            # 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 master and replica 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    }\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    start_server {} {\n        test \"Master stream is correctly processed while the replica has a script in -BUSY state\" {\n            set load_handle0 [start_write_load $master_host $master_port 3]\n            set slave [srv 0 client]\n            $slave config set lua-time-limit 500\n            $slave slaveof $master_host $master_port\n\n            # Wait for the slave to be online\n            wait_for_condition 500 100 {\n                [lindex [$slave role] 3] eq {connected}\n            } else {\n                fail \"Replica still not connected after some time\"\n            }\n\n            # Wait some time to make sure the master is sending data\n            # to the slave.\n            after 5000\n\n            # Stop the ability of the slave to process data by sendig\n            # a script that will put it in BUSY state.\n            $slave eval {for i=1,3000000000 do end} 0\n\n            # Wait some time again so that more master stream will\n            # be processed.\n            after 2000\n\n            # Stop the write load\n            stop_write_load $load_handle0\n\n            # number of keys\n            wait_for_condition 500 100 {\n                [$master debug digest] eq [$slave debug digest]\n            } else {\n                fail \"Different datasets between replica and master\"\n            }\n        }\n    }\n}\n\ntest {slave fails full sync and diskless load swapdb recovers it} {\n    start_server {tags {\"repl\"}} {\n        set slave [srv 0 client]\n        set slave_host [srv 0 host]\n        set slave_port [srv 0 port]\n        set slave_log [srv 0 stdout]\n        start_server {} {\n            set master [srv 0 client]\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n\n            # Put different data sets on the master and slave\n            # we need to put large keys on the master since the slave replies to info only once in 2mb\n            $slave debug populate 2000 slave 10\n            $master debug populate 200 master 100000\n            $master config set rdbcompression no\n\n            # Set master and slave to use diskless replication\n            $master config set repl-diskless-sync yes\n            $master config set repl-diskless-sync-delay 0\n            $slave config set repl-diskless-load swapdb\n\n            # Set master with a slow rdb generation, so that we can easily disconnect it mid sync\n            # 10ms per key, with 200 keys is 2 seconds\n            $master config set rdb-key-save-delay 10000\n\n            # Start the replication process...\n            $slave slaveof $master_host $master_port\n\n            # wait for the slave to start reading the rdb\n            wait_for_condition 50 100 {\n                [s -1 loading] eq 1\n            } else {\n                fail \"Replica didn't get into loading mode\"\n            }\n\n            # make sure that next sync will not start immediately so that we can catch the slave in betweeen syncs\n            $master config set repl-diskless-sync-delay 5\n            # for faster server shutdown, make rdb saving fast again (the fork is already uses the slow one)\n            $master config set rdb-key-save-delay 0\n\n            # waiting slave to do flushdb (key count drop)\n            wait_for_condition 50 100 {\n                2000 != [scan [regexp -inline {keys\\=([\\d]*)} [$slave info keyspace]] keys=%d]\n            } else {\n                fail \"Replica didn't flush\"\n            }\n\n            # make sure we're still loading\n            assert_equal [s -1 loading] 1\n\n            # kill the slave connection on the master\n            set killed [$master client kill type slave]\n\n            # wait for loading to stop (fail)\n            wait_for_condition 50 100 {\n                [s -1 loading] eq 0\n            } else {\n                fail \"Replica didn't disconnect\"\n            }\n\n            # make sure the original keys were restored\n            assert_equal [$slave dbsize] 2000\n        }\n    }\n}\n\ntest {diskless loading short read} {\n    start_server {tags {\"repl\"}} {\n        set replica [srv 0 client]\n        set replica_host [srv 0 host]\n        set replica_port [srv 0 port]\n        start_server {} {\n            set master [srv 0 client]\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n\n            # Set master and replica to use diskless replication\n            $master config set repl-diskless-sync yes\n            $master config set rdbcompression no\n            $replica config set repl-diskless-load swapdb\n            # Try to fill the master with all types of data types / encodings\n            for {set k 0} {$k < 3} {incr k} {\n                for {set i 0} {$i < 10} {incr i} {\n                    r set \"$k int_$i\" [expr {int(rand()*10000)}]\n                    r expire \"$k int_$i\" [expr {int(rand()*10000)}]\n                    r set \"$k string_$i\" [string repeat A [expr {int(rand()*1000000)}]]\n                    r hset \"$k hash_small\" [string repeat A [expr {int(rand()*10)}]]  0[string repeat A [expr {int(rand()*10)}]]\n                    r hset \"$k hash_large\" [string repeat A [expr {int(rand()*10000)}]] [string repeat A [expr {int(rand()*1000000)}]]\n                    r sadd \"$k set_small\" [string repeat A [expr {int(rand()*10)}]]\n                    r sadd \"$k set_large\" [string repeat A [expr {int(rand()*1000000)}]]\n                    r zadd \"$k zset_small\" [expr {rand()}] [string repeat A [expr {int(rand()*10)}]]\n                    r zadd \"$k zset_large\" [expr {rand()}] [string repeat A [expr {int(rand()*1000000)}]]\n                    r lpush \"$k list_small\" [string repeat A [expr {int(rand()*10)}]]\n                    r lpush \"$k list_large\" [string repeat A [expr {int(rand()*1000000)}]]\n                    for {set j 0} {$j < 10} {incr j} {\n                        r xadd \"$k stream\" * foo \"asdf\" bar \"1234\"\n                    }\n                    r xgroup create \"$k stream\" \"mygroup_$i\" 0\n                    r xreadgroup GROUP \"mygroup_$i\" Alice COUNT 1 STREAMS \"$k stream\" >\n                }\n            }\n\n            # Start the replication process...\n            $master config set repl-diskless-sync-delay 0\n            $replica replicaof $master_host $master_port\n\n            # kill the replication at various points\n            set attempts 3\n            if {$::accurate} { set attempts 10 }\n            for {set i 0} {$i < $attempts} {incr i} {\n                # wait for the replica to start reading the rdb\n                # using the log file since the replica only responds to INFO once in 2mb\n                wait_for_log_message -1 \"*Loading DB in memory*\" 5 2000 1\n\n                # add some additional random sleep so that we kill the master on a different place each time\n                after [expr {int(rand()*100)}]\n\n                # kill the replica connection on the master\n                set killed [$master client kill type replica]\n\n                if {[catch {\n                    set res [wait_for_log_message -1 \"*Internal error in RDB*\" 5 100 10]\n                    if {$::verbose} {\n                        puts $res\n                    }\n                }]} {\n                    puts \"failed triggering short read\"\n                    # force the replica to try another full sync\n                    $master client kill type replica\n                    $master set asdf asdf\n                    # the side effect of resizing the backlog is that it is flushed (16k is the min size)\n                    $master config set repl-backlog-size [expr {16384 + $i}]\n                }\n                # wait for loading to stop (fail)\n                wait_for_condition 100 10 {\n                    [s -1 loading] eq 0\n                } else {\n                    fail \"Replica didn't disconnect\"\n                }\n            }\n            # enable fast shutdown\n            $master config set rdb-key-save-delay 0\n        }\n    }\n}\n\n# get current stime and utime metrics for a thread (since it's creation)\nproc get_cpu_metrics { statfile } {\n    if { [ catch {\n        set fid   [ open $statfile r ]\n        set data  [ read $fid 1024 ]\n        ::close $fid\n        set data  [ split $data ]\n\n        ;## number of jiffies it has been scheduled...\n        set utime [ lindex $data 13 ]\n        set stime [ lindex $data 14 ]\n    } err ] } {\n        error \"assertion:can't parse /proc: $err\"\n    }\n    set mstime [clock milliseconds]\n    return [ list $mstime $utime $stime ]\n}\n\n# compute %utime and %stime of a thread between two measurements\nproc compute_cpu_usage {start end} {\n    set clock_ticks [exec getconf CLK_TCK]\n    # convert ms time to jiffies and calc delta\n    set dtime [ expr { ([lindex $end 0] - [lindex $start 0]) * double($clock_ticks) / 1000 } ]\n    set utime [ expr { [lindex $end 1] - [lindex $start 1] } ]\n    set stime [ expr { [lindex $end 2] - [lindex $start 2] } ]\n    set pucpu  [ expr { ($utime / $dtime) * 100 } ]\n    set pscpu  [ expr { ($stime / $dtime) * 100 } ]\n    return [ list $pucpu $pscpu ]\n}\n\n\n# test diskless rdb pipe with multiple replicas, which may drop half way\nstart_server {tags {\"repl\"}} {\n    set master [srv 0 client]\n    $master config set repl-diskless-sync yes\n    $master config set repl-diskless-sync-delay 1\n    set master_host [srv 0 host]\n    set master_port [srv 0 port]\n    set master_pid [srv 0 pid]\n    # put enough data in the db that the rdb file will be bigger than the socket buffers\n    # and since we'll have key-load-delay of 100, 20000 keys will take at least 2 seconds\n    # we also need the replica to process requests during transfer (which it does only once in 2mb)\n    $master debug populate 20000 test 10000\n    $master config set rdbcompression no\n    # If running on Linux, we also measure utime/stime to detect possible I/O handling issues\n    set os [catch {exec unamee}]\n    set measure_time [expr {$os == \"Linux\"} ? 1 : 0]\n    foreach all_drop {no slow fast all} {\n        test \"diskless $all_drop replicas drop during rdb pipe\" {\n            set replicas {}\n            set replicas_alive {}\n            # start one replica that will read the rdb fast, and one that will be slow\n            start_server {} {\n                lappend replicas [srv 0 client]\n                lappend replicas_alive [srv 0 client]\n                start_server {} {\n                    lappend replicas [srv 0 client]\n                    lappend replicas_alive [srv 0 client]\n\n                    # start replication\n                    # it's enough for just one replica to be slow, and have it's write handler enabled\n                    # so that the whole rdb generation process is bound to that\n                    [lindex $replicas 0] config set repl-diskless-load swapdb\n                    [lindex $replicas 0] config set key-load-delay 100\n                    [lindex $replicas 0] replicaof $master_host $master_port\n                    [lindex $replicas 1] replicaof $master_host $master_port\n\n                    # wait for the replicas to start reading the rdb\n                    # using the log file since the replica only responds to INFO once in 2mb\n                    wait_for_log_message -1 \"*Loading DB in memory*\" 8 800 10\n\n                    if {$measure_time} {\n                        set master_statfile \"/proc/$master_pid/stat\"\n                        set master_start_metrics [get_cpu_metrics $master_statfile]\n                        set start_time [clock seconds]\n                    }\n\n                    # wait a while so that the pipe socket writer will be\n                    # blocked on write (since replica 0 is slow to read from the socket)\n                    after 500\n\n                    # add some command to be present in the command stream after the rdb.\n                    $master incr $all_drop\n\n                    # disconnect replicas depending on the current test\n                    if {$all_drop == \"all\" || $all_drop == \"fast\"} {\n                        exec kill [srv 0 pid]\n                        set replicas_alive [lreplace $replicas_alive 1 1]\n                    }\n                    if {$all_drop == \"all\" || $all_drop == \"slow\"} {\n                        exec kill [srv -1 pid]\n                        set replicas_alive [lreplace $replicas_alive 0 0]\n                    }\n\n                    # wait for rdb child to exit\n                    wait_for_condition 500 100 {\n                        [s -2 rdb_bgsave_in_progress] == 0\n                    } else {\n                        fail \"rdb child didn't terminate\"\n                    }\n\n                    # make sure we got what we were aiming for, by looking for the message in the log file\n                    if {$all_drop == \"all\"} {\n                        wait_for_log_message -2 \"*Diskless rdb transfer, last replica dropped, killing fork child*\" 12 1 1\n                    }\n                    if {$all_drop == \"no\"} {\n                        wait_for_log_message -2 \"*Diskless rdb transfer, done reading from pipe, 2 replicas still up*\" 12 1 1\n                    }\n                    if {$all_drop == \"slow\" || $all_drop == \"fast\"} {\n                        wait_for_log_message -2 \"*Diskless rdb transfer, done reading from pipe, 1 replicas still up*\" 12 1 1\n                    }\n\n                    # make sure we don't have a busy loop going thought epoll_wait\n                    if {$measure_time} {\n                        set master_end_metrics [get_cpu_metrics $master_statfile]\n                        set time_elapsed [expr {[clock seconds]-$start_time}]\n                        set master_cpu [compute_cpu_usage $master_start_metrics $master_end_metrics]\n                        set master_utime [lindex $master_cpu 0]\n                        set master_stime [lindex $master_cpu 1]\n                        if {$::verbose} {\n                            puts \"elapsed: $time_elapsed\"\n                            puts \"master utime: $master_utime\"\n                            puts \"master stime: $master_stime\"\n                        }\n                        if {$all_drop == \"all\" || $all_drop == \"slow\"} {\n                            assert {$master_utime < 70}\n                            assert {$master_stime < 70}\n                        }\n                        if {$all_drop == \"none\" || $all_drop == \"fast\"} {\n                            assert {$master_utime < 15}\n                            assert {$master_stime < 15}\n                        }\n                    }\n\n                    # verify the data integrity\n                    foreach replica $replicas_alive {\n                        # Wait that replicas acknowledge they are online so\n                        # we are sure that DBSIZE and DEBUG DIGEST will not\n                        # fail because of timing issues.\n                        wait_for_condition 50 100 {\n                            [lindex [$replica role] 3] eq {connected}\n                        } else {\n                            fail \"replicas still not connected after some time\"\n                        }\n\n                        # Make sure that replicas and master have same\n                        # number of keys\n                        wait_for_condition 50 100 {\n                            [$master dbsize] == [$replica dbsize]\n                        } else {\n                            fail \"Different number of keys between master and replicas after too long time.\"\n                        }\n\n                        # Check digests\n                        set digest [$master debug digest]\n                        set digest0 [$replica debug digest]\n                        assert {$digest ne 0000000000000000000000000000000000000000}\n                        assert {$digest eq $digest0}\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/Makefile",
    "content": "\n# find the OS\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\n# Compile flags for linux / osx\nifeq ($(uname_S),Linux)\n\tSHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -shared\nelse\n\tSHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup\nendif\n\nTEST_MODULES = \\\n    commandfilter.so \\\n    testrdb.so \\\n    fork.so \\\n    infotest.so \\\n    propagate.so \\\n    misc.so \\\n    hooks.so \\\n    blockonkeys.so \\\n    scan.so \\\n    datatype.so \\\n    auth.so\n\n.PHONY: all\n\nall: $(TEST_MODULES)\n\n%.xo: %.c ../../src/redismodule.h\n\t$(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@\n\n%.so: %.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\n.PHONY: clean\n\nclean:\n\trm -f $(TEST_MODULES) $(TEST_MODULES:.so=.xo)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/auth.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n// A simple global user\nstatic RedisModuleUser *global = NULL;\nstatic long long client_change_delta = 0;\n\nvoid UserChangedCallback(uint64_t client_id, void *privdata) {\n    REDISMODULE_NOT_USED(privdata);\n    REDISMODULE_NOT_USED(client_id);\n    client_change_delta++;\n}\n\nint Auth_CreateModuleUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (global) {\n        RedisModule_FreeModuleUser(global);\n    }\n\n    global = RedisModule_CreateModuleUser(\"global\");\n    RedisModule_SetModuleUserACL(global, \"allcommands\");\n    RedisModule_SetModuleUserACL(global, \"allkeys\");\n    RedisModule_SetModuleUserACL(global, \"on\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\nint Auth_AuthModuleUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    uint64_t client_id;\n    RedisModule_AuthenticateClientWithUser(ctx, global, UserChangedCallback, NULL, &client_id);\n\n    return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id);\n}\n\nint Auth_AuthRealUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    size_t length;\n    uint64_t client_id;\n\n    RedisModuleString *user_string = argv[1];\n    const char *name = RedisModule_StringPtrLen(user_string, &length);\n\n    if (RedisModule_AuthenticateClientWithACLUser(ctx, name, length, \n            UserChangedCallback, NULL, &client_id) == REDISMODULE_ERR) {\n        return RedisModule_ReplyWithError(ctx, \"Invalid user\");   \n    }\n\n    return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id);\n}\n\nint Auth_ChangeCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    long long result = client_change_delta;\n    client_change_delta = 0;\n    return RedisModule_ReplyWithLongLong(ctx, result);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"testacl\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.authrealuser\",\n        Auth_AuthRealUser,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.createmoduleuser\",\n        Auth_CreateModuleUser,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.authmoduleuser\",\n        Auth_AuthModuleUser,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.changecount\",\n        Auth_ChangeCount,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/blockonkeys.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\n#define LIST_SIZE 1024\n\ntypedef struct {\n    long long list[LIST_SIZE];\n    long long length;\n} fsl_t; /* Fixed-size list */\n\nstatic RedisModuleType *fsltype = NULL;\n\nfsl_t *fsl_type_create() {\n    fsl_t *o;\n    o = RedisModule_Alloc(sizeof(*o));\n    o->length = 0;\n    return o;\n}\n\nvoid fsl_type_free(fsl_t *o) {\n    RedisModule_Free(o);\n}\n\n/* ========================== \"fsltype\" type methods ======================= */\n\nvoid *fsl_rdb_load(RedisModuleIO *rdb, int encver) {\n    if (encver != 0) {\n        return NULL;\n    }\n    fsl_t *fsl = fsl_type_create();\n    fsl->length = RedisModule_LoadUnsigned(rdb);\n    for (long long i = 0; i < fsl->length; i++)\n        fsl->list[i] = RedisModule_LoadSigned(rdb);\n    return fsl;\n}\n\nvoid fsl_rdb_save(RedisModuleIO *rdb, void *value) {\n    fsl_t *fsl = value;\n    RedisModule_SaveUnsigned(rdb,fsl->length);\n    for (long long i = 0; i < fsl->length; i++)\n        RedisModule_SaveSigned(rdb, fsl->list[i]);\n}\n\nvoid fsl_aofrw(RedisModuleIO *aof, RedisModuleString *key, void *value) {\n    fsl_t *fsl = value;\n    for (long long i = 0; i < fsl->length; i++)\n        RedisModule_EmitAOF(aof, \"FSL.PUSH\",\"sl\", key, fsl->list[i]);\n}\n\nvoid fsl_free(void *value) {\n    fsl_type_free(value);\n}\n\n/* ========================== helper methods ======================= */\n\nint get_fsl(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode, int create, fsl_t **fsl, int reply_on_failure) {\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);\n\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != fsltype) {\n        RedisModule_CloseKey(key);\n        if (reply_on_failure)\n            RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);\n        return 0;\n    }\n\n    /* Create an empty value object if the key is currently empty. */\n    if (type == REDISMODULE_KEYTYPE_EMPTY) {\n        if (!create) {\n            /* Key is empty but we cannot create */\n            RedisModule_CloseKey(key);\n            *fsl = NULL;\n            return 1;\n        }\n        *fsl = fsl_type_create();\n        RedisModule_ModuleTypeSetValue(key, fsltype, *fsl);\n    } else {\n        *fsl = RedisModule_ModuleTypeGetValue(key);\n    }\n\n    RedisModule_CloseKey(key);\n    return 1;\n}\n\n/* ========================== commands ======================= */\n\n/* FSL.PUSH <key> <int> - Push an integer to the fixed-size list (to the right).\n * It must be greater than the element in the head of the list. */\nint fsl_push(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3)\n        return RedisModule_WrongArity(ctx);\n\n    long long ele;\n    if (RedisModule_StringToLongLong(argv[2],&ele) != REDISMODULE_OK)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid integer\");\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 1, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (fsl->length == LIST_SIZE)\n        return RedisModule_ReplyWithError(ctx,\"ERR list is full\");\n\n    if (fsl->length != 0 && fsl->list[fsl->length-1] >= ele)\n        return RedisModule_ReplyWithError(ctx,\"ERR new element has to be greater than the head element\");\n\n    fsl->list[fsl->length++] = ele;\n    RedisModule_SignalKeyAsReady(ctx, argv[1]);\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\nint bpop_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0) || !fsl)\n        return REDISMODULE_ERR;\n\n    RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    return REDISMODULE_OK;\n}\n\nint bpop_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\n/* FSL.BPOP <key> <timeout> - Block clients until list has two or more elements.\n * When that happens, unblock client and pop the last two elements (from the right). */\nint fsl_bpop(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3)\n        return RedisModule_WrongArity(ctx);\n\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK || timeout < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid timeout\");\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (!fsl) {\n        RedisModule_BlockClientOnKeys(ctx, bpop_reply_callback, bpop_timeout_callback,\n                                      NULL, timeout, &argv[1], 1, NULL);\n    } else {\n        RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    }\n\n    return REDISMODULE_OK;\n}\n\nint bpopgt_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n    long long *pgt = RedisModule_GetBlockedClientPrivateData(ctx);\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0) || !fsl)\n        return REDISMODULE_ERR;\n\n    if (fsl->list[fsl->length-1] <= *pgt)\n        return REDISMODULE_ERR;\n\n    RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    return REDISMODULE_OK;\n}\n\nint bpopgt_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\nvoid bpopgt_free_privdata(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_Free(privdata);\n}\n\n/* FSL.BPOPGT <key> <gt> <timeout> - Block clients until list has an element greater than <gt>.\n * When that happens, unblock client and pop the last element (from the right). */\nint fsl_bpopgt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4)\n        return RedisModule_WrongArity(ctx);\n\n    long long gt;\n    if (RedisModule_StringToLongLong(argv[2],&gt) != REDISMODULE_OK)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid integer\");\n\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid timeout\");\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (!fsl || fsl->list[fsl->length-1] <= gt) {\n        /* We use malloc so the tests in blockedonkeys.tcl can check for memory leaks */\n        long long *pgt = RedisModule_Alloc(sizeof(long long));\n        *pgt = gt;\n        RedisModule_BlockClientOnKeys(ctx, bpopgt_reply_callback, bpopgt_timeout_callback,\n                                      bpopgt_free_privdata, timeout, &argv[1], 1, pgt);\n    } else {\n        RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    }\n\n    return REDISMODULE_OK;\n}\n\nint bpoppush_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *src_keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n    RedisModuleString *dst_keyname = RedisModule_GetBlockedClientPrivateData(ctx);\n\n    fsl_t *src;\n    if (!get_fsl(ctx, src_keyname, REDISMODULE_READ, 0, &src, 0) || !src)\n        return REDISMODULE_ERR;\n\n    fsl_t *dst;\n    if (!get_fsl(ctx, dst_keyname, REDISMODULE_WRITE, 1, &dst, 0) || !dst)\n        return REDISMODULE_ERR;\n\n    long long ele = src->list[--src->length];\n    dst->list[dst->length++] = ele;\n    RedisModule_SignalKeyAsReady(ctx, dst_keyname);\n    return RedisModule_ReplyWithLongLong(ctx, ele);\n}\n\nint bpoppush_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\nvoid bpoppush_free_privdata(RedisModuleCtx *ctx, void *privdata) {\n    RedisModule_FreeString(ctx, privdata);\n}\n\n/* FSL.BPOPPUSH <src> <dst> <timeout> - Block clients until <src> has an element.\n * When that happens, unblock client, pop the last element from <src> and push it to <dst>\n * (from the right). */\nint fsl_bpoppush(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4)\n        return RedisModule_WrongArity(ctx);\n\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid timeout\");\n\n    fsl_t *src;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &src, 1))\n        return REDISMODULE_OK;\n\n    if (!src) {\n        /* Retain string for reply callback */\n        RedisModule_RetainString(ctx, argv[2]);\n        /* Key is empty, we must block */\n        RedisModule_BlockClientOnKeys(ctx, bpoppush_reply_callback, bpoppush_timeout_callback,\n                                      bpoppush_free_privdata, timeout, &argv[1], 1, argv[2]);\n    } else {\n        fsl_t *dst;\n        if (!get_fsl(ctx, argv[2], REDISMODULE_WRITE, 1, &dst, 1))\n            return REDISMODULE_OK;\n        long long ele = src->list[--src->length];\n        dst->list[dst->length++] = ele;\n        RedisModule_SignalKeyAsReady(ctx, argv[2]);\n        RedisModule_ReplyWithLongLong(ctx, ele);\n    }\n\n    return REDISMODULE_OK;\n}\n\n/* FSL.GETALL <key> - Reply with an array containing all elements. */\nint fsl_getall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2)\n        return RedisModule_WrongArity(ctx);\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (!fsl)\n        return RedisModule_ReplyWithArray(ctx, 0);\n\n    RedisModule_ReplyWithArray(ctx, fsl->length);\n    for (int i = 0; i < fsl->length; i++)\n        RedisModule_ReplyWithLongLong(ctx, fsl->list[i]);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx, \"blockonkeys\", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModuleTypeMethods tm = {\n        .version = REDISMODULE_TYPE_METHOD_VERSION,\n        .rdb_load = fsl_rdb_load,\n        .rdb_save = fsl_rdb_save,\n        .aof_rewrite = fsl_aofrw,\n        .mem_usage = NULL,\n        .free = fsl_free,\n        .digest = NULL\n    };\n\n    fsltype = RedisModule_CreateDataType(ctx, \"fsltype_t\", 0, &tm);\n    if (fsltype == NULL)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.push\",fsl_push,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.bpop\",fsl_bpop,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.bpopgt\",fsl_bpopgt,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.bpoppush\",fsl_bpoppush,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.getall\",fsl_getall,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/commandfilter.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n#include <string.h>\n\nstatic RedisModuleString *log_key_name;\n\nstatic const char log_command_name[] = \"commandfilter.log\";\nstatic const char ping_command_name[] = \"commandfilter.ping\";\nstatic const char unregister_command_name[] = \"commandfilter.unregister\";\nstatic int in_log_command = 0;\n\nstatic RedisModuleCommandFilter *filter = NULL;\n\nint CommandFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    (void) argc;\n    (void) argv;\n\n    RedisModule_ReplyWithLongLong(ctx,\n            RedisModule_UnregisterCommandFilter(ctx, filter));\n\n    return REDISMODULE_OK;\n}\n\nint CommandFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    (void) argc;\n    (void) argv;\n\n    RedisModuleCallReply *reply = RedisModule_Call(ctx, \"ping\", \"c\", \"@log\");\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithSimpleString(ctx, \"Unknown command or invalid arguments\");\n    }\n\n    return REDISMODULE_OK;\n}\n\nint CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    RedisModuleString *s = RedisModule_CreateString(ctx, \"\", 0);\n\n    int i;\n    for (i = 1; i < argc; i++) {\n        size_t arglen;\n        const char *arg = RedisModule_StringPtrLen(argv[i], &arglen);\n\n        if (i > 1) RedisModule_StringAppendBuffer(ctx, s, \" \", 1);\n        RedisModule_StringAppendBuffer(ctx, s, arg, arglen);\n    }\n\n    RedisModuleKey *log = RedisModule_OpenKey(ctx, log_key_name, REDISMODULE_WRITE|REDISMODULE_READ);\n    RedisModule_ListPush(log, REDISMODULE_LIST_HEAD, s);\n    RedisModule_CloseKey(log);\n    RedisModule_FreeString(ctx, s);\n\n    in_log_command = 1;\n\n    size_t cmdlen;\n    const char *cmdname = RedisModule_StringPtrLen(argv[1], &cmdlen);\n    RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, \"v\", &argv[2], argc - 2);\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithSimpleString(ctx, \"Unknown command or invalid arguments\");\n    }\n\n    in_log_command = 0;\n\n    return REDISMODULE_OK;\n}\n\nvoid CommandFilter_CommandFilter(RedisModuleCommandFilterCtx *filter)\n{\n    if (in_log_command) return;  /* don't process our own RM_Call() from CommandFilter_LogCommand() */\n\n    /* Fun manipulations:\n     * - Remove @delme\n     * - Replace @replaceme\n     * - Append @insertbefore or @insertafter\n     * - Prefix with Log command if @log encounterd\n     */\n    int log = 0;\n    int pos = 0;\n    while (pos < RedisModule_CommandFilterArgsCount(filter)) {\n        const RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, pos);\n        size_t arg_len;\n        const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len);\n\n        if (arg_len == 6 && !memcmp(arg_str, \"@delme\", 6)) {\n            RedisModule_CommandFilterArgDelete(filter, pos);\n            continue;\n        } \n        if (arg_len == 10 && !memcmp(arg_str, \"@replaceme\", 10)) {\n            RedisModule_CommandFilterArgReplace(filter, pos,\n                    RedisModule_CreateString(NULL, \"--replaced--\", 12));\n        } else if (arg_len == 13 && !memcmp(arg_str, \"@insertbefore\", 13)) {\n            RedisModule_CommandFilterArgInsert(filter, pos,\n                    RedisModule_CreateString(NULL, \"--inserted-before--\", 19));\n            pos++;\n        } else if (arg_len == 12 && !memcmp(arg_str, \"@insertafter\", 12)) {\n            RedisModule_CommandFilterArgInsert(filter, pos + 1,\n                    RedisModule_CreateString(NULL, \"--inserted-after--\", 18));\n            pos++;\n        } else if (arg_len == 4 && !memcmp(arg_str, \"@log\", 4)) {\n            log = 1;\n        }\n        pos++;\n    }\n\n    if (log) RedisModule_CommandFilterArgInsert(filter, 0,\n            RedisModule_CreateString(NULL, log_command_name, sizeof(log_command_name)-1));\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (RedisModule_Init(ctx,\"commandfilter\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (argc != 2) {\n        RedisModule_Log(ctx, \"warning\", \"Log key name not specified\");\n        return REDISMODULE_ERR;\n    }\n\n    long long noself = 0;\n    log_key_name = RedisModule_CreateStringFromString(ctx, argv[0]);\n    RedisModule_StringToLongLong(argv[1], &noself);\n\n    if (RedisModule_CreateCommand(ctx,log_command_name,\n                CommandFilter_LogCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,ping_command_name,\n                CommandFilter_PingCommand,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,unregister_command_name,\n                CommandFilter_UnregisterCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter, \n                    noself ? REDISMODULE_CMDFILTER_NOSELF : 0))\n            == NULL) return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnUnload(RedisModuleCtx *ctx) {\n    RedisModule_FreeString(ctx, log_key_name);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/datatype.c",
    "content": "/* This module current tests a small subset but should be extended in the future\n * for general ModuleDataType coverage.\n */\n\n#include \"redismodule.h\"\n\nstatic RedisModuleType *datatype = NULL;\n\ntypedef struct {\n    long long intval;\n    RedisModuleString *strval;\n} DataType;\n\nstatic void *datatype_load(RedisModuleIO *io, int encver) {\n    (void) encver;\n\n    int intval = RedisModule_LoadSigned(io);\n    if (RedisModule_IsIOError(io)) return NULL;\n\n    RedisModuleString *strval = RedisModule_LoadString(io);\n    if (RedisModule_IsIOError(io)) return NULL;\n\n    DataType *dt = (DataType *) RedisModule_Alloc(sizeof(DataType));\n    dt->intval = intval;\n    dt->strval = strval;\n    return dt;\n}\n\nstatic void datatype_save(RedisModuleIO *io, void *value) {\n    DataType *dt = (DataType *) value;\n    RedisModule_SaveSigned(io, dt->intval);\n    RedisModule_SaveString(io, dt->strval);\n}\n\nstatic void datatype_free(void *value) {\n    if (value) {\n        DataType *dt = (DataType *) value;\n\n        if (dt->strval) RedisModule_FreeString(NULL, dt->strval);\n        RedisModule_Free(dt);\n    }\n}\n\nstatic int datatype_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    long long intval;\n\n    if (RedisModule_StringToLongLong(argv[2], &intval) != REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"Invalid integr value\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    DataType *dt = RedisModule_Calloc(sizeof(DataType), 1);\n    dt->intval = intval;\n    dt->strval = argv[3];\n    RedisModule_RetainString(ctx, dt->strval);\n\n    RedisModule_ModuleTypeSetValue(key, datatype, dt);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_restore(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    DataType *dt = RedisModule_LoadDataTypeFromString(argv[2], datatype);\n    if (!dt) {\n        RedisModule_ReplyWithError(ctx, \"Invalid data\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModule_ModuleTypeSetValue(key, datatype, dt);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);\n    DataType *dt = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_CloseKey(key);\n\n    RedisModule_ReplyWithArray(ctx, 2);\n    RedisModule_ReplyWithLongLong(ctx, dt->intval);\n    RedisModule_ReplyWithString(ctx, dt->strval);\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_dump(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);\n    DataType *dt = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_CloseKey(key);\n\n    RedisModuleString *reply = RedisModule_SaveDataTypeToString(ctx, dt, datatype);\n    if (!reply) {\n        RedisModule_ReplyWithError(ctx, \"Failed to save\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModule_ReplyWithString(ctx, reply);\n    RedisModule_FreeString(ctx, reply);\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_swap(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *a = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModuleKey *b = RedisModule_OpenKey(ctx, argv[2], REDISMODULE_WRITE);\n    void *val = RedisModule_ModuleTypeGetValue(a);\n\n    int error = (RedisModule_ModuleTypeReplaceValue(b, datatype, val, &val) == REDISMODULE_ERR ||\n                 RedisModule_ModuleTypeReplaceValue(a, datatype, val, NULL) == REDISMODULE_ERR);\n    if (!error)\n        RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n    else\n        RedisModule_ReplyWithError(ctx, \"ERR failed\");\n\n    RedisModule_CloseKey(a);\n    RedisModule_CloseKey(b);\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"datatype\",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS);\n\n    RedisModuleTypeMethods datatype_methods = {\n        .version = REDISMODULE_TYPE_METHOD_VERSION,\n        .rdb_load = datatype_load,\n        .rdb_save = datatype_save,\n        .free = datatype_free,\n    };\n\n    datatype = RedisModule_CreateDataType(ctx, \"test___dt\", 1, &datatype_methods);\n    if (datatype == NULL)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.set\", datatype_set,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.get\", datatype_get,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.restore\", datatype_restore,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.dump\", datatype_dump,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.swap\", datatype_swap,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/fork.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n\n/* define macros for having usleep */\n#define _BSD_SOURCE\n#define _DEFAULT_SOURCE\n\n#include \"redismodule.h\"\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\n#define UNUSED(V) ((void) V)\n\nint child_pid = -1;\nint exitted_with_code = -1;\n\nvoid done_handler(int exitcode, int bysignal, void *user_data) {\n    child_pid = -1;\n    exitted_with_code = exitcode;\n    assert(user_data==(void*)0xdeadbeef);\n    UNUSED(bysignal);\n}\n\nint fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    long long code_to_exit_with;\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModule_StringToLongLong(argv[1], &code_to_exit_with);\n    exitted_with_code = -1;\n    child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef);\n    if (child_pid < 0) {\n        RedisModule_ReplyWithError(ctx, \"Fork failed\");\n        return REDISMODULE_OK;\n    } else if (child_pid > 0) {\n        /* parent */\n        RedisModule_ReplyWithLongLong(ctx, child_pid);\n        return REDISMODULE_OK;\n    }\n\n    /* child */\n    RedisModule_Log(ctx, \"notice\", \"fork child started\");\n    usleep(500000);\n    RedisModule_Log(ctx, \"notice\", \"fork child exiting\");\n    RedisModule_ExitFromChild(code_to_exit_with);\n    /* unreachable */\n    return 0;\n}\n\nint fork_exitcode(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    UNUSED(argv);\n    UNUSED(argc);\n    RedisModule_ReplyWithLongLong(ctx, exitted_with_code);\n    return REDISMODULE_OK;\n}\n\nint fork_kill(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    UNUSED(argv);\n    UNUSED(argc);\n    if (RedisModule_KillForkChild(child_pid) != REDISMODULE_OK)\n        RedisModule_ReplyWithError(ctx, \"KillForkChild failed\");\n    else\n        RedisModule_ReplyWithLongLong(ctx, 1);\n    child_pid = -1;\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    UNUSED(argv);\n    UNUSED(argc);\n    if (RedisModule_Init(ctx,\"fork\",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fork.create\", fork_create,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fork.exitcode\", fork_exitcode,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fork.kill\", fork_kill,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/hooks.c",
    "content": "/* This module is used to test the server events hooks API.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2019, 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 \"redismodule.h\"\n#include <stdio.h>\n#include <string.h>\n\n/* We need to store events to be able to test and see what we got, and we can't\n * store them in the key-space since that would mess up rdb loading (duplicates)\n * and be lost of flushdb. */\nRedisModuleDict *event_log = NULL;\n\ntypedef struct EventElement {\n    long count;\n    RedisModuleString *last_val_string;\n    long last_val_int;\n} EventElement;\n\nvoid LogStringEvent(RedisModuleCtx *ctx, const char* keyname, const char* data) {\n    EventElement *event = RedisModule_DictGetC(event_log, (void*)keyname, strlen(keyname), NULL);\n    if (!event) {\n        event = RedisModule_Alloc(sizeof(EventElement));\n        memset(event, 0, sizeof(EventElement));\n        RedisModule_DictSetC(event_log, (void*)keyname, strlen(keyname), event);\n    }\n    if (event->last_val_string) RedisModule_FreeString(ctx, event->last_val_string);\n    event->last_val_string = RedisModule_CreateString(ctx, data, strlen(data));\n    event->count++;\n}\n\nvoid LogNumericEvent(RedisModuleCtx *ctx, const char* keyname, long data) {\n    REDISMODULE_NOT_USED(ctx);\n    EventElement *event = RedisModule_DictGetC(event_log, (void*)keyname, strlen(keyname), NULL);\n    if (!event) {\n        event = RedisModule_Alloc(sizeof(EventElement));\n        memset(event, 0, sizeof(EventElement));\n        RedisModule_DictSetC(event_log, (void*)keyname, strlen(keyname), event);\n    }\n    event->last_val_int = data;\n    event->count++;\n}\n\nvoid FreeEvent(RedisModuleCtx *ctx, EventElement *event) {\n    if (event->last_val_string)\n        RedisModule_FreeString(ctx, event->last_val_string);\n    RedisModule_Free(event);\n}\n\nint cmdEventCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    EventElement *event = RedisModule_DictGet(event_log, argv[1], NULL);\n    RedisModule_ReplyWithLongLong(ctx, event? event->count: 0);\n    return REDISMODULE_OK;\n}\n\nint cmdEventLast(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    EventElement *event = RedisModule_DictGet(event_log, argv[1], NULL);\n    if (event && event->last_val_string)\n        RedisModule_ReplyWithString(ctx, event->last_val_string);\n    else if (event)\n        RedisModule_ReplyWithLongLong(ctx, event->last_val_int);\n    else\n        RedisModule_ReplyWithNull(ctx);\n    return REDISMODULE_OK;\n}\n\nvoid clearEvents(RedisModuleCtx *ctx)\n{\n    RedisModuleString *key;\n    EventElement *event;\n    RedisModuleDictIter *iter = RedisModule_DictIteratorStart(event_log, \"^\", NULL);\n    while((key = RedisModule_DictNext(ctx, iter, (void**)&event)) != NULL) {\n        event->count = 0;\n        event->last_val_int = 0;\n        if (event->last_val_string) RedisModule_FreeString(ctx, event->last_val_string);\n        event->last_val_string = NULL;\n        RedisModule_DictDel(event_log, key, NULL);\n        RedisModule_Free(event);\n    }\n    RedisModule_DictIteratorStop(iter);\n}\n\nint cmdEventsClear(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argc);\n    REDISMODULE_NOT_USED(argv);\n    clearEvents(ctx);\n    return REDISMODULE_OK;\n}\n\n/* Client state change callback. */\nvoid clientChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleClientInfo *ci = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED) ?\n        \"client-connected\" : \"client-disconnected\";\n    LogNumericEvent(ctx, keyname, ci->id);\n}\n\nvoid flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleFlushInfo *fi = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) ?\n        \"flush-start\" : \"flush-end\";\n    LogNumericEvent(ctx, keyname, fi->dbnum);\n}\n\nvoid roleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    RedisModuleReplicationInfo *ri = data;\n    char *keyname = (sub == REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER) ?\n        \"role-master\" : \"role-replica\";\n    LogStringEvent(ctx, keyname, ri->masterhost);\n}\n\nvoid replicationChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = (sub == REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE) ?\n        \"replica-online\" : \"replica-offline\";\n    LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid rasterLinkChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = (sub == REDISMODULE_SUBEVENT_MASTER_LINK_UP) ?\n        \"masterlink-up\" : \"masterlink-down\";\n    LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid persistenceCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = NULL;\n    switch (sub) {\n        case REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START: keyname = \"persistence-rdb-start\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START: keyname = \"persistence-aof-start\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START: keyname = \"persistence-syncrdb-start\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_ENDED: keyname = \"persistence-end\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_FAILED: keyname = \"persistence-failed\"; break;\n    }\n    /* modifying the keyspace from the fork child is not an option, using log instead */\n    RedisModule_Log(ctx, \"warning\", \"module-event-%s\", keyname);\n    if (sub == REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START)\n        LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid loadingCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = NULL;\n    switch (sub) {\n        case REDISMODULE_SUBEVENT_LOADING_RDB_START: keyname = \"loading-rdb-start\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_AOF_START: keyname = \"loading-aof-start\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_REPL_START: keyname = \"loading-repl-start\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_ENDED: keyname = \"loading-end\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_FAILED: keyname = \"loading-failed\"; break;\n    }\n    LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid loadingProgressCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleLoadingProgress *ei = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB) ?\n        \"loading-progress-rdb\" : \"loading-progress-aof\";\n    LogNumericEvent(ctx, keyname, ei->progress);\n}\n\nvoid shutdownCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n    REDISMODULE_NOT_USED(sub);\n\n    RedisModule_Log(ctx, \"warning\", \"module-event-%s\", \"shutdown\");\n}\n\nvoid cronLoopCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(sub);\n\n    RedisModuleCronLoop *ei = data;\n    LogNumericEvent(ctx, \"cron-loop\", ei->hz);\n}\n\nvoid moduleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleModuleChange *ei = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_MODULE_LOADED) ?\n        \"module-loaded\" : \"module-unloaded\";\n    LogStringEvent(ctx, keyname, ei->module_name);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"testhook\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    /* replication related hooks */\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ReplicationRoleChanged, roleChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ReplicaChange, replicationChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_MasterLinkChange, rasterLinkChangeCallback);\n\n    /* persistence related hooks */\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_Persistence, persistenceCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_Loading, loadingCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_LoadingProgress, loadingProgressCallback);\n\n    /* other hooks */\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ClientChange, clientChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_FlushDB, flushdbCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_Shutdown, shutdownCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_CronLoop, cronLoopCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ModuleChange, moduleChangeCallback);\n\n    event_log = RedisModule_CreateDict(ctx);\n\n    if (RedisModule_CreateCommand(ctx,\"hooks.event_count\", cmdEventCount,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"hooks.event_last\", cmdEventLast,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"hooks.clear\", cmdEventsClear,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnUnload(RedisModuleCtx *ctx) {\n    clearEvents(ctx);\n    RedisModule_FreeDict(ctx, event_log);\n    event_log = NULL;\n    return REDISMODULE_OK;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/infotest.c",
    "content": "#include \"redismodule.h\"\n\n#include <string.h>\n\nvoid InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) {\n    RedisModule_InfoAddSection(ctx, \"\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"global\", -2);\n    RedisModule_InfoAddFieldULongLong(ctx, \"uglobal\", (unsigned long long)-2);\n\n    RedisModule_InfoAddSection(ctx, \"Spanish\");\n    RedisModule_InfoAddFieldCString(ctx, \"uno\", \"one\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"dos\", 2);\n\n    RedisModule_InfoAddSection(ctx, \"Italian\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"due\", 2);\n    RedisModule_InfoAddFieldDouble(ctx, \"tre\", 3.3);\n\n    RedisModule_InfoAddSection(ctx, \"keyspace\");\n    RedisModule_InfoBeginDictField(ctx, \"db0\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"keys\", 3);\n    RedisModule_InfoAddFieldLongLong(ctx, \"expires\", 1);\n    RedisModule_InfoEndDictField(ctx);\n\n    if (for_crash_report) {\n        RedisModule_InfoAddSection(ctx, \"Klingon\");\n        RedisModule_InfoAddFieldCString(ctx, \"one\", \"wa’\");\n        RedisModule_InfoAddFieldCString(ctx, \"two\", \"cha’\");\n        RedisModule_InfoAddFieldCString(ctx, \"three\", \"wej\");\n    }\n\n}\n\nint info_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, char field_type)\n{\n    if (argc != 3 && argc != 4) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    int err = REDISMODULE_OK;\n    const char *section, *field;\n    section = RedisModule_StringPtrLen(argv[1], NULL);\n    field = RedisModule_StringPtrLen(argv[2], NULL);\n    RedisModuleServerInfoData *info = RedisModule_GetServerInfo(ctx, section);\n    if (field_type=='i') {\n        long long ll = RedisModule_ServerInfoGetFieldSigned(info, field, &err);\n        if (err==REDISMODULE_OK)\n            RedisModule_ReplyWithLongLong(ctx, ll);\n    } else if (field_type=='u') {\n        unsigned long long ll = (unsigned long long)RedisModule_ServerInfoGetFieldUnsigned(info, field, &err);\n        if (err==REDISMODULE_OK)\n            RedisModule_ReplyWithLongLong(ctx, ll);\n    } else if (field_type=='d') {\n        double d = RedisModule_ServerInfoGetFieldDouble(info, field, &err);\n        if (err==REDISMODULE_OK)\n            RedisModule_ReplyWithDouble(ctx, d);\n    } else if (field_type=='c') {\n        const char *str = RedisModule_ServerInfoGetFieldC(info, field);\n        if (str)\n            RedisModule_ReplyWithCString(ctx, str);\n    } else {\n        RedisModuleString *str = RedisModule_ServerInfoGetField(ctx, info, field);\n        if (str) {\n            RedisModule_ReplyWithString(ctx, str);\n            RedisModule_FreeString(ctx, str);\n        } else\n            err=REDISMODULE_ERR;\n    }\n    if (err!=REDISMODULE_OK)\n        RedisModule_ReplyWithError(ctx, \"not found\");\n    RedisModule_FreeServerInfo(ctx, info);\n    return REDISMODULE_OK;\n}\n\nint info_gets(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 's');\n}\n\nint info_getc(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'c');\n}\n\nint info_geti(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'i');\n}\n\nint info_getu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'u');\n}\n\nint info_getd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'd');\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    if (RedisModule_Init(ctx,\"infotest\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_RegisterInfoFunc(ctx, InfoFunc) == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"info.gets\", info_gets,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.getc\", info_getc,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.geti\", info_geti,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.getu\", info_getu,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.getd\", info_getd,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/misc.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n#include <errno.h>\n\n#define UNUSED(x) (void)(x)\n\nint test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    const char* cmdname = RedisModule_StringPtrLen(argv[1], NULL);\n    RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, \"v\", argv+2, argc-2);\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithError(ctx, strerror(errno));\n    }\n    return REDISMODULE_OK;\n}\n\nint test_call_info(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    RedisModuleCallReply *reply;\n    if (argc>1)\n        reply = RedisModule_Call(ctx, \"info\", \"s\", argv[1]);\n    else\n        reply = RedisModule_Call(ctx, \"info\", \"\");\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithError(ctx, strerror(errno));\n    }\n    return REDISMODULE_OK;\n}\n\nint test_ld_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    UNUSED(argv);\n    UNUSED(argc);\n    long double ld = 0.00000000000000001L;\n    const char *ldstr = \"0.00000000000000001\";\n    RedisModuleString *s1 = RedisModule_CreateStringFromLongDouble(ctx, ld, 1);\n    RedisModuleString *s2 =\n        RedisModule_CreateString(ctx, ldstr, strlen(ldstr));\n    if (RedisModule_StringCompare(s1, s2) != 0) {\n        char err[4096];\n        snprintf(err, 4096,\n            \"Failed to convert long double to string ('%s' != '%s')\",\n            RedisModule_StringPtrLen(s1, NULL),\n            RedisModule_StringPtrLen(s2, NULL));\n        RedisModule_ReplyWithError(ctx, err);\n        goto final;\n    }\n    long double ld2 = 0;\n    if (RedisModule_StringToLongDouble(s2, &ld2) == REDISMODULE_ERR) {\n        RedisModule_ReplyWithError(ctx,\n            \"Failed to convert string to long double\");\n        goto final;\n    }\n    if (ld2 != ld) {\n        char err[4096];\n        snprintf(err, 4096,\n            \"Failed to convert string to long double (%.40Lf != %.40Lf)\",\n            ld2,\n            ld);\n        RedisModule_ReplyWithError(ctx, err);\n        goto final;\n    }\n\n    /* Make sure we can't convert a string that has \\0 in it */\n    char buf[4] = \"123\";\n    buf[1] = '\\0';\n    RedisModuleString *s3 = RedisModule_CreateString(ctx, buf, 3);\n    long double ld3;\n    if (RedisModule_StringToLongDouble(s3, &ld3) == REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"Invalid string successfully converted to long double\");\n        RedisModule_FreeString(ctx, s3);\n        goto final;\n    }\n    RedisModule_FreeString(ctx, s3);\n\n    RedisModule_ReplyWithLongDouble(ctx, ld2);\nfinal:\n    RedisModule_FreeString(ctx, s1);\n    RedisModule_FreeString(ctx, s2);\n    return REDISMODULE_OK;\n}\n\nint test_flushall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModule_ResetDataset(1, 0);\n    RedisModule_ReplyWithCString(ctx, \"Ok\");\n    return REDISMODULE_OK;\n}\n\nint test_dbsize(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    long long ll = RedisModule_DbSize(ctx);\n    RedisModule_ReplyWithLongLong(ctx, ll);\n    return REDISMODULE_OK;\n}\n\nint test_randomkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *str = RedisModule_RandomKey(ctx);\n    RedisModule_ReplyWithString(ctx, str);\n    RedisModule_FreeString(ctx, str);\n    return REDISMODULE_OK;\n}\n\nRedisModuleKey *open_key_or_reply(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) {\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);\n    if (!key) {\n        RedisModule_ReplyWithError(ctx, \"key not found\");\n        return NULL;\n    }\n    return key;\n}\n\nint test_getlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lru;\n    RedisModule_GetLRU(key, &lru);\n    RedisModule_ReplyWithLongLong(ctx, lru);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint test_setlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lru;\n    if (RedisModule_StringToLongLong(argv[2], &lru) != REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"invalid idle time\");\n        return REDISMODULE_OK;\n    }\n    int was_set = RedisModule_SetLRU(key, lru)==REDISMODULE_OK;\n    RedisModule_ReplyWithLongLong(ctx, was_set);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint test_getlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lfu;\n    RedisModule_GetLFU(key, &lfu);\n    RedisModule_ReplyWithLongLong(ctx, lfu);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lfu;\n    if (RedisModule_StringToLongLong(argv[2], &lfu) != REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"invalid freq\");\n        return REDISMODULE_OK;\n    }\n    int was_set = RedisModule_SetLFU(key, lfu)==REDISMODULE_OK;\n    RedisModule_ReplyWithLongLong(ctx, was_set);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    if (RedisModule_Init(ctx,\"misc\",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.call_generic\", test_call_generic,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.call_info\", test_call_info,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.ld_conversion\", test_ld_conv, \"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.flushall\", test_flushall,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.dbsize\", test_dbsize,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.randomkey\", test_randomkey,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.setlru\", test_setlru,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.getlru\", test_getlru,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.setlfu\", test_setlfu,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.getlfu\", test_getlfu,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/propagate.c",
    "content": "/* This module is used to test the propagation (replication + AOF) of\n * commands, via the RedisModule_Replicate() interface, in asynchronous\n * contexts, such as callbacks not implementing commands, and thread safe\n * contexts.\n *\n * We create a timer callback and a threads using a thread safe context.\n * Using both we try to propagate counters increments, and later we check\n * if the replica contains the changes as expected.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2019, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n#include <pthread.h>\n\n/* Timer callback. */\nvoid timerHandler(RedisModuleCtx *ctx, void *data) {\n    REDISMODULE_NOT_USED(ctx);\n    REDISMODULE_NOT_USED(data);\n\n    static int times = 0;\n\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"timer\");\n    times++;\n\n    if (times < 10)\n        RedisModule_CreateTimer(ctx,100,timerHandler,NULL);\n    else\n        times = 0;\n}\n\n/* The thread entry point. */\nvoid *threadMain(void *arg) {\n    REDISMODULE_NOT_USED(arg);\n    RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(NULL);\n    RedisModule_SelectDb(ctx,9); /* Tests ran in database number 9. */\n    for (int i = 0; i < 10; i++) {\n        RedisModule_ThreadSafeContextLock(ctx);\n        RedisModule_Replicate(ctx,\"INCR\",\"c\",\"a-from-thread\");\n        RedisModule_Replicate(ctx,\"INCR\",\"c\",\"b-from-thread\");\n        RedisModule_ThreadSafeContextUnlock(ctx);\n    }\n    RedisModule_FreeThreadSafeContext(ctx);\n    return NULL;\n}\n\nint propagateTestCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleTimerID timer_id =\n        RedisModule_CreateTimer(ctx,100,timerHandler,NULL);\n    REDISMODULE_NOT_USED(timer_id);\n\n    pthread_t tid;\n    if (pthread_create(&tid,NULL,threadMain,NULL) != 0)\n        return RedisModule_ReplyWithError(ctx,\"-ERR Can't start thread\");\n    REDISMODULE_NOT_USED(tid);\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n}\n\nint propagateTest2Command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    /* Replicate two commands to test MULTI/EXEC wrapping. */\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-1\");\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-2\");\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n}\n\nint propagateTest3Command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleCallReply *reply;\n\n    /* This test mixes multiple propagation systems. */\n    reply = RedisModule_Call(ctx, \"INCR\", \"c!\", \"using-call\");\n    RedisModule_FreeCallReply(reply);\n\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-1\");\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-2\");\n\n    reply = RedisModule_Call(ctx, \"INCR\", \"c!\", \"after-call\");\n    RedisModule_FreeCallReply(reply);\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"propagate-test\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"propagate-test\",\n                propagateTestCommand,\n                \"\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"propagate-test-2\",\n                propagateTest2Command,\n                \"\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"propagate-test-3\",\n                propagateTest3Command,\n                \"\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/scan.c",
    "content": "#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\ntypedef struct {\n    size_t nkeys;\n} scan_strings_pd;\n\nvoid scan_strings_callback(RedisModuleCtx *ctx, RedisModuleString* keyname, RedisModuleKey* key, void *privdata) {\n    scan_strings_pd* pd = privdata;\n    int was_opened = 0;\n    if (!key) {\n        key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ);\n        was_opened = 1;\n    }\n\n    if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING) {\n        size_t len;\n        char * data = RedisModule_StringDMA(key, &len, REDISMODULE_READ);\n        RedisModule_ReplyWithArray(ctx, 2);\n        RedisModule_ReplyWithString(ctx, keyname);\n        RedisModule_ReplyWithStringBuffer(ctx, data, len);\n        pd->nkeys++;\n    }\n    if (was_opened)\n        RedisModule_CloseKey(key);\n}\n\nint scan_strings(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    scan_strings_pd pd = {\n        .nkeys = 0,\n    };\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);\n\n    RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate();\n    while(RedisModule_Scan(ctx, cursor, scan_strings_callback, &pd));\n    RedisModule_ScanCursorDestroy(cursor);\n\n    RedisModule_ReplySetArrayLength(ctx, pd.nkeys);\n    return REDISMODULE_OK;\n}\n\ntypedef struct {\n    RedisModuleCtx *ctx;\n    size_t nreplies;\n} scan_key_pd;\n\nvoid scan_key_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata) {\n    REDISMODULE_NOT_USED(key);\n    scan_key_pd* pd = privdata;\n    RedisModule_ReplyWithArray(pd->ctx, 2);\n    RedisModule_ReplyWithString(pd->ctx, field);\n    if (value)\n        RedisModule_ReplyWithString(pd->ctx, value);\n    else\n        RedisModule_ReplyWithNull(pd->ctx);\n    pd->nreplies++;\n}\n\nint scan_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    scan_key_pd pd = {\n        .ctx = ctx,\n        .nreplies = 0,\n    };\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);\n    if (!key) {\n        RedisModule_ReplyWithError(ctx, \"not found\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);\n\n    RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate();\n    while(RedisModule_ScanKey(key, cursor, scan_key_callback, &pd));\n    RedisModule_ScanCursorDestroy(cursor);\n\n    RedisModule_ReplySetArrayLength(ctx, pd.nreplies);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    if (RedisModule_Init(ctx, \"scan\", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx, \"scan.scan_strings\", scan_strings, \"\", 0, 0, 0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx, \"scan.scan_key\", scan_key, \"\", 0, 0, 0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/modules/testrdb.c",
    "content": "#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n\n/* Module configuration, save aux or not? */\nlong long conf_aux_count = 0;\n\n/* Registered type */\nRedisModuleType *testrdb_type = NULL;\n\n/* Global values to store and persist to aux */\nRedisModuleString *before_str = NULL;\nRedisModuleString *after_str = NULL;\n\nvoid *testrdb_type_load(RedisModuleIO *rdb, int encver) {\n    int count = RedisModule_LoadSigned(rdb);\n    RedisModuleString *str = RedisModule_LoadString(rdb);\n    float f = RedisModule_LoadFloat(rdb);\n    long double ld = RedisModule_LoadLongDouble(rdb);\n    if (RedisModule_IsIOError(rdb)) {\n        RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb);\n        if (str)\n            RedisModule_FreeString(ctx, str);\n        return NULL;\n    }\n    /* Using the values only after checking for io errors. */\n    assert(count==1);\n    assert(encver==1);\n    assert(f==1.5f);\n    assert(ld==0.333333333333333333L);\n    return str;\n}\n\nvoid testrdb_type_save(RedisModuleIO *rdb, void *value) {\n    RedisModuleString *str = (RedisModuleString*)value;\n    RedisModule_SaveSigned(rdb, 1);\n    RedisModule_SaveString(rdb, str);\n    RedisModule_SaveFloat(rdb, 1.5);\n    RedisModule_SaveLongDouble(rdb, 0.333333333333333333L);\n}\n\nvoid testrdb_aux_save(RedisModuleIO *rdb, int when) {\n    if (conf_aux_count==1) assert(when == REDISMODULE_AUX_AFTER_RDB);\n    if (conf_aux_count==0) assert(0);\n    if (when == REDISMODULE_AUX_BEFORE_RDB) {\n        if (before_str) {\n            RedisModule_SaveSigned(rdb, 1);\n            RedisModule_SaveString(rdb, before_str);\n        } else {\n            RedisModule_SaveSigned(rdb, 0);\n        }\n    } else {\n        if (after_str) {\n            RedisModule_SaveSigned(rdb, 1);\n            RedisModule_SaveString(rdb, after_str);\n        } else {\n            RedisModule_SaveSigned(rdb, 0);\n        }\n    }\n}\n\nint testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) {\n    assert(encver == 1);\n    if (conf_aux_count==1) assert(when == REDISMODULE_AUX_AFTER_RDB);\n    if (conf_aux_count==0) assert(0);\n    RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb);\n    if (when == REDISMODULE_AUX_BEFORE_RDB) {\n        if (before_str)\n            RedisModule_FreeString(ctx, before_str);\n        before_str = NULL;\n        int count = RedisModule_LoadSigned(rdb);\n        if (RedisModule_IsIOError(rdb))\n            return REDISMODULE_ERR;\n        if (count)\n            before_str = RedisModule_LoadString(rdb);\n    } else {\n        if (after_str)\n            RedisModule_FreeString(ctx, after_str);\n        after_str = NULL;\n        int count = RedisModule_LoadSigned(rdb);\n        if (RedisModule_IsIOError(rdb))\n            return REDISMODULE_ERR;\n        if (count)\n            after_str = RedisModule_LoadString(rdb);\n    }\n    if (RedisModule_IsIOError(rdb))\n        return REDISMODULE_ERR;\n    return REDISMODULE_OK;\n}\n\nvoid testrdb_type_free(void *value) {\n    if (value)\n        RedisModule_FreeString(NULL, (RedisModuleString*)value);\n}\n\nint testrdb_set_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    if (before_str)\n        RedisModule_FreeString(ctx, before_str);\n    before_str = argv[1];\n    RedisModule_RetainString(ctx, argv[1]);\n    RedisModule_ReplyWithLongLong(ctx, 1);\n    return REDISMODULE_OK;\n}\n\nint testrdb_get_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    if (argc != 1){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    if (before_str)\n        RedisModule_ReplyWithString(ctx, before_str);\n    else\n        RedisModule_ReplyWithStringBuffer(ctx, \"\", 0);\n    return REDISMODULE_OK;\n}\n\nint testrdb_set_after(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    if (after_str)\n        RedisModule_FreeString(ctx, after_str);\n    after_str = argv[1];\n    RedisModule_RetainString(ctx, argv[1]);\n    RedisModule_ReplyWithLongLong(ctx, 1);\n    return REDISMODULE_OK;\n}\n\nint testrdb_get_after(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    if (argc != 1){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    if (after_str)\n        RedisModule_ReplyWithString(ctx, after_str);\n    else\n        RedisModule_ReplyWithStringBuffer(ctx, \"\", 0);\n    return REDISMODULE_OK;\n}\n\nint testrdb_set_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModuleString *str = RedisModule_ModuleTypeGetValue(key);\n    if (str)\n        RedisModule_FreeString(ctx, str);\n    RedisModule_ModuleTypeSetValue(key, testrdb_type, argv[2]);\n    RedisModule_RetainString(ctx, argv[2]);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithLongLong(ctx, 1);\n    return REDISMODULE_OK;\n}\n\nint testrdb_get_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModuleString *str = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithString(ctx, str);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"testrdb\",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS);\n\n    if (argc > 0)\n        RedisModule_StringToLongLong(argv[0], &conf_aux_count);\n\n    if (conf_aux_count==0) {\n        RedisModuleTypeMethods datatype_methods = {\n            .version = 1,\n            .rdb_load = testrdb_type_load,\n            .rdb_save = testrdb_type_save,\n            .aof_rewrite = NULL,\n            .digest = NULL,\n            .free = testrdb_type_free,\n        };\n\n        testrdb_type = RedisModule_CreateDataType(ctx, \"test__rdb\", 1, &datatype_methods);\n        if (testrdb_type == NULL)\n            return REDISMODULE_ERR;\n    } else {\n        RedisModuleTypeMethods datatype_methods = {\n            .version = REDISMODULE_TYPE_METHOD_VERSION,\n            .rdb_load = testrdb_type_load,\n            .rdb_save = testrdb_type_save,\n            .aof_rewrite = NULL,\n            .digest = NULL,\n            .free = testrdb_type_free,\n            .aux_load = testrdb_aux_load,\n            .aux_save = testrdb_aux_save,\n            .aux_save_triggers = (conf_aux_count == 1 ?\n                                  REDISMODULE_AUX_AFTER_RDB :\n                                  REDISMODULE_AUX_BEFORE_RDB | REDISMODULE_AUX_AFTER_RDB)\n        };\n\n        testrdb_type = RedisModule_CreateDataType(ctx, \"test__rdb\", 1, &datatype_methods);\n        if (testrdb_type == NULL)\n            return REDISMODULE_ERR;\n    }\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.set.before\", testrdb_set_before,\"deny-oom\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.get.before\", testrdb_get_before,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.set.after\", testrdb_set_after,\"deny-oom\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.get.after\", testrdb_get_after,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.set.key\", testrdb_set_key,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.get.key\", testrdb_get_key,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnUnload(RedisModuleCtx *ctx) {\n    if (before_str)\n        RedisModule_FreeString(ctx, before_str);\n    if (after_str)\n        RedisModule_FreeString(ctx, after_str);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/sentinel/run.tcl",
    "content": "# Sentinel test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This software 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.\nset ::tlsdir \"../../tls\"\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    end_tests\n}\n\nif {[catch main e]} {\n    puts $::errorInfo\n    cleanup\n    exit 1\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 receive 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 receive 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": "Chapter02/redis-6.0.3/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 receive 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 receive failover info\"\n    }\n}\n\ntest \"New master [join $addr {:}] role matches\" {\n    assert {[RI $master_id role] eq {master}}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 02_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                    ([RI $id master_link_status] eq {up})\n                } else {\n                    fail \"Redis slave $id is replicating from wrong master\"\n                }\n            }\n        }\n    }\n}\n\nproc 02_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 receive 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\n02_test_slaves_replication\n02_crash_and_failover\n02_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\n02_crash_and_failover\n02_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.\n02_test_slaves_replication\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/sentinel/tests/03-runtime-reconf.tcl",
    "content": "# Test runtime reconfiguration command SENTINEL SET.\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/tests/sentinel/tests/05-manual.tcl",
    "content": "# Test manual failover\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Manual failover works\" {\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    catch {S 0 SENTINEL FAILOVER mymaster} reply\n    assert {$reply eq \"OK\"}\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 receive failover info\"\n        }\n    }\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\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/sentinel/tests/06-ckquorum.tcl",
    "content": "# Test for the SENTINEL CKQUORUM command\n\nsource \"../tests/includes/init-tests.tcl\"\nset num_sentinels [llength $::sentinel_instances]\n\ntest \"CKQUORUM reports OK and the right amount of Sentinels\" {\n    foreach_sentinel_id id {\n        assert_match \"*OK $num_sentinels usable*\" [S $id SENTINEL CKQUORUM mymaster]\n    }\n}\n\ntest \"CKQUORUM detects quorum cannot be reached\" {\n    set orig_quorum [expr {$num_sentinels/2+1}]\n    S 0 SENTINEL SET mymaster quorum [expr {$num_sentinels+1}]\n    catch {[S 0 SENTINEL CKQUORUM mymaster]} err\n    assert_match \"*NOQUORUM*\" $err\n    S 0 SENTINEL SET mymaster quorum $orig_quorum\n}\n\ntest \"CKQUORUM detects failover authorization cannot be reached\" {\n    set orig_quorum [expr {$num_sentinels/2+1}]\n    S 0 SENTINEL SET mymaster quorum 1\n    kill_instance sentinel 1\n    kill_instance sentinel 2\n    kill_instance sentinel 3\n    after 5000\n    catch {[S 0 SENTINEL CKQUORUM mymaster]} err\n    assert_match \"*NOQUORUM*\" $err\n    S 0 SENTINEL SET mymaster quorum $orig_quorum\n    restart_instance sentinel 1\n    restart_instance sentinel 2\n    restart_instance sentinel 3\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/sentinel/tests/07-down-conditions.tcl",
    "content": "# Test conditions where an instance is considered to be down\n\nsource \"../tests/includes/init-tests.tcl\"\nsource \"../../../tests/support/cli.tcl\"\n\nproc ensure_master_up {} {\n    wait_for_condition 1000 50 {\n        [dict get [S 4 sentinel master mymaster] flags] eq \"master\"\n    } else {\n        fail \"Master flags are not just 'master'\"\n    }\n}\n\nproc ensure_master_down {} {\n    wait_for_condition 1000 50 {\n        [string match *down* \\\n            [dict get [S 4 sentinel master mymaster] flags]]\n    } else {\n        fail \"Master is not flagged SDOWN\"\n    }\n}\n\ntest \"Crash the majority of Sentinels to prevent failovers for this unit\" {\n    for {set id 0} {$id < $quorum} {incr id} {\n        kill_instance sentinel $id\n    }\n}\n\ntest \"SDOWN is triggered by non-responding but not crashed instance\" {\n    lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port\n    ensure_master_up\n    exec ../../../src/redis-cli -h $host -p $port {*}[rediscli_tls_config \"../../../tests\"] debug sleep 10 > /dev/null &\n    ensure_master_down\n    ensure_master_up\n}\n\ntest \"SDOWN is triggered by crashed instance\" {\n    lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port\n    ensure_master_up\n    kill_instance redis 0\n    ensure_master_down\n    restart_instance redis 0\n    ensure_master_up\n}\n\ntest \"SDOWN is triggered by masters advertising as slaves\" {\n    ensure_master_up\n    R 0 slaveof 127.0.0.1 34567\n    ensure_master_down\n    R 0 slaveof no one\n    ensure_master_up\n}\n\ntest \"SDOWN is triggered by misconfigured instance repling with errors\" {\n    ensure_master_up\n    set orig_dir [lindex [R 0 config get dir] 1]\n    set orig_save [lindex [R 0 config get save] 1]\n    # Set dir to / and filename to \"tmp\" to make sure it will fail.\n    R 0 config set dir /\n    R 0 config set dbfilename tmp\n    R 0 config set save \"1000000 1000000\"\n    R 0 bgsave\n    ensure_master_down\n    R 0 config set save $orig_save\n    R 0 config set dir $orig_dir\n    R 0 config set dbfilename dump.rdb\n    R 0 bgsave\n    ensure_master_up\n}\n\n# We use this test setup to also test command renaming, as a side\n# effect of the master going down if we send PONG instead of PING\ntest \"SDOWN is triggered if we rename PING to PONG\" {\n    ensure_master_up\n    S 4 SENTINEL SET mymaster rename-command PING PONG\n    ensure_master_down\n    S 4 SENTINEL SET mymaster rename-command PING PING\n    ensure_master_up\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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\ntest \"(init) Remove old master entry from sentinels\" {\n    foreach_sentinel_id id {\n        catch {S $id SENTINEL REMOVE mymaster}\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        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 1000 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 1000 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 1000 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": "Chapter02/redis-6.0.3/tests/sentinel/tmp/.gitignore",
    "content": "redis_*\nsentinel_*\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/support/cli.tcl",
    "content": "proc rediscli_tls_config {testsdir} {\n    set tlsdir [file join $testsdir tls]\n    set cert [file join $tlsdir redis.crt]\n    set key [file join $tlsdir redis.key]\n    set cacert [file join $tlsdir ca.crt]\n\n    if {$::tls} {\n        return [list --tls --cert $cert --key $key --cacert $cacert]\n    } else {\n        return {}\n    }\n}\n\nproc rediscli {port {opts {}}} {\n    set cmd [list src/redis-cli -p $port]\n    lappend cmd {*}[rediscli_tls_config \"tests\"]\n    lappend cmd {*}$opts\n    return $cmd\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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        set ip_port [lindex [split $start_node @] 0]\n        lassign [split $ip_port :] start_host start_port\n        if {[catch {\n            set r {}\n            set r [redis $start_host $start_port 0 $::tls]\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 \" $ip_port: $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        set addr [lindex [split $addr @] 0]\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 0 $::tls]}\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        eval {return [lrange $argv 2 1+[lindex $argv 1]]}\n        evalsha {return [lrange $argv 2 1+[lindex $argv 1]]}\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": "Chapter02/redis-6.0.3/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::addr {}\narray set ::redis::blocking {}\narray set ::redis::deferred {}\narray set ::redis::reconnect {}\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} {tls 0} {tlsoptions {}}} {\n    if {$tls} {\n        package require tls\n        ::tls::init \\\n            -cafile \"$::tlsdir/ca.crt\" \\\n            -certfile \"$::tlsdir/redis.crt\" \\\n            -keyfile \"$::tlsdir/redis.key\" \\\n            {*}$tlsoptions\n        set fd [::tls::socket $server $port]\n    } else {\n        set fd [socket $server $port]\n    }\n    fconfigure $fd -translation binary\n    set id [incr ::redis::id]\n    set ::redis::fd($id) $fd\n    set ::redis::addr($id) [list $server $port]\n    set ::redis::blocking($id) 1\n    set ::redis::deferred($id) $defer\n    set ::redis::reconnect($id) 0\n    set ::redis::tls $tls\n    ::redis::redis_reset_state $id\n    interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id\n}\n\n# This is a wrapper to the actual dispatching procedure that handles\n# reconnection if needed.\nproc ::redis::__dispatch__ {id method args} {\n    set errorcode [catch {::redis::__dispatch__raw__ $id $method $args} retval]\n    if {$errorcode && $::redis::reconnect($id) && $::redis::fd($id) eq {}} {\n        # Try again if the connection was lost.\n        # FIXME: we don't re-select the previously selected DB, nor we check\n        # if we are inside a transaction that needs to be re-issued from\n        # scratch.\n        set errorcode [catch {::redis::__dispatch__raw__ $id $method $args} retval]\n    }\n    return -code $errorcode $retval\n}\n\nproc ::redis::__dispatch__raw__ {id method argv} {\n    set fd $::redis::fd($id)\n\n    # Reconnect the link if needed.\n    if {$fd eq {}} {\n        lassign $::redis::addr($id) host port\n        if {$::redis::tls} {\n            set ::redis::fd($id) [::tls::socket $host $port]\n        } else {\n            set ::redis::fd($id) [socket $host $port]\n        }\n        fconfigure $::redis::fd($id) -translation binary\n        set fd $::redis::fd($id)\n    }\n\n    set blocking $::redis::blocking($id)\n    set deferred $::redis::deferred($id)\n    if {$blocking == 0} {\n        if {[llength $argv] == 0} {\n            error \"Please provide a callback in non-blocking mode\"\n        }\n        set callback [lindex $argv end]\n        set argv [lrange $argv 0 end-1]\n    }\n    if {[info command ::redis::__method__$method] eq {}} {\n        set cmd \"*[expr {[llength $argv]+1}]\\r\\n\"\n        append cmd \"$[string length $method]\\r\\n$method\\r\\n\"\n        foreach a $argv {\n            append cmd \"$[string length $a]\\r\\n$a\\r\\n\"\n        }\n        ::redis::redis_write $fd $cmd\n        if {[catch {flush $fd}]} {\n            set ::redis::fd($id) {}\n            return -code error \"I/O error reading reply\"\n        }\n\n        if {!$deferred} {\n            if {$blocking} {\n                ::redis::redis_read_reply $id $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] $argv\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__reconnect {id fd val} {\n    set ::redis::reconnect($id) $val\n}\n\nproc ::redis::__method__read {id fd} {\n    ::redis::redis_read_reply $id $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::addr($id)}\n    catch {unset ::redis::blocking($id)}\n    catch {unset ::redis::deferred($id)}\n    catch {unset ::redis::reconnect($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 {id 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 $id $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 {id 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 $id $fd}\n        default {\n            if {$type eq {}} {\n                set ::redis::fd($id) {}\n                return -code error \"I/O error reading reply\"\n            }\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": "Chapter02/redis-6.0.3/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 \"Can't 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    send_data_packet $::test_server_fd server-killing $pid\n    catch {exec kill $pid}\n    if {$::valgrind} {\n        set max_wait 60000\n    } else {\n        set max_wait 10000\n    }\n    while {[is_alive $config]} {\n        incr wait 10\n\n        if {$wait >= $max_wait} {\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    # Remove this pid from the set of active pids in the test server.\n    send_data_packet $::test_server_fd server-killed $pid\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        if {$::tls} {\n            set fd [::tls::socket $host $port] \n        } else {\n            set fd [socket $host $port]\n        }\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\n# Write the configuration in the dictionary 'config' in the specified\n# file name.\nproc create_server_config_file {filename config} {\n    set fp [open $filename 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\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 0 $::tls]\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    if {$::tls} {\n        dict set config \"tls-cert-file\" [format \"%s/tests/tls/redis.crt\" [pwd]]\n        dict set config \"tls-key-file\" [format \"%s/tests/tls/redis.key\" [pwd]]\n        dict set config \"tls-dh-params-file\" [format \"%s/tests/tls/redis.dh\" [pwd]]\n        dict set config \"tls-ca-cert-file\" [format \"%s/tests/tls/ca.crt\" [pwd]]\n        dict set config \"loglevel\" \"debug\"\n    }\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    if {$::tls} {\n        dict set config \"port\" 0\n        dict set config \"tls-port\" $::port\n        dict set config \"tls-cluster\" \"yes\"\n        dict set config \"tls-replication\" \"yes\"\n    } else {\n        dict set config port $::port\n    }\n\n    set unixsocket [file normalize [format \"%s/%s\" [dict get $config \"dir\"] \"socket\"]]\n    dict set config \"unixsocket\" $unixsocket\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    create_server_config_file $config_file $config\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    # We need a loop here to retry with different ports.\n    set server_started 0\n    while {$server_started == 0} {\n        if {$::verbose} {\n            puts -nonewline \"=== ($tags) Starting server ${::host}:${::port} \"\n        }\n\n        send_data_packet $::test_server_fd \"server-spawning\" \"port $::port\"\n\n        if {$::valgrind} {\n            set pid [exec valgrind --track-origins=yes --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &]\n        } elseif ($::stack_logging) {\n            set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt src/redis-server $config_file > $stdout 2> $stderr &]\n        } else {\n            set pid [exec src/redis-server $config_file > $stdout 2> $stderr &]\n        }\n\n        # Tell the test server about this new instance.\n        send_data_packet $::test_server_fd server-spawned $pid\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        # Wait for actual startup\n        set checkperiod 100; # Milliseconds\n        set maxiter [expr {120*1000/100}] ; # Wait up to 2 minutes.\n        set port_busy 0\n        while {![info exists _pid]} {\n            regexp {PID:\\s(\\d+)} [exec cat $stdout] _ _pid\n            after $checkperiod\n            incr maxiter -1\n            if {$maxiter == 0} {\n                start_server_error $config_file \"No PID detected in log $stdout\"\n                puts \"--- LOG CONTENT ---\"\n                puts [exec cat $stdout]\n                puts \"-------------------\"\n                break\n            }\n\n            # Check if the port is actually busy and the server failed\n            # for this reason.\n            if {[regexp {Could not create server TCP} [exec cat $stdout]]} {\n                set port_busy 1\n                break\n            }\n        }\n\n        # Sometimes we have to try a different port, even if we checked\n        # for availability. Other test clients may grab the port before we\n        # are able to do it for example.\n        if {$port_busy} {\n            puts \"Port $::port was already busy, trying another port...\"\n            set ::port [find_available_port [expr {$::port+1}]]\n            if {$::tls} {\n                dict set config \"tls-port\" $::port\n            } else {\n                dict set config port $::port\n            }\n            create_server_config_file $config_file $config\n            continue; # Try again\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        set server_started 1\n    }\n\n    # setup properties to be able to initialize a client object\n    set port_param [expr $::tls ? {\"tls-port\"} : {\"port\"}]\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_param]} { set port [dict get $config $port_param] }\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    dict set srv \"unixsocket\" $unixsocket\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        if {$::wait_server} {\n            set msg \"server started PID: [dict get $srv \"pid\"]. press any key to continue...\"\n            puts $msg\n            read stdin 1\n        }\n\n        while 1 {\n            # check that the server actually started and is ready for connections\n            if {[exec grep -i \"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": "Chapter02/redis-6.0.3/tests/support/test.tcl",
    "content": "set ::num_tests 0\nset ::num_passed 0\nset ::num_failed 0\nset ::num_skipped 0\nset ::num_aborted 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        set context \"(context: [info frame -1])\"\n        error \"assertion:Expected [uplevel 1 [list subst -nocommands $condition]] $context\"\n    }\n}\n\nproc assert_no_match {pattern value} {\n    if {[string match $pattern $value]} {\n        set context \"(context: [info frame -1])\"\n        error \"assertion:Expected '$value' to not match '$pattern' $context\"\n    }\n}\n\nproc assert_match {pattern value} {\n    if {![string match $pattern $value]} {\n        set context \"(context: [info frame -1])\"\n        error \"assertion:Expected '$value' to match '$pattern' $context\"\n    }\n}\n\nproc assert_equal {value expected {detail \"\"}} {\n    if {$expected ne $value} {\n        if {$detail ne \"\"} {\n            set detail \"(detail: $detail)\"\n        } else {\n            set detail \"(context: [info frame -1])\"\n        }\n        error \"assertion:Expected '$value' to be equal to '$expected' $detail\"\n    }\n}\n\nproc assert_lessthan {value expected {detail \"\"}} {\n    if {!($value < $expected)} {\n        if {$detail ne \"\"} {\n            set detail \"(detail: $detail)\"\n        } else {\n            set detail \"(context: [info frame -1])\"\n        }\n        error \"assertion:Expected '$value' to be lessthan to '$expected' $detail\"\n    }\n}\n\nproc assert_range {value min max {detail \"\"}} {\n    if {!($value <= $max && $value >= $min)} {\n        if {$detail ne \"\"} {\n            set detail \"(detail: $detail)\"\n        } else {\n            set detail \"(context: [info frame -1])\"\n        }\n        error \"assertion:Expected '$value' to be between to '$min' and '$max' $detail\"\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 caught\"\n    }\n}\n\nproc assert_encoding {enc key} {\n    set dbg [r debug object $key]\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            incr ::num_aborted\n            send_data_packet $::test_server_fd ignore $name\n            return\n        }\n    }\n\n    # abort if test name in skiptests\n    if {[lsearch $::skiptests $name] >= 0} {\n        incr ::num_skipped\n        send_data_packet $::test_server_fd skip $name\n        return\n    }\n\n    # abort if test name in skiptests\n    if {[llength $::only_tests] > 0 && [lsearch $::only_tests $name] < 0} {\n        incr ::num_skipped\n        send_data_packet $::test_server_fd skip $name\n        return\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            incr ::num_aborted\n            send_data_packet $::test_server_fd ignore $name\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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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\nproc wait_for_ofs_sync {r1 r2} {\n    wait_for_condition 50 100 {\n        [status $r1 master_repl_offset] eq [status $r2 master_repl_offset]\n    } else {\n        fail \"replica didn't sync in time\"\n    }\n}\n\nproc wait_for_log_message {srv_idx pattern last_lines maxtries delay} {\n    set retry $maxtries\n    set stdout [srv $srv_idx stdout]\n    while {$retry} {\n        set result [exec tail -$last_lines < $stdout]\n        set result [split $result \"\\n\"]\n        foreach line $result {\n            if {[string match $pattern $line]} {\n                return $line\n            }\n        }\n        incr retry -1\n        after $delay\n    }\n    if {$retry == 0} {\n        fail \"log message of '$pattern' not found\"\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    for {set db 0} {$db < 16} {incr db} {\n        {*}$r select $db\n        foreach k [lsort [{*}$r keys *]] {\n            set type [{*}$r type $k]\n            append o [csvstring $db] , [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    }\n    {*}$r select 9\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 {set fd1 [socket 127.0.0.1 $j]}] &&\n            [catch {set fd2 [socket 127.0.0.1 [expr $j+10000]]}]} {\n            return $j\n        } else {\n            catch {\n                close $fd1\n                close $fd2\n            }\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};49m$str\\033\\[0m\"\n        }\n    } else {\n        return $str\n    }\n}\n\n# Execute a background process writing random data for the specified number\n# of seconds to the specified Redis instance.\nproc start_write_load {host port seconds} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds $::tls &\n}\n\n# Stop a process generating write load executed with start_write_load.\nproc stop_write_load {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n\nproc K { x y } { set x } \n\n# Shuffle a list. From Tcl wiki. Originally from Steve Cohen that improved\n# other versions. Code should be under public domain.\nproc lshuffle {list} {\n    set n [llength $list]\n    while {$n>0} {\n        set j [expr {int(rand()*$n)}]\n        lappend slist [lindex $list $j]\n        incr n -1\n        set temp [lindex $list $n]\n        set list [lreplace [K $list [set list {}]] $j $j $temp]\n    }\n    return $slist\n}\n\n# Execute a background process writing complex data for the specified number\n# of ops to the specified Redis instance.\nproc 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 $::tls &\n}\n\n# Stop a process generating write load executed with start_bg_complex_data.\nproc stop_bg_complex_data {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/test_helper.tcl",
    "content": "# Redis test suite. Copyright (C) 2009 Salvatore Sanfilippo antirez@gmail.com\n# This software 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/keyspace\n    unit/scan\n    unit/type/string\n    unit/type/incr\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/type/stream\n    unit/type/stream-cgroups\n    unit/sort\n    unit/expire\n    unit/other\n    unit/multi\n    unit/quit\n    unit/aofrw\n    unit/acl\n    integration/block-repl\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    integration/logging\n    integration/psync2\n    integration/psync2-reg\n    integration/psync2-pingoff\n    unit/pubsub\n    unit/slowlog\n    unit/scripting\n    unit/maxmemory\n    unit/introspection\n    unit/introspection-2\n    unit/limits\n    unit/obuf-limits\n    unit/bitops\n    unit/bitfield\n    unit/geo\n    unit/memefficiency\n    unit/hyperloglog\n    unit/lazyfree\n    unit/wait\n    unit/pendingquerybuf\n    unit/tls\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 ::tls 0\nset ::stack_logging 0\nset ::verbose 0\nset ::quiet 0\nset ::denytags {}\nset ::skiptests {}\nset ::allowtags {}\nset ::only_tests {}\nset ::single_tests {}\nset ::run_solo_tests {}\nset ::skip_till \"\"\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\nset ::timeout 1200; # 20 minutes without progresses will quit the test.\nset ::last_progress [clock seconds]\nset ::active_servers {} ; # Pids of active Redis instances.\nset ::dont_clean 0\nset ::wait_server 0\nset ::stop_on_failure 0\nset ::loop 0\nset ::tlsdir \"tests/tls\"\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\n# This function is called by one of the test clients when it receives\n# a \"run\" command from the server, with a filename as data.\n# It will run the specified test source file and signal it to the\n# test server when finished.\nproc execute_test_file 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# This function is called by one of the test clients when it receives\n# a \"run_code\" command from the server, with a verbatim test source code\n# as argument, and an associated name.\n# It will run the specified code and signal it to the test server when\n# finished.\nproc execute_test_code {name code} {\n    eval $code\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 0 $::tls]\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 $::tls]\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\n# Test wrapped into run_solo are sent back from the client to the\n# test server, so that the test server will send them again to\n# clients once the clients are idle.\nproc run_solo {name code} {\n    if {$::numclients == 1 || $::loop || $::external} {\n        # run_solo is not supported in these scenarios, just run the code.\n        eval $code\n        return\n    }\n    send_data_packet $::test_server_fd run_solo [list $name $code]\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    if {$::external} {\n        set p [exec $tclsh [info script] {*}$::argv \\\n            --client $port --port $::port &]\n        lappend ::clients_pids $p\n    } else {\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\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.\nproc test_server_cron {} {\n    set elapsed [expr {[clock seconds]-$::last_progress}]\n\n    if {$elapsed > $::timeout} {\n        set err \"\\[[colorstr red TIMEOUT]\\]: clients state report follows.\"\n        puts $err\n        lappend ::failed_tests $err\n        show_clients_state\n        kill_clients\n        force_kill_all_servers\n        the_end\n    }\n\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# skip: a test was skipped by skipfile or individual test options.\n# ignore: a test was skipped by a group tag.\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    set ::last_progress [clock seconds]\n\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) $data\"\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 {skip}} {\n        if {!$::quiet} {\n            puts \"\\[[colorstr yellow $status]\\]: $data\"\n        }\n    } elseif {$status eq {ignore}} {\n        if {!$::quiet} {\n            puts \"\\[[colorstr cyan $status]\\]: $data\"\n        }\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            if {$::stop_on_failure} {\n            puts -nonewline \"(Test stopped, press enter to continue)\"\n            flush stdout\n            gets stdin\n        }\n    } elseif {$status eq {exception}} {\n        puts \"\\[[colorstr red $status]\\]: $data\"\n        kill_clients\n        force_kill_all_servers\n        exit 1\n    } elseif {$status eq {testing}} {\n        set ::active_clients_task($fd) \"(IN PROGRESS) $data\"\n    } elseif {$status eq {server-spawning}} {\n        set ::active_clients_task($fd) \"(SPAWNING SERVER) $data\"\n    } elseif {$status eq {server-spawned}} {\n        lappend ::active_servers $data\n        set ::active_clients_task($fd) \"(SPAWNED SERVER) pid:$data\"\n    } elseif {$status eq {server-killing}} {\n        set ::active_clients_task($fd) \"(KILLING SERVER) pid:$data\"\n    } elseif {$status eq {server-killed}} {\n        set ::active_servers [lsearch -all -inline -not -exact $::active_servers $data]\n        set ::active_clients_task($fd) \"(KILLED SERVER) pid:$data\"\n    } elseif {$status eq {run_solo}} {\n        lappend ::run_solo_tests $data\n    } else {\n        if {!$::quiet} {\n            puts \"\\[$status\\]: $data\"\n        }\n    }\n}\n\nproc show_clients_state {} {\n    # The following loop is only useful for debugging tests that may\n    # enter an infinite loop.\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\nproc kill_clients {} {\n    foreach p $::clients_pids {\n        catch {exec kill $p}\n    }\n}\n\nproc force_kill_all_servers {} {\n    foreach p $::active_servers {\n        puts \"Killing still running Redis server $p\"\n        catch {exec kill -9 $p}\n    }\n}\n\nproc lpop {listVar {count 1}} {\n    upvar 1 $listVar l\n    set ele [lindex $l 0]\n    set l [lrange $l 1 end]\n    set ele\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    # 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        if {$::loop && $::next_test == [llength $::all_tests]} {\n            set ::next_test 0\n        }\n    } elseif {[llength $::run_solo_tests] != 0 && [llength $::active_clients] == 0} {\n        if {!$::quiet} {\n            puts [colorstr bold-white \"Testing solo test\"]\n            set ::active_clients_task($fd) \"ASSIGNED: $fd solo test\"\n        }\n        set ::clients_start_time($fd) [clock seconds]\n        send_data_packet $fd run_code [lpop ::run_solo_tests]\n        lappend ::active_clients $fd\n    } else {\n        lappend ::idle_clients $fd\n        set ::active_clients_task($fd) \"SLEEPING, no more units to assign\"\n        if {[llength $::active_clients] == 0} {\n            the_end\n        }\n    }\n}\n\n# The the_end function 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        if {!$::dont_clean} cleanup\n        exit 1\n    } else {\n        puts \"\\n[colorstr bold-white {\\o/}] [colorstr bold-green {All tests passed without errors!}]\\n\"\n        if {!$::dont_clean} 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_test_file $data\n        } elseif {$cmd eq {run_code}} {\n            foreach {name code} $data break\n            execute_test_code $name $code\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        \"--stack-logging    Enable OSX leaks/malloc stack logging.\"\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). this option can be repeated.\"\n        \"--list-tests       List all the available test units.\"\n        \"--only <test>      Just execute the specified test by test name. this option can be repeated.\"\n        \"--skip-till <unit> Skip all units until (and including) the specified one.\"\n        \"--clients <num>    Number of test clients (default 16).\"\n        \"--timeout <sec>    Test timeout in seconds (default 10 min).\"\n        \"--force-failure    Force the execution of a test that always fails.\"\n        \"--config <k> <v>   Extra config file argument.\"\n        \"--skipfile <file>  Name of a file containing test names that should be skipped (one per line).\"\n        \"--dont-clean       Don't delete redis log files after the run.\"\n        \"--stop             Blocks once the first test fails.\"\n        \"--loop             Execute the specified set of tests forever.\"\n        \"--wait-server      Wait after server is started (so that you can attach a debugger).\"\n        \"--tls              Run tests in TLS mode.\"\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 {--config}} {\n        set arg2 [lindex $argv [expr $j+2]]\n        lappend ::global_overrides $arg\n        lappend ::global_overrides $arg2\n        incr j 2\n    } elseif {$opt eq {--skipfile}} {\n        incr j\n        set fp [open $arg r]\n        set file_data [read $fp]\n        close $fp\n        set ::skiptests [split $file_data \"\\n\"]\n    } elseif {$opt eq {--valgrind}} {\n        set ::valgrind 1\n    } elseif {$opt eq {--stack-logging}} {\n        if {[string match {*Darwin*} [exec uname -a]]} {\n            set ::stack_logging 1\n        }\n    } elseif {$opt eq {--quiet}} {\n        set ::quiet 1\n    } elseif {$opt eq {--tls}} {\n        package require tls 1.6\n        set ::tls 1\n        ::tls::init \\\n            -cafile \"$::tlsdir/ca.crt\" \\\n            -certfile \"$::tlsdir/redis.crt\" \\\n            -keyfile \"$::tlsdir/redis.key\"\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        lappend ::single_tests $arg\n        incr j\n    } elseif {$opt eq {--only}} {\n        lappend ::only_tests $arg\n        incr j\n    } elseif {$opt eq {--skip-till}} {\n        set ::skip_till $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 {--verbose}} {\n        set ::verbose 1\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 {--dont-clean}} {\n        set ::dont_clean 1\n    } elseif {$opt eq {--wait-server}} {\n        set ::wait_server 1\n    } elseif {$opt eq {--stop}} {\n        set ::stop_on_failure 1\n    } elseif {$opt eq {--loop}} {\n        set ::loop 1\n    } elseif {$opt eq {--timeout}} {\n        set ::timeout $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\n# If --skil-till option was given, we populate the list of single tests\n# to run with everything *after* the specified unit.\nif {$::skip_till != \"\"} {\n    set skipping 1\n    foreach t $::all_tests {\n        if {$skipping == 0} {\n            lappend ::single_tests $t\n        }\n        if {$t == $::skip_till} {\n            set skipping 0\n        }\n    }\n    if {$skipping} {\n        puts \"test $::skip_till not found\"\n        exit 0\n    }\n}\n\n# Override the list of tests with the specific tests we want to run\n# in case there was some filter, that is --single or --skip-till options.\nif {[llength $::single_tests] > 0} {\n    set ::all_tests $::single_tests\n}\n\nproc attach_to_replication_stream {} {\n    if {$::tls} {\n        set s [::tls::socket [srv 0 \"host\"] [srv 0 \"port\"]]\n    } else {\n        set s [socket [srv 0 \"host\"] [srv 0 \"port\"]]\n    }\n    fconfigure $s -translation binary\n    puts -nonewline $s \"SYNC\\r\\n\"\n    flush $s\n\n    # Get the count\n    while 1 {\n        set count [gets $s]\n        set prefix [string range $count 0 0]\n        if {$prefix ne {}} break; # Newlines are allowed as PINGs.\n    }\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": "Chapter02/redis-6.0.3/tests/unit/acl.tcl",
    "content": "start_server {tags {\"acl\"}} {\n    test {Connections start with the default user} {\n        r ACL WHOAMI\n    } {default}\n\n    test {It is possible to create new users} {\n        r ACL setuser newuser\n    }\n\n    test {New users start disabled} {\n        r ACL setuser newuser >passwd1\n        catch {r AUTH newuser passwd1} err\n        set err\n    } {*WRONGPASS*}\n\n    test {Enabling the user allows the login} {\n        r ACL setuser newuser on +acl\n        r AUTH newuser passwd1\n        r ACL WHOAMI\n    } {newuser}\n\n    test {Only the set of correct passwords work} {\n        r ACL setuser newuser >passwd2\n        catch {r AUTH newuser passwd1} e\n        assert {$e eq \"OK\"}\n        catch {r AUTH newuser passwd2} e\n        assert {$e eq \"OK\"}\n        catch {r AUTH newuser passwd3} e\n        set e\n    } {*WRONGPASS*}\n\n    test {It is possible to remove passwords from the set of valid ones} {\n        r ACL setuser newuser <passwd1\n        catch {r AUTH newuser passwd1} e\n        set e\n    } {*WRONGPASS*}\n\n    test {Test password hashes can be added} {\n        r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6\n        catch {r AUTH newuser passwd4} e\n        assert {$e eq \"OK\"}\n    }\n\n    test {Test password hashes validate input} {\n        # Validate Length\n        catch {r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e} e\n        # Validate character outside set\n        catch {r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4eq} e\n        set e\n    } {*Error in ACL SETUSER modifier*}\n\n    test {ACL GETUSER returns the password hash instead of the actual password} {\n        set passstr [dict get [r ACL getuser newuser] passwords]\n        assert_match {*34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6*} $passstr\n        assert_no_match {*passwd4*} $passstr\n    }\n\n    test {Test hashed passwords removal} {\n        r ACL setuser newuser !34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6\n        set passstr [dict get [r ACL getuser newuser] passwords]\n        assert_no_match {*34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6*} $passstr\n    }\n\n    test {By default users are not able to access any command} {\n        catch {r SET foo bar} e\n        set e\n    } {*NOPERM*}\n\n    test {By default users are not able to access any key} {\n        r ACL setuser newuser +set\n        catch {r SET foo bar} e\n        set e\n    } {*NOPERM*key*}\n\n    test {It's possible to allow the access of a subset of keys} {\n        r ACL setuser newuser allcommands ~foo:* ~bar:*\n        r SET foo:1 a\n        r SET bar:2 b\n        catch {r SET zap:3 c} e\n        r ACL setuser newuser allkeys; # Undo keys ACL\n        set e\n    } {*NOPERM*key*}\n\n    test {Users can be configured to authenticate with any password} {\n        r ACL setuser newuser nopass\n        r AUTH newuser zipzapblabla\n    } {OK}\n\n    test {ACLs can exclude single commands} {\n        r ACL setuser newuser -ping\n        r INCR mycounter ; # Should not raise an error\n        catch {r PING} e\n        set e\n    } {*NOPERM*}\n\n    test {ACLs can include or exclude whole classes of commands} {\n        r ACL setuser newuser -@all +@set +acl\n        r SADD myset a b c; # Should not raise an error\n        r ACL setuser newuser +@all -@string\n        r SADD myset a b c; # Again should not raise an error\n        # String commands instead should raise an error\n        catch {r SET foo bar} e\n        r ACL setuser newuser allcommands; # Undo commands ACL\n        set e\n    } {*NOPERM*}\n\n    test {ACLs can include single subcommands} {\n        r ACL setuser newuser +@all -client\n        r ACL setuser newuser +client|id +client|setname\n        r CLIENT ID; # Should not fail\n        r CLIENT SETNAME foo ; # Should not fail\n        catch {r CLIENT KILL type master} e\n        set e\n    } {*NOPERM*}\n\n    # Note that the order of the generated ACL rules is not stable in Redis\n    # so we need to match the different parts and not as a whole string.\n    test {ACL GETUSER is able to translate back command permissions} {\n        # Subtractive\n        r ACL setuser newuser reset +@all ~* -@string +incr -debug +debug|digest\n        set cmdstr [dict get [r ACL getuser newuser] commands]\n        assert_match {*+@all*} $cmdstr\n        assert_match {*-@string*} $cmdstr\n        assert_match {*+incr*} $cmdstr\n        assert_match {*-debug +debug|digest**} $cmdstr\n\n        # Additive\n        r ACL setuser newuser reset +@string -incr +acl +debug|digest +debug|segfault\n        set cmdstr [dict get [r ACL getuser newuser] commands]\n        assert_match {*-@all*} $cmdstr\n        assert_match {*+@string*} $cmdstr\n        assert_match {*-incr*} $cmdstr\n        assert_match {*+debug|digest*} $cmdstr\n        assert_match {*+debug|segfault*} $cmdstr\n        assert_match {*+acl*} $cmdstr\n    }\n\n    test {ACL #5998 regression: memory leaks adding / removing subcommands} {\n        r AUTH default \"\"\n        r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c\n        r ACL setuser newuser -debug\n        # The test framework will detect a leak if any.\n    }\n\n    test {ACL LOG shows failed command executions at toplevel} {\n        r ACL LOG RESET\n        r ACL setuser antirez >foo on +set ~object:1234\n        r ACL setuser antirez +eval +multi +exec\n        r AUTH antirez foo\n        catch {r GET foo}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry username] eq {antirez}}\n        assert {[dict get $entry context] eq {toplevel}}\n        assert {[dict get $entry reason] eq {command}}\n        assert {[dict get $entry object] eq {get}}\n    }\n\n    test {ACL LOG is able to test similar events} {\n        r AUTH antirez foo\n        catch {r GET foo}\n        catch {r GET foo}\n        catch {r GET foo}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry count] == 4}\n    }\n\n    test {ACL LOG is able to log keys access violations and key name} {\n        r AUTH antirez foo\n        catch {r SET somekeynotallowed 1234}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry reason] eq {key}}\n        assert {[dict get $entry object] eq {somekeynotallowed}}\n    }\n\n    test {ACL LOG RESET is able to flush the entries in the log} {\n        r ACL LOG RESET\n        assert {[llength [r ACL LOG]] == 0}\n    }\n\n    test {ACL LOG can distinguish the transaction context (1)} {\n        r AUTH antirez foo\n        r MULTI\n        catch {r INCR foo}\n        catch {r EXEC}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {multi}}\n        assert {[dict get $entry object] eq {incr}}\n    }\n\n    test {ACL LOG can distinguish the transaction context (2)} {\n        set rd1 [redis_deferring_client]\n        r ACL SETUSER antirez +incr\n\n        r AUTH antirez foo\n        r MULTI\n        r INCR object:1234\n        $rd1 ACL SETUSER antirez -incr\n        $rd1 read\n        catch {r EXEC}\n        $rd1 close\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {multi}}\n        assert {[dict get $entry object] eq {incr}}\n        r ACL SETUSER antirez -incr\n    }\n\n    test {ACL can log errors in the context of Lua scripting} {\n        r AUTH antirez foo\n        catch {r EVAL {redis.call('incr','foo')} 0}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {lua}}\n        assert {[dict get $entry object] eq {incr}}\n    }\n\n    test {ACL LOG can accept a numerical argument to show less entries} {\n        r AUTH antirez foo\n        catch {r INCR foo}\n        catch {r INCR foo}\n        catch {r INCR foo}\n        catch {r INCR foo}\n        r AUTH default \"\"\n        assert {[llength [r ACL LOG]] > 1}\n        assert {[llength [r ACL LOG 2]] == 2}\n    }\n\n    test {ACL LOG can log failed auth attempts} {\n        catch {r AUTH antirez wrong-password}\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {toplevel}}\n        assert {[dict get $entry reason] eq {auth}}\n        assert {[dict get $entry object] eq {AUTH}}\n        assert {[dict get $entry username] eq {antirez}}\n    }\n\n    test {ACL LOG entries are limited to a maximum amount} {\n        r ACL LOG RESET\n        r CONFIG SET acllog-max-len 5\n        r AUTH antirez foo\n        for {set j 0} {$j < 10} {incr j} {\n            catch {r SET obj:$j 123}\n        }\n        r AUTH default \"\"\n        assert {[llength [r ACL LOG]] == 5}\n    }\n\n    test {When default user is off, new connections are not authenticated} {\n        r ACL setuser default off\n        catch {set rd1 [redis_deferring_client]} e\n        r ACL setuser default on\n        set e\n    } {*NOAUTH*}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/aofrw.tcl",
    "content": "start_server {tags {\"aofrw\"}} {\n    # Enable the AOF\n    r config set appendonly yes\n    r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.\n    waitForBgrewriteaof r\n\n    foreach rdbpre {yes no} {\n        r config set aof-use-rdb-preamble $rdbpre\n        test \"AOF rewrite during write load: RDB preamble=$rdbpre\" {\n            # Start a write load for 10 seconds\n            set master [srv 0 client]\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n            set load_handle0 [start_write_load $master_host $master_port 10]\n            set load_handle1 [start_write_load $master_host $master_port 10]\n            set load_handle2 [start_write_load $master_host $master_port 10]\n            set load_handle3 [start_write_load $master_host $master_port 10]\n            set load_handle4 [start_write_load $master_host $master_port 10]\n\n            # Make sure the instance is really receiving data\n            wait_for_condition 50 100 {\n                [r dbsize] > 0\n            } else {\n                fail \"No write load detected.\"\n            }\n\n            # After 3 seconds, start a rewrite, while the write load is still\n            # active.\n            after 3000\n            r bgrewriteaof\n            waitForBgrewriteaof r\n\n            # Let it run a bit more so that we'll append some data to the new\n            # AOF.\n            after 1000\n\n            # Stop the processes generating the load if they are still active\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            # Make sure that we remain the only connected client.\n            # This step is needed to make sure there are no pending writes\n            # that will be processed between the two \"debug digest\" calls.\n            wait_for_condition 50 100 {\n                [llength [split [string trim [r client list]] \"\\n\"]] == 1\n            } else {\n                puts [r client list]\n                fail \"Clients generating loads are not disconnecting\"\n            }\n\n            # Get the data set digest\n            set d1 [r debug digest]\n\n            # Load the AOF\n            r debug loadaof\n            set d2 [r debug digest]\n\n            # Make sure they are the same\n            assert {$d1 eq $d2}\n        }\n    }\n}\n\nstart_server {tags {\"aofrw\"} overrides {aof-use-rdb-preamble no}} {\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 -5 < [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 {quicklist} {\n            test \"AOF rewrite of list with $e encoding, $d data\" {\n                r flushall\n                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": "Chapter02/redis-6.0.3/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*any 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    } {WRONGPASS*}\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": "Chapter02/redis-6.0.3/tests/unit/bitfield.tcl",
    "content": "start_server {tags {\"bitops\"}} {\n    test {BITFIELD signed SET and GET basics} {\n        r del bits\n        set results {}\n        lappend results [r bitfield bits set i8 0 -100]\n        lappend results [r bitfield bits set i8 0 101]\n        lappend results [r bitfield bits get i8 0]\n        set results\n    } {0 -100 101}\n\n    test {BITFIELD unsigned SET and GET basics} {\n        r del bits\n        set results {}\n        lappend results [r bitfield bits set u8 0 255]\n        lappend results [r bitfield bits set u8 0 100]\n        lappend results [r bitfield bits get u8 0]\n        set results\n    } {0 255 100}\n\n    test {BITFIELD #<idx> form} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 65\n        r bitfield bits set u8 #1 66\n        r bitfield bits set u8 #2 67\n        r get bits\n    } {ABC}\n\n    test {BITFIELD basic INCRBY form} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 10\n        lappend results [r bitfield bits incrby u8 #0 100]\n        lappend results [r bitfield bits incrby u8 #0 100]\n        set results\n    } {110 210}\n\n    test {BITFIELD chaining of multiple commands} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 10\n        lappend results [r bitfield bits incrby u8 #0 100 incrby u8 #0 100]\n        set results\n    } {{110 210}}\n\n    test {BITFIELD unsigned overflow wrap} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 100\n        lappend results [r bitfield bits overflow wrap incrby u8 #0 257]\n        lappend results [r bitfield bits get u8 #0]\n        lappend results [r bitfield bits overflow wrap incrby u8 #0 255]\n        lappend results [r bitfield bits get u8 #0]\n    } {101 101 100 100}\n\n    test {BITFIELD unsigned overflow sat} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 100\n        lappend results [r bitfield bits overflow sat incrby u8 #0 257]\n        lappend results [r bitfield bits get u8 #0]\n        lappend results [r bitfield bits overflow sat incrby u8 #0 -255]\n        lappend results [r bitfield bits get u8 #0]\n    } {255 255 0 0}\n\n    test {BITFIELD signed overflow wrap} {\n        r del bits\n        set results {}\n        r bitfield bits set i8 #0 100\n        lappend results [r bitfield bits overflow wrap incrby i8 #0 257]\n        lappend results [r bitfield bits get i8 #0]\n        lappend results [r bitfield bits overflow wrap incrby i8 #0 255]\n        lappend results [r bitfield bits get i8 #0]\n    } {101 101 100 100}\n\n    test {BITFIELD signed overflow sat} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 100\n        lappend results [r bitfield bits overflow sat incrby i8 #0 257]\n        lappend results [r bitfield bits get i8 #0]\n        lappend results [r bitfield bits overflow sat incrby i8 #0 -255]\n        lappend results [r bitfield bits get i8 #0]\n    } {127 127 -128 -128}\n\n    test {BITFIELD overflow detection fuzzing} {\n        for {set j 0} {$j < 1000} {incr j} {\n            set bits [expr {[randomInt 64]+1}]\n            set sign [randomInt 2]\n            set range [expr {2**$bits}]\n            if {$bits == 64} {set sign 1} ; # u64 is not supported by BITFIELD.\n            if {$sign} {\n                set min [expr {-($range/2)}]\n                set type \"i$bits\"\n            } else {\n                set min 0\n                set type \"u$bits\"\n            }\n            set max [expr {$min+$range-1}]\n\n            # Compare Tcl vs Redis\n            set range2 [expr {$range*2}]\n            set value [expr {($min*2)+[randomInt $range2]}]\n            set increment [expr {($min*2)+[randomInt $range2]}]\n            if {$value > 9223372036854775807} {\n                set value 9223372036854775807\n            }\n            if {$value < -9223372036854775808} {\n                set value -9223372036854775808\n            }\n            if {$increment > 9223372036854775807} {\n                set increment 9223372036854775807\n            }\n            if {$increment < -9223372036854775808} {\n                set increment -9223372036854775808\n            }\n\n            set overflow 0\n            if {$value > $max || $value < $min} {set overflow 1}\n            if {($value + $increment) > $max} {set overflow 1}\n            if {($value + $increment) < $min} {set overflow 1}\n\n            r del bits\n            set res1 [r bitfield bits overflow fail set $type 0 $value]\n            set res2 [r bitfield bits overflow fail incrby $type 0 $increment]\n\n            if {$overflow && [lindex $res1 0] ne {} &&\n                             [lindex $res2 0] ne {}} {\n                fail \"OW not detected where needed: $type $value+$increment\"\n            }\n            if {!$overflow && ([lindex $res1 0] eq {} ||\n                               [lindex $res2 0] eq {})} {\n                fail \"OW detected where NOT needed: $type $value+$increment\"\n            }\n        }\n    }\n\n    test {BITFIELD overflow wrap fuzzing} {\n        for {set j 0} {$j < 1000} {incr j} {\n            set bits [expr {[randomInt 64]+1}]\n            set sign [randomInt 2]\n            set range [expr {2**$bits}]\n            if {$bits == 64} {set sign 1} ; # u64 is not supported by BITFIELD.\n            if {$sign} {\n                set min [expr {-($range/2)}]\n                set type \"i$bits\"\n            } else {\n                set min 0\n                set type \"u$bits\"\n            }\n            set max [expr {$min+$range-1}]\n\n            # Compare Tcl vs Redis\n            set range2 [expr {$range*2}]\n            set value [expr {($min*2)+[randomInt $range2]}]\n            set increment [expr {($min*2)+[randomInt $range2]}]\n            if {$value > 9223372036854775807} {\n                set value 9223372036854775807\n            }\n            if {$value < -9223372036854775808} {\n                set value -9223372036854775808\n            }\n            if {$increment > 9223372036854775807} {\n                set increment 9223372036854775807\n            }\n            if {$increment < -9223372036854775808} {\n                set increment -9223372036854775808\n            }\n\n            r del bits\n            r bitfield bits overflow wrap set $type 0 $value\n            r bitfield bits overflow wrap incrby $type 0 $increment\n            set res [lindex [r bitfield bits get $type 0] 0]\n\n            set expected 0\n            if {$sign} {incr expected [expr {$max+1}]}\n            incr expected $value\n            incr expected $increment\n            set expected [expr {$expected % $range}]\n            if {$sign} {incr expected $min}\n\n            if {$res != $expected} {\n                fail \"WRAP error: $type $value+$increment = $res, should be $expected\"\n            }\n        }\n    }\n\n    test {BITFIELD regression for #3221} {\n        r set bits 1\n        r bitfield bits get u1 0\n    } {0}\n\n    test {BITFIELD regression for #3564} {\n        for {set j 0} {$j < 10} {incr j} {\n            r del mystring\n            set res [r BITFIELD mystring SET i8 0 10 SET i8 64 10 INCRBY i8 10 99900]\n            assert {$res eq {0 0 60}}\n        }\n        r del mystring\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 {BITFIELD: setup slave} {\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 {BITFIELD: write on master, read on slave} {\n            $master del bits\n            assert_equal 0 [$master bitfield bits set u8 0 255]\n            assert_equal 255 [$master bitfield bits set u8 0 100]\n            wait_for_ofs_sync $master $slave\n            assert_equal 100 [$slave bitfield_ro bits get u8 0]\n        }\n\n        test {BITFIELD_RO fails when write option is used} {\n            catch {$slave bitfield_ro bits set u8 0 100 get u8 0} err\n            assert_match {*ERR BITFIELD_RO only supports the GET subcommand*} $err\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/bitops.tcl",
    "content": "# Compare Redis commands 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    test {BITCOUNT returns 0 with out of range indexes} {\n        r set str \"xxxx\"\n        r bitcount str 4 10\n    } 0\n\n    test {BITCOUNT returns 0 with negative indexes where start > end} {\n        r set str \"xxxx\"\n        r bitcount str -6 -7\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 foo\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": "Chapter02/redis-6.0.3/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 can set an absolute expire} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        set now [clock milliseconds]\n        r restore foo [expr $now+3000] $encoded absttl\n        set ttl [r pttl foo]\n        assert {$ttl >= 2900 && $ttl <= 3100}\n        r get foo\n    } {bar}\n    \n    test {RESTORE can set LRU} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        r config set maxmemory-policy allkeys-lru\n        r restore foo 0 $encoded idletime 1000\n        set idle [r object idletime foo]\n        assert {$idle >= 1000 && $idle <= 1010}\n        r get foo\n    } {bar}\n    \n    test {RESTORE can set LFU} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        r config set maxmemory-policy allkeys-lfu\n        r restore foo 0 $encoded freq 100\n        set freq [r object freq foo]\n        assert {$freq == 100}\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 < 40000} {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] == 40000*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    test {MIGRATE can migrate multiple keys at once} {\n        set first [srv 0 client]\n        r set key1 \"v1\"\n        r set key2 \"v2\"\n        r set key3 \"v3\"\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 key1] == 1}\n            assert {[$second exists key1] == 0}\n            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys key1 key2 key3]\n            assert {$ret eq {OK}}\n            assert {[$first exists key1] == 0}\n            assert {[$first exists key2] == 0}\n            assert {[$first exists key3] == 0}\n            assert {[$second get key1] eq {v1}}\n            assert {[$second get key2] eq {v2}}\n            assert {[$second get key3] eq {v3}}\n        }\n    }\n\n    test {MIGRATE with multiple keys must have empty key arg} {\n        catch {r MIGRATE 127.0.0.1 6379 NotEmpty 9 5000 keys a b c} e\n        set e\n    } {*empty string*}\n\n    test {MIGRATE with multiple keys migrate just existing ones} {\n        set first [srv 0 client]\n        r set key1 \"v1\"\n        r set key2 \"v2\"\n        r set key3 \"v3\"\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            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys nokey-1 nokey-2 nokey-2]\n            assert {$ret eq {NOKEY}}\n\n            assert {[$first exists key1] == 1}\n            assert {[$second exists key1] == 0}\n            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys nokey-1 key1 nokey-2 key2 nokey-3 key3]\n            assert {$ret eq {OK}}\n            assert {[$first exists key1] == 0}\n            assert {[$first exists key2] == 0}\n            assert {[$first exists key3] == 0}\n            assert {[$second get key1] eq {v1}}\n            assert {[$second get key2] eq {v2}}\n            assert {[$second get key3] eq {v3}}\n        }\n    }\n\n    test {MIGRATE with multiple keys: stress command rewriting} {\n        set first [srv 0 client]\n        r flushdb\n        r mset a 1 b 2 c 3 d 4 c 5 e 6 f 7 g 8 h 9 i 10 l 11 m 12 n 13 o 14 p 15 q 16\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            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys a b c d e f g h i l m n o p q]\n\n            assert {[$first dbsize] == 0}\n            assert {[$second dbsize] == 15}\n        }\n    }\n\n    test {MIGRATE with multiple keys: delete just ack keys} {\n        set first [srv 0 client]\n        r flushdb\n        r mset a 1 b 2 c 3 d 4 c 5 e 6 f 7 g 8 h 9 i 10 l 11 m 12 n 13 o 14 p 15 q 16\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            $second mset c _ d _; # Two busy keys and no REPLACE used\n\n            catch {r -1 migrate $second_host $second_port \"\" 9 5000 keys a b c d e f g h i l m n o p q} e\n\n            assert {[$first dbsize] == 2}\n            assert {[$second dbsize] == 15}\n            assert {[$first exists c] == 1}\n            assert {[$first exists d] == 1}\n        }\n    }\n\n    test {MIGRATE AUTH: correct and wrong password cases} {\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            $second config set requirepass foobar\n            $second auth foobar\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 AUTH foobar]\n            assert {$ret eq {OK}}\n            assert {[$second exists list] == 1}\n            assert {[$second lrange list 0 -1] eq {d c b a}}\n\n            r -1 lpush list a b c d\n            $second config set requirepass foobar2\n            catch {r -1 migrate $second_host $second_port list 9 5000 AUTH foobar} err\n            assert_match {*WRONGPASS*} $err\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 time 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    test {EXPIRE with empty string as TTL should report an error} {\n        r set foo bar\n        catch {r expire foo \"\"} e\n        set e\n    } {*not an integer*}\n\n    test {SET - use EX/PX option, TTL should not be reseted after loadaof} {\n        r config set appendonly yes\n        r set foo bar EX 100\n        after 2000\n        r debug loadaof\n        set ttl [r ttl foo]\n        assert {$ttl <= 98 && $ttl > 90}\n\n        r set foo bar PX 100000\n        after 2000\n        r debug loadaof\n        set ttl [r ttl foo]\n        assert {$ttl <= 98 && $ttl > 90}\n    }\n\n    test {SET command will remove expire} {\n        r set foo bar EX 100\n        r set foo bar\n        r ttl foo\n    } {-1}\n\n    test {SET - use KEEPTTL option, TTL should not be removed} {\n        r set foo bar EX 100\n        r set foo bar KEEPTTL\n        set ttl [r ttl foo]\n        assert {$ttl <= 100 && $ttl > 90}\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/geo.tcl",
    "content": "# Helper functions to simulate search-in-radius in the Tcl side in order to\n# verify the Redis implementation with a fuzzy test.\nproc geo_degrad deg {expr {$deg*atan(1)*8/360}}\n\nproc geo_distance {lon1d lat1d lon2d lat2d} {\n    set lon1r [geo_degrad $lon1d]\n    set lat1r [geo_degrad $lat1d]\n    set lon2r [geo_degrad $lon2d]\n    set lat2r [geo_degrad $lat2d]\n    set v [expr {sin(($lon2r - $lon1r) / 2)}]\n    set u [expr {sin(($lat2r - $lat1r) / 2)}]\n    expr {2.0 * 6372797.560856 * \\\n            asin(sqrt($u * $u + cos($lat1r) * cos($lat2r) * $v * $v))}\n}\n\nproc geo_random_point {lonvar latvar} {\n    upvar 1 $lonvar lon\n    upvar 1 $latvar lat\n    # Note that the actual latitude limit should be -85 to +85, we restrict\n    # the test to -70 to +70 since in this range the algorithm is more precise\n    # while outside this range occasionally some element may be missing.\n    set lon [expr {-180 + rand()*360}]\n    set lat [expr {-70 + rand()*140}]\n}\n\n# Return elements non common to both the lists.\n# This code is from http://wiki.tcl.tk/15489\nproc compare_lists {List1 List2} {\n   set DiffList {}\n   foreach Item $List1 {\n      if {[lsearch -exact $List2 $Item] == -1} {\n         lappend DiffList $Item\n      }\n   }\n   foreach Item $List2 {\n      if {[lsearch -exact $List1 $Item] == -1} {\n         if {[lsearch -exact $DiffList $Item] == -1} {\n            lappend DiffList $Item\n         }\n      }\n   }\n   return $DiffList\n}\n\n# The following list represents sets of random seed, search position\n# and radius that caused bugs in the past. It is used by the randomized\n# test later as a starting point. When the regression vectors are scanned\n# the code reverts to using random data.\n#\n# The format is: seed km lon lat\nset regression_vectors {\n    {1482225976969 7083 81.634948934258375 30.561509253718668}\n    {1482340074151 5416 -70.863281847379767 -46.347003465679947}\n    {1499014685896 6064 -89.818768962202014 -40.463868561416803}\n    {1412 156 149.29737817929004 15.95807862745508}\n    {441574 143 59.235461856813856 66.269555127373678}\n    {160645 187 -101.88575239939883 49.061997951502917}\n    {750269 154 -90.187939661642517 66.615930412251487}\n    {342880 145 163.03472387745728 64.012747720821181}\n    {729955 143 137.86663517256579 63.986745399416776}\n    {939895 151 59.149620271823181 65.204186651485145}\n    {1412 156 149.29737817929004 15.95807862745508}\n    {564862 149 84.062063109158544 -65.685403922426232}\n    {1546032440391 16751 -1.8175081637769495 20.665668878082954}\n}\nset rv_idx 0\n\nstart_server {tags {\"geo\"}} {\n    test {GEOADD create} {\n        r geoadd nyc -73.9454966 40.747533 \"lic market\"\n    } {1}\n\n    test {GEOADD update} {\n        r geoadd nyc -73.9454966 40.747533 \"lic market\"\n    } {0}\n\n    test {GEOADD invalid coordinates} {\n        catch {\n            r geoadd nyc -73.9454966 40.747533 \"lic market\" \\\n                foo bar \"luck market\"\n        } err\n        set err\n    } {*valid*}\n\n    test {GEOADD multi add} {\n        r geoadd nyc -73.9733487 40.7648057 \"central park n/q/r\" -73.9903085 40.7362513 \"union square\" -74.0131604 40.7126674 \"wtc one\" -73.7858139 40.6428986 \"jfk\" -73.9375699 40.7498929 \"q4\" -73.9564142 40.7480973 4545\n    } {6}\n\n    test {Check geoset values} {\n        r zrange nyc 0 -1 withscores\n    } {{wtc one} 1791873972053020 {union square} 1791875485187452 {central park n/q/r} 1791875761332224 4545 1791875796750882 {lic market} 1791875804419201 q4 1791875830079666 jfk 1791895905559723}\n\n    test {GEORADIUS simple (sorted)} {\n        r georadius nyc -73.9798091 40.7598464 3 km asc\n    } {{central park n/q/r} 4545 {union square}}\n\n    test {GEORADIUS withdist (sorted)} {\n        r georadius nyc -73.9798091 40.7598464 3 km withdist asc\n    } {{{central park n/q/r} 0.7750} {4545 2.3651} {{union square} 2.7697}}\n\n    test {GEORADIUS with COUNT} {\n        r georadius nyc -73.9798091 40.7598464 10 km COUNT 3\n    } {{central park n/q/r} 4545 {union square}}\n\n    test {GEORADIUS with COUNT but missing integer argument} {\n        catch {r georadius nyc -73.9798091 40.7598464 10 km COUNT} e\n        set e\n    } {ERR*syntax*}\n\n    test {GEORADIUS with COUNT DESC} {\n        r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC\n    } {{wtc one} q4}\n\n    test {GEORADIUS HUGE, issue #2767} {\n        r geoadd users -47.271613776683807 -54.534504198047678 user_000000\n        llength [r GEORADIUS users 0 0 50000 km WITHCOORD]\n    } {1}\n\n    test {GEORADIUSBYMEMBER simple (sorted)} {\n        r georadiusbymember nyc \"wtc one\" 7 km\n    } {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}\n\n    test {GEORADIUSBYMEMBER withdist (sorted)} {\n        r georadiusbymember nyc \"wtc one\" 7 km withdist\n    } {{{wtc one} 0.0000} {{union square} 3.2544} {{central park n/q/r} 6.7000} {4545 6.1975} {{lic market} 6.8969}}\n\n    test {GEOHASH is able to return geohash strings} {\n        # Example from Wikipedia.\n        r del points\n        r geoadd points -5.6 42.6 test\n        lindex [r geohash points test] 0\n    } {ezs42e44yx0}\n\n    test {GEOPOS simple} {\n        r del points\n        r geoadd points 10 20 a 30 40 b\n        lassign [lindex [r geopos points a b] 0] x1 y1\n        lassign [lindex [r geopos points a b] 1] x2 y2\n        assert {abs($x1 - 10) < 0.001}\n        assert {abs($y1 - 20) < 0.001}\n        assert {abs($x2 - 30) < 0.001}\n        assert {abs($y2 - 40) < 0.001}\n    }\n\n    test {GEOPOS missing element} {\n        r del points\n        r geoadd points 10 20 a 30 40 b\n        lindex [r geopos points a x b] 1\n    } {}\n\n    test {GEODIST simple & unit} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        set m [r geodist points Palermo Catania]\n        assert {$m > 166274 && $m < 166275}\n        set km [r geodist points Palermo Catania km]\n        assert {$km > 166.2 && $km < 166.3}\n    }\n\n    test {GEODIST missing elements} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        set m [r geodist points Palermo Agrigento]\n        assert {$m eq {}}\n        set m [r geodist points Ragusa Agrigento]\n        assert {$m eq {}}\n        set m [r geodist empty_key Palermo Catania]\n        assert {$m eq {}}\n    }\n\n    test {GEORADIUS STORE option: syntax error} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        catch {r georadius points 13.361389 38.115556 50 km store} e\n        set e\n    } {*ERR*syntax*}\n\n    test {GEORANGE STORE option: incompatible options} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        catch {r georadius points 13.361389 38.115556 50 km store points2 withdist} e\n        assert_match {*ERR*} $e\n        catch {r georadius points 13.361389 38.115556 50 km store points2 withhash} e\n        assert_match {*ERR*} $e\n        catch {r georadius points 13.361389 38.115556 50 km store points2 withcoords} e\n        assert_match {*ERR*} $e\n    }\n\n    test {GEORANGE STORE option: plain usage} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        r georadius points 13.361389 38.115556 500 km store points2\n        assert_equal [r zrange points 0 -1] [r zrange points2 0 -1]\n    }\n\n    test {GEORANGE STOREDIST option: plain usage} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        r georadius points 13.361389 38.115556 500 km storedist points2\n        set res [r zrange points2 0 -1 withscores]\n        assert {[lindex $res 1] < 1}\n        assert {[lindex $res 3] > 166}\n        assert {[lindex $res 3] < 167}\n    }\n\n    test {GEORANGE STOREDIST option: COUNT ASC and DESC} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        r georadius points 13.361389 38.115556 500 km storedist points2 asc count 1\n        assert {[r zcard points2] == 1}\n        set res [r zrange points2 0 -1 withscores]\n        assert {[lindex $res 0] eq \"Palermo\"}\n\n        r georadius points 13.361389 38.115556 500 km storedist points2 desc count 1\n        assert {[r zcard points2] == 1}\n        set res [r zrange points2 0 -1 withscores]\n        assert {[lindex $res 0] eq \"Catania\"}\n    }\n\n    test {GEOADD + GEORANGE randomized test} {\n        set attempt 30\n        while {[incr attempt -1]} {\n            set rv [lindex $regression_vectors $rv_idx]\n            incr rv_idx\n\n            unset -nocomplain debuginfo\n            set srand_seed [clock milliseconds]\n            if {$rv ne {}} {set srand_seed [lindex $rv 0]}\n            lappend debuginfo \"srand_seed is $srand_seed\"\n            expr {srand($srand_seed)} ; # If you need a reproducible run\n            r del mypoints\n\n            if {[randomInt 10] == 0} {\n                # From time to time use very big radiuses\n                set radius_km [expr {[randomInt 50000]+10}]\n            } else {\n                # Normally use a few - ~200km radiuses to stress\n                # test the code the most in edge cases.\n                set radius_km [expr {[randomInt 200]+10}]\n            }\n            if {$rv ne {}} {set radius_km [lindex $rv 1]}\n            set radius_m [expr {$radius_km*1000}]\n            geo_random_point search_lon search_lat\n            if {$rv ne {}} {\n                set search_lon [lindex $rv 2]\n                set search_lat [lindex $rv 3]\n            }\n            lappend debuginfo \"Search area: $search_lon,$search_lat $radius_km km\"\n            set tcl_result {}\n            set argv {}\n            for {set j 0} {$j < 20000} {incr j} {\n                geo_random_point lon lat\n                lappend argv $lon $lat \"place:$j\"\n                set distance [geo_distance $lon $lat $search_lon $search_lat]\n                if {$distance < $radius_m} {\n                    lappend tcl_result \"place:$j\"\n                }\n                lappend debuginfo \"place:$j $lon $lat [expr {$distance/1000}] km\"\n            }\n            r geoadd mypoints {*}$argv\n            set res [lsort [r georadius mypoints $search_lon $search_lat $radius_km km]]\n            set res2 [lsort $tcl_result]\n            set test_result OK\n\n            if {$res != $res2} {\n                set rounding_errors 0\n                set diff [compare_lists $res $res2]\n                foreach place $diff {\n                    set mydist [geo_distance $lon $lat $search_lon $search_lat]\n                    set mydist [expr $mydist/1000]\n                    if {($mydist / $radius_km) > 0.999} {\n                        incr rounding_errors\n                        continue\n                    }\n                    if {$mydist < $radius_m} {\n                        # This is a false positive for redis since given the \n                        # same points the higher precision calculation provided \n                        # by TCL shows the point within range\n                        incr rounding_errors\n                        continue\n                    }\n                }\n\n                # Make sure this is a real error and not a rounidng issue.\n                if {[llength $diff] == $rounding_errors} {\n                    set res $res2; # Error silenced\n                }\n            }\n\n            if {$res != $res2} {\n                set diff [compare_lists $res $res2]\n                puts \"*** Possible problem in GEO radius query ***\"\n                puts \"Redis: $res\"\n                puts \"Tcl  : $res2\"\n                puts \"Diff : $diff\"\n                puts [join $debuginfo \"\\n\"]\n                foreach place $diff {\n                    if {[lsearch -exact $res2 $place] != -1} {\n                        set where \"(only in Tcl)\"\n                    } else {\n                        set where \"(only in Redis)\"\n                    }\n                    lassign [lindex [r geopos mypoints $place] 0] lon lat\n                    set mydist [geo_distance $lon $lat $search_lon $search_lat]\n                    set mydist [expr $mydist/1000]\n                    puts \"$place -> [r geopos mypoints $place] $mydist $where\"\n                    if {($mydist / $radius_km) > 0.999} {incr rounding_errors}\n                }\n                set test_result FAIL\n            }\n            unset -nocomplain debuginfo\n            if {$test_result ne {OK}} break\n        }\n        set test_result\n    } {OK}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 {Fuzzing dense/sparse encoding: Redis should always detect errors} {\n        for {set j 0} {$j < 1000} {incr j} {\n            r del hll\n            set items {}\n            set numitems [randomInt 3000]\n            for {set i 0} {$i < $numitems} {incr i} {\n                lappend items [expr {rand()}]\n            }\n            r pfadd hll {*}$items\n\n            # Corrupt it in some random way.\n            for {set i 0} {$i < 5} {incr i} {\n                set len [r strlen hll]\n                set pos [randomInt $len]\n                set byte [randstring 1 1 binary]\n                r setrange hll $pos $byte\n                # Don't modify more bytes 50% of times\n                if {rand() < 0.5} break\n            }\n\n            # Use the hyperloglog to check if it crashes\n            # Redis in some way.\n            catch {\n                r pfcount hll\n            }\n        }\n    }\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 #1} {\n        r del hll1 hll2 hll3\n        for {set x 1} {$x < 10000} {incr x} {\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 {PFCOUNT multiple-keys merge returns cardinality of union #2} {\n        r del hll1 hll2 hll3\n        set elements {}\n        for {set x 1} {$x < 10000} {incr x} {\n            for {set j 1} {$j <= 3} {incr j} {\n                set rint [randomInt 20000]\n                r pfadd hll$j $rint\n                lappend elements $rint\n            }\n        }\n        set realcard [llength [lsort -unique $elements]]\n        set card [r pfcount hll1 hll2 hll3]\n        set err [expr {abs($card-$realcard)}]\n        assert {$err < (double($card)/100)*5}\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    test {PFADD / PFCOUNT cache invalidation works} {\n        r del hll\n        r pfadd hll a b c\n        r pfcount hll\n        assert {[r getrange hll 15 15] eq \"\\x00\"}\n        r pfadd hll a b c\n        assert {[r getrange hll 15 15] eq \"\\x00\"}\n        r pfadd hll 1 2 3\n        assert {[r getrange hll 15 15] eq \"\\x80\"}\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/introspection-2.tcl",
    "content": "proc cmdstat {cmd} {\n    if {[regexp \"\\r\\ncmdstat_$cmd:(.*?)\\r\\n\" [r info commandstats] _ value]} {\n        set _ $value\n    }\n}\n\nstart_server {tags {\"introspection\"}} {\n    test {TTL and TYPYE do not alter the last access time of a key} {\n        r set foo bar\n        after 3000\n        r ttl foo\n        r type foo\n        assert {[r object idletime foo] >= 2}\n    }\n\n    test {TOUCH alters the last access time of a key} {\n        r set foo bar\n        after 3000\n        r touch foo\n        assert {[r object idletime foo] < 2}\n    }\n\n    test {TOUCH returns the number of existing keys specified} {\n        r flushdb\n        r set key1 1\n        r set key2 2\n        r touch key0 key1 key2 key3\n    } 2\n\n    test {command stats for GEOADD} {\n        r config resetstat\n        r GEOADD foo 0 0 bar\n        assert_match {*calls=1,*} [cmdstat geoadd]\n        assert_match {} [cmdstat zadd]\n    }\n\n    test {command stats for EXPIRE} {\n        r config resetstat\n        r SET foo bar\n        r EXPIRE foo 0\n        assert_match {*calls=1,*} [cmdstat expire]\n        assert_match {} [cmdstat del]\n    }\n\n    test {command stats for BRPOP} {\n        r config resetstat\n        r LPUSH list foo\n        r BRPOP list 0\n        assert_match {*calls=1,*} [cmdstat brpop]\n        assert_match {} [cmdstat rpop]\n    }\n\n    test {command stats for MULTI} {\n        r config resetstat\n        r MULTI\n        r set foo bar\n        r GEOADD foo2 0 0 bar\n        r EXPIRE foo2 0\n        r EXEC\n        assert_match {*calls=1,*} [cmdstat multi]\n        assert_match {*calls=1,*} [cmdstat exec]\n        assert_match {*calls=1,*} [cmdstat set]\n        assert_match {*calls=1,*} [cmdstat expire]\n        assert_match {*calls=1,*} [cmdstat geoadd]\n    }\n\n    test {command stats for scripts} {\n        r config resetstat\n        r set mykey myval\n        r eval {\n            redis.call('set', KEYS[1], 0)\n            redis.call('expire', KEYS[1], 0)\n            redis.call('geoadd', KEYS[1], 0, 0, \"bar\")\n        } 1 mykey\n        assert_match {*calls=1,*} [cmdstat eval]\n        assert_match {*calls=2,*} [cmdstat set]\n        assert_match {*calls=1,*} [cmdstat expire]\n        assert_match {*calls=1,*} [cmdstat geoadd]\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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=26 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        assert_match {*OK*} [$rd read]\n        r set foo bar\n        r get foo\n        list [$rd read] [$rd read]\n    } {*\"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        $rd read ;# Discard the OK\n        r eval {redis.call('set',KEYS[1],ARGV[1])} 1 foo bar\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    test {CONFIG sanity} {\n        # Do CONFIG GET, CONFIG SET and then CONFIG GET again\n        # Skip immutable configs, one with no get, and other complicated configs\n        set skip_configs {\n            rdbchecksum\n            daemonize\n            io-threads-do-reads\n            tcp-backlog\n            always-show-logo\n            syslog-enabled\n            cluster-enabled\n            aclfile\n            unixsocket\n            pidfile\n            syslog-ident\n            appendfilename\n            supervised\n            syslog-facility\n            databases\n            port\n            io-threads\n            tls-port\n            tls-prefer-server-ciphers\n            tls-cert-file\n            tls-key-file\n            tls-dh-params-file\n            tls-ca-cert-file\n            tls-ca-cert-dir\n            tls-protocols\n            tls-ciphers\n            tls-ciphersuites\n            logfile\n            unixsocketperm\n            slaveof\n            bind\n            requirepass\n            server_cpulist\n            bio_cpulist\n            aof_rewrite_cpulist\n            bgsave_cpulist\n        }\n\n        set configs {}\n        foreach {k v} [r config get *] {\n            if {[lsearch $skip_configs $k] != -1} {\n                continue\n            }\n            dict set configs $k $v\n            # try to set the config to the same value it already has\n            r config set $k $v\n        }\n\n        set newconfigs {}\n        foreach {k v} [r config get *] {\n            if {[lsearch $skip_configs $k] != -1} {\n                continue\n            }\n            dict set newconfigs $k $v\n        }\n\n        dict for {k v} $configs {\n            set vv [dict get $newconfigs $k]\n            if {$v != $vv} {\n                fail \"config $k mismatch, expecting $v but got $vv\"\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/keyspace.tcl",
    "content": "start_server {tags {\"keyspace\"}} {\n    test {DEL against a single item} {\n        r set x foo\n        assert {[r get x] eq \"foo\"}\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 \"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* [r read]]\n        append res [r read]\n        append res [string match PONG* [r read]]\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 are the same (existing)} {\n        r set mykey foo\n        r rename mykey mykey\n    } {OK}\n\n    test {RENAMENX where source and dest key are the same (existing)} {\n        r set mykey foo\n        r renamenx mykey mykey\n    } {0}\n\n    test {RENAME where source and dest key are the same (non existing)} {\n        r del mykey\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 {MOVE against non-integer DB (#1428)} {\n        r set mykey hello\n        catch {r move mykey notanumber} e\n        set e\n    } {*ERR*index out of range}\n\n    test {MOVE can move key expire metadata as well} {\n        r select 10\n        r flushdb\n        r select 9\n        r set mykey foo ex 100\n        r move mykey 10\n        assert {[r ttl mykey] == -2}\n        r select 10\n        assert {[r ttl mykey] > 0 && [r ttl mykey] <= 100}\n        assert {[r get mykey] eq \"foo\"}\n        r select 9\n    }\n\n    test {MOVE does not create an expire if it does not exist} {\n        r select 10\n        r flushdb\n        r select 9\n        r set mykey foo\n        r move mykey 10\n        assert {[r ttl mykey] == -2}\n        r select 10\n        assert {[r ttl mykey] == -1}\n        assert {[r get mykey] eq \"foo\"}\n        r select 9\n    }\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 {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 {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": "Chapter02/redis-6.0.3/tests/unit/latency-monitor.tcl",
    "content": "start_server {tags {\"latency-monitor\"}} {\n    # Set a threshold high enough to avoid spurious latency events.\n    r config set latency-monitor-threshold 200\n    r latency reset\n\n    test {Test latency events logging} {\n        r debug sleep 0.3\n        after 1100\n        r debug sleep 0.4\n        after 1100\n        r debug sleep 0.5\n        assert {[r latency history command] >= 3}\n    }\n\n    test {LATENCY HISTORY output is ok} {\n        set min 250\n        set max 450\n        foreach event [r latency history command] {\n            lassign $event time latency\n            assert {$latency >= $min && $latency <= $max}\n            incr min 100\n            incr max 100\n            set last_time $time ; # Used in the next test\n        }\n    }\n\n    test {LATENCY LATEST output is ok} {\n        foreach event [r latency latest] {\n            lassign $event eventname time latency max\n            assert {$eventname eq \"command\"}\n            assert {$max >= 450 & $max <= 650}\n            assert {$time == $last_time}\n            break\n        }\n    }\n\n    test {LATENCY HISTORY / RESET with wrong event name is fine} {\n        assert {[llength [r latency history blabla]] == 0}\n        assert {[r latency reset blabla] == 0}\n    }\n\n    test {LATENCY DOCTOR produces some output} {\n        assert {[string length [r latency doctor]] > 0}\n    }\n\n    test {LATENCY RESET is able to reset events} {\n        assert {[r latency reset] > 0}\n        assert {[r latency latest] eq {}}\n    }\n\n    test {LATENCY of expire events are correctly collected} {\n        r config set latency-monitor-threshold 20\n        r eval {\n            local i = 0\n            while (i < 1000000) do\n                redis.call('sadd','mybigkey',i)\n                i = i+1\n             end\n        } 0\n        r pexpire mybigkey 1\n        after 500\n        assert_match {*expire-cycle*} [r latency latest]\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/lazyfree.tcl",
    "content": "start_server {tags {\"lazyfree\"}} {\n    test \"UNLINK can reclaim memory in background\" {\n        set orig_mem [s used_memory]\n        set args {}\n        for {set i 0} {$i < 100000} {incr i} {\n            lappend args $i\n        }\n        r sadd myset {*}$args\n        assert {[r scard myset] == 100000}\n        set peak_mem [s used_memory]\n        assert {[r unlink myset] == 1}\n        assert {$peak_mem > $orig_mem+1000000}\n        wait_for_condition 50 100 {\n            [s used_memory] < $peak_mem &&\n            [s used_memory] < $orig_mem*2\n        } else {\n            fail \"Memory is not reclaimed by UNLINK\"\n        }\n    }\n\n    test \"FLUSHDB ASYNC can reclaim memory in background\" {\n        set orig_mem [s used_memory]\n        set args {}\n        for {set i 0} {$i < 100000} {incr i} {\n            lappend args $i\n        }\n        r sadd myset {*}$args\n        assert {[r scard myset] == 100000}\n        set peak_mem [s used_memory]\n        r flushdb async\n        assert {$peak_mem > $orig_mem+1000000}\n        wait_for_condition 50 100 {\n            [s used_memory] < $peak_mem &&\n            [s used_memory] < $orig_mem*2\n        } else {\n            fail \"Memory is not reclaimed by FLUSHDB ASYNC\"\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/limits.tcl",
    "content": "start_server {tags {\"limits\"} overrides {maxclients 10}} {\n    if {$::tls} {\n        set expected_code \"*I/O error*\"\n    } else {\n        set expected_code \"*ERR max*reached*\"\n    }\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    } $expected_code\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/maxmemory.tcl",
    "content": "start_server {tags {\"maxmemory\"}} {\n    test \"Without maxmemory small integers are shared\" {\n        r config set maxmemory 0\n        r set a 1\n        assert {[r object refcount a] > 1}\n    }\n\n    test \"With maxmemory and non-LRU policy integers are still shared\" {\n        r config set maxmemory 1073741824\n        r config set maxmemory-policy allkeys-random\n        r set a 1\n        assert {[r object refcount a] > 1}\n    }\n\n    test \"With maxmemory and LRU policy integers are not shared\" {\n        r config set maxmemory 1073741824\n        r config set maxmemory-policy allkeys-lru\n        r set a 1\n        r config set maxmemory-policy volatile-lru\n        r set b 1\n        assert {[r object refcount a] == 1}\n        assert {[r object refcount b] == 1}\n        r config set maxmemory 0\n    }\n\n    foreach policy {\n        allkeys-random allkeys-lru allkeys-lfu volatile-lru volatile-lfu 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-lfu 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\nproc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline} {\n    start_server {tags {\"maxmemory\"}} {\n        start_server {} {\n        set slave_pid [s process_id]\n        test \"$test_name\" {\n            set slave [srv 0 client]\n            set slave_host [srv 0 host]\n            set slave_port [srv 0 port]\n            set master [srv -1 client]\n            set master_host [srv -1 host]\n            set master_port [srv -1 port]\n\n            # add 100 keys of 100k (10MB total)\n            for {set j 0} {$j < 100} {incr j} {\n                $master setrange \"key:$j\" 100000 asdf\n            }\n\n            # make sure master doesn't disconnect slave because of timeout\n            $master config set repl-timeout 1200 ;# 20 minutes (for valgrind and slow machines)\n            $master config set maxmemory-policy allkeys-random\n            $master config set client-output-buffer-limit \"replica 100000000 100000000 300\"\n            $master config set repl-backlog-size [expr {10*1024}]\n\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            # measure used memory after the slave connected and set maxmemory\n            set orig_used [s -1 used_memory]\n            set orig_client_buf [s -1 mem_clients_normal]\n            set orig_mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]\n            set orig_used_no_repl [expr {$orig_used - $orig_mem_not_counted_for_evict}]\n            set limit [expr {$orig_used - $orig_mem_not_counted_for_evict + 20*1024}]\n\n            if {$limit_memory==1} {\n                $master config set maxmemory $limit\n            }\n\n            # put the slave to sleep\n            set rd_slave [redis_deferring_client]\n            exec kill -SIGSTOP $slave_pid\n\n            # send some 10mb worth of commands that don't increase the memory usage\n            if {$pipeline == 1} {\n                set rd_master [redis_deferring_client -1]\n                for {set k 0} {$k < $cmd_count} {incr k} {\n                    $rd_master setrange key:0 0 [string repeat A $payload_len]\n                }\n                for {set k 0} {$k < $cmd_count} {incr k} {\n                    #$rd_master read\n                }\n            } else {\n                for {set k 0} {$k < $cmd_count} {incr k} {\n                    $master setrange key:0 0 [string repeat A $payload_len]\n                }\n            }\n\n            set new_used [s -1 used_memory]\n            set slave_buf [s -1 mem_clients_slaves]\n            set client_buf [s -1 mem_clients_normal]\n            set mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]\n            set used_no_repl [expr {$new_used - $mem_not_counted_for_evict}]\n            set delta [expr {($used_no_repl - $client_buf) - ($orig_used_no_repl - $orig_client_buf)}]\n\n            assert {[$master dbsize] == 100}\n            assert {$slave_buf > 2*1024*1024} ;# some of the data may have been pushed to the OS buffers\n            set delta_max [expr {$cmd_count / 2}] ;# 1 byte unaccounted for, with 1M commands will consume some 1MB\n            assert {$delta < $delta_max && $delta > -$delta_max}\n\n            $master client kill type slave\n            set killed_used [s -1 used_memory]\n            set killed_slave_buf [s -1 mem_clients_slaves]\n            set killed_mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]\n            set killed_used_no_repl [expr {$killed_used - $killed_mem_not_counted_for_evict}]\n            set delta_no_repl [expr {$killed_used_no_repl - $used_no_repl}]\n            assert {$killed_slave_buf == 0}\n            assert {$delta_no_repl > -$delta_max && $delta_no_repl < $delta_max}\n\n        }\n        # unfreeze slave process (after the 'test' succeeded or failed, but before we attempt to terminate the server\n        exec kill -SIGCONT $slave_pid\n        }\n    }\n}\n\n# test that slave buffer are counted correctly\n# we wanna use many small commands, and we don't wanna wait long\n# so we need to use a pipeline (redis_deferring_client)\n# that may cause query buffer to fill and induce eviction, so we disable it\ntest_slave_buffers {slave buffer are counted correctly} 1000000 10 0 1\n\n# test that slave buffer don't induce eviction\n# test again with fewer (and bigger) commands without pipeline, but with eviction\ntest_slave_buffers \"replica buffer don't induce eviction\" 100000 100 1 0\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/memefficiency.tcl",
    "content": "proc test_memory_efficiency {range} {\n    r flushall\n    set rd [redis_deferring_client]\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        $rd 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    for {set j 0} {$j < 10000} {incr j} {\n        $rd read ; # Discard replies\n    }\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\nrun_solo {defrag} {\nstart_server {tags {\"defrag\"}} {\n    if {[string match {*jemalloc*} [s mem_allocator]]} {\n        test \"Active defrag\" {\n            r config set save \"\" ;# prevent bgsave from interfereing with save below\n            r config set hz 100\n            r config set activedefrag no\n            r config set active-defrag-threshold-lower 5\n            r config set active-defrag-cycle-min 65\n            r config set active-defrag-cycle-max 75\n            r config set active-defrag-ignore-bytes 2mb\n            r config set maxmemory 100mb\n            r config set maxmemory-policy allkeys-lru\n            r debug populate 700000 asdf1 150\n            r debug populate 170000 asdf2 300\n            r ping ;# trigger eviction following the previous population\n            after 120 ;# serverCron only updates the info once in 100ms\n            set frag [s allocator_frag_ratio]\n            if {$::verbose} {\n                puts \"frag $frag\"\n            }\n            assert {$frag >= 1.4}\n\n            r config set latency-monitor-threshold 5\n            r latency reset\n            r config set maxmemory 110mb ;# prevent further eviction (not to fail the digest test)\n            set digest [r debug digest]\n            catch {r config set activedefrag yes} e\n            if {![string match {DISABLED*} $e]} {\n                # Wait for the active defrag to start working (decision once a\n                # second).\n                wait_for_condition 50 100 {\n                    [s active_defrag_running] ne 0\n                } else {\n                    fail \"defrag not started.\"\n                }\n\n                # Wait for the active defrag to stop working.\n                wait_for_condition 150 100 {\n                    [s active_defrag_running] eq 0\n                } else {\n                    after 120 ;# serverCron only updates the info once in 100ms\n                    puts [r info memory]\n                    puts [r memory malloc-stats]\n                    fail \"defrag didn't stop.\"\n                }\n\n                # Test the the fragmentation is lower.\n                after 120 ;# serverCron only updates the info once in 100ms\n                set frag [s allocator_frag_ratio]\n                set max_latency 0\n                foreach event [r latency latest] {\n                    lassign $event eventname time latency max\n                    if {$eventname == \"active-defrag-cycle\"} {\n                        set max_latency $max\n                    }\n                }\n                if {$::verbose} {\n                    puts \"frag $frag\"\n                    puts \"max latency $max_latency\"\n                    puts [r latency latest]\n                    puts [r latency history active-defrag-cycle]\n                }\n                assert {$frag < 1.1}\n                # due to high fragmentation, 100hz, and active-defrag-cycle-max set to 75,\n                # we expect max latency to be not much higher than 7.5ms but due to rare slowness threshold is set higher\n                assert {$max_latency <= 30}\n            } else {\n                set _ \"\"\n            }\n            # verify the data isn't corrupted or changed\n            set newdigest [r debug digest]\n            assert {$digest eq $newdigest}\n            r save ;# saving an rdb iterates over all the data / pointers\n        } {OK}\n\n        test \"Active defrag big keys\" {\n            r flushdb\n            r config resetstat\n            r config set save \"\" ;# prevent bgsave from interfereing with save below\n            r config set hz 100\n            r config set activedefrag no\n            r config set active-defrag-max-scan-fields 1000\n            r config set active-defrag-threshold-lower 5\n            r config set active-defrag-cycle-min 65\n            r config set active-defrag-cycle-max 75\n            r config set active-defrag-ignore-bytes 2mb\n            r config set maxmemory 0\n            r config set list-max-ziplist-size 5 ;# list of 10k items will have 2000 quicklist nodes\n            r config set stream-node-max-entries 5\n            r hmset hash h1 v1 h2 v2 h3 v3\n            r lpush list a b c d\n            r zadd zset 0 a 1 b 2 c 3 d\n            r sadd set a b c d\n            r xadd stream * item 1 value a\n            r xadd stream * item 2 value b\n            r xgroup create stream mygroup 0\n            r xreadgroup GROUP mygroup Alice COUNT 1 STREAMS stream >\n\n            # create big keys with 10k items\n            set rd [redis_deferring_client]\n            for {set j 0} {$j < 10000} {incr j} {\n                $rd hset bighash $j [concat \"asdfasdfasdf\" $j]\n                $rd lpush biglist [concat \"asdfasdfasdf\" $j]\n                $rd zadd bigzset $j [concat \"asdfasdfasdf\" $j]\n                $rd sadd bigset [concat \"asdfasdfasdf\" $j]\n                $rd xadd bigstream * item 1 value a\n            }\n            for {set j 0} {$j < 50000} {incr j} {\n                $rd read ; # Discard replies\n            }\n\n            set expected_frag 1.7\n            if {$::accurate} {\n                # scale the hash to 1m fields in order to have a measurable the latency\n                for {set j 10000} {$j < 1000000} {incr j} {\n                    $rd hset bighash $j [concat \"asdfasdfasdf\" $j]\n                }\n                for {set j 10000} {$j < 1000000} {incr j} {\n                    $rd read ; # Discard replies\n                }\n                # creating that big hash, increased used_memory, so the relative frag goes down\n                set expected_frag 1.3\n            }\n\n            # add a mass of string keys\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd setrange $j 150 a\n            }\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd read ; # Discard replies\n            }\n            assert_equal [r dbsize] 500010\n\n            # create some fragmentation\n            for {set j 0} {$j < 500000} {incr j 2} {\n                $rd del $j\n            }\n            for {set j 0} {$j < 500000} {incr j 2} {\n                $rd read ; # Discard replies\n            }\n            assert_equal [r dbsize] 250010\n\n            # start defrag\n            after 120 ;# serverCron only updates the info once in 100ms\n            set frag [s allocator_frag_ratio]\n            if {$::verbose} {\n                puts \"frag $frag\"\n            }\n            assert {$frag >= $expected_frag}\n            r config set latency-monitor-threshold 5\n            r latency reset\n\n            set digest [r debug digest]\n            catch {r config set activedefrag yes} e\n            if {![string match {DISABLED*} $e]} {\n                # wait for the active defrag to start working (decision once a second)\n                wait_for_condition 50 100 {\n                    [s active_defrag_running] ne 0\n                } else {\n                    fail \"defrag not started.\"\n                }\n\n                # wait for the active defrag to stop working\n                wait_for_condition 500 100 {\n                    [s active_defrag_running] eq 0\n                } else {\n                    after 120 ;# serverCron only updates the info once in 100ms\n                    puts [r info memory]\n                    puts [r memory malloc-stats]\n                    fail \"defrag didn't stop.\"\n                }\n\n                # test the the fragmentation is lower\n                after 120 ;# serverCron only updates the info once in 100ms\n                set frag [s allocator_frag_ratio]\n                set max_latency 0\n                foreach event [r latency latest] {\n                    lassign $event eventname time latency max\n                    if {$eventname == \"active-defrag-cycle\"} {\n                        set max_latency $max\n                    }\n                }\n                if {$::verbose} {\n                    puts \"frag $frag\"\n                    puts \"max latency $max_latency\"\n                    puts [r latency latest]\n                    puts [r latency history active-defrag-cycle]\n                }\n                assert {$frag < 1.1}\n                # due to high fragmentation, 100hz, and active-defrag-cycle-max set to 75,\n                # we expect max latency to be not much higher than 7.5ms but due to rare slowness threshold is set higher\n                assert {$max_latency <= 30}\n            }\n            # verify the data isn't corrupted or changed\n            set newdigest [r debug digest]\n            assert {$digest eq $newdigest}\n            r save ;# saving an rdb iterates over all the data / pointers\n        } {OK}\n\n        test \"Active defrag big list\" {\n            r flushdb\n            r config resetstat\n            r config set save \"\" ;# prevent bgsave from interfereing with save below\n            r config set hz 100\n            r config set activedefrag no\n            r config set active-defrag-max-scan-fields 1000\n            r config set active-defrag-threshold-lower 5\n            r config set active-defrag-cycle-min 65\n            r config set active-defrag-cycle-max 75\n            r config set active-defrag-ignore-bytes 2mb\n            r config set maxmemory 0\n            r config set list-max-ziplist-size 5 ;# list of 500k items will have 100k quicklist nodes\n\n            # create big keys with 10k items\n            set rd [redis_deferring_client]\n\n            set expected_frag 1.7\n            # add a mass of list nodes to two lists (allocations are interlaced)\n            set val [string repeat A 100] ;# 5 items of 100 bytes puts us in the 640 bytes bin, which has 32 regs, so high potential for fragmentation\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd lpush biglist1 $val\n                $rd lpush biglist2 $val\n            }\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd read ; # Discard replies\n                $rd read ; # Discard replies\n            }\n\n            # create some fragmentation\n            r del biglist2\n\n            # start defrag\n            after 120 ;# serverCron only updates the info once in 100ms\n            set frag [s allocator_frag_ratio]\n            if {$::verbose} {\n                puts \"frag $frag\"\n            }\n\n            assert {$frag >= $expected_frag}\n            r config set latency-monitor-threshold 5\n            r latency reset\n\n            set digest [r debug digest]\n            catch {r config set activedefrag yes} e\n            if {![string match {DISABLED*} $e]} {\n                # wait for the active defrag to start working (decision once a second)\n                wait_for_condition 50 100 {\n                    [s active_defrag_running] ne 0\n                } else {\n                    fail \"defrag not started.\"\n                }\n\n                # wait for the active defrag to stop working\n                wait_for_condition 500 100 {\n                    [s active_defrag_running] eq 0\n                } else {\n                    after 120 ;# serverCron only updates the info once in 100ms\n                    puts [r info memory]\n                    puts [r info stats]\n                    puts [r memory malloc-stats]\n                    fail \"defrag didn't stop.\"\n                }\n\n                # test the the fragmentation is lower\n                after 120 ;# serverCron only updates the info once in 100ms\n                set frag [s allocator_frag_ratio]\n                set max_latency 0\n                foreach event [r latency latest] {\n                    lassign $event eventname time latency max\n                    if {$eventname == \"active-defrag-cycle\"} {\n                        set max_latency $max\n                    }\n                }\n                if {$::verbose} {\n                    puts \"frag $frag\"\n                    puts \"max latency $max_latency\"\n                    puts [r latency latest]\n                    puts [r latency history active-defrag-cycle]\n                }\n                assert {$frag < 1.1}\n                # due to high fragmentation, 100hz, and active-defrag-cycle-max set to 75,\n                # we expect max latency to be not much higher than 7.5ms but due to rare slowness threshold is set higher\n                assert {$max_latency <= 30}\n            }\n            # verify the data isn't corrupted or changed\n            set newdigest [r debug digest]\n            assert {$digest eq $newdigest}\n            r save ;# saving an rdb iterates over all the data / pointers\n            r del biglist1 ;# coverage for quicklistBookmarksClear\n        } {1}\n    }\n}\n} ;# run_solo\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/auth.tcl",
    "content": "set testmodule [file normalize tests/modules/auth.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {Modules can create a user that can be authenticated} {\n        # Make sure we start authenticated with default user\n        r auth default \"\"\n        assert_equal [r acl whoami] \"default\"\n        r auth.createmoduleuser\n\n        set id [r auth.authmoduleuser]\n        assert_equal [r client id] $id\n\n        # Verify returned id is the same as our current id and\n        # we are authenticated with the specified user\n        assert_equal [r acl whoami] \"global\"\n    }\n\n    test {De-authenticating clients is tracked and kills clients} {\n        assert_equal [r auth.changecount] 0\n        r auth.createmoduleuser\n\n        # Catch the I/O exception that was thrown when Redis\n        # disconnected with us. \n        catch { [r ping] } e\n        assert_match {*I/O*} $e\n\n        # Check that a user change was registered\n        assert_equal [r auth.changecount] 1\n    }\n\n    test {Modules cant authenticate with ACLs users that dont exist} {\n        catch { [r auth.authrealuser auth-module-test-fake] } e\n        assert_match {*Invalid user*} $e\n    }\n\n    test {Modules can authenticate with ACL users} {\n        assert_equal [r acl whoami] \"default\"\n\n        # Create user to auth into\n        r acl setuser auth-module-test on allkeys allcommands\n\n        set id [r auth.authrealuser auth-module-test]\n\n        # Verify returned id is the same as our current id and\n        # we are authenticated with the specified user\n        assert_equal [r client id] $id\n        assert_equal [r acl whoami] \"auth-module-test\"\n    }\n\n    test {Client callback is called on user switch} {\n        assert_equal [r auth.changecount] 0\n\n        # Auth again and validate change count\n        r auth.authrealuser auth-module-test\n        assert_equal [r auth.changecount] 1\n\n        # Re-auth with the default user\n        r auth default \"\"\n        assert_equal [r auth.changecount] 1\n        assert_equal [r acl whoami] \"default\"\n\n        # Re-auth with the default user again, to\n        # verify the callback isn't fired again\n        r auth default \"\"\n        assert_equal [r auth.changecount] 0\n        assert_equal [r acl whoami] \"default\"\n    }\n\n}"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/blockonkeys.tcl",
    "content": "set testmodule [file normalize tests/modules/blockonkeys.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test \"Module client blocked on keys: Circular BPOPPUSH\" {\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n\n        r del src dst\n\n        $rd1 fsl.bpoppush src dst 0\n        $rd2 fsl.bpoppush dst src 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {2}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n\n        r fsl.push src 42\n\n        assert_equal {42} [r fsl.getall src]\n        assert_equal {} [r fsl.getall dst]\n    }\n\n    test \"Module client blocked on keys: Self-referential BPOPPUSH\" {\n        set rd1 [redis_deferring_client]\n\n        r del src\n\n        $rd1 fsl.bpoppush src src 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push src 42\n\n        assert_equal {42} [r fsl.getall src]\n    }\n\n    test {Module client blocked on keys (no metadata): No block} {\n        r del k\n        r fsl.push k 33\n        r fsl.push k 34\n        r fsl.bpop k 0\n    } {34}\n\n    test {Module client blocked on keys (no metadata): Timeout} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpop k 1\n        assert_equal {Request timedout} [$rd read]\n    }\n\n    test {Module client blocked on keys (no metadata): Blocked} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpop k 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push k 34\n        assert_equal {34} [$rd read]\n    }\n\n    test {Module client blocked on keys (with metadata): No block} {\n        r del k\n        r fsl.push k 34\n        r fsl.bpopgt k 30 0\n    } {34}\n\n    test {Module client blocked on keys (with metadata): Timeout} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        r fsl.push k 33\n        $rd fsl.bpopgt k 35 1\n        assert_equal {Request timedout} [$rd read]\n        r client kill id $cid ;# try to smoke-out client-related memory leak\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, case 1} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        r fsl.push k 33\n        $rd fsl.bpopgt k 33 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push k 34\n        assert_equal {34} [$rd read]\n        r client kill id $cid ;# try to smoke-out client-related memory leak\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, case 2} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push k 33\n        r fsl.push k 34\n        r fsl.push k 35\n        r fsl.push k 36\n        assert_equal {36} [$rd read]\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, CLIENT KILL} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r client kill id $cid ;# try to smoke-out client-related memory leak\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, CLIENT UNBLOCK TIMEOUT} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r client unblock $cid timeout ;# try to smoke-out client-related memory leak\n        assert_equal {Request timedout} [$rd read]\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, CLIENT UNBLOCK ERROR} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r client unblock $cid error ;# try to smoke-out client-related memory leak\n        assert_error \"*unblocked*\" {$rd read}\n    }\n\n    test {Module client blocked on keys does not wake up on wrong type} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpop k 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r lpush k 12\n        r lpush k 13\n        r lpush k 14\n        r del k\n        r fsl.push k 34\n        assert_equal {34} [$rd read]\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/commandfilter.tcl",
    "content": "set testmodule [file normalize tests/modules/commandfilter.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule log-key 0\n\n    test {Command Filter handles redirected commands} {\n        r set mykey @log\n        r lrange log-key 0 -1\n    } \"{set mykey @log}\"\n\n    test {Command Filter can call RedisModule_CommandFilterArgDelete} {\n        r rpush mylist elem1 @delme elem2\n        r lrange mylist 0 -1\n    } {elem1 elem2}\n\n    test {Command Filter can call RedisModule_CommandFilterArgInsert} {\n        r del mylist\n        r rpush mylist elem1 @insertbefore elem2 @insertafter elem3\n        r lrange mylist 0 -1\n    } {elem1 --inserted-before-- @insertbefore elem2 @insertafter --inserted-after-- elem3}\n\n    test {Command Filter can call RedisModule_CommandFilterArgReplace} {\n        r del mylist\n        r rpush mylist elem1 @replaceme elem2\n        r lrange mylist 0 -1\n    } {elem1 --replaced-- elem2}\n\n    test {Command Filter applies on RM_Call() commands} {\n        r del log-key\n        r commandfilter.ping\n        r lrange log-key 0 -1\n    } \"{ping @log}\"\n\n    test {Command Filter applies on Lua redis.call()} {\n        r del log-key\n        r eval \"redis.call('ping', '@log')\" 0\n        r lrange log-key 0 -1\n    } \"{ping @log}\"\n\n    test {Command Filter applies on Lua redis.call() that calls a module} {\n        r del log-key\n        r eval \"redis.call('commandfilter.ping')\" 0\n        r lrange log-key 0 -1\n    } \"{ping @log}\"\n\n    test {Command Filter is unregistered implicitly on module unload} {\n        r del log-key\n        r module unload commandfilter\n        r set mykey @log\n        r lrange log-key 0 -1\n    } {}\n\n    r module load $testmodule log-key 0\n\n    test {Command Filter unregister works as expected} {\n        # Validate reloading succeeded\n        r del log-key\n        r set mykey @log\n        assert_equal \"{set mykey @log}\" [r lrange log-key 0 -1]\n\n        # Unregister\n        r commandfilter.unregister\n        r del log-key\n\n        r set mykey @log\n        r lrange log-key 0 -1\n    } {}\n\n    r module unload commandfilter\n    r module load $testmodule log-key 1\n\n    test {Command Filter REDISMODULE_CMDFILTER_NOSELF works as expected} {\n        r set mykey @log\n        assert_equal \"{set mykey @log}\" [r lrange log-key 0 -1]\n\n        r del log-key\n        r commandfilter.ping\n        assert_equal {} [r lrange log-key 0 -1]\n\n        r eval \"redis.call('commandfilter.ping')\" 0\n        assert_equal {} [r lrange log-key 0 -1]\n    }\n\n} \n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/datatype.tcl",
    "content": "set testmodule [file normalize tests/modules/datatype.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {DataType: Test module is sane, GET/SET work.} {\n        r datatype.set dtkey 100 stringval\n        assert {[r datatype.get dtkey] eq {100 stringval}}\n    }\n\n    test {DataType: RM_SaveDataTypeToString(), RM_LoadDataTypeFromString() work} {\n        r datatype.set dtkey -1111 MyString\n        set encoded [r datatype.dump dtkey]\n\n        r datatype.restore dtkeycopy $encoded\n        assert {[r datatype.get dtkeycopy] eq {-1111 MyString}}\n    }\n\n    test {DataType: Handle truncated RM_LoadDataTypeFromString()} {\n        r datatype.set dtkey -1111 MyString\n        set encoded [r datatype.dump dtkey]\n        set truncated [string range $encoded 0 end-1]\n\n        catch {r datatype.restore dtkeycopy $truncated} e\n        set e\n    } {*Invalid*}\n\n    test {DataType: ModuleTypeReplaceValue() happy path works} {\n        r datatype.set key-a 1 AAA\n        r datatype.set key-b 2 BBB\n\n        assert {[r datatype.swap key-a key-b] eq {OK}}\n        assert {[r datatype.get key-a] eq {2 BBB}}\n        assert {[r datatype.get key-b] eq {1 AAA}}\n    }\n\n    test {DataType: ModuleTypeReplaceValue() fails on non-module keys} {\n        r datatype.set key-a 1 AAA\n        r set key-b RedisString\n\n        catch {r datatype.swap key-a key-b} e\n        set e\n    } {*ERR*}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/fork.tcl",
    "content": "set testmodule [file normalize tests/modules/fork.so]\n\nproc count_log_message {pattern} {\n    set result [exec grep -c $pattern < [srv 0 stdout]]\n}\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {Module fork} {\n        # the argument to fork.create is the exitcode on termination\n        r fork.create 3\n        wait_for_condition 20 100 {\n            [r fork.exitcode] != -1\n        } else {\n            fail \"fork didn't terminate\"\n        }\n        r fork.exitcode\n    } {3}\n\n    test {Module fork kill} {\n        r fork.create 3\n        after 250\n        r fork.kill\n\n        assert {[count_log_message \"fork child started\"] eq \"2\"}\n        assert {[count_log_message \"Received SIGUSR1 in child\"] eq \"1\"}\n        assert {[count_log_message \"fork child exiting\"] eq \"1\"}\n    }\n\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/hooks.tcl",
    "content": "set testmodule [file normalize tests/modules/hooks.so]\n\ntags \"modules\" {\n    start_server [list overrides [list loadmodule \"$testmodule\" appendonly yes]] {\n\n        test {Test clients connection / disconnection hooks} {\n            for {set j 0} {$j < 2} {incr j} {\n                set rd1 [redis_deferring_client]\n                $rd1 close\n            }\n            assert {[r hooks.event_count client-connected] > 1}\n            assert {[r hooks.event_count client-disconnected] > 1}\n        }\n\n        test {Test module cron hook} {\n            after 100\n            assert {[r hooks.event_count cron-loop] > 0}\n            set hz [r hooks.event_last cron-loop]\n            assert_equal $hz 10\n        }\n\n        test {Test module loaded / unloaded hooks} {\n            set othermodule [file normalize tests/modules/infotest.so]\n            r module load $othermodule\n            r module unload infotest\n            assert_equal [r hooks.event_last module-loaded] \"infotest\"\n            assert_equal [r hooks.event_last module-unloaded] \"infotest\"\n        }\n\n        test {Test module aofrw hook} {\n            r debug populate 1000 foo 10000 ;# 10mb worth of data\n            r config set rdbcompression no ;# rdb progress is only checked once in 2mb\n            r BGREWRITEAOF\n            waitForBgrewriteaof r\n            assert_equal [string match {*module-event-persistence-aof-start*} [exec tail -20 < [srv 0 stdout]]] 1\n            assert_equal [string match {*module-event-persistence-end*} [exec tail -20 < [srv 0 stdout]]] 1\n        }\n\n        test {Test module aof load and rdb/aof progress hooks} {\n            # create some aof tail (progress is checked only once in 1000 commands)\n            for {set j 0} {$j < 4000} {incr j} {\n                r set \"bar$j\" x\n            }\n            # set some configs that will cause many loading progress events during aof loading\n            r config set key-load-delay 500\n            r config set dynamic-hz no\n            r config set hz 500\n            r DEBUG LOADAOF\n            assert_equal [r hooks.event_last loading-aof-start] 0\n            assert_equal [r hooks.event_last loading-end] 0\n            assert {[r hooks.event_count loading-rdb-start] == 0}\n            assert_lessthan 2 [r hooks.event_count loading-progress-rdb] ;# comes from the preamble section\n            assert_lessthan 2 [r hooks.event_count loading-progress-aof]\n            if {$::verbose} {\n                puts \"rdb progress events [r hooks.event_count loading-progress-rdb]\"\n                puts \"aof progress events [r hooks.event_count loading-progress-aof]\"\n            }\n        }\n        # undo configs before next test\n        r config set dynamic-hz yes\n        r config set key-load-delay 0\n\n        test {Test module rdb save hook} {\n            # debug reload does: save, flush, load:\n            assert {[r hooks.event_count persistence-syncrdb-start] == 0}\n            assert {[r hooks.event_count loading-rdb-start] == 0}\n            r debug reload\n            assert {[r hooks.event_count persistence-syncrdb-start] == 1}\n            assert {[r hooks.event_count loading-rdb-start] == 1}\n        }\n\n        test {Test flushdb hooks} {\n            r flushdb\n            assert_equal [r hooks.event_last flush-start] 9\n            assert_equal [r hooks.event_last flush-end] 9\n            r flushall\n            assert_equal [r hooks.event_last flush-start] -1\n            assert_equal [r hooks.event_last flush-end] -1\n        }\n\n        # replication related tests\n        set master [srv 0 client]\n        set master_host [srv 0 host]\n        set master_port [srv 0 port]\n        start_server {} {\n            r module load $testmodule\n            set replica [srv 0 client]\n            set replica_host [srv 0 host]\n            set replica_port [srv 0 port]\n            $replica replicaof $master_host $master_port\n\n            wait_for_condition 50 100 {\n                [string match {*master_link_status:up*} [r info replication]]\n            } else {\n                fail \"Can't turn the instance into a replica\"\n            }\n\n            test {Test master link up hook} {\n                assert_equal [r hooks.event_count masterlink-up] 1\n                assert_equal [r hooks.event_count masterlink-down] 0\n            }\n\n            test {Test role-replica hook} {\n                assert_equal [r hooks.event_count role-replica] 1\n                assert_equal [r hooks.event_count role-master] 0\n                assert_equal [r hooks.event_last role-replica] [s 0 master_host]\n            }\n\n            test {Test replica-online hook} {\n                assert_equal [r -1 hooks.event_count replica-online] 1\n                assert_equal [r -1 hooks.event_count replica-offline] 0\n            }\n\n            test {Test master link down hook} {\n                r client kill type master\n                assert_equal [r hooks.event_count masterlink-down] 1\n            }\n\n            $replica replicaof no one\n\n            test {Test role-master hook} {\n                assert_equal [r hooks.event_count role-replica] 1\n                assert_equal [r hooks.event_count role-master] 1\n                assert_equal [r hooks.event_last role-master] {}\n            }\n\n            test {Test replica-offline hook} {\n                assert_equal [r -1 hooks.event_count replica-online] 1\n                assert_equal [r -1 hooks.event_count replica-offline] 1\n            }\n            # get the replica stdout, to be used by the next test\n            set replica_stdout [srv 0 stdout]\n        }\n\n\n        # look into the log file of the server that just exited\n        test {Test shutdown hook} {\n            assert_equal [string match {*module-event-shutdown*} [exec tail -5 < $replica_stdout]] 1\n        }\n\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/infotest.tcl",
    "content": "set testmodule [file normalize tests/modules/infotest.so]\n\n# Return value for INFO property\nproc field {info property} {\n    if {[regexp \"\\r\\n$property:(.*?)\\r\\n\" $info _ value]} {\n        set _ $value\n    }\n}\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule log-key 0\n\n    test {module reading info} {\n        # check string, integer and float fields\n        assert_equal [r info.gets replication role] \"master\"\n        assert_equal [r info.getc replication role] \"master\"\n        assert_equal [r info.geti stats expired_keys] 0\n        assert_equal [r info.getd stats expired_stale_perc] 0\n\n        # check signed and unsigned\n        assert_equal [r info.geti infotest infotest_global] -2\n        assert_equal [r info.getu infotest infotest_uglobal] -2\n\n        # the above are always 0, try module info that is non-zero\n        assert_equal [r info.geti infotest_italian infotest_due] 2\n        set tre [r info.getd infotest_italian infotest_tre]\n        assert {$tre > 3.2 && $tre < 3.4 }\n\n        # search using the wrong section\n        catch { [r info.gets badname redis_version] } e\n        assert_match {*not found*} $e\n\n        # check that section filter works\n        assert { [string match \"*usec_per_call*\" [r info.gets all cmdstat_info.gets] ] }\n        catch { [r info.gets default cmdstat_info.gets] ] } e\n        assert_match {*not found*} $e\n    }\n\n    test {module info all} {\n        set info [r info all]\n        # info all does not contain modules\n        assert { ![string match \"*Spanish*\" $info] }\n        assert { ![string match \"*infotest_*\" $info] }\n        assert { [string match \"*used_memory*\" $info] }\n    }\n\n    test {module info everything} {\n        set info [r info everything]\n        # info everything contains all default sections, but not ones for crash report\n        assert { [string match \"*infotest_global*\" $info] }\n        assert { [string match \"*Spanish*\" $info] }\n        assert { [string match \"*Italian*\" $info] }\n        assert { [string match \"*used_memory*\" $info] }\n        assert { ![string match \"*Klingon*\" $info] }\n        field $info infotest_dos\n    } {2}\n\n    test {module info modules} {\n        set info [r info modules]\n        # info all does not contain modules\n        assert { [string match \"*Spanish*\" $info] }\n        assert { [string match \"*infotest_global*\" $info] }\n        assert { ![string match \"*used_memory*\" $info] }\n    }\n\n    test {module info one module} {\n        set info [r info INFOTEST]\n        # info all does not contain modules\n        assert { [string match \"*Spanish*\" $info] }\n        assert { ![string match \"*used_memory*\" $info] }\n        field $info infotest_global\n    } {-2}\n\n    test {module info one section} {\n        set info [r info INFOTEST_SPANISH]\n        assert { ![string match \"*used_memory*\" $info] }\n        assert { ![string match \"*Italian*\" $info] }\n        assert { ![string match \"*infotest_global*\" $info] }\n        field $info infotest_uno\n    } {one}\n\n    test {module info dict} {\n        set info [r info infotest_keyspace]\n        set keyspace [field $info infotest_db0]\n        set keys [scan [regexp -inline {keys\\=([\\d]*)} $keyspace] keys=%d]\n    } {3}\n\n    # TODO: test crash report.\n} \n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/misc.tcl",
    "content": "set testmodule [file normalize tests/modules/misc.so]\n\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {test RM_Call} {\n        set info [r test.call_info commandstats]\n        # cmdstat is not in a default section, so we also test an argument was passed\n        assert { [string match \"*cmdstat_module*\" $info] }\n    }\n\n    test {test RM_Call args array} {\n        set info [r test.call_generic info commandstats]\n        # cmdstat is not in a default section, so we also test an argument was passed\n        assert { [string match \"*cmdstat_module*\" $info] }\n    }\n\n    test {test long double conversions} {\n        set ld [r test.ld_conversion]\n        assert {[string match $ld \"0.00000000000000001\"]}\n    }\n\n    test {test module db commands} {\n        r set x foo\n        set key [r test.randomkey]\n        assert_equal $key \"x\"\n        assert_equal [r test.dbsize] 1\n        r test.flushall\n        assert_equal [r test.dbsize] 0\n    }\n\n    test {test modle lru api} {\n        r config set maxmemory-policy allkeys-lru\n        r set x foo\n        set lru [r test.getlru x]\n        assert { $lru <= 1000 }\n        set was_set [r test.setlru x 100000]\n        assert { $was_set == 1 }\n        set idle [r object idletime x]\n        assert { $idle >= 100 }\n        set lru [r test.getlru x]\n        assert { $lru >= 100000 }\n        r config set maxmemory-policy allkeys-lfu\n        set lru [r test.getlru x]\n        assert { $lru == -1 }\n        set was_set [r test.setlru x 100000]\n        assert { $was_set == 0 }\n    }\n    r config set maxmemory-policy allkeys-lru\n\n    test {test modle lfu api} {\n        r config set maxmemory-policy allkeys-lfu\n        r set x foo\n        set lfu [r test.getlfu x]\n        assert { $lfu >= 1 }\n        set was_set [r test.setlfu x 100]\n        assert { $was_set == 1 }\n        set freq [r object freq x]\n        assert { $freq <= 100 }\n        set lfu [r test.getlfu x]\n        assert { $lfu <= 100 }\n        r config set maxmemory-policy allkeys-lru\n        set lfu [r test.getlfu x]\n        assert { $lfu == -1 }\n        set was_set [r test.setlfu x 100]\n        assert { $was_set == 0 }\n    }\n\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/propagate.tcl",
    "content": "set testmodule [file normalize tests/modules/propagate.so]\n\ntags \"modules\" {\n    test {Modules can propagate in async and threaded contexts} {\n        start_server {} {\n            set replica [srv 0 client]\n            set replica_host [srv 0 host]\n            set replica_port [srv 0 port]\n            start_server [list overrides [list loadmodule \"$testmodule\"]] {\n                set master [srv 0 client]\n                set master_host [srv 0 host]\n                set master_port [srv 0 port]\n\n                # Start the replication process...\n                $replica replicaof $master_host $master_port\n                wait_for_sync $replica\n\n                after 1000\n                $master propagate-test\n\n                wait_for_condition 5000 10 {\n                    ([$replica get timer] eq \"10\") && \\\n                    ([$replica get a-from-thread] eq \"10\")\n                } else {\n                    fail \"The two counters don't match the expected value.\"\n                }\n\n                $master propagate-test-2\n                $master propagate-test-3\n                $master multi\n                $master propagate-test-2\n                $master propagate-test-3\n                $master exec\n                wait_for_ofs_sync $master $replica\n\n                assert_equal [s -1 unexpected_error_replies] 0\n            }\n        }\n    }\n}\n\ntags \"modules aof\" {\n    test {Modules RM_Replicate replicates MULTI/EXEC correctly} {\n        start_server [list overrides [list loadmodule \"$testmodule\"]] {\n            # Enable the AOF\n            r config set appendonly yes\n            r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.\n            waitForBgrewriteaof r\n\n            r propagate-test-2\n            r propagate-test-3\n            r multi\n            r propagate-test-2\n            r propagate-test-3\n            r exec\n\n            # Load the AOF\n            r debug loadaof\n\n            assert_equal [s 0 unexpected_error_replies] 0\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/scan.tcl",
    "content": "set testmodule [file normalize tests/modules/scan.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {Module scan keyspace} {\n        # the module create a scan command with filtering which also return values\n        r set x 1\n        r set y 2\n        r set z 3\n        r hset h f v\n        lsort [r scan.scan_strings]\n    } {{x 1} {y 2} {z 3}}\n\n    test {Module scan hash ziplist} {\n        r hmset hh f1 v1 f2 v2\n        lsort [r scan.scan_key hh]\n    } {{f1 v1} {f2 v2}}\n\n    test {Module scan hash dict} {\n        r config set hash-max-ziplist-entries 2\n        r hmset hh f3 v3\n        lsort [r scan.scan_key hh]\n    } {{f1 v1} {f2 v2} {f3 v3}}\n\n    test {Module scan zset ziplist} {\n        r zadd zz 1 f1 2 f2\n        lsort [r scan.scan_key zz]\n    } {{f1 1} {f2 2}}\n\n    test {Module scan zset dict} {\n        r config set zset-max-ziplist-entries 2\n        r zadd zz 3 f3\n        lsort [r scan.scan_key zz]\n    } {{f1 1} {f2 2} {f3 3}}\n\n    test {Module scan set intset} {\n        r sadd ss 1 2\n        lsort [r scan.scan_key ss]\n    } {{1 {}} {2 {}}}\n\n    test {Module scan set dict} {\n        r config set set-max-intset-entries 2\n        r sadd ss 3\n        lsort [r scan.scan_key ss]\n    } {{1 {}} {2 {}} {3 {}}}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/moduleapi/testrdb.tcl",
    "content": "set testmodule [file normalize tests/modules/testrdb.so]\n\ntags \"modules\" {\n    test {modules are able to persist types} {\n        start_server [list overrides [list loadmodule \"$testmodule\"]] {\n            r testrdb.set.key key1 value1\n            assert_equal \"value1\" [r testrdb.get.key key1]\n            r debug reload\n            assert_equal \"value1\" [r testrdb.get.key key1]\n        }\n    }\n\n    test {modules global are lost without aux} {\n        set server_path [tmpdir \"server.module-testrdb\"]\n        start_server [list overrides [list loadmodule \"$testmodule\" \"dir\" $server_path]] {\n            r testrdb.set.before global1\n            assert_equal \"global1\" [r testrdb.get.before]\n        }\n        start_server [list overrides [list loadmodule \"$testmodule\" \"dir\" $server_path]] {\n            assert_equal \"\" [r testrdb.get.before]\n        }\n    }\n\n    test {modules are able to persist globals before and after} {\n        set server_path [tmpdir \"server.module-testrdb\"]\n        start_server [list overrides [list loadmodule \"$testmodule 2\" \"dir\" $server_path]] {\n            r testrdb.set.before global1\n            r testrdb.set.after global2\n            assert_equal \"global1\" [r testrdb.get.before]\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n        start_server [list overrides [list loadmodule \"$testmodule 2\" \"dir\" $server_path]] {\n            assert_equal \"global1\" [r testrdb.get.before]\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n\n    }\n\n    test {modules are able to persist globals just after} {\n        set server_path [tmpdir \"server.module-testrdb\"]\n        start_server [list overrides [list loadmodule \"$testmodule 1\" \"dir\" $server_path]] {\n            r testrdb.set.after global2\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n        start_server [list overrides [list loadmodule \"$testmodule 1\" \"dir\" $server_path]] {\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n    }\n\n    tags {repl} {\n        test {diskless loading short read with module} {\n            start_server [list overrides [list loadmodule \"$testmodule\"]] {\n                set replica [srv 0 client]\n                set replica_host [srv 0 host]\n                set replica_port [srv 0 port]\n                start_server [list overrides [list loadmodule \"$testmodule\"]] {\n                    set master [srv 0 client]\n                    set master_host [srv 0 host]\n                    set master_port [srv 0 port]\n\n                    # Set master and replica to use diskless replication\n                    $master config set repl-diskless-sync yes\n                    $master config set rdbcompression no\n                    $replica config set repl-diskless-load swapdb\n                    for {set k 0} {$k < 30} {incr k} {\n                        r testrdb.set.key key$k [string repeat A [expr {int(rand()*1000000)}]]\n                    }\n\n                    # Start the replication process...\n                    $master config set repl-diskless-sync-delay 0\n                    $replica replicaof $master_host $master_port\n\n                    # kill the replication at various points\n                    set attempts 3\n                    if {$::accurate} { set attempts 10 }\n                    for {set i 0} {$i < $attempts} {incr i} {\n                        # wait for the replica to start reading the rdb\n                        # using the log file since the replica only responds to INFO once in 2mb\n                        wait_for_log_message -1 \"*Loading DB in memory*\" 5 2000 1\n\n                        # add some additional random sleep so that we kill the master on a different place each time\n                        after [expr {int(rand()*100)}]\n\n                        # kill the replica connection on the master\n                        set killed [$master client kill type replica]\n\n                        if {[catch {\n                            set res [wait_for_log_message -1 \"*Internal error in RDB*\" 5 100 10]\n                            if {$::verbose} {\n                                puts $res\n                            }\n                        }]} {\n                            puts \"failed triggering short read\"\n                            # force the replica to try another full sync\n                            $master client kill type replica\n                            $master set asdf asdf\n                            # the side effect of resizing the backlog is that it is flushed (16k is the min size)\n                            $master config set repl-backlog-size [expr {16384 + $i}]\n                        }\n                        # wait for loading to stop (fail)\n                        wait_for_condition 100 10 {\n                            [s -1 loading] eq 0\n                        } else {\n                            fail \"Replica didn't disconnect\"\n                        }\n                    }\n                    # enable fast shutdown\n                    $master config set rdb-key-save-delay 0\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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    test {DISCARD should not fail during OOM} {\n        set rd [redis_deferring_client]\n        $rd config set maxmemory 1\n        assert  {[$rd read] eq {OK}}\n        r multi\n        catch {r set x 1} e\n        assert_match {OOM*} $e\n        r discard\n        $rd config set maxmemory 0\n        assert  {[$rd read] eq {OK}}\n        $rd close\n        r ping\n    } {PONG}\n\n    test {MULTI and script timeout} {\n        # check that if MULTI arrives during timeout, it is either refused, or\n        # allowed to pass, and we don't end up executing half of the transaction\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r config set lua-time-limit 10\n        r set xx 1\n        $rd1 eval {while true do end} 0\n        after 200\n        catch { $rd2 multi; $rd2 read } e\n        catch { $rd2 incr xx; $rd2 read } e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        catch { $rd2 incr xx; $rd2 read } e\n        catch { $rd2 exec; $rd2 read } e\n        set xx [r get xx]\n        # make sure that either the whole transcation passed or none of it (we actually expect none)\n        assert { $xx == 1 || $xx == 3}\n        # check that the connection is no longer in multi state\n        $rd2 ping asdf\n        set pong [$rd2 read]\n        assert_equal $pong \"asdf\"\n    }\n\n    test {EXEC and script timeout} {\n        # check that if EXEC arrives during timeout, we don't end up executing\n        # half of the transaction, and also that we exit the multi state\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r config set lua-time-limit 10\n        r set xx 1\n        catch { $rd2 multi; $rd2 read } e\n        catch { $rd2 incr xx; $rd2 read } e\n        $rd1 eval {while true do end} 0\n        after 200\n        catch { $rd2 incr xx; $rd2 read } e\n        catch { $rd2 exec; $rd2 read } e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        set xx [r get xx]\n        # make sure that either the whole transcation passed or none of it (we actually expect none)\n        assert { $xx == 1 || $xx == 3}\n        # check that the connection is no longer in multi state\n        $rd2 ping asdf\n        set pong [$rd2 read]\n        assert_equal $pong \"asdf\"\n    }\n\n    test {MULTI-EXEC body and script timeout} {\n        # check that we don't run an imcomplete transaction due to some commands\n        # arriving during busy script\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r config set lua-time-limit 10\n        r set xx 1\n        catch { $rd2 multi; $rd2 read } e\n        catch { $rd2 incr xx; $rd2 read } e\n        $rd1 eval {while true do end} 0\n        after 200\n        catch { $rd2 incr xx; $rd2 read } e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        catch { $rd2 exec; $rd2 read } e\n        set xx [r get xx]\n        # make sure that either the whole transcation passed or none of it (we actually expect none)\n        assert { $xx == 1 || $xx == 3}\n        # check that the connection is no longer in multi state\n        $rd2 ping asdf\n        set pong [$rd2 read]\n        assert_equal $pong \"asdf\"\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 >= 70000 && $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": "Chapter02/redis-6.0.3/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    } {*index is out of range*}\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 config set aof-use-rdb-preamble no\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 config set aof-use-rdb-preamble no\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            if {$::tls} {\n                set fd2 [::tls::socket $::host $::port]\n            } else {\n                set fd2 [socket $::host $::port]\n            }\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        r del foo\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": "Chapter02/redis-6.0.3/tests/unit/pendingquerybuf.tcl",
    "content": "proc info_memory {r property} {\n    if {[regexp \"\\r\\n$property:(.*?)\\r\\n\" [{*}$r info memory] _ value]} {\n        set _ $value\n    }\n}\n\nproc prepare_value {size} {\n    set _v \"c\" \n    for {set i 1} {$i < $size} {incr i} {\n        append _v 0   \n    }\n    return $_v\n}\n\nstart_server {tags {\"wait\"}} {\nstart_server {} {\n    set slave [srv 0 client]\n    set slave_host [srv 0 host]\n    set slave_port [srv 0 port]\n    set master [srv -1 client]\n    set master_host [srv -1 host]\n    set master_port [srv -1 port]\n\n    test \"pending querybuf: check size of pending_querybuf after set a big value\" {\n        $slave slaveof $master_host $master_port\n        set _v [prepare_value [expr 32*1024*1024]]\n        $master set key $_v \n        after 2000\n        set m_usedmemory [info_memory $master used_memory]\n        set s_usedmemory [info_memory $slave used_memory]\n        if { $s_usedmemory > $m_usedmemory + 10*1024*1024 } {\n            fail \"the used_memory of replica is much larger than master. Master:$m_usedmemory Replica:$s_usedmemory\"\n        }\n    }  \n}}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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            if {$::tls} {\n                set s [::tls::socket [srv 0 host] [srv 0 port]]\n            } else {\n                set s [socket [srv 0 host] [srv 0 port]]\n            }\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": "Chapter02/redis-6.0.3/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 \"Pub/Sub PING\" {\n        set rd1 [redis_deferring_client]\n        subscribe $rd1 somechannel\n        # While subscribed to non-zero channels PING works in Pub/Sub mode.\n        $rd1 ping\n        $rd1 ping \"foo\"\n        set reply1 [$rd1 read]\n        set reply2 [$rd1 read]\n        unsubscribe $rd1 somechannel\n        # Now we are unsubscribed, PING should just return PONG.\n        $rd1 ping\n        set reply3 [$rd1 read]\n        $rd1 close\n        list $reply1 $reply2 $reply3\n    } {{pong {}} {pong foo} PONG}\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 \"NUMSUB returns numbers, not strings (#1561)\" {\n        r pubsub numsub abc def\n    } {abc 0 def 0}\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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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    test \"SCAN TYPE\" {\n        r flushdb\n        # populate only creates strings\n        r debug populate 1000\n\n        # Check non-strings are excluded\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur type \"list\"]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        assert_equal 0 [llength $keys]\n\n        # Check strings are included\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur type \"string\"]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        assert_equal 1000 [llength $keys]\n\n        # Check all three args work together\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur type \"string\" match \"key:*\" count 10]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        assert_equal 1000 [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    test \"ZSCAN scores: regression test for issue #2175\" {\n        r del mykey\n        for {set j 0} {$j < 500} {incr j} {\n            r zadd mykey 9.8813129168249309e-323 $j\n        }\n        set res [lindex [r zscan mykey 0] 1]\n        set first_score [lindex $res 1]\n        assert {$first_score != 0}\n    }\n\n    test \"SCAN regression test for issue #4906\" {\n        for {set k 0} {$k < 100} {incr k} {\n            r del set\n            r sadd set x; # Make sure it's not intset encoded\n            set toremove {}\n            unset -nocomplain found\n            array set found {}\n\n            # Populate the set\n            set numele [expr {101+[randomInt 1000]}]\n            for {set j 0} {$j < $numele} {incr j} {\n                r sadd set $j\n                if {$j >= 100} {\n                    lappend toremove $j\n                }\n            }\n\n            # Start scanning\n            set cursor 0\n            set iteration 0\n            set del_iteration [randomInt 10]\n            while {!($cursor == 0 && $iteration != 0)} {\n                lassign [r sscan set $cursor] cursor items\n\n                # Mark found items. We expect to find from 0 to 99 at the end\n                # since those elements will never be removed during the scanning.\n                foreach i $items {\n                    set found($i) 1\n                }\n                incr iteration\n                # At some point remove most of the items to trigger the\n                # rehashing to a smaller hash table.\n                if {$iteration == $del_iteration} {\n                    r srem set {*}$toremove\n                }\n            }\n\n            # Verify that SSCAN reported everything from 0 to 99\n            for {set j 0} {$j < 100} {incr j} {\n                if {![info exists found($j)]} {\n                    fail \"SSCAN element missing $j\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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',KEYS[1])} 1 mykey\n    } {myval}\n\n    test {EVALSHA - Can we call a SHA1 if already defined?} {\n        r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey\n    } {myval}\n\n    test {EVALSHA - Can we call a SHA1 in uppercase?} {\n        r evalsha FD758D1589D044DD850A6F05D52F2EEFD27F033F 1 mykey\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 set x 0\n        r eval {\n            local foo = redis.pcall('incr',KEYS[1])\n            return {type(foo),foo}\n        } 1 x\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',KEYS[1])\n            return {type(foo),foo}\n        } 1 mykey\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',KEYS[1],0,-1)\n            return {type(foo),foo[1],foo[2],foo[3],# foo}\n        } 1 mylist\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',KEYS[1],'myval')\n            return {type(foo),foo['ok']}\n        } 1 mykey\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',KEYS[1])\n            return {type(foo),foo['err']}\n        } 1 mykey\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',KEYS[1])\n            return {type(foo),foo == false}\n        } 1 mykey\n    } {boolean 1}\n\n    test {EVAL - Is the Lua client using 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',KEYS[1])} 1 mykey\n    } {this is DB 10}\n\n    test {EVAL - SELECT inside Lua should not affect the caller} {\n        # here we DB 10 is selected\n        r set mykey \"original value\"\n        r eval {return redis.pcall('select','9')} 0\n        set res [r get mykey]\n        r select 9\n        set res\n    } {original value}\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('blpop','x',0)} 0} e\n        set e\n    } {*not allowed*}\n\n    test {EVAL - Scripts can't run XREAD and XREADGROUP with BLOCK option} {\n        r del s\n        r xgroup create s g $ MKSTREAM\n        set res [r eval {return redis.pcall('xread','STREAMS','s','$')} 1 s]\n        assert {$res eq {}}\n        assert_error \"*xread command is not allowed with BLOCK option from scripts\" {r eval {return redis.pcall('xread','BLOCK',0,'STREAMS','s','$')} 1 s}\n        set res [r eval {return redis.pcall('xreadgroup','group','g','c','STREAMS','s','>')} 1 s]\n        assert {$res eq {}}\n        assert_error \"*xreadgroup command is not allowed with BLOCK option from scripts\" {r eval {return redis.pcall('xreadgroup','group','g','c','BLOCK',0,'STREAMS','s','>')} 1 s}\n    }\n\n    test {EVAL - Scripts can't run certain commands} {\n        set e {}\n        r debug lua-always-replicate-commands 0\n        catch {\n            r eval \"redis.pcall('randomkey'); return redis.pcall('set','x','ciao')\" 0\n        } e\n        r debug lua-always-replicate-commands 1\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',KEYS[1],'val')} 1 foo\n        } e\n        set e\n    } {*against a key*}\n\n    test {EVAL - JSON numeric decoding} {\n        # We must return the table as a string because otherwise\n        # Redis converts floats to ints and we get 0 and 1023 instead\n        # of 0.0003 and 1023.2 as the parsed output.\n        r eval {return\n                 table.concat(\n                   cjson.decode(\n                    \"[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]\"), \" \")\n        } 0\n    } {0 -5000 -1 0.0003 1023.2 0}\n\n    test {EVAL - JSON string decoding} {\n        r eval {local decoded = cjson.decode('{\"keya\": \"a\", \"keyb\": \"b\"}')\n                return {decoded.keya, decoded.keyb}\n        } 0\n    } {a b}\n\n    test {EVAL - cmsgpack can pack double?} {\n        r eval {local encoded = cmsgpack.pack(0.1)\n                local h = \"\"\n                for i = 1, #encoded do\n                    h = h .. string.format(\"%02x\",string.byte(encoded,i))\n                end\n                return h\n        } 0\n    } {cb3fb999999999999a}\n\n    test {EVAL - cmsgpack can pack negative int64?} {\n        r eval {local encoded = cmsgpack.pack(-1099511627776)\n                local h = \"\"\n                for i = 1, #encoded do\n                    h = h .. string.format(\"%02x\",string.byte(encoded,i))\n                end\n                return h\n        } 0\n    } {d3ffffff0000000000}\n\n    test {EVAL - cmsgpack can pack and unpack circular references?} {\n        r eval {local a = {x=nil,y=5}\n                local b = {x=a}\n                a['x'] = b\n                local encoded = cmsgpack.pack(a)\n                local h = \"\"\n                -- cmsgpack encodes to a depth of 16, but can't encode\n                -- references, so the encoded object has a deep copy recusive\n                -- depth of 16.\n                for i = 1, #encoded do\n                    h = h .. string.format(\"%02x\",string.byte(encoded,i))\n                end\n                -- when unpacked, re.x.x != re because the unpack creates\n                -- individual tables down to a depth of 16.\n                -- (that's why the encoded output is so large)\n                local re = cmsgpack.unpack(encoded)\n                assert(re)\n                assert(re.x)\n                assert(re.x.x.y == re.y)\n                assert(re.x.x.x.x.y == re.y)\n                assert(re.x.x.x.x.x.x.y == re.y)\n                assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y)\n                -- maximum working depth:\n                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y)\n                -- now the last x would be b above and has no y\n                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x)\n                -- so, the final x.x is at the depth limit and was assigned nil\n                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil)\n                return {h, re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5}\n        } 0\n    } {82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0 1 1}\n\n    test {EVAL - Numerical sanity check from bitop} {\n        r eval {assert(0x7fffffff == 2147483647, \"broken hex literals\");\n                assert(0xffffffff == -1 or 0xffffffff == 2^32-1,\n                    \"broken hex literals\");\n                assert(tostring(-1) == \"-1\", \"broken tostring()\");\n                assert(tostring(0xffffffff) == \"-1\" or\n                    tostring(0xffffffff) == \"4294967295\",\n                    \"broken tostring()\")\n        } 0\n    } {}\n\n    test {EVAL - Verify minimal bitop functionality} {\n        r eval {assert(bit.tobit(1) == 1);\n                assert(bit.band(1) == 1);\n                assert(bit.bxor(1,2) == 3);\n                assert(bit.bor(1,2,4,8,16,32,64,128) == 255)\n        } 0\n    } {}\n\n    test {EVAL - Able to parse trailing comments} {\n        r eval {return 'hello' --trailing comment} 0\n    } {hello}\n\n    test {SCRIPTING FLUSH - is able to clear the scripts cache?} {\n        r set mykey myval\n        set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]\n        assert_equal $v myval\n        set e \"\"\n        r script flush\n        catch {r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey} 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 debug lua-always-replicate-commands 0\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        set res [r eval {return redis.call('smembers',KEYS[1])} 1 myset]\n        r debug lua-always-replicate-commands 1\n        set res\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',KEYS[1],'desc')} 1 myset\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',KEYS[1],'by','_')} 1 myset\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',KEYS[1],'by','_','get','#','get','_:*')} 1 myset\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 * 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 config set aof-use-rdb-preamble no\n        r eval {redis.call(\"set\",KEYS[1],\"100\")} 1 foo\n        r eval {redis.call(\"incr\",KEYS[1])} 1 foo\n        r eval {redis.call(\"incr\",KEYS[1])} 1 foo\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 1] 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    test {Call Redis command with many args from Lua (issue #1764)} {\n        r eval {\n            local i\n            local x={}\n            redis.call('del','mylist')\n            for i=1,100 do\n                table.insert(x,i)\n            end\n            redis.call('rpush','mylist',unpack(x))\n            return redis.call('lrange','mylist',0,-1)\n        } 0\n    } {1 2 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100}\n\n    test {Number conversion precision test (issue #1118)} {\n        r eval {\n              local value = 9007199254740991\n              redis.call(\"set\",\"foo\",value)\n              return redis.call(\"get\",\"foo\")\n        } 0\n    } {9007199254740991}\n\n    test {String containing number precision test (regression of issue #1118)} {\n        r eval {\n            redis.call(\"set\", \"key\", \"12039611435714932082\")\n            return redis.call(\"get\", \"key\")\n        } 0\n    } {12039611435714932082}\n\n    test {Verify negative arg count is error instead of crash (issue #1842)} {\n        catch { r eval { return \"hello\" } -12 } e\n        set e\n    } {ERR Number of keys can't be negative}\n\n    test {Correct handling of reused argv (issue #1939)} {\n        r eval {\n              for i = 0, 10 do\n                  redis.call('SET', 'a', '1')\n                  redis.call('MGET', 'a', 'b', 'c')\n                  redis.call('EXPIRE', 'a', 0)\n                  redis.call('GET', 'a')\n                  redis.call('MGET', 'a', 'b', 'c')\n              end\n        } 0\n    }\n\n    test {Functions in the Redis namespace are able to report errors} {\n        catch {\n            r eval {\n                  redis.sha1hex()\n            } 0\n        } e\n        set e\n    } {*wrong number*}\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',KEYS[1],'y'); while true do end} 1 x\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 could 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\nforeach cmdrepl {0 1} {\n    start_server {tags {\"scripting repl\"}} {\n        start_server {} {\n            if {$cmdrepl == 1} {\n                set rt \"(commands replication)\"\n            } else {\n                set rt \"(scripts replication)\"\n                r debug lua-always-replicate-commands 1\n            }\n\n            test \"Before the replica connects we issue two EVAL commands $rt\" {\n                # One with an error, but still executing a command.\n                # SHA is: 67164fc43fa971f76fd1aaeeaf60c1c178d25876\n                catch {\n                    r eval {redis.call('incr',KEYS[1]); redis.call('nonexisting')} 1 x\n                }\n                # One command is correct:\n                # SHA is: 6f5ade10a69975e903c6d07b10ea44c6382381a5\n                r eval {return redis.call('incr',KEYS[1])} 1 x\n            } {2}\n\n            test \"Connect a replica to the master instance $rt\" {\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 replica\"\n                }\n            }\n\n            test \"Now use EVALSHA against the master, with both SHAs $rt\" {\n                # The server should replicate successful and unsuccessful\n                # commands as EVAL instead of EVALSHA.\n                catch {\n                    r evalsha 67164fc43fa971f76fd1aaeeaf60c1c178d25876 1 x\n                }\n                r evalsha 6f5ade10a69975e903c6d07b10ea44c6382381a5 1 x\n            } {4}\n\n            test \"If EVALSHA was replicated as EVAL, 'x' should be '4' $rt\" {\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 $rt\" {\n                set rd [redis_deferring_client]\n                $rd brpop a 0\n                r eval {\n                    redis.call(\"lpush\",KEYS[1],\"1\");\n                    redis.call(\"lpush\",KEYS[1],\"2\");\n                } 1 a\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 replica 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 $rt\" {\n                r del x\n                r eval {if tonumber(ARGV[1]) > 0 then redis.call('incr', KEYS[1]) end} 1 x 0\n                r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 0\n                r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 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            test \"Lua scripts using SELECT are replicated correctly $rt\" {\n                r eval {\n                    redis.call(\"set\",\"foo1\",\"bar1\")\n                    redis.call(\"select\",\"10\")\n                    redis.call(\"incr\",\"x\")\n                    redis.call(\"select\",\"11\")\n                    redis.call(\"incr\",\"z\")\n                } 0\n                r eval {\n                    redis.call(\"set\",\"foo1\",\"bar1\")\n                    redis.call(\"select\",\"10\")\n                    redis.call(\"incr\",\"x\")\n                    redis.call(\"select\",\"11\")\n                    redis.call(\"incr\",\"z\")\n                } 0\n                wait_for_condition 50 100 {\n                    [r -1 debug digest] eq [r debug digest]\n                } else {\n                    fail \"Master-Replica desync after Lua script using SELECT.\"\n                }\n            }\n        }\n    }\n}\n\nstart_server {tags {\"scripting repl\"}} {\n    start_server {overrides {appendonly yes aof-use-rdb-preamble no}} {\n        test \"Connect a replica to the master 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 replica\"\n            }\n        }\n\n        test \"Redis.replicate_commands() must be issued before any write\" {\n            r eval {\n                redis.call('set','foo','bar');\n                return redis.replicate_commands();\n            } 0\n        } {}\n\n        test \"Redis.replicate_commands() must be issued before any write (2)\" {\n            r eval {\n                return redis.replicate_commands();\n            } 0\n        } {1}\n\n        test \"Redis.set_repl() must be issued after replicate_commands()\" {\n            r debug lua-always-replicate-commands 0\n            catch {\n                r eval {\n                    redis.set_repl(redis.REPL_ALL);\n                } 0\n            } e\n            r debug lua-always-replicate-commands 1\n            set e\n        } {*only after turning on*}\n\n        test \"Redis.set_repl() don't accept invalid values\" {\n            catch {\n                r eval {\n                    redis.replicate_commands();\n                    redis.set_repl(12345);\n                } 0\n            } e\n            set e\n        } {*Invalid*flags*}\n\n        test \"Test selective replication of certain Redis commands from Lua\" {\n            r del a b c d\n            r eval {\n                redis.replicate_commands();\n                redis.call('set','a','1');\n                redis.set_repl(redis.REPL_NONE);\n                redis.call('set','b','2');\n                redis.set_repl(redis.REPL_AOF);\n                redis.call('set','c','3');\n                redis.set_repl(redis.REPL_ALL);\n                redis.call('set','d','4');\n            } 0\n\n            wait_for_condition 50 100 {\n                [r -1 mget a b c d] eq {1 {} {} 4}\n            } else {\n                fail \"Only a and c should be replicated to replica\"\n            }\n\n            # Master should have everything right now\n            assert {[r mget a b c d] eq {1 2 3 4}}\n\n            # After an AOF reload only a, c and d should exist\n            r debug loadaof\n\n            assert {[r mget a b c d] eq {1 {} 3 4}}\n        }\n\n        test \"PRNG is seeded randomly for command replication\" {\n            set a [\n                r eval {\n                    redis.replicate_commands();\n                    return math.random()*100000;\n                } 0\n            ]\n            set b [\n                r eval {\n                    redis.replicate_commands();\n                    return math.random()*100000;\n                } 0\n            ]\n            assert {$a ne $b}\n        }\n\n        test \"Using side effects is not a problem with command replication\" {\n            r eval {\n                redis.replicate_commands();\n                redis.call('set','time',redis.call('time')[1])\n            } 0\n\n            assert {[r get time] ne {}}\n\n            wait_for_condition 50 100 {\n                [r get time] eq [r -1 get time]\n            } else {\n                fail \"Time key does not match between master and replica\"\n            }\n        }\n    }\n}\n\nstart_server {tags {\"scripting\"}} {\n    r script debug sync\n    r eval {return 'hello'} 0\n    r eval {return 'hello'} 0\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 client setname foobar\n        r debug sleep 0.2\n        set e [lindex [r slowlog get] 0]\n        assert_equal [llength $e] 6\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        assert_equal {foobar} [lindex $e 5]\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    test {SLOWLOG - can clean older entires} {\n        r client setname lastentry_client\n        r config set slowlog-max-len 1\n        r debug sleep 0.2\n        assert {[llength [r slowlog get]] == 1}\n        set e [lindex [r slowlog get] 0]\n        assert_equal {lastentry_client} [lindex $e 5]\n    }\n\n    test {SLOWLOG - can be disabled} {\n        r config set slowlog-max-len 1\n        r config set slowlog-log-slower-than 1\n        r slowlog reset\n        r debug sleep 0.2\n        assert_equal [r slowlog len] 1\n        r config set slowlog-log-slower-than -1\n        r slowlog reset\n        r debug sleep 0.2\n        assert_equal [r slowlog len] 0\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/sort.tcl",
    "content": "start_server {\n    tags {\"sort\"}\n    overrides {\n        \"list-max-ziplist-size\" 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 quicklist \"Old Ziplist\"\n        1000 lpush quicklist \"Old Linked list\"\n        10000 lpush quicklist \"Old 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 quicklist 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 quicklist sort-res\n    }\n\n    test \"SORT extracts STORE correctly\" {\n        r command getkeys sort abc store def\n    } {abc def}\n\n    test \"SORT extracts multiple STORE correctly\" {\n        r command getkeys sort abc store invalid store stillbad store def\n    } {abc def}\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',KEYS[1],'by','nosort','asc'),\n                    redis.call('sort',KEYS[1],'by','nosort','desc')}\n        } 1 zset\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 issue 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    test \"SORT by nosort retains native order for lists\" {\n        r del testa\n        r lpush testa 2 1 4 3 5\n        r sort testa by nosort\n    } {5 3 4 1 2}\n\n    test \"SORT by nosort plus store retains native order for lists\" {\n        r del testa\n        r lpush testa 2 1 4 3 5\n        r sort testa by nosort store testb\n        r lrange testb 0 -1\n    } {5 3 4 1 2}\n\n    test \"SORT by nosort with limit returns based on original list order\" {\n        r sort testa by nosort limit 0 3 store testb\n        r lrange testb 0 -1\n    } {5 3 4}\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": "Chapter02/redis-6.0.3/tests/unit/tls.tcl",
    "content": "start_server {tags {\"tls\"}} {\n    if {$::tls} {\n        package require tls\n\n        test {TLS: Not accepting non-TLS connections on a TLS port} {\n            set s [redis [srv 0 host] [srv 0 port]]\n            catch {$s PING} e\n            set e\n        } {*I/O error*}\n\n        test {TLS: Verify tls-auth-clients behaves as expected} {\n            set s [redis [srv 0 host] [srv 0 port]]\n            ::tls::import [$s channel]\n            catch {$s PING} e\n            assert_match {*error*} $e\n\n            r CONFIG SET tls-auth-clients no\n\n            set s [redis [srv 0 host] [srv 0 port]]\n            ::tls::import [$s channel]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-auth-clients yes\n        }\n\n        test {TLS: Verify tls-protocols behaves as expected} {\n            r CONFIG SET tls-protocols TLSv1.2\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.2 0}]\n            catch {$s PING} e\n            assert_match {*I/O error*} $e\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.2 1}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-protocols \"\"\n        }\n\n        test {TLS: Verify tls-ciphers behaves as expected} {\n            r CONFIG SET tls-protocols TLSv1.2\n            r CONFIG SET tls-ciphers \"DEFAULT:-AES128-SHA256\"\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"-ALL:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {*I/O error*} $e\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"-ALL:AES256-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-ciphers \"DEFAULT\"\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"-ALL:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-protocols \"\"\n            r CONFIG SET tls-ciphers \"DEFAULT\"\n        }\n\n        test {TLS: Verify tls-prefer-server-ciphers behaves as expected} {\n            r CONFIG SET tls-protocols TLSv1.2\n            r CONFIG SET tls-ciphers \"AES128-SHA256:AES256-SHA256\"\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"AES256-SHA256:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            assert_equal \"AES256-SHA256\" [dict get [::tls::status [$s channel]] cipher]\n\n            r CONFIG SET tls-prefer-server-ciphers yes\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"AES256-SHA256:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            assert_equal \"AES128-SHA256\" [dict get [::tls::status [$s channel]] cipher]\n\n            r CONFIG SET tls-protocols \"\"\n            r CONFIG SET tls-ciphers \"DEFAULT\"\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/tracking.tcl",
    "content": "start_server {tags {\"tracking\"}} {\n    # Create a deferred client we'll use to redirect invalidation\n    # messages to.\n    set rd1 [redis_deferring_client]\n    $rd1 client id\n    set redir [$rd1 read]\n    $rd1 subscribe __redis__:invalidate\n    $rd1 read ; # Consume the SUBSCRIBE reply.\n\n    # Create another client as well in order to test NOLOOP\n    set rd2 [redis_deferring_client]\n\n    test {Clients are able to enable tracking and redirect it} {\n        r CLIENT TRACKING on REDIRECT $redir\n    } {*OK}\n\n    test {The other connection is able to get invalidations} {\n        r SET a 1\n        r GET a\n        r INCR a\n        r INCR b ; # This key should not be notified, since it wasn't fetched.\n        set keys [lindex [$rd1 read] 2]\n        assert {[llength $keys] == 1}\n        assert {[lindex $keys 0] eq {a}}\n    }\n\n    test {The client is now able to disable tracking} {\n        # Make sure to add a few more keys in the tracking list\n        # so that we can check for leaks, as a side effect.\n        r MGET a b c d e f g\n        r CLIENT TRACKING off\n    }\n\n    test {Clients can enable the BCAST mode with the empty prefix} {\n        r CLIENT TRACKING on BCAST REDIRECT $redir\n    } {*OK*}\n\n    test {The connection gets invalidation messages about all the keys} {\n        r MSET a 1 b 2 c 3\n        set keys [lsort [lindex [$rd1 read] 2]]\n        assert {$keys eq {a b c}}\n    }\n\n    test {Clients can enable the BCAST mode with prefixes} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on BCAST REDIRECT $redir PREFIX a: PREFIX b:\n        r MULTI\n        r INCR a:1\n        r INCR a:2\n        r INCR b:1\n        r INCR b:2\n        r EXEC\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys2 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1 {*}$keys2]]\n        assert {$keys eq {a:1 a:2 b:1 b:2}}\n    }\n    \n    test {Adding prefixes to BCAST mode works} {\n        r CLIENT TRACKING on BCAST REDIRECT $redir PREFIX c:\n        r INCR c:1234\n        set keys [lsort [lindex [$rd1 read] 2]]\n        assert {$keys eq {c:1234}}\n    }\n\n    test {Tracking NOLOOP mode in standard mode works} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on REDIRECT $redir NOLOOP\n        r MGET otherkey1 loopkey otherkey2\n        $rd2 SET otherkey1 1; # We should get this\n        r SET loopkey 1 ; # We should not get this\n        $rd2 SET otherkey2 1; # We should get this\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys2 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1 {*}$keys2]]\n        assert {$keys eq {otherkey1 otherkey2}}\n    }\n\n    test {Tracking NOLOOP mode in BCAST mode works} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on BCAST REDIRECT $redir NOLOOP\n        $rd2 SET otherkey1 1; # We should get this\n        r SET loopkey 1 ; # We should not get this\n        $rd2 SET otherkey2 1; # We should get this\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys2 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1 {*}$keys2]]\n        assert {$keys eq {otherkey1 otherkey2}}\n    }\n\n    test {Tracking gets notification of expired keys} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on BCAST REDIRECT $redir NOLOOP\n        r SET mykey myval px 1\n        r SET mykeyotherkey myval ; # We should not get it\n        after 1000\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1]]\n        assert {$keys eq {mykey}}\n    }\n\n    test {Tracking gets notification on tracking table key eviction} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on REDIRECT $redir NOLOOP\n        r MSET key1 1 key2 2\n        # Let the server track the two keys for us\n        r MGET key1 key2\n        # Force the eviction of all the keys but one:\n        r config set tracking-table-max-keys 1\n        # Note that we may have other keys in the table for this client,\n        # since we disabled/enabled tracking multiple time with the same\n        # ID, and tracking does not do ID cleanups for performance reasons.\n        # So we check that eventually we'll receive one or the other key,\n        # otherwise the test will die for timeout.\n        while 1 {\n            set keys [lindex [$rd1 read] 2]\n            if {$keys eq {key1} || $keys eq {key2}} break\n        }\n        # We should receive an expire notification for one of\n        # the two keys (only one must remain)\n        assert {$keys eq {key1} || $keys eq {key2}}\n    }\n\n    $rd1 close\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 __avoid_collisions__[randstring 0 8 alpha]\n            set val __avoid_collisions__[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 __avoid_collisions__[randstring 0 8 alpha]\n            set val __avoid_collisions__[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 an hash table?} {\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 {HINCRBYFLOAT fails against hash value that contains a null-terminator in the middle} {\n        r hset h f \"1\\x002\"\n        catch {r hincrbyfloat h f 1} err\n        set rv {}\n        lappend rv [string match \"ERR*not*float*\" $err]\n    } {1}\n\n    test {HSTRLEN against the small hash} {\n        set err {}\n        foreach k [array names smallhash *] {\n            if {[string length $smallhash($k)] ne [r hstrlen smallhash $k]} {\n                set err \"[string length $smallhash($k)] != [r hstrlen smallhash $k]\"\n                break\n            }\n        }\n        set _ $err\n    } {}\n\n    test {HSTRLEN against the big hash} {\n        set err {}\n        foreach k [array names bighash *] {\n            if {[string length $bighash($k)] ne [r hstrlen bighash $k]} {\n                set err \"[string length $bighash($k)] != [r hstrlen bighash $k]\"\n                puts \"HSTRLEN and logical length mismatch:\"\n                puts \"key: $k\"\n                puts \"Logical content: $bighash($k)\"\n                puts \"Server  content: [r hget bighash $k]\"\n            }\n        }\n        set _ $err\n    } {}\n\n    test {HSTRLEN against non existing field} {\n        set rv {}\n        lappend rv [r hstrlen smallhash __123123123__]\n        lappend rv [r hstrlen bighash __123123123__]\n        set _ $rv\n    } {0 0}\n\n    test {HSTRLEN corner cases} {\n        set vals {\n            -9223372036854775808 9223372036854775807 9223372036854775808\n            {} 0 -1 x\n        }\n        foreach v $vals {\n            r hmset smallhash field $v\n            r hmset bighash field $v\n            set len1 [string length $v]\n            set len2 [r hstrlen smallhash field]\n            set len3 [r hstrlen bighash field]\n            assert {$len1 == $len2}\n            assert {$len2 == $len3}\n        }\n    }\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    # The following test can only be executed if we don't use Valgrind, and if\n    # we are using x86_64 architecture, because:\n    #\n    # 1) Valgrind has floating point limitations, no support for 80 bits math.\n    # 2) Other archs may have the same limits.\n    #\n    # 1.23 cannot be represented correctly with 64 bit doubles, so we skip\n    # the test, since we are only testing pretty printing here and is not\n    # a bug if the program outputs things like 1.299999...\n    if {!$::valgrind && [string match *x86_64* [exec uname -a]]} {\n        test {Test HINCRBYFLOAT for correct float representation (issue #2846)} {\n            r del myhash\n            assert {[r hincrbyfloat myhash float 1.23] eq {1.23}}\n            assert {[r hincrbyfloat myhash float 0.77] eq {2}}\n            assert {[r hincrbyfloat myhash float -0.1] eq {1.9}}\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/type/incr.tcl",
    "content": "start_server {tags {\"incr\"}} {\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 {INCR uses shared objects in the 0-9999 range} {\n        r set foo -1\n        r incr foo\n        assert {[r object refcount foo] > 1}\n        r set foo 9998\n        r incr foo\n        assert {[r object refcount foo] > 1}\n        r incr foo\n        assert {[r object refcount foo] == 1}\n    }\n\n    test {INCR can modify objects in-place} {\n        r set foo 20000\n        r incr foo\n        assert {[r object refcount foo] == 1}\n        set old [lindex [split [r debug object foo]] 1]\n        r incr foo\n        set new [lindex [split [r debug object foo]] 1]\n        assert {[string range $old 0 2] eq \"at:\"}\n        assert {[string range $new 0 2] eq \"at:\"}\n        assert {$old eq $new}\n    }\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 {string to double with null terminator} {\n        r set foo 1\n        r setrange foo 2 2\n        catch {r incrbyfloat foo 1} err\n        format $err\n    } {ERR*valid*}\n\n    test {No negative zero} {\n        r del foo\n        r incrbyfloat foo [expr double(1)/41]\n        r incrbyfloat foo [expr double(-1)/41]\n        r get foo\n    } {0}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/type/list-2.tcl",
    "content": "start_server {\n    tags {\"list\"}\n    overrides {\n        \"list-max-ziplist-size\" 4\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 before_len [llength $mylist]\n                    set before_len_r [r llen mylist]\n                    set mylist [lrange $mylist $min $max]\n                    r ltrim mylist $min $max\n                    assert_equal $mylist [r lrange mylist 0 -1] \"failed trim\"\n\n                    set starting [r llen mylist]\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                        assert_equal $mylist [r lrange mylist 0 -1] \"failed append match\"\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/type/list-3.tcl",
    "content": "start_server {\n    tags {list ziplist}\n    overrides {\n        \"list-max-ziplist-size\" 16\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    test {Regression for quicklist #3343 bug} {\n        r del mylist\n        r lpush mylist 401\n        r lpush mylist 392\n        r rpush mylist [string repeat x 5105]\"799\"\n        r lset mylist -1 [string repeat x 1014]\"702\"\n        r lpop mylist\n        r lset mylist -1 [string repeat x 4149]\"852\"\n        r linsert mylist before 401 [string repeat x 9927]\"12\"\n        r lrange mylist 0 -1\n        r ping ; # It's enough if the server is still alive\n    } {PONG}\n\n    test {Stress tester for #3343-alike bugs} {\n        r del key\n        for {set j 0} {$j < 10000} {incr j} {\n            set op [randomInt 6]\n            set small_signed_count [expr 5-[randomInt 10]]\n            if {[randomInt 2] == 0} {\n                set ele [randomInt 1000]\n            } else {\n                set ele [string repeat x [randomInt 10000]][randomInt 1000]\n            }\n            switch $op {\n                0 {r lpush key $ele}\n                1 {r rpush key $ele}\n                2 {r lpop key}\n                3 {r rpop key}\n                4 {\n                    catch {r lset key $small_signed_count $ele}\n                }\n                5 {\n                    set otherele [randomInt 1000]\n                    if {[randomInt 2] == 0} {\n                        set where before\n                    } else {\n                        set where after\n                    }\n                    r linsert key $where $otherele $ele\n                }\n            }\n        }\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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/tests/unit/type/list.tcl",
    "content": "start_server {\n    tags {\"list\"}\n    overrides {\n        \"list-max-ziplist-size\" 5\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 aa]\n        assert_equal 2 [r rpush myziplist1 bb]\n        assert_equal 3 [r rpush myziplist1 cc]\n        assert_equal 3 [r llen myziplist1]\n        assert_equal aa [r lindex myziplist1 0]\n        assert_equal bb [r lindex myziplist1 1]\n        assert_equal cc [r lindex myziplist1 2]\n        assert_equal {} [r lindex myziplist2 3]\n        assert_equal cc [r rpop myziplist1]\n        assert_equal aa [r lpop myziplist1]\n        assert_encoding quicklist 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 quicklist 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 quicklist 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 quicklist 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} {\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_list {key entries} {\n        r del $key\n        foreach entry $entries { r rpush $key $entry }\n        assert_encoding quicklist $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_list 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_list blist1 \"a $large c\"\n            create_list 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_list 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_list 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    test \"BLPOP when new key is moved into place\" {\n        set rd [redis_deferring_client]\n\n        $rd blpop foo 5\n        r lpush bob abc def hij\n        r rename bob foo\n        $rd read\n    } {foo hij}\n\n    test \"BLPOP when result key is created by SORT..STORE\" {\n        set rd [redis_deferring_client]\n\n        # zero out list from previous test without explicit delete\n        r lpop foo\n        r lpop foo\n        r lpop foo\n\n        $rd blpop foo 5\n        r lpush notfoo hello hola aguacate konichiwa zanzibar\n        r sort notfoo ALPHA store foo\n        $rd read\n    } {foo aguacate}\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            r del blist1\n            $rd $pop blist1 0.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 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_list xlist \"$large c\"\n            assert_equal 3 [r rpushx xlist d]\n            assert_equal 4 [r lpushx xlist a]\n            assert_equal 6 [r rpushx xlist 42 x]\n            assert_equal 9 [r lpushx xlist y3 y2 y1]\n            assert_equal \"y1 y2 y3 a $large c d 42 x\" [r lrange xlist 0 -1]\n        }\n\n        test \"LINSERT - $type\" {\n            create_list xlist \"a $large c d\"\n            assert_equal 5 [r linsert xlist before c zz] \"before c\"\n            assert_equal \"a $large zz c d\" [r lrange xlist 0 10] \"lrangeA\"\n            assert_equal 6 [r linsert xlist after c yy] \"after c\"\n            assert_equal \"a $large zz c yy d\" [r lrange xlist 0 10] \"lrangeB\"\n            assert_equal 7 [r linsert xlist after d dd] \"after d\"\n            assert_equal -1 [r linsert xlist after bad ddd] \"after bad\"\n            assert_equal \"a $large zz c yy d dd\" [r lrange xlist 0 10] \"lrangeC\"\n            assert_equal 8 [r linsert xlist before a aa] \"before a\"\n            assert_equal -1 [r linsert xlist before bad aaa] \"before bad\"\n            assert_equal \"aa a $large zz c yy d dd\" [r lrange xlist 0 10] \"lrangeD\"\n\n            # check inserting integer encoded value\n            assert_equal 9 [r linsert xlist before aa 42] \"before aa\"\n            assert_equal 42 [r lrange xlist 0 0] \"lrangeE\"\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    foreach {type num} {quicklist 250 quicklist 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_list 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 quicklist mylist2\n        }\n\n        test \"RPOPLPUSH with the same list as src and dst - $type\" {\n            create_list 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_list srclist \"a b c $large\"\n                create_list 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 quicklist 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_list 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_list 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} {quicklist 250 quicklist 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_list 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_list 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_list 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_list 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_list 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_list 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_list 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_list 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_list 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": "Chapter02/redis-6.0.3/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 \"SPOP with <count>=1 - $type\" {\n            create_set myset $contents\n            assert_encoding $type myset\n            assert_equal $contents [lsort [list [r spop myset 1] [r spop myset 1] [r spop myset 1]]]\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    foreach {type contents} {\n        hashtable {a b c d e f g h i j k l m n o p q r s t u v w x y z} \n        intset {1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 26 3 4 5 6 7 8 9}\n    } {\n        test \"SPOP with <count>\" {\n            create_set myset $contents\n            assert_encoding $type myset\n            assert_equal $contents [lsort [concat [r spop myset 11] [r spop myset 9] [r spop myset 0] [r spop myset 4] [r spop myset 1] [r spop myset 0] [r spop myset 1] [r spop myset 0]]]\n            assert_equal 0 [r scard myset]\n        }\n    }\n\n    # As seen in intsetRandomMembers\n    test \"SPOP using integers, testing Knuth's and Floyd's algorithm\" {\n        create_set myset {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        assert_encoding intset myset\n        assert_equal 20 [r scard myset]\n        r spop myset 1\n        assert_equal 19 [r scard myset]\n        r spop myset 2\n        assert_equal 17 [r scard myset]\n        r spop myset 3\n        assert_equal 14 [r scard myset]\n        r spop myset 10\n        assert_equal 4 [r scard myset]\n        r spop myset 10\n        assert_equal 0 [r scard myset]\n        r spop myset 1\n        assert_equal 0 [r scard myset]\n    } {}\n\n    test \"SPOP using integers with Knuth's algorithm\" {\n        r spop nonexisting_key 100\n    } {}\n\n    test \"SPOP new implementation: code path #1\" {\n        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        create_set myset $content\n        set res [r spop myset 30]\n        assert {[lsort $content] eq [lsort $res]}\n    }\n\n    test \"SPOP new implementation: code path #2\" {\n        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        create_set myset $content\n        set res [r spop myset 2]\n        assert {[llength $res] == 2}\n        assert {[r scard myset] == 18}\n        set union [concat [r smembers myset] $res]\n        assert {[lsort $union] eq [lsort $content]}\n    }\n\n    test \"SPOP new implementation: code path #3\" {\n        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        create_set myset $content\n        set res [r spop myset 18]\n        assert {[llength $res] == 18}\n        assert {[r scard myset] == 2}\n        set union [concat [r smembers myset] $res]\n        assert {[lsort $union] eq [lsort $content]}\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 0 [r smove myset1 myset1 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": "Chapter02/redis-6.0.3/tests/unit/type/stream-cgroups.tcl",
    "content": "start_server {\n    tags {\"stream\"}\n} {\n    test {XGROUP CREATE: creation and duplicate group name detection} {\n        r DEL mystream\n        r XADD mystream * foo bar\n        r XGROUP CREATE mystream mygroup $\n        catch {r XGROUP CREATE mystream mygroup $} err\n        set err\n    } {BUSYGROUP*}\n\n    test {XGROUP CREATE: automatic stream creation fails without MKSTREAM} {\n        r DEL mystream\n        catch {r XGROUP CREATE mystream mygroup $} err\n        set err\n    } {ERR*}\n\n    test {XGROUP CREATE: automatic stream creation works with MKSTREAM} {\n        r DEL mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n    } {OK}\n\n    test {XREADGROUP will return only new elements} {\n        r XADD mystream * a 1\n        r XADD mystream * b 2\n        # XREADGROUP should return only the new elements \"a 1\" \"b 1\"\n        # and not the element \"foo bar\" which was pre existing in the\n        # stream (see previous test)\n        set reply [\n            r XREADGROUP GROUP mygroup client-1 STREAMS mystream \">\"\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        lindex $reply 0 1 0 1\n    } {a 1}\n\n    test {XREADGROUP can read the history of the elements we own} {\n        # Add a few more elements\n        r XADD mystream * c 3\n        r XADD mystream * d 4\n        # Read a few elements using a different consumer name\n        set reply [\n            r XREADGROUP GROUP mygroup client-2 STREAMS mystream \">\"\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        assert {[lindex $reply 0 1 0 1] eq {c 3}}\n\n        set r1 [r XREADGROUP GROUP mygroup client-1 COUNT 10 STREAMS mystream 0]\n        set r2 [r XREADGROUP GROUP mygroup client-2 COUNT 10 STREAMS mystream 0]\n        assert {[lindex $r1 0 1 0 1] eq {a 1}}\n        assert {[lindex $r2 0 1 0 1] eq {c 3}}\n    }\n\n    test {XPENDING is able to return pending items} {\n        set pending [r XPENDING mystream mygroup - + 10]\n        assert {[llength $pending] == 4}\n        for {set j 0} {$j < 4} {incr j} {\n            set item [lindex $pending $j]\n            if {$j < 2} {\n                set owner client-1\n            } else {\n                set owner client-2\n            }\n            assert {[lindex $item 1] eq $owner}\n            assert {[lindex $item 1] eq $owner}\n        }\n    }\n\n    test {XPENDING can return single consumer items} {\n        set pending [r XPENDING mystream mygroup - + 10 client-1]\n        assert {[llength $pending] == 2}\n    }\n\n    test {XACK is able to remove items from the client/group PEL} {\n        set pending [r XPENDING mystream mygroup - + 10 client-1]\n        set id1 [lindex $pending 0 0]\n        set id2 [lindex $pending 1 0]\n        assert {[r XACK mystream mygroup $id1] eq 1}\n        set pending [r XPENDING mystream mygroup - + 10 client-1]\n        assert {[llength $pending] == 1}\n        set id [lindex $pending 0 0]\n        assert {$id eq $id2}\n        set global_pel [r XPENDING mystream mygroup - + 10]\n        assert {[llength $global_pel] == 3}\n    }\n\n    test {XACK can't remove the same item multiple times} {\n        assert {[r XACK mystream mygroup $id1] eq 0}\n    }\n\n    test {XACK is able to accept multiple arguments} {\n        # One of the IDs was already removed, so it should ack\n        # just ID2.\n        assert {[r XACK mystream mygroup $id1 $id2] eq 1}\n    }\n\n    test {XACK should fail if got at least one invalid ID} {\n        r del mystream\n        r xgroup create s g $ MKSTREAM\n        r xadd s * f1 v1\n        set c [llength [lindex [r xreadgroup group g c streams s >] 0 1]]\n        assert {$c == 1}\n        set pending [r xpending s g - + 10 c]\n        set id1 [lindex $pending 0 0]\n        assert_error \"*Invalid stream ID specified*\" {r xack s g $id1 invalid-id}\n        assert {[r xack s g $id1] eq 1}\n    }\n\n    test {PEL NACK reassignment after XGROUP SETID event} {\n        r del events\n        r xadd events * f1 v1\n        r xadd events * f1 v1\n        r xadd events * f1 v1\n        r xadd events * f1 v1\n        r xgroup create events g1 $\n        r xadd events * f1 v1\n        set c [llength [lindex [r xreadgroup group g1 c1 streams events >] 0 1]]\n        assert {$c == 1}\n        r xgroup setid events g1 -\n        set c [llength [lindex [r xreadgroup group g1 c2 streams events >] 0 1]]\n        assert {$c == 5}\n    }\n\n    test {XREADGROUP will not report data on empty history. Bug #5577} {\n        r del events\n        r xadd events * a 1\n        r xadd events * b 2\n        r xadd events * c 3\n        r xgroup create events mygroup 0\n\n        # Current local PEL should be empty\n        set res [r xpending events mygroup - + 10]\n        assert {[llength $res] == 0}\n\n        # So XREADGROUP should read an empty history as well\n        set res [r xreadgroup group mygroup myconsumer count 3 streams events 0]\n        assert {[llength [lindex $res 0 1]] == 0}\n\n        # We should fetch all the elements in the stream asking for >\n        set res [r xreadgroup group mygroup myconsumer count 3 streams events >]\n        assert {[llength [lindex $res 0 1]] == 3}\n\n        # Now the history is populated with three not acked entries\n        set res [r xreadgroup group mygroup myconsumer count 3 streams events 0]\n        assert {[llength [lindex $res 0 1]] == 3}\n    }\n\n    test {XREADGROUP history reporting of deleted entries. Bug #5570} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        r XADD mystream 1 field1 A\n        r XREADGROUP GROUP mygroup myconsumer STREAMS mystream >\n        r XADD mystream MAXLEN 1 2 field1 B\n        r XREADGROUP GROUP mygroup myconsumer STREAMS mystream >\n\n        # Now we have two pending entries, however one should be deleted\n        # and one should be ok (we should only see \"B\")\n        set res [r XREADGROUP GROUP mygroup myconsumer STREAMS mystream 0-1]\n        assert {[lindex $res 0 1 0] == {1-0 {}}}\n        assert {[lindex $res 0 1 1] == {2-0 {field1 B}}}\n    }\n\n    test {Blocking XREADGROUP will not reply with an empty array} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        r XADD mystream 666 f v\n        set res [r XREADGROUP GROUP mygroup Alice BLOCK 10 STREAMS mystream \">\"]\n        assert {[lindex $res 0 1 0] == {666-0 {f v}}}\n        r XADD mystream 667 f2 v2\n        r XDEL mystream 667\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 10 STREAMS mystream \">\"\n        after 20\n        assert {[$rd read] == {}} ;# before the fix, client didn't even block, but was served synchronously with {mystream {}}\n    }\n\n    test {XGROUP DESTROY should unblock XREADGROUP with -NOGROUP} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 100 STREAMS mystream \">\"\n        r XGROUP DESTROY mystream mygroup\n        assert_error \"*NOGROUP*\" {$rd read}\n    }\n\n    test {RENAME can unblock XREADGROUP with data} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS mystream \">\"\n        r XGROUP CREATE mystream2 mygroup $ MKSTREAM\n        r XADD mystream2 100 f1 v1\n        r RENAME mystream2 mystream\n        assert_equal \"{mystream {{100-0 {f1 v1}}}}\" [$rd read] ;# mystream2 had mygroup before RENAME\n    }\n\n    test {RENAME can unblock XREADGROUP with -NOGROUP} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS mystream \">\"\n        r XADD mystream2 100 f1 v1\n        r RENAME mystream2 mystream\n        assert_error \"*NOGROUP*\" {$rd read} ;# mystream2 didn't have mygroup before RENAME\n    }\n\n    test {XCLAIM can claim PEL items from another consumer} {\n        # Add 3 items into the stream, and create a consumer group\n        r del mystream\n        set id1 [r XADD mystream * a 1]\n        set id2 [r XADD mystream * b 2]\n        set id3 [r XADD mystream * c 3]\n        r XGROUP CREATE mystream mygroup 0\n\n        # Client 1 reads item 1 from the stream without acknowledgements.\n        # Client 2 then claims pending item 1 from the PEL of client 1\n        set reply [\n            r XREADGROUP GROUP mygroup client1 count 1 STREAMS mystream >\n        ]\n        assert {[llength [lindex $reply 0 1 0 1]] == 2}\n        assert {[lindex $reply 0 1 0 1] eq {a 1}}\n        r debug sleep 0.2\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id1\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        assert {[lindex $reply 0 1] eq {a 1}}\n\n        # Client 1 reads another 2 items from stream\n        r XREADGROUP GROUP mygroup client1 count 2 STREAMS mystream >\n        r debug sleep 0.2\n\n        # Delete item 2 from the stream. Now client 1 has PEL that contains\n        # only item 3. Try to use client 2 to claim the deleted item 2\n        # from the PEL of client 1, this should return nil\n        r XDEL mystream $id2\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id2\n        ]\n        assert {[llength $reply] == 1}\n        assert_equal \"\" [lindex $reply 0]\n\n        # Delete item 3 from the stream. Now client 1 has PEL that is empty.\n        # Try to use client 2 to claim the deleted item 3 from the PEL\n        # of client 1, this should return nil\n        r debug sleep 0.2\n        r XDEL mystream $id3\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id3\n        ]\n        assert {[llength $reply] == 1}\n        assert_equal \"\" [lindex $reply 0]\n    }\n\n    test {XCLAIM without JUSTID increments delivery count} {\n        # Add 3 items into the stream, and create a consumer group\n        r del mystream\n        set id1 [r XADD mystream * a 1]\n        set id2 [r XADD mystream * b 2]\n        set id3 [r XADD mystream * c 3]\n        r XGROUP CREATE mystream mygroup 0\n\n        # Client 1 reads item 1 from the stream without acknowledgements.\n        # Client 2 then claims pending item 1 from the PEL of client 1\n        set reply [\n            r XREADGROUP GROUP mygroup client1 count 1 STREAMS mystream >\n        ]\n        assert {[llength [lindex $reply 0 1 0 1]] == 2}\n        assert {[lindex $reply 0 1 0 1] eq {a 1}}\n        r debug sleep 0.2\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id1\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        assert {[lindex $reply 0 1] eq {a 1}}\n\n        set reply [\n            r XPENDING mystream mygroup - + 10\n        ]\n        assert {[llength [lindex $reply 0]] == 4}\n        assert {[lindex $reply 0 3] == 2}\n\n        # Client 3 then claims pending item 1 from the PEL of client 2 using JUSTID\n        r debug sleep 0.2\n        set reply [\n            r XCLAIM mystream mygroup client3 10 $id1 JUSTID\n        ]\n        assert {[llength $reply] == 1}\n        assert {[lindex $reply 0] eq $id1}\n\n        set reply [\n            r XPENDING mystream mygroup - + 10\n        ]\n        assert {[llength [lindex $reply 0]] == 4}\n        assert {[lindex $reply 0 3] == 2}\n    }\n\n    test {XINFO FULL output} {\n        r del x\n        r XADD x 100 a 1\n        r XADD x 101 b 1\n        r XADD x 102 c 1\n        r XADD x 103 e 1\n        r XADD x 104 f 1\n        r XGROUP CREATE x g1 0\n        r XGROUP CREATE x g2 0\n        r XREADGROUP GROUP g1 Alice COUNT 1 STREAMS x >\n        r XREADGROUP GROUP g1 Bob COUNT 1 STREAMS x >\n        r XREADGROUP GROUP g1 Bob NOACK COUNT 1 STREAMS x >\n        r XREADGROUP GROUP g2 Charlie COUNT 4 STREAMS x >\n        r XDEL x 103\n\n        set reply [r XINFO STREAM x FULL]\n        assert_equal [llength $reply] 12\n        assert_equal [lindex $reply 1] 4 ;# stream length\n        assert_equal [lindex $reply 9] \"{100-0 {a 1}} {101-0 {b 1}} {102-0 {c 1}} {104-0 {f 1}}\" ;# entries\n        assert_equal [lindex $reply 11 0 1] \"g1\" ;# first group name\n        assert_equal [lindex $reply 11 0 7 0 0] \"100-0\" ;# first entry in group's PEL\n        assert_equal [lindex $reply 11 0 9 0 1] \"Alice\" ;# first consumer\n        assert_equal [lindex $reply 11 0 9 0 7 0 0] \"100-0\" ;# first entry in first consumer's PEL\n        assert_equal [lindex $reply 11 1 1] \"g2\" ;# second group name\n        assert_equal [lindex $reply 11 1 9 0 1] \"Charlie\" ;# first consumer\n        assert_equal [lindex $reply 11 1 9 0 7 0 0] \"100-0\" ;# first entry in first consumer's PEL\n        assert_equal [lindex $reply 11 1 9 0 7 1 0] \"101-0\" ;# second entry in first consumer's PEL\n\n        set reply [r XINFO STREAM x FULL COUNT 1]\n        assert_equal [llength $reply] 12\n        assert_equal [lindex $reply 1] 4\n        assert_equal [lindex $reply 9] \"{100-0 {a 1}}\"\n    }\n\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        foreach noack {0 1} {\n            test \"Consumer group last ID propagation to slave (NOACK=$noack)\" {\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                $master del stream\n                $master xadd stream * a 1\n                $master xadd stream * a 2\n                $master xadd stream * a 3\n                $master xgroup create stream mygroup 0\n\n                # Consume the first two items on the master\n                for {set j 0} {$j < 2} {incr j} {\n                    if {$noack} {\n                        set item [$master xreadgroup group mygroup \\\n                                  myconsumer COUNT 1 NOACK STREAMS stream >]\n                    } else {\n                        set item [$master xreadgroup group mygroup \\\n                                  myconsumer COUNT 1 STREAMS stream >]\n                    }\n                    set id [lindex $item 0 1 0 0]\n                    if {$noack == 0} {\n                        assert {[$master xack stream mygroup $id] eq \"1\"}\n                    }\n                }\n\n                wait_for_ofs_sync $master $slave\n\n                # Turn slave into master\n                $slave slaveof no one\n\n                set item [$slave xreadgroup group mygroup myconsumer \\\n                          COUNT 1 STREAMS stream >]\n\n                # The consumed enty should be the third\n                set myentry [lindex $item 0 1 0 1]\n                assert {$myentry eq {a 3}}\n            }\n        }\n    }\n\n    start_server {tags {\"stream\"} overrides {appendonly yes aof-use-rdb-preamble no}} {\n        test {Empty stream with no lastid can be rewrite into AOF correctly} {\n            r XGROUP CREATE mystream group-name $ MKSTREAM\n            assert {[dict get [r xinfo stream mystream] length] == 0}\n            set grpinfo [r xinfo groups mystream]\n            r bgrewriteaof\n            waitForBgrewriteaof r\n            r debug loadaof\n            assert {[dict get [r xinfo stream mystream] length] == 0}\n            assert {[r xinfo groups mystream] == $grpinfo}\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/type/stream.tcl",
    "content": "# return value is like strcmp() and similar.\nproc streamCompareID {a b} {\n    if {$a eq $b} {return 0}\n    lassign [split $a -] a_ms a_seq\n    lassign [split $b -] b_ms b_seq\n    if {$a_ms > $b_ms} {return 1}\n    if {$a_ms < $b_ms} {return -1}\n    # Same ms case, compare seq.\n    if {$a_seq > $b_seq} {return 1}\n    if {$a_seq < $b_seq} {return -1}\n}\n\n# return the ID immediately greater than the specified one.\n# Note that this function does not care to handle 'seq' overflow\n# since it's a 64 bit value.\nproc streamNextID {id} {\n    lassign [split $id -] ms seq\n    incr seq\n    join [list $ms $seq] -\n}\n\n# Generate a random stream entry ID with the ms part between min and max\n# and a low sequence number (0 - 999 range), in order to stress test\n# XRANGE against a Tcl implementation implementing the same concept\n# with Tcl-only code in a linear array.\nproc streamRandomID {min_id max_id} {\n    lassign [split $min_id -] min_ms min_seq\n    lassign [split $max_id -] max_ms max_seq\n    set delta [expr {$max_ms-$min_ms+1}]\n    set ms [expr {$min_ms+[randomInt $delta]}]\n    set seq [randomInt 1000]\n    return $ms-$seq\n}\n\n# Tcl-side implementation of XRANGE to perform fuzz testing in the Redis\n# XRANGE implementation.\nproc streamSimulateXRANGE {items start end} {\n    set res {}\n    foreach i $items  {\n        set this_id [lindex $i 0]\n        if {[streamCompareID $this_id $start] >= 0} {\n            if {[streamCompareID $this_id $end] <= 0} {\n                lappend res $i\n            }\n        }\n    }\n    return $res\n}\n\nset content {} ;# Will be populated with Tcl side copy of the stream content.\n\nstart_server {\n    tags {\"stream\"}\n} {\n    test {XADD can add entries into a stream that XRANGE can fetch} {\n        r XADD mystream * item 1 value a\n        r XADD mystream * item 2 value b\n        assert_equal 2 [r XLEN mystream]\n        set items [r XRANGE mystream - +]\n        assert_equal [lindex $items 0 1] {item 1 value a}\n        assert_equal [lindex $items 1 1] {item 2 value b}\n    }\n\n    test {XADD IDs are incremental} {\n        set id1 [r XADD mystream * item 1 value a]\n        set id2 [r XADD mystream * item 2 value b]\n        set id3 [r XADD mystream * item 3 value c]\n        assert {[streamCompareID $id1 $id2] == -1}\n        assert {[streamCompareID $id2 $id3] == -1}\n    }\n\n    test {XADD IDs are incremental when ms is the same as well} {\n        r multi\n        r XADD mystream * item 1 value a\n        r XADD mystream * item 2 value b\n        r XADD mystream * item 3 value c\n        lassign [r exec] id1 id2 id3\n        assert {[streamCompareID $id1 $id2] == -1}\n        assert {[streamCompareID $id2 $id3] == -1}\n    }\n\n    test {XADD IDs correctly report an error when overflowing} {\n        r DEL mystream\n        r xadd mystream 18446744073709551615-18446744073709551615 a b\n        assert_error ERR* {r xadd mystream * c d}\n    }\n\n    test {XADD with MAXLEN option} {\n        r DEL mystream\n        for {set j 0} {$j < 1000} {incr j} {\n            if {rand() < 0.9} {\n                r XADD mystream MAXLEN 5 * xitem $j\n            } else {\n                r XADD mystream MAXLEN 5 * yitem $j\n            }\n        }\n        set res [r xrange mystream - +]\n        set expected 995\n        foreach r $res {\n            assert {[lindex $r 1 1] == $expected}\n            incr expected\n        }\n    }\n\n    test {XADD mass insertion and XLEN} {\n        r DEL mystream\n        r multi\n        for {set j 0} {$j < 10000} {incr j} {\n            # From time to time insert a field with a different set\n            # of fields in order to stress the stream compression code.\n            if {rand() < 0.9} {\n                r XADD mystream * item $j\n            } else {\n                r XADD mystream * item $j otherfield foo\n            }\n        }\n        r exec\n\n        set items [r XRANGE mystream - +]\n        for {set j 0} {$j < 10000} {incr j} {\n            assert {[lrange [lindex $items $j 1] 0 1] eq [list item $j]}\n        }\n        assert {[r xlen mystream] == $j}\n    }\n\n    test {XADD with ID 0-0} {\n        r DEL otherstream\n        catch {r XADD otherstream 0-0 k v} err\n        assert {[r EXISTS otherstream] == 0}\n    }\n\n    test {XRANGE COUNT works as expected} {\n        assert {[llength [r xrange mystream - + COUNT 10]] == 10}\n    }\n\n    test {XREVRANGE COUNT works as expected} {\n        assert {[llength [r xrevrange mystream + - COUNT 10]] == 10}\n    }\n\n    test {XRANGE can be used to iterate the whole stream} {\n        set last_id \"-\"\n        set j 0\n        while 1 {\n            set elements [r xrange mystream $last_id + COUNT 100]\n            if {[llength $elements] == 0} break\n            foreach e $elements {\n                assert {[lrange [lindex $e 1] 0 1] eq [list item $j]}\n                incr j;\n            }\n            set last_id [streamNextID [lindex $elements end 0]]\n        }\n        assert {$j == 10000}\n    }\n\n    test {XREVRANGE returns the reverse of XRANGE} {\n        assert {[r xrange mystream - +] == [lreverse [r xrevrange mystream + -]]}\n    }\n\n    test {XREAD with non empty stream} {\n        set res [r XREAD COUNT 1 STREAMS mystream 0-0]\n        assert {[lrange [lindex $res 0 1 0 1] 0 1] eq {item 0}}\n    }\n\n    test {Non blocking XREAD with empty streams} {\n        set res [r XREAD STREAMS s1 s2 0-0 0-0]\n        assert {$res eq {}}\n    }\n\n    test {XREAD with non empty second stream} {\n        set res [r XREAD COUNT 1 STREAMS nostream mystream 0-0 0-0]\n        assert {[lindex $res 0 0] eq {mystream}}\n        assert {[lrange [lindex $res 0 1 0 1] 0 1] eq {item 0}}\n    }\n\n    test {Blocking XREAD waiting new data} {\n        r XADD s2 * old abcd1234\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s1 s2 s3 $ $ $\n        r XADD s2 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test {Blocking XREAD waiting old data} {\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s1 s2 s3 $ 0-0 $\n        r XADD s2 * foo abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {old abcd1234}}\n    }\n\n    test {Blocking XREAD will not reply with an empty array} {\n        r del s1\n        r XADD s1 666 f v\n        r XADD s1 667 f2 v2\n        r XDEL s1 667\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 10 STREAMS s1 666\n        after 20\n        assert {[$rd read] == {}} ;# before the fix, client didn't even block, but was served synchronously with {s1 {}}\n    }\n\n    test \"XREAD: XADD + DEL should not awake client\" {\n        set rd [redis_deferring_client]\n        r del s1\n        $rd XREAD BLOCK 20000 STREAMS s1 $\n        r multi\n        r XADD s1 * old abcd1234\n        r DEL s1\n        r exec\n        r XADD s1 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s1}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test \"XREAD: XADD + DEL + LPUSH should not awake client\" {\n        set rd [redis_deferring_client]\n        r del s1\n        $rd XREAD BLOCK 20000 STREAMS s1 $\n        r multi\n        r XADD s1 * old abcd1234\n        r DEL s1\n        r LPUSH s1 foo bar\n        r exec\n        r DEL s1\n        r XADD s1 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s1}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test {XREAD with same stream name multiple times should work} {\n        r XADD s2 * old abcd1234\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s2 s2 s2 $ $ $\n        r XADD s2 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test {XREAD + multiple XADD inside transaction} {\n        r XADD s2 * old abcd1234\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s2 s2 s2 $ $ $\n        r MULTI\n        r XADD s2 * field one\n        r XADD s2 * field two\n        r XADD s2 * field three\n        r EXEC\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {field one}}\n        assert {[lindex $res 0 1 1 1] eq {field two}}\n    }\n\n    test {XDEL basic test} {\n        r del somestream\n        r xadd somestream * foo value0\n        set id [r xadd somestream * foo value1]\n        r xadd somestream * foo value2\n        r xdel somestream $id\n        assert {[r xlen somestream] == 2}\n        set result [r xrange somestream - +]\n        assert {[lindex $result 0 1 1] eq {value0}}\n        assert {[lindex $result 1 1 1] eq {value2}}\n    }\n\n    # Here the idea is to check the consistency of the stream data structure\n    # as we remove all the elements down to zero elements.\n    test {XDEL fuzz test} {\n        r del somestream\n        set ids {}\n        set x 0; # Length of the stream\n        while 1 {\n            lappend ids [r xadd somestream * item $x]\n            incr x\n            # Add enough elements to have a few radix tree nodes inside the stream.\n            if {[dict get [r xinfo stream somestream] radix-tree-keys] > 20} break\n        }\n\n        # Now remove all the elements till we reach an empty stream\n        # and after every deletion, check that the stream is sane enough\n        # to report the right number of elements with XRANGE: this will also\n        # force accessing the whole data structure to check sanity.\n        assert {[r xlen somestream] == $x}\n\n        # We want to remove elements in random order to really test the\n        # implementation in a better way.\n        set ids [lshuffle $ids]\n        foreach id $ids {\n            assert {[r xdel somestream $id] == 1}\n            incr x -1\n            assert {[r xlen somestream] == $x}\n            # The test would be too slow calling XRANGE for every iteration.\n            # Do it every 100 removal.\n            if {$x % 100 == 0} {\n                set res [r xrange somestream - +]\n                assert {[llength $res] == $x}\n            }\n        }\n    }\n\n    test {XRANGE fuzzing} {\n        set low_id [lindex $items 0 0]\n        set high_id [lindex $items end 0]\n        for {set j 0} {$j < 100} {incr j} {\n            set start [streamRandomID $low_id $high_id]\n            set end [streamRandomID $low_id $high_id]\n            set range [r xrange mystream $start $end]\n            set tcl_range [streamSimulateXRANGE $items $start $end]\n            if {$range ne $tcl_range} {\n                puts \"*** WARNING *** - XRANGE fuzzing mismatch: $start - $end\"\n                puts \"---\"\n                puts \"XRANGE: '$range'\"\n                puts \"---\"\n                puts \"TCL: '$tcl_range'\"\n                puts \"---\"\n                fail \"XRANGE fuzzing failed, check logs for details\"\n            }\n        }\n    }\n\n    test {XREVRANGE regression test for issue #5006} {\n        # Add non compressed entries\n        r xadd teststream 1234567891230 key1 value1\n        r xadd teststream 1234567891240 key2 value2\n        r xadd teststream 1234567891250 key3 value3\n\n        # Add SAMEFIELD compressed entries\n        r xadd teststream2 1234567891230 key1 value1\n        r xadd teststream2 1234567891240 key1 value2\n        r xadd teststream2 1234567891250 key1 value3\n\n        assert_equal [r xrevrange teststream 1234567891245 -] {{1234567891240-0 {key2 value2}} {1234567891230-0 {key1 value1}}}\n\n        assert_equal [r xrevrange teststream2 1234567891245 -] {{1234567891240-0 {key1 value2}} {1234567891230-0 {key1 value1}}}\n    }\n\n    test {XREAD streamID edge (no-blocking)} {\n        r del x\n        r XADD x 1-1 f v\n        r XADD x 1-18446744073709551615 f v\n        r XADD x 2-1 f v\n        set res [r XREAD BLOCK 0 STREAMS x 1-18446744073709551615]\n        assert {[lindex $res 0 1 0] == {2-1 {f v}}}\n    }\n\n    test {XREAD streamID edge (blocking)} {\n        r del x\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 0 STREAMS x 1-18446744073709551615\n        r XADD x 1-1 f v\n        r XADD x 1-18446744073709551615 f v\n        r XADD x 2-1 f v\n        set res [$rd read]\n        assert {[lindex $res 0 1 0] == {2-1 {f v}}}\n    }\n\n    test {XADD streamID edge} {\n        r del x\n        r XADD x 2577343934890-18446744073709551615 f v ;# we need the timestamp to be in the future\n        r XADD x * f2 v2\n        assert_equal [r XRANGE x - +] {{2577343934890-18446744073709551615 {f v}} {2577343934891-0 {f2 v2}}}\n    }\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes}} {\n    test {XADD with MAXLEN > xlen can propagate correctly} {\n        for {set j 0} {$j < 100} {incr j} {\n            r XADD mystream * xitem v\n        }\n        r XADD mystream MAXLEN 200 * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n        r debug loadaof\n        r XADD mystream * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n    }\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes}} {\n    test {XADD with ~ MAXLEN can propagate correctly} {\n        for {set j 0} {$j < 100} {incr j} {\n            r XADD mystream * xitem v\n        }\n        r XADD mystream MAXLEN ~ $j * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n        r config set stream-node-max-entries 1\n        r debug loadaof\n        r XADD mystream * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n    }\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes stream-node-max-entries 10}} {\n    test {XTRIM with ~ MAXLEN can propagate correctly} {\n        for {set j 0} {$j < 100} {incr j} {\n            r XADD mystream * xitem v\n        }\n        r XTRIM mystream MAXLEN ~ 85\n        assert {[r xlen mystream] == 90}\n        r config set stream-node-max-entries 1\n        r debug loadaof\n        r XADD mystream * xitem v\n        incr j\n        assert {[r xlen mystream] == 91}\n    }\n}\n\nstart_server {tags {\"stream xsetid\"}} {\n    test {XADD can CREATE an empty stream} {\n        r XADD mystream MAXLEN 0 * a b\n        assert {[dict get [r xinfo stream mystream] length] == 0}\n    }\n\n    test {XSETID can set a specific ID} {\n        r XSETID mystream \"200-0\"\n        assert {[dict get [r xinfo stream mystream] last-generated-id] == \"200-0\"}\n    }\n\n    test {XSETID cannot SETID with smaller ID} {\n        r XADD mystream * a b\n        catch {r XSETID mystream \"1-1\"} err\n        r XADD mystream MAXLEN 0 * a b\n        set err\n    } {ERR*smaller*}\n\n    test {XSETID cannot SETID on non-existent key} {\n        catch {r XSETID stream 1-1} err\n        set _ $err\n    } {ERR no such key}\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes aof-use-rdb-preamble no}} {\n    test {Empty stream can be rewrite into AOF correctly} {\n        r XADD mystream MAXLEN 0 * a b\n        assert {[dict get [r xinfo stream mystream] length] == 0}\n        r bgrewriteaof\n        waitForBgrewriteaof r\n        r debug loadaof\n        assert {[dict get [r xinfo stream mystream] length] == 0}\n    }\n\n    test {Stream can be rewrite into AOF correctly after XDEL lastid} {\n        r XSETID mystream 0-0\n        r XADD mystream 1-1 a b\n        r XADD mystream 2-2 a b\n        assert {[dict get [r xinfo stream mystream] length] == 2}\n        r XDEL mystream 2-2\n        r bgrewriteaof\n        waitForBgrewriteaof r\n        r debug loadaof\n        assert {[dict get [r xinfo stream mystream] length] == 1}\n        assert {[dict get [r xinfo stream mystream] last-generated-id] == \"2-2\"}\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/type/string.tcl",
    "content": "start_server {tags {\"string\"}} {\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 {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            r flushdb\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 \"Element at position $x is $val instead of $x\"\n                    break\n                }\n            }\n            set _ $err\n        } {}\n\n        test {DBSIZE should be 10000 now} {\n            r dbsize\n        } {10000}\n    }\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 {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 {GETSET (set new value)} {\n        r del foo\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 {GETRANGE with huge ranges, Github issue #1844} {\n        r set foo bar\n        r getrange foo 0 4294967297\n    } {bar}\n\n    set rna1 {CACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTG}\n    set rna2 {ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}\n    set rnalcs {ACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}\n\n    test {STRALGO LCS string output with STRINGS option} {\n        r STRALGO LCS STRINGS $rna1 $rna2\n    } $rnalcs\n\n    test {STRALGO LCS len} {\n        r STRALGO LCS LEN STRINGS $rna1 $rna2\n    } [string length $rnalcs]\n\n    test {LCS with KEYS option} {\n        r set virus1 $rna1\n        r set virus2 $rna2\n        r STRALGO LCS KEYS virus1 virus2\n    } $rnalcs\n\n    test {LCS indexes} {\n        dict get [r STRALGO LCS IDX KEYS virus1 virus2] matches\n    } {{{238 238} {239 239}} {{236 236} {238 238}} {{229 230} {236 237}} {{224 224} {235 235}} {{1 222} {13 234}}}\n\n    test {LCS indexes with match len} {\n        dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN] matches\n    } {{{238 238} {239 239} 1} {{236 236} {238 238} 1} {{229 230} {236 237} 2} {{224 224} {235 235} 1} {{1 222} {13 234} 222}}\n\n    test {LCS indexes with match len and minimum match len} {\n        dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches\n    } {{{1 222} {13 234} 222}}\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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 \"ZADD with options syntax error with incomplete pair\" {\n            r del ztmp\n            catch {r zadd ztmp xx 10 x 20} err\n            set err\n        } {ERR*}\n\n        test \"ZADD XX option without key - $encoding\" {\n            r del ztmp\n            assert {[r zadd ztmp xx 10 x] == 0}\n            assert {[r type ztmp] eq {none}}\n        }\n\n        test \"ZADD XX existing key - $encoding\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            assert {[r zadd ztmp xx 20 y] == 0}\n            assert {[r zcard ztmp] == 1}\n        }\n\n        test \"ZADD XX returns the number of elements actually added\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            set retval [r zadd ztmp 10 x 20 y 30 z]\n            assert {$retval == 2}\n        }\n\n        test \"ZADD XX updates existing elements score\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            r zadd ztmp xx 5 foo 11 x 21 y 40 zap\n            assert {[r zcard ztmp] == 3}\n            assert {[r zscore ztmp x] == 11}\n            assert {[r zscore ztmp y] == 21}\n        }\n\n        test \"ZADD XX and NX are not compatible\" {\n            r del ztmp\n            catch {r zadd ztmp xx nx 10 x} err\n            set err\n        } {ERR*}\n\n        test \"ZADD NX with non existing key\" {\n            r del ztmp\n            r zadd ztmp nx 10 x 20 y 30 z\n            assert {[r zcard ztmp] == 3}\n        }\n\n        test \"ZADD NX only add new elements without updating old ones\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            assert {[r zadd ztmp nx 11 x 21 y 100 a 200 b] == 2}\n            assert {[r zscore ztmp x] == 10}\n            assert {[r zscore ztmp y] == 20}\n            assert {[r zscore ztmp a] == 100}\n            assert {[r zscore ztmp b] == 200}\n        }\n\n        test \"ZADD INCR works like ZINCRBY\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            r zadd ztmp INCR 15 x\n            assert {[r zscore ztmp x] == 25}\n        }\n\n        test \"ZADD INCR works with a single score-elemenet pair\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            catch {r zadd ztmp INCR 15 x 10 y} err\n            set err\n        } {ERR*}\n\n        test \"ZADD CH option changes return value to all changed elements\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            assert {[r zadd ztmp 11 x 21 y 30 z] == 0}\n            assert {[r zadd ztmp ch 12 x 22 y 30 z] == 2}\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            r del ztmp\n            r zadd ztmp 10 a 20 b 30 c\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        test \"ZINCRBY return value\" {\n            r del ztmp\n            set retval [r zincrby ztmp 1.0 x]\n            assert {$retval == 1.0}\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/ZLEXCOUNT 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 \"ZLEXCOUNT advanced\" {\n            create_default_lex_zset\n    \n            assert_equal 9 [r zlexcount zset - +]\n            assert_equal 0 [r zlexcount zset + -]\n            assert_equal 0 [r zlexcount zset + \\[c]\n            assert_equal 0 [r zlexcount zset \\[c -]\n            assert_equal 8 [r zlexcount zset \\[bar +]\n            assert_equal 5 [r zlexcount zset \\[bar \\[foo]\n            assert_equal 4 [r zlexcount zset \\[bar (foo]\n            assert_equal 4 [r zlexcount zset (bar \\[foo]\n            assert_equal 3 [r zlexcount zset (bar (foo]\n            assert_equal 5 [r zlexcount zset - (foo]\n            assert_equal 1 [r zlexcount zset (maxstring +]\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        test \"Basic ZPOP with a single key - $encoding\" {\n            r del zset\n            assert_equal {} [r zpopmin zset]\n            create_zset zset {-1 a 1 b 2 c 3 d 4 e}\n            assert_equal {a -1} [r zpopmin zset]\n            assert_equal {b 1} [r zpopmin zset]\n            assert_equal {e 4} [r zpopmax zset]\n            assert_equal {d 3} [r zpopmax zset]\n            assert_equal {c 2} [r zpopmin zset]\n            assert_equal 0 [r exists zset]\n            r set foo bar\n            assert_error \"*WRONGTYPE*\" {r zpopmin foo}\n        }\n\n        test \"ZPOP with count - $encoding\" {\n            r del z1 z2 z3 foo\n            r set foo bar\n            assert_equal {} [r zpopmin z1 2]\n            assert_error \"*WRONGTYPE*\" {r zpopmin foo 2}\n            create_zset z1 {0 a 1 b 2 c 3 d}\n            assert_equal {a 0 b 1} [r zpopmin z1 2]\n            assert_equal {d 3 c 2} [r zpopmax z1 2]\n        }\n\n        test \"BZPOP with a single existing sorted set - $encoding\" {\n            set rd [redis_deferring_client]\n            create_zset zset {0 a 1 b 2 c}\n\n            $rd bzpopmin zset 5\n            assert_equal {zset a 0} [$rd read]\n            $rd bzpopmin zset 5\n            assert_equal {zset b 1} [$rd read]\n            $rd bzpopmax zset 5\n            assert_equal {zset c 2} [$rd read]\n            assert_equal 0 [r exists zset]\n        }\n\n        test \"BZPOP with multiple existing sorted sets - $encoding\" {\n            set rd [redis_deferring_client]\n            create_zset z1 {0 a 1 b 2 c}\n            create_zset z2 {3 d 4 e 5 f}\n\n            $rd bzpopmin z1 z2 5\n            assert_equal {z1 a 0} [$rd read]\n            $rd bzpopmax z1 z2 5\n            assert_equal {z1 c 2} [$rd read]\n            assert_equal 1 [r zcard z1]\n            assert_equal 3 [r zcard z2]\n\n            $rd bzpopmax z2 z1 5\n            assert_equal {z2 f 5} [$rd read]\n            $rd bzpopmin z2 z1 5\n            assert_equal {z2 d 3} [$rd read]\n            assert_equal 1 [r zcard z1]\n            assert_equal 1 [r zcard z2]\n        }\n\n        test \"BZPOP second sorted set has members - $encoding\" {\n            set rd [redis_deferring_client]\n            r del z1\n            create_zset z2 {3 d 4 e 5 f}\n            $rd bzpopmax z1 z2 5\n            assert_equal {z2 f 5} [$rd read]\n            $rd bzpopmin z2 z1 5\n            assert_equal {z2 d 3} [$rd read]\n            assert_equal 0 [r zcard z1]\n            assert_equal 1 [r zcard z2]\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    test {ZUNIONSTORE result is sorted} {\n        # Create two sets with common and not common elements, perform\n        # the UNION, check that elements are still sorted.\n        r del one two dest\n        set cmd1 [list r zadd one]\n        set cmd2 [list r zadd two]\n        for {set j 0} {$j < 1000} {incr j} {\n            lappend cmd1 [expr rand()] [randomInt 1000]\n            lappend cmd2 [expr rand()] [randomInt 1000]\n        }\n        {*}$cmd1\n        {*}$cmd2\n        assert {[r zcard one] > 100}\n        assert {[r zcard two] > 100}\n        r zunionstore dest 2 one two\n        set oldscore 0\n        foreach {ele score} [r zrange dest 0 -1 withscores] {\n            assert {$score >= $oldscore}\n            set oldscore $score\n        }\n    }\n\n    test \"ZSET commands don't accept the empty strings as valid score\" {\n        assert_error \"*not*float*\" {r zadd myzset \"\" abc}\n    }\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        test \"BZPOPMIN, ZADD + DEL should not awake blocked client\" {\n            set rd [redis_deferring_client]\n            r del zset\n\n            $rd bzpopmin zset 0\n            r multi\n            r zadd zset 0 foo\n            r del zset\n            r exec\n            r del zset\n            r zadd zset 1 bar\n            $rd read\n        } {zset bar 1}\n\n        test \"BZPOPMIN, ZADD + DEL + SET should not awake blocked client\" {\n            set rd [redis_deferring_client]\n            r del list\n\n            r del zset\n\n            $rd bzpopmin zset 0\n            r multi\n            r zadd zset 0 foo\n            r del zset\n            r set zset foo\n            r exec\n            r del zset\n            r zadd zset 1 bar\n            $rd read\n        } {zset bar 1}\n\n        test \"BZPOPMIN with same key multiple times should work\" {\n            set rd [redis_deferring_client]\n            r del z1 z2\n\n            # Data arriving after the BZPOPMIN.\n            $rd bzpopmin z1 z2 z2 z1 0\n            r zadd z1 0 a\n            assert_equal [$rd read] {z1 a 0}\n            $rd bzpopmin z1 z2 z2 z1 0\n            r zadd z2 1 b\n            assert_equal [$rd read] {z2 b 1}\n\n            # Data already there.\n            r zadd z1 0 a\n            r zadd z2 1 b\n            $rd bzpopmin z1 z2 z2 z1 0\n            assert_equal [$rd read] {z1 a 0}\n            $rd bzpopmin z1 z2 z2 z1 0\n            assert_equal [$rd read] {z2 b 1}\n        }\n\n        test \"MULTI/EXEC is isolated from the point of view of BZPOPMIN\" {\n            set rd [redis_deferring_client]\n            r del zset\n            $rd bzpopmin zset 0\n            r multi\n            r zadd zset 0 a\n            r zadd zset 1 b\n            r zadd zset 2 c\n            r exec\n            $rd read\n        } {zset a 0}\n\n        test \"BZPOPMIN with variadic ZADD\" {\n            set rd [redis_deferring_client]\n            r del zset\n            if {$::valgrind} {after 100}\n            $rd bzpopmin zset 0\n            if {$::valgrind} {after 100}\n            assert_equal 2 [r zadd zset -1 foo 1 bar]\n            if {$::valgrind} {after 100}\n            assert_equal {zset foo -1} [$rd read]\n            assert_equal {bar} [r zrange zset 0 -1]\n        }\n\n        test \"BZPOPMIN with zero timeout should block indefinitely\" {\n            set rd [redis_deferring_client]\n            r del zset\n            $rd bzpopmin zset 0\n            after 1000\n            r zadd zset 0 foo\n            assert_equal {zset foo 0} [$rd read]\n        }\n    }\n\n    tags {\"slow\"} {\n        stressers ziplist\n        stressers skiplist\n    }\n\n    test {ZSET skiplist order consistency when elements are moved} {\n        set original_max [lindex [r config get zset-max-ziplist-entries] 1]\n        r config set zset-max-ziplist-entries 0\n        for {set times 0} {$times < 10} {incr times} {\n            r del zset\n            for {set j 0} {$j < 1000} {incr j} {\n                r zadd zset [randomInt 50] ele-[randomInt 10]\n            }\n\n            # Make sure that element ordering is correct\n            set prev_element {}\n            set prev_score -1\n            foreach {element score} [r zrange zset 0 -1 WITHSCORES] {\n                # Assert that elements are in increasing ordering\n                assert {\n                    $prev_score < $score ||\n                    ($prev_score == $score &&\n                     [string compare $prev_element $element] == -1)\n                }\n                set prev_element $element\n                set prev_score $score\n            }\n        }\n        r config set zset-max-ziplist-entries $original_max\n    }\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/tests/unit/wait.tcl",
    "content": "source tests/support/cli.tcl\n\nstart_server {tags {\"wait\"}} {\nstart_server {} {\n    set slave [srv 0 client]\n    set slave_host [srv 0 host]\n    set slave_port [srv 0 port]\n    set master [srv -1 client]\n    set master_host [srv -1 host]\n    set master_port [srv -1 port]\n\n    test {Setup slave} {\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 {WAIT should acknowledge 1 additional copy of the data} {\n        $master set foo 0\n        $master incr foo\n        $master incr foo\n        $master incr foo\n        assert {[$master wait 1 5000] == 1}\n        assert {[$slave get foo] == 3}\n    }\n\n    test {WAIT should not acknowledge 2 additional copies of the data} {\n        $master incr foo\n        assert {[$master wait 2 1000] <= 1}\n    }\n\n    test {WAIT should not acknowledge 1 additional copy if slave is blocked} {\n        set cmd [rediscli $slave_port \"-h $slave_host debug sleep 5\"]\n        exec {*}$cmd > /dev/null 2> /dev/null &\n        after 1000 ;# Give redis-cli the time to execute the command.\n        $master set foo 0\n        $master incr foo\n        $master incr foo\n        $master incr foo\n        assert {[$master wait 1 3000] == 0}\n    }\n}}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/utils/cluster_fail_time.tcl",
    "content": "# This simple script is used in order to estimate the average PFAIL->FAIL\n# state switch after a failure.\n\nset ::sleep_time 10     ; # How much to sleep to trigger PFAIL.\nset ::fail_port 30016   ; # Node to put in sleep.\nset ::other_port 30001  ; # Node to use to monitor the flag switch.\n\nproc avg vector {\n    set sum 0.0\n    foreach x $vector {\n        set sum [expr {$sum+$x}]\n    }\n    expr {$sum/[llength $vector]}\n}\n\nset samples {}\nwhile 1 {\n    exec redis-cli -p $::fail_port debug sleep $::sleep_time > /dev/null &\n\n    # Wait for fail? to appear.\n    while 1 {\n        set output [exec redis-cli -p $::other_port cluster nodes]\n        if {[string match {*fail\\?*} $output]} break\n        after 100\n    }\n\n    puts \"FAIL?\"\n    set start [clock milliseconds]\n\n    # Wait for fail? to disappear.\n    while 1 {\n        set output [exec redis-cli -p $::other_port cluster nodes]\n        if {![string match {*fail\\?*} $output]} break\n        after 100\n    }\n\n    puts \"FAIL\"\n    set now [clock milliseconds]\n    set elapsed [expr {$now-$start}]\n    puts $elapsed\n    lappend samples $elapsed\n\n    puts \"AVG([llength $samples]): [avg $samples]\"\n\n    # Wait for the instance to be available again.\n    exec redis-cli -p $::fail_port ping\n\n    # Wait for the fail flag to be cleared.\n    after 2000\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/corrupt_rdb.c",
    "content": "/* Trivia program to corrupt an RDB file in order to check the RDB check\n * program behavior and effectiveness.\n *\n * Copyright (C) 2016 Salvatore Sanfilippo.\n * This software is released in the 3-clause BSD license. */\n\n#include <stdio.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <time.h>\n\nint main(int argc, char **argv) {\n    struct stat stat;\n    int fd, cycles;\n\n    if (argc != 3) {\n        fprintf(stderr,\"Usage: <filename> <cycles>\\n\");\n        exit(1);\n    }\n\n    srand(time(NULL));\n    char *filename = argv[1];\n    cycles = atoi(argv[2]);\n    fd = open(filename,O_RDWR);\n    if (fd == -1) {\n        perror(\"open\");\n        exit(1);\n    }\n    fstat(fd,&stat);\n\n    while(cycles--) {\n        unsigned char buf[32];\n        unsigned long offset = rand()%stat.st_size;\n        int writelen = 1+rand()%31;\n        int j;\n\n        for (j = 0; j < writelen; j++) buf[j] = (char)rand();\n        lseek(fd,offset,SEEK_SET);\n        printf(\"Writing %d bytes at offset %lu\\n\", writelen, offset);\n        write(fd,buf,writelen);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/create-cluster/.gitignore",
    "content": "config.sh\n*.rdb\n*.aof\n*.conf\n*.log\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/create-cluster/README",
    "content": "Create-custer is a small script used to easily start a big number of Redis\ninstances configured to run in cluster mode. Its main goal is to allow manual\ntesting in a condition which is not easy to replicate with the Redis cluster\nunit tests, for example when a lot of instances are needed in order to trigger\na given bug.\n\nThe tool can also be used just to easily create a number of instances in a\nRedis Cluster in order to experiment a bit with the system.\n\nUSAGE\n---\n\nTo create a cluster, follow these steps:\n\n1. Edit create-cluster and change the start / end port, depending on the\nnumber of instances you want to create.\n2. Use \"./create-cluster start\" in order to run the instances.\n3. Use \"./create-cluster create\" in order to execute redis-cli --cluster create, so that\nan actual Redis cluster will be created. (If you're accessing your setup via a local container, ensure that the CLUSTER_HOST value is changed to your local IP)\n4. Now you are ready to play with the cluster. AOF files and logs for each instances are created in the current directory.\n\nIn order to stop a cluster:\n\n1. Use \"./create-cluster stop\" to stop all the instances. After you stopped the instances you can use \"./create-cluster start\" to restart them if you change your mind.\n2. Use \"./create-cluster clean\" to remove all the AOF / log files to restart with a clean environment.\n\nUse the command \"./create-cluster help\" to get the full list of features.\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/create-cluster/create-cluster",
    "content": "#!/bin/bash\n\n# Settings\nBIN_PATH=\"../../src/\"\nCLUSTER_HOST=127.0.0.1\nPORT=30000\nTIMEOUT=2000\nNODES=6\nREPLICAS=1\nPROTECTED_MODE=yes\nADDITIONAL_OPTIONS=\"\"\n\n# You may want to put the above config parameters into config.sh in order to\n# override the defaults without modifying this script.\n\nif [ -a config.sh ]\nthen\n    source \"config.sh\"\nfi\n\n# Computed vars\nENDPORT=$((PORT+NODES))\n\nif [ \"$1\" == \"start\" ]\nthen\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        echo \"Starting $PORT\"\n        $BIN_PATH/redis-server --port $PORT  --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"create\" ]\nthen\n    HOSTS=\"\"\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        HOSTS=\"$HOSTS $CLUSTER_HOST:$PORT\"\n    done\n    $BIN_PATH/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS\n    exit 0\nfi\n\nif [ \"$1\" == \"stop\" ]\nthen\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        echo \"Stopping $PORT\"\n        $BIN_PATH/redis-cli -p $PORT shutdown nosave\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"watch\" ]\nthen\n    PORT=$((PORT+1))\n    while [ 1 ]; do\n        clear\n        date\n        $BIN_PATH/redis-cli -p $PORT cluster nodes | head -30\n        sleep 1\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"tail\" ]\nthen\n    INSTANCE=$2\n    PORT=$((PORT+INSTANCE))\n    tail -f ${PORT}.log\n    exit 0\nfi\n\nif [ \"$1\" == \"tailall\" ]\nthen\n    tail -f *.log\n    exit 0\nfi\n\nif [ \"$1\" == \"call\" ]\nthen\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        $BIN_PATH/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"clean\" ]\nthen\n    rm -rf *.log\n    rm -rf appendonly*.aof\n    rm -rf dump*.rdb\n    rm -rf nodes*.conf\n    exit 0\nfi\n\nif [ \"$1\" == \"clean-logs\" ]\nthen\n    rm -rf *.log\n    exit 0\nfi\n\necho \"Usage: $0 [start|create|stop|watch|tail|clean|call]\"\necho \"start       -- Launch Redis Cluster instances.\"\necho \"create      -- Create a cluster using redis-cli --cluster create.\"\necho \"stop        -- Stop Redis Cluster instances.\"\necho \"watch       -- Show CLUSTER NODES output (first 30 lines) of first node.\"\necho \"tail <id>   -- Run tail -f of instance at base port + ID.\"\necho \"tailall     -- Run tail -f for all the log files at once.\"\necho \"clean       -- Remove all instances data, logs, configs.\"\necho \"clean-logs  -- Remove just instances logs.\"\necho \"call <cmd>  -- Call a command (up to 7 arguments) on all nodes.\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/gen-test-certs.sh",
    "content": "#!/bin/bash\nmkdir -p tests/tls\nopenssl genrsa -out tests/tls/ca.key 4096\nopenssl req \\\n    -x509 -new -nodes -sha256 \\\n    -key tests/tls/ca.key \\\n    -days 3650 \\\n    -subj '/O=Redis Test/CN=Certificate Authority' \\\n    -out tests/tls/ca.crt\nopenssl genrsa -out tests/tls/redis.key 2048\nopenssl req \\\n    -new -sha256 \\\n    -key tests/tls/redis.key \\\n    -subj '/O=Redis Test/CN=Server' | \\\n    openssl x509 \\\n        -req -sha256 \\\n        -CA tests/tls/ca.crt \\\n        -CAkey tests/tls/ca.key \\\n        -CAserial tests/tls/ca.txt \\\n        -CAcreateserial \\\n        -days 365 \\\n        -out tests/tls/redis.crt\nopenssl dhparam -out tests/tls/redis.dh 2048\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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  \"cluster\",\n  \"geo\",\n  \"stream\"\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.githubusercontent.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": "Chapter02/redis-6.0.3/utils/graphs/commits-over-time/README.md",
    "content": "This Tcl script is what I used in order to generate the graph you\ncan find at http://antirez.com/news/98. It's really quick & dirty, more\na trow away program than anything else, but probably could be reused or\nmodified in the future in order to visualize other similar data or an\nupdated version of the same data.\n\nThe usage is trivial:\n\n    ./genhtml.tcl > output.html\n\nThe generated HTML is quite broken but good enough to grab a screenshot\nfrom the browser. Feel free to improve it if you got time / interest.\n\nNote that the code filtering the tags, and the hardcoded branch name, does\nnot make the script, as it is, able to analyze a different repository.\nHowever the changes needed are trivial.\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/graphs/commits-over-time/genhtml.tcl",
    "content": "#!/usr/bin/env tclsh\n\n# Load commits history as \"sha1 unixtime\".\nset commits [exec git log unstable {--pretty=\"%H %at\"}]\nset raw_tags [exec git tag]\n\n# Load all the tags that are about stable releases.\nforeach tag $raw_tags {\n    if {[string match v*-stable $tag]} {\n        set tag [string range $tag 1 end-7]\n        puts $tag\n    }\n    if {[regexp {^[0-9]+.[0-9]+.[0-9]+$} $tag]} {\n        lappend tags $tag\n    }\n}\n\n# For each tag, create a list of \"name unixtime\"\nforeach tag $tags {\n    set taginfo [exec git log $tag -n 1 \"--pretty=\\\"$tag %at\\\"\"]\n    set taginfo [string trim $taginfo {\"}]\n    lappend labels $taginfo\n}\n\n# For each commit, check the amount of code changed and create an array\n# mapping the commit to the number of lines affected.\nforeach c $commits {\n    set stat [exec git show --oneline --numstat [lindex $c 0]]\n    set linenum 0\n    set affected 0\n    foreach line [split $stat \"\\n\"] {\n        incr linenum\n        if {$linenum == 1 || [string match *deps/* $line]} continue\n        if {[catch {llength $line} numfields]} continue\n        if {$numfields == 0} continue\n        catch {\n            incr affected [lindex $line 0]\n            incr affected [lindex $line 1]\n        }\n    }\n    set commit_to_affected([lindex $c 0]) $affected\n}\n\nset base_time [lindex [lindex $commits end] 1]\nputs [clock format $base_time]\n\n# Generate a graph made of HTML DIVs.\nputs {<html>\n<style>\n.box {\n    position:absolute;\n    width:10px;\n    height:5px;\n    border:1px black solid;\n    background-color:#44aa33;\n    opacity: 0.04;\n}\n.label {\n    position:absolute;\n    background-color:#dddddd;\n    font-family:helvetica;\n    font-size:12px;\n    padding:2px;\n    color:#666;\n    border:1px #aaa solid;\n    border-radius: 5px;\n}\n#outer {\n    position:relative;\n    width:1500;\n    height:500;\n    border:1px #aaa solid;\n}\n</style>\n<div id=\"outer\">\n}\nforeach c $commits {\n    set sha [lindex $c 0]\n    set t [expr {([lindex $c 1]-$base_time)/(3600*24*2)}]\n    set affected [expr $commit_to_affected($sha)]\n    set left $t\n    set height [expr {log($affected)*20}]\n    puts \"<div class=\\\"box\\\" style=\\\"left:$left; bottom:0; height:$height\\\"></div>\"\n}\n\nset bottom -30\nforeach l $labels {\n    set name [lindex $l 0]\n    set t [expr {([lindex $l 1]-$base_time)/(3600*24*2)}]\n    set left $t\n    if {$left < 0} continue\n    incr bottom -20\n    if  {$bottom == -210} {set bottom -30}\n    puts \"<div class=\\\"label\\\" style=\\\"left:$left; bottom:$bottom\\\">$name</div>\"\n}\nputs {</div></html>}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/hashtable/README",
    "content": "Hash table implementation related utilities.\n\nrehashing.c\n---\n\nVisually show buckets in the two hash tables between rehashings. Also stress\ntest getRandomKeys() implementation, that may actually disappear from\nRedis soon, however visualization some code is reusable in new bugs\ninvestigation.\n\nCompile with:\n\n    cc -I ../../src/ rehashing.c ../../src/zmalloc.c ../../src/dict.c -o rehashing_test\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/hashtable/rehashing.c",
    "content": "#include \"redis.h\"\n#include \"dict.h\"\n\nvoid _redisAssert(char *x, char *y, int l) {\n    printf(\"ASSERT: %s %s %d\\n\",x,y,l);\n    exit(1);\n}\n\nunsigned int dictKeyHash(const void *keyp) {\n    unsigned long key = (unsigned long)keyp;\n    key = dictGenHashFunction(&key,sizeof(key));\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\nint dictKeyCompare(void *privdata, const void *key1, const void *key2) {\n    unsigned long k1 = (unsigned long)key1;\n    unsigned long k2 = (unsigned long)key2;\n    return k1 == k2;\n}\n\ndictType dictTypeTest = {\n    dictKeyHash,                   /* hash function */\n    NULL,                          /* key dup */\n    NULL,                          /* val dup */\n    dictKeyCompare,                /* key compare */\n    NULL,                          /* key destructor */\n    NULL                           /* val destructor */\n};\n\nvoid showBuckets(dictht ht) {\n    if (ht.table == NULL) {\n        printf(\"NULL\\n\");\n    } else {\n        int j;\n        for (j = 0; j < ht.size; j++) {\n            printf(\"%c\", ht.table[j] ? '1' : '0');\n        }\n        printf(\"\\n\");\n    }\n}\n\nvoid show(dict *d) {\n    int j;\n    if (d->rehashidx != -1) {\n        printf(\"rhidx: \");\n        for (j = 0; j < d->rehashidx; j++)\n            printf(\".\");\n        printf(\"|\\n\");\n    }\n    printf(\"ht[0]: \");\n    showBuckets(d->ht[0]);\n    printf(\"ht[1]: \");\n    showBuckets(d->ht[1]);\n    printf(\"\\n\");\n}\n\nint sortPointers(const void *a, const void *b) {\n    unsigned long la, lb;\n\n    la = (long) (*((dictEntry**)a));\n    lb = (long) (*((dictEntry**)b));\n    return la-lb;\n}\n\nvoid stressGetKeys(dict *d, int times, int *perfect_run, int *approx_run) {\n    int j;\n\n    dictEntry **des = zmalloc(sizeof(dictEntry*)*dictSize(d));\n    for (j = 0; j < times; j++) {\n        int requested = rand() % (dictSize(d)+1);\n        int returned = dictGetSomeKeys(d, des, requested);\n        int dup = 0;\n\n        qsort(des,returned,sizeof(dictEntry*),sortPointers);\n        if (returned > 1) {\n            int i;\n            for (i = 0; i < returned-1; i++) {\n                if (des[i] == des[i+1]) dup++;\n            }\n        }\n\n        if (requested == returned && dup == 0) {\n            (*perfect_run)++;\n        } else {\n            (*approx_run)++;\n            printf(\"Requested, returned, duplicated: %d %d %d\\n\",\n                requested, returned, dup);\n        }\n    }\n    zfree(des);\n}\n\n#define MAX1 120\n#define MAX2 1000\nint main(void) {\n    dict *d = dictCreate(&dictTypeTest,NULL);\n    unsigned long i;\n    srand(time(NULL));\n\n    for (i = 0; i < MAX1; i++) {\n        dictAdd(d,(void*)i,NULL);\n        show(d);\n    }\n    printf(\"Size: %d\\n\", (int)dictSize(d));\n\n    for (i = 0; i < MAX1; i++) {\n        dictDelete(d,(void*)i);\n        dictResize(d);\n        show(d);\n    }\n    dictRelease(d);\n\n    d = dictCreate(&dictTypeTest,NULL);\n\n    printf(\"Stress testing dictGetSomeKeys\\n\");\n    int perfect_run = 0, approx_run = 0;\n\n    for (i = 0; i < MAX2; i++) {\n        dictAdd(d,(void*)i,NULL);\n        stressGetKeys(d,100,&perfect_run,&approx_run);\n    }\n\n    for (i = 0; i < MAX2; i++) {\n        dictDelete(d,(void*)i);\n        dictResize(d);\n        stressGetKeys(d,100,&perfect_run,&approx_run);\n    }\n\n    printf(\"dictGetSomeKey, %d perfect runs, %d approximated runs\\n\",\n        perfect_run, approx_run);\n\n    dictRelease(d);\n\n    printf(\"TEST PASSED!\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/hyperloglog/.gitignore",
    "content": "*.txt\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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# Service installer for redis server, runs interactively by default.\n#\n# To run this script non-interactively (for automation/provisioning purposes),\n# feed the variables into the script. Any missing variables will be prompted!\n# Tip: Environment variables also support command substitution (see REDIS_EXECUTABLE)\n#\n# Example:\n#\n# sudo REDIS_PORT=1234 \\\n# \t\t REDIS_CONFIG_FILE=/etc/redis/1234.conf \\\n# \t\t REDIS_LOG_FILE=/var/log/redis_1234.log \\\n# \t\t REDIS_DATA_DIR=/var/lib/redis/1234 \\\n# \t\t REDIS_EXECUTABLE=`command -v redis-server` ./utils/install_server.sh\n#\n# This generates a redis config file and an /etc/init.d script, and installs them.\n#\n# /!\\ This script should be run as root\n#\n# NOTE: This script will not work on Mac OSX.\n#       It supports Debian and Ubuntu Linux.\n#\n################################################################################\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_MANUAL_EXECUTION=false\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#bail if this system is managed by systemd\n_pid_1_exe=\"$(readlink -f /proc/1/exe)\"\nif [ \"${_pid_1_exe##*/}\" = systemd ]\nthen\n\techo \"This systems seems to use systemd.\"\n\techo \"Please take a look at the provided example service unit files in this directory, and adapt and install them. Sorry!\"\n\texit 1\nfi\nunset _pid_1_exe\n\nif ! echo $REDIS_PORT | egrep -q '^[0-9]+$' ; then\n\t_MANUAL_EXECUTION=true\n\t#Read the redis port\n\tread  -p \"Please select the redis port for this instance: [$_REDIS_PORT] \" REDIS_PORT\n\tif ! echo $REDIS_PORT | egrep -q '^[0-9]+$' ; then\n\t\techo \"Selecting default: $_REDIS_PORT\"\n\t\tREDIS_PORT=$_REDIS_PORT\n\tfi\nfi\n\nif [ -z \"$REDIS_CONFIG_FILE\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#read the redis config file\n\t_REDIS_CONFIG_FILE=\"/etc/redis/$REDIS_PORT.conf\"\n\tread -p \"Please select the redis config file name [$_REDIS_CONFIG_FILE] \" REDIS_CONFIG_FILE\n\tif [ -z \"$REDIS_CONFIG_FILE\" ] ; then\n\t\tREDIS_CONFIG_FILE=$_REDIS_CONFIG_FILE\n\t\techo \"Selected default - $REDIS_CONFIG_FILE\"\n\tfi\nfi\n\nif [ -z \"$REDIS_LOG_FILE\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#read the redis log file path\n\t_REDIS_LOG_FILE=\"/var/log/redis_$REDIS_PORT.log\"\n\tread -p \"Please select the redis log file name [$_REDIS_LOG_FILE] \" REDIS_LOG_FILE\n\tif [ -z \"$REDIS_LOG_FILE\" ] ; then\n\t\tREDIS_LOG_FILE=$_REDIS_LOG_FILE\n\t\techo \"Selected default - $REDIS_LOG_FILE\"\n\tfi\nfi\n\nif [ -z \"$REDIS_DATA_DIR\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#get the redis data directory\n\t_REDIS_DATA_DIR=\"/var/lib/redis/$REDIS_PORT\"\n\tread -p \"Please select the data directory for this instance [$_REDIS_DATA_DIR] \" REDIS_DATA_DIR\n\tif [ -z \"$REDIS_DATA_DIR\" ] ; then\n\t\tREDIS_DATA_DIR=$_REDIS_DATA_DIR\n\t\techo \"Selected default - $REDIS_DATA_DIR\"\n\tfi\nfi\n\nif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#get the redis executable path\n\t_REDIS_EXECUTABLE=`command -v redis-server`\n\tread -p \"Please select the redis executable path [$_REDIS_EXECUTABLE] \" REDIS_EXECUTABLE\n\tif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t\tREDIS_EXECUTABLE=$_REDIS_EXECUTABLE\n\n\t\tif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t\t\techo \"Mmmmm...  it seems like you don't have a redis executable. Did you run make install yet?\"\n\t\t\texit 1\n\t\tfi\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\nif $_MANUAL_EXECUTION == true ; then\n\tread -p \"Is this ok? Then press ENTER to go on or Ctrl-C to abort.\" _UNUSED_\nfi\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 .\\+#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 \"$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": "Chapter02/redis-6.0.3/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 the file server.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\nYou can optionally specify a number of times to run, so that the program\nwill output averages of different runs, by adding an additional argument.\nFor instance in order to run the test 10 times use:\n\n    ruby test-lru.rb /tmp/lru.html 10\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/lru/lfu-simulation.c",
    "content": "#include <stdio.h>\n#include <time.h>\n#include <stdint.h>\n#include <stdlib.h>\n\nint decr_every = 1;\nint keyspace_size = 1000000;\ntime_t switch_after = 30; /* Switch access pattern after N seconds. */\n\nstruct entry {\n    /* Field that the LFU Redis implementation will have (we have\n     * 24 bits of total space in the object->lru field). */\n    uint8_t counter;    /* Logarithmic counter. */\n    uint16_t decrtime;  /* (Reduced precision) time of last decrement. */\n\n    /* Fields only useful for visualization. */\n    uint64_t hits;      /* Number of real accesses. */\n    time_t ctime;       /* Key creation time. */\n};\n\n#define to_16bit_minutes(x) ((x/60) & 65535)\n#define COUNTER_INIT_VAL 5\n\n/* Compute the difference in minutes between two 16 bit minutes times\n * obtained with to_16bit_minutes(). Since they can wrap around if\n * we detect the overflow we account for it as if the counter wrapped\n * a single time. */\nuint16_t minutes_diff(uint16_t now, uint16_t prev) {\n    if (now >= prev) return now-prev;\n    return 65535-prev+now;\n}\n\n/* Increment a couter logaritmically: the greatest is its value, the\n * less likely is that the counter is really incremented.\n * The maximum value of the counter is saturated at 255. */\nuint8_t log_incr(uint8_t counter) {\n    if (counter == 255) return counter;\n    double r = (double)rand()/RAND_MAX;\n    double baseval = counter-COUNTER_INIT_VAL;\n    if (baseval < 0) baseval = 0;\n    double limit = 1.0/(baseval*10+1);\n    if (r < limit) counter++;\n    return counter;\n}\n\n/* Simulate an access to an entry. */\nvoid access_entry(struct entry *e) {\n    e->counter = log_incr(e->counter);\n    e->hits++;\n}\n\n/* Return the entry LFU value and as a side effect decrement the\n * entry value if the decrement time was reached. */\nuint8_t scan_entry(struct entry *e) {\n    if (minutes_diff(to_16bit_minutes(time(NULL)),e->decrtime)\n        >= decr_every)\n    {\n        if (e->counter) {\n            if (e->counter > COUNTER_INIT_VAL*2) {\n                e->counter /= 2;\n            } else {\n                e->counter--;\n            }\n        }\n        e->decrtime = to_16bit_minutes(time(NULL));\n    }\n    return e->counter;\n}\n\n/* Print the entry info. */\nvoid show_entry(long pos, struct entry *e) {\n    char *tag = \"normal       \";\n\n    if (pos >= 10 && pos <= 14) tag = \"new no access\";\n    if (pos >= 15 && pos <= 19) tag = \"new accessed \";\n    if (pos >= keyspace_size -5) tag= \"old no access\";\n\n    printf(\"%ld] <%s> frequency:%d decrtime:%d [%lu hits | age:%ld sec]\\n\",\n        pos, tag, e->counter, e->decrtime, (unsigned long)e->hits,\n            time(NULL) - e->ctime);\n}\n\nint main(void) {\n    time_t start = time(NULL);\n    time_t new_entry_time = start;\n    time_t display_time = start;\n    struct entry *entries = malloc(sizeof(*entries)*keyspace_size);\n    long j;\n\n    /* Initialize. */\n    for (j = 0; j < keyspace_size; j++) {\n        entries[j].counter = COUNTER_INIT_VAL;\n        entries[j].decrtime = to_16bit_minutes(start);\n        entries[j].hits = 0;\n        entries[j].ctime = time(NULL);\n    }\n\n    while(1) {\n        time_t now = time(NULL);\n        long idx;\n\n        /* Scan N random entries (simulates the eviction under maxmemory). */\n        for (j = 0; j < 3; j++) {\n            scan_entry(entries+(rand()%keyspace_size));\n        }\n\n        /* Access a random entry: use a power-law access pattern up to\n         * 'switch_after' seconds. Then revert to flat access pattern. */\n        if (now-start < switch_after) {\n            /* Power law. */\n            idx = 1;\n            while((rand() % 21) != 0 && idx < keyspace_size) idx *= 2;\n            if (idx > keyspace_size) idx = keyspace_size;\n            idx = rand() % idx;\n        } else {\n            /* Flat. */\n            idx = rand() % keyspace_size;\n        }\n\n        /* Never access entries between position 10 and 14, so that\n         * we simulate what happens to new entries that are never\n         * accessed VS new entries which are accessed in positions\n         * 15-19.\n         *\n         * Also never access last 5 entry, so that we have keys which\n         * are never recreated (old), and never accessed. */\n        if ((idx < 10 || idx > 14) && (idx < keyspace_size-5))\n            access_entry(entries+idx);\n\n        /* Simulate the addition of new entries at positions between\n         * 10 and 19, a random one every 10 seconds. */\n        if (new_entry_time <= now) {\n            idx = 10+(rand()%10);\n            entries[idx].counter = COUNTER_INIT_VAL;\n            entries[idx].decrtime = to_16bit_minutes(time(NULL));\n            entries[idx].hits = 0;\n            entries[idx].ctime = time(NULL);\n            new_entry_time = now+10;\n        }\n\n        /* Show the first 20 entries and the last 20 entries. */\n        if (display_time != now) {\n            printf(\"=============================\\n\");\n            printf(\"Current minutes time: %d\\n\", (int)to_16bit_minutes(now));\n            printf(\"Access method: %s\\n\",\n                (now-start < switch_after) ? \"power-law\" : \"flat\");\n\n            for (j = 0; j < 20; j++)\n                show_entry(j,entries+j);\n\n            for (j = keyspace_size-20; j < keyspace_size; j++)\n                show_entry(j,entries+j);\n            display_time = now;\n        }\n    }\n    return 0;\n}\n\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/lru/test-lru.rb",
    "content": "require 'rubygems'\nrequire 'redis'\n\n$runs = []; # Remember the error rate of each run for average purposes.\n$o = {};    # Options set parsing arguments\n\ndef testit(filename)\n    r = Redis.new\n    r.config(\"SET\",\"maxmemory\",\"2000000\")\n    if $o[:ttl]\n        r.config(\"SET\",\"maxmemory-policy\",\"volatile-ttl\")\n    else\n        r.config(\"SET\",\"maxmemory-policy\",\"allkeys-lru\")\n    end\n    r.config(\"SET\",\"maxmemory-samples\",5)\n    r.config(\"RESETSTAT\")\n    r.flushall\n\n    html = \"\"\n    html << <<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    .otherdb {\n        border: 1px red solid;\n    }\n\n    .ex {\n        background-color: #666;\n    }\n    </style>\n    <pre>\nEOF\n\n    # Fill the DB up to the first eviction.\n    oldsize = r.dbsize\n    id = 0\n    while true\n        id += 1\n        begin\n            r.set(id,\"foo\")\n        rescue\n            break\n        end\n        newsize = r.dbsize\n        break if newsize == oldsize # A key was evicted? Stop.\n        oldsize = newsize\n    end\n\n    inserted = r.dbsize\n    first_set_max_id = id\n    html << \"#{r.dbsize} keys inserted.\\n\"\n\n    # Access keys sequentially, so that in theory the first part will be expired\n    # and the latter part will not, according to perfect LRU.\n\n    if $o[:ttl]\n        STDERR.puts \"Set increasing expire value\"\n        (1..first_set_max_id).each{|id|\n            r.expire(id,1000+id)\n            STDERR.print(\".\") if (id % 150) == 0\n        }\n    else\n        STDERR.puts \"Access keys sequentially\"\n        (1..first_set_max_id).each{|id|\n            r.get(id)\n            sleep 0.001\n            STDERR.print(\".\") if (id % 150) == 0\n        }\n    end\n    STDERR.puts\n\n    # Insert more 50% keys. We expect that the new keys will rarely be expired\n    # since their last access time is recent compared to the others.\n    #\n    # Note that we insert the first 100 keys of the new set into DB1 instead\n    # of DB0, so that we can try how cross-DB eviction works.\n    half = inserted/2\n    html << \"Insert enough keys to evict half the keys we inserted.\\n\"\n    add = 0\n\n    otherdb_start_idx = id+1\n    otherdb_end_idx = id+100\n    while true\n        add += 1\n        id += 1\n        if id >= otherdb_start_idx && id <= otherdb_end_idx\n            r.select(1)\n            r.set(id,\"foo\")\n            r.select(0)\n        else\n            r.set(id,\"foo\")\n        end\n        break if r.info['evicted_keys'].to_i >= half\n    end\n\n    html << \"#{add} additional keys added.\\n\"\n    html << \"#{r.dbsize} keys in DB.\\n\"\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\n    errors = 0\n    e = 1\n    error_per_key = 100000.0/first_set_max_id\n    half_set_size = first_set_max_id/2\n    maxerr = 0\n    (1..(first_set_max_id/2)).each{|id|\n        if id >= otherdb_start_idx && id <= otherdb_end_idx\n            r.select(1)\n            exists = r.exists(id)\n            r.select(0)\n        else\n            exists = r.exists(id)\n        end\n        if id < first_set_max_id/2\n            thiserr = error_per_key * ((half_set_size-id).to_f/half_set_size)\n            maxerr += thiserr\n            errors += thiserr if exists\n        elsif id >= first_set_max_id/2\n            thiserr = error_per_key * ((id-half_set_size).to_f/half_set_size)\n            maxerr += thiserr\n            errors += thiserr if !exists\n        end\n    }\n    errors = errors*100/maxerr\n\n    STDERR.puts \"Test finished with #{errors}% error! Generating HTML on stdout.\"\n\n    html << \"#{errors}% error!\\n\"\n    html << \"</pre>\"\n    $runs << errors\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 >= otherdb_start_idx && id <= otherdb_end_idx\n            c << \" otherdb\"\n        elsif id <= first_set_max_id\n            c << \" old\"\n        else\n            c << \" new\"\n        end\n\n        # Add class if exists\n        if id >= otherdb_start_idx && id <= otherdb_end_idx\n            r.select(1)\n            exists = r.exists(id)\n            r.select(0)\n        else\n            exists = r.exists(id)\n        end\n\n        c << \" ex\" if exists\n        html << \"<div title=\\\"#{id}\\\" class=\\\"#{c}\\\"></div>\"\n    }\n\n    # Close HTML page\n\n    html << <<EOF\n    </body>\n    </html>\nEOF\n\n    f = File.open(filename,\"w\")\n    f.write(html)\n    f.close\nend\n\ndef print_avg\n    avg = ($runs.reduce {|a,b| a+b}) / $runs.length\n    puts \"#{$runs.length} runs, AVG is #{avg}\"\nend\n\nif ARGV.length < 1\n    STDERR.puts \"Usage: ruby test-lru.rb <html-output-filename> [--runs <count>] [--ttl]\"\n    STDERR.puts \"Options:\"\n    STDERR.puts \"  --runs <count>    Execute the test <count> times.\"\n    STDERR.puts \"  --ttl             Set keys with increasing TTL values\"\n    STDERR.puts \"                    (starting from 1000 seconds) in order to\"\n    STDERR.puts \"                    test the volatile-lru policy.\"\n    exit 1\nend\n\nfilename = ARGV[0]\n$o[:numruns] = 1\n\n# Options parsing\ni = 1\nwhile i < ARGV.length\n    if ARGV[i] == '--runs'\n        $o[:numruns] = ARGV[i+1].to_i\n        i+= 1\n    elsif ARGV[i] == '--ttl'\n        $o[:ttl] = true\n    else\n        STDERR.puts \"Unknown option #{ARGV[i]}\"\n        exit 1\n    end\n    i+= 1\nend\n\n$o[:numruns].times {\n    testit(filename)\n    print_avg if $o[:numruns] != 1\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/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\n### BEGIN INIT INFO\n# Provides:     redis_6379\n# Default-Start:        2 3 4 5\n# Default-Stop:         0 1 6\n# Short-Description:    Redis data structure server\n# Description:          Redis data structure server. See https://redis.io\n### END INIT INFO\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": "Chapter02/redis-6.0.3/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        PID=$(cat $PIDFILE)\n        if [ ! -x /proc/${PID} ]\n        then\n            echo 'Redis is not running'\n        else\n            echo \"Redis is running ($PID)\"\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": "Chapter02/redis-6.0.3/utils/releasetools/01_create_tarball.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}\"\ncd ~/hack/redis\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": "Chapter02/redis-6.0.3/utils/releasetools/02_upload_tarball.sh",
    "content": "#!/bin/bash\necho \"Uploading...\"\nscp /tmp/redis-${1}.tar.gz antirez@antirez.com:/var/virtual/download.redis.io/httpdocs/releases/\necho \"Updating web site... (press any key if it is a stable release, or Ctrl+C)\"\nread x\nssh antirez@antirez.com \"cd /var/virtual/download.redis.io/httpdocs; ./update.sh ${1}\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/releasetools/03_test_release.sh",
    "content": "#!/bin/sh\nif [ $# != \"1\" ]\nthen\n    echo \"Usage: ${0} <git-ref>\"\n    exit 1\nfi\n\nTAG=$1\nTARNAME=\"redis-${TAG}.tar.gz\"\nDOWNLOADURL=\"http://download.redis.io/releases/${TARNAME}\"\n\nssh antirez@metal \"export TERM=xterm;\n                   cd /tmp;\n                   rm -rf test_release_tmp_dir;\n                   cd test_release_tmp_dir;\n                   rm -f $TARNAME;\n                   rm -rf redis-${TAG};\n                   wget $DOWNLOADURL;\n                   tar xvzf $TARNAME;\n                   cd redis-${TAG};\n                   make;\n                   ./runtest;\n                   ./runtest-sentinel;\n                   if [ -x runtest-cluster ]; then\n                       ./runtest-cluster;\n                   fi\"\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/releasetools/04_release_hash.sh",
    "content": "#!/bin/bash\nSHA=$(curl -s http://download.redis.io/releases/redis-${1}.tar.gz | shasum -a 256 | cut -f 1 -d' ')\nENTRY=\"hash redis-${1}.tar.gz sha256 $SHA http://download.redis.io/releases/redis-${1}.tar.gz\"\necho $ENTRY >> ~/hack/redis-hashes/README\nvi ~/hack/redis-hashes/README\necho \"Press any key to commit, Ctrl-C to abort).\"\nread yes\n(cd ~/hack/redis-hashes; git commit -a -m \"${1} hash.\"; git push)\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/releasetools/changelog.tcl",
    "content": "#!/usr/bin/env tclsh\n\nif {[llength $::argv] != 2 && [llength $::argv] != 3} {\n    puts \"Usage: $::argv0 <branch> <version> \\[<num-commits>\\]\"\n    exit 1\n}\n\nset branch [lindex $::argv 0]\nset ver [lindex $::argv 1]\nif {[llength $::argv] == 3} {\n    set count [lindex ::$argv 2]\n} else {\n    set count 100\n}\n\nset template {\n================================================================================\nRedis %ver%     Released %date%\n================================================================================\n\nUpgrade urgency <URGENCY>: <DESCRIPTION>\n}\n\nset template [string trim $template]\nappend template \"\\n\\n\"\nset date [clock format [clock seconds]]\nset template [string map [list %ver% $ver %date% $date] $template]\n\nappend template [exec git log $branch~$count..$branch \"--format=format:%an in commit %h:%n %s\" --shortstat]\n\n#Older, more verbose version.\n#\n#append template [exec git log $branch~30..$branch \"--format=format:+-------------------------------------------------------------------------------%n| %s%n| By %an, %ai%n+--------------------------------------------------------------------------------%nhttps://github.com/antirez/redis/commit/%H%n%n%b\" --stat]\n\nputs $template\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/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": "Chapter02/redis-6.0.3/utils/srandmember/README.md",
    "content": "The utilities in this directory plot the distribution of SRANDMEMBER to\nevaluate how fair it is.\n\nSee http://theshfl.com/redis_sets for more information on the topic that lead\nto such investigation fix.\n\nshowdist.rb -- shows the distribution of the frequency elements are returned.\n               The x axis is the number of times elements were returned, and\n               the y axis is how many elements were returned with such\n               frequency.\n\nshowfreq.rb -- shows the frequency each element was returned.\n               The x axis is the element number.\n               The y axis is the times it was returned.\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/srandmember/showdist.rb",
    "content": "require 'redis'\n\nr = Redis.new\nr.select(9)\nr.del(\"myset\");\nr.sadd(\"myset\",(0..999).to_a)\nfreq = {}\n100.times {\n    res = r.pipelined {\n        1000.times {\n            r.srandmember(\"myset\")\n        }\n    }\n    res.each{|ele|\n        freq[ele] = 0 if freq[ele] == nil\n        freq[ele] += 1\n    }\n}\n\n# Convert into frequency distribution\ndist = {}\nfreq.each{|item,count|\n    dist[count] = 0 if dist[count] == nil\n    dist[count] += 1\n}\n\nmin = dist.keys.min\nmax = dist.keys.max\n(min..max).each{|x|\n    count = dist[x]\n    count = 0 if count == nil\n    puts \"#{x} -> #{\"*\"*count}\"\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/srandmember/showfreq.rb",
    "content": "require 'redis'\n\nr = Redis.new\nr.select(9)\nr.del(\"myset\");\nr.sadd(\"myset\",(0..999).to_a)\nfreq = {}\n500.times {\n    res = r.pipelined {\n        1000.times {\n            r.srandmember(\"myset\")\n        }\n    }\n    res.each{|ele|\n        freq[ele] = 0 if freq[ele] == nil\n        freq[ele] += 1\n    }\n}\n\n# Print the frequency each element was yeld to process it with gnuplot\nfreq.each{|item,count|\n    puts \"#{item} #{count}\"\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/systemd-redis_multiple_servers@.service",
    "content": "# example systemd template service unit file for multiple redis-servers\n#\n# You can use this file as a blueprint for your actual template service unit\n# file, if you intend to run multiple independent redis-server instances in\n# parallel using systemd's \"template unit files\" feature. If you do, you will\n# want to choose a better basename for your service unit by renaming this file\n# when copying it.\n#\n# Please take a look at the provided \"systemd-redis_server.service\" example\n# service unit file, too, if you choose to use this approach at managing\n# multiple redis-server instances via systemd.\n\n[Unit]\nDescription=Redis data structure server - instance %i\nDocumentation=https://redis.io/documentation\n# This template unit assumes your redis-server configuration file(s)\n# to live at /etc/redis/redis_server_<INSTANCE_NAME>.conf\nAssertPathExists=/etc/redis/redis_server_%i.conf\n#Before=your_application.service another_example_application.service\n#AssertPathExists=/var/lib/redis\n\n[Service]\nExecStart=/usr/local/bin/redis-server /etc/redis/redis_server_%i.conf\nLimitNOFILE=10032\nNoNewPrivileges=yes\n#OOMScoreAdjust=-900\n#PrivateTmp=yes\nType=notify\nTimeoutStartSec=infinity\nTimeoutStopSec=infinity\nUMask=0077\n#User=redis\n#Group=redis\n#WorkingDirectory=/var/lib/redis\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/systemd-redis_server.service",
    "content": "# example systemd service unit file for redis-server\n#\n# In order to use this as a template for providing a redis service in your\n# environment, _at the very least_ make sure to adapt the redis configuration\n# file you intend to use as needed (make sure to set \"supervised systemd\"), and\n# to set sane TimeoutStartSec and TimeoutStopSec property values in the unit's\n# \"[Service]\" section to fit your needs.\n#\n# Some properties, such as User= and Group=, are highly desirable for virtually\n# all deployments of redis, but cannot be provided in a manner that fits all\n# expectable environments. Some of these properties have been commented out in\n# this example service unit file, but you are highly encouraged to set them to\n# fit your needs.\n#\n# Please refer to systemd.unit(5), systemd.service(5), and systemd.exec(5) for\n# more information.\n\n[Unit]\nDescription=Redis data structure server\nDocumentation=https://redis.io/documentation\n#Before=your_application.service another_example_application.service\n#AssertPathExists=/var/lib/redis\n\n[Service]\nExecStart=/usr/local/bin/redis-server --supervised systemd --daemonize no\n## Alternatively, have redis-server load a configuration file:\n#ExecStart=/usr/local/bin/redis-server /path/to/your/redis.conf\nLimitNOFILE=10032\nNoNewPrivileges=yes\n#OOMScoreAdjust=-900\n#PrivateTmp=yes\nType=notify\nTimeoutStartSec=infinity\nTimeoutStopSec=infinity\nUMask=0077\n#User=redis\n#Group=redis\n#WorkingDirectory=/var/lib/redis\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/tracking_collisions.c",
    "content": "/* This is a small program used in order to understand the collison rate\n * of CRC64 (ISO version) VS other stronger hashing functions in the context\n * of hashing keys for the Redis \"tracking\" feature (client side caching\n * assisted by the server).\n *\n * The program attempts to hash keys with common names in the form of\n *\n *  prefix:<counter>\n *\n * And counts the resulting collisons generated in the 24 bits of output\n * needed for the tracking feature invalidation table (16 millions + entries)\n *\n * Compile with:\n *\n *  cc -O2 ./tracking_collisions.c ../src/crc64.c ../src/sha1.c\n *  ./a.out\n *\n * --------------------------------------------------------------------------\n *\n * Copyright (C) 2019 Salvatore Sanfilippo\n * This code is released under the BSD 2 clause license.\n */\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdio.h>\n#include \"../src/crc64.h\"\n#include \"../src/sha1.h\"\n\n#define TABLE_SIZE (1<<24)\nint Table[TABLE_SIZE];\n\nuint64_t crc64Hash(char *key, size_t len) {\n    return crc64(0,(unsigned char*)key,len);\n}\n\nuint64_t sha1Hash(char *key, size_t len) {\n    SHA1_CTX ctx;\n    unsigned char hash[20];\n\n    SHA1Init(&ctx);\n    SHA1Update(&ctx,(unsigned char*)key,len);\n    SHA1Final(hash,&ctx);\n    uint64_t hash64;\n    memcpy(&hash64,hash,sizeof(hash64));\n    return hash64;\n}\n\n/* Test the hashing function provided as callback and return the\n * number of collisions found. */\nunsigned long testHashingFunction(uint64_t (*hash)(char *, size_t)) {\n    unsigned long collisions = 0;\n    memset(Table,0,sizeof(Table));\n    char *prefixes[] = {\"object\", \"message\", \"user\", NULL};\n    for (int i = 0; prefixes[i] != NULL; i++) {\n        for (int j = 0; j < TABLE_SIZE/2; j++) {\n            char keyname[128];\n            size_t keylen = snprintf(keyname,sizeof(keyname),\"%s:%d\",\n                                     prefixes[i],j);\n            uint64_t bucket = hash(keyname,keylen) % TABLE_SIZE;\n            if (Table[bucket]) {\n                collisions++;\n            } else {\n                Table[bucket] = 1;\n            }\n        }\n    }\n    return collisions;\n}\n\nint main(void) {\n    printf(\"SHA1 : %lu\\n\", testHashingFunction(sha1Hash));\n    printf(\"CRC64: %lu\\n\", testHashingFunction(crc64Hash));\n    return 0;\n}\n"
  },
  {
    "path": "Chapter02/redis-6.0.3/utils/whatisdoing.sh",
    "content": "# This script is from http://poormansprofiler.org/\n#\n# NOTE: Instead of using this script, you should use the Redis\n# Software Watchdog, which provides a similar functionality but in\n# a more reliable / easy to use way.\n#\n# Check http://redis.io/topics/latency for more information.\n\n#!/bin/bash\nnsamples=1\nsleeptime=0\npid=$(ps auxww | grep '[r]edis-server' | awk '{print $2}')\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"
  },
  {
    "path": "Chapter03/code/SharedMutex.cpp",
    "content": "/**\r\n * SharedMutex.cpp\r\n * zhangyl 20191108\r\n */\r\n\r\n#include \"SharedMutex.h\"\r\n\r\nSharedMutex::SharedMutex()\r\n{\r\n#ifdef WIN32\r\n    ::InitializeSRWLock(&m_SRWLock);\r\n#else\r\n    ::pthread_rwlock_init(&m_SRWLock, nullptr);\r\n#endif   \r\n}\r\n\r\nSharedMutex::~SharedMutex()\r\n{\r\n#ifdef WIN32\r\n    //Windows϶дҪʽ\r\n#else\r\n    ::pthread_rwlock_destroy(&m_SRWLock);\r\n#endif\r\n}\r\n\r\nvoid SharedMutex::acquireReadLock()\r\n{\r\n#ifdef WIN32\r\n    ::AcquireSRWLockShared(&m_SRWLock);\r\n#else\r\n    ::pthread_rwlock_rdlock(&m_SRWLock);\r\n#endif   \r\n}\r\n\r\nvoid SharedMutex::acquireWriteLock()\r\n{\r\n#ifdef WIN32\r\n    ::AcquireSRWLockExclusive(&m_SRWLock);\r\n#else\r\n    ::pthread_rwlock_wrlock(&m_SRWLock);\r\n#endif   \r\n}\r\n\r\nvoid SharedMutex::unlockReadLock()\r\n{\r\n#ifdef WIN32   \r\n    ::ReleaseSRWLockShared(&m_SRWLock);   \r\n#else\r\n    ::pthread_rwlock_unlock(&m_SRWLock);\r\n#endif\r\n}\r\n\r\nvoid SharedMutex::unlockWriteLock()\r\n{\r\n#ifdef WIN32  \r\n    ::ReleaseSRWLockExclusive(&m_SRWLock);\r\n#else\r\n    ::pthread_rwlock_unlock(&m_SRWLock);\r\n#endif\r\n}\r\n\r\nSharedLockGuard::SharedLockGuard(SharedMutex& sharedMutex) :\r\n    m_SharedMutex(sharedMutex)\r\n{\r\n    m_SharedMutex.acquireReadLock();\r\n}\r\n\r\nSharedLockGuard::~SharedLockGuard()\r\n{\r\n    m_SharedMutex.unlockReadLock();\r\n}\r\n\r\nUniqueLockGuard::UniqueLockGuard(SharedMutex& sharedMutex) :\r\n    m_SharedMutex(sharedMutex)\r\n{\r\n    m_SharedMutex.acquireWriteLock();\r\n}\r\n\r\nUniqueLockGuard::~UniqueLockGuard()\r\n{\r\n    m_SharedMutex.unlockWriteLock();\r\n}\r\n"
  },
  {
    "path": "Chapter03/code/SharedMutex.h",
    "content": "/** \r\n * SharedMutex.h C++11 ûstd::shared_mutex, Լʵһ\r\n * zhangyl 20191108\r\n */\r\n\r\n#ifndef __SHARED_MUTEX_H__\r\n#define __SHARED_MUTEX_H__\r\n\r\n#ifdef WIN32\r\n#include <Windows.h>\r\n#else\r\n#include \r\n#include <pthread.h>\r\n#endif\r\n\r\nclass SharedMutex final\r\n{\r\npublic:\r\n    SharedMutex();\r\n    ~SharedMutex();\r\n\r\n    void acquireReadLock();\r\n    void acquireWriteLock();\r\n    void unlockReadLock();\r\n    void unlockWriteLock();\r\n\r\nprivate:\r\n    SharedMutex(const SharedMutex& rhs) = delete;\r\n    SharedMutex& operator =(const SharedMutex& rhs) = delete;\r\n\r\nprivate:\r\n#ifdef WIN32\r\n    SRWLOCK             m_SRWLock;\r\n#else\r\n    pthread_rwlock_t    m_SRWLock;\r\n#endif   \r\n};\r\n\r\nclass SharedLockGuard final\r\n{\r\npublic:\r\n    SharedLockGuard(SharedMutex& sharedMutex);\r\n    ~SharedLockGuard();\r\n\r\nprivate:\r\n    SharedLockGuard(const SharedLockGuard& rhs) = delete;\r\n    SharedLockGuard operator=(const SharedLockGuard& rhs) = delete;\r\n\r\nprivate:\r\n    SharedMutex&        m_SharedMutex;\r\n};\r\n\r\nclass UniqueLockGuard final\r\n{\r\npublic:\r\n    UniqueLockGuard(SharedMutex& sharedMutex);\r\n    ~UniqueLockGuard();\r\n\r\nprivate:\r\n    UniqueLockGuard(const UniqueLockGuard& rhs) = delete;\r\n    UniqueLockGuard operator=(const UniqueLockGuard& rhs) = delete;\r\n\r\nprivate:\r\n    SharedMutex& m_SharedMutex;\r\n};\r\n\r\n\r\n#endif //!__SHARED_MUTEX_H__\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/Resource.h",
    "content": "//{{NO_DEPENDENCIES}}\r\n// Microsoft Visual C++ generated include file.\r\n// Used by SingleInstance.rc\r\n//\r\n\r\n#define IDS_APP_TITLE\t\t\t103\r\n\r\n#define IDR_MAINFRAME\t\t\t128\r\n#define IDD_SINGLEINSTANCE_DIALOG\t102\r\n#define IDD_ABOUTBOX\t\t\t103\r\n#define IDM_ABOUT\t\t\t\t104\r\n#define IDM_EXIT\t\t\t\t105\r\n#define IDI_SINGLEINSTANCE\t\t\t107\r\n#define IDI_SMALL\t\t\t\t108\r\n#define IDC_SINGLEINSTANCE\t\t\t109\r\n#define IDC_MYICON\t\t\t\t2\r\n#ifndef IDC_STATIC\r\n#define IDC_STATIC\t\t\t\t-1\r\n#endif\r\n// Next default values for new objects\r\n//\r\n#ifdef APSTUDIO_INVOKED\r\n#ifndef APSTUDIO_READONLY_SYMBOLS\r\n\r\n#define _APS_NO_MFC\t\t\t\t\t130\r\n#define _APS_NEXT_RESOURCE_VALUE\t129\r\n#define _APS_NEXT_COMMAND_VALUE\t\t32771\r\n#define _APS_NEXT_CONTROL_VALUE\t\t1000\r\n#define _APS_NEXT_SYMED_VALUE\t\t110\r\n#endif\r\n#endif\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/SingleInstance.cpp",
    "content": "// SingleInstance.cpp : Defines the entry point for the application.\r\n//\r\n\r\n#include \"stdafx.h\"\r\n#include \"SingleInstance.h\"\r\n\r\n#define MAX_LOADSTRING 100\r\n\r\n// Global Variables:\r\nHINSTANCE hInst;\t\t\t\t\t\t\t\t// current instance\r\nTCHAR szTitle[MAX_LOADSTRING];\t\t\t\t\t// The title bar text\r\nTCHAR szWindowClass[MAX_LOADSTRING];\t\t\t// the main window class name\r\n\r\n// Forward declarations of functions included in this code module:\r\nATOM\t\t\t\tMyRegisterClass(HINSTANCE hInstance);\r\nBOOL\t\t\t\tInitInstance(HINSTANCE, int);\r\nLRESULT CALLBACK\tWndProc(HWND, UINT, WPARAM, LPARAM);\r\nINT_PTR CALLBACK\tAbout(HWND, UINT, WPARAM, LPARAM);\r\n\r\nbool CheckInstance()\r\n{\r\n    HANDLE hSingleInstanceMutex = CreateMutex(NULL, FALSE, _T(\"MySingleInstanceApp\"));\r\n    if (hSingleInstanceMutex != NULL)\r\n    {\r\n        if (GetLastError() == ERROR_ALREADY_EXISTS)\r\n        {\r\n            return true;\r\n        }\r\n    }\r\n\r\n    return false;\r\n}\r\n\r\nint APIENTRY _tWinMain(_In_ HINSTANCE hInstance,\r\n                     _In_opt_ HINSTANCE hPrevInstance,\r\n                     _In_ LPTSTR    lpCmdLine,\r\n                     _In_ int       nCmdShow)\r\n{\r\n\tUNREFERENCED_PARAMETER(hPrevInstance);\r\n\tUNREFERENCED_PARAMETER(lpCmdLine);\r\n\r\n    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);\r\n    LoadString(hInstance, IDC_SINGLEINSTANCE, szWindowClass, MAX_LOADSTRING);\r\n\r\n    if (CheckInstance())\r\n    {\r\n        HWND hwndPre = FindWindow(szWindowClass, NULL);\r\n        if (IsWindow(hwndPre))\r\n        {\r\n            if (::IsIconic(hwndPre))\r\n                ::SendMessage(hwndPre, WM_SYSCOMMAND, SC_RESTORE | HTCAPTION, 0);\r\n\r\n            ::SetWindowPos(hwndPre, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);\r\n            ::SetForegroundWindow(hwndPre);\r\n            ::SetFocus(hwndPre);\r\n            return 0;\r\n        }\r\n    }\r\n\r\n \t// TODO: Place code here.\r\n\tMSG msg;\r\n\tHACCEL hAccelTable;\r\n\r\n\t// Initialize global strings\r\n\t\r\n\tMyRegisterClass(hInstance);\r\n\r\n\t// Perform application initialization:\r\n\tif (!InitInstance (hInstance, nCmdShow))\r\n\t{\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\thAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SINGLEINSTANCE));\r\n\r\n\t// Main message loop:\r\n\twhile (GetMessage(&msg, NULL, 0, 0))\r\n\t{\r\n\t\tif (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))\r\n\t\t{\r\n\t\t\tTranslateMessage(&msg);\r\n\t\t\tDispatchMessage(&msg);\r\n\t\t}\r\n\t}\r\n\r\n\treturn (int) msg.wParam;\r\n}\r\n\r\n\r\n\r\n//\r\n//  FUNCTION: MyRegisterClass()\r\n//\r\n//  PURPOSE: Registers the window class.\r\n//\r\nATOM MyRegisterClass(HINSTANCE hInstance)\r\n{\r\n\tWNDCLASSEX wcex;\r\n\r\n\twcex.cbSize = sizeof(WNDCLASSEX);\r\n\r\n\twcex.style\t\t\t= CS_HREDRAW | CS_VREDRAW;\r\n\twcex.lpfnWndProc\t= WndProc;\r\n\twcex.cbClsExtra\t\t= 0;\r\n\twcex.cbWndExtra\t\t= 0;\r\n\twcex.hInstance\t\t= hInstance;\r\n\twcex.hIcon\t\t\t= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SINGLEINSTANCE));\r\n\twcex.hCursor\t\t= LoadCursor(NULL, IDC_ARROW);\r\n\twcex.hbrBackground\t= (HBRUSH)(COLOR_WINDOW+1);\r\n\twcex.lpszMenuName\t= MAKEINTRESOURCE(IDC_SINGLEINSTANCE);\r\n\twcex.lpszClassName\t= szWindowClass;\r\n\twcex.hIconSm\t\t= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));\r\n\r\n\treturn RegisterClassEx(&wcex);\r\n}\r\n\r\n//\r\n//   FUNCTION: InitInstance(HINSTANCE, int)\r\n//\r\n//   PURPOSE: Saves instance handle and creates main window\r\n//\r\n//   COMMENTS:\r\n//\r\n//        In this function, we save the instance handle in a global variable and\r\n//        create and display the main program window.\r\n//\r\nBOOL InitInstance(HINSTANCE hInstance, int nCmdShow)\r\n{\r\n   HWND hWnd;\r\n\r\n   hInst = hInstance; // Store instance handle in our global variable\r\n\r\n   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,\r\n      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);\r\n\r\n   if (!hWnd)\r\n   {\r\n      return FALSE;\r\n   }\r\n\r\n   ShowWindow(hWnd, nCmdShow);\r\n   UpdateWindow(hWnd);\r\n\r\n   return TRUE;\r\n}\r\n\r\n//\r\n//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)\r\n//\r\n//  PURPOSE:  Processes messages for the main window.\r\n//\r\n//  WM_COMMAND\t- process the application menu\r\n//  WM_PAINT\t- Paint the main window\r\n//  WM_DESTROY\t- post a quit message and return\r\n//\r\n//\r\nLRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r\n{\r\n\tint wmId, wmEvent;\r\n\tPAINTSTRUCT ps;\r\n\tHDC hdc;\r\n\r\n\tswitch (message)\r\n\t{\r\n\tcase WM_COMMAND:\r\n\t\twmId    = LOWORD(wParam);\r\n\t\twmEvent = HIWORD(wParam);\r\n\t\t// Parse the menu selections:\r\n\t\tswitch (wmId)\r\n\t\t{\r\n\t\tcase IDM_ABOUT:\r\n\t\t\tDialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);\r\n\t\t\tbreak;\r\n\t\tcase IDM_EXIT:\r\n\t\t\tDestroyWindow(hWnd);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\treturn DefWindowProc(hWnd, message, wParam, lParam);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase WM_PAINT:\r\n\t\thdc = BeginPaint(hWnd, &ps);\r\n\t\t// TODO: Add any drawing code here...\r\n\t\tEndPaint(hWnd, &ps);\r\n\t\tbreak;\r\n\tcase WM_DESTROY:\r\n\t\tPostQuitMessage(0);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\treturn DefWindowProc(hWnd, message, wParam, lParam);\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\n// Message handler for about box.\r\nINT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r\n{\r\n\tUNREFERENCED_PARAMETER(lParam);\r\n\tswitch (message)\r\n\t{\r\n\tcase WM_INITDIALOG:\r\n\t\treturn (INT_PTR)TRUE;\r\n\r\n\tcase WM_COMMAND:\r\n\t\tif (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)\r\n\t\t{\r\n\t\t\tEndDialog(hDlg, LOWORD(wParam));\r\n\t\t\treturn (INT_PTR)TRUE;\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\treturn (INT_PTR)FALSE;\r\n}\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/SingleInstance.h",
    "content": "#pragma once\r\n\r\n#include \"resource.h\"\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/SingleInstance.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.40629.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"SingleInstance\", \"SingleInstance.vcxproj\", \"{83BFFE4B-7CE2-4D8D-873D-C5CF8E353B85}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{83BFFE4B-7CE2-4D8D-873D-C5CF8E353B85}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{83BFFE4B-7CE2-4D8D-873D-C5CF8E353B85}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{83BFFE4B-7CE2-4D8D-873D-C5CF8E353B85}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{83BFFE4B-7CE2-4D8D-873D-C5CF8E353B85}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/SingleInstance.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{83BFFE4B-7CE2-4D8D-873D-C5CF8E353B85}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>SingleInstance</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"Resource.h\" />\r\n    <ClInclude Include=\"SingleInstance.h\" />\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"SingleInstance.cpp\" />\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"SingleInstance.rc\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"SingleInstance.ico\" />\r\n    <Image Include=\"small.ico\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter03/code/SingleInstance/SingleInstance.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Resource.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"SingleInstance.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"SingleInstance.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"SingleInstance.rc\">\r\n      <Filter>Resource Files</Filter>\r\n    </ResourceCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"small.ico\">\r\n      <Filter>Resource Files</Filter>\r\n    </Image>\r\n    <Image Include=\"SingleInstance.ico\">\r\n      <Filter>Resource Files</Filter>\r\n    </Image>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter03/code/SingleInstance/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// SingleInstance.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\r\n// Windows Header Files:\r\n#include <windows.h>\r\n\r\n// C RunTime Header Files\r\n#include <stdlib.h>\r\n#include <malloc.h>\r\n#include <memory.h>\r\n#include <tchar.h>\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter03/code/SingleInstance/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "Chapter03/code/TaskPool.cpp",
    "content": "/**\r\n * ģͣTaskPool.cpp\r\n * zhangyl 2019.02.14\r\n */\r\n#include \"TaskPool.h\"\r\n\r\nTaskPool::TaskPool() : m_bRunning(false)\r\n{\r\n\r\n}\r\n\r\nTaskPool::~TaskPool()\r\n{\r\n    removeAllTasks();\r\n}\r\n\r\nvoid TaskPool::init(int threadNum/* = 5*/)\r\n{\r\n    if (threadNum <= 0)\r\n        threadNum = 5;\r\n\r\n    m_bRunning = true;\r\n\r\n    for (int i = 0; i < threadNum; ++i)\r\n    {\r\n        std::shared_ptr<std::thread> spThread;\r\n        spThread.reset(new std::thread(std::bind(&TaskPool::threadFunc, this)));\r\n        m_threads.push_back(spThread);\r\n    }\r\n}\r\n\r\nvoid TaskPool::threadFunc()\r\n{\r\n    std::shared_ptr<Task> spTask;\r\n    while (true)\r\n    {\r\n        std::unique_lock<std::mutex> guard(m_mutexList);\r\n        while (m_taskList.empty())\r\n        {                 \r\n            if (!m_bRunning)\r\n                break;\r\n            \r\n            //˻ʵĻpthread_cond_waitͷִС\r\n            //仯ʣpthread_cond_waitֱӻ\r\n            m_cv.wait(guard);\r\n        }\r\n\r\n        if (!m_bRunning)\r\n            break;\r\n\r\n        spTask = m_taskList.front();\r\n        m_taskList.pop_front();\r\n\r\n        if (spTask == NULL)\r\n            continue;\r\n\r\n        spTask->doIt();\r\n        spTask.reset();\r\n    }\r\n\r\n    std::cout << \"exit thread, threadID: \" << std::this_thread::get_id() << std::endl;\r\n}\r\n\r\nvoid TaskPool::stop()\r\n{\r\n    m_bRunning = false;\r\n    m_cv.notify_all();\r\n\r\n    //ȴ߳˳\r\n    for (auto& iter : m_threads)\r\n    {\r\n        if (iter->joinable())\r\n            iter->join();\r\n    }\r\n}\r\n\r\nvoid TaskPool::addTask(Task* task)\r\n{\r\n    std::shared_ptr<Task> spTask;\r\n    spTask.reset(task);\r\n\r\n    {\r\n        std::lock_guard<std::mutex> guard(m_mutexList);       \r\n        //m_taskList.push_back(std::make_shared<Task>(task));\r\n        m_taskList.push_back(spTask);\r\n        std::cout << \"add a Task.\" << std::endl;\r\n    }\r\n    \r\n    m_cv.notify_one();\r\n}\r\n\r\nvoid TaskPool::removeAllTasks()\r\n{\r\n    {\r\n        std::lock_guard<std::mutex> guard(m_mutexList);\r\n        for (auto& iter : m_taskList)\r\n        {\r\n            iter.reset();\r\n        }\r\n        m_taskList.clear();\r\n    }\r\n}"
  },
  {
    "path": "Chapter03/code/TaskPool.h",
    "content": "/** \r\n * ģͣTaskPool.h\r\n * zhangyl 2019.02.14\r\n */\r\n#include <thread>\r\n#include <mutex>\r\n#include <condition_variable>\r\n#include <list>\r\n#include <vector>\r\n#include <memory>\r\n#include <iostream>\r\n\r\nclass Task\r\n{\r\npublic:\r\n    virtual void doIt()\r\n    {\r\n        std::cout << \"handle a task...\" << std::endl;\r\n    }\r\n\r\n    virtual ~Task()\r\n    {\r\n        //Ϊ˿һtask٣ⲹ\r\n        std::cout << \"a task destructed...\" << std::endl;\r\n    }\r\n};\r\n\r\nclass TaskPool final\r\n{\r\npublic:\r\n    TaskPool();\r\n    ~TaskPool();\r\n    TaskPool(const TaskPool& rhs) = delete;\r\n    TaskPool& operator=(const TaskPool& rhs) = delete;\r\n\r\npublic:\r\n    void init(int threadNum = 5);\r\n    void stop();\r\n\r\n    void addTask(Task* task);\r\n    void removeAllTasks();\r\n\r\nprivate:\r\n    void threadFunc();\r\n\r\nprivate:\r\n    std::list<std::shared_ptr<Task>>            m_taskList;\r\n    std::mutex                                  m_mutexList;\r\n    std::condition_variable                     m_cv;\r\n    bool                                        m_bRunning;\r\n    std::vector<std::shared_ptr<std::thread>>   m_threads;\r\n};"
  },
  {
    "path": "Chapter03/code/TestFiber.cpp",
    "content": "#include \"stdafx.h\"\r\n#include <Windows.h>\r\n#include <string>\r\n\r\nchar g_szTime[64] = { \"time not set...\" };\r\nLPVOID mainWorkerFiber = NULL;\r\n\r\nvoid WINAPI workerFiberProc(LPVOID lpFiberParameter)\r\n{\r\n    while (true)\r\n    {\r\n        //һܺʱĲ\r\n        SYSTEMTIME st;\r\n        GetLocalTime(&st);\r\n        wsprintfA(g_szTime, \"%04d-%02d-%02d %02d:%02d:%02d\", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);\r\n        printf(\"%s\\n\", g_szTime);\r\n\r\n        //л˳\r\n        //SwitchToFiber(mainWorkerFiber);\r\n    }  \r\n}\r\n\r\nint main()\r\n{\r\n    mainWorkerFiber = ConvertThreadToFiber(NULL);\r\n    \r\n    int index = 0;\r\n    while (index < 100)\r\n    {\r\n        ++index;\r\n                 \r\n        LPVOID pWorkerFiber = CreateFiber(0, workerFiberProc, NULL);\r\n        if (pWorkerFiber == NULL)\r\n            return -1;\r\n        //лµ˳\r\n        SwitchToFiber(pWorkerFiber);\r\n\r\n        memset(g_szTime, 0, sizeof(g_szTime));\r\n        strncpy(g_szTime, \"time not set...\", strlen(\"time not set...\"));\r\n        \r\n\r\n        printf(\"%s\\n\", g_szTime);\r\n        \r\n        Sleep(1000);      \r\n    }\r\n\r\n    //л߳ģʽ\r\n    ConvertFiberToThread();\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter03/code/TestSharedMutexBenchmark.cpp",
    "content": "/**\n * std::shared_mutex与std::mutex的性能对比\n * zhangyl 2016.11.10\n */\n\n //读线程数量\n#define READER_THREAD_COUNT  8\n//最大循环次数\n#define LOOP_COUNT           5000000\n\n#include <iostream>\n#include <mutex>  \n#include <shared_mutex>\n#include <thread>\n\nclass shared_mutex_counter\n{\npublic:\n\tshared_mutex_counter() = default;\n\t~shared_mutex_counter() = default;\n\n\t//使用std::shared_mutex，同一时刻多个读线程可以同时访问value_值\n\tunsigned int get() const\n\t{\n\t\t//注意：这里使用std::shared_lock\n\t\tstd::shared_lock<std::shared_mutex> lock(mutex_);\n\t\treturn value_;\n\t}\n\n\t//使用std::shared_mutex，同一个时刻仅有一个写线程可以修改value_值\n\tvoid increment()\n\t{\n\t\t//注意：这里使用std::unique_lock\n\t\tstd::unique_lock<std::shared_mutex> lock(mutex_);\n\t\tvalue_++;\n\t}\n\n\t//使用std::shared_mutex，同一个时刻仅有一个写线程可以重置value_值\n\tvoid reset()\n\t{\n\t\t//注意：这里使用std::unique_lock\n\t\tstd::unique_lock<std::shared_mutex> lock(mutex_);\n\t\tvalue_ = 0;\n\t}\n\nprivate:\n\tmutable std::shared_mutex   mutex_;\n\t//value_是多个线程的共享资源\n\tunsigned int                value_ = 0;\n};\n\nclass mutex_counter\n{\npublic:\n\tmutex_counter() = default;\n\t~mutex_counter() = default;\n\n\t//使用std::mutex，同一时刻仅有一个线程可以访问value_的值\n\tunsigned int get() const\n\t{\n\t\tstd::unique_lock<std::mutex> lk(mutex_);\n\t\treturn value_;\n\t}\n\n\t//使用std::mutex，同一时刻仅有一个线程可以修改value_的值\n\tvoid increment()\n\t{\n\t\tstd::unique_lock<std::mutex> lk(mutex_);\n\t\tvalue_++;\n\t}\n\nprivate:\n\tmutable std::mutex      mutex_;\n\t//value_是多个线程的共享资源\n\tunsigned int            value_ = 0;\n};\n\n//测试std::shared_mutex\nvoid test_shared_mutex()\n{\n\tshared_mutex_counter counter;\n\tint temp;\n\n\t//写线程函数\n\tauto writer = [&counter]() {\n\t\tfor (int i = 0; i < LOOP_COUNT; i++)\n\t\t{\n\t\t\tcounter.increment();\n\t\t}\n\t};\n\n\t//读线程函数\n\tauto reader = [&counter, &temp]() {\n\t\tfor (int i = 0; i < LOOP_COUNT; i++)\n\t\t{\n\t\t\ttemp = counter.get();\n\t\t}\n\t};\n\n\t//存放读线程对象指针的数组\n\tstd::thread** tarray = new std::thread * [READER_THREAD_COUNT];\n\n\t//记录起始时间\n\tclock_t start = clock();\n\n\t//创建READER_THREAD_COUNT个读线程\n\tfor (int i = 0; i < READER_THREAD_COUNT; i++)\n\t{\n\t\ttarray[i] = new std::thread(reader);\n\t}\n\n\t//创建一个写线程\n\tstd::thread tw(writer);\n\n\tfor (int i = 0; i < READER_THREAD_COUNT; i++)\n\t{\n\t\ttarray[i]->join();\n\t}\n\ttw.join();\n\n\t//记录起始时间\n\tclock_t end = clock();\n\tprintf(\"[test_shared_mutex]\\n\");\n\tprintf(\"thread count: %d\\n\", READER_THREAD_COUNT);\n\tprintf(\"result: %d cost: %dms temp: %d \\n\", counter.get(), end - start, temp);\n}\n\n//测试std::mutex\nvoid test_mutex()\n{\n\tmutex_counter counter;\n\n\tint temp;\n\n\t//写线程函数\n\tauto writer = [&counter]() {\n\t\tfor (int i = 0; i < LOOP_COUNT; i++)\n\t\t{\n\t\t\tcounter.increment();\n\t\t}\n\t};\n\n\t//读线程函数\n\tauto reader = [&counter, &temp]() {\n\t\tfor (int i = 0; i < LOOP_COUNT; i++)\n\t\t{\n\t\t\ttemp = counter.get();\n\t\t}\n\t};\n\n\t//存放读线程对象指针的数组\n\tstd::thread** tarray = new std::thread * [READER_THREAD_COUNT];\n\n\t//记录起始时间\n\tclock_t start = clock();\n\n\t//创建READER_THREAD_COUNT个读线程\n\tfor (int i = 0; i < READER_THREAD_COUNT; i++)\n\t{\n\t\ttarray[i] = new std::thread(reader);\n\t}\n\n\t//创建一个写线程\n\tstd::thread tw(writer);\n\n\tfor (int i = 0; i < READER_THREAD_COUNT; i++)\n\t{\n\t\ttarray[i]->join();\n\t}\n\ttw.join();\n\n\t//记录结束时间\n\tclock_t end = clock();\n\tprintf(\"[test_mutex]\\n\");\n\tprintf(\"thread count:%d\\n\", READER_THREAD_COUNT);\n\tprintf(\"result:%d cost:%dms temp:%d \\n\", counter.get(), end - start, temp);\n}\n\nint main()\n{\n\t//为了排除测试程序的无关因素，测试时只开启一个  \n\ttest_mutex();\n\t//test_shared_mutex();\n\treturn 0;\n}"
  },
  {
    "path": "Chapter03/code/TestWindowsConditionVariable/TestWindowsConditionVariable.cpp",
    "content": "/** \r\n * ʾ Windows ʹ\r\n * zhangyl 20191111\r\n */\r\n\r\n#include <Windows.h>\r\n#include <iostream>\r\n#include <list>\r\n\r\nclass Task\r\n{\r\npublic:\r\n    Task(int taskID)\r\n    {\r\n        this->taskID = taskID;\r\n    }\r\n\r\n    void doTask()\r\n    {\r\n        std::cout << \"handle a task, taskID: \" << taskID << \", threadID: \" << GetCurrentThreadId() << std::endl;\r\n    }\r\n\r\nprivate:\r\n    int taskID;\r\n};\r\n\r\nCRITICAL_SECTION    myCriticalSection;\r\nCONDITION_VARIABLE  myConditionVar;\r\nstd::list<Task*>    tasks;\r\n\r\nDWORD WINAPI consumerThread(LPVOID param)\r\n{\r\n    Task* pTask = NULL;\r\n    while (true)\r\n    {\r\n        //ٽ\r\n        EnterCriticalSection(&myCriticalSection);\r\n        while (tasks.empty())\r\n        {\r\n            //SleepConditionVariableCS𣬹ǰ뿪ٽִС\r\n            //仯ʣSleepConditionVariableCSֱӽٽ\r\n            SleepConditionVariableCS(&myConditionVar, &myCriticalSection, INFINITE);\r\n        }\r\n\r\n        pTask = tasks.front();\r\n        tasks.pop_front();\r\n\r\n        //SleepConditionVariableCSѺٽ\r\n        //Ϊ߳лtasksҪٴ뿪ٽ\r\n        LeaveCriticalSection(&myCriticalSection);\r\n\r\n        if (pTask == NULL)\r\n            continue;\r\n\r\n        pTask->doTask();\r\n        delete pTask;\r\n        pTask = NULL;\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\nDWORD WINAPI producerThread(LPVOID param)\r\n{\r\n    int taskID = 0;\r\n    Task* pTask = NULL;\r\n\r\n    while (true)\r\n    {\r\n        pTask = new Task(taskID);\r\n\r\n        //ٽ\r\n        EnterCriticalSection(&myCriticalSection);\r\n        tasks.push_back(pTask);\r\n        std::cout << \"produce a task, taskID: \" << taskID << \", threadID: \" << GetCurrentThreadId() << std::endl;\r\n\r\n        LeaveCriticalSection(&myCriticalSection);\r\n\r\n        WakeConditionVariable(&myConditionVar);\r\n\r\n        taskID++;\r\n\r\n        //1\r\n        Sleep(1000);\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\nint main()\r\n{\r\n    InitializeCriticalSection(&myCriticalSection);\r\n    InitializeConditionVariable(&myConditionVar);\r\n\r\n    //5߳\r\n    HANDLE consumerThreadHandles[5];\r\n    for (int i = 0; i < 5; ++i)\r\n    {\r\n        consumerThreadHandles[i] = CreateThread(NULL, 0, consumerThread, NULL, 0, NULL);\r\n    }\r\n\r\n    //һ߳\r\n    HANDLE producerThreadHandle = CreateThread(NULL, 0, producerThread, NULL, 0, NULL);\r\n\r\n    //ȴ߳˳\r\n    WaitForSingleObject(producerThreadHandle, INFINITE);\r\n\r\n    //ȴ߳˳\r\n    for (int i = 0; i < 5; ++i)\r\n    {\r\n        WaitForSingleObject(consumerThreadHandles[i], INFINITE);\r\n    }\r\n\r\n    DeleteCriticalSection(&myCriticalSection);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter03/code/TestWindowsConditionVariable/TestWindowsConditionVariable.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.29123.88\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"TestWindowsConditionVariable\", \"TestWindowsConditionVariable.vcxproj\", \"{2A908430-217D-4795-BCA8-2158DDE2A3D3}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Release|x64.Build.0 = Release|x64\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{2A908430-217D-4795-BCA8-2158DDE2A3D3}.Release|x86.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {9B46F9E1-DE6F-4B07-A5C9-9C953F7FD26E}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter03/code/TestWindowsConditionVariable/TestWindowsConditionVariable.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>16.0</VCProjectVersion>\r\n    <ProjectGuid>{2A908430-217D-4795-BCA8-2158DDE2A3D3}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>TestWindowsConditionVariable</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"TestWindowsConditionVariable.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter03/code/TestWindowsConditionVariable/TestWindowsConditionVariable.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"TestWindowsConditionVariable.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter03/code/c11mutex.cpp",
    "content": "#include <iostream>\r\n#include <chrono>\r\n#include <thread>\r\n#include <mutex>\r\n\r\n// protected by g_num_mutex\r\nint g_num = 0;  \r\nstd::mutex g_num_mutex;\r\n \r\nvoid slow_increment(int id) \r\n{\r\n    for (int i = 0; i < 3; ++i) {\r\n        g_num_mutex.lock();\r\n        ++g_num;\r\n        std::cout << id << \" => \" << g_num << std::endl;\r\n        g_num_mutex.unlock();\r\n\t\t\r\n\t\t//sleep for 1 second\r\n        std::this_thread::sleep_for(std::chrono::seconds(1));\r\n    }\r\n}\r\n \r\nint main()\r\n{\r\n    std::thread t1(slow_increment, 0);\r\n    std::thread t2(slow_increment, 1);\r\n    t1.join();\r\n    t2.join();\r\n\t\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/c11threadlocal.cpp",
    "content": "#include <thread>\r\n#include <chrono>\r\n#include <iostream>\r\n\r\nthread_local int g_mydata = 1;\r\n\r\nvoid thread_func1()\r\n{\r\n\twhile (true)\r\n\t{\r\n\t\t++g_mydata;\r\n\t}\r\n}\r\n\r\nvoid thread_func2()\r\n{\r\n\twhile (true)\r\n\t{\r\n\t\tstd::cout << \"g_mydata = \" << g_mydata << \", ThreadID = \" << std::this_thread::get_id() << std::endl;\r\n\t\tstd::this_thread::sleep_for(std::chrono::seconds(1));\r\n\t}\r\n}\r\n\r\nint main()\r\n{\r\n\tstd::thread t1(thread_func1);\r\n\tstd::thread t2(thread_func2);\r\n\r\n\tt1.join();\r\n\tt2.join();\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/cpp11cv.cpp",
    "content": "#include <thread>\r\n#include <mutex>\r\n#include <condition_variable>\r\n#include <unistd.h>\r\n#include <list>\r\n#include <iostream>\r\n#include <chrono>\r\n\r\nclass Task\r\n{\r\npublic:\r\n\tTask(int taskID)\r\n\t{\r\n\t\tthis->taskID = taskID;\r\n\t}\r\n\t\r\n\tvoid doTask()\r\n\t{\r\n\t\tstd::cout << \"handle a task, taskID: \" << taskID << \", threadID: \" << std::this_thread::get_id() << std::endl; \r\n\t}\r\n\t\r\nprivate:\r\n\tint taskID;\r\n};\r\n\r\nstd::mutex                mymutex;\r\nstd::list<Task*>          tasks;\r\nstd::condition_variable   mycv;\r\n\r\nvoid* consumer_thread()\r\n{\t\r\n\tTask* pTask = NULL;\r\n\twhile (true)\r\n\t{\r\n\t\tstd::unique_lock<std::mutex> guard(mymutex);\r\n\t\twhile (tasks.empty())\r\n\t\t{\t\t\t\t\r\n\t\t\t//如果获得了互斥锁，但是条件不合适的话，pthread_cond_wait会释放锁，不往下执行。\r\n\t\t\t//当发生变化后，条件合适，pthread_cond_wait将直接获得锁。\r\n\t\t\tmycv.wait(guard);\r\n\t\t}\r\n\t\t\r\n\t\tpTask = tasks.front();\r\n\t\ttasks.pop_front();\r\n\t\t\r\n\t\tif (pTask == NULL)\r\n\t\t\tcontinue;\r\n\r\n\t\tpTask->doTask();\r\n\t\tdelete pTask;\r\n\t\tpTask = NULL;\t\t\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* producer_thread()\r\n{\r\n\tint taskID = 0;\r\n\tTask* pTask = NULL;\r\n\t\r\n\twhile (true)\r\n\t{\r\n\t\tpTask = new Task(taskID);\r\n\t\t\t\r\n\t\t//使用括号减小guard锁的作用范围\r\n\t\t{\r\n\t\t\tstd::lock_guard<std::mutex> guard(mymutex);\r\n\t\t\ttasks.push_back(pTask);\r\n\t\t\tstd::cout << \"produce a task, taskID: \" << taskID << \", threadID: \" << std::this_thread::get_id() << std::endl; \r\n\t\t}\r\n\t\t\r\n\t\t//释放信号量，通知消费者线程\r\n\t\tmycv.notify_one();\r\n\t\t\r\n\t\ttaskID ++;\r\n\r\n\t\t//休眠1秒\r\n\t\tstd::this_thread::sleep_for(std::chrono::seconds(1));\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\t//创建5个消费者线程\r\n\tstd::thread consumer1(consumer_thread);\r\n\tstd::thread consumer2(consumer_thread);\r\n\tstd::thread consumer3(consumer_thread);\r\n\tstd::thread consumer4(consumer_thread);\r\n\tstd::thread consumer5(consumer_thread);\r\n\t\r\n\t//创建一个生产者线程\r\n\tstd::thread producer(producer_thread);\r\n\r\n\tproducer.join();\r\n\tconsumer1.join();\r\n\tconsumer2.join();\r\n\tconsumer3.join();\r\n\tconsumer4.join();\r\n\tconsumer5.join();\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/cv.cpp",
    "content": "#include <pthread.h>\r\n#include <errno.h>\r\n#include <unistd.h>\r\n#include <list>\r\n#include <semaphore.h>\r\n#include <iostream>\r\n\r\nclass Task\r\n{\r\npublic:\r\n\tTask(int taskID)\r\n\t{\r\n\t\tthis->taskID = taskID;\r\n\t}\r\n\t\r\n\tvoid doTask()\r\n\t{\r\n\t\tstd::cout << \"handle a task, taskID: \" << taskID << \", threadID: \" << pthread_self() << std::endl; \r\n\t}\r\n\t\r\nprivate:\r\n\tint taskID;\r\n};\r\n\r\npthread_mutex_t  mymutex;\r\nstd::list<Task*> tasks;\r\npthread_cond_t   mycv;\r\n\r\nvoid* consumer_thread(void* param)\r\n{\t\r\n\tTask* pTask = NULL;\r\n\twhile (true)\r\n\t{\r\n\t\tpthread_mutex_lock(&mymutex);\r\n\t\twhile (tasks.empty())\r\n\t\t{\t\t\t\t\r\n\t\t\t//如果获得了互斥锁，但是条件不合适的话，pthread_cond_wait会释放锁，不往下执行。\r\n\t\t\t//当发生变化后，条件合适，pthread_cond_wait将直接获得锁。\r\n\t\t\tpthread_cond_wait(&mycv, &mymutex);\r\n\t\t}\r\n\t\t\r\n\t\tpTask = tasks.front();\r\n\t\ttasks.pop_front();\r\n\r\n\t\tpthread_mutex_unlock(&mymutex);\r\n\t\t\r\n\t\tif (pTask == NULL)\r\n\t\t\tcontinue;\r\n\r\n\t\tpTask->doTask();\r\n\t\tdelete pTask;\r\n\t\tpTask = NULL;\t\t\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* producer_thread(void* param)\r\n{\r\n\tint taskID = 0;\r\n\tTask* pTask = NULL;\r\n\t\r\n\twhile (true)\r\n\t{\r\n\t\tpTask = new Task(taskID);\r\n\t\t\t\r\n\t\tpthread_mutex_lock(&mymutex);\r\n\t\ttasks.push_back(pTask);\r\n\t\tstd::cout << \"produce a task, taskID: \" << taskID << \", threadID: \" << pthread_self() << std::endl; \r\n\t\t\r\n\t\tpthread_mutex_unlock(&mymutex);\r\n\t\t\r\n\t\t//释放条件信号，通知消费者线程\r\n\t\tpthread_cond_signal(&mycv);\r\n\t\t\r\n\t\ttaskID ++;\r\n\r\n\t\t//休眠1秒\r\n\t\tsleep(1);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\tpthread_mutex_init(&mymutex, NULL);\r\n\tpthread_cond_init(&mycv, NULL);\r\n\r\n\t//创建5个消费者线程\r\n\tpthread_t consumerThreadID[5];\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);\r\n\t}\r\n\t\r\n\t//创建一个生产者线程\r\n\tpthread_t producerThreadID;\r\n\tpthread_create(&producerThreadID, NULL, producer_thread, NULL);\r\n\r\n\tpthread_join(producerThreadID, NULL);\r\n\t\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(consumerThreadID[i], NULL);\r\n\t}\r\n\t\r\n\tpthread_cond_destroy(&mycv);\r\n\tpthread_mutex_destroy(&mymutex);\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/insecurecrtfunction.cpp",
    "content": "// ConsoleApplication13.cpp : Defines the entry point for the console application.\r\n//\r\n\r\n#include \"stdafx.h\"\r\n#include <time.h>\r\n\r\nint main()\r\n{\r\n    time_t tNow = time(NULL);\r\n    time_t tEnd = tNow + 1800;\r\n    //עе\r\n    struct tm* ptm = localtime(&tNow);\r\n    //struct tm* ptmEnd = localtime(&tEnd);\r\n\r\n    char szTmp[50] = { 0 };\r\n    strftime(szTmp, 50, \"%H:%M:%S\", ptm);\r\n\r\n    struct tm* ptmEnd = localtime(&tEnd);\r\n    char szEnd[50] = { 0 };\r\n    strftime(szEnd, 50, \"%H:%M:%S\", ptmEnd);\r\n    printf(\"%s \\n\", szTmp);\r\n    printf(\"%s \\n\", szEnd);\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter03/code/linuxtid.cpp",
    "content": "#include <sys/syscall.h>\r\n#include <unistd.h>\r\n#include <stdio.h>\r\n#include <pthread.h>\r\n\r\nvoid* thread_proc(void* arg)\r\n{\r\n\tpthread_t* tid1 = (pthread_t*)arg;\r\n\tint tid2 = syscall(SYS_gettid);\r\n\tpthread_t tid3 = pthread_self();\r\n\t\r\n\twhile(true)\r\n\t{\r\n\t\tprintf(\"tid1: %ld, tid2: %ld, tid3: %ld\\n\", *tid1, tid2, tid3);\r\n\t\tsleep(1);\r\n\t}\r\n\t\r\n}\r\n\r\nint main()\r\n{\t\r\n\tpthread_t tid;\r\n\tpthread_create(&tid, NULL, thread_proc, &tid);\r\n\t\r\n\tpthread_join(tid, NULL);\r\n\t\r\n\treturn 0;\r\n}\r\n"
  },
  {
    "path": "Chapter03/code/linuxtls.cpp",
    "content": "#include <pthread.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n\r\n//线程局部存储key\r\npthread_key_t thread_log_key;\r\n\r\nvoid write_to_thread_log(const char* message)\r\n{\r\n\tif (message == NULL)\r\n\t\treturn;\r\n\t\r\n\tFILE* logfile = (FILE*)pthread_getspecific(thread_log_key);\r\n\tfprintf(logfile, \"%s\\n\", message);\r\n\tfflush(logfile);\r\n} \r\n\r\nvoid close_thread_log(void* logfile)\r\n{\r\n\tchar logfilename[128];\r\n\tsprintf(logfilename, \"close logfile: thread%ld.log\\n\", (unsigned long)pthread_self());\r\n\tprintf(logfilename);\r\n\t\r\n\tfclose((FILE *)logfile);\r\n} \r\n\r\nvoid* thread_function(void* args)\r\n{\r\n\tchar logfilename[128];\r\n\tsprintf(logfilename, \"thread%ld.log\", (unsigned long)pthread_self());\r\n\t\r\n\tFILE* logfile = fopen(logfilename, \"w\");\r\n\tif (logfile != NULL)\r\n\t{\r\n\t\tpthread_setspecific(thread_log_key, logfile);\r\n\t\r\n\t\twrite_to_thread_log(\"Thread starting...\");\r\n\t}\r\n\t\r\n\treturn NULL;\r\n} \r\n\r\nint main()\r\n{\r\n\tpthread_t threadIDs[5];\t\r\n\tpthread_key_create(&thread_log_key, close_thread_log);\r\n\tfor(int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&threadIDs[i], NULL, thread_function, NULL);\r\n\t}\r\n\t\r\n\tfor(int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(threadIDs[i], NULL);\r\n\t}\r\n\t\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/linuxtls2.cpp",
    "content": "#include <pthread.h>\r\n#include <iostream>\r\n#include <unistd.h>\r\n\r\n//线程局部存储key\r\n__thread int g_mydata = 99;\r\n\r\nvoid* thread_function1(void* args)\r\n{\r\n\twhile (true)\r\n\t{\r\n\t\tg_mydata ++;\r\n\t}\r\n\t\r\n\treturn NULL;\r\n} \r\n\r\nvoid* thread_function2(void* args)\r\n{\r\n\twhile (true)\r\n\t{\t\t\r\n\t\tstd::cout << \"g_mydata = \" << g_mydata << \", ThreadID: \" << pthread_self() << std::endl;\r\n\t\tsleep(1);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n} \r\n\r\nint main()\r\n{\r\n\tpthread_t threadIDs[2];\t\r\n\tpthread_create(&threadIDs[0], NULL, thread_function1, NULL);\r\n\tpthread_create(&threadIDs[1], NULL, thread_function2, NULL);\r\n\t\r\n\tfor(int i = 0; i < 2; ++i)\r\n\t{\r\n\t\tpthread_join(threadIDs[i], NULL);\r\n\t}\r\n\t\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/makesurethread.cpp",
    "content": "#include <thread>\r\n#include <mutex>\r\n#include <condition_variable>\r\n#include <iostream>\r\n\r\nstd::mutex \t\t\t\tmymutex;\r\nstd::condition_variable mycv;\r\nbool success = false;\r\n\r\nvoid thread_func()\r\n{\r\n\t{\r\n        std::unique_lock<std::mutex> lock(mymutex);\r\n\t\tsuccess = true;\r\n\t\tmycv.notify_all();\r\n\t}\r\n\t\r\n\t//实际的线程执行的工作代码放在下面\r\n    //这里为了模拟方便，简单地写个死循环\r\n    while (true)\r\n    {\r\n\r\n    }\r\n}\r\n\r\nint main()\r\n{\r\n\tstd::thread t(thread_func);\r\n\t\r\n\t//使用花括号减小锁的粒度\r\n\t{\r\n\t\tstd::unique_lock<std::mutex> lock(mymutex);\r\n\t\twhile (!success)\r\n\t\t{\r\n\t\t\tmycv.wait(lock);\r\n\t\t}\r\n\t}\r\n\t\r\n\tstd::cout << \"start thread successfully.\" << std::endl; \r\n\t\r\n\tt.join();\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/makesurethreadgroup.cpp",
    "content": "#include <thread>\r\n#include <mutex>\r\n#include <condition_variable>\r\n#include <iostream>\r\n#include <vector>\r\n#include <memory>\r\n\r\nstd::mutex \t\t\t\tmymutex;\r\nstd::condition_variable mycv;\r\nbool success = false;\r\n\r\nvoid thread_func()\r\n{\r\n    {\r\n        std::unique_lock<std::mutex> lock(mymutex);\r\n        success = true;\r\n        mycv.notify_all();\r\n    }\r\n\r\n    //实际的线程执行的工作代码放在下面\r\n    //这里为了模拟方便，简单地写个死循环\r\n    while (true)\r\n    {\r\n\r\n    }\r\n}\r\n\r\nint main()\r\n{\r\n    std::vector<std::shared_ptr<std::thread>> threads;\r\n    \r\n    for (int i = 0; i < 5; ++i)\r\n    {\r\n        std::shared_ptr<std::thread> spthread;\r\n        spthread.reset(new std::thread(thread_func));\r\n\r\n        //使用花括号减小锁的粒度\r\n        {\r\n            std::unique_lock<std::mutex> lock(mymutex);\r\n            while (!success)\r\n            {\r\n                mycv.wait(lock);\r\n            }\r\n        }\r\n\r\n        std::cout << \"start thread successfully， index: \" << i << std::endl;\r\n\r\n        threads.push_back(spthread);\r\n    }\r\n\r\n    for (auto& iter : threads)\r\n    {\r\n        iter->join();\r\n    }\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter03/code/rwlock.cpp",
    "content": "#include <pthread.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n\r\nint resourceID = 0;\r\npthread_rwlock_t myrwlock;\r\n\r\nvoid* read_thread(void* param)\r\n{\t\r\n\twhile (true)\r\n\t{\r\n\t\t//请求读锁\r\n\t\tpthread_rwlock_rdlock(&myrwlock);\r\n\r\n\t\tstd::cout << \"read thread ID: \" << pthread_self() << \", resourceID: \" << resourceID << std::endl;\r\n\t\t\t\t\r\n\t\t//使用睡眠模拟读线程读的过程消耗了很久的时间\r\n\t\tsleep(1);\r\n\t\t\t\t\r\n\t\tpthread_rwlock_unlock(&myrwlock);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* write_thread(void* param)\r\n{\r\n\twhile (true)\r\n\t{\r\n\t\t//请求写锁\r\n\t\tpthread_rwlock_wrlock(&myrwlock);\r\n\r\n\t\t++resourceID;\r\n\t\tstd::cout << \"write thread ID: \" << pthread_self() << \", resourceID: \" << resourceID << std::endl;\r\n\t\t\t\t\r\n\t\t//使用睡眠模拟读线程读的过程消耗了很久的时间\r\n\t\tsleep(1);\r\n\t\t\t\t\r\n\t\tpthread_rwlock_unlock(&myrwlock);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\tpthread_rwlock_init(&myrwlock, NULL);\r\n\r\n\t//创建5个请求读锁线程\r\n\tpthread_t readThreadID[5];\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&readThreadID[i], NULL, read_thread, NULL);\r\n\t}\r\n\t\r\n\t//创建一个请求写锁线程\r\n\tpthread_t writeThreadID;\r\n\tpthread_create(&writeThreadID, NULL, write_thread, NULL);\r\n\r\n\tpthread_join(writeThreadID, NULL);\r\n\t\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(readThreadID[i], NULL);\r\n\t}\r\n\t\r\n\tpthread_rwlock_destroy(&myrwlock);\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/rwlock2.cpp",
    "content": "#include <pthread.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n\r\nint resourceID = 0;\r\npthread_rwlock_t myrwlock;\r\n\r\nvoid* read_thread(void* param)\r\n{\t\r\n\twhile (true)\r\n\t{\r\n\t\t//请求读锁\r\n\t\tpthread_rwlock_rdlock(&myrwlock);\r\n\r\n\t\tstd::cout << \"read thread ID: \" << pthread_self() << \", resourceID: \" << resourceID << std::endl;\r\n\t\t\t\t\r\n\t\t//使用睡眠模拟读线程读的过程消耗了很久的时间\r\n\t\tsleep(1);\r\n\t\t\t\t\r\n\t\tpthread_rwlock_unlock(&myrwlock);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* write_thread(void* param)\r\n{\r\n\twhile (true)\r\n\t{\r\n\t\t//请求写锁\r\n\t\tpthread_rwlock_wrlock(&myrwlock);\r\n\r\n\t\t++resourceID;\r\n\t\tstd::cout << \"write thread ID: \" << pthread_self() << \", resourceID: \" << resourceID << std::endl;\r\n\t\t\t\t\r\n\t\t//使用睡眠模拟读线程读的过程消耗了很久的时间\r\n\t\tsleep(1);\r\n\t\t\r\n\t\tpthread_rwlock_unlock(&myrwlock);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\tpthread_rwlockattr_t attr;\r\n\tpthread_rwlockattr_init(&attr);\r\n\t//设置成请求写锁优先\r\n\tpthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);\r\n\tpthread_rwlock_init(&myrwlock, &attr);\r\n\r\n\t//创建5个请求读锁线程\r\n\tpthread_t readThreadID[5];\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&readThreadID[i], NULL, read_thread, NULL);\r\n\t}\r\n\t\r\n\t//创建一个请求写锁线程\r\n\tpthread_t writeThreadID;\r\n\tpthread_create(&writeThreadID, NULL, write_thread, NULL);\r\n\r\n\tpthread_join(writeThreadID, NULL);\r\n\t\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(readThreadID[i], NULL);\r\n\t}\r\n\t\r\n\tpthread_rwlock_destroy(&myrwlock);\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/rwlock3.cpp",
    "content": "#include <pthread.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n\r\nint resourceID = 0;\r\npthread_rwlock_t myrwlock;\r\n\r\nvoid* read_thread(void* param)\r\n{\t\r\n\twhile (true)\r\n\t{\r\n\t\t//请求读锁\r\n\t\tpthread_rwlock_rdlock(&myrwlock);\r\n\r\n\t\tstd::cout << \"read thread ID: \" << pthread_self() << \", resourceID: \" << resourceID << std::endl;\r\n\t\t\t\t\r\n\t\t//使用睡眠模拟读线程读的过程消耗了很久的时间\r\n\t\tsleep(1);\r\n\t\t\t\t\r\n\t\tpthread_rwlock_unlock(&myrwlock);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* write_thread(void* param)\r\n{\r\n\twhile (true)\r\n\t{\r\n\t\t//请求写锁\r\n\t\tpthread_rwlock_wrlock(&myrwlock);\r\n\r\n\t\t++resourceID;\r\n\t\tstd::cout << \"write thread ID: \" << pthread_self() << \", resourceID: \" << resourceID << std::endl;\r\n\t\t\t\t\t\t\r\n\t\tpthread_rwlock_unlock(&myrwlock);\r\n\t\t\r\n\t\t//放在这里增加请求读锁线程获得锁的几率\r\n\t\tsleep(1);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\tpthread_rwlockattr_t attr;\r\n\tpthread_rwlockattr_init(&attr);\r\n\t//设置成请求写锁优先\r\n\tpthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);\r\n\tpthread_rwlock_init(&myrwlock, &attr);\r\n\r\n\t//创建5个请求读锁线程\r\n\tpthread_t readThreadID[5];\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&readThreadID[i], NULL, read_thread, NULL);\r\n\t}\r\n\t\r\n\t//创建一个请求写锁线程\r\n\tpthread_t writeThreadID;\r\n\tpthread_create(&writeThreadID, NULL, write_thread, NULL);\r\n\r\n\tpthread_join(writeThreadID, NULL);\r\n\t\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(readThreadID[i], NULL);\r\n\t}\r\n\t\r\n\tpthread_rwlock_destroy(&myrwlock);\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/semaphore.cpp",
    "content": "#include <pthread.h>\r\n#include <errno.h>\r\n#include <unistd.h>\r\n#include <list>\r\n#include <semaphore.h>\r\n#include <iostream>\r\n\r\nclass Task\r\n{\r\npublic:\r\n\tTask(int taskID)\r\n\t{\r\n\t\tthis->taskID = taskID;\r\n\t}\r\n\t\r\n\tvoid doTask()\r\n\t{\r\n\t\tstd::cout << \"handle a task, taskID: \" << taskID << \", threadID: \" << pthread_self() << std::endl; \r\n\t}\r\n\t\r\nprivate:\r\n\tint taskID;\r\n};\r\n\r\npthread_mutex_t  mymutex;\r\nstd::list<Task*> tasks;\r\nsem_t            mysemaphore;\r\n\r\nvoid* consumer_thread(void* param)\r\n{\t\r\n\tTask* pTask = NULL;\r\n\twhile (true)\r\n\t{\r\n\t\tstruct timespec ts;\r\n\t\tts.tv_sec = 3;\r\n\t\tts.tv_nsec = 0;\r\n\t\t\r\n\t\tif (sem_timewait(&mysemaphore, &ts) != 0)\r\n\t\t{\r\n\t\t\tif (errno == ETIMEOUT)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"ETIMEOUT\" << std::endl;\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\t\r\n\t\tif (tasks.empty())\r\n\t\t\tcontinue;\r\n\t\t\r\n\t\tpthread_mutex_lock(&mymutex);\t\r\n\t\tpTask = tasks.front();\r\n\t\ttasks.pop_front();\r\n\t\tpthread_mutex_unlock(&mymutex);\r\n\t\t\r\n\t\tpTask->doTask();\r\n\t\tdelete pTask;\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* producer_thread(void* param)\r\n{\r\n\tint taskID = 0;\r\n\tTask* pTask = NULL;\r\n\t\r\n\twhile (true)\r\n\t{\r\n\t\t//pTask = new Task(taskID);\r\n\t\t\t\r\n\t\tpthread_mutex_lock(&mymutex);\r\n\t\t//tasks.push_back(pTask);\r\n\t\t//std::cout << \"produce a task, taskID: \" << taskID << \", threadID: \" << pthread_self() << std::endl; \r\n\t\t\r\n\t\tpthread_mutex_unlock(&mymutex);\r\n\t\t\r\n\t\t//释放信号量，通知消费者线程\r\n\t\tsem_post(&mysemaphore);\r\n\t\t\r\n\t\ttaskID ++;\r\n\r\n\t\t//休眠1秒\r\n\t\tsleep(1);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\tpthread_mutex_init(&mymutex, NULL);\r\n\t//初始信号量资源计数为0\r\n\tsem_init(&mysemaphore, 0, 0);\r\n\r\n\t//创建5个消费者线程\r\n\tpthread_t consumerThreadID[5];\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);\r\n\t}\r\n\t\r\n\t//创建一个生产者线程\r\n\t//pthread_t producerThreadID;\r\n\t//pthread_create(&producerThreadID, NULL, producer_thread, NULL);\r\n\r\n\t//pthread_join(producerThreadID, NULL);\r\n\t\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(consumerThreadID[i], NULL);\r\n\t}\r\n\t\r\n\tsem_destroy(&mysemaphore);\r\n\tpthread_mutex_destroy(&mymutex);\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/semaphore_timewait.cpp",
    "content": "#include <pthread.h>\r\n#include <errno.h>\r\n#include <unistd.h>\r\n#include <list>\r\n#include <semaphore.h>\r\n#include <iostream>\r\n\r\nclass Task\r\n{\r\npublic:\r\n\tTask(int taskID)\r\n\t{\r\n\t\tthis->taskID = taskID;\r\n\t}\r\n\t\r\n\tvoid doTask()\r\n\t{\r\n\t\tstd::cout << \"handle a task, taskID: \" << taskID << \", threadID: \" << pthread_self() << std::endl; \r\n\t}\r\n\t\r\nprivate:\r\n\tint taskID;\r\n};\r\n\r\npthread_mutex_t  mymutex;\r\nstd::list<Task*> tasks;\r\nsem_t            mysemaphore;\r\n\r\nvoid* consumer_thread(void* param)\r\n{\t\r\n\tTask* pTask = NULL;\r\n\twhile (true)\r\n\t{\r\n\t\tstruct timespec ts;\r\n\t\tts.tv_sec = 3;\r\n\t\tts.tv_nsec = 0;\r\n\t\t\r\n\t\tif (sem_timewait(&mysemaphore, &ts) != 0)\r\n\t\t{\r\n\t\t\tif (errno == ETIMEDOUT)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"ETIMEOUT\" << std::endl;\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\t\r\n\t\tif (tasks.empty())\r\n\t\t\tcontinue;\r\n\t\t\r\n\t\tpthread_mutex_lock(&mymutex);\t\r\n\t\tpTask = tasks.front();\r\n\t\ttasks.pop_front();\r\n\t\tpthread_mutex_unlock(&mymutex);\r\n\t\t\r\n\t\tpTask->doTask();\r\n\t\tdelete pTask;\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nvoid* producer_thread(void* param)\r\n{\r\n\tint taskID = 0;\r\n\tTask* pTask = NULL;\r\n\t\r\n\twhile (true)\r\n\t{\r\n\t\t//pTask = new Task(taskID);\r\n\t\t\t\r\n\t\tpthread_mutex_lock(&mymutex);\r\n\t\t//tasks.push_back(pTask);\r\n\t\t//std::cout << \"produce a task, taskID: \" << taskID << \", threadID: \" << pthread_self() << std::endl; \r\n\t\t\r\n\t\tpthread_mutex_unlock(&mymutex);\r\n\t\t\r\n\t\t//释放信号量，通知消费者线程\r\n\t\tsem_post(&mysemaphore);\r\n\t\t\r\n\t\ttaskID ++;\r\n\r\n\t\t//休眠1秒\r\n\t\tsleep(1);\r\n\t}\r\n\t\r\n\treturn NULL;\r\n}\r\n\r\nint main()\r\n{\r\n\tpthread_mutex_init(&mymutex, NULL);\r\n\t//初始信号量资源计数为0\r\n\tsem_init(&mysemaphore, 0, 0);\r\n\r\n\t//创建5个消费者线程\r\n\tpthread_t consumerThreadID[5];\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);\r\n\t}\r\n\t\r\n\t//创建一个生产者线程\r\n\t//pthread_t producerThreadID;\r\n\t//pthread_create(&producerThreadID, NULL, producer_thread, NULL);\r\n\r\n\t//pthread_join(producerThreadID, NULL);\r\n\t\r\n\tfor (int i = 0; i < 5; ++i)\r\n\t{\r\n\t\tpthread_join(consumerThreadID[i], NULL);\r\n\t}\r\n\t\r\n\tsem_destroy(&mysemaphore);\r\n\tpthread_mutex_destroy(&mymutex);\r\n\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter03/code/taskpoolmain.cpp",
    "content": "#include \"TaskPool.h\"\r\n#include <chrono>\r\n\r\nint main()\r\n{\r\n    TaskPool threadPool;\r\n    threadPool.init();\r\n\r\n    Task* task = NULL;\r\n    for (int i = 0; i < 10; ++i)\r\n    {\r\n        task = new Task();\r\n        threadPool.addTask(task);\r\n    }\r\n    \r\n    std::this_thread::sleep_for(std::chrono::seconds(5));\r\n\r\n    threadPool.stop();\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter03/code/test_cpp11_thread_id.cpp",
    "content": "//test_cpp11_thread_id.cpp\n#include <thread>\n#include <iostream>\n#include <sstream>\n\nvoid worker_thread_func()\n{\n    while (true)\n    {\n\n    }\n}\n\nint main()\n{\n    std::thread t(worker_thread_func);\n    //获取线程t的ID\n    std::thread::id worker_thread_id = t.get_id();\n    std::cout << \"worker thread id: \" << worker_thread_id << std::endl;\n\n    //获取主线程的线程ID\n    std::thread::id main_thread_id = std::this_thread::get_id();\n    //先将std::thread::id转换成std::ostringstream对象\n    std::ostringstream oss;\n    oss << main_thread_id;\n    //再将std::ostringstream对象转换成std::string\n    std::string str = oss.str();\n    std::cout << \"main thread id: \" << str << std::endl;\n    //最后将std::string转换成整型值\n    unsigned long long threadid = std::stoull(str);\n    std::cout << \"main thread id: \" << threadid << std::endl;\n\n    while (true)\n    {\n    \t//权宜之计，让主线程不要退出\n    }\n\n    return 0;\n}"
  },
  {
    "path": "Chapter03/code/test_destroy_locked_mutex.cpp",
    "content": "//test_destroy_locked_mutex.cpp\n#include <pthread.h>\n#include <stdio.h>\n#include <errno.h>\n\nint main()\n{\n\tpthread_mutex_t mymutex;\n\tpthread_mutex_init(&mymutex, NULL);\n\tint ret = pthread_mutex_lock(&mymutex);\n\t\n\t//尝试对被锁定的mutex对象进行销毁\n\tret = pthread_mutex_destroy(&mymutex);\n\tif (ret != 0)\n\t{\n\t\tif (ret == EBUSY)\n\t\t\tprintf(\"EBUSY\\n\");\n\t\tprintf(\"Failed to destroy mutex.\\n\");\n\t}\n\n\tret = pthread_mutex_unlock(&mymutex);\n\t//尝试对已经解锁的mutex对象进行销毁\n\tret = pthread_mutex_destroy(&mymutex);\n\tif (ret == 0)\n\t\tprintf(\"Succeeded to destroy mutex.\\n\");\n\n\treturn 0;\n}"
  },
  {
    "path": "Chapter03/code/windowstls.cpp",
    "content": "#include <Windows.h>\r\n#include <iostream>\r\n\r\n__declspec(thread) int g_mydata = 1;\r\n\r\nDWORD __stdcall WorkerThreadProc1(LPVOID lpThreadParameter)\r\n{\r\n    while (true)\r\n    {\r\n        ++g_mydata;\r\n        //std::cout << \"g_mydata = \" << g_mydata << \", ThreadID = \" << GetCurrentThreadId() << std::endl;\r\n        Sleep(1000);\r\n    }\r\n    return 0;\r\n}\r\n\r\nDWORD __stdcall WorkerThreadProc2(LPVOID lpThreadParameter)\r\n{\r\n    while (true)\r\n    {       \r\n        std::cout << \"g_mydata = \" << g_mydata << \", ThreadID = \" << GetCurrentThreadId() << std::endl;\r\n        Sleep(1000);\r\n    }\r\n    return 0;\r\n}\r\n\r\nint main()\r\n{\r\n    HANDLE hWorkerThreads[2];\r\n    hWorkerThreads[0] = CreateThread(NULL, 0, WorkerThreadProc1, NULL, 0, NULL);\r\n    hWorkerThreads[1] = CreateThread(NULL, 0, WorkerThreadProc2, NULL, 0, NULL);\r\n    \r\n    CloseHandle(hWorkerThreads[0]);\r\n    CloseHandle(hWorkerThreads[1]);\r\n\r\n    while (true)\r\n    {\r\n        Sleep(1000);\r\n    }\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPModel.cpp",
    "content": "#include \"StdAfx.h\"\r\n#include \"IOCPModel.h\"\r\n#include \"MainDlg.h\"\r\n\r\n// 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能，详见配套文档)\r\n#define WORKER_THREADS_PER_PROCESSOR 2\r\n// 同时投递的Accept请求的数量(这个要根据实际的情况灵活设置)\r\n#define MAX_POST_ACCEPT              10\r\n// 传递给Worker线程的退出信号\r\n#define EXIT_CODE                    NULL\r\n\r\n\r\n// 释放指针和句柄资源的宏\r\n\r\n// 释放指针宏\r\n#define RELEASE(x)                      {if(x != NULL ){delete x;x=NULL;}}\r\n// 释放句柄宏\r\n#define RELEASE_HANDLE(x)               {if(x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x);x = NULL;}}\r\n// 释放Socket宏\r\n#define RELEASE_SOCKET(x)               {if(x !=INVALID_SOCKET) { closesocket(x);x=INVALID_SOCKET;}}\r\n\r\n\r\n\r\nCIOCPModel::CIOCPModel(void) :\r\n\tm_nThreads(0),\r\n\tm_hShutdownEvent(NULL),\r\n\tm_hIOCompletionPort(NULL),\r\n\tm_phWorkerThreads(NULL),\r\n\tm_strIP(DEFAULT_IP),\r\n\tm_nPort(DEFAULT_PORT),\r\n\tm_pMain(NULL),\r\n\tm_lpfnAcceptEx(NULL),\r\n\tm_pListenContext(NULL)\r\n{\r\n}\r\n\r\n\r\nCIOCPModel::~CIOCPModel(void)\r\n{\r\n\t// 确保资源彻底释放\r\n\tthis->Stop();\r\n}\r\n\r\n\r\n\r\n\r\n///////////////////////////////////////////////////////////////////\r\n// 工作者线程：  为IOCP请求服务的工作者线程\r\n//         也就是每当完成端口上出现了完成数据包，就将之取出来进行处理的线程\r\n///////////////////////////////////////////////////////////////////\r\n\r\nDWORD WINAPI CIOCPModel::_WorkerThread(LPVOID lpParam)\r\n{\r\n\tTHREADPARAMS_WORKER* pParam = (THREADPARAMS_WORKER*)lpParam;\r\n\tCIOCPModel* pIOCPModel = (CIOCPModel*)pParam->pIOCPModel;\r\n\tint nThreadNo = (int)pParam->nThreadNo;\r\n\r\n\tpIOCPModel->_ShowMessage(_T(\"工作者线程启动，ID: %d.\"), nThreadNo);\r\n\r\n\tOVERLAPPED* pOverlapped = NULL;\r\n\tPER_SOCKET_CONTEXT* pSocketContext = NULL;\r\n\tDWORD                dwBytesTransfered = 0;\r\n\r\n\t// 循环处理请求，知道接收到Shutdown信息为止\r\n\twhile (WAIT_OBJECT_0 != WaitForSingleObject(pIOCPModel->m_hShutdownEvent, 0))\r\n\t{\r\n\t\tBOOL bReturn = GetQueuedCompletionStatus(\r\n\t\t\tpIOCPModel->m_hIOCompletionPort,\r\n\t\t\t&dwBytesTransfered,\r\n\t\t\t(PULONG_PTR)&pSocketContext,\r\n\t\t\t&pOverlapped,\r\n\t\t\tINFINITE);\r\n\r\n\t\t// 如果收到的是退出标志，则直接退出\r\n\t\tif (EXIT_CODE == (DWORD)pSocketContext)\r\n\t\t{\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\t// 判断是否出现了错误\r\n\t\tif (!bReturn)\r\n\t\t{\r\n\t\t\tDWORD dwErr = GetLastError();\r\n\r\n\t\t\t// 显示一下提示信息\r\n\t\t\tif (!pIOCPModel->HandleError(pSocketContext, dwErr))\r\n\t\t\t{\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// 读取传入的参数\r\n\t\t\tPER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);\r\n\r\n\t\t\t// 判断是否有客户端断开了\r\n\t\t\tif ((0 == dwBytesTransfered) && (RECV_POSTED == pIoContext->m_OpType || SEND_POSTED == pIoContext->m_OpType))\r\n\t\t\t{\r\n\t\t\t\tpIOCPModel->_ShowMessage(_T(\"客户端 %s:%d 断开连接.\"), inet_ntoa(pSocketContext->m_ClientAddr.sin_addr), ntohs(pSocketContext->m_ClientAddr.sin_port));\r\n\r\n\t\t\t\t// 释放掉对应的资源\r\n\t\t\t\tpIOCPModel->_RemoveContext(pSocketContext);\r\n\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tswitch (pIoContext->m_OpType)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Accept  \r\n\t\t\t\tcase ACCEPT_POSTED:\r\n\t\t\t\t{\r\n\r\n\t\t\t\t\t// 为了增加代码可读性，这里用专门的_DoAccept函数进行处理连入请求\r\n\t\t\t\t\tpIOCPModel->_DoAccept(pSocketContext, pIoContext);\r\n\r\n\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t\t// RECV\r\n\t\t\t\tcase RECV_POSTED:\r\n\t\t\t\t{\r\n\t\t\t\t\t// 为了增加代码可读性，这里用专门的_DoRecv函数进行处理接收请求\r\n\t\t\t\t\tpIOCPModel->_DoRecv(pSocketContext, pIoContext);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t\t// SEND\r\n\t\t\t\t// 这里略过不写了，要不代码太多了，不容易理解，Send操作相对来讲简单一些\r\n\t\t\t\tcase SEND_POSTED:\r\n\t\t\t\t{\r\n\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\t// 不应该执行到这里\r\n\t\t\t\t\tTRACE(_T(\"_WorkThread中的 pIoContext->m_OpType 参数异常.\\n\"));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t} //switch\r\n\t\t\t}//if\r\n\t\t}//if\r\n\r\n\t}//while\r\n\r\n\tTRACE(_T(\"工作者线程 %d 号退出.\\n\"), nThreadNo);\r\n\r\n\t// 释放线程参数\r\n\tRELEASE(lpParam);\r\n\r\n\treturn 0;\r\n}\r\n\r\n\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\t    系统初始化和终止\r\n//\r\n//====================================================================================\r\n\r\n// 初始化WinSock 2.2\r\nbool CIOCPModel::LoadSocketLib()\r\n{\r\n\tWSADATA wsaData;\r\n\tint nResult;\r\n\tnResult = ::WSAStartup(MAKEWORD(2, 2), &wsaData);\r\n\t// 错误(一般都不可能出现)\r\n\tif (nResult != NO_ERROR && wsaData.wHighVersion != 2 && wsaData.wVersion != 2)\r\n\t{\r\n\t\t_ShowMessage(_T(\"初始化WinSock 2.2失败！\\n\"));\r\n\t\treturn false;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n//\t启动服务器\r\nbool CIOCPModel::Start()\r\n{\r\n\t// 初始化线程互斥量\r\n\t::InitializeCriticalSection(&m_csContextList);\r\n\r\n\t// 建立系统退出的事件通知\r\n\tm_hShutdownEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);\r\n\r\n\t// 初始化IOCP\r\n\tif (!_InitializeIOCP())\r\n\t{\r\n\t\t_ShowMessage(_T(\"初始化IOCP失败！\\n\"));\r\n\t\treturn false;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t_ShowMessage(_T(\"\\nIOCP初始化完毕\\n.\"));\r\n\t}\r\n\r\n\t// 初始化Socket\r\n\tif (!_InitializeListenSocket())\r\n\t{\r\n\t\t_ShowMessage(_T(\"Listen Socket初始化失败！\\n\"));\r\n\t\t_Uninitialize();\r\n\t\treturn false;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t_ShowMessage(_T(\"Listen Socket初始化完毕.\"));\r\n\t}\r\n\r\n\t_ShowMessage(_T(\"系统准备就绪，等候连接....\\n\"));\r\n\r\n\treturn true;\r\n}\r\n\r\n//\t开始发送系统退出消息，退出完成端口和线程资源\r\nvoid CIOCPModel::Stop()\r\n{\r\n\tif (m_pListenContext != NULL && m_pListenContext->m_Socket != INVALID_SOCKET)\r\n\t{\r\n\t\t// 激活关闭消息通知\r\n\t\t::SetEvent(m_hShutdownEvent);\r\n\r\n\t\tfor (int i = 0; i < m_nThreads; i++)\r\n\t\t{\r\n\t\t\t// 通知所有的完成端口操作退出\r\n\t\t\t::PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);\r\n\t\t}\r\n\r\n\t\t// 等待所有的客户端资源退出\r\n\t\t::WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);\r\n\r\n\t\t// 清除客户端列表信息\r\n\t\t_ClearContextList();\r\n\r\n\t\t// 释放其他资源\r\n\t\t_Uninitialize();\r\n\r\n\t\t_ShowMessage(_T(\"停止监听\\n\"));\r\n\t}\r\n}\r\n\r\n// 初始化完成端口\r\nbool CIOCPModel::_InitializeIOCP()\r\n{\r\n\t// 建立第一个完成端口\r\n\tm_hIOCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);\r\n\tif (m_hIOCompletionPort == NULL)\r\n\t{\r\n\t\t_ShowMessage(_T(\"建立完成端口失败！错误代码: %d!\\n\"), WSAGetLastError());\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// 根据本机中的处理器数量，建立对应的线程数\r\n\tm_nThreads = WORKER_THREADS_PER_PROCESSOR * _GetNoOfProcessors();\r\n\r\n\t// 为工作者线程初始化句柄\r\n\tm_phWorkerThreads = new HANDLE[m_nThreads];\r\n\r\n\t// 根据计算出来的数量建立工作者线程\r\n\tDWORD nThreadID;\r\n\tfor (int i = 0; i < m_nThreads; i++)\r\n\t{\r\n\t\tTHREADPARAMS_WORKER* pThreadParams = new THREADPARAMS_WORKER;\r\n\t\tpThreadParams->pIOCPModel = this;\r\n\t\tpThreadParams->nThreadNo = i + 1;\r\n\t\tm_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, (void*)pThreadParams, 0, &nThreadID);\r\n\t}\r\n\r\n\tTRACE(\" 建立 _WorkerThread %d 个.\\n\", m_nThreads);\r\n\r\n\treturn true;\r\n}\r\n\r\n// 初始化Socket\r\nbool CIOCPModel::_InitializeListenSocket()\r\n{\r\n\t// AcceptEx 和 GetAcceptExSockaddrs 的GUID，用于导出函数指针\r\n\tGUID GuidAcceptEx = WSAID_ACCEPTEX;\r\n\tGUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;\r\n\r\n\t// 服务器地址信息，用于绑定Socket\r\n\tstruct sockaddr_in ServerAddress;\r\n\r\n\t// 生成用于监听的Socket的信息\r\n\tm_pListenContext = new PER_SOCKET_CONTEXT;\r\n\r\n\t// 需要使用重叠IO，必须得使用WSASocket来建立Socket，才可以支持重叠IO操作\r\n\tm_pListenContext->m_Socket = ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);\r\n\tif (m_pListenContext->m_Socket == INVALID_SOCKET)\r\n\t{\r\n\t\t_ShowMessage(_T(\"初始化Socket失败，错误代码: %d.\\n\"), ::WSAGetLastError());\r\n\t\treturn false;\r\n\t}\r\n\t\r\n\tTRACE(_T(\"WSASocket() 完成.\\n\"));\r\n\t\r\n\r\n\t// 将Listen Socket绑定至完成端口中\r\n\tif (::CreateIoCompletionPort((HANDLE)m_pListenContext->m_Socket, m_hIOCompletionPort, (DWORD)m_pListenContext, 0) == NULL)\r\n\t{\r\n\t\t_ShowMessage(_T(\"绑定 Listen Socket至完成端口失败！错误代码: %d/n\"), ::WSAGetLastError());\r\n\t\tRELEASE_SOCKET(m_pListenContext->m_Socket);\r\n\t\treturn false;\r\n\t}\r\n\t\r\n\tTRACE(_T(\"Listen Socket绑定完成端口 完成.\\n\"));\r\n\t\r\n\t// 填充地址信息\r\n\tZeroMemory((char*)&ServerAddress, sizeof(ServerAddress));\r\n\tServerAddress.sin_family = AF_INET;\r\n\t// 这里可以绑定任何可用的IP地址，或者绑定一个指定的IP地址 \r\n\tServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);      \r\n\tServerAddress.sin_port = htons(m_nPort);\r\n\r\n\t// 绑定地址和端口\r\n\tif (::bind(m_pListenContext->m_Socket, (struct sockaddr*) & ServerAddress, sizeof(ServerAddress)) == SOCKET_ERROR)\r\n\t{\r\n\t\t_ShowMessage(_T(\"bind()函数执行错误.\\n\"));\r\n\t\treturn false;\r\n\t}\r\n\r\n\tTRACE(_T(\"bind() 完成.\\n\"));\r\n\t\r\n\t// 开始进行监听\r\n\tif (::listen(m_pListenContext->m_Socket, SOMAXCONN) == SOCKET_ERROR)\r\n\t{\r\n\t\t_ShowMessage(_T(\"Listen()函数执行出现错误.\\n\"));\r\n\t\treturn false;\r\n\t}\r\n\r\n\tTRACE(_T(\"Listen() 完成.\\n\"));\r\n\t\r\n\t// 使用AcceptEx函数，因为这个是属于WinSock2规范之外的微软另外提供的扩展函数\r\n\t// 所以需要额外获取一下函数的指针，\r\n\t// 获取AcceptEx函数指针\r\n\tDWORD dwBytes = 0;\r\n\tif (SOCKET_ERROR == WSAIoctl(\r\n\t\tm_pListenContext->m_Socket,\r\n\t\tSIO_GET_EXTENSION_FUNCTION_POINTER,\r\n\t\t&GuidAcceptEx,\r\n\t\tsizeof(GuidAcceptEx),\r\n\t\t&m_lpfnAcceptEx,\r\n\t\tsizeof(m_lpfnAcceptEx),\r\n\t\t&dwBytes,\r\n\t\tNULL,\r\n\t\tNULL))\r\n\t{\r\n\t\t_ShowMessage(_T(\"WSAIoctl 未能获取AcceptEx函数指针。错误代码: %d\\n\"), WSAGetLastError());\r\n\t\t_Uninitialize();\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// 获取GetAcceptExSockAddrs函数指针，也是同理\r\n\tint nRet = ::WSAIoctl(\r\n\t\tm_pListenContext->m_Socket,\r\n\t\tSIO_GET_EXTENSION_FUNCTION_POINTER,\r\n\t\t&GuidGetAcceptExSockAddrs,\r\n\t\tsizeof(GuidGetAcceptExSockAddrs),\r\n\t\t&m_lpfnGetAcceptExSockAddrs,\r\n\t\tsizeof(m_lpfnGetAcceptExSockAddrs),\r\n\t\t&dwBytes,\r\n\t\tNULL,\r\n\t\tNULL);\r\n\tif (nRet == SOCKET_ERROR)\r\n\t{\r\n\t\t_ShowMessage(_T(\"WSAIoctl 未能获取GuidGetAcceptExSockAddrs函数指针。错误代码: %d\\n\"), ::WSAGetLastError());\r\n\t\t_Uninitialize();\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// 为AcceptEx 准备参数，然后投递AcceptEx I/O请求\r\n\tfor (int i = 0; i < MAX_POST_ACCEPT; ++i)\r\n\t{\r\n\t\t// 新建一个IO_CONTEXT\r\n\t\tPER_IO_CONTEXT* pAcceptIoContext = m_pListenContext->GetNewIoContext();\r\n\t\tif (!_PostAccept(pAcceptIoContext))\r\n\t\t{\r\n\t\t\tm_pListenContext->RemoveContext(pAcceptIoContext);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t_ShowMessage(_T(\"投递 %d 个AcceptEx请求完毕\"), MAX_POST_ACCEPT);\r\n\r\n\treturn true;\r\n}\r\n\r\n//\t最后释放掉所有资源\r\nvoid CIOCPModel::_Uninitialize()\r\n{\r\n\t// 删除客户端列表的互斥量\r\n\t::DeleteCriticalSection(&m_csContextList);\r\n\r\n\t// 关闭系统退出事件句柄\r\n\tRELEASE_HANDLE(m_hShutdownEvent);\r\n\r\n\t// 释放工作者线程句柄指针\r\n\tfor (int i = 0; i < m_nThreads; i++)\r\n\t{\r\n\t\tRELEASE_HANDLE(m_phWorkerThreads[i]);\r\n\t}\r\n\r\n\tRELEASE(m_phWorkerThreads);\r\n\r\n\t// 关闭IOCP句柄\r\n\tRELEASE_HANDLE(m_hIOCompletionPort);\r\n\r\n\t// 关闭监听Socket\r\n\tRELEASE(m_pListenContext);\r\n\r\n\t_ShowMessage(_T(\"释放资源完毕.\\n\"));\r\n}\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\t    投递完成端口请求\r\n//\r\n//====================================================================================\r\n\r\n// 投递Accept请求\r\nbool CIOCPModel::_PostAccept(PER_IO_CONTEXT* pAcceptIoContext)\r\n{\r\n\t// 准备参数\r\n\tDWORD dwBytes = 0;\r\n\tpAcceptIoContext->m_OpType = ACCEPT_POSTED;\r\n\tWSABUF* p_wbuf = &pAcceptIoContext->m_wsaBuf;\r\n\tOVERLAPPED* p_ol = &pAcceptIoContext->m_Overlapped;\r\n\r\n\t// 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 ) \r\n\tpAcceptIoContext->m_sockAccept = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);\r\n\tif (pAcceptIoContext->m_sockAccept == INVALID_SOCKET)\r\n\t{\r\n\t\t_ShowMessage(_T(\"创建用于Accept的Socket失败！错误代码: %d\"), WSAGetLastError());\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// 投递AcceptEx\r\n\tif (!m_lpfnAcceptEx(m_pListenContext->m_Socket, pAcceptIoContext->m_sockAccept, p_wbuf->buf, p_wbuf->len - ((sizeof(SOCKADDR_IN) + 16) * 2),\r\n\t\tsizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, p_ol))\r\n\t{\r\n\t\tif (WSA_IO_PENDING != ::WSAGetLastError())\r\n\t\t{\r\n\t\t\t_ShowMessage(_T(\"投递 AcceptEx 请求失败，错误代码: %d\"), ::WSAGetLastError());\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n//传入的是ListenSocket的Context，我们需要复制一份出来给新连入的Socket用\r\n// 原来的Context还是要在上面继续投递下一个Accept请求\r\nbool CIOCPModel::_DoAccept(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext)\r\n{\r\n\tSOCKADDR_IN* ClientAddr = NULL;\r\n\tSOCKADDR_IN* LocalAddr = NULL;\r\n\tint remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);\r\n\r\n\t// 1. 首先取得连入客户端的地址信息\r\n\t// 这个 m_lpfnGetAcceptExSockAddrs 不得了啊~~~~~~\r\n\t// 不但可以取得客户端和本地端的地址信息，还能顺便取出客户端发来的第一组数据，老强大了...\r\n\tm_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN) + 16) * 2),\r\n\t\tsizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);\r\n\r\n\t_ShowMessage(_T(\"客户端 %s:%d 连入.\"), ::inet_ntoa(ClientAddr->sin_addr), ::ntohs(ClientAddr->sin_port));\r\n\t_ShowMessage(_T(\"客户额 %s:%d 信息：%s.\"), ::inet_ntoa(ClientAddr->sin_addr), ::ntohs(ClientAddr->sin_port), pIoContext->m_wsaBuf.buf);\r\n\r\n\r\n\t// 2. 这里需要注意，这里传入的这个是ListenSocket上的Context，这个Context我们还需要用于监听下一个连接\r\n\t// 所以我还得要将ListenSocket上的Context复制出来一份为新连入的Socket新建一个SocketContext\r\n\r\n\tPER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;\r\n\tpNewSocketContext->m_Socket = pIoContext->m_sockAccept;\r\n\tmemcpy(&(pNewSocketContext->m_ClientAddr), ClientAddr, sizeof(SOCKADDR_IN));\r\n\r\n\t// 参数设置完毕，将这个Socket和完成端口绑定(这也是一个关键步骤)\r\n\tif (!_AssociateWithIOCP(pNewSocketContext))\r\n\t{\r\n\t\tRELEASE(pNewSocketContext);\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// 3. 继续，建立其下的IoContext，用于在这个Socket上投递第一个Recv数据请求\r\n\tPER_IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();\r\n\tpNewIoContext->m_OpType = RECV_POSTED;\r\n\tpNewIoContext->m_sockAccept = pNewSocketContext->m_Socket;\r\n\t// 如果Buffer需要保留，就自己拷贝一份出来\r\n\t//memcpy( pNewIoContext->m_szBuffer,pIoContext->m_szBuffer,MAX_BUFFER_LEN );\r\n\r\n\t// 绑定完毕之后，就可以开始在这个Socket上投递完成请求了\r\n\tif (!_PostRecv(pNewIoContext))\r\n\t{\r\n\t\tpNewSocketContext->RemoveContext(pNewIoContext);\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// 4. 如果投递成功，那么就把这个有效的客户端信息，加入到ContextList中去(需要统一管理，方便释放资源)\r\n\t_AddToContextList(pNewSocketContext);\r\n\r\n\t// 5. 使用完毕之后，把Listen Socket的那个IoContext重置，然后准备投递新的AcceptEx\r\n\tpIoContext->ResetBuffer();\r\n\treturn this->_PostAccept(pIoContext);\r\n}\r\n\r\n// 投递接收数据请求\r\nbool CIOCPModel::_PostRecv(PER_IO_CONTEXT* pIoContext)\r\n{\r\n\t// 初始化变量\r\n\tDWORD dwFlags = 0;\r\n\tDWORD dwBytes = 0;\r\n\tWSABUF* p_wbuf = &pIoContext->m_wsaBuf;\r\n\tOVERLAPPED* p_ol = &pIoContext->m_Overlapped;\r\n\r\n\tpIoContext->ResetBuffer();\r\n\tpIoContext->m_OpType = RECV_POSTED;\r\n\r\n\t// 初始化完成后，，投递WSARecv请求\r\n\tint nBytesRecv = ::WSARecv(pIoContext->m_sockAccept, p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL);\r\n\r\n\t// 如果返回值错误，并且错误的代码并非是Pending的话，那就说明这个重叠请求失败了\r\n\tif (nBytesRecv == SOCKET_ERROR && ::WSAGetLastError() == WSA_IO_PENDING)\r\n\t{\r\n\t\t_ShowMessage(_T(\"投递第一个WSARecv失败！\"));\r\n\t\treturn false;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\n// 在有接收的数据到达的时候，进行处理\r\nbool CIOCPModel::_DoRecv(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext)\r\n{\r\n\t// 先把上一次的数据显示出现，然后就重置状态，发出下一个Recv请求\r\n\tSOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;\r\n\t_ShowMessage(_T(\"收到  %s:%d 信息：%s\"), ::inet_ntoa(ClientAddr->sin_addr), ::ntohs(ClientAddr->sin_port), pIoContext->m_wsaBuf.buf);\r\n\r\n\t// 然后开始投递下一个WSARecv请求\r\n\treturn _PostRecv(pIoContext);\r\n}\r\n\r\n// 将句柄(Socket)绑定到完成端口中\r\nbool CIOCPModel::_AssociateWithIOCP(PER_SOCKET_CONTEXT* pContext)\r\n{\r\n\t// 将用于和客户端通信的SOCKET绑定到完成端口中\r\n\tHANDLE hTemp = ::CreateIoCompletionPort((HANDLE)pContext->m_Socket, m_hIOCompletionPort, (DWORD)pContext, 0);\r\n\tif (hTemp == NULL)\r\n\t{\r\n\t\t_ShowMessage(_T(\"执行CreateIoCompletionPort()出现错误.错误代码：%d\"), ::GetLastError());\r\n\t\treturn false;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\t    ContextList 相关操作\r\n//\r\n//====================================================================================\r\n\r\n\r\n// 将客户端的相关信息存储到数组中\r\nvoid CIOCPModel::_AddToContextList(PER_SOCKET_CONTEXT* pHandleData)\r\n{\r\n\t::EnterCriticalSection(&m_csContextList);\r\n\tm_arrayClientContext.Add(pHandleData);\r\n\t::LeaveCriticalSection(&m_csContextList);\r\n}\r\n\r\n//\t移除某个特定的Context\r\nvoid CIOCPModel::_RemoveContext(PER_SOCKET_CONTEXT* pSocketContext)\r\n{\r\n\t::EnterCriticalSection(&m_csContextList);\r\n\r\n\tfor (int i = 0; i < m_arrayClientContext.GetCount(); i++)\r\n\t{\r\n\t\tif (m_arrayClientContext.GetAt(i) == pSocketContext)\r\n\t\t{\r\n\t\t\tRELEASE(pSocketContext);\r\n\t\t\tm_arrayClientContext.RemoveAt(i);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\t::LeaveCriticalSection(&m_csContextList);\r\n}\r\n\r\n// 清空客户端信息\r\nvoid CIOCPModel::_ClearContextList()\r\n{\r\n\t::EnterCriticalSection(&m_csContextList);\r\n\r\n\tfor (int i = 0; i < m_arrayClientContext.GetCount(); i++)\r\n\t{\r\n\t\tdelete m_arrayClientContext.GetAt(i);\r\n\t}\r\n\r\n\tm_arrayClientContext.RemoveAll();\r\n\r\n\t::LeaveCriticalSection(&m_csContextList);\r\n}\r\n\r\n\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\t       其他辅助函数定义\r\n//\r\n//====================================================================================\r\n\r\n// 获得本机的IP地址\r\nCString CIOCPModel::GetLocalIP()\r\n{\r\n\t// 获得本机主机名\r\n\tchar hostname[MAX_PATH] = { 0 };\r\n\t::gethostname(hostname, MAX_PATH);\r\n\tstruct hostent FAR* lpHostEnt = ::gethostbyname(hostname);\r\n\tif (lpHostEnt == NULL)\r\n\t\treturn DEFAULT_IP;\r\n\r\n\t// 取得IP地址列表中的第一个为返回的IP(因为一台主机可能会绑定多个IP)\r\n\tLPSTR lpAddr = lpHostEnt->h_addr_list[0];\r\n\r\n\t// 将IP地址转化成字符串形式\r\n\tstruct in_addr inAddr;\r\n\tmemmove(&inAddr, lpAddr, 4);\r\n\tm_strIP = CString(inet_ntoa(inAddr));\r\n\r\n\treturn m_strIP;\r\n}\r\n\r\n// 获得本机中处理器的数量\r\nint CIOCPModel::_GetNoOfProcessors()\r\n{\r\n\tSYSTEM_INFO si;\r\n\t::GetSystemInfo(&si);\r\n\treturn si.dwNumberOfProcessors;\r\n}\r\n\r\n// 在主界面中显示提示信息\r\nvoid CIOCPModel::_ShowMessage(const CString szFormat, ...) const\r\n{\r\n\t// 根据传入的参数格式化字符串\r\n\tCString   strMessage;\r\n\tva_list   arglist;\r\n\r\n\t// 处理变长参数\r\n\tva_start(arglist, szFormat);\r\n\tstrMessage.FormatV(szFormat, arglist);\r\n\tva_end(arglist);\r\n\r\n\t// 在主界面中显示\r\n\tCMainDlg* pMain = (CMainDlg*)m_pMain;\r\n\tif (m_pMain != NULL)\r\n\t{\r\n\t\tpMain->AddInformation(strMessage);\r\n\t\tTRACE(strMessage + _T(\"\\n\"));\r\n\t}\r\n}\r\n\r\n// 判断客户端Socket是否已经断开，否则在一个无效的Socket上投递WSARecv操作会出现异常\r\n// 使用的方法是尝试向这个socket发送数据，判断这个socket调用的返回值\r\n// 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候，服务器端是无法收到客户端断开的通知的\r\nbool CIOCPModel::_IsSocketAlive(SOCKET s)\r\n{\r\n\tint nByteSent = send(s, \"\", 0, 0);\r\n\tif (nByteSent == SOCKET_ERROR) \r\n\t\treturn false;\r\n\r\n\treturn true;\r\n}\r\n\r\n// 显示并处理完成端口上的错误\r\nbool CIOCPModel::HandleError(PER_SOCKET_CONTEXT* pContext, const DWORD& dwErr)\r\n{\r\n\t// 如果是超时了，就再继续等吧  \r\n\tif (WAIT_TIMEOUT == dwErr)\r\n\t{\r\n\t\t// 确认客户端是否还活着...\r\n\t\tif (!_IsSocketAlive(pContext->m_Socket))\r\n\t\t{\r\n\t\t\t_ShowMessage(_T(\"检测到客户端异常退出！\"));\r\n\t\t\t_RemoveContext(pContext);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t_ShowMessage(_T(\"网络操作超时！重试中...\"));\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\r\n\t// 可能是客户端异常退出了\r\n\telse if (dwErr == ERROR_NETNAME_DELETED)\r\n\t{\r\n\t\t_ShowMessage(_T(\"检测到客户端异常退出！\"));\r\n\t\t_RemoveContext(pContext);\r\n\t\treturn true;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t_ShowMessage(_T(\"完成端口操作出现错误，线程退出。错误代码：%d\"), dwErr);\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPModel.h",
    "content": "/*\r\n==========================================================================\r\n\r\nPurpose:\r\n\r\n\t* 这个类CIOCPModel是本代码的核心类，用于说明WinSock服务器端编程模型中的\r\n\t  完成端口(IOCP)的使用方法，并使用MFC对话框程序来调用这个类实现了基本的\r\n\t  服务器网络通信的功能。\r\n\r\n\t* 其中的PER_IO_DATA结构体是封装了用于每一个重叠操作的参数\r\n\t  PER_HANDLE_DATA 是封装了用于每一个Socket的参数，也就是用于每一个完成端口的参数\r\n\r\nNotes:\r\n\r\n\t* 具体讲明了服务器端建立完成端口、建立工作者线程、投递Recv请求、投递Accept请求的方法，\r\n\t  所有的客户端连入的Socket都需要绑定到IOCP上，所有从客户端发来的数据，都会实时显示到\r\n\t  主界面中去。\r\n\r\n==========================================================================\r\n*/\r\n\r\n#pragma once\r\n\r\n// winsock 2 的头文件和库\r\n#include <winsock2.h>\r\n#include <MSWSock.h>\r\n#pragma comment(lib,\"ws2_32.lib\")\r\n\r\n// 缓冲区长度 (1024*8)\r\n// 之所以为什么设置8K，也是一个江湖上的经验值\r\n// 如果确实客户端发来的每组数据都比较少，那么就设置得小一些，省内存\r\n#define MAX_BUFFER_LEN        8192  \r\n// 默认端口\r\n#define DEFAULT_PORT          8080    \r\n// 默认IP地址\r\n#define DEFAULT_IP            _T(\"127.0.0.1\")\r\n\r\n\r\n//////////////////////////////////////////////////////////////////\r\n// 在完成端口上投递的I/O操作的类型\r\ntypedef enum _OPERATION_TYPE\r\n{\r\n\tACCEPT_POSTED,                     // 标志投递的Accept操作\r\n\tSEND_POSTED,                       // 标志投递的是发送操作\r\n\tRECV_POSTED,                       // 标志投递的是接收操作\r\n\tNULL_POSTED                        // 用于初始化，无意义\r\n\r\n}OPERATION_TYPE;\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\t单IO数据结构体定义(用于每一个重叠操作的参数)\r\n//\r\n//====================================================================================\r\n\r\ntypedef struct _PER_IO_CONTEXT\r\n{\r\n\tOVERLAPPED     m_Overlapped;                               // 每一个重叠网络操作的重叠结构(针对每一个Socket的每一个操作，都要有一个)              \r\n\tSOCKET         m_sockAccept;                               // 这个网络操作所使用的Socket\r\n\tWSABUF         m_wsaBuf;                                   // WSA类型的缓冲区，用于给重叠操作传参数的\r\n\tchar           m_szBuffer[MAX_BUFFER_LEN];                 // 这个是WSABUF里具体存字符的缓冲区\r\n\tOPERATION_TYPE m_OpType;                                   // 标识网络操作的类型(对应上面的枚举)\r\n\r\n\t// 初始化\r\n\t_PER_IO_CONTEXT()\r\n\t{\r\n\t\tZeroMemory(&m_Overlapped, sizeof(m_Overlapped));\r\n\t\tZeroMemory(m_szBuffer, MAX_BUFFER_LEN);\r\n\t\tm_sockAccept = INVALID_SOCKET;\r\n\t\tm_wsaBuf.buf = m_szBuffer;\r\n\t\tm_wsaBuf.len = MAX_BUFFER_LEN;\r\n\t\tm_OpType = NULL_POSTED;\r\n\t}\r\n\t// 释放掉Socket\r\n\t~_PER_IO_CONTEXT()\r\n\t{\r\n\t\tif (m_sockAccept != INVALID_SOCKET)\r\n\t\t{\r\n\t\t\tclosesocket(m_sockAccept);\r\n\t\t\tm_sockAccept = INVALID_SOCKET;\r\n\t\t}\r\n\t}\r\n\t// 重置缓冲区内容\r\n\tvoid ResetBuffer()\r\n\t{\r\n\t\tZeroMemory(m_szBuffer, MAX_BUFFER_LEN);\r\n\t}\r\n\r\n} PER_IO_CONTEXT, * PPER_IO_CONTEXT;\r\n\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\t单句柄数据结构体定义(用于每一个完成端口，也就是每一个Socket的参数)\r\n//\r\n//====================================================================================\r\n\r\ntypedef struct _PER_SOCKET_CONTEXT\r\n{\r\n\tSOCKET      m_Socket;                                  // 每一个客户端连接的Socket\r\n\tSOCKADDR_IN m_ClientAddr;                              // 客户端的地址\r\n\tCArray<_PER_IO_CONTEXT*> m_arrayIoContext;             // 客户端网络操作的上下文数据，\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   // 也就是说对于每一个客户端Socket，是可以在上面同时投递多个IO请求的\r\n\r\n\t// 初始化\r\n\t_PER_SOCKET_CONTEXT()\r\n\t{\r\n\t\tm_Socket = INVALID_SOCKET;\r\n\t\tmemset(&m_ClientAddr, 0, sizeof(m_ClientAddr));\r\n\t}\r\n\r\n\t// 释放资源\r\n\t~_PER_SOCKET_CONTEXT()\r\n\t{\r\n\t\tif (m_Socket != INVALID_SOCKET)\r\n\t\t{\r\n\t\t\tclosesocket(m_Socket);\r\n\t\t\tm_Socket = INVALID_SOCKET;\r\n\t\t}\r\n\t\t// 释放掉所有的IO上下文数据\r\n\t\tfor (int i = 0; i < m_arrayIoContext.GetCount(); i++)\r\n\t\t{\r\n\t\t\tdelete m_arrayIoContext.GetAt(i);\r\n\t\t}\r\n\t\tm_arrayIoContext.RemoveAll();\r\n\t}\r\n\r\n\t// 获取一个新的IoContext\r\n\t_PER_IO_CONTEXT* GetNewIoContext()\r\n\t{\r\n\t\t_PER_IO_CONTEXT* p = new _PER_IO_CONTEXT;\r\n\r\n\t\tm_arrayIoContext.Add(p);\r\n\r\n\t\treturn p;\r\n\t}\r\n\r\n\t// 从数组中移除一个指定的IoContext\r\n\tvoid RemoveContext(_PER_IO_CONTEXT* pContext)\r\n\t{\r\n\t\tASSERT(pContext != NULL);\r\n\r\n\t\tfor (int i = 0; i < m_arrayIoContext.GetCount(); i++)\r\n\t\t{\r\n\t\t\tif (pContext == m_arrayIoContext.GetAt(i))\r\n\t\t\t{\r\n\t\t\t\tdelete pContext;\r\n\t\t\t\tpContext = NULL;\r\n\t\t\t\tm_arrayIoContext.RemoveAt(i);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n} PER_SOCKET_CONTEXT, * PPER_SOCKET_CONTEXT;\r\n\r\n\r\n\r\n\r\n//====================================================================================\r\n//\r\n//\t\t\t\tCIOCPModel类定义\r\n//\r\n//====================================================================================\r\n\r\n// 工作者线程的线程参数\r\nclass CIOCPModel;\r\ntypedef struct _tagThreadParams_WORKER\r\n{\r\n\tCIOCPModel* pIOCPModel;                                   // 类指针，用于调用类中的函数\r\n\tint         nThreadNo;                                    // 线程编号\r\n\r\n} THREADPARAMS_WORKER, * PTHREADPARAM_WORKER;\r\n\r\n// CIOCPModel类\r\nclass CIOCPModel\r\n{\r\npublic:\r\n\tCIOCPModel(void);\r\n\t~CIOCPModel(void);\r\n\r\npublic:\r\n\t// 启动服务器\r\n\tbool Start();\r\n\r\n\t//\t停止服务器\r\n\tvoid Stop();\r\n\r\n\t// 加载Socket库\r\n\tbool LoadSocketLib();\r\n\r\n\t// 卸载Socket库，彻底完事\r\n\tvoid UnloadSocketLib() { WSACleanup(); }\r\n\r\n\t// 获得本机的IP地址\r\n\tCString GetLocalIP();\r\n\r\n\t// 设置监听端口\r\n\tvoid SetPort(const int& nPort) { m_nPort = nPort; }\r\n\r\n\t// 设置主界面的指针，用于调用显示信息到界面中\r\n\tvoid SetMainDlg(CDialog* p) { m_pMain = p; }\r\n\r\nprivate:\r\n\t// 初始化IOCP\r\n\tbool _InitializeIOCP();\r\n\r\n\t// 初始化Socket\r\n\tbool _InitializeListenSocket();\r\n\r\n\t// 最后释放资源\r\n\tvoid _Uninitialize();\r\n\r\n\t// 投递Accept请求\r\n\tbool _PostAccept(PER_IO_CONTEXT* pAcceptIoContext);\r\n\r\n\t// 投递接收数据请求\r\n\tbool _PostRecv(PER_IO_CONTEXT* pIoContext);\r\n\r\n\t// 在有客户端连入的时候，进行处理\r\n\tbool _DoAccept(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext);\r\n\r\n\t// 在有接收的数据到达的时候，进行处理\r\n\tbool _DoRecv(PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext);\r\n\r\n\t// 将客户端的相关信息存储到数组中\r\n\tvoid _AddToContextList(PER_SOCKET_CONTEXT* pSocketContext);\r\n\r\n\t// 将客户端的信息从数组中移除\r\n\tvoid _RemoveContext(PER_SOCKET_CONTEXT* pSocketContext);\r\n\r\n\t// 清空客户端信息\r\n\tvoid _ClearContextList();\r\n\r\n\t// 将句柄绑定到完成端口中\r\n\tbool _AssociateWithIOCP(PER_SOCKET_CONTEXT* pContext);\r\n\r\n\t// 处理完成端口上的错误\r\n\tbool HandleError(PER_SOCKET_CONTEXT* pContext, const DWORD& dwErr);\r\n\r\n\t// 线程函数，为IOCP请求服务的工作者线程\r\n\tstatic DWORD WINAPI _WorkerThread(LPVOID lpParam);\r\n\r\n\t// 获得本机的处理器数量\r\n\tint _GetNoOfProcessors();\r\n\r\n\t// 判断客户端Socket是否已经断开\r\n\tbool _IsSocketAlive(SOCKET s);\r\n\r\n\t// 在主界面中显示信息\r\n\tvoid _ShowMessage(const CString szFormat, ...) const;\r\n\r\nprivate:\r\n\t// 用来通知线程系统退出的事件，为了能够更好的退出线程\r\n\tHANDLE                       m_hShutdownEvent;              \r\n\t// 完成端口的句柄\r\n\tHANDLE                       m_hIOCompletionPort;           \r\n\t// 工作者线程的句柄指针\r\n\tHANDLE* m_phWorkerThreads;             \r\n\t// 生成的线程数量\r\n\tint\t\t                     m_nThreads;                    \r\n\t// 服务器端的IP地址\r\n\tCString                      m_strIP;  \r\n\t// 服务器端的监听端口\r\n\tint                          m_nPort;                      \r\n\t// 主界面的界面指针，用于在主界面中显示消息\r\n\tCDialog* m_pMain;                       \r\n\t// 用于Worker线程同步的互斥量\r\n\tCRITICAL_SECTION             m_csContextList;               \r\n\t// 客户端Socket的Context信息\r\n\tCArray<PER_SOCKET_CONTEXT*>  m_arrayClientContext;                  \r\n\t// 用于监听的Socket的Context信息\r\n\tPER_SOCKET_CONTEXT*\t\t\t m_pListenContext;              \r\n\t// AcceptEx 和 GetAcceptExSockaddrs 的函数指针，用于调用这两个扩展函数\r\n\tLPFN_ACCEPTEX                m_lpfnAcceptEx;                \r\n\tLPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs;\r\n\r\n};\r\n\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerApp.cpp",
    "content": "// IOCPServerApp.cpp : 定义应用程序的类行为。\r\n//\r\n#include \"stdafx.h\"\r\n#include \"IOCPServerApp.h\"\r\n#include \"MainDlg.h\"\r\n\r\n#ifdef _DEBUG\r\n#define new DEBUG_NEW\r\n#endif\r\n\r\n\r\n// CPiggyIOCPServerApp\r\n\r\nBEGIN_MESSAGE_MAP(CIOCPServerApp, CWinApp)\r\n\tON_COMMAND(ID_HELP, &CWinApp::OnHelp)\r\nEND_MESSAGE_MAP()\r\n\r\n\r\n// CPiggyIOCPServerApp 构造\r\nCIOCPServerApp::CIOCPServerApp()\r\n{\r\n\t// TODO: 在此处添加构造代码，\r\n\t// 将所有重要的初始化放置在 InitInstance 中\r\n}\r\n\r\n\r\n// 唯一的一个 CIOCPServerApp 对象\r\nCIOCPServerApp theApp;\r\n\r\n\r\n// CIOCPServerApp 初始化\r\nBOOL CIOCPServerApp::InitInstance()\r\n{\r\n\t// 如果一个运行在 Windows XP 上的应用程序清单指定要\r\n\t// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式，\r\n\t//则需要 InitCommonControlsEx()。否则，将无法创建窗口。\r\n\tINITCOMMONCONTROLSEX InitCtrls;\r\n\tInitCtrls.dwSize = sizeof(InitCtrls);\r\n\t// 将它设置为包括所有要在应用程序中使用的\r\n\t// 公共控件类。\r\n\tInitCtrls.dwICC = ICC_WIN95_CLASSES;\r\n\tInitCommonControlsEx(&InitCtrls);\r\n\r\n\tCWinApp::InitInstance();\r\n\r\n\t// 标准初始化\r\n\t// 如果未使用这些功能并希望减小\r\n\t// 最终可执行文件的大小，则应移除下列\r\n\t// 不需要的特定初始化例程\r\n\t// 更改用于存储设置的注册表项\r\n\t// TODO: 应适当修改该字符串，\r\n\t// 例如修改为公司或组织名\r\n\tSetRegistryKey(_T(\"应用程序向导生成的本地应用程序\"));\r\n\r\n\tCMainDlg dlg;\r\n\tm_pMainWnd = &dlg;\r\n\tINT_PTR nResponse = dlg.DoModal();\r\n\tif (nResponse == IDOK)\r\n\t{\r\n\t\t// TODO: 在此放置处理何时用\r\n\t\t//  “确定”来关闭对话框的代码\r\n\t}\r\n\telse if (nResponse == IDCANCEL)\r\n\t{\r\n\t\t// TODO: 在此放置处理何时用\r\n\t\t//  “取消”来关闭对话框的代码\r\n\t}\r\n\r\n\t// 由于对话框已关闭，所以将返回 FALSE 以便退出应用程序，\r\n\t//  而不是启动应用程序的消息泵。\r\n\treturn FALSE;\r\n}\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerApp.h",
    "content": "// IOCPServerApp.h : PROJECT_NAME 应用程序的主头文件\r\n//\r\n\r\n#pragma once\r\n\r\n#ifndef __AFXWIN_H__\r\n\t#error \"在包含此文件之前包含“stdafx.h”以生成 PCH 文件\"\r\n#endif\r\n\r\n//#include \"resource.h\"\t\t// 主符号\r\n\r\n\r\n// CIOCPServerApp:\r\n// 有关此类的实现，请参阅 CIOCPServerApp.cpp\r\n//\r\nclass CIOCPServerApp : public CWinApp\r\n{\r\npublic:\r\n\tCIOCPServerApp();\r\n\r\n// 重写\r\n\tpublic:\r\n\tvirtual BOOL InitInstance();\r\n\r\n// 实现\r\n\r\n\tDECLARE_MESSAGE_MAP()\r\n};\r\n\r\nextern CIOCPServerApp theApp;"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerDemo.rc",
    "content": "// Microsoft Visual C++ generated resource script.\r\n//\r\n#include \"resource.h\"\r\n\r\n#define APSTUDIO_READONLY_SYMBOLS\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 2 resource.\r\n//\r\n#ifndef APSTUDIO_INVOKED\r\n#include \"targetver.h\"\r\n#endif\r\n#include \"afxres.h\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#undef APSTUDIO_READONLY_SYMBOLS\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// Chinese (Simplified, PRC) resources\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)\r\nLANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED\r\n#pragma code_page(936)\r\n\r\n#ifdef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// TEXTINCLUDE\r\n//\r\n\r\n1 TEXTINCLUDE \r\nBEGIN\r\n    \"resource.h\\0\"\r\nEND\r\n\r\n2 TEXTINCLUDE \r\nBEGIN\r\n    \"#ifndef APSTUDIO_INVOKED\\r\\n\"\r\n    \"#include \"\"targetver.h\"\"\\r\\n\"\r\n    \"#endif\\r\\n\"\r\n    \"#include \"\"afxres.h\"\"\\r\\n\"\r\n    \"\\0\"\r\nEND\r\n\r\n3 TEXTINCLUDE \r\nBEGIN\r\n    \"#define _AFX_NO_SPLITTER_RESOURCES\\r\\n\"\r\n    \"#define _AFX_NO_OLE_RESOURCES\\r\\n\"\r\n    \"#define _AFX_NO_TRACKER_RESOURCES\\r\\n\"\r\n    \"#define _AFX_NO_PROPERTY_RESOURCES\\r\\n\"\r\n    \"\\r\\n\"\r\n    \"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)\\r\\n\"\r\n    \"LANGUAGE 4, 2\\r\\n\"\r\n    \"#pragma code_page(936)\\r\\n\"\r\n    \"#include \"\"res\\\\PiggyIOCPServer.rc2\"\"  // ?? Microsoft Visual C++ ??-|??o???\\r\\n\"\r\n    \"#include \"\"l.CHS\\\\afxres.rc\"\"      // ?o???|?t\\r\\n\"\r\n    \"#endif\\r\\n\"\r\n    \"\\0\"\r\nEND\r\n\r\n#endif    // APSTUDIO_INVOKED\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Icon\r\n//\r\n\r\n// Icon with lowest ID value placed first to ensure application icon\r\n// remains consistent on all systems.\r\nIDR_MAINFRAME           ICON                    \"res\\\\IOCPServerDemo.ico\"\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Dialog\r\n//\r\n\r\nIDD_PIGGYIOCPSERVER_DIALOG DIALOGEX 0, 0, 334, 213\r\nSTYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r\nEXSTYLE WS_EX_APPWINDOW\r\nCAPTION \"IOCP Server Demo\"\r\nFONT 9, \"MS Shell Dlg\", 0, 0, 0x1\r\nBEGIN\r\n    DEFPUSHBUTTON   \"ʼ\",IDOK,59,192,50,14\r\n    PUSHBUTTON      \"˳\",IDCANCEL,221,192,50,14\r\n    LTEXT           \"IPַ\",IDC_STATIC,14,158,48,8\r\n    LTEXT           \"127.0.0.1\",IDC_STATIC_SERVERIP,64,159,79,8\r\n    PUSHBUTTON      \"ֹͣ\",IDC_STOP,139,192,50,14,WS_DISABLED\r\n    CONTROL         \"\",IDC_LIST_INFO,\"SysListView32\",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,320,141\r\n    LTEXT           \"˿ڣ\",IDC_STATIC,168,159,35,8\r\n    EDITTEXT        IDC_EDIT_PORT,214,157,40,14,ES_AUTOHSCROLL\r\nEND\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Version\r\n//\r\n\r\nVS_VERSION_INFO VERSIONINFO\r\n FILEVERSION 1,0,0,1\r\n PRODUCTVERSION 1,0,0,1\r\n FILEFLAGSMASK 0x3fL\r\n#ifdef _DEBUG\r\n FILEFLAGS 0x1L\r\n#else\r\n FILEFLAGS 0x0L\r\n#endif\r\n FILEOS 0x4L\r\n FILETYPE 0x1L\r\n FILESUBTYPE 0x0L\r\nBEGIN\r\n    BLOCK \"StringFileInfo\"\r\n    BEGIN\r\n        BLOCK \"080403a8\"\r\n        BEGIN\r\n            VALUE \"CompanyName\", \"TODO: <˾>\"\r\n            VALUE \"FileDescription\", \"TODO: <ļ˵>\"\r\n            VALUE \"FileVersion\", \"1.0.0.1\"\r\n            VALUE \"InternalName\", \"PiggyIOCPServer.exe\"\r\n            VALUE \"LegalCopyright\", \"TODO: (C) <˾>Ȩ\"\r\n            VALUE \"OriginalFilename\", \"IOCPServerDemo.exe\"\r\n            VALUE \"ProductName\", \"TODO: <Ʒ>\"\r\n            VALUE \"ProductVersion\", \"1.0.0.1\"\r\n        END\r\n    END\r\n    BLOCK \"VarFileInfo\"\r\n    BEGIN\r\n        VALUE \"Translation\", 0x804, 936\r\n    END\r\nEND\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// DESIGNINFO\r\n//\r\n\r\n#ifdef APSTUDIO_INVOKED\r\nGUIDELINES DESIGNINFO\r\nBEGIN\r\n    IDD_PIGGYIOCPSERVER_DIALOG, DIALOG\r\n    BEGIN\r\n        LEFTMARGIN, 7\r\n        RIGHTMARGIN, 327\r\n        TOPMARGIN, 7\r\n        BOTTOMMARGIN, 206\r\n    END\r\nEND\r\n#endif    // APSTUDIO_INVOKED\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// AFX_DIALOG_LAYOUT\r\n//\r\n\r\nIDD_PIGGYIOCPSERVER_DIALOG AFX_DIALOG_LAYOUT\r\nBEGIN\r\n    0\r\nEND\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// String Table\r\n//\r\n\r\n\r\n#endif    // Chinese (Simplified, PRC) resources\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n\r\n#ifndef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 3 resource.\r\n//\r\n#define _AFX_NO_SPLITTER_RESOURCES\r\n#define _AFX_NO_OLE_RESOURCES\r\n#define _AFX_NO_TRACKER_RESOURCES\r\n#define _AFX_NO_PROPERTY_RESOURCES\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)\r\nLANGUAGE 4, 2\r\n#pragma code_page(936)\r\n#include \"res\\IOCPServerDemo.rc2\"  // ?? Microsoft Visual C++ ??-|??o???\r\n#include \"l.CHS\\afxres.rc\"      // ?o???|?t\r\n#endif\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#endif    // not APSTUDIO_INVOKED"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerDemo.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.30406.217\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"IOCPServerDemo\", \"IOCPServerDemo.vcxproj\", \"{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {AD6FBEE8-A7FB-4B78-A759-2423F02128A5}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerDemo.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"gb2312\"?>\r\n<VisualStudioProject\r\n\tProjectType=\"Visual C++\"\r\n\tVersion=\"9.00\"\r\n\tName=\"PiggyIOCPServer\"\r\n\tProjectGUID=\"{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}\"\r\n\tRootNamespace=\"PiggyIOCPServer\"\r\n\tKeyword=\"MFCProj\"\r\n\tTargetFrameworkVersion=\"196613\"\r\n\t>\r\n\t<Platforms>\r\n\t\t<Platform\r\n\t\t\tName=\"Win32\"\r\n\t\t/>\r\n\t</Platforms>\r\n\t<ToolFiles>\r\n\t</ToolFiles>\r\n\t<Configurations>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|Win32\"\r\n\t\t\tOutputDirectory=\"$(SolutionDir)$(ConfigurationName)\"\r\n\t\t\tIntermediateDirectory=\"$(ConfigurationName)\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tUseOfMFC=\"1\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tMkTypLibCompatible=\"false\"\r\n\t\t\t\tValidateParameters=\"true\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"WIN32;_WINDOWS;_DEBUG\"\r\n\t\t\t\tMinimalRebuild=\"true\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"1\"\r\n\t\t\t\tUsePrecompiledHeader=\"2\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDebugInformationFormat=\"4\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tCulture=\"2052\"\r\n\t\t\t\tAdditionalIncludeDirectories=\"$(IntDir)\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tShowProgress=\"0\"\r\n\t\t\t\tLinkIncremental=\"0\"\r\n\t\t\t\tSuppressStartupBanner=\"false\"\r\n\t\t\t\tGenerateDebugInformation=\"true\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tTargetMachine=\"1\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|Win32\"\r\n\t\t\tOutputDirectory=\"$(SolutionDir)$(ConfigurationName)\"\r\n\t\t\tIntermediateDirectory=\"$(ConfigurationName)\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tUseOfMFC=\"1\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\tWholeProgramOptimization=\"1\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tMkTypLibCompatible=\"false\"\r\n\t\t\t\tValidateParameters=\"true\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"2\"\r\n\t\t\t\tEnableIntrinsicFunctions=\"true\"\r\n\t\t\t\tPreprocessorDefinitions=\"WIN32;_WINDOWS;NDEBUG\"\r\n\t\t\t\tMinimalRebuild=\"false\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"2\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tCulture=\"2052\"\r\n\t\t\t\tAdditionalIncludeDirectories=\"$(IntDir)\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tLinkIncremental=\"1\"\r\n\t\t\t\tGenerateDebugInformation=\"true\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tOptimizeReferences=\"2\"\r\n\t\t\t\tEnableCOMDATFolding=\"2\"\r\n\t\t\t\tTargetMachine=\"1\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t</Configurations>\r\n\t<References>\r\n\t</References>\r\n\t<Files>\r\n\t\t<Filter\r\n\t\t\tName=\"Source Files\"\r\n\t\t\tFilter=\"cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx\"\r\n\t\t\tUniqueIdentifier=\"{4FC737F1-C7A5-4376-A066-2A32D752A2FF}\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\IOCPModel.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\MainDlg.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\PiggyIOCPServer.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\stdafx.cpp\"\r\n\t\t\t\t>\r\n\t\t\t\t<FileConfiguration\r\n\t\t\t\t\tName=\"Debug|Win32\"\r\n\t\t\t\t\t>\r\n\t\t\t\t\t<Tool\r\n\t\t\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\t\t\tUsePrecompiledHeader=\"1\"\r\n\t\t\t\t\t/>\r\n\t\t\t\t</FileConfiguration>\r\n\t\t\t\t<FileConfiguration\r\n\t\t\t\t\tName=\"Release|Win32\"\r\n\t\t\t\t\t>\r\n\t\t\t\t\t<Tool\r\n\t\t\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\t\t\tUsePrecompiledHeader=\"1\"\r\n\t\t\t\t\t/>\r\n\t\t\t\t</FileConfiguration>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t\t<Filter\r\n\t\t\tName=\"Header Files\"\r\n\t\t\tFilter=\"h;hpp;hxx;hm;inl;inc;xsd\"\r\n\t\t\tUniqueIdentifier=\"{93995380-89BD-4b04-88EB-625FBE52EBFB}\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\ClientContext.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\IOCPModel.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\MainDlg.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\PiggyIOCPServer.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\Resource.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\stdafx.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\targetver.h\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t\t<Filter\r\n\t\t\tName=\"Resource Files\"\r\n\t\t\tFilter=\"rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav\"\r\n\t\t\tUniqueIdentifier=\"{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\res\\PiggyIOCPServer.ico\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\PiggyIOCPServer.rc\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\res\\PiggyIOCPServer.rc2\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\".\\XPStyle.manifest\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t\t<File\r\n\t\t\tRelativePath=\".\\ReadMe.txt\"\r\n\t\t\t>\r\n\t\t</File>\r\n\t</Files>\r\n\t<Globals>\r\n\t\t<Global\r\n\t\t\tName=\"RESOURCE_FILE\"\r\n\t\t\tValue=\"PiggyIOCPServer.rc\"\r\n\t\t/>\r\n\t</Globals>\r\n</VisualStudioProject>\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerDemo.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{9FE00D8B-927C-4E82-8F1E-27A9385FEC25}</ProjectGuid>\r\n    <RootNamespace>PiggyIOCPServer</RootNamespace>\r\n    <Keyword>MFCProj</Keyword>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n    <ProjectName>IOCPServerDemo</ProjectName>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <UseOfMfc>Static</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <UseOfMfc>Dynamic</UseOfMfc>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup>\r\n    <_ProjectFileVersion>12.0.30501.0</_ProjectFileVersion>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>$(SolutionDir)$(Configuration)\\</OutDir>\r\n    <IntDir>$(Configuration)\\</IntDir>\r\n    <LinkIncremental />\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>$(SolutionDir)$(Configuration)\\</OutDir>\r\n    <IntDir>$(Configuration)\\</IntDir>\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Midl>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MkTypLibCompatible>false</MkTypLibCompatible>\r\n      <ValidateAllParameters>true</ValidateAllParameters>\r\n    </Midl>\r\n    <ClCompile>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>\r\n      </MinimalRebuild>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <Culture>0x0804</Culture>\r\n      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n    </ResourceCompile>\r\n    <Link>\r\n      <ShowProgress>NotSet</ShowProgress>\r\n      <SuppressStartupBanner>false</SuppressStartupBanner>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Windows</SubSystem>\r\n      <TargetMachine>MachineX86</TargetMachine>\r\n      <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Midl>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MkTypLibCompatible>false</MkTypLibCompatible>\r\n      <ValidateAllParameters>true</ValidateAllParameters>\r\n    </Midl>\r\n    <ClCompile>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <Culture>0x0804</Culture>\r\n      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n    </ResourceCompile>\r\n    <Link>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Windows</SubSystem>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <TargetMachine>MachineX86</TargetMachine>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"IOCPModel.cpp\" />\r\n    <ClCompile Include=\"MainDlg.cpp\" />\r\n    <ClCompile Include=\"IOCPServerApp.cpp\" />\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"IOCPModel.h\" />\r\n    <ClInclude Include=\"MainDlg.h\" />\r\n    <ClInclude Include=\"IOCPServerApp.h\" />\r\n    <ClInclude Include=\"Resource.h\" />\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"res\\IOCPServerDemo.ico\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"IOCPServerDemo.rc\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <None Include=\"res\\IOCPServerDemo.rc2\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Manifest Include=\"XPStyle.manifest\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n  <ProjectExtensions>\r\n    <VisualStudio>\r\n      <UserProperties RESOURCE_FILE=\"PiggyIOCPServer.rc\" />\r\n    </VisualStudio>\r\n  </ProjectExtensions>\r\n</Project>"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/IOCPServerDemo.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"IOCPModel.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MainDlg.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"IOCPServerApp.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"IOCPModel.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MainDlg.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Resource.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"IOCPServerApp.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Manifest Include=\"XPStyle.manifest\">\r\n      <Filter>Resource Files</Filter>\r\n    </Manifest>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"res\\IOCPServerDemo.ico\">\r\n      <Filter>Resource Files</Filter>\r\n    </Image>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"IOCPServerDemo.rc\">\r\n      <Filter>Resource Files</Filter>\r\n    </ResourceCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <None Include=\"res\\IOCPServerDemo.rc2\">\r\n      <Filter>Resource Files</Filter>\r\n    </None>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/MainDlg.cpp",
    "content": "#include \"stdafx.h\"\r\n#include \"IOCPServerApp.h\"\r\n#include \"MainDlg.h\"\r\n\r\n#ifdef _DEBUG\r\n#define new DEBUG_NEW\r\n#endif\r\n\r\n// CMainDlg 对话框\r\nCMainDlg::CMainDlg(CWnd* pParent /*=NULL*/)\r\n\t: CDialog(CMainDlg::IDD, pParent)\r\n{\r\n\tm_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);\r\n}\r\n\r\nvoid CMainDlg::DoDataExchange(CDataExchange* pDX)\r\n{\r\n\tCDialog::DoDataExchange(pDX);\r\n}\r\n\r\nBEGIN_MESSAGE_MAP(CMainDlg, CDialog)\r\n\tON_WM_SYSCOMMAND()\r\n\tON_WM_PAINT()\r\n\tON_WM_QUERYDRAGICON()\r\n\t//}}AFX_MSG_MAP\r\n\tON_BN_CLICKED(IDOK, &CMainDlg::OnBnClickedOk)\r\n\tON_BN_CLICKED(IDC_STOP, &CMainDlg::OnBnClickedStop)\r\n\tON_WM_CLOSE()\r\n\t//ON_MESSAGE(WM_MSG_NEW_MSG,OnNewMsg)\r\n\tON_BN_CLICKED(IDCANCEL, &CMainDlg::OnBnClickedCancel)\r\n\tON_WM_DESTROY()\r\nEND_MESSAGE_MAP()\r\n\r\n\r\n// CMainDlg 消息处理程序\r\nBOOL CMainDlg::OnInitDialog()\r\n{\r\n\tCDialog::OnInitDialog();\r\n\r\n\t// 设置此对话框的图标。当应用程序主窗口不是对话框时，框架将自动\r\n\t//  执行此操作\r\n\tSetIcon(m_hIcon, TRUE);\t\t\t// 设置大图标\r\n\tSetIcon(m_hIcon, FALSE);\t\t// 设置小图标\r\n\r\n\t// 初始化界面信息\r\n\tInit();\r\n\r\n\treturn TRUE;  // 除非将焦点设置到控件，否则返回 TRUE\r\n}\r\n\r\nvoid CMainDlg::OnSysCommand(UINT nID, LPARAM lParam)\r\n{\t\r\n\tCDialog::OnSysCommand(nID, lParam);\t\r\n}\r\n\r\n// 如果向对话框添加最小化按钮，则需要下面的代码\r\n//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序，\r\n//  这将由框架自动完成。\r\nvoid CMainDlg::OnPaint()\r\n{\r\n\tif (IsIconic())\r\n\t{\r\n\t\tCPaintDC dc(this); // 用于绘制的设备上下文\r\n\r\n\t\tSendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);\r\n\r\n\t\t// 使图标在工作区矩形中居中\r\n\t\tint cxIcon = GetSystemMetrics(SM_CXICON);\r\n\t\tint cyIcon = GetSystemMetrics(SM_CYICON);\r\n\t\tCRect rect;\r\n\t\tGetClientRect(&rect);\r\n\t\tint x = (rect.Width() - cxIcon + 1) / 2;\r\n\t\tint y = (rect.Height() - cyIcon + 1) / 2;\r\n\r\n\t\t// 绘制图标\r\n\t\tdc.DrawIcon(x, y, m_hIcon);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tCDialog::OnPaint();\r\n\t}\r\n}\r\n\r\n//当用户拖动最小化窗口时系统调用此函数取得光标显示。\r\nHCURSOR CMainDlg::OnQueryDragIcon()\r\n{\r\n\treturn static_cast<HCURSOR>(m_hIcon);\r\n}\r\n\r\n// 初始化Socket库以及界面信息\r\nvoid CMainDlg::Init()\r\n{\r\n\t// 初始化Socket库\r\n\tif (!m_IOCP.LoadSocketLib())\r\n\t{\r\n\t\tAfxMessageBox(_T(\"加载Winsock 2.2失败，服务器端无法运行！\"));\r\n\t\tPostQuitMessage(0);\r\n\t}\r\n\r\n\t// 设置本机IP地址\r\n\tSetDlgItemText(IDC_STATIC_SERVERIP, m_IOCP.GetLocalIP());\r\n\t// 设置默认端口\r\n\tSetDlgItemInt(IDC_EDIT_PORT, DEFAULT_PORT);\r\n\t// 初始化列表\r\n\tInitListCtrl();\r\n\t// 绑定主界面指针(为了方便在界面中显示信息 )\r\n\tm_IOCP.SetMainDlg(this);\r\n}\r\n\r\n//\t开始监听\r\nvoid CMainDlg::OnBnClickedOk()\r\n{\r\n\tif (false == m_IOCP.Start())\r\n\t{\r\n\t\tAfxMessageBox(_T(\"服务器启动失败！\"));\r\n\t\treturn;\r\n\t}\r\n\r\n\tGetDlgItem(IDOK)->EnableWindow(FALSE);\r\n\tGetDlgItem(IDC_STOP)->EnableWindow(TRUE);\r\n}\r\n\r\n//\t结束监听\r\nvoid CMainDlg::OnBnClickedStop()\r\n{\r\n\tm_IOCP.Stop();\r\n\r\n\tGetDlgItem(IDC_STOP)->EnableWindow(FALSE);\r\n\tGetDlgItem(IDOK)->EnableWindow(TRUE);\r\n}\r\n\r\n//\t初始化List Control\r\nvoid CMainDlg::InitListCtrl()\r\n{\r\n\tCListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_INFO);\r\n\tpList->SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);\r\n\tpList->InsertColumn(0, _T(\"输出信息\"), LVCFMT_LEFT, 500);\r\n}\r\n\r\n//\t点击“退出”的时候，停止监听，清理Socket类库\r\nvoid CMainDlg::OnBnClickedCancel()\r\n{\r\n\t// 停止监听\r\n\tm_IOCP.Stop();\r\n\r\n\tCDialog::OnCancel();\r\n}\r\n\r\n//\t系统退出的时候，为确保资源释放，停止监听，清空Socket类库\r\nvoid CMainDlg::OnDestroy()\r\n{\r\n\tOnBnClickedCancel();\r\n\r\n\tCDialog::OnDestroy();\r\n}\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/MainDlg.h",
    "content": "// MainDlg.h : 头文件\r\n//\r\n#pragma once\r\n\r\n#include \"IOCPModel.h\"\r\n\r\n// CMainDlg 对话框\r\nclass CMainDlg : public CDialog\r\n{\r\npublic:\r\n\tCMainDlg(CWnd* pParent = NULL);\t// 标准构造函数\r\n\r\n// 对话框数据\r\n\tenum { IDD = IDD_PIGGYIOCPSERVER_DIALOG };\r\n\r\nprotected:\r\n\tvirtual void DoDataExchange(CDataExchange* pDX);\t// DDX/DDV 支持\r\n\r\n// 实现\r\nprotected:\r\n\tHICON m_hIcon;\r\n\r\n\t// 生成的消息映射函数\r\n\tvirtual BOOL OnInitDialog();\r\n\tafx_msg void OnSysCommand(UINT nID, LPARAM lParam);\r\n\tafx_msg void OnPaint();\r\n\tafx_msg HCURSOR OnQueryDragIcon();\r\n\t// 开始监听\r\n\tafx_msg void OnBnClickedOk();\r\n\t// 停止监听\r\n\tafx_msg void OnBnClickedStop();\r\n\t// \"退出\"按钮\r\n\tafx_msg void OnBnClickedCancel();\r\n\t//\t系统退出的时候，为确保资源释放，停止监听，清空Socket类库\r\n\tafx_msg void OnDestroy();\r\n\r\n\tDECLARE_MESSAGE_MAP()\r\n\r\nprivate:\r\n\t// 初始化Socket库以及界面信息\r\n\tvoid Init();\r\n\r\n\t// 初始化List控件\r\n\tvoid InitListCtrl();\r\n\r\npublic:\r\n\t// 当前客户端有新消息到来的时候，在主界面中显示新到来的信息(在类CIOCPModel中调用)\r\n\tvoid AddInformation(const CString& strInfo)\r\n\t{\r\n\t\tCListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_INFO);\r\n\t\tpList->InsertItem(0, strInfo);\r\n\t}\r\n\r\nprivate:\r\n\t// 主要对象，完成端口模型\r\n\tCIOCPModel m_IOCP;\r\n};\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/XPStyle.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n\r\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\r\n\r\n<assemblyIdentity\r\n\r\n  name=\"XP style manifest\"\r\n\r\n  processorArchitecture=\"x86\"\r\n\r\n  version=\"1.0.0.0\"\r\n\r\n  type=\"win32\"/>\r\n\r\n<dependency>\r\n\r\n  <dependentAssembly>\r\n\r\n    <assemblyIdentity\r\n\r\n      type=\"win32\"\r\n\r\n      name=\"Microsoft.Windows.Common-Controls\"\r\n\r\n      version=\"6.0.0.0\"\r\n\r\n      processorArchitecture=\"x86\"\r\n\r\n      publicKeyToken=\"6595b64144ccf1df\"\r\n\r\n      language=\"*\"\r\n\r\n    />\r\n\r\n  </dependentAssembly>\r\n\r\n</dependency>\r\n\r\n</assembly>"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/res/IOCPServerDemo.rc2",
    "content": "//\r\n// IOCPServerDemo.RC2 - Microsoft Visual C++ ֱӱ༭Դ\r\n//\r\n\r\n#ifdef APSTUDIO_INVOKED\r\n#error ļ Microsoft Visual C++ ༭\r\n#endif //APSTUDIO_INVOKED\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// ڴ˴ֶ༭Դ...\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n1 RT_MANIFEST \"XPStyle.manifest\"\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\r\n// Microsoft Visual C++ generated include file.\r\n// Used by IOCPServerDemo.rc\r\n//\r\n#define IDC_STOP                        3\r\n#define IDM_ABOUTBOX                    0x0010\r\n#define IDS_ABOUTBOX                    101\r\n#define IDD_PIGGYIOCPSERVER_DIALOG      102\r\n#define IDR_MAINFRAME                   128\r\n#define IDC_LIST2                       1001\r\n#define IDC_LIST_INFO                   1001\r\n#define IDC_EDIT_PORT                   1002\r\n#define IDC_STATIC_SERVERIP             1003\r\n\r\n// Next default values for new objects\r\n// \r\n#ifdef APSTUDIO_INVOKED\r\n#ifndef APSTUDIO_READONLY_SYMBOLS\r\n#define _APS_NEXT_RESOURCE_VALUE        130\r\n#define _APS_NEXT_COMMAND_VALUE         32771\r\n#define _APS_NEXT_CONTROL_VALUE         1004\r\n#define _APS_NEXT_SYMED_VALUE           101\r\n#endif\r\n#endif\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/stdafx.cpp",
    "content": "// stdafx.cpp : ֻ׼ļԴļ\r\n// IOCPServerDemo.pch ΪԤͷ\r\n// stdafx.obj ԤϢ\r\n#include \"stdafx.h\"\r\n\r\n\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/stdafx.h",
    "content": "// stdafx.h : ׼ϵͳļİļ\r\n// Ǿʹõĵ\r\n// ضĿİļ\r\n\r\n#pragma once\r\n\r\n#define _WINSOCK_DEPRECATED_NO_WARNINGS\r\n\r\n#ifndef _SECURE_ATL\r\n#define _SECURE_ATL 1\r\n#endif\r\n\r\n#ifndef VC_EXTRALEAN\r\n#define VC_EXTRALEAN            //  Windows ͷųʹõ\r\n#endif\r\n\r\n#include \"targetver.h\"\r\n\r\n#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // ĳЩ CString 캯ʽ\r\n\r\n// ر MFC ĳЩɷĺԵľϢ\r\n#define _AFX_ALL_WARNINGS\r\n\r\n#include <afxwin.h>         // MFC ͱ׼\r\n#include <afxext.h>         // MFC չ\r\n\r\n\r\n\r\n\r\n\r\n#ifndef _AFX_NO_OLE_SUPPORT\r\n#include <afxdtctl.h>           // MFC  Internet Explorer 4 ؼ֧\r\n#endif\r\n#ifndef _AFX_NO_AFXCMN_SUPPORT\r\n#include <afxcmn.h>                     // MFC  Windows ؼ֧\r\n#endif // _AFX_NO_AFXCMN_SUPPORT\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef _UNICODE\r\n#if defined _M_IX86\r\n#pragma comment(linker,\"/manifestdependency:\\\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\\\"\")\r\n#elif defined _M_IA64\r\n#pragma comment(linker,\"/manifestdependency:\\\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\\\"\")\r\n#elif defined _M_X64\r\n#pragma comment(linker,\"/manifestdependency:\\\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\\\"\")\r\n#else\r\n#pragma comment(linker,\"/manifestdependency:\\\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\\\"\")\r\n#endif\r\n#endif\r\n\r\n#include \"Resource.h\"\r\n"
  },
  {
    "path": "Chapter04/code/IOCPServerDemo/targetver.h",
    "content": "﻿#pragma once\r\n\r\n// 以下宏定义要求的最低平台。要求的最低平台\r\n// 是具有运行应用程序所需功能的 Windows、Internet Explorer 等产品的\r\n// 最早版本。通过在指定版本及更低版本的平台上启用所有可用的功能，宏可以\r\n// 正常工作。\r\n\r\n// 如果必须要针对低于以下指定版本的平台，请修改下列定义。\r\n// 有关不同平台对应值的最新信息，请参考 MSDN。\r\n#ifndef WINVER                          // 指定要求的最低平台是 Windows Vista。\r\n#define WINVER 0x0600           // 将此值更改为相应的值，以适用于 Windows 的其他版本。\r\n#endif\r\n\r\n#ifndef _WIN32_WINNT            // 指定要求的最低平台是 Windows Vista。\r\n#define _WIN32_WINNT 0x0600     // 将此值更改为相应的值，以适用于 Windows 的其他版本。\r\n#endif\r\n\r\n#ifndef _WIN32_WINDOWS          // 指定要求的最低平台是 Windows 98。\r\n#define _WIN32_WINDOWS 0x0410 // 将此值更改为适当的值，以适用于 Windows Me 或更高版本。\r\n#endif\r\n\r\n#ifndef _WIN32_IE                       // 指定要求的最低平台是 Internet Explorer 7.0。\r\n#define _WIN32_IE 0x0700        // 将此值更改为相应的值，以适用于 IE 的其他版本。\r\n#endif\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/Resource.h",
    "content": "//{{NO_DEPENDENCIES}}\r\n// Microsoft Visual C++ generated include file.\r\n// Used by WSAAsyncSelect.rc\r\n//\r\n\r\n#define IDS_APP_TITLE\t\t\t103\r\n\r\n#define IDR_MAINFRAME\t\t\t128\r\n#define IDD_WSAASYNCSELECT_DIALOG\t102\r\n#define IDD_ABOUTBOX\t\t\t103\r\n#define IDM_ABOUT\t\t\t\t104\r\n#define IDM_EXIT\t\t\t\t105\r\n#define IDI_WSAASYNCSELECT\t\t\t107\r\n#define IDI_SMALL\t\t\t\t108\r\n#define IDC_WSAASYNCSELECT\t\t\t109\r\n#define IDC_MYICON\t\t\t\t2\r\n#ifndef IDC_STATIC\r\n#define IDC_STATIC\t\t\t\t-1\r\n#endif\r\n// Next default values for new objects\r\n//\r\n#ifdef APSTUDIO_INVOKED\r\n#ifndef APSTUDIO_READONLY_SYMBOLS\r\n\r\n#define _APS_NO_MFC\t\t\t\t\t130\r\n#define _APS_NEXT_RESOURCE_VALUE\t129\r\n#define _APS_NEXT_COMMAND_VALUE\t\t32771\r\n#define _APS_NEXT_CONTROL_VALUE\t\t1000\r\n#define _APS_NEXT_SYMED_VALUE\t\t110\r\n#endif\r\n#endif\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/WSAAsyncSelect.cpp",
    "content": "// WSAAsyncSelect.cpp : Defines the entry point for the application.\r\n//\r\n\r\n#include \"stdafx.h\"\r\n#include <winsock2.h>\r\n#include \"WSAAsyncSelect.h\"\r\n\r\n#pragma comment(lib, \"ws2_32.lib\")\r\n\r\n//socket Ϣ\r\n#define WM_SOCKET   WM_USER + 1\r\n\r\n//ǰû\r\nint    g_nCount = 0;\r\n\r\nSOCKET              InitSocket();\r\nATOM MyRegisterClass(HINSTANCE hInstance);\r\nHWND InitInstance(HINSTANCE hInstance, int nCmdShow);\r\nLRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);\r\nLRESULT OnSocketEvent(HWND hWnd, WPARAM wParam, LPARAM lParam);\r\n\r\nint APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)\r\n{\r\n\tUNREFERENCED_PARAMETER(hPrevInstance);\r\n\tUNREFERENCED_PARAMETER(lpCmdLine);\r\n\r\n    SOCKET hListenSocket = InitSocket();\r\n    if (hListenSocket == INVALID_SOCKET)\r\n        return 1;\r\n\r\n\tMSG msg;\r\n\tMyRegisterClass(hInstance);\r\n\r\n    HWND hwnd = InitInstance(hInstance, nCmdShow);\r\n    if (hwnd == NULL)\r\n        return 1;\r\n\r\n    // WSAAsyncSelect  socket  hwnd һ\r\n    if (WSAAsyncSelect(hListenSocket, hwnd, WM_SOCKET, FD_ACCEPT) == SOCKET_ERROR)\r\n        return 1;\r\n\r\n\twhile (GetMessage(&msg, NULL, 0, 0))\r\n\t{\t\t\r\n        TranslateMessage(&msg);\r\n        DispatchMessage(&msg);\t\r\n\t}\r\n\r\n    closesocket(hListenSocket);\r\n    WSACleanup();\r\n\r\n\treturn (int) msg.wParam;\r\n}\r\n\r\nSOCKET InitSocket()\r\n{\r\n    //1. ʼ׽ֿ\r\n    WORD wVersionRequested;\r\n    WSADATA wsaData;\r\n    wVersionRequested = MAKEWORD(1, 1);\r\n    int nError = WSAStartup(wVersionRequested, &wsaData);\r\n    if (nError != 0)\r\n        return INVALID_SOCKET;\r\n\r\n    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)\r\n    {\r\n        WSACleanup();\r\n        return INVALID_SOCKET;\r\n    }\r\n\r\n    //2. ڼ׽\r\n    SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, 0);\r\n    SOCKADDR_IN addrSrv;\r\n    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);\r\n    addrSrv.sin_family = AF_INET;\r\n    addrSrv.sin_port = htons(6000);\r\n\r\n    //3. ׽\r\n    if (bind(hListenSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == SOCKET_ERROR)\r\n    {\r\n        closesocket(hListenSocket);\r\n        WSACleanup();\r\n        return INVALID_SOCKET;\r\n    }\r\n\r\n    //4. ׽Ϊģʽ׼ܿͻ\r\n    if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)\r\n    {\r\n        closesocket(hListenSocket);\r\n        WSACleanup();\r\n        return INVALID_SOCKET;\r\n    }\r\n\r\n    return hListenSocket;\r\n}\r\n\r\nATOM MyRegisterClass(HINSTANCE hInstance)\r\n{\r\n\tWNDCLASSEX wcex;\r\n\r\n\twcex.cbSize = sizeof(WNDCLASSEX);\r\n\r\n\twcex.style\t\t\t= CS_HREDRAW | CS_VREDRAW;\r\n\twcex.lpfnWndProc\t= WndProc;\r\n\twcex.cbClsExtra\t\t= 0;\r\n\twcex.cbWndExtra\t\t= 0;\r\n\twcex.hInstance\t\t= hInstance;\r\n\twcex.hIcon\t\t\t= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WSAASYNCSELECT));\r\n\twcex.hCursor\t\t= LoadCursor(NULL, IDC_ARROW);\r\n\twcex.hbrBackground\t= (HBRUSH)(COLOR_WINDOW+1);\r\n\twcex.lpszMenuName\t= NULL;\r\n\twcex.lpszClassName\t= _T(\"DemoWindowCls\");\r\n\twcex.hIconSm\t\t= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));\r\n\r\n\treturn RegisterClassEx(&wcex);\r\n}\r\n\r\nHWND InitInstance(HINSTANCE hInstance, int nCmdShow)\r\n{  \r\n   HWND hWnd = CreateWindow(_T(\"DemoWindowCls\"), _T(\"DemoWindow\"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);\r\n   if (!hWnd)\r\n      return NULL;\r\n\r\n   ShowWindow(hWnd, nCmdShow);\r\n   UpdateWindow(hWnd);\r\n\r\n   return hWnd;\r\n}\r\n\r\nLRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n\tint wmId, wmEvent;\r\n\tPAINTSTRUCT ps;\r\n\tHDC hdc;\r\n\r\n    switch (uMsg)\r\n\t{\r\n    case WM_SOCKET:\r\n        return OnSocketEvent(hWnd, wParam, lParam);\r\n\r\n\r\n\tcase WM_PAINT:\r\n\t\thdc = BeginPaint(hWnd, &ps);\r\n\t\t// TODO: Add any drawing code here...\r\n\t\tEndPaint(hWnd, &ps);\r\n\t\tbreak;\r\n\r\n\tcase WM_DESTROY:\r\n\t\tPostQuitMessage(0);\r\n\t\tbreak;\r\n\tdefault:\r\n        return DefWindowProc(hWnd, uMsg, wParam, lParam);\r\n\t}\r\n\r\n\treturn 0;\r\n}\r\n\r\nLRESULT OnSocketEvent(HWND hWnd, WPARAM wParam, LPARAM lParam)\r\n{\r\n    SOCKET s = (SOCKET)wParam;\r\n    int nEventType = WSAGETSELECTEVENT(lParam);\r\n    int nErrorCode = WSAGETSELECTERROR(lParam);\r\n    if (nErrorCode != 0)\r\n        return 1;\r\n\r\n    switch (nEventType)\r\n    {\r\n    case FD_ACCEPT:\r\n    {\r\n        //accept¼;\r\n        SOCKADDR_IN addrClient;\r\n        int len = sizeof(SOCKADDR);\r\n        //ȴͻ\r\n        SOCKET hSockClient = accept(s, (SOCKADDR*)&addrClient, &len);\r\n        if (hSockClient != SOCKET_ERROR)\r\n        {\r\n            //Ŀͻsocket FD_READ/FD_CLOSE ¼\r\n            if (WSAAsyncSelect(hSockClient, hWnd, WM_SOCKET, FD_READ | FD_CLOSE) == SOCKET_ERROR)\r\n            {\r\n                closesocket(hSockClient);\r\n                return 1;\r\n            }\r\n\r\n            g_nCount++;  \r\n            TCHAR szLogMsg[64];\r\n            wsprintf(szLogMsg, _T(\"a client connected, socket: %d, current: %d\\n\"), (int)hSockClient, g_nCount);\r\n            OutputDebugString(szLogMsg);\r\n        }\r\n    }\r\n    break;\r\n\r\n    case FD_READ:\r\n    {\r\n        char szBuf[64] = { 0 };\r\n        int n = recv(s, szBuf, 64, 0);\r\n        if (n > 0)\r\n        {\r\n            OutputDebugStringA(szBuf);\r\n        }\r\n        else if (n <= 0)\r\n        {\r\n            g_nCount--;\r\n            TCHAR szLogMsg[64];\r\n            wsprintf(szLogMsg, _T(\"a client disconnected, socket: %d, current: %d\\n\"), (int)s, g_nCount);\r\n            OutputDebugString(szLogMsg);\r\n            closesocket(s);\r\n        }\r\n    }\r\n    break;\r\n\r\n    case FD_CLOSE:\r\n    {\r\n        g_nCount--;\r\n        TCHAR szLogMsg[64];\r\n        wsprintf(szLogMsg, _T(\"a client disconnected, socket: %d, current: %d\\n\"), (int)s, g_nCount);\r\n        OutputDebugString(szLogMsg);\r\n        closesocket(s);\r\n    }\r\n    break;\r\n\r\n    }// end switch\r\n\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/WSAAsyncSelect.h",
    "content": "#pragma once\r\n\r\n#include \"resource.h\"\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/WSAAsyncSelect.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.40629.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"WSAAsyncSelect\", \"WSAAsyncSelect.vcxproj\", \"{87BE6464-6DE0-484B-8E81-57C8AB0C84CB}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{87BE6464-6DE0-484B-8E81-57C8AB0C84CB}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{87BE6464-6DE0-484B-8E81-57C8AB0C84CB}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{87BE6464-6DE0-484B-8E81-57C8AB0C84CB}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{87BE6464-6DE0-484B-8E81-57C8AB0C84CB}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/WSAAsyncSelect.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{87BE6464-6DE0-484B-8E81-57C8AB0C84CB}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>WSAAsyncSelect</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"Resource.h\" />\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n    <ClInclude Include=\"WSAAsyncSelect.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n    <ClCompile Include=\"WSAAsyncSelect.cpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"WSAAsyncSelect.rc\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"small.ico\" />\r\n    <Image Include=\"WSAAsyncSelect.ico\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/WSAAsyncSelect.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Resource.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"WSAAsyncSelect.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"WSAAsyncSelect.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"WSAAsyncSelect.rc\">\r\n      <Filter>Resource Files</Filter>\r\n    </ResourceCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"small.ico\">\r\n      <Filter>Resource Files</Filter>\r\n    </Image>\r\n    <Image Include=\"WSAAsyncSelect.ico\">\r\n      <Filter>Resource Files</Filter>\r\n    </Image>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// WSAAsyncSelect.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\r\n// Windows Header Files:\r\n#include <windows.h>\r\n\r\n// C RunTime Header Files\r\n#include <stdlib.h>\r\n#include <malloc.h>\r\n#include <memory.h>\r\n#include <tchar.h>\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter04/code/WSAAsyncSelect/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/WSAEventSelect.cpp",
    "content": "/**\r\n * WSAEventSelect ģʾ\r\n * zhangyl 2019.03.16\r\n */\r\n#include \"stdafx.h\"\r\n#include <winsock2.h>\r\n#include <stdio.h>\r\n#include <vector>\r\n\r\n#pragma comment(lib, \"ws2_32.lib\")\r\n\r\nint main(int argc, _TCHAR* argv[])\r\n{\r\n    //1. ʼ׽ֿ\r\n    WORD wVersionRequested;\r\n    WSADATA wsaData;\r\n    wVersionRequested = MAKEWORD(1, 1);\r\n    int nError = WSAStartup(wVersionRequested, &wsaData);\r\n    if (nError != 0)\r\n        return -1;\r\n   \r\n    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)\r\n    {\r\n        WSACleanup();\r\n        return -1;\r\n    }\r\n\r\n    //2. ڼ׽\r\n    SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);\r\n    SOCKADDR_IN addrSrv;\r\n    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);\r\n    addrSrv.sin_family = AF_INET;\r\n    addrSrv.sin_port = htons(6000);\r\n    \r\n    //3. ׽\r\n    if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == SOCKET_ERROR)\r\n    {\r\n        closesocket(sockSrv);\r\n        WSACleanup();\r\n        return -1;\r\n    }\r\n    \r\n    //4. ׽Ϊģʽ׼ܿͻ\r\n    if (listen(sockSrv, SOMAXCONN) == SOCKET_ERROR)\r\n    {\r\n        closesocket(sockSrv);\r\n        WSACleanup();\r\n        return -1;\r\n    }\r\n\r\n    WSAEVENT hListenEvent = WSACreateEvent();\r\n    if (WSAEventSelect(sockSrv, hListenEvent, FD_ACCEPT) == SOCKET_ERROR)\r\n    {\r\n        WSACloseEvent(hListenEvent);\r\n        closesocket(sockSrv);\r\n        WSACleanup();\r\n        return -1;\r\n    }\r\n        \r\n\r\n    WSAEVENT* pEvents = new WSAEVENT[1];\r\n    pEvents[0] = hListenEvent;\r\n    SOCKET* pSockets = new SOCKET[1];\r\n    pSockets[0] = sockSrv;\r\n    DWORD dwCount = 1;\r\n    bool bNeedToMove;\r\n\r\n    while (true)\r\n    {\r\n        bNeedToMove = false;\r\n        DWORD dwResult = WSAWaitForMultipleEvents(dwCount, pEvents, FALSE, WSA_INFINITE, FALSE);\r\n        if (dwResult == WSA_WAIT_FAILED)\r\n            continue;\r\n\r\n        DWORD dwIndex = dwResult - WSA_WAIT_EVENT_0;\r\n        for (DWORD i = 0; i <= dwIndex; ++i)\r\n        {\r\n            //ͨdwIndexҵhEventsеWSAEvent󣬽ҵӦsocket\r\n            WSANETWORKEVENTS  triggeredEvents;\r\n            if (WSAEnumNetworkEvents(pSockets[i], pEvents[i], &triggeredEvents) == SOCKET_ERROR)\r\n                continue;\r\n\r\n            if (triggeredEvents.lNetworkEvents & FD_ACCEPT)\r\n            {\r\n                if (triggeredEvents.iErrorCode[FD_ACCEPT_BIT] != 0)\r\n                    continue;\r\n\r\n                //accept¼;\r\n                SOCKADDR_IN addrClient;\r\n                int len = sizeof(SOCKADDR);\r\n                //ȴͻ\r\n                SOCKET hSockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);\r\n                if (hSockClient != SOCKET_ERROR)\r\n                {\r\n                    //ͻsocketĿɶ͹ر¼\r\n                    WSAEVENT hClientEvent = WSACreateEvent();\r\n                    if (WSAEventSelect(hSockClient, hClientEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)\r\n                    {\r\n                        WSACloseEvent(hClientEvent);\r\n                        closesocket(hSockClient); \r\n                        continue;\r\n                    }\r\n                        \r\n                    WSAEVENT* pEvents2 = new WSAEVENT[dwCount + 1];\r\n                    SOCKET* pSockets2 = new SOCKET[dwCount + 1];\r\n                    memcpy(pEvents2, pEvents, dwCount * sizeof(WSAEVENT));\r\n                    pEvents2[dwCount] = hClientEvent;\r\n                    memcpy(pSockets2, pSockets, dwCount * sizeof(SOCKET));\r\n                    pSockets2[dwCount] = hSockClient;\r\n                    delete[] pEvents;\r\n                    delete[] pSockets;\r\n                    pEvents = pEvents2;\r\n                    pSockets = pSockets2;\r\n\r\n                    dwCount++;\r\n\r\n                    printf(\"a client connected, socket: %d, current: %d\\n\", (int)hSockClient, dwCount - 1);\r\n                }\r\n            }\r\n            else if (triggeredEvents.lNetworkEvents & FD_READ)\r\n            {\r\n                if (triggeredEvents.iErrorCode[FD_READ_BIT] != 0)\r\n                    continue;\r\n\r\n                char szBuf[64] = { 0 };\r\n                int nRet = recv(pSockets[i], szBuf, 64, 0);\r\n                if (nRet > 0)\r\n                {\r\n                    printf(\"recv data: %s, client: %d\\n\", szBuf, pSockets[i]);\r\n                }\r\n            }\r\n            else if (triggeredEvents.lNetworkEvents & FD_CLOSE)\r\n            {\r\n                //˴Ҫж\r\n                //if (triggeredEvents.iErrorCode[FD_READ_BIT] != 0)\r\n                //    continue;\r\n                \r\n                printf(\"a client disconnected, socket: %d, current: %d\\n\", (int)pSockets[i], dwCount - 2);\r\n\r\n                WSACloseEvent(pEvents[i]);\r\n                closesocket(pSockets[i]);\r\n\r\n                //ΪЧѭͳһƳ\r\n                pSockets[i] = INVALID_SOCKET;\r\n\r\n                bNeedToMove = true;\r\n            }\r\n            \r\n        }// end for-loop\r\n\r\n        if (bNeedToMove)\r\n        {\r\n            //ƳЧ¼\r\n            std::vector<SOCKET> vValidSockets;\r\n            std::vector<HANDLE> vValidEvents;\r\n            for (size_t i = 0; i < dwCount; ++i)\r\n            {\r\n                if (pSockets[i] != INVALID_SOCKET)\r\n                {\r\n                    vValidSockets.push_back(pSockets[i]);\r\n                    vValidEvents.push_back(pEvents[i]);\r\n                }\r\n            }\r\n\r\n            size_t validSize = vValidSockets.size();\r\n            if (validSize > 0)\r\n            {\r\n                WSAEVENT* pEvents2 = new WSAEVENT[validSize];\r\n                SOCKET* pSockets2 = new SOCKET[validSize];\r\n                memcpy(pEvents2, &vValidEvents[0], validSize * sizeof(WSAEVENT));\r\n                memcpy(pSockets2, &vValidSockets[0], validSize * sizeof(SOCKET));\r\n                delete[] pEvents;\r\n                delete[] pSockets;\r\n                pEvents = pEvents2;\r\n                pSockets = pSockets2;\r\n\r\n                dwCount = validSize;\r\n            }\r\n        }\r\n        \r\n    }// end while-loop\r\n\r\n    closesocket(sockSrv);\r\n\r\n    WSACleanup();\r\n                     \r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/WSAEventSelect.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.40629.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"WSAEventSelect\", \"WSAEventSelect.vcxproj\", \"{7D04DFA2-70C2-4F25-AC66-F0F587648169}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{7D04DFA2-70C2-4F25-AC66-F0F587648169}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{7D04DFA2-70C2-4F25-AC66-F0F587648169}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{7D04DFA2-70C2-4F25-AC66-F0F587648169}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{7D04DFA2-70C2-4F25-AC66-F0F587648169}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/WSAEventSelect.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{7D04DFA2-70C2-4F25-AC66-F0F587648169}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>WSAEventSelect</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n    <ClCompile Include=\"WSAEventSelect.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/WSAEventSelect.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"WSAEventSelect.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// WSAEventSelect.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#include <stdio.h>\r\n#include <tchar.h>\r\n\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter04/code/WSAEventSelect/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "Chapter04/code/blocking_client.cpp",
    "content": "/**\n * 验证阻塞模式下send函数的行为，client端\n * zhangyl 2018.12.17\n */\n#include <sys/types.h> \n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <iostream>\n#include <string.h>\n\n#define SERVER_ADDRESS \"127.0.0.1\"\n#define SERVER_PORT     3000\n#define SEND_DATA       \"helloworld\"\n\nint main(int argc, char* argv[])\n{\n    //1.创建一个socket\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (clientfd == -1)\n    {\n        std::cout << \"create client socket error.\" << std::endl;\n        return -1;\n    }\n\n    //2.连接服务器\n    struct sockaddr_in serveraddr;\n    serveraddr.sin_family = AF_INET;\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\n    serveraddr.sin_port = htons(SERVER_PORT);\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\n    {\n        std::cout << \"connect socket error.\" << std::endl;\n        close(clientfd);\n        return -1;\n    }\n\n    //3. 不断向服务器发送数据，或者出错退出\n    int count = 0;\n    while (true)\n    {\n        int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\n        if (ret != strlen(SEND_DATA))\n        {\n            std::cout << \"send data error.\" << std::endl;\n            break;\n        } \n        else\n        {\n            count ++;\n            std::cout << \"send data successfully, count = \" << count << std::endl;\n        }\n    }\n\n    //5. 关闭socket\n    close(clientfd);\n\n    return 0;\n}"
  },
  {
    "path": "Chapter04/code/blocking_client_recv.cpp",
    "content": "/**\r\n * 验证阻塞模式下recv函数的行为，client端，blocking_client_recv.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\t\r\n\tchar recvbuf[32] = {0};\r\n    //3.直接调用recv函数，程序会阻塞在recv函数调用处\r\n\tint ret = recv(clientfd, recvbuf, 32, 0);\r\n\tif (ret > 0) \r\n\t{\r\n\t\tstd::cout << \"recv successfully.\" << std::endl;\r\n\t} \r\n\telse \r\n\t{\r\n\t\tstd::cout << \"recv data error.\" << std::endl;\r\n\t}\r\n\t\r\n\t//4. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/blocking_client_send.cpp",
    "content": "/**\r\n * 验证阻塞模式下send函数的行为，client端\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3. 不断向服务器发送数据，或者出错退出\r\n\tint count = 0;\r\n\twhile (true)\r\n\t{\r\n\t\tint ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\r\n\t\tif (ret != strlen(SEND_DATA))\r\n\t\t{\r\n\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\tbreak;\r\n\t\t} \r\n\t\telse\r\n\t\t{\r\n\t\t\tcount ++;\r\n\t\t\tstd::cout << \"send data successfully, count = \" << count << std::endl;\r\n\t\t}\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/blocking_server.cpp",
    "content": "/**\r\n * ֤ģʽsendΪserver\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.һsocket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == -1)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.ʼַ\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n    if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socket error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3.\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    while (true)\r\n    {\r\n        struct sockaddr_in clientaddr;\r\n        socklen_t clientaddrlen = sizeof(clientaddr);\r\n\t\t//4. ܿͻ\r\n        int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);\r\n        if (clientfd != -1)\r\n        {         \t\r\n\t\t\t//ֻӣrecvȡκ\r\n\t\t\tstd:: cout << \"accept a client connection.\" << std::endl;\r\n        }\r\n    }\r\n\t\r\n\t//7.رsocket\r\n\tclose(listenfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/client.cpp",
    "content": "/**\r\n * TCP客户端通信基本流程\r\n * zhangyl 2018.12.13\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3. 向服务器发送数据\r\n\tint ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\r\n\tif (ret != strlen(SEND_DATA))\r\n\t{\r\n\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\tstd::cout << \"send data successfully, data: \" << SEND_DATA << std::endl;\r\n\t\r\n\t//4. 从服务器收取数据\r\n\tchar recvBuf[32] = {0};\r\n\tret = recv(clientfd, recvBuf, 32, 0);\r\n\tif (ret > 0) \r\n\t{\r\n\t\tstd::cout << \"recv data successfully, data: \" << recvBuf << std::endl;\r\n\t} \r\n\telse \r\n\t{\r\n\t\tstd::cout << \"recv data error, data: \" << recvBuf << std::endl;\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/client2.cpp",
    "content": "/**\r\n * TCP客户端通信基本流程\r\n * zhangyl 2018.12.13\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3. 向服务器发送数据\r\n\tint ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\r\n\tif (ret != strlen(SEND_DATA))\r\n\t{\r\n\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\tstd::cout << \"send data successfully, data: \" << SEND_DATA << std::endl;\r\n\t\r\n\t//4. 从服务器收取数据\r\n\tchar recvBuf[32] = {0};\r\n\tret = recv(clientfd, recvBuf, 32, 0);\r\n\tif (ret > 0) \r\n\t{\r\n\t\tstd::cout << \"recv data successfully, data: \" << recvBuf << std::endl;\r\n\t} \r\n\telse \r\n\t{\r\n\t\tstd::cout << \"recv data error, data: \" << recvBuf << std::endl;\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\t//close(clientfd);\r\n\t//这里仅仅是为了让客户端程序不退出\r\n\twhile (true) \r\n\t{\r\n\t\tsleep(3);\r\n\t}\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/epoll_server.cpp",
    "content": "/** \r\n * ֤epollLTETģʽepoll_server.cpp\r\n * zhangyl 2019.04.01\r\n */\r\n#include<sys/types.h>\r\n#include<sys/socket.h>\r\n#include<arpa/inet.h>\r\n#include<unistd.h>\r\n#include<fcntl.h>\r\n#include<sys/epoll.h>\r\n#include<poll.h>\r\n#include<iostream>\r\n#include<string.h>\r\n#include<vector>\r\n#include<errno.h>\r\n#include<iostream>\r\n\r\nint main()\r\n{\r\n    //һsocket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == -1)\r\n    {\r\n        std::cout << \"create listen socket error\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //ipַͶ˿ں\r\n    int on = 1;\r\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));\r\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));\r\n\r\n\r\n    //sockerΪ\r\n    int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\r\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n    if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1)\r\n    {\r\n        close(listenfd);\r\n        std::cout << \"set listenfd to nonblock error\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //ʼַ\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n\r\n    if (bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socker error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    //\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n\r\n    //epollfd\r\n    int epollfd = epoll_create(1);\r\n    if (epollfd == -1)\r\n    {\r\n        std::cout << \"create epollfd error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    epoll_event listen_fd_event;\r\n    listen_fd_event.data.fd = listenfd;\r\n    listen_fd_event.events = EPOLLIN;\r\n    //ȡע͵һУʹETģʽ\r\n    //listen_fd_event.events |= EPOLLET;\r\n\r\n    //sokcet󶨵epollfdȥ\r\n    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &listen_fd_event) == -1)\r\n    {\r\n        std::cout << \"epoll_ctl error\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    int n;\r\n    while (true)\r\n    {\r\n        epoll_event epoll_events[1024];\r\n        n = epoll_wait(epollfd, epoll_events, 1024, 1000);\r\n        if (n < 0)\r\n        {\r\n            //źж\r\n            if (errno == EINTR) \r\n                continue;\r\n\r\n            //,˳\r\n            break;\r\n        }\r\n        else if (n == 0)\r\n        {\r\n            //ʱ,\r\n            continue;\r\n        }\r\n\t\t\r\n        for (size_t i = 0; i < n; ++i)\r\n        {\r\n            //¼ɶ\r\n            if (epoll_events[i].events & EPOLLIN)\r\n            {\r\n                if (epoll_events[i].data.fd == listenfd)\r\n                {\r\n                    //socket,\r\n                    struct sockaddr_in clientaddr;\r\n                    socklen_t clientaddrlen = sizeof(clientaddr);\r\n                    int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);\r\n                    if (clientfd != -1)\r\n                    {\r\n                        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n                        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)\r\n                        {\r\n                            close(clientfd);\r\n                            std::cout << \"set clientfd to nonblocking error.\" << std::endl;\r\n                        }\r\n                        else\r\n                        {\r\n                            epoll_event client_fd_event;\r\n                            client_fd_event.data.fd = clientfd;\r\n                            client_fd_event.events = EPOLLIN;\r\n                            //ȡעһУʹETģʽ\r\n                            //client_fd_event.events |= EPOLLET; \r\n                            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event) != -1)\r\n                            {\r\n                                std::cout << \"new client accepted,clientfd: \" << clientfd << std::endl;\r\n                            }\r\n                            else\r\n                            {\r\n                                std::cout << \"add client fd to epollfd error\" << std::endl;\r\n                                close(clientfd);\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    std::cout << \"client fd: \" << epoll_events[i].data.fd << \" recv data.\" << std::endl;\r\n                    //ͨclientfd\r\n                    char ch;\r\n                    //ÿֻһֽ\r\n                    int m = recv(epoll_events[i].data.fd, &ch, 1, 0);\r\n                    if (m == 0)\r\n                    {\r\n                        //Զ˹رӣepollfdƳclientfd\r\n                        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\r\n                        {\r\n                            std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\r\n                        }\r\n                        close(epoll_events[i].data.fd);\r\n                    }\r\n                    else if (m < 0)\r\n                    {\r\n                        //\r\n                        if (errno != EWOULDBLOCK && errno != EINTR)\r\n                        {\r\n                            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\r\n                            {\r\n                                std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\r\n                            }\r\n                            close(epoll_events[i].data.fd);\r\n                        }\r\n                    }\r\n                    else\r\n                    {\r\n                        //յ\r\n                        std::cout << \"recv from client:\" << epoll_events[i].data.fd << \", \" << ch << std::endl;\r\n                    }\r\n                }\r\n            }\r\n            else if (epoll_events[i].events & POLLERR)\r\n            {\r\n                // TODO ݲ\r\n            }\r\n        }\r\n    }\r\n\r\n    close(listenfd);\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter04/code/epoll_server_with_oneshot.cpp",
    "content": "/** \r\n * ֤ epoll EPOLLONESHOT ѡepoll_server_with_oneshot.cpp\r\n * zhangyl 2019.04.01\r\n */\r\n#include<sys/types.h>\r\n#include<sys/socket.h>\r\n#include<arpa/inet.h>\r\n#include<unistd.h>\r\n#include<fcntl.h>\r\n#include<sys/epoll.h>\r\n#include<poll.h>\r\n#include<iostream>\r\n#include<string.h>\r\n#include<vector>\r\n#include<errno.h>\r\n#include<iostream>\r\n\r\nint main()\r\n{\r\n    //һsocket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == -1)\r\n    {\r\n        std::cout << \"create listen socket error\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //ipַͶ˿ں\r\n    int on = 1;\r\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));\r\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));\r\n\r\n\r\n    //sockerΪ\r\n    int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\r\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n    if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1)\r\n    {\r\n        close(listenfd);\r\n        std::cout << \"set listenfd to nonblock error\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //ʼַ\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n\r\n    if (bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socker error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    //\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n\r\n    //epollfd\r\n    int epollfd = epoll_create(1);\r\n    if (epollfd == -1)\r\n    {\r\n        std::cout << \"create epollfd error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    epoll_event listen_fd_event;\r\n    listen_fd_event.data.fd = listenfd;\r\n    listen_fd_event.events = EPOLLIN;\r\n\t\r\n    //sokcet󶨵epollfdȥ\r\n    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &listen_fd_event) == -1)\r\n    {\r\n        std::cout << \"epoll_ctl error\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    int n;\r\n    while (true)\r\n    {\r\n        epoll_event epoll_events[1024];\r\n        n = epoll_wait(epollfd, epoll_events, 1024, 1000);\r\n        if (n < 0)\r\n        {\r\n            //źж\r\n            if (errno == EINTR) \r\n                continue;\r\n\r\n            //,˳\r\n            break;\r\n        }\r\n        else if (n == 0)\r\n        {\r\n            //ʱ,\r\n            continue;\r\n        }\r\n        for (size_t i = 0; i < n; ++i)\r\n        {\r\n            //¼ɶ\r\n            if (epoll_events[i].events & EPOLLIN)\r\n            {\r\n                if (epoll_events[i].data.fd == listenfd)\r\n                {\r\n                    //socket,\r\n                    struct sockaddr_in clientaddr;\r\n                    socklen_t clientaddrlen = sizeof(clientaddr);\r\n                    int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);\r\n                    if (clientfd != -1)\r\n                    {\r\n                        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n                        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)\r\n                        {\r\n                            close(clientfd);\r\n                            std::cout << \"set clientfd to nonblocking error.\" << std::endl;\r\n                        }\r\n                        else\r\n                        {\r\n                            epoll_event client_fd_event;\r\n                            client_fd_event.data.fd = clientfd;\r\n                            client_fd_event.events = EPOLLIN;\r\n\t\t\t\t\t\t\t//clientfdEPOLLONESHOTѡ\r\n\t\t\t\t\t\t\tclient_fd_event.events |= EPOLLONESHOT;\r\n                            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event) != -1)\r\n                            {\r\n                                std::cout << \"new client accepted,clientfd: \" << clientfd << std::endl;\r\n                            }\r\n                            else\r\n                            {\r\n                                std::cout << \"add client fd to epollfd error\" << std::endl;\r\n                                close(clientfd);\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    std::cout << \"client fd: \" << epoll_events[i].data.fd << \" recv data.\" << std::endl;\r\n                    //ͨclientfd\r\n                    char ch;\r\n                    //ÿֻһֽ\r\n                    int m = recv(epoll_events[i].data.fd, &ch, 1, 0);\r\n                    if (m == 0)\r\n                    {\r\n                        //Զ˹رӣepollfdƳclientfd\r\n                        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\r\n                        {\r\n                            std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\r\n                        }\r\n                        close(epoll_events[i].data.fd);\r\n                    }\r\n                    else if (m < 0)\r\n                    {\r\n                        //\r\n                        if (errno != EWOULDBLOCK && errno != EINTR)\r\n                        {\r\n                            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\r\n                            {\r\n                                std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\r\n                            }\r\n                            close(epoll_events[i].data.fd);\r\n                        }\r\n                    }\r\n                    else\r\n                    {\r\n                        //յ\r\n                        std::cout << \"recv from client:\" << epoll_events[i].data.fd << \", \" << ch << std::endl;\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t//ٴΪclientfdٴעEPOLLIN¼\r\n\t\t\t\t\t\t//epoll_event client_fd_event;\r\n\t\t\t\t\t\t//client_fd_event.data.fd = epoll_events[i].data.fd;\r\n\t\t\t\t\t\t//client_fd_event.events = EPOLLIN;\t\t\t\t\t\r\n\t\t\t\t\t\t//if (epoll_ctl(epollfd, EPOLL_CTL_MOD, epoll_events[i].data.fd, &client_fd_event) != -1)\r\n\t\t\t\t\t\t//{\r\n\t\t\t\t\t\t//\tstd::cout << \"rearm EPOLLIN event to clientfd: \" << epoll_events[i].data.fd << std::endl;\r\n\t\t\t\t\t\t//}\r\n\t\t\t\t\t\t//else\r\n\t\t\t\t\t\t//{\r\n\t\t\t\t\t\t//\tif (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\r\n\t\t\t\t\t\t//\t{\r\n\t\t\t\t\t\t//\t\tstd::cout << \"remove clientfd from epoll fd successfully, clientfd:\" << epoll_events[i].data.fd << std::endl;\r\n\t\t\t\t\t\t//\t}\r\n\t\t\t\t\t\t//\tclose(epoll_events[i].data.fd);\r\n\t\t\t\t\t\t//}\r\n                    }\r\n                }\r\n            }\r\n            else if (epoll_events[i].events & POLLERR)\r\n            {\r\n                // TODO ݲ\r\n            }\r\n        }\r\n    }\r\n\r\n    close(listenfd);\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter04/code/epoll_server_with_oneshot_2.cpp",
    "content": "/** \n * 验证 epoll EPOLLONESHOT 选项，epoll_server_with_oneshot_2.cpp\n * zhangyl 2019.04.01\n */\n#include<sys/types.h>\n#include<sys/socket.h>\n#include<arpa/inet.h>\n#include<unistd.h>\n#include<fcntl.h>\n#include<sys/epoll.h>\n#include<poll.h>\n#include<iostream>\n#include<string.h>\n#include<vector>\n#include<errno.h>\n#include<iostream>\n\nint main()\n{\n    //创建一个监听socket\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (listenfd == -1)\n    {\n        std::cout << \"create listen socket error\" << std::endl;\n        return -1;\n    }\n\n    //设置重用ip地址和端口号\n    int on = 1;\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));\n\n\n    //将监听socker设置为非阻塞的\n    int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n    if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1)\n    {\n        close(listenfd);\n        std::cout << \"set listenfd to nonblock error\" << std::endl;\n        return -1;\n    }\n\n    //初始化服务器地址\n    struct sockaddr_in bindaddr;\n    bindaddr.sin_family = AF_INET;\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\n    bindaddr.sin_port = htons(3000);\n\n    if (bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) == -1)\n    {\n        std::cout << \"bind listen socker error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    //启动监听\n    if (listen(listenfd, SOMAXCONN) == -1)\n    {\n        std::cout << \"listen error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n\n    //创建epollfd\n    int epollfd = epoll_create(1);\n    if (epollfd == -1)\n    {\n        std::cout << \"create epollfd error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    epoll_event listen_fd_event;\n    listen_fd_event.data.fd = listenfd;\n    listen_fd_event.events = EPOLLIN;\n    \n    //将监听sokcet绑定到epollfd上去\n    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &listen_fd_event) == -1)\n    {\n        std::cout << \"epoll_ctl error\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    int n;\n    while (true)\n    {\n        epoll_event epoll_events[1024];\n        n = epoll_wait(epollfd, epoll_events, 1024, 1000);\n        if (n < 0)\n        {\n            //被信号中断\n            if (errno == EINTR) \n                continue;\n\n            //出错，退出\n            break;\n        }\n        else if (n == 0)\n        {\n            //超时，继续\n            continue;\n        }\n        for (size_t i = 0; i < n; ++i)\n        {\n            //事件可读\n            if (epoll_events[i].events & EPOLLIN)\n            {\n                if (epoll_events[i].data.fd == listenfd)\n                {\n                    //侦听socket，接受新连接\n                    struct sockaddr_in clientaddr;\n                    socklen_t clientaddrlen = sizeof(clientaddr);\n                    int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);\n                    if (clientfd != -1)\n                    {\n                        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\n                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n                        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)\n                        {\n                            close(clientfd);\n                            std::cout << \"set clientfd to nonblocking error.\" << std::endl;\n                        }\n                        else\n                        {\n                            epoll_event client_fd_event;\n                            client_fd_event.data.fd = clientfd;\n                            client_fd_event.events = EPOLLIN;\n                            //为clientfd注册EPOLLONESHOT事件\n                            client_fd_event.events |= EPOLLONESHOT;\n                            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event) != -1)\n                            {\n                                std::cout << \"new client accepted,clientfd: \" << clientfd << std::endl;\n                            }\n                            else\n                            {\n                                std::cout << \"add client fd to epollfd error\" << std::endl;\n                                close(clientfd);\n                            }\n                        }\n                    }\n                }\n                else\n                {\n                    std::cout << \"client fd: \" << epoll_events[i].data.fd << \" recv data.\" << std::endl;\n                    //普通clientfd\n                    char ch;\n                    //每次只收一个字节\n                    int m = recv(epoll_events[i].data.fd, &ch, 1, 0);\n                    if (m == 0)\n                    {\n                        //对端关闭了连接，从epollfd上移除clientfd\n                        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                        {\n                            std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\n                        }\n                        close(epoll_events[i].data.fd);\n                    }\n                    else if (m < 0)\n                    {\n                        //出错\n                        if (errno != EWOULDBLOCK && errno != EINTR)\n                        {\n                            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                            {\n                                std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\n                            }\n                            close(epoll_events[i].data.fd);\n                        }\n                    }\n                    else\n                    {\n                        //正常收到数据\n                        std::cout << \"recv from client:\" << epoll_events[i].data.fd << \", \" << ch << std::endl;\n\n                        epoll_event client_fd_event;\n                        client_fd_event.data.fd = epoll_events[i].data.fd;\n                        client_fd_event.events = EPOLLIN;\n                        //这里再次为clientfd注册EPOLLIN事件\n                        if (epoll_ctl(epollfd, EPOLL_CTL_MOD, epoll_events[i].data.fd, &client_fd_event) != -1)\n                        {\n                            std::cout << \"rearm EPOLLIN event to clientfd: \" << epoll_events[i].data.fd << std::endl;\n                        }\n                        else\n                        {\n                            //若epoll_ctl调用失败，则从epollfd上移除clientfd并关闭clientfd\n                            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                            {\n                                std::cout << \"remove clientfd from epoll fd successfully, clientfd:\" << epoll_events[i].data.fd << std::endl;\n                            }\n                            close(epoll_events[i].data.fd);\n                        }\n\n                    }\n                }\n            }\n            else if (epoll_events[i].events & POLLERR)\n            {\n                //TODO暂不处理\n            }\n        }\n    }\n\n    close(listenfd);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter04/code/epoll_server_write_event_et.cpp",
    "content": "/**\n * 验证epoll LT与ET模式在处理写事件上的区别，epoll_server_write_event_et.cpp\n * zhangyl 2019.04.01\n */\n#include<sys/types.h>\n#include<sys/socket.h>\n#include<arpa/inet.h>\n#include<unistd.h>\n#include<fcntl.h>\n#include<sys/epoll.h>\n#include<poll.h>\n#include<iostream>\n#include<string.h>\n#include<vector>\n#include<errno.h>\n#include<iostream>\n\nint main()\n{\n    //创建一个监听socket\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (listenfd == -1)\n    {\n        std::cout << \"create listen socket error\" << std::endl;\n        return -1;\n    }\n\n    //设置重用IP地址和端口号\n    int on = 1;\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));\n\n    //将监听socker设置为非阻塞的\n    int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n    if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1)\n    {\n        close(listenfd);\n        std::cout << \"set listenfd to nonblock error\" << std::endl;\n        return -1;\n    }\n\n    //初始化服务器地址\n    struct sockaddr_in bindaddr;\n    bindaddr.sin_family = AF_INET;\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\n    bindaddr.sin_port = htons(3000);\n\n    if (bind(listenfd, (struct sockaddr*) & bindaddr, sizeof(bindaddr)) == -1)\n    {\n        std::cout << \"bind listen socker error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    //启动监听\n    if (listen(listenfd, SOMAXCONN) == -1)\n    {\n        std::cout << \"listen error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n\n    //创建epollfd\n    int epollfd = epoll_create(1);\n    if (epollfd == -1)\n    {\n        std::cout << \"create epollfd error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    epoll_event listen_fd_event;\n    listen_fd_event.data.fd = listenfd;\n    listen_fd_event.events = EPOLLIN;\n    //若取消注释这一行，则使用ET模式\n    //listen_fd_event.events |= EPOLLET;\n\n    //将监听sokcet绑定到epollfd上\n    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &listen_fd_event) == -1)\n    {\n        std::cout << \"epoll_ctl error\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    int n;\n    while (true)\n    {\n        epoll_event epoll_events[1024];\n        n = epoll_wait(epollfd, epoll_events, 1024, 1000);\n        if (n < 0)\n        {\n            //被信号中断\n            if (errno == EINTR)\n                continue;\n\n            //出错，退出\n            break;\n        }\n        else if (n == 0)\n        {\n            //超时，继续\n            continue;\n        }\n        for (size_t i = 0; i < n; ++i)\n        {\n            //有读事件\n            if (epoll_events[i].events & EPOLLIN)\n            {\n                if (epoll_events[i].data.fd == listenfd)\n                {\n                    //侦听socket，接受新连接\n                    struct sockaddr_in clientaddr;\n                    socklen_t clientaddrlen = sizeof(clientaddr);\n                    int clientfd = accept(listenfd, (struct sockaddr*) & clientaddr, &clientaddrlen);\n                    if (clientfd != -1)\n                    {\n                        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\n                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n                        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)\n                        {\n                            close(clientfd);\n                            std::cout << \"set clientfd to nonblocking error.\" << std::endl;\n                        }\n                        else\n                        {\n                            epoll_event client_fd_event;\n                            client_fd_event.data.fd = clientfd;\n                            //同时侦听新来连接socket的读和写时间\n                            client_fd_event.events = EPOLLIN | EPOLLOUT;\n                            //若取消注释这一行，则使用ET模式\n                            client_fd_event.events |= EPOLLET; \n                            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event) != -1)\n                            {\n                                std::cout << \"new client accepted,clientfd: \" << clientfd << std::endl;\n                            }\n                            else\n                            {\n                                std::cout << \"add client fd to epollfd error\" << std::endl;\n                                close(clientfd);\n                            }\n                        }\n                    }\n                }\n                else\n                {\n                    std::cout << \"client fd: \" << epoll_events[i].data.fd << \" recv data.\" << std::endl;\n                    //普通clientfd\n                    char recvbuf[1024] = { 0 };\n                    //读取数据\n                    int m = recv(epoll_events[i].data.fd, recvbuf, 1024, 0);\n                    if (m == 0)\n                    {\n                        //对端关闭了连接，从epollfd上移除clientfd\n                        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                        {\n                            std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\n                        }\n                        close(epoll_events[i].data.fd);\n                    }\n                    else if (m < 0)\n                    {\n                        //出错\n                        if (errno != EWOULDBLOCK && errno != EINTR)\n                        {\n                            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                            {\n                                std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\n                            }\n                            close(epoll_events[i].data.fd);\n                        }\n                    }\n                    else\n                    {\n                        //正常收到数据\n                        std::cout << \"recv from client:\" << epoll_events[i].data.fd << \", \" << recvbuf << std::endl;\n                       \n                        epoll_event client_fd_event;\n                        client_fd_event.data.fd = epoll_events[i].data.fd;\n                        //再次给clientfd注册检测可写事件\n                        client_fd_event.events = EPOLLIN | EPOLLOUT | EPOLLET;\n                        if (epoll_ctl(epollfd, EPOLL_CTL_MOD, epoll_events[i].data.fd, &client_fd_event) != -1)\n                        {\n                            std::cout << \"epoll_ctl successfully, mode: EPOLL_CTL_MOD, clientfd:\" << epoll_events[i].data.fd << std::endl;\n                        }\n\n                    }\n                }\n            }\n            else if (epoll_events[i].events & EPOLLOUT)\n            {\n                //只处理客户端fd的写事件\n                if (epoll_events[i].data.fd != listenfd)\n                {\n                    //打印结果\n                    std::cout << \"EPOLLOUT triggered,clientfd: \" << epoll_events[i].data.fd << std::endl;\n                }\n            }\n            else if (epoll_events[i].events & EPOLLERR)\n            {\n                //TODO 暂不处理\n            }\n        }\n    }\n\n    close(listenfd);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter04/code/epoll_server_write_event_lt.cpp",
    "content": "/**\n * 验证epoll LT与ET模式在处理写事件上的区别，epoll_server_write_event.cpp\n * zhangyl 2019.04.01\n */\n#include<sys/types.h>\n#include<sys/socket.h>\n#include<arpa/inet.h>\n#include<unistd.h>\n#include<fcntl.h>\n#include<sys/epoll.h>\n#include<poll.h>\n#include<iostream>\n#include<string.h>\n#include<vector>\n#include<errno.h>\n#include<iostream>\n\nint main()\n{\n    //创建一个监听socket\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (listenfd == -1)\n    {\n        std::cout << \"create listen socket error\" << std::endl;\n        return -1;\n    }\n\n    //设置重用IP地址和端口号\n    int on = 1;\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));\n\n    //将监听socker设置为非阻塞的\n    int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n    if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1)\n    {\n        close(listenfd);\n        std::cout << \"set listenfd to nonblock error\" << std::endl;\n        return -1;\n    }\n\n    //初始化服务器地址\n    struct sockaddr_in bindaddr;\n    bindaddr.sin_family = AF_INET;\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\n    bindaddr.sin_port = htons(3000);\n\n    if (bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) == -1)\n    {\n        std::cout << \"bind listen socker error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    //启动监听\n    if (listen(listenfd, SOMAXCONN) == -1)\n    {\n        std::cout << \"listen error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n\n    //创建epollfd\n    int epollfd = epoll_create(1);\n    if (epollfd == -1)\n    {\n        std::cout << \"create epollfd error.\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    epoll_event listen_fd_event;\n    listen_fd_event.data.fd = listenfd;\n    listen_fd_event.events = EPOLLIN;\n    //取消注释这一行，则使用ET模式\n    //listen_fd_event.events |= EPOLLET;\n\n    //将监听sokcet绑定到epollfd上\n    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &listen_fd_event) == -1)\n    {\n        std::cout << \"epoll_ctl error\" << std::endl;\n        close(listenfd);\n        return -1;\n    }\n\n    int n;\n    while (true)\n    {\n        epoll_event epoll_events[1024];\n        n = epoll_wait(epollfd, epoll_events, 1024, 1000);\n        if (n < 0)\n        {\n            //被信号中断\n            if (errno == EINTR)\n                continue;\n\n            //出错，退出\n            break;\n        }\n        else if (n == 0)\n        {\n            //超时，继续\n            continue;\n        }\n        \n        for (size_t i = 0; i < n; ++i)\n        {\n            //事件可读\n            if (epoll_events[i].events & EPOLLIN)\n            {\n                if (epoll_events[i].data.fd == listenfd)\n                {\n                    //侦听socket，接受新连接\n                    struct sockaddr_in clientaddr;\n                    socklen_t clientaddrlen = sizeof(clientaddr);\n                    int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);\n                    if (clientfd != -1)\n                    {\n                        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\n                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n                        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)\n                        {\n                            close(clientfd);\n                            std::cout << \"set clientfd to nonblocking error.\" << std::endl;\n                        }\n                        else\n                        {\n                            epoll_event client_fd_event;\n                            client_fd_event.data.fd = clientfd;\n                            //同时侦听新来连接socket的读和写事件\n                            client_fd_event.events = EPOLLIN | EPOLLOUT;\n                            //取消注释这一行时，使用ET模式\n                            //client_fd_event.events |= EPOLLET; \n                            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event) != -1)\n                            {\n                                std::cout << \"new client accepted,clientfd: \" << clientfd << std::endl;\n                            }\n                            else\n                            {\n                                std::cout << \"add client fd to epollfd error\" << std::endl;\n                                close(clientfd);\n                            }\n                        }\n                    }\n                }\n                else\n                {\n                    std::cout << \"client fd: \" << epoll_events[i].data.fd << \" recv data.\" << std::endl;\n                    //普通clientfd\n                    char recvbuf[1024] = { 0 };\n                    //读取数据\n                    int m = recv(epoll_events[i].data.fd, recvbuf, 1024, 0);\n                    if (m == 0)\n                    {\n                        //对端关闭了连接，从epollfd上移除clientfd\n                        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                        {\n                            std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\n                        }\n                        close(epoll_events[i].data.fd);\n                    }\n                    else if (m < 0)\n                    {\n                        //出错\n                        if (errno != EWOULDBLOCK && errno != EINTR)\n                        {\n                            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)\n                            {\n                                std::cout << \"client disconnected,clientfd:\" << epoll_events[i].data.fd << std::endl;\n                            }\n                            close(epoll_events[i].data.fd);\n                        }\n                    }\n                    else\n                    {\n                        //正常收到数据\n                        std::cout << \"recv from client:\" << epoll_events[i].data.fd << \", \" << recvbuf << std::endl;\n                    }\n                }\n            }\n            else if (epoll_events[i].events & EPOLLOUT)\n            {\n                //只处理客户端fd的可写事件\n                if (epoll_events[i].data.fd != listenfd)\n                {\n                    //打印结果\n                    std::cout << \"EPOLLOUT triggered,clientfd: \" << epoll_events[i].data.fd << std::endl;\n                }\n            }\n            else if (epoll_events[i].events & EPOLLERR)\n            {\n                //TODO 暂不处理\n            }\n        }\n    }\n\n    close(listenfd);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter04/code/gethostbyname_linux.cpp",
    "content": "#include <sys/types.h>  \r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <arpa/inet.h>\r\n#include <netdb.h>\r\n#include <stdio.h>\r\n\r\n//extern int h_errno;\r\n\r\nbool connect_to_server(const char* server, short port)\r\n{\r\n    int hSocket = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (hSocket == -1)\r\n        return false;\r\n\r\n    struct sockaddr_in addrSrv = { 0 };\r\n    struct hostent* pHostent = NULL;\r\n    //unsigned int addr = 0;\r\n\r\n    //如果传入的参数 server 的值是 somesite.com 这种域名域名形式则 if 条件成立，\r\n\t//接着调用 gethostbyname 解析域名为 4 字节的 ip 地址（整型）\r\n\tif (addrSrv.sin_addr.s_addr = inet_addr(server) == INADDR_NONE)\r\n    {       \r\n\t\tpHostent = gethostbyname(server);\r\n        if (pHostent == NULL)      \r\n            return false;\r\n        \r\n        //当存在多个域名时，我们只取第一个\r\n        addrSrv.sin_addr.s_addr = *((unsigned long*)pHostent->h_addr_list[0]);\r\n    }\r\n\r\n    addrSrv.sin_family = AF_INET;\r\n    addrSrv.sin_port = htons(port);\r\n    int ret = connect(hSocket, (struct sockaddr*)&addrSrv, sizeof(addrSrv));\r\n    if (ret == -1)\r\n        return false;\r\n\r\n    return true;\r\n}\r\n\r\nint main()\r\n{\r\n\tif (connect_to_server(\"baidu.com\", 80))\r\n\t\tprintf(\"connect successfully.\\n\");\r\n\telse\r\n\t\tprintf(\"connect error.\\n\");\r\n\t\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter04/code/linux_ioctl.cpp",
    "content": "/**\r\n * 演示如何获取当前 socket 对应的接收缓冲区中有多少数据可读，linux_ioctl.cpp\r\n * zhangyl 2019.11.12\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <sys/ioctl.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <poll.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <vector>\r\n#include <errno.h>\r\n\r\n//无效fd标记\r\n#define INVALID_FD  -1\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //创建一个侦听socket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == INVALID_FD)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\t\r\n\t//将侦听socket设置为非阻塞的\r\n\tint oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\r\n\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\tif (fcntl(listenfd, F_SETFL,  newSocketFlag) == -1)\r\n\t{\r\n\t\tclose(listenfd);\r\n\t\tstd::cout << \"set listenfd to nonblock error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\t//复用地址和端口号\r\n\tint on = 1;\r\n\tsetsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));\r\n\tsetsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof(on));\r\n\t\r\n\t//初始化服务器地址\r\n\tstruct sockaddr_in bindaddr;\r\n\tbindaddr.sin_family = AF_INET;\r\n\tbindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n\tbindaddr.sin_port = htons(3000);\r\n\tif (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)\r\n\t{\r\n\t    std::cout << \"bind listen socket error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n\t    return -1;\r\n\t}\r\n\t\r\n\t//启动侦听\r\n\tif (listen(listenfd, SOMAXCONN) == -1)\r\n\t{\r\n\t    std::cout << \"listen error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n\t    return -1;\r\n\t}\t\r\n\t\r\n\tstd::vector<pollfd> fds;\r\n\tpollfd listen_fd_info;\r\n\tlisten_fd_info.fd = listenfd;\r\n\tlisten_fd_info.events = POLLIN;\r\n\tlisten_fd_info.revents = 0;\r\n\tfds.push_back(listen_fd_info);\r\n\t\r\n\t//是否存在无效的fd标志\r\n\tbool exist_invalid_fd;\r\n\tint n;\r\n\twhile (true)\r\n\t{\r\n\t\texist_invalid_fd = false;\r\n\t\tn = poll(&fds[0], fds.size(), 1000);\r\n\t\tif (n < 0)\r\n\t\t{\r\n\t\t\t//被信号中断\r\n\t\t\tif (errno == EINTR)\r\n\t\t\t\tcontinue;\r\n\t\t\t\r\n\t\t\t//出错，退出\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\telse if (n == 0)\r\n\t\t{\r\n\t\t\t//超时，继续\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\t\r\n\t\tint size = fds.size();\r\n\t\tfor (size_t i = 0; i < size; ++i)\r\n\t\t{\r\n\t\t\t// 事件可读\r\n\t\t\tif (fds[i].revents & POLLIN)\r\n\t\t\t{\r\n\t\t\t\tif (fds[i].fd == listenfd)\r\n\t\t\t\t{\r\n\t\t\t\t\t//侦听socket，接受新连接\r\n\t\t\t\t\tstruct sockaddr_in clientaddr;\r\n\t\t\t\t\tsocklen_t clientaddrlen = sizeof(clientaddr);\r\n\t\t\t\t\t//接受客户端连接, 并加入到fds集合中\r\n\t\t\t\t\tint clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);\r\n\t\t\t\t\tif (clientfd != -1)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//将客户端socket设置为非阻塞的\r\n\t\t\t\t\t\tint oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n\t\t\t\t\t\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\t\t\t\t\t\tif (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tclose(clientfd);\r\n\t\t\t\t\t\t\tstd::cout << \"set clientfd to nonblock error.\" << std::endl;\t\t\t\t\t\t\r\n\t\t\t\t\t\t} \r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tstruct pollfd client_fd_info;\r\n\t\t\t\t\t\t\tclient_fd_info.fd = clientfd;\r\n\t\t\t\t\t\t\tclient_fd_info.events = POLLIN;\r\n\t\t\t\t\t\t\tclient_fd_info.revents = 0;\r\n\t\t\t\t\t\t\tfds.push_back(client_fd_info);\r\n\t\t\t\t\t\t\tstd::cout << \"new client accepted, clientfd: \" << clientfd << std::endl;\r\n\t\t\t\t\t\t}\t\t\t\t\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse \r\n\t\t\t\t{\r\n\t\t\t\t\t//socket 可读时获取当前接收缓冲区中的字节数目\r\n\t\t\t\t\tulong bytesToRecv = 0;\r\n\t\t\t\t\tif (ioctl(fds[i].fd, FIONREAD, &bytesToRecv) == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstd::cout << \"bytesToRecv: \" << bytesToRecv << std::endl;\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\r\n\t\t\t\t\t//普通clientfd,收取数据\r\n\t\t\t\t\tchar buf[64] = { 0 };\r\n\t\t\t\t\tint m = recv(fds[i].fd, buf, 64, 0);\r\n\t\t\t\t\tif (m <= 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (errno != EINTR && errno != EWOULDBLOCK)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t//出错或对端关闭了连接，关闭对应的clientfd，并设置无效标志位\t\r\n\t\t\t\t\t\t\tstd::cout << \"client disconnected, clientfd: \" << fds[i].fd << std::endl;\r\n\t\t\t\t\t\t\tclose(fds[i].fd);\r\n\t\t\t\t\t\t\tfds[i].fd = INVALID_FD;\r\n\t\t\t\t\t\t\texist_invalid_fd = true;\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t}\t\t\t\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstd::cout << \"recv from client: \" << buf << \", clientfd: \" << fds[i].fd << std::endl;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (fds[i].revents & POLLERR)\r\n\t\t\t{\r\n\t\t\t\t//TODO: 暂且不处理\r\n\t\t\t}\r\n\t\t\t\r\n\t\t}// end  outer-for-loop\r\n\t\t\r\n\t\tif (exist_invalid_fd)\r\n\t\t{\r\n\t\t\t//统一清理无效的fd\r\n\t\t\tfor (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); )\r\n\t\t\t{\r\n\t\t\t\tif (iter->fd == INVALID_FD)\r\n\t\t\t\t\titer = fds.erase(iter);\r\n\t\t\t\telse\r\n\t\t\t\t\t++iter;\r\n\t\t\t}\r\n\t\t}\t\r\n\t}// end  while-loop\r\n \r\n\t//关闭所有socket\r\n\tfor (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); ++ iter)\r\n\t\tclose(iter->fd);\t\t\t\r\n\t\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter04/code/linux_nonblocking_connect.cpp",
    "content": "/**\r\n * Linux 下正确的异步的connect写法，linux_nonblocking_connect.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //将clientfd设置成非阻塞模式\r\n    int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n    if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n    {\r\n        close(clientfd);\r\n        std::cout << \"set socket to nonblock error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    for (;;)\r\n    {\r\n        int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));\r\n        if (ret == 0)\r\n        {\r\n            std::cout << \"connect to server successfully.\" << std::endl;\r\n            close(clientfd);\r\n            return 0;\r\n        }\r\n        else if (ret == -1)\r\n        {\r\n            if (errno == EINTR)\r\n            {\r\n                //connect 动作被信号中断，重试connect\r\n                std::cout << \"connecting interruptted by signal, try again.\" << std::endl;\r\n                continue;\r\n            }\r\n            else if (errno == EINPROGRESS)\r\n            {\r\n                //连接正在尝试中\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                //真的出错了，\r\n                close(clientfd);\r\n                return -1;\r\n            }\r\n        }\r\n    }\r\n\r\n    fd_set writeset;\r\n    FD_ZERO(&writeset);\r\n    FD_SET(clientfd, &writeset);\r\n    struct timeval tv;\r\n    tv.tv_sec = 3;\r\n    tv.tv_usec = 0;\r\n    //3.调用select函数判断socket是否可写\r\n    if (select(clientfd + 1, NULL, &writeset, NULL, &tv) != 1)\r\n    {\r\n        std::cout << \"[select] connect to server error.\" << std::endl;\r\n        close(clientfd);\r\n        return -1;\r\n    }\r\n\r\n    int err;\r\n    socklen_t len = static_cast<socklen_t>(sizeof err);\r\n    //4.调用getsockopt检测此时socket是否出错\r\n    if (::getsockopt(clientfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)\r\n    {\r\n        close(clientfd);\r\n        return -1;\r\n    }\r\n\r\n    if (err == 0)\r\n        std::cout << \"connect to server successfully.\" << std::endl;\r\n    else\r\n        std::cout << \"connect to server error.\" << std::endl;\r\n\r\n    close(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/linux_nonblocking_connect_poll.cpp",
    "content": "/**\r\n * Linux ʹpollʵ첽connectlinux_nonblocking_connect_poll.cpp\r\n * zhangyl 2019.03.16\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <poll.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.һsocket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\t\r\n\t// clientfd óɷģʽ\t\r\n\tint oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\tif (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"set socket to nonblock error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\r\n    //2.ӷ\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n\tfor (;;)\r\n\t{\r\n\t\tint ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));\r\n\t\tif (ret == 0)\r\n\t\t{\r\n\t\t\tstd::cout << \"connect to server successfully.\" << std::endl;\r\n\t\t\tclose(clientfd);\r\n\t\t\treturn 0;\r\n\t\t} \r\n\t\telse if (ret == -1) \r\n\t\t{\r\n\t\t\tif (errno == EINTR)\r\n\t\t\t{\r\n\t\t\t\t//connect źжϣconnect\r\n\t\t\t\tstd::cout << \"connecting interruptted by signal, try again.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} else if (errno == EINPROGRESS)\r\n\t\t\t{\r\n\t\t\t\t//ڳ\r\n\t\t\t\tbreak;\r\n\t\t\t} else {\r\n\t\t\t\t//ĳˣ\r\n\t\t\t\tclose(clientfd);\r\n\t\t\t\treturn -1;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\tpollfd event;\r\n\tevent.fd = clientfd;\r\n\tevent.events = POLLOUT;\r\n\tint timeout = 3000;\r\n\tif (poll(&event, 1, timeout) != 1)\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"[poll] connect to server error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\tif (!(event.revents & POLLOUT))\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"[POLLOUT] connect to server error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\tint err;\r\n    socklen_t len = static_cast<socklen_t>(sizeof err);\r\n    if (::getsockopt(clientfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)\r\n        return -1;\r\n        \r\n    if (err == 0)\r\n        std::cout << \"connect to server successfully.\" << std::endl;\r\n    else\r\n    \tstd::cout << \"connect to server error.\" << std::endl;\r\n    \r\n\t//5. رsocket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/nagle_client.cpp",
    "content": "/**\r\n * 验证nagle算法\r\n * zhangyl 2018.12.13\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\t\r\n\tint count = 1000;\r\n\tint ret;\r\n\t//3. 连续向服务器发送1000次数据\r\n\twhile (count > 0)\r\n\t{\r\n\t\tret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\r\n\t\tif (ret != strlen(SEND_DATA))\r\n\t\t{\r\n\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcount --;\r\n\t}\r\n\t\r\n\tif (count == 0)\r\n\t\tstd::cout << \"send data successfully\" << std::endl;\r\n\t\r\n\t//4. 从客户端收取数据\r\n\tchar recvBuf[32] = {0};\r\n\tret = recv(clientfd, recvBuf, 32, 0);\r\n\tif (ret > 0) \r\n\t{\r\n\t\tstd::cout << \"recv data successfully, data: \" << recvBuf << std::endl;\r\n\t} \r\n\telse \r\n\t{\r\n\t\tstd::cout << \"recv data error, data: \" << recvBuf << std::endl;\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/nodelay_client.cpp",
    "content": "/**\r\n * 验证禁用nagle算法\r\n * zhangyl 2018.12.13\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n//for TCP_NODELAY definition\r\n#include <netinet/tcp.h>\t\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\t\r\n\t//禁用nagle算法\r\n\tint optval = 1;\r\n\tif (setsockopt(clientfd, IPPROTO_TCP, TCP_NODELAY, &optval, static_cast<socklen_t>(sizeof optval)) != 0)\r\n\t{\r\n        std::cout << \"set nodelay error.\" << std::endl;\r\n        close(clientfd);\r\n        return -1;\r\n\t}\r\n\t\r\n\tint count = 1000;\r\n\tint ret;\r\n\t//3. 连续向服务器发送1000次数据\r\n\twhile (count > 0)\r\n\t{\r\n\t\tret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\r\n\t\tif (ret != strlen(SEND_DATA))\r\n\t\t{\r\n\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcount --;\r\n\t}\r\n\t\r\n\tif (count == 0)\r\n\t\tstd::cout << \"send data successfully\" << std::endl;\r\n\t\r\n\t//4. 从客户端收取数据\r\n\tchar recvBuf[32] = {0};\r\n\tret = recv(clientfd, recvBuf, 32, 0);\r\n\tif (ret > 0) \r\n\t{\r\n\t\tstd::cout << \"recv data successfully, data: \" << recvBuf << std::endl;\r\n\t} \r\n\telse \r\n\t{\r\n\t\tstd::cout << \"recv data error, data: \" << recvBuf << std::endl;\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/nonblocking_client.cpp",
    "content": "/**\n * 验证非阻塞模式下send函数的行为，client端，nonblocking_client.cpp\n * zhangyl 2018.12.17\n */\n#include <sys/types.h> \n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <iostream>\n#include <string.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#define SERVER_ADDRESS \"127.0.0.1\"\n#define SERVER_PORT     3000\n#define SEND_DATA       \"helloworld\"\n\nint main(int argc, char* argv[])\n{\n    //1.创建一个socket\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (clientfd == -1)\n    {\n        std::cout << \"create client socket error.\" << std::endl;\n        return -1;\n    }\n\n    //2.连接服务器\n    struct sockaddr_in serveraddr;\n    serveraddr.sin_family = AF_INET;\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\n    serveraddr.sin_port = htons(SERVER_PORT);\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\n    {\n        std::cout << \"connect socket error.\" << std::endl;\n        close(clientfd);\n        return -1;\n    }\n    \n    //连接成功以后，我们再将clientfd设置为非阻塞模式，\n    //不能在创建时就设置，这样会影响到connect函数的行为\n    int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\n    if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\n    {\n        close(clientfd);\n        std::cout << \"set socket to nonblock error.\" << std::endl;\n        return -1;\n    }\n    \n    //3.不断地向服务器发送数据，或者出错退出\n    int count = 0;\n    while (true)\n    {\n        int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\n        if (ret == -1) \n        {\n            //非阻塞模式下，send函数由于TCP窗口太小发不出去数据，错误码是EWOULDBLOCK\n            if (errno == EWOULDBLOCK)\n            {\n                std::cout << \"send data error as TCP Window size is too small.\" << std::endl;\n                continue;\n            } \n            else if (errno == EINTR)\n            {\n                //如果被信号中断，则继续重试\n                std::cout << \"sending data interrupted by signal.\" << std::endl;\n                continue;\n            } \n            else \n            {\n                std::cout << \"send data error.\" << std::endl;\n                break;\n            }\n        }\n        else if (ret == 0)\n        {\n            //对端关闭了连接，我们也关闭\n            std::cout << \"send data error.\" << std::endl;\n            close(clientfd);\n            break;\n        } \n        else\n        {\n            count ++;\n            std::cout << \"send data successfully, count = \" << count << std::endl;\n        }\n    }\n    \n    //4.关闭socket\n    close(clientfd);\n    \n    return 0;\n}"
  },
  {
    "path": "Chapter04/code/nonblocking_client_recv.cpp",
    "content": "/**\r\n * 验证非阻塞模式下recv函数的行为，client端，nonblocking_client_recv.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\t\r\n\t//连接成功以后，我们再将 clientfd 设置成非阻塞模式，\r\n\t//不能在创建时就设置，这样会影响到 connect 函数的行为\r\n\tint oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\tif (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"set socket to nonblock error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\twhile (true)\r\n\t{\r\n\t\tchar recvbuf[32] = {0};\r\n\t\t//由于clientfd被设置成了非阻塞模式，所以无论是否有数据，recv函数都不会阻塞程序\r\n\t\tint ret = recv(clientfd, recvbuf, 32, 0);\r\n\t\tif (ret > 0) \r\n\t\t{\r\n\t\t\t//收到了数据\r\n\t\t\tstd::cout << \"recv successfully.\" << std::endl;\r\n\t\t} \r\n\t\telse if (ret == 0)\r\n\t\t{\r\n\t\t\t//对端关闭了连接\r\n\t\t\tstd::cout << \"peer close the socket.\" << std::endl;\t\r\n\t\t\tbreak;\r\n\t\t} \r\n\t\telse if (ret == -1) \r\n\t\t{\r\n\t\t\tif (errno == EWOULDBLOCK)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"There is no data available now.\" << std::endl;\r\n\t\t\t} \r\n\t\t\telse if (errno == EINTR) \r\n\t\t\t{\r\n\t\t\t\t//如果被信号中断了，则继续重试recv函数\r\n\t\t\t\tstd::cout << \"recv data interrupted by signal.\" << std::endl;\t\t\t\t\r\n\t\t\t} else\r\n\t\t\t{\r\n\t\t\t\t//真的出错了\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\t//3. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/nonblocking_client_send.cpp",
    "content": "/**\r\n * 验证非阻塞模式下send函数的行为，client端，nonblocking_client.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\t\r\n\t//连接成功以后，我们再将 clientfd 设置成非阻塞模式，\r\n\t//不能在创建时就设置，这样会影响到 connect 函数的行为\r\n\tint oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\tif (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"set socket to nonblock error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\r\n\t//3. 不断向服务器发送数据，或者出错退出\r\n\tint count = 0;\r\n\twhile (true)\r\n\t{\r\n\t\tint ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);\r\n\t\tif (ret == -1) \r\n\t\t{\r\n\t\t\t//非阻塞模式下send函数由于TCP窗口太小发不出去数据，错误码是EWOULDBLOCK\r\n\t\t\tif (errno == EWOULDBLOCK)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"send data error as TCP Window size is too small.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} \r\n\t\t\telse if (errno == EINTR)\r\n\t\t\t{\r\n\t\t\t\t//如果被信号中断，我们继续重试\r\n\t\t\t\tstd::cout << \"sending data interrupted by signal.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} \r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (ret == 0)\r\n\t\t{\r\n\t\t\t//对端关闭了连接，我们也关闭\r\n\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\tclose(clientfd);\r\n\t\t\tbreak;\r\n\t\t} \r\n\t\telse\r\n\t\t{\r\n\t\t\tcount ++;\r\n\t\t\tstd::cout << \"send data successfully, count = \" << count << std::endl;\r\n\t\t}\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/nonblocking_client_send_zero_bytes.cpp",
    "content": "/**\r\n * 验证非阻塞模式下send函数发送0字节的行为，client端，\r\n * nonblocking_client_send_zero_bytes.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\t\r\n\t//连接成功以后，我们再将 clientfd 设置成非阻塞模式，\r\n\t//不能在创建时就设置，这样会影响到 connect 函数的行为\r\n\tint oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\tif (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"set socket to nonblock error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\r\n\t//3. 不断向服务器发送数据，或者出错退出\r\n\tint count = 0;\r\n\twhile (true)\r\n\t{\r\n\t\t//发送 0 字节的数据\r\n\t\tint ret = send(clientfd, SEND_DATA, 0, 0);\r\n\t\tif (ret == -1) \r\n\t\t{\r\n\t\t\t//非阻塞模式下send函数由于TCP窗口太小发不出去数据，错误码是EWOULDBLOCK\r\n\t\t\tif (errno == EWOULDBLOCK)\r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"send data error as TCP Window size is too small.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} \r\n\t\t\telse if (errno == EINTR)\r\n\t\t\t{\r\n\t\t\t\t//如果被信号中断，我们继续重试\r\n\t\t\t\tstd::cout << \"sending data interrupted by signal.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} \r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (ret == 0)\r\n\t\t{\r\n\t\t\t//发送了0字节\r\n\t\t\tstd::cout << \"send 0 byte data.\" << std::endl;\r\n\t\t} \r\n\t\telse\r\n\t\t{\r\n\t\t\tcount ++;\r\n\t\t\tstd::cout << \"send data successfully, count = \" << count << std::endl;\r\n\t\t}\r\n\t\t\r\n\t\t//每三秒发一次\r\n\t\tsleep(3);\r\n\t}\r\n\t\r\n\t//5. 关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/nonblocking_connect.cpp",
    "content": "/**\r\n * 异步的connect写法，nonblocking_connect.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define SERVER_ADDRESS  \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n#define SEND_DATA       \"helloworld\"\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\t\r\n\t//将clientfd设置成非阻塞模式\r\n\tint oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n\tint newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n\tif (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)\r\n\t{\r\n\t\tclose(clientfd);\r\n\t\tstd::cout << \"set socket to nonblock error.\" << std::endl;\r\n\t\treturn -1;\r\n\t}\r\n\t\r\n\t//2.连接服务器\r\n\tstruct sockaddr_in serveraddr;\r\n\tserveraddr.sin_family = AF_INET;\r\n\tserveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n\tserveraddr.sin_port = htons(SERVER_PORT);\r\n\tfor (;;)\r\n\t{\r\n\t\tint ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));\r\n\t\tif (ret == 0)\r\n\t\t{\r\n\t\t\tstd::cout << \"connect to server successfully.\" << std::endl;\r\n\t\t\tclose(clientfd);\r\n\t\t\treturn 0;\r\n\t\t} \r\n\t\telse if (ret == -1) \r\n\t\t{\r\n\t\t\tif (errno == EINTR)\r\n\t\t\t{\r\n\t\t\t\t//connect 动作被信号中断，重试connect\r\n\t\t\t\tstd::cout << \"connecting interruptted by signal, try again.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} \r\n\t\t\telse if (errno == EINPROGRESS)\r\n\t\t\t{\r\n\t\t\t\t//连接正在尝试中\r\n\t\t\t\tbreak;\r\n\t\t\t} \r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//真的出错了，\r\n\t\t\t\tclose(clientfd);\r\n\t\t\t\treturn -1;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\tfd_set writeset;\r\n\tFD_ZERO(&writeset);\r\n\tFD_SET(clientfd, &writeset);\r\n\tstruct timeval tv;\r\n\ttv.tv_sec = 3;  \r\n\ttv.tv_usec = 0;\r\n\t//3.调用select函数判断socket是否可写\r\n\tif (select(clientfd + 1, NULL, &writeset, NULL, &tv) == 1)\r\n\t{\r\n\t\tstd::cout << \"[select] connect to server successfully.\" << std::endl;\r\n\t} \r\n\telse \r\n\t{\r\n\t\tstd::cout << \"[select] connect to server error.\" << std::endl;\r\n\t}\r\n\r\n\tclose(clientfd);\r\n\t\r\n\treturn 0;\r\n}"
  },
  {
    "path": "Chapter04/code/poll_server.cpp",
    "content": "/**\r\n * 演示 poll 函数的用法，poll_server.cpp\r\n * zhangyl 2019.03.16\r\n */\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <poll.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <vector>\r\n#include <errno.h>\r\n\r\n//无效fd标记\r\n#define INVALID_FD  -1\r\n\r\nint main(int argc, char *argv[])\r\n{\r\n    //创建一个侦听socket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == INVALID_FD)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //将侦听socket设置为非阻塞的\r\n    int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);\r\n    int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n    if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1)\r\n    {\r\n        close(listenfd);\r\n        std::cout << \"set listenfd to nonblock error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //复用地址和端口号\r\n    int on = 1;\r\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));\r\n    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof(on));\r\n\r\n    //初始化服务器地址\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n    if (bind(listenfd, (struct sockaddr *) &bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socket error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    //启动侦听\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n        close(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    std::vector<pollfd> fds;\r\n    pollfd listen_fd_info;\r\n    listen_fd_info.fd = listenfd;\r\n    listen_fd_info.events = POLLIN;\r\n    listen_fd_info.revents = 0;\r\n    fds.push_back(listen_fd_info);\r\n\r\n    //是否存在无效的fd标志\r\n    bool exist_invalid_fd;\r\n    int n;\r\n    while (true)\r\n    {\r\n        exist_invalid_fd = false;\r\n        n = poll(&fds[0], fds.size(), 1000);\r\n        if (n < 0)\r\n        {\r\n            //被信号中断\r\n            if (errno == EINTR)\r\n                continue;\r\n\r\n            //出错，退出\r\n            break;\r\n        }\r\n        else if (n == 0)\r\n        {\r\n            //超时，继续\r\n            continue;\r\n        }\r\n\r\n        for (size_t i = 0; i < fds.size(); ++i)\r\n        {\r\n            // 事件可读\r\n            if (fds[i].revents & POLLIN)\r\n            {\r\n                if (fds[i].fd == listenfd)\r\n                {\r\n                    //侦听socket，接受新连接\r\n                    struct sockaddr_in clientaddr;\r\n                    socklen_t clientaddrlen = sizeof(clientaddr);\r\n                    //接受客户端连接, 并加入到fds集合中\r\n                    int clientfd = accept(listenfd, (struct sockaddr *) &clientaddr, &clientaddrlen);\r\n                    if (clientfd != -1)\r\n                    {\r\n                        //将客户端socket设置为非阻塞的\r\n                        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);\r\n                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;\r\n                        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)\r\n                        {\r\n                            close(clientfd);\r\n                            std::cout << \"set clientfd to nonblock error.\" << std::endl;\r\n                        }\r\n                        else\r\n                        {\r\n                            struct pollfd client_fd_info;\r\n                            client_fd_info.fd = clientfd;\r\n                            client_fd_info.events = POLLIN;\r\n                            client_fd_info.revents = 0;\r\n                            fds.push_back(client_fd_info);\r\n                            std::cout << \"new client accepted, clientfd: \" << clientfd << std::endl;\r\n                        }\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    //普通clientfd,收取数据\r\n                    char buf[64] = {0};\r\n                    int m = recv(fds[i].fd, buf, 64, 0);\r\n                    if (m <= 0)\r\n                    {\r\n                        if (errno != EINTR && errno != EWOULDBLOCK)\r\n                        {\r\n                            //出错或对端关闭了连接，关闭对应的clientfd，并设置无效标志位\r\n                            for (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); ++iter)\r\n                            {\r\n                                if (iter->fd == fds[i].fd)\r\n                                {\r\n                                    std::cout << \"client disconnected, clientfd: \" << fds[i].fd << std::endl;\r\n                                    close(fds[i].fd);\r\n                                    iter->fd = INVALID_FD;\r\n                                    exist_invalid_fd = true;\r\n                                    break;\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                    else\r\n                    {\r\n                        std::cout << \"recv from client: \" << buf << \", clientfd: \" << fds[i].fd << std::endl;\r\n                    }\r\n                }\r\n            } else if (fds[i].revents & POLLERR)\r\n            {\r\n                //TODO: 暂且不处理\r\n            }\r\n\r\n        }// end  outer-for-loop\r\n\r\n        if (exist_invalid_fd)\r\n        {\r\n            //统一清理无效的fd\r\n            for (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end();)\r\n            {\r\n                if (iter->fd == INVALID_FD)\r\n                    iter = fds.erase(iter);\r\n                else\r\n                    ++iter;\r\n            }\r\n        }\r\n    }// end  while-loop\r\n\r\n\r\n    //关闭所有socket\r\n    for (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); ++iter)\r\n        close(iter->fd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/select_client_tv0.cpp",
    "content": "/**\r\n * 验证select时间参数设置为0，select_client_tv0.cpp\r\n * zhangyl 2018.12.25\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <string.h>\r\n#include <errno.h>\r\n#include <iostream>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\r\n\tint ret;\r\n\twhile (true)\r\n\t{\r\n\t\tfd_set readset;\r\n\t\tFD_ZERO(&readset);\r\n\t\t//将侦听socket加入到待检测的可读事件中去\r\n\t\tFD_SET(clientfd, &readset);\t\r\n\t\ttimeval tm;\r\n\t\ttm.tv_sec = 0;\r\n\t\ttm.tv_usec = 0;\t\r\n\t\t\r\n\t\t//暂且只检测可读事件，不检测可写和异常事件\r\n\t\tret = select(clientfd + 1, &readset, NULL, NULL, &tm);\r\n\t\tstd::cout << \"tm.tv_sec: \" << tm.tv_sec << \", tm.tv_usec: \" << tm.tv_usec << std::endl;\r\n\t\tif (ret == -1)\r\n\t\t{\r\n\t\t\t//除了被信号中断的情形，其他情况都是出错\r\n\t\t\tif (errno != EINTR)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\telse if (ret == 0)\r\n\t\t{\r\n\t\t\t//select函数超时\r\n\t\t\tstd::cout << \"no event in specific time interval.\" << std::endl;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (FD_ISSET(clientfd, &readset))\r\n\t\t\t{\r\n\t\t\t\t//检测到可读事件\r\n\t\t\t\tchar recvbuf[32];\r\n\t\t\t\tmemset(recvbuf, 0, sizeof(recvbuf));\r\n\t\t\t\t//假设对端发数据的时候不超过31个字符。\r\n\t\t\t\tint n = recv(clientfd, recvbuf, 32, 0);\r\n\t\t\t\tif (n < 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t//除了被信号中断的情形，其他情况都是出错\r\n\t\t\t\t\tif (errno != EINTR)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\telse if (n == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t//对端关闭了连接\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::cout << \"recv data: \" << recvbuf << std::endl;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"other socket event.\" << std::endl;\r\n\t\t\t}\r\n\t\t}\r\n\t}\t\t\r\n\t\r\n\t\r\n\t//关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/select_client_tvnull.cpp",
    "content": "/**\r\n * 验证select时间参数设置为NULL，select_client_tvnull.cpp\r\n * zhangyl 2018.12.25\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <string.h>\r\n#include <errno.h>\r\n#include <iostream>\r\n\r\n#define SERVER_ADDRESS \"127.0.0.1\"\r\n#define SERVER_PORT     3000\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //创建一个socket\r\n    int clientfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (clientfd == -1)\r\n    {\r\n        std::cout << \"create client socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //连接服务器\r\n    struct sockaddr_in serveraddr;\r\n    serveraddr.sin_family = AF_INET;\r\n    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);\r\n    serveraddr.sin_port = htons(SERVER_PORT);\r\n    if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)\r\n    {\r\n        std::cout << \"connect socket error.\" << std::endl;\r\n\t\tclose(clientfd);\r\n        return -1;\r\n    }\r\n\r\n\tint ret;\r\n\twhile (true)\r\n\t{\r\n\t\tfd_set readset;\r\n\t\tFD_ZERO(&readset);\r\n\t\t//将侦听socket加入到待检测的可读事件中去\r\n\t\tFD_SET(clientfd, &readset);\t\r\n\t\t//timeval tm;\r\n\t\t//tm.tv_sec = 0;\r\n\t\t//tm.tv_usec = 0;\t\r\n\t\t\r\n\t\t//暂且只检测可读事件，不检测可写和异常事件\r\n\t\tret = select(clientfd + 1, &readset, NULL, NULL, NULL);\r\n\t\tif (ret == -1)\r\n\t\t{\r\n\t\t\t//除了被信号中断的情形，其他情况都是出错\r\n\t\t\tif (errno != EINTR)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\telse if (ret == 0)\r\n\t\t{\r\n\t\t\t//select函数超时\r\n\t\t\tstd::cout << \"no event in specific time interval.\" << std::endl;\r\n\t\t\tcontinue;\r\n\t\t} \r\n\t\telse\r\n\t\t{\r\n\t\t\tif (FD_ISSET(clientfd, &readset))\r\n\t\t\t{\r\n\t\t\t\t//检测到可读事件\r\n\t\t\t\tchar recvbuf[32];\r\n\t\t\t\tmemset(recvbuf, 0, sizeof(recvbuf));\r\n\t\t\t\t//假设对端发数据的时候不超过31个字符。\r\n\t\t\t\tint n = recv(clientfd, recvbuf, 32, 0);\r\n\t\t\t\tif (n < 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t//除了被信号中断的情形，其他情况都是出错\r\n\t\t\t\t\tif (errno != EINTR)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\telse if (n == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t//对端关闭了连接\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tstd::cout << \"recv data: \" << recvbuf << std::endl;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"other socket event.\" << std::endl;\r\n\t\t\t}\r\n\t\t}\r\n\t}\t\t\r\n\t\r\n\t//关闭socket\r\n\tclose(clientfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/select_server.cpp",
    "content": "/**\r\n * select函数示例，server端, select_server.cpp\r\n * zhangyl 2018.12.24\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <sys/time.h>\r\n#include <vector>\r\n#include <errno.h>\r\n\r\n//自定义代表无效fd的值\r\n#define INVALID_FD -1\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //创建一个侦听socket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == INVALID_FD)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //初始化服务器地址\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n    if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socket error.\" << std::endl;\r\n    \tclose(listenfd);\r\n        return -1;\r\n    }\r\n    \r\n    //启动侦听\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n    \tclose(listenfd);\r\n        return -1;\r\n    }\r\n    \r\n    //存储客户端socket的数组\r\n    std::vector<int> clientfds;\r\n    int maxfd;\r\n    \r\n    while (true) \r\n    {\t\r\n    \tfd_set readset;\r\n    \tFD_ZERO(&readset);\r\n    \t\r\n    \t//将侦听socket加入到待检测的可读事件中去\r\n    \tFD_SET(listenfd, &readset);\r\n    \t\r\n    \tmaxfd = listenfd;\r\n    \t//将客户端fd加入到待检测的可读事件中去\r\n    \tint clientfdslength = clientfds.size();\r\n    \tfor (int i = 0; i < clientfdslength; ++i)\r\n    \t{\r\n    \t\tif (clientfds[i] != INVALID_FD)\r\n    \t\t{\r\n    \t\t\tFD_SET(clientfds[i], &readset);\r\n\r\n    \t\t\tif (maxfd < clientfds[i])\r\n    \t\t\t\tmaxfd = clientfds[i];\r\n    \t\t}\r\n    \t}\r\n    \t\r\n    \ttimeval tm;\r\n    \ttm.tv_sec = 1;\r\n    \ttm.tv_usec = 0;\r\n    \t//暂且只检测可读事件，不检测可写和异常事件\r\n    \tint ret = select(maxfd + 1, &readset, NULL, NULL, &tm);\r\n    \tif (ret == -1)\r\n    \t{\r\n    \t\t//出错，退出程序。\r\n    \t\tif (errno != EINTR)\r\n    \t\t\tbreak;\r\n    \t}\r\n    \telse if (ret == 0)\r\n    \t{\r\n    \t\t//select 函数超时，下次继续\r\n    \t\tcontinue;\r\n    \t} \r\n    \telse\r\n        {\r\n    \t\t//检测到某个socket有事件\r\n    \t\tif (FD_ISSET(listenfd, &readset))\r\n    \t\t{\r\n    \t\t\t//侦听socket的可读事件，则表明有新的连接到来\r\n    \t\t\tstruct sockaddr_in clientaddr;\r\n    \t\t\tsocklen_t clientaddrlen = sizeof(clientaddr);\r\n    \t\t\t//4. 接受客户端连接\r\n    \t\t\tint clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);\r\n    \t\t\tif (clientfd == INVALID_FD)\t\t\t\t\t\r\n    \t\t\t{         \t\r\n    \t\t\t\t//接受连接出错，退出程序\r\n    \t\t\t\tbreak;\r\n    \t\t\t}\r\n    \t\t\t\r\n    \t\t\t//只接受连接，不调用recv收取任何数据\r\n    \t\t\tstd:: cout << \"accept a client connection, fd: \" << clientfd << std::endl;\r\n    \t\t\tclientfds.push_back(clientfd);\r\n    \t\t} \r\n    \t\telse \r\n    \t\t{\r\n    \t\t\t//假设对端发来的数据长度不超过63个字符\r\n    \t\t\tchar recvbuf[64];\r\n    \t\t\tint clientfdslength = clientfds.size();\r\n    \t\t\tfor (int i = 0; i < clientfdslength; ++i)\r\n    \t\t\t{\r\n    \t\t\t\tif (clientfds[i] != INVALID_FD && FD_ISSET(clientfds[i], &readset))\r\n    \t\t\t\t{\t\t\t\t\r\n    \t\t\t\t\tmemset(recvbuf, 0, sizeof(recvbuf));\r\n    \t\t\t\t\t//非侦听socket，则接收数据\r\n    \t\t\t\t\tint length = recv(clientfds[i], recvbuf, 64, 0);\r\n    \t\t\t\t\tif (length <= 0)\r\n    \t\t\t\t\t{\r\n    \t\t\t\t\t\t//收取数据出错了\r\n    \t\t\t\t\t\tstd::cout << \"recv data error, clientfd: \" << clientfds[i] << std::endl;\t\t\t\t\t\t\t\r\n    \t\t\t\t\t\tclose(clientfds[i]);\r\n    \t\t\t\t\t\t//不直接删除该元素，将该位置的元素置位INVALID_FD\r\n    \t\t\t\t\t\tclientfds[i] = INVALID_FD;\r\n    \t\t\t\t\t\tcontinue;\r\n    \t\t\t\t\t}\r\n    \t\t\t\t\t\r\n    \t\t\t\t\tstd::cout << \"clientfd: \" << clientfds[i] << \", recv data: \" << recvbuf << std::endl;\t\t\t\t\t\r\n    \t\t\t\t}\r\n    \t\t\t}\r\n    \t\t\t\r\n    \t\t}\r\n    \t}\r\n    }\r\n    \r\n    //关闭所有客户端socket\r\n    int clientfdslength = clientfds.size();\r\n    for (int i = 0; i < clientfdslength; ++i)\r\n    {\r\n    \tif (clientfds[i] != INVALID_FD)\r\n    \t{\r\n    \t\tclose(clientfds[i]);\r\n    \t}\r\n    }\r\n    \r\n    //关闭侦听socket\r\n    close(listenfd);\r\n    \r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/server.cpp",
    "content": "/**\r\n * TCPͨŻ\r\n * zhangyl 2018.12.13\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.һsocket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == -1)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.ʼַ\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n    if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socket error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3.\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\r\n    while (true)\r\n    {\r\n        struct sockaddr_in clientaddr;\r\n        socklen_t clientaddrlen = sizeof(clientaddr);\r\n\t\t//4. ܿͻ\r\n        int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);\r\n        if (clientfd != -1)\r\n        {         \t\r\n\t\t\tchar recvBuf[32] = {0};\r\n\t\t\t//5. ӿͻ˽\r\n\t\t\tint ret = recv(clientfd, recvBuf, 32, 0);\r\n\t\t\tif (ret > 0) \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"recv data from client, data: \" << recvBuf << std::endl;\r\n\t\t\t\t//6. յԭⲻطͻ\r\n\t\t\t\tret = send(clientfd, recvBuf, strlen(recvBuf), 0);\r\n\t\t\t\tif (ret != strlen(recvBuf))\r\n\t\t\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\t\telse\r\n\t\t\t\t\tstd::cout << \"send data to client successfully, data: \" << recvBuf << std::endl;\r\n\t\t\t} \r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"recv data error.\" << std::endl;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tclose(clientfd);\r\n        }\r\n    }\r\n\t\r\n\t//7.رsocket\r\n\tclose(listenfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/server2.cpp",
    "content": "/**\r\n * TCPͨŻ\r\n * zhangyl 2018.12.13\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <vector>\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.һsocket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == -1)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.ʼַ\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n    if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socket error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3.\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\t\r\n\t//¼пͻӵ\r\n\tstd::vector<int> clientfds;\r\n    while (true)\r\n    {\r\n        struct sockaddr_in clientaddr;\r\n        socklen_t clientaddrlen = sizeof(clientaddr);\r\n\t\t//4. ܿͻ\r\n        int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);\r\n        if (clientfd != -1)\r\n        {         \t\r\n\t\t\tchar recvBuf[32] = {0};\r\n\t\t\t//5. ӿͻ˽\r\n\t\t\tint ret = recv(clientfd, recvBuf, 32, 0);\r\n\t\t\tif (ret > 0) \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"recv data from client, data: \" << recvBuf << std::endl;\r\n\t\t\t\t//6. յԭⲻطͻ\r\n\t\t\t\tret = send(clientfd, recvBuf, strlen(recvBuf), 0);\r\n\t\t\t\tif (ret != strlen(recvBuf))\r\n\t\t\t\t\tstd::cout << \"send data error.\" << std::endl;\r\n\t\t\t\t\r\n\t\t\t\tstd::cout << \"send data to client successfully, data: \" << recvBuf << std::endl;\r\n\t\t\t} \r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"recv data error.\" << std::endl;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t//close(clientfd);\r\n\t\t\tclientfds.push_back(clientfd);\r\n        }\r\n    }\r\n\t\r\n\t//7.رsocket\r\n\tclose(listenfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter04/code/server_recv_zero_bytes.cpp",
    "content": "/**\r\n * 验证recv函数接受0字节的行为，server端，server_recv_zero_bytes.cpp\r\n * zhangyl 2018.12.17\r\n */\r\n#include <sys/types.h> \r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n#include <string.h>\r\n#include <vector>\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    //1.创建一个侦听socket\r\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (listenfd == -1)\r\n    {\r\n        std::cout << \"create listen socket error.\" << std::endl;\r\n        return -1;\r\n    }\r\n\r\n    //2.初始化服务器地址\r\n    struct sockaddr_in bindaddr;\r\n    bindaddr.sin_family = AF_INET;\r\n    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    bindaddr.sin_port = htons(3000);\r\n    if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)\r\n    {\r\n        std::cout << \"bind listen socket error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\r\n\t//3.启动侦听\r\n    if (listen(listenfd, SOMAXCONN) == -1)\r\n    {\r\n        std::cout << \"listen error.\" << std::endl;\r\n\t\tclose(listenfd);\r\n        return -1;\r\n    }\r\n\t\r\n\tint clientfd; \r\n\tstruct sockaddr_in clientaddr;\r\n\tsocklen_t clientaddrlen = sizeof(clientaddr);\r\n\t//4. 接受客户端连接\r\n\tclientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);\r\n\tif (clientfd != -1)\r\n\t{         \t\r\n\t\twhile (true)\r\n\t\t{\r\n\t\t\tchar recvBuf[32] = {0};\r\n\t\t\t//5. 从客户端接受数据,客户端没有数据来的时候会recv函数会阻塞\r\n\t\t\tint ret = recv(clientfd, recvBuf, 32, 0);\r\n\t\t\tif (ret > 0) \r\n\t\t\t{\r\n\t\t\t\tstd::cout << \"recv data from client, data: \" << recvBuf << std::endl;\t\t\t\t\r\n\t\t\t} \r\n\t\t\telse if (ret == 0)\r\n\t\t\t{\r\n\t\t\t\t//“假设recv返回值为0时是收到了0个字节”\r\n\t\t\t\tstd::cout << \"recv 0 byte data.\" << std::endl;\r\n\t\t\t\tcontinue;\r\n\t\t\t} \r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//出错\r\n\t\t\t\tstd::cout << \"recv data error.\" << std::endl;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\t\t\t\t\r\n\t}\r\n\r\n\t\r\n\t//关闭客户端socket\r\n\tclose(clientfd);\r\n\t//7.关闭侦听socket\r\n\tclose(listenfd);\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter06/code/ProtocolStream.cpp",
    "content": "#ifndef _WIN32\r\n#include <arpa/inet.h>\r\n#else\r\n#include <Winsock2.h>\r\n#pragma comment(lib, \"Ws2_32.lib\")\r\n#endif\r\n\r\n#include \"ProtocolStream.h\"\r\n#include <string.h>\r\n#include <stdio.h>\r\n#include <sys/types.h>\r\n#include <cassert>\r\n#include <algorithm>\r\n#include <stdio.h>\r\n\r\nusing namespace std;\r\n\r\nnamespace net\r\n{\r\n    //У\r\n    unsigned short checksum(const unsigned short* buffer, int size)\r\n    {\r\n        unsigned int cksum = 0;\r\n        while (size > 1)\r\n        {\r\n            cksum += *buffer++;\r\n            size -= sizeof(unsigned short);\r\n        }\r\n        if (size)\r\n        {\r\n            cksum += *(unsigned char*)buffer;\r\n        }\r\n        //32λת16\r\n        while (cksum >> 16)\r\n            cksum = (cksum >> 16) + (cksum & 0xffff);\r\n\r\n        return (unsigned short)(~cksum);\r\n    }\r\n\r\n    //һ4ֽڵֵѹ1~5ֽ\r\n    void write7BitEncoded(uint32_t value, std::string& buf)\r\n    {\n        do\n        {\n            unsigned char c = (unsigned char)(value & 0x7F);\n            value >>= 7;\n            if (value)\n                c |= 0x80;\n\n            buf.append(1, c);\n        } while (value);\r\n    }\r\n\r\n    //һ8ֽڵֵ1~10ֽ\r\n    void write7BitEncoded(uint64_t value, std::string& buf)\n    {\n        do\n        {\n            unsigned char c = (unsigned char)(value & 0x7F);\n            value >>= 7;\n            if (value)\n                c |= 0x80;\n\n            buf.append(1, c);\n        } while (value);\n    }\r\n\r\n    //һ1~5ֽڵֵַԭ4ֽڵֵ\r\n    void read7BitEncoded(const char* buf, uint32_t len, uint32_t& value)\r\n    {\r\n        char c;\n        value = 0;\n        int bitCount = 0;\n        int index = 0;\n        do\n        {\n            c = buf[index];\n            uint32_t x = (c & 0x7F);\n            x <<= bitCount;\n            value += x;\n            bitCount += 7;\n            ++index;\n        } while (c & 0x80);\r\n    }\r\n\r\n    //һ1~10ֽڵֵԭ4ֽڵֵ\r\n    void read7BitEncoded(const char* buf, uint32_t len, uint64_t& value)\r\n    {\r\n        char c;\n        value = 0;\n        int bitCount = 0;\n        int index = 0;\n        do\n        {\n            c = buf[index];\n            uint64_t x = (c & 0x7F);\n            x <<= bitCount;\n            value += x;\n            bitCount += 7;\n            ++index;\n        } while (c & 0x80);\r\n    }\r\n\r\n    BinaryStreamReader::BinaryStreamReader(const char* ptr_, size_t len_)\r\n        : ptr(ptr_), len(len_), cur(ptr_)\r\n    {\r\n        cur += BINARY_PACKLEN_LEN_2 + CHECKSUM_LEN;\r\n    }\r\n    bool BinaryStreamReader::IsEmpty() const\r\n    {\r\n        return len <= BINARY_PACKLEN_LEN_2;\r\n    }\r\n    size_t BinaryStreamReader::GetSize() const\r\n    {\r\n        return len;\r\n    }\r\n    bool BinaryStreamReader::ReadCString(char* str, size_t strlen, /* out */ size_t& outlen)\r\n    {\r\n        size_t fieldlen;\r\n        size_t headlen;\r\n        if (!ReadLengthWithoutOffset(headlen, fieldlen)) {\r\n            return false;\r\n        }\r\n\r\n        // user buffer is not enough\r\n        if (fieldlen > strlen) {\r\n            return false;\r\n        }\r\n\r\n        // ƫƵݵλ\r\n        //cur += BINARY_PACKLEN_LEN_2;\t\r\n        cur += headlen;\r\n        if (cur + fieldlen > ptr + len)\r\n        {\r\n            outlen = 0;\r\n            return false;\r\n        }\r\n        memcpy(str, cur, fieldlen);\r\n        outlen = fieldlen;\r\n        cur += outlen;\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadString(string* str, size_t maxlen, size_t& outlen)\r\n    {\r\n        size_t headlen;\r\n        size_t fieldlen;\r\n        if (!ReadLengthWithoutOffset(headlen, fieldlen)) {\r\n            return false;\r\n        }\r\n\r\n        // user buffer is not enough\r\n        if (maxlen != 0 && fieldlen > maxlen) {\r\n            return false;\r\n        }\r\n\r\n        // ƫƵݵλ\r\n        //cur += BINARY_PACKLEN_LEN_2;\t\r\n        cur += headlen;\r\n        if (cur + fieldlen > ptr + len)\r\n        {\r\n            outlen = 0;\r\n            return false;\r\n        }\r\n        str->assign(cur, fieldlen);\r\n        outlen = fieldlen;\r\n        cur += outlen;\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadCCString(const char** str, size_t maxlen, size_t& outlen)\r\n    {\r\n        size_t headlen;\r\n        size_t fieldlen;\r\n        if (!ReadLengthWithoutOffset(headlen, fieldlen)) {\r\n            return false;\r\n        }\r\n        // user buffer is not enough\r\n        if (maxlen != 0 && fieldlen > maxlen) {\r\n            return false;\r\n        }\r\n\r\n        // ƫƵݵλ\r\n        //cur += BINARY_PACKLEN_LEN_2;\t\r\n        cur += headlen;\r\n\r\n        //memcpy(str, cur, fieldlen);\r\n        if (cur + fieldlen > ptr + len)\r\n        {\r\n            outlen = 0;\r\n            return false;\r\n        }\r\n        *str = cur;\r\n        outlen = fieldlen;\r\n        cur += outlen;\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadInt32(int32_t& i)\r\n    {\r\n        const int VALUE_SIZE = sizeof(int32_t);\r\n\r\n        if (cur + VALUE_SIZE > ptr + len)\r\n            return false;\r\n\r\n        memcpy(&i, cur, VALUE_SIZE);\r\n        i = ntohl(i);\r\n\r\n        cur += VALUE_SIZE;\r\n\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadInt64(int64_t& i)\r\n    {\r\n        char int64str[128];\r\n        size_t length;\r\n        if (!ReadCString(int64str, 128, length))\r\n            return false;\r\n\r\n        i = atoll(int64str);\r\n\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadShort(short& i)\r\n    {\r\n        const int VALUE_SIZE = sizeof(short);\r\n\r\n        if (cur + VALUE_SIZE > ptr + len) {\r\n            return false;\r\n        }\r\n\r\n        memcpy(&i, cur, VALUE_SIZE);\r\n        i = ntohs(i);\r\n\r\n        cur += VALUE_SIZE;\r\n\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadChar(char& c)\r\n    {\r\n        const int VALUE_SIZE = sizeof(char);\r\n\r\n        if (cur + VALUE_SIZE > ptr + len) {\r\n            return false;\r\n        }\r\n\r\n        memcpy(&c, cur, VALUE_SIZE);\r\n        cur += VALUE_SIZE;\r\n\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadLength(size_t& outlen)\r\n    {\r\n        size_t headlen;\r\n        if (!ReadLengthWithoutOffset(headlen, outlen)) {\r\n            return false;\r\n        }\r\n\r\n        //cur += BINARY_PACKLEN_LEN_2;\r\n        cur += headlen;\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::ReadLengthWithoutOffset(size_t& headlen, size_t& outlen)\r\n    {\r\n        headlen = 0;\r\n        const char* temp = cur;\r\n        char buf[5];\r\n        for (size_t i = 0; i < sizeof(buf); i++)\r\n        {\r\n            memcpy(buf + i, temp, sizeof(char));\r\n            temp++;\r\n            headlen++;\r\n\r\n            //if ((buf[i] >> 7 | 0x0) == 0x0)\r\n            if ((buf[i] & 0x80) == 0x00)\r\n                break;\r\n        }\r\n        if (cur + headlen > ptr + len)\r\n            return false;\r\n\r\n        unsigned int value;\r\n        read7BitEncoded(buf, headlen, value);\r\n        outlen = value;\r\n\r\n        /*if ( cur + BINARY_PACKLEN_LEN_2 > ptr + len ) {\r\n        return false;\r\n        }\r\n\r\n        unsigned int tmp;\r\n        memcpy(&tmp, cur, sizeof(tmp));\r\n        outlen = ntohl(tmp);*/\r\n        return true;\r\n    }\r\n    bool BinaryStreamReader::IsEnd() const\r\n    {\r\n        assert(cur <= ptr + len);\r\n        return cur == ptr + len;\r\n    }\r\n    const char* BinaryStreamReader::GetData() const\r\n    {\r\n        return ptr;\r\n    }\r\n    size_t BinaryStreamReader::ReadAll(char* szBuffer, size_t iLen) const\r\n    {\r\n        size_t iRealLen = min(iLen, len);\r\n        memcpy(szBuffer, ptr, iRealLen);\r\n        return iRealLen;\r\n    }\r\n\r\n    //=================class BinaryStreamWriter implementation============//\r\n    BinaryStreamWriter::BinaryStreamWriter(string* data) :\r\n        m_data(data)\r\n    {\r\n        m_data->clear();\r\n        char str[BINARY_PACKLEN_LEN_2 + CHECKSUM_LEN];\r\n        m_data->append(str, sizeof(str));\r\n    }\r\n    bool BinaryStreamWriter::WriteCString(const char* str, size_t len)\r\n    {\r\n        std::string buf;\r\n        write7BitEncoded(len, buf);\r\n\r\n        m_data->append(buf);\r\n\r\n        m_data->append(str, len);\r\n\r\n        //unsigned int ulen = htonl(len);\r\n        //m_data->append((char*)&ulen,sizeof(ulen));\r\n        //m_data->append(str,len);\r\n        return true;\r\n    }\r\n    bool BinaryStreamWriter::WriteString(const string& str)\r\n    {\r\n        return WriteCString(str.c_str(), str.length());\r\n    }\r\n    const char* BinaryStreamWriter::GetData() const\r\n    {\r\n        return m_data->data();\r\n    }\r\n    size_t BinaryStreamWriter::GetSize() const\r\n    {\r\n        return m_data->length();\r\n    }\r\n    bool BinaryStreamWriter::WriteInt32(int32_t i, bool isNULL)\r\n    {\r\n        int32_t i2 = 999999999;\r\n        if (isNULL == false)\r\n            i2 = htonl(i);\r\n        m_data->append((char*)& i2, sizeof(i2));\r\n        return true;\r\n    }\r\n    bool BinaryStreamWriter::WriteInt64(int64_t value, bool isNULL)\r\n    {\r\n        char int64str[128];\r\n        if (isNULL == false)\r\n        {\r\n#ifndef _WIN32\r\n            sprintf(int64str, \"%ld\", value);\r\n#else\r\n            sprintf(int64str, \"%lld\", value);\r\n#endif\r\n            WriteCString(int64str, strlen(int64str));\r\n        }\r\n        else\r\n            WriteCString(int64str, 0);\r\n        return true;\r\n    }\r\n    bool BinaryStreamWriter::WriteShort(short i, bool isNULL)\r\n    {\r\n        short i2 = 0;\r\n        if (isNULL == false)\r\n            i2 = htons(i);\r\n        m_data->append((char*)& i2, sizeof(i2));\r\n        return true;\r\n    }\r\n    bool BinaryStreamWriter::WriteChar(char c, bool isNULL)\r\n    {\r\n        char c2 = 0;\r\n        if (isNULL == false)\r\n            c2 = c;\r\n        (*m_data) += c2;\r\n        return true;\r\n    }\r\n    bool BinaryStreamWriter::WriteDouble(double value, bool isNULL)\r\n    {\r\n        char   doublestr[128];\r\n        if (isNULL == false)\r\n        {\r\n            sprintf(doublestr, \"%f\", value);\r\n            WriteCString(doublestr, strlen(doublestr));\r\n        }\r\n        else\r\n            WriteCString(doublestr, 0);\r\n        return true;\r\n    }\r\n    void BinaryStreamWriter::Flush()\r\n    {\r\n        char* ptr = &(*m_data)[0];\r\n        unsigned int ulen = htonl(m_data->length());\r\n        memcpy(ptr, &ulen, sizeof(ulen));\r\n    }\r\n    void BinaryStreamWriter::Clear()\r\n    {\r\n        m_data->clear();\r\n        char str[BINARY_PACKLEN_LEN_2 + CHECKSUM_LEN];\r\n        m_data->append(str, sizeof(str));\r\n    }\r\n}// end namespace\r\n\r\n"
  },
  {
    "path": "Chapter06/code/ProtocolStream.h",
    "content": "/**\r\n *  һǿЭ, protocolstream.h\r\n *  zhangyl 2017.05.27\r\n */\r\n\r\n#ifndef __PROTOCOL_STREAM_H__\r\n#define __PROTOCOL_STREAM_H__\r\n\r\n#include <stdlib.h>\r\n#include <sys/types.h>\r\n#include <string>\r\n#include <sstream>\r\n#include <stdint.h>\r\n\r\n //ЭĴ࣬ڲķ֮ͨѶͳһЩ\r\nnamespace net\r\n{\r\n    enum\r\n    {\r\n        TEXT_PACKLEN_LEN = 4,\r\n        TEXT_PACKAGE_MAXLEN = 0xffff,\r\n        BINARY_PACKLEN_LEN = 2,\r\n        BINARY_PACKAGE_MAXLEN = 0xffff,\r\n\r\n        TEXT_PACKLEN_LEN_2 = 6,\r\n        TEXT_PACKAGE_MAXLEN_2 = 0xffffff,\r\n\r\n        BINARY_PACKLEN_LEN_2 = 4,               //4ֽͷ\r\n        BINARY_PACKAGE_MAXLEN_2 = 0x10000000,   //󳤶256M,㹻\r\n\r\n        CHECKSUM_LEN = 2,\r\n    };\r\n\r\n    //У\r\n    unsigned short checksum(const unsigned short* buffer, int size);\r\n    //һ4ֽڵֵѹ1~5ֽ\r\n    void write7BitEncoded(uint32_t value, std::string& buf);\r\n    //һ8ֽڵֵ1~10ֽ\r\n    void write7BitEncoded(uint64_t value, std::string& buf);\r\n\r\n    //һ1~5ֽڵֵַԭ4ֽڵֵ\r\n    void read7BitEncoded(const char* buf, uint32_t len, uint32_t& value);\r\n    //һ1~10ֽڵֵԭ4ֽڵֵ\r\n    void read7BitEncoded(const char* buf, uint32_t len, uint64_t& value);\r\n\r\n    class BinaryStreamReader final\r\n    {\r\n    public:\r\n        BinaryStreamReader(const char* ptr, size_t len);\r\n        ~BinaryStreamReader() = default;\r\n\r\n        virtual const char* GetData() const;\r\n        virtual size_t GetSize() const;\r\n        bool IsEmpty() const;\r\n        bool ReadString(std::string* str, size_t maxlen, size_t& outlen);\r\n        bool ReadCString(char* str, size_t strlen, size_t& len);\r\n        bool ReadCCString(const char** str, size_t maxlen, size_t& outlen);\r\n        bool ReadInt32(int32_t& i);\r\n        bool ReadInt64(int64_t& i);\r\n        bool ReadShort(short& i);\r\n        bool ReadChar(char& c);\r\n        size_t ReadAll(char* szBuffer, size_t iLen) const;\r\n        bool IsEnd() const;\r\n        const char* GetCurrent() const { return cur; }\r\n\r\n    public:\r\n        bool ReadLength(size_t& len);\r\n        bool ReadLengthWithoutOffset(size_t& headlen, size_t& outlen);\r\n\r\n    private:\r\n        BinaryStreamReader(const BinaryStreamReader&) = delete;\r\n        BinaryStreamReader& operator=(const BinaryStreamReader&) = delete;\r\n\r\n    private:\r\n        const char* const ptr;\r\n        const size_t      len;\r\n        const char* cur;\r\n    };\r\n\r\n    class BinaryStreamWriter final\r\n    {\r\n    public:\r\n        BinaryStreamWriter(std::string* data);\r\n        ~BinaryStreamWriter() = default;\r\n\r\n        virtual const char* GetData() const;\r\n        virtual size_t GetSize() const;\r\n        bool WriteCString(const char* str, size_t len);\r\n        bool WriteString(const std::string& str);\r\n        bool WriteDouble(double value, bool isNULL = false);\r\n        bool WriteInt64(int64_t value, bool isNULL = false);\r\n        bool WriteInt32(int32_t i, bool isNULL = false);\r\n        bool WriteShort(short i, bool isNULL = false);\r\n        bool WriteChar(char c, bool isNULL = false);\r\n        size_t GetCurrentPos() const { return m_data->length(); }\r\n        void Flush();\r\n        void Clear();\r\n\r\n    private:\r\n        BinaryStreamWriter(const BinaryStreamWriter&) = delete;\r\n        BinaryStreamWriter& operator=(const BinaryStreamWriter&) = delete;\r\n\r\n    private:\r\n        std::string* m_data;\r\n    };\r\n\r\n}// end namespace\r\n\r\n#endif //!__PROTOCOL_STREAM_H__"
  },
  {
    "path": "Chapter06/code/RecvMail/Base64Util.cpp",
    "content": "#include \"Base64Util.h\"\r\n/////////////////////////////////////////////////////////////////////////////////////////////////\r\n//\r\nstatic const char DECODE_TABLE[] =\r\n{\r\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r\n\t62,\t\t\t\t\t\t\t\t\t\t\t\t\t// '+'\r\n\t0, 0, 0,\r\n\t63,\t\t\t\t\t\t\t\t\t\t\t\t\t// '/'\r\n\t52, 53, 54, 55, 56, 57, 58, 59, 60, 61,\t\t\t\t// '0'-'9'\r\n\t0, 0, 0, 0, 0, 0, 0,\r\n\t0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,\r\n\t13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'\r\n\t0, 0, 0, 0, 0, 0,\r\n\t26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,\r\n\t39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'\r\n};\r\n\r\nstatic const char ENCODE_TABLE[] = { \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\" };\r\n\r\nint Base64Util::encode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest)\r\n{\r\n\tchar c1, c2, c3;\r\n\tint i = 0, lenDest(0), lDiv(lenSource / 3), lMod(lenSource % 3);\r\n\tfor (; i < lDiv; ++i, lenDest += 4)\r\n\t{\r\n\t\tif (lenDest + 4 >= maxDest)\r\n\t\t\treturn 0;\r\n\t\tc1 = *pSource++;\r\n\t\tc2 = *pSource++;\r\n\t\tc3 = *pSource++;\r\n\t\t*pDest++ = ENCODE_TABLE[c1 >> 2];\r\n\t\t*pDest++ = ENCODE_TABLE[((c1 << 4) | (c2 >> 4)) & 0X3F];\r\n\t\t*pDest++ = ENCODE_TABLE[((c2 << 2) | (c3 >> 6)) & 0X3F];\r\n\t\t*pDest++ = ENCODE_TABLE[c3 & 0X3F];\r\n\t}\r\n\tif (lMod == 1)\r\n\t{\r\n\t\tif (lenDest + 4 >= maxDest) return(0);\r\n\t\tc1 = *pSource++;\r\n\t\t*pDest++ = ENCODE_TABLE[(c1 & 0XFC) >> 2];\r\n\t\t*pDest++ = ENCODE_TABLE[((c1 & 0X03) << 4)];\r\n\t\t*pDest++ = chMask;\r\n\t\t*pDest++ = chMask;\r\n\t\tlenDest += 4;\r\n\t}\r\n\telse if (lMod == 2)\r\n\t{\r\n\t\tif (lenDest + 4 >= maxDest) return(0);\r\n\t\tc1 = *pSource++;\r\n\t\tc2 = *pSource++;\r\n\t\t*pDest++ = ENCODE_TABLE[(c1 & 0XFC) >> 2];\r\n\t\t*pDest++ = ENCODE_TABLE[((c1 & 0X03) << 4) | ((c2 & 0XF0) >> 4)];\r\n\t\t*pDest++ = ENCODE_TABLE[((c2 & 0X0F) << 2)];\r\n\t\t*pDest++ = chMask;\r\n\t\tlenDest += 4;\r\n\t}\r\n\r\n\t*pDest = 0;\r\n\r\n\treturn lenDest;\r\n}\r\n\r\nint Base64Util::decode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest)\r\n{\r\n\tint lenDest = 0, nValue = 0, i = 0;\r\n\tfor (; i < lenSource; i += 4)\r\n\t{\r\n\t\tnValue = DECODE_TABLE[(int)(*pSource)] << 18;\r\n\t\tpSource++;\r\n\t\tnValue += DECODE_TABLE[(int)*pSource] << 12;\r\n\t\tpSource++;\r\n\t\tif (++lenDest >= maxDest)\r\n\t\t\tbreak;\r\n\r\n\t\t*pDest++ = char((nValue & 0X00FF0000) >> 16);\r\n\r\n\t\tif (*pSource != chMask)\r\n\t\t{\r\n\t\t\tnValue += DECODE_TABLE[(int)*pSource] << 6;\r\n\t\t\tpSource++;\r\n\t\t\tif (++lenDest >= maxDest)\r\n\t\t\t\tbreak;\r\n\t\t\t*pDest++ = (nValue & 0X0000FF00) >> 8;\r\n\r\n\t\t\tif (*pSource != chMask)\r\n\t\t\t{\r\n\t\t\t\tnValue += DECODE_TABLE[(int)*pSource];\r\n\t\t\t\tpSource++;\r\n\t\t\t\tif (++lenDest >= maxDest)\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t*pDest++ = nValue & 0X000000FF;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t*pDest = 0;\r\n\r\n\treturn lenDest;\r\n}\r\n\r\nvoid Base64Util::decode(const char* pData, int nDataByte, std::string& strDecode)\r\n{\r\n\tstrDecode.clear();\r\n\tint nValue = 0, i = 0, OutByte = 0;\r\n\twhile (i < nDataByte)\r\n\t{\r\n\t\tif (*pData != '\\r' && *pData != '\\n')\r\n\t\t{\r\n\t\t\tnValue = DECODE_TABLE[*pData++] << 18;\r\n\t\t\tnValue += DECODE_TABLE[*pData++] << 12;\r\n\t\t\tstrDecode += (char)((nValue & 0x00FF0000) >> 16);\r\n\t\t\tOutByte++;\r\n\t\t\tif (*pData != '=')\r\n\t\t\t{\r\n\t\t\t\tnValue += DECODE_TABLE[*pData++] << 6;\r\n\t\t\t\tstrDecode += (char)((nValue & 0x0000FF00) >> 8);\r\n\t\t\t\tOutByte++;\r\n\t\t\t\tif (*pData != '=')\r\n\t\t\t\t{\r\n\t\t\t\t\tnValue += DECODE_TABLE[*pData++];\r\n\t\t\t\t\tstrDecode += (char)(nValue & 0x000000FF);\r\n\t\t\t\t\tOutByte++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ti += 4;\r\n\t\t}\r\n\t\telse\t// س,\r\n\t\t{\r\n\t\t\tpData++;\r\n\t\t\ti++;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nbool Base64Util::check(char* lpString)\r\n{\r\n\tfor (; *lpString; ++lpString)\r\n\t{\r\n\t\tswitch (*lpString)\r\n\t\t{\r\n\t\tcase\t'+': *lpString = '@';\tbreak;\r\n\t\tcase\t'@': *lpString = '+';\tbreak;\r\n\t\tcase\t'=': *lpString = '$';\tbreak;\r\n\t\tcase\t'$': *lpString = '=';\tbreak;\r\n\t\tcase\t'/': *lpString = '#';\tbreak;\r\n\t\tcase\t'#': *lpString = '/';\tbreak;\r\n\t\tdefault:\r\n\t\t\tif (*lpString >= 'A' && *lpString <= 'Z')\r\n\t\t\t\t*lpString = *lpString - 'A' + 'a';\r\n\t\t\telse if (*lpString >= 'a' && *lpString <= 'z')\r\n\t\t\t\t*lpString = *lpString - 'a' + 'A';\r\n\t\t\telse if (*lpString >= '0' && *lpString <= '4')\r\n\t\t\t\t*lpString = *lpString - '0' + '5';\r\n\t\t\telse if (*lpString >= '5' && *lpString <= '9')\r\n\t\t\t\t*lpString = *lpString - '5' + '0';\r\n\t\t\telse\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\treturn true;\r\n}\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/Base64Util.h",
    "content": "#pragma once\r\n\r\n#include <string>\r\n\r\nclass Base64Util final\r\n{\r\nprivate:\r\n    Base64Util() = delete;\r\n    ~Base64Util() = delete;\r\n    Base64Util(const Base64Util& rhs) = delete;\r\n    Base64Util& operator=(const Base64Util& rhs) = delete;\r\n\r\npublic:\r\n    static int encode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest);\r\n    static int decode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest);\r\n    static void decode(const char* pData, int nDataByte, std::string& strDecode);\r\n\r\n    static bool check(char* lpString);\r\n};\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/EncodeUtil.cpp",
    "content": "/**\r\n * Windowsƽ̨ñʽ໥ת, EncodingUtil.cpp\r\n * zhangyl 2017.03.29\r\n **/\r\n//#include \"stdafx.h\"\r\n#include <locale.h>\r\n#include <Windows.h>\r\n //#include <iconv.h>          //linux only\r\n#include \"EncodeUtil.h\"\r\n\r\nwchar_t* EncodeUtil::AnsiToUnicode(const char* lpszStr)\r\n{\r\n\twchar_t* lpUnicode;\r\n\tint nLen;\r\n\r\n\tif (NULL == lpszStr)\r\n\t\treturn NULL;\r\n\r\n\tnLen = ::MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, NULL, 0);\r\n\tif (0 == nLen)\r\n\t\treturn NULL;\r\n\r\n\tlpUnicode = new wchar_t[nLen + 1];\r\n\tif (NULL == lpUnicode)\r\n\t\treturn NULL;\r\n\r\n\tmemset(lpUnicode, 0, sizeof(wchar_t) * (nLen + 1));\r\n\tnLen = ::MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, lpUnicode, nLen);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\treturn lpUnicode;\r\n}\r\n\r\nchar* EncodeUtil::UnicodeToAnsi(const wchar_t* lpszStr)\r\n{\r\n\tchar* lpAnsi;\r\n\tint nLen;\r\n\r\n\tif (NULL == lpszStr)\r\n\t\treturn NULL;\r\n\r\n\tnLen = ::WideCharToMultiByte(CP_ACP, 0, lpszStr, -1, NULL, 0, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t\treturn NULL;\r\n\r\n\tlpAnsi = new char[nLen + 1];\r\n\tif (NULL == lpAnsi)\r\n\t\treturn NULL;\r\n\r\n\tmemset(lpAnsi, 0, nLen + 1);\r\n\tnLen = ::WideCharToMultiByte(CP_ACP, 0, lpszStr, -1, lpAnsi, nLen, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpAnsi;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\treturn lpAnsi;\r\n}\r\n\r\nchar* EncodeUtil::AnsiToUtf8(const char* lpszStr)\r\n{\r\n\twchar_t* lpUnicode;\r\n\tchar* lpUtf8;\r\n\tint nLen;\r\n\r\n\tif (NULL == lpszStr)\r\n\t\treturn NULL;\r\n\r\n\tnLen = ::MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t\treturn NULL;\r\n\r\n\tlpUnicode = new wchar_t[nLen + 1];\r\n\tif (NULL == lpUnicode)\r\n\t\treturn NULL;\r\n\r\n\tmemset(lpUnicode, 0, sizeof(wchar_t) * (nLen + 1));\r\n\tnLen = ::MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, lpUnicode, nLen);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tnLen = ::WideCharToMultiByte(CP_UTF8, 0, lpUnicode, -1, NULL, 0, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tlpUtf8 = new char[nLen + 1];\r\n\tif (NULL == lpUtf8)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tmemset(lpUtf8, 0, nLen + 1);\r\n\tnLen = ::WideCharToMultiByte(CP_UTF8, 0, lpUnicode, -1, lpUtf8, nLen, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\tdelete[]lpUtf8;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tdelete[]lpUnicode;\r\n\r\n\treturn lpUtf8;\r\n}\r\n\r\nchar* EncodeUtil::Utf8ToAnsi(const char* lpszStr)\r\n{\r\n\twchar_t* lpUnicode;\r\n\tchar* lpAnsi;\r\n\tint nLen;\r\n\r\n\tif (NULL == lpszStr)\r\n\t\treturn NULL;\r\n\r\n\tnLen = ::MultiByteToWideChar(CP_UTF8, 0, lpszStr, -1, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t\treturn NULL;\r\n\r\n\tlpUnicode = new wchar_t[nLen + 1];\r\n\tif (NULL == lpUnicode)\r\n\t\treturn NULL;\r\n\r\n\tmemset(lpUnicode, 0, sizeof(wchar_t) * (nLen + 1));\r\n\tnLen = ::MultiByteToWideChar(CP_UTF8, 0, lpszStr, -1, lpUnicode, nLen);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tnLen = ::WideCharToMultiByte(CP_ACP, 0, lpUnicode, -1, NULL, 0, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tlpAnsi = new char[nLen + 1];\r\n\tif (NULL == lpAnsi)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tmemset(lpAnsi, 0, nLen + 1);\r\n\tnLen = ::WideCharToMultiByte(CP_ACP, 0, lpUnicode, -1, lpAnsi, nLen, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\tdelete[]lpAnsi;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tdelete[]lpUnicode;\r\n\r\n\treturn lpAnsi;\r\n}\r\n\r\nchar* EncodeUtil::UnicodeToUtf8(const wchar_t* lpszStr)\r\n{\r\n\tchar* lpUtf8;\r\n\tint nLen;\r\n\r\n\tif (NULL == lpszStr)\r\n\t\treturn NULL;\r\n\r\n\tnLen = ::WideCharToMultiByte(CP_UTF8, 0, lpszStr, -1, NULL, 0, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t\treturn NULL;\r\n\r\n\tlpUtf8 = new char[nLen + 1];\r\n\tif (NULL == lpUtf8)\r\n\t\treturn NULL;\r\n\r\n\tmemset(lpUtf8, 0, nLen + 1);\r\n\tnLen = ::WideCharToMultiByte(CP_UTF8, 0, lpszStr, -1, lpUtf8, nLen, NULL, NULL);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUtf8;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\treturn lpUtf8;\r\n}\r\n\r\nwchar_t* EncodeUtil::Utf8ToUnicode(const char* lpszStr)\r\n{\r\n\twchar_t* lpUnicode;\r\n\tint nLen;\r\n\r\n\tif (NULL == lpszStr)\r\n\t\treturn NULL;\r\n\r\n\tnLen = ::MultiByteToWideChar(CP_UTF8, 0, lpszStr, -1, NULL, 0);\r\n\tif (0 == nLen)\r\n\t\treturn NULL;\r\n\r\n\tlpUnicode = new wchar_t[nLen + 1];\r\n\tif (NULL == lpUnicode)\r\n\t\treturn NULL;\r\n\r\n\tmemset(lpUnicode, 0, sizeof(wchar_t) * (nLen + 1));\r\n\tnLen = ::MultiByteToWideChar(CP_UTF8, 0, lpszStr, -1, lpUnicode, nLen);\r\n\tif (0 == nLen)\r\n\t{\r\n\t\tdelete[]lpUnicode;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\treturn lpUnicode;\r\n}\r\n\r\nbool EncodeUtil::AnsiToUnicode(const char* lpszAnsi, wchar_t* lpszUnicode, int nLen)\r\n{\r\n\tint nRet = ::MultiByteToWideChar(CP_ACP, 0, lpszAnsi, -1, lpszUnicode, nLen);\r\n\treturn (0 == nRet) ? FALSE : TRUE;\r\n}\r\n\r\nbool EncodeUtil::UnicodeToAnsi(const wchar_t* lpszUnicode, char* lpszAnsi, int nLen)\r\n{\r\n\tint nRet = ::WideCharToMultiByte(CP_ACP, 0, lpszUnicode, -1, lpszAnsi, nLen, NULL, NULL);\r\n\treturn (0 == nRet) ? FALSE : TRUE;\r\n}\r\n\r\nbool EncodeUtil::AnsiToUtf8(const char* lpszAnsi, char* lpszUtf8, int nLen)\r\n{\r\n\twchar_t* lpszUnicode = EncodeUtil::AnsiToUnicode(lpszAnsi);\r\n\tif (NULL == lpszUnicode)\r\n\t\treturn FALSE;\r\n\r\n\tint nRet = EncodeUtil::UnicodeToUtf8(lpszUnicode, lpszUtf8, nLen);\r\n\r\n\tdelete[]lpszUnicode;\r\n\r\n\treturn (0 == nRet) ? FALSE : TRUE;\r\n}\r\n\r\nbool EncodeUtil::Utf8ToAnsi(const char* lpszUtf8, char* lpszAnsi, int nLen)\r\n{\r\n\twchar_t* lpszUnicode = EncodeUtil::Utf8ToUnicode(lpszUtf8);\r\n\tif (NULL == lpszUnicode)\r\n\t\treturn FALSE;\r\n\r\n\tint nRet = UnicodeToAnsi(lpszUnicode, lpszAnsi, nLen);\r\n\r\n\tdelete[]lpszUnicode;\r\n\r\n\treturn (0 == nRet) ? FALSE : TRUE;\r\n}\r\n\r\nbool EncodeUtil::UnicodeToUtf8(const wchar_t* lpszUnicode, char* lpszUtf8, int nLen)\r\n{\r\n\tint nRet = ::WideCharToMultiByte(CP_UTF8, 0, lpszUnicode, -1, lpszUtf8, nLen, NULL, NULL);\r\n\treturn (0 == nRet) ? FALSE : TRUE;\r\n}\r\n\r\nbool EncodeUtil::Utf8ToUnicode(const char* lpszUtf8, wchar_t* lpszUnicode, int nLen)\r\n{\r\n\tint nRet = ::MultiByteToWideChar(CP_UTF8, 0, lpszUtf8, -1, lpszUnicode, nLen);\r\n\treturn (0 == nRet) ? FALSE : TRUE;\r\n}\r\n\r\nstd::wstring EncodeUtil::AnsiToUnicode(const std::string& strAnsi)\r\n{\r\n\tstd::wstring strUnicode;\r\n\r\n\twchar_t* lpszUnicode = EncodeUtil::AnsiToUnicode(strAnsi.c_str());\r\n\tif (lpszUnicode != NULL)\r\n\t{\r\n\t\tstrUnicode = lpszUnicode;\r\n\t\tdelete[]lpszUnicode;\r\n\t}\r\n\r\n\treturn strUnicode;\r\n}\r\nstd::string EncodeUtil::UnicodeToAnsi(const std::wstring& strUnicode)\r\n{\r\n\tstd::string strAnsi;\r\n\r\n\tchar* lpszAnsi = UnicodeToAnsi(strUnicode.c_str());\r\n\tif (lpszAnsi != NULL)\r\n\t{\r\n\t\tstrAnsi = lpszAnsi;\r\n\t\tdelete[]lpszAnsi;\r\n\t}\r\n\r\n\treturn strAnsi;\r\n}\r\n\r\nstd::string EncodeUtil::AnsiToUtf8(const std::string& strAnsi)\r\n{\r\n\tstd::string strUtf8;\r\n\r\n\tchar* lpszUtf8 = AnsiToUtf8(strAnsi.c_str());\r\n\tif (lpszUtf8 != NULL)\r\n\t{\r\n\t\tstrUtf8 = lpszUtf8;\r\n\t\tdelete[]lpszUtf8;\r\n\t}\r\n\r\n\treturn strUtf8;\r\n}\r\n\r\nstd::string EncodeUtil::Utf8ToAnsi(const std::string& strUtf8)\r\n{\r\n\tstd::string strAnsi;\r\n\r\n\tchar* lpszAnsi = Utf8ToAnsi(strUtf8.c_str());\r\n\tif (lpszAnsi != NULL)\r\n\t{\r\n\t\tstrAnsi = lpszAnsi;\r\n\t\tdelete[]lpszAnsi;\r\n\t}\r\n\r\n\treturn strAnsi;\r\n}\r\n\r\nstd::string EncodeUtil::UnicodeToUtf8(const std::wstring& strUnicode)\r\n{\r\n\tstd::string strUtf8;\r\n\r\n\tchar* lpszUtf8 = EncodeUtil::UnicodeToUtf8(strUnicode.c_str());\r\n\tif (lpszUtf8 != NULL)\r\n\t{\r\n\t\tstrUtf8 = lpszUtf8;\r\n\t\tdelete[]lpszUtf8;\r\n\t}\r\n\r\n\treturn strUtf8;\r\n}\r\n\r\nstd::wstring EncodeUtil::Utf8ToUnicode(const std::string& strUtf8)\r\n{\r\n\tstd::wstring strUnicode;\r\n\r\n\twchar_t* lpszUnicode = EncodeUtil::Utf8ToUnicode(strUtf8.c_str());\r\n\tif (lpszUnicode != NULL)\r\n\t{\r\n\t\tstrUnicode = lpszUnicode;\r\n\t\tdelete[]lpszUnicode;\r\n\t}\r\n\r\n\treturn strUnicode;\r\n}\r\n\r\nvoid EncodeUtil::DecodeQuotedPrintable(const char* pSrc, char* pDest)\r\n{\r\n\tlong LEN = strlen(pSrc);\r\n\tint step = 0;\r\n\r\n\tchar temp[3];\r\n\tlong before = 0;\r\n\tlong offset = 0;\r\n\tdo\r\n\t{\r\n\t\tif (pSrc[offset] != '=')\r\n\t\t{\r\n\t\t\toffset++;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tmemcpy(pDest + step, pSrc + before, offset - before);\r\n\t\tstep += offset - before;\r\n\t\tbefore = offset + 1;\r\n\r\n\t\toffset++;\r\n\t\tif ((pSrc[offset] == 0x0D) || (pSrc[offset] == 0x0A))\r\n\t\t{\r\n\t\t\tbefore = offset + 1;\r\n\t\t\toffset++;\r\n\r\n\t\t\tif ((pSrc[offset] == 0x0D) || (pSrc[offset] == 0x0A))\r\n\t\t\t{\r\n\t\t\t\tbefore = offset + 1;\r\n\t\t\t\toffset++;\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\ttemp[0] = pSrc[offset];\r\n\t\ttemp[0] = temp[0] < 0x41 ? temp[0] - 0x30 : (temp[0] < 0x61 ? temp[0] - 0x37 : temp[0] - 0x57);\r\n\r\n\t\toffset++;\r\n\t\ttemp[1] = pSrc[offset];\r\n\t\ttemp[1] = temp[1] < 0x41 ? temp[1] - 0x30 : (temp[1] < 0x61 ? temp[1] - 0x37 : temp[1] - 0x57);\r\n\r\n\t\ttemp[2] = (temp[0] << 4) | (temp[1] & 0x0F);\r\n\r\n\t\tmemcpy(pDest + step, temp + 2, 1);\r\n\t\tstep += 1;\r\n\r\n\t\tbefore = offset + 1;\r\n\t\toffset++;\r\n\r\n\t} while (offset < LEN);\r\n\r\n\tif (before < LEN)\r\n\t{\r\n\t\tmemcpy(pDest + step, pSrc + before, LEN - before);\r\n\t\tstep += LEN - before;\r\n\t}\r\n\r\n\r\n\tpDest[step] = 0x00;\r\n}\r\n\r\nbool EncodeUtil::UTF8ToUnicode(const char* pszUTF8, std::wstring& strDest)\r\n{\r\n\tstrDest.clear();\r\n\tlong lLen = ::MultiByteToWideChar(CP_UTF8, 0, pszUTF8, -1, NULL, 0);\r\n\tif (lLen <= 0) \r\n\t\treturn false;\r\n\r\n\twchar_t* pDest = new wchar_t[lLen + 10];\r\n\tmemset(pDest, 0, sizeof(wchar_t) * (lLen + 10));\r\n\tlong lnWide = ::MultiByteToWideChar(CP_UTF8, 0, pszUTF8, -1, pDest, lLen);\r\n\tpDest[lLen - 1] = 0;\r\n\tstrDest.append(pDest);\r\n\r\n\treturn true;\r\n}\r\n\r\nbool EncodeUtil::GB2312ToUnicode(const char* pszGB2312, std::wstring& strDest)\r\n{\r\n\tstrDest.clear();\r\n\tlong lLen = ::MultiByteToWideChar(936, 0, pszGB2312, -1, NULL, 0);\r\n\tif (lLen <= 0) \r\n\t\treturn false;\r\n\r\n\twchar_t* pDest = new wchar_t[lLen + 10];\r\n\tmemset(pDest, 0, sizeof(wchar_t) * (lLen + 10));\r\n\tlong lnWide = ::MultiByteToWideChar(936, 0, pszGB2312, -1, pDest, lLen);\r\n\tpDest[lLen - 1] = 0;\r\n\tstrDest.append(pDest);\r\n\r\n\treturn true;\r\n}\r\n\r\nbool EncodeUtil::UnicodeToGB2312(const wchar_t* pszUnicode, std::string& strDest)\r\n{\r\n\tstrDest.clear();\r\n\r\n\tlong lLen = ::WideCharToMultiByte(936, 0, pszUnicode, -1, NULL, 0, NULL, NULL);\r\n\tif (lLen <= 0) \r\n\t\treturn false;\r\n\r\n\tchar* pDest = new char[lLen + 10];\r\n\tmemset(pDest, 0, sizeof(char) * lLen + 10);\r\n\tlong lCovertLen = ::WideCharToMultiByte(936, 0, pszUnicode, -1, pDest, lLen, NULL, NULL);\r\n\tpDest[lLen - 1] = 0;\r\n\tstrDest.append(pDest);\r\n\r\n\treturn true;\r\n}\r\n\r\nstd::string EncodeUtil::ws2s(const std::wstring& ws)\r\n{\r\n\tsize_t i;\r\n\tstd::string curLocale = setlocale(LC_ALL, NULL);\r\n\tsetlocale(LC_ALL, \"chs\");\r\n\tconst wchar_t* _source = ws.c_str();\r\n\tsize_t _dsize = 2 * ws.size() + 1;\r\n\tchar* _dest = new char[_dsize];\r\n\tmemset(_dest, 0x0, _dsize);\r\n\twcstombs_s(&i, _dest, _dsize, _source, _dsize);\r\n\tstd::string result = _dest;\r\n\tdelete[] _dest;\r\n\tsetlocale(LC_ALL, curLocale.c_str());\r\n\treturn result;\r\n}\r\n\r\nstd::wstring EncodeUtil::s2ws(const std::string& s)\r\n{\r\n\tsize_t i;\r\n\tstd::string curLocale = setlocale(LC_ALL, NULL);\r\n\tsetlocale(LC_ALL, \"chs\");\r\n\tconst char* _source = s.c_str();\r\n\tsize_t _dsize = s.size() + 1;\r\n\twchar_t* _dest = new wchar_t[_dsize];\r\n\twmemset(_dest, 0x0, _dsize);\r\n\tmbstowcs_s(&i, _dest, _dsize, _source, _dsize);\r\n\tstd::wstring result = _dest;\r\n\tdelete[] _dest;\r\n\tsetlocale(LC_ALL, curLocale.c_str());\r\n\treturn result;\r\n}\r\n\r\n\r\n//int EncodeUtil::code_convert(char* from_charset, char* to_charset, char* inbuf, size_t inlen, char* outbuf, size_t& outlen)\r\n//{\r\n//    iconv_t cd;\r\n//    char** pin = &inbuf;\r\n//    char** pout = &outbuf;\r\n//\r\n//    cd = iconv_open(to_charset, from_charset);\r\n//    if (cd == 0)\r\n//        return false;\r\n//\r\n//    memset(outbuf, 0, outlen);\r\n//\r\n//    if (iconv(cd, pin, &inlen, pout, &outlen) == -1)\r\n//        return false;\r\n//\r\n//    iconv_close(cd);\r\n//    return true;\r\n//}\r\n//\r\n//bool EncodeUtil::Utf8ToGbk(char *inbuf, size_t inlen, char *outbuf, size_t outlen)\r\n//{\r\n//    return code_convert(\"utf-8\", \"gbk\", inbuf, inlen, outbuf, outlen);\r\n//}\r\n//\r\n//bool EncodeUtil::GbkToUtf8(char* inbuf, size_t inlen, char* outbuf, size_t outlen)\r\n//{\r\n//    return code_convert(\"gbk\", \"utf-8\", inbuf, inlen, outbuf, outlen);\r\n//}\r\n//\r\n//bool EncodeUtil::Utf8ToGbk2(char* inbuf, size_t inlen, char* outbuf, size_t& outlen)\r\n//{\r\n//    return code_convert(\"gbk\", \"utf-8\", inbuf, inlen, outbuf, outlen);\r\n//}\r\n//\r\n//int EncodeUtil::GbkToUtf8(char* utfstr, const char* srcstr, int maxutfstrlen)\r\n//{\r\n//    if (NULL == srcstr)\r\n//        return -1;\r\n//\r\n//    //ȽgbkתΪunicode\r\n//    if (NULL == setlocale(LC_ALL, \"zh_CN.gbk\"))//תΪunicodeǰ,ǰΪgbk\r\n//        return -1;\r\n//\r\n//    int unicodelen = mbstowcs(NULL, srcstr, 0);//תĳ\r\n//    if (unicodelen <= 0)\r\n//        return -1;\r\n//\r\n//    wchar_t* unicodestr = (wchar_t *)calloc(sizeof(wchar_t), unicodelen + 1);\r\n//    mbstowcs(unicodestr, srcstr, strlen(srcstr));//gbkתΪunicode\r\n//\r\n//    //unicodeתΪutf8\r\n//    if (NULL == setlocale(LC_ALL, \"zh_CN.utf8\"))//unicodeת,ǰΪutf8\r\n//        return -1;\r\n//\r\n//    int utflen = wcstombs(NULL, unicodestr, 0);//תĳ\r\n//    if (utflen <= 0)\r\n//        return -1;\r\n//    else if (utflen >= maxutfstrlen)//жϿռǷ㹻\r\n//        return -1;\r\n//\r\n//    wcstombs(utfstr, unicodestr, utflen);\r\n//    utfstr[utflen] = 0;//ӽ\r\n//    free(unicodestr);\r\n//\r\n//    return utflen;\r\n//}"
  },
  {
    "path": "Chapter06/code/RecvMail/EncodeUtil.h",
    "content": "/** \r\n * Windowsƽ̨ñʽ໥ת, EncodingUtil.h\r\n * zhangyl 2017.03.29\r\n **/\r\n#ifndef __ENCODE_H__\r\n#define __ENCODE_H__\r\n\r\n//#ifdef ENCODE_EXPORTS\r\n//#define ENCODE_API __declspec(dllexport)\r\n//#else\r\n//#define ENCODE_API __declspec(dllimport)\r\n//#endif\r\n\r\n#include <string>\r\n\r\n#define ENCODE_API\r\n\r\nclass ENCODE_API EncodeUtil\r\n{\r\npublic:\r\n    //===BEGIN: ע⣺6,Ҫⲿͷŷصַָ룬ڴй¶\r\n    static wchar_t* AnsiToUnicode(const char* lpszStr);\r\n    static char* UnicodeToAnsi(const wchar_t* lpszStr);\r\n    static char* AnsiToUtf8(const char* lpszStr);\r\n    static char* Utf8ToAnsi(const char* lpszStr);\r\n    static char* UnicodeToUtf8(const wchar_t* lpszStr);\r\n    static wchar_t* Utf8ToUnicode(const char* lpszStr);\r\n    //===END: ע⣺6,Ҫⲿͷŷصַָ룬ڴй¶\r\n\r\n    //===BEGIN: ºһҪתԴַָ룬ڶǴתĿ껺ָ룬Ŀ껺ĴС\r\n    static bool AnsiToUnicode(const char* lpszAnsi, wchar_t* lpszUnicode, int nLen);\r\n    static bool UnicodeToAnsi(const wchar_t* lpszUnicode, char* lpszAnsi, int nLen);\r\n    static bool AnsiToUtf8(const char* lpszAnsi, char* lpszUtf8, int nLen);\r\n    static bool Utf8ToAnsi(const char* lpszUtf8, char* lpszAnsi, int nLen);\r\n    static bool UnicodeToUtf8(const wchar_t* lpszUnicode, char* lpszUtf8, int nLen);\r\n    static bool Utf8ToUnicode(const char* lpszUtf8, wchar_t* lpszUnicode, int nLen);\r\n    //===END: ºһҪתԴַָ룬ڶǴתĿ껺ָ룬Ŀ껺ĴС\r\n\r\n    static std::wstring AnsiToUnicode(const std::string& strAnsi);\r\n    static std::string UnicodeToAnsi(const std::wstring& strUnicode);\r\n    static std::string AnsiToUtf8(const std::string& strAnsi);\r\n    static std::string Utf8ToAnsi(const std::string& strUtf8);\r\n    static std::string UnicodeToUtf8(const std::wstring& strUnicode);\r\n    static std::wstring Utf8ToUnicode(const std::string& strUtf8);\r\n\r\n    static void DecodeQuotedPrintable(const char* pSrc, char* pDest);\r\n    static bool UTF8ToUnicode(const char* pszUTF8, std::wstring& strDest);\r\n    static bool GB2312ToUnicode(const char* pszGB2312, std::wstring& strDest);\r\n    static bool UnicodeToGB2312(const wchar_t* pszUnicode, std::string& strDest);\r\n\r\n    static std::string ws2s(const std::wstring& ws);\r\n    static std::wstring s2ws(const std::string& s);\r\n\r\nprivate:\r\n    EncodeUtil() = delete;\r\n    ~EncodeUtil() = delete;\r\n\r\n    EncodeUtil(const EncodeUtil& rhs) = delete;\r\n    EncodeUtil& operator=(const EncodeUtil& rhs) = delete;\r\n};\r\n\r\n#endif // !__ENCODE_H__\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/MailHelper.cpp",
    "content": "#include \"MailHelper.h\"\r\n\r\n#include \"EncodeUtil.h\"\r\n#include \"Base64Util.h\"\r\n\r\n#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))\r\n\r\nbool MailHelper::m_bIsFromMobilePlatform = false;\r\nstd::string MailHelper::m_strMail;\r\n\r\nstd::vector<std::string> MailHelper::m_vecBoundary;\r\nstd::vector<std::wstring> MailHelper::m_vecText;\r\nstd::vector<std::wstring> MailHelper::m_vecHTML;\r\nstd::map<std::wstring, std::string>\tMailHelper::m_vecAttachment;\r\n\r\nwchar_t MailHelper::g_szEmailSavePath[256] = { 0 };\r\n\r\nvoid MailHelper::splitString(const std::string& str, std::vector<std::string>& v, const char* delimiter/* = \"|\"*/)\r\n{\r\n    if (delimiter == NULL || str.empty())\r\n        return;\r\n\r\n    std::string buf(str);\r\n    size_t pos = std::string::npos;\r\n    std::string substr;\r\n    int delimiterlength = strlen(delimiter);\r\n    while (true)\r\n    {\r\n        pos = buf.find(delimiter);\r\n        if (pos != std::string::npos)\r\n        {\r\n            substr = buf.substr(0, pos);\r\n            if (!substr.empty())\r\n                v.push_back(substr);\r\n\r\n            buf = buf.substr(pos + delimiterlength);\r\n        }\r\n        else\r\n        {\r\n            if (!buf.empty())\r\n                v.push_back(buf);\r\n            break;\r\n        }\r\n    }\r\n}\r\n\r\nint MailHelper::analyzeTags(const std::vector<std::string>& vecString, std::map<std::string, std::string>& mapString)\r\n{\r\n    mapString.clear();\r\n    size_t nCount = vecString.size();\r\n    std::string strTemp, strContent, strTag;\r\n    for (size_t i = 0, j = 0; i < nCount; ++i)\r\n    {\r\n        const std::string& strItem = vecString[i];\r\n        if (strItem.empty())\r\n            continue;\r\n\r\n        if (strItem[0] == 0x20 || strItem[1] == 0x09)\r\n        {\r\n            if (!strTag.empty())\r\n                strContent += strItem;\r\n\r\n            continue;\r\n        }\r\n\r\n        if (!strTag.empty())\r\n        {\r\n            mapString.insert(std::pair<std::string, std::string>(strTag, strContent));\r\n            strTag.clear();\r\n        }\r\n\r\n        strTemp = strItem;\r\n        MailHelper::stringToUpper(strTemp);\r\n\r\n        for (j = 0; j < sizeof(EMAIL_TAG_LIST) / sizeof(EMAIL_TAG_LIST[0]); ++j)\r\n        {\r\n            //EMAIL_TAG_LISTͷ\r\n            if (strTemp.find(EMAIL_TAG_LIST[j]) == 0)\r\n            {\r\n                strTag = EMAIL_TAG_LIST[j];\r\n                strContent = strItem.substr(strlen(EMAIL_TAG_LIST[j]));\r\n            }\r\n        }\r\n\r\n\r\n        if (i == nCount - 1 && !strTag.empty())\r\n        {\r\n            mapString.insert(std::pair<std::string, std::string>(strTag, strContent));\r\n            strTag.clear();\r\n        }\r\n    }\r\n\r\n    return (long)mapString.size();\r\n}\r\n\r\n//\"3 Mar 2020 17:56:49\"ʽתڽṹ\r\nbool MailHelper::parseTimeString(const std::string& dateString, TimeItem& timeItem)\r\n{\r\n    std::vector<std::string> vecItems;\r\n    MailHelper::splitString(dateString, vecItems, \" \");\r\n    if (vecItems.size() != 4)\r\n        return false;\r\n\r\n    timeItem.day = atoi(vecItems[0].c_str());\r\n    timeItem.year = atoi(vecItems[2].c_str());\r\n    if (vecItems[1] == \"Jan\")\r\n        timeItem.month = 1;\r\n    else if (vecItems[1] == \"Feb\")\r\n        timeItem.month = 2;\r\n    else if (vecItems[1] == \"Mar\")\r\n        timeItem.month = 3;\r\n    else if (vecItems[1] == \"Apr\")\r\n        timeItem.month = 4;\r\n    else if (vecItems[1] == \"May\")\r\n        timeItem.month = 5;\r\n    else if (vecItems[1] == \"Jun\")\r\n        timeItem.month = 6;\r\n    else if (vecItems[1] == \"Jul\")\r\n        timeItem.month = 7;\r\n    else if (vecItems[1] == \"Aug\")\r\n        timeItem.month = 8;\r\n    else if (vecItems[1] == \"Sept\")\r\n        timeItem.month = 9;\r\n    else if (vecItems[1] == \"Oct\")\r\n        timeItem.month = 10;\r\n    else if (vecItems[1] == \"Nov\")\r\n        timeItem.month = 11;\r\n    else if (vecItems[1] == \"Dec\")\r\n        timeItem.month = 12;\r\n\r\n    std::vector<std::string> vecItems2;\r\n    //17:56:49ֳʱ\r\n    MailHelper::splitString(vecItems[3], vecItems2, \":\");\r\n    if (vecItems2.size() != 3)\r\n        return false;\r\n\r\n    timeItem.hour = atoi(vecItems2[0].c_str());\r\n    timeItem.minute = atoi(vecItems2[1].c_str());\r\n    timeItem.second = atoi(vecItems2[2].c_str());\r\n\r\n    return true;\r\n}\r\n\r\nvoid MailHelper::analyzeString(const std::string& strData, std::wstring& strResult)\r\n{\r\n    strResult.clear();\r\n    long lOffset = strData.find(\"=?\", 0);\r\n    if (lOffset == std::string::npos)\r\n    {\r\n        strResult = EncodeUtil::s2ws(strData);\r\n        return;\r\n    }\r\n\r\n    long lPhaseEnd = 0, lCharsetEnd = 0, lCodeEnd = 0;\r\n    long lCharsetType = 0;\r\n    std::string strPhase, strCharset, strLastCharset;\r\n    char cCodeType = 0;\r\n    std::string strTemp;\r\n    std::wstring strUnicode;\r\n    //ȡ=?֮ǰ\r\n    lOffset = 0;\r\n    while ((lOffset = strData.find(\"=?\", lOffset)) >= 0)\r\n    {\r\n        lOffset += 2;\r\n        strPhase = strData.substr(lOffset);\t// ,ȡһ\r\n\r\n        lPhaseEnd = MailHelper::findOneOf(strPhase, \"\\x09\\x0A\\x0D\\x20\");\r\n        if (lPhaseEnd != std::string::npos)\r\n            strPhase = strPhase.substr(0, lPhaseEnd);\r\n\r\n        lPhaseEnd = strPhase.rfind('?');\r\n        if (lPhaseEnd == std::string::npos || strPhase.rfind('=') != lPhaseEnd + 1)\r\n            lPhaseEnd = strPhase.length() - 1;\r\n\r\n        //ȡ=??=֮\r\n        strPhase = strPhase.substr(0, lPhaseEnd);\r\n\r\n        //ȡַ\r\n        lCharsetEnd = strPhase.find('?', 0);\r\n        strCharset = strPhase.substr(0, lCharsetEnd);\r\n        lCharsetType = getCharset(strCharset.c_str());\r\n\r\n        strPhase = strPhase.substr(lCharsetEnd + 1);\r\n\r\n        //ȡ뷽ʽ\r\n        lCodeEnd = strPhase.find('?', 0);\r\n        cCodeType = strPhase[0];\r\n        strPhase = strPhase.substr(lCodeEnd + 1);\r\n\r\n        if (!strPhase.empty())\r\n        {\r\n            switch (cCodeType)\r\n            {\r\n            case 'b':\r\n            case 'B':\r\n                Base64Util::decode(strPhase.c_str(), strPhase.length(), strTemp);\r\n                break;\r\n            case 'q':\r\n            case 'Q':\r\n            {\r\n                char szDest[128] = { 0 };\r\n                EncodeUtil::DecodeQuotedPrintable(strPhase.c_str(), szDest);\r\n                strTemp = szDest;\r\n                break;\r\n            }\r\n            }\r\n\r\n            if (lCharsetType == CHARSET_UTF8_ID)\r\n            {\r\n                EncodeUtil::UTF8ToUnicode(strTemp.c_str(), strUnicode);\r\n                strResult += strUnicode;\r\n            }\r\n            else if (lCharsetType == CHARSET_GB2312_ID || lCharsetType == CHARSET_GBK_ID)\r\n            {\r\n                EncodeUtil::GB2312ToUnicode(strTemp.c_str(), strUnicode);\r\n                strResult += strUnicode;\r\n            }\r\n\r\n            lOffset += (lPhaseEnd + 1);\r\n        }\r\n    }\r\n}\r\n\r\nsize_t MailHelper::findOneOf(const std::string& str, const std::string& matchPatterns)\r\n{\r\n    size_t pos = std::string::npos;\r\n    for (size_t i = 0; i < matchPatterns.length(); ++i)\r\n    {\r\n        pos = str.find(matchPatterns[i]);\r\n        if (pos != std::string::npos)\r\n            return pos;\r\n    }\r\n\r\n    return pos;\r\n}\r\n\r\nlong MailHelper::getCharset(const char* pszData)\r\n{\r\n    std::string strCharset(pszData);\r\n    MailHelper::stringToUpper(strCharset);\r\n    if (strCharset == \"UTF-8\")\r\n        return CHARSET_UTF8_ID;\r\n    if (strCharset == \"GB2312\")\r\n        return CHARSET_GB2312_ID;\r\n    if (strCharset == \"GBK\")\r\n        return CHARSET_GBK_ID;\r\n\r\n    return CHARSET_GB2312_ID;\r\n}\r\n\r\n// ַȫתΪд\r\nvoid MailHelper::stringToUpper(std::string& str)\r\n{\r\n    for (size_t i = 0; i < str.length(); ++i)\r\n    {\r\n        //ASCIIдֵַȶӦСдֵַС32.\r\n        //*p -= 0x20; // 0x20ʮƾ32\r\n        if (str[i] >= 'a' && str[i] <= 'z')           \r\n            str[i] -= 32;\r\n    }\r\n}\r\n\r\n// ַȫתΪСд\r\nvoid MailHelper::stringToLower(std::string& str)\r\n{\r\n    for (size_t i = 0; i < str.length(); ++i)\r\n    {\r\n        //ASCIIдֵַȶӦСдֵַС32.\r\n        //*p -= 0x20; // 0x20ʮƾ32\r\n        if (str[i] >= 'a' && str[i] <= 'z')\r\n            str[i] += 32;\r\n    }\r\n}\r\n\r\n//޼ַ\r\nvoid MailHelper::trim(std::string& str, const char delimiter/* = ' '*/)\r\n{\r\n    std::string temp1;\r\n    size_t length = str.length();\r\n    for (size_t i = 0; i < length; ++i)\r\n    {\r\n        if (str[i] == delimiter)\r\n            continue;\r\n        else\r\n        {\r\n            temp1 = str.substr(i);\r\n            break;\r\n        }\r\n    }\r\n\r\n    std::string temp2;\r\n    length = temp1.length();\r\n    if (length > 0)\r\n    {\r\n        for (size_t i = length - 1; i >= 0; --i)\r\n        {\r\n            if (temp1[i] == delimiter)\r\n                continue;\r\n            else\r\n            {\r\n                temp2 = temp1.substr(0, i + 1);\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    \r\n    str = temp2;\r\n}\r\n\r\nvoid MailHelper::trim(std::wstring& str, const wchar_t delimiter/* = L' '*/)\r\n{\r\n    std::wstring temp1;\r\n    size_t length = str.length();\r\n    for (size_t i = 0; i < length; ++i)\r\n    {\r\n        if (str[i] == delimiter)\r\n            continue;\r\n        else\r\n        {\r\n            temp1 = str.substr(i + 1);\r\n        }\r\n    }\r\n\r\n    std::wstring temp2;\r\n    length = temp1.length();\r\n    if (length > 0)\r\n    {\r\n        for (size_t i = length - 1; i >= 0; --i)\r\n        {\r\n            if (temp1[i] == delimiter)\r\n                continue;\r\n            else\r\n            {\r\n                temp2 = temp1.substr(0, i);\r\n            }\r\n        }\r\n    }\r\n\r\n    str = temp2;\r\n}\r\n\r\nbool MailHelper::analyzeBody(const std::string& strBody)\r\n{\r\n    if (strBody.empty())\r\n        return false;\r\n\r\n    m_strMail = strBody;\r\n\r\n    if (!getBoundaryTag() && !detectMobilePlatform())\r\n        return false;\r\n\r\n    size_t lCount = m_vecBoundary.size();\r\n    std::string strBoundary;\r\n    for (size_t i = 0; i < lCount; ++i)\r\n    {\r\n        strBoundary = \"--\";\r\n        strBoundary += m_vecBoundary[i];\r\n        analyzeBoundary(strBoundary, m_vecBoundary[i]);\r\n    }\r\n\r\n    for (const auto& iter : m_vecAttachment)\r\n    {\r\n        if (!iter.first.empty() && !iter.second.empty())\r\n        {\r\n            std::string strUtf8File = EncodeUtil::UnicodeToUtf8(iter.first);\r\n            FILE* file = fopen(strUtf8File.c_str(), \"wb+\");\r\n            if (file == nullptr)\r\n                continue;\r\n\r\n            //TODOʵʿʼܱȽϴһδȫдɹ\r\n            //Ӧһѭÿηһֱȫ\r\n            size_t writtenLength = fwrite(iter.second.c_str(), sizeof(char), iter.second.length(), file);\r\n            if (writtenLength != iter.second.length())\r\n            {\r\n                //perror(\"write err\");\r\n                //дļʧ\r\n                fclose(file);\r\n                continue;\r\n            }\r\n\r\n            fclose(file);\r\n        }\r\n    }\r\n\r\n    \r\n\r\n    return true;\r\n}\r\n\r\nbool MailHelper::getBoundaryTag()\r\n{\r\n    m_vecBoundary.clear();\r\n    if (m_strMail.empty())\r\n        return false;\r\n\r\n    const char BOUNDARY_TAG[] = \"boundary=\\\"\";\r\n    const long BOUNDARY_TAG_LEN = ARRAYSIZE(BOUNDARY_TAG) - 1;\r\n    long lStart = 0, lEnd = 0;\r\n    std::string strItem;\r\n    while ((lStart = m_strMail.find(BOUNDARY_TAG, lStart)) != std::string::npos)\r\n    {\r\n        lEnd = m_strMail.find('\"', lStart + BOUNDARY_TAG_LEN);\r\n        strItem = m_strMail.substr(lStart + BOUNDARY_TAG_LEN, lEnd - lStart - BOUNDARY_TAG_LEN);\r\n        m_vecBoundary.push_back(strItem);\r\n        lStart = lEnd + 1;\r\n    }\r\n\r\n    return m_vecBoundary.size() > 0;\r\n}\r\n\r\nbool MailHelper::analyzeBoundary(const std::string& strString, const std::string& strBoundary)\r\n{\r\n    std::vector<std::string> vecString;\r\n    splitString(m_strMail, vecString, strString.c_str());\r\n    if (vecString.empty()) \r\n        return false;\r\n\r\n    long lStart = 0, lEnd = 0;\r\n    long lTextType = 0;\r\n    std::wstring strContent;\r\n    std::wstring strFileName;\r\n    std::string strAttachment;\r\n    std::string strTemp;\r\n    size_t lCount = vecString.size();\r\n    for (size_t i = 0; i < lCount; ++i)\r\n    {\r\n        const std::string& strItem = vecString[i];\r\n        if (strItem.empty())\r\n            continue;\r\n\r\n        if (strItem.find(\"boundary=\") != std::string::npos)\r\n            continue;\r\n\r\n        if (strItem.find(\"Content-Type\") == std::string::npos) \r\n            continue;\r\n\r\n        if (isExistBoundary(strItem, strBoundary.c_str()))\r\n            continue;\r\n\r\n        lTextType = analyzeBoundary(strItem, strContent, strAttachment, strFileName);\r\n        if (strContent.empty() && lTextType != APPLICATION_TYPE_ID) \r\n            continue;\r\n\r\n        switch (lTextType)\r\n        {\r\n        case PLAIN_TYPE_ID:\r\n            m_vecText.push_back(strContent);\r\n            break;\r\n        case HTML_TYPE_ID:\r\n            m_vecHTML.push_back(strContent);\r\n            break;\r\n        case APPLICATION_TYPE_ID:\r\n        {\r\n            for (int i = 0; i < 10; ++i)\r\n            {\r\n                if (m_vecAttachment.find(strFileName) == m_vecAttachment.end())\r\n                {\r\n                    m_vecAttachment.insert(std::pair<std::wstring, std::string>(strFileName, strAttachment));\r\n                    break;\r\n                }\r\n\r\n                char szTemp[32] = { 0 };\r\n                sprintf(szTemp, \"(%d)\", i + 1);\r\n                strTemp = szTemp;\r\n                std::wstring strTempW = EncodeUtil::AnsiToUnicode(strTemp);\r\n                strTempW += strFileName;\r\n                //strTemp.Format(_T(\"(%d)%s\"), i + 1, strFileName);\r\n                strFileName = strTempW;\r\n            }\r\n        }\r\n\r\n        //if (!strFileName.empty() && !strContent.empty())\r\n        //{\r\n        //    std::string strUtf8File = EncodeUtil::UnicodeToUtf8(strFileName);\r\n        //    FILE* file = fopen(strUtf8File.c_str(), \"wb+\");\r\n        //    if (file == nullptr)\r\n        //        continue;\r\n\r\n        //    size_t writtenLength = fwrite(strContent.c_str(), sizeof(wchar_t), strContent.length(), file);\r\n        //    if (writtenLength != strContent.length())\r\n        //    {\r\n        //        //дļʧ\r\n        //        fclose(file);\r\n        //        continue;\r\n        //    }\r\n\r\n        //    fclose(file);\r\n        //}\r\n        break;\r\n        }\r\n    }\r\n\r\n    return true;\r\n}\r\n\r\nbool MailHelper::detectMobilePlatform()\r\n{\r\n    if ((m_strMail.find(\"Content-Type\") != std::string::npos) && (m_strMail.find(\"text/plain\") != std::string::npos) &&\r\n        (m_strMail.find(\"Content-Transfer-Encoding\") != std::string::npos) && (m_strMail.find(\"base64\") != std::string::npos) &&\r\n        (m_strMail.find(\"gb2312\") != std::string::npos || m_strMail.find(\"utf-8\") != std::string::npos))\r\n    {\r\n        long lIndex1 = m_strMail.find(\"\\r\\n\\r\\n\");\r\n        long lIndex2 = m_strMail.find(\"\\r\\n.\\r\\n\");\r\n        std::string strTmp;\r\n        if (lIndex1 != std::string::npos && lIndex2 != std::string::npos && lIndex2 > lIndex1)\r\n        {\r\n            strTmp = m_strMail.substr(lIndex1 + 3, lIndex2 - lIndex1 - 2);\r\n            std::string strDecoded;\r\n            Base64Util::decode(strTmp.c_str(), strTmp.length(), strDecoded);\r\n            std::wstring strResult;\r\n            if (m_strMail.find(\"gb2312\") != std::string::npos)\r\n                EncodeUtil::GB2312ToUnicode(strDecoded.c_str(), strResult);\r\n            else if (m_strMail.find(\"utf-8\") != std::string::npos)\r\n                EncodeUtil::UTF8ToUnicode(strDecoded.c_str(), strResult);\r\n            //strResult.Trim();\r\n            trim(strResult);\r\n            m_vecText.push_back(strResult);\r\n            //m_aryHTML.push_back(CString(strResult));\r\n            m_bIsFromMobilePlatform = true;\r\n        }\r\n    }\r\n\r\n    return m_bIsFromMobilePlatform;\r\n}\r\n\r\nbool MailHelper::isExistBoundary(const std::string& strData, const char* lpExclude)\r\n{\r\n    size_t lCount = m_vecBoundary.size();\r\n    for (size_t i = 0; i < lCount; ++i)\r\n    {\r\n        if (m_vecBoundary[i].compare(lpExclude) == 0)\r\n            continue;\r\n\r\n        if (strData.find(m_vecBoundary[i]) != std::string::npos) \r\n            return true;\r\n    }\r\n\r\n    return false;\r\n}\r\n\r\nint MailHelper::analyzeBoundary(const std::string& strData, std::wstring& strResult, std::string& strAttachment, std::wstring& strFileName)\r\n{\r\n    strResult.clear();\r\n    strFileName.clear();\r\n    strAttachment.clear();\r\n\r\n    std::vector<std::string> vecString;\r\n    std::map<std::string, std::string> mapString;\r\n    //SplitString(strData, COMMAND_END_FLAG, aryString, 100);\r\n    splitString(strData, vecString, COMMAND_END_FLAG);\r\n    analyzeTag(vecString, mapString);\r\n\r\n    long lStart = 0, lEnd = 0;\r\n    std::string strTemp, strTemp2, strContent;\r\n    long lType = 0, lCharsetType = 0;\r\n    auto iter = mapString.find(EMAIL_CONTENT_TAG);  \r\n    if (iter != mapString.end())\r\n    {\r\n        strTemp = iter->second;\r\n        stringToUpper(strTemp);\r\n        //strTemp.MakeUpper();\r\n        if (strTemp.find(PLAIN_CONTENT_TYPE) != std::string::npos)\r\n            lType = PLAIN_TYPE_ID;\r\n        else if (strTemp.find(HTML_CONTENT_TYPE) != std::string::npos)\r\n        {\r\n            lStart = strTemp.find(\"NAME=\\\"\");\r\n            if (lStart == std::string::npos)\r\n            {\r\n                lType = HTML_TYPE_ID;\r\n            }\r\n            else\r\n            {\r\n                lType = APPLICATION_TYPE_ID;\r\n                lEnd = strTemp.find(\"\\\"\", lStart + 6);\r\n                if (lEnd != std::string::npos)\r\n                    strTemp2 = iter->second.substr(lStart + 6, lEnd - lStart - 6);\r\n                //strTemp2.Trim('\"');\r\n                trim(strTemp2);\r\n                if (!strTemp2.empty()) \r\n                    analyzeString(strTemp2, strFileName);\r\n            }\r\n        }\r\n        else if (strTemp.find(APPLICATION_CONTENT_TYPE) != std::string::npos)\r\n        {\r\n            lType = APPLICATION_TYPE_ID;\r\n            lStart = strTemp.find(\"NAME=\\\"\");\r\n            if (lStart != std::string::npos)\r\n            {\r\n                lEnd = strTemp.find(\"\\\"\", lStart + 6);\r\n                if (lEnd != std::string::npos)\r\n                    strTemp2 = iter->second.substr(lStart + 6, lEnd - lStart - 6);\r\n                //strTemp2.Trim('\"');\r\n                trim(strTemp2);\r\n                if (!strTemp2.empty()) \r\n                    analyzeString(strTemp2, strFileName);\r\n            }\r\n            else\r\n            {\r\n                lStart = strTemp.find(\"NAME=\");\r\n                if (lStart != std::string::npos)\r\n                    lStart = strTemp.find(\"\\\"\", lStart + 5);\r\n                if (lStart != std::string::npos)\r\n                {\r\n                    lEnd = strTemp.find(\"\\\"\", lStart + 1);\r\n                    if (lEnd != std::string::npos)\r\n                        strTemp2 = iter->second.substr(lStart, lEnd - lStart);\r\n                    //strTemp2.Trim('\"');\r\n                    trim(strTemp2);\r\n\r\n                    if (!strTemp2.empty()) \r\n                        analyzeString(strTemp2, strFileName);\r\n                }\r\n            }\r\n        }\r\n\r\n        lCharsetType = findCharset(strTemp.c_str());\r\n    }\r\n\r\n    long lEncode = 0;\r\n    iter = mapString.find(EMAIL_ENCODING_TAG);\r\n    if (iter != mapString.end())\r\n    {\r\n        strTemp = iter->second;\r\n        //strTemp.MakeUpper();\r\n        stringToUpper(strTemp);\r\n        if (strTemp.find(ENCODE_BASE64_TYPE) != std::string::npos)\r\n            lEncode = ENCODE_BASE64_ID;\r\n        else if (strTemp.find(ECONDE_QP_TYPE) != std::string::npos)\r\n            lEncode = ENCODE_QUOTE_PRINTABLE_ID;\r\n        else if (strTemp.find(ECONDE_7BIT_TYPE) != std::string::npos)\r\n            lEncode = ENCODE_7_BIT_ID;\r\n    }\r\n\r\n    if (lType == 0 || lEncode == 0) \r\n        return 0;\r\n\r\n    lStart = strData.find(\"Content-Type:\");\r\n    if (lStart == std::string::npos)\r\n        return false;\r\n\r\n    lEnd = strData.find(\"\\r\\n\\r\\n\", lStart);\r\n    if (lEnd == std::string::npos)\r\n        return false;\r\n\r\n    std::string strBody = strData.substr(lEnd + 4);\r\n    if (lEncode == ENCODE_BASE64_ID)\r\n    {\r\n        //ȥĽβ\r\n        long lEnd = strBody.find(\"\\r\\n\\r\\n\");\t \r\n        if (lEnd != std::string::npos)\r\n            strBody = strBody.substr(0, lEnd);\r\n    }\r\n\r\n    //if (lType==APPLICATION_TYPE_ID)\r\n    //{\r\n    //\tCFileException ex;\r\n    //\tCFile file;\r\n    //\tfile.Open(_T(\"1112.txt\"), CFile::modeWrite |CFile::shareDenyWrite | CFile::modeCreate, &ex);\r\n    //\tfile.Write(strBody.GetString(),strBody.GetLength());\r\n    //}\r\n\r\n    switch (lEncode)\r\n    {\r\n    case ENCODE_BASE64_ID:\r\n        Base64Util::decode(strBody.c_str(), strBody.length(), strContent);\r\n        if (lType == APPLICATION_TYPE_ID)\r\n        {\r\n            strAttachment = strContent;\r\n        }\r\n        else\r\n        {\r\n            if (lCharsetType == CHARSET_UTF8_ID)\r\n                EncodeUtil::UTF8ToUnicode(strContent.c_str(), strResult);\r\n            else\r\n                EncodeUtil::GB2312ToUnicode(strContent.c_str(), strResult);\r\n        }\r\n        break;\r\n\r\n    case ENCODE_QUOTE_PRINTABLE_ID:\r\n    {\r\n        char* pDest = new char[strBody.length() * 2];\r\n        EncodeUtil::DecodeQuotedPrintable(strBody.c_str(), pDest);\r\n        strContent = pDest;\r\n        delete[] pDest;\r\n        if (lType == APPLICATION_TYPE_ID)\r\n        {\r\n            strAttachment = strContent;\r\n        }\r\n        else\r\n        {\r\n            if (lCharsetType == CHARSET_UTF8_ID)\r\n                EncodeUtil::UTF8ToUnicode(strContent.c_str(), strResult);\r\n            else\r\n                EncodeUtil::GB2312ToUnicode(strContent.c_str(), strResult);\r\n        }\r\n    }\r\n        \r\n        break;\r\n\r\n    case ENCODE_7_BIT_ID:\r\n    {\r\n        strContent = strBody;\r\n        if (lType == APPLICATION_TYPE_ID)\r\n        {\r\n            strAttachment = strContent;\r\n        }\r\n        else\r\n        {\r\n            if (lCharsetType == CHARSET_UTF8_ID)\r\n                EncodeUtil::UTF8ToUnicode(strContent.c_str(), strResult);\r\n            else\r\n                strResult = EncodeUtil::AnsiToUnicode(strBody);\r\n        }\r\n    }\r\n        break;\r\n\r\n    default:\r\n        return 0;\r\n    }\r\n\r\n    return lType;\r\n}\r\n\r\nsize_t MailHelper::analyzeTag(const std::vector<std::string>& vecString, std::map<std::string, std::string>& mapString)\r\n{\r\n    mapString.clear();\r\n    size_t lCount = vecString.size();\r\n    std::string strTemp, strContent, strTag;\r\n    for (size_t i = 0, j = 0; i < lCount; ++i)\r\n    {\r\n        const std::string& strItem = vecString[i];\r\n        if (strItem.empty()) \r\n            continue;\r\n\r\n        if (strItem[0] == 0x20 || strItem[0] == 0x09)\r\n        {\r\n            if (!strTag.empty()) \r\n                strContent += strItem;\r\n                \r\n            continue;\r\n        }\r\n\r\n        if (!strTag.empty())\r\n        {\r\n            mapString.insert(std::pair<std::string, std::string>(strTag, strContent));\r\n            strTag.clear();\r\n        }\r\n\r\n        strTemp = strItem;\r\n        stringToUpper(strTemp);\r\n\r\n        for (j = 0; j < ARRAYSIZE(EMAIL_TAG_LIST); ++j)\r\n        {\r\n            if (strTemp.find(EMAIL_TAG_LIST[j]) == 0)\r\n            {\r\n                strTag = EMAIL_TAG_LIST[j];\r\n                strContent = strItem.substr(strlen(EMAIL_TAG_LIST[j]));\r\n            }\r\n        }\r\n\r\n        if (i == lCount - 1 && !strTag.empty())\r\n        {\r\n            mapString.insert(std::pair<std::string, std::string>(strTag, strContent));\r\n            strTag.clear();\r\n        }\r\n    }\r\n\r\n    return (long)mapString.size();\r\n}\r\n\r\nlong MailHelper::findCharset(const char* pszData)\r\n{\r\n    std::string strCharset(pszData);\r\n    stringToUpper(strCharset);\r\n    //strCharset.MakeUpper();\r\n\r\n    if (strCharset.find(\"UTF-8\") != std::string::npos) \r\n        return CHARSET_UTF8_ID;\r\n\r\n    if (strCharset.find(\"GB2312\") != std::string::npos)\r\n        return CHARSET_GB2312_ID;\r\n\r\n    if (strCharset.find(\"GBK\") != std::string::npos)\r\n        return CHARSET_GBK_ID;\r\n\r\n    if (strCharset.find(\"7BIT\") != std::string::npos)\r\n        return CHARSET_ASCII_ID;\r\n\r\n    return CHARSET_GB2312_ID;\r\n}"
  },
  {
    "path": "Chapter06/code/RecvMail/MailHelper.h",
    "content": "#ifndef __MAIL_HELPER_H__\r\n#define __MAIL_HELPER_H__\r\n\r\n#include <string>\r\n#include <map>\r\n#include <vector>\r\n\r\n#define LINE_END_FLAG\t\t            \"\\r\\n\"\r\n#define COMMAND_END_FLAG\t            \"\\r\\n\"\r\n#define DATA_END_FLAG\t\t            \".\\r\\n\"\r\n#define TITLE_CODE_TYPE\t\t            \"=?gb2312?B?\"\r\n#define TITLE_CODE_TYPE_END\t            \"?=\"\r\n\r\n#define EMAIL_DATE_TAG\t\t\t        \"DATE:\"\r\n#define EMAIL_SUBJECT_TAG\t\t        \"SUBJECT:\"\r\n#define EMAIL_MESSAGE_ID_TAG\t        \"MESSAGE-ID:\"\r\n#define EMAIL_FROM_TAG\t\t\t        \"FROM:\"\r\n#define EMAIL_CONTENT_TAG\t\t        \"CONTENT-TYPE:\"\r\n#define EMAIL_BOUNDARY_TAG\t\t        \"BOUNDARY=\"\r\n#define\tEMAIL_ENCODING_TAG\t\t        \"CONTENT-TRANSFER-ENCODING:\"\r\n#define EMAIL_CONTENT_DESPOSITION_TAG   \"Content-Disposition:\"\r\n\r\n#define\tPLAIN_CONTENT_TYPE\t\t\t\t\"TEXT/PLAIN\"\r\n#define\tHTML_CONTENT_TYPE\t\t\t\t\"TEXT/HTML\"\r\n#define\tAPPLICATION_CONTENT_TYPE\t\t\"APPLICATION\"\r\n\r\n#define ENCODE_BASE64_TYPE\t\t\t\t\"BASE64\"\r\n#define ECONDE_QP_TYPE\t\t\t\t\t\"QUOTED-PRINTABLE\"\r\n#define ECONDE_7BIT_TYPE\t\t\t\t\"7BIT\"\r\n#define MAIL_ADRRESS_SPLIT_LINE\t\t\t_T(\"--split line--\")\r\n#define MAIL_ADRRESS_SPLIT_LINE_DISPLAY\t_T(\"-----------------\\r\\n\")\r\n\r\nconst char EMAIL_TAG_LIST[][32] =\r\n{\r\n    EMAIL_SUBJECT_TAG,\r\n    EMAIL_DATE_TAG,\r\n    EMAIL_MESSAGE_ID_TAG,\r\n    EMAIL_FROM_TAG,\r\n    EMAIL_CONTENT_TAG,\r\n    EMAIL_ENCODING_TAG,\r\n};\r\n\r\nenum TEXT_TYPE_ID\r\n{\r\n    UNKONW_TEXT_TYPE_ID = 0,\r\n    PLAIN_TYPE_ID = 1,\r\n    HTML_TYPE_ID,\r\n    APPLICATION_TYPE_ID\r\n};\r\n\r\nenum ENCODE_TYPE_ID\r\n{\r\n    ENCODE_BASE64_ID = 1,\r\n    ENCODE_QUOTE_PRINTABLE_ID,\r\n    ENCODE_7_BIT_ID\r\n};\r\n\r\nenum CHARSET_TYPE_ID\r\n{\r\n    CHARSET_GB2312_ID = 0,\r\n    CHARSET_UTF8_ID,\r\n    CHARSET_GBK_ID,\r\n    CHARSET_ASCII_ID\r\n};\r\n\r\nstruct TimeItem\r\n{\r\n    int year;\r\n    int month;\r\n    int day;\r\n\r\n    int hour;\r\n    int minute;\r\n    int second;\r\n};\r\n\r\nclass MailHelper final\r\n{\r\npublic:\r\n\tMailHelper() = delete;\r\n\t~MailHelper() = delete;\r\n\r\npublic:\r\n    static void splitString(const std::string& str, std::vector<std::string>& v, const char* delimiter = \"|\");\r\n    static int analyzeTags(const std::vector<std::string>& aryString, std::map<std::string, std::string>& mapString);\r\n    static bool parseTimeString(const std::string& dateString, TimeItem& timeItem);\r\n    static void analyzeString(const std::string& strData, std::wstring& strResult);\r\n    static size_t findOneOf(const std::string& str, const std::string& matchPatterns);\r\n    static long getCharset(const char* pszData);\r\n    static void stringToUpper(std::string& str);\r\n    static void stringToLower(std::string& str);\r\n    static void trim(std::string& str, const char delimiter = ' ');\r\n    static void trim(std::wstring& str, const wchar_t delimiter = L' ');\r\n    static bool analyzeBody(const std::string& strBody);\r\n    static bool getBoundaryTag();\r\n    static bool analyzeBoundary(const std::string& strString, const std::string& strBoundary);\r\n    static bool detectMobilePlatform();\r\n    static bool isExistBoundary(const std::string& strData, const char* lpExclude);\r\n    static int analyzeBoundary(const std::string& strData, std::wstring& strResult, std::string& strAttachment, std::wstring& strFileName);\r\n    static size_t analyzeTag(const std::vector<std::string>& vecString, std::map<std::string, std::string>& mapString);\r\n    static long findCharset(const char* pszData);\r\nprivate:\r\n    static bool\t\t                                m_bIsFromMobilePlatform;\r\n\r\n    static std::string                              m_strMail;\r\n\r\n    static std::vector<std::string>\t                m_vecBoundary;\r\n    static std::vector<std::wstring>\t            m_vecText;\r\n    static std::vector<std::wstring>\t            m_vecHTML;\r\n\r\n    static std::map<std::wstring, std::string>\t    m_vecAttachment;\t//\r\n\r\n    static wchar_t                                  g_szEmailSavePath[256];\r\n};\r\n\r\n#endif //!__MAIL_HELPER_H__\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/MailThread.cpp",
    "content": "#include \"StdAfx.h\"\r\n#include \"MailThread.h\"\r\n#include \"LogTrace.h\"\r\n\r\nusing namespace Log;\r\n\r\nMailThread::MailThread(void)\r\n    :m_lCheckMailCount(0), m_lFinishCount(0), m_bDispatched(TRUE)\r\n{\r\n}\r\n\r\nMailThread::~MailThread(void)\r\n{\r\n}\r\n\r\nDWORD MailThread::execute()\r\n{\r\n    m_lStatus = THREAD_STATUS_FREE;\r\n    m_EventCancel.ResetEvent();\r\n\r\n    CStringA strUser, strUrl, strPassword;\r\n    m_lStatus = THREAD_STATUS_CONNECT_DB;\r\n    m_lStatus = THREAD_STATUS_FREE;\r\n    MailAnalysis\tmail;\r\n    long lWaitTime = 1;\r\n    long lMailCount = 0, i = 0, lMailIndex = 0;\r\n    long lTick = 0;\r\n    bool bExit = false;\r\n    while (!bExit && !m_EventCancel.Lock(lWaitTime))\r\n    {\r\n        lWaitTime = 1000 * 30;\r\n        if (!m_Pop3Socket.isConnected())\r\n        {\r\n            m_lStatus = THREAD_STATUS_LOGON;\r\n            strUrl = m_szUrl;\r\n            if (!m_Pop3Socket.connect(strUrl, m_lPort))\r\n            {\r\n                m_Pop3Socket.close();\r\n                m_lStatus = THREAD_STATUS_FREE;\r\n                continue;\r\n            }\r\n\r\n            strUser = m_strUser;\r\n            strPassword = m_strPassword;\r\n            if (!m_Pop3Socket.logon(strUser, strPassword))\r\n            {\r\n                m_Pop3Socket.close();\r\n                m_lStatus = THREAD_STATUS_FREE;\r\n                continue;\r\n            }\r\n        }\r\n\r\n        m_lStatus = THREAD_STATUS_GET_LIST;\r\n        if (!m_Pop3Socket.getList())\r\n        {\r\n            m_Pop3Socket.close();\r\n            m_lStatus = THREAD_STATUS_FREE;\r\n            continue;\r\n        }\r\n\r\n        lMailCount = m_Pop3Socket.getMailCount();\r\n        if (lMailCount <= 0) m_lStatus = THREAD_STATUS_EMPTY_EMAIL;\r\n\r\n        MailLogNormal(_T(\"[MailThread::Execute] need scan email (%d).\"), lMailCount);\r\n\r\n        //ʼʼ\r\n        for (lTick = 0; i < lMailCount; ++i, ++lTick)\r\n        {\r\n            if (i == lMailCount - 1)\r\n                ++ m_lFinishCount;\r\n\r\n            if (lTick % 20 == 0)\r\n            {\r\n                if (m_EventCancel.Lock(1))\r\n                {\r\n                    bExit = TRUE;\r\n                    break;\r\n                }\r\n            }\r\n\r\n            m_lStatus = THREAD_STATUS_CHECK_SUBJECT;\r\n            lMailIndex = m_Pop3Socket.getMainIndex(lMailCount - i - 1);\t//ʼʼ\r\n            if (!m_Pop3Socket.getHeader(lMailIndex))\r\n            {\r\n                if (!m_Pop3Socket.isConnected()) break;\r\n                MailLogWarning(_T(\"[MailThread] Failed to get Email header, [Email Index=%d], program will try next time.\"), lMailIndex);\r\n                continue;\r\n            }\r\n\r\n            ++m_lCheckMailCount;\r\n            //Ƿʼtrav_research\r\n            BOOL bStoreIntoDB = FALSE;\r\n            do\r\n            {\r\n                //if (!MailFilter.FilterMailHeader(m_Pop3Socket.GetTitle(), m_Pop3Socket.GetFrom(), m_Pop3Socket.GetMessageID(), m_Pop3Socket.GetDate(), m_Pop3Socket.GetTime()))\r\n                //    break;\r\n\r\n                ::OutputDebugString(m_Pop3Socket.getTitle());\r\n                ::OutputDebugString(_T(\"\\t\"));\r\n                ::OutputDebugString(m_Pop3Socket.getFrom());\r\n                ::OutputDebugString(_T(\"\\t\"));\r\n                ::OutputDebugString(m_Pop3Socket.getMessageID());\r\n                ::OutputDebugString(_T(\"\\t\"));\r\n                //::OutputDebugString(m_Pop3Socket.GetDate());\r\n                ::OutputDebugString(_T(\"\\r\\n\"));\r\n                \r\n\r\n                m_lStatus = THREAD_STATUS_DOWNLOAD_ID;\r\n                if (!m_Pop3Socket.getMail(lMailIndex, mail))\r\n                    break;\r\n\r\n                //if (!MailFilter.FilterContent(mail))\r\n                //    break;\r\n\r\n                bStoreIntoDB = TRUE;\r\n\r\n            } while (0);\r\n\r\n            //еʼдݿ\r\n            m_lStatus = THREAD_STATUS_INTO_DB;\r\n            //MailFilter.WriteDBComplete(m_Pop3Socket.getMessageID(), m_Pop3Socket.getTitle(), m_Pop3Socket.getFrom(), m_Pop3Socket.getDate(), m_Pop3Socket.getTime());\r\n            //if (bStoreIntoDB)\r\n            //    MailFilter.WriteDB();\r\n        }\r\n\r\n        MailLogNormal(_T(\"[MailThread::Execute] start scan finish.(%d/%d)\"), i, lMailCount);\r\n\r\n        if (i == lMailCount)\r\n        {\r\n            i = 0;\r\n            m_Pop3Socket.quit();\r\n            m_Pop3Socket.close();\r\n            //5ɨһ\r\n            lWaitTime = 1000 * 60 * 5;\t\r\n        }\r\n\r\n        m_lStatus = THREAD_STATUS_FREE;\r\n    }\r\n\r\n    m_dwThreadId = 0;\r\n    if (m_hThread)\r\n    {\r\n        ::CloseHandle(m_hThread);\r\n        m_hThread = NULL;\r\n    }\r\n\r\n    m_lStatus = THREAD_STATUS_STOP;\r\n    MailLogNormal(_T(\"[MailThread::Execute] Exit\"));\r\n\r\n    return TRUE;\r\n}\r\n\r\nvoid MailThread::setServerInfo(LPCTSTR lpUrl, long lPort, LPCTSTR lpUser, LPCTSTR lpPassword)\r\n{\r\n    m_szUrl = lpUrl;\r\n    m_lPort = lPort;\r\n    m_strUser = lpUser;\r\n    m_strPassword = lpPassword;\r\n}\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/MailThread.h",
    "content": "#pragma once\r\n#include <deque>\r\n#include \"Pop3Socket.h\"\r\n\r\nclass MailThread\r\n{\r\npublic:\r\n\tMailThread(void);\r\n\tvirtual ~MailThread(void);\r\n\r\n\tvoid setServerInfo(LPCTSTR lpUrl,long lPort,LPCTSTR lpUser,LPCTSTR lpPassword);\r\n\r\n\tinline long getCheckMailCount() const { return m_lCheckMailCount; }\r\n\tinline long getFinishCount() const { return m_lFinishCount; }\r\n\t__int64 getTotalRecvSize() const { return m_Pop3Socket.getTotalRecvSize(); }\r\n\tinline void enableDispatch(BOOL bEnable) { m_bDispatched=bEnable;}\r\n\r\nprotected:\r\n\tvirtual DWORD execute();\r\n\r\n\tCString\t        m_szUrl;\r\n\tlong\t        m_lPort;\r\n\tCString\t        m_strUser;\r\n\tCString         m_strPassword;\r\n\r\n\tlong\t        m_lCheckMailCount;\t//һɨʼ\r\n\tlong\t        m_lFinishCount;\t\t//ɨɴ\r\n\r\n\tbool\t        m_bDispatched;\t\t//Ƿת\r\n\r\n\tPop3Socket\t    m_Pop3Socket;\r\n};\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/Platform.cpp",
    "content": "#include \"Platform.h\"\r\n\r\n#ifdef WIN32\r\n\r\nNetworkInitializer::NetworkInitializer()\r\n{\r\n    WORD wVersionRequested = MAKEWORD(2, 2);\r\n    WSADATA wsaData;\r\n    ::WSAStartup(wVersionRequested, &wsaData);   \r\n}\r\n\r\nNetworkInitializer::~NetworkInitializer()\r\n{\r\n    ::WSACleanup();\r\n}\r\n\r\n#endif"
  },
  {
    "path": "Chapter06/code/RecvMail/Platform.h",
    "content": "#pragma once\r\n\r\n#include <stdint.h>\r\n\r\n#if defined(__GNUC__)\r\n#pragma GCC diagnostic push\r\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\r\n#elif defined(_MSC_VER)\r\n#pragma warning(disable : 4996)\r\n#endif\r\n\r\n#ifdef WIN32\r\n\r\n#pragma comment(lib, \"Ws2_32.lib\")\r\n#pragma comment(lib, \"Shlwapi.lib\")\r\n\r\n//remove warning C4996 on Windows\r\n//#define _CRT_SECURE_NO_WARNINGS\r\n\r\ntypedef int          socklen_t;\r\n//typedef uint64_t     ssize_t;\r\ntypedef unsigned int in_addr_t;\r\n\r\n//Windows ûЩṹĶ壬Ϊֲ㣬ֶЩṹ\r\n#define  XPOLLIN         1\r\n#define  XPOLLPRI        2\r\n#define  XPOLLOUT        4\r\n#define  XPOLLERR        8 \r\n#define  XPOLLHUP        16\r\n#define  XPOLLNVAL       32\r\n#define  XPOLLRDHUP      8192\r\n\r\n#define  XEPOLL_CTL_ADD  1\r\n#define  XEPOLL_CTL_DEL  2\r\n#define  XEPOLL_CTL_MOD  3\r\n\r\n#pragma pack(push, 1)\r\ntypedef union epoll_data {\r\n    void*    ptr;\r\n    int      fd;\r\n    uint32_t u32;\r\n    uint64_t u64;\r\n} epoll_data_t;\r\n\r\nstruct epoll_event {\r\n    uint32_t     events;    /* Epoll events */\r\n    epoll_data_t data;      /* User data variable */\r\n};\r\n#pragma pack(pop)\r\n\r\n#include <winsock2.h>\r\n#include <ws2tcpip.h>\r\n#include <Windows.h>\r\n#include <Ws2ipdef.h>\r\n#include <io.h>     //_pipe\r\n#include <fcntl.h>  //for O_BINARY\r\n#include <shlwapi.h>\r\n\r\nclass NetworkInitializer\r\n{\r\npublic:\r\n    NetworkInitializer();\r\n    ~NetworkInitializer();\r\n};\r\n\r\n#define sleepMS(n) ::Sleep(n)\r\n\r\n#else\r\n\r\ntypedef int SOCKET;\r\n\r\n#define SOCKET_ERROR -1\r\n\r\n#define closesocket(s) close(s)\r\n\r\n\r\n#include <arpa/inet.h>\r\n\r\n#include <netinet/in.h>\r\n#include <netinet/tcp.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n\r\n#include <unistd.h>\r\n#include <stdint.h>\r\n#include <endian.h>\r\n#include <poll.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <inttypes.h>\r\n#include <errno.h>\r\n#include <dirent.h>\r\n\r\n\r\n#include <sys/socket.h>\r\n#include <sys/select.h>\r\n#include <sys/time.h>\r\n#include <sys/types.h>\r\n#include <sys/eventfd.h>\r\n#include <sys/stat.h>\r\n#include <sys/uio.h>\r\n#include <sys/epoll.h>\r\n#include <sys/syscall.h>\r\n\r\n//for ubuntu readv not found\r\n#ifdef __UBUNTU\r\n#include <sys/uio.h>\r\n#endif \r\n\r\n\r\n#define  XPOLLIN         POLLIN\r\n#define  XPOLLPRI        POLLPRI\r\n#define  XPOLLOUT        POLLOUT\r\n#define  XPOLLERR        POLLERR \r\n#define  XPOLLHUP        POLLHUP\r\n#define  XPOLLNVAL       POLLNVAL\r\n#define  XPOLLRDHUP      POLLRDHUP\r\n\r\n#define  XEPOLL_CTL_ADD  EPOLL_CTL_ADD\r\n#define  XEPOLL_CTL_DEL  EPOLL_CTL_DEL\r\n#define  XEPOLL_CTL_MOD  EPOLL_CTL_MOD\r\n\r\n//Linuxû֮\r\n#define ntohll(x) be64toh(x)\r\n#define htonll(x) htobe64(x)\r\n\r\n#define sleepMS(n) ::usleep(n * 1000)\r\n\r\n#endif\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/Pop3Socket.cpp",
    "content": "//#include \"StdAfx.h\"\r\n#include \"Pop3Socket.h\"\r\n\r\n#include <vector>\r\n#include <map>\r\n#include <iostream>\r\n#include <stdio.h>\r\n\r\n#include \"MailHelper.h\"\r\n#include \"EncodeUtil.h\"\r\n\r\n\r\n//ṹȫ1ֽڶ\r\nstruct MailItem\r\n{\r\n    //ʼID\r\n    long\t                                        id;\r\n    //ʼС\r\n    long\t                                        size;\r\n    //ʼ\r\n    std::wstring\t\t                            title;\r\n    //ʼ\r\n    std::wstring\t                                subject;\r\n    int64_t\t\t                                    date;\r\n    int64_t\t\t                                    time;\r\n    //\r\n    std::string\t\t                                from;\r\n    //ʼIDһ                            ΪʼΨһʶ\r\n    std::string\t\t                                messageID;\r\n    //ʼ\r\n    std::wstring\t                                content;\r\n    //͸\r\n    static std::vector<std::wstring, std::string>\tm_vecAttachments;\r\n};\r\n\r\n\r\nstruct Pop3Socket::Impl\r\n{\r\n    typedef std::vector<MailItem>   MAIL_LIST;\r\n    MAIL_LIST\t                    _MailList;\r\n};\r\n\r\nPop3Socket::Pop3Socket(void) : \r\n    m_hSocket(INVALID_SOCKET), m_bConnected(false), m_pImpl(new Impl)\r\n{\r\n    memset(m_szBuffer, 0, sizeof(m_szBuffer));\r\n}\r\n\r\nPop3Socket::~Pop3Socket(void)\r\n{\r\n}\r\n\r\nvoid Pop3Socket::quit()\r\n{\r\n    if (m_hSocket == INVALID_SOCKET)\r\n        return;\r\n\r\n    m_strSend = \"QUIT \\r\\n\";\r\n\r\n    request(false);\r\n}\r\n\r\nvoid Pop3Socket::close()\r\n{\r\n    if (m_hSocket != INVALID_SOCKET)\r\n    {\r\n        closesocket(m_hSocket);\r\n        m_hSocket = INVALID_SOCKET;\r\n    }\r\n\r\n    m_bConnected = false;\r\n}\r\n\r\nlong Pop3Socket::getMailCount() const\r\n{\r\n    return (long)m_pImpl->_MailList.size();\r\n}\r\n\r\nlong Pop3Socket::getMailID(long index) const\r\n{\r\n    if (index < 0 || (long)m_pImpl->_MailList.size() <= index)\r\n        return -1;\r\n\r\n    return m_pImpl->_MailList[index].id;\r\n}\r\n\r\nlong Pop3Socket::getMailSize(long lIndex) const\r\n{\r\n    if (lIndex < 0 || (long)m_pImpl->_MailList.size() <= lIndex) \r\n        return -1;\r\n\r\n    return m_pImpl->_MailList[lIndex].size;\r\n}\r\n\r\nbool Pop3Socket::connect(const char* pszUrl, short port/* = 110*/)\r\n{\r\n    struct sockaddr_in server = { 0 };\r\n    struct hostent* pHostent = NULL;\r\n    unsigned int addr = 0;\r\n\r\n    Pop3Socket::close();\r\n    m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\r\n\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    long tmSend(15 * 1000L), tmRecv(15 * 1000L), noDelay(1);\r\n    setsockopt(m_hSocket, IPPROTO_TCP, TCP_NODELAY, (LPSTR)& noDelay, sizeof(long));\r\n    setsockopt(m_hSocket, SOL_SOCKET, SO_SNDTIMEO, (LPSTR)& tmSend, sizeof(long));\r\n    setsockopt(m_hSocket, SOL_SOCKET, SO_RCVTIMEO, (LPSTR)& tmRecv, sizeof(long));\r\n\r\n    if (inet_addr(pszUrl) == INADDR_NONE)\r\n        pHostent = gethostbyname(pszUrl);\r\n    else\r\n    {\r\n        addr = inet_addr(pszUrl);\r\n        pHostent = gethostbyaddr((char*)& addr, sizeof(addr), AF_INET);\r\n    }\r\n\r\n    if (pHostent == nullptr) \r\n        return false;\r\n\r\n    server.sin_family = AF_INET;\r\n    server.sin_port = htons((u_short)port);\r\n    server.sin_addr.s_addr = *((unsigned long*)pHostent->h_addr);\r\n    if (::connect(m_hSocket, (struct sockaddr*) & server, sizeof(server)) == SOCKET_ERROR)\r\n        return false;\r\n\r\n    if (!recvData()) \r\n        return false;\r\n\r\n    if (!verifyRecvData()) \r\n        return false;\r\n    \r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::recvBody()\r\n{\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    int nRecv = 0;\r\n    long lRecvSize = 0;\r\n    long lPos = 0;\r\n    long lEndLen = 0;\r\n    m_strRecv.clear();\r\n    long lTryCount = 20;\r\n    while (true)\r\n    {\r\n        fd_set fd = { 0 };\r\n        timeval tmout = { 0,1 };\r\n        FD_SET(m_hSocket, &fd);\r\n        if (select(0, &fd, NULL, NULL, &tmout) < 1 || !FD_ISSET(m_hSocket, &fd))\r\n        {\r\n            if (lTryCount <= 0) break;\r\n\r\n            Sleep(500);\r\n            --lTryCount;\r\n            continue;\r\n        }\r\n\r\n        memset(m_szBuffer, 0, sizeof(m_szBuffer));\r\n        nRecv = ::recv(m_hSocket, m_szBuffer, sizeof(m_szBuffer) - 1, NULL);\r\n        if (nRecv == SOCKET_ERROR)\r\n        {\r\n            Pop3Socket::close();\r\n            return false;\r\n        }\r\n\r\n        if (nRecv > 0)\r\n        {\r\n            m_strRecv.append(m_szBuffer, nRecv);\r\n            lRecvSize += nRecv;\r\n        }\r\n\r\n        if (m_strRecv.find(\"\\r\\n.\\r\\n\") != std::string::npos)\r\n            break;\r\n    }\r\n\r\n    //ϸжʼβ,Ч\r\n    if (m_strRecv.find(\"\\r\\n.\\r\\n\") == std::string::npos)\r\n        return false;\r\n\r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::recvData()\r\n{\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    int nRecv = 0;\r\n    long lRecvSize = 0;\r\n    long lPos = 0;\r\n    long lEndLen = 0;\r\n    m_strRecv.clear();\r\n    long nTryCount = 5;\r\n    while (true)\r\n    {\r\n        fd_set fd = { 0 };\r\n        timeval tv = { 0,1 };\r\n        FD_SET(m_hSocket, &fd);\r\n        if (select(0, &fd, NULL, NULL, &tv) < 1 || !FD_ISSET(m_hSocket, &fd))\r\n        {\r\n            if (nTryCount <= 0)\r\n                break;\r\n\r\n            --nTryCount;\r\n            sleepMS(50);\r\n            continue;\r\n        }\r\n\r\n        memset(m_szBuffer, 0, sizeof(m_szBuffer));\r\n        nRecv = ::recv(m_hSocket, m_szBuffer, sizeof(m_szBuffer) - 1, NULL);\r\n        if (nRecv == SOCKET_ERROR)\r\n        {\r\n            Pop3Socket::close();\r\n            return false;\r\n        }\r\n\r\n        if (nRecv > 0)\r\n        {\r\n            m_strRecv.append(m_szBuffer, nRecv);\r\n            lRecvSize += nRecv;\r\n        }\r\n\r\n        //յ\\r\\nֹ֮ͣ\r\n        if (m_strRecv.find(\"\\r\\n\") != std::string::npos) \r\n            break;\r\n    }\r\n\r\n    //ϸжϽβ,Ч\r\n    if (m_strRecv.find(\"\\r\\n\") == std::string::npos)\r\n        return false;\r\n\r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::verifyRecvData()\r\n{\r\n    if (m_strRecv.empty()) \r\n        return false;\r\n\r\n    //յǰ4ַǲ\"+OK\"\"-ERR\"\r\n    char szHeader[5] = { 0 };\r\n    long lHeaderSize = sizeof(szHeader) - 1;\r\n    strncpy_s(szHeader, m_strRecv.c_str(), lHeaderSize);\r\n    _strupr_s(szHeader);\r\n    if (szHeader[0] == '+' && szHeader[1] == 'O' && szHeader[2] == 'K') \r\n        return true;\r\n\r\n    if (szHeader[0] == '-' && szHeader[1] == 'E' && szHeader[2] == 'R' && szHeader[3] == 'R') \r\n        return false;\r\n\r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::logon(const char* pszUser, const char* pszPassword)\r\n{\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    int nResult = 0;\r\n    char szTmp[32] = { 0 };\r\n    sprintf(szTmp, \"USER %s\\r\\n\", pszUser);\r\n    m_strSend.clear();\r\n    m_strSend.append(szTmp, strlen(szTmp));\r\n    if (!request(false))\r\n        return false;\r\n\r\n    if (!verifyRecvData()) \r\n        return false;\r\n\r\n    memset(szTmp, 0, sizeof(szTmp));\r\n    sprintf(szTmp, \"PASS %s\\r\\n\", pszPassword);\r\n    m_strSend.clear();\r\n    m_strSend.append(szTmp, strlen(szTmp));\r\n    if (!request(false)) \r\n        return false;\r\n\r\n    if (!verifyRecvData()) \r\n        return false;\r\n\r\n    m_bConnected = true;\r\n\r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::getStat()\r\n{\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    m_strSend = \"STAT\\r\\n\";\r\n    if (!request(FALSE)) \r\n        return false;\r\n\r\n    if (!verifyRecvData()) \r\n        return false;\r\n\r\n    /*\r\n    m_strSend.Format(\"UIDL\\r\\n\",0);\r\n    if (!request(DATA_END_FLAG)) \r\n        return false;\r\n    if (!verifyRecvData()) \r\n        return false;\r\n    */\r\n\r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::getList()\r\n{\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    m_strSend = \"LIST\\r\\n\";\r\n    if (!request(true)) \r\n        return false;\r\n\r\n    if (!verifyRecvData()) \r\n        return false;\r\n\r\n    Impl::MAIL_LIST& mailList = m_pImpl->_MailList;\r\n    mailList.clear();\r\n\r\n    //صʼбַ+OK 5 30284\\r\\n1 8284\\r\\n2 11032\\r\\n3 2989\\r\\n4 3871\\r\\n5 4108\\r\\n.\\r\\n\r\n    MailItem item = { 0 };\r\n    std::vector<std::string> vecString; \r\n    std::vector<std::string> vecStringItem;\r\n    MailHelper::splitString(m_strRecv, vecString, \"\\r\\n\");\r\n    size_t count = vecString.size();\r\n    std::cout << \"mail list: \" << std::endl;\r\n    for (size_t i = 0; i < count; ++i)\r\n    {\r\n        const std::string& strItem = vecString[i];\r\n        if (strItem[0] == '+') \r\n            continue;\r\n\r\n        MailHelper::splitString(strItem, vecStringItem, \" \");\r\n        if (vecStringItem.size() == 2)\r\n        {\r\n            item.id = atoi(vecStringItem[0].c_str());\r\n            item.size = atoi(vecStringItem[1].c_str());\r\n\r\n            std::cout << \"id: \" << item.id << \", size: \" << item.size << std::endl;\r\n            mailList.push_back(item);\r\n        }\r\n        vecStringItem.clear();\r\n    }\r\n\r\n    return true;\r\n}\r\n\r\n\r\nbool Pop3Socket::request(bool toRecvBody)\r\n{\r\n    if (m_hSocket == INVALID_SOCKET)\r\n        return false;\r\n\r\n    if (m_strSend.empty()) \r\n        return true;\r\n\r\n    m_strRecv.clear();\r\n    \r\n    int result = ::send(m_hSocket, m_strSend.c_str(), (int)m_strSend.length(), NULL);\r\n    \r\n    Sleep(300);\r\n\r\n    if (result == SOCKET_ERROR)\r\n        return false;\r\n\r\n    if (toRecvBody)\r\n    {\r\n        if (!recvBody())\r\n        {\r\n            Pop3Socket::close();\r\n            return false;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        if (!recvData())\r\n        {\r\n            Pop3Socket::close();\r\n            return false;\r\n        }\r\n    }\r\n\r\n    return true;\r\n}\r\n\r\nbool Pop3Socket::getHeader(int index)\r\n{\r\n    //m_dwDate = m_dwTime = 0;\r\n    //m_strFrom.Empty();\r\n    //m_strMessageID.Empty();\r\n    //m_strTitle.Empty();\r\n    //m_strSubject.Empty();\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    int nResult = 0;\r\n    char szTmp[16] = { 0 };\r\n    sprintf(szTmp, \"TOP % d 1\\r\\n\", index);\r\n    m_strSend.clear();\r\n    m_strSend.append(szTmp, strlen(szTmp));\r\n\r\n    if (!request(true)) \r\n        return false;\r\n\r\n    //ʼʽ\r\n    /*\r\n        +OK 4108 octets\r\n        Received: from sonic310-21.consmr.mail.gq1.yahoo.com (unknown [98.137.69.147])\r\n\t        by mx29 (Coremail) with SMTP id T8CowABHlztmml5erAoHAQ--.23443S3;\r\n\t        Wed, 04 Mar 2020 01:56:57 +0800 (CST)\r\n        DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1583258213; bh=ABL3sF+YL/syl+mwknwxiAlvKPRNYq4AYTujNrPA86g=; h=Date:From:Reply-To:Subject:References:From:Subject; b=OrAQTs0GJnA9yVA08gRolpsoe9E2PQhc3BLvK0msqlZkIYPYVLD1SHAHc7eI3imH4b+hggrFA6wUeiSqqq2du3tOokCU8ckq3LrbdI82EZ013M3KL6o2y+/wdPIj9Mo1TeGbmqtthYBOpGvEgwzsQMNnydkJdy5tDaW6IBT2Ux+IaP0K+jp71eYXcWjdR0mSyu3aMhLqc0z4l5HlZYpZRQG1hjxZOaCH/UjgBAdr98JecVvuRp4s5iGe6OIxc0p3xzRZBxTlLdgdHjmTKHQ00eTNCfFYai2rMxf4830lMYTwKI6O/iu3jUbTA2yjxx0LrYBFTiWzFetwAQupKLw3Qg==\r\n        X-YMail-OSG: 7HG016cVM1nEI.fdz8BF9PN3tO6MvrppAOwu_jpQ09s4eVdYvLXavghvjDvWrRW\r\n         B7PF6pZKuhiLjV7yCErxEmbWUKPLzX.WL4RJ9u4tnPC4NyVp30cLaoGZVIapWeFtqRpKlh31orVY\r\n         WTsWE9FcDuHts5p2MPAd7Si52EZfyPuoffEIWrd481hx1IdSsRQN_V7mpfxihvReOIoQ5rCWuMzd\r\n         oK5kXOho8iOwXlEVPzdTs33RD5rQmwbycPtLS7.FARNxWl9yO9Lrd25gDYa1hXvgG4aQptJQK5aD\r\n         cHQpZUYqdiaNUaEoGoIDQR_HVndus53gTyzUzmJONpDo6wQM39O.pih7VGCrgLqB2_hHeJdPEUIk\r\n         jCcwkqNn0cfDyc4QwBdQ65jcgm2cJDPFgoODhxDIqTqeFVbXFr2cXLand8vAqARi3tlnmsOUA7ZI\r\n         DiSvhSx8eYGd4_frX1LfP.TpctO9Uuc3ZP6iP_K24F9HE9HNN9_swBUEPlBOB3jjSPSOdmiEMteF\r\n         NWj1qOJ8i47BwMBILtx0dZheRRSxvfzSA.JmnUghFo80EgaGRgXYIEAzt8hpvxdZbtwrg0k0WPeF\r\n         Y4LC2my.A9XcsnF58558bweJDaDHCJyLGFnE8__ZQI163vMPqY6QbU3OP0EJz2OE1rPBOrq9PUol\r\n         TZjOEu6ghV1PG2HhX0Ydc.vvq5mloqbKusdzV5EgtpFZjLdp1_RQWuI1LG865Ig756HBaozMU6RG\r\n         0FUMn86pvXRBbNMPD6ADwcw4rdw.Xqk5TRZkqJpSp6KX82OjAgFu0xxMiZnQ7LNemrsJ2UQK9Y2_\r\n         nm8nrwOIX03Ol6Z2KspWUcPNkqPIZ6vGAr9FO9qqE_elB3K4hh04lq_KS5Tv_XoI3deD4r3J6RTb\r\n         O9xp5O6cbe0Svy7FS7DosvJfK958_57Kk_6vk6wxxc3D8cx_k6P.yPbphTCLYFdfnbV5sRKNvUKT\r\n         .apHpO8d0GUf29QtSc3dwBDrLEcRSpguJ3tMKBc2GZPCwUMOgf2b24zFZ49.D7MRQbZifaHsk4dJ\r\n         L9jxS2qdN5pSjhZUjbLCUQ2YGcYgmNnTbfjAIaxqUWNSgpypIYNmi.lgG4bM_gW7sXH_Y3TULcsC\r\n         .1GXTSjZUdUvvkr7BDnzUy3FGqv9Eyfb7GOwPzTXLzdurcd6eHx0ejCmC6gVJIwoIh9S0YSK659a\r\n         K2usThSAyogrxqQ664fZr70CrLJehr5OZNLstPt3fiJhyUR1DXrlm6myQ9uSQ5vPTl0p2.DemDaY\r\n         k84mtcZO0EEjKIzqeSvZ505Fex0u.66Mzu2lmr07WwMCE7wgqwOSWRnYNCz2rWcLmXA_TVDtdJ85\r\n         bHZ79FY6Vs5pGJjp.7YgDnVqysBp95w--\r\n        Received: from sonic.gate.mail.ne1.yahoo.com by sonic310.consmr.mail.gq1.yahoo.com with HTTP; Tue, 3 Mar 2020 17:56:53 +0000\r\n        Date: Tue, 3 Mar 2020 17:56:49 +0000 (UTC)\r\n        From: Peter Edward Copley <noodlelife@yahoo.com>\r\n        Reply-To: Peter Edward Copley <pshun3592@gmail.com>\r\n        Message-ID: <729348196.5391236.1583258209467@mail.yahoo.com>\r\n        Subject: Re:Hello\r\n        MIME-Version: 1.0\r\n        Content-Type: multipart/alternative; \r\n\t        boundary=\"----=_Part_5391235_1821490954.1583258209466\"\r\n        References: <729348196.5391236.1583258209467.ref@mail.yahoo.com>\r\n        X-Mailer: WebService/1.1.15302 YMailNorrin Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36\r\n        X-CM-TRANSID:T8CowABHlztmml5erAoHAQ--.23443S3\r\n        Authentication-Results: mx29; spf=pass smtp.mail=noodlelife@yahoo.com;\r\n\t         dkim=pass header.i=@yahoo.com\r\n        X-Coremail-Antispam: 1Uf129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73\r\n\t        VFW2AGmfu7bjvjm3AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxU-NtIUUUUU\r\n\r\n        ------=_Part_5391235_1821490954.1583258209466\r\n        .\r\n\r\n    */\r\n    std::vector<std::string> vecString;\r\n    std::vector<std::string> vecStringItems;\r\n    std::map<std::string, std::string> mapString;\r\n    MailHelper::splitString(m_strRecv, vecString, \"\\r\\n\");\r\n    MailHelper::analyzeTags(vecString, mapString);\r\n    std::string strTemp;\r\n    long lStart = 0, lEnd = 0;\r\n\r\n    //ʼʱ\r\n    auto iter = mapString.find(EMAIL_DATE_TAG);\t\r\n    if (iter != mapString.end())\r\n    {\r\n        //ȡʽ\"Tue, 3 Mar 2020 17:56:49 +0000 (UTC)\"еʱ\r\n        lStart = iter->second.find(\",\", 0);\r\n        strTemp = iter->second.substr(lStart + 1);\r\n        lStart = strTemp.find(\"+\", 0);\r\n        //ȥʱ\r\n        if (lStart != std::string::npos) \r\n            strTemp = strTemp.substr(0, lStart);\r\n\r\n        TimeItem timeItem;\r\n        if (MailHelper::parseTimeString(strTemp, timeItem))\r\n        {\r\n            m_pImpl->_MailList[index - 1].date = timeItem.year * 10000 + timeItem.month * 100 + timeItem.day;\r\n            m_pImpl->_MailList[index - 1].time = timeItem.hour * 10000 + timeItem.minute * 100 + timeItem.second;\r\n        }      \r\n    }\r\n\r\n    iter = mapString.find(EMAIL_SUBJECT_TAG);\r\n    if (iter != mapString.end())\r\n    {\r\n        m_pImpl->_MailList[index - 1].subject = EncodeUtil::s2ws(std::string(EMAIL_SUBJECT_TAG + iter->second));\r\n        MailHelper::analyzeString(iter->second, m_pImpl->_MailList[index - 1].title);\r\n    }\r\n\r\n    iter = mapString.find(EMAIL_FROM_TAG);\r\n    if (iter != mapString.end())\r\n    {\r\n        lStart = iter->second.find('<');\r\n        if (lStart != std::string::npos)\r\n        {\r\n            lEnd = iter->second.find('>', lStart + 1);\r\n            if (lEnd != std::string::npos)\r\n            {\r\n                strTemp = iter->second.substr(lStart + 1, lEnd - lStart - 1);\r\n                m_pImpl->_MailList[index - 1].from = strTemp;\r\n            }\r\n        }\r\n    }\r\n\r\n    iter = mapString.find(EMAIL_MESSAGE_ID_TAG);\r\n    if (iter != mapString.end())\r\n    {\r\n        strTemp = iter->second;\r\n        lStart = iter->second.find('<');\r\n        if (lStart != std::string::npos)\r\n        {\r\n            lEnd = iter->second.find('>', lStart + 1);\r\n            if (lEnd != std::string::npos)\r\n                strTemp = iter->second.substr(lStart + 1, lEnd - lStart - 1);\r\n        }\r\n        m_pImpl->_MailList[index - 1].messageID = strTemp;\r\n    }\r\n\r\n    return true;\r\n}\r\n\r\nstd::wstring Pop3Socket::getTitle(int index) const\r\n{\r\n    return m_pImpl->_MailList[index - 1].title;\r\n}\r\n\r\nstd::string Pop3Socket::getFrom(int index) const\r\n{ \r\n    return m_pImpl->_MailList[index - 1].from;\r\n}\r\n\r\nint64_t Pop3Socket::getDate(int index) const\r\n{ \r\n    return m_pImpl->_MailList[index - 1].date;\r\n}\r\n\r\nint64_t\tPop3Socket::getTime(int index) const\r\n{ \r\n    return m_pImpl->_MailList[index - 1].time;\r\n}\r\n\r\nstd::string Pop3Socket::getMessageID(int index) const\r\n{ \r\n    return m_pImpl->_MailList[index - 1].messageID;\r\n}\r\n\r\nstd::wstring Pop3Socket::getSubject(int index) const\r\n{ \r\n    return m_pImpl->_MailList[index - 1].subject;\r\n}\r\n\r\nbool Pop3Socket::getMail(int index)\r\n{\r\n    if (m_hSocket == INVALID_SOCKET) \r\n        return false;\r\n\r\n    char szCmd[32] = { 0 };\r\n    sprintf(szCmd, \"RETR %d\\r\\n\", index);\r\n    m_strSend = szCmd;\r\n    if (!request(true)) \r\n        return false;\r\n\r\n    if (!verifyRecvData()) \r\n        return false;\r\n\r\n    return MailHelper::analyzeBody(m_strRecv);\r\n}"
  },
  {
    "path": "Chapter06/code/RecvMail/Pop3Socket.h",
    "content": "/** \r\n * ȡʼߣPop3Socket.h\r\n * zhangyl 2020.04.25\r\n */\r\n\r\n#ifndef __POP3_SOCKET_H__\r\n#define __POP3_SOCKET_H__\r\n\r\n#include <vector>\r\n#include <map>\r\n#include <string>\r\n#include <memory>\r\n\r\n#include \"Platform.h\"\r\n\r\nclass Pop3Socket\r\n{\r\npublic:\r\n    Pop3Socket(void);\r\n    ~Pop3Socket(void);\r\n\r\n    bool connect(const char* pszUrl, short port = 110);\r\n    bool logon(const char* pszUser, const char* pszPassword);\r\n    bool isConnected() const { return m_hSocket; }\r\n    void quit();\r\n    void close();\r\n\r\n    bool getStat();\r\n    bool getList();\r\n    long getMailCount() const;\r\n    long getMailID(long index) const;\r\n    long getMailSize(long index) const;\r\n    bool getHeader(int index);\r\n    bool getMail(int index);\r\n    \t\r\n    std::wstring getTitle(int nMailIndex) const;\r\n    std::string getFrom(int nMailIndex) const;\r\n    int64_t\tgetDate(int nMailIndex) const;\r\n    int64_t\tgetTime(int nMailIndex) const;\r\n    std::string getMessageID(int nMailIndex) const;\r\n    std::wstring getSubject(int nMailIndex) const;\r\n\r\nprivate:\r\n    bool recvBody();\r\n    bool recvData();\r\n\r\n    bool request(bool toRecvBody);\r\n\r\n    bool verifyRecvData();\r\n    \r\n\r\nprivate:\r\n    SOCKET\t\t                m_hSocket;\r\n    std::string\t                m_strSend;\r\n    std::string\t                m_strRecv;\r\n    char\t\t                m_szBuffer[1024 * 200];\r\n    bool\t\t                m_bConnected;\r\n\r\n    struct                      Impl;\r\n    std::unique_ptr<Impl>       m_pImpl;\r\n};\r\n\r\n#endif //!__POP3_SOCKET_H__\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/RecvMail.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.29926.136\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"RecvMail\", \"RecvMail.vcxproj\", \"{458BC5EA-6E94-490D-8924-41D5EC6C2B17}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Release|x64.Build.0 = Release|x64\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{458BC5EA-6E94-490D-8924-41D5EC6C2B17}.Release|x86.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {8C8ADD6D-8AFC-4744-AD7E-33DE004CFE81}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/RecvMail.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>16.0</VCProjectVersion>\r\n    <ProjectGuid>{458BC5EA-6E94-490D-8924-41D5EC6C2B17}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>RecvMail</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;WIN32;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"Base64Util.cpp\" />\r\n    <ClCompile Include=\"EncodeUtil.cpp\" />\r\n    <ClCompile Include=\"MailHelper.cpp\" />\r\n    <ClCompile Include=\"Platform.cpp\" />\r\n    <ClCompile Include=\"Pop3Socket.cpp\" />\r\n    <ClCompile Include=\"main.cpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"Base64Util.h\" />\r\n    <ClInclude Include=\"EncodeUtil.h\" />\r\n    <ClInclude Include=\"MailHelper.h\" />\r\n    <ClInclude Include=\"Platform.h\" />\r\n    <ClInclude Include=\"Pop3Socket.h\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter06/code/RecvMail/RecvMail.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"Pop3Socket.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"main.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"Platform.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"Base64Util.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"EncodeUtil.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"MailHelper.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"Pop3Socket.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Platform.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Base64Util.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"EncodeUtil.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"MailHelper.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter06/code/RecvMail/mailcontent.txt",
    "content": "+OK 93763 octets\r\nReceived: from qq.com (unknown [183.3.226.165])\r\n\tby mx27 (Coremail) with SMTP id TcCowABHJo+dMqReI+72Bg--.18000S3;\r\n\tSat, 25 Apr 2020 20:52:45 +0800 (CST)\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s201512;\r\n\tt=1587819165; bh=RLNDml5+GusG7KQTgkjeS/Mpn1m/LmqBUaz6Nmo6ukY=;\r\n\th=From:To:Subject:Mime-Version:Date:Message-ID;\r\n\tb=K3sJK+aPQ9zHu1GUvKckofm3cfocpze10XBp9FufVVVYS423myQnFWMaREpGGbeaS\r\n\t vrCGdjawcfhXpkvGZnhOkJZrtut1er5zWZRkmsDnqvoekRURXKt3wWyOv5WUuSPHZI\r\n\t NzGjMQbtYmbWjFla7zs1Cg81UQKRtg1s5KxWwGVQ=\r\nX-QQ-FEAT: CPmoSFXLZ/TSSc3nxNJn8bUc57myjtkH8mxkmSC9/G9nP1mNDXcYVAAERmmiE\r\n\t038rlXj8w6qkTmh1317bdJp9MqMMEUSgpJC5DulJn4k6WCURo4NEYDiuUQK/J+YfUQnpETt\r\n\tw4aQYpj6nKAIqKgorGGK0zy6oQWavfOgssyvSU15d6wqlw904x6aZhS3KAUAM4+eGitBRk9\r\n\tfxUEABnV/opGuLtZ/fex+UsUAVgXFbTZPoYjhxoM4ZKJsDEJ38x/9QHR1FymBebmAvNzzbB\r\n\tJT45M4OYwynKE/mrFR1FPSeXA=\r\nX-QQ-SSF: 00010000000000F000000000000000Z\r\nX-HAS-ATTACH: no\r\nX-QQ-BUSINESS-ORIGIN: 2\r\nX-Originating-IP: 255.21.142.175\r\nX-QQ-STYLE: \r\nX-QQ-mid: webmail504t1587819163t7387219\r\nFrom: \"=?gb18030?B?1/PRp7fG?=\" <balloonwj@qq.com>\r\nTo: \"=?gb18030?B?dGVzdGZvcm15Ym9vaw==?=\" <testformybook@163.com>\r\nSubject: =?gb18030?B?suLK1NPKvP4=?=\r\nMime-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"----=_NextPart_5EA4329B_0FBAC2B8_51634C9D\"\r\nContent-Transfer-Encoding: 8Bit\r\nDate: Sat, 25 Apr 2020 20:52:43 +0800\r\nX-Priority: 3\r\nMessage-ID: <tencent_855A7727508F28D762951979338305E06B08@qq.com>\r\nX-QQ-MIME: TCMime 1.0 by Tencent\r\nX-Mailer: QQMail 2.x\r\nX-QQ-Mailer: QQMail 2.x\r\nX-QQ-SENDSIZE: 520\r\nReceived: from qq.com (unknown [127.0.0.1])\r\n\tby smtp.qq.com (ESMTP) with SMTP\r\n\tid ; Sat, 25 Apr 2020 20:52:44 +0800 (CST)\r\nFeedback-ID: webmail:qq.com:bgweb:bgweb16\r\nX-CM-TRANSID:TcCowABHJo+dMqReI+72Bg--.18000S3\r\nAuthentication-Results: mx27; spf=pass smtp.mail=balloonwj@qq.com; dki\r\n\tm=pass header.i=@qq.com\r\nX-Coremail-Antispam: 1Uf129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73\r\n\tVFW2AGmfu7bjvjm3AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxU-LIDUUUUU\r\n\r\nThis is a multi-part message in MIME format.\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_51634C9D\r\nContent-Type: multipart/alternative;\r\n\tboundary=\"----=_NextPart_5EA4329B_0FBAC2B8_71508FA9\";\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_71508FA9\r\nContent-Type: text/plain;\r\n\tcharset=\"gb18030\"\r\nContent-Transfer-Encoding: base64\r\n\r\n1eLKx9K7t+Ky4srU08q8/qOsuqzT0MG9uPa4vbz+oaM=\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_71508FA9\r\nContent-Type: text/html;\r\n\tcharset=\"gb18030\"\r\nContent-Transfer-Encoding: base64\r\n\r\nPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNo\r\nYXJzZXQ9R0IxODAzMCI+PGRpdj7V4srH0ru34rLiytTTyrz+o6y6rNPQwb249ri9vP6hozwv\r\nZGl2Pg==\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_71508FA9--\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_51634C9D\r\nContent-Type: application/octet-stream;\r\n\tcharset=\"gb18030\";\r\n\tname=\"self.jpg\"\r\nContent-Disposition: attachment; filename=\"self.jpg\"\r\nContent-Transfer-Encoding: base64\r\n\r\n/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRof\r\nHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwh\r\nMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAAR\r\nCAEXAR8DASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAA\r\nAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK\r\nFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG\r\nh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl\r\n5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA\r\nAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYk\r\nNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE\r\nhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk\r\n5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD14dKP9kUc0uMVkIQiig9aX1oAMUY5NLn5\r\nqb8uaAAcZpmP3m7LdOlPP3+tHFADcfJ707GfpTd/FO+nagkbgYOVqJzgAngUss+xCT2rj9Y8\r\nSSR/Ja/PL3fsKlysBt6hrMNggMzgEnhO5rnrjxjhz5VtIcd34rk7m5hecyX17vkPXHP60gud\r\nLcDZMSPc4FZc9y+Q6geLLsgt5aADpiq934sugMmGM59xXNXOoWMUeYnBf/rpWXLqsLocoBj3\r\nzWbcjRQR1UfjG2N2N8hicHJjkOAfxrQ/4SGG30550nIGcJyOa8cv79ZZvkTj+VRx3k0kJtJJ\r\n3SGQg884rdXsQ4ano9x48xNsScZ7bDVKXx3qETj549h7mvOp7a5tC53+YB3SmR3nGHB+pNUo\r\nX3E9D062+IpyMzoh94/610Fn47SX/nm4B6x8E14jI8ZGUxUSTyRHMUjofZ6TptbMV0fSOn+J\r\n7G8by5cJJ6P3ra+SUAwuvtXzZYeKb20wlxIZE/UCvTPBfjm2uCllNPvJP7sng/Q+9Gq3BnqE\r\nU+eN/IPQ1YR89Kz0ljuEDggHsRU0chzg1RJdopgf0/I0UwFoxjtSikoAKdimZ70/tSAZRilp\r\nBQUGOKYRT6Q0ATg0tFFMBKbzzTiOc00Z5yB1oAd1pOc9KcOgpPrQAhzmkJoc5PsKIoDcSYHR\r\nOuKdgWo6OJ3APQVL9kyO9XPK2AADAFKzBEZj0UZrVQRskjitUmM+oSWu8i0iIEhTqfbNeR/E\r\njUr3T/F19ZR/6NblI5IYk64I/wAQa9j0iE6jeGYpiHeXUHq59TXknxzhFt45t5VG557CPA+j\r\nMKmVNW1NK/ItEcB9pkyTM5yfU81HJqLp9xyDVTZIeo3n1Jpog9XBPtSUInC5DpL+d+pqP7TO\r\naCB70wnZwBVco1IXzJPUD61NFcPG4OTiquT2AqSNC/HSocTRSNmDUI3QCVQX6Zxinz2Vpdpl\r\nD5cn6VQSOBB+82Z68GpI7/BJ4xjAFTawFW7srmwcb+UPQ9jUQKOMjeD7GtaOSHyNhC4PQd81\r\nUuLN4CHjcEHp/wDrrREMpFyPxp8FwYJA8eUIPUHmkDgnDpgmmOnPHNK1wTPXfBnjycxiG9Ik\r\nIGPNP8Y969QtNRtrxP3b/VO4r5e0zUX0+6DoMjOHR+hFer+HtZhubLzrWTPln54HPIHsaytY\r\npo9ZjfPBPzjv61MH3+xrD0u/S7TYJA5AyOxx/jWoJc4z1FUQWwfXrRmo9+R1pQaAH5pcnFMz\r\n81OzQMXNMIz19acaaDmgocTTSadTe1ICzSAc/WloFMYHrRTSKQigQ/8AhppPNHQVGRvPsKAH\r\nxYlmCE4BPWtCPEU6QxL8n8ZrGuH2FCdo56+lRaF4kj1G7kstn79AXR/76A4zVQaLg+h0kkuA\r\nSe3SsPU9QLwPBFy8n7sY96m1ySdINyOoFcXqHi7TfCVjJqeqyCSc8W9uh/eSn29vetrmyVlc\r\n6u71DTvCWgy6jqcwggiHzHuT6Adya+Z/FPiS48XeJZ9XvVMe/wCSCAH/AFUY6D6+tR+LPGmq\r\n+NLtL3UHCRRuRDbR/wCriH9T71ipk/MDxUN3MJu5Y2b/AL52D0FIcIPkTHu9NwUBdzgmoJH8\r\n08ck/jTRzPcbLJz9/wDKqxc9v1qcwYH7zj2pPL4zgAGqGiMAlwM06SQRg8596jaXbwOTUByT\r\nl6hotMebg05JTvzxUOzngVIkHOTSsWW4pS7Y4q/vAhKSE59qpJHsUH5asuUkhQkcjg4qQKsi\r\nb85xkd8VEjkfIDg+/SrWwkZTnHSmyReZGHQYNUiWiD75wetWbC/u9OuRPbTOhH6/UVUc5HIw\r\nR+lCSEjaevrQ0CPX/C3i+C8jgcyeVdxn54/X1x7V6dFciREk/gccGvlq2leCYOjkEcgiva/B\r\n3iQ3em2/nYAfgHPGe4rLYpo9Gjc44qQPkVm204Dun41dB561RBYD5HNOzUAPPHSpAaQyTNAp\r\nmaBQA+ikFFAFumZIcD1pc0Z4oKAmjrSZyc9qbv44PWgBxNMcj+9n6Uxyc4HpTJZBHGXJ4HpQ\r\nBgeLdUFnYiEPskckk/3B3NRfCq28+0vNakzm4fyYc9o09PxrlPFNzNqepfZI0zJcSCEIP7g5\r\nP5mvRtN0n+wdF0226yQHDkdOetOnHqbUoXJ/FMxFjKyclENfMPjlUGq27sXNxIheTP6Yr65k\r\ntoZE+eMHPrXA+LPhtoGuakL++tZkd41j8yCTZsx7VvuVLVWR8yo4Nu6E853gfzp8U+Gwgx7m\r\nuv8AGHgC58HXMd7HN9q0uWTYJscxk9nH9a4goY3KORkHGBWbVmYtF9ESTryfVzwKkeSGNMGQ\r\nH6VQSMvjg4qb7O6J8kf500YuIkk4P+rQk+pqu++T778e1Tm2ndfnIRPpSeWiDGM1YipgdFpR\r\nGON9TyDpx+HrUL0ih4cA8Cn56etVQWJ4qZz8+BUjJXkp6SEgds1VkJz+FOiPTPrUFF8vsPmR\r\nk+4p4I++Oj9R70BN5I9famJG4BH4iqiwmuolzEJELpwRWaSQfQ1f83Dc/lVW8iwfMToashBH\r\nICPeu58B6ikbz2Ur8OQ4H8yK89STH+elaOn3j291DNG+HQ5FYyRofQ+l3bpiCV8yRcB/74rp\r\nIpMge1ec6Vq8d7YWuowvwSEkj/uPXcWcokgjcHrzSRDNQHLcVIDVZCKlBpgTA04God9PBoAm\r\nzRmo80uaQFzs1JnpTc4znil7e3rQUNOehNI4CdBwKHcAe5pkhyDn8hQA2QjgmsPVNUgghkeQ\r\ngRxcmrt7cEI6IC59SeBXnnizUJPs4tYn57gdyemaBmr8OtMfWfE0+rzgmC16E95DXp2oov2d\r\nixwBzk1m+ENLj8N+FLSGbCTMPMm9S57VptayXr+bdDy4E5WD19zW0VpY2puzuXYxmEH2qO8n\r\nRLd94BGORUsf+pBHIrA1+78q3cE4pjiryPIfip4h8nRLqxCo63RCAH+HBzkflXkUKGVEnSPe\r\nejk9jXReNbttZ8Tm3RsxWwwfTPeiws47cBNmSeCg/j+tYyYVFedjOitpJT3/ACq3/Z4ihMly\r\n/A7VsPEYISYo0x3d+iVjPHc6jdhAScDOM8AepoTuZyj2KMshdyIxgHpR9m8iPfKPnPQV1Vp4\r\nbSytH1HUD+7jG/B7+1ZEoMjfa5QE38xxegrSLMZQtsYskHljJ+/jp6VTMVX7h98hycgd6gOX\r\nPFaEFNEAJI6ChBl+lWxBgHHQfrUkFsSvHXtioaLjqZ0oyT6UiA5+lXp7cRLj0ot7cuR70rFb\r\nsvadH5s59xipLmIxPnHFW9CgLyb8Z5/lV7UbTfCSKwvZm3LdHIXIxISO9MQiRChqa7Qgc1RD\r\nkH3FbXMrEDjY5FPjdg2RS3HJDio4zg0Ad14U1QwHyCf3Mzpkeh7GvZ9Pka3xA/VB+Y9a+e9F\r\n2vNFGXx+8wD/ACr3HTrt57W3kc/v4X8iT39KzEzrEJ6jpUyHgVTgfIxU6HimSWgaUVCDT0NA\r\nEwpaYDSORwNpOe9IC/knPy0mfWnY9O1ROcY7GgoBjPNQyucegp7v1z0FZF5czS5FumSCcvQB\r\nleJNcTToPLi5kIwB71m+E9JtpprjxBrxSPT7UjHmdJJOw98fzrIkE2q3E17KX8kOQD6fSuM8\r\ne+LL29vbbSoc2mn2IBhtUPAI/jf1NNaFRPomw1MX0pv7htiZxBEf4B6n3q3c6qpT5DXztbfF\r\nfUIIAstjGQnUiTGa07P4wWkvFza3ER9RhxRzvsdP7tvc7yL4g/8ACOeLb7TdckxpM0iva3WM\r\n/ZyR8yv/ALBPQ9qh+Inii1ttFe6tp45w42wmNw4fPpivMvFXivRNb0+5MMjyTywqgQxkYIPF\r\ncTahxF5fzY9G6CjndglyRmnAt2MbyTl3bMkh3k+9dlYWYCZ6E9XNc9pluPMyPXFdHd3KWlm5\r\n/gQfP7+1YOeouXqZ+uXoi2QQ8x9dn/PQ9s10nhPQiIRPcxjMhyTjqf8ACuY8L2Emv639qmGY\r\nIj36E113ivVZrcRaBpLf6ZdD946f8skq79CbGP4j1H+3b0Wlmf8AiWwn55P+ejiuZ1S4FuXR\r\nHy+MD2Fb+pi08PaaLGE759mN/qe9cPPcGSQ7856mtYGE+yGc8D1qeOByAEQ81Y0ywe7m8yTo\r\nOtdBZ2SZMx+4gyfatOYhUzn0snLJCeS/XnoK3I9PCW5k2YQDin6Vb/bZ5LnYdhOBx2rX1OIi\r\nxSADmRwn4VLmaKFjh72NTOQg6elW7a2xCDjn/wCtUc4D3H++/b0rXEA/coPQ9abkTBalnw/Z\r\nuIOO9aV/b/u9gHvVrR7cRWKccnk1YngyD71zTZ1LY861OyIRyB3Nc5ICj5r068skkkkQjqMi\r\nvPNTtzBcOhGKcJ3diZw0uUH6VH0NSJzxUftW6MDY0DB1KNHwQex7165bSyaffxRyvmK4QOjn\r\nrkdjXjuiyeXqds3GRIPvdK9pTydQFoP4ACT6ioYHZRPl/qKuI/FYOj3BeMo5+eM7DW0DQQWA\r\naenFRA05DQBOHp+ajFPoA0OgqCX5COuO+Kkzxg9u9V5STnHHvSLI33y/L9xB2qHGyN9gwDxn\r\nvVlyBH6elVpZMI77DjHegDjNKij+wTJKRvtpHQAnjrxXjXiTY+u3T5yPMr1S51BNLj1OQzxh\r\n5SfLyeAa8huCby6Mxzgn86EUVLshLfA4qnbx7z+tT37/AD461Z0yICJ5nTgdvWrRLGhPLjzj\r\np0p1sSTUUjmSYgVesIsyD06momaQNqw/dyRoOuMJ7mm+JLt3ng0u3bL/AMZHqf8AP61Z0spH\r\ncTXcrDyrcHFUdCT7ZqL6pdPsjDk7z2HUn8K57WdzbfQ7ayntPBnhYXDqGk2jZH3kc9BVLRbe\r\nW0tLjVb+QPqF5+8mkc8Rp6Vmhj4m8SCa7/d6fZDKRn9PxPWmeONd+VNNt0EcLgFx3IHSrXYU\r\nu7Oa1PUHv72S6dy4/gz6VFpGmSandoiAkZ5PqaonMrpCg7/rXqXhbRBpelm5udifJvJNauVl\r\nYxUb6srSaclhFHaRcORlz7VX1V/s1lFp8fM9wcOR+taAPmg3sxI807/oOwrK07Oo6w96RmFD\r\nsjz7VHOaWN+wsks7KOMdao65L5ckcI4IDufyxW7ACRvP4f41xfiG736xMA+RjZ9KfMOxjxEP\r\nfwjjArag2SX0cfU4JxWDpmJdRTjOxOfxroNPT/iaIe5Q/hzSnMUIaHWaeB5P0JFSyIDk1U08\r\n7PPQ9pCatu/BrO5qkZtzHmZDjvXDeK7Py7gyBetd/c5Hzjsc1z3im0821dx1AzUxdmFr6Hmv\r\nQ5/OkkXBB9alI+cg9DUJPAHpXbE42TWuRIHHY16t4Y1ON9j7zgoA+f4DXksRxJXUeHNTNnfR\r\nFz+73/P9KiSA9j0xwL27I+47jP1xXQxPkVxuh3JEzoT8kjl4z6jNdZEcHPqKBFwGpgaroalB\r\n4pgTg0/NQjJHFPzgUgNL/lmSagfkjPQ9qmcgRnsAKqyOXkjwOAKQxZHTOSenasDX9VNva79m\r\newQfxn0rXfEhJ7D0ri/EFwI5rucnP2aMpGH6ByOPxqWB5vrM81489ze7MnKQoOnB5xWE5Ecf\r\n4ZrQ1S4+0SW9tEd4t48E+/fmsa4JeByOmcCrWw2jLfMs3PrWySINNA6DrWTF9/eO3H41ovG7\r\nw29seXc5IpiI4LchAf8AlpJ09hWtpcHmuVAOE7jvVC8lFuCgP7x+B/sJXT+FLYyoNic9s1lU\r\ndjWmjP12GTT9ChteRJcPjA7062iQeXZR5kjtwEMfaWY9j7DqfpXVeIfDk11qGjLCPM272f2N\r\na1h4XSK7jPlgJGMA9z6n8ay5vdN4x6lHStM/s7TnvbkB0jBkd5DjJ9TXkuo6hPqd/PeTPl5G\r\nJ+noBXqfxMuzaaNHZRZBuCAcegrznTLBHm8yUfu4hvI9T2FXTstTOac2afhLRHubxJ5E4Q55\r\nr0TWCPssenIRmXBk/wByqujXdjp1imcZAzj1NUJ78PJcXZI3yHgGpk+o+XojM8Q6gQEtbfIk\r\nfgD0rT0q0FvDHCjfcTH+J/E1i29o0+q+dKOU5z1rrbKNUT1c9c9qS7F8pNLL5ac9APyrzO9n\r\nEt9cEdBkZr0LVD5FlI5PQGvOUjd7Cec9X46VoKxPoCZdzjGe9dLpw/4mMZx0jIJ/GsjRoPKh\r\nDY++/wCgFa1pOILvPYx5rNu7KSsjatuNRkTs6B81fIAGS4FcxJqkiTRyRRknBHSmm8u784cP\r\nGPahbAb88kL/ALkEEkf0qvLbi5scHnKYqjFbyRkF89eDWxp4L2pBGCCRU2EeP6pbGzvpISMY\r\nPFUJPvcV1vj62WDUYJB/y0TmuOJzXZT1RzVNx8X3wa0YHKfOO1Z0X3q1bAZLgjIxg1Zn1PRf\r\nC+o5sLeTeCIptjj0zXo9vJwAeDXi/hiUxzmEZIkIyM17DZvmML1GMg+tZjNUcGpQaqRuRwfw\r\nqyj0CLAqQHioAakD0AXsmQ+w6CoJQ6DryM81aj457mobjlJO/GKQyMII0REHbJrhPGFsQI5o\r\n+SZiHQnGTjiu+z+7BPp29K5DxHbG5tZxHkEDeD7ipY0eKymaN0mxjB8t0PrWfeSBLd0BXNbe\r\nriM35MeMSDfgetc1dnJQdquIMfZwM5jGO9bsVvh5LnZnYMJVDQgJvOQL82PkPtXRpHiDAXiM\r\nY/Gsqk+Q6KcFM5O5spnm84jOeTXpfgaMfZARjNcTJJIjvhs57Gu08ASDmPoRWLm2tTZwS2PR\r\noLdNgJGX9anMQ9KfEPkp54FSQcR4r0Iardxl48iMZ/HtWLaeE0jhHyDG/eT616O6Bz0FVp/L\r\njTHGB2rRSFqcM/h55zgAgD8BTJ/DEZA3uSR3z0rd1DWbSzBMk6CsE+MNLkk2fakB9zihtFK4\r\nyDQzbsSjk5PQ1q2llMX56UtvciUAjoehrRtpMnFLTcpnPeKIzFpcwQHeRgAe9cvd6U8GhRwg\r\nfOSBg+pr0y8s47sDzADg5rLu7NNwyBhDkUyUcvZ2x8iR9mAmQgqeztIxdI78/uc1rOEij2Ae\r\n1ZOoXkOnxieUkZxGiAcufQVA+YnlijI6D5HBAq/GkJA2AetcXqOs6vHE7xWttCg5IaYPJj6Z\r\nqSz1XWLe3t7u6jL2ko/1kaZI+orT2dSOrM1VjLZndxxKR60+OMIXA71m2F/56Ah0cHoQetas\r\nXTJqL3LsecfEbH22zQf3GP61wddl8QZxJr2zPKQqP61xtdlL4Tmqbj4zg1fgnKfcrOqzGf3Z\r\nPerMjs/ByB9ULuPuJ/M4r1qwPlxoh9se1eTeC3QzIp6ydfbFer2/zwD2rMZr56VYTmqcRyQM\r\n9Ktx0ICcVIvSoQeKlB4oA0N/JwuSKDzkU9ANnHT+dB4HP5UgKs52R7x0Fc7qcog0K4m74z7m\r\nujuBlMCuE1W4+03TwRljbxHEZ7PJ9fQUijyrUU8jUpfMOD6VzVyhkncscKg//VXZ+ONPksLu\r\nO+AOycdfSuYktvtCRjP+sOcj0/zmmh7k1pIdPtPOTG9+R7Cur02WHUbFLiEEoeJE7g1x1++b\r\nQkeuE+gpvh/XJdEvRKF8yGTiSP1HqPelUp86Kp1OQ6y50efzDNbjzEzXR6FAdPuLCcjYJQY5\r\nB6HPFauiPY6pYpdWUgeJ/TqD6EdquXOmB4y8fHfHv61yyWljo57nVwHMY+lK4JFU9KuPPtIz\r\n3xz9a0tmRSEZNxceUDkGuY1W9vZI3+zW00uOiJxn8a7p7SN/vgGopIIduMYHtWkUuoJniXiX\r\nQruytbPVdXzPbySYktYH2BOOBn+tcvcXFjI5EWkQwJ/10JIr6EvdKW8spLSYQz28vWKTp/8A\r\nWrkI/hpolpdefIWMaHIjkkyBXanFLRnK4Sb1MnwpYXNhodjcyk+RcEgo5+5zwa6+O0eOfBHN\r\nTPJYoI4ynnhPuJ0Ax0rUs4HuG8yQcvXPUs37ptT54r3ip5B2VmXkfWuvns9ifd7Vi3ltms+U\r\ntHMG28yQVjeIfDEmqQyGP5Jrc/uN/Af1rsHs8HpUcjzQDgnHpVw913FNc2iPGIvDWryXXkfY\r\nnQk8u/AH416bbWcNpp1rp0J80RIASOcmtP7bn78KH8KXz9/EaBB7CqlVVtCY0WndkFnpkEH3\r\nI0B9qsyIIxVmKPAFUNduRZaRd3B48uEn9Kw3NDxLxDeG+169nzkGQgfQcVlU5iSST1NIBk13\r\nJWONigVJGMqaUR/uzUsI2Rc9X4oFc6PwtePZSF9m+E8EV6ro2owXcWIZMnuh6ivJ/DbmOdHw\r\nChOwivQY7TynSa3Oxxyj+nsfasupZ3EQ44q3GazNPuRcQJMO4/KtJDyOaZJYBqTNRIeBUooA\r\n1Y+gPt37UP04psY4IznnvT8MRjBpAVZz8npxjJrhNDjSWCSyl/4+oS6SI/UHJ598+tdzcx53\r\ngdOuK5rUNCtp5hPL+7uuqXEZwR+NIo57x5ZfafD+xEBIG/GOmK8rgwlvjsmU/wDr16TrNzqA\r\nSS1uZhcQHKJIAAQD1J/CuCvLfyg8yDCEkAY4Iz2pIfQy72MfZTs5GCKwmQgEH8K3Jxw4PIPN\r\nZ9xbZGQ/5mtEyWiXRPEGoaBd/aLGfYT9+NhlH+or1HRfijpt3Ds1GP7JOO45Rvxrx3ympDGR\r\n16+lKdNT3GptH0N4W8Q2Oqz3X2GTdGH/ALtdpGeK+cPh1qp07xRDG74iuf3Z+vavoq0k8yMG\r\nuWpDkdjpi+ZXLgTNQTwMRxVpOaeUzSQHN3MU4zjNZUltcznHJrs3t955FM+yInYVdh89jnLD\r\nR/LcPIuTXT2kCRoAKhOE9Kkjl96ZDbZZnAcdKyLuIc8VrAgpWbeAc1YjGlAqu8QkHTNSXL7P\r\nrSWkm8kVBZUfTA/SpI7IRjkVsxxjApkqDBpWC7MwjHArjPiJd/Z/DTxq3zTyCP8ADvXbyV5X\r\n8Ub35rG1B9ZCP0H9aIK80TPRHnMn3zSxDL0w9ali4GTXYcw5z2A61MEOQnfvREn/AC0PHpUo\r\nP8I6mgg2dI42R7sF3Gz869OtgPIfJyRxn1ry3TCUn74j+fjtivSXn8uyTBwZx8nuT3rJs0sd\r\nBoG77I/pk1ux1j6VH5FqidCBzWtH0oGWkNSD7tQR9Km/hpgbGCD680E08dVqM8E1JJBIcPwM\r\nk8VhXrvcz+TF9xOrnoK3pANnPX1rAeVYpwXBP7vGMfx5qRoyp9LCPsQAvJnJccEfTsK5a80O\r\nTTtS2W5BtJn5hnGRv+vau+uDiEOXQFDkZOM+tcr4p1S1NkDHJvJkA/dAnFBRwPiHS/IdC9s1\r\nuTx1BH5isNLdZU2PwQMH2Nb2uahdXfNzD5fljYADwff61zhkf7RkHAHWriwM+5tDGelV4jsk\r\nG/kdK3ZZUcHIDj+dZ7paF8mMoPatCCqwa1ulkhbDqQ6e1fS/hbUk1LRLS9XpNGH+h7/rXzhJ\r\nHBMgaOQgg9SK9b+FmomPTpdOlP8Aqn3x+4PX9f51hW1RrRPWY3FWAeKzIpcgVaSWsImxbNVp\r\nHxQZPeoJJBWlxFS5kwKm07E6ZPNVJyHNQR3ElkX8tC4PYUPuNG3I4jBQVmXcvB5rnbjxBqn2\r\n0+ZY4g9Qearz6y8oOyOQ44rPnK5DTkxLJt61HpwO+T2ese3l1AyFzgIe2OlbFnmMc9a0hqKe\r\nmhsg4FVp5eDTTcAD71Up7gEmkwihlzIMGvEfHd8LzxNMFbKQqIx/WvWtW1GOxsJ7qU4WNC/+\r\n9XglzO9zcSTyHLyOWP41pRjrczraKxDUg6j0FRDrUsQJeug5y6B0/KhD85+nFGdgz+VImHJ2\r\nDr+lJiSLdpevZ3SOOhBBz0Oa7rw1cfabuIXL5+zR/u09a80ckOB/Ou38JWBv7CWRZCk0DjH5\r\nVjNGkWerW53oHTntV+Ak9Ntclp95PFMkcpAlPAz0k/8Ar109vOJVDYw44oiyWjRSpBUCVODx\r\nVjNvoPamgfxE9sU7tzSnAcjr7VJJC49BWZdxAXAIGRIcGtZ/piqtzGJBjuOR7UDRhT28Ftdf\r\nvI/MD8oTzg+lY3iuOzTTpUlIQSp9wdn7Gn+LYtT2JNZFyj/fjzjBHQg1yZ07U9btHkmkhgh5\r\nD5c5BHqKy8i0upxF/O4MiSHpwKyXkzMgGfU+9WtXi8i6ePzDJsON78Z/Cs7zEj759TW0EDZP\r\nJJgHNUPNd3HfnpTp5TI5ATjPempiMe/rWhmTgEuiAH0r0Lwvbvo7wTofnI3kZ7d65bw9pU+p\r\n3qeVHnHJJr0200tzAJnjKSAYwfSjluaR0OxtLsSxoUbgjNXknI71xEWrjSCBejZaO+PMHPln\r\n3rpEnDjKPkHkEdDXHOHIzqsahuEHeopLsVmvPgdazLu9kA+Q1Nx8hqS3gz1p1tP5hzmuPe9v\r\npJiEgDp678GtK31VLSMeZBOD34zVp3JcDpbgI8ZOKz7a3CWUjlMfPmsx/FkO8IkZTPeQVFqH\r\nify4xDEE5HQc0y/ZTLjygGkF4gBrATU7m5OyOHn36CnG0necTSTOSB0HAod0LktubP2sueGz\r\nRvqrE4AFRSym4R44ZAHBwTjpThBzG7I4r4h6lcNFDZRowt2O9m9T2Fedkc17dqOkC+s3t7nY\r\nwI+XI5JrzDXPDVzo8+SN8B6P6V1xp2WhxVHdmCg5qeIEvtFPEQHUYNOiGyTPWpETCJycOMem\r\nabbo8U+R0IPJHStmwEFxf77sF8dB+FUblwb0uhBjjOAR3qE9bDa7FOSJfMycjHSu48DO9pJj\r\nrDKcSD0PY1zmlWz3c8wKb4Y/nGeldFocotHjEw8oSDeHI4JzSm+gJHoN3Zie3cjAcDKH3q5p\r\n0hkRCfvkDNUUuCbXzOCMZ+Q5zVrRPntc/hUdQ6G8n3amFQRnIqUGtEI3UJ2Df1xzR3yRQOhz\r\n2NOfmpJIZHOOAB71mvP+9d952YCH2NabuOBWZe2xGZoRk4wU9akorkPcmQAbx064rkr+z1Cz\r\n1Sf7LbYE43lPMxh+hraSQiZxHdC3lD9HHBqK81O8t+ZYLOc9n8wp/SkUeTeKEnnungvf+PhD\r\nwnl5OP7+a5Ke0MD4Mn4Ec16X4hvXubi4nuJrOLKABADI4Hsa4S7iW4JeMnYOrueTWkAZkeWX\r\nJOeBUeMvgc1cu0jgjABJJ6mrGgWX23WbWDGQ8gz9K1RFtbHrHgbw/wDZtGS6dHffzsTqa7k6\r\nY7xo4cpGRzE4+5Wt4esEs9OQAYAHAqW4sxbwybM/OSTnua6Xa1khXfNqeU+I4C8ctsU++SM+\r\n/anaFcSCyi2PlCg47Vq3sRM8xMezJzzyKwdCBiEtq/WCR0/WuGpE69UjoZJ3x0/Gqpt5Jz14\r\nq8iAp60IDGfauS5qphaWYj4NW5I4wnKA02N0PfmrIQEVaJZz95bwbjiE/TFUBp8ZOfLx9BXU\r\nypHj7tVpCgT0qrsr2jMqKJIkwiYqK+vrewtJLm4kEcUY5P8AQVNe3cFpCZJn2D07mvONZu7n\r\nxDq1vacpBv8AkjHb1J96qOon3Ow0rUDqLpNL+6EgykfoO3410PkCIcICT6VU0rQ4RHHJJGN4\r\n5B9K6Dy/s6eZ5JkfPRO9dlrQ0OWcvfuZUGnzOP3p8zZgoPes3U7IXtvJDNB8h4INdlaQDY7u\r\ncH+VUbu089LjOM5+T6VrG1rENtu7PJrbwoJxIC+xASB7msjVNPjsrQ4QiSNzGTmvVItDmkLu\r\nSdm/IAHQ1geI/CE9zA9zvCOT1I6io5L3Y1ueZ3EoeGMDmU+nUVfTSLuOy3+S5B6nHStNPCeo\r\n293Akk6pP1SJOXA9T6Vsiy13S5D51zIiOcjMYdPy61ix2sYml6j9jtTaPayR/wB98ZIH0ro9\r\nMkglhSOImOMH/WlMkir1hFHqs/2W9sxFeJyhjP3x6of6Vq2+j/ZJ/MjkcZ7jj8x3rFpoZbsh\r\naRwRQxuNg4GDnNWI7MwPm3d0PUYPWkjgwwLgfUCtCNAUHfHSmSPt55gg389/etFGyN3rVKAB\r\n0PqDVmE8FfSgR0o5yPWmbw4JHal6GkCKg4GKAIZ4zIPkOCOQaoySXUQAdAfcVpYPUmqF3qth\r\nZ3kNpc3KJcXH+qVs8/j0H49ahtLc0hTnUfLBNvyMi8iS8z9ojQ/9szmvPvFIeMpaadbOjycl\r\n3zwPavW5J7OK4ggnuI4Zp/liErgBj6D8x+dVNY8Pwo4u5gvmBCS+fQVUYuQ4pqzktHseAS2F\r\nzbYN5v8An6Ac1n3NhNIm8W0yIf45OBXt0Xg7fbmbBku5OST0jz2FYN/4Au5nHm73ctgGQ9D/\r\nALIrdQYnaUjyH7ETwBvPQV6X8OPCc32oXcgHXl+o/Cug0v4cTQsGuYdpG7hmBJWvRdE0qKxs\r\n4FiCCKLgrj8quKa3IuuhObMPawQvOcI4346n0FXNQghNlJ5khjROrg8io7bUtObWbjTY7tPt\r\n+0PJHn5wD6fzx9K0UtwIRBL+99Scc1aqX0QVKbg+aatfU4HV9MFtMfO4iPIPpXGTwNaa35gx\r\nsuUB46ZHBr2rULKO7jMcmSCOwryWZft9zeQWkTNFYyHbLk7XI6jPXP8AT6isanRPqb0Kc6kJ\r\nSS+Hfy6FyAjqKsbM1QkvIbOxa6kJ2oOg6k9hWjZyfbLSOfy3j3jOyQYIrkaXNy9TVUqnsvbW\r\n929r+ZC8Q+lQmSaPo/StF46iaEbSSOBU8grmVJczE8iqU93c4Pl4HvVuC+tLu3WZI7oK2cf6\r\nO7d8dVBH60y5uLSGJ5HjuAqjJzbuP1IxU+0Vr3Op4Oup+zcHfbY5y7tJJSZJSXc9zV7wp4bB\r\nmk1OYAfwRk+nc1rWtg2rRxNBFIiSDO91xtXua9AtvDVsLWJHKG3CfIgTn6100Ipu5y1ualJw\r\nkrNGClvJH8kIEkx5CHvWlHpe9ENxkEHOwP8AzNXbu+0/Rj5JV/M24zHA8jY+qg1jSeJ7ZJZi\r\n6XfkAAofsso5/Fa6+eC91tIyWFrzXtIQbXkmXbwmKGeaKMeYR2Gfb8apm3jlR/MGTH85GMds\r\n1z1t4hu9Svr+eOWcWKlUt40tWcFuMncqkjpnB/ve1XLrXEGnSReRdvMwAIS2kBYdycgCojWp\r\nyhz8x2VsrxVOoqXI29Nk7K/Ru1rrr08zSsDGIBkdeasT6fbXLpNJG+8DZgHgj6VFZWInS2nz\r\nKF2hgh+X8SD0NbN9cx2NhNeXQWG2gXcctnPoB6EngfWtpyikn0POhGp7Tkitb2t5mBZaVH9q\r\nkuo7XyzIfvkcmr1/o8dzbvkF+O470vhy31hrZb7VpYoGmffFAy/PGn8IPbPtjI788Cv4r125\r\nsri0sNOMT39y+FibkBf7x/l+fpWUq8VHn5dPxOtYGrPEfV6ck2t2nppu79l3Mq08OG5hiO4R\r\nXUByHFXb+yz++Qr5yf65E9fWujstIlsUEk0qvL5YEgXO3djnGecZrFnu/Lu0Dpl5ZMHFXUip\r\nK0Tz1J6mUEyOelSxjGKra7eQ6VD9oxlZjiMEcE+/tVPUY7q102a7/tKVZYl3AbECZz0xjPsM\r\nk1585qEn1selh8BOqoOT5VN2je+r+Sfc3UGAakUYPyniqOk3Ml5pkFxKmx5EyR/X6Hr+NaCh\r\ndxxWkXzJNHJVpypVJU5bptfcdF70maOoqK4uIrWB5pmwijnAyT7ADkn2pN2IjFyaUdWyDVNT\r\nt9I06W9umIjjHQdWPYD3Nc/pun/Zmn8Ta8QtyQWVX5FtH2UD+9jj1/EmsrxFb3uta1pdtekx\r\nLczZitA3+riUZZnP98jPA4GMZrpL+SxsZrObXHVLMShLOyjUv5jL/G4AyQMjCjjpnJOBzc3P\r\nJt7Lv37s+jp4VYejCnCV51LuXLq+VfZj6tO7203stc7R4I9V17+3dbtL8InFnbf2dNIqKOjH\r\nahBJ69+v0xr6v4usbTz59Rs7wwDakUb2kkfmH0y6gDjJ59KNUvJ9U8e2dhFeTWltp9u13dmC\r\nYruyQQjY9gvXsxrpE07Tr7UINbU/anEQFuxbckanncg6AnjnrXRS5lGUIWvff83uY4qVGUqV\r\nTERfLy3UU9Eukduu7e/XVle21OK+S28qyniR4g+JEwVyOhHrWF4p0jUtQ0S5trMSTzOyvGoI\r\nVVIcHcCxGOM11Goido/JtrqK3uZCGXzIvM4xycZH8643SL7V7/x5cQTawj2WnjymWGHakzsO\r\nQRk9CDyScY46mt6so8ipW30OTL4SUpYqnJLk96zu9mrLRdXZbo2G1i00e0sLPW7+Nbx41ikd\r\nh8rMANxJ9Pc1x2jaw/hqK9s4NQsJobiZ1soDdI6QjJwzNnhTwcZ7eprQ1LUbGf4mobu5igtt\r\nNhwPNkVAZWHJ564BH4rUeu+I7O31a3v7W+tpjb5fbHKrbuORwe9ZSlGV5KVuW6XfbXqd9OnK\r\njGNP2Tm6qUpLZXu3FLR20av620N3wZLodvcXwiv47/VuJL29YfK2eu1um0e1dTcTyYSSExiP\r\nqXcdBXOeAbL7L4TjuLkeZPqDtdSH+8WPH6AfnWrqmo2sOhX16XLwwRs3y92A6fjV0Fakm9Op\r\n5+YJVMdKnFuTvbXq9tNFpfZdjCk1DWvEus63pemXVtbWFsogNyUZnLkfNtIIGR830wPWsVIN\r\nV0vxPa+H7aXThawQC5mKxsipFkhhyzc/xZ7k8nrV/wAD3V3pfh5JJdC1KeS6ka6kmQRbZN3Q\r\njc4IG3Haq/hvSU8WXeq+Ir5WFvd3OIIiAAyoMKG5OR0BHQ7fSudSlLlavzP8t/8AI9iUadB1\r\nYzUfZQSS0TvLa/r8TVzLm0+bxDqaXlnawLp0bFreOWRojMe0hUKfl9AcfqRRqEms2GrWWlra\r\n2f2u5y4RZmbCDOc/KMdDzz0Nb3hjV7fxBp41SeLybqBvJkEYwpOMjb7YNJ4WnXWPFWs+IpZM\r\nwRYs7Vs54GCxH14P/AjVTjFxi4S1l/T/AMjH3oSqKvTXLRTSWr1ei62d3q3bWxD8u/B4Poaz\r\nfEl19k0Sby8mWbEMYHUluOPfGa1db1m2nu2WxsjPz880bKOf+BEVmixGp6hC95pl1dQWR8yS\r\n3jubc4yOC6eZ+QOM/SirH3XFbs4cugo1Y1q1uWOr1XTZb9XZai6boeqrawWdpLbOka45tmwP\r\nUn5+uaqeKdIu7WfTNLvLyF/tsuZFt4dvlxqRuOSx9c9O34HdFzod7cXllY+Dle9t1UuptrUK\r\nmRkc78H8KjfSoUun1G28Jzrd+TsjjV7eKJevOFfqc4JwalUoyjyrb57deh3U68qVf21VpS1a\r\nuoK7a0d+ba7vc6+3trGSxCRQ7YmU47FaZcXD3DzwAGPYgw49/SqfhyXVJbFItVsYrS7DEFI2\r\nBDjsRgnHp17Vo3CeXIRjJxkZr0FON1yo+eqxdOThJptdU7r7zlNXnGjWd5qG95G2sQrHAyT3\r\nPucDHvVHT7W8v/C8Vxfa1dGa4jLNDFDGMKemCUznGOR+FQePrlPKtNMadYBezgyykZCovr+J\r\nB/DrU0/imSCxUr4h02ZgMLAtrxj3Pm1x1J/vXrovO2/zR9BhqEvqUKij70m3flbslovsytrf\r\ntsinqDnw14Y/0PWbiJlwkFuYImDEnJy2zOMZPWtXw9pFyIFnv7l7q5dAGeQk5Hpk9R71yYub\r\njxH4ksori5juUQ+fKY0CgY6L1PGeP+BV6TZWU8cMYt28uJOME8GnhuZyco7LRa/8FmWcS9lR\r\np0p255Nyk7JPsvsxfd6rqAW208KHIAbPRuQK5rw/p517TLjVL57+cXV4z21uly6KiqeDhSPm\r\nyP0FXvGrywaC0CLvubt1tYVx95m6j8s1JMNZ8L+GGdbzSxDY24Vc2rguQMAZ8zGSfbqelVXa\r\ndW0ldRWpGDpuGF/dS5Z1JJLe9lvay6tpfI5yOWNfGF/crcX15HZSG3sbZ7hpDNLtwy5bJ25G\r\nSeg4Paui03wxcC6m1K91OZNTuCBKYUjKKM52rvRiAOPriua0hV0/w1qBlkxetayTGUFvMDsm\r\nRk9jwPxFbWk6zqbeFItQv4ztijaZnK48xUBxnPdgR9c1lQgr++9bc3od2YTqNfuHpdU9Uruy\r\n0t5N3b6tu/pHIdSvvG9xawareTWekxhpC/lqHc4ypCKARjPUZ+U8itSzjMsKTSJ85Peq/hC2\r\ne28JTXs7D7bqTvdSZ64P3fz5P/Aqu2/Cc/kK3w6lyc3V6/18jyM2qQVb2UbWguXRJXa3end3\r\nKN9Z2728086wNCn70SNklGHfnpXKwQTa8y3F2DFYBt0MH8Uv+03t7f8A6zteJdc0mxK6VdCY\r\nxzJmRIV+6Ce5yP0qxPEIpgECgHoFrKqo1JvXRb/8E0hUrYTDxquLUpX5W+i6uPm779FtuCgK\r\nAAAAOABUydKhQ1IDxVHknR9qaVBYZAOORntRnOaqapcy2el3M8MLyzIh8tEQsWboOBz1xUt2\r\nV2VRhKc1CO70Od0+5tp/FWr63ezCKx09BapKRxkHLY9TnIHruFaumWMkuqDxNrUbwIuEsLR8\r\nfuIz/E3+0evtn6Ym8J+EYbTSdOk1ONpLpGa4MMh+RZW/iI/iYDAyc47Yqp4r1DxDeyy6Db6N\r\nGftZMa3iSlkVTwd2VG049+/Gaypx5Ic9RedvPzPo6ko1q7oYaSSSUXJtK0Fo7X3vq299bLS9\r\n5fALf2g+ra5OD5moTkqG7RLwo/mPwrI8EX19by6npVsC9tY3DBCedqkt/Vc/ia7WysbfQtNs\r\n4FOFjiWEv64HJ/GoYILLS7mS1srcx+a7TSsBwSTkktXRSo8jg09r387/APBOKvmNOarrlupu\r\nPL5KOi/8l0+ZYuNSW00O51G5VDLaws0mwdSBwPxPFcd4M1FLDT0hlR5dSvpDdORznf0LH+EY\r\n/M5xWn4w06bUtLazt7pbeKSVHnLqWLIMnCgAlmzggDrisa3tRGsWkaNbyabCTiS9mOLiU8gl\r\nV/hJ45bBAPAGKUnOVVOK2X5/1uzbCxw/1FqcrOcteiSW1+r1d7Ru3psF5pemap4rmitIY10u\r\nwka91W4J3CSXk7cnPA5yM45bvWNpNhFqUUt5dW64v5nnMfTA3ZA+mc/nXaan4cudP8C3Ol+H\r\n4VZpFCuuQJHyRvJJ4JIyPp06CuSml1t7drK00P7M0cIgDG5VtgAxk+pxWCgozvKN9L6K6u+n\r\n9dz0KeJliMPy0alkmldySaS1cnd31b6XsopHeaTc/ZIILdYSibd6xk5wv+yazPG80OoQaV4f\r\ngxGdQux5uBykakFmz+R/A1v2CztpaSLbosm0KkbNwB61hah4Vj1LXJNUv3iuYUiEMFvtOF9W\r\nbnBOSe3p6V114TcOSK3PCy+rRp4l1qkvhu11bfT8dfkXfG+sJp3hS5itpP3syiCARnBO7jAI\r\n9Bk/hTtOubHQPD9rpwvoFEEYVyhHLHlj+JJNcTqnhWD+07ZreEQqpY4RBhiOnPtirtt4REyS\r\nh7iVRIu3htuz6Gslz87k15I2qSw0cPClTm2m3KTa67Lq9ld79SPUfE2jaBpE2naFG0L4crsB\r\n4dhjO4kkkf0rH0+W8tLKGyjd0gRCzFT1bOT+pq7a+D7mO++0apdxypAfkVFwPYt/h+tdMmjs\r\nbXzLVI5Sx+XLbQw9zg4/KinBt87VktEjbGVqUaaw8J88pPmlLo3stX21+85uW4TTNPWTYsrS\r\nuPLji58yToABVzwrDFZ2V/e3SK2rRMxvWlI3QqOQAOgUgA5HB/AY2NP8PpY3izXbie9Kko+z\r\naIwf4UHOBjjPU9zSa34X07WY/OuBLBPEpHnwsNzDBGD6jmiVKpfnttsv66mlGthlTeGbaUt5\r\nLy6W0938b2dtLHJaNqGq6Qn9pL5UEGquZCxtzIyAMQAPmXjHb0rqdKvLzU9Riuj4kiNtBkfZ\r\nYrcRrJn1+YkdvyrE1Hw54li0eK3XU0mtbVFdFltlUKuOzAEnHvVXRtMvkuiL+/VVKfKsESks\r\nPcleB9P0qcPTlpHkk/np+ZeNqU6kp1lWp21suXW2yWsL3tp+p3koI1GK6FzGiY5G8CoNR1Nv\r\ntHmQa9YWkeOEaEytn1yrCuNu9Ce9uTHHI+9H5Qkjj1FbOneCY5XH9oSO6nIZIvkDD3I5P549\r\nq7Ks5zXKo7fI8fD06NP95zpX8ub7k9PvsczqeqXL+IJLy3vzf3xiMCpFZn5U74AYkHr3zz6V\r\nux6PrFtZfaY/KkXAISO1zIy464Mg59s11tnoyaXMVhjggtCciOKMDPs3qfetGKNhP/ALfogH\r\nU/4VlHDON25b9m/8zqr5vGaio01aOl5KOq7WS0Xp97PPfDtlBf6nd3Ntrdq+ozDbNbXenFXi\r\nA6gDzB0wAcZ6c11OhaRqWlLqF1e6pPfySKNsLRbNpGegBIGc9sdKdqnhfS9T1i01pzJBd27B\r\npDEwHmhem7j8M+nHpjbgvIZWdInDkdQKKGHcbuS221f5XFjsxjVt7N6SSTTjG6tsk0lpppa3\r\nmcJqWqxX3jrTYbneiaZEZ5FSJ58ynGPlUHp8pzSeMtetNZax0S1aZRcTiS5EkLxful5x8wB5\r\nwen92urttMt9Nmv7myX/AEq7YvJJKxbc3OB7AZOAK5NtNMUv2y5tlS6EHkjc+7B3Z/4DWbo1\r\nLNNrV6+n/DHVTxmF54VIxf7uNo6q3Nq72t/M29+iNO1+yxTW9xPPDBIzFEZ3Clj6DPU+1VPG\r\nd4moNY+G7aVTJdzKbkowPlxggkn07H8PpWhpVhJqmmTWmo2SmGYbNjNuP+97H3q74f8ACOle\r\nHxK1pC0k5ABmmO58ZzjsB+A7DNaVIzqLkj8L3OPCYnD0X7eV3UjsraX6Nvy7dS1e2sVpZukA\r\n2L5aRoFOAiAYAFZAdII98hAA7mrV3Peusv2kKkefkzVaO3jlgMMx3h/WulQ5ZanlczkrlaXR\r\nNOv76LUzBHJOmMOctnHTjOD+VLeQZRzsw4OfrV9EFpD5MQwE9TVSMzu7vMU2FACPQ1EqSvLl\r\nRpKvVmkpybS0V3svIzEODxU4qOVPKmx1B5B9RSg5rlEO/wCE88M/9BVf+/Ev/wARUtr448Lt\r\ncjfqy7Rz/qJf/iKKKFuIlj+JHh+S7mjfVEUbiVZYJei9P4O9WIviH4WuY90mpIDnco8iXg/9\r\n8UUV0F1CObx34VuFlEmtb0k+6v2eUY/8cqKbx/4Xaxy+rDcg4Agl/wDiKKKaCOxX07x34Uhz\r\nP/aCoXTJPkyk/T7lOk8deEty3A1Fd7fdb7NJkZ/4BRRTUnHYPtFqb4geH2sAlvrSLIOebeX/\r\nAOIrKtfG3hmK4ZzqCNJL0zBJ8x9/koop8zsTHdmlL8R/DMhlt/7ZVMp8uLaXj8dn9Kj07xx4\r\nW0yymP8AbJmO7ew8iXj6ZSiihSfKKcVcim8b+Fp3SRdSG1hnBgk4/wDHKup4z8HywpG+pIyp\r\n8wBgl6/98UUVD3K6DZfGnhGSOQf2ohJ9beX/AOIp9v428IW9rHGmpqijnAgl/wDiKKKoz+2T\r\nP478ISoEfVlYA8Zt5f8A4iq1z428I8EaiHdDkI0MuD/45RRS5maqTI7rx54XuLBi+rqu+I5X\r\n7PKcH/viqWmeMPCTWu+fUkZucfuJf/iKKKpSa2JlFWJLTxt4V3nOqryf+eEvH/jlX4PHfhIS\r\nF11n5mOMG3lwMf8AAKKKmOxDKeq+PPDtxPA0OuBIYz+8T7PL8/8A45Uj/ETwzDCWOqqV64EE\r\nv/xFFFO5q3eIknj7wy8AH9qrsccjyJeP/HKhsvGHhWyT93qwyflX9xLwP++KKKaqS5TFxVwv\r\nPiBoLRMbTUonkzgBoJQPr9ysXWvGGg30kVumpqoLh2byZP8A4iiikpNS0GtjpNM8c+FraHB1\r\nf/yBL/8AEVej+InhUf8AMWXGf+feX/4iiihslmVrPj3wzLGETVlPPeCX/wCIrMl8YeH5rdBD\r\nrCLInIzBL/8AEUUU7msNyRPGugi2/eaohbG5R5EnHt9ym3vjPw3JZAHUtp2hvlhk6/8AfFFF\r\nRzNNWJfvT1Kz+LvDctsBHqW0p0Bik/8AiKrr4v0HH/H+P+/Mn+FFFZz3Gf/Z\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_51634C9D\r\nContent-Type: application/octet-stream;\r\n\tcharset=\"gb18030\";\r\n\tname=\"test.docx\"\r\nContent-Disposition: attachment; filename=\"test.docx\"\r\nContent-Transfer-Encoding: base64\r\n\r\nUEsDBBQABgAIAAAAIQCshlBXjgEAAMAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo\r\noAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0lMtOwzAQRfdI/EPkLUrcskAINe0CyhIq\r\nUcTaOJPWIn7I477+nnEfUVu1TaGwiZTY994zE3s6vbmukil4VNbkrJ21WAJG2kKZUc7eh8/p\r\nPUswCFOIyhrI2QKQ9brXV53hwgEmpDaYs3EI7oFzlGPQAjPrwNBKab0WgV79iDshv8QI+G2r\r\ndcelNQFMSEP0YN3OE5RiUoWkP6fPKxJnRix5XO2LUTlTOurjd35Q4aHCPYlwrlJSBKqNT02x\r\nx5WumTJSLvfgWDm8IfAjCXFll2k7YK17pWZ6VUAyED68CE3kfGZ9wQsrJ5qqzk7bHOC0Zakk\r\n1Pro5ryVgEh/SVdZvaKFMhv+oxwYFhXg31OsfM+M/1Bh3C9LkHQ8mvuhMY1FZ6uILW1zGoRA\r\nTTonZPfQpk1Nx7VzI8IMPt/+jWLLvBGkpNs0FJ8VnNHxHzajtm6ECDQhgC+f7Ys5ljanIulm\r\nDLx1SBPH/6LszYCI6pSunAMfFNQj4tAVqxNpNFxcH8R5WEBxIJsv52/3GwAA//8DAFBLAwQU\r\nAAYACAAAACEAHpEat/MAAABOAgAACwAIAl9yZWxzLy5yZWxzIKIEAiigAAIAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAIyS20oDQQyG7wXfYch9N9sKItLZ3kihdyLrA4SZ7AF3Dsyk2r69\r\noyC6UNte5vTny0/Wm4Ob1DunPAavYVnVoNibYEffa3htt4sHUFnIW5qCZw1HzrBpbm/WLzyR\r\nlKE8jDGrouKzhkEkPiJmM7CjXIXIvlS6kBxJCVOPkcwb9Yyrur7H9FcDmpmm2lkNaWfvQLXH\r\nWDZf1g5dNxp+Cmbv2MuJFcgHYW/ZLmIqbEnGco1qKfUsGmwwzyWdkWKsCjbgaaLV9UT/X4uO\r\nhSwJoQmJz/N8dZwDWl4PdNmiecevOx8hWSwWfXv7Q4OzL2g+AQAA//8DAFBLAwQUAAYACAAA\r\nACEANgQqKDoBAAA9BAAAHAAIAXdvcmQvX3JlbHMvZG9jdW1lbnQueG1sLnJlbHMgogQBKKAA\r\nAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsk81OwzAQhO9IvEPk\r\nO3FToEWoaS+A1CsUcXaddWIR25F3+cnbY1KVpjQJHHKx5LU883nWu1h9mjJ6B4/a2ZQl8YRF\r\nYKXLtM1T9rx5uLhhEZKwmSidhZTVgGy1PD9bPEIpKFzCQlcYBRWLKSuIqlvOURZgBMauAhtO\r\nlPNGUNj6nFdCvooc+HQymXHf1mDLI81onaXMr7NLFm3qKjj/re2U0hLunHwzYKnDgiMQhZdh\r\n0BQ+B0rZvhIHTsa7EeZjIlCIBg7+zZY3azLEMO1hMFp6h05RLJ3huwS+Xz4/Dpcj1SXgi6bi\r\nXimQ1I7g99EQR9LD0dHqf7SjcT6EsYMcsp+Naa+cpY3Ylq12/JSGIK7HhNAmjMMhAgOZFrwp\r\nJnFl874/eTUmwwdsn04mo1Xch8GPhn75BQAA//8DAFBLAwQUAAYACAAAACEA5TdWixcEAAD1\r\nCgAAEQAAAHdvcmQvZG9jdW1lbnQueG1spFbNbttGEL4X6DsQvNsiJdqSFpYCx7YMA24hxOm5\r\nWC2X4kLk7mJ3Jdo95VYUOffUh8gpQE59mvTnMTqzpCi6MpxEPkgccme++eaXPHt1XxbBhhsr\r\nlJyE8XEUBlwylQq5nIQ/vZ0djcLAOipTWijJJ+EDt+Gr6fffnVUkVWxdcukCgJCWVJpNwtw5\r\nTXo9y3JeUntcCmaUVZk7ZqrsqSwTjPcqZdJeP4ojL2mjGLcW/F1QuaE2bODKfTSluQRfmTIl\r\ndfZYmWWvpGa11keArqkTC1EI9wDY0ekWRk3CtZGkIXTUEkITUhNqLlsLsxfFE35ry8smA95j\r\nz/ACOChpc6F3YRyKBiHmW0qb54LYlMVWr9JxsuevDflranBpaAWl2AHuwT2RjLQ2Kos6D1jf\r\nXVX/jxhHzwXTVAQhWg5fQ+Gxzy2TkgrZwhyWmm5yYSJe0t/XRq11S0eLl6HdyFWLhYP5Dcyi\r\nUz953dDsNwHsje5dTjUPg5KRm6VUhi4KYFTFSYAdGU5hWSxU+oBXHVQElk36ZhJG0WycjJJZ\r\nuH10yTO6LhyeXCbJyXDkLfXcoKFpLjMlnQWLXEjQ5NS6cyto2Jue9RoluG5t0NBN//74/t8P\r\nv3/+9O7zp/eo5Wpd/6/3SJ0n8TAZPEWqOfEWh5JCcoe57GZBqrlRKusEDeu4nkIA10TIQkge\r\npMK6t5DO0EuvW+m2lbAMECpUiVDJcmVu0kl4OhjGw/HgqjngqXD4eDiMZ+PoIsKqaMLvHe5+\r\ndj8JT/qQsBhw2MMkHAxHw34ywoKgVpZx5q5q3cI7g6qBKmzZ/kkCwgJMRmBc68M7ZW4CAd7i\r\nMJC0hDb6648///nt1yBuNNiPm2tDdS7YzMA51oGSZefJrWIr27xE6F5bf3l91UtDqoucyiU/\r\ntxoCQDo+2Zo85/+lXjuhXFJHg7XZXxJfDkAL5taGQ5lAIvBraIF0QDoeo8nNXECFamhIRVMs\r\nqGNdLMwSWGCWvCImDe8f2S0KoWeiKDBclANDeLngUHRovxOEoMQ6wx3LUcxA9Q0UAbE6Bx53\r\nB4VerJ97Su4zU6IlvE0C6E9gB43pW4xi3z7btOBia66NdddclQEKQA44QFIpoZtb27DZqjRh\r\n1gQ8M+Dj+TatifXs3oPczinKu/EFGfKLG1Ot8PPmzlHjYOHhUEAkFakz/fO1ek3Zyk9Fq3sl\r\n01YT09UsGwvMoVKdzdtdZcu7X+Cogh6Px/jhBLsV5NPRoB5iopc/UDR2SsPzBIcWkMQyx7EY\r\nRf52oZxT5e644FnnNOc05TDxoxOY6opkSjm8HY/7eLtcO39b7wDCVIH73WrKYPqTvm8I/7V5\r\nbQSG5x40HOCCg6+siqAwF9ArsEjifj2mpI7YZ6B+82CGm6+16X8AAAD//wMAUEsDBAoAAAAA\r\nAAAAIQDy3zRxnI8AAJyPAAAVAAAAd29yZC9tZWRpYS9pbWFnZTEucG5niVBORw0KGgoAAAAN\r\nSUhEUgAABIEAAAM8CAIAAABPpyezAAAAAXNSR0IArs4c6QAAAAlwSFlzAAASdAAAEnQB3mYf\r\neAAAj0FJREFUeF7t3QmcJGV9P+AZEJFDJZHDWyRAQEBIolGB5VYE/SseEEQNGhTFIyweEQWC\r\nCCqKCoiKgBcRMHJ7cLqwsLvcKiCngFyiAgthuW/2/6uunpqaPqt7Zt6dnX76M+Jsd9X7vvXU\r\nO9397fett4cXLlw45EaAAAECBAgQIECAAAECSQSWSFKLSggQIECAAAECBAgQIEAgE5DB9AMC\r\nBAgQIECAAAECBAikE5DB0lmriQABAgQIECBAgAABAjKYPkCAAAECBAgQIECAAIF0AjJYOms1\r\nESBAgAABAgQIECBAQAbTBwgQIECAAAECBAgQIJBOQAZLZ60mAgQIECBAgAABAgQIyGD6AIGe\r\nBR577LEtt9zy9NNP73lPOxAgQIAAAQIECAy8gAw28F0AQO8Cz3nOcz796U9/85vfjDDWvPfB\r\nBx88PDzcIaHFQxHhWu7be1uyPf7jP/5jYgvsrxmx11VXXbXCCivE4QdC34VM+I55Zg6lriXH\r\nNi95yUv+7//+r92WcVzNhxb3VCm8KLN6e7o2eJFsoL91Zl/cz+8i6VQqJUCAwKAJyGCDdsYd\r\n77gEIj5FwIjbW97ylnPPPXeZZZbJ80aeu/JbVLBw4cJtt9023qoWd0YGOOmkk5r3jcQSuaVd\r\nkGuXrPKS85gXb/huu+22cR3VBO0c0eXNb37zvvvuG4e/xx57dCi1Oerk4a0cbxbJG9kf/ehH\r\nW2+99fbbb981IbcMY3HI7e6fIOPJKibvwPpbT1m63cmI/j9Z50m5BAgQIDBdBGSw6XImHUcS\r\ngUhW36rd4m1W3H79619vscUWu+22W1Re3FnEj3hDH9vce++9L37xi2Pc7F3vele+12mnnRZ7\r\nPfroo/lD7Rq+7rrr3nTTTTfeeGPDBhFXzjnnnHg0vz8G5eKfcYtfkhi0reQvf/nLs571rK22\r\n2irfIiJZjCm1HA9897vf/fDDD8f2RVmzZs26//77w7MIP3Hgv/vd72LLSTqoPOMVIbn45cc/\r\n/nGRrssRuiGVxUmP1ja8ZY+D/cY3vrHzzjtPUpsntVj9baL6W/5BjBsBAgQIEOggIIPpHgR6\r\nE4iIFSkoxg3iDfeuu+56wgkndAg/8cY9BlU+85nPvP71r49A0jAO9oIXvOC666579atf3XL8\r\nITLM+uuv3zzzLeJK3B+PTrWP22+//fbIUQ2aLRsZGsstt1wcSL5xKEWe+cpXvvK3v/3tkUce\r\nye+M0uK/L3vZy3o7PZW3zrNrNC/C8HbbbfeHP/whT8jFLZoUCTlych6bY+QzTt+narf45fDD\r\nD48777vvvmIYM85IjAHGP//+7/++cium0Ib626T2tyl0pjWFAAECBKaAgAw2BU6CJiw+AvnI\r\nSQyVxBvxeFP+17/+NXJUhKt4p56/NS+PnMQb9NjmrW99a8S2eF8ewz7N42Brr712vPtvN4oV\r\no2fxUHmyYrxRPuqoo+L+MCs+bm+4PicfgMpbUqS7fJvIOeU7i8u38o3LY1blQsr3t9slyo8j\r\njQwWkTJmFb7//e8PmfCJO6PkhiS57LLLrrXWWsWoV+SuSF8bbrhh7H7xxRfn3SGmbv7Lv/zL\r\nGmuskf+zQ1NbHloxazRq/+AHP9ihi0UY+7u/+7siEBZbRgNiXmIeqIrAFkXlA55xTuPOU045\r\nZb311st3iS0vu+yydgGsc3s6HF2U3HxCG645zDcY/zw6/S1Bf1t8nu20lAABAgQmU6Dho1//\r\nJECgikAxnzDfuJigWL4/fi/+dmPy4Yknntj8p/z85z+/eQQmLzAfhCne9BcVxT35JMYoP78z\r\n7sknN+YjNlFLMTEyCp89e3a+Tdwf/y2OLt+yKCS2jMbkO+blF4UceeSR5cJb7pJXXT6chkY2\r\nqBYHmO+Yt7842IYGdGhqlUNrPvaGxsSxRxjOh7zyW/yex+PyljnRRhttFAWWT27DaS3c2lE3\r\ntKfz0XU4oTla3OKX8pmt0oHbnQ79rfgjatfPu/4pde1vfZwguxAgQIDANBPIFg9wI0CgV4Eq\r\nGaz8FrzISOXI0aHSIqJERcWMuCLVtMtgHd6Ox/vCopw8Y5RTVt6SotI8bBTvQYtY0mGX/Liq\r\nZ7ByFdG2PEJEA3KoePTlL395HoE6NzU26HpoVVJKy+zRcIKOOOKI973vfXHdVBFWm89gQzkt\r\n219uT+ej69DyYsdymu21GxfbxyKfeffQ39L0t77PlB0JECBAYHoImIs4mYOMyp52AsX6hw3r\r\nIrZb2zCfHvbHP/7xFa94RVxB1LwuYvM8vQazyCQxbe/oo4+O+2OeXvwe97Rz7byORewbkwDz\r\nfWNiZKyK8Y//+I/louKf+VIZMSMr5gHGMZYvVOu8S6+nOq8iZGIe3VlnnZWvvRHreeTLkMTM\r\nwKeeeirm13Vtal5v10Pr2ryYW3jIIYfk5zFfWiNu5b2inXPmzInBsTinN99883HHHddhCfvy\r\nji3dig06q3Y4oTHp8cwzz9yvdotf2s2BLC890m4RzmhMMa9Vf6vSz8ff37p2SBsQIECAwPQW\r\nkMGm9/l1dBMsEO/U809fyuNg+UKIDXkmTxSRmuItXVz4FBmj5b5RVOdl3OOio7ikKkqIq8ti\r\nsCJ+T7D+YX75UwxDxUpxsf7+ZHz5WFQRuTSOK0JXDCvFKh0hFqErotef//znyELFtVgTfArb\r\nFBeXdUVInjFjxrHHHhtrHjanmlhYZaeddgqNKCACT+TG2KXlsoppGlyxluJKtuhpCxYsKK5e\r\na7e7/lYR1mYECBAgQGA8AjLYePTsO3AC5S+tiiGjzTbbLMZM4s4ISM0r+MX73YgWe+65Zyw4\r\nseaaazavi5i/g+8wOpH7xlrn119//ec///n4b+d1z6OKWG8wBpe6npiWW8aOsXs++hS3aH+8\r\na4+0GWu1x63KLl3rLW8QuTQy3mGHHfaiF70oH6CLbBPR6/vf/34xMpYHs+aDamhqudjm7WPN\r\nj6Dr2rb44oFY2DBmG86cObMhq8QpjmLLI5DlRVbKkyKa1//o3J7OR9fhhEaTop3R4LjFL12/\r\n0Kzr4Rcb6G9p+lv1M2JLAgQIEJh+AjLY9DunjmgSBWJ6XozexBd/RR3xRu2MM86IVQpjguID\r\nDzxQrKhWrj7ms0V6+fCHP7z66qs3r4uYv3fvOjoRb/djECYuRor/dl73PN8yVmgsVjKMX1p+\r\nQ1fLLWPHOJx4KIahYh2O4kBinCoSZudd2qF3CIT5CvXHHHNMeXAvgtmvfvWryLdFpu213jzI\r\nxQy9YoJomMQKjZ27Rb4yYYzL3XHHHXHWGoJxjA7tv//+1UcgywsVdm5P56PrcEI/9rGPxRHF\r\nkF3+9XT5fyfkpr81/xEVfxrNwu3Ob/xpT8jpUAgBAgQITE+B6XFZm6MgkFigmIuYr5pQLF/R\r\nsFZHMSrSbv3Dds1uWGihvEZFscxDy3UR83mSxbNVsVxeee3EotLylg0tbNfyDrs0rMlRbknz\r\nUoF5G/Jayot/5Gt1lJcwKSZ/ljNhecXClodWHo+KFSk7rBwYbYuSywuWRI15M+L+hpbnC2Dm\r\nJ73dS0KxQXmtws7t6aDa8oRGaeXzlbd2PEsj6m/5yp8V/zSa+2f1/pb4mUp1BAgQIDA1BYZ9\r\nVjc9s7WjmjSBGFaK0bD83XlcyhX/jLf4+chY3OKfcdVWvKWOMZN8QY78oWKvlu3KU1z1YZZJ\r\nO7jBKjhOUHzVW34e2x15vk28w87PY/5FZ523j3mMna/xGyxlR0uAAAECBAg0CchgOgUBAgQI\r\nECBAgAABAgTSCbgeLJ21mggQIECAAAECBAgQICCD6QMECBAgQIAAAQIECBBIJyCDpbNWEwEC\r\nBAgQIECAAAECBGQwfYAAAQIECBAgQIAAAQLpBGSwdNZqIkCAAAECBAgQIECAgAymDxAgQIAA\r\nAQIECBAgQCCdgAyWzlpNBAgQIECAAAECBAgQkMH0AQIECBAgQIAAAQIECKQTkMHSWauJAAEC\r\nBAgQIECAAAECMpg+QIAAAQIECBAgQIAAgXQCMlg6azURIECAAAECBAgQIEBABtMHCBAgQIAA\r\nAQIECBAgkE5ABktnrSYCBAgQIECAAAECBAjIYPoAAQIECBAgQIAAAQIE0gnIYOms1USAAAEC\r\nBAgQIECAAAEZTB8gQIAAAQIECBAgQIBAOgEZLJ21mggQIECAAAECBAgQIDC8cOHCKajwzW9+\r\n88477/zyl7/87Gc/ewo274knnthrr7122GGHOXPmnHLKKc0t/PSnP/2Od7yjfP9DDz30yU9+\r\nct9991111VVbHtGtt976sY997IEHHmh49IUvfOFPfvKT5ZdfPu4vFxK/f+ADHwilDj4HH3zw\r\na1/72nyDZtLLLrss2vO9732v3KQ4tK9+9as777xz3Nmhije84Q1T9uxMwQ6jSQQIECBAgAAB\r\nAgQKgT4zWASPeE/fnDQmSjYPDF/5yleWWmqpiSpzAsuJvLTffvsddthhRxxxxGqrrdYQt6Lx\r\nzXdG4Pna175WpKkOjWnevagu9uoc5NoVG7Xvscce5UejnN/+9rcXXXRR+c78hOZpMA6wyG8N\r\nxUZpxx9//JQ9OxN4ohVFgAABAgQIECBAYMIF+pmL+OSTT1544YUxChT/jd8nvE1R4PDwcB/F\r\nxrjNu9/97kgIfezb0y5XXHHFeuutF2NT0c7mphZ3nnrqqRuP3CICRap885vfXNxT/uVb3/pW\r\n3oBofGz21re+NY7lgx/8YMShnhrWcuMoJMa7YkzsmGOO2WqrrWbPnj1v3ryll146No7f3/nO\r\nd8ZDcU/c8jAZI2ARwCJlTdLJHf8RKYEAAQIECBAgQIDA4ivQTwaLBHLLLbdsvvnm11xzTfw+\r\nGQc/nhmS/eW36kcR6egXv/hFRJfYJdrZ3NTizu222y7PNhF+XvKSl8R/83823z71qU9FaRGW\r\nYmZg5KUY/YuA9/GPf/y73/3u+IPQSSed9KUvfSkGtSJcRbr79a9/HYdw9tln5xXFgNjpp5/e\r\nEPZi44MOOqjdIORkC1c/F7YkQIAAAQIECBAgsNgJ9JPB4iKouBxorbXWWmeddeL3hmN+8MEH\r\nYzAqH+T57Gc/m0eImF8Xv//85z8v3xnv+7fddttiOKh5/Ormm28uNogSioqizCgt3zHqihrj\r\noZgeGaNMMYg0c+bMuL/lZVoTcnquu+66qPEFL3hBXlo0rGFoq7nqSEE77rhjuyvB8nLioCJx\r\n3XjjjcVYWRxIzBWMyJRv0DLvVTmimGH4mte8Jt8yfonBrgh4EfZ+85vfRMsjS8cvMfkwTkcZ\r\ntsNw4ngScpUG24YAAQIECBAgQIDANBbo+Xqw/GKhGFeJd/Mx1+7II48sL+oQlxhFcvjMZz4T\r\nQ0ChFhvHIhOvfvWrY67dySefHGNH+YBP3PItDznkkDwe5MXuuuuu+Y759pH08ouOGh4988wz\r\nIwFGpInM8IUvfCG2zzfL15DYc889i8gx4WcurzGGAfMru6KdcelXtLm4ZKt8Z3GkBx54YGwf\r\nVnFQzU0qDrP5oSg2dtxpp51inYy4/Cw2yK8Hu+eee0KvytFF4V+s3Rou/Yp9i9PUUE5+jP/2\r\nb/8WjO1WCunQ5iqtsg0BAgQIECBAgACBwRXIR1eq3yJFvOtd74qBoNglosg222wT9+S7x5J6\r\nMTz1jW98o7m0uLPYKx6N3eOfxY759uWSG7ZveLRc/qWXXlqUnBcb91Q/nF63jEbus88+u+22\r\nWy5Q3IIi4l/DnQVRS5Ncr8xSlBbbl3GKwqP8qCX+2dDsOOQoqvn+lkeXn6aNxt7Kzcg3yBlb\r\nHlc8FBvEZr3q2Z4AAQIECBAgQIAAgd7mIsZA07HHHrvhhhvmS6XHNU4xHTHuifvjn3/5y1+u\r\nvvrqTTbZpDnRxhVEr3zlK/N1IOIWwziPPPLIS1/60vKW8c+4Mx6KOxu2j3vKj8Y/YwAqnwHY\r\nsNzfZIfpSEEf/ehHo5YY3yumXEYz3ve+95WnEeZTLu+9994Ya1pzzTWbL6CK9sdDse7FiSee\r\nWKw7XxSYLztZzOeMtNPhuAI/VlyMIcR2cx3zpUqKCZMx+TAWUynW4cgvTiuakVcUfxgdaoyr\r\nxb7+9a9PzSUrJ7sDKJ8AAQIECBAgQIDAOAV6y2BxKVRccBVDNPkb+ng3HzPc4p64f5ztqL57\r\nnigiRcSMxAgPkSWq7zv+Lf/93//9ec97XkSU+G/kljzARBve9ra3RUqMtSLzVQfjFmtaHH30\r\n0ZHNNttss3JozOnCsLxMYgS2CKhFgTFpMxJaUU7nL0mbNWtWlB8LHuZHF+ku5oiWjzQyXlFy\r\nlBktjJmEkZybV2hs2HH8XEogQIAAAQIECBAgQKBBoIcMFpcJnXDCCTEIVsSM/A193BP3x6Mr\r\nrrjisssue8cdd7RULo8Ftdwydozd46F895gF9/jjjxdFzZ07d911142Rt8h7MVwWs/Xy4aN2\r\n1U3qmS4fS2TCH//4xzEVMBofLSyW0IgGxMVvcalYPqYUv5eXQ4yUFUtlFPc0L0LYMBJV1Ngw\r\npBbXa8VlZp/73OdyjTgLUXIswFheFyR+L8etPDlvuumm5fb88pe/jFOZX4wXt3ItLVdBjBU7\r\n/uu//mtSkRVOgAABAgQIECBAYFoK9JDB8qmG22+/fXkSWvwe98T98WjEgBj2iUl0xZJ68Uv+\r\nez7psxBsuWXsWGSJ2DiGieIbkPNdopAYOCqqjnmA8+fPj/sjgRx11FENJyZaMtmnqjiWCGCx\r\nSEakkec+97lx57/+67/GWo4NKwpGhmme2hf3dFjhveHRmGT4ox/9KE9Z5aLypRRjFmLxZcpx\r\nOmLjn/3sZzFOWAxqxUKI5biVj4M1TAQN0ignn1Na1HLGGWfk15g1ewbyKqusMtnOyidAgAAB\r\nAgQIECAw/QR6yGCRgmKoZ+21125QiHvi/nzFvxhIiaUO4xqtfOAlBq/yeNCcNxq2jLX+YvSm\r\nyBKxfeSEGPUqLvqKYvNH478xTy+vIhLIXnvtVY52sShiDJHFQ5M6rS4/nEiAMfkwvsWrfCFW\r\njG7FURffuZy3reX3OHfoTLF9fhRxi4mXRTRq2CVfKfEtb3lL/Le8rHw+0hUltESInPaiF72o\r\nIanGZXgxAplfsBfDjxG9Qjh+iav4Hn300fwiveIWdV1wwQUxkjb9/h4cEQECBAgQIECAAIHJ\r\nFuh5bfrJbtDULz8SUVy+FSvpf/nLX45F5/MAVl6bPv4Z4ScGxPKF+Mu/xxBZvohIXE5WXtM/\r\n7ikeit/Li/jHP2NNjr333juGtuL3mPl5wAEHxBVi5e1bFtjQpDJsyxXn46q2POU27Jh/T0DD\r\neSmaMfXPlxYSIECAAAECBAgQmFICMtiUOh0aQ4AAAQIECBAgQIDANBfoYS7iNJdweAQIECBA\r\ngAABAgQIEJh8ARls8o3VQIAAAQIECBAgQIAAgREBGUxfIECAAAECBAgQIECAQDoBGSydtZoI\r\nECBAgAABAgQIECAgg+kDBAgQIECAAAECBAgQSCcgg6WzVhMBAgQIECBAgAABAgRkMH2AAAEC\r\nBAgQIECAAAEC6QSGTz97drra1ESAAAECBAgQIECAAIHBFvAdzYN9/h39FBB45plnHn/iyaWf\r\nvdQSSxiXngLnQxMIECBAgAABApMs4D3fJAMrnkAFgYULF1bYyiYECBAgQIAAAQLTQUAGmw5n\r\n0TEQIECAAAECBAgQILC4CMhgi8uZ0s7pLLBwyDjYdD6/jo0AAQIECBAgUBaQwfQHAoteYOEz\r\ni74NWkCAAAECBAgQIJBGwJocaZzVQqCtQKzJ8cijjy+7zNINa3JcdNnvqREgQIAAAQIECEw/\r\nARls+p1TR7SYCUQGe/iRx5Zb9jnNGewNr/3nxexgNJcAAQIECBAgQKCbgLmI3YQ8TmCSBYaH\r\ns49C4r+TXI/iCRAgQIAAAQIEpoSAcbApcRo0YpAFIoA9+NAjz11+2YYYFnMRYxzshrMOuOsP\r\nJw2szyqvfteaW+89sIfvwAkQIECAAIFpKSCDTcvT6qAWJ4GYi/jAQ488b/llm+civuC+MyKA\r\nzdhpq8XpeCa0rXOPm5XFsDftNaGlKowAAQIECBAgsCgFZLBFqa9uAiEQGezBBx9+7nOXa85g\r\nT8358Ix/23zAleb+fPaMT/9uwBEcPgECBAgQIDCdBFwPNp3OpmNZXAWeWdjm+8HiIrGnFw76\r\njyvlFtd+rd0ECBAgQIBAawHjYHoGgUUsEONg9z/w0POft3yLcbB5H5nxjhn19q36rUXc0MTV\r\n3/qpvMK5p8ydscdliStXHQECBAgQIEBg8gSMg02erZIJVBV4ps0w2NDwEjFVsf5TtbDpsl1x\r\n4IHgRoAAAQIECBCYRgLe3Eyjk+lQFk+B2tr0z7Remz6m4cU0xfxn0G7FgZuLOGin3vESIECA\r\nAIHpLiCDTfcz7PgWa4ElYhxsYf1nsT6QPhpfHHgguE1fgdtvv/3++++fvsfnyAgQIECAQAsB\r\nb250CwKLWCC+HyziRvy3RTuMgwWLcbBF3EMnq/qjjjoqhn9f8YpXrLDCCvH7ZFWjXAIECBAg\r\nMPUEZLBE5+TBRx9PVNNEVzN1Wp64JSmrWxjXPrW8xaVQEc3yn0G7FQfuerDeT30MLp1bul15\r\n5ZW9l1Fpj4aKos6LL7447sxvnYt44IEHig3Kv1eq2EYECBAgQGBxFpDBEp29Jx5fXDPY1Gl5\r\n4pakrK7t2vSJuqdqpptADC5tWbptsMEGEY0m/CAj2jVUFHW+4Q1viDvzWwxzxQBX1zA24Q1T\r\nIAECBAgQmOICMliiE/TYY/1ksNbrNPTb5P5Kq9jy4eH9Orer6wZdD6tiS+76v8d32nPegw+3\r\nBq+OULG6rs22AYHEAi1HvU477bQJb8a9997btcxdd901wtjkDcR1bYANCBAgQIDAFBTolMHO\r\nu+zPe3zt/Phvynbf/LfH3/H5yZo20/JA0tT46KOP9sHY+hqhPgqq7dJfaVVaXjFfVdys3fFV\r\naUns+8OTb/zb/feeef41/TrV96tY3ThryXdvOxdxQkpXyIAJnHjiic1HfMABByzCpS9iIO6G\r\nG24YsPPgcAkQIECAQFuBThnsF+fefOvQqh/8zx/le8cAQsvbxOp++sg7hx5bUJTZrtLqoxld\r\nm1e9xrwxXQtsucHjjz9RZcfy8XYwr9iMCSmta8uLZBW/tExZ5fvHE8O6tiQXvvIvD638d8sc\r\neswlVcA7bFOxuuYSLr/88nIHrtKMpwfwcq8qLrbpXSCCVsStlvvNmTOn9/J62OOiiy66beQW\r\nv3/0ox8t7/zTn/60h7JsSoAAAQIEprVApwz29i1Wu+LUn+y9+5tygRhFaXmbWJ9vfnilBQsW\r\nzP3dXztX2t+QTsumVq8xP/z+jveJJ5+ssmNZuIN5xWZMSGldW75w4b4jJ2vf4vfywcad5W2q\r\nOLTcpmtLYq/Lb7zvoQX3HvCRf3n40SfmXnZb33XFjlWqay5/9uzZ//zP/9xrvQufebrXXWxP\r\noKXAddddV9y/9957l4PQ6aefPqloL37xi18+cnv9619/4IEHlqtrlwwntUkKJ0CAAAECU1Og\r\nUwbb7LUvu+WifXfZ8Q0pm77L/pcNPXbrfx5wQrJK09T4ZLUM9tBjT/9g1r17HXZFfvjjGQbc\r\n6au3/9uBd1x+y+gcyP5Kq9Ly5ujVcsywZUKrfqKrtOQ7P79i+WWWOuW8q5Zb5tk/Of3S6oU3\r\nb1mluoa9IoBtscUWxZ233HLLeBpgXwJ9CJSv+3rLW96y7bbbFoV8//vfv/vuuzuXGTMGY23D\r\nb5Zu8c/+phE+//nP/8Y3vtHrIURd//u//5vXH7/0uvvU2v6Mj4x91v3IGVOrfalawyGX5pCq\r\nx6mHwGIhMOXW5Jh9+Kazf7b75afsnowvTY1PPfVU5yOaf9/TR531yCcPfei0C+74xHvWGP/h\r\n77PTi554/OlPHXn3YXF91D0P911g15bnJZfzVTFVsjxncpwBLKqo0pI7733kkE9t9F87b/bV\r\nPbb9/RV/vf7WLu84O7BUqa5h9yKA5W+8VltttZNPPrmK/DPxBWFuBMYt0DARce21137d615X\r\nLvWSS9rO0Y2s9ba3ve0f//EfY23Dz5Ru8c+4c5999uma38bZ/Cg/aom63vOe9+T1xy/jKfOm\r\nQzcqItBGh940nqJavH9u8ZnWRht95NAzbipVtM0RC288ZMO+K77ppjMO/chGtYPou4zYkUOu\r\nx2E8vci+BAhMuMCUW5PjHfvdFmtyHHb8Hyb8UKsUOHm1P/10l8lmux55x+kXz1+w4NYFt966\r\n0yd/8E/vOLQWbNreOh/OO7522xf+tzaf884rfnTC6dt++Ad9l9a15Q0tyd8ufLF2i1/G+e6h\r\nXHjXluz+zXl/vfOmK66+NRZF/PzB2bSr/z7kpCrnveU2Xatr3uv3v/99fmdx2t7xjndUacAz\r\nC9t8P1iVnW1DYETgd7/7XYERExFjJGrllVeuMh0xVpCPrPWrX/2qnWXMJPzQhz7Uq/T5559f\r\n7NJ5TOymm26K8id0vuJNpx1/YVH7hdfe2GvjG7ZvylMbxpKP9Vs9Zl144ZEzt11jjY3Kw12r\r\nv2WHvkJYFhjWWGPbmUdeeOFQ1DSOxnPI8TiMoxPZlQCByRDo8C5/5oHnbXfgrau+/oudr4Pq\r\nUEI8tP+Xvxo/+/z3Fz/z2c/t9vFPdN44f3TW7+/Z7AM/6Xrx1YTXGwX+df5DhxxzbVF7y8uu\r\nGuqteIDnzb24c4PvvOeRb//8yqh6g+0OueHWe9odfhXAfJsb/nzfZjtmpb35Qz8459IbqxxL\r\ny8K7trzhqrPopZG+8jvzGFbOJM1VVASMHXtqSTuozo0p79VfdXkMq36aYssIe3+67Y74b8Ne\r\nF176uznf2Xjh9Z+o//RUaO8bnx5v83Y9vff9JmyPrAEbHjJa3MiBZwgJb1POoZdjL8etWBUj\r\n3/WXv/xl+bXjj3/8Y3OR5YD0//7f/4t/5reGF51zzjmnvG/8s7xBrMdRfvRnP/tZh0djyyoz\r\nFXs5+rHbNg5ATUTnLpe54SHZs2pxG1tdqbLyA1XbkHXC+q2hmt49OORmHHrvO132mD/n8C8e\r\nPmf+hJdbpcBrT/riF0+6tsqWk79N1rXG/XfaZzMX+etVU7uvu/rt+53ztu/fckefR7S471Z/\r\noqn4XD/pa3Ls/YU9H3744fsfeOC+BQu+953DusbIi697+IAj533pE5t1fe/euahe643Srrrl\r\n0Z32mbXtJi8ql9zcHRrqrVjRwqEuk81WecEyn9zh1b/87o7/sd3r9zr4+K5QXTc48IjLV1tr\r\n2Z8d/J4zjtpli9eu3nX7dht0bXmxYzECtu++9VU64peuo2EVAbNg080wtvnlN35wwn6HnfW9\r\nY+O/x3xs5jW/+EWCAy+qiBUR/+mf/qkYDatedV9zEesza6pfYlKeilObSNXj/Kyx+3fduam6\r\nXuur6MehDhVz+eKKr0ItJiLmvzdMR2zZP5/3vOfFlpGaIqFFZvv0yO2uu+6KSFaUGVc8djgr\r\nMZhWXEcW0xqLmYRRwhVXXBGrdXQ9o5EhowGR5eK/MY7XdfsOG5xx0MzRUbBsuyNPndSLsVbf\r\nfe/SYNXVN4xj6uMZH9n2yPqRbXjI0bv3/+SdFcIhp+QQCNedvF/cvj/3nqp/WffM/X62x+ht\r\nzL7z5981tPKKK7YvLK9v5Hby6GpBTbs0VbRfp62Hhu655+6hVVZaqepxNGyXX5fX9SVsdK/O\r\nr503Xnvh0Lprdvg7HXsdYKeX7OZXzc4v8DfdcPXQhq/q8+KVh0864tztvpT9zJzby+Uq11+T\r\n75X/vP1Ll8w8+da/jGBdcu1d8esrX7XSS9qenIf/cv01M0eqfvsRlxx8ffvaW9V1SeX+271/\r\njIJXfyfVdF1nTEI/o6cn/JhlXp9knmJNjq9+ef8F9933gyNH3xx0cPnakb8dWrDgPw/4SXe7\r\nblv0VG8Utt4rl1nhOSvs+l+/iAmB3coe83iVioaHKk3of+6yS39y59cdf8hueQVdE2CHdv5w\r\n/81/uO/2a626crFNf6VVbHkx5zBCV/lCiWIorMOkxCqAcRSVWvLw3dvv+8nNdt7unj9cNP+W\r\ny9Z5+9t7OpXljStVN7b0WBEx3qRGDOu10mee6X0uYvaeImZC9frWcvTTmdN3vXDmGlVfhrIX\r\nkTVmrjs6TnbjDsdX2XnMeMDQzDV6ed2rpsihePErXeuVT0TMH2mYjnjcccc1y+6www6xIO2O\r\nO+645pprlh+NfT/84Q8X93SeKxiPFteRFdMaI4BFCauuumrX0xltPvzww6MBkdbiv/vvv385\r\n/nXdfewGZ5yaxZhdTy+NKE1yCBta41V9TTpsPLCbDj1gJIEN7br3OBPYEIfcl0PklrnnX5VZ\r\n3DV/ftW/pixlrbdDfJhau318i1XuOve7RTjqmINqoer4u7f4+OjOdx/fPleNrSir6arj92uf\r\nw+657pou8a/DEY78hfUwPzlLWaOvZTHSEa+dxXv2jjmo9h5/26tHB81vPOTqbdu/3R9bUTZ4\r\ne+S2w8Ntt6/Nr+0Y/zo4XH/rT7O4lN1umd9DBstTVnEbHnr41qtv/tjJ+bX3D99R61yvWGm5\r\nNjVH8Lvk48ffdetIGcN3PXze8be2u0y5ZV1f+d4lJ01QDCtNUD7ygKrXDGcnfMwtJqFvO9of\r\nuv1xRZeIWebZJPO4JVqT4+if1L9krFvjhk45ONbk2PnyU+oDKV2377xB9Xrzcsq1P/TIk3N/\r\nV3Vx864VLbFkJeoJ+Uav0b+NUnFxZ3/rIlZpeTEC1m4UuetoWFfArLNWMHzTpz8ZW176g+Pu\r\nvvmiN3/2v8vdI14KTjnllLgnYtLuu3df9KVKdc3dL5bl6DxW0LLH9nE5WPaeYtftjtiu5xBW\r\nNGCbz8YMiguPP63C5ze1D+bjJeiIbYq9V9/9goUX9PQGMfaovXbtXPWZrtJfP4eCqbz0fKyI\r\nWOYrr44Y6ah5ncMIbEVma3Bfbrl2r6aVTlBUF2NiK6ywwpVXXtlhh4hbEboaNthpp50q1dG8\r\nUf0t93bbbBN/ISO3DiGstvpF/ZPJ/Imy5482y43YcIe3tP1UvPYR6MiTcXMt5auW+v6Au2gL\r\nh5yCQySwE8+9a5UtdthilaG776n4DrYhZa249jqrjHbzTjnoupO/m1X28Y/OKEbJVpzx0X3f\r\nWR+ab/qDbYxzsXHEsKGrzm8zZJdFtj6HwW46dOeZF254yOnx6ld5tLohZY29xrNTDjrjI2tk\r\nld1YeqnMXjlLL6RjKRrjXP6iOdQ2HmSRrc9nibsPPv6uhUPLbbpu7el9/iPFQFa3p9x6ytp0\r\nhy1O/e/4ed371y3v8fDtd8WEpeVe3np0NAtgEfyyend4XW33Lb67wyqvXGW5l7autV7XqpuP\r\nbhwbRur7nzktl1vrIUnWKsyfbXc95JDsVaLam6HRho5MP61PPqz4GV+t/2WFRB+M2eydgsFZ\r\n86754OePjv92OyUT+XgsyBE/m+/W/xSyiWrN3+Y/8N/fOa96DOtc75JLLlmlYeUME9t3mBib\r\nrLSuLS+PgLVrVZXRsK5H1LUlUcJzln/u36658fdnnr7mplutvcXW5TK32267d77znXFPxKT4\r\nfUKqywspwm3+z/Ly9F1r6XeDPHpsM5S9xWx8gh4z8aHbAHvjZ2ijO49+zJd9ML/hIZ8dDWCN\r\nra664FhtvtaFMw8qJoW1qK03EQ51r3YTEfOHq0xHLMPfXrpVXw4xBrKKC8maZxJusMEGHWLY\r\npptu2nzmi+mUvXWKWAAvG0rKu2x5dKrVq+RNsfJgjPHG6hdD2x19weh1Q719tBkNLIWn9sNX\r\nN9Q/Ah05oFot5cHoMQtH9PsB90jpHHIJDjENcU4Wit49Y+0VV24aCBudMvj9udfF+NXIhMPG\r\nlFX7dxF98hw0NDpbcXSALBtwyypr/Ua8NEOxXlOrOLdiNLTc0jGNzGYirrN2h1mQbZ8xsnkT\r\n2QzfbdZcd6hxIGz05WijQ8+I8auRP8zGlFX7dxF98hw0NLoIa+MrZ7vpxKUX6npNreLc6tHQ\r\nckvHNDKbidjh8572z5t/mXtLLJf0ys3X3eNVy2db3fXwHeWN77n14GKa4sm3XjI3m3z49pGR\r\nrjEp6575c2uDQpu9qjbl6p5HagMXy98+55KZIzMVR6cajoy8bbbD6/ZYq/7R3kvWWueQj6ya\r\nTVy85+6Dj7ikPjeyPrmxnuhmrD268efzyFeExmyv+pTIg+dWHuCtPzHUItiGr3rL7rWP6saG\r\nsGK6YP1DuY90+fC4MQvfkC1q27TvyJzoXU+/YPdt4oO6Thnsf0/77YLHVv3oJ7Ml9eLWbhSl\r\nx1fHLpuf8tX1Y4v/ev8GnSudwNX22jVojVe84Eu7b7bB2vXLwxreZ/d61Es961lVdmk5DlZl\r\nx5bbTEhpXVvekBtbtqTKNl0Ps2tL8hJOP/y4Zz85f7v9vxm/333j6GJoMUUwPo+POzfZZJPN\r\nN998oqqLcoqjy8vs53qwXgfCRqLHUC2ElZ864hn6gFeNLBZQm8nQLoXVngyyHDd6i2kPp243\r\ncgH7kdvWXxiy15gO8x2ykfWZQyNzLWKOYqeBrto74vrHj9HUbY+sT/C48VUHFNfAdD01oxtw\r\nGLGIleULlvJExPzOKtMRY7MoJFaHj+eNV5Ru1ReIjzmHxYVkMagVHalhWY4ovIeTOzT0d3/3\r\ndz1tX984zzH19yZjPrNuDmGx7ZG1TyV3Pf2I7CVxaKh8YVfV6SnZy3V84J3tvuGup9/Y9mPu\r\nI2cev2724WesEFGaJFkeG679sdVvfX7AXezPIafgEBeCHX/V0HqbZqFopZVifKn0Re6RbY6/\r\nqj7fcIeVzz3+3NFJfmNHm+oDaSPJKhu6Grrr3OPnb1qbbbjDekNX1ecaZnGvXlnTn282RXF0\r\nhmLUd2I20tV1WKvcyI+vc025kT09Q+QzOmozfLOXovLzQfnl6PR1Z24bf84jn4GMHW2qD6SN\r\nJKvaxLQLZ2577d61V874ux55zc1fYVtPJ84+thydoRj11V41uw5rjXnN3OH4ciN7cbjn1oNm\r\nP7xwaJX3zFhuaK2Vap9+PXRHMTZ6z60zv3fz+SNzBWOe4Vdn1y7xyqcX1lJWjET99Hu168G+\r\nd/MtQ8u9P8tUtQbc8/Ct2aN3nX919kvcYsvzjr+6NnXw4ZNqhS5cZbUd8o3Lt6zSq8+/qz6Q\r\nFZUeFJeojSS6l5bS9kvLsxzre9UrOn/2zb0olJ8Y8vkSY95JHTQyXTAvM9a8bZhuGPNRa++x\r\ns2f+eNpviNqxRG4satu478g0xuKNV6cMtuNbXnPFeafu/en6FTXtxmR6OuYqG2+27rO/cNCp\r\n+ZbjHAiqUl27bT6839nf+ellMSOxoSX9lbnUUktV2bHlOFiVHVtuMyGlVWx5342svmOVllxy\r\n9MkPXXPeJp/87DLPfd5jDz14+f/8T7n8fDiuGJTrXHWV6ppLiADWx/VgC3v7frDax7ojf8QN\r\nISxW0B6d91B7/zn2fWc2uzy/xYtR4+BWDK7X3z7mI1ajExXbvx3M3uCUVg6IyROdpijWPtTL\r\nb1l+KhoQu5XelFbsFBxGocpXecV1Wc0fmZWX64j5gQ1DUjHYFZ9QxPL0E7o6/FBcYFZeqrHl\r\nNMiKJ7v6ZmPeckemKi8O33b2UbmDl4bOOlwyMvIKXHsVjpfraGA2OeWCepRr2d74QxlJetsc\r\nUU5hlaYEVyeobckhB+NQuxBsvR3ymYC18aViNmIWzmLOYH2S4NqbxATAofXqq/nUU9Z362tq\r\nxOzC9XYYnVyY5aYY7BrZNYt2+a2220gZDX12ZDSu/p567XfumxVY26Pl4h75oNvYRq44Y9P1\r\nouY+FuTIXzDiw5asVbWXouL5IAtnoy9/tXn6xUtsPWXlb7hrb7mzQYyRyca1T01Gdx198qjt\r\nNvZDzkJjZDSuXkj9Vbu2R8vB7/zpaWwj8w+L+vmg5pI5N98aI1c7rFP77sjlXr5KlpRuH8lg\r\n+aORlL5bnyu42qrZimjL5YNRf7nurni0fMtmBp5fv6Arv3xrdKrhx9aNgDdSeDaoFbdWy3U8\r\nfNJJ5Upf9/5VsurqdY2dqXhH6dK1sU19XX2IrKHPtf1nPiRV/6yuKYRt89nTT79x9NPs2pT2\r\n9pNXYz7Dzg2fdudzDYsFWcd8njd61jplsK03XueWyw/e5X2bVT2iidjuHXucf+qvL/32F7ab\r\niMLGVcYBn5hx9yNLPfRoPYONq6yhof7e0E/scF9/pfXX8nFytdy9a0vu/tPNvz/qW3978p4b\r\nfjvnZzM/fPCbN3xk6JFyUZGO/vu//7vKIFjs1bW65kb2F8B6tqq9pxh9bm8cCStPDKx/Nl+q\r\nouMiGeWn/+y1ZPQdaNv3oj1eFDx6NWv715uqIBxGpOL6rg5f7dXS89JLLy3fH9/NVS4hRtJi\r\ndcRYej5uVVaQ73DKtt9++/Kjd9wxZs5L1XPdw3ZjXlmz/cohrGnKf3Z1Rnbr7drGWnNafD9Y\r\ntuhMpykrY95flWdJ9rA8QFUKDrkUhzz4bDJyLVaWlkaW5bjuupg0WJrUN2ZAqmGdjBjqinUy\r\ninmKtaRVG1lruLUf1MqzYPM+LffImpYHs8ZGto9snf828uBTzKkvv8LVPhAsTeobMyDVsE5G\r\nDHXFJ5nFPMVa0mo12NV+UCvPgs37tNyjdilj7YmjsZF9voTec+vParMHzz8+X9gwu0Arbrfl\r\n2ab2aISof39XbX7g0NBLYsmN7P+Xzwej8gjUeIHWXXddeH3cXb98K5vimE81XDELePVbfVCr\r\n1XIdMaGxdpHYSKXLvesjr3vXivW6hlZatrTE4t0X1hqfBbnGpvZ43XJ+jWhx1vOn49KVEquv\r\nMXTQzvXJhGvMLNZJGu1jxdcR5BeEXTh21kSc37ETK8rP8aO/V1ooonO/nthH81UxZrz2FRNb\r\nbB+lffSAS4ceW/DBz435ips+ysl3WXrppfvYN94a9LFXu136K62/lk9gs4uiurZk5X9Ybbd5\r\n8w6cd9U79v/mew456vO1XxpaEh/pVWxb1+qay+ljBCwvpKfvaM4/1h0dz8oGtIqnjrETA5u+\r\nE2dMm1ff/ehKq3KU5w9WxGu7WZd5jb0Uz6HQ6mP6a7ak5sjt4osvLgewWEc+X5MwrmyMW9+9\r\nupeT2Xrbv/71rz0XUl99ofwGp2MI67mCkR023OGzR4zcIseNfP1UNmWl2mqjpTHhvhvRfkcO\r\nuc3AO9SmIcakwZHhrP1iPGtkIKwxzYz5dy36lEeb1l47xp9G0lstoI18/UU4d51O2H6bxoyV\r\nnbZaXMuDY1Mjs6vS2oyzdfhDyr/xoTR6XfuAMh/ZaEwzY/5diz7l5d9rQyYj76JrAa00o7/r\r\ndMKors02jRkrO5haXMuDY1Mjx34WW/U5JB9xar7Vl0asTSYsElf8Vl+ZsD4Y1bjs4UtWXG7V\r\n0bIaL98aGglXbZboqO85Mt61yuvHJPp6XfUrzWrb5pex1QflGptaj2cVIfInhtEOMfJ5dX3e\r\nUPZOauxkxPbljkxfr/BJ2siTfjE7acplsIp8CTbL0uCPd553XH2Z+HHWuMwyzxlnCYtq96nT\r\n8sQtSVndwl7Wpq898479MuVsVlPtjzofGup7QevyUPvo60TtLWx5JY1yXxw7o6NLLy29oDRM\r\nBemje3Mo0MoTEWPuX7EwRsMv5WmBsW8xHfGCCy4oiooRsPXXzy7KnahbfGleuaiXvrT1Clix\r\nqH3z4h+nnXZar82ov7KWPqKoT9gfKajN4lexLmJtYcSNsgU6Gr5ZrFIb8k808lvFxT/LA2GV\r\nl2ir1JiR6FH+qIZDMUG3fIKnfX+oTUMcWSB+ZIn5epRqCE61C7lGYlfzaFPtnvrDjQGtlKNq\r\n42zXXFdx6cXmjBU9vHbp2ciIWcdGVvxziM1q0xDHXN2SfWySv21uCEW1odOR2NU82lS7p/5w\r\nY0Ar5ajaOFsPk4xbDGvla+jVX9A7NrK6Q7Ys4cg8w/rKhJu3Wxrx4UtOvuSrxbhTVkfDsocP\r\n12cDDq2yYVziNTLSVW9MrJaR5711XxmDWkMrLpsPrZx3/DUjX/D1cKz2cXDx1WSxLkit08S3\r\nh9XubKrr5Es+Prs2WJcXWL5lK3NcHfGs+i2PYC1ueTzKL8yNy7zq18ePfmTZvMtNh+aFjUnq\r\nMSpW+86weGXJv2wkT+ojC/XGRYOH3hSPy2DVT9m4tnzOcxbXDDZ1Wp64JSmr6+nbwZpW0qj/\r\nXWdPHbVEVHzEUlsYt0O/bbpmePRLT0rXLmerFGRvL+NtbXnCc1wdXPu4P5s6X3rPGR8ftRkF\r\nqA3RlS4dq82hHFkjsXatcW9/Yhxyr4aJiHvssUexMEbDLw0ZbNasWc3isYh8w51/+tOfejsx\r\npa1jhC3CVXn3hu8fKz8U8yHLMSwiYs8Xp9W/+ac0SzDG+2q30a/uanxbVF8YcduZM68e2vvo\r\nbGnEkSGtHg+7PK5V4RPRsaWPzFKcmAmKHHJdDjFwVZqGmKuMXhFWjkux7MX5d8fEsZHrshoH\r\nturLzedLctQvFastqJFFpu9nV5XVV+uoXa91V77YRn7LFuLIFk0cm87i3tpKis0VZUN1Y680\r\nG8l0tZpin6KR1f9Gy9MQ871GPz8sx6VsVaur4+mi9YIc2UVZpZex+qVi9WWo4iUuu6qsvjhD\r\nfRXg0hJV2WUC2Wvo2HQW99ZeWBtHx6Ih+cL29Wu0y3vVaop9ikZWd4gtN9u0Ps8w3+slK5WW\r\nRqyNa8WiGl+tTVP86tX1RTLq3/d1/fzIOaMLcmQbZCXULy2rL8hRLNcRa2zEmNUqX3hnbcnE\r\noZV3qIW9rPB8PY/YPVb7WGm5vAHF/fHtYXHnUIu6atMg1133e3mB5aZm63n0ZDBUm4g4+rVt\r\n5Su3shBWn5pY/zCv5VzEYky1/mjDh9+xTkt2CWGsuJsv+lQfLC0uBI4JE/F4p0UvOqyH4aFe\r\nBR58+LFed5ki20+dliduSbLqnn766SuuuSH+23DGL7z0d3O+s/HC6z9R/xl5OFvCralz1K7v\r\nr91ffgOZf0XtyOYt3lqWSoot4xlpdJuGp6emd6blx8slN0ySLj8vNrV7TG3ZPzY8ZPTIRg48\r\nQ2h1GxSHbk8ERx45Jrx23rzhZSq+lzm2j0u/yvfHXMS8kHi0ofDYrFx+XC1W3rG8Nn0MwTVE\r\nvtgyKirv3vJKs1hKMYotFlTsdvRNH2yP/B007Fhe8aXUdUs9d8xXsBaHNbbPtuzoRU3lOqqU\r\nVtp+tElj/kpb/KVXAamX0WpvDjng4DicdG1Tl7n2pFiYKr9//pzD4/fsdvic+dn9I5uPPlB/\r\nPNugKCnfslbOyM5jaxl9qF52/nC51Hp5nStq2Cv2ybYfacr8OVX+HPJtOr5qll4240+x86tm\r\n+ZUv33K0MzW9ao5daarli2P9zuaX5w4vwfWX6tFvqKru8Lbv33JHw9bzb9l9v3Pett/VF9fu\r\nv2PO1fHPt8c937/64vl3nfj92kO1c3/HnIvj/vLP7tk29eIuPil76FvX3fWt79e32f2kW4pH\r\n843uuO7q3Ucefdv3L/7WdQ/V7y8q3e/ib825K1rYUNfb9ru4Vld9+4a9soquy46iukNzBBt9\r\nj5O/kzq9+ORuw/o3iI14N64ftmFskC/Ake1Xmw8Ry+OW9y8erW8Tj9U/FqzeYlsSIDAZAr1m\r\nsMlow1Qss1sGm4ptXhRtKqegiEydm9CQqS666KLY/rbbGr+MPp/Q2PJzxXL5DRms8+eQkdAa\r\n2lZltY9eRDtEjzZvukvve0pveFpnqdGX1/w4O35I0Trmjf30ZOynJcWBjj+EcSi/GWr1kVU8\r\n3jKETc/+0MvfkG0JEEgoYC5ib8OXtiYw4QLZepULs+/fm/CSFThoAv/6r//a+ZAbNsgvuHr5\r\ny1/e8EVesZB9MYcwslPzFy73ChtpLdb56LBXpMHmcbPeaql/+2WbxZq7TfIbmaIYX/XV66zY\r\nWjPzSzfqt3aXZJav+ip9G/OY74goX1hWfNVQLxIcci0OvfQa2xIgsAgEEuY9VREg0EIgxsEu\r\nv/qP1eciDgqicbBqZ7p42YiVDKvsEZu13CViUvmh2Cb+GXdGmcXoWUMVMVmxYZfya1hkqtgx\r\nH2preSsPyuWzIhuiYJRW5YjybYqLpltP4GuYFBRTRRoHtkbaXp+MNBKnShN/x04Xal6bPt9l\r\n5DruetMbdqpfgVCahtuqvWN2ipkuu3IIWA55B+vJoXrPsSUBAokFeniFS9wy1REYEAEZrPWJ\r\nlsEWxR9AzEuM1BTRK35ZFPVnF6H98Y9/jAbEZWl5MKt4K8e/1tdaN3zImW904+mHjC7YUZ/W\r\n33BpRr5ht+8Q3zBupasCilbXC6slttik1IjW24/uWGtaz1cNcMgFOVT8w7EZAQKLSmC44alq\r\nEYzEqZLAYAs888wzf7j2xle/ao0llhgzN/iiy37/1G9nztji1XWef/zOYDn98RP58c499w8z\r\ndpszWMfuaKeXQCxCvHp8yerA3zjkXYDDwP8pACCQCbgeTD8gsOgFJvKruBf90WgBAQJjBASw\r\nnIMDB08NBAgUAjKYzkCAAAECBAgQIECAAIF0AjJYOms1EWgnYEqwvkGAAAECBAgQGBwBGWxw\r\nzrUjncICJiNO4ZOjaQQIECBAgACBiRVYYlEtBqJeAgSK9bsWxheENS3DPbF/6kojQIAAAQIE\r\nCBCYIgLGwabIidAMAgQIECBAgAABAgQGQkAGG4jT7CCnuIDrwab4CdI8AgQIECBAgMAECshg\r\nE4ipKAL9CrgerF85+xEgQIAAAQIEFjsBGWyxO2UaTIAAAQIECBAgQIDAYiwggy3GJ0/Tp41A\r\nviaHGwECBAgQIECAwCAIDD/zzDODcJwdjvHGs7981x9OmhCEVV79rjXetNeEFKWQgRK47Ipr\r\nX7vBqxoO+eLfXv7Ub2fO2OLV9fv/8TsDZTL0x0/kxzv33D/M2G3OYB27oyVAgAABAgSmtcCg\r\nZ7Abf/OVCGAzdtpqQs7y3ONmZTHsjV+YkNIUMiACsSDHb6+89jXrv2p4eLh8yDKYDDYgfwIO\r\nkwABAgQIDJrAoM9FvOuqk2fsuMVQDAZOxE8UFQUOWh9yvAQIECBAgAABAgQIVBcY9HGweQe/\r\ndsa7Nq3u1XXLuSedv/Eel3XdrI8NYpDkwUcff+4ySy/ydcynTkv6YJyCu8QJveyKa167wTqL\r\nbhxs3v4b73lWBZqtD5y3z8aN29V2bvlIhRI7bDLZcxErHPQaux334/e+vNbGbOuh/PDb7pgj\r\nlB/e+sADh/ZsQ1sqfHxQve99+7Ef3OnmXWoHk/16+I3NReTH0vRomwNahMfS+9GP7sGhg97g\r\n4LR/Jhj7BPDD1Yqng8UFp92fd4vz3uYpvEUJoypdn0Prm47Zrnbf7S1fckbaENvP3jx/relQ\r\nRepnnelxFNWfMJ36LlZdu//I/q3/tmpPIkPPPP30IP/M+dZrFt6yxwT+RIGT5Bnv1O+57/74\r\n7ySVX73YqdOS6m2eyls+/fTTF//2D/HfhkZeeOnv5nxvxsLrP17/CfeUt7lf2ugDx9xWrcbY\r\ntvrG1YpcWBx4hjD5t9uO+cBGX5rbtp7bbrttYeejjEfz/Ud+6QVw8o9vTA1dDnZ02zEbjt0r\r\n/lWpf1SuK7FBVl3lto3HIes2xa0S2SKQaFFlEpypcaidWtH6z3mxxska3/rJrvbI2M7aeKRj\r\nntY6P8e1enTkvubHxlaU/dXUnk/bVbFIn16nx1E0vSY49eN4Pir134rPDrW/tiViObZB/hka\r\nXmJCZiGOFjLcSPrto36VC99535M/O2/Bfxx0VRn8nQf9+T8O+nNxz5cO+2m70xGJ+rHHHo//\r\ndj1ff7rrqXfu/+c//fWRrlv2t0H1lhTl33DL3z61/0//Y6/D4pf+Kp3Ge9XOabYuYsMxZp+h\r\nRP+cWrf46Gbj5ls20nPj4Ts1PPDBY2+fkMYveoR5+++079zbN95n3ryRYbHuBzZv9llrbDUj\r\nBtFaku0/r3sJk7FFfHIXt2zk66w9s98qtCPfsL5X0abb5866cbVX5GOE7W61I285yjYZh9ZT\r\nmckc5u2/5827HTcvvx24dfyVTNSfRU/H29PGyXB6atXkbpwf85hb6xO1+OPMO/rwG7c+sHlC\r\nQx04PrPP+2o18Obn/QKx0tyKNrVsvM9xu938w4ZXkLCf+n895QNazI7Cqa/W50e3Kr24j76o\r\nll5fa38LnV5kt55q7/B6FRj39rEKwsJ4AzxxP2OXVcjbd/NdT+1z9CMf+86dJ/zmwU9s99Jy\r\now/Y4YULHhvacq8/f+eUm/52z8NDj3U6okcffbTKAf/4zCdis9Mv+WuVjfvbpmJLisK/f8ys\r\nPT607dc/84EDv3NifzXaa9IFKr3Avfy9P87fTh632xoxFaT+3nL0/7JX7vozeQ95ZdKPrVIF\r\na6zWOVJkhbR4q9b6GTYi2Na71OcxjpKM0FVqz2RslOXI/P1VfpbavhMbrXzkdGanPLs3f9kp\r\nveK0fJHJttp3aL/q7+Um42jbl5nMISoaze0b7xPyN86aOzGfTUyaWDKcSTuCvgoe83zWNoIs\r\n7jjxoUDMHK/wd1/RsNWrQPGRQ1FG8bxZymUN6a3xw5p4qRn9y8n3j53znRZREpseR1HxxHbf\r\nbJBOfXuNEYXs5bH+Wtnwa0fJThlsycq37iertkXl8pasWOAElLlEjIMtbPgZXv3bFX+a9x2K\r\nAsfeFjy24LNH/u3a2++NfHXr9T/50J7f+Zf3Hrble3+65S6/2vLjc/f+9sVDjy0YuvOKH594\r\nxls/8qPYuMOxP/54Fq463x567Om8rlN+fd2Dj3Tfvlt5rR+v0pLynnFcC5dY4rhfXxEH++n9\r\nf3X3fQ/2V2/XvXb5+Kdjmyr/7VpUyg0SXuMXLyMVxj66H/zLX7FaizeTkTyGtt686ZKx7sVN\r\n5haVDvn2m1tcFNWyVeVXnnosadrutmN/eFbZoRhLahpPmswDb1d2dpbyW+thzfyD7JFu0moc\r\nrMhlbT8wz8J69UHDRaEQgTqBw6I5sgmodfBwxmSCzmM4iy1OlsCGhs6a3dswfPnpq+/Brfx5\r\ncyTb5lE2v2P0GbV+herYJ6CsMxfJN9920T2zTI+jqP784NRXt+q+5djX29rnmM/qsFdcoNK9\r\nzF62mPACo/LxlpmPg429LRxZDKD7wTXtO9RqHOygXV4Qo1IRioaGVvjBl7b7p3VeVpR84x0L\r\nPvq5X0VEeeGKy3965ozTzjyjQ6VPPPlk1yb9/Lz7I+csuPOKBQsWfOqbvzpqn3fFLpF+W+7Y\r\nt16VljTU+JNfXr3vRzZ9x8Xnzdxxg88f8JMvfuZ9L3vR33U9nF43+OF3vxm7VPlvryVP7vaL\r\n4Vc0b7zzbj/c6eh57y1/oprNv9vtuCkWwVqduTaXqu+08eGNW9eu++755L/ivbtsvfEPj915\r\n4xgJy9JI7yX0XGXlHW6PfBgbx4trdrJG21ZaZmC0rBZtv/22/OGItrVFCipXPMU2XAQOtSrX\r\n2C2boTrFb4sAZ5GLjFniIevd7Vq02OLUJsYeuNusPW8e/fNt6ovZE0N+5GvsVhcoLynQCJMl\r\n16ZnzUKuXsTtt908tNrmWVURplosazRaRFZVPCflyxUUt7n7b3z4yOdGtW3X2G231ZJ3mOlx\r\nFB3YnPo++tSYP4Dib2doaPTXsbN6G9bn6H8crI+25rt0Hg3ru9g+S45LTZquNBr+x+9U/Glx\r\nbVbztSuPDa324mU/8Y7VTz1s63/e4MW7f/X08jF+/agrV1tr2WO/+W+nHfHBLV67ejYm1v72\r\nZIUMdvWf7jjwoy+LABZh749/vCcvLLJWy1vf2lVaUhR+21/nr/DCVSOAXXnllTFAF2378cG7\r\nz9z7O58+6qpPH/bnsy/PLnKbqFs+Ala+Vblnomqf0uXM23//eT3OgWqYLNIwAeTlkTPOKs/Y\r\nz96alObfLXqN9odcTKmsfxhbmjzQML2y+RPXskr7i50iog4dfvS8VpMX69dLTMhwZO/IcSHX\r\narvVpk0cN9rCUluy+Tb1f7aYeBmPze29zqm4R3KHPPevsdt+xQzVqciStyk5ziKnGDNnNFrT\r\n+O9SAxdTnOh/sbJr8XwWlyptNWunFrP6erwoqNqEtEhNDTO9R/YrjYO1m1UwNDRjZNRs5Nn5\r\nx+99xSLpMtPjKNrROfV9dKriD6B8HUbxLqJ9n65X1SmDtXvjnt/fR1vzXSap2AkseeH1n6j4\r\nUwWhmF743GWfHaNSc475SHmvo/bbNO5ca9WV8zvj2rAOt6eeeqprjQd//NVrvGyFKClG2xrq\r\n6rpv9Q2qtCQvLQLYt3948cG7vz1+P++KW6Nh5118xf333z9z5swrzvzJgqEVDjvhd3OvmLBL\r\n1/IRsPKtyj3VD3wytlxiiVjHJaZqTuLFmfHy+8HZm++zcY+fvze8vDbFkVrO2Ld+3XSsW3H4\r\nahN4ncE4qXs65NpV6jGFstIlcVXmIkbjY7Lm0M23Zak3doin5zFTxbs+M4/z6Nvufvux+x6+\r\n2uYzao9nQXSfjRunj268+dYjc5WK+ULlCUP75DvXbt0W5Zisoxh/uYkdsjkotQBWLG4+/kOY\r\nvBIS40zegVQuueWnDa0vO1o8cbIPAGZtddyYy8Cyv/82OawEl03SbjshbTSpjj51jv42+mgU\r\nMvrRVT8fPo2dxLWILgebHkdR+Y9iyKnvajV60WLtE+jm9z/5i2y5nDHXJWy8cbzta7vgXC+X\r\nb1VatW7CC4zGj7vMFsjDa32n4k+bMzRWI4tVldqZnYtsHKztyogj0beK9grlcloqVVhhcbwt\r\niQD2pe+eefC+WQCL22YbrLrBBhussMKqkcVuvXPBdjt+4IozZz7y+NPfO/bacTRmTCNro149\r\n3zNRtfdbTjEftsXKiF2fBbpvkKWjG2+s/+n3PZ2/VT3xBHPgatlKb/PiM9ZY/m3irvTuflQd\r\nt+jpkLNnzzV223kyp1DGh86r/TDeOUS7Gt8JjfNIe9z99psbcnJt+uiYY49kvUYxvtn2Wrra\r\nfKw8Yy6Ot4QO9fyVZfBFdxlLT+coIU5P7ZrUjUfHAOofsrRZlWMxxKmNwK42OgRWcszeImZP\r\n4W1Xucjm35Wv/iyvl9j1Qtus69cSV/FpzuiHTyOZrFhno8USqrH//rOLj3p6HKWZ+N4yPY6i\r\nsotT35mqxcVdI0sIt1tjtXH+TQyXtZiJV7yDffqppyr+dF8uvfbesmJpsVnFAiegzCWGm983\r\nx/VgFX+GhptSShTY+EZ6QcV2ZjtGYGu/OP0zTz+TdYoKEex3J36ivFlL+eZyvnLgQfHzxf0O\r\n+Nyee33iP2eOvyWf+uaZP/7qzkVHXn/99Wd+YLuIYRHA4vaTnxyy9vq7xKM3/PVv7eqq2qQR\r\nkx9+5xsNRVW5p7xLrzVWOR1dtsnOae2CsMmJYPECmC1iOPpCOqFJKSt86PA9Dx+aUnOsejjk\r\nLK61a3zxHqL8ZFxhLmIt78byY/WFEW8+ujYKEjvW33Asuvl8G+8z5uzX4mdtBf3y+7IZW61R\r\nX28lexmu3+qH/cFjh9774wiUcTxbH7jLzbFIWVwUNqE9qvJ7hPFsmMyh9mlA9re3GBklwxnP\r\nGZzofUc/o6r39DafVi1uOBGUsgDWof9l0SJy2MiEhjEf1mVfQdF2kaVY5jOuemk7KNVmZkR9\r\nOl+LuYjzjjtu5KkkH4KJL5LffOisKuvVTnRv6Fre9DiKhsN06rue94bPL4p1ZcZ+UFFbN7q8\r\nHHL907eRTy1KE/47fT/Yks96VoefCkGg9TvPSSo22tNHyS3FK14MFpu13L1BJqYXdm1b3vLY\r\nLFs/sE3Iqr1Fz96p9y3fdcfPf+6zDz/88P0PPHDfggWHHXrI+FvyrT223Pw9h175x9uGatd8\r\nxRTEfAQsD2A/+/YX//b4cxfcees/r7Vyu7oqNqnYfZdPfCZ+r/Lfiaqxq2rXDYpe1CKCTdAE\r\nxWzEfPPZPV8P1vUJKf8CqBjZyT9LnaQlg/tCqHbItS+637rlJ8T1g29+A9B4AUSLN9f1vDvy\r\nyGo71xb0H73yoTyfryvyJG5Qm4M5uoL+SE3ZlX55CMvfCtU+yq4fdi12xXuj2rr22bu3bDZT\r\nP7OLJvGwei568hxqMz2nzgzdnmVih8nD6ac1k7ZPu3Gw6OMd4vNUx6l/PV3XDwBKl7+Nflj3\r\n4xlz943PpzrMEIjdSumtfG6yq89azIyoLz/b8mq7yGw71YPgyBDMPkMj37IYZU/GTI7+utP0\r\nOIrmY3fq++kP9bVG62Ngtdku9XdFDX92ta+FqK0aXZtrki9O2ukSlLjmp8Otn7bW9pmkYiew\r\n5IqDYFWXT6wts9H5qPNHM52O14MND8WoXaXbuw+4urzds9rcmss64EtfXHDffUcc/t3O1VRs\r\nyctf8qIjv/qumV889cpbb2sOYGus9tKhBbdGRZ963790qK5ik/ISfhDjYNX+O1E1Vjof3TbK\r\n03WLWyyzmf/0lUPGFJh9ftvj9WDtm10fhs/fi2ef8dSmadQuLpiwb2+JQy4Ovxtg68c7H3L2\r\nTFn7/twxz5Q33lyaXte8YH3bS/VLI0b9NTb9Xh3mYMZFYVkIy9e5jA+7R8cE8vk4JbLmCe/p\r\nj2R8NU6iQ61XNFwAMFkfVYwPod3ek4gzOQ3ut9Sq42Dl8qc+ToelRVpBlf+YazMYu09vaFND\r\n6++mqK0s2PTN7qWBx/r4e70dtTetxWdEpYSwaJe1mR5H0XD+nfp+nznGXDadfYFd7dUy/n/s\r\nCPGYC8ZeHnNN8hA23GFxhXjfXrFRFVdomPACo3njLPPCo7acsfm6o4dZW1m+3QBXs0Y9hpVW\r\nqJ87++oNP3xOecsPfegzP/jBN6q0Mxi3eve7Z53Y+luM45quuRddNuMNr62yIMq7v3HHiZ8Z\r\n82XQFU9llc16akkUePNtf9t1r5M+8IHNYmn+YgQsC2BDQ6953zFv2fiV+310oyr1VtnmQ5/4\r\nTB7DiluVe6qUPHnbZJ4X/37G6/+54cxe+vsrn/rD52Zssnb9Cw9WP3Ry2tBmnfaWlWVLAh8e\r\nqwR3XlygNrpUG4nv+vlrh0O6affswYUL5865bsYuv5nYY69fIdHUviaL/BhGlmFvzrD1Q621\r\nrn68cdfszYuSS7vWfj1wq1l7xteCjFtnXB5jl6EfPYaGMzZv/w/etvPoLIpOlxKO81yP62j6\r\n35lDB7tBwhn7N5uj5H+5+w3tO7L4aflJbzHGafkdFM39IHtWGBp9Bi890eUO5We+Ln+B8eSw\r\n+ezaS0JtOD2G17IVQks7Fc8e0bb4Yvf6U07LhpaeodOvcFMc82J9FF2fL536rkT5Bo1vFxpe\r\nBUcert3d+B5iBLlTBqvYjMV6swt/+MYZm76qfgjZh+7xaz7yEP/fcdApclc2daw2faz+S1bM\r\n3POv3XDs+8V3v+9DJx7zg4pKW7313bN+3TaDXXjp7zf818Z36g0lv//rf9lrx1X2Pv7Or+64\r\n/EE/+MORX9ykYtXVN4vMUKUl5QIjhu3w6R9+YMft8imIeQCL2zd+etlm//Ky17zqhdVrn35b\r\ndspgV+05Y9NaBoufVx48/Y690xHdskf29xUZ7PzrZvzH2YN17I6WAAECBAgQmNYCndZFbDeB\r\nLb+/7+uSJqnYaE8/JZfnesVyGjH9acklhtf49vDq3x7+h0M7/cQGa3w7Ns52yXccnTc19rqe\r\n2rqInds2StpxXcSl6iOTna4wenp4ib3/586Y07jbnr8eetb8/DS1rL3vMxjH07UlSy65Yvln\r\njdXWu/yUQ3b/t80uP+PUtdbYoHjocx/Y5nXrrRv/HEdjxmjEqFdDUVXumaja+yvnmWdiqZWF\r\n+X/H/sQ1jtEho3cNZ/8dtFtx4IHgRoAAAQIECBCYRgIxDvbkNDqcng/lwp9sMzrXK97pZhfe\r\nFIGqY2n52Ncz8fNM/ZcsamXzpjb8wBnlPbd66/azfn1CxZa9ZuM3/nZe62lXSy75rCuuum6D\r\n9dZ++ulO3xJ2xa2PfeYH9w7decWCBbce843ti28eq9iAKpt1bskxxxxbpZCW27zvfe/te9/F\r\nd8fh4SXmXXL5xq/7p4ULa+tejtwu/f0fnrpurxmb18Zpo1u+ZMwcy8X3eKu2/C+fqY2DDc2d\r\nfe2Mfx/zN1W1BNsRIECAAAECBKakwPBTTw52BvufbUfnemXpKw9gtaGtzgMPkcEigMWb5jyG\r\nZb/X501t+O+nj8lgb9x+1m9OeNZSS3XtAHEuXvPa1/72sstabhkLJ15z/U3rrLV6tnZ/x9t3\r\nT/3Tj08684XLL//rw3fuWmkfG3RuyTHHHrvzzv3Ue/TRR7/vvePNYFWc45CnVLcPz/Mv/O2m\r\nG76m4cxeevkfnrp+nxlbjmSwbMQ175m1n8oLtPRxihfRLvU/opG/qdqnGwuH5p1z3cbv//Ui\r\napJqCRAgQIAAAQITL9Bpbfqua2pPgw3GzPXKZnxlcxGHnjXyS/7Pdj/Pyh/KZzCOTBhbspH0\r\nN785IaCerHCLzS677LIOK8IvvfTS0QW6sr9j4xfGXMRP7bJx1y372yDa0KEl4+mk/bWnvFcF\r\n5myT8Vc0gSUUYi3Wpo+sFb2x3tOKzlZLYtHlptlP8QeYHdfI310ce+crM8fT4exLgAABAgQI\r\nEFgUAgN/oUW8vVtyyZHoFb/kv4/c0zmD1R9dsv4WOXujvOSkvl9cZpnnVOkkL1pxuctO+Pjm\r\nr/mHKhv3t03FlvRX+EDu1Xpt+lVW32berOtrHbLWJ5fI/zvtf4o/wyUvmHX9yqu/eSC7hIMm\r\nQIAAAQIEpq3AcIwJTNuDq3BgF/3s7f3O9Wo7b+r17zm1Qs09bxLraty74MEXrPDcit8E0HMF\r\nlXfo3JJjj+3/erD3jnsuYuWDmEIbhud5F1y22UavbTizl13+hze89p9vuPi7d/2ptqrvQN5W\r\n+Yet13z9xwfy0B00AQIECBAgMG0FBj6D/e92M94U19uMfA1udrFNbaJX3LquTR/bZMtyjFwS\r\nVvuWsHlnX/v6HU+ZjP4S79QfeuTx5ZddeipksCnSkslwTl9m5wyWvj1qJECAAAECBAgQmFSB\r\nJfKVJAb2Z6XVth73XK8x86ZWXO1Nk4QZy0g8Z6klssUkFvX5mjotWeQUE9OAPMzn/y3/TOqf\r\nvsIJECBAgAABAgQWkcDwE088sYiqnirV/umy78+/pfVy8L02caVXvvEfXvvRXvey/YALLLXU\r\nUrPnXbr5xv/aMDH4t1dcFXMRBxzH4RMgQIAAAQIEpp+ADDb9zqkjWswEllxyyfx6sKeffrrc\r\n9Mhgi9mRaC4BAgQIECBAgEAFARmsApJNCEymQLsMNpl1KpsAAQIECBAgQGCRCQw//vigz0Vc\r\nZPYqJlATePazlzp37sVbzHj9E08M9CKlugMBAgQIECBAYEAEIoM9PiCH6jAJTE2BZz/72efM\r\nuXjLTSKD+UBkap4irSJAgAABAgQITKTAwH9H80RiKotAPwLPxNcbDC2s/deNAAECBAgQIEBg\r\n+gsMP/aYcbDpf5od4VQWWGqpZ82ed8nmG7/uySefmsrt1DYCBAgQIECAAIEJERh+9LHHJqQg\r\nhRAg0J/Ac5Zeetb5F2216RseMzG4P0F7ESBAgAABAgQWK4HhRx+VwRarM6ax007gOc9Z+jfn\r\nXfjGzTY0KD3tzq0DIkCAAAECBAi0EHA9mG5BgAABAgQIECBAgACBdALDjzz6aLra1ESAQJPA\r\nMs95Tj4OZmKw3kGAAAECBAgQGASB4YcfkcEG4UQ7xqkrsOwyzzl79gVv2nyjR0wMnrpnScsI\r\nECBAgAABAhMmMPzQw49MWGEKIkCgd4Hlll3m7Nnz3rT5xj4Q6R3PHgQIECBAgACBxU/A9WCL\r\n3znTYgIECBAgQIAAAQIEFl8BGWzxPXdaPk0E4tuZFy4c8h3N0+R0OgwCBAgQIECAQDeB4Qcf\r\nMhexG5LHCUymwPLLLXPWuXO33mLGQw+7OHMyoZVNgAABAgQIEJgaApHBHp4aLdEKAgMqsPxy\r\ny45kMB+IDGgfcNgECBAgQIDAQAmYizhQp9vBTlGBmIvoRoAAAQIECBAgMCACww8YBxuQU+0w\r\np6rAc5db9sxz5rx5y00etEjpVD1H2kWAAAECBAgQmECB4QceNBdxAj0VRaBngecuv+wZs+Zs\r\ns9UmLs7s2c4OBAgQIECAAIHFUGDJPb+w12LY7OnS5Gt+8dlDTjn73lXeuM69x33hyP85Z97Z\r\n+c81S6z/upcvN/Yorz7+q8fOX/v1qy6b3333eT/44qXLv3GdF0wXi8E9jqWfvdRNN9+2xmqv\r\nePyJJwdXwZETIEAgocCY19D5Fxz85bMfefU/vzJ/3c3+efnyW661cvv21F+Rl7u865YJj0lV\r\nBAgsTgLD9xsHS36+4rn76CvyWl+181fevm72yw3HfeG6V+e/RzA7Z8XP/udG8exf2rJbKzd4\r\n10E7rNltI48nFZg7b25e3zPPZP/Lb/HPLbfastyO52XjYOdvs9WmD1ikNOn5URkBAoMrEBns\r\noLs3GX3djNx18D1vjFfh7Jfr1t9ju3WHVlx5pbY+8er8m5U/vMdmK2blnL3SyEv54Ho6cgIE\r\nehWIDPZQr/vYfpwCVx9/YO25uzSENf/C2rP/22oZ7Je1DLZhwydw+V7vHTr1oLtnxMvG3ef9\r\nMP9lnI2x++QJVM5gy53+m/O3fWNkMBODJ+9sKJkAAQKZQPbqefb8wuLFb9pklbPnXN7K5p/e\r\nu+dO67RGK7+OR4GzVtql3ZbQCRAg0FJg+P4HZLDUfSOeu4++crTS7Fl+qJa7PlnLXXdfePDP\r\nh96b/54NhR149F2b1B+69pefPfbapuautM3uu2zRYc5E6uNTX11g7pxsHGzGJjM6izzvuSMZ\r\nzKC0vkOAAIEkAtlr69A7Gz7HbPpw895zDzvqjDurNWj9xtKq7WYrAgQGVGB4gQyW/NQ3jYNl\r\nz/JXrleMjN3ws71Ozj6TW/+dX68Nc82PD+2uWvuzW95z0LHzX/TC+S/ccs/3vKre6Oyhu2fk\r\nm7lNNYHZ582OJm2+2eadG/b8kQxmYvBUO4PaQ4DANBWovc6OvMgWx1j9JTW2PGZouzHzWaap\r\nlMMiQGCSBHw/2CTB9lDs1ccfdeYq7yw9la/5ni/v+fX42WHNSGv/ddiFQ5vt8vV/Gzrm2Plv\r\n3n2XPT6553pXH/iz2mBYPBqvAQJYD9ZpN33mqWfiJ6/ztNNPKyo/5eRTGhqy0BeEpT01aiNA\r\nYKAF7r4nG9y68uR4hZ0/FB+DHvhfe2U/B/1mfnZn7ffaQ51uf7v73oE2dPAECIxPYHjB/eYi\r\njo+w972vPuHA/xmdi7jSm7da6cxZjTMMX7RVfVgs+1hu1kr/fkDtUrHarbx7sVnvrbDHpAuc\r\nddZZUcfWW2+d13TKL07JI1mszLH99tsX1T//ecuddvZ5b3nTZvc/4HqwST8pKiBAgEC8jP7m\r\nzpX+9sIZn11p7kHzZ3x9+/pckuwFt/TPDCqu1j50zt8ayFbZJJuZUruCoP2yHZgJECDQSWD4\r\nvvsfJJRY4JoTvvablT48c7O/L+ode8//zT7sqDu3/Fwx4TB/GTjk0Ln5y8AGO40+NP+8H31j\r\nVu2juvXf8bWRV5HEh6O6dgJnnXV2PLT11m/KNzjlF6eWMti7i71WeN7yeQYzMVhfIkCAwOQL\r\n3PCzvf/4wq3mnzl/469tv+Lswy5Y+ZP/L196I3tJze5sNb3/2l997pwXfKYIXdmL8r1vPKC+\r\n4+S3WQ0ECEw3AXMRp8QZXWezGUOzLrim1pZrTjjqzBe+Y0wAi3tX2nDmAZ/79/WzDa447muf\r\n27v+Uwtgr/r3Az4ngE2JEzm2EU/VbsV92T+eqf80tNZcxCl4+jSJAIFpKTD/vHl3bbXRyNSS\r\nv998JID1drArrbjK0Py7O89W7K1EWxMgMFgCxsEWwfnOR73eOP+o+ozE2hBW9vHbVWu9+YVz\r\nz7xzxugnbVnr4hO7U4ZqY1/NA2i18TEfxS2Ck9hrlSeccHzx/WDxJWHvfe9ORQkxDvbrs2a/\r\ndevNjYP1qmp7AgQI9CpwzQm/Gtr+/608MuQVL6ylqwPGFlaeYNIwDpZ9YFqa0tL0aK+tsj0B\r\nAoMmMPx/C8xFTHrS41n7p3/IaowphTuuXa76/2Z/56iz7lpp6//8j83LE8yv+9We57zg05/I\r\nJp3HvrNW/vDum45OYowMdui3791qf9Mhkp7Eia3s+c9d9rTfnPeWN252/4OPTGzJSiNAgACB\r\nlgLzz//RN+/e+MCx0w5b3lnfvfRaPHrPcfPzl+xOOzoBBAgQaCWw5Of2/DyZlAIrr7PRi+69\r\nYP56u3zoNcsU9V5zwte/9b+/f+QNu+z9+nuP+MGps65dYv1/fely2cM3/O+3577w7Tu+rpbK\r\n5l97wQUX/X7W7AtGfy65/aGhFdffYk1fD5byJE5sXc9Z+tk33nzrmv+w6uNPPDGxJSuNAAEC\r\nBFoKPHLbFRc9/LKt1nlB+dGWd9Y3uOePsy7+3UUjr7/3vGijddde80X3/ubkX96w5HrLXvWz\r\n379wy7eta4EOvY0AgcoCw/fe90DljW04MQLXnPj1c1be5T83qT31X/erz//suhdtNfLPvIba\r\nnUOv3u5TK1/wravW+tTH35A/sY/ZMd/ynou+/e17tvyScbCJOTWLpJS/X+G5vzrz3P/35i0M\r\nSi8Sf5USIDCAAvPn/Phbd2/01XePWX6j+c542T2mNnUlXpEbNs7RRjZY+31eiAewGzlkAuMQ\r\nkMHGgWdXAhMhIINNhKIyCBAgQIAAAQKLjcDw3ffev9g0VkMJTEeBlf7+efk42Pz/Myg9HU+w\r\nYyJAgAABAgQIjBWwNr0eQWDRC1ibftGfAy0gQIAAAQIECKQSGL5z/n2p6lIPAQItBGIcLF+b\r\n3jiY/kGAAAECBAgQGASB4b/c9X+DcJyOkcCUFXjxyn/3izPOefs2W/71bh+ITNmzpGEECBAg\r\nQIAAgQkTGD7okMMnrDAFESBAgAABAgQIECBAgEBHgeGTf3E6IgIECBAgQIAAAQIECBBIIzD8\r\nm9lz09SkFgIECBAgQIAAAQIECBAYnnfx7ygQIECAAAECBAgQIECAQBqB4d9deW2amtRCgAAB\r\nAgQIECBAgAABAr4fTB8gQIAAAQIECBAgQIBAOgEZLJ21mggQIECAAAECBAgQICCD6QMECBAg\r\nQIAAAQIECBBIJyCDpbNWEwECBAgQIECAAAECBGQwfYAAAQIECBAgQIAAAQLpBGSwdNZqIkCA\r\nAAECBAgQIECAgAymDxAgQIAAAQIECBAgQCCdgAyWzlpNBAgQIECAAAECBAgQkMH0AQIECBAg\r\nQIAAAQIECKQTkMHSWauJAAECBAgQIECAAAECMpg+QIAAAQIECBAgQIAAgXQCMlg6azURIECA\r\nAAECBAgQIEBABtMHCBAgQIAAAQIECBAgkE5ABktnrSYCBAgQIECAAAECBAjIYPoAAQIECBAg\r\nQIAAAQIE0gnIYOms1USAAAECBAgQIECAAAEZTB8gQIAAAQIECBAgQIBAOgEZLJ21mggQIECA\r\nAAECBAgQICCD6QMECBAgQIAAAQIECBBIJyCDpbNWEwECBAgQIECAAAECBGQwfYAAAQIECBAg\r\nQIAAAQLpBGSwdNZqIkCAAAECBAgQIECAwPBvr7iGAgECBAgQIECAAAECBAikETAOlsZZLQQI\r\nECBAgAABAgQIEMgEZDD9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBA\r\ngAABAgQIECBAIJ3A8KW/vypdbWoiQIAAAQIECBAgQIDAYAtEBvvDYAs4egIECBAgQIAAAQIE\r\nCKQTGL7osivS1aYmAgQIECBAgAABAgQIDLbA8AWX/G6wBRw9AQIECBAgQIAAAQIE0gkMz7ng\r\n0nS1qYkAAQIECBAgQIAAAQKDLTB87pyLBlvA0RMgQIAAAQIECBAgQCCdwPDZ585NV5uaCBAg\r\nQIAAAQIECBAgMNgCw6effe5gCzh6AgQIECBAgAABAgQIpBMY/sWvz0pXm5oIECBAgAABAgQI\r\nECAw2ALDJ5zyq8EWcPQECBAgQIAAAQIECBBIJzD881PPTFebmggQIECAAAECBAgQIDDYAsML\r\nFy4cbAFHT4AAAQIECBAgQIAAgXQCS6SrSk0ECBAgQIAAAQIECBAYeAEZbOC7AAACBAgQIECA\r\nAAECBBIKyGAJsVVFgAABAgQIECBAgMDAC8hgA98FABAgQIAAAQIECBAgkFBABkuIrSoCBAgQ\r\nIECAAAECBAZeQAYb+C4AgAABAgQIECBAgACBhAIyWEJsVREgQIAAAQIECBAgMPACMtjAdwEA\r\nBAgQIECAAAECBAgkFJDBEmKrigABAgQIECBAgACBgReQwQa+CwAgQIAAAQIECBAgQCChgAyW\r\nEFtVBAgQIECAAAECBAgMvIAMNvBdAAABAgQIECBAgAABAgkFZLCE2KoiQIAAAQIECBAgQGDg\r\nBWSwge8CAAgQIECAAAECBAgQSCgggyXEVhUBAgQIECBAgAABAgMvIIMNfBcAQIAAAQIECBAg\r\nQIBAQgEZLCG2qggQIECAAAECBAgQGHgBGWzguwAAAgQIECBAgAABAgQSCshgCbFVRYAAAQIE\r\nCBAgQIDAwAvIYAPfBQAQIECAAAECBAgQIJBQQAZLiK0qAgQIECBAgAABAgQGXkAGG/guAIAA\r\nAQIECBAgQIAAgYQCMlhCbFURIECAAAECBAgQIDDwAjLYwHcBAAQIECBAgAABAgQIJBSQwRJi\r\nq4oAAQIECBAgQIAAgYEXkMEGvgsAIECAAAECBAgQIEAgoYAMlhBbVQQIECBAgAABAgQIDLyA\r\nDDbwXQAAAQIECBAgQIAAAQIJBWSwhNiqIkCAAAECBAgQIEBg4AVksIHvAgAIECBAgAABAgQI\r\nEEgoIIMlxFYVAQIECBAgQIAAAQIDLyCDDXwXAECAAAECBAgQIECAQEIBGSwhtqoIECBAgAAB\r\nAgQIEBh4ARls4LsAAAIECBAgQIAAAQIEEgrIYAmxVUWAAAECBAgQIECAwMALyGAD3wUAECBA\r\ngAABAgQIECCQUEAGS4itKgIECBAgQIAAAQIEBl5ABhv4LgCAAAECBAgQIECAAIGEAjJYQmxV\r\nESBAgAABAgQIECAw8AIy2MB3AQAECBAgQIAAAQIECCQUkMESYquKAAECBAgQIECAAIGBF5DB\r\nBr4LACBAgAABAgQIECBAIKGADJYQW1UECBAgQIAAAQIECAy8gAw28F0AAAECBAgQIECAAAEC\r\nCQVksITYqiJAgAABAgQIECBAYOAFZLCB7wIACBAgQIAAAQIECBBIKCCDJcRWFQECBAgQIECA\r\nAAECAy8ggw18FwBAgAABAgQIECBAgEBCARksIbaqCBAgQIAAAQIECBAYeAEZbOC7AAACBAgQ\r\nIECAAAECBBIKyGAJsVVFgAABAgQIECBAgMDAC8hgA98FABAgQIAAAQIECBAgkFBABkuIrSoC\r\nBAgQIECAAAECBAZeQAYb+C4AgAABAgQIECBAgACBhAIyWEJsVREgQIAAAQIECBAgMPACMtjA\r\ndwEABAgQIECAAAECBAgkFJDBEmKrigABAgQIECBAgACBgReQwQa+CwAgQIAAAQIECBAgQCCh\r\ngAyWEFtVBAgQIECAAAECBAgMvIAMNvBdAAABAgQIECBAgAABAgkFZLCE2KoiQIAAAQIECBAg\r\nQGDgBWSwge8CAAgQIECAAAECBAgQSCgggyXEVhUBAgQIECBAgAABAgMvIIMNfBcAQIAAAQIE\r\nCBAgQIBAQgEZLCG2qggQIECAAAECBAgQGHgBGWzguwAAAgQIECBAgAABAgQSCshgCbFVRYAA\r\nAQIECBAgQIDAwAvIYAPfBQAQIECAAAECBAgQIJBQQAZLiK0qAgQIECBAgAABAgQGXkAGG/gu\r\nAIAAAQIECBAgQIAAgYQCMlhCbFURIECAAAECBAgQIDDwAjLYwHcBAAQIECBAgAABAgQIJBSQ\r\nwRJiq4oAAQIECBAgQIAAgYEXkMEGvgsAIECAAAECBAgQIEAgoYAMlhBbVQQIECBAgAABAgQI\r\nDLyADDbwXQAAAQIECBAgQIAAAQIJBWSwhNiqIkCAAAECBAgQIEBg4AVksIHvAgAIECBAgAAB\r\nAgQIEEgoIIMlxFYVAQIECBAgQIAAAQIDLyCDDXwXAECAAAECBAgQIECAQEIBGSwhtqoIECBA\r\ngAABAgQIEBh4ARls4LsAAAIECBAgQIAAAQIEEgrIYAmxVUWAAAECBAgQIECAwMALyGAD3wUA\r\nECBAgAABAgQIECCQUEAGS4itKgIECBAgQIAAAQIEBl5ABhv4LgCAAAECBAgQIECAAIGEAjJY\r\nQmxVESBAgAABAgQIECAw8AIy2MB3AQAECBAgQIAAAQIECCQUkMESYquKAAECBAgQIECAAIGB\r\nF5DBBr4LACBAgAABAgQIECBAIKGADJYQW1UECBAgQIAAAQIECAy8gAw28F0AAAECBAgQIECA\r\nAAECCQVksITYqiJAgAABAgQIECBAYOAFZLCB7wIACBAgQIAAAQIECBBIKCCDJcRWFQECBAgQ\r\nIECAAAECAy8ggw18FwBAgAABAgQIECBAgEBCARksIbaqCBAgQIAAAQIECBAYeAEZbOC7AAAC\r\nBAgQIECAAAECBBIKyGAJsVVFgAABAgQIECBAgMDAC8hgA98FABAgQIAAAQIECBAgkFBABkuI\r\nrSoCBAgQIECAAAECBAZeQAYb+C4AgAABAgQIECBAgACBhAIyWEJsVREgQIAAAQIECBAgMPAC\r\nMtjAdwEABAgQIECAAAECBAgkFJDBEmKrigABAgQIECBAgACBgReQwQa+CwAgQIAAAQIECBAg\r\nQCChgAyWEFtVBAgQIECAAAECBAgMvIAMNvBdAAABAgQIECBAgAABAgkFZLCE2KoiQIAAAQIE\r\nCBAgQGDgBWSwge8CAAgQIECAAAECBAgQSCgggyXEVhUBAgQIECBAgAABAgMvIIMNfBcAQIAA\r\nAQIECBAgQIBAQgEZLCG2qggQIECAAAECBAgQGHgBGWzguwAAAgQIECBAgAABAgQSCshgCbFV\r\nRYAAAQIECBAgQIDAwAvIYAPfBQAQIECAAAECBAgQIJBQQAZLiK0qAgQIECBAgAABAgQGXkAG\r\nG/guAIAAAQIECBAgQIAAgYQCMlhCbFURIECAAAECBAgQIDDwAjLYwHcBAAQIECBAgAABAgQI\r\nJBSQwRJiq4oAAQIECBAgQIAAgYEXkMEGvgsAIECAAAECBAgQIEAgoYAMlhBbVQQIECBAgAAB\r\nAgQIDLyADDbwXQAAAQIECBAgQIAAAQIJBWSwhNiqIkCAAAECBAgQIEBg4AVksIHvAgAIECBA\r\ngAABAgQIEEgoIIMlxFYVAQIECBAgQIAAAQIDLyCDDXwXAECAAAECBAgQIECAQEIBGSwhtqoI\r\nECBAgAABAgQIEBh4ARls4LsAAAIECBAgQIAAAQIEEgrIYAmxVUWAAAECBAgQIECAwMALyGAD\r\n3wUAECBAgAABAgQIECCQUEAGS4itKgIECBAgQIAAAQIEBl5ABhv4LgCAAAECBAgQIECAAIGE\r\nAjJYQmxVESBAgAABAgQIECAw8AIy2MB3AQAECBAgQIAAAQIECCQUkMESYquKAAECBAgQIECA\r\nAIGBF5DBBr4LACBAgAABAgQIECBAIKGADJYQW1UECBAgQIAAAQIECAy8gAw28F0AAAECBAgQ\r\nIECAAAECCQVksITYqiJAgAABAgQIECBAYOAFZLCB7wIACBAgQIAAAQIECBBIKCCDJcRWFQEC\r\nBAgQIECAAAECAy8ggw18FwBAgAABAgQIECAwVQVOOOGEqdo07epfQAbr386eBAgQIECAAAEC\r\nBAgQ6FVAButVzPYECBAgQIAAAQIECBDoX0AG69/OngQIECBAgAABAgQIEOhVQAbrVcz2BAgQ\r\nIECAAAECBAgQ6F9ABuvfzp4ECBAgQIAAAQIECBDoVUAG61XM9gQIECBAgAABAgQIEOhfQAbr\r\n386eBAgQIECAAAECBAgQ6FVAButVzPYECBAgQIAAAQIECBDoX0AG69/OngQIECBAgAABAgQI\r\nEOhVQAbrVcz2BAgQIECAAAECBAgQ6F9ABuvfzp4ECBAgQIAAAQIECBDoVUAG61XM9gQIECBA\r\ngAABAgQIEOhfQAbr386eBAgQIECAAAECBAgQ6FVAButVzPYECBAgQIAAAQIECBDoX0AG69/O\r\nngQIECBAgAABAgQIEOhVQAbrVcz2BAgQIECAAAECBAgQ6F9ABuvfzp4ECBAgQIAAAQIECBDo\r\nVUAG61XM9gQIECBAgAABAgQIEOhfQAbr386eBAgQIECAAAECBAgQ6FVAButVzPYECBAgQIAA\r\nAQIECBDoX0AG69/OngQIECBAgAABAgQIEOhVQAbrVcz2BAgQIECAAAECBAgQ6F9ABuvfzp4E\r\nCBAgQIAAAQIECBDoVUAG61XM9gQIECBAgAABAgQIEOhfQAbr386eBAgQIECAAAECBAgQ6FVA\r\nButVzPYECBAgQIAAAQIECBDoX0AG69/OngQIECBAgAABAgQIEOhVQAbrVcz2BAgQIECAAAEC\r\nBAgQ6F9ABuvfzp4ECBAgQIAAAQIECBDoVUAG61XM9gQIECBAgAABAgQIEOhfQAbr386eBAgQ\r\nIECAAAECBAgQ6FVAButVzPYECBAgQIAAAQIECBDoX0AG69/OngQIECBAgAABAgQIEOhVQAbr\r\nVcz2BAgQIECAAAECBAgQ6F9ABuvfzp4ECBAgQIAAAQIECBDoVUAG61XM9gQIECBAgAABAgQI\r\nEOhfQAbr386eBAgQIECAAAECBAgQ6FVAButVzPYECBAgQIAAAQIECBDoX0AG69/OngQIECBA\r\ngAABAgQIEOhVQAbrVcz2BAgQIECAAAECBAgQ6F9ABuvfzp4ECBAgQIAAAQIECBDoVUAG61XM\r\n9gQIECBAgAABAgQIEOhfQAbr386eBAgQIECAAAECBAgQ6FVAButVzPYECBAgQIAAAQIECBDo\r\nX0AG69/OngQIECBAgAABAgQIEOhVQAbrVcz2BAgQIECAAAECBAgQ6F9geOHChf3vbU8CBAgQ\r\nIECAAAECBCZN4IQTTpi0svspePvtt+9nN/uMFZDB9AgCBAgQIECAAAECBAikEzAXMZ21mggQ\r\nIECAAAECBAgQICCD6QMECBAgQIAAAQIECBBIJyCDpbNWEwECBAgQIECAAAECBGQwfYAAAQIE\r\nCBAgQIAAAQLpBGSwdNZqIkCAAAECBAgQIECAgAymDxAgQIAAAQIECBAgQCCdgAyWzlpNBAgQ\r\nIECAAAECBAgQkMH0AQIECBAgQIAAAQIECKQTkMHSWauJAAECBAgQIECAAAECMpg+QIAAAQIE\r\nCBAgQIAAgXQCMlg6azURIECAAAECBAgQIEBABtMHCBAgQIAAAQIECBAgkE5ABktnrSYCBAgQ\r\nIECAAAECBAjIYPoAAQIECBAgQIAAAQIE0gnIYOms1USAAAECBAgQIECAAAEZTB8gQIAAAQIE\r\nCBAgQIBAOgEZLJ21mggQIECAAAECBAgQICCD6QMECBAgQIAAAQIECBBIJyCDpbNWEwECBAgQ\r\nIECAAAECBGQwfYAAAQIECBAgQIAAAQLpBGSwdNZqIkCAAAECBAgQIECAgAymDxAgQIAAAQIE\r\nCBAgQCCdgAyWzlpNBAgQIECAAAECBAgQaJ3B9ttvPzQECBAgQIAAAQIECBAgMOECxsEmnFSB\r\nBAgQIECAAAECBAgQaCswmsGMfekmBAgQIECAAAECBAgQmGyBegYTwCYbWvkECBAgQIAAAQIE\r\nCBAIgeGFCxc2B7B9992XDgECBAgQIECAAAECBAhMuECWwaLQPIaJXhPuq0ACBAgQIECAAAEC\r\nBAiUBepzEaUv3YIAAQIECBAgQIAAAQIJBOrjYAlqUgUBAgQIECBAgAABAgQIWJteHyBAgAAB\r\nAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIE\r\nCBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAAB\r\nAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIE\r\nCBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAAB\r\nAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIE\r\nCBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAAB\r\nAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIE\r\nCBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAAB\r\nAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIE\r\nCBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAAB\r\nAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIE\r\nCBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAAB\r\nAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIE\r\nCBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACB\r\ndAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIE\r\nCMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6\r\nARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIE\r\nZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2A\r\nDJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIy\r\nmD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAG\r\nS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlM\r\nHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOl\r\ns1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYP\r\nECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZ\r\nq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcI\r\nECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azV\r\nRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQI\r\nECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moi\r\nQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQI\r\nECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREg\r\nQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQI\r\nECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAg\r\nQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQI\r\nECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAg\r\nQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQI\r\nECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAg\r\nQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQI\r\nECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAg\r\nQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQI\r\nECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAg\r\nQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQI\r\nECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAg\r\nQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQI\r\nEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAg\r\nQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQI\r\npBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAg\r\nQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTS\r\nCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAg\r\nIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukE\r\nZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQ\r\nwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIy\r\nWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg\r\n+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARks\r\nnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9\r\ngAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbO\r\nWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5A\r\ngAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2et\r\nJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBA\r\ngAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YT\r\nAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBA\r\ngAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kA\r\nAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBA\r\ngAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAA\r\nAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBA\r\ngAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAA\r\nAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBA\r\ngAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAA\r\nAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBA\r\ngAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAA\r\nAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBA\r\ngAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAA\r\nAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBA\r\ngACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAA\r\nAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBA\r\ngEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAA\r\nAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBA\r\nIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAA\r\nAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQ\r\nTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAA\r\nARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgn\r\nIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICA\r\nDKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQ\r\nwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG\r\n0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg\r\n6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPp\r\nAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB0\r\n1moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQB\r\nAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDpr\r\nNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gAB\r\nAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWa\r\nCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAAB\r\nAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0E\r\nCBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAAB\r\nAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIE\r\nCBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAAB\r\nAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIE\r\nCBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAAB\r\nAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIE\r\nCBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAAB\r\nAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIE\r\nCBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAAB\r\nAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIE\r\nCBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAAB\r\nAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIE\r\nCBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAAB\r\nAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIE\r\nCBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAAB\r\nAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIE\r\nCBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACB\r\ndAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIE\r\nCMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6\r\nARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIE\r\nZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2A\r\nDJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIy\r\nmD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAG\r\nS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azVRIAAAQIECBAgQIAAARlM\r\nHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQIECBAgAABAgQIEEgnIIOl\r\ns1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moiQIAAAQIECBAgQICADKYP\r\nECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQIECBAgAABAgQIpBOQwdJZ\r\nq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREgQIAAAQIECBAgQEAG0wcI\r\nECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQIECBAgAABAgTSCchg6azV\r\nRIAAAQIECBAgQIAAARlMHyBAgAABAgQIECBAgEA6ARksnbWaCBAgQIAAAQIECBAgIIPpAwQI\r\nECBAgAABAgQIEEgnIIOls1YTAQIECBAgQIAAAQIEZDB9gAABAgQIECBAgAABAukEZLB01moi\r\nQIAAAQIECBAgQICADKYPECBAgAABAgQIECBAIJ2ADJbOWk0ECBAgQIAAAQIECBCQwfQBAgQI\r\nECBAgAABAgQIpBOQwdJZq4kAAQIECBAgQIAAAQIymD5AgAABAgQIECBAgACBdAIyWDprNREg\r\nQIAAAQIECBAgQEAG0wcIECBAgAABAgQIECCQTkAGS2etJgIECBAgQIAAAQIECMhg+gABAgQI\r\nECBAgAABAgTSCfx/OBDG+e+pN1AAAAAASUVORK5CYIJQSwMEFAAGAAgAAAAhAOlsTo20BgAA\r\nqxsAABUAAAB3b3JkL3RoZW1lL3RoZW1lMS54bWzsWU9vE0cUv1fqdxjtHWIndogjHBQ7Nmkh\r\nEMWGiuN4Pd4dMruzmhkn+IbgiFSpKq04FKnqpYeqLRJIrVT6ZRpKRanEV+ibmd31TrxuEogo\r\naokQiWd/8/6/37xZn79wK2JojwhJedz0qmcrHiKxz4c0DpretX73zIqHpMLxEDMek6Y3IdK7\r\nsPbhB+fxqgpJRBDsj+UqbnqhUsnqwoL0YRnLszwhMTwbcRFhBR9FsDAUeB/kRmxhsVJZXogw\r\njT0U4wjEXh2NqE/Qs59/efHNg99u34N/3lqmo8NAUaykXvCZ6GkNxNlosMPdqkbIiWwzgfYw\r\na3qgbsj3++SW8hDDUsGDplcxP97C2vkFvJpuYmrO3sK+rvlJ96UbhruLRqcIBrnSarfWOLeR\r\nyzcApmZxnU6n3anm8gwA+z54am0pyqx1V6qtTGYBZP+cld2u1Cs1F1+QvzRjc6PVatUbqS1W\r\nqAHZP2sz+JXKcm190cEbkMXXZ/C11nq7vezgDcjil2fw3XON5ZqLN6CQ0Xh3Bq0T2u2m0nPI\r\niLPNUvgKwFcqKXyKgmrIq0urGPFYzau1CN/kogsADWRY0RipSUJG2IdibuNoICjWCvAqwYUn\r\ndsmXM0taF5K+oIlqeh8nGBpjKu/V0+9fPX2MDu48Objz08Hduwd3frSCnF2bOA6Ku15++9lf\r\nD2+jPx9//fL+F+V4WcT//sO9Z79+Xg6E9pma8/zLR388efT8wacvvrtfAl8XeFCE92lEJLpC\r\n9tEOj8AxExXXcjIQJ9vRDzEt7liPA4ljrLWUyO+o0EFfmWCWZsexo0XcCF4XQB9lwIvjm47B\r\nvVCMFS3RfCmMHOAW56zFRWkULmldhTD3x3FQrlyMi7gdjPfKdLdx7OS3M06AN7OydBxvh8Qx\r\nc5vhWOGAxEQh/YzvElLi3Q1KnbhuUV9wyUcK3aCohWlpSPp04FTTdNMmjSAvkzKfId9ObLau\r\noxZnZV5vkD0XCV2BWYnxfcKcMF7EY4WjMpF9HLFiwC9jFZYZ2ZsIv4jrSAWZDgjjqDMkUpbt\r\nuSrA30LSL2FgrNK0b7FJ5CKFortlMi9jzovIDb7bDnGUlGF7NA6L2I/kLpQoRttclcG3uNsh\r\n+jPkAcdz032dEifdR7PBNRo4Jk0LRD8Zi5JcXiTcqd/ehI0wMVQDpO5wdUTjfyJuRoG5rYbT\r\nI26gyudfPSyx+12l7HU4vcp6ZvMQUc/DHabnNhdD+u6z8wYex9sEGmL2iHpPzu/J2fvPk/O8\r\nfj59Sp6yMBC0nkXsoG3G7mju1D2ijPXUhJHL0gzeEs6eYRcW9T5z8ST5LSwJ4U/dyaDAwQUC\r\nmz1IcPUJVWEvxAkM7VVPCwlkKjqQKOESLotmuVS2xsPgr+xVs64vIZY5JFZbfGiXl/RydtfI\r\nxRirAnOhzRQtaQHHVbZ0LhUKvr2Osqo26tjaqsY0Q4qOttxlHWJzKYeQ567BYh5NGGoQjEIQ\r\n5WW4+mvVcNnBjAx13G2OsrSYLJxmimSIhyTNkfZ7NkdVk6SsVmYc0X7YYtAXxyOiVtDW0GLf\r\nQNtxklRUV5ujLsvem2Qpq+BplkDa4XZkcbE5WYz2m16jvlj3kI+TpjeCezL8GSWQdannSMwC\r\neOfkK2HL/shmNl0+zWYjc8xtgiq8+rBxn3HY4YFESLWBZWhLwzxKS4DFWpO1f7EOYT0tB0rY\r\n6HhWLK1AMfxrVkAc3dSS0Yj4qpjswoqOnf2YUikfKyJ64XAfDdhY7GBIvy5V8GdIJbzuMIyg\r\nP8C7OR1t88gl57Tpim/EDM6uY5aEOKVb3aJZJ1u4IaTcBvOpYB74Vmq7ce7krpiWPyVXimX8\r\nP3NFnyfw9mFpqDPgwxtigZHulKbHhQo5sFASUr8rYHAw3AHVAu934TEUFbynNr8F2dO/bc9Z\r\nGaat4RKpdmiABIXzSIWCkG2gJVN9RwirpmeXFclSQaaiCubKxJo9IHuE9TUHLuuz3UMhlLph\r\nk5QGDO5w/bmf0w4aBHrIKfabw2T52Wt74G1PPraZwSmXh81Ak8U/NzEfD6anqt1vtmdnb9ER\r\n/WA6ZtWyrgBlhaOgkbb9a5pwwqPWMtaMx4v1zDjI4qzHsJgPRAm8Q0L6Pzj/qPAZMWWsD9Q+\r\n3wFuRfDlhRYGZQNVfcYOHkgTpF0cwOBkF20xaVE2tOnopKOWHdanPOnmeg8FW1t2nHyfMNj5\r\ncOaqc3rxNIOdRtiJtV2bG2rI7OEWhaVRdpExiTHflhW/yeKDm5DoDfjOYMyUNMUE31MJDDN0\r\nz/QBNL/VaLau/Q0AAP//AwBQSwMEFAAGAAgAAAAhAGmQf8kjBAAAIAoAABEAAAB3b3JkL3Nl\r\ndHRpbmdzLnhtbLRWW1PbOBR+35n9Dxk/b0gcDGU9hA4QUuiEtlPD7rNsn8RadBtJjgm/vkeS\r\nhctyGaY7+2TpXL5zdK4+/njP2WgL2lAp5km6N01GICpZU7GZJ7c3y/FRMjKWiJowKWCe7MAk\r\nH09+/+24yw1Yi2JmhBDC5LyaJ421Kp9MTNUAJ2ZPKhDIXEvNicWr3kw40XetGleSK2JpSRm1\r\nu8lsOj1Mehg5T1ot8h5izGmlpZFr61RyuV7TCvpP1NDvsRs0F7JqOQjrLU40MPRBCtNQZSIa\r\n/1U0fGITQbZvPWLLWZTr0ulbkv1zO6nrR433uOcUlJYVGIMJ4iw8lxMqHmHS7BnQY6j3MNST\r\nYHvioFA9nfrT4Llhz/RfyHbI4oqWmuiQZiwA5wWv8quNkJqUDIuqS7PkBCvqQUo+6nIFusIk\r\nYTkeTpOJY5ToBdboQn6Rtmi1lq2oL4Eg7VX2Ukrbs2tYk5bZG1IWVio0sCXofTbrwWtNOgzT\r\nJ03rv0BbWhFWKFIhKYqmB4fBj5oaxcjuUmr6IIUlbDHoXmAX7aJGhA7yEfY16VlArxqiSYVe\r\n9+bP0YSWLGK6ntGY0m+tqGzrK7fX883k4mTQb1hKfbsKcSGMiAoKfAqDs52FhWwx4O70N61t\r\n44VqF9QVkC2ckerOMGKaU9frntmyG02oj0cgeOmLe4UToWjo2n4Hi+3qZUn9T2vsigq4BLpp\r\n7JXAiLMex8DyYkV2srVeNvR/ESYIPlAQjnUQqP1UuJY1JMhqNX1Waq+WqlPw2cWKesOQxFxh\r\nusE7WNgdw6AJW9AHOBX1Z3wFxTkTIvzrHrzlAAgXmq84H292CpZAMIo4Wf8fYz5nS0bVNcXW\r\n0Veixu76r8YmXR7S5eoO10dt4uE7dl5Mw3Sane4vDw9CLJzYezinWfoh239JZ5FlBx+OXuIs\r\n/8yOsqXjoGe9Pzx3Q/mbPjkOJ5fkEQ8Fck54qSkZXbuxjVo8L/XdGRWRXwKuLfiZU7RlZI7H\r\ngWE4YWyJTRsZvu957ubEAtYell0TvRlwewn9IhUn1edHLDcDQX/CYaeCtU4TFZIXzaVZ1uNR\r\ngZ3HI920ZRG1BI7en1g4Ob9utQOcDOHpcosb2zfBigxzD8T4tnBFCcTYU0PJPHloxudfnDam\r\nn+nCLXq4JkqFaVlu0nnCXPOnTs3ircaF7y/lZtbzZp6HN8fzF1K5x6J0f3AC4YhS/WGg7Ufa\r\n/kDDdRbksoF2EGkHA+0w0vCHo8sbbD7NqLjDCROPjr6WjMkO6stInCfPSCEIpiEKMNVuwWAH\r\nyNwT+o1jRtsc7nGRQU0t/kcpWnNyj3ttOvMLpZfGjYJz8YmsQ3LC6gl1VBOLOUh99p4o+6r/\r\nly9dXkNFsUKLHS+HNbIXHGfU2AIUbhwrNT7ZT80/PPLwa3fyAwAA//8DAFBLAwQUAAYACAAA\r\nACEAF6AWTgIBAACsAQAAFAAAAHdvcmQvd2ViU2V0dGluZ3MueG1sjNDBSgMxEAbgu+A7LLm3\r\n2ZUisnS3IFLxIoL6AGl2dhvMZMJMaqxPb9qqIF56yySZj5l/ufpAX70Di6PQqWZeqwqCpcGF\r\nqVOvL+vZjaokmTAYTwE6tQdRq/7yYpnbDJtnSKn8lKooQVq0ndqmFFutxW4BjcwpQiiPIzGa\r\nVEqeNBp+28WZJYwmuY3zLu31VV1fq2+Gz1FoHJ2FO7I7hJCO/ZrBF5GCbF2UHy2fo2XiITJZ\r\nECn7oD95aFz4ZZrFPwidZRIa07wso08T6QNV2pv6eEKvKrTtwxSIzcaXBHOzUH2Jj2Jy6D5h\r\nTXzLlAVYH66N95SfHu9Lof9k3H8BAAD//wMAUEsDBBQABgAIAAAAIQBSG1YtWwgAAFQ/AAAa\r\nAAAAd29yZC9zdHlsZXNXaXRoRWZmZWN0cy54bWy0W81S40YQvqcq76DSHWxjFnap9W6xsD9U\r\n7Q+7hsp5LI3xBEmjSDKGfYCk8gI55ZJbDrnllBfiOdIzI42FZFndSJyM5Zn++vfrsZl++fo2\r\nDJwbnqRCRhN3tDt0HR550hfR1cS9vHi389x10oxFPgtkxCfuHU/d169+/OHl6ijN7gKeOiAg\r\nSo9WsTdxF1kWHw0GqbfgIUt3Q+ElMpXzbNeT4UDO58Ljg5VM/MHecDTUf8WJ9HiaAtoJi25Y\r\n6ubiwro0GfMIsOYyCVmW7srkahCy5HoZ74D0mGViJgKR3YHs4UEhRk7cZRId5QrtWIXUliOj\r\nUP5S7EhqVmzANTtPpbcMeZRpxEHCA9BBRulCxGszHisNTFwUKt1sM+ImDIp1q3i0X8OzJmNi\r\ncJqwFYRiLbAmboMzfLMpDIwfVHzXUa1KHA23GZNHRImwOmBUeIhZaBIyEVkxj3NN2blQD13y\r\n+30il7FVJxbdpJ1F11aWKkuCZsMDXXll01KSgFrpThcs5q4TekdnV5FM2CwAjVajfUdlpPsK\r\nqMKX3imfs2WQpeptcp7kb/N3+uWdjLLUWR2x1BPiAigEpIQCBH44jlLhwiecpdlxKlj5w7f5\r\nM/X5Qi0sf2h3emlWEvhG+MIdKNBrnkSw8YYFE3fPPEq/2wej4smJ0sssylcFLLoqnvFo53Ja\r\n1m/ifl/snHxWj2YANXFZsjM9VsIG2vjiteSE2LrErKp4DLgFmGZqGBf8yecfpXfN/WkGH0xc\r\nYG398PLsPBEyARqcuC9e5A+nPBQfhO9zRfDFwmghfP7TgkeXKffXz7++0/SaS/TkMsrAMQeH\r\nOopB6r+99XisaA7wIqYi9FltAA6CflDC0QotxVob86CCqh/+UkDm3t6IsuBMtSRH678VSFu9\r\n7Ay0pywqG6DlknQddxex313Es+4ioJ129cVhdxFwEOmqhcmNUlbig5pJzyRfOSfGLwxBbExZ\r\ntaOWRa07aknTuqOWI607ainRuqOWAa07agFv3VGLb+uOWji37vCYJq5qFo21N1CFfSGyAPpc\r\nC9ONOlJd3hScc5awq4TFC0c1xqra28hyupxlOFU1nT6eLKdZItVxscUje6YMHs3Jb8N4wVIB\r\np+o2oI6uv1BHF+d9IuD42QL1zCRfzSZ9qtjIB+cB8/hCBj5PnAt+ayJK2P9ZOtOYefp83qJc\r\nx7B+FFeLzIFTnWq5rZ44aHB6syeM/I8i1T7Y2s0PGkxpE46K4UFDXjYL/8R9sQwL1yBOIweG\r\nzwlhrkBoFbe7aF+FqF7ErVaoAGBMMO2CboKWj9DfNBe6fBVjjP6mFT1SPkJ/07geKV/nx/b4\r\nkpnmFH4WcVDldUiu3RMZyGS+DIoaaKWHQ3IFWwicCeQitvJRJHFIruAH9Okcex58c8PkKTkW\r\nax4loJDDYVB0seFtIQelQnsjgkXkAFWw9ghY3biWAEQm3W/8RqgfcanNQLO0PWu2lvO4wQPQ\r\nglBn6K9LmbWfofcaOA+LchbBzyUpd3Bo44bKw6Ll+WT6HSHG3RofAahbByQAdWuFBKCG/Gg+\r\n89ieiAfp3hwJWGRatl1Mpx2amQ/JzGyBaC2gp76JOH81VG9zLtT7JgKFHKB630SgkKNT6WW2\r\nbyKweuubCKyGrtEcozKnUowi980ykD0JICzqh7wRQP2QNwKoH/JGAHUn73aQ/sgbgUXmBsup\r\nZfJGAOkllK/6FqhM3gggMjcYtst/Myr6npay/cttD+SNQCEHqE7eCBRydJrIG4Gll1AyoYJl\r\nqQ6B1Q95I4D6IW8EUD/kjQDqh7wRQP2QNwKoO3m3g/RH3ggsMjdYTi2TNwKITA8WqEzeCCC9\r\nhMING8lbV/2TkzcChRygOnkjUMjRqRCqPaQisMgBqmBZ8kZg6SWUZMixdHJTjOqHvBEW9UPe\r\nCKB+yBsB1A95I4C6k3c7SH/kjcAic4Pl1DJ5I4DI9GCByuSNACJzw0by1sX45OSNQCEHqE7e\r\nCBRydCqEankOgUUOUAXLkjcCS+dLZ/JGAOkljwWiWNQPeSMs6oe8EUD9kDcCqDt5t4P0R94I\r\nLDI3WE4tkzcCiEwPFqhM3gggMjdsJG9dI09O3ggUcoDq5I1AIUenQqiWvBFY5ABVsCzVIbD6\r\nIW8EkE7MzuSNANJLHgGkq4gSpn7IG2FRP+SNAOpO3u0g/ZE3AovMDZZTy+SNACLTgwUqkzcC\r\niMwN6p4t3BdFX08dNSQB9p5BcasBDbjXECQsYG7gNz7nCUwF8vbbIR0BCwsJiA3pgTXxjZTX\r\nDu5i97ghQdBQYhYIqa903+lbOqVBhPHhlkmCiy8nzgczAFPbp1Pq4c0bmDEqjwupMSc9qgl6\r\nZncxjOzExc1yJQ1GidRcVj4CpBeewUAQ0xM/asQH1ujJp3zQR//LNgfUf8PkkoJYCV+uTuCm\r\neiKDYsvQGPWzVzyYSRhrhF2got4GrxqxrqO3ACW9jCfbdBzWlGy4PK8VXU9uFOoU8112cMms\r\ne3CV02jboGWmLoxv03BU09C40dFXzY176nrB6JbWZH0G3KwYBHQWGPfDH2eRD4at8tktE2r/\r\nlhlR8PkJD4JPTAcrk3Hz0oDPM/PpaKj7Y0UUBDGTYfP+RF8f15psEgAxLytj3iojmpMhWoYz\r\nnuSX0ZsSdm+Dq80tWOM8W22guU5orJeb9XpQSOvSGdc0ecOCQMLQnL73r9WZMZi7+2JnD/Mo\r\nBSK6LtQ7gQroniAw2K2yQhs8HB7vjw73x0YqDGHqsrPjjqPnedZ9X487mmfgAb262RMPytVb\r\nppAgUzWsWKUVbZSCLTPL/e//3f/79/1fv97/8dv9n/84a8urXsoJpewmlpuDrqIn8kjhm/TV\r\n/wAAAP//AwBQSwMEFAAGAAgAAAAhAHzShXlwAQAA4QIAABEACAFkb2NQcm9wcy9jb3JlLnht\r\nbCCiBAEooAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJxSwU7D\r\nMAy9I/EPVe5t0o4hVLWdBGgnJiExBOIWEm8La9IoyVb696Tt2q2CE7fY7/nZfk62+JZlcARj\r\nRaVyFEcEBaBYxYXa5uh1vQzvUGAdVZyWlYIcNWDRori+yphOWWXg2VQajBNgA6+kbMp0jnbO\r\n6RRjy3YgqY08Q3lwUxlJnQ/NFmvK9nQLOCHkFktwlFNHcSsY6lERnSQ5GyX1wZSdAGcYSpCg\r\nnMVxFOMz14GR9s+CDrlgSuEa7Xc6jXupzVkPjuxvK0ZiXddRPevG8PPH+H319NKtGgrVesUA\r\nFRlnqROuhCLD56d/2cPnFzDXp8fAA8wAdZUpKJdCdUVDpvV6D01dGW593STyhRwsM0I7f8Fe\r\ndZLw7JJat/In3Qjg983Q4DfQ9jFwFO1fKGZdozH0+3T29WMCD7whaW/fgLzNHh7XS1QkJCEh\r\nuQmT+TpO0nmSEvLR7jOpbw3qE/I02b8VB4HemumnLH4AAAD//wMAUEsDBBQABgAIAAAAIQAK\r\nRdqw1gcAAGM8AAAPAAAAd29yZC9zdHlsZXMueG1stFvNUtxGEL6nKu+g0h3vHwGb8tqFsR1T\r\n5R/shcpZK82yEyTNRtIa8AMklRfIKZfccsgtp7wQz5GeHmlWSKtVNxIn2NFMf/37tRamn7+8\r\niULnq0hSqeKpO3oydB0R+yqQ8eXUvTh/u/fUddLMiwMvVLGYurcidV+++P6759dHaXYbitQB\r\nAXF6FPlTd5llq6PBIPWXIvLSJ2olYni4UEnkZfAxuRxEXnK1Xu35Klp5mZzLUGa3g/FweODm\r\nYhKKFLVYSF+8Vv46EnGG5weJCEGiitOlXKWFtGuKtGuVBKtE+SJNwegoNPIiT8ZWzGi/JiiS\r\nfqJStciegDEDo9FAi4LjoyH+FoWuE/lHp5exSrx5CM67Hu27L8BzgfJfi4W3DrNUf0zOkvxj\r\n/gl/vFVxljrXR17qS3kOLgUBkQRZ747jVLrwRHhpdpxKr/zwTb6mny/1xvJDe9JPs5LAVzKQ\r\n7kCDXokkhoNfvXDqjs1S+s0ujIqVE62X2ZTvCr34slgT8d7FrKzf1P223Dv5qJfmADV1vWRv\r\ndqyFDdD44mfJCSvrErOr4jGINUR+ZhIQ/CkW75V/JYJZBg+mLiQxLl6cniVSJZBkU/fZs3xx\r\nJiL5TgaB0PlebIyXMhA/LUV8kYpgs/75LSZvLtFX6zgDxxwcYhTDNHhz44uVTjvAiz0doY/6\r\nAAQeyqOEgwqt5UYbs1BBxcVfCsjc21tRlsLTFeqg/juB0Op1Z6CxtqhsAMpl6TrpLmK/u4gf\r\nuosAsurqi8PuIoCXu2phcqOUlfSgZso3yVfOickzQxBbU1afqGVR64la0rSeqOVI64laSrSe\r\nqGVA64lawFtP1OLbeqIWzp0nfA+Jq5pFE/QGqbDPZRYKfX4nAY06Ul3eFJwzL/EuE2+1dHRj\r\nrKq9iyxn63lGUxXp9OFkOcsSFV+2emRsyuDBnPwmWi29VMJbTovrxx1df67fWpwfExm0Qv1g\r\nkq9mE75VbOWDs9DzxVKFgUicc3FjIso4/1E5s5XnQxdsVa5jWN/Ly2XmzJbYclvBDhqc3uwJ\r\nI/+9TNEHO4vpoMGUNuGkGB405GWz8A8ikOuocA3hbeTA8DkjzBUIVHG3i/Z1iOpF3GqFDgDF\r\nBNMu+CagfIL+prnw5esYU/Q3reiB8gn6m8b1QPmYH7vjy2aa1/Cl0yGV1yG7dk9UqJLFOixq\r\noJUeDtkVbCFoJrCL2MonkcQhu4Lv0adz7PvwzY2Sp+xYbHiUgcIOh0HBYqPbwg5KhfZGDIvY\r\nAapgjRlY3biWAcQm3S/iq9R/0+I2A2Rp+67ZWs6TBg9ACyK9Q39eq6z9HXrcwHlUlNMY/lyS\r\nCoeGNmmoPCpank+m3zFi3K3xMYC6dUAGULdWyABqyI/mdx7bE+kg3ZsjA4tNy7aLYdqRmfmQ\r\nzcwWiNcCeuqbhPevhuptzoV63ySgsANU75sEFHZ0Kr3M9k0CVm99k4DV0DWaY1TmVI5R7L5Z\r\nBrJvAgSL+iFvAlA/5E0A6oe8CUDdybsdpD/yJmCxucFyapm8CUC4hfNV3wKVyZsAxOYGw3b5\r\n34yKvodSdn+57YG8CSjsANXJm4DCjk4TeROwcAsnEypYluoIWP2QNwGoH/ImAPVD3gSgfsib\r\nANQPeROAupN3O0h/5E3AYnOD5dQyeROA2PRggcrkTQDCLRxu2EreWPWPTt4EFHaA6uRNQGFH\r\np0Ko9iWVgMUOUAXLkjcBC7dwkiHHwuTmGNUPeRMs6oe8CUD9kDcBqB/yJgB1J+92kP7Im4DF\r\n5gbLqWXyJgCx6cEClcmbAMTmhq3kjcX46ORNQGEHqE7eBBR2dCqEanmOgMUOUAXLkjcBC/Ol\r\nM3kTgHDLQ4E4FvVD3gSL+iFvAlA/5E0A6k7e7SD9kTcBi80NllPL5E0AYtODBSqTNwGIzQ1b\r\nyRtr5NHJm4DCDlCdvAko7OhUCNWSNwGLHaAKlqU6AlY/5E0AwsTsTN4EINzyACCsIk6Y+iFv\r\ngkX9kDcBqDt5t4P0R94ELDY3WE4tkzcBiE0PFqhM3gQgNjfoe7ZwX5R8PXXUkATUewbFrQYy\r\n4LghSFTA3MAvYiESGJIS7bdDOgIWFjIQG9KDauIrpa4c2sXuSUOCkKHkPJQKr3Tf4i2d0iDC\r\n5HDHJMH5pxPnnRmAqZ3DlLp/8wZmjMrjQnrMCSfXQM/sdgUjO6viZrmWBqNEei4rHwHCjacw\r\nEOThxI8e8YE9OPmUD/rgv2xzQPwdJpc0xLUM1PUJ3FRPVFgcGRqjfvaLhbnKlvkYFB4DVRGx\r\nrqO/BCX9TCS7dBzWlGy4PI+KbiY3CnWK+S47uGT23bvKCUvNWmb6wvguDUc1DY0bHbxqbtxT\r\n1wtGt1CTzTvgdsUgoPPQuB9+OY0DMAxGAPF/aibUwY1nRMHzExGGHzwMVqZWzVtDscjM09EQ\r\n+2NFFAQxU1Hz+QSvj6Mm2wSAN8vKmI/aiGY3x+toLpL85ntTwo63uNrcgjXOs9UGmmNCU73c\r\nrNe9QtqUzqSmySsvDBUMzeG9f1Rn7sHc3Sc7e5hHKZTxVaHeCVRA9wSBOVedFWjwcHi8Pzrc\r\nnxipMISJZWfHHUdP86z7thl3NGvgAdzd7Il75eqvU0iQmR5WrNIKGqVhy8xy9/t/d//+fffX\r\nr3d//Hb35z/OxvKql3JCKbvJy80hV9EjeaTwTfrifwAAAP//AwBQSwMEFAAGAAgAAAAhACEp\r\nrt8bAgAAxQUAABIAAAB3b3JkL2ZvbnRUYWJsZS54bWy8lE1u2zAQhfcFegeB+1jUTxzHiByg\r\naQx0k0WTHICmKZsofwQObTVn6LL36A16m/YeHZG028Zw63RRCRCkN+TD8MMbXV1/1CrbCgfS\r\nmoYUI0oyYbhdSrNqyOPD/GxCMvDMLJmyRjTkSQC5nr1+ddVPW2s8ZLjfwFTzhqy976Z5Dnwt\r\nNIOR7YTBYmudZh4/3SrXzH3YdGfc6o55uZBK+qe8pHRMko07xcW2reTireUbLYwP+3MnFDpa\r\nA2vZwc6tP8Wtt27ZOcsFAJ5Zq+inmTR7m6I+MNKSOwu29SM8TB47ygcr3F7Q8KYVyTSfvlsZ\r\n69hCIbu+qMksgcv6qWEaxRum5MLJUOiYsSAKrG2Zaggt6Zye43O4a1oNT5IPDnzNHAi/X0ij\r\n3DIt1dNOhV4CxEInPV/v9C1zcmgolkCusLCBBW3IbU1peTufk6gU2B1Fpb54k5QSm4rXZVKq\r\nvYLJwcaCT1hSRB9U0CftCn3mMToHJL5/+fTt6+cAgil/h3R2Hd9Lfb8x6SgHjAo6RvsKuac7\r\nLnzGaDKO8u+M2Mbb5HsaonSQ6ieicjKZD+pzRAWmOoA9hghh0yJBOxHRg9QCsjvRZ++tZuZI\r\naEoEUmFw6hCe6kWhccE3hOzU0OAxDkNzMTn/L6G5YRqnhx0hMRCIJAYiLxuffyNBx7+OT40/\r\nt7LeK8P4oDJcf8kGJuMyjOEfxifNEcx+AAAA//8DAFBLAwQUAAYACAAAACEAwH1LhnABAADF\r\nAgAAEAAIAWRvY1Byb3BzL2FwcC54bWwgogQBKKAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\nAAAAAAAAAAAAAAAAAAAAAACcUstOwzAQvCPxD1HurVOEAKGNESpCHHhUaoCzZW8SC8e2bLdq\r\n/55N04Ygbvi0M2uPZ8eGu11nsi2GqJ0t88W8yDO00iltmzJ/rx5nN3kWk7BKGGexzPcY8zt+\r\nfgar4DyGpDFmJGFjmbcp+VvGomyxE3FObUud2oVOJIKhYa6utcQHJzcd2sQuiuKK4S6hVahm\r\nfhTMB8XbbfqvqHKy9xc/qr0nwxwq7LwRCflrb8fMlUsdsJGFyiVhKt0hL4geAaxEg5EvgA0F\r\nfLqgDngoYNmKIGSi/Pg1sAmCe++NliJRrvxFy+Ciq1P2dkgg608Dm24BSmWNchN02vcephCe\r\ntR1cDAW5CqIJwrdHayOCtRQGlzQ6r4WJCOyHgKXrvLB7Tj5PFel9xXdfuYc+m+OR3+RkxE+d\r\n2rUXkrz8GnbCw5oCQUXuT2o/BDzRYwTTX0lB2QbVac/fRh/fx/Ar+eJyXtA65HXi6EHG78K/\r\nAQAA//8DAFBLAQItABQABgAIAAAAIQCshlBXjgEAAMAFAAATAAAAAAAAAAAAAAAAAAAAAABb\r\nQ29udGVudF9UeXBlc10ueG1sUEsBAi0AFAAGAAgAAAAhAB6RGrfzAAAATgIAAAsAAAAAAAAA\r\nAAAAAAAAxwMAAF9yZWxzLy5yZWxzUEsBAi0AFAAGAAgAAAAhADYEKig6AQAAPQQAABwAAAAA\r\nAAAAAAAAAAAA6wYAAHdvcmQvX3JlbHMvZG9jdW1lbnQueG1sLnJlbHNQSwECLQAUAAYACAAA\r\nACEA5TdWixcEAAD1CgAAEQAAAAAAAAAAAAAAAABnCQAAd29yZC9kb2N1bWVudC54bWxQSwEC\r\nLQAKAAAAAAAAACEA8t80cZyPAACcjwAAFQAAAAAAAAAAAAAAAACtDQAAd29yZC9tZWRpYS9p\r\nbWFnZTEucG5nUEsBAi0AFAAGAAgAAAAhAOlsTo20BgAAqxsAABUAAAAAAAAAAAAAAAAAfJ0A\r\nAHdvcmQvdGhlbWUvdGhlbWUxLnhtbFBLAQItABQABgAIAAAAIQBpkH/JIwQAACAKAAARAAAA\r\nAAAAAAAAAAAAAGOkAAB3b3JkL3NldHRpbmdzLnhtbFBLAQItABQABgAIAAAAIQAXoBZOAgEA\r\nAKwBAAAUAAAAAAAAAAAAAAAAALWoAAB3b3JkL3dlYlNldHRpbmdzLnhtbFBLAQItABQABgAI\r\nAAAAIQBSG1YtWwgAAFQ/AAAaAAAAAAAAAAAAAAAAAOmpAAB3b3JkL3N0eWxlc1dpdGhFZmZl\r\nY3RzLnhtbFBLAQItABQABgAIAAAAIQB80oV5cAEAAOECAAARAAAAAAAAAAAAAAAAAHyyAABk\r\nb2NQcm9wcy9jb3JlLnhtbFBLAQItABQABgAIAAAAIQAKRdqw1gcAAGM8AAAPAAAAAAAAAAAA\r\nAAAAACO1AAB3b3JkL3N0eWxlcy54bWxQSwECLQAUAAYACAAAACEAISmu3xsCAADFBQAAEgAA\r\nAAAAAAAAAAAAAAAmvQAAd29yZC9mb250VGFibGUueG1sUEsBAi0AFAAGAAgAAAAhAMB9S4Zw\r\nAQAAxQIAABAAAAAAAAAAAAAAAAAAcb8AAGRvY1Byb3BzL2FwcC54bWxQSwUGAAAAAA0ADQBM\r\nAwAAF8IAAAAA\r\n\r\n------=_NextPart_5EA4329B_0FBAC2B8_51634C9D--\r\n.\r\n"
  },
  {
    "path": "Chapter06/code/RecvMail/main.cpp",
    "content": "#include <iostream>\r\n#include \"Pop3Socket.h\"\r\n#include \"Platform.h\"\r\n\r\n//Winsockʼ\r\n#ifdef WIN32\r\nNetworkInitializer windowsNetworkInitializer;\r\n#endif\r\n\r\nconst std::string servername = \"MailAlertSysem\";\r\nconst std::string mailserver = \"pop.163.com\";\r\nconst short mailport = 110;\r\nconst std::string mailuser = \"testformybook@163.com\";\r\nconst std::string mailpassword = \"2019hhxxttxs\";\r\nconst std::string mailto = \"balloonwj@qq.com;analogous_love@qq.com\";\r\n\r\nint main()\r\n{\r\n    Pop3Socket pop3Socket;\r\n    //\r\n    if (!pop3Socket.connect(mailserver.c_str(), mailport))\r\n    {\r\n        std::cout << \"Unable to connect to mail server\" << std::endl;\r\n        return 1;\r\n    }\r\n\r\n    //½\r\n    if (!pop3Socket.logon(mailuser.c_str(), mailpassword.c_str()))\r\n    {\r\n        std::cout << \"Unable to logon to mail server\" << std::endl;\r\n        return 1;\r\n    }\r\n\r\n    //ȡʼб\r\n    if (!pop3Socket.getList())\r\n    {\r\n        std::cout << \"Unable to get mail list from mail server\" << std::endl;\r\n        return 1;\r\n    }\r\n\r\n    long mailCount = pop3Socket.getMailCount();\r\n    std::cout << \"Mail count: \" << mailCount << std::endl;\r\n\r\n    int mailIndex;\r\n    //ʼʼ\r\n    for (int i = 0; i < mailCount; ++i)\r\n    {\r\n        //ʼʼ\r\n        mailIndex = pop3Socket.getMailID(mailCount - i - 1);\r\n        if (!pop3Socket.getHeader(mailIndex))\r\n        {\r\n            if (!pop3Socket.isConnected())\r\n                break;\r\n\r\n            continue;\r\n        }\r\n\r\n        std::wcout << L\"title: \" << pop3Socket.getTitle(mailIndex) << std::endl;\r\n        std::cout << \"from: \" << pop3Socket.getFrom(mailIndex) << std::endl;\r\n        std::cout << \"messageID: \" << pop3Socket.getMessageID(mailIndex) << std::endl;\r\n\r\n        std::cout << \"date: \" << pop3Socket.getDate(mailIndex) << std::endl;\r\n        std::cout << \"time: \" << pop3Socket.getTime(mailIndex) << std::endl;\r\n\r\n\r\n        if (!pop3Socket.getMail(mailIndex))\r\n            break;\r\n\r\n    }\r\n\r\n    pop3Socket.quit();\r\n    pop3Socket.close();\r\n\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/Base64Util.cpp",
    "content": "#include \"Base64Util.h\"\r\n/////////////////////////////////////////////////////////////////////////////////////////////////\r\nstatic const char __DeBase64Tab__[] =\r\n{\r\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r\n    62,        // '+'\r\n    0, 0, 0,\r\n    63,        // '/'\r\n    52, 53, 54, 55, 56, 57, 58, 59, 60, 61,        // '0'-'9'\r\n    0, 0, 0, 0, 0, 0, 0,\r\n    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,\r\n    13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,        // 'A'-'Z'\r\n    0, 0, 0, 0, 0, 0,\r\n    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,\r\n    39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,        // 'a'-'z'\r\n};\r\nstatic const char __EnBase64Tab__[] = { \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\" };\r\n\r\nint Base64Util::encode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest)\r\n{\r\n    char c1, c2, c3;\r\n    int i = 0, lenDest(0), lDiv(lenSource / 3), lMod(lenSource % 3);\r\n    for (; i < lDiv; ++i, lenDest += 4)\r\n    {\r\n        if (lenDest + 4 >= maxDest) \r\n            return 0;\r\n        c1 = *pSource++;\r\n        c2 = *pSource++;\r\n        c3 = *pSource++;\r\n        *pDest++ = __EnBase64Tab__[c1 >> 2];\r\n        *pDest++ = __EnBase64Tab__[((c1 << 4) | (c2 >> 4)) & 0X3F];\r\n        *pDest++ = __EnBase64Tab__[((c2 << 2) | (c3 >> 6)) & 0X3F];\r\n        *pDest++ = __EnBase64Tab__[c3 & 0X3F];\r\n    }\r\n    if (lMod == 1)\r\n    {\r\n        if (lenDest + 4 >= maxDest) return(0);\r\n        c1 = *pSource++;\r\n        *pDest++ = __EnBase64Tab__[(c1 & 0XFC) >> 2];\r\n        *pDest++ = __EnBase64Tab__[((c1 & 0X03) << 4)];\r\n        *pDest++ = chMask;\r\n        *pDest++ = chMask;\r\n        lenDest += 4;\r\n    }\r\n    else if (lMod == 2)\r\n    {\r\n        if (lenDest + 4 >= maxDest) return(0);\r\n        c1 = *pSource++;\r\n        c2 = *pSource++;\r\n        *pDest++ = __EnBase64Tab__[(c1 & 0XFC) >> 2];\r\n        *pDest++ = __EnBase64Tab__[((c1 & 0X03) << 4) | ((c2 & 0XF0) >> 4)];\r\n        *pDest++ = __EnBase64Tab__[((c2 & 0X0F) << 2)];\r\n        *pDest++ = chMask;\r\n        lenDest += 4;\r\n    }\r\n    *pDest = 0;\r\n    return(lenDest);\r\n}\r\n\r\nint Base64Util::decode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest)\r\n{\r\n    int lenDest = 0, nValue = 0, i = 0;\r\n    for (; i < lenSource; i += 4)\r\n    {\r\n        nValue = __DeBase64Tab__[(int)(*pSource)] << 18;\r\n        pSource++;\r\n        nValue += __DeBase64Tab__[(int)*pSource] << 12;\r\n        pSource++;\r\n        if (++lenDest >= maxDest) \r\n            break;\r\n\r\n        *pDest++ = char((nValue & 0X00FF0000) >> 16);\r\n\r\n        if (*pSource != chMask)\r\n        {\r\n            nValue += __DeBase64Tab__[(int)*pSource] << 6;\r\n            pSource++;\r\n            if (++lenDest >= maxDest) \r\n                break;\r\n            *pDest++ = (nValue & 0X0000FF00) >> 8;\r\n\r\n            if (*pSource != chMask)\r\n            {\r\n                nValue += __DeBase64Tab__[(int)*pSource];\r\n                pSource++;\r\n                if (++lenDest >= maxDest) \r\n                    break;\r\n                *pDest++ = nValue & 0X000000FF;\r\n            }\r\n        }\r\n    }\r\n    *pDest = 0;\r\n    return(lenDest);\r\n}\r\n\r\nbool Base64Util::check(char* lpString)\r\n{\r\n    for (; *lpString; ++lpString)\r\n    {\r\n        switch (*lpString)\r\n        {\r\n        case\t'+': *lpString = '@';\tbreak;\r\n        case\t'@': *lpString = '+';\tbreak;\r\n        case\t'=': *lpString = '$';\tbreak;\r\n        case\t'$': *lpString = '=';\tbreak;\r\n        case\t'/': *lpString = '#';\tbreak;\r\n        case\t'#': *lpString = '/';\tbreak;\r\n        default:\r\n            if (*lpString >= 'A' && *lpString <= 'Z')\r\n                * lpString = *lpString - 'A' + 'a';\r\n            else if (*lpString >= 'a' && *lpString <= 'z')\r\n                * lpString = *lpString - 'a' + 'A';\r\n            else if (*lpString >= '0' && *lpString <= '4')\r\n                * lpString = *lpString - '0' + '5';\r\n            else if (*lpString >= '5' && *lpString <= '9')\r\n                * lpString = *lpString - '5' + '0';\r\n            else\r\n                return false;\r\n        }\r\n    }\r\n    return true;\r\n}\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/Base64Util.h",
    "content": "#pragma once\r\n\r\nclass Base64Util final\r\n{\r\nprivate:\r\n    Base64Util() = delete;\r\n    ~Base64Util() = delete;\r\n    Base64Util(const Base64Util& rhs) = delete;\r\n    Base64Util& operator=(const Base64Util& rhs) = delete;\r\n\r\npublic:\r\n    static int encode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest);\r\n    static int decode(char* pDest, const char* pSource, int lenSource, char chMask, int maxDest);\r\n    static bool check(char* lpString);\r\n};\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.6)\r\n\r\nproject (SEND_MAIL_DEMO)\r\n\r\n\r\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++0x -g -Wall -O0 -Wno-unused-variable -pthread\")\r\n\r\nset(srcs \r\nBase64Util.cpp\r\nMailMonitor.cpp\r\nPlatform.cpp\r\nSmtpSocket.cpp\r\nmain.cpp\r\n)\r\n\r\nadd_executable(alertmailsystem ${srcs})\r\nTARGET_LINK_LIBRARIES(alertmailsystem)\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/MailMonitor.cpp",
    "content": "#include \"MailMonitor.h\"\r\n#include <functional>\r\n#include <sstream>\r\n#include <iostream>\r\n#include <string.h>\r\n#include \"SmtpSocket.h\"\r\n\r\nMailMonitor& MailMonitor::getInstance()\r\n{\r\n    static MailMonitor instance;\r\n    return instance;\r\n}\r\n\r\nbool MailMonitor::initMonitorMailInfo(const std::string& servername, const std::string& mailserver, short mailport, const std::string& mailfrom, const std::string& mailfromPassword, const std::string& mailto)\r\n{\r\n    if (servername.empty() || mailserver.empty() || mailport < 0 || mailfrom.empty() || mailfromPassword.empty() || mailto.empty())\r\n    {\r\n        std::cout << \"Mail account info is not config, not start MailAlert\" << std::endl;\r\n        return false;\r\n    }\r\n        \r\n\r\n    m_strMailName = servername;\r\n\r\n    m_strMailServer = mailserver;\r\n    m_nMailPort = mailport;\r\n    m_strFrom = mailfrom;\r\n    m_strFromPassword = mailfromPassword;\r\n\r\n    split(mailto, m_strMailTo, \";\");\r\n\r\n    std::ostringstream osSubject;\r\n    osSubject << \"[\" << m_strMailName << \"]\";\r\n\r\n    SmtpSocket::sendMail(m_strMailServer, m_nMailPort, m_strFrom, m_strFromPassword, m_strMailTo, osSubject.str(), \"You have started Mail Alert System.\");\r\n\r\n    return true;\r\n}\r\n\r\nvoid MailMonitor::uninit()\r\n{\r\n    m_bExit = true;\r\n\r\n    m_cvAlert.notify_one();\r\n\r\n    if (m_spMailAlertThread->joinable())\r\n        m_spMailAlertThread->join();\r\n}\r\n\r\nvoid MailMonitor::wait()\r\n{\r\n    if (m_spMailAlertThread->joinable())\r\n        m_spMailAlertThread->join();\r\n}\r\n\r\nvoid MailMonitor::run()\r\n{\r\n    m_spMailAlertThread.reset(new std::thread(std::bind(&MailMonitor::alertThread, this)));\r\n}\r\n\r\nvoid MailMonitor::alertThread()\r\n{\r\n    m_bRunning = true;\r\n\r\n    while (true)\r\n    {        \r\n        MailItem mailItem;\r\n        {\r\n            std::unique_lock<std::mutex> guard(m_mutexAlert);\r\n            while (m_listMailItemsToSend.empty())\r\n            {\r\n                if (m_bExit)\r\n                    return;\r\n\r\n                m_cvAlert.wait(guard);\r\n            }\r\n\r\n            mailItem = m_listMailItemsToSend.front();\r\n            m_listMailItemsToSend.pop_front();\r\n        }\r\n\r\n        std::ostringstream osSubject;\r\n        osSubject << \"[\" << m_strMailName << \"]\" << mailItem.subject;\r\n        SmtpSocket::sendMail(m_strMailServer, m_nMailPort, m_strFrom, m_strFromPassword, m_strMailTo, osSubject.str(), mailItem.content);\r\n    }// end outer-while-loop\r\n\r\n    m_bRunning = false;\r\n}\r\n\r\nbool MailMonitor::alert(const std::string& subject, const std::string& content)\r\n{\r\n    if (m_strMailServer.empty() || m_nMailPort < 0 || m_strFrom.empty() || m_strFromPassword.empty() || m_strMailTo.empty())\r\n        return false;\r\n\r\n    MailItem mailItem;\r\n    mailItem.subject = subject;\r\n    mailItem.content = content;\r\n\r\n    {\r\n        std::lock_guard<std::mutex> lock_guard(m_mutexAlert);\r\n        m_listMailItemsToSend.push_back(mailItem);\r\n        m_cvAlert.notify_one();\r\n    }\r\n    \r\n    return true;\r\n}\r\n\r\nvoid MailMonitor::split(const std::string& str, std::vector<std::string>& v, const char* delimiter/* = \"|\"*/)\r\n{\r\n    if (delimiter == NULL || str.empty())\r\n        return;\r\n\r\n    std::string buf(str);\r\n    size_t pos = std::string::npos;\r\n    std::string substr;\r\n    int delimiterlength = strlen(delimiter);\r\n    while (true)\r\n    {\r\n        pos = buf.find(delimiter);\r\n        if (pos != std::string::npos)\r\n        {\r\n            substr = buf.substr(0, pos);\r\n            if (!substr.empty())\r\n                v.push_back(substr);\r\n\r\n            buf = buf.substr(pos + delimiterlength);\r\n        }\r\n        else\r\n        {\r\n            if (!buf.empty())\r\n                v.push_back(buf);\r\n            break;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "Chapter06/code/SendMail/MailMonitor.h",
    "content": "/**\r\n * ʼ߳, MailMonitor.h\r\n * zhangyl 2019.05.11\r\n */\r\n\r\n#pragma once\r\n\r\n#include <string>\r\n#include <vector>\r\n#include <list>\r\n#include <memory>\r\n#include <mutex>\r\n#include <condition_variable>\r\n#include <thread>\r\n\r\nstruct MailItem\r\n{\r\n    std::string subject;\r\n    std::string content;\r\n};\r\n\r\nclass MailMonitor final\r\n{\r\npublic:\r\n    static MailMonitor& getInstance();\r\n\r\nprivate:\r\n    MailMonitor() = default;\r\n    ~MailMonitor() = default;\r\n    MailMonitor(const MailMonitor & rhs) = delete;\r\n    MailMonitor& operator=(const MailMonitor & rhs) = delete;\r\n\r\npublic:\r\n    bool initMonitorMailInfo(const std::string& servername, const std::string& mailserver, short mailport, const std::string& mailfrom, const std::string& mailfromPassword, const std::string& mailto);\r\n    void uninit();\r\n    void wait();\r\n\r\n    void run();\r\n\r\n    bool alert(const std::string& subject, const std::string& content);\r\n\r\nprivate:\r\n    void alertThread();\r\n\r\n    void split(const std::string& str, std::vector<std::string>& v, const char* delimiter = \"|\");\r\n    \r\nprivate:\r\n    std::string                             m_strMailName;              //ڱʶһ̨͵ʼ\r\n    std::string                             m_strMailServer;\r\n    short                                   m_nMailPort;\r\n    std::string                             m_strFrom;\r\n    std::string                             m_strFromPassword;\r\n    std::vector<std::string>                m_strMailTo;\r\n\r\n    std::list<MailItem>                     m_listMailItemsToSend;      //д־\r\n    std::shared_ptr<std::thread>            m_spMailAlertThread;\r\n    std::mutex                              m_mutexAlert;\r\n    std::condition_variable                 m_cvAlert;\r\n    bool                                    m_bExit;                    //˳־\r\n    bool                                    m_bRunning;                 //б־\r\n};\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/Platform.cpp",
    "content": "#include \"Platform.h\"\r\n\r\n#ifdef WIN32\r\n\r\nNetworkInitializer::NetworkInitializer()\r\n{\r\n    WORD wVersionRequested = MAKEWORD(2, 2);\r\n    WSADATA wsaData;\r\n    ::WSAStartup(wVersionRequested, &wsaData);   \r\n}\r\n\r\nNetworkInitializer::~NetworkInitializer()\r\n{\r\n    ::WSACleanup();\r\n}\r\n\r\n#endif"
  },
  {
    "path": "Chapter06/code/SendMail/Platform.h",
    "content": "#pragma once\r\n\r\n#include <stdint.h>\r\n\r\n#if defined(__GNUC__)\r\n#pragma GCC diagnostic push\r\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\r\n#elif defined(_MSC_VER)\r\n#pragma warning(disable : 4996)\r\n#endif\r\n\r\n#ifdef WIN32\r\n\r\n#pragma comment(lib, \"Ws2_32.lib\")\r\n#pragma comment(lib, \"Shlwapi.lib\")\r\n\r\n//remove warning C4996 on Windows\r\n//#define _CRT_SECURE_NO_WARNINGS\r\n\r\ntypedef int          socklen_t;\r\n//typedef uint64_t     ssize_t;\r\ntypedef unsigned int in_addr_t;\r\n\r\n//Windows ûЩṹĶ壬Ϊֲ㣬ֶЩṹ\r\n#define  XPOLLIN         1\r\n#define  XPOLLPRI        2\r\n#define  XPOLLOUT        4\r\n#define  XPOLLERR        8 \r\n#define  XPOLLHUP        16\r\n#define  XPOLLNVAL       32\r\n#define  XPOLLRDHUP      8192\r\n\r\n#define  XEPOLL_CTL_ADD  1\r\n#define  XEPOLL_CTL_DEL  2\r\n#define  XEPOLL_CTL_MOD  3\r\n\r\n#pragma pack(push, 1)\r\ntypedef union epoll_data {\r\n    void*    ptr;\r\n    int      fd;\r\n    uint32_t u32;\r\n    uint64_t u64;\r\n} epoll_data_t;\r\n\r\nstruct epoll_event {\r\n    uint32_t     events;    /* Epoll events */\r\n    epoll_data_t data;      /* User data variable */\r\n};\r\n#pragma pack(pop)\r\n\r\n#include <winsock2.h>\r\n#include <ws2tcpip.h>\r\n#include <Windows.h>\r\n#include <Ws2ipdef.h>\r\n#include <io.h>     //_pipe\r\n#include <fcntl.h>  //for O_BINARY\r\n#include <shlwapi.h>\r\n\r\nclass NetworkInitializer\r\n{\r\npublic:\r\n    NetworkInitializer();\r\n    ~NetworkInitializer();\r\n};\r\n\r\n#else\r\n\r\ntypedef int SOCKET;\r\n\r\n#define SOCKET_ERROR -1\r\n\r\n#define closesocket(s) close(s)\r\n\r\n\r\n#include <arpa/inet.h>\r\n\r\n#include <netinet/in.h>\r\n#include <netinet/tcp.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n\r\n#include <unistd.h>\r\n#include <stdint.h>\r\n#include <endian.h>\r\n#include <poll.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <inttypes.h>\r\n#include <errno.h>\r\n#include <dirent.h>\r\n\r\n\r\n#include <sys/socket.h>\r\n#include <sys/select.h>\r\n#include <sys/time.h>\r\n#include <sys/types.h>\r\n#include <sys/eventfd.h>\r\n#include <sys/stat.h>\r\n#include <sys/uio.h>\r\n#include <sys/epoll.h>\r\n#include <sys/syscall.h>\r\n\r\n//for ubuntu readv not found\r\n#ifdef __UBUNTU\r\n#include <sys/uio.h>\r\n#endif \r\n\r\n\r\n#define  XPOLLIN         POLLIN\r\n#define  XPOLLPRI        POLLPRI\r\n#define  XPOLLOUT        POLLOUT\r\n#define  XPOLLERR        POLLERR \r\n#define  XPOLLHUP        POLLHUP\r\n#define  XPOLLNVAL       POLLNVAL\r\n#define  XPOLLRDHUP      POLLRDHUP\r\n\r\n#define  XEPOLL_CTL_ADD  EPOLL_CTL_ADD\r\n#define  XEPOLL_CTL_DEL  EPOLL_CTL_DEL\r\n#define  XEPOLL_CTL_MOD  EPOLL_CTL_MOD\r\n\r\n//Linuxû֮\r\n#define ntohll(x) be64toh(x)\r\n#define htonll(x) htobe64(x)\r\n\r\n#endif\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/SendMail.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.29926.136\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"SendMail\", \"SendMail.vcxproj\", \"{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Release|x64.Build.0 = Release|x64\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}.Release|x86.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {76762611-25F0-4943-B458-47638DAB90E0}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter06/code/SendMail/SendMail.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>16.0</VCProjectVersion>\r\n    <ProjectGuid>{2A4F31FB-9FC1-428B-8F6C-4D68D6500DC8}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>SendMail</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ConformanceMode>true</ConformanceMode>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"Base64Util.cpp\" />\r\n    <ClCompile Include=\"MailMonitor.cpp\" />\r\n    <ClCompile Include=\"main.cpp\" />\r\n    <ClCompile Include=\"Platform.cpp\" />\r\n    <ClCompile Include=\"SmtpSocket.cpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"Base64Util.h\" />\r\n    <ClInclude Include=\"MailMonitor.h\" />\r\n    <ClInclude Include=\"Platform.h\" />\r\n    <ClInclude Include=\"SmtpSocket.h\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter06/code/SendMail/SendMail.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"MailMonitor.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"main.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"Platform.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"SmtpSocket.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"Base64Util.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"MailMonitor.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Platform.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"SmtpSocket.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"Base64Util.h\">\r\n      <Filter>Source Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter06/code/SendMail/SmtpSocket.cpp",
    "content": "#include \"SmtpSocket.h\"\r\n#include <sstream>\r\n#include <time.h>\r\n#include <string.h>\r\n#include \"Base64Util.h\"\r\n#include \"Platform.h\"\r\n\r\nbool SmtpSocket::sendMail(const std::string& server, short port, const std::string& from, const std::string& fromPassword, \r\n                          const std::vector<std::string>& to, const std::string& subject, const std::string& mailData)\r\n{\r\n    size_t atSymbolPos = from.find_first_of(\"@\");\r\n    if (atSymbolPos == std::string::npos)\r\n        return false;\r\n\r\n    std::string strUser = from.substr(0, atSymbolPos);\r\n    \r\n    SmtpSocket smtpSocket;\r\n    //smtp.163.com 25\r\n    if (!smtpSocket.connect(server.c_str(), port))\r\n        return false;\r\n\r\n    //testformybook 2019hhxxttxs\r\n    if (!smtpSocket.logon(strUser.c_str(), fromPassword.c_str()))\r\n        return false;\r\n\r\n    //testformybook@163.com \r\n    if (!smtpSocket.setMailFrom(from.c_str()))\r\n        return false;\r\n\r\n    if (!smtpSocket.setMailTo(to))\r\n        return false;\r\n\r\n    if (!smtpSocket.send(subject, mailData))\r\n        return false;\r\n\r\n    return true;\r\n}\r\n\r\nSmtpSocket::SmtpSocket() : m_bConnected(false), m_hSocket(-1)\r\n{\r\n\r\n}\r\n\r\nSmtpSocket::~SmtpSocket()\r\n{\r\n    quit();\r\n}\r\n\r\nbool SmtpSocket::checkResponse(const char* recvCode)\r\n{\r\n    char recvBuffer[1024] = { 0 };\r\n    long lResult = 0;\r\n    lResult = recv(m_hSocket, recvBuffer, 1024, 0);\r\n    if (lResult == SOCKET_ERROR || lResult < 3)\r\n        return false;\r\n\r\n    return  recvCode[0] == recvBuffer[0] && \\\r\n            recvCode[1] == recvBuffer[1] && \\\r\n            recvCode[2] == recvBuffer[2] ? true : false;\r\n}\r\n\r\nvoid SmtpSocket::quit()\r\n{\r\n    if (m_hSocket < 0)\r\n        return;\r\n\r\n    //˳\r\n    if (::send(m_hSocket, \"QUIT\\r\\n\", strlen(\"QUIT\\r\\n\"), 0) == SOCKET_ERROR)\r\n    {\r\n        closeConnection();\r\n        return;\r\n    }\r\n\r\n    if (!checkResponse(\"221\")) \r\n        return;\r\n}\r\n\r\n\r\nbool SmtpSocket::logon(const char* pszUser, const char* pszPassword)\r\n{\r\n    if (m_hSocket < 0)\r\n        return false;\r\n\r\n    //\"AUTH LOGIN\"\r\n    if (::send(m_hSocket, \"AUTH LOGIN\\r\\n\", strlen(\"AUTH LOGIN\\r\\n\"), 0) == SOCKET_ERROR) \r\n        return false;\r\n\r\n    if (!checkResponse(\"334\")) \r\n        return false;\r\n\r\n    //;base64û\r\n    char szUserEncoded[64] = { 0 };\r\n    Base64Util::encode(szUserEncoded, pszUser, strlen(pszUser), '=', 64);\r\n    strncat(szUserEncoded, \"\\r\\n\", 64);\r\n    //MailLogNormalA(\"[SmtpSocket::Logon] Logon [User:%s].\", lpUser);\r\n    if (::send(m_hSocket, szUserEncoded, strlen(szUserEncoded), 0) == SOCKET_ERROR) \r\n        return false;\r\n\r\n    if (!checkResponse(\"334\")) \r\n        return false;\r\n\r\n    //;base64\r\n    //֤\r\n    char szPwdEncoded[64] = { 0 };\r\n    Base64Util::encode(szPwdEncoded, pszPassword, strlen(pszPassword), '=', 64);\r\n    strncat(szPwdEncoded, \"\\r\\n\", 64);\r\n    //MailLogNormalA(\"[SmtpSocket::Logon] Logon [User:%s] [Pass:*****].\", lpUser);\r\n    if (::send(m_hSocket, szPwdEncoded, strlen(szPwdEncoded), 0) == SOCKET_ERROR)\r\n        return false;\r\n\r\n    if (!checkResponse(\"235\")) \r\n        return false;\r\n\r\n    m_strUser = pszUser;\r\n    m_strPassword = pszPassword;\r\n\r\n    return true;\r\n}\r\n\r\nvoid SmtpSocket::closeConnection()\r\n{\r\n    if (m_hSocket >= 0)\r\n    {\r\n        closesocket(m_hSocket);\r\n        m_hSocket = -1;\r\n        m_bConnected = false;\r\n    }\r\n}\r\n\r\nbool SmtpSocket::connect(const char* pszUrl, short port/* = 25*/)\r\n{\r\n    //MailLogNormalA(\"[SmtpSocket::Connect] Start connect [%s:%d].\", lpUrl, lPort);\r\n    struct sockaddr_in server = { 0 };\r\n    struct hostent* pHostent = NULL;\r\n    unsigned int addr = 0;\r\n\r\n    closeConnection();\r\n    m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\r\n\r\n    if (m_hSocket < 0) \r\n        return false;\r\n\r\n    long tmSend(15 * 1000L), tmRecv(15 * 1000L), noDelay(1);\r\n    setsockopt(m_hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)& noDelay, sizeof(long));\r\n    setsockopt(m_hSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)& tmSend, sizeof(long));\r\n    setsockopt(m_hSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)& tmRecv, sizeof(long));\r\n\r\n    if (inet_addr(pszUrl) == INADDR_NONE)\r\n    {\r\n        pHostent = gethostbyname(pszUrl);\r\n    }\r\n    else\r\n    {\r\n        addr = inet_addr(pszUrl);\r\n        pHostent = gethostbyaddr((char*)& addr, sizeof(addr), AF_INET);\r\n    }\r\n\r\n    if (!pHostent)\r\n        return false;\r\n\r\n    server.sin_family = AF_INET;\r\n    server.sin_port = htons((u_short)port);\r\n    server.sin_addr.s_addr = *((unsigned long*)pHostent->h_addr);\r\n    if (::connect(m_hSocket, (struct sockaddr*) & server, sizeof(server)) == SOCKET_ERROR)\r\n        return false;\r\n\r\n    if (!checkResponse(\"220\")) \r\n        return false;\r\n\r\n    //\"HELO \"+\r\n    //string strTmp=\"HELO \"+SmtpAddr+\"\\r\\n\";\r\n    char szSend[256] = { 0 };\r\n    snprintf(szSend, sizeof(szSend), \"HELO %s\\r\\n\", pszUrl);\r\n    if (::send(m_hSocket, szSend, strlen(szSend), 0) == SOCKET_ERROR) \r\n        return false;\r\n\r\n    if (!checkResponse(\"250\")) \r\n        return false;\r\n\r\n    m_bConnected = true;\r\n\r\n    return true;\r\n}\r\n\r\nbool SmtpSocket::setMailFrom(const char* pszFrom)\r\n{\r\n    if (m_hSocket < 0) \r\n        return false;\r\n\r\n    char szSend[256] = { 0 };\r\n    snprintf(szSend, sizeof(szSend), \"MAIL FROM:<%s>\\r\\n\", pszFrom);\r\n    if (::send(m_hSocket, szSend, strlen(szSend), 0) == SOCKET_ERROR) \r\n        return false;\r\n\r\n    if (!checkResponse(\"250\")) \r\n        return false;\r\n\r\n    m_strFrom = pszFrom;\r\n\r\n    return true;\r\n}\r\n\r\nbool SmtpSocket::setMailTo(const std::vector<std::string>& sendTo)\r\n{\r\n    if (m_hSocket < 0)\r\n        return false;\r\n\r\n    char szSend[256] = { 0 };\r\n\r\n    for (const auto& iter : sendTo)\r\n    {\r\n        snprintf(szSend, sizeof(szSend), \"rcpt to:  <%s>\\r\\n\", iter.c_str());\r\n        if (::send(m_hSocket, szSend, strlen(szSend), 0) == SOCKET_ERROR)\r\n            return false;\r\n\r\n        if (!checkResponse(\"250\"))\r\n            return false;\r\n    }\r\n    \r\n\r\n    m_strTo = sendTo;\r\n\r\n    return true;\r\n}\r\n\r\nbool SmtpSocket::send(const std::string& subject, const std::string& mailData)\r\n{\r\n    if (m_hSocket < 0) \r\n        return false;\r\n\r\n    std::ostringstream osContent;\r\n\r\n    //ע⣺ʼ֮һҪһ\r\n    osContent << \"Date: \" << time(nullptr) << \"\\r\\n\";\r\n    osContent << \"from: \" << m_strFrom << \"\\r\\n\";\r\n    osContent << \"to: \";\r\n    for (const auto& iter : m_strTo)\r\n    {\r\n        osContent << iter << \";\";\r\n    }\r\n    osContent << \"\\r\\n\";\r\n    osContent << \"subject: \" << subject << \"\\r\\n\";\r\n    osContent << \"Content-Type: text/plain; charset=UTF-8\\r\\n\";\r\n    osContent << \"Content-Transfer-Encoding: quoted-printable\\r\\n\\r\\n\";\r\n    osContent << mailData << \"\\r\\n.\\r\\n\";\r\n\r\n    std::string data = osContent.str();\r\n    const char* lpSendBuffer = data.c_str();\r\n\r\n    //\"DATA\\r\\n\"\r\n    if (::send(m_hSocket, \"DATA\\r\\n\", strlen(\"DATA\\r\\n\"), 0) == SOCKET_ERROR)\r\n        return false;\r\n\r\n    if (!checkResponse(\"354\"))\r\n        return false;\r\n\r\n    long dwSend = 0;\r\n    long dwOffset = 0;\r\n    long lTotal = data.length();\r\n    long lResult = 0;\r\n    const long SEND_MAX_SIZE = 1024 * 100000;\r\n    while ((long)dwOffset < lTotal)\r\n    {\r\n        if (lTotal - dwOffset > SEND_MAX_SIZE) \r\n            dwSend = SEND_MAX_SIZE;\r\n        else \r\n            dwSend = lTotal - dwOffset;\r\n\r\n        lResult = ::send(m_hSocket, lpSendBuffer + dwOffset, dwSend, 0);\r\n        if (lResult == SOCKET_ERROR) \r\n            return false;\r\n\r\n        dwOffset += lResult;\r\n    }\r\n\r\n    if (!checkResponse(\"250\")) \r\n        return false;\r\n\r\n    return true;\r\n}"
  },
  {
    "path": "Chapter06/code/SendMail/SmtpSocket.h",
    "content": "/**\r\n * ʼ࣬SmtpSocket.h\r\n * zhangyl 2019.05.11\r\n */\r\n\r\n#pragma once\r\n\r\n#include <string>\r\n#include <vector>\r\n#include \"Platform.h\"\r\n\r\nclass SmtpSocket final\r\n{\r\npublic:\r\n    static bool sendMail(const std::string& server, short port, const std::string& from, const std::string& fromPassword,\r\n                         const std::vector<std::string>& to, const std::string& subject, const std::string& mailData);\r\n\r\npublic:\r\n\tSmtpSocket(void);\r\n\t~SmtpSocket(void);\r\n\t\r\n\tbool isConnected() const { return m_hSocket; }\r\n\tbool connect(const char* pszUrl, short port = 25);\r\n\tbool logon(const char* pszUser, const char* pszPassword);\r\n\tbool setMailFrom(const char* pszFrom);\r\n\tbool setMailTo(const std::vector<std::string>& sendTo);\r\n\tbool send(const std::string& subject, const std::string& mailData);\r\n\r\n\tvoid closeConnection();\r\n\tvoid quit();\t//˳\r\n\r\nprivate:\r\n\t/*\r\n\t:\r\n\t\t֤ӷصǰλʹݽĲǷһ\r\n\r\n\tע:\r\n\t\t211 ϵͳ״̬\r\n\t\t214 Ϣ\r\n\t\t220 ׼\r\n\t\t221 ر\r\n\t\t235 û֤ɹ\r\n\t\t250 \r\n\t\t251 ûڱأתĵ·\r\n\t\t334 ȴû֤Ϣ\r\n\t\t354 ʼʼ\r\n\t\t421 񲻿\r\n\t\t450 δִУæ\r\n\t\t451 ֹش\r\n\t\t452 δִУ洢ռ䲻\r\n\t\t500 ʶԴ\r\n\t\t501 ﷨\r\n\t\t502 ֧\r\n\t\t503 ˳\r\n\t\t504 ֧\r\n\t\t550 δִУ䲻\r\n\t\t551 Ǳû\r\n\t\t552 ֹ洢ռ䲻\r\n\t\t553 δִУȷ\r\n\t\t554 ʧ\r\n\t*/\r\n\tbool checkResponse(const char* recvCode);\r\n\r\nprivate:\t\r\n\tbool\t\t                m_bConnected;\r\n\tSOCKET\t\t                m_hSocket;\r\n    std::string                 m_strUser;\r\n    std::string                 m_strPassword;\r\n    std::string                 m_strFrom;\r\n    std::vector<std::string>    m_strTo;\r\n};"
  },
  {
    "path": "Chapter06/code/SendMail/main.cpp",
    "content": "/**\r\n *  ʼdemo\r\n *  zhangyl 2020.04.09\r\n **/\r\n\r\n#include <iostream>\r\n#include <stdlib.h>\r\n#include \"Platform.h\"\r\n#include \"MailMonitor.h\"\r\n\r\n//Winsockʼ\r\n#ifdef WIN32\r\nNetworkInitializer windowsNetworkInitializer;\r\n#endif\r\n\r\n\r\n#ifndef WIN32\r\nvoid prog_exit(int signo)\r\n{\r\n    std::cout << \"program recv signal [\" << signo << \"] to exit.\" << std::endl;\r\n\r\n    //ֹͣʼͷ\r\n    MailMonitor::getInstance().uninit();\r\n}\r\n\r\n#endif\r\n\r\nconst std::string servername = \"MailAlertSysem\";\r\nconst std::string mailserver = \"smtp.163.com\";\r\nconst short mailport = 25;\r\nconst std::string mailuser = \"testformybook@163.com\";\r\nconst std::string mailpassword = \"2019hhxxttxs\";\r\nconst std::string mailto = \"balloonwj@qq.com;analogous_love@qq.com\";\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n#ifndef WIN32\r\n    //źŴ\r\n    signal(SIGCHLD, SIG_DFL);\r\n    signal(SIGPIPE, SIG_IGN);\r\n    signal(SIGINT, prog_exit);\r\n    signal(SIGTERM, prog_exit);\r\n#endif\r\n  \r\n    \r\n    bool bInitSuccess = MailMonitor::getInstance().initMonitorMailInfo(servername, mailserver, mailport, mailuser, mailpassword, mailto);\r\n    if (bInitSuccess)\r\n        MailMonitor::getInstance().run();\r\n\r\n    const std::string subject = \"Alert Mail\";\r\n    const std::string content = \"This is an alert mail from \" + mailuser;\r\n    MailMonitor::getInstance().alert(subject, content);\r\n\r\n    //ȴʼ߳˳\r\n    MailMonitor::getInstance().wait();\r\n\r\n    return 0;\r\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/CMakeLists.txt",
    "content": "## \n## ʱʹcmake .\n## zhangyl 2019.08.30\n## QQȺ578019391\n##\n\ncmake_minimum_required(VERSION 2.6)\n\nproject (WEB_SOCKET_SERVER)\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++11 -g -Wall -O0 -Wno-unused-variable -pthread\")\n\n\ninclude_directories(\n\t/usr/include/uuid/\n)\n\nlink_directories(\n\t/usr/lib64/\n    ${PROJECT_SOURCE_DIR}/lib\n)\n\nset(net_srcs \nbase/AsyncLog.cpp\nbase/ConfigFileReader.cpp\nbase/Platform.cpp\nbase/Timestamp.cpp\n\nnet/Acceptor.cpp\nnet/Buffer.cpp\nnet/Channel.cpp\nnet/Connector.cpp\nnet/EpollPoller.cpp\nnet/EventLoop.cpp\nnet/EventLoopThread.cpp\nnet/EventLoopThreadPool.cpp\nnet/InetAddress.cpp\nnet/Poller.cpp\nnet/PollPoller.cpp\nnet/ProtocolStream.cpp\nnet/SelectPoller.cpp\nnet/Sockets.cpp\nnet/TcpClient.cpp\nnet/TcpConnection.cpp\nnet/TcpServer.cpp\nnet/Timer.cpp\nnet/TimerQueue.cpp\n)\n\nset(utils_srcs\nutils/DaemonRun.cpp\nutils/StringUtil.cpp\n)\n\nset(zlib_srcs\nzlib1.2.11/zutil.c\nzlib1.2.11/uncompr.c\nzlib1.2.11/trees.c\nzlib1.2.11/inftrees.c\nzlib1.2.11/inflate.c\nzlib1.2.11/inffast.c\nzlib1.2.11/infback.c\nzlib1.2.11/gzwrite.c\nzlib1.2.11/gzread.c\nzlib1.2.11/gzlib.c\nzlib1.2.11/gzclose.c\nzlib1.2.11/deflate.c\nzlib1.2.11/crc32.c\nzlib1.2.11/compress.c\nzlib1.2.11/adler32.c\nzlib1.2.11/ZlibUtil.cpp\n)\n\nset(websocketsrc\nwebsocketsrc/MyWebSocketServer.cpp\nwebsocketsrc/MyWebSocketSession.cpp\n)\n\nset(app_srcs\nappsrc/main.cpp\nappsrc/BusinessSession.cpp\n)\n\nadd_executable(websocketserver ${net_srcs} ${utils_srcs} ${zlib_srcs} ${websocketsrc} ${app_srcs})\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/README.md",
    "content": "# WebSocketServer\nA light and high performance WebSocket Server.\n\n\n\n**Compile**\n\nOn Windows, use Visual Studio 2019 to open `WebSocketServer.sln` and then compile it.\n\nOn Linux, use this following instructions to compile:\n\n```\ncd WebSocketServer/\ncmake .\nmake\n```\n\n\n\n**Test Server**\n\nUse a websocket tool to connect `ws://120.55.94.78:9988`, then send message.\n\n\n\nIf you are interested in High Performance Server Developing, welcome join in QQ Group: `578019391`.\n\n\n\n这是一个轻量级高性能 WebSocket 服务。\n\n**编译方法**\n\nWindows 系统使用 Visual Studio 2019 打开 `WebSocketServer.sln` 文件编译即可。\n\nLinux 下使用如下步骤编译：\n\n```\ncd WebSocketServer/\ncmake .\nmake\n```\n\n\n\n**测试服务器**\n\n你可以使用任意 websocket 工具去连接地址 `ws://120.55.94.78:9988` 然后给它发消息即可。\n\n\n\n如果你对高性能服务器开发感兴趣，欢迎加入 QQ 群：`578019391`。"
  },
  {
    "path": "Chapter06/code/WebSocketServer/WebSocketServer.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29123.88\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"WebSocketServer\", \"WebSocketServer.vcxproj\", \"{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Debug|x64.Build.0 = Debug|x64\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Debug|x86.Build.0 = Debug|Win32\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Release|x64.ActiveCfg = Release|x64\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Release|x64.Build.0 = Release|x64\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Release|x86.ActiveCfg = Release|Win32\n\t\t{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}.Release|x86.Build.0 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {AE874DD1-8D0E-4DF3-BD78-F5F99C4272D1}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/WebSocketServer.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <ProjectGuid>{1CA1FE98-1553-4B6C-9507-0B13A85B0C68}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>WebSocketServer</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>./</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <OutputFile>./$(TargetName)$(TargetExt)</OutputFile>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"appsrc\\BusinessSession.cpp\" />\n    <ClCompile Include=\"appsrc\\main.cpp\" />\n    <ClCompile Include=\"base\\AsyncLog.cpp\" />\n    <ClCompile Include=\"base\\ConfigFileReader.cpp\" />\n    <ClCompile Include=\"base\\Platform.cpp\" />\n    <ClCompile Include=\"base\\Timestamp.cpp\" />\n    <ClCompile Include=\"net\\Acceptor.cpp\" />\n    <ClCompile Include=\"net\\Buffer.cpp\" />\n    <ClCompile Include=\"net\\Channel.cpp\" />\n    <ClCompile Include=\"net\\Connector.cpp\" />\n    <ClCompile Include=\"net\\EpollPoller.cpp\" />\n    <ClCompile Include=\"net\\EventLoop.cpp\" />\n    <ClCompile Include=\"net\\EventLoopThread.cpp\" />\n    <ClCompile Include=\"net\\EventLoopThreadPool.cpp\" />\n    <ClCompile Include=\"net\\InetAddress.cpp\" />\n    <ClCompile Include=\"net\\Poller.cpp\" />\n    <ClCompile Include=\"net\\PollPoller.cpp\" />\n    <ClCompile Include=\"net\\ProtocolStream.cpp\" />\n    <ClCompile Include=\"net\\SelectPoller.cpp\" />\n    <ClCompile Include=\"net\\Sockets.cpp\" />\n    <ClCompile Include=\"net\\TcpClient.cpp\" />\n    <ClCompile Include=\"net\\TcpConnection.cpp\" />\n    <ClCompile Include=\"net\\TcpServer.cpp\" />\n    <ClCompile Include=\"net\\Timer.cpp\" />\n    <ClCompile Include=\"net\\TimerQueue.cpp\" />\n    <ClCompile Include=\"utils\\DaemonRun.cpp\" />\n    <ClCompile Include=\"utils\\StringUtil.cpp\" />\n    <ClCompile Include=\"websocketsrc\\MyWebSocketServer.cpp\" />\n    <ClCompile Include=\"websocketsrc\\MyWebSocketSession.cpp\" />\n    <ClCompile Include=\"zlib1.2.11\\adler32.c\" />\n    <ClCompile Include=\"zlib1.2.11\\compress.c\" />\n    <ClCompile Include=\"zlib1.2.11\\crc32.c\" />\n    <ClCompile Include=\"zlib1.2.11\\deflate.c\" />\n    <ClCompile Include=\"zlib1.2.11\\gzclose.c\" />\n    <ClCompile Include=\"zlib1.2.11\\gzlib.c\" />\n    <ClCompile Include=\"zlib1.2.11\\gzread.c\" />\n    <ClCompile Include=\"zlib1.2.11\\gzwrite.c\" />\n    <ClCompile Include=\"zlib1.2.11\\infback.c\" />\n    <ClCompile Include=\"zlib1.2.11\\inffast.c\" />\n    <ClCompile Include=\"zlib1.2.11\\inflate.c\" />\n    <ClCompile Include=\"zlib1.2.11\\inftrees.c\" />\n    <ClCompile Include=\"zlib1.2.11\\trees.c\" />\n    <ClCompile Include=\"zlib1.2.11\\uncompr.c\" />\n    <ClCompile Include=\"zlib1.2.11\\zlibdemo.c\" />\n    <ClCompile Include=\"zlib1.2.11\\ZlibUtil.cpp\" />\n    <ClCompile Include=\"zlib1.2.11\\zutil.c\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"appsrc\\BusinessSession.h\" />\n    <ClInclude Include=\"base\\AsyncLog.h\" />\n    <ClInclude Include=\"base\\ConfigFileReader.h\" />\n    <ClInclude Include=\"base\\Platform.h\" />\n    <ClInclude Include=\"base\\RAIIWrapper.h\" />\n    <ClInclude Include=\"base\\Singleton.h\" />\n    <ClInclude Include=\"base\\Timestamp.h\" />\n    <ClInclude Include=\"net\\Acceptor.h\" />\n    <ClInclude Include=\"net\\Buffer.h\" />\n    <ClInclude Include=\"net\\Callbacks.h\" />\n    <ClInclude Include=\"net\\Channel.h\" />\n    <ClInclude Include=\"net\\Connector.h\" />\n    <ClInclude Include=\"net\\Endian.h\" />\n    <ClInclude Include=\"net\\EpollPoller.h\" />\n    <ClInclude Include=\"net\\EventLoop.h\" />\n    <ClInclude Include=\"net\\EventLoopThread.h\" />\n    <ClInclude Include=\"net\\EventLoopThreadPool.h\" />\n    <ClInclude Include=\"net\\InetAddress.h\" />\n    <ClInclude Include=\"net\\Poller.h\" />\n    <ClInclude Include=\"net\\PollPoller.h\" />\n    <ClInclude Include=\"net\\ProtocolStream.h\" />\n    <ClInclude Include=\"net\\SelectPoller.h\" />\n    <ClInclude Include=\"net\\Sockets.h\" />\n    <ClInclude Include=\"net\\TcpClient.h\" />\n    <ClInclude Include=\"net\\TcpConnection.h\" />\n    <ClInclude Include=\"net\\TcpServer.h\" />\n    <ClInclude Include=\"net\\Timer.h\" />\n    <ClInclude Include=\"net\\TimerId.h\" />\n    <ClInclude Include=\"net\\TimerQueue.h\" />\n    <ClInclude Include=\"utils\\DaemonRun.h\" />\n    <ClInclude Include=\"utils\\StringUtil.h\" />\n    <ClInclude Include=\"websocketsrc\\MyWebSocketServer.h\" />\n    <ClInclude Include=\"websocketsrc\\MyWebSocketSession.h\" />\n    <ClInclude Include=\"websocketsrc\\WebSocketHandshake.h\" />\n    <ClInclude Include=\"zlib1.2.11\\crc32.h\" />\n    <ClInclude Include=\"zlib1.2.11\\deflate.h\" />\n    <ClInclude Include=\"zlib1.2.11\\gzguts.h\" />\n    <ClInclude Include=\"zlib1.2.11\\inffast.h\" />\n    <ClInclude Include=\"zlib1.2.11\\inffixed.h\" />\n    <ClInclude Include=\"zlib1.2.11\\inflate.h\" />\n    <ClInclude Include=\"zlib1.2.11\\inftrees.h\" />\n    <ClInclude Include=\"zlib1.2.11\\trees.h\" />\n    <ClInclude Include=\"zlib1.2.11\\zconf.h\" />\n    <ClInclude Include=\"zlib1.2.11\\zlib.h\" />\n    <ClInclude Include=\"zlib1.2.11\\ZlibUtil.h\" />\n    <ClInclude Include=\"zlib1.2.11\\zutil.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"zlib1.2.11\\文件概览.txt\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter06/code/WebSocketServer/WebSocketServer.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"appsrc\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"base\">\n      <UniqueIdentifier>{6e63e9cf-c248-4b6a-b868-beec0e100de5}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"net\">\n      <UniqueIdentifier>{018c4d1d-000d-49f1-8a77-3a74777b9829}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"utils\">\n      <UniqueIdentifier>{8761e956-1a59-483f-983e-2536228d70fd}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"websocketsrc\">\n      <UniqueIdentifier>{9bd6f171-2927-4859-84f6-5cabeb9e46f1}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"zlib1.2.11\">\n      <UniqueIdentifier>{50d03c8f-0a49-4aa8-8dea-e2c3dca3259a}</UniqueIdentifier>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"appsrc\\BusinessSession.cpp\">\n      <Filter>appsrc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"appsrc\\main.cpp\">\n      <Filter>appsrc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"base\\AsyncLog.cpp\">\n      <Filter>base</Filter>\n    </ClCompile>\n    <ClCompile Include=\"base\\ConfigFileReader.cpp\">\n      <Filter>base</Filter>\n    </ClCompile>\n    <ClCompile Include=\"base\\Platform.cpp\">\n      <Filter>base</Filter>\n    </ClCompile>\n    <ClCompile Include=\"base\\Timestamp.cpp\">\n      <Filter>base</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Acceptor.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Buffer.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Channel.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Connector.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\EpollPoller.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\EventLoop.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\EventLoopThread.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\EventLoopThreadPool.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\InetAddress.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Poller.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\PollPoller.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\ProtocolStream.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\SelectPoller.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Sockets.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\TcpClient.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\TcpConnection.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\TcpServer.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\Timer.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"net\\TimerQueue.cpp\">\n      <Filter>net</Filter>\n    </ClCompile>\n    <ClCompile Include=\"utils\\DaemonRun.cpp\">\n      <Filter>utils</Filter>\n    </ClCompile>\n    <ClCompile Include=\"utils\\StringUtil.cpp\">\n      <Filter>utils</Filter>\n    </ClCompile>\n    <ClCompile Include=\"websocketsrc\\MyWebSocketServer.cpp\">\n      <Filter>websocketsrc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"websocketsrc\\MyWebSocketSession.cpp\">\n      <Filter>websocketsrc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\adler32.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\compress.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\crc32.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\deflate.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\gzclose.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\gzlib.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\gzread.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\gzwrite.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\infback.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\inffast.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\inflate.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\inftrees.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\trees.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\uncompr.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\zlibdemo.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\ZlibUtil.cpp\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n    <ClCompile Include=\"zlib1.2.11\\zutil.c\">\n      <Filter>zlib1.2.11</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"appsrc\\BusinessSession.h\">\n      <Filter>appsrc</Filter>\n    </ClInclude>\n    <ClInclude Include=\"base\\AsyncLog.h\">\n      <Filter>base</Filter>\n    </ClInclude>\n    <ClInclude Include=\"base\\ConfigFileReader.h\">\n      <Filter>base</Filter>\n    </ClInclude>\n    <ClInclude Include=\"base\\Platform.h\">\n      <Filter>base</Filter>\n    </ClInclude>\n    <ClInclude Include=\"base\\RAIIWrapper.h\">\n      <Filter>base</Filter>\n    </ClInclude>\n    <ClInclude Include=\"base\\Singleton.h\">\n      <Filter>base</Filter>\n    </ClInclude>\n    <ClInclude Include=\"base\\Timestamp.h\">\n      <Filter>base</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Acceptor.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Buffer.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Callbacks.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Channel.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Connector.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Endian.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\EpollPoller.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\EventLoop.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\EventLoopThread.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\EventLoopThreadPool.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\InetAddress.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Poller.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\PollPoller.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\ProtocolStream.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\SelectPoller.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Sockets.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\TcpClient.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\TcpConnection.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\TcpServer.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\Timer.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\TimerId.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"net\\TimerQueue.h\">\n      <Filter>net</Filter>\n    </ClInclude>\n    <ClInclude Include=\"utils\\DaemonRun.h\">\n      <Filter>utils</Filter>\n    </ClInclude>\n    <ClInclude Include=\"utils\\StringUtil.h\">\n      <Filter>utils</Filter>\n    </ClInclude>\n    <ClInclude Include=\"websocketsrc\\MyWebSocketServer.h\">\n      <Filter>websocketsrc</Filter>\n    </ClInclude>\n    <ClInclude Include=\"websocketsrc\\MyWebSocketSession.h\">\n      <Filter>websocketsrc</Filter>\n    </ClInclude>\n    <ClInclude Include=\"websocketsrc\\WebSocketHandshake.h\">\n      <Filter>websocketsrc</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\crc32.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\deflate.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\gzguts.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\inffast.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\inffixed.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\inflate.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\inftrees.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\trees.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\zconf.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\zlib.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\ZlibUtil.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n    <ClInclude Include=\"zlib1.2.11\\zutil.h\">\n      <Filter>zlib1.2.11</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <Text Include=\"zlib1.2.11\\文件概览.txt\">\n      <Filter>zlib1.2.11</Filter>\n    </Text>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter06/code/WebSocketServer/appsrc/BusinessSession.cpp",
    "content": "/***************************************************************\n * Ȩ :\n * ļ   : BusinessSession.h\n *      : zhangyl\n * 汾     : 1.0.0.0\n *  : 2019.03.29\n *      :\n ***************************************************************/\n#include \"BusinessSession.h\"\n\nstd::string BusinessSession::m_strWelcomeMsg = \"Welcome to WebSocket Server, contact: balloonwj@qq.com\";\n\nBusinessSession::BusinessSession(std::shared_ptr<TcpConnection>& conn) : MyWebSocketSession(conn)\n{\n    \n}\n\nbool BusinessSession::onMessage(const std::string& strClientMsg)\n{   \n    //TODO: յϢﴦһϢ\n    send(strClientMsg);\n\n    return true;\n}\n\nvoid BusinessSession::onConnect()\n{ \n    //ͻӭ\n    sendWelcomeMsg();     \n}\n\nvoid BusinessSession::sendWelcomeMsg()\n{\n    send(BusinessSession::m_strWelcomeMsg);\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/appsrc/BusinessSession.h",
    "content": "/***************************************************************\n * Ȩ :\n * ļ   : BusinessSession.h\n *      : zhangyl\n * 汾     : 1.0.0.0\n *  : 2019.03.29\n *      :\n ***************************************************************/\n\n#ifndef __BUSINESS_SESSION_H__\n#define __BUSINESS_SESSION_H__\n\n#include <string>\n\n#include \"../websocketsrc/MyWebSocketSession.h\"\n\n/** \n * ʹ÷ԼBusinessSession࣬רעҵ\n * BussinessSessionרעҵWebSocketSessionרעͨű\n */\nclass BusinessSession : public MyWebSocketSession\n{\npublic:\n    BusinessSession(std::shared_ptr<TcpConnection>& conn);\n    virtual ~BusinessSession() = default;\n\npublic:\n    void onConnect() override;\n    bool onMessage(const std::string& strClientMsg) override;\n \nprivate:\n    void sendWelcomeMsg();\n\nprivate:\n    static std::string          m_strWelcomeMsg;        //ӭϢ\n};\n\n#endif //!__BUSINESS_SESSION_H__\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/appsrc/main.cpp",
    "content": "/**\n *  ں\n *  zhangyl 2017.03.09\n **/\n\n#include <iostream>\n#include <stdlib.h>\n#include \"../base/Platform.h\"\n#include \"../base/Singleton.h\"\n#include \"../base/AsyncLog.h\"\n#include \"../net/EventLoop.h\"\n#include \"../net/EventLoopThreadPool.h\"\n#include \"../utils/DaemonRun.h\"\n#include \"../websocketsrc/MyWebSocketServer.h\"\n\n\nusing namespace net;\n\n//Winsockʼ\n#ifdef WIN32\nNetworkInitializer windowsNetworkInitializer;\n#endif\n\nEventLoop g_mainLoop;\n\n#ifndef WIN32\nvoid prog_exit(int signo)\n{\n    std::cout << \"program recv signal [\" << signo << \"] to exit.\" << std::endl;\n\n\n    g_mainLoop.quit();\n\n    LOGC(\"app finish......\");\n\n    CAsyncLog::Uninit();\n}\n#endif\n\nint main(int argc, char* argv[])\n{\n#ifndef WIN32\n    //źŴ\n    signal(SIGCHLD, SIG_DFL);\n    signal(SIGPIPE, SIG_IGN);\n    signal(SIGINT, prog_exit);\n    signal(SIGTERM, prog_exit);\n\n    int ch;\n    bool bdaemon = false;\n    while ((ch = getopt(argc, argv, \"dv\")) != -1)\n    {\n        switch (ch)\n        {\n        case 'd':\n            bdaemon = true;\n            break;\n        }\n    }\n\n    if (bdaemon)\n        daemon_run();\n\n#endif\n\n    CAsyncLog::Init();\n\n    Singleton<MyWebSocketServer>::Instance().init(\"0.0.0.0\", 9988, &g_mainLoop);\n\n    LOGC(\"websocket server initialization complete.\");\n\n    g_mainLoop.loop();\n\n    return 0;\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/AsyncLog.cpp",
    "content": "/**\n * @desc:   첽־࣬AsyncLog.cpp\n * @author: zhangyl\n * @date:   2019.04.13\n */\n#include \"AsyncLog.h\"\n#include <ctime>\n#include <time.h>\n#include <sys/timeb.h>\n#include <stdio.h>\n#include <string.h>\n#include <sstream>\n#include <iostream>\n#include <stdarg.h>\n#include \"../base/Platform.h\"\n\n#define MAX_LINE_LENGTH   256\n#define DEFAULT_ROLL_SIZE 10 * 1024 * 1024\n\nbool CAsyncLog::m_bTruncateLongLog = false;\nFILE* CAsyncLog::m_hLogFile = NULL;\nstd::string CAsyncLog::m_strFileName = \"default\";\nstd::string CAsyncLog::m_strFileNamePID = \"\";\nLOG_LEVEL CAsyncLog::m_nCurrentLevel = LOG_LEVEL_INFO;\nint64_t CAsyncLog::m_nFileRollSize = DEFAULT_ROLL_SIZE;\nint64_t CAsyncLog::m_nCurrentWrittenSize = 0;\nstd::list<std::string> CAsyncLog::m_listLinesToWrite;\nstd::shared_ptr<std::thread> CAsyncLog::m_spWriteThread;\nstd::mutex CAsyncLog::m_mutexWrite;\nstd::condition_variable CAsyncLog::m_cvWrite;\nbool CAsyncLog::CAsyncLog::m_bExit = false;\nbool CAsyncLog::m_bRunning = false;\n\nbool CAsyncLog::Init(const char* pszLogFileName/* = nullptr*/, bool bTruncateLongLine/* = false*/, int64_t nRollSize/* = 10 * 1024 * 1024*/)\n{\n    m_bTruncateLongLog = bTruncateLongLine;\n    m_nFileRollSize = nRollSize;\n   \n    if (pszLogFileName == nullptr || pszLogFileName[0] == 0)\n    {\n        m_strFileName.clear();\n    }\n    else\n        m_strFileName = pszLogFileName;\n\n    //ȡidٿͬһ̵Ĳͬ־ļ\n    char szPID[8];\n#ifdef WIN32\n    snprintf(szPID, sizeof(szPID), \"%05d\", (int)::GetCurrentProcessId());  \n#else\n    snprintf(szPID, sizeof(szPID), \"%05d\", (int)::getpid());\n#endif\n    m_strFileNamePID = szPID;\n\n    //TODOļ\n\n    m_spWriteThread.reset(new std::thread(WriteThreadProc));\n\n    return true;\n}\n\nvoid CAsyncLog::Uninit()\n{\n    m_bExit = true;\n\n    m_cvWrite.notify_one();\n\n    if (m_spWriteThread->joinable())\n        m_spWriteThread->join();\n    \n    if(m_hLogFile != nullptr)\n\t{\n        fclose(m_hLogFile);\n        m_hLogFile = nullptr;\n\t}\n}\n\nvoid CAsyncLog::SetLevel(LOG_LEVEL nLevel)\n{\n    if (nLevel < LOG_LEVEL_TRACE || nLevel > LOG_LEVEL_FATAL)\n        return;\n    \n    m_nCurrentLevel = nLevel;\n}\n\nbool CAsyncLog::IsRunning()\n{\n    return m_bRunning;\n}\n\nbool CAsyncLog::Output(long nLevel, const char* pszFmt, ...)\n{\n    if (nLevel != LOG_LEVEL_CRITICAL)\n    {\n        if (nLevel < m_nCurrentLevel)\n            return false;\n    }\n    \n    std::string strLine;\n    MakeLinePrefix(nLevel, strLine);\n\n    //log\n    std::string strLogMsg;\n\n    //ȼһ²ĳȣԱڷռ\n    va_list ap;\n    va_start(ap, pszFmt);\n    int nLogMsgLength = vsnprintf(NULL, 0, pszFmt, ap);\n    va_end(ap);\n\n    //һ\\0\n    if ((int)strLogMsg.capacity() < nLogMsgLength + 1)\n    {\n        strLogMsg.resize(nLogMsgLength + 1);\n    }\n    va_list aq;\n    va_start(aq, pszFmt);\n    vsnprintf((char*)strLogMsg.data(), strLogMsg.capacity(), pszFmt, aq);\n    va_end(aq);\n\n    //stringȷlengthԣָһlength\n    std::string strMsgFormal;\n    strMsgFormal.append(strLogMsg.c_str(), nLogMsgLength);\n\n    //־ضϣ־ֻȡǰMAX_LINE_LENGTHַ\n    if (m_bTruncateLongLog)\n        strMsgFormal = strMsgFormal.substr(0, MAX_LINE_LENGTH);\n\n    strLine += strMsgFormal;\n\n    //̨Żÿһĩβһз\n    if (!m_strFileName.empty())\n    {\n        strLine += \"\\n\";\n    }\n\t\n    if (nLevel != LOG_LEVEL_FATAL && nLevel != LOG_LEVEL_SYSERROR)\n    {\n        std::lock_guard<std::mutex> lock_guard(m_mutexWrite);\n        m_listLinesToWrite.push_back(strLine);\n        m_cvWrite.notify_one();\n    }\n    else\n    {\n        //ΪFATAL־crash򣬲ȡͬд־ķ\n        std::cout << strLine << std::endl;\n#ifdef _WIN32\n        OutputDebugStringA(strLine.c_str());\n        OutputDebugStringA(\"\\n\");\n#endif\n        \n        if (!m_strFileName.empty())\n        {\n            if (m_hLogFile == nullptr)\n            {\n                //½ļ\n                char szNow[64];\n                time_t now = time(NULL);\n                tm time;\n#ifdef _WIN32\n                localtime_s(&time, &now);\n#else\n                localtime_r(&now, &time);\n#endif\n                strftime(szNow, sizeof(szNow), \"%Y%m%d%H%M%S\", &time);\n\n                std::string strNewFileName(m_strFileName);\n                strNewFileName += \".\";\n                strNewFileName += szNow;\n                strNewFileName += \".\";\n                strNewFileName += m_strFileNamePID;\n                strNewFileName += \".log\";\n                if (!CreateNewFile(strNewFileName.c_str()))\n                    return false;\n            }// end inner if \n\n            WriteToFile(strLine);\n\n        }// end outer-if\n\n        if (nLevel == LOG_LEVEL_FATAL)\n        {\n            //ócrash\n            Crash();\n        } \n    }\n    \n\n    return true;\n}\n\nbool CAsyncLog::Output(long nLevel, const char* pszFileName, int nLineNo, const char* pszFmt, ...)\n{\n    if (nLevel != LOG_LEVEL_CRITICAL)\n    {\n        if (nLevel < m_nCurrentLevel)\n            return false;\n    }\n\n    std::string strLine;\n    MakeLinePrefix(nLevel, strLine);\n\n    //ǩ\n    char szFileName[512] = { 0 };\n    snprintf(szFileName, sizeof(szFileName), \"[%s:%d]\", pszFileName, nLineNo);\n    strLine += szFileName;\n\n    //־\n    std::string strLogMsg;\n\n    //ȼһ²ĳȣԱڷռ\n    va_list ap;\n    va_start(ap, pszFmt);\n    int nLogMsgLength = vsnprintf(NULL, 0, pszFmt, ap);\n    va_end(ap);\n\n    //һ\\0\n    if ((int)strLogMsg.capacity() < nLogMsgLength + 1)\n    {\n        strLogMsg.resize(nLogMsgLength + 1);\n    }\n    va_list aq;\n    va_start(aq, pszFmt);\n    vsnprintf((char*)strLogMsg.data(), strLogMsg.capacity(), pszFmt, aq);\n    va_end(aq);\n\n    //stringȷlengthԣָһlength\n    std::string strMsgFormal;\n    strMsgFormal.append(strLogMsg.c_str(), nLogMsgLength);\n\n    //־ضϣ־ֻȡǰMAX_LINE_LENGTHַ\n    if (m_bTruncateLongLog)\n        strMsgFormal = strMsgFormal.substr(0, MAX_LINE_LENGTH);\n\n    strLine += strMsgFormal;\n\n    //̨Żÿһĩβһз\n    if (!m_strFileName.empty())\n    { \n        strLine += \"\\n\";\n    }\n\n    if (nLevel != LOG_LEVEL_FATAL && nLevel != LOG_LEVEL_SYSERROR)\n    {\n        std::lock_guard<std::mutex> lock_guard(m_mutexWrite);\n        m_listLinesToWrite.push_back(strLine);\n        m_cvWrite.notify_one();\n    }\n    else\n    {\n        //ΪFATAL־crash򣬲ȡͬд־ķ\n        std::cout << strLine << std::endl;\n#ifdef _WIN32\n        OutputDebugStringA(strLine.c_str());\n        OutputDebugStringA(\"\\n\");\n#endif\n\n        if (!m_strFileName.empty())\n        {\n            if (m_hLogFile == nullptr)\n            {\n                //½ļ\n                char szNow[64];\n                time_t now = time(NULL);\n                tm time;\n#ifdef _WIN32\n                localtime_s(&time, &now);\n#else\n                localtime_r(&now, &time);\n#endif\n                strftime(szNow, sizeof(szNow), \"%Y%m%d%H%M%S\", &time);\n\n                std::string strNewFileName(m_strFileName);\n                strNewFileName += \".\";\n                strNewFileName += szNow;\n                strNewFileName += \".\";\n                strNewFileName += m_strFileNamePID;\n                strNewFileName += \".log\";\n                if (!CreateNewFile(strNewFileName.c_str()))\n                    return false;\n            }// end inner if \n\n            WriteToFile(strLine);     \n        }// end outer-if\n\n        if (nLevel == LOG_LEVEL_FATAL)\n        {\n            //ócrash\n            Crash();\n        }    \n    }\n\n    return true;\n}\n\nbool CAsyncLog::OutputBinary(unsigned char* buffer, size_t size)\n{\n    //std::string strBinary;\n    std::ostringstream os;\n\n    static const size_t PRINTSIZE = 512;\n    char szbuf[PRINTSIZE * 3 + 8];\n\n    size_t lsize = 0;\n    size_t lprintbufsize = 0;\n    int index = 0;\n    os << \"address[\" << (long)buffer << \"] size[\" << size << \"] \\n\";\n    while (true)\n    {\n        memset(szbuf, 0, sizeof(szbuf));\n        if (size > lsize)\n        {\n            lprintbufsize = (size - lsize);\n            lprintbufsize = lprintbufsize > PRINTSIZE ? PRINTSIZE : lprintbufsize;\n            FormLog(index, szbuf, sizeof(szbuf), buffer + lsize, lprintbufsize);\n            size_t len = strlen(szbuf);\n\n            //if (stream().buffer().avail() < static_cast<int>(len))\n            //{\n            //    stream() << szbuf + (len - stream().buffer().avail());\n            //    break;\n            //}\n            //else\n            //{\n            os << szbuf;\n            //}\n            lsize += lprintbufsize;\n        }\n        else\n        {\n            break;\n        }\n    }\n\n    std::lock_guard<std::mutex> lock_guard(m_mutexWrite);\n    m_listLinesToWrite.push_back(os.str());\n    m_cvWrite.notify_one();\n\n    return true;\n}\n\nconst char* CAsyncLog::ullto4Str(int n)\n{\n    static char buf[64 + 1];\n    memset(buf, 0, sizeof(buf));\n    sprintf(buf, \"%06u\", n);\n    return buf;\n}\n\nchar g_szchar[17] = \"0123456789abcdef\";\n\nchar* CAsyncLog::FormLog(int& index, char* szbuf, size_t size_buf, unsigned char* buffer, size_t size)\n{\n    size_t len = 0;\n    size_t lsize = 0;\n    int headlen = 0;\n    char szhead[64 + 1] = { 0 };\n    //memset(szhead, 0, sizeof(szhead));\n    while (size > lsize && len + 10 < size_buf)\n    {\n        if (lsize % 32 == 0)\n        {\n            if (0 != headlen)\n            {\n                szbuf[len++] = '\\n';\n            }\n\n            memset(szhead, 0, sizeof(szhead));\n            strncpy(szhead, ullto4Str(index++), sizeof(szhead) - 1);\n            headlen = strlen(szhead);\n            szhead[headlen++] = ' ';\n\n            strcat(szbuf, szhead);\n            len += headlen;\n\n        }\n        if (lsize % 16 == 0 && 0 != headlen)\n            szbuf[len++] = ' ';\n        szbuf[len++] = g_szchar[(buffer[lsize] >> 4) & 0xf];\n        szbuf[len++] = g_szchar[(buffer[lsize]) & 0xf];\n        lsize++;\n    }\n    szbuf[len++] = '\\n';\n    szbuf[len++] = '\\0';\n    return szbuf;\n}\n\nvoid CAsyncLog::MakeLinePrefix(long nLevel, std::string& strPrefix)\n{\n    //\n    strPrefix = \"[INFO]\";\n    if (nLevel == LOG_LEVEL_TRACE)\n        strPrefix = \"[TRACE]\";\n    else if (nLevel == LOG_LEVEL_DEBUG)\n        strPrefix = \"[DEBUG]\";\n    else if (nLevel == LOG_LEVEL_WARNING)\n        strPrefix = \"[WARN]\";\n    else if (nLevel == LOG_LEVEL_ERROR)\n        strPrefix = \"[ERROR]\";\n    else if (nLevel == LOG_LEVEL_SYSERROR)\n        strPrefix = \"[SYSE]\";\n    else if (nLevel == LOG_LEVEL_FATAL)\n        strPrefix = \"[FATAL]\";\n    else if (nLevel == LOG_LEVEL_CRITICAL)\n        strPrefix = \"[CRITICAL]\";\n\n    //ʱ\n    char szTime[64] = { 0 };\n    GetTime(szTime, sizeof(szTime));\n\n    strPrefix += \"[\";\n    strPrefix += szTime;\n    strPrefix += \"]\";\n\n    //ǰ߳Ϣ\n    char szThreadID[32] = { 0 };\n    //дLinux\n    //std::ostringstream osThreadID;\n    //osThreadID << std::this_thread::get_id();\n#ifdef WIN32\n    DWORD threadId = ::GetCurrentThreadId();\n#else\n    int threadId = syscall(SYS_gettid);\n#endif\n    snprintf(szThreadID, sizeof(szThreadID), \"[%d]\", (int32_t)threadId);\n    strPrefix += szThreadID;   \n}\n\nvoid CAsyncLog::GetTime(char* pszTime, int nTimeStrLength)\n{   \n    struct timeb tp;\n    ftime(&tp);\n    \n    time_t now = tp.time;\n    tm time;\n#ifdef _WIN32\n    localtime_s(&time, &now);\n#else\n    localtime_r(&now, &time);\n#endif\n    \n    snprintf(pszTime, nTimeStrLength, \"%04d-%02d-%02d %02d:%02d:%02d:%03d\", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, tp.millitm);\n    //strftime(pszTime, nTimeStrLength, \"[%Y-%m-%d %H:%M:%S:]\", &time);\n}\n\nbool CAsyncLog::CreateNewFile(const char* pszLogFileName)\n{\n    if (m_hLogFile != nullptr)\n    {\n        fclose(m_hLogFile);\n    }\n\n    //ʼ½ļ\n    m_hLogFile = fopen(pszLogFileName, \"w+\");\n    return m_hLogFile != nullptr;\n}\n\nbool CAsyncLog::WriteToFile(const std::string& data)\n{\n    //Ϊ˷ֹļһд꣬һѭд\n    std::string strLocal(data);\n    int ret = 0;\n    while (true)\n    {\n        ret = fwrite(strLocal.c_str(), 1, strLocal.length(), m_hLogFile);\n        if (ret <= 0)\n            return false;\n        else if (ret <= (int)strLocal.length())\n        {\n            strLocal.erase(0, ret);\n        }\n\n        if (strLocal.empty())\n            break;\n    }\n    \n\n    //::OutputDebugStringA(strDebugInfo.c_str());\n\n    fflush(m_hLogFile);\n\n    return true;\n}\n\nvoid CAsyncLog::Crash()\n{\n    char* p = nullptr;\n    *p = 0;\n}\n\nvoid CAsyncLog::WriteThreadProc()\n{\n    m_bRunning = true;\n\n    while (true)\n    {        \n        if (!m_strFileName.empty())\n        {\n            if (m_hLogFile == nullptr || m_nCurrentWrittenSize >= m_nFileRollSize)\n            {\n                //m_nCurrentWrittenSizeС\n                m_nCurrentWrittenSize = 0;\n\n                //һλļСrollsize½ļ\n                char szNow[64];\n                time_t now = time(NULL);\n                tm time;\n#ifdef _WIN32\n                localtime_s(&time, &now);\n#else\n                localtime_r(&now, &time);\n#endif\n                strftime(szNow, sizeof(szNow), \"%Y%m%d%H%M%S\", &time);\n\n                std::string strNewFileName(m_strFileName);\n                strNewFileName += \".\";\n                strNewFileName += szNow;\n                strNewFileName += \".\";\n                strNewFileName += m_strFileNamePID;\n                strNewFileName += \".log\";\n                if (!CreateNewFile(strNewFileName.c_str()))\n                    return;\n            }// end inner if        \n        }// end outer-if\n\n\n        std::string strLine;\n        {\n            std::unique_lock<std::mutex> guard(m_mutexWrite);\n            while (m_listLinesToWrite.empty())\n            {\n                if (m_bExit)\n                    return;\n\n                m_cvWrite.wait(guard);\n            }\n\n            strLine = m_listLinesToWrite.front();\n            m_listLinesToWrite.pop_front();\n        }\n\n        \n        std::cout << strLine << std::endl;\n\n#ifdef _WIN32\n        OutputDebugStringA(strLine.c_str());\n        OutputDebugStringA(\"\\n\");\n#endif\n\n        if (!m_strFileName.empty())\n        {\n            if (!WriteToFile(strLine))\n                return;\n\n            m_nCurrentWrittenSize += strLine.length();\n        }     \n    }// end outer-while-loop\n\n    m_bRunning = false;\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/AsyncLog.h",
    "content": "/** \n * @desc:   첽־࣬AsyncLog.h\n * @author: zhangyl\n * @date:   2019.04.13\n */\n\n#ifndef __ASYNC_LOG_H__\n#define __ASYNC_LOG_H__\n\n#include <stdio.h>\n#include <string>\n#include <list>\n#include <thread>\n#include <memory>\n#include <mutex>\n#include <condition_variable>\n\n//#ifdef LOG_EXPORTS\n//#define LOG_API __declspec(dllexport)\n//#else\n//#define LOG_API __declspec(dllimport)\n//#endif\n\n#define LOG_API\n\nenum LOG_LEVEL\n{\n    LOG_LEVEL_TRACE,\n    LOG_LEVEL_DEBUG,\n    LOG_LEVEL_INFO,\n    LOG_LEVEL_WARNING,\n    LOG_LEVEL_ERROR,    //ҵ\n    LOG_LEVEL_SYSERROR, //ͬ־ڳ˳ǰ\n    LOG_LEVEL_FATAL,    //FATAL ־ڳ־˳\n    LOG_LEVEL_CRITICAL  //CRITICAL ־־ƣ\n};\n\n//TODO: Ӽ\n//ע⣺ӡ־ϢģʽַҪ_T()\n//e.g. LOGI(_T(\"GroupID=%u, GroupName=%s, GroupName=%s.\"), lpGroupInfo->m_nGroupCode, lpGroupInfo->m_strAccount.c_str(), lpGroupInfo->m_strName.c_str());\n#define LOGT(...)    CAsyncLog::Output(LOG_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)\n#define LOGD(...)    CAsyncLog::Output(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)\n#define LOGI(...)    CAsyncLog::Output(LOG_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)\n#define LOGW(...)    CAsyncLog::Output(LOG_LEVEL_WARNING, __FILE__, __LINE__,__VA_ARGS__)\n#define LOGE(...)    CAsyncLog::Output(LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)\n#define LOGSYSE(...) CAsyncLog::Output(LOG_LEVEL_SYSERROR, __FILE__, __LINE__, __VA_ARGS__)\n#define LOGF(...)    CAsyncLog::Output(LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__)        //ΪFATAL־crash򣬲ȡͬд־ķ\n#define LOGC(...)    CAsyncLog::Output(LOG_LEVEL_CRITICAL, __FILE__, __LINE__, __VA_ARGS__)     //ؼϢ־\n\n//ݰĶƸʽ\n#define LOG_DEBUG_BIN(buf, buflength) CAsyncLog::OutputBinary(buf, buflength)\n\nclass LOG_API CAsyncLog\n{\npublic:\n    static bool Init(const char* pszLogFileName = nullptr, bool bTruncateLongLine = false, int64_t nRollSize = 10 * 1024 * 1024);\n\tstatic void Uninit();\n\n    static void SetLevel(LOG_LEVEL nLevel);\n    static bool IsRunning();\n\t\n\t//߳IDźںǩк\n\tstatic bool Output(long nLevel, const char* pszFmt, ...);\n\t//߳IDźںǩк\t\n    static bool Output(long nLevel, const char* pszFileName, int nLineNo, const char* pszFmt, ...);\n\n    static bool OutputBinary(unsigned char* buffer, size_t size);\n\nprivate:\n    CAsyncLog() = delete;\n    ~CAsyncLog() = delete;\n\n    CAsyncLog(const CAsyncLog& rhs) = delete;\n    CAsyncLog& operator=(const CAsyncLog& rhs) = delete;\n\n    static void MakeLinePrefix(long nLevel, std::string& strPrefix);\n    static void GetTime(char* pszTime, int nTimeStrLength);\n    static bool CreateNewFile(const char* pszLogFileName);\n    static bool WriteToFile(const std::string& data);\n    //ó\n    static void Crash();\n\n    static const char* ullto4Str(int n);\n    static char* FormLog(int& index, char* szbuf, size_t size_buf, unsigned char* buffer, size_t size);\n\n    static void WriteThreadProc();\n\t\nprivate:\n\tstatic bool\t\t                        m_bToFile;\t\t\t    //־дļд̨  \n\tstatic FILE*                            m_hLogFile;\n    static std::string                      m_strFileName;          //־ļ\n    static std::string                      m_strFileNamePID;    //ļеĽid\n    static bool                             m_bTruncateLongLog;     //־Ƿض\n    static LOG_LEVEL                        m_nCurrentLevel;        //ǰ־\n    static int64_t                          m_nFileRollSize;        //־ļֽ\n    static int64_t                          m_nCurrentWrittenSize;  //ѾдֽĿ\n    static std::list<std::string>           m_listLinesToWrite;     //д־\n    static std::shared_ptr<std::thread>     m_spWriteThread;\n    static std::mutex                       m_mutexWrite;\n    static std::condition_variable          m_cvWrite;\n    static bool                             m_bExit;                //˳־\n    static bool                             m_bRunning;             //б־\n};\n\n#endif // !__ASYNC_LOG_H__"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/ConfigFileReader.cpp",
    "content": "/**\n *  򵥵ļȡ࣬ConfigFileReader.cpp\n *  zhangyl 2017.05.27\n */\n#include \"ConfigFileReader.h\"\n#include <stdio.h>  //for snprintf\n#include <string.h>\n\nCConfigFileReader::CConfigFileReader(const char* filename)\n{\n\t_LoadFile(filename);\n}\n\nCConfigFileReader::~CConfigFileReader()\n{\n}\n\nchar* CConfigFileReader::GetConfigName(const char* name)\n{\n\tif (!m_load_ok)\n\t\treturn NULL;\n\n\tchar* value = NULL;\n    std::map<std::string, std::string>::iterator it = m_config_map.find(name);\n\tif (it != m_config_map.end())\n    {\n\t\tvalue = (char*)it->second.c_str();\n\t}\n\n\treturn value;\n}\n\nint CConfigFileReader::SetConfigValue(const char* name, const char* value)\n{\n    if(!m_load_ok)\n        return -1;\n\n    std::map<std::string, std::string>::iterator it = m_config_map.find(name);\n    if(it != m_config_map.end())\n    {\n        it->second = value;\n    }\n    else\n    {\n        m_config_map.insert(std::make_pair(name, value));\n    }\n\n    return _WriteFile();\n}\nvoid CConfigFileReader::_LoadFile(const char* filename)\n{\n    m_config_file.clear();\n    m_config_file.append(filename);\n\tFILE* fp = fopen(filename, \"r\");\n\tif (!fp)\t\t\n\t\treturn;\n\n\tchar buf[256];\n\tfor (;;)\n\t{\n\t\tchar* p = fgets(buf, 256, fp);\n\t\tif (!p)\n\t\t\tbreak;\n\n\t\tsize_t len = strlen(buf);\n\t\tif (buf[len - 1] == '\\n')\n\t\t\tbuf[len - 1] = 0;\t\t\t// remove \\n at the end\n\n\t\tchar* ch = strchr(buf, '#');\t// remove string start with #\n\t\tif (ch)\n\t\t\t*ch = 0;\n\n\t\tif (strlen(buf) == 0)\n\t\t\tcontinue;\n\n\t\t_ParseLine(buf);\n\t}\n\n\tfclose(fp);\n\tm_load_ok = true;\n}\n\nint CConfigFileReader::_WriteFile(const char* filename)\n{\n   FILE* fp = NULL;\n   if(filename == NULL)\n   {\n       fp = fopen(m_config_file.c_str(), \"w\");\n   }\n   else\n   {\n       fp = fopen(filename, \"w\");\n   }\n   if(fp == NULL)\n   {\n       return -1;\n   }\n\n   char szPaire[128];\n   std::map<std::string, std::string>::iterator it = m_config_map.begin();\n   for (; it != m_config_map.end(); it++)\n   {\n      memset(szPaire, 0, sizeof(szPaire));\n      snprintf(szPaire, sizeof(szPaire), \"%s=%s\\n\", it->first.c_str(), it->second.c_str());\n      size_t ret =  fwrite(szPaire, strlen(szPaire),1,fp);\n      if(ret != 1)\n      {\n          fclose(fp);\n          return -1;\n      }\n   }\n   fclose(fp);\n   return 0;\n}\n\nvoid CConfigFileReader::_ParseLine(char* line)\n{\n\tchar* p = strchr(line, '=');\n\tif (p == NULL)\n\t\treturn;\n\n\t*p = 0;\n\tchar* key =  _TrimSpace(line);\n\tchar* value = _TrimSpace(p + 1);\n\tif (key && value)\n\t{\n        m_config_map.insert(std::make_pair(key, value));\n\t}\n}\n\nchar* CConfigFileReader::_TrimSpace(char* name)\n{\n\t// remove starting space or tab\n\tchar* start_pos = name;\n\twhile ( (*start_pos == ' ') || (*start_pos == '\\t') || (*start_pos == '\\r'))\n\t{\n\t\tstart_pos++;\n\t}\n\n\tif (strlen(start_pos) == 0)\n\t\treturn NULL;\n\n\t// remove ending space or tab\n\tchar* end_pos = name + strlen(name) - 1;\n\twhile ( (*end_pos == ' ') || (*end_pos == '\\t') || (*end_pos == '\\r'))\n\t{\n\t\t*end_pos = 0;\n\t\tend_pos--;\n\t}\n\n\tint len = (int)(end_pos - start_pos) + 1;\n\tif (len <= 0)\n\t\treturn NULL;\n\n\treturn start_pos;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/ConfigFileReader.h",
    "content": "/** \n *  򵥵ļȡ࣬ConfigFileReader.h\n *  zhangyl 2017.05.27\n */\n#ifndef __CONFIG_FILE_READER_H__\n#define __CONFIG_FILE_READER_H__\n\n#include <map>\n#include <string>\n\nclass CConfigFileReader\n{\npublic:\n\tCConfigFileReader(const char* filename);\n\t~CConfigFileReader();\n\n    char* GetConfigName(const char* name);\n    int SetConfigValue(const char* name, const char*  value);\n\nprivate:\n    void  _LoadFile(const char* filename);\n    int   _WriteFile(const char* filename = NULL);\n    void  _ParseLine(char* line);\n    char* _TrimSpace(char* name);\n\n    bool                                m_load_ok;\n    std::map<std::string, std::string>  m_config_map;\n    std::string                         m_config_file;\n};\n\n\n#endif //!__CONFIG_FILE_READER_H__\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/Platform.cpp",
    "content": "#include \"Platform.h\"\n\n#ifdef WIN32\n\nNetworkInitializer::NetworkInitializer()\n{\n    WORD wVersionRequested = MAKEWORD(2, 2);\n    WSADATA wsaData;\n    ::WSAStartup(wVersionRequested, &wsaData);   \n}\n\nNetworkInitializer::~NetworkInitializer()\n{\n    ::WSACleanup();\n}\n\n#endif"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/Platform.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#if defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n#elif defined(_MSC_VER)\n#pragma warning(disable : 4996)\n#endif\n\n#ifdef WIN32\n\n#pragma comment(lib, \"Ws2_32.lib\")\n#pragma comment(lib, \"Shlwapi.lib\")\n\n//remove warning C4996 on Windows\n//#define _CRT_SECURE_NO_WARNINGS\n\ntypedef int          socklen_t;\n//typedef uint64_t     ssize_t;\ntypedef unsigned int in_addr_t;\n\n//Windows ûЩṹĶ壬Ϊֲ㣬ֶЩṹ\n#define  XPOLLIN         1\n#define  XPOLLPRI        2\n#define  XPOLLOUT        4\n#define  XPOLLERR        8 \n#define  XPOLLHUP        16\n#define  XPOLLNVAL       32\n#define  XPOLLRDHUP      8192\n\n#define  XEPOLL_CTL_ADD  1\n#define  XEPOLL_CTL_DEL  2\n#define  XEPOLL_CTL_MOD  3\n\n#pragma pack(push, 1)\ntypedef union epoll_data {\n    void*    ptr;\n    int      fd;\n    uint32_t u32;\n    uint64_t u64;\n} epoll_data_t;\n\nstruct epoll_event {\n    uint32_t     events;    /* Epoll events */\n    epoll_data_t data;      /* User data variable */\n};\n#pragma pack(pop)\n\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#include <Windows.h>\n#include <Ws2ipdef.h>\n#include <io.h>     //_pipe\n#include <fcntl.h>  //for O_BINARY\n#include <shlwapi.h>\n\nclass NetworkInitializer\n{\npublic:\n    NetworkInitializer();\n    ~NetworkInitializer();\n};\n\n#else\n\ntypedef int SOCKET;\n\n#define SOCKET_ERROR -1\n\n#define closesocket(s) close(s)\n\n\n#include <arpa/inet.h>\n\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <netinet/in.h>\n#include <netdb.h>\n\n#include <unistd.h>\n#include <stdint.h>\n#include <endian.h>\n#include <poll.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <inttypes.h>\n#include <errno.h>\n#include <dirent.h>\n\n\n#include <sys/socket.h>\n#include <sys/select.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/eventfd.h>\n#include <sys/stat.h>\n#include <sys/uio.h>\n#include <sys/epoll.h>\n#include <sys/syscall.h>\n\n//for ubuntu readv not found\n#ifdef __UBUNTU\n#include <sys/uio.h>\n#endif \n\n\n#define  XPOLLIN         POLLIN\n#define  XPOLLPRI        POLLPRI\n#define  XPOLLOUT        POLLOUT\n#define  XPOLLERR        POLLERR \n#define  XPOLLHUP        POLLHUP\n#define  XPOLLNVAL       POLLNVAL\n#define  XPOLLRDHUP      POLLRDHUP\n\n#define  XEPOLL_CTL_ADD  EPOLL_CTL_ADD\n#define  XEPOLL_CTL_DEL  EPOLL_CTL_DEL\n#define  XEPOLL_CTL_MOD  EPOLL_CTL_MOD\n\n//Linuxû֮\n#define ntohll(x) be64toh(x)\n#define htonll(x) htobe64(x)\n\n#endif\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/RAIIWrapper.h",
    "content": "#ifndef __RAII_WRAPPER_H__\n#define __RAII_WRAPPER_H__\n\ntemplate<class T>\nclass RAIIWrapper\n{\npublic:\n    RAIIWrapper(T* p) :ptr(p) {}\n\n    virtual ~RAIIWrapper()\n    {\n        if (ptr != NULL)\n        {\n            delete ptr;\n            ptr = NULL;\n        }\n    }\nprivate:\n    T* ptr;\n};\n\n#endif //!__RAII_WRAPPER_H__\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/Singleton.h",
    "content": "#pragma once\n\ntemplate<typename T>\nclass Singleton\n{\npublic:\n\tstatic T& Instance()\n\t{\n\t\t//pthread_once(&ponce_, &Singleton::init);\n\t\tif (nullptr == value_)\n\t\t{\n\t\t\tvalue_ = new T();\n\t\t}\n\t\treturn *value_;\n\t}\n\nprivate:\n\tSingleton();\n\t~Singleton();\n\n\tSingleton(const Singleton&);\n\tSingleton& operator=(const Singleton&);\n\n\tstatic void init()\n\t{\n\t\tvalue_ = new T();\n\t\t//::atexit(destroy);\n\t}\n\n\tstatic void destroy()\n\t{\n\t\tdelete value_;\n\t}\n\nprivate:\n\t//static pthread_once_t ponce_;\n\tstatic T*             value_;\n};\n\n//template<typename T>\n//pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;\n\ntemplate<typename T>\nT* Singleton<T>::value_ = nullptr;"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/Timestamp.cpp",
    "content": "#include \"Timestamp.h\"\n#include <chrono>\n#include <ctime>\n#include <iomanip>\n#include <sstream>\n#include <stdio.h>\n\nstatic_assert(sizeof(Timestamp) == sizeof(int64_t), \"sizeof(Timestamp) error\");\n\nTimestamp::Timestamp(int64_t microSecondsSinceEpoch)\n: microSecondsSinceEpoch_(microSecondsSinceEpoch)\n{\n}\n\nstring Timestamp::toString() const\n{\n    char buf[64] = { 0 };\n    int64_t seconds = microSecondsSinceEpoch_ / kMicroSecondsPerSecond;\n    int64_t microseconds = microSecondsSinceEpoch_ % kMicroSecondsPerSecond;\n    snprintf(buf, sizeof(buf)-1, \"%lld.%06lld\", (long long int)seconds, (long long int)microseconds);\n    return buf;\n}\n\nstring Timestamp::toFormattedString(bool showMicroseconds) const\n{\n\ttime_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);\n\tstruct tm tm_time;\n\n#ifdef WIN32\n\tlocaltime_s(&tm_time, &seconds);\n#else\n\tstruct tm *ptm;\n\tptm = localtime(&seconds);\n\ttm_time = *ptm;\n#endif\n\t//ss << std::put_time(&ptm, \"%F %T\");\n\t\t//<<\t\t((double)(microSecondsSinceEpoch_ % kMicroSecondsPerSecond)) / kMicroSecondsPerSecond;\n\n\tchar buf[32] = { 0 };\n\n\tif (showMicroseconds)\n\t{\n\t\tint microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond);\n#ifdef WIN32\n\t\t_snprintf_s(buf, sizeof(buf), \"%4d%02d%02d %02d:%02d:%02d.%06d\",\n\t\t\ttm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\n\t\t\ttm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,\n\t\t\tmicroseconds);\n#else\n\t\tsnprintf(buf, sizeof(buf), \"%4d%02d%02d %02d:%02d:%02d.%06d\",\n\t\t\ttm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\n\t\t\ttm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,\n\t\t\tmicroseconds);\n#endif\n\t}\n\telse\n\t{\n#ifdef WIN32\n\t\t_snprintf_s(buf, sizeof(buf), \"%4d%02d%02d %02d:%02d:%02d\",\n\t\t\ttm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\n\t\t\ttm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);\n#else\n\t\tsnprintf(buf, sizeof(buf), \"%4d%02d%02d %02d:%02d:%02d\",\n\t\t\ttm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\n\t\t\ttm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);\n#endif\n\t}\n\t\n\t\n\treturn buf;\n}\n\nTimestamp Timestamp::now()\n{\n\tchrono::time_point<chrono::system_clock, chrono::microseconds> now = chrono::time_point_cast<chrono::microseconds>(\n\t\tchrono::system_clock::now());\n\n\tint64_t microSeconds = now.time_since_epoch().count();\n\tTimestamp time(microSeconds);\n\treturn time;\n}\n\nTimestamp Timestamp::invalid()\n{\n\treturn Timestamp();\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/base/Timestamp.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <algorithm>\n#include <string>\n\nusing namespace std;\n\t\n\tclass Timestamp\n\t{\n\tpublic:\n\t\t///\n\t\t/// Constucts an invalid Timestamp.\n\t\t///\n\t\tTimestamp() : microSecondsSinceEpoch_(0)\n\t\t{\n\t\t}\n\n\t\t///\n\t\t/// Constucts a Timestamp at specific time\n\t\t///\n\t\t/// @param microSecondsSinceEpoch\n\t\texplicit Timestamp(int64_t microSecondsSinceEpoch);\n\n        Timestamp& operator+=(Timestamp lhs)\n        {\n            this->microSecondsSinceEpoch_ += lhs.microSecondsSinceEpoch_;\n            return *this;\n        }\n\n        Timestamp& operator+=(int64_t lhs)\n        {\n            this->microSecondsSinceEpoch_ += lhs;\n            return *this;\n        }\n\n        Timestamp& operator-=(Timestamp lhs)\n        {\n            this->microSecondsSinceEpoch_ -= lhs.microSecondsSinceEpoch_;\n            return *this;\n        }\n\n        Timestamp& operator-=(int64_t lhs)\n        {\n            this->microSecondsSinceEpoch_ -= lhs;\n            return *this;\n        }\n\n\t\tvoid swap(Timestamp& that)\n\t\t{\n\t\t\tstd::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_);\n\t\t}\n\n\t\t// default copy/assignment/dtor are Okay\n\n\t\tstring toString() const;\n\t\tstring toFormattedString(bool showMicroseconds = true) const;\n\n\t\tbool valid() const { return microSecondsSinceEpoch_ > 0; }\n\n\t\t// for internal usage.\n\t\tint64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }\n\t\ttime_t secondsSinceEpoch() const\n\t\t{\n\t\t\treturn static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);\n\t\t}\n\n\t\t///\n\t\t/// Get time of now.\n\t\t///\n\t\tstatic Timestamp now();\n\t\tstatic Timestamp invalid();\n\n\t\tstatic const int kMicroSecondsPerSecond = 1000 * 1000;\n\n\tprivate:\n\t\tint64_t     microSecondsSinceEpoch_;\n\t};\n\n\tinline bool operator<(Timestamp lhs, Timestamp rhs)\n\t{\n\t\treturn lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();\n\t}\n\n\tinline bool operator>(Timestamp lhs, Timestamp rhs)\n\t{\n\t\treturn rhs < lhs;\n\t}\n\n\tinline bool operator<=(Timestamp lhs, Timestamp rhs)\n\t{\n\t\treturn !(lhs > rhs);\n\t}\n\n\tinline bool operator>=(Timestamp lhs, Timestamp rhs)\n\t{\n\t\treturn !(lhs < rhs);\n\t}\n\n\tinline bool operator==(Timestamp lhs, Timestamp rhs)\n\t{\n\t\treturn lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();\n\t}\n\n\tinline bool operator!=(Timestamp lhs, Timestamp rhs)\n\t{\n\t\treturn !(lhs == rhs);\n\t}\n\n\t///\n\t/// Gets time difference of two timestamps, result in seconds.\n\t///\n\t/// @param high, low\n\t/// @return (high-low) in seconds\n\t/// @c double has 52-bit precision, enough for one-microsecond\n\t/// resolution for next 100 years.\n\tinline double timeDifference(Timestamp high, Timestamp low)\n\t{\n\t\tint64_t diff = high.microSecondsSinceEpoch() - low.microSecondsSinceEpoch();\n\t\treturn static_cast<double>(diff) / Timestamp::kMicroSecondsPerSecond;\n\t}\n\n\t///\n\t/// Add @c seconds to given timestamp.\n\t///\n\t/// @return timestamp+seconds as Timestamp\n\t///\n\tinline Timestamp addTime(Timestamp timestamp, int64_t microseconds)\n\t{\n\t\t//int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond);\n\t\treturn Timestamp(timestamp.microSecondsSinceEpoch() + microseconds);\n\t}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Acceptor.cpp",
    "content": "#include \"Acceptor.h\"\n\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"EventLoop.h\"\n#include \"InetAddress.h\"\n#include \"Sockets.h\"\n\nusing namespace net;\n\nAcceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)\n    : loop_(loop),\n    acceptSocket_(sockets::createNonblockingOrDie()),\n    acceptChannel_(loop, acceptSocket_.fd()),\n    listenning_(false)   \n{\n#ifndef WIN32\n    idleFd_ = ::open(\"/dev/null\", O_RDONLY | O_CLOEXEC);\n#endif\n\n    acceptSocket_.setReuseAddr(true);\n    acceptSocket_.setReusePort(reuseport);\n    acceptSocket_.bindAddress(listenAddr);\n    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));\n}\n\nAcceptor::~Acceptor()\n{\n    acceptChannel_.disableAll();\n    acceptChannel_.remove();\n#ifndef WIN32\n    ::close(idleFd_);\n#endif\n}\n\nvoid Acceptor::listen()\n{\n    loop_->assertInLoopThread();\n    listenning_ = true;\n    acceptSocket_.listen();\n    acceptChannel_.enableReading();\n}\n\nvoid Acceptor::handleRead()\n{\n    loop_->assertInLoopThread();\n    InetAddress peerAddr;\n    //FIXME loop until no more\n    int connfd = acceptSocket_.accept(&peerAddr);\n    if (connfd >= 0)\n    {\n         string hostport = peerAddr.toIpPort();\n         LOGD(\"Accepts of %s\", hostport.c_str());\n        //newConnectionCallback_ʵָTcpServer::newConnection(int sockfd, const InetAddress& peerAddr)\n        if (newConnectionCallback_)\n        {\n            newConnectionCallback_(connfd, peerAddr);\n        }\n        else\n        {\n            sockets::close(connfd);\n        }\n    }\n    else\n    {\n        LOGSYSE(\"in Acceptor::handleRead\");\n\n#ifndef WIN32\n        /*\n        The special problem of accept()ing when you can't\n\n        Many implementations of the POSIX accept function (for example, found in post-2004 Linux) \n        have the peculiar behaviour of not removing a connection from the pending queue in all error cases.\n\n        For example, larger servers often run out of file descriptors (because of resource limits),\n        causing accept to fail with ENFILE but not rejecting the connection, leading to libev signalling\n        readiness on the next iteration again (the connection still exists after all), and typically \n        causing the program to loop at 100% CPU usage.\n\n        Unfortunately, the set of errors that cause this issue differs between operating systems,\n        there is usually little the app can do to remedy the situation, and no known thread-safe\n        method of removing the connection to cope with overload is known (to me).\n\n        One of the easiest ways to handle this situation is to just ignore it - when the program encounters\n        an overload, it will just loop until the situation is over. While this is a form of busy waiting,\n        no OS offers an event-based way to handle this situation, so it's the best one can do.\n\n        A better way to handle the situation is to log any errors other than EAGAIN and EWOULDBLOCK, \n        making sure not to flood the log with such messages, and continue as usual, which at least gives\n        the user an idea of what could be wrong (\"raise the ulimit!\"). For extra points one could \n        stop the ev_io watcher on the listening fd \"for a while\", which reduces CPU usage.\n\n        If your program is single-threaded, then you could also keep a dummy file descriptor for overload\n        situations (e.g. by opening /dev/null), and when you run into ENFILE or EMFILE, close it, \n        run accept, close that fd, and create a new dummy fd. This will gracefully refuse clients under\n        typical overload conditions.\n\n        The last way to handle it is to simply log the error and exit, as is often done with malloc\n        failures, but this results in an easy opportunity for a DoS attack.\n        */\n        if (errno == EMFILE)\n        {\n            ::close(idleFd_);\n            idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);\n            ::close(idleFd_);\n            idleFd_ = ::open(\"/dev/null\", O_RDONLY | O_CLOEXEC);\n        }\n#endif\n    }\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Acceptor.h",
    "content": "#pragma once\n\n#include <functional>\n\n#include \"Channel.h\"\n#include \"Sockets.h\"\n//#include \"EventLoop.h\"\n\nnamespace net\n{\n    class EventLoop;\n    class InetAddress;\n\n    ///\n    /// Acceptor of incoming TCP connections.\n    ///\n    class Acceptor\n    {\n    public:\n        typedef std::function<void(int sockfd, const InetAddress&)> NewConnectionCallback;\n\n        Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);\n        ~Acceptor();\n\n        //ӵĻص\n        void setNewConnectionCallback(const NewConnectionCallback& cb)\n        {\n            newConnectionCallback_ = cb;\n        }\n\n        bool listenning() const { return listenning_; }\n        void listen();\n\n    private:\n        void handleRead();\n\n    private:\n        EventLoop*            loop_;\n        Socket                acceptSocket_;\n        Channel               acceptChannel_;\n        NewConnectionCallback newConnectionCallback_;\n        bool                  listenning_;\n\n#ifndef WIN32\n        int                   idleFd_;\n#endif\n    };\n\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Buffer.cpp",
    "content": "#include \"Buffer.h\"\n\n#include \"../base/Platform.h\"\n#include \"Sockets.h\"\n#include \"Callbacks.h\"\n\nusing namespace net;\n\nconst char Buffer::kCRLF[] = \"\\r\\n\";\n\nconst size_t Buffer::kCheapPrepend;\nconst size_t Buffer::kInitialSize;\n\nint32_t Buffer::readFd(int fd, int* savedErrno)\n{\n\t// saved an ioctl()/FIONREAD call to tell how much to read\n\tchar extrabuf[65536];\n    const size_t writable = writableBytes();\n#ifndef WIN32\n\tstruct iovec vec[2];\n\t\n\tvec[0].iov_base = begin() + writerIndex_;\n\tvec[0].iov_len = writable;\n\tvec[1].iov_base = extrabuf;\n\tvec[1].iov_len = sizeof extrabuf;\n\t// when there is enough space in this buffer, don't read into extrabuf.\n\t// when extrabuf is used, we read 128k-1 bytes at most.\n\tconst int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;\n\tconst ssize_t n = sockets::readv(fd, vec, iovcnt);\n#else\n    const int32_t n = sockets::read(fd, extrabuf, sizeof(extrabuf));\n#endif\n\tif (n <= 0)\n\t{\n#ifdef WIN32\n        *savedErrno = ::WSAGetLastError();\n#else\n\t\t*savedErrno = errno;\n#endif\n\t}\n\telse if (implicit_cast<size_t>(n) <= writable)\n\t{\n#ifdef WIN32\n        //Windowsƽ̨ҪֶѽյݼbufferУLinuxƽ̨Ѿ struct iovec ָ˻дλ\n        append(extrabuf, n);\n#else\n        writerIndex_ += n;\n#endif\n\t}\n\telse\n\t{\n#ifdef WIN32\n\t\t//Windowsƽֱ̨ӽеֽڷ뻺ȥ\n        append(extrabuf, n);\n#else\n        //Linuxƽ̨ʣµֽڲȥ\n        writerIndex_ = buffer_.size();\n        append(extrabuf, n - writable);\n#endif\n\t}\n\t// if (n == writable + sizeof extrabuf)\n\t// {\n\t//   goto line_30;\n\t// }\n\treturn n;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Buffer.h",
    "content": "#pragma once\n\n#include <algorithm>\n#include <vector>\n#include <string>\n#include <string.h>\n\n#include \"../base/Platform.h\"\n#include \"Sockets.h\"\n#include \"Endian.h\"\n//#include <unistd.h>  // ssize_t\n\n\nnamespace net\n{\n\n\t/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer\n\t///\n\t/// @code\n\t/// +-------------------+------------------+------------------+\n\t/// | prependable bytes |  readable bytes  |  writable bytes  |\n\t/// |                   |     (CONTENT)    |                  |\n\t/// +-------------------+------------------+------------------+\n\t/// |                   |                  |                  |\n\t/// 0      <=      readerIndex   <=   writerIndex    <=     size\n\t/// @endcode\n\tclass Buffer\n\t{\n\tpublic:\n\t\tstatic const size_t kCheapPrepend = 8;\n\t\tstatic const size_t kInitialSize = 1024;\n\n\t\texplicit Buffer(size_t initialSize = kInitialSize)\n\t\t\t: buffer_(kCheapPrepend + initialSize),\n\t\t\treaderIndex_(kCheapPrepend),\n\t\t\twriterIndex_(kCheapPrepend)\n\t\t{\n\t\t\t//assert(readableBytes() == 0);\n\t\t\t//assert(writableBytes() == initialSize);\n\t\t\t//assert(prependableBytes() == kCheapPrepend);\n\t\t}\n\n\t\t// implicit copy-ctor, move-ctor, dtor and assignment are fine\n\t\t// NOTE: implicit move-ctor is added in g++ 4.6\n\n\t\tvoid swap(Buffer& rhs)\n\t\t{\n\t\t\tbuffer_.swap(rhs.buffer_);\n\t\t\tstd::swap(readerIndex_, rhs.readerIndex_);\n\t\t\tstd::swap(writerIndex_, rhs.writerIndex_);\n\t\t}\n\n\t\tsize_t readableBytes() const\n\t\t{\n\t\t\treturn writerIndex_ - readerIndex_;\n\t\t}\n\n\t\tsize_t writableBytes() const\n\t\t{\n\t\t\treturn buffer_.size() - writerIndex_;\n\t\t}\n\n\t\tsize_t prependableBytes() const\n\t\t{\n\t\t\treturn readerIndex_;\n\t\t}\n\n\t\tconst char* peek() const\n\t\t{\n\t\t\treturn begin() + readerIndex_;\n\t\t}\n\n        const char* findString(const char* targetStr) const\n        {\n            const char* found = std::search(peek(), beginWrite(), targetStr, targetStr + strlen(targetStr));\n            return found == beginWrite() ? nullptr : found;\n        }\n\n\t\tconst char* findCRLF() const\n\t\t{\n\t\t\t// FIXME: replace with memmem()?\n\t\t\tconst char* crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF + 2);\n\t\t\treturn crlf == beginWrite() ? nullptr : crlf;\n\t\t}\n\n\t\tconst char* findCRLF(const char* start) const\n\t\t{\n            if (peek() > start)\n                return nullptr;\n\n            if (start > beginWrite())\n                return nullptr;\n\n\t\t\t// FIXME: replace with memmem()?\n\t\t\tconst char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF + 2);\n\t\t\treturn crlf == beginWrite() ? nullptr : crlf;\n\t\t}\n\n\t\tconst char* findEOL() const\n\t\t{\n\t\t\tconst void* eol = memchr(peek(), '\\n', readableBytes());\n\t\t\treturn static_cast<const char*>(eol);\n\t\t}\n\n\t\tconst char* findEOL(const char* start) const\n\t\t{\n            if (peek() > start)\n                return nullptr;\n\n            if (start > beginWrite())\n                return nullptr;\n\n\t\t\tconst void* eol = memchr(start, '\\n', beginWrite() - start);\n\t\t\treturn static_cast<const char*>(eol);\n\t\t}\n\n\t\t// retrieve returns void, to prevent\n\t\t// string str(retrieve(readableBytes()), readableBytes());\n\t\t// the evaluation of two functions are unspecified\n\t\tbool retrieve(size_t len)\n\t\t{\n            if (len > readableBytes())\n                return false;\n\n\t\t\tif (len < readableBytes())\n\t\t\t{\n\t\t\t\treaderIndex_ += len;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tretrieveAll();\n\t\t\t}\n\n            return true;\n\t\t}\n\n\t\tbool retrieveUntil(const char* end)\n\t\t{\n            if (peek() > end)\n                return false;\n\n            if (end > beginWrite())\n                return false;\n\n\t\t\tretrieve(end - peek());\n\n            return true;\n\t\t}\n\n\t\tvoid retrieveInt64()\n\t\t{\n\t\t\tretrieve(sizeof(int64_t));\n\t\t}\n\n\t\tvoid retrieveInt32()\n\t\t{\n\t\t\tretrieve(sizeof(int32_t));\n\t\t}\n\n\t\tvoid retrieveInt16()\n\t\t{\n\t\t\tretrieve(sizeof(int16_t));\n\t\t}\n\n\t\tvoid retrieveInt8()\n\t\t{\n\t\t\tretrieve(sizeof(int8_t));\n\t\t}\n\n\t\tvoid retrieveAll()\n\t\t{\n\t\t\treaderIndex_ = kCheapPrepend;\n\t\t\twriterIndex_ = kCheapPrepend;\n\t\t}\n\n\t\tstd::string retrieveAllAsString()\n\t\t{\n\t\t\treturn retrieveAsString(readableBytes());;\n\t\t}\n\n\t\tstd::string retrieveAsString(size_t len)\n\t\t{\n            if (len > readableBytes())\n                return \"\";\n\n\t\t\tstd::string result(peek(), len);\n\t\t\tretrieve(len);\n\t\t\treturn result;\n\t\t}\n\n\t\tstd::string toStringPiece() const\n\t\t{\n\t\t\treturn std::string(peek(), static_cast<int>(readableBytes()));\n\t\t}\n\n\t\tvoid append(const std::string& str)\n\t\t{\n\t\t\tappend(str.c_str(), str.size());\n\t\t}\n\n\t\tvoid append(const char* /*restrict*/ data, size_t len)\n\t\t{\n\t\t\t//ʵ൱ڰǰŲ\n            ensureWritableBytes(len);\n\t\t\tstd::copy(data, data + len, beginWrite());\n\t\t\thasWritten(len);\n\t\t}\n\n\t\tvoid append(const void* /*restrict*/ data, size_t len)\n\t\t{\n\t\t\tappend(static_cast<const char*>(data), len);\n\t\t}\n\n\t\tbool ensureWritableBytes(size_t len)\n\t\t{\n\t\t\t//ʣµĿдռСҪĿռlenlenȸռ\n            if (writableBytes() < len)\n\t\t\t{\n\t\t\t\tmakeSpace(len);\n\t\t\t}\n\n            return true;\n\n            //if (writableBytes() >= len)\n            //    return false;\n\t\t}\n\n\t\tchar* beginWrite()\n\t\t{\n\t\t\treturn begin() + writerIndex_;\n\t\t}\n\n\t\tconst char* beginWrite() const\n\t\t{\n\t\t\treturn begin() + writerIndex_;\n\t\t}\n\n\t\tbool hasWritten(size_t len)\n\t\t{\n            if (len > writableBytes())\n                return false;\n\n\t\t\twriterIndex_ += len;\n            return true;\n\t\t}\n\n\t\tbool unwrite(size_t len)\n\t\t{\n            if (len > readableBytes())\n                return false;\n\n\t\t\twriterIndex_ -= len;\n            return true;\n\t\t}\n\n\t\t///\n\t\t/// Append int64_t using network endian\n\t\t///\n\t\tvoid appendInt64(int64_t x)\n\t\t{\n\t\t\tint64_t be64 = sockets::hostToNetwork64(x);\n\t\t\tappend(&be64, sizeof be64);\n\t\t}\n\n\t\t///\n\t\t/// Append int32_t using network endian\n\t\t///\n\t\tvoid appendInt32(int32_t x)\n\t\t{\n\t\t\tint32_t be32 = sockets::hostToNetwork32(x);\n\t\t\tappend(&be32, sizeof be32);\n\t\t}\n\n\t\tvoid appendInt16(int16_t x)\n\t\t{\n\t\t\tint16_t be16 = sockets::hostToNetwork16(x);\n\t\t\tappend(&be16, sizeof be16);\n\t\t}\n\n\t\tvoid appendInt8(int8_t x)\n\t\t{\n\t\t\tappend(&x, sizeof x);\n\t\t}\n\n\t\t///\n\t\t/// Read int64_t from network endian\n\t\t///\n\t\t/// Require: buf->readableBytes() >= sizeof(int32_t)\n\t\tint64_t readInt64()\n\t\t{\n\t\t\tint64_t result = peekInt64();\n\t\t\tretrieveInt64();\n\t\t\treturn result;\n\t\t}\n\n\t\t///\n\t\t/// Read int32_t from network endian\n\t\t///\n\t\t/// Require: buf->readableBytes() >= sizeof(int32_t)\n\t\tint32_t readInt32()\n\t\t{\n\t\t\tint32_t result = peekInt32();\n\t\t\tretrieveInt32();\n\t\t\treturn result;\n\t\t}\n\n\t\tint16_t readInt16()\n\t\t{\n\t\t\tint16_t result = peekInt16();\n\t\t\tretrieveInt16();\n\t\t\treturn result;\n\t\t}\n\n\t\tint8_t readInt8()\n\t\t{\n\t\t\tint8_t result = peekInt8();\n\t\t\tretrieveInt8();\n\t\t\treturn result;\n\t\t}\n\n\t\t///\n\t\t/// Peek int64_t from network endian\n\t\t///\n\t\t/// Require: buf->readableBytes() >= sizeof(int64_t)\n\t\tint64_t peekInt64() const\n\t\t{\n            if (readableBytes() < sizeof(int64_t))\n                return -1;\n\n\t\t\tint64_t be64 = 0;\n\t\t\t::memcpy(&be64, peek(), sizeof be64);\n\t\t\treturn sockets::networkToHost64(be64);\n\t\t}\n\n\t\t///\n\t\t/// Peek int32_t from network endian\n\t\t///\n\t\t/// Require: buf->readableBytes() >= sizeof(int32_t)\n\t\tint32_t peekInt32() const\n\t\t{\n            if (readableBytes() < sizeof(int32_t))\n                return -1;\n\n\t\t\tint32_t be32 = 0;\n\t\t\t::memcpy(&be32, peek(), sizeof be32);\n\t\t\treturn sockets::networkToHost32(be32);\n\t\t}\n\n\t\tint16_t peekInt16() const\n\t\t{\n            if (readableBytes() < sizeof(int16_t))\n                return -1;\n\n\t\t\tint16_t be16 = 0;\n\t\t\t::memcpy(&be16, peek(), sizeof be16);\n\t\t\treturn sockets::networkToHost16(be16);\n\t\t}\n\n\t\tint8_t peekInt8() const\n\t\t{\n            if (readableBytes() < sizeof(int8_t))\n                return -1;\n\n\t\t\tint8_t x = *peek();\n\t\t\treturn x;\n\t\t}\n\n\t\t///\n\t\t/// Prepend int64_t using network endian\n\t\t///\n\t\tvoid prependInt64(int64_t x)\n\t\t{\n\t\t\tint64_t be64 = sockets::hostToNetwork64(x);\n\t\t\tprepend(&be64, sizeof be64);\n\t\t}\n\n\t\t///\n\t\t/// Prepend int32_t using network endian\n\t\t///\n\t\tvoid prependInt32(int32_t x)\n\t\t{\n\t\t\tint32_t be32 = sockets::hostToNetwork32(x);\n\t\t\tprepend(&be32, sizeof be32);\n\t\t}\n\n\t\tvoid prependInt16(int16_t x)\n\t\t{\n\t\t\tint16_t be16 = sockets::hostToNetwork16(x);\n\t\t\tprepend(&be16, sizeof be16);\n\t\t}\n\n\t\tvoid prependInt8(int8_t x)\n\t\t{\n\t\t\tprepend(&x, sizeof x);\n\t\t}\n\n\t\tbool prepend(const void* /*restrict*/ data, size_t len)\n\t\t{\n            if (len > prependableBytes())\n                return false;\n\n\t\t\treaderIndex_ -= len;\n\t\t\tconst char* d = static_cast<const char*>(data);\n\t\t\tstd::copy(d, d + len, begin() + readerIndex_);\n            return true;\n\t\t}\n\n\t\tvoid shrink(size_t reserve)\n\t\t{\n\t\t\t// FIXME: use vector::shrink_to_fit() in C++ 11 if possible.\n\t\t\tBuffer other;\n\t\t\tother.ensureWritableBytes(readableBytes() + reserve);\n\t\t\tother.append(toStringPiece());\n\t\t\tswap(other);\n\t\t}\n\n\t\tsize_t internalCapacity() const\n\t\t{\n\t\t\treturn buffer_.capacity();\n\t\t}\n\n\t\t/// Read data directly into buffer.\n\t\t///\n\t\t/// It may implement with readv(2)\n\t\t/// @return result of read(2), @c errno is saved\n\t\tint32_t readFd(int fd, int* savedErrno);\n\n\tprivate:\n\n\t\tchar* begin()\n\t\t{\n\t\t\treturn &*buffer_.begin();\n\t\t}\n\n\t\tconst char* begin() const\n\t\t{\n\t\t\treturn &*buffer_.begin();\n\t\t}\n\n\t\tvoid makeSpace(size_t len)\n\t\t{\n\t\t\t//kCheapPrependΪĿռ\n            if (writableBytes() + prependableBytes() < len + kCheapPrepend)\n\t\t\t{\n\t\t\t\t// FIXME: move readable data\n\t\t\t\tbuffer_.resize(writerIndex_ + len);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// move readable data to the front, make space inside buffer\n\t\t\t\t//assert(kCheapPrepend < readerIndex_);\n                if (kCheapPrepend >= readerIndex_)\n                    return;\n\n\t\t\t\tsize_t readable = readableBytes();\n\t\t\t\tstd::copy(begin() + readerIndex_,\n\t\t\t\t\tbegin() + writerIndex_,\n\t\t\t\t\tbegin() + kCheapPrepend);\n\t\t\t\treaderIndex_ = kCheapPrepend;\n\t\t\t\twriterIndex_ = readerIndex_ + readable;\n\t\t\t\t//assert(readable == readableBytes());\n\t\t\t}\n\t\t}\n\n\tprivate:\n\t\tstd::vector<char>           buffer_;\n\t\tsize_t                      readerIndex_;\n\t\tsize_t                      writerIndex_;\n\n\t\tstatic const char           kCRLF[];\n\t};\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Callbacks.h",
    "content": "#pragma once\n\n#include <functional>\n#include <memory>\n#include \"../base/Timestamp.h\"\n\n// Adapted from google-protobuf stubs/common.h\n\ntemplate<typename To, typename From>\ninline To implicit_cast(From const& f)\n{\n    return f;\n}\n\n// When you upcast (that is, cast a pointer from type Foo to type\n// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts\n// always succeed.  When you downcast (that is, cast a pointer from\n// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because\n// how do you know the pointer is really of type SubclassOfFoo?  It\n// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,\n// when you downcast, you should use this macro.  In debug mode, we\n// use dynamic_cast<> to double-check the downcast is legal (we die\n// if it's not).  In normal mode, we do the efficient static_cast<>\n// instead.  Thus, it's important to test in debug mode to make sure\n// the cast is legal!\n//    This is the only place in the code we should use dynamic_cast<>.\n// In particular, you SHOULDN'T be using dynamic_cast<> in order to\n// do RTTI (eg code like this:\n//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);\n//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);\n// You should design the code some other way not to need this.\n\ntemplate<typename To, typename From>     // use like this: down_cast<T*>(foo);\ninline To down_cast(From* f)                     // so we only accept pointers\n{\n    // Ensures that To is a sub-type of From *.  This test is here only\n    // for compile-time type checking, and has no overhead in an\n    // optimized build at run-time, as it will be optimized away\n    // completely.\n    if (false)\n    {\n        implicit_cast<From*, To>(0);\n    }\n\n#if !defined(NDEBUG) && !defined(GOOGLE_PROTOBUF_NO_RTTI)\n    assert(f == NULL || dynamic_cast<To>(f) != NULL);  // RTTI: debug mode only!\n#endif\n    return static_cast<To>(f);\n}\n\ntemplate<typename To, typename From>\ninline std::shared_ptr<To> down_pointer_cast(const std::shared_ptr<From> & f)\n{\n    if (false)\n    {\n        implicit_cast<From*, To*>(0);\n    }\n\n#ifndef NDEBUG\n    assert(f == NULL || dynamic_cast<To*>(get_pointer(f)) != NULL);\n#endif\n    return std::static_pointer_cast<To>(f);\n}\n\nnamespace net\n{\n\n    // All client visible callbacks go here.\n\n    class Buffer;\n    class TcpConnection;\n    typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;\n    typedef std::function<void()> TimerCallback;\n    typedef std::function<void(const TcpConnectionPtr&)> ConnectionCallback;\n    typedef std::function<void(const TcpConnectionPtr&)> CloseCallback;\n    typedef std::function<void(const TcpConnectionPtr&)> WriteCompleteCallback;\n    typedef std::function<void(const TcpConnectionPtr&, size_t)> HighWaterMarkCallback;\n\n    // the data has been read to (buf, len)\n    typedef std::function<void(const TcpConnectionPtr&, Buffer*, Timestamp)> MessageCallback;\n\n    void defaultConnectionCallback(const TcpConnectionPtr& conn);\n    void defaultMessageCallback(const TcpConnectionPtr& conn, Buffer* buffer, Timestamp receiveTime);\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Channel.cpp",
    "content": "#include \"Channel.h\"\n#include <sstream>\n\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"Poller.h\"\n#include \"EventLoop.h\"\n\nusing namespace net;\n\nconst int Channel::kNoneEvent = 0;\nconst int Channel::kReadEvent = XPOLLIN | XPOLLPRI;\nconst int Channel::kWriteEvent = XPOLLOUT;\n\nChannel::Channel(EventLoop* loop, int fd__): loop_(loop),\n                                            fd_(fd__),\n                                            events_(0),\n                                            revents_(0),\n                                            index_(-1),\n                                            logHup_(true),\n                                            tied_(false)/*,\n                                            eventHandling_(false),\n                                            addedToLoop_(false)\n                                            */\n{\n}\n\nChannel::~Channel()\n{\n\t//assert(!eventHandling_);\n\t//assert(!addedToLoop_);\n\tif (loop_->isInLoopThread())\n\t{\n\t\t//assert(!loop_->hasChannel(this));\n\t}\n}\n\nvoid Channel::tie(const std::shared_ptr<void>& obj)\n{\n\ttie_ = obj;\n\ttied_ = true;\n}\n\nbool Channel::enableReading() \n{ \n    events_ |= kReadEvent;\n    return update();\n}\n\nbool Channel::disableReading()\n{\n    events_ &= ~kReadEvent; \n    \n    return update();\n}\n\nbool Channel::enableWriting() \n{\n    events_ |= kWriteEvent; \n    \n    return update(); \n}\n\nbool Channel::disableWriting()\n{ \n    events_ &= ~kWriteEvent; \n    return update();\n}\n\nbool Channel::disableAll()\n{ \n    events_ = kNoneEvent; \n    return update(); \n}\n\nbool Channel::update()\n{\n\t//addedToLoop_ = true;\n\treturn loop_->updateChannel(this);\n}\n\nvoid Channel::remove()\n{\n\tif (!isNoneEvent())\n        return;\n\t//addedToLoop_ = false;\n\tloop_->removeChannel(this);\n}\n\nvoid Channel::handleEvent(Timestamp receiveTime)\n{\n\tstd::shared_ptr<void> guard;\n\tif (tied_)\n\t{\n\t\tguard = tie_.lock();\n\t\tif (guard)\n\t\t{\n\t\t\thandleEventWithGuard(receiveTime);\n\t\t}\n\t}\n\telse\n\t{\n\t\thandleEventWithGuard(receiveTime);\n\t}\n}\n\nvoid Channel::handleEventWithGuard(Timestamp receiveTime)\n{\n\t//eventHandling_ = true;\n    /*\n    XPOLLIN ¼\n    XPOLLPRI¼ʾݣtcp socketĴ\n    POLLRDNORM , ¼ʾͨݿɶ\n    POLLRDBAND ,¼ʾݿɶ\n    XPOLLOUTд¼\n    POLLWRNORM , д¼ʾͨݿд\n    POLLWRBAND ,д¼ʾݿд   \n    XPOLLRDHUP (since Linux 2.6.17)Stream socketһ˹رӣעstream socket֪raw socket,dgram socketд˹رӣҪʹ¼붨_GNU_SOURCE ꡣ¼ж·Ƿ쳣ȻͨõķʹƣҪʹ¼ͷļ\n    #define _GNU_SOURCE\n      #include <poll.h>\n    XPOLLERRںôreventsʾ豸\n    XPOLLHUPںôreventsʾ豸pollfdsocketʾsocketûϽӣ˵ֻsocket()ûнconnect\n    XPOLLNVALںôreventsʾǷļfdûд\n    */\n\tLOGD(reventsToString().c_str());\n\tif ((revents_ & XPOLLHUP) && !(revents_ & XPOLLIN))\n\t{\n\t\tif (logHup_)\n\t\t{\n\t\t\tLOGW(\"Channel::handle_event() XPOLLHUP\");\n\t\t}\n\t\tif (closeCallback_) closeCallback_();\n\t}\n\n\tif (revents_ & XPOLLNVAL)\n\t{\n\t\tLOGW(\"Channel::handle_event() XPOLLNVAL\");\n\t}\n\n\tif (revents_ & (XPOLLERR | XPOLLNVAL))\n\t{\n\t\tif (errorCallback_) \n            errorCallback_();\n\t}\n    \n\tif (revents_ & (XPOLLIN | XPOLLPRI | XPOLLRDHUP))\n\t{\n\t\t//socketʱreadCallback_ָAcceptor::handleRead\n        //ǿͻsocketʱTcpConnection::handleRead \n        if (readCallback_) \n            readCallback_(receiveTime);\n\t}\n\n\tif (revents_ & XPOLLOUT)\n\t{\n\t\t//״̬socketwriteCallback_ָConnector::handleWrite()\n        if (writeCallback_) \n            writeCallback_();\n\t}\n\t//eventHandling_ = false;\n}\n\nstring Channel::reventsToString() const\n{\n\tstd::ostringstream oss;\n\toss << fd_ << \": \";\n\tif (revents_ & XPOLLIN)\n\t\toss << \"IN \";\n\tif (revents_ & XPOLLPRI)\n\t\toss << \"PRI \";\n\tif (revents_ & XPOLLOUT)\n\t\toss << \"OUT \";\n\tif (revents_ & XPOLLHUP)\n\t\toss << \"HUP \";\n\tif (revents_ & XPOLLRDHUP)\n\t\toss << \"RDHUP \";\n\tif (revents_ & XPOLLERR)\n\t\toss << \"ERR \";\n\tif (revents_ & XPOLLNVAL)\n\t\toss << \"NVAL \";\n\n\treturn oss.str().c_str();\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Channel.h",
    "content": "#pragma once\n\n#include <memory>\n#include <functional>\n\n#include \"../base/Timestamp.h\"\n\nnamespace net\n{\n\tclass EventLoop;\n\n\t///\n\t/// A selectable I/O channel.\n\t///\n\t/// This class doesn't own the file descriptor.\n\t/// The file descriptor could be a socket,\n\t/// an eventfd, a timerfd, or a signalfd\n\tclass Channel\n\t{\n\tpublic:\n\t\ttypedef std::function<void()> EventCallback;\n\t\ttypedef std::function<void(Timestamp)> ReadEventCallback;\n\n\t\tChannel(EventLoop* loop, int fd);\n\t\t~Channel();\n\n\t\tvoid handleEvent(Timestamp receiveTime);\n\t\tvoid setReadCallback(const ReadEventCallback& cb)\n\t\t{\n\t\t\treadCallback_ = cb;\n\t\t}\n\t\tvoid setWriteCallback(const EventCallback& cb)\n\t\t{\n\t\t\twriteCallback_ = cb;\n\t\t}\n\t\tvoid setCloseCallback(const EventCallback& cb)\n\t\t{\n\t\t\tcloseCallback_ = cb;\n\t\t}\n\t\tvoid setErrorCallback(const EventCallback& cb)\n\t\t{\n\t\t\terrorCallback_ = cb;\n\t\t}\n\n\t\t/// Tie this channel to the owner object managed by shared_ptr,\n\t\t/// prevent the owner object being destroyed in handleEvent.\n\t\tvoid tie(const std::shared_ptr<void>&);\n\n\t\tint fd() const { return fd_; }\n\t\tint events() const { return events_; }\n\t\tvoid set_revents(int revt) { revents_ = revt; }  // used by pollers\n        void add_revents(int revt) { revents_ |= revt; } // used by pollers\n\t\t// int revents() const { return revents_; }\n\t\tbool isNoneEvent() const { return events_ == kNoneEvent; }\n\n        bool enableReading();\n        bool disableReading();\n        bool enableWriting();\n        bool disableWriting();\n        bool disableAll();\n\n\t\tbool isWriting() const { return events_ & kWriteEvent; }\n\n\t\t// for Poller\n\t\tint index() { return index_; }\n\t\tvoid set_index(int idx) { index_ = idx; }\n\n\t\t// for debug\n\t\tstring reventsToString() const;\n\n\t\tvoid doNotLogHup() { logHup_ = false; }\n\n\t\tEventLoop* ownerLoop() { return loop_; }\n\t\tvoid remove();\n\n\tprivate:\n\t\tbool update();\n\t\tvoid handleEventWithGuard(Timestamp receiveTime);\n\n\t\tstatic const int            kNoneEvent;\n\t\tstatic const int            kReadEvent;\n\t\tstatic const int            kWriteEvent;\n\n\t\tEventLoop*                  loop_;\n\t\tconst int                   fd_;\n\t\tint                         events_;\n\t\tint                         revents_; // it's the received event types of epoll or poll\n\t\tint                         index_; // used by Poller.\n\t\tbool                        logHup_;\n\n\t\tstd::weak_ptr<void>         tie_;           //std::shared_ptr<void>/std::shared_ptr<void>ָͬ\n\t\tbool                        tied_;\n\t\t//bool                        eventHandling_;\n\t\t//bool                        addedToLoop_;\n\t\tReadEventCallback           readCallback_;\n\t\tEventCallback               writeCallback_;\n\t\tEventCallback               closeCallback_;\n\t\tEventCallback               errorCallback_;\n\t};\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Connector.cpp",
    "content": "#include \"Connector.h\"\n#include <functional>\n#include <errno.h>\n#include <sstream>\n#include <iostream>\n#include <string.h> //for strerror\n#include \"../base/AsyncLog.h\"\n#include \"../base/Platform.h\"\n#include \"Channel.h\"\n#include \"EventLoop.h\"\n#include \"Sockets.h\"\n\nusing namespace net;\n\nconst int Connector::kMaxRetryDelayMs;\n\nConnector::Connector(EventLoop* loop, const InetAddress& serverAddr)\n    : loop_(loop),\n    serverAddr_(serverAddr),\n    connect_(false),\n    state_(kDisconnected),\n    retryDelayMs_(kInitRetryDelayMs)\n{\n}\n\nConnector::~Connector()\n{\n}\n\nvoid Connector::start()\n{\n    connect_ = true;\n    loop_->runInLoop(std::bind(&Connector::startInLoop, this)); // FIXME: unsafe\n}\n\nvoid Connector::startInLoop()\n{\n    loop_->assertInLoopThread();\n    //assert(state_ == kDisconnected);\n    if (state_ != kDisconnected)\n        return;\n\n    if (connect_)\n    {\n        connect();\n    }\n    else\n    {\n        LOGD(\"do not connect\");\n    }\n}\n\nvoid Connector::stop()\n{\n    connect_ = false;\n    loop_->queueInLoop(std::bind(&Connector::stopInLoop, shared_from_this())); // FIXME: unsafe\n    // FIXME: cancel timer\n}\n\nvoid Connector::stopInLoop()\n{\n    //std::stringstream ss;\t\n    //ss << \"stopInLoop eventloop threadid = \" << std::this_thread::get_id();\n    //std::cout << ss.str() << std::endl;\n\n    loop_->assertInLoopThread();\n    if (state_ == kConnecting)\n    {\n        setState(kDisconnected);\n        int sockfd = removeAndResetChannel();\n        retry(sockfd);\n    }\n}\n\nvoid Connector::connect()\n{\n    int sockfd = sockets::createNonblockingOrDie();\n    int ret = sockets::connect(sockfd, serverAddr_.getSockAddrInet());\n#ifdef WIN32\n    int savedErrno = ::WSAGetLastError();\n    if (savedErrno == EWOULDBLOCK)\n        connecting(sockfd);\n    else\n    {\n        //TODO: ȷǷҪر\n        retry(sockfd);\n        sockets::close(sockfd);\n    }\n        \n#else\n    int savedErrno = (ret == 0) ? 0 : errno;\n    switch (savedErrno)\n    {\n    case 0:\n    case EINPROGRESS:\n    case EINTR:\n    case EISCONN:\n        connecting(sockfd);\n        break;\n\n    case EAGAIN:\n    case EADDRINUSE:\n    case EADDRNOTAVAIL:\n    case ECONNREFUSED:\n    case ENETUNREACH:\n        retry(sockfd);\n        break;\n\n    case EACCES:\n    case EPERM:\n    case EAFNOSUPPORT:\n    case EALREADY:\n    case EBADF:\n    case EFAULT:\n    case ENOTSOCK:\n        LOGSYSE(\"connect error in Connector::startInLoop, %d \", savedErrno);\n        sockets::close(sockfd);\n        break;\n\n    default:\n        LOGSYSE(\"Unexpected error in Connector::startInLoop, %d \", savedErrno);\n        sockets::close(sockfd);\n        // connectErrorCallback_();\n        break;\n    }\n\n#endif\n}\n\nvoid Connector::restart()\n{\n    loop_->assertInLoopThread();\n    setState(kDisconnected);\n    retryDelayMs_ = kInitRetryDelayMs;\n    connect_ = true;\n    startInLoop();\n}\n\nvoid Connector::connecting(int sockfd)\n{\n    setState(kConnecting);\n    //assert(!channel_);\n    channel_.reset(new Channel(loop_, sockfd));\n    channel_->setWriteCallback(std::bind(&Connector::handleWrite, this)); // FIXME: unsafe\n    channel_->setErrorCallback(std::bind(&Connector::handleError, this)); // FIXME: unsafe\n\n    // channel_->tie(shared_from_this()); is not working,\n    // as channel_ is not managed by shared_ptr\n    channel_->enableWriting();\n}\n\nint Connector::removeAndResetChannel()\n{\n    channel_->disableAll();\n    channel_->remove();\n    int sockfd = channel_->fd();\n    // Can't reset channel_ here, because we are inside Channel::handleEvent\n    loop_->queueInLoop(std::bind(&Connector::resetChannel, shared_from_this())); // FIXME: unsafe\n    return sockfd;\n}\n\nvoid Connector::resetChannel()\n{\n    channel_.reset();\n}\n\nvoid Connector::handleWrite()\n{\n    LOGD(\"Connector::handleWrite %d\", state_);\n\n    if (state_ == kConnecting)\n    {\n        int sockfd = removeAndResetChannel();\n        int err = sockets::getSocketError(sockfd);\n        if (err)\n        {\n            LOGW(\"Connector::handleWrite - SO_ERROR = %d %s\", err, strerror(err));\n            retry(sockfd);\n        }\n        else if (sockets::isSelfConnect(sockfd))\n        {\n            LOGW(\"Connector::handleWrite - Self connect\");\n            retry(sockfd);\n        }\n        else\n        {\n            setState(kConnected);\n            if (connect_)\n            {\n                //newConnectionCallback_ָTcpClient::newConnection(int sockfd)\n                newConnectionCallback_(sockfd);\n            }\n            else\n            {\n                sockets::close(sockfd);\n            }\n        }\n    }\n    else\n    {\n        // what happened?\n        //assert(state_ == kDisconnected);\n        if (state_ != kDisconnected)\n            LOGSYSE(\"state_ != kDisconnected\");\n    }\n}\n\nvoid Connector::handleError()\n{\n    LOGE(\"Connector::handleError state=%d\", state_);\n    if (state_ == kConnecting)\n    {\n        int sockfd = removeAndResetChannel();\n        int err = sockets::getSocketError(sockfd);\n        LOGD(\"SO_ERROR = %d %s\", err, strerror(err));\n        LOGE(\"Connector::handleError state=%d\", state_);\n        retry(sockfd);\n    }\n}\n\nvoid Connector::retry(int sockfd)\n{\n    sockets::close(sockfd);\n    setState(kDisconnected);\n    if (connect_)\n    {\n        LOGI(\"Connector::retry - Retry connecting to %s in %d  milliseconds.\", serverAddr_.toIpPort().c_str(), retryDelayMs_);\n        //loop_->runAfter(retryDelayMs_/1000.0,\n        //                std::bind(&Connector::startInLoop, shared_from_this()));\n        //retryDelayMs_ = std::min(retryDelayMs_ * 2, kMaxRetryDelayMs);\n        //ʱԣ todo\n    }\n    else\n    {\n        LOGD(\"do not connect\");\n    }\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Connector.h",
    "content": "#pragma once\n\n#include \"InetAddress.h\"\n#include <functional>\n#include <memory>\n//#include \"EventLoop.h\"\n\nnamespace net\n{\n\n\tclass Channel;\n\tclass EventLoop;\n\n\tclass Connector : public std::enable_shared_from_this<Connector>\n\t{\n\tpublic:\n\t\ttypedef std::function<void (int sockfd)> NewConnectionCallback;\n\n\t\tConnector(EventLoop* loop, const InetAddress& serverAddr);\n\t\t~Connector();\n\n\t\tvoid setNewConnectionCallback(const NewConnectionCallback& cb)\n\t\t{ newConnectionCallback_ = cb; }\n\n\t\tvoid start();  // can be called in any thread\n\t\tvoid restart();  // must be called in loop thread\n\t\tvoid stop();  // can be called in any thread\n\n\t\tconst InetAddress& serverAddress() const { return serverAddr_; }\n\n\tprivate:\n\t\tenum States { kDisconnected, kConnecting, kConnected };\n\t\tstatic const int kMaxRetryDelayMs = 30*1000;\n\t\tstatic const int kInitRetryDelayMs = 500;\n\n\t\tvoid setState(States s) { state_ = s; }\n\t\tvoid startInLoop();\n\t\tvoid stopInLoop();\n\t\tvoid connect();\n\t\tvoid connecting(int sockfd);\n\t\tvoid handleWrite();\n\t\tvoid handleError();\n\t\tvoid retry(int sockfd);\n\t\tint removeAndResetChannel();\n\t\tvoid resetChannel();\n\n\t\tEventLoop* loop_;\n\t\tInetAddress serverAddr_;\n\t\tbool connect_; // atomic\n\t\tStates state_;  // FIXME: use atomic variable\n\t\tstd::shared_ptr<Channel> channel_;\n\t\tNewConnectionCallback newConnectionCallback_;\n\t\tint retryDelayMs_;\n\t};\n\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Endian.h",
    "content": "#pragma once\n\nnamespace net\n{\n\tnamespace sockets\n\t{\n\t\tinline uint64_t hostToNetwork64(uint64_t host64)\n\t\t{\n#ifdef WIN32\n            return htonll(host64);\n#else\n\t\t\treturn htobe64(host64);\n#endif\n\t\t}\n\n\t\tinline uint32_t hostToNetwork32(uint32_t host32)\n\t\t{\n#ifdef WIN32\n            return htonl(host32);\n#else\n            return htobe32(host32);\n#endif\n\t\t}\n\n\t\tinline uint16_t hostToNetwork16(uint16_t host16)\n\t\t{\n#ifdef WIN32\t\t\n\t\t\treturn htons(host16);\n#else\n            return htobe16(host16);\n#endif\n\t\t}\n\n\t\tinline uint64_t networkToHost64(uint64_t net64)\n\t\t{\n#ifdef WIN32\n            return ntohll(net64);\n#else\n\t\t\treturn be64toh(net64);\n#endif\n\t\t}\n\n\t\tinline uint32_t networkToHost32(uint32_t net32)\n\t\t{\n#ifdef WIN32\n            return ntohl(net32);\n#else\n\t\t\treturn be32toh(net32);\n#endif\n\t\t}\n\n\t\tinline uint16_t networkToHost16(uint16_t net16)\n\t\t{\n#ifdef WIN32\n\t\t\treturn ntohs(net16);\n#else\n            return be16toh(net16);\n#endif\n\t\t}\n\t}// end namespace sockets\n}// end namespace net\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EpollPoller.cpp",
    "content": "#include \"EpollPoller.h\"\n\n#ifndef WIN32\n#include <string.h>\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"EventLoop.h\"\n#include \"Channel.h\"\n\n\n\nusing namespace net;\n\n// On Linux, the constants of poll(2) and epoll(4)\n// are expected to be the same.\n//static_assert(EXPOLLIN == XPOLLIN, \"EXPOLLIN == XPOLLIN\");\n//static_assert(EXPOLLPRI == XPOLLPRI, \"EXPOLLPRI == XPOLLPRI\");\n//static_assert(EXPOLLOUT == XPOLLOUT, \"EXPOLLOUT == XPOLLOUT\");\n//static_assert(EXPOLLRDHUP == XPOLLRDHUP,\"EXPOLLRDHUP == XPOLLRDHUP\");\n//static_assert(EXPOLLERR == XPOLLERR, \"EXPOLLERR == XPOLLERR\");\n//static_assert(EXPOLLHUP == XPOLLHUP, \"EXPOLLHUP == XPOLLHUP\");\n\nnamespace\n{\n\tconst int kNew = -1;\n\tconst int kAdded = 1;\n\tconst int kDeleted = 2;\n}\n\nEPollPoller::EPollPoller(EventLoop* loop)\n:epollfd_(::epoll_create1(EPOLL_CLOEXEC)),\nevents_(kInitEventListSize), \nownerLoop_(loop)\n{\n\tif (epollfd_ < 0)\n\t{\n\t\tLOGF(\"EPollPoller::EPollPoller\");\n\t}\n}\n\nEPollPoller::~EPollPoller()\n{\n\t::close(epollfd_);\n}\n\nbool EPollPoller::hasChannel(Channel* channel) const\n{\n\tassertInLoopThread();\n\tChannelMap::const_iterator it = channels_.find(channel->fd());\n\treturn it != channels_.end() && it->second == channel;\n}\n\nvoid EPollPoller::assertInLoopThread() const\n{\n    ownerLoop_->assertInLoopThread();\n}\n\nTimestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)\n{\n\tint numEvents = ::epoll_wait(epollfd_,\n\t\t&*events_.begin(),\n\t\tstatic_cast<int>(events_.size()),\n\t\ttimeoutMs);\n\tint savedErrno = errno;\n\tTimestamp now(Timestamp::now());\n\tif (numEvents > 0)\n\t{\n\t\t//LOG_TRACE << numEvents << \" events happended\";\n\t\tfillActiveChannels(numEvents, activeChannels);\n\t\tif (static_cast<size_t>(numEvents) == events_.size())\n\t\t{\n\t\t\tevents_.resize(events_.size() * 2);\n\t\t}\n\t}\n\telse if (numEvents == 0)\n\t{\n\t\t//LOG_TRACE << \" nothing happended\";\n\t}\n\telse\n\t{\n\t\t// error happens, log uncommon ones\n\t\tif (savedErrno != EINTR)\n\t\t{\n\t\t\terrno = savedErrno;\n\t\t\tLOGSYSE(\"EPollPoller::poll()\");\n\t\t}\n\t}\n\treturn now;\n}\n\nvoid EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const\n{\n\t//assert(static_cast<size_t>(numEvents) <= events_.size());\n\tfor (int i = 0; i < numEvents; ++i)\n\t{\n\t\tChannel* channel = static_cast<Channel*>(events_[i].data.ptr);\n\t\tint fd = channel->fd();\n\t\tChannelMap::const_iterator it = channels_.find(fd);\n        if (it == channels_.end() || it->second != channel)\n            return;\n\t\tchannel->set_revents(events_[i].events);\n\t\tactiveChannels->push_back(channel);\n\t}\n}\n\nbool EPollPoller::updateChannel(Channel* channel)\n{\n\tassertInLoopThread();\n\tLOGD(\"fd = %d  events = %d\", channel->fd(), channel->events());\n\tconst int index = channel->index();\n\tif (index == kNew || index == kDeleted)\n\t{\n\t\t// a new one, add with XEPOLL_CTL_ADD\n\t\tint fd = channel->fd();\n\t\tif (index == kNew)\n\t\t{          \n            //assert(channels_.find(fd) == channels_.end())\n            if (channels_.find(fd) != channels_.end())\n            {\n                LOGE(\"fd = %d  must not exist in channels_\", fd);\n                return false;\n            }\n                \n\n\t\t\tchannels_[fd] = channel;\n\t\t}\n\t\telse // index == kDeleted\n\t\t{\n            //assert(channels_.find(fd) != channels_.end());\n            if (channels_.find(fd) == channels_.end())\n            {\n                LOGE(\"fd = %d  must exist in channels_\", fd);\n                return false;\n            }\n            \n\t\t\t//assert(channels_[fd] == channel);\n            if (channels_[fd] != channel)\n            {\n                LOGE(\"current channel is not matched current fd, fd = %d\", fd);\n                return false;\n            }\n\t\t}\n\t\tchannel->set_index(kAdded);\n\t\t\n        return update(XEPOLL_CTL_ADD, channel);\n\t}\n\telse\n\t{\n\t\t// update existing one with XEPOLL_CTL_MOD/DEL\n\t\tint fd = channel->fd();\n\t\t//assert(channels_.find(fd) != channels_.end());\n\t\t//assert(channels_[fd] == channel);\n\t\t//assert(index == kAdded);\n        if (channels_.find(fd) == channels_.end() || channels_[fd] != channel || index != kAdded)\n        {\n            LOGE(\"current channel is not matched current fd, fd = %d, channel = 0x%x\", fd, channel);\n            return false;\n        }\n\n\t\tif (channel->isNoneEvent())\n\t\t{\n            if (update(XEPOLL_CTL_DEL, channel))\n            {\n                channel->set_index(kDeleted);\n                return true;\n            }\n            return false;\n\t\t}\n\t\telse\n\t\t{\n            return update(XEPOLL_CTL_MOD, channel);\n\t\t}\n\t}\n}\n\nvoid EPollPoller::removeChannel(Channel* channel)\n{\n\tassertInLoopThread();\n\tint fd = channel->fd();\n\t\n    //assert(channels_.find(fd) != channels_.end());\n    //assert(channels_[fd] == channel);\n    //assert(channel->isNoneEvent());\n    if (channels_.find(fd) == channels_.end() || channels_[fd] != channel || !channel->isNoneEvent())\n        return;\n\t\n    int index = channel->index();\n\t//assert(index == kAdded || index == kDeleted);\n    if (index != kAdded && index != kDeleted)\n        return;\n\n    size_t n = channels_.erase(fd);\n\t(void)n;\n\t//assert(n == 1);\n    if (n != 1)\n        return;\n\n    if (index == kAdded)\n\t{\n\t\tupdate(XEPOLL_CTL_DEL, channel);\n\t}\n\tchannel->set_index(kNew);\n}\n\nbool EPollPoller::update(int operation, Channel* channel)\n{\n\tstruct epoll_event event;\n\tmemset(&event, 0, sizeof event);\n\tevent.events = channel->events();\n\tevent.data.ptr = channel;\n\tint fd = channel->fd();\n\tif (::epoll_ctl(epollfd_, operation, fd, &event) < 0)\n\t{\n\t\tif (operation == XEPOLL_CTL_DEL)\n\t\t{\n\t\t\tLOGE(\"epoll_ctl op=%d fd=%d, epollfd=%d, errno=%d, errorInfo: %s\", operation, fd, epollfd_, errno, strerror(errno));\n\t\t}\n\t\telse\n\t\t{           \n            LOGE(\"epoll_ctl op=%d fd=%d, epollfd=%d, errno=%d, errorInfo: %s\", operation, fd, epollfd_, errno, strerror(errno));\n\t\t}\n\n        return false;\n\t}\n\n    return true;\n}\n\n#endif"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EpollPoller.h",
    "content": "#pragma once\n\n#ifndef WIN32\n\n#include <vector>\n#include <map>\n\n#include \"../base/Timestamp.h\"\n//#include \"EventLoop.h\"\n#include \"Poller.h\"\n\nstruct epoll_event;\n\nnamespace net\n{\n    class EventLoop;\n\n    class EPollPoller : public Poller\n\t{\n\tpublic:\n\t\tEPollPoller(EventLoop* loop);\n\t\tvirtual ~EPollPoller();\n\n\t\tvirtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);\n\t\tvirtual bool updateChannel(Channel* channel);\n\t\tvirtual void removeChannel(Channel* channel);\n\n\t\tvirtual bool hasChannel(Channel* channel) const;\n\n\t\t//static EPollPoller* newDefaultPoller(EventLoop* loop);\n\n        void assertInLoopThread() const;\n\n\tprivate:\n\t\tstatic const int kInitEventListSize = 16;\n\n\t\tvoid fillActiveChannels(int numEvents, ChannelList* activeChannels) const;\n\t\tbool update(int operation, Channel* channel);\t\t\n\n\tprivate:\n\t\ttypedef std::vector<struct epoll_event> EventList;\n\n\t\tint                 epollfd_;\n\t\tEventList           events_;\n\n\t\ttypedef std::map<int, Channel*> ChannelMap;\n\n\t\tChannelMap          channels_;\n\t\tEventLoop*          ownerLoop_;\n\t};\n\n}\n\n#endif"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EventLoop.cpp",
    "content": "#include \"EventLoop.h\"\n\n#include <sstream>\n#include <iostream>\n#include <string.h>\n\n#include \"../base/AsyncLog.h\"\n#include \"Channel.h\"\n#include \"Sockets.h\"\n#include \"InetAddress.h\"\n\n#ifdef WIN32\n#include \"SelectPoller.h\"\n#else\n#include \"EpollPoller.h\"\n#endif\n\n\nusing namespace net;\n\n//ڲfd˿ڣⲿʹ˿\n//#define INNER_WAKEUP_LISTEN_PORT 10000\n\nthread_local  EventLoop* t_loopInThisThread = 0;\n\nconst int kPollTimeMs = 1;\n\nEventLoop* getEventLoopOfCurrentThread()\n{\n\treturn t_loopInThisThread;\n}\n\n// ̺߳дeventloop\nEventLoop::EventLoop()\n: looping_(false),\nquit_(false),\neventHandling_(false),\ncallingPendingFunctors_(false),\nthreadId_(std::this_thread::get_id()),\ntimerQueue_(new TimerQueue(this)),\niteration_(0L),\ncurrentActiveChannel_(NULL)\n{\n    createWakeupfd();\n    \n#ifdef WIN32\n    wakeupChannel_.reset(new Channel(this, wakeupFdRecv_));\n    poller_.reset(new SelectPoller(this));\n    \n#else\n    wakeupChannel_.reset(new Channel(this, wakeupFd_));\n    poller_.reset(new EPollPoller(this));\n#endif\n    \n    if (t_loopInThisThread)\n\t{\n\t\tLOGF(\"Another EventLoop  exists in this thread \");\n\t}\n\telse\n\t{\n\t\tt_loopInThisThread = this;\n\t}\n\twakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));\n\t// we are always reading the wakeupfd\n\twakeupChannel_->enableReading();\n\n\t//std::stringstream ss;\t\n\t//ss << \"eventloop create threadid = \" << threadId_;\n\t//std::cout << ss.str() << std::endl;\n}\n\nEventLoop::~EventLoop()\n{\n    assertInLoopThread();\n    LOGD(\"EventLoop 0x%x destructs.\", this);\n\n\t//std::stringstream ss;\n\t//ss << \"eventloop destructs threadid = \" << threadId_;\n\t//std::cout << ss.str() << std::endl;\n\n\twakeupChannel_->disableAll();\n\twakeupChannel_->remove();\n  \n#ifdef WIN32\n    sockets::close(wakeupFdSend_);\n    sockets::close(wakeupFdRecv_);\n    sockets::close(wakeupFdListen_);\n#else\n    sockets::close(wakeupFd_);\n#endif\n\n    //_close(fdpipe_[0]);\n    //_close(fdpipe_[1]);\n\n\tt_loopInThisThread = NULL;\n}\n\nvoid EventLoop::loop()\n{\n\t//assert(!looping_);\n\tassertInLoopThread();\n\tlooping_ = true;\n\tquit_ = false;  // FIXME: what if someone calls quit() before loop() ?\n\tLOGD(\"EventLoop 0x%x  start looping\", this);\n\n\twhile (!quit_)\n\t{       \n        timerQueue_->doTimer();\n\n        activeChannels_.clear();\n\t\tpollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);\n\t\t//if (Logger::logLevel() <= Logger::TRACE)\n\t\t//{\n\t\t\tprintActiveChannels();\n\t\t//}\n        ++iteration_;\n\t\t// TODO sort channel by priority\n\t\teventHandling_ = true;\n\t\tfor (const auto& it : activeChannels_)\n\t\t{\n\t\t\tcurrentActiveChannel_ = it;\n\t\t\tcurrentActiveChannel_->handleEvent(pollReturnTime_);\n\t\t}\n\t\tcurrentActiveChannel_ = nullptr;\n\t\teventHandling_ = false;\n\t\tdoPendingFunctors();\n\n\t\tif (frameFunctor_)\n\t\t{\n\t\t\tframeFunctor_();\n\t\t}\t\t\n\t}\n\n\tLOGD(\"EventLoop 0x%0x stop looping\", this);\n\tlooping_ = false;  \n\n\n    std::ostringstream oss;\n    oss << std::this_thread::get_id();\n    std::string stid = oss.str();\n    LOGI(\"Exiting loop, EventLoop object: 0x%x , threadID: %s\", this, stid.c_str());\n}\n\nvoid EventLoop::quit()\n{\n\tquit_ = true;\n\t// There is a chance that loop() just executes while(!quit_) and exists,\n\t// then EventLoop destructs, then we are accessing an invalid object.\n\t// Can be fixed using mutex_ in both places.\n\tif (!isInLoopThread())\n\t{\n\t\twakeup();\n\t}\n}\n\nvoid EventLoop::runInLoop(const Functor& cb)\n{\n\tif (isInLoopThread())\n\t{\n\t\tcb();\n\t}\n\telse\n\t{\n\t\tqueueInLoop(cb);\n\t}\n}\n\nvoid EventLoop::queueInLoop(const Functor& cb)\n{\n\t{\n\t\tstd::unique_lock<std::mutex> lock(mutex_);\n\t\tpendingFunctors_.push_back(cb);\n\t}\n\n\tif (!isInLoopThread() || callingPendingFunctors_)\n\t{\n\t\twakeup();\n\t}\n}\n\nvoid EventLoop::setFrameFunctor(const Functor& cb)\n{\n\tframeFunctor_ = cb;\n}\n\nTimerId EventLoop::runAt(const Timestamp& time, const TimerCallback& cb)\n{\n    //ִֻһ\n    return timerQueue_->addTimer(cb, time, 0, 1);\n}\n\nTimerId EventLoop::runAfter(int64_t delay, const TimerCallback& cb)\n{\n    Timestamp time(addTime(Timestamp::now(), delay));\n    return runAt(time, cb);\n}\n\nTimerId EventLoop::runEvery(int64_t interval, const TimerCallback& cb)\n{  \n    Timestamp time(addTime(Timestamp::now(), interval));\n    //-1ʾһֱظȥ\n    return timerQueue_->addTimer(cb, time, interval, -1);\n}\n\nTimerId EventLoop::runAt(const Timestamp& time, TimerCallback&& cb)\n{\n    return timerQueue_->addTimer(std::move(cb), time, 0, 1);\n}\n\nTimerId EventLoop::runAfter(int64_t delay, TimerCallback&& cb)\n{\n    Timestamp time(addTime(Timestamp::now(), delay));\n    return runAt(time, std::move(cb));\n}\n\nTimerId EventLoop::runEvery(int64_t interval, TimerCallback&& cb)\n{\n    Timestamp time(addTime(Timestamp::now(), interval));\n    return timerQueue_->addTimer(std::move(cb), time, interval, -1);\n}\n\nvoid EventLoop::cancel(TimerId timerId, bool off)\n{\n    return timerQueue_->cancel(timerId, off);\n}\n\nvoid EventLoop::remove(TimerId timerId)\n{\n    return timerQueue_->removeTimer(timerId);\n}\n\nbool EventLoop::updateChannel(Channel* channel)\n{\n\t//assert(channel->ownerLoop() == this);\n    if (channel->ownerLoop() != this)\n        return false;\n\n    assertInLoopThread();\n\t\n    return poller_->updateChannel(channel);\n}\n\nvoid EventLoop::removeChannel(Channel* channel)\n{\n\t//assert(channel->ownerLoop() == this);\n    if (channel->ownerLoop() != this)\n        return;\n\n    assertInLoopThread();\n\tif (eventHandling_)\n\t{\n\t\t//assert(currentActiveChannel_ == channel || std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());\n\t}\n\n    LOGD(\"Remove channel, channel = 0x%x, fd = %d\", channel, channel->fd());\n\tpoller_->removeChannel(channel);\n}\n\nbool EventLoop::hasChannel(Channel* channel)\n{\n\t//assert(channel->ownerLoop() == this);\n\tassertInLoopThread();\n\treturn poller_->hasChannel(channel);\n}\n\nbool EventLoop::createWakeupfd()\n{\n#ifdef WIN32\n    //if (_pipe(fdpipe_, 256, O_BINARY) == -1)\n    //{\n    //    //óҵ\n    //    LOGF(\"Unable to create pipe, EventLoop: 0x%x\", this);\n    //    return false;\n    //}\n        \n    wakeupFdListen_ = sockets::createOrDie();\n    wakeupFdSend_ = sockets::createOrDie();\n\n    //WindowsҪһsocket  \n    struct sockaddr_in bindaddr;\n    bindaddr.sin_family = AF_INET;\n    bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n    //portΪ0Ȼbindٽͨgetsocknameȡportȡ˿ڵ\n    bindaddr.sin_port = 0;\n    sockets::setReuseAddr(wakeupFdListen_, true);\n    sockets::bindOrDie(wakeupFdListen_, bindaddr);\n    sockets::listenOrDie(wakeupFdListen_);\n     \n    struct sockaddr_in serveraddr;\n    int serveraddrlen = sizeof(serveraddr);\n    if (getsockname(wakeupFdListen_, (sockaddr*)&serveraddr, &serveraddrlen) < 0)\n    {\n        //óҵ\n        LOGF(\"Unable to bind address info, EventLoop: 0x%x\", this);\n        return false;\n    }\n\n    int useport = ntohs(serveraddr.sin_port);\n    LOGD(\"wakeup fd use port: %d\", useport);\n         \n    //serveraddr.sin_family = AF_INET;\n    //serveraddr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");\n    //serveraddr.sin_port = htons(INNER_WAKEUP_LISTEN_PORT);   \n    if (::connect(wakeupFdSend_, (struct sockaddr*) & serveraddr, sizeof(serveraddr)) < 0)\n    {\n        //óҵ\n        LOGF(\"Unable to connect to wakeup peer, EventLoop: 0x%x\", this);\n        return false;\n    }\n\n    struct sockaddr_in clientaddr;\n    socklen_t clientaddrlen = sizeof(clientaddr);\n    wakeupFdRecv_ = ::accept(wakeupFdListen_, (struct sockaddr*)&clientaddr, &clientaddrlen);\n    if (wakeupFdRecv_ < 0)\n    {\n        //óҵ\n        LOGF(\"Unable to accept wakeup peer, EventLoop: 0x%x\", this);\n        return false;\n    }\n\n    sockets::setNonBlockAndCloseOnExec(wakeupFdSend_);\n    sockets::setNonBlockAndCloseOnExec(wakeupFdRecv_);\n\n#else\n    //Linuxһeventfd͹ˣʵֶд\n    wakeupFd_ = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);\n    if (wakeupFd_ < 0)\n    {\n        //óҵ\n        LOGF(\"Unable to create wakeup eventfd, EventLoop: 0x%x\", this);\n        return false;\n    }\n\n#endif\n\n    return true;\n}\n\nvoid EventLoop::abortNotInLoopThread()\n{\n\tstd::stringstream ss;\n\tss << \"threadid_ = \" << threadId_ << \" this_thread::get_id() = \" << std::this_thread::get_id();\n\tLOGF(\"EventLoop::abortNotInLoopThread - EventLoop %s\", ss.str().c_str());\n}\n\nbool EventLoop::wakeup()\n{\n\tuint64_t one = 1;\n#ifdef WIN32\n    int32_t n = sockets::write(wakeupFdSend_, &one, sizeof(one));\n#else\n    int32_t n = sockets::write(wakeupFd_, &one, sizeof(one));\n#endif\n\n    \n\tif (n != sizeof one)\n\t{\n#ifdef WIN32\n        DWORD error = WSAGetLastError();\n        LOGSYSE(\"EventLoop::wakeup() writes %d  bytes instead of 8, fd: %d, error: %d\", n, wakeupFdSend_, (int32_t)error);\n#else\n        int error = errno;\n        LOGSYSE(\"EventLoop::wakeup() writes %d  bytes instead of 8, fd: %d, error: %d, errorinfo: %s\", n, wakeupFd_, error, strerror(error));\n#endif\n        \n        \n        return false;\n\t}\n\n    return true;\n}\n\nbool EventLoop::handleRead()\n{\n\tuint64_t one = 1;\n#ifdef WIN32\n    int32_t n = sockets::read(wakeupFdRecv_, &one, sizeof(one));\n#else\n    int32_t n = sockets::read(wakeupFd_, &one, sizeof(one));\n#endif\n\n    if (n != sizeof one)\n    {\n#ifdef WIN32\n        DWORD error = WSAGetLastError();\n        LOGSYSE(\"EventLoop::wakeup() read %d  bytes instead of 8, fd: %d, error: %d\", n, wakeupFdRecv_, (int32_t)error);\n#else\n        int error = errno;\n        LOGSYSE(\"EventLoop::wakeup() read %d  bytes instead of 8, fd: %d, error: %d, errorinfo: %s\", n, wakeupFd_, error, strerror(error));\n#endif\n        return false;\n    }\n\n    return true;\n}\n\nvoid EventLoop::doPendingFunctors()\n{\n\tstd::vector<Functor> functors;\n\tcallingPendingFunctors_ = true;\n\n\t{\n\t\tstd::unique_lock<std::mutex> lock(mutex_);\n\t\tfunctors.swap(pendingFunctors_);\n\t}\n\n\tfor (size_t i = 0; i < functors.size(); ++i)\n\t{\n\t\tfunctors[i]();\n\t}\n\tcallingPendingFunctors_ = false;\n}\n\nvoid EventLoop::printActiveChannels() const\n{\n\t//TODO: ĳfor-each ﷨\n    for (ChannelList::const_iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it)\n\t{\n\t\tconst Channel* ch = *it;\n\t\tLOGD(\"{%s}\", ch->reventsToString().c_str());\n\t}\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EventLoop.h",
    "content": "#pragma once\n\n#include <vector>\n#include <memory>\n#include <functional>\n#include <thread>\n#include <mutex>\n#include <condition_variable>\n\n#include \"../base/Timestamp.h\"\n#include \"../base/Platform.h\"\n#include \"Callbacks.h\"\n#include \"Sockets.h\"\n#include \"TimerId.h\"\n#include \"TimerQueue.h\"\n\nnamespace net\n{\n    class EventLoop;\n\tclass Channel;\n    class Poller;\n    //class TimerQueue;\n    class CTimerHeap;\n\n\t///\n\t/// Reactor, at most one per thread.\n\t///\n\t/// This is an interface class, so don't expose too much details.\n\tclass EventLoop\n\t{\n\tpublic:\n\t\ttypedef std::function<void()> Functor;\n\n\t\tEventLoop();\n\t\t~EventLoop();  // force out-line dtor, for scoped_ptr members.\n\n\t\t///\n\t\t/// Loops forever.\n\t\t///\n\t\t/// Must be called in the same thread as creation of the object.\n\t\t///\n\t\tvoid loop();\n\n\t\t/// Quits loop.\n\t\t///\n\t\t/// This is not 100% thread safe, if you call through a raw pointer,\n\t\t/// better to call through shared_ptr<EventLoop> for 100% safety.\n\t\tvoid quit();\n\n\t\t///\n\t\t/// Time when poll returns, usually means data arrival.\n\t\t///\n\t\tTimestamp pollReturnTime() const { return pollReturnTime_; }\n\n\t\tint64_t iteration() const { return iteration_; }\n\n\t\t/// Runs callback immediately in the loop thread.\n\t\t/// It wakes up the loop, and run the cb.\n\t\t/// If in the same loop thread, cb is run within the function.\n\t\t/// Safe to call from other threads.\n\t\tvoid runInLoop(const Functor& cb);\n\t\t/// Queues callback in the loop thread.\n\t\t/// Runs after finish pooling.\n\t\t/// Safe to call from other threads.\n\t\tvoid queueInLoop(const Functor& cb);\n\n        // timersʱ䵥λ΢\n        ///\n        /// Runs callback at 'time'.\n        /// Safe to call from other threads.\n        ///\n        TimerId runAt(const Timestamp& time, const TimerCallback& cb);\n        ///\n        /// Runs callback after @c delay seconds.\n        /// Safe to call from other threads.\n        ///\n        TimerId runAfter(int64_t delay, const TimerCallback& cb);\n        ///\n        /// Runs callback every @c interval seconds.\n        /// Safe to call from other threads.\n        ///\n        TimerId runEvery(int64_t interval, const TimerCallback& cb);\n        ///\n        /// Cancels the timer.\n        /// Safe to call from other threads.\n        ///\n        void cancel(TimerId timerId, bool off);\n\n        void remove(TimerId timerId);\n\n        \n        TimerId runAt(const Timestamp& time, TimerCallback&& cb);\n        TimerId runAfter(int64_t delay, TimerCallback&& cb);\n        TimerId runEvery(int64_t interval, TimerCallback&& cb);\n\n\t\tvoid setFrameFunctor(const Functor& cb);\n\n        // internal usage\n\t\tbool updateChannel(Channel* channel);\n\t\tvoid removeChannel(Channel* channel);\n\t\tbool hasChannel(Channel* channel);\n\n\t\t// pid_t threadId() const { return threadId_; }\n\t\tvoid assertInLoopThread()\n\t\t{\n\t\t\tif (!isInLoopThread())\n\t\t\t{\n\t\t\t\tabortNotInLoopThread();\n\t\t\t}\n\t\t}\n\t\tbool isInLoopThread() const { return threadId_ == std::this_thread::get_id(); }\n\t\t// bool callingPendingFunctors() const { return callingPendingFunctors_; }\n\t\tbool eventHandling() const { return eventHandling_; }\n\n\t\tconst std::thread::id getThreadID() const\n\t\t{\n\t\t\treturn threadId_;\n\t\t}\n\n\tprivate:\n        bool createWakeupfd();\n        bool wakeup();\n\t\tvoid abortNotInLoopThread();\n\t\tbool handleRead();  // waked up handler\n\t\tvoid doPendingFunctors();\n\n\t\tvoid printActiveChannels() const; // DEBUG\n\n    private:\n\t\ttypedef std::vector<Channel*> ChannelList;\n\n\t\tbool                                looping_; /* atomic */\n\t\tbool                                quit_; /* atomic and shared between threads, okay on x86, I guess. */\n\t\tbool                                eventHandling_; /* atomic */\n\t\tbool                                callingPendingFunctors_; /* atomic */\n\t\tconst std::thread::id               threadId_;\n\t\tTimestamp                           pollReturnTime_;\n\t\tstd::shared_ptr<Poller>             poller_;\n        std::shared_ptr<TimerQueue>         timerQueue_;\n        int64_t                             iteration_;\n#ifdef WIN32\n        SOCKET                              wakeupFdSend_;\n        SOCKET                              wakeupFdListen_;\n        SOCKET                              wakeupFdRecv_;\n\n        //int                                 fdpipe_[2];\n#else\n        SOCKET                              wakeupFd_;          //TODO: fdʲôʱͷţ\n#endif\n\t\t// unlike in TimerQueue, which is an internal class,\n\t\t// we don't expose Channel to client.\n\t\tstd::shared_ptr<Channel>            wakeupChannel_;\n\t\n\t\t// scratch variables\n\t\tChannelList                         activeChannels_;\n\t\tChannel*                            currentActiveChannel_;\n\n\t\tstd::mutex                          mutex_;\n\t\tstd::vector<Functor>                pendingFunctors_; // Guarded by mutex_\n\n\t\tFunctor                             frameFunctor_;\n\t};\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EventLoopThread.cpp",
    "content": "#include \"EventLoopThread.h\"\n#include <functional>\n#include \"EventLoop.h\"\n\nusing namespace net;\n\nEventLoopThread::EventLoopThread(const ThreadInitCallback& cb,\n\t\t\t\t\t\t\t\t const std::string& name/* = \"\"*/)\n\t\t\t\t\t\t\t\t : loop_(NULL),\n\t\t\t\t\t\t\t\t exiting_(false),\n\t\t\t\t\t\t\t\t callback_(cb)\n{\n}\n\nEventLoopThread::~EventLoopThread()\n{\n\texiting_ = true;\n\tif (loop_ != NULL) // not 100% race-free, eg. threadFunc could be running callback_.\n\t{\n\t\t// still a tiny chance to call destructed object, if threadFunc exits just now.\n\t\t// but when EventLoopThread destructs, usually programming is exiting anyway.\n\t\tloop_->quit();\n\t\tthread_->join();\n\t}\n}\n\nEventLoop* EventLoopThread::startLoop()\n{\n\t//assert(!thread_.started());\n\t//thread_.start();\n\n\tthread_.reset(new std::thread(std::bind(&EventLoopThread::threadFunc, this)));\n\n\t{\n\t\tstd::unique_lock<std::mutex> lock(mutex_);\n\t\twhile (loop_ == NULL)\n\t\t{\n\t\t\tcond_.wait(lock);\n\t\t}\n\t}\n\n\treturn loop_;\n}\n\nvoid EventLoopThread::stopLoop()\n{\n    if (loop_ != NULL)\n        loop_->quit();\n\n    thread_->join();\n}\n\nvoid EventLoopThread::threadFunc()\n{\n\tEventLoop loop;\n\n\tif (callback_)\n\t{\n\t\tcallback_(&loop);\n\t}\n\n\t{\n\t\t//һһ̴߳\n        std::unique_lock<std::mutex> lock(mutex_);\n\t\tloop_ = &loop;\n\t\tcond_.notify_all();\n\t}\n\n\tloop.loop();\n\t//assert(exiting_);\n\tloop_ = NULL;\n}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EventLoopThread.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <condition_variable> \n#include <thread>\n#include <string>\n#include <functional>\n//#include \"EventLoop.h\"\n\nnamespace net\n{\n\n\tclass EventLoop;\n\n\tclass EventLoopThread\n\t{\n\tpublic:\n\t\ttypedef std::function<void(EventLoop*)> ThreadInitCallback;\n\n\t\tEventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(), const std::string& name = \"\");\n\t\t~EventLoopThread();\n\t\tEventLoop* startLoop();\n        void stopLoop();\n\n\tprivate:\n\t\tvoid threadFunc();\n\n\t\tEventLoop*                   loop_;\n\t\tbool                         exiting_;\n\t\tstd::shared_ptr<std::thread> thread_;\n\t\tstd::mutex                   mutex_;\n\t\tstd::condition_variable      cond_;\n\t\tThreadInitCallback           callback_;\n\t};\n\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EventLoopThreadPool.cpp",
    "content": "#include \"EventLoopThreadPool.h\"\n#include <stdio.h>\n#include <assert.h>\n#include <sstream>\n#include <string>\n#include \"EventLoop.h\"\n#include \"EventLoopThread.h\"\n#include \"Callbacks.h\"\n\nusing namespace net;\n\n\nEventLoopThreadPool::EventLoopThreadPool()\n: baseLoop_(NULL),\nstarted_(false),\nnumThreads_(0),\nnext_(0)\n{\n}\n\nEventLoopThreadPool::~EventLoopThreadPool()\n{\n\t// Don't delete loop, it's stack variable\n}\n\nvoid EventLoopThreadPool::Init(EventLoop* baseLoop, int numThreads)\n{\n\tnumThreads_ = numThreads;\n\tbaseLoop_ = baseLoop;\n}\n\nvoid EventLoopThreadPool::start(const ThreadInitCallback& cb)\n{\n    //assert(baseLoop_);\n    if (baseLoop_ == NULL)\n        return;\n    \n    //assert(!started_);\n    if (started_)\n        return;  \n\t\n    baseLoop_->assertInLoopThread();\n\n\tstarted_ = true;\n\n\tfor (int i = 0; i < numThreads_; ++i)\n\t{\n\t\tchar buf[128];\n\t\tsnprintf(buf, sizeof buf, \"%s%d\", name_.c_str(), i);\n\n\t\tstd::shared_ptr<EventLoopThread> t(new EventLoopThread(cb, buf));\n\t\t//EventLoopThread* t = new EventLoopThread(cb, buf);\n\t\tthreads_.push_back(t);\n\t\tloops_.push_back(t->startLoop());\n\t}\n\tif (numThreads_ == 0 && cb)\n\t{\n\t\tcb(baseLoop_);\n\t}\n}\n\nvoid EventLoopThreadPool::stop()\n{\n    for (auto& iter : threads_)\n    {\n        iter->stopLoop();\n    }\n}\n\nEventLoop* EventLoopThreadPool::getNextLoop()\n{\n\tbaseLoop_->assertInLoopThread();\n    //assert(started_);\n    if (!started_)\n        return NULL;\n\t\n    EventLoop* loop = baseLoop_;\n\n\tif (!loops_.empty())\n\t{\n\t\t// round-robin\n\t\tloop = loops_[next_];\n\t\t++next_;\n\t\tif (implicit_cast<size_t>(next_) >= loops_.size())\n\t\t{\n\t\t\tnext_ = 0;\n\t\t}\n\t}\n\treturn loop;\n}\n\nEventLoop* EventLoopThreadPool::getLoopForHash(size_t hashCode)\n{\n\tbaseLoop_->assertInLoopThread();\n\tEventLoop* loop = baseLoop_;\n\n\tif (!loops_.empty())\n\t{\n\t\tloop = loops_[hashCode % loops_.size()];\n\t}\n\treturn loop;\n}\n\nstd::vector<EventLoop*> EventLoopThreadPool::getAllLoops()\n{\n\tbaseLoop_->assertInLoopThread();\n\tif (loops_.empty())\n\t{\n\t\treturn std::vector<EventLoop*>(1, baseLoop_);\n\t}\n\telse\n\t{\n\t\treturn loops_;\n\t}\n}\n\nconst std::string EventLoopThreadPool::info() const\n{\n\tstd::stringstream ss;\n\tss << \"print threads id info \" << endl;\n\tfor (size_t i = 0; i < loops_.size(); i++)\n\t{\n\t\tss << i << \": id = \" << loops_[i]->getThreadID() << endl;\n\t}\n\treturn ss.str();\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/EventLoopThreadPool.h",
    "content": "#pragma once\n\n#include <vector>\n#include <functional>\n#include <memory>\n#include <string>\n//#include \"EventLoop.h\"\n\nnamespace net\n{\n\n\tclass EventLoop;\n\tclass EventLoopThread;\n\n\tclass EventLoopThreadPool\n\t{\n\tpublic:\n\t\ttypedef std::function<void(EventLoop*)> ThreadInitCallback;\n\n\t\tEventLoopThreadPool();\n\t\t~EventLoopThreadPool();\n\t\t\n\t\tvoid Init(EventLoop* baseLoop, int numThreads);\n\t\tvoid start(const ThreadInitCallback& cb = ThreadInitCallback());\n\n        void stop();\n\n\t\t// valid after calling start()\n\t\t/// round-robin\n\t\tEventLoop* getNextLoop();\n\n\t\t/// with the same hash code, it will always return the same EventLoop\n\t\tEventLoop* getLoopForHash(size_t hashCode);\n\n\t\tstd::vector<EventLoop*> getAllLoops();\n\n\t\tbool started() const\n\t\t{ return started_; }\n\n\t\tconst std::string& name() const\n\t\t{ return name_; }\n\n\t\tconst std::string info() const;\n\n\tprivate:\n\n\t\tEventLoop*                                      baseLoop_;\n\t\tstd::string                                     name_;\n\t\tbool                                            started_;\n\t\tint                                             numThreads_;\n\t\tint                                             next_;\n\t\tstd::vector<std::shared_ptr<EventLoopThread> >  threads_;\n\t\tstd::vector<EventLoop*>                         loops_;\n\t};\n\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/InetAddress.cpp",
    "content": "#include \"InetAddress.h\"\n#include <string.h>\n#include \"../base/AsyncLog.h\"\n#include \"Endian.h\"\n#include \"Sockets.h\"\n\n// INADDR_ANY use (type)value casting.\n//static const in_addr_t kInaddrAny = INADDR_ANY;\n//static const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;\n\nstatic const in_addr_t kInaddrAny = INADDR_ANY;\nstatic const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;\n\n//     /* Structure describing an Internet socket address.  */\n//     struct sockaddr_in {\n//         sa_family_t    sin_family; /* address family: AF_INET */\n//         uint16_t       sin_port;   /* port in network byte order */\n//         struct in_addr sin_addr;   /* internet address */\n//     };\n\n//     /* Internet address. */\n//     typedef uint32_t in_addr_t;\n//     struct in_addr {\n//         in_addr_t       s_addr;     /* address in network byte order */\n//     };\n\nusing namespace net;\n\n//static_assert(sizeof(InetAddress) == sizeof(struct sockaddr_in), \"sizeof(InetAddress) == sizeof(struct sockaddr_in)\");\n\nInetAddress::InetAddress(uint16_t port, bool loopbackOnly/* = false*/)\n{\n\tmemset(&addr_, 0, sizeof addr_);\n\taddr_.sin_family = AF_INET;\n\tin_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;\n\taddr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);\n\taddr_.sin_port = sockets::hostToNetwork16(port);\n}\n\nInetAddress::InetAddress(const std::string& ip, uint16_t port)\n{\n    memset(&addr_, 0, sizeof addr_);\n\tsockets::fromIpPort(ip.c_str(), port, &addr_);\n}\n\nstd::string InetAddress::toIpPort() const\n{\n\tchar buf[32];\n\tsockets::toIpPort(buf, sizeof buf, addr_);\n\treturn buf;\n}\n\nstd::string InetAddress::toIp() const\n{\n\tchar buf[32];\n\tsockets::toIp(buf, sizeof buf, addr_);\n\treturn buf;\n}\n\nuint16_t InetAddress::toPort() const\n{\n\treturn sockets::networkToHost16(addr_.sin_port);\n}\n\nstatic thread_local char t_resolveBuffer[64 * 1024];\n\nbool InetAddress::resolve(const std::string& hostname, InetAddress* out)\n{\n\t//assert(out != NULL);\n\tstruct hostent hent;\n\tstruct hostent* he = NULL;\n\tint herrno = 0;\n\tmemset(&hent, 0, sizeof(hent));\n\n#ifndef WIN32\n\tint ret = gethostbyname_r(hostname.c_str(), &hent, t_resolveBuffer, sizeof t_resolveBuffer, &he, &herrno);\n\tif (ret == 0 && he != NULL)\n\t{\n\t\t//assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t));\n\t\tout->addr_.sin_addr = *reinterpret_cast<struct in_addr*>(he->h_addr);\n\t\treturn true;\n\t}\n\n\tif (ret)\n\t{\n\t\tLOGSYSE(\"InetAddress::resolve\");\n\t}\n\n#endif\n    //TODO: Windowsʵһ\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/InetAddress.h",
    "content": "#pragma once\n\n#include <string>\n#include \"../base/Platform.h\"\n\nnamespace net\n{\n\t///\n\t/// Wrapper of sockaddr_in.\n\t///\n\t/// This is an POD interface class.\n\tclass InetAddress\n\t{\n\tpublic:\n\t\t/// Constructs an endpoint with given port number.\n\t\t/// Mostly used in TcpServer listening.\n\t\texplicit InetAddress(uint16_t port = 0, bool loopbackOnly = false);\n\n\t\t/// Constructs an endpoint with given ip and port.\n\t\t/// @c ip should be \"1.2.3.4\"\n\t\tInetAddress(const std::string& ip, uint16_t port);\n\n\t\t/// Constructs an endpoint with given struct @c sockaddr_in\n\t\t/// Mostly used when accepting new connections\n\t\tInetAddress(const struct sockaddr_in& addr)\n\t\t\t: addr_(addr)\n\t\t{ }\n\n\t\tstd::string toIp() const;\n\t\tstd::string toIpPort() const;\n\t\tuint16_t toPort() const;\n\n\t\t// default copy/assignment are Okay\n\n\t\tconst struct sockaddr_in& getSockAddrInet() const { return addr_; }\n\t\tvoid setSockAddrInet(const struct sockaddr_in& addr) { addr_ = addr; }\n\n\t\tuint32_t ipNetEndian() const { return addr_.sin_addr.s_addr; }\n\t\tuint16_t portNetEndian() const { return addr_.sin_port; }\n\n\t\t// resolve hostname to IP address, not changing port or sin_family\n\t\t// return true on success.\n\t\t// thread safe\n\t\tstatic bool resolve(const std::string& hostname, InetAddress* result);\n\t\t// static std::vector<InetAddress> resolveAll(const char* hostname, uint16_t port = 0);\n\n\tprivate:\n        struct sockaddr_in      addr_;\n\t\n\t};\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/PollPoller.cpp",
    "content": "#include \"PollPoller.h\"\n\n#ifndef WIN32\n\n#include \"../base/AsyncLog.h\"\n#include \"Channel.h\"\n#include \"EventLoop.h\"\n\n#include <errno.h>\n#include <poll.h>\n\nusing namespace net;\n\nPollPoller::PollPoller(EventLoop* loop) : ownerLoop_(loop)\n{\n}\n\nPollPoller::~PollPoller()\n{\n}\n\nTimestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)\n{\n    // XXX pollfds_ shouldn't change\n    int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);\n    int savedErrno = errno;\n    Timestamp now(Timestamp::now());\n    if (numEvents > 0)\n    {\n        LOGD(\"%d  events happended\", numEvents);\n        fillActiveChannels(numEvents, activeChannels);\n    }\n    else if (numEvents == 0)\n    {\n        LOGD(\"nothing happended\");\n    }\n    else\n    {\n        if (savedErrno != EINTR)\n        {\n            errno = savedErrno;\n            LOGSYSE(\"PollPoller::poll()\");\n        }\n    }\n    return now;\n}\n\nvoid PollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const\n{\n    for (PollFdList::const_iterator pfd = pollfds_.begin(); pfd != pollfds_.end() && numEvents > 0; ++pfd)\n    {\n        if (pfd->revents > 0)\n        {\n            --numEvents;\n            ChannelMap::const_iterator ch = channels_.find(pfd->fd);\n            //assert(ch != channels_.end());\n            if (ch == channels_.end())\n                continue;\n\n            Channel * channel = ch->second;\n            //assert(channel->fd() == pfd->fd);\n            if (channel->fd() != pfd->fd)\n                continue;\n\n            channel->set_revents(pfd->revents);\n            // pfd->revents = 0;\n            activeChannels->push_back(channel);\n        }\n    }\n}\n\nbool PollPoller::updateChannel(Channel * channel)\n{\n    assertInLoopThread();\n    LOGD(\"fd = %d events = %d\", channel->fd(), channel->events());\n    if (channel->index() < 0)\n    {\n        // a new one, add to pollfds_\n        //assert(channels_.find(channel->fd()) == channels_.end());\n        if (channels_.find(channel->fd()) != channels_.end())\n            return false;\n\n        struct pollfd pfd;\n        pfd.fd = channel->fd();\n        pfd.events = static_cast<short>(channel->events());\n        pfd.revents = 0;\n        pollfds_.push_back(pfd);\n        int idx = static_cast<int>(pollfds_.size()) - 1;\n        channel->set_index(idx);\n        channels_[pfd.fd] = channel;\n    }\n    else\n    {\n        // update existing one\n        //assert(channels_.find(channel->fd()) != channels_.end());\n        //assert(channels_[channel->fd()] == channel);\n        if (channels_.find(channel->fd()) == channels_.end() || channels_[channel->fd()] != channel)\n            return false;\n\n        int idx = channel->index();\n        //assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));\n        if (0 > idx || idx >= static_cast<int>(pollfds_.size()))\n            return false;\n\n        struct pollfd& pfd = pollfds_[idx];\n        //TODO: Ϊʲô -channel->fd() \n        //assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd() - 1);\n        pfd.events = static_cast<short>(channel->events());\n        pfd.revents = 0;\n        if (channel->isNoneEvent())\n        {\n            // ignore this pollfd\n            pfd.fd = -channel->fd() - 1;\n        }\n    }\n\n    return true;\n}\n\nvoid PollPoller::removeChannel(Channel * channel)\n{\n    assertInLoopThread();\n    LOGD(\"fd = %d\", channel->fd());\n    \n    //assert(channels_.find(channel->fd()) != channels_.end());\n    //assert(channels_[channel->fd()] == channel);\n    //assert(channel->isNoneEvent());\n\n    if (channels_.find(channel->fd()) == channels_.end() || channels_[channel->fd()] != channel || !channel->isNoneEvent())\n        return;\n\n    int idx = channel->index();\n    //assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));\n    if (0 > idx && idx >= static_cast<int>(pollfds_.size()))\n        return;\n\n    const struct pollfd& pfd = pollfds_[idx]; (void)pfd;\n    //TODO: Ϊʲô -channel->fd()\n    //assert(pfd.fd == -channel->fd() - 1 && pfd.events == channel->events());\n    \n    size_t n = channels_.erase(channel->fd());\n    //assert(n == 1); (void)n;\n    if (n != 1)\n        return;\n\n    if (implicit_cast<size_t>(idx) == pollfds_.size() - 1)\n    {\n        pollfds_.pop_back();\n    }\n    else\n    {\n        int channelAtEnd = pollfds_.back().fd;\n        iter_swap(pollfds_.begin() + idx, pollfds_.end() - 1);\n        if (channelAtEnd < 0)\n        {\n            channelAtEnd = -channelAtEnd - 1;\n        }\n        channels_[channelAtEnd]->set_index(idx);\n        pollfds_.pop_back();\n    }\n}\n\nvoid PollPoller::assertInLoopThread() const\n{\n    ownerLoop_->assertInLoopThread();\n}\n\n#endif //!WIN32"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/PollPoller.h",
    "content": "#pragma once\n\n#ifndef WIN32\n#include \"Poller.h\"\n\n#include <vector>\n#include <map>\n\nstruct pollfd;\n\nnamespace net\n{\n    class Channel;\n    class EventLoop;\n    ///\n    /// IO Multiplexing with poll(2).\n    ///\n    class PollPoller : public Poller\n    {\n    public:\n\n        PollPoller(EventLoop* loop);\n        virtual ~PollPoller();\n\n        virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);\n        virtual bool updateChannel(Channel* channel);\n        virtual void removeChannel(Channel* channel);\n\n        void assertInLoopThread() const;\n        \n\n    private:\n        void fillActiveChannels(int numEvents, ChannelList* activeChannels) const;\n\n    private:\n        typedef std::vector<struct pollfd>  PollFdList;\n        typedef std::map<int, Channel*>     ChannelMap;\n\n        ChannelMap                          channels_;\n        PollFdList                          pollfds_;\n        EventLoop*                          ownerLoop_;\n    };\n\n}\n\n#endif \n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Poller.cpp",
    "content": "#include \"Poller.h\"\n#include \"Channel.h\"\n\nusing namespace net;\n\nPoller::Poller()\n{\n}\n\nPoller::~Poller()\n{\n}\n\n//bool Poller::hasChannel(Channel* channel) const\n//{\n//    //assertInLoopThread();\n//    //ChannelMap::const_iterator it = channels_.find(channel->fd());\n//    //return it != channels_.end() && it->second == channel;\n//\n//    return false;\n//}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Poller.h",
    "content": "#pragma once\n\n#include <vector>\n#include \"../base/Timestamp.h\"\n\nnamespace net\n{\n    class Channel;\n\n    class Poller\n    {\n    public:\n        Poller();\n        ~Poller();\n\n    public:\n        typedef std::vector<Channel*> ChannelList;\n\n        virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;\n        virtual bool updateChannel(Channel* channel) = 0;\n        virtual void removeChannel(Channel* channel) = 0;\n\n        virtual bool hasChannel(Channel* channel) const = 0;\n    };\n}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/ProtocolStream.cpp",
    "content": "#ifndef _WIN32\n#include <arpa/inet.h>\n#else\n#include <Winsock2.h>\n#pragma comment(lib, \"Ws2_32.lib\")\n#endif\n\n#include \"ProtocolStream.h\"\n#include <string.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <cassert>\n#include <algorithm>\n#include <stdio.h>\n\nusing namespace std;\n\nnamespace net\n{\n    //У\n    unsigned short checksum(const unsigned short *buffer, int size)\n    {\n        unsigned int cksum = 0;\n        while (size > 1)\n        {\n            cksum += *buffer++;\n            size -= sizeof(unsigned short);\n        }\n        if (size)\n        {\n            cksum += *(unsigned char*)buffer;\n        }\n        //32λת16\n        while (cksum >> 16)\n            cksum = (cksum >> 16) + (cksum & 0xffff);\n\n        return (unsigned short)(~cksum);\n    }\n\n    //һֽڵֵѹ1~5ֽ\n    bool compress_(unsigned int i, char *buf, size_t &len)\n    {\n        len = 0;\n        for (int a = 4; a >= 0; a--)\n        {\n            char c;\n            c = i >> (a * 7) & 0x7f;\n            if (c == 0x00 && len == 0)\n                continue;\n\n            if (a == 0)\n                c &= 0x7f;\n            else\n                c |= 0x80;\n            buf[len] = c;\n            len++;\n        }\n        if (len == 0)\n        {\n            len++;\n            buf[0] = 0;\n        }\n\n        //cout << \"compress:\" << i << endl;\n        //cout << \"compress len:\" << len << endl;\n        return true;\n    }\n\n    //һ1~5ֽڵֵԭֽڵֵ\n    bool uncompress_(char *buf, size_t len, unsigned int &i)\n    {\n        i = 0;\n        for (int index = 0; index < (int)len; index++)\n        {\n            char c = *(buf + index);\n            i = i << 7;\n\n            c &= 0x7f;\n            i |= c;\n        }\n        //cout << \"uncompress:\" << i << endl;\n        return true;\n    }\n\n    BinaryReadStream::BinaryReadStream(const char* ptr_, size_t len_)\n        : ptr(ptr_), len(len_), cur(ptr_)\n    {\n        cur += BINARY_PACKLEN_LEN_2 + CHECKSUM_LEN;\n    }\n    bool BinaryReadStream::IsEmpty() const\n    {\n        return len <= BINARY_PACKLEN_LEN_2;\n    }\n    size_t BinaryReadStream::GetSize() const\n    {\n        return len;\n    }\n    bool BinaryReadStream::ReadCString(char* str, size_t strlen, /* out */ size_t& outlen)\n    {\n        size_t fieldlen;\n        size_t headlen;\n        if (!ReadLengthWithoutOffset(headlen, fieldlen)) {\n            return false;\n        }\n\n        // user buffer is not enough\n        if (fieldlen > strlen) {\n            return false;\n        }\n\n        // ƫƵݵλ\n        //cur += BINARY_PACKLEN_LEN_2;\t\n        cur += headlen;\n        if (cur + fieldlen > ptr + len)\n        {\n            outlen = 0;\n            return false;\n        }\n        memcpy(str, cur, fieldlen);\n        outlen = fieldlen;\n        cur += outlen;\n        return true;\n    }\n    bool BinaryReadStream::ReadString(string* str, size_t maxlen, size_t& outlen)\n    {\n        size_t headlen;\n        size_t fieldlen;\n        if (!ReadLengthWithoutOffset(headlen, fieldlen)) {\n            return false;\n        }\n\n        // user buffer is not enough\n        if (maxlen != 0 && fieldlen > maxlen) {\n            return false;\n        }\n\n        // ƫƵݵλ\n        //cur += BINARY_PACKLEN_LEN_2;\t\n        cur += headlen;\n        if (cur + fieldlen > ptr + len)\n        {\n            outlen = 0;\n            return false;\n        }\n        str->assign(cur, fieldlen);\n        outlen = fieldlen;\n        cur += outlen;\n        return true;\n    }\n    bool BinaryReadStream::ReadCCString(const char** str, size_t maxlen, size_t& outlen)\n    {\n        size_t headlen;\n        size_t fieldlen;\n        if (!ReadLengthWithoutOffset(headlen, fieldlen)) {\n            return false;\n        }\n        // user buffer is not enough\n        if (maxlen != 0 && fieldlen > maxlen) {\n            return false;\n        }\n\n        // ƫƵݵλ\n        //cur += BINARY_PACKLEN_LEN_2;\t\n        cur += headlen;\n\n        //memcpy(str, cur, fieldlen);\n        if (cur + fieldlen > ptr + len)\n        {\n            outlen = 0;\n            return false;\n        }\n        *str = cur;\n        outlen = fieldlen;\n        cur += outlen;\n        return true;\n    }\n    bool BinaryReadStream::ReadInt32(int32_t& i)\n    {\n        const int VALUE_SIZE = sizeof(int32_t);\n\n        if (cur + VALUE_SIZE > ptr + len)\n            return false;\n\n        memcpy(&i, cur, VALUE_SIZE);\n        i = ntohl(i);\n\n        cur += VALUE_SIZE;\n\n        return true;\n    }\n    bool BinaryReadStream::ReadInt64(int64_t& i)\n    {\n        char int64str[128];\n        size_t length;\n        if (!ReadCString(int64str, 128, length))\n            return false;\n\n        i = atoll(int64str);\n\n        return true;\n    }\n    bool BinaryReadStream::ReadShort(short& i)\n    {\n        const int VALUE_SIZE = sizeof(short);\n\n        if (cur + VALUE_SIZE > ptr + len) {\n            return false;\n        }\n\n        memcpy(&i, cur, VALUE_SIZE);\n        i = ntohs(i);\n\n        cur += VALUE_SIZE;\n\n        return true;\n    }\n    bool BinaryReadStream::ReadChar(char& c)\n    {\n        const int VALUE_SIZE = sizeof(char);\n\n        if (cur + VALUE_SIZE > ptr + len) {\n            return false;\n        }\n\n        memcpy(&c, cur, VALUE_SIZE);\n        cur += VALUE_SIZE;\n\n        return true;\n    }\n    bool BinaryReadStream::ReadLength(size_t & outlen)\n    {\n        size_t headlen;\n        if (!ReadLengthWithoutOffset(headlen, outlen)) {\n            return false;\n        }\n\n        //cur += BINARY_PACKLEN_LEN_2;\n        cur += headlen;\n        return true;\n    }\n    bool BinaryReadStream::ReadLengthWithoutOffset(size_t& headlen, size_t & outlen)\n    {\n        headlen = 0;\n        const char *temp = cur;\n        char buf[5];\n        for (size_t i = 0; i<sizeof(buf); i++)\n        {\n            memcpy(buf + i, temp, sizeof(char));\n            temp++;\n            headlen++;\n\n            //if ((buf[i] >> 7 | 0x0) == 0x0)\n            if ((buf[i] & 0x80) == 0x00)\n                break;\n        }\n        if (cur + headlen > ptr + len)\n            return false;\n\n        unsigned int value;\n        uncompress_(buf, headlen, value);\n        outlen = value;\n\n        /*if ( cur + BINARY_PACKLEN_LEN_2 > ptr + len ) {\n        return false;\n        }\n\n        unsigned int tmp;\n        memcpy(&tmp, cur, sizeof(tmp));\n        outlen = ntohl(tmp);*/\n        return true;\n    }\n    bool BinaryReadStream::IsEnd() const\n    {\n        assert(cur <= ptr + len);\n        return cur == ptr + len;\n    }\n    const char* BinaryReadStream::GetData() const\n    {\n        return ptr;\n    }\n    size_t BinaryReadStream::ReadAll(char * szBuffer, size_t iLen) const\n    {\n        size_t iRealLen = min(iLen, len);\n        memcpy(szBuffer, ptr, iRealLen);\n        return iRealLen;\n    }\n\n    //=================class BinaryWriteStream implementation============//\n    BinaryWriteStream::BinaryWriteStream(string *data) :\n        m_data(data)\n    {\n        m_data->clear();\n        char str[BINARY_PACKLEN_LEN_2 + CHECKSUM_LEN];\n        m_data->append(str, sizeof(str));\n    }\n    bool BinaryWriteStream::WriteCString(const char* str, size_t len)\n    {\n        char buf[5];\n        size_t buflen;\n        compress_(len, buf, buflen);\n        m_data->append(buf, sizeof(char)*buflen);\n\n        m_data->append(str, len);\n\n        //unsigned int ulen = htonl(len);\n        //m_data->append((char*)&ulen,sizeof(ulen));\n        //m_data->append(str,len);\n        return true;\n    }\n    bool BinaryWriteStream::WriteString(const string& str)\n    {\n        return WriteCString(str.c_str(), str.length());\n    }\n    const char* BinaryWriteStream::GetData() const\n    {\n        return m_data->data();\n    }\n    size_t BinaryWriteStream::GetSize() const\n    {\n        return m_data->length();\n    }\n    bool BinaryWriteStream::WriteInt32(int32_t i, bool isNULL)\n    {\n        int32_t i2 = 999999999;\n        if (isNULL == false)\n            i2 = htonl(i);\n        m_data->append((char*)&i2, sizeof(i2));\n        return true;\n    }\n    bool BinaryWriteStream::WriteInt64(int64_t value, bool isNULL)\n    {\n        char int64str[128];\n        if (isNULL == false)\n        {\n#ifndef _WIN32\n            sprintf(int64str, \"%ld\", value);\n#else\n            sprintf(int64str, \"%lld\", value);\n#endif\n            WriteCString(int64str, strlen(int64str));\n        }\n        else\n            WriteCString(int64str, 0);\n        return true;\n    }\n    bool BinaryWriteStream::WriteShort(short i, bool isNULL)\n    {\n        short i2 = 0;\n        if (isNULL == false)\n            i2 = htons(i);\n        m_data->append((char*)&i2, sizeof(i2));\n        return true;\n    }\n    bool BinaryWriteStream::WriteChar(char c, bool isNULL)\n    {\n        char c2 = 0;\n        if (isNULL == false)\n            c2 = c;\n        (*m_data) += c2;\n        return true;\n    }\n    bool BinaryWriteStream::WriteDouble(double value, bool isNULL)\n    {\n        char   doublestr[128];\n        if (isNULL == false)\n        {\n            sprintf(doublestr, \"%f\", value);\n            WriteCString(doublestr, strlen(doublestr));\n        }\n        else\n            WriteCString(doublestr, 0);\n        return true;\n    }\n    void BinaryWriteStream::Flush()\n    {\n        char *ptr = &(*m_data)[0];\n        unsigned int ulen = htonl(m_data->length());\n        memcpy(ptr, &ulen, sizeof(ulen));\n    }\n    void BinaryWriteStream::Clear()\n    {\n        m_data->clear();\n        char str[BINARY_PACKLEN_LEN_2 + CHECKSUM_LEN];\n        m_data->append(str, sizeof(str));\n    }\n}// end namespace\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/ProtocolStream.h",
    "content": "/**\n *  һǿЭ, protocolstream.h\n *  zhangyl 2017.05.27\n */\n\n#ifndef __PROTOCOL_STREAM_H__\n#define __PROTOCOL_STREAM_H__\n\n#include <stdlib.h>\n#include <sys/types.h>\n#include <string>\n#include <sstream>\n#include <stdint.h>\n\nusing namespace std;\n\n//ЭĴ࣬ڲķ֮ͨѶͳһЩ\nnamespace net\n{\n    enum\n    {\n        TEXT_PACKLEN_LEN = 4,\n        TEXT_PACKAGE_MAXLEN = 0xffff,\n        BINARY_PACKLEN_LEN = 2,\n        BINARY_PACKAGE_MAXLEN = 0xffff,\n\n        TEXT_PACKLEN_LEN_2 = 6,\n        TEXT_PACKAGE_MAXLEN_2 = 0xffffff,\n\n        BINARY_PACKLEN_LEN_2 = 4,               //4ֽͷ\n        BINARY_PACKAGE_MAXLEN_2 = 0x10000000,   //󳤶256M,㹻\n\n        CHECKSUM_LEN = 2,\n    };\n\n    //У\n    unsigned short checksum(const unsigned short* buffer, int size);\n    bool compress_(unsigned int i, char* buf, size_t& len);\n    bool uncompress_(char* buf, size_t len, unsigned int& i);\n\n    //ӿڶ\n    struct IReadStream\n    {\n        virtual ~IReadStream() {}\n        virtual const char* GetData() const = 0;\n        virtual size_t GetSize() const = 0;\n    };\n\n    struct IWriteStream\n    {\n        virtual ~IWriteStream() {}\n        virtual const char* GetData() const = 0;\n        virtual size_t GetSize() const = 0;\n    };\n\n    class BinaryReadStream : public IReadStream\n    {\n    private:\n        const char* const ptr;\n        const size_t      len;\n        const char*       cur;\n        BinaryReadStream(const BinaryReadStream&);\n        BinaryReadStream& operator=(const BinaryReadStream&);\n\n    public:\n        BinaryReadStream(const char* ptr, size_t len);\n        virtual const char* GetData() const;\n        virtual size_t GetSize() const;\n        bool IsEmpty() const;\n        bool ReadString(string* str, size_t maxlen, size_t& outlen);\n        bool ReadCString(char* str, size_t strlen, size_t& len);\n        bool ReadCCString(const char** str, size_t maxlen, size_t& outlen);\n        bool ReadInt32(int32_t& i);\n        bool ReadInt64(int64_t& i);\n        bool ReadShort(short& i);\n        bool ReadChar(char& c);\n        size_t ReadAll(char* szBuffer, size_t iLen) const;\n        bool IsEnd() const;\n        const char* GetCurrent() const{ return cur; }\n\n    public:\n        bool ReadLength(size_t & len);\n        bool ReadLengthWithoutOffset(size_t &headlen, size_t & outlen);\n    };\n\n    class BinaryWriteStream : public IWriteStream\n    {\n    public:\n        BinaryWriteStream(string* data);\n        virtual const char* GetData() const;\n        virtual size_t GetSize() const;\n        bool WriteCString(const char* str, size_t len);\n        bool WriteString(const string& str);\n        bool WriteDouble(double value, bool isNULL = false);\n        bool WriteInt64(int64_t value, bool isNULL = false);\n        bool WriteInt32(int32_t i, bool isNULL = false);\n        bool WriteShort(short i, bool isNULL = false);\n        bool WriteChar(char c, bool isNULL = false);\n        size_t GetCurrentPos() const{ return m_data->length(); }\n        void Flush();\n        void Clear();\n    private:\n        string* m_data;\n    };\n\n}// end namespace\n\n#endif //!__PROTOCOL_STREAM_H__"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/SelectPoller.cpp",
    "content": "#include \"SelectPoller.h\"\n\n#include <sstream>\n#include <string.h>\n\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"EventLoop.h\"\n#include \"Channel.h\"\n\nusing namespace net;\n\nnamespace\n{\n    const int kNew = -1;\n    const int kAdded = 1;\n    const int kDeleted = 2;\n}\n\nSelectPoller::SelectPoller(EventLoop* loop) : ownerLoop_(loop)\n{\n    \n}\n\nSelectPoller::~SelectPoller()\n{\n}\n\nbool SelectPoller::hasChannel(Channel* channel) const\n{\n    assertInLoopThread();\n    ChannelMap::const_iterator it = channels_.find(channel->fd());\n    return it != channels_.end() && it->second == channel;\n}\n\nvoid SelectPoller::assertInLoopThread() const\n{\n    ownerLoop_->assertInLoopThread();\n}\n\nTimestamp SelectPoller::poll(int timeoutMs, ChannelList* activeChannels)\n{    \n    fd_set readfds, writefds;\n    FD_ZERO(&readfds);\n    FD_ZERO(&writefds);\n    \n    //ȡfd \n    int maxfd = 0;\n    \n    int tmpfd;\n    for (const auto& iter : channels_)\n    {\n        tmpfd = iter.first;\n        if (tmpfd > maxfd)\n            maxfd = tmpfd;\n\n        if (iter.second->events() & XPOLLIN)\n            FD_SET(tmpfd, &readfds);\n\n        if (iter.second->events() & XPOLLOUT)\n            FD_SET(tmpfd, &writefds);\n    }\n    \n    struct timeval timeout;\n    timeout.tv_sec = timeoutMs / 1000;\n    timeout.tv_usec = (timeoutMs - timeoutMs / 1000 * 1000) * 1000;\n    int numEvents = select(maxfd + 1, &readfds, &writefds, NULL, &timeout);\n    Timestamp now(Timestamp::now());\n    if (numEvents > 0)\n    {\n        //LOG_TRACE << numEvents << \" events happended\";\n        fillActiveChannels(numEvents, activeChannels, readfds, writefds);\n        if (static_cast<size_t>(numEvents) == events_.size())\n        {\n            events_.resize(events_.size() * 2);\n        }\n    }\n    else if (numEvents == 0)\n    {\n        //LOG_TRACE << \" nothing happended\";\n    }\n    else\n    {\n        int savedErrno;\n#ifdef WIN32\n        savedErrno = (int)WSAGetLastError();\n#else\n        savedErrno = errno;\n#endif\n                     \n        LOGSYSE(\"SelectPoller::poll() error, errno: %d\", savedErrno);\n    }\n\n    return now;\n}\n\nvoid SelectPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels, fd_set& readfds, fd_set& writefds) const\n{\n    Channel* channel = NULL;\n    bool eventTriggered = false;\n    //TODOÿαchannels_Ч̫ܷͣĽ\n    int currentCount = 0;\n    for (const auto& iter : channels_)\n    {\n        channel = iter.second;\n\n        if (FD_ISSET(iter.first, &readfds))\n        {\n            channel->add_revents(XPOLLIN);\n            eventTriggered = true;\n        }\n                      \n        if (FD_ISSET(iter.first, &writefds))\n        {\n            channel->add_revents(XPOLLOUT);\n            eventTriggered = true;\n        }\n            \n        if (eventTriggered)\n        {\n            activeChannels->push_back(channel);\n            //ñ־\n            eventTriggered = false;\n\n            ++ currentCount;\n            if (currentCount >= numEvents)\n                break;\n        }            \n    }// end for-loop\n}\n\nbool SelectPoller::updateChannel(Channel* channel)\n{\n    assertInLoopThread();\n    //LOG_TRACE << \"fd = \" << channel->fd() << \" events = \" << channel->events();\n    const int index = channel->index();\n    if (index == kNew || index == kDeleted)\n    {\n        // a new one, add with XEPOLL_CTL_ADD\n        int fd = channel->fd();\n        if (index == kNew)\n        {\n            //assert(channels_.find(fd) == channels_.end())\n            if (channels_.find(fd) != channels_.end())\n            {\n                LOGE(\"fd = %d must not exist in channels_\", fd);\n                return false;\n            }\n\n\n            channels_[fd] = channel;\n\n        }\n        else // index == kDeleted\n        {\n            //assert(channels_.find(fd) != channels_.end());\n            if (channels_.find(fd) == channels_.end())\n            {\n                LOGE(\"fd = %d must not exist in channels_\", fd);\n                return false;\n            }\n\n            //assert(channels_[fd] == channel);\n            if (channels_[fd] != channel)\n            {\n                LOGE(\"current channel is not matched current fd, fd = %d\", fd);\n                return false;\n            }\n        }\n        channel->set_index(kAdded);\n\n        return update(XEPOLL_CTL_ADD, channel);\n    }\n    else\n    {\n        // update existing one with XEPOLL_CTL_MOD/DEL\n        int fd = channel->fd();\n        //assert(channels_.find(fd) != channels_.end());\n        //assert(channels_[fd] == channel);\n        //assert(index == kAdded);\n        if (channels_.find(fd) == channels_.end() || channels_[fd] != channel || index != kAdded)\n        {\n            LOGE(\"current channel is not matched current fd, fd = %d, channel = 0x%x\", fd, channel);\n            return false;\n        }\n\n        if (channel->isNoneEvent())\n        {\n            if (update(XEPOLL_CTL_DEL, channel))\n            {\n                channel->set_index(kDeleted);\n                return true;\n            }\n            return false;\n        }\n        else\n        {\n            return update(XEPOLL_CTL_MOD, channel);\n        }\n    }\n}\n\nvoid SelectPoller::removeChannel(Channel* channel)\n{\n    assertInLoopThread();\n    int fd = channel->fd();\n    //LOG_TRACE << \"fd = \" << fd;\n    //assert(channels_.find(fd) != channels_.end());\n    if (channels_.find(fd) == channels_.end())\n        return;\n\n    if (channels_[fd] != channel)\n    {\n        return;\n    }\n   // assert(channels_[fd] == channel);\n    //assert(channel->isNoneEvent());\n\n    if (!channel->isNoneEvent())\n        return;\n\n    int index = channel->index();\n    //assert(index == kAdded || index == kDeleted);\n    size_t n = channels_.erase(fd);\n    if (n != 1)\n        return;\n\n    //(void)n;\n    //assert(n == 1);\n\n    if (index == kAdded)\n    {\n        //update(XEPOLL_CTL_DEL, channel);\n    }\n    channel->set_index(kNew);\n}\n\nbool SelectPoller::update(int operation, Channel* channel)\n{\n    int fd = channel->fd();\n    if (operation == XEPOLL_CTL_ADD)\n    {\n        struct epoll_event event;\n        memset(&event, 0, sizeof event);\n        event.events = channel->events();        \n        event.data.ptr = channel;\n\n        events_.push_back(std::move(event));\n        return true;\n    }   \n\n    if (operation == XEPOLL_CTL_DEL)\n    {\n        for (auto iter = events_.begin(); iter != events_.end(); ++iter)\n        {\n            if (iter->data.ptr == channel)\n            {\n                events_.erase(iter);\n                return true;\n            }\n        }\n    }\n    else if (operation == XEPOLL_CTL_MOD)\n    {\n        for (auto iter = events_.begin(); iter != events_.end(); ++iter)\n        {\n            if (iter->data.ptr == channel)\n            {\n                iter->events = channel->events();\n                return true;\n            }\n        }\n    }\n    \n    \n    std::ostringstream os;\n    os << \"SelectPoller update fd failed, op = \" << operation << \", fd = \" << fd;\n    os << \", events_ content: \\n\";\n    for (const auto& iter : events_)\n    {\n        os << \"fd: \" << iter.data.fd << \", Channel: 0x%x: \" << iter.data.ptr << \", events: \" << iter.events << \"\\n\";\n    }\n    LOGE(\"%s\", os.str().c_str()); \n       \n    return false;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/SelectPoller.h",
    "content": "#pragma once\n\n#include \"Poller.h\"\n#include <map>\n#include \"../base/Platform.h\"\n//#include \"EventLoop.h\"\n\nnamespace net\n{\n    class EventLoop;\n    class Channel;\n\n    class SelectPoller : public Poller\n    {\n    public:\n        SelectPoller(EventLoop* loop);\n        virtual ~SelectPoller();\n\n        virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);\n        virtual bool updateChannel(Channel* channel);\n        virtual void removeChannel(Channel* channel);\n\n        virtual bool hasChannel(Channel* channel) const;\n\n        //static EPollPoller* newDefaultPoller(EventLoop* loop);\n\n        void assertInLoopThread() const;\n\n    private:\n        static const int kInitEventListSize = 16;\n\n        void fillActiveChannels(int numEvents, ChannelList* activeChannels, fd_set& readfds, fd_set& writefds) const;\n        bool update(int operation, Channel* channel);\n\n    private:\n        typedef std::vector<struct epoll_event> EventList;\n\n        int             epollfd_;\n        EventList       events_;\n\n        typedef std::map<int, Channel*> ChannelMap;\n\n        ChannelMap      channels_;\n        EventLoop*      ownerLoop_;\n    };\n\n}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Sockets.cpp",
    "content": "#include \"Sockets.h\"\n\n#include <stdio.h>  // snprintf\n#include <string.h>\n\n//#include \"../base/Platform.h\"\n\n#include \"../base/AsyncLog.h\"\n#include \"InetAddress.h\"\n#include \"Endian.h\"\n#include \"Callbacks.h\"\n\nusing namespace net;\n\nSocket::~Socket()\n{\n\tsockets::close(sockfd_);\n}\n\n//bool Socket::getTcpInfo(struct tcp_info* tcpi) const\n//{\n//\t//socklen_t len = sizeof(*tcpi);\n//\t//memset(tcpi, 0, len);\n//\t//return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpi, &len) == 0;\n//    return false;\n//}\n\nbool Socket::getTcpInfoString(char* buf, int len) const\n{\n\t//struct tcp_info tcpi;\n\t//bool ok = getTcpInfo(&tcpi);\n\t//if (ok)\n\t//{\n\t//\tsnprintf(buf, len, \"unrecovered=%u \"\n\t//\t\t\"rto=%u ato=%u snd_mss=%u rcv_mss=%u \"\n\t//\t\t\"lost=%u retrans=%u rtt=%u rttvar=%u \"\n\t//\t\t\"sshthresh=%u cwnd=%u total_retrans=%u\",\n\t//\t\ttcpi.tcpi_retransmits,  // Number of unrecovered [RTO] timeouts\n\t//\t\ttcpi.tcpi_rto,          // Retransmit timeout in usec\n\t//\t\ttcpi.tcpi_ato,          // Predicted tick of soft clock in usec\n\t//\t\ttcpi.tcpi_snd_mss,\n\t//\t\ttcpi.tcpi_rcv_mss,\n\t//\t\ttcpi.tcpi_lost,         // Lost packets\n\t//\t\ttcpi.tcpi_retrans,      // Retransmitted packets out\n\t//\t\ttcpi.tcpi_rtt,          // Smoothed round trip time in usec\n\t//\t\ttcpi.tcpi_rttvar,       // Medium deviation\n\t//\t\ttcpi.tcpi_snd_ssthresh,\n\t//\t\ttcpi.tcpi_snd_cwnd,\n\t//\t\ttcpi.tcpi_total_retrans);  // Total retransmits for entire connection\n\t//}\n\t//return ok;\n    return false;\n}\n\nvoid Socket::bindAddress(const InetAddress& addr)\n{\n\tsockets::bindOrDie(sockfd_, addr.getSockAddrInet());\n}\n\nvoid Socket::listen()\n{\n\tsockets::listenOrDie(sockfd_);\n}\n\nint Socket::accept(InetAddress* peeraddr)\n{\n\tstruct sockaddr_in addr;\n\tmemset(&addr, 0, sizeof addr);\n\tint connfd = sockets::accept(sockfd_, &addr);\n\tif (connfd >= 0)\n\t{\n\t\tpeeraddr->setSockAddrInet(addr);\n\t}\n\treturn connfd;\n}\n\nvoid Socket::shutdownWrite()\n{\n\tsockets::shutdownWrite(sockfd_);\n}\n\nvoid Socket::setTcpNoDelay(bool on)\n{\n\tint optval = on ? 1 : 0;\n#ifdef WIN32\n    ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char*)&optval, sizeof(optval));\n#else\n\t::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, static_cast<socklen_t>(sizeof optval));\n#endif\n\t// FIXME CHECK\n}\n\nvoid Socket::setReuseAddr(bool on)\n{\n    sockets::setReuseAddr(sockfd_, on);\n}\n\nvoid Socket::setReusePort(bool on)\n{\n    sockets::setReusePort(sockfd_, on);\n}\n\nvoid Socket::setKeepAlive(bool on)\n{\n#ifdef WIN32\n    //TODO: ȫWindowsд\n#else\n\tint optval = on ? 1 : 0;\n\t::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, static_cast<socklen_t>(sizeof optval));\n#endif\n\t// FIXME CHECK\n}\n\n//namespace\n//{\n//\t//typedef struct sockaddr SA;\n//\n//\t\n//\n//}\n\nconst struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)\n{\n\treturn static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));\n}\n\nstruct sockaddr* sockets::sockaddr_cast(struct sockaddr_in* addr)\n{\n\treturn static_cast<struct sockaddr*>(implicit_cast<void*>(addr));\n}\n\nconst struct sockaddr_in* sockets::sockaddr_in_cast(const struct sockaddr* addr)\n{\n\treturn static_cast<const struct sockaddr_in*>(implicit_cast<const void*>(addr));\n}\n\nstruct sockaddr_in* sockets::sockaddr_in_cast(struct sockaddr* addr)\n{\n\treturn static_cast<struct sockaddr_in*>(implicit_cast<void*>(addr));\n}\n\nSOCKET sockets::createOrDie()\n{\n#ifdef WIN32\n    SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    if (sockfd < 0)\n    {\n        LOGF(\"sockets::createNonblockingOrDie\");\n    }\n#else\n    SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);\n    if (sockfd < 0)\n    {\n        LOGF(\"sockets::createNonblockingOrDie\");\n    }\n#endif\n\n    return sockfd;\n}\n\nSOCKET sockets::createNonblockingOrDie()\n{\n#ifdef WIN32\n    SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    if (sockfd < 0)\n    {\n        LOGF(\"sockets::createNonblockingOrDie\");\n    }   \n#else\n    SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);\n    if (sockfd < 0)\n    {\n        LOGF(\"sockets::createNonblockingOrDie\");\n    } \n#endif\n\n    setNonBlockAndCloseOnExec(sockfd);\n\treturn sockfd;\n}\n\nvoid sockets::setNonBlockAndCloseOnExec(SOCKET sockfd)\n{\n#ifdef WIN32\n    //socketóɷ\n    unsigned long on = 1;\n    ::ioctlsocket(sockfd, FIONBIO, &on);\n#else\n    // non-block\n    int flags = ::fcntl(sockfd, F_GETFL, 0);\n    flags |= O_NONBLOCK;\n    int ret = ::fcntl(sockfd, F_SETFL, flags);\n    // FIXME check\n\n    // close-on-exec\n    flags = ::fcntl(sockfd, F_GETFD, 0);\n    flags |= FD_CLOEXEC;\n    ret = ::fcntl(sockfd, F_SETFD, flags);\n    // FIXME check\n\n    (void)ret;\n#endif       \n}\n\nvoid sockets::bindOrDie(SOCKET sockfd, const struct sockaddr_in& addr)\n{\n\tint ret = ::bind(sockfd, sockaddr_cast(&addr), static_cast<socklen_t>(sizeof addr));\n\tif (ret < 0)\n\t{\n\t\tLOGF(\"sockets::bindOrDie\");\n\t}\n}\n\nvoid sockets::listenOrDie(SOCKET sockfd)\n{\n\tint ret = ::listen(sockfd, SOMAXCONN);\n\tif (ret < 0)\n\t{\n\t\tLOGF(\"sockets::listenOrDie\");\n\t}\n}\n\nint sockets::accept(SOCKET sockfd, struct sockaddr_in* addr)\n{\n\tsocklen_t addrlen = static_cast<socklen_t>(sizeof *addr);\n#ifdef WIN32\n    SOCKET connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);\n    setNonBlockAndCloseOnExec(connfd);\n#else  \n    SOCKET connfd = ::accept4(sockfd, sockaddr_cast(addr), &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);\n#endif\n\tif (connfd < 0)\n\t{\n#ifdef WIN32\n        int savedErrno = ::WSAGetLastError();\n        LOGSYSE(\"Socket::accept\");\n        if (savedErrno != WSAEWOULDBLOCK)\n            LOGF(\"unexpected error of ::accept %d\", savedErrno);\t\t\n#else\n        int savedErrno = errno;\n        LOGSYSE(\"Socket::accept\");\n        switch (savedErrno)\n        {\n        case EAGAIN:\n        case ECONNABORTED:\n        case EINTR:\n        case EPROTO: // ???\n        case EPERM:\n        case EMFILE: // per-process lmit of open file desctiptor ???\n            // expected errors\n            errno = savedErrno;\n            break;\n        case EBADF:\n        case EFAULT:\n        case EINVAL:\n        case ENFILE:\n        case ENOBUFS:\n        case ENOMEM:\n        case ENOTSOCK:\n        case EOPNOTSUPP:\n            // unexpected errors\n            LOGF(\"unexpected error of ::accept %d\", savedErrno);\n            break;\n        default:\n            LOGF(\"unknown error of ::accept %d\", savedErrno);\n            break;\n    }\n        \n#endif\n\t}\n\n\treturn connfd;\n}\n\nvoid sockets::setReuseAddr(SOCKET sockfd, bool on)\n{\n    int optval = on ? 1 : 0;\n#ifdef WIN32\n    ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)& optval, sizeof(optval));\n#else\n    ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, static_cast<socklen_t>(sizeof optval));\n#endif\n    // FIXME CHECK\n}\n\nvoid sockets::setReusePort(SOCKET sockfd, bool on)\n{\n    //Windows ϵͳû SO_REUSEPORT ѡ\n#ifndef WIN32\n    int optval = on ? 1 : 0;\n    int ret = ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, static_cast<socklen_t>(sizeof optval));\n    if (ret < 0 && on)\n    {\n        LOGSYSE(\"SO_REUSEPORT failed.\");\n    }\n#endif\n}\n\nint sockets::connect(SOCKET sockfd, const struct sockaddr_in& addr)\n{\n\treturn ::connect(sockfd, sockaddr_cast(&addr), static_cast<socklen_t>(sizeof addr));\n}\n\nint32_t sockets::read(SOCKET sockfd, void *buf, int32_t count)\n{\n#ifdef WIN32\n    return ::recv(sockfd, (char*)buf, count, 0);\n#else\n\treturn ::read(sockfd, buf, count);\n#endif\n}\n\n#ifndef WIN32\nssize_t sockets::readv(SOCKET sockfd, const struct iovec *iov, int iovcnt)\n{\n\treturn ::readv(sockfd, iov, iovcnt);\n}\n#endif\n\nint32_t sockets::write(SOCKET sockfd, const void *buf, int32_t count)\n{\n#ifdef WIN32\n    return ::send(sockfd, (const char*)buf, count, 0);\n#else\n    return ::write(sockfd, buf, count);\n#endif\n    \n}\n\nvoid sockets::close(SOCKET sockfd)\n{\n#ifdef WIN32   \n    if (::closesocket(sockfd) < 0)\n#else\n    if (::close(sockfd) < 0)\n#endif\n    {\n        LOGSYSE(\"sockets::close, fd=%d, errno=%d, errorinfo=%s\", sockfd, errno, strerror(errno));\n    } \n}\n\nvoid sockets::shutdownWrite(SOCKET sockfd)\n{\n#ifdef WIN32\n    if (::shutdown(sockfd, SD_SEND) < 0)\t\n#else\n    if (::shutdown(sockfd, SHUT_WR) < 0)\n#endif        \n\t{\n\t\tLOGSYSE(\"sockets::shutdownWrite\");\n\t}\n}\n\nvoid sockets::toIpPort(char* buf, size_t size, const struct sockaddr_in& addr)\n{\n    //if (size >= sizeof(struct sockaddr_in))\n    //    return;\n\n\t::inet_ntop(AF_INET, &addr.sin_addr, buf, static_cast<socklen_t>(size));\n\tsize_t end = ::strlen(buf);\n\tuint16_t port = sockets::networkToHost16(addr.sin_port);\n    //if (size > end)\n    //    return;\n\n\tsnprintf(buf + end, size - end, \":%u\", port);\n}\n\nvoid sockets::toIp(char* buf, size_t size, const struct sockaddr_in& addr)\n{\n    if (size >= sizeof(struct sockaddr_in))\n        return;\n\n    ::inet_ntop(AF_INET, &addr.sin_addr, buf, static_cast<socklen_t>(size));\n}\n\nvoid sockets::fromIpPort(const char* ip, uint16_t port, struct sockaddr_in* addr)\n{\n\taddr->sin_family = AF_INET;\n    //TODO: УдĶԲ\n#ifdef WIN32\n    addr->sin_port = htons(port);\n#else\n\taddr->sin_port = htobe16(port);\n#endif\n\tif (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)\n\t{\n\t\tLOGSYSE(\"sockets::fromIpPort\");\n\t}\n}\n\nint sockets::getSocketError(SOCKET sockfd)\n{\n\tint optval;\n#ifdef WIN32\n    int optvallen = sizeof(optval);\n    if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&optval, &optvallen) < 0)\n        return ::WSAGetLastError();\n#else\n\tsocklen_t optlen = static_cast<socklen_t>(sizeof optval);\n\n\tif (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)\n\t\treturn errno;\n#endif\n\treturn optval;\n}\n\nstruct sockaddr_in sockets::getLocalAddr(SOCKET sockfd)\n{\n    struct sockaddr_in localaddr = { 0 };\n\tmemset(&localaddr, 0, sizeof localaddr);\n\tsocklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);\n    ::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen);\n\t//if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)\n\t//{\n\t//\tLOG_SYSERR << \"sockets::getLocalAddr\";\n    //  return \n\t//}\n\treturn localaddr;\n}\n\nstruct sockaddr_in sockets::getPeerAddr(SOCKET sockfd)\n{\n    struct sockaddr_in peeraddr = { 0 };\n\tmemset(&peeraddr, 0, sizeof peeraddr);\n\tsocklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);\n    ::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen);\n\t//if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)\n\t//{\n\t//\tLOG_SYSERR << \"sockets::getPeerAddr\";\n\t//}\n\treturn peeraddr;\n}\n\nbool sockets::isSelfConnect(SOCKET sockfd)\n{\n\tstruct sockaddr_in localaddr = getLocalAddr(sockfd);\n\tstruct sockaddr_in peeraddr = getPeerAddr(sockfd);\n\treturn localaddr.sin_port == peeraddr.sin_port && localaddr.sin_addr.s_addr == peeraddr.sin_addr.s_addr;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Sockets.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include \"../base/Platform.h\"\n\n// struct tcp_info is in <netinet/tcp.h>\nstruct tcp_info;\n\nnamespace net\n{\n\tclass InetAddress;\n\n\t///\n\t/// Wrapper of socket file descriptor.\n\t///\n\t/// It closes the sockfd when desctructs.\n\t/// It's thread safe, all operations are delagated to OS.\n\tclass Socket\n\t{\n\tpublic:      \n\t\texplicit Socket(int sockfd) : sockfd_(sockfd)\n\t\t{ }\n\n\t\t// Socket(Socket&&) // move constructor in C++11\n\t\t~Socket();\n\n        SOCKET fd() const { return sockfd_; }\n\t\t// return true if success.\n        //TODO: ע͵\n\t\t//bool getTcpInfo(struct tcp_info*) const;\n\t\tbool getTcpInfoString(char* buf, int len) const;\n\n\t\t/// abort if address in use\n\t\tvoid bindAddress(const InetAddress& localaddr);\n\t\t/// abort if address in use\n\t\tvoid listen();\n\n\t\t/// On success, returns a non-negative integer that is\n\t\t/// a descriptor for the accepted socket, which has been\n\t\t/// set to non-blocking and close-on-exec. *peeraddr is assigned.\n\t\t/// On error, -1 is returned, and *peeraddr is untouched.\n\t\tint accept(InetAddress* peeraddr);\n\n\t\tvoid shutdownWrite();\n\n\t\t///\n\t\t/// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm).\n\t\t///\n\t\tvoid setTcpNoDelay(bool on);\n\n\t\t///\n\t\t/// Enable/disable SO_REUSEADDR\n\t\t///\n\t\tvoid setReuseAddr(bool on);\n\n\t\t///\n\t\t/// Enable/disable SO_REUSEPORT\n\t\t///\n\t\tvoid setReusePort(bool on);\n\n\t\t///\n\t\t/// Enable/disable SO_KEEPALIVE\n\t\t///\n\t\tvoid setKeepAlive(bool on);\n\n\tprivate:\n\t\tconst SOCKET sockfd_;\n\t};\n\n\tnamespace sockets\n\t{\n\t\t///\n\t\t/// Creates a socket file descriptor,\n\t\t/// abort if any error.\n        SOCKET createOrDie();\n        SOCKET createNonblockingOrDie();\n\n        void setNonBlockAndCloseOnExec(SOCKET sockfd);\n\n        void setReuseAddr(SOCKET sockfd, bool on);\n        void setReusePort(SOCKET sockfd, bool on);\n\n\t\tint  connect(SOCKET sockfd, const struct sockaddr_in& addr);\n\t\tvoid bindOrDie(SOCKET sockfd, const struct sockaddr_in& addr);\n\t\tvoid listenOrDie(SOCKET sockfd);\n\t\tint  accept(SOCKET sockfd, struct sockaddr_in* addr);\n        int32_t read(SOCKET sockfd, void *buf, int32_t count);\n#ifndef WIN32\n\t\tssize_t readv(SOCKET sockfd, const struct iovec *iov, int iovcnt);\n#endif\n\t\tint32_t write(SOCKET sockfd, const void *buf, int32_t count);\n\t\tvoid close(SOCKET sockfd);\n\t\tvoid shutdownWrite(SOCKET sockfd);\n\n\t\tvoid toIpPort(char* buf, size_t size, const struct sockaddr_in& addr);\n\t\tvoid toIp(char* buf, size_t size, const struct sockaddr_in& addr);\n\t\tvoid fromIpPort(const char* ip, uint16_t port, struct sockaddr_in* addr);\n\n\t\tint getSocketError(SOCKET sockfd);\n\n\t\tconst struct sockaddr* sockaddr_cast(const struct sockaddr_in* addr);\n\t\tstruct sockaddr* sockaddr_cast(struct sockaddr_in* addr);\n\t\tconst struct sockaddr_in* sockaddr_in_cast(const struct sockaddr* addr);\n\t\tstruct sockaddr_in* sockaddr_in_cast(struct sockaddr* addr);\n\n\t\tstruct sockaddr_in getLocalAddr(SOCKET sockfd);\n\t\tstruct sockaddr_in getPeerAddr(SOCKET sockfd);\n\t\tbool isSelfConnect(SOCKET sockfd);\n\t}\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TcpClient.cpp",
    "content": "#include <stdio.h>  // snprintf\n#include <functional>\n\n#include \"TcpClient.h\"\n#include \"../base/AsyncLog.h\"\n#include \"../base/Platform.h\"\n#include \"EventLoop.h\"\n#include \"Sockets.h\"\n#include \"Connector.h\"\n\nusing namespace net;\n\n// TcpClient::TcpClient(EventLoop* loop)\n//   : loop_(loop)\n// {\n// }\n\n// TcpClient::TcpClient(EventLoop* loop, const string& host, uint16_t port)\n//   : loop_(CHECK_NOTNULL(loop)),\n//     serverAddr_(host, port)\n// {\n// }\n\nnamespace net\n{\n    namespace detail\n    {\n\n        void removeConnection(EventLoop* loop, const TcpConnectionPtr& conn)\n        {\n            loop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));\n        }\n\n        void removeConnector(const ConnectorPtr& connector)\n        {\n            //connector->\n        }\n\n    }\n}\n\n\nTcpClient::TcpClient(EventLoop* loop,\n    const InetAddress& serverAddr,\n    const std::string& nameArg)\n    : loop_(loop),\n    connector_(new Connector(loop, serverAddr)),\n    name_(nameArg),\n    connectionCallback_(defaultConnectionCallback),\n    messageCallback_(defaultMessageCallback),\n    retry_(false),\n    connect_(true),\n    nextConnId_(1)\n{\n    connector_->setNewConnectionCallback(\n        std::bind(&TcpClient::newConnection, this, std::placeholders::_1));\n    // FIXME setConnectFailedCallback\n    LOGD(\"TcpClient::TcpClient[%s] - connector 0x%x\", name_.c_str(), connector_.get());\n}\n\nTcpClient::~TcpClient()\n{\n    LOGD(\"TcpClient::~TcpClient[%s] - connector 0x%x\", name_.c_str(), connector_.get());\n    TcpConnectionPtr conn;\n    bool unique = false;\n    {\n        std::unique_lock<std::mutex> lock(mutex_);\n        unique = connection_.unique();\n        conn = connection_;\n    }\n    if (conn)\n    {\n        //assert(loop_ == conn->getLoop());\n        if (loop_ != conn->getLoop())\n            return;\n\n        // FIXME: not 100% safe, if we are in different thread\n        CloseCallback cb = std::bind(&detail::removeConnection, loop_, std::placeholders::_1);\n        loop_->runInLoop(std::bind(&TcpConnection::setCloseCallback, conn, cb));\n        if (unique)\n        {\n            conn->forceClose();\n        }\n    }\n    else\n    {\n        connector_->stop();\n        // FIXME: HACK\n        // loop_->runAfter(1, boost::bind(&detail::removeConnector, connector_));\n    }\n}\n\nvoid TcpClient::connect()\n{\n    // FIXME: check state\n    LOGD(\"TcpClient::connect[%s] - connecting to %s\", name_.c_str(), connector_->serverAddress().toIpPort().c_str());\n    connect_ = true;\n    connector_->start();\n}\n\nvoid TcpClient::disconnect()\n{\n    connect_ = false;\n\n    {\n        std::unique_lock<std::mutex> lock(mutex_);\n        if (connection_)\n        {\n            connection_->shutdown();\n        }\n    }\n}\n\nvoid TcpClient::stop()\n{\n    connect_ = false;\n    connector_->stop();\n}\n\nvoid TcpClient::newConnection(int sockfd)\n{\n    loop_->assertInLoopThread();\n    InetAddress peerAddr(sockets::getPeerAddr(sockfd));\n    char buf[32];\n    snprintf(buf, sizeof buf, \":%s#%d\", peerAddr.toIpPort().c_str(), nextConnId_);\n    ++nextConnId_;\n    string connName = name_ + buf;\n\n    InetAddress localAddr(sockets::getLocalAddr(sockfd));\n    // FIXME poll with zero timeout to double confirm the new connection\n    // FIXME use make_shared if necessary\n    TcpConnectionPtr conn(new TcpConnection(loop_, connName, sockfd, localAddr, peerAddr));\n    conn->setConnectionCallback(connectionCallback_);\n    conn->setMessageCallback(messageCallback_);\n    conn->setWriteCompleteCallback(writeCompleteCallback_);\n    conn->setCloseCallback(std::bind(&TcpClient::removeConnection, this, std::placeholders::_1)); // FIXME: unsafe\n    \n    {\n        std::unique_lock<std::mutex> lock(mutex_);\n        connection_ = conn;\n    }\n\n    conn->connectEstablished();\n}\n\nvoid TcpClient::removeConnection(const TcpConnectionPtr& conn)\n{\n    loop_->assertInLoopThread();\n    //assert(loop_ == conn->getLoop());\n    if (loop_ != conn->getLoop())\n        return;\n\n    {\n        std::unique_lock<std::mutex> lock(mutex_);\n        //assert(connection_ == conn);\n        if (connection_ != conn)\n            return;\n\n        connection_.reset();\n    }\n\n    loop_->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));\n    if (retry_ && connect_)\n    {\n        LOGD( \"TcpClient::connect[%s] - Reconnecting to %s\", name_.c_str(), connector_->serverAddress().toIpPort().c_str());\n        connector_->restart();\n    }\n}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TcpClient.h",
    "content": "#pragma once\n\n#include <string>\n#include <mutex>\n#include \"TcpConnection.h\"\n\nnamespace net\n{\n    class EventLoop;\n    class Connector;\n    typedef std::shared_ptr<Connector> ConnectorPtr;\n\n    class TcpClient\n    {\n    public:\n        // TcpClient(EventLoop* loop);\n        // TcpClient(EventLoop* loop, const string& host, uint16_t port);\n        TcpClient(EventLoop* loop, const InetAddress& serverAddr, const string& nameArg);\n        ~TcpClient();  // force out-line dtor, for scoped_ptr members.\n\n        void connect();\n        void disconnect();\n        void stop();\n\n        TcpConnectionPtr connection() const\n        {\n            std::unique_lock<std::mutex> lock(mutex_);\n            return connection_;\n        }\n\n        EventLoop* getLoop() const { return loop_; }\n        bool retry() const;\n        void enableRetry() { retry_ = true; }\n\n        const std::string& name() const\n        {\n            return name_;\n        }\n\n        /// Set connection callback.\n        /// Not thread safe.\n        void setConnectionCallback(const ConnectionCallback& cb)\n        {\n            connectionCallback_ = cb;\n        }\n\n        /// Set message callback.\n        /// Not thread safe.\n        void setMessageCallback(const MessageCallback& cb)\n        {\n            messageCallback_ = cb;\n        }\n\n        /// Set write complete callback.\n        /// Not thread safe.\n        void setWriteCompleteCallback(const WriteCompleteCallback& cb)\n        {\n            writeCompleteCallback_ = cb;\n        }\n\n    private:\n        /// Not thread safe, but in loop\n        void newConnection(int sockfd);\n        /// Not thread safe, but in loop\n        void removeConnection(const TcpConnectionPtr& conn);\n\n        EventLoop* loop_;\n        ConnectorPtr connector_; // avoid revealing Connector\n        const std::string name_;\n        ConnectionCallback connectionCallback_;\n        MessageCallback messageCallback_;\n        WriteCompleteCallback writeCompleteCallback_;\n        bool retry_;   // atomic\n        bool connect_; // atomic\n        // always in loop thread\n        int nextConnId_;\n        mutable std::mutex mutex_;\n        TcpConnectionPtr connection_; // @GuardedBy mutex_\n    };\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TcpConnection.cpp",
    "content": "#include \"TcpConnection.h\"\n\n#include <functional>\n#include <thread>\n#include <sstream>\n#include <errno.h>\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"Sockets.h\"\n#include \"EventLoop.h\"\n#include \"Channel.h\"\n\nusing namespace net;\n\nvoid net::defaultConnectionCallback(const TcpConnectionPtr& conn)\n{\n    LOGD(\"%s -> is %s\",\n        conn->localAddress().toIpPort().c_str(),\n        conn->peerAddress().toIpPort().c_str(),\n        (conn->connected() ? \"UP\" : \"DOWN\"));\n    // do not call conn->forceClose(), because some users want to register message callback only.\n}\n\nvoid net::defaultMessageCallback(const TcpConnectionPtr&, Buffer* buf, Timestamp)\n{\n    buf->retrieveAll();\n}\n\nTcpConnection::TcpConnection(EventLoop* loop, const string& nameArg, int sockfd, const InetAddress& localAddr, const InetAddress& peerAddr)\n    : loop_(loop),\n    name_(nameArg),\n    state_(kConnecting),\n    socket_(new Socket(sockfd)),\n    channel_(new Channel(loop, sockfd)),\n    localAddr_(localAddr),\n    peerAddr_(peerAddr),\n    highWaterMark_(64 * 1024 * 1024)\n{ \n    channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));\n    channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));\n    channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));\n    channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));\n    LOGD(\"TcpConnection::ctor[%s] at 0x%x fd=%d\", name_.c_str(), this, sockfd);\n    socket_->setKeepAlive(true);\n}\n\nTcpConnection::~TcpConnection()\n{\n    LOGD(\"TcpConnection::dtor[%s] at 0x%x fd=%d state=%s\", \n         name_.c_str(), this, channel_->fd(),stateToString());\n    //assert(state_ == kDisconnected);\n}\n\nbool TcpConnection::getTcpInfo(struct tcp_info* tcpi) const\n{\n    //return socket_->getTcpInfo(tcpi);\n    return false;\n}\n\nstring TcpConnection::getTcpInfoString() const\n{\n    char buf[1024];\n    buf[0] = '\\0';\n    socket_->getTcpInfoString(buf, sizeof buf);\n    return buf;\n}\n\nvoid TcpConnection::send(const void* data, int len)\n{\n    if (state_ == kConnected)\n    {\n        if (loop_->isInLoopThread())\n        {\n            sendInLoop(data, len);\n        }\n        else\n        {\n            string message(static_cast<const char*>(data), len);\n            loop_->runInLoop(\n                std::bind(static_cast<void (TcpConnection::*)(const string&)>(&TcpConnection::sendInLoop),\n                    this,     // FIXME\n                    message));\n        }\n    }\n}\n\nvoid TcpConnection::send(const string & message)\n{\n    if (state_ == kConnected)\n    {\n        if (loop_->isInLoopThread())\n        {\n            sendInLoop(message);\n        }\n        else\n        {\n            loop_->runInLoop(\n                std::bind(static_cast<void (TcpConnection::*)(const string&)>(&TcpConnection::sendInLoop),\n                    this,     // FIXME\n                    message));\n            //std::forward<string>(message)));\n        }\n    }\n}\n\n// FIXME efficiency!!!\nvoid TcpConnection::send(Buffer * buf)\n{\n    if (state_ == kConnected)\n    {\n        if (loop_->isInLoopThread())\n        {\n            sendInLoop(buf->peek(), buf->readableBytes());\n            buf->retrieveAll();\n        }\n        else\n        {\n            loop_->runInLoop(\n                std::bind(static_cast<void (TcpConnection::*)(const string&)>(&TcpConnection::sendInLoop),\n                    this,     // FIXME\n                    buf->retrieveAllAsString()));\n            //std::forward<string>(message)));\n        }\n    }\n}\n\nvoid TcpConnection::sendInLoop(const string & message)\n{\n    sendInLoop(message.c_str(), message.size());\n}\n\nvoid TcpConnection::sendInLoop(const void* data, size_t len)\n{\n    loop_->assertInLoopThread();\n    int32_t nwrote = 0;\n    size_t remaining = len;\n    bool faultError = false;\n    if (state_ == kDisconnected)\n    {\n        LOGW(\"disconnected, give up writing\");\n        return;\n    }\n    // if no thing in output queue, try writing directly\n    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)\n    {\n        nwrote = sockets::write(channel_->fd(), data, len);\n        //TODO: ӡthreadidڵԣȥ\n        //std::stringstream ss;\n        //ss << std::this_thread::get_id();\n        //LOGI << \"send data in threadID = \" << ss;\n\n        if (nwrote >= 0)\n        {\n            remaining = len - nwrote;\n            if (remaining == 0 && writeCompleteCallback_)\n            {\n                loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));\n            }\n        }\n        else // nwrote < 0\n        {\n            nwrote = 0;\n            if (errno != EWOULDBLOCK)\n            {\n                LOGSYSE(\"TcpConnection::sendInLoop\");\n                if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?\n                {\n                    faultError = true;\n                }\n            }\n        }\n    }\n\n    //assert(remaining <= len);\n    if (remaining > len)\n        return;\n\n    if (!faultError && remaining > 0)\n    {\n        size_t oldLen = outputBuffer_.readableBytes();\n        if (oldLen + remaining >= highWaterMark_\n            && oldLen < highWaterMark_\n            && highWaterMarkCallback_)\n        {\n            loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));\n        }\n        outputBuffer_.append(static_cast<const char*>(data) + nwrote, remaining);\n        if (!channel_->isWriting())\n        {\n            channel_->enableWriting();\n        }\n    }\n}\n\nvoid TcpConnection::shutdown()\n{\n    // FIXME: use compare and swap\n    if (state_ == kConnected)\n    {\n        setState(kDisconnecting);\n        // FIXME: shared_from_this()?\n        loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));\n    }\n}\n\nvoid TcpConnection::shutdownInLoop()\n{\n    loop_->assertInLoopThread();\n    if (!channel_->isWriting())\n    {\n        // we are not writing\n        socket_->shutdownWrite();\n    }\n}\n\n// void TcpConnection::shutdownAndForceCloseAfter(double seconds)\n// {\n//   // FIXME: use compare and swap\n//   if (state_ == kConnected)\n//   {\n//     setState(kDisconnecting);\n//     loop_->runInLoop(boost::bind(&TcpConnection::shutdownAndForceCloseInLoop, this, seconds));\n//   }\n// }\n\n// void TcpConnection::shutdownAndForceCloseInLoop(double seconds)\n// {\n//   loop_->assertInLoopThread();\n//   if (!channel_->isWriting())\n//   {\n//     // we are not writing\n//     socket_->shutdownWrite();\n//   }\n//   loop_->runAfter(\n//       seconds,\n//       makeWeakCallback(shared_from_this(),\n//                        &TcpConnection::forceCloseInLoop));\n// }\n\nvoid TcpConnection::forceClose()\n{\n    // FIXME: use compare and swap\n    if (state_ == kConnected || state_ == kDisconnecting)\n    {\n        setState(kDisconnecting);\n        loop_->queueInLoop(std::bind(&TcpConnection::forceCloseInLoop, shared_from_this()));\n    }\n}\n\n\nvoid TcpConnection::forceCloseInLoop()\n{\n    loop_->assertInLoopThread();\n    if (state_ == kConnected || state_ == kDisconnecting)\n    {\n        // as if we received 0 byte in handleRead();\n        handleClose();\n    }\n}\n\nconst char* TcpConnection::stateToString() const\n{\n    switch (state_)\n    {\n    case kDisconnected:\n        return \"kDisconnected\";\n    case kConnecting:\n        return \"kConnecting\";\n    case kConnected:\n        return \"kConnected\";\n    case kDisconnecting:\n        return \"kDisconnecting\";\n    default:\n        return \"unknown state\";\n    }\n}\n\nvoid TcpConnection::setTcpNoDelay(bool on)\n{\n    socket_->setTcpNoDelay(on);\n}\n\nvoid TcpConnection::connectEstablished()\n{\n    loop_->assertInLoopThread();\n    if (state_ != kConnecting)\n    {\n        //һ֧\n        return;\n    }\n        \n    setState(kConnected);\n    channel_->tie(shared_from_this());\n\n    //ִдʱԶ˹ر\n    if (!channel_->enableReading())\n    {\n        LOGE(\"enableReading failed.\");\n        //setState(kDisconnected);\n        handleClose();\n        return;\n    }\n\n    //connectionCallback_ָvoid XXServer::OnConnection(const std::shared_ptr<TcpConnection>& conn)\n    connectionCallback_(shared_from_this());\n}\n\nvoid TcpConnection::connectDestroyed()\n{\n    loop_->assertInLoopThread();\n    if (state_ == kConnected)\n    {\n        setState(kDisconnected);\n        channel_->disableAll();\n\n        connectionCallback_(shared_from_this());\n    }\n    channel_->remove();\n}\n\nvoid TcpConnection::handleRead(Timestamp receiveTime)\n{\n    loop_->assertInLoopThread();\n    int savedErrno = 0;\n    int32_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);\n    if (n > 0)\n    {\n        //messageCallback_ָCTcpSession::OnRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receiveTime)\n        messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);\n    }\n    else if (n == 0)\n    {\n        handleClose();\n    }\n    else\n    {\n        errno = savedErrno;\n        LOGSYSE(\"TcpConnection::handleRead\");\n        handleError();\n    }\n}\n\nvoid TcpConnection::handleWrite()\n{\n    loop_->assertInLoopThread();\n    if (channel_->isWriting())\n    {\n        int32_t n = sockets::write(channel_->fd(), outputBuffer_.peek(), outputBuffer_.readableBytes());\n        if (n > 0)\n        {\n            outputBuffer_.retrieve(n);\n            if (outputBuffer_.readableBytes() == 0)\n            {\n                channel_->disableWriting();\n                if (writeCompleteCallback_)\n                {\n                    loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));\n                }\n                if (state_ == kDisconnecting)\n                {\n                    shutdownInLoop();\n                }\n            }\n        }\n        else\n        {\n            LOGSYSE(\"TcpConnection::handleWrite\");\n            // if (state_ == kDisconnecting)\n            // {\n            //   shutdownInLoop();\n            // }\n            //added by zhangyl 2019.05.06\n            handleClose();\n        }\n    }\n    else\n    {\n        LOGD(\"Connection fd = %d  is down, no more writing\", channel_->fd());\n    }\n}\n\nvoid TcpConnection::handleClose()\n{\n    //Linuxϵһӳ⣬ͬʱhandleErrorhandleClose\n    //Ϊ˱ظرӣжµǰ״̬\n    //Ѿرˣֱӷ\n    if (state_ == kDisconnected)\n        return;\n    \n    loop_->assertInLoopThread();\n    LOGD(\"fd = %d  state = %s\", channel_->fd(), stateToString());\n    //assert(state_ == kConnected || state_ == kDisconnecting);\n    // we don't close fd, leave it to dtor, so we can find leaks easily.\n    setState(kDisconnected);\n    channel_->disableAll();\n\n    TcpConnectionPtr guardThis(shared_from_this());\n    connectionCallback_(guardThis);\n    // must be the last line\n    closeCallback_(guardThis);\n\n    //ֻҵϵĹرգsocket fdTcpConnectionйر\n    //if (socket_)\n    //{\n    //    sockets::close(socket_->fd());\n    //}\n}\n\nvoid TcpConnection::handleError()\n{\n    int err = sockets::getSocketError(channel_->fd());\n    //󣬴ӡһ\n    LOGD(\"TcpConnection::%s handleError [%d] - SO_ERROR = %s\" , name_.c_str(), err , strerror(err));\n\n    //handleClose()رӣChannelfd\n    handleClose();    \n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TcpConnection.h",
    "content": "#pragma once\n\n#include <memory>\n\n#include \"Callbacks.h\"\n#include \"Buffer.h\"\n#include \"InetAddress.h\"\n//#include \"EventLoop.h\"\n\n// struct tcp_info is in <netinet/tcp.h>\nstruct tcp_info;\n\nnamespace net\n{\n    class EventLoop;\n\tclass Channel;\n\tclass Socket;\n\n\t///\n\t/// TCP connection, for both client and server usage.\n\t///\n\t/// This is an interface class, so don't expose too much details.\n\tclass TcpConnection : public std::enable_shared_from_this<TcpConnection>\n\t{\n\tpublic:\n\t\t/// Constructs a TcpConnection with a connected sockfd\n\t\t///\n\t\t/// User should not create this object.\n\t\tTcpConnection(EventLoop* loop,\n\t\t\t            const string& name,\n\t\t\t            int sockfd,\n\t\t\t            const InetAddress& localAddr,\n\t\t\t            const InetAddress& peerAddr);\n\t\t~TcpConnection();\n\n\t\tEventLoop* getLoop() const { return loop_; }\n\t\tconst string& name() const { return name_; }\n\t\tconst InetAddress& localAddress() const { return localAddr_; }\n\t\tconst InetAddress& peerAddress() const { return peerAddr_; }\n\t\tbool connected() const { return state_ == kConnected; }\n\t\t// return true if success.\n\t\tbool getTcpInfo(struct tcp_info*) const;\n\t\tstring getTcpInfoString() const;\n\n\t\t// void send(string&& message); // C++11\n\t\tvoid send(const void* message, int len);\n\t\tvoid send(const string& message);\n\t\t// void send(Buffer&& message); // C++11\n\t\tvoid send(Buffer* message);  // this one will swap data\n\t\tvoid shutdown(); // NOT thread safe, no simultaneous calling\n\t\t// void shutdownAndForceCloseAfter(double seconds); // NOT thread safe, no simultaneous calling\n\t\tvoid forceClose();\n\n\t\tvoid setTcpNoDelay(bool on);\n\n\t\tvoid setConnectionCallback(const ConnectionCallback& cb)\n\t\t{\n\t\t\tconnectionCallback_ = cb;\n\t\t}\n\n\t\tvoid setMessageCallback(const MessageCallback& cb)\n\t\t{\n\t\t\tmessageCallback_ = cb;\n\t\t}\n\n        //óɹִеĻص\n\t\tvoid setWriteCompleteCallback(const WriteCompleteCallback& cb)\n\t\t{\n\t\t\twriteCompleteCallback_ = cb;\n\t\t}\n\n\t\tvoid setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark)\n\t\t{\n\t\t\thighWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark;\n\t\t}\n\n\t\t/// Advanced interface\n\t\tBuffer* inputBuffer()\n\t\t{\n\t\t\treturn &inputBuffer_;\n\t\t}\n\n\t\tBuffer* outputBuffer()\n\t\t{\n\t\t\treturn &outputBuffer_;\n\t\t}\n\n\t\t/// Internal use only.\n\t\tvoid setCloseCallback(const CloseCallback& cb)\n\t\t{\n\t\t\tcloseCallback_ = cb;\n\t\t}\n\n\t\t// called when TcpServer accepts a new connection\n\t\tvoid connectEstablished();   // should be called only once\n\t\t// called when TcpServer has removed me from its map\n\t\tvoid connectDestroyed();  // should be called only once\n\n\tprivate:\n\t\tenum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };\n\t\tvoid handleRead(Timestamp receiveTime);\n\t\tvoid handleWrite();\n\t\tvoid handleClose();\n\t\tvoid handleError();\n\t\t// void sendInLoop(string&& message);\n\t\tvoid sendInLoop(const string& message);\n\t\tvoid sendInLoop(const void* message, size_t len);\n\t\tvoid shutdownInLoop();\n\t\t// void shutdownAndForceCloseInLoop(double seconds);\n\t\tvoid forceCloseInLoop();\n\t\tvoid setState(StateE s) { state_ = s; }\n\t\tconst char* stateToString() const;\n\n    private:\n\t\tEventLoop*                  loop_;\n\t\tconst string                name_;\n\t\tStateE                      state_;  // FIXME: use atomic variable\n\t\t// we don't expose those classes to client.\n\t\tstd::shared_ptr<Socket>     socket_;\n\t\tstd::shared_ptr<Channel>    channel_;\n\t\tconst InetAddress           localAddr_;\n\t\tconst InetAddress           peerAddr_;\n\t\tConnectionCallback          connectionCallback_;\n\t\tMessageCallback             messageCallback_;\n\t\tWriteCompleteCallback       writeCompleteCallback_;\n\t\tHighWaterMarkCallback       highWaterMarkCallback_;\n\t\tCloseCallback               closeCallback_;\n\t\tsize_t                      highWaterMark_;\n\t\tBuffer                      inputBuffer_;\n\t\tBuffer                      outputBuffer_; // FIXME: use list<Buffer> as output buffer.\n\n\t\t// FIXME: creationTime_, lastReceiveTime_\n\t\t//        bytesReceived_, bytesSent_\n\t};\n\n\ttypedef std::shared_ptr<TcpConnection> TcpConnectionPtr;\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TcpServer.cpp",
    "content": "#include \"TcpServer.h\"\n\n#include <stdio.h>  // snprintf\n#include <functional>\n\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"../base/Singleton.h\"\n#include \"Acceptor.h\"\n#include \"EventLoop.h\"\n#include \"EventLoopThreadPool.h\"\n#include \"Sockets.h\"\n\nusing namespace net;\n\nTcpServer::TcpServer(EventLoop* loop,\n    const InetAddress& listenAddr,\n    const std::string& nameArg,\n    Option option)\n    : loop_(loop),\n    hostport_(listenAddr.toIpPort()),\n    name_(nameArg),\n    acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),\n    //threadPool_(new EventLoopThreadPool(loop, name_)),\n    connectionCallback_(defaultConnectionCallback),\n    messageCallback_(defaultMessageCallback),\n    started_(0),\n    nextConnId_(1)\n{\n    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));\n}\n\nTcpServer::~TcpServer()\n{\n    loop_->assertInLoopThread();\n    LOGD(\"TcpServer::~TcpServer [%s] destructing\", name_.c_str());\n\n    stop();\n}\n\n//void TcpServer::setThreadNum(int numThreads)\n//{\n//  assert(0 <= numThreads);\n//  threadPool_->setThreadNum(numThreads);\n//}\n\nvoid TcpServer::start(int workerThreadCount/* = 4*/)\n{\n    if (started_ == 0)\n    {\n        eventLoopThreadPool_.reset(new EventLoopThreadPool());\n        eventLoopThreadPool_->Init(loop_, workerThreadCount);\n        eventLoopThreadPool_->start();\n        \n        //threadPool_->start(threadInitCallback_);\n        //assert(!acceptor_->listenning());\n        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));\n        started_ = 1;\n    }\n}\n\nvoid TcpServer::stop()\n{\n    if (started_ == 0)\n        return;\n\n    for (ConnectionMap::iterator it = connections_.begin(); it != connections_.end(); ++it)\n    {\n        TcpConnectionPtr conn = it->second;\n        it->second.reset();\n        conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));\n        conn.reset();\n    }\n\n    eventLoopThreadPool_->stop();\n\n    started_ = 0;\n}\n\nvoid TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)\n{\n    loop_->assertInLoopThread();\n    EventLoop* ioLoop = eventLoopThreadPool_->getNextLoop();\n    char buf[32];\n    snprintf(buf, sizeof buf, \":%s#%d\", hostport_.c_str(), nextConnId_);\n    ++nextConnId_;\n    string connName = name_ + buf;\n\n    LOGD(\"TcpServer::newConnection [%s] - new connection [%s] from %s\", name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());\n\n    InetAddress localAddr(sockets::getLocalAddr(sockfd));\n    // FIXME poll with zero timeout to double confirm the new connection\n    // FIXME use make_shared if necessary\n    TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));\n    connections_[connName] = conn;\n    conn->setConnectionCallback(connectionCallback_);\n    conn->setMessageCallback(messageCallback_);\n    conn->setWriteCompleteCallback(writeCompleteCallback_);\n    conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)); // FIXME: unsafe\n    //̷߳io¼TcpConnection::connectEstablished\n    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));\n}\n\nvoid TcpServer::removeConnection(const TcpConnectionPtr& conn)\n{\n    // FIXME: unsafe\n    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));\n}\n\nvoid TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)\n{\n    loop_->assertInLoopThread();\n    LOGD(\"TcpServer::removeConnectionInLoop [%s] - connection %s\", name_.c_str(), conn->name().c_str());\n    size_t n = connections_.erase(conn->name());\n    //(void)n;\n    //assert(n == 1);\n    if (n != 1)\n    {\n        //TcpConneactionڴУԷͶϿˡ\n        LOGD(\"TcpServer::removeConnectionInLoop [%s] - connection %s, connection does not exist.\", name_.c_str(), conn->name().c_str());\n        return;\n    }\n\n    EventLoop* ioLoop = conn->getLoop();\n    ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));\n}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TcpServer.h",
    "content": "#pragma once\n\n#include <atomic>\n//#include <cstdatomic> // gccͷļ\n#include <map>\n#include <memory>\n//#include \"EventLoop.h\"\n\n#include \"TcpConnection.h\"\n\nnamespace net\n{\n\tclass Acceptor;\n\tclass EventLoop;\n\tclass EventLoopThreadPool;\n\n\t///\n\t/// TCP server, supports single-threaded and thread-pool models.\n\t///\n\t/// This is an interface class, so don't expose too much details.\n\tclass TcpServer\n\t{\n\tpublic:\n\t\ttypedef std::function<void(EventLoop*)> ThreadInitCallback;\n\t\tenum Option\n\t\t{\n\t\t\tkNoReusePort,\n\t\t\tkReusePort,\n\t\t};\n\n\t\t//TcpServer(EventLoop* loop, const InetAddress& listenAddr);\n\t\tTcpServer(EventLoop* loop,\n\t\t\t      const InetAddress& listenAddr,\n\t\t\t      const std::string& nameArg,\n\t\t\t      Option option = kReusePort);      //TODO: Ĭ޸ĳkReusePort\n\t\t~TcpServer();  // force out-line dtor, for scoped_ptr members.\n\n\t\tconst std::string& hostport() const { return hostport_; }\n\t\tconst std::string& name() const { return name_; }\n\t\tEventLoop* getLoop() const { return loop_; }\n\n\t\t/// Set the number of threads for handling input.\n\t\t///\n\t\t/// Always accepts new connection in loop's thread.\n\t\t/// Must be called before @c start\n\t\t/// @param numThreads\n\t\t/// - 0 means all I/O in loop's thread, no thread will created.\n\t\t///   this is the default value.\n\t\t/// - 1 means all I/O in another thread.\n\t\t/// - N means a thread pool with N threads, new connections\n\t\t///   are assigned on a round-robin basis.\n\t\t\n\t\t//void setThreadNum(int numThreads);\n\t\tvoid setThreadInitCallback(const ThreadInitCallback& cb)\n\t\t{ threadInitCallback_ = cb; }\n\t\t/// valid after calling start()\n\t\t//std::shared_ptr<EventLoopThreadPool> threadPool()\n\t\t//{ return threadPool_; }\n\n\t\t/// Starts the server if it's not listenning.\n\t\t///\n\t\t/// It's harmless to call it multiple times.\n\t\t/// Thread safe.\n\t\tvoid start(int workerThreadCount = 4);\n\n        void stop();\n\n\t\t/// Set connection callback.\n\t\t/// Not thread safe.\n\t\tvoid setConnectionCallback(const ConnectionCallback& cb)\n\t\t{ connectionCallback_ = cb; }\n\n\t\t/// Set message callback.\n\t\t/// Not thread safe.\n\t\tvoid setMessageCallback(const MessageCallback& cb)\n\t\t{ messageCallback_ = cb; }\n\n\t\t/// Set write complete callback.\n\t\t/// Not thread safe.\n\t\tvoid setWriteCompleteCallback(const WriteCompleteCallback& cb)\n\t\t{ writeCompleteCallback_ = cb; }\n\n\t\tvoid removeConnection(const TcpConnectionPtr& conn);\n\n\tprivate:\n\t\t/// Not thread safe, but in loop\n\t\tvoid newConnection(int sockfd, const InetAddress& peerAddr);\n\t\t/// Thread safe.\n\t\t\n\t\t/// Not thread safe, but in loop\n\t\tvoid removeConnectionInLoop(const TcpConnectionPtr& conn);\n\n\t\ttypedef std::map<string, TcpConnectionPtr> ConnectionMap;\n\n    private:\n\t\tEventLoop*                                      loop_;  // the acceptor loop\n\t\tconst string                                    hostport_;\n\t\tconst string                                    name_;\n\t\tstd::shared_ptr<Acceptor>                       acceptor_; // avoid revealing Acceptor\n        std::shared_ptr<EventLoopThreadPool>            eventLoopThreadPool_;\n\t\tConnectionCallback                              connectionCallback_;\n\t\tMessageCallback                                 messageCallback_;\n\t\tWriteCompleteCallback                           writeCompleteCallback_;\n\t\tThreadInitCallback                              threadInitCallback_;\n\t\tstd::atomic<int>                                started_;\n\t\tint                                             nextConnId_;  // always in loop thread\n\t\tConnectionMap                                   connections_;\n\t};\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Timer.cpp",
    "content": "#include \"Timer.h\"\n\nusing namespace net;\n\nstd::atomic<int64_t> Timer::s_numCreated_;\n\n//void Timer::restart(Timestamp now)\n//{\n//    if (repeat_)\n//    {\n//        expiration_ = addTime(now, interval_);\n//    }\n//    else\n//    {\n//        expiration_ = Timestamp::invalid();\n//    }\n//}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/Timer.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <stdint.h>\n#include \"../base/Timestamp.h\"\n#include \"../net/Callbacks.h\"\n\nnamespace net\n{ \n    ///\n    /// Internal class for timer event.\n    ///\n    class Timer\n    {\n    public:\n        Timer(const TimerCallback& cb, Timestamp when, int64_t interval, int64_t repeatCount = -1)\n            : callback_(cb),\n            expiration_(when),\n            interval_(interval),\n            repeatCount_(repeatCount),       \n            sequence_(++s_numCreated_),\n            canceled_(false)\n        { }\n\n\n        Timer(TimerCallback&& cb, Timestamp when, int64_t interval)\n            : callback_(std::move(cb)),\n            expiration_(when),\n            interval_(interval),\n            repeatCount_(-1),\n            sequence_(++s_numCreated_),\n            canceled_(false)          \n        { }\n\n        void run()\n        {\n            if (canceled_)\n                return;\n\n            callback_();\n\n            if (repeatCount_ != -1)\n            {\n                --repeatCount_;\n                if (repeatCount_ == 0)\n                {\n                    //repeatCount_ = 0;\n                    return;\n                }                               \n            }\n           \n            expiration_ += interval_;               \n        }\n\n        bool isCanceled() const\n        {\n            return canceled_;\n        }\n\n        void cancel(bool off)\n        {\n            canceled_ = off;\n        }\n\n        Timestamp expiration() const { return expiration_; }\n        int64_t getRepeatCount() const { return repeatCount_; }\n        int64_t sequence() const { return sequence_; }\n\n        //void restart(Timestamp now);\n\n        static int64_t numCreated() { return s_numCreated_; }\n\n    private:\n        //noncopyable\n        Timer(const Timer& rhs) = delete;\n        Timer& operator=(const Timer& rhs) = delete;\n\n    private:\n        const TimerCallback         callback_;\n        Timestamp                   expiration_;\n        const int64_t               interval_;\n        int64_t                     repeatCount_;       //ظ-1 ʾһֱظȥ\n        const int64_t               sequence_;\n        bool                        canceled_;          //Ƿȡ״̬\n\n        static std::atomic<int64_t> s_numCreated_;\n    };\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TimerId.h",
    "content": "#pragma once\n\nnamespace net\n{\n    class Timer;\n\n    ///\n    /// An opaque identifier, for canceling Timer.\n    ///\n    class TimerId\n    {\n    public:\n        TimerId(): timer_(NULL), sequence_(0)\n        {\n        }\n\n        TimerId(Timer* timer, int64_t seq) : timer_(timer), sequence_(seq)\n        {\n        }\n\n        Timer* getTimer()\n        {\n            return timer_;\n        }\n\n        // default copy-ctor, dtor and assignment are okay\n\n        friend class TimerQueue;\n\n    private:\n        Timer*      timer_;\n        int64_t     sequence_;\n    };\n\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TimerQueue.cpp",
    "content": "#include \"TimerQueue.h\"\n\n#include <functional>\n\n#include \"../base/Platform.h\"\n#include \"../base/AsyncLog.h\"\n#include \"EventLoop.h\"\n#include \"Timer.h\"\n#include \"TimerId.h\"\n\nnamespace net\n{\n    //namespace detail\n    //{\n\n    //    int createTimerfd()\n    //    {\n    //        int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);\n    //        if (timerfd < 0)\n    //        {\n    //            LOG_SYSFATAL << \"Failed in timerfd_create\";\n    //        }\n    //        return timerfd;\n    //    }\n\n    //    struct timespec howMuchTimeFromNow(Timestamp when)\n    //    {\n    //        int64_t microseconds = when.microSecondsSinceEpoch()\n    //            - Timestamp::now().microSecondsSinceEpoch();\n    //        if (microseconds < 100)\n    //        {\n    //            microseconds = 100;\n    //        }\n    //        struct timespec ts;\n    //        ts.tv_sec = static_cast<time_t>(\n    //            microseconds / Timestamp::kMicroSecondsPerSecond);\n    //        ts.tv_nsec = static_cast<long>(\n    //            (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);\n    //        return ts;\n    //    }\n\n    //    void readTimerfd(int timerfd, Timestamp now)\n    //    {\n    //        uint64_t howmany;\n    //        ssize_t n = ::read(timerfd, &howmany, sizeof howmany);\n    //        LOG_TRACE << \"TimerQueue::handleRead() \" << howmany << \" at \" << now.toString();\n    //        if (n != sizeof howmany)\n    //        {\n    //            LOG_ERROR << \"TimerQueue::handleRead() reads \" << n << \" bytes instead of 8\";\n    //        }\n    //    }\n\n    //    void resetTimerfd(int timerfd, Timestamp expiration)\n    //    {\n    //        // wake up loop by timerfd_settime()\n    //        struct itimerspec newValue;\n    //        struct itimerspec oldValue;\n    //        bzero(&newValue, sizeof newValue);\n    //        bzero(&oldValue, sizeof oldValue);\n    //        newValue.it_value = howMuchTimeFromNow(expiration);\n    //        int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);\n    //        if (ret)\n    //        {\n    //            LOG_SYSERR << \"timerfd_settime()\";\n    //        }\n    //    }\n\n    //}\n}\n\nusing namespace net;\n//using namespace net::detail;\n\nTimerQueue::TimerQueue(EventLoop* loop)\n    : loop_(loop),\n    /*timerfd_(createTimerfd()),\n    timerfdChannel_(loop, timerfd_),*/\n    timers_()\n    //callingExpiredTimers_(false)\n{\n    //timerfdChannel_.setReadCallback(\n    //    std::bind(&TimerQueue::handleRead, this));\n    // we are always reading the timerfd, we disarm it with timerfd_settime.\n    //timerfdҵepollfd\n    //timerfdChannel_.enableReading();\n}\n\nTimerQueue::~TimerQueue()\n{\n    //timerfdChannel_.disableAll();\n    //timerfdChannel_.remove();\n    //::close(timerfd_);\n    // do not remove channel, since we're in EventLoop::dtor();\n    for (TimerList::iterator it = timers_.begin(); it != timers_.end(); ++it)\n    {\n        delete it->second;\n    }\n}\n\nTimerId TimerQueue::addTimer(const TimerCallback& cb, Timestamp when, int64_t interval, int64_t repeatCount)\n{\n    Timer* timer = new Timer(cb, when, interval);\n    loop_->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));\n    return TimerId(timer, timer->sequence());\n}\n\nTimerId TimerQueue::addTimer(TimerCallback&& cb, Timestamp when, int64_t interval, int64_t repeatCount)\n{\n    Timer* timer = new Timer(std::move(cb), when, interval, repeatCount);\n    loop_->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));\n    return TimerId(timer, timer->sequence());\n}\n\nvoid TimerQueue::removeTimer(TimerId timerId)\n{\n    loop_->runInLoop(std::bind(&TimerQueue::removeTimerInLoop, this, timerId));\n}\n\nvoid TimerQueue::cancel(TimerId timerId, bool off)\n{\n    loop_->runInLoop(std::bind(&TimerQueue::cancelTimerInLoop, this, timerId, off));\n}\n\nvoid TimerQueue::doTimer()\n{\n    loop_->assertInLoopThread();\n    \n    Timestamp now(Timestamp::now());\n\n    for (auto iter = timers_.begin(); iter != timers_.end(); )\n    {\n        //if (iter->first <= now)\n        if (iter->second->expiration() <= now)\n        {\n            //LOGD(\"time: %lld\", iter->second->expiration().microSecondsSinceEpoch());\n            iter->second->run();\n            if (iter->second->getRepeatCount() == 0)\n            {\n                iter = timers_.erase(iter);\n            }\n            else\n            {\n                ++iter;\n            }\n        }\n        else\n        {\n            break;\n        }           \n    }\n\n\n    //readTimerfd(timerfd_, now);\n\n    //std::vector<Entry> expired = getExpired(now);\n\n    //callingExpiredTimers_ = true;\n    //cancelingTimers_.clear();\n    //// safe to callback outside critical section\n    //for (std::vector<Entry>::iterator it = expired.begin();\n    //    it != expired.end(); ++it)\n    //{\n    //    it->second->run();\n    //}\n    //callingExpiredTimers_ = false;\n\n    //reset(expired, now);\n}\n\nvoid TimerQueue::addTimerInLoop(Timer* timer)\n{\n    loop_->assertInLoopThread();\n    /*bool earliestChanged = */insert(timer);\n\n    //if (earliestChanged)\n    //{\n    //    resetTimerfd(timerfd_, timer->expiration());\n    //}\n}\n\nvoid TimerQueue::removeTimerInLoop(TimerId timerId)\n{\n    loop_->assertInLoopThread();\n    //assert(timers_.size() == activeTimers_.size());\n    //ActiveTimer timer(timerId.timer_, timerId.sequence_);\n    //ActiveTimerSet::iterator it = activeTimers_.find(timer);\n\n    Timer* timer = timerId.timer_;\n    for (auto iter = timers_.begin(); iter != timers_.end(); ++iter)\n    {\n        if (iter->second == timer)\n        {\n            timers_.erase(iter);\n            break;\n        }\n    }  \n}\n\nvoid TimerQueue::cancelTimerInLoop(TimerId timerId, bool off)\n{\n    loop_->assertInLoopThread();\n\n    Timer* timer = timerId.timer_;\n    for (auto iter = timers_.begin(); iter != timers_.end(); ++iter)\n    {\n        if (iter->second == timer)\n        {\n            iter->second->cancel(off);\n            break;\n        }\n    }\n\n    ////assert(timers_.size() == activeTimers_.size());\n    //ActiveTimer timer(timerId.timer_, timerId.sequence_);\n    //ActiveTimerSet::iterator it = activeTimers_.find(timer);\n    //if (it != activeTimers_.end())\n    //{\n    //    size_t n = timers_.erase(Entry(it->first->expiration(), it->first));\n    //    //assert(n == 1); (void)n;\n    //    delete it->first; // FIXME: no delete please\n    //    activeTimers_.erase(it);\n    //}\n    //else if (callingExpiredTimers_)\n    //{\n    //    cancelingTimers_.insert(timer);\n    //}\n    ////assert(timers_.size() == activeTimers_.size());\n}\n\n//std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)\n//{\n//    assert(timers_.size() == activeTimers_.size());\n//    std::vector<Entry> expired;\n//    Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));\n//    TimerList::iterator end = timers_.lower_bound(sentry);\n//    assert(end == timers_.end() || now < end->first);\n//    std::copy(timers_.begin(), end, back_inserter(expired));\n//    timers_.erase(timers_.begin(), end);\n//\n//    for (std::vector<Entry>::iterator it = expired.begin();\n//        it != expired.end(); ++it)\n//    {\n//        ActiveTimer timer(it->second, it->second->sequence());\n//        size_t n = activeTimers_.erase(timer);\n//        assert(n == 1); (void)n;\n//    }\n//\n//    assert(timers_.size() == activeTimers_.size());\n//    return expired;\n//}\n\n//void TimerQueue::reset(const std::vector<Entry> & expired, Timestamp now)\n//{\n//    Timestamp nextExpire;\n//\n//    for (std::vector<Entry>::const_iterator it = expired.begin();\n//        it != expired.end(); ++it)\n//    {\n//        ActiveTimer timer(it->second, it->second->sequence());\n//        if (it->second->getRepeatCount()\n//            && cancelingTimers_.find(timer) == cancelingTimers_.end())\n//        {\n//            it->second->restart(now);\n//            insert(it->second);\n//        }\n//        else\n//        {\n//            // FIXME move to a free list\n//            delete it->second; // FIXME: no delete please\n//        }\n//    }\n//\n//    if (!timers_.empty())\n//    {\n//        nextExpire = timers_.begin()->second->expiration();\n//    }\n//\n//    if (nextExpire.valid())\n//    {\n//        resetTimerfd(timerfd_, nextExpire);\n//    }\n//}\n\nvoid TimerQueue::insert(Timer* timer)\n{\n    loop_->assertInLoopThread();\n    //assert(timers_.size() == activeTimers_.size());\n    bool earliestChanged = false;\n    Timestamp when = timer->expiration();\n    //TimerList::iterator it = timers_.begin();\n    //if (it == timers_.end() || when < it->first)\n    //{\n    //    earliestChanged = true;\n    //}\n    //{\n        /*std::pair<TimerList::iterator, bool> result = */timers_.insert(Entry(when, timer));\n        //assert(result.second); (void)result;\n    //}\n    //{\n    //    std::pair<ActiveTimerSet::iterator, bool> result = activeTimers_.insert(ActiveTimer(timer, timer->sequence()));\n        //assert(result.second); (void)result;\n    //}\n\n    //assert(timers_.size() == activeTimers_.size());\n    //return earliestChanged;\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/net/TimerQueue.h",
    "content": "#pragma once\n\n#include <set>\n#include <vector>\n\n#include \"../base/Timestamp.h\"\n#include \"../net/Callbacks.h\"\n#include \"../net/Channel.h\"\n\nnamespace net\n{\n\n    class EventLoop;\n    class Timer;\n    class TimerId;\n\n    ///\n    /// A best efforts timer queue.\n    /// No guarantee that the callback will be on time.\n    ///\n    class TimerQueue\n    {\n    public:\n        TimerQueue(EventLoop* loop);\n        ~TimerQueue();\n\n        ///\n        /// Schedules the callback to be run at given time,\n        /// repeats if @c interval > 0.0.\n        ///\n        /// Must be thread safe. Usually be called from other threads.\n        //intervalλ΢\n        TimerId addTimer(const TimerCallback& cb, Timestamp when, int64_t interval, int64_t repeatCount);\n\n        TimerId addTimer(TimerCallback&& cb, Timestamp when, int64_t interval, int64_t repeatCount);\n\n        void removeTimer(TimerId timerId);\n\n        void cancel(TimerId timerId, bool off);\n\n        // called when timerfd alarms\n        void doTimer();\n      \n    private:\n        //noncopyable\n        TimerQueue(const TimerQueue& rhs) = delete;\n        TimerQueue& operator=(const TimerQueue& rhs) = delete;\n\n        // FIXME: use unique_ptr<Timer> instead of raw pointers.\n        typedef std::pair<Timestamp, Timer*>    Entry;\n        typedef std::set<Entry>                 TimerList;\n        typedef std::pair<Timer*, int64_t>      ActiveTimer;\n        typedef std::set<ActiveTimer>           ActiveTimerSet;\n\n        void addTimerInLoop(Timer* timer);\n        void removeTimerInLoop(TimerId timerId);\n        void cancelTimerInLoop(TimerId timerId, bool off);\n        \n        // move out all expired timers\n        //std::vector<Entry> getExpired(Timestamp now);\n        //void reset(const std::vector<Entry>& expired, Timestamp now);\n\n        void insert(Timer* timer);\n\n    private:\n        EventLoop*          loop_;\n        //const int           timerfd_;\n        //Channel             timerfdChannel_;\n        // Timer list sorted by expiration\n        TimerList           timers_;\n\n        //for cancel()\n        //ActiveTimerSet      activeTimers_;\n        //bool                callingExpiredTimers_; /* atomic */\n        //ActiveTimerSet      cancelingTimers_;\n    };\n\n}\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/utils/DaemonRun.cpp",
    "content": "/**\n* ػ̷ʽ\n* zhangyl 2018.08.20\n*/\n#ifndef _WIN32\n#include <unistd.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <iostream>\n#include \"DaemonRun.h\"\n\nvoid daemon_run()\n{\n    int pid;\n    signal(SIGCHLD, SIG_IGN);\n    //1ڸУfork´ӽ̵ĽID\n    //2ӽУfork0\n    //3ִforkһֵ\n    pid = fork();\n    if (pid < 0)\n    {\n        std::cout << \"fork error\" << std::endl;\n        exit(-1);\n    }\n    //˳ӽ̶\n    else if (pid > 0) {\n        exit(0);\n    }\n    //֮ǰparentchildͬһsession,parentǻỰsessionͷ,\n    //parentΪỰͷ̣exitִеĻôӽ̻Ϊ¶̣init\n    //ִsetsid()֮,child»һµĻỰ(session)id\n    //ʱparent˳֮,Ӱ쵽childˡ\n    setsid();\n    int fd;\n    fd = open(\"/dev/null\", O_RDWR, 0);\n    if (fd != -1)\n    {\n        dup2(fd, STDIN_FILENO);\n        dup2(fd, STDOUT_FILENO);\n        dup2(fd, STDERR_FILENO);\n    }\n    if (fd > 2)\n        close(fd);\n}\n#endif"
  },
  {
    "path": "Chapter06/code/WebSocketServer/utils/DaemonRun.h",
    "content": "/** \n * ػ̷ʽ\n * zhangyl 2018.08.20\n */\n#ifndef _WIN32\nvoid daemon_run();\n#endif\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/utils/StringUtil.cpp",
    "content": "/**\n * ַ, StringUtil.cpp\n * zhangyl 2018.03.09\n */\n#include \"StringUtil.h\"\n#include <string.h>\n\nvoid StringUtil::Split(const std::string& str, std::vector<std::string>& v, const char* delimiter/* = \"|\"*/)\n{\n    if (delimiter == NULL || str.empty())\n        return;\n\n    std::string buf(str);\n    size_t pos = std::string::npos;\n    std::string substr;\n    int delimiterlength = strlen(delimiter);\n    while (true)\n    {\n        pos = buf.find(delimiter);\n        if (pos != std::string::npos)\n        {\n            substr = buf.substr(0, pos);\n            if (!substr.empty())\n                v.push_back(substr);\n\n            buf = buf.substr(pos + delimiterlength);\n        }\n        else\n        {\n            if (!buf.empty())\n                v.push_back(buf);\n            break;\n        }           \n    }\n}\n\nvoid StringUtil::Cut(const std::string& str, std::vector<std::string>& v, const char* delimiter/* = \"|\"*/)\n{\n    if (delimiter == NULL || str.empty())\n        return;\n\n    std::string buf(str);\n    int delimiterlength = strlen(delimiter);\n    size_t pos = buf.find(delimiter);\n    if (pos == std::string::npos)\n        return;\n\n    std::string substr1 = buf.substr(0, pos);\n    std::string substr2 = buf.substr(pos + 1);\n    if (!substr1.empty())\n        v.push_back(substr1);\n\n    if (!substr2.empty())\n        v.push_back(substr2);\n}\n\nstd::string& StringUtil::Replace(std::string& str, const std::string& toReplaced, const std::string& newStr)\n{\n    if (toReplaced.empty() || newStr.empty())\n        return str;\n    \n    for (std::string::size_type pos = 0; pos != std::string::npos; pos += newStr.length())\n    {\n        pos = str.find(toReplaced, pos);\n        if (pos != std::string::npos)\n            str.replace(pos, toReplaced.length(), newStr);\n        else\n            break;\n    }\n\n    return str;\n}\n\nvoid StringUtil::trimLeft(std::string& str, char trimmed/* = ' '*/)\n{\n    std::string tmp = str;\n    size_t length = tmp.length();\n    for (size_t i = 0; i < length; ++i)\n    {\n        if (tmp[i] != trimmed)\n        {\n            str = tmp.substr(i);\n            break;\n        }\n    }\n}\n\nvoid StringUtil::trimRight(std::string& str, char trimmed/* = ' '*/)\n{\n    std::string tmp = str;\n    size_t length = tmp.length();\n    for (size_t i = length - 1; i >=0; --i)\n    {\n        if (tmp[i] != trimmed)\n        {\n            str = tmp.substr(0, i + 1);\n            break;\n        }\n    }\n}\n\nvoid StringUtil::trim(std::string& str, char trimmed/* = ' '*/)\n{\n    trimLeft(str, trimmed);\n    trimRight(str, trimmed);\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/utils/StringUtil.h",
    "content": "/**\n * ַ, StringUtil.h\n * zhangyl 2018.03.09\n */\n#ifndef __STRING_UTIL_H__\n#define __STRING_UTIL_H__\n\n#include <string>\n#include <vector>\n\nclass StringUtil\n{\nprivate:\n    StringUtil() = delete;\n    ~StringUtil() = delete;\n    StringUtil(const StringUtil& rhs) = delete;\n    StringUtil& operator=(const StringUtil& rhs) = delete;\n\npublic:\n    static void Split(const std::string& str, std::vector<std::string>& v, const char* delimiter = \"|\");\n    static void Cut(const std::string& str, std::vector<std::string>& v, const char* delimiter = \"|\");\n    static std::string& Replace(std::string& str, const std::string& toReplaced, const std::string& newStr);\n\n    static void trimLeft(std::string& str, char trimmed = ' ');\n    static void trimRight(std::string& str, char trimmed = ' ');\n    static void trim(std::string& str, char trimmed = ' ');\n};\n\n\n#endif //!__STRING_UTIL_H__"
  },
  {
    "path": "Chapter06/code/WebSocketServer/utils/UUIDGenerator.cpp",
    "content": "/**\n * ȫΨһUUIDɹ࣬WindowsʵʹõGUID, UUIDGenerator.cpp\n * zhangyl 20190710\n */\n\n#include \"UUIDGenerator.h\"\n\n\n#ifdef WIN32\n\n#include <combaseapi.h>\n\nstd::string UUIDGenerator::generate()\n{\n    GUID guid;\n    CoCreateGuid(&guid);\n    char cBuffer[64] = { 0 };\n    sprintf_s(cBuffer, sizeof(cBuffer),\n        \"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\",\n        guid.Data1, guid.Data2,\n        guid.Data3, guid.Data4[0],\n        guid.Data4[1], guid.Data4[2],\n        guid.Data4[3], guid.Data4[4],\n        guid.Data4[5], guid.Data4[6],\n        guid.Data4[7]);\n    return std::string(cBuffer, 36);\n}\n\n#else\n\n#include <uuid.h>\n\nstd::string UUIDGenerator::generate()\n{\n    uuid_t uuid;\n    char str[40] = { 0 };\n\n    uuid_generate(uuid);\n    uuid_unparse(uuid, str);\n    return std::string(str, 36);\n}\n\n#endif"
  },
  {
    "path": "Chapter06/code/WebSocketServer/utils/UUIDGenerator.h",
    "content": "/** \n * ȫΨһUUIDɹ࣬WindowsʵʹõGUID, UUIDGenerator.h\n * zhangyl 20190710\n */\n\n#ifndef __UUID_GENERATOR_H__\n#define __UUID_GENERATOR_H__\n\n#include <string>\n\nclass UUIDGenerator final\n{\nprivate:\n    UUIDGenerator() = delete;\n    ~UUIDGenerator() = delete;\n\n    UUIDGenerator(const UUIDGenerator& rhs) = delete;\n    UUIDGenerator& operator =(const UUIDGenerator& rhs) = delete;\n\npublic:\n    static std::string generate();\n\n};\n\n#endif //!__UUID_GENERATOR_H__\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/websocketsrc/MyWebSocketServer.cpp",
    "content": "/**\n * MyWebSocketServer࣬MyMyWebSocketServer.cpp\n * zhangyl 2019.08.28\n */\n\n#include \"MyWebSocketServer.h\"\n\n#include <string>\n\n#include \"../base/AsyncLog.h\"\n#include \"../base/Singleton.h\"\n#include \"../net/InetAddress.h\"\n#include \"../net/TcpServer.h\"\n#include \"../net/EventLoop.h\"\n#include \"../net/EventLoopThread.h\"\n#include \"../net/EventLoopThreadPool.h\"\n#include \"../base/Platform.h\"\n#include \"../appsrc/BusinessSession.h\"\n\n#include \"MyWebSocketSession.h\"\n\nMyWebSocketServer::MyWebSocketServer()\n{\n\n}\n\nbool MyWebSocketServer::init(const char* ip, short port, EventLoop* loop)\n{\n    InetAddress addr(ip, port);\n    m_server.reset(new TcpServer(loop, addr, \"MY-WEBSOCKET-SERVER\", TcpServer::kReusePort));\n    m_server->setConnectionCallback(std::bind(&MyWebSocketServer::onConnection, this, std::placeholders::_1));\n    //\n    unsigned int threadCount = std::thread::hardware_concurrency() + 2;\n    m_server->start(threadCount);\n\n    return true;\n}\n\nvoid MyWebSocketServer::uninit()\n{\n    if (m_server)\n        m_server->stop();\n}\n\n//ӵûӶϿҪͨconn->connected()жϣһֻloop\nvoid MyWebSocketServer::onConnection(std::shared_ptr<TcpConnection> conn)\n{\n    if (conn->connected())\n    {\n        std::shared_ptr<BusinessSession> spSession(new BusinessSession(conn));\n        conn->setMessageCallback(std::bind(&MyWebSocketSession::onRead, spSession.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));\n\n        {           \n            std::lock_guard<std::mutex> guard(m_mutexForSession);\n            m_listSessions.push_back(spSession);\n        }    \n    }\n    else\n    {\n        onClose(conn);\n    }\n}\n\n//ӶϿ\nvoid MyWebSocketServer::onClose(const std::shared_ptr<TcpConnection>& conn)\n{\n    //TODO: Ĵ߼̫ңҪŻ\n    std::unique_lock<std::mutex> guard(m_mutexForSession);\n    for (auto iter = m_listSessions.begin(); iter != m_listSessions.end(); ++iter)\n    {\n        //ͨȶconnectionҵӦsession\n        if ((*iter)->getConnectionPtr() == conn)\n        {         \n            (*iter)->onDisconnect();\n            LOGI(\"client: [%s] disconnected, session: 0x%0x\", (*iter)->getClientInfo(), (int64_t)((*iter).get()));\n\n            m_listSessions.erase(iter);\n            return;\n        }\n    }\n\n\n    LOGE(\"Unable to find session, conn = 0x%llx\\n\", (int64_t)conn->getLoop());\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/websocketsrc/MyWebSocketServer.h",
    "content": "/**\n * WebSocketServer࣬MyWebSocketServer.h\n * zhangyl 2019.08.28\n */\n#ifndef __MY_WEBSOCKET_SERVER_H__\n#define __MY_WEBSOCKET_SERVER_H__\n\n#include \"MyWebSocketServer.h\"\n\n#include <memory>\n#include <mutex>\n#include <thread>\n#include <list>\n\n#include \"../net/TcpServer.h\"\n\nusing namespace net;\n\nclass BusinessSession;\n\nclass MyWebSocketServer final\n{\npublic:\n    MyWebSocketServer();\n    ~MyWebSocketServer() = default;\n    MyWebSocketServer(const MyWebSocketServer& rhs) = delete;\n    MyWebSocketServer& operator =(const MyWebSocketServer& rhs) = delete;\n\npublic:\n    bool init(const char* ip, short port, EventLoop* loop);\n    void uninit();\n\n    //ӵûӶϿҪͨconn->connected()жϣһֻloop\n    void onConnection(std::shared_ptr<TcpConnection> conn);\n    //ӶϿ\n    void onClose(const std::shared_ptr<TcpConnection>& conn);\n\nprivate:\n    std::shared_ptr<TcpServer>                             m_server; \n\n    std::list<std::shared_ptr<BusinessSession>>            m_listSessions;\n    std::mutex                                             m_mutexForSession;      //֮߳䱣m_sessions\n\n    //ŵǰipַ\n    std::string                                            m_strWsHost;\n    //ŵǰö˿ں\n    int                                                    m_wsPort;\n};\n\n#endif //!__MY_WEBSOCKET_SERVER_H__"
  },
  {
    "path": "Chapter06/code/WebSocketServer/websocketsrc/MyWebSocketSession.cpp",
    "content": "/**\n * WebSocketỰ, MyWebSocketSession.cpp\n * zhangyl 2017.03.09\n */\n#include \"MyWebSocketSession.h\"\n#include <sstream>\n#include <list>\n\n#ifndef WIN32\n#include <strings.h>\n#endif\n\n#include \"../net/EventLoopThread.h\"\n#include \"../base/Singleton.h\"\n#include \"../base/AsyncLog.h\"\n#include \"../base/Platform.h\"\n#include \"../utils/StringUtil.h\"\n#include \"../utils/UUIDGenerator.h\"\n#include \"../zlib1.2.11/ZlibUtil.h\"\n\n#include \"WebSocketHandshake.h\"\n\n//clienthttp50M\n#define MAX_WEBSOCKET_CLIENT_PACKAGE_LENGTH        50 * 1024 * 1024\n//СwebsocketͷС\n#define MIN_WEBSOCKET_PACKAGE_HEADER_LENGTH        6\n\n//˷ְĴС10M\n#define MAX_WEBSOCKET_SERVER_PACKAGE_LENGTH 10 * 1024 * 1024\n\nMyWebSocketSession::MyWebSocketSession(std::shared_ptr<TcpConnection>& conn) : m_bUpdateToWebSocket(false), m_tmpConn(conn), m_bClientCompressed(false)\n{\n    m_strClientInfo = conn->peerAddress().toIpPort();\n}\n\nvoid MyWebSocketSession::onRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receivTime)\n{\n    while (true)\n    {\n        /**\n        GET /realtime HTTP/1.1\n        Host: 127.0.0.1:9989\n        Connection: Upgrade\n        Pragma: no-cache\n        Cache-Control: no-cache\n        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36\n        Upgrade: websocket\n        Origin: http://coolaf.com\n        Sec-WebSocket-Version: 13\n        Accept-Encoding: gzip, deflate, br\n        Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\n        Sec-WebSocket-Key: IqcAWodjyPDJuhGgZwkpKg==\n        Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\n       */\n        size_t readableBytesCount = pBuffer->readableBytes();\n        if (readableBytesCount >= MAX_WEBSOCKET_CLIENT_PACKAGE_LENGTH)\n        {\n            this->close();\n            return;\n        }\n\n        //WEBSOCKET RFC ĵhttps://www.rfc-editor.org/rfc/rfc6455.txt\n\n\n        /*\n          0                   1                   2                   3\n          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n         +-+-+-+-+-------+-+-------------+-------------------------------+\n         |F|R|R|R| opcode|M| Payload len |    Extended payload length    |\n         |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |\n         |N|V|V|V|       |S|             |   (if payload len==126/127)   |\n         | |1|2|3|       |K|             |                               |\n         +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +\n         |     Extended payload length continued, if payload len == 127  |\n         + - - - - - - - - - - - - - - - +-------------------------------+\n         |                               |Masking-key, if MASK set to 1  |\n         +-------------------------------+-------------------------------+\n         | Masking-key (continued)       |          Payload Data         |\n         +-------------------------------- - - - - - - - - - - - - - - - +\n         :                     Payload Data continued ...                :\n         + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +\n         |                     Payload Data continued ...                |\n         +---------------------------------------------------------------+\n\n         */\n\n         //δ֣Эݴ\n        if (!m_bUpdateToWebSocket)\n        {\n            const char* pos = pBuffer->findString(\"\\r\\n\\r\\n\");\n            bool foundEngTag = (pos != nullptr);\n\n            if (!foundEngTag)\n            {\n                //û\n                if (pBuffer->readableBytes() < MAX_WEBSOCKET_CLIENT_PACKAGE_LENGTH)\n                    return;\n                //Ƿݰ\n                else\n                {\n                    conn->forceClose();\n                    return;\n                }\n            }\n\n            //4\\r\\n\\r\\nĳ\n            size_t length = pos - (pBuffer->peek()) + 4;\n            std::string currentData(pBuffer->peek(), length);\n            pBuffer->retrieve(length);\n\n            if (!handleHandshake(currentData, conn))\n            {\n                conn->forceClose();\n                return;\n            }\n\n            LOGI(\"websocket message: %s\", currentData.c_str());\n        }\n        //\n        else\n        {\n            if (readableBytesCount < MIN_WEBSOCKET_PACKAGE_HEADER_LENGTH)\n                return;\n            \n            if (!decodePackage(pBuffer, conn))\n                conn->forceClose();\n                \n            //ܰǷΪݲ㶼Ӧ˳ѭ\n            return;\n        }\n    }\n    \n    //ݽ    \n}\n\nbool MyWebSocketSession::decodePackage(Buffer* pBuffer, const std::shared_ptr<TcpConnection>& conn)\n{\n    size_t readableBytesCount = pBuffer->readableBytes();\n    \n    const int32_t TWO_FLAG_BYTES = 2;\n\n    //ͷ\n    const int32_t MAX_HEADER_LENGTH = 14;\n    char pBytes[MAX_HEADER_LENGTH] = {0};\n    //Ѿյݴʱǰͷ󲿷\n    if (readableBytesCount > MAX_HEADER_LENGTH)\n        memcpy(pBytes, pBuffer->peek(), MAX_HEADER_LENGTH * sizeof(char));\n    else\n        memcpy(pBytes, pBuffer->peek(), readableBytesCount * sizeof(char));\n\n    bool FIN = (pBytes[0] & 0x80);\n    //TODO: ͲУˣΪδ֪Ŀͻ֮Լ\n    //bool RSV1, RSV2, RSV3;\n    //ȡһֽڵĵ4λȡ\n    int32_t opcode = (int32_t)(pBytes[0] & 0xF);\n\n    //if (!FIN && opcode != MyOpCode::CONTINUATION_FRAME)\n    //{\n    //    LOGE(\"FIN did not match opcode, client: %s\", conn->peerAddress().toIpPort().c_str());\n    //    return false;\n    //}\n\n    //if (opcode == MyOpCode::CLOSE)\n    //{\n    //    LOGE(\"receive CLOSE opcode, client: %s\", conn->peerAddress().toIpPort().c_str());\n    //    return false;\n    //}\n\n    //ȡڶֽڵλ˵ͻ˷ֶαΪ1\n    bool mask = ((pBytes[1] & 0x80));\n    //if (!mask)\n    //{\n    //    LOGE(\"invalid mask flag, client: %s\", conn->peerAddress().toIpPort().c_str());\n    //    return false;\n    //}\n\n    int32_t headerSize = 0;\n    int64_t bodyLength = 0;\n    //mask־ĸֽڵmasking-key\n    if (mask)\n        headerSize += 4;\n\n    //ȡڶֽڵĵλ\n    int32_t payloadLength = (int32_t)(pBytes[1] & 0x7F);\n    if (payloadLength <= 0 && payloadLength > 127)\n    {\n        LOGE(\"invalid payload length, payloadLength: %d, client: %s\", payloadLength, conn->peerAddress().toIpPort().c_str());\n        return false;\n    }\n\n    if (payloadLength > 0 && payloadLength <= 125)\n    {\n        headerSize += TWO_FLAG_BYTES;\n        bodyLength = payloadLength;\n    }               \n    else if (payloadLength == 126)\n    {\n        headerSize += TWO_FLAG_BYTES;\n        headerSize += sizeof(short);\n\n        if ((int32_t)readableBytesCount < headerSize)\n            return true;\n\n        short tmp;\n        memcpy(&tmp, &pBytes[2], 2);\n        int32_t extendedPayloadLength = ::ntohs(tmp);\n        bodyLength = extendedPayloadLength;\n        //峤ȲҪ\n        if (bodyLength < 126 || bodyLength > UINT16_MAX)\n        {\n            LOGE(\"illegal extendedPayloadLength, extendedPayloadLength: %d, client: %s\", bodyLength, conn->peerAddress().toIpPort().c_str());\n            return false;\n        }        \n    }\n    else if (payloadLength == 127)\n    {\n        headerSize += TWO_FLAG_BYTES;\n        headerSize += sizeof(uint64_t);\n        \n        //Ȳ\n        if ((int32_t)readableBytesCount < headerSize)\n            return true;\n\n        int64_t tmp;\n        memcpy(&tmp, &pBytes[2], 8);\n        int64_t extendedPayloadLength = ::ntohll(tmp);\n        bodyLength = extendedPayloadLength;\n        //峤ȲҪ\n        if (bodyLength <= UINT16_MAX)\n        {\n            LOGE(\"illegal extendedPayloadLength, extendedPayloadLength: %lld, client: %s\", bodyLength, conn->peerAddress().toIpPort().c_str());\n            return false;\n        }        \n    }\n\n    if ((int32_t)readableBytesCount < headerSize + bodyLength)\n        return true;\n\n    //ȡͷ\n    pBuffer->retrieve(headerSize);\n    std::string payloadData(pBuffer->peek(), bodyLength);\n    //ȡ\n    pBuffer->retrieve(bodyLength);\n\n    if (mask)\n    {\n        char maskingKey[4] = { 0 };\n        //headerSize - 4masking-keyλ\n        memcpy(maskingKey, pBytes + headerSize - 4, 4);\n        unmaskData(payloadData, maskingKey);\n    }\n    \n    if (FIN)\n    {\n        //һƬ֮ǰĺϲеĻ\n        m_strParsedData.append(payloadData);\n        //\n        if (!processPackage(m_strParsedData, (MyOpCode)opcode, conn))\n            return false;\n\n        m_strParsedData.clear();\n    }\n    else\n    {\n        //һƬȻ\n        m_strParsedData.append(payloadData);\n    } \n\n    return true;\n}\n\nstd::string MyWebSocketSession::getHeader(const char* headerField) const\n{\n    auto iter = m_mapHttpHeaders.find(headerField);\n    if (iter == m_mapHttpHeaders.end())\n        return \"\";\n\n    return iter->second;\n}\n\nstd::string MyWebSocketSession::getHeaderIgnoreCase(const char* headerField) const\n{\n    for (const auto& iter : m_mapHttpHeaders)\n    {\n#ifdef WIN32\n        if (stricmp(iter.first.c_str(), headerField) == 0)\n#else\n        if (strcasecmp(iter.first.c_str(), headerField) == 0)      \n#endif\n        {\n            return iter.second;\n        }\n    }\n\n    return \"\";\n}\n\nvoid MyWebSocketSession::close()\n{\n    if (m_tmpConn.expired())\n        return;\n\n    std::shared_ptr<TcpConnection> conn = m_tmpConn.lock();\n    conn->forceClose();\n}\n\nbool MyWebSocketSession::handleHandshake(const std::string& data, const std::shared_ptr<TcpConnection>& conn)\n{\n    std::vector<std::string> vecHttpHeaders;\n    StringUtil::Split(data, vecHttpHeaders, \"\\r\\n\");\n    //3\n    if (vecHttpHeaders.size() < 3)\n        return false;\n\n    std::vector<std::string> v;\n    size_t vecLength = vecHttpHeaders.size();\n    for (size_t i = 0; i < vecLength; ++i)\n    {\n        //һлòƺЭ汾\n        if (i == 0)\n        {\n            if (!parseHttpPath(vecHttpHeaders[i]))\n                return false;\n        }\n        else\n        {\n            //ͷ־\n            v.clear();\n            StringUtil::Cut(vecHttpHeaders[i], v, \":\");\n            if (v.size() < 2)\n                return false;\n\n            StringUtil::trim(v[1]);\n            m_mapHttpHeaders[v[0]] = v[1];\n        }\n    }\n\n    /* TODOԿͻ˵Ҫ \n        ֱһЧ HTTP \n        ķΪ GET, HTTP 汾 1.1\n         REQUEST-URI ĵ涨Ҫ(鿴 Page 13)\n         Host ͷ\n         Upgrade: websocket ͷ,ֵΪ websocket\n         Connection: Upgrade ͷ,ֵΪ Upgrade\n         Sec-WebSocket-Key ͷ\n         Sec-WebSocket-Version: 13 ͷ,ֵΪ 13\n         Origin ͷ\n        ܰ Sec-WebSocket-Protocol ͷ,涨Э\n        ܰ Sec-WebSocket-Extensions ,涨Эչ\n        ֶܰ, cookie \n     */\n\n    auto target = m_mapHttpHeaders.find(\"Connection\");\n    if (target == m_mapHttpHeaders.end() || target->second != \"Upgrade\")\n        return false;\n\n    target = m_mapHttpHeaders.find(\"Upgrade\");\n    if (target == m_mapHttpHeaders.end() || target->second != \"websocket\")\n        return false;\n\n    target = m_mapHttpHeaders.find(\"Host\");\n    if (target == m_mapHttpHeaders.end() || target->second.empty())\n        return false;\n\n    target = m_mapHttpHeaders.find(\"Origin\");\n    if (target == m_mapHttpHeaders.end() || target->second.empty())\n        return false;\n\n    //TODO: ĳɲִСд\n    target = m_mapHttpHeaders.find(\"User-Agent\");\n    if (target != m_mapHttpHeaders.end())\n    {\n        m_strUserAgent = target->second;\n    }\n\n    target = m_mapHttpHeaders.find(\"Sec-WebSocket-Extensions\");\n    if (target != m_mapHttpHeaders.end())\n    {\n        std::vector<std::string> vecExtensions;\n        StringUtil::Split(target->second, vecExtensions, \";\");\n\n        for (const auto& iter : vecExtensions)\n        {\n            if (iter == \"permessage-deflate\")\n            {\n                m_bClientCompressed = true;\n                break;\n            }\n        }\n    }\n\n    target = m_mapHttpHeaders.find(\"Sec-WebSocket-Key\");\n    if (target == m_mapHttpHeaders.end() || target->second.empty())\n        return false;\n\n    char secWebSocketAccept[29] = {};\n    js::WebSocketHandshake::generate(target->second.c_str(), secWebSocketAccept);\n    std::string response;\n    makeUpgradeResponse(secWebSocketAccept, response);\n    conn->send(response);\n    \n    m_bUpdateToWebSocket = true;\n\n    //ʵʵд\n    onConnect();\n\n    return true;\n}\n\nbool MyWebSocketSession::parseHttpPath(const std::string& str)\n{\n    std::vector<std::string> vecTags;\n    StringUtil::Split(str, vecTags, \" \");\n    if (vecTags.size() != 3)\n        return false;\n\n    //TODO: ӦòִСдıȽ\n    if (vecTags[0] != \"GET\")\n        return false;\n\n    std::vector<std::string> vecPathAndParams;\n    StringUtil::Split(vecTags[1], vecPathAndParams, \"?\");\n    //һ·\n    if (vecPathAndParams.empty())\n        return false;\n\n    m_strURL = vecPathAndParams[0];\n    if (vecPathAndParams.size() >= 2)\n        m_strParams = vecPathAndParams[1];\n\n    //WebSocketЭ汾ű1.1\n    if (vecTags[2] != \"HTTP/1.1\")\n        return false;\n\n    return true;\n}\n\nvoid MyWebSocketSession::makeUpgradeResponse(const char* secWebSocketAccept, std::string& response)\n{\n    response = \"HTTP/1.1 101 Switching Protocols\\r\\n\"\n               \"Upgrade: websocket\\r\\n\"\n               \"Sec-Websocket-Accept: \";  \n    response += secWebSocketAccept;\n    response += \"\\r\\nServer: BTCMEXWebsocketServer 1.0.0\\r\\n\";\n    response += \"Connection: Upgrade\\r\\n\"\n                \"Sec-WebSocket-Version: 13\\r\\n\";\n    if (m_bClientCompressed)\n        response += \"Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover\\r\\n\";\n\n    response += \"Date: \";\n\n    char szNow[64];\n    time_t now = time(NULL);\n    tm time;\n#ifdef _WIN32\n    localtime_s(&time, &now);\n#else\n    localtime_r(&now, &time);\n#endif\n    strftime(szNow, sizeof(szNow), \"%Y%m%d%H%M%S\", &time);\n    response += szNow;\n    response += \"\\r\\n\\r\\n\";   \n}\n\nvoid MyWebSocketSession::unmaskData(std::string& src, const char* maskingKey)\n{\n    /*\n     *  Masking-keyɿͻѡ 32 λӰغɵĳȡ롢㷨\n\n        ȣ裺\n\n        original-octet-iΪԭʼݵĵ i ֽڡ\n        transformed-octet-iΪתݵĵ i ֽڡ\n        jΪi mod 4Ľ\n        masking-key-octet-jΪ mask key  j ֽڡ\n        㷨Ϊ original-octet-i  masking-key-octet-j 󣬵õ transformed-octet-i\n\n        j  = i MOD 4\n        transformed-octet-i = original-octet-i XOR masking-key-octet-j\n     */\n    char j;\n    for (size_t n = 0; n < src.length(); ++n)\n    {\n        j = n % 4;\n        src[n] = src[n] ^ maskingKey[j];\n    }\n}\n\nbool MyWebSocketSession::processPackage(const std::string& data, MyOpCode opcode, const std::shared_ptr<TcpConnection>& conn)\n{\n    if (opcode == MyOpCode::CLOSE)\n    {\n        LOGE(\"received CLOSE opcode, close session, client: %s\", conn->peerAddress().toIpPort().c_str());\n        return false;\n    }\n    else if (opcode == MyOpCode::PING)\n    {\n        onPing();\n    }\n    else if (opcode == MyOpCode::PONG)\n    {\n        onPong();\n    }\n    else if (opcode == MyOpCode::TEXT_FRAME || opcode == MyOpCode::BINARY_FRAME)\n    {\n        std::string out;\n        if (m_bClientCompressed)\n        {\n            if (!ZlibUtil::inflate(data, out))\n            {\n                LOGE(\"uncompress failed, dataLength: %d, client: %s\", data.length(), conn->peerAddress().toIpPort().c_str());\n                return false;\n            }\n        }\n        else\n            out = data;\n\n        LOGI(\"receid data: %s, client: %s\", out.c_str(), conn->peerAddress().toIpPort().c_str());\n\n        onMessage(out);\n    }\n    \n    return true;\n}\n\nvoid MyWebSocketSession::send(const std::string& data, int32_t opcode/* = MyOpCode::TEXT_FRAME*/, bool compress/* = false*/)\n{\n    /*\n          0                   1                   2                   3\n          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n         +-+-+-+-+-------+-+-------------+-------------------------------+\n         |F|R|R|R| opcode|M| Payload len |    Extended payload length    |\n         |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |\n         |N|V|V|V|       |S|             |   (if payload len==126/127)   |\n         | |1|2|3|       |K|             |                               |\n         +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +\n         |     Extended payload length continued, if payload len == 127  |\n         + - - - - - - - - - - - - - - - +-------------------------------+\n         |                               |Masking-key, if MASK set to 1  |\n         +-------------------------------+-------------------------------+\n         | Masking-key (continued)       |          Payload Data         |\n         +-------------------------------- - - - - - - - - - - - - - - - +\n         :                     Payload Data continued ...                :\n         + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +\n         |                     Payload Data continued ...                |\n         +---------------------------------------------------------------+\n     */\n\n    size_t dataLength = data.length();\n    std::string destbuf;\n    if (m_bClientCompressed && dataLength > 0)\n    {\n        if (!ZlibUtil::deflate(data, destbuf))\n        {\n            LOGE(\"compress buf error, data: %s\", data.c_str());\n            return;\n        }\n    }\n    else\n        destbuf = data;\n\n    LOGI(\"destbuf.length(): %d\", destbuf.length());\n    \n    dataLength = destbuf.length();\n\n    char firstTwoBytes[2] = { 0 };\n    //FIN\n    firstTwoBytes[0] |= 0x80;\n\n    //if (!(flags & SND_CONTINUATION)) {\n    //opcode\n    firstTwoBytes[0] |= opcode;\n    //}\n\n    //TODOΪɶǣ\n    const char compressFlag = 0x40;\n    if (m_bClientCompressed)\n        firstTwoBytes[0] |= compressFlag;\n    \n    //mask = 0;\n    //ʵʷ͵ݰ\n    std::string actualSendData;\n\n    if (dataLength < 126)\n    {\n        firstTwoBytes[1] = dataLength;\n        actualSendData.append(firstTwoBytes, 2);\n    }\n    else if (dataLength <= UINT16_MAX)  //2ֽ޷ֵ65535\n    {\n        firstTwoBytes[1] = 126;\n        char extendedPlayloadLength[2] = { 0 };\n        uint16_t tmp = ::htons(dataLength);\n        memcpy(&extendedPlayloadLength, &tmp, 2);\n        actualSendData.append(firstTwoBytes, 2);\n        actualSendData.append(extendedPlayloadLength, 2);\n    }\n    else\n    {\n        firstTwoBytes[1] = 127;\n        char extendedPlayloadLength[8] = { 0 };\n        uint64_t tmp = ::htonll((uint64_t)dataLength);\n        memcpy(&extendedPlayloadLength, &tmp, 8);\n        actualSendData.append(firstTwoBytes, 2);\n        actualSendData.append(extendedPlayloadLength, 8);\n    }   \n    \n    actualSendData.append(destbuf);\n\n    sendPackage(actualSendData.c_str(), actualSendData.length());\n\n    //LOG_DEBUG_BIN((unsigned char*)actualSendData.c_str(), actualSendData.length());\n}\n\nvoid MyWebSocketSession::send(const char* data, size_t dataLength, int32_t opcode/* = MyOpCode::TEXT_FRAME*/, bool compress/* = false*/)\n{\n    std::string str(data, dataLength);\n    send(str, opcode, compress);\n}\n\nvoid MyWebSocketSession::sendAndClose(const char* data, size_t dataLength, bool compress/* = false*/)\n{\n    send(data, dataLength, MyOpCode::CLOSE, compress);\n}\n\nvoid MyWebSocketSession::onPing()\n{\n    send(\"\", 0, MyOpCode::PONG, m_bClientCompressed);\n}\n\nvoid MyWebSocketSession::sendPackage(const char* data, size_t length)\n{\n    if (m_tmpConn.expired())\n        return;\n\n    std::shared_ptr<TcpConnection> conn = m_tmpConn.lock();\n    conn->send(data, length);\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/websocketsrc/MyWebSocketSession.h",
    "content": "/**\n * WebSocketỰ, MyWebSocketSession.h\n * zhangyl 2019.08.28\n */\n#ifndef __MY_WEBSOCKET_SESSION_H__\n#define __MY_WEBSOCKET_SESSION_H__\n\n#include \"../base/Timestamp.h\"\n\n#include \"../net/Buffer.h\"\n#include \"../net/TcpConnection.h\"\n\n#include <string>\n#include <map>\n#include <memory>\n\nusing namespace net;\n\n// 4 bits\nenum MyOpCode : unsigned char\n{\n    CONTINUATION_FRAME  = 0x0,\n    TEXT_FRAME          = 0x1,\n    BINARY_FRAME        = 0x2,\n    RESERVED1           = 0x3,\n    RESERVED2           = 0x4,\n    RESERVED3           = 0x5,\n    RESERVED4           = 0x6,\n    RESERVED5           = 0x7,\n    CLOSE               = 0x8,\n    PING                = 0x9,\n    PONG                = 0xA,\n    RESERVED6           = 0xB,\n    RESERVED7           = 0xC,\n    RESERVED8           = 0xD,\n    RESERVED9           = 0xE,\n    RESERVED10          = 0xF\n};\n\nclass MyWebSocketSession\n{\npublic:\n    MyWebSocketSession(std::shared_ptr<TcpConnection>& conn);\n    virtual ~MyWebSocketSession() = default;\n\n    MyWebSocketSession(const MyWebSocketSession& rhs) = delete;\n    MyWebSocketSession& operator =(const MyWebSocketSession& rhs) = delete;\n\npublic:\n    const char* getClientInfo() const\n    {\n        return m_strClientInfo.c_str();\n    }\n\n    const char* getUserAgent() const\n    {\n        return m_strUserAgent.c_str();\n    }\n\n    std::shared_ptr<TcpConnection> getConnectionPtr()\n    {\n        if (m_tmpConn.expired())\n            return NULL;\n\n        return m_tmpConn.lock();\n    }\n\n    std::string getHeader(const char* headerField) const;\n    std::string getHeaderIgnoreCase(const char* headerField) const;\n\n    std::string getUrl() const\n    {\n        return m_strURL;\n    }\n\n    std::string getParams() const\n    {\n        return m_strParams;\n    }\n\n    void close();\n       \n    void send(const std::string& data, int32_t opcode = MyOpCode::TEXT_FRAME, bool compress = false);\n    void send(const char* data, size_t dataLength, int32_t opcode = MyOpCode::TEXT_FRAME, bool compress = false);\n    void sendAndClose(const char* data, size_t dataLength, bool compress = false);\n\n    //ఴд\n    virtual void onConnect()\n    {\n\n    }\n\n    //ఴд\n    virtual void onDisconnect()\n    {\n\n    }\n\n    //дúҵ\n    virtual bool onMessage(const std::string& strClientMsg)\n    {\n        return false;\n    }\n\n    //дúҵ\n    virtual void onPing();\n\n    //дúҵ\n    virtual void onPong()\n    {\n\n    }\n\n    //ݿɶ, ᱻloop\n    void onRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receivTime);\n    \nprivate:\n    bool handleHandshake(const std::string& data, const std::shared_ptr<TcpConnection>& conn);\n    bool decodePackage(Buffer* pBuffer, const std::shared_ptr<TcpConnection>& conn);\n    bool parseHttpPath(const std::string& str);\n    void makeUpgradeResponse(const char* secWebSocketAccept, std::string& response);\n    void unmaskData(std::string& src, const char* maskingKey);\n    bool processPackage(const std::string& data, MyOpCode opcode, const std::shared_ptr<TcpConnection>& conn);\n\n    void sendPackage(const char* data, size_t length);\n\nprotected:\n    std::string                  m_strClientInfo;\n    std::string                  m_strUserAgent;\n\nprivate:\n    //ǷѾΪhttpЭ\n    bool                               m_bUpdateToWebSocket;\n    std::weak_ptr<TcpConnection>       m_tmpConn;\n    //key httpֶvalue httpֵֶ\n    std::map<std::string, std::string> m_mapHttpHeaders;\n    //·\n    std::string                        m_strURL;\n    //ַĲ\n    std::string                        m_strParams;\n\n    //\n    std::string                        m_strParsedData;\n\n    //ͻǷҪѹ\n    bool                               m_bClientCompressed;\n};\n\n\n#endif //!__MY_WEBSOCKET_SESSION_H__"
  },
  {
    "path": "Chapter06/code/WebSocketServer/websocketsrc/WebSocketHandshake.h",
    "content": "/*\n * Authored by Alex Hultman, 2018-2019.\n * Intellectual property of third-party.\n\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n\n *     http://www.apache.org/licenses/LICENSE-2.0\n\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n*/\n\n#ifndef UWS_WEBSOCKETHANDSHAKE_H\n#define UWS_WEBSOCKETHANDSHAKE_H\n\n#include <cstdint>\n#include <cstddef>\n\nnamespace js {\n\nstruct WebSocketHandshake {\n    template <int N, typename T>\n    struct static_for {\n        void operator()(uint32_t *a, uint32_t *b) {\n            static_for<N - 1, T>()(a, b);\n            T::template f<N - 1>(a, b);\n        }\n    };\n\n    template <typename T>\n    struct static_for<0, T> {\n        void operator()(uint32_t *a, uint32_t *hash) {}\n    };\n\n    template <int state>\n    struct Sha1Loop {\n        static inline uint32_t rol(uint32_t value, size_t bits) {return (value << bits) | (value >> (32 - bits));}\n        static inline uint32_t blk(uint32_t b[16], size_t i) {\n            return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1);\n        }\n\n        template <int i>\n        static inline void f(uint32_t *a, uint32_t *b) {\n            switch (state) {\n            case 1:\n                a[i % 5] += ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5);\n                a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);\n                break;\n            case 2:\n                b[i] = blk(b, i);\n                a[(1 + i) % 5] += ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5);\n                a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30);\n                break;\n            case 3:\n                b[(i + 4) % 16] = blk(b, (i + 4) % 16);\n                a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5);\n                a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);\n                break;\n            case 4:\n                b[(i + 8) % 16] = blk(b, (i + 8) % 16);\n                a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | (a[(3 + i) % 5] & a[(2 + i) % 5])) + b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5);\n                a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);\n                break;\n            case 5:\n                b[(i + 12) % 16] = blk(b, (i + 12) % 16);\n                a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5);\n                a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);\n                break;\n            case 6:\n                b[i] += a[4 - i];\n            }\n        }\n    };\n\n    static inline void sha1(uint32_t hash[5], uint32_t b[16]) {\n        uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]};\n        static_for<16, Sha1Loop<1>>()(a, b);\n        static_for<4, Sha1Loop<2>>()(a, b);\n        static_for<20, Sha1Loop<3>>()(a, b);\n        static_for<20, Sha1Loop<4>>()(a, b);\n        static_for<20, Sha1Loop<5>>()(a, b);\n        static_for<5, Sha1Loop<6>>()(a, hash);\n    }\n\n    static inline void base64(unsigned char *src, char *dst) {\n        const char *b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n        for (int i = 0; i < 18; i += 3) {\n            *dst++ = b64[(src[i] >> 2) & 63];\n            *dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];\n            *dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];\n            *dst++ = b64[src[i + 2] & 63];\n        }\n        *dst++ = b64[(src[18] >> 2) & 63];\n        *dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];\n        *dst++ = b64[((src[19] & 15) << 2)];\n        *dst++ = '=';\n    }\n\npublic:\n    static inline void generate(const char input[24], char output[28]) {\n        uint32_t b_output[5] = {\n            0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0\n        };\n        uint32_t b_input[16] = {\n            0, 0, 0, 0, 0, 0, 0x32353845, 0x41464135, 0x2d453931, 0x342d3437, 0x44412d39,\n            0x3543412d, 0x43354142, 0x30444338, 0x35423131, 0x80000000\n        };\n\n        for (int i = 0; i < 6; i++) {\n            b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 | (input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24;\n        }\n        sha1(b_output, b_input);\n        uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480};\n        sha1(b_output, last_b);\n        for (int i = 0; i < 5; i++) {\n            uint32_t tmp = b_output[i];\n            char *bytes = (char *) &b_output[i];\n            bytes[3] = tmp & 0xff;\n            bytes[2] = (tmp >> 8) & 0xff;\n            bytes[1] = (tmp >> 16) & 0xff;\n            bytes[0] = (tmp >> 24) & 0xff;\n        }\n        base64((unsigned char *) b_output, output);\n    }\n};\n\n}\n\n#endif // UWS_WEBSOCKETHANDSHAKE_H\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/ZlibUtil.cpp",
    "content": "/*\n *  ѹ࣬ZlibUtil.cpp\n *  zhangyl 2018.03.09\n */\n#include \"../zlib1.2.11/zlib.h\"\n#include <string.h>\n#include \"ZlibUtil.h\"\n\n//֧ѹ10M\n#define MAX_COMPRESS_BUF_SIZE 10*1024*1024\n\nbool ZlibUtil::CompressBuf(const char* pSrcBuf, size_t nSrcBufLength, char* pDestBuf, size_t& nDestBufLength)\n{\n    \n    if (pSrcBuf == NULL || nSrcBufLength == 0 || nSrcBufLength > MAX_COMPRESS_BUF_SIZE || pDestBuf == NULL)\n        return false;\n\n    //㻺СΪڴ\n    //ѹĳǲᳬnDestBufLength\n    nDestBufLength = compressBound(nSrcBufLength);\n\n    //ѹ\n    int ret = compress((Bytef*)pDestBuf, (uLongf*)&nDestBufLength, (const Bytef*)pSrcBuf, nSrcBufLength);\n    if (ret != Z_OK)\n        return false;\n\n    return true;\n}\n\nbool ZlibUtil::CompressBuf(const std::string& strSrcBuf, std::string& strDestBuf)\n{\n    if (strSrcBuf.empty())\n        return false;\n\n    int nSrcLength = strSrcBuf.length();\n    if (nSrcLength > MAX_COMPRESS_BUF_SIZE)\n        return false;\n\n    int nDestBufLength = compressBound(nSrcLength);\n    if (nDestBufLength <= 0)\n        return false;\n\n    char* pDestBuf = new char[nDestBufLength];\n    memset(pDestBuf, 0, nDestBufLength * sizeof(char));\n\n    //ѹ\n    int ret = compress((Bytef*)pDestBuf, (uLongf*)&nDestBufLength, (const Bytef*)strSrcBuf.c_str(), nSrcLength);\n    if (ret != Z_OK)\n    {\n        delete[] pDestBuf;\n        return false;\n    }\n\n    strDestBuf.append(pDestBuf, nDestBufLength);\n    delete[] pDestBuf;\n\n    return true;\n}\n\nbool ZlibUtil::UncompressBuf(const std::string& strSrcBuf, std::string& strDestBuf, size_t nDestBufLength)\n{\n    char* pDestBuf = new char[nDestBufLength];\n    memset(pDestBuf, 0, nDestBufLength * sizeof(char));\n    int nPrevDestBufLength = nDestBufLength;\n    //ѹ\n    int ret = uncompress((Bytef*)pDestBuf, (uLongf*)&nDestBufLength, (const Bytef*)strSrcBuf.c_str(), strSrcBuf.length());\n    if (ret != Z_OK)\n    {\n        delete[] pDestBuf;\n        return false;\n    }\n\n    //if (nPrevDestBufLength == nDestBufLength)\n    //{\n    //    int k = 0;\n    //    k++;\n    //}\n    strDestBuf.append(pDestBuf, nDestBufLength);\n    delete[] pDestBuf;\n\n    return true;\n}\n\nbool ZlibUtil::deflate(const std::string& strSrc, std::string& strDest)\n{\n    int err = Z_DATA_ERROR;\n    // Create stream\n    z_stream zS = { 0 };\n    // Set output data streams, do this here to avoid overwriting on recursive calls\n    const int OUTPUT_BUF_SIZE = 8192;\n    Bytef bytesOut[OUTPUT_BUF_SIZE] = { 0 };\n\n    // Initialise the z_stream\n    err = ::deflateInit2(&zS, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);\n    if (err != Z_OK)\n    {\n        //TRACE_UNZIP(\"; Error: new stream failed: %d\\n\", err);\n        return false;\n    }\n    // Use whatever input is provided\n    zS.next_in = (Bytef*)(strSrc.c_str());\n    zS.avail_in = strSrc.length();\n\n    do {\n        try\n        {\n            // Initialise stream values\n            //zS->zalloc = (alloc_func)0;\n            //zS->zfree = (free_func)0;\n            //zS->opaque = (voidpf)0;\n\n            zS.next_out = bytesOut;\n            zS.avail_out = OUTPUT_BUF_SIZE;\n\n            // Try to unzip the data\n            //TRACE_UNZIP(\"; inflate(ain=%6u tin=%6u aout=%6u tout=%6u)\", zS->avail_in, zS->total_in, zS->avail_out, zS->total_out);\n            err = ::deflate(&zS, Z_SYNC_FLUSH);\n\n            // Is zip finished reading all currently available input and writing all generated output\n            if (err == Z_STREAM_END)\n            {\n                // Finish up\n                int kerr = ::deflateEnd(&zS);\n                //ķؽ\n                //if (err != Z_OK)\n                //{\n                //    //TRACE_UNZIP(\"; Error: end stream failed: %d\\n\", err);\n                //    return false;\n                //}\n                //TRACE_UNZIP(\"; Z_STREAM_END\\n\");\n\n                // Got a good result, set the size to the amount unzipped in this call (including all recursive calls)\n\n                strDest.append((const char*)bytesOut, OUTPUT_BUF_SIZE - zS.avail_out);\n                return true;\n            }\n            else if ((err == Z_OK) && (zS.avail_out == 0) && (zS.avail_in != 0))\n            {\n                // Output array was not big enough, call recursively until there is enough space\n                //TRACE_UNZIP(\"; output array not big enough (ain=%u)\\n\", zS->avail_in);\n\n                strDest.append((const char*)bytesOut, OUTPUT_BUF_SIZE - zS.avail_out);\n\n                continue;\n            }\n            else if ((err == Z_OK) && (zS.avail_in == 0))\n            {\n                //TRACE_UNZIP(\"; all input processed\\n\");\n                // All available input has been processed, everything ok.\n                // Set the size to the amount unzipped in this call (including all recursive calls)              \n                strDest.append((const char*)bytesOut, OUTPUT_BUF_SIZE - zS.avail_out);\n\n                int kerr = ::deflateEnd(&zS);\n                //Ľ\n                //if (err != Z_OK)\n                //{\n                //    //TRACE_UNZIP(\"; Error: end stream failed: %d\\n\", err);\n                //    return false;\n                //}\n\n                break;\n            }\n            else\n            {\n                return false;\n            }\n        }\n        catch (...)\n        {\n            return false;\n        }\n    } while (true);\n\n\n    if (err == Z_OK)\n    {\n        //ȥ4Ϊȥdeflatĩβ00 00 ff ff\n        strDest = strDest.substr(0, strDest.length() - 4);\n        return true;\n    }\n\n    return false;\n}\n\nbool ZlibUtil::inflate(const std::string& strSrc, std::string& strDest)\n{\n    int err = Z_DATA_ERROR;\n    // Create stream\n    z_stream zS = { 0 };\n    // Set output data streams, do this here to avoid overwriting on recursive calls\n    const int OUTPUT_BUF_SIZE = 8192;\n    Bytef bytesOut[OUTPUT_BUF_SIZE] = { 0 };\n\n    // Initialise the z_stream\n    err = ::inflateInit2(&zS, -15);\n    if (err != Z_OK)\n    {\n        //TRACE_UNZIP(\"; Error: new stream failed: %d\\n\", err);\n        return false;\n    }\n\n    // Use whatever input is provided\n    zS.next_in = (Bytef*)(strSrc.c_str());\n    zS.avail_in = strSrc.length();\n\n    do {\n        try\n        {\n            // Initialise stream values\n            //zS->zalloc = (alloc_func)0;\n            //zS->zfree = (free_func)0;\n            //zS->opaque = (voidpf)0;\n        \n            zS.next_out = bytesOut;\n            zS.avail_out = OUTPUT_BUF_SIZE;\n                  \n            // Try to unzip the data\n            //TRACE_UNZIP(\"; inflate(ain=%6u tin=%6u aout=%6u tout=%6u)\", zS->avail_in, zS->total_in, zS->avail_out, zS->total_out);\n            err = ::inflate(&zS, Z_SYNC_FLUSH);\n\n            // Is zip finished reading all currently available input and writing all generated output\n            if (err == Z_STREAM_END)\n            {\n                // Finish up\n                int kerr = ::inflateEnd(&zS);\n                //ķؽ\n                //if (err != Z_OK)\n                //{\n                //    //TRACE_UNZIP(\"; Error: end stream failed: %d\\n\", err);\n                //    return false;\n                //}\n                //TRACE_UNZIP(\"; Z_STREAM_END\\n\");\n\n                // Got a good result, set the size to the amount unzipped in this call (including all recursive calls)\n\n                strDest.append((const char*)bytesOut, OUTPUT_BUF_SIZE - zS.avail_out);\n                return true;\n            }\n            else if ((err == Z_OK) && (zS.avail_out == 0) && (zS.avail_in != 0))\n            {\n                // Output array was not big enough, call recursively until there is enough space\n                //TRACE_UNZIP(\"; output array not big enough (ain=%u)\\n\", zS->avail_in);\n\n                strDest.append((const char*)bytesOut, OUTPUT_BUF_SIZE - zS.avail_out);\n\n                continue;\n            }\n            else if ((err == Z_OK) && (zS.avail_in == 0))\n            {\n                //TRACE_UNZIP(\"; all input processed\\n\");\n                // All available input has been processed, everything ok.\n                // Set the size to the amount unzipped in this call (including all recursive calls)\n                strDest.append((const char*)bytesOut, OUTPUT_BUF_SIZE - zS.avail_out);\n\n                int kerr = ::inflateEnd(&zS);\n                //Ľ\n                //if (err != Z_OK)\n                //{\n                //    //TRACE_UNZIP(\"; Error: end stream failed: %d\\n\", err);\n                //    return false;\n                //}\n\n                break;\n            }\n            else\n            {\n                return false;\n            }\n        }\n        catch (...)\n        {\n            return false;\n        }\n    } while (true);\n    \n\n    return err == Z_OK;\n}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/ZlibUtil.h",
    "content": "/*\n *  ѹ࣬ZlibUtil.h\n *  zhangyl 2018.03.09\n */\n#ifndef __ZLIB_UTIL_H__\n#define __ZLIB_UTIL_H__\n#include <string>\n\nclass ZlibUtil\n{\nprivate:\n    ZlibUtil() = delete;\n    ~ZlibUtil() = delete;\n    ZlibUtil(const ZlibUtil& rhs) = delete;\n\npublic:\n    static bool CompressBuf(const char* pSrcBuf, size_t nSrcBufLength, char* pDestBuf, size_t& nDestBufLength);\n    static bool CompressBuf(const std::string& strSrcBuf, std::string& strDestBuf);\n    static bool UncompressBuf(const std::string& strSrcBuf, std::string& strDestBuf, size_t nDestBufLength);\n  \n    //gzipѹ\n    static bool inflate(const std::string& strSrc, std::string& dest);\n    //gzipѹ\n    static bool deflate(const std::string& strSrc, std::string& strDest);\n    \n};\n\n\n\n#endif //!__ZLIB_UTIL_H__"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/adler32.c",
    "content": "/* adler32.c -- compute the Adler-32 checksum of a data stream\n * Copyright (C) 1995-2011, 2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* @(#) $Id$ */\n\n#include \"zutil.h\"\n\nlocal uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));\n\n#define BASE 65521U     /* largest prime smaller than 65536 */\n#define NMAX 5552\n/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */\n\n#define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;}\n#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);\n#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);\n#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);\n#define DO16(buf)   DO8(buf,0); DO8(buf,8);\n\n/* use NO_DIVIDE if your processor does not do division in hardware --\n   try it both ways to see which is faster */\n#ifdef NO_DIVIDE\n/* note that this assumes BASE is 65521, where 65536 % 65521 == 15\n   (thank you to John Reiser for pointing this out) */\n#  define CHOP(a) \\\n    do { \\\n        unsigned long tmp = a >> 16; \\\n        a &= 0xffffUL; \\\n        a += (tmp << 4) - tmp; \\\n    } while (0)\n#  define MOD28(a) \\\n    do { \\\n        CHOP(a); \\\n        if (a >= BASE) a -= BASE; \\\n    } while (0)\n#  define MOD(a) \\\n    do { \\\n        CHOP(a); \\\n        MOD28(a); \\\n    } while (0)\n#  define MOD63(a) \\\n    do { /* this assumes a is not negative */ \\\n        z_off64_t tmp = a >> 32; \\\n        a &= 0xffffffffL; \\\n        a += (tmp << 8) - (tmp << 5) + tmp; \\\n        tmp = a >> 16; \\\n        a &= 0xffffL; \\\n        a += (tmp << 4) - tmp; \\\n        tmp = a >> 16; \\\n        a &= 0xffffL; \\\n        a += (tmp << 4) - tmp; \\\n        if (a >= BASE) a -= BASE; \\\n    } while (0)\n#else\n#  define MOD(a) a %= BASE\n#  define MOD28(a) a %= BASE\n#  define MOD63(a) a %= BASE\n#endif\n\n/* ========================================================================= */\nuLong ZEXPORT adler32_z(adler, buf, len)\n    uLong adler;\n    const Bytef *buf;\n    z_size_t len;\n{\n    unsigned long sum2;\n    unsigned n;\n\n    /* split Adler-32 into component sums */\n    sum2 = (adler >> 16) & 0xffff;\n    adler &= 0xffff;\n\n    /* in case user likes doing a byte at a time, keep it fast */\n    if (len == 1) {\n        adler += buf[0];\n        if (adler >= BASE)\n            adler -= BASE;\n        sum2 += adler;\n        if (sum2 >= BASE)\n            sum2 -= BASE;\n        return adler | (sum2 << 16);\n    }\n\n    /* initial Adler-32 value (deferred check for len == 1 speed) */\n    if (buf == Z_NULL)\n        return 1L;\n\n    /* in case short lengths are provided, keep it somewhat fast */\n    if (len < 16) {\n        while (len--) {\n            adler += *buf++;\n            sum2 += adler;\n        }\n        if (adler >= BASE)\n            adler -= BASE;\n        MOD28(sum2);            /* only added so many BASE's */\n        return adler | (sum2 << 16);\n    }\n\n    /* do length NMAX blocks -- requires just one modulo operation */\n    while (len >= NMAX) {\n        len -= NMAX;\n        n = NMAX / 16;          /* NMAX is divisible by 16 */\n        do {\n            DO16(buf);          /* 16 sums unrolled */\n            buf += 16;\n        } while (--n);\n        MOD(adler);\n        MOD(sum2);\n    }\n\n    /* do remaining bytes (less than NMAX, still just one modulo) */\n    if (len) {                  /* avoid modulos if none remaining */\n        while (len >= 16) {\n            len -= 16;\n            DO16(buf);\n            buf += 16;\n        }\n        while (len--) {\n            adler += *buf++;\n            sum2 += adler;\n        }\n        MOD(adler);\n        MOD(sum2);\n    }\n\n    /* return recombined sums */\n    return adler | (sum2 << 16);\n}\n\n/* ========================================================================= */\nuLong ZEXPORT adler32(adler, buf, len)\n    uLong adler;\n    const Bytef *buf;\n    uInt len;\n{\n    return adler32_z(adler, buf, len);\n}\n\n/* ========================================================================= */\nlocal uLong adler32_combine_(adler1, adler2, len2)\n    uLong adler1;\n    uLong adler2;\n    z_off64_t len2;\n{\n    unsigned long sum1;\n    unsigned long sum2;\n    unsigned rem;\n\n    /* for negative len, return invalid adler32 as a clue for debugging */\n    if (len2 < 0)\n        return 0xffffffffUL;\n\n    /* the derivation of this formula is left as an exercise for the reader */\n    MOD63(len2);                /* assumes len2 >= 0 */\n    rem = (unsigned)len2;\n    sum1 = adler1 & 0xffff;\n    sum2 = rem * sum1;\n    MOD(sum2);\n    sum1 += (adler2 & 0xffff) + BASE - 1;\n    sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;\n    if (sum1 >= BASE) sum1 -= BASE;\n    if (sum1 >= BASE) sum1 -= BASE;\n    if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1);\n    if (sum2 >= BASE) sum2 -= BASE;\n    return sum1 | (sum2 << 16);\n}\n\n/* ========================================================================= */\nuLong ZEXPORT adler32_combine(adler1, adler2, len2)\n    uLong adler1;\n    uLong adler2;\n    z_off_t len2;\n{\n    return adler32_combine_(adler1, adler2, len2);\n}\n\nuLong ZEXPORT adler32_combine64(adler1, adler2, len2)\n    uLong adler1;\n    uLong adler2;\n    z_off64_t len2;\n{\n    return adler32_combine_(adler1, adler2, len2);\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/compress.c",
    "content": "/* compress.c -- compress a memory buffer\n * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* @(#) $Id$ */\n\n#define ZLIB_INTERNAL\n#include \"zlib.h\"\n\n/* ===========================================================================\n     Compresses the source buffer into the destination buffer. The level\n   parameter has the same meaning as in deflateInit.  sourceLen is the byte\n   length of the source buffer. Upon entry, destLen is the total size of the\n   destination buffer, which must be at least 0.1% larger than sourceLen plus\n   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.\n\n     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_BUF_ERROR if there was not enough room in the output buffer,\n   Z_STREAM_ERROR if the level parameter is invalid.\n*/\nint ZEXPORT compress2 (dest, destLen, source, sourceLen, level)\n    Bytef *dest;\n    uLongf *destLen;\n    const Bytef *source;\n    uLong sourceLen;\n    int level;\n{\n    z_stream stream;\n    int err;\n    const uInt max = (uInt)-1;\n    uLong left;\n\n    left = *destLen;\n    *destLen = 0;\n\n    stream.zalloc = (alloc_func)0;\n    stream.zfree = (free_func)0;\n    stream.opaque = (voidpf)0;\n\n    err = deflateInit(&stream, level);\n    if (err != Z_OK) return err;\n\n    stream.next_out = dest;\n    stream.avail_out = 0;\n    stream.next_in = (z_const Bytef *)source;\n    stream.avail_in = 0;\n\n    do {\n        if (stream.avail_out == 0) {\n            stream.avail_out = left > (uLong)max ? max : (uInt)left;\n            left -= stream.avail_out;\n        }\n        if (stream.avail_in == 0) {\n            stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen;\n            sourceLen -= stream.avail_in;\n        }\n        err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH);\n    } while (err == Z_OK);\n\n    *destLen = stream.total_out;\n    deflateEnd(&stream);\n    return err == Z_STREAM_END ? Z_OK : err;\n}\n\n/* ===========================================================================\n */\nint ZEXPORT compress (dest, destLen, source, sourceLen)\n    Bytef *dest;\n    uLongf *destLen;\n    const Bytef *source;\n    uLong sourceLen;\n{\n    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);\n}\n\n/* ===========================================================================\n     If the default memLevel or windowBits for deflateInit() is changed, then\n   this function needs to be updated.\n */\nuLong ZEXPORT compressBound (sourceLen)\n    uLong sourceLen;\n{\n    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +\n           (sourceLen >> 25) + 13;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/crc32.c",
    "content": "/* crc32.c -- compute the CRC-32 of a data stream\n * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n *\n * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster\n * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing\n * tables for updating the shift register in one step with three exclusive-ors\n * instead of four steps with four exclusive-ors.  This results in about a\n * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.\n */\n\n/* @(#) $Id$ */\n\n/*\n  Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore\n  protection on the static variables used to control the first-use generation\n  of the crc tables.  Therefore, if you #define DYNAMIC_CRC_TABLE, you should\n  first call get_crc_table() to initialize the tables before allowing more than\n  one thread to use crc32().\n\n  DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.\n */\n\n#ifdef MAKECRCH\n#  include <stdio.h>\n#  ifndef DYNAMIC_CRC_TABLE\n#    define DYNAMIC_CRC_TABLE\n#  endif /* !DYNAMIC_CRC_TABLE */\n#endif /* MAKECRCH */\n\n#include \"zutil.h\"      /* for STDC and FAR definitions */\n\n/* Definitions for doing the crc four data bytes at a time. */\n#if !defined(NOBYFOUR) && defined(Z_U4)\n#  define BYFOUR\n#endif\n#ifdef BYFOUR\n   local unsigned long crc32_little OF((unsigned long,\n                        const unsigned char FAR *, z_size_t));\n   local unsigned long crc32_big OF((unsigned long,\n                        const unsigned char FAR *, z_size_t));\n#  define TBLS 8\n#else\n#  define TBLS 1\n#endif /* BYFOUR */\n\n/* Local functions for crc concatenation */\nlocal unsigned long gf2_matrix_times OF((unsigned long *mat,\n                                         unsigned long vec));\nlocal void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));\nlocal uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));\n\n\n#ifdef DYNAMIC_CRC_TABLE\n\nlocal volatile int crc_table_empty = 1;\nlocal z_crc_t FAR crc_table[TBLS][256];\nlocal void make_crc_table OF((void));\n#ifdef MAKECRCH\n   local void write_table OF((FILE *, const z_crc_t FAR *));\n#endif /* MAKECRCH */\n/*\n  Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:\n  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.\n\n  Polynomials over GF(2) are represented in binary, one bit per coefficient,\n  with the lowest powers in the most significant bit.  Then adding polynomials\n  is just exclusive-or, and multiplying a polynomial by x is a right shift by\n  one.  If we call the above polynomial p, and represent a byte as the\n  polynomial q, also with the lowest power in the most significant bit (so the\n  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,\n  where a mod b means the remainder after dividing a by b.\n\n  This calculation is done using the shift-register method of multiplying and\n  taking the remainder.  The register is initialized to zero, and for each\n  incoming bit, x^32 is added mod p to the register if the bit is a one (where\n  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by\n  x (which is shifting right by one and adding x^32 mod p if the bit shifted\n  out is a one).  We start with the highest power (least significant bit) of\n  q and repeat for all eight bits of q.\n\n  The first table is simply the CRC of all possible eight bit values.  This is\n  all the information needed to generate CRCs on data a byte at a time for all\n  combinations of CRC register values and incoming bytes.  The remaining tables\n  allow for word-at-a-time CRC calculation for both big-endian and little-\n  endian machines, where a word is four bytes.\n*/\nlocal void make_crc_table()\n{\n    z_crc_t c;\n    int n, k;\n    z_crc_t poly;                       /* polynomial exclusive-or pattern */\n    /* terms of polynomial defining this crc (except x^32): */\n    static volatile int first = 1;      /* flag to limit concurrent making */\n    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};\n\n    /* See if another task is already doing this (not thread-safe, but better\n       than nothing -- significantly reduces duration of vulnerability in\n       case the advice about DYNAMIC_CRC_TABLE is ignored) */\n    if (first) {\n        first = 0;\n\n        /* make exclusive-or pattern from polynomial (0xedb88320UL) */\n        poly = 0;\n        for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)\n            poly |= (z_crc_t)1 << (31 - p[n]);\n\n        /* generate a crc for every 8-bit value */\n        for (n = 0; n < 256; n++) {\n            c = (z_crc_t)n;\n            for (k = 0; k < 8; k++)\n                c = c & 1 ? poly ^ (c >> 1) : c >> 1;\n            crc_table[0][n] = c;\n        }\n\n#ifdef BYFOUR\n        /* generate crc for each value followed by one, two, and three zeros,\n           and then the byte reversal of those as well as the first table */\n        for (n = 0; n < 256; n++) {\n            c = crc_table[0][n];\n            crc_table[4][n] = ZSWAP32(c);\n            for (k = 1; k < 4; k++) {\n                c = crc_table[0][c & 0xff] ^ (c >> 8);\n                crc_table[k][n] = c;\n                crc_table[k + 4][n] = ZSWAP32(c);\n            }\n        }\n#endif /* BYFOUR */\n\n        crc_table_empty = 0;\n    }\n    else {      /* not first */\n        /* wait for the other guy to finish (not efficient, but rare) */\n        while (crc_table_empty)\n            ;\n    }\n\n#ifdef MAKECRCH\n    /* write out CRC tables to crc32.h */\n    {\n        FILE *out;\n\n        out = fopen(\"crc32.h\", \"w\");\n        if (out == NULL) return;\n        fprintf(out, \"/* crc32.h -- tables for rapid CRC calculation\\n\");\n        fprintf(out, \" * Generated automatically by crc32.c\\n */\\n\\n\");\n        fprintf(out, \"local const z_crc_t FAR \");\n        fprintf(out, \"crc_table[TBLS][256] =\\n{\\n  {\\n\");\n        write_table(out, crc_table[0]);\n#  ifdef BYFOUR\n        fprintf(out, \"#ifdef BYFOUR\\n\");\n        for (k = 1; k < 8; k++) {\n            fprintf(out, \"  },\\n  {\\n\");\n            write_table(out, crc_table[k]);\n        }\n        fprintf(out, \"#endif\\n\");\n#  endif /* BYFOUR */\n        fprintf(out, \"  }\\n};\\n\");\n        fclose(out);\n    }\n#endif /* MAKECRCH */\n}\n\n#ifdef MAKECRCH\nlocal void write_table(out, table)\n    FILE *out;\n    const z_crc_t FAR *table;\n{\n    int n;\n\n    for (n = 0; n < 256; n++)\n        fprintf(out, \"%s0x%08lxUL%s\", n % 5 ? \"\" : \"    \",\n                (unsigned long)(table[n]),\n                n == 255 ? \"\\n\" : (n % 5 == 4 ? \",\\n\" : \", \"));\n}\n#endif /* MAKECRCH */\n\n#else /* !DYNAMIC_CRC_TABLE */\n/* ========================================================================\n * Tables of CRC-32s of all single-byte values, made by make_crc_table().\n */\n#include \"crc32.h\"\n#endif /* DYNAMIC_CRC_TABLE */\n\n/* =========================================================================\n * This function can be used by asm versions of crc32()\n */\nconst z_crc_t FAR * ZEXPORT get_crc_table()\n{\n#ifdef DYNAMIC_CRC_TABLE\n    if (crc_table_empty)\n        make_crc_table();\n#endif /* DYNAMIC_CRC_TABLE */\n    return (const z_crc_t FAR *)crc_table;\n}\n\n/* ========================================================================= */\n#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)\n#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1\n\n/* ========================================================================= */\nunsigned long ZEXPORT crc32_z(crc, buf, len)\n    unsigned long crc;\n    const unsigned char FAR *buf;\n    z_size_t len;\n{\n    if (buf == Z_NULL) return 0UL;\n\n#ifdef DYNAMIC_CRC_TABLE\n    if (crc_table_empty)\n        make_crc_table();\n#endif /* DYNAMIC_CRC_TABLE */\n\n#ifdef BYFOUR\n    if (sizeof(void *) == sizeof(ptrdiff_t)) {\n        z_crc_t endian;\n\n        endian = 1;\n        if (*((unsigned char *)(&endian)))\n            return crc32_little(crc, buf, len);\n        else\n            return crc32_big(crc, buf, len);\n    }\n#endif /* BYFOUR */\n    crc = crc ^ 0xffffffffUL;\n    while (len >= 8) {\n        DO8;\n        len -= 8;\n    }\n    if (len) do {\n        DO1;\n    } while (--len);\n    return crc ^ 0xffffffffUL;\n}\n\n/* ========================================================================= */\nunsigned long ZEXPORT crc32(crc, buf, len)\n    unsigned long crc;\n    const unsigned char FAR *buf;\n    uInt len;\n{\n    return crc32_z(crc, buf, len);\n}\n\n#ifdef BYFOUR\n\n/*\n   This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit\n   integer pointer type. This violates the strict aliasing rule, where a\n   compiler can assume, for optimization purposes, that two pointers to\n   fundamentally different types won't ever point to the same memory. This can\n   manifest as a problem only if one of the pointers is written to. This code\n   only reads from those pointers. So long as this code remains isolated in\n   this compilation unit, there won't be a problem. For this reason, this code\n   should not be copied and pasted into a compilation unit in which other code\n   writes to the buffer that is passed to these routines.\n */\n\n/* ========================================================================= */\n#define DOLIT4 c ^= *buf4++; \\\n        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \\\n            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]\n#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4\n\n/* ========================================================================= */\nlocal unsigned long crc32_little(crc, buf, len)\n    unsigned long crc;\n    const unsigned char FAR *buf;\n    z_size_t len;\n{\n    register z_crc_t c;\n    register const z_crc_t FAR *buf4;\n\n    c = (z_crc_t)crc;\n    c = ~c;\n    while (len && ((ptrdiff_t)buf & 3)) {\n        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);\n        len--;\n    }\n\n    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;\n    while (len >= 32) {\n        DOLIT32;\n        len -= 32;\n    }\n    while (len >= 4) {\n        DOLIT4;\n        len -= 4;\n    }\n    buf = (const unsigned char FAR *)buf4;\n\n    if (len) do {\n        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);\n    } while (--len);\n    c = ~c;\n    return (unsigned long)c;\n}\n\n/* ========================================================================= */\n#define DOBIG4 c ^= *buf4++; \\\n        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \\\n            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]\n#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4\n\n/* ========================================================================= */\nlocal unsigned long crc32_big(crc, buf, len)\n    unsigned long crc;\n    const unsigned char FAR *buf;\n    z_size_t len;\n{\n    register z_crc_t c;\n    register const z_crc_t FAR *buf4;\n\n    c = ZSWAP32((z_crc_t)crc);\n    c = ~c;\n    while (len && ((ptrdiff_t)buf & 3)) {\n        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);\n        len--;\n    }\n\n    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;\n    while (len >= 32) {\n        DOBIG32;\n        len -= 32;\n    }\n    while (len >= 4) {\n        DOBIG4;\n        len -= 4;\n    }\n    buf = (const unsigned char FAR *)buf4;\n\n    if (len) do {\n        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);\n    } while (--len);\n    c = ~c;\n    return (unsigned long)(ZSWAP32(c));\n}\n\n#endif /* BYFOUR */\n\n#define GF2_DIM 32      /* dimension of GF(2) vectors (length of CRC) */\n\n/* ========================================================================= */\nlocal unsigned long gf2_matrix_times(mat, vec)\n    unsigned long *mat;\n    unsigned long vec;\n{\n    unsigned long sum;\n\n    sum = 0;\n    while (vec) {\n        if (vec & 1)\n            sum ^= *mat;\n        vec >>= 1;\n        mat++;\n    }\n    return sum;\n}\n\n/* ========================================================================= */\nlocal void gf2_matrix_square(square, mat)\n    unsigned long *square;\n    unsigned long *mat;\n{\n    int n;\n\n    for (n = 0; n < GF2_DIM; n++)\n        square[n] = gf2_matrix_times(mat, mat[n]);\n}\n\n/* ========================================================================= */\nlocal uLong crc32_combine_(crc1, crc2, len2)\n    uLong crc1;\n    uLong crc2;\n    z_off64_t len2;\n{\n    int n;\n    unsigned long row;\n    unsigned long even[GF2_DIM];    /* even-power-of-two zeros operator */\n    unsigned long odd[GF2_DIM];     /* odd-power-of-two zeros operator */\n\n    /* degenerate case (also disallow negative lengths) */\n    if (len2 <= 0)\n        return crc1;\n\n    /* put operator for one zero bit in odd */\n    odd[0] = 0xedb88320UL;          /* CRC-32 polynomial */\n    row = 1;\n    for (n = 1; n < GF2_DIM; n++) {\n        odd[n] = row;\n        row <<= 1;\n    }\n\n    /* put operator for two zero bits in even */\n    gf2_matrix_square(even, odd);\n\n    /* put operator for four zero bits in odd */\n    gf2_matrix_square(odd, even);\n\n    /* apply len2 zeros to crc1 (first square will put the operator for one\n       zero byte, eight zero bits, in even) */\n    do {\n        /* apply zeros operator for this bit of len2 */\n        gf2_matrix_square(even, odd);\n        if (len2 & 1)\n            crc1 = gf2_matrix_times(even, crc1);\n        len2 >>= 1;\n\n        /* if no more bits set, then done */\n        if (len2 == 0)\n            break;\n\n        /* another iteration of the loop with odd and even swapped */\n        gf2_matrix_square(odd, even);\n        if (len2 & 1)\n            crc1 = gf2_matrix_times(odd, crc1);\n        len2 >>= 1;\n\n        /* if no more bits set, then done */\n    } while (len2 != 0);\n\n    /* return combined crc */\n    crc1 ^= crc2;\n    return crc1;\n}\n\n/* ========================================================================= */\nuLong ZEXPORT crc32_combine(crc1, crc2, len2)\n    uLong crc1;\n    uLong crc2;\n    z_off_t len2;\n{\n    return crc32_combine_(crc1, crc2, len2);\n}\n\nuLong ZEXPORT crc32_combine64(crc1, crc2, len2)\n    uLong crc1;\n    uLong crc2;\n    z_off64_t len2;\n{\n    return crc32_combine_(crc1, crc2, len2);\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/crc32.h",
    "content": "/* crc32.h -- tables for rapid CRC calculation\n * Generated automatically by crc32.c\n */\n\nlocal const z_crc_t FAR crc_table[TBLS][256] =\n{\n  {\n    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,\n    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,\n    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,\n    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,\n    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,\n    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,\n    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,\n    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,\n    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,\n    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,\n    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,\n    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,\n    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,\n    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,\n    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,\n    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,\n    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,\n    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,\n    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,\n    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,\n    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,\n    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,\n    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,\n    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,\n    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,\n    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,\n    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,\n    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,\n    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,\n    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,\n    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,\n    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,\n    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,\n    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,\n    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,\n    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,\n    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,\n    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,\n    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,\n    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,\n    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,\n    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,\n    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,\n    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,\n    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,\n    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,\n    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,\n    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,\n    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,\n    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,\n    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,\n    0x2d02ef8dUL\n#ifdef BYFOUR\n  },\n  {\n    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,\n    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,\n    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,\n    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,\n    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,\n    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,\n    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,\n    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,\n    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,\n    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,\n    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,\n    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,\n    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,\n    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,\n    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,\n    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,\n    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,\n    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,\n    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,\n    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,\n    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,\n    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,\n    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,\n    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,\n    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,\n    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,\n    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,\n    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,\n    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,\n    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,\n    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,\n    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,\n    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,\n    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,\n    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,\n    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,\n    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,\n    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,\n    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,\n    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,\n    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,\n    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,\n    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,\n    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,\n    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,\n    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,\n    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,\n    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,\n    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,\n    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,\n    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,\n    0x9324fd72UL\n  },\n  {\n    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,\n    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,\n    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,\n    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,\n    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,\n    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,\n    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,\n    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,\n    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,\n    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,\n    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,\n    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,\n    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,\n    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,\n    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,\n    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,\n    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,\n    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,\n    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,\n    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,\n    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,\n    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,\n    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,\n    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,\n    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,\n    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,\n    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,\n    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,\n    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,\n    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,\n    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,\n    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,\n    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,\n    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,\n    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,\n    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,\n    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,\n    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,\n    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,\n    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,\n    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,\n    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,\n    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,\n    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,\n    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,\n    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,\n    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,\n    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,\n    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,\n    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,\n    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,\n    0xbe9834edUL\n  },\n  {\n    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,\n    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,\n    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,\n    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,\n    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,\n    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,\n    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,\n    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,\n    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,\n    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,\n    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,\n    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,\n    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,\n    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,\n    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,\n    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,\n    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,\n    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,\n    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,\n    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,\n    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,\n    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,\n    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,\n    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,\n    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,\n    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,\n    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,\n    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,\n    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,\n    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,\n    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,\n    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,\n    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,\n    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,\n    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,\n    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,\n    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,\n    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,\n    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,\n    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,\n    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,\n    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,\n    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,\n    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,\n    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,\n    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,\n    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,\n    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,\n    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,\n    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,\n    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,\n    0xde0506f1UL\n  },\n  {\n    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,\n    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,\n    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,\n    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,\n    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,\n    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,\n    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,\n    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,\n    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,\n    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,\n    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,\n    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,\n    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,\n    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,\n    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,\n    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,\n    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,\n    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,\n    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,\n    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,\n    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,\n    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,\n    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,\n    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,\n    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,\n    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,\n    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,\n    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,\n    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,\n    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,\n    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,\n    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,\n    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,\n    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,\n    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,\n    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,\n    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,\n    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,\n    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,\n    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,\n    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,\n    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,\n    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,\n    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,\n    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,\n    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,\n    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,\n    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,\n    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,\n    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,\n    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,\n    0x8def022dUL\n  },\n  {\n    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,\n    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,\n    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,\n    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,\n    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,\n    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,\n    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,\n    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,\n    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,\n    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,\n    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,\n    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,\n    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,\n    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,\n    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,\n    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,\n    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,\n    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,\n    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,\n    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,\n    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,\n    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,\n    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,\n    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,\n    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,\n    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,\n    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,\n    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,\n    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,\n    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,\n    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,\n    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,\n    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,\n    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,\n    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,\n    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,\n    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,\n    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,\n    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,\n    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,\n    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,\n    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,\n    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,\n    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,\n    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,\n    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,\n    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,\n    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,\n    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,\n    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,\n    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,\n    0x72fd2493UL\n  },\n  {\n    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,\n    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,\n    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,\n    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,\n    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,\n    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,\n    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,\n    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,\n    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,\n    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,\n    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,\n    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,\n    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,\n    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,\n    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,\n    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,\n    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,\n    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,\n    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,\n    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,\n    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,\n    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,\n    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,\n    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,\n    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,\n    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,\n    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,\n    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,\n    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,\n    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,\n    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,\n    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,\n    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,\n    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,\n    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,\n    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,\n    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,\n    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,\n    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,\n    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,\n    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,\n    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,\n    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,\n    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,\n    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,\n    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,\n    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,\n    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,\n    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,\n    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,\n    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,\n    0xed3498beUL\n  },\n  {\n    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,\n    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,\n    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,\n    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,\n    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,\n    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,\n    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,\n    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,\n    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,\n    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,\n    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,\n    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,\n    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,\n    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,\n    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,\n    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,\n    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,\n    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,\n    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,\n    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,\n    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,\n    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,\n    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,\n    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,\n    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,\n    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,\n    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,\n    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,\n    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,\n    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,\n    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,\n    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,\n    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,\n    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,\n    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,\n    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,\n    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,\n    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,\n    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,\n    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,\n    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,\n    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,\n    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,\n    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,\n    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,\n    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,\n    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,\n    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,\n    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,\n    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,\n    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,\n    0xf10605deUL\n#endif\n  }\n};\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/deflate.c",
    "content": "/* deflate.c -- compress data using the deflation algorithm\n * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/*\n *  ALGORITHM\n *\n *      The \"deflation\" process depends on being able to identify portions\n *      of the input text which are identical to earlier input (within a\n *      sliding window trailing behind the input currently being processed).\n *\n *      The most straightforward technique turns out to be the fastest for\n *      most input files: try all possible matches and select the longest.\n *      The key feature of this algorithm is that insertions into the string\n *      dictionary are very simple and thus fast, and deletions are avoided\n *      completely. Insertions are performed at each input character, whereas\n *      string matches are performed only when the previous match ends. So it\n *      is preferable to spend more time in matches to allow very fast string\n *      insertions and avoid deletions. The matching algorithm for small\n *      strings is inspired from that of Rabin & Karp. A brute force approach\n *      is used to find longer strings when a small match has been found.\n *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze\n *      (by Leonid Broukhis).\n *         A previous version of this file used a more sophisticated algorithm\n *      (by Fiala and Greene) which is guaranteed to run in linear amortized\n *      time, but has a larger average cost, uses more memory and is patented.\n *      However the F&G algorithm may be faster for some highly redundant\n *      files if the parameter max_chain_length (described below) is too large.\n *\n *  ACKNOWLEDGEMENTS\n *\n *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and\n *      I found it in 'freeze' written by Leonid Broukhis.\n *      Thanks to many people for bug reports and testing.\n *\n *  REFERENCES\n *\n *      Deutsch, L.P.,\"DEFLATE Compressed Data Format Specification\".\n *      Available in http://tools.ietf.org/html/rfc1951\n *\n *      A description of the Rabin and Karp algorithm is given in the book\n *         \"Algorithms\" by R. Sedgewick, Addison-Wesley, p252.\n *\n *      Fiala,E.R., and Greene,D.H.\n *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595\n *\n */\n\n/* @(#) $Id$ */\n\n#include \"deflate.h\"\n\nconst char deflate_copyright[] =\n   \" deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler \";\n/*\n  If you use the zlib library in a product, an acknowledgment is welcome\n  in the documentation of your product. If for some reason you cannot\n  include such an acknowledgment, I would appreciate that you keep this\n  copyright string in the executable of your product.\n */\n\n/* ===========================================================================\n *  Function prototypes.\n */\ntypedef enum {\n    need_more,      /* block not completed, need more input or more output */\n    block_done,     /* block flush performed */\n    finish_started, /* finish started, need only more output at next deflate */\n    finish_done     /* finish done, accept no more input or output */\n} block_state;\n\ntypedef block_state (*compress_func) OF((deflate_state *s, int flush));\n/* Compression function. Returns the block state after the call. */\n\nlocal int deflateStateCheck      OF((z_streamp strm));\nlocal void slide_hash     OF((deflate_state *s));\nlocal void fill_window    OF((deflate_state *s));\nlocal block_state deflate_stored OF((deflate_state *s, int flush));\nlocal block_state deflate_fast   OF((deflate_state *s, int flush));\n#ifndef FASTEST\nlocal block_state deflate_slow   OF((deflate_state *s, int flush));\n#endif\nlocal block_state deflate_rle    OF((deflate_state *s, int flush));\nlocal block_state deflate_huff   OF((deflate_state *s, int flush));\nlocal void lm_init        OF((deflate_state *s));\nlocal void putShortMSB    OF((deflate_state *s, uInt b));\nlocal void flush_pending  OF((z_streamp strm));\nlocal unsigned read_buf   OF((z_streamp strm, Bytef *buf, unsigned size));\n#ifdef ASMV\n#  pragma message(\"Assembler code may have bugs -- use at your own risk\")\n      void match_init OF((void)); /* asm code initialization */\n      uInt longest_match  OF((deflate_state *s, IPos cur_match));\n#else\nlocal uInt longest_match  OF((deflate_state *s, IPos cur_match));\n#endif\n\n#ifdef ZLIB_DEBUG\nlocal  void check_match OF((deflate_state *s, IPos start, IPos match,\n                            int length));\n#endif\n\n/* ===========================================================================\n * Local data\n */\n\n#define NIL 0\n/* Tail of hash chains */\n\n#ifndef TOO_FAR\n#  define TOO_FAR 4096\n#endif\n/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */\n\n/* Values for max_lazy_match, good_match and max_chain_length, depending on\n * the desired pack level (0..9). The values given below have been tuned to\n * exclude worst case performance for pathological files. Better values may be\n * found for specific files.\n */\ntypedef struct config_s {\n   ush good_length; /* reduce lazy search above this match length */\n   ush max_lazy;    /* do not perform lazy search above this match length */\n   ush nice_length; /* quit search above this match length */\n   ush max_chain;\n   compress_func func;\n} config;\n\n#ifdef FASTEST\nlocal const config configuration_table[2] = {\n/*      good lazy nice chain */\n/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */\n/* 1 */ {4,    4,  8,    4, deflate_fast}}; /* max speed, no lazy matches */\n#else\nlocal const config configuration_table[10] = {\n/*      good lazy nice chain */\n/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */\n/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */\n/* 2 */ {4,    5, 16,    8, deflate_fast},\n/* 3 */ {4,    6, 32,   32, deflate_fast},\n\n/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */\n/* 5 */ {8,   16, 32,   32, deflate_slow},\n/* 6 */ {8,   16, 128, 128, deflate_slow},\n/* 7 */ {8,   32, 128, 256, deflate_slow},\n/* 8 */ {32, 128, 258, 1024, deflate_slow},\n/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */\n#endif\n\n/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4\n * For deflate_fast() (levels <= 3) good is ignored and lazy has a different\n * meaning.\n */\n\n/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */\n#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0))\n\n/* ===========================================================================\n * Update a hash value with the given input byte\n * IN  assertion: all calls to UPDATE_HASH are made with consecutive input\n *    characters, so that a running hash key can be computed from the previous\n *    key instead of complete recalculation each time.\n */\n#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)\n\n\n/* ===========================================================================\n * Insert string str in the dictionary and set match_head to the previous head\n * of the hash chain (the most recent string with same hash key). Return\n * the previous length of the hash chain.\n * If this file is compiled with -DFASTEST, the compression level is forced\n * to 1, and no hash chains are maintained.\n * IN  assertion: all calls to INSERT_STRING are made with consecutive input\n *    characters and the first MIN_MATCH bytes of str are valid (except for\n *    the last MIN_MATCH-1 bytes of the input file).\n */\n#ifdef FASTEST\n#define INSERT_STRING(s, str, match_head) \\\n   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \\\n    match_head = s->head[s->ins_h], \\\n    s->head[s->ins_h] = (Pos)(str))\n#else\n#define INSERT_STRING(s, str, match_head) \\\n   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \\\n    match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \\\n    s->head[s->ins_h] = (Pos)(str))\n#endif\n\n/* ===========================================================================\n * Initialize the hash table (avoiding 64K overflow for 16 bit systems).\n * prev[] will be initialized on the fly.\n */\n#define CLEAR_HASH(s) \\\n    s->head[s->hash_size-1] = NIL; \\\n    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));\n\n/* ===========================================================================\n * Slide the hash table when sliding the window down (could be avoided with 32\n * bit values at the expense of memory usage). We slide even when level == 0 to\n * keep the hash table consistent if we switch back to level > 0 later.\n */\nlocal void slide_hash(s)\n    deflate_state *s;\n{\n    unsigned n, m;\n    Posf *p;\n    uInt wsize = s->w_size;\n\n    n = s->hash_size;\n    p = &s->head[n];\n    do {\n        m = *--p;\n        *p = (Pos)(m >= wsize ? m - wsize : NIL);\n    } while (--n);\n    n = wsize;\n#ifndef FASTEST\n    p = &s->prev[n];\n    do {\n        m = *--p;\n        *p = (Pos)(m >= wsize ? m - wsize : NIL);\n        /* If n is not on any hash chain, prev[n] is garbage but\n         * its value will never be used.\n         */\n    } while (--n);\n#endif\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateInit_(strm, level, version, stream_size)\n    z_streamp strm;\n    int level;\n    const char *version;\n    int stream_size;\n{\n    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,\n                         Z_DEFAULT_STRATEGY, version, stream_size);\n    /* To do: ignore strm->next_in if we use it as window */\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,\n                  version, stream_size)\n    z_streamp strm;\n    int  level;\n    int  method;\n    int  windowBits;\n    int  memLevel;\n    int  strategy;\n    const char *version;\n    int stream_size;\n{\n    deflate_state *s;\n    int wrap = 1;\n    static const char my_version[] = ZLIB_VERSION;\n\n    ushf *overlay;\n    /* We overlay pending_buf and d_buf+l_buf. This works since the average\n     * output size for (length,distance) codes is <= 24 bits.\n     */\n\n    if (version == Z_NULL || version[0] != my_version[0] ||\n        stream_size != sizeof(z_stream)) {\n        return Z_VERSION_ERROR;\n    }\n    if (strm == Z_NULL) return Z_STREAM_ERROR;\n\n    strm->msg = Z_NULL;\n    if (strm->zalloc == (alloc_func)0) {\n#ifdef Z_SOLO\n        return Z_STREAM_ERROR;\n#else\n        strm->zalloc = zcalloc;\n        strm->opaque = (voidpf)0;\n#endif\n    }\n    if (strm->zfree == (free_func)0)\n#ifdef Z_SOLO\n        return Z_STREAM_ERROR;\n#else\n        strm->zfree = zcfree;\n#endif\n\n#ifdef FASTEST\n    if (level != 0) level = 1;\n#else\n    if (level == Z_DEFAULT_COMPRESSION) level = 6;\n#endif\n\n    if (windowBits < 0) { /* suppress zlib wrapper */\n        wrap = 0;\n        windowBits = -windowBits;\n    }\n#ifdef GZIP\n    else if (windowBits > 15) {\n        wrap = 2;       /* write gzip wrapper instead */\n        windowBits -= 16;\n    }\n#endif\n    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||\n        windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||\n        strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) {\n        return Z_STREAM_ERROR;\n    }\n    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */\n    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));\n    if (s == Z_NULL) return Z_MEM_ERROR;\n    strm->state = (struct internal_state FAR *)s;\n    s->strm = strm;\n    s->status = INIT_STATE;     /* to pass state test in deflateReset() */\n\n    s->wrap = wrap;\n    s->gzhead = Z_NULL;\n    s->w_bits = (uInt)windowBits;\n    s->w_size = 1 << s->w_bits;\n    s->w_mask = s->w_size - 1;\n\n    s->hash_bits = (uInt)memLevel + 7;\n    s->hash_size = 1 << s->hash_bits;\n    s->hash_mask = s->hash_size - 1;\n    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);\n\n    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));\n    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos));\n    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos));\n\n    s->high_water = 0;      /* nothing written to s->window yet */\n\n    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */\n\n    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);\n    s->pending_buf = (uchf *) overlay;\n    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);\n\n    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||\n        s->pending_buf == Z_NULL) {\n        s->status = FINISH_STATE;\n        strm->msg = ERR_MSG(Z_MEM_ERROR);\n        deflateEnd (strm);\n        return Z_MEM_ERROR;\n    }\n    s->d_buf = overlay + s->lit_bufsize/sizeof(ush);\n    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;\n\n    s->level = level;\n    s->strategy = strategy;\n    s->method = (Byte)method;\n\n    return deflateReset(strm);\n}\n\n/* =========================================================================\n * Check for a valid deflate stream state. Return 0 if ok, 1 if not.\n */\nlocal int deflateStateCheck (strm)\n    z_streamp strm;\n{\n    deflate_state *s;\n    if (strm == Z_NULL ||\n        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)\n        return 1;\n    s = strm->state;\n    if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE &&\n#ifdef GZIP\n                                           s->status != GZIP_STATE &&\n#endif\n                                           s->status != EXTRA_STATE &&\n                                           s->status != NAME_STATE &&\n                                           s->status != COMMENT_STATE &&\n                                           s->status != HCRC_STATE &&\n                                           s->status != BUSY_STATE &&\n                                           s->status != FINISH_STATE))\n        return 1;\n    return 0;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)\n    z_streamp strm;\n    const Bytef *dictionary;\n    uInt  dictLength;\n{\n    deflate_state *s;\n    uInt str, n;\n    int wrap;\n    unsigned avail;\n    z_const unsigned char *next;\n\n    if (deflateStateCheck(strm) || dictionary == Z_NULL)\n        return Z_STREAM_ERROR;\n    s = strm->state;\n    wrap = s->wrap;\n    if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)\n        return Z_STREAM_ERROR;\n\n    /* when using zlib wrappers, compute Adler-32 for provided dictionary */\n    if (wrap == 1)\n        strm->adler = adler32(strm->adler, dictionary, dictLength);\n    s->wrap = 0;                    /* avoid computing Adler-32 in read_buf */\n\n    /* if dictionary would fill window, just replace the history */\n    if (dictLength >= s->w_size) {\n        if (wrap == 0) {            /* already empty otherwise */\n            CLEAR_HASH(s);\n            s->strstart = 0;\n            s->block_start = 0L;\n            s->insert = 0;\n        }\n        dictionary += dictLength - s->w_size;  /* use the tail */\n        dictLength = s->w_size;\n    }\n\n    /* insert dictionary into window and hash */\n    avail = strm->avail_in;\n    next = strm->next_in;\n    strm->avail_in = dictLength;\n    strm->next_in = (z_const Bytef *)dictionary;\n    fill_window(s);\n    while (s->lookahead >= MIN_MATCH) {\n        str = s->strstart;\n        n = s->lookahead - (MIN_MATCH-1);\n        do {\n            UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);\n#ifndef FASTEST\n            s->prev[str & s->w_mask] = s->head[s->ins_h];\n#endif\n            s->head[s->ins_h] = (Pos)str;\n            str++;\n        } while (--n);\n        s->strstart = str;\n        s->lookahead = MIN_MATCH-1;\n        fill_window(s);\n    }\n    s->strstart += s->lookahead;\n    s->block_start = (long)s->strstart;\n    s->insert = s->lookahead;\n    s->lookahead = 0;\n    s->match_length = s->prev_length = MIN_MATCH-1;\n    s->match_available = 0;\n    strm->next_in = next;\n    strm->avail_in = avail;\n    s->wrap = wrap;\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)\n    z_streamp strm;\n    Bytef *dictionary;\n    uInt  *dictLength;\n{\n    deflate_state *s;\n    uInt len;\n\n    if (deflateStateCheck(strm))\n        return Z_STREAM_ERROR;\n    s = strm->state;\n    len = s->strstart + s->lookahead;\n    if (len > s->w_size)\n        len = s->w_size;\n    if (dictionary != Z_NULL && len)\n        zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len);\n    if (dictLength != Z_NULL)\n        *dictLength = len;\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateResetKeep (strm)\n    z_streamp strm;\n{\n    deflate_state *s;\n\n    if (deflateStateCheck(strm)) {\n        return Z_STREAM_ERROR;\n    }\n\n    strm->total_in = strm->total_out = 0;\n    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */\n    strm->data_type = Z_UNKNOWN;\n\n    s = (deflate_state *)strm->state;\n    s->pending = 0;\n    s->pending_out = s->pending_buf;\n\n    if (s->wrap < 0) {\n        s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */\n    }\n    s->status =\n#ifdef GZIP\n        s->wrap == 2 ? GZIP_STATE :\n#endif\n        s->wrap ? INIT_STATE : BUSY_STATE;\n    strm->adler =\n#ifdef GZIP\n        s->wrap == 2 ? crc32(0L, Z_NULL, 0) :\n#endif\n        adler32(0L, Z_NULL, 0);\n    s->last_flush = Z_NO_FLUSH;\n\n    _tr_init(s);\n\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateReset (strm)\n    z_streamp strm;\n{\n    int ret;\n\n    ret = deflateResetKeep(strm);\n    if (ret == Z_OK)\n        lm_init(strm->state);\n    return ret;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateSetHeader (strm, head)\n    z_streamp strm;\n    gz_headerp head;\n{\n    if (deflateStateCheck(strm) || strm->state->wrap != 2)\n        return Z_STREAM_ERROR;\n    strm->state->gzhead = head;\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflatePending (strm, pending, bits)\n    unsigned *pending;\n    int *bits;\n    z_streamp strm;\n{\n    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;\n    if (pending != Z_NULL)\n        *pending = strm->state->pending;\n    if (bits != Z_NULL)\n        *bits = strm->state->bi_valid;\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflatePrime (strm, bits, value)\n    z_streamp strm;\n    int bits;\n    int value;\n{\n    deflate_state *s;\n    int put;\n\n    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;\n    s = strm->state;\n    if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))\n        return Z_BUF_ERROR;\n    do {\n        put = Buf_size - s->bi_valid;\n        if (put > bits)\n            put = bits;\n        s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);\n        s->bi_valid += put;\n        _tr_flush_bits(s);\n        value >>= put;\n        bits -= put;\n    } while (bits);\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateParams(strm, level, strategy)\n    z_streamp strm;\n    int level;\n    int strategy;\n{\n    deflate_state *s;\n    compress_func func;\n\n    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;\n    s = strm->state;\n\n#ifdef FASTEST\n    if (level != 0) level = 1;\n#else\n    if (level == Z_DEFAULT_COMPRESSION) level = 6;\n#endif\n    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {\n        return Z_STREAM_ERROR;\n    }\n    func = configuration_table[s->level].func;\n\n    if ((strategy != s->strategy || func != configuration_table[level].func) &&\n        s->high_water) {\n        /* Flush the last buffer: */\n        int err = deflate(strm, Z_BLOCK);\n        if (err == Z_STREAM_ERROR)\n            return err;\n        if (strm->avail_out == 0)\n            return Z_BUF_ERROR;\n    }\n    if (s->level != level) {\n        if (s->level == 0 && s->matches != 0) {\n            if (s->matches == 1)\n                slide_hash(s);\n            else\n                CLEAR_HASH(s);\n            s->matches = 0;\n        }\n        s->level = level;\n        s->max_lazy_match   = configuration_table[level].max_lazy;\n        s->good_match       = configuration_table[level].good_length;\n        s->nice_match       = configuration_table[level].nice_length;\n        s->max_chain_length = configuration_table[level].max_chain;\n    }\n    s->strategy = strategy;\n    return Z_OK;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)\n    z_streamp strm;\n    int good_length;\n    int max_lazy;\n    int nice_length;\n    int max_chain;\n{\n    deflate_state *s;\n\n    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;\n    s = strm->state;\n    s->good_match = (uInt)good_length;\n    s->max_lazy_match = (uInt)max_lazy;\n    s->nice_match = nice_length;\n    s->max_chain_length = (uInt)max_chain;\n    return Z_OK;\n}\n\n/* =========================================================================\n * For the default windowBits of 15 and memLevel of 8, this function returns\n * a close to exact, as well as small, upper bound on the compressed size.\n * They are coded as constants here for a reason--if the #define's are\n * changed, then this function needs to be changed as well.  The return\n * value for 15 and 8 only works for those exact settings.\n *\n * For any setting other than those defaults for windowBits and memLevel,\n * the value returned is a conservative worst case for the maximum expansion\n * resulting from using fixed blocks instead of stored blocks, which deflate\n * can emit on compressed data for some combinations of the parameters.\n *\n * This function could be more sophisticated to provide closer upper bounds for\n * every combination of windowBits and memLevel.  But even the conservative\n * upper bound of about 14% expansion does not seem onerous for output buffer\n * allocation.\n */\nuLong ZEXPORT deflateBound(strm, sourceLen)\n    z_streamp strm;\n    uLong sourceLen;\n{\n    deflate_state *s;\n    uLong complen, wraplen;\n\n    /* conservative upper bound for compressed data */\n    complen = sourceLen +\n              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;\n\n    /* if can't get parameters, return conservative bound plus zlib wrapper */\n    if (deflateStateCheck(strm))\n        return complen + 6;\n\n    /* compute wrapper length */\n    s = strm->state;\n    switch (s->wrap) {\n    case 0:                                 /* raw deflate */\n        wraplen = 0;\n        break;\n    case 1:                                 /* zlib wrapper */\n        wraplen = 6 + (s->strstart ? 4 : 0);\n        break;\n#ifdef GZIP\n    case 2:                                 /* gzip wrapper */\n        wraplen = 18;\n        if (s->gzhead != Z_NULL) {          /* user-supplied gzip header */\n            Bytef *str;\n            if (s->gzhead->extra != Z_NULL)\n                wraplen += 2 + s->gzhead->extra_len;\n            str = s->gzhead->name;\n            if (str != Z_NULL)\n                do {\n                    wraplen++;\n                } while (*str++);\n            str = s->gzhead->comment;\n            if (str != Z_NULL)\n                do {\n                    wraplen++;\n                } while (*str++);\n            if (s->gzhead->hcrc)\n                wraplen += 2;\n        }\n        break;\n#endif\n    default:                                /* for compiler happiness */\n        wraplen = 6;\n    }\n\n    /* if not default parameters, return conservative bound */\n    if (s->w_bits != 15 || s->hash_bits != 8 + 7)\n        return complen + wraplen;\n\n    /* default settings: return tight bound for that case */\n    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +\n           (sourceLen >> 25) + 13 - 6 + wraplen;\n}\n\n/* =========================================================================\n * Put a short in the pending buffer. The 16-bit value is put in MSB order.\n * IN assertion: the stream state is correct and there is enough room in\n * pending_buf.\n */\nlocal void putShortMSB (s, b)\n    deflate_state *s;\n    uInt b;\n{\n    put_byte(s, (Byte)(b >> 8));\n    put_byte(s, (Byte)(b & 0xff));\n}\n\n/* =========================================================================\n * Flush as much pending output as possible. All deflate() output, except for\n * some deflate_stored() output, goes through this function so some\n * applications may wish to modify it to avoid allocating a large\n * strm->next_out buffer and copying into it. (See also read_buf()).\n */\nlocal void flush_pending(strm)\n    z_streamp strm;\n{\n    unsigned len;\n    deflate_state *s = strm->state;\n\n    _tr_flush_bits(s);\n    len = s->pending;\n    if (len > strm->avail_out) len = strm->avail_out;\n    if (len == 0) return;\n\n    zmemcpy(strm->next_out, s->pending_out, len);\n    strm->next_out  += len;\n    s->pending_out  += len;\n    strm->total_out += len;\n    strm->avail_out -= len;\n    s->pending      -= len;\n    if (s->pending == 0) {\n        s->pending_out = s->pending_buf;\n    }\n}\n\n/* ===========================================================================\n * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1].\n */\n#define HCRC_UPDATE(beg) \\\n    do { \\\n        if (s->gzhead->hcrc && s->pending > (beg)) \\\n            strm->adler = crc32(strm->adler, s->pending_buf + (beg), \\\n                                s->pending - (beg)); \\\n    } while (0)\n\n/* ========================================================================= */\nint ZEXPORT deflate (strm, flush)\n    z_streamp strm;\n    int flush;\n{\n    int old_flush; /* value of flush param for previous deflate call */\n    deflate_state *s;\n\n    if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) {\n        return Z_STREAM_ERROR;\n    }\n    s = strm->state;\n\n    if (strm->next_out == Z_NULL ||\n        (strm->avail_in != 0 && strm->next_in == Z_NULL) ||\n        (s->status == FINISH_STATE && flush != Z_FINISH)) {\n        ERR_RETURN(strm, Z_STREAM_ERROR);\n    }\n    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);\n\n    old_flush = s->last_flush;\n    s->last_flush = flush;\n\n    /* Flush as much pending output as possible */\n    if (s->pending != 0) {\n        flush_pending(strm);\n        if (strm->avail_out == 0) {\n            /* Since avail_out is 0, deflate will be called again with\n             * more output space, but possibly with both pending and\n             * avail_in equal to zero. There won't be anything to do,\n             * but this is not an error situation so make sure we\n             * return OK instead of BUF_ERROR at next call of deflate:\n             */\n            s->last_flush = -1;\n            return Z_OK;\n        }\n\n    /* Make sure there is something to do and avoid duplicate consecutive\n     * flushes. For repeated and useless calls with Z_FINISH, we keep\n     * returning Z_STREAM_END instead of Z_BUF_ERROR.\n     */\n    } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&\n               flush != Z_FINISH) {\n        ERR_RETURN(strm, Z_BUF_ERROR);\n    }\n\n    /* User must not provide more input after the first FINISH: */\n    if (s->status == FINISH_STATE && strm->avail_in != 0) {\n        ERR_RETURN(strm, Z_BUF_ERROR);\n    }\n\n    /* Write the header */\n    if (s->status == INIT_STATE) {\n        /* zlib header */\n        uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;\n        uInt level_flags;\n\n        if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)\n            level_flags = 0;\n        else if (s->level < 6)\n            level_flags = 1;\n        else if (s->level == 6)\n            level_flags = 2;\n        else\n            level_flags = 3;\n        header |= (level_flags << 6);\n        if (s->strstart != 0) header |= PRESET_DICT;\n        header += 31 - (header % 31);\n\n        putShortMSB(s, header);\n\n        /* Save the adler32 of the preset dictionary: */\n        if (s->strstart != 0) {\n            putShortMSB(s, (uInt)(strm->adler >> 16));\n            putShortMSB(s, (uInt)(strm->adler & 0xffff));\n        }\n        strm->adler = adler32(0L, Z_NULL, 0);\n        s->status = BUSY_STATE;\n\n        /* Compression must start with an empty pending buffer */\n        flush_pending(strm);\n        if (s->pending != 0) {\n            s->last_flush = -1;\n            return Z_OK;\n        }\n    }\n#ifdef GZIP\n    if (s->status == GZIP_STATE) {\n        /* gzip header */\n        strm->adler = crc32(0L, Z_NULL, 0);\n        put_byte(s, 31);\n        put_byte(s, 139);\n        put_byte(s, 8);\n        if (s->gzhead == Z_NULL) {\n            put_byte(s, 0);\n            put_byte(s, 0);\n            put_byte(s, 0);\n            put_byte(s, 0);\n            put_byte(s, 0);\n            put_byte(s, s->level == 9 ? 2 :\n                     (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?\n                      4 : 0));\n            put_byte(s, OS_CODE);\n            s->status = BUSY_STATE;\n\n            /* Compression must start with an empty pending buffer */\n            flush_pending(strm);\n            if (s->pending != 0) {\n                s->last_flush = -1;\n                return Z_OK;\n            }\n        }\n        else {\n            put_byte(s, (s->gzhead->text ? 1 : 0) +\n                     (s->gzhead->hcrc ? 2 : 0) +\n                     (s->gzhead->extra == Z_NULL ? 0 : 4) +\n                     (s->gzhead->name == Z_NULL ? 0 : 8) +\n                     (s->gzhead->comment == Z_NULL ? 0 : 16)\n                     );\n            put_byte(s, (Byte)(s->gzhead->time & 0xff));\n            put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));\n            put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));\n            put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));\n            put_byte(s, s->level == 9 ? 2 :\n                     (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?\n                      4 : 0));\n            put_byte(s, s->gzhead->os & 0xff);\n            if (s->gzhead->extra != Z_NULL) {\n                put_byte(s, s->gzhead->extra_len & 0xff);\n                put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);\n            }\n            if (s->gzhead->hcrc)\n                strm->adler = crc32(strm->adler, s->pending_buf,\n                                    s->pending);\n            s->gzindex = 0;\n            s->status = EXTRA_STATE;\n        }\n    }\n    if (s->status == EXTRA_STATE) {\n        if (s->gzhead->extra != Z_NULL) {\n            ulg beg = s->pending;   /* start of bytes to update crc */\n            uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex;\n            while (s->pending + left > s->pending_buf_size) {\n                uInt copy = s->pending_buf_size - s->pending;\n                zmemcpy(s->pending_buf + s->pending,\n                        s->gzhead->extra + s->gzindex, copy);\n                s->pending = s->pending_buf_size;\n                HCRC_UPDATE(beg);\n                s->gzindex += copy;\n                flush_pending(strm);\n                if (s->pending != 0) {\n                    s->last_flush = -1;\n                    return Z_OK;\n                }\n                beg = 0;\n                left -= copy;\n            }\n            zmemcpy(s->pending_buf + s->pending,\n                    s->gzhead->extra + s->gzindex, left);\n            s->pending += left;\n            HCRC_UPDATE(beg);\n            s->gzindex = 0;\n        }\n        s->status = NAME_STATE;\n    }\n    if (s->status == NAME_STATE) {\n        if (s->gzhead->name != Z_NULL) {\n            ulg beg = s->pending;   /* start of bytes to update crc */\n            int val;\n            do {\n                if (s->pending == s->pending_buf_size) {\n                    HCRC_UPDATE(beg);\n                    flush_pending(strm);\n                    if (s->pending != 0) {\n                        s->last_flush = -1;\n                        return Z_OK;\n                    }\n                    beg = 0;\n                }\n                val = s->gzhead->name[s->gzindex++];\n                put_byte(s, val);\n            } while (val != 0);\n            HCRC_UPDATE(beg);\n            s->gzindex = 0;\n        }\n        s->status = COMMENT_STATE;\n    }\n    if (s->status == COMMENT_STATE) {\n        if (s->gzhead->comment != Z_NULL) {\n            ulg beg = s->pending;   /* start of bytes to update crc */\n            int val;\n            do {\n                if (s->pending == s->pending_buf_size) {\n                    HCRC_UPDATE(beg);\n                    flush_pending(strm);\n                    if (s->pending != 0) {\n                        s->last_flush = -1;\n                        return Z_OK;\n                    }\n                    beg = 0;\n                }\n                val = s->gzhead->comment[s->gzindex++];\n                put_byte(s, val);\n            } while (val != 0);\n            HCRC_UPDATE(beg);\n        }\n        s->status = HCRC_STATE;\n    }\n    if (s->status == HCRC_STATE) {\n        if (s->gzhead->hcrc) {\n            if (s->pending + 2 > s->pending_buf_size) {\n                flush_pending(strm);\n                if (s->pending != 0) {\n                    s->last_flush = -1;\n                    return Z_OK;\n                }\n            }\n            put_byte(s, (Byte)(strm->adler & 0xff));\n            put_byte(s, (Byte)((strm->adler >> 8) & 0xff));\n            strm->adler = crc32(0L, Z_NULL, 0);\n        }\n        s->status = BUSY_STATE;\n\n        /* Compression must start with an empty pending buffer */\n        flush_pending(strm);\n        if (s->pending != 0) {\n            s->last_flush = -1;\n            return Z_OK;\n        }\n    }\n#endif\n\n    /* Start a new block or continue the current one.\n     */\n    if (strm->avail_in != 0 || s->lookahead != 0 ||\n        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {\n        block_state bstate;\n\n        bstate = s->level == 0 ? deflate_stored(s, flush) :\n                 s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :\n                 s->strategy == Z_RLE ? deflate_rle(s, flush) :\n                 (*(configuration_table[s->level].func))(s, flush);\n\n        if (bstate == finish_started || bstate == finish_done) {\n            s->status = FINISH_STATE;\n        }\n        if (bstate == need_more || bstate == finish_started) {\n            if (strm->avail_out == 0) {\n                s->last_flush = -1; /* avoid BUF_ERROR next call, see above */\n            }\n            return Z_OK;\n            /* If flush != Z_NO_FLUSH && avail_out == 0, the next call\n             * of deflate should use the same flush parameter to make sure\n             * that the flush is complete. So we don't have to output an\n             * empty block here, this will be done at next call. This also\n             * ensures that for a very small output buffer, we emit at most\n             * one empty block.\n             */\n        }\n        if (bstate == block_done) {\n            if (flush == Z_PARTIAL_FLUSH) {\n                _tr_align(s);\n            } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */\n                _tr_stored_block(s, (char*)0, 0L, 0);\n                /* For a full flush, this empty block will be recognized\n                 * as a special marker by inflate_sync().\n                 */\n                if (flush == Z_FULL_FLUSH) {\n                    CLEAR_HASH(s);             /* forget history */\n                    if (s->lookahead == 0) {\n                        s->strstart = 0;\n                        s->block_start = 0L;\n                        s->insert = 0;\n                    }\n                }\n            }\n            flush_pending(strm);\n            if (strm->avail_out == 0) {\n              s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */\n              return Z_OK;\n            }\n        }\n    }\n\n    if (flush != Z_FINISH) return Z_OK;\n    if (s->wrap <= 0) return Z_STREAM_END;\n\n    /* Write the trailer */\n#ifdef GZIP\n    if (s->wrap == 2) {\n        put_byte(s, (Byte)(strm->adler & 0xff));\n        put_byte(s, (Byte)((strm->adler >> 8) & 0xff));\n        put_byte(s, (Byte)((strm->adler >> 16) & 0xff));\n        put_byte(s, (Byte)((strm->adler >> 24) & 0xff));\n        put_byte(s, (Byte)(strm->total_in & 0xff));\n        put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));\n        put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));\n        put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));\n    }\n    else\n#endif\n    {\n        putShortMSB(s, (uInt)(strm->adler >> 16));\n        putShortMSB(s, (uInt)(strm->adler & 0xffff));\n    }\n    flush_pending(strm);\n    /* If avail_out is zero, the application will call deflate again\n     * to flush the rest.\n     */\n    if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */\n    return s->pending != 0 ? Z_OK : Z_STREAM_END;\n}\n\n/* ========================================================================= */\nint ZEXPORT deflateEnd (strm)\n    z_streamp strm;\n{\n    int status;\n\n    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;\n\n    status = strm->state->status;\n\n    /* Deallocate in reverse order of allocations: */\n    TRY_FREE(strm, strm->state->pending_buf);\n    TRY_FREE(strm, strm->state->head);\n    TRY_FREE(strm, strm->state->prev);\n    TRY_FREE(strm, strm->state->window);\n\n    ZFREE(strm, strm->state);\n    strm->state = Z_NULL;\n\n    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;\n}\n\n/* =========================================================================\n * Copy the source state to the destination state.\n * To simplify the source, this is not supported for 16-bit MSDOS (which\n * doesn't have enough memory anyway to duplicate compression states).\n */\nint ZEXPORT deflateCopy (dest, source)\n    z_streamp dest;\n    z_streamp source;\n{\n#ifdef MAXSEG_64K\n    return Z_STREAM_ERROR;\n#else\n    deflate_state *ds;\n    deflate_state *ss;\n    ushf *overlay;\n\n\n    if (deflateStateCheck(source) || dest == Z_NULL) {\n        return Z_STREAM_ERROR;\n    }\n\n    ss = source->state;\n\n    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));\n\n    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));\n    if (ds == Z_NULL) return Z_MEM_ERROR;\n    dest->state = (struct internal_state FAR *) ds;\n    zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));\n    ds->strm = dest;\n\n    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));\n    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos));\n    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos));\n    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);\n    ds->pending_buf = (uchf *) overlay;\n\n    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||\n        ds->pending_buf == Z_NULL) {\n        deflateEnd (dest);\n        return Z_MEM_ERROR;\n    }\n    /* following zmemcpy do not work for 16-bit MSDOS */\n    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));\n    zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));\n    zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));\n    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);\n\n    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);\n    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);\n    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;\n\n    ds->l_desc.dyn_tree = ds->dyn_ltree;\n    ds->d_desc.dyn_tree = ds->dyn_dtree;\n    ds->bl_desc.dyn_tree = ds->bl_tree;\n\n    return Z_OK;\n#endif /* MAXSEG_64K */\n}\n\n/* ===========================================================================\n * Read a new buffer from the current input stream, update the adler32\n * and total number of bytes read.  All deflate() input goes through\n * this function so some applications may wish to modify it to avoid\n * allocating a large strm->next_in buffer and copying from it.\n * (See also flush_pending()).\n */\nlocal unsigned read_buf(strm, buf, size)\n    z_streamp strm;\n    Bytef *buf;\n    unsigned size;\n{\n    unsigned len = strm->avail_in;\n\n    if (len > size) len = size;\n    if (len == 0) return 0;\n\n    strm->avail_in  -= len;\n\n    zmemcpy(buf, strm->next_in, len);\n    if (strm->state->wrap == 1) {\n        strm->adler = adler32(strm->adler, buf, len);\n    }\n#ifdef GZIP\n    else if (strm->state->wrap == 2) {\n        strm->adler = crc32(strm->adler, buf, len);\n    }\n#endif\n    strm->next_in  += len;\n    strm->total_in += len;\n\n    return len;\n}\n\n/* ===========================================================================\n * Initialize the \"longest match\" routines for a new zlib stream\n */\nlocal void lm_init (s)\n    deflate_state *s;\n{\n    s->window_size = (ulg)2L*s->w_size;\n\n    CLEAR_HASH(s);\n\n    /* Set the default configuration parameters:\n     */\n    s->max_lazy_match   = configuration_table[s->level].max_lazy;\n    s->good_match       = configuration_table[s->level].good_length;\n    s->nice_match       = configuration_table[s->level].nice_length;\n    s->max_chain_length = configuration_table[s->level].max_chain;\n\n    s->strstart = 0;\n    s->block_start = 0L;\n    s->lookahead = 0;\n    s->insert = 0;\n    s->match_length = s->prev_length = MIN_MATCH-1;\n    s->match_available = 0;\n    s->ins_h = 0;\n#ifndef FASTEST\n#ifdef ASMV\n    match_init(); /* initialize the asm code */\n#endif\n#endif\n}\n\n#ifndef FASTEST\n/* ===========================================================================\n * Set match_start to the longest match starting at the given string and\n * return its length. Matches shorter or equal to prev_length are discarded,\n * in which case the result is equal to prev_length and match_start is\n * garbage.\n * IN assertions: cur_match is the head of the hash chain for the current\n *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1\n * OUT assertion: the match length is not greater than s->lookahead.\n */\n#ifndef ASMV\n/* For 80x86 and 680x0, an optimized version will be provided in match.asm or\n * match.S. The code will be functionally equivalent.\n */\nlocal uInt longest_match(s, cur_match)\n    deflate_state *s;\n    IPos cur_match;                             /* current match */\n{\n    unsigned chain_length = s->max_chain_length;/* max hash chain length */\n    register Bytef *scan = s->window + s->strstart; /* current string */\n    register Bytef *match;                      /* matched string */\n    register int len;                           /* length of current match */\n    int best_len = (int)s->prev_length;         /* best match length so far */\n    int nice_match = s->nice_match;             /* stop if match long enough */\n    IPos limit = s->strstart > (IPos)MAX_DIST(s) ?\n        s->strstart - (IPos)MAX_DIST(s) : NIL;\n    /* Stop when cur_match becomes <= limit. To simplify the code,\n     * we prevent matches with the string of window index 0.\n     */\n    Posf *prev = s->prev;\n    uInt wmask = s->w_mask;\n\n#ifdef UNALIGNED_OK\n    /* Compare two bytes at a time. Note: this is not always beneficial.\n     * Try with and without -DUNALIGNED_OK to check.\n     */\n    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;\n    register ush scan_start = *(ushf*)scan;\n    register ush scan_end   = *(ushf*)(scan+best_len-1);\n#else\n    register Bytef *strend = s->window + s->strstart + MAX_MATCH;\n    register Byte scan_end1  = scan[best_len-1];\n    register Byte scan_end   = scan[best_len];\n#endif\n\n    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.\n     * It is easy to get rid of this optimization if necessary.\n     */\n    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, \"Code too clever\");\n\n    /* Do not waste too much time if we already have a good match: */\n    if (s->prev_length >= s->good_match) {\n        chain_length >>= 2;\n    }\n    /* Do not look for matches beyond the end of the input. This is necessary\n     * to make deflate deterministic.\n     */\n    if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead;\n\n    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, \"need lookahead\");\n\n    do {\n        Assert(cur_match < s->strstart, \"no future\");\n        match = s->window + cur_match;\n\n        /* Skip to next match if the match length cannot increase\n         * or if the match length is less than 2.  Note that the checks below\n         * for insufficient lookahead only occur occasionally for performance\n         * reasons.  Therefore uninitialized memory will be accessed, and\n         * conditional jumps will be made that depend on those values.\n         * However the length of the match is limited to the lookahead, so\n         * the output of deflate is not affected by the uninitialized values.\n         */\n#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)\n        /* This code assumes sizeof(unsigned short) == 2. Do not use\n         * UNALIGNED_OK if your compiler uses a different size.\n         */\n        if (*(ushf*)(match+best_len-1) != scan_end ||\n            *(ushf*)match != scan_start) continue;\n\n        /* It is not necessary to compare scan[2] and match[2] since they are\n         * always equal when the other bytes match, given that the hash keys\n         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at\n         * strstart+3, +5, ... up to strstart+257. We check for insufficient\n         * lookahead only every 4th comparison; the 128th check will be made\n         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is\n         * necessary to put more guard bytes at the end of the window, or\n         * to check more often for insufficient lookahead.\n         */\n        Assert(scan[2] == match[2], \"scan[2]?\");\n        scan++, match++;\n        do {\n        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&\n                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&\n                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&\n                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&\n                 scan < strend);\n        /* The funny \"do {}\" generates better code on most compilers */\n\n        /* Here, scan <= window+strstart+257 */\n        Assert(scan <= s->window+(unsigned)(s->window_size-1), \"wild scan\");\n        if (*scan == *match) scan++;\n\n        len = (MAX_MATCH - 1) - (int)(strend-scan);\n        scan = strend - (MAX_MATCH-1);\n\n#else /* UNALIGNED_OK */\n\n        if (match[best_len]   != scan_end  ||\n            match[best_len-1] != scan_end1 ||\n            *match            != *scan     ||\n            *++match          != scan[1])      continue;\n\n        /* The check at best_len-1 can be removed because it will be made\n         * again later. (This heuristic is not always a win.)\n         * It is not necessary to compare scan[2] and match[2] since they\n         * are always equal when the other bytes match, given that\n         * the hash keys are equal and that HASH_BITS >= 8.\n         */\n        scan += 2, match++;\n        Assert(*scan == *match, \"match[2]?\");\n\n        /* We check for insufficient lookahead only every 8th comparison;\n         * the 256th check will be made at strstart+258.\n         */\n        do {\n        } while (*++scan == *++match && *++scan == *++match &&\n                 *++scan == *++match && *++scan == *++match &&\n                 *++scan == *++match && *++scan == *++match &&\n                 *++scan == *++match && *++scan == *++match &&\n                 scan < strend);\n\n        Assert(scan <= s->window+(unsigned)(s->window_size-1), \"wild scan\");\n\n        len = MAX_MATCH - (int)(strend - scan);\n        scan = strend - MAX_MATCH;\n\n#endif /* UNALIGNED_OK */\n\n        if (len > best_len) {\n            s->match_start = cur_match;\n            best_len = len;\n            if (len >= nice_match) break;\n#ifdef UNALIGNED_OK\n            scan_end = *(ushf*)(scan+best_len-1);\n#else\n            scan_end1  = scan[best_len-1];\n            scan_end   = scan[best_len];\n#endif\n        }\n    } while ((cur_match = prev[cur_match & wmask]) > limit\n             && --chain_length != 0);\n\n    if ((uInt)best_len <= s->lookahead) return (uInt)best_len;\n    return s->lookahead;\n}\n#endif /* ASMV */\n\n#else /* FASTEST */\n\n/* ---------------------------------------------------------------------------\n * Optimized version for FASTEST only\n */\nlocal uInt longest_match(s, cur_match)\n    deflate_state *s;\n    IPos cur_match;                             /* current match */\n{\n    register Bytef *scan = s->window + s->strstart; /* current string */\n    register Bytef *match;                       /* matched string */\n    register int len;                           /* length of current match */\n    register Bytef *strend = s->window + s->strstart + MAX_MATCH;\n\n    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.\n     * It is easy to get rid of this optimization if necessary.\n     */\n    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, \"Code too clever\");\n\n    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, \"need lookahead\");\n\n    Assert(cur_match < s->strstart, \"no future\");\n\n    match = s->window + cur_match;\n\n    /* Return failure if the match length is less than 2:\n     */\n    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;\n\n    /* The check at best_len-1 can be removed because it will be made\n     * again later. (This heuristic is not always a win.)\n     * It is not necessary to compare scan[2] and match[2] since they\n     * are always equal when the other bytes match, given that\n     * the hash keys are equal and that HASH_BITS >= 8.\n     */\n    scan += 2, match += 2;\n    Assert(*scan == *match, \"match[2]?\");\n\n    /* We check for insufficient lookahead only every 8th comparison;\n     * the 256th check will be made at strstart+258.\n     */\n    do {\n    } while (*++scan == *++match && *++scan == *++match &&\n             *++scan == *++match && *++scan == *++match &&\n             *++scan == *++match && *++scan == *++match &&\n             *++scan == *++match && *++scan == *++match &&\n             scan < strend);\n\n    Assert(scan <= s->window+(unsigned)(s->window_size-1), \"wild scan\");\n\n    len = MAX_MATCH - (int)(strend - scan);\n\n    if (len < MIN_MATCH) return MIN_MATCH - 1;\n\n    s->match_start = cur_match;\n    return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;\n}\n\n#endif /* FASTEST */\n\n#ifdef ZLIB_DEBUG\n\n#define EQUAL 0\n/* result of memcmp for equal strings */\n\n/* ===========================================================================\n * Check that the match at match_start is indeed a match.\n */\nlocal void check_match(s, start, match, length)\n    deflate_state *s;\n    IPos start, match;\n    int length;\n{\n    /* check that the match is indeed a match */\n    if (zmemcmp(s->window + match,\n                s->window + start, length) != EQUAL) {\n        fprintf(stderr, \" start %u, match %u, length %d\\n\",\n                start, match, length);\n        do {\n            fprintf(stderr, \"%c%c\", s->window[match++], s->window[start++]);\n        } while (--length != 0);\n        z_error(\"invalid match\");\n    }\n    if (z_verbose > 1) {\n        fprintf(stderr,\"\\\\[%d,%d]\", start-match, length);\n        do { putc(s->window[start++], stderr); } while (--length != 0);\n    }\n}\n#else\n#  define check_match(s, start, match, length)\n#endif /* ZLIB_DEBUG */\n\n/* ===========================================================================\n * Fill the window when the lookahead becomes insufficient.\n * Updates strstart and lookahead.\n *\n * IN assertion: lookahead < MIN_LOOKAHEAD\n * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD\n *    At least one byte has been read, or avail_in == 0; reads are\n *    performed for at least two bytes (required for the zip translate_eol\n *    option -- not supported here).\n */\nlocal void fill_window(s)\n    deflate_state *s;\n{\n    unsigned n;\n    unsigned more;    /* Amount of free space at the end of the window. */\n    uInt wsize = s->w_size;\n\n    Assert(s->lookahead < MIN_LOOKAHEAD, \"already enough lookahead\");\n\n    do {\n        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);\n\n        /* Deal with !@#$% 64K limit: */\n        if (sizeof(int) <= 2) {\n            if (more == 0 && s->strstart == 0 && s->lookahead == 0) {\n                more = wsize;\n\n            } else if (more == (unsigned)(-1)) {\n                /* Very unlikely, but possible on 16 bit machine if\n                 * strstart == 0 && lookahead == 1 (input done a byte at time)\n                 */\n                more--;\n            }\n        }\n\n        /* If the window is almost full and there is insufficient lookahead,\n         * move the upper half to the lower one to make room in the upper half.\n         */\n        if (s->strstart >= wsize+MAX_DIST(s)) {\n\n            zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more);\n            s->match_start -= wsize;\n            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */\n            s->block_start -= (long) wsize;\n            slide_hash(s);\n            more += wsize;\n        }\n        if (s->strm->avail_in == 0) break;\n\n        /* If there was no sliding:\n         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&\n         *    more == window_size - lookahead - strstart\n         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)\n         * => more >= window_size - 2*WSIZE + 2\n         * In the BIG_MEM or MMAP case (not yet supported),\n         *   window_size == input_size + MIN_LOOKAHEAD  &&\n         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.\n         * Otherwise, window_size == 2*WSIZE so more >= 2.\n         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.\n         */\n        Assert(more >= 2, \"more < 2\");\n\n        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);\n        s->lookahead += n;\n\n        /* Initialize the hash value now that we have some input: */\n        if (s->lookahead + s->insert >= MIN_MATCH) {\n            uInt str = s->strstart - s->insert;\n            s->ins_h = s->window[str];\n            UPDATE_HASH(s, s->ins_h, s->window[str + 1]);\n#if MIN_MATCH != 3\n            Call UPDATE_HASH() MIN_MATCH-3 more times\n#endif\n            while (s->insert) {\n                UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);\n#ifndef FASTEST\n                s->prev[str & s->w_mask] = s->head[s->ins_h];\n#endif\n                s->head[s->ins_h] = (Pos)str;\n                str++;\n                s->insert--;\n                if (s->lookahead + s->insert < MIN_MATCH)\n                    break;\n            }\n        }\n        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,\n         * but this is not important since only literal bytes will be emitted.\n         */\n\n    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);\n\n    /* If the WIN_INIT bytes after the end of the current data have never been\n     * written, then zero those bytes in order to avoid memory check reports of\n     * the use of uninitialized (or uninitialised as Julian writes) bytes by\n     * the longest match routines.  Update the high water mark for the next\n     * time through here.  WIN_INIT is set to MAX_MATCH since the longest match\n     * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.\n     */\n    if (s->high_water < s->window_size) {\n        ulg curr = s->strstart + (ulg)(s->lookahead);\n        ulg init;\n\n        if (s->high_water < curr) {\n            /* Previous high water mark below current data -- zero WIN_INIT\n             * bytes or up to end of window, whichever is less.\n             */\n            init = s->window_size - curr;\n            if (init > WIN_INIT)\n                init = WIN_INIT;\n            zmemzero(s->window + curr, (unsigned)init);\n            s->high_water = curr + init;\n        }\n        else if (s->high_water < (ulg)curr + WIN_INIT) {\n            /* High water mark at or above current data, but below current data\n             * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up\n             * to end of window, whichever is less.\n             */\n            init = (ulg)curr + WIN_INIT - s->high_water;\n            if (init > s->window_size - s->high_water)\n                init = s->window_size - s->high_water;\n            zmemzero(s->window + s->high_water, (unsigned)init);\n            s->high_water += init;\n        }\n    }\n\n    Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,\n           \"not enough room for search\");\n}\n\n/* ===========================================================================\n * Flush the current block, with given end-of-file flag.\n * IN assertion: strstart is set to the end of the current match.\n */\n#define FLUSH_BLOCK_ONLY(s, last) { \\\n   _tr_flush_block(s, (s->block_start >= 0L ? \\\n                   (charf *)&s->window[(unsigned)s->block_start] : \\\n                   (charf *)Z_NULL), \\\n                (ulg)((long)s->strstart - s->block_start), \\\n                (last)); \\\n   s->block_start = s->strstart; \\\n   flush_pending(s->strm); \\\n   Tracev((stderr,\"[FLUSH]\")); \\\n}\n\n/* Same but force premature exit if necessary. */\n#define FLUSH_BLOCK(s, last) { \\\n   FLUSH_BLOCK_ONLY(s, last); \\\n   if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \\\n}\n\n/* Maximum stored block length in deflate format (not including header). */\n#define MAX_STORED 65535\n\n/* Minimum of a and b. */\n#define MIN(a, b) ((a) > (b) ? (b) : (a))\n\n/* ===========================================================================\n * Copy without compression as much as possible from the input stream, return\n * the current block state.\n *\n * In case deflateParams() is used to later switch to a non-zero compression\n * level, s->matches (otherwise unused when storing) keeps track of the number\n * of hash table slides to perform. If s->matches is 1, then one hash table\n * slide will be done when switching. If s->matches is 2, the maximum value\n * allowed here, then the hash table will be cleared, since two or more slides\n * is the same as a clear.\n *\n * deflate_stored() is written to minimize the number of times an input byte is\n * copied. It is most efficient with large input and output buffers, which\n * maximizes the opportunites to have a single copy from next_in to next_out.\n */\nlocal block_state deflate_stored(s, flush)\n    deflate_state *s;\n    int flush;\n{\n    /* Smallest worthy block size when not flushing or finishing. By default\n     * this is 32K. This can be as small as 507 bytes for memLevel == 1. For\n     * large input and output buffers, the stored block size will be larger.\n     */\n    unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size);\n\n    /* Copy as many min_block or larger stored blocks directly to next_out as\n     * possible. If flushing, copy the remaining available input to next_out as\n     * stored blocks, if there is enough space.\n     */\n    unsigned len, left, have, last = 0;\n    unsigned used = s->strm->avail_in;\n    do {\n        /* Set len to the maximum size block that we can copy directly with the\n         * available input data and output space. Set left to how much of that\n         * would be copied from what's left in the window.\n         */\n        len = MAX_STORED;       /* maximum deflate stored block length */\n        have = (s->bi_valid + 42) >> 3;         /* number of header bytes */\n        if (s->strm->avail_out < have)          /* need room for header */\n            break;\n            /* maximum stored block length that will fit in avail_out: */\n        have = s->strm->avail_out - have;\n        left = s->strstart - s->block_start;    /* bytes left in window */\n        if (len > (ulg)left + s->strm->avail_in)\n            len = left + s->strm->avail_in;     /* limit len to the input */\n        if (len > have)\n            len = have;                         /* limit len to the output */\n\n        /* If the stored block would be less than min_block in length, or if\n         * unable to copy all of the available input when flushing, then try\n         * copying to the window and the pending buffer instead. Also don't\n         * write an empty block when flushing -- deflate() does that.\n         */\n        if (len < min_block && ((len == 0 && flush != Z_FINISH) ||\n                                flush == Z_NO_FLUSH ||\n                                len != left + s->strm->avail_in))\n            break;\n\n        /* Make a dummy stored block in pending to get the header bytes,\n         * including any pending bits. This also updates the debugging counts.\n         */\n        last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0;\n        _tr_stored_block(s, (char *)0, 0L, last);\n\n        /* Replace the lengths in the dummy stored block with len. */\n        s->pending_buf[s->pending - 4] = len;\n        s->pending_buf[s->pending - 3] = len >> 8;\n        s->pending_buf[s->pending - 2] = ~len;\n        s->pending_buf[s->pending - 1] = ~len >> 8;\n\n        /* Write the stored block header bytes. */\n        flush_pending(s->strm);\n\n#ifdef ZLIB_DEBUG\n        /* Update debugging counts for the data about to be copied. */\n        s->compressed_len += len << 3;\n        s->bits_sent += len << 3;\n#endif\n\n        /* Copy uncompressed bytes from the window to next_out. */\n        if (left) {\n            if (left > len)\n                left = len;\n            zmemcpy(s->strm->next_out, s->window + s->block_start, left);\n            s->strm->next_out += left;\n            s->strm->avail_out -= left;\n            s->strm->total_out += left;\n            s->block_start += left;\n            len -= left;\n        }\n\n        /* Copy uncompressed bytes directly from next_in to next_out, updating\n         * the check value.\n         */\n        if (len) {\n            read_buf(s->strm, s->strm->next_out, len);\n            s->strm->next_out += len;\n            s->strm->avail_out -= len;\n            s->strm->total_out += len;\n        }\n    } while (last == 0);\n\n    /* Update the sliding window with the last s->w_size bytes of the copied\n     * data, or append all of the copied data to the existing window if less\n     * than s->w_size bytes were copied. Also update the number of bytes to\n     * insert in the hash tables, in the event that deflateParams() switches to\n     * a non-zero compression level.\n     */\n    used -= s->strm->avail_in;      /* number of input bytes directly copied */\n    if (used) {\n        /* If any input was used, then no unused input remains in the window,\n         * therefore s->block_start == s->strstart.\n         */\n        if (used >= s->w_size) {    /* supplant the previous history */\n            s->matches = 2;         /* clear hash */\n            zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size);\n            s->strstart = s->w_size;\n        }\n        else {\n            if (s->window_size - s->strstart <= used) {\n                /* Slide the window down. */\n                s->strstart -= s->w_size;\n                zmemcpy(s->window, s->window + s->w_size, s->strstart);\n                if (s->matches < 2)\n                    s->matches++;   /* add a pending slide_hash() */\n            }\n            zmemcpy(s->window + s->strstart, s->strm->next_in - used, used);\n            s->strstart += used;\n        }\n        s->block_start = s->strstart;\n        s->insert += MIN(used, s->w_size - s->insert);\n    }\n    if (s->high_water < s->strstart)\n        s->high_water = s->strstart;\n\n    /* If the last block was written to next_out, then done. */\n    if (last)\n        return finish_done;\n\n    /* If flushing and all input has been consumed, then done. */\n    if (flush != Z_NO_FLUSH && flush != Z_FINISH &&\n        s->strm->avail_in == 0 && (long)s->strstart == s->block_start)\n        return block_done;\n\n    /* Fill the window with any remaining input. */\n    have = s->window_size - s->strstart - 1;\n    if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) {\n        /* Slide the window down. */\n        s->block_start -= s->w_size;\n        s->strstart -= s->w_size;\n        zmemcpy(s->window, s->window + s->w_size, s->strstart);\n        if (s->matches < 2)\n            s->matches++;           /* add a pending slide_hash() */\n        have += s->w_size;          /* more space now */\n    }\n    if (have > s->strm->avail_in)\n        have = s->strm->avail_in;\n    if (have) {\n        read_buf(s->strm, s->window + s->strstart, have);\n        s->strstart += have;\n    }\n    if (s->high_water < s->strstart)\n        s->high_water = s->strstart;\n\n    /* There was not enough avail_out to write a complete worthy or flushed\n     * stored block to next_out. Write a stored block to pending instead, if we\n     * have enough input for a worthy block, or if flushing and there is enough\n     * room for the remaining input as a stored block in the pending buffer.\n     */\n    have = (s->bi_valid + 42) >> 3;         /* number of header bytes */\n        /* maximum stored block length that will fit in pending: */\n    have = MIN(s->pending_buf_size - have, MAX_STORED);\n    min_block = MIN(have, s->w_size);\n    left = s->strstart - s->block_start;\n    if (left >= min_block ||\n        ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH &&\n         s->strm->avail_in == 0 && left <= have)) {\n        len = MIN(left, have);\n        last = flush == Z_FINISH && s->strm->avail_in == 0 &&\n               len == left ? 1 : 0;\n        _tr_stored_block(s, (charf *)s->window + s->block_start, len, last);\n        s->block_start += len;\n        flush_pending(s->strm);\n    }\n\n    /* We've done all we can with the available input and output. */\n    return last ? finish_started : need_more;\n}\n\n/* ===========================================================================\n * Compress as much as possible from the input stream, return the current\n * block state.\n * This function does not perform lazy evaluation of matches and inserts\n * new strings in the dictionary only for unmatched strings or for short\n * matches. It is used only for the fast compression options.\n */\nlocal block_state deflate_fast(s, flush)\n    deflate_state *s;\n    int flush;\n{\n    IPos hash_head;       /* head of the hash chain */\n    int bflush;           /* set if current block must be flushed */\n\n    for (;;) {\n        /* Make sure that we always have enough lookahead, except\n         * at the end of the input file. We need MAX_MATCH bytes\n         * for the next match, plus MIN_MATCH bytes to insert the\n         * string following the next match.\n         */\n        if (s->lookahead < MIN_LOOKAHEAD) {\n            fill_window(s);\n            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {\n                return need_more;\n            }\n            if (s->lookahead == 0) break; /* flush the current block */\n        }\n\n        /* Insert the string window[strstart .. strstart+2] in the\n         * dictionary, and set hash_head to the head of the hash chain:\n         */\n        hash_head = NIL;\n        if (s->lookahead >= MIN_MATCH) {\n            INSERT_STRING(s, s->strstart, hash_head);\n        }\n\n        /* Find the longest match, discarding those <= prev_length.\n         * At this point we have always match_length < MIN_MATCH\n         */\n        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {\n            /* To simplify the code, we prevent matches with the string\n             * of window index 0 (in particular we have to avoid a match\n             * of the string with itself at the start of the input file).\n             */\n            s->match_length = longest_match (s, hash_head);\n            /* longest_match() sets match_start */\n        }\n        if (s->match_length >= MIN_MATCH) {\n            check_match(s, s->strstart, s->match_start, s->match_length);\n\n            _tr_tally_dist(s, s->strstart - s->match_start,\n                           s->match_length - MIN_MATCH, bflush);\n\n            s->lookahead -= s->match_length;\n\n            /* Insert new strings in the hash table only if the match length\n             * is not too large. This saves time but degrades compression.\n             */\n#ifndef FASTEST\n            if (s->match_length <= s->max_insert_length &&\n                s->lookahead >= MIN_MATCH) {\n                s->match_length--; /* string at strstart already in table */\n                do {\n                    s->strstart++;\n                    INSERT_STRING(s, s->strstart, hash_head);\n                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are\n                     * always MIN_MATCH bytes ahead.\n                     */\n                } while (--s->match_length != 0);\n                s->strstart++;\n            } else\n#endif\n            {\n                s->strstart += s->match_length;\n                s->match_length = 0;\n                s->ins_h = s->window[s->strstart];\n                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);\n#if MIN_MATCH != 3\n                Call UPDATE_HASH() MIN_MATCH-3 more times\n#endif\n                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not\n                 * matter since it will be recomputed at next deflate call.\n                 */\n            }\n        } else {\n            /* No match, output a literal byte */\n            Tracevv((stderr,\"%c\", s->window[s->strstart]));\n            _tr_tally_lit (s, s->window[s->strstart], bflush);\n            s->lookahead--;\n            s->strstart++;\n        }\n        if (bflush) FLUSH_BLOCK(s, 0);\n    }\n    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;\n    if (flush == Z_FINISH) {\n        FLUSH_BLOCK(s, 1);\n        return finish_done;\n    }\n    if (s->last_lit)\n        FLUSH_BLOCK(s, 0);\n    return block_done;\n}\n\n#ifndef FASTEST\n/* ===========================================================================\n * Same as above, but achieves better compression. We use a lazy\n * evaluation for matches: a match is finally adopted only if there is\n * no better match at the next window position.\n */\nlocal block_state deflate_slow(s, flush)\n    deflate_state *s;\n    int flush;\n{\n    IPos hash_head;          /* head of hash chain */\n    int bflush;              /* set if current block must be flushed */\n\n    /* Process the input block. */\n    for (;;) {\n        /* Make sure that we always have enough lookahead, except\n         * at the end of the input file. We need MAX_MATCH bytes\n         * for the next match, plus MIN_MATCH bytes to insert the\n         * string following the next match.\n         */\n        if (s->lookahead < MIN_LOOKAHEAD) {\n            fill_window(s);\n            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {\n                return need_more;\n            }\n            if (s->lookahead == 0) break; /* flush the current block */\n        }\n\n        /* Insert the string window[strstart .. strstart+2] in the\n         * dictionary, and set hash_head to the head of the hash chain:\n         */\n        hash_head = NIL;\n        if (s->lookahead >= MIN_MATCH) {\n            INSERT_STRING(s, s->strstart, hash_head);\n        }\n\n        /* Find the longest match, discarding those <= prev_length.\n         */\n        s->prev_length = s->match_length, s->prev_match = s->match_start;\n        s->match_length = MIN_MATCH-1;\n\n        if (hash_head != NIL && s->prev_length < s->max_lazy_match &&\n            s->strstart - hash_head <= MAX_DIST(s)) {\n            /* To simplify the code, we prevent matches with the string\n             * of window index 0 (in particular we have to avoid a match\n             * of the string with itself at the start of the input file).\n             */\n            s->match_length = longest_match (s, hash_head);\n            /* longest_match() sets match_start */\n\n            if (s->match_length <= 5 && (s->strategy == Z_FILTERED\n#if TOO_FAR <= 32767\n                || (s->match_length == MIN_MATCH &&\n                    s->strstart - s->match_start > TOO_FAR)\n#endif\n                )) {\n\n                /* If prev_match is also MIN_MATCH, match_start is garbage\n                 * but we will ignore the current match anyway.\n                 */\n                s->match_length = MIN_MATCH-1;\n            }\n        }\n        /* If there was a match at the previous step and the current\n         * match is not better, output the previous match:\n         */\n        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {\n            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;\n            /* Do not insert strings in hash table beyond this. */\n\n            check_match(s, s->strstart-1, s->prev_match, s->prev_length);\n\n            _tr_tally_dist(s, s->strstart -1 - s->prev_match,\n                           s->prev_length - MIN_MATCH, bflush);\n\n            /* Insert in hash table all strings up to the end of the match.\n             * strstart-1 and strstart are already inserted. If there is not\n             * enough lookahead, the last two strings are not inserted in\n             * the hash table.\n             */\n            s->lookahead -= s->prev_length-1;\n            s->prev_length -= 2;\n            do {\n                if (++s->strstart <= max_insert) {\n                    INSERT_STRING(s, s->strstart, hash_head);\n                }\n            } while (--s->prev_length != 0);\n            s->match_available = 0;\n            s->match_length = MIN_MATCH-1;\n            s->strstart++;\n\n            if (bflush) FLUSH_BLOCK(s, 0);\n\n        } else if (s->match_available) {\n            /* If there was no match at the previous position, output a\n             * single literal. If there was a match but the current match\n             * is longer, truncate the previous match to a single literal.\n             */\n            Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n            _tr_tally_lit(s, s->window[s->strstart-1], bflush);\n            if (bflush) {\n                FLUSH_BLOCK_ONLY(s, 0);\n            }\n            s->strstart++;\n            s->lookahead--;\n            if (s->strm->avail_out == 0) return need_more;\n        } else {\n            /* There is no previous match to compare with, wait for\n             * the next step to decide.\n             */\n            s->match_available = 1;\n            s->strstart++;\n            s->lookahead--;\n        }\n    }\n    Assert (flush != Z_NO_FLUSH, \"no flush?\");\n    if (s->match_available) {\n        Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n        _tr_tally_lit(s, s->window[s->strstart-1], bflush);\n        s->match_available = 0;\n    }\n    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;\n    if (flush == Z_FINISH) {\n        FLUSH_BLOCK(s, 1);\n        return finish_done;\n    }\n    if (s->last_lit)\n        FLUSH_BLOCK(s, 0);\n    return block_done;\n}\n#endif /* FASTEST */\n\n/* ===========================================================================\n * For Z_RLE, simply look for runs of bytes, generate matches only of distance\n * one.  Do not maintain a hash table.  (It will be regenerated if this run of\n * deflate switches away from Z_RLE.)\n */\nlocal block_state deflate_rle(s, flush)\n    deflate_state *s;\n    int flush;\n{\n    int bflush;             /* set if current block must be flushed */\n    uInt prev;              /* byte at distance one to match */\n    Bytef *scan, *strend;   /* scan goes up to strend for length of run */\n\n    for (;;) {\n        /* Make sure that we always have enough lookahead, except\n         * at the end of the input file. We need MAX_MATCH bytes\n         * for the longest run, plus one for the unrolled loop.\n         */\n        if (s->lookahead <= MAX_MATCH) {\n            fill_window(s);\n            if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {\n                return need_more;\n            }\n            if (s->lookahead == 0) break; /* flush the current block */\n        }\n\n        /* See how many times the previous byte repeats */\n        s->match_length = 0;\n        if (s->lookahead >= MIN_MATCH && s->strstart > 0) {\n            scan = s->window + s->strstart - 1;\n            prev = *scan;\n            if (prev == *++scan && prev == *++scan && prev == *++scan) {\n                strend = s->window + s->strstart + MAX_MATCH;\n                do {\n                } while (prev == *++scan && prev == *++scan &&\n                         prev == *++scan && prev == *++scan &&\n                         prev == *++scan && prev == *++scan &&\n                         prev == *++scan && prev == *++scan &&\n                         scan < strend);\n                s->match_length = MAX_MATCH - (uInt)(strend - scan);\n                if (s->match_length > s->lookahead)\n                    s->match_length = s->lookahead;\n            }\n            Assert(scan <= s->window+(uInt)(s->window_size-1), \"wild scan\");\n        }\n\n        /* Emit match if have run of MIN_MATCH or longer, else emit literal */\n        if (s->match_length >= MIN_MATCH) {\n            check_match(s, s->strstart, s->strstart - 1, s->match_length);\n\n            _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);\n\n            s->lookahead -= s->match_length;\n            s->strstart += s->match_length;\n            s->match_length = 0;\n        } else {\n            /* No match, output a literal byte */\n            Tracevv((stderr,\"%c\", s->window[s->strstart]));\n            _tr_tally_lit (s, s->window[s->strstart], bflush);\n            s->lookahead--;\n            s->strstart++;\n        }\n        if (bflush) FLUSH_BLOCK(s, 0);\n    }\n    s->insert = 0;\n    if (flush == Z_FINISH) {\n        FLUSH_BLOCK(s, 1);\n        return finish_done;\n    }\n    if (s->last_lit)\n        FLUSH_BLOCK(s, 0);\n    return block_done;\n}\n\n/* ===========================================================================\n * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.\n * (It will be regenerated if this run of deflate switches away from Huffman.)\n */\nlocal block_state deflate_huff(s, flush)\n    deflate_state *s;\n    int flush;\n{\n    int bflush;             /* set if current block must be flushed */\n\n    for (;;) {\n        /* Make sure that we have a literal to write. */\n        if (s->lookahead == 0) {\n            fill_window(s);\n            if (s->lookahead == 0) {\n                if (flush == Z_NO_FLUSH)\n                    return need_more;\n                break;      /* flush the current block */\n            }\n        }\n\n        /* Output a literal byte */\n        s->match_length = 0;\n        Tracevv((stderr,\"%c\", s->window[s->strstart]));\n        _tr_tally_lit (s, s->window[s->strstart], bflush);\n        s->lookahead--;\n        s->strstart++;\n        if (bflush) FLUSH_BLOCK(s, 0);\n    }\n    s->insert = 0;\n    if (flush == Z_FINISH) {\n        FLUSH_BLOCK(s, 1);\n        return finish_done;\n    }\n    if (s->last_lit)\n        FLUSH_BLOCK(s, 0);\n    return block_done;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/deflate.h",
    "content": "/* deflate.h -- internal compression state\n * Copyright (C) 1995-2016 Jean-loup Gailly\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* WARNING: this file should *not* be used by applications. It is\n   part of the implementation of the compression library and is\n   subject to change. Applications should only use zlib.h.\n */\n\n/* @(#) $Id$ */\n\n#ifndef DEFLATE_H\n#define DEFLATE_H\n\n#include \"zutil.h\"\n\n/* define NO_GZIP when compiling if you want to disable gzip header and\n   trailer creation by deflate().  NO_GZIP would be used to avoid linking in\n   the crc code when it is not needed.  For shared libraries, gzip encoding\n   should be left enabled. */\n#ifndef NO_GZIP\n#  define GZIP\n#endif\n\n/* ===========================================================================\n * Internal compression state.\n */\n\n#define LENGTH_CODES 29\n/* number of length codes, not counting the special END_BLOCK code */\n\n#define LITERALS  256\n/* number of literal bytes 0..255 */\n\n#define L_CODES (LITERALS+1+LENGTH_CODES)\n/* number of Literal or Length codes, including the END_BLOCK code */\n\n#define D_CODES   30\n/* number of distance codes */\n\n#define BL_CODES  19\n/* number of codes used to transfer the bit lengths */\n\n#define HEAP_SIZE (2*L_CODES+1)\n/* maximum heap size */\n\n#define MAX_BITS 15\n/* All codes must not exceed MAX_BITS bits */\n\n#define Buf_size 16\n/* size of bit buffer in bi_buf */\n\n#define INIT_STATE    42    /* zlib header -> BUSY_STATE */\n#ifdef GZIP\n#  define GZIP_STATE  57    /* gzip header -> BUSY_STATE | EXTRA_STATE */\n#endif\n#define EXTRA_STATE   69    /* gzip extra block -> NAME_STATE */\n#define NAME_STATE    73    /* gzip file name -> COMMENT_STATE */\n#define COMMENT_STATE 91    /* gzip comment -> HCRC_STATE */\n#define HCRC_STATE   103    /* gzip header CRC -> BUSY_STATE */\n#define BUSY_STATE   113    /* deflate -> FINISH_STATE */\n#define FINISH_STATE 666    /* stream complete */\n/* Stream status */\n\n\n/* Data structure describing a single value and its code string. */\ntypedef struct ct_data_s {\n    union {\n        ush  freq;       /* frequency count */\n        ush  code;       /* bit string */\n    } fc;\n    union {\n        ush  dad;        /* father node in Huffman tree */\n        ush  len;        /* length of bit string */\n    } dl;\n} FAR ct_data;\n\n#define Freq fc.freq\n#define Code fc.code\n#define Dad  dl.dad\n#define Len  dl.len\n\ntypedef struct static_tree_desc_s  static_tree_desc;\n\ntypedef struct tree_desc_s {\n    ct_data *dyn_tree;           /* the dynamic tree */\n    int     max_code;            /* largest code with non zero frequency */\n    const static_tree_desc *stat_desc;  /* the corresponding static tree */\n} FAR tree_desc;\n\ntypedef ush Pos;\ntypedef Pos FAR Posf;\ntypedef unsigned IPos;\n\n/* A Pos is an index in the character window. We use short instead of int to\n * save space in the various tables. IPos is used only for parameter passing.\n */\n\ntypedef struct internal_state {\n    z_streamp strm;      /* pointer back to this zlib stream */\n    int   status;        /* as the name implies */\n    Bytef *pending_buf;  /* output still pending */\n    ulg   pending_buf_size; /* size of pending_buf */\n    Bytef *pending_out;  /* next pending byte to output to the stream */\n    ulg   pending;       /* nb of bytes in the pending buffer */\n    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */\n    gz_headerp  gzhead;  /* gzip header information to write */\n    ulg   gzindex;       /* where in extra, name, or comment */\n    Byte  method;        /* can only be DEFLATED */\n    int   last_flush;    /* value of flush param for previous deflate call */\n\n                /* used by deflate.c: */\n\n    uInt  w_size;        /* LZ77 window size (32K by default) */\n    uInt  w_bits;        /* log2(w_size)  (8..16) */\n    uInt  w_mask;        /* w_size - 1 */\n\n    Bytef *window;\n    /* Sliding window. Input bytes are read into the second half of the window,\n     * and move to the first half later to keep a dictionary of at least wSize\n     * bytes. With this organization, matches are limited to a distance of\n     * wSize-MAX_MATCH bytes, but this ensures that IO is always\n     * performed with a length multiple of the block size. Also, it limits\n     * the window size to 64K, which is quite useful on MSDOS.\n     * To do: use the user input buffer as sliding window.\n     */\n\n    ulg window_size;\n    /* Actual size of window: 2*wSize, except when the user input buffer\n     * is directly used as sliding window.\n     */\n\n    Posf *prev;\n    /* Link to older string with same hash index. To limit the size of this\n     * array to 64K, this link is maintained only for the last 32K strings.\n     * An index in this array is thus a window index modulo 32K.\n     */\n\n    Posf *head; /* Heads of the hash chains or NIL. */\n\n    uInt  ins_h;          /* hash index of string to be inserted */\n    uInt  hash_size;      /* number of elements in hash table */\n    uInt  hash_bits;      /* log2(hash_size) */\n    uInt  hash_mask;      /* hash_size-1 */\n\n    uInt  hash_shift;\n    /* Number of bits by which ins_h must be shifted at each input\n     * step. It must be such that after MIN_MATCH steps, the oldest\n     * byte no longer takes part in the hash key, that is:\n     *   hash_shift * MIN_MATCH >= hash_bits\n     */\n\n    long block_start;\n    /* Window position at the beginning of the current output block. Gets\n     * negative when the window is moved backwards.\n     */\n\n    uInt match_length;           /* length of best match */\n    IPos prev_match;             /* previous match */\n    int match_available;         /* set if previous match exists */\n    uInt strstart;               /* start of string to insert */\n    uInt match_start;            /* start of matching string */\n    uInt lookahead;              /* number of valid bytes ahead in window */\n\n    uInt prev_length;\n    /* Length of the best match at previous step. Matches not greater than this\n     * are discarded. This is used in the lazy match evaluation.\n     */\n\n    uInt max_chain_length;\n    /* To speed up deflation, hash chains are never searched beyond this\n     * length.  A higher limit improves compression ratio but degrades the\n     * speed.\n     */\n\n    uInt max_lazy_match;\n    /* Attempt to find a better match only when the current match is strictly\n     * smaller than this value. This mechanism is used only for compression\n     * levels >= 4.\n     */\n#   define max_insert_length  max_lazy_match\n    /* Insert new strings in the hash table only if the match length is not\n     * greater than this length. This saves time but degrades compression.\n     * max_insert_length is used only for compression levels <= 3.\n     */\n\n    int level;    /* compression level (1..9) */\n    int strategy; /* favor or force Huffman coding*/\n\n    uInt good_match;\n    /* Use a faster search when the previous match is longer than this */\n\n    int nice_match; /* Stop searching when current match exceeds this */\n\n                /* used by trees.c: */\n    /* Didn't use ct_data typedef below to suppress compiler warning */\n    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */\n    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */\n    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */\n\n    struct tree_desc_s l_desc;               /* desc. for literal tree */\n    struct tree_desc_s d_desc;               /* desc. for distance tree */\n    struct tree_desc_s bl_desc;              /* desc. for bit length tree */\n\n    ush bl_count[MAX_BITS+1];\n    /* number of codes at each bit length for an optimal tree */\n\n    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */\n    int heap_len;               /* number of elements in the heap */\n    int heap_max;               /* element of largest frequency */\n    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.\n     * The same heap array is used to build all trees.\n     */\n\n    uch depth[2*L_CODES+1];\n    /* Depth of each subtree used as tie breaker for trees of equal frequency\n     */\n\n    uchf *l_buf;          /* buffer for literals or lengths */\n\n    uInt  lit_bufsize;\n    /* Size of match buffer for literals/lengths.  There are 4 reasons for\n     * limiting lit_bufsize to 64K:\n     *   - frequencies can be kept in 16 bit counters\n     *   - if compression is not successful for the first block, all input\n     *     data is still in the window so we can still emit a stored block even\n     *     when input comes from standard input.  (This can also be done for\n     *     all blocks if lit_bufsize is not greater than 32K.)\n     *   - if compression is not successful for a file smaller than 64K, we can\n     *     even emit a stored file instead of a stored block (saving 5 bytes).\n     *     This is applicable only for zip (not gzip or zlib).\n     *   - creating new Huffman trees less frequently may not provide fast\n     *     adaptation to changes in the input data statistics. (Take for\n     *     example a binary file with poorly compressible code followed by\n     *     a highly compressible string table.) Smaller buffer sizes give\n     *     fast adaptation but have of course the overhead of transmitting\n     *     trees more frequently.\n     *   - I can't count above 4\n     */\n\n    uInt last_lit;      /* running index in l_buf */\n\n    ushf *d_buf;\n    /* Buffer for distances. To simplify the code, d_buf and l_buf have\n     * the same number of elements. To use different lengths, an extra flag\n     * array would be necessary.\n     */\n\n    ulg opt_len;        /* bit length of current block with optimal trees */\n    ulg static_len;     /* bit length of current block with static trees */\n    uInt matches;       /* number of string matches in current block */\n    uInt insert;        /* bytes at end of window left to insert */\n\n#ifdef ZLIB_DEBUG\n    ulg compressed_len; /* total bit length of compressed file mod 2^32 */\n    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */\n#endif\n\n    ush bi_buf;\n    /* Output buffer. bits are inserted starting at the bottom (least\n     * significant bits).\n     */\n    int bi_valid;\n    /* Number of valid bits in bi_buf.  All bits above the last valid bit\n     * are always zero.\n     */\n\n    ulg high_water;\n    /* High water mark offset in window for initialized bytes -- bytes above\n     * this are set to zero in order to avoid memory check warnings when\n     * longest match routines access bytes past the input.  This is then\n     * updated to the new high water mark.\n     */\n\n} FAR deflate_state;\n\n/* Output a byte on the stream.\n * IN assertion: there is enough room in pending_buf.\n */\n#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);}\n\n\n#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)\n/* Minimum amount of lookahead, except at the end of the input file.\n * See deflate.c for comments about the MIN_MATCH+1.\n */\n\n#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)\n/* In order to simplify the code, particularly on 16 bit machines, match\n * distances are limited to MAX_DIST instead of WSIZE.\n */\n\n#define WIN_INIT MAX_MATCH\n/* Number of bytes after end of data in window to initialize in order to avoid\n   memory checker errors from longest match routines */\n\n        /* in trees.c */\nvoid ZLIB_INTERNAL _tr_init OF((deflate_state *s));\nint ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));\nvoid ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,\n                        ulg stored_len, int last));\nvoid ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));\nvoid ZLIB_INTERNAL _tr_align OF((deflate_state *s));\nvoid ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,\n                        ulg stored_len, int last));\n\n#define d_code(dist) \\\n   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])\n/* Mapping from a distance to a distance code. dist is the distance - 1 and\n * must not have side effects. _dist_code[256] and _dist_code[257] are never\n * used.\n */\n\n#ifndef ZLIB_DEBUG\n/* Inline versions of _tr_tally for speed: */\n\n#if defined(GEN_TREES_H) || !defined(STDC)\n  extern uch ZLIB_INTERNAL _length_code[];\n  extern uch ZLIB_INTERNAL _dist_code[];\n#else\n  extern const uch ZLIB_INTERNAL _length_code[];\n  extern const uch ZLIB_INTERNAL _dist_code[];\n#endif\n\n# define _tr_tally_lit(s, c, flush) \\\n  { uch cc = (c); \\\n    s->d_buf[s->last_lit] = 0; \\\n    s->l_buf[s->last_lit++] = cc; \\\n    s->dyn_ltree[cc].Freq++; \\\n    flush = (s->last_lit == s->lit_bufsize-1); \\\n   }\n# define _tr_tally_dist(s, distance, length, flush) \\\n  { uch len = (uch)(length); \\\n    ush dist = (ush)(distance); \\\n    s->d_buf[s->last_lit] = dist; \\\n    s->l_buf[s->last_lit++] = len; \\\n    dist--; \\\n    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \\\n    s->dyn_dtree[d_code(dist)].Freq++; \\\n    flush = (s->last_lit == s->lit_bufsize-1); \\\n  }\n#else\n# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)\n# define _tr_tally_dist(s, distance, length, flush) \\\n              flush = _tr_tally(s, distance, length)\n#endif\n\n#endif /* DEFLATE_H */\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/gzclose.c",
    "content": "/* gzclose.c -- zlib gzclose() function\n * Copyright (C) 2004, 2010 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n#include \"gzguts.h\"\n\n/* gzclose() is in a separate file so that it is linked in only if it is used.\n   That way the other gzclose functions can be used instead to avoid linking in\n   unneeded compression or decompression routines. */\nint ZEXPORT gzclose(file)\n    gzFile file;\n{\n#ifndef NO_GZCOMPRESS\n    gz_statep state;\n\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n\n    return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file);\n#else\n    return gzclose_r(file);\n#endif\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/gzguts.h",
    "content": "/* gzguts.h -- zlib internal header definitions for gz* operations\n * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n#ifdef _MSC_VER\n#pragma  warning( disable: 4996 ) \n#endif\n\n#ifdef _LARGEFILE64_SOURCE\n#  ifndef _LARGEFILE_SOURCE\n#    define _LARGEFILE_SOURCE 1\n#  endif\n#  ifdef _FILE_OFFSET_BITS\n#    undef _FILE_OFFSET_BITS\n#  endif\n#endif\n\n#ifdef HAVE_HIDDEN\n#  define ZLIB_INTERNAL __attribute__((visibility (\"hidden\")))\n#else\n#  define ZLIB_INTERNAL\n#endif\n\n#include <stdio.h>\n#include \"zlib.h\"\n#ifdef STDC\n#  include <string.h>\n#  include <stdlib.h>\n#  include <limits.h>\n#endif\n\n#ifndef _POSIX_SOURCE\n#  define _POSIX_SOURCE\n#endif\n#include <fcntl.h>\n\n#ifdef _WIN32\n#  include <stddef.h>\n#endif\n\n#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)\n#  include <io.h>\n#endif\n\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define WIDECHAR\n#endif\n\n#ifdef WINAPI_FAMILY\n#  define open _open\n#  define read _read\n#  define write _write\n#  define close _close\n#endif\n\n#ifdef NO_DEFLATE       /* for compatibility with old definition */\n#  define NO_GZCOMPRESS\n#endif\n\n#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)\n#  ifndef HAVE_VSNPRINTF\n#    define HAVE_VSNPRINTF\n#  endif\n#endif\n\n#if defined(__CYGWIN__)\n#  ifndef HAVE_VSNPRINTF\n#    define HAVE_VSNPRINTF\n#  endif\n#endif\n\n#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410)\n#  ifndef HAVE_VSNPRINTF\n#    define HAVE_VSNPRINTF\n#  endif\n#endif\n\n#ifndef HAVE_VSNPRINTF\n#  ifdef MSDOS\n/* vsnprintf may exist on some MS-DOS compilers (DJGPP?),\n   but for now we just assume it doesn't. */\n#    define NO_vsnprintf\n#  endif\n#  ifdef __TURBOC__\n#    define NO_vsnprintf\n#  endif\n#  ifdef WIN32\n/* In Win32, vsnprintf is available as the \"non-ANSI\" _vsnprintf. */\n#    if !defined(vsnprintf) && !defined(NO_vsnprintf)\n#      if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )\n#         define vsnprintf _vsnprintf\n#      endif\n#    endif\n#  endif\n#  ifdef __SASC\n#    define NO_vsnprintf\n#  endif\n#  ifdef VMS\n#    define NO_vsnprintf\n#  endif\n#  ifdef __OS400__\n#    define NO_vsnprintf\n#  endif\n#  ifdef __MVS__\n#    define NO_vsnprintf\n#  endif\n#endif\n\n/* unlike snprintf (which is required in C99), _snprintf does not guarantee\n   null termination of the result -- however this is only used in gzlib.c where\n   the result is assured to fit in the space provided */\n#if defined(_MSC_VER) && _MSC_VER < 1900\n#  define snprintf _snprintf\n#endif\n\n#ifndef local\n#  define local static\n#endif\n/* since \"static\" is used to mean two completely different things in C, we\n   define \"local\" for the non-static meaning of \"static\", for readability\n   (compile with -Dlocal if your debugger can't find static symbols) */\n\n/* gz* functions always use library allocation functions */\n#ifndef STDC\n  extern voidp  malloc OF((uInt size));\n  extern void   free   OF((voidpf ptr));\n#endif\n\n/* get errno and strerror definition */\n#if defined UNDER_CE\n#  include <windows.h>\n#  define zstrerror() gz_strwinerror((DWORD)GetLastError())\n#else\n#  ifndef NO_STRERROR\n#    include <errno.h>\n#    define zstrerror() strerror(errno)\n#  else\n#    define zstrerror() \"stdio error (consult errno)\"\n#  endif\n#endif\n\n/* provide prototypes for these when building zlib without LFS */\n#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0\n    ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));\n    ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));\n    ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));\n    ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));\n#endif\n\n/* default memLevel */\n#if MAX_MEM_LEVEL >= 8\n#  define DEF_MEM_LEVEL 8\n#else\n#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL\n#endif\n\n/* default i/o buffer size -- double this for output when reading (this and\n   twice this must be able to fit in an unsigned type) */\n#define GZBUFSIZE 8192\n\n/* gzip modes, also provide a little integrity check on the passed structure */\n#define GZ_NONE 0\n#define GZ_READ 7247\n#define GZ_WRITE 31153\n#define GZ_APPEND 1     /* mode set to GZ_WRITE after the file is opened */\n\n/* values for gz_state how */\n#define LOOK 0      /* look for a gzip header */\n#define COPY 1      /* copy input directly */\n#define GZIP 2      /* decompress a gzip stream */\n\n/* internal gzip file state data structure */\ntypedef struct {\n        /* exposed contents for gzgetc() macro */\n    struct gzFile_s x;      /* \"x\" for exposed */\n                            /* x.have: number of bytes available at x.next */\n                            /* x.next: next output data to deliver or write */\n                            /* x.pos: current position in uncompressed data */\n        /* used for both reading and writing */\n    int mode;               /* see gzip modes above */\n    int fd;                 /* file descriptor */\n    char *path;             /* path or fd for error messages */\n    unsigned size;          /* buffer size, zero if not allocated yet */\n    unsigned want;          /* requested buffer size, default is GZBUFSIZE */\n    unsigned char *in;      /* input buffer (double-sized when writing) */\n    unsigned char *out;     /* output buffer (double-sized when reading) */\n    int direct;             /* 0 if processing gzip, 1 if transparent */\n        /* just for reading */\n    int how;                /* 0: get header, 1: copy, 2: decompress */\n    z_off64_t start;        /* where the gzip data started, for rewinding */\n    int eof;                /* true if end of input file reached */\n    int past;               /* true if read requested past end */\n        /* just for writing */\n    int level;              /* compression level */\n    int strategy;           /* compression strategy */\n        /* seek request */\n    z_off64_t skip;         /* amount to skip (already rewound if backwards) */\n    int seek;               /* true if seek request pending */\n        /* error information */\n    int err;                /* error code */\n    char *msg;              /* error message */\n        /* zlib inflate or deflate stream */\n    z_stream strm;          /* stream structure in-place (not a pointer) */\n} gz_state;\ntypedef gz_state FAR *gz_statep;\n\n/* shared functions */\nvoid ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));\n#if defined UNDER_CE\nchar ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));\n#endif\n\n/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t\n   value -- needed when comparing unsigned to z_off64_t, which is signed\n   (possible z_off64_t types off_t, off64_t, and long are all signed) */\n#ifdef INT_MAX\n#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)\n#else\nunsigned ZLIB_INTERNAL gz_intmax OF((void));\n#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())\n#endif\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/gzlib.c",
    "content": "/* gzlib.c -- zlib functions common to reading and writing gzip files\n * Copyright (C) 2004-2017 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n#include \"gzguts.h\"\n\n#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__)\n#  define LSEEK _lseeki64\n#else\n#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0\n#  define LSEEK lseek64\n#else\n#  define LSEEK lseek\n#endif\n#endif\n\n/* Local functions */\nlocal void gz_reset OF((gz_statep));\nlocal gzFile gz_open OF((const void *, int, const char *));\n\n#if defined UNDER_CE\n\n/* Map the Windows error number in ERROR to a locale-dependent error message\n   string and return a pointer to it.  Typically, the values for ERROR come\n   from GetLastError.\n\n   The string pointed to shall not be modified by the application, but may be\n   overwritten by a subsequent call to gz_strwinerror\n\n   The gz_strwinerror function does not change the current setting of\n   GetLastError. */\nchar ZLIB_INTERNAL *gz_strwinerror (error)\n     DWORD error;\n{\n    static char buf[1024];\n\n    wchar_t *msgbuf;\n    DWORD lasterr = GetLastError();\n    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM\n        | FORMAT_MESSAGE_ALLOCATE_BUFFER,\n        NULL,\n        error,\n        0, /* Default language */\n        (LPVOID)&msgbuf,\n        0,\n        NULL);\n    if (chars != 0) {\n        /* If there is an \\r\\n appended, zap it.  */\n        if (chars >= 2\n            && msgbuf[chars - 2] == '\\r' && msgbuf[chars - 1] == '\\n') {\n            chars -= 2;\n            msgbuf[chars] = 0;\n        }\n\n        if (chars > sizeof (buf) - 1) {\n            chars = sizeof (buf) - 1;\n            msgbuf[chars] = 0;\n        }\n\n        wcstombs(buf, msgbuf, chars + 1);\n        LocalFree(msgbuf);\n    }\n    else {\n        sprintf(buf, \"unknown win32 error (%ld)\", error);\n    }\n\n    SetLastError(lasterr);\n    return buf;\n}\n\n#endif /* UNDER_CE */\n\n/* Reset gzip file state */\nlocal void gz_reset(state)\n    gz_statep state;\n{\n    state->x.have = 0;              /* no output data available */\n    if (state->mode == GZ_READ) {   /* for reading ... */\n        state->eof = 0;             /* not at end of file */\n        state->past = 0;            /* have not read past end yet */\n        state->how = LOOK;          /* look for gzip header */\n    }\n    state->seek = 0;                /* no seek request pending */\n    gz_error(state, Z_OK, NULL);    /* clear error */\n    state->x.pos = 0;               /* no uncompressed data yet */\n    state->strm.avail_in = 0;       /* no input data yet */\n}\n\n/* Open a gzip file either by name or file descriptor. */\nlocal gzFile gz_open(path, fd, mode)\n    const void *path;\n    int fd;\n    const char *mode;\n{\n    gz_statep state;\n    z_size_t len;\n    int oflag;\n#ifdef O_CLOEXEC\n    int cloexec = 0;\n#endif\n#ifdef O_EXCL\n    int exclusive = 0;\n#endif\n\n    /* check input */\n    if (path == NULL)\n        return NULL;\n\n    /* allocate gzFile structure to return */\n    state = (gz_statep)malloc(sizeof(gz_state));\n    if (state == NULL)\n        return NULL;\n    state->size = 0;            /* no buffers allocated yet */\n    state->want = GZBUFSIZE;    /* requested buffer size */\n    state->msg = NULL;          /* no error message yet */\n\n    /* interpret mode */\n    state->mode = GZ_NONE;\n    state->level = Z_DEFAULT_COMPRESSION;\n    state->strategy = Z_DEFAULT_STRATEGY;\n    state->direct = 0;\n    while (*mode) {\n        if (*mode >= '0' && *mode <= '9')\n            state->level = *mode - '0';\n        else\n            switch (*mode) {\n            case 'r':\n                state->mode = GZ_READ;\n                break;\n#ifndef NO_GZCOMPRESS\n            case 'w':\n                state->mode = GZ_WRITE;\n                break;\n            case 'a':\n                state->mode = GZ_APPEND;\n                break;\n#endif\n            case '+':       /* can't read and write at the same time */\n                free(state);\n                return NULL;\n            case 'b':       /* ignore -- will request binary anyway */\n                break;\n#ifdef O_CLOEXEC\n            case 'e':\n                cloexec = 1;\n                break;\n#endif\n#ifdef O_EXCL\n            case 'x':\n                exclusive = 1;\n                break;\n#endif\n            case 'f':\n                state->strategy = Z_FILTERED;\n                break;\n            case 'h':\n                state->strategy = Z_HUFFMAN_ONLY;\n                break;\n            case 'R':\n                state->strategy = Z_RLE;\n                break;\n            case 'F':\n                state->strategy = Z_FIXED;\n                break;\n            case 'T':\n                state->direct = 1;\n                break;\n            default:        /* could consider as an error, but just ignore */\n                ;\n            }\n        mode++;\n    }\n\n    /* must provide an \"r\", \"w\", or \"a\" */\n    if (state->mode == GZ_NONE) {\n        free(state);\n        return NULL;\n    }\n\n    /* can't force transparent read */\n    if (state->mode == GZ_READ) {\n        if (state->direct) {\n            free(state);\n            return NULL;\n        }\n        state->direct = 1;      /* for empty file */\n    }\n\n    /* save the path name for error messages */\n#ifdef WIDECHAR\n    if (fd == -2) {\n        len = wcstombs(NULL, path, 0);\n        if (len == (z_size_t)-1)\n            len = 0;\n    }\n    else\n#endif\n        len = strlen((const char *)path);\n    state->path = (char *)malloc(len + 1);\n    if (state->path == NULL) {\n        free(state);\n        return NULL;\n    }\n#ifdef WIDECHAR\n    if (fd == -2)\n        if (len)\n            wcstombs(state->path, path, len + 1);\n        else\n            *(state->path) = 0;\n    else\n#endif\n#if !defined(NO_snprintf) && !defined(NO_vsnprintf)\n        (void)snprintf(state->path, len + 1, \"%s\", (const char *)path);\n#else\n        strcpy(state->path, path);\n#endif\n\n    /* compute the flags for open() */\n    oflag =\n#ifdef O_LARGEFILE\n        O_LARGEFILE |\n#endif\n#ifdef O_BINARY\n        O_BINARY |\n#endif\n#ifdef O_CLOEXEC\n        (cloexec ? O_CLOEXEC : 0) |\n#endif\n        (state->mode == GZ_READ ?\n         O_RDONLY :\n         (O_WRONLY | O_CREAT |\n#ifdef O_EXCL\n          (exclusive ? O_EXCL : 0) |\n#endif\n          (state->mode == GZ_WRITE ?\n           O_TRUNC :\n           O_APPEND)));\n\n    /* open the file with the appropriate flags (or just use fd) */\n    state->fd = fd > -1 ? fd : (\n#ifdef WIDECHAR\n        fd == -2 ? _wopen(path, oflag, 0666) :\n#endif\n        open((const char *)path, oflag, 0666));\n    if (state->fd == -1) {\n        free(state->path);\n        free(state);\n        return NULL;\n    }\n    if (state->mode == GZ_APPEND) {\n        LSEEK(state->fd, 0, SEEK_END);  /* so gzoffset() is correct */\n        state->mode = GZ_WRITE;         /* simplify later checks */\n    }\n\n    /* save the current position for rewinding (only if reading) */\n    if (state->mode == GZ_READ) {\n        state->start = LSEEK(state->fd, 0, SEEK_CUR);\n        if (state->start == -1) state->start = 0;\n    }\n\n    /* initialize stream */\n    gz_reset(state);\n\n    /* return stream */\n    return (gzFile)state;\n}\n\n/* -- see zlib.h -- */\ngzFile ZEXPORT gzopen(path, mode)\n    const char *path;\n    const char *mode;\n{\n    return gz_open(path, -1, mode);\n}\n\n/* -- see zlib.h -- */\ngzFile ZEXPORT gzopen64(path, mode)\n    const char *path;\n    const char *mode;\n{\n    return gz_open(path, -1, mode);\n}\n\n/* -- see zlib.h -- */\ngzFile ZEXPORT gzdopen(fd, mode)\n    int fd;\n    const char *mode;\n{\n    char *path;         /* identifier for error messages */\n    gzFile gz;\n\n    if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL)\n        return NULL;\n#if !defined(NO_snprintf) && !defined(NO_vsnprintf)\n    (void)snprintf(path, 7 + 3 * sizeof(int), \"<fd:%d>\", fd);\n#else\n    sprintf(path, \"<fd:%d>\", fd);   /* for debugging */\n#endif\n    gz = gz_open(path, fd, mode);\n    free(path);\n    return gz;\n}\n\n/* -- see zlib.h -- */\n#ifdef WIDECHAR\ngzFile ZEXPORT gzopen_w(path, mode)\n    const wchar_t *path;\n    const char *mode;\n{\n    return gz_open(path, -2, mode);\n}\n#endif\n\n/* -- see zlib.h -- */\nint ZEXPORT gzbuffer(file, size)\n    gzFile file;\n    unsigned size;\n{\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return -1;\n\n    /* make sure we haven't already allocated memory */\n    if (state->size != 0)\n        return -1;\n\n    /* check and set requested size */\n    if ((size << 1) < size)\n        return -1;              /* need to be able to double it */\n    if (size < 2)\n        size = 2;               /* need two bytes to check magic header */\n    state->want = size;\n    return 0;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzrewind(file)\n    gzFile file;\n{\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n\n    /* check that we're reading and that there's no error */\n    if (state->mode != GZ_READ ||\n            (state->err != Z_OK && state->err != Z_BUF_ERROR))\n        return -1;\n\n    /* back up and start over */\n    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)\n        return -1;\n    gz_reset(state);\n    return 0;\n}\n\n/* -- see zlib.h -- */\nz_off64_t ZEXPORT gzseek64(file, offset, whence)\n    gzFile file;\n    z_off64_t offset;\n    int whence;\n{\n    unsigned n;\n    z_off64_t ret;\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return -1;\n\n    /* check that there's no error */\n    if (state->err != Z_OK && state->err != Z_BUF_ERROR)\n        return -1;\n\n    /* can only seek from start or relative to current position */\n    if (whence != SEEK_SET && whence != SEEK_CUR)\n        return -1;\n\n    /* normalize offset to a SEEK_CUR specification */\n    if (whence == SEEK_SET)\n        offset -= state->x.pos;\n    else if (state->seek)\n        offset += state->skip;\n    state->seek = 0;\n\n    /* if within raw area while reading, just go there */\n    if (state->mode == GZ_READ && state->how == COPY &&\n            state->x.pos + offset >= 0) {\n        ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR);\n        if (ret == -1)\n            return -1;\n        state->x.have = 0;\n        state->eof = 0;\n        state->past = 0;\n        state->seek = 0;\n        gz_error(state, Z_OK, NULL);\n        state->strm.avail_in = 0;\n        state->x.pos += offset;\n        return state->x.pos;\n    }\n\n    /* calculate skip amount, rewinding if needed for back seek when reading */\n    if (offset < 0) {\n        if (state->mode != GZ_READ)         /* writing -- can't go backwards */\n            return -1;\n        offset += state->x.pos;\n        if (offset < 0)                     /* before start of file! */\n            return -1;\n        if (gzrewind(file) == -1)           /* rewind, then skip to offset */\n            return -1;\n    }\n\n    /* if reading, skip what's in output buffer (one less gzgetc() check) */\n    if (state->mode == GZ_READ) {\n        n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?\n            (unsigned)offset : state->x.have;\n        state->x.have -= n;\n        state->x.next += n;\n        state->x.pos += n;\n        offset -= n;\n    }\n\n    /* request skip (if not zero) */\n    if (offset) {\n        state->seek = 1;\n        state->skip = offset;\n    }\n    return state->x.pos + offset;\n}\n\n/* -- see zlib.h -- */\nz_off_t ZEXPORT gzseek(file, offset, whence)\n    gzFile file;\n    z_off_t offset;\n    int whence;\n{\n    z_off64_t ret;\n\n    ret = gzseek64(file, (z_off64_t)offset, whence);\n    return ret == (z_off_t)ret ? (z_off_t)ret : -1;\n}\n\n/* -- see zlib.h -- */\nz_off64_t ZEXPORT gztell64(file)\n    gzFile file;\n{\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return -1;\n\n    /* return position */\n    return state->x.pos + (state->seek ? state->skip : 0);\n}\n\n/* -- see zlib.h -- */\nz_off_t ZEXPORT gztell(file)\n    gzFile file;\n{\n    z_off64_t ret;\n\n    ret = gztell64(file);\n    return ret == (z_off_t)ret ? (z_off_t)ret : -1;\n}\n\n/* -- see zlib.h -- */\nz_off64_t ZEXPORT gzoffset64(file)\n    gzFile file;\n{\n    z_off64_t offset;\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return -1;\n\n    /* compute and return effective offset in file */\n    offset = LSEEK(state->fd, 0, SEEK_CUR);\n    if (offset == -1)\n        return -1;\n    if (state->mode == GZ_READ)             /* reading */\n        offset -= state->strm.avail_in;     /* don't count buffered input */\n    return offset;\n}\n\n/* -- see zlib.h -- */\nz_off_t ZEXPORT gzoffset(file)\n    gzFile file;\n{\n    z_off64_t ret;\n\n    ret = gzoffset64(file);\n    return ret == (z_off_t)ret ? (z_off_t)ret : -1;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzeof(file)\n    gzFile file;\n{\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return 0;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return 0;\n\n    /* return end-of-file state */\n    return state->mode == GZ_READ ? state->past : 0;\n}\n\n/* -- see zlib.h -- */\nconst char * ZEXPORT gzerror(file, errnum)\n    gzFile file;\n    int *errnum;\n{\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return NULL;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return NULL;\n\n    /* return error information */\n    if (errnum != NULL)\n        *errnum = state->err;\n    return state->err == Z_MEM_ERROR ? \"out of memory\" :\n                                       (state->msg == NULL ? \"\" : state->msg);\n}\n\n/* -- see zlib.h -- */\nvoid ZEXPORT gzclearerr(file)\n    gzFile file;\n{\n    gz_statep state;\n\n    /* get internal structure and check integrity */\n    if (file == NULL)\n        return;\n    state = (gz_statep)file;\n    if (state->mode != GZ_READ && state->mode != GZ_WRITE)\n        return;\n\n    /* clear error and end-of-file */\n    if (state->mode == GZ_READ) {\n        state->eof = 0;\n        state->past = 0;\n    }\n    gz_error(state, Z_OK, NULL);\n}\n\n/* Create an error message in allocated memory and set state->err and\n   state->msg accordingly.  Free any previous error message already there.  Do\n   not try to free or allocate space if the error is Z_MEM_ERROR (out of\n   memory).  Simply save the error message as a static string.  If there is an\n   allocation failure constructing the error message, then convert the error to\n   out of memory. */\nvoid ZLIB_INTERNAL gz_error(state, err, msg)\n    gz_statep state;\n    int err;\n    const char *msg;\n{\n    /* free previously allocated message and clear */\n    if (state->msg != NULL) {\n        if (state->err != Z_MEM_ERROR)\n            free(state->msg);\n        state->msg = NULL;\n    }\n\n    /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */\n    if (err != Z_OK && err != Z_BUF_ERROR)\n        state->x.have = 0;\n\n    /* set error code, and if no message, then done */\n    state->err = err;\n    if (msg == NULL)\n        return;\n\n    /* for an out of memory error, return literal string when requested */\n    if (err == Z_MEM_ERROR)\n        return;\n\n    /* construct error message with path */\n    if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) ==\n            NULL) {\n        state->err = Z_MEM_ERROR;\n        return;\n    }\n#if !defined(NO_snprintf) && !defined(NO_vsnprintf)\n    (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3,\n                   \"%s%s%s\", state->path, \": \", msg);\n#else\n    strcpy(state->msg, state->path);\n    strcat(state->msg, \": \");\n    strcat(state->msg, msg);\n#endif\n}\n\n#ifndef INT_MAX\n/* portably return maximum value for an int (when limits.h presumed not\n   available) -- we need to do this to cover cases where 2's complement not\n   used, since C standard permits 1's complement and sign-bit representations,\n   otherwise we could just use ((unsigned)-1) >> 1 */\nunsigned ZLIB_INTERNAL gz_intmax()\n{\n    unsigned p, q;\n\n    p = 1;\n    do {\n        q = p;\n        p <<= 1;\n        p++;\n    } while (p > q);\n    return q >> 1;\n}\n#endif\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/gzread.c",
    "content": "/* gzread.c -- zlib functions for reading gzip files\n * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n#include \"gzguts.h\"\n\n/* Local functions */\nlocal int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));\nlocal int gz_avail OF((gz_statep));\nlocal int gz_look OF((gz_statep));\nlocal int gz_decomp OF((gz_statep));\nlocal int gz_fetch OF((gz_statep));\nlocal int gz_skip OF((gz_statep, z_off64_t));\nlocal z_size_t gz_read OF((gz_statep, voidp, z_size_t));\n\n/* Use read() to load a buffer -- return -1 on error, otherwise 0.  Read from\n   state->fd, and update state->eof, state->err, and state->msg as appropriate.\n   This function needs to loop on read(), since read() is not guaranteed to\n   read the number of bytes requested, depending on the type of descriptor. */\nlocal int gz_load(state, buf, len, have)\n    gz_statep state;\n    unsigned char *buf;\n    unsigned len;\n    unsigned *have;\n{\n    int ret;\n    unsigned get, max = ((unsigned)-1 >> 2) + 1;\n\n    *have = 0;\n    do {\n        get = len - *have;\n        if (get > max)\n            get = max;\n        ret = read(state->fd, buf + *have, get);\n        if (ret <= 0)\n            break;\n        *have += (unsigned)ret;\n    } while (*have < len);\n    if (ret < 0) {\n        gz_error(state, Z_ERRNO, zstrerror());\n        return -1;\n    }\n    if (ret == 0)\n        state->eof = 1;\n    return 0;\n}\n\n/* Load up input buffer and set eof flag if last data loaded -- return -1 on\n   error, 0 otherwise.  Note that the eof flag is set when the end of the input\n   file is reached, even though there may be unused data in the buffer.  Once\n   that data has been used, no more attempts will be made to read the file.\n   If strm->avail_in != 0, then the current data is moved to the beginning of\n   the input buffer, and then the remainder of the buffer is loaded with the\n   available data from the input file. */\nlocal int gz_avail(state)\n    gz_statep state;\n{\n    unsigned got;\n    z_streamp strm = &(state->strm);\n\n    if (state->err != Z_OK && state->err != Z_BUF_ERROR)\n        return -1;\n    if (state->eof == 0) {\n        if (strm->avail_in) {       /* copy what's there to the start */\n            unsigned char *p = state->in;\n            unsigned const char *q = strm->next_in;\n            unsigned n = strm->avail_in;\n            do {\n                *p++ = *q++;\n            } while (--n);\n        }\n        if (gz_load(state, state->in + strm->avail_in,\n                    state->size - strm->avail_in, &got) == -1)\n            return -1;\n        strm->avail_in += got;\n        strm->next_in = state->in;\n    }\n    return 0;\n}\n\n/* Look for gzip header, set up for inflate or copy.  state->x.have must be 0.\n   If this is the first time in, allocate required memory.  state->how will be\n   left unchanged if there is no more input data available, will be set to COPY\n   if there is no gzip header and direct copying will be performed, or it will\n   be set to GZIP for decompression.  If direct copying, then leftover input\n   data from the input buffer will be copied to the output buffer.  In that\n   case, all further file reads will be directly to either the output buffer or\n   a user buffer.  If decompressing, the inflate state will be initialized.\n   gz_look() will return 0 on success or -1 on failure. */\nlocal int gz_look(state)\n    gz_statep state;\n{\n    z_streamp strm = &(state->strm);\n\n    /* allocate read buffers and inflate memory */\n    if (state->size == 0) {\n        /* allocate buffers */\n        state->in = (unsigned char *)malloc(state->want);\n        state->out = (unsigned char *)malloc(state->want << 1);\n        if (state->in == NULL || state->out == NULL) {\n            free(state->out);\n            free(state->in);\n            gz_error(state, Z_MEM_ERROR, \"out of memory\");\n            return -1;\n        }\n        state->size = state->want;\n\n        /* allocate inflate memory */\n        state->strm.zalloc = Z_NULL;\n        state->strm.zfree = Z_NULL;\n        state->strm.opaque = Z_NULL;\n        state->strm.avail_in = 0;\n        state->strm.next_in = Z_NULL;\n        if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) {    /* gunzip */\n            free(state->out);\n            free(state->in);\n            state->size = 0;\n            gz_error(state, Z_MEM_ERROR, \"out of memory\");\n            return -1;\n        }\n    }\n\n    /* get at least the magic bytes in the input buffer */\n    if (strm->avail_in < 2) {\n        if (gz_avail(state) == -1)\n            return -1;\n        if (strm->avail_in == 0)\n            return 0;\n    }\n\n    /* look for gzip magic bytes -- if there, do gzip decoding (note: there is\n       a logical dilemma here when considering the case of a partially written\n       gzip file, to wit, if a single 31 byte is written, then we cannot tell\n       whether this is a single-byte file, or just a partially written gzip\n       file -- for here we assume that if a gzip file is being written, then\n       the header will be written in a single operation, so that reading a\n       single byte is sufficient indication that it is not a gzip file) */\n    if (strm->avail_in > 1 &&\n            strm->next_in[0] == 31 && strm->next_in[1] == 139) {\n        inflateReset(strm);\n        state->how = GZIP;\n        state->direct = 0;\n        return 0;\n    }\n\n    /* no gzip header -- if we were decoding gzip before, then this is trailing\n       garbage.  Ignore the trailing garbage and finish. */\n    if (state->direct == 0) {\n        strm->avail_in = 0;\n        state->eof = 1;\n        state->x.have = 0;\n        return 0;\n    }\n\n    /* doing raw i/o, copy any leftover input to output -- this assumes that\n       the output buffer is larger than the input buffer, which also assures\n       space for gzungetc() */\n    state->x.next = state->out;\n    if (strm->avail_in) {\n        memcpy(state->x.next, strm->next_in, strm->avail_in);\n        state->x.have = strm->avail_in;\n        strm->avail_in = 0;\n    }\n    state->how = COPY;\n    state->direct = 1;\n    return 0;\n}\n\n/* Decompress from input to the provided next_out and avail_out in the state.\n   On return, state->x.have and state->x.next point to the just decompressed\n   data.  If the gzip stream completes, state->how is reset to LOOK to look for\n   the next gzip stream or raw data, once state->x.have is depleted.  Returns 0\n   on success, -1 on failure. */\nlocal int gz_decomp(state)\n    gz_statep state;\n{\n    int ret = Z_OK;\n    unsigned had;\n    z_streamp strm = &(state->strm);\n\n    /* fill output buffer up to end of deflate stream */\n    had = strm->avail_out;\n    do {\n        /* get more input for inflate() */\n        if (strm->avail_in == 0 && gz_avail(state) == -1)\n            return -1;\n        if (strm->avail_in == 0) {\n            gz_error(state, Z_BUF_ERROR, \"unexpected end of file\");\n            break;\n        }\n\n        /* decompress and handle errors */\n        ret = inflate(strm, Z_NO_FLUSH);\n        if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {\n            gz_error(state, Z_STREAM_ERROR,\n                     \"internal error: inflate stream corrupt\");\n            return -1;\n        }\n        if (ret == Z_MEM_ERROR) {\n            gz_error(state, Z_MEM_ERROR, \"out of memory\");\n            return -1;\n        }\n        if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */\n            gz_error(state, Z_DATA_ERROR,\n                     strm->msg == NULL ? \"compressed data error\" : strm->msg);\n            return -1;\n        }\n    } while (strm->avail_out && ret != Z_STREAM_END);\n\n    /* update available output */\n    state->x.have = had - strm->avail_out;\n    state->x.next = strm->next_out - state->x.have;\n\n    /* if the gzip stream completed successfully, look for another */\n    if (ret == Z_STREAM_END)\n        state->how = LOOK;\n\n    /* good decompression */\n    return 0;\n}\n\n/* Fetch data and put it in the output buffer.  Assumes state->x.have is 0.\n   Data is either copied from the input file or decompressed from the input\n   file depending on state->how.  If state->how is LOOK, then a gzip header is\n   looked for to determine whether to copy or decompress.  Returns -1 on error,\n   otherwise 0.  gz_fetch() will leave state->how as COPY or GZIP unless the\n   end of the input file has been reached and all data has been processed.  */\nlocal int gz_fetch(state)\n    gz_statep state;\n{\n    z_streamp strm = &(state->strm);\n\n    do {\n        switch(state->how) {\n        case LOOK:      /* -> LOOK, COPY (only if never GZIP), or GZIP */\n            if (gz_look(state) == -1)\n                return -1;\n            if (state->how == LOOK)\n                return 0;\n            break;\n        case COPY:      /* -> COPY */\n            if (gz_load(state, state->out, state->size << 1, &(state->x.have))\n                    == -1)\n                return -1;\n            state->x.next = state->out;\n            return 0;\n        case GZIP:      /* -> GZIP or LOOK (if end of gzip stream) */\n            strm->avail_out = state->size << 1;\n            strm->next_out = state->out;\n            if (gz_decomp(state) == -1)\n                return -1;\n        }\n    } while (state->x.have == 0 && (!state->eof || strm->avail_in));\n    return 0;\n}\n\n/* Skip len uncompressed bytes of output.  Return -1 on error, 0 on success. */\nlocal int gz_skip(state, len)\n    gz_statep state;\n    z_off64_t len;\n{\n    unsigned n;\n\n    /* skip over len bytes or reach end-of-file, whichever comes first */\n    while (len)\n        /* skip over whatever is in output buffer */\n        if (state->x.have) {\n            n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ?\n                (unsigned)len : state->x.have;\n            state->x.have -= n;\n            state->x.next += n;\n            state->x.pos += n;\n            len -= n;\n        }\n\n        /* output buffer empty -- return if we're at the end of the input */\n        else if (state->eof && state->strm.avail_in == 0)\n            break;\n\n        /* need more data to skip -- load up output buffer */\n        else {\n            /* get more output, looking for header if required */\n            if (gz_fetch(state) == -1)\n                return -1;\n        }\n    return 0;\n}\n\n/* Read len bytes into buf from file, or less than len up to the end of the\n   input.  Return the number of bytes read.  If zero is returned, either the\n   end of file was reached, or there was an error.  state->err must be\n   consulted in that case to determine which. */\nlocal z_size_t gz_read(state, buf, len)\n    gz_statep state;\n    voidp buf;\n    z_size_t len;\n{\n    z_size_t got;\n    unsigned n;\n\n    /* if len is zero, avoid unnecessary operations */\n    if (len == 0)\n        return 0;\n\n    /* process a skip request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_skip(state, state->skip) == -1)\n            return 0;\n    }\n\n    /* get len bytes to buf, or less than len if at the end */\n    got = 0;\n    do {\n        /* set n to the maximum amount of len that fits in an unsigned int */\n        n = -1;\n        if (n > len)\n            n = len;\n\n        /* first just try copying data from the output buffer */\n        if (state->x.have) {\n            if (state->x.have < n)\n                n = state->x.have;\n            memcpy(buf, state->x.next, n);\n            state->x.next += n;\n            state->x.have -= n;\n        }\n\n        /* output buffer empty -- return if we're at the end of the input */\n        else if (state->eof && state->strm.avail_in == 0) {\n            state->past = 1;        /* tried to read past end */\n            break;\n        }\n\n        /* need output data -- for small len or new stream load up our output\n           buffer */\n        else if (state->how == LOOK || n < (state->size << 1)) {\n            /* get more output, looking for header if required */\n            if (gz_fetch(state) == -1)\n                return 0;\n            continue;       /* no progress yet -- go back to copy above */\n            /* the copy above assures that we will leave with space in the\n               output buffer, allowing at least one gzungetc() to succeed */\n        }\n\n        /* large len -- read directly into user buffer */\n        else if (state->how == COPY) {      /* read directly */\n            if (gz_load(state, (unsigned char *)buf, n, &n) == -1)\n                return 0;\n        }\n\n        /* large len -- decompress directly into user buffer */\n        else {  /* state->how == GZIP */\n            state->strm.avail_out = n;\n            state->strm.next_out = (unsigned char *)buf;\n            if (gz_decomp(state) == -1)\n                return 0;\n            n = state->x.have;\n            state->x.have = 0;\n        }\n\n        /* update progress */\n        len -= n;\n        buf = (char *)buf + n;\n        got += n;\n        state->x.pos += n;\n    } while (len);\n\n    /* return number of bytes read into user buffer */\n    return got;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzread(file, buf, len)\n    gzFile file;\n    voidp buf;\n    unsigned len;\n{\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n\n    /* check that we're reading and that there's no (serious) error */\n    if (state->mode != GZ_READ ||\n            (state->err != Z_OK && state->err != Z_BUF_ERROR))\n        return -1;\n\n    /* since an int is returned, make sure len fits in one, otherwise return\n       with an error (this avoids a flaw in the interface) */\n    if ((int)len < 0) {\n        gz_error(state, Z_STREAM_ERROR, \"request does not fit in an int\");\n        return -1;\n    }\n\n    /* read len or fewer bytes to buf */\n    len = gz_read(state, buf, len);\n\n    /* check for an error */\n    if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)\n        return -1;\n\n    /* return the number of bytes read (this is assured to fit in an int) */\n    return (int)len;\n}\n\n/* -- see zlib.h -- */\nz_size_t ZEXPORT gzfread(buf, size, nitems, file)\n    voidp buf;\n    z_size_t size;\n    z_size_t nitems;\n    gzFile file;\n{\n    z_size_t len;\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return 0;\n    state = (gz_statep)file;\n\n    /* check that we're reading and that there's no (serious) error */\n    if (state->mode != GZ_READ ||\n            (state->err != Z_OK && state->err != Z_BUF_ERROR))\n        return 0;\n\n    /* compute bytes to read -- error on overflow */\n    len = nitems * size;\n    if (size && len / size != nitems) {\n        gz_error(state, Z_STREAM_ERROR, \"request does not fit in a size_t\");\n        return 0;\n    }\n\n    /* read len or fewer bytes to buf, return the number of full items read */\n    return len ? gz_read(state, buf, len) / size : 0;\n}\n\n/* -- see zlib.h -- */\n#ifdef Z_PREFIX_SET\n#  undef z_gzgetc\n#else\n#  undef gzgetc\n#endif\nint ZEXPORT gzgetc(file)\n    gzFile file;\n{\n    int ret;\n    unsigned char buf[1];\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n\n    /* check that we're reading and that there's no (serious) error */\n    if (state->mode != GZ_READ ||\n        (state->err != Z_OK && state->err != Z_BUF_ERROR))\n        return -1;\n\n    /* try output buffer (no need to check for skip request) */\n    if (state->x.have) {\n        state->x.have--;\n        state->x.pos++;\n        return *(state->x.next)++;\n    }\n\n    /* nothing there -- try gz_read() */\n    ret = gz_read(state, buf, 1);\n    return ret < 1 ? -1 : buf[0];\n}\n\nint ZEXPORT gzgetc_(file)\ngzFile file;\n{\n    return gzgetc(file);\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzungetc(c, file)\n    int c;\n    gzFile file;\n{\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n\n    /* check that we're reading and that there's no (serious) error */\n    if (state->mode != GZ_READ ||\n        (state->err != Z_OK && state->err != Z_BUF_ERROR))\n        return -1;\n\n    /* process a skip request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_skip(state, state->skip) == -1)\n            return -1;\n    }\n\n    /* can't push EOF */\n    if (c < 0)\n        return -1;\n\n    /* if output buffer empty, put byte at end (allows more pushing) */\n    if (state->x.have == 0) {\n        state->x.have = 1;\n        state->x.next = state->out + (state->size << 1) - 1;\n        state->x.next[0] = (unsigned char)c;\n        state->x.pos--;\n        state->past = 0;\n        return c;\n    }\n\n    /* if no room, give up (must have already done a gzungetc()) */\n    if (state->x.have == (state->size << 1)) {\n        gz_error(state, Z_DATA_ERROR, \"out of room to push characters\");\n        return -1;\n    }\n\n    /* slide output data if needed and insert byte before existing data */\n    if (state->x.next == state->out) {\n        unsigned char *src = state->out + state->x.have;\n        unsigned char *dest = state->out + (state->size << 1);\n        while (src > state->out)\n            *--dest = *--src;\n        state->x.next = dest;\n    }\n    state->x.have++;\n    state->x.next--;\n    state->x.next[0] = (unsigned char)c;\n    state->x.pos--;\n    state->past = 0;\n    return c;\n}\n\n/* -- see zlib.h -- */\nchar * ZEXPORT gzgets(file, buf, len)\n    gzFile file;\n    char *buf;\n    int len;\n{\n    unsigned left, n;\n    char *str;\n    unsigned char *eol;\n    gz_statep state;\n\n    /* check parameters and get internal structure */\n    if (file == NULL || buf == NULL || len < 1)\n        return NULL;\n    state = (gz_statep)file;\n\n    /* check that we're reading and that there's no (serious) error */\n    if (state->mode != GZ_READ ||\n        (state->err != Z_OK && state->err != Z_BUF_ERROR))\n        return NULL;\n\n    /* process a skip request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_skip(state, state->skip) == -1)\n            return NULL;\n    }\n\n    /* copy output bytes up to new line or len - 1, whichever comes first --\n       append a terminating zero to the string (we don't check for a zero in\n       the contents, let the user worry about that) */\n    str = buf;\n    left = (unsigned)len - 1;\n    if (left) do {\n        /* assure that something is in the output buffer */\n        if (state->x.have == 0 && gz_fetch(state) == -1)\n            return NULL;                /* error */\n        if (state->x.have == 0) {       /* end of file */\n            state->past = 1;            /* read past end */\n            break;                      /* return what we have */\n        }\n\n        /* look for end-of-line in current output buffer */\n        n = state->x.have > left ? left : state->x.have;\n        eol = (unsigned char *)memchr(state->x.next, '\\n', n);\n        if (eol != NULL)\n            n = (unsigned)(eol - state->x.next) + 1;\n\n        /* copy through end-of-line, or remainder if not found */\n        memcpy(buf, state->x.next, n);\n        state->x.have -= n;\n        state->x.next += n;\n        state->x.pos += n;\n        left -= n;\n        buf += n;\n    } while (left && eol == NULL);\n\n    /* return terminated string, or if nothing, end of file */\n    if (buf == str)\n        return NULL;\n    buf[0] = 0;\n    return str;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzdirect(file)\n    gzFile file;\n{\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return 0;\n    state = (gz_statep)file;\n\n    /* if the state is not known, but we can find out, then do so (this is\n       mainly for right after a gzopen() or gzdopen()) */\n    if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)\n        (void)gz_look(state);\n\n    /* return 1 if transparent, 0 if processing a gzip stream */\n    return state->direct;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzclose_r(file)\n    gzFile file;\n{\n    int ret, err;\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n\n    /* check that we're reading */\n    if (state->mode != GZ_READ)\n        return Z_STREAM_ERROR;\n\n    /* free memory and close file */\n    if (state->size) {\n        inflateEnd(&(state->strm));\n        free(state->out);\n        free(state->in);\n    }\n    err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK;\n    gz_error(state, Z_OK, NULL);\n    free(state->path);\n    ret = close(state->fd);\n    free(state);\n    return ret ? Z_ERRNO : err;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/gzwrite.c",
    "content": "/* gzwrite.c -- zlib functions for writing gzip files\n * Copyright (C) 2004-2017 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n#include \"gzguts.h\"\n\n/* Local functions */\nlocal int gz_init OF((gz_statep));\nlocal int gz_comp OF((gz_statep, int));\nlocal int gz_zero OF((gz_statep, z_off64_t));\nlocal z_size_t gz_write OF((gz_statep, voidpc, z_size_t));\n\n/* Initialize state for writing a gzip file.  Mark initialization by setting\n   state->size to non-zero.  Return -1 on a memory allocation failure, or 0 on\n   success. */\nlocal int gz_init(state)\n    gz_statep state;\n{\n    int ret;\n    z_streamp strm = &(state->strm);\n\n    /* allocate input buffer (double size for gzprintf) */\n    state->in = (unsigned char *)malloc(state->want << 1);\n    if (state->in == NULL) {\n        gz_error(state, Z_MEM_ERROR, \"out of memory\");\n        return -1;\n    }\n\n    /* only need output buffer and deflate state if compressing */\n    if (!state->direct) {\n        /* allocate output buffer */\n        state->out = (unsigned char *)malloc(state->want);\n        if (state->out == NULL) {\n            free(state->in);\n            gz_error(state, Z_MEM_ERROR, \"out of memory\");\n            return -1;\n        }\n\n        /* allocate deflate memory, set up for gzip compression */\n        strm->zalloc = Z_NULL;\n        strm->zfree = Z_NULL;\n        strm->opaque = Z_NULL;\n        ret = deflateInit2(strm, state->level, Z_DEFLATED,\n                           MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);\n        if (ret != Z_OK) {\n            free(state->out);\n            free(state->in);\n            gz_error(state, Z_MEM_ERROR, \"out of memory\");\n            return -1;\n        }\n        strm->next_in = NULL;\n    }\n\n    /* mark state as initialized */\n    state->size = state->want;\n\n    /* initialize write buffer if compressing */\n    if (!state->direct) {\n        strm->avail_out = state->size;\n        strm->next_out = state->out;\n        state->x.next = strm->next_out;\n    }\n    return 0;\n}\n\n/* Compress whatever is at avail_in and next_in and write to the output file.\n   Return -1 if there is an error writing to the output file or if gz_init()\n   fails to allocate memory, otherwise 0.  flush is assumed to be a valid\n   deflate() flush value.  If flush is Z_FINISH, then the deflate() state is\n   reset to start a new gzip stream.  If gz->direct is true, then simply write\n   to the output file without compressing, and ignore flush. */\nlocal int gz_comp(state, flush)\n    gz_statep state;\n    int flush;\n{\n    int ret, writ;\n    unsigned have, put, max = ((unsigned)-1 >> 2) + 1;\n    z_streamp strm = &(state->strm);\n\n    /* allocate memory if this is the first time through */\n    if (state->size == 0 && gz_init(state) == -1)\n        return -1;\n\n    /* write directly if requested */\n    if (state->direct) {\n        while (strm->avail_in) {\n            put = strm->avail_in > max ? max : strm->avail_in;\n            writ = write(state->fd, strm->next_in, put);\n            if (writ < 0) {\n                gz_error(state, Z_ERRNO, zstrerror());\n                return -1;\n            }\n            strm->avail_in -= (unsigned)writ;\n            strm->next_in += writ;\n        }\n        return 0;\n    }\n\n    /* run deflate() on provided input until it produces no more output */\n    ret = Z_OK;\n    do {\n        /* write out current buffer contents if full, or if flushing, but if\n           doing Z_FINISH then don't write until we get to Z_STREAM_END */\n        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&\n            (flush != Z_FINISH || ret == Z_STREAM_END))) {\n            while (strm->next_out > state->x.next) {\n                put = strm->next_out - state->x.next > (int)max ? max :\n                      (unsigned)(strm->next_out - state->x.next);\n                writ = write(state->fd, state->x.next, put);\n                if (writ < 0) {\n                    gz_error(state, Z_ERRNO, zstrerror());\n                    return -1;\n                }\n                state->x.next += writ;\n            }\n            if (strm->avail_out == 0) {\n                strm->avail_out = state->size;\n                strm->next_out = state->out;\n                state->x.next = state->out;\n            }\n        }\n\n        /* compress */\n        have = strm->avail_out;\n        ret = deflate(strm, flush);\n        if (ret == Z_STREAM_ERROR) {\n            gz_error(state, Z_STREAM_ERROR,\n                      \"internal error: deflate stream corrupt\");\n            return -1;\n        }\n        have -= strm->avail_out;\n    } while (have);\n\n    /* if that completed a deflate stream, allow another to start */\n    if (flush == Z_FINISH)\n        deflateReset(strm);\n\n    /* all done, no errors */\n    return 0;\n}\n\n/* Compress len zeros to output.  Return -1 on a write error or memory\n   allocation failure by gz_comp(), or 0 on success. */\nlocal int gz_zero(state, len)\n    gz_statep state;\n    z_off64_t len;\n{\n    int first;\n    unsigned n;\n    z_streamp strm = &(state->strm);\n\n    /* consume whatever's left in the input buffer */\n    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)\n        return -1;\n\n    /* compress len zeros (len guaranteed > 0) */\n    first = 1;\n    while (len) {\n        n = GT_OFF(state->size) || (z_off64_t)state->size > len ?\n            (unsigned)len : state->size;\n        if (first) {\n            memset(state->in, 0, n);\n            first = 0;\n        }\n        strm->avail_in = n;\n        strm->next_in = state->in;\n        state->x.pos += n;\n        if (gz_comp(state, Z_NO_FLUSH) == -1)\n            return -1;\n        len -= n;\n    }\n    return 0;\n}\n\n/* Write len bytes from buf to file.  Return the number of bytes written.  If\n   the returned value is less than len, then there was an error. */\nlocal z_size_t gz_write(state, buf, len)\n    gz_statep state;\n    voidpc buf;\n    z_size_t len;\n{\n    z_size_t put = len;\n\n    /* if len is zero, avoid unnecessary operations */\n    if (len == 0)\n        return 0;\n\n    /* allocate memory if this is the first time through */\n    if (state->size == 0 && gz_init(state) == -1)\n        return 0;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            return 0;\n    }\n\n    /* for small len, copy to input buffer, otherwise compress directly */\n    if (len < state->size) {\n        /* copy to input buffer, compress when full */\n        do {\n            unsigned have, copy;\n\n            if (state->strm.avail_in == 0)\n                state->strm.next_in = state->in;\n            have = (unsigned)((state->strm.next_in + state->strm.avail_in) -\n                              state->in);\n            copy = state->size - have;\n            if (copy > len)\n                copy = len;\n            memcpy(state->in + have, buf, copy);\n            state->strm.avail_in += copy;\n            state->x.pos += copy;\n            buf = (const char *)buf + copy;\n            len -= copy;\n            if (len && gz_comp(state, Z_NO_FLUSH) == -1)\n                return 0;\n        } while (len);\n    }\n    else {\n        /* consume whatever's left in the input buffer */\n        if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)\n            return 0;\n\n        /* directly compress user buffer to file */\n        state->strm.next_in = (z_const Bytef *)buf;\n        do {\n            unsigned n = (unsigned)-1;\n            if (n > len)\n                n = len;\n            state->strm.avail_in = n;\n            state->x.pos += n;\n            if (gz_comp(state, Z_NO_FLUSH) == -1)\n                return 0;\n            len -= n;\n        } while (len);\n    }\n\n    /* input was all buffered or compressed */\n    return put;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzwrite(file, buf, len)\n    gzFile file;\n    voidpc buf;\n    unsigned len;\n{\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return 0;\n    state = (gz_statep)file;\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return 0;\n\n    /* since an int is returned, make sure len fits in one, otherwise return\n       with an error (this avoids a flaw in the interface) */\n    if ((int)len < 0) {\n        gz_error(state, Z_DATA_ERROR, \"requested length does not fit in int\");\n        return 0;\n    }\n\n    /* write len bytes from buf (the return value will fit in an int) */\n    return (int)gz_write(state, buf, len);\n}\n\n/* -- see zlib.h -- */\nz_size_t ZEXPORT gzfwrite(buf, size, nitems, file)\n    voidpc buf;\n    z_size_t size;\n    z_size_t nitems;\n    gzFile file;\n{\n    z_size_t len;\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return 0;\n    state = (gz_statep)file;\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return 0;\n\n    /* compute bytes to read -- error on overflow */\n    len = nitems * size;\n    if (size && len / size != nitems) {\n        gz_error(state, Z_STREAM_ERROR, \"request does not fit in a size_t\");\n        return 0;\n    }\n\n    /* write len bytes to buf, return the number of full items written */\n    return len ? gz_write(state, buf, len) / size : 0;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzputc(file, c)\n    gzFile file;\n    int c;\n{\n    unsigned have;\n    unsigned char buf[1];\n    gz_statep state;\n    z_streamp strm;\n\n    /* get internal structure */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n    strm = &(state->strm);\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return -1;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            return -1;\n    }\n\n    /* try writing to input buffer for speed (state->size == 0 if buffer not\n       initialized) */\n    if (state->size) {\n        if (strm->avail_in == 0)\n            strm->next_in = state->in;\n        have = (unsigned)((strm->next_in + strm->avail_in) - state->in);\n        if (have < state->size) {\n            state->in[have] = (unsigned char)c;\n            strm->avail_in++;\n            state->x.pos++;\n            return c & 0xff;\n        }\n    }\n\n    /* no room in buffer or not initialized, use gz_write() */\n    buf[0] = (unsigned char)c;\n    if (gz_write(state, buf, 1) != 1)\n        return -1;\n    return c & 0xff;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzputs(file, str)\n    gzFile file;\n    const char *str;\n{\n    int ret;\n    z_size_t len;\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return -1;\n    state = (gz_statep)file;\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return -1;\n\n    /* write string */\n    len = strlen(str);\n    ret = gz_write(state, str, len);\n    return ret == 0 && len != 0 ? -1 : ret;\n}\n\n#if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#include <stdarg.h>\n\n/* -- see zlib.h -- */\nint ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va)\n{\n    int len;\n    unsigned left;\n    char *next;\n    gz_statep state;\n    z_streamp strm;\n\n    /* get internal structure */\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n    strm = &(state->strm);\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return Z_STREAM_ERROR;\n\n    /* make sure we have some buffer space */\n    if (state->size == 0 && gz_init(state) == -1)\n        return state->err;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            return state->err;\n    }\n\n    /* do the printf() into the input buffer, put length in len -- the input\n       buffer is double-sized just for this function, so there is guaranteed to\n       be state->size bytes available after the current contents */\n    if (strm->avail_in == 0)\n        strm->next_in = state->in;\n    next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);\n    next[state->size - 1] = 0;\n#ifdef NO_vsnprintf\n#  ifdef HAS_vsprintf_void\n    (void)vsprintf(next, format, va);\n    for (len = 0; len < state->size; len++)\n        if (next[len] == 0) break;\n#  else\n    len = vsprintf(next, format, va);\n#  endif\n#else\n#  ifdef HAS_vsnprintf_void\n    (void)vsnprintf(next, state->size, format, va);\n    len = strlen(next);\n#  else\n    len = vsnprintf(next, state->size, format, va);\n#  endif\n#endif\n\n    /* check that printf() results fit in buffer */\n    if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)\n        return 0;\n\n    /* update buffer and position, compress first half if past that */\n    strm->avail_in += (unsigned)len;\n    state->x.pos += len;\n    if (strm->avail_in >= state->size) {\n        left = strm->avail_in - state->size;\n        strm->avail_in = state->size;\n        if (gz_comp(state, Z_NO_FLUSH) == -1)\n            return state->err;\n        memcpy(state->in, state->in + state->size, left);\n        strm->next_in = state->in;\n        strm->avail_in = left;\n    }\n    return len;\n}\n\nint ZEXPORTVA gzprintf(gzFile file, const char *format, ...)\n{\n    va_list va;\n    int ret;\n\n    va_start(va, format);\n    ret = gzvprintf(file, format, va);\n    va_end(va);\n    return ret;\n}\n\n#else /* !STDC && !Z_HAVE_STDARG_H */\n\n/* -- see zlib.h -- */\nint ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,\n                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)\n    gzFile file;\n    const char *format;\n    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,\n        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;\n{\n    unsigned len, left;\n    char *next;\n    gz_statep state;\n    z_streamp strm;\n\n    /* get internal structure */\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n    strm = &(state->strm);\n\n    /* check that can really pass pointer in ints */\n    if (sizeof(int) != sizeof(void *))\n        return Z_STREAM_ERROR;\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return Z_STREAM_ERROR;\n\n    /* make sure we have some buffer space */\n    if (state->size == 0 && gz_init(state) == -1)\n        return state->error;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            return state->error;\n    }\n\n    /* do the printf() into the input buffer, put length in len -- the input\n       buffer is double-sized just for this function, so there is guaranteed to\n       be state->size bytes available after the current contents */\n    if (strm->avail_in == 0)\n        strm->next_in = state->in;\n    next = (char *)(strm->next_in + strm->avail_in);\n    next[state->size - 1] = 0;\n#ifdef NO_snprintf\n#  ifdef HAS_sprintf_void\n    sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,\n            a13, a14, a15, a16, a17, a18, a19, a20);\n    for (len = 0; len < size; len++)\n        if (next[len] == 0)\n            break;\n#  else\n    len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,\n                  a12, a13, a14, a15, a16, a17, a18, a19, a20);\n#  endif\n#else\n#  ifdef HAS_snprintf_void\n    snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9,\n             a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);\n    len = strlen(next);\n#  else\n    len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8,\n                   a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);\n#  endif\n#endif\n\n    /* check that printf() results fit in buffer */\n    if (len == 0 || len >= state->size || next[state->size - 1] != 0)\n        return 0;\n\n    /* update buffer and position, compress first half if past that */\n    strm->avail_in += len;\n    state->x.pos += len;\n    if (strm->avail_in >= state->size) {\n        left = strm->avail_in - state->size;\n        strm->avail_in = state->size;\n        if (gz_comp(state, Z_NO_FLUSH) == -1)\n            return state->err;\n        memcpy(state->in, state->in + state->size, left);\n        strm->next_in = state->in;\n        strm->avail_in = left;\n    }\n    return (int)len;\n}\n\n#endif\n\n/* -- see zlib.h -- */\nint ZEXPORT gzflush(file, flush)\n    gzFile file;\n    int flush;\n{\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return Z_STREAM_ERROR;\n\n    /* check flush parameter */\n    if (flush < 0 || flush > Z_FINISH)\n        return Z_STREAM_ERROR;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            return state->err;\n    }\n\n    /* compress remaining data with requested flush */\n    (void)gz_comp(state, flush);\n    return state->err;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzsetparams(file, level, strategy)\n    gzFile file;\n    int level;\n    int strategy;\n{\n    gz_statep state;\n    z_streamp strm;\n\n    /* get internal structure */\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n    strm = &(state->strm);\n\n    /* check that we're writing and that there's no error */\n    if (state->mode != GZ_WRITE || state->err != Z_OK)\n        return Z_STREAM_ERROR;\n\n    /* if no change is requested, then do nothing */\n    if (level == state->level && strategy == state->strategy)\n        return Z_OK;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            return state->err;\n    }\n\n    /* change compression parameters for subsequent input */\n    if (state->size) {\n        /* flush previous input with previous parameters before changing */\n        if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)\n            return state->err;\n        deflateParams(strm, level, strategy);\n    }\n    state->level = level;\n    state->strategy = strategy;\n    return Z_OK;\n}\n\n/* -- see zlib.h -- */\nint ZEXPORT gzclose_w(file)\n    gzFile file;\n{\n    int ret = Z_OK;\n    gz_statep state;\n\n    /* get internal structure */\n    if (file == NULL)\n        return Z_STREAM_ERROR;\n    state = (gz_statep)file;\n\n    /* check that we're writing */\n    if (state->mode != GZ_WRITE)\n        return Z_STREAM_ERROR;\n\n    /* check for seek request */\n    if (state->seek) {\n        state->seek = 0;\n        if (gz_zero(state, state->skip) == -1)\n            ret = state->err;\n    }\n\n    /* flush, free memory, and close file */\n    if (gz_comp(state, Z_FINISH) == -1)\n        ret = state->err;\n    if (state->size) {\n        if (!state->direct) {\n            (void)deflateEnd(&(state->strm));\n            free(state->out);\n        }\n        free(state->in);\n    }\n    gz_error(state, Z_OK, NULL);\n    free(state->path);\n    if (close(state->fd) == -1)\n        ret = Z_ERRNO;\n    free(state);\n    return ret;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/infback.c",
    "content": "/* infback.c -- inflate using a call-back interface\n * Copyright (C) 1995-2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/*\n   This code is largely copied from inflate.c.  Normally either infback.o or\n   inflate.o would be linked into an application--not both.  The interface\n   with inffast.c is retained so that optimized assembler-coded versions of\n   inflate_fast() can be used with either inflate.c or infback.c.\n */\n\n#include \"zutil.h\"\n#include \"inftrees.h\"\n#include \"inflate.h\"\n#include \"inffast.h\"\n\n/* function prototypes */\nlocal void fixedtables OF((struct inflate_state FAR *state));\n\n/*\n   strm provides memory allocation functions in zalloc and zfree, or\n   Z_NULL to use the library memory allocation functions.\n\n   windowBits is in the range 8..15, and window is a user-supplied\n   window and output buffer that is 2**windowBits bytes.\n */\nint ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)\nz_streamp strm;\nint windowBits;\nunsigned char FAR *window;\nconst char *version;\nint stream_size;\n{\n    struct inflate_state FAR *state;\n\n    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||\n        stream_size != (int)(sizeof(z_stream)))\n        return Z_VERSION_ERROR;\n    if (strm == Z_NULL || window == Z_NULL ||\n        windowBits < 8 || windowBits > 15)\n        return Z_STREAM_ERROR;\n    strm->msg = Z_NULL;                 /* in case we return an error */\n    if (strm->zalloc == (alloc_func)0) {\n#ifdef Z_SOLO\n        return Z_STREAM_ERROR;\n#else\n        strm->zalloc = zcalloc;\n        strm->opaque = (voidpf)0;\n#endif\n    }\n    if (strm->zfree == (free_func)0)\n#ifdef Z_SOLO\n        return Z_STREAM_ERROR;\n#else\n    strm->zfree = zcfree;\n#endif\n    state = (struct inflate_state FAR *)ZALLOC(strm, 1,\n                                               sizeof(struct inflate_state));\n    if (state == Z_NULL) return Z_MEM_ERROR;\n    Tracev((stderr, \"inflate: allocated\\n\"));\n    strm->state = (struct internal_state FAR *)state;\n    state->dmax = 32768U;\n    state->wbits = (uInt)windowBits;\n    state->wsize = 1U << windowBits;\n    state->window = window;\n    state->wnext = 0;\n    state->whave = 0;\n    return Z_OK;\n}\n\n/*\n   Return state with length and distance decoding tables and index sizes set to\n   fixed code decoding.  Normally this returns fixed tables from inffixed.h.\n   If BUILDFIXED is defined, then instead this routine builds the tables the\n   first time it's called, and returns those tables the first time and\n   thereafter.  This reduces the size of the code by about 2K bytes, in\n   exchange for a little execution time.  However, BUILDFIXED should not be\n   used for threaded applications, since the rewriting of the tables and virgin\n   may not be thread-safe.\n */\nlocal void fixedtables(state)\nstruct inflate_state FAR *state;\n{\n#ifdef BUILDFIXED\n    static int virgin = 1;\n    static code *lenfix, *distfix;\n    static code fixed[544];\n\n    /* build fixed huffman tables if first call (may not be thread safe) */\n    if (virgin) {\n        unsigned sym, bits;\n        static code *next;\n\n        /* literal/length table */\n        sym = 0;\n        while (sym < 144) state->lens[sym++] = 8;\n        while (sym < 256) state->lens[sym++] = 9;\n        while (sym < 280) state->lens[sym++] = 7;\n        while (sym < 288) state->lens[sym++] = 8;\n        next = fixed;\n        lenfix = next;\n        bits = 9;\n        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);\n\n        /* distance table */\n        sym = 0;\n        while (sym < 32) state->lens[sym++] = 5;\n        distfix = next;\n        bits = 5;\n        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);\n\n        /* do this just once */\n        virgin = 0;\n    }\n#else /* !BUILDFIXED */\n#   include \"inffixed.h\"\n#endif /* BUILDFIXED */\n    state->lencode = lenfix;\n    state->lenbits = 9;\n    state->distcode = distfix;\n    state->distbits = 5;\n}\n\n/* Macros for inflateBack(): */\n\n/* Load returned state from inflate_fast() */\n#define LOAD() \\\n    do { \\\n        put = strm->next_out; \\\n        left = strm->avail_out; \\\n        next = strm->next_in; \\\n        have = strm->avail_in; \\\n        hold = state->hold; \\\n        bits = state->bits; \\\n    } while (0)\n\n/* Set state from registers for inflate_fast() */\n#define RESTORE() \\\n    do { \\\n        strm->next_out = put; \\\n        strm->avail_out = left; \\\n        strm->next_in = next; \\\n        strm->avail_in = have; \\\n        state->hold = hold; \\\n        state->bits = bits; \\\n    } while (0)\n\n/* Clear the input bit accumulator */\n#define INITBITS() \\\n    do { \\\n        hold = 0; \\\n        bits = 0; \\\n    } while (0)\n\n/* Assure that some input is available.  If input is requested, but denied,\n   then return a Z_BUF_ERROR from inflateBack(). */\n#define PULL() \\\n    do { \\\n        if (have == 0) { \\\n            have = in(in_desc, &next); \\\n            if (have == 0) { \\\n                next = Z_NULL; \\\n                ret = Z_BUF_ERROR; \\\n                goto inf_leave; \\\n            } \\\n        } \\\n    } while (0)\n\n/* Get a byte of input into the bit accumulator, or return from inflateBack()\n   with an error if there is no input available. */\n#define PULLBYTE() \\\n    do { \\\n        PULL(); \\\n        have--; \\\n        hold += (unsigned long)(*next++) << bits; \\\n        bits += 8; \\\n    } while (0)\n\n/* Assure that there are at least n bits in the bit accumulator.  If there is\n   not enough available input to do that, then return from inflateBack() with\n   an error. */\n#define NEEDBITS(n) \\\n    do { \\\n        while (bits < (unsigned)(n)) \\\n            PULLBYTE(); \\\n    } while (0)\n\n/* Return the low n bits of the bit accumulator (n < 16) */\n#define BITS(n) \\\n    ((unsigned)hold & ((1U << (n)) - 1))\n\n/* Remove n bits from the bit accumulator */\n#define DROPBITS(n) \\\n    do { \\\n        hold >>= (n); \\\n        bits -= (unsigned)(n); \\\n    } while (0)\n\n/* Remove zero to seven bits as needed to go to a byte boundary */\n#define BYTEBITS() \\\n    do { \\\n        hold >>= bits & 7; \\\n        bits -= bits & 7; \\\n    } while (0)\n\n/* Assure that some output space is available, by writing out the window\n   if it's full.  If the write fails, return from inflateBack() with a\n   Z_BUF_ERROR. */\n#define ROOM() \\\n    do { \\\n        if (left == 0) { \\\n            put = state->window; \\\n            left = state->wsize; \\\n            state->whave = left; \\\n            if (out(out_desc, put, left)) { \\\n                ret = Z_BUF_ERROR; \\\n                goto inf_leave; \\\n            } \\\n        } \\\n    } while (0)\n\n/*\n   strm provides the memory allocation functions and window buffer on input,\n   and provides information on the unused input on return.  For Z_DATA_ERROR\n   returns, strm will also provide an error message.\n\n   in() and out() are the call-back input and output functions.  When\n   inflateBack() needs more input, it calls in().  When inflateBack() has\n   filled the window with output, or when it completes with data in the\n   window, it calls out() to write out the data.  The application must not\n   change the provided input until in() is called again or inflateBack()\n   returns.  The application must not change the window/output buffer until\n   inflateBack() returns.\n\n   in() and out() are called with a descriptor parameter provided in the\n   inflateBack() call.  This parameter can be a structure that provides the\n   information required to do the read or write, as well as accumulated\n   information on the input and output such as totals and check values.\n\n   in() should return zero on failure.  out() should return non-zero on\n   failure.  If either in() or out() fails, than inflateBack() returns a\n   Z_BUF_ERROR.  strm->next_in can be checked for Z_NULL to see whether it\n   was in() or out() that caused in the error.  Otherwise,  inflateBack()\n   returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format\n   error, or Z_MEM_ERROR if it could not allocate memory for the state.\n   inflateBack() can also return Z_STREAM_ERROR if the input parameters\n   are not correct, i.e. strm is Z_NULL or the state was not initialized.\n */\nint ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)\nz_streamp strm;\nin_func in;\nvoid FAR *in_desc;\nout_func out;\nvoid FAR *out_desc;\n{\n    struct inflate_state FAR *state;\n    z_const unsigned char FAR *next;    /* next input */\n    unsigned char FAR *put;     /* next output */\n    unsigned have, left;        /* available input and output */\n    unsigned long hold;         /* bit buffer */\n    unsigned bits;              /* bits in bit buffer */\n    unsigned copy;              /* number of stored or match bytes to copy */\n    unsigned char FAR *from;    /* where to copy match bytes from */\n    code here;                  /* current decoding table entry */\n    code last;                  /* parent table entry */\n    unsigned len;               /* length to copy for repeats, bits to drop */\n    int ret;                    /* return code */\n    static const unsigned short order[19] = /* permutation of code lengths */\n        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};\n\n    /* Check that the strm exists and that the state was initialized */\n    if (strm == Z_NULL || strm->state == Z_NULL)\n        return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n\n    /* Reset the state */\n    strm->msg = Z_NULL;\n    state->mode = TYPE;\n    state->last = 0;\n    state->whave = 0;\n    next = strm->next_in;\n    have = next != Z_NULL ? strm->avail_in : 0;\n    hold = 0;\n    bits = 0;\n    put = state->window;\n    left = state->wsize;\n\n    /* Inflate until end of block marked as last */\n    for (;;)\n        switch (state->mode) {\n        case TYPE:\n            /* determine and dispatch block type */\n            if (state->last) {\n                BYTEBITS();\n                state->mode = DONE;\n                break;\n            }\n            NEEDBITS(3);\n            state->last = BITS(1);\n            DROPBITS(1);\n            switch (BITS(2)) {\n            case 0:                             /* stored block */\n                Tracev((stderr, \"inflate:     stored block%s\\n\",\n                        state->last ? \" (last)\" : \"\"));\n                state->mode = STORED;\n                break;\n            case 1:                             /* fixed block */\n                fixedtables(state);\n                Tracev((stderr, \"inflate:     fixed codes block%s\\n\",\n                        state->last ? \" (last)\" : \"\"));\n                state->mode = LEN;              /* decode codes */\n                break;\n            case 2:                             /* dynamic block */\n                Tracev((stderr, \"inflate:     dynamic codes block%s\\n\",\n                        state->last ? \" (last)\" : \"\"));\n                state->mode = TABLE;\n                break;\n            case 3:\n                strm->msg = (char *)\"invalid block type\";\n                state->mode = BAD;\n            }\n            DROPBITS(2);\n            break;\n\n        case STORED:\n            /* get and verify stored block length */\n            BYTEBITS();                         /* go to byte boundary */\n            NEEDBITS(32);\n            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {\n                strm->msg = (char *)\"invalid stored block lengths\";\n                state->mode = BAD;\n                break;\n            }\n            state->length = (unsigned)hold & 0xffff;\n            Tracev((stderr, \"inflate:       stored length %u\\n\",\n                    state->length));\n            INITBITS();\n\n            /* copy stored block from input to output */\n            while (state->length != 0) {\n                copy = state->length;\n                PULL();\n                ROOM();\n                if (copy > have) copy = have;\n                if (copy > left) copy = left;\n                zmemcpy(put, next, copy);\n                have -= copy;\n                next += copy;\n                left -= copy;\n                put += copy;\n                state->length -= copy;\n            }\n            Tracev((stderr, \"inflate:       stored end\\n\"));\n            state->mode = TYPE;\n            break;\n\n        case TABLE:\n            /* get dynamic table entries descriptor */\n            NEEDBITS(14);\n            state->nlen = BITS(5) + 257;\n            DROPBITS(5);\n            state->ndist = BITS(5) + 1;\n            DROPBITS(5);\n            state->ncode = BITS(4) + 4;\n            DROPBITS(4);\n#ifndef PKZIP_BUG_WORKAROUND\n            if (state->nlen > 286 || state->ndist > 30) {\n                strm->msg = (char *)\"too many length or distance symbols\";\n                state->mode = BAD;\n                break;\n            }\n#endif\n            Tracev((stderr, \"inflate:       table sizes ok\\n\"));\n\n            /* get code length code lengths (not a typo) */\n            state->have = 0;\n            while (state->have < state->ncode) {\n                NEEDBITS(3);\n                state->lens[order[state->have++]] = (unsigned short)BITS(3);\n                DROPBITS(3);\n            }\n            while (state->have < 19)\n                state->lens[order[state->have++]] = 0;\n            state->next = state->codes;\n            state->lencode = (code const FAR *)(state->next);\n            state->lenbits = 7;\n            ret = inflate_table(CODES, state->lens, 19, &(state->next),\n                                &(state->lenbits), state->work);\n            if (ret) {\n                strm->msg = (char *)\"invalid code lengths set\";\n                state->mode = BAD;\n                break;\n            }\n            Tracev((stderr, \"inflate:       code lengths ok\\n\"));\n\n            /* get length and distance code code lengths */\n            state->have = 0;\n            while (state->have < state->nlen + state->ndist) {\n                for (;;) {\n                    here = state->lencode[BITS(state->lenbits)];\n                    if ((unsigned)(here.bits) <= bits) break;\n                    PULLBYTE();\n                }\n                if (here.val < 16) {\n                    DROPBITS(here.bits);\n                    state->lens[state->have++] = here.val;\n                }\n                else {\n                    if (here.val == 16) {\n                        NEEDBITS(here.bits + 2);\n                        DROPBITS(here.bits);\n                        if (state->have == 0) {\n                            strm->msg = (char *)\"invalid bit length repeat\";\n                            state->mode = BAD;\n                            break;\n                        }\n                        len = (unsigned)(state->lens[state->have - 1]);\n                        copy = 3 + BITS(2);\n                        DROPBITS(2);\n                    }\n                    else if (here.val == 17) {\n                        NEEDBITS(here.bits + 3);\n                        DROPBITS(here.bits);\n                        len = 0;\n                        copy = 3 + BITS(3);\n                        DROPBITS(3);\n                    }\n                    else {\n                        NEEDBITS(here.bits + 7);\n                        DROPBITS(here.bits);\n                        len = 0;\n                        copy = 11 + BITS(7);\n                        DROPBITS(7);\n                    }\n                    if (state->have + copy > state->nlen + state->ndist) {\n                        strm->msg = (char *)\"invalid bit length repeat\";\n                        state->mode = BAD;\n                        break;\n                    }\n                    while (copy--)\n                        state->lens[state->have++] = (unsigned short)len;\n                }\n            }\n\n            /* handle error breaks in while */\n            if (state->mode == BAD) break;\n\n            /* check for end-of-block code (better have one) */\n            if (state->lens[256] == 0) {\n                strm->msg = (char *)\"invalid code -- missing end-of-block\";\n                state->mode = BAD;\n                break;\n            }\n\n            /* build code tables -- note: do not change the lenbits or distbits\n               values here (9 and 6) without reading the comments in inftrees.h\n               concerning the ENOUGH constants, which depend on those values */\n            state->next = state->codes;\n            state->lencode = (code const FAR *)(state->next);\n            state->lenbits = 9;\n            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),\n                                &(state->lenbits), state->work);\n            if (ret) {\n                strm->msg = (char *)\"invalid literal/lengths set\";\n                state->mode = BAD;\n                break;\n            }\n            state->distcode = (code const FAR *)(state->next);\n            state->distbits = 6;\n            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,\n                            &(state->next), &(state->distbits), state->work);\n            if (ret) {\n                strm->msg = (char *)\"invalid distances set\";\n                state->mode = BAD;\n                break;\n            }\n            Tracev((stderr, \"inflate:       codes ok\\n\"));\n            state->mode = LEN;\n\n        case LEN:\n            /* use inflate_fast() if we have enough input and output */\n            if (have >= 6 && left >= 258) {\n                RESTORE();\n                if (state->whave < state->wsize)\n                    state->whave = state->wsize - left;\n                inflate_fast(strm, state->wsize);\n                LOAD();\n                break;\n            }\n\n            /* get a literal, length, or end-of-block code */\n            for (;;) {\n                here = state->lencode[BITS(state->lenbits)];\n                if ((unsigned)(here.bits) <= bits) break;\n                PULLBYTE();\n            }\n            if (here.op && (here.op & 0xf0) == 0) {\n                last = here;\n                for (;;) {\n                    here = state->lencode[last.val +\n                            (BITS(last.bits + last.op) >> last.bits)];\n                    if ((unsigned)(last.bits + here.bits) <= bits) break;\n                    PULLBYTE();\n                }\n                DROPBITS(last.bits);\n            }\n            DROPBITS(here.bits);\n            state->length = (unsigned)here.val;\n\n            /* process literal */\n            if (here.op == 0) {\n                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n                        \"inflate:         literal '%c'\\n\" :\n                        \"inflate:         literal 0x%02x\\n\", here.val));\n                ROOM();\n                *put++ = (unsigned char)(state->length);\n                left--;\n                state->mode = LEN;\n                break;\n            }\n\n            /* process end of block */\n            if (here.op & 32) {\n                Tracevv((stderr, \"inflate:         end of block\\n\"));\n                state->mode = TYPE;\n                break;\n            }\n\n            /* invalid code */\n            if (here.op & 64) {\n                strm->msg = (char *)\"invalid literal/length code\";\n                state->mode = BAD;\n                break;\n            }\n\n            /* length code -- get extra bits, if any */\n            state->extra = (unsigned)(here.op) & 15;\n            if (state->extra != 0) {\n                NEEDBITS(state->extra);\n                state->length += BITS(state->extra);\n                DROPBITS(state->extra);\n            }\n            Tracevv((stderr, \"inflate:         length %u\\n\", state->length));\n\n            /* get distance code */\n            for (;;) {\n                here = state->distcode[BITS(state->distbits)];\n                if ((unsigned)(here.bits) <= bits) break;\n                PULLBYTE();\n            }\n            if ((here.op & 0xf0) == 0) {\n                last = here;\n                for (;;) {\n                    here = state->distcode[last.val +\n                            (BITS(last.bits + last.op) >> last.bits)];\n                    if ((unsigned)(last.bits + here.bits) <= bits) break;\n                    PULLBYTE();\n                }\n                DROPBITS(last.bits);\n            }\n            DROPBITS(here.bits);\n            if (here.op & 64) {\n                strm->msg = (char *)\"invalid distance code\";\n                state->mode = BAD;\n                break;\n            }\n            state->offset = (unsigned)here.val;\n\n            /* get distance extra bits, if any */\n            state->extra = (unsigned)(here.op) & 15;\n            if (state->extra != 0) {\n                NEEDBITS(state->extra);\n                state->offset += BITS(state->extra);\n                DROPBITS(state->extra);\n            }\n            if (state->offset > state->wsize - (state->whave < state->wsize ?\n                                                left : 0)) {\n                strm->msg = (char *)\"invalid distance too far back\";\n                state->mode = BAD;\n                break;\n            }\n            Tracevv((stderr, \"inflate:         distance %u\\n\", state->offset));\n\n            /* copy match from window to output */\n            do {\n                ROOM();\n                copy = state->wsize - state->offset;\n                if (copy < left) {\n                    from = put + copy;\n                    copy = left - copy;\n                }\n                else {\n                    from = put - state->offset;\n                    copy = left;\n                }\n                if (copy > state->length) copy = state->length;\n                state->length -= copy;\n                left -= copy;\n                do {\n                    *put++ = *from++;\n                } while (--copy);\n            } while (state->length != 0);\n            break;\n\n        case DONE:\n            /* inflate stream terminated properly -- write leftover output */\n            ret = Z_STREAM_END;\n            if (left < state->wsize) {\n                if (out(out_desc, state->window, state->wsize - left))\n                    ret = Z_BUF_ERROR;\n            }\n            goto inf_leave;\n\n        case BAD:\n            ret = Z_DATA_ERROR;\n            goto inf_leave;\n\n        default:                /* can't happen, but makes compilers happy */\n            ret = Z_STREAM_ERROR;\n            goto inf_leave;\n        }\n\n    /* Return unused input */\n  inf_leave:\n    strm->next_in = next;\n    strm->avail_in = have;\n    return ret;\n}\n\nint ZEXPORT inflateBackEnd(strm)\nz_streamp strm;\n{\n    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)\n        return Z_STREAM_ERROR;\n    ZFREE(strm, strm->state);\n    strm->state = Z_NULL;\n    Tracev((stderr, \"inflate: end\\n\"));\n    return Z_OK;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inffast.c",
    "content": "/* inffast.c -- fast decoding\n * Copyright (C) 1995-2017 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n#include \"zutil.h\"\n#include \"inftrees.h\"\n#include \"inflate.h\"\n#include \"inffast.h\"\n\n#ifdef ASMINF\n#  pragma message(\"Assembler code may have bugs -- use at your own risk\")\n#else\n\n/*\n   Decode literal, length, and distance codes and write out the resulting\n   literal and match bytes until either not enough input or output is\n   available, an end-of-block is encountered, or a data error is encountered.\n   When large enough input and output buffers are supplied to inflate(), for\n   example, a 16K input buffer and a 64K output buffer, more than 95% of the\n   inflate execution time is spent in this routine.\n\n   Entry assumptions:\n\n        state->mode == LEN\n        strm->avail_in >= 6\n        strm->avail_out >= 258\n        start >= strm->avail_out\n        state->bits < 8\n\n   On return, state->mode is one of:\n\n        LEN -- ran out of enough output space or enough available input\n        TYPE -- reached end of block code, inflate() to interpret next block\n        BAD -- error in block data\n\n   Notes:\n\n    - The maximum input bits used by a length/distance pair is 15 bits for the\n      length code, 5 bits for the length extra, 15 bits for the distance code,\n      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.\n      Therefore if strm->avail_in >= 6, then there is enough input to avoid\n      checking for available input while decoding.\n\n    - The maximum bytes that a single length/distance pair can output is 258\n      bytes, which is the maximum length that can be coded.  inflate_fast()\n      requires strm->avail_out >= 258 for each loop to avoid checking for\n      output space.\n */\nvoid ZLIB_INTERNAL inflate_fast(strm, start)\nz_streamp strm;\nunsigned start;         /* inflate()'s starting value for strm->avail_out */\n{\n    struct inflate_state FAR *state;\n    z_const unsigned char FAR *in;      /* local strm->next_in */\n    z_const unsigned char FAR *last;    /* have enough input while in < last */\n    unsigned char FAR *out;     /* local strm->next_out */\n    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */\n    unsigned char FAR *end;     /* while out < end, enough space available */\n#ifdef INFLATE_STRICT\n    unsigned dmax;              /* maximum distance from zlib header */\n#endif\n    unsigned wsize;             /* window size or zero if not using window */\n    unsigned whave;             /* valid bytes in the window */\n    unsigned wnext;             /* window write index */\n    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */\n    unsigned long hold;         /* local strm->hold */\n    unsigned bits;              /* local strm->bits */\n    code const FAR *lcode;      /* local strm->lencode */\n    code const FAR *dcode;      /* local strm->distcode */\n    unsigned lmask;             /* mask for first level of length codes */\n    unsigned dmask;             /* mask for first level of distance codes */\n    code here;                  /* retrieved table entry */\n    unsigned op;                /* code bits, operation, extra bits, or */\n                                /*  window position, window bytes to copy */\n    unsigned len;               /* match length, unused bytes */\n    unsigned dist;              /* match distance */\n    unsigned char FAR *from;    /* where to copy match from */\n\n    /* copy state to local variables */\n    state = (struct inflate_state FAR *)strm->state;\n    in = strm->next_in;\n    last = in + (strm->avail_in - 5);\n    out = strm->next_out;\n    beg = out - (start - strm->avail_out);\n    end = out + (strm->avail_out - 257);\n#ifdef INFLATE_STRICT\n    dmax = state->dmax;\n#endif\n    wsize = state->wsize;\n    whave = state->whave;\n    wnext = state->wnext;\n    window = state->window;\n    hold = state->hold;\n    bits = state->bits;\n    lcode = state->lencode;\n    dcode = state->distcode;\n    lmask = (1U << state->lenbits) - 1;\n    dmask = (1U << state->distbits) - 1;\n\n    /* decode literals and length/distances until end-of-block or not enough\n       input data or output space */\n    do {\n        if (bits < 15) {\n            hold += (unsigned long)(*in++) << bits;\n            bits += 8;\n            hold += (unsigned long)(*in++) << bits;\n            bits += 8;\n        }\n        here = lcode[hold & lmask];\n      dolen:\n        op = (unsigned)(here.bits);\n        hold >>= op;\n        bits -= op;\n        op = (unsigned)(here.op);\n        if (op == 0) {                          /* literal */\n            Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n                    \"inflate:         literal '%c'\\n\" :\n                    \"inflate:         literal 0x%02x\\n\", here.val));\n            *out++ = (unsigned char)(here.val);\n        }\n        else if (op & 16) {                     /* length base */\n            len = (unsigned)(here.val);\n            op &= 15;                           /* number of extra bits */\n            if (op) {\n                if (bits < op) {\n                    hold += (unsigned long)(*in++) << bits;\n                    bits += 8;\n                }\n                len += (unsigned)hold & ((1U << op) - 1);\n                hold >>= op;\n                bits -= op;\n            }\n            Tracevv((stderr, \"inflate:         length %u\\n\", len));\n            if (bits < 15) {\n                hold += (unsigned long)(*in++) << bits;\n                bits += 8;\n                hold += (unsigned long)(*in++) << bits;\n                bits += 8;\n            }\n            here = dcode[hold & dmask];\n          dodist:\n            op = (unsigned)(here.bits);\n            hold >>= op;\n            bits -= op;\n            op = (unsigned)(here.op);\n            if (op & 16) {                      /* distance base */\n                dist = (unsigned)(here.val);\n                op &= 15;                       /* number of extra bits */\n                if (bits < op) {\n                    hold += (unsigned long)(*in++) << bits;\n                    bits += 8;\n                    if (bits < op) {\n                        hold += (unsigned long)(*in++) << bits;\n                        bits += 8;\n                    }\n                }\n                dist += (unsigned)hold & ((1U << op) - 1);\n#ifdef INFLATE_STRICT\n                if (dist > dmax) {\n                    strm->msg = (char *)\"invalid distance too far back\";\n                    state->mode = BAD;\n                    break;\n                }\n#endif\n                hold >>= op;\n                bits -= op;\n                Tracevv((stderr, \"inflate:         distance %u\\n\", dist));\n                op = (unsigned)(out - beg);     /* max distance in output */\n                if (dist > op) {                /* see if copy from window */\n                    op = dist - op;             /* distance back in window */\n                    if (op > whave) {\n                        if (state->sane) {\n                            strm->msg =\n                                (char *)\"invalid distance too far back\";\n                            state->mode = BAD;\n                            break;\n                        }\n#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n                        if (len <= op - whave) {\n                            do {\n                                *out++ = 0;\n                            } while (--len);\n                            continue;\n                        }\n                        len -= op - whave;\n                        do {\n                            *out++ = 0;\n                        } while (--op > whave);\n                        if (op == 0) {\n                            from = out - dist;\n                            do {\n                                *out++ = *from++;\n                            } while (--len);\n                            continue;\n                        }\n#endif\n                    }\n                    from = window;\n                    if (wnext == 0) {           /* very common case */\n                        from += wsize - op;\n                        if (op < len) {         /* some from window */\n                            len -= op;\n                            do {\n                                *out++ = *from++;\n                            } while (--op);\n                            from = out - dist;  /* rest from output */\n                        }\n                    }\n                    else if (wnext < op) {      /* wrap around window */\n                        from += wsize + wnext - op;\n                        op -= wnext;\n                        if (op < len) {         /* some from end of window */\n                            len -= op;\n                            do {\n                                *out++ = *from++;\n                            } while (--op);\n                            from = window;\n                            if (wnext < len) {  /* some from start of window */\n                                op = wnext;\n                                len -= op;\n                                do {\n                                    *out++ = *from++;\n                                } while (--op);\n                                from = out - dist;      /* rest from output */\n                            }\n                        }\n                    }\n                    else {                      /* contiguous in window */\n                        from += wnext - op;\n                        if (op < len) {         /* some from window */\n                            len -= op;\n                            do {\n                                *out++ = *from++;\n                            } while (--op);\n                            from = out - dist;  /* rest from output */\n                        }\n                    }\n                    while (len > 2) {\n                        *out++ = *from++;\n                        *out++ = *from++;\n                        *out++ = *from++;\n                        len -= 3;\n                    }\n                    if (len) {\n                        *out++ = *from++;\n                        if (len > 1)\n                            *out++ = *from++;\n                    }\n                }\n                else {\n                    from = out - dist;          /* copy direct from output */\n                    do {                        /* minimum length is three */\n                        *out++ = *from++;\n                        *out++ = *from++;\n                        *out++ = *from++;\n                        len -= 3;\n                    } while (len > 2);\n                    if (len) {\n                        *out++ = *from++;\n                        if (len > 1)\n                            *out++ = *from++;\n                    }\n                }\n            }\n            else if ((op & 64) == 0) {          /* 2nd level distance code */\n                here = dcode[here.val + (hold & ((1U << op) - 1))];\n                goto dodist;\n            }\n            else {\n                strm->msg = (char *)\"invalid distance code\";\n                state->mode = BAD;\n                break;\n            }\n        }\n        else if ((op & 64) == 0) {              /* 2nd level length code */\n            here = lcode[here.val + (hold & ((1U << op) - 1))];\n            goto dolen;\n        }\n        else if (op & 32) {                     /* end-of-block */\n            Tracevv((stderr, \"inflate:         end of block\\n\"));\n            state->mode = TYPE;\n            break;\n        }\n        else {\n            strm->msg = (char *)\"invalid literal/length code\";\n            state->mode = BAD;\n            break;\n        }\n    } while (in < last && out < end);\n\n    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */\n    len = bits >> 3;\n    in -= len;\n    bits -= len << 3;\n    hold &= (1U << bits) - 1;\n\n    /* update state and return */\n    strm->next_in = in;\n    strm->next_out = out;\n    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));\n    strm->avail_out = (unsigned)(out < end ?\n                                 257 + (end - out) : 257 - (out - end));\n    state->hold = hold;\n    state->bits = bits;\n    return;\n}\n\n/*\n   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):\n   - Using bit fields for code structure\n   - Different op definition to avoid & for extra bits (do & for table bits)\n   - Three separate decoding do-loops for direct, window, and wnext == 0\n   - Special case for distance > 1 copies to do overlapped load and store copy\n   - Explicit branch predictions (based on measured branch probabilities)\n   - Deferring match copy and interspersed it with decoding subsequent codes\n   - Swapping literal/length else\n   - Swapping window/direct else\n   - Larger unrolled copy loops (three is about right)\n   - Moving len -= 3 statement into middle of loop\n */\n\n#endif /* !ASMINF */\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inffast.h",
    "content": "/* inffast.h -- header to use inffast.c\n * Copyright (C) 1995-2003, 2010 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* WARNING: this file should *not* be used by applications. It is\n   part of the implementation of the compression library and is\n   subject to change. Applications should only use zlib.h.\n */\n\nvoid ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inffixed.h",
    "content": "    /* inffixed.h -- table for decoding fixed codes\n     * Generated automatically by makefixed().\n     */\n\n    /* WARNING: this file should *not* be used by applications.\n       It is part of the implementation of this library and is\n       subject to change. Applications should only use zlib.h.\n     */\n\n    static const code lenfix[512] = {\n        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},\n        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},\n        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},\n        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},\n        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},\n        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},\n        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},\n        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},\n        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},\n        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},\n        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},\n        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},\n        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},\n        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},\n        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},\n        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},\n        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},\n        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},\n        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},\n        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},\n        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},\n        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},\n        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},\n        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},\n        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},\n        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},\n        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},\n        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},\n        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},\n        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},\n        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},\n        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},\n        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},\n        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},\n        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},\n        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},\n        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},\n        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},\n        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},\n        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},\n        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},\n        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},\n        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},\n        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},\n        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},\n        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},\n        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},\n        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},\n        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},\n        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},\n        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},\n        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},\n        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},\n        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},\n        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},\n        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},\n        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},\n        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},\n        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},\n        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},\n        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},\n        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},\n        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},\n        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},\n        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},\n        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},\n        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},\n        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},\n        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},\n        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},\n        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},\n        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},\n        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},\n        {0,9,255}\n    };\n\n    static const code distfix[32] = {\n        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},\n        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},\n        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},\n        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},\n        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},\n        {22,5,193},{64,5,0}\n    };\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inflate.c",
    "content": "/* inflate.c -- zlib decompression\n * Copyright (C) 1995-2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/*\n * Change history:\n *\n * 1.2.beta0    24 Nov 2002\n * - First version -- complete rewrite of inflate to simplify code, avoid\n *   creation of window when not needed, minimize use of window when it is\n *   needed, make inffast.c even faster, implement gzip decoding, and to\n *   improve code readability and style over the previous zlib inflate code\n *\n * 1.2.beta1    25 Nov 2002\n * - Use pointers for available input and output checking in inffast.c\n * - Remove input and output counters in inffast.c\n * - Change inffast.c entry and loop from avail_in >= 7 to >= 6\n * - Remove unnecessary second byte pull from length extra in inffast.c\n * - Unroll direct copy to three copies per loop in inffast.c\n *\n * 1.2.beta2    4 Dec 2002\n * - Change external routine names to reduce potential conflicts\n * - Correct filename to inffixed.h for fixed tables in inflate.c\n * - Make hbuf[] unsigned char to match parameter type in inflate.c\n * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)\n *   to avoid negation problem on Alphas (64 bit) in inflate.c\n *\n * 1.2.beta3    22 Dec 2002\n * - Add comments on state->bits assertion in inffast.c\n * - Add comments on op field in inftrees.h\n * - Fix bug in reuse of allocated window after inflateReset()\n * - Remove bit fields--back to byte structure for speed\n * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths\n * - Change post-increments to pre-increments in inflate_fast(), PPC biased?\n * - Add compile time option, POSTINC, to use post-increments instead (Intel?)\n * - Make MATCH copy in inflate() much faster for when inflate_fast() not used\n * - Use local copies of stream next and avail values, as well as local bit\n *   buffer and bit count in inflate()--for speed when inflate_fast() not used\n *\n * 1.2.beta4    1 Jan 2003\n * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings\n * - Move a comment on output buffer sizes from inffast.c to inflate.c\n * - Add comments in inffast.c to introduce the inflate_fast() routine\n * - Rearrange window copies in inflate_fast() for speed and simplification\n * - Unroll last copy for window match in inflate_fast()\n * - Use local copies of window variables in inflate_fast() for speed\n * - Pull out common wnext == 0 case for speed in inflate_fast()\n * - Make op and len in inflate_fast() unsigned for consistency\n * - Add FAR to lcode and dcode declarations in inflate_fast()\n * - Simplified bad distance check in inflate_fast()\n * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new\n *   source file infback.c to provide a call-back interface to inflate for\n *   programs like gzip and unzip -- uses window as output buffer to avoid\n *   window copying\n *\n * 1.2.beta5    1 Jan 2003\n * - Improved inflateBack() interface to allow the caller to provide initial\n *   input in strm.\n * - Fixed stored blocks bug in inflateBack()\n *\n * 1.2.beta6    4 Jan 2003\n * - Added comments in inffast.c on effectiveness of POSTINC\n * - Typecasting all around to reduce compiler warnings\n * - Changed loops from while (1) or do {} while (1) to for (;;), again to\n *   make compilers happy\n * - Changed type of window in inflateBackInit() to unsigned char *\n *\n * 1.2.beta7    27 Jan 2003\n * - Changed many types to unsigned or unsigned short to avoid warnings\n * - Added inflateCopy() function\n *\n * 1.2.0        9 Mar 2003\n * - Changed inflateBack() interface to provide separate opaque descriptors\n *   for the in() and out() functions\n * - Changed inflateBack() argument and in_func typedef to swap the length\n *   and buffer address return values for the input function\n * - Check next_in and next_out for Z_NULL on entry to inflate()\n *\n * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.\n */\n\n#include \"zutil.h\"\n#include \"inftrees.h\"\n#include \"inflate.h\"\n#include \"inffast.h\"\n\n#ifdef MAKEFIXED\n#  ifndef BUILDFIXED\n#    define BUILDFIXED\n#  endif\n#endif\n\n/* function prototypes */\nlocal int inflateStateCheck OF((z_streamp strm));\nlocal void fixedtables OF((struct inflate_state FAR *state));\nlocal int updatewindow OF((z_streamp strm, const unsigned char FAR *end,\n                           unsigned copy));\n#ifdef BUILDFIXED\n   void makefixed OF((void));\n#endif\nlocal unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,\n                              unsigned len));\n\nlocal int inflateStateCheck(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n    if (strm == Z_NULL ||\n        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)\n        return 1;\n    state = (struct inflate_state FAR *)strm->state;\n    if (state == Z_NULL || state->strm != strm ||\n        state->mode < HEAD || state->mode > SYNC)\n        return 1;\n    return 0;\n}\n\nint ZEXPORT inflateResetKeep(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    strm->total_in = strm->total_out = state->total = 0;\n    strm->msg = Z_NULL;\n    if (state->wrap)        /* to support ill-conceived Java test suite */\n        strm->adler = state->wrap & 1;\n    state->mode = HEAD;\n    state->last = 0;\n    state->havedict = 0;\n    state->dmax = 32768U;\n    state->head = Z_NULL;\n    state->hold = 0;\n    state->bits = 0;\n    state->lencode = state->distcode = state->next = state->codes;\n    state->sane = 1;\n    state->back = -1;\n    Tracev((stderr, \"inflate: reset\\n\"));\n    return Z_OK;\n}\n\nint ZEXPORT inflateReset(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    state->wsize = 0;\n    state->whave = 0;\n    state->wnext = 0;\n    return inflateResetKeep(strm);\n}\n\nint ZEXPORT inflateReset2(strm, windowBits)\nz_streamp strm;\nint windowBits;\n{\n    int wrap;\n    struct inflate_state FAR *state;\n\n    /* get the state */\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n\n    /* extract wrap request from windowBits parameter */\n    if (windowBits < 0) {\n        wrap = 0;\n        windowBits = -windowBits;\n    }\n    else {\n        wrap = (windowBits >> 4) + 5;\n#ifdef GUNZIP\n        if (windowBits < 48)\n            windowBits &= 15;\n#endif\n    }\n\n    /* set number of window bits, free window if different */\n    if (windowBits && (windowBits < 8 || windowBits > 15))\n        return Z_STREAM_ERROR;\n    if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {\n        ZFREE(strm, state->window);\n        state->window = Z_NULL;\n    }\n\n    /* update state and reset the rest of it */\n    state->wrap = wrap;\n    state->wbits = (unsigned)windowBits;\n    return inflateReset(strm);\n}\n\nint ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)\nz_streamp strm;\nint windowBits;\nconst char *version;\nint stream_size;\n{\n    int ret;\n    struct inflate_state FAR *state;\n\n    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||\n        stream_size != (int)(sizeof(z_stream)))\n        return Z_VERSION_ERROR;\n    if (strm == Z_NULL) return Z_STREAM_ERROR;\n    strm->msg = Z_NULL;                 /* in case we return an error */\n    if (strm->zalloc == (alloc_func)0) {\n#ifdef Z_SOLO\n        return Z_STREAM_ERROR;\n#else\n        strm->zalloc = zcalloc;\n        strm->opaque = (voidpf)0;\n#endif\n    }\n    if (strm->zfree == (free_func)0)\n#ifdef Z_SOLO\n        return Z_STREAM_ERROR;\n#else\n        strm->zfree = zcfree;\n#endif\n    state = (struct inflate_state FAR *)\n            ZALLOC(strm, 1, sizeof(struct inflate_state));\n    if (state == Z_NULL) return Z_MEM_ERROR;\n    Tracev((stderr, \"inflate: allocated\\n\"));\n    strm->state = (struct internal_state FAR *)state;\n    state->strm = strm;\n    state->window = Z_NULL;\n    state->mode = HEAD;     /* to pass state test in inflateReset2() */\n    ret = inflateReset2(strm, windowBits);\n    if (ret != Z_OK) {\n        ZFREE(strm, state);\n        strm->state = Z_NULL;\n    }\n    return ret;\n}\n\nint ZEXPORT inflateInit_(strm, version, stream_size)\nz_streamp strm;\nconst char *version;\nint stream_size;\n{\n    return inflateInit2_(strm, DEF_WBITS, version, stream_size);\n}\n\nint ZEXPORT inflatePrime(strm, bits, value)\nz_streamp strm;\nint bits;\nint value;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    if (bits < 0) {\n        state->hold = 0;\n        state->bits = 0;\n        return Z_OK;\n    }\n    if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR;\n    value &= (1L << bits) - 1;\n    state->hold += (unsigned)value << state->bits;\n    state->bits += (uInt)bits;\n    return Z_OK;\n}\n\n/*\n   Return state with length and distance decoding tables and index sizes set to\n   fixed code decoding.  Normally this returns fixed tables from inffixed.h.\n   If BUILDFIXED is defined, then instead this routine builds the tables the\n   first time it's called, and returns those tables the first time and\n   thereafter.  This reduces the size of the code by about 2K bytes, in\n   exchange for a little execution time.  However, BUILDFIXED should not be\n   used for threaded applications, since the rewriting of the tables and virgin\n   may not be thread-safe.\n */\nlocal void fixedtables(state)\nstruct inflate_state FAR *state;\n{\n#ifdef BUILDFIXED\n    static int virgin = 1;\n    static code *lenfix, *distfix;\n    static code fixed[544];\n\n    /* build fixed huffman tables if first call (may not be thread safe) */\n    if (virgin) {\n        unsigned sym, bits;\n        static code *next;\n\n        /* literal/length table */\n        sym = 0;\n        while (sym < 144) state->lens[sym++] = 8;\n        while (sym < 256) state->lens[sym++] = 9;\n        while (sym < 280) state->lens[sym++] = 7;\n        while (sym < 288) state->lens[sym++] = 8;\n        next = fixed;\n        lenfix = next;\n        bits = 9;\n        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);\n\n        /* distance table */\n        sym = 0;\n        while (sym < 32) state->lens[sym++] = 5;\n        distfix = next;\n        bits = 5;\n        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);\n\n        /* do this just once */\n        virgin = 0;\n    }\n#else /* !BUILDFIXED */\n#   include \"inffixed.h\"\n#endif /* BUILDFIXED */\n    state->lencode = lenfix;\n    state->lenbits = 9;\n    state->distcode = distfix;\n    state->distbits = 5;\n}\n\n#ifdef MAKEFIXED\n#include <stdio.h>\n\n/*\n   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also\n   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes\n   those tables to stdout, which would be piped to inffixed.h.  A small program\n   can simply call makefixed to do this:\n\n    void makefixed(void);\n\n    int main(void)\n    {\n        makefixed();\n        return 0;\n    }\n\n   Then that can be linked with zlib built with MAKEFIXED defined and run:\n\n    a.out > inffixed.h\n */\nvoid makefixed()\n{\n    unsigned low, size;\n    struct inflate_state state;\n\n    fixedtables(&state);\n    puts(\"    /* inffixed.h -- table for decoding fixed codes\");\n    puts(\"     * Generated automatically by makefixed().\");\n    puts(\"     */\");\n    puts(\"\");\n    puts(\"    /* WARNING: this file should *not* be used by applications.\");\n    puts(\"       It is part of the implementation of this library and is\");\n    puts(\"       subject to change. Applications should only use zlib.h.\");\n    puts(\"     */\");\n    puts(\"\");\n    size = 1U << 9;\n    printf(\"    static const code lenfix[%u] = {\", size);\n    low = 0;\n    for (;;) {\n        if ((low % 7) == 0) printf(\"\\n        \");\n        printf(\"{%u,%u,%d}\", (low & 127) == 99 ? 64 : state.lencode[low].op,\n               state.lencode[low].bits, state.lencode[low].val);\n        if (++low == size) break;\n        putchar(',');\n    }\n    puts(\"\\n    };\");\n    size = 1U << 5;\n    printf(\"\\n    static const code distfix[%u] = {\", size);\n    low = 0;\n    for (;;) {\n        if ((low % 6) == 0) printf(\"\\n        \");\n        printf(\"{%u,%u,%d}\", state.distcode[low].op, state.distcode[low].bits,\n               state.distcode[low].val);\n        if (++low == size) break;\n        putchar(',');\n    }\n    puts(\"\\n    };\");\n}\n#endif /* MAKEFIXED */\n\n/*\n   Update the window with the last wsize (normally 32K) bytes written before\n   returning.  If window does not exist yet, create it.  This is only called\n   when a window is already in use, or when output has been written during this\n   inflate call, but the end of the deflate stream has not been reached yet.\n   It is also called to create a window for dictionary data when a dictionary\n   is loaded.\n\n   Providing output buffers larger than 32K to inflate() should provide a speed\n   advantage, since only the last 32K of output is copied to the sliding window\n   upon return from inflate(), and since all distances after the first 32K of\n   output will fall in the output data, making match copies simpler and faster.\n   The advantage may be dependent on the size of the processor's data caches.\n */\nlocal int updatewindow(strm, end, copy)\nz_streamp strm;\nconst Bytef *end;\nunsigned copy;\n{\n    struct inflate_state FAR *state;\n    unsigned dist;\n\n    state = (struct inflate_state FAR *)strm->state;\n\n    /* if it hasn't been done already, allocate space for the window */\n    if (state->window == Z_NULL) {\n        state->window = (unsigned char FAR *)\n                        ZALLOC(strm, 1U << state->wbits,\n                               sizeof(unsigned char));\n        if (state->window == Z_NULL) return 1;\n    }\n\n    /* if window not in use yet, initialize */\n    if (state->wsize == 0) {\n        state->wsize = 1U << state->wbits;\n        state->wnext = 0;\n        state->whave = 0;\n    }\n\n    /* copy state->wsize or less output bytes into the circular window */\n    if (copy >= state->wsize) {\n        zmemcpy(state->window, end - state->wsize, state->wsize);\n        state->wnext = 0;\n        state->whave = state->wsize;\n    }\n    else {\n        dist = state->wsize - state->wnext;\n        if (dist > copy) dist = copy;\n        zmemcpy(state->window + state->wnext, end - copy, dist);\n        copy -= dist;\n        if (copy) {\n            zmemcpy(state->window, end - copy, copy);\n            state->wnext = copy;\n            state->whave = state->wsize;\n        }\n        else {\n            state->wnext += dist;\n            if (state->wnext == state->wsize) state->wnext = 0;\n            if (state->whave < state->wsize) state->whave += dist;\n        }\n    }\n    return 0;\n}\n\n/* Macros for inflate(): */\n\n/* check function to use adler32() for zlib or crc32() for gzip */\n#ifdef GUNZIP\n#  define UPDATE(check, buf, len) \\\n    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))\n#else\n#  define UPDATE(check, buf, len) adler32(check, buf, len)\n#endif\n\n/* check macros for header crc */\n#ifdef GUNZIP\n#  define CRC2(check, word) \\\n    do { \\\n        hbuf[0] = (unsigned char)(word); \\\n        hbuf[1] = (unsigned char)((word) >> 8); \\\n        check = crc32(check, hbuf, 2); \\\n    } while (0)\n\n#  define CRC4(check, word) \\\n    do { \\\n        hbuf[0] = (unsigned char)(word); \\\n        hbuf[1] = (unsigned char)((word) >> 8); \\\n        hbuf[2] = (unsigned char)((word) >> 16); \\\n        hbuf[3] = (unsigned char)((word) >> 24); \\\n        check = crc32(check, hbuf, 4); \\\n    } while (0)\n#endif\n\n/* Load registers with state in inflate() for speed */\n#define LOAD() \\\n    do { \\\n        put = strm->next_out; \\\n        left = strm->avail_out; \\\n        next = strm->next_in; \\\n        have = strm->avail_in; \\\n        hold = state->hold; \\\n        bits = state->bits; \\\n    } while (0)\n\n/* Restore state from registers in inflate() */\n#define RESTORE() \\\n    do { \\\n        strm->next_out = put; \\\n        strm->avail_out = left; \\\n        strm->next_in = next; \\\n        strm->avail_in = have; \\\n        state->hold = hold; \\\n        state->bits = bits; \\\n    } while (0)\n\n/* Clear the input bit accumulator */\n#define INITBITS() \\\n    do { \\\n        hold = 0; \\\n        bits = 0; \\\n    } while (0)\n\n/* Get a byte of input into the bit accumulator, or return from inflate()\n   if there is no input available. */\n#define PULLBYTE() \\\n    do { \\\n        if (have == 0) goto inf_leave; \\\n        have--; \\\n        hold += (unsigned long)(*next++) << bits; \\\n        bits += 8; \\\n    } while (0)\n\n/* Assure that there are at least n bits in the bit accumulator.  If there is\n   not enough available input to do that, then return from inflate(). */\n#define NEEDBITS(n) \\\n    do { \\\n        while (bits < (unsigned)(n)) \\\n            PULLBYTE(); \\\n    } while (0)\n\n/* Return the low n bits of the bit accumulator (n < 16) */\n#define BITS(n) \\\n    ((unsigned)hold & ((1U << (n)) - 1))\n\n/* Remove n bits from the bit accumulator */\n#define DROPBITS(n) \\\n    do { \\\n        hold >>= (n); \\\n        bits -= (unsigned)(n); \\\n    } while (0)\n\n/* Remove zero to seven bits as needed to go to a byte boundary */\n#define BYTEBITS() \\\n    do { \\\n        hold >>= bits & 7; \\\n        bits -= bits & 7; \\\n    } while (0)\n\n/*\n   inflate() uses a state machine to process as much input data and generate as\n   much output data as possible before returning.  The state machine is\n   structured roughly as follows:\n\n    for (;;) switch (state) {\n    ...\n    case STATEn:\n        if (not enough input data or output space to make progress)\n            return;\n        ... make progress ...\n        state = STATEm;\n        break;\n    ...\n    }\n\n   so when inflate() is called again, the same case is attempted again, and\n   if the appropriate resources are provided, the machine proceeds to the\n   next state.  The NEEDBITS() macro is usually the way the state evaluates\n   whether it can proceed or should return.  NEEDBITS() does the return if\n   the requested bits are not available.  The typical use of the BITS macros\n   is:\n\n        NEEDBITS(n);\n        ... do something with BITS(n) ...\n        DROPBITS(n);\n\n   where NEEDBITS(n) either returns from inflate() if there isn't enough\n   input left to load n bits into the accumulator, or it continues.  BITS(n)\n   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops\n   the low n bits off the accumulator.  INITBITS() clears the accumulator\n   and sets the number of available bits to zero.  BYTEBITS() discards just\n   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()\n   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.\n\n   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return\n   if there is no input available.  The decoding of variable length codes uses\n   PULLBYTE() directly in order to pull just enough bytes to decode the next\n   code, and no more.\n\n   Some states loop until they get enough input, making sure that enough\n   state information is maintained to continue the loop where it left off\n   if NEEDBITS() returns in the loop.  For example, want, need, and keep\n   would all have to actually be part of the saved state in case NEEDBITS()\n   returns:\n\n    case STATEw:\n        while (want < need) {\n            NEEDBITS(n);\n            keep[want++] = BITS(n);\n            DROPBITS(n);\n        }\n        state = STATEx;\n    case STATEx:\n\n   As shown above, if the next state is also the next case, then the break\n   is omitted.\n\n   A state may also return if there is not enough output space available to\n   complete that state.  Those states are copying stored data, writing a\n   literal byte, and copying a matching string.\n\n   When returning, a \"goto inf_leave\" is used to update the total counters,\n   update the check value, and determine whether any progress has been made\n   during that inflate() call in order to return the proper return code.\n   Progress is defined as a change in either strm->avail_in or strm->avail_out.\n   When there is a window, goto inf_leave will update the window with the last\n   output written.  If a goto inf_leave occurs in the middle of decompression\n   and there is no window currently, goto inf_leave will create one and copy\n   output to the window for the next call of inflate().\n\n   In this implementation, the flush parameter of inflate() only affects the\n   return code (per zlib.h).  inflate() always writes as much as possible to\n   strm->next_out, given the space available and the provided input--the effect\n   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers\n   the allocation of and copying into a sliding window until necessary, which\n   provides the effect documented in zlib.h for Z_FINISH when the entire input\n   stream available.  So the only thing the flush parameter actually does is:\n   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it\n   will return Z_BUF_ERROR if it has not reached the end of the stream.\n */\n\nint ZEXPORT inflate(strm, flush)\nz_streamp strm;\nint flush;\n{\n    struct inflate_state FAR *state;\n    z_const unsigned char FAR *next;    /* next input */\n    unsigned char FAR *put;     /* next output */\n    unsigned have, left;        /* available input and output */\n    unsigned long hold;         /* bit buffer */\n    unsigned bits;              /* bits in bit buffer */\n    unsigned in, out;           /* save starting available input and output */\n    unsigned copy;              /* number of stored or match bytes to copy */\n    unsigned char FAR *from;    /* where to copy match bytes from */\n    code here;                  /* current decoding table entry */\n    code last;                  /* parent table entry */\n    unsigned len;               /* length to copy for repeats, bits to drop */\n    int ret;                    /* return code */\n#ifdef GUNZIP\n    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */\n#endif\n    static const unsigned short order[19] = /* permutation of code lengths */\n        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};\n\n    if (inflateStateCheck(strm) || strm->next_out == Z_NULL ||\n        (strm->next_in == Z_NULL && strm->avail_in != 0))\n        return Z_STREAM_ERROR;\n\n    state = (struct inflate_state FAR *)strm->state;\n    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */\n    LOAD();\n    in = have;\n    out = left;\n    ret = Z_OK;\n    for (;;)\n        switch (state->mode) {\n        case HEAD:\n            if (state->wrap == 0) {\n                state->mode = TYPEDO;\n                break;\n            }\n            NEEDBITS(16);\n#ifdef GUNZIP\n            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */\n                if (state->wbits == 0)\n                    state->wbits = 15;\n                state->check = crc32(0L, Z_NULL, 0);\n                CRC2(state->check, hold);\n                INITBITS();\n                state->mode = FLAGS;\n                break;\n            }\n            state->flags = 0;           /* expect zlib header */\n            if (state->head != Z_NULL)\n                state->head->done = -1;\n            if (!(state->wrap & 1) ||   /* check if zlib header allowed */\n#else\n            if (\n#endif\n                ((BITS(8) << 8) + (hold >> 8)) % 31) {\n                strm->msg = (char *)\"incorrect header check\";\n                state->mode = BAD;\n                break;\n            }\n            if (BITS(4) != Z_DEFLATED) {\n                strm->msg = (char *)\"unknown compression method\";\n                state->mode = BAD;\n                break;\n            }\n            DROPBITS(4);\n            len = BITS(4) + 8;\n            if (state->wbits == 0)\n                state->wbits = len;\n            if (len > 15 || len > state->wbits) {\n                strm->msg = (char *)\"invalid window size\";\n                state->mode = BAD;\n                break;\n            }\n            state->dmax = 1U << len;\n            Tracev((stderr, \"inflate:   zlib header ok\\n\"));\n            strm->adler = state->check = adler32(0L, Z_NULL, 0);\n            state->mode = hold & 0x200 ? DICTID : TYPE;\n            INITBITS();\n            break;\n#ifdef GUNZIP\n        case FLAGS:\n            NEEDBITS(16);\n            state->flags = (int)(hold);\n            if ((state->flags & 0xff) != Z_DEFLATED) {\n                strm->msg = (char *)\"unknown compression method\";\n                state->mode = BAD;\n                break;\n            }\n            if (state->flags & 0xe000) {\n                strm->msg = (char *)\"unknown header flags set\";\n                state->mode = BAD;\n                break;\n            }\n            if (state->head != Z_NULL)\n                state->head->text = (int)((hold >> 8) & 1);\n            if ((state->flags & 0x0200) && (state->wrap & 4))\n                CRC2(state->check, hold);\n            INITBITS();\n            state->mode = TIME;\n        case TIME:\n            NEEDBITS(32);\n            if (state->head != Z_NULL)\n                state->head->time = hold;\n            if ((state->flags & 0x0200) && (state->wrap & 4))\n                CRC4(state->check, hold);\n            INITBITS();\n            state->mode = OS;\n        case OS:\n            NEEDBITS(16);\n            if (state->head != Z_NULL) {\n                state->head->xflags = (int)(hold & 0xff);\n                state->head->os = (int)(hold >> 8);\n            }\n            if ((state->flags & 0x0200) && (state->wrap & 4))\n                CRC2(state->check, hold);\n            INITBITS();\n            state->mode = EXLEN;\n        case EXLEN:\n            if (state->flags & 0x0400) {\n                NEEDBITS(16);\n                state->length = (unsigned)(hold);\n                if (state->head != Z_NULL)\n                    state->head->extra_len = (unsigned)hold;\n                if ((state->flags & 0x0200) && (state->wrap & 4))\n                    CRC2(state->check, hold);\n                INITBITS();\n            }\n            else if (state->head != Z_NULL)\n                state->head->extra = Z_NULL;\n            state->mode = EXTRA;\n        case EXTRA:\n            if (state->flags & 0x0400) {\n                copy = state->length;\n                if (copy > have) copy = have;\n                if (copy) {\n                    if (state->head != Z_NULL &&\n                        state->head->extra != Z_NULL) {\n                        len = state->head->extra_len - state->length;\n                        zmemcpy(state->head->extra + len, next,\n                                len + copy > state->head->extra_max ?\n                                state->head->extra_max - len : copy);\n                    }\n                    if ((state->flags & 0x0200) && (state->wrap & 4))\n                        state->check = crc32(state->check, next, copy);\n                    have -= copy;\n                    next += copy;\n                    state->length -= copy;\n                }\n                if (state->length) goto inf_leave;\n            }\n            state->length = 0;\n            state->mode = NAME;\n        case NAME:\n            if (state->flags & 0x0800) {\n                if (have == 0) goto inf_leave;\n                copy = 0;\n                do {\n                    len = (unsigned)(next[copy++]);\n                    if (state->head != Z_NULL &&\n                            state->head->name != Z_NULL &&\n                            state->length < state->head->name_max)\n                        state->head->name[state->length++] = (Bytef)len;\n                } while (len && copy < have);\n                if ((state->flags & 0x0200) && (state->wrap & 4))\n                    state->check = crc32(state->check, next, copy);\n                have -= copy;\n                next += copy;\n                if (len) goto inf_leave;\n            }\n            else if (state->head != Z_NULL)\n                state->head->name = Z_NULL;\n            state->length = 0;\n            state->mode = COMMENT;\n        case COMMENT:\n            if (state->flags & 0x1000) {\n                if (have == 0) goto inf_leave;\n                copy = 0;\n                do {\n                    len = (unsigned)(next[copy++]);\n                    if (state->head != Z_NULL &&\n                            state->head->comment != Z_NULL &&\n                            state->length < state->head->comm_max)\n                        state->head->comment[state->length++] = (Bytef)len;\n                } while (len && copy < have);\n                if ((state->flags & 0x0200) && (state->wrap & 4))\n                    state->check = crc32(state->check, next, copy);\n                have -= copy;\n                next += copy;\n                if (len) goto inf_leave;\n            }\n            else if (state->head != Z_NULL)\n                state->head->comment = Z_NULL;\n            state->mode = HCRC;\n        case HCRC:\n            if (state->flags & 0x0200) {\n                NEEDBITS(16);\n                if ((state->wrap & 4) && hold != (state->check & 0xffff)) {\n                    strm->msg = (char *)\"header crc mismatch\";\n                    state->mode = BAD;\n                    break;\n                }\n                INITBITS();\n            }\n            if (state->head != Z_NULL) {\n                state->head->hcrc = (int)((state->flags >> 9) & 1);\n                state->head->done = 1;\n            }\n            strm->adler = state->check = crc32(0L, Z_NULL, 0);\n            state->mode = TYPE;\n            break;\n#endif\n        case DICTID:\n            NEEDBITS(32);\n            strm->adler = state->check = ZSWAP32(hold);\n            INITBITS();\n            state->mode = DICT;\n        case DICT:\n            if (state->havedict == 0) {\n                RESTORE();\n                return Z_NEED_DICT;\n            }\n            strm->adler = state->check = adler32(0L, Z_NULL, 0);\n            state->mode = TYPE;\n        case TYPE:\n            if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;\n        case TYPEDO:\n            if (state->last) {\n                BYTEBITS();\n                state->mode = CHECK;\n                break;\n            }\n            NEEDBITS(3);\n            state->last = BITS(1);\n            DROPBITS(1);\n            switch (BITS(2)) {\n            case 0:                             /* stored block */\n                Tracev((stderr, \"inflate:     stored block%s\\n\",\n                        state->last ? \" (last)\" : \"\"));\n                state->mode = STORED;\n                break;\n            case 1:                             /* fixed block */\n                fixedtables(state);\n                Tracev((stderr, \"inflate:     fixed codes block%s\\n\",\n                        state->last ? \" (last)\" : \"\"));\n                state->mode = LEN_;             /* decode codes */\n                if (flush == Z_TREES) {\n                    DROPBITS(2);\n                    goto inf_leave;\n                }\n                break;\n            case 2:                             /* dynamic block */\n                Tracev((stderr, \"inflate:     dynamic codes block%s\\n\",\n                        state->last ? \" (last)\" : \"\"));\n                state->mode = TABLE;\n                break;\n            case 3:\n                strm->msg = (char *)\"invalid block type\";\n                state->mode = BAD;\n            }\n            DROPBITS(2);\n            break;\n        case STORED:\n            BYTEBITS();                         /* go to byte boundary */\n            NEEDBITS(32);\n            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {\n                strm->msg = (char *)\"invalid stored block lengths\";\n                state->mode = BAD;\n                break;\n            }\n            state->length = (unsigned)hold & 0xffff;\n            Tracev((stderr, \"inflate:       stored length %u\\n\",\n                    state->length));\n            INITBITS();\n            state->mode = COPY_;\n            if (flush == Z_TREES) goto inf_leave;\n        case COPY_:\n            state->mode = COPY;\n        case COPY:\n            copy = state->length;\n            if (copy) {\n                if (copy > have) copy = have;\n                if (copy > left) copy = left;\n                if (copy == 0) goto inf_leave;\n                zmemcpy(put, next, copy);\n                have -= copy;\n                next += copy;\n                left -= copy;\n                put += copy;\n                state->length -= copy;\n                break;\n            }\n            Tracev((stderr, \"inflate:       stored end\\n\"));\n            state->mode = TYPE;\n            break;\n        case TABLE:\n            NEEDBITS(14);\n            state->nlen = BITS(5) + 257;\n            DROPBITS(5);\n            state->ndist = BITS(5) + 1;\n            DROPBITS(5);\n            state->ncode = BITS(4) + 4;\n            DROPBITS(4);\n#ifndef PKZIP_BUG_WORKAROUND\n            if (state->nlen > 286 || state->ndist > 30) {\n                strm->msg = (char *)\"too many length or distance symbols\";\n                state->mode = BAD;\n                break;\n            }\n#endif\n            Tracev((stderr, \"inflate:       table sizes ok\\n\"));\n            state->have = 0;\n            state->mode = LENLENS;\n        case LENLENS:\n            while (state->have < state->ncode) {\n                NEEDBITS(3);\n                state->lens[order[state->have++]] = (unsigned short)BITS(3);\n                DROPBITS(3);\n            }\n            while (state->have < 19)\n                state->lens[order[state->have++]] = 0;\n            state->next = state->codes;\n            state->lencode = (const code FAR *)(state->next);\n            state->lenbits = 7;\n            ret = inflate_table(CODES, state->lens, 19, &(state->next),\n                                &(state->lenbits), state->work);\n            if (ret) {\n                strm->msg = (char *)\"invalid code lengths set\";\n                state->mode = BAD;\n                break;\n            }\n            Tracev((stderr, \"inflate:       code lengths ok\\n\"));\n            state->have = 0;\n            state->mode = CODELENS;\n        case CODELENS:\n            while (state->have < state->nlen + state->ndist) {\n                for (;;) {\n                    here = state->lencode[BITS(state->lenbits)];\n                    if ((unsigned)(here.bits) <= bits) break;\n                    PULLBYTE();\n                }\n                if (here.val < 16) {\n                    DROPBITS(here.bits);\n                    state->lens[state->have++] = here.val;\n                }\n                else {\n                    if (here.val == 16) {\n                        NEEDBITS(here.bits + 2);\n                        DROPBITS(here.bits);\n                        if (state->have == 0) {\n                            strm->msg = (char *)\"invalid bit length repeat\";\n                            state->mode = BAD;\n                            break;\n                        }\n                        len = state->lens[state->have - 1];\n                        copy = 3 + BITS(2);\n                        DROPBITS(2);\n                    }\n                    else if (here.val == 17) {\n                        NEEDBITS(here.bits + 3);\n                        DROPBITS(here.bits);\n                        len = 0;\n                        copy = 3 + BITS(3);\n                        DROPBITS(3);\n                    }\n                    else {\n                        NEEDBITS(here.bits + 7);\n                        DROPBITS(here.bits);\n                        len = 0;\n                        copy = 11 + BITS(7);\n                        DROPBITS(7);\n                    }\n                    if (state->have + copy > state->nlen + state->ndist) {\n                        strm->msg = (char *)\"invalid bit length repeat\";\n                        state->mode = BAD;\n                        break;\n                    }\n                    while (copy--)\n                        state->lens[state->have++] = (unsigned short)len;\n                }\n            }\n\n            /* handle error breaks in while */\n            if (state->mode == BAD) break;\n\n            /* check for end-of-block code (better have one) */\n            if (state->lens[256] == 0) {\n                strm->msg = (char *)\"invalid code -- missing end-of-block\";\n                state->mode = BAD;\n                break;\n            }\n\n            /* build code tables -- note: do not change the lenbits or distbits\n               values here (9 and 6) without reading the comments in inftrees.h\n               concerning the ENOUGH constants, which depend on those values */\n            state->next = state->codes;\n            state->lencode = (const code FAR *)(state->next);\n            state->lenbits = 9;\n            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),\n                                &(state->lenbits), state->work);\n            if (ret) {\n                strm->msg = (char *)\"invalid literal/lengths set\";\n                state->mode = BAD;\n                break;\n            }\n            state->distcode = (const code FAR *)(state->next);\n            state->distbits = 6;\n            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,\n                            &(state->next), &(state->distbits), state->work);\n            if (ret) {\n                strm->msg = (char *)\"invalid distances set\";\n                state->mode = BAD;\n                break;\n            }\n            Tracev((stderr, \"inflate:       codes ok\\n\"));\n            state->mode = LEN_;\n            if (flush == Z_TREES) goto inf_leave;\n        case LEN_:\n            state->mode = LEN;\n        case LEN:\n            if (have >= 6 && left >= 258) {\n                RESTORE();\n                inflate_fast(strm, out);\n                LOAD();\n                if (state->mode == TYPE)\n                    state->back = -1;\n                break;\n            }\n            state->back = 0;\n            for (;;) {\n                here = state->lencode[BITS(state->lenbits)];\n                if ((unsigned)(here.bits) <= bits) break;\n                PULLBYTE();\n            }\n            if (here.op && (here.op & 0xf0) == 0) {\n                last = here;\n                for (;;) {\n                    here = state->lencode[last.val +\n                            (BITS(last.bits + last.op) >> last.bits)];\n                    if ((unsigned)(last.bits + here.bits) <= bits) break;\n                    PULLBYTE();\n                }\n                DROPBITS(last.bits);\n                state->back += last.bits;\n            }\n            DROPBITS(here.bits);\n            state->back += here.bits;\n            state->length = (unsigned)here.val;\n            if ((int)(here.op) == 0) {\n                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n                        \"inflate:         literal '%c'\\n\" :\n                        \"inflate:         literal 0x%02x\\n\", here.val));\n                state->mode = LIT;\n                break;\n            }\n            if (here.op & 32) {\n                Tracevv((stderr, \"inflate:         end of block\\n\"));\n                state->back = -1;\n                state->mode = TYPE;\n                break;\n            }\n            if (here.op & 64) {\n                strm->msg = (char *)\"invalid literal/length code\";\n                state->mode = BAD;\n                break;\n            }\n            state->extra = (unsigned)(here.op) & 15;\n            state->mode = LENEXT;\n        case LENEXT:\n            if (state->extra) {\n                NEEDBITS(state->extra);\n                state->length += BITS(state->extra);\n                DROPBITS(state->extra);\n                state->back += state->extra;\n            }\n            Tracevv((stderr, \"inflate:         length %u\\n\", state->length));\n            state->was = state->length;\n            state->mode = DIST;\n        case DIST:\n            for (;;) {\n                here = state->distcode[BITS(state->distbits)];\n                if ((unsigned)(here.bits) <= bits) break;\n                PULLBYTE();\n            }\n            if ((here.op & 0xf0) == 0) {\n                last = here;\n                for (;;) {\n                    here = state->distcode[last.val +\n                            (BITS(last.bits + last.op) >> last.bits)];\n                    if ((unsigned)(last.bits + here.bits) <= bits) break;\n                    PULLBYTE();\n                }\n                DROPBITS(last.bits);\n                state->back += last.bits;\n            }\n            DROPBITS(here.bits);\n            state->back += here.bits;\n            if (here.op & 64) {\n                strm->msg = (char *)\"invalid distance code\";\n                state->mode = BAD;\n                break;\n            }\n            state->offset = (unsigned)here.val;\n            state->extra = (unsigned)(here.op) & 15;\n            state->mode = DISTEXT;\n        case DISTEXT:\n            if (state->extra) {\n                NEEDBITS(state->extra);\n                state->offset += BITS(state->extra);\n                DROPBITS(state->extra);\n                state->back += state->extra;\n            }\n#ifdef INFLATE_STRICT\n            if (state->offset > state->dmax) {\n                strm->msg = (char *)\"invalid distance too far back\";\n                state->mode = BAD;\n                break;\n            }\n#endif\n            Tracevv((stderr, \"inflate:         distance %u\\n\", state->offset));\n            state->mode = MATCH;\n        case MATCH:\n            if (left == 0) goto inf_leave;\n            copy = out - left;\n            if (state->offset > copy) {         /* copy from window */\n                copy = state->offset - copy;\n                if (copy > state->whave) {\n                    if (state->sane) {\n                        strm->msg = (char *)\"invalid distance too far back\";\n                        state->mode = BAD;\n                        break;\n                    }\n#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n                    Trace((stderr, \"inflate.c too far\\n\"));\n                    copy -= state->whave;\n                    if (copy > state->length) copy = state->length;\n                    if (copy > left) copy = left;\n                    left -= copy;\n                    state->length -= copy;\n                    do {\n                        *put++ = 0;\n                    } while (--copy);\n                    if (state->length == 0) state->mode = LEN;\n                    break;\n#endif\n                }\n                if (copy > state->wnext) {\n                    copy -= state->wnext;\n                    from = state->window + (state->wsize - copy);\n                }\n                else\n                    from = state->window + (state->wnext - copy);\n                if (copy > state->length) copy = state->length;\n            }\n            else {                              /* copy from output */\n                from = put - state->offset;\n                copy = state->length;\n            }\n            if (copy > left) copy = left;\n            left -= copy;\n            state->length -= copy;\n            do {\n                *put++ = *from++;\n            } while (--copy);\n            if (state->length == 0) state->mode = LEN;\n            break;\n        case LIT:\n            if (left == 0) goto inf_leave;\n            *put++ = (unsigned char)(state->length);\n            left--;\n            state->mode = LEN;\n            break;\n        case CHECK:\n            if (state->wrap) {\n                NEEDBITS(32);\n                out -= left;\n                strm->total_out += out;\n                state->total += out;\n                if ((state->wrap & 4) && out)\n                    strm->adler = state->check =\n                        UPDATE(state->check, put - out, out);\n                out = left;\n                if ((state->wrap & 4) && (\n#ifdef GUNZIP\n                     state->flags ? hold :\n#endif\n                     ZSWAP32(hold)) != state->check) {\n                    strm->msg = (char *)\"incorrect data check\";\n                    state->mode = BAD;\n                    break;\n                }\n                INITBITS();\n                Tracev((stderr, \"inflate:   check matches trailer\\n\"));\n            }\n#ifdef GUNZIP\n            state->mode = LENGTH;\n        case LENGTH:\n            if (state->wrap && state->flags) {\n                NEEDBITS(32);\n                if (hold != (state->total & 0xffffffffUL)) {\n                    strm->msg = (char *)\"incorrect length check\";\n                    state->mode = BAD;\n                    break;\n                }\n                INITBITS();\n                Tracev((stderr, \"inflate:   length matches trailer\\n\"));\n            }\n#endif\n            state->mode = DONE;\n        case DONE:\n            ret = Z_STREAM_END;\n            goto inf_leave;\n        case BAD:\n            ret = Z_DATA_ERROR;\n            goto inf_leave;\n        case MEM:\n            return Z_MEM_ERROR;\n        case SYNC:\n        default:\n            return Z_STREAM_ERROR;\n        }\n\n    /*\n       Return from inflate(), updating the total counts and the check value.\n       If there was no progress during the inflate() call, return a buffer\n       error.  Call updatewindow() to create and/or update the window state.\n       Note: a memory error from inflate() is non-recoverable.\n     */\n  inf_leave:\n    RESTORE();\n    if (state->wsize || (out != strm->avail_out && state->mode < BAD &&\n            (state->mode < CHECK || flush != Z_FINISH)))\n        if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {\n            state->mode = MEM;\n            return Z_MEM_ERROR;\n        }\n    in -= strm->avail_in;\n    out -= strm->avail_out;\n    strm->total_in += in;\n    strm->total_out += out;\n    state->total += out;\n    if ((state->wrap & 4) && out)\n        strm->adler = state->check =\n            UPDATE(state->check, strm->next_out - out, out);\n    strm->data_type = (int)state->bits + (state->last ? 64 : 0) +\n                      (state->mode == TYPE ? 128 : 0) +\n                      (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);\n    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)\n        ret = Z_BUF_ERROR;\n    return ret;\n}\n\nint ZEXPORT inflateEnd(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n    if (inflateStateCheck(strm))\n        return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    if (state->window != Z_NULL) ZFREE(strm, state->window);\n    ZFREE(strm, strm->state);\n    strm->state = Z_NULL;\n    Tracev((stderr, \"inflate: end\\n\"));\n    return Z_OK;\n}\n\nint ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)\nz_streamp strm;\nBytef *dictionary;\nuInt *dictLength;\n{\n    struct inflate_state FAR *state;\n\n    /* check state */\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n\n    /* copy dictionary */\n    if (state->whave && dictionary != Z_NULL) {\n        zmemcpy(dictionary, state->window + state->wnext,\n                state->whave - state->wnext);\n        zmemcpy(dictionary + state->whave - state->wnext,\n                state->window, state->wnext);\n    }\n    if (dictLength != Z_NULL)\n        *dictLength = state->whave;\n    return Z_OK;\n}\n\nint ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)\nz_streamp strm;\nconst Bytef *dictionary;\nuInt dictLength;\n{\n    struct inflate_state FAR *state;\n    unsigned long dictid;\n    int ret;\n\n    /* check state */\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    if (state->wrap != 0 && state->mode != DICT)\n        return Z_STREAM_ERROR;\n\n    /* check for correct dictionary identifier */\n    if (state->mode == DICT) {\n        dictid = adler32(0L, Z_NULL, 0);\n        dictid = adler32(dictid, dictionary, dictLength);\n        if (dictid != state->check)\n            return Z_DATA_ERROR;\n    }\n\n    /* copy dictionary to window using updatewindow(), which will amend the\n       existing dictionary if appropriate */\n    ret = updatewindow(strm, dictionary + dictLength, dictLength);\n    if (ret) {\n        state->mode = MEM;\n        return Z_MEM_ERROR;\n    }\n    state->havedict = 1;\n    Tracev((stderr, \"inflate:   dictionary set\\n\"));\n    return Z_OK;\n}\n\nint ZEXPORT inflateGetHeader(strm, head)\nz_streamp strm;\ngz_headerp head;\n{\n    struct inflate_state FAR *state;\n\n    /* check state */\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;\n\n    /* save header structure */\n    state->head = head;\n    head->done = 0;\n    return Z_OK;\n}\n\n/*\n   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found\n   or when out of input.  When called, *have is the number of pattern bytes\n   found in order so far, in 0..3.  On return *have is updated to the new\n   state.  If on return *have equals four, then the pattern was found and the\n   return value is how many bytes were read including the last byte of the\n   pattern.  If *have is less than four, then the pattern has not been found\n   yet and the return value is len.  In the latter case, syncsearch() can be\n   called again with more data and the *have state.  *have is initialized to\n   zero for the first call.\n */\nlocal unsigned syncsearch(have, buf, len)\nunsigned FAR *have;\nconst unsigned char FAR *buf;\nunsigned len;\n{\n    unsigned got;\n    unsigned next;\n\n    got = *have;\n    next = 0;\n    while (next < len && got < 4) {\n        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))\n            got++;\n        else if (buf[next])\n            got = 0;\n        else\n            got = 4 - got;\n        next++;\n    }\n    *have = got;\n    return next;\n}\n\nint ZEXPORT inflateSync(strm)\nz_streamp strm;\n{\n    unsigned len;               /* number of bytes to look at or looked at */\n    unsigned long in, out;      /* temporary to save total_in and total_out */\n    unsigned char buf[4];       /* to restore bit buffer to byte string */\n    struct inflate_state FAR *state;\n\n    /* check parameters */\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;\n\n    /* if first time, start search in bit buffer */\n    if (state->mode != SYNC) {\n        state->mode = SYNC;\n        state->hold <<= state->bits & 7;\n        state->bits -= state->bits & 7;\n        len = 0;\n        while (state->bits >= 8) {\n            buf[len++] = (unsigned char)(state->hold);\n            state->hold >>= 8;\n            state->bits -= 8;\n        }\n        state->have = 0;\n        syncsearch(&(state->have), buf, len);\n    }\n\n    /* search available input */\n    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);\n    strm->avail_in -= len;\n    strm->next_in += len;\n    strm->total_in += len;\n\n    /* return no joy or set up to restart inflate() on a new block */\n    if (state->have != 4) return Z_DATA_ERROR;\n    in = strm->total_in;  out = strm->total_out;\n    inflateReset(strm);\n    strm->total_in = in;  strm->total_out = out;\n    state->mode = TYPE;\n    return Z_OK;\n}\n\n/*\n   Returns true if inflate is currently at the end of a block generated by\n   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP\n   implementation to provide an additional safety check. PPP uses\n   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored\n   block. When decompressing, PPP checks that at the end of input packet,\n   inflate is waiting for these length bytes.\n */\nint ZEXPORT inflateSyncPoint(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    return state->mode == STORED && state->bits == 0;\n}\n\nint ZEXPORT inflateCopy(dest, source)\nz_streamp dest;\nz_streamp source;\n{\n    struct inflate_state FAR *state;\n    struct inflate_state FAR *copy;\n    unsigned char FAR *window;\n    unsigned wsize;\n\n    /* check input */\n    if (inflateStateCheck(source) || dest == Z_NULL)\n        return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)source->state;\n\n    /* allocate space */\n    copy = (struct inflate_state FAR *)\n           ZALLOC(source, 1, sizeof(struct inflate_state));\n    if (copy == Z_NULL) return Z_MEM_ERROR;\n    window = Z_NULL;\n    if (state->window != Z_NULL) {\n        window = (unsigned char FAR *)\n                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));\n        if (window == Z_NULL) {\n            ZFREE(source, copy);\n            return Z_MEM_ERROR;\n        }\n    }\n\n    /* copy state */\n    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));\n    zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));\n    copy->strm = dest;\n    if (state->lencode >= state->codes &&\n        state->lencode <= state->codes + ENOUGH - 1) {\n        copy->lencode = copy->codes + (state->lencode - state->codes);\n        copy->distcode = copy->codes + (state->distcode - state->codes);\n    }\n    copy->next = copy->codes + (state->next - state->codes);\n    if (window != Z_NULL) {\n        wsize = 1U << state->wbits;\n        zmemcpy(window, state->window, wsize);\n    }\n    copy->window = window;\n    dest->state = (struct internal_state FAR *)copy;\n    return Z_OK;\n}\n\nint ZEXPORT inflateUndermine(strm, subvert)\nz_streamp strm;\nint subvert;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n    state->sane = !subvert;\n    return Z_OK;\n#else\n    (void)subvert;\n    state->sane = 1;\n    return Z_DATA_ERROR;\n#endif\n}\n\nint ZEXPORT inflateValidate(strm, check)\nz_streamp strm;\nint check;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;\n    state = (struct inflate_state FAR *)strm->state;\n    if (check)\n        state->wrap |= 4;\n    else\n        state->wrap &= ~4;\n    return Z_OK;\n}\n\nlong ZEXPORT inflateMark(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n\n    if (inflateStateCheck(strm))\n        return -(1L << 16);\n    state = (struct inflate_state FAR *)strm->state;\n    return (long)(((unsigned long)((long)state->back)) << 16) +\n        (state->mode == COPY ? state->length :\n            (state->mode == MATCH ? state->was - state->length : 0));\n}\n\nunsigned long ZEXPORT inflateCodesUsed(strm)\nz_streamp strm;\n{\n    struct inflate_state FAR *state;\n    if (inflateStateCheck(strm)) return (unsigned long)-1;\n    state = (struct inflate_state FAR *)strm->state;\n    return (unsigned long)(state->next - state->codes);\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inflate.h",
    "content": "/* inflate.h -- internal inflate state definition\n * Copyright (C) 1995-2016 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* WARNING: this file should *not* be used by applications. It is\n   part of the implementation of the compression library and is\n   subject to change. Applications should only use zlib.h.\n */\n\n/* define NO_GZIP when compiling if you want to disable gzip header and\n   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in\n   the crc code when it is not needed.  For shared libraries, gzip decoding\n   should be left enabled. */\n#ifndef NO_GZIP\n#  define GUNZIP\n#endif\n\n/* Possible inflate modes between inflate() calls */\ntypedef enum {\n    HEAD = 16180,   /* i: waiting for magic header */\n    FLAGS,      /* i: waiting for method and flags (gzip) */\n    TIME,       /* i: waiting for modification time (gzip) */\n    OS,         /* i: waiting for extra flags and operating system (gzip) */\n    EXLEN,      /* i: waiting for extra length (gzip) */\n    EXTRA,      /* i: waiting for extra bytes (gzip) */\n    NAME,       /* i: waiting for end of file name (gzip) */\n    COMMENT,    /* i: waiting for end of comment (gzip) */\n    HCRC,       /* i: waiting for header crc (gzip) */\n    DICTID,     /* i: waiting for dictionary check value */\n    DICT,       /* waiting for inflateSetDictionary() call */\n        TYPE,       /* i: waiting for type bits, including last-flag bit */\n        TYPEDO,     /* i: same, but skip check to exit inflate on new block */\n        STORED,     /* i: waiting for stored size (length and complement) */\n        COPY_,      /* i/o: same as COPY below, but only first time in */\n        COPY,       /* i/o: waiting for input or output to copy stored block */\n        TABLE,      /* i: waiting for dynamic block table lengths */\n        LENLENS,    /* i: waiting for code length code lengths */\n        CODELENS,   /* i: waiting for length/lit and distance code lengths */\n            LEN_,       /* i: same as LEN below, but only first time in */\n            LEN,        /* i: waiting for length/lit/eob code */\n            LENEXT,     /* i: waiting for length extra bits */\n            DIST,       /* i: waiting for distance code */\n            DISTEXT,    /* i: waiting for distance extra bits */\n            MATCH,      /* o: waiting for output space to copy string */\n            LIT,        /* o: waiting for output space to write literal */\n    CHECK,      /* i: waiting for 32-bit check value */\n    LENGTH,     /* i: waiting for 32-bit length (gzip) */\n    DONE,       /* finished check, done -- remain here until reset */\n    BAD,        /* got a data error -- remain here until reset */\n    MEM,        /* got an inflate() memory error -- remain here until reset */\n    SYNC        /* looking for synchronization bytes to restart inflate() */\n} inflate_mode;\n\n/*\n    State transitions between above modes -\n\n    (most modes can go to BAD or MEM on error -- not shown for clarity)\n\n    Process header:\n        HEAD -> (gzip) or (zlib) or (raw)\n        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->\n                  HCRC -> TYPE\n        (zlib) -> DICTID or TYPE\n        DICTID -> DICT -> TYPE\n        (raw) -> TYPEDO\n    Read deflate blocks:\n            TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK\n            STORED -> COPY_ -> COPY -> TYPE\n            TABLE -> LENLENS -> CODELENS -> LEN_\n            LEN_ -> LEN\n    Read deflate codes in fixed or dynamic block:\n                LEN -> LENEXT or LIT or TYPE\n                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN\n                LIT -> LEN\n    Process trailer:\n        CHECK -> LENGTH -> DONE\n */\n\n/* State maintained between inflate() calls -- approximately 7K bytes, not\n   including the allocated sliding window, which is up to 32K bytes. */\nstruct inflate_state {\n    z_streamp strm;             /* pointer back to this zlib stream */\n    inflate_mode mode;          /* current inflate mode */\n    int last;                   /* true if processing last block */\n    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip,\n                                   bit 2 true to validate check value */\n    int havedict;               /* true if dictionary provided */\n    int flags;                  /* gzip header method and flags (0 if zlib) */\n    unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */\n    unsigned long check;        /* protected copy of check value */\n    unsigned long total;        /* protected copy of output count */\n    gz_headerp head;            /* where to save gzip header information */\n        /* sliding window */\n    unsigned wbits;             /* log base 2 of requested window size */\n    unsigned wsize;             /* window size or zero if not using window */\n    unsigned whave;             /* valid bytes in the window */\n    unsigned wnext;             /* window write index */\n    unsigned char FAR *window;  /* allocated sliding window, if needed */\n        /* bit accumulator */\n    unsigned long hold;         /* input bit accumulator */\n    unsigned bits;              /* number of bits in \"in\" */\n        /* for string and stored block copying */\n    unsigned length;            /* literal or length of data to copy */\n    unsigned offset;            /* distance back to copy string from */\n        /* for table and code decoding */\n    unsigned extra;             /* extra bits needed */\n        /* fixed and dynamic code tables */\n    code const FAR *lencode;    /* starting table for length/literal codes */\n    code const FAR *distcode;   /* starting table for distance codes */\n    unsigned lenbits;           /* index bits for lencode */\n    unsigned distbits;          /* index bits for distcode */\n        /* dynamic table building */\n    unsigned ncode;             /* number of code length code lengths */\n    unsigned nlen;              /* number of length code lengths */\n    unsigned ndist;             /* number of distance code lengths */\n    unsigned have;              /* number of code lengths in lens[] */\n    code FAR *next;             /* next available space in codes[] */\n    unsigned short lens[320];   /* temporary storage for code lengths */\n    unsigned short work[288];   /* work area for code table building */\n    code codes[ENOUGH];         /* space for code tables */\n    int sane;                   /* if false, allow invalid distance too far */\n    int back;                   /* bits back of last unprocessed length/lit */\n    unsigned was;               /* initial length of match */\n};\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inftrees.c",
    "content": "/* inftrees.c -- generate Huffman trees for efficient decoding\n * Copyright (C) 1995-2017 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n#include \"zutil.h\"\n#include \"inftrees.h\"\n\n#define MAXBITS 15\n\nconst char inflate_copyright[] =\n   \" inflate 1.2.11 Copyright 1995-2017 Mark Adler \";\n/*\n  If you use the zlib library in a product, an acknowledgment is welcome\n  in the documentation of your product. If for some reason you cannot\n  include such an acknowledgment, I would appreciate that you keep this\n  copyright string in the executable of your product.\n */\n\n/*\n   Build a set of tables to decode the provided canonical Huffman code.\n   The code lengths are lens[0..codes-1].  The result starts at *table,\n   whose indices are 0..2^bits-1.  work is a writable array of at least\n   lens shorts, which is used as a work area.  type is the type of code\n   to be generated, CODES, LENS, or DISTS.  On return, zero is success,\n   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table\n   on return points to the next available entry's address.  bits is the\n   requested root table index bits, and on return it is the actual root\n   table index bits.  It will differ if the request is greater than the\n   longest code or if it is less than the shortest code.\n */\nint ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)\ncodetype type;\nunsigned short FAR *lens;\nunsigned codes;\ncode FAR * FAR *table;\nunsigned FAR *bits;\nunsigned short FAR *work;\n{\n    unsigned len;               /* a code's length in bits */\n    unsigned sym;               /* index of code symbols */\n    unsigned min, max;          /* minimum and maximum code lengths */\n    unsigned root;              /* number of index bits for root table */\n    unsigned curr;              /* number of index bits for current table */\n    unsigned drop;              /* code bits to drop for sub-table */\n    int left;                   /* number of prefix codes available */\n    unsigned used;              /* code entries in table used */\n    unsigned huff;              /* Huffman code */\n    unsigned incr;              /* for incrementing code, index */\n    unsigned fill;              /* index for replicating entries */\n    unsigned low;               /* low bits for current root entry */\n    unsigned mask;              /* mask for low root bits */\n    code here;                  /* table entry for duplication */\n    code FAR *next;             /* next available space in table */\n    const unsigned short FAR *base;     /* base value table to use */\n    const unsigned short FAR *extra;    /* extra bits table to use */\n    unsigned match;             /* use base and extra for symbol >= match */\n    unsigned short count[MAXBITS+1];    /* number of codes of each length */\n    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */\n    static const unsigned short lbase[31] = { /* Length codes 257..285 base */\n        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,\n        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};\n    static const unsigned short lext[31] = { /* Length codes 257..285 extra */\n        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,\n        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202};\n    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */\n        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,\n        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,\n        8193, 12289, 16385, 24577, 0, 0};\n    static const unsigned short dext[32] = { /* Distance codes 0..29 extra */\n        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,\n        23, 23, 24, 24, 25, 25, 26, 26, 27, 27,\n        28, 28, 29, 29, 64, 64};\n\n    /*\n       Process a set of code lengths to create a canonical Huffman code.  The\n       code lengths are lens[0..codes-1].  Each length corresponds to the\n       symbols 0..codes-1.  The Huffman code is generated by first sorting the\n       symbols by length from short to long, and retaining the symbol order\n       for codes with equal lengths.  Then the code starts with all zero bits\n       for the first code of the shortest length, and the codes are integer\n       increments for the same length, and zeros are appended as the length\n       increases.  For the deflate format, these bits are stored backwards\n       from their more natural integer increment ordering, and so when the\n       decoding tables are built in the large loop below, the integer codes\n       are incremented backwards.\n\n       This routine assumes, but does not check, that all of the entries in\n       lens[] are in the range 0..MAXBITS.  The caller must assure this.\n       1..MAXBITS is interpreted as that code length.  zero means that that\n       symbol does not occur in this code.\n\n       The codes are sorted by computing a count of codes for each length,\n       creating from that a table of starting indices for each length in the\n       sorted table, and then entering the symbols in order in the sorted\n       table.  The sorted table is work[], with that space being provided by\n       the caller.\n\n       The length counts are used for other purposes as well, i.e. finding\n       the minimum and maximum length codes, determining if there are any\n       codes at all, checking for a valid set of lengths, and looking ahead\n       at length counts to determine sub-table sizes when building the\n       decoding tables.\n     */\n\n    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */\n    for (len = 0; len <= MAXBITS; len++)\n        count[len] = 0;\n    for (sym = 0; sym < codes; sym++)\n        count[lens[sym]]++;\n\n    /* bound code lengths, force root to be within code lengths */\n    root = *bits;\n    for (max = MAXBITS; max >= 1; max--)\n        if (count[max] != 0) break;\n    if (root > max) root = max;\n    if (max == 0) {                     /* no symbols to code at all */\n        here.op = (unsigned char)64;    /* invalid code marker */\n        here.bits = (unsigned char)1;\n        here.val = (unsigned short)0;\n        *(*table)++ = here;             /* make a table to force an error */\n        *(*table)++ = here;\n        *bits = 1;\n        return 0;     /* no symbols, but wait for decoding to report error */\n    }\n    for (min = 1; min < max; min++)\n        if (count[min] != 0) break;\n    if (root < min) root = min;\n\n    /* check for an over-subscribed or incomplete set of lengths */\n    left = 1;\n    for (len = 1; len <= MAXBITS; len++) {\n        left <<= 1;\n        left -= count[len];\n        if (left < 0) return -1;        /* over-subscribed */\n    }\n    if (left > 0 && (type == CODES || max != 1))\n        return -1;                      /* incomplete set */\n\n    /* generate offsets into symbol table for each length for sorting */\n    offs[1] = 0;\n    for (len = 1; len < MAXBITS; len++)\n        offs[len + 1] = offs[len] + count[len];\n\n    /* sort symbols by length, by symbol order within each length */\n    for (sym = 0; sym < codes; sym++)\n        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;\n\n    /*\n       Create and fill in decoding tables.  In this loop, the table being\n       filled is at next and has curr index bits.  The code being used is huff\n       with length len.  That code is converted to an index by dropping drop\n       bits off of the bottom.  For codes where len is less than drop + curr,\n       those top drop + curr - len bits are incremented through all values to\n       fill the table with replicated entries.\n\n       root is the number of index bits for the root table.  When len exceeds\n       root, sub-tables are created pointed to by the root entry with an index\n       of the low root bits of huff.  This is saved in low to check for when a\n       new sub-table should be started.  drop is zero when the root table is\n       being filled, and drop is root when sub-tables are being filled.\n\n       When a new sub-table is needed, it is necessary to look ahead in the\n       code lengths to determine what size sub-table is needed.  The length\n       counts are used for this, and so count[] is decremented as codes are\n       entered in the tables.\n\n       used keeps track of how many table entries have been allocated from the\n       provided *table space.  It is checked for LENS and DIST tables against\n       the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in\n       the initial root table size constants.  See the comments in inftrees.h\n       for more information.\n\n       sym increments through all symbols, and the loop terminates when\n       all codes of length max, i.e. all codes, have been processed.  This\n       routine permits incomplete codes, so another loop after this one fills\n       in the rest of the decoding tables with invalid code markers.\n     */\n\n    /* set up for code type */\n    switch (type) {\n    case CODES:\n        base = extra = work;    /* dummy value--not used */\n        match = 20;\n        break;\n    case LENS:\n        base = lbase;\n        extra = lext;\n        match = 257;\n        break;\n    default:    /* DISTS */\n        base = dbase;\n        extra = dext;\n        match = 0;\n    }\n\n    /* initialize state for loop */\n    huff = 0;                   /* starting code */\n    sym = 0;                    /* starting code symbol */\n    len = min;                  /* starting code length */\n    next = *table;              /* current table to fill in */\n    curr = root;                /* current table index bits */\n    drop = 0;                   /* current bits to drop from code for index */\n    low = (unsigned)(-1);       /* trigger new sub-table when len > root */\n    used = 1U << root;          /* use root table entries */\n    mask = used - 1;            /* mask for comparing low */\n\n    /* check available table space */\n    if ((type == LENS && used > ENOUGH_LENS) ||\n        (type == DISTS && used > ENOUGH_DISTS))\n        return 1;\n\n    /* process all codes and make table entries */\n    for (;;) {\n        /* create table entry */\n        here.bits = (unsigned char)(len - drop);\n        if (work[sym] + 1U < match) {\n            here.op = (unsigned char)0;\n            here.val = work[sym];\n        }\n        else if (work[sym] >= match) {\n            here.op = (unsigned char)(extra[work[sym] - match]);\n            here.val = base[work[sym] - match];\n        }\n        else {\n            here.op = (unsigned char)(32 + 64);         /* end of block */\n            here.val = 0;\n        }\n\n        /* replicate for those indices with low len bits equal to huff */\n        incr = 1U << (len - drop);\n        fill = 1U << curr;\n        min = fill;                 /* save offset to next table */\n        do {\n            fill -= incr;\n            next[(huff >> drop) + fill] = here;\n        } while (fill != 0);\n\n        /* backwards increment the len-bit code huff */\n        incr = 1U << (len - 1);\n        while (huff & incr)\n            incr >>= 1;\n        if (incr != 0) {\n            huff &= incr - 1;\n            huff += incr;\n        }\n        else\n            huff = 0;\n\n        /* go to next symbol, update count, len */\n        sym++;\n        if (--(count[len]) == 0) {\n            if (len == max) break;\n            len = lens[work[sym]];\n        }\n\n        /* create new sub-table if needed */\n        if (len > root && (huff & mask) != low) {\n            /* if first time, transition to sub-tables */\n            if (drop == 0)\n                drop = root;\n\n            /* increment past last table */\n            next += min;            /* here min is 1 << curr */\n\n            /* determine length of next table */\n            curr = len - drop;\n            left = (int)(1 << curr);\n            while (curr + drop < max) {\n                left -= count[curr + drop];\n                if (left <= 0) break;\n                curr++;\n                left <<= 1;\n            }\n\n            /* check for enough space */\n            used += 1U << curr;\n            if ((type == LENS && used > ENOUGH_LENS) ||\n                (type == DISTS && used > ENOUGH_DISTS))\n                return 1;\n\n            /* point entry in root table to sub-table */\n            low = huff & mask;\n            (*table)[low].op = (unsigned char)curr;\n            (*table)[low].bits = (unsigned char)root;\n            (*table)[low].val = (unsigned short)(next - *table);\n        }\n    }\n\n    /* fill in remaining table entry if code is incomplete (guaranteed to have\n       at most one remaining entry, since if the code is incomplete, the\n       maximum code length that was allowed to get this far is one bit) */\n    if (huff != 0) {\n        here.op = (unsigned char)64;            /* invalid code marker */\n        here.bits = (unsigned char)(len - drop);\n        here.val = (unsigned short)0;\n        next[huff] = here;\n    }\n\n    /* set return parameters */\n    *table += used;\n    *bits = root;\n    return 0;\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/inftrees.h",
    "content": "/* inftrees.h -- header to use inftrees.c\n * Copyright (C) 1995-2005, 2010 Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* WARNING: this file should *not* be used by applications. It is\n   part of the implementation of the compression library and is\n   subject to change. Applications should only use zlib.h.\n */\n\n/* Structure for decoding tables.  Each entry provides either the\n   information needed to do the operation requested by the code that\n   indexed that table entry, or it provides a pointer to another\n   table that indexes more bits of the code.  op indicates whether\n   the entry is a pointer to another table, a literal, a length or\n   distance, an end-of-block, or an invalid code.  For a table\n   pointer, the low four bits of op is the number of index bits of\n   that table.  For a length or distance, the low four bits of op\n   is the number of extra bits to get after the code.  bits is\n   the number of bits in this code or part of the code to drop off\n   of the bit buffer.  val is the actual byte to output in the case\n   of a literal, the base length or distance, or the offset from\n   the current table to the next table.  Each entry is four bytes. */\ntypedef struct {\n    unsigned char op;           /* operation, extra bits, table bits */\n    unsigned char bits;         /* bits in this part of the code */\n    unsigned short val;         /* offset in table or code value */\n} code;\n\n/* op values as set by inflate_table():\n    00000000 - literal\n    0000tttt - table link, tttt != 0 is the number of table index bits\n    0001eeee - length or distance, eeee is the number of extra bits\n    01100000 - end of block\n    01000000 - invalid code\n */\n\n/* Maximum size of the dynamic table.  The maximum number of code structures is\n   1444, which is the sum of 852 for literal/length codes and 592 for distance\n   codes.  These values were found by exhaustive searches using the program\n   examples/enough.c found in the zlib distribtution.  The arguments to that\n   program are the number of symbols, the initial root table size, and the\n   maximum bit length of a code.  \"enough 286 9 15\" for literal/length codes\n   returns returns 852, and \"enough 30 6 15\" for distance codes returns 592.\n   The initial root table size (9 or 6) is found in the fifth argument of the\n   inflate_table() calls in inflate.c and infback.c.  If the root table size is\n   changed, then these maximum sizes would be need to be recalculated and\n   updated. */\n#define ENOUGH_LENS 852\n#define ENOUGH_DISTS 592\n#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)\n\n/* Type of code to build for inflate_table() */\ntypedef enum {\n    CODES,\n    LENS,\n    DISTS\n} codetype;\n\nint ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,\n                             unsigned codes, code FAR * FAR *table,\n                             unsigned FAR *bits, unsigned short FAR *work));\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/trees.c",
    "content": "/* trees.c -- output deflated data using Huffman coding\n * Copyright (C) 1995-2017 Jean-loup Gailly\n * detect_data_type() function provided freely by Cosmin Truta, 2006\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/*\n *  ALGORITHM\n *\n *      The \"deflation\" process uses several Huffman trees. The more\n *      common source values are represented by shorter bit sequences.\n *\n *      Each code tree is stored in a compressed form which is itself\n * a Huffman encoding of the lengths of all the code strings (in\n * ascending order by source values).  The actual code strings are\n * reconstructed from the lengths in the inflate process, as described\n * in the deflate specification.\n *\n *  REFERENCES\n *\n *      Deutsch, L.P.,\"'Deflate' Compressed Data Format Specification\".\n *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc\n *\n *      Storer, James A.\n *          Data Compression:  Methods and Theory, pp. 49-50.\n *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.\n *\n *      Sedgewick, R.\n *          Algorithms, p290.\n *          Addison-Wesley, 1983. ISBN 0-201-06672-6.\n */\n\n/* @(#) $Id$ */\n\n/* #define GEN_TREES_H */\n\n#include \"deflate.h\"\n\n#ifdef ZLIB_DEBUG\n#  include <ctype.h>\n#endif\n\n/* ===========================================================================\n * Constants\n */\n\n#define MAX_BL_BITS 7\n/* Bit length codes must not exceed MAX_BL_BITS bits */\n\n#define END_BLOCK 256\n/* end of block literal code */\n\n#define REP_3_6      16\n/* repeat previous bit length 3-6 times (2 bits of repeat count) */\n\n#define REPZ_3_10    17\n/* repeat a zero length 3-10 times  (3 bits of repeat count) */\n\n#define REPZ_11_138  18\n/* repeat a zero length 11-138 times  (7 bits of repeat count) */\n\nlocal const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */\n   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};\n\nlocal const int extra_dbits[D_CODES] /* extra bits for each distance code */\n   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};\n\nlocal const int extra_blbits[BL_CODES]/* extra bits for each bit length code */\n   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};\n\nlocal const uch bl_order[BL_CODES]\n   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};\n/* The lengths of the bit length codes are sent in order of decreasing\n * probability, to avoid transmitting the lengths for unused bit length codes.\n */\n\n/* ===========================================================================\n * Local data. These are initialized only once.\n */\n\n#define DIST_CODE_LEN  512 /* see definition of array dist_code below */\n\n#if defined(GEN_TREES_H) || !defined(STDC)\n/* non ANSI compilers may not accept trees.h */\n\nlocal ct_data static_ltree[L_CODES+2];\n/* The static literal tree. Since the bit lengths are imposed, there is no\n * need for the L_CODES extra codes used during heap construction. However\n * The codes 286 and 287 are needed to build a canonical tree (see _tr_init\n * below).\n */\n\nlocal ct_data static_dtree[D_CODES];\n/* The static distance tree. (Actually a trivial tree since all codes use\n * 5 bits.)\n */\n\nuch _dist_code[DIST_CODE_LEN];\n/* Distance codes. The first 256 values correspond to the distances\n * 3 .. 258, the last 256 values correspond to the top 8 bits of\n * the 15 bit distances.\n */\n\nuch _length_code[MAX_MATCH-MIN_MATCH+1];\n/* length code for each normalized match length (0 == MIN_MATCH) */\n\nlocal int base_length[LENGTH_CODES];\n/* First normalized length for each code (0 = MIN_MATCH) */\n\nlocal int base_dist[D_CODES];\n/* First normalized distance for each code (0 = distance of 1) */\n\n#else\n#  include \"trees.h\"\n#endif /* GEN_TREES_H */\n\nstruct static_tree_desc_s {\n    const ct_data *static_tree;  /* static tree or NULL */\n    const intf *extra_bits;      /* extra bits for each code or NULL */\n    int     extra_base;          /* base index for extra_bits */\n    int     elems;               /* max number of elements in the tree */\n    int     max_length;          /* max bit length for the codes */\n};\n\nlocal const static_tree_desc  static_l_desc =\n{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};\n\nlocal const static_tree_desc  static_d_desc =\n{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};\n\nlocal const static_tree_desc  static_bl_desc =\n{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS};\n\n/* ===========================================================================\n * Local (static) routines in this file.\n */\n\nlocal void tr_static_init OF((void));\nlocal void init_block     OF((deflate_state *s));\nlocal void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));\nlocal void gen_bitlen     OF((deflate_state *s, tree_desc *desc));\nlocal void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));\nlocal void build_tree     OF((deflate_state *s, tree_desc *desc));\nlocal void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));\nlocal void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));\nlocal int  build_bl_tree  OF((deflate_state *s));\nlocal void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,\n                              int blcodes));\nlocal void compress_block OF((deflate_state *s, const ct_data *ltree,\n                              const ct_data *dtree));\nlocal int  detect_data_type OF((deflate_state *s));\nlocal unsigned bi_reverse OF((unsigned value, int length));\nlocal void bi_windup      OF((deflate_state *s));\nlocal void bi_flush       OF((deflate_state *s));\n\n#ifdef GEN_TREES_H\nlocal void gen_trees_header OF((void));\n#endif\n\n#ifndef ZLIB_DEBUG\n#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)\n   /* Send a code of the given tree. c and tree must not have side effects */\n\n#else /* !ZLIB_DEBUG */\n#  define send_code(s, c, tree) \\\n     { if (z_verbose>2) fprintf(stderr,\"\\ncd %3d \",(c)); \\\n       send_bits(s, tree[c].Code, tree[c].Len); }\n#endif\n\n/* ===========================================================================\n * Output a short LSB first on the stream.\n * IN assertion: there is enough room in pendingBuf.\n */\n#define put_short(s, w) { \\\n    put_byte(s, (uch)((w) & 0xff)); \\\n    put_byte(s, (uch)((ush)(w) >> 8)); \\\n}\n\n/* ===========================================================================\n * Send a value on a given number of bits.\n * IN assertion: length <= 16 and value fits in length bits.\n */\n#ifdef ZLIB_DEBUG\nlocal void send_bits      OF((deflate_state *s, int value, int length));\n\nlocal void send_bits(s, value, length)\n    deflate_state *s;\n    int value;  /* value to send */\n    int length; /* number of bits */\n{\n    Tracevv((stderr,\" l %2d v %4x \", length, value));\n    Assert(length > 0 && length <= 15, \"invalid length\");\n    s->bits_sent += (ulg)length;\n\n    /* If not enough room in bi_buf, use (valid) bits from bi_buf and\n     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))\n     * unused bits in value.\n     */\n    if (s->bi_valid > (int)Buf_size - length) {\n        s->bi_buf |= (ush)value << s->bi_valid;\n        put_short(s, s->bi_buf);\n        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);\n        s->bi_valid += length - Buf_size;\n    } else {\n        s->bi_buf |= (ush)value << s->bi_valid;\n        s->bi_valid += length;\n    }\n}\n#else /* !ZLIB_DEBUG */\n\n#define send_bits(s, value, length) \\\n{ int len = length;\\\n  if (s->bi_valid > (int)Buf_size - len) {\\\n    int val = (int)value;\\\n    s->bi_buf |= (ush)val << s->bi_valid;\\\n    put_short(s, s->bi_buf);\\\n    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\\\n    s->bi_valid += len - Buf_size;\\\n  } else {\\\n    s->bi_buf |= (ush)(value) << s->bi_valid;\\\n    s->bi_valid += len;\\\n  }\\\n}\n#endif /* ZLIB_DEBUG */\n\n\n/* the arguments must not have side effects */\n\n/* ===========================================================================\n * Initialize the various 'constant' tables.\n */\nlocal void tr_static_init()\n{\n#if defined(GEN_TREES_H) || !defined(STDC)\n    static int static_init_done = 0;\n    int n;        /* iterates over tree elements */\n    int bits;     /* bit counter */\n    int length;   /* length value */\n    int code;     /* code value */\n    int dist;     /* distance index */\n    ush bl_count[MAX_BITS+1];\n    /* number of codes at each bit length for an optimal tree */\n\n    if (static_init_done) return;\n\n    /* For some embedded targets, global variables are not initialized: */\n#ifdef NO_INIT_GLOBAL_POINTERS\n    static_l_desc.static_tree = static_ltree;\n    static_l_desc.extra_bits = extra_lbits;\n    static_d_desc.static_tree = static_dtree;\n    static_d_desc.extra_bits = extra_dbits;\n    static_bl_desc.extra_bits = extra_blbits;\n#endif\n\n    /* Initialize the mapping length (0..255) -> length code (0..28) */\n    length = 0;\n    for (code = 0; code < LENGTH_CODES-1; code++) {\n        base_length[code] = length;\n        for (n = 0; n < (1<<extra_lbits[code]); n++) {\n            _length_code[length++] = (uch)code;\n        }\n    }\n    Assert (length == 256, \"tr_static_init: length != 256\");\n    /* Note that the length 255 (match length 258) can be represented\n     * in two different ways: code 284 + 5 bits or code 285, so we\n     * overwrite length_code[255] to use the best encoding:\n     */\n    _length_code[length-1] = (uch)code;\n\n    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */\n    dist = 0;\n    for (code = 0 ; code < 16; code++) {\n        base_dist[code] = dist;\n        for (n = 0; n < (1<<extra_dbits[code]); n++) {\n            _dist_code[dist++] = (uch)code;\n        }\n    }\n    Assert (dist == 256, \"tr_static_init: dist != 256\");\n    dist >>= 7; /* from now on, all distances are divided by 128 */\n    for ( ; code < D_CODES; code++) {\n        base_dist[code] = dist << 7;\n        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {\n            _dist_code[256 + dist++] = (uch)code;\n        }\n    }\n    Assert (dist == 256, \"tr_static_init: 256+dist != 512\");\n\n    /* Construct the codes of the static literal tree */\n    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;\n    n = 0;\n    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;\n    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;\n    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;\n    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;\n    /* Codes 286 and 287 do not exist, but we must include them in the\n     * tree construction to get a canonical Huffman tree (longest code\n     * all ones)\n     */\n    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);\n\n    /* The static distance tree is trivial: */\n    for (n = 0; n < D_CODES; n++) {\n        static_dtree[n].Len = 5;\n        static_dtree[n].Code = bi_reverse((unsigned)n, 5);\n    }\n    static_init_done = 1;\n\n#  ifdef GEN_TREES_H\n    gen_trees_header();\n#  endif\n#endif /* defined(GEN_TREES_H) || !defined(STDC) */\n}\n\n/* ===========================================================================\n * Genererate the file trees.h describing the static trees.\n */\n#ifdef GEN_TREES_H\n#  ifndef ZLIB_DEBUG\n#    include <stdio.h>\n#  endif\n\n#  define SEPARATOR(i, last, width) \\\n      ((i) == (last)? \"\\n};\\n\\n\" :    \\\n       ((i) % (width) == (width)-1 ? \",\\n\" : \", \"))\n\nvoid gen_trees_header()\n{\n    FILE *header = fopen(\"trees.h\", \"w\");\n    int i;\n\n    Assert (header != NULL, \"Can't open trees.h\");\n    fprintf(header,\n            \"/* header created automatically with -DGEN_TREES_H */\\n\\n\");\n\n    fprintf(header, \"local const ct_data static_ltree[L_CODES+2] = {\\n\");\n    for (i = 0; i < L_CODES+2; i++) {\n        fprintf(header, \"{{%3u},{%3u}}%s\", static_ltree[i].Code,\n                static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));\n    }\n\n    fprintf(header, \"local const ct_data static_dtree[D_CODES] = {\\n\");\n    for (i = 0; i < D_CODES; i++) {\n        fprintf(header, \"{{%2u},{%2u}}%s\", static_dtree[i].Code,\n                static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));\n    }\n\n    fprintf(header, \"const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\\n\");\n    for (i = 0; i < DIST_CODE_LEN; i++) {\n        fprintf(header, \"%2u%s\", _dist_code[i],\n                SEPARATOR(i, DIST_CODE_LEN-1, 20));\n    }\n\n    fprintf(header,\n        \"const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\\n\");\n    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {\n        fprintf(header, \"%2u%s\", _length_code[i],\n                SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));\n    }\n\n    fprintf(header, \"local const int base_length[LENGTH_CODES] = {\\n\");\n    for (i = 0; i < LENGTH_CODES; i++) {\n        fprintf(header, \"%1u%s\", base_length[i],\n                SEPARATOR(i, LENGTH_CODES-1, 20));\n    }\n\n    fprintf(header, \"local const int base_dist[D_CODES] = {\\n\");\n    for (i = 0; i < D_CODES; i++) {\n        fprintf(header, \"%5u%s\", base_dist[i],\n                SEPARATOR(i, D_CODES-1, 10));\n    }\n\n    fclose(header);\n}\n#endif /* GEN_TREES_H */\n\n/* ===========================================================================\n * Initialize the tree data structures for a new zlib stream.\n */\nvoid ZLIB_INTERNAL _tr_init(s)\n    deflate_state *s;\n{\n    tr_static_init();\n\n    s->l_desc.dyn_tree = s->dyn_ltree;\n    s->l_desc.stat_desc = &static_l_desc;\n\n    s->d_desc.dyn_tree = s->dyn_dtree;\n    s->d_desc.stat_desc = &static_d_desc;\n\n    s->bl_desc.dyn_tree = s->bl_tree;\n    s->bl_desc.stat_desc = &static_bl_desc;\n\n    s->bi_buf = 0;\n    s->bi_valid = 0;\n#ifdef ZLIB_DEBUG\n    s->compressed_len = 0L;\n    s->bits_sent = 0L;\n#endif\n\n    /* Initialize the first block of the first file: */\n    init_block(s);\n}\n\n/* ===========================================================================\n * Initialize a new block.\n */\nlocal void init_block(s)\n    deflate_state *s;\n{\n    int n; /* iterates over tree elements */\n\n    /* Initialize the trees. */\n    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;\n    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;\n    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;\n\n    s->dyn_ltree[END_BLOCK].Freq = 1;\n    s->opt_len = s->static_len = 0L;\n    s->last_lit = s->matches = 0;\n}\n\n#define SMALLEST 1\n/* Index within the heap array of least frequent node in the Huffman tree */\n\n\n/* ===========================================================================\n * Remove the smallest element from the heap and recreate the heap with\n * one less element. Updates heap and heap_len.\n */\n#define pqremove(s, tree, top) \\\n{\\\n    top = s->heap[SMALLEST]; \\\n    s->heap[SMALLEST] = s->heap[s->heap_len--]; \\\n    pqdownheap(s, tree, SMALLEST); \\\n}\n\n/* ===========================================================================\n * Compares to subtrees, using the tree depth as tie breaker when\n * the subtrees have equal frequency. This minimizes the worst case length.\n */\n#define smaller(tree, n, m, depth) \\\n   (tree[n].Freq < tree[m].Freq || \\\n   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))\n\n/* ===========================================================================\n * Restore the heap property by moving down the tree starting at node k,\n * exchanging a node with the smallest of its two sons if necessary, stopping\n * when the heap property is re-established (each father smaller than its\n * two sons).\n */\nlocal void pqdownheap(s, tree, k)\n    deflate_state *s;\n    ct_data *tree;  /* the tree to restore */\n    int k;               /* node to move down */\n{\n    int v = s->heap[k];\n    int j = k << 1;  /* left son of k */\n    while (j <= s->heap_len) {\n        /* Set j to the smallest of the two sons: */\n        if (j < s->heap_len &&\n            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {\n            j++;\n        }\n        /* Exit if v is smaller than both sons */\n        if (smaller(tree, v, s->heap[j], s->depth)) break;\n\n        /* Exchange v with the smallest son */\n        s->heap[k] = s->heap[j];  k = j;\n\n        /* And continue down the tree, setting j to the left son of k */\n        j <<= 1;\n    }\n    s->heap[k] = v;\n}\n\n/* ===========================================================================\n * Compute the optimal bit lengths for a tree and update the total bit length\n * for the current block.\n * IN assertion: the fields freq and dad are set, heap[heap_max] and\n *    above are the tree nodes sorted by increasing frequency.\n * OUT assertions: the field len is set to the optimal bit length, the\n *     array bl_count contains the frequencies for each bit length.\n *     The length opt_len is updated; static_len is also updated if stree is\n *     not null.\n */\nlocal void gen_bitlen(s, desc)\n    deflate_state *s;\n    tree_desc *desc;    /* the tree descriptor */\n{\n    ct_data *tree        = desc->dyn_tree;\n    int max_code         = desc->max_code;\n    const ct_data *stree = desc->stat_desc->static_tree;\n    const intf *extra    = desc->stat_desc->extra_bits;\n    int base             = desc->stat_desc->extra_base;\n    int max_length       = desc->stat_desc->max_length;\n    int h;              /* heap index */\n    int n, m;           /* iterate over the tree elements */\n    int bits;           /* bit length */\n    int xbits;          /* extra bits */\n    ush f;              /* frequency */\n    int overflow = 0;   /* number of elements with bit length too large */\n\n    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;\n\n    /* In a first pass, compute the optimal bit lengths (which may\n     * overflow in the case of the bit length tree).\n     */\n    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */\n\n    for (h = s->heap_max+1; h < HEAP_SIZE; h++) {\n        n = s->heap[h];\n        bits = tree[tree[n].Dad].Len + 1;\n        if (bits > max_length) bits = max_length, overflow++;\n        tree[n].Len = (ush)bits;\n        /* We overwrite tree[n].Dad which is no longer needed */\n\n        if (n > max_code) continue; /* not a leaf node */\n\n        s->bl_count[bits]++;\n        xbits = 0;\n        if (n >= base) xbits = extra[n-base];\n        f = tree[n].Freq;\n        s->opt_len += (ulg)f * (unsigned)(bits + xbits);\n        if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits);\n    }\n    if (overflow == 0) return;\n\n    Tracev((stderr,\"\\nbit length overflow\\n\"));\n    /* This happens for example on obj2 and pic of the Calgary corpus */\n\n    /* Find the first bit length which could increase: */\n    do {\n        bits = max_length-1;\n        while (s->bl_count[bits] == 0) bits--;\n        s->bl_count[bits]--;      /* move one leaf down the tree */\n        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */\n        s->bl_count[max_length]--;\n        /* The brother of the overflow item also moves one step up,\n         * but this does not affect bl_count[max_length]\n         */\n        overflow -= 2;\n    } while (overflow > 0);\n\n    /* Now recompute all bit lengths, scanning in increasing frequency.\n     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all\n     * lengths instead of fixing only the wrong ones. This idea is taken\n     * from 'ar' written by Haruhiko Okumura.)\n     */\n    for (bits = max_length; bits != 0; bits--) {\n        n = s->bl_count[bits];\n        while (n != 0) {\n            m = s->heap[--h];\n            if (m > max_code) continue;\n            if ((unsigned) tree[m].Len != (unsigned) bits) {\n                Tracev((stderr,\"code %d bits %d->%d\\n\", m, tree[m].Len, bits));\n                s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq;\n                tree[m].Len = (ush)bits;\n            }\n            n--;\n        }\n    }\n}\n\n/* ===========================================================================\n * Generate the codes for a given tree and bit counts (which need not be\n * optimal).\n * IN assertion: the array bl_count contains the bit length statistics for\n * the given tree and the field len is set for all tree elements.\n * OUT assertion: the field code is set for all tree elements of non\n *     zero code length.\n */\nlocal void gen_codes (tree, max_code, bl_count)\n    ct_data *tree;             /* the tree to decorate */\n    int max_code;              /* largest code with non zero frequency */\n    ushf *bl_count;            /* number of codes at each bit length */\n{\n    ush next_code[MAX_BITS+1]; /* next code value for each bit length */\n    unsigned code = 0;         /* running code value */\n    int bits;                  /* bit index */\n    int n;                     /* code index */\n\n    /* The distribution counts are first used to generate the code values\n     * without bit reversal.\n     */\n    for (bits = 1; bits <= MAX_BITS; bits++) {\n        code = (code + bl_count[bits-1]) << 1;\n        next_code[bits] = (ush)code;\n    }\n    /* Check that the bit counts in bl_count are consistent. The last code\n     * must be all ones.\n     */\n    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,\n            \"inconsistent bit counts\");\n    Tracev((stderr,\"\\ngen_codes: max_code %d \", max_code));\n\n    for (n = 0;  n <= max_code; n++) {\n        int len = tree[n].Len;\n        if (len == 0) continue;\n        /* Now reverse the bits */\n        tree[n].Code = (ush)bi_reverse(next_code[len]++, len);\n\n        Tracecv(tree != static_ltree, (stderr,\"\\nn %3d %c l %2d c %4x (%x) \",\n             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));\n    }\n}\n\n/* ===========================================================================\n * Construct one Huffman tree and assigns the code bit strings and lengths.\n * Update the total bit length for the current block.\n * IN assertion: the field freq is set for all tree elements.\n * OUT assertions: the fields len and code are set to the optimal bit length\n *     and corresponding code. The length opt_len is updated; static_len is\n *     also updated if stree is not null. The field max_code is set.\n */\nlocal void build_tree(s, desc)\n    deflate_state *s;\n    tree_desc *desc; /* the tree descriptor */\n{\n    ct_data *tree         = desc->dyn_tree;\n    const ct_data *stree  = desc->stat_desc->static_tree;\n    int elems             = desc->stat_desc->elems;\n    int n, m;          /* iterate over heap elements */\n    int max_code = -1; /* largest code with non zero frequency */\n    int node;          /* new node being created */\n\n    /* Construct the initial heap, with least frequent element in\n     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].\n     * heap[0] is not used.\n     */\n    s->heap_len = 0, s->heap_max = HEAP_SIZE;\n\n    for (n = 0; n < elems; n++) {\n        if (tree[n].Freq != 0) {\n            s->heap[++(s->heap_len)] = max_code = n;\n            s->depth[n] = 0;\n        } else {\n            tree[n].Len = 0;\n        }\n    }\n\n    /* The pkzip format requires that at least one distance code exists,\n     * and that at least one bit should be sent even if there is only one\n     * possible code. So to avoid special checks later on we force at least\n     * two codes of non zero frequency.\n     */\n    while (s->heap_len < 2) {\n        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);\n        tree[node].Freq = 1;\n        s->depth[node] = 0;\n        s->opt_len--; if (stree) s->static_len -= stree[node].Len;\n        /* node is 0 or 1 so it does not have extra bits */\n    }\n    desc->max_code = max_code;\n\n    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,\n     * establish sub-heaps of increasing lengths:\n     */\n    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);\n\n    /* Construct the Huffman tree by repeatedly combining the least two\n     * frequent nodes.\n     */\n    node = elems;              /* next internal node of the tree */\n    do {\n        pqremove(s, tree, n);  /* n = node of least frequency */\n        m = s->heap[SMALLEST]; /* m = node of next least frequency */\n\n        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */\n        s->heap[--(s->heap_max)] = m;\n\n        /* Create a new node father of n and m */\n        tree[node].Freq = tree[n].Freq + tree[m].Freq;\n        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?\n                                s->depth[n] : s->depth[m]) + 1);\n        tree[n].Dad = tree[m].Dad = (ush)node;\n#ifdef DUMP_BL_TREE\n        if (tree == s->bl_tree) {\n            fprintf(stderr,\"\\nnode %d(%d), sons %d(%d) %d(%d)\",\n                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);\n        }\n#endif\n        /* and insert the new node in the heap */\n        s->heap[SMALLEST] = node++;\n        pqdownheap(s, tree, SMALLEST);\n\n    } while (s->heap_len >= 2);\n\n    s->heap[--(s->heap_max)] = s->heap[SMALLEST];\n\n    /* At this point, the fields freq and dad are set. We can now\n     * generate the bit lengths.\n     */\n    gen_bitlen(s, (tree_desc *)desc);\n\n    /* The field len is now set, we can generate the bit codes */\n    gen_codes ((ct_data *)tree, max_code, s->bl_count);\n}\n\n/* ===========================================================================\n * Scan a literal or distance tree to determine the frequencies of the codes\n * in the bit length tree.\n */\nlocal void scan_tree (s, tree, max_code)\n    deflate_state *s;\n    ct_data *tree;   /* the tree to be scanned */\n    int max_code;    /* and its largest code of non zero frequency */\n{\n    int n;                     /* iterates over all tree elements */\n    int prevlen = -1;          /* last emitted length */\n    int curlen;                /* length of current code */\n    int nextlen = tree[0].Len; /* length of next code */\n    int count = 0;             /* repeat count of the current code */\n    int max_count = 7;         /* max repeat count */\n    int min_count = 4;         /* min repeat count */\n\n    if (nextlen == 0) max_count = 138, min_count = 3;\n    tree[max_code+1].Len = (ush)0xffff; /* guard */\n\n    for (n = 0; n <= max_code; n++) {\n        curlen = nextlen; nextlen = tree[n+1].Len;\n        if (++count < max_count && curlen == nextlen) {\n            continue;\n        } else if (count < min_count) {\n            s->bl_tree[curlen].Freq += count;\n        } else if (curlen != 0) {\n            if (curlen != prevlen) s->bl_tree[curlen].Freq++;\n            s->bl_tree[REP_3_6].Freq++;\n        } else if (count <= 10) {\n            s->bl_tree[REPZ_3_10].Freq++;\n        } else {\n            s->bl_tree[REPZ_11_138].Freq++;\n        }\n        count = 0; prevlen = curlen;\n        if (nextlen == 0) {\n            max_count = 138, min_count = 3;\n        } else if (curlen == nextlen) {\n            max_count = 6, min_count = 3;\n        } else {\n            max_count = 7, min_count = 4;\n        }\n    }\n}\n\n/* ===========================================================================\n * Send a literal or distance tree in compressed form, using the codes in\n * bl_tree.\n */\nlocal void send_tree (s, tree, max_code)\n    deflate_state *s;\n    ct_data *tree; /* the tree to be scanned */\n    int max_code;       /* and its largest code of non zero frequency */\n{\n    int n;                     /* iterates over all tree elements */\n    int prevlen = -1;          /* last emitted length */\n    int curlen;                /* length of current code */\n    int nextlen = tree[0].Len; /* length of next code */\n    int count = 0;             /* repeat count of the current code */\n    int max_count = 7;         /* max repeat count */\n    int min_count = 4;         /* min repeat count */\n\n    /* tree[max_code+1].Len = -1; */  /* guard already set */\n    if (nextlen == 0) max_count = 138, min_count = 3;\n\n    for (n = 0; n <= max_code; n++) {\n        curlen = nextlen; nextlen = tree[n+1].Len;\n        if (++count < max_count && curlen == nextlen) {\n            continue;\n        } else if (count < min_count) {\n            do { send_code(s, curlen, s->bl_tree); } while (--count != 0);\n\n        } else if (curlen != 0) {\n            if (curlen != prevlen) {\n                send_code(s, curlen, s->bl_tree); count--;\n            }\n            Assert(count >= 3 && count <= 6, \" 3_6?\");\n            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);\n\n        } else if (count <= 10) {\n            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);\n\n        } else {\n            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);\n        }\n        count = 0; prevlen = curlen;\n        if (nextlen == 0) {\n            max_count = 138, min_count = 3;\n        } else if (curlen == nextlen) {\n            max_count = 6, min_count = 3;\n        } else {\n            max_count = 7, min_count = 4;\n        }\n    }\n}\n\n/* ===========================================================================\n * Construct the Huffman tree for the bit lengths and return the index in\n * bl_order of the last bit length code to send.\n */\nlocal int build_bl_tree(s)\n    deflate_state *s;\n{\n    int max_blindex;  /* index of last bit length code of non zero freq */\n\n    /* Determine the bit length frequencies for literal and distance trees */\n    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);\n    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);\n\n    /* Build the bit length tree: */\n    build_tree(s, (tree_desc *)(&(s->bl_desc)));\n    /* opt_len now includes the length of the tree representations, except\n     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.\n     */\n\n    /* Determine the number of bit length codes to send. The pkzip format\n     * requires that at least 4 bit length codes be sent. (appnote.txt says\n     * 3 but the actual value used is 4.)\n     */\n    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {\n        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;\n    }\n    /* Update opt_len to include the bit length tree and counts */\n    s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4;\n    Tracev((stderr, \"\\ndyn trees: dyn %ld, stat %ld\",\n            s->opt_len, s->static_len));\n\n    return max_blindex;\n}\n\n/* ===========================================================================\n * Send the header for a block using dynamic Huffman trees: the counts, the\n * lengths of the bit length codes, the literal tree and the distance tree.\n * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.\n */\nlocal void send_all_trees(s, lcodes, dcodes, blcodes)\n    deflate_state *s;\n    int lcodes, dcodes, blcodes; /* number of codes for each tree */\n{\n    int rank;                    /* index in bl_order */\n\n    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, \"not enough codes\");\n    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,\n            \"too many codes\");\n    Tracev((stderr, \"\\nbl counts: \"));\n    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */\n    send_bits(s, dcodes-1,   5);\n    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */\n    for (rank = 0; rank < blcodes; rank++) {\n        Tracev((stderr, \"\\nbl code %2d \", bl_order[rank]));\n        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);\n    }\n    Tracev((stderr, \"\\nbl tree: sent %ld\", s->bits_sent));\n\n    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */\n    Tracev((stderr, \"\\nlit tree: sent %ld\", s->bits_sent));\n\n    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */\n    Tracev((stderr, \"\\ndist tree: sent %ld\", s->bits_sent));\n}\n\n/* ===========================================================================\n * Send a stored block\n */\nvoid ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)\n    deflate_state *s;\n    charf *buf;       /* input block */\n    ulg stored_len;   /* length of input block */\n    int last;         /* one if this is the last block for a file */\n{\n    send_bits(s, (STORED_BLOCK<<1)+last, 3);    /* send block type */\n    bi_windup(s);        /* align on byte boundary */\n    put_short(s, (ush)stored_len);\n    put_short(s, (ush)~stored_len);\n    zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len);\n    s->pending += stored_len;\n#ifdef ZLIB_DEBUG\n    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;\n    s->compressed_len += (stored_len + 4) << 3;\n    s->bits_sent += 2*16;\n    s->bits_sent += stored_len<<3;\n#endif\n}\n\n/* ===========================================================================\n * Flush the bits in the bit buffer to pending output (leaves at most 7 bits)\n */\nvoid ZLIB_INTERNAL _tr_flush_bits(s)\n    deflate_state *s;\n{\n    bi_flush(s);\n}\n\n/* ===========================================================================\n * Send one empty static block to give enough lookahead for inflate.\n * This takes 10 bits, of which 7 may remain in the bit buffer.\n */\nvoid ZLIB_INTERNAL _tr_align(s)\n    deflate_state *s;\n{\n    send_bits(s, STATIC_TREES<<1, 3);\n    send_code(s, END_BLOCK, static_ltree);\n#ifdef ZLIB_DEBUG\n    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */\n#endif\n    bi_flush(s);\n}\n\n/* ===========================================================================\n * Determine the best encoding for the current block: dynamic trees, static\n * trees or store, and write out the encoded block.\n */\nvoid ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)\n    deflate_state *s;\n    charf *buf;       /* input block, or NULL if too old */\n    ulg stored_len;   /* length of input block */\n    int last;         /* one if this is the last block for a file */\n{\n    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */\n    int max_blindex = 0;  /* index of last bit length code of non zero freq */\n\n    /* Build the Huffman trees unless a stored block is forced */\n    if (s->level > 0) {\n\n        /* Check if the file is binary or text */\n        if (s->strm->data_type == Z_UNKNOWN)\n            s->strm->data_type = detect_data_type(s);\n\n        /* Construct the literal and distance trees */\n        build_tree(s, (tree_desc *)(&(s->l_desc)));\n        Tracev((stderr, \"\\nlit data: dyn %ld, stat %ld\", s->opt_len,\n                s->static_len));\n\n        build_tree(s, (tree_desc *)(&(s->d_desc)));\n        Tracev((stderr, \"\\ndist data: dyn %ld, stat %ld\", s->opt_len,\n                s->static_len));\n        /* At this point, opt_len and static_len are the total bit lengths of\n         * the compressed block data, excluding the tree representations.\n         */\n\n        /* Build the bit length tree for the above two trees, and get the index\n         * in bl_order of the last bit length code to send.\n         */\n        max_blindex = build_bl_tree(s);\n\n        /* Determine the best encoding. Compute the block lengths in bytes. */\n        opt_lenb = (s->opt_len+3+7)>>3;\n        static_lenb = (s->static_len+3+7)>>3;\n\n        Tracev((stderr, \"\\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u \",\n                opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,\n                s->last_lit));\n\n        if (static_lenb <= opt_lenb) opt_lenb = static_lenb;\n\n    } else {\n        Assert(buf != (char*)0, \"lost buf\");\n        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */\n    }\n\n#ifdef FORCE_STORED\n    if (buf != (char*)0) { /* force stored block */\n#else\n    if (stored_len+4 <= opt_lenb && buf != (char*)0) {\n                       /* 4: two words for the lengths */\n#endif\n        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.\n         * Otherwise we can't have processed more than WSIZE input bytes since\n         * the last block flush, because compression would have been\n         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to\n         * transform a block into a stored block.\n         */\n        _tr_stored_block(s, buf, stored_len, last);\n\n#ifdef FORCE_STATIC\n    } else if (static_lenb >= 0) { /* force static trees */\n#else\n    } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {\n#endif\n        send_bits(s, (STATIC_TREES<<1)+last, 3);\n        compress_block(s, (const ct_data *)static_ltree,\n                       (const ct_data *)static_dtree);\n#ifdef ZLIB_DEBUG\n        s->compressed_len += 3 + s->static_len;\n#endif\n    } else {\n        send_bits(s, (DYN_TREES<<1)+last, 3);\n        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,\n                       max_blindex+1);\n        compress_block(s, (const ct_data *)s->dyn_ltree,\n                       (const ct_data *)s->dyn_dtree);\n#ifdef ZLIB_DEBUG\n        s->compressed_len += 3 + s->opt_len;\n#endif\n    }\n    Assert (s->compressed_len == s->bits_sent, \"bad compressed size\");\n    /* The above check is made mod 2^32, for files larger than 512 MB\n     * and uLong implemented on 32 bits.\n     */\n    init_block(s);\n\n    if (last) {\n        bi_windup(s);\n#ifdef ZLIB_DEBUG\n        s->compressed_len += 7;  /* align on byte boundary */\n#endif\n    }\n    Tracev((stderr,\"\\ncomprlen %lu(%lu) \", s->compressed_len>>3,\n           s->compressed_len-7*last));\n}\n\n/* ===========================================================================\n * Save the match info and tally the frequency counts. Return true if\n * the current block must be flushed.\n */\nint ZLIB_INTERNAL _tr_tally (s, dist, lc)\n    deflate_state *s;\n    unsigned dist;  /* distance of matched string */\n    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */\n{\n    s->d_buf[s->last_lit] = (ush)dist;\n    s->l_buf[s->last_lit++] = (uch)lc;\n    if (dist == 0) {\n        /* lc is the unmatched char */\n        s->dyn_ltree[lc].Freq++;\n    } else {\n        s->matches++;\n        /* Here, lc is the match length - MIN_MATCH */\n        dist--;             /* dist = match distance - 1 */\n        Assert((ush)dist < (ush)MAX_DIST(s) &&\n               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&\n               (ush)d_code(dist) < (ush)D_CODES,  \"_tr_tally: bad match\");\n\n        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;\n        s->dyn_dtree[d_code(dist)].Freq++;\n    }\n\n#ifdef TRUNCATE_BLOCK\n    /* Try to guess if it is profitable to stop the current block here */\n    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {\n        /* Compute an upper bound for the compressed length */\n        ulg out_length = (ulg)s->last_lit*8L;\n        ulg in_length = (ulg)((long)s->strstart - s->block_start);\n        int dcode;\n        for (dcode = 0; dcode < D_CODES; dcode++) {\n            out_length += (ulg)s->dyn_dtree[dcode].Freq *\n                (5L+extra_dbits[dcode]);\n        }\n        out_length >>= 3;\n        Tracev((stderr,\"\\nlast_lit %u, in %ld, out ~%ld(%ld%%) \",\n               s->last_lit, in_length, out_length,\n               100L - out_length*100L/in_length));\n        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;\n    }\n#endif\n    return (s->last_lit == s->lit_bufsize-1);\n    /* We avoid equality with lit_bufsize because of wraparound at 64K\n     * on 16 bit machines and because stored blocks are restricted to\n     * 64K-1 bytes.\n     */\n}\n\n/* ===========================================================================\n * Send the block data compressed using the given Huffman trees\n */\nlocal void compress_block(s, ltree, dtree)\n    deflate_state *s;\n    const ct_data *ltree; /* literal tree */\n    const ct_data *dtree; /* distance tree */\n{\n    unsigned dist;      /* distance of matched string */\n    int lc;             /* match length or unmatched char (if dist == 0) */\n    unsigned lx = 0;    /* running index in l_buf */\n    unsigned code;      /* the code to send */\n    int extra;          /* number of extra bits to send */\n\n    if (s->last_lit != 0) do {\n        dist = s->d_buf[lx];\n        lc = s->l_buf[lx++];\n        if (dist == 0) {\n            send_code(s, lc, ltree); /* send a literal byte */\n            Tracecv(isgraph(lc), (stderr,\" '%c' \", lc));\n        } else {\n            /* Here, lc is the match length - MIN_MATCH */\n            code = _length_code[lc];\n            send_code(s, code+LITERALS+1, ltree); /* send the length code */\n            extra = extra_lbits[code];\n            if (extra != 0) {\n                lc -= base_length[code];\n                send_bits(s, lc, extra);       /* send the extra length bits */\n            }\n            dist--; /* dist is now the match distance - 1 */\n            code = d_code(dist);\n            Assert (code < D_CODES, \"bad d_code\");\n\n            send_code(s, code, dtree);       /* send the distance code */\n            extra = extra_dbits[code];\n            if (extra != 0) {\n                dist -= (unsigned)base_dist[code];\n                send_bits(s, dist, extra);   /* send the extra distance bits */\n            }\n        } /* literal or match pair ? */\n\n        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */\n        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,\n               \"pendingBuf overflow\");\n\n    } while (lx < s->last_lit);\n\n    send_code(s, END_BLOCK, ltree);\n}\n\n/* ===========================================================================\n * Check if the data type is TEXT or BINARY, using the following algorithm:\n * - TEXT if the two conditions below are satisfied:\n *    a) There are no non-portable control characters belonging to the\n *       \"black list\" (0..6, 14..25, 28..31).\n *    b) There is at least one printable character belonging to the\n *       \"white list\" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).\n * - BINARY otherwise.\n * - The following partially-portable control characters form a\n *   \"gray list\" that is ignored in this detection algorithm:\n *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).\n * IN assertion: the fields Freq of dyn_ltree are set.\n */\nlocal int detect_data_type(s)\n    deflate_state *s;\n{\n    /* black_mask is the bit mask of black-listed bytes\n     * set bits 0..6, 14..25, and 28..31\n     * 0xf3ffc07f = binary 11110011111111111100000001111111\n     */\n    unsigned long black_mask = 0xf3ffc07fUL;\n    int n;\n\n    /* Check for non-textual (\"black-listed\") bytes. */\n    for (n = 0; n <= 31; n++, black_mask >>= 1)\n        if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))\n            return Z_BINARY;\n\n    /* Check for textual (\"white-listed\") bytes. */\n    if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0\n            || s->dyn_ltree[13].Freq != 0)\n        return Z_TEXT;\n    for (n = 32; n < LITERALS; n++)\n        if (s->dyn_ltree[n].Freq != 0)\n            return Z_TEXT;\n\n    /* There are no \"black-listed\" or \"white-listed\" bytes:\n     * this stream either is empty or has tolerated (\"gray-listed\") bytes only.\n     */\n    return Z_BINARY;\n}\n\n/* ===========================================================================\n * Reverse the first len bits of a code, using straightforward code (a faster\n * method would use a table)\n * IN assertion: 1 <= len <= 15\n */\nlocal unsigned bi_reverse(code, len)\n    unsigned code; /* the value to invert */\n    int len;       /* its bit length */\n{\n    register unsigned res = 0;\n    do {\n        res |= code & 1;\n        code >>= 1, res <<= 1;\n    } while (--len > 0);\n    return res >> 1;\n}\n\n/* ===========================================================================\n * Flush the bit buffer, keeping at most 7 bits in it.\n */\nlocal void bi_flush(s)\n    deflate_state *s;\n{\n    if (s->bi_valid == 16) {\n        put_short(s, s->bi_buf);\n        s->bi_buf = 0;\n        s->bi_valid = 0;\n    } else if (s->bi_valid >= 8) {\n        put_byte(s, (Byte)s->bi_buf);\n        s->bi_buf >>= 8;\n        s->bi_valid -= 8;\n    }\n}\n\n/* ===========================================================================\n * Flush the bit buffer and align the output on a byte boundary\n */\nlocal void bi_windup(s)\n    deflate_state *s;\n{\n    if (s->bi_valid > 8) {\n        put_short(s, s->bi_buf);\n    } else if (s->bi_valid > 0) {\n        put_byte(s, (Byte)s->bi_buf);\n    }\n    s->bi_buf = 0;\n    s->bi_valid = 0;\n#ifdef ZLIB_DEBUG\n    s->bits_sent = (s->bits_sent+7) & ~7;\n#endif\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/trees.h",
    "content": "/* header created automatically with -DGEN_TREES_H */\n\nlocal const ct_data static_ltree[L_CODES+2] = {\n{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},\n{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},\n{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},\n{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},\n{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},\n{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},\n{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},\n{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},\n{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},\n{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},\n{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},\n{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},\n{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},\n{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},\n{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},\n{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},\n{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},\n{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},\n{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},\n{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},\n{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},\n{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},\n{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},\n{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},\n{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},\n{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},\n{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},\n{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},\n{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},\n{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},\n{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},\n{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},\n{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},\n{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},\n{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},\n{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},\n{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},\n{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},\n{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},\n{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},\n{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},\n{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},\n{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},\n{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},\n{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},\n{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},\n{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},\n{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},\n{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},\n{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},\n{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},\n{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},\n{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},\n{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},\n{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},\n{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},\n{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},\n{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}\n};\n\nlocal const ct_data static_dtree[D_CODES] = {\n{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},\n{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},\n{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},\n{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},\n{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},\n{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}\n};\n\nconst uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,\n 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,\n10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,\n11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,\n12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,\n13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,\n13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,\n14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,\n14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,\n14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,\n15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,\n15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,\n15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,\n18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,\n23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,\n26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,\n27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,\n27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,\n28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,\n28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,\n28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,\n29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,\n29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,\n29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29\n};\n\nconst uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,\n13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,\n17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,\n19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\n21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,\n22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,\n23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,\n26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,\n26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,\n27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28\n};\n\nlocal const int base_length[LENGTH_CODES] = {\n0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,\n64, 80, 96, 112, 128, 160, 192, 224, 0\n};\n\nlocal const int base_dist[D_CODES] = {\n    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,\n   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,\n 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576\n};\n\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/uncompr.c",
    "content": "/* uncompr.c -- decompress a memory buffer\n * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* @(#) $Id$ */\n\n#define ZLIB_INTERNAL\n#include \"zlib.h\"\n\n/* ===========================================================================\n     Decompresses the source buffer into the destination buffer.  *sourceLen is\n   the byte length of the source buffer. Upon entry, *destLen is the total size\n   of the destination buffer, which must be large enough to hold the entire\n   uncompressed data. (The size of the uncompressed data must have been saved\n   previously by the compressor and transmitted to the decompressor by some\n   mechanism outside the scope of this compression library.) Upon exit,\n   *destLen is the size of the decompressed data and *sourceLen is the number\n   of source bytes consumed. Upon return, source + *sourceLen points to the\n   first unused input byte.\n\n     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_BUF_ERROR if there was not enough room in the output buffer, or\n   Z_DATA_ERROR if the input data was corrupted, including if the input data is\n   an incomplete zlib stream.\n*/\nint ZEXPORT uncompress2 (dest, destLen, source, sourceLen)\n    Bytef *dest;\n    uLongf *destLen;\n    const Bytef *source;\n    uLong *sourceLen;\n{\n    z_stream stream;\n    int err;\n    const uInt max = (uInt)-1;\n    uLong len, left;\n    Byte buf[1];    /* for detection of incomplete stream when *destLen == 0 */\n\n    len = *sourceLen;\n    if (*destLen) {\n        left = *destLen;\n        *destLen = 0;\n    }\n    else {\n        left = 1;\n        dest = buf;\n    }\n\n    stream.next_in = (z_const Bytef *)source;\n    stream.avail_in = 0;\n    stream.zalloc = (alloc_func)0;\n    stream.zfree = (free_func)0;\n    stream.opaque = (voidpf)0;\n\n    err = inflateInit(&stream);\n    if (err != Z_OK) return err;\n\n    stream.next_out = dest;\n    stream.avail_out = 0;\n\n    do {\n        if (stream.avail_out == 0) {\n            stream.avail_out = left > (uLong)max ? max : (uInt)left;\n            left -= stream.avail_out;\n        }\n        if (stream.avail_in == 0) {\n            stream.avail_in = len > (uLong)max ? max : (uInt)len;\n            len -= stream.avail_in;\n        }\n        err = inflate(&stream, Z_NO_FLUSH);\n    } while (err == Z_OK);\n\n    *sourceLen -= len + stream.avail_in;\n    if (dest != buf)\n        *destLen = stream.total_out;\n    else if (stream.total_out && err == Z_BUF_ERROR)\n        left = 1;\n\n    inflateEnd(&stream);\n    return err == Z_STREAM_END ? Z_OK :\n           err == Z_NEED_DICT ? Z_DATA_ERROR  :\n           err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :\n           err;\n}\n\nint ZEXPORT uncompress (dest, destLen, source, sourceLen)\n    Bytef *dest;\n    uLongf *destLen;\n    const Bytef *source;\n    uLong sourceLen;\n{\n    return uncompress2(dest, destLen, source, &sourceLen);\n}\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/zconf.h",
    "content": "/* zconf.h -- configuration of the zlib compression library\n * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* @(#) $Id$ */\n\n#ifndef ZCONF_H\n#define ZCONF_H\n\n/*\n * If you *really* need a unique prefix for all types and library functions,\n * compile with -DZ_PREFIX. The \"standard\" zlib should be compiled without it.\n * Even better than compiling with -DZ_PREFIX would be to use configure to set\n * this permanently in zconf.h using \"./configure --zprefix\".\n */\n#ifdef Z_PREFIX     /* may be set to #if 1 by ./configure */\n#  define Z_PREFIX_SET\n\n/* all linked symbols and init macros */\n#  define _dist_code            z__dist_code\n#  define _length_code          z__length_code\n#  define _tr_align             z__tr_align\n#  define _tr_flush_bits        z__tr_flush_bits\n#  define _tr_flush_block       z__tr_flush_block\n#  define _tr_init              z__tr_init\n#  define _tr_stored_block      z__tr_stored_block\n#  define _tr_tally             z__tr_tally\n#  define adler32               z_adler32\n#  define adler32_combine       z_adler32_combine\n#  define adler32_combine64     z_adler32_combine64\n#  define adler32_z             z_adler32_z\n#  ifndef Z_SOLO\n#    define compress              z_compress\n#    define compress2             z_compress2\n#    define compressBound         z_compressBound\n#  endif\n#  define crc32                 z_crc32\n#  define crc32_combine         z_crc32_combine\n#  define crc32_combine64       z_crc32_combine64\n#  define crc32_z               z_crc32_z\n#  define deflate               z_deflate\n#  define deflateBound          z_deflateBound\n#  define deflateCopy           z_deflateCopy\n#  define deflateEnd            z_deflateEnd\n#  define deflateGetDictionary  z_deflateGetDictionary\n#  define deflateInit           z_deflateInit\n#  define deflateInit2          z_deflateInit2\n#  define deflateInit2_         z_deflateInit2_\n#  define deflateInit_          z_deflateInit_\n#  define deflateParams         z_deflateParams\n#  define deflatePending        z_deflatePending\n#  define deflatePrime          z_deflatePrime\n#  define deflateReset          z_deflateReset\n#  define deflateResetKeep      z_deflateResetKeep\n#  define deflateSetDictionary  z_deflateSetDictionary\n#  define deflateSetHeader      z_deflateSetHeader\n#  define deflateTune           z_deflateTune\n#  define deflate_copyright     z_deflate_copyright\n#  define get_crc_table         z_get_crc_table\n#  ifndef Z_SOLO\n#    define gz_error              z_gz_error\n#    define gz_intmax             z_gz_intmax\n#    define gz_strwinerror        z_gz_strwinerror\n#    define gzbuffer              z_gzbuffer\n#    define gzclearerr            z_gzclearerr\n#    define gzclose               z_gzclose\n#    define gzclose_r             z_gzclose_r\n#    define gzclose_w             z_gzclose_w\n#    define gzdirect              z_gzdirect\n#    define gzdopen               z_gzdopen\n#    define gzeof                 z_gzeof\n#    define gzerror               z_gzerror\n#    define gzflush               z_gzflush\n#    define gzfread               z_gzfread\n#    define gzfwrite              z_gzfwrite\n#    define gzgetc                z_gzgetc\n#    define gzgetc_               z_gzgetc_\n#    define gzgets                z_gzgets\n#    define gzoffset              z_gzoffset\n#    define gzoffset64            z_gzoffset64\n#    define gzopen                z_gzopen\n#    define gzopen64              z_gzopen64\n#    ifdef _WIN32\n#      define gzopen_w              z_gzopen_w\n#    endif\n#    define gzprintf              z_gzprintf\n#    define gzputc                z_gzputc\n#    define gzputs                z_gzputs\n#    define gzread                z_gzread\n#    define gzrewind              z_gzrewind\n#    define gzseek                z_gzseek\n#    define gzseek64              z_gzseek64\n#    define gzsetparams           z_gzsetparams\n#    define gztell                z_gztell\n#    define gztell64              z_gztell64\n#    define gzungetc              z_gzungetc\n#    define gzvprintf             z_gzvprintf\n#    define gzwrite               z_gzwrite\n#  endif\n#  define inflate               z_inflate\n#  define inflateBack           z_inflateBack\n#  define inflateBackEnd        z_inflateBackEnd\n#  define inflateBackInit       z_inflateBackInit\n#  define inflateBackInit_      z_inflateBackInit_\n#  define inflateCodesUsed      z_inflateCodesUsed\n#  define inflateCopy           z_inflateCopy\n#  define inflateEnd            z_inflateEnd\n#  define inflateGetDictionary  z_inflateGetDictionary\n#  define inflateGetHeader      z_inflateGetHeader\n#  define inflateInit           z_inflateInit\n#  define inflateInit2          z_inflateInit2\n#  define inflateInit2_         z_inflateInit2_\n#  define inflateInit_          z_inflateInit_\n#  define inflateMark           z_inflateMark\n#  define inflatePrime          z_inflatePrime\n#  define inflateReset          z_inflateReset\n#  define inflateReset2         z_inflateReset2\n#  define inflateResetKeep      z_inflateResetKeep\n#  define inflateSetDictionary  z_inflateSetDictionary\n#  define inflateSync           z_inflateSync\n#  define inflateSyncPoint      z_inflateSyncPoint\n#  define inflateUndermine      z_inflateUndermine\n#  define inflateValidate       z_inflateValidate\n#  define inflate_copyright     z_inflate_copyright\n#  define inflate_fast          z_inflate_fast\n#  define inflate_table         z_inflate_table\n#  ifndef Z_SOLO\n#    define uncompress            z_uncompress\n#    define uncompress2           z_uncompress2\n#  endif\n#  define zError                z_zError\n#  ifndef Z_SOLO\n#    define zcalloc               z_zcalloc\n#    define zcfree                z_zcfree\n#  endif\n#  define zlibCompileFlags      z_zlibCompileFlags\n#  define zlibVersion           z_zlibVersion\n\n/* all zlib typedefs in zlib.h and zconf.h */\n#  define Byte                  z_Byte\n#  define Bytef                 z_Bytef\n#  define alloc_func            z_alloc_func\n#  define charf                 z_charf\n#  define free_func             z_free_func\n#  ifndef Z_SOLO\n#    define gzFile                z_gzFile\n#  endif\n#  define gz_header             z_gz_header\n#  define gz_headerp            z_gz_headerp\n#  define in_func               z_in_func\n#  define intf                  z_intf\n#  define out_func              z_out_func\n#  define uInt                  z_uInt\n#  define uIntf                 z_uIntf\n#  define uLong                 z_uLong\n#  define uLongf                z_uLongf\n#  define voidp                 z_voidp\n#  define voidpc                z_voidpc\n#  define voidpf                z_voidpf\n\n/* all zlib structs in zlib.h and zconf.h */\n#  define gz_header_s           z_gz_header_s\n#  define internal_state        z_internal_state\n\n#endif\n\n#if defined(__MSDOS__) && !defined(MSDOS)\n#  define MSDOS\n#endif\n#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)\n#  define OS2\n#endif\n#if defined(_WINDOWS) && !defined(WINDOWS)\n#  define WINDOWS\n#endif\n#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)\n#  ifndef WIN32\n#    define WIN32\n#  endif\n#endif\n#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)\n#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)\n#    ifndef SYS16BIT\n#      define SYS16BIT\n#    endif\n#  endif\n#endif\n\n/*\n * Compile with -DMAXSEG_64K if the alloc function cannot allocate more\n * than 64k bytes at a time (needed on systems with 16-bit int).\n */\n#ifdef SYS16BIT\n#  define MAXSEG_64K\n#endif\n#ifdef MSDOS\n#  define UNALIGNED_OK\n#endif\n\n#ifdef __STDC_VERSION__\n#  ifndef STDC\n#    define STDC\n#  endif\n#  if __STDC_VERSION__ >= 199901L\n#    ifndef STDC99\n#      define STDC99\n#    endif\n#  endif\n#endif\n#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))\n#  define STDC\n#endif\n#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))\n#  define STDC\n#endif\n#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))\n#  define STDC\n#endif\n#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))\n#  define STDC\n#endif\n\n#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */\n#  define STDC\n#endif\n\n#ifndef STDC\n#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */\n#    define const       /* note: need a more gentle solution here */\n#  endif\n#endif\n\n#if defined(ZLIB_CONST) && !defined(z_const)\n#  define z_const const\n#else\n#  define z_const\n#endif\n\n#ifdef Z_SOLO\n   typedef unsigned long z_size_t;\n#else\n#  define z_longlong long long\n#  if defined(NO_SIZE_T)\n     typedef unsigned NO_SIZE_T z_size_t;\n#  elif defined(STDC)\n#    include <stddef.h>\n     typedef size_t z_size_t;\n#  else\n     typedef unsigned long z_size_t;\n#  endif\n#  undef z_longlong\n#endif\n\n/* Maximum value for memLevel in deflateInit2 */\n#ifndef MAX_MEM_LEVEL\n#  ifdef MAXSEG_64K\n#    define MAX_MEM_LEVEL 8\n#  else\n#    define MAX_MEM_LEVEL 9\n#  endif\n#endif\n\n/* Maximum value for windowBits in deflateInit2 and inflateInit2.\n * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files\n * created by gzip. (Files created by minigzip can still be extracted by\n * gzip.)\n */\n#ifndef MAX_WBITS\n#  define MAX_WBITS   15 /* 32K LZ77 window */\n#endif\n\n/* The memory requirements for deflate are (in bytes):\n            (1 << (windowBits+2)) +  (1 << (memLevel+9))\n that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)\n plus a few kilobytes for small objects. For example, if you want to reduce\n the default memory requirements from 256K to 128K, compile with\n     make CFLAGS=\"-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7\"\n Of course this will generally degrade compression (there's no free lunch).\n\n   The memory requirements for inflate are (in bytes) 1 << windowBits\n that is, 32K for windowBits=15 (default value) plus about 7 kilobytes\n for small objects.\n*/\n\n                        /* Type declarations */\n\n#ifndef OF /* function prototypes */\n#  ifdef STDC\n#    define OF(args)  args\n#  else\n#    define OF(args)  ()\n#  endif\n#endif\n\n#ifndef Z_ARG /* function prototypes for stdarg */\n#  if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#    define Z_ARG(args)  args\n#  else\n#    define Z_ARG(args)  ()\n#  endif\n#endif\n\n/* The following definitions for FAR are needed only for MSDOS mixed\n * model programming (small or medium model with some far allocations).\n * This was tested only with MSC; for other MSDOS compilers you may have\n * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,\n * just define FAR to be empty.\n */\n#ifdef SYS16BIT\n#  if defined(M_I86SM) || defined(M_I86MM)\n     /* MSC small or medium model */\n#    define SMALL_MEDIUM\n#    ifdef _MSC_VER\n#      define FAR _far\n#    else\n#      define FAR far\n#    endif\n#  endif\n#  if (defined(__SMALL__) || defined(__MEDIUM__))\n     /* Turbo C small or medium model */\n#    define SMALL_MEDIUM\n#    ifdef __BORLANDC__\n#      define FAR _far\n#    else\n#      define FAR far\n#    endif\n#  endif\n#endif\n\n#if defined(WINDOWS) || defined(WIN32)\n   /* If building or using zlib as a DLL, define ZLIB_DLL.\n    * This is not mandatory, but it offers a little performance increase.\n    */\n#  ifdef ZLIB_DLL\n#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))\n#      ifdef ZLIB_INTERNAL\n#        define ZEXTERN extern __declspec(dllexport)\n#      else\n#        define ZEXTERN extern __declspec(dllimport)\n#      endif\n#    endif\n#  endif  /* ZLIB_DLL */\n   /* If building or using zlib with the WINAPI/WINAPIV calling convention,\n    * define ZLIB_WINAPI.\n    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.\n    */\n#  ifdef ZLIB_WINAPI\n#    ifdef FAR\n#      undef FAR\n#    endif\n#    include <windows.h>\n     /* No need for _export, use ZLIB.DEF instead. */\n     /* For complete Windows compatibility, use WINAPI, not __stdcall. */\n#    define ZEXPORT WINAPI\n#    ifdef WIN32\n#      define ZEXPORTVA WINAPIV\n#    else\n#      define ZEXPORTVA FAR CDECL\n#    endif\n#  endif\n#endif\n\n#if defined (__BEOS__)\n#  ifdef ZLIB_DLL\n#    ifdef ZLIB_INTERNAL\n#      define ZEXPORT   __declspec(dllexport)\n#      define ZEXPORTVA __declspec(dllexport)\n#    else\n#      define ZEXPORT   __declspec(dllimport)\n#      define ZEXPORTVA __declspec(dllimport)\n#    endif\n#  endif\n#endif\n\n#ifndef ZEXTERN\n#  define ZEXTERN extern\n#endif\n#ifndef ZEXPORT\n#  define ZEXPORT\n#endif\n#ifndef ZEXPORTVA\n#  define ZEXPORTVA\n#endif\n\n#ifndef FAR\n#  define FAR\n#endif\n\n#if !defined(__MACTYPES__)\ntypedef unsigned char  Byte;  /* 8 bits */\n#endif\ntypedef unsigned int   uInt;  /* 16 bits or more */\ntypedef unsigned long  uLong; /* 32 bits or more */\n\n#ifdef SMALL_MEDIUM\n   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */\n#  define Bytef Byte FAR\n#else\n   typedef Byte  FAR Bytef;\n#endif\ntypedef char  FAR charf;\ntypedef int   FAR intf;\ntypedef uInt  FAR uIntf;\ntypedef uLong FAR uLongf;\n\n#ifdef STDC\n   typedef void const *voidpc;\n   typedef void FAR   *voidpf;\n   typedef void       *voidp;\n#else\n   typedef Byte const *voidpc;\n   typedef Byte FAR   *voidpf;\n   typedef Byte       *voidp;\n#endif\n\n#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)\n#  include <limits.h>\n#  if (UINT_MAX == 0xffffffffUL)\n#    define Z_U4 unsigned\n#  elif (ULONG_MAX == 0xffffffffUL)\n#    define Z_U4 unsigned long\n#  elif (USHRT_MAX == 0xffffffffUL)\n#    define Z_U4 unsigned short\n#  endif\n#endif\n\n#ifdef Z_U4\n   typedef Z_U4 z_crc_t;\n#else\n   typedef unsigned long z_crc_t;\n#endif\n\n#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */\n#  define Z_HAVE_UNISTD_H\n#endif\n\n#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */\n#  define Z_HAVE_STDARG_H\n#endif\n\n#ifdef STDC\n#  ifndef Z_SOLO\n#    include <sys/types.h>      /* for off_t */\n#  endif\n#endif\n\n#if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#  ifndef Z_SOLO\n#    include <stdarg.h>         /* for va_list */\n#  endif\n#endif\n\n#ifdef _WIN32\n#  ifndef Z_SOLO\n#    include <stddef.h>         /* for wchar_t */\n#  endif\n#endif\n\n/* a little trick to accommodate both \"#define _LARGEFILE64_SOURCE\" and\n * \"#define _LARGEFILE64_SOURCE 1\" as requesting 64-bit operations, (even\n * though the former does not conform to the LFS document), but considering\n * both \"#undef _LARGEFILE64_SOURCE\" and \"#define _LARGEFILE64_SOURCE 0\" as\n * equivalently requesting no 64-bit operations\n */\n#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1\n#  undef _LARGEFILE64_SOURCE\n#endif\n\n#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)\n#  define Z_HAVE_UNISTD_H\n#endif\n#ifndef Z_SOLO\n#  if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)\n#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */\n#    ifdef VMS\n#      include <unixio.h>       /* for off_t */\n#    endif\n#    ifndef z_off_t\n#      define z_off_t off_t\n#    endif\n#  endif\n#endif\n\n#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0\n#  define Z_LFS64\n#endif\n\n#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)\n#  define Z_LARGE64\n#endif\n\n#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)\n#  define Z_WANT64\n#endif\n\n#if !defined(SEEK_SET) && !defined(Z_SOLO)\n#  define SEEK_SET        0       /* Seek from beginning of file.  */\n#  define SEEK_CUR        1       /* Seek from current position.  */\n#  define SEEK_END        2       /* Set file pointer to EOF plus \"offset\" */\n#endif\n\n#ifndef z_off_t\n#  define z_off_t long\n#endif\n\n#if !defined(_WIN32) && defined(Z_LARGE64)\n#  define z_off64_t off64_t\n#else\n#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)\n#    define z_off64_t __int64\n#  else\n#    define z_off64_t z_off_t\n#  endif\n#endif\n\n/* MVS linker does not support external names larger than 8 bytes */\n#if defined(__MVS__)\n  #pragma map(deflateInit_,\"DEIN\")\n  #pragma map(deflateInit2_,\"DEIN2\")\n  #pragma map(deflateEnd,\"DEEND\")\n  #pragma map(deflateBound,\"DEBND\")\n  #pragma map(inflateInit_,\"ININ\")\n  #pragma map(inflateInit2_,\"ININ2\")\n  #pragma map(inflateEnd,\"INEND\")\n  #pragma map(inflateSync,\"INSY\")\n  #pragma map(inflateSetDictionary,\"INSEDI\")\n  #pragma map(compressBound,\"CMBND\")\n  #pragma map(inflate_table,\"INTABL\")\n  #pragma map(inflate_fast,\"INFA\")\n  #pragma map(inflate_copyright,\"INCOPY\")\n#endif\n\n#endif /* ZCONF_H */\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/zlib.h",
    "content": "/* zlib.h -- interface of the 'zlib' general purpose compression library\n  version 1.2.11, January 15th, 2017\n\n  Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n\n  Jean-loup Gailly        Mark Adler\n  jloup@gzip.org          madler@alumni.caltech.edu\n\n\n  The data format used by the zlib library is described by RFCs (Request for\n  Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950\n  (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).\n*/\n\n#ifndef ZLIB_H\n#define ZLIB_H\n\n#include \"zconf.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ZLIB_VERSION \"1.2.11\"\n#define ZLIB_VERNUM 0x12b0\n#define ZLIB_VER_MAJOR 1\n#define ZLIB_VER_MINOR 2\n#define ZLIB_VER_REVISION 11\n#define ZLIB_VER_SUBREVISION 0\n\n/*\n    The 'zlib' compression library provides in-memory compression and\n  decompression functions, including integrity checks of the uncompressed data.\n  This version of the library supports only one compression method (deflation)\n  but other algorithms will be added later and will have the same stream\n  interface.\n\n    Compression can be done in a single step if the buffers are large enough,\n  or can be done by repeated calls of the compression function.  In the latter\n  case, the application must provide more input and/or consume the output\n  (providing more output space) before each call.\n\n    The compressed data format used by default by the in-memory functions is\n  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped\n  around a deflate stream, which is itself documented in RFC 1951.\n\n    The library also supports reading and writing files in gzip (.gz) format\n  with an interface similar to that of stdio using the functions that start\n  with \"gz\".  The gzip format is different from the zlib format.  gzip is a\n  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.\n\n    This library can optionally read and write gzip and raw deflate streams in\n  memory as well.\n\n    The zlib format was designed to be compact and fast for use in memory\n  and on communications channels.  The gzip format was designed for single-\n  file compression on file systems, has a larger header than zlib to maintain\n  directory information, and uses a different, slower check method than zlib.\n\n    The library does not install any signal handler.  The decoder checks\n  the consistency of the compressed data, so the library should never crash\n  even in the case of corrupted input.\n*/\n\ntypedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));\ntypedef void   (*free_func)  OF((voidpf opaque, voidpf address));\n\nstruct internal_state;\n\ntypedef struct z_stream_s {\n    z_const Bytef *next_in;     /* next input byte */\n    uInt     avail_in;  /* number of bytes available at next_in */\n    uLong    total_in;  /* total number of input bytes read so far */\n\n    Bytef    *next_out; /* next output byte will go here */\n    uInt     avail_out; /* remaining free space at next_out */\n    uLong    total_out; /* total number of bytes output so far */\n\n    z_const char *msg;  /* last error message, NULL if no error */\n    struct internal_state FAR *state; /* not visible by applications */\n\n    alloc_func zalloc;  /* used to allocate the internal state */\n    free_func  zfree;   /* used to free the internal state */\n    voidpf     opaque;  /* private data object passed to zalloc and zfree */\n\n    int     data_type;  /* best guess about the data type: binary or text\n                           for deflate, or the decoding state for inflate */\n    uLong   adler;      /* Adler-32 or CRC-32 value of the uncompressed data */\n    uLong   reserved;   /* reserved for future use */\n} z_stream;\n\ntypedef z_stream FAR *z_streamp;\n\n/*\n     gzip header information passed to and from zlib routines.  See RFC 1952\n  for more details on the meanings of these fields.\n*/\ntypedef struct gz_header_s {\n    int     text;       /* true if compressed data believed to be text */\n    uLong   time;       /* modification time */\n    int     xflags;     /* extra flags (not used when writing a gzip file) */\n    int     os;         /* operating system */\n    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */\n    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */\n    uInt    extra_max;  /* space at extra (only when reading header) */\n    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */\n    uInt    name_max;   /* space at name (only when reading header) */\n    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */\n    uInt    comm_max;   /* space at comment (only when reading header) */\n    int     hcrc;       /* true if there was or will be a header crc */\n    int     done;       /* true when done reading gzip header (not used\n                           when writing a gzip file) */\n} gz_header;\n\ntypedef gz_header FAR *gz_headerp;\n\n/*\n     The application must update next_in and avail_in when avail_in has dropped\n   to zero.  It must update next_out and avail_out when avail_out has dropped\n   to zero.  The application must initialize zalloc, zfree and opaque before\n   calling the init function.  All other fields are set by the compression\n   library and must not be updated by the application.\n\n     The opaque value provided by the application will be passed as the first\n   parameter for calls of zalloc and zfree.  This can be useful for custom\n   memory management.  The compression library attaches no meaning to the\n   opaque value.\n\n     zalloc must return Z_NULL if there is not enough memory for the object.\n   If zlib is used in a multi-threaded application, zalloc and zfree must be\n   thread safe.  In that case, zlib is thread-safe.  When zalloc and zfree are\n   Z_NULL on entry to the initialization function, they are set to internal\n   routines that use the standard library functions malloc() and free().\n\n     On 16-bit systems, the functions zalloc and zfree must be able to allocate\n   exactly 65536 bytes, but will not be required to allocate more than this if\n   the symbol MAXSEG_64K is defined (see zconf.h).  WARNING: On MSDOS, pointers\n   returned by zalloc for objects of exactly 65536 bytes *must* have their\n   offset normalized to zero.  The default allocation function provided by this\n   library ensures this (see zutil.c).  To reduce memory requirements and avoid\n   any allocation of 64K objects, at the expense of compression ratio, compile\n   the library with -DMAX_WBITS=14 (see zconf.h).\n\n     The fields total_in and total_out can be used for statistics or progress\n   reports.  After compression, total_in holds the total size of the\n   uncompressed data and may be saved for use by the decompressor (particularly\n   if the decompressor wants to decompress everything in a single step).\n*/\n\n                        /* constants */\n\n#define Z_NO_FLUSH      0\n#define Z_PARTIAL_FLUSH 1\n#define Z_SYNC_FLUSH    2\n#define Z_FULL_FLUSH    3\n#define Z_FINISH        4\n#define Z_BLOCK         5\n#define Z_TREES         6\n/* Allowed flush values; see deflate() and inflate() below for details */\n\n#define Z_OK            0\n#define Z_STREAM_END    1\n#define Z_NEED_DICT     2\n#define Z_ERRNO        (-1)\n#define Z_STREAM_ERROR (-2)\n#define Z_DATA_ERROR   (-3)\n#define Z_MEM_ERROR    (-4)\n#define Z_BUF_ERROR    (-5)\n#define Z_VERSION_ERROR (-6)\n/* Return codes for the compression/decompression functions. Negative values\n * are errors, positive values are used for special but normal events.\n */\n\n#define Z_NO_COMPRESSION         0\n#define Z_BEST_SPEED             1\n#define Z_BEST_COMPRESSION       9\n#define Z_DEFAULT_COMPRESSION  (-1)\n/* compression levels */\n\n#define Z_FILTERED            1\n#define Z_HUFFMAN_ONLY        2\n#define Z_RLE                 3\n#define Z_FIXED               4\n#define Z_DEFAULT_STRATEGY    0\n/* compression strategy; see deflateInit2() below for details */\n\n#define Z_BINARY   0\n#define Z_TEXT     1\n#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */\n#define Z_UNKNOWN  2\n/* Possible values of the data_type field for deflate() */\n\n#define Z_DEFLATED   8\n/* The deflate compression method (the only one supported in this version) */\n\n#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */\n\n#define zlib_version zlibVersion()\n/* for compatibility with versions < 1.0.2 */\n\n\n                        /* basic functions */\n\nZEXTERN const char * ZEXPORT zlibVersion OF((void));\n/* The application can compare zlibVersion and ZLIB_VERSION for consistency.\n   If the first character differs, the library code actually used is not\n   compatible with the zlib.h header file used by the application.  This check\n   is automatically made by deflateInit and inflateInit.\n */\n\n/*\nZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));\n\n     Initializes the internal stream state for compression.  The fields\n   zalloc, zfree and opaque must be initialized before by the caller.  If\n   zalloc and zfree are set to Z_NULL, deflateInit updates them to use default\n   allocation functions.\n\n     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:\n   1 gives best speed, 9 gives best compression, 0 gives no compression at all\n   (the input data is simply copied a block at a time).  Z_DEFAULT_COMPRESSION\n   requests a default compromise between speed and compression (currently\n   equivalent to level 6).\n\n     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_STREAM_ERROR if level is not a valid compression level, or\n   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible\n   with the version assumed by the caller (ZLIB_VERSION).  msg is set to null\n   if there is no error message.  deflateInit does not perform any compression:\n   this will be done by deflate().\n*/\n\n\nZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));\n/*\n    deflate compresses as much data as possible, and stops when the input\n  buffer becomes empty or the output buffer becomes full.  It may introduce\n  some output latency (reading input without producing any output) except when\n  forced to flush.\n\n    The detailed semantics are as follows.  deflate performs one or both of the\n  following actions:\n\n  - Compress more input starting at next_in and update next_in and avail_in\n    accordingly.  If not all input can be processed (because there is not\n    enough room in the output buffer), next_in and avail_in are updated and\n    processing will resume at this point for the next call of deflate().\n\n  - Generate more output starting at next_out and update next_out and avail_out\n    accordingly.  This action is forced if the parameter flush is non zero.\n    Forcing flush frequently degrades the compression ratio, so this parameter\n    should be set only when necessary.  Some output may be provided even if\n    flush is zero.\n\n    Before the call of deflate(), the application should ensure that at least\n  one of the actions is possible, by providing more input and/or consuming more\n  output, and updating avail_in or avail_out accordingly; avail_out should\n  never be zero before the call.  The application can consume the compressed\n  output when it wants, for example when the output buffer is full (avail_out\n  == 0), or after each call of deflate().  If deflate returns Z_OK and with\n  zero avail_out, it must be called again after making room in the output\n  buffer because there might be more output pending. See deflatePending(),\n  which can be used if desired to determine whether or not there is more ouput\n  in that case.\n\n    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to\n  decide how much data to accumulate before producing output, in order to\n  maximize compression.\n\n    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is\n  flushed to the output buffer and the output is aligned on a byte boundary, so\n  that the decompressor can get all input data available so far.  (In\n  particular avail_in is zero after the call if enough output space has been\n  provided before the call.) Flushing may degrade compression for some\n  compression algorithms and so it should be used only when necessary.  This\n  completes the current deflate block and follows it with an empty stored block\n  that is three bits plus filler bits to the next byte, followed by four bytes\n  (00 00 ff ff).\n\n    If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the\n  output buffer, but the output is not aligned to a byte boundary.  All of the\n  input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.\n  This completes the current deflate block and follows it with an empty fixed\n  codes block that is 10 bits long.  This assures that enough bytes are output\n  in order for the decompressor to finish the block before the empty fixed\n  codes block.\n\n    If flush is set to Z_BLOCK, a deflate block is completed and emitted, as\n  for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to\n  seven bits of the current block are held to be written as the next byte after\n  the next deflate block is completed.  In this case, the decompressor may not\n  be provided enough bits at this point in order to complete decompression of\n  the data provided so far to the compressor.  It may need to wait for the next\n  block to be emitted.  This is for advanced applications that need to control\n  the emission of deflate blocks.\n\n    If flush is set to Z_FULL_FLUSH, all output is flushed as with\n  Z_SYNC_FLUSH, and the compression state is reset so that decompression can\n  restart from this point if previous compressed data has been damaged or if\n  random access is desired.  Using Z_FULL_FLUSH too often can seriously degrade\n  compression.\n\n    If deflate returns with avail_out == 0, this function must be called again\n  with the same value of the flush parameter and more output space (updated\n  avail_out), until the flush is complete (deflate returns with non-zero\n  avail_out).  In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that\n  avail_out is greater than six to avoid repeated flush markers due to\n  avail_out == 0 on return.\n\n    If the parameter flush is set to Z_FINISH, pending input is processed,\n  pending output is flushed and deflate returns with Z_STREAM_END if there was\n  enough output space.  If deflate returns with Z_OK or Z_BUF_ERROR, this\n  function must be called again with Z_FINISH and more output space (updated\n  avail_out) but no more input data, until it returns with Z_STREAM_END or an\n  error.  After deflate has returned Z_STREAM_END, the only possible operations\n  on the stream are deflateReset or deflateEnd.\n\n    Z_FINISH can be used in the first deflate call after deflateInit if all the\n  compression is to be done in a single step.  In order to complete in one\n  call, avail_out must be at least the value returned by deflateBound (see\n  below).  Then deflate is guaranteed to return Z_STREAM_END.  If not enough\n  output space is provided, deflate will not return Z_STREAM_END, and it must\n  be called again as described above.\n\n    deflate() sets strm->adler to the Adler-32 checksum of all input read\n  so far (that is, total_in bytes).  If a gzip stream is being generated, then\n  strm->adler will be the CRC-32 checksum of the input read so far.  (See\n  deflateInit2 below.)\n\n    deflate() may update strm->data_type if it can make a good guess about\n  the input data type (Z_BINARY or Z_TEXT).  If in doubt, the data is\n  considered binary.  This field is only for information purposes and does not\n  affect the compression algorithm in any manner.\n\n    deflate() returns Z_OK if some progress has been made (more input\n  processed or more output produced), Z_STREAM_END if all input has been\n  consumed and all output has been produced (only when flush is set to\n  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example\n  if next_in or next_out was Z_NULL or the state was inadvertently written over\n  by the application), or Z_BUF_ERROR if no progress is possible (for example\n  avail_in or avail_out was zero).  Note that Z_BUF_ERROR is not fatal, and\n  deflate() can be called again with more input and more output space to\n  continue compressing.\n*/\n\n\nZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));\n/*\n     All dynamically allocated data structures for this stream are freed.\n   This function discards any unprocessed input and does not flush any pending\n   output.\n\n     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the\n   stream state was inconsistent, Z_DATA_ERROR if the stream was freed\n   prematurely (some input or output was discarded).  In the error case, msg\n   may be set but then points to a static string (which must not be\n   deallocated).\n*/\n\n\n/*\nZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));\n\n     Initializes the internal stream state for decompression.  The fields\n   next_in, avail_in, zalloc, zfree and opaque must be initialized before by\n   the caller.  In the current version of inflate, the provided input is not\n   read or consumed.  The allocation of a sliding window will be deferred to\n   the first call of inflate (if the decompression does not complete on the\n   first call).  If zalloc and zfree are set to Z_NULL, inflateInit updates\n   them to use default allocation functions.\n\n     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the\n   version assumed by the caller, or Z_STREAM_ERROR if the parameters are\n   invalid, such as a null pointer to the structure.  msg is set to null if\n   there is no error message.  inflateInit does not perform any decompression.\n   Actual decompression will be done by inflate().  So next_in, and avail_in,\n   next_out, and avail_out are unused and unchanged.  The current\n   implementation of inflateInit() does not process any header information --\n   that is deferred until inflate() is called.\n*/\n\n\nZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));\n/*\n    inflate decompresses as much data as possible, and stops when the input\n  buffer becomes empty or the output buffer becomes full.  It may introduce\n  some output latency (reading input without producing any output) except when\n  forced to flush.\n\n  The detailed semantics are as follows.  inflate performs one or both of the\n  following actions:\n\n  - Decompress more input starting at next_in and update next_in and avail_in\n    accordingly.  If not all input can be processed (because there is not\n    enough room in the output buffer), then next_in and avail_in are updated\n    accordingly, and processing will resume at this point for the next call of\n    inflate().\n\n  - Generate more output starting at next_out and update next_out and avail_out\n    accordingly.  inflate() provides as much output as possible, until there is\n    no more input data or no more space in the output buffer (see below about\n    the flush parameter).\n\n    Before the call of inflate(), the application should ensure that at least\n  one of the actions is possible, by providing more input and/or consuming more\n  output, and updating the next_* and avail_* values accordingly.  If the\n  caller of inflate() does not provide both available input and available\n  output space, it is possible that there will be no progress made.  The\n  application can consume the uncompressed output when it wants, for example\n  when the output buffer is full (avail_out == 0), or after each call of\n  inflate().  If inflate returns Z_OK and with zero avail_out, it must be\n  called again after making room in the output buffer because there might be\n  more output pending.\n\n    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,\n  Z_BLOCK, or Z_TREES.  Z_SYNC_FLUSH requests that inflate() flush as much\n  output as possible to the output buffer.  Z_BLOCK requests that inflate()\n  stop if and when it gets to the next deflate block boundary.  When decoding\n  the zlib or gzip format, this will cause inflate() to return immediately\n  after the header and before the first block.  When doing a raw inflate,\n  inflate() will go ahead and process the first block, and will return when it\n  gets to the end of that block, or when it runs out of data.\n\n    The Z_BLOCK option assists in appending to or combining deflate streams.\n  To assist in this, on return inflate() always sets strm->data_type to the\n  number of unused bits in the last byte taken from strm->next_in, plus 64 if\n  inflate() is currently decoding the last block in the deflate stream, plus\n  128 if inflate() returned immediately after decoding an end-of-block code or\n  decoding the complete header up to just before the first byte of the deflate\n  stream.  The end-of-block will not be indicated until all of the uncompressed\n  data from that block has been written to strm->next_out.  The number of\n  unused bits may in general be greater than seven, except when bit 7 of\n  data_type is set, in which case the number of unused bits will be less than\n  eight.  data_type is set as noted here every time inflate() returns for all\n  flush options, and so can be used to determine the amount of currently\n  consumed input in bits.\n\n    The Z_TREES option behaves as Z_BLOCK does, but it also returns when the\n  end of each deflate block header is reached, before any actual data in that\n  block is decoded.  This allows the caller to determine the length of the\n  deflate block header for later use in random access within a deflate block.\n  256 is added to the value of strm->data_type when inflate() returns\n  immediately after reaching the end of the deflate block header.\n\n    inflate() should normally be called until it returns Z_STREAM_END or an\n  error.  However if all decompression is to be performed in a single step (a\n  single call of inflate), the parameter flush should be set to Z_FINISH.  In\n  this case all pending input is processed and all pending output is flushed;\n  avail_out must be large enough to hold all of the uncompressed data for the\n  operation to complete.  (The size of the uncompressed data may have been\n  saved by the compressor for this purpose.)  The use of Z_FINISH is not\n  required to perform an inflation in one step.  However it may be used to\n  inform inflate that a faster approach can be used for the single inflate()\n  call.  Z_FINISH also informs inflate to not maintain a sliding window if the\n  stream completes, which reduces inflate's memory footprint.  If the stream\n  does not complete, either because not all of the stream is provided or not\n  enough output space is provided, then a sliding window will be allocated and\n  inflate() can be called again to continue the operation as if Z_NO_FLUSH had\n  been used.\n\n     In this implementation, inflate() always flushes as much output as\n  possible to the output buffer, and always uses the faster approach on the\n  first call.  So the effects of the flush parameter in this implementation are\n  on the return value of inflate() as noted below, when inflate() returns early\n  when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of\n  memory for a sliding window when Z_FINISH is used.\n\n     If a preset dictionary is needed after this call (see inflateSetDictionary\n  below), inflate sets strm->adler to the Adler-32 checksum of the dictionary\n  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets\n  strm->adler to the Adler-32 checksum of all output produced so far (that is,\n  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described\n  below.  At the end of the stream, inflate() checks that its computed Adler-32\n  checksum is equal to that saved by the compressor and returns Z_STREAM_END\n  only if the checksum is correct.\n\n    inflate() can decompress and check either zlib-wrapped or gzip-wrapped\n  deflate data.  The header type is detected automatically, if requested when\n  initializing with inflateInit2().  Any information contained in the gzip\n  header is not retained unless inflateGetHeader() is used.  When processing\n  gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output\n  produced so far.  The CRC-32 is checked against the gzip trailer, as is the\n  uncompressed length, modulo 2^32.\n\n    inflate() returns Z_OK if some progress has been made (more input processed\n  or more output produced), Z_STREAM_END if the end of the compressed data has\n  been reached and all uncompressed output has been produced, Z_NEED_DICT if a\n  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was\n  corrupted (input stream not conforming to the zlib format or incorrect check\n  value, in which case strm->msg points to a string with a more specific\n  error), Z_STREAM_ERROR if the stream structure was inconsistent (for example\n  next_in or next_out was Z_NULL, or the state was inadvertently written over\n  by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR\n  if no progress was possible or if there was not enough room in the output\n  buffer when Z_FINISH is used.  Note that Z_BUF_ERROR is not fatal, and\n  inflate() can be called again with more input and more output space to\n  continue decompressing.  If Z_DATA_ERROR is returned, the application may\n  then call inflateSync() to look for a good compression block if a partial\n  recovery of the data is to be attempted.\n*/\n\n\nZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));\n/*\n     All dynamically allocated data structures for this stream are freed.\n   This function discards any unprocessed input and does not flush any pending\n   output.\n\n     inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state\n   was inconsistent.\n*/\n\n\n                        /* Advanced functions */\n\n/*\n    The following functions are needed only in some special applications.\n*/\n\n/*\nZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,\n                                     int  level,\n                                     int  method,\n                                     int  windowBits,\n                                     int  memLevel,\n                                     int  strategy));\n\n     This is another version of deflateInit with more compression options.  The\n   fields next_in, zalloc, zfree and opaque must be initialized before by the\n   caller.\n\n     The method parameter is the compression method.  It must be Z_DEFLATED in\n   this version of the library.\n\n     The windowBits parameter is the base two logarithm of the window size\n   (the size of the history buffer).  It should be in the range 8..15 for this\n   version of the library.  Larger values of this parameter result in better\n   compression at the expense of memory usage.  The default value is 15 if\n   deflateInit is used instead.\n\n     For the current implementation of deflate(), a windowBits value of 8 (a\n   window size of 256 bytes) is not supported.  As a result, a request for 8\n   will result in 9 (a 512-byte window).  In that case, providing 8 to\n   inflateInit2() will result in an error when the zlib header with 9 is\n   checked against the initialization of inflate().  The remedy is to not use 8\n   with deflateInit2() with this initialization, or at least in that case use 9\n   with inflateInit2().\n\n     windowBits can also be -8..-15 for raw deflate.  In this case, -windowBits\n   determines the window size.  deflate() will then generate raw deflate data\n   with no zlib header or trailer, and will not compute a check value.\n\n     windowBits can also be greater than 15 for optional gzip encoding.  Add\n   16 to windowBits to write a simple gzip header and trailer around the\n   compressed data instead of a zlib wrapper.  The gzip header will have no\n   file name, no extra data, no comment, no modification time (set to zero), no\n   header crc, and the operating system will be set to the appropriate value,\n   if the operating system was determined at compile time.  If a gzip stream is\n   being written, strm->adler is a CRC-32 instead of an Adler-32.\n\n     For raw deflate or gzip encoding, a request for a 256-byte window is\n   rejected as invalid, since only the zlib header provides a means of\n   transmitting the window size to the decompressor.\n\n     The memLevel parameter specifies how much memory should be allocated\n   for the internal compression state.  memLevel=1 uses minimum memory but is\n   slow and reduces compression ratio; memLevel=9 uses maximum memory for\n   optimal speed.  The default value is 8.  See zconf.h for total memory usage\n   as a function of windowBits and memLevel.\n\n     The strategy parameter is used to tune the compression algorithm.  Use the\n   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a\n   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no\n   string match), or Z_RLE to limit match distances to one (run-length\n   encoding).  Filtered data consists mostly of small values with a somewhat\n   random distribution.  In this case, the compression algorithm is tuned to\n   compress them better.  The effect of Z_FILTERED is to force more Huffman\n   coding and less string matching; it is somewhat intermediate between\n   Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.  Z_RLE is designed to be almost as\n   fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data.  The\n   strategy parameter only affects the compression ratio but not the\n   correctness of the compressed output even if it is not set appropriately.\n   Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler\n   decoder for special applications.\n\n     deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid\n   method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is\n   incompatible with the version assumed by the caller (ZLIB_VERSION).  msg is\n   set to null if there is no error message.  deflateInit2 does not perform any\n   compression: this will be done by deflate().\n*/\n\nZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,\n                                             const Bytef *dictionary,\n                                             uInt  dictLength));\n/*\n     Initializes the compression dictionary from the given byte sequence\n   without producing any compressed output.  When using the zlib format, this\n   function must be called immediately after deflateInit, deflateInit2 or\n   deflateReset, and before any call of deflate.  When doing raw deflate, this\n   function must be called either before any call of deflate, or immediately\n   after the completion of a deflate block, i.e. after all input has been\n   consumed and all output has been delivered when using any of the flush\n   options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH.  The\n   compressor and decompressor must use exactly the same dictionary (see\n   inflateSetDictionary).\n\n     The dictionary should consist of strings (byte sequences) that are likely\n   to be encountered later in the data to be compressed, with the most commonly\n   used strings preferably put towards the end of the dictionary.  Using a\n   dictionary is most useful when the data to be compressed is short and can be\n   predicted with good accuracy; the data can then be compressed better than\n   with the default empty dictionary.\n\n     Depending on the size of the compression data structures selected by\n   deflateInit or deflateInit2, a part of the dictionary may in effect be\n   discarded, for example if the dictionary is larger than the window size\n   provided in deflateInit or deflateInit2.  Thus the strings most likely to be\n   useful should be put at the end of the dictionary, not at the front.  In\n   addition, the current implementation of deflate will use at most the window\n   size minus 262 bytes of the provided dictionary.\n\n     Upon return of this function, strm->adler is set to the Adler-32 value\n   of the dictionary; the decompressor may later use this value to determine\n   which dictionary has been used by the compressor.  (The Adler-32 value\n   applies to the whole dictionary even if only a subset of the dictionary is\n   actually used by the compressor.) If a raw deflate was requested, then the\n   Adler-32 value is not computed and strm->adler is not set.\n\n     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a\n   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is\n   inconsistent (for example if deflate has already been called for this stream\n   or if not at a block boundary for raw deflate).  deflateSetDictionary does\n   not perform any compression: this will be done by deflate().\n*/\n\nZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm,\n                                             Bytef *dictionary,\n                                             uInt  *dictLength));\n/*\n     Returns the sliding dictionary being maintained by deflate.  dictLength is\n   set to the number of bytes in the dictionary, and that many bytes are copied\n   to dictionary.  dictionary must have enough space, where 32768 bytes is\n   always enough.  If deflateGetDictionary() is called with dictionary equal to\n   Z_NULL, then only the dictionary length is returned, and nothing is copied.\n   Similary, if dictLength is Z_NULL, then it is not set.\n\n     deflateGetDictionary() may return a length less than the window size, even\n   when more than the window size in input has been provided. It may return up\n   to 258 bytes less in that case, due to how zlib's implementation of deflate\n   manages the sliding window and lookahead for matches, where matches can be\n   up to 258 bytes long. If the application needs the last window-size bytes of\n   input, then that would need to be saved by the application outside of zlib.\n\n     deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the\n   stream state is inconsistent.\n*/\n\nZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,\n                                    z_streamp source));\n/*\n     Sets the destination stream as a complete copy of the source stream.\n\n     This function can be useful when several compression strategies will be\n   tried, for example when there are several ways of pre-processing the input\n   data with a filter.  The streams that will be discarded should then be freed\n   by calling deflateEnd.  Note that deflateCopy duplicates the internal\n   compression state which can be quite large, so this strategy is slow and can\n   consume lots of memory.\n\n     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not\n   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent\n   (such as zalloc being Z_NULL).  msg is left unchanged in both source and\n   destination.\n*/\n\nZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));\n/*\n     This function is equivalent to deflateEnd followed by deflateInit, but\n   does not free and reallocate the internal compression state.  The stream\n   will leave the compression level and any other attributes that may have been\n   set unchanged.\n\n     deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent (such as zalloc or state being Z_NULL).\n*/\n\nZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,\n                                      int level,\n                                      int strategy));\n/*\n     Dynamically update the compression level and compression strategy.  The\n   interpretation of level and strategy is as in deflateInit2().  This can be\n   used to switch between compression and straight copy of the input data, or\n   to switch to a different kind of input data requiring a different strategy.\n   If the compression approach (which is a function of the level) or the\n   strategy is changed, and if any input has been consumed in a previous\n   deflate() call, then the input available so far is compressed with the old\n   level and strategy using deflate(strm, Z_BLOCK).  There are three approaches\n   for the compression levels 0, 1..3, and 4..9 respectively.  The new level\n   and strategy will take effect at the next call of deflate().\n\n     If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does\n   not have enough output space to complete, then the parameter change will not\n   take effect.  In this case, deflateParams() can be called again with the\n   same parameters and more output space to try again.\n\n     In order to assure a change in the parameters on the first try, the\n   deflate stream should be flushed using deflate() with Z_BLOCK or other flush\n   request until strm.avail_out is not zero, before calling deflateParams().\n   Then no more input data should be provided before the deflateParams() call.\n   If this is done, the old level and strategy will be applied to the data\n   compressed before deflateParams(), and the new level and strategy will be\n   applied to the the data compressed after deflateParams().\n\n     deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream\n   state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if\n   there was not enough output space to complete the compression of the\n   available input data before a change in the strategy or approach.  Note that\n   in the case of a Z_BUF_ERROR, the parameters are not changed.  A return\n   value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be\n   retried with more output space.\n*/\n\nZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,\n                                    int good_length,\n                                    int max_lazy,\n                                    int nice_length,\n                                    int max_chain));\n/*\n     Fine tune deflate's internal compression parameters.  This should only be\n   used by someone who understands the algorithm used by zlib's deflate for\n   searching for the best matching string, and even then only by the most\n   fanatic optimizer trying to squeeze out the last compressed bit for their\n   specific input data.  Read the deflate.c source code for the meaning of the\n   max_lazy, good_length, nice_length, and max_chain parameters.\n\n     deflateTune() can be called after deflateInit() or deflateInit2(), and\n   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.\n */\n\nZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,\n                                       uLong sourceLen));\n/*\n     deflateBound() returns an upper bound on the compressed size after\n   deflation of sourceLen bytes.  It must be called after deflateInit() or\n   deflateInit2(), and after deflateSetHeader(), if used.  This would be used\n   to allocate an output buffer for deflation in a single pass, and so would be\n   called before deflate().  If that first deflate() call is provided the\n   sourceLen input bytes, an output buffer allocated to the size returned by\n   deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed\n   to return Z_STREAM_END.  Note that it is possible for the compressed size to\n   be larger than the value returned by deflateBound() if flush options other\n   than Z_FINISH or Z_NO_FLUSH are used.\n*/\n\nZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,\n                                       unsigned *pending,\n                                       int *bits));\n/*\n     deflatePending() returns the number of bytes and bits of output that have\n   been generated, but not yet provided in the available output.  The bytes not\n   provided would be due to the available output space having being consumed.\n   The number of bits of output not provided are between 0 and 7, where they\n   await more bits to join them in order to fill out a full byte.  If pending\n   or bits are Z_NULL, then those values are not set.\n\n     deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent.\n */\n\nZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,\n                                     int bits,\n                                     int value));\n/*\n     deflatePrime() inserts bits in the deflate output stream.  The intent\n   is that this function is used to start off the deflate output with the bits\n   leftover from a previous deflate stream when appending to it.  As such, this\n   function can only be used for raw deflate, and must be used before the first\n   deflate() call after a deflateInit2() or deflateReset().  bits must be less\n   than or equal to 16, and that many of the least significant bits of value\n   will be inserted in the output.\n\n     deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough\n   room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the\n   source stream state was inconsistent.\n*/\n\nZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,\n                                         gz_headerp head));\n/*\n     deflateSetHeader() provides gzip header information for when a gzip\n   stream is requested by deflateInit2().  deflateSetHeader() may be called\n   after deflateInit2() or deflateReset() and before the first call of\n   deflate().  The text, time, os, extra field, name, and comment information\n   in the provided gz_header structure are written to the gzip header (xflag is\n   ignored -- the extra flags are set according to the compression level).  The\n   caller must assure that, if not Z_NULL, name and comment are terminated with\n   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are\n   available there.  If hcrc is true, a gzip header crc is included.  Note that\n   the current versions of the command-line version of gzip (up through version\n   1.3.x) do not support header crc's, and will report that it is a \"multi-part\n   gzip file\" and give up.\n\n     If deflateSetHeader is not used, the default gzip header has text false,\n   the time set to zero, and os set to 255, with no extra, name, or comment\n   fields.  The gzip header is returned to the default state by deflateReset().\n\n     deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent.\n*/\n\n/*\nZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,\n                                     int  windowBits));\n\n     This is another version of inflateInit with an extra parameter.  The\n   fields next_in, avail_in, zalloc, zfree and opaque must be initialized\n   before by the caller.\n\n     The windowBits parameter is the base two logarithm of the maximum window\n   size (the size of the history buffer).  It should be in the range 8..15 for\n   this version of the library.  The default value is 15 if inflateInit is used\n   instead.  windowBits must be greater than or equal to the windowBits value\n   provided to deflateInit2() while compressing, or it must be equal to 15 if\n   deflateInit2() was not used.  If a compressed stream with a larger window\n   size is given as input, inflate() will return with the error code\n   Z_DATA_ERROR instead of trying to allocate a larger window.\n\n     windowBits can also be zero to request that inflate use the window size in\n   the zlib header of the compressed stream.\n\n     windowBits can also be -8..-15 for raw inflate.  In this case, -windowBits\n   determines the window size.  inflate() will then process raw deflate data,\n   not looking for a zlib or gzip header, not generating a check value, and not\n   looking for any check values for comparison at the end of the stream.  This\n   is for use with other formats that use the deflate compressed data format\n   such as zip.  Those formats provide their own check values.  If a custom\n   format is developed using the raw deflate format for compressed data, it is\n   recommended that a check value such as an Adler-32 or a CRC-32 be applied to\n   the uncompressed data as is done in the zlib, gzip, and zip formats.  For\n   most applications, the zlib format should be used as is.  Note that comments\n   above on the use in deflateInit2() applies to the magnitude of windowBits.\n\n     windowBits can also be greater than 15 for optional gzip decoding.  Add\n   32 to windowBits to enable zlib and gzip decoding with automatic header\n   detection, or add 16 to decode only the gzip format (the zlib format will\n   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a\n   CRC-32 instead of an Adler-32.  Unlike the gunzip utility and gzread() (see\n   below), inflate() will not automatically decode concatenated gzip streams.\n   inflate() will return Z_STREAM_END at the end of the gzip stream.  The state\n   would need to be reset to continue decoding a subsequent gzip stream.\n\n     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the\n   version assumed by the caller, or Z_STREAM_ERROR if the parameters are\n   invalid, such as a null pointer to the structure.  msg is set to null if\n   there is no error message.  inflateInit2 does not perform any decompression\n   apart from possibly reading the zlib header if present: actual decompression\n   will be done by inflate().  (So next_in and avail_in may be modified, but\n   next_out and avail_out are unused and unchanged.) The current implementation\n   of inflateInit2() does not process any header information -- that is\n   deferred until inflate() is called.\n*/\n\nZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,\n                                             const Bytef *dictionary,\n                                             uInt  dictLength));\n/*\n     Initializes the decompression dictionary from the given uncompressed byte\n   sequence.  This function must be called immediately after a call of inflate,\n   if that call returned Z_NEED_DICT.  The dictionary chosen by the compressor\n   can be determined from the Adler-32 value returned by that call of inflate.\n   The compressor and decompressor must use exactly the same dictionary (see\n   deflateSetDictionary).  For raw inflate, this function can be called at any\n   time to set the dictionary.  If the provided dictionary is smaller than the\n   window and there is already data in the window, then the provided dictionary\n   will amend what's there.  The application must insure that the dictionary\n   that was used for compression is provided.\n\n     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a\n   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is\n   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the\n   expected one (incorrect Adler-32 value).  inflateSetDictionary does not\n   perform any decompression: this will be done by subsequent calls of\n   inflate().\n*/\n\nZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,\n                                             Bytef *dictionary,\n                                             uInt  *dictLength));\n/*\n     Returns the sliding dictionary being maintained by inflate.  dictLength is\n   set to the number of bytes in the dictionary, and that many bytes are copied\n   to dictionary.  dictionary must have enough space, where 32768 bytes is\n   always enough.  If inflateGetDictionary() is called with dictionary equal to\n   Z_NULL, then only the dictionary length is returned, and nothing is copied.\n   Similary, if dictLength is Z_NULL, then it is not set.\n\n     inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the\n   stream state is inconsistent.\n*/\n\nZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));\n/*\n     Skips invalid compressed data until a possible full flush point (see above\n   for the description of deflate with Z_FULL_FLUSH) can be found, or until all\n   available input is skipped.  No output is provided.\n\n     inflateSync searches for a 00 00 FF FF pattern in the compressed data.\n   All full flush points have this pattern, but not all occurrences of this\n   pattern are full flush points.\n\n     inflateSync returns Z_OK if a possible full flush point has been found,\n   Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point\n   has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.\n   In the success case, the application may save the current current value of\n   total_in which indicates where valid compressed data was found.  In the\n   error case, the application may repeatedly call inflateSync, providing more\n   input each time, until success or end of the input data.\n*/\n\nZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,\n                                    z_streamp source));\n/*\n     Sets the destination stream as a complete copy of the source stream.\n\n     This function can be useful when randomly accessing a large stream.  The\n   first pass through the stream can periodically record the inflate state,\n   allowing restarting inflate at those points when randomly accessing the\n   stream.\n\n     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not\n   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent\n   (such as zalloc being Z_NULL).  msg is left unchanged in both source and\n   destination.\n*/\n\nZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));\n/*\n     This function is equivalent to inflateEnd followed by inflateInit,\n   but does not free and reallocate the internal decompression state.  The\n   stream will keep attributes that may have been set by inflateInit2.\n\n     inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent (such as zalloc or state being Z_NULL).\n*/\n\nZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,\n                                      int windowBits));\n/*\n     This function is the same as inflateReset, but it also permits changing\n   the wrap and window size requests.  The windowBits parameter is interpreted\n   the same as it is for inflateInit2.  If the window size is changed, then the\n   memory allocated for the window is freed, and the window will be reallocated\n   by inflate() if needed.\n\n     inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent (such as zalloc or state being Z_NULL), or if\n   the windowBits parameter is invalid.\n*/\n\nZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,\n                                     int bits,\n                                     int value));\n/*\n     This function inserts bits in the inflate input stream.  The intent is\n   that this function is used to start inflating at a bit position in the\n   middle of a byte.  The provided bits will be used before any bytes are used\n   from next_in.  This function should only be used with raw inflate, and\n   should be used before the first inflate() call after inflateInit2() or\n   inflateReset().  bits must be less than or equal to 16, and that many of the\n   least significant bits of value will be inserted in the input.\n\n     If bits is negative, then the input stream bit buffer is emptied.  Then\n   inflatePrime() can be called again to put bits in the buffer.  This is used\n   to clear out bits leftover after feeding inflate a block description prior\n   to feeding inflate codes.\n\n     inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent.\n*/\n\nZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));\n/*\n     This function returns two values, one in the lower 16 bits of the return\n   value, and the other in the remaining upper bits, obtained by shifting the\n   return value down 16 bits.  If the upper value is -1 and the lower value is\n   zero, then inflate() is currently decoding information outside of a block.\n   If the upper value is -1 and the lower value is non-zero, then inflate is in\n   the middle of a stored block, with the lower value equaling the number of\n   bytes from the input remaining to copy.  If the upper value is not -1, then\n   it is the number of bits back from the current bit position in the input of\n   the code (literal or length/distance pair) currently being processed.  In\n   that case the lower value is the number of bytes already emitted for that\n   code.\n\n     A code is being processed if inflate is waiting for more input to complete\n   decoding of the code, or if it has completed decoding but is waiting for\n   more output space to write the literal or match data.\n\n     inflateMark() is used to mark locations in the input data for random\n   access, which may be at bit positions, and to note those cases where the\n   output of a code may span boundaries of random access blocks.  The current\n   location in the input stream can be determined from avail_in and data_type\n   as noted in the description for the Z_BLOCK flush parameter for inflate.\n\n     inflateMark returns the value noted above, or -65536 if the provided\n   source stream state was inconsistent.\n*/\n\nZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,\n                                         gz_headerp head));\n/*\n     inflateGetHeader() requests that gzip header information be stored in the\n   provided gz_header structure.  inflateGetHeader() may be called after\n   inflateInit2() or inflateReset(), and before the first call of inflate().\n   As inflate() processes the gzip stream, head->done is zero until the header\n   is completed, at which time head->done is set to one.  If a zlib stream is\n   being decoded, then head->done is set to -1 to indicate that there will be\n   no gzip header information forthcoming.  Note that Z_BLOCK or Z_TREES can be\n   used to force inflate() to return immediately after header processing is\n   complete and before any actual data is decompressed.\n\n     The text, time, xflags, and os fields are filled in with the gzip header\n   contents.  hcrc is set to true if there is a header CRC.  (The header CRC\n   was valid if done is set to one.) If extra is not Z_NULL, then extra_max\n   contains the maximum number of bytes to write to extra.  Once done is true,\n   extra_len contains the actual extra field length, and extra contains the\n   extra field, or that field truncated if extra_max is less than extra_len.\n   If name is not Z_NULL, then up to name_max characters are written there,\n   terminated with a zero unless the length is greater than name_max.  If\n   comment is not Z_NULL, then up to comm_max characters are written there,\n   terminated with a zero unless the length is greater than comm_max.  When any\n   of extra, name, or comment are not Z_NULL and the respective field is not\n   present in the header, then that field is set to Z_NULL to signal its\n   absence.  This allows the use of deflateSetHeader() with the returned\n   structure to duplicate the header.  However if those fields are set to\n   allocated memory, then the application will need to save those pointers\n   elsewhere so that they can be eventually freed.\n\n     If inflateGetHeader is not used, then the header information is simply\n   discarded.  The header is always checked for validity, including the header\n   CRC if present.  inflateReset() will reset the process to discard the header\n   information.  The application would need to call inflateGetHeader() again to\n   retrieve the header from the next gzip stream.\n\n     inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source\n   stream state was inconsistent.\n*/\n\n/*\nZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,\n                                        unsigned char FAR *window));\n\n     Initialize the internal stream state for decompression using inflateBack()\n   calls.  The fields zalloc, zfree and opaque in strm must be initialized\n   before the call.  If zalloc and zfree are Z_NULL, then the default library-\n   derived memory allocation routines are used.  windowBits is the base two\n   logarithm of the window size, in the range 8..15.  window is a caller\n   supplied buffer of that size.  Except for special applications where it is\n   assured that deflate was used with small window sizes, windowBits must be 15\n   and a 32K byte window must be supplied to be able to decompress general\n   deflate streams.\n\n     See inflateBack() for the usage of these routines.\n\n     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of\n   the parameters are invalid, Z_MEM_ERROR if the internal state could not be\n   allocated, or Z_VERSION_ERROR if the version of the library does not match\n   the version of the header file.\n*/\n\ntypedef unsigned (*in_func) OF((void FAR *,\n                                z_const unsigned char FAR * FAR *));\ntypedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));\n\nZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,\n                                    in_func in, void FAR *in_desc,\n                                    out_func out, void FAR *out_desc));\n/*\n     inflateBack() does a raw inflate with a single call using a call-back\n   interface for input and output.  This is potentially more efficient than\n   inflate() for file i/o applications, in that it avoids copying between the\n   output and the sliding window by simply making the window itself the output\n   buffer.  inflate() can be faster on modern CPUs when used with large\n   buffers.  inflateBack() trusts the application to not change the output\n   buffer passed by the output function, at least until inflateBack() returns.\n\n     inflateBackInit() must be called first to allocate the internal state\n   and to initialize the state with the user-provided window buffer.\n   inflateBack() may then be used multiple times to inflate a complete, raw\n   deflate stream with each call.  inflateBackEnd() is then called to free the\n   allocated state.\n\n     A raw deflate stream is one with no zlib or gzip header or trailer.\n   This routine would normally be used in a utility that reads zip or gzip\n   files and writes out uncompressed files.  The utility would decode the\n   header and process the trailer on its own, hence this routine expects only\n   the raw deflate stream to decompress.  This is different from the default\n   behavior of inflate(), which expects a zlib header and trailer around the\n   deflate stream.\n\n     inflateBack() uses two subroutines supplied by the caller that are then\n   called by inflateBack() for input and output.  inflateBack() calls those\n   routines until it reads a complete deflate stream and writes out all of the\n   uncompressed data, or until it encounters an error.  The function's\n   parameters and return types are defined above in the in_func and out_func\n   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the\n   number of bytes of provided input, and a pointer to that input in buf.  If\n   there is no input available, in() must return zero -- buf is ignored in that\n   case -- and inflateBack() will return a buffer error.  inflateBack() will\n   call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].\n   out() should return zero on success, or non-zero on failure.  If out()\n   returns non-zero, inflateBack() will return with an error.  Neither in() nor\n   out() are permitted to change the contents of the window provided to\n   inflateBackInit(), which is also the buffer that out() uses to write from.\n   The length written by out() will be at most the window size.  Any non-zero\n   amount of input may be provided by in().\n\n     For convenience, inflateBack() can be provided input on the first call by\n   setting strm->next_in and strm->avail_in.  If that input is exhausted, then\n   in() will be called.  Therefore strm->next_in must be initialized before\n   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called\n   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in\n   must also be initialized, and then if strm->avail_in is not zero, input will\n   initially be taken from strm->next_in[0 ..  strm->avail_in - 1].\n\n     The in_desc and out_desc parameters of inflateBack() is passed as the\n   first parameter of in() and out() respectively when they are called.  These\n   descriptors can be optionally used to pass any information that the caller-\n   supplied in() and out() functions need to do their job.\n\n     On return, inflateBack() will set strm->next_in and strm->avail_in to\n   pass back any unused input that was provided by the last in() call.  The\n   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR\n   if in() or out() returned an error, Z_DATA_ERROR if there was a format error\n   in the deflate stream (in which case strm->msg is set to indicate the nature\n   of the error), or Z_STREAM_ERROR if the stream was not properly initialized.\n   In the case of Z_BUF_ERROR, an input or output error can be distinguished\n   using strm->next_in which will be Z_NULL only if in() returned an error.  If\n   strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning\n   non-zero.  (in() will always be called before out(), so strm->next_in is\n   assured to be defined if out() returns non-zero.)  Note that inflateBack()\n   cannot return Z_OK.\n*/\n\nZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));\n/*\n     All memory allocated by inflateBackInit() is freed.\n\n     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream\n   state was inconsistent.\n*/\n\nZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));\n/* Return flags indicating compile-time options.\n\n    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:\n     1.0: size of uInt\n     3.2: size of uLong\n     5.4: size of voidpf (pointer)\n     7.6: size of z_off_t\n\n    Compiler, assembler, and debug options:\n     8: ZLIB_DEBUG\n     9: ASMV or ASMINF -- use ASM code\n     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention\n     11: 0 (reserved)\n\n    One-time table building (smaller code, but not thread-safe if true):\n     12: BUILDFIXED -- build static block decoding tables when needed\n     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed\n     14,15: 0 (reserved)\n\n    Library content (indicates missing functionality):\n     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking\n                          deflate code when not needed)\n     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect\n                    and decode gzip streams (to avoid linking crc code)\n     18-19: 0 (reserved)\n\n    Operation variations (changes in library functionality):\n     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate\n     21: FASTEST -- deflate algorithm with only one, lowest compression level\n     22,23: 0 (reserved)\n\n    The sprintf variant used by gzprintf (zero is best):\n     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format\n     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!\n     26: 0 = returns value, 1 = void -- 1 means inferred string length returned\n\n    Remainder:\n     27-31: 0 (reserved)\n */\n\n#ifndef Z_SOLO\n\n                        /* utility functions */\n\n/*\n     The following utility functions are implemented on top of the basic\n   stream-oriented functions.  To simplify the interface, some default options\n   are assumed (compression level and memory usage, standard memory allocation\n   functions).  The source code of these utility functions can be modified if\n   you need special options.\n*/\n\nZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,\n                                 const Bytef *source, uLong sourceLen));\n/*\n     Compresses the source buffer into the destination buffer.  sourceLen is\n   the byte length of the source buffer.  Upon entry, destLen is the total size\n   of the destination buffer, which must be at least the value returned by\n   compressBound(sourceLen).  Upon exit, destLen is the actual size of the\n   compressed data.  compress() is equivalent to compress2() with a level\n   parameter of Z_DEFAULT_COMPRESSION.\n\n     compress returns Z_OK if success, Z_MEM_ERROR if there was not\n   enough memory, Z_BUF_ERROR if there was not enough room in the output\n   buffer.\n*/\n\nZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,\n                                  const Bytef *source, uLong sourceLen,\n                                  int level));\n/*\n     Compresses the source buffer into the destination buffer.  The level\n   parameter has the same meaning as in deflateInit.  sourceLen is the byte\n   length of the source buffer.  Upon entry, destLen is the total size of the\n   destination buffer, which must be at least the value returned by\n   compressBound(sourceLen).  Upon exit, destLen is the actual size of the\n   compressed data.\n\n     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough\n   memory, Z_BUF_ERROR if there was not enough room in the output buffer,\n   Z_STREAM_ERROR if the level parameter is invalid.\n*/\n\nZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));\n/*\n     compressBound() returns an upper bound on the compressed size after\n   compress() or compress2() on sourceLen bytes.  It would be used before a\n   compress() or compress2() call to allocate the destination buffer.\n*/\n\nZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,\n                                   const Bytef *source, uLong sourceLen));\n/*\n     Decompresses the source buffer into the destination buffer.  sourceLen is\n   the byte length of the source buffer.  Upon entry, destLen is the total size\n   of the destination buffer, which must be large enough to hold the entire\n   uncompressed data.  (The size of the uncompressed data must have been saved\n   previously by the compressor and transmitted to the decompressor by some\n   mechanism outside the scope of this compression library.) Upon exit, destLen\n   is the actual size of the uncompressed data.\n\n     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not\n   enough memory, Z_BUF_ERROR if there was not enough room in the output\n   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In\n   the case where there is not enough room, uncompress() will fill the output\n   buffer with the uncompressed data up to that point.\n*/\n\nZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest,   uLongf *destLen,\n                                    const Bytef *source, uLong *sourceLen));\n/*\n     Same as uncompress, except that sourceLen is a pointer, where the\n   length of the source is *sourceLen.  On return, *sourceLen is the number of\n   source bytes consumed.\n*/\n\n                        /* gzip file access functions */\n\n/*\n     This library supports reading and writing files in gzip (.gz) format with\n   an interface similar to that of stdio, using the functions that start with\n   \"gz\".  The gzip format is different from the zlib format.  gzip is a gzip\n   wrapper, documented in RFC 1952, wrapped around a deflate stream.\n*/\n\ntypedef struct gzFile_s *gzFile;    /* semi-opaque gzip file descriptor */\n\n/*\nZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));\n\n     Opens a gzip (.gz) file for reading or writing.  The mode parameter is as\n   in fopen (\"rb\" or \"wb\") but can also include a compression level (\"wb9\") or\n   a strategy: 'f' for filtered data as in \"wb6f\", 'h' for Huffman-only\n   compression as in \"wb1h\", 'R' for run-length encoding as in \"wb1R\", or 'F'\n   for fixed code compression as in \"wb9F\".  (See the description of\n   deflateInit2 for more information about the strategy parameter.)  'T' will\n   request transparent writing or appending with no compression and not using\n   the gzip format.\n\n     \"a\" can be used instead of \"w\" to request that the gzip stream that will\n   be written be appended to the file.  \"+\" will result in an error, since\n   reading and writing to the same gzip file is not supported.  The addition of\n   \"x\" when writing will create the file exclusively, which fails if the file\n   already exists.  On systems that support it, the addition of \"e\" when\n   reading or writing will set the flag to close the file on an execve() call.\n\n     These functions, as well as gzip, will read and decode a sequence of gzip\n   streams in a file.  The append function of gzopen() can be used to create\n   such a file.  (Also see gzflush() for another way to do this.)  When\n   appending, gzopen does not test whether the file begins with a gzip stream,\n   nor does it look for the end of the gzip streams to begin appending.  gzopen\n   will simply append a gzip stream to the existing file.\n\n     gzopen can be used to read a file which is not in gzip format; in this\n   case gzread will directly read from the file without decompression.  When\n   reading, this will be detected automatically by looking for the magic two-\n   byte gzip header.\n\n     gzopen returns NULL if the file could not be opened, if there was\n   insufficient memory to allocate the gzFile state, or if an invalid mode was\n   specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).\n   errno can be checked to determine if the reason gzopen failed was that the\n   file could not be opened.\n*/\n\nZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));\n/*\n     gzdopen associates a gzFile with the file descriptor fd.  File descriptors\n   are obtained from calls like open, dup, creat, pipe or fileno (if the file\n   has been previously opened with fopen).  The mode parameter is as in gzopen.\n\n     The next call of gzclose on the returned gzFile will also close the file\n   descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor\n   fd.  If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,\n   mode);.  The duplicated descriptor should be saved to avoid a leak, since\n   gzdopen does not close fd if it fails.  If you are using fileno() to get the\n   file descriptor from a FILE *, then you will have to use dup() to avoid\n   double-close()ing the file descriptor.  Both gzclose() and fclose() will\n   close the associated file descriptor, so they need to have different file\n   descriptors.\n\n     gzdopen returns NULL if there was insufficient memory to allocate the\n   gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not\n   provided, or '+' was provided), or if fd is -1.  The file descriptor is not\n   used until the next gz* read, write, seek, or close operation, so gzdopen\n   will not detect if fd is invalid (unless fd is -1).\n*/\n\nZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));\n/*\n     Set the internal buffer size used by this library's functions.  The\n   default buffer size is 8192 bytes.  This function must be called after\n   gzopen() or gzdopen(), and before any other calls that read or write the\n   file.  The buffer memory allocation is always deferred to the first read or\n   write.  Three times that size in buffer space is allocated.  A larger buffer\n   size of, for example, 64K or 128K bytes will noticeably increase the speed\n   of decompression (reading).\n\n     The new buffer size also affects the maximum length for gzprintf().\n\n     gzbuffer() returns 0 on success, or -1 on failure, such as being called\n   too late.\n*/\n\nZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));\n/*\n     Dynamically update the compression level or strategy.  See the description\n   of deflateInit2 for the meaning of these parameters.  Previously provided\n   data is flushed before the parameter change.\n\n     gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not\n   opened for writing, Z_ERRNO if there is an error writing the flushed data,\n   or Z_MEM_ERROR if there is a memory allocation error.\n*/\n\nZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));\n/*\n     Reads the given number of uncompressed bytes from the compressed file.  If\n   the input file is not in gzip format, gzread copies the given number of\n   bytes into the buffer directly from the file.\n\n     After reaching the end of a gzip stream in the input, gzread will continue\n   to read, looking for another gzip stream.  Any number of gzip streams may be\n   concatenated in the input file, and will all be decompressed by gzread().\n   If something other than a gzip stream is encountered after a gzip stream,\n   that remaining trailing garbage is ignored (and no error is returned).\n\n     gzread can be used to read a gzip file that is being concurrently written.\n   Upon reaching the end of the input, gzread will return with the available\n   data.  If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then\n   gzclearerr can be used to clear the end of file indicator in order to permit\n   gzread to be tried again.  Z_OK indicates that a gzip stream was completed\n   on the last gzread.  Z_BUF_ERROR indicates that the input file ended in the\n   middle of a gzip stream.  Note that gzread does not return -1 in the event\n   of an incomplete gzip stream.  This error is deferred until gzclose(), which\n   will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip\n   stream.  Alternatively, gzerror can be used before gzclose to detect this\n   case.\n\n     gzread returns the number of uncompressed bytes actually read, less than\n   len for end of file, or -1 for error.  If len is too large to fit in an int,\n   then nothing is read, -1 is returned, and the error state is set to\n   Z_STREAM_ERROR.\n*/\n\nZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,\n                                     gzFile file));\n/*\n     Read up to nitems items of size size from file to buf, otherwise operating\n   as gzread() does.  This duplicates the interface of stdio's fread(), with\n   size_t request and return types.  If the library defines size_t, then\n   z_size_t is identical to size_t.  If not, then z_size_t is an unsigned\n   integer type that can contain a pointer.\n\n     gzfread() returns the number of full items read of size size, or zero if\n   the end of the file was reached and a full item could not be read, or if\n   there was an error.  gzerror() must be consulted if zero is returned in\n   order to determine if there was an error.  If the multiplication of size and\n   nitems overflows, i.e. the product does not fit in a z_size_t, then nothing\n   is read, zero is returned, and the error state is set to Z_STREAM_ERROR.\n\n     In the event that the end of file is reached and only a partial item is\n   available at the end, i.e. the remaining uncompressed data length is not a\n   multiple of size, then the final partial item is nevetheless read into buf\n   and the end-of-file flag is set.  The length of the partial item read is not\n   provided, but could be inferred from the result of gztell().  This behavior\n   is the same as the behavior of fread() implementations in common libraries,\n   but it prevents the direct use of gzfread() to read a concurrently written\n   file, reseting and retrying on end-of-file, when size is not 1.\n*/\n\nZEXTERN int ZEXPORT gzwrite OF((gzFile file,\n                                voidpc buf, unsigned len));\n/*\n     Writes the given number of uncompressed bytes into the compressed file.\n   gzwrite returns the number of uncompressed bytes written or 0 in case of\n   error.\n*/\n\nZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,\n                                      z_size_t nitems, gzFile file));\n/*\n     gzfwrite() writes nitems items of size size from buf to file, duplicating\n   the interface of stdio's fwrite(), with size_t request and return types.  If\n   the library defines size_t, then z_size_t is identical to size_t.  If not,\n   then z_size_t is an unsigned integer type that can contain a pointer.\n\n     gzfwrite() returns the number of full items written of size size, or zero\n   if there was an error.  If the multiplication of size and nitems overflows,\n   i.e. the product does not fit in a z_size_t, then nothing is written, zero\n   is returned, and the error state is set to Z_STREAM_ERROR.\n*/\n\nZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));\n/*\n     Converts, formats, and writes the arguments to the compressed file under\n   control of the format string, as in fprintf.  gzprintf returns the number of\n   uncompressed bytes actually written, or a negative zlib error code in case\n   of error.  The number of uncompressed bytes written is limited to 8191, or\n   one less than the buffer size given to gzbuffer().  The caller should assure\n   that this limit is not exceeded.  If it is exceeded, then gzprintf() will\n   return an error (0) with nothing written.  In this case, there may also be a\n   buffer overflow with unpredictable consequences, which is possible only if\n   zlib was compiled with the insecure functions sprintf() or vsprintf()\n   because the secure snprintf() or vsnprintf() functions were not available.\n   This can be determined using zlibCompileFlags().\n*/\n\nZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));\n/*\n     Writes the given null-terminated string to the compressed file, excluding\n   the terminating null character.\n\n     gzputs returns the number of characters written, or -1 in case of error.\n*/\n\nZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));\n/*\n     Reads bytes from the compressed file until len-1 characters are read, or a\n   newline character is read and transferred to buf, or an end-of-file\n   condition is encountered.  If any characters are read or if len == 1, the\n   string is terminated with a null character.  If no characters are read due\n   to an end-of-file or len < 1, then the buffer is left untouched.\n\n     gzgets returns buf which is a null-terminated string, or it returns NULL\n   for end-of-file or in case of error.  If there was an error, the contents at\n   buf are indeterminate.\n*/\n\nZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));\n/*\n     Writes c, converted to an unsigned char, into the compressed file.  gzputc\n   returns the value that was written, or -1 in case of error.\n*/\n\nZEXTERN int ZEXPORT gzgetc OF((gzFile file));\n/*\n     Reads one byte from the compressed file.  gzgetc returns this byte or -1\n   in case of end of file or error.  This is implemented as a macro for speed.\n   As such, it does not do all of the checking the other functions do.  I.e.\n   it does not check to see if file is NULL, nor whether the structure file\n   points to has been clobbered or not.\n*/\n\nZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));\n/*\n     Push one character back onto the stream to be read as the first character\n   on the next read.  At least one character of push-back is allowed.\n   gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will\n   fail if c is -1, and may fail if a character has been pushed but not read\n   yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the\n   output buffer size of pushed characters is allowed.  (See gzbuffer above.)\n   The pushed character will be discarded if the stream is repositioned with\n   gzseek() or gzrewind().\n*/\n\nZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));\n/*\n     Flushes all pending output into the compressed file.  The parameter flush\n   is as in the deflate() function.  The return value is the zlib error number\n   (see function gzerror below).  gzflush is only permitted when writing.\n\n     If the flush parameter is Z_FINISH, the remaining data is written and the\n   gzip stream is completed in the output.  If gzwrite() is called again, a new\n   gzip stream will be started in the output.  gzread() is able to read such\n   concatenated gzip streams.\n\n     gzflush should be called only when strictly necessary because it will\n   degrade compression if called too often.\n*/\n\n/*\nZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,\n                                   z_off_t offset, int whence));\n\n     Sets the starting position for the next gzread or gzwrite on the given\n   compressed file.  The offset represents a number of bytes in the\n   uncompressed data stream.  The whence parameter is defined as in lseek(2);\n   the value SEEK_END is not supported.\n\n     If the file is opened for reading, this function is emulated but can be\n   extremely slow.  If the file is opened for writing, only forward seeks are\n   supported; gzseek then compresses a sequence of zeroes up to the new\n   starting position.\n\n     gzseek returns the resulting offset location as measured in bytes from\n   the beginning of the uncompressed stream, or -1 in case of error, in\n   particular if the file is opened for writing and the new starting position\n   would be before the current position.\n*/\n\nZEXTERN int ZEXPORT    gzrewind OF((gzFile file));\n/*\n     Rewinds the given file. This function is supported only for reading.\n\n     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)\n*/\n\n/*\nZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));\n\n     Returns the starting position for the next gzread or gzwrite on the given\n   compressed file.  This position represents a number of bytes in the\n   uncompressed data stream, and is zero when starting, even if appending or\n   reading a gzip stream from the middle of a file using gzdopen().\n\n     gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)\n*/\n\n/*\nZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));\n\n     Returns the current offset in the file being read or written.  This offset\n   includes the count of bytes that precede the gzip stream, for example when\n   appending or when using gzdopen() for reading.  When reading, the offset\n   does not include as yet unused buffered input.  This information can be used\n   for a progress indicator.  On error, gzoffset() returns -1.\n*/\n\nZEXTERN int ZEXPORT gzeof OF((gzFile file));\n/*\n     Returns true (1) if the end-of-file indicator has been set while reading,\n   false (0) otherwise.  Note that the end-of-file indicator is set only if the\n   read tried to go past the end of the input, but came up short.  Therefore,\n   just like feof(), gzeof() may return false even if there is no more data to\n   read, in the event that the last read request was for the exact number of\n   bytes remaining in the input file.  This will happen if the input file size\n   is an exact multiple of the buffer size.\n\n     If gzeof() returns true, then the read functions will return no more data,\n   unless the end-of-file indicator is reset by gzclearerr() and the input file\n   has grown since the previous end of file was detected.\n*/\n\nZEXTERN int ZEXPORT gzdirect OF((gzFile file));\n/*\n     Returns true (1) if file is being copied directly while reading, or false\n   (0) if file is a gzip stream being decompressed.\n\n     If the input file is empty, gzdirect() will return true, since the input\n   does not contain a gzip stream.\n\n     If gzdirect() is used immediately after gzopen() or gzdopen() it will\n   cause buffers to be allocated to allow reading the file to determine if it\n   is a gzip file.  Therefore if gzbuffer() is used, it should be called before\n   gzdirect().\n\n     When writing, gzdirect() returns true (1) if transparent writing was\n   requested (\"wT\" for the gzopen() mode), or false (0) otherwise.  (Note:\n   gzdirect() is not needed when writing.  Transparent writing must be\n   explicitly requested, so the application already knows the answer.  When\n   linking statically, using gzdirect() will include all of the zlib code for\n   gzip file reading and decompression, which may not be desired.)\n*/\n\nZEXTERN int ZEXPORT    gzclose OF((gzFile file));\n/*\n     Flushes all pending output if necessary, closes the compressed file and\n   deallocates the (de)compression state.  Note that once file is closed, you\n   cannot call gzerror with file, since its structures have been deallocated.\n   gzclose must not be called more than once on the same file, just as free\n   must not be called more than once on the same allocation.\n\n     gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a\n   file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the\n   last read ended in the middle of a gzip stream, or Z_OK on success.\n*/\n\nZEXTERN int ZEXPORT gzclose_r OF((gzFile file));\nZEXTERN int ZEXPORT gzclose_w OF((gzFile file));\n/*\n     Same as gzclose(), but gzclose_r() is only for use when reading, and\n   gzclose_w() is only for use when writing or appending.  The advantage to\n   using these instead of gzclose() is that they avoid linking in zlib\n   compression or decompression code that is not used when only reading or only\n   writing respectively.  If gzclose() is used, then both compression and\n   decompression code will be included the application when linking to a static\n   zlib library.\n*/\n\nZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));\n/*\n     Returns the error message for the last error which occurred on the given\n   compressed file.  errnum is set to zlib error number.  If an error occurred\n   in the file system and not in the compression library, errnum is set to\n   Z_ERRNO and the application may consult errno to get the exact error code.\n\n     The application must not modify the returned string.  Future calls to\n   this function may invalidate the previously returned string.  If file is\n   closed, then the string previously returned by gzerror will no longer be\n   available.\n\n     gzerror() should be used to distinguish errors from end-of-file for those\n   functions above that do not distinguish those cases in their return values.\n*/\n\nZEXTERN void ZEXPORT gzclearerr OF((gzFile file));\n/*\n     Clears the error and end-of-file flags for file.  This is analogous to the\n   clearerr() function in stdio.  This is useful for continuing to read a gzip\n   file that is being written concurrently.\n*/\n\n#endif /* !Z_SOLO */\n\n                        /* checksum functions */\n\n/*\n     These functions are not related to compression but are exported\n   anyway because they might be useful in applications using the compression\n   library.\n*/\n\nZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));\n/*\n     Update a running Adler-32 checksum with the bytes buf[0..len-1] and\n   return the updated checksum.  If buf is Z_NULL, this function returns the\n   required initial value for the checksum.\n\n     An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed\n   much faster.\n\n   Usage example:\n\n     uLong adler = adler32(0L, Z_NULL, 0);\n\n     while (read_buffer(buffer, length) != EOF) {\n       adler = adler32(adler, buffer, length);\n     }\n     if (adler != original_adler) error();\n*/\n\nZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf,\n                                    z_size_t len));\n/*\n     Same as adler32(), but with a size_t length.\n*/\n\n/*\nZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,\n                                          z_off_t len2));\n\n     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1\n   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for\n   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of\n   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.  Note\n   that the z_off_t type (like off_t) is a signed integer.  If len2 is\n   negative, the result has no meaning or utility.\n*/\n\nZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));\n/*\n     Update a running CRC-32 with the bytes buf[0..len-1] and return the\n   updated CRC-32.  If buf is Z_NULL, this function returns the required\n   initial value for the crc.  Pre- and post-conditioning (one's complement) is\n   performed within this function so it shouldn't be done by the application.\n\n   Usage example:\n\n     uLong crc = crc32(0L, Z_NULL, 0);\n\n     while (read_buffer(buffer, length) != EOF) {\n       crc = crc32(crc, buffer, length);\n     }\n     if (crc != original_crc) error();\n*/\n\nZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf,\n                                  z_size_t len));\n/*\n     Same as crc32(), but with a size_t length.\n*/\n\n/*\nZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));\n\n     Combine two CRC-32 check values into one.  For two sequences of bytes,\n   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were\n   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32\n   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and\n   len2.\n*/\n\n\n                        /* various hacks, don't look :) */\n\n/* deflateInit and inflateInit are macros to allow checking the zlib version\n * and the compiler's view of z_stream:\n */\nZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,\n                                     const char *version, int stream_size));\nZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,\n                                     const char *version, int stream_size));\nZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,\n                                      int windowBits, int memLevel,\n                                      int strategy, const char *version,\n                                      int stream_size));\nZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,\n                                      const char *version, int stream_size));\nZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,\n                                         unsigned char FAR *window,\n                                         const char *version,\n                                         int stream_size));\n#ifdef Z_PREFIX_SET\n#  define z_deflateInit(strm, level) \\\n          deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))\n#  define z_inflateInit(strm) \\\n          inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))\n#  define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \\\n          deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\\\n                        (strategy), ZLIB_VERSION, (int)sizeof(z_stream))\n#  define z_inflateInit2(strm, windowBits) \\\n          inflateInit2_((strm), (windowBits), ZLIB_VERSION, \\\n                        (int)sizeof(z_stream))\n#  define z_inflateBackInit(strm, windowBits, window) \\\n          inflateBackInit_((strm), (windowBits), (window), \\\n                           ZLIB_VERSION, (int)sizeof(z_stream))\n#else\n#  define deflateInit(strm, level) \\\n          deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))\n#  define inflateInit(strm) \\\n          inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))\n#  define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \\\n          deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\\\n                        (strategy), ZLIB_VERSION, (int)sizeof(z_stream))\n#  define inflateInit2(strm, windowBits) \\\n          inflateInit2_((strm), (windowBits), ZLIB_VERSION, \\\n                        (int)sizeof(z_stream))\n#  define inflateBackInit(strm, windowBits, window) \\\n          inflateBackInit_((strm), (windowBits), (window), \\\n                           ZLIB_VERSION, (int)sizeof(z_stream))\n#endif\n\n#ifndef Z_SOLO\n\n/* gzgetc() macro and its supporting function and exposed data structure.  Note\n * that the real internal state is much larger than the exposed structure.\n * This abbreviated structure exposes just enough for the gzgetc() macro.  The\n * user should not mess with these exposed elements, since their names or\n * behavior could change in the future, perhaps even capriciously.  They can\n * only be used by the gzgetc() macro.  You have been warned.\n */\nstruct gzFile_s {\n    unsigned have;\n    unsigned char *next;\n    z_off64_t pos;\n};\nZEXTERN int ZEXPORT gzgetc_ OF((gzFile file));  /* backward compatibility */\n#ifdef Z_PREFIX_SET\n#  undef z_gzgetc\n#  define z_gzgetc(g) \\\n          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))\n#else\n#  define gzgetc(g) \\\n          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))\n#endif\n\n/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or\n * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if\n * both are true, the application gets the *64 functions, and the regular\n * functions are changed to 64 bits) -- in case these are set on systems\n * without large file support, _LFS64_LARGEFILE must also be true\n */\n#ifdef Z_LARGE64\n   ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));\n   ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));\n   ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));\n   ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));\n   ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));\n   ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));\n#endif\n\n#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)\n#  ifdef Z_PREFIX_SET\n#    define z_gzopen z_gzopen64\n#    define z_gzseek z_gzseek64\n#    define z_gztell z_gztell64\n#    define z_gzoffset z_gzoffset64\n#    define z_adler32_combine z_adler32_combine64\n#    define z_crc32_combine z_crc32_combine64\n#  else\n#    define gzopen gzopen64\n#    define gzseek gzseek64\n#    define gztell gztell64\n#    define gzoffset gzoffset64\n#    define adler32_combine adler32_combine64\n#    define crc32_combine crc32_combine64\n#  endif\n#  ifndef Z_LARGE64\n     ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));\n     ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));\n     ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));\n     ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));\n     ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));\n     ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));\n#  endif\n#else\n   ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));\n   ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));\n   ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));\n   ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));\n   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));\n   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));\n#endif\n\n#else /* Z_SOLO */\n\n   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));\n   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));\n\n#endif /* !Z_SOLO */\n\n/* undocumented functions */\nZEXTERN const char   * ZEXPORT zError           OF((int));\nZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp));\nZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table    OF((void));\nZEXTERN int            ZEXPORT inflateUndermine OF((z_streamp, int));\nZEXTERN int            ZEXPORT inflateValidate OF((z_streamp, int));\nZEXTERN unsigned long  ZEXPORT inflateCodesUsed OF ((z_streamp));\nZEXTERN int            ZEXPORT inflateResetKeep OF((z_streamp));\nZEXTERN int            ZEXPORT deflateResetKeep OF((z_streamp));\n#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO)\nZEXTERN gzFile         ZEXPORT gzopen_w OF((const wchar_t *path,\n                                            const char *mode));\n#endif\n#if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#  ifndef Z_SOLO\nZEXTERN int            ZEXPORTVA gzvprintf Z_ARG((gzFile file,\n                                                  const char *format,\n                                                  va_list va));\n#  endif\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* ZLIB_H */\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/zlibdemo.c",
    "content": "//zlibѹѹʹʾ\n//#include \"zlib.h\"\n\n//int CompressBuf()\n//{\n//    char text[] = \"zlib compress and uncompress test\\nturingo@163.com\\n2012-11-05\\n\";\n//    uLong tlen = strlen(text) + 1;  /* ҪַĽ'\\0'Ҳһ */\n//    char* buf = NULL;\n//    uLong blen;\n//\n//    /* 㻺СΪڴ */\n//    blen = compressBound(tlen); /* ѹĳǲᳬblen */\n//    if ((buf = (char*)malloc(sizeof(char) * blen)) == NULL)\n//    {\n//        printf(\"no enough memory!\\n\");\n//        return -1;\n//    }\n//\n//    /* ѹ */\n//    if (compress((Bytef*)buf, &blen, (const Bytef*)text, tlen) != Z_OK)\n//    {\n//        printf(\"compress failed!\\n\");\n//        return -1;\n//    }\n//\n//    /* ѹ */\n//    if (uncompress((Bytef*)text, &tlen, (const Bytef*)buf, blen) != Z_OK)\n//    {\n//        printf(\"uncompress failed!\\n\");\n//        return -1;\n//    }\n//\n//    /* ӡͷڴ */\n//    printf(\"%s\", text);\n//    if (buf != NULL)\n//    {\n//        free(buf);\n//        buf = NULL;\n//    }\n//\n//    return 0;\n//}"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/zutil.c",
    "content": "/* zutil.c -- target dependent utility functions for the compression library\n * Copyright (C) 1995-2017 Jean-loup Gailly\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* @(#) $Id$ */\n\n#include \"zutil.h\"\n#ifndef Z_SOLO\n#  include \"gzguts.h\"\n#endif\n\nz_const char * const z_errmsg[10] = {\n    (z_const char *)\"need dictionary\",     /* Z_NEED_DICT       2  */\n    (z_const char *)\"stream end\",          /* Z_STREAM_END      1  */\n    (z_const char *)\"\",                    /* Z_OK              0  */\n    (z_const char *)\"file error\",          /* Z_ERRNO         (-1) */\n    (z_const char *)\"stream error\",        /* Z_STREAM_ERROR  (-2) */\n    (z_const char *)\"data error\",          /* Z_DATA_ERROR    (-3) */\n    (z_const char *)\"insufficient memory\", /* Z_MEM_ERROR     (-4) */\n    (z_const char *)\"buffer error\",        /* Z_BUF_ERROR     (-5) */\n    (z_const char *)\"incompatible version\",/* Z_VERSION_ERROR (-6) */\n    (z_const char *)\"\"\n};\n\n\nconst char * ZEXPORT zlibVersion()\n{\n    return ZLIB_VERSION;\n}\n\nuLong ZEXPORT zlibCompileFlags()\n{\n    uLong flags;\n\n    flags = 0;\n    switch ((int)(sizeof(uInt))) {\n    case 2:     break;\n    case 4:     flags += 1;     break;\n    case 8:     flags += 2;     break;\n    default:    flags += 3;\n    }\n    switch ((int)(sizeof(uLong))) {\n    case 2:     break;\n    case 4:     flags += 1 << 2;        break;\n    case 8:     flags += 2 << 2;        break;\n    default:    flags += 3 << 2;\n    }\n    switch ((int)(sizeof(voidpf))) {\n    case 2:     break;\n    case 4:     flags += 1 << 4;        break;\n    case 8:     flags += 2 << 4;        break;\n    default:    flags += 3 << 4;\n    }\n    switch ((int)(sizeof(z_off_t))) {\n    case 2:     break;\n    case 4:     flags += 1 << 6;        break;\n    case 8:     flags += 2 << 6;        break;\n    default:    flags += 3 << 6;\n    }\n#ifdef ZLIB_DEBUG\n    flags += 1 << 8;\n#endif\n#if defined(ASMV) || defined(ASMINF)\n    flags += 1 << 9;\n#endif\n#ifdef ZLIB_WINAPI\n    flags += 1 << 10;\n#endif\n#ifdef BUILDFIXED\n    flags += 1 << 12;\n#endif\n#ifdef DYNAMIC_CRC_TABLE\n    flags += 1 << 13;\n#endif\n#ifdef NO_GZCOMPRESS\n    flags += 1L << 16;\n#endif\n#ifdef NO_GZIP\n    flags += 1L << 17;\n#endif\n#ifdef PKZIP_BUG_WORKAROUND\n    flags += 1L << 20;\n#endif\n#ifdef FASTEST\n    flags += 1L << 21;\n#endif\n#if defined(STDC) || defined(Z_HAVE_STDARG_H)\n#  ifdef NO_vsnprintf\n    flags += 1L << 25;\n#    ifdef HAS_vsprintf_void\n    flags += 1L << 26;\n#    endif\n#  else\n#    ifdef HAS_vsnprintf_void\n    flags += 1L << 26;\n#    endif\n#  endif\n#else\n    flags += 1L << 24;\n#  ifdef NO_snprintf\n    flags += 1L << 25;\n#    ifdef HAS_sprintf_void\n    flags += 1L << 26;\n#    endif\n#  else\n#    ifdef HAS_snprintf_void\n    flags += 1L << 26;\n#    endif\n#  endif\n#endif\n    return flags;\n}\n\n#ifdef ZLIB_DEBUG\n#include <stdlib.h>\n#  ifndef verbose\n#    define verbose 0\n#  endif\nint ZLIB_INTERNAL z_verbose = verbose;\n\nvoid ZLIB_INTERNAL z_error (m)\n    char *m;\n{\n    fprintf(stderr, \"%s\\n\", m);\n    exit(1);\n}\n#endif\n\n/* exported to allow conversion of error code to string for compress() and\n * uncompress()\n */\nconst char * ZEXPORT zError(err)\n    int err;\n{\n    return ERR_MSG(err);\n}\n\n#if defined(_WIN32_WCE)\n    /* The Microsoft C Run-Time Library for Windows CE doesn't have\n     * errno.  We define it as a global variable to simplify porting.\n     * Its value is always 0 and should not be used.\n     */\n    int errno = 0;\n#endif\n\n#ifndef HAVE_MEMCPY\n\nvoid ZLIB_INTERNAL zmemcpy(dest, source, len)\n    Bytef* dest;\n    const Bytef* source;\n    uInt  len;\n{\n    if (len == 0) return;\n    do {\n        *dest++ = *source++; /* ??? to be unrolled */\n    } while (--len != 0);\n}\n\nint ZLIB_INTERNAL zmemcmp(s1, s2, len)\n    const Bytef* s1;\n    const Bytef* s2;\n    uInt  len;\n{\n    uInt j;\n\n    for (j = 0; j < len; j++) {\n        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;\n    }\n    return 0;\n}\n\nvoid ZLIB_INTERNAL zmemzero(dest, len)\n    Bytef* dest;\n    uInt  len;\n{\n    if (len == 0) return;\n    do {\n        *dest++ = 0;  /* ??? to be unrolled */\n    } while (--len != 0);\n}\n#endif\n\n#ifndef Z_SOLO\n\n#ifdef SYS16BIT\n\n#ifdef __TURBOC__\n/* Turbo C in 16-bit mode */\n\n#  define MY_ZCALLOC\n\n/* Turbo C malloc() does not allow dynamic allocation of 64K bytes\n * and farmalloc(64K) returns a pointer with an offset of 8, so we\n * must fix the pointer. Warning: the pointer must be put back to its\n * original form in order to free it, use zcfree().\n */\n\n#define MAX_PTR 10\n/* 10*64K = 640K */\n\nlocal int next_ptr = 0;\n\ntypedef struct ptr_table_s {\n    voidpf org_ptr;\n    voidpf new_ptr;\n} ptr_table;\n\nlocal ptr_table table[MAX_PTR];\n/* This table is used to remember the original form of pointers\n * to large buffers (64K). Such pointers are normalized with a zero offset.\n * Since MSDOS is not a preemptive multitasking OS, this table is not\n * protected from concurrent access. This hack doesn't work anyway on\n * a protected system like OS/2. Use Microsoft C instead.\n */\n\nvoidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)\n{\n    voidpf buf;\n    ulg bsize = (ulg)items*size;\n\n    (void)opaque;\n\n    /* If we allocate less than 65520 bytes, we assume that farmalloc\n     * will return a usable pointer which doesn't have to be normalized.\n     */\n    if (bsize < 65520L) {\n        buf = farmalloc(bsize);\n        if (*(ush*)&buf != 0) return buf;\n    } else {\n        buf = farmalloc(bsize + 16L);\n    }\n    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;\n    table[next_ptr].org_ptr = buf;\n\n    /* Normalize the pointer to seg:0 */\n    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;\n    *(ush*)&buf = 0;\n    table[next_ptr++].new_ptr = buf;\n    return buf;\n}\n\nvoid ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)\n{\n    int n;\n\n    (void)opaque;\n\n    if (*(ush*)&ptr != 0) { /* object < 64K */\n        farfree(ptr);\n        return;\n    }\n    /* Find the original pointer */\n    for (n = 0; n < next_ptr; n++) {\n        if (ptr != table[n].new_ptr) continue;\n\n        farfree(table[n].org_ptr);\n        while (++n < next_ptr) {\n            table[n-1] = table[n];\n        }\n        next_ptr--;\n        return;\n    }\n    Assert(0, \"zcfree: ptr not found\");\n}\n\n#endif /* __TURBOC__ */\n\n\n#ifdef M_I86\n/* Microsoft C in 16-bit mode */\n\n#  define MY_ZCALLOC\n\n#if (!defined(_MSC_VER) || (_MSC_VER <= 600))\n#  define _halloc  halloc\n#  define _hfree   hfree\n#endif\n\nvoidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)\n{\n    (void)opaque;\n    return _halloc((long)items, size);\n}\n\nvoid ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)\n{\n    (void)opaque;\n    _hfree(ptr);\n}\n\n#endif /* M_I86 */\n\n#endif /* SYS16BIT */\n\n\n#ifndef MY_ZCALLOC /* Any system without a special alloc function */\n\n#ifndef STDC\nextern voidp  malloc OF((uInt size));\nextern voidp  calloc OF((uInt items, uInt size));\nextern void   free   OF((voidpf ptr));\n#endif\n\nvoidpf ZLIB_INTERNAL zcalloc (opaque, items, size)\n    voidpf opaque;\n    unsigned items;\n    unsigned size;\n{\n    (void)opaque;\n    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :\n                              (voidpf)calloc(items, size);\n}\n\nvoid ZLIB_INTERNAL zcfree (opaque, ptr)\n    voidpf opaque;\n    voidpf ptr;\n{\n    (void)opaque;\n    free(ptr);\n}\n\n#endif /* MY_ZCALLOC */\n\n#endif /* !Z_SOLO */\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/zutil.h",
    "content": "/* zutil.h -- internal interface and configuration of the compression library\n * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler\n * For conditions of distribution and use, see copyright notice in zlib.h\n */\n\n/* WARNING: this file should *not* be used by applications. It is\n   part of the implementation of the compression library and is\n   subject to change. Applications should only use zlib.h.\n */\n\n/* @(#) $Id$ */\n\n#ifndef ZUTIL_H\n#define ZUTIL_H\n\n#ifdef HAVE_HIDDEN\n#  define ZLIB_INTERNAL __attribute__((visibility (\"hidden\")))\n#else\n#  define ZLIB_INTERNAL\n#endif\n\n#include \"zlib.h\"\n\n#if defined(STDC) && !defined(Z_SOLO)\n#  if !(defined(_WIN32_WCE) && defined(_MSC_VER))\n#    include <stddef.h>\n#  endif\n#  include <string.h>\n#  include <stdlib.h>\n#endif\n\n#ifdef Z_SOLO\n   typedef long ptrdiff_t;  /* guess -- will be caught if guess is wrong */\n#endif\n\n#ifndef local\n#  define local static\n#endif\n/* since \"static\" is used to mean two completely different things in C, we\n   define \"local\" for the non-static meaning of \"static\", for readability\n   (compile with -Dlocal if your debugger can't find static symbols) */\n\ntypedef unsigned char  uch;\ntypedef uch FAR uchf;\ntypedef unsigned short ush;\ntypedef ush FAR ushf;\ntypedef unsigned long  ulg;\n\nextern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */\n/* (size given to avoid silly warnings with Visual C++) */\n\n#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]\n\n#define ERR_RETURN(strm,err) \\\n  return (strm->msg = ERR_MSG(err), (err))\n/* To be used only when the state is known to be valid */\n\n        /* common constants */\n\n#ifndef DEF_WBITS\n#  define DEF_WBITS MAX_WBITS\n#endif\n/* default windowBits for decompression. MAX_WBITS is for compression only */\n\n#if MAX_MEM_LEVEL >= 8\n#  define DEF_MEM_LEVEL 8\n#else\n#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL\n#endif\n/* default memLevel */\n\n#define STORED_BLOCK 0\n#define STATIC_TREES 1\n#define DYN_TREES    2\n/* The three kinds of block type */\n\n#define MIN_MATCH  3\n#define MAX_MATCH  258\n/* The minimum and maximum match lengths */\n\n#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */\n\n        /* target dependencies */\n\n#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))\n#  define OS_CODE  0x00\n#  ifndef Z_SOLO\n#    if defined(__TURBOC__) || defined(__BORLANDC__)\n#      if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))\n         /* Allow compilation with ANSI keywords only enabled */\n         void _Cdecl farfree( void *block );\n         void *_Cdecl farmalloc( unsigned long nbytes );\n#      else\n#        include <alloc.h>\n#      endif\n#    else /* MSC or DJGPP */\n#      include <malloc.h>\n#    endif\n#  endif\n#endif\n\n#ifdef AMIGA\n#  define OS_CODE  1\n#endif\n\n#if defined(VAXC) || defined(VMS)\n#  define OS_CODE  2\n#  define F_OPEN(name, mode) \\\n     fopen((name), (mode), \"mbc=60\", \"ctx=stm\", \"rfm=fix\", \"mrs=512\")\n#endif\n\n#ifdef __370__\n#  if __TARGET_LIB__ < 0x20000000\n#    define OS_CODE 4\n#  elif __TARGET_LIB__ < 0x40000000\n#    define OS_CODE 11\n#  else\n#    define OS_CODE 8\n#  endif\n#endif\n\n#if defined(ATARI) || defined(atarist)\n#  define OS_CODE  5\n#endif\n\n#ifdef OS2\n#  define OS_CODE  6\n#  if defined(M_I86) && !defined(Z_SOLO)\n#    include <malloc.h>\n#  endif\n#endif\n\n#if defined(MACOS) || defined(TARGET_OS_MAC)\n#  define OS_CODE  7\n#  ifndef Z_SOLO\n#    if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os\n#      include <unix.h> /* for fdopen */\n#    else\n#      ifndef fdopen\n#        define fdopen(fd,mode) NULL /* No fdopen() */\n#      endif\n#    endif\n#  endif\n#endif\n\n#ifdef __acorn\n#  define OS_CODE 13\n#endif\n\n#if defined(WIN32) && !defined(__CYGWIN__)\n#  define OS_CODE  10\n#endif\n\n#ifdef _BEOS_\n#  define OS_CODE  16\n#endif\n\n#ifdef __TOS_OS400__\n#  define OS_CODE 18\n#endif\n\n#ifdef __APPLE__\n#  define OS_CODE 19\n#endif\n\n#if defined(_BEOS_) || defined(RISCOS)\n#  define fdopen(fd,mode) NULL /* No fdopen() */\n#endif\n\n#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX\n#  if defined(_WIN32_WCE)\n#    define fdopen(fd,mode) NULL /* No fdopen() */\n#    ifndef _PTRDIFF_T_DEFINED\n       typedef int ptrdiff_t;\n#      define _PTRDIFF_T_DEFINED\n#    endif\n#  else\n#    define fdopen(fd,type)  _fdopen(fd,type)\n#  endif\n#endif\n\n#if defined(__BORLANDC__) && !defined(MSDOS)\n  #pragma warn -8004\n  #pragma warn -8008\n  #pragma warn -8066\n#endif\n\n/* provide prototypes for these when building zlib without LFS */\n#if !defined(_WIN32) && \\\n    (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)\n    ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));\n    ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));\n#endif\n\n        /* common defaults */\n\n#ifndef OS_CODE\n#  define OS_CODE  3     /* assume Unix */\n#endif\n\n#ifndef F_OPEN\n#  define F_OPEN(name, mode) fopen((name), (mode))\n#endif\n\n         /* functions */\n\n#if defined(pyr) || defined(Z_SOLO)\n#  define NO_MEMCPY\n#endif\n#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)\n /* Use our own functions for small and medium model with MSC <= 5.0.\n  * You may have to use the same strategy for Borland C (untested).\n  * The __SC__ check is for Symantec.\n  */\n#  define NO_MEMCPY\n#endif\n#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)\n#  define HAVE_MEMCPY\n#endif\n#ifdef HAVE_MEMCPY\n#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */\n#    define zmemcpy _fmemcpy\n#    define zmemcmp _fmemcmp\n#    define zmemzero(dest, len) _fmemset(dest, 0, len)\n#  else\n#    define zmemcpy memcpy\n#    define zmemcmp memcmp\n#    define zmemzero(dest, len) memset(dest, 0, len)\n#  endif\n#else\n   void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));\n   int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));\n   void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));\n#endif\n\n/* Diagnostic functions */\n#ifdef ZLIB_DEBUG\n#  include <stdio.h>\n   extern int ZLIB_INTERNAL z_verbose;\n   extern void ZLIB_INTERNAL z_error OF((char *m));\n#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}\n#  define Trace(x) {if (z_verbose>=0) fprintf x ;}\n#  define Tracev(x) {if (z_verbose>0) fprintf x ;}\n#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}\n#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}\n#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}\n#else\n#  define Assert(cond,msg)\n#  define Trace(x)\n#  define Tracev(x)\n#  define Tracevv(x)\n#  define Tracec(c,x)\n#  define Tracecv(c,x)\n#endif\n\n#ifndef Z_SOLO\n   voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,\n                                    unsigned size));\n   void ZLIB_INTERNAL zcfree  OF((voidpf opaque, voidpf ptr));\n#endif\n\n#define ZALLOC(strm, items, size) \\\n           (*((strm)->zalloc))((strm)->opaque, (items), (size))\n#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))\n#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}\n\n/* Reverse the bytes in a 32-bit value */\n#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \\\n                    (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))\n\n#endif /* ZUTIL_H */\n"
  },
  {
    "path": "Chapter06/code/WebSocketServer/zlib1.2.11/文件概览.txt",
    "content": " 9、文件概览\n    alder32.c：计算数据流的Alder-32校验和，实现alder32()。\n    crc32.h和crc32.c：计算数据流的CRC-32，实现crc32()。\n    deflate.h和deflate.c：使用默认算法压缩数据，实现deflate函数簇。\n    inflate.h和inflate.c：zlib的解压，实现inflate函数簇。\n    compress.c：实现内存缓冲区的压缩，包括compress(), compress2(), compressBound()。\n    uncompr.c：实现内存缓冲区的解压，包括uncompress()。\n    gzguts.h和gzlib.c：读写gzip文件的通用实现，包括gzopen(), gzdopen(), gzbuffer(), gzrewind(), gzseek(), gztell(), gzoffset(), gzeof(), gzerror(), gzclearerr()。\n    gzclose.c：实现gzclose()。\n    gzread.c：读取gzip文件的实现，包括gzread(), gzgetc(), gzungetc(), gzgets(), gzdirect(), gzclose_r()。\n    gzwrite.c：写gzip文件的实现，包括gzwrite(), gzputc(), gzputs(), gzprintf(), gzflush(), gzsetparams(), gzclose_w()。\n    infback.c：使用回调接口实现解压，包括inflateBackInit(), inflateBack(), inflateBackEnd()。\n    zutil.h和zutil.c：zlib库用到的工具函数。包括zlibVersion(), zlibCompileFlags(), zError()。\n    zlib.h：zlib库导出的接口描述文件，应用程序使用zlib库时需要本文件。\n    zconf.h：zlib库的编译配置文件，如果编译时需要给所有库函数加上唯一的前缀，或者需要针对不同平台作特殊编译，需要用到本文件。还包括标准 C/C++兼容性定义；编译成DLL时是否使用WINAPI/WINAPIV调用方式；类型定义Byte，uInt, uLong, voidpf等。\n    inftrees.h和inftrees.c：为有效的解码生成Huffman树。    \n    trees.h和trees.c：使用Huffman编码输出压缩的数据。\n    inffixed.h：使用固定编码压缩。\n    inffast.h和inffast.c：快速解压数据。\n\t\n\t文章来源：http://blog.csdn.net/damenhanter/article/details/30757685"
  },
  {
    "path": "Chapter06/code/curl/CurlClient.cpp",
    "content": "/**\r\n * 封装curl的HTTP库，CurlClient.cpp\r\n * zhangyl 2019.08.27\r\n */\r\n\r\n#include \"CurlClient.h\"\r\n#include <iostream>  \r\n\r\n//有数据响应时的回调函数\r\nsize_t reqReply(void* ptr, size_t size, size_t nmemb, void* stream)\r\n{\r\n    std::string* str = (std::string*)stream;\r\n    (*str).append((char*)ptr, size * nmemb);\r\n    return size * nmemb;\r\n}\r\n\r\nbool CCurlClient::m_bGlobalInit = false;\r\n\r\nCCurlClient::CCurlClient()\r\n{\r\n}\r\n\r\nCCurlClient::~CCurlClient()\r\n{\r\n}\r\n\r\nvoid CCurlClient::init()\r\n{\r\n    if (!m_bGlobalInit)\r\n    {\r\n        curl_global_init(CURL_GLOBAL_ALL);\r\n        m_bGlobalInit = true;\r\n    }\r\n}\r\n\r\nvoid CCurlClient::uninit()\r\n{\r\n    if (m_bGlobalInit)\r\n    {\r\n        curl_global_cleanup();\r\n        m_bGlobalInit = false;\r\n    }\r\n}\r\n\r\n//HTTP GET  \r\nbool CCurlClient::get(const char* url, const char* headers, std::string& response,\r\n                      bool autoRedirect/* = false*/, bool reserveHeaders/* = false*/, int64_t connTimeout/* = 1L*/, int64_t readTimeout/* = 5L*/)\r\n{\r\n    //初始化CURL对象\r\n    CURL* curl = curl_easy_init();\r\n    if (curl == nullptr)\r\n        return false;\r\n\r\n    //设置请求的URL \r\n    curl_easy_setopt(curl, CURLOPT_URL, url);\r\n    //不使用HTTPS，如果需要使用HTTPS，则将最后一个参数设置为true\r\n    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);\r\n    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);\r\n    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);\r\n    curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);\r\n    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);\r\n    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&response);\r\n    //禁用SIGALRM+sigsetjmp/siglongjmp的超时机制\r\n    //采用其他超时机制，因为该机制修改了一个全局变量，所以在多线程下可能出现问题\r\n    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);\r\n    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, connTimeout); \r\n    curl_easy_setopt(curl, CURLOPT_TIMEOUT, readTimeout);\r\n\r\n    //遇到HTTP 3xx状态码是否自动重定位\r\n    if (autoRedirect)\r\n        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);\r\n    else\r\n        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L);\r\n\r\n    if (reserveHeaders)\r\n        curl_easy_setopt(curl, CURLOPT_HEADER, 1L);\r\n    else\r\n        curl_easy_setopt(curl, CURLOPT_HEADER, 0L);\r\n\r\n    //添加自定义HTTP头信息\r\n    if (headers != nullptr)\r\n    {\r\n        struct curl_slist* chunk = NULL;\r\n        chunk = curl_slist_append(chunk, headers);\r\n        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);\r\n    }\r\n\r\n    //发起HTTP请求\r\n    CURLcode res = curl_easy_perform(curl);\r\n\r\n    //释放CURL对象\r\n    curl_easy_cleanup(curl);\r\n\r\n    return res == CURLcode::CURLE_OK;\r\n}\r\n\r\n//http POST  \r\nbool CCurlClient::post(const char* url, const char* headers, const char* postParams, std::string& response,\r\n                       bool autoRedirect/* = false*/, bool reserveHeaders/* = false*/, int64_t connTimeout/* = 1L*/, int64_t readTimeout/* = 5L*/)\r\n{\r\n    //初始化CURL对象\r\n    CURL* curl = curl_easy_init();\r\n    if (curl == nullptr)\r\n        return false;\r\n\r\n    //设置请求方式为post\r\n    curl_easy_setopt(curl, CURLOPT_POST, 1L);\r\n    //设置请求的URL\r\n    curl_easy_setopt(curl, CURLOPT_URL, url);\r\n    //设置POST请求的参数\r\n    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postParams);\r\n    //不启用HTTPS\r\n    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);\r\n    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);\r\n    //设置相关回调函数 \r\n    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);\r\n    curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);\r\n    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, reqReply);\r\n    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&response);\r\n    //禁用SIGALRM+sigsetjmp/siglongjmp的超时机制，\r\n    //采用其他超时机制，因为该机制修改了一个全局变量，所以在多线程下可能会出现问题\r\n    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);\r\n    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, connTimeout);\r\n    curl_easy_setopt(curl, CURLOPT_TIMEOUT, readTimeout);\r\n\r\n    //遇到HTTP 3xx状态码是否自动重定位\r\n    if (autoRedirect)\r\n        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);\r\n    else\r\n        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L);\r\n\r\n    if (reserveHeaders)\r\n        curl_easy_setopt(curl, CURLOPT_HEADER, 1L);\r\n    else\r\n        curl_easy_setopt(curl, CURLOPT_HEADER, 0L);\r\n\r\n    //添加自定义头信息\r\n    if (headers != nullptr)\r\n    {\r\n        struct curl_slist* chunk = NULL;\r\n        chunk = curl_slist_append(chunk, headers);\r\n        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);\r\n    }\r\n\r\n    //发起HTTP请求\r\n    CURLcode res = curl_easy_perform(curl);\r\n\r\n    //释放CURL对象\r\n    curl_easy_cleanup(curl);\r\n\r\n    return res == CURLcode::CURLE_OK;\r\n}\r\n"
  },
  {
    "path": "Chapter06/code/curl/CurlClient.h",
    "content": "/** \r\n * 封装curl的HTTP库，CurlClient.h\r\n * zhangyl 2019.08.27\r\n */\r\n\r\n#ifndef __CURL_CLIENT_H__\r\n#define __CURL_CLIENT_H__\r\n\r\n#include <string>\r\n\r\n#include \"curl.h\"\r\n\r\nclass CCurlClient final\r\n{\r\npublic:\r\n    CCurlClient();\r\n    ~CCurlClient();\r\n\r\n    CCurlClient(const CCurlClient& rhs) = delete;\r\n    CCurlClient& operator=(const CCurlClient& rhs) = delete;\r\n\r\n    /**\r\n     * 初始化libcurl\r\n     * 非线程安全函数，建议在程序初始化时调用一次，以免因多线程调用curl_easy_init而崩溃\r\n     */\r\n    static void init();\r\n    /**\r\n     * 反初始化libcurl\r\n     * 非线程安全函数，建议在程序退出时调用一次\r\n     */\r\n    static void uninit();\r\n\r\n    /** 发送HTTP GET请求\r\n     * @param URL请求的网址\r\n     * @param headers随请求发送的自定义HTTP头信息，多个自定义头之间使用\\r\\n分割，最后一个以\\r\\n结束，若无自定义HTTP头信息，则将其设置为nullptr\r\n     * @param response如果请求成功，则存储HTTP请求结果（注意：response在函数调用中是追加模式，也就是说如果上一次response的值不清空，则调用这个函数时会追加，而不是覆盖）\r\n     * @param autoRedirect请求得到HTTP 3xx的状态码是否自动重定向至新的URL\r\n     * @param bReserveHeaders请求的结果中（存储于response），是否保留HTTP头信息\r\n     * @param connTimeout连接超时时间，单位为秒（对于某些HTTP URI资源不好的，若总是返回超时，则可以将该参数设置得大一点）\r\n     * @param readTimeout读取数据超时时间，单位为秒（对于某些HTTP URI资源不好的，若总是返回超时，则可以将该参数设置得大一点）\r\n     */\r\n    bool get(const char* url, const char* headers, std::string& response, bool autoRedirect = false, bool reserveHeaders = false, int64_t connTimeout = 1L, int64_t readTimeout = 5L);\r\n    \r\n    /** 发送HTTP POST请求\r\n     * @param url请求的网址\r\n     * @param headers随请求发送的自定义HTTP头信息，多个自定义头之间使用\\r\\n分割，最后一个以\\r\\n结束，若无自定义HTTP头信息，则将其设置为nullptr\r\n     * @param postParams post参数内容\r\n     * @param response如果请求成功，则存储HTTP请求的结果（注意：response在函数调用中是追加模式，也就是说如果上一次response的值不清空，则调用这个函数时会追加，而不是覆盖）\r\n     * @param autoRedirect请求得到HTTP 3xx的状态码是否自动重定向至新的URL\r\n     * @param bReserveHeaders请求的结果中（存储于response）是否保留HTTP头信息\r\n     * @param connTimeout 连接超时时间，单位为秒（对于某些HTTP URI资源不好的，若总是返回超时，则可以将该参数设置得大一点）\r\n     * @param readTimeout读取数据超时时间，单位为秒（对于某些HTTP URI资源不好的，若总是返回超时，则可以将该参数设置得大一点）\r\n     */\r\n    bool post(const char* url, const char* headers, const char* postParams, std::string& response, bool autoRedirect = false, bool reserveHeaders = false, int64_t connTimeout = 1L, int64_t readTimeout = 5L);\r\n\r\n\r\nprivate:\r\n    static bool  m_bGlobalInit;\r\n};\r\n\r\n#endif //!__CURL_CLIENT_H__"
  },
  {
    "path": "Chapter06/code/curl/curl.h",
    "content": "#ifndef __CURL_CURL_H\r\n#define __CURL_CURL_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n/*\r\n * If you have libcurl problems, all docs and details are found here:\r\n *   https://curl.haxx.se/libcurl/\r\n *\r\n * curl-library mailing list subscription and unsubscription web interface:\r\n *   https://cool.haxx.se/mailman/listinfo/curl-library/\r\n */\r\n\r\n#ifdef CURL_NO_OLDIES\r\n#define CURL_STRICTER\r\n#endif\r\n\r\n#include \"curlver.h\"         /* libcurl version defines   */\r\n#include \"system.h\"          /* determine things run-time */\r\n\r\n/*\r\n * Define WIN32 when build target is Win32 API\r\n */\r\n\r\n#if (defined(_WIN32) || defined(__WIN32__)) && \\\r\n     !defined(WIN32) && !defined(__SYMBIAN32__)\r\n#define WIN32\r\n#endif\r\n\r\n#include <stdio.h>\r\n#include <limits.h>\r\n\r\n#if defined(__FreeBSD__) && (__FreeBSD__ >= 2)\r\n/* Needed for __FreeBSD_version symbol definition */\r\n#include <osreldate.h>\r\n#endif\r\n\r\n/* The include stuff here below is mainly for time_t! */\r\n#include <sys/types.h>\r\n#include <time.h>\r\n\r\n#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)\r\n#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \\\r\n      defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H))\r\n/* The check above prevents the winsock2 inclusion if winsock.h already was\r\n   included, since they can't co-exist without problems */\r\n#include <winsock2.h>\r\n#include <ws2tcpip.h>\r\n#endif\r\n#endif\r\n\r\n/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish\r\n   libc5-based Linux systems. Only include it on systems that are known to\r\n   require it! */\r\n#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \\\r\n    defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \\\r\n    defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \\\r\n    defined(__CYGWIN__) || \\\r\n   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000))\r\n#include <sys/select.h>\r\n#endif\r\n\r\n#if !defined(WIN32) && !defined(_WIN32_WCE)\r\n#include <sys/socket.h>\r\n#endif\r\n\r\n#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__)\r\n#include <sys/time.h>\r\n#endif\r\n\r\n#ifdef __BEOS__\r\n#include <support/SupportDefs.h>\r\n#endif\r\n\r\n/* Compatibility for non-Clang compilers */\r\n#ifndef __has_declspec_attribute\r\n#  define __has_declspec_attribute(x) 0\r\n#endif\r\n\r\n#ifdef  __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)\r\ntypedef struct Curl_easy CURL;\r\ntypedef struct Curl_share CURLSH;\r\n#else\r\ntypedef void CURL;\r\ntypedef void CURLSH;\r\n#endif\r\n\r\n/*\r\n * libcurl external API function linkage decorations.\r\n */\r\n\r\n#ifdef CURL_STATICLIB\r\n#  define CURL_EXTERN\r\n#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) || \\\r\n     (__has_declspec_attribute(dllexport) && \\\r\n      __has_declspec_attribute(dllimport))\r\n#  if defined(BUILDING_LIBCURL)\r\n#    define CURL_EXTERN  __declspec(dllexport)\r\n#  else\r\n#    define CURL_EXTERN  __declspec(dllimport)\r\n#  endif\r\n#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS)\r\n#  define CURL_EXTERN CURL_EXTERN_SYMBOL\r\n#else\r\n#  define CURL_EXTERN\r\n#endif\r\n\r\n#ifndef curl_socket_typedef\r\n/* socket typedef */\r\n#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)\r\ntypedef SOCKET curl_socket_t;\r\n#define CURL_SOCKET_BAD INVALID_SOCKET\r\n#else\r\ntypedef int curl_socket_t;\r\n#define CURL_SOCKET_BAD -1\r\n#endif\r\n#define curl_socket_typedef\r\n#endif /* curl_socket_typedef */\r\n\r\n/* enum for the different supported SSL backends */\r\ntypedef enum {\r\n  CURLSSLBACKEND_NONE = 0,\r\n  CURLSSLBACKEND_OPENSSL = 1,\r\n  CURLSSLBACKEND_GNUTLS = 2,\r\n  CURLSSLBACKEND_NSS = 3,\r\n  CURLSSLBACKEND_OBSOLETE4 = 4,  /* Was QSOSSL. */\r\n  CURLSSLBACKEND_GSKIT = 5,\r\n  CURLSSLBACKEND_POLARSSL = 6,\r\n  CURLSSLBACKEND_WOLFSSL = 7,\r\n  CURLSSLBACKEND_SCHANNEL = 8,\r\n  CURLSSLBACKEND_SECURETRANSPORT = 9,\r\n  CURLSSLBACKEND_AXTLS = 10, /* never used since 7.63.0 */\r\n  CURLSSLBACKEND_MBEDTLS = 11,\r\n  CURLSSLBACKEND_MESALINK = 12\r\n} curl_sslbackend;\r\n\r\n/* aliases for library clones and renames */\r\n#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL\r\n#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL\r\n\r\n/* deprecated names: */\r\n#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL\r\n#define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT\r\n\r\nstruct curl_httppost {\r\n  struct curl_httppost *next;       /* next entry in the list */\r\n  char *name;                       /* pointer to allocated name */\r\n  long namelength;                  /* length of name length */\r\n  char *contents;                   /* pointer to allocated data contents */\r\n  long contentslength;              /* length of contents field, see also\r\n                                       CURL_HTTPPOST_LARGE */\r\n  char *buffer;                     /* pointer to allocated buffer contents */\r\n  long bufferlength;                /* length of buffer field */\r\n  char *contenttype;                /* Content-Type */\r\n  struct curl_slist *contentheader; /* list of extra headers for this form */\r\n  struct curl_httppost *more;       /* if one field name has more than one\r\n                                       file, this link should link to following\r\n                                       files */\r\n  long flags;                       /* as defined below */\r\n\r\n/* specified content is a file name */\r\n#define CURL_HTTPPOST_FILENAME (1<<0)\r\n/* specified content is a file name */\r\n#define CURL_HTTPPOST_READFILE (1<<1)\r\n/* name is only stored pointer do not free in formfree */\r\n#define CURL_HTTPPOST_PTRNAME (1<<2)\r\n/* contents is only stored pointer do not free in formfree */\r\n#define CURL_HTTPPOST_PTRCONTENTS (1<<3)\r\n/* upload file from buffer */\r\n#define CURL_HTTPPOST_BUFFER (1<<4)\r\n/* upload file from pointer contents */\r\n#define CURL_HTTPPOST_PTRBUFFER (1<<5)\r\n/* upload file contents by using the regular read callback to get the data and\r\n   pass the given pointer as custom pointer */\r\n#define CURL_HTTPPOST_CALLBACK (1<<6)\r\n/* use size in 'contentlen', added in 7.46.0 */\r\n#define CURL_HTTPPOST_LARGE (1<<7)\r\n\r\n  char *showfilename;               /* The file name to show. If not set, the\r\n                                       actual file name will be used (if this\r\n                                       is a file part) */\r\n  void *userp;                      /* custom pointer used for\r\n                                       HTTPPOST_CALLBACK posts */\r\n  curl_off_t contentlen;            /* alternative length of contents\r\n                                       field. Used if CURL_HTTPPOST_LARGE is\r\n                                       set. Added in 7.46.0 */\r\n};\r\n\r\n/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered\r\n   deprecated but was the only choice up until 7.31.0 */\r\ntypedef int (*curl_progress_callback)(void *clientp,\r\n                                      double dltotal,\r\n                                      double dlnow,\r\n                                      double ultotal,\r\n                                      double ulnow);\r\n\r\n/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in\r\n   7.32.0, it avoids floating point and provides more detailed information. */\r\ntypedef int (*curl_xferinfo_callback)(void *clientp,\r\n                                      curl_off_t dltotal,\r\n                                      curl_off_t dlnow,\r\n                                      curl_off_t ultotal,\r\n                                      curl_off_t ulnow);\r\n\r\n#ifndef CURL_MAX_READ_SIZE\r\n  /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */\r\n#define CURL_MAX_READ_SIZE 524288\r\n#endif\r\n\r\n#ifndef CURL_MAX_WRITE_SIZE\r\n  /* Tests have proven that 20K is a very bad buffer size for uploads on\r\n     Windows, while 16K for some odd reason performed a lot better.\r\n     We do the ifndef check to allow this value to easier be changed at build\r\n     time for those who feel adventurous. The practical minimum is about\r\n     400 bytes since libcurl uses a buffer of this size as a scratch area\r\n     (unrelated to network send operations). */\r\n#define CURL_MAX_WRITE_SIZE 16384\r\n#endif\r\n\r\n#ifndef CURL_MAX_HTTP_HEADER\r\n/* The only reason to have a max limit for this is to avoid the risk of a bad\r\n   server feeding libcurl with a never-ending header that will cause reallocs\r\n   infinitely */\r\n#define CURL_MAX_HTTP_HEADER (100*1024)\r\n#endif\r\n\r\n/* This is a magic return code for the write callback that, when returned,\r\n   will signal libcurl to pause receiving on the current transfer. */\r\n#define CURL_WRITEFUNC_PAUSE 0x10000001\r\n\r\ntypedef size_t (*curl_write_callback)(char *buffer,\r\n                                      size_t size,\r\n                                      size_t nitems,\r\n                                      void *outstream);\r\n\r\n/* This callback will be called when a new resolver request is made */\r\ntypedef int (*curl_resolver_start_callback)(void *resolver_state,\r\n                                            void *reserved, void *userdata);\r\n\r\n/* enumeration of file types */\r\ntypedef enum {\r\n  CURLFILETYPE_FILE = 0,\r\n  CURLFILETYPE_DIRECTORY,\r\n  CURLFILETYPE_SYMLINK,\r\n  CURLFILETYPE_DEVICE_BLOCK,\r\n  CURLFILETYPE_DEVICE_CHAR,\r\n  CURLFILETYPE_NAMEDPIPE,\r\n  CURLFILETYPE_SOCKET,\r\n  CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */\r\n\r\n  CURLFILETYPE_UNKNOWN /* should never occur */\r\n} curlfiletype;\r\n\r\n#define CURLFINFOFLAG_KNOWN_FILENAME    (1<<0)\r\n#define CURLFINFOFLAG_KNOWN_FILETYPE    (1<<1)\r\n#define CURLFINFOFLAG_KNOWN_TIME        (1<<2)\r\n#define CURLFINFOFLAG_KNOWN_PERM        (1<<3)\r\n#define CURLFINFOFLAG_KNOWN_UID         (1<<4)\r\n#define CURLFINFOFLAG_KNOWN_GID         (1<<5)\r\n#define CURLFINFOFLAG_KNOWN_SIZE        (1<<6)\r\n#define CURLFINFOFLAG_KNOWN_HLINKCOUNT  (1<<7)\r\n\r\n/* Content of this structure depends on information which is known and is\r\n   achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man\r\n   page for callbacks returning this structure -- some fields are mandatory,\r\n   some others are optional. The FLAG field has special meaning. */\r\nstruct curl_fileinfo {\r\n  char *filename;\r\n  curlfiletype filetype;\r\n  time_t time;\r\n  unsigned int perm;\r\n  int uid;\r\n  int gid;\r\n  curl_off_t size;\r\n  long int hardlinks;\r\n\r\n  struct {\r\n    /* If some of these fields is not NULL, it is a pointer to b_data. */\r\n    char *time;\r\n    char *perm;\r\n    char *user;\r\n    char *group;\r\n    char *target; /* pointer to the target filename of a symlink */\r\n  } strings;\r\n\r\n  unsigned int flags;\r\n\r\n  /* used internally */\r\n  char *b_data;\r\n  size_t b_size;\r\n  size_t b_used;\r\n};\r\n\r\n/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */\r\n#define CURL_CHUNK_BGN_FUNC_OK      0\r\n#define CURL_CHUNK_BGN_FUNC_FAIL    1 /* tell the lib to end the task */\r\n#define CURL_CHUNK_BGN_FUNC_SKIP    2 /* skip this chunk over */\r\n\r\n/* if splitting of data transfer is enabled, this callback is called before\r\n   download of an individual chunk started. Note that parameter \"remains\" works\r\n   only for FTP wildcard downloading (for now), otherwise is not used */\r\ntypedef long (*curl_chunk_bgn_callback)(const void *transfer_info,\r\n                                        void *ptr,\r\n                                        int remains);\r\n\r\n/* return codes for CURLOPT_CHUNK_END_FUNCTION */\r\n#define CURL_CHUNK_END_FUNC_OK      0\r\n#define CURL_CHUNK_END_FUNC_FAIL    1 /* tell the lib to end the task */\r\n\r\n/* If splitting of data transfer is enabled this callback is called after\r\n   download of an individual chunk finished.\r\n   Note! After this callback was set then it have to be called FOR ALL chunks.\r\n   Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC.\r\n   This is the reason why we don't need \"transfer_info\" parameter in this\r\n   callback and we are not interested in \"remains\" parameter too. */\r\ntypedef long (*curl_chunk_end_callback)(void *ptr);\r\n\r\n/* return codes for FNMATCHFUNCTION */\r\n#define CURL_FNMATCHFUNC_MATCH    0 /* string corresponds to the pattern */\r\n#define CURL_FNMATCHFUNC_NOMATCH  1 /* pattern doesn't match the string */\r\n#define CURL_FNMATCHFUNC_FAIL     2 /* an error occurred */\r\n\r\n/* callback type for wildcard downloading pattern matching. If the\r\n   string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */\r\ntypedef int (*curl_fnmatch_callback)(void *ptr,\r\n                                     const char *pattern,\r\n                                     const char *string);\r\n\r\n/* These are the return codes for the seek callbacks */\r\n#define CURL_SEEKFUNC_OK       0\r\n#define CURL_SEEKFUNC_FAIL     1 /* fail the entire transfer */\r\n#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so\r\n                                    libcurl might try other means instead */\r\ntypedef int (*curl_seek_callback)(void *instream,\r\n                                  curl_off_t offset,\r\n                                  int origin); /* 'whence' */\r\n\r\n/* This is a return code for the read callback that, when returned, will\r\n   signal libcurl to immediately abort the current transfer. */\r\n#define CURL_READFUNC_ABORT 0x10000000\r\n/* This is a return code for the read callback that, when returned, will\r\n   signal libcurl to pause sending data on the current transfer. */\r\n#define CURL_READFUNC_PAUSE 0x10000001\r\n\r\n/* Return code for when the trailing headers' callback has terminated\r\n   without any errors*/\r\n#define CURL_TRAILERFUNC_OK 0\r\n/* Return code for when was an error in the trailing header's list and we\r\n  want to abort the request */\r\n#define CURL_TRAILERFUNC_ABORT 1\r\n\r\ntypedef size_t (*curl_read_callback)(char *buffer,\r\n                                      size_t size,\r\n                                      size_t nitems,\r\n                                      void *instream);\r\n\r\ntypedef int (*curl_trailer_callback)(struct curl_slist **list,\r\n                                      void *userdata);\r\n\r\ntypedef enum {\r\n  CURLSOCKTYPE_IPCXN,  /* socket created for a specific IP connection */\r\n  CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */\r\n  CURLSOCKTYPE_LAST    /* never use */\r\n} curlsocktype;\r\n\r\n/* The return code from the sockopt_callback can signal information back\r\n   to libcurl: */\r\n#define CURL_SOCKOPT_OK 0\r\n#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return\r\n                                CURLE_ABORTED_BY_CALLBACK */\r\n#define CURL_SOCKOPT_ALREADY_CONNECTED 2\r\n\r\ntypedef int (*curl_sockopt_callback)(void *clientp,\r\n                                     curl_socket_t curlfd,\r\n                                     curlsocktype purpose);\r\n\r\nstruct curl_sockaddr {\r\n  int family;\r\n  int socktype;\r\n  int protocol;\r\n  unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it\r\n                           turned really ugly and painful on the systems that\r\n                           lack this type */\r\n  struct sockaddr addr;\r\n};\r\n\r\ntypedef curl_socket_t\r\n(*curl_opensocket_callback)(void *clientp,\r\n                            curlsocktype purpose,\r\n                            struct curl_sockaddr *address);\r\n\r\ntypedef int\r\n(*curl_closesocket_callback)(void *clientp, curl_socket_t item);\r\n\r\ntypedef enum {\r\n  CURLIOE_OK,            /* I/O operation successful */\r\n  CURLIOE_UNKNOWNCMD,    /* command was unknown to callback */\r\n  CURLIOE_FAILRESTART,   /* failed to restart the read */\r\n  CURLIOE_LAST           /* never use */\r\n} curlioerr;\r\n\r\ntypedef enum {\r\n  CURLIOCMD_NOP,         /* no operation */\r\n  CURLIOCMD_RESTARTREAD, /* restart the read stream from start */\r\n  CURLIOCMD_LAST         /* never use */\r\n} curliocmd;\r\n\r\ntypedef curlioerr (*curl_ioctl_callback)(CURL *handle,\r\n                                         int cmd,\r\n                                         void *clientp);\r\n\r\n#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS\r\n/*\r\n * The following typedef's are signatures of malloc, free, realloc, strdup and\r\n * calloc respectively.  Function pointers of these types can be passed to the\r\n * curl_global_init_mem() function to set user defined memory management\r\n * callback routines.\r\n */\r\ntypedef void *(*curl_malloc_callback)(size_t size);\r\ntypedef void (*curl_free_callback)(void *ptr);\r\ntypedef void *(*curl_realloc_callback)(void *ptr, size_t size);\r\ntypedef char *(*curl_strdup_callback)(const char *str);\r\ntypedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);\r\n\r\n#define CURL_DID_MEMORY_FUNC_TYPEDEFS\r\n#endif\r\n\r\n/* the kind of data that is passed to information_callback*/\r\ntypedef enum {\r\n  CURLINFO_TEXT = 0,\r\n  CURLINFO_HEADER_IN,    /* 1 */\r\n  CURLINFO_HEADER_OUT,   /* 2 */\r\n  CURLINFO_DATA_IN,      /* 3 */\r\n  CURLINFO_DATA_OUT,     /* 4 */\r\n  CURLINFO_SSL_DATA_IN,  /* 5 */\r\n  CURLINFO_SSL_DATA_OUT, /* 6 */\r\n  CURLINFO_END\r\n} curl_infotype;\r\n\r\ntypedef int (*curl_debug_callback)\r\n       (CURL *handle,      /* the handle/transfer this concerns */\r\n        curl_infotype type, /* what kind of data */\r\n        char *data,        /* points to the data */\r\n        size_t size,       /* size of the data pointed to */\r\n        void *userptr);    /* whatever the user please */\r\n\r\n/* All possible error codes from all sorts of curl functions. Future versions\r\n   may return other values, stay prepared.\r\n\r\n   Always add new return codes last. Never *EVER* remove any. The return\r\n   codes must remain the same!\r\n */\r\n\r\ntypedef enum {\r\n  CURLE_OK = 0,\r\n  CURLE_UNSUPPORTED_PROTOCOL,    /* 1 */\r\n  CURLE_FAILED_INIT,             /* 2 */\r\n  CURLE_URL_MALFORMAT,           /* 3 */\r\n  CURLE_NOT_BUILT_IN,            /* 4 - [was obsoleted in August 2007 for\r\n                                    7.17.0, reused in April 2011 for 7.21.5] */\r\n  CURLE_COULDNT_RESOLVE_PROXY,   /* 5 */\r\n  CURLE_COULDNT_RESOLVE_HOST,    /* 6 */\r\n  CURLE_COULDNT_CONNECT,         /* 7 */\r\n  CURLE_WEIRD_SERVER_REPLY,      /* 8 */\r\n  CURLE_REMOTE_ACCESS_DENIED,    /* 9 a service was denied by the server\r\n                                    due to lack of access - when login fails\r\n                                    this is not returned. */\r\n  CURLE_FTP_ACCEPT_FAILED,       /* 10 - [was obsoleted in April 2006 for\r\n                                    7.15.4, reused in Dec 2011 for 7.24.0]*/\r\n  CURLE_FTP_WEIRD_PASS_REPLY,    /* 11 */\r\n  CURLE_FTP_ACCEPT_TIMEOUT,      /* 12 - timeout occurred accepting server\r\n                                    [was obsoleted in August 2007 for 7.17.0,\r\n                                    reused in Dec 2011 for 7.24.0]*/\r\n  CURLE_FTP_WEIRD_PASV_REPLY,    /* 13 */\r\n  CURLE_FTP_WEIRD_227_FORMAT,    /* 14 */\r\n  CURLE_FTP_CANT_GET_HOST,       /* 15 */\r\n  CURLE_HTTP2,                   /* 16 - A problem in the http2 framing layer.\r\n                                    [was obsoleted in August 2007 for 7.17.0,\r\n                                    reused in July 2014 for 7.38.0] */\r\n  CURLE_FTP_COULDNT_SET_TYPE,    /* 17 */\r\n  CURLE_PARTIAL_FILE,            /* 18 */\r\n  CURLE_FTP_COULDNT_RETR_FILE,   /* 19 */\r\n  CURLE_OBSOLETE20,              /* 20 - NOT USED */\r\n  CURLE_QUOTE_ERROR,             /* 21 - quote command failure */\r\n  CURLE_HTTP_RETURNED_ERROR,     /* 22 */\r\n  CURLE_WRITE_ERROR,             /* 23 */\r\n  CURLE_OBSOLETE24,              /* 24 - NOT USED */\r\n  CURLE_UPLOAD_FAILED,           /* 25 - failed upload \"command\" */\r\n  CURLE_READ_ERROR,              /* 26 - couldn't open/read from file */\r\n  CURLE_OUT_OF_MEMORY,           /* 27 */\r\n  /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error\r\n           instead of a memory allocation error if CURL_DOES_CONVERSIONS\r\n           is defined\r\n  */\r\n  CURLE_OPERATION_TIMEDOUT,      /* 28 - the timeout time was reached */\r\n  CURLE_OBSOLETE29,              /* 29 - NOT USED */\r\n  CURLE_FTP_PORT_FAILED,         /* 30 - FTP PORT operation failed */\r\n  CURLE_FTP_COULDNT_USE_REST,    /* 31 - the REST command failed */\r\n  CURLE_OBSOLETE32,              /* 32 - NOT USED */\r\n  CURLE_RANGE_ERROR,             /* 33 - RANGE \"command\" didn't work */\r\n  CURLE_HTTP_POST_ERROR,         /* 34 */\r\n  CURLE_SSL_CONNECT_ERROR,       /* 35 - wrong when connecting with SSL */\r\n  CURLE_BAD_DOWNLOAD_RESUME,     /* 36 - couldn't resume download */\r\n  CURLE_FILE_COULDNT_READ_FILE,  /* 37 */\r\n  CURLE_LDAP_CANNOT_BIND,        /* 38 */\r\n  CURLE_LDAP_SEARCH_FAILED,      /* 39 */\r\n  CURLE_OBSOLETE40,              /* 40 - NOT USED */\r\n  CURLE_FUNCTION_NOT_FOUND,      /* 41 - NOT USED starting with 7.53.0 */\r\n  CURLE_ABORTED_BY_CALLBACK,     /* 42 */\r\n  CURLE_BAD_FUNCTION_ARGUMENT,   /* 43 */\r\n  CURLE_OBSOLETE44,              /* 44 - NOT USED */\r\n  CURLE_INTERFACE_FAILED,        /* 45 - CURLOPT_INTERFACE failed */\r\n  CURLE_OBSOLETE46,              /* 46 - NOT USED */\r\n  CURLE_TOO_MANY_REDIRECTS,      /* 47 - catch endless re-direct loops */\r\n  CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */\r\n  CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */\r\n  CURLE_OBSOLETE50,              /* 50 - NOT USED */\r\n  CURLE_OBSOLETE51,              /* 51 - NOT USED */\r\n  CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */\r\n  CURLE_SSL_ENGINE_NOTFOUND,     /* 53 - SSL crypto engine not found */\r\n  CURLE_SSL_ENGINE_SETFAILED,    /* 54 - can not set SSL crypto engine as\r\n                                    default */\r\n  CURLE_SEND_ERROR,              /* 55 - failed sending network data */\r\n  CURLE_RECV_ERROR,              /* 56 - failure in receiving network data */\r\n  CURLE_OBSOLETE57,              /* 57 - NOT IN USE */\r\n  CURLE_SSL_CERTPROBLEM,         /* 58 - problem with the local certificate */\r\n  CURLE_SSL_CIPHER,              /* 59 - couldn't use specified cipher */\r\n  CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint\r\n                                     wasn't verified fine */\r\n  CURLE_BAD_CONTENT_ENCODING,    /* 61 - Unrecognized/bad encoding */\r\n  CURLE_LDAP_INVALID_URL,        /* 62 - Invalid LDAP URL */\r\n  CURLE_FILESIZE_EXCEEDED,       /* 63 - Maximum file size exceeded */\r\n  CURLE_USE_SSL_FAILED,          /* 64 - Requested FTP SSL level failed */\r\n  CURLE_SEND_FAIL_REWIND,        /* 65 - Sending the data requires a rewind\r\n                                    that failed */\r\n  CURLE_SSL_ENGINE_INITFAILED,   /* 66 - failed to initialise ENGINE */\r\n  CURLE_LOGIN_DENIED,            /* 67 - user, password or similar was not\r\n                                    accepted and we failed to login */\r\n  CURLE_TFTP_NOTFOUND,           /* 68 - file not found on server */\r\n  CURLE_TFTP_PERM,               /* 69 - permission problem on server */\r\n  CURLE_REMOTE_DISK_FULL,        /* 70 - out of disk space on server */\r\n  CURLE_TFTP_ILLEGAL,            /* 71 - Illegal TFTP operation */\r\n  CURLE_TFTP_UNKNOWNID,          /* 72 - Unknown transfer ID */\r\n  CURLE_REMOTE_FILE_EXISTS,      /* 73 - File already exists */\r\n  CURLE_TFTP_NOSUCHUSER,         /* 74 - No such user */\r\n  CURLE_CONV_FAILED,             /* 75 - conversion failed */\r\n  CURLE_CONV_REQD,               /* 76 - caller must register conversion\r\n                                    callbacks using curl_easy_setopt options\r\n                                    CURLOPT_CONV_FROM_NETWORK_FUNCTION,\r\n                                    CURLOPT_CONV_TO_NETWORK_FUNCTION, and\r\n                                    CURLOPT_CONV_FROM_UTF8_FUNCTION */\r\n  CURLE_SSL_CACERT_BADFILE,      /* 77 - could not load CACERT file, missing\r\n                                    or wrong format */\r\n  CURLE_REMOTE_FILE_NOT_FOUND,   /* 78 - remote file not found */\r\n  CURLE_SSH,                     /* 79 - error from the SSH layer, somewhat\r\n                                    generic so the error message will be of\r\n                                    interest when this has happened */\r\n\r\n  CURLE_SSL_SHUTDOWN_FAILED,     /* 80 - Failed to shut down the SSL\r\n                                    connection */\r\n  CURLE_AGAIN,                   /* 81 - socket is not ready for send/recv,\r\n                                    wait till it's ready and try again (Added\r\n                                    in 7.18.2) */\r\n  CURLE_SSL_CRL_BADFILE,         /* 82 - could not load CRL file, missing or\r\n                                    wrong format (Added in 7.19.0) */\r\n  CURLE_SSL_ISSUER_ERROR,        /* 83 - Issuer check failed.  (Added in\r\n                                    7.19.0) */\r\n  CURLE_FTP_PRET_FAILED,         /* 84 - a PRET command failed */\r\n  CURLE_RTSP_CSEQ_ERROR,         /* 85 - mismatch of RTSP CSeq numbers */\r\n  CURLE_RTSP_SESSION_ERROR,      /* 86 - mismatch of RTSP Session Ids */\r\n  CURLE_FTP_BAD_FILE_LIST,       /* 87 - unable to parse FTP file list */\r\n  CURLE_CHUNK_FAILED,            /* 88 - chunk callback reported error */\r\n  CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the\r\n                                    session will be queued */\r\n  CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not\r\n                                     match */\r\n  CURLE_SSL_INVALIDCERTSTATUS,   /* 91 - invalid certificate status */\r\n  CURLE_HTTP2_STREAM,            /* 92 - stream error in HTTP/2 framing layer\r\n                                    */\r\n  CURLE_RECURSIVE_API_CALL,      /* 93 - an api function was called from\r\n                                    inside a callback */\r\n  CURL_LAST /* never use! */\r\n} CURLcode;\r\n\r\n#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all\r\n                          the obsolete stuff removed! */\r\n\r\n/* Previously obsolete error code re-used in 7.38.0 */\r\n#define CURLE_OBSOLETE16 CURLE_HTTP2\r\n\r\n/* Previously obsolete error codes re-used in 7.24.0 */\r\n#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED\r\n#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT\r\n\r\n/*  compatibility with older names */\r\n#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING\r\n#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY\r\n\r\n/* The following were added in 7.62.0 */\r\n#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION\r\n\r\n/* The following were added in 7.21.5, April 2011 */\r\n#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION\r\n\r\n/* The following were added in 7.17.1 */\r\n/* These are scheduled to disappear by 2009 */\r\n#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION\r\n\r\n/* The following were added in 7.17.0 */\r\n/* These are scheduled to disappear by 2009 */\r\n#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */\r\n#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46\r\n#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44\r\n#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10\r\n#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16\r\n#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32\r\n#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29\r\n#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12\r\n#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20\r\n#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40\r\n#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24\r\n#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57\r\n#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN\r\n\r\n#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED\r\n#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE\r\n#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR\r\n#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL\r\n#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS\r\n#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR\r\n#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED\r\n\r\n/* The following were added earlier */\r\n\r\n#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT\r\n\r\n#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR\r\n#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED\r\n#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED\r\n\r\n#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE\r\n#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME\r\n\r\n/* This was the error code 50 in 7.7.3 and a few earlier versions, this\r\n   is no longer used by libcurl but is instead #defined here only to not\r\n   make programs break */\r\n#define CURLE_ALREADY_COMPLETE 99999\r\n\r\n/* Provide defines for really old option names */\r\n#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */\r\n#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */\r\n#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA\r\n\r\n/* Since long deprecated options with no code in the lib that does anything\r\n   with them. */\r\n#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40\r\n#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72\r\n\r\n#endif /*!CURL_NO_OLDIES*/\r\n\r\n/* This prototype applies to all conversion callbacks */\r\ntypedef CURLcode (*curl_conv_callback)(char *buffer, size_t length);\r\n\r\ntypedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl,    /* easy handle */\r\n                                          void *ssl_ctx, /* actually an\r\n                                                            OpenSSL SSL_CTX */\r\n                                          void *userptr);\r\n\r\ntypedef enum {\r\n  CURLPROXY_HTTP = 0,   /* added in 7.10, new in 7.19.4 default is to use\r\n                           CONNECT HTTP/1.1 */\r\n  CURLPROXY_HTTP_1_0 = 1,   /* added in 7.19.4, force to use CONNECT\r\n                               HTTP/1.0  */\r\n  CURLPROXY_HTTPS = 2, /* added in 7.52.0 */\r\n  CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already\r\n                           in 7.10 */\r\n  CURLPROXY_SOCKS5 = 5, /* added in 7.10 */\r\n  CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */\r\n  CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the\r\n                                   host name rather than the IP address. added\r\n                                   in 7.18.0 */\r\n} curl_proxytype;  /* this enum was added in 7.10 */\r\n\r\n/*\r\n * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options:\r\n *\r\n * CURLAUTH_NONE         - No HTTP authentication\r\n * CURLAUTH_BASIC        - HTTP Basic authentication (default)\r\n * CURLAUTH_DIGEST       - HTTP Digest authentication\r\n * CURLAUTH_NEGOTIATE    - HTTP Negotiate (SPNEGO) authentication\r\n * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated)\r\n * CURLAUTH_NTLM         - HTTP NTLM authentication\r\n * CURLAUTH_DIGEST_IE    - HTTP Digest authentication with IE flavour\r\n * CURLAUTH_NTLM_WB      - HTTP NTLM authentication delegated to winbind helper\r\n * CURLAUTH_BEARER       - HTTP Bearer token authentication\r\n * CURLAUTH_ONLY         - Use together with a single other type to force no\r\n *                         authentication or just that single type\r\n * CURLAUTH_ANY          - All fine types set\r\n * CURLAUTH_ANYSAFE      - All fine types except Basic\r\n */\r\n\r\n#define CURLAUTH_NONE         ((unsigned long)0)\r\n#define CURLAUTH_BASIC        (((unsigned long)1)<<0)\r\n#define CURLAUTH_DIGEST       (((unsigned long)1)<<1)\r\n#define CURLAUTH_NEGOTIATE    (((unsigned long)1)<<2)\r\n/* Deprecated since the advent of CURLAUTH_NEGOTIATE */\r\n#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE\r\n/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */\r\n#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE\r\n#define CURLAUTH_NTLM         (((unsigned long)1)<<3)\r\n#define CURLAUTH_DIGEST_IE    (((unsigned long)1)<<4)\r\n#define CURLAUTH_NTLM_WB      (((unsigned long)1)<<5)\r\n#define CURLAUTH_BEARER       (((unsigned long)1)<<6)\r\n#define CURLAUTH_ONLY         (((unsigned long)1)<<31)\r\n#define CURLAUTH_ANY          (~CURLAUTH_DIGEST_IE)\r\n#define CURLAUTH_ANYSAFE      (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE))\r\n\r\n#define CURLSSH_AUTH_ANY       ~0     /* all types supported by the server */\r\n#define CURLSSH_AUTH_NONE      0      /* none allowed, silly but complete */\r\n#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */\r\n#define CURLSSH_AUTH_PASSWORD  (1<<1) /* password */\r\n#define CURLSSH_AUTH_HOST      (1<<2) /* host key files */\r\n#define CURLSSH_AUTH_KEYBOARD  (1<<3) /* keyboard interactive */\r\n#define CURLSSH_AUTH_AGENT     (1<<4) /* agent (ssh-agent, pageant...) */\r\n#define CURLSSH_AUTH_GSSAPI    (1<<5) /* gssapi (kerberos, ...) */\r\n#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY\r\n\r\n#define CURLGSSAPI_DELEGATION_NONE        0      /* no delegation (default) */\r\n#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */\r\n#define CURLGSSAPI_DELEGATION_FLAG        (1<<1) /* delegate always */\r\n\r\n#define CURL_ERROR_SIZE 256\r\n\r\nenum curl_khtype {\r\n  CURLKHTYPE_UNKNOWN,\r\n  CURLKHTYPE_RSA1,\r\n  CURLKHTYPE_RSA,\r\n  CURLKHTYPE_DSS,\r\n  CURLKHTYPE_ECDSA,\r\n  CURLKHTYPE_ED25519\r\n};\r\n\r\nstruct curl_khkey {\r\n  const char *key; /* points to a zero-terminated string encoded with base64\r\n                      if len is zero, otherwise to the \"raw\" data */\r\n  size_t len;\r\n  enum curl_khtype keytype;\r\n};\r\n\r\n/* this is the set of return values expected from the curl_sshkeycallback\r\n   callback */\r\nenum curl_khstat {\r\n  CURLKHSTAT_FINE_ADD_TO_FILE,\r\n  CURLKHSTAT_FINE,\r\n  CURLKHSTAT_REJECT, /* reject the connection, return an error */\r\n  CURLKHSTAT_DEFER,  /* do not accept it, but we can't answer right now so\r\n                        this causes a CURLE_DEFER error but otherwise the\r\n                        connection will be left intact etc */\r\n  CURLKHSTAT_LAST    /* not for use, only a marker for last-in-list */\r\n};\r\n\r\n/* this is the set of status codes pass in to the callback */\r\nenum curl_khmatch {\r\n  CURLKHMATCH_OK,       /* match */\r\n  CURLKHMATCH_MISMATCH, /* host found, key mismatch! */\r\n  CURLKHMATCH_MISSING,  /* no matching host/key found */\r\n  CURLKHMATCH_LAST      /* not for use, only a marker for last-in-list */\r\n};\r\n\r\ntypedef int\r\n  (*curl_sshkeycallback) (CURL *easy,     /* easy handle */\r\n                          const struct curl_khkey *knownkey, /* known */\r\n                          const struct curl_khkey *foundkey, /* found */\r\n                          enum curl_khmatch, /* libcurl's view on the keys */\r\n                          void *clientp); /* custom pointer passed from app */\r\n\r\n/* parameter for the CURLOPT_USE_SSL option */\r\ntypedef enum {\r\n  CURLUSESSL_NONE,    /* do not attempt to use SSL */\r\n  CURLUSESSL_TRY,     /* try using SSL, proceed anyway otherwise */\r\n  CURLUSESSL_CONTROL, /* SSL for the control connection or fail */\r\n  CURLUSESSL_ALL,     /* SSL for all communication or fail */\r\n  CURLUSESSL_LAST     /* not an option, never use */\r\n} curl_usessl;\r\n\r\n/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */\r\n\r\n/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the\r\n   name of improving interoperability with older servers. Some SSL libraries\r\n   have introduced work-arounds for this flaw but those work-arounds sometimes\r\n   make the SSL communication fail. To regain functionality with those broken\r\n   servers, a user can this way allow the vulnerability back. */\r\n#define CURLSSLOPT_ALLOW_BEAST (1<<0)\r\n\r\n/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those\r\n   SSL backends where such behavior is present. */\r\n#define CURLSSLOPT_NO_REVOKE (1<<1)\r\n\r\n/* The default connection attempt delay in milliseconds for happy eyeballs.\r\n   CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document\r\n   this value, keep them in sync. */\r\n#define CURL_HET_DEFAULT 200L\r\n\r\n/* The default connection upkeep interval in milliseconds. */\r\n#define CURL_UPKEEP_INTERVAL_DEFAULT 60000L\r\n\r\n#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all\r\n                          the obsolete stuff removed! */\r\n\r\n/* Backwards compatibility with older names */\r\n/* These are scheduled to disappear by 2009 */\r\n\r\n#define CURLFTPSSL_NONE CURLUSESSL_NONE\r\n#define CURLFTPSSL_TRY CURLUSESSL_TRY\r\n#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL\r\n#define CURLFTPSSL_ALL CURLUSESSL_ALL\r\n#define CURLFTPSSL_LAST CURLUSESSL_LAST\r\n#define curl_ftpssl curl_usessl\r\n#endif /*!CURL_NO_OLDIES*/\r\n\r\n/* parameter for the CURLOPT_FTP_SSL_CCC option */\r\ntypedef enum {\r\n  CURLFTPSSL_CCC_NONE,    /* do not send CCC */\r\n  CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */\r\n  CURLFTPSSL_CCC_ACTIVE,  /* Initiate the shutdown */\r\n  CURLFTPSSL_CCC_LAST     /* not an option, never use */\r\n} curl_ftpccc;\r\n\r\n/* parameter for the CURLOPT_FTPSSLAUTH option */\r\ntypedef enum {\r\n  CURLFTPAUTH_DEFAULT, /* let libcurl decide */\r\n  CURLFTPAUTH_SSL,     /* use \"AUTH SSL\" */\r\n  CURLFTPAUTH_TLS,     /* use \"AUTH TLS\" */\r\n  CURLFTPAUTH_LAST /* not an option, never use */\r\n} curl_ftpauth;\r\n\r\n/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */\r\ntypedef enum {\r\n  CURLFTP_CREATE_DIR_NONE,  /* do NOT create missing dirs! */\r\n  CURLFTP_CREATE_DIR,       /* (FTP/SFTP) if CWD fails, try MKD and then CWD\r\n                               again if MKD succeeded, for SFTP this does\r\n                               similar magic */\r\n  CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD\r\n                               again even if MKD failed! */\r\n  CURLFTP_CREATE_DIR_LAST   /* not an option, never use */\r\n} curl_ftpcreatedir;\r\n\r\n/* parameter for the CURLOPT_FTP_FILEMETHOD option */\r\ntypedef enum {\r\n  CURLFTPMETHOD_DEFAULT,   /* let libcurl pick */\r\n  CURLFTPMETHOD_MULTICWD,  /* single CWD operation for each path part */\r\n  CURLFTPMETHOD_NOCWD,     /* no CWD at all */\r\n  CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */\r\n  CURLFTPMETHOD_LAST       /* not an option, never use */\r\n} curl_ftpmethod;\r\n\r\n/* bitmask defines for CURLOPT_HEADEROPT */\r\n#define CURLHEADER_UNIFIED  0\r\n#define CURLHEADER_SEPARATE (1<<0)\r\n\r\n/* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */\r\n#define CURLALTSVC_IMMEDIATELY  (1<<0)\r\n#define CURLALTSVC_ALTUSED      (1<<1)\r\n#define CURLALTSVC_READONLYFILE (1<<2)\r\n#define CURLALTSVC_H1           (1<<3)\r\n#define CURLALTSVC_H2           (1<<4)\r\n#define CURLALTSVC_H3           (1<<5)\r\n\r\n/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */\r\n#define CURLPROTO_HTTP   (1<<0)\r\n#define CURLPROTO_HTTPS  (1<<1)\r\n#define CURLPROTO_FTP    (1<<2)\r\n#define CURLPROTO_FTPS   (1<<3)\r\n#define CURLPROTO_SCP    (1<<4)\r\n#define CURLPROTO_SFTP   (1<<5)\r\n#define CURLPROTO_TELNET (1<<6)\r\n#define CURLPROTO_LDAP   (1<<7)\r\n#define CURLPROTO_LDAPS  (1<<8)\r\n#define CURLPROTO_DICT   (1<<9)\r\n#define CURLPROTO_FILE   (1<<10)\r\n#define CURLPROTO_TFTP   (1<<11)\r\n#define CURLPROTO_IMAP   (1<<12)\r\n#define CURLPROTO_IMAPS  (1<<13)\r\n#define CURLPROTO_POP3   (1<<14)\r\n#define CURLPROTO_POP3S  (1<<15)\r\n#define CURLPROTO_SMTP   (1<<16)\r\n#define CURLPROTO_SMTPS  (1<<17)\r\n#define CURLPROTO_RTSP   (1<<18)\r\n#define CURLPROTO_RTMP   (1<<19)\r\n#define CURLPROTO_RTMPT  (1<<20)\r\n#define CURLPROTO_RTMPE  (1<<21)\r\n#define CURLPROTO_RTMPTE (1<<22)\r\n#define CURLPROTO_RTMPS  (1<<23)\r\n#define CURLPROTO_RTMPTS (1<<24)\r\n#define CURLPROTO_GOPHER (1<<25)\r\n#define CURLPROTO_SMB    (1<<26)\r\n#define CURLPROTO_SMBS   (1<<27)\r\n#define CURLPROTO_ALL    (~0) /* enable everything */\r\n\r\n/* long may be 32 or 64 bits, but we should never depend on anything else\r\n   but 32 */\r\n#define CURLOPTTYPE_LONG          0\r\n#define CURLOPTTYPE_OBJECTPOINT   10000\r\n#define CURLOPTTYPE_STRINGPOINT   10000\r\n#define CURLOPTTYPE_FUNCTIONPOINT 20000\r\n#define CURLOPTTYPE_OFF_T         30000\r\n\r\n/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the\r\n   string options from the header file */\r\n\r\n/* name is uppercase CURLOPT_<name>,\r\n   type is one of the defined CURLOPTTYPE_<type>\r\n   number is unique identifier */\r\n#ifdef CINIT\r\n#undef CINIT\r\n#endif\r\n\r\n#ifdef CURL_ISOCPP\r\n#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu\r\n#else\r\n/* The macro \"##\" is ISO C, we assume pre-ISO C doesn't support it. */\r\n#define LONG          CURLOPTTYPE_LONG\r\n#define OBJECTPOINT   CURLOPTTYPE_OBJECTPOINT\r\n#define STRINGPOINT   CURLOPTTYPE_OBJECTPOINT\r\n#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT\r\n#define OFF_T         CURLOPTTYPE_OFF_T\r\n#define CINIT(name,type,number) CURLOPT_/**/name = type + number\r\n#endif\r\n\r\n/*\r\n * This macro-mania below setups the CURLOPT_[what] enum, to be used with\r\n * curl_easy_setopt(). The first argument in the CINIT() macro is the [what]\r\n * word.\r\n */\r\n\r\ntypedef enum {\r\n  /* This is the FILE * or void * the regular output should be written to. */\r\n  CINIT(WRITEDATA, OBJECTPOINT, 1),\r\n\r\n  /* The full URL to get/put */\r\n  CINIT(URL, STRINGPOINT, 2),\r\n\r\n  /* Port number to connect to, if other than default. */\r\n  CINIT(PORT, LONG, 3),\r\n\r\n  /* Name of proxy to use. */\r\n  CINIT(PROXY, STRINGPOINT, 4),\r\n\r\n  /* \"user:password;options\" to use when fetching. */\r\n  CINIT(USERPWD, STRINGPOINT, 5),\r\n\r\n  /* \"user:password\" to use with proxy. */\r\n  CINIT(PROXYUSERPWD, STRINGPOINT, 6),\r\n\r\n  /* Range to get, specified as an ASCII string. */\r\n  CINIT(RANGE, STRINGPOINT, 7),\r\n\r\n  /* not used */\r\n\r\n  /* Specified file stream to upload from (use as input): */\r\n  CINIT(READDATA, OBJECTPOINT, 9),\r\n\r\n  /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE\r\n   * bytes big. */\r\n  CINIT(ERRORBUFFER, OBJECTPOINT, 10),\r\n\r\n  /* Function that will be called to store the output (instead of fwrite). The\r\n   * parameters will use fwrite() syntax, make sure to follow them. */\r\n  CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),\r\n\r\n  /* Function that will be called to read the input (instead of fread). The\r\n   * parameters will use fread() syntax, make sure to follow them. */\r\n  CINIT(READFUNCTION, FUNCTIONPOINT, 12),\r\n\r\n  /* Time-out the read operation after this amount of seconds */\r\n  CINIT(TIMEOUT, LONG, 13),\r\n\r\n  /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about\r\n   * how large the file being sent really is. That allows better error\r\n   * checking and better verifies that the upload was successful. -1 means\r\n   * unknown size.\r\n   *\r\n   * For large file support, there is also a _LARGE version of the key\r\n   * which takes an off_t type, allowing platforms with larger off_t\r\n   * sizes to handle larger files.  See below for INFILESIZE_LARGE.\r\n   */\r\n  CINIT(INFILESIZE, LONG, 14),\r\n\r\n  /* POST static input fields. */\r\n  CINIT(POSTFIELDS, OBJECTPOINT, 15),\r\n\r\n  /* Set the referrer page (needed by some CGIs) */\r\n  CINIT(REFERER, STRINGPOINT, 16),\r\n\r\n  /* Set the FTP PORT string (interface name, named or numerical IP address)\r\n     Use i.e '-' to use default address. */\r\n  CINIT(FTPPORT, STRINGPOINT, 17),\r\n\r\n  /* Set the User-Agent string (examined by some CGIs) */\r\n  CINIT(USERAGENT, STRINGPOINT, 18),\r\n\r\n  /* If the download receives less than \"low speed limit\" bytes/second\r\n   * during \"low speed time\" seconds, the operations is aborted.\r\n   * You could i.e if you have a pretty high speed connection, abort if\r\n   * it is less than 2000 bytes/sec during 20 seconds.\r\n   */\r\n\r\n  /* Set the \"low speed limit\" */\r\n  CINIT(LOW_SPEED_LIMIT, LONG, 19),\r\n\r\n  /* Set the \"low speed time\" */\r\n  CINIT(LOW_SPEED_TIME, LONG, 20),\r\n\r\n  /* Set the continuation offset.\r\n   *\r\n   * Note there is also a _LARGE version of this key which uses\r\n   * off_t types, allowing for large file offsets on platforms which\r\n   * use larger-than-32-bit off_t's.  Look below for RESUME_FROM_LARGE.\r\n   */\r\n  CINIT(RESUME_FROM, LONG, 21),\r\n\r\n  /* Set cookie in request: */\r\n  CINIT(COOKIE, STRINGPOINT, 22),\r\n\r\n  /* This points to a linked list of headers, struct curl_slist kind. This\r\n     list is also used for RTSP (in spite of its name) */\r\n  CINIT(HTTPHEADER, OBJECTPOINT, 23),\r\n\r\n  /* This points to a linked list of post entries, struct curl_httppost */\r\n  CINIT(HTTPPOST, OBJECTPOINT, 24),\r\n\r\n  /* name of the file keeping your private SSL-certificate */\r\n  CINIT(SSLCERT, STRINGPOINT, 25),\r\n\r\n  /* password for the SSL or SSH private key */\r\n  CINIT(KEYPASSWD, STRINGPOINT, 26),\r\n\r\n  /* send TYPE parameter? */\r\n  CINIT(CRLF, LONG, 27),\r\n\r\n  /* send linked-list of QUOTE commands */\r\n  CINIT(QUOTE, OBJECTPOINT, 28),\r\n\r\n  /* send FILE * or void * to store headers to, if you use a callback it\r\n     is simply passed to the callback unmodified */\r\n  CINIT(HEADERDATA, OBJECTPOINT, 29),\r\n\r\n  /* point to a file to read the initial cookies from, also enables\r\n     \"cookie awareness\" */\r\n  CINIT(COOKIEFILE, STRINGPOINT, 31),\r\n\r\n  /* What version to specifically try to use.\r\n     See CURL_SSLVERSION defines below. */\r\n  CINIT(SSLVERSION, LONG, 32),\r\n\r\n  /* What kind of HTTP time condition to use, see defines */\r\n  CINIT(TIMECONDITION, LONG, 33),\r\n\r\n  /* Time to use with the above condition. Specified in number of seconds\r\n     since 1 Jan 1970 */\r\n  CINIT(TIMEVALUE, LONG, 34),\r\n\r\n  /* 35 = OBSOLETE */\r\n\r\n  /* Custom request, for customizing the get command like\r\n     HTTP: DELETE, TRACE and others\r\n     FTP: to use a different list command\r\n     */\r\n  CINIT(CUSTOMREQUEST, STRINGPOINT, 36),\r\n\r\n  /* FILE handle to use instead of stderr */\r\n  CINIT(STDERR, OBJECTPOINT, 37),\r\n\r\n  /* 38 is not used */\r\n\r\n  /* send linked-list of post-transfer QUOTE commands */\r\n  CINIT(POSTQUOTE, OBJECTPOINT, 39),\r\n\r\n  CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */\r\n\r\n  CINIT(VERBOSE, LONG, 41),      /* talk a lot */\r\n  CINIT(HEADER, LONG, 42),       /* throw the header out too */\r\n  CINIT(NOPROGRESS, LONG, 43),   /* shut off the progress meter */\r\n  CINIT(NOBODY, LONG, 44),       /* use HEAD to get http document */\r\n  CINIT(FAILONERROR, LONG, 45),  /* no output on http error codes >= 400 */\r\n  CINIT(UPLOAD, LONG, 46),       /* this is an upload */\r\n  CINIT(POST, LONG, 47),         /* HTTP POST method */\r\n  CINIT(DIRLISTONLY, LONG, 48),  /* bare names when listing directories */\r\n\r\n  CINIT(APPEND, LONG, 50),       /* Append instead of overwrite on upload! */\r\n\r\n  /* Specify whether to read the user+password from the .netrc or the URL.\r\n   * This must be one of the CURL_NETRC_* enums below. */\r\n  CINIT(NETRC, LONG, 51),\r\n\r\n  CINIT(FOLLOWLOCATION, LONG, 52),  /* use Location: Luke! */\r\n\r\n  CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */\r\n  CINIT(PUT, LONG, 54),          /* HTTP PUT */\r\n\r\n  /* 55 = OBSOLETE */\r\n\r\n  /* DEPRECATED\r\n   * Function that will be called instead of the internal progress display\r\n   * function. This function should be defined as the curl_progress_callback\r\n   * prototype defines. */\r\n  CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56),\r\n\r\n  /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION\r\n     callbacks */\r\n  CINIT(PROGRESSDATA, OBJECTPOINT, 57),\r\n#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA\r\n\r\n  /* We want the referrer field set automatically when following locations */\r\n  CINIT(AUTOREFERER, LONG, 58),\r\n\r\n  /* Port of the proxy, can be set in the proxy string as well with:\r\n     \"[host]:[port]\" */\r\n  CINIT(PROXYPORT, LONG, 59),\r\n\r\n  /* size of the POST input data, if strlen() is not good to use */\r\n  CINIT(POSTFIELDSIZE, LONG, 60),\r\n\r\n  /* tunnel non-http operations through a HTTP proxy */\r\n  CINIT(HTTPPROXYTUNNEL, LONG, 61),\r\n\r\n  /* Set the interface string to use as outgoing network interface */\r\n  CINIT(INTERFACE, STRINGPOINT, 62),\r\n\r\n  /* Set the krb4/5 security level, this also enables krb4/5 awareness.  This\r\n   * is a string, 'clear', 'safe', 'confidential' or 'private'.  If the string\r\n   * is set but doesn't match one of these, 'private' will be used.  */\r\n  CINIT(KRBLEVEL, STRINGPOINT, 63),\r\n\r\n  /* Set if we should verify the peer in ssl handshake, set 1 to verify. */\r\n  CINIT(SSL_VERIFYPEER, LONG, 64),\r\n\r\n  /* The CApath or CAfile used to validate the peer certificate\r\n     this option is used only if SSL_VERIFYPEER is true */\r\n  CINIT(CAINFO, STRINGPOINT, 65),\r\n\r\n  /* 66 = OBSOLETE */\r\n  /* 67 = OBSOLETE */\r\n\r\n  /* Maximum number of http redirects to follow */\r\n  CINIT(MAXREDIRS, LONG, 68),\r\n\r\n  /* Pass a long set to 1 to get the date of the requested document (if\r\n     possible)! Pass a zero to shut it off. */\r\n  CINIT(FILETIME, LONG, 69),\r\n\r\n  /* This points to a linked list of telnet options */\r\n  CINIT(TELNETOPTIONS, OBJECTPOINT, 70),\r\n\r\n  /* Max amount of cached alive connections */\r\n  CINIT(MAXCONNECTS, LONG, 71),\r\n\r\n  CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */\r\n\r\n  /* 73 = OBSOLETE */\r\n\r\n  /* Set to explicitly use a new connection for the upcoming transfer.\r\n     Do not use this unless you're absolutely sure of this, as it makes the\r\n     operation slower and is less friendly for the network. */\r\n  CINIT(FRESH_CONNECT, LONG, 74),\r\n\r\n  /* Set to explicitly forbid the upcoming transfer's connection to be re-used\r\n     when done. Do not use this unless you're absolutely sure of this, as it\r\n     makes the operation slower and is less friendly for the network. */\r\n  CINIT(FORBID_REUSE, LONG, 75),\r\n\r\n  /* Set to a file name that contains random data for libcurl to use to\r\n     seed the random engine when doing SSL connects. */\r\n  CINIT(RANDOM_FILE, STRINGPOINT, 76),\r\n\r\n  /* Set to the Entropy Gathering Daemon socket pathname */\r\n  CINIT(EGDSOCKET, STRINGPOINT, 77),\r\n\r\n  /* Time-out connect operations after this amount of seconds, if connects are\r\n     OK within this time, then fine... This only aborts the connect phase. */\r\n  CINIT(CONNECTTIMEOUT, LONG, 78),\r\n\r\n  /* Function that will be called to store headers (instead of fwrite). The\r\n   * parameters will use fwrite() syntax, make sure to follow them. */\r\n  CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79),\r\n\r\n  /* Set this to force the HTTP request to get back to GET. Only really usable\r\n     if POST, PUT or a custom request have been used first.\r\n   */\r\n  CINIT(HTTPGET, LONG, 80),\r\n\r\n  /* Set if we should verify the Common name from the peer certificate in ssl\r\n   * handshake, set 1 to check existence, 2 to ensure that it matches the\r\n   * provided hostname. */\r\n  CINIT(SSL_VERIFYHOST, LONG, 81),\r\n\r\n  /* Specify which file name to write all known cookies in after completed\r\n     operation. Set file name to \"-\" (dash) to make it go to stdout. */\r\n  CINIT(COOKIEJAR, STRINGPOINT, 82),\r\n\r\n  /* Specify which SSL ciphers to use */\r\n  CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83),\r\n\r\n  /* Specify which HTTP version to use! This must be set to one of the\r\n     CURL_HTTP_VERSION* enums set below. */\r\n  CINIT(HTTP_VERSION, LONG, 84),\r\n\r\n  /* Specifically switch on or off the FTP engine's use of the EPSV command. By\r\n     default, that one will always be attempted before the more traditional\r\n     PASV command. */\r\n  CINIT(FTP_USE_EPSV, LONG, 85),\r\n\r\n  /* type of the file keeping your SSL-certificate (\"DER\", \"PEM\", \"ENG\") */\r\n  CINIT(SSLCERTTYPE, STRINGPOINT, 86),\r\n\r\n  /* name of the file keeping your private SSL-key */\r\n  CINIT(SSLKEY, STRINGPOINT, 87),\r\n\r\n  /* type of the file keeping your private SSL-key (\"DER\", \"PEM\", \"ENG\") */\r\n  CINIT(SSLKEYTYPE, STRINGPOINT, 88),\r\n\r\n  /* crypto engine for the SSL-sub system */\r\n  CINIT(SSLENGINE, STRINGPOINT, 89),\r\n\r\n  /* set the crypto engine for the SSL-sub system as default\r\n     the param has no meaning...\r\n   */\r\n  CINIT(SSLENGINE_DEFAULT, LONG, 90),\r\n\r\n  /* Non-zero value means to use the global dns cache */\r\n  CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */\r\n\r\n  /* DNS cache timeout */\r\n  CINIT(DNS_CACHE_TIMEOUT, LONG, 92),\r\n\r\n  /* send linked-list of pre-transfer QUOTE commands */\r\n  CINIT(PREQUOTE, OBJECTPOINT, 93),\r\n\r\n  /* set the debug function */\r\n  CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94),\r\n\r\n  /* set the data for the debug function */\r\n  CINIT(DEBUGDATA, OBJECTPOINT, 95),\r\n\r\n  /* mark this as start of a cookie session */\r\n  CINIT(COOKIESESSION, LONG, 96),\r\n\r\n  /* The CApath directory used to validate the peer certificate\r\n     this option is used only if SSL_VERIFYPEER is true */\r\n  CINIT(CAPATH, STRINGPOINT, 97),\r\n\r\n  /* Instruct libcurl to use a smaller receive buffer */\r\n  CINIT(BUFFERSIZE, LONG, 98),\r\n\r\n  /* Instruct libcurl to not use any signal/alarm handlers, even when using\r\n     timeouts. This option is useful for multi-threaded applications.\r\n     See libcurl-the-guide for more background information. */\r\n  CINIT(NOSIGNAL, LONG, 99),\r\n\r\n  /* Provide a CURLShare for mutexing non-ts data */\r\n  CINIT(SHARE, OBJECTPOINT, 100),\r\n\r\n  /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),\r\n     CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and\r\n     CURLPROXY_SOCKS5. */\r\n  CINIT(PROXYTYPE, LONG, 101),\r\n\r\n  /* Set the Accept-Encoding string. Use this to tell a server you would like\r\n     the response to be compressed. Before 7.21.6, this was known as\r\n     CURLOPT_ENCODING */\r\n  CINIT(ACCEPT_ENCODING, STRINGPOINT, 102),\r\n\r\n  /* Set pointer to private data */\r\n  CINIT(PRIVATE, OBJECTPOINT, 103),\r\n\r\n  /* Set aliases for HTTP 200 in the HTTP Response header */\r\n  CINIT(HTTP200ALIASES, OBJECTPOINT, 104),\r\n\r\n  /* Continue to send authentication (user+password) when following locations,\r\n     even when hostname changed. This can potentially send off the name\r\n     and password to whatever host the server decides. */\r\n  CINIT(UNRESTRICTED_AUTH, LONG, 105),\r\n\r\n  /* Specifically switch on or off the FTP engine's use of the EPRT command (\r\n     it also disables the LPRT attempt). By default, those ones will always be\r\n     attempted before the good old traditional PORT command. */\r\n  CINIT(FTP_USE_EPRT, LONG, 106),\r\n\r\n  /* Set this to a bitmask value to enable the particular authentications\r\n     methods you like. Use this in combination with CURLOPT_USERPWD.\r\n     Note that setting multiple bits may cause extra network round-trips. */\r\n  CINIT(HTTPAUTH, LONG, 107),\r\n\r\n  /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx\r\n     in second argument. The function must be matching the\r\n     curl_ssl_ctx_callback proto. */\r\n  CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108),\r\n\r\n  /* Set the userdata for the ssl context callback function's third\r\n     argument */\r\n  CINIT(SSL_CTX_DATA, OBJECTPOINT, 109),\r\n\r\n  /* FTP Option that causes missing dirs to be created on the remote server.\r\n     In 7.19.4 we introduced the convenience enums for this option using the\r\n     CURLFTP_CREATE_DIR prefix.\r\n  */\r\n  CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110),\r\n\r\n  /* Set this to a bitmask value to enable the particular authentications\r\n     methods you like. Use this in combination with CURLOPT_PROXYUSERPWD.\r\n     Note that setting multiple bits may cause extra network round-trips. */\r\n  CINIT(PROXYAUTH, LONG, 111),\r\n\r\n  /* FTP option that changes the timeout, in seconds, associated with\r\n     getting a response.  This is different from transfer timeout time and\r\n     essentially places a demand on the FTP server to acknowledge commands\r\n     in a timely manner. */\r\n  CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112),\r\n#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT\r\n\r\n  /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to\r\n     tell libcurl to resolve names to those IP versions only. This only has\r\n     affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */\r\n  CINIT(IPRESOLVE, LONG, 113),\r\n\r\n  /* Set this option to limit the size of a file that will be downloaded from\r\n     an HTTP or FTP server.\r\n\r\n     Note there is also _LARGE version which adds large file support for\r\n     platforms which have larger off_t sizes.  See MAXFILESIZE_LARGE below. */\r\n  CINIT(MAXFILESIZE, LONG, 114),\r\n\r\n  /* See the comment for INFILESIZE above, but in short, specifies\r\n   * the size of the file being uploaded.  -1 means unknown.\r\n   */\r\n  CINIT(INFILESIZE_LARGE, OFF_T, 115),\r\n\r\n  /* Sets the continuation offset.  There is also a LONG version of this;\r\n   * look above for RESUME_FROM.\r\n   */\r\n  CINIT(RESUME_FROM_LARGE, OFF_T, 116),\r\n\r\n  /* Sets the maximum size of data that will be downloaded from\r\n   * an HTTP or FTP server.  See MAXFILESIZE above for the LONG version.\r\n   */\r\n  CINIT(MAXFILESIZE_LARGE, OFF_T, 117),\r\n\r\n  /* Set this option to the file name of your .netrc file you want libcurl\r\n     to parse (using the CURLOPT_NETRC option). If not set, libcurl will do\r\n     a poor attempt to find the user's home directory and check for a .netrc\r\n     file in there. */\r\n  CINIT(NETRC_FILE, STRINGPOINT, 118),\r\n\r\n  /* Enable SSL/TLS for FTP, pick one of:\r\n     CURLUSESSL_TRY     - try using SSL, proceed anyway otherwise\r\n     CURLUSESSL_CONTROL - SSL for the control connection or fail\r\n     CURLUSESSL_ALL     - SSL for all communication or fail\r\n  */\r\n  CINIT(USE_SSL, LONG, 119),\r\n\r\n  /* The _LARGE version of the standard POSTFIELDSIZE option */\r\n  CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120),\r\n\r\n  /* Enable/disable the TCP Nagle algorithm */\r\n  CINIT(TCP_NODELAY, LONG, 121),\r\n\r\n  /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */\r\n  /* 123 OBSOLETE. Gone in 7.16.0 */\r\n  /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */\r\n  /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */\r\n  /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */\r\n  /* 127 OBSOLETE. Gone in 7.16.0 */\r\n  /* 128 OBSOLETE. Gone in 7.16.0 */\r\n\r\n  /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option\r\n     can be used to change libcurl's default action which is to first try\r\n     \"AUTH SSL\" and then \"AUTH TLS\" in this order, and proceed when a OK\r\n     response has been received.\r\n\r\n     Available parameters are:\r\n     CURLFTPAUTH_DEFAULT - let libcurl decide\r\n     CURLFTPAUTH_SSL     - try \"AUTH SSL\" first, then TLS\r\n     CURLFTPAUTH_TLS     - try \"AUTH TLS\" first, then SSL\r\n  */\r\n  CINIT(FTPSSLAUTH, LONG, 129),\r\n\r\n  CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130),\r\n  CINIT(IOCTLDATA, OBJECTPOINT, 131),\r\n\r\n  /* 132 OBSOLETE. Gone in 7.16.0 */\r\n  /* 133 OBSOLETE. Gone in 7.16.0 */\r\n\r\n  /* zero terminated string for pass on to the FTP server when asked for\r\n     \"account\" info */\r\n  CINIT(FTP_ACCOUNT, STRINGPOINT, 134),\r\n\r\n  /* feed cookie into cookie engine */\r\n  CINIT(COOKIELIST, STRINGPOINT, 135),\r\n\r\n  /* ignore Content-Length */\r\n  CINIT(IGNORE_CONTENT_LENGTH, LONG, 136),\r\n\r\n  /* Set to non-zero to skip the IP address received in a 227 PASV FTP server\r\n     response. Typically used for FTP-SSL purposes but is not restricted to\r\n     that. libcurl will then instead use the same IP address it used for the\r\n     control connection. */\r\n  CINIT(FTP_SKIP_PASV_IP, LONG, 137),\r\n\r\n  /* Select \"file method\" to use when doing FTP, see the curl_ftpmethod\r\n     above. */\r\n  CINIT(FTP_FILEMETHOD, LONG, 138),\r\n\r\n  /* Local port number to bind the socket to */\r\n  CINIT(LOCALPORT, LONG, 139),\r\n\r\n  /* Number of ports to try, including the first one set with LOCALPORT.\r\n     Thus, setting it to 1 will make no additional attempts but the first.\r\n  */\r\n  CINIT(LOCALPORTRANGE, LONG, 140),\r\n\r\n  /* no transfer, set up connection and let application use the socket by\r\n     extracting it with CURLINFO_LASTSOCKET */\r\n  CINIT(CONNECT_ONLY, LONG, 141),\r\n\r\n  /* Function that will be called to convert from the\r\n     network encoding (instead of using the iconv calls in libcurl) */\r\n  CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142),\r\n\r\n  /* Function that will be called to convert to the\r\n     network encoding (instead of using the iconv calls in libcurl) */\r\n  CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143),\r\n\r\n  /* Function that will be called to convert from UTF8\r\n     (instead of using the iconv calls in libcurl)\r\n     Note that this is used only for SSL certificate processing */\r\n  CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144),\r\n\r\n  /* if the connection proceeds too quickly then need to slow it down */\r\n  /* limit-rate: maximum number of bytes per second to send or receive */\r\n  CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145),\r\n  CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146),\r\n\r\n  /* Pointer to command string to send if USER/PASS fails. */\r\n  CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147),\r\n\r\n  /* callback function for setting socket options */\r\n  CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148),\r\n  CINIT(SOCKOPTDATA, OBJECTPOINT, 149),\r\n\r\n  /* set to 0 to disable session ID re-use for this transfer, default is\r\n     enabled (== 1) */\r\n  CINIT(SSL_SESSIONID_CACHE, LONG, 150),\r\n\r\n  /* allowed SSH authentication methods */\r\n  CINIT(SSH_AUTH_TYPES, LONG, 151),\r\n\r\n  /* Used by scp/sftp to do public/private key authentication */\r\n  CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152),\r\n  CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153),\r\n\r\n  /* Send CCC (Clear Command Channel) after authentication */\r\n  CINIT(FTP_SSL_CCC, LONG, 154),\r\n\r\n  /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */\r\n  CINIT(TIMEOUT_MS, LONG, 155),\r\n  CINIT(CONNECTTIMEOUT_MS, LONG, 156),\r\n\r\n  /* set to zero to disable the libcurl's decoding and thus pass the raw body\r\n     data to the application even when it is encoded/compressed */\r\n  CINIT(HTTP_TRANSFER_DECODING, LONG, 157),\r\n  CINIT(HTTP_CONTENT_DECODING, LONG, 158),\r\n\r\n  /* Permission used when creating new files and directories on the remote\r\n     server for protocols that support it, SFTP/SCP/FILE */\r\n  CINIT(NEW_FILE_PERMS, LONG, 159),\r\n  CINIT(NEW_DIRECTORY_PERMS, LONG, 160),\r\n\r\n  /* Set the behaviour of POST when redirecting. Values must be set to one\r\n     of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */\r\n  CINIT(POSTREDIR, LONG, 161),\r\n\r\n  /* used by scp/sftp to verify the host's public key */\r\n  CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162),\r\n\r\n  /* Callback function for opening socket (instead of socket(2)). Optionally,\r\n     callback is able change the address or refuse to connect returning\r\n     CURL_SOCKET_BAD.  The callback should have type\r\n     curl_opensocket_callback */\r\n  CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163),\r\n  CINIT(OPENSOCKETDATA, OBJECTPOINT, 164),\r\n\r\n  /* POST volatile input fields. */\r\n  CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165),\r\n\r\n  /* set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy */\r\n  CINIT(PROXY_TRANSFER_MODE, LONG, 166),\r\n\r\n  /* Callback function for seeking in the input stream */\r\n  CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167),\r\n  CINIT(SEEKDATA, OBJECTPOINT, 168),\r\n\r\n  /* CRL file */\r\n  CINIT(CRLFILE, STRINGPOINT, 169),\r\n\r\n  /* Issuer certificate */\r\n  CINIT(ISSUERCERT, STRINGPOINT, 170),\r\n\r\n  /* (IPv6) Address scope */\r\n  CINIT(ADDRESS_SCOPE, LONG, 171),\r\n\r\n  /* Collect certificate chain info and allow it to get retrievable with\r\n     CURLINFO_CERTINFO after the transfer is complete. */\r\n  CINIT(CERTINFO, LONG, 172),\r\n\r\n  /* \"name\" and \"pwd\" to use when fetching. */\r\n  CINIT(USERNAME, STRINGPOINT, 173),\r\n  CINIT(PASSWORD, STRINGPOINT, 174),\r\n\r\n    /* \"name\" and \"pwd\" to use with Proxy when fetching. */\r\n  CINIT(PROXYUSERNAME, STRINGPOINT, 175),\r\n  CINIT(PROXYPASSWORD, STRINGPOINT, 176),\r\n\r\n  /* Comma separated list of hostnames defining no-proxy zones. These should\r\n     match both hostnames directly, and hostnames within a domain. For\r\n     example, local.com will match local.com and www.local.com, but NOT\r\n     notlocal.com or www.notlocal.com. For compatibility with other\r\n     implementations of this, .local.com will be considered to be the same as\r\n     local.com. A single * is the only valid wildcard, and effectively\r\n     disables the use of proxy. */\r\n  CINIT(NOPROXY, STRINGPOINT, 177),\r\n\r\n  /* block size for TFTP transfers */\r\n  CINIT(TFTP_BLKSIZE, LONG, 178),\r\n\r\n  /* Socks Service */\r\n  CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */\r\n\r\n  /* Socks Service */\r\n  CINIT(SOCKS5_GSSAPI_NEC, LONG, 180),\r\n\r\n  /* set the bitmask for the protocols that are allowed to be used for the\r\n     transfer, which thus helps the app which takes URLs from users or other\r\n     external inputs and want to restrict what protocol(s) to deal\r\n     with. Defaults to CURLPROTO_ALL. */\r\n  CINIT(PROTOCOLS, LONG, 181),\r\n\r\n  /* set the bitmask for the protocols that libcurl is allowed to follow to,\r\n     as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs\r\n     to be set in both bitmasks to be allowed to get redirected to. Defaults\r\n     to all protocols except FILE and SCP. */\r\n  CINIT(REDIR_PROTOCOLS, LONG, 182),\r\n\r\n  /* set the SSH knownhost file name to use */\r\n  CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183),\r\n\r\n  /* set the SSH host key callback, must point to a curl_sshkeycallback\r\n     function */\r\n  CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184),\r\n\r\n  /* set the SSH host key callback custom pointer */\r\n  CINIT(SSH_KEYDATA, OBJECTPOINT, 185),\r\n\r\n  /* set the SMTP mail originator */\r\n  CINIT(MAIL_FROM, STRINGPOINT, 186),\r\n\r\n  /* set the list of SMTP mail receiver(s) */\r\n  CINIT(MAIL_RCPT, OBJECTPOINT, 187),\r\n\r\n  /* FTP: send PRET before PASV */\r\n  CINIT(FTP_USE_PRET, LONG, 188),\r\n\r\n  /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */\r\n  CINIT(RTSP_REQUEST, LONG, 189),\r\n\r\n  /* The RTSP session identifier */\r\n  CINIT(RTSP_SESSION_ID, STRINGPOINT, 190),\r\n\r\n  /* The RTSP stream URI */\r\n  CINIT(RTSP_STREAM_URI, STRINGPOINT, 191),\r\n\r\n  /* The Transport: header to use in RTSP requests */\r\n  CINIT(RTSP_TRANSPORT, STRINGPOINT, 192),\r\n\r\n  /* Manually initialize the client RTSP CSeq for this handle */\r\n  CINIT(RTSP_CLIENT_CSEQ, LONG, 193),\r\n\r\n  /* Manually initialize the server RTSP CSeq for this handle */\r\n  CINIT(RTSP_SERVER_CSEQ, LONG, 194),\r\n\r\n  /* The stream to pass to INTERLEAVEFUNCTION. */\r\n  CINIT(INTERLEAVEDATA, OBJECTPOINT, 195),\r\n\r\n  /* Let the application define a custom write method for RTP data */\r\n  CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196),\r\n\r\n  /* Turn on wildcard matching */\r\n  CINIT(WILDCARDMATCH, LONG, 197),\r\n\r\n  /* Directory matching callback called before downloading of an\r\n     individual file (chunk) started */\r\n  CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198),\r\n\r\n  /* Directory matching callback called after the file (chunk)\r\n     was downloaded, or skipped */\r\n  CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199),\r\n\r\n  /* Change match (fnmatch-like) callback for wildcard matching */\r\n  CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200),\r\n\r\n  /* Let the application define custom chunk data pointer */\r\n  CINIT(CHUNK_DATA, OBJECTPOINT, 201),\r\n\r\n  /* FNMATCH_FUNCTION user pointer */\r\n  CINIT(FNMATCH_DATA, OBJECTPOINT, 202),\r\n\r\n  /* send linked-list of name:port:address sets */\r\n  CINIT(RESOLVE, OBJECTPOINT, 203),\r\n\r\n  /* Set a username for authenticated TLS */\r\n  CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204),\r\n\r\n  /* Set a password for authenticated TLS */\r\n  CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205),\r\n\r\n  /* Set authentication type for authenticated TLS */\r\n  CINIT(TLSAUTH_TYPE, STRINGPOINT, 206),\r\n\r\n  /* Set to 1 to enable the \"TE:\" header in HTTP requests to ask for\r\n     compressed transfer-encoded responses. Set to 0 to disable the use of TE:\r\n     in outgoing requests. The current default is 0, but it might change in a\r\n     future libcurl release.\r\n\r\n     libcurl will ask for the compressed methods it knows of, and if that\r\n     isn't any, it will not ask for transfer-encoding at all even if this\r\n     option is set to 1.\r\n\r\n  */\r\n  CINIT(TRANSFER_ENCODING, LONG, 207),\r\n\r\n  /* Callback function for closing socket (instead of close(2)). The callback\r\n     should have type curl_closesocket_callback */\r\n  CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208),\r\n  CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209),\r\n\r\n  /* allow GSSAPI credential delegation */\r\n  CINIT(GSSAPI_DELEGATION, LONG, 210),\r\n\r\n  /* Set the name servers to use for DNS resolution */\r\n  CINIT(DNS_SERVERS, STRINGPOINT, 211),\r\n\r\n  /* Time-out accept operations (currently for FTP only) after this amount\r\n     of milliseconds. */\r\n  CINIT(ACCEPTTIMEOUT_MS, LONG, 212),\r\n\r\n  /* Set TCP keepalive */\r\n  CINIT(TCP_KEEPALIVE, LONG, 213),\r\n\r\n  /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */\r\n  CINIT(TCP_KEEPIDLE, LONG, 214),\r\n  CINIT(TCP_KEEPINTVL, LONG, 215),\r\n\r\n  /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */\r\n  CINIT(SSL_OPTIONS, LONG, 216),\r\n\r\n  /* Set the SMTP auth originator */\r\n  CINIT(MAIL_AUTH, STRINGPOINT, 217),\r\n\r\n  /* Enable/disable SASL initial response */\r\n  CINIT(SASL_IR, LONG, 218),\r\n\r\n  /* Function that will be called instead of the internal progress display\r\n   * function. This function should be defined as the curl_xferinfo_callback\r\n   * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */\r\n  CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219),\r\n\r\n  /* The XOAUTH2 bearer token */\r\n  CINIT(XOAUTH2_BEARER, STRINGPOINT, 220),\r\n\r\n  /* Set the interface string to use as outgoing network\r\n   * interface for DNS requests.\r\n   * Only supported by the c-ares DNS backend */\r\n  CINIT(DNS_INTERFACE, STRINGPOINT, 221),\r\n\r\n  /* Set the local IPv4 address to use for outgoing DNS requests.\r\n   * Only supported by the c-ares DNS backend */\r\n  CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222),\r\n\r\n  /* Set the local IPv6 address to use for outgoing DNS requests.\r\n   * Only supported by the c-ares DNS backend */\r\n  CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223),\r\n\r\n  /* Set authentication options directly */\r\n  CINIT(LOGIN_OPTIONS, STRINGPOINT, 224),\r\n\r\n  /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */\r\n  CINIT(SSL_ENABLE_NPN, LONG, 225),\r\n\r\n  /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */\r\n  CINIT(SSL_ENABLE_ALPN, LONG, 226),\r\n\r\n  /* Time to wait for a response to a HTTP request containing an\r\n   * Expect: 100-continue header before sending the data anyway. */\r\n  CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227),\r\n\r\n  /* This points to a linked list of headers used for proxy requests only,\r\n     struct curl_slist kind */\r\n  CINIT(PROXYHEADER, OBJECTPOINT, 228),\r\n\r\n  /* Pass in a bitmask of \"header options\" */\r\n  CINIT(HEADEROPT, LONG, 229),\r\n\r\n  /* The public key in DER form used to validate the peer public key\r\n     this option is used only if SSL_VERIFYPEER is true */\r\n  CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230),\r\n\r\n  /* Path to Unix domain socket */\r\n  CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231),\r\n\r\n  /* Set if we should verify the certificate status. */\r\n  CINIT(SSL_VERIFYSTATUS, LONG, 232),\r\n\r\n  /* Set if we should enable TLS false start. */\r\n  CINIT(SSL_FALSESTART, LONG, 233),\r\n\r\n  /* Do not squash dot-dot sequences */\r\n  CINIT(PATH_AS_IS, LONG, 234),\r\n\r\n  /* Proxy Service Name */\r\n  CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235),\r\n\r\n  /* Service Name */\r\n  CINIT(SERVICE_NAME, STRINGPOINT, 236),\r\n\r\n  /* Wait/don't wait for pipe/mutex to clarify */\r\n  CINIT(PIPEWAIT, LONG, 237),\r\n\r\n  /* Set the protocol used when curl is given a URL without a protocol */\r\n  CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238),\r\n\r\n  /* Set stream weight, 1 - 256 (default is 16) */\r\n  CINIT(STREAM_WEIGHT, LONG, 239),\r\n\r\n  /* Set stream dependency on another CURL handle */\r\n  CINIT(STREAM_DEPENDS, OBJECTPOINT, 240),\r\n\r\n  /* Set E-xclusive stream dependency on another CURL handle */\r\n  CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241),\r\n\r\n  /* Do not send any tftp option requests to the server */\r\n  CINIT(TFTP_NO_OPTIONS, LONG, 242),\r\n\r\n  /* Linked-list of host:port:connect-to-host:connect-to-port,\r\n     overrides the URL's host:port (only for the network layer) */\r\n  CINIT(CONNECT_TO, OBJECTPOINT, 243),\r\n\r\n  /* Set TCP Fast Open */\r\n  CINIT(TCP_FASTOPEN, LONG, 244),\r\n\r\n  /* Continue to send data if the server responds early with an\r\n   * HTTP status code >= 300 */\r\n  CINIT(KEEP_SENDING_ON_ERROR, LONG, 245),\r\n\r\n  /* The CApath or CAfile used to validate the proxy certificate\r\n     this option is used only if PROXY_SSL_VERIFYPEER is true */\r\n  CINIT(PROXY_CAINFO, STRINGPOINT, 246),\r\n\r\n  /* The CApath directory used to validate the proxy certificate\r\n     this option is used only if PROXY_SSL_VERIFYPEER is true */\r\n  CINIT(PROXY_CAPATH, STRINGPOINT, 247),\r\n\r\n  /* Set if we should verify the proxy in ssl handshake,\r\n     set 1 to verify. */\r\n  CINIT(PROXY_SSL_VERIFYPEER, LONG, 248),\r\n\r\n  /* Set if we should verify the Common name from the proxy certificate in ssl\r\n   * handshake, set 1 to check existence, 2 to ensure that it matches\r\n   * the provided hostname. */\r\n  CINIT(PROXY_SSL_VERIFYHOST, LONG, 249),\r\n\r\n  /* What version to specifically try to use for proxy.\r\n     See CURL_SSLVERSION defines below. */\r\n  CINIT(PROXY_SSLVERSION, LONG, 250),\r\n\r\n  /* Set a username for authenticated TLS for proxy */\r\n  CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251),\r\n\r\n  /* Set a password for authenticated TLS for proxy */\r\n  CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252),\r\n\r\n  /* Set authentication type for authenticated TLS for proxy */\r\n  CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253),\r\n\r\n  /* name of the file keeping your private SSL-certificate for proxy */\r\n  CINIT(PROXY_SSLCERT, STRINGPOINT, 254),\r\n\r\n  /* type of the file keeping your SSL-certificate (\"DER\", \"PEM\", \"ENG\") for\r\n     proxy */\r\n  CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255),\r\n\r\n  /* name of the file keeping your private SSL-key for proxy */\r\n  CINIT(PROXY_SSLKEY, STRINGPOINT, 256),\r\n\r\n  /* type of the file keeping your private SSL-key (\"DER\", \"PEM\", \"ENG\") for\r\n     proxy */\r\n  CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257),\r\n\r\n  /* password for the SSL private key for proxy */\r\n  CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258),\r\n\r\n  /* Specify which SSL ciphers to use for proxy */\r\n  CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259),\r\n\r\n  /* CRL file for proxy */\r\n  CINIT(PROXY_CRLFILE, STRINGPOINT, 260),\r\n\r\n  /* Enable/disable specific SSL features with a bitmask for proxy, see\r\n     CURLSSLOPT_* */\r\n  CINIT(PROXY_SSL_OPTIONS, LONG, 261),\r\n\r\n  /* Name of pre proxy to use. */\r\n  CINIT(PRE_PROXY, STRINGPOINT, 262),\r\n\r\n  /* The public key in DER form used to validate the proxy public key\r\n     this option is used only if PROXY_SSL_VERIFYPEER is true */\r\n  CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263),\r\n\r\n  /* Path to an abstract Unix domain socket */\r\n  CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264),\r\n\r\n  /* Suppress proxy CONNECT response headers from user callbacks */\r\n  CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265),\r\n\r\n  /* The request target, instead of extracted from the URL */\r\n  CINIT(REQUEST_TARGET, STRINGPOINT, 266),\r\n\r\n  /* bitmask of allowed auth methods for connections to SOCKS5 proxies */\r\n  CINIT(SOCKS5_AUTH, LONG, 267),\r\n\r\n  /* Enable/disable SSH compression */\r\n  CINIT(SSH_COMPRESSION, LONG, 268),\r\n\r\n  /* Post MIME data. */\r\n  CINIT(MIMEPOST, OBJECTPOINT, 269),\r\n\r\n  /* Time to use with the CURLOPT_TIMECONDITION. Specified in number of\r\n     seconds since 1 Jan 1970. */\r\n  CINIT(TIMEVALUE_LARGE, OFF_T, 270),\r\n\r\n  /* Head start in milliseconds to give happy eyeballs. */\r\n  CINIT(HAPPY_EYEBALLS_TIMEOUT_MS, LONG, 271),\r\n\r\n  /* Function that will be called before a resolver request is made */\r\n  CINIT(RESOLVER_START_FUNCTION, FUNCTIONPOINT, 272),\r\n\r\n  /* User data to pass to the resolver start callback. */\r\n  CINIT(RESOLVER_START_DATA, OBJECTPOINT, 273),\r\n\r\n  /* send HAProxy PROXY protocol header? */\r\n  CINIT(HAPROXYPROTOCOL, LONG, 274),\r\n\r\n  /* shuffle addresses before use when DNS returns multiple */\r\n  CINIT(DNS_SHUFFLE_ADDRESSES, LONG, 275),\r\n\r\n  /* Specify which TLS 1.3 ciphers suites to use */\r\n  CINIT(TLS13_CIPHERS, STRINGPOINT, 276),\r\n  CINIT(PROXY_TLS13_CIPHERS, STRINGPOINT, 277),\r\n\r\n  /* Disallow specifying username/login in URL. */\r\n  CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278),\r\n\r\n  /* DNS-over-HTTPS URL */\r\n  CINIT(DOH_URL, STRINGPOINT, 279),\r\n\r\n  /* Preferred buffer size to use for uploads */\r\n  CINIT(UPLOAD_BUFFERSIZE, LONG, 280),\r\n\r\n  /* Time in ms between connection upkeep calls for long-lived connections. */\r\n  CINIT(UPKEEP_INTERVAL_MS, LONG, 281),\r\n\r\n  /* Specify URL using CURL URL API. */\r\n  CINIT(CURLU, OBJECTPOINT, 282),\r\n\r\n  /* add trailing data just after no more data is available */\r\n  CINIT(TRAILERFUNCTION, FUNCTIONPOINT, 283),\r\n\r\n  /* pointer to be passed to HTTP_TRAILER_FUNCTION */\r\n  CINIT(TRAILERDATA, OBJECTPOINT, 284),\r\n\r\n  /* set this to 1L to allow HTTP/0.9 responses or 0L to disallow */\r\n  CINIT(HTTP09_ALLOWED, LONG, 285),\r\n\r\n  /* alt-svc control bitmask */\r\n  CINIT(ALTSVC_CTRL, LONG, 286),\r\n\r\n  /* alt-svc cache file name to possibly read from/write to */\r\n  CINIT(ALTSVC, STRINGPOINT, 287),\r\n\r\n  CURLOPT_LASTENTRY /* the last unused */\r\n} CURLoption;\r\n\r\n#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all\r\n                          the obsolete stuff removed! */\r\n\r\n/* Backwards compatibility with older names */\r\n/* These are scheduled to disappear by 2011 */\r\n\r\n/* This was added in version 7.19.1 */\r\n#define CURLOPT_POST301 CURLOPT_POSTREDIR\r\n\r\n/* These are scheduled to disappear by 2009 */\r\n\r\n/* The following were added in 7.17.0 */\r\n#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD\r\n#define CURLOPT_FTPAPPEND CURLOPT_APPEND\r\n#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY\r\n#define CURLOPT_FTP_SSL CURLOPT_USE_SSL\r\n\r\n/* The following were added earlier */\r\n\r\n#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD\r\n#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL\r\n\r\n#else\r\n/* This is set if CURL_NO_OLDIES is defined at compile-time */\r\n#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */\r\n#endif\r\n\r\n\r\n  /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host\r\n     name resolves addresses using more than one IP protocol version, this\r\n     option might be handy to force libcurl to use a specific IP version. */\r\n#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP\r\n                                     versions that your system allows */\r\n#define CURL_IPRESOLVE_V4       1 /* resolve to IPv4 addresses */\r\n#define CURL_IPRESOLVE_V6       2 /* resolve to IPv6 addresses */\r\n\r\n  /* three convenient \"aliases\" that follow the name scheme better */\r\n#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER\r\n\r\n  /* These enums are for use with the CURLOPT_HTTP_VERSION option. */\r\nenum {\r\n  CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd\r\n                             like the library to choose the best possible\r\n                             for us! */\r\n  CURL_HTTP_VERSION_1_0,  /* please use HTTP 1.0 in the request */\r\n  CURL_HTTP_VERSION_1_1,  /* please use HTTP 1.1 in the request */\r\n  CURL_HTTP_VERSION_2_0,  /* please use HTTP 2 in the request */\r\n  CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */\r\n  CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE,  /* please use HTTP 2 without HTTP/1.1\r\n                                           Upgrade */\r\n\r\n  CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */\r\n};\r\n\r\n/* Convenience definition simple because the name of the version is HTTP/2 and\r\n   not 2.0. The 2_0 version of the enum name was set while the version was\r\n   still planned to be 2.0 and we stick to it for compatibility. */\r\n#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0\r\n\r\n/*\r\n * Public API enums for RTSP requests\r\n */\r\nenum {\r\n    CURL_RTSPREQ_NONE, /* first in list */\r\n    CURL_RTSPREQ_OPTIONS,\r\n    CURL_RTSPREQ_DESCRIBE,\r\n    CURL_RTSPREQ_ANNOUNCE,\r\n    CURL_RTSPREQ_SETUP,\r\n    CURL_RTSPREQ_PLAY,\r\n    CURL_RTSPREQ_PAUSE,\r\n    CURL_RTSPREQ_TEARDOWN,\r\n    CURL_RTSPREQ_GET_PARAMETER,\r\n    CURL_RTSPREQ_SET_PARAMETER,\r\n    CURL_RTSPREQ_RECORD,\r\n    CURL_RTSPREQ_RECEIVE,\r\n    CURL_RTSPREQ_LAST /* last in list */\r\n};\r\n\r\n  /* These enums are for use with the CURLOPT_NETRC option. */\r\nenum CURL_NETRC_OPTION {\r\n  CURL_NETRC_IGNORED,     /* The .netrc will never be read.\r\n                           * This is the default. */\r\n  CURL_NETRC_OPTIONAL,    /* A user:password in the URL will be preferred\r\n                           * to one in the .netrc. */\r\n  CURL_NETRC_REQUIRED,    /* A user:password in the URL will be ignored.\r\n                           * Unless one is set programmatically, the .netrc\r\n                           * will be queried. */\r\n  CURL_NETRC_LAST\r\n};\r\n\r\nenum {\r\n  CURL_SSLVERSION_DEFAULT,\r\n  CURL_SSLVERSION_TLSv1, /* TLS 1.x */\r\n  CURL_SSLVERSION_SSLv2,\r\n  CURL_SSLVERSION_SSLv3,\r\n  CURL_SSLVERSION_TLSv1_0,\r\n  CURL_SSLVERSION_TLSv1_1,\r\n  CURL_SSLVERSION_TLSv1_2,\r\n  CURL_SSLVERSION_TLSv1_3,\r\n\r\n  CURL_SSLVERSION_LAST /* never use, keep last */\r\n};\r\n\r\nenum {\r\n  CURL_SSLVERSION_MAX_NONE =     0,\r\n  CURL_SSLVERSION_MAX_DEFAULT =  (CURL_SSLVERSION_TLSv1   << 16),\r\n  CURL_SSLVERSION_MAX_TLSv1_0 =  (CURL_SSLVERSION_TLSv1_0 << 16),\r\n  CURL_SSLVERSION_MAX_TLSv1_1 =  (CURL_SSLVERSION_TLSv1_1 << 16),\r\n  CURL_SSLVERSION_MAX_TLSv1_2 =  (CURL_SSLVERSION_TLSv1_2 << 16),\r\n  CURL_SSLVERSION_MAX_TLSv1_3 =  (CURL_SSLVERSION_TLSv1_3 << 16),\r\n\r\n  /* never use, keep last */\r\n  CURL_SSLVERSION_MAX_LAST =     (CURL_SSLVERSION_LAST    << 16)\r\n};\r\n\r\nenum CURL_TLSAUTH {\r\n  CURL_TLSAUTH_NONE,\r\n  CURL_TLSAUTH_SRP,\r\n  CURL_TLSAUTH_LAST /* never use, keep last */\r\n};\r\n\r\n/* symbols to use with CURLOPT_POSTREDIR.\r\n   CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303\r\n   can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302\r\n   | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */\r\n\r\n#define CURL_REDIR_GET_ALL  0\r\n#define CURL_REDIR_POST_301 1\r\n#define CURL_REDIR_POST_302 2\r\n#define CURL_REDIR_POST_303 4\r\n#define CURL_REDIR_POST_ALL \\\r\n    (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303)\r\n\r\ntypedef enum {\r\n  CURL_TIMECOND_NONE,\r\n\r\n  CURL_TIMECOND_IFMODSINCE,\r\n  CURL_TIMECOND_IFUNMODSINCE,\r\n  CURL_TIMECOND_LASTMOD,\r\n\r\n  CURL_TIMECOND_LAST\r\n} curl_TimeCond;\r\n\r\n/* Special size_t value signaling a zero-terminated string. */\r\n#define CURL_ZERO_TERMINATED ((size_t) -1)\r\n\r\n/* curl_strequal() and curl_strnequal() are subject for removal in a future\r\n   release */\r\nCURL_EXTERN int curl_strequal(const char *s1, const char *s2);\r\nCURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n);\r\n\r\n/* Mime/form handling support. */\r\ntypedef struct curl_mime_s      curl_mime;      /* Mime context. */\r\ntypedef struct curl_mimepart_s  curl_mimepart;  /* Mime part context. */\r\n\r\n/*\r\n * NAME curl_mime_init()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Create a mime context and return its handle. The easy parameter is the\r\n * target handle.\r\n */\r\nCURL_EXTERN curl_mime *curl_mime_init(CURL *easy);\r\n\r\n/*\r\n * NAME curl_mime_free()\r\n *\r\n * DESCRIPTION\r\n *\r\n * release a mime handle and its substructures.\r\n */\r\nCURL_EXTERN void curl_mime_free(curl_mime *mime);\r\n\r\n/*\r\n * NAME curl_mime_addpart()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Append a new empty part to the given mime context and return a handle to\r\n * the created part.\r\n */\r\nCURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime);\r\n\r\n/*\r\n * NAME curl_mime_name()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime/form part name.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name);\r\n\r\n/*\r\n * NAME curl_mime_filename()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part remote file name.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part,\r\n                                        const char *filename);\r\n\r\n/*\r\n * NAME curl_mime_type()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part type.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype);\r\n\r\n/*\r\n * NAME curl_mime_encoder()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime data transfer encoder.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part,\r\n                                       const char *encoding);\r\n\r\n/*\r\n * NAME curl_mime_data()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part data source from memory data,\r\n */\r\nCURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part,\r\n                                    const char *data, size_t datasize);\r\n\r\n/*\r\n * NAME curl_mime_filedata()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part data source from named file.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part,\r\n                                        const char *filename);\r\n\r\n/*\r\n * NAME curl_mime_data_cb()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part data source from callback function.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part,\r\n                                       curl_off_t datasize,\r\n                                       curl_read_callback readfunc,\r\n                                       curl_seek_callback seekfunc,\r\n                                       curl_free_callback freefunc,\r\n                                       void *arg);\r\n\r\n/*\r\n * NAME curl_mime_subparts()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part data source from subparts.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part,\r\n                                        curl_mime *subparts);\r\n/*\r\n * NAME curl_mime_headers()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Set mime part headers.\r\n */\r\nCURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part,\r\n                                       struct curl_slist *headers,\r\n                                       int take_ownership);\r\n\r\n/* Old form API. */\r\n/* name is uppercase CURLFORM_<name> */\r\n#ifdef CFINIT\r\n#undef CFINIT\r\n#endif\r\n\r\n#ifdef CURL_ISOCPP\r\n#define CFINIT(name) CURLFORM_ ## name\r\n#else\r\n/* The macro \"##\" is ISO C, we assume pre-ISO C doesn't support it. */\r\n#define CFINIT(name) CURLFORM_/**/name\r\n#endif\r\n\r\ntypedef enum {\r\n  CFINIT(NOTHING),        /********* the first one is unused ************/\r\n\r\n  /*  */\r\n  CFINIT(COPYNAME),\r\n  CFINIT(PTRNAME),\r\n  CFINIT(NAMELENGTH),\r\n  CFINIT(COPYCONTENTS),\r\n  CFINIT(PTRCONTENTS),\r\n  CFINIT(CONTENTSLENGTH),\r\n  CFINIT(FILECONTENT),\r\n  CFINIT(ARRAY),\r\n  CFINIT(OBSOLETE),\r\n  CFINIT(FILE),\r\n\r\n  CFINIT(BUFFER),\r\n  CFINIT(BUFFERPTR),\r\n  CFINIT(BUFFERLENGTH),\r\n\r\n  CFINIT(CONTENTTYPE),\r\n  CFINIT(CONTENTHEADER),\r\n  CFINIT(FILENAME),\r\n  CFINIT(END),\r\n  CFINIT(OBSOLETE2),\r\n\r\n  CFINIT(STREAM),\r\n  CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */\r\n\r\n  CURLFORM_LASTENTRY /* the last unused */\r\n} CURLformoption;\r\n\r\n#undef CFINIT /* done */\r\n\r\n/* structure to be used as parameter for CURLFORM_ARRAY */\r\nstruct curl_forms {\r\n  CURLformoption option;\r\n  const char     *value;\r\n};\r\n\r\n/* use this for multipart formpost building */\r\n/* Returns code for curl_formadd()\r\n *\r\n * Returns:\r\n * CURL_FORMADD_OK             on success\r\n * CURL_FORMADD_MEMORY         if the FormInfo allocation fails\r\n * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form\r\n * CURL_FORMADD_NULL           if a null pointer was given for a char\r\n * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed\r\n * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used\r\n * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or error)\r\n * CURL_FORMADD_MEMORY         if a curl_httppost struct cannot be allocated\r\n * CURL_FORMADD_MEMORY         if some allocation for string copying failed.\r\n * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array\r\n *\r\n ***************************************************************************/\r\ntypedef enum {\r\n  CURL_FORMADD_OK, /* first, no error */\r\n\r\n  CURL_FORMADD_MEMORY,\r\n  CURL_FORMADD_OPTION_TWICE,\r\n  CURL_FORMADD_NULL,\r\n  CURL_FORMADD_UNKNOWN_OPTION,\r\n  CURL_FORMADD_INCOMPLETE,\r\n  CURL_FORMADD_ILLEGAL_ARRAY,\r\n  CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */\r\n\r\n  CURL_FORMADD_LAST /* last */\r\n} CURLFORMcode;\r\n\r\n/*\r\n * NAME curl_formadd()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Pretty advanced function for building multi-part formposts. Each invoke\r\n * adds one part that together construct a full post. Then use\r\n * CURLOPT_HTTPPOST to send it off to libcurl.\r\n */\r\nCURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost,\r\n                                      struct curl_httppost **last_post,\r\n                                      ...);\r\n\r\n/*\r\n * callback function for curl_formget()\r\n * The void *arg pointer will be the one passed as second argument to\r\n *   curl_formget().\r\n * The character buffer passed to it must not be freed.\r\n * Should return the buffer length passed to it as the argument \"len\" on\r\n *   success.\r\n */\r\ntypedef size_t (*curl_formget_callback)(void *arg, const char *buf,\r\n                                        size_t len);\r\n\r\n/*\r\n * NAME curl_formget()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Serialize a curl_httppost struct built with curl_formadd().\r\n * Accepts a void pointer as second argument which will be passed to\r\n * the curl_formget_callback function.\r\n * Returns 0 on success.\r\n */\r\nCURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg,\r\n                             curl_formget_callback append);\r\n/*\r\n * NAME curl_formfree()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Free a multipart formpost previously built with curl_formadd().\r\n */\r\nCURL_EXTERN void curl_formfree(struct curl_httppost *form);\r\n\r\n/*\r\n * NAME curl_getenv()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Returns a malloc()'ed string that MUST be curl_free()ed after usage is\r\n * complete. DEPRECATED - see lib/README.curlx\r\n */\r\nCURL_EXTERN char *curl_getenv(const char *variable);\r\n\r\n/*\r\n * NAME curl_version()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Returns a static ascii string of the libcurl version.\r\n */\r\nCURL_EXTERN char *curl_version(void);\r\n\r\n/*\r\n * NAME curl_easy_escape()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Escapes URL strings (converts all letters consider illegal in URLs to their\r\n * %XX versions). This function returns a new allocated string or NULL if an\r\n * error occurred.\r\n */\r\nCURL_EXTERN char *curl_easy_escape(CURL *handle,\r\n                                   const char *string,\r\n                                   int length);\r\n\r\n/* the previous version: */\r\nCURL_EXTERN char *curl_escape(const char *string,\r\n                              int length);\r\n\r\n\r\n/*\r\n * NAME curl_easy_unescape()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Unescapes URL encoding in strings (converts all %XX codes to their 8bit\r\n * versions). This function returns a new allocated string or NULL if an error\r\n * occurred.\r\n * Conversion Note: On non-ASCII platforms the ASCII %XX codes are\r\n * converted into the host encoding.\r\n */\r\nCURL_EXTERN char *curl_easy_unescape(CURL *handle,\r\n                                     const char *string,\r\n                                     int length,\r\n                                     int *outlength);\r\n\r\n/* the previous version */\r\nCURL_EXTERN char *curl_unescape(const char *string,\r\n                                int length);\r\n\r\n/*\r\n * NAME curl_free()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Provided for de-allocation in the same translation unit that did the\r\n * allocation. Added in libcurl 7.10\r\n */\r\nCURL_EXTERN void curl_free(void *p);\r\n\r\n/*\r\n * NAME curl_global_init()\r\n *\r\n * DESCRIPTION\r\n *\r\n * curl_global_init() should be invoked exactly once for each application that\r\n * uses libcurl and before any call of other libcurl functions.\r\n *\r\n * This function is not thread-safe!\r\n */\r\nCURL_EXTERN CURLcode curl_global_init(long flags);\r\n\r\n/*\r\n * NAME curl_global_init_mem()\r\n *\r\n * DESCRIPTION\r\n *\r\n * curl_global_init() or curl_global_init_mem() should be invoked exactly once\r\n * for each application that uses libcurl.  This function can be used to\r\n * initialize libcurl and set user defined memory management callback\r\n * functions.  Users can implement memory management routines to check for\r\n * memory leaks, check for mis-use of the curl library etc.  User registered\r\n * callback routines with be invoked by this library instead of the system\r\n * memory management routines like malloc, free etc.\r\n */\r\nCURL_EXTERN CURLcode curl_global_init_mem(long flags,\r\n                                          curl_malloc_callback m,\r\n                                          curl_free_callback f,\r\n                                          curl_realloc_callback r,\r\n                                          curl_strdup_callback s,\r\n                                          curl_calloc_callback c);\r\n\r\n/*\r\n * NAME curl_global_cleanup()\r\n *\r\n * DESCRIPTION\r\n *\r\n * curl_global_cleanup() should be invoked exactly once for each application\r\n * that uses libcurl\r\n */\r\nCURL_EXTERN void curl_global_cleanup(void);\r\n\r\n/* linked-list structure for the CURLOPT_QUOTE option (and other) */\r\nstruct curl_slist {\r\n  char *data;\r\n  struct curl_slist *next;\r\n};\r\n\r\n/*\r\n * NAME curl_global_sslset()\r\n *\r\n * DESCRIPTION\r\n *\r\n * When built with multiple SSL backends, curl_global_sslset() allows to\r\n * choose one. This function can only be called once, and it must be called\r\n * *before* curl_global_init().\r\n *\r\n * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The\r\n * backend can also be specified via the name parameter (passing -1 as id).\r\n * If both id and name are specified, the name will be ignored. If neither id\r\n * nor name are specified, the function will fail with\r\n * CURLSSLSET_UNKNOWN_BACKEND and set the \"avail\" pointer to the\r\n * NULL-terminated list of available backends.\r\n *\r\n * Upon success, the function returns CURLSSLSET_OK.\r\n *\r\n * If the specified SSL backend is not available, the function returns\r\n * CURLSSLSET_UNKNOWN_BACKEND and sets the \"avail\" pointer to a NULL-terminated\r\n * list of available SSL backends.\r\n *\r\n * The SSL backend can be set only once. If it has already been set, a\r\n * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE.\r\n */\r\n\r\ntypedef struct {\r\n  curl_sslbackend id;\r\n  const char *name;\r\n} curl_ssl_backend;\r\n\r\ntypedef enum {\r\n  CURLSSLSET_OK = 0,\r\n  CURLSSLSET_UNKNOWN_BACKEND,\r\n  CURLSSLSET_TOO_LATE,\r\n  CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */\r\n} CURLsslset;\r\n\r\nCURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,\r\n                                          const curl_ssl_backend ***avail);\r\n\r\n/*\r\n * NAME curl_slist_append()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Appends a string to a linked list. If no list exists, it will be created\r\n * first. Returns the new list, after appending.\r\n */\r\nCURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *,\r\n                                                 const char *);\r\n\r\n/*\r\n * NAME curl_slist_free_all()\r\n *\r\n * DESCRIPTION\r\n *\r\n * free a previously built curl_slist.\r\n */\r\nCURL_EXTERN void curl_slist_free_all(struct curl_slist *);\r\n\r\n/*\r\n * NAME curl_getdate()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Returns the time, in seconds since 1 Jan 1970 of the time string given in\r\n * the first argument. The time argument in the second parameter is unused\r\n * and should be set to NULL.\r\n */\r\nCURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused);\r\n\r\n/* info about the certificate chain, only for OpenSSL builds. Asked\r\n   for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */\r\nstruct curl_certinfo {\r\n  int num_of_certs;             /* number of certificates with information */\r\n  struct curl_slist **certinfo; /* for each index in this array, there's a\r\n                                   linked list with textual information in the\r\n                                   format \"name: value\" */\r\n};\r\n\r\n/* Information about the SSL library used and the respective internal SSL\r\n   handle, which can be used to obtain further information regarding the\r\n   connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */\r\nstruct curl_tlssessioninfo {\r\n  curl_sslbackend backend;\r\n  void *internals;\r\n};\r\n\r\n#define CURLINFO_STRING   0x100000\r\n#define CURLINFO_LONG     0x200000\r\n#define CURLINFO_DOUBLE   0x300000\r\n#define CURLINFO_SLIST    0x400000\r\n#define CURLINFO_PTR      0x400000 /* same as SLIST */\r\n#define CURLINFO_SOCKET   0x500000\r\n#define CURLINFO_OFF_T    0x600000\r\n#define CURLINFO_MASK     0x0fffff\r\n#define CURLINFO_TYPEMASK 0xf00000\r\n\r\ntypedef enum {\r\n  CURLINFO_NONE, /* first, never use this */\r\n  CURLINFO_EFFECTIVE_URL    = CURLINFO_STRING + 1,\r\n  CURLINFO_RESPONSE_CODE    = CURLINFO_LONG   + 2,\r\n  CURLINFO_TOTAL_TIME       = CURLINFO_DOUBLE + 3,\r\n  CURLINFO_NAMELOOKUP_TIME  = CURLINFO_DOUBLE + 4,\r\n  CURLINFO_CONNECT_TIME     = CURLINFO_DOUBLE + 5,\r\n  CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,\r\n  CURLINFO_SIZE_UPLOAD      = CURLINFO_DOUBLE + 7,\r\n  CURLINFO_SIZE_UPLOAD_T    = CURLINFO_OFF_T  + 7,\r\n  CURLINFO_SIZE_DOWNLOAD    = CURLINFO_DOUBLE + 8,\r\n  CURLINFO_SIZE_DOWNLOAD_T  = CURLINFO_OFF_T  + 8,\r\n  CURLINFO_SPEED_DOWNLOAD   = CURLINFO_DOUBLE + 9,\r\n  CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T  + 9,\r\n  CURLINFO_SPEED_UPLOAD     = CURLINFO_DOUBLE + 10,\r\n  CURLINFO_SPEED_UPLOAD_T   = CURLINFO_OFF_T  + 10,\r\n  CURLINFO_HEADER_SIZE      = CURLINFO_LONG   + 11,\r\n  CURLINFO_REQUEST_SIZE     = CURLINFO_LONG   + 12,\r\n  CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG   + 13,\r\n  CURLINFO_FILETIME         = CURLINFO_LONG   + 14,\r\n  CURLINFO_FILETIME_T       = CURLINFO_OFF_T  + 14,\r\n  CURLINFO_CONTENT_LENGTH_DOWNLOAD   = CURLINFO_DOUBLE + 15,\r\n  CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T  + 15,\r\n  CURLINFO_CONTENT_LENGTH_UPLOAD     = CURLINFO_DOUBLE + 16,\r\n  CURLINFO_CONTENT_LENGTH_UPLOAD_T   = CURLINFO_OFF_T  + 16,\r\n  CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,\r\n  CURLINFO_CONTENT_TYPE     = CURLINFO_STRING + 18,\r\n  CURLINFO_REDIRECT_TIME    = CURLINFO_DOUBLE + 19,\r\n  CURLINFO_REDIRECT_COUNT   = CURLINFO_LONG   + 20,\r\n  CURLINFO_PRIVATE          = CURLINFO_STRING + 21,\r\n  CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG   + 22,\r\n  CURLINFO_HTTPAUTH_AVAIL   = CURLINFO_LONG   + 23,\r\n  CURLINFO_PROXYAUTH_AVAIL  = CURLINFO_LONG   + 24,\r\n  CURLINFO_OS_ERRNO         = CURLINFO_LONG   + 25,\r\n  CURLINFO_NUM_CONNECTS     = CURLINFO_LONG   + 26,\r\n  CURLINFO_SSL_ENGINES      = CURLINFO_SLIST  + 27,\r\n  CURLINFO_COOKIELIST       = CURLINFO_SLIST  + 28,\r\n  CURLINFO_LASTSOCKET       = CURLINFO_LONG   + 29,\r\n  CURLINFO_FTP_ENTRY_PATH   = CURLINFO_STRING + 30,\r\n  CURLINFO_REDIRECT_URL     = CURLINFO_STRING + 31,\r\n  CURLINFO_PRIMARY_IP       = CURLINFO_STRING + 32,\r\n  CURLINFO_APPCONNECT_TIME  = CURLINFO_DOUBLE + 33,\r\n  CURLINFO_CERTINFO         = CURLINFO_PTR    + 34,\r\n  CURLINFO_CONDITION_UNMET  = CURLINFO_LONG   + 35,\r\n  CURLINFO_RTSP_SESSION_ID  = CURLINFO_STRING + 36,\r\n  CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG   + 37,\r\n  CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG   + 38,\r\n  CURLINFO_RTSP_CSEQ_RECV   = CURLINFO_LONG   + 39,\r\n  CURLINFO_PRIMARY_PORT     = CURLINFO_LONG   + 40,\r\n  CURLINFO_LOCAL_IP         = CURLINFO_STRING + 41,\r\n  CURLINFO_LOCAL_PORT       = CURLINFO_LONG   + 42,\r\n  CURLINFO_TLS_SESSION      = CURLINFO_PTR    + 43,\r\n  CURLINFO_ACTIVESOCKET     = CURLINFO_SOCKET + 44,\r\n  CURLINFO_TLS_SSL_PTR      = CURLINFO_PTR    + 45,\r\n  CURLINFO_HTTP_VERSION     = CURLINFO_LONG   + 46,\r\n  CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47,\r\n  CURLINFO_PROTOCOL         = CURLINFO_LONG   + 48,\r\n  CURLINFO_SCHEME           = CURLINFO_STRING + 49,\r\n  /* Fill in new entries below here! */\r\n\r\n  /* Preferably these would be defined conditionally based on the\r\n     sizeof curl_off_t being 64-bits */\r\n  CURLINFO_TOTAL_TIME_T     = CURLINFO_OFF_T + 50,\r\n  CURLINFO_NAMELOOKUP_TIME_T = CURLINFO_OFF_T + 51,\r\n  CURLINFO_CONNECT_TIME_T   = CURLINFO_OFF_T + 52,\r\n  CURLINFO_PRETRANSFER_TIME_T = CURLINFO_OFF_T + 53,\r\n  CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54,\r\n  CURLINFO_REDIRECT_TIME_T  = CURLINFO_OFF_T + 55,\r\n  CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56,\r\n\r\n  CURLINFO_LASTONE          = 56\r\n} CURLINFO;\r\n\r\n/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as\r\n   CURLINFO_HTTP_CODE */\r\n#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE\r\n\r\ntypedef enum {\r\n  CURLCLOSEPOLICY_NONE, /* first, never use this */\r\n\r\n  CURLCLOSEPOLICY_OLDEST,\r\n  CURLCLOSEPOLICY_LEAST_RECENTLY_USED,\r\n  CURLCLOSEPOLICY_LEAST_TRAFFIC,\r\n  CURLCLOSEPOLICY_SLOWEST,\r\n  CURLCLOSEPOLICY_CALLBACK,\r\n\r\n  CURLCLOSEPOLICY_LAST /* last, never use this */\r\n} curl_closepolicy;\r\n\r\n#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */\r\n#define CURL_GLOBAL_WIN32 (1<<1)\r\n#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)\r\n#define CURL_GLOBAL_NOTHING 0\r\n#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL\r\n#define CURL_GLOBAL_ACK_EINTR (1<<2)\r\n\r\n\r\n/*****************************************************************************\r\n * Setup defines, protos etc for the sharing stuff.\r\n */\r\n\r\n/* Different data locks for a single share */\r\ntypedef enum {\r\n  CURL_LOCK_DATA_NONE = 0,\r\n  /*  CURL_LOCK_DATA_SHARE is used internally to say that\r\n   *  the locking is just made to change the internal state of the share\r\n   *  itself.\r\n   */\r\n  CURL_LOCK_DATA_SHARE,\r\n  CURL_LOCK_DATA_COOKIE,\r\n  CURL_LOCK_DATA_DNS,\r\n  CURL_LOCK_DATA_SSL_SESSION,\r\n  CURL_LOCK_DATA_CONNECT,\r\n  CURL_LOCK_DATA_PSL,\r\n  CURL_LOCK_DATA_LAST\r\n} curl_lock_data;\r\n\r\n/* Different lock access types */\r\ntypedef enum {\r\n  CURL_LOCK_ACCESS_NONE = 0,   /* unspecified action */\r\n  CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */\r\n  CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */\r\n  CURL_LOCK_ACCESS_LAST        /* never use */\r\n} curl_lock_access;\r\n\r\ntypedef void (*curl_lock_function)(CURL *handle,\r\n                                   curl_lock_data data,\r\n                                   curl_lock_access locktype,\r\n                                   void *userptr);\r\ntypedef void (*curl_unlock_function)(CURL *handle,\r\n                                     curl_lock_data data,\r\n                                     void *userptr);\r\n\r\n\r\ntypedef enum {\r\n  CURLSHE_OK,  /* all is fine */\r\n  CURLSHE_BAD_OPTION, /* 1 */\r\n  CURLSHE_IN_USE,     /* 2 */\r\n  CURLSHE_INVALID,    /* 3 */\r\n  CURLSHE_NOMEM,      /* 4 out of memory */\r\n  CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */\r\n  CURLSHE_LAST        /* never use */\r\n} CURLSHcode;\r\n\r\ntypedef enum {\r\n  CURLSHOPT_NONE,  /* don't use */\r\n  CURLSHOPT_SHARE,   /* specify a data type to share */\r\n  CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */\r\n  CURLSHOPT_LOCKFUNC,   /* pass in a 'curl_lock_function' pointer */\r\n  CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */\r\n  CURLSHOPT_USERDATA,   /* pass in a user data pointer used in the lock/unlock\r\n                           callback functions */\r\n  CURLSHOPT_LAST  /* never use */\r\n} CURLSHoption;\r\n\r\nCURL_EXTERN CURLSH *curl_share_init(void);\r\nCURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...);\r\nCURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *);\r\n\r\n/****************************************************************************\r\n * Structures for querying information about the curl library at runtime.\r\n */\r\n\r\ntypedef enum {\r\n  CURLVERSION_FIRST,\r\n  CURLVERSION_SECOND,\r\n  CURLVERSION_THIRD,\r\n  CURLVERSION_FOURTH,\r\n  CURLVERSION_FIFTH,\r\n  CURLVERSION_LAST /* never actually use this */\r\n} CURLversion;\r\n\r\n/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by\r\n   basically all programs ever that want to get version information. It is\r\n   meant to be a built-in version number for what kind of struct the caller\r\n   expects. If the struct ever changes, we redefine the NOW to another enum\r\n   from above. */\r\n#define CURLVERSION_NOW CURLVERSION_FIFTH\r\n\r\ntypedef struct {\r\n  CURLversion age;          /* age of the returned struct */\r\n  const char *version;      /* LIBCURL_VERSION */\r\n  unsigned int version_num; /* LIBCURL_VERSION_NUM */\r\n  const char *host;         /* OS/host/cpu/machine when configured */\r\n  int features;             /* bitmask, see defines below */\r\n  const char *ssl_version;  /* human readable string */\r\n  long ssl_version_num;     /* not used anymore, always 0 */\r\n  const char *libz_version; /* human readable string */\r\n  /* protocols is terminated by an entry with a NULL protoname */\r\n  const char * const *protocols;\r\n\r\n  /* The fields below this were added in CURLVERSION_SECOND */\r\n  const char *ares;\r\n  int ares_num;\r\n\r\n  /* This field was added in CURLVERSION_THIRD */\r\n  const char *libidn;\r\n\r\n  /* These field were added in CURLVERSION_FOURTH */\r\n\r\n  /* Same as '_libiconv_version' if built with HAVE_ICONV */\r\n  int iconv_ver_num;\r\n\r\n  const char *libssh_version; /* human readable string */\r\n\r\n  /* These fields were added in CURLVERSION_FIFTH */\r\n\r\n  unsigned int brotli_ver_num; /* Numeric Brotli version\r\n                                  (MAJOR << 24) | (MINOR << 12) | PATCH */\r\n  const char *brotli_version; /* human readable string. */\r\n\r\n} curl_version_info_data;\r\n\r\n#define CURL_VERSION_IPV6         (1<<0)  /* IPv6-enabled */\r\n#define CURL_VERSION_KERBEROS4    (1<<1)  /* Kerberos V4 auth is supported\r\n                                             (deprecated) */\r\n#define CURL_VERSION_SSL          (1<<2)  /* SSL options are present */\r\n#define CURL_VERSION_LIBZ         (1<<3)  /* libz features are present */\r\n#define CURL_VERSION_NTLM         (1<<4)  /* NTLM auth is supported */\r\n#define CURL_VERSION_GSSNEGOTIATE (1<<5)  /* Negotiate auth is supported\r\n                                             (deprecated) */\r\n#define CURL_VERSION_DEBUG        (1<<6)  /* Built with debug capabilities */\r\n#define CURL_VERSION_ASYNCHDNS    (1<<7)  /* Asynchronous DNS resolves */\r\n#define CURL_VERSION_SPNEGO       (1<<8)  /* SPNEGO auth is supported */\r\n#define CURL_VERSION_LARGEFILE    (1<<9)  /* Supports files larger than 2GB */\r\n#define CURL_VERSION_IDN          (1<<10) /* Internationized Domain Names are\r\n                                             supported */\r\n#define CURL_VERSION_SSPI         (1<<11) /* Built against Windows SSPI */\r\n#define CURL_VERSION_CONV         (1<<12) /* Character conversions supported */\r\n#define CURL_VERSION_CURLDEBUG    (1<<13) /* Debug memory tracking supported */\r\n#define CURL_VERSION_TLSAUTH_SRP  (1<<14) /* TLS-SRP auth is supported */\r\n#define CURL_VERSION_NTLM_WB      (1<<15) /* NTLM delegation to winbind helper\r\n                                             is supported */\r\n#define CURL_VERSION_HTTP2        (1<<16) /* HTTP2 support built-in */\r\n#define CURL_VERSION_GSSAPI       (1<<17) /* Built against a GSS-API library */\r\n#define CURL_VERSION_KERBEROS5    (1<<18) /* Kerberos V5 auth is supported */\r\n#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */\r\n#define CURL_VERSION_PSL          (1<<20) /* Mozilla's Public Suffix List, used\r\n                                             for cookie domain verification */\r\n#define CURL_VERSION_HTTPS_PROXY  (1<<21) /* HTTPS-proxy support built-in */\r\n#define CURL_VERSION_MULTI_SSL    (1<<22) /* Multiple SSL backends available */\r\n#define CURL_VERSION_BROTLI       (1<<23) /* Brotli features are present. */\r\n#define CURL_VERSION_ALTSVC       (1<<24) /* Alt-Svc handling built-in */\r\n\r\n /*\r\n * NAME curl_version_info()\r\n *\r\n * DESCRIPTION\r\n *\r\n * This function returns a pointer to a static copy of the version info\r\n * struct. See above.\r\n */\r\nCURL_EXTERN curl_version_info_data *curl_version_info(CURLversion);\r\n\r\n/*\r\n * NAME curl_easy_strerror()\r\n *\r\n * DESCRIPTION\r\n *\r\n * The curl_easy_strerror function may be used to turn a CURLcode value\r\n * into the equivalent human readable error string.  This is useful\r\n * for printing meaningful error messages.\r\n */\r\nCURL_EXTERN const char *curl_easy_strerror(CURLcode);\r\n\r\n/*\r\n * NAME curl_share_strerror()\r\n *\r\n * DESCRIPTION\r\n *\r\n * The curl_share_strerror function may be used to turn a CURLSHcode value\r\n * into the equivalent human readable error string.  This is useful\r\n * for printing meaningful error messages.\r\n */\r\nCURL_EXTERN const char *curl_share_strerror(CURLSHcode);\r\n\r\n/*\r\n * NAME curl_easy_pause()\r\n *\r\n * DESCRIPTION\r\n *\r\n * The curl_easy_pause function pauses or unpauses transfers. Select the new\r\n * state by setting the bitmask, use the convenience defines below.\r\n *\r\n */\r\nCURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);\r\n\r\n#define CURLPAUSE_RECV      (1<<0)\r\n#define CURLPAUSE_RECV_CONT (0)\r\n\r\n#define CURLPAUSE_SEND      (1<<2)\r\n#define CURLPAUSE_SEND_CONT (0)\r\n\r\n#define CURLPAUSE_ALL       (CURLPAUSE_RECV|CURLPAUSE_SEND)\r\n#define CURLPAUSE_CONT      (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT)\r\n\r\n#ifdef  __cplusplus\r\n}\r\n#endif\r\n\r\n/* unfortunately, the easy.h and multi.h include files need options and info\r\n  stuff before they can be included! */\r\n#include \"easy.h\" /* nothing in curl is fun without the easy stuff */\r\n#include \"multi.h\"\r\n#include \"urlapi.h\"\r\n\r\n/* the typechecker doesn't work in C++ (yet) */\r\n#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \\\r\n    ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \\\r\n    !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK)\r\n#include \"typecheck-gcc.h\"\r\n#else\r\n#if defined(__STDC__) && (__STDC__ >= 1)\r\n/* This preprocessor magic that replaces a call with the exact same call is\r\n   only done to make sure application authors pass exactly three arguments\r\n   to these functions. */\r\n#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)\r\n#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg)\r\n#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)\r\n#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)\r\n#endif /* __STDC__ >= 1 */\r\n#endif /* gcc >= 4.3 && !__cplusplus */\r\n\r\n#endif /* __CURL_CURL_H */\r\n"
  },
  {
    "path": "Chapter06/code/curl/curlver.h",
    "content": "#ifndef __CURL_CURLVER_H\r\n#define __CURL_CURLVER_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n/* This header file contains nothing but libcurl version info, generated by\r\n   a script at release-time. This was made its own header file in 7.11.2 */\r\n\r\n/* This is the global package copyright */\r\n#define LIBCURL_COPYRIGHT \"1996 - 2019 Daniel Stenberg, <daniel@haxx.se>.\"\r\n\r\n/* This is the version number of the libcurl package from which this header\r\n   file origins: */\r\n#define LIBCURL_VERSION \"7.64.1-DEV\"\r\n\r\n/* The numeric version number is also available \"in parts\" by using these\r\n   defines: */\r\n#define LIBCURL_VERSION_MAJOR 7\r\n#define LIBCURL_VERSION_MINOR 64\r\n#define LIBCURL_VERSION_PATCH 1\r\n\r\n/* This is the numeric version of the libcurl version number, meant for easier\r\n   parsing and comparions by programs. The LIBCURL_VERSION_NUM define will\r\n   always follow this syntax:\r\n\r\n         0xXXYYZZ\r\n\r\n   Where XX, YY and ZZ are the main version, release and patch numbers in\r\n   hexadecimal (using 8 bits each). All three numbers are always represented\r\n   using two digits.  1.2 would appear as \"0x010200\" while version 9.11.7\r\n   appears as \"0x090b07\".\r\n\r\n   This 6-digit (24 bits) hexadecimal number does not show pre-release number,\r\n   and it is always a greater number in a more recent release. It makes\r\n   comparisons with greater than and less than work.\r\n\r\n   Note: This define is the full hex number and _does not_ use the\r\n   CURL_VERSION_BITS() macro since curl's own configure script greps for it\r\n   and needs it to contain the full number.\r\n*/\r\n#define LIBCURL_VERSION_NUM 0x074001\r\n\r\n/*\r\n * This is the date and time when the full source package was created. The\r\n * timestamp is not stored in git, as the timestamp is properly set in the\r\n * tarballs by the maketgz script.\r\n *\r\n * The format of the date follows this template:\r\n *\r\n * \"2007-11-23\"\r\n */\r\n#define LIBCURL_TIMESTAMP \"[unreleased]\"\r\n\r\n#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z)\r\n#define CURL_AT_LEAST_VERSION(x,y,z) \\\r\n  (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))\r\n\r\n#endif /* __CURL_CURLVER_H */\r\n"
  },
  {
    "path": "Chapter06/code/curl/easy.h",
    "content": "#ifndef __CURL_EASY_H\r\n#define __CURL_EASY_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n#ifdef  __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\nCURL_EXTERN CURL *curl_easy_init(void);\r\nCURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);\r\nCURL_EXTERN CURLcode curl_easy_perform(CURL *curl);\r\nCURL_EXTERN void curl_easy_cleanup(CURL *curl);\r\n\r\n/*\r\n * NAME curl_easy_getinfo()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Request internal information from the curl session with this function.  The\r\n * third argument MUST be a pointer to a long, a pointer to a char * or a\r\n * pointer to a double (as the documentation describes elsewhere).  The data\r\n * pointed to will be filled in accordingly and can be relied upon only if the\r\n * function returns CURLE_OK.  This function is intended to get used *AFTER* a\r\n * performed transfer, all results from this function are undefined until the\r\n * transfer is completed.\r\n */\r\nCURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...);\r\n\r\n\r\n/*\r\n * NAME curl_easy_duphandle()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Creates a new curl session handle with the same options set for the handle\r\n * passed in. Duplicating a handle could only be a matter of cloning data and\r\n * options, internal state info and things like persistent connections cannot\r\n * be transferred. It is useful in multithreaded applications when you can run\r\n * curl_easy_duphandle() for each new thread to avoid a series of identical\r\n * curl_easy_setopt() invokes in every thread.\r\n */\r\nCURL_EXTERN CURL *curl_easy_duphandle(CURL *curl);\r\n\r\n/*\r\n * NAME curl_easy_reset()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Re-initializes a CURL handle to the default values. This puts back the\r\n * handle to the same state as it was in when it was just created.\r\n *\r\n * It does keep: live connections, the Session ID cache, the DNS cache and the\r\n * cookies.\r\n */\r\nCURL_EXTERN void curl_easy_reset(CURL *curl);\r\n\r\n/*\r\n * NAME curl_easy_recv()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Receives data from the connected socket. Use after successful\r\n * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.\r\n */\r\nCURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen,\r\n                                    size_t *n);\r\n\r\n/*\r\n * NAME curl_easy_send()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Sends data over the connected socket. Use after successful\r\n * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.\r\n */\r\nCURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,\r\n                                    size_t buflen, size_t *n);\r\n\r\n\r\n/*\r\n * NAME curl_easy_upkeep()\r\n *\r\n * DESCRIPTION\r\n *\r\n * Performs connection upkeep for the given session handle.\r\n */\r\nCURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl);\r\n\r\n#ifdef  __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "Chapter06/code/curl/mprintf.h",
    "content": "#ifndef __CURL_MPRINTF_H\r\n#define __CURL_MPRINTF_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n#include <stdarg.h>\r\n#include <stdio.h> /* needed for FILE */\r\n#include \"curl.h\"  /* for CURL_EXTERN */\r\n\r\n#ifdef  __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\nCURL_EXTERN int curl_mprintf(const char *format, ...);\r\nCURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...);\r\nCURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...);\r\nCURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength,\r\n                               const char *format, ...);\r\nCURL_EXTERN int curl_mvprintf(const char *format, va_list args);\r\nCURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args);\r\nCURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args);\r\nCURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength,\r\n                                const char *format, va_list args);\r\nCURL_EXTERN char *curl_maprintf(const char *format, ...);\r\nCURL_EXTERN char *curl_mvaprintf(const char *format, va_list args);\r\n\r\n#ifdef  __cplusplus\r\n}\r\n#endif\r\n\r\n#endif /* __CURL_MPRINTF_H */\r\n"
  },
  {
    "path": "Chapter06/code/curl/multi.h",
    "content": "#ifndef __CURL_MULTI_H\r\n#define __CURL_MULTI_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n/*\r\n  This is an \"external\" header file. Don't give away any internals here!\r\n\r\n  GOALS\r\n\r\n  o Enable a \"pull\" interface. The application that uses libcurl decides where\r\n    and when to ask libcurl to get/send data.\r\n\r\n  o Enable multiple simultaneous transfers in the same thread without making it\r\n    complicated for the application.\r\n\r\n  o Enable the application to select() on its own file descriptors and curl's\r\n    file descriptors simultaneous easily.\r\n\r\n*/\r\n\r\n/*\r\n * This header file should not really need to include \"curl.h\" since curl.h\r\n * itself includes this file and we expect user applications to do #include\r\n * <curl/curl.h> without the need for especially including multi.h.\r\n *\r\n * For some reason we added this include here at one point, and rather than to\r\n * break existing (wrongly written) libcurl applications, we leave it as-is\r\n * but with this warning attached.\r\n */\r\n#include \"curl.h\"\r\n\r\n#ifdef  __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)\r\ntypedef struct Curl_multi CURLM;\r\n#else\r\ntypedef void CURLM;\r\n#endif\r\n\r\ntypedef enum {\r\n  CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or\r\n                                    curl_multi_socket*() soon */\r\n  CURLM_OK,\r\n  CURLM_BAD_HANDLE,      /* the passed-in handle is not a valid CURLM handle */\r\n  CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */\r\n  CURLM_OUT_OF_MEMORY,   /* if you ever get this, you're in deep sh*t */\r\n  CURLM_INTERNAL_ERROR,  /* this is a libcurl bug */\r\n  CURLM_BAD_SOCKET,      /* the passed in socket argument did not match */\r\n  CURLM_UNKNOWN_OPTION,  /* curl_multi_setopt() with unsupported option */\r\n  CURLM_ADDED_ALREADY,   /* an easy handle already added to a multi handle was\r\n                            attempted to get added - again */\r\n  CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a\r\n                               callback */\r\n  CURLM_LAST\r\n} CURLMcode;\r\n\r\n/* just to make code nicer when using curl_multi_socket() you can now check\r\n   for CURLM_CALL_MULTI_SOCKET too in the same style it works for\r\n   curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */\r\n#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM\r\n\r\n/* bitmask bits for CURLMOPT_PIPELINING */\r\n#define CURLPIPE_NOTHING   0L\r\n#define CURLPIPE_HTTP1     1L\r\n#define CURLPIPE_MULTIPLEX 2L\r\n\r\ntypedef enum {\r\n  CURLMSG_NONE, /* first, not used */\r\n  CURLMSG_DONE, /* This easy handle has completed. 'result' contains\r\n                   the CURLcode of the transfer */\r\n  CURLMSG_LAST /* last, not used */\r\n} CURLMSG;\r\n\r\nstruct CURLMsg {\r\n  CURLMSG msg;       /* what this message means */\r\n  CURL *easy_handle; /* the handle it concerns */\r\n  union {\r\n    void *whatever;    /* message-specific data */\r\n    CURLcode result;   /* return code for transfer */\r\n  } data;\r\n};\r\ntypedef struct CURLMsg CURLMsg;\r\n\r\n/* Based on poll(2) structure and values.\r\n * We don't use pollfd and POLL* constants explicitly\r\n * to cover platforms without poll(). */\r\n#define CURL_WAIT_POLLIN    0x0001\r\n#define CURL_WAIT_POLLPRI   0x0002\r\n#define CURL_WAIT_POLLOUT   0x0004\r\n\r\nstruct curl_waitfd {\r\n  curl_socket_t fd;\r\n  short events;\r\n  short revents; /* not supported yet */\r\n};\r\n\r\n/*\r\n * Name:    curl_multi_init()\r\n *\r\n * Desc:    inititalize multi-style curl usage\r\n *\r\n * Returns: a new CURLM handle to use in all 'curl_multi' functions.\r\n */\r\nCURL_EXTERN CURLM *curl_multi_init(void);\r\n\r\n/*\r\n * Name:    curl_multi_add_handle()\r\n *\r\n * Desc:    add a standard curl handle to the multi stack\r\n *\r\n * Returns: CURLMcode type, general multi error code.\r\n */\r\nCURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle,\r\n                                            CURL *curl_handle);\r\n\r\n /*\r\n  * Name:    curl_multi_remove_handle()\r\n  *\r\n  * Desc:    removes a curl handle from the multi stack again\r\n  *\r\n  * Returns: CURLMcode type, general multi error code.\r\n  */\r\nCURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,\r\n                                               CURL *curl_handle);\r\n\r\n /*\r\n  * Name:    curl_multi_fdset()\r\n  *\r\n  * Desc:    Ask curl for its fd_set sets. The app can use these to select() or\r\n  *          poll() on. We want curl_multi_perform() called as soon as one of\r\n  *          them are ready.\r\n  *\r\n  * Returns: CURLMcode type, general multi error code.\r\n  */\r\nCURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,\r\n                                       fd_set *read_fd_set,\r\n                                       fd_set *write_fd_set,\r\n                                       fd_set *exc_fd_set,\r\n                                       int *max_fd);\r\n\r\n/*\r\n * Name:     curl_multi_wait()\r\n *\r\n * Desc:     Poll on all fds within a CURLM set as well as any\r\n *           additional fds passed to the function.\r\n *\r\n * Returns:  CURLMcode type, general multi error code.\r\n */\r\nCURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,\r\n                                      struct curl_waitfd extra_fds[],\r\n                                      unsigned int extra_nfds,\r\n                                      int timeout_ms,\r\n                                      int *ret);\r\n\r\n /*\r\n  * Name:    curl_multi_perform()\r\n  *\r\n  * Desc:    When the app thinks there's data available for curl it calls this\r\n  *          function to read/write whatever there is right now. This returns\r\n  *          as soon as the reads and writes are done. This function does not\r\n  *          require that there actually is data available for reading or that\r\n  *          data can be written, it can be called just in case. It returns\r\n  *          the number of handles that still transfer data in the second\r\n  *          argument's integer-pointer.\r\n  *\r\n  * Returns: CURLMcode type, general multi error code. *NOTE* that this only\r\n  *          returns errors etc regarding the whole multi stack. There might\r\n  *          still have occurred problems on individual transfers even when\r\n  *          this returns OK.\r\n  */\r\nCURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,\r\n                                         int *running_handles);\r\n\r\n /*\r\n  * Name:    curl_multi_cleanup()\r\n  *\r\n  * Desc:    Cleans up and removes a whole multi stack. It does not free or\r\n  *          touch any individual easy handles in any way. We need to define\r\n  *          in what state those handles will be if this function is called\r\n  *          in the middle of a transfer.\r\n  *\r\n  * Returns: CURLMcode type, general multi error code.\r\n  */\r\nCURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);\r\n\r\n/*\r\n * Name:    curl_multi_info_read()\r\n *\r\n * Desc:    Ask the multi handle if there's any messages/informationals from\r\n *          the individual transfers. Messages include informationals such as\r\n *          error code from the transfer or just the fact that a transfer is\r\n *          completed. More details on these should be written down as well.\r\n *\r\n *          Repeated calls to this function will return a new struct each\r\n *          time, until a special \"end of msgs\" struct is returned as a signal\r\n *          that there is no more to get at this point.\r\n *\r\n *          The data the returned pointer points to will not survive calling\r\n *          curl_multi_cleanup().\r\n *\r\n *          The 'CURLMsg' struct is meant to be very simple and only contain\r\n *          very basic information. If more involved information is wanted,\r\n *          we will provide the particular \"transfer handle\" in that struct\r\n *          and that should/could/would be used in subsequent\r\n *          curl_easy_getinfo() calls (or similar). The point being that we\r\n *          must never expose complex structs to applications, as then we'll\r\n *          undoubtably get backwards compatibility problems in the future.\r\n *\r\n * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out\r\n *          of structs. It also writes the number of messages left in the\r\n *          queue (after this read) in the integer the second argument points\r\n *          to.\r\n */\r\nCURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,\r\n                                          int *msgs_in_queue);\r\n\r\n/*\r\n * Name:    curl_multi_strerror()\r\n *\r\n * Desc:    The curl_multi_strerror function may be used to turn a CURLMcode\r\n *          value into the equivalent human readable error string.  This is\r\n *          useful for printing meaningful error messages.\r\n *\r\n * Returns: A pointer to a zero-terminated error message.\r\n */\r\nCURL_EXTERN const char *curl_multi_strerror(CURLMcode);\r\n\r\n/*\r\n * Name:    curl_multi_socket() and\r\n *          curl_multi_socket_all()\r\n *\r\n * Desc:    An alternative version of curl_multi_perform() that allows the\r\n *          application to pass in one of the file descriptors that have been\r\n *          detected to have \"action\" on them and let libcurl perform.\r\n *          See man page for details.\r\n */\r\n#define CURL_POLL_NONE   0\r\n#define CURL_POLL_IN     1\r\n#define CURL_POLL_OUT    2\r\n#define CURL_POLL_INOUT  3\r\n#define CURL_POLL_REMOVE 4\r\n\r\n#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD\r\n\r\n#define CURL_CSELECT_IN   0x01\r\n#define CURL_CSELECT_OUT  0x02\r\n#define CURL_CSELECT_ERR  0x04\r\n\r\ntypedef int (*curl_socket_callback)(CURL *easy,      /* easy handle */\r\n                                    curl_socket_t s, /* socket */\r\n                                    int what,        /* see above */\r\n                                    void *userp,     /* private callback\r\n                                                        pointer */\r\n                                    void *socketp);  /* private socket\r\n                                                        pointer */\r\n/*\r\n * Name:    curl_multi_timer_callback\r\n *\r\n * Desc:    Called by libcurl whenever the library detects a change in the\r\n *          maximum number of milliseconds the app is allowed to wait before\r\n *          curl_multi_socket() or curl_multi_perform() must be called\r\n *          (to allow libcurl's timed events to take place).\r\n *\r\n * Returns: The callback should return zero.\r\n */\r\ntypedef int (*curl_multi_timer_callback)(CURLM *multi,    /* multi handle */\r\n                                         long timeout_ms, /* see above */\r\n                                         void *userp);    /* private callback\r\n                                                             pointer */\r\n\r\nCURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,\r\n                                        int *running_handles);\r\n\r\nCURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle,\r\n                                               curl_socket_t s,\r\n                                               int ev_bitmask,\r\n                                               int *running_handles);\r\n\r\nCURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle,\r\n                                            int *running_handles);\r\n\r\n#ifndef CURL_ALLOW_OLD_MULTI_SOCKET\r\n/* This macro below was added in 7.16.3 to push users who recompile to use\r\n   the new curl_multi_socket_action() instead of the old curl_multi_socket()\r\n*/\r\n#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z)\r\n#endif\r\n\r\n/*\r\n * Name:    curl_multi_timeout()\r\n *\r\n * Desc:    Returns the maximum number of milliseconds the app is allowed to\r\n *          wait before curl_multi_socket() or curl_multi_perform() must be\r\n *          called (to allow libcurl's timed events to take place).\r\n *\r\n * Returns: CURLM error code.\r\n */\r\nCURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle,\r\n                                         long *milliseconds);\r\n\r\n#undef CINIT /* re-using the same name as in curl.h */\r\n\r\n#ifdef CURL_ISOCPP\r\n#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num\r\n#else\r\n/* The macro \"##\" is ISO C, we assume pre-ISO C doesn't support it. */\r\n#define LONG          CURLOPTTYPE_LONG\r\n#define OBJECTPOINT   CURLOPTTYPE_OBJECTPOINT\r\n#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT\r\n#define OFF_T         CURLOPTTYPE_OFF_T\r\n#define CINIT(name,type,number) CURLMOPT_/**/name = type + number\r\n#endif\r\n\r\ntypedef enum {\r\n  /* This is the socket callback function pointer */\r\n  CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1),\r\n\r\n  /* This is the argument passed to the socket callback */\r\n  CINIT(SOCKETDATA, OBJECTPOINT, 2),\r\n\r\n    /* set to 1 to enable pipelining for this multi handle */\r\n  CINIT(PIPELINING, LONG, 3),\r\n\r\n   /* This is the timer callback function pointer */\r\n  CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4),\r\n\r\n  /* This is the argument passed to the timer callback */\r\n  CINIT(TIMERDATA, OBJECTPOINT, 5),\r\n\r\n  /* maximum number of entries in the connection cache */\r\n  CINIT(MAXCONNECTS, LONG, 6),\r\n\r\n  /* maximum number of (pipelining) connections to one host */\r\n  CINIT(MAX_HOST_CONNECTIONS, LONG, 7),\r\n\r\n  /* maximum number of requests in a pipeline */\r\n  CINIT(MAX_PIPELINE_LENGTH, LONG, 8),\r\n\r\n  /* a connection with a content-length longer than this\r\n     will not be considered for pipelining */\r\n  CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9),\r\n\r\n  /* a connection with a chunk length longer than this\r\n     will not be considered for pipelining */\r\n  CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10),\r\n\r\n  /* a list of site names(+port) that are blacklisted from\r\n     pipelining */\r\n  CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11),\r\n\r\n  /* a list of server types that are blacklisted from\r\n     pipelining */\r\n  CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12),\r\n\r\n  /* maximum number of open connections in total */\r\n  CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),\r\n\r\n   /* This is the server push callback function pointer */\r\n  CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14),\r\n\r\n  /* This is the argument passed to the server push callback */\r\n  CINIT(PUSHDATA, OBJECTPOINT, 15),\r\n\r\n  CURLMOPT_LASTENTRY /* the last unused */\r\n} CURLMoption;\r\n\r\n\r\n/*\r\n * Name:    curl_multi_setopt()\r\n *\r\n * Desc:    Sets options for the multi handle.\r\n *\r\n * Returns: CURLM error code.\r\n */\r\nCURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,\r\n                                        CURLMoption option, ...);\r\n\r\n\r\n/*\r\n * Name:    curl_multi_assign()\r\n *\r\n * Desc:    This function sets an association in the multi handle between the\r\n *          given socket and a private pointer of the application. This is\r\n *          (only) useful for curl_multi_socket uses.\r\n *\r\n * Returns: CURLM error code.\r\n */\r\nCURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,\r\n                                        curl_socket_t sockfd, void *sockp);\r\n\r\n\r\n/*\r\n * Name: curl_push_callback\r\n *\r\n * Desc: This callback gets called when a new stream is being pushed by the\r\n *       server. It approves or denies the new stream.\r\n *\r\n * Returns: CURL_PUSH_OK or CURL_PUSH_DENY.\r\n */\r\n#define CURL_PUSH_OK   0\r\n#define CURL_PUSH_DENY 1\r\n\r\nstruct curl_pushheaders;  /* forward declaration only */\r\n\r\nCURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h,\r\n                                        size_t num);\r\nCURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h,\r\n                                         const char *name);\r\n\r\ntypedef int (*curl_push_callback)(CURL *parent,\r\n                                  CURL *easy,\r\n                                  size_t num_headers,\r\n                                  struct curl_pushheaders *headers,\r\n                                  void *userp);\r\n\r\n#ifdef __cplusplus\r\n} /* end of extern \"C\" */\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "Chapter06/code/curl/stdcheaders.h",
    "content": "#ifndef __STDC_HEADERS_H\r\n#define __STDC_HEADERS_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n#include <sys/types.h>\r\n\r\nsize_t fread(void *, size_t, size_t, FILE *);\r\nsize_t fwrite(const void *, size_t, size_t, FILE *);\r\n\r\nint strcasecmp(const char *, const char *);\r\nint strncasecmp(const char *, const char *, size_t);\r\n\r\n#endif /* __STDC_HEADERS_H */\r\n"
  },
  {
    "path": "Chapter06/code/curl/system.h",
    "content": "#ifndef __CURL_SYSTEM_H\r\n#define __CURL_SYSTEM_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n/*\r\n * Try to keep one section per platform, compiler and architecture, otherwise,\r\n * if an existing section is reused for a different one and later on the\r\n * original is adjusted, probably the piggybacking one can be adversely\r\n * changed.\r\n *\r\n * In order to differentiate between platforms/compilers/architectures use\r\n * only compiler built in predefined preprocessor symbols.\r\n *\r\n * curl_off_t\r\n * ----------\r\n *\r\n * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit\r\n * wide signed integral data type. The width of this data type must remain\r\n * constant and independent of any possible large file support settings.\r\n *\r\n * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit\r\n * wide signed integral data type if there is no 64-bit type.\r\n *\r\n * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall\r\n * only be violated if off_t is the only 64-bit data type available and the\r\n * size of off_t is independent of large file support settings. Keep your\r\n * build on the safe side avoiding an off_t gating.  If you have a 64-bit\r\n * off_t then take for sure that another 64-bit data type exists, dig deeper\r\n * and you will find it.\r\n *\r\n */\r\n\r\n#if defined(__DJGPP__) || defined(__GO32__)\r\n#  if defined(__DJGPP__) && (__DJGPP__ > 1)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__SALFORDC__)\r\n#  define CURL_TYPEOF_CURL_OFF_T     long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     L\r\n#  define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__BORLANDC__)\r\n#  if (__BORLANDC__ < 0x520)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     __int64\r\n#    define CURL_FORMAT_CURL_OFF_T     \"I64d\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"I64u\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     i64\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ui64\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__TURBOC__)\r\n#  define CURL_TYPEOF_CURL_OFF_T     long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     L\r\n#  define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__WATCOMC__)\r\n#  if defined(__386__)\r\n#    define CURL_TYPEOF_CURL_OFF_T     __int64\r\n#    define CURL_FORMAT_CURL_OFF_T     \"I64d\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"I64u\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     i64\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ui64\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__POCC__)\r\n#  if (__POCC__ < 280)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  elif defined(_MSC_VER)\r\n#    define CURL_TYPEOF_CURL_OFF_T     __int64\r\n#    define CURL_FORMAT_CURL_OFF_T     \"I64d\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"I64u\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     i64\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ui64\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__LCC__)\r\n#  define CURL_TYPEOF_CURL_OFF_T     long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     L\r\n#  define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__SYMBIAN32__)\r\n#  if defined(__EABI__)  /* Treat all ARM compilers equally */\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  elif defined(__CW32__)\r\n#    pragma longlong on\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  elif defined(__VC32__)\r\n#    define CURL_TYPEOF_CURL_OFF_T     __int64\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int\r\n\r\n#elif defined(__MWERKS__)\r\n#  define CURL_TYPEOF_CURL_OFF_T     long long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     LL\r\n#  define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(_WIN32_WCE)\r\n#  define CURL_TYPEOF_CURL_OFF_T     __int64\r\n#  define CURL_FORMAT_CURL_OFF_T     \"I64d\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"I64u\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     i64\r\n#  define CURL_SUFFIX_CURL_OFF_TU    ui64\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__MINGW32__)\r\n#  define CURL_TYPEOF_CURL_OFF_T     long long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"I64d\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"I64u\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     LL\r\n#  define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#  define CURL_PULL_SYS_TYPES_H      1\r\n#  define CURL_PULL_WS2TCPIP_H       1\r\n\r\n#elif defined(__VMS)\r\n#  if defined(__VAX)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int\r\n\r\n#elif defined(__OS400__)\r\n#  if defined(__ILEC400__)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#    define CURL_PULL_SYS_TYPES_H      1\r\n#    define CURL_PULL_SYS_SOCKET_H     1\r\n#  endif\r\n\r\n#elif defined(__MVS__)\r\n#  if defined(__IBMC__) || defined(__IBMCPP__)\r\n#    if defined(_ILP32)\r\n#    elif defined(_LP64)\r\n#    endif\r\n#    if defined(_LONG_LONG)\r\n#      define CURL_TYPEOF_CURL_OFF_T     long long\r\n#      define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#      define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#      define CURL_SUFFIX_CURL_OFF_T     LL\r\n#      define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#    elif defined(_LP64)\r\n#      define CURL_TYPEOF_CURL_OFF_T     long\r\n#      define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#      define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#      define CURL_SUFFIX_CURL_OFF_T     L\r\n#      define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#    else\r\n#      define CURL_TYPEOF_CURL_OFF_T     long\r\n#      define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#      define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#      define CURL_SUFFIX_CURL_OFF_T     L\r\n#      define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#    endif\r\n#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#    define CURL_PULL_SYS_TYPES_H      1\r\n#    define CURL_PULL_SYS_SOCKET_H     1\r\n#  endif\r\n\r\n#elif defined(__370__)\r\n#  if defined(__IBMC__) || defined(__IBMCPP__)\r\n#    if defined(_ILP32)\r\n#    elif defined(_LP64)\r\n#    endif\r\n#    if defined(_LONG_LONG)\r\n#      define CURL_TYPEOF_CURL_OFF_T     long long\r\n#      define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#      define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#      define CURL_SUFFIX_CURL_OFF_T     LL\r\n#      define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#    elif defined(_LP64)\r\n#      define CURL_TYPEOF_CURL_OFF_T     long\r\n#      define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#      define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#      define CURL_SUFFIX_CURL_OFF_T     L\r\n#      define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#    else\r\n#      define CURL_TYPEOF_CURL_OFF_T     long\r\n#      define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#      define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#      define CURL_SUFFIX_CURL_OFF_T     L\r\n#      define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#    endif\r\n#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#    define CURL_PULL_SYS_TYPES_H      1\r\n#    define CURL_PULL_SYS_SOCKET_H     1\r\n#  endif\r\n\r\n#elif defined(TPF)\r\n#  define CURL_TYPEOF_CURL_OFF_T     long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     L\r\n#  define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n#elif defined(__TINYC__) /* also known as tcc */\r\n\r\n#  define CURL_TYPEOF_CURL_OFF_T     long long\r\n#  define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#  define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#  define CURL_SUFFIX_CURL_OFF_T     LL\r\n#  define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#  define CURL_PULL_SYS_TYPES_H      1\r\n#  define CURL_PULL_SYS_SOCKET_H     1\r\n\r\n#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Oracle Solaris Studio */\r\n#  if !defined(__LP64) && (defined(__ILP32) ||                          \\\r\n                           defined(__i386) ||                           \\\r\n                           defined(__sparcv8) ||                        \\\r\n                           defined(__sparcv8plus))\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  elif defined(__LP64) || \\\r\n        defined(__amd64) || defined(__sparcv9)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#  define CURL_PULL_SYS_TYPES_H      1\r\n#  define CURL_PULL_SYS_SOCKET_H     1\r\n\r\n#elif defined(__xlc__) /* IBM xlc compiler */\r\n#  if !defined(_LP64)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#  define CURL_PULL_SYS_TYPES_H      1\r\n#  define CURL_PULL_SYS_SOCKET_H     1\r\n\r\n/* ===================================== */\r\n/*    KEEP MSVC THE PENULTIMATE ENTRY    */\r\n/* ===================================== */\r\n\r\n#elif defined(_MSC_VER)\r\n#  if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)\r\n#    define CURL_TYPEOF_CURL_OFF_T     __int64\r\n#    define CURL_FORMAT_CURL_OFF_T     \"I64d\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"I64u\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     i64\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ui64\r\n#  else\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n\r\n/* ===================================== */\r\n/*    KEEP GENERIC GCC THE LAST ENTRY    */\r\n/* ===================================== */\r\n\r\n#elif defined(__GNUC__) && !defined(_SCO_DS)\r\n#  if !defined(__LP64__) &&                                             \\\r\n  (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) ||      \\\r\n   defined(__ppc__) || defined(__powerpc__) || defined(__arm__) ||      \\\r\n   defined(__sparc__) || defined(__mips__) || defined(__sh__) ||        \\\r\n   defined(__XTENSA__) ||                                               \\\r\n   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4)  ||               \\\r\n   (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L))\r\n#    define CURL_TYPEOF_CURL_OFF_T     long long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"lld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"llu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     LL\r\n#    define CURL_SUFFIX_CURL_OFF_TU    ULL\r\n#  elif defined(__LP64__) || \\\r\n        defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \\\r\n        (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \\\r\n        (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L)\r\n#    define CURL_TYPEOF_CURL_OFF_T     long\r\n#    define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n#    define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n#    define CURL_SUFFIX_CURL_OFF_T     L\r\n#    define CURL_SUFFIX_CURL_OFF_TU    UL\r\n#  endif\r\n#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t\r\n#  define CURL_PULL_SYS_TYPES_H      1\r\n#  define CURL_PULL_SYS_SOCKET_H     1\r\n\r\n#else\r\n/* generic \"safe guess\" on old 32 bit style */\r\n# define CURL_TYPEOF_CURL_OFF_T     long\r\n# define CURL_FORMAT_CURL_OFF_T     \"ld\"\r\n# define CURL_FORMAT_CURL_OFF_TU    \"lu\"\r\n# define CURL_SUFFIX_CURL_OFF_T     L\r\n# define CURL_SUFFIX_CURL_OFF_TU    UL\r\n# define CURL_TYPEOF_CURL_SOCKLEN_T int\r\n#endif\r\n\r\n#ifdef _AIX\r\n/* AIX needs <sys/poll.h> */\r\n#define CURL_PULL_SYS_POLL_H\r\n#endif\r\n\r\n\r\n/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file  */\r\n/* ws2tcpip.h is required here to properly make type definitions below. */\r\n#ifdef CURL_PULL_WS2TCPIP_H\r\n#  include <winsock2.h>\r\n#  include <windows.h>\r\n#  include <ws2tcpip.h>\r\n#endif\r\n\r\n/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file  */\r\n/* sys/types.h is required here to properly make type definitions below. */\r\n#ifdef CURL_PULL_SYS_TYPES_H\r\n#  include <sys/types.h>\r\n#endif\r\n\r\n/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file  */\r\n/* sys/socket.h is required here to properly make type definitions below. */\r\n#ifdef CURL_PULL_SYS_SOCKET_H\r\n#  include <sys/socket.h>\r\n#endif\r\n\r\n/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file    */\r\n/* sys/poll.h is required here to properly make type definitions below.   */\r\n#ifdef CURL_PULL_SYS_POLL_H\r\n#  include <sys/poll.h>\r\n#endif\r\n\r\n/* Data type definition of curl_socklen_t. */\r\n#ifdef CURL_TYPEOF_CURL_SOCKLEN_T\r\n  typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t;\r\n#endif\r\n\r\n/* Data type definition of curl_off_t. */\r\n\r\n#ifdef CURL_TYPEOF_CURL_OFF_T\r\n  typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;\r\n#endif\r\n\r\n/*\r\n * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow\r\n * these to be visible and exported by the external libcurl interface API,\r\n * while also making them visible to the library internals, simply including\r\n * curl_setup.h, without actually needing to include curl.h internally.\r\n * If some day this section would grow big enough, all this should be moved\r\n * to its own header file.\r\n */\r\n\r\n/*\r\n * Figure out if we can use the ## preprocessor operator, which is supported\r\n * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__\r\n * or  __cplusplus so we need to carefully check for them too.\r\n */\r\n\r\n#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \\\r\n  defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \\\r\n  defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \\\r\n  defined(__ILEC400__)\r\n  /* This compiler is believed to have an ISO compatible preprocessor */\r\n#define CURL_ISOCPP\r\n#else\r\n  /* This compiler is believed NOT to have an ISO compatible preprocessor */\r\n#undef CURL_ISOCPP\r\n#endif\r\n\r\n/*\r\n * Macros for minimum-width signed and unsigned curl_off_t integer constants.\r\n */\r\n\r\n#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)\r\n#  define __CURL_OFF_T_C_HLPR2(x) x\r\n#  define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)\r\n#  define CURL_OFF_T_C(Val)  __CURL_OFF_T_C_HLPR1(Val) ## \\\r\n                             __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)\r\n#  define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \\\r\n                             __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)\r\n#else\r\n#  ifdef CURL_ISOCPP\r\n#    define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix\r\n#  else\r\n#    define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix\r\n#  endif\r\n#  define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)\r\n#  define CURL_OFF_T_C(Val)  __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)\r\n#  define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)\r\n#endif\r\n\r\n#endif /* __CURL_SYSTEM_H */\r\n"
  },
  {
    "path": "Chapter06/code/curl/typecheck-gcc.h",
    "content": "#ifndef __CURL_TYPECHECK_GCC_H\r\n#define __CURL_TYPECHECK_GCC_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n/* wraps curl_easy_setopt() with typechecking */\r\n\r\n/* To add a new kind of warning, add an\r\n *   if(_curl_is_sometype_option(_curl_opt))\r\n *     if(!_curl_is_sometype(value))\r\n *       _curl_easy_setopt_err_sometype();\r\n * block and define _curl_is_sometype_option, _curl_is_sometype and\r\n * _curl_easy_setopt_err_sometype below\r\n *\r\n * NOTE: We use two nested 'if' statements here instead of the && operator, in\r\n *       order to work around gcc bug #32061.  It affects only gcc 4.3.x/4.4.x\r\n *       when compiling with -Wlogical-op.\r\n *\r\n * To add an option that uses the same type as an existing option, you'll just\r\n * need to extend the appropriate _curl_*_option macro\r\n */\r\n#define curl_easy_setopt(handle, option, value)                               \\\r\n__extension__ ({                                                              \\\r\n  __typeof__(option) _curl_opt = option;                                     \\\r\n  if(__builtin_constant_p(_curl_opt)) {                                       \\\r\n    if(_curl_is_long_option(_curl_opt))                                       \\\r\n      if(!_curl_is_long(value))                                               \\\r\n        _curl_easy_setopt_err_long();                                         \\\r\n    if(_curl_is_off_t_option(_curl_opt))                                      \\\r\n      if(!_curl_is_off_t(value))                                              \\\r\n        _curl_easy_setopt_err_curl_off_t();                                   \\\r\n    if(_curl_is_string_option(_curl_opt))                                     \\\r\n      if(!_curl_is_string(value))                                             \\\r\n        _curl_easy_setopt_err_string();                                       \\\r\n    if(_curl_is_write_cb_option(_curl_opt))                                   \\\r\n      if(!_curl_is_write_cb(value))                                           \\\r\n        _curl_easy_setopt_err_write_callback();                               \\\r\n    if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION)                        \\\r\n      if(!_curl_is_resolver_start_callback(value))                            \\\r\n        _curl_easy_setopt_err_resolver_start_callback();                      \\\r\n    if((_curl_opt) == CURLOPT_READFUNCTION)                                   \\\r\n      if(!_curl_is_read_cb(value))                                            \\\r\n        _curl_easy_setopt_err_read_cb();                                      \\\r\n    if((_curl_opt) == CURLOPT_IOCTLFUNCTION)                                  \\\r\n      if(!_curl_is_ioctl_cb(value))                                           \\\r\n        _curl_easy_setopt_err_ioctl_cb();                                     \\\r\n    if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION)                                \\\r\n      if(!_curl_is_sockopt_cb(value))                                         \\\r\n        _curl_easy_setopt_err_sockopt_cb();                                   \\\r\n    if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION)                             \\\r\n      if(!_curl_is_opensocket_cb(value))                                      \\\r\n        _curl_easy_setopt_err_opensocket_cb();                                \\\r\n    if((_curl_opt) == CURLOPT_PROGRESSFUNCTION)                               \\\r\n      if(!_curl_is_progress_cb(value))                                        \\\r\n        _curl_easy_setopt_err_progress_cb();                                  \\\r\n    if((_curl_opt) == CURLOPT_DEBUGFUNCTION)                                  \\\r\n      if(!_curl_is_debug_cb(value))                                           \\\r\n        _curl_easy_setopt_err_debug_cb();                                     \\\r\n    if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION)                               \\\r\n      if(!_curl_is_ssl_ctx_cb(value))                                         \\\r\n        _curl_easy_setopt_err_ssl_ctx_cb();                                   \\\r\n    if(_curl_is_conv_cb_option(_curl_opt))                                    \\\r\n      if(!_curl_is_conv_cb(value))                                            \\\r\n        _curl_easy_setopt_err_conv_cb();                                      \\\r\n    if((_curl_opt) == CURLOPT_SEEKFUNCTION)                                   \\\r\n      if(!_curl_is_seek_cb(value))                                            \\\r\n        _curl_easy_setopt_err_seek_cb();                                      \\\r\n    if(_curl_is_cb_data_option(_curl_opt))                                    \\\r\n      if(!_curl_is_cb_data(value))                                            \\\r\n        _curl_easy_setopt_err_cb_data();                                      \\\r\n    if((_curl_opt) == CURLOPT_ERRORBUFFER)                                    \\\r\n      if(!_curl_is_error_buffer(value))                                       \\\r\n        _curl_easy_setopt_err_error_buffer();                                 \\\r\n    if((_curl_opt) == CURLOPT_STDERR)                                         \\\r\n      if(!_curl_is_FILE(value))                                               \\\r\n        _curl_easy_setopt_err_FILE();                                         \\\r\n    if(_curl_is_postfields_option(_curl_opt))                                 \\\r\n      if(!_curl_is_postfields(value))                                         \\\r\n        _curl_easy_setopt_err_postfields();                                   \\\r\n    if((_curl_opt) == CURLOPT_HTTPPOST)                                       \\\r\n      if(!_curl_is_arr((value), struct curl_httppost))                        \\\r\n        _curl_easy_setopt_err_curl_httpost();                                 \\\r\n    if((_curl_opt) == CURLOPT_MIMEPOST)                                       \\\r\n      if(!_curl_is_ptr((value), curl_mime))                                   \\\r\n        _curl_easy_setopt_err_curl_mimepost();                                \\\r\n    if(_curl_is_slist_option(_curl_opt))                                      \\\r\n      if(!_curl_is_arr((value), struct curl_slist))                           \\\r\n        _curl_easy_setopt_err_curl_slist();                                   \\\r\n    if((_curl_opt) == CURLOPT_SHARE)                                          \\\r\n      if(!_curl_is_ptr((value), CURLSH))                                      \\\r\n        _curl_easy_setopt_err_CURLSH();                                       \\\r\n  }                                                                           \\\r\n  curl_easy_setopt(handle, _curl_opt, value);                                 \\\r\n})\r\n\r\n/* wraps curl_easy_getinfo() with typechecking */\r\n/* FIXME: don't allow const pointers */\r\n#define curl_easy_getinfo(handle, info, arg)                                  \\\r\n__extension__ ({                                                              \\\r\n  __typeof__(info) _curl_info = info;                                         \\\r\n  if(__builtin_constant_p(_curl_info)) {                                      \\\r\n    if(_curl_is_string_info(_curl_info))                                      \\\r\n      if(!_curl_is_arr((arg), char *))                                        \\\r\n        _curl_easy_getinfo_err_string();                                      \\\r\n    if(_curl_is_long_info(_curl_info))                                        \\\r\n      if(!_curl_is_arr((arg), long))                                          \\\r\n        _curl_easy_getinfo_err_long();                                        \\\r\n    if(_curl_is_double_info(_curl_info))                                      \\\r\n      if(!_curl_is_arr((arg), double))                                        \\\r\n        _curl_easy_getinfo_err_double();                                      \\\r\n    if(_curl_is_slist_info(_curl_info))                                       \\\r\n      if(!_curl_is_arr((arg), struct curl_slist *))                           \\\r\n        _curl_easy_getinfo_err_curl_slist();                                  \\\r\n    if(_curl_is_tlssessioninfo_info(_curl_info))                              \\\r\n      if(!_curl_is_arr((arg), struct curl_tlssessioninfo *))                  \\\r\n        _curl_easy_getinfo_err_curl_tlssesssioninfo();                        \\\r\n    if(_curl_is_certinfo_info(_curl_info))                                    \\\r\n      if(!_curl_is_arr((arg), struct curl_certinfo *))                        \\\r\n        _curl_easy_getinfo_err_curl_certinfo();                               \\\r\n    if(_curl_is_socket_info(_curl_info))                                      \\\r\n      if(!_curl_is_arr((arg), curl_socket_t))                                 \\\r\n        _curl_easy_getinfo_err_curl_socket();                                 \\\r\n    if(_curl_is_off_t_info(_curl_info))                                       \\\r\n      if(!_curl_is_arr((arg), curl_off_t))                                    \\\r\n        _curl_easy_getinfo_err_curl_off_t();                                  \\\r\n  }                                                                           \\\r\n  curl_easy_getinfo(handle, _curl_info, arg);                                 \\\r\n})\r\n\r\n/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(),\r\n * for now just make sure that the functions are called with three\r\n * arguments\r\n */\r\n#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)\r\n#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)\r\n\r\n\r\n/* the actual warnings, triggered by calling the _curl_easy_setopt_err*\r\n * functions */\r\n\r\n/* To define a new warning, use _CURL_WARNING(identifier, \"message\") */\r\n#define _CURL_WARNING(id, message)                                            \\\r\n  static void __attribute__((__warning__(message)))                           \\\r\n  __attribute__((__unused__)) __attribute__((__noinline__))                   \\\r\n  id(void) { __asm__(\"\"); }\r\n\r\n_CURL_WARNING(_curl_easy_setopt_err_long,\r\n  \"curl_easy_setopt expects a long argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_curl_off_t,\r\n  \"curl_easy_setopt expects a curl_off_t argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_string,\r\n              \"curl_easy_setopt expects a \"\r\n              \"string ('char *' or char[]) argument for this option\"\r\n  )\r\n_CURL_WARNING(_curl_easy_setopt_err_write_callback,\r\n  \"curl_easy_setopt expects a curl_write_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_resolver_start_callback,\r\n              \"curl_easy_setopt expects a \"\r\n              \"curl_resolver_start_callback argument for this option\"\r\n  )\r\n_CURL_WARNING(_curl_easy_setopt_err_read_cb,\r\n  \"curl_easy_setopt expects a curl_read_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb,\r\n  \"curl_easy_setopt expects a curl_ioctl_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb,\r\n  \"curl_easy_setopt expects a curl_sockopt_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb,\r\n              \"curl_easy_setopt expects a \"\r\n              \"curl_opensocket_callback argument for this option\"\r\n  )\r\n_CURL_WARNING(_curl_easy_setopt_err_progress_cb,\r\n  \"curl_easy_setopt expects a curl_progress_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_debug_cb,\r\n  \"curl_easy_setopt expects a curl_debug_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb,\r\n  \"curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_conv_cb,\r\n  \"curl_easy_setopt expects a curl_conv_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_seek_cb,\r\n  \"curl_easy_setopt expects a curl_seek_callback argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_cb_data,\r\n              \"curl_easy_setopt expects a \"\r\n              \"private data pointer as argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_error_buffer,\r\n              \"curl_easy_setopt expects a \"\r\n              \"char buffer of CURL_ERROR_SIZE as argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_FILE,\r\n  \"curl_easy_setopt expects a 'FILE *' argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_postfields,\r\n  \"curl_easy_setopt expects a 'void *' or 'char *' argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_curl_httpost,\r\n              \"curl_easy_setopt expects a 'struct curl_httppost *' \"\r\n              \"argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost,\r\n              \"curl_easy_setopt expects a 'curl_mime *' \"\r\n              \"argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_curl_slist,\r\n  \"curl_easy_setopt expects a 'struct curl_slist *' argument for this option\")\r\n_CURL_WARNING(_curl_easy_setopt_err_CURLSH,\r\n  \"curl_easy_setopt expects a CURLSH* argument for this option\")\r\n\r\n_CURL_WARNING(_curl_easy_getinfo_err_string,\r\n  \"curl_easy_getinfo expects a pointer to 'char *' for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_long,\r\n  \"curl_easy_getinfo expects a pointer to long for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_double,\r\n  \"curl_easy_getinfo expects a pointer to double for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_curl_slist,\r\n  \"curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,\r\n              \"curl_easy_getinfo expects a pointer to \"\r\n              \"'struct curl_tlssessioninfo *' for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo,\r\n              \"curl_easy_getinfo expects a pointer to \"\r\n              \"'struct curl_certinfo *' for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_curl_socket,\r\n  \"curl_easy_getinfo expects a pointer to curl_socket_t for this info\")\r\n_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,\r\n  \"curl_easy_getinfo expects a pointer to curl_off_t for this info\")\r\n\r\n/* groups of curl_easy_setops options that take the same type of argument */\r\n\r\n/* To add a new option to one of the groups, just add\r\n *   (option) == CURLOPT_SOMETHING\r\n * to the or-expression. If the option takes a long or curl_off_t, you don't\r\n * have to do anything\r\n */\r\n\r\n/* evaluates to true if option takes a long argument */\r\n#define _curl_is_long_option(option)                                          \\\r\n  (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)\r\n\r\n#define _curl_is_off_t_option(option)                                         \\\r\n  ((option) > CURLOPTTYPE_OFF_T)\r\n\r\n/* evaluates to true if option takes a char* argument */\r\n#define _curl_is_string_option(option)                                        \\\r\n  ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET ||                                \\\r\n   (option) == CURLOPT_ACCEPT_ENCODING ||                                     \\\r\n   (option) == CURLOPT_ALTSVC ||                                              \\\r\n   (option) == CURLOPT_CAINFO ||                                              \\\r\n   (option) == CURLOPT_CAPATH ||                                              \\\r\n   (option) == CURLOPT_COOKIE ||                                              \\\r\n   (option) == CURLOPT_COOKIEFILE ||                                          \\\r\n   (option) == CURLOPT_COOKIEJAR ||                                           \\\r\n   (option) == CURLOPT_COOKIELIST ||                                          \\\r\n   (option) == CURLOPT_CRLFILE ||                                             \\\r\n   (option) == CURLOPT_CUSTOMREQUEST ||                                       \\\r\n   (option) == CURLOPT_DEFAULT_PROTOCOL ||                                    \\\r\n   (option) == CURLOPT_DNS_INTERFACE ||                                       \\\r\n   (option) == CURLOPT_DNS_LOCAL_IP4 ||                                       \\\r\n   (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \\\r\n   (option) == CURLOPT_DNS_SERVERS ||                                         \\\r\n   (option) == CURLOPT_DOH_URL ||                                             \\\r\n   (option) == CURLOPT_EGDSOCKET ||                                           \\\r\n   (option) == CURLOPT_FTPPORT ||                                             \\\r\n   (option) == CURLOPT_FTP_ACCOUNT ||                                         \\\r\n   (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \\\r\n   (option) == CURLOPT_INTERFACE ||                                           \\\r\n   (option) == CURLOPT_ISSUERCERT ||                                          \\\r\n   (option) == CURLOPT_KEYPASSWD ||                                           \\\r\n   (option) == CURLOPT_KRBLEVEL ||                                            \\\r\n   (option) == CURLOPT_LOGIN_OPTIONS ||                                       \\\r\n   (option) == CURLOPT_MAIL_AUTH ||                                           \\\r\n   (option) == CURLOPT_MAIL_FROM ||                                           \\\r\n   (option) == CURLOPT_NETRC_FILE ||                                          \\\r\n   (option) == CURLOPT_NOPROXY ||                                             \\\r\n   (option) == CURLOPT_PASSWORD ||                                            \\\r\n   (option) == CURLOPT_PINNEDPUBLICKEY ||                                     \\\r\n   (option) == CURLOPT_PRE_PROXY ||                                           \\\r\n   (option) == CURLOPT_PROXY ||                                               \\\r\n   (option) == CURLOPT_PROXYPASSWORD ||                                       \\\r\n   (option) == CURLOPT_PROXYUSERNAME ||                                       \\\r\n   (option) == CURLOPT_PROXYUSERPWD ||                                        \\\r\n   (option) == CURLOPT_PROXY_CAINFO ||                                        \\\r\n   (option) == CURLOPT_PROXY_CAPATH ||                                        \\\r\n   (option) == CURLOPT_PROXY_CRLFILE ||                                       \\\r\n   (option) == CURLOPT_PROXY_KEYPASSWD ||                                     \\\r\n   (option) == CURLOPT_PROXY_PINNEDPUBLICKEY ||                               \\\r\n   (option) == CURLOPT_PROXY_SERVICE_NAME ||                                  \\\r\n   (option) == CURLOPT_PROXY_SSLCERT ||                                       \\\r\n   (option) == CURLOPT_PROXY_SSLCERTTYPE ||                                   \\\r\n   (option) == CURLOPT_PROXY_SSLKEY ||                                        \\\r\n   (option) == CURLOPT_PROXY_SSLKEYTYPE ||                                    \\\r\n   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                               \\\r\n   (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD ||                              \\\r\n   (option) == CURLOPT_PROXY_TLSAUTH_USERNAME ||                              \\\r\n   (option) == CURLOPT_PROXY_TLSAUTH_TYPE ||                                  \\\r\n   (option) == CURLOPT_RANDOM_FILE ||                                         \\\r\n   (option) == CURLOPT_RANGE ||                                               \\\r\n   (option) == CURLOPT_REFERER ||                                             \\\r\n   (option) == CURLOPT_RTSP_SESSION_ID ||                                     \\\r\n   (option) == CURLOPT_RTSP_STREAM_URI ||                                     \\\r\n   (option) == CURLOPT_RTSP_TRANSPORT ||                                      \\\r\n   (option) == CURLOPT_SERVICE_NAME ||                                        \\\r\n   (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE ||                               \\\r\n   (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 ||                             \\\r\n   (option) == CURLOPT_SSH_KNOWNHOSTS ||                                      \\\r\n   (option) == CURLOPT_SSH_PRIVATE_KEYFILE ||                                 \\\r\n   (option) == CURLOPT_SSH_PUBLIC_KEYFILE ||                                  \\\r\n   (option) == CURLOPT_SSLCERT ||                                             \\\r\n   (option) == CURLOPT_SSLCERTTYPE ||                                         \\\r\n   (option) == CURLOPT_SSLENGINE ||                                           \\\r\n   (option) == CURLOPT_SSLKEY ||                                              \\\r\n   (option) == CURLOPT_SSLKEYTYPE ||                                          \\\r\n   (option) == CURLOPT_SSL_CIPHER_LIST ||                                     \\\r\n   (option) == CURLOPT_TLSAUTH_PASSWORD ||                                    \\\r\n   (option) == CURLOPT_TLSAUTH_TYPE ||                                        \\\r\n   (option) == CURLOPT_TLSAUTH_USERNAME ||                                    \\\r\n   (option) == CURLOPT_UNIX_SOCKET_PATH ||                                    \\\r\n   (option) == CURLOPT_URL ||                                                 \\\r\n   (option) == CURLOPT_USERAGENT ||                                           \\\r\n   (option) == CURLOPT_USERNAME ||                                            \\\r\n   (option) == CURLOPT_USERPWD ||                                             \\\r\n   (option) == CURLOPT_XOAUTH2_BEARER ||                                      \\\r\n   0)\r\n\r\n/* evaluates to true if option takes a curl_write_callback argument */\r\n#define _curl_is_write_cb_option(option)                                      \\\r\n  ((option) == CURLOPT_HEADERFUNCTION ||                                      \\\r\n   (option) == CURLOPT_WRITEFUNCTION)\r\n\r\n/* evaluates to true if option takes a curl_conv_callback argument */\r\n#define _curl_is_conv_cb_option(option)                                       \\\r\n  ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION ||                            \\\r\n   (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION ||                          \\\r\n   (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION)\r\n\r\n/* evaluates to true if option takes a data argument to pass to a callback */\r\n#define _curl_is_cb_data_option(option)                                       \\\r\n  ((option) == CURLOPT_CHUNK_DATA ||                                          \\\r\n   (option) == CURLOPT_CLOSESOCKETDATA ||                                     \\\r\n   (option) == CURLOPT_DEBUGDATA ||                                           \\\r\n   (option) == CURLOPT_FNMATCH_DATA ||                                        \\\r\n   (option) == CURLOPT_HEADERDATA ||                                          \\\r\n   (option) == CURLOPT_INTERLEAVEDATA ||                                      \\\r\n   (option) == CURLOPT_IOCTLDATA ||                                           \\\r\n   (option) == CURLOPT_OPENSOCKETDATA ||                                      \\\r\n   (option) == CURLOPT_PRIVATE ||                                             \\\r\n   (option) == CURLOPT_PROGRESSDATA ||                                        \\\r\n   (option) == CURLOPT_READDATA ||                                            \\\r\n   (option) == CURLOPT_SEEKDATA ||                                            \\\r\n   (option) == CURLOPT_SOCKOPTDATA ||                                         \\\r\n   (option) == CURLOPT_SSH_KEYDATA ||                                         \\\r\n   (option) == CURLOPT_SSL_CTX_DATA ||                                        \\\r\n   (option) == CURLOPT_WRITEDATA ||                                           \\\r\n   (option) == CURLOPT_RESOLVER_START_DATA ||                                 \\\r\n   (option) == CURLOPT_CURLU ||                                               \\\r\n   0)\r\n\r\n/* evaluates to true if option takes a POST data argument (void* or char*) */\r\n#define _curl_is_postfields_option(option)                                    \\\r\n  ((option) == CURLOPT_POSTFIELDS ||                                          \\\r\n   (option) == CURLOPT_COPYPOSTFIELDS ||                                      \\\r\n   0)\r\n\r\n/* evaluates to true if option takes a struct curl_slist * argument */\r\n#define _curl_is_slist_option(option)                                         \\\r\n  ((option) == CURLOPT_HTTP200ALIASES ||                                      \\\r\n   (option) == CURLOPT_HTTPHEADER ||                                          \\\r\n   (option) == CURLOPT_MAIL_RCPT ||                                           \\\r\n   (option) == CURLOPT_POSTQUOTE ||                                           \\\r\n   (option) == CURLOPT_PREQUOTE ||                                            \\\r\n   (option) == CURLOPT_PROXYHEADER ||                                         \\\r\n   (option) == CURLOPT_QUOTE ||                                               \\\r\n   (option) == CURLOPT_RESOLVE ||                                             \\\r\n   (option) == CURLOPT_TELNETOPTIONS ||                                       \\\r\n   0)\r\n\r\n/* groups of curl_easy_getinfo infos that take the same type of argument */\r\n\r\n/* evaluates to true if info expects a pointer to char * argument */\r\n#define _curl_is_string_info(info)                                            \\\r\n  (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG)\r\n\r\n/* evaluates to true if info expects a pointer to long argument */\r\n#define _curl_is_long_info(info)                                              \\\r\n  (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE)\r\n\r\n/* evaluates to true if info expects a pointer to double argument */\r\n#define _curl_is_double_info(info)                                            \\\r\n  (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST)\r\n\r\n/* true if info expects a pointer to struct curl_slist * argument */\r\n#define _curl_is_slist_info(info)                                       \\\r\n  (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST))\r\n\r\n/* true if info expects a pointer to struct curl_tlssessioninfo * argument */\r\n#define _curl_is_tlssessioninfo_info(info)                              \\\r\n  (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION))\r\n\r\n/* true if info expects a pointer to struct curl_certinfo * argument */\r\n#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO)\r\n\r\n/* true if info expects a pointer to struct curl_socket_t argument */\r\n#define _curl_is_socket_info(info)                                            \\\r\n  (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T)\r\n\r\n/* true if info expects a pointer to curl_off_t argument */\r\n#define _curl_is_off_t_info(info)                                             \\\r\n  (CURLINFO_OFF_T < (info))\r\n\r\n\r\n/* typecheck helpers -- check whether given expression has requested type*/\r\n\r\n/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros,\r\n * otherwise define a new macro. Search for __builtin_types_compatible_p\r\n * in the GCC manual.\r\n * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is\r\n * the actual expression passed to the curl_easy_setopt macro. This\r\n * means that you can only apply the sizeof and __typeof__ operators, no\r\n * == or whatsoever.\r\n */\r\n\r\n/* XXX: should evaluate to true if expr is a pointer */\r\n#define _curl_is_any_ptr(expr)                                                \\\r\n  (sizeof(expr) == sizeof(void *))\r\n\r\n/* evaluates to true if expr is NULL */\r\n/* XXX: must not evaluate expr, so this check is not accurate */\r\n#define _curl_is_NULL(expr)                                                   \\\r\n  (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL)))\r\n\r\n/* evaluates to true if expr is type*, const type* or NULL */\r\n#define _curl_is_ptr(expr, type)                                              \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   __builtin_types_compatible_p(__typeof__(expr), type *) ||                  \\\r\n   __builtin_types_compatible_p(__typeof__(expr), const type *))\r\n\r\n/* evaluates to true if expr is one of type[], type*, NULL or const type* */\r\n#define _curl_is_arr(expr, type)                                              \\\r\n  (_curl_is_ptr((expr), type) ||                                              \\\r\n   __builtin_types_compatible_p(__typeof__(expr), type []))\r\n\r\n/* evaluates to true if expr is a string */\r\n#define _curl_is_string(expr)                                                 \\\r\n  (_curl_is_arr((expr), char) ||                                              \\\r\n   _curl_is_arr((expr), signed char) ||                                       \\\r\n   _curl_is_arr((expr), unsigned char))\r\n\r\n/* evaluates to true if expr is a long (no matter the signedness)\r\n * XXX: for now, int is also accepted (and therefore short and char, which\r\n * are promoted to int when passed to a variadic function) */\r\n#define _curl_is_long(expr)                                                   \\\r\n  (__builtin_types_compatible_p(__typeof__(expr), long) ||                    \\\r\n   __builtin_types_compatible_p(__typeof__(expr), signed long) ||             \\\r\n   __builtin_types_compatible_p(__typeof__(expr), unsigned long) ||           \\\r\n   __builtin_types_compatible_p(__typeof__(expr), int) ||                     \\\r\n   __builtin_types_compatible_p(__typeof__(expr), signed int) ||              \\\r\n   __builtin_types_compatible_p(__typeof__(expr), unsigned int) ||            \\\r\n   __builtin_types_compatible_p(__typeof__(expr), short) ||                   \\\r\n   __builtin_types_compatible_p(__typeof__(expr), signed short) ||            \\\r\n   __builtin_types_compatible_p(__typeof__(expr), unsigned short) ||          \\\r\n   __builtin_types_compatible_p(__typeof__(expr), char) ||                    \\\r\n   __builtin_types_compatible_p(__typeof__(expr), signed char) ||             \\\r\n   __builtin_types_compatible_p(__typeof__(expr), unsigned char))\r\n\r\n/* evaluates to true if expr is of type curl_off_t */\r\n#define _curl_is_off_t(expr)                                                  \\\r\n  (__builtin_types_compatible_p(__typeof__(expr), curl_off_t))\r\n\r\n/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */\r\n/* XXX: also check size of an char[] array? */\r\n#define _curl_is_error_buffer(expr)                                           \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   __builtin_types_compatible_p(__typeof__(expr), char *) ||                  \\\r\n   __builtin_types_compatible_p(__typeof__(expr), char[]))\r\n\r\n/* evaluates to true if expr is of type (const) void* or (const) FILE* */\r\n#if 0\r\n#define _curl_is_cb_data(expr)                                                \\\r\n  (_curl_is_ptr((expr), void) ||                                              \\\r\n   _curl_is_ptr((expr), FILE))\r\n#else /* be less strict */\r\n#define _curl_is_cb_data(expr)                                                \\\r\n  _curl_is_any_ptr(expr)\r\n#endif\r\n\r\n/* evaluates to true if expr is of type FILE* */\r\n#define _curl_is_FILE(expr)                                             \\\r\n  (_curl_is_NULL(expr) ||                                              \\\r\n   (__builtin_types_compatible_p(__typeof__(expr), FILE *)))\r\n\r\n/* evaluates to true if expr can be passed as POST data (void* or char*) */\r\n#define _curl_is_postfields(expr)                                             \\\r\n  (_curl_is_ptr((expr), void) ||                                              \\\r\n   _curl_is_arr((expr), char) ||                                              \\\r\n   _curl_is_arr((expr), unsigned char))\r\n\r\n/* FIXME: the whole callback checking is messy...\r\n * The idea is to tolerate char vs. void and const vs. not const\r\n * pointers in arguments at least\r\n */\r\n/* helper: __builtin_types_compatible_p distinguishes between functions and\r\n * function pointers, hide it */\r\n#define _curl_callback_compatible(func, type)                                 \\\r\n  (__builtin_types_compatible_p(__typeof__(func), type) ||                    \\\r\n   __builtin_types_compatible_p(__typeof__(func) *, type))\r\n\r\n/* evaluates to true if expr is of type curl_resolver_start_callback */\r\n#define _curl_is_resolver_start_callback(expr)       \\\r\n  (_curl_is_NULL(expr) || \\\r\n   _curl_callback_compatible((expr), curl_resolver_start_callback))\r\n\r\n/* evaluates to true if expr is of type curl_read_callback or \"similar\" */\r\n#define _curl_is_read_cb(expr)                                          \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), __typeof__(fread) *) ||                  \\\r\n   _curl_callback_compatible((expr), curl_read_callback) ||                   \\\r\n   _curl_callback_compatible((expr), _curl_read_callback1) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_read_callback2) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_read_callback3) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_read_callback4) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_read_callback5) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_read_callback6))\r\ntypedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *);\r\ntypedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *);\r\ntypedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *);\r\ntypedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *);\r\ntypedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *);\r\ntypedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *);\r\n\r\n/* evaluates to true if expr is of type curl_write_callback or \"similar\" */\r\n#define _curl_is_write_cb(expr)                                               \\\r\n  (_curl_is_read_cb(expr) ||                                            \\\r\n   _curl_callback_compatible((expr), __typeof__(fwrite) *) ||                 \\\r\n   _curl_callback_compatible((expr), curl_write_callback) ||                  \\\r\n   _curl_callback_compatible((expr), _curl_write_callback1) ||                \\\r\n   _curl_callback_compatible((expr), _curl_write_callback2) ||                \\\r\n   _curl_callback_compatible((expr), _curl_write_callback3) ||                \\\r\n   _curl_callback_compatible((expr), _curl_write_callback4) ||                \\\r\n   _curl_callback_compatible((expr), _curl_write_callback5) ||                \\\r\n   _curl_callback_compatible((expr), _curl_write_callback6))\r\ntypedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *);\r\ntypedef size_t (*_curl_write_callback2)(const char *, size_t, size_t,\r\n                                       const void *);\r\ntypedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *);\r\ntypedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *);\r\ntypedef size_t (*_curl_write_callback5)(const void *, size_t, size_t,\r\n                                       const void *);\r\ntypedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *);\r\n\r\n/* evaluates to true if expr is of type curl_ioctl_callback or \"similar\" */\r\n#define _curl_is_ioctl_cb(expr)                                         \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_ioctl_callback) ||                  \\\r\n   _curl_callback_compatible((expr), _curl_ioctl_callback1) ||                \\\r\n   _curl_callback_compatible((expr), _curl_ioctl_callback2) ||                \\\r\n   _curl_callback_compatible((expr), _curl_ioctl_callback3) ||                \\\r\n   _curl_callback_compatible((expr), _curl_ioctl_callback4))\r\ntypedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *);\r\ntypedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *);\r\ntypedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *);\r\ntypedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *);\r\n\r\n/* evaluates to true if expr is of type curl_sockopt_callback or \"similar\" */\r\n#define _curl_is_sockopt_cb(expr)                                       \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_sockopt_callback) ||                \\\r\n   _curl_callback_compatible((expr), _curl_sockopt_callback1) ||              \\\r\n   _curl_callback_compatible((expr), _curl_sockopt_callback2))\r\ntypedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype);\r\ntypedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t,\r\n                                      curlsocktype);\r\n\r\n/* evaluates to true if expr is of type curl_opensocket_callback or\r\n   \"similar\" */\r\n#define _curl_is_opensocket_cb(expr)                                    \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_opensocket_callback) ||             \\\r\n   _curl_callback_compatible((expr), _curl_opensocket_callback1) ||           \\\r\n   _curl_callback_compatible((expr), _curl_opensocket_callback2) ||           \\\r\n   _curl_callback_compatible((expr), _curl_opensocket_callback3) ||           \\\r\n   _curl_callback_compatible((expr), _curl_opensocket_callback4))\r\ntypedef curl_socket_t (*_curl_opensocket_callback1)\r\n  (void *, curlsocktype, struct curl_sockaddr *);\r\ntypedef curl_socket_t (*_curl_opensocket_callback2)\r\n  (void *, curlsocktype, const struct curl_sockaddr *);\r\ntypedef curl_socket_t (*_curl_opensocket_callback3)\r\n  (const void *, curlsocktype, struct curl_sockaddr *);\r\ntypedef curl_socket_t (*_curl_opensocket_callback4)\r\n  (const void *, curlsocktype, const struct curl_sockaddr *);\r\n\r\n/* evaluates to true if expr is of type curl_progress_callback or \"similar\" */\r\n#define _curl_is_progress_cb(expr)                                      \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_progress_callback) ||               \\\r\n   _curl_callback_compatible((expr), _curl_progress_callback1) ||             \\\r\n   _curl_callback_compatible((expr), _curl_progress_callback2))\r\ntypedef int (*_curl_progress_callback1)(void *,\r\n    double, double, double, double);\r\ntypedef int (*_curl_progress_callback2)(const void *,\r\n    double, double, double, double);\r\n\r\n/* evaluates to true if expr is of type curl_debug_callback or \"similar\" */\r\n#define _curl_is_debug_cb(expr)                                         \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_debug_callback) ||                  \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback1) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback2) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback3) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback4) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback5) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback6) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback7) ||                \\\r\n   _curl_callback_compatible((expr), _curl_debug_callback8))\r\ntypedef int (*_curl_debug_callback1) (CURL *,\r\n    curl_infotype, char *, size_t, void *);\r\ntypedef int (*_curl_debug_callback2) (CURL *,\r\n    curl_infotype, char *, size_t, const void *);\r\ntypedef int (*_curl_debug_callback3) (CURL *,\r\n    curl_infotype, const char *, size_t, void *);\r\ntypedef int (*_curl_debug_callback4) (CURL *,\r\n    curl_infotype, const char *, size_t, const void *);\r\ntypedef int (*_curl_debug_callback5) (CURL *,\r\n    curl_infotype, unsigned char *, size_t, void *);\r\ntypedef int (*_curl_debug_callback6) (CURL *,\r\n    curl_infotype, unsigned char *, size_t, const void *);\r\ntypedef int (*_curl_debug_callback7) (CURL *,\r\n    curl_infotype, const unsigned char *, size_t, void *);\r\ntypedef int (*_curl_debug_callback8) (CURL *,\r\n    curl_infotype, const unsigned char *, size_t, const void *);\r\n\r\n/* evaluates to true if expr is of type curl_ssl_ctx_callback or \"similar\" */\r\n/* this is getting even messier... */\r\n#define _curl_is_ssl_ctx_cb(expr)                                       \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_ssl_ctx_callback) ||                \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) ||              \\\r\n   _curl_callback_compatible((expr), _curl_ssl_ctx_callback8))\r\ntypedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *);\r\ntypedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *);\r\ntypedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *);\r\ntypedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *,\r\n                                            const void *);\r\n#ifdef HEADER_SSL_H\r\n/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX\r\n * this will of course break if we're included before OpenSSL headers...\r\n */\r\ntypedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *);\r\ntypedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *);\r\ntypedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *);\r\ntypedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX,\r\n                                           const void *);\r\n#else\r\ntypedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5;\r\ntypedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6;\r\ntypedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7;\r\ntypedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8;\r\n#endif\r\n\r\n/* evaluates to true if expr is of type curl_conv_callback or \"similar\" */\r\n#define _curl_is_conv_cb(expr)                                          \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_conv_callback) ||                   \\\r\n   _curl_callback_compatible((expr), _curl_conv_callback1) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_conv_callback2) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_conv_callback3) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_conv_callback4))\r\ntypedef CURLcode (*_curl_conv_callback1)(char *, size_t length);\r\ntypedef CURLcode (*_curl_conv_callback2)(const char *, size_t length);\r\ntypedef CURLcode (*_curl_conv_callback3)(void *, size_t length);\r\ntypedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);\r\n\r\n/* evaluates to true if expr is of type curl_seek_callback or \"similar\" */\r\n#define _curl_is_seek_cb(expr)                                          \\\r\n  (_curl_is_NULL(expr) ||                                                     \\\r\n   _curl_callback_compatible((expr), curl_seek_callback) ||                   \\\r\n   _curl_callback_compatible((expr), _curl_seek_callback1) ||                 \\\r\n   _curl_callback_compatible((expr), _curl_seek_callback2))\r\ntypedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);\r\ntypedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);\r\n\r\n\r\n#endif /* __CURL_TYPECHECK_GCC_H */\r\n"
  },
  {
    "path": "Chapter06/code/curl/urlapi.h",
    "content": "#ifndef __CURL_URLAPI_H\r\n#define __CURL_URLAPI_H\r\n/***************************************************************************\r\n *                                  _   _ ____  _\r\n *  Project                     ___| | | |  _ \\| |\r\n *                             / __| | | | |_) | |\r\n *                            | (__| |_| |  _ <| |___\r\n *                             \\___|\\___/|_| \\_\\_____|\r\n *\r\n * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.\r\n *\r\n * This software is licensed as described in the file COPYING, which\r\n * you should have received as part of this distribution. The terms\r\n * are also available at https://curl.haxx.se/docs/copyright.html.\r\n *\r\n * You may opt to use, copy, modify, merge, publish, distribute and/or sell\r\n * copies of the Software, and permit persons to whom the Software is\r\n * furnished to do so, under the terms of the COPYING file.\r\n *\r\n * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\r\n * KIND, either express or implied.\r\n *\r\n ***************************************************************************/\r\n\r\n#include \"curl.h\"\r\n\r\n#ifdef  __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n/* the error codes for the URL API */\r\ntypedef enum {\r\n  CURLUE_OK,\r\n  CURLUE_BAD_HANDLE,          /* 1 */\r\n  CURLUE_BAD_PARTPOINTER,     /* 2 */\r\n  CURLUE_MALFORMED_INPUT,     /* 3 */\r\n  CURLUE_BAD_PORT_NUMBER,     /* 4 */\r\n  CURLUE_UNSUPPORTED_SCHEME,  /* 5 */\r\n  CURLUE_URLDECODE,           /* 6 */\r\n  CURLUE_OUT_OF_MEMORY,       /* 7 */\r\n  CURLUE_USER_NOT_ALLOWED,    /* 8 */\r\n  CURLUE_UNKNOWN_PART,        /* 9 */\r\n  CURLUE_NO_SCHEME,           /* 10 */\r\n  CURLUE_NO_USER,             /* 11 */\r\n  CURLUE_NO_PASSWORD,         /* 12 */\r\n  CURLUE_NO_OPTIONS,          /* 13 */\r\n  CURLUE_NO_HOST,             /* 14 */\r\n  CURLUE_NO_PORT,             /* 15 */\r\n  CURLUE_NO_QUERY,            /* 16 */\r\n  CURLUE_NO_FRAGMENT          /* 17 */\r\n} CURLUcode;\r\n\r\ntypedef enum {\r\n  CURLUPART_URL,\r\n  CURLUPART_SCHEME,\r\n  CURLUPART_USER,\r\n  CURLUPART_PASSWORD,\r\n  CURLUPART_OPTIONS,\r\n  CURLUPART_HOST,\r\n  CURLUPART_PORT,\r\n  CURLUPART_PATH,\r\n  CURLUPART_QUERY,\r\n  CURLUPART_FRAGMENT\r\n} CURLUPart;\r\n\r\n#define CURLU_DEFAULT_PORT (1<<0)       /* return default port number */\r\n#define CURLU_NO_DEFAULT_PORT (1<<1)    /* act as if no port number was set,\r\n                                           if the port number matches the\r\n                                           default for the scheme */\r\n#define CURLU_DEFAULT_SCHEME (1<<2)     /* return default scheme if\r\n                                           missing */\r\n#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */\r\n#define CURLU_PATH_AS_IS (1<<4)         /* leave dot sequences */\r\n#define CURLU_DISALLOW_USER (1<<5)      /* no user+password allowed */\r\n#define CURLU_URLDECODE (1<<6)          /* URL decode on get */\r\n#define CURLU_URLENCODE (1<<7)          /* URL encode on set */\r\n#define CURLU_APPENDQUERY (1<<8)        /* append a form style part */\r\n#define CURLU_GUESS_SCHEME (1<<9)       /* legacy curl-style guessing */\r\n\r\ntypedef struct Curl_URL CURLU;\r\n\r\n/*\r\n * curl_url() creates a new CURLU handle and returns a pointer to it.\r\n * Must be freed with curl_url_cleanup().\r\n */\r\nCURL_EXTERN CURLU *curl_url(void);\r\n\r\n/*\r\n * curl_url_cleanup() frees the CURLU handle and related resources used for\r\n * the URL parsing. It will not free strings previously returned with the URL\r\n * API.\r\n */\r\nCURL_EXTERN void curl_url_cleanup(CURLU *handle);\r\n\r\n/*\r\n * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new\r\n * handle must also be freed with curl_url_cleanup().\r\n */\r\nCURL_EXTERN CURLU *curl_url_dup(CURLU *in);\r\n\r\n/*\r\n * curl_url_get() extracts a specific part of the URL from a CURLU\r\n * handle. Returns error code. The returned pointer MUST be freed with\r\n * curl_free() afterwards.\r\n */\r\nCURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what,\r\n                                   char **part, unsigned int flags);\r\n\r\n/*\r\n * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns\r\n * error code. The passed in string will be copied. Passing a NULL instead of\r\n * a part string, clears that part.\r\n */\r\nCURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what,\r\n                                   const char *part, unsigned int flags);\r\n\r\n\r\n#ifdef __cplusplus\r\n} /* end of extern \"C\" */\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "Chapter06/code/包分片逻辑.cpp",
    "content": "//与客户端交互协议包头\ntypedef struct tagNtPkgHead\n{\n    unsigned char   bStartFlag;     //协议包起始标志 0xFF\n    unsigned char   bVer;           //版本号\n    unsigned char   bEncryptFlag;   //加密标志(如果不加密,则为0)\n    unsigned char   bFrag;          //是否有包分片(1 有包分片 0 无包分片)\n    unsigned short  wLen;           //总包长\n    unsigned short  wCmd;           //命令号\n    unsigned short  wSeq;           //包的序列号,业务使用\n    unsigned short  wCrc;           //Crc16校验码\n    unsigned int    dwSID;          //会话ID\n    unsigned short  wTotal;         //有包分片时，分片总数\n    unsigned short  wCurSeq;        //有包分片时，分片序号，从0开始，无分片时也为0\n} NtPkgHead, *PNtPkgHead;\n\nUINT CSocketClient::RecvDataThreadProc(LPVOID lpParam)\n{\n    LOG_NORMAL(\"Start recv data thread.\");\n    DWORD           dwWaitResult;\n    std::string     strPkg;\n    //临时存放一个完整的包数据的变量\n    std::string     strTotalPkg;\n    unsigned short  uPkgLen = 0;\n    unsigned int    uBodyLen = 0;\n    unsigned int    uTotalPkgLen = 0;\n    unsigned int    uCmd = 0;\n    NtPkgHead       pkgHead;\n    unsigned short  uTotal = 0;\n    //记录上一次的包分片序号，包分片序号从0开始\n    unsigned short  uCurSeq = 0;\n    int             nWaitTimeout = 1;\n\n    CSocketClient* pSocketClient = (CSocketClient*)lpParam;\n\n    while (!m_bExit)\n    {      \n        //检测是否有数据\n        if (!pSocketClient->CheckReceivedData())\n        {\n            //休眠10豪秒\n            Sleep(10);\n            continue;\n        }\n            \n        //接收数据，并放入pSocketClient->m_strRecvBuf中\n        if (!pSocketClient->Recv())\n        {\n            LOG_ERROR(\"Recv data error\");\n                \n            //收数据出错，清空接收缓冲区，可以做一些关闭连接、重连等动作，\n            pSocketClient->m_strRecvBuf.clear();\n\n            Reconnect();\n            continue;\n        }\n\n        //一定要放在一个循环里面解包，因为可能一片数据中有多个包，\n        while (true)\n        {\n            //判断当前收到的数据是否够一个包头大小\n            if (pSocketClient->m_strRecvBuf.length() < sizeof(NtPkgHead))\n                break;\n\n            memset(&pkgHead, 0, sizeof(pkgHead));\n            memcpy_s(&pkgHead, sizeof(pkgHead), pSocketClient->m_strRecvBuf.c_str(), sizeof(pkgHead));\n\t\t\t\n\t\t\t//对包消息头检验\n            if (!CheckPkgHead(&pkgHead))\n            {\n                //如果包头检验不通过，缓冲区里面的数据已经是脏数据了，直接清空掉，\n                //可以做一些关闭连接并重连的动作             \n                LOG_ERROR(\"Check package head error, discard data %d bytes\", (int)pSocketClient->m_strRecvBuf.length());\n                \n                pSocketClient->m_strRecvBuf.clear();\n\n                Reconnect();\n                break;\n            }\n\n            //判断当前数据是否够一个整包的大小\n            uPkgLen = ntohs(pkgHead.wLen);\n            if (pSocketClient->m_strRecvBuf.length() < uPkgLen)\n                break;\n\n            strPkg.clear();\n            strPkg.append(pSocketClient->m_strRecvBuf.c_str(), uPkgLen);\n\n            //从收取缓冲区中移除已经处理的数据部分\n            pSocketClient->m_strRecvBuf.erase(0, uPkgLen);\n       \n            uTotal = ::ntohs(pkgHead.wTotal);\n            uCurSeq = ::ntohs(pkgHead.wCurSeq);\n            //无分片或第一个分片\n            if (uCurSeq == 0)\n            {\n                strTotalPkg.clear();\n                uTotalPkgLen = 0;\n            }\n\n            uBodyLen = uPkgLen - sizeof(NtPkgHead);\n            uTotalPkgLen += uBodyLen;\n            strTotalPkg.append(strPkg.data() + sizeof(NtPkgHead), uBodyLen);\n\n            //无分包 或 分包的最后一个包 则将组装后的包发送出去\n            if (uTotal == 0 || (uTotal != 0 && uTotal == uCurSeq + 1))\n            {\n                uCmd = ::ntohs(pkgHead.wCmd);\n\n                //ProxyPackage是解析出来的业务包定义\n                ProxyPackage proxyPackage;\n                //拷贝业务号\n                proxyPackage.nCmd = uCmd;\n                //拷贝包长度\n                proxyPackage.nLength = uTotalPkgLen;\n                //拷贝包体内容\n                proxyPackage.pszJson = new char[uTotalPkgLen];\n                memset(proxyPackage.pszJson, 0, uTotalPkgLen * sizeof(char));\n                memcpy_s(proxyPackage.pszJson, uTotalPkgLen, strTotalPkg.c_str(), strTotalPkg.length());\n\n                //将一个完整的包交给业务处理\n                pSocketClient->m_pNetProxy->AddPackage((const char*)&proxyPackage, sizeof(proxyPackage));\n            }\n        }// end inner-while-loop\n\n\n    }// end outer-while-loop\n\n\n    LOG_NORMAL(\"Exit recv data thread.\");\n\n    return 0;\n}"
  },
  {
    "path": "Chapter07/codes/timerV1/Timer.cpp",
    "content": "#include \"Timer.h\"\n#include <time.h>\n\nstd::atomic<int> Timer::s_initialId = 0;\n\nTimer::Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback)\n{\n    m_repeatedTimes = repeatedTimes;\n    m_interval = interval;\n\n    //当前时间加上触发间隔得到下一次的过期时间\n    m_expiredTime = (int64_t)time(nullptr) + interval;\n\n    m_callback = timerCallback;\n\n    //生成一个唯一的id\n    ++s_initialId;\n    m_id = s_initialId;\n}\n\nbool Timer::isExpired()\n{\n    int64_t now = time(nullptr);\n    return now >= m_expiredTime;\n}\n\nvoid Timer::run()\n{\n    m_callback();\n\n    if (m_repeatedTimes >= 1)\n    {\n        --m_repeatedTimes;\n    }\n\n    m_expiredTime += m_interval;\n}"
  },
  {
    "path": "Chapter07/codes/timerV1/Timer.h",
    "content": "#ifndef __TIMER_H__\n#define __TIMER_H__\n\n#include <functional>\n\ntypedef std::function<void()> TimerCallback;\n\nclass Timer\n{\npublic:\n    /**\n     * @param repeatedTimes 定时器重复次数，设置为-1表示一直重复下去\n     * @param interval      下一次触发的时间间隔\n     * @param timerCallback 定时器触发后的回调函数\n     */\n    Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback);\n    ~Timer();\n\n    int64_t getId() const\n    {\n        return m_id;\n    }\n\n    int64_t getExpiredTime() const\n    {\n        return m_expiredTime;\n    }\n\n    bool isExpired();\n\n    int32_t getRepeatedTimes() const\n    {\n        return m_repeatedTimes;\n    }\n\n    void run();\n\n    //其他实现暂且省略\n\nprivate:\n    //定时器的id，唯一标识一个定时器\n    int64_t                     m_id;\n    //定时器的到期时间\n    time_t                      m_expiredTime;\n    //定时器重复触发的次数\n    int32_t                     m_repeatedTimes;\n    //定时器触发后的回调函数\n    TimerCallback               m_callback;\n    //触发时间间隔                \n    int64_t                     m_interval;\n\n    //定时器id基准值\n    static std::atomic<int>     s_initialId;\n};\n\n\n#endif //!__TIMER_H__"
  },
  {
    "path": "Chapter07/codes/timerV1/TimerManager.cpp",
    "content": "#include \"TimerManager.h\"\n    \nint64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback)\n{\n    Timer* pTimer = new Timer(repeatedCount, interval, timerCallback);\n\n    m_listTimers.push_back(pTimer);\n\n    //对定时器对象按过期时间从小到大排序\n    m_listTimers.sort(TimerCompare());\n\n    return pTimer->getId();\n}\n\nbool TimerManager::removeTimer(int64_t timerId)\n{\n    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); ++iter)\n    {\n        if ((*iter)->getId() == timerId)\n        {\n                m_listTimers.erase(iter);\n                return true;\n        }\n    }\n\n    return false;\n}\n\nvoid TimerManager::checkAndHandleTimers()\n{\n    //遍历过程中是否调整了部分定时器的过期时间\n    bool adjusted = false;\n    Timer* deletedTimer;   \n    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); )\n    {\n        if ((*iter)->isExpired())\n        {\n            (*iter)->run();\n            \n            if ((*iter)->getRepeatedTimes() == 0)\n            {\n                //定时器不需要再触发时从集合中移除该对象\n                deletedTimer = *iter;\n                iter = m_listTimers.erase(iter);\n                delete deletedTimer;\n                continue;\n            }\n            else \n            {\n                ++iter;\n                //标记下集合中有定时器调整了过期时间\n                adjusted = true;\n            }\n        }\n        else\n        {\n            //找到大于当前系统时间的定时器对象就不需要继续往下检查了，退出循环\n            break;\n        }// end if      \n    }// end for-loop\n\n    //由于调整了部分定时器的过期时间，需要重新排序一下\n    if (adjusted)\n    {\n        m_listTimers.sort(TimerCompare());\n    }\n}"
  },
  {
    "path": "Chapter07/codes/timerV1/TimerManager.h",
    "content": "#ifndef __TIMER_MANAGER_H__\n#define __TIMER_MANAGER_H__\n\n#include <stdint.h>\n#include <list>\n\n#include \"Timer.h\"\n\n//std::list<Timer*> m_listTimers;\n\n// void EventLoop::check_and_handle_timers()\n// {\n//     for (auto& timer : m_listTimers)\n//     {\n//         if (timer->isExpired())\n//         {\n//             timer->run();\n//         }\n//     }\n// }\n\n\nstruct TimerCompare  \n{  \n    bool operator () (const Timer* lhs, const Timer* rhs)  \n    {  \n        return lhs->getExpiredTime() <  rhs->getExpiredTime();\n    }\n}; \n\nvoid defaultTimerCallback()\n{\n\n}\n\nclass TimerManager\n{\npublic:\n    TimerManager() = default;\n    ~TimerManager() = default;\n\n    /** 添加定时器\n     * @param repeatedCount 重复次数\n     * @param 触发间隔\n     * @\n     * @return 返回创建成功的定时器id\n     */\n    int64_t addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback);\n\n    /** 移除指定id的定时器\n     * @param timerId 待移除的定时器id\n     * @return 成功移除定时器返回true，反之返回false\n     */\n    bool removeTimer(int64_t timerId);\n\n    /** 检测定时器是否到期，如果到期则触发定时器函数\n     */\n    void checkAndHandleTimers();\n\n\nprivate:\n    std::list<Timer*> m_listTimers;\n\n};\n\n#endif //!__TIMER_MANAGER_H__"
  },
  {
    "path": "Chapter07/codes/timerV2/Timer.cpp",
    "content": "#include \"Timer.h\"\n#include <time.h>\n\nstd::atomic<int> Timer::s_initialId = 0;\n\nTimer::Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback)\n{\n    m_repeatedTimes = repeatedTimes;\n    m_interval = interval;\n\n    //当前时间加上触发间隔得到下一次的过期时间\n    m_expiredTime = (int64_t)time(nullptr) + interval;\n\n    m_callback = timerCallback;\n\n    //生成一个唯一的id\n    ++s_initialId;\n    m_id = s_initialId;\n}\n\nbool Timer::isExpired()\n{\n    int64_t now = time(nullptr);\n    return now >= m_expiredTime;\n}\n\nvoid Timer::run()\n{\n    m_callback();\n\n    if (m_repeatedTimes >= 1)\n    {\n        --m_repeatedTimes;\n    }\n\n    m_expiredTime += m_interval;\n}"
  },
  {
    "path": "Chapter07/codes/timerV2/Timer.h",
    "content": "#ifndef __TIMER_H__\n#define __TIMER_H__\n\n#include <functional>\n\ntypedef std::function<void()> TimerCallback;\n\nclass Timer\n{\npublic:\n    /**\n     * @param repeatedTimes 定时器重复次数，设置为-1表示一直重复下去\n     * @param interval      下一次触发的时间间隔\n     * @param timerCallback 定时器触发后的回调函数\n     */\n    Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback);\n    ~Timer();\n\n    int64_t getId() const\n    {\n        return m_id;\n    }\n\n    int64_t getExpiredTime() const\n    {\n        return m_expiredTime;\n    }\n\n    bool isExpired();\n\n    int32_t getRepeatedTimes() const\n    {\n        return m_repeatedTimes;\n    }\n\n    void run();\n\n    //其他实现暂且省略\n\nprivate:\n    //定时器的id，唯一标识一个定时器\n    int64_t                     m_id;\n    //定时器的到期时间\n    time_t                      m_expiredTime;\n    //定时器重复触发的次数\n    int32_t                     m_repeatedTimes;\n    //定时器触发后的回调函数\n    TimerCallback               m_callback;\n    //触发时间间隔                \n    int64_t                     m_interval;\n\n    //定时器id基准值\n    static std::atomic<int>     s_initialId;\n};\n\n\n#endif //!__TIMER_H__"
  },
  {
    "path": "Chapter07/codes/timerV2/TimerManager.cpp",
    "content": "#include \"TimerManager.h\"\n    \nint64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback)\n{\n    Timer* pTimer = new Timer(repeatedCount, interval, timerCallback);\n    int64_t timerId = pTimer->getId();\n\n    //插入时会自动按TimerCompare对象进行排序\n    m_mapTimers[timerId] = pTimer;\n\n    return timerId;\n}\n\nbool TimerManager::removeTimer(int64_t timerId)\n{\n    auto iter = m_mapTimers.find(timerId);\n    if (iter != m_mapTimers.end())\n    {\n        m_mapTimers.erase(iter);\n        return true;\n    }\n\n    return false;\n}\n\nvoid TimerManager::checkAndHandleTimers()\n{\n    //遍历过程中是否调整了部分定时器的过期时间\n    bool adjusted = false;\n    Timer* deletedTimer;\n    for (auto iter = m_mapTimers.begin(); iter != m_mapTimers.end(); )\n    {\n        if (iter->second->isExpired())\n        {\n            iter->second->run();\n            \n            if (iter->second->getRepeatedTimes() == 0)\n            {\n                //定时器不需要再触发时从集合中移除该对象\n                deletedTimer = *iter;\n                iter = m_listTimers.erase(iter);\n                delete deletedTimer;\n                continue;\n            }\n            else \n            {\n                ++iter;\n                //标记下集合中有定时器调整了过期时间\n                adjusted = true;\n            }\n        }\n        else\n        {\n            //找到大于当前系统时间的定时器对象就不需要继续往下检查了，退出循环\n            break;\n        }   \n    }\n\n    //由于调整了部分定时器的过期时间，需要重新排序一下\n    if (adjusted)\n    {\n        std::map<int64_t, Timer*, TimerCompare> localMapTimers;    \n        for (const auto& iter : m_mapTimers)\n        {\n            localMapTimers[iter.first] = iter.second;\n        }\n\n        m_mapTimers.clear();\n        m_mapTimers.swap(localMapTimers);\n    }\n}"
  },
  {
    "path": "Chapter07/codes/timerV2/TimerManager.h",
    "content": "#ifndef __TIMER_MANAGER_H__\n#define __TIMER_MANAGER_H__\n\n#include <stdint.h>\n#include <map>\n\n#include \"Timer.h\"\n\n//std::list<Timer*> m_listTimers;\n\n// void EventLoop::check_and_handle_timers()\n// {\n//     for (auto& timer : m_listTimers)\n//     {\n//         if (timer->isExpired())\n//         {\n//             timer->run();\n//         }\n//     }\n// }\n\n\nstruct TimerCompare  \n{  \n    bool operator () (const Timer* lhs, const Timer* rhs)  \n    {  \n        return lhs->getExpiredTime() <  rhs->getExpiredTime();\n    }\n}; \n\nvoid defaultTimerCallback()\n{\n\n}\n\nclass TimerManager\n{\npublic:\n    TimerManager() = default;\n    ~TimerManager() = default;\n\n    /** 添加定时器\n     * @param repeatedCount 重复次数\n     * @param 触发间隔\n     * @\n     * @return 返回创建成功的定时器id\n     */\n    int64_t addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback);\n\n    /** 移除指定id的定时器\n     * @param timerId 待移除的定时器id\n     * @return 成功移除定时器返回true，反之返回false\n     */\n    bool removeTimer(int64_t timerId);\n\n    /** 检测定时器是否到期，如果到期则触发定时器函数\n     */\n    void checkAndHandleTimers();\n\n\nprivate:\n    //key是定时器id，value是定时器对象，注意模板的第三个参数是自定义排序对象TimerCompare\n    std::map<int64_t, Timer*, TimerCompare>   m_mapTimers;\n};\n\n#endif //!__TIMER_MANAGER_H__"
  },
  {
    "path": "Chapter07/codes/timerV3/Timer.cpp",
    "content": "#include \"Timer.h\"\n#include <time.h>\n\nstd::atomic<int> Timer::s_initialId = 0;\n\nTimer::Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback)\n{\n    m_repeatedTimes = repeatedTimes;\n    m_interval = interval;\n\n    //当前时间加上触发间隔得到下一次的过期时间\n    m_expiredTime = (int64_t)time(nullptr) + interval;\n\n    m_callback = timerCallback;\n\n    //生成一个唯一的id\n    ++s_initialId;\n    m_id = s_initialId;\n}\n\nTimer::Timer(int32_t repeatedTimes, int64_t interval, int64_t expiredTime, int64_t slotBaseTime, const TimerCallback& timerCallback)\n{\n    m_repeatedTimes = repeatedTimes;\n    m_interval = interval;\n\n    m_expiredTime = expiredTime;\n\n    m_slotBaseTime = slotBaseTime;\n\n    m_callback = timerCallback;\n\n    //生成一个唯一的id\n    ++s_initialId;\n    m_id = s_initialId;\n}\n\nbool Timer::isExpired()\n{\n    int64_t now = time(nullptr);\n    return now >= m_expiredTime;\n}\n\nvoid Timer::run()\n{\n    m_callback();\n\n    if (m_repeatedTimes >= 1)\n    {\n        --m_repeatedTimes;\n    }\n\n    m_expiredTime += m_interval;\n}"
  },
  {
    "path": "Chapter07/codes/timerV3/Timer.h",
    "content": "#ifndef __TIMER_H__\n#define __TIMER_H__\n\n#include <functional>\n\ntypedef std::function<void()> TimerCallback;\n\nclass Timer\n{\npublic:\n    Timer() = default;\n    /**\n     * @param repeatedTimes 定时器重复次数，设置为-1表示一直重复下去\n     * @param interval      下一次触发的时间间隔\n     * @param timerCallback 定时器触发后的回调函数\n     */\n    Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback);\n\n    /**\n     * @param repeatedTimes 定时器重复次数，设置为-1表示一直重复下去\n     * @param expiredTime   下一次触发的时间\n     * @param slotBaseTime  定时器所在时间槽的基准时间\n     * @param timerCallback 定时器触发后的回调函数\n     */\n    Timer(int32_t repeatedTimes, int64_t interval, int64_t expiredTime, int64_t slotBaseTime, const TimerCallback& timerCallback);\n\n    ~Timer();\n\n    int64_t getId() const\n    {\n        return m_id;\n    }\n\n    int64_t getExpiredTime() const\n    {\n        return m_expiredTime;\n    }\n\n    bool isExpired();\n\n    int32_t getRepeatedTimes() const\n    {\n        return m_repeatedTimes;\n    }\n\n    int64_t getInterval() const\n    {\n        return m_interval;\n    }\n\n    int64_t getSlotBaseTime() const\n    {\n        return m_slotBaseTime;\n    }\n\n    void setSlotBaseTime(int64_t slotBaseTime)\n    {\n        m_slotBaseTime = slotBaseTime;\n    }\n\n    void run();\n\n    //其他实现暂且省略\n\nprivate:\n    //定时器的id，唯一标识一个定时器\n    int64_t                     m_id;\n    //定时器的到期时间\n    time_t                      m_expiredTime;\n    //定时器重复触发的次数\n    int32_t                     m_repeatedTimes;\n    //定时器触发后的回调函数\n    TimerCallback               m_callback;\n    //触发时间间隔                \n    int64_t                     m_interval;\n    //定时器对象所在的时间槽的基准时间\n    int64_t                     m_slotBaseTime;\n    \n\n    //定时器id基准值\n    static std::atomic<int>     s_initialId;\n};\n\n\n#endif //!__TIMER_H__"
  },
  {
    "path": "Chapter07/codes/timerV3/TimerManager.cpp",
    "content": "#include \"TimerManager.h\"\n\nTimerManager::TimerManager(int64_t slotInterval)\n{\n    m_slotInterval = slotInterval;\n}\n    \nint64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback)\n{\n    int64_t now = (int64_t)time(nullptr);\n    \n    //计算位于哪个槽中\n    int slotIndex = interval % m_slotInterval;\n\n    Timer* pTimer = new Timer(repeatedCount, interval, now + interval, timerCallback);\n    int64_t timerId = pTimer->getId();\n    \n    TimerList* pTimerList;\n    if (m_timeWheel.empty())\n    {      \n        //时间轮是空的\n        pTimerList = new TimerList();\n        if (slotIndex == 0)\n        {\n            //第一个元素         \n            m_timeWheel.push_back(pTimerList);\n        }\n        else\n        {\n            //不是第一个元素\n             m_timeWheel.resize(slotIndex + 1);\n             //在第slotIndex个槽中填充\n             m_timeWheel[slotIndex] = pTimerList;\n        }       \n    } \n    else if (m_timeWheel.size() > slotIndex + 1)\n    {\n        //时间轮不是空的，槽已经存在，但是链表不存在\n        pTimerList = m_timeWheel[slotIndex];\n        if (pTimerList == nullptr)\n        {\n            pTimerList = new TimerList();\n            m_timeWheel[slotIndex] = pTimerList;\n        }\n    }\n    else \n    {\n        //m_timeWheel.size() <= slotIndex + 1\n        //时间轮不是空的，但槽不存在\n        //扩展时间轮\n        std::vector<TimerList*> tmpTimeWheel = m_timeWheel;\n        m_timeWheel.clear();\n        //在尾部多扩展8个槽，减少下次扩展槽内存复制的几率\n        m_timeWheel.resize(slotIndex + 1 + 8);\n        int32_t tmpTimeWheelSize = tmpTimeWheel.size();\n        for (int32_t i = 0; i < tmpTimeWheelSize; ++i)\n        {\n            m_timeWheel[i] = tmpTimeWheel[i];\n        }\n\n        //补上扩展的槽\n        pTimerList = new TimerList();\n        m_timeWheel[slotIndex] = pTimerList;\n    }\n\n    pTimerList->push_back(pTimer);\n\n    return timerId;\n}\n\nbool TimerManager::removeTimer(Timer* timer)\n{\n    int32_t slotIndex = timer->getInterval() / m_slotInterval;\n    if (m_timeWheel.size() < slotIndex + 1)\n        return false;\n\n    TimerList* pTimerList = m_timeWheel[slotIndex];\n    if (pTimerList == nullptr)\n        return false;\n\n    for (auto iter = pTimerList->begin(); iter != pTimerList->end(); ++iter)\n    {\n        pTimerList->erase(iter);\n        return true;\n    }        \n\n    return false;\n}\n\nvoid TimerManager::checkAndHandleTimers()\n{\n    //先计算当前时间位于哪个槽中\n    int64_t now = (int64_t)time(nullptr);\n    \n    //取第一个槽的起始时间\n    if (m_timeWheel.empty())\n        return;\n\n    TimerList* pTimeList = m_timeWheel[0];\n    if (pTimeList == nullptr)\n        return;\n\n        \n\n}"
  },
  {
    "path": "Chapter07/codes/timerV3/TimerManager.h",
    "content": "#ifndef __TIMER_MANAGER_H__\n#define __TIMER_MANAGER_H__\n\n#include <stdint.h>\n#include <list>\n#include <vector>\n\n#include \"Timer.h\"\n\n\n\ntypedef std::list<Timer*> TimerList;\n\nvoid defaultTimerCallback()\n{\n\n}\n\nclass TimerManager\n{\npublic:\n    /**\n     * @param slotInterval 时间槽的时间长度\n     */\n    TimerManager(int64_t slotInterval);\n    ~TimerManager() = default;\n\n    /** 添加定时器\n     * @param repeatedCount 重复次数\n     * @param 触发间隔\n     * @\n     * @return 返回创建成功的定时器id\n     */\n    int64_t addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback);\n\n    /** 移除指定id的定时器\n     * @param timer 待移除的定时器指针\n     * @return 成功移除定时器返回true，反之返回false\n     */\n    bool removeTimer(Timer* timer);\n\n    /** 检测定时器是否到期，如果到期则触发定时器函数\n     */\n    void checkAndHandleTimers();\n\n\nprivate:\n    //时间轮对象\n    std::vector<TimerList*>   m_timeWheel;\n    //时间槽间隔\n    int64_t                   m_slotInterval;\n};\n\n#endif //!__TIMER_MANAGER_H__"
  },
  {
    "path": "Chapter07/redis-6.0.3/.github/workflows/ci.yml",
    "content": "name: CI\n\non: [push, pull_request]\n\njobs:\n  test-ubuntu-latest:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5\n        ./runtest --verbose\n    - name: module api test\n      run: ./runtest-moduleapi --verbose\n\n  build-ubuntu-old:\n    runs-on: ubuntu-16.04\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n\n  build-macos-latest:\n    runs-on: macos-latest\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/.github/workflows/daily.yml",
    "content": "name: Daily\n\non:\n  schedule:\n    - cron: '0 7 * * *'\n\njobs:\n  test-jemalloc:\n    runs-on: ubuntu-latest\n    timeout-minutes: 1200\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5\n        ./runtest --accurate --verbose\n    - name: module api test\n      run: ./runtest-moduleapi --verbose\n\n  test-libc-malloc:\n    runs-on: ubuntu-latest\n    timeout-minutes: 1200\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make MALLOC=libc\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5\n        ./runtest --accurate --verbose\n    - name: module api test\n      run: ./runtest-moduleapi --verbose\n\n  test-valgrind:\n    runs-on: ubuntu-latest\n    timeout-minutes: 14400\n    steps:\n    - uses: actions/checkout@v1\n    - name: make\n      run: make valgrind\n    - name: test\n      run: |\n        sudo apt-get install tcl8.5 valgrind -y\n        ./runtest --valgrind --verbose --clients 1\n    - name: module api test\n      run: ./runtest-moduleapi --valgrind --verbose --clients 1\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/.gitignore",
    "content": ".*.swp\n*.o\n*.xo\n*.so\n*.d\n*.log\ndump.rdb\nredis-benchmark\nredis-check-aof\nredis-check-rdb\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\nsrc/nodes.conf\ndeps/lua/src/lua\ndeps/lua/src/luac\ndeps/lua/src/liblua.a\n.make-*\n.prerequisites\n*.dSYM\nMakefile.dep\n.vscode/*\n.idea/*\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/00-RELEASENOTES",
    "content": "Redis 6.0 release notes\n=======================\n\n--------------------------------------------------------------------------------\nUpgrade urgency levels:\n\nLOW:      No need to upgrade unless there are new features you want to use.\nMODERATE: Program an upgrade of the server, but it's not urgent.\nHIGH:     There is a critical bug that may affect a subset of users. Upgrade!\nCRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.\nSECURITY: There are security fixes in the release.\n--------------------------------------------------------------------------------\n\n================================================================================\nRedis 6.0.3     Released Sat May 16 18:10:21 CEST 2020\n================================================================================\n\nUpgrade urgency CRITICAL: a crash introduced in 6.0.2 is now fixed.\n\n1eab62f7e Remove the client from CLOSE_ASAP list before caching the master.\n\n================================================================================\nRedis 6.0.2     Released Fri May 15 22:24:36 CEST 2020\n================================================================================\n\nUpgrade urgency MODERATE: many not critical bugfixes in different areas.\n                          Critical fix to client side caching when\n                          keys are evicted from the tracking table but\n                          no notifications are sent.\n\nThe following are the most serious fix:\n\n* XPENDING should not update consumer's seen-time\n* optimize memory usage of deferred replies - fixed\n* Fix CRC64 initialization outside the Redis server itself.\n* stringmatchlen() should not expect null terminated strings.\n* Cluster nodes availability checks improved when there is\n  high Pub/Sub load on the cluster bus.\n* Redis Benchmark: Fix coredump because of double free\n* Tracking: send eviction messages when evicting entries.\n* rax.c updated from upstream antirez/rax.\n* fix redis 6.0 not freeing closed connections during loading.\n\nNew features:\n\n* Support setcpuaffinity on linux/bsd\n* Client Side Caching: Add Tracking Prefix Number Stats in Server Info\n* Add --user argument to redis-benchmark.c (ACL)\n\nFull list of commits:\n\nYossi Gottlieb in commit 16ba33c05:\n TLS: Fix test failures on recent Debian/Ubuntu.\n 1 file changed, 20 deletions(-)\n\nYossi Gottlieb in commit 77ae66930:\n TLS: Add crypto locks for older OpenSSL support.\n 1 file changed, 45 insertions(+)\n\nDavid Carlier in commit 389697988:\n NetBSD build update.\n 3 files changed, 30 insertions(+), 1 deletion(-)\n\nMadelyn Olson in commit 2435341d7:\n Added a refcount on timer events to prevent deletion of recursive timer calls\n 2 files changed, 12 insertions(+)\n\nantirez in commit 80c906bd3:\n Cache master without checking of deferred close flags.\n 3 files changed, 11 insertions(+), 8 deletions(-)\n\nantirez in commit 74249be4a:\n Track events processed while blocked globally.\n 5 files changed, 32 insertions(+), 17 deletions(-)\n\nantirez in commit 8bf660af9:\n Some rework of #7234.\n 4 files changed, 77 insertions(+), 65 deletions(-)\n\nOran Agra in commit 9da134cd8:\n fix redis 6.0 not freeing closed connections during loading.\n 3 files changed, 133 insertions(+), 58 deletions(-)\n\nantirez in commit f7f219a13:\n Regression test for #7249.\n 1 file changed, 22 insertions(+)\n\nantirez in commit 693629585:\n rax.c updated from upstream antirez/rax.\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\nantirez in commit e3b5648df:\n Tracking: send eviction messages when evicting entries.\n 2 files changed, 29 insertions(+), 12 deletions(-)\n\nOran Agra in commit 5c41802d5:\n fix unstable replication test\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nShooterIT in commit a23cdbb94:\n Redis Benchmark: Fix coredump because of double free\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 1276058ea:\n Cluster: clarify we always resolve the sender.\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nantirez in commit 002fcde3d:\n Cluster: refactor ping/data delay handling.\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\nantirez in commit 960186a71:\n Cluster: introduce data_received field.\n 2 files changed, 27 insertions(+), 10 deletions(-)\n\nantirez in commit 3672875b4:\n stringmatchlen() should not expect null terminated strings.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nBrad Dunbar in commit 24e12641d:\n Remove unreachable branch.\n 1 file changed, 2 deletions(-)\n\nhwware in commit c7edffbd5:\n add jemalloc-bg-thread config in redis conf\n 1 file changed, 3 insertions(+)\n\nhwware in commit 8a9c84f4a:\n add include guard for lolwut.h\n 1 file changed, 6 insertions(+)\n\nantirez in commit cb683a84f:\n Don't propagate spurious MULTI on DEBUG LOADAOF.\n 2 files changed, 6 insertions(+), 3 deletions(-)\n\nantirez in commit 84d9766d6:\n Dump recent backlog on master query generating errors.\n 1 file changed, 29 insertions(+)\n\nTitouan Christophe in commit ec1e106ec:\n make struct user anonymous (only typedefed)\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit e48c37316:\n Test: --dont-clean should do first cleanup.\n 1 file changed, 2 insertions(+), 5 deletions(-)\n\nBenjamin Sergeant in commit 1e561cfaa:\n Add --user argument to redis-benchmark.c (ACL)\n 1 file changed, 15 insertions(+), 2 deletions(-)\n\nantirez in commit d1af82a88:\n Drop not needed part from #7194.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nMuhammad Zahalqa in commit 897a360d0:\n Fix compiler warnings on function rev(unsigned long)\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nantirez in commit ac316d8cc:\n Move CRC64 initialization in main().\n 2 files changed, 1 insertion(+), 4 deletions(-)\n\nantirez in commit fc7bc3204:\n Fix CRC64 initialization outside the Redis server itself.\n 1 file changed, 3 insertions(+)\n\nhwware in commit a6e55c096:\n Client Side Caching: Add Tracking Prefix Number Stats in Server Info\n 3 files changed, 8 insertions(+)\n\nantirez in commit b062fd523:\n Fix NetBSD build by fixing redis_set_thread_title() support.\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nantirez in commit 4efb25d9c:\n Rework a bit the documentation for CPU pinning.\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\nzhenwei pi in commit d6436eb7c:\n Support setcpuaffinity on linux/bsd\n 12 files changed, 180 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit 3a441c7d9:\n XPENDING should not update consumer's seen-time\n 4 files changed, 33 insertions(+), 20 deletions(-)\n\nOran Agra in commit 75addb4fe:\n optimize memory usage of deferred replies - fixed\n 1 file changed, 29 insertions(+)\n\nDeliang Yang in commit c57d9146f:\n reformat code\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nOran Agra in commit 3d3861dd8:\n add daily github actions with libc malloc and valgrind\n 5 files changed, 106 insertions(+), 18 deletions(-)\n\n\n================================================================================\nRedis 6.0.1     Released Sat May 02 00:06:07 CEST 2020\n================================================================================\n\nUpgrade urgency HIGH: This release fixes a crash when builiding against\n                      Libc malloc.\n\nHere we revert 8110ba888, an optimization that causes a crash due to a\nbug in the code. It does not happen with the default allocator because of\ndifferences between Jemalloc and libc malloc, so this escaped all our\ntesting but was reported by a user. We'll add back the original optimization\nthat was reverted here later, after checking what happens: it is not a\ncritical optimization.\n\nThe other commits are minor stuff:\n\nantirez in commit db73d0998:\n Cast printf() argument to the format specifier.\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nantirez in commit 7c0fe7271:\n Revert \"optimize memory usage of deferred replies\"\n 1 file changed, 31 deletions(-)\n\nantirez in commit 8fe25edc7:\n Save a call to stopThreadedIOIfNeeded() for the base case.\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\n================================================================================\nRedis 6.0.0 GA  Released Thu Apr 30 14:55:02 CEST 2020\n================================================================================\n\nUpgrade urgency CRITICAL: many bugs fixed compared to the last release\n                          candidate. Better to upgrade if you see things\n                          affecting your environment in the changelog.\n\nHi all, finally we have Redis 6.0.0 GA! Enjoy this new Redis release.\nMost of the documentation was updated today so that you can likely\nfind what you are looking for about the new features at redis.io.\nThis is the list of what changed compared to the previoius release candidate:\n\n* XCLAIM AOF/replicas propagation fixed.\n* Client side caching: new NOLOOP option to avoid getting notified about\n  changes performed by ourselves.\n* ACL GENPASS now uses HMAC-SHA256 and have an optional \"bits\" argument.\n  It means you can use it as a general purpose \"secure random strings\"\n  primitive!\n* Cluster \"SLOTS\" subcommand memory optimization.\n* The LCS command is now a subcommand of STRALGO.\n* Meaningful offset for replicas as well. More successful partial\n  resynchronizations.\n* Optimize memory usage of deferred replies.\n* Faster CRC64 algorithm for faster RDB loading.\n* XINFO STREAM FULL, a new subcommand to get the whole stream state.\n* CLIENT KILL USER <username>.\n* MIGRATE AUTH2 option, for ACL style authentication support.\n* Other random bugfixes.\n\nEnjoy Redis 6! :-)\nGoodbye antirez\n\nList of commits in this release:\n\nantirez in commit 1f9b82bd5:\n Update help.h again before Redis 6 GA.\n 1 file changed, 17 insertions(+), 12 deletions(-)\n\nantirez in commit 3fcffe7d0:\n redis-cli: fix hints with subcommands.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 455d8a05c:\n redis-cli command help updated.\n 1 file changed, 165 insertions(+), 25 deletions(-)\n\nzhaozhao.zz in commit 70287bbc9:\n lazyfree & eviction: record latency generated by lazyfree eviction\n 1 file changed, 18 insertions(+), 13 deletions(-)\n\nantirez in commit 7be21139a:\n MIGRATE AUTH2 for ACL support.\n 1 file changed, 19 insertions(+), 5 deletions(-)\n\nantirez in commit e1ee1a49d:\n CLIENT KILL USER <username>.\n 1 file changed, 11 insertions(+)\n\nantirez in commit d56f058c0:\n Fix tracking table max keys option in redis.conf.\n 1 file changed, 12 insertions(+), 9 deletions(-)\n\nantirez in commit 96dd5fc93:\n redis-cli: safer cluster fix with unreachalbe masters.\n 1 file changed, 26 insertions(+), 1 deletion(-)\n\nantirez in commit 5b59d9c5d:\n redis-cli: simplify cluster nodes coverage display.\n 1 file changed, 10 insertions(+), 17 deletions(-)\n\nantirez in commit c163d4add:\n redis-cli: try to make clusterManagerFixOpenSlot() more readable.\n 1 file changed, 25 insertions(+), 6 deletions(-)\n\nGuy Benoish in commit aab74b715:\n XINFO STREAM FULL should have a default COUNT of 10\n 1 file changed, 8 insertions(+), 4 deletions(-)\n\nantirez in commit 606134f9d:\n Comment clearly why we moved some code in #6623.\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nsrzhao in commit ee627bb66:\n fix pipelined WAIT performance issue.\n 1 file changed, 13 insertions(+), 13 deletions(-)\n\nantirez in commit 47b8a7f9b:\n Fix create-cluster BIN_PATH.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nGuy Benoish in commit 6c0bc608a:\n Extend XINFO STREAM output\n 2 files changed, 226 insertions(+), 34 deletions(-)\n\nhwware in commit 5bfc18950:\n Fix not used marco in cluster.c\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nItamar Haber in commit 56d628f85:\n Update create-cluster\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nItamar Haber in commit cac9d7cf7:\n Adds `BIN_PATH` to create-cluster\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\nOran Agra in commit b712fba17:\n hickup, re-fix dictEncObjKeyCompare\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\nOran Agra in commit ea63aea72:\n fix loading race in psync2 tests\n 3 files changed, 15 insertions(+), 1 deletion(-)\n\nantirez in commit 64e588bfa:\n Rework comment in dictEncObjKeyCompare().\n 1 file changed, 8 insertions(+), 9 deletions(-)\n\nOran Agra in commit 0d1e8c93b:\n allow dictFind using static robj\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\nMadelyn Olson in commit a1bed447b:\n Added crcspeed library\n 2 files changed, 341 insertions(+)\n\nMadelyn Olson in commit a75fa3aad:\n Made crc64 test consistent\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\nMadelyn Olson in commit 52c75e9db:\n Implemented CRC64 based on slice by 4\n 5 files changed, 124 insertions(+), 157 deletions(-)\n\nOran Agra in commit 8110ba888:\n optimize memory usage of deferred replies\n 1 file changed, 31 insertions(+)\n\nOran Agra in commit e4d2bb62b:\n Keep track of meaningful replication offset in replicas too\n 5 files changed, 212 insertions(+), 92 deletions(-)\n\nantirez in commit fea9788cc:\n Fix STRALGO command flags.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nDave-in-lafayette in commit 2144047e1:\n fix for unintended crash during panic response\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nGuy Benoish in commit 43329c9b6:\n Add the stream tag to XSETID tests\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nDave-in-lafayette in commit 1e17d3de7:\n fix for crash during panic before all threads are up\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 3722f89f4:\n LCS -> STRALGO LCS.\n 4 files changed, 28 insertions(+), 15 deletions(-)\n\nantirez in commit 373ae6061:\n Also use propagate() in streamPropagateGroupID().\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\nyanhui13 in commit f03f1fad6:\n add tcl test for cluster slots\n 1 file changed, 44 insertions(+)\n\nyanhui13 in commit 374ffdf1c:\n optimize the output of cluster slots\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\nantirez in commit 4db38d2ef:\n Minor aesthetic changes to #7135.\n 1 file changed, 5 insertions(+), 7 deletions(-)\n\nValentino Geron in commit f0a261448:\n XREADGROUP with NOACK should propagate only one XGROUP SETID command\n 1 file changed, 13 insertions(+), 7 deletions(-)\n\nantirez in commit fbdef6a9b:\n ACL: re-enable command execution of disabled users.\n 1 file changed, 4 deletions(-)\n\nantirez in commit 05a41da75:\n getRandomBytes(): use HMAC-SHA256.\n 1 file changed, 30 insertions(+), 10 deletions(-)\n\nantirez in commit 345c3768d:\n ACL GENPASS: take number of bits as argument.\n 1 file changed, 21 insertions(+), 6 deletions(-)\n\nantirez in commit 639c8a1d9:\n ACL GENPASS: emit 256 bits instead of 128.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 321acea03:\n ACL: deny commands execution of disabled users.\n 1 file changed, 4 insertions(+)\n\nTheo Buehler in commit b0920e6e8:\n TLS: Fix build with SSL_OP_NO_CLIENT_RENEGOTIATION\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nYossi Gottlieb in commit 149b658b5:\n TLS: Fix build on older verisons of OpenSSL.\n 1 file changed, 2 insertions(+)\n\nantirez in commit 06917e581:\n Tracking: test expired keys notifications.\n 1 file changed, 13 insertions(+)\n\nantirez in commit e434b2ce4:\n Tracking: NOLOOP tests.\n 1 file changed, 32 insertions(+)\n\nantirez in commit f3a172887:\n Tracking: signal key as modified when evicting.\n 1 file changed, 1 insertion(+)\n\nantirez in commit e63bb7ec8:\n Tracking: NOLOOP further implementation and fixes.\n 2 files changed, 21 insertions(+), 6 deletions(-)\n\nantirez in commit 6791ff052:\n Tracking: NOLOOP internals implementation.\n 17 files changed, 174 insertions(+), 112 deletions(-)\n\nantirez in commit 725b8cc68:\n Implement redis_set_thread_title for MacOS.\n 1 file changed, 6 insertions(+)\n\nzhenwei pi in commit 3575b8706:\n Threaded IO: set thread name for redis-server\n 3 files changed, 28 insertions(+)\n\nantirez in commit a76c67578:\n Sentinel: small refactoring of sentinelCollectTerminatedScripts().\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\nomg-by in commit 3a27064c4:\n fix(sentinel): sentinel.running_scripts will always increase more times and not reset\n 1 file changed, 1 insertion(+)\n\nantirez in commit 5c4c73e2c:\n A few comments and name changes for #7103.\n 1 file changed, 13 insertions(+), 4 deletions(-)\n\nOran Agra in commit 6148f9493:\n testsuite run the defrag latency test solo\n 3 files changed, 42 insertions(+), 2 deletions(-)\n\nJamie Scott in commit 51d3012d4:\n Adding acllog-max-len to Redis.conf\n 1 file changed, 9 insertions(+)\n\nantirez in commit c39f16c42:\n Fix XCLAIM propagation in AOF/replicas for blocking XREADGROUP.\n 2 files changed, 8 insertions(+), 3 deletions(-)\n\n================================================================================\nRedis 6.0-rc4     Released Thu Apr 16 16:10:35 CEST 2020\n================================================================================\n\nUpgrade urgency LOW: If you are using RC3 without issues, don't rush.\n\nHi all, this the latest release candidate of Redis 6. This is likely to\nbe very similar to what you'll see in Redis 6 GA. Please test it and\nreport any issue :-)\n\nMain changes in this release:\n\n    * Big INFO speedup when using a lot of of clients.\n    * Big speedup on all the blocking commands: now blocking\n      on the same key is O(1) instead of being O(N).\n    * Stale replicas now allow MULTI/EXEC.\n    * New command: LCS (Longest Common Subsequence).\n    * Add a new configuration to make DEL like UNLINK.\n    * RDB loading speedup.\n    * Many bugs fixed (see the commit messages at the end of this node)\n\nSee you in 14 days for Redis 6 GA.\n\nList of commits:\n\nantirez in commit 9f594e243:\n Update SDS to latest version.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 48781dd95:\n RESP3: fix HELLO map len in Sentinel mode.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 371ab0cff:\n Don't allow empty spaces in ACL usernames.\n 1 file changed, 36 insertions(+), 8 deletions(-)\n\nantirez in commit b86140ac5:\n Don't allow empty spaces in ACL key patterns.\n 1 file changed, 12 insertions(+), 1 deletion(-)\n\nliumiuyong in commit a7ee3c3e7:\n FIX: truncate max/min longitude,latitude related geo_point (ex:  {180, 85.05112878} )\n 1 file changed, 4 insertions(+)\n\nGuy Benoish in commit e5b9eb817:\n Typo in getTimeoutFromObjectOrReply's error reply\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 0f31bb5c1:\n Fix HELLO reply in Sentinel mode, see #6160.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit b92d9a895:\n fix spelling in acl.c\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nantirez in commit 8f896e57a:\n Fix zsetAdd() top comment spelling.\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nhayleeliu in commit 8f5157058:\n fix spelling mistake in bitops.c\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit ddeda9ceb:\n Fix function names in zslDeleteNode() top comment.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit bde1f0a8e:\n RESP3: change streams items from maps to arrays.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit bec68bff2:\n Use the special static refcount for stack objects.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 0f239e51b:\n RDB: refactor some RDB loading code into dbAddRDBLoad().\n 3 files changed, 22 insertions(+), 4 deletions(-)\n\nantirez in commit f855db61b:\n incrRefCount(): abort on statically allocated object.\n 2 files changed, 12 insertions(+), 2 deletions(-)\n\nantirez in commit 23094ba01:\n More powerful DEBUG RELOAD.\n 3 files changed, 55 insertions(+), 16 deletions(-)\n\nantirez in commit 8161a7a3e:\n RDB: clarify a condition in rdbLoadRio().\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\nantirez in commit 61b153073:\n RDB: load files faster avoiding useless free+realloc.\n 7 files changed, 40 insertions(+), 28 deletions(-)\n\nantirez in commit 414debfd0:\n Speedup: unblock clients on keys in O(1).\n 4 files changed, 50 insertions(+), 23 deletions(-)\n\nantirez in commit cbcd07777:\n Fix ACL HELP table missing comma.\n 1 file changed, 12 insertions(+), 12 deletions(-)\n\nmymilkbottles in commit 2437455f2:\n Judge the log level in advance\n 1 file changed, 1 insertion(+)\n\nantirez in commit 35c64b898:\n Speedup INFO by counting client memory incrementally.\n 4 files changed, 52 insertions(+), 26 deletions(-)\n\nqetu3790 in commit c3ac71748:\n fix comments about RESIZE DB opcode in rdb.c\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\nantirez in commit c8dbcff9d:\n Clarify redis.conf comment about lazyfree-lazy-user-del.\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\nzhaozhao.zz in commit abd5156f2:\n lazyfree: add a new configuration lazyfree-lazy-user-del\n 4 files changed, 7 insertions(+), 2 deletions(-)\n\nantirez in commit 5719b3054:\n LCS: more tests.\n 1 file changed, 8 insertions(+)\n\nantirez in commit c89e1f293:\n LCS: allow KEYS / STRINGS to be anywhere.\n 1 file changed, 6 deletions(-)\n\nantirez in commit 0b16f8d44:\n LCS tests.\n 1 file changed, 22 insertions(+)\n\nantirez in commit 9254a805d:\n LCS: get rid of STOREIDX option. Fix get keys helper.\n 2 files changed, 20 insertions(+), 21 deletions(-)\n\nantirez in commit a4c490703:\n LCS: fix stale comment.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit cb92c23de:\n LCS: output LCS len as well in IDX mode.\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nantirez in commit 56a52e804:\n LCS: MINMATCHLEN and WITHMATCHLEN options.\n 1 file changed, 24 insertions(+), 11 deletions(-)\n\nantirez in commit ebb09a5c3:\n LCS: 7x speedup by accessing the array with better locality.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit a9f8a8cba:\n LCS: implement KEYS option.\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\nantirez in commit 4aa24e62a:\n LCS: other fixes to range emission.\n 1 file changed, 20 insertions(+), 16 deletions(-)\n\nantirez in commit 2b67b6b87:\n LCS: fix emission of last range starting at index 0.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 420aac727:\n LCS: implement range indexes option.\n 1 file changed, 59 insertions(+), 9 deletions(-)\n\nantirez in commit a518a9a76:\n LCS: initial functionality implemented.\n 4 files changed, 156 insertions(+), 1 deletion(-)\n\nsrzhao in commit 026cc11b0:\n Check OOM at script start to get stable lua OOM state.\n 3 files changed, 11 insertions(+), 4 deletions(-)\n\nOran Agra in commit 02b594f6a:\n diffrent fix for runtest --host --port\n 2 files changed, 13 insertions(+), 13 deletions(-)\n\nGuy Benoish in commit f695d1830:\n Try to fix time-sensitive tests in blockonkey.tcl\n 1 file changed, 54 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit 0e42cfc36:\n Use __attribute__ only if __GNUC__ is defined\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\nGuy Benoish in commit 91ed9b3c4:\n Modules: Perform printf-like format checks in variadic API\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nValentino Geron in commit 3e0d20962:\n XREAD and XREADGROUP should not be allowed from scripts when BLOCK option is being used\n 3 files changed, 18 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 240094c9b:\n Stale replica should allow MULTI/EXEC\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nXudong Zhang in commit 209f3a1eb:\n fix integer overflow\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 024c380b9:\n Fix no-negative-zero test\n 1 file changed, 1 insertion(+)\n\nOran Agra in commit a38ff404b:\n modules don't signalModifiedKey in setKey() since that's done (optionally) in RM_CloseKey\n 4 files changed, 8 insertions(+), 8 deletions(-)\n\nOran Agra in commit 814874d68:\n change CI to build and run the module api tests\n 1 file changed, 2 insertions(+)\n\nOran Agra in commit 061616c1b:\n fix possible warning on incomplete struct init\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nGuy Benoish in commit 7764996be:\n Make sure Redis does not reply with negative zero\n 2 files changed, 10 insertions(+)\n\nGuy Benoish in commit eba28e2ce:\n DEBUG OBJECT should pass keyname to module when loading\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\nDavid Carlier in commit 15c9e79a7:\n debug, dump registers on arm too.\n 1 file changed, 55 insertions(+), 27 deletions(-)\n\nhwware in commit cd2b5df97:\n fix spelling in cluster.c\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nValentino Geron in commit 8cdc153f5:\n XACK should be executed in a \"all or nothing\" fashion.\n 2 files changed, 23 insertions(+), 1 deletion(-)\n\nhwware in commit b35407fa7:\n add check for not switching between optin optout mode directly\n 1 file changed, 12 insertions(+), 1 deletion(-)\n\nhwware in commit 4395889c9:\n add check for not providing both optin optout flag\n 1 file changed, 8 insertions(+)\n\nGuy Benoish in commit 1907e0f18:\n PERSIST should notify a keyspace event\n 1 file changed, 1 insertion(+)\n\nGuy Benoish in commit c35a53169:\n streamReplyWithRange: Redundant XSETIDs to replica\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 6fe66e096:\n Simplify comment in moduleTryServeClientBlockedOnKey().\n 1 file changed, 3 insertions(+), 12 deletions(-)\n\nGuy Benoish in commit 193fc241c:\n Fix memory corruption in moduleHandleBlockedClients\n 3 files changed, 149 insertions(+), 46 deletions(-)\n\n================================================================================\nRedis 6.0-rc3     Released Tue Mar 31 17:42:39 CEST 2020\n================================================================================\n\nUpgrade urgency CRITICAL: A connection management bug introduced with the\n                          SSL implementation can crash Redis easily.\n\nDear users, this is a list of the major changes in this release, please check \nthe list of commits for detail:\n\n* Fix crash due to refactoring for SSL, for the connection code.\n* Precise timeouts for blocking commands. Now the timeouts have HZ\n  resolution regardless of the number of connected clinets. New timeouts\n  are stored in a radix tree and sorted by expire time.\n* Fix rare crash when resizing the event loop because of CONFIG maxclients.\n* Fix systemd readiness after successful partial resync.\n* Redis-cli ask password mode to be prompted at startup (for additional safety).\n* Keyspace notifications added to MIGRATE / RESTORE.\n* Threaded I/O bugs fixed.\n* Implement new ACL style AUTH in Sentinel.\n* Make 'requirepass' more backward compatible with Redis <= 5.\n* ACL: Handle default user as disabled if it's off regardless of \"nopass\".\n* Fix a potential inconsistency when upgrading an instance in Redis Cluster\n  and restarting it. The instance will act as a replica but will actually be\n  set as a master immediately. However the choice of what to do with already\n  expired keys, on loading, was made from the POV of replicas.\n* Abort transactions after -READONLY error.\n* Many different fixes to module APIs.\n* BITFIELD_RO added to call the command on read only replicas.\n* PSYNC2: meaningful offset implementation. Allow the disconnected master\n  that is still sending PINGs to replicas, to be able to successfully\n  PSYNC incrementally to new slaves, discarding the last part of the\n  replication backlog consisting only of PINGs.\n* Fix pipelined MULTI/EXEC during Lua scripts are in BUSY state.\n* Re-fix propagation API in modules, broken again after other changes.\n\nantirez in commit ef1b1f01:\n cast raxSize() to avoid warning with format spec.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 9f347fab:\n Minor changes to #7037.\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\nGuy Benoish in commit a509400d:\n Modules: Test MULTI/EXEC replication of RM_Replicate\n 6 files changed, 49 insertions(+), 9 deletions(-)\n\nGuy Benoish in commit 805c8c94:\n RENAME can unblock XREADGROUP\n 3 files changed, 25 insertions(+), 1 deletion(-)\n\nantirez in commit 97b80b57:\n Fix the propagate Tcl test after module changes.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 4f6b6b80:\n Modify the propagate unit test to show more cases.\n 1 file changed, 30 insertions(+), 2 deletions(-)\n\nantirez in commit 616b1cb7:\n Fix module commands propagation double MULTI bug.\n 4 files changed, 25 insertions(+), 8 deletions(-)\n\nantirez in commit 08fdef4b:\n Fix RM_Call() stale comment due to cut&paste.\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\nOMG-By in commit 26b79ca1:\n fix: dict.c->dictResize()->minimal  type\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nzhaozhao.zz in commit fa418637:\n PSYNC2: reset backlog_idx and master_repl_offset correctly\n 1 file changed, 10 insertions(+), 5 deletions(-)\n\nantirez in commit bbbc80ac:\n Precise timeouts: reference client pointer directly.\n 1 file changed, 13 insertions(+), 16 deletions(-)\n\nantirez in commit c3b268a0:\n timeout.c created: move client timeouts code there.\n 5 files changed, 198 insertions(+), 167 deletions(-)\n\nOran Agra in commit 0f7dfc37:\n AOFRW on an empty stream created with MKSTREAM loads badkly\n 2 files changed, 15 insertions(+), 1 deletion(-)\n\nantirez in commit 67643ead:\n Precise timeouts: cleaup the table on unblock.\n 3 files changed, 21 insertions(+), 2 deletions(-)\n\nantirez in commit ad94066e:\n Precise timeouts: fix comments after functional change.\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\nantirez in commit a443ec2e:\n Precise timeouts: use only radix tree for timeouts.\n 3 files changed, 15 insertions(+), 38 deletions(-)\n\nantirez in commit 6862fd70:\n Precise timeouts: fast exit for clientsHandleShortTimeout().\n 1 file changed, 1 insertion(+)\n\nantirez in commit 30f1df8c:\n Precise timeouts: fix bugs in initial implementation.\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\nantirez in commit 7add0f24:\n Precise timeouts: working initial implementation.\n 3 files changed, 110 insertions(+), 28 deletions(-)\n\nantirez in commit 9d6d1779:\n Precise timeouts: refactor unblocking on timeout.\n 2 files changed, 33 insertions(+), 13 deletions(-)\n\nantirez in commit 316a8f15:\n PSYNC2: fix backlog_idx when adjusting for meaningful offset\n 1 file changed, 3 insertions(+)\n\n伯成 in commit 11db53f8:\n Boost up performance for redis PUB-SUB patterns matching\n 3 files changed, 43 insertions(+), 11 deletions(-)\n\nantirez in commit e257f121:\n PSYNC2: meaningful offset test.\n 2 files changed, 62 insertions(+)\n\nantirez in commit 5f72f696:\n PSYNC2: meaningful offset implemented.\n 3 files changed, 40 insertions(+), 1 deletion(-)\n\nantirez in commit 8caa2714:\n Explain why we allow transactions in -BUSY state.\n 1 file changed, 9 insertions(+), 2 deletions(-)\n\nOran Agra in commit e43cd831:\n MULTI/EXEC during LUA script timeout are messed up\n 2 files changed, 73 insertions(+)\n\nantirez in commit 34b89832:\n Improve comments of replicationCacheMasterUsingMyself().\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nantirez in commit 70a98a43:\n Fix BITFIELD_RO test.\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\nantirez in commit 8783304a:\n Abort transactions after -READONLY error. Fix #7014.\n 1 file changed, 1 insertion(+)\n\nantirez in commit ec9cf002:\n Minor changes to BITFIELD_RO PR #6951.\n 1 file changed, 9 insertions(+), 6 deletions(-)\n\nbodong.ybd in commit b3e4abf0:\n Added BITFIELD_RO variants for read-only operations.\n 4 files changed, 54 insertions(+), 1 deletion(-)\n\nantirez in commit 50f8f950:\n Modules: updated function doc after #7003.\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit f2f3dc5e:\n Allow RM_GetContextFlags to work with ctx==NULL\n 1 file changed, 16 insertions(+), 14 deletions(-)\n\nhwware in commit eb808879:\n fix potentical memory leak in redis-cli\n 1 file changed, 2 insertions(+)\n\nYossi Gottlieb in commit cdcab0e8:\n Fix crashes related to failed/rejected accepts.\n 1 file changed, 6 insertions(+), 5 deletions(-)\n\nYossi Gottlieb in commit 50dcd9f9:\n Cluster: fix misleading accept errors.\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\nYossi Gottlieb in commit 87dbd8f5:\n Conns: Fix connClose() / connAccept() behavior.\n 3 files changed, 48 insertions(+), 32 deletions(-)\n\nhwware in commit 81e8686c:\n remove redundant Semicolon\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit c7524a7e:\n clean CLIENT_TRACKING_CACHING flag when disabled caching\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit 2dd1ca6a:\n add missing commands in cluster help\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nartix in commit 95324b81:\n Support Redis Cluster Proxy PROXY INFO command\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\n박승현 in commit 04c53fa1:\n Update redis.conf\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nWuYunlong in commit 0578157d:\n Fix master replica inconsistency for upgrading scenario.\n 3 files changed, 9 insertions(+), 2 deletions(-)\n\nWuYunlong in commit 299f1d02:\n Add 14-consistency-check.tcl to prove there is a data consistency issue.\n 1 file changed, 87 insertions(+)\n\nantirez in commit 61b98f32:\n Regression test for #7011.\n 1 file changed, 7 insertions(+)\n\nantirez in commit 34ea2f4e:\n ACL: default user off should not allow automatic authentication.\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\nantirez in commit cbbf9b39:\n Sentinel: document auth-user directive.\n 1 file changed, 12 insertions(+)\n\nantirez in commit 9c2e42dd:\n ACL: Make Redis 6 more backward compatible with requirepass.\n 4 files changed, 17 insertions(+), 15 deletions(-)\n\nantirez in commit d387f67d:\n Sentinel: implement auth-user directive for ACLs.\n 1 file changed, 38 insertions(+), 7 deletions(-)\n\nzhaozhao.zz in commit 7c078416:\n Threaded IO: bugfix client kill may crash redis\n 1 file changed, 11 insertions(+), 5 deletions(-)\n\nzhaozhao.zz in commit 9cc7038e:\n Threaded IO: handle pending reads clients ASAP after event loop\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nantirez in commit da8c7c49:\n Example sentinel conf: document requirepass.\n 1 file changed, 8 insertions(+)\n\nantirez in commit bdb338cf:\n Aesthetic changes in PR #6989.\n 1 file changed, 9 insertions(+), 5 deletions(-)\n\nzhaozhao.zz in commit b3e03054:\n Threaded IO: bugfix #6988 process events while blocked\n 1 file changed, 5 insertions(+)\n\nantirez in commit e628f944:\n Restore newline at the end of redis-cli.c\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nchendianqiang in commit 5d4c4df3:\n use correct list for moduleUnregisterUsedAPI\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nguodongxiaren in commit da14982d:\n string literal should be const char*\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nItamar Haber in commit dc8885a1:\n Adds keyspace notifications to migrate and restore\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\nbodong.ybd in commit bfb18e55:\n Remove duplicate obj files in Makefile\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nbodong.ybd in commit 76d57161:\n Fix bug of tcl test using external server\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\nfengpf in commit 0e5820d8:\n fix comments in latency.c\n 2 files changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 916dd79f:\n Update linenoise.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nlifubang in commit c0c67c9b:\n add askpass mode\n 1 file changed, 19 insertions(+), 1 deletion(-)\n\nlifubang in commit e1c29434:\n update linenoise to https://github.com/antirez/linenoise/tree/fc9667a81d43911a6690fb1e68c16e6e3bb8df05\n 4 files changed, 59 insertions(+), 4 deletions(-)\n\nJamie Scott in commit e5a063bc:\n Remove default guidance in Redis.conf\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\nJamie Scott in commit d28cbaf7:\n Update Redis.conf to improve TLS usability\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nJohannes Truschnigg in commit 23d5e8b8:\n Signal systemd readiness atfer Partial Resync\n 1 file changed, 4 insertions(+)\n\nOran Agra in commit 61738154:\n fix for flaky psync2 test\n 1 file changed, 21 insertions(+)\n\nantirez in commit 70e0e499:\n ae.c: fix crash when resizing the event loop.\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\nantirez in commit b3e4aa67:\n Fix release notes spelling mistake.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\n\n================================================================================\nRedis 6.0 RC2     Released Thu Mar 05 15:40:53 CET 2020\n================================================================================\n\nUpgrade urgency MODERATE: Normal bugfixing release of a non-GA branch.\n\nHi Redis users, Redis 6 is approaching and will be released 30th of April.\nNew release candidates will be released at the end of March, then another\none mid April, to finally reach the GA at the end of April.\n\nRedis 6 RC2 brings many fixes and new things, especially in the area of\nclient side caching. This is the list of big changes in this release. As\nusually you can find the full list of commits at the end:\n\nNew features and improvements:\n\n* ACL LOG: log denied commands, keys accesses and authentications.\n* Client side caching redesigned. Now we use keys not caching slots.\n* Client side caching: Broadcasting mode implemented.\n* Client side caching: OPTIN/OPTOUT modes implemented.\n* Remove RDB files used for replication in persistence-less instances (option).\n\nFixes (only selected ones, see commits for all the fixes):\n\n* Different fixes to streams in edge cases.\n* Fix duplicated CLIENT SETNAME reply because of RESP3 changes.\n* Fix crash due to new active expire division by zero.\n* Avoid sentinel changes promoted_slave to be its own replica.\n* Fix bug on KEYS command where pattern starts with * followed by \\x00.\n* Threaded I/O: now the main thread is used as well to do I/O.\n* Many fixes to modules APIs, and more to come in the next RCs.\n* ld2string should fail if string contains \\0 in the middle.\n* Make the Redis test more reliable.\n* Fix SPOP returning nil (see #4709). WARNING: API change.\n\nqetu3790 in commit 4af0d7fd:\n Fix not used constant in lru_test_mode.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit 6ef01878:\n add missing file marco\n 1 file changed, 5 insertions(+)\n\nShooterIT in commit fe81d5c8:\n Avoid compiler warnings\n 1 file changed, 1 insertion(+)\n\nantirez in commit c2f01d7f:\n RDB deletion: document it in example redis.conf.\n 1 file changed, 13 insertions(+)\n\nantirez in commit 127e09bc:\n Make sync RDB deletion configurable. Default to no.\n 3 files changed, 22 insertions(+), 4 deletions(-)\n\nantirez in commit a20303c6:\n Check that the file exists in removeRDBUsedToSyncReplicas().\n 1 file changed, 8 insertions(+), 4 deletions(-)\n\nantirez in commit 7a23b945:\n Log RDB deletion in persistence-less instances.\n 1 file changed, 15 insertions(+), 2 deletions(-)\n\nantirez in commit baaf869f:\n Introduce bg_unlink().\n 1 file changed, 31 insertions(+), 3 deletions(-)\n\nantirez in commit be4bc1a5:\n Remove RDB files used for replication in persistence-less instances.\n 3 files changed, 56 insertions(+), 1 deletion(-)\n\nantirez in commit 07dc1b42:\n Use a smaller getkeys global buffer.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nOran Agra in commit 10e71b3d:\n Optimize temporary memory allocations for getKeysFromCommand mechanism\n 1 file changed, 31 insertions(+), 10 deletions(-)\n\nantirez in commit edc0ed14:\n Modules: reformat RM_Scan() top comment a bit.\n 1 file changed, 21 insertions(+), 12 deletions(-)\n\nantirez in commit c5319612:\n Modules: more details in RM_Scan API top comment.\n 1 file changed, 22 insertions(+), 6 deletions(-)\n\nOran Agra in commit fff6b26a:\n RM_Scan disable dict rehashing\n 2 files changed, 21 insertions(+), 6 deletions(-)\n\nGuy Benoish in commit 65048460:\n Add RM_CreateStringFromDouble\n 2 files changed, 14 insertions(+)\n\nOran Agra in commit 3144a278:\n add no_auth to COMMAND INFO\n 1 file changed, 1 insertion(+)\n\nOran Agra in commit afe0b16c:\n module api docs for aux_save and aux_load\n 2 files changed, 7 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit df152b0c:\n streamReplyWithRangeFromConsumerPEL: Redundant streamDecodeID\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\nantirez in commit e3c1f439:\n Show Redis version when not understanding a config directive.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nantirez in commit 141c0679:\n Changelog: explain Redis 6 SPOP change.\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nbodong.ybd in commit fe902461:\n Fix spop return nil #4709\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 9d4219eb:\n Fix SDS misuse in enumConfigSet(). Related to #6778.\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nantirez in commit 84243064:\n Remove useless comment from enumConfigSet().\n 1 file changed, 1 deletion(-)\n\nPonnuvel Palaniyappan in commit dafb94db:\n Fix a potential overflow with strncpy\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\nantirez in commit ea697b63:\n Improve aeDeleteEventLoop() top comment grammar.\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nwangyuan21 in commit dd479880:\n free time event when delete eventloop\n 1 file changed, 7 insertions(+)\n\nsrzhao in commit ecf3b2ef:\n fix impl of aof-child whitelist SIGUSR1 feature.\n 1 file changed, 5 insertions(+), 4 deletions(-)\n\nmeir@redislabs.com in commit 2966132c:\n Changed log level for module fork api from 'notice' to 'verbos'.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nhwware in commit 7277e5d8:\n format fix\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nhwware in commit 1bb5ee9c:\n fix potentical memory leaks\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nHengjian Tang in commit 97329733:\n modify the read buf size according to the write buf size PROTO_IOBUF_LEN defined before\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nAriel in commit 15ea1324:\n fix ThreadSafeContext lock/unlock function names\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 4d12c37c:\n XREADGROUP should propagate XCALIM/SETID in MULTI/EXEC\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nOran Agra in commit 12626ce9:\n fix race in module api test for fork\n 2 files changed, 2 insertions(+), 3 deletions(-)\n\nGuy Benoish in commit 2ecab0b6:\n Modules: Do not auto-unblock clients if not blocked on keys\n 1 file changed, 22 insertions(+), 7 deletions(-)\n\nOran Agra in commit 635321d4:\n fix github actions failing latency test for active defrag - part 2\n 2 files changed, 5 insertions(+), 4 deletions(-)\n\nOran Agra in commit 0b988fa9:\n fix github actions failing latency test for active defrag\n 2 files changed, 14 insertions(+), 13 deletions(-)\n\nOran Agra in commit 60096bc1:\n Fix latency sensitivity of new defrag test\n 1 file changed, 32 insertions(+), 8 deletions(-)\n\nantirez in commit b4395426:\n Tracking: optin/out implemented.\n 3 files changed, 82 insertions(+), 16 deletions(-)\n\nantirez in commit ef3551d1:\n Test engine: experimental change to avoid busy port problems.\n 1 file changed, 84 insertions(+), 49 deletions(-)\n\nantirez in commit 72c05351:\n Test engine: detect timeout when checking for Redis startup.\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\nantirez in commit 294c9af4:\n Test engine: better tracking of what workers are doing.\n 2 files changed, 12 insertions(+), 4 deletions(-)\n\nhwware in commit ba027079:\n add missing subcommand description for debug oom\n 1 file changed, 1 insertion(+)\n\nGuy Benoish in commit 5d0890c0:\n Fix memory leak in test_ld_conv\n 1 file changed, 4 insertions(+)\n\nMadelyn Olson in commit d1f22eac:\n Give an error message if you specify redirect twice\n 1 file changed, 7 insertions(+)\n\nMadelyn Olson in commit 762fbcb6:\n Minor CSC fixes and fixed documentation\n 2 files changed, 16 insertions(+), 17 deletions(-)\n\nOran Agra in commit 349aa245:\n Defrag big lists in portions to avoid latency and freeze\n 4 files changed, 350 insertions(+), 34 deletions(-)\n\nGuy Benoish in commit b4ddc7b7:\n XGROUP DESTROY should unblock XREADGROUP with -NOGROUP\n 2 files changed, 11 insertions(+)\n\nhayashier in commit 73806f74:\n fix typo from fss to rss\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nantirez in commit b6129f86:\n Test is more complex now, increase default timeout.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit f15fb727:\n Tracking: fix max-keys configuration directive.\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\nItamar Haber in commit e374573f:\n Fixes segfault on calling trackingGetTotalKeys\n 1 file changed, 1 insertion(+)\n\nantirez in commit 73d47d57:\n Signal key as modified when expired on-access.\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\nantirez in commit b7cb28d5:\n Tracking: first set of tests for the feature.\n 1 file changed, 66 insertions(+)\n\nantirez in commit 1db72571:\n Tracking: fix operators precedence error in bcast check.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit fe96e29d:\n Tracking: fix behavior when switchinig from normal to BCAST.\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\nantirez in commit f21be1ec:\n Tracking: fix sending messages bug + tracking off bug.\n 2 files changed, 28 insertions(+), 20 deletions(-)\n\nantirez in commit 6fb1aa23:\n Tracking: BCAST: basic feature now works.\n 3 files changed, 55 insertions(+), 40 deletions(-)\n\nantirez in commit d4fe79a1:\n Tracking: BCAST: broadcasting of keys in prefixes implemented.\n 2 files changed, 94 insertions(+), 9 deletions(-)\n\nantirez in commit abb81c63:\n Tracking: BCAST: registration in the prefix table.\n 3 files changed, 67 insertions(+), 20 deletions(-)\n\nantirez in commit 77da9608:\n Tracking: BCAST: parsing of the options + skeleton.\n 4 files changed, 73 insertions(+), 19 deletions(-)\n\nantirez in commit 3e8c69a9:\n Tracking: always reply with an array of keys.\n 2 files changed, 10 insertions(+), 3 deletions(-)\n\nantirez in commit a788c373:\n Tracking: minor change of names and new INFO field.\n 4 files changed, 11 insertions(+), 4 deletions(-)\n\nantirez in commit df838927:\n Rax.c: populate data field after random walk.\n 1 file changed, 1 insertion(+)\n\nantirez in commit 0517da36:\n Tracking: rename INFO field with total items.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 3c16d6b3:\n Tracking: first conversion from hashing to key names.\n 3 files changed, 84 insertions(+), 114 deletions(-)\n\nOran Agra in commit 3b4f1477:\n add no-slowlog option to RM_CreateCommand\n 1 file changed, 3 insertions(+)\n\nKhem Raj in commit 5e762d84:\n Mark extern definition of SDS_NOINIT in sds.h\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nlifubang in commit 54f5499a:\n correct help info for --user and --pass\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nSeunghoon Woo in commit 0c952b13:\n [FIX] revisit CVE-2015-8080 vulnerability\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\nGuy Benoish in commit dd34f703:\n Diskless-load emptyDb-related fixes\n 3 files changed, 44 insertions(+), 28 deletions(-)\n\nlifubang in commit 5e042dbc:\n fix ssl flag check for redis-cli\n 1 file changed, 10 insertions(+), 9 deletions(-)\n\nGuy Benoish in commit dcbe8bfa:\n Exclude \"keymiss\" notification from NOTIFY_ALL\n 5 files changed, 12 insertions(+), 7 deletions(-)\n\nOran Agra in commit 36caf2e4:\n update RM_SignalModifiedKey doc comment\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\nOran Agra in commit 3067352a:\n Add handling of short read of module id in rdb\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nYossi Gottlieb in commit 9baaf858:\n TLS: Update documentation.\n 2 files changed, 32 insertions(+), 31 deletions(-)\n\nOran Agra in commit 4440133e:\n A few non-data commands that should be allowed while loading or stale\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\nOran Agra in commit c9577941:\n Memory leak when bind config is provided twice\n 1 file changed, 4 insertions(+)\n\nOran Agra in commit 1333a46b:\n fix maxmemory config warning\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\nOran Agra in commit 8e7282eb:\n Fix client flags to be int64 in module.c\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\nOran Agra in commit a678390e:\n moduleRDBLoadError, add key name, and use panic rather than exit\n 1 file changed, 5 insertions(+), 4 deletions(-)\n\nOran Agra in commit 919fbf42:\n reduce repeated calls to use_diskless_load\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\nOran Agra in commit 22e45d46:\n freeClientAsync don't lock mutex if there's just one thread\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\nOran Agra in commit ba289244:\n move restartAOFAfterSYNC from replicaofCommand to replicationUnsetMaster\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\nOran Agra in commit f42ce57d:\n stopAppendOnly resets aof_rewrite_scheduled\n 1 file changed, 1 insertion(+)\n\nOran Agra in commit df096bc9:\n add SAVE subcommand to ACL HELP and top comment\n 1 file changed, 2 insertions(+)\n\nOran Agra in commit a55e5847:\n DEBUG HELP - add PROTOCOL\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\nGuy Benoish in commit 5a6cfbf4:\n Some refactroing using getClientType instead of CLIENT_SLAVE\n 2 files changed, 18 insertions(+), 26 deletions(-)\n\nGuy Benoish in commit fae306b3:\n Fix small bugs related to replica and monitor ambiguity\n 2 files changed, 8 insertions(+), 6 deletions(-)\n\nYossi Gottlieb in commit 73630966:\n TLS: Some redis.conf clarifications.\n 1 file changed, 10 insertions(+), 11 deletions(-)\n\nOran Agra in commit 488e1947:\n config.c verbose error replies for CONFIG SET, like config file parsing\n 1 file changed, 31 insertions(+), 97 deletions(-)\n\nOran Agra in commit c82ccf06:\n memoryGetKeys helper function so that ACL can limit access to keys for MEMORY command\n 3 files changed, 18 insertions(+), 1 deletion(-)\n\nantirez in commit 51c1a9f8:\n ACL LOG: make max log entries configurable.\n 4 files changed, 19 insertions(+)\n\nantirez in commit ea1e1b12:\n ACL LOG: test for AUTH reason.\n 1 file changed, 9 insertions(+)\n\nantirez in commit 7379c78a:\n ACL LOG: log failed auth attempts.\n 5 files changed, 34 insertions(+), 12 deletions(-)\n\nantirez in commit 9f6e84f6:\n ACL LOG: implement a few basic tests.\n 1 file changed, 87 insertions(+)\n\nantirez in commit 82790e51:\n ACL LOG: also log ACL errors in the scripting/MULTI ctx.\n 2 files changed, 6 insertions(+), 2 deletions(-)\n\nantirez in commit 943008eb:\n ACL LOG: implement LOG RESET.\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\nantirez in commit e271a611:\n ACL LOG: group similar entries in a given time delta.\n 1 file changed, 58 insertions(+), 3 deletions(-)\n\nantirez in commit f1974d5d:\n ACL LOG: actually emit entries.\n 3 files changed, 34 insertions(+), 5 deletions(-)\n\nantirez in commit d9b153c9:\n ACL LOG: implement ACL LOG subcommadn skeleton.\n 1 file changed, 37 insertions(+)\n\nantirez in commit 577fc438:\n ACL LOG: data structures and initial functions.\n 5 files changed, 54 insertions(+), 5 deletions(-)\n\nLeo Murillo in commit f7a94526:\n Set ZSKIPLIST_MAXLEVEL to optimal value given 2^64 elements and p=0.25\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nWuYunlong in commit eecfa979:\n Fix lua related memory leak.\n 1 file changed, 1 insertion(+)\n\nWuYunlong in commit d2509811:\n Add tcl regression test in scripting.tcl to reproduce memory leak.\n 1 file changed, 5 insertions(+)\n\nYossi Gottlieb in commit 29d4a150:\n TLS: Fix missing initialization in redis-cli.\n 1 file changed, 9 insertions(+)\n\nOran Agra in commit ec0c61da:\n fix uninitialized info_cb var in module.c\n 1 file changed, 1 insertion(+)\n\nGuy Benoish in commit 6fe55c2f:\n ld2string should fail if string contains \\0 in the middle\n 5 files changed, 20 insertions(+), 11 deletions(-)\n\nantirez in commit bbce3ba9:\n Add more info in the unblockClientFromModule() function.\n 1 file changed, 7 insertions(+), 1 deletion(-)\n\nGuy Benoish in commit 40295fb3:\n Modules: Fix blocked-client-related memory leak\n 3 files changed, 51 insertions(+), 6 deletions(-)\n\nantirez in commit 8e9d19bc:\n Change error message for #6775.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nVasyl Melnychuk in commit ba146d4c:\n Make error when submitting command in incorrect context more explicit\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nantirez in commit 721a39dd:\n Document I/O threads in redis.conf.\n 1 file changed, 46 insertions(+)\n\nantirez in commit 5be3a15a:\n Setting N I/O threads should mean N-1 additional + 1 main thread.\n 1 file changed, 25 insertions(+), 22 deletions(-)\n\nantirez in commit cbabf779:\n Simplify #6379 changes.\n 2 files changed, 4 insertions(+), 9 deletions(-)\n\nWuYunlong in commit 658749cc:\n Free allocated sds in pfdebugCommand() to avoid memory leak.\n 1 file changed, 1 insertion(+)\n\nWuYunlong in commit 47988c96:\n Fix potential memory leak of clusterLoadConfig().\n 1 file changed, 20 insertions(+), 5 deletions(-)\n\nWuYunlong in commit cc90f79b:\n Fix potential memory leak of rioWriteBulkStreamID().\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\nantirez in commit ecd17e81:\n Jump to right label on AOF parsing error.\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\nantirez in commit 1927932b:\n Port PR #6110 to new connection object code.\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nantirez in commit f2df5773:\n A few comments about main thread serving I/O as well.\n 1 file changed, 7 insertions(+), 1 deletion(-)\n\nzhaozhao.zz in commit b3ff8a4b:\n Threaded IO: use main thread to handle read work\n 1 file changed, 8 insertions(+), 1 deletion(-)\n\nzhaozhao.zz in commit b1f2c510:\n Threaded IO: use main thread to handle write work\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\nShooterIT in commit 7bbafc56:\n Rename rdb asynchronously\n 1 file changed, 7 insertions(+)\n\nLeo Murillo in commit c7f75266:\n Fix bug on KEYS command where pattern starts with * followed by \\x00 (null char).\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nJamie Scott in commit ed7ea13a:\n Update to directive in redis.conf (missing s)\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 3be77623:\n Free fakeclient argv on AOF error.\n 1 file changed, 11 insertions(+), 3 deletions(-)\n\nantirez in commit 15f6b748:\n Git ignore: ignore more files.\n 1 file changed, 2 insertions(+)\n\nGuy Benoish in commit 1b5bf40c:\n Blocking XREAD[GROUP] should always reply with valid data (or timeout)\n 3 files changed, 44 insertions(+), 10 deletions(-)\n\nJohn Sully in commit 954c20ed:\n Add support for incremental build with header files\n 2 files changed, 6 insertions(+), 1 deletion(-)\n\nWuYunlong in commit 11c3afd7:\n Fix petential cluster link error.\n 1 file changed, 4 insertions(+)\n\nYossi Gottlieb in commit b752e83d:\n Add REDISMODULE_CTX_FLAGS_MULTI_DIRTY.\n 2 files changed, 8 insertions(+)\n\nhwware in commit e16eb874:\n typo fix in acl.c\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\nItamar Haber in commit 35ea9d23:\n Adjusts 'io_threads_num' max to 128\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 38729126:\n XCLAIM: Create the consumer only on successful claims.\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\nyz1509 in commit b9a15303:\n avoid sentinel changes promoted_slave to be its own replica.\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\nantirez in commit 5e7e5e6b:\n Fix active expire division by zero.\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\nantirez in commit e61dde88:\n Fix duplicated CLIENT SETNAME reply.\n 1 file changed, 1 deletion(-)\n\nGuy Benoish in commit cddf1da2:\n Stream: Handle streamID-related edge cases\n 4 files changed, 54 insertions(+), 4 deletions(-)\n\nOran Agra in commit 52ea44e5:\n config.c adjust config limits and mutable\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\nantirez in commit 0f28ea16:\n Inline protocol: handle empty strings well.\n 1 file changed, 2 insertions(+), 6 deletions(-)\n\nantirez in commit 00e5fefe:\n Fix ip and missing mode in RM_GetClusterNodeInfo().\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\n================================================================================\nRedis 6.0 RC1   Released Thu Dec 19 09:58:24 CEST 2019\n================================================================================\n\nUpgrade urgency LOW: This is the first RC of Redis 6.\n\nIntroduction to the Redis 6 release\n===================================\n\nRedis 6 improves Redis in a number of key areas and is one of the largest\nRedis releases in the history of the project, so here we'll list only\nthe biggest features in this release:\n\n* The modules system now has a number of new APIs that allow module authors\n  to make things otherwise not possible in the past. It is possible to\n  store arbitrary module private data in RDB files, to hook on different\n  server events, capture and rewrite commands executions, block clients on\n  keys, and so forth. \n* The Redis active expire cycle was rewritten for much faster eviction of keys\n  that are already expired. Now the effort is tunable.\n* Redis now supports SSL on all channels.\n* ACL support, you can define users that can run only certain commands and/or\n  can only access only certain keys patterns.\n* Redis now supports a new protocol called RESP3, which returns more\n  semantical replies: new clients using this protocol can understand just\n  from the reply what type to return to the calling program.\n* There is server-side support for client-side caching of key values. This\n  feature is still experimental and will get more changes during the next\n  release candidates, but you can already test it and read about it here:\n  https://redis.io/topics/client-side-caching\n* Redis can now optionally use threads to handle I/O, allowing to serve\n  2 times as much operations per second in a single instance when\n  pipelining cannot be used.\n* Diskless replication is now supported even on replicas: a replica is now\n  able, under certain conditions the user can configure, to load the RDB\n  in the first synchronization directly from the socket to the memory.\n* Redis-benchmark now supports a Redis Cluster mode.\n* SRANDMEMBER and similar commands have a better distribution.\n* Redis-cli improvements.\n* Systemd support rewritten.\n* A Redis Cluster proxy was released here:\n  https://github.com/artix75/redis-cluster-proxy\n* A Disque module for Redis was released here:\n  https://github.com/antirez/disque-module\n\nThanks to all the users and developers who made this release possible.\nWe'll follow up with more RC releases, until the code looks production ready\nand we don't get reports of serious issues for a while.\n\nA special thank you for the amount of work put into this release\n(in decreasing number of commits, only listing contributors with more\nthan a single commit) by:\n\n   685  antirez\n    81  zhaozhao.zz\n    76  Oran Agra\n    51  artix\n    28  Madelyn Olson\n    27  Yossi Gottlieb\n    15  David Carlier\n    14  Guy Benoish\n    14  Guy Korland\n    13  Itamar Haber\n     9  Angus Pearson\n     8  WuYunlong\n     8  yongman\n     7  vattezhang\n     7  Chris Lamb\n     5  Dvir Volk\n     5  meir@redislabs.com\n     5  chendianqiang\n     5  John Sully\n     4  dejun.xdj\n     4  Daniel Dai\n     4  Johannes Truschnigg\n     4  swilly22\n     3  Bruce Merry\n     3  filipecosta90\n     3  youjiali1995\n     2  James Rouzier\n     2  Andrey Bugaevskiy\n     2  Brad Solomon\n     2  Hamid Alaei\n     2  Michael Chaten\n     2  Steve Webster\n     2  Wander Hillen\n     2  Weiliang Li\n     2  Yuan Zhou\n     2  charsyam\n     2  hujie\n     2  jem\n     2  shenlongxing\n     2  valentino\n     2  zhudacai 00228490\n     2  喜欢兰花山丘\n\nMigrating from 5.0 to 6.0\n=========================\n\nRedis 6.0 is mostly a strict superset of 5.0, you should not have any problem\nupgrading your application from 5.0 to 6.0. However this is a list of small\nnon-backward compatible changes introduced in the 6.0 release:\n\n* The SPOP <count> command no longer returns null when the set key does not\n  exist. Now it returns the empty set as it should and as happens when it is\n  called with a 0 argument. This is technically a fix, however it changes the\n  old behavior.\n\n--------------------------------------------------------------------------------\n\nCredits: For each release, a list of changes with the relative author is\nprovided. Where not specified the implementation and design is done by\nSalvatore Sanfilippo. Thanks to Redis Labs for making all this possible.\nAlso many thanks to all the other contributors and the amazing community\nwe have.\n\nCommit messages may contain additional credits.\n\nEnjoy,\nSalvatore\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/BUGS",
    "content": "Please check https://github.com/antirez/redis/issues\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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      https://groups.google.com/forum/m/#!forum/Redis-db\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 mailing list.\n\n  There is also an active community of Redis users at Stack Overflow:\n\n      http://stackoverflow.com/questions/tagged/redis\n\n# How to provide a patch for a new feature\n\n1. If it is a major feature or a semantical change, please don't start coding\nstraight away: if your feature is not a conceptual fit you'll lose a lot of\ntime writing the code without any reason. Start by posting in the mailing list\nand creating an issue at Github with the description of, exactly, what you want\nto accomplish and why. Use cases are important for features to be accepted.\nHere you'll see if there is consensus about your idea.\n\n2. If in step 1 you get an acknowledgment 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 ( https://help.github.com/articles/creating-a-pull-request/ )\n    e. Done :)\n\n3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait\nfor a *very* long time. However this is not lack of interest, as the project\ngets more and more users, we find ourselves in a constant need to prioritize\ncertain issues/PRs over others. If you think your issue/PR is very important\ntry to popularize it, have other users commenting and sharing their point of\nview and so forth. This helps.\n\n4. For minor fixes just open a pull request on Github.\n\nThanks!\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/COPYING",
    "content": "Copyright (c) 2006-2015, 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": "Chapter07/redis-6.0.3/INSTALL",
    "content": "See README\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 - We believe in code efficiency. Computers get faster and faster, yet we\n    believe that abusing computing capabilities is not wise: the amount of\n    operations you can do for a given amount of energy remains anyway a\n    significant parameter: it allows to do more with less computers and, at\n    the same time, having a smaller environmental impact. Similarly Redis is\n    able to \"scale down\" to smaller devices. It is perfectly usable in a\n    Raspberry Pi and other small ARM based computers. Faster code having\n    just the layers of abstractions that are really needed will also result,\n    often, in more predictable performances. We think likewise about memory\n    usage, one of the fundamental goals of the Redis project is to\n    incrementally build more and more memory efficient data structures, so that\n    problems that were not approachable in RAM in the past will be perfectly\n    fine to handle in the future.\n\n5 - 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\n6 - 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. Complexity is also a form of lock-in: code that is\n    very hard to understand cannot be modified by users in an independent way\n    regardless of the license. One of the main Redis goals is to remain\n    understandable, enough for a single programmer to have a clear idea of how\n    it works in detail just reading the source code for a couple of weeks.\n\n7 - Threading is not a silver bullet. Instead of making Redis threaded we\n    believe on the idea of an efficient (mostly) single threaded Redis core.\n    Multiple of such cores, that may run in the same computer or may run\n    in multiple computers, are abstracted away as a single big system by\n    higher order protocols and features: Redis Cluster and the upcoming\n    Redis Proxy are our main goals. A shared nothing approach is not just\n    much simpler (see the previous point in this document), is also optimal\n    in NUMA systems. In the specific case of Redis it allows for each instance\n    to have a more limited amount of data, making the Redis persist-by-fork\n    approach more sounding. In the future we may explore parallelism only for\n    I/O, which is the low hanging fruit: minimal complexity could provide an\n    improved single process experience.\n\n8 - 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\n    trade-offs to the user.\n\n9 - 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\n10 - All the above points are put together in what we call opportunistic\n     programming: trying to get the most for the user with minimal increases\n     in complexity (hanging fruits). Solve 95% of the problem with 5% of the\n     code when it is acceptable. Avoid a fixed schedule but follow the flow of\n     user requests, inspiration, Redis internal readiness for certain features\n     (sometimes many past changes reach a critical point making a previously\n     complex feature very easy to obtain).\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/README.md",
    "content": "This README is just a fast *quick start* document. You can find more detailed documentation at [redis.io](https://redis.io).\n\nWhat is Redis?\n--------------\n\nRedis is often referred as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.\n\nData structures implemented into Redis have a few special properties:\n\n* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that is also non-volatile.\n* Implementation of data structures stress on memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modeled using an high level programming language.\n* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, cluster, high availability.\n\nAnother good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations to work with complex data types like Lists, Sets, ordered data structures, and so forth.\n\nIf you want to know more, this is a list of selected starting points:\n\n* Introduction to Redis data types. http://redis.io/topics/data-types-intro\n* Try Redis directly inside your browser. http://try.redis.io\n* The full list of Redis commands. http://redis.io/commands\n* There is much more inside the Redis official documentation. http://redis.io/documentation\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, and both 32 bit\nand 64 bit systems.\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\nTo build with TLS support, you'll need OpenSSL development libraries (e.g.\nlibssl-dev on Debian/Ubuntu) and run:\n\n    % make BUILD_TLS=yes\n\nYou can run a 32 bit Redis binary using:\n\n    % make 32bit\n\nAfter building Redis, it is a good idea to test it using:\n\n    % make test\n\nIf TLS is built, running the tests with TLS enabled (you will need `tcl-tls`\ninstalled):\n\n    % ./utils/gen-test-certs.sh\n    % ./runtest --tls\n\n\nFixing build problems with dependencies or cached build options\n---------\n\nRedis has some dependencies which are included into the `deps` directory.\n`make` does not automatically rebuild dependencies even if something in\nthe source code of dependencies changes.\n\nWhen you update the source code with `git pull` or when code inside the\ndependencies tree is modified in any other way, make sure to use the following\ncommand in order to really clean everything and rebuild from scratch:\n\n    make distclean\n\nThis will clean: jemalloc, lua, hiredis, linenoise.\n\nAlso if you force certain build options like 32bit target, no C compiler\noptimizations (for debugging purposes), and other similar build time options,\nthose options are cached indefinitely until you issue a `make distclean`\ncommand.\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  `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 by passing parameters directly\nas options using the command line. Examples:\n\n    % ./redis-server --port 9999 --replicaof 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\nRunning Redis with TLS:\n------------------\n\nPlease consult the [TLS.md](TLS.md) file for more information on\nhow to use Redis with TLS.\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 at 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\n_Note_: `install_server.sh` will not work on Mac OSX; it is built for Linux only.\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][1] file included in the Redis\nsource distribution.\n\nPlease see the [CONTRIBUTING][2] file in this source distribution for more\ninformation.\n\n[1]: https://github.com/antirez/redis/blob/unstable/COPYING\n[2]: https://github.com/antirez/redis/blob/unstable/CONTRIBUTING\n\nRedis internals\n===\n\nIf you are reading this README you are likely in front of a Github page\nor you just untarred the Redis distribution tar ball. In both the cases\nyou are basically one step away from the source code, so here we explain\nthe Redis source code layout, what is in each file as a general idea, the\nmost important functions and structures inside the Redis server and so forth.\nWe keep all the discussion at a high level without digging into the details\nsince this document would be huge otherwise and our code base changes\ncontinuously, but a general idea should be a good starting point to\nunderstand more. Moreover most of the code is heavily commented and easy\nto follow.\n\nSource code layout\n---\n\nThe Redis root directory just contains this README, the Makefile which\ncalls the real Makefile inside the `src` directory and an example\nconfiguration for Redis and Sentinel. You can find a few shell\nscripts that are used in order to execute the Redis, Redis Cluster and\nRedis Sentinel unit tests, which are implemented inside the `tests`\ndirectory.\n\nInside the root are the following important directories:\n\n* `src`: contains the Redis implementation, written in C.\n* `tests`: contains the unit tests, implemented in Tcl.\n* `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `antirez/redis`.\n\nThere are a few more directories but they are not very important for our goals\nhere. We'll focus mostly on `src`, where the Redis implementation is contained,\nexploring what there is inside each file. The order in which files are\nexposed is the logical one to follow in order to disclose different layers\nof complexity incrementally.\n\nNote: lately Redis was refactored quite a bit. Function names and file\nnames have been changed, so you may find that this documentation reflects the\n`unstable` branch more closely. For instance in Redis 3.0 the `server.c`\nand `server.h` files were named `redis.c` and `redis.h`. However the overall\nstructure is the same. Keep in mind that all the new developments and pull\nrequests should be performed against the `unstable` branch.\n\nserver.h\n---\n\nThe simplest way to understand how a program works is to understand the\ndata structures it uses. So we'll start from the main header file of\nRedis, which is `server.h`.\n\nAll the server configuration and in general all the shared state is\ndefined in a global structure called `server`, of type `struct redisServer`.\nA few important fields in this structure are:\n\n* `server.db` is an array of Redis databases, where data is stored.\n* `server.commands` is the command table.\n* `server.clients` is a linked list of clients connected to the server.\n* `server.master` is a special client, the master, if the instance is a replica.\n\nThere are tons of other fields. Most fields are commented directly inside\nthe structure definition.\n\nAnother important Redis data structure is the one defining a client.\nIn the past it was called `redisClient`, now just `client`. The structure\nhas many fields, here we'll just show the main ones:\n\n    struct client {\n        int fd;\n        sds querybuf;\n        int argc;\n        robj **argv;\n        redisDb *db;\n        int flags;\n        list *reply;\n        char buf[PROTO_REPLY_CHUNK_BYTES];\n        ... many other fields ...\n    }\n\nThe client structure defines a *connected client*:\n\n* The `fd` field is the client socket file descriptor.\n* `argc` and `argv` are populated with the command the client is executing, so that functions implementing a given Redis command can read the arguments.\n* `querybuf` accumulates the requests from the client, which are parsed by the Redis server according to the Redis protocol and executed by calling the implementations of the commands the client is executing.\n* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writable.\n\nAs you can see in the client structure above, arguments in a command\nare described as `robj` structures. The following is the full `robj`\nstructure, which defines a *Redis object*:\n\n    typedef struct redisObject {\n        unsigned type:4;\n        unsigned encoding:4;\n        unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */\n        int refcount;\n        void *ptr;\n    } robj;\n\nBasically this structure can represent all the basic Redis data types like\nstrings, lists, sets, sorted sets and so forth. The interesting thing is that\nit has a `type` field, so that it is possible to know what type a given\nobject has, and a `refcount`, so that the same object can be referenced\nin multiple places without allocating it multiple times. Finally the `ptr`\nfield points to the actual representation of the object, which might vary\neven for the same type, depending on the `encoding` used.\n\nRedis objects are used extensively in the Redis internals, however in order\nto avoid the overhead of indirect accesses, recently in many places\nwe just use plain dynamic strings not wrapped inside a Redis object.\n\nserver.c\n---\n\nThis is the entry point of the Redis server, where the `main()` function\nis defined. The following are the most important steps in order to startup\nthe Redis server.\n\n* `initServerConfig()` setups the default values of the `server` structure.\n* `initServer()` allocates the data structures needed to operate, setup the listening socket, and so forth.\n* `aeMain()` starts the event loop which listens for new connections.\n\nThere are two special functions called periodically by the event loop:\n\n1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timedout clients.\n2. `beforeSleep()` is called every time the event loop fired, Redis served a few requests, and is returning back into the event loop.\n\nInside server.c you can find code that handles other vital things of the Redis server:\n\n* `call()` is used in order to call a given command in the context of a given client.\n* `activeExpireCycle()` handles eviciton of keys with a time to live set via the `EXPIRE` command.\n* `freeMemoryIfNeeded()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.\n* The global variable `redisCommandTable` defines all the Redis commands, specifying the name of the command, the function implementing the command, the number of arguments required, and other properties of each command.\n\nnetworking.c\n---\n\nThis file defines all the I/O functions with clients, masters and replicas\n(which in Redis are just special clients):\n\n* `createClient()` allocates and initializes a new client.\n* the `addReply*()` family of functions are used by commands implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.\n* `writeToClient()` transmits the data pending in the output buffers to the client and is called by the *writable event handler* `sendReplyToClient()`.\n* `readQueryFromClient()` is the *readable event handler* and accumulates data from read from the client into the query buffer.\n* `processInputBuffer()` is the entry point in order to parse the client query buffer according to the Redis protocol. Once commands are ready to be processed, it calls `processCommand()` which is defined inside `server.c` in order to actually execute the command.\n* `freeClient()` deallocates, disconnects and removes a client.\n\naof.c and rdb.c\n---\n\nAs you can guess from the names these files implement the RDB and AOF\npersistence for Redis. Redis uses a persistence model based on the `fork()`\nsystem call in order to create a thread with the same (shared) memory\ncontent of the main Redis thread. This secondary thread dumps the content\nof the memory on disk. This is used by `rdb.c` to create the snapshots\non disk and by `aof.c` in order to perform the AOF rewrite when the\nappend only file gets too big.\n\nThe implementation inside `aof.c` has additional functions in order to\nimplement an API that allows commands to append new commands into the AOF\nfile as clients execute them.\n\nThe `call()` function defined inside `server.c` is responsible to call\nthe functions that in turn will write the commands into the AOF.\n\ndb.c\n---\n\nCertain Redis commands operate on specific data types, others are general.\nExamples of generic commands are `DEL` and `EXPIRE`. They operate on keys\nand not on their values specifically. All those generic commands are\ndefined inside `db.c`.\n\nMoreover `db.c` implements an API in order to perform certain operations\non the Redis dataset without directly accessing the internal data structures.\n\nThe most important functions inside `db.c` which are used in many commands\nimplementations are the following:\n\n* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.\n* `dbAdd()` and its higher level counterpart `setKey()` create a new key in a Redis database.\n* `dbDelete()` removes a key and its associated value.\n* `emptyDb()` removes an entire single database or all the databases defined.\n\nThe rest of the file implements the generic commands exposed to the client.\n\nobject.c\n---\n\nThe `robj` structure defining Redis objects was already described. Inside\n`object.c` there are all the functions that operate with Redis objects at\na basic level, like functions to allocate new objects, handle the reference\ncounting and so forth. Notable functions inside this file:\n\n* `incrRefcount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.\n* `createObject()` allocates a new object. There are also specialized functions to allocate string objects having a specific content, like `createStringObjectFromLongLong()` and similar functions.\n\nThis file also implements the `OBJECT` command.\n\nreplication.c\n---\n\nThis is one of the most complex files inside Redis, it is recommended to\napproach it only after getting a bit familiar with the rest of the code base.\nIn this file there is the implementation of both the master and replica role\nof Redis.\n\nOne of the most important functions inside this file is `replicationFeedSlaves()` that writes commands to the clients representing replica instances connected\nto our master, so that the replicas can get the writes performed by the clients:\nthis way their data set will remain synchronized with the one in the master.\n\nThis file also implements both the `SYNC` and `PSYNC` commands that are\nused in order to perform the first synchronization between masters and\nreplicas, or to continue the replication after a disconnection.\n\nOther C files\n---\n\n* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client commands implementations for these data types.\n* `ae.c` implements the Redis event loop, it's a self contained library which is simple to read and understand.\n* `sds.c` is the Redis string library, check http://github.com/antirez/sds for more information.\n* `anet.c` is a library to use POSIX networking in a simpler way compared to the raw interface exposed by the kernel.\n* `dict.c` is an implementation of a non-blocking hash table which rehashes incrementally.\n* `scripting.c` implements Lua scripting. It is completely self contained from the rest of the Redis implementation and is simple enough to understand if you are familar with the Lua API.\n* `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3].\n\n[3]: http://redis.io/topics/cluster-spec\n\nAnatomy of a Redis command\n---\n\nAll the Redis commands are defined in the following way:\n\n    void foobarCommand(client *c) {\n        printf(\"%s\",c->argv[1]->ptr); /* Do something with the argument. */\n        addReply(c,shared.ok); /* Reply something to the client. */\n    }\n\nThe command is then referenced inside `server.c` in the command table:\n\n    {\"foobar\",foobarCommand,2,\"rtF\",0,NULL,0,0,0,0,0},\n\nIn the above example `2` is the number of arguments the command takes,\nwhile `\"rtF\"` are the command flags, as documented in the command table\ntop comment inside `server.c`.\n\nAfter the command operates in some way, it returns a reply to the client,\nusually using `addReply()` or a similar function defined inside `networking.c`.\n\nThere are tons of commands implementations inside the Redis source code\nthat can serve as examples of actual commands implementations. To write\na few toy commands can be a good exercise to familiarize with the code base.\n\nThere are also many other files not described here, but it is useless to\ncover everything. We want to just help you with the first steps.\nEventually you'll find your way inside the Redis code base :-)\n\nEnjoy!\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/TLS.md",
    "content": "TLS Support\n===========\n\nGetting Started\n---------------\n\n### Building\n\nTo build with TLS support you'll need OpenSSL development libraries (e.g.\nlibssl-dev on Debian/Ubuntu).\n\nRun `make BUILD_TLS=yes`.\n\n### Tests\n\nTo run Redis test suite with TLS, you'll need TLS support for TCL (i.e.\n`tcl-tls` package on Debian/Ubuntu).\n\n1. Run `./utils/gen-test-certs.sh` to generate a root CA and a server\n   certificate.\n\n2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis\n   Cluster tests in TLS mode.\n\n### Running manually\n\nTo manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was\ninvoked so sample certificates/keys are available):\n\n    ./src/redis-server --tls-port 6379 --port 0 \\\n        --tls-cert-file ./tests/tls/redis.crt \\\n        --tls-key-file ./tests/tls/redis.key \\\n        --tls-ca-cert-file ./tests/tls/ca.crt\n\nTo connect to this Redis server with `redis-cli`:\n\n    ./src/redis-cli --tls \\\n        --cert ./tests/tls/redis.crt \\\n        --key ./tests/tls/redis.key \\\n        --cacert ./tests/tls/ca.crt\n\nThis will disable TCP and enable TLS on port 6379. It's also possible to have\nboth TCP and TLS available, but you'll need to assign different ports.\n\nTo make a Replica connect to the master using TLS, use `--tls-replication yes`,\nand to make Redis Cluster use TLS across nodes use `--tls-cluster yes`.\n\nConnections\n-----------\n\nAll socket operations now go through a connection abstraction layer that hides\nI/O and read/write event handling from the caller.\n\n**Multi-threading I/O is not currently supported for TLS**, as a TLS connection\nneeds to do its own manipulation of AE events which is not thread safe. The\nsolution is probably to manage independent AE loops for I/O threads and longer\nterm association of connections with threads. This may potentially improve\noverall performance as well.\n\nSync IO for TLS is currently implemented in a hackish way, i.e. making the\nsocket blocking and configuring socket-level timeout.  This means the timeout\nvalue may not be so accurate, and there would be a lot of syscall overhead.\nHowever I believe that getting rid of syncio completely in favor of pure async\nwork is probably a better move than trying to fix that. For replication it would\nprobably not be so hard. For cluster keys migration it might be more difficult,\nbut there are probably other good reasons to improve that part anyway.\n\nTo-Do List\n----------\n\n- [ ] Add session caching support. Check if/how it's handled by clients to\n  assess how useful/important it is.\n- [ ] redis-benchmark support. The current implementation is a mix of using\n  hiredis for parsing and basic networking (establishing connections), but\n  directly manipulating sockets for most actions. This will need to be cleaned\n  up for proper TLS support. The best approach is probably to migrate to hiredis\n  async mode.\n- [ ] redis-cli `--slave` and `--rdb` support.\n\nMulti-port\n----------\n\nConsider the implications of allowing TLS to be configured on a separate port,\nmaking Redis listening on multiple ports:\n\n1. Startup banner port notification\n2. Proctitle\n3. How slaves announce themselves\n4. Cluster bus port calculation\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\nifeq ($(BUILD_TLS),yes)\n    HIREDIS_MAKE_FLAGS = USE_SSL=1\nendif\n\nhiredis: .make-prerequisites\n\t@printf '%b %b\\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)\n\tcd hiredis && $(MAKE) static $(HIREDIS_MAKE_FLAGS)\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 -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' $(CFLAGS)\nLUA_LDFLAGS+= $(LDFLAGS)\n# lua's Makefile defines AR=\"ar rcu\", which is unusual, and makes it more\n# challenging to cross-compile lua (and redis).  These defines make it easier\n# to fit redis into cross-compilation environments, which typically set AR.\nAR=ar\nARFLAGS=rcu\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)\" AR=\"$(AR) $(ARFLAGS)\"\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-version=5.1.0-0-g0 --with-lg-quantum=3 --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": "Chapter07/redis-6.0.3/deps/README.md",
    "content": "This directory contains all Redis dependencies, except for the libc that\nshould be provided by the operating system.\n\n* **Jemalloc** is our memory allocator, used as replacement for libc malloc on Linux by default. It has good performances and excellent fragmentation behavior. This component is upgraded from time to time.\n* **hiredis** is the official C client library for Redis. It is used by redis-cli, redis-benchmark and Redis Sentinel. It is part of the Redis official ecosystem but is developed externally from the Redis repository, so we just upgrade it as needed.\n* **linenoise** is a readline replacement. It is developed by the same authors of Redis but is managed as a separated project and updated as needed.\n* **lua** is Lua 5.1 with minor changes for security and additional libraries.\n\nHow to upgrade the above dependencies\n===\n\nJemalloc\n---\n\nJemalloc is modified with changes that allow us to implement the Redis\nactive defragmentation logic. However this feature of Redis is not mandatory\nand Redis is able to understand if the Jemalloc version it is compiled\nagainst supports such Redis-specific modifications. So in theory, if you\nare not interested in the active defragmentation, you can replace Jemalloc\njust following tose steps:\n\n1. Remove the jemalloc directory.\n2. Substitute it with the new jemalloc source tree.\n3. Edit the Makefile localted in the same directory as the README you are\n   reading, and change the --with-version in the Jemalloc configure script\n   options with the version you are using. This is required because otherwise\n   Jemalloc configuration script is broken and will not work nested in another\n   git repository.\n\nHowever note that we change Jemalloc settings via the `configure` script of Jemalloc using the `--with-lg-quantum` option, setting it to the value of 3 instead of 4. This provides us with more size classes that better suit the Redis data structures, in order to gain memory efficiency.\n\nIf you want to upgrade Jemalloc while also providing support for\nactive defragmentation, in addition to the above steps you need to perform\nthe following additional steps:\n\n5. In Jemalloc three, file `include/jemalloc/jemalloc_macros.h.in`, make sure\n   to add `#define JEMALLOC_FRAG_HINT`.\n6. Implement the function `je_get_defrag_hint()` inside `src/jemalloc.c`. You\n   can see how it is implemented in the current Jemalloc source tree shipped\n   with Redis, and rewrite it according to the new Jemalloc internals, if they\n   changed, otherwise you could just copy the old implementation if you are\n   upgrading just to a similar version of Jemalloc.\n\nHiredis\n---\n\nHiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:\n\n1. Check with diff if hiredis API changed and what impact it could have in Redis.\n2. Make sure thet the SDS library inside Hiredis and inside Redis are compatible.\n3. After the upgrade, run the Redis Sentinel test.\n4. Check manually that redis-cli and redis-benchmark behave as expecteed, since we have no tests for CLI utilities currently.\n\nLinenoise\n---\n\nLinenoise is rarely upgraded as needed. The upgrade process is trivial since\nRedis uses a non modified version of linenoise, so to upgrade just do the\nfollowing:\n\n1. Remove the linenoise directory.\n2. Substitute it with the new linenoise source tree.\n\nLua\n---\n\nWe use Lua 5.1 and no upgrade is planned currently, since we don't want to break\nLua scripts for new Lua features: in the context of Redis Lua scripts the\ncapabilities of 5.1 are usually more than enough, the release is rock solid,\nand we definitely don't want to break old scripts.\n\nSo upgrading of Lua is up to the Redis project maintainers and should be a\nmanual procedure performed by taking a diff between the different versions.\n\nCurrently we have at least the following differences between official Lua 5.1\nand our version:\n\n1. Makefile is modified to allow a different compiler than GCC.\n2. We have the implementation source code, and directly link to the following external libraries: `lua_cjson.o`, `lua_struct.o`, `lua_cmsgpack.o` and `lua_bit.o`.\n3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order toa void direct bytecode execution.\n\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/.gitignore",
    "content": "/hiredis-test\n/examples/hiredis-example*\n/*.o\n/*.so\n/*.dylib\n/*.a\n/*.pc\n*.dSYM\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/.travis.yml",
    "content": "language: c\nsudo: false\ncompiler:\n  - gcc\n  - clang\n\nos:\n  - linux\n  - osx\n\nbranches:\n  only:\n    - staging\n    - trying\n    - master\n\nbefore_script:\n    - if [ \"$TRAVIS_OS_NAME\" == \"osx\" ] ; then brew update; brew install redis; fi\n\naddons:\n  apt:\n    packages:\n    - libc6-dbg\n    - libc6-dev\n    - libc6:i386\n    - libc6-dev-i386\n    - libc6-dbg:i386\n    - gcc-multilib\n    - g++-multilib\n    - valgrind\n\nenv:\n  - BITS=\"32\"\n  - BITS=\"64\"\n\nscript:\n  - EXTRA_CMAKE_OPTS=\"-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON\";\n    if [ \"$TRAVIS_OS_NAME\" == \"osx\" ]; then\n      if [ \"$BITS\" == \"32\" ]; then\n        CFLAGS=\"-m32 -Werror\";\n        CXXFLAGS=\"-m32 -Werror\";\n        LDFLAGS=\"-m32\";\n        EXTRA_CMAKE_OPTS=;\n      else\n        CFLAGS=\"-Werror\";\n        CXXFLAGS=\"-Werror\";\n      fi;\n    else\n      TEST_PREFIX=\"valgrind --track-origins=yes --leak-check=full\";\n      if [ \"$BITS\" == \"32\" ]; then\n        CFLAGS=\"-m32 -Werror\";\n        CXXFLAGS=\"-m32 -Werror\";\n        LDFLAGS=\"-m32\";\n        EXTRA_CMAKE_OPTS=;\n      else\n        CFLAGS=\"-Werror\";\n        CXXFLAGS=\"-Werror\";\n      fi;\n    fi;\n    export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS\n  - mkdir build/ && cd build/\n  - cmake .. ${EXTRA_CMAKE_OPTS}\n  - make VERBOSE=1\n  - ctest -V\n\nmatrix:\n  include:\n    # Windows MinGW cross compile on Linux\n    - os: linux\n      dist: xenial\n      compiler: mingw\n      addons:\n        apt:\n          packages:\n            - ninja-build\n            - gcc-mingw-w64-x86-64\n            - g++-mingw-w64-x86-64\n      script:\n        - mkdir build && cd build\n        - CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on\n        - ninja -v\n\n    # Windows MSVC 2017\n    - os: windows\n      compiler: msvc\n      env:\n        - MATRIX_EVAL=\"CC=cl.exe && CXX=cl.exe\"\n      before_install:\n        - eval \"${MATRIX_EVAL}\"\n      install:\n        - choco install ninja\n      script:\n        - mkdir build && cd build\n        - cmd.exe /C '\"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Auxiliary\\Build\\vcvarsall.bat\" amd64 &&\n          cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release &&\n          ninja -v'\n        - ctest -V\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/CHANGELOG.md",
    "content": "### 1.0.0 (unreleased)\n\n**BREAKING CHANGES**:\n\n* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now\n  protocol errors. This is consistent with the RESP specification. On 32-bit\n  platforms, the upper bound is lowered to `SIZE_MAX`.\n\n* Change `redisReply.len` to `size_t`, as it denotes the the size of a string\n\n  User code should compare this to `size_t` values as well.  If it was used to\n  compare to other values, casting might be necessary or can be removed, if\n  casting was applied before.\n\n### 0.x.x (unreleased)\n**BREAKING CHANGES**:\n\n* Change `redisReply.len` to `size_t`, as it denotes the the size of a string\n\nUser code should compare this to `size_t` values as well.\nIf it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.\n\n* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.\n\n### 0.14.0 (2018-09-25)\n\n* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])\n* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])\n* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])\n* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])\n* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])\n* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])\n* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])\n* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])\n* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])\n* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])\n* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])\n* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])\n* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])\n* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])\n* Fix libevent leak (zfz [515228])\n* Clean up GCC warning (Ichito Nagata [2ec774])\n* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])\n* Solaris compilation fix (Donald Whyte [41b07d])\n* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])\n* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])\n* libuv use after free fix (Paul Scott [cbb956])\n* Properly close socket fd on reconnect attempt (WSL [64d1ec])\n* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])\n* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])\n* Update libevent (Chris Xin [386802])\n* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])\n* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])\n* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])\n* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])\n* Compatibility fix for strerror_r (Tom Lee [bb1747])\n* Properly detect integer parse/overflow errors (Justin Brewer [93421f])\n* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])\n* Catch a buffer overflow when formatting the error message\n* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13\n* Fix warnings, when compiled with -Wshadow\n* Make hiredis compile in Cygwin on Windows, now CI-tested\n* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now\n  protocol errors. This is consistent with the RESP specification. On 32-bit\n  platforms, the upper bound is lowered to `SIZE_MAX`.\n\n* Remove backwards compatibility macro's\n\nThis removes the following old function aliases, use the new name now:\n\n| Old                         | New                    |\n| --------------------------- | ---------------------- |\n| redisReplyReaderCreate      | redisReaderCreate      |\n| redisReplyReaderCreate      | redisReaderCreate      |\n| redisReplyReaderFree        | redisReaderFree        |\n| redisReplyReaderFeed        | redisReaderFeed        |\n| redisReplyReaderGetReply    | redisReaderGetReply    |\n| redisReplyReaderSetPrivdata | redisReaderSetPrivdata |\n| redisReplyReaderGetObject   | redisReaderGetObject   |\n| redisReplyReaderGetError    | redisReaderGetError    |\n\n* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS`\n\nPreviously it broke some builds for people that had `DEBUG` set to some arbitrary value,\ndue to debugging other software.\nBy renaming we avoid unintentional name clashes.\n\nSimply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again.\n\n### 0.13.3 (2015-09-16)\n\n* Revert \"Clear `REDIS_CONNECTED` flag when connection is closed\".\n* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni)\n\n\nIf the `REDIS_CONNECTED` flag is cleared,\nthe async onDisconnect callback function will never be called.\nThis causes problems as the disconnect is never reported back to the user.\n\n### 0.13.2 (2015-08-25)\n\n* Prevent crash on pending replies in async code (Thanks, @switch-st)\n* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs)\n* Add MacOS X addapter (Thanks, @dizzus)\n* Add Qt adapter (Thanks, Pietro Cerutti)\n* Add Ivykis adapter (Thanks, Gergely Nagy)\n\nAll adapters are provided as is and are only tested where possible.\n\n### 0.13.1 (2015-05-03)\n\nThis is a bug fix release.\nThe new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code.\nAnother commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects.\nOther non-C99 code can now use hiredis as usual again.\nSorry for the inconvenience.\n\n* Fix memory leak in async reply handling (Salvatore Sanfilippo)\n* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa)\n\n### 0.13.0 (2015-04-16)\n\nThis release adds a minimal Windows compatibility layer.\nThe parser, standalone since v0.12.0, can now be compiled on Windows\n(and thus used in other client libraries as well)\n\n* Windows compatibility layer for parser code (tzickel)\n* Properly escape data printed to PKGCONF file (Dan Skorupski)\n* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff)\n* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra)\n\n### 0.12.1 (2015-01-26)\n\n* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location\n* Fix `make test` as 32 bit build on 64 bit platform\n\n### 0.12.0 (2015-01-22)\n\n* Add optional KeepAlive support\n\n* Try again on EINTR errors\n\n* Add libuv adapter\n\n* Add IPv6 support\n\n* Remove possibility of multiple close on same fd\n\n* Add ability to bind source address on connect\n\n* Add redisConnectFd() and redisFreeKeepFd()\n\n* Fix getaddrinfo() memory leak\n\n* Free string if it is unused (fixes memory leak)\n\n* Improve redisAppendCommandArgv performance 2.5x\n\n* Add support for SO_REUSEADDR\n\n* Fix redisvFormatCommand format parsing\n\n* Add GLib 2.0 adapter\n\n* Refactor reading code into read.c\n\n* Fix errno error buffers to not clobber errors\n\n* Generate pkgconf during build\n\n* Silence _BSD_SOURCE warnings\n\n* Improve digit counting for multibulk creation\n\n\n### 0.11.0\n\n* Increase the maximum multi-bulk reply depth to 7.\n\n* Increase the read buffer size from 2k to 16k.\n\n* Use poll(2) instead of select(2) to support large fds (>= 1024).\n\n### 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": "Chapter07/redis-6.0.3/deps/hiredis/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)\nINCLUDE(GNUInstallDirs)\nPROJECT(hiredis)\n\nOPTION(ENABLE_SSL \"Build hiredis_ssl for SSL support\" OFF)\n\nMACRO(getVersionBit name)\n  SET(VERSION_REGEX \"^#define ${name} (.+)$\")\n  FILE(STRINGS \"${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h\"\n    VERSION_BIT REGEX ${VERSION_REGEX})\n  STRING(REGEX REPLACE ${VERSION_REGEX} \"\\\\1\" ${name} \"${VERSION_BIT}\")\nENDMACRO(getVersionBit)\n\ngetVersionBit(HIREDIS_MAJOR)\ngetVersionBit(HIREDIS_MINOR)\ngetVersionBit(HIREDIS_PATCH)\ngetVersionBit(HIREDIS_SONAME)\nSET(VERSION \"${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}\")\nMESSAGE(\"Detected version: ${VERSION}\")\n\nPROJECT(hiredis VERSION \"${VERSION}\")\n\nSET(ENABLE_EXAMPLES OFF CACHE BOOL \"Enable building hiredis examples\")\n\nADD_LIBRARY(hiredis SHARED\n    async.c\n    dict.c\n    hiredis.c\n    net.c\n    read.c\n    sds.c\n    sockcompat.c)\n\nSET_TARGET_PROPERTIES(hiredis\n    PROPERTIES\n    VERSION \"${HIREDIS_SONAME}\")\nIF(WIN32 OR MINGW)\n    TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)\nENDIF()\nTARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)\n\nCONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)\n\nINSTALL(TARGETS hiredis\n    DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n\nINSTALL(FILES hiredis.h read.h sds.h async.h\n    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)\n    \nINSTALL(DIRECTORY adapters\n    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)\n    \nINSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc\n    DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)\n\nIF(ENABLE_SSL)\n    IF (NOT OPENSSL_ROOT_DIR)\n        IF (APPLE)\n            SET(OPENSSL_ROOT_DIR \"/usr/local/opt/openssl\")\n        ENDIF()\n    ENDIF()\n    FIND_PACKAGE(OpenSSL REQUIRED)\n    ADD_LIBRARY(hiredis_ssl SHARED\n        ssl.c)\n    TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE \"${OPENSSL_INCLUDE_DIR}\")\n    TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})\n    CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)\n\n    INSTALL(TARGETS hiredis_ssl\n        DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n\n    INSTALL(FILES hiredis_ssl.h\n        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)\n    \n    INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc\n        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)\nENDIF()\n\nIF(NOT (WIN32 OR MINGW))\n    ENABLE_TESTING()\n    ADD_EXECUTABLE(hiredis-test test.c)\n    TARGET_LINK_LIBRARIES(hiredis-test hiredis)\n    ADD_TEST(NAME hiredis-test\n        COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)\nENDIF()\n\n# Add examples\nIF(ENABLE_EXAMPLES)\n  ADD_SUBDIRECTORY(examples)\nENDIF(ENABLE_EXAMPLES)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 read.o sockcompat.o\nSSL_OBJ=ssl.o\nEXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib\nifeq ($(USE_SSL),1)\nEXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl\nendif\nTESTS=hiredis-test\nLIBNAME=libhiredis\nSSL_LIBNAME=libhiredis_ssl\nPKGCONFNAME=hiredis.pc\nSSL_PKGCONFNAME=hiredis_ssl.pc\n\nHIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')\nHIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')\nHIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}')\nHIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}')\n\n# Installation related variables and target\nPREFIX?=/usr/local\nINCLUDE_PATH?=include/hiredis\nLIBRARY_PATH?=lib\nPKGCONF_PATH?=pkgconfig\nINSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH)\nINSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)\nINSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH)\n\n# redis-server configuration used for testing\nREDIS_PORT=56379\nREDIS_SERVER=redis-server\ndefine REDIS_TEST_CONFIG\n\tdaemonize yes\n\tpidfile /tmp/hiredis-test-redis.pid\n\tport $(REDIS_PORT)\n\tbind 127.0.0.1\n\tunixsocket /tmp/hiredis-test-redis.sock\nendef\nexport REDIS_TEST_CONFIG\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')\nCXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')\nOPTIMIZATION?=-O3\nWARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers\nDEBUG_FLAGS?= -g -ggdb\nREAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)\nREAL_LDFLAGS=$(LDFLAGS)\n\nDYLIBSUFFIX=so\nSTLIBSUFFIX=a\nDYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)\nDYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)\nDYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)\nSSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)\nDYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)\nSTLIBNAME=$(LIBNAME).$(STLIBSUFFIX)\nSSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)\nSTLIB_MAKE_CMD=$(AR) rcs\n\n# Platform-specific overrides\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\nUSE_SSL?=0\n\n# This is required for test.c only\nifeq ($(USE_SSL),1)\n  CFLAGS+=-DHIREDIS_TEST_SSL\nendif\n\nifeq ($(uname_S),Linux)\n  SSL_LDFLAGS=-lssl -lcrypto\nelse\n  OPENSSL_PREFIX?=/usr/local/opt/openssl\n  CFLAGS+=-I$(OPENSSL_PREFIX)/include\n  SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto\nendif\n\nifeq ($(uname_S),SunOS)\n  REAL_LDFLAGS+= -ldl -lnsl -lsocket\n  DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)\nendif\nifeq ($(uname_S),Darwin)\n  DYLIBSUFFIX=dylib\n  DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)\n  DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)\nendif\n\nall: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)\nifeq ($(USE_SSL),1)\nall: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)\nendif\n\n# Deps (use make dep to generate this)\nasync.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h\ndict.o: dict.c fmacros.h dict.h\nhiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h win32.h\nnet.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h\nread.o: read.c fmacros.h read.h sds.h\nsds.o: sds.c sds.h\nsockcompat.o: sockcompat.c sockcompat.h\nssl.o: ssl.c hiredis.h\ntest.o: test.c fmacros.h hiredis.h read.h sds.h\n\n$(DYLIBNAME): $(OBJ)\n\t$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)\n\n$(STLIBNAME): $(OBJ)\n\t$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)\n\n$(SSL_DYLIBNAME): $(SSL_OBJ)\n\t$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)\n\n$(SSL_STLIBNAME): $(SSL_OBJ)\n\t$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)\n\ndynamic: $(DYLIBNAME)\nstatic: $(STLIBNAME)\nifeq ($(USE_SSL),1)\ndynamic: $(SSL_DYLIBNAME)\nstatic: $(SSL_STLIBNAME)\nendif\n\n# Binaries:\nhiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)\n\nhiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS)\n\nhiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)\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: examples/example-ae.c adapters/ae.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)\nendif\n\nifndef LIBUV_DIR\nhiredis-example-libuv:\n\t@echo \"Please specify LIBUV_DIR (e.g. ../libuv/)\"\n\t@false\nelse\nhiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS)\nendif\n\nifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),)\nhiredis-example-qt:\n\t@echo \"Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR\"\n\t@false\nelse\nhiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME)\n\t$(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \\\n\t    $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore\n\t$(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \\\n\t    $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore\n\t$(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore\nendif\n\nhiredis-example: examples/example.c $(STLIBNAME)\n\t$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)\n\nexamples: $(EXAMPLES)\n\nTEST_LIBS = $(STLIBNAME)\nifeq ($(USE_SSL),1)\n    TEST_LIBS += $(SSL_STLIBNAME) -lssl -lcrypto -lpthread\nendif\nhiredis-test: test.o $(TEST_LIBS)\n\nhiredis-%: %.o $(STLIBNAME)\n\t$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)\n\ntest: hiredis-test\n\t./hiredis-test\n\ncheck: hiredis-test\n\tTEST_SSL=$(USE_SSL) ./test.sh\n\n.c.o:\n\t$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<\n\nclean:\n\trm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov\n\ndep:\n\t$(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c\n\nINSTALL?= cp -pPR\n\n$(PKGCONFNAME): hiredis.h\n\t@echo \"Generating $@ for pkgconfig...\"\n\t@echo prefix=$(PREFIX) > $@\n\t@echo exec_prefix=\\$${prefix} >> $@\n\t@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@\n\t@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@\n\t@echo >> $@\n\t@echo Name: hiredis >> $@\n\t@echo Description: Minimalistic C client library for Redis. >> $@\n\t@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@\n\t@echo Libs: -L\\$${libdir} -lhiredis >> $@\n\t@echo Cflags: -I\\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@\n\n$(SSL_PKGCONFNAME): hiredis.h\n\t@echo \"Generating $@ for pkgconfig...\"\n\t@echo prefix=$(PREFIX) > $@\n\t@echo exec_prefix=\\$${prefix} >> $@\n\t@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@\n\t@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@\n\t@echo >> $@\n\t@echo Name: hiredis_ssl >> $@\n\t@echo Description: SSL Support for hiredis. >> $@\n\t@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@\n\t@echo Requires: hiredis >> $@\n\t@echo Libs: -L\\$${libdir} -lhiredis_ssl >> $@\n\t@echo Libs.private: -lssl -lcrypto >> $@\n\ninstall: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)\n\tmkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)\n\t$(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)\n\t$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters\n\t$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)\n\tcd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)\n\t$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)\n\tmkdir -p $(INSTALL_PKGCONF_PATH)\n\t$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_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\n32bit-vars:\n\t$(eval CFLAGS=-m32)\n\t$(eval 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 32bit-vars gprof gcov noopt\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/README.md",
    "content": "[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)\n\n**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**\n\n# 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 a 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 to `1.0.0`\n\nVersion 1.0.0 marks a stable release of hiredis.\nIt includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.\nIt also bundles the updated `sds` library, to sync up with upstream and Redis.\nFor most applications a recompile against the new hiredis should be enough.\nFor code changes see the [Changelog](CHANGELOG.md).\n\n## Upgrading from `<0.9.0`\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```c\nredisContext *redisConnect(const char *ip, int port);\nvoid *redisCommand(redisContext *c, const char *format, ...);\nvoid freeReplyObject(void *reply);\n```\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 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```c\nredisContext *c = redisConnect(\"127.0.0.1\", 6379);\nif (c == NULL || c->err) {\n    if (c) {\n        printf(\"Error: %s\\n\", c->errstr);\n        // handle error\n    } else {\n        printf(\"Can't allocate redis context\\n\");\n    }\n}\n```\n\n*Note: A `redisContext` is not thread-safe.*\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```c\nreply = redisCommand(context, \"SET foo bar\");\n```\n\nThe specifier `%s` interpolates a string in the command, and uses `strlen` to\ndetermine the length of the string:\n```c\nreply = 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```c\nreply = redisCommand(context, \"SET foo %b\", value, (size_t) 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```c\nreply = redisCommand(context, \"SET key:%s %s\", myid, value);\n```\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-reply 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) frees 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```c\nvoid redisFree(redisContext *c);\n```\nThis function immediately closes the socket and then frees 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```c\nvoid *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```c\nvoid redisAppendCommand(redisContext *c, const char *format, ...);\nvoid 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```c\nredisReply *reply;\nredisAppendCommand(context,\"SET foo bar\");\nredisAppendCommand(context,\"GET foo\");\nredisGetReply(context,&reply); // reply for SET\nfreeReplyObject(reply);\nredisGetReply(context,&reply); // reply for GET\nfreeReplyObject(reply);\n```\nThis API can also be used to implement a blocking subscriber:\n```c\nreply = redisCommand(context,\"SUBSCRIBE foo\");\nfreeReplyObject(reply);\nwhile(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*Note: A `redisAsyncContext` is not thread-safe.*\n\n```c\nredisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\nif (c->err) {\n    printf(\"Error: %s\\n\", c->errstr);\n    // handle error\n}\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```c\nvoid(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 freed 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```c\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\n```\n`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.\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```c\nvoid(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```c\nint redisAsyncCommand(\n  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n  const char *format, ...);\nint 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 freed. When the callback\nfor a command is non-`NULL`, the memory is freed 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```c\nvoid 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 freed.\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```c\nredisReader *redisReaderCreate(void);\nvoid redisReaderFree(redisReader *reader);\nint redisReaderFeed(redisReader *reader, const char *buf, size_t len);\nint 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\nThe parser limits the level of nesting for multi bulk payloads to 7. If the\nmulti bulk nesting level is higher than this, the parser returns an 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\nKiB 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```c\ncontext->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.\nHiredis is currently maintained by Matt Stancliff (matt at genges dot com) and\nJan-Erik Rediger (janerik at fnordig dot com)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/hiredis/adapters/glib.h",
    "content": "#ifndef __HIREDIS_GLIB_H__\n#define __HIREDIS_GLIB_H__\n\n#include <glib.h>\n\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct\n{\n    GSource source;\n    redisAsyncContext *ac;\n    GPollFD poll_fd;\n} RedisSource;\n\nstatic void\nredis_source_add_read (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events |= G_IO_IN;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_del_read (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events &= ~G_IO_IN;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_add_write (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events |= G_IO_OUT;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_del_write (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n    g_return_if_fail(source);\n    source->poll_fd.events &= ~G_IO_OUT;\n    g_main_context_wakeup(g_source_get_context((GSource *)data));\n}\n\nstatic void\nredis_source_cleanup (gpointer data)\n{\n    RedisSource *source = (RedisSource *)data;\n\n    g_return_if_fail(source);\n\n    redis_source_del_read(source);\n    redis_source_del_write(source);\n    /*\n     * It is not our responsibility to remove ourself from the\n     * current main loop. However, we will remove the GPollFD.\n     */\n    if (source->poll_fd.fd >= 0) {\n        g_source_remove_poll((GSource *)data, &source->poll_fd);\n        source->poll_fd.fd = -1;\n    }\n}\n\nstatic gboolean\nredis_source_prepare (GSource *source,\n                      gint    *timeout_)\n{\n    RedisSource *redis = (RedisSource *)source;\n    *timeout_ = -1;\n    return !!(redis->poll_fd.events & redis->poll_fd.revents);\n}\n\nstatic gboolean\nredis_source_check (GSource *source)\n{\n    RedisSource *redis = (RedisSource *)source;\n    return !!(redis->poll_fd.events & redis->poll_fd.revents);\n}\n\nstatic gboolean\nredis_source_dispatch (GSource      *source,\n                       GSourceFunc   callback,\n                       gpointer      user_data)\n{\n    RedisSource *redis = (RedisSource *)source;\n\n    if ((redis->poll_fd.revents & G_IO_OUT)) {\n        redisAsyncHandleWrite(redis->ac);\n        redis->poll_fd.revents &= ~G_IO_OUT;\n    }\n\n    if ((redis->poll_fd.revents & G_IO_IN)) {\n        redisAsyncHandleRead(redis->ac);\n        redis->poll_fd.revents &= ~G_IO_IN;\n    }\n\n    if (callback) {\n        return callback(user_data);\n    }\n\n    return TRUE;\n}\n\nstatic void\nredis_source_finalize (GSource *source)\n{\n    RedisSource *redis = (RedisSource *)source;\n\n    if (redis->poll_fd.fd >= 0) {\n        g_source_remove_poll(source, &redis->poll_fd);\n        redis->poll_fd.fd = -1;\n    }\n}\n\nstatic GSource *\nredis_source_new (redisAsyncContext *ac)\n{\n    static GSourceFuncs source_funcs = {\n        .prepare  = redis_source_prepare,\n        .check     = redis_source_check,\n        .dispatch = redis_source_dispatch,\n        .finalize = redis_source_finalize,\n    };\n    redisContext *c = &ac->c;\n    RedisSource *source;\n\n    g_return_val_if_fail(ac != NULL, NULL);\n\n    source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);\n    source->ac = ac;\n    source->poll_fd.fd = c->fd;\n    source->poll_fd.events = 0;\n    source->poll_fd.revents = 0;\n    g_source_add_poll((GSource *)source, &source->poll_fd);\n\n    ac->ev.addRead = redis_source_add_read;\n    ac->ev.delRead = redis_source_del_read;\n    ac->ev.addWrite = redis_source_add_write;\n    ac->ev.delWrite = redis_source_del_write;\n    ac->ev.cleanup = redis_source_cleanup;\n    ac->ev.data = source;\n\n    return (GSource *)source;\n}\n\n#endif /* __HIREDIS_GLIB_H__ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/adapters/ivykis.h",
    "content": "#ifndef __HIREDIS_IVYKIS_H__\n#define __HIREDIS_IVYKIS_H__\n#include <iv.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct redisIvykisEvents {\n    redisAsyncContext *context;\n    struct iv_fd fd;\n} redisIvykisEvents;\n\nstatic void redisIvykisReadEvent(void *arg) {\n    redisAsyncContext *context = (redisAsyncContext *)arg;\n    redisAsyncHandleRead(context);\n}\n\nstatic void redisIvykisWriteEvent(void *arg) {\n    redisAsyncContext *context = (redisAsyncContext *)arg;\n    redisAsyncHandleWrite(context);\n}\n\nstatic void redisIvykisAddRead(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);\n}\n\nstatic void redisIvykisDelRead(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_in(&e->fd, NULL);\n}\n\nstatic void redisIvykisAddWrite(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);\n}\n\nstatic void redisIvykisDelWrite(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n    iv_fd_set_handler_out(&e->fd, NULL);\n}\n\nstatic void redisIvykisCleanup(void *privdata) {\n    redisIvykisEvents *e = (redisIvykisEvents*)privdata;\n\n    iv_fd_unregister(&e->fd);\n    free(e);\n}\n\nstatic int redisIvykisAttach(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisIvykisEvents *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 = (redisIvykisEvents*)malloc(sizeof(*e));\n    e->context = ac;\n\n    /* Register functions to start/stop listening for events */\n    ac->ev.addRead = redisIvykisAddRead;\n    ac->ev.delRead = redisIvykisDelRead;\n    ac->ev.addWrite = redisIvykisAddWrite;\n    ac->ev.delWrite = redisIvykisDelWrite;\n    ac->ev.cleanup = redisIvykisCleanup;\n    ac->ev.data = e;\n\n    /* Initialize and install read/write events */\n    IV_FD_INIT(&e->fd);\n    e->fd.fd = c->fd;\n    e->fd.handler_in = redisIvykisReadEvent;\n    e->fd.handler_out = redisIvykisWriteEvent;\n    e->fd.handler_err = NULL;\n    e->fd.cookie = e->context;\n\n    iv_fd_register(&e->fd);\n\n    return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 <event2/event.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\n#define REDIS_LIBEVENT_DELETED 0x01\n#define REDIS_LIBEVENT_ENTERED 0x02\n\ntypedef struct redisLibeventEvents {\n    redisAsyncContext *context;\n    struct event *ev;\n    struct event_base *base;\n    struct timeval tv;\n    short flags;\n    short state;\n} redisLibeventEvents;\n\nstatic void redisLibeventDestroy(redisLibeventEvents *e) {\n    free(e);\n}\n\nstatic void redisLibeventHandler(int fd, short event, void *arg) {\n    ((void)fd);\n    redisLibeventEvents *e = (redisLibeventEvents*)arg;\n    e->state |= REDIS_LIBEVENT_ENTERED;\n\n    #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\\\n        redisLibeventDestroy(e);\\\n        return; \\\n    }\n\n    if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {\n        redisAsyncHandleTimeout(e->context);\n        CHECK_DELETED();\n    }\n\n    if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {\n        redisAsyncHandleRead(e->context);\n        CHECK_DELETED();\n    }\n\n    if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {\n        redisAsyncHandleWrite(e->context);\n        CHECK_DELETED();\n    }\n\n    e->state &= ~REDIS_LIBEVENT_ENTERED;\n    #undef CHECK_DELETED\n}\n\nstatic void redisLibeventUpdate(void *privdata, short flag, int isRemove) {\n    redisLibeventEvents *e = (redisLibeventEvents *)privdata;\n    const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;\n\n    if (isRemove) {\n        if ((e->flags & flag) == 0) {\n            return;\n        } else {\n            e->flags &= ~flag;\n        }\n    } else {\n        if (e->flags & flag) {\n            return;\n        } else {\n            e->flags |= flag;\n        }\n    }\n\n    event_del(e->ev);\n    event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,\n                 redisLibeventHandler, privdata);\n    event_add(e->ev, tv);\n}\n\nstatic void redisLibeventAddRead(void *privdata) {\n    redisLibeventUpdate(privdata, EV_READ, 0);\n}\n\nstatic void redisLibeventDelRead(void *privdata) {\n    redisLibeventUpdate(privdata, EV_READ, 1);\n}\n\nstatic void redisLibeventAddWrite(void *privdata) {\n    redisLibeventUpdate(privdata, EV_WRITE, 0);\n}\n\nstatic void redisLibeventDelWrite(void *privdata) {\n    redisLibeventUpdate(privdata, EV_WRITE, 1);\n}\n\nstatic void redisLibeventCleanup(void *privdata) {\n    redisLibeventEvents *e = (redisLibeventEvents*)privdata;\n    if (!e) {\n        return;\n    }\n    event_del(e->ev);\n    event_free(e->ev);\n    e->ev = NULL;\n\n    if (e->state & REDIS_LIBEVENT_ENTERED) {\n        e->state |= REDIS_LIBEVENT_DELETED;\n    } else {\n        redisLibeventDestroy(e);\n    }\n}\n\nstatic void redisLibeventSetTimeout(void *privdata, struct timeval tv) {\n    redisLibeventEvents *e = (redisLibeventEvents *)privdata;\n    short flags = e->flags;\n    e->flags = 0;\n    e->tv = tv;\n    redisLibeventUpdate(e, flags, 0);\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*)calloc(1, 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.scheduleTimer = redisLibeventSetTimeout;\n    ac->ev.data = e;\n\n    /* Initialize and install read/write events */\n    e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);\n    e->base = base;\n    return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/adapters/libuv.h",
    "content": "#ifndef __HIREDIS_LIBUV_H__\n#define __HIREDIS_LIBUV_H__\n#include <stdlib.h>\n#include <uv.h>\n#include \"../hiredis.h\"\n#include \"../async.h\"\n#include <string.h>\n\ntypedef struct redisLibuvEvents {\n  redisAsyncContext* context;\n  uv_poll_t          handle;\n  int                events;\n} redisLibuvEvents;\n\n\nstatic void redisLibuvPoll(uv_poll_t* handle, int status, int events) {\n  redisLibuvEvents* p = (redisLibuvEvents*)handle->data;\n  int ev = (status ? p->events : events);\n\n  if (p->context != NULL && (ev & UV_READABLE)) {\n    redisAsyncHandleRead(p->context);\n  }\n  if (p->context != NULL && (ev & UV_WRITABLE)) {\n    redisAsyncHandleWrite(p->context);\n  }\n}\n\n\nstatic void redisLibuvAddRead(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events |= UV_READABLE;\n\n  uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n}\n\n\nstatic void redisLibuvDelRead(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events &= ~UV_READABLE;\n\n  if (p->events) {\n    uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n  } else {\n    uv_poll_stop(&p->handle);\n  }\n}\n\n\nstatic void redisLibuvAddWrite(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events |= UV_WRITABLE;\n\n  uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n}\n\n\nstatic void redisLibuvDelWrite(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->events &= ~UV_WRITABLE;\n\n  if (p->events) {\n    uv_poll_start(&p->handle, p->events, redisLibuvPoll);\n  } else {\n    uv_poll_stop(&p->handle);\n  }\n}\n\n\nstatic void on_close(uv_handle_t* handle) {\n  redisLibuvEvents* p = (redisLibuvEvents*)handle->data;\n\n  free(p);\n}\n\n\nstatic void redisLibuvCleanup(void *privdata) {\n  redisLibuvEvents* p = (redisLibuvEvents*)privdata;\n\n  p->context = NULL; // indicate that context might no longer exist\n  uv_close((uv_handle_t*)&p->handle, on_close);\n}\n\n\nstatic int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {\n  redisContext *c = &(ac->c);\n\n  if (ac->ev.data != NULL) {\n    return REDIS_ERR;\n  }\n\n  ac->ev.addRead  = redisLibuvAddRead;\n  ac->ev.delRead  = redisLibuvDelRead;\n  ac->ev.addWrite = redisLibuvAddWrite;\n  ac->ev.delWrite = redisLibuvDelWrite;\n  ac->ev.cleanup  = redisLibuvCleanup;\n\n  redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));\n\n  if (!p) {\n    return REDIS_ERR;\n  }\n\n  memset(p, 0, sizeof(*p));\n\n  if (uv_poll_init(loop, &p->handle, c->fd) != 0) {\n    return REDIS_ERR;\n  }\n\n  ac->ev.data    = p;\n  p->handle.data = p;\n  p->context     = ac;\n\n  return REDIS_OK;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/adapters/macosx.h",
    "content": "//\n//  Created by Дмитрий Бахвалов on 13.07.15.\n//  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.\n//\n\n#ifndef __HIREDIS_MACOSX_H__\n#define __HIREDIS_MACOSX_H__\n\n#include <CoreFoundation/CoreFoundation.h>\n\n#include \"../hiredis.h\"\n#include \"../async.h\"\n\ntypedef struct {\n    redisAsyncContext *context;\n    CFSocketRef socketRef;\n    CFRunLoopSourceRef sourceRef;\n} RedisRunLoop;\n\nstatic int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {\n    if( redisRunLoop != NULL ) {\n        if( redisRunLoop->sourceRef != NULL ) {\n            CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);\n            CFRelease(redisRunLoop->sourceRef);\n        }\n        if( redisRunLoop->socketRef != NULL ) {\n            CFSocketInvalidate(redisRunLoop->socketRef);\n            CFRelease(redisRunLoop->socketRef);\n        }\n        free(redisRunLoop);\n    }\n    return REDIS_ERR;\n}\n\nstatic void redisMacOSAddRead(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);\n}\n\nstatic void redisMacOSDelRead(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);\n}\n\nstatic void redisMacOSAddWrite(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);\n}\n\nstatic void redisMacOSDelWrite(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);\n}\n\nstatic void redisMacOSCleanup(void *privdata) {\n    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;\n    freeRedisRunLoop(redisRunLoop);\n}\n\nstatic void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {\n    redisAsyncContext* context = (redisAsyncContext*) info;\n\n    switch (callbackType) {\n        case kCFSocketReadCallBack:\n            redisAsyncHandleRead(context);\n            break;\n\n        case kCFSocketWriteCallBack:\n            redisAsyncHandleWrite(context);\n            break;\n\n        default:\n            break;\n    }\n}\n\nstatic int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {\n    redisContext *redisCtx = &(redisAsyncCtx->c);\n\n    /* Nothing should be attached when something is already attached */\n    if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;\n\n    RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));\n    if( !redisRunLoop ) return REDIS_ERR;\n\n    /* Setup redis stuff */\n    redisRunLoop->context = redisAsyncCtx;\n\n    redisAsyncCtx->ev.addRead  = redisMacOSAddRead;\n    redisAsyncCtx->ev.delRead  = redisMacOSDelRead;\n    redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;\n    redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;\n    redisAsyncCtx->ev.cleanup  = redisMacOSCleanup;\n    redisAsyncCtx->ev.data     = redisRunLoop;\n\n    /* Initialize and install read/write events */\n    CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };\n\n    redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,\n                                                       kCFSocketReadCallBack | kCFSocketWriteCallBack,\n                                                       redisMacOSAsyncCallback,\n                                                       &socketCtx);\n    if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);\n\n    redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);\n    if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);\n\n    CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);\n\n    return REDIS_OK;\n}\n\n#endif\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/adapters/qt.h",
    "content": "/*-\n * Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>\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 *\n * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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#ifndef __HIREDIS_QT_H__\n#define __HIREDIS_QT_H__\n#include <QSocketNotifier>\n#include \"../async.h\"\n\nstatic void RedisQtAddRead(void *);\nstatic void RedisQtDelRead(void *);\nstatic void RedisQtAddWrite(void *);\nstatic void RedisQtDelWrite(void *);\nstatic void RedisQtCleanup(void *);\n\nclass RedisQtAdapter : public QObject {\n\n    Q_OBJECT\n\n    friend\n    void RedisQtAddRead(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->addRead();\n    }\n\n    friend\n    void RedisQtDelRead(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->delRead();\n    }\n\n    friend\n    void RedisQtAddWrite(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->addWrite();\n    }\n\n    friend\n    void RedisQtDelWrite(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->delWrite();\n    }\n\n    friend\n    void RedisQtCleanup(void * adapter) {\n        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);\n        a->cleanup();\n    }\n\n    public:\n        RedisQtAdapter(QObject * parent = 0)\n            : QObject(parent), m_ctx(0), m_read(0), m_write(0) { }\n\n        ~RedisQtAdapter() {\n            if (m_ctx != 0) {\n                m_ctx->ev.data = NULL;\n            }\n        }\n\n        int setContext(redisAsyncContext * ac) {\n            if (ac->ev.data != NULL) {\n                return REDIS_ERR;\n            }\n            m_ctx = ac;\n            m_ctx->ev.data = this;\n            m_ctx->ev.addRead = RedisQtAddRead;\n            m_ctx->ev.delRead = RedisQtDelRead;\n            m_ctx->ev.addWrite = RedisQtAddWrite;\n            m_ctx->ev.delWrite = RedisQtDelWrite;\n            m_ctx->ev.cleanup = RedisQtCleanup;\n            return REDIS_OK;\n        }\n\n    private:\n        void addRead() {\n            if (m_read) return;\n            m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);\n            connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));\n        }\n\n        void delRead() {\n            if (!m_read) return;\n            delete m_read;\n            m_read = 0;\n        }\n\n        void addWrite() {\n            if (m_write) return;\n            m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);\n            connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));\n        }\n\n        void delWrite() {\n            if (!m_write) return;\n            delete m_write;\n            m_write = 0;\n        }\n\n        void cleanup() {\n            delRead();\n            delWrite();\n        }\n\n    private slots:\n        void read() { redisAsyncHandleRead(m_ctx); }\n        void write() { redisAsyncHandleWrite(m_ctx); }\n\n    private:\n        redisAsyncContext * m_ctx;\n        QSocketNotifier * m_read;\n        QSocketNotifier * m_write;\n};\n\n#endif /* !__HIREDIS_QT_H__ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/appveyor.yml",
    "content": "# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)\nenvironment:\n    matrix:\n        - CYG_BASH: C:\\cygwin64\\bin\\bash\n          CC: gcc\n        - CYG_BASH: C:\\cygwin\\bin\\bash\n          CC: gcc\n          CFLAGS: -m32\n          CXXFLAGS: -m32\n          LDFLAGS: -m32\n\nclone_depth: 1\n\n# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail\ninit:\n    - git config --global core.autocrlf input\n\n# Install needed build dependencies\ninstall:\n    - '%CYG_BASH% -lc \"cygcheck -dc cygwin\"'\n\nbuild_script:\n    - 'echo building...'\n    - '%CYG_BASH% -lc \"cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; mkdir build && cd build && cmake .. -G \\\"Unix Makefiles\\\" && make VERBOSE=1\"'\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#ifndef _MSC_VER\n#include <strings.h>\n#endif\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#include \"win32.h\"\n\n#include \"async_private.h\"\n\n/* Forward declaration of function in hiredis.c */\nint __redisAppendCommand(redisContext *c, const 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((const unsigned char *)key,\n                               sdslen((const sds)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((const sds)key1);\n    l2 = sdslen((const 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;\n\n    ac = realloc(c,sizeof(redisAsyncContext));\n    if (ac == NULL)\n        return NULL;\n\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    ac->ev.scheduleTimer = 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    if (!ac)\n        return;\n\n    redisContext *c = &(ac->c);\n    ac->err = c->err;\n    ac->errstr = c->errstr;\n}\n\nredisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {\n    redisOptions myOptions = *options;\n    redisContext *c;\n    redisAsyncContext *ac;\n\n    myOptions.options |= REDIS_OPT_NONBLOCK;\n    c = redisConnectWithOptions(&myOptions);\n    if (c == NULL) {\n        return NULL;\n    }\n    ac = redisAsyncInitialize(c);\n    if (ac == NULL) {\n        redisFree(c);\n        return NULL;\n    }\n    __redisAsyncCopyError(ac);\n    return ac;\n}\n\nredisAsyncContext *redisAsyncConnect(const char *ip, int port) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    return redisAsyncConnectWithOptions(&options);\n}\n\nredisAsyncContext *redisAsyncConnectBind(const char *ip, int port,\n                                         const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    return redisAsyncConnectWithOptions(&options);\n}\n\nredisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,\n                                                  const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.options |= REDIS_OPT_REUSEADDR;\n    options.endpoint.tcp.source_addr = source_addr;\n    return redisAsyncConnectWithOptions(&options);\n}\n\nredisAsyncContext *redisAsyncConnectUnix(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    return redisAsyncConnectWithOptions(&options);\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 (cb == NULL)\n        return REDIS_ERR_OOM;\n\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. */\nvoid __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        int ret = __redisShiftCallback(&ac->replies,NULL);\n        assert(ret == 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    /* cleanup event library on disconnect.\n     * this is safe to call multiple times */\n    _EL_CLEANUP(ac);\n\n    /* For non-clean disconnects, __redisAsyncFree() will execute pending\n     * callbacks with a NULL-reply. */\n    if (!(c->flags & REDIS_NO_AUTO_FREE)) {\n      __redisAsyncFree(ac);\n    }\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\n    /** unset the auto-free flag here, because disconnect undoes this */\n    c->flags &= ~REDIS_NO_AUTO_FREE;\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    redisCallback *cb;\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            cb = dictGetEntryVal(de);\n\n            /* If this is an subscribe reply decrease pending counter. */\n            if (strcasecmp(stype+pvariant,\"subscribe\") == 0) {\n                cb->pending_subs -= 1;\n            }\n\n            memcpy(dstcb,cb,sizeof(*dstcb));\n\n            /* If this is an unsubscribe message, remove it. */\n            if (strcasecmp(stype+pvariant,\"unsubscribe\") == 0) {\n                if (cb->pending_subs == 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\n                /* Unset subscribed flag only when no pipelined pending subscribe. */\n                if (reply->element[2]->integer == 0\n                    && dictSize(ac->sub.channels) == 0\n                    && dictSize(ac->sub.patterns) == 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 = {NULL, NULL, 0, NULL};\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                && ac->replies.head == NULL) {\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                c->reader->fn->freeObject(reply);\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 successful, the connect callback\n * is called with a REDIS_ERR status and the context is free'd. */\nstatic int __redisAsyncHandleConnect(redisAsyncContext *ac) {\n    int completed = 0;\n    redisContext *c = &(ac->c);\n    if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {\n        /* Error! */\n        redisCheckSocketError(c);\n        if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);\n        __redisAsyncDisconnect(ac);\n        return REDIS_ERR;\n    } else if (completed == 1) {\n        /* connected! */\n        if (ac->onConnect) ac->onConnect(ac, REDIS_OK);\n        c->flags |= REDIS_CONNECTED;\n        return REDIS_OK;\n    } else {\n        return REDIS_OK;\n    }\n}\n\nvoid redisAsyncRead(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\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\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    c->funcs->async_read(ac);\n}\n\nvoid redisAsyncWrite(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    int done = 0;\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\nvoid redisAsyncHandleWrite(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    c->funcs->async_write(ac);\n}\n\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\nvoid redisAsyncHandleTimeout(redisAsyncContext *ac) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n\n    if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {\n        /* Nothing to do - just an idle timeout */\n        return;\n    }\n\n    if (!c->err) {\n        __redisSetError(c, REDIS_ERR_TIMEOUT, \"Timeout\");\n    }\n\n    if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {\n        ac->onConnect(ac, REDIS_ERR);\n    }\n\n    while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {\n        __redisRunCallback(ac, &cb, NULL);\n    }\n\n    /**\n     * TODO: Don't automatically sever the connection,\n     * rather, allow to ignore <x> responses before the queue is clear\n     */\n    __redisAsyncDisconnect(ac);\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 const char *nextArgument(const char *start, const char **str, size_t *len) {\n    const 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, const char *cmd, size_t len) {\n    redisContext *c = &(ac->c);\n    redisCallback cb;\n    struct dict *cbdict;\n    dictEntry *de;\n    redisCallback *existcb;\n    int pvariant, hasnext;\n    const char *cstr, *astr;\n    size_t clen, alen;\n    const char *p;\n    sds sname;\n    int ret;\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    cb.pending_subs = 1;\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                cbdict = ac->sub.patterns;\n            else\n                cbdict = ac->sub.channels;\n\n            de = dictFind(cbdict,sname);\n\n            if (de != NULL) {\n                existcb = dictGetEntryVal(de);\n                cb.pending_subs = existcb->pending_subs + 1;\n            }\n\n            ret = dictReplace(cbdict,sname,&cb);\n\n            if (ret == 0) sdsfree(sname);\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\n    /* We don't want to pass -1 or -2 to future functions as a length. */\n    if (len < 0)\n        return REDIS_ERR;\n\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    sds cmd;\n    int len;\n    int status;\n    len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);\n    if (len < 0)\n        return REDIS_ERR;\n    status = __redisAsyncCommand(ac,fn,privdata,cmd,len);\n    sdsfree(cmd);\n    return status;\n}\n\nint redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {\n    int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);\n    return status;\n}\n\nvoid redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {\n    if (!ac->c.timeout) {\n        ac->c.timeout = calloc(1, sizeof(tv));\n    }\n\n    if (tv.tv_sec == ac->c.timeout->tv_sec &&\n        tv.tv_usec == ac->c.timeout->tv_usec) {\n        return;\n    }\n\n    *ac->c.timeout = tv;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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    int pending_subs;\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);\ntypedef void(redisTimerCallback)(void *timer, void *privdata);\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        void (*scheduleTimer)(void *privdata, struct timeval tv);\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    /* Address used for connect() */\n    struct sockaddr *saddr;\n    size_t addrlen;\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 *redisAsyncConnectWithOptions(const redisOptions *options);\nredisAsyncContext *redisAsyncConnect(const char *ip, int port);\nredisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);\nredisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,\n                                                  const char *source_addr);\nredisAsyncContext *redisAsyncConnectUnix(const char *path);\nint redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\n\nvoid redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);\nvoid redisAsyncDisconnect(redisAsyncContext *ac);\nvoid redisAsyncFree(redisAsyncContext *ac);\n\n/* Handle read/write events */\nvoid redisAsyncHandleRead(redisAsyncContext *ac);\nvoid redisAsyncHandleWrite(redisAsyncContext *ac);\nvoid redisAsyncHandleTimeout(redisAsyncContext *ac);\nvoid redisAsyncRead(redisAsyncContext *ac);\nvoid redisAsyncWrite(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);\nint redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/async_private.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_PRIVATE_H\n#define __HIREDIS_ASYNC_PRIVATE_H\n\n#define _EL_ADD_READ(ctx)                                         \\\n    do {                                                          \\\n        refreshTimeout(ctx);                                      \\\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)                                          \\\n    do {                                                            \\\n        refreshTimeout(ctx);                                        \\\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        ctx->ev.cleanup = NULL; \\\n    } while(0);\n\nstatic inline void refreshTimeout(redisAsyncContext *ctx) {\n    if (ctx->c.timeout && ctx->ev.scheduleTimer &&\n        (ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) {\n        ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);\n    // } else {\n    //     printf(\"Not scheduling timer.. (tmo=%p)\\n\", ctx->c.timeout);\n    //     if (ctx->c.timeout){\n    //         printf(\"tv_sec: %u. tv_usec: %u\\n\", ctx->c.timeout->tv_sec,\n    //                ctx->c.timeout->tv_usec);\n    //     }\n    }\n}\n\nvoid __redisAsyncDisconnect(redisAsyncContext *ac);\nvoid redisProcessCallbacks(redisAsyncContext *ac);\n\n#endif  /* __HIREDIS_ASYNC_PRIVATE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 succeed. */\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 initial 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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/hiredis/examples/CMakeLists.txt",
    "content": "INCLUDE(FindPkgConfig)\n# Check for GLib\n\nPKG_CHECK_MODULES(GLIB2 glib-2.0)\nif (GLIB2_FOUND)\n    INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS})\n    LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS})\n    ADD_EXECUTABLE(example-glib example-glib.c)\n    TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES})\nENDIF(GLIB2_FOUND)\n\nFIND_PATH(LIBEV ev.h\n    HINTS /usr/local /usr/opt/local\n    ENV LIBEV_INCLUDE_DIR)\n\nif (LIBEV)\n    # Just compile and link with libev\n    ADD_EXECUTABLE(example-libev example-libev.c)\n    TARGET_LINK_LIBRARIES(example-libev hiredis ev)\nENDIF()\n\nFIND_PATH(LIBEVENT event.h)\nif (LIBEVENT)\n    ADD_EXECUTABLE(example-libevent example-libevent)\n    TARGET_LINK_LIBRARIES(example-libevent hiredis event)\nENDIF()\n\nFIND_PATH(LIBUV uv.h)\nIF (LIBUV)\n    ADD_EXECUTABLE(example-libuv example-libuv.c)\n    TARGET_LINK_LIBRARIES(example-libuv hiredis uv)\nENDIF()\n\nIF (APPLE)\n    FIND_LIBRARY(CF CoreFoundation)\n    ADD_EXECUTABLE(example-macosx example-macosx.c)\n    TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF})\nENDIF()\n\nIF (ENABLE_SSL)\n    ADD_EXECUTABLE(example-ssl example-ssl.c)\n    TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl)\nENDIF()\n\nADD_EXECUTABLE(example example.c)\nTARGET_LINK_LIBRARIES(example hiredis)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-ae.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\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        aeStop(loop);\n        return;\n    }\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        aeStop(loop);\n        return;\n    }\n\n    printf(\"Disconnected...\\n\");\n    aeStop(loop);\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(64);\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": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-glib.c",
    "content": "#include <stdlib.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/glib.h>\n\nstatic GMainLoop *mainloop;\n\nstatic void\nconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,\n            int status)\n{\n    if (status != REDIS_OK) {\n        g_printerr(\"Failed to connect: %s\\n\", ac->errstr);\n        g_main_loop_quit(mainloop);\n    } else {\n        g_printerr(\"Connected...\\n\");\n    }\n}\n\nstatic void\ndisconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,\n               int status)\n{\n    if (status != REDIS_OK) {\n        g_error(\"Failed to disconnect: %s\", ac->errstr);\n    } else {\n        g_printerr(\"Disconnected...\\n\");\n        g_main_loop_quit(mainloop);\n    }\n}\n\nstatic void\ncommand_cb(redisAsyncContext *ac,\n           gpointer r,\n           gpointer user_data G_GNUC_UNUSED)\n{\n    redisReply *reply = r;\n\n    if (reply) {\n        g_print(\"REPLY: %s\\n\", reply->str);\n    }\n\n    redisAsyncDisconnect(ac);\n}\n\ngint\nmain (gint argc     G_GNUC_UNUSED,\n      gchar *argv[] G_GNUC_UNUSED)\n{\n    redisAsyncContext *ac;\n    GMainContext *context = NULL;\n    GSource *source;\n\n    ac = redisAsyncConnect(\"127.0.0.1\", 6379);\n    if (ac->err) {\n        g_printerr(\"%s\\n\", ac->errstr);\n        exit(EXIT_FAILURE);\n    }\n\n    source = redis_source_new(ac);\n    mainloop = g_main_loop_new(context, FALSE);\n    g_source_attach(source, context);\n\n    redisAsyncSetConnectCallback(ac, connect_cb);\n    redisAsyncSetDisconnectCallback(ac, disconnect_cb);\n    redisAsyncCommand(ac, command_cb, NULL, \"SET key 1234\");\n    redisAsyncCommand(ac, command_cb, NULL, \"GET key\");\n\n    g_main_loop_run(mainloop);\n\n    return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-ivykis.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/ivykis.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    iv_init();\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    redisIvykisAttach(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\n    iv_main();\n\n    iv_deinit();\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-libev.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\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": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-libevent-ssl.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis.h>\n#include <hiredis_ssl.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    if (argc < 5) {\n        fprintf(stderr,\n                \"Usage: %s <key> <host> <port> <cert> <certKey> [ca]\\n\", argv[0]);\n        exit(1);\n    }\n\n    const char *value = argv[1];\n    size_t nvalue = strlen(value);\n\n    const char *hostname = argv[2];\n    int port = atoi(argv[3]);\n\n    const char *cert = argv[4];\n    const char *certKey = argv[5];\n    const char *caCert = argc > 5 ? argv[6] : NULL;\n\n    redisAsyncContext *c = redisAsyncConnect(hostname, port);\n    if (c->err) {\n        /* Let *c leak for now... */\n        printf(\"Error: %s\\n\", c->errstr);\n        return 1;\n    }\n    if (redisSecureConnection(&c->c, caCert, cert, certKey, \"sni\") != REDIS_OK) {\n        printf(\"SSL Error!\\n\");\n        exit(1);\n    }\n\n    redisLibeventAttach(c,base);\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n    redisAsyncCommand(c, NULL, NULL, \"SET key %b\", value, nvalue);\n    redisAsyncCommand(c, getCallback, (char*)\"end-1\", \"GET key\");\n    event_base_dispatch(base);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-libevent.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\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) {\n        if (c->errstr) {\n            printf(\"errstr: %s\\n\", c->errstr);\n        }\n        return;\n    }\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    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, \"127.0.0.1\", 6379);\n    struct timeval tv = {0};\n    tv.tv_sec = 1;\n    options.timeout = &tv;\n\n\n    redisAsyncContext *c = redisAsyncConnectWithOptions(&options);\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": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-libuv.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/libuv.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    uv_loop_t* loop = uv_default_loop();\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    redisLibuvAttach(c,loop);\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    uv_run(loop, UV_RUN_DEFAULT);\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-macosx.c",
    "content": "//\n//  Created by Дмитрий Бахвалов on 13.07.15.\n//  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.\n//\n\n#include <stdio.h>\n\n#include <hiredis.h>\n#include <async.h>\n#include <adapters/macosx.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    CFRunLoopStop(CFRunLoopGetCurrent());\n    printf(\"Disconnected...\\n\");\n}\n\nint main (int argc, char **argv) {\n    signal(SIGPIPE, SIG_IGN);\n\n    CFRunLoopRef loop = CFRunLoopGetCurrent();\n    if( !loop ) {\n        printf(\"Error: Cannot get current run loop\\n\");\n        return 1;\n    }\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    redisMacOSAttach(c, loop);\n\n    redisAsyncSetConnectCallback(c,connectCallback);\n    redisAsyncSetDisconnectCallback(c,disconnectCallback);\n\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\n    CFRunLoopRun();\n\n    return 0;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-qt.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\n#include <QCoreApplication>\n#include <QTimer>\n\n#include \"example-qt.h\"\n\nvoid getCallback(redisAsyncContext *, void * r, void * privdata) {\n\n    redisReply * reply = static_cast<redisReply *>(r);\n    ExampleQt * ex = static_cast<ExampleQt *>(privdata);\n    if (reply == nullptr || ex == nullptr) return;\n\n    cout << \"key: \" << reply->str << endl;\n\n    ex->finish();\n}\n\nvoid ExampleQt::run() {\n\n    m_ctx = redisAsyncConnect(\"localhost\", 6379);\n\n    if (m_ctx->err) {\n        cerr << \"Error: \" << m_ctx->errstr << endl;\n        redisAsyncFree(m_ctx);\n        emit finished();\n    }\n\n    m_adapter.setContext(m_ctx);\n\n    redisAsyncCommand(m_ctx, NULL, NULL, \"SET key %s\", m_value);\n    redisAsyncCommand(m_ctx, getCallback, this, \"GET key\");\n}\n\nint main (int argc, char **argv) {\n\n    QCoreApplication app(argc, argv);\n\n    ExampleQt example(argv[argc-1]);\n\n    QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit()));\n    QTimer::singleShot(0, &example, SLOT(run()));\n\n    return app.exec();\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-qt.h",
    "content": "#ifndef __HIREDIS_EXAMPLE_QT_H\n#define __HIREDIS_EXAMPLE_QT_H\n\n#include <adapters/qt.h>\n\nclass ExampleQt : public QObject {\n\n    Q_OBJECT\n\n    public:\n        ExampleQt(const char * value, QObject * parent = 0)\n            : QObject(parent), m_value(value) {}\n\n    signals:\n        void finished();\n\n    public slots:\n        void run();\n\n    private:\n        void finish() { emit finished(); }\n\n    private:\n        const char * m_value;\n        redisAsyncContext * m_ctx;\n        RedisQtAdapter m_adapter;\n\n    friend\n    void getCallback(redisAsyncContext *, void *, void *);\n};\n\n#endif /* !__HIREDIS_EXAMPLE_QT_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example-ssl.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <hiredis.h>\n#include <hiredis_ssl.h>\n\nint main(int argc, char **argv) {\n    unsigned int j;\n    redisContext *c;\n    redisReply *reply;\n    if (argc < 4) {\n        printf(\"Usage: %s <host> <port> <cert> <key> [ca]\\n\", argv[0]);\n        exit(1);\n    }\n    const char *hostname = (argc > 1) ? argv[1] : \"127.0.0.1\";\n    int port = atoi(argv[2]);\n    const char *cert = argv[3];\n    const char *key = argv[4];\n    const char *ca = argc > 4 ? argv[5] : NULL;\n\n    struct timeval tv = { 1, 500000 }; // 1.5 seconds\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, hostname, port);\n    options.timeout = &tv;\n    c = redisConnectWithOptions(&options);\n\n    if (c == NULL || c->err) {\n        if (c) {\n            printf(\"Connection error: %s\\n\", c->errstr);\n            redisFree(c);\n        } else {\n            printf(\"Connection error: can't allocate redis context\\n\");\n        }\n        exit(1);\n    }\n\n    if (redisSecureConnection(c, ca, cert, key, \"sni\") != REDIS_OK) {\n        printf(\"Couldn't initialize SSL!\\n\");\n        printf(\"Error: %s\\n\", c->errstr);\n        redisFree(c);\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\", (size_t) 3, \"hello\", (size_t) 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,\"%u\",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    /* Disconnects and frees the context */\n    redisFree(c);\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/examples/example.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <hiredis.h>\n\nint main(int argc, char **argv) {\n    unsigned int j, isunix = 0;\n    redisContext *c;\n    redisReply *reply;\n    const char *hostname = (argc > 1) ? argv[1] : \"127.0.0.1\";\n\n    if (argc > 2) {\n        if (*argv[2] == 'u' || *argv[2] == 'U') {\n            isunix = 1;\n            /* in this case, host is the path to the unix socket */\n            printf(\"Will connect to unix socket @%s\\n\", hostname);\n        }\n    }\n\n    int port = (argc > 2) ? atoi(argv[2]) : 6379;\n\n    struct timeval timeout = { 1, 500000 }; // 1.5 seconds\n    if (isunix) {\n        c = redisConnectUnixWithTimeout(hostname, timeout);\n    } else {\n        c = redisConnectWithTimeout(hostname, port, timeout);\n    }\n    if (c == NULL || c->err) {\n        if (c) {\n            printf(\"Connection error: %s\\n\", c->errstr);\n            redisFree(c);\n        } else {\n            printf(\"Connection error: can't allocate redis context\\n\");\n        }\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\", (size_t) 3, \"hello\", (size_t) 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,\"%u\",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    /* Disconnects and frees the context */\n    redisFree(c);\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/fmacros.h",
    "content": "#ifndef __HIREDIS_FMACRO_H\n#define __HIREDIS_FMACRO_H\n\n#define _XOPEN_SOURCE 600\n#define _POSIX_C_SOURCE 200112L\n\n#if defined(__APPLE__) && defined(__MACH__)\n/* Enable TCP_KEEPALIVE */\n#define _DARWIN_C_SOURCE\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/hiredis.c",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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 <assert.h>\n#include <errno.h>\n#include <ctype.h>\n\n#include \"hiredis.h\"\n#include \"net.h\"\n#include \"sds.h\"\n#include \"async.h\"\n#include \"win32.h\"\n\nstatic redisContextFuncs redisContextDefaultFuncs = {\n    .free_privdata = NULL,\n    .async_read = redisAsyncRead,\n    .async_write = redisAsyncWrite,\n    .read = redisNetRead,\n    .write = redisNetWrite\n};\n\nstatic redisReply *createReplyObject(int type);\nstatic void *createStringObject(const redisReadTask *task, char *str, size_t len);\nstatic void *createArrayObject(const redisReadTask *task, size_t elements);\nstatic void *createIntegerObject(const redisReadTask *task, long long value);\nstatic void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);\nstatic void *createNilObject(const redisReadTask *task);\nstatic void *createBoolObject(const redisReadTask *task, int bval);\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    createDoubleObject,\n    createNilObject,\n    createBoolObject,\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    if (r == NULL)\n        return;\n\n    switch(r->type) {\n    case REDIS_REPLY_INTEGER:\n        break; /* Nothing to free */\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n        if (r->element != NULL) {\n            for (j = 0; j < r->elements; j++)\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    case REDIS_REPLY_DOUBLE:\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    assert(task->type == REDIS_REPLY_ERROR  ||\n           task->type == REDIS_REPLY_STATUS ||\n           task->type == REDIS_REPLY_STRING ||\n           task->type == REDIS_REPLY_VERB);\n\n    /* Copy string value */\n    if (task->type == REDIS_REPLY_VERB) {\n        buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */\n        if (buf == NULL) {\n            freeReplyObject(r);\n            return NULL;\n        }\n        memcpy(r->vtype,str,3);\n        r->vtype[3] = '\\0';\n        memcpy(buf,str+4,len-4);\n        buf[len-4] = '\\0';\n        r->len = len-4;\n    } else {\n        buf = malloc(len+1);\n        if (buf == NULL) {\n            freeReplyObject(r);\n            return NULL;\n        }\n        memcpy(buf,str,len);\n        buf[len] = '\\0';\n        r->len = len;\n    }\n    r->str = buf;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createArrayObject(const redisReadTask *task, size_t elements) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(task->type);\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->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\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->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_DOUBLE);\n    if (r == NULL)\n        return NULL;\n\n    r->dval = value;\n    r->str = malloc(len+1);\n    if (r->str == NULL) {\n        freeReplyObject(r);\n        return NULL;\n    }\n\n    /* The double reply also has the original protocol string representing a\n     * double as a null terminated string. This way the caller does not need\n     * to format back for string conversion, especially since Redis does efforts\n     * to make the string more human readable avoiding the calssical double\n     * decimal string conversion artifacts. */\n    memcpy(r->str, str, len);\n    r->str[len] = '\\0';\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\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->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createBoolObject(const redisReadTask *task, int bval) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_BOOL);\n    if (r == NULL)\n        return NULL;\n\n    r->integer = bval != 0;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * Implementation borrowed from link in redis/src/util.c:string2ll(). */\nstatic uint32_t countDigits(uint64_t v) {\n  uint32_t result = 1;\n  for (;;) {\n    if (v < 10) return result;\n    if (v < 100) return result + 1;\n    if (v < 1000) return result + 2;\n    if (v < 10000) return result + 3;\n    v /= 10000U;\n    result += 4;\n  }\n}\n\n/* Helper that calculates the bulk length given a certain string length. */\nstatic size_t bulklen(size_t len) {\n    return 1+countDigits(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 error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */\n    int j;\n\n    /* Abort if there is not target to set */\n    if (target == NULL)\n        return -1;\n\n    /* Build the command string accordingly to protocol */\n    curarg = sdsempty();\n    if (curarg == NULL)\n        return -1;\n\n    while(*c != '\\0') {\n        if (*c != '%' || c[1] == '\\0') {\n            if (*c == ' ') {\n                if (touched) {\n                    newargv = realloc(curargv,sizeof(char*)*(argc+1));\n                    if (newargv == NULL) goto memory_err;\n                    curargv = newargv;\n                    curargv[argc++] = curarg;\n                    totlen += bulklen(sdslen(curarg));\n\n                    /* curarg is put in argv so it can be overwritten. */\n                    curarg = sdsempty();\n                    if (curarg == NULL) goto memory_err;\n                    touched = 0;\n                }\n            } else {\n                newarg = sdscatlen(curarg,c,1);\n                if (newarg == NULL) goto memory_err;\n                curarg = newarg;\n                touched = 1;\n            }\n        } else {\n            char *arg;\n            size_t size;\n\n            /* Set newarg so it can be checked even if it is not touched. */\n            newarg = curarg;\n\n            switch(c[1]) {\n            case 's':\n                arg = va_arg(ap,char*);\n                size = strlen(arg);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case 'b':\n                arg = va_arg(ap,char*);\n                size = va_arg(ap,size_t);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case '%':\n                newarg = sdscat(curarg,\"%\");\n                break;\n            default:\n                /* Try to detect printf format */\n                {\n                    static const char intfmts[] = \"diouxX\";\n                    static const char flags[] = \"#0-+ \";\n                    char _format[16];\n                    const char *_p = c+1;\n                    size_t _l = 0;\n                    va_list _cpy;\n\n                    /* Flags */\n                    while (*_p != '\\0' && strchr(flags,*_p) != NULL) _p++;\n\n                    /* Field width */\n                    while (*_p != '\\0' && isdigit(*_p)) _p++;\n\n                    /* Precision */\n                    if (*_p == '.') {\n                        _p++;\n                        while (*_p != '\\0' && isdigit(*_p)) _p++;\n                    }\n\n                    /* Copy va_list before consuming with va_arg */\n                    va_copy(_cpy,ap);\n\n                    /* Integer conversion (without modifiers) */\n                    if (strchr(intfmts,*_p) != NULL) {\n                        va_arg(ap,int);\n                        goto fmt_valid;\n                    }\n\n                    /* Double conversion (without modifiers) */\n                    if (strchr(\"eEfFgGaA\",*_p) != NULL) {\n                        va_arg(ap,double);\n                        goto fmt_valid;\n                    }\n\n                    /* Size: char */\n                    if (_p[0] == 'h' && _p[1] == 'h') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* char gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: short */\n                    if (_p[0] == 'h') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* short gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long long */\n                    if (_p[0] == 'l' && _p[1] == 'l') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long */\n                    if (_p[0] == 'l') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                fmt_invalid:\n                    va_end(_cpy);\n                    goto format_err;\n\n                fmt_valid:\n                    _l = (_p+1)-c;\n                    if (_l < sizeof(_format)-2) {\n                        memcpy(_format,c,_l);\n                        _format[_l] = '\\0';\n                        newarg = sdscatvprintf(curarg,_format,_cpy);\n\n                        /* Update current position (note: outer blocks\n                         * increment c twice so compensate here) */\n                        c = _p-1;\n                    }\n\n                    va_end(_cpy);\n                    break;\n                }\n            }\n\n            if (newarg == NULL) goto memory_err;\n            curarg = newarg;\n\n            touched = 1;\n            c++;\n        }\n        c++;\n    }\n\n    /* Add the last argument if needed */\n    if (touched) {\n        newargv = realloc(curargv,sizeof(char*)*(argc+1));\n        if (newargv == NULL) goto memory_err;\n        curargv = newargv;\n        curargv[argc++] = curarg;\n        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+countDigits(argc)+2;\n\n    /* Build the command at protocol level */\n    cmd = malloc(totlen+1);\n    if (cmd == NULL) goto memory_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\nformat_err:\n    error_type = -2;\n    goto cleanup;\n\nmemory_err:\n    error_type = -1;\n    goto cleanup;\n\ncleanup:\n    if (curargv) {\n        while(argc--)\n            sdsfree(curargv[argc]);\n        free(curargv);\n    }\n\n    sdsfree(curarg);\n    free(cmd);\n\n    return error_type;\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 as a size_t. 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\n    /* The API says \"-1\" means bad result, but we now also return \"-2\" in some\n     * cases.  Force the return value to always be -1. */\n    if (len < 0)\n        len = -1;\n\n    return len;\n}\n\n/* Format a command according to the Redis protocol using an sds string and\n * sdscatfmt for the processing of arguments. 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 redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,\n                              const size_t *argvlen)\n{\n    sds cmd;\n    unsigned long long totlen;\n    int j;\n    size_t len;\n\n    /* Abort on a NULL target */\n    if (target == NULL)\n        return -1;\n\n    /* Calculate our total size */\n    totlen = 1+countDigits(argc)+2;\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        totlen += bulklen(len);\n    }\n\n    /* Use an SDS string for command construction */\n    cmd = sdsempty();\n    if (cmd == NULL)\n        return -1;\n\n    /* We already know how much storage we need */\n    cmd = sdsMakeRoomFor(cmd, totlen);\n    if (cmd == NULL)\n        return -1;\n\n    /* Construct command */\n    cmd = sdscatfmt(cmd, \"*%i\\r\\n\", argc);\n    for (j=0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        cmd = sdscatfmt(cmd, \"$%u\\r\\n\", len);\n        cmd = sdscatlen(cmd, argv[j], len);\n        cmd = sdscatlen(cmd, \"\\r\\n\", sizeof(\"\\r\\n\")-1);\n    }\n\n    assert(sdslen(cmd)==totlen);\n\n    *target = cmd;\n    return totlen;\n}\n\nvoid redisFreeSdsCommand(sds cmd) {\n    sdsfree(cmd);\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    /* Abort on a NULL target */\n    if (target == NULL)\n        return -1;\n\n    /* Calculate number of bytes needed for the command */\n    totlen = 1+countDigits(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 redisFreeCommand(char *cmd) {\n    free(cmd);\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\nredisReader *redisReaderCreate(void) {\n    return redisReaderCreateWithFunctions(&defaultFunctions);\n}\n\nstatic redisContext *redisContextInit(const redisOptions *options) {\n    redisContext *c;\n\n    c = calloc(1, sizeof(*c));\n    if (c == NULL)\n        return NULL;\n\n    c->funcs = &redisContextDefaultFuncs;\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n    c->fd = REDIS_INVALID_FD;\n\n    if (c->obuf == NULL || c->reader == NULL) {\n        redisFree(c);\n        return NULL;\n    }\n    (void)options; /* options are used in other functions */\n    return c;\n}\n\nvoid redisFree(redisContext *c) {\n    if (c == NULL)\n        return;\n    redisNetClose(c);\n\n    sdsfree(c->obuf);\n    redisReaderFree(c->reader);\n    free(c->tcp.host);\n    free(c->tcp.source_addr);\n    free(c->unix_sock.path);\n    free(c->timeout);\n    free(c->saddr);\n    if (c->funcs->free_privdata) {\n        c->funcs->free_privdata(c->privdata);\n    }\n    memset(c, 0xff, sizeof(*c));\n    free(c);\n}\n\nredisFD redisFreeKeepFd(redisContext *c) {\n    redisFD fd = c->fd;\n    c->fd = REDIS_INVALID_FD;\n    redisFree(c);\n    return fd;\n}\n\nint redisReconnect(redisContext *c) {\n    c->err = 0;\n    memset(c->errstr, '\\0', strlen(c->errstr));\n\n    if (c->privdata && c->funcs->free_privdata) {\n        c->funcs->free_privdata(c->privdata);\n        c->privdata = NULL;\n    }\n\n    redisNetClose(c);\n\n    sdsfree(c->obuf);\n    redisReaderFree(c->reader);\n\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n\n    if (c->connection_type == REDIS_CONN_TCP) {\n        return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,\n                c->timeout, c->tcp.source_addr);\n    } else if (c->connection_type == REDIS_CONN_UNIX) {\n        return redisContextConnectUnix(c, c->unix_sock.path, c->timeout);\n    } else {\n        /* Something bad happened here and shouldn't have. There isn't\n           enough information in the context to reconnect. */\n        __redisSetError(c,REDIS_ERR_OTHER,\"Not enough information to reconnect\");\n    }\n\n    return REDIS_ERR;\n}\n\nredisContext *redisConnectWithOptions(const redisOptions *options) {\n    redisContext *c = redisContextInit(options);\n    if (c == NULL) {\n        return NULL;\n    }\n    if (!(options->options & REDIS_OPT_NONBLOCK)) {\n        c->flags |= REDIS_BLOCK;\n    }\n    if (options->options & REDIS_OPT_REUSEADDR) {\n        c->flags |= REDIS_REUSEADDR;\n    }\n    if (options->options & REDIS_OPT_NOAUTOFREE) {\n      c->flags |= REDIS_NO_AUTO_FREE;\n    }\n\n    if (options->type == REDIS_CONN_TCP) {\n        redisContextConnectBindTcp(c, options->endpoint.tcp.ip,\n                                   options->endpoint.tcp.port, options->timeout,\n                                   options->endpoint.tcp.source_addr);\n    } else if (options->type == REDIS_CONN_UNIX) {\n        redisContextConnectUnix(c, options->endpoint.unix_socket,\n                                options->timeout);\n    } else if (options->type == REDIS_CONN_USERFD) {\n        c->fd = options->endpoint.fd;\n        c->flags |= REDIS_CONNECTED;\n    } else {\n        // Unknown type - FIXME - FREE\n        return NULL;\n    }\n    if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {\n        redisContextSetTimeout(c, *options->timeout);\n    }\n    return 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    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.timeout = &tv;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectNonBlock(const char *ip, int port) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectBindNonBlock(const char *ip, int port,\n                                       const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,\n                                                const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnix(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    options.timeout = &tv;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnixNonBlock(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectFd(redisFD fd) {\n    redisOptions options = {0};\n    options.type = REDIS_CONN_USERFD;\n    options.endpoint.fd = fd;\n    return redisConnectWithOptions(&options);\n}\n\n/* Set read/write timeout on a blocking socket. */\nint redisSetTimeout(redisContext *c, const struct timeval tv) {\n    if (c->flags & REDIS_BLOCK)\n        return redisContextSetTimeout(c,tv);\n    return REDIS_ERR;\n}\n\n/* Enable connection KeepAlive. */\nint redisEnableKeepAlive(redisContext *c) {\n    if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)\n        return REDIS_ERR;\n    return REDIS_OK;\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 redisGetReplyFromReader 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 = c->funcs->read(c, buf, sizeof(buf));\n    if (nread > 0) {\n        if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) {\n            __redisSetError(c, c->reader->err, c->reader->errstr);\n            return REDIS_ERR;\n        } else {\n        }\n    } else if (nread < 0) {\n        return REDIS_ERR;\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 * successfully 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 occurred trying to write and sets\n * c->errstr to hold the appropriate error string.\n */\nint redisBufferWrite(redisContext *c, int *done) {\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        int nwritten = c->funcs->write(c);\n        if (nwritten < 0) {\n            return REDIS_ERR;\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, const 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 redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {\n\n    if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {\n        return REDIS_ERR;\n    }\n\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    } else if (len == -2) {\n        __redisSetError(c,REDIS_ERR_OTHER,\"Invalid format string\");\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    sds cmd;\n    int len;\n\n    len = redisFormatSdsCommandArgv(&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        sdsfree(cmd);\n        return REDIS_ERR;\n    }\n\n    sdsfree(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 successfully 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    va_start(ap,format);\n    void *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": "Chapter07/redis-6.0.3/deps/hiredis/hiredis.h",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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 \"read.h\"\n#include <stdarg.h> /* for va_list */\n#ifndef _MSC_VER\n#include <sys/time.h> /* for struct timeval */\n#else\nstruct timeval; /* forward declaration */\n#endif\n#include <stdint.h> /* uintXX_t, etc */\n#include \"sds.h\" /* for sds */\n\n#define HIREDIS_MAJOR 0\n#define HIREDIS_MINOR 14\n#define HIREDIS_PATCH 0\n#define HIREDIS_SONAME 0.14\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/* Flag that is set when we should set SO_REUSEADDR before calling bind() */\n#define REDIS_REUSEADDR 0x80\n\n/**\n * Flag that indicates the user does not want the context to\n * be automatically freed upon error\n */\n#define REDIS_NO_AUTO_FREE 0x200\n\n#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */\n\n/* number of times we retry to connect in the case of EADDRNOTAVAIL and\n * SO_REUSEADDR is being used. */\n#define REDIS_CONNECT_RETRIES  10\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    double dval; /* The double when type is REDIS_REPLY_DOUBLE */\n    size_t len; /* Length of string */\n    char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING\n                  and REDIS_REPLY_DOUBLE (in additionl to dval). */\n    char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null\n                      terminated 3 character content type, such as \"txt\". */\n    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */\n    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */\n} redisReply;\n\nredisReader *redisReaderCreate(void);\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);\nint redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);\nvoid redisFreeCommand(char *cmd);\nvoid redisFreeSdsCommand(sds cmd);\n\nenum redisConnectionType {\n    REDIS_CONN_TCP,\n    REDIS_CONN_UNIX,\n    REDIS_CONN_USERFD\n};\n\nstruct redisSsl;\n\n#define REDIS_OPT_NONBLOCK 0x01\n#define REDIS_OPT_REUSEADDR 0x02\n\n/**\n * Don't automatically free the async object on a connection failure,\n * or other implicit conditions. Only free on an explicit call to disconnect() or free()\n */\n#define REDIS_OPT_NOAUTOFREE 0x04\n\n/* In Unix systems a file descriptor is a regular signed int, with -1\n * representing an invalid descriptor. In Windows it is a SOCKET\n * (32- or 64-bit unsigned integer depending on the architecture), where\n * all bits set (~0) is INVALID_SOCKET.  */\n#ifndef _WIN32\ntypedef int redisFD;\n#define REDIS_INVALID_FD -1\n#else\n#ifdef _WIN64\ntypedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */\n#else\ntypedef unsigned long redisFD;      /* SOCKET = 32-bit UINT_PTR */\n#endif\n#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */\n#endif\n\ntypedef struct {\n    /*\n     * the type of connection to use. This also indicates which\n     * `endpoint` member field to use\n     */\n    int type;\n    /* bit field of REDIS_OPT_xxx */\n    int options;\n    /* timeout value. if NULL, no timeout is used */\n    const struct timeval *timeout;\n    union {\n        /** use this field for tcp/ip connections */\n        struct {\n            const char *source_addr;\n            const char *ip;\n            int port;\n        } tcp;\n        /** use this field for unix domain sockets */\n        const char *unix_socket;\n        /**\n         * use this field to have hiredis operate an already-open\n         * file descriptor */\n        redisFD fd;\n    } endpoint;\n} redisOptions;\n\n/**\n * Helper macros to initialize options to their specified fields.\n */\n#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \\\n    (opts)->type = REDIS_CONN_TCP; \\\n    (opts)->endpoint.tcp.ip = ip_; \\\n    (opts)->endpoint.tcp.port = port_;\n\n#define REDIS_OPTIONS_SET_UNIX(opts, path) \\\n    (opts)->type = REDIS_CONN_UNIX;        \\\n    (opts)->endpoint.unix_socket = path;\n\nstruct redisAsyncContext;\nstruct redisContext;\n\ntypedef struct redisContextFuncs {\n    void (*free_privdata)(void *);\n    void (*async_read)(struct redisAsyncContext *);\n    void (*async_write)(struct redisAsyncContext *);\n    int (*read)(struct redisContext *, char *, size_t);\n    int (*write)(struct redisContext *);\n} redisContextFuncs;\n\n/* Context for a connection to Redis */\ntypedef struct redisContext {\n    const redisContextFuncs *funcs;   /* Function table */\n\n    int err; /* Error flags, 0 when there is no error */\n    char errstr[128]; /* String representation of error when applicable */\n    redisFD fd;\n    int flags;\n    char *obuf; /* Write buffer */\n    redisReader *reader; /* Protocol reader */\n\n    enum redisConnectionType connection_type;\n    struct timeval *timeout;\n\n    struct {\n        char *host;\n        char *source_addr;\n        int port;\n    } tcp;\n\n    struct {\n        char *path;\n    } unix_sock;\n\n    /* For non-blocking connect */\n    struct sockadr *saddr;\n    size_t addrlen;\n\n    /* Additional private data for hiredis addons such as SSL */\n    void *privdata;\n} redisContext;\n\nredisContext *redisConnectWithOptions(const redisOptions *options);\nredisContext *redisConnect(const char *ip, int port);\nredisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);\nredisContext *redisConnectNonBlock(const char *ip, int port);\nredisContext *redisConnectBindNonBlock(const char *ip, int port,\n                                       const char *source_addr);\nredisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,\n                                                const char *source_addr);\nredisContext *redisConnectUnix(const char *path);\nredisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);\nredisContext *redisConnectUnixNonBlock(const char *path);\nredisContext *redisConnectFd(redisFD fd);\n\n/**\n * Reconnect the given context using the saved information.\n *\n * This re-uses the exact same connect options as in the initial connection.\n * host, ip (or path), timeout and bind address are reused,\n * flags are used unmodified from the existing context.\n *\n * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.\n */\nint redisReconnect(redisContext *c);\n\nint redisSetTimeout(redisContext *c, const struct timeval tv);\nint redisEnableKeepAlive(redisContext *c);\nvoid redisFree(redisContext *c);\nredisFD redisFreeKeepFd(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 formatted command to the output buffer. Use these functions in blocking mode\n * to get a pipeline of commands. */\nint redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);\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": "Chapter07/redis-6.0.3/deps/hiredis/hiredis.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\nincludedir=${prefix}/include\npkgincludedir=${includedir}/hiredis\n\nName: hiredis\nDescription: Minimalistic C client library for Redis.\nVersion: @PROJECT_VERSION@\nLibs: -L${libdir} -lhiredis\nCflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/hiredis_ssl.h",
    "content": "\n/*\n * Copyright (c) 2019, Redis Labs\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_SSL_H\n#define __HIREDIS_SSL_H\n\n/* This is the underlying struct for SSL in ssl.h, which is not included to\n * keep build dependencies short here.\n */\nstruct ssl_st;\n\n/**\n * Secure the connection using SSL. This should be done before any command is\n * executed on the connection.\n */\nint redisSecureConnection(redisContext *c, const char *capath, const char *certpath,\n                          const char *keypath, const char *servername);\n\n/**\n * Initiate SSL/TLS negotiation on a provided context.\n */\n\nint redisInitiateSSL(redisContext *c, struct ssl_st *ssl);\n\n#endif  /* __HIREDIS_SSL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/hiredis_ssl.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\nincludedir=${prefix}/include\npkgincludedir=${includedir}/hiredis\n\nName: hiredis_ssl\nDescription: SSL Support for hiredis.\nVersion: @PROJECT_VERSION@\nRequires: hiredis\nLibs: -L${libdir} -lhiredis_ssl\nLibs.private: -lssl -lcrypto\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/net.c",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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 <fcntl.h>\n#include <string.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <limits.h>\n#include <stdlib.h>\n\n#include \"net.h\"\n#include \"sds.h\"\n#include \"sockcompat.h\"\n#include \"win32.h\"\n\n/* Defined in hiredis.c */\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\nvoid redisNetClose(redisContext *c) {\n    if (c && c->fd != REDIS_INVALID_FD) {\n        close(c->fd);\n        c->fd = REDIS_INVALID_FD;\n    }\n}\n\nint redisNetRead(redisContext *c, char *buf, size_t bufcap) {\n    int nread = recv(c->fd, buf, bufcap, 0);\n    if (nread == -1) {\n        if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n            return 0;\n        } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {\n            /* especially in windows */\n            __redisSetError(c, REDIS_ERR_TIMEOUT, \"recv timeout\");\n            return -1;\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    } else if (nread == 0) {\n        __redisSetError(c, REDIS_ERR_EOF, \"Server closed the connection\");\n        return -1;\n    } else {\n        return nread;\n    }\n}\n\nint redisNetWrite(redisContext *c) {\n    int nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);\n    if (nwritten < 0) {\n        if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n    return nwritten;\n}\n\nstatic void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {\n    int errorno = errno;  /* snprintf() may change errno */\n    char buf[128] = { 0 };\n    size_t len = 0;\n\n    if (prefix != NULL)\n        len = snprintf(buf,sizeof(buf),\"%s: \",prefix);\n    strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);\n    __redisSetError(c,type,buf);\n}\n\nstatic int redisSetReuseAddr(redisContext *c) {\n    int on = 1;\n    if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int redisCreateSocket(redisContext *c, int type) {\n    redisFD s;\n    if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n    c->fd = s;\n    if (type == AF_INET) {\n        if (redisSetReuseAddr(c) == REDIS_ERR) {\n            return REDIS_ERR;\n        }\n    }\n    return REDIS_OK;\n}\n\nstatic int redisSetBlocking(redisContext *c, int blocking) {\n#ifndef _WIN32\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(c->fd, F_GETFL)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_GETFL)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n\n    if (blocking)\n        flags &= ~O_NONBLOCK;\n    else\n        flags |= O_NONBLOCK;\n\n    if (fcntl(c->fd, F_SETFL, flags) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_SETFL)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n#else\n    u_long mode = blocking ? 0 : 1;\n    if (ioctl(c->fd, FIONBIO, &mode) == -1) {\n        __redisSetErrorFromErrno(c, REDIS_ERR_IO, \"ioctl(FIONBIO)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n#endif /* _WIN32 */\n    return REDIS_OK;\n}\n\nint redisKeepAlive(redisContext *c, int interval) {\n    int val = 1;\n    redisFD fd = c->fd;\n\n    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = interval;\n\n#if defined(__APPLE__) && defined(__MACH__)\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n#else\n#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = interval/3;\n    if (val == 0) val = 1;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = 3;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n#endif\n#endif\n\n    return REDIS_OK;\n}\n\nstatic int redisSetTcpNoDelay(redisContext *c) {\n    int yes = 1;\n    if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(TCP_NODELAY)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\n#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)\n\nstatic int redisContextTimeoutMsec(redisContext *c, long *result)\n{\n    const struct timeval *timeout = c->timeout;\n    long msec = -1;\n\n    /* Only use timeout when not NULL. */\n    if (timeout != NULL) {\n        if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {\n            *result = msec;\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    *result = msec;\n    return REDIS_OK;\n}\n\nstatic int redisContextWaitReady(redisContext *c, long msec) {\n    struct pollfd   wfd[1];\n\n    wfd[0].fd     = c->fd;\n    wfd[0].events = POLLOUT;\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            redisNetClose(c);\n            return REDIS_ERR;\n        } else if (res == 0) {\n            errno = ETIMEDOUT;\n            __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n            redisNetClose(c);\n            return REDIS_ERR;\n        }\n\n        if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {\n            redisCheckSocketError(c);\n            return REDIS_ERR;\n        }\n\n        return REDIS_OK;\n    }\n\n    __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n    redisNetClose(c);\n    return REDIS_ERR;\n}\n\nint redisCheckConnectDone(redisContext *c, int *completed) {\n    int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);\n    if (rc == 0) {\n        *completed = 1;\n        return REDIS_OK;\n    }\n    switch (errno) {\n    case EISCONN:\n        *completed = 1;\n        return REDIS_OK;\n    case EALREADY:\n    case EINPROGRESS:\n    case EWOULDBLOCK:\n        *completed = 0;\n        return REDIS_OK;\n    default:\n        return REDIS_ERR;\n    }\n}\n\nint redisCheckSocketError(redisContext *c) {\n    int err = 0, errno_saved = errno;\n    socklen_t errlen = sizeof(err);\n\n    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"getsockopt(SO_ERROR)\");\n        return REDIS_ERR;\n    }\n\n    if (err == 0) {\n        err = errno_saved;\n    }\n\n    if (err) {\n        errno = err;\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n\n    return REDIS_OK;\n}\n\nint redisContextSetTimeout(redisContext *c, const struct timeval tv) {\n    const void *to_ptr = &tv;\n    size_t to_sz = sizeof(tv);\n#ifdef _WIN32\n    DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;\n    to_ptr = &timeout_msec;\n    to_sz = sizeof(timeout_msec);\n#endif\n    if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_RCVTIMEO)\");\n        return REDIS_ERR;\n    }\n    if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_SNDTIMEO)\");\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int _redisContextConnectTcp(redisContext *c, const char *addr, int port,\n                                   const struct timeval *timeout,\n                                   const char *source_addr) {\n    redisFD s;\n    int rv, n;\n    char _port[6];  /* strlen(\"65535\"); */\n    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;\n    int blocking = (c->flags & REDIS_BLOCK);\n    int reuseaddr = (c->flags & REDIS_REUSEADDR);\n    int reuses = 0;\n    long timeout_msec = -1;\n\n    servinfo = NULL;\n    c->connection_type = REDIS_CONN_TCP;\n    c->tcp.port = port;\n\n    /* We need to take possession of the passed parameters\n     * to make them reusable for a reconnect.\n     * We also carefully check we don't free data we already own,\n     * as in the case of the reconnect method.\n     *\n     * This is a bit ugly, but atleast it works and doesn't leak memory.\n     **/\n    if (c->tcp.host != addr) {\n        free(c->tcp.host);\n\n        c->tcp.host = strdup(addr);\n    }\n\n    if (timeout) {\n        if (c->timeout != timeout) {\n            if (c->timeout == NULL)\n                c->timeout = malloc(sizeof(struct timeval));\n\n            memcpy(c->timeout, timeout, sizeof(struct timeval));\n        }\n    } else {\n        free(c->timeout);\n        c->timeout = NULL;\n    }\n\n    if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {\n        __redisSetError(c, REDIS_ERR_IO, \"Invalid timeout specified\");\n        goto error;\n    }\n\n    if (source_addr == NULL) {\n        free(c->tcp.source_addr);\n        c->tcp.source_addr = NULL;\n    } else if (c->tcp.source_addr != source_addr) {\n        free(c->tcp.source_addr);\n        c->tcp.source_addr = strdup(source_addr);\n    }\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(c->tcp.host,_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) {\naddrretry:\n        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)\n            continue;\n\n        c->fd = s;\n        if (redisSetBlocking(c,0) != REDIS_OK)\n            goto error;\n        if (c->tcp.source_addr) {\n            int bound = 0;\n            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */\n            if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {\n                char buf[128];\n                snprintf(buf,sizeof(buf),\"Can't get addr: %s\",gai_strerror(rv));\n                __redisSetError(c,REDIS_ERR_OTHER,buf);\n                goto error;\n            }\n\n            if (reuseaddr) {\n                n = 1;\n                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,\n                               sizeof(n)) < 0) {\n                    freeaddrinfo(bservinfo);\n                    goto error;\n                }\n            }\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            freeaddrinfo(bservinfo);\n            if (!bound) {\n                char buf[128];\n                snprintf(buf,sizeof(buf),\"Can't bind socket: %s\",strerror(errno));\n                __redisSetError(c,REDIS_ERR_OTHER,buf);\n                goto error;\n            }\n        }\n\n        /* For repeat connection */\n        free(c->saddr);\n        c->saddr = malloc(p->ai_addrlen);\n        memcpy(c->saddr, p->ai_addr, p->ai_addrlen);\n        c->addrlen = p->ai_addrlen;\n\n        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {\n            if (errno == EHOSTUNREACH) {\n                redisNetClose(c);\n                continue;\n            } else if (errno == EINPROGRESS) {\n                if (blocking) {\n                    goto wait_for_ready;\n                }\n                /* This is ok.\n                 * Note that even when it's in blocking mode, we unset blocking\n                 * for `connect()`\n                 */\n            } else if (errno == EADDRNOTAVAIL && reuseaddr) {\n                if (++reuses >= REDIS_CONNECT_RETRIES) {\n                    goto error;\n                } else {\n                    redisNetClose(c);\n                    goto addrretry;\n                }\n            } else {\n                wait_for_ready:\n                if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)\n                    goto error;\n            }\n        }\n        if (blocking && redisSetBlocking(c,1) != REDIS_OK)\n            goto error;\n        if (redisSetTcpNoDelay(c) != REDIS_OK)\n            goto error;\n\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    if(servinfo) {\n        freeaddrinfo(servinfo);\n    }\n\n    return rv;  // Need to return REDIS_OK if alright\n}\n\nint redisContextConnectTcp(redisContext *c, const char *addr, int port,\n                           const struct timeval *timeout) {\n    return _redisContextConnectTcp(c, addr, port, timeout, NULL);\n}\n\nint redisContextConnectBindTcp(redisContext *c, const char *addr, int port,\n                               const struct timeval *timeout,\n                               const char *source_addr) {\n    return _redisContextConnectTcp(c, addr, port, timeout, source_addr);\n}\n\nint redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {\n#ifndef _WIN32\n    int blocking = (c->flags & REDIS_BLOCK);\n    struct sockaddr_un *sa;\n    long timeout_msec = -1;\n\n    if (redisCreateSocket(c,AF_UNIX) < 0)\n        return REDIS_ERR;\n    if (redisSetBlocking(c,0) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->connection_type = REDIS_CONN_UNIX;\n    if (c->unix_sock.path != path)\n        c->unix_sock.path = strdup(path);\n\n    if (timeout) {\n        if (c->timeout != timeout) {\n            if (c->timeout == NULL)\n                c->timeout = malloc(sizeof(struct timeval));\n\n            memcpy(c->timeout, timeout, sizeof(struct timeval));\n        }\n    } else {\n        free(c->timeout);\n        c->timeout = NULL;\n    }\n\n    if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)\n        return REDIS_ERR;\n\n    sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));\n    c->addrlen = sizeof(struct sockaddr_un);\n    sa->sun_family = AF_UNIX;\n    strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);\n    if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {\n        if (errno == EINPROGRESS && !blocking) {\n            /* This is ok. */\n        } else {\n            if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)\n                return REDIS_ERR;\n        }\n    }\n\n    /* Reset socket to be blocking after connect(2). */\n    if (blocking && redisSetBlocking(c,1) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->flags |= REDIS_CONNECTED;\n    return REDIS_OK;\n#else\n    /* We currently do not support Unix sockets for Windows. */\n    /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */\n    errno = EPROTONOSUPPORT;\n    return REDIS_ERR;\n#endif /* _WIN32 */\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/net.h",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig 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\nvoid redisNetClose(redisContext *c);\nint redisNetRead(redisContext *c, char *buf, size_t bufcap);\nint redisNetWrite(redisContext *c);\n\nint redisCheckSocketError(redisContext *c);\nint redisContextSetTimeout(redisContext *c, const struct timeval tv);\nint redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);\nint redisContextConnectBindTcp(redisContext *c, const char *addr, int port,\n                               const struct timeval *timeout,\n                               const char *source_addr);\nint redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);\nint redisKeepAlive(redisContext *c, int interval);\nint redisCheckConnectDone(redisContext *c, int *completed);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/read.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#ifndef _MSC_VER\n#include <unistd.h>\n#include <strings.h>\n#endif\n#include <assert.h>\n#include <errno.h>\n#include <ctype.h>\n#include <limits.h>\n#include <math.h>\n\n#include \"read.h\"\n#include \"sds.h\"\n#include \"win32.h\"\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    sdsfree(r->buf);\n    r->buf = NULL;\n    r->pos = r->len = 0;\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 (pos==_len) {\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/* Convert a string into a long long. Returns REDIS_OK if the string could be\n * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value\n * will be set to the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a long long: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. */\nstatic int 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 REDIS_ERR;\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 REDIS_OK;\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 REDIS_ERR;\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 REDIS_OK;\n    } else {\n        return REDIS_ERR;\n    }\n\n    while (plen < slen && p[0] >= '0' && p[0] <= '9') {\n        if (v > (ULLONG_MAX / 10)) /* Overflow. */\n            return REDIS_ERR;\n        v *= 10;\n\n        if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */\n            return REDIS_ERR;\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 REDIS_ERR;\n\n    if (negative) {\n        if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */\n            return REDIS_ERR;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > LLONG_MAX) /* Overflow. */\n            return REDIS_ERR;\n        if (value != NULL) *value = v;\n    }\n    return REDIS_OK;\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               prv->type == REDIS_REPLY_MAP ||\n               prv->type == REDIS_REPLY_SET);\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                long long v;\n                if (string2ll(p, len, &v) == REDIS_ERR) {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Bad integer value\");\n                    return REDIS_ERR;\n                }\n                obj = r->fn->createInteger(cur,v);\n            } else {\n                obj = (void*)REDIS_REPLY_INTEGER;\n            }\n        } else if (cur->type == REDIS_REPLY_DOUBLE) {\n            if (r->fn && r->fn->createDouble) {\n                char buf[326], *eptr;\n                double d;\n\n                if ((size_t)len >= sizeof(buf)) {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Double value is too large\");\n                    return REDIS_ERR;\n                }\n\n                memcpy(buf,p,len);\n                buf[len] = '\\0';\n\n                if (strcasecmp(buf,\",inf\") == 0) {\n                    d = INFINITY; /* Positive infinite. */\n                } else if (strcasecmp(buf,\",-inf\") == 0) {\n                    d = -INFINITY; /* Nevative infinite. */\n                } else {\n                    d = strtod((char*)buf,&eptr);\n                    if (buf[0] == '\\0' || eptr[0] != '\\0' || isnan(d)) {\n                        __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                                \"Bad double value\");\n                        return REDIS_ERR;\n                    }\n                }\n                obj = r->fn->createDouble(cur,d,buf,len);\n            } else {\n                obj = (void*)REDIS_REPLY_DOUBLE;\n            }\n        } else if (cur->type == REDIS_REPLY_NIL) {\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n        } else if (cur->type == REDIS_REPLY_BOOL) {\n            int bval = p[0] == 't' || p[0] == 'T';\n            if (r->fn && r->fn->createBool)\n                obj = r->fn->createBool(cur,bval);\n            else\n                obj = (void*)REDIS_REPLY_BOOL;\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 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\n        if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bad bulk string length\");\n            return REDIS_ERR;\n        }\n\n        if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bulk string length out of range\");\n            return REDIS_ERR;\n        }\n\n        if (len == -1) {\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 ((cur->type == REDIS_REPLY_VERB && len < 4) ||\n                    (cur->type == REDIS_REPLY_VERB && s[5] != ':'))\n                {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Verbatim string 4 bytes of content type are \"\n                            \"missing or incorrectly encoded.\");\n                    return REDIS_ERR;\n                }\n                if (r->fn && r->fn->createString)\n                    obj = r->fn->createString(cur,s+2,len);\n                else\n                    obj = (void*)(long)cur->type;\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\n/* Process the array, map and set types. */\nstatic int processAggregateItem(redisReader *r) {\n    redisReadTask *cur = &(r->rstack[r->ridx]);\n    void *obj;\n    char *p;\n    long long elements;\n    int root = 0, len;\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,&len)) != NULL) {\n        if (string2ll(p, len, &elements) == REDIS_ERR) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bad multi-bulk length\");\n            return REDIS_ERR;\n        }\n\n        root = (r->ridx == 0);\n\n        if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Multi-bulk length out of range\");\n            return REDIS_ERR;\n        }\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 (cur->type == REDIS_REPLY_MAP) elements *= 2;\n\n            if (r->fn && r->fn->createArray)\n                obj = r->fn->createArray(cur,elements);\n            else\n                obj = (void*)(long)cur->type;\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_DOUBLE;\n                break;\n            case '_':\n                cur->type = REDIS_REPLY_NIL;\n                break;\n            case '$':\n                cur->type = REDIS_REPLY_STRING;\n                break;\n            case '*':\n                cur->type = REDIS_REPLY_ARRAY;\n                break;\n            case '%':\n                cur->type = REDIS_REPLY_MAP;\n                break;\n            case '~':\n                cur->type = REDIS_REPLY_SET;\n                break;\n            case '#':\n                cur->type = REDIS_REPLY_BOOL;\n                break;\n            case '=':\n                cur->type = REDIS_REPLY_VERB;\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    case REDIS_REPLY_DOUBLE:\n    case REDIS_REPLY_NIL:\n    case REDIS_REPLY_BOOL:\n        return processLineItem(r);\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        return processBulkItem(r);\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n        return processAggregateItem(r);\n    default:\n        assert(NULL);\n        return REDIS_ERR; /* Avoid warning. */\n    }\n}\n\nredisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {\n    redisReader *r;\n\n    r = calloc(1,sizeof(redisReader));\n    if (r == NULL)\n        return NULL;\n\n    r->fn = fn;\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 == NULL)\n        return;\n    if (r->reply != NULL && r->fn && r->fn->freeObject)\n        r->fn->freeObject(r->reply);\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        } else if (r->reply != NULL && r->fn && r->fn->freeObject) {\n            r->fn->freeObject(r->reply);\n        }\n        r->reply = NULL;\n    }\n    return REDIS_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/read.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\n#ifndef __HIREDIS_READ_H\n#define __HIREDIS_READ_H\n#include <stdio.h> /* for size_t */\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 occurred. 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_TIMEOUT 6 /* Timed out */\n#define REDIS_ERR_OTHER 2 /* Everything else... */\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#define REDIS_REPLY_DOUBLE 7\n#define REDIS_REPLY_BOOL 8\n#define REDIS_REPLY_MAP 9\n#define REDIS_REPLY_SET 10\n#define REDIS_REPLY_ATTR 11\n#define REDIS_REPLY_PUSH 12\n#define REDIS_REPLY_BIGNUM 13\n#define REDIS_REPLY_VERB 14\n\n#define REDIS_READER_MAX_BUF (1024*16)  /* Default max unused reader buffer. */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\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*, size_t);\n    void *(*createInteger)(const redisReadTask*, long long);\n    void *(*createDouble)(const redisReadTask*, double, char*, size_t);\n    void *(*createNil)(const redisReadTask*);\n    void *(*createBool)(const redisReadTask*, int);\n    void (*freeObject)(void*);\n} redisReplyObjectFunctions;\n\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 *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);\nvoid redisReaderFree(redisReader *r);\nint redisReaderFeed(redisReader *r, const char *buf, size_t len);\nint redisReaderGetReply(redisReader *r, void **reply);\n\n#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))\n#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)\n#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/sds.c",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include \"sds.h\"\n#include \"sdsalloc.h\"\n\nstatic inline int sdsHdrSize(char type) {\n    switch(type&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return sizeof(struct sdshdr5);\n        case SDS_TYPE_8:\n            return sizeof(struct sdshdr8);\n        case SDS_TYPE_16:\n            return sizeof(struct sdshdr16);\n        case SDS_TYPE_32:\n            return sizeof(struct sdshdr32);\n        case SDS_TYPE_64:\n            return sizeof(struct sdshdr64);\n    }\n    return 0;\n}\n\nstatic inline char sdsReqType(size_t string_size) {\n    if (string_size < 32)\n        return SDS_TYPE_5;\n    if (string_size < 0xff)\n        return SDS_TYPE_8;\n    if (string_size < 0xffff)\n        return SDS_TYPE_16;\n    if (string_size < 0xffffffff)\n        return SDS_TYPE_32;\n    return SDS_TYPE_64;\n}\n\n/* Create a new sds string with the content specified by the 'init' pointer\n * and 'initlen'.\n * If NULL is used for 'init' the string is initialized with zero bytes.\n *\n * The string is always null-termined (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3);\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    void *sh;\n    sds s;\n    char type = sdsReqType(initlen);\n    /* Empty strings are usually created in order to append. Use type 8\n     * since type 5 is not good at this. */\n    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;\n    int hdrlen = sdsHdrSize(type);\n    unsigned char *fp; /* flags pointer. */\n\n    sh = s_malloc(hdrlen+initlen+1);\n    if (sh == NULL) return NULL;\n    if (!init)\n        memset(sh, 0, hdrlen+initlen+1);\n    s = (char*)sh+hdrlen;\n    fp = ((unsigned char*)s)-1;\n    switch(type) {\n        case SDS_TYPE_5: {\n            *fp = type | (initlen << SDS_TYPE_BITS);\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n    }\n    if (initlen && init)\n        memcpy(s, init, initlen);\n    s[initlen] = '\\0';\n    return s;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null terminated C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    s_free((char*)s-sdsHdrSize(s[-1]));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    int reallen = strlen(s);\n    sdssetlen(s, reallen);\n}\n\n/* Modify an sds string in-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    sdssetlen(s, 0);\n    s[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n    void *sh, *newsh;\n    size_t avail = sdsavail(s);\n    size_t len, newlen;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n\n    /* Return ASAP if there is enough space left. */\n    if (avail >= addlen) return s;\n\n    len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n\n    type = sdsReqType(newlen);\n\n    /* Don't use type 5: the user is appending to the string and type 5 is\n     * not able to remember empty space, so sdsMakeRoomFor() must be called\n     * at every appending operation. */\n    if (type == SDS_TYPE_5) type = SDS_TYPE_8;\n\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+newlen+1);\n        if (newsh == NULL) {\n            s_free(sh);\n            return NULL;\n        }\n        s = (char*)newsh+hdrlen;\n    } else {\n        /* Since the header size changes, need to move the string forward,\n         * and can't use realloc */\n        newsh = s_malloc(hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, newlen);\n    return s;\n}\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    void *sh, *newsh;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n    size_t len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n\n    type = sdsReqType(len);\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        newsh = s_malloc(hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, len);\n    return s;\n}\n\n/* Return the total size of the allocation of the specifed sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    size_t alloc = sdsalloc(s);\n    return sdsHdrSize(s[-1])+alloc+1;\n}\n\n/* Return the pointer of the actual SDS allocation (normally SDS strings\n * are referenced by the start of the string buffer). */\nvoid *sdsAllocPtr(sds s) {\n    return (void*) (s-sdsHdrSize(s[-1]));\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, int incr) {\n    unsigned char flags = s[-1];\n    size_t len;\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            unsigned char *fp = ((unsigned char*)s)-1;\n            unsigned char oldlen = SDS_TYPE_5_LEN(flags);\n            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));\n            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);\n            len = oldlen+incr;\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        default: len = 0; /* Just to avoid compilation warnings. */\n    }\n    s[len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    size_t curlen = sdslen(s);\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    memcpy(s+curlen, t, len);\n    sdssetlen(s, curlen+len);\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    if (sdsalloc(s) < len) {\n        s = sdsMakeRoomFor(s,len-sdslen(s));\n        if (s == NULL) return NULL;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-termined string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the length of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[SDS_LLSTR_SIZE];\n    int len = sdsll2str(buf,value);\n\n    return sdsnewlen(buf,len);\n}\n\n/* Like sdscatprintf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char staticbuf[1024], *buf = staticbuf, *t;\n    size_t buflen = strlen(fmt)*2;\n\n    /* We try to start using a static buffer for speed.\n     * If not possible we revert to heap allocation. */\n    if (buflen > sizeof(staticbuf)) {\n        buf = s_malloc(buflen);\n        if (buf == NULL) return NULL;\n    } else {\n        buflen = sizeof(staticbuf);\n    }\n\n    /* Try with buffers two times bigger every time we fail to\n     * fit the string in the current buffer size. */\n    while(1) {\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        va_end(cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) s_free(buf);\n            buflen *= 2;\n            buf = s_malloc(buflen);\n            if (buf == NULL) return NULL;\n            continue;\n        }\n        break;\n    }\n\n    /* Finally concat the obtained string to the SDS string and return it. */\n    t = sdscat(s, buf);\n    if (buf != staticbuf) s_free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b).\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    const char *f = fmt;\n    int i;\n    va_list ap;\n\n    va_start(ap,fmt);\n    i = sdslen(s); /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        size_t l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sdsavail(s)==0) {\n            s = sdsMakeRoomFor(s,1);\n            if (s == NULL) goto fmt_error;\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sdsavail(s) < l) {\n                    s = sdsMakeRoomFor(s,l);\n                    if (s == NULL) goto fmt_error;\n                }\n                memcpy(s+i,str,l);\n                sdsinclen(s,l);\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        if (s == NULL) goto fmt_error;\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else\n                    unum = va_arg(ap,unsigned long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        if (s == NULL) goto fmt_error;\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sdsinclen(s,1);\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sdsinclen(s,1);\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n\nfmt_error:\n    va_end(ap);\n    return NULL;\n}\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"Aa. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"Hello World\".\n */\nsds sdstrim(sds s, const char *cset) {\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > sp && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (s != sp) memmove(s, sp, len);\n    s[len] = '\\0';\n    sdssetlen(s,len);\n    return s;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nvoid sdsrange(sds s, int start, int end) {\n    size_t newlen, len = sdslen(s);\n\n    if (len == 0) return;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (signed)len) {\n            newlen = 0;\n        } else if (end >= (signed)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(s, s+start, newlen);\n    s[newlen] = 0;\n    sdssetlen(s,newlen);\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = tolower(s[j]);\n}\n\n/* Apply toupper() to every character of the sds string 's'. */\nvoid sdstoupper(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     positive if s1 > s2.\n *     negative if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1-l2;\n    return cmp;\n}\n\n/* Split 's' with separator in 'sep'. An array\n * of sds strings is returned. *count will be set\n * by reference to the number of tokens returned.\n *\n * On out of memory, zero length string, zero length\n * separator, NULL is returned.\n *\n * Note that 'sep' is able to split a string using\n * a multi-character separator. For example\n * sdssplit(\"foo_-_bar\",\"_-_\"); will return two\n * elements \"foo\" and \"bar\".\n *\n * This version of the function is binary-safe but\n * requires length arguments. sdssplit() is just the\n * same function but for zero-terminated strings.\n */\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5, start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = s_malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = s_realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        s_free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    s_free(tokens);\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\nint is_hex_digit(char c) {\n    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||\n           (c >= 'A' && c <= 'F');\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             is_hex_digit(*(p+2)) &&\n                                             is_hex_digit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            {\n                char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));\n                if (new_vector == NULL) {\n                    s_free(vector);\n                    return NULL;\n                }\n\n                vector = new_vector;\n                vector[*argc] = current;\n                (*argc)++;\n                current = NULL;\n            }\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = s_malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    s_free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscat(join,sep);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n/* Wrappers to the allocators used by SDS. Note that SDS will actually\n * just use the macros defined into sdsalloc.h in order to avoid to pay\n * the overhead of function calls. Here we define these wrappers only for\n * the programs SDS is linked to, if they want to touch the SDS internals\n * even if they use a different allocator. */\nvoid *sds_malloc(size_t size) { return s_malloc(size); }\nvoid *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }\nvoid sds_free(void *ptr) { s_free(ptr); }\n\n#if defined(SDS_TEST_MAIN)\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.h\"\n\n#define UNUSED(x) (void)(x)\nint sdsTest(void) {\n    {\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"Hello %s World %I,%I--\", \"Hi!\", LLONG_MIN,LLONG_MAX);\n        test_cond(\"sdscatfmt() seems working in the base case\",\n            sdslen(x) == 60 &&\n            memcmp(x,\"--Hello Hi! World -9223372036854775808,\"\n                     \"9223372036854775807--\",60) == 0)\n        printf(\"[%s]\\n\",x);\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"%u,%U--\", UINT_MAX, ULLONG_MAX);\n        test_cond(\"sdscatfmt() seems working with unsigned numbers\",\n            sdslen(x) == 35 &&\n            memcmp(x,\"--4294967295,18446744073709551615--\",35) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" x\");\n        test_cond(\"sdstrim() works when all chars match\",\n            sdslen(x) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" \");\n        test_cond(\"sdstrim() works when a single char remains\",\n            sdslen(x) == 1 && x[0] == 'x')\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            unsigned int oldfree;\n            char *p;\n            int step = 10, j, i;\n\n            sdsfree(x);\n            sdsfree(y);\n            x = sdsnew(\"0\");\n            test_cond(\"sdsnew() free/len buffers\", sdslen(x) == 1 && sdsavail(x) == 0);\n\n            /* Run the test a few times in order to hit the first two\n             * SDS header types. */\n            for (i = 0; i < 10; i++) {\n                int oldlen = sdslen(x);\n                x = sdsMakeRoomFor(x,step);\n                int type = x[-1]&SDS_TYPE_MASK;\n\n                test_cond(\"sdsMakeRoomFor() len\", sdslen(x) == oldlen);\n                if (type != SDS_TYPE_5) {\n                    test_cond(\"sdsMakeRoomFor() free\", sdsavail(x) >= step);\n                    oldfree = sdsavail(x);\n                }\n                p = x+oldlen;\n                for (j = 0; j < step; j++) {\n                    p[j] = 'A'+j;\n                }\n                sdsIncrLen(x,step);\n            }\n            test_cond(\"sdsMakeRoomFor() content\",\n                memcmp(\"0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ\",x,101) == 0);\n            test_cond(\"sdsMakeRoomFor() final length\",sdslen(x)==101);\n\n            sdsfree(x);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n\n#ifdef SDS_TEST_MAIN\nint main(void) {\n    return sdsTest();\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/sds.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\n#ifdef _MSC_VER\n#define __attribute__(x)\n#endif\n\n#include <sys/types.h>\n#include <stdarg.h>\n#include <stdint.h>\n\ntypedef char *sds;\n\n/* Note: sdshdr5 is never used, we just access the flags byte directly.\n * However is here to document the layout of type 5 SDS strings. */\nstruct __attribute__ ((__packed__)) sdshdr5 {\n    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr8 {\n    uint8_t len; /* used */\n    uint8_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr16 {\n    uint16_t len; /* used */\n    uint16_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr32 {\n    uint32_t len; /* used */\n    uint32_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr64 {\n    uint64_t len; /* used */\n    uint64_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\n\n#define SDS_TYPE_5  0\n#define SDS_TYPE_8  1\n#define SDS_TYPE_16 2\n#define SDS_TYPE_32 3\n#define SDS_TYPE_64 4\n#define SDS_TYPE_MASK 7\n#define SDS_TYPE_BITS 3\n#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));\n#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))\n#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)\n\nstatic inline size_t sdslen(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->len;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->len;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->len;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->len;\n    }\n    return 0;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            return 0;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            return sh->alloc - sh->len;\n        }\n    }\n    return 0;\n}\n\nstatic inline void sdssetlen(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len = (uint8_t)newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len = (uint16_t)newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len = (uint32_t)newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len = (uint64_t)newlen;\n            break;\n    }\n}\n\nstatic inline void sdsinclen(sds s, size_t inc) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len += (uint8_t)inc;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len += (uint16_t)inc;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len += (uint32_t)inc;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len += (uint64_t)inc;\n            break;\n    }\n}\n\n/* sdsalloc() = sdsavail() + sdslen() */\nstatic inline size_t sdsalloc(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->alloc;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->alloc;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->alloc;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->alloc;\n    }\n    return 0;\n}\n\nstatic inline void sdssetalloc(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            /* Nothing to do, this type has no total allocation info. */\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->alloc = (uint8_t)newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->alloc = (uint16_t)newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->alloc = (uint32_t)newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->alloc = (uint64_t)newlen;\n            break;\n    }\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nsds sdstrim(sds s, const char *cset);\nvoid sdsrange(sds s, int start, int end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, int incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\nvoid *sdsAllocPtr(sds s);\n\n/* Export the allocator used by SDS to the program using SDS.\n * Sometimes the program SDS is linked to, may use a different set of\n * allocators, but may want to allocate or free things that SDS will\n * respectively free or allocate. */\nvoid *sds_malloc(size_t size);\nvoid *sds_realloc(void *ptr, size_t size);\nvoid sds_free(void *ptr);\n\n#ifdef REDIS_TEST\nint sdsTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/sdsalloc.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* SDS allocator selection.\n *\n * This file is used in order to change the SDS allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#define s_malloc malloc\n#define s_realloc realloc\n#define s_free free\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/sockcompat.c",
    "content": "/*\n * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>\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#define REDIS_SOCKCOMPAT_IMPLEMENTATION\n#include \"sockcompat.h\"\n\n#ifdef _WIN32\nstatic int _wsaErrorToErrno(int err) {\n    switch (err) {\n        case WSAEWOULDBLOCK:\n            return EWOULDBLOCK;\n        case WSAEINPROGRESS:\n            return EINPROGRESS;\n        case WSAEALREADY:\n            return EALREADY;\n        case WSAENOTSOCK:\n            return ENOTSOCK;\n        case WSAEDESTADDRREQ:\n            return EDESTADDRREQ;\n        case WSAEMSGSIZE:\n            return EMSGSIZE;\n        case WSAEPROTOTYPE:\n            return EPROTOTYPE;\n        case WSAENOPROTOOPT:\n            return ENOPROTOOPT;\n        case WSAEPROTONOSUPPORT:\n            return EPROTONOSUPPORT;\n        case WSAEOPNOTSUPP:\n            return EOPNOTSUPP;\n        case WSAEAFNOSUPPORT:\n            return EAFNOSUPPORT;\n        case WSAEADDRINUSE:\n            return EADDRINUSE;\n        case WSAEADDRNOTAVAIL:\n            return EADDRNOTAVAIL;\n        case WSAENETDOWN:\n            return ENETDOWN;\n        case WSAENETUNREACH:\n            return ENETUNREACH;\n        case WSAENETRESET:\n            return ENETRESET;\n        case WSAECONNABORTED:\n            return ECONNABORTED;\n        case WSAECONNRESET:\n            return ECONNRESET;\n        case WSAENOBUFS:\n            return ENOBUFS;\n        case WSAEISCONN:\n            return EISCONN;\n        case WSAENOTCONN:\n            return ENOTCONN;\n        case WSAETIMEDOUT:\n            return ETIMEDOUT;\n        case WSAECONNREFUSED:\n            return ECONNREFUSED;\n        case WSAELOOP:\n            return ELOOP;\n        case WSAENAMETOOLONG:\n            return ENAMETOOLONG;\n        case WSAEHOSTUNREACH:\n            return EHOSTUNREACH;\n        case WSAENOTEMPTY:\n            return ENOTEMPTY;\n        default:\n            /* We just return a generic I/O error if we could not find a relevant error. */\n            return EIO;\n    }\n}\n\nstatic void _updateErrno(int success) {\n    errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError());\n}\n\nstatic int _initWinsock() {\n    static int s_initialized = 0;\n    if (!s_initialized) {\n        static WSADATA wsadata;\n        int err = WSAStartup(MAKEWORD(2,2), &wsadata);\n        if (err != 0) {\n            errno = _wsaErrorToErrno(err);\n            return 0;\n        }\n        s_initialized = 1;\n    }\n    return 1;\n}\n\nint win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {\n    /* Note: This function is likely to be called before other functions, so run init here. */\n    if (!_initWinsock()) {\n        return EAI_FAIL;\n    }\n\n    switch (getaddrinfo(node, service, hints, res)) {\n        case 0:                     return 0;\n        case WSATRY_AGAIN:          return EAI_AGAIN;\n        case WSAEINVAL:             return EAI_BADFLAGS;\n        case WSAEAFNOSUPPORT:       return EAI_FAMILY;\n        case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY;\n        case WSAHOST_NOT_FOUND:     return EAI_NONAME;\n        case WSATYPE_NOT_FOUND:     return EAI_SERVICE;\n        case WSAESOCKTNOSUPPORT:    return EAI_SOCKTYPE;\n        default:                    return EAI_FAIL;     /* Including WSANO_RECOVERY */\n    }\n}\n\nconst char *win32_gai_strerror(int errcode) {\n    switch (errcode) {\n        case 0:            errcode = 0;                     break;\n        case EAI_AGAIN:    errcode = WSATRY_AGAIN;          break;\n        case EAI_BADFLAGS: errcode = WSAEINVAL;             break;\n        case EAI_FAMILY:   errcode = WSAEAFNOSUPPORT;       break;\n        case EAI_MEMORY:   errcode = WSA_NOT_ENOUGH_MEMORY; break;\n        case EAI_NONAME:   errcode = WSAHOST_NOT_FOUND;     break;\n        case EAI_SERVICE:  errcode = WSATYPE_NOT_FOUND;     break;\n        case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT;    break;\n        default:           errcode = WSANO_RECOVERY;        break; /* Including EAI_FAIL */\n    }\n    return gai_strerror(errcode);\n}\n\nvoid win32_freeaddrinfo(struct addrinfo *res) {\n    freeaddrinfo(res);\n}\n\nSOCKET win32_socket(int domain, int type, int protocol) {\n    SOCKET s;\n\n    /* Note: This function is likely to be called before other functions, so run init here. */\n    if (!_initWinsock()) {\n        return INVALID_SOCKET;\n    }\n\n    _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET);\n    return s;\n}\n\nint win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) {\n    int ret = ioctlsocket(fd, (long)request, argp);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {\n    int ret = bind(sockfd, addr, addrlen);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {\n    int ret = connect(sockfd, addr, addrlen);\n    _updateErrno(ret != SOCKET_ERROR);\n\n    /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as\n     * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX\n     * logic consistent. */\n    if (errno == EWOULDBLOCK) {\n        errno = EINPROGRESS;\n    }\n\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) {\n    int ret = 0;\n    if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {\n        if (*optlen >= sizeof (struct timeval)) {\n            struct timeval *tv = optval;\n            DWORD timeout = 0;\n            socklen_t dwlen = 0;\n            ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen);\n            tv->tv_sec = timeout / 1000;\n            tv->tv_usec = (timeout * 1000) % 1000000;\n        } else {\n            ret = WSAEFAULT;\n        }\n        *optlen = sizeof (struct timeval);\n    } else {\n        ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);\n    }\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {\n    int ret = 0;\n    if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {\n        struct timeval *tv = optval;\n        DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;\n        ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));\n    } else {\n        ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen);\n    }\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_close(SOCKET fd) {\n    int ret = closesocket(fd);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) {\n    int ret = recv(sockfd, (char*)buf, (int)len, flags);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) {\n    int ret = send(sockfd, (const char*)buf, (int)len, flags);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n\nint win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {\n    int ret = WSAPoll(fds, nfds, timeout);\n    _updateErrno(ret != SOCKET_ERROR);\n    return ret != SOCKET_ERROR ? ret : -1;\n}\n#endif /* _WIN32 */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/sockcompat.h",
    "content": "/*\n * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>\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 __SOCKCOMPAT_H\n#define __SOCKCOMPAT_H\n\n#ifndef _WIN32\n/* For POSIX systems we use the standard BSD socket API. */\n#include <unistd.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 <netdb.h>\n#include <poll.h>\n#else\n/* For Windows we use winsock. */\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#include <stddef.h>\n\n#ifdef _MSC_VER\ntypedef signed long ssize_t;\n#endif\n\n/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */\nint win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);\nconst char *win32_gai_strerror(int errcode);\nvoid win32_freeaddrinfo(struct addrinfo *res);\nSOCKET win32_socket(int domain, int type, int protocol);\nint win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);\nint win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);\nint win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);\nint win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);\nint win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);\nint win32_close(SOCKET fd);\nssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);\nssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);\ntypedef ULONG nfds_t;\nint win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);\n\n#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION\n#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)\n#undef gai_strerror\n#define gai_strerror(errcode) win32_gai_strerror(errcode)\n#define freeaddrinfo(res) win32_freeaddrinfo(res)\n#define socket(domain, type, protocol) win32_socket(domain, type, protocol)\n#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)\n#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)\n#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)\n#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)\n#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)\n#define close(fd) win32_close(fd)\n#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)\n#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)\n#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)\n#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */\n#endif /* _WIN32 */\n\n#endif /* __SOCKCOMPAT_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/ssl.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 * Copyright (c) 2019, Redis Labs\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 \"hiredis.h\"\n#include \"async.h\"\n\n#include <assert.h>\n#include <pthread.h>\n#include <errno.h>\n#include <string.h>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#include \"async_private.h\"\n\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\n/* The SSL context is attached to SSL/TLS connections as a privdata. */\ntypedef struct redisSSLContext {\n    /**\n     * OpenSSL SSL_CTX; It is optional and will not be set when using\n     * user-supplied SSL.\n     */\n    SSL_CTX *ssl_ctx;\n\n    /**\n     * OpenSSL SSL object.\n     */\n    SSL *ssl;\n\n    /**\n     * SSL_write() requires to be called again with the same arguments it was\n     * previously called with in the event of an SSL_read/SSL_write situation\n     */\n    size_t lastLen;\n\n    /** Whether the SSL layer requires read (possibly before a write) */\n    int wantRead;\n\n    /**\n     * Whether a write was requested prior to a read. If set, the write()\n     * should resume whenever a read takes place, if possible\n     */\n    int pendingWrite;\n} redisSSLContext;\n\n/* Forward declaration */\nredisContextFuncs redisContextSSLFuncs;\n\n#ifdef HIREDIS_SSL_TRACE\n/**\n * Callback used for debugging\n */\nstatic void sslLogCallback(const SSL *ssl, int where, int ret) {\n    const char *retstr = \"\";\n    int should_log = 1;\n    /* Ignore low-level SSL stuff */\n\n    if (where & SSL_CB_ALERT) {\n        should_log = 1;\n    }\n    if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {\n        should_log = 1;\n    }\n    if ((where & SSL_CB_EXIT) && ret == 0) {\n        should_log = 1;\n    }\n\n    if (!should_log) {\n        return;\n    }\n\n    retstr = SSL_alert_type_string(ret);\n    printf(\"ST(0x%x). %s. R(0x%x)%s\\n\", where, SSL_state_string_long(ssl), ret, retstr);\n\n    if (where == SSL_CB_HANDSHAKE_DONE) {\n        printf(\"Using SSL version %s. Cipher=%s\\n\", SSL_get_version(ssl), SSL_get_cipher_name(ssl));\n    }\n}\n#endif\n\n/**\n * OpenSSL global initialization and locking handling callbacks.\n * Note that this is only required for OpenSSL < 1.1.0.\n */\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#define HIREDIS_USE_CRYPTO_LOCKS\n#endif\n\n#ifdef HIREDIS_USE_CRYPTO_LOCKS\ntypedef pthread_mutex_t sslLockType;\nstatic void sslLockInit(sslLockType *l) {\n    pthread_mutex_init(l, NULL);\n}\nstatic void sslLockAcquire(sslLockType *l) {\n    pthread_mutex_lock(l);\n}\nstatic void sslLockRelease(sslLockType *l) {\n    pthread_mutex_unlock(l);\n}\nstatic pthread_mutex_t *ossl_locks;\n\nstatic void opensslDoLock(int mode, int lkid, const char *f, int line) {\n    sslLockType *l = ossl_locks + lkid;\n\n    if (mode & CRYPTO_LOCK) {\n        sslLockAcquire(l);\n    } else {\n        sslLockRelease(l);\n    }\n\n    (void)f;\n    (void)line;\n}\n\nstatic void initOpensslLocks(void) {\n    unsigned ii, nlocks;\n    if (CRYPTO_get_locking_callback() != NULL) {\n        /* Someone already set the callback before us. Don't destroy it! */\n        return;\n    }\n    nlocks = CRYPTO_num_locks();\n    ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);\n    for (ii = 0; ii < nlocks; ii++) {\n        sslLockInit(ossl_locks + ii);\n    }\n    CRYPTO_set_locking_callback(opensslDoLock);\n}\n#endif /* HIREDIS_USE_CRYPTO_LOCKS */\n\n/**\n * SSL Connection initialization.\n */\n\nstatic int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {\n    if (c->privdata) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"redisContext was already associated\");\n        return REDIS_ERR;\n    }\n    c->privdata = calloc(1, sizeof(redisSSLContext));\n\n    c->funcs = &redisContextSSLFuncs;\n    redisSSLContext *rssl = c->privdata;\n\n    rssl->ssl_ctx = ssl_ctx;\n    rssl->ssl = ssl;\n\n    SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n    SSL_set_fd(rssl->ssl, c->fd);\n    SSL_set_connect_state(rssl->ssl);\n\n    ERR_clear_error();\n    int rv = SSL_connect(rssl->ssl);\n    if (rv == 1) {\n        return REDIS_OK;\n    }\n\n    rv = SSL_get_error(rssl->ssl, rv);\n    if (((c->flags & REDIS_BLOCK) == 0) &&\n        (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {\n        return REDIS_OK;\n    }\n\n    if (c->err == 0) {\n        char err[512];\n        if (rv == SSL_ERROR_SYSCALL)\n            snprintf(err,sizeof(err)-1,\"SSL_connect failed: %s\",strerror(errno));\n        else {\n            unsigned long e = ERR_peek_last_error();\n            snprintf(err,sizeof(err)-1,\"SSL_connect failed: %s\",\n                    ERR_reason_error_string(e));\n        }\n        __redisSetError(c, REDIS_ERR_IO, err);\n    }\n    return REDIS_ERR;\n}\n\nint redisInitiateSSL(redisContext *c, SSL *ssl) {\n    return redisSSLConnect(c, NULL, ssl);\n}\n\nint redisSecureConnection(redisContext *c, const char *capath,\n                          const char *certpath, const char *keypath, const char *servername) {\n\n    SSL_CTX *ssl_ctx = NULL;\n    SSL *ssl = NULL;\n\n    /* Initialize global OpenSSL stuff */\n    static int isInit = 0;\n    if (!isInit) {\n        isInit = 1;\n        SSL_library_init();\n#ifdef HIREDIS_USE_CRYPTO_LOCKS\n        initOpensslLocks();\n#endif\n    }\n\n    ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n    if (!ssl_ctx) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"Failed to create SSL_CTX\");\n        goto error;\n    }\n\n#ifdef HIREDIS_SSL_TRACE\n    SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);\n#endif\n    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);\n    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);\n    if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"certpath and keypath must be specified together\");\n        goto error;\n    }\n\n    if (capath) {\n        if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Invalid CA certificate\");\n            goto error;\n        }\n    }\n    if (certpath) {\n        if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Invalid client certificate\");\n            goto error;\n        }\n        if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Invalid client key\");\n            goto error;\n        }\n    }\n\n    ssl = SSL_new(ssl_ctx);\n    if (!ssl) {\n        __redisSetError(c, REDIS_ERR_OTHER, \"Couldn't create new SSL instance\");\n        goto error;\n    }\n    if (servername) {\n        if (!SSL_set_tlsext_host_name(ssl, servername)) {\n            __redisSetError(c, REDIS_ERR_OTHER, \"Couldn't set server name indication\");\n            goto error;\n        }\n    }\n\n    return redisSSLConnect(c, ssl_ctx, ssl);\n\nerror:\n    if (ssl) SSL_free(ssl);\n    if (ssl_ctx) SSL_CTX_free(ssl_ctx);\n    return REDIS_ERR;\n}\n\nstatic int maybeCheckWant(redisSSLContext *rssl, int rv) {\n    /**\n     * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set\n     * and true is returned. False is returned otherwise\n     */\n    if (rv == SSL_ERROR_WANT_READ) {\n        rssl->wantRead = 1;\n        return 1;\n    } else if (rv == SSL_ERROR_WANT_WRITE) {\n        rssl->pendingWrite = 1;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/**\n * Implementation of redisContextFuncs for SSL connections.\n */\n\nstatic void redisSSLFreeContext(void *privdata){\n    redisSSLContext *rsc = privdata;\n\n    if (!rsc) return;\n    if (rsc->ssl) {\n        SSL_free(rsc->ssl);\n        rsc->ssl = NULL;\n    }\n    if (rsc->ssl_ctx) {\n        SSL_CTX_free(rsc->ssl_ctx);\n        rsc->ssl_ctx = NULL;\n    }\n    free(rsc);\n}\n\nstatic int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {\n    redisSSLContext *rssl = c->privdata;\n\n    int nread = SSL_read(rssl->ssl, buf, bufcap);\n    if (nread > 0) {\n        return nread;\n    } else if (nread == 0) {\n        __redisSetError(c, REDIS_ERR_EOF, \"Server closed the connection\");\n        return -1;\n    } else {\n        int err = SSL_get_error(rssl->ssl, nread);\n        if (c->flags & REDIS_BLOCK) {\n            /**\n             * In blocking mode, we should never end up in a situation where\n             * we get an error without it being an actual error, except\n             * in the case of EINTR, which can be spuriously received from\n             * debuggers or whatever.\n             */\n            if (errno == EINTR) {\n                return 0;\n            } else {\n                const char *msg = NULL;\n                if (errno == EAGAIN) {\n                    msg = \"Resource temporarily unavailable\";\n                }\n                __redisSetError(c, REDIS_ERR_IO, msg);\n                return -1;\n            }\n        }\n\n        /**\n         * We can very well get an EWOULDBLOCK/EAGAIN, however\n         */\n        if (maybeCheckWant(rssl, err)) {\n            return 0;\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n}\n\nstatic int redisSSLWrite(redisContext *c) {\n    redisSSLContext *rssl = c->privdata;\n\n    size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);\n    int rv = SSL_write(rssl->ssl, c->obuf, len);\n\n    if (rv > 0) {\n        rssl->lastLen = 0;\n    } else if (rv < 0) {\n        rssl->lastLen = len;\n\n        int err = SSL_get_error(rssl->ssl, rv);\n        if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {\n            return 0;\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n    return rv;\n}\n\nstatic void redisSSLAsyncRead(redisAsyncContext *ac) {\n    int rv;\n    redisSSLContext *rssl = ac->c.privdata;\n    redisContext *c = &ac->c;\n\n    rssl->wantRead = 0;\n\n    if (rssl->pendingWrite) {\n        int done;\n\n        /* This is probably just a write event */\n        rssl->pendingWrite = 0;\n        rv = redisBufferWrite(c, &done);\n        if (rv == REDIS_ERR) {\n            __redisAsyncDisconnect(ac);\n            return;\n        } else if (!done) {\n            _EL_ADD_WRITE(ac);\n        }\n    }\n\n    rv = redisBufferRead(c);\n    if (rv == REDIS_ERR) {\n        __redisAsyncDisconnect(ac);\n    } else {\n        _EL_ADD_READ(ac);\n        redisProcessCallbacks(ac);\n    }\n}\n\nstatic void redisSSLAsyncWrite(redisAsyncContext *ac) {\n    int rv, done = 0;\n    redisSSLContext *rssl = ac->c.privdata;\n    redisContext *c = &ac->c;\n\n    rssl->pendingWrite = 0;\n    rv = redisBufferWrite(c, &done);\n    if (rv == REDIS_ERR) {\n        __redisAsyncDisconnect(ac);\n        return;\n    }\n\n    if (!done) {\n        if (rssl->wantRead) {\n            /* Need to read-before-write */\n            rssl->pendingWrite = 1;\n            _EL_DEL_WRITE(ac);\n        } else {\n            /* No extra reads needed, just need to write more */\n            _EL_ADD_WRITE(ac);\n        }\n    } else {\n        /* Already done! */\n        _EL_DEL_WRITE(ac);\n    }\n\n    /* Always reschedule a read */\n    _EL_ADD_READ(ac);\n}\n\nredisContextFuncs redisContextSSLFuncs = {\n    .free_privdata = redisSSLFreeContext,\n    .async_read = redisSSLAsyncRead,\n    .async_write = redisSSLAsyncWrite,\n    .read = redisSSLRead,\n    .write = redisSSLWrite\n};\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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/socket.h>\n#include <sys/time.h>\n#include <netdb.h>\n#include <assert.h>\n#include <unistd.h>\n#include <signal.h>\n#include <errno.h>\n#include <limits.h>\n\n#include \"hiredis.h\"\n#ifdef HIREDIS_TEST_SSL\n#include \"hiredis_ssl.h\"\n#endif\n#include \"net.h\"\n\nenum connection_type {\n    CONN_TCP,\n    CONN_UNIX,\n    CONN_FD,\n    CONN_SSL\n};\n\nstruct config {\n    enum connection_type type;\n\n    struct {\n        const char *host;\n        int port;\n        struct timeval timeout;\n    } tcp;\n\n    struct {\n        const char *path;\n    } unix_sock;\n\n    struct {\n        const char *host;\n        int port;\n        const char *ca_cert;\n        const char *cert;\n        const char *key;\n    } ssl;\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\n/* The assert() calls below have side effects, so we need assert()\n * even if we are compiling without asserts (-DNDEBUG). */\n#ifdef NDEBUG\n#undef assert\n#define assert(e) (void)(e)\n#endif\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 int disconnect(redisContext *c, int keep_fd) {\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, but keep the fd if requested. */\n    if (keep_fd)\n        return redisFreeKeepFd(c);\n    redisFree(c);\n    return -1;\n}\n\nstatic void do_ssl_handshake(redisContext *c, struct config config) {\n#ifdef HIREDIS_TEST_SSL\n    redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);\n    if (c->err) {\n        printf(\"SSL error: %s\\n\", c->errstr);\n        redisFree(c);\n        exit(1);\n    }\n#else\n    (void) c;\n    (void) config;\n#endif\n}\n\nstatic redisContext *do_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_SSL) {\n        c = redisConnect(config.ssl.host, config.ssl.port);\n    } else if (config.type == CONN_UNIX) {\n        c = redisConnectUnix(config.unix_sock.path);\n    } else if (config.type == CONN_FD) {\n        /* Create a dummy connection just to get an fd to inherit */\n        redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);\n        if (dummy_ctx) {\n            int fd = disconnect(dummy_ctx, 1);\n            printf(\"Connecting to inherited fd %d\\n\", fd);\n            c = redisConnectFd(fd);\n        }\n    } else {\n        assert(NULL);\n    }\n\n    if (c == NULL) {\n        printf(\"Connection error: can't allocate redis context\\n\");\n        exit(1);\n    } else if (c->err) {\n        printf(\"Connection error: %s\\n\", c->errstr);\n        redisFree(c);\n        exit(1);\n    }\n\n    if (config.type == CONN_SSL) {\n        do_ssl_handshake(c, config);\n    }\n\n    return select_database(c);\n}\n\nstatic void do_reconnect(redisContext *c, struct config config) {\n    redisReconnect(c);\n\n    if (config.type == CONN_SSL) {\n        do_ssl_handshake(c, config);\n    }\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\",(size_t)3,\"b\\0r\",(size_t)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\",(size_t)3,\"\",(size_t)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\",(size_t)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    sds sds_cmd;\n\n    sds_cmd = sdsempty();\n    test(\"Format command into sds by passing argc/argv without lengths: \");\n    len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);\n    test_cond(strncmp(sds_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    sdsfree(sds_cmd);\n\n    sds_cmd = sdsempty();\n    test(\"Format command into sds by passing argc/argv with lengths: \");\n    len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);\n    test_cond(strncmp(sds_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    sdsfree(sds_cmd);\n}\n\nstatic void test_append_formatted_commands(struct config config) {\n    redisContext *c;\n    redisReply *reply;\n    char *cmd;\n    int len;\n\n    c = do_connect(config);\n\n    test(\"Append format command: \");\n\n    len = redisFormatCommand(&cmd, \"SET foo bar\");\n\n    test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK);\n\n    assert(redisGetReply(c, (void*)&reply) == REDIS_OK);\n\n    free(cmd);\n    freeReplyObject(reply);\n\n    disconnect(c, 0);\n}\n\nstatic void test_reply_reader(void) {\n    redisReader *reader;\n    void *reply;\n    int ret;\n    int i;\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 > 7: \");\n    reader = redisReaderCreate();\n\n    for (i = 0; i < 9; i++) {\n        redisReaderFeed(reader,(char*)\"*1\\r\\n\",4);\n    }\n\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(\"Correctly parses LLONG_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":9223372036854775807\\r\\n\",22);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK &&\n            ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&\n            ((redisReply*)reply)->integer == LLONG_MAX);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when > LLONG_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":9223372036854775808\\r\\n\",22);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Bad integer value\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Correctly parses LLONG_MIN: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":-9223372036854775808\\r\\n\",23);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_OK &&\n            ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&\n            ((redisReply*)reply)->integer == LLONG_MIN);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when < LLONG_MIN: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \":-9223372036854775809\\r\\n\",23);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Bad integer value\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when array < -1: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"*-2\\r\\n+asdf\\r\\n\",12);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Multi-bulk length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when bulk < -1: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"$-2\\r\\nasdf\\r\\n\",11);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n              strcasecmp(reader->errstr,\"Bulk string length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n#if LLONG_MAX > SIZE_MAX\n    test(\"Set error when array > SIZE_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"*9223372036854775807\\r\\n+asdf\\r\\n\",29);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n            strcasecmp(reader->errstr,\"Multi-bulk length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n\n    test(\"Set error when bulk > SIZE_MAX: \");\n    reader = redisReaderCreate();\n    redisReaderFeed(reader, \"$9223372036854775807\\r\\nasdf\\r\\n\",28);\n    ret = redisReaderGetReply(reader,&reply);\n    test_cond(ret == REDIS_ERR &&\n            strcasecmp(reader->errstr,\"Bulk string length out of range\") == 0);\n    freeReplyObject(reply);\n    redisReaderFree(reader);\n#endif\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_free_null(void) {\n    void *redisCtx = NULL;\n    void *reply = NULL;\n\n    test(\"Don't fail when redisFree is passed a NULL value: \");\n    redisFree(redisCtx);\n    test_cond(redisCtx == NULL);\n\n    test(\"Don't fail when freeReplyObject is passed a NULL value: \");\n    freeReplyObject(reply);\n    test_cond(reply == NULL);\n}\n\n#define HIREDIS_BAD_DOMAIN \"idontexist-noreally.com\"\nstatic void test_blocking_connection_errors(void) {\n    redisContext *c;\n    struct addrinfo hints = {.ai_family = AF_INET};\n    struct addrinfo *ai_tmp = NULL;\n\n    int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, \"6379\", &hints, &ai_tmp);\n    if (rv != 0) {\n        // Address does *not* exist\n        test(\"Returns error when host cannot be resolved: \");\n        // First see if this domain name *actually* resolves to NXDOMAIN\n        c = redisConnect(HIREDIS_BAD_DOMAIN, 6379);\n        test_cond(\n            c->err == REDIS_ERR_OTHER &&\n            (strcmp(c->errstr, \"Name or service not known\") == 0 ||\n             strcmp(c->errstr, \"Can't resolve: \" HIREDIS_BAD_DOMAIN) == 0 ||\n             strcmp(c->errstr, \"Name does not resolve\") == 0 ||\n             strcmp(c->errstr,\n                    \"nodename nor servname provided, or not known\") == 0 ||\n             strcmp(c->errstr, \"No address associated with hostname\") == 0 ||\n             strcmp(c->errstr, \"Temporary failure in name resolution\") == 0 ||\n             strcmp(c->errstr,\n                    \"hostname nor servname provided, or not known\") == 0 ||\n             strcmp(c->errstr, \"no address associated with name\") == 0));\n        redisFree(c);\n    } else {\n        printf(\"Skipping NXDOMAIN test. Found evil ISP!\\n\");\n        freeaddrinfo(ai_tmp);\n    }\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_sock 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 = do_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\",(size_t)3,\"hello\\x00world\",(size_t)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, 0);\n}\n\nstatic void test_blocking_connection_timeouts(struct config config) {\n    redisContext *c;\n    redisReply *reply;\n    ssize_t s;\n    const char *cmd = \"DEBUG SLEEP 3\\r\\n\";\n    struct timeval tv;\n\n    c = do_connect(config);\n    test(\"Successfully completes a command when the timeout is not exceeded: \");\n    reply = redisCommand(c,\"SET foo fast\");\n    freeReplyObject(reply);\n    tv.tv_sec = 0;\n    tv.tv_usec = 10000;\n    redisSetTimeout(c, tv);\n    reply = redisCommand(c, \"GET foo\");\n    test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, \"fast\", 4) == 0);\n    freeReplyObject(reply);\n    disconnect(c, 0);\n\n    c = do_connect(config);\n    test(\"Does not return a reply when the command times out: \");\n    redisAppendFormattedCommand(c, cmd, strlen(cmd));\n    s = c->funcs->write(c);\n    tv.tv_sec = 0;\n    tv.tv_usec = 10000;\n    redisSetTimeout(c, tv);\n    reply = redisCommand(c, \"GET foo\");\n    test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, \"Resource temporarily unavailable\") == 0);\n    freeReplyObject(reply);\n\n    test(\"Reconnect properly reconnects after a timeout: \");\n    do_reconnect(c, config);\n    reply = redisCommand(c, \"PING\");\n    test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, \"PONG\") == 0);\n    freeReplyObject(reply);\n\n    test(\"Reconnect properly uses owned parameters: \");\n    config.tcp.host = \"foo\";\n    config.unix_sock.path = \"foo\";\n    do_reconnect(c, config);\n    reply = redisCommand(c, \"PING\");\n    test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, \"PONG\") == 0);\n    freeReplyObject(reply);\n\n    disconnect(c, 0);\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 = do_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 || (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 = do_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_invalid_timeout_errors(struct config config) {\n    redisContext *c;\n\n    test(\"Set error when an invalid timeout usec value is given to redisConnectWithTimeout: \");\n\n    config.tcp.timeout.tv_sec = 0;\n    config.tcp.timeout.tv_usec = 10000001;\n\n    c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);\n\n    test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, \"Invalid timeout specified\") == 0);\n    redisFree(c);\n\n    test(\"Set error when an invalid timeout sec value is given to redisConnectWithTimeout: \");\n\n    config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;\n    config.tcp.timeout.tv_usec = 0;\n\n    c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);\n\n    test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, \"Invalid timeout specified\") == 0);\n    redisFree(c);\n}\n\nstatic void test_throughput(struct config config) {\n    redisContext *c = do_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    replies = malloc(sizeof(redisReply*)*num);\n    t1 = usec();\n    for (i = 0; i < num; i++) {\n        replies[i] = redisCommand(c, \"INCRBY incrkey %d\", 1000000);\n        assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx INCRBY: %.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    replies = malloc(sizeof(redisReply*)*num);\n    for (i = 0; i < num; i++)\n        redisAppendCommand(c,\"INCRBY incrkey %d\", 1000000);\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_INTEGER);\n    }\n    t2 = usec();\n    for (i = 0; i < num; i++) freeReplyObject(replies[i]);\n    free(replies);\n    printf(\"\\t(%dx INCRBY (pipelined): %.3fs)\\n\", num, (t2-t1)/1000000.0);\n\n    disconnect(c, 0);\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_sock = {\n            .path = \"/tmp/redis.sock\"\n        }\n    };\n    int throughput = 1;\n    int test_inherit_fd = 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_sock.path = argv[0];\n        } else if (argc >= 1 && !strcmp(argv[0],\"--skip-throughput\")) {\n            throughput = 0;\n        } else if (argc >= 1 && !strcmp(argv[0],\"--skip-inherit-fd\")) {\n            test_inherit_fd = 0;\n#ifdef HIREDIS_TEST_SSL\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-port\")) {\n            argv++; argc--;\n            cfg.ssl.port = atoi(argv[0]);\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-host\")) {\n            argv++; argc--;\n            cfg.ssl.host = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-ca-cert\")) {\n            argv++; argc--;\n            cfg.ssl.ca_cert  = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-cert\")) {\n            argv++; argc--;\n            cfg.ssl.cert = argv[0];\n        } else if (argc >= 2 && !strcmp(argv[0],\"--ssl-key\")) {\n            argv++; argc--;\n            cfg.ssl.key = argv[0];\n#endif\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    test_free_null();\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_connection_timeouts(cfg);\n    test_blocking_io_errors(cfg);\n    test_invalid_timeout_errors(cfg);\n    test_append_formatted_commands(cfg);\n    if (throughput) test_throughput(cfg);\n\n    printf(\"\\nTesting against Unix socket connection (%s):\\n\", cfg.unix_sock.path);\n    cfg.type = CONN_UNIX;\n    test_blocking_connection(cfg);\n    test_blocking_connection_timeouts(cfg);\n    test_blocking_io_errors(cfg);\n    if (throughput) test_throughput(cfg);\n\n#ifdef HIREDIS_TEST_SSL\n    if (cfg.ssl.port && cfg.ssl.host) {\n        printf(\"\\nTesting against SSL connection (%s:%d):\\n\", cfg.ssl.host, cfg.ssl.port);\n        cfg.type = CONN_SSL;\n\n        test_blocking_connection(cfg);\n        test_blocking_connection_timeouts(cfg);\n        test_blocking_io_errors(cfg);\n        test_invalid_timeout_errors(cfg);\n        test_append_formatted_commands(cfg);\n        if (throughput) test_throughput(cfg);\n    }\n#endif\n\n    if (test_inherit_fd) {\n        printf(\"\\nTesting against inherited fd (%s):\\n\", cfg.unix_sock.path);\n        cfg.type = CONN_FD;\n        test_blocking_connection(cfg);\n    }\n\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": "Chapter07/redis-6.0.3/deps/hiredis/test.sh",
    "content": "#!/bin/sh -ue\n\nREDIS_SERVER=${REDIS_SERVER:-redis-server}\nREDIS_PORT=${REDIS_PORT:-56379}\nREDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}\nTEST_SSL=${TEST_SSL:-0}\nSSL_TEST_ARGS=\n\ntmpdir=$(mktemp -d)\nPID_FILE=${tmpdir}/hiredis-test-redis.pid\nSOCK_FILE=${tmpdir}/hiredis-test-redis.sock\n\nif [ \"$TEST_SSL\" = \"1\" ]; then\n    SSL_CA_CERT=${tmpdir}/ca.crt\n    SSL_CA_KEY=${tmpdir}/ca.key\n    SSL_CERT=${tmpdir}/redis.crt\n    SSL_KEY=${tmpdir}/redis.key\n\n    openssl genrsa -out ${tmpdir}/ca.key 4096\n    openssl req \\\n        -x509 -new -nodes -sha256 \\\n        -key ${SSL_CA_KEY} \\\n        -days 3650 \\\n        -subj '/CN=Hiredis Test CA' \\\n        -out ${SSL_CA_CERT}\n    openssl genrsa -out ${SSL_KEY} 2048\n    openssl req \\\n        -new -sha256 \\\n        -key ${SSL_KEY} \\\n        -subj '/CN=Hiredis Test Cert' | \\\n        openssl x509 \\\n            -req -sha256 \\\n            -CA ${SSL_CA_CERT} \\\n            -CAkey ${SSL_CA_KEY} \\\n            -CAserial ${tmpdir}/ca.txt \\\n            -CAcreateserial \\\n            -days 365 \\\n            -out ${SSL_CERT}\n\n    SSL_TEST_ARGS=\"--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}\"\nfi\n\ncleanup() {\n  set +e\n  kill $(cat ${PID_FILE})\n  rm -rf ${tmpdir}\n}\ntrap cleanup INT TERM EXIT\n\ncat > ${tmpdir}/redis.conf <<EOF\ndaemonize yes\npidfile ${PID_FILE}\nport ${REDIS_PORT}\nbind 127.0.0.1\nunixsocket ${SOCK_FILE}\nEOF\n\nif [ \"$TEST_SSL\" = \"1\" ]; then\n    cat >> ${tmpdir}/redis.conf <<EOF\ntls-port ${REDIS_SSL_PORT}\ntls-ca-cert-file ${SSL_CA_CERT}\ntls-cert-file ${SSL_CERT}\ntls-key-file ${SSL_KEY}\nEOF\nfi\n\ncat ${tmpdir}/redis.conf\n${REDIS_SERVER} ${tmpdir}/redis.conf\n\n${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/hiredis/win32.h",
    "content": "#ifndef _WIN32_HELPER_INCLUDE\n#define _WIN32_HELPER_INCLUDE\n#ifdef _MSC_VER\n\n#include <winsock2.h> /* for struct timeval */\n\n#ifndef inline\n#define inline __inline\n#endif\n\n#ifndef strcasecmp\n#define strcasecmp stricmp\n#endif\n\n#ifndef strncasecmp\n#define strncasecmp strnicmp\n#endif\n\n#ifndef va_copy\n#define va_copy(d,s) ((d) = (s))\n#endif\n\n#ifndef snprintf\n#define snprintf c99_snprintf\n\n__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)\n{\n    int count = -1;\n\n    if (size != 0)\n        count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);\n    if (count == -1)\n        count = _vscprintf(format, ap);\n\n    return count;\n}\n\n__inline int c99_snprintf(char* str, size_t size, const char* format, ...)\n{\n    int count;\n    va_list ap;\n\n    va_start(ap, format);\n    count = c99_vsnprintf(str, size, format, ap);\n    va_end(ap);\n\n    return count;\n}\n#endif\n#endif /* _MSC_VER */\n\n#ifdef _WIN32\n#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)\n#endif /* _WIN32 */\n\n#endif /* _WIN32_HELPER_INCLUDE */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/.appveyor.yml",
    "content": "version: '{build}'\n\nenvironment:\n  matrix:\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n    MSVC: amd64\n  - MSYSTEM: MINGW32\n    CPU: i686\n    MSVC: x86\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n  - MSYSTEM: MINGW32\n    CPU: i686\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n    MSVC: amd64\n    CONFIG_FLAGS: --enable-debug\n  - MSYSTEM: MINGW32\n    CPU: i686\n    MSVC: x86\n    CONFIG_FLAGS: --enable-debug\n  - MSYSTEM: MINGW64\n    CPU: x86_64\n    CONFIG_FLAGS: --enable-debug\n  - MSYSTEM: MINGW32\n    CPU: i686\n    CONFIG_FLAGS: --enable-debug\n\ninstall:\n  - set PATH=c:\\msys64\\%MSYSTEM%\\bin;c:\\msys64\\usr\\bin;%PATH%\n  - if defined MSVC call \"c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" %MSVC%\n  - if defined MSVC pacman --noconfirm -Rsc mingw-w64-%CPU%-gcc gcc\n  - pacman --noconfirm -Suy mingw-w64-%CPU%-make\n\nbuild_script:\n  - bash -c \"autoconf\"\n  - bash -c \"./configure $CONFIG_FLAGS\"\n  - mingw32-make\n  - file lib/jemalloc.dll\n  - mingw32-make tests\n  - mingw32-make -k check\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/.autom4te.cfg",
    "content": "begin-language: \"Autoconf-without-aclocal-m4\"\nargs: --no-cache\nend-language: \"Autoconf-without-aclocal-m4\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/.gitattributes",
    "content": "* text=auto eol=lf\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/.gitignore",
    "content": "/bin/jemalloc-config\n/bin/jemalloc.sh\n/bin/jeprof\n\n/config.stamp\n/config.log\n/config.status\n/configure\n\n/doc/html.xsl\n/doc/manpages.xsl\n/doc/jemalloc.xml\n/doc/jemalloc.html\n/doc/jemalloc.3\n\n/jemalloc.pc\n\n/lib/\n\n/Makefile\n\n/include/jemalloc/internal/jemalloc_preamble.h\n/include/jemalloc/internal/jemalloc_internal_defs.h\n/include/jemalloc/internal/private_namespace.gen.h\n/include/jemalloc/internal/private_namespace.h\n/include/jemalloc/internal/private_namespace_jet.gen.h\n/include/jemalloc/internal/private_namespace_jet.h\n/include/jemalloc/internal/private_symbols.awk\n/include/jemalloc/internal/private_symbols_jet.awk\n/include/jemalloc/internal/public_namespace.h\n/include/jemalloc/internal/public_symbols.txt\n/include/jemalloc/internal/public_unnamespace.h\n/include/jemalloc/internal/size_classes.h\n/include/jemalloc/jemalloc.h\n/include/jemalloc/jemalloc_defs.h\n/include/jemalloc/jemalloc_macros.h\n/include/jemalloc/jemalloc_mangle.h\n/include/jemalloc/jemalloc_mangle_jet.h\n/include/jemalloc/jemalloc_protos.h\n/include/jemalloc/jemalloc_protos_jet.h\n/include/jemalloc/jemalloc_rename.h\n/include/jemalloc/jemalloc_typedefs.h\n\n/src/*.[od]\n/src/*.sym\n\n/run_tests.out/\n\n/test/test.sh\ntest/include/test/jemalloc_test.h\ntest/include/test/jemalloc_test_defs.h\n\n/test/integration/[A-Za-z]*\n!/test/integration/[A-Za-z]*.*\n/test/integration/*.[od]\n/test/integration/*.out\n\n/test/integration/cpp/[A-Za-z]*\n!/test/integration/cpp/[A-Za-z]*.*\n/test/integration/cpp/*.[od]\n/test/integration/cpp/*.out\n\n/test/src/*.[od]\n\n/test/stress/[A-Za-z]*\n!/test/stress/[A-Za-z]*.*\n/test/stress/*.[od]\n/test/stress/*.out\n\n/test/unit/[A-Za-z]*\n!/test/unit/[A-Za-z]*.*\n/test/unit/*.[od]\n/test/unit/*.out\n\n/VERSION\n\n*.pdb\n*.sdf\n*.opendb\n*.VC.db\n*.opensdf\n*.cachefile\n*.suo\n*.user\n*.sln.docstates\n*.tmp\n.vs/\n/msvc/Win32/\n/msvc/x64/\n/msvc/projects/*/*/Debug*/\n/msvc/projects/*/*/Release*/\n/msvc/projects/*/*/Win32/\n/msvc/projects/*/*/x64/\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/.travis.yml",
    "content": "language: generic\ndist: precise\n\nmatrix:\n  include:\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: osx\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=clang CXX=clang++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--enable-debug\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"-m32\" CONFIGURE_FLAGS=\"--with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n      addons:\n        apt:\n          packages:\n            - gcc-multilib\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --enable-prof\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-debug --with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --disable-stats\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--enable-prof --with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=tcache:false\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--disable-stats --with-malloc-conf=background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false,dss:primary\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false,percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=tcache:false,background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary,percpu_arena:percpu\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=dss:primary,background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n    - os: linux\n      env: CC=gcc CXX=g++ COMPILER_FLAGS=\"\" CONFIGURE_FLAGS=\"--with-malloc-conf=percpu_arena:percpu,background_thread:true\" EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"\n\n\nbefore_script:\n  - autoconf\n  - ./configure ${COMPILER_FLAGS:+       CC=\"$CC $COMPILER_FLAGS\"       CXX=\"$CXX $COMPILER_FLAGS\" }       $CONFIGURE_FLAGS\n  - make -j3\n  - make -j3 tests\n\nscript:\n  - make check\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/COPYING",
    "content": "Unless otherwise specified, files in the jemalloc source distribution are\nsubject to the following license:\n--------------------------------------------------------------------------------\nCopyright (C) 2002-2018 Jason Evans <jasone@canonware.com>.\nAll rights reserved.\nCopyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.\nCopyright (C) 2009-2018 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": "Chapter07/redis-6.0.3/deps/jemalloc/ChangeLog",
    "content": "Following are change highlights associated with official releases.  Important\nbug fixes are all mentioned, but some internal enhancements are omitted here for\nbrevity.  Much more detail can be found in the git revision history:\n\n    https://github.com/jemalloc/jemalloc\n\n* 5.1.0 (May 4th, 2018)\n\n  This release is primarily about fine-tuning, ranging from several new features\n  to numerous notable performance and portability enhancements.  The release and\n  prior dev versions have been running in multiple large scale applications for\n  months, and the cumulative improvements are substantial in many cases.\n\n  Given the long and successful production runs, this release is likely a good\n  candidate for applications to upgrade, from both jemalloc 5.0 and before.  For\n  performance-critical applications, the newly added TUNING.md provides\n  guidelines on jemalloc tuning.\n\n  New features:\n  - Implement transparent huge page support for internal metadata.  (@interwq)\n  - Add opt.thp to allow enabling / disabling transparent huge pages for all\n    mappings.  (@interwq)\n  - Add maximum background thread count option.  (@djwatson)\n  - Allow prof_active to control opt.lg_prof_interval and prof.gdump.\n    (@interwq)\n  - Allow arena index lookup based on allocation addresses via mallctl.\n    (@lionkov)\n  - Allow disabling initial-exec TLS model.  (@davidtgoldblatt, @KenMacD)\n  - Add opt.lg_extent_max_active_fit to set the max ratio between the size of\n    the active extent selected (to split off from) and the size of the requested\n    allocation.  (@interwq, @davidtgoldblatt)\n  - Add retain_grow_limit to set the max size when growing virtual address\n    space.  (@interwq)\n  - Add mallctl interfaces:\n    + arena.<i>.retain_grow_limit  (@interwq)\n    + arenas.lookup  (@lionkov)\n    + max_background_threads  (@djwatson)\n    + opt.lg_extent_max_active_fit  (@interwq)\n    + opt.max_background_threads  (@djwatson)\n    + opt.metadata_thp  (@interwq)\n    + opt.thp  (@interwq)\n    + stats.metadata_thp  (@interwq)\n\n  Portability improvements:\n  - Support GNU/kFreeBSD configuration.  (@paravoid)\n  - Support m68k, nios2 and SH3 architectures.  (@paravoid)\n  - Fall back to FD_CLOEXEC when O_CLOEXEC is unavailable.  (@zonyitoo)\n  - Fix symbol listing for cross-compiling.  (@tamird)\n  - Fix high bits computation on ARM.  (@davidtgoldblatt, @paravoid)\n  - Disable the CPU_SPINWAIT macro for Power.  (@davidtgoldblatt, @marxin)\n  - Fix MSVC 2015 & 2017 builds.  (@rustyx)\n  - Improve RISC-V support.  (@EdSchouten)\n  - Set name mangling script in strict mode.  (@nicolov)\n  - Avoid MADV_HUGEPAGE on ARM.  (@marxin)\n  - Modify configure to determine return value of strerror_r.\n    (@davidtgoldblatt, @cferris1000)\n  - Make sure CXXFLAGS is tested with CPP compiler.  (@nehaljwani)\n  - Fix 32-bit build on MSVC.  (@rustyx)\n  - Fix external symbol on MSVC.  (@maksqwe)\n  - Avoid a printf format specifier warning.  (@jasone)\n  - Add configure option --disable-initial-exec-tls which can allow jemalloc to\n    be dynamically loaded after program startup.  (@davidtgoldblatt, @KenMacD)\n  - AArch64: Add ILP32 support.  (@cmuellner)\n  - Add --with-lg-vaddr configure option to support cross compiling.\n    (@cmuellner, @davidtgoldblatt)\n\n  Optimizations and refactors:\n  - Improve active extent fit with extent_max_active_fit.  This considerably\n    reduces fragmentation over time and improves virtual memory and metadata\n    usage.  (@davidtgoldblatt, @interwq)\n  - Eagerly coalesce large extents to reduce fragmentation.  (@interwq)\n  - sdallocx: only read size info when page aligned (i.e. possibly sampled),\n    which speeds up the sized deallocation path significantly.  (@interwq)\n  - Avoid attempting new mappings for in place expansion with retain, since\n    it rarely succeeds in practice and causes high overhead.  (@interwq)\n  - Refactor OOM handling in newImpl.  (@wqfish)\n  - Add internal fine-grained logging functionality for debugging use.\n    (@davidtgoldblatt)\n  - Refactor arena / tcache interactions.  (@davidtgoldblatt)\n  - Refactor extent management with dumpable flag.  (@davidtgoldblatt)\n  - Add runtime detection of lazy purging.  (@interwq)\n  - Use pairing heap instead of red-black tree for extents_avail.  (@djwatson)\n  - Use sysctl on startup in FreeBSD.  (@trasz)\n  - Use thread local prng state instead of atomic.  (@djwatson)\n  - Make decay to always purge one more extent than before, because in\n    practice large extents are usually the ones that cross the decay threshold.\n    Purging the additional extent helps save memory as well as reduce VM\n    fragmentation.  (@interwq)\n  - Fast division by dynamic values.  (@davidtgoldblatt)\n  - Improve the fit for aligned allocation.  (@interwq, @edwinsmith)\n  - Refactor extent_t bitpacking.  (@rkmisra)\n  - Optimize the generated assembly for ticker operations.  (@davidtgoldblatt)\n  - Convert stats printing to use a structured text emitter.  (@davidtgoldblatt)\n  - Remove preserve_lru feature for extents management.  (@djwatson)\n  - Consolidate two memory loads into one on the fast deallocation path.\n    (@davidtgoldblatt, @interwq)\n\n  Bug fixes (most of the issues are only relevant to jemalloc 5.0):\n  - Fix deadlock with multithreaded fork in OS X.  (@davidtgoldblatt)\n  - Validate returned file descriptor before use.  (@zonyitoo)\n  - Fix a few background thread initialization and shutdown issues.  (@interwq)\n  - Fix an extent coalesce + decay race by taking both coalescing extents off\n    the LRU list.  (@interwq)\n  - Fix potentially unbound increase during decay, caused by one thread keep\n    stashing memory to purge while other threads generating new pages.  The\n    number of pages to purge is checked to prevent this.  (@interwq)\n  - Fix a FreeBSD bootstrap assertion.  (@strejda, @interwq)\n  - Handle 32 bit mutex counters.  (@rkmisra)\n  - Fix a indexing bug when creating background threads.  (@davidtgoldblatt,\n    @binliu19)\n  - Fix arguments passed to extent_init.  (@yuleniwo, @interwq)\n  - Fix addresses used for ordering mutexes.  (@rkmisra)\n  - Fix abort_conf processing during bootstrap.  (@interwq)\n  - Fix include path order for out-of-tree builds.  (@cmuellner)\n\n  Incompatible changes:\n  - Remove --disable-thp.  (@interwq)\n  - Remove mallctl interfaces:\n    + config.thp  (@interwq)\n\n  Documentation:\n  - Add TUNING.md.  (@interwq, @davidtgoldblatt, @djwatson)\n\n* 5.0.1 (July 1, 2017)\n\n  This bugfix release fixes several issues, most of which are obscure enough\n  that typical applications are not impacted.\n\n  Bug fixes:\n  - Update decay->nunpurged before purging, in order to avoid potential update\n    races and subsequent incorrect purging volume.  (@interwq)\n  - Only abort on dlsym(3) error if the failure impacts an enabled feature (lazy\n    locking and/or background threads).  This mitigates an initialization\n    failure bug for which we still do not have a clear reproduction test case.\n    (@interwq)\n  - Modify tsd management so that it neither crashes nor leaks if a thread's\n    only allocation activity is to call free() after TLS destructors have been\n    executed.  This behavior was observed when operating with GNU libc, and is\n    unlikely to be an issue with other libc implementations.  (@interwq)\n  - Mask signals during background thread creation.  This prevents signals from\n    being inadvertently delivered to background threads.  (@jasone,\n    @davidtgoldblatt, @interwq)\n  - Avoid inactivity checks within background threads, in order to prevent\n    recursive mutex acquisition.  (@interwq)\n  - Fix extent_grow_retained() to use the specified hooks when the\n    arena.<i>.extent_hooks mallctl is used to override the default hooks.\n    (@interwq)\n  - Add missing reentrancy support for custom extent hooks which allocate.\n    (@interwq)\n  - Post-fork(2), re-initialize the list of tcaches associated with each arena\n    to contain no tcaches except the forking thread's.  (@interwq)\n  - Add missing post-fork(2) mutex reinitialization for extent_grow_mtx.  This\n    fixes potential deadlocks after fork(2).  (@interwq)\n  - Enforce minimum autoconf version (currently 2.68), since 2.63 is known to\n    generate corrupt configure scripts.  (@jasone)\n  - Ensure that the configured page size (--with-lg-page) is no larger than the\n    configured huge page size (--with-lg-hugepage).  (@jasone)\n\n* 5.0.0 (June 13, 2017)\n\n  Unlike all previous jemalloc releases, this release does not use naturally\n  aligned \"chunks\" for virtual memory management, and instead uses page-aligned\n  \"extents\".  This change has few externally visible effects, but the internal\n  impacts are... extensive.  Many other internal changes combine to make this\n  the most cohesively designed version of jemalloc so far, with ample\n  opportunity for further enhancements.\n\n  Continuous integration is now an integral aspect of development thanks to the\n  efforts of @davidtgoldblatt, and the dev branch tends to remain reasonably\n  stable on the tested platforms (Linux, FreeBSD, macOS, and Windows).  As a\n  side effect the official release frequency may decrease over time.\n\n  New features:\n  - Implement optional per-CPU arena support; threads choose which arena to use\n    based on current CPU rather than on fixed thread-->arena associations.\n    (@interwq)\n  - Implement two-phase decay of unused dirty pages.  Pages transition from\n    dirty-->muzzy-->clean, where the first phase transition relies on\n    madvise(... MADV_FREE) semantics, and the second phase transition discards\n    pages such that they are replaced with demand-zeroed pages on next access.\n    (@jasone)\n  - Increase decay time resolution from seconds to milliseconds.  (@jasone)\n  - Implement opt-in per CPU background threads, and use them for asynchronous\n    decay-driven unused dirty page purging.  (@interwq)\n  - Add mutex profiling, which collects a variety of statistics useful for\n    diagnosing overhead/contention issues.  (@interwq)\n  - Add C++ new/delete operator bindings.  (@djwatson)\n  - Support manually created arena destruction, such that all data and metadata\n    are discarded.  Add MALLCTL_ARENAS_DESTROYED for accessing merged stats\n    associated with destroyed arenas.  (@jasone)\n  - Add MALLCTL_ARENAS_ALL as a fixed index for use in accessing\n    merged/destroyed arena statistics via mallctl.  (@jasone)\n  - Add opt.abort_conf to optionally abort if invalid configuration options are\n    detected during initialization.  (@interwq)\n  - Add opt.stats_print_opts, so that e.g. JSON output can be selected for the\n    stats dumped during exit if opt.stats_print is true.  (@jasone)\n  - Add --with-version=VERSION for use when embedding jemalloc into another\n    project's git repository.  (@jasone)\n  - Add --disable-thp to support cross compiling.  (@jasone)\n  - Add --with-lg-hugepage to support cross compiling.  (@jasone)\n  - Add mallctl interfaces (various authors):\n    + background_thread\n    + opt.abort_conf\n    + opt.retain\n    + opt.percpu_arena\n    + opt.background_thread\n    + opt.{dirty,muzzy}_decay_ms\n    + opt.stats_print_opts\n    + arena.<i>.initialized\n    + arena.<i>.destroy\n    + arena.<i>.{dirty,muzzy}_decay_ms\n    + arena.<i>.extent_hooks\n    + arenas.{dirty,muzzy}_decay_ms\n    + arenas.bin.<i>.slab_size\n    + arenas.nlextents\n    + arenas.lextent.<i>.size\n    + arenas.create\n    + stats.background_thread.{num_threads,num_runs,run_interval}\n    + stats.mutexes.{ctl,background_thread,prof,reset}.\n      {num_ops,num_spin_acq,num_wait,max_wait_time,total_wait_time,max_num_thds,\n      num_owner_switch}\n    + stats.arenas.<i>.{dirty,muzzy}_decay_ms\n    + stats.arenas.<i>.uptime\n    + stats.arenas.<i>.{pmuzzy,base,internal,resident}\n    + stats.arenas.<i>.{dirty,muzzy}_{npurge,nmadvise,purged}\n    + stats.arenas.<i>.bins.<j>.{nslabs,reslabs,curslabs}\n    + stats.arenas.<i>.bins.<j>.mutex.\n      {num_ops,num_spin_acq,num_wait,max_wait_time,total_wait_time,max_num_thds,\n      num_owner_switch}\n    + stats.arenas.<i>.lextents.<j>.{nmalloc,ndalloc,nrequests,curlextents}\n    + stats.arenas.i.mutexes.{large,extent_avail,extents_dirty,extents_muzzy,\n      extents_retained,decay_dirty,decay_muzzy,base,tcache_list}.\n      {num_ops,num_spin_acq,num_wait,max_wait_time,total_wait_time,max_num_thds,\n      num_owner_switch}\n\n  Portability improvements:\n  - Improve reentrant allocation support, such that deadlock is less likely if\n    e.g. a system library call in turn allocates memory.  (@davidtgoldblatt,\n    @interwq)\n  - Support static linking of jemalloc with glibc.  (@djwatson)\n\n  Optimizations and refactors:\n  - Organize virtual memory as \"extents\" of virtual memory pages, rather than as\n    naturally aligned \"chunks\", and store all metadata in arbitrarily distant\n    locations.  This reduces virtual memory external fragmentation, and will\n    interact better with huge pages (not yet explicitly supported).  (@jasone)\n  - Fold large and huge size classes together; only small and large size classes\n    remain.  (@jasone)\n  - Unify the allocation paths, and merge most fast-path branching decisions.\n    (@davidtgoldblatt, @interwq)\n  - Embed per thread automatic tcache into thread-specific data, which reduces\n    conditional branches and dereferences.  Also reorganize tcache to increase\n    fast-path data locality.  (@interwq)\n  - Rewrite atomics to closely model the C11 API, convert various\n    synchronization from mutex-based to atomic, and use the explicit memory\n    ordering control to resolve various hypothetical races without increasing\n    synchronization overhead.  (@davidtgoldblatt)\n  - Extensively optimize rtree via various methods:\n    + Add multiple layers of rtree lookup caching, since rtree lookups are now\n      part of fast-path deallocation.  (@interwq)\n    + Determine rtree layout at compile time.  (@jasone)\n    + Make the tree shallower for common configurations.  (@jasone)\n    + Embed the root node in the top-level rtree data structure, thus avoiding\n      one level of indirection.  (@jasone)\n    + Further specialize leaf elements as compared to internal node elements,\n      and directly embed extent metadata needed for fast-path deallocation.\n      (@jasone)\n    + Ignore leading always-zero address bits (architecture-specific).\n      (@jasone)\n  - Reorganize headers (ongoing work) to make them hermetic, and disentangle\n    various module dependencies.  (@davidtgoldblatt)\n  - Convert various internal data structures such as size class metadata from\n    boot-time-initialized to compile-time-initialized.  Propagate resulting data\n    structure simplifications, such as making arena metadata fixed-size.\n    (@jasone)\n  - Simplify size class lookups when constrained to size classes that are\n    multiples of the page size.  This speeds lookups, but the primary benefit is\n    complexity reduction in code that was the source of numerous regressions.\n    (@jasone)\n  - Lock individual extents when possible for localized extent operations,\n    rather than relying on a top-level arena lock.  (@davidtgoldblatt, @jasone)\n  - Use first fit layout policy instead of best fit, in order to improve\n    packing.  (@jasone)\n  - If munmap(2) is not in use, use an exponential series to grow each arena's\n    virtual memory, so that the number of disjoint virtual memory mappings\n    remains low.  (@jasone)\n  - Implement per arena base allocators, so that arenas never share any virtual\n    memory pages.  (@jasone)\n  - Automatically generate private symbol name mangling macros.  (@jasone)\n\n  Incompatible changes:\n  - Replace chunk hooks with an expanded/normalized set of extent hooks.\n    (@jasone)\n  - Remove ratio-based purging.  (@jasone)\n  - Remove --disable-tcache.  (@jasone)\n  - Remove --disable-tls.  (@jasone)\n  - Remove --enable-ivsalloc.  (@jasone)\n  - Remove --with-lg-size-class-group.  (@jasone)\n  - Remove --with-lg-tiny-min.  (@jasone)\n  - Remove --disable-cc-silence.  (@jasone)\n  - Remove --enable-code-coverage.  (@jasone)\n  - Remove --disable-munmap (replaced by opt.retain).  (@jasone)\n  - Remove Valgrind support.  (@jasone)\n  - Remove quarantine support.  (@jasone)\n  - Remove redzone support.  (@jasone)\n  - Remove mallctl interfaces (various authors):\n    + config.munmap\n    + config.tcache\n    + config.tls\n    + config.valgrind\n    + opt.lg_chunk\n    + opt.purge\n    + opt.lg_dirty_mult\n    + opt.decay_time\n    + opt.quarantine\n    + opt.redzone\n    + opt.thp\n    + arena.<i>.lg_dirty_mult\n    + arena.<i>.decay_time\n    + arena.<i>.chunk_hooks\n    + arenas.initialized\n    + arenas.lg_dirty_mult\n    + arenas.decay_time\n    + arenas.bin.<i>.run_size\n    + arenas.nlruns\n    + arenas.lrun.<i>.size\n    + arenas.nhchunks\n    + arenas.hchunk.<i>.size\n    + arenas.extend\n    + stats.cactive\n    + stats.arenas.<i>.lg_dirty_mult\n    + stats.arenas.<i>.decay_time\n    + stats.arenas.<i>.metadata.{mapped,allocated}\n    + stats.arenas.<i>.{npurge,nmadvise,purged}\n    + stats.arenas.<i>.huge.{allocated,nmalloc,ndalloc,nrequests}\n    + stats.arenas.<i>.bins.<j>.{nruns,reruns,curruns}\n    + stats.arenas.<i>.lruns.<j>.{nmalloc,ndalloc,nrequests,curruns}\n    + stats.arenas.<i>.hchunks.<j>.{nmalloc,ndalloc,nrequests,curhchunks}\n\n  Bug fixes:\n  - Improve interval-based profile dump triggering to dump only one profile when\n    a single allocation's size exceeds the interval.  (@jasone)\n  - Use prefixed function names (as controlled by --with-jemalloc-prefix) when\n    pruning backtrace frames in jeprof.  (@jasone)\n\n* 4.5.0 (February 28, 2017)\n\n  This is the first release to benefit from much broader continuous integration\n  testing, thanks to @davidtgoldblatt.  Had we had this testing infrastructure\n  in place for prior releases, it would have caught all of the most serious\n  regressions fixed by this release.\n\n  New features:\n  - Add --disable-thp and the opt.thp mallctl to provide opt-out mechanisms for\n    transparent huge page integration.  (@jasone)\n  - Update zone allocator integration to work with macOS 10.12.  (@glandium)\n  - Restructure *CFLAGS configuration, so that CFLAGS behaves typically, and\n    EXTRA_CFLAGS provides a way to specify e.g. -Werror during building, but not\n    during configuration.  (@jasone, @ronawho)\n\n  Bug fixes:\n  - Fix DSS (sbrk(2)-based) allocation.  This regression was first released in\n    4.3.0.  (@jasone)\n  - Handle race in per size class utilization computation.  This functionality\n    was first released in 4.0.0.  (@interwq)\n  - Fix lock order reversal during gdump.  (@jasone)\n  - Fix/refactor tcache synchronization.  This regression was first released in\n    4.0.0.  (@jasone)\n  - Fix various JSON-formatted malloc_stats_print() bugs.  This functionality\n    was first released in 4.3.0.  (@jasone)\n  - Fix huge-aligned allocation.  This regression was first released in 4.4.0.\n    (@jasone)\n  - When transparent huge page integration is enabled, detect what state pages\n    start in according to the kernel's current operating mode, and only convert\n    arena chunks to non-huge during purging if that is not their initial state.\n    This functionality was first released in 4.4.0.  (@jasone)\n  - Fix lg_chunk clamping for the --enable-cache-oblivious --disable-fill case.\n    This regression was first released in 4.0.0.  (@jasone, @428desmo)\n  - Properly detect sparc64 when building for Linux.  (@glaubitz)\n\n* 4.4.0 (December 3, 2016)\n\n  New features:\n  - Add configure support for *-*-linux-android.  (@cferris1000, @jasone)\n  - Add the --disable-syscall configure option, for use on systems that place\n    security-motivated limitations on syscall(2).  (@jasone)\n  - Add support for Debian GNU/kFreeBSD.  (@thesam)\n\n  Optimizations:\n  - Add extent serial numbers and use them where appropriate as a sort key that\n    is higher priority than address, so that the allocation policy prefers older\n    extents.  This tends to improve locality (decrease fragmentation) when\n    memory grows downward.  (@jasone)\n  - Refactor madvise(2) configuration so that MADV_FREE is detected and utilized\n    on Linux 4.5 and newer.  (@jasone)\n  - Mark partially purged arena chunks as non-huge-page.  This improves\n    interaction with Linux's transparent huge page functionality.  (@jasone)\n\n  Bug fixes:\n  - Fix size class computations for edge conditions involving extremely large\n    allocations.  This regression was first released in 4.0.0.  (@jasone,\n    @ingvarha)\n  - Remove overly restrictive assertions related to the cactive statistic.  This\n    regression was first released in 4.1.0.  (@jasone)\n  - Implement a more reliable detection scheme for os_unfair_lock on macOS.\n    (@jszakmeister)\n\n* 4.3.1 (November 7, 2016)\n\n  Bug fixes:\n  - Fix a severe virtual memory leak.  This regression was first released in\n    4.3.0.  (@interwq, @jasone)\n  - Refactor atomic and prng APIs to restore support for 32-bit platforms that\n    use pre-C11 toolchains, e.g. FreeBSD's mips.  (@jasone)\n\n* 4.3.0 (November 4, 2016)\n\n  This is the first release that passes the test suite for multiple Windows\n  configurations, thanks in large part to @glandium setting up continuous\n  integration via AppVeyor (and Travis CI for Linux and OS X).\n\n  New features:\n  - Add \"J\" (JSON) support to malloc_stats_print().  (@jasone)\n  - Add Cray compiler support.  (@ronawho)\n\n  Optimizations:\n  - Add/use adaptive spinning for bootstrapping and radix tree node\n    initialization.  (@jasone)\n\n  Bug fixes:\n  - Fix large allocation to search starting in the optimal size class heap,\n    which can substantially reduce virtual memory churn and fragmentation.  This\n    regression was first released in 4.0.0.  (@mjp41, @jasone)\n  - Fix stats.arenas.<i>.nthreads accounting.  (@interwq)\n  - Fix and simplify decay-based purging.  (@jasone)\n  - Make DSS (sbrk(2)-related) operations lockless, which resolves potential\n    deadlocks during thread exit.  (@jasone)\n  - Fix over-sized allocation of radix tree leaf nodes.  (@mjp41, @ogaun,\n    @jasone)\n  - Fix over-sized allocation of arena_t (plus associated stats) data\n    structures.  (@jasone, @interwq)\n  - Fix EXTRA_CFLAGS to not affect configuration.  (@jasone)\n  - Fix a Valgrind integration bug.  (@ronawho)\n  - Disallow 0x5a junk filling when running in Valgrind.  (@jasone)\n  - Fix a file descriptor leak on Linux.  This regression was first released in\n    4.2.0.  (@vsarunas, @jasone)\n  - Fix static linking of jemalloc with glibc.  (@djwatson)\n  - Use syscall(2) rather than {open,read,close}(2) during boot on Linux.  This\n    works around other libraries' system call wrappers performing reentrant\n    allocation.  (@kspinka, @Whissi, @jasone)\n  - Fix OS X default zone replacement to work with OS X 10.12.  (@glandium,\n    @jasone)\n  - Fix cached memory management to avoid needless commit/decommit operations\n    during purging, which resolves permanent virtual memory map fragmentation\n    issues on Windows.  (@mjp41, @jasone)\n  - Fix TSD fetches to avoid (recursive) allocation.  This is relevant to\n    non-TLS and Windows configurations.  (@jasone)\n  - Fix malloc_conf overriding to work on Windows.  (@jasone)\n  - Forcibly disable lazy-lock on Windows (was forcibly *enabled*).  (@jasone)\n\n* 4.2.1 (June 8, 2016)\n\n  Bug fixes:\n  - Fix bootstrapping issues for configurations that require allocation during\n    tsd initialization (e.g. --disable-tls).  (@cferris1000, @jasone)\n  - Fix gettimeofday() version of nstime_update().  (@ronawho)\n  - Fix Valgrind regressions in calloc() and chunk_alloc_wrapper().  (@ronawho)\n  - Fix potential VM map fragmentation regression.  (@jasone)\n  - Fix opt_zero-triggered in-place huge reallocation zeroing.  (@jasone)\n  - Fix heap profiling context leaks in reallocation edge cases.  (@jasone)\n\n* 4.2.0 (May 12, 2016)\n\n  New features:\n  - Add the arena.<i>.reset mallctl, which makes it possible to discard all of\n    an arena's allocations in a single operation.  (@jasone)\n  - Add the stats.retained and stats.arenas.<i>.retained statistics.  (@jasone)\n  - Add the --with-version configure option.  (@jasone)\n  - Support --with-lg-page values larger than actual page size.  (@jasone)\n\n  Optimizations:\n  - Use pairing heaps rather than red-black trees for various hot data\n    structures.  (@djwatson, @jasone)\n  - Streamline fast paths of rtree operations.  (@jasone)\n  - Optimize the fast paths of calloc() and [m,d,sd]allocx().  (@jasone)\n  - Decommit unused virtual memory if the OS does not overcommit.  (@jasone)\n  - Specify MAP_NORESERVE on Linux if [heuristic] overcommit is active, in order\n    to avoid unfortunate interactions during fork(2).  (@jasone)\n\n  Bug fixes:\n  - Fix chunk accounting related to triggering gdump profiles.  (@jasone)\n  - Link against librt for clock_gettime(2) if glibc < 2.17.  (@jasone)\n  - Scale leak report summary according to sampling probability.  (@jasone)\n\n* 4.1.1 (May 3, 2016)\n\n  This bugfix release resolves a variety of mostly minor issues, though the\n  bitmap fix is critical for 64-bit Windows.\n\n  Bug fixes:\n  - Fix the linear scan version of bitmap_sfu() to shift by the proper amount\n    even when sizeof(long) is not the same as sizeof(void *), as on 64-bit\n    Windows.  (@jasone)\n  - Fix hashing functions to avoid unaligned memory accesses (and resulting\n    crashes).  This is relevant at least to some ARM-based platforms.\n    (@rkmisra)\n  - Fix fork()-related lock rank ordering reversals.  These reversals were\n    unlikely to cause deadlocks in practice except when heap profiling was\n    enabled and active.  (@jasone)\n  - Fix various chunk leaks in OOM code paths.  (@jasone)\n  - Fix malloc_stats_print() to print opt.narenas correctly.  (@jasone)\n  - Fix MSVC-specific build/test issues.  (@rustyx, @yuslepukhin)\n  - Fix a variety of test failures that were due to test fragility rather than\n    core bugs.  (@jasone)\n\n* 4.1.0 (February 28, 2016)\n\n  This release is primarily about optimizations, but it also incorporates a lot\n  of portability-motivated refactoring and enhancements.  Many people worked on\n  this release, to an extent that even with the omission here of minor changes\n  (see git revision history), and of the people who reported and diagnosed\n  issues, so much of the work was contributed that starting with this release,\n  changes are annotated with author credits to help reflect the collaborative\n  effort involved.\n\n  New features:\n  - Implement decay-based unused dirty page purging, a major optimization with\n    mallctl API impact.  This is an alternative to the existing ratio-based\n    unused dirty page purging, and is intended to eventually become the sole\n    purging mechanism.  New mallctls:\n    + opt.purge\n    + opt.decay_time\n    + arena.<i>.decay\n    + arena.<i>.decay_time\n    + arenas.decay_time\n    + stats.arenas.<i>.decay_time\n    (@jasone, @cevans87)\n  - Add --with-malloc-conf, which makes it possible to embed a default\n    options string during configuration.  This was motivated by the desire to\n    specify --with-malloc-conf=purge:decay , since the default must remain\n    purge:ratio until the 5.0.0 release.  (@jasone)\n  - Add MS Visual Studio 2015 support.  (@rustyx, @yuslepukhin)\n  - Make *allocx() size class overflow behavior defined.  The maximum\n    size class is now less than PTRDIFF_MAX to protect applications against\n    numerical overflow, and all allocation functions are guaranteed to indicate\n    errors rather than potentially crashing if the request size exceeds the\n    maximum size class.  (@jasone)\n  - jeprof:\n    + Add raw heap profile support.  (@jasone)\n    + Add --retain and --exclude for backtrace symbol filtering.  (@jasone)\n\n  Optimizations:\n  - Optimize the fast path to combine various bootstrapping and configuration\n    checks and execute more streamlined code in the common case.  (@interwq)\n  - Use linear scan for small bitmaps (used for small object tracking).  In\n    addition to speeding up bitmap operations on 64-bit systems, this reduces\n    allocator metadata overhead by approximately 0.2%.  (@djwatson)\n  - Separate arena_avail trees, which substantially speeds up run tree\n    operations.  (@djwatson)\n  - Use memoization (boot-time-computed table) for run quantization.  Separate\n    arena_avail trees reduced the importance of this optimization.  (@jasone)\n  - Attempt mmap-based in-place huge reallocation.  This can dramatically speed\n    up incremental huge reallocation.  (@jasone)\n\n  Incompatible changes:\n  - Make opt.narenas unsigned rather than size_t.  (@jasone)\n\n  Bug fixes:\n  - Fix stats.cactive accounting regression.  (@rustyx, @jasone)\n  - Handle unaligned keys in hash().  This caused problems for some ARM systems.\n    (@jasone, @cferris1000)\n  - Refactor arenas array.  In addition to fixing a fork-related deadlock, this\n    makes arena lookups faster and simpler.  (@jasone)\n  - Move retained memory allocation out of the default chunk allocation\n    function, to a location that gets executed even if the application installs\n    a custom chunk allocation function.  This resolves a virtual memory leak.\n    (@buchgr)\n  - Fix a potential tsd cleanup leak.  (@cferris1000, @jasone)\n  - Fix run quantization.  In practice this bug had no impact unless\n    applications requested memory with alignment exceeding one page.\n    (@jasone, @djwatson)\n  - Fix LinuxThreads-specific bootstrapping deadlock.  (Cosmin Paraschiv)\n  - jeprof:\n    + Don't discard curl options if timeout is not defined.  (@djwatson)\n    + Detect failed profile fetches.  (@djwatson)\n  - Fix stats.arenas.<i>.{dss,lg_dirty_mult,decay_time,pactive,pdirty} for\n    --disable-stats case.  (@jasone)\n\n* 4.0.4 (October 24, 2015)\n\n  This bugfix release fixes another xallocx() regression.  No other regressions\n  have come to light in over a month, so this is likely a good starting point\n  for people who prefer to wait for \"dot one\" releases with all the major issues\n  shaken out.\n\n  Bug fixes:\n  - Fix xallocx(..., MALLOCX_ZERO to zero the last full trailing page of large\n    allocations that have been randomly assigned an offset of 0 when\n    --enable-cache-oblivious configure option is enabled.\n\n* 4.0.3 (September 24, 2015)\n\n  This bugfix release continues the trend of xallocx() and heap profiling fixes.\n\n  Bug fixes:\n  - Fix xallocx(..., MALLOCX_ZERO) to zero all trailing bytes of large\n    allocations when --enable-cache-oblivious configure option is enabled.\n  - Fix xallocx(..., MALLOCX_ZERO) to zero trailing bytes of huge allocations\n    when resizing from/to a size class that is not a multiple of the chunk size.\n  - Fix prof_tctx_dump_iter() to filter out nodes that were created after heap\n    profile dumping started.\n  - Work around a potentially bad thread-specific data initialization\n    interaction with NPTL (glibc's pthreads implementation).\n\n* 4.0.2 (September 21, 2015)\n\n  This bugfix release addresses a few bugs specific to heap profiling.\n\n  Bug fixes:\n  - Fix ixallocx_prof_sample() to never modify nor create sampled small\n    allocations.  xallocx() is in general incapable of moving small allocations,\n    so this fix removes buggy code without loss of generality.\n  - Fix irallocx_prof_sample() to always allocate large regions, even when\n    alignment is non-zero.\n  - Fix prof_alloc_rollback() to read tdata from thread-specific data rather\n    than dereferencing a potentially invalid tctx.\n\n* 4.0.1 (September 15, 2015)\n\n  This is a bugfix release that is somewhat high risk due to the amount of\n  refactoring required to address deep xallocx() problems.  As a side effect of\n  these fixes, xallocx() now tries harder to partially fulfill requests for\n  optional extra space.  Note that a couple of minor heap profiling\n  optimizations are included, but these are better thought of as performance\n  fixes that were integral to discovering most of the other bugs.\n\n  Optimizations:\n  - Avoid a chunk metadata read in arena_prof_tctx_set(), since it is in the\n    fast path when heap profiling is enabled.  Additionally, split a special\n    case out into arena_prof_tctx_reset(), which also avoids chunk metadata\n    reads.\n  - Optimize irallocx_prof() to optimistically update the sampler state.  The\n    prior implementation appears to have been a holdover from when\n    rallocx()/xallocx() functionality was combined as rallocm().\n\n  Bug fixes:\n  - Fix TLS configuration such that it is enabled by default for platforms on\n    which it works correctly.\n  - Fix arenas_cache_cleanup() and arena_get_hard() to handle\n    allocation/deallocation within the application's thread-specific data\n    cleanup functions even after arenas_cache is torn down.\n  - Fix xallocx() bugs related to size+extra exceeding HUGE_MAXCLASS.\n  - Fix chunk purge hook calls for in-place huge shrinking reallocation to\n    specify the old chunk size rather than the new chunk size.  This bug caused\n    no correctness issues for the default chunk purge function, but was\n    visible to custom functions set via the \"arena.<i>.chunk_hooks\" mallctl.\n  - Fix heap profiling bugs:\n    + Fix heap profiling to distinguish among otherwise identical sample sites\n      with interposed resets (triggered via the \"prof.reset\" mallctl).  This bug\n      could cause data structure corruption that would most likely result in a\n      segfault.\n    + Fix irealloc_prof() to prof_alloc_rollback() on OOM.\n    + Make one call to prof_active_get_unlocked() per allocation event, and use\n      the result throughout the relevant functions that handle an allocation\n      event.  Also add a missing check in prof_realloc().  These fixes protect\n      allocation events against concurrent prof_active changes.\n    + Fix ixallocx_prof() to pass usize_max and zero to ixallocx_prof_sample()\n      in the correct order.\n    + Fix prof_realloc() to call prof_free_sampled_object() after calling\n      prof_malloc_sample_object().  Prior to this fix, if tctx and old_tctx were\n      the same, the tctx could have been prematurely destroyed.\n  - Fix portability bugs:\n    + Don't bitshift by negative amounts when encoding/decoding run sizes in\n      chunk header maps.  This affected systems with page sizes greater than 8\n      KiB.\n    + Rename index_t to szind_t to avoid an existing type on Solaris.\n    + Add JEMALLOC_CXX_THROW to the memalign() function prototype, in order to\n      match glibc and avoid compilation errors when including both\n      jemalloc/jemalloc.h and malloc.h in C++ code.\n    + Don't assume that /bin/sh is appropriate when running size_classes.sh\n      during configuration.\n    + Consider __sparcv9 a synonym for __sparc64__ when defining LG_QUANTUM.\n    + Link tests to librt if it contains clock_gettime(2).\n\n* 4.0.0 (August 17, 2015)\n\n  This version contains many speed and space optimizations, both minor and\n  major.  The major themes are generalization, unification, and simplification.\n  Although many of these optimizations cause no visible behavior change, their\n  cumulative effect is substantial.\n\n  New features:\n  - Normalize size class spacing to be consistent across the complete size\n    range.  By default there are four size classes per size doubling, but this\n    is now configurable via the --with-lg-size-class-group option.  Also add the\n    --with-lg-page, --with-lg-page-sizes, --with-lg-quantum, and\n    --with-lg-tiny-min options, which can be used to tweak page and size class\n    settings.  Impacts:\n    + Worst case performance for incrementally growing/shrinking reallocation\n      is improved because there are far fewer size classes, and therefore\n      copying happens less often.\n    + Internal fragmentation is limited to 20% for all but the smallest size\n      classes (those less than four times the quantum).  (1B + 4 KiB)\n      and (1B + 4 MiB) previously suffered nearly 50% internal fragmentation.\n    + Chunk fragmentation tends to be lower because there are fewer distinct run\n      sizes to pack.\n  - Add support for explicit tcaches.  The \"tcache.create\", \"tcache.flush\", and\n    \"tcache.destroy\" mallctls control tcache lifetime and flushing, and the\n    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to the *allocx() API\n    control which tcache is used for each operation.\n  - Implement per thread heap profiling, as well as the ability to\n    enable/disable heap profiling on a per thread basis.  Add the \"prof.reset\",\n    \"prof.lg_sample\", \"thread.prof.name\", \"thread.prof.active\",\n    \"opt.prof_thread_active_init\", \"prof.thread_active_init\", and\n    \"thread.prof.active\" mallctls.\n  - Add support for per arena application-specified chunk allocators, configured\n    via the \"arena.<i>.chunk_hooks\" mallctl.\n  - Refactor huge allocation to be managed by arenas, so that arenas now\n    function as general purpose independent allocators.  This is important in\n    the context of user-specified chunk allocators, aside from the scalability\n    benefits.  Related new statistics:\n    + The \"stats.arenas.<i>.huge.allocated\", \"stats.arenas.<i>.huge.nmalloc\",\n      \"stats.arenas.<i>.huge.ndalloc\", and \"stats.arenas.<i>.huge.nrequests\"\n      mallctls provide high level per arena huge allocation statistics.\n    + The \"arenas.nhchunks\", \"arenas.hchunk.<i>.size\",\n      \"stats.arenas.<i>.hchunks.<j>.nmalloc\",\n      \"stats.arenas.<i>.hchunks.<j>.ndalloc\",\n      \"stats.arenas.<i>.hchunks.<j>.nrequests\", and\n      \"stats.arenas.<i>.hchunks.<j>.curhchunks\" mallctls provide per size class\n      statistics.\n  - Add the 'util' column to malloc_stats_print() output, which reports the\n    proportion of available regions that are currently in use for each small\n    size class.\n  - Add \"alloc\" and \"free\" modes for for junk filling (see the \"opt.junk\"\n    mallctl), so that it is possible to separately enable junk filling for\n    allocation versus deallocation.\n  - Add the jemalloc-config script, which provides information about how\n    jemalloc was configured, and how to integrate it into application builds.\n  - Add metadata statistics, which are accessible via the \"stats.metadata\",\n    \"stats.arenas.<i>.metadata.mapped\", and\n    \"stats.arenas.<i>.metadata.allocated\" mallctls.\n  - Add the \"stats.resident\" mallctl, which reports the upper limit of\n    physically resident memory mapped by the allocator.\n  - Add per arena control over unused dirty page purging, via the\n    \"arenas.lg_dirty_mult\", \"arena.<i>.lg_dirty_mult\", and\n    \"stats.arenas.<i>.lg_dirty_mult\" mallctls.\n  - Add the \"prof.gdump\" mallctl, which makes it possible to toggle the gdump\n    feature on/off during program execution.\n  - Add sdallocx(), which implements sized deallocation.  The primary\n    optimization over dallocx() is the removal of a metadata read, which often\n    suffers an L1 cache miss.\n  - Add missing header includes in jemalloc/jemalloc.h, so that applications\n    only have to #include <jemalloc/jemalloc.h>.\n  - Add support for additional platforms:\n    + Bitrig\n    + Cygwin\n    + DragonFlyBSD\n    + iOS\n    + OpenBSD\n    + OpenRISC/or1k\n\n  Optimizations:\n  - Maintain dirty runs in per arena LRUs rather than in per arena trees of\n    dirty-run-containing chunks.  In practice this change significantly reduces\n    dirty page purging volume.\n  - Integrate whole chunks into the unused dirty page purging machinery.  This\n    reduces the cost of repeated huge allocation/deallocation, because it\n    effectively introduces a cache of chunks.\n  - Split the arena chunk map into two separate arrays, in order to increase\n    cache locality for the frequently accessed bits.\n  - Move small run metadata out of runs, into arena chunk headers.  This reduces\n    run fragmentation, smaller runs reduce external fragmentation for small size\n    classes, and packed (less uniformly aligned) metadata layout improves CPU\n    cache set distribution.\n  - Randomly distribute large allocation base pointer alignment relative to page\n    boundaries in order to more uniformly utilize CPU cache sets.  This can be\n    disabled via the --disable-cache-oblivious configure option, and queried via\n    the \"config.cache_oblivious\" mallctl.\n  - Micro-optimize the fast paths for the public API functions.\n  - Refactor thread-specific data to reside in a single structure.  This assures\n    that only a single TLS read is necessary per call into the public API.\n  - Implement in-place huge allocation growing and shrinking.\n  - Refactor rtree (radix tree for chunk lookups) to be lock-free, and make\n    additional optimizations that reduce maximum lookup depth to one or two\n    levels.  This resolves what was a concurrency bottleneck for per arena huge\n    allocation, because a global data structure is critical for determining\n    which arenas own which huge allocations.\n\n  Incompatible changes:\n  - Replace --enable-cc-silence with --disable-cc-silence to suppress spurious\n    warnings by default.\n  - Assure that the constness of malloc_usable_size()'s return type matches that\n    of the system implementation.\n  - Change the heap profile dump format to support per thread heap profiling,\n    rename pprof to jeprof, and enhance it with the --thread=<n> option.  As a\n    result, the bundled jeprof must now be used rather than the upstream\n    (gperftools) pprof.\n  - Disable \"opt.prof_final\" by default, in order to avoid atexit(3), which can\n    internally deadlock on some platforms.\n  - Change the \"arenas.nlruns\" mallctl type from size_t to unsigned.\n  - Replace the \"stats.arenas.<i>.bins.<j>.allocated\" mallctl with\n    \"stats.arenas.<i>.bins.<j>.curregs\".\n  - Ignore MALLOC_CONF in set{uid,gid,cap} binaries.\n  - Ignore MALLOCX_ARENA(a) in dallocx(), in favor of using the\n    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to control tcache usage.\n\n  Removed features:\n  - Remove the *allocm() API, which is superseded by the *allocx() API.\n  - Remove the --enable-dss options, and make dss non-optional on all platforms\n    which support sbrk(2).\n  - Remove the \"arenas.purge\" mallctl, which was obsoleted by the\n    \"arena.<i>.purge\" mallctl in 3.1.0.\n  - Remove the unnecessary \"opt.valgrind\" mallctl; jemalloc automatically\n    detects whether it is running inside Valgrind.\n  - Remove the \"stats.huge.allocated\", \"stats.huge.nmalloc\", and\n    \"stats.huge.ndalloc\" mallctls.\n  - Remove the --enable-mremap option.\n  - Remove the \"stats.chunks.current\", \"stats.chunks.total\", and\n    \"stats.chunks.high\" mallctls.\n\n  Bug fixes:\n  - Fix the cactive statistic to decrease (rather than increase) when active\n    memory decreases.  This regression was first released in 3.5.0.\n  - Fix OOM handling in memalign() and valloc().  A variant of this bug existed\n    in all releases since 2.0.0, which introduced these functions.\n  - Fix an OOM-related regression in arena_tcache_fill_small(), which could\n    cause cache corruption on OOM.  This regression was present in all releases\n    from 2.2.0 through 3.6.0.\n  - Fix size class overflow handling for malloc(), posix_memalign(), memalign(),\n    calloc(), and realloc() when profiling is enabled.\n  - Fix the \"arena.<i>.dss\" mallctl to return an error if \"primary\" or\n    \"secondary\" precedence is specified, but sbrk(2) is not supported.\n  - Fix fallback lg_floor() implementations to handle extremely large inputs.\n  - Ensure the default purgeable zone is after the default zone on OS X.\n  - Fix latent bugs in atomic_*().\n  - Fix the \"arena.<i>.dss\" mallctl to handle read-only calls.\n  - Fix tls_model configuration to enable the initial-exec model when possible.\n  - Mark malloc_conf as a weak symbol so that the application can override it.\n  - Correctly detect glibc's adaptive pthread mutexes.\n  - Fix the --without-export configure option.\n\n* 3.6.0 (March 31, 2014)\n\n  This version contains a critical bug fix for a regression present in 3.5.0 and\n  3.5.1.\n\n  Bug fixes:\n  - Fix a regression in arena_chunk_alloc() that caused crashes during\n    small/large allocation if chunk allocation failed.  In the absence of this\n    bug, chunk allocation failure would result in allocation failure, e.g.  NULL\n    return from malloc().  This regression was introduced in 3.5.0.\n  - Fix backtracing for gcc intrinsics-based backtracing by specifying\n    -fno-omit-frame-pointer to gcc.  Note that the application (and all the\n    libraries it links to) must also be compiled with this option for\n    backtracing to be reliable.\n  - Use dss allocation precedence for huge allocations as well as small/large\n    allocations.\n  - Fix test assertion failure message formatting.  This bug did not manifest on\n    x86_64 systems because of implementation subtleties in va_list.\n  - Fix inconsequential test failures for hash and SFMT code.\n\n  New features:\n  - Support heap profiling on FreeBSD.  This feature depends on the proc\n    filesystem being mounted during heap profile dumping.\n\n* 3.5.1 (February 25, 2014)\n\n  This version primarily addresses minor bugs in test code.\n\n  Bug fixes:\n  - Configure Solaris/Illumos to use MADV_FREE.\n  - Fix junk filling for mremap(2)-based huge reallocation.  This is only\n    relevant if configuring with the --enable-mremap option specified.\n  - Avoid compilation failure if 'restrict' C99 keyword is not supported by the\n    compiler.\n  - Add a configure test for SSE2 rather than assuming it is usable on i686\n    systems.  This fixes test compilation errors, especially on 32-bit Linux\n    systems.\n  - Fix mallctl argument size mismatches (size_t vs. uint64_t) in the stats unit\n    test.\n  - Fix/remove flawed alignment-related overflow tests.\n  - Prevent compiler optimizations that could change backtraces in the\n    prof_accum unit test.\n\n* 3.5.0 (January 22, 2014)\n\n  This version focuses on refactoring and automated testing, though it also\n  includes some non-trivial heap profiling optimizations not mentioned below.\n\n  New features:\n  - Add the *allocx() API, which is a successor to the experimental *allocm()\n    API.  The *allocx() functions are slightly simpler to use because they have\n    fewer parameters, they directly return the results of primary interest, and\n    mallocx()/rallocx() avoid the strict aliasing pitfall that\n    allocm()/rallocm() share with posix_memalign().  Note that *allocm() is\n    slated for removal in the next non-bugfix release.\n  - Add support for LinuxThreads.\n\n  Bug fixes:\n  - Unless heap profiling is enabled, disable floating point code and don't link\n    with libm.  This, in combination with e.g. EXTRA_CFLAGS=-mno-sse on x64\n    systems, makes it possible to completely disable floating point register\n    use.  Some versions of glibc neglect to save/restore caller-saved floating\n    point registers during dynamic lazy symbol loading, and the symbol loading\n    code uses whatever malloc the application happens to have linked/loaded\n    with, the result being potential floating point register corruption.\n  - Report ENOMEM rather than EINVAL if an OOM occurs during heap profiling\n    backtrace creation in imemalign().  This bug impacted posix_memalign() and\n    aligned_alloc().\n  - Fix a file descriptor leak in a prof_dump_maps() error path.\n  - Fix prof_dump() to close the dump file descriptor for all relevant error\n    paths.\n  - Fix rallocm() to use the arena specified by the ALLOCM_ARENA(s) flag for\n    allocation, not just deallocation.\n  - Fix a data race for large allocation stats counters.\n  - Fix a potential infinite loop during thread exit.  This bug occurred on\n    Solaris, and could affect other platforms with similar pthreads TSD\n    implementations.\n  - Don't junk-fill reallocations unless usable size changes.  This fixes a\n    violation of the *allocx()/*allocm() semantics.\n  - Fix growing large reallocation to junk fill new space.\n  - Fix huge deallocation to junk fill when munmap is disabled.\n  - Change the default private namespace prefix from empty to je_, and change\n    --with-private-namespace-prefix so that it prepends an additional prefix\n    rather than replacing je_.  This reduces the likelihood of applications\n    which statically link jemalloc experiencing symbol name collisions.\n  - Add missing private namespace mangling (relevant when\n    --with-private-namespace is specified).\n  - Add and use JEMALLOC_INLINE_C so that static inline functions are marked as\n    static even for debug builds.\n  - Add a missing mutex unlock in a malloc_init_hard() error path.  In practice\n    this error path is never executed.\n  - Fix numerous bugs in malloc_strotumax() error handling/reporting.  These\n    bugs had no impact except for malformed inputs.\n  - Fix numerous bugs in malloc_snprintf().  These bugs were not exercised by\n    existing calls, so they had no impact.\n\n* 3.4.1 (October 20, 2013)\n\n  Bug fixes:\n  - Fix a race in the \"arenas.extend\" mallctl that could cause memory corruption\n    of internal data structures and subsequent crashes.\n  - Fix Valgrind integration flaws that caused Valgrind warnings about reads of\n    uninitialized memory in:\n    + arena chunk headers\n    + internal zero-initialized data structures (relevant to tcache and prof\n      code)\n  - Preserve errno during the first allocation.  A readlink(2) call during\n    initialization fails unless /etc/malloc.conf exists, so errno was typically\n    set during the first allocation prior to this fix.\n  - Fix compilation warnings reported by gcc 4.8.1.\n\n* 3.4.0 (June 2, 2013)\n\n  This version is essentially a small bugfix release, but the addition of\n  aarch64 support requires that the minor version be incremented.\n\n  Bug fixes:\n  - Fix race-triggered deadlocks in chunk_record().  These deadlocks were\n    typically triggered by multiple threads concurrently deallocating huge\n    objects.\n\n  New features:\n  - Add support for the aarch64 architecture.\n\n* 3.3.1 (March 6, 2013)\n\n  This version fixes bugs that are typically encountered only when utilizing\n  custom run-time options.\n\n  Bug fixes:\n  - Fix a locking order bug that could cause deadlock during fork if heap\n    profiling were enabled.\n  - Fix a chunk recycling bug that could cause the allocator to lose track of\n    whether a chunk was zeroed.  On FreeBSD, NetBSD, and OS X, it could cause\n    corruption if allocating via sbrk(2) (unlikely unless running with the\n    \"dss:primary\" option specified).  This was completely harmless on Linux\n    unless using mlockall(2) (and unlikely even then, unless the\n    --disable-munmap configure option or the \"dss:primary\" option was\n    specified).  This regression was introduced in 3.1.0 by the\n    mlockall(2)/madvise(2) interaction fix.\n  - Fix TLS-related memory corruption that could occur during thread exit if the\n    thread never allocated memory.  Only the quarantine and prof facilities were\n    susceptible.\n  - Fix two quarantine bugs:\n    + Internal reallocation of the quarantined object array leaked the old\n      array.\n    + Reallocation failure for internal reallocation of the quarantined object\n      array (very unlikely) resulted in memory corruption.\n  - Fix Valgrind integration to annotate all internally allocated memory in a\n    way that keeps Valgrind happy about internal data structure access.\n  - Fix building for s390 systems.\n\n* 3.3.0 (January 23, 2013)\n\n  This version includes a few minor performance improvements in addition to the\n  listed new features and bug fixes.\n\n  New features:\n  - Add clipping support to lg_chunk option processing.\n  - Add the --enable-ivsalloc option.\n  - Add the --without-export option.\n  - Add the --disable-zone-allocator option.\n\n  Bug fixes:\n  - Fix \"arenas.extend\" mallctl to output the number of arenas.\n  - Fix chunk_recycle() to unconditionally inform Valgrind that returned memory\n    is undefined.\n  - Fix build break on FreeBSD related to alloca.h.\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 additional 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": "Chapter07/redis-6.0.3/deps/jemalloc/INSTALL.md",
    "content": "Building and installing a packaged release of jemalloc can be as simple as\ntyping the following while in the root directory of the source tree:\n\n    ./configure\n    make\n    make install\n\nIf building from unpackaged developer sources, the simplest command sequence\nthat might work is:\n\n    ./autogen.sh\n    make dist\n    make\n    make install\n\nNote that documentation is not built by the default target because doing so\nwould create a dependency on xsltproc in packaged releases, hence the\nrequirement to either run 'make dist' or avoid installing docs via the various\ninstall_* targets documented below.\n\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\n    Print a definitive list of options.\n\n* `--prefix=<install-root-dir>`\n\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-version=(<major>.<minor>.<bugfix>-<nrev>-g<gid>|VERSION)`\n\n    The VERSION file is mandatory for successful configuration, and the\n    following steps are taken to assure its presence:\n    1) If --with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid> is specified,\n       generate VERSION using the specified value.\n    2) If --with-version is not specified in either form and the source\n       directory is inside a git repository, try to generate VERSION via 'git\n       describe' invocations that pattern-match release tags.\n    3) If VERSION is missing, generate it with a bogus version:\n       0.0.0-0-g0000000000000000000000000000000000000000\n\n    Note that --with-version=VERSION bypasses (1) and (2), which simplifies\n    VERSION configuration when embedding a jemalloc release into another\n    project's git repository.\n\n* `--with-rpath=<colon-separated-rpath>`\n\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\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\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* `--without-export`\n\n    Don't export public APIs.  This can be useful when building jemalloc as a\n    static library, or to avoid exporting public APIs when using the zone\n    allocator on OSX.\n\n* `--with-private-namespace=<prefix>`\n\n    Prefix all library-private APIs with <prefix>je_.  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, <prefix> is empty, which results in a symbol prefix of je_ .\n\n* `--with-install-suffix=<suffix>`\n\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* `--with-malloc-conf=<malloc_conf>`\n\n    Embed `<malloc_conf>` as a run-time options string that is processed prior to\n    the malloc_conf global variable, the /etc/malloc.conf symlink, and the\n    MALLOC_CONF environment variable.  For example, to change the default decay\n    time to 30 seconds:\n\n      --with-malloc-conf=decay_ms:30000\n\n* `--enable-debug`\n\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\n    Disable statistics gathering functionality.  See the \"opt.stats_print\"\n    option documentation for usage details.\n\n* `--enable-prof`\n\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\n    Use the libunwind library (http://www.nongnu.org/libunwind/) for stack\n    backtracing.\n\n* `--disable-prof-libgcc`\n\n    Disable the use of libgcc's backtracing functionality.\n\n* `--disable-prof-gcc`\n\n    Disable the use of gcc intrinsics for backtracing.\n\n* `--with-static-libunwind=<libunwind.a>`\n\n    Statically link against the specified libunwind.a rather than dynamically\n    linking with -lunwind.\n\n* `--disable-fill`\n\n    Disable support for junk/zero filling of memory.  See the \"opt.junk\" and\n    \"opt.zero\" option documentation for usage details.\n\n* `--disable-zone-allocator`\n\n    Disable zone allocator for Darwin.  This means jemalloc won't be hooked as\n    the default allocator on OSX/iOS.\n\n* `--enable-utrace`\n\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\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\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-cache-oblivious`\n\n    Disable cache-oblivious large allocation alignment for large allocation\n    requests with no alignment constraints.  If this feature is disabled, all\n    large allocations are page-aligned as an implementation artifact, which can\n    severely harm CPU cache utilization.  However, the cache-oblivious layout\n    comes at the cost of one extra page per large allocation, which in the\n    most extreme case increases physical memory usage for the 16 KiB size class\n    to 20 KiB.\n\n* `--disable-syscall`\n\n    Disable use of syscall(2) rather than {open,read,write,close}(2).  This is\n    intended as a workaround for systems that place security limitations on\n    syscall(2).\n\n* `--disable-cxx`\n\n    Disable C++ integration.  This will cause new and delete operator\n    implementations to be omitted.\n\n* `--with-xslroot=<path>`\n\n    Specify where to find DocBook XSL stylesheets when building the\n    documentation.\n\n* `--with-lg-page=<lg-page>`\n\n    Specify the base 2 log of the allocator page size, which must in turn be at\n    least as large as the system page size.  By default the configure script\n    determines the host's page size and sets the allocator page size equal to\n    the system page size, so this option need not be specified unless the\n    system page size may change between configuration and execution, e.g. when\n    cross compiling.\n\n* `--with-lg-page-sizes=<lg-page-sizes>`\n\n    Specify the comma-separated base 2 logs of the page sizes to support.  This\n    option may be useful when cross compiling in combination with\n    `--with-lg-page`, but its primary use case is for integration with FreeBSD's\n    libc, wherein jemalloc is embedded.\n\n* `--with-lg-hugepage=<lg-hugepage>`\n\n    Specify the base 2 log of the system huge page size.  This option is useful\n    when cross compiling, or when overriding the default for systems that do\n    not explicitly support huge pages.\n\n* `--with-lg-quantum=<lg-quantum>`\n\n    Specify the base 2 log of the minimum allocation alignment.  jemalloc needs\n    to know the minimum alignment that meets the following C standard\n    requirement (quoted from the April 12, 2011 draft of the C11 standard):\n\n    >  The pointer returned if the allocation succeeds is suitably aligned so\n      that it may be assigned to a pointer to any type of object with a\n      fundamental alignment requirement and then used to access such an object\n      or an array of such objects in the space allocated [...]\n\n    This setting is architecture-specific, and although jemalloc includes known\n    safe values for the most commonly used modern architectures, there is a\n    wrinkle related to GNU libc (glibc) that may impact your choice of\n    <lg-quantum>.  On most modern architectures, this mandates 16-byte\n    alignment (<lg-quantum>=4), but the glibc developers chose not to meet this\n    requirement for performance reasons.  An old discussion can be found at\n    <https://sourceware.org/bugzilla/show_bug.cgi?id=206> .  Unlike glibc,\n    jemalloc does follow the C standard by default (caveat: jemalloc\n    technically cheats for size classes smaller than the quantum), but the fact\n    that Linux systems already work around this allocator noncompliance means\n    that it is generally safe in practice to let jemalloc's minimum alignment\n    follow glibc's lead.  If you specify `--with-lg-quantum=3` during\n    configuration, jemalloc will provide additional size classes that are not\n    16-byte-aligned (24, 40, and 56).\n\n* `--with-lg-vaddr=<lg-vaddr>`\n\n    Specify the number of significant virtual address bits.  By default, the\n    configure script attempts to detect virtual address size on those platforms\n    where it knows how, and picks a default otherwise.  This option may be\n    useful when cross-compiling.\n\n* `--disable-initial-exec-tls`\n\n    Disable the initial-exec TLS model for jemalloc's internal thread-local\n    storage (on those platforms that support explicit settings).  This can allow\n    jemalloc to be dynamically loaded after program startup (e.g. using dlopen).\n    Note that in this case, there will be two malloc implementations operating\n    in the same process, which will almost certainly result in confusing runtime\n    crashes if pointers leak from one implementation to the other.\n\nThe following environment variables (not a definitive list) impact configure's\nbehavior:\n\n* `CFLAGS=\"?\"`\n* `CXXFLAGS=\"?\"`\n\n    Pass these flags to the C/C++ compiler.  Any flags set by the configure\n    script are prepended, which means explicitly set flags generally take\n    precedence.  Take care when specifying flags such as -Werror, because\n    configure tests may be affected in undesirable ways.\n\n* `EXTRA_CFLAGS=\"?\"`\n* `EXTRA_CXXFLAGS=\"?\"`\n\n    Append these flags to CFLAGS/CXXFLAGS, without passing them to the\n    compiler(s) during configuration.  This makes it possible to add flags such\n    as -Werror, while allowing the configure script to determine what other\n    flags are appropriate for the specified configuration.\n\n* `CPPFLAGS=\"?\"`\n\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\n* `LD_LIBRARY_PATH=\"?\"`\n\n    'ld' uses this colon-separated list to find libraries.\n\n* `LDFLAGS=\"?\"`\n\n    Pass these flags when linking.\n\n* `PATH=\"?\"`\n\n    'configure' uses this to find programs.\n\nIn some cases it may be necessary to work around configuration results that do\nnot match reality.  For example, Linux 4.5 added support for the MADV_FREE flag\nto madvise(2), which can cause problems if building on a host with MADV_FREE\nsupport and deploying to a target without.  To work around this, use a cache\nfile to override the relevant configuration variable defined in configure.ac,\ne.g.:\n\n    echo \"je_cv_madv_free=no\" > config.cache && ./configure -C\n\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_pc\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\n## Advanced installation\n\nOptionally, define make variables when invoking make, including (not\nexclusively):\n\n* `INCLUDEDIR=\"?\"`\n\n    Use this as the installation prefix for header files.\n\n* `LIBDIR=\"?\"`\n\n    Use this as the installation prefix for libraries.\n\n* `MANDIR=\"?\"`\n\n    Use this as the installation prefix for man pages.\n\n* `DESTDIR=\"?\"`\n\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\n* `CC=\"?\"`\n\n    Use this to invoke the C compiler.\n\n* `CFLAGS=\"?\"`\n\n    Pass these flags to the compiler.\n\n* `CPPFLAGS=\"?\"`\n\n    Pass these flags to the C preprocessor.\n\n* `LDFLAGS=\"?\"`\n\n    Pass these flags when linking.\n\n* `PATH=\"?\"`\n\n    Use this to search for programs used during configuration and building.\n\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\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": "Chapter07/redis-6.0.3/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@\nCXX := @CXX@\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$(objroot)include -I$(srcroot)include\nCONFIGURE_CFLAGS := @CONFIGURE_CFLAGS@\nSPECIFIED_CFLAGS := @SPECIFIED_CFLAGS@\nEXTRA_CFLAGS := @EXTRA_CFLAGS@\nCFLAGS := $(strip $(CONFIGURE_CFLAGS) $(SPECIFIED_CFLAGS) $(EXTRA_CFLAGS))\nCONFIGURE_CXXFLAGS := @CONFIGURE_CXXFLAGS@\nSPECIFIED_CXXFLAGS := @SPECIFIED_CXXFLAGS@\nEXTRA_CXXFLAGS := @EXTRA_CXXFLAGS@\nCXXFLAGS := $(strip $(CONFIGURE_CXXFLAGS) $(SPECIFIED_CXXFLAGS) $(EXTRA_CXXFLAGS))\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 := $(addprefix $(srcroot),@cfghdrs_in@)\ncfghdrs_out := @cfghdrs_out@\ncfgoutputs_in := $(addprefix $(srcroot),@cfgoutputs_in@)\ncfgoutputs_out := @cfgoutputs_out@\nenable_autogen := @enable_autogen@\nenable_prof := @enable_prof@\nenable_zone_allocator := @enable_zone_allocator@\nMALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF\nlink_whole_archive := @link_whole_archive@\nDSO_LDFLAGS = @DSO_LDFLAGS@\nSOREV = @SOREV@\nPIC_CFLAGS = @PIC_CFLAGS@\nCTARGET = @CTARGET@\nLDTARGET = @LDTARGET@\nTEST_LD_MODE = @TEST_LD_MODE@\nMKLIB = @MKLIB@\nAR = @AR@\nARFLAGS = @ARFLAGS@\nDUMP_SYMS = @DUMP_SYMS@\nAWK := @AWK@\nCC_MM = @CC_MM@\nLM := @LM@\nINSTALL = @INSTALL@\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 := $(objroot)bin/jemalloc-config $(objroot)bin/jemalloc.sh $(objroot)bin/jeprof\nC_HDRS := $(objroot)include/jemalloc/jemalloc$(install_suffix).h\nC_SRCS := $(srcroot)src/jemalloc.c \\\n\t$(srcroot)src/arena.c \\\n\t$(srcroot)src/background_thread.c \\\n\t$(srcroot)src/base.c \\\n\t$(srcroot)src/bin.c \\\n\t$(srcroot)src/bitmap.c \\\n\t$(srcroot)src/ckh.c \\\n\t$(srcroot)src/ctl.c \\\n\t$(srcroot)src/div.c \\\n\t$(srcroot)src/extent.c \\\n\t$(srcroot)src/extent_dss.c \\\n\t$(srcroot)src/extent_mmap.c \\\n\t$(srcroot)src/hash.c \\\n\t$(srcroot)src/hooks.c \\\n\t$(srcroot)src/large.c \\\n\t$(srcroot)src/log.c \\\n\t$(srcroot)src/malloc_io.c \\\n\t$(srcroot)src/mutex.c \\\n\t$(srcroot)src/mutex_pool.c \\\n\t$(srcroot)src/nstime.c \\\n\t$(srcroot)src/pages.c \\\n\t$(srcroot)src/prng.c \\\n\t$(srcroot)src/prof.c \\\n\t$(srcroot)src/rtree.c \\\n\t$(srcroot)src/stats.c \\\n\t$(srcroot)src/sz.c \\\n\t$(srcroot)src/tcache.c \\\n\t$(srcroot)src/ticker.c \\\n\t$(srcroot)src/tsd.c \\\n\t$(srcroot)src/witness.c\nifeq ($(enable_zone_allocator), 1)\nC_SRCS += $(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\nifeq (1, $(link_whole_archive))\nLJEMALLOC := -Wl,--whole-archive -L$(objroot)lib -l$(LIBJEMALLOC) -Wl,--no-whole-archive\nelse\nLJEMALLOC := $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\nendif\nPC := $(objroot)jemalloc.pc\nMAN3 := $(objroot)doc/jemalloc$(install_suffix).3\nDOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml\nDOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.html)\nDOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.3)\nDOCS := $(DOCS_HTML) $(DOCS_MAN3)\nC_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \\\n\t$(srcroot)test/src/btalloc_1.c $(srcroot)test/src/math.c \\\n\t$(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \\\n\t$(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \\\n\t$(srcroot)test/src/thd.c $(srcroot)test/src/timer.c\nifeq (1, $(link_whole_archive))\nC_UTIL_INTEGRATION_SRCS :=\nC_UTIL_CPP_SRCS :=\nelse\nC_UTIL_INTEGRATION_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c\nC_UTIL_CPP_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c\nendif\nTESTS_UNIT := \\\n\t$(srcroot)test/unit/a0.c \\\n\t$(srcroot)test/unit/arena_reset.c \\\n\t$(srcroot)test/unit/atomic.c \\\n\t$(srcroot)test/unit/background_thread.c \\\n\t$(srcroot)test/unit/background_thread_enable.c \\\n\t$(srcroot)test/unit/base.c \\\n\t$(srcroot)test/unit/bitmap.c \\\n\t$(srcroot)test/unit/ckh.c \\\n\t$(srcroot)test/unit/decay.c \\\n\t$(srcroot)test/unit/div.c \\\n\t$(srcroot)test/unit/emitter.c \\\n\t$(srcroot)test/unit/extent_quantize.c \\\n\t$(srcroot)test/unit/fork.c \\\n\t$(srcroot)test/unit/hash.c \\\n\t$(srcroot)test/unit/hooks.c \\\n\t$(srcroot)test/unit/junk.c \\\n\t$(srcroot)test/unit/junk_alloc.c \\\n\t$(srcroot)test/unit/junk_free.c \\\n\t$(srcroot)test/unit/log.c \\\n\t$(srcroot)test/unit/mallctl.c \\\n\t$(srcroot)test/unit/malloc_io.c \\\n\t$(srcroot)test/unit/math.c \\\n\t$(srcroot)test/unit/mq.c \\\n\t$(srcroot)test/unit/mtx.c \\\n\t$(srcroot)test/unit/pack.c \\\n\t$(srcroot)test/unit/pages.c \\\n\t$(srcroot)test/unit/ph.c \\\n\t$(srcroot)test/unit/prng.c \\\n\t$(srcroot)test/unit/prof_accum.c \\\n\t$(srcroot)test/unit/prof_active.c \\\n\t$(srcroot)test/unit/prof_gdump.c \\\n\t$(srcroot)test/unit/prof_idump.c \\\n\t$(srcroot)test/unit/prof_reset.c \\\n\t$(srcroot)test/unit/prof_tctx.c \\\n\t$(srcroot)test/unit/prof_thread_name.c \\\n\t$(srcroot)test/unit/ql.c \\\n\t$(srcroot)test/unit/qr.c \\\n\t$(srcroot)test/unit/rb.c \\\n\t$(srcroot)test/unit/retained.c \\\n\t$(srcroot)test/unit/rtree.c \\\n\t$(srcroot)test/unit/SFMT.c \\\n\t$(srcroot)test/unit/size_classes.c \\\n\t$(srcroot)test/unit/slab.c \\\n\t$(srcroot)test/unit/smoothstep.c \\\n\t$(srcroot)test/unit/spin.c \\\n\t$(srcroot)test/unit/stats.c \\\n\t$(srcroot)test/unit/stats_print.c \\\n\t$(srcroot)test/unit/ticker.c \\\n\t$(srcroot)test/unit/nstime.c \\\n\t$(srcroot)test/unit/tsd.c \\\n\t$(srcroot)test/unit/witness.c \\\n\t$(srcroot)test/unit/zero.c\nifeq (@enable_prof@, 1)\nTESTS_UNIT += \\\n\t$(srcroot)test/unit/arena_reset_prof.c\nendif\nTESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \\\n\t$(srcroot)test/integration/allocated.c \\\n\t$(srcroot)test/integration/extent.c \\\n\t$(srcroot)test/integration/mallocx.c \\\n\t$(srcroot)test/integration/MALLOCX_ARENA.c \\\n\t$(srcroot)test/integration/overflow.c \\\n\t$(srcroot)test/integration/posix_memalign.c \\\n\t$(srcroot)test/integration/rallocx.c \\\n\t$(srcroot)test/integration/sdallocx.c \\\n\t$(srcroot)test/integration/thread_arena.c \\\n\t$(srcroot)test/integration/thread_tcache_enabled.c \\\n\t$(srcroot)test/integration/xallocx.c\nifeq (@enable_cxx@, 1)\nCPP_SRCS := $(srcroot)src/jemalloc_cpp.cpp\nTESTS_INTEGRATION_CPP := $(srcroot)test/integration/cpp/basic.cpp\nelse\nCPP_SRCS :=\nTESTS_INTEGRATION_CPP :=\nendif\nTESTS_STRESS := $(srcroot)test/stress/microbench.c\n\nTESTS := $(TESTS_UNIT) $(TESTS_INTEGRATION) $(TESTS_INTEGRATION_CPP) $(TESTS_STRESS)\n\nPRIVATE_NAMESPACE_HDRS := $(objroot)include/jemalloc/internal/private_namespace.h $(objroot)include/jemalloc/internal/private_namespace_jet.h\nPRIVATE_NAMESPACE_GEN_HDRS := $(PRIVATE_NAMESPACE_HDRS:%.h=%.gen.h)\nC_SYM_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.sym.$(O))\nC_SYMS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.sym)\nC_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.$(O))\nCPP_OBJS := $(CPP_SRCS:$(srcroot)%.cpp=$(objroot)%.$(O))\nC_PIC_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.pic.$(O))\nCPP_PIC_OBJS := $(CPP_SRCS:$(srcroot)%.cpp=$(objroot)%.pic.$(O))\nC_JET_SYM_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.sym.$(O))\nC_JET_SYMS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.sym)\nC_JET_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.$(O))\nC_TESTLIB_UNIT_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.unit.$(O))\nC_TESTLIB_INTEGRATION_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))\nC_UTIL_INTEGRATION_OBJS := $(C_UTIL_INTEGRATION_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))\nC_TESTLIB_STRESS_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.stress.$(O))\nC_TESTLIB_OBJS := $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(C_TESTLIB_STRESS_OBJS)\n\nTESTS_UNIT_OBJS := $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%.$(O))\nTESTS_INTEGRATION_OBJS := $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%.$(O))\nTESTS_INTEGRATION_CPP_OBJS := $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%.$(O))\nTESTS_STRESS_OBJS := $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%.$(O))\nTESTS_OBJS := $(TESTS_UNIT_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_STRESS_OBJS)\nTESTS_CPP_OBJS := $(TESTS_INTEGRATION_CPP_OBJS)\n\n.PHONY: all dist build_doc_html build_doc_man build_doc\n.PHONY: install_bin install_include install_lib\n.PHONY: install_doc_html install_doc_man install_doc install\n.PHONY: tests check clean distclean relclean\n\n.SECONDARY : $(PRIVATE_NAMESPACE_GEN_HDRS) $(TESTS_OBJS) $(TESTS_CPP_OBJS)\n\n# Default target.\nall: build_lib\n\ndist: build_doc\n\n$(objroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl\n\t$(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<\n\n$(objroot)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 $(C_SYM_OBJS:%.$(O)=%.d)\n-include $(C_OBJS:%.$(O)=%.d)\n-include $(CPP_OBJS:%.$(O)=%.d)\n-include $(C_PIC_OBJS:%.$(O)=%.d)\n-include $(CPP_PIC_OBJS:%.$(O)=%.d)\n-include $(C_JET_SYM_OBJS:%.$(O)=%.d)\n-include $(C_JET_OBJS:%.$(O)=%.d)\n-include $(C_TESTLIB_OBJS:%.$(O)=%.d)\n-include $(TESTS_OBJS:%.$(O)=%.d)\n-include $(TESTS_CPP_OBJS:%.$(O)=%.d)\nendif\n\n$(C_SYM_OBJS): $(objroot)src/%.sym.$(O): $(srcroot)src/%.c\n$(C_SYM_OBJS): CPPFLAGS += -DJEMALLOC_NO_PRIVATE_NAMESPACE\n$(C_SYMS): $(objroot)src/%.sym: $(objroot)src/%.sym.$(O)\n$(C_OBJS): $(objroot)src/%.$(O): $(srcroot)src/%.c\n$(CPP_OBJS): $(objroot)src/%.$(O): $(srcroot)src/%.cpp\n$(C_PIC_OBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.c\n$(C_PIC_OBJS): CFLAGS += $(PIC_CFLAGS)\n$(CPP_PIC_OBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.cpp\n$(CPP_PIC_OBJS): CXXFLAGS += $(PIC_CFLAGS)\n$(C_JET_SYM_OBJS): $(objroot)src/%.jet.sym.$(O): $(srcroot)src/%.c\n$(C_JET_SYM_OBJS): CPPFLAGS += -DJEMALLOC_JET -DJEMALLOC_NO_PRIVATE_NAMESPACE\n$(C_JET_SYMS): $(objroot)src/%.jet.sym: $(objroot)src/%.jet.sym.$(O)\n$(C_JET_OBJS): $(objroot)src/%.jet.$(O): $(srcroot)src/%.c\n$(C_JET_OBJS): CPPFLAGS += -DJEMALLOC_JET\n$(C_TESTLIB_UNIT_OBJS): $(objroot)test/src/%.unit.$(O): $(srcroot)test/src/%.c\n$(C_TESTLIB_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST\n$(C_TESTLIB_INTEGRATION_OBJS): $(objroot)test/src/%.integration.$(O): $(srcroot)test/src/%.c\n$(C_TESTLIB_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST\n$(C_UTIL_INTEGRATION_OBJS): $(objroot)src/%.integration.$(O): $(srcroot)src/%.c\n$(C_TESTLIB_STRESS_OBJS): $(objroot)test/src/%.stress.$(O): $(srcroot)test/src/%.c\n$(C_TESTLIB_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST -DJEMALLOC_STRESS_TESTLIB\n$(C_TESTLIB_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include\n$(TESTS_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST\n$(TESTS_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST\n$(TESTS_INTEGRATION_CPP_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_CPP_TEST\n$(TESTS_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST\n$(TESTS_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.c\n$(TESTS_CPP_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.cpp\n$(TESTS_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include\n$(TESTS_CPP_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include\nifneq ($(IMPORTLIB),$(SO))\n$(CPP_OBJS) $(C_SYM_OBJS) $(C_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS): CPPFLAGS += -DDLLEXPORT\nendif\n\n# Dependencies.\nifndef CC_MM\nHEADER_DIRS = $(srcroot)include/jemalloc/internal \\\n\t$(objroot)include/jemalloc $(objroot)include/jemalloc/internal\nHEADERS = $(filter-out $(PRIVATE_NAMESPACE_HDRS),$(wildcard $(foreach dir,$(HEADER_DIRS),$(dir)/*.h)))\n$(C_SYM_OBJS) $(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS) $(TESTS_CPP_OBJS): $(HEADERS)\n$(TESTS_OBJS) $(TESTS_CPP_OBJS): $(objroot)test/include/test/jemalloc_test.h\nendif\n\n$(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_INTEGRATION_CPP_OBJS): $(objroot)include/jemalloc/internal/private_namespace.h\n$(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_STRESS_OBJS) $(TESTS_UNIT_OBJS) $(TESTS_STRESS_OBJS): $(objroot)include/jemalloc/internal/private_namespace_jet.h\n\n$(C_SYM_OBJS) $(C_OBJS) $(C_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS): %.$(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\n$(C_SYMS): %.sym:\n\t@mkdir -p $(@D)\n\t$(DUMP_SYMS) $< | $(AWK) -f $(objroot)include/jemalloc/internal/private_symbols.awk > $@\n\n$(C_JET_SYMS): %.sym:\n\t@mkdir -p $(@D)\n\t$(DUMP_SYMS) $< | $(AWK) -f $(objroot)include/jemalloc/internal/private_symbols_jet.awk > $@\n\n$(objroot)include/jemalloc/internal/private_namespace.gen.h: $(C_SYMS)\n\t$(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@\n\n$(objroot)include/jemalloc/internal/private_namespace_jet.gen.h: $(C_JET_SYMS)\n\t$(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@\n\n%.h: %.gen.h\n\t@if ! `cmp -s $< $@` ; then echo \"cp $< $<\"; cp $< $@ ; fi\n\n$(CPP_OBJS) $(CPP_PIC_OBJS) $(TESTS_CPP_OBJS): %.$(O):\n\t@mkdir -p $(@D)\n\t$(CXX) $(CXXFLAGS) -c $(CPPFLAGS) $(CTARGET) $<\nifdef CC_MM\n\t@$(CXX) -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),$(C_PIC_OBJS),$(C_OBJS)) $(if $(PIC_CFLAGS),$(CPP_PIC_OBJS),$(CPP_OBJS))\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) : $(C_PIC_OBJS) $(CPP_PIC_OBJS)\n$(objroot)lib/$(LIBJEMALLOC).$(A) : $(C_OBJS) $(CPP_OBJS)\n$(objroot)lib/$(LIBJEMALLOC)_s.$(A) : $(C_OBJS) $(CPP_OBJS)\n\n$(STATIC_LIBS):\n\t@mkdir -p $(@D)\n\t$(AR) $(ARFLAGS)@AROUT@ $+\n\n$(objroot)test/unit/%$(EXE): $(objroot)test/unit/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS)\n\t@mkdir -p $(@D)\n\t$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)\n\n$(objroot)test/integration/%$(EXE): $(objroot)test/integration/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\n\t@mkdir -p $(@D)\n\t$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LJEMALLOC) $(LDFLAGS) $(filter-out -lm,$(filter -lrt -lpthread -lstdc++,$(LIBS))) $(LM) $(EXTRA_LDFLAGS)\n\n$(objroot)test/integration/cpp/%$(EXE): $(objroot)test/integration/cpp/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\n\t@mkdir -p $(@D)\n\t$(CXX) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(EXTRA_LDFLAGS)\n\n$(objroot)test/stress/%$(EXE): $(objroot)test/stress/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)\n\t@mkdir -p $(@D)\n\t$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)\n\nbuild_lib_shared: $(DSOS)\nbuild_lib_static: $(STATIC_LIBS)\nbuild_lib: build_lib_shared build_lib_static\n\ninstall_bin:\n\t$(INSTALL) -d $(BINDIR)\n\t@for b in $(BINS); do \\\n\techo \"$(INSTALL) -m 755 $$b $(BINDIR)\"; \\\n\t$(INSTALL) -m 755 $$b $(BINDIR); \\\ndone\n\ninstall_include:\n\t$(INSTALL) -d $(INCLUDEDIR)/jemalloc\n\t@for h in $(C_HDRS); do \\\n\techo \"$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc\"; \\\n\t$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc; \\\ndone\n\ninstall_lib_shared: $(DSOS)\n\t$(INSTALL) -d $(LIBDIR)\n\t$(INSTALL) -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\t$(INSTALL) -d $(LIBDIR)\n\t@for l in $(STATIC_LIBS); do \\\n\techo \"$(INSTALL) -m 755 $$l $(LIBDIR)\"; \\\n\t$(INSTALL) -m 755 $$l $(LIBDIR); \\\ndone\n\ninstall_lib_pc: $(PC)\n\t$(INSTALL) -d $(LIBDIR)/pkgconfig\n\t@for l in $(PC); do \\\n\techo \"$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig\"; \\\n\t$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig; \\\ndone\n\ninstall_lib: install_lib_shared install_lib_static install_lib_pc\n\ninstall_doc_html:\n\t$(INSTALL) -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\t$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \\\ndone\n\ninstall_doc_man:\n\t$(INSTALL) -d $(MANDIR)/man3\n\t@for d in $(DOCS_MAN3); do \\\n\techo \"$(INSTALL) -m 644 $$d $(MANDIR)/man3\"; \\\n\t$(INSTALL) -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_unit: $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%$(EXE))\ntests_integration: $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%$(EXE)) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%$(EXE))\ntests_stress: $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%$(EXE))\ntests: tests_unit tests_integration tests_stress\n\ncheck_unit_dir:\n\t@mkdir -p $(objroot)test/unit\ncheck_integration_dir:\n\t@mkdir -p $(objroot)test/integration\nstress_dir:\n\t@mkdir -p $(objroot)test/stress\ncheck_dir: check_unit_dir check_integration_dir\n\ncheck_unit: tests_unit check_unit_dir\n\t$(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)\ncheck_integration_prof: tests_integration check_integration_dir\nifeq ($(enable_prof), 1)\n\t$(MALLOC_CONF)=\"prof:true\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\n\t$(MALLOC_CONF)=\"prof:true,prof_active:false\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\nendif\ncheck_integration_decay: tests_integration check_integration_dir\n\t$(MALLOC_CONF)=\"dirty_decay_ms:-1,muzzy_decay_ms:-1\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\n\t$(MALLOC_CONF)=\"dirty_decay_ms:0,muzzy_decay_ms:0\" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\ncheck_integration: tests_integration check_integration_dir\n\t$(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)\nstress: tests_stress stress_dir\n\t$(SHELL) $(objroot)test/test.sh $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%)\ncheck: check_unit check_integration check_integration_decay check_integration_prof\n\nclean:\n\trm -f $(PRIVATE_NAMESPACE_HDRS)\n\trm -f $(PRIVATE_NAMESPACE_GEN_HDRS)\n\trm -f $(C_SYM_OBJS)\n\trm -f $(C_SYMS)\n\trm -f $(C_OBJS)\n\trm -f $(CPP_OBJS)\n\trm -f $(C_PIC_OBJS)\n\trm -f $(CPP_PIC_OBJS)\n\trm -f $(C_JET_SYM_OBJS)\n\trm -f $(C_JET_SYMS)\n\trm -f $(C_JET_OBJS)\n\trm -f $(C_TESTLIB_OBJS)\n\trm -f $(C_SYM_OBJS:%.$(O)=%.d)\n\trm -f $(C_OBJS:%.$(O)=%.d)\n\trm -f $(CPP_OBJS:%.$(O)=%.d)\n\trm -f $(C_PIC_OBJS:%.$(O)=%.d)\n\trm -f $(CPP_PIC_OBJS:%.$(O)=%.d)\n\trm -f $(C_JET_SYM_OBJS:%.$(O)=%.d)\n\trm -f $(C_JET_OBJS:%.$(O)=%.d)\n\trm -f $(C_TESTLIB_OBJS:%.$(O)=%.d)\n\trm -f $(TESTS_OBJS:%.$(O)=%$(EXE))\n\trm -f $(TESTS_OBJS)\n\trm -f $(TESTS_OBJS:%.$(O)=%.d)\n\trm -f $(TESTS_OBJS:%.$(O)=%.out)\n\trm -f $(TESTS_CPP_OBJS:%.$(O)=%$(EXE))\n\trm -f $(TESTS_CPP_OBJS)\n\trm -f $(TESTS_CPP_OBJS:%.$(O)=%.d)\n\trm -f $(TESTS_CPP_OBJS:%.$(O)=%.out)\n\trm -f $(DSOS) $(STATIC_LIBS)\n\ndistclean: clean\n\trm -f $(objroot)bin/jemalloc-config\n\trm -f $(objroot)bin/jemalloc.sh\n\trm -f $(objroot)bin/jeprof\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 $(objroot)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": "Chapter07/redis-6.0.3/deps/jemalloc/README",
    "content": "jemalloc is a general purpose malloc(3) implementation that emphasizes\nfragmentation avoidance and scalable concurrency support.  jemalloc first came\ninto use as the FreeBSD libc allocator in 2005, and since then it has found its\nway into numerous applications that rely on its predictable behavior.  In 2010\njemalloc development efforts broadened to include developer support features\nsuch as heap profiling and extensive monitoring/tuning hooks.  Modern jemalloc\nreleases continue to be integrated back into FreeBSD, and therefore versatility\nremains critical.  Ongoing development efforts trend toward making jemalloc\namong the best allocators for a broad range of demanding applications, and\neliminating/mitigating weaknesses that have practical repercussions for real\nworld applications.\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://jemalloc.net/\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/TUNING.md",
    "content": "This document summarizes the common approaches for performance fine tuning with\njemalloc (as of 5.1.0).  The default configuration of jemalloc tends to work\nreasonably well in practice, and most applications should not have to tune any\noptions. However, in order to cover a wide range of applications and avoid\npathological cases, the default setting is sometimes kept conservative and\nsuboptimal, even for many common workloads.  When jemalloc is properly tuned for\na specific application / workload, it is common to improve system level metrics\nby a few percent, or make favorable trade-offs.\n\n\n## Notable runtime options for performance tuning\n\nRuntime options can be set via\n[malloc_conf](http://jemalloc.net/jemalloc.3.html#tuning).\n\n* [background_thread](http://jemalloc.net/jemalloc.3.html#background_thread)\n\n    Enabling jemalloc background threads generally improves the tail latency for\n    application threads, since unused memory purging is shifted to the dedicated\n    background threads.  In addition, unintended purging delay caused by\n    application inactivity is avoided with background threads.\n\n    Suggested: `background_thread:true` when jemalloc managed threads can be\n    allowed.\n\n* [metadata_thp](http://jemalloc.net/jemalloc.3.html#opt.metadata_thp)\n\n    Allowing jemalloc to utilize transparent huge pages for its internal\n    metadata usually reduces TLB misses significantly, especially for programs\n    with large memory footprint and frequent allocation / deallocation\n    activities.  Metadata memory usage may increase due to the use of huge\n    pages.\n\n    Suggested for allocation intensive programs: `metadata_thp:auto` or\n    `metadata_thp:always`, which is expected to improve CPU utilization at a\n    small memory cost.\n\n* [dirty_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.dirty_decay_ms) and\n  [muzzy_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.muzzy_decay_ms)\n\n    Decay time determines how fast jemalloc returns unused pages back to the\n    operating system, and therefore provides a fairly straightforward trade-off\n    between CPU and memory usage.  Shorter decay time purges unused pages faster\n    to reduces memory usage (usually at the cost of more CPU cycles spent on\n    purging), and vice versa.\n\n    Suggested: tune the values based on the desired trade-offs.\n\n* [narenas](http://jemalloc.net/jemalloc.3.html#opt.narenas)\n\n    By default jemalloc uses multiple arenas to reduce internal lock contention.\n    However high arena count may also increase overall memory fragmentation,\n    since arenas manage memory independently.  When high degree of parallelism\n    is not expected at the allocator level, lower number of arenas often\n    improves memory usage.\n\n    Suggested: if low parallelism is expected, try lower arena count while\n    monitoring CPU and memory usage.\n\n* [percpu_arena](http://jemalloc.net/jemalloc.3.html#opt.percpu_arena)\n\n    Enable dynamic thread to arena association based on running CPU.  This has\n    the potential to improve locality, e.g. when thread to CPU affinity is\n    present.\n    \n    Suggested: try `percpu_arena:percpu` or `percpu_arena:phycpu` if\n    thread migration between processors is expected to be infrequent.\n\nExamples:\n\n* High resource consumption application, prioritizing CPU utilization:\n\n    `background_thread:true,metadata_thp:auto` combined with relaxed decay time\n    (increased `dirty_decay_ms` and / or `muzzy_decay_ms`,\n    e.g. `dirty_decay_ms:30000,muzzy_decay_ms:30000`).\n\n* High resource consumption application, prioritizing memory usage:\n\n    `background_thread:true` combined with shorter decay time (decreased\n    `dirty_decay_ms` and / or `muzzy_decay_ms`,\n    e.g. `dirty_decay_ms:5000,muzzy_decay_ms:5000`), and lower arena count\n    (e.g. number of CPUs).\n\n* Low resource consumption application:\n\n    `narenas:1,lg_tcache_max:13` combined with shorter decay time (decreased\n    `dirty_decay_ms` and / or `muzzy_decay_ms`,e.g.\n    `dirty_decay_ms:1000,muzzy_decay_ms:0`).\n\n* Extremely conservative -- minimize memory usage at all costs, only suitable when\nallocation activity is very rare:\n\n    `narenas:1,tcache:false,dirty_decay_ms:0,muzzy_decay_ms:0`\n\nNote that it is recommended to combine the options with `abort_conf:true` which\naborts immediately on illegal options.\n\n## Beyond runtime options\n\nIn addition to the runtime options, there are a number of programmatic ways to\nimprove application performance with jemalloc.\n\n* [Explicit arenas](http://jemalloc.net/jemalloc.3.html#arenas.create)\n\n    Manually created arenas can help performance in various ways, e.g. by\n    managing locality and contention for specific usages.  For example,\n    applications can explicitly allocate frequently accessed objects from a\n    dedicated arena with\n    [mallocx()](http://jemalloc.net/jemalloc.3.html#MALLOCX_ARENA) to improve\n    locality.  In addition, explicit arenas often benefit from individually\n    tuned options, e.g. relaxed [decay\n    time](http://jemalloc.net/jemalloc.3.html#arena.i.dirty_decay_ms) if\n    frequent reuse is expected.\n\n* [Extent hooks](http://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks)\n\n    Extent hooks allow customization for managing underlying memory.  One use\n    case for performance purpose is to utilize huge pages -- for example,\n    [HHVM](https://github.com/facebook/hhvm/blob/master/hphp/util/alloc.cpp)\n    uses explicit arenas with customized extent hooks to manage 1GB huge pages\n    for frequently accessed data, which reduces TLB misses significantly.\n\n* [Explicit thread-to-arena\n  binding](http://jemalloc.net/jemalloc.3.html#thread.arena)\n\n    It is common for some threads in an application to have different memory\n    access / allocation patterns.  Threads with heavy workloads often benefit\n    from explicit binding, e.g. binding very active threads to dedicated arenas\n    may reduce contention at the allocator level.\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/jemalloc/bin/jemalloc-config.in",
    "content": "#!/bin/sh\n\nusage() {\n\tcat <<EOF\nUsage:\n  @BINDIR@/jemalloc-config <option>\nOptions:\n  --help | -h  : Print usage.\n  --version    : Print jemalloc version.\n  --revision   : Print shared library revision number.\n  --config     : Print configure options used to build jemalloc.\n  --prefix     : Print installation directory prefix.\n  --bindir     : Print binary installation directory.\n  --datadir    : Print data installation directory.\n  --includedir : Print include installation directory.\n  --libdir     : Print library installation directory.\n  --mandir     : Print manual page installation directory.\n  --cc         : Print compiler used to build jemalloc.\n  --cflags     : Print compiler flags used to build jemalloc.\n  --cppflags   : Print preprocessor flags used to build jemalloc.\n  --cxxflags   : Print C++ compiler flags used to build jemalloc.\n  --ldflags    : Print library flags used to build jemalloc.\n  --libs       : Print libraries jemalloc was linked against.\nEOF\n}\n\nprefix=\"@prefix@\"\nexec_prefix=\"@exec_prefix@\"\n\ncase \"$1\" in\n--help | -h)\n\tusage\n\texit 0\n\t;;\n--version)\n\techo \"@jemalloc_version@\"\n\t;;\n--revision)\n\techo \"@rev@\"\n\t;;\n--config)\n\techo \"@CONFIG@\"\n\t;;\n--prefix)\n\techo \"@PREFIX@\"\n\t;;\n--bindir)\n\techo \"@BINDIR@\"\n\t;;\n--datadir)\n\techo \"@DATADIR@\"\n\t;;\n--includedir)\n\techo \"@INCLUDEDIR@\"\n\t;;\n--libdir)\n\techo \"@LIBDIR@\"\n\t;;\n--mandir)\n\techo \"@MANDIR@\"\n\t;;\n--cc)\n\techo \"@CC@\"\n\t;;\n--cflags)\n\techo \"@CFLAGS@\"\n\t;;\n--cppflags)\n\techo \"@CPPFLAGS@\"\n\t;;\n--cxxflags)\n\techo \"@CXXFLAGS@\"\n\t;;\n--ldflags)\n\techo \"@LDFLAGS@ @EXTRA_LDFLAGS@\"\n\t;;\n--libs)\n\techo \"@LIBS@\"\n\t;;\n*)\n\tusage\n\texit 1\nesac\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/jemalloc/bin/jeprof.in",
    "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/jeprof \"program\" \"profile\"\n#   Enters \"interactive\" mode\n#\n# % tools/jeprof --text \"program\" \"profile\"\n#   Generates one line per procedure\n#\n# % tools/jeprof --gv \"program\" \"profile\"\n#   Generates annotated call-graph and displays via \"gv\"\n#\n# % tools/jeprof --gv --focus=Mutex \"program\" \"profile\"\n#   Restrict to code paths that involve an entry that matches \"Mutex\"\n#\n# % tools/jeprof --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/jeprof --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/jeprof --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;\nuse Cwd;\n\nmy $JEPROF_VERSION = \"@jemalloc_version@\";\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 JEPROF_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\", \"--fail\");\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:\njeprof [options] <program> <profiles>\n   <profiles> is a space separated list of profile names.\njeprof [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).\njeprof [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     jeprof http://myserver.com:80$HEAP_PAGE\n   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).\njeprof --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 jeprof 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 backtraces with nodes matching <regexp>\n   --thread=<n>        Show profile for thread <n>\n   --ignore=<regexp>   Ignore backtraces with 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   --retain=<regexp>   Retain only nodes that match <regexp>\n   --exclude=<regexp>  Exclude all nodes that match <regexp>\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   JEPROF_TMPDIR        Profiles directory. Defaults to \\$HOME/jeprof\n   JEPROF_TOOLS         Prefix for object tools pathnames\n\nExamples:\n\njeprof /bin/ls ls.prof\n                       Enters \"interactive\" mode\njeprof --text /bin/ls ls.prof\n                       Outputs one line per procedure\njeprof --web /bin/ls ls.prof\n                       Displays annotated call-graph in web browser\njeprof --gv /bin/ls ls.prof\n                       Displays annotated call-graph via 'gv'\njeprof --gv --focus=Mutex /bin/ls ls.prof\n                       Restricts to code paths including a .*Mutex.* entry\njeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof\n                       Code paths including Mutex but not string\njeprof --list=getdir /bin/ls ls.prof\n                       (Per-line) annotated source listing for getdir()\njeprof --disasm=getdir /bin/ls ls.prof\n                       (Per-PC) annotated disassembly for getdir()\n\njeprof http://localhost:1234/\n                       Enters \"interactive\" mode\njeprof --text localhost:1234\n                       Outputs one line per procedure for localhost:1234\njeprof --raw localhost:1234 > ./local.raw\njeprof --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\njeprof (part of jemalloc $JEPROF_VERSION)\nbased on pprof (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/jeprof$$.sym\";\n  $main::tmpfile_ps = \"/tmp/jeprof$$\";\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_thread = undef;\n  $main::opt_ignore = '';\n  $main::opt_scale = 0;\n  $main::opt_heapcheck = 0;\n  $main::opt_retain = '';\n  $main::opt_exclude = '';\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             \"thread=s\"       => \\$main::opt_thread,\n             \"ignore=s\"       => \\$main::opt_ignore,\n             \"scale=i\"        => \\$main::opt_scale,\n             \"heapcheck\"      => \\$main::opt_heapcheck,\n             \"retain=s\"       => \\$main::opt_retain,\n             \"exclude=s\"      => \\$main::opt_exclude,\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 FilterAndPrint {\n  my ($profile, $symbols, $libs, $thread) = @_;\n\n  # Get total data in profile\n  my $total = TotalProfile($profile);\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 %s\\n\",\n               (defined($thread) ? \" (t$thread)\" : \"\"),\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\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  # 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  if (!defined($main::opt_thread)) {\n    FilterAndPrint($profile, $symbols, $libs);\n  }\n  if (defined($data->{threads})) {\n    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {\n      if (defined($main::opt_thread) &&\n          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {\n        my $thread_profile = $data->{threads}{$thread};\n        FilterAndPrint($thread_profile, $symbols, $libs, $thread);\n      }\n    }\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 jeprof!  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 'jeprof';\n    while ( defined ($_ = $term->readline('(jeprof) '))) {\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 \"(jeprof) \";\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 jeprof 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 jeprof\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  my $profile_marker;\n  if ($main::profile_type eq 'heap') {\n    $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } elsif ($main::profile_type eq 'growth') {\n    $GROWTH_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } elsif ($main::profile_type eq 'contention') {\n    $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } else { # elsif ($main::profile_type eq 'cpu')\n    $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  }\n\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    # --raw/http: For everything to work correctly for non-remote profiles, we\n    # would need to extend PrintProfileData() to handle all possible profile\n    # types, re-enable the code that is currently disabled in ReadCPUProfile()\n    # and FixCallerAddresses(), and remove the remote profile dumping code in\n    # the block above.\n    die \"--raw/http: jeprof can only dump remote profiles for --raw\\n\";\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 jeprof_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=\\\"jeprof_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 FilterFrames {\n  my $symbols = shift;\n  my $profile = shift;\n\n  if ($main::opt_retain eq '' && $main::opt_exclude eq '') {\n    return $profile;\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      my $sym;\n      if (exists($symbols->{$a})) {\n        $sym = $symbols->{$a}->[0];\n      } else {\n        $sym = $a;\n      }\n      if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {\n        next;\n      }\n      if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {\n        next;\n      }\n      push(@path, $a);\n    }\n    if (scalar(@path) > 0) {\n      my $reduced_path = join(\"\\n\", @path);\n      AddEntry($result, $reduced_path, $count);\n    }\n  }\n\n  return $result;\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 ('@JEMALLOC_PREFIX@calloc',\n                      'cfree',\n                      '@JEMALLOC_PREFIX@malloc',\n                      'newImpl',\n                      'void* newImpl',\n                      '@JEMALLOC_PREFIX@free',\n                      '@JEMALLOC_PREFIX@memalign',\n                      '@JEMALLOC_PREFIX@posix_memalign',\n                      '@JEMALLOC_PREFIX@aligned_alloc',\n                      'pvalloc',\n                      '@JEMALLOC_PREFIX@valloc',\n                      '@JEMALLOC_PREFIX@realloc',\n                      '@JEMALLOC_PREFIX@mallocx',\n                      '@JEMALLOC_PREFIX@rallocx',\n                      '@JEMALLOC_PREFIX@xallocx',\n                      '@JEMALLOC_PREFIX@dallocx',\n                      '@JEMALLOC_PREFIX@sdallocx',\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          # Throw away the portion of the backtrace seen so far, under the\n          # assumption that previous frames were for functions internal to the\n          # allocator.\n          @path = ();\n          next;\n        }\n      }\n      push(@path, $a);\n    }\n    my $reduced_path = join(\"\\n\", @path);\n    AddEntry($result, $reduced_path, $count);\n  }\n\n  $result = FilterFrames($symbols, $result);\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 = @_;\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\nsub URLEncode {\n  my $str = shift;\n  $str =~ s/([^A-Za-z0-9\\-_.!~*'()])/ sprintf \"%%%02x\", ord $1 /eg;\n  return $str;\n}\n\nsub AppendSymbolFilterParams {\n  my $url = shift;\n  my @params = ();\n  if ($main::opt_retain ne '') {\n    push(@params, sprintf(\"retain=%s\", URLEncode($main::opt_retain)));\n  }\n  if ($main::opt_exclude ne '') {\n    push(@params, sprintf(\"exclude=%s\", URLEncode($main::opt_exclude)));\n  }\n  if (scalar @params > 0) {\n    $url = sprintf(\"%s?%s\", $url, join(\"&\", @params));\n  }\n  return $url;\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      $url = AppendSymbolFilterParams($url);\n      $command_line = ShellEscape(@URL_FETCHER, \"-d\", \"\\@$main::tmpfile_sym\",\n                                  $url);\n    } else {\n      $url = AppendSymbolFilterParams($url);\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      # Set $profile_type for consumption by PrintSymbolizedProfile.\n      $main::profile_type = 'cpu';\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      # Set $profile_type for consumption by PrintSymbolizedProfile.\n      if ($path =~ m/$HEAP_PAGE/) {\n        $main::profile_type = 'heap';\n      } elsif ($path =~ m/$GROWTH_PAGE/) {\n        $main::profile_type = 'growth';\n      } elsif ($path =~ m/$CONTENTION_PAGE/) {\n        $main::profile_type = 'contention';\n      }\n    }\n\n    my $profile_dir = $ENV{\"JEPROF_TMPDIR\"} || ($ENV{HOME} . \"/jeprof\");\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 jeprof, which jeprof 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->{threads}     Map of thread IDs to profile objects\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  $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $heap_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  if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {\n    # Skip \"--- ...\" line for profile types that have their own headers.\n    $header = ReadProfileHeader(*PROFILE) || \"\";\n  }\n\n  $main::profile_type = '';\n\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/^heap/) {\n    $main::profile_type = 'heap';\n    $result = ReadThreadedHeapProfile($prog, $fname, $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  # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()\n  # dumps unadjusted profiles.\n  {\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      $pc--;\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 HeapProfileIndex {\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  return $index;\n}\n\nsub ReadMappedLibraries {\n  my $fh = shift;\n  my $map = \"\";\n  # Read the /proc/self/maps data\n  while (<$fh>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    $map .= $_;\n  }\n  return $map;\n}\n\nsub ReadMemoryMap {\n  my $fh = shift;\n  my $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  return $map;\n}\n\nsub AdjustSamples {\n  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;\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  return ($n1, $s1, $n2, $s2);\n}\n\nsub ReadHeapProfile {\n  my $prog = shift;\n  local *PROFILE = shift;\n  my $header = shift;\n\n  my $index = HeapProfileIndex();\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      $map .= ReadMappedLibraries(*PROFILE);\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      $map .= ReadMemoryMap(*PROFILE);\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      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,\n                                 $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 ReadThreadedHeapProfile {\n  my ($prog, $fname, $header) = @_;\n\n  my $index = HeapProfileIndex();\n  my $sampling_algorithm = 0;\n  my $sample_adjustment = 0;\n  chomp($header);\n  my $type = \"unknown\";\n  # Assuming a very specific type of header for now.\n  if ($header =~ m\"^heap_v2/(\\d+)\") {\n    $type = \"_v2\";\n    $sampling_algorithm = 2;\n    $sample_adjustment = int($1);\n  }\n  if ($type ne \"_v2\" || !defined($sample_adjustment)) {\n    die \"Threaded heap profiles require v2 sampling with a sample rate\\n\";\n  }\n\n  my $profile = {};\n  my $thread_profiles = {};\n  my $pcs = {};\n  my $map = \"\";\n  my $stack = \"\";\n\n  while (<PROFILE>) {\n    s/\\r//g;\n    if (/^MAPPED_LIBRARIES:/) {\n      $map .= ReadMappedLibraries(*PROFILE);\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      $map .= ReadMemoryMap(*PROFILE);\n      last;\n    }\n\n    # Read entry of the form:\n    # @ a1 a2 ... an\n    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]\n    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]\n    #     ...\n    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]\n    s/^\\s*//;\n    s/\\s*$//;\n    if (m/^@\\s+(.*)$/) {\n      $stack = $1;\n    } elsif (m/^\\s*(t(\\*|\\d+)):\\s+(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\]$/) {\n      if ($stack eq \"\") {\n        # Still in the header, so this is just a per-thread summary.\n        next;\n      }\n      my $thread = $2;\n      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);\n      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,\n                                 $n1, $s1, $n2, $s2);\n      if ($thread eq \"*\") {\n        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);\n      } else {\n        if (!exists($thread_profiles->{$thread})) {\n          $thread_profiles->{$thread} = {};\n        }\n        AddEntries($thread_profiles->{$thread}, $pcs,\n                   FixCallerAddresses($stack), $counts[$index]);\n      }\n    }\n  }\n\n  my $r = {};\n  $r->{version} = \"heap\";\n  $r->{period} = 1;\n  $r->{profile} = $profile;\n  $r->{threads} = $thread_profiles;\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 jeprof\n        # So we just silently ignore it for now\n      } elsif ($variable eq \"discarded samples\") {\n        # Currently nothing is done with this value in jeprof\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|^/|) {\n      if (-f \"/usr/lib/debug$file\") {\n        return \"/usr/lib/debug$file\";\n      } elsif (-f \"/usr/lib/debug$file.debug\") {\n        return \"/usr/lib/debug$file.debug\";\n      }\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 = Cwd::abs_path(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    } elsif (($l =~ /^($h)-($h)\\s+..x.\\s+($h)\\s+\\S+:\\S+\\s+\\d+\\s+(\\S+)$/i) && ($4 eq $prog)) {\n      # PIEs and address space randomization do not play well with our\n      # default assumption that main executable is at lowest\n      # addresses. So we're detecting main executable in\n      # /proc/self/maps as well.\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    }\n    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in\n    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)\n    #\n    # Example:\n    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s\n    # o.1 NCH -1\n    elsif ($l =~ /^(0x$h)\\s(0x$h)\\s\\d+\\s\\d+\\s0x$h\\sr-x\\s\\d+\\s\\d+\\s0x\\d+\\s(COW|NCO)\\s(NC|NNC)\\svnode\\s(\\S+\\.so(\\.\\d+)*)/) {\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = $zero_offset;\n      $lib = FindLibrary($5);\n\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    if($main::opt_debug) { printf STDERR \"$start:$finish ($offset) $lib\\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 jeprof --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 jeprof --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 jeprof --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    # Use debug library if it exists\n    my $debug_libname = DebuggingLibrary($libname);\n    if ($debug_libname) {\n        $libname = $debug_libname;\n    }\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) JEPROF_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 jeprof.\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# JEPROF_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 $JEPROF_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{\"JEPROF_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 \\$JEPROF_TOOLS) '$tools'\\n\");\n    }\n  } else {\n    # ... otherwise use the version that exists in the same directory as\n    # jeprof.  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/jeprof 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 \"  jeprof \\\\\\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 parameters 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 jeprof 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": "Chapter07/redis-6.0.3/deps/jemalloc/build-aux/config.guess",
    "content": "#! /bin/sh\n# Attempt to guess a canonical system name.\n#   Copyright 1992-2016 Free Software Foundation, Inc.\n\ntimestamp='2016-10-02'\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 3 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\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n#\n# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.\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\n#\n# Please send patches to <config-patches@gnu.org>.\n\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 1992-2016 Free 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\ncase \"${UNAME_SYSTEM}\" in\nLinux|GNU|GNU/*)\n\t# If the system lacks a compiler, then just pick glibc.\n\t# We could probably try harder.\n\tLIBC=gnu\n\n\teval $set_cc_for_build\n\tcat <<-EOF > $dummy.c\n\t#include <features.h>\n\t#if defined(__UCLIBC__)\n\tLIBC=uclibc\n\t#elif defined(__dietlibc__)\n\tLIBC=dietlibc\n\t#else\n\tLIBC=gnu\n\t#endif\n\tEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`\n\t;;\nesac\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=`(uname -p 2>/dev/null || \\\n\t    /sbin/$sysctl 2>/dev/null || \\\n\t    /usr/sbin/$sysctl 2>/dev/null || \\\n\t    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    earmv*)\n\t\tarch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\\(armv[0-9]\\).*$,\\1,'`\n\t\tendian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\\(eb\\)$,\\1,p'`\n\t\tmachine=${arch}${endian}-unknown\n\t\t;;\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) and ABI.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\tos=netbsdelf\n\t\t;;\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# Determine ABI tags.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\texpr='s/^earmv[0-9]/-eabi/;s/eb$//'\n\t\tabi=`echo ${UNAME_MACHINE_ARCH} | sed -e \"$expr\"`\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/[-_].*//' | cut -d. -f1,2`\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}${abi}\"\n\texit ;;\n    *:Bitrig:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_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    *:LibertyBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\\.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${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    *:Sortix:*:*)\n\techo ${UNAME_MACHINE}-unknown-sortix\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/lslpp ] ; then\n\t\tIBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |\n\t\t\t   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`\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    *:MINGW64*:*)\n\techo ${UNAME_MACHINE}-pc-mingw64\n\texit ;;\n    *:MINGW*:*)\n\techo ${UNAME_MACHINE}-pc-mingw32\n\texit ;;\n    *: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-${LIBC}`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 \"[:upper:]\" \"[:lower:]\"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}\n\texit ;;\n    i*86:Minix:*:*)\n\techo ${UNAME_MACHINE}-pc-minix\n\texit ;;\n    aarch64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    aarch64_be:Linux:*:*)\n\tUNAME_MACHINE=aarch64_be\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\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=gnulibc1 ; fi\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    arc:Linux:*:* | arceb:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${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-${LIBC}\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-${LIBC}eabi\n\t    else\n\t\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf\n\t    fi\n\tfi\n\texit ;;\n    avr32*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    cris:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    crisv32:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    e2k:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    frv:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    hexagon:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    i*86:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    ia64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    k1om:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m32r*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m68*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\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-${LIBC}\"; exit; }\n\t;;\n    mips64el:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    openrisc*:Linux:*:*)\n\techo or1k-unknown-linux-${LIBC}\n\texit ;;\n    or32:Linux:*:* | or1k*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    padre:Linux:*:*)\n\techo sparc-unknown-linux-${LIBC}\n\texit ;;\n    parisc64:Linux:*:* | hppa64:Linux:*:*)\n\techo hppa64-unknown-linux-${LIBC}\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-${LIBC} ;;\n\t  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;\n\t  *)    echo hppa-unknown-linux-${LIBC} ;;\n\tesac\n\texit ;;\n    ppc64:Linux:*:*)\n\techo powerpc64-unknown-linux-${LIBC}\n\texit ;;\n    ppc:Linux:*:*)\n\techo powerpc-unknown-linux-${LIBC}\n\texit ;;\n    ppc64le:Linux:*:*)\n\techo powerpc64le-unknown-linux-${LIBC}\n\texit ;;\n    ppcle:Linux:*:*)\n\techo powerpcle-unknown-linux-${LIBC}\n\texit ;;\n    riscv32:Linux:*:* | riscv64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    s390:Linux:*:* | s390x:Linux:*:*)\n\techo ${UNAME_MACHINE}-ibm-linux-${LIBC}\n\texit ;;\n    sh64*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sh*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sparc:Linux:*:* | sparc64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    tile*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    vax:Linux:*:*)\n\techo ${UNAME_MACHINE}-dec-linux-${LIBC}\n\texit ;;\n    x86_64:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    xtensa*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\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 configure 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    x86_64:Haiku:*:*)\n\techo x86_64-unknown-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    SX-ACE:SUPER-UX:*:*)\n\techo sxace-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\teval $set_cc_for_build\n\tif test \"$UNAME_PROCESSOR\" = unknown ; then\n\t    UNAME_PROCESSOR=powerpc\n\tfi\n\tif test `echo \"$UNAME_RELEASE\" | sed -e 's/\\..*//'` -le 10 ; then\n\t    if [ \"$CC_FOR_BUILD\" != no_compiler_found ]; then\n\t\tif (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\tthen\n\t\t    case $UNAME_PROCESSOR in\n\t\t\ti386) UNAME_PROCESSOR=x86_64 ;;\n\t\t\tpowerpc) UNAME_PROCESSOR=powerpc64 ;;\n\t\t    esac\n\t\tfi\n\t    fi\n\telif test \"$UNAME_PROCESSOR\" = i386 ; then\n\t    # Avoid executing cc on OS X 10.9, as it ships with a stub\n\t    # that puts up a graphical alert prompting to install\n\t    # developer tools.  Any system running Mac OS X 10.7 or\n\t    # later (Darwin 11 and later) is required to have a 64-bit\n\t    # processor. This is not true of the ARM version of Darwin\n\t    # that Apple uses in portable devices.\n\t    UNAME_PROCESSOR=x86_64\n\tfi\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 ;;\n    amd64:Isilon\\ OneFS:*:*)\n\techo x86_64-unknown-onefs\n\texit ;;\nesac\n\ncat >&2 <<EOF\n$0: unable to guess system type\n\nThis script (version $timestamp), has failed to recognize the\noperating system you are using. If your script is old, overwrite\nconfig.guess and config.sub with the latest versions from:\n\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess\nand\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub\n\nIf $0 has already been updated, send the following data and any\ninformation you think might be pertinent to config-patches@gnu.org to\nprovide the necessary information 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": "Chapter07/redis-6.0.3/deps/jemalloc/build-aux/config.sub",
    "content": "#! /bin/sh\n# Configuration validation subroutine script.\n#   Copyright 1992-2016 Free Software Foundation, Inc.\n\ntimestamp='2016-11-04'\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 3 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\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n\n\n# Please send patches to <config-patches@gnu.org>.\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\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 or 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 1992-2016 Free 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-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \\\n  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \\\n  kopensolaris*-gnu* | cloudabi*-eabi* | \\\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*178)\n\t\tos=-lynxos178\n\t\t;;\n\t-lynx*5)\n\t\tos=-lynxos5\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 | arceb \\\n\t| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \\\n\t| avr | avr32 \\\n\t| ba \\\n\t| be32 | be64 \\\n\t| bfin \\\n\t| c4x | c8051 | clipper \\\n\t| d10v | d30v | dlx | dsp16xx \\\n\t| e2k | epiphany \\\n\t| fido | fr30 | frv | ft32 \\\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| k1om \\\n\t| le32 | le64 \\\n\t| lm32 \\\n\t| m32c | m32r | m32rle | m68000 | m68k | m88k \\\n\t| maxq | mb | microblaze | microblazeel | 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| mipsisa32r6 | mipsisa32r6el \\\n\t| mipsisa64 | mipsisa64el \\\n\t| mipsisa64r2 | mipsisa64r2el \\\n\t| mipsisa64r6 | mipsisa64r6el \\\n\t| mipsisa64sb1 | mipsisa64sb1el \\\n\t| mipsisa64sr71k | mipsisa64sr71kel \\\n\t| mipsr5900 | mipsr5900el \\\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 | nios2eb | nios2el \\\n\t| ns16k | ns32k \\\n\t| open8 | or1k | or1knd | or32 \\\n\t| pdp10 | pdp11 | pj | pjl \\\n\t| powerpc | powerpc64 | powerpc64le | powerpcle \\\n\t| pru \\\n\t| pyramid \\\n\t| riscv32 | riscv64 \\\n\t| rl78 | rx \\\n\t| score \\\n\t| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]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| visium \\\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\tleon|leon[3-9])\n\t\tbasic_machine=sparc-$basic_machine\n\t\t;;\n\tm6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | 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-* | arceb-* \\\n\t| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \\\n\t| avr-* | avr32-* \\\n\t| ba-* \\\n\t| be32-* | be64-* \\\n\t| bfin-* | bs2000-* \\\n\t| c[123]* | c30-* | [cjt]90-* | c4x-* \\\n\t| c8051-* | clipper-* | craynv-* | cydra-* \\\n\t| d10v-* | d30v-* | dlx-* \\\n\t| e2k-* | 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| k1om-* \\\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-* \\\n\t| microblaze-* | microblazeel-* \\\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| mipsisa32r6-* | mipsisa32r6el-* \\\n\t| mipsisa64-* | mipsisa64el-* \\\n\t| mipsisa64r2-* | mipsisa64r2el-* \\\n\t| mipsisa64r6-* | mipsisa64r6el-* \\\n\t| mipsisa64sb1-* | mipsisa64sb1el-* \\\n\t| mipsisa64sr71k-* | mipsisa64sr71kel-* \\\n\t| mipsr5900-* | mipsr5900el-* \\\n\t| mipstx39-* | mipstx39el-* \\\n\t| mmix-* \\\n\t| mt-* \\\n\t| msp430-* \\\n\t| nds32-* | nds32le-* | nds32be-* \\\n\t| nios-* | nios2-* | nios2eb-* | nios2el-* \\\n\t| none-* | np1-* | ns16k-* | ns32k-* \\\n\t| open8-* \\\n\t| or1k*-* \\\n\t| orion-* \\\n\t| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \\\n\t| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \\\n\t| pru-* \\\n\t| pyramid-* \\\n\t| riscv32-* | riscv64-* \\\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| visium-* \\\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\tasmjs)\n\t\tbasic_machine=asmjs-unknown\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\te500v[12])\n\t\tbasic_machine=powerpc-unknown\n\t\tos=$os\"spe\"\n\t\t;;\n\te500v[12]-*)\n\t\tbasic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=$os\"spe\"\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\tleon-*|leon[3-9]-*)\n\t\tbasic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`\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\tmingw64)\n\t\tbasic_machine=x86_64-pc\n\t\tos=-mingw64\n\t\t;;\n\tmingw32)\n\t\tbasic_machine=i686-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\tmoxiebox)\n\t\tbasic_machine=moxie-unknown\n\t\tos=-moxiebox\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=i686-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)\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)\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 | rdos64)\n\t\tbasic_machine=x86_64-pc\n\t\tos=-rdos\n\t\t;;\n\trdos32)\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* | -plan9* \\\n\t      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \\\n\t      | -aos* | -aros* | -cloudabi* | -sortix* \\\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      | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \\\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      | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \\\n\t      | -linux-newlib* | -linux-musl* | -linux-uclibc* \\\n\t      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \\\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      | -onefs* | -tirtos* | -phoenix* | -fuchsia*)\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-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-ios)\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\tc8051-*)\n\t\tos=-elf\n\t\t;;\n\thexagon-*)\n\t\tos=-elf\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": "Chapter07/redis-6.0.3/deps/jemalloc/build-aux/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": "Chapter07/redis-6.0.3/deps/jemalloc/config.stamp.in",
    "content": ""
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/configure.ac",
    "content": "dnl Process this file with autoconf to produce a configure script.\nAC_PREREQ(2.68)\nAC_INIT([Makefile.in])\n\nAC_CONFIG_AUX_DIR([build-aux])\n\ndnl ============================================================================\ndnl Custom macro definitions.\n\ndnl JE_CONCAT_VVV(r, a, b)\ndnl\ndnl Set $r to the concatenation of $a and $b, with a space separating them iff\ndnl both $a and $b are non-empty.\nAC_DEFUN([JE_CONCAT_VVV],\nif test \"x[$]{$2}\" = \"x\" -o \"x[$]{$3}\" = \"x\" ; then\n  $1=\"[$]{$2}[$]{$3}\"\nelse\n  $1=\"[$]{$2} [$]{$3}\"\nfi\n)\n\ndnl JE_APPEND_VS(a, b)\ndnl\ndnl Set $a to the concatenation of $a and b, with a space separating them iff\ndnl both $a and b are non-empty.\nAC_DEFUN([JE_APPEND_VS],\n  T_APPEND_V=$2\n  JE_CONCAT_VVV($1, $1, T_APPEND_V)\n)\n\nCONFIGURE_CFLAGS=\nSPECIFIED_CFLAGS=\"${CFLAGS}\"\ndnl JE_CFLAGS_ADD(cflag)\ndnl\ndnl CFLAGS is the concatenation of CONFIGURE_CFLAGS and SPECIFIED_CFLAGS\ndnl (ignoring EXTRA_CFLAGS, which does not impact configure tests.  This macro\ndnl appends to CONFIGURE_CFLAGS and regenerates CFLAGS.\nAC_DEFUN([JE_CFLAGS_ADD],\n[\nAC_MSG_CHECKING([whether compiler supports $1])\nT_CONFIGURE_CFLAGS=\"${CONFIGURE_CFLAGS}\"\nJE_APPEND_VS(CONFIGURE_CFLAGS, $1)\nJE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[\n]], [[\n    return 0;\n]])],\n              [je_cv_cflags_added=$1]\n              AC_MSG_RESULT([yes]),\n              [je_cv_cflags_added=]\n              AC_MSG_RESULT([no])\n              [CONFIGURE_CFLAGS=\"${T_CONFIGURE_CFLAGS}\"]\n)\nJE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)\n])\n\ndnl JE_CFLAGS_SAVE()\ndnl JE_CFLAGS_RESTORE()\ndnl\ndnl Save/restore CFLAGS.  Nesting is not supported.\nAC_DEFUN([JE_CFLAGS_SAVE],\nSAVED_CONFIGURE_CFLAGS=\"${CONFIGURE_CFLAGS}\"\n)\nAC_DEFUN([JE_CFLAGS_RESTORE],\nCONFIGURE_CFLAGS=\"${SAVED_CONFIGURE_CFLAGS}\"\nJE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)\n)\n\nCONFIGURE_CXXFLAGS=\nSPECIFIED_CXXFLAGS=\"${CXXFLAGS}\"\ndnl JE_CXXFLAGS_ADD(cxxflag)\nAC_DEFUN([JE_CXXFLAGS_ADD],\n[\nAC_MSG_CHECKING([whether compiler supports $1])\nT_CONFIGURE_CXXFLAGS=\"${CONFIGURE_CXXFLAGS}\"\nJE_APPEND_VS(CONFIGURE_CXXFLAGS, $1)\nJE_CONCAT_VVV(CXXFLAGS, CONFIGURE_CXXFLAGS, SPECIFIED_CXXFLAGS)\nAC_LANG_PUSH([C++])\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n[[\n]], [[\n    return 0;\n]])],\n              [je_cv_cxxflags_added=$1]\n              AC_MSG_RESULT([yes]),\n              [je_cv_cxxflags_added=]\n              AC_MSG_RESULT([no])\n              [CONFIGURE_CXXFLAGS=\"${T_CONFIGURE_CXXFLAGS}\"]\n)\nAC_LANG_POP([C++])\nJE_CONCAT_VVV(CXXFLAGS, CONFIGURE_CXXFLAGS, SPECIFIED_CXXFLAGS)\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\nCONFIG=`echo ${ac_configure_args} | sed -e 's#'\"'\"'\\([^ ]*\\)'\"'\"'#\\1#g'`\nAC_SUBST([CONFIG])\n\ndnl Library revision.\nrev=2\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], [false], [$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\n\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\ndnl check if a cray prgenv wrapper compiler is being used\nje_cv_cray_prgenv_wrapper=\"\"\nif test \"x${PE_ENV}\" != \"x\" ; then\n  case \"${CC}\" in\n    CC|cc)\n\tje_cv_cray_prgenv_wrapper=\"yes\"\n\t;;\n    *)\n       ;;\n  esac\nfi\n\nAC_CACHE_CHECK([whether compiler is cray],\n              [je_cv_cray],\n              [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],\n                                                  [\n#ifndef _CRAYC\n  int fail[-1];\n#endif\n])],\n                            [je_cv_cray=yes],\n                            [je_cv_cray=no])])\n\nif test \"x${je_cv_cray}\" = \"xyes\" ; then\n  AC_CACHE_CHECK([whether cray compiler version is 8.4],\n                [je_cv_cray_84],\n                [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],\n                                                      [\n#if !(_RELEASE_MAJOR == 8 && _RELEASE_MINOR == 4)\n  int fail[-1];\n#endif\n])],\n                              [je_cv_cray_84=yes],\n                              [je_cv_cray_84=no])])\nfi\n\nif test \"x$GCC\" = \"xyes\" ; then\n  JE_CFLAGS_ADD([-std=gnu11])\n  if test \"x$je_cv_cflags_added\" = \"x-std=gnu11\" ; then\n    AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])\n  else\n    JE_CFLAGS_ADD([-std=gnu99])\n    if test \"x$je_cv_cflags_added\" = \"x-std=gnu99\" ; then\n      AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])\n    fi\n  fi\n  JE_CFLAGS_ADD([-Wall])\n  JE_CFLAGS_ADD([-Wshorten-64-to-32])\n  JE_CFLAGS_ADD([-Wsign-compare])\n  JE_CFLAGS_ADD([-Wundef])\n  JE_CFLAGS_ADD([-Wno-format-zero-length])\n  JE_CFLAGS_ADD([-pipe])\n  JE_CFLAGS_ADD([-g3])\nelif test \"x$je_cv_msvc\" = \"xyes\" ; then\n  CC=\"$CC -nologo\"\n  JE_CFLAGS_ADD([-Zi])\n  JE_CFLAGS_ADD([-MT])\n  JE_CFLAGS_ADD([-W3])\n  JE_CFLAGS_ADD([-FS])\n  JE_APPEND_VS(CPPFLAGS, -I${srcdir}/include/msvc_compat)\nfi\nif test \"x$je_cv_cray\" = \"xyes\" ; then\n  dnl cray compiler 8.4 has an inlining bug\n  if test \"x$je_cv_cray_84\" = \"xyes\" ; then\n    JE_CFLAGS_ADD([-hipa2])\n    JE_CFLAGS_ADD([-hnognu])\n  fi\n  dnl ignore unreachable code warning\n  JE_CFLAGS_ADD([-hnomessage=128])\n  dnl ignore redefinition of \"malloc\", \"free\", etc warning\n  JE_CFLAGS_ADD([-hnomessage=1357])\nfi\nAC_SUBST([CONFIGURE_CFLAGS])\nAC_SUBST([SPECIFIED_CFLAGS])\nAC_SUBST([EXTRA_CFLAGS])\nAC_PROG_CPP\n\nAC_ARG_ENABLE([cxx],\n  [AS_HELP_STRING([--disable-cxx], [Disable C++ integration])],\nif test \"x$enable_cxx\" = \"xno\" ; then\n  enable_cxx=\"0\"\nelse\n  enable_cxx=\"1\"\nfi\n,\nenable_cxx=\"1\"\n)\nif test \"x$enable_cxx\" = \"x1\" ; then\n  dnl Require at least c++14, which is the first version to support sized\n  dnl deallocation.  C++ support is not compiled otherwise.\n  m4_include([m4/ax_cxx_compile_stdcxx.m4])\n  AX_CXX_COMPILE_STDCXX([14], [noext], [optional])\n  if test \"x${HAVE_CXX14}\" = \"x1\" ; then\n    JE_CXXFLAGS_ADD([-Wall])\n    JE_CXXFLAGS_ADD([-g3])\n\n    SAVED_LIBS=\"${LIBS}\"\n    JE_APPEND_VS(LIBS, -lstdc++)\n    JE_COMPILABLE([libstdc++ linkage], [\n#include <stdlib.h>\n], [[\n\tint *arr = (int *)malloc(sizeof(int) * 42);\n\tif (arr == NULL)\n\t\treturn 1;\n]], [je_cv_libstdcxx])\n    if test \"x${je_cv_libstdcxx}\" = \"xno\" ; then\n      LIBS=\"${SAVED_LIBS}\"\n    fi\n  else\n    enable_cxx=\"0\"\n  fi\nfi\nAC_SUBST([enable_cxx])\nAC_SUBST([CONFIGURE_CXXFLAGS])\nAC_SUBST([SPECIFIED_CXXFLAGS])\nAC_SUBST([EXTRA_CXXFLAGS])\n\nAC_C_BIGENDIAN([ac_cv_big_endian=1], [ac_cv_big_endian=0])\nif test \"x${ac_cv_big_endian}\" = \"x1\" ; then\n  AC_DEFINE_UNQUOTED([JEMALLOC_BIG_ENDIAN], [ ])\nfi\n\nif test \"x${je_cv_msvc}\" = \"xyes\" -a \"x${ac_cv_header_inttypes_h}\" = \"xno\"; then\n  JE_APPEND_VS(CPPFLAGS, -I${srcdir}/include/msvc_compat/C99)\nfi\n\nif test \"x${je_cv_msvc}\" = \"xyes\" ; then\n  LG_SIZEOF_PTR=LG_SIZEOF_PTR_WIN\n  AC_MSG_RESULT([Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit])\nelse\n  AC_CHECK_SIZEOF([void *])\n  if test \"x${ac_cv_sizeof_void_p}\" = \"x8\" ; then\n    LG_SIZEOF_PTR=3\n  elif test \"x${ac_cv_sizeof_void_p}\" = \"x4\" ; then\n    LG_SIZEOF_PTR=2\n  else\n    AC_MSG_ERROR([Unsupported pointer size: ${ac_cv_sizeof_void_p}])\n  fi\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([long long])\nif test \"x${ac_cv_sizeof_long_long}\" = \"x8\" ; then\n  LG_SIZEOF_LONG_LONG=3\nelif test \"x${ac_cv_sizeof_long_long}\" = \"x4\" ; then\n  LG_SIZEOF_LONG_LONG=2\nelse\n  AC_MSG_ERROR([Unsupported long long size: ${ac_cv_sizeof_long_long}])\nfi\nAC_DEFINE_UNQUOTED([LG_SIZEOF_LONG_LONG], [$LG_SIZEOF_LONG_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  i686|x86_64)\n\tHAVE_CPU_SPINWAIT=1\n\tif test \"x${je_cv_msvc}\" = \"xyes\" ; then\n\t    AC_CACHE_VAL([je_cv_pause_msvc],\n\t      [JE_COMPILABLE([pause instruction MSVC], [],\n\t\t\t\t\t[[_mm_pause(); return 0;]],\n\t\t\t\t\t[je_cv_pause_msvc])])\n\t    if test \"x${je_cv_pause_msvc}\" = \"xyes\" ; then\n\t\tCPU_SPINWAIT='_mm_pause()'\n\t    fi\n\telse\n\t    AC_CACHE_VAL([je_cv_pause],\n\t      [JE_COMPILABLE([pause instruction], [],\n\t\t\t\t\t[[__asm__ volatile(\"pause\"); return 0;]],\n\t\t\t\t\t[je_cv_pause])])\n\t    if test \"x${je_cv_pause}\" = \"xyes\" ; then\n\t\tCPU_SPINWAIT='__asm__ volatile(\"pause\")'\n\t    fi\n\tfi\n\t;;\n  *)\n\tHAVE_CPU_SPINWAIT=0\n\t;;\nesac\nAC_DEFINE_UNQUOTED([HAVE_CPU_SPINWAIT], [$HAVE_CPU_SPINWAIT])\nAC_DEFINE_UNQUOTED([CPU_SPINWAIT], [$CPU_SPINWAIT])\n\nAC_ARG_WITH([lg_vaddr],\n  [AS_HELP_STRING([--with-lg-vaddr=<lg-vaddr>], [Number of significant virtual address bits])],\n  [LG_VADDR=\"$with_lg_vaddr\"], [LG_VADDR=\"detect\"])\n\ncase \"${host_cpu}\" in\n  aarch64)\n    if test \"x$LG_VADDR\" = \"xdetect\"; then\n      AC_MSG_CHECKING([number of significant virtual address bits])\n      if test \"x${LG_SIZEOF_PTR}\" = \"x2\" ; then\n        #aarch64 ILP32\n        LG_VADDR=32\n      else\n        #aarch64 LP64\n        LG_VADDR=48\n      fi\n      AC_MSG_RESULT([$LG_VADDR])\n    fi\n    ;;\n  x86_64)\n    if test \"x$LG_VADDR\" = \"xdetect\"; then\n      AC_CACHE_CHECK([number of significant virtual address bits],\n                     [je_cv_lg_vaddr],\n                     AC_RUN_IFELSE([AC_LANG_PROGRAM(\n[[\n#include <stdio.h>\n#ifdef _WIN32\n#include <limits.h>\n#include <intrin.h>\ntypedef unsigned __int32 uint32_t;\n#else\n#include <stdint.h>\n#endif\n]], [[\n\tuint32_t r[[4]];\n\tuint32_t eax_in = 0x80000008U;\n#ifdef _WIN32\n\t__cpuid((int *)r, (int)eax_in);\n#else\n\tasm volatile (\"cpuid\"\n\t    : \"=a\" (r[[0]]), \"=b\" (r[[1]]), \"=c\" (r[[2]]), \"=d\" (r[[3]])\n\t    : \"a\" (eax_in), \"c\" (0)\n\t);\n#endif\n\tuint32_t eax_out = r[[0]];\n\tuint32_t vaddr = ((eax_out & 0x0000ff00U) >> 8);\n\tFILE *f = fopen(\"conftest.out\", \"w\");\n\tif (f == NULL) {\n\t\treturn 1;\n\t}\n\tif (vaddr > (sizeof(void *) << 3)) {\n\t\tvaddr = sizeof(void *) << 3;\n\t}\n\tfprintf(f, \"%u\", vaddr);\n\tfclose(f);\n\treturn 0;\n]])],\n                   [je_cv_lg_vaddr=`cat conftest.out`],\n                   [je_cv_lg_vaddr=error],\n                   [je_cv_lg_vaddr=57]))\n      if test \"x${je_cv_lg_vaddr}\" != \"x\" ; then\n        LG_VADDR=\"${je_cv_lg_vaddr}\"\n      fi\n      if test \"x${LG_VADDR}\" != \"xerror\" ; then\n        AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR])\n      else\n        AC_MSG_ERROR([cannot determine number of significant virtual address bits])\n      fi\n    fi\n    ;;\n  *)\n    if test \"x$LG_VADDR\" = \"xdetect\"; then\n      AC_MSG_CHECKING([number of significant virtual address bits])\n      if test \"x${LG_SIZEOF_PTR}\" = \"x3\" ; then\n        LG_VADDR=64\n      elif test \"x${LG_SIZEOF_PTR}\" = \"x2\" ; then\n        LG_VADDR=32\n      elif test \"x${LG_SIZEOF_PTR}\" = \"xLG_SIZEOF_PTR_WIN\" ; then\n        LG_VADDR=\"(1U << (LG_SIZEOF_PTR_WIN+3))\"\n      else\n        AC_MSG_ERROR([Unsupported lg(pointer size): ${LG_SIZEOF_PTR}])\n      fi\n      AC_MSG_RESULT([$LG_VADDR])\n    fi\n    ;;\nesac\nAC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR])\n\nLD_PRELOAD_VAR=\"LD_PRELOAD\"\nso=\"so\"\nimportlib=\"${so}\"\no=\"$ac_objext\"\na=\"a\"\nexe=\"$ac_exeext\"\nlibprefix=\"lib\"\nlink_whole_archive=\"0\"\nDSO_LDFLAGS='-shared -Wl,-soname,$(@F)'\nRPATH='-Wl,-rpath,$(1)'\nSOREV=\"${so}.${rev}\"\nPIC_CFLAGS='-fPIC -DPIC'\nCTARGET='-o $@'\nLDTARGET='-o $@'\nTEST_LD_MODE=\nEXTRA_LDFLAGS=\nARFLAGS='crus'\nAROUT=' $@'\nCC_MM=1\n\nif test \"x$je_cv_cray_prgenv_wrapper\" = \"xyes\" ; then\n  TEST_LD_MODE='-dynamic'\nfi\n\nif test \"x${je_cv_cray}\" = \"xyes\" ; then\n  CC_MM=\nfi\n\nAN_MAKEVAR([AR], [AC_PROG_AR])\nAN_PROGRAM([ar], [AC_PROG_AR])\nAC_DEFUN([AC_PROG_AR], [AC_CHECK_TOOL(AR, ar, :)])\nAC_PROG_AR\n\nAN_MAKEVAR([NM], [AC_PROG_NM])\nAN_PROGRAM([nm], [AC_PROG_NM])\nAC_DEFUN([AC_PROG_NM], [AC_CHECK_TOOL(NM, nm, :)])\nAC_PROG_NM\n\nAC_PROG_AWK\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_retain=\"0\"\nmaps_coalesce=\"1\"\nDUMP_SYMS=\"${NM} -a\"\nSYM_PREFIX=\"\"\ncase \"${host}\" in\n  *-*-darwin* | *-*-ios*)\n\tabi=\"macho\"\n\tRPATH=\"\"\n\tLD_PRELOAD_VAR=\"DYLD_INSERT_LIBRARIES\"\n\tso=\"dylib\"\n\timportlib=\"${so}\"\n\tforce_tls=\"0\"\n\tDSO_LDFLAGS='-shared -Wl,-install_name,$(LIBDIR)/$(@F)'\n\tSOREV=\"${rev}.${so}\"\n\tsbrk_deprecated=\"1\"\n\tSYM_PREFIX=\"_\"\n\t;;\n  *-*-freebsd*)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ])\n\tforce_lazy_lock=\"1\"\n\t;;\n  *-*-dragonfly*)\n\tabi=\"elf\"\n\t;;\n  *-*-openbsd*)\n\tabi=\"elf\"\n\tforce_tls=\"0\"\n\t;;\n  *-*-bitrig*)\n\tabi=\"elf\"\n\t;;\n  *-*-linux-android)\n\tdnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.\n\tJE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS], [ ])\n\tAC_DEFINE([JEMALLOC_HAS_ALLOCA_H])\n\tAC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tAC_DEFINE([JEMALLOC_C11_ATOMICS])\n\tforce_tls=\"0\"\n\tif test \"${LG_SIZEOF_PTR}\" = \"3\"; then\n\t  default_retain=\"1\"\n\tfi\n\t;;\n  *-*-linux*)\n\tdnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.\n\tJE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS], [ ])\n\tAC_DEFINE([JEMALLOC_HAS_ALLOCA_H])\n\tAC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tAC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ])\n\tif test \"${LG_SIZEOF_PTR}\" = \"3\"; then\n\t  default_retain=\"1\"\n\tfi\n\t;;\n  *-*-kfreebsd*)\n\tdnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.\n\tJE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)\n\tabi=\"elf\"\n\tAC_DEFINE([JEMALLOC_HAS_ALLOCA_H])\n\tAC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ])\n\tAC_DEFINE([JEMALLOC_THREADED_INIT], [ ])\n\tAC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ])\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                          [abi=\"elf\"],\n                          [abi=\"aout\"])\n\tAC_MSG_RESULT([$abi])\n\t;;\n  *-*-solaris2*)\n\tabi=\"elf\"\n\tRPATH='-Wl,-R,$(1)'\n\tdnl Solaris needs this for sigwait().\n\tJE_APPEND_VS(CPPFLAGS, -D_POSIX_PTHREAD_SEMANTICS)\n\tJE_APPEND_VS(LIBS, -lposix4 -lsocket -lnsl)\n\t;;\n  *-ibm-aix*)\n\tif test \"${LG_SIZEOF_PTR}\" = \"3\"; 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* | *-*-cygwin*)\n\tabi=\"pecoff\"\n\tforce_tls=\"0\"\n\tmaps_coalesce=\"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  AR='lib'\n\t  ARFLAGS='-nologo -out:'\n\t  AROUT='$@'\n\t  CC_MM=\n        else\n\t  importlib=\"${so}\"\n\t  DSO_LDFLAGS=\"-shared\"\n\t  link_whole_archive=\"1\"\n\tfi\n\tcase \"${host}\" in\n\t  *-*-cygwin*)\n\t    DUMP_SYMS=\"dumpbin /SYMBOLS\"\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\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\n\nJEMALLOC_USABLE_SIZE_CONST=const\nAC_CHECK_HEADERS([malloc.h], [\n  AC_MSG_CHECKING([whether malloc_usable_size definition can use const argument])\n  AC_COMPILE_IFELSE([AC_LANG_PROGRAM(\n    [#include <malloc.h>\n     #include <stddef.h>\n    size_t malloc_usable_size(const void *ptr);\n    ],\n    [])],[\n                AC_MSG_RESULT([yes])\n         ],[\n                JEMALLOC_USABLE_SIZE_CONST=\n                AC_MSG_RESULT([no])\n         ])\n])\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([link_whole_archive])\nAC_SUBST([DSO_LDFLAGS])\nAC_SUBST([EXTRA_LDFLAGS])\nAC_SUBST([SOREV])\nAC_SUBST([PIC_CFLAGS])\nAC_SUBST([CTARGET])\nAC_SUBST([LDTARGET])\nAC_SUBST([TEST_LD_MODE])\nAC_SUBST([MKLIB])\nAC_SUBST([ARFLAGS])\nAC_SUBST([AROUT])\nAC_SUBST([DUMP_SYMS])\nAC_SUBST([CC_MM])\n\ndnl Determine whether libm must be linked to use e.g. log(3).\nAC_SEARCH_LIBS([log], [m], , [AC_MSG_ERROR([Missing math functions])])\nif test \"x$ac_cv_search_log\" != \"xnone required\" ; then\n  LM=\"$ac_cv_search_log\"\nelse\n  LM=\nfi\nAC_SUBST(LM)\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_ADD([-fvisibility=hidden])\n    JE_CXXFLAGS_ADD([-fvisibility=hidden])\n  fi\nfi\ndnl Check for tls_model attribute support (clang 3.0 still lacks support).\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([tls_model attribute], [],\n              [static __thread int\n               __attribute__((tls_model(\"initial-exec\"), unused)) foo;\n               foo = 0;],\n              [je_cv_tls_model])\nJE_CFLAGS_RESTORE()\ndnl (Setting of JEMALLOC_TLS_MODEL is done later, after we've checked for\ndnl --disable-initial-exec-tls)\n\ndnl Check for alloc_size attribute support.\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([alloc_size attribute], [#include <stdlib.h>],\n              [void *foo(size_t size) __attribute__((alloc_size(1)));],\n              [je_cv_alloc_size])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_alloc_size}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR_ALLOC_SIZE], [ ])\nfi\ndnl Check for format(gnu_printf, ...) attribute support.\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([format(gnu_printf, ...) attribute], [#include <stdlib.h>],\n              [void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));],\n              [je_cv_format_gnu_printf])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_format_gnu_printf}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF], [ ])\nfi\ndnl Check for format(printf, ...) attribute support.\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],\n              [void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));],\n              [je_cv_format_printf])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_format_printf}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_PRINTF], [ ])\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([LD], [ld], [false], [$PATH])\nAC_PATH_PROG([AUTOCONF], [autoconf], [false], [$PATH])\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=\"\"])\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  AC_DEFINE([JEMALLOC_IS_MALLOC])\nelse\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\nAC_SUBST([JEMALLOC_PREFIX])\nAC_SUBST([JEMALLOC_CPREFIX])\n\nAC_ARG_WITH([export],\n  [AS_HELP_STRING([--without-export], [disable exporting jemalloc public APIs])],\n  [if test \"x$with_export\" = \"xno\"; then\n  AC_DEFINE([JEMALLOC_EXPORT],[])\nfi]\n)\n\npublic_syms=\"aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_message malloc_stats_print malloc_usable_size mallocx nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx\"\ndnl Check for additional platform-specific public API functions.\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 Check for allocator-related functions that should be wrapped.\nwrap_syms=\nif test \"x${JEMALLOC_PREFIX}\" = \"x\" ; then\n  AC_CHECK_FUNC([__libc_calloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_CALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_calloc\"])\n  AC_CHECK_FUNC([__libc_free],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_FREE], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_free\"])\n  AC_CHECK_FUNC([__libc_malloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_malloc\"])\n  AC_CHECK_FUNC([__libc_memalign],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MEMALIGN], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_memalign\"])\n  AC_CHECK_FUNC([__libc_realloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_REALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_realloc\"])\n  AC_CHECK_FUNC([__libc_valloc],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_VALLOC], [ ])\n\t\t wrap_syms=\"${wrap_syms} __libc_valloc\"])\n  AC_CHECK_FUNC([__posix_memalign],\n\t\t[AC_DEFINE([JEMALLOC_OVERRIDE___POSIX_MEMALIGN], [ ])\n\t\t wrap_syms=\"${wrap_syms} __posix_memalign\"])\nfi\n\ncase \"${host}\" in\n  *-*-mingw* | *-*-cygwin*)\n    wrap_syms=\"${wrap_syms} tls_callback\"\n    ;;\n  *)\n    ;;\nesac\n\ndnl Mangle library-private APIs.\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}je_\"],\n  [JEMALLOC_PRIVATE_NAMESPACE=\"je_\"]\n)\nAC_DEFINE_UNQUOTED([JEMALLOC_PRIVATE_NAMESPACE], [$JEMALLOC_PRIVATE_NAMESPACE])\nprivate_namespace=\"$JEMALLOC_PRIVATE_NAMESPACE\"\nAC_SUBST([private_namespace])\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\ndnl Specify default malloc_conf.\nAC_ARG_WITH([malloc_conf],\n  [AS_HELP_STRING([--with-malloc-conf=<malloc_conf>], [config.malloc_conf options string])],\n  [JEMALLOC_CONFIG_MALLOC_CONF=\"$with_malloc_conf\"],\n  [JEMALLOC_CONFIG_MALLOC_CONF=\"\"]\n)\nconfig_malloc_conf=\"$JEMALLOC_CONFIG_MALLOC_CONF\"\nAC_DEFINE_UNQUOTED([JEMALLOC_CONFIG_MALLOC_CONF], [\"$config_malloc_conf\"])\n\ndnl Substitute @je_@ in jemalloc_protos.h.in, primarily to make generation of\ndnl jemalloc_protos_jet.h easy.\nje_=\"je_\"\nAC_SUBST([je_])\n\ncfgoutputs_in=\"Makefile.in\"\ncfgoutputs_in=\"${cfgoutputs_in} jemalloc.pc.in\"\ncfgoutputs_in=\"${cfgoutputs_in} doc/html.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} doc/manpages.xsl.in\"\ncfgoutputs_in=\"${cfgoutputs_in} doc/jemalloc.xml.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/jemalloc_macros.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/jemalloc_protos.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/jemalloc_typedefs.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} include/jemalloc/internal/jemalloc_preamble.h.in\"\ncfgoutputs_in=\"${cfgoutputs_in} test/test.sh.in\"\ncfgoutputs_in=\"${cfgoutputs_in} test/include/test/jemalloc_test.h.in\"\n\ncfgoutputs_out=\"Makefile\"\ncfgoutputs_out=\"${cfgoutputs_out} jemalloc.pc\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/html.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/manpages.xsl\"\ncfgoutputs_out=\"${cfgoutputs_out} doc/jemalloc.xml\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc_macros.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc_protos.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/jemalloc_typedefs.h\"\ncfgoutputs_out=\"${cfgoutputs_out} include/jemalloc/internal/jemalloc_preamble.h\"\ncfgoutputs_out=\"${cfgoutputs_out} test/test.sh\"\ncfgoutputs_out=\"${cfgoutputs_out} test/include/test/jemalloc_test.h\"\n\ncfgoutputs_tup=\"Makefile\"\ncfgoutputs_tup=\"${cfgoutputs_tup} jemalloc.pc:jemalloc.pc.in\"\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.xml:doc/jemalloc.xml.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc_macros.h:include/jemalloc/jemalloc_macros.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc_protos.h:include/jemalloc/jemalloc_protos.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/jemalloc_typedefs.h:include/jemalloc/jemalloc_typedefs.h.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} include/jemalloc/internal/jemalloc_preamble.h\"\ncfgoutputs_tup=\"${cfgoutputs_tup} test/test.sh:test/test.sh.in\"\ncfgoutputs_tup=\"${cfgoutputs_tup} test/include/test/jemalloc_test.h:test/include/test/jemalloc_test.h.in\"\n\ncfghdrs_in=\"include/jemalloc/jemalloc_defs.h.in\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/jemalloc_internal_defs.h.in\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/private_symbols.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/private_namespace.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/public_namespace.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/public_unnamespace.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/internal/size_classes.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/jemalloc_rename.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/jemalloc_mangle.sh\"\ncfghdrs_in=\"${cfghdrs_in} include/jemalloc/jemalloc.sh\"\ncfghdrs_in=\"${cfghdrs_in} test/include/test/jemalloc_test_defs.h.in\"\n\ncfghdrs_out=\"include/jemalloc/jemalloc_defs.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc${install_suffix}.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/private_symbols.awk\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/private_symbols_jet.awk\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/public_symbols.txt\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/public_namespace.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/public_unnamespace.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/size_classes.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_protos_jet.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_rename.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_mangle.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/jemalloc_mangle_jet.h\"\ncfghdrs_out=\"${cfghdrs_out} include/jemalloc/internal/jemalloc_internal_defs.h\"\ncfghdrs_out=\"${cfghdrs_out} test/include/test/jemalloc_test_defs.h\"\n\ncfghdrs_tup=\"include/jemalloc/jemalloc_defs.h:include/jemalloc/jemalloc_defs.h.in\"\ncfghdrs_tup=\"${cfghdrs_tup} include/jemalloc/internal/jemalloc_internal_defs.h:include/jemalloc/internal/jemalloc_internal_defs.h.in\"\ncfghdrs_tup=\"${cfghdrs_tup} test/include/test/jemalloc_test_defs.h:test/include/test/jemalloc_test_defs.h.in\"\n\ndnl Do not compile with debugging by default.\nAC_ARG_ENABLE([debug],\n  [AS_HELP_STRING([--enable-debug],\n                  [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], [ ])\nfi\nif test \"x$enable_debug\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_DEBUG], [ ])\nfi\nAC_SUBST([enable_debug])\n\ndnl Only optimize if not debugging.\nif test \"x$enable_debug\" = \"x0\" ; then\n  if test \"x$GCC\" = \"xyes\" ; then\n    JE_CFLAGS_ADD([-O3])\n    JE_CXXFLAGS_ADD([-O3])\n    JE_CFLAGS_ADD([-funroll-loops])\n  elif test \"x$je_cv_msvc\" = \"xyes\" ; then\n    JE_CFLAGS_ADD([-O2])\n    JE_CXXFLAGS_ADD([-O2])\n  else\n    JE_CFLAGS_ADD([-O])\n    JE_CXXFLAGS_ADD([-O])\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], [unw_backtrace], [JE_APPEND_VS(LIBS, $LUNWIND)],\n                 [enable_prof_libunwind=\"0\"])\n  else\n    JE_APPEND_VS(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  if test \"x${enable_prof_libgcc}\" = \"x1\" ; then\n    AC_CHECK_LIB([gcc], [_Unwind_Backtrace], [JE_APPEND_VS(LIBS, -lgcc)], [enable_prof_libgcc=\"0\"])\n  fi\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  JE_CFLAGS_ADD([-fno-omit-frame-pointer])\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  dnl Heap profiling uses the log(3) function.\n  JE_APPEND_VS(LIBS, $LM)\n\n  AC_DEFINE([JEMALLOC_PROF], [ ])\nfi\nAC_SUBST([enable_prof])\n\ndnl Indicate whether adjacent virtual memory mappings automatically coalesce\ndnl (and fragment on demand).\nif test \"x${maps_coalesce}\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_MAPS_COALESCE], [ ])\nfi\n\ndnl Indicate whether to retain memory (rather than using munmap()) by default.\nif test \"x$default_retain\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_RETAIN], [ ])\nfi\n\ndnl Enable allocation from DSS if supported by the OS.\nhave_dss=\"1\"\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  if test \"x$sbrk_deprecated\" = \"x1\" ; then\n    AC_MSG_RESULT([Disabling dss allocation because sbrk is deprecated])\n    have_dss=\"0\"\n  fi\nelse\n  have_dss=\"0\"\nfi\n\nif test \"x$have_dss\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_DSS], [ ])\nfi\n\ndnl Support the junk/zero filling option by default.\nAC_ARG_ENABLE([fill],\n  [AS_HELP_STRING([--disable-fill], [Disable support for junk/zero filling])],\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 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\ndnl Support cache-oblivious allocation alignment by default.\nAC_ARG_ENABLE([cache-oblivious],\n  [AS_HELP_STRING([--disable-cache-oblivious],\n                  [Disable support for cache-oblivious allocation alignment])],\n[if test \"x$enable_cache_oblivious\" = \"xno\" ; then\n  enable_cache_oblivious=\"0\"\nelse\n  enable_cache_oblivious=\"1\"\nfi\n],\n[enable_cache_oblivious=\"1\"]\n)\nif test \"x$enable_cache_oblivious\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_CACHE_OBLIVIOUS], [ ])\nfi\nAC_SUBST([enable_cache_oblivious])\n\ndnl Do not log by default.\nAC_ARG_ENABLE([log],\n  [AS_HELP_STRING([--enable-log], [Support debug logging])],\n[if test \"x$enable_log\" = \"xno\" ; then\n  enable_log=\"0\"\nelse\n  enable_log=\"1\"\nfi\n],\n[enable_log=\"0\"]\n)\nif test \"x$enable_log\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_LOG], [ ])\nfi\nAC_SUBST([enable_log])\n\n\nJE_COMPILABLE([a program using __builtin_unreachable], [\nvoid foo (void) {\n  __builtin_unreachable();\n}\n], [\n\t{\n\t\tfoo();\n\t}\n], [je_cv_gcc_builtin_unreachable])\nif test \"x${je_cv_gcc_builtin_unreachable}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [__builtin_unreachable])\nelse\n  AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [abort])\nfi\n\ndnl ============================================================================\ndnl Check for  __builtin_ffsl(), then ffsl(3), and fail if neither are found.\ndnl One of those two functions should (theoretically) exist on all platforms\ndnl that jemalloc currently has a chance of functioning on without modification.\ndnl We additionally assume ffs[ll]() or __builtin_ffs[ll]() are defined if\ndnl ffsl() or __builtin_ffsl() are defined, respectively.\nJE_COMPILABLE([a program using __builtin_ffsl], [\n#include <stdio.h>\n#include <strings.h>\n#include <string.h>\n], [\n\t{\n\t\tint rv = __builtin_ffsl(0x08);\n\t\tprintf(\"%d\\n\", rv);\n\t}\n], [je_cv_gcc_builtin_ffsl])\nif test \"x${je_cv_gcc_builtin_ffsl}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_INTERNAL_FFSLL], [__builtin_ffsll])\n  AC_DEFINE([JEMALLOC_INTERNAL_FFSL], [__builtin_ffsl])\n  AC_DEFINE([JEMALLOC_INTERNAL_FFS], [__builtin_ffs])\nelse\n  JE_COMPILABLE([a program using ffsl], [\n  #include <stdio.h>\n  #include <strings.h>\n  #include <string.h>\n  ], [\n\t{\n\t\tint rv = ffsl(0x08);\n\t\tprintf(\"%d\\n\", rv);\n\t}\n  ], [je_cv_function_ffsl])\n  if test \"x${je_cv_function_ffsl}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_INTERNAL_FFSLL], [ffsll])\n    AC_DEFINE([JEMALLOC_INTERNAL_FFSL], [ffsl])\n    AC_DEFINE([JEMALLOC_INTERNAL_FFS], [ffs])\n  else\n    AC_MSG_ERROR([Cannot build without ffsl(3) or __builtin_ffsl()])\n  fi\nfi\n\nAC_ARG_WITH([lg_quantum],\n  [AS_HELP_STRING([--with-lg-quantum=<lg-quantum>],\n   [Base 2 log of minimum allocation alignment])],\n  [LG_QUANTA=\"$with_lg_quantum\"],\n  [LG_QUANTA=\"3 4\"])\nif test \"x$with_lg_quantum\" != \"x\" ; then\n  AC_DEFINE_UNQUOTED([LG_QUANTUM], [$with_lg_quantum])\nfi\n\nAC_ARG_WITH([lg_page],\n  [AS_HELP_STRING([--with-lg-page=<lg-page>], [Base 2 log of system page size])],\n  [LG_PAGE=\"$with_lg_page\"], [LG_PAGE=\"detect\"])\nif test \"x$LG_PAGE\" = \"xdetect\"; then\n  AC_CACHE_CHECK([LG_PAGE],\n               [je_cv_lg_page],\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    int 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 = JEMALLOC_INTERNAL_FFSL(result) - 1;\n\n    f = fopen(\"conftest.out\", \"w\");\n    if (f == NULL) {\n\treturn 1;\n    }\n    fprintf(f, \"%d\", result);\n    fclose(f);\n\n    return 0;\n]])],\n                             [je_cv_lg_page=`cat conftest.out`],\n                             [je_cv_lg_page=undefined],\n                             [je_cv_lg_page=12]))\nfi\nif test \"x${je_cv_lg_page}\" != \"x\" ; then\n  LG_PAGE=\"${je_cv_lg_page}\"\nfi\nif test \"x${LG_PAGE}\" != \"xundefined\" ; then\n   AC_DEFINE_UNQUOTED([LG_PAGE], [$LG_PAGE])\nelse\n   AC_MSG_ERROR([cannot determine value for LG_PAGE])\nfi\n\nAC_ARG_WITH([lg_hugepage],\n  [AS_HELP_STRING([--with-lg-hugepage=<lg-hugepage>],\n   [Base 2 log of system huge page size])],\n  [je_cv_lg_hugepage=\"${with_lg_hugepage}\"],\n  [je_cv_lg_hugepage=\"\"])\nif test \"x${je_cv_lg_hugepage}\" = \"x\" ; then\n  dnl Look in /proc/meminfo (Linux-specific) for information on the default huge\n  dnl page size, if any.  The relevant line looks like:\n  dnl\n  dnl   Hugepagesize:       2048 kB\n  if test -e \"/proc/meminfo\" ; then\n    hpsk=[`cat /proc/meminfo 2>/dev/null | \\\n          grep -e '^Hugepagesize:[[:space:]]\\+[0-9]\\+[[:space:]]kB$' | \\\n          awk '{print $2}'`]\n    if test \"x${hpsk}\" != \"x\" ; then\n      je_cv_lg_hugepage=10\n      while test \"${hpsk}\" -gt 1 ; do\n        hpsk=\"$((hpsk / 2))\"\n        je_cv_lg_hugepage=\"$((je_cv_lg_hugepage + 1))\"\n      done\n    fi\n  fi\n\n  dnl Set default if unable to automatically configure.\n  if test \"x${je_cv_lg_hugepage}\" = \"x\" ; then\n    je_cv_lg_hugepage=21\n  fi\nfi\nif test \"x${LG_PAGE}\" != \"xundefined\" -a \\\n        \"${je_cv_lg_hugepage}\" -lt \"${LG_PAGE}\" ; then\n  AC_MSG_ERROR([Huge page size (2^${je_cv_lg_hugepage}) must be at least page size (2^${LG_PAGE})])\nfi\nAC_DEFINE_UNQUOTED([LG_HUGEPAGE], [${je_cv_lg_hugepage}])\n\nAC_ARG_WITH([lg_page_sizes],\n  [AS_HELP_STRING([--with-lg-page-sizes=<lg-page-sizes>],\n   [Base 2 logs of system page sizes to support])],\n  [LG_PAGE_SIZES=\"$with_lg_page_sizes\"], [LG_PAGE_SIZES=\"$LG_PAGE\"])\n\ndnl ============================================================================\ndnl jemalloc configuration.\ndnl\n\nAC_ARG_WITH([version],\n  [AS_HELP_STRING([--with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid>],\n   [Version string])],\n  [\n    echo \"${with_version}\" | grep ['^[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+-[0-9]\\+-g[0-9a-f]\\+$'] 2>&1 1>/dev/null\n    if test $? -eq 0 ; then\n      echo \"$with_version\" > \"${objroot}VERSION\"\n    else\n      echo \"${with_version}\" | grep ['^VERSION$'] 2>&1 1>/dev/null\n      if test $? -ne 0 ; then\n        AC_MSG_ERROR([${with_version} does not match <major>.<minor>.<bugfix>-<nrev>-g<gid> or VERSION])\n      fi\n    fi\n  ], [\n    dnl Set VERSION if source directory is inside a git repository.\n    if test \"x`test ! \\\"${srcroot}\\\" && cd \\\"${srcroot}\\\"; git rev-parse --is-inside-work-tree 2>/dev/null`\" = \"xtrue\" ; then\n      dnl Pattern globs aren't powerful enough to match both single- and\n      dnl double-digit version numbers, so iterate over patterns to support up\n      dnl to version 99.99.99 without any accidental matches.\n      for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \\\n                     '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \\\n                     '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \\\n                     '[0-9][0-9].[0-9][0-9].[0-9]' \\\n                     '[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do\n        (test ! \"${srcroot}\" && cd \"${srcroot}\"; git describe --long --abbrev=40 --match=\"${pattern}\") > \"${objroot}VERSION.tmp\" 2>/dev/null\n        if test $? -eq 0 ; then\n          mv \"${objroot}VERSION.tmp\" \"${objroot}VERSION\"\n          break\n        fi\n      done\n    fi\n    rm -f \"${objroot}VERSION.tmp\"\n  ])\n\nif test ! -e \"${objroot}VERSION\" ; then\n  if test ! -e \"${srcroot}VERSION\" ; then\n    AC_MSG_RESULT(\n      [Missing VERSION file, and unable to generate it; creating bogus VERSION])\n    echo \"0.0.0-0-g0000000000000000000000000000000000000000\" > \"${objroot}VERSION\"\n  else\n    cp ${srcroot}VERSION ${objroot}VERSION\n  fi\nfi\njemalloc_version=`cat \"${objroot}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_DEFINE([JEMALLOC_HAVE_PTHREAD], [ ])\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], [JE_APPEND_VS(LIBS, -lpthread)],\n               [AC_SEARCH_LIBS([pthread_create], , ,\n                               AC_MSG_ERROR([libpthread is missing]))])\n  wrap_syms=\"${wrap_syms} pthread_create\"\n  have_pthread=\"1\"\n  dnl Check if we have dlsym support.\n  have_dlsym=\"1\"\n  AC_CHECK_HEADERS([dlfcn.h],\n    AC_CHECK_FUNC([dlsym], [],\n      [AC_CHECK_LIB([dl], [dlsym], [LIBS=\"$LIBS -ldl\"], [have_dlsym=\"0\"])]),\n    [have_dlsym=\"0\"])\n  if test \"x$have_dlsym\" = \"x1\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_DLSYM], [ ])\n  fi\n  JE_COMPILABLE([pthread_atfork(3)], [\n#include <pthread.h>\n], [\n  pthread_atfork((void *)0, (void *)0, (void *)0);\n], [je_cv_pthread_atfork])\n  if test \"x${je_cv_pthread_atfork}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_PTHREAD_ATFORK], [ ])\n  fi\n  dnl Check if pthread_setname_np is available with the expected API.\n  JE_COMPILABLE([pthread_setname_np(3)], [\n#include <pthread.h>\n], [\n  pthread_setname_np(pthread_self(), \"setname_test\");\n], [je_cv_pthread_setname_np])\n  if test \"x${je_cv_pthread_setname_np}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_PTHREAD_SETNAME_NP], [ ])\n  fi\nfi\n\nJE_APPEND_VS(CPPFLAGS, -D_REENTRANT)\n\ndnl Check whether clock_gettime(2) is in libc or librt.\nAC_SEARCH_LIBS([clock_gettime], [rt])\n\ndnl Cray wrapper compiler often adds `-lrt` when using `-static`. Check with\ndnl `-dynamic` as well in case a user tries to dynamically link in jemalloc\nif test \"x$je_cv_cray_prgenv_wrapper\" = \"xyes\" ; then\n  if test \"$ac_cv_search_clock_gettime\" != \"-lrt\"; then\n    JE_CFLAGS_SAVE()\n\n    unset ac_cv_search_clock_gettime\n    JE_CFLAGS_ADD([-dynamic])\n    AC_SEARCH_LIBS([clock_gettime], [rt])\n\n    JE_CFLAGS_RESTORE()\n  fi\nfi\n\ndnl check for CLOCK_MONOTONIC_COARSE (Linux-specific).\nJE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC_COARSE, ...)], [\n#include <time.h>\n], [\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC_COARSE, &ts);\n], [je_cv_clock_monotonic_coarse])\nif test \"x${je_cv_clock_monotonic_coarse}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE])\nfi\n\ndnl check for CLOCK_MONOTONIC.\nJE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC, ...)], [\n#include <unistd.h>\n#include <time.h>\n], [\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\n#if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0\n#  error _POSIX_MONOTONIC_CLOCK missing/invalid\n#endif\n], [je_cv_clock_monotonic])\nif test \"x${je_cv_clock_monotonic}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC])\nfi\n\ndnl Check for mach_absolute_time().\nJE_COMPILABLE([mach_absolute_time()], [\n#include <mach/mach_time.h>\n], [\n\tmach_absolute_time();\n], [je_cv_mach_absolute_time])\nif test \"x${je_cv_mach_absolute_time}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_MACH_ABSOLUTE_TIME])\nfi\n\ndnl Use syscall(2) (if available) by default.\nAC_ARG_ENABLE([syscall],\n  [AS_HELP_STRING([--disable-syscall], [Disable use of syscall(2)])],\n[if test \"x$enable_syscall\" = \"xno\" ; then\n  enable_syscall=\"0\"\nelse\n  enable_syscall=\"1\"\nfi\n],\n[enable_syscall=\"1\"]\n)\nif test \"x$enable_syscall\" = \"x1\" ; then\n  dnl Check if syscall(2) is usable.  Treat warnings as errors, so that e.g. OS\n  dnl X 10.12's deprecation warning prevents use.\n  JE_CFLAGS_SAVE()\n  JE_CFLAGS_ADD([-Werror])\n  JE_COMPILABLE([syscall(2)], [\n#include <sys/syscall.h>\n#include <unistd.h>\n], [\n\tsyscall(SYS_write, 2, \"hello\", 5);\n],\n                [je_cv_syscall])\n  JE_CFLAGS_RESTORE()\n  if test \"x$je_cv_syscall\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_USE_SYSCALL], [ ])\n  fi\nfi\n\ndnl Check if the GNU-specific secure_getenv function exists.\nAC_CHECK_FUNC([secure_getenv],\n              [have_secure_getenv=\"1\"],\n              [have_secure_getenv=\"0\"]\n             )\nif test \"x$have_secure_getenv\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SECURE_GETENV], [ ])\nfi\n\ndnl Check if the GNU-specific sched_getcpu function exists.\nAC_CHECK_FUNC([sched_getcpu],\n              [have_sched_getcpu=\"1\"],\n              [have_sched_getcpu=\"0\"]\n             )\nif test \"x$have_sched_getcpu\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SCHED_GETCPU], [ ])\nfi\n\ndnl Check if the GNU-specific sched_setaffinity function exists.\nAC_CHECK_FUNC([sched_setaffinity],\n              [have_sched_setaffinity=\"1\"],\n              [have_sched_setaffinity=\"0\"]\n             )\nif test \"x$have_sched_setaffinity\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_SCHED_SETAFFINITY], [ ])\nfi\n\ndnl Check if the Solaris/BSD issetugid function exists.\nAC_CHECK_FUNC([issetugid],\n              [have_issetugid=\"1\"],\n              [have_issetugid=\"0\"]\n             )\nif test \"x$have_issetugid\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_ISSETUGID], [ ])\nfi\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  wrap_syms=\"${wrap_syms} _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])\n  wrap_syms=\"${wrap_syms} _malloc_prefork _malloc_postfork\"\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=\"\"]\n)\nif test \"x${enable_lazy_lock}\" = \"x\" ; then\n  if test \"x${force_lazy_lock}\" = \"x1\" ; then\n    AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])\n    enable_lazy_lock=\"1\"\n  else\n    enable_lazy_lock=\"0\"\n  fi\nfi\nif test \"x${enable_lazy_lock}\" = \"x1\" -a \"x${abi}\" = \"xpecoff\" ; then\n  AC_MSG_RESULT([Forcing no lazy-lock because thread creation monitoring is unimplemented])\n  enable_lazy_lock=\"0\"\nfi\nif test \"x$enable_lazy_lock\" = \"x1\" ; then\n  if test \"x$have_dlsym\" = \"x1\" ; then\n    AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ])\n  else\n    AC_MSG_ERROR([Missing dlsym support: lazy-lock cannot be enabled.])\n  fi\nfi\nAC_SUBST([enable_lazy_lock])\n\ndnl Automatically configure TLS.\nif test \"x${force_tls}\" = \"x1\" ; then\n  enable_tls=\"1\"\nelif test \"x${force_tls}\" = \"x0\" ; then\n  enable_tls=\"0\"\nelse\n  enable_tls=\"1\"\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\")\nelse\n  enable_tls=\"0\"\nfi\nAC_SUBST([enable_tls])\nif test \"x${enable_tls}\" = \"x1\" ; then\n  AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ])\nfi\n\ndnl ============================================================================\ndnl Check for C11 atomics.\n\nJE_COMPILABLE([C11 atomics], [\n#include <stdint.h>\n#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)\n#include <stdatomic.h>\n#else\n#error Atomics not available\n#endif\n], [\n    uint64_t *p = (uint64_t *)0;\n    uint64_t x = 1;\n    volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;\n    uint64_t r = atomic_fetch_add(a, x) + x;\n    return r == 0;\n], [je_cv_c11_atomics])\nif test \"x${je_cv_c11_atomics}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_C11_ATOMICS])\nfi\n\ndnl ============================================================================\ndnl Check for GCC-style __atomic atomics.\n\nJE_COMPILABLE([GCC __atomic atomics], [\n], [\n    int x = 0;\n    int val = 1;\n    int y = __atomic_fetch_add(&x, val, __ATOMIC_RELAXED);\n    int after_add = x;\n    return after_add == 1;\n], [je_cv_gcc_atomic_atomics])\nif test \"x${je_cv_gcc_atomic_atomics}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_GCC_ATOMIC_ATOMICS])\nfi\n\ndnl ============================================================================\ndnl Check for GCC-style __sync atomics.\n\nJE_COMPILABLE([GCC __sync atomics], [\n], [\n    int x = 0;\n    int before_add = __sync_fetch_and_add(&x, 1);\n    int after_add = x;\n    return (before_add == 0) && (after_add == 1);\n], [je_cv_gcc_sync_atomics])\nif test \"x${je_cv_gcc_sync_atomics}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_GCC_SYNC_ATOMICS])\nfi\n\ndnl ============================================================================\ndnl Check for atomic(3) operations as provided on Darwin.\ndnl We need this not for the atomic operations (which are provided above), but\ndnl rather for the OSSpinLock type it exposes.\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 for madvise(2).\n\nJE_COMPILABLE([madvise(2)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, 0);\n], [je_cv_madvise])\nif test \"x${je_cv_madvise}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ])\n\n  dnl Check for madvise(..., MADV_FREE).\n  JE_COMPILABLE([madvise(..., MADV_FREE)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_FREE);\n], [je_cv_madv_free])\n  if test \"x${je_cv_madv_free}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n  elif test \"x${je_cv_madvise}\" = \"xyes\" ; then\n    case \"${host_cpu}\" in i686|x86_64)\n        case \"${host}\" in *-*-linux*)\n            AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])\n            AC_DEFINE([JEMALLOC_DEFINE_MADVISE_FREE], [ ])\n\t    ;;\n        esac\n        ;;\n    esac\n  fi\n\n  dnl Check for madvise(..., MADV_DONTNEED).\n  JE_COMPILABLE([madvise(..., MADV_DONTNEED)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_DONTNEED);\n], [je_cv_madv_dontneed])\n  if test \"x${je_cv_madv_dontneed}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])\n  fi\n\n  dnl Check for madvise(..., MADV_DO[NT]DUMP).\n  JE_COMPILABLE([madvise(..., MADV_DO[[NT]]DUMP)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_DONTDUMP);\n\tmadvise((void *)0, 0, MADV_DODUMP);\n], [je_cv_madv_dontdump])\n  if test \"x${je_cv_madv_dontdump}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_MADVISE_DONTDUMP], [ ])\n  fi\n \n  dnl Check for madvise(..., MADV_[NO]HUGEPAGE).\n  JE_COMPILABLE([madvise(..., MADV_[[NO]]HUGEPAGE)], [\n#include <sys/mman.h>\n], [\n\tmadvise((void *)0, 0, MADV_HUGEPAGE);\n\tmadvise((void *)0, 0, MADV_NOHUGEPAGE);\n], [je_cv_thp])\ncase \"${host_cpu}\" in\n  arm*)\n    ;;\n  *)\n  if test \"x${je_cv_thp}\" = \"xyes\" ; then\n    AC_DEFINE([JEMALLOC_HAVE_MADVISE_HUGE], [ ])\n  fi\n  ;;\nesac\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 __builtin_clz() and __builtin_clzl().\n\nAC_CACHE_CHECK([for __builtin_clz],\n               [je_cv_builtin_clz],\n               [AC_LINK_IFELSE([AC_LANG_PROGRAM([],\n                                                [\n                                                {\n                                                        unsigned x = 0;\n                                                        int y = __builtin_clz(x);\n                                                }\n                                                {\n                                                        unsigned long x = 0;\n                                                        int y = __builtin_clzl(x);\n                                                }\n                                                ])],\n                               [je_cv_builtin_clz=yes],\n                               [je_cv_builtin_clz=no])])\n\nif test \"x${je_cv_builtin_clz}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_BUILTIN_CLZ], [ ])\nfi\n\ndnl ============================================================================\ndnl Check for os_unfair_lock operations as provided on Darwin.\n\nJE_COMPILABLE([Darwin os_unfair_lock_*()], [\n#include <os/lock.h>\n#include <AvailabilityMacros.h>\n], [\n\t#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200\n\t#error \"os_unfair_lock is not supported\"\n\t#else\n\tos_unfair_lock lock = OS_UNFAIR_LOCK_INIT;\n\tos_unfair_lock_lock(&lock);\n\tos_unfair_lock_unlock(&lock);\n\t#endif\n], [je_cv_os_unfair_lock])\nif test \"x${je_cv_os_unfair_lock}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_OS_UNFAIR_LOCK], [ ])\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\nAC_ARG_ENABLE([zone-allocator],\n  [AS_HELP_STRING([--disable-zone-allocator],\n                  [Disable zone allocator for Darwin])],\n[if test \"x$enable_zone_allocator\" = \"xno\" ; then\n  enable_zone_allocator=\"0\"\nelse\n  enable_zone_allocator=\"1\"\nfi\n],\n[if test \"x${abi}\" = \"xmacho\"; then\n  enable_zone_allocator=\"1\"\nfi\n]\n)\nAC_SUBST([enable_zone_allocator])\n\nif test \"x${enable_zone_allocator}\" = \"x1\" ; then\n  if test \"x${abi}\" != \"xmacho\"; then\n    AC_MSG_ERROR([--enable-zone-allocator is only supported on Darwin])\n  fi\n  AC_DEFINE([JEMALLOC_ZONE], [ ])\nfi\n\ndnl ============================================================================\ndnl Use initial-exec TLS by default.\nAC_ARG_ENABLE([initial-exec-tls],\n  [AS_HELP_STRING([--disable-initial-exec-tls],\n                  [Disable the initial-exec tls model])],\n[if test \"x$enable_initial_exec_tls\" = \"xno\" ; then\n  enable_initial_exec_tls=\"0\"\nelse\n  enable_initial_exec_tls=\"1\"\nfi\n],\n[enable_initial_exec_tls=\"1\"]\n)\nAC_SUBST([enable_initial_exec_tls])\n\nif test \"x${je_cv_tls_model}\" = \"xyes\" -a \\\n       \"x${enable_initial_exec_tls}\" = \"x1\" ; then\n  AC_DEFINE([JEMALLOC_TLS_MODEL],\n            [__attribute__((tls_model(\"initial-exec\")))])\nelse\n  AC_DEFINE([JEMALLOC_TLS_MODEL], [ ])\nfi\n\ndnl ============================================================================\ndnl Enable background threads if possible.\n\nif test \"x${have_pthread}\" = \"x1\" -a \"x${have_dlsym}\" = \"x1\" \\\n    -a \"x${je_cv_os_unfair_lock}\" != \"xyes\" \\\n    -a \"x${je_cv_osspin}\" != \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_BACKGROUND_THREAD])\nfi\n\ndnl ============================================================================\ndnl Check for glibc malloc hooks\n\nJE_COMPILABLE([glibc malloc hook], [\n#include <stddef.h>\n\nextern void (* __free_hook)(void *ptr);\nextern void *(* __malloc_hook)(size_t size);\nextern void *(* __realloc_hook)(void *ptr, size_t size);\n], [\n  void *ptr = 0L;\n  if (__malloc_hook) ptr = __malloc_hook(1);\n  if (__realloc_hook) ptr = __realloc_hook(ptr, 2);\n  if (__free_hook && ptr) __free_hook(ptr);\n], [je_cv_glibc_malloc_hook])\nif test \"x${je_cv_glibc_malloc_hook}\" = \"xyes\" ; then\n  if test \"x${JEMALLOC_PREFIX}\" = \"x\" ; then\n    AC_DEFINE([JEMALLOC_GLIBC_MALLOC_HOOK], [ ])\n    wrap_syms=\"${wrap_syms} __free_hook __malloc_hook __realloc_hook\"\n  fi\nfi\n\nJE_COMPILABLE([glibc memalign hook], [\n#include <stddef.h>\n\nextern void *(* __memalign_hook)(size_t alignment, size_t size);\n], [\n  void *ptr = 0L;\n  if (__memalign_hook) ptr = __memalign_hook(16, 7);\n], [je_cv_glibc_memalign_hook])\nif test \"x${je_cv_glibc_memalign_hook}\" = \"xyes\" ; then\n  if test \"x${JEMALLOC_PREFIX}\" = \"x\" ; then\n    AC_DEFINE([JEMALLOC_GLIBC_MEMALIGN_HOOK], [ ])\n    wrap_syms=\"${wrap_syms} __memalign_hook\"\n  fi\nfi\n\nJE_COMPILABLE([pthreads adaptive mutexes], [\n#include <pthread.h>\n], [\n  pthread_mutexattr_t attr;\n  pthread_mutexattr_init(&attr);\n  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);\n  pthread_mutexattr_destroy(&attr);\n], [je_cv_pthread_mutex_adaptive_np])\nif test \"x${je_cv_pthread_mutex_adaptive_np}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP], [ ])\nfi\n\nJE_CFLAGS_SAVE()\nJE_CFLAGS_ADD([-D_GNU_SOURCE])\nJE_CFLAGS_ADD([-Werror])\nJE_CFLAGS_ADD([-herror_on_warning])\nJE_COMPILABLE([strerror_r returns char with gnu source], [\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n], [\n  char *buffer = (char *) malloc(100);\n  char *error = strerror_r(EINVAL, buffer, 100);\n  printf(\"%s\\n\", error);\n], [je_cv_strerror_r_returns_char_with_gnu_source])\nJE_CFLAGS_RESTORE()\nif test \"x${je_cv_strerror_r_returns_char_with_gnu_source}\" = \"xyes\" ; then\n  AC_DEFINE([JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE], [ ])\nfi\n\ndnl ============================================================================\ndnl Check for typedefs, structures, and compiler characteristics.\nAC_HEADER_STDBOOL\n\ndnl ============================================================================\ndnl Define commands that generate output files.\n\nAC_CONFIG_COMMANDS([include/jemalloc/internal/public_symbols.txt], [\n  f=\"${objroot}include/jemalloc/internal/public_symbols.txt\"\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  cp /dev/null \"${f}\"\n  for nm in `echo ${mangling_map} |tr ',' ' '` ; do\n    n=`echo ${nm} |tr ':' ' ' |awk '{print $[]1}'`\n    m=`echo ${nm} |tr ':' ' ' |awk '{print $[]2}'`\n    echo \"${n}:${m}\" >> \"${f}\"\n    dnl Remove name from public_syms so that it isn't redefined later.\n    public_syms=`for sym in ${public_syms}; do echo \"${sym}\"; done |grep -v \"^${n}\\$\" |tr '\\n' ' '`\n  done\n  for sym in ${public_syms} ; do\n    n=\"${sym}\"\n    m=\"${JEMALLOC_PREFIX}${sym}\"\n    echo \"${n}:${m}\" >> \"${f}\"\n  done\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  mangling_map=\"${mangling_map}\"\n  public_syms=\"${public_syms}\"\n  JEMALLOC_PREFIX=\"${JEMALLOC_PREFIX}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/private_symbols.awk], [\n  f=\"${objroot}include/jemalloc/internal/private_symbols.awk\"\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  export_syms=`for sym in ${public_syms}; do echo \"${JEMALLOC_PREFIX}${sym}\"; done; for sym in ${wrap_syms}; do echo \"${sym}\"; done;`\n  \"${srcdir}/include/jemalloc/internal/private_symbols.sh\" \"${SYM_PREFIX}\" ${export_syms} > \"${objroot}include/jemalloc/internal/private_symbols.awk\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  public_syms=\"${public_syms}\"\n  wrap_syms=\"${wrap_syms}\"\n  SYM_PREFIX=\"${SYM_PREFIX}\"\n  JEMALLOC_PREFIX=\"${JEMALLOC_PREFIX}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/private_symbols_jet.awk], [\n  f=\"${objroot}include/jemalloc/internal/private_symbols_jet.awk\"\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  export_syms=`for sym in ${public_syms}; do echo \"jet_${sym}\"; done; for sym in ${wrap_syms}; do echo \"${sym}\"; done;`\n  \"${srcdir}/include/jemalloc/internal/private_symbols.sh\" \"${SYM_PREFIX}\" ${export_syms} > \"${objroot}include/jemalloc/internal/private_symbols_jet.awk\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  public_syms=\"${public_syms}\"\n  wrap_syms=\"${wrap_syms}\"\n  SYM_PREFIX=\"${SYM_PREFIX}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/public_namespace.h], [\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  \"${srcdir}/include/jemalloc/internal/public_namespace.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" > \"${objroot}include/jemalloc/internal/public_namespace.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/public_unnamespace.h], [\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  \"${srcdir}/include/jemalloc/internal/public_unnamespace.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" > \"${objroot}include/jemalloc/internal/public_unnamespace.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/internal/size_classes.h], [\n  mkdir -p \"${objroot}include/jemalloc/internal\"\n  \"${SHELL}\" \"${srcdir}/include/jemalloc/internal/size_classes.sh\" \"${LG_QUANTA}\" 3 \"${LG_PAGE_SIZES}\" 2 > \"${objroot}include/jemalloc/internal/size_classes.h\"\n], [\n  SHELL=\"${SHELL}\"\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  LG_QUANTA=\"${LG_QUANTA}\"\n  LG_PAGE_SIZES=\"${LG_PAGE_SIZES}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_protos_jet.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  cat \"${srcdir}/include/jemalloc/jemalloc_protos.h.in\" | sed -e 's/@je_@/jet_/g' > \"${objroot}include/jemalloc/jemalloc_protos_jet.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_rename.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc_rename.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" > \"${objroot}include/jemalloc/jemalloc_rename.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_mangle.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc_mangle.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" je_ > \"${objroot}include/jemalloc/jemalloc_mangle.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc_mangle_jet.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc_mangle.sh\" \"${objroot}include/jemalloc/internal/public_symbols.txt\" jet_ > \"${objroot}include/jemalloc/jemalloc_mangle_jet.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n])\nAC_CONFIG_COMMANDS([include/jemalloc/jemalloc.h], [\n  mkdir -p \"${objroot}include/jemalloc\"\n  \"${srcdir}/include/jemalloc/jemalloc.sh\" \"${objroot}\" > \"${objroot}include/jemalloc/jemalloc${install_suffix}.h\"\n], [\n  srcdir=\"${srcdir}\"\n  objroot=\"${objroot}\"\n  install_suffix=\"${install_suffix}\"\n])\n\ndnl Process .in files.\nAC_SUBST([cfghdrs_in])\nAC_SUBST([cfghdrs_out])\nAC_CONFIG_HEADERS([$cfghdrs_tup])\n\ndnl ============================================================================\ndnl Generate outputs.\n\nAC_CONFIG_FILES([$cfgoutputs_tup config.stamp bin/jemalloc-config bin/jemalloc.sh bin/jeprof])\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([CONFIG             : ${CONFIG}])\nAC_MSG_RESULT([CC                 : ${CC}])\nAC_MSG_RESULT([CONFIGURE_CFLAGS   : ${CONFIGURE_CFLAGS}])\nAC_MSG_RESULT([SPECIFIED_CFLAGS   : ${SPECIFIED_CFLAGS}])\nAC_MSG_RESULT([EXTRA_CFLAGS       : ${EXTRA_CFLAGS}])\nAC_MSG_RESULT([CPPFLAGS           : ${CPPFLAGS}])\nAC_MSG_RESULT([CXX                : ${CXX}])\nAC_MSG_RESULT([CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}])\nAC_MSG_RESULT([SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}])\nAC_MSG_RESULT([EXTRA_CXXFLAGS     : ${EXTRA_CXXFLAGS}])\nAC_MSG_RESULT([LDFLAGS            : ${LDFLAGS}])\nAC_MSG_RESULT([EXTRA_LDFLAGS      : ${EXTRA_LDFLAGS}])\nAC_MSG_RESULT([DSO_LDFLAGS        : ${DSO_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([DATADIR            : ${DATADIR}])\nAC_MSG_RESULT([INCLUDEDIR         : ${INCLUDEDIR}])\nAC_MSG_RESULT([LIBDIR             : ${LIBDIR}])\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([malloc_conf        : ${config_malloc_conf}])\nAC_MSG_RESULT([autogen            : ${enable_autogen}])\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([fill               : ${enable_fill}])\nAC_MSG_RESULT([utrace             : ${enable_utrace}])\nAC_MSG_RESULT([xmalloc            : ${enable_xmalloc}])\nAC_MSG_RESULT([log                : ${enable_log}])\nAC_MSG_RESULT([lazy_lock          : ${enable_lazy_lock}])\nAC_MSG_RESULT([cache-oblivious    : ${enable_cache_oblivious}])\nAC_MSG_RESULT([cxx                : ${enable_cxx}])\nAC_MSG_RESULT([===============================================================================])\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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:output method=\"xml\" encoding=\"utf-8\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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>mallocx</refname>\n    <refname>rallocx</refname>\n    <refname>xallocx</refname>\n    <refname>sallocx</refname>\n    <refname>dallocx</refname>\n    <refname>sdallocx</refname>\n    <refname>nallocx</refname>\n    <refname>mallctl</refname>\n    <refname>mallctlnametomib</refname>\n    <refname>mallctlbymib</refname>\n    <refname>malloc_stats_print</refname>\n    <refname>malloc_usable_size</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://jemalloc.net/\">jemalloc website</ulink>.</para>\n  </refsect1>\n  <refsynopsisdiv>\n    <title>SYNOPSIS</title>\n    <funcsynopsis>\n      <funcsynopsisinfo>#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>void *<function>mallocx</function></funcdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void *<function>rallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>size_t <function>xallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</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>size_t <function>sallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>dallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>void <function>sdallocx</function></funcdef>\n          <paramdef>void *<parameter>ptr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</parameter></paramdef>\n        </funcprototype>\n        <funcprototype>\n          <funcdef>size_t <function>nallocx</function></funcdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>int <parameter>flags</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_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>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_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    </funcsynopsis>\n  </refsynopsisdiv>\n  <refsect1 id=\"description\">\n    <title>DESCRIPTION</title>\n    <refsect2>\n      <title>Standard API</title>\n\n      <para>The <function>malloc()</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()</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()</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()</function> function\n      allocates <parameter>size</parameter> bytes of memory such that the\n      allocation's base address is a 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 as\n      <code language=\"C\">sizeof(<type>void *</type>)</code>.</para>\n\n      <para>The <function>aligned_alloc()</function> function\n      allocates <parameter>size</parameter> bytes of memory such that the\n      allocation's base address is a 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()</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()</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()</function> function behaves identically to\n      <function>malloc()</function> for the specified size.</para>\n\n      <para>The <function>free()</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      <para>The <function>mallocx()</function>,\n      <function>rallocx()</function>,\n      <function>xallocx()</function>,\n      <function>sallocx()</function>,\n      <function>dallocx()</function>,\n      <function>sdallocx()</function>, and\n      <function>nallocx()</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 id=\"MALLOCX_LG_ALIGN\">\n            <term><constant>MALLOCX_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 id=\"MALLOCX_ALIGN\">\n            <term><constant>MALLOCX_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 id=\"MALLOCX_ZERO\">\n            <term><constant>MALLOCX_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 macro is\n            absent, newly allocated memory is uninitialized.</para></listitem>\n          </varlistentry>\n          <varlistentry id=\"MALLOCX_TCACHE\">\n            <term><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)\n            </constant></term>\n\n            <listitem><para>Use the thread-specific cache (tcache) specified by\n            the identifier <parameter>tc</parameter>, which must have been\n            acquired via the <link\n            linkend=\"tcache.create\"><mallctl>tcache.create</mallctl></link>\n            mallctl.  This macro does not validate that\n            <parameter>tc</parameter> specifies a valid\n            identifier.</para></listitem>\n          </varlistentry>\n          <varlistentry id=\"MALLOC_TCACHE_NONE\">\n            <term><constant>MALLOCX_TCACHE_NONE</constant></term>\n\n            <listitem><para>Do not use a thread-specific cache (tcache).  Unless\n            <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant> or\n            <constant>MALLOCX_TCACHE_NONE</constant> is specified, an\n            automatically managed tcache will be used under many circumstances.\n            This macro cannot be used in the same <parameter>flags</parameter>\n            argument as\n            <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant>.</para></listitem>\n          </varlistentry>\n          <varlistentry id=\"MALLOCX_ARENA\">\n            <term><constant>MALLOCX_ARENA(<parameter>a</parameter>)\n            </constant></term>\n\n            <listitem><para>Use the arena specified by the index\n            <parameter>a</parameter>.  This macro has no effect for regions that\n            were allocated via an arena other than the one specified.  This\n            macro does not validate that <parameter>a</parameter> specifies an\n            arena index in the valid range.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>mallocx()</function> function allocates at\n      least <parameter>size</parameter> bytes of memory, and returns a pointer\n      to the base address of the allocation.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n\n      <para>The <function>rallocx()</function> function resizes the\n      allocation at <parameter>ptr</parameter> to be at least\n      <parameter>size</parameter> bytes, and returns a pointer to the base\n      address of the resulting allocation, which may or may not have moved from\n      its original location.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n\n      <para>The <function>xallocx()</function> function resizes the\n      allocation at <parameter>ptr</parameter> in place to be at least\n      <parameter>size</parameter> bytes, and returns the real size of the\n      allocation.  If <parameter>extra</parameter> is non-zero, an attempt is\n      made to resize 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 to resize.\n      Behavior is undefined if <parameter>size</parameter> is\n      <constant>0</constant>, or if <code\n      language=\"C\">(<parameter>size</parameter> + <parameter>extra</parameter>\n      &gt; <constant>SIZE_T_MAX</constant>)</code>.</para>\n\n      <para>The <function>sallocx()</function> function returns the\n      real size of the allocation at <parameter>ptr</parameter>.</para>\n\n      <para>The <function>dallocx()</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>sdallocx()</function> function is an\n      extension of <function>dallocx()</function> with a\n      <parameter>size</parameter> parameter to allow the caller to pass in the\n      allocation size as an optimization.  The minimum valid input size is the\n      original requested size of the allocation, and the maximum valid input\n      size is the corresponding value returned by\n      <function>nallocx()</function> or\n      <function>sallocx()</function>.</para>\n\n      <para>The <function>nallocx()</function> function allocates no\n      memory, but it performs the same size computation as the\n      <function>mallocx()</function> function, and returns the real\n      size of the allocation that would result from the equivalent\n      <function>mallocx()</function> function call, or\n      <constant>0</constant> if the inputs exceed the maximum supported size\n      class and/or alignment.  Behavior is undefined if\n      <parameter>size</parameter> is <constant>0</constant>.</para>\n\n      <para>The <function>mallctl()</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()</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 <quote>Management Information Base</quote> (MIB) that can be passed\n      repeatedly to <function>mallctlbymib()</function>.  Upon\n      successful return from <function>mallctlnametomib()</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;\nsize_t mib[4];\nsize_t len, miblen;\n\nlen = sizeof(nbins);\nmallctl(\"arenas.nbins\", &nbins, &len, NULL, 0);\n\nmiblen = 4;\nmallctlnametomib(\"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, (void *)&bin_size, &len, NULL, 0);\n\t/* Do something with bin_size... */\n}]]></programlisting></para>\n\n      <varlistentry id=\"malloc_stats_print_opts\">\n      </varlistentry>\n      <para>The <function>malloc_stats_print()</function> function writes\n      summary statistics via the <parameter>write_cb</parameter> callback\n      function pointer and <parameter>cbopaque</parameter> data passed to\n      <parameter>write_cb</parameter>, or <function>malloc_message()</function>\n      if <parameter>write_cb</parameter> is <constant>NULL</constant>.  The\n      statistics are presented in human-readable form unless <quote>J</quote> is\n      specified as a character within the <parameter>opts</parameter> string, in\n      which case the statistics are presented in <ulink\n      url=\"http://www.json.org/\">JSON format</ulink>.  This function can be\n      called repeatedly.  General information that never changes during\n      execution can be omitted by specifying <quote>g</quote> as a character\n      within the <parameter>opts</parameter> string.  Note that\n      <function>malloc_message()</function> uses the\n      <function>mallctl*()</function> functions internally, so inconsistent\n      statistics can be reported if multiple threads use these functions\n      simultaneously.  If <option>--enable-stats</option> is specified during\n      configuration, <quote>m</quote>, <quote>d</quote>, and <quote>a</quote>\n      can be specified to omit merged arena, destroyed merged arena, and per\n      arena statistics, respectively; <quote>b</quote> and <quote>l</quote> can\n      be specified to omit per size class statistics for bins and large objects,\n      respectively; <quote>x</quote> can be specified to omit all mutex\n      statistics.  Unrecognized characters are silently ignored.  Note that\n      thread caching may prevent some statistics from being completely up to\n      date, since extra locking would be required to merge counters that track\n      thread cache operations.</para>\n\n      <para>The <function>malloc_usable_size()</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()</function> function is not a\n      mechanism for in-place <function>realloc()</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()</function> should not be\n      depended on, since such behavior is entirely implementation-dependent.\n      </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 specified via <option>--with-malloc-conf</option>, the\n    string pointed to by the global variable <varname>malloc_conf</varname>, the\n    <quote>name</quote> of the file referenced by the symbolic link named\n    <filename 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.  Note that\n    <varname>malloc_conf</varname> may be read before\n    <function>main()</function> is entered, so the declaration of\n    <varname>malloc_conf</varname> should specify an initializer that contains\n    the final value to be read by jemalloc.  <option>--with-malloc-conf</option>\n    and <varname>malloc_conf</varname> are compile-time mechanisms, whereas\n    <filename class=\"symlink\">/etc/malloc.conf</filename> and\n    <envar>MALLOC_CONF</envar> can be safely set any time prior to program\n    invocation.</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    <citerefentry><refentrytitle>sbrk</refentrytitle>\n    <manvolnum>2</manvolnum></citerefentry> is supported by the operating\n    system, this allocator uses both\n    <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, this allocator supports\n    thread-specific caching, in order to make it possible to completely avoid\n    synchronization for most allocation requests.  Such caching allows very fast\n    allocation in the common case, but it increases memory usage and\n    fragmentation, since a bounded number of objects can remain allocated in\n    each thread cache.</para>\n\n    <para>Memory is conceptually broken into extents.  Extents are always\n    aligned to multiples of the page size.  This alignment makes it possible to\n    find metadata for user objects quickly.  User objects are broken into two\n    categories according to size: small and large.  Contiguous small objects\n    comprise a slab, which resides within a single extent, whereas large objects\n    each have their own extents backing them.</para>\n\n    <para>Small objects are managed in groups by slabs.  Each slab maintains\n    a bitmap to track which regions are in use.  Allocation requests that are no\n    more than half the quantum (8 or 16, depending on architecture) are rounded\n    up to the nearest power of two that is at least <code\n    language=\"C\">sizeof(<type>double</type>)</code>.  All other object size\n    classes are multiples of the quantum, spaced such that there are four size\n    classes for each doubling in size, which limits internal fragmentation to\n    approximately 20% for all but the smallest size classes.  Small size classes\n    are smaller than four times the page size, and large size classes extend\n    from four times the page size up to the largest size class that does not\n    exceed <constant>PTRDIFF_MAX</constant>.</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>The <function>realloc()</function>,\n    <function>rallocx()</function>, and\n    <function>xallocx()</function> functions may resize allocations\n    without moving them under limited circumstances.  Unlike the\n    <function>*allocx()</function> API, the standard API does not\n    officially round up the usable size of an allocation to the nearest size\n    class, so technically it is necessary to call\n    <function>realloc()</function> to grow e.g. a 9-byte allocation to\n    16 bytes, or shrink a 16-byte allocation to 9 bytes.  Growth and shrinkage\n    trivially succeeds in place as long as the pre-size and post-size both round\n    up to the same size class.  No other API guarantees are made regarding\n    in-place resizing, but the current implementation also tries to resize large\n    allocations in place, as long as the pre-size and post-size are both large.\n    For shrinkage to succeed, the extent allocator must support splitting (see\n    <link\n    linkend=\"arena.i.extent_hooks\"><mallctl>arena.&lt;i&gt;.extent_hooks</mallctl></link>).\n    Growth only succeeds if the trailing memory is currently available, and the\n    extent allocator supports merging.</para>\n\n    <para>Assuming 4 KiB pages and a 16-byte quantum on a 64-bit system, the\n    size classes in each category are as shown in <xref linkend=\"size_classes\"\n    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=\"8\">Small</entry>\n          <entry>lg</entry>\n          <entry>[8]</entry>\n        </row>\n        <row>\n          <entry>16</entry>\n          <entry>[16, 32, 48, 64, 80, 96, 112, 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, 4096]</entry>\n        </row>\n        <row>\n          <entry>1 KiB</entry>\n          <entry>[5 KiB, 6 KiB, 7 KiB, 8 KiB]</entry>\n        </row>\n        <row>\n          <entry>2 KiB</entry>\n          <entry>[10 KiB, 12 KiB, 14 KiB]</entry>\n        </row>\n        <row>\n          <entry morerows=\"15\">Large</entry>\n          <entry>2 KiB</entry>\n          <entry>[16 KiB]</entry>\n        </row>\n        <row>\n          <entry>4 KiB</entry>\n          <entry>[20 KiB, 24 KiB, 28 KiB, 32 KiB]</entry>\n        </row>\n        <row>\n          <entry>8 KiB</entry>\n          <entry>[40 KiB, 48 KiB, 54 KiB, 64 KiB]</entry>\n        </row>\n        <row>\n          <entry>16 KiB</entry>\n          <entry>[80 KiB, 96 KiB, 112 KiB, 128 KiB]</entry>\n        </row>\n        <row>\n          <entry>32 KiB</entry>\n          <entry>[160 KiB, 192 KiB, 224 KiB, 256 KiB]</entry>\n        </row>\n        <row>\n          <entry>64 KiB</entry>\n          <entry>[320 KiB, 384 KiB, 448 KiB, 512 KiB]</entry>\n        </row>\n        <row>\n          <entry>128 KiB</entry>\n          <entry>[640 KiB, 768 KiB, 896 KiB, 1 MiB]</entry>\n        </row>\n        <row>\n          <entry>256 KiB</entry>\n          <entry>[1280 KiB, 1536 KiB, 1792 KiB, 2 MiB]</entry>\n        </row>\n        <row>\n          <entry>512 KiB</entry>\n          <entry>[2560 KiB, 3 MiB, 3584 KiB, 4 MiB]</entry>\n        </row>\n        <row>\n          <entry>1 MiB</entry>\n          <entry>[5 MiB, 6 MiB, 7 MiB, 8 MiB]</entry>\n        </row>\n        <row>\n          <entry>2 MiB</entry>\n          <entry>[10 MiB, 12 MiB, 14 MiB, 16 MiB]</entry>\n        </row>\n        <row>\n          <entry>4 MiB</entry>\n          <entry>[20 MiB, 24 MiB, 28 MiB, 32 MiB]</entry>\n        </row>\n        <row>\n          <entry>8 MiB</entry>\n          <entry>[40 MiB, 48 MiB, 56 MiB, 64 MiB]</entry>\n        </row>\n        <row>\n          <entry>...</entry>\n          <entry>...</entry>\n        </row>\n        <row>\n          <entry>512 PiB</entry>\n          <entry>[2560 PiB, 3 EiB, 3584 PiB, 4 EiB]</entry>\n        </row>\n        <row>\n          <entry>1 EiB</entry>\n          <entry>[5 EiB, 6 EiB, 7 EiB]</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*()</function> functions.  Value types are specified in\n    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    and <mallctl>arena.&lt;i&gt;.{initialized,purge,decay,dss}</mallctl>,\n    <literal>&lt;i&gt;</literal> equal to\n    <constant>MALLCTL_ARENAS_ALL</constant> can be used to operate on all arenas\n    or access the summation of statistics from all arenas; similarly\n    <literal>&lt;i&gt;</literal> equal to\n    <constant>MALLCTL_ARENAS_DESTROYED</constant> can be used to access the\n    summation of statistics from all destroyed arenas.  These constants can be\n    utilized either via <function>mallctlnametomib()</function> followed by\n    <function>mallctlbymib()</function>, or via code such as the following:\n    <programlisting language=\"C\"><![CDATA[\n#define STRINGIFY_HELPER(x) #x\n#define STRINGIFY(x) STRINGIFY_HELPER(x)\n\nmallctl(\"arena.\" STRINGIFY(MALLCTL_ARENAS_ALL) \".decay\",\n    NULL, NULL, NULL, 0);]]></programlisting>\n    Take special note of the <link\n    linkend=\"epoch\"><mallctl>epoch</mallctl></link> mallctl, which controls\n    refreshing of cached dynamic statistics.</para>\n\n    <variablelist>\n      <varlistentry id=\"version\">\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*()</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 id=\"background_thread\">\n        <term>\n          <mallctl>background_thread</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Enable/disable internal background worker threads.  When\n        set to true, background threads are created on demand (the number of\n        background threads will be no more than the number of CPUs or active\n        arenas).  Threads run periodically, and handle <link\n        linkend=\"arena.i.decay\">purging</link> asynchronously.  When switching\n        off, background threads are terminated synchronously.  Note that after\n        <citerefentry><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry>\n        function, the state in the child process will be disabled regardless\n        the state in parent process. See <link\n        linkend=\"stats.background_thread.num_threads\"><mallctl>stats.background_thread</mallctl></link>\n        for related stats.  <link\n        linkend=\"opt.background_thread\"><mallctl>opt.background_thread</mallctl></link>\n        can be used to set the default option.  This option is only available on\n        selected pthread-based platforms.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"max_background_threads\">\n        <term>\n          <mallctl>max_background_threads</mallctl>\n          (<type>size_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Maximum number of background worker threads that will\n        be created.  This value is capped at <link\n        linkend=\"opt.max_background_threads\"><mallctl>opt.max_background_threads</mallctl></link> at\n        startup.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"config.cache_oblivious\">\n        <term>\n          <mallctl>config.cache_oblivious</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para><option>--enable-cache-oblivious</option> was specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"config.debug\">\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 id=\"config.fill\">\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 id=\"config.lazy_lock\">\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 id=\"config.malloc_conf\">\n        <term>\n          <mallctl>config.malloc_conf</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Embedded configure-time-specified run-time options\n        string, empty unless <option>--with-malloc-conf</option> was specified\n        during build configuration.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"config.prof\">\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 id=\"config.prof_libgcc\">\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 id=\"config.prof_libunwind\">\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 id=\"config.stats\">\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\n      <varlistentry id=\"config.utrace\">\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 id=\"config.xmalloc\">\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.  Note that runtime option warnings are not included\n        (see <link\n        linkend=\"opt.abort_conf\"><mallctl>opt.abort_conf</mallctl></link> for\n        that). 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.abort_conf\">\n        <term>\n          <mallctl>opt.abort_conf</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Abort-on-invalid-configuration enabled/disabled.  If\n        true, invalid runtime options 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.metadata_thp\">\n        <term>\n          <mallctl>opt.metadata_thp</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Controls whether to allow jemalloc to use transparent\n        huge page (THP) for internal metadata (see <link\n        linkend=\"stats.metadata\">stats.metadata</link>).  <quote>always</quote>\n        allows such usage.  <quote>auto</quote> uses no THP initially, but may\n        begin to do so when metadata usage reaches certain level.  The default\n        is <quote>disabled</quote>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.retain\">\n        <term>\n          <mallctl>opt.retain</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>If true, retain unused virtual memory for later reuse\n        rather than discarding it by calling\n        <citerefentry><refentrytitle>munmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> or equivalent (see <link\n        linkend=\"stats.retained\">stats.retained</link> for related details).\n        This option is disabled by default unless discarding virtual memory is\n        known to trigger\n        platform-specific performance problems, e.g. for [64-bit] Linux, which\n        has a quirk in its virtual memory allocation algorithm that causes\n        semi-permanent VM map holes under normal jemalloc operation.  Although\n        <citerefentry><refentrytitle>munmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> causes issues on 32-bit Linux as\n        well, retaining virtual memory for 32-bit Linux is disabled by default\n        due to the practical possibility of address space exhaustion.\n        </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 if\n        <citerefentry><refentrytitle>sbrk</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> is supported by the operating\n        system: <quote>disabled</quote>, <quote>primary</quote>, and\n        <quote>secondary</quote>; otherwise only <quote>disabled</quote> is\n        supported.  The default is <quote>secondary</quote> if\n        <citerefentry><refentrytitle>sbrk</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> is supported by the operating\n        system; <quote>disabled</quote> otherwise.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.narenas\">\n        <term>\n          <mallctl>opt.narenas</mallctl>\n          (<type>unsigned</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.percpu_arena\">\n        <term>\n          <mallctl>opt.percpu_arena</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Per CPU arena mode.  Use the <quote>percpu</quote>\n        setting to enable this feature, which uses number of CPUs to determine\n        number of arenas, and bind threads to arenas dynamically based on the\n        CPU the thread runs on currently.  <quote>phycpu</quote> setting uses\n        one arena per physical CPU, which means the two hyper threads on the\n        same CPU share one arena.  Note that no runtime checking regarding the\n        availability of hyper threading is done at the moment.  When set to\n        <quote>disabled</quote>, narenas and thread to arena association will\n        not be impacted by this option.  The default is <quote>disabled</quote>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.background_thread\">\n        <term>\n          <mallctl>opt.background_thread</mallctl>\n          (<type>const bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Internal background worker threads enabled/disabled.\n        Because of potential circular dependencies, enabling background thread\n        using this option may cause crash or deadlock during initialization. For\n        a reliable way to use this feature, see <link\n        linkend=\"background_thread\">background_thread</link> for dynamic control\n        options and details.  This option is disabled by\n        default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.max_background_threads\">\n        <term>\n          <mallctl>opt.max_background_threads</mallctl>\n          (<type>const size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum number of background threads that will be created\n        if <link linkend=\"background_thread\">background_thread</link> is set.\n        Defaults to number of cpus.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.dirty_decay_ms\">\n        <term>\n          <mallctl>opt.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused dirty pages until an equivalent set of unused dirty pages\n        is purged (i.e. converted to muzzy via e.g.\n        <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>\n        if supported by the operating system, or converted to clean otherwise)\n        and/or reused.  Dirty pages are defined as previously having been\n        potentially written to by the application, and therefore consuming\n        physical memory, yet having no current use.  The pages are incrementally\n        purged according to a sigmoidal decay curve that starts and ends with\n        zero purge rate.  A decay time of 0 causes all unused dirty pages to be\n        purged immediately upon creation.  A decay time of -1 disables purging.\n        The default decay time is 10 seconds.  See <link\n        linkend=\"arenas.dirty_decay_ms\"><mallctl>arenas.dirty_decay_ms</mallctl></link>\n        and <link\n        linkend=\"arena.i.dirty_decay_ms\"><mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl></link>\n        for related dynamic control options.  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for a description of muzzy pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.muzzy_decay_ms\">\n        <term>\n          <mallctl>opt.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused muzzy pages until an equivalent set of unused muzzy pages\n        is purged (i.e. converted to clean) and/or reused.  Muzzy pages are\n        defined as previously having been unused dirty pages that were\n        subsequently purged in a manner that left them subject to the\n        reclamation whims of the operating system (e.g.\n        <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>),\n        and therefore in an indeterminate state.  The pages are incrementally\n        purged according to a sigmoidal decay curve that starts and ends with\n        zero purge rate.  A decay time of 0 causes all unused muzzy pages to be\n        purged immediately upon creation.  A decay time of -1 disables purging.\n        The default decay time is 10 seconds.  See <link\n        linkend=\"arenas.muzzy_decay_ms\"><mallctl>arenas.muzzy_decay_ms</mallctl></link>\n        and <link\n        linkend=\"arena.i.muzzy_decay_ms\"><mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl></link>\n        for related dynamic control options.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.lg_extent_max_active_fit\">\n        <term>\n          <mallctl>opt.lg_extent_max_active_fit</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>When reusing dirty extents, this determines the (log\n        base 2 of the) maximum ratio between the size of the active extent\n        selected (to split off from) and the size of the requested allocation.\n        This prevents the splitting of large active extents for smaller\n        allocations, which can reduce fragmentation over the long run\n        (especially for non-active extents).  Lower value may reduce\n        fragmentation, at the cost of extra active extents.  The default value\n        is 6, which gives a maximum ratio of 64 (2^6).</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()</function>\n        function is called at program exit via an\n        <citerefentry><refentrytitle>atexit</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> function.  <link\n        linkend=\"opt.stats_print_opts\"><mallctl>opt.stats_print_opts</mallctl></link>\n        can be combined to specify output options. 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.  Furthermore, <function>atexit()</function> may\n        allocate memory during application initialization and then deadlock\n        internally when jemalloc in turn calls\n        <function>atexit()</function>, so this option is not\n        universally usable (though the application can register its own\n        <function>atexit()</function> function with equivalent\n        functionality).  Therefore, this option should only be used with care;\n        it is 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.stats_print_opts\">\n        <term>\n          <mallctl>opt.stats_print_opts</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Options (the <parameter>opts</parameter> string) to pass\n        to the <function>malloc_stats_print()</function> at exit (enabled\n        through <link\n        linkend=\"opt.stats_print\"><mallctl>opt.stats_print</mallctl></link>). See\n        available options in <link\n        linkend=\"malloc_stats_print_opts\"><function>malloc_stats_print()</function></link>.\n        Has no effect unless <link\n        linkend=\"opt.stats_print\"><mallctl>opt.stats_print</mallctl></link> is\n        enabled.  The default is <quote></quote>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"opt.junk\">\n        <term>\n          <mallctl>opt.junk</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n          [<option>--enable-fill</option>]\n        </term>\n        <listitem><para>Junk filling.  If set to <quote>alloc</quote>, each byte\n        of uninitialized allocated memory will be initialized to\n        <literal>0xa5</literal>.  If set to <quote>free</quote>, all deallocated\n        memory will be initialized to <literal>0x5a</literal>.  If set to\n        <quote>true</quote>, both allocated and deallocated memory will be\n        initialized, and if set to <quote>false</quote>, junk filling be\n        disabled entirely.  This is intended for debugging and will impact\n        performance negatively.  This option is <quote>false</quote> by default\n        unless <option>--enable-debug</option> is specified during\n        configuration, in which case it is <quote>true</quote> by\n        default.</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()</function> and\n        <function>rallocx()</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.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        </term>\n        <listitem><para>Thread-specific caching (tcache) enabled/disabled.  When\n        there are multiple threads, each thread uses a tcache for objects up to\n        a certain size.  Thread-specific caching allows many allocations to be\n        satisfied without performing any thread synchronization, at the cost of\n        increased memory use.  See the <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.</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        </term>\n        <listitem><para>Maximum size class (log base 2) to cache in the\n        thread-specific cache (tcache).  At a minimum, all small size classes\n        are 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.thp\">\n        <term>\n          <mallctl>opt.thp</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Transparent hugepage (THP) mode. Settings \"always\",\n        \"never\" and \"default\" are available if THP is supported by the operating\n        system.  The \"always\" setting enables transparent hugepage for all user\n        memory mappings with\n        <parameter><constant>MADV_HUGEPAGE</constant></parameter>; \"never\"\n        ensures no transparent hugepage with\n        <parameter><constant>MADV_NOHUGEPAGE</constant></parameter>; the default\n        setting \"default\" makes no changes.  Note that: this option does not\n        affect THP for jemalloc internal metadata (see <link\n        linkend=\"opt.metadata_thp\"><mallctl>opt.metadata_thp</mallctl></link>);\n        in addition, for arenas with customized <link\n        linkend=\"arena.i.extent_hooks\"><mallctl>extent_hooks</mallctl></link>,\n        this option is bypassed as it is implemented as part of the default\n        extent hooks.</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 <command>jeprof</command> command, which is based on the\n        <command>pprof</command> that is developed as part of the <ulink\n        url=\"http://code.google.com/p/gperftools/\">gperftools\n        package</ulink>.  See <link linkend=\"heap_profile_format\">HEAP PROFILE\n        FORMAT</link> for heap profile format documentation.</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.prof_thread_active_init\">\n        <term>\n          <mallctl>opt.prof_thread_active_init</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Initial setting for <link\n        linkend=\"thread.prof.active\"><mallctl>thread.prof.active</mallctl></link>\n        in newly created threads.  The initial setting for newly created threads\n        can also be changed during execution via the <link\n        linkend=\"prof.thread_active_init\"><mallctl>prof.thread_active_init</mallctl></link>\n        mallctl.  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>size_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>Set the initial state of <link\n        linkend=\"prof.gdump\"><mallctl>prof.gdump</mallctl></link>, which when\n        enabled triggers a memory profile dump every time the total virtual\n        memory exceeds the previous maximum.  This option is disabled by\n        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.  Note that <function>atexit()</function> may allocate\n        memory during application initialization and then deadlock internally\n        when jemalloc in turn calls <function>atexit()</function>, so\n        this option is not universally usable (though the application can\n        register its own <function>atexit()</function> function with\n        equivalent functionality).  This option is disabled by\n        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 id=\"thread.arena\">\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=\"arena.i.initialized\"><mallctl>arena.i.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 id=\"thread.allocatedp\">\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*()</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 id=\"thread.deallocatedp\">\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*()</function> calls.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.tcache.enabled\">\n        <term>\n          <mallctl>thread.tcache.enabled</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\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        linkend=\"thread.tcache.flush\"><mallctl>thread.tcache.flush</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.tcache.flush\">\n        <term>\n          <mallctl>thread.tcache.flush</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Flush calling thread's thread-specific cache (tcache).\n        This interface releases all cached objects and internal data structures\n        associated with the calling thread's tcache.  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=\"thread.prof.name\">\n        <term>\n          <mallctl>thread.prof.name</mallctl>\n          (<type>const char *</type>)\n          <literal>r-</literal> or\n          <literal>-w</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Get/set the descriptive name associated with the calling\n        thread in memory profile dumps.  An internal copy of the name string is\n        created, so the input string need not be maintained after this interface\n        completes execution.  The output string of this interface should be\n        copied for non-ephemeral uses, because multiple implementation details\n        can cause asynchronous string deallocation.  Furthermore, each\n        invocation of this interface can only read or write; simultaneous\n        read/write is not supported due to string lifetime limitations.  The\n        name string must be nil-terminated and comprised only of characters in\n        the sets recognized\n        by <citerefentry><refentrytitle>isgraph</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry> and\n        <citerefentry><refentrytitle>isblank</refentrytitle>\n        <manvolnum>3</manvolnum></citerefentry>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"thread.prof.active\">\n        <term>\n          <mallctl>thread.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 for the\n        calling thread.  This is an activation mechanism in addition to <link\n        linkend=\"prof.active\"><mallctl>prof.active</mallctl></link>; both must\n        be active for the calling thread to sample.  This flag is enabled by\n        default.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"tcache.create\">\n        <term>\n          <mallctl>tcache.create</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Create an explicit thread-specific cache (tcache) and\n        return an identifier that can be passed to the <link\n        linkend=\"MALLOCX_TCACHE\"><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant></link>\n        macro to explicitly use the specified cache rather than the\n        automatically managed one that is used by default.  Each explicit cache\n        can be used by only one thread at a time; the application must assure\n        that this constraint holds.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"tcache.flush\">\n        <term>\n          <mallctl>tcache.flush</mallctl>\n          (<type>unsigned</type>)\n          <literal>-w</literal>\n        </term>\n        <listitem><para>Flush the specified thread-specific cache (tcache).  The\n        same considerations apply to this interface as to <link\n        linkend=\"thread.tcache.flush\"><mallctl>thread.tcache.flush</mallctl></link>,\n        except that the tcache will never be automatically discarded.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"tcache.destroy\">\n        <term>\n          <mallctl>tcache.destroy</mallctl>\n          (<type>unsigned</type>)\n          <literal>-w</literal>\n        </term>\n        <listitem><para>Flush the specified thread-specific cache (tcache) and\n        make the identifier available for use during a future tcache creation.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.initialized\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.initialized</mallctl>\n          (<type>bool</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Get whether the specified arena's statistics are\n        initialized (i.e. the arena was initialized prior to the current epoch).\n        This interface can also be nominally used to query whether the merged\n        statistics corresponding to <constant>MALLCTL_ARENAS_ALL</constant> are\n        initialized (always true).</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.decay\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.decay</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Trigger decay-based purging of unused dirty/muzzy pages\n        for arena &lt;i&gt;, or for all arenas if &lt;i&gt; equals\n        <constant>MALLCTL_ARENAS_ALL</constant>.  The proportion of unused\n        dirty/muzzy pages to be purged depends on the current time; see <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        and <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzy_decay_ms</mallctl></link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.purge\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.purge</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Purge all unused dirty pages for arena &lt;i&gt;, or for\n        all arenas if &lt;i&gt; equals <constant>MALLCTL_ARENAS_ALL</constant>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.reset\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.reset</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Discard all of the arena's extant allocations.  This\n        interface can only be used with arenas explicitly created via <link\n        linkend=\"arenas.create\"><mallctl>arenas.create</mallctl></link>.  None\n        of the arena's discarded/cached allocations may accessed afterward.  As\n        part of this requirement, all thread caches which were used to\n        allocate/deallocate in conjunction with the arena must be flushed\n        beforehand.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.destroy\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.destroy</mallctl>\n          (<type>void</type>)\n          <literal>--</literal>\n        </term>\n        <listitem><para>Destroy the arena.  Discard all of the arena's extant\n        allocations using the same mechanism as for <link\n        linkend=\"arena.i.reset\"><mallctl>arena.&lt;i&gt;.reset</mallctl></link>\n        (with all the same constraints and side effects), merge the arena stats\n        into those accessible at arena index\n        <constant>MALLCTL_ARENAS_DESTROYED</constant>, and then completely\n        discard all metadata associated with the arena.  Future calls to <link\n        linkend=\"arenas.create\"><mallctl>arenas.create</mallctl></link> may\n        recycle the arena index.  Destruction will fail if any threads are\n        currently associated with the arena as a result of calls to <link\n        linkend=\"thread.arena\"><mallctl>thread.arena</mallctl></link>.</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        <constant>MALLCTL_ARENAS_ALL</constant>.  See <link\n        linkend=\"opt.dss\"><mallctl>opt.dss</mallctl></link> for supported\n        settings.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.dirty_decay_ms\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current per-arena approximate time in milliseconds from\n        the creation of a set of unused dirty pages until an equivalent set of\n        unused dirty pages is purged and/or reused.  Each time this interface is\n        set, all currently unused dirty pages are considered to have fully\n        decayed, which causes immediate purging of all unused dirty pages unless\n        the decay time is set to -1 (i.e. purging disabled).  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.muzzy_decay_ms\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current per-arena approximate time in milliseconds from\n        the creation of a set of unused muzzy pages until an equivalent set of\n        unused muzzy pages is purged and/or reused.  Each time this interface is\n        set, all currently unused muzzy pages are considered to have fully\n        decayed, which causes immediate purging of all unused muzzy pages unless\n        the decay time is set to -1 (i.e. purging disabled).  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.retain_grow_limit\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.retain_grow_limit</mallctl>\n          (<type>size_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Maximum size to grow retained region (only relevant when\n        <link linkend=\"opt.retain\"><mallctl>opt.retain</mallctl></link> is\n        enabled).  This controls the maximum increment to expand virtual memory,\n        or allocation through <link\n        linkend=\"arena.i.extent_hooks\"><mallctl>arena.&lt;i&gt;extent_hooks</mallctl></link>.\n        In particular, if customized extent hooks reserve physical memory\n        (e.g. 1G huge pages), this is useful to control the allocation hook's\n        input size.  The default is no limit.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arena.i.extent_hooks\">\n        <term>\n          <mallctl>arena.&lt;i&gt;.extent_hooks</mallctl>\n          (<type>extent_hooks_t *</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Get or set the extent management hook functions for\n        arena &lt;i&gt;.  The functions must be capable of operating on all\n        extant extents associated with arena &lt;i&gt;, usually by passing\n        unknown extents to the replaced functions.  In practice, it is feasible\n        to control allocation for arenas explicitly created via <link\n        linkend=\"arenas.create\"><mallctl>arenas.create</mallctl></link> such\n        that all extents originate from an application-supplied extent allocator\n        (by specifying the custom extent hook functions during arena creation),\n        but the automatically created arenas will have already created extents\n        prior to the application having an opportunity to take over extent\n        allocation.</para>\n\n        <programlisting language=\"C\"><![CDATA[\ntypedef extent_hooks_s extent_hooks_t;\nstruct extent_hooks_s {\n\textent_alloc_t\t\t*alloc;\n\textent_dalloc_t\t\t*dalloc;\n\textent_destroy_t\t*destroy;\n\textent_commit_t\t\t*commit;\n\textent_decommit_t\t*decommit;\n\textent_purge_t\t\t*purge_lazy;\n\textent_purge_t\t\t*purge_forced;\n\textent_split_t\t\t*split;\n\textent_merge_t\t\t*merge;\n};]]></programlisting>\n        <para>The <type>extent_hooks_t</type> structure comprises function\n        pointers which are described individually below.  jemalloc uses these\n        functions to manage extent lifetime, which starts off with allocation of\n        mapped committed memory, in the simplest case followed by deallocation.\n        However, there are performance and platform reasons to retain extents\n        for later reuse.  Cleanup attempts cascade from deallocation to decommit\n        to forced purging to lazy purging, which gives the extent management\n        functions opportunities to reject the most permanent cleanup operations\n        in favor of less permanent (and often less costly) operations.  All\n        operations except allocation can be universally opted out of by setting\n        the hook pointers to <constant>NULL</constant>, or selectively opted out\n        of by returning failure.  Note that once the extent hook is set, the\n        structure is accessed directly by the associated arenas, so it must\n        remain valid for the entire lifetime of the arenas.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef void *<function>(extent_alloc_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>new_addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>alignment</parameter></paramdef>\n          <paramdef>bool *<parameter>zero</parameter></paramdef>\n          <paramdef>bool *<parameter>commit</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent allocation function conforms to the\n        <type>extent_alloc_t</type> type and upon success returns a pointer to\n        <parameter>size</parameter> bytes of mapped memory on behalf of arena\n        <parameter>arena_ind</parameter> such that the extent's base address is\n        a multiple of <parameter>alignment</parameter>, as well as setting\n        <parameter>*zero</parameter> to indicate whether the extent is zeroed\n        and <parameter>*commit</parameter> to indicate whether the extent is\n        committed.  Upon error the function returns <constant>NULL</constant>\n        and leaves <parameter>*zero</parameter> and\n        <parameter>*commit</parameter> unmodified.  The\n        <parameter>size</parameter> parameter is always a multiple of the page\n        size.  The <parameter>alignment</parameter> parameter is always a power\n        of two at least as large as the page size.  Zeroing is mandatory if\n        <parameter>*zero</parameter> is true upon function entry.  Committing is\n        mandatory if <parameter>*commit</parameter> is true upon function entry.\n        If <parameter>new_addr</parameter> is not <constant>NULL</constant>, the\n        returned pointer must be <parameter>new_addr</parameter> on success or\n        <constant>NULL</constant> on error.  Committed memory may be committed\n        in absolute terms as on a system that does not overcommit, or in\n        implicit terms as on a system that overcommits and satisfies physical\n        memory needs on demand via soft page faults.  Note that replacing the\n        default extent allocation function makes the arena's <link\n        linkend=\"arena.i.dss\"><mallctl>arena.&lt;i&gt;.dss</mallctl></link>\n        setting irrelevant.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_dalloc_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>\n        An extent deallocation function conforms to the\n        <type>extent_dalloc_t</type> type and deallocates an extent at given\n        <parameter>addr</parameter> and <parameter>size</parameter> with\n        <parameter>committed</parameter>/decommited memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>, returning false upon\n        success.  If the function returns true, this indicates opt-out from\n        deallocation; the virtual memory mapping associated with the extent\n        remains mapped, in the same commit state, and available for future use,\n        in which case it will be automatically retained for later reuse.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef void <function>(extent_destroy_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>\n        An extent destruction function conforms to the\n        <type>extent_destroy_t</type> type and unconditionally destroys an\n        extent at given <parameter>addr</parameter> and\n        <parameter>size</parameter> with\n        <parameter>committed</parameter>/decommited memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>.  This function may be\n        called to destroy retained extents during arena destruction (see <link\n        linkend=\"arena.i.destroy\"><mallctl>arena.&lt;i&gt;.destroy</mallctl></link>).</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_commit_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>offset</parameter></paramdef>\n          <paramdef>size_t <parameter>length</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent commit function conforms to the\n        <type>extent_commit_t</type> type and commits zeroed physical memory to\n        back pages within an extent at given <parameter>addr</parameter> and\n        <parameter>size</parameter> at <parameter>offset</parameter> bytes,\n        extending for <parameter>length</parameter> on behalf of arena\n        <parameter>arena_ind</parameter>, returning false upon success.\n        Committed memory may be committed in absolute terms as on a system that\n        does not overcommit, or in implicit terms as on a system that\n        overcommits and satisfies physical memory needs on demand via soft page\n        faults. If the function returns true, this indicates insufficient\n        physical memory to satisfy the request.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_decommit_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>offset</parameter></paramdef>\n          <paramdef>size_t <parameter>length</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent decommit function conforms to the\n        <type>extent_decommit_t</type> type and decommits any physical memory\n        that is backing pages within an extent at given\n        <parameter>addr</parameter> and <parameter>size</parameter> at\n        <parameter>offset</parameter> bytes, extending for\n        <parameter>length</parameter> on behalf of arena\n        <parameter>arena_ind</parameter>, returning false upon success, in which\n        case the pages will be committed via the extent commit function before\n        being reused.  If the function returns true, this indicates opt-out from\n        decommit; the memory remains committed and available for future use, in\n        which case it will be automatically retained for later reuse.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_purge_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>offset</parameter></paramdef>\n          <paramdef>size_t <parameter>length</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent purge function conforms to the\n        <type>extent_purge_t</type> type and discards physical pages\n        within the virtual memory mapping associated with an extent at given\n        <parameter>addr</parameter> and <parameter>size</parameter> at\n        <parameter>offset</parameter> bytes, extending for\n        <parameter>length</parameter> on behalf of arena\n        <parameter>arena_ind</parameter>.  A lazy extent purge function (e.g.\n        implemented via\n        <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>)\n        can delay purging indefinitely and leave the pages within the purged\n        virtual memory range in an indeterminite state, whereas a forced extent\n        purge function immediately purges, and the pages within the virtual\n        memory range will be zero-filled the next time they are accessed.  If\n        the function returns true, this indicates failure to purge.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_split_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr</parameter></paramdef>\n          <paramdef>size_t <parameter>size</parameter></paramdef>\n          <paramdef>size_t <parameter>size_a</parameter></paramdef>\n          <paramdef>size_t <parameter>size_b</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent split function conforms to the\n        <type>extent_split_t</type> type and optionally splits an extent at\n        given <parameter>addr</parameter> and <parameter>size</parameter> into\n        two adjacent extents, the first of <parameter>size_a</parameter> bytes,\n        and the second of <parameter>size_b</parameter> bytes, operating on\n        <parameter>committed</parameter>/decommitted memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>, returning false upon\n        success.  If the function returns true, this indicates that the extent\n        remains unsplit and therefore should continue to be operated on as a\n        whole.</para>\n\n        <funcsynopsis><funcprototype>\n          <funcdef>typedef bool <function>(extent_merge_t)</function></funcdef>\n          <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>\n          <paramdef>void *<parameter>addr_a</parameter></paramdef>\n          <paramdef>size_t <parameter>size_a</parameter></paramdef>\n          <paramdef>void *<parameter>addr_b</parameter></paramdef>\n          <paramdef>size_t <parameter>size_b</parameter></paramdef>\n          <paramdef>bool <parameter>committed</parameter></paramdef>\n          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>\n        </funcprototype></funcsynopsis>\n        <literallayout></literallayout>\n        <para>An extent merge function conforms to the\n        <type>extent_merge_t</type> type and optionally merges adjacent extents,\n        at given <parameter>addr_a</parameter> and <parameter>size_a</parameter>\n        with given <parameter>addr_b</parameter> and\n        <parameter>size_b</parameter> into one contiguous extent, operating on\n        <parameter>committed</parameter>/decommitted memory as indicated, on\n        behalf of arena <parameter>arena_ind</parameter>, returning false upon\n        success.  If the function returns true, this indicates that the extents\n        remain distinct mappings and therefore should continue to be operated on\n        independently.</para>\n        </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.dirty_decay_ms\">\n        <term>\n          <mallctl>arenas.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current default per-arena approximate time in\n        milliseconds from the creation of a set of unused dirty pages until an\n        equivalent set of unused dirty pages is purged and/or reused, used to\n        initialize <link\n        linkend=\"arena.i.dirty_decay_ms\"><mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl></link>\n        during arena creation.  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.muzzy_decay_ms\">\n        <term>\n          <mallctl>arenas.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Current default per-arena approximate time in\n        milliseconds from the creation of a set of unused muzzy pages until an\n        equivalent set of unused muzzy pages is purged and/or reused, used to\n        initialize <link\n        linkend=\"arena.i.muzzy_decay_ms\"><mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl></link>\n        during arena creation.  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for additional information.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.quantum\">\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 id=\"arenas.page\">\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 id=\"arenas.tcache_max\">\n        <term>\n          <mallctl>arenas.tcache_max</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Maximum thread-cached size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.nbins\">\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 id=\"arenas.nhbins\">\n        <term>\n          <mallctl>arenas.nhbins</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\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 id=\"arenas.bin.i.nregs\">\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 slab.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.bin.i.slab_size\">\n        <term>\n          <mallctl>arenas.bin.&lt;i&gt;.slab_size</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of bytes per slab.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.nlextents\">\n        <term>\n          <mallctl>arenas.nlextents</mallctl>\n          (<type>unsigned</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Total number of large size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.lextent.i.size\">\n        <term>\n          <mallctl>arenas.lextent.&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 id=\"arenas.create\">\n        <term>\n          <mallctl>arenas.create</mallctl>\n          (<type>unsigned</type>, <type>extent_hooks_t *</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Explicitly create a new arena outside the range of\n        automatically managed arenas, with optionally specified extent hooks,\n        and return the new arena index.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"arenas.lookup\">\n        <term>\n          <mallctl>arenas.lookup</mallctl>\n          (<type>unsigned</type>, <type>void*</type>)\n          <literal>rw</literal>\n        </term>\n        <listitem><para>Index of the arena to which an allocation belongs to.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.thread_active_init\">\n        <term>\n          <mallctl>prof.thread_active_init</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Control the initial setting for <link\n        linkend=\"thread.prof.active\"><mallctl>thread.prof.active</mallctl></link>\n        in newly created threads.  See the <link\n        linkend=\"opt.prof_thread_active_init\"><mallctl>opt.prof_thread_active_init</mallctl></link>\n        option for additional information.</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, as well as the interrelated <link\n        linkend=\"thread.prof.active\"><mallctl>thread.prof.active</mallctl></link>\n        mallctl.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.dump\">\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 id=\"prof.gdump\">\n        <term>\n          <mallctl>prof.gdump</mallctl>\n          (<type>bool</type>)\n          <literal>rw</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>When enabled, trigger a memory profile dump every time\n        the total virtual memory exceeds the previous maximum.  Profiles are\n        dumped to 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.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.reset\">\n        <term>\n          <mallctl>prof.reset</mallctl>\n          (<type>size_t</type>)\n          <literal>-w</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Reset all memory profile statistics, and optionally\n        update the sample rate (see <link\n        linkend=\"opt.lg_prof_sample\"><mallctl>opt.lg_prof_sample</mallctl></link>\n        and <link\n        linkend=\"prof.lg_sample\"><mallctl>prof.lg_sample</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.lg_sample\">\n        <term>\n          <mallctl>prof.lg_sample</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-prof</option>]\n        </term>\n        <listitem><para>Get the current sample rate (see <link\n        linkend=\"opt.lg_prof_sample\"><mallctl>opt.lg_prof_sample</mallctl></link>).\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"prof.interval\">\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        interval-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.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>,\n        <link linkend=\"stats.arenas.i.pmuzzy\">\n        <mallctl>stats.arenas.&lt;i&gt;.pmuzzy</mallctl></link>, nor pages\n        entirely devoted to allocator metadata.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.metadata\">\n        <term>\n          <mallctl>stats.metadata</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Total number of bytes dedicated to metadata, which\n        comprise base allocations used for bootstrap-sensitive allocator\n        metadata structures (see <link\n        linkend=\"stats.arenas.i.base\"><mallctl>stats.arenas.&lt;i&gt;.base</mallctl></link>)\n        and internal allocations (see <link\n        linkend=\"stats.arenas.i.internal\"><mallctl>stats.arenas.&lt;i&gt;.internal</mallctl></link>).\n        Transparent huge page (enabled with <link\n        linkend=\"opt.metadata_thp\">opt.metadata_thp</link>) usage is not\n        considered.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.metadata_thp\">\n        <term>\n          <mallctl>stats.metadata_thp</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of transparent huge pages (THP) used for\n        metadata.  See <link\n        linkend=\"stats.metadata\"><mallctl>stats.metadata</mallctl></link> and\n        <link linkend=\"opt.metadata_thp\">opt.metadata_thp</link>) for\n        details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.resident\">\n        <term>\n          <mallctl>stats.resident</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Maximum number of bytes in physically resident data\n        pages mapped by the allocator, comprising all pages dedicated to\n        allocator metadata, pages backing active allocations, and unused dirty\n        pages.  This is a maximum rather than precise because pages may not\n        actually be physically resident if they correspond to demand-zeroed\n        virtual memory that has not yet been touched.  This is a multiple of the\n        page size, and is larger than <link\n        linkend=\"stats.active\"><mallctl>stats.active</mallctl></link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mapped\">\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 active extents mapped by the\n        allocator.  This is larger than <link\n        linkend=\"stats.active\"><mallctl>stats.active</mallctl></link>.  This\n        does not include inactive extents, even those that contain unused dirty\n        pages, which means that there is no strict ordering between this and\n        <link\n        linkend=\"stats.resident\"><mallctl>stats.resident</mallctl></link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.retained\">\n        <term>\n          <mallctl>stats.retained</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 virtual memory mappings that\n        were retained rather than being returned to the operating system via\n        e.g. <citerefentry><refentrytitle>munmap</refentrytitle>\n        <manvolnum>2</manvolnum></citerefentry> or similar.  Retained virtual\n        memory is typically untouched, decommitted, or purged, so it has no\n        strongly associated physical memory (see <link\n        linkend=\"arena.i.extent_hooks\">extent hooks</link> for details).\n        Retained memory is excluded from mapped memory statistics, e.g. <link\n        linkend=\"stats.mapped\"><mallctl>stats.mapped</mallctl></link>.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.background_thread.num_threads\">\n        <term>\n          <mallctl>stats.background_thread.num_threads</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para> Number of <link linkend=\"background_thread\">background\n        threads</link> running currently.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.background_thread.num_runs\">\n        <term>\n          <mallctl>stats.background_thread.num_runs</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para> Total number of runs from all <link\n        linkend=\"background_thread\">background threads</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.background_thread.run_interval\">\n        <term>\n          <mallctl>stats.background_thread.run_interval</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para> Average run interval in nanoseconds of <link\n        linkend=\"background_thread\">background threads</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.ctl\">\n        <term>\n          <mallctl>stats.mutexes.ctl.{counter};</mallctl>\n          (<type>counter specific type</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>ctl</varname> mutex (global\n        scope; mallctl related).  <mallctl>{counter}</mallctl> is one of the\n        counters below:</para>\n        <varlistentry id=\"mutex_counters\">\n          <listitem><para><varname>num_ops</varname> (<type>uint64_t</type>):\n          Total number of lock acquisition operations on this mutex.</para>\n\n\t  <para><varname>num_spin_acq</varname> (<type>uint64_t</type>): Number\n\t  of times the mutex was spin-acquired.  When the mutex is currently\n\t  locked and cannot be acquired immediately, a short period of\n\t  spin-retry within jemalloc will be performed.  Acquired through spin\n\t  generally means the contention was lightweight and not causing context\n\t  switches.</para>\n\n\t  <para><varname>num_wait</varname> (<type>uint64_t</type>): Number of\n\t  times the mutex was wait-acquired, which means the mutex contention\n\t  was not solved by spin-retry, and blocking operation was likely\n\t  involved in order to acquire the mutex.  This event generally implies\n\t  higher cost / longer delay, and should be investigated if it happens\n\t  often.</para>\n\n\t  <para><varname>max_wait_time</varname> (<type>uint64_t</type>):\n\t  Maximum length of time in nanoseconds spent on a single wait-acquired\n\t  lock operation.  Note that to avoid profiling overhead on the common\n\t  path, this does not consider spin-acquired cases.</para>\n\n\t  <para><varname>total_wait_time</varname> (<type>uint64_t</type>):\n\t  Cumulative time in nanoseconds spent on wait-acquired lock operations.\n\t  Similarly, spin-acquired cases are not considered.</para>\n\n\t  <para><varname>max_num_thds</varname> (<type>uint32_t</type>): Maximum\n\t  number of threads waiting on this mutex simultaneously.  Similarly,\n\t  spin-acquired cases are not considered.</para>\n\n\t  <para><varname>num_owner_switch</varname> (<type>uint64_t</type>):\n\t  Number of times the current mutex owner is different from the previous\n\t  one.  This event does not generally imply an issue; rather it is an\n\t  indicator of how often the protected data are accessed by different\n\t  threads.\n\t  </para>\n\t  </listitem>\n\t</varlistentry>\n\t</listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.background_thread\">\n        <term>\n          <mallctl>stats.mutexes.background_thread.{counter}</mallctl>\n\t  (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>background_thread</varname> mutex\n        (global scope; <link\n        linkend=\"background_thread\"><mallctl>background_thread</mallctl></link>\n        related).  <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.prof\">\n        <term>\n          <mallctl>stats.mutexes.prof.{counter}</mallctl>\n\t  (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>prof</varname> mutex (global\n        scope; profiling related).  <mallctl>{counter}</mallctl> is one of the\n        counters in <link linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.mutexes.reset\">\n        <term>\n          <mallctl>stats.mutexes.reset</mallctl>\n\t  (<type>void</type>) <literal>--</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Reset all mutex profile statistics, including global\n        mutexes, arena mutexes and bin mutexes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.dss\">\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 id=\"stats.arenas.i.dirty_decay_ms\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused dirty pages until an equivalent set of unused dirty pages\n        is purged and/or reused.  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_decay_ms\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_decay_ms</mallctl>\n          (<type>ssize_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Approximate time in milliseconds from the creation of a\n        set of unused muzzy pages until an equivalent set of unused muzzy pages\n        is purged and/or reused.  See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.nthreads\">\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 id=\"stats.arenas.i.uptime\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.uptime</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Time elapsed (in nanoseconds) since the arena was\n        created.  If &lt;i&gt; equals <constant>0</constant> or\n        <constant>MALLCTL_ARENAS_ALL</constant>, this is the uptime since malloc\n        initialization.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.pactive\">\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 extents.</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 extents that are\n        potentially dirty, and for which <function>madvise()</function> or\n        similar has not been called.  See <link\n        linkend=\"opt.dirty_decay_ms\"><mallctl>opt.dirty_decay_ms</mallctl></link>\n        for a description of dirty pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.pmuzzy\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.pmuzzy</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n        </term>\n        <listitem><para>Number of pages within unused extents that are muzzy.\n        See <link\n        linkend=\"opt.muzzy_decay_ms\"><mallctl>opt.muzzy_decay_ms</mallctl></link>\n        for a description of muzzy pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mapped\">\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 id=\"stats.arenas.i.retained\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.retained</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of retained bytes.  See <link\n        linkend=\"stats.retained\"><mallctl>stats.retained</mallctl></link> for\n        details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.base\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.base</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>\n        Number of bytes dedicated to bootstrap-sensitive allocator metadata\n        structures.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.internal\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.internal</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of bytes dedicated to internal allocations.\n        Internal allocations differ from application-originated allocations in\n        that they are for internal use, and that they are omitted from heap\n        profiles.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.metadata_thp\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.metadata_thp</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of transparent huge pages (THP) used for\n        metadata.  See <link linkend=\"opt.metadata_thp\">opt.metadata_thp</link>\n        for details.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.resident\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.resident</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Maximum number of bytes in physically resident data\n        pages mapped by the arena, comprising all pages dedicated to allocator\n        metadata, pages backing active allocations, and unused dirty pages.\n        This is a maximum rather than precise because pages may not actually be\n        physically resident if they correspond to demand-zeroed virtual memory\n        that has not yet been touched.  This is a multiple of the page\n        size.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.dirty_npurge\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_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 id=\"stats.arenas.i.dirty_nmadvise\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_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()</function> or similar\n        calls made to purge dirty pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.dirty_purged\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.dirty_purged</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of dirty pages purged.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_npurge\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_npurge</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of muzzy page purge sweeps performed.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_nmadvise\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_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()</function> or similar\n        calls made to purge muzzy pages.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.muzzy_purged\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.muzzy_purged</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Number of muzzy pages purged.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.small.allocated\">\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 id=\"stats.arenas.i.small.nmalloc\">\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 times a small allocation was\n        requested from the arena's bins, whether to fill the relevant tcache if\n        <link linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is\n        enabled, or to directly satisfy an allocation request\n        otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.small.ndalloc\">\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 times a small allocation was\n        returned to the arena's bins, whether to flush the relevant tcache if\n        <link linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is\n        enabled, or to directly deallocate an allocation\n        otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.small.nrequests\">\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 allocation requests satisfied by\n        all bin size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.large.allocated\">\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 id=\"stats.arenas.i.large.nmalloc\">\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 times a large extent was allocated\n        from the arena, whether to fill the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly satisfy\n        an allocation request otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.large.ndalloc\">\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 times a large extent was returned\n        to the arena, whether to flush the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly\n        deallocate an allocation otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.large.nrequests\">\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 allocation requests satisfied by\n        all large size classes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nmalloc\">\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 times a bin region of the\n        corresponding size class was allocated from the arena, whether to fill\n        the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled, or\n        to directly satisfy an allocation request otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.ndalloc\">\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 times a bin region of the\n        corresponding size class was returned to the arena, whether to flush the\n        relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled, or\n        to directly deallocate an allocation otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nrequests\">\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 requests satisfied by\n        bin regions of the corresponding size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.curregs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curregs</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of regions for this size\n        class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nfills\">\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        </term>\n        <listitem><para>Cumulative number of tcache fills.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nflushes\">\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        </term>\n        <listitem><para>Cumulative number of tcache flushes.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nslabs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nslabs</mallctl>\n          (<type>uint64_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Cumulative number of slabs created.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.nreslabs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nreslabs</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 slab from which\n        to allocate changed.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.j.curslabs\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curslabs</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of slabs.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.bins.mutex\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.mutex.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on\n        <varname>arena.&lt;i&gt;.bins.&lt;j&gt;</varname> mutex (arena bin\n        scope; bin operation related).  <mallctl>{counter}</mallctl> is one of\n        the counters in <link linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.nmalloc\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&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 times a large extent of the\n        corresponding size class was allocated from the arena, whether to fill\n        the relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly satisfy\n        an allocation request otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.ndalloc\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&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 times a large extent of the\n        corresponding size class was returned to the arena, whether to flush the\n        relevant tcache if <link\n        linkend=\"opt.tcache\"><mallctl>opt.tcache</mallctl></link> is enabled and\n        the size class is within the range being cached, or to directly\n        deallocate an allocation otherwise.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.nrequests\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&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 satisfied by\n        large extents of the corresponding size class.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.lextents.j.curlextents\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.lextents.&lt;j&gt;.curlextents</mallctl>\n          (<type>size_t</type>)\n          <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Current number of large allocations for this size class.\n        </para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.large\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.large.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.large</varname>\n        mutex (arena scope; large allocation related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extent_avail\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extent_avail.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extent_avail\n        </varname> mutex (arena scope; extent avail related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extents_dirty\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_dirty.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_dirty\n        </varname> mutex (arena scope; dirty extents related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extents_muzzy\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_muzzy.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_muzzy\n        </varname> mutex (arena scope; muzzy extents related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.extents_retained\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_retained.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_retained\n        </varname> mutex (arena scope; retained extents related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.decay_dirty\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.decay_dirty.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.decay_dirty\n        </varname> mutex (arena scope; decay for dirty pages related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.decay_muzzy\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.decay_muzzy.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.decay_muzzy\n        </varname> mutex (arena scope; decay for muzzy pages related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.base\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.base.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on <varname>arena.&lt;i&gt;.base</varname>\n        mutex (arena scope; base allocator related).\n        <mallctl>{counter}</mallctl> is one of the counters in <link\n        linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n      <varlistentry id=\"stats.arenas.i.mutexes.tcache_list\">\n        <term>\n          <mallctl>stats.arenas.&lt;i&gt;.mutexes.tcache_list.{counter}</mallctl>\n          (<type>counter specific type</type>) <literal>r-</literal>\n          [<option>--enable-stats</option>]\n        </term>\n        <listitem><para>Statistics on\n        <varname>arena.&lt;i&gt;.tcache_list</varname> mutex (arena scope;\n        tcache to arena association related).  This mutex is expected to be\n        accessed less often.  <mallctl>{counter}</mallctl> is one of the\n        counters in <link linkend=\"mutex_counters\">mutex profiling\n        counters</link>.</para></listitem>\n      </varlistentry>\n\n    </variablelist>\n  </refsect1>\n  <refsect1 id=\"heap_profile_format\">\n    <title>HEAP PROFILE FORMAT</title>\n    <para>Although the heap profiling functionality was originally designed to\n    be compatible with the\n    <command>pprof</command> command that is developed as part of the <ulink\n    url=\"http://code.google.com/p/gperftools/\">gperftools\n    package</ulink>, the addition of per thread heap profiling functionality\n    required a different heap profile format.  The <command>jeprof</command>\n    command is derived from <command>pprof</command>, with enhancements to\n    support the heap profile format described here.</para>\n\n    <para>In the following hypothetical heap profile, <constant>[...]</constant>\n    indicates elision for the sake of compactness.  <programlisting><![CDATA[\nheap_v2/524288\n  t*: 28106: 56637512 [0: 0]\n  [...]\n  t3: 352: 16777344 [0: 0]\n  [...]\n  t99: 17754: 29341640 [0: 0]\n  [...]\n@ 0x5f86da8 0x5f5a1dc [...] 0x29e4d4e 0xa200316 0xabb2988 [...]\n  t*: 13: 6688 [0: 0]\n  t3: 12: 6496 [0: ]\n  t99: 1: 192 [0: 0]\n[...]\n\nMAPPED_LIBRARIES:\n[...]]]></programlisting> The following matches the above heap profile, but most\ntokens are replaced with <constant>&lt;description&gt;</constant> to indicate\ndescriptions of the corresponding fields.  <programlisting><![CDATA[\n<heap_profile_format_version>/<mean_sample_interval>\n  <aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n  [...]\n  <thread_3_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]\n  [...]\n  <thread_99_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]\n  [...]\n@ <top_frame> <frame> [...] <frame> <frame> <frame> [...]\n  <backtrace_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n  <backtrace_thread_3>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n  <backtrace_thread_99>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]\n[...]\n\nMAPPED_LIBRARIES:\n</proc/<pid>/maps>]]></programlisting></para>\n  </refsect1>\n\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 <quote>uninitialized</quote>\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.</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()</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()</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    <quote><computeroutput>&lt;jemalloc&gt;: </computeroutput></quote>.</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()</function> and\n      <function>calloc()</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()</function> function\n      returns the value 0 if successful; otherwise it returns an error value.\n      The <function>posix_memalign()</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()</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()</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()</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()</function>\n      function always leaves the original buffer intact when an error occurs.\n      </para>\n\n      <para>The <function>free()</function> function returns no\n      value.</para>\n    </refsect2>\n    <refsect2>\n      <title>Non-standard API</title>\n      <para>The <function>mallocx()</function> and\n      <function>rallocx()</function> functions return a pointer to\n      the allocated memory if successful; otherwise a <constant>NULL</constant>\n      pointer is returned to indicate insufficient contiguous memory was\n      available to service the allocation request.  </para>\n\n      <para>The <function>xallocx()</function> function returns the\n      real size of the resulting resized allocation pointed to by\n      <parameter>ptr</parameter>, which is a value less than\n      <parameter>size</parameter> if the allocation could not be adequately\n      grown in place.  </para>\n\n      <para>The <function>sallocx()</function> function returns the\n      real size of the allocation pointed to by <parameter>ptr</parameter>.\n      </para>\n\n      <para>The <function>nallocx()</function> returns the real size\n      that would result from a successful equivalent\n      <function>mallocx()</function> function call, or zero if\n      insufficient memory is available to perform the size computation.  </para>\n\n      <para>The <function>mallctl()</function>,\n      <function>mallctlnametomib()</function>, and\n      <function>mallctlbymib()</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>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*()</function>\n            read/write processing.</para></listitem>\n          </varlistentry>\n        </variablelist>\n      </para>\n\n      <para>The <function>malloc_usable_size()</function> function\n      returns the usable size of the allocation pointed to by\n      <parameter>ptr</parameter>.  </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 that only one arena should be automatically\n    created:\n      <programlisting language=\"C\"><![CDATA[\nmalloc_conf = \"narenas:1\";]]></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()</function>,\n    <function>calloc()</function>,\n    <function>realloc()</function>, and\n    <function>free()</function> functions conform to ISO/IEC\n    9899:1990 (<quote>ISO C90</quote>).</para>\n\n    <para>The <function>posix_memalign()</function> function conforms\n    to IEEE Std 1003.1-2001 (<quote>POSIX.1</quote>).</para>\n  </refsect1>\n</refentry>\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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=\"0\"/>\n  <xsl:template match=\"function\">\n    <xsl:call-template name=\"inline.monoseq\"/>\n  </xsl:template>\n  <xsl:template match=\"mallctl\">\n    <quote><xsl:call-template name=\"inline.monoseq\"/></quote>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_EXTERNS_H\n#define JEMALLOC_INTERNAL_ARENA_EXTERNS_H\n\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/pages.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n\nextern ssize_t opt_dirty_decay_ms;\nextern ssize_t opt_muzzy_decay_ms;\n\nextern percpu_arena_mode_t opt_percpu_arena;\nextern const char *percpu_arena_mode_names[];\n\nextern const uint64_t h_steps[SMOOTHSTEP_NSTEPS];\nextern malloc_mutex_t arenas_lock;\n\nvoid arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,\n    unsigned *nthreads, const char **dss, ssize_t *dirty_decay_ms,\n    ssize_t *muzzy_decay_ms, size_t *nactive, size_t *ndirty, size_t *nmuzzy);\nvoid arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,\n    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,\n    size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,\n    bin_stats_t *bstats, arena_stats_large_t *lstats);\nvoid arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent);\n#ifdef JEMALLOC_JET\nsize_t arena_slab_regind(extent_t *slab, szind_t binind, const void *ptr);\n#endif\nextent_t *arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena,\n    size_t usize, size_t alignment, bool *zero);\nvoid arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent);\nvoid arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent, size_t oldsize);\nvoid arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent, size_t oldsize);\nssize_t arena_dirty_decay_ms_get(arena_t *arena);\nbool arena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);\nssize_t arena_muzzy_decay_ms_get(arena_t *arena);\nbool arena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);\nvoid arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,\n    bool all);\nvoid arena_reset(tsd_t *tsd, arena_t *arena);\nvoid arena_destroy(tsd_t *tsd, arena_t *arena);\nvoid arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);\nvoid arena_alloc_junk_small(void *ptr, const bin_info_t *bin_info,\n    bool zero);\n\ntypedef void (arena_dalloc_junk_small_t)(void *, const bin_info_t *);\nextern arena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small;\n\nvoid *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,\n    szind_t ind, bool zero);\nvoid *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache);\nvoid arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize);\nvoid arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,\n    bool slow_path);\nvoid arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena,\n    extent_t *extent, void *ptr);\nvoid arena_dalloc_small(tsdn_t *tsdn, void *ptr);\nbool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,\n    size_t extra, bool zero);\nvoid *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,\n    size_t size, size_t alignment, bool zero, tcache_t *tcache);\ndss_prec_t arena_dss_prec_get(arena_t *arena);\nbool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);\nssize_t arena_dirty_decay_ms_default_get(void);\nbool arena_dirty_decay_ms_default_set(ssize_t decay_ms);\nssize_t arena_muzzy_decay_ms_default_get(void);\nbool arena_muzzy_decay_ms_default_set(ssize_t decay_ms);\nbool arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena,\n    size_t *old_limit, size_t *new_limit);\nunsigned arena_nthreads_get(arena_t *arena, bool internal);\nvoid arena_nthreads_inc(arena_t *arena, bool internal);\nvoid arena_nthreads_dec(arena_t *arena, bool internal);\nsize_t arena_extent_sn_next(arena_t *arena);\narena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);\nvoid arena_boot(void);\nvoid arena_prefork0(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork1(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork2(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork3(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork4(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork5(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork6(tsdn_t *tsdn, arena_t *arena);\nvoid arena_prefork7(tsdn_t *tsdn, arena_t *arena);\nvoid arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);\nvoid arena_postfork_child(tsdn_t *tsdn, arena_t *arena);\n\n#endif /* JEMALLOC_INTERNAL_ARENA_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_inlines_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_A_H\n#define JEMALLOC_INTERNAL_ARENA_INLINES_A_H\n\nstatic inline unsigned\narena_ind_get(const arena_t *arena) {\n\treturn base_ind_get(arena->base);\n}\n\nstatic inline void\narena_internal_add(arena_t *arena, size_t size) {\n\tatomic_fetch_add_zu(&arena->stats.internal, size, ATOMIC_RELAXED);\n}\n\nstatic inline void\narena_internal_sub(arena_t *arena, size_t size) {\n\tatomic_fetch_sub_zu(&arena->stats.internal, size, ATOMIC_RELAXED);\n}\n\nstatic inline size_t\narena_internal_get(arena_t *arena) {\n\treturn atomic_load_zu(&arena->stats.internal, ATOMIC_RELAXED);\n}\n\nstatic inline bool\narena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes) {\n\tcassert(config_prof);\n\n\tif (likely(prof_interval == 0 || !prof_active_get_unlocked())) {\n\t\treturn false;\n\t}\n\n\treturn prof_accum_add(tsdn, &arena->prof_accum, accumbytes);\n}\n\nstatic inline void\npercpu_arena_update(tsd_t *tsd, unsigned cpu) {\n\tassert(have_percpu_arena);\n\tarena_t *oldarena = tsd_arena_get(tsd);\n\tassert(oldarena != NULL);\n\tunsigned oldind = arena_ind_get(oldarena);\n\n\tif (oldind != cpu) {\n\t\tunsigned newind = cpu;\n\t\tarena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);\n\t\tassert(newarena != NULL);\n\n\t\t/* Set new arena/tcache associations. */\n\t\tarena_migrate(tsd, oldind, newind);\n\t\ttcache_t *tcache = tcache_get(tsd);\n\t\tif (tcache != NULL) {\n\t\t\ttcache_arena_reassociate(tsd_tsdn(tsd), tcache,\n\t\t\t    newarena);\n\t\t}\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_A_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_inlines_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_B_H\n#define JEMALLOC_INTERNAL_ARENA_INLINES_B_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nJEMALLOC_ALWAYS_INLINE prof_tctx_t *\narena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\t/* Static check. */\n\tif (alloc_ctx == NULL) {\n\t\tconst extent_t *extent = iealloc(tsdn, ptr);\n\t\tif (unlikely(!extent_slab_get(extent))) {\n\t\t\treturn large_prof_tctx_get(tsdn, extent);\n\t\t}\n\t} else {\n\t\tif (unlikely(!alloc_ctx->slab)) {\n\t\t\treturn large_prof_tctx_get(tsdn, iealloc(tsdn, ptr));\n\t\t}\n\t}\n\treturn (prof_tctx_t *)(uintptr_t)1U;\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, UNUSED size_t usize,\n    alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\t/* Static check. */\n\tif (alloc_ctx == NULL) {\n\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\tif (unlikely(!extent_slab_get(extent))) {\n\t\t\tlarge_prof_tctx_set(tsdn, extent, tctx);\n\t\t}\n\t} else {\n\t\tif (unlikely(!alloc_ctx->slab)) {\n\t\t\tlarge_prof_tctx_set(tsdn, iealloc(tsdn, ptr), tctx);\n\t\t}\n\t}\n}\n\nstatic inline void\narena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, UNUSED prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\textent_t *extent = iealloc(tsdn, ptr);\n\tassert(!extent_slab_get(extent));\n\n\tlarge_prof_tctx_reset(tsdn, extent);\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) {\n\ttsd_t *tsd;\n\tticker_t *decay_ticker;\n\n\tif (unlikely(tsdn_null(tsdn))) {\n\t\treturn;\n\t}\n\ttsd = tsdn_tsd(tsdn);\n\tdecay_ticker = decay_ticker_get(tsd, arena_ind_get(arena));\n\tif (unlikely(decay_ticker == NULL)) {\n\t\treturn;\n\t}\n\tif (unlikely(ticker_ticks(decay_ticker, nticks))) {\n\t\tarena_decay(tsdn, arena, false, false);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_decay_tick(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_assert_not_owner(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_assert_not_owner(tsdn, &arena->decay_muzzy.mtx);\n\n\tarena_decay_ticks(tsdn, arena, 1);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\narena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,\n    tcache_t *tcache, bool slow_path) {\n\tassert(!tsdn_null(tsdn) || tcache == NULL);\n\tassert(size != 0);\n\n\tif (likely(tcache != NULL)) {\n\t\tif (likely(size <= SMALL_MAXCLASS)) {\n\t\t\treturn tcache_alloc_small(tsdn_tsd(tsdn), arena,\n\t\t\t    tcache, size, ind, zero, slow_path);\n\t\t}\n\t\tif (likely(size <= tcache_maxclass)) {\n\t\t\treturn tcache_alloc_large(tsdn_tsd(tsdn), arena,\n\t\t\t    tcache, size, ind, zero, slow_path);\n\t\t}\n\t\t/* (size > tcache_maxclass) case falls through. */\n\t\tassert(size > tcache_maxclass);\n\t}\n\n\treturn arena_malloc_hard(tsdn, arena, size, ind, zero);\n}\n\nJEMALLOC_ALWAYS_INLINE arena_t *\narena_aalloc(tsdn_t *tsdn, const void *ptr) {\n\treturn extent_arena_get(iealloc(tsdn, ptr));\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\narena_salloc(tsdn_t *tsdn, const void *ptr) {\n\tassert(ptr != NULL);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tszind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true);\n\tassert(szind != NSIZES);\n\n\treturn sz_index2size(szind);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\narena_vsalloc(tsdn_t *tsdn, const void *ptr) {\n\t/*\n\t * Return 0 if ptr is not within an extent managed by jemalloc.  This\n\t * function has two extra costs relative to isalloc():\n\t * - The rtree calls cannot claim to be dependent lookups, which induces\n\t *   rtree lookup load dependencies.\n\t * - The lookup may fail, so there is an extra branch to check for\n\t *   failure.\n\t */\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent;\n\tszind_t szind;\n\tif (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, false, &extent, &szind)) {\n\t\treturn 0;\n\t}\n\n\tif (extent == NULL) {\n\t\treturn 0;\n\t}\n\tassert(extent_state_get(extent) == extent_state_active);\n\t/* Only slab members should be looked up via interior pointers. */\n\tassert(extent_addr_get(extent) == ptr || extent_slab_get(extent));\n\n\tassert(szind != NSIZES);\n\n\treturn sz_index2size(szind);\n}\n\nstatic inline void\narena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {\n\tassert(ptr != NULL);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tszind_t szind;\n\tbool slab;\n\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,\n\t    true, &szind, &slab);\n\n\tif (config_debug) {\n\t\textent_t *extent = rtree_extent_read(tsdn, &extents_rtree,\n\t\t    rtree_ctx, (uintptr_t)ptr, true);\n\t\tassert(szind == extent_szind_get(extent));\n\t\tassert(szind < NSIZES);\n\t\tassert(slab == extent_slab_get(extent));\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\tarena_dalloc_small(tsdn, ptr);\n\t} else {\n\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\tlarge_dalloc(tsdn, extent);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,\n    alloc_ctx_t *alloc_ctx, bool slow_path) {\n\tassert(!tsdn_null(tsdn) || tcache == NULL);\n\tassert(ptr != NULL);\n\n\tif (unlikely(tcache == NULL)) {\n\t\tarena_dalloc_no_tcache(tsdn, ptr);\n\t\treturn;\n\t}\n\n\tszind_t szind;\n\tbool slab;\n\trtree_ctx_t *rtree_ctx;\n\tif (alloc_ctx != NULL) {\n\t\tszind = alloc_ctx->szind;\n\t\tslab = alloc_ctx->slab;\n\t\tassert(szind != NSIZES);\n\t} else {\n\t\trtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));\n\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &szind, &slab);\n\t}\n\n\tif (config_debug) {\n\t\trtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));\n\t\textent_t *extent = rtree_extent_read(tsdn, &extents_rtree,\n\t\t    rtree_ctx, (uintptr_t)ptr, true);\n\t\tassert(szind == extent_szind_get(extent));\n\t\tassert(szind < NSIZES);\n\t\tassert(slab == extent_slab_get(extent));\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\ttcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,\n\t\t    slow_path);\n\t} else {\n\t\tif (szind < nhbins) {\n\t\t\tif (config_prof && unlikely(szind < NBINS)) {\n\t\t\t\tarena_dalloc_promoted(tsdn, ptr, tcache,\n\t\t\t\t    slow_path);\n\t\t\t} else {\n\t\t\t\ttcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,\n\t\t\t\t    szind, slow_path);\n\t\t\t}\n\t\t} else {\n\t\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\t\tlarge_dalloc(tsdn, extent);\n\t\t}\n\t}\n}\n\nstatic inline void\narena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {\n\tassert(ptr != NULL);\n\tassert(size <= LARGE_MAXCLASS);\n\n\tszind_t szind;\n\tbool slab;\n\tif (!config_prof || !opt_prof) {\n\t\t/*\n\t\t * There is no risk of being confused by a promoted sampled\n\t\t * object, so base szind and slab on the given size.\n\t\t */\n\t\tszind = sz_size2index(size);\n\t\tslab = (szind < NBINS);\n\t}\n\n\tif ((config_prof && opt_prof) || config_debug) {\n\t\trtree_ctx_t rtree_ctx_fallback;\n\t\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,\n\t\t    &rtree_ctx_fallback);\n\n\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &szind, &slab);\n\n\t\tassert(szind == sz_size2index(size));\n\t\tassert((config_prof && opt_prof) || slab == (szind < NBINS));\n\n\t\tif (config_debug) {\n\t\t\textent_t *extent = rtree_extent_read(tsdn,\n\t\t\t    &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);\n\t\t\tassert(szind == extent_szind_get(extent));\n\t\t\tassert(slab == extent_slab_get(extent));\n\t\t}\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\tarena_dalloc_small(tsdn, ptr);\n\t} else {\n\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\tlarge_dalloc(tsdn, extent);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,\n    alloc_ctx_t *alloc_ctx, bool slow_path) {\n\tassert(!tsdn_null(tsdn) || tcache == NULL);\n\tassert(ptr != NULL);\n\tassert(size <= LARGE_MAXCLASS);\n\n\tif (unlikely(tcache == NULL)) {\n\t\tarena_sdalloc_no_tcache(tsdn, ptr, size);\n\t\treturn;\n\t}\n\n\tszind_t szind;\n\tbool slab;\n\tUNUSED alloc_ctx_t local_ctx;\n\tif (config_prof && opt_prof) {\n\t\tif (alloc_ctx == NULL) {\n\t\t\t/* Uncommon case and should be a static check. */\n\t\t\trtree_ctx_t rtree_ctx_fallback;\n\t\t\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,\n\t\t\t    &rtree_ctx_fallback);\n\t\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t\t    (uintptr_t)ptr, true, &local_ctx.szind,\n\t\t\t    &local_ctx.slab);\n\t\t\tassert(local_ctx.szind == sz_size2index(size));\n\t\t\talloc_ctx = &local_ctx;\n\t\t}\n\t\tslab = alloc_ctx->slab;\n\t\tszind = alloc_ctx->szind;\n\t} else {\n\t\t/*\n\t\t * There is no risk of being confused by a promoted sampled\n\t\t * object, so base szind and slab on the given size.\n\t\t */\n\t\tszind = sz_size2index(size);\n\t\tslab = (szind < NBINS);\n\t}\n\n\tif (config_debug) {\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));\n\t\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &szind, &slab);\n\t\textent_t *extent = rtree_extent_read(tsdn,\n\t\t    &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);\n\t\tassert(szind == extent_szind_get(extent));\n\t\tassert(slab == extent_slab_get(extent));\n\t}\n\n\tif (likely(slab)) {\n\t\t/* Small allocation. */\n\t\ttcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,\n\t\t    slow_path);\n\t} else {\n\t\tif (szind < nhbins) {\n\t\t\tif (config_prof && unlikely(szind < NBINS)) {\n\t\t\t\tarena_dalloc_promoted(tsdn, ptr, tcache,\n\t\t\t\t    slow_path);\n\t\t\t} else {\n\t\t\t\ttcache_dalloc_large(tsdn_tsd(tsdn),\n\t\t\t\t    tcache, ptr, szind, slow_path);\n\t\t\t}\n\t\t} else {\n\t\t\textent_t *extent = iealloc(tsdn, ptr);\n\t\t\tlarge_dalloc(tsdn, extent);\n\t\t}\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_B_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_stats.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_STATS_H\n#define JEMALLOC_INTERNAL_ARENA_STATS_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\n/*\n * In those architectures that support 64-bit atomics, we use atomic updates for\n * our 64-bit values.  Otherwise, we use a plain uint64_t and synchronize\n * externally.\n */\n#ifdef JEMALLOC_ATOMIC_U64\ntypedef atomic_u64_t arena_stats_u64_t;\n#else\n/* Must hold the arena stats mutex while reading atomically. */\ntypedef uint64_t arena_stats_u64_t;\n#endif\n\ntypedef struct arena_stats_large_s arena_stats_large_t;\nstruct arena_stats_large_s {\n\t/*\n\t * Total number of allocation/deallocation requests served directly by\n\t * the arena.\n\t */\n\tarena_stats_u64_t\tnmalloc;\n\tarena_stats_u64_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\tarena_stats_u64_t\tnrequests; /* Partially derived. */\n\n\t/* Current number of allocations of this size class. */\n\tsize_t\t\tcurlextents; /* Derived. */\n};\n\ntypedef struct arena_stats_decay_s arena_stats_decay_t;\nstruct arena_stats_decay_s {\n\t/* Total number of purge sweeps. */\n\tarena_stats_u64_t\tnpurge;\n\t/* Total number of madvise calls made. */\n\tarena_stats_u64_t\tnmadvise;\n\t/* Total number of pages purged. */\n\tarena_stats_u64_t\tpurged;\n};\n\n/*\n * Arena stats.  Note that fields marked \"derived\" are not directly maintained\n * within the arena code; rather their values are derived during stats merge\n * requests.\n */\ntypedef struct arena_stats_s arena_stats_t;\nstruct arena_stats_s {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_t\t\tmtx;\n#endif\n\n\t/* Number of bytes currently mapped, excluding retained memory. */\n\tatomic_zu_t\t\tmapped; /* Partially derived. */\n\n\t/*\n\t * Number of unused virtual memory bytes currently retained.  Retained\n\t * bytes are technically mapped (though always decommitted or purged),\n\t * but they are excluded from the mapped statistic (above).\n\t */\n\tatomic_zu_t\t\tretained; /* Derived. */\n\n\tarena_stats_decay_t\tdecay_dirty;\n\tarena_stats_decay_t\tdecay_muzzy;\n\n\tatomic_zu_t\t\tbase; /* Derived. */\n\tatomic_zu_t\t\tinternal;\n\tatomic_zu_t\t\tresident; /* Derived. */\n\tatomic_zu_t\t\tmetadata_thp;\n\n\tatomic_zu_t\t\tallocated_large; /* Derived. */\n\tarena_stats_u64_t\tnmalloc_large; /* Derived. */\n\tarena_stats_u64_t\tndalloc_large; /* Derived. */\n\tarena_stats_u64_t\tnrequests_large; /* Derived. */\n\n\t/* Number of bytes cached in tcache associated with this arena. */\n\tatomic_zu_t\t\ttcache_bytes; /* Derived. */\n\n\tmutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes];\n\n\t/* One element for each large size class. */\n\tarena_stats_large_t\tlstats[NSIZES - NBINS];\n\n\t/* Arena uptime. */\n\tnstime_t\t\tuptime;\n};\n\nstatic inline bool\narena_stats_init(UNUSED tsdn_t *tsdn, arena_stats_t *arena_stats) {\n\tif (config_debug) {\n\t\tfor (size_t i = 0; i < sizeof(arena_stats_t); i++) {\n\t\t\tassert(((char *)arena_stats)[i] == 0);\n\t\t}\n\t}\n#ifndef JEMALLOC_ATOMIC_U64\n\tif (malloc_mutex_init(&arena_stats->mtx, \"arena_stats\",\n\t    WITNESS_RANK_ARENA_STATS, malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n#endif\n\t/* Memory is zeroed, so there is no need to clear stats. */\n\treturn false;\n}\n\nstatic inline void\narena_stats_lock(tsdn_t *tsdn, arena_stats_t *arena_stats) {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_lock(tsdn, &arena_stats->mtx);\n#endif\n}\n\nstatic inline void\narena_stats_unlock(tsdn_t *tsdn, arena_stats_t *arena_stats) {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_unlock(tsdn, &arena_stats->mtx);\n#endif\n}\n\nstatic inline uint64_t\narena_stats_read_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    arena_stats_u64_t *p) {\n#ifdef JEMALLOC_ATOMIC_U64\n\treturn atomic_load_u64(p, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\treturn *p;\n#endif\n}\n\nstatic inline void\narena_stats_add_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    arena_stats_u64_t *p, uint64_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tatomic_fetch_add_u64(p, x, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\t*p += x;\n#endif\n}\n\nUNUSED static inline void\narena_stats_sub_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    arena_stats_u64_t *p, uint64_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tUNUSED uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);\n\tassert(r - x <= r);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\t*p -= x;\n\tassert(*p + x >= *p);\n#endif\n}\n\n/*\n * Non-atomically sets *dst += src.  *dst needs external synchronization.\n * This lets us avoid the cost of a fetch_add when its unnecessary (note that\n * the types here are atomic).\n */\nstatic inline void\narena_stats_accum_u64(arena_stats_u64_t *dst, uint64_t src) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tuint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);\n\tatomic_store_u64(dst, src + cur_dst, ATOMIC_RELAXED);\n#else\n\t*dst += src;\n#endif\n}\n\nstatic inline size_t\narena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p) {\n#ifdef JEMALLOC_ATOMIC_U64\n\treturn atomic_load_zu(p, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\treturn atomic_load_zu(p, ATOMIC_RELAXED);\n#endif\n}\n\nstatic inline void\narena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,\n    size_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tatomic_fetch_add_zu(p, x, ATOMIC_RELAXED);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\tsize_t cur = atomic_load_zu(p, ATOMIC_RELAXED);\n\tatomic_store_zu(p, cur + x, ATOMIC_RELAXED);\n#endif\n}\n\nstatic inline void\narena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,\n    size_t x) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tUNUSED size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);\n\tassert(r - x <= r);\n#else\n\tmalloc_mutex_assert_owner(tsdn, &arena_stats->mtx);\n\tsize_t cur = atomic_load_zu(p, ATOMIC_RELAXED);\n\tatomic_store_zu(p, cur - x, ATOMIC_RELAXED);\n#endif\n}\n\n/* Like the _u64 variant, needs an externally synchronized *dst. */\nstatic inline void\narena_stats_accum_zu(atomic_zu_t *dst, size_t src) {\n\tsize_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);\n\tatomic_store_zu(dst, src + cur_dst, ATOMIC_RELAXED);\n}\n\nstatic inline void\narena_stats_large_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,\n    szind_t szind, uint64_t nrequests) {\n\tarena_stats_lock(tsdn, arena_stats);\n\tarena_stats_add_u64(tsdn, arena_stats, &arena_stats->lstats[szind -\n\t    NBINS].nrequests, nrequests);\n\tarena_stats_unlock(tsdn, arena_stats);\n}\n\nstatic inline void\narena_stats_mapped_add(tsdn_t *tsdn, arena_stats_t *arena_stats, size_t size) {\n\tarena_stats_lock(tsdn, arena_stats);\n\tarena_stats_add_zu(tsdn, arena_stats, &arena_stats->mapped, size);\n\tarena_stats_unlock(tsdn, arena_stats);\n}\n\n\n#endif /* JEMALLOC_INTERNAL_ARENA_STATS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_structs_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H\n#define JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H\n\n#include \"jemalloc/internal/bitmap.h\"\n\nstruct arena_slab_data_s {\n\t/* Per region allocated/deallocated bitmap. */\n\tbitmap_t\tbitmap[BITMAP_GROUPS_MAX];\n};\n\n#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_structs_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H\n#define JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H\n\n#include \"jemalloc/internal/arena_stats.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/nstime.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/smoothstep.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nstruct arena_decay_s {\n\t/* Synchronizes all non-atomic fields. */\n\tmalloc_mutex_t\t\tmtx;\n\t/*\n\t * True if a thread is currently purging the extents associated with\n\t * this decay structure.\n\t */\n\tbool\t\t\tpurging;\n\t/*\n\t * Approximate time in milliseconds from the creation of a set of unused\n\t * dirty pages until an equivalent set of unused dirty pages is purged\n\t * and/or reused.\n\t */\n\tatomic_zd_t\t\ttime_ms;\n\t/* time / SMOOTHSTEP_NSTEPS. */\n\tnstime_t\t\tinterval;\n\t/*\n\t * Time at which the current decay interval logically started.  We do\n\t * not actually advance to a new epoch until sometime after it starts\n\t * because of scheduling and computation delays, and it is even possible\n\t * to completely skip epochs.  In all cases, during epoch advancement we\n\t * merge all relevant activity into the most recently recorded epoch.\n\t */\n\tnstime_t\t\tepoch;\n\t/* Deadline randomness generator. */\n\tuint64_t\t\tjitter_state;\n\t/*\n\t * Deadline for current epoch.  This is the sum of interval and per\n\t * epoch jitter which is a uniform random variable in [0..interval).\n\t * Epochs always advance by precise multiples of interval, but we\n\t * randomize the deadline to reduce the likelihood of arenas purging in\n\t * lockstep.\n\t */\n\tnstime_t\t\tdeadline;\n\t/*\n\t * Number of unpurged pages at beginning of current epoch.  During epoch\n\t * advancement we use the delta between arena->decay_*.nunpurged and\n\t * extents_npages_get(&arena->extents_*) to determine how many dirty\n\t * pages, if any, were generated.\n\t */\n\tsize_t\t\t\tnunpurged;\n\t/*\n\t * Trailing log of how many unused dirty pages were generated during\n\t * each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last\n\t * element is the most recent epoch.  Corresponding epoch times are\n\t * relative to epoch.\n\t */\n\tsize_t\t\t\tbacklog[SMOOTHSTEP_NSTEPS];\n\n\t/*\n\t * Pointer to associated stats.  These stats are embedded directly in\n\t * the arena's stats due to how stats structures are shared between the\n\t * arena and ctl code.\n\t *\n\t * Synchronization: Same as associated arena's stats field. */\n\tarena_stats_decay_t\t*stats;\n\t/* Peak number of pages in associated extents.  Used for debug only. */\n\tuint64_t\t\tceil_npages;\n};\n\nstruct arena_s {\n\t/*\n\t * Number of threads currently assigned to this arena.  Each thread has\n\t * two distinct assignments, one for application-serving allocation, and\n\t * the other for internal metadata allocation.  Internal metadata must\n\t * not be allocated from arenas explicitly created via the arenas.create\n\t * mallctl, because the arena.<i>.reset mallctl indiscriminately\n\t * discards all allocations for the affected arena.\n\t *\n\t *   0: Application allocation.\n\t *   1: Internal metadata allocation.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_u_t\t\tnthreads[2];\n\n\t/*\n\t * When percpu_arena is enabled, to amortize the cost of reading /\n\t * updating the current CPU id, track the most recent thread accessing\n\t * this arena, and only read CPU if there is a mismatch.\n\t */\n\ttsdn_t\t\t*last_thd;\n\n\t/* Synchronization: internal. */\n\tarena_stats_t\t\tstats;\n\n\t/*\n\t * Lists of tcaches and cache_bin_array_descriptors for extant threads\n\t * associated with this arena.  Stats from these are merged\n\t * incrementally, and at exit if opt_stats_print is enabled.\n\t *\n\t * Synchronization: tcache_ql_mtx.\n\t */\n\tql_head(tcache_t)\t\t\ttcache_ql;\n\tql_head(cache_bin_array_descriptor_t)\tcache_bin_array_descriptor_ql;\n\tmalloc_mutex_t\t\t\t\ttcache_ql_mtx;\n\n\t/* Synchronization: internal. */\n\tprof_accum_t\t\tprof_accum;\n\tuint64_t\t\tprof_accumbytes;\n\n\t/*\n\t * PRNG state for cache index randomization of large allocation base\n\t * pointers.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_zu_t\t\toffset_state;\n\n\t/*\n\t * Extent serial number generator state.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_zu_t\t\textent_sn_next;\n\n\t/*\n\t * Represents a dss_prec_t, but atomically.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_u_t\t\tdss_prec;\n\n\t/*\n\t * Number of pages in active extents.\n\t *\n\t * Synchronization: atomic.\n\t */\n\tatomic_zu_t\t\tnactive;\n\n\t/*\n\t * Extant large allocations.\n\t *\n\t * Synchronization: large_mtx.\n\t */\n\textent_list_t\t\tlarge;\n\t/* Synchronizes all large allocation/update/deallocation. */\n\tmalloc_mutex_t\t\tlarge_mtx;\n\n\t/*\n\t * Collections of extents that were previously allocated.  These are\n\t * used when allocating extents, in an attempt to re-use address space.\n\t *\n\t * Synchronization: internal.\n\t */\n\textents_t\t\textents_dirty;\n\textents_t\t\textents_muzzy;\n\textents_t\t\textents_retained;\n\n\t/*\n\t * Decay-based purging state, responsible for scheduling extent state\n\t * transitions.\n\t *\n\t * Synchronization: internal.\n\t */\n\tarena_decay_t\t\tdecay_dirty; /* dirty --> muzzy */\n\tarena_decay_t\t\tdecay_muzzy; /* muzzy --> retained */\n\n\t/*\n\t * Next extent size class in a growing series to use when satisfying a\n\t * request via the extent hooks (only if opt_retain).  This limits the\n\t * number of disjoint virtual memory ranges so that extent merging can\n\t * be effective even if multiple arenas' extent allocation requests are\n\t * highly interleaved.\n\t *\n\t * retain_grow_limit is the max allowed size ind to expand (unless the\n\t * required size is greater).  Default is no limit, and controlled\n\t * through mallctl only.\n\t *\n\t * Synchronization: extent_grow_mtx\n\t */\n\tpszind_t\t\textent_grow_next;\n\tpszind_t\t\tretain_grow_limit;\n\tmalloc_mutex_t\t\textent_grow_mtx;\n\n\t/*\n\t * Available extent structures that were allocated via\n\t * base_alloc_extent().\n\t *\n\t * Synchronization: extent_avail_mtx.\n\t */\n\textent_tree_t\t\textent_avail;\n\tmalloc_mutex_t\t\textent_avail_mtx;\n\n\t/*\n\t * bins is used to store heaps of free regions.\n\t *\n\t * Synchronization: internal.\n\t */\n\tbin_t\t\t\tbins[NBINS];\n\n\t/*\n\t * Base allocator, from which arena metadata are allocated.\n\t *\n\t * Synchronization: internal.\n\t */\n\tbase_t\t\t\t*base;\n\t/* Used to determine uptime.  Read-only after initialization. */\n\tnstime_t\t\tcreate_time;\n};\n\n/* Used in conjunction with tsd for fast arena-related context lookup. */\nstruct arena_tdata_s {\n\tticker_t\t\tdecay_ticker;\n};\n\n/* Used to pass rtree lookup context down the path. */\nstruct alloc_ctx_s {\n\tszind_t szind;\n\tbool slab;\n};\n\n#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/arena_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ARENA_TYPES_H\n#define JEMALLOC_INTERNAL_ARENA_TYPES_H\n\n/* Maximum number of regions in one slab. */\n#define LG_SLAB_MAXREGS\t\t(LG_PAGE - LG_TINY_MIN)\n#define SLAB_MAXREGS\t\t(1U << LG_SLAB_MAXREGS)\n\n/* Default decay times in milliseconds. */\n#define DIRTY_DECAY_MS_DEFAULT\tZD(10 * 1000)\n#define MUZZY_DECAY_MS_DEFAULT\tZD(10 * 1000)\n/* Number of event ticks between time checks. */\n#define DECAY_NTICKS_PER_UPDATE\t1000\n\ntypedef struct arena_slab_data_s arena_slab_data_t;\ntypedef struct arena_decay_s arena_decay_t;\ntypedef struct arena_s arena_t;\ntypedef struct arena_tdata_s arena_tdata_t;\ntypedef struct alloc_ctx_s alloc_ctx_t;\n\ntypedef enum {\n\tpercpu_arena_mode_names_base   = 0, /* Used for options processing. */\n\n\t/*\n\t * *_uninit are used only during bootstrapping, and must correspond\n\t * to initialized variant plus percpu_arena_mode_enabled_base.\n\t */\n\tpercpu_arena_uninit            = 0,\n\tper_phycpu_arena_uninit        = 1,\n\n\t/* All non-disabled modes must come after percpu_arena_disabled. */\n\tpercpu_arena_disabled          = 2,\n\n\tpercpu_arena_mode_names_limit  = 3, /* Used for options processing. */\n\tpercpu_arena_mode_enabled_base = 3,\n\n\tpercpu_arena                   = 3,\n\tper_phycpu_arena               = 4  /* Hyper threads share arena. */\n} percpu_arena_mode_t;\n\n#define PERCPU_ARENA_ENABLED(m)\t((m) >= percpu_arena_mode_enabled_base)\n#define PERCPU_ARENA_DEFAULT\tpercpu_arena_disabled\n\n#endif /* JEMALLOC_INTERNAL_ARENA_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/assert.h",
    "content": "#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/util.h\"\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 assert(e) do {\t\t\t\t\t\t\t\\\n\tif (unlikely(config_debug && !(e))) {\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#ifndef not_reached\n#define not_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\tunreachable();\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n\n#ifndef not_implemented\n#define not_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#ifndef assert_not_implemented\n#define assert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (unlikely(config_debug && !(e))) {\t\t\t\t\\\n\t\tnot_implemented();\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#ifndef cassert\n#define cassert(c) do {\t\t\t\t\t\t\t\\\n\tif (unlikely(!(c))) {\t\t\t\t\t\t\\\n\t\tnot_reached();\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_H\n#define JEMALLOC_INTERNAL_ATOMIC_H\n\n#define ATOMIC_INLINE static inline\n\n#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS)\n#  include \"jemalloc/internal/atomic_gcc_atomic.h\"\n#elif defined(JEMALLOC_GCC_SYNC_ATOMICS)\n#  include \"jemalloc/internal/atomic_gcc_sync.h\"\n#elif defined(_MSC_VER)\n#  include \"jemalloc/internal/atomic_msvc.h\"\n#elif defined(JEMALLOC_C11_ATOMICS)\n#  include \"jemalloc/internal/atomic_c11.h\"\n#else\n#  error \"Don't have atomics implemented on this platform.\"\n#endif\n\n/*\n * This header gives more or less a backport of C11 atomics. The user can write\n * JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_sizeof_type); to generate\n * counterparts of the C11 atomic functions for type, as so:\n *   JEMALLOC_GENERATE_ATOMICS(int *, pi, 3);\n * and then write things like:\n *   int *some_ptr;\n *   atomic_pi_t atomic_ptr_to_int;\n *   atomic_store_pi(&atomic_ptr_to_int, some_ptr, ATOMIC_RELAXED);\n *   int *prev_value = atomic_exchange_pi(&ptr_to_int, NULL, ATOMIC_ACQ_REL);\n *   assert(some_ptr == prev_value);\n * and expect things to work in the obvious way.\n *\n * Also included (with naming differences to avoid conflicts with the standard\n * library):\n *   atomic_fence(atomic_memory_order_t) (mimics C11's atomic_thread_fence).\n *   ATOMIC_INIT (mimics C11's ATOMIC_VAR_INIT).\n */\n\n/*\n * Pure convenience, so that we don't have to type \"atomic_memory_order_\"\n * quite so often.\n */\n#define ATOMIC_RELAXED atomic_memory_order_relaxed\n#define ATOMIC_ACQUIRE atomic_memory_order_acquire\n#define ATOMIC_RELEASE atomic_memory_order_release\n#define ATOMIC_ACQ_REL atomic_memory_order_acq_rel\n#define ATOMIC_SEQ_CST atomic_memory_order_seq_cst\n\n/*\n * Not all platforms have 64-bit atomics.  If we do, this #define exposes that\n * fact.\n */\n#if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)\n#  define JEMALLOC_ATOMIC_U64\n#endif\n\nJEMALLOC_GENERATE_ATOMICS(void *, p, LG_SIZEOF_PTR)\n\n/*\n * There's no actual guarantee that sizeof(bool) == 1, but it's true on the only\n * platform that actually needs to know the size, MSVC.\n */\nJEMALLOC_GENERATE_ATOMICS(bool, b, 0)\n\nJEMALLOC_GENERATE_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)\n\nJEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)\n\nJEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)\n\nJEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)\n\n#ifdef JEMALLOC_ATOMIC_U64\nJEMALLOC_GENERATE_INT_ATOMICS(uint64_t, u64, 3)\n#endif\n\n#undef ATOMIC_INLINE\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_c11.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_C11_H\n#define JEMALLOC_INTERNAL_ATOMIC_C11_H\n\n#include <stdatomic.h>\n\n#define ATOMIC_INIT(...) ATOMIC_VAR_INIT(__VA_ARGS__)\n\n#define atomic_memory_order_t memory_order\n#define atomic_memory_order_relaxed memory_order_relaxed\n#define atomic_memory_order_acquire memory_order_acquire\n#define atomic_memory_order_release memory_order_release\n#define atomic_memory_order_acq_rel memory_order_acq_rel\n#define atomic_memory_order_seq_cst memory_order_seq_cst\n\n#define atomic_fence atomic_thread_fence\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\ntypedef _Atomic(type) atomic_##short_type##_t;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * A strict interpretation of the C standard prevents\t\t\\\n\t * atomic_load from taking a const argument, but it's\t\t\\\n\t * convenient for our purposes. This cast is a workaround.\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tatomic_##short_type##_t* a_nonconst =\t\t\t\t\\\n\t    (atomic_##short_type##_t*)a;\t\t\t\t\\\n\treturn atomic_load_explicit(a_nonconst, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a,\t\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\tatomic_store_explicit(a, val, mo);\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn atomic_exchange_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn atomic_compare_exchange_weak_explicit(a, expected,\t\\\n\t    desired, success_mo, failure_mo);\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn atomic_compare_exchange_strong_explicit(a, expected,\t\\\n\t    desired, success_mo, failure_mo);\t\t\t\t\\\n}\n\n/*\n * Integral types have some special operations available that non-integral ones\n * lack.\n */\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, \t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_add_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_sub_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_and_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_or_explicit(a, val, mo);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn atomic_fetch_xor_explicit(a, val, mo);\t\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_C11_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_gcc_atomic.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H\n#define JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H\n\n#include \"jemalloc/internal/assert.h\"\n\n#define ATOMIC_INIT(...) {__VA_ARGS__}\n\ntypedef enum {\n\tatomic_memory_order_relaxed,\n\tatomic_memory_order_acquire,\n\tatomic_memory_order_release,\n\tatomic_memory_order_acq_rel,\n\tatomic_memory_order_seq_cst\n} atomic_memory_order_t;\n\nATOMIC_INLINE int\natomic_enum_to_builtin(atomic_memory_order_t mo) {\n\tswitch (mo) {\n\tcase atomic_memory_order_relaxed:\n\t\treturn __ATOMIC_RELAXED;\n\tcase atomic_memory_order_acquire:\n\t\treturn __ATOMIC_ACQUIRE;\n\tcase atomic_memory_order_release:\n\t\treturn __ATOMIC_RELEASE;\n\tcase atomic_memory_order_acq_rel:\n\t\treturn __ATOMIC_ACQ_REL;\n\tcase atomic_memory_order_seq_cst:\n\t\treturn __ATOMIC_SEQ_CST;\n\t}\n\t/* Can't happen; the switch is exhaustive. */\n\tnot_reached();\n}\n\nATOMIC_INLINE void\natomic_fence(atomic_memory_order_t mo) {\n\t__atomic_thread_fence(atomic_enum_to_builtin(mo));\n}\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\ttype repr;\t\t\t\t\t\t\t\\\n} atomic_##short_type##_t;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\ttype result;\t\t\t\t\t\t\t\\\n\t__atomic_load(&a->repr, &result, atomic_enum_to_builtin(mo));\t\\\n\treturn result;\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\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a, type val,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\t__atomic_store(&a->repr, &val, atomic_enum_to_builtin(mo));\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\ttype result;\t\t\t\t\t\t\t\\\n\t__atomic_exchange(&a->repr, &val, &result,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n\treturn result;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn __atomic_compare_exchange(&a->repr, expected, &desired,\t\\\n\t    true, atomic_enum_to_builtin(success_mo),\t\t\t\\\n\t    atomic_enum_to_builtin(failure_mo));\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\treturn __atomic_compare_exchange(&a->repr, expected, &desired,\t\\\n\t    false,\t\t\t\t\t\t\t\\\n\t    atomic_enum_to_builtin(success_mo),\t\t\t\t\\\n\t    atomic_enum_to_builtin(failure_mo));\t\t\t\\\n}\n\n\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_add(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_sub(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_and(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_or(&a->repr, val,\t\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __atomic_fetch_xor(&a->repr, val,\t\t\t\\\n\t    atomic_enum_to_builtin(mo));\t\t\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_gcc_sync.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H\n#define JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H\n\n#define ATOMIC_INIT(...) {__VA_ARGS__}\n\ntypedef enum {\n\tatomic_memory_order_relaxed,\n\tatomic_memory_order_acquire,\n\tatomic_memory_order_release,\n\tatomic_memory_order_acq_rel,\n\tatomic_memory_order_seq_cst\n} atomic_memory_order_t;\n\nATOMIC_INLINE void\natomic_fence(atomic_memory_order_t mo) {\n\t/* Easy cases first: no barrier, and full barrier. */\n\tif (mo == atomic_memory_order_relaxed) {\n\t\tasm volatile(\"\" ::: \"memory\");\n\t\treturn;\n\t}\n\tif (mo == atomic_memory_order_seq_cst) {\n\t\tasm volatile(\"\" ::: \"memory\");\n\t\t__sync_synchronize();\n\t\tasm volatile(\"\" ::: \"memory\");\n\t\treturn;\n\t}\n\tasm volatile(\"\" ::: \"memory\");\n#  if defined(__i386__) || defined(__x86_64__)\n\t/* This is implicit on x86. */\n#  elif defined(__ppc__)\n\tasm volatile(\"lwsync\");\n#  elif defined(__sparc__) && defined(__arch64__)\n\tif (mo == atomic_memory_order_acquire) {\n\t\tasm volatile(\"membar #LoadLoad | #LoadStore\");\n\t} else if (mo == atomic_memory_order_release) {\n\t\tasm volatile(\"membar #LoadStore | #StoreStore\");\n\t} else {\n\t\tasm volatile(\"membar #LoadLoad | #LoadStore | #StoreStore\");\n\t}\n#  else\n\t__sync_synchronize();\n#  endif\n\tasm volatile(\"\" ::: \"memory\");\n}\n\n/*\n * A correct implementation of seq_cst loads and stores on weakly ordered\n * architectures could do either of the following:\n *   1. store() is weak-fence -> store -> strong fence, load() is load ->\n *      strong-fence.\n *   2. store() is strong-fence -> store, load() is strong-fence -> load ->\n *      weak-fence.\n * The tricky thing is, load() and store() above can be the load or store\n * portions of a gcc __sync builtin, so we have to follow GCC's lead, which\n * means going with strategy 2.\n * On strongly ordered architectures, the natural strategy is to stick a strong\n * fence after seq_cst stores, and have naked loads.  So we want the strong\n * fences in different places on different architectures.\n * atomic_pre_sc_load_fence and atomic_post_sc_store_fence allow us to\n * accomplish this.\n */\n\nATOMIC_INLINE void\natomic_pre_sc_load_fence() {\n#  if defined(__i386__) || defined(__x86_64__) ||\t\t\t\\\n    (defined(__sparc__) && defined(__arch64__))\n\tatomic_fence(atomic_memory_order_relaxed);\n#  else\n\tatomic_fence(atomic_memory_order_seq_cst);\n#  endif\n}\n\nATOMIC_INLINE void\natomic_post_sc_store_fence() {\n#  if defined(__i386__) || defined(__x86_64__) ||\t\t\t\\\n    (defined(__sparc__) && defined(__arch64__))\n\tatomic_fence(atomic_memory_order_seq_cst);\n#  else\n\tatomic_fence(atomic_memory_order_relaxed);\n#  endif\n\n}\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\ttype volatile repr;\t\t\t\t\t\t\\\n} atomic_##short_type##_t;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\tif (mo == atomic_memory_order_seq_cst) {\t\t\t\\\n\t\tatomic_pre_sc_load_fence();\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ttype result = a->repr;\t\t\t\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_acquire);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn result;\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\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a,\t\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_release);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta->repr = val;\t\t\t\t\t\t\t\\\n\tif (mo == atomic_memory_order_seq_cst) {\t\t\t\\\n\t\tatomic_post_sc_store_fence();\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\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * Because of FreeBSD, we care about gcc 4.2, which doesn't have\\\n\t * an atomic exchange builtin.  We fake it with a CAS loop.\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\twhile (true) {\t\t\t\t\t\t\t\\\n\t\ttype old = a->repr;\t\t\t\t\t\\\n\t\tif (__sync_bool_compare_and_swap(&a->repr, old, val)) {\t\\\n\t\t\treturn old;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\ttype prev = __sync_val_compare_and_swap(&a->repr, *expected,\t\\\n\t    desired);\t\t\t\t\t\t\t\\\n\tif (prev == *expected) {\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*expected = prev;\t\t\t\t\t\\\n\t\treturn false;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\ttype prev = __sync_val_compare_and_swap(&a->repr, *expected,\t\\\n\t    desired);\t\t\t\t\t\t\t\\\n\tif (prev == *expected) {\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*expected = prev;\t\t\t\t\t\\\n\t\treturn false;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,\t\t\t\\\n    /* unused */ lg_size)\t\t\t\t\t\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_add(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_sub(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_and(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_or(&a->repr, val);\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn __sync_fetch_and_xor(&a->repr, val);\t\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/atomic_msvc.h",
    "content": "#ifndef JEMALLOC_INTERNAL_ATOMIC_MSVC_H\n#define JEMALLOC_INTERNAL_ATOMIC_MSVC_H\n\n#define ATOMIC_INIT(...) {__VA_ARGS__}\n\ntypedef enum {\n\tatomic_memory_order_relaxed,\n\tatomic_memory_order_acquire,\n\tatomic_memory_order_release,\n\tatomic_memory_order_acq_rel,\n\tatomic_memory_order_seq_cst\n} atomic_memory_order_t;\n\ntypedef char atomic_repr_0_t;\ntypedef short atomic_repr_1_t;\ntypedef long atomic_repr_2_t;\ntypedef __int64 atomic_repr_3_t;\n\nATOMIC_INLINE void\natomic_fence(atomic_memory_order_t mo) {\n\t_ReadWriteBarrier();\n#  if defined(_M_ARM) || defined(_M_ARM64)\n\t/* ARM needs a barrier for everything but relaxed. */\n\tif (mo != atomic_memory_order_relaxed) {\n\t\tMemoryBarrier();\n\t}\n#  elif defined(_M_IX86) || defined (_M_X64)\n\t/* x86 needs a barrier only for seq_cst. */\n\tif (mo == atomic_memory_order_seq_cst) {\n\t\tMemoryBarrier();\n\t}\n#  else\n#  error \"Don't know how to create atomics for this platform for MSVC.\"\n#  endif\n\t_ReadWriteBarrier();\n}\n\n#define ATOMIC_INTERLOCKED_REPR(lg_size) atomic_repr_ ## lg_size ## _t\n\n#define ATOMIC_CONCAT(a, b) ATOMIC_RAW_CONCAT(a, b)\n#define ATOMIC_RAW_CONCAT(a, b) a ## b\n\n#define ATOMIC_INTERLOCKED_NAME(base_name, lg_size) ATOMIC_CONCAT(\t\\\n    base_name, ATOMIC_INTERLOCKED_SUFFIX(lg_size))\n\n#define ATOMIC_INTERLOCKED_SUFFIX(lg_size)\t\t\t\t\\\n    ATOMIC_CONCAT(ATOMIC_INTERLOCKED_SUFFIX_, lg_size)\n\n#define ATOMIC_INTERLOCKED_SUFFIX_0 8\n#define ATOMIC_INTERLOCKED_SUFFIX_1 16\n#define ATOMIC_INTERLOCKED_SUFFIX_2\n#define ATOMIC_INTERLOCKED_SUFFIX_3 64\n\n#define JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size)\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) repr;\t\t\t\t\\\n} atomic_##short_type##_t;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_load_##short_type(const atomic_##short_type##_t *a,\t\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) ret = a->repr;\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_acquire);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn (type) ret;\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\\\nATOMIC_INLINE void\t\t\t\t\t\t\t\\\natomic_store_##short_type(atomic_##short_type##_t *a,\t\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\tif (mo != atomic_memory_order_relaxed) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_release);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta->repr = (ATOMIC_INTERLOCKED_REPR(lg_size)) val;\t\t\\\n\tif (mo == atomic_memory_order_seq_cst) {\t\t\t\\\n\t\tatomic_fence(atomic_memory_order_seq_cst);\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\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_exchange_##short_type(atomic_##short_type##_t *a, type val,\t\\\n    atomic_memory_order_t mo) {\t\t\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchange,\t\\\n\t    lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) e =\t\t\t\t\\\n\t    (ATOMIC_INTERLOCKED_REPR(lg_size))*expected;\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) d =\t\t\t\t\\\n\t    (ATOMIC_INTERLOCKED_REPR(lg_size))desired;\t\t\t\\\n\tATOMIC_INTERLOCKED_REPR(lg_size) old =\t\t\t\t\\\n\t    ATOMIC_INTERLOCKED_NAME(_InterlockedCompareExchange, \t\\\n\t\tlg_size)(&a->repr, d, e);\t\t\t\t\\\n\tif (old == e) {\t\t\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*expected = (type)old;\t\t\t\t\t\\\n\t\treturn false;\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\\\nATOMIC_INLINE bool\t\t\t\t\t\t\t\\\natomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,\t\\\n    type *expected, type desired, atomic_memory_order_t success_mo,\t\\\n    atomic_memory_order_t failure_mo) {\t\t\t\t\t\\\n\t/* We implement the weak version with strong semantics. */\t\\\n\treturn atomic_compare_exchange_weak_##short_type(a, expected,\t\\\n\t    desired, success_mo, failure_mo);\t\t\t\t\\\n}\n\n\n#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size)\t\\\nJEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size)\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_add_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchangeAdd,\t\\\n\t    lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\\\n}\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_sub_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * MSVC warns on negation of unsigned operands, but for us it\t\\\n\t * gives exactly the right semantics (MAX_TYPE + 1 - operand).\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\t__pragma(warning(push))\t\t\t\t\t\t\\\n\t__pragma(warning(disable: 4146))\t\t\t\t\\\n\treturn atomic_fetch_add_##short_type(a, -val, mo);\t\t\\\n\t__pragma(warning(pop))\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_and_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedAnd, lg_size)(\t\\\n\t    &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_or_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedOr, lg_size)(\t\\\n\t    &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\nATOMIC_INLINE type\t\t\t\t\t\t\t\\\natomic_fetch_xor_##short_type(atomic_##short_type##_t *a,\t\t\\\n    type val, atomic_memory_order_t mo) {\t\t\t\t\\\n\treturn (type)ATOMIC_INTERLOCKED_NAME(_InterlockedXor, lg_size)(\t\\\n\t    &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);\t\t\\\n}\n\n#endif /* JEMALLOC_INTERNAL_ATOMIC_MSVC_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/background_thread_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H\n#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H\n\nextern bool opt_background_thread;\nextern size_t opt_max_background_threads;\nextern malloc_mutex_t background_thread_lock;\nextern atomic_b_t background_thread_enabled_state;\nextern size_t n_background_threads;\nextern size_t max_background_threads;\nextern background_thread_info_t *background_thread_info;\nextern bool can_enable_background_thread;\n\nbool background_thread_create(tsd_t *tsd, unsigned arena_ind);\nbool background_threads_enable(tsd_t *tsd);\nbool background_threads_disable(tsd_t *tsd);\nvoid background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, size_t npages_new);\nvoid background_thread_prefork0(tsdn_t *tsdn);\nvoid background_thread_prefork1(tsdn_t *tsdn);\nvoid background_thread_postfork_parent(tsdn_t *tsdn);\nvoid background_thread_postfork_child(tsdn_t *tsdn);\nbool background_thread_stats_read(tsdn_t *tsdn,\n    background_thread_stats_t *stats);\nvoid background_thread_ctl_init(tsdn_t *tsdn);\n\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\nextern int pthread_create_wrapper(pthread_t *__restrict, const pthread_attr_t *,\n    void *(*)(void *), void *__restrict);\n#endif\nbool background_thread_boot0(void);\nbool background_thread_boot1(tsdn_t *tsdn);\n\n#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/background_thread_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H\n#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H\n\nJEMALLOC_ALWAYS_INLINE bool\nbackground_thread_enabled(void) {\n\treturn atomic_load_b(&background_thread_enabled_state, ATOMIC_RELAXED);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nbackground_thread_enabled_set(tsdn_t *tsdn, bool state) {\n\tmalloc_mutex_assert_owner(tsdn, &background_thread_lock);\n\tatomic_store_b(&background_thread_enabled_state, state, ATOMIC_RELAXED);\n}\n\nJEMALLOC_ALWAYS_INLINE background_thread_info_t *\narena_background_thread_info_get(arena_t *arena) {\n\tunsigned arena_ind = arena_ind_get(arena);\n\treturn &background_thread_info[arena_ind % ncpus];\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nbackground_thread_wakeup_time_get(background_thread_info_t *info) {\n\tuint64_t next_wakeup = nstime_ns(&info->next_wakeup);\n\tassert(atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE) ==\n\t    (next_wakeup == BACKGROUND_THREAD_INDEFINITE_SLEEP));\n\treturn next_wakeup;\n}\n\nJEMALLOC_ALWAYS_INLINE void\nbackground_thread_wakeup_time_set(tsdn_t *tsdn, background_thread_info_t *info,\n    uint64_t wakeup_time) {\n\tmalloc_mutex_assert_owner(tsdn, &info->mtx);\n\tatomic_store_b(&info->indefinite_sleep,\n\t    wakeup_time == BACKGROUND_THREAD_INDEFINITE_SLEEP, ATOMIC_RELEASE);\n\tnstime_init(&info->next_wakeup, wakeup_time);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nbackground_thread_indefinite_sleep(background_thread_info_t *info) {\n\treturn atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE);\n}\n\nJEMALLOC_ALWAYS_INLINE void\narena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena,\n    bool is_background_thread) {\n\tif (!background_thread_enabled() || is_background_thread) {\n\t\treturn;\n\t}\n\tbackground_thread_info_t *info =\n\t    arena_background_thread_info_get(arena);\n\tif (background_thread_indefinite_sleep(info)) {\n\t\tbackground_thread_interval_check(tsdn, arena,\n\t\t    &arena->decay_dirty, 0);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/background_thread_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H\n#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H\n\n/* This file really combines \"structs\" and \"types\", but only transitionally. */\n\n#if defined(JEMALLOC_BACKGROUND_THREAD) || defined(JEMALLOC_LAZY_LOCK)\n#  define JEMALLOC_PTHREAD_CREATE_WRAPPER\n#endif\n\n#define BACKGROUND_THREAD_INDEFINITE_SLEEP UINT64_MAX\n#define MAX_BACKGROUND_THREAD_LIMIT MALLOCX_ARENA_LIMIT\n\ntypedef enum {\n\tbackground_thread_stopped,\n\tbackground_thread_started,\n\t/* Thread waits on the global lock when paused (for arena_reset). */\n\tbackground_thread_paused,\n} background_thread_state_t;\n\nstruct background_thread_info_s {\n#ifdef JEMALLOC_BACKGROUND_THREAD\n\t/* Background thread is pthread specific. */\n\tpthread_t\t\tthread;\n\tpthread_cond_t\t\tcond;\n#endif\n\tmalloc_mutex_t\t\tmtx;\n\tbackground_thread_state_t\tstate;\n\t/* When true, it means no wakeup scheduled. */\n\tatomic_b_t\t\tindefinite_sleep;\n\t/* Next scheduled wakeup time (absolute time in ns). */\n\tnstime_t\t\tnext_wakeup;\n\t/*\n\t *  Since the last background thread run, newly added number of pages\n\t *  that need to be purged by the next wakeup.  This is adjusted on\n\t *  epoch advance, and is used to determine whether we should signal the\n\t *  background thread to wake up earlier.\n\t */\n\tsize_t\t\t\tnpages_to_purge_new;\n\t/* Stats: total number of runs since started. */\n\tuint64_t\t\ttot_n_runs;\n\t/* Stats: total sleep time since started. */\n\tnstime_t\t\ttot_sleep_time;\n};\ntypedef struct background_thread_info_s background_thread_info_t;\n\nstruct background_thread_stats_s {\n\tsize_t num_threads;\n\tuint64_t num_runs;\n\tnstime_t run_interval;\n};\ntypedef struct background_thread_stats_s background_thread_stats_t;\n\n#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_EXTERNS_H\n#define JEMALLOC_INTERNAL_BASE_EXTERNS_H\n\nextern metadata_thp_mode_t opt_metadata_thp;\nextern const char *metadata_thp_mode_names[];\n\nbase_t *b0get(void);\nbase_t *base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);\nvoid base_delete(tsdn_t *tsdn, base_t *base);\nextent_hooks_t *base_extent_hooks_get(base_t *base);\nextent_hooks_t *base_extent_hooks_set(base_t *base,\n    extent_hooks_t *extent_hooks);\nvoid *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);\nextent_t *base_alloc_extent(tsdn_t *tsdn, base_t *base);\nvoid base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,\n    size_t *resident, size_t *mapped, size_t *n_thp);\nvoid base_prefork(tsdn_t *tsdn, base_t *base);\nvoid base_postfork_parent(tsdn_t *tsdn, base_t *base);\nvoid base_postfork_child(tsdn_t *tsdn, base_t *base);\nbool base_boot(tsdn_t *tsdn);\n\n#endif /* JEMALLOC_INTERNAL_BASE_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_INLINES_H\n#define JEMALLOC_INTERNAL_BASE_INLINES_H\n\nstatic inline unsigned\nbase_ind_get(const base_t *base) {\n\treturn base->ind;\n}\n\nstatic inline bool\nmetadata_thp_enabled(void) {\n\treturn (opt_metadata_thp != metadata_thp_disabled);\n}\n#endif /* JEMALLOC_INTERNAL_BASE_INLINES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_STRUCTS_H\n#define JEMALLOC_INTERNAL_BASE_STRUCTS_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\n/* Embedded at the beginning of every block of base-managed virtual memory. */\nstruct base_block_s {\n\t/* Total size of block's virtual memory mapping. */\n\tsize_t\t\tsize;\n\n\t/* Next block in list of base's blocks. */\n\tbase_block_t\t*next;\n\n\t/* Tracks unused trailing space. */\n\textent_t\textent;\n};\n\nstruct base_s {\n\t/* Associated arena's index within the arenas array. */\n\tunsigned\tind;\n\n\t/*\n\t * User-configurable extent hook functions.  Points to an\n\t * extent_hooks_t.\n\t */\n\tatomic_p_t\textent_hooks;\n\n\t/* Protects base_alloc() and base_stats_get() operations. */\n\tmalloc_mutex_t\tmtx;\n\n\t/* Using THP when true (metadata_thp auto mode). */\n\tbool\t\tauto_thp_switched;\n\t/*\n\t * Most recent size class in the series of increasingly large base\n\t * extents.  Logarithmic spacing between subsequent allocations ensures\n\t * that the total number of distinct mappings remains small.\n\t */\n\tpszind_t\tpind_last;\n\n\t/* Serial number generation state. */\n\tsize_t\t\textent_sn_next;\n\n\t/* Chain of all blocks associated with base. */\n\tbase_block_t\t*blocks;\n\n\t/* Heap of extents that track unused trailing space within blocks. */\n\textent_heap_t\tavail[NSIZES];\n\n\t/* Stats, only maintained if config_stats. */\n\tsize_t\t\tallocated;\n\tsize_t\t\tresident;\n\tsize_t\t\tmapped;\n\t/* Number of THP regions touched. */\n\tsize_t\t\tn_thp;\n};\n\n#endif /* JEMALLOC_INTERNAL_BASE_STRUCTS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/base_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BASE_TYPES_H\n#define JEMALLOC_INTERNAL_BASE_TYPES_H\n\ntypedef struct base_block_s base_block_t;\ntypedef struct base_s base_t;\n\n#define METADATA_THP_DEFAULT metadata_thp_disabled\n\n/*\n * In auto mode, arenas switch to huge pages for the base allocator on the\n * second base block.  a0 switches to thp on the 5th block (after 20 megabytes\n * of metadata), since more metadata (e.g. rtree nodes) come from a0's base.\n */\n\n#define BASE_AUTO_THP_THRESHOLD    2\n#define BASE_AUTO_THP_THRESHOLD_A0 5\n\ntypedef enum {\n\tmetadata_thp_disabled   = 0,\n\t/*\n\t * Lazily enable hugepage for metadata. To avoid high RSS caused by THP\n\t * + low usage arena (i.e. THP becomes a significant percentage), the\n\t * \"auto\" option only starts using THP after a base allocator used up\n\t * the first THP region.  Starting from the second hugepage (in a single\n\t * arena), \"auto\" behaves the same as \"always\", i.e. madvise hugepage\n\t * right away.\n\t */\n\tmetadata_thp_auto       = 1,\n\tmetadata_thp_always     = 2,\n\tmetadata_thp_mode_limit = 3\n} metadata_thp_mode_t;\n\n#endif /* JEMALLOC_INTERNAL_BASE_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bin.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BIN_H\n#define JEMALLOC_INTERNAL_BIN_H\n\n#include \"jemalloc/internal/extent_types.h\"\n#include \"jemalloc/internal/extent_structs.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/bin_stats.h\"\n\n/*\n * A bin contains a set of extents that are currently being used for slab\n * allocations.\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 slab has the following layout:\n *\n *   /--------------------\\\n *   | region 0           |\n *   |--------------------|\n *   | region 1           |\n *   |--------------------|\n *   | ...                |\n *   | ...                |\n *   | ...                |\n *   |--------------------|\n *   | region nregs-1     |\n *   \\--------------------/\n */\ntypedef struct bin_info_s bin_info_t;\nstruct bin_info_s {\n\t/* Size of regions in a slab for this bin's size class. */\n\tsize_t\t\t\treg_size;\n\n\t/* Total size of a slab for this bin's size class. */\n\tsize_t\t\t\tslab_size;\n\n\t/* Total number of regions in a slab for this bin's size class. */\n\tuint32_t\t\tnregs;\n\n\t/*\n\t * Metadata used to manipulate bitmaps for slabs associated with this\n\t * bin.\n\t */\n\tbitmap_info_t\t\tbitmap_info;\n};\n\nextern const bin_info_t bin_infos[NBINS];\n\n\ntypedef struct bin_s bin_t;\nstruct bin_s {\n\t/* All operations on bin_t fields require lock ownership. */\n\tmalloc_mutex_t\t\tlock;\n\n\t/*\n\t * Current slab being used to service allocations of this bin's size\n\t * class.  slabcur is independent of slabs_{nonfull,full}; whenever\n\t * slabcur is reassigned, the previous slab must be deallocated or\n\t * inserted into slabs_{nonfull,full}.\n\t */\n\textent_t\t\t*slabcur;\n\n\t/*\n\t * Heap of non-full slabs.  This heap is used to assure that new\n\t * allocations come from the non-full slab that is oldest/lowest in\n\t * memory.\n\t */\n\textent_heap_t\t\tslabs_nonfull;\n\n\t/* List used to track full slabs. */\n\textent_list_t\t\tslabs_full;\n\n\t/* Bin statistics. */\n\tbin_stats_t\tstats;\n};\n\n/* Initializes a bin to empty.  Returns true on error. */\nbool bin_init(bin_t *bin);\n\n/* Forking. */\nvoid bin_prefork(tsdn_t *tsdn, bin_t *bin);\nvoid bin_postfork_parent(tsdn_t *tsdn, bin_t *bin);\nvoid bin_postfork_child(tsdn_t *tsdn, bin_t *bin);\n\n/* Stats. */\nstatic inline void\nbin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tmalloc_mutex_prof_read(tsdn, &dst_bin_stats->mutex_data, &bin->lock);\n\tdst_bin_stats->nmalloc += bin->stats.nmalloc;\n\tdst_bin_stats->ndalloc += bin->stats.ndalloc;\n\tdst_bin_stats->nrequests += bin->stats.nrequests;\n\tdst_bin_stats->curregs += bin->stats.curregs;\n\tdst_bin_stats->nfills += bin->stats.nfills;\n\tdst_bin_stats->nflushes += bin->stats.nflushes;\n\tdst_bin_stats->nslabs += bin->stats.nslabs;\n\tdst_bin_stats->reslabs += bin->stats.reslabs;\n\tdst_bin_stats->curslabs += bin->stats.curslabs;\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n}\n\n#endif /* JEMALLOC_INTERNAL_BIN_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bin_stats.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BIN_STATS_H\n#define JEMALLOC_INTERNAL_BIN_STATS_H\n\n#include \"jemalloc/internal/mutex_prof.h\"\n\ntypedef struct bin_stats_s bin_stats_t;\nstruct bin_stats_s {\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/*\n\t * Current number of regions of this size class, including regions\n\t * currently cached by tcache.\n\t */\n\tsize_t\t\tcurregs;\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 slabs created for this bin's size class. */\n\tuint64_t\tnslabs;\n\n\t/*\n\t * Total number of slabs reused by extracting them from the slabs heap\n\t * for this bin's size class.\n\t */\n\tuint64_t\treslabs;\n\n\t/* Current number of slabs in this bin. */\n\tsize_t\t\tcurslabs;\n\n\tmutex_prof_data_t mutex_data;\n};\n\n#endif /* JEMALLOC_INTERNAL_BIN_STATS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bit_util.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BIT_UTIL_H\n#define JEMALLOC_INTERNAL_BIT_UTIL_H\n\n#include \"jemalloc/internal/assert.h\"\n\n#define BIT_UTIL_INLINE static inline\n\n/* Sanity check. */\n#if !defined(JEMALLOC_INTERNAL_FFSLL) || !defined(JEMALLOC_INTERNAL_FFSL) \\\n    || !defined(JEMALLOC_INTERNAL_FFS)\n#  error JEMALLOC_INTERNAL_FFS{,L,LL} should have been defined by configure\n#endif\n\n\nBIT_UTIL_INLINE unsigned\nffs_llu(unsigned long long bitmap) {\n\treturn JEMALLOC_INTERNAL_FFSLL(bitmap);\n}\n\nBIT_UTIL_INLINE unsigned\nffs_lu(unsigned long bitmap) {\n\treturn JEMALLOC_INTERNAL_FFSL(bitmap);\n}\n\nBIT_UTIL_INLINE unsigned\nffs_u(unsigned bitmap) {\n\treturn JEMALLOC_INTERNAL_FFS(bitmap);\n}\n\nBIT_UTIL_INLINE unsigned\nffs_zu(size_t bitmap) {\n#if LG_SIZEOF_PTR == LG_SIZEOF_INT\n\treturn ffs_u(bitmap);\n#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG\n\treturn ffs_lu(bitmap);\n#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG\n\treturn ffs_llu(bitmap);\n#else\n#error No implementation for size_t ffs()\n#endif\n}\n\nBIT_UTIL_INLINE unsigned\nffs_u64(uint64_t bitmap) {\n#if LG_SIZEOF_LONG == 3\n\treturn ffs_lu(bitmap);\n#elif LG_SIZEOF_LONG_LONG == 3\n\treturn ffs_llu(bitmap);\n#else\n#error No implementation for 64-bit ffs()\n#endif\n}\n\nBIT_UTIL_INLINE unsigned\nffs_u32(uint32_t bitmap) {\n#if LG_SIZEOF_INT == 2\n\treturn ffs_u(bitmap);\n#else\n#error No implementation for 32-bit ffs()\n#endif\n\treturn ffs_u(bitmap);\n}\n\nBIT_UTIL_INLINE uint64_t\npow2_ceil_u64(uint64_t x) {\n\tx--;\n\tx |= x >> 1;\n\tx |= x >> 2;\n\tx |= x >> 4;\n\tx |= x >> 8;\n\tx |= x >> 16;\n\tx |= x >> 32;\n\tx++;\n\treturn x;\n}\n\nBIT_UTIL_INLINE uint32_t\npow2_ceil_u32(uint32_t x) {\n\tx--;\n\tx |= x >> 1;\n\tx |= x >> 2;\n\tx |= x >> 4;\n\tx |= x >> 8;\n\tx |= x >> 16;\n\tx++;\n\treturn x;\n}\n\n/* Compute the smallest power of 2 that is >= x. */\nBIT_UTIL_INLINE size_t\npow2_ceil_zu(size_t x) {\n#if (LG_SIZEOF_PTR == 3)\n\treturn pow2_ceil_u64(x);\n#else\n\treturn pow2_ceil_u32(x);\n#endif\n}\n\n#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tsize_t ret;\n\tassert(x != 0);\n\n\tasm (\"bsr %1, %0\"\n\t    : \"=r\"(ret) // Outputs.\n\t    : \"r\"(x)    // Inputs.\n\t    );\n\tassert(ret < UINT_MAX);\n\treturn (unsigned)ret;\n}\n#elif (defined(_MSC_VER))\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tunsigned long ret;\n\n\tassert(x != 0);\n\n#if (LG_SIZEOF_PTR == 3)\n\t_BitScanReverse64(&ret, x);\n#elif (LG_SIZEOF_PTR == 2)\n\t_BitScanReverse(&ret, x);\n#else\n#  error \"Unsupported type size for lg_floor()\"\n#endif\n\tassert(ret < UINT_MAX);\n\treturn (unsigned)ret;\n}\n#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tassert(x != 0);\n\n#if (LG_SIZEOF_PTR == LG_SIZEOF_INT)\n\treturn ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x);\n#elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG)\n\treturn ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clzl(x);\n#else\n#  error \"Unsupported type size for lg_floor()\"\n#endif\n}\n#else\nBIT_UTIL_INLINE unsigned\nlg_floor(size_t x) {\n\tassert(x != 0);\n\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\tif (x == SIZE_T_MAX) {\n\t\treturn (8 << LG_SIZEOF_PTR) - 1;\n\t}\n\tx++;\n\treturn ffs_zu(x) - 2;\n}\n#endif\n\n#undef BIT_UTIL_INLINE\n\n#endif /* JEMALLOC_INTERNAL_BIT_UTIL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/bitmap.h",
    "content": "#ifndef JEMALLOC_INTERNAL_BITMAP_H\n#define JEMALLOC_INTERNAL_BITMAP_H\n\n#include \"jemalloc/internal/arena_types.h\"\n#include \"jemalloc/internal/bit_util.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\ntypedef unsigned long bitmap_t;\n#define LG_SIZEOF_BITMAP\tLG_SIZEOF_LONG\n\n/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */\n#if LG_SLAB_MAXREGS > LG_CEIL_NSIZES\n/* Maximum bitmap bit count is determined by maximum regions per slab. */\n#  define LG_BITMAP_MAXBITS\tLG_SLAB_MAXREGS\n#else\n/* Maximum bitmap bit count is determined by number of extent size classes. */\n#  define LG_BITMAP_MAXBITS\tLG_CEIL_NSIZES\n#endif\n#define BITMAP_MAXBITS\t\t(ZU(1) << LG_BITMAP_MAXBITS)\n\n/* Number of bits per group. */\n#define LG_BITMAP_GROUP_NBITS\t\t(LG_SIZEOF_BITMAP + 3)\n#define BITMAP_GROUP_NBITS\t\t(1U << LG_BITMAP_GROUP_NBITS)\n#define BITMAP_GROUP_NBITS_MASK\t\t(BITMAP_GROUP_NBITS-1)\n\n/*\n * Do some analysis on how big the bitmap is before we use a tree.  For a brute\n * force linear search, if we would have to call ffs_lu() more than 2^3 times,\n * use a tree instead.\n */\n#if LG_BITMAP_MAXBITS - LG_BITMAP_GROUP_NBITS > 3\n#  define BITMAP_USE_TREE\n#endif\n\n/* Number of groups required to store a given number of bits. */\n#define BITMAP_BITS2GROUPS(nbits)\t\t\t\t\t\\\n    (((nbits) + BITMAP_GROUP_NBITS_MASK) >> LG_BITMAP_GROUP_NBITS)\n\n/*\n * Number of groups required at a particular level for a given number of bits.\n */\n#define BITMAP_GROUPS_L0(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(nbits)\n#define BITMAP_GROUPS_L1(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(nbits))\n#define BITMAP_GROUPS_L2(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))\n#define BITMAP_GROUPS_L3(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(\t\t\\\n\tBITMAP_BITS2GROUPS((nbits)))))\n#define BITMAP_GROUPS_L4(nbits)\t\t\t\t\t\t\\\n    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(\t\t\\\n\tBITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))))\n\n/*\n * Assuming the number of levels, number of groups required for a given number\n * of bits.\n */\n#define BITMAP_GROUPS_1_LEVEL(nbits)\t\t\t\t\t\\\n    BITMAP_GROUPS_L0(nbits)\n#define BITMAP_GROUPS_2_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_1_LEVEL(nbits) + BITMAP_GROUPS_L1(nbits))\n#define BITMAP_GROUPS_3_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_2_LEVEL(nbits) + BITMAP_GROUPS_L2(nbits))\n#define BITMAP_GROUPS_4_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_3_LEVEL(nbits) + BITMAP_GROUPS_L3(nbits))\n#define BITMAP_GROUPS_5_LEVEL(nbits)\t\t\t\t\t\\\n    (BITMAP_GROUPS_4_LEVEL(nbits) + BITMAP_GROUPS_L4(nbits))\n\n/*\n * Maximum number of groups required to support LG_BITMAP_MAXBITS.\n */\n#ifdef BITMAP_USE_TREE\n\n#if LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_1_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_1_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 2\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_2_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_2_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 3\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_3_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_3_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 4\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_4_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_4_LEVEL(BITMAP_MAXBITS)\n#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 5\n#  define BITMAP_GROUPS(nbits)\tBITMAP_GROUPS_5_LEVEL(nbits)\n#  define BITMAP_GROUPS_MAX\tBITMAP_GROUPS_5_LEVEL(BITMAP_MAXBITS)\n#else\n#  error \"Unsupported bitmap size\"\n#endif\n\n/*\n * Maximum number of levels possible.  This could be statically computed based\n * on LG_BITMAP_MAXBITS:\n *\n * #define BITMAP_MAX_LEVELS \\\n *     (LG_BITMAP_MAXBITS / LG_SIZEOF_BITMAP) \\\n *     + !!(LG_BITMAP_MAXBITS % LG_SIZEOF_BITMAP)\n *\n * However, that would not allow the generic BITMAP_INFO_INITIALIZER() macro, so\n * instead hardcode BITMAP_MAX_LEVELS to the largest number supported by the\n * various cascading macros.  The only additional cost this incurs is some\n * unused trailing entries in bitmap_info_t structures; the bitmaps themselves\n * are not impacted.\n */\n#define BITMAP_MAX_LEVELS\t5\n\n#define BITMAP_INFO_INITIALIZER(nbits) {\t\t\t\t\\\n\t/* nbits. */\t\t\t\t\t\t\t\\\n\tnbits,\t\t\t\t\t\t\t\t\\\n\t/* nlevels. */\t\t\t\t\t\t\t\\\n\t(BITMAP_GROUPS_L0(nbits) > BITMAP_GROUPS_L1(nbits)) +\t\t\\\n\t    (BITMAP_GROUPS_L1(nbits) > BITMAP_GROUPS_L2(nbits)) +\t\\\n\t    (BITMAP_GROUPS_L2(nbits) > BITMAP_GROUPS_L3(nbits)) +\t\\\n\t    (BITMAP_GROUPS_L3(nbits) > BITMAP_GROUPS_L4(nbits)) + 1,\t\\\n\t/* levels. */\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\\\n\t\t{0},\t\t\t\t\t\t\t\\\n\t\t{BITMAP_GROUPS_L0(nbits)},\t\t\t\t\\\n\t\t{BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)},\t\\\n\t\t{BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits) +\t\\\n\t\t    BITMAP_GROUPS_L0(nbits)},\t\t\t\t\\\n\t\t{BITMAP_GROUPS_L3(nbits) + BITMAP_GROUPS_L2(nbits) +\t\\\n\t\t    BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)},\t\\\n\t\t{BITMAP_GROUPS_L4(nbits) + BITMAP_GROUPS_L3(nbits) +\t\\\n\t\t     BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits)\t\\\n\t\t     + BITMAP_GROUPS_L0(nbits)}\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n\n#else /* BITMAP_USE_TREE */\n\n#define BITMAP_GROUPS(nbits)\tBITMAP_BITS2GROUPS(nbits)\n#define BITMAP_GROUPS_MAX\tBITMAP_BITS2GROUPS(BITMAP_MAXBITS)\n\n#define BITMAP_INFO_INITIALIZER(nbits) {\t\t\t\t\\\n\t/* nbits. */\t\t\t\t\t\t\t\\\n\tnbits,\t\t\t\t\t\t\t\t\\\n\t/* ngroups. */\t\t\t\t\t\t\t\\\n\tBITMAP_BITS2GROUPS(nbits)\t\t\t\t\t\\\n}\n\n#endif /* BITMAP_USE_TREE */\n\ntypedef struct bitmap_level_s {\n\t/* Offset of this level's groups within the array of groups. */\n\tsize_t group_offset;\n} bitmap_level_t;\n\ntypedef struct bitmap_info_s {\n\t/* Logical number of bits in bitmap (stored at bottom level). */\n\tsize_t nbits;\n\n#ifdef BITMAP_USE_TREE\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#else /* BITMAP_USE_TREE */\n\t/* Number of groups necessary for nbits. */\n\tsize_t ngroups;\n#endif /* BITMAP_USE_TREE */\n} bitmap_info_t;\n\nvoid bitmap_info_init(bitmap_info_t *binfo, size_t nbits);\nvoid bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill);\nsize_t bitmap_size(const bitmap_info_t *binfo);\n\nstatic inline bool\nbitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo) {\n#ifdef BITMAP_USE_TREE\n\tsize_t 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#else\n\tsize_t i;\n\n\tfor (i = 0; i < binfo->ngroups; i++) {\n\t\tif (bitmap[i] != 0) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n#endif\n}\n\nstatic inline bool\nbitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {\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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));\n}\n\nstatic inline void\nbitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {\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));\n\tgoff = bit >> LG_BITMAP_GROUP_NBITS;\n\tgp = &bitmap[goff];\n\tg = *gp;\n\tassert(g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));\n\tg ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);\n\t*gp = g;\n\tassert(bitmap_get(bitmap, binfo, bit));\n#ifdef BITMAP_USE_TREE\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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));\n\t\t\tg ^= ZU(1) << (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\t}\n\t\t}\n\t}\n#endif\n}\n\n/* ffu: find first unset >= bit. */\nstatic inline size_t\nbitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {\n\tassert(min_bit < binfo->nbits);\n\n#ifdef BITMAP_USE_TREE\n\tsize_t bit = 0;\n\tfor (unsigned level = binfo->nlevels; level--;) {\n\t\tsize_t lg_bits_per_group = (LG_BITMAP_GROUP_NBITS * (level +\n\t\t    1));\n\t\tbitmap_t group = bitmap[binfo->levels[level].group_offset + (bit\n\t\t    >> lg_bits_per_group)];\n\t\tunsigned group_nmask = (unsigned)(((min_bit > bit) ? (min_bit -\n\t\t    bit) : 0) >> (lg_bits_per_group - LG_BITMAP_GROUP_NBITS));\n\t\tassert(group_nmask <= BITMAP_GROUP_NBITS);\n\t\tbitmap_t group_mask = ~((1LU << group_nmask) - 1);\n\t\tbitmap_t group_masked = group & group_mask;\n\t\tif (group_masked == 0LU) {\n\t\t\tif (group == 0LU) {\n\t\t\t\treturn binfo->nbits;\n\t\t\t}\n\t\t\t/*\n\t\t\t * min_bit was preceded by one or more unset bits in\n\t\t\t * this group, but there are no other unset bits in this\n\t\t\t * group.  Try again starting at the first bit of the\n\t\t\t * next sibling.  This will recurse at most once per\n\t\t\t * non-root level.\n\t\t\t */\n\t\t\tsize_t sib_base = bit + (ZU(1) << lg_bits_per_group);\n\t\t\tassert(sib_base > min_bit);\n\t\t\tassert(sib_base > bit);\n\t\t\tif (sib_base >= binfo->nbits) {\n\t\t\t\treturn binfo->nbits;\n\t\t\t}\n\t\t\treturn bitmap_ffu(bitmap, binfo, sib_base);\n\t\t}\n\t\tbit += ((size_t)(ffs_lu(group_masked) - 1)) <<\n\t\t    (lg_bits_per_group - LG_BITMAP_GROUP_NBITS);\n\t}\n\tassert(bit >= min_bit);\n\tassert(bit < binfo->nbits);\n\treturn bit;\n#else\n\tsize_t i = min_bit >> LG_BITMAP_GROUP_NBITS;\n\tbitmap_t g = bitmap[i] & ~((1LU << (min_bit & BITMAP_GROUP_NBITS_MASK))\n\t    - 1);\n\tsize_t bit;\n\tdo {\n\t\tbit = ffs_lu(g);\n\t\tif (bit != 0) {\n\t\t\treturn (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);\n\t\t}\n\t\ti++;\n\t\tg = bitmap[i];\n\t} while (i < binfo->ngroups);\n\treturn binfo->nbits;\n#endif\n}\n\n/* sfu: set first unset. */\nstatic inline size_t\nbitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {\n\tsize_t bit;\n\tbitmap_t g;\n\tunsigned i;\n\n\tassert(!bitmap_full(bitmap, binfo));\n\n#ifdef BITMAP_USE_TREE\n\ti = binfo->nlevels - 1;\n\tg = bitmap[binfo->levels[i].group_offset];\n\tbit = ffs_lu(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) + (ffs_lu(g) - 1);\n\t}\n#else\n\ti = 0;\n\tg = bitmap[0];\n\twhile ((bit = ffs_lu(g)) == 0) {\n\t\ti++;\n\t\tg = bitmap[i];\n\t}\n\tbit = (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);\n#endif\n\tbitmap_set(bitmap, binfo, bit);\n\treturn bit;\n}\n\nstatic inline void\nbitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {\n\tsize_t goff;\n\tbitmap_t *gp;\n\tbitmap_t g;\n\tUNUSED bool 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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK))) == 0);\n\tg ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);\n\t*gp = g;\n\tassert(!bitmap_get(bitmap, binfo, bit));\n#ifdef BITMAP_USE_TREE\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 & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)))\n\t\t\t    == 0);\n\t\t\tg ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);\n\t\t\t*gp = g;\n\t\t\tif (!propagate) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n#endif /* BITMAP_USE_TREE */\n}\n\n#endif /* JEMALLOC_INTERNAL_BITMAP_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/cache_bin.h",
    "content": "#ifndef JEMALLOC_INTERNAL_CACHE_BIN_H\n#define JEMALLOC_INTERNAL_CACHE_BIN_H\n\n#include \"jemalloc/internal/ql.h\"\n\n/*\n * The cache_bins are the mechanism that the tcache and the arena use to\n * communicate.  The tcache fills from and flushes to the arena by passing a\n * cache_bin_t to fill/flush.  When the arena needs to pull stats from the\n * tcaches associated with it, it does so by iterating over its\n * cache_bin_array_descriptor_t objects and reading out per-bin stats it\n * contains.  This makes it so that the arena need not know about the existence\n * of the tcache at all.\n */\n\n\n/*\n * The count of the number of cached allocations in a bin.  We make this signed\n * so that negative numbers can encode \"invalid\" states (e.g. a low water mark\n * of -1 for a cache that has been depleted).\n */\ntypedef int32_t cache_bin_sz_t;\n\ntypedef struct cache_bin_stats_s cache_bin_stats_t;\nstruct cache_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 nrequests;\n};\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 */\ntypedef struct cache_bin_info_s cache_bin_info_t;\nstruct cache_bin_info_s {\n\t/* Upper limit on ncached. */\n\tcache_bin_sz_t ncached_max;\n};\n\ntypedef struct cache_bin_s cache_bin_t;\nstruct cache_bin_s {\n\t/* Min # cached since last GC. */\n\tcache_bin_sz_t low_water;\n\t/* # of cached objects. */\n\tcache_bin_sz_t ncached;\n\t/*\n\t * ncached and stats are both modified frequently.  Let's keep them\n\t * close so that they have a higher chance of being on the same\n\t * cacheline, thus less write-backs.\n\t */\n\tcache_bin_stats_t tstats;\n\t/*\n\t * Stack of available objects.\n\t *\n\t * To make use of adjacent cacheline prefetch, the items in the avail\n\t * stack goes to higher address for newer allocations.  avail points\n\t * just above the available space, which means that\n\t * avail[-ncached, ... -1] are available items and the lowest item will\n\t * be allocated first.\n\t */\n\tvoid **avail;\n};\n\ntypedef struct cache_bin_array_descriptor_s cache_bin_array_descriptor_t;\nstruct cache_bin_array_descriptor_s {\n\t/*\n\t * The arena keeps a list of the cache bins associated with it, for\n\t * stats collection.\n\t */\n\tql_elm(cache_bin_array_descriptor_t) link;\n\t/* Pointers to the tcache bins. */\n\tcache_bin_t *bins_small;\n\tcache_bin_t *bins_large;\n};\n\nstatic inline void\ncache_bin_array_descriptor_init(cache_bin_array_descriptor_t *descriptor,\n    cache_bin_t *bins_small, cache_bin_t *bins_large) {\n\tql_elm_new(descriptor, link);\n\tdescriptor->bins_small = bins_small;\n\tdescriptor->bins_large = bins_large;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\ncache_bin_alloc_easy(cache_bin_t *bin, bool *success) {\n\tvoid *ret;\n\n\tif (unlikely(bin->ncached == 0)) {\n\t\tbin->low_water = -1;\n\t\t*success = false;\n\t\treturn NULL;\n\t}\n\t/*\n\t * success (instead of ret) should be checked upon the return of this\n\t * function.  We avoid checking (ret == NULL) because there is never a\n\t * null stored on the avail stack (which is unknown to the compiler),\n\t * and eagerly checking ret would cause pipeline stall (waiting for the\n\t * cacheline).\n\t */\n\t*success = true;\n\tret = *(bin->avail - bin->ncached);\n\tbin->ncached--;\n\n\tif (unlikely(bin->ncached < bin->low_water)) {\n\t\tbin->low_water = bin->ncached;\n\t}\n\n\treturn ret;\n}\n\n#endif /* JEMALLOC_INTERNAL_CACHE_BIN_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ckh.h",
    "content": "#ifndef JEMALLOC_INTERNAL_CKH_H\n#define JEMALLOC_INTERNAL_CKH_H\n\n#include \"jemalloc/internal/tsd.h\"\n\n/* Cuckoo hashing implementation.  Skip to the end for the interface. */\n\n/******************************************************************************/\n/* INTERNAL DEFINITIONS -- IGNORE */\n/******************************************************************************/\n\n/* Maintain counters used to get an idea of performance. */\n/* #define CKH_COUNT */\n/* Print counter values in ckh_delete() (requires CKH_COUNT). */\n/* #define CKH_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/* Typedefs to allow easy function pointer passing. */\ntypedef void ckh_hash_t (const void *, size_t[2]);\ntypedef bool ckh_keycomp_t (const void *, const void *);\n\n/* Hash table cell. */\ntypedef struct {\n\tconst void *key;\n\tconst void *data;\n} ckhc_t;\n\n/* The hash table itself. */\ntypedef struct {\n#ifdef CKH_COUNT\n\t/* Counters used to get an idea of performance. */\n\tuint64_t ngrows;\n\tuint64_t nshrinks;\n\tuint64_t nshrinkfails;\n\tuint64_t ninserts;\n\tuint64_t nrelocs;\n#endif\n\n\t/* Used for pseudo-random number generation. */\n\tuint64_t prng_state;\n\n\t/* Total number of items. */\n\tsize_t count;\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 lg_minbuckets;\n\tunsigned lg_curbuckets;\n\n\t/* Hash and comparison functions. */\n\tckh_hash_t *hash;\n\tckh_keycomp_t *keycomp;\n\n\t/* Hash table with 2^lg_curbuckets buckets. */\n\tckhc_t *tab;\n} ckh_t;\n\n/******************************************************************************/\n/* BEGIN PUBLIC API */\n/******************************************************************************/\n\n/* Lifetime management.  Minitems is the initial capacity. */\nbool ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,\n    ckh_keycomp_t *keycomp);\nvoid ckh_delete(tsd_t *tsd, ckh_t *ckh);\n\n/* Get the number of elements in the set. */\nsize_t ckh_count(ckh_t *ckh);\n\n/*\n * To iterate over the elements in the table, initialize *tabind to 0 and call\n * this function until it returns true.  Each call that returns false will\n * update *key and *data to the next element in the table, assuming the pointers\n * are non-NULL.\n */\nbool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data);\n\n/*\n * Basic hash table operations -- insert, removal, lookup.  For ckh_remove and\n * ckh_search, key or data can be NULL.  The hash-table only stores pointers to\n * the key and value, and doesn't do any lifetime management.\n */\nbool ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data);\nbool ckh_remove(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key,\n    void **data);\nbool ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data);\n\n/* Some useful hash and comparison functions for strings and pointers. */\nvoid ckh_string_hash(const void *key, size_t r_hash[2]);\nbool ckh_string_keycomp(const void *k1, const void *k2);\nvoid ckh_pointer_hash(const void *key, size_t r_hash[2]);\nbool ckh_pointer_keycomp(const void *k1, const void *k2);\n\n#endif /* JEMALLOC_INTERNAL_CKH_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ctl.h",
    "content": "#ifndef JEMALLOC_INTERNAL_CTL_H\n#define JEMALLOC_INTERNAL_CTL_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/stats.h\"\n\n/* Maximum ctl tree depth. */\n#define CTL_MAX_DEPTH\t7\n\ntypedef struct ctl_node_s {\n\tbool named;\n} ctl_node_t;\n\ntypedef struct ctl_named_node_s {\n\tctl_node_t node;\n\tconst char *name;\n\t/* If (nchildren == 0), this is a terminal node. */\n\tsize_t nchildren;\n\tconst ctl_node_t *children;\n\tint (*ctl)(tsd_t *, const size_t *, size_t, void *, size_t *, void *,\n\t    size_t);\n} ctl_named_node_t;\n\ntypedef struct ctl_indexed_node_s {\n\tstruct ctl_node_s node;\n\tconst ctl_named_node_t *(*index)(tsdn_t *, const size_t *, size_t,\n\t    size_t);\n} ctl_indexed_node_t;\n\ntypedef struct ctl_arena_stats_s {\n\tarena_stats_t astats;\n\n\t/* Aggregate stats for small size classes, based on bin stats. */\n\tsize_t allocated_small;\n\tuint64_t nmalloc_small;\n\tuint64_t ndalloc_small;\n\tuint64_t nrequests_small;\n\n\tbin_stats_t bstats[NBINS];\n\tarena_stats_large_t lstats[NSIZES - NBINS];\n} ctl_arena_stats_t;\n\ntypedef struct ctl_stats_s {\n\tsize_t allocated;\n\tsize_t active;\n\tsize_t metadata;\n\tsize_t metadata_thp;\n\tsize_t resident;\n\tsize_t mapped;\n\tsize_t retained;\n\n\tbackground_thread_stats_t background_thread;\n\tmutex_prof_data_t mutex_prof_data[mutex_prof_num_global_mutexes];\n} ctl_stats_t;\n\ntypedef struct ctl_arena_s ctl_arena_t;\nstruct ctl_arena_s {\n\tunsigned arena_ind;\n\tbool initialized;\n\tql_elm(ctl_arena_t) destroyed_link;\n\n\t/* Basic stats, supported even if !config_stats. */\n\tunsigned nthreads;\n\tconst char *dss;\n\tssize_t dirty_decay_ms;\n\tssize_t muzzy_decay_ms;\n\tsize_t pactive;\n\tsize_t pdirty;\n\tsize_t pmuzzy;\n\n\t/* NULL if !config_stats. */\n\tctl_arena_stats_t *astats;\n};\n\ntypedef struct ctl_arenas_s {\n\tuint64_t epoch;\n\tunsigned narenas;\n\tql_head(ctl_arena_t) destroyed;\n\n\t/*\n\t * Element 0 corresponds to merged stats for extant arenas (accessed via\n\t * MALLCTL_ARENAS_ALL), element 1 corresponds to merged stats for\n\t * destroyed arenas (accessed via MALLCTL_ARENAS_DESTROYED), and the\n\t * remaining MALLOCX_ARENA_LIMIT elements correspond to arenas.\n\t */\n\tctl_arena_t *arenas[2 + MALLOCX_ARENA_LIMIT];\n} ctl_arenas_t;\n\nint ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen);\nint ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp);\n\nint ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen);\nbool ctl_boot(void);\nvoid ctl_prefork(tsdn_t *tsdn);\nvoid ctl_postfork_parent(tsdn_t *tsdn);\nvoid ctl_postfork_child(tsdn_t *tsdn);\n\n#define xmallctl(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 xmallctlnametomib(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 xmallctlbymib(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_INTERNAL_CTL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/div.h",
    "content": "#ifndef JEMALLOC_INTERNAL_DIV_H\n#define JEMALLOC_INTERNAL_DIV_H\n\n#include \"jemalloc/internal/assert.h\"\n\n/*\n * This module does the division that computes the index of a region in a slab,\n * given its offset relative to the base.\n * That is, given a divisor d, an n = i * d (all integers), we'll return i.\n * We do some pre-computation to do this more quickly than a CPU division\n * instruction.\n * We bound n < 2^32, and don't support dividing by one.\n */\n\ntypedef struct div_info_s div_info_t;\nstruct div_info_s {\n\tuint32_t magic;\n#ifdef JEMALLOC_DEBUG\n\tsize_t d;\n#endif\n};\n\nvoid div_init(div_info_t *div_info, size_t divisor);\n\nstatic inline size_t\ndiv_compute(div_info_t *div_info, size_t n) {\n\tassert(n <= (uint32_t)-1);\n\t/*\n\t * This generates, e.g. mov; imul; shr on x86-64. On a 32-bit machine,\n\t * the compilers I tried were all smart enough to turn this into the\n\t * appropriate \"get the high 32 bits of the result of a multiply\" (e.g.\n\t * mul; mov edx eax; on x86, umull on arm, etc.).\n\t */\n\tsize_t i = ((uint64_t)n * (uint64_t)div_info->magic) >> 32;\n#ifdef JEMALLOC_DEBUG\n\tassert(i * div_info->d == n);\n#endif\n\treturn i;\n}\n\n#endif /* JEMALLOC_INTERNAL_DIV_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/emitter.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EMITTER_H\n#define JEMALLOC_INTERNAL_EMITTER_H\n\n#include \"jemalloc/internal/ql.h\"\n\ntypedef enum emitter_output_e emitter_output_t;\nenum emitter_output_e {\n\temitter_output_json,\n\temitter_output_table\n};\n\ntypedef enum emitter_justify_e emitter_justify_t;\nenum emitter_justify_e {\n\temitter_justify_left,\n\temitter_justify_right,\n\t/* Not for users; just to pass to internal functions. */\n\temitter_justify_none\n};\n\ntypedef enum emitter_type_e emitter_type_t;\nenum emitter_type_e {\n\temitter_type_bool,\n\temitter_type_int,\n\temitter_type_unsigned,\n\temitter_type_uint32,\n\temitter_type_uint64,\n\temitter_type_size,\n\temitter_type_ssize,\n\temitter_type_string,\n\t/*\n\t * A title is a column title in a table; it's just a string, but it's\n\t * not quoted.\n\t */\n\temitter_type_title,\n};\n\ntypedef struct emitter_col_s emitter_col_t;\nstruct emitter_col_s {\n\t/* Filled in by the user. */\n\temitter_justify_t justify;\n\tint width;\n\temitter_type_t type;\n\tunion {\n\t\tbool bool_val;\n\t\tint int_val;\n\t\tunsigned unsigned_val;\n\t\tuint32_t uint32_val;\n\t\tuint64_t uint64_val;\n\t\tsize_t size_val;\n\t\tssize_t ssize_val;\n\t\tconst char *str_val;\n\t};\n\n\t/* Filled in by initialization. */\n\tql_elm(emitter_col_t) link;\n};\n\ntypedef struct emitter_row_s emitter_row_t;\nstruct emitter_row_s {\n\tql_head(emitter_col_t) cols;\n};\n\nstatic inline void\nemitter_row_init(emitter_row_t *row) {\n\tql_new(&row->cols);\n}\n\nstatic inline void\nemitter_col_init(emitter_col_t *col, emitter_row_t *row) {\n\tql_elm_new(col, link);\n\tql_tail_insert(&row->cols, col, link);\n}\n\ntypedef struct emitter_s emitter_t;\nstruct emitter_s {\n\temitter_output_t output;\n\t/* The output information. */\n\tvoid (*write_cb)(void *, const char *);\n\tvoid *cbopaque;\n\tint nesting_depth;\n\t/* True if we've already emitted a value at the given depth. */\n\tbool item_at_depth;\n};\n\nstatic inline void\nemitter_init(emitter_t *emitter, emitter_output_t emitter_output,\n    void (*write_cb)(void *, const char *), void *cbopaque) {\n\temitter->output = emitter_output;\n\temitter->write_cb = write_cb;\n\temitter->cbopaque = cbopaque;\n\temitter->item_at_depth = false;\n\temitter->nesting_depth = 0;\n}\n\n/* Internal convenience function.  Write to the emitter the given string. */\nJEMALLOC_FORMAT_PRINTF(2, 3)\nstatic inline void\nemitter_printf(emitter_t *emitter, const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);\n\tva_end(ap);\n}\n\n/* Write to the emitter the given string, but only in table mode. */\nJEMALLOC_FORMAT_PRINTF(2, 3)\nstatic inline void\nemitter_table_printf(emitter_t *emitter, const char *format, ...) {\n\tif (emitter->output == emitter_output_table) {\n\t\tva_list ap;\n\t\tva_start(ap, format);\n\t\tmalloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);\n\t\tva_end(ap);\n\t}\n}\n\nstatic inline void\nemitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,\n    emitter_justify_t justify, int width) {\n\tsize_t written;\n\tif (justify == emitter_justify_none) {\n\t\twritten = malloc_snprintf(out_fmt, out_size,\n\t\t    \"%%%s\", fmt_specifier);\n\t} else if (justify == emitter_justify_left) {\n\t\twritten = malloc_snprintf(out_fmt, out_size,\n\t\t    \"%%-%d%s\", width, fmt_specifier);\n\t} else {\n\t\twritten = malloc_snprintf(out_fmt, out_size,\n\t\t    \"%%%d%s\", width, fmt_specifier);\n\t}\n\t/* Only happens in case of bad format string, which *we* choose. */\n\tassert(written <  out_size);\n}\n\n/*\n * Internal.  Emit the given value type in the relevant encoding (so that the\n * bool true gets mapped to json \"true\", but the string \"true\" gets mapped to\n * json \"\\\"true\\\"\", for instance.\n *\n * Width is ignored if justify is emitter_justify_none.\n */\nstatic inline void\nemitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,\n    emitter_type_t value_type, const void *value) {\n\tsize_t str_written;\n#define BUF_SIZE 256\n#define FMT_SIZE 10\n\t/*\n\t * We dynamically generate a format string to emit, to let us use the\n\t * snprintf machinery.  This is kinda hacky, but gets the job done\n\t * quickly without having to think about the various snprintf edge\n\t * cases.\n\t */\n\tchar fmt[FMT_SIZE];\n\tchar buf[BUF_SIZE];\n\n#define EMIT_SIMPLE(type, format)\t\t\t\t\t\\\n\temitter_gen_fmt(fmt, FMT_SIZE, format, justify, width);\t\t\\\n\temitter_printf(emitter, fmt, *(const type *)value);\t\t\\\n\n\tswitch (value_type) {\n\tcase emitter_type_bool:\n\t\temitter_gen_fmt(fmt, FMT_SIZE, \"s\", justify, width);\n\t\temitter_printf(emitter, fmt, *(const bool *)value ?\n\t\t    \"true\" : \"false\");\n\t\tbreak;\n\tcase emitter_type_int:\n\t\tEMIT_SIMPLE(int, \"d\")\n\t\tbreak;\n\tcase emitter_type_unsigned:\n\t\tEMIT_SIMPLE(unsigned, \"u\")\n\t\tbreak;\n\tcase emitter_type_ssize:\n\t\tEMIT_SIMPLE(ssize_t, \"zd\")\n\t\tbreak;\n\tcase emitter_type_size:\n\t\tEMIT_SIMPLE(size_t, \"zu\")\n\t\tbreak;\n\tcase emitter_type_string:\n\t\tstr_written = malloc_snprintf(buf, BUF_SIZE, \"\\\"%s\\\"\",\n\t\t    *(const char *const *)value);\n\t\t/*\n\t\t * We control the strings we output; we shouldn't get anything\n\t\t * anywhere near the fmt size.\n\t\t */\n\t\tassert(str_written < BUF_SIZE);\n\t\temitter_gen_fmt(fmt, FMT_SIZE, \"s\", justify, width);\n\t\temitter_printf(emitter, fmt, buf);\n\t\tbreak;\n\tcase emitter_type_uint32:\n\t\tEMIT_SIMPLE(uint32_t, FMTu32)\n\t\tbreak;\n\tcase emitter_type_uint64:\n\t\tEMIT_SIMPLE(uint64_t, FMTu64)\n\t\tbreak;\n\tcase emitter_type_title:\n\t\tEMIT_SIMPLE(char *const, \"s\");\n\t\tbreak;\n\tdefault:\n\t\tunreachable();\n\t}\n#undef BUF_SIZE\n#undef FMT_SIZE\n}\n\n\n/* Internal functions.  In json mode, tracks nesting state. */\nstatic inline void\nemitter_nest_inc(emitter_t *emitter) {\n\temitter->nesting_depth++;\n\temitter->item_at_depth = false;\n}\n\nstatic inline void\nemitter_nest_dec(emitter_t *emitter) {\n\temitter->nesting_depth--;\n\temitter->item_at_depth = true;\n}\n\nstatic inline void\nemitter_indent(emitter_t *emitter) {\n\tint amount = emitter->nesting_depth;\n\tconst char *indent_str;\n\tif (emitter->output == emitter_output_json) {\n\t\tindent_str = \"\\t\";\n\t} else {\n\t\tamount *= 2;\n\t\tindent_str = \" \";\n\t}\n\tfor (int i = 0; i < amount; i++) {\n\t\temitter_printf(emitter, \"%s\", indent_str);\n\t}\n}\n\nstatic inline void\nemitter_json_key_prefix(emitter_t *emitter) {\n\temitter_printf(emitter, \"%s\\n\", emitter->item_at_depth ? \",\" : \"\");\n\temitter_indent(emitter);\n}\n\nstatic inline void\nemitter_begin(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth == 0);\n\t\temitter_printf(emitter, \"{\");\n\t\temitter_nest_inc(emitter);\n\t} else {\n\t\t// tabular init\n\t\temitter_printf(emitter, \"%s\", \"\");\n\t}\n}\n\nstatic inline void\nemitter_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth == 1);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n}\\n\");\n\t}\n}\n\n/*\n * Note emits a different kv pair as well, but only in table mode.  Omits the\n * note if table_note_key is NULL.\n */\nstatic inline void\nemitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,\n    emitter_type_t value_type, const void *value,\n    const char *table_note_key, emitter_type_t table_note_value_type,\n    const void *table_note_value) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"\\\"%s\\\": \", json_key);\n\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t    value_type, value);\n\t} else {\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"%s: \", table_key);\n\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t    value_type, value);\n\t\tif (table_note_key != NULL) {\n\t\t\temitter_printf(emitter, \" (%s: \", table_note_key);\n\t\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t\t    table_note_value_type, table_note_value);\n\t\t\temitter_printf(emitter, \")\");\n\t\t}\n\t\temitter_printf(emitter, \"\\n\");\n\t}\n\temitter->item_at_depth = true;\n}\n\nstatic inline void\nemitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,\n    emitter_type_t value_type, const void *value) {\n\temitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,\n\t    emitter_type_bool, NULL);\n}\n\nstatic inline void\nemitter_json_kv(emitter_t *emitter, const char *json_key,\n    emitter_type_t value_type, const void *value) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_kv(emitter, json_key, NULL, value_type, value);\n\t}\n}\n\nstatic inline void\nemitter_table_kv(emitter_t *emitter, const char *table_key,\n    emitter_type_t value_type, const void *value) {\n\tif (emitter->output == emitter_output_table) {\n\t\temitter_kv(emitter, NULL, table_key, value_type, value);\n\t}\n}\n\nstatic inline void\nemitter_dict_begin(emitter_t *emitter, const char *json_key,\n    const char *table_header) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"\\\"%s\\\": {\", json_key);\n\t\temitter_nest_inc(emitter);\n\t} else {\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"%s\\n\", table_header);\n\t\temitter_nest_inc(emitter);\n\t}\n}\n\nstatic inline void\nemitter_dict_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n\");\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"}\");\n\t} else {\n\t\temitter_nest_dec(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_dict_begin(emitter_t *emitter, const char *json_key) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_dict_begin(emitter, json_key, NULL);\n\t}\n}\n\nstatic inline void\nemitter_json_dict_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_dict_end(emitter);\n\t}\n}\n\nstatic inline void\nemitter_table_dict_begin(emitter_t *emitter, const char *table_key) {\n\tif (emitter->output == emitter_output_table) {\n\t\temitter_dict_begin(emitter, NULL, table_key);\n\t}\n}\n\nstatic inline void\nemitter_table_dict_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_table) {\n\t\temitter_dict_end(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_arr_begin(emitter_t *emitter, const char *json_key) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"\\\"%s\\\": [\", json_key);\n\t\temitter_nest_inc(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_arr_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n\");\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"]\");\n\t}\n}\n\nstatic inline void\nemitter_json_arr_obj_begin(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_printf(emitter, \"{\");\n\t\temitter_nest_inc(emitter);\n\t}\n}\n\nstatic inline void\nemitter_json_arr_obj_end(emitter_t *emitter) {\n\tif (emitter->output == emitter_output_json) {\n\t\tassert(emitter->nesting_depth > 0);\n\t\temitter_nest_dec(emitter);\n\t\temitter_printf(emitter, \"\\n\");\n\t\temitter_indent(emitter);\n\t\temitter_printf(emitter, \"}\");\n\t}\n}\n\nstatic inline void\nemitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,\n    const void *value) {\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_key_prefix(emitter);\n\t\temitter_print_value(emitter, emitter_justify_none, -1,\n\t\t    value_type, value);\n\t}\n}\n\nstatic inline void\nemitter_table_row(emitter_t *emitter, emitter_row_t *row) {\n\tif (emitter->output != emitter_output_table) {\n\t\treturn;\n\t}\n\temitter_col_t *col;\n\tql_foreach(col, &row->cols, link) {\n\t\temitter_print_value(emitter, col->justify, col->width,\n\t\t    col->type, (const void *)&col->bool_val);\n\t}\n\temitter_table_printf(emitter, \"\\n\");\n}\n\n#endif /* JEMALLOC_INTERNAL_EMITTER_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_dss.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_DSS_H\n#define JEMALLOC_INTERNAL_EXTENT_DSS_H\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 DSS_PREC_DEFAULT dss_prec_secondary\n#define DSS_DEFAULT \"secondary\"\n\nextern const char *dss_prec_names[];\n\nextern const char *opt_dss;\n\ndss_prec_t extent_dss_prec_get(void);\nbool extent_dss_prec_set(dss_prec_t dss_prec);\nvoid *extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit);\nbool extent_in_dss(void *addr);\nbool extent_dss_mergeable(void *addr_a, void *addr_b);\nvoid extent_dss_boot(void);\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_DSS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_EXTERNS_H\n#define JEMALLOC_INTERNAL_EXTENT_EXTERNS_H\n\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n#include \"jemalloc/internal/ph.h\"\n#include \"jemalloc/internal/rtree.h\"\n\nextern size_t opt_lg_extent_max_active_fit;\n\nextern rtree_t extents_rtree;\nextern const extent_hooks_t extent_hooks_default;\nextern mutex_pool_t extent_mutex_pool;\n\nextent_t *extent_alloc(tsdn_t *tsdn, arena_t *arena);\nvoid extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent);\n\nextent_hooks_t *extent_hooks_get(arena_t *arena);\nextent_hooks_t *extent_hooks_set(tsd_t *tsd, arena_t *arena,\n    extent_hooks_t *extent_hooks);\n\n#ifdef JEMALLOC_JET\nsize_t extent_size_quantize_floor(size_t size);\nsize_t extent_size_quantize_ceil(size_t size);\n#endif\n\nrb_proto(, extent_avail_, extent_tree_t, extent_t)\nph_proto(, extent_heap_, extent_heap_t, extent_t)\n\nbool extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,\n    bool delay_coalesce);\nextent_state_t extents_state_get(const extents_t *extents);\nsize_t extents_npages_get(extents_t *extents);\nextent_t *extents_alloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,\n    size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,\n    bool *zero, bool *commit);\nvoid extents_dalloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent);\nextent_t *extents_evict(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_min);\nvoid extents_prefork(tsdn_t *tsdn, extents_t *extents);\nvoid extents_postfork_parent(tsdn_t *tsdn, extents_t *extents);\nvoid extents_postfork_child(tsdn_t *tsdn, extents_t *extents);\nextent_t *extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit);\nvoid extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent);\nvoid extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent);\nvoid extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent);\nbool extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nbool extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nbool extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nbool extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length);\nextent_t *extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b);\nbool extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b);\n\nbool extent_boot(void);\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_INLINES_H\n#define JEMALLOC_INTERNAL_EXTENT_INLINES_H\n\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n#include \"jemalloc/internal/pages.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/sz.h\"\n\nstatic inline void\nextent_lock(tsdn_t *tsdn, extent_t *extent) {\n\tassert(extent != NULL);\n\tmutex_pool_lock(tsdn, &extent_mutex_pool, (uintptr_t)extent);\n}\n\nstatic inline void\nextent_unlock(tsdn_t *tsdn, extent_t *extent) {\n\tassert(extent != NULL);\n\tmutex_pool_unlock(tsdn, &extent_mutex_pool, (uintptr_t)extent);\n}\n\nstatic inline void\nextent_lock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {\n\tassert(extent1 != NULL && extent2 != NULL);\n\tmutex_pool_lock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,\n\t    (uintptr_t)extent2);\n}\n\nstatic inline void\nextent_unlock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {\n\tassert(extent1 != NULL && extent2 != NULL);\n\tmutex_pool_unlock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,\n\t    (uintptr_t)extent2);\n}\n\nstatic inline arena_t *\nextent_arena_get(const extent_t *extent) {\n\tunsigned arena_ind = (unsigned)((extent->e_bits &\n\t    EXTENT_BITS_ARENA_MASK) >> EXTENT_BITS_ARENA_SHIFT);\n\t/*\n\t * The following check is omitted because we should never actually read\n\t * a NULL arena pointer.\n\t */\n\tif (false && arena_ind >= MALLOCX_ARENA_LIMIT) {\n\t\treturn NULL;\n\t}\n\tassert(arena_ind < MALLOCX_ARENA_LIMIT);\n\treturn (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_ACQUIRE);\n}\n\nstatic inline szind_t\nextent_szind_get_maybe_invalid(const extent_t *extent) {\n\tszind_t szind = (szind_t)((extent->e_bits & EXTENT_BITS_SZIND_MASK) >>\n\t    EXTENT_BITS_SZIND_SHIFT);\n\tassert(szind <= NSIZES);\n\treturn szind;\n}\n\nstatic inline szind_t\nextent_szind_get(const extent_t *extent) {\n\tszind_t szind = extent_szind_get_maybe_invalid(extent);\n\tassert(szind < NSIZES); /* Never call when \"invalid\". */\n\treturn szind;\n}\n\nstatic inline size_t\nextent_usize_get(const extent_t *extent) {\n\treturn sz_index2size(extent_szind_get(extent));\n}\n\nstatic inline size_t\nextent_sn_get(const extent_t *extent) {\n\treturn (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>\n\t    EXTENT_BITS_SN_SHIFT);\n}\n\nstatic inline extent_state_t\nextent_state_get(const extent_t *extent) {\n\treturn (extent_state_t)((extent->e_bits & EXTENT_BITS_STATE_MASK) >>\n\t    EXTENT_BITS_STATE_SHIFT);\n}\n\nstatic inline bool\nextent_zeroed_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_ZEROED_MASK) >>\n\t    EXTENT_BITS_ZEROED_SHIFT);\n}\n\nstatic inline bool\nextent_committed_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_COMMITTED_MASK) >>\n\t    EXTENT_BITS_COMMITTED_SHIFT);\n}\n\nstatic inline bool\nextent_dumpable_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_DUMPABLE_MASK) >>\n\t    EXTENT_BITS_DUMPABLE_SHIFT);\n}\n\nstatic inline bool\nextent_slab_get(const extent_t *extent) {\n\treturn (bool)((extent->e_bits & EXTENT_BITS_SLAB_MASK) >>\n\t    EXTENT_BITS_SLAB_SHIFT);\n}\n\nstatic inline unsigned\nextent_nfree_get(const extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\treturn (unsigned)((extent->e_bits & EXTENT_BITS_NFREE_MASK) >>\n\t    EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void *\nextent_base_get(const extent_t *extent) {\n\tassert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||\n\t    !extent_slab_get(extent));\n\treturn PAGE_ADDR2BASE(extent->e_addr);\n}\n\nstatic inline void *\nextent_addr_get(const extent_t *extent) {\n\tassert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||\n\t    !extent_slab_get(extent));\n\treturn extent->e_addr;\n}\n\nstatic inline size_t\nextent_size_get(const extent_t *extent) {\n\treturn (extent->e_size_esn & EXTENT_SIZE_MASK);\n}\n\nstatic inline size_t\nextent_esn_get(const extent_t *extent) {\n\treturn (extent->e_size_esn & EXTENT_ESN_MASK);\n}\n\nstatic inline size_t\nextent_bsize_get(const extent_t *extent) {\n\treturn extent->e_bsize;\n}\n\nstatic inline void *\nextent_before_get(const extent_t *extent) {\n\treturn (void *)((uintptr_t)extent_base_get(extent) - PAGE);\n}\n\nstatic inline void *\nextent_last_get(const extent_t *extent) {\n\treturn (void *)((uintptr_t)extent_base_get(extent) +\n\t    extent_size_get(extent) - PAGE);\n}\n\nstatic inline void *\nextent_past_get(const extent_t *extent) {\n\treturn (void *)((uintptr_t)extent_base_get(extent) +\n\t    extent_size_get(extent));\n}\n\nstatic inline arena_slab_data_t *\nextent_slab_data_get(extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\treturn &extent->e_slab_data;\n}\n\nstatic inline const arena_slab_data_t *\nextent_slab_data_get_const(const extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\treturn &extent->e_slab_data;\n}\n\nstatic inline prof_tctx_t *\nextent_prof_tctx_get(const extent_t *extent) {\n\treturn (prof_tctx_t *)atomic_load_p(&extent->e_prof_tctx,\n\t    ATOMIC_ACQUIRE);\n}\n\nstatic inline void\nextent_arena_set(extent_t *extent, arena_t *arena) {\n\tunsigned arena_ind = (arena != NULL) ? arena_ind_get(arena) : ((1U <<\n\t    MALLOCX_ARENA_BITS) - 1);\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_ARENA_MASK) |\n\t    ((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);\n}\n\nstatic inline void\nextent_addr_set(extent_t *extent, void *addr) {\n\textent->e_addr = addr;\n}\n\nstatic inline void\nextent_addr_randomize(UNUSED tsdn_t *tsdn, extent_t *extent, size_t alignment) {\n\tassert(extent_base_get(extent) == extent_addr_get(extent));\n\n\tif (alignment < PAGE) {\n\t\tunsigned lg_range = LG_PAGE -\n\t\t    lg_floor(CACHELINE_CEILING(alignment));\n\t\tsize_t r;\n\t\tif (!tsdn_null(tsdn)) {\n\t\t\ttsd_t *tsd = tsdn_tsd(tsdn);\n\t\t\tr = (size_t)prng_lg_range_u64(\n\t\t\t    tsd_offset_statep_get(tsd), lg_range);\n\t\t} else {\n\t\t\tr = prng_lg_range_zu(\n\t\t\t    &extent_arena_get(extent)->offset_state,\n\t\t\t    lg_range, true);\n\t\t}\n\t\tuintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -\n\t\t    lg_range);\n\t\textent->e_addr = (void *)((uintptr_t)extent->e_addr +\n\t\t    random_offset);\n\t\tassert(ALIGNMENT_ADDR2BASE(extent->e_addr, alignment) ==\n\t\t    extent->e_addr);\n\t}\n}\n\nstatic inline void\nextent_size_set(extent_t *extent, size_t size) {\n\tassert((size & ~EXTENT_SIZE_MASK) == 0);\n\textent->e_size_esn = size | (extent->e_size_esn & ~EXTENT_SIZE_MASK);\n}\n\nstatic inline void\nextent_esn_set(extent_t *extent, size_t esn) {\n\textent->e_size_esn = (extent->e_size_esn & ~EXTENT_ESN_MASK) | (esn &\n\t    EXTENT_ESN_MASK);\n}\n\nstatic inline void\nextent_bsize_set(extent_t *extent, size_t bsize) {\n\textent->e_bsize = bsize;\n}\n\nstatic inline void\nextent_szind_set(extent_t *extent, szind_t szind) {\n\tassert(szind <= NSIZES); /* NSIZES means \"invalid\". */\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_SZIND_MASK) |\n\t    ((uint64_t)szind << EXTENT_BITS_SZIND_SHIFT);\n}\n\nstatic inline void\nextent_nfree_set(extent_t *extent, unsigned nfree) {\n\tassert(extent_slab_get(extent));\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_NFREE_MASK) |\n\t    ((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void\nextent_nfree_inc(extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\textent->e_bits += ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void\nextent_nfree_dec(extent_t *extent) {\n\tassert(extent_slab_get(extent));\n\textent->e_bits -= ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);\n}\n\nstatic inline void\nextent_sn_set(extent_t *extent, size_t sn) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_SN_MASK) |\n\t    ((uint64_t)sn << EXTENT_BITS_SN_SHIFT);\n}\n\nstatic inline void\nextent_state_set(extent_t *extent, extent_state_t state) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_STATE_MASK) |\n\t    ((uint64_t)state << EXTENT_BITS_STATE_SHIFT);\n}\n\nstatic inline void\nextent_zeroed_set(extent_t *extent, bool zeroed) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_ZEROED_MASK) |\n\t    ((uint64_t)zeroed << EXTENT_BITS_ZEROED_SHIFT);\n}\n\nstatic inline void\nextent_committed_set(extent_t *extent, bool committed) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_COMMITTED_MASK) |\n\t    ((uint64_t)committed << EXTENT_BITS_COMMITTED_SHIFT);\n}\n\nstatic inline void\nextent_dumpable_set(extent_t *extent, bool dumpable) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_DUMPABLE_MASK) |\n\t    ((uint64_t)dumpable << EXTENT_BITS_DUMPABLE_SHIFT);\n}\n\nstatic inline void\nextent_slab_set(extent_t *extent, bool slab) {\n\textent->e_bits = (extent->e_bits & ~EXTENT_BITS_SLAB_MASK) |\n\t    ((uint64_t)slab << EXTENT_BITS_SLAB_SHIFT);\n}\n\nstatic inline void\nextent_prof_tctx_set(extent_t *extent, prof_tctx_t *tctx) {\n\tatomic_store_p(&extent->e_prof_tctx, tctx, ATOMIC_RELEASE);\n}\n\nstatic inline void\nextent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,\n    bool slab, szind_t szind, size_t sn, extent_state_t state, bool zeroed,\n    bool committed, bool dumpable) {\n\tassert(addr == PAGE_ADDR2BASE(addr) || !slab);\n\n\textent_arena_set(extent, arena);\n\textent_addr_set(extent, addr);\n\textent_size_set(extent, size);\n\textent_slab_set(extent, slab);\n\textent_szind_set(extent, szind);\n\textent_sn_set(extent, sn);\n\textent_state_set(extent, state);\n\textent_zeroed_set(extent, zeroed);\n\textent_committed_set(extent, committed);\n\textent_dumpable_set(extent, dumpable);\n\tql_elm_new(extent, ql_link);\n\tif (config_prof) {\n\t\textent_prof_tctx_set(extent, NULL);\n\t}\n}\n\nstatic inline void\nextent_binit(extent_t *extent, void *addr, size_t bsize, size_t sn) {\n\textent_arena_set(extent, NULL);\n\textent_addr_set(extent, addr);\n\textent_bsize_set(extent, bsize);\n\textent_slab_set(extent, false);\n\textent_szind_set(extent, NSIZES);\n\textent_sn_set(extent, sn);\n\textent_state_set(extent, extent_state_active);\n\textent_zeroed_set(extent, true);\n\textent_committed_set(extent, true);\n\textent_dumpable_set(extent, true);\n}\n\nstatic inline void\nextent_list_init(extent_list_t *list) {\n\tql_new(list);\n}\n\nstatic inline extent_t *\nextent_list_first(const extent_list_t *list) {\n\treturn ql_first(list);\n}\n\nstatic inline extent_t *\nextent_list_last(const extent_list_t *list) {\n\treturn ql_last(list, ql_link);\n}\n\nstatic inline void\nextent_list_append(extent_list_t *list, extent_t *extent) {\n\tql_tail_insert(list, extent, ql_link);\n}\n\nstatic inline void\nextent_list_prepend(extent_list_t *list, extent_t *extent) {\n\tql_head_insert(list, extent, ql_link);\n}\n\nstatic inline void\nextent_list_replace(extent_list_t *list, extent_t *to_remove,\n    extent_t *to_insert) {\n\tql_after_insert(to_remove, to_insert, ql_link);\n\tql_remove(list, to_remove, ql_link);\n}\n\nstatic inline void\nextent_list_remove(extent_list_t *list, extent_t *extent) {\n\tql_remove(list, extent, ql_link);\n}\n\nstatic inline int\nextent_sn_comp(const extent_t *a, const extent_t *b) {\n\tsize_t a_sn = extent_sn_get(a);\n\tsize_t b_sn = extent_sn_get(b);\n\n\treturn (a_sn > b_sn) - (a_sn < b_sn);\n}\n\nstatic inline int\nextent_esn_comp(const extent_t *a, const extent_t *b) {\n\tsize_t a_esn = extent_esn_get(a);\n\tsize_t b_esn = extent_esn_get(b);\n\n\treturn (a_esn > b_esn) - (a_esn < b_esn);\n}\n\nstatic inline int\nextent_ad_comp(const extent_t *a, const extent_t *b) {\n\tuintptr_t a_addr = (uintptr_t)extent_addr_get(a);\n\tuintptr_t b_addr = (uintptr_t)extent_addr_get(b);\n\n\treturn (a_addr > b_addr) - (a_addr < b_addr);\n}\n\nstatic inline int\nextent_ead_comp(const extent_t *a, const extent_t *b) {\n\tuintptr_t a_eaddr = (uintptr_t)a;\n\tuintptr_t b_eaddr = (uintptr_t)b;\n\n\treturn (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);\n}\n\nstatic inline int\nextent_snad_comp(const extent_t *a, const extent_t *b) {\n\tint ret;\n\n\tret = extent_sn_comp(a, b);\n\tif (ret != 0) {\n\t\treturn ret;\n\t}\n\n\tret = extent_ad_comp(a, b);\n\treturn ret;\n}\n\nstatic inline int\nextent_esnead_comp(const extent_t *a, const extent_t *b) {\n\tint ret;\n\n\tret = extent_esn_comp(a, b);\n\tif (ret != 0) {\n\t\treturn ret;\n\t}\n\n\tret = extent_ead_comp(a, b);\n\treturn ret;\n}\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_INLINES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_mmap.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H\n#define JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H\n\nextern bool opt_retain;\n\nvoid *extent_alloc_mmap(void *new_addr, size_t size, size_t alignment,\n    bool *zero, bool *commit);\nbool extent_dalloc_mmap(void *addr, size_t size);\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_STRUCTS_H\n#define JEMALLOC_INTERNAL_EXTENT_STRUCTS_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bitmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/ph.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\ntypedef enum {\n\textent_state_active   = 0,\n\textent_state_dirty    = 1,\n\textent_state_muzzy    = 2,\n\textent_state_retained = 3\n} extent_state_t;\n\n/* Extent (span of pages).  Use accessor functions for e_* fields. */\nstruct extent_s {\n\t/*\n\t * Bitfield containing several fields:\n\t *\n\t * a: arena_ind\n\t * b: slab\n\t * c: committed\n\t * d: dumpable\n\t * z: zeroed\n\t * t: state\n\t * i: szind\n\t * f: nfree\n\t * n: sn\n\t *\n\t * nnnnnnnn ... nnnnffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa\n\t *\n\t * arena_ind: Arena from which this extent came, or all 1 bits if\n\t *            unassociated.\n\t *\n\t * slab: The slab flag indicates whether the extent is used for a slab\n\t *       of small regions.  This helps differentiate small size classes,\n\t *       and it indicates whether interior pointers can be looked up via\n\t *       iealloc().\n\t *\n\t * committed: The committed flag indicates whether physical memory is\n\t *            committed to the extent, whether explicitly or implicitly\n\t *            as on a system that overcommits and satisfies physical\n\t *            memory needs on demand via soft page faults.\n\t *\n\t * dumpable: The dumpable flag indicates whether or not we've set the\n\t *           memory in question to be dumpable.  Note that this\n\t *           interacts somewhat subtly with user-specified extent hooks,\n\t *           since we don't know if *they* are fiddling with\n\t *           dumpability (in which case, we don't want to undo whatever\n\t *           they're doing).  To deal with this scenario, we:\n\t *             - Make dumpable false only for memory allocated with the\n\t *               default hooks.\n\t *             - Only allow memory to go from non-dumpable to dumpable,\n\t *               and only once.\n\t *             - Never make the OS call to allow dumping when the\n\t *               dumpable bit is already set.\n\t *           These three constraints mean that we will never\n\t *           accidentally dump user memory that the user meant to set\n\t *           nondumpable with their extent hooks.\n\t *\n\t *\n\t * zeroed: The zeroed flag is used by extent recycling code to track\n\t *         whether memory is zero-filled.\n\t *\n\t * state: The state flag is an extent_state_t.\n\t *\n\t * szind: The szind flag indicates usable size class index for\n\t *        allocations residing in this extent, regardless of whether the\n\t *        extent is a slab.  Extent size and usable size often differ\n\t *        even for non-slabs, either due to sz_large_pad or promotion of\n\t *        sampled small regions.\n\t *\n\t * nfree: Number of free regions in slab.\n\t *\n\t * sn: Serial number (potentially non-unique).\n\t *\n\t *     Serial numbers may wrap around if !opt_retain, but as long as\n\t *     comparison functions fall back on address comparison for equal\n\t *     serial numbers, stable (if imperfect) ordering is maintained.\n\t *\n\t *     Serial numbers may not be unique even in the absence of\n\t *     wrap-around, e.g. when splitting an extent and assigning the same\n\t *     serial number to both resulting adjacent extents.\n\t */\n\tuint64_t\t\te_bits;\n#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))\n\n#define EXTENT_BITS_ARENA_WIDTH  MALLOCX_ARENA_BITS\n#define EXTENT_BITS_ARENA_SHIFT  0\n#define EXTENT_BITS_ARENA_MASK  MASK(EXTENT_BITS_ARENA_WIDTH, EXTENT_BITS_ARENA_SHIFT)\n\n#define EXTENT_BITS_SLAB_WIDTH  1\n#define EXTENT_BITS_SLAB_SHIFT  (EXTENT_BITS_ARENA_WIDTH + EXTENT_BITS_ARENA_SHIFT)\n#define EXTENT_BITS_SLAB_MASK  MASK(EXTENT_BITS_SLAB_WIDTH, EXTENT_BITS_SLAB_SHIFT)\n\n#define EXTENT_BITS_COMMITTED_WIDTH  1\n#define EXTENT_BITS_COMMITTED_SHIFT  (EXTENT_BITS_SLAB_WIDTH + EXTENT_BITS_SLAB_SHIFT)\n#define EXTENT_BITS_COMMITTED_MASK  MASK(EXTENT_BITS_COMMITTED_WIDTH, EXTENT_BITS_COMMITTED_SHIFT)\n\n#define EXTENT_BITS_DUMPABLE_WIDTH  1\n#define EXTENT_BITS_DUMPABLE_SHIFT  (EXTENT_BITS_COMMITTED_WIDTH + EXTENT_BITS_COMMITTED_SHIFT)\n#define EXTENT_BITS_DUMPABLE_MASK  MASK(EXTENT_BITS_DUMPABLE_WIDTH, EXTENT_BITS_DUMPABLE_SHIFT)\n\n#define EXTENT_BITS_ZEROED_WIDTH  1\n#define EXTENT_BITS_ZEROED_SHIFT  (EXTENT_BITS_DUMPABLE_WIDTH + EXTENT_BITS_DUMPABLE_SHIFT)\n#define EXTENT_BITS_ZEROED_MASK  MASK(EXTENT_BITS_ZEROED_WIDTH, EXTENT_BITS_ZEROED_SHIFT)\n\n#define EXTENT_BITS_STATE_WIDTH  2\n#define EXTENT_BITS_STATE_SHIFT  (EXTENT_BITS_ZEROED_WIDTH + EXTENT_BITS_ZEROED_SHIFT)\n#define EXTENT_BITS_STATE_MASK  MASK(EXTENT_BITS_STATE_WIDTH, EXTENT_BITS_STATE_SHIFT)\n\n#define EXTENT_BITS_SZIND_WIDTH  LG_CEIL_NSIZES\n#define EXTENT_BITS_SZIND_SHIFT  (EXTENT_BITS_STATE_WIDTH + EXTENT_BITS_STATE_SHIFT)\n#define EXTENT_BITS_SZIND_MASK  MASK(EXTENT_BITS_SZIND_WIDTH, EXTENT_BITS_SZIND_SHIFT)\n\n#define EXTENT_BITS_NFREE_WIDTH  (LG_SLAB_MAXREGS + 1)\n#define EXTENT_BITS_NFREE_SHIFT  (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)\n#define EXTENT_BITS_NFREE_MASK  MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)\n\n#define EXTENT_BITS_SN_SHIFT  (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)\n#define EXTENT_BITS_SN_MASK  (UINT64_MAX << EXTENT_BITS_SN_SHIFT)\n\n\t/* Pointer to the extent that this structure is responsible for. */\n\tvoid\t\t\t*e_addr;\n\n\tunion {\n\t\t/*\n\t\t * Extent size and serial number associated with the extent\n\t\t * structure (different than the serial number for the extent at\n\t\t * e_addr).\n\t\t *\n\t\t * ssssssss [...] ssssssss ssssnnnn nnnnnnnn\n\t\t */\n\t\tsize_t\t\t\te_size_esn;\n\t#define EXTENT_SIZE_MASK\t((size_t)~(PAGE-1))\n\t#define EXTENT_ESN_MASK\t\t((size_t)PAGE-1)\n\t\t/* Base extent size, which may not be a multiple of PAGE. */\n\t\tsize_t\t\t\te_bsize;\n\t};\n\n\t/*\n\t * List linkage, used by a variety of lists:\n\t * - bin_t's slabs_full\n\t * - extents_t's LRU\n\t * - stashed dirty extents\n\t * - arena's large allocations\n\t */\n\tql_elm(extent_t)\tql_link;\n\n\t/*\n\t * Linkage for per size class sn/address-ordered heaps, and\n\t * for extent_avail\n\t */\n\tphn(extent_t)\t\tph_link;\n\n\tunion {\n\t\t/* Small region slab metadata. */\n\t\tarena_slab_data_t\te_slab_data;\n\n\t\t/*\n\t\t * Profile counters, used for large objects.  Points to a\n\t\t * prof_tctx_t.\n\t\t */\n\t\tatomic_p_t\t\te_prof_tctx;\n\t};\n};\ntypedef ql_head(extent_t) extent_list_t;\ntypedef ph(extent_t) extent_tree_t;\ntypedef ph(extent_t) extent_heap_t;\n\n/* Quantized collection of extents, with built-in LRU queue. */\nstruct extents_s {\n\tmalloc_mutex_t\t\tmtx;\n\n\t/*\n\t * Quantized per size class heaps of extents.\n\t *\n\t * Synchronization: mtx.\n\t */\n\textent_heap_t\t\theaps[NPSIZES+1];\n\n\t/*\n\t * Bitmap for which set bits correspond to non-empty heaps.\n\t *\n\t * Synchronization: mtx.\n\t */\n\tbitmap_t\t\tbitmap[BITMAP_GROUPS(NPSIZES+1)];\n\n\t/*\n\t * LRU of all extents in heaps.\n\t *\n\t * Synchronization: mtx.\n\t */\n\textent_list_t\t\tlru;\n\n\t/*\n\t * Page sum for all extents in heaps.\n\t *\n\t * The synchronization here is a little tricky.  Modifications to npages\n\t * must hold mtx, but reads need not (though, a reader who sees npages\n\t * without holding the mutex can't assume anything about the rest of the\n\t * state of the extents_t).\n\t */\n\tatomic_zu_t\t\tnpages;\n\n\t/* All stored extents must be in the same state. */\n\textent_state_t\t\tstate;\n\n\t/*\n\t * If true, delay coalescing until eviction; otherwise coalesce during\n\t * deallocation.\n\t */\n\tbool\t\t\tdelay_coalesce;\n};\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_STRUCTS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/extent_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTENT_TYPES_H\n#define JEMALLOC_INTERNAL_EXTENT_TYPES_H\n\ntypedef struct extent_s extent_t;\ntypedef struct extents_s extents_t;\n\n#define EXTENT_HOOKS_INITIALIZER\tNULL\n\n#define EXTENT_GROW_MAX_PIND (NPSIZES - 1)\n\n/*\n * When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)\n * is the max ratio between the size of the active extent and the new extent.\n */\n#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6\n\n#endif /* JEMALLOC_INTERNAL_EXTENT_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/hash.h",
    "content": "#ifndef JEMALLOC_INTERNAL_HASH_H\n#define JEMALLOC_INTERNAL_HASH_H\n\n#include \"jemalloc/internal/assert.h\"\n\n/*\n * The following hash function is based on MurmurHash3, placed into the public\n * domain by Austin Appleby.  See https://github.com/aappleby/smhasher for\n * details.\n */\n\n/******************************************************************************/\n/* Internal implementation. */\nstatic inline uint32_t\nhash_rotl_32(uint32_t x, int8_t r) {\n\treturn ((x << r) | (x >> (32 - r)));\n}\n\nstatic inline uint64_t\nhash_rotl_64(uint64_t x, int8_t r) {\n\treturn ((x << r) | (x >> (64 - r)));\n}\n\nstatic inline uint32_t\nhash_get_block_32(const uint32_t *p, int i) {\n\t/* Handle unaligned read. */\n\tif (unlikely((uintptr_t)p & (sizeof(uint32_t)-1)) != 0) {\n\t\tuint32_t ret;\n\n\t\tmemcpy(&ret, (uint8_t *)(p + i), sizeof(uint32_t));\n\t\treturn ret;\n\t}\n\n\treturn p[i];\n}\n\nstatic inline uint64_t\nhash_get_block_64(const uint64_t *p, int i) {\n\t/* Handle unaligned read. */\n\tif (unlikely((uintptr_t)p & (sizeof(uint64_t)-1)) != 0) {\n\t\tuint64_t ret;\n\n\t\tmemcpy(&ret, (uint8_t *)(p + i), sizeof(uint64_t));\n\t\treturn ret;\n\t}\n\n\treturn p[i];\n}\n\nstatic inline uint32_t\nhash_fmix_32(uint32_t h) {\n\th ^= h >> 16;\n\th *= 0x85ebca6b;\n\th ^= h >> 13;\n\th *= 0xc2b2ae35;\n\th ^= h >> 16;\n\n\treturn h;\n}\n\nstatic inline uint64_t\nhash_fmix_64(uint64_t k) {\n\tk ^= k >> 33;\n\tk *= KQU(0xff51afd7ed558ccd);\n\tk ^= k >> 33;\n\tk *= KQU(0xc4ceb9fe1a85ec53);\n\tk ^= k >> 33;\n\n\treturn k;\n}\n\nstatic inline uint32_t\nhash_x86_32(const void *key, int len, uint32_t seed) {\n\tconst uint8_t *data = (const uint8_t *) key;\n\tconst int nblocks = len / 4;\n\n\tuint32_t h1 = seed;\n\n\tconst uint32_t c1 = 0xcc9e2d51;\n\tconst uint32_t c2 = 0x1b873593;\n\n\t/* body */\n\t{\n\t\tconst uint32_t *blocks = (const uint32_t *) (data + nblocks*4);\n\t\tint i;\n\n\t\tfor (i = -nblocks; i; i++) {\n\t\t\tuint32_t k1 = hash_get_block_32(blocks, i);\n\n\t\t\tk1 *= c1;\n\t\t\tk1 = hash_rotl_32(k1, 15);\n\t\t\tk1 *= c2;\n\n\t\t\th1 ^= k1;\n\t\t\th1 = hash_rotl_32(h1, 13);\n\t\t\th1 = h1*5 + 0xe6546b64;\n\t\t}\n\t}\n\n\t/* tail */\n\t{\n\t\tconst uint8_t *tail = (const uint8_t *) (data + nblocks*4);\n\n\t\tuint32_t k1 = 0;\n\n\t\tswitch (len & 3) {\n\t\tcase 3: k1 ^= tail[2] << 16;\n\t\tcase 2: k1 ^= tail[1] << 8;\n\t\tcase 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);\n\t\t\tk1 *= c2; h1 ^= k1;\n\t\t}\n\t}\n\n\t/* finalization */\n\th1 ^= len;\n\n\th1 = hash_fmix_32(h1);\n\n\treturn h1;\n}\n\nUNUSED static inline void\nhash_x86_128(const void *key, const int len, uint32_t seed,\n    uint64_t r_out[2]) {\n\tconst uint8_t * data = (const uint8_t *) key;\n\tconst int nblocks = len / 16;\n\n\tuint32_t h1 = seed;\n\tuint32_t h2 = seed;\n\tuint32_t h3 = seed;\n\tuint32_t h4 = seed;\n\n\tconst uint32_t c1 = 0x239b961b;\n\tconst uint32_t c2 = 0xab0e9789;\n\tconst uint32_t c3 = 0x38b34ae5;\n\tconst uint32_t c4 = 0xa1e38b93;\n\n\t/* body */\n\t{\n\t\tconst uint32_t *blocks = (const uint32_t *) (data + nblocks*16);\n\t\tint i;\n\n\t\tfor (i = -nblocks; i; i++) {\n\t\t\tuint32_t k1 = hash_get_block_32(blocks, i*4 + 0);\n\t\t\tuint32_t k2 = hash_get_block_32(blocks, i*4 + 1);\n\t\t\tuint32_t k3 = hash_get_block_32(blocks, i*4 + 2);\n\t\t\tuint32_t k4 = hash_get_block_32(blocks, i*4 + 3);\n\n\t\t\tk1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;\n\n\t\t\th1 = hash_rotl_32(h1, 19); h1 += h2;\n\t\t\th1 = h1*5 + 0x561ccd1b;\n\n\t\t\tk2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;\n\n\t\t\th2 = hash_rotl_32(h2, 17); h2 += h3;\n\t\t\th2 = h2*5 + 0x0bcaa747;\n\n\t\t\tk3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;\n\n\t\t\th3 = hash_rotl_32(h3, 15); h3 += h4;\n\t\t\th3 = h3*5 + 0x96cd1c35;\n\n\t\t\tk4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;\n\n\t\t\th4 = hash_rotl_32(h4, 13); h4 += h1;\n\t\t\th4 = h4*5 + 0x32ac3b17;\n\t\t}\n\t}\n\n\t/* tail */\n\t{\n\t\tconst uint8_t *tail = (const uint8_t *) (data + nblocks*16);\n\t\tuint32_t k1 = 0;\n\t\tuint32_t k2 = 0;\n\t\tuint32_t k3 = 0;\n\t\tuint32_t k4 = 0;\n\n\t\tswitch (len & 15) {\n\t\tcase 15: k4 ^= tail[14] << 16;\n\t\tcase 14: k4 ^= tail[13] << 8;\n\t\tcase 13: k4 ^= tail[12] << 0;\n\t\t\tk4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;\n\n\t\tcase 12: k3 ^= tail[11] << 24;\n\t\tcase 11: k3 ^= tail[10] << 16;\n\t\tcase 10: k3 ^= tail[ 9] << 8;\n\t\tcase  9: k3 ^= tail[ 8] << 0;\n\t\t     k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;\n\n\t\tcase  8: k2 ^= tail[ 7] << 24;\n\t\tcase  7: k2 ^= tail[ 6] << 16;\n\t\tcase  6: k2 ^= tail[ 5] << 8;\n\t\tcase  5: k2 ^= tail[ 4] << 0;\n\t\t\tk2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;\n\n\t\tcase  4: k1 ^= tail[ 3] << 24;\n\t\tcase  3: k1 ^= tail[ 2] << 16;\n\t\tcase  2: k1 ^= tail[ 1] << 8;\n\t\tcase  1: k1 ^= tail[ 0] << 0;\n\t\t\tk1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;\n\t\t}\n\t}\n\n\t/* finalization */\n\th1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;\n\n\th1 += h2; h1 += h3; h1 += h4;\n\th2 += h1; h3 += h1; h4 += h1;\n\n\th1 = hash_fmix_32(h1);\n\th2 = hash_fmix_32(h2);\n\th3 = hash_fmix_32(h3);\n\th4 = hash_fmix_32(h4);\n\n\th1 += h2; h1 += h3; h1 += h4;\n\th2 += h1; h3 += h1; h4 += h1;\n\n\tr_out[0] = (((uint64_t) h2) << 32) | h1;\n\tr_out[1] = (((uint64_t) h4) << 32) | h3;\n}\n\nUNUSED static inline void\nhash_x64_128(const void *key, const int len, const uint32_t seed,\n    uint64_t r_out[2]) {\n\tconst uint8_t *data = (const uint8_t *) key;\n\tconst int nblocks = len / 16;\n\n\tuint64_t h1 = seed;\n\tuint64_t h2 = seed;\n\n\tconst uint64_t c1 = KQU(0x87c37b91114253d5);\n\tconst uint64_t c2 = KQU(0x4cf5ad432745937f);\n\n\t/* body */\n\t{\n\t\tconst uint64_t *blocks = (const uint64_t *) (data);\n\t\tint i;\n\n\t\tfor (i = 0; i < nblocks; i++) {\n\t\t\tuint64_t k1 = hash_get_block_64(blocks, i*2 + 0);\n\t\t\tuint64_t k2 = hash_get_block_64(blocks, i*2 + 1);\n\n\t\t\tk1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;\n\n\t\t\th1 = hash_rotl_64(h1, 27); h1 += h2;\n\t\t\th1 = h1*5 + 0x52dce729;\n\n\t\t\tk2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;\n\n\t\t\th2 = hash_rotl_64(h2, 31); h2 += h1;\n\t\t\th2 = h2*5 + 0x38495ab5;\n\t\t}\n\t}\n\n\t/* tail */\n\t{\n\t\tconst uint8_t *tail = (const uint8_t*)(data + nblocks*16);\n\t\tuint64_t k1 = 0;\n\t\tuint64_t k2 = 0;\n\n\t\tswitch (len & 15) {\n\t\tcase 15: k2 ^= ((uint64_t)(tail[14])) << 48; /* falls through */\n\t\tcase 14: k2 ^= ((uint64_t)(tail[13])) << 40; /* falls through */\n\t\tcase 13: k2 ^= ((uint64_t)(tail[12])) << 32; /* falls through */\n\t\tcase 12: k2 ^= ((uint64_t)(tail[11])) << 24; /* falls through */\n\t\tcase 11: k2 ^= ((uint64_t)(tail[10])) << 16; /* falls through */\n\t\tcase 10: k2 ^= ((uint64_t)(tail[ 9])) << 8;  /* falls through */\n\t\tcase  9: k2 ^= ((uint64_t)(tail[ 8])) << 0;\n\t\t\tk2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;\n\t\t\t/* falls through */\n\t\tcase  8: k1 ^= ((uint64_t)(tail[ 7])) << 56; /* falls through */\n\t\tcase  7: k1 ^= ((uint64_t)(tail[ 6])) << 48; /* falls through */\n\t\tcase  6: k1 ^= ((uint64_t)(tail[ 5])) << 40; /* falls through */\n\t\tcase  5: k1 ^= ((uint64_t)(tail[ 4])) << 32; /* falls through */\n\t\tcase  4: k1 ^= ((uint64_t)(tail[ 3])) << 24; /* falls through */\n\t\tcase  3: k1 ^= ((uint64_t)(tail[ 2])) << 16; /* falls through */\n\t\tcase  2: k1 ^= ((uint64_t)(tail[ 1])) << 8;  /* falls through */\n\t\tcase  1: k1 ^= ((uint64_t)(tail[ 0])) << 0;\n\t\t\tk1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;\n\t\t}\n\t}\n\n\t/* finalization */\n\th1 ^= len; h2 ^= len;\n\n\th1 += h2;\n\th2 += h1;\n\n\th1 = hash_fmix_64(h1);\n\th2 = hash_fmix_64(h2);\n\n\th1 += h2;\n\th2 += h1;\n\n\tr_out[0] = h1;\n\tr_out[1] = h2;\n}\n\n/******************************************************************************/\n/* API. */\nstatic inline void\nhash(const void *key, size_t len, const uint32_t seed, size_t r_hash[2]) {\n\tassert(len <= INT_MAX); /* Unfortunate implementation limitation. */\n\n#if (LG_SIZEOF_PTR == 3 && !defined(JEMALLOC_BIG_ENDIAN))\n\thash_x64_128(key, (int)len, seed, (uint64_t *)r_hash);\n#else\n\t{\n\t\tuint64_t hashes[2];\n\t\thash_x86_128(key, (int)len, seed, hashes);\n\t\tr_hash[0] = (size_t)hashes[0];\n\t\tr_hash[1] = (size_t)hashes[1];\n\t}\n#endif\n}\n\n#endif /* JEMALLOC_INTERNAL_HASH_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/hooks.h",
    "content": "#ifndef JEMALLOC_INTERNAL_HOOKS_H\n#define JEMALLOC_INTERNAL_HOOKS_H\n\nextern JEMALLOC_EXPORT void (*hooks_arena_new_hook)();\nextern JEMALLOC_EXPORT void (*hooks_libc_hook)();\n\n#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)\n\n#define open JEMALLOC_HOOK(open, hooks_libc_hook)\n#define read JEMALLOC_HOOK(read, hooks_libc_hook)\n#define write JEMALLOC_HOOK(write, hooks_libc_hook)\n#define readlink JEMALLOC_HOOK(readlink, hooks_libc_hook)\n#define close JEMALLOC_HOOK(close, hooks_libc_hook)\n#define creat JEMALLOC_HOOK(creat, hooks_libc_hook)\n#define secure_getenv JEMALLOC_HOOK(secure_getenv, hooks_libc_hook)\n/* Note that this is undef'd and re-define'd in src/prof.c. */\n#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)\n\n#endif /* JEMALLOC_INTERNAL_HOOKS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h",
    "content": "#ifndef JEMALLOC_INTERNAL_DECLS_H\n#define JEMALLOC_INTERNAL_DECLS_H\n\n#include <math.h>\n#ifdef _WIN32\n#  include <windows.h>\n#  include \"msvc_compat/windows_extra.h\"\n#  ifdef _WIN64\n#    if LG_VADDR <= 32\n#      error Generate the headers using x64 vcargs\n#    endif\n#  else\n#    if LG_VADDR > 32\n#      undef LG_VADDR\n#      define LG_VADDR 32\n#    endif\n#  endif\n#else\n#  include <sys/param.h>\n#  include <sys/mman.h>\n#  if !defined(__pnacl__) && !defined(__native_client__)\n#    include <sys/syscall.h>\n#    if !defined(SYS_write) && defined(__NR_write)\n#      define SYS_write __NR_write\n#    endif\n#    if defined(SYS_open) && defined(__aarch64__)\n       /* Android headers may define SYS_open to __NR_open even though\n        * __NR_open may not exist on AArch64 (superseded by __NR_openat). */\n#      undef SYS_open\n#    endif\n#    include <sys/uio.h>\n#  endif\n#  include <pthread.h>\n#  include <signal.h>\n#  ifdef JEMALLOC_OS_UNFAIR_LOCK\n#    include <os/lock.h>\n#  endif\n#  ifdef JEMALLOC_GLIBC_MALLOC_HOOK\n#    include <sched.h>\n#  endif\n#  include <errno.h>\n#  include <sys/time.h>\n#  include <time.h>\n#  ifdef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME\n#    include <mach/mach_time.h>\n#  endif\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#ifndef SSIZE_MAX\n#  define SSIZE_MAX\t((ssize_t)(SIZE_T_MAX >> 1))\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 <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#  ifdef JEMALLOC_HAS_RESTRICT\n#    define restrict __restrict\n#  endif\n/* Disable warnings about deprecated system functions. */\n#  pragma warning(disable: 4996)\n#if _MSC_VER < 1800\nstatic int\nisblank(int c) {\n\treturn (c == '\\t' || c == ' ');\n}\n#endif\n#else\n#  include <unistd.h>\n#endif\n#include <fcntl.h>\n\n#endif /* JEMALLOC_INTERNAL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in",
    "content": "#ifndef JEMALLOC_INTERNAL_DEFS_H_\n#define JEMALLOC_INTERNAL_DEFS_H_\n/*\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 * Define overrides for non-standard allocator-related functions if they are\n * present on the system.\n */\n#undef JEMALLOC_OVERRIDE___LIBC_CALLOC\n#undef JEMALLOC_OVERRIDE___LIBC_FREE\n#undef JEMALLOC_OVERRIDE___LIBC_MALLOC\n#undef JEMALLOC_OVERRIDE___LIBC_MEMALIGN\n#undef JEMALLOC_OVERRIDE___LIBC_REALLOC\n#undef JEMALLOC_OVERRIDE___LIBC_VALLOC\n#undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN\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\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/* 1 if CPU_SPINWAIT is defined, 0 otherwise. */\n#undef HAVE_CPU_SPINWAIT\n\n/*\n * Number of significant bits in virtual addresses.  This may be less than the\n * total number of bits in a pointer, e.g. on x64, for which the uppermost 16\n * bits are the same as bit 47.\n */\n#undef LG_VADDR\n\n/* Defined if C11 atomics are available. */\n#undef JEMALLOC_C11_ATOMICS\n\n/* Defined if GCC __atomic atomics are available. */\n#undef JEMALLOC_GCC_ATOMIC_ATOMICS\n\n/* Defined if GCC __sync atomics are available. */\n#undef JEMALLOC_GCC_SYNC_ATOMICS\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 __builtin_clz() and __builtin_clzl() are available.\n */\n#undef JEMALLOC_HAVE_BUILTIN_CLZ\n\n/*\n * Defined if os_unfair_lock_*() functions are available, as provided by Darwin.\n */\n#undef JEMALLOC_OS_UNFAIR_LOCK\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/* Defined if syscall(2) is usable. */\n#undef JEMALLOC_USE_SYSCALL\n\n/*\n * Defined if secure_getenv(3) is available.\n */\n#undef JEMALLOC_HAVE_SECURE_GETENV\n\n/*\n * Defined if issetugid(2) is available.\n */\n#undef JEMALLOC_HAVE_ISSETUGID\n\n/* Defined if pthread_atfork(3) is available. */\n#undef JEMALLOC_HAVE_PTHREAD_ATFORK\n\n/* Defined if pthread_setname_np(3) is available. */\n#undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP\n\n/*\n * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.\n */\n#undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE\n\n/*\n * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.\n */\n#undef JEMALLOC_HAVE_CLOCK_MONOTONIC\n\n/*\n * Defined if mach_absolute_time() is available.\n */\n#undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME\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/* Non-empty if the tls_model attribute is supported. */\n#undef JEMALLOC_TLS_MODEL\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_DSS enables use of sbrk(2) to allocate extents from the data storage\n * segment (DSS).\n */\n#undef JEMALLOC_DSS\n\n/* Support memory filling (junk/zero). */\n#undef JEMALLOC_FILL\n\n/* Support utrace(2)-based tracing. */\n#undef JEMALLOC_UTRACE\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/*\n * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size\n * classes).\n */\n#undef LG_QUANTUM\n\n/* One page is 2^LG_PAGE bytes. */\n#undef LG_PAGE\n\n/*\n * One huge page is 2^LG_HUGEPAGE bytes.  Note that this is defined even if the\n * system does not explicitly support huge pages; system calls that require\n * explicit huge page support are separately configured.\n */\n#undef LG_HUGEPAGE\n\n/*\n * If defined, adjacent virtual memory mappings with identical attributes\n * automatically coalesce, and they fragment when changes are made to subranges.\n * This is the normal order of things for mmap()/munmap(), but on Windows\n * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.\n * mappings do *not* coalesce/fragment.\n */\n#undef JEMALLOC_MAPS_COALESCE\n\n/*\n * If defined, retain memory for later reuse by default rather than using e.g.\n * munmap() to unmap freed extents.  This is enabled on 64-bit Linux because\n * common sequences of mmap()/munmap() calls will cause virtual memory map\n * holes.\n */\n#undef JEMALLOC_RETAIN\n\n/* TLS is used to map arenas and magazine caches to threads. */\n#undef JEMALLOC_TLS\n\n/*\n * Used to mark unreachable code to quiet \"end of non-void\" compiler warnings.\n * Don't use this directly; instead use unreachable() from util.h\n */\n#undef JEMALLOC_INTERNAL_UNREACHABLE\n\n/*\n * ffs*() functions to use for bitmapping.  Don't use these directly; instead,\n * use ffs_*() from util.h.\n */\n#undef JEMALLOC_INTERNAL_FFSLL\n#undef JEMALLOC_INTERNAL_FFSL\n#undef JEMALLOC_INTERNAL_FFS\n\n/*\n * If defined, explicitly attempt to more uniformly distribute large allocation\n * pointer alignments across all cache indices.\n */\n#undef JEMALLOC_CACHE_OBLIVIOUS\n\n/*\n * If defined, enable logging facilities.  We make this a configure option to\n * avoid taking extra branches everywhere.\n */\n#undef JEMALLOC_LOG\n\n/*\n * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.\n */\n#undef JEMALLOC_ZONE\n\n/*\n * Methods for determining whether the OS overcommits.\n * JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's\n *                                         /proc/sys/vm.overcommit_memory file.\n * JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.\n */\n#undef JEMALLOC_SYSCTL_VM_OVERCOMMIT\n#undef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY\n\n/* Defined if madvise(2) is available. */\n#undef JEMALLOC_HAVE_MADVISE\n\n/*\n * Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE\n * arguments to madvise(2).\n */\n#undef JEMALLOC_HAVE_MADVISE_HUGE\n\n/*\n * Methods for purging unused pages differ between operating systems.\n *\n *   madvise(..., MADV_FREE) : This marks pages as being unused, such that they\n *                             will be discarded rather than swapped out.\n *   madvise(..., MADV_DONTNEED) : If JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS is\n *                                 defined, this immediately discards pages,\n *                                 such that new pages will be demand-zeroed if\n *                                 the address region is later touched;\n *                                 otherwise this behaves similarly to\n *                                 MADV_FREE, though typically with higher\n *                                 system overhead.\n */\n#undef JEMALLOC_PURGE_MADVISE_FREE\n#undef JEMALLOC_PURGE_MADVISE_DONTNEED\n#undef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS\n\n/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */\n#undef JEMALLOC_DEFINE_MADVISE_FREE\n\n/*\n * Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.\n */\n#undef JEMALLOC_MADVISE_DONTDUMP\n\n/*\n * Defined if transparent huge pages (THPs) are supported via the\n * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.\n */\n#undef JEMALLOC_THP\n\n/* Define if operating system has alloca.h header. */\n#undef JEMALLOC_HAS_ALLOCA_H\n\n/* C99 restrict keyword supported. */\n#undef JEMALLOC_HAS_RESTRICT\n\n/* For use by hash code. */\n#undef JEMALLOC_BIG_ENDIAN\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(long long) == 2^LG_SIZEOF_LONG_LONG. */\n#undef LG_SIZEOF_LONG_LONG\n\n/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */\n#undef LG_SIZEOF_INTMAX_T\n\n/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */\n#undef JEMALLOC_GLIBC_MALLOC_HOOK\n\n/* glibc memalign hook. */\n#undef JEMALLOC_GLIBC_MEMALIGN_HOOK\n\n/* pthread support */\n#undef JEMALLOC_HAVE_PTHREAD\n\n/* dlsym() support */\n#undef JEMALLOC_HAVE_DLSYM\n\n/* Adaptive mutex support in pthreads. */\n#undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP\n\n/* GNU specific sched_getcpu support */\n#undef JEMALLOC_HAVE_SCHED_GETCPU\n\n/* GNU specific sched_setaffinity support */\n#undef JEMALLOC_HAVE_SCHED_SETAFFINITY\n\n/*\n * If defined, all the features necessary for background threads are present.\n */\n#undef JEMALLOC_BACKGROUND_THREAD\n\n/*\n * If defined, jemalloc symbols are not exported (doesn't work when\n * JEMALLOC_PREFIX is not defined).\n */\n#undef JEMALLOC_EXPORT\n\n/* config.malloc_conf options string. */\n#undef JEMALLOC_CONFIG_MALLOC_CONF\n\n/* If defined, jemalloc takes the malloc/free/etc. symbol names. */\n#undef JEMALLOC_IS_MALLOC\n\n/*\n * Defined if strerror_r returns char * if _GNU_SOURCE is defined.\n */\n#undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE\n\n#endif /* JEMALLOC_INTERNAL_DEFS_H_ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_EXTERNS_H\n#define JEMALLOC_INTERNAL_EXTERNS_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/tsd_types.h\"\n\n/* TSD checks this to set thread local slow state accordingly. */\nextern bool malloc_slow;\n\n/* Run-time options. */\nextern bool opt_abort;\nextern bool opt_abort_conf;\nextern const char *opt_junk;\nextern bool opt_junk_alloc;\nextern bool opt_junk_free;\nextern bool opt_utrace;\nextern bool opt_xmalloc;\nextern bool opt_zero;\nextern unsigned opt_narenas;\n\n/* Number of CPUs. */\nextern unsigned ncpus;\n\n/* Number of arenas used for automatic multiplexing of threads and arenas. */\nextern unsigned narenas_auto;\n\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 */\nextern atomic_p_t arenas[];\n\nvoid *a0malloc(size_t size);\nvoid a0dalloc(void *ptr);\nvoid *bootstrap_malloc(size_t size);\nvoid *bootstrap_calloc(size_t num, size_t size);\nvoid bootstrap_free(void *ptr);\nvoid arena_set(unsigned ind, arena_t *arena);\nunsigned narenas_total_get(void);\narena_t *arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);\narena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);\narena_t *arena_choose_hard(tsd_t *tsd, bool internal);\nvoid arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);\nvoid iarena_cleanup(tsd_t *tsd);\nvoid arena_cleanup(tsd_t *tsd);\nvoid arenas_tdata_cleanup(tsd_t *tsd);\nvoid jemalloc_prefork(void);\nvoid jemalloc_postfork_parent(void);\nvoid jemalloc_postfork_child(void);\nbool malloc_initialized(void);\n\n#endif /* JEMALLOC_INTERNAL_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INCLUDES_H\n#define JEMALLOC_INTERNAL_INCLUDES_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.\n *\n * Historically, we dealt with this by each header into four sections (types,\n * structs, externs, and inlines), and included each header file multiple times\n * in this file, picking out the portion we want on each pass using the\n * following #defines:\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 * We're moving toward a world in which the dependencies are explicit; each file\n * will #include the headers it depends on (rather than relying on them being\n * implicitly available via this file including every header file in the\n * project).\n *\n * We're now in an intermediate state: we've broken up the header files to avoid\n * having to include each one multiple times, but have not yet moved the\n * dependency information into the header files (i.e. we still rely on the\n * ordering in this file to ensure all a header's dependencies are available in\n * its translation unit).  Each component is now broken up into multiple header\n * files, corresponding to the sections above (e.g. instead of \"foo.h\", we now\n * have \"foo_types.h\", \"foo_structs.h\", \"foo_externs.h\", \"foo_inlines.h\").\n *\n * Those files which have been converted to explicitly include their\n * inter-component dependencies are now in the initial HERMETIC HEADERS\n * section.  All headers may still rely on jemalloc_preamble.h (which, by fiat,\n * must be included first in every translation unit) for system headers and\n * global jemalloc definitions, however.\n */\n\n/******************************************************************************/\n/* TYPES */\n/******************************************************************************/\n\n#include \"jemalloc/internal/extent_types.h\"\n#include \"jemalloc/internal/base_types.h\"\n#include \"jemalloc/internal/arena_types.h\"\n#include \"jemalloc/internal/tcache_types.h\"\n#include \"jemalloc/internal/prof_types.h\"\n\n/******************************************************************************/\n/* STRUCTS */\n/******************************************************************************/\n\n#include \"jemalloc/internal/arena_structs_a.h\"\n#include \"jemalloc/internal/extent_structs.h\"\n#include \"jemalloc/internal/base_structs.h\"\n#include \"jemalloc/internal/prof_structs.h\"\n#include \"jemalloc/internal/arena_structs_b.h\"\n#include \"jemalloc/internal/tcache_structs.h\"\n#include \"jemalloc/internal/background_thread_structs.h\"\n\n/******************************************************************************/\n/* EXTERNS */\n/******************************************************************************/\n\n#include \"jemalloc/internal/jemalloc_internal_externs.h\"\n#include \"jemalloc/internal/extent_externs.h\"\n#include \"jemalloc/internal/base_externs.h\"\n#include \"jemalloc/internal/arena_externs.h\"\n#include \"jemalloc/internal/large_externs.h\"\n#include \"jemalloc/internal/tcache_externs.h\"\n#include \"jemalloc/internal/prof_externs.h\"\n#include \"jemalloc/internal/background_thread_externs.h\"\n\n/******************************************************************************/\n/* INLINES */\n/******************************************************************************/\n\n#include \"jemalloc/internal/jemalloc_internal_inlines_a.h\"\n#include \"jemalloc/internal/base_inlines.h\"\n/*\n * Include portions of arena code interleaved with tcache code in order to\n * resolve circular dependencies.\n */\n#include \"jemalloc/internal/prof_inlines_a.h\"\n#include \"jemalloc/internal/arena_inlines_a.h\"\n#include \"jemalloc/internal/extent_inlines.h\"\n#include \"jemalloc/internal/jemalloc_internal_inlines_b.h\"\n#include \"jemalloc/internal/tcache_inlines.h\"\n#include \"jemalloc/internal/arena_inlines_b.h\"\n#include \"jemalloc/internal/jemalloc_internal_inlines_c.h\"\n#include \"jemalloc/internal/prof_inlines_b.h\"\n#include \"jemalloc/internal/background_thread_inlines.h\"\n\n#endif /* JEMALLOC_INTERNAL_INCLUDES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INLINES_A_H\n#define JEMALLOC_INTERNAL_INLINES_A_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bit_util.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nJEMALLOC_ALWAYS_INLINE malloc_cpuid_t\nmalloc_getcpu(void) {\n\tassert(have_percpu_arena);\n#if defined(JEMALLOC_HAVE_SCHED_GETCPU)\n\treturn (malloc_cpuid_t)sched_getcpu();\n#else\n\tnot_reached();\n\treturn -1;\n#endif\n}\n\n/* Return the chosen arena index based on current cpu. */\nJEMALLOC_ALWAYS_INLINE unsigned\npercpu_arena_choose(void) {\n\tassert(have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena));\n\n\tmalloc_cpuid_t cpuid = malloc_getcpu();\n\tassert(cpuid >= 0);\n\n\tunsigned arena_ind;\n\tif ((opt_percpu_arena == percpu_arena) || ((unsigned)cpuid < ncpus /\n\t    2)) {\n\t\tarena_ind = cpuid;\n\t} else {\n\t\tassert(opt_percpu_arena == per_phycpu_arena);\n\t\t/* Hyper threads on the same physical CPU share arena. */\n\t\tarena_ind = cpuid - ncpus / 2;\n\t}\n\n\treturn arena_ind;\n}\n\n/* Return the limit of percpu auto arena range, i.e. arenas[0...ind_limit). */\nJEMALLOC_ALWAYS_INLINE unsigned\npercpu_arena_ind_limit(percpu_arena_mode_t mode) {\n\tassert(have_percpu_arena && PERCPU_ARENA_ENABLED(mode));\n\tif (mode == per_phycpu_arena && ncpus > 1) {\n\t\tif (ncpus % 2) {\n\t\t\t/* This likely means a misconfig. */\n\t\t\treturn ncpus / 2 + 1;\n\t\t}\n\t\treturn ncpus / 2;\n\t} else {\n\t\treturn ncpus;\n\t}\n}\n\nstatic inline arena_tdata_t *\narena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing) {\n\tarena_tdata_t *tdata;\n\tarena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);\n\n\tif (unlikely(arenas_tdata == NULL)) {\n\t\t/* arenas_tdata hasn't been initialized yet. */\n\t\treturn arena_tdata_get_hard(tsd, ind);\n\t}\n\tif (unlikely(ind >= tsd_narenas_tdata_get(tsd))) {\n\t\t/*\n\t\t * ind is invalid, cache is old (too small), or tdata to be\n\t\t * initialized.\n\t\t */\n\t\treturn (refresh_if_missing ? arena_tdata_get_hard(tsd, ind) :\n\t\t    NULL);\n\t}\n\n\ttdata = &arenas_tdata[ind];\n\tif (likely(tdata != NULL) || !refresh_if_missing) {\n\t\treturn tdata;\n\t}\n\treturn arena_tdata_get_hard(tsd, ind);\n}\n\nstatic inline arena_t *\narena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {\n\tarena_t *ret;\n\n\tassert(ind < MALLOCX_ARENA_LIMIT);\n\n\tret = (arena_t *)atomic_load_p(&arenas[ind], ATOMIC_ACQUIRE);\n\tif (unlikely(ret == NULL)) {\n\t\tif (init_if_missing) {\n\t\t\tret = arena_init(tsdn, ind,\n\t\t\t    (extent_hooks_t *)&extent_hooks_default);\n\t\t}\n\t}\n\treturn ret;\n}\n\nstatic inline ticker_t *\ndecay_ticker_get(tsd_t *tsd, unsigned ind) {\n\tarena_tdata_t *tdata;\n\n\ttdata = arena_tdata_get(tsd, ind, true);\n\tif (unlikely(tdata == NULL)) {\n\t\treturn NULL;\n\t}\n\treturn &tdata->decay_ticker;\n}\n\nJEMALLOC_ALWAYS_INLINE cache_bin_t *\ntcache_small_bin_get(tcache_t *tcache, szind_t binind) {\n\tassert(binind < NBINS);\n\treturn &tcache->bins_small[binind];\n}\n\nJEMALLOC_ALWAYS_INLINE cache_bin_t *\ntcache_large_bin_get(tcache_t *tcache, szind_t binind) {\n\tassert(binind >= NBINS &&binind < nhbins);\n\treturn &tcache->bins_large[binind - NBINS];\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntcache_available(tsd_t *tsd) {\n\t/*\n\t * Thread specific auto tcache might be unavailable if: 1) during tcache\n\t * initialization, or 2) disabled through thread.tcache.enabled mallctl\n\t * or config options.  This check covers all cases.\n\t */\n\tif (likely(tsd_tcache_enabled_get(tsd))) {\n\t\t/* Associated arena == NULL implies tcache init in progress. */\n\t\tassert(tsd_tcachep_get(tsd)->arena == NULL ||\n\t\t    tcache_small_bin_get(tsd_tcachep_get(tsd), 0)->avail !=\n\t\t    NULL);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE tcache_t *\ntcache_get(tsd_t *tsd) {\n\tif (!tcache_available(tsd)) {\n\t\treturn NULL;\n\t}\n\n\treturn tsd_tcachep_get(tsd);\n}\n\nstatic inline void\npre_reentrancy(tsd_t *tsd, arena_t *arena) {\n\t/* arena is the current context.  Reentry from a0 is not allowed. */\n\tassert(arena != arena_get(tsd_tsdn(tsd), 0, false));\n\n\tbool fast = tsd_fast(tsd);\n\tassert(tsd_reentrancy_level_get(tsd) < INT8_MAX);\n\t++*tsd_reentrancy_levelp_get(tsd);\n\tif (fast) {\n\t\t/* Prepare slow path for reentrancy. */\n\t\ttsd_slow_update(tsd);\n\t\tassert(tsd->state == tsd_state_nominal_slow);\n\t}\n}\n\nstatic inline void\npost_reentrancy(tsd_t *tsd) {\n\tint8_t *reentrancy_level = tsd_reentrancy_levelp_get(tsd);\n\tassert(*reentrancy_level > 0);\n\tif (--*reentrancy_level == 0) {\n\t\ttsd_slow_update(tsd);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_INLINES_A_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INLINES_B_H\n#define JEMALLOC_INTERNAL_INLINES_B_H\n\n#include \"jemalloc/internal/rtree.h\"\n\n/* Choose an arena based on a per-thread value. */\nstatic inline arena_t *\narena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal) {\n\tarena_t *ret;\n\n\tif (arena != NULL) {\n\t\treturn arena;\n\t}\n\n\t/* During reentrancy, arena 0 is the safest bet. */\n\tif (unlikely(tsd_reentrancy_level_get(tsd) > 0)) {\n\t\treturn arena_get(tsd_tsdn(tsd), 0, true);\n\t}\n\n\tret = internal ? tsd_iarena_get(tsd) : tsd_arena_get(tsd);\n\tif (unlikely(ret == NULL)) {\n\t\tret = arena_choose_hard(tsd, internal);\n\t\tassert(ret);\n\t\tif (tcache_available(tsd)) {\n\t\t\ttcache_t *tcache = tcache_get(tsd);\n\t\t\tif (tcache->arena != NULL) {\n\t\t\t\t/* See comments in tcache_data_init().*/\n\t\t\t\tassert(tcache->arena ==\n\t\t\t\t    arena_get(tsd_tsdn(tsd), 0, false));\n\t\t\t\tif (tcache->arena != ret) {\n\t\t\t\t\ttcache_arena_reassociate(tsd_tsdn(tsd),\n\t\t\t\t\t    tcache, ret);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttcache_arena_associate(tsd_tsdn(tsd), tcache,\n\t\t\t\t    ret);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Note that for percpu arena, if the current arena is outside of the\n\t * auto percpu arena range, (i.e. thread is assigned to a manually\n\t * managed arena), then percpu arena is skipped.\n\t */\n\tif (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena) &&\n\t    !internal && (arena_ind_get(ret) <\n\t    percpu_arena_ind_limit(opt_percpu_arena)) && (ret->last_thd !=\n\t    tsd_tsdn(tsd))) {\n\t\tunsigned ind = percpu_arena_choose();\n\t\tif (arena_ind_get(ret) != ind) {\n\t\t\tpercpu_arena_update(tsd, ind);\n\t\t\tret = tsd_arena_get(tsd);\n\t\t}\n\t\tret->last_thd = tsd_tsdn(tsd);\n\t}\n\n\treturn ret;\n}\n\nstatic inline arena_t *\narena_choose(tsd_t *tsd, arena_t *arena) {\n\treturn arena_choose_impl(tsd, arena, false);\n}\n\nstatic inline arena_t *\narena_ichoose(tsd_t *tsd, arena_t *arena) {\n\treturn arena_choose_impl(tsd, arena, true);\n}\n\nstatic inline bool\narena_is_auto(arena_t *arena) {\n\tassert(narenas_auto > 0);\n\treturn (arena_ind_get(arena) < narenas_auto);\n}\n\nJEMALLOC_ALWAYS_INLINE extent_t *\niealloc(tsdn_t *tsdn, const void *ptr) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\treturn rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true);\n}\n\n#endif /* JEMALLOC_INTERNAL_INLINES_B_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h",
    "content": "#ifndef JEMALLOC_INTERNAL_INLINES_C_H\n#define JEMALLOC_INTERNAL_INLINES_C_H\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/witness.h\"\n\n/*\n * Translating the names of the 'i' functions:\n *   Abbreviations used in the first part of the function name (before\n *   alloc/dalloc) describe what that function accomplishes:\n *     a: arena (query)\n *     s: size (query, or sized deallocation)\n *     e: extent (query)\n *     p: aligned (allocates)\n *     vs: size (query, without knowing that the pointer is into the heap)\n *     r: rallocx implementation\n *     x: xallocx implementation\n *   Abbreviations used in the second part of the function name (after\n *   alloc/dalloc) describe the arguments it takes\n *     z: whether to return zeroed memory\n *     t: accepts a tcache_t * parameter\n *     m: accepts an arena_t * parameter\n */\n\nJEMALLOC_ALWAYS_INLINE arena_t *\niaalloc(tsdn_t *tsdn, const void *ptr) {\n\tassert(ptr != NULL);\n\n\treturn arena_aalloc(tsdn, ptr);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nisalloc(tsdn_t *tsdn, const void *ptr) {\n\tassert(ptr != NULL);\n\n\treturn arena_salloc(tsdn, ptr);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,\n    bool is_internal, arena_t *arena, bool slow_path) {\n\tvoid *ret;\n\n\tassert(size != 0);\n\tassert(!is_internal || tcache == NULL);\n\tassert(!is_internal || arena == NULL || arena_is_auto(arena));\n\tif (!tsdn_null(tsdn) && tsd_reentrancy_level_get(tsdn_tsd(tsdn)) == 0) {\n\t\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t\t    WITNESS_RANK_CORE, 0);\n\t}\n\n\tret = arena_malloc(tsdn, arena, size, ind, zero, tcache, slow_path);\n\tif (config_stats && is_internal && likely(ret != NULL)) {\n\t\tarena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));\n\t}\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path) {\n\treturn iallocztm(tsd_tsdn(tsd), size, ind, zero, tcache_get(tsd), false,\n\t    NULL, slow_path);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,\n    tcache_t *tcache, bool is_internal, arena_t *arena) {\n\tvoid *ret;\n\n\tassert(usize != 0);\n\tassert(usize == sz_sa2u(usize, alignment));\n\tassert(!is_internal || tcache == NULL);\n\tassert(!is_internal || arena == NULL || arena_is_auto(arena));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tret = arena_palloc(tsdn, arena, usize, alignment, zero, tcache);\n\tassert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);\n\tif (config_stats && is_internal && likely(ret != NULL)) {\n\t\tarena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));\n\t}\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,\n    tcache_t *tcache, arena_t *arena) {\n\treturn ipallocztm(tsdn, usize, alignment, zero, tcache, false, arena);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero) {\n\treturn ipallocztm(tsd_tsdn(tsd), usize, alignment, zero,\n\t    tcache_get(tsd), false, NULL);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nivsalloc(tsdn_t *tsdn, const void *ptr) {\n\treturn arena_vsalloc(tsdn, ptr);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nidalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, alloc_ctx_t *alloc_ctx,\n    bool is_internal, bool slow_path) {\n\tassert(ptr != NULL);\n\tassert(!is_internal || tcache == NULL);\n\tassert(!is_internal || arena_is_auto(iaalloc(tsdn, ptr)));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\tif (config_stats && is_internal) {\n\t\tarena_internal_sub(iaalloc(tsdn, ptr), isalloc(tsdn, ptr));\n\t}\n\tif (!is_internal && !tsdn_null(tsdn) &&\n\t    tsd_reentrancy_level_get(tsdn_tsd(tsdn)) != 0) {\n\t\tassert(tcache == NULL);\n\t}\n\tarena_dalloc(tsdn, ptr, tcache, alloc_ctx, slow_path);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nidalloc(tsd_t *tsd, void *ptr) {\n\tidalloctm(tsd_tsdn(tsd), ptr, tcache_get(tsd), NULL, false, true);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nisdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,\n    alloc_ctx_t *alloc_ctx, bool slow_path) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\tarena_sdalloc(tsdn, ptr, size, tcache, alloc_ctx, slow_path);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,\n    size_t extra, size_t alignment, bool zero, tcache_t *tcache,\n    arena_t *arena) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\tvoid *p;\n\tsize_t usize, copysize;\n\n\tusize = sz_sa2u(size + extra, alignment);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\tp = ipalloct(tsdn, usize, alignment, zero, tcache, arena);\n\tif (p == NULL) {\n\t\tif (extra == 0) {\n\t\t\treturn NULL;\n\t\t}\n\t\t/* Try again, without extra this time. */\n\t\tusize = sz_sa2u(size, alignment);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\treturn NULL;\n\t\t}\n\t\tp = ipalloct(tsdn, usize, alignment, zero, tcache, arena);\n\t\tif (p == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t}\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\tmemcpy(p, ptr, copysize);\n\tisdalloct(tsdn, ptr, oldsize, tcache, NULL, true);\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,\n    bool zero, tcache_t *tcache, arena_t *arena) {\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))\n\t    != 0) {\n\t\t/*\n\t\t * Existing object alignment is inadequate; allocate new space\n\t\t * and copy.\n\t\t */\n\t\treturn iralloct_realign(tsdn, ptr, oldsize, size, 0, alignment,\n\t\t    zero, tcache, arena);\n\t}\n\n\treturn arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,\n\t    tcache);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\niralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,\n    bool zero) {\n\treturn iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,\n\t    tcache_get(tsd), NULL);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,\n    size_t alignment, bool zero) {\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))\n\t    != 0) {\n\t\t/* Existing object alignment is inadequate. */\n\t\treturn true;\n\t}\n\n\treturn arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero);\n}\n\nJEMALLOC_ALWAYS_INLINE int\niget_defrag_hint(tsdn_t *tsdn, void* ptr, int *bin_util, int *run_util) {\n\tint defrag = 0;\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\tszind_t szind;\n\tbool is_slab;\n\trtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr, true, &szind, &is_slab);\n\tif (likely(is_slab)) {\n\t\t/* Small allocation. */\n\t\textent_t *slab = iealloc(tsdn, ptr);\n\t\tarena_t *arena = extent_arena_get(slab);\n\t\tszind_t binind = extent_szind_get(slab);\n\t\tbin_t *bin = &arena->bins[binind];\n\t\tmalloc_mutex_lock(tsdn, &bin->lock);\n\t\t/* don't bother moving allocations from the slab currently used for new allocations */\n\t\tif (slab != bin->slabcur) {\n\t\t\tconst bin_info_t *bin_info = &bin_infos[binind];\n\t\t\tsize_t availregs = bin_info->nregs * bin->stats.curslabs;\n\t\t\t*bin_util = ((long long)bin->stats.curregs<<16) / availregs;\n\t\t\t*run_util = ((long long)(bin_info->nregs - extent_nfree_get(slab))<<16) / bin_info->nregs;\n\t\t\tdefrag = 1;\n\t\t}\n\t\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t}\n\treturn defrag;\n}\n\n#endif /* JEMALLOC_INTERNAL_INLINES_C_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MACROS_H\n#define JEMALLOC_INTERNAL_MACROS_H\n\n#ifdef JEMALLOC_DEBUG\n#  define JEMALLOC_ALWAYS_INLINE static inline\n#else\n#  define JEMALLOC_ALWAYS_INLINE JEMALLOC_ATTR(always_inline) static inline\n#endif\n#ifdef _MSC_VER\n#  define inline _inline\n#endif\n\n#define UNUSED JEMALLOC_ATTR(unused)\n\n#define ZU(z)\t((size_t)z)\n#define ZD(z)\t((ssize_t)z)\n#define QU(q)\t((uint64_t)q)\n#define QD(q)\t((int64_t)q)\n\n#define KZU(z)\tZU(z##ULL)\n#define KZD(z)\tZD(z##LL)\n#define KQU(q)\tQU(q##ULL)\n#define KQD(q)\tQI(q##LL)\n\n#ifndef __DECONST\n#  define\t__DECONST(type, var)\t((type)(uintptr_t)(const void *)(var))\n#endif\n\n#if !defined(JEMALLOC_HAS_RESTRICT) || defined(__cplusplus)\n#  define restrict\n#endif\n\n/* Various function pointers are statick and immutable except during testing. */\n#ifdef JEMALLOC_JET\n#  define JET_MUTABLE\n#else\n#  define JET_MUTABLE const\n#endif\n\n#define JEMALLOC_VA_ARGS_HEAD(head, ...) head\n#define JEMALLOC_VA_ARGS_TAIL(head, ...) __VA_ARGS__\n\n#endif /* JEMALLOC_INTERNAL_MACROS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TYPES_H\n#define JEMALLOC_INTERNAL_TYPES_H\n\n/* Page size index type. */\ntypedef unsigned pszind_t;\n\n/* Size class index type. */\ntypedef unsigned szind_t;\n\n/* Processor / core id type. */\ntypedef int malloc_cpuid_t;\n\n/*\n * Flags bits:\n *\n * a: arena\n * t: tcache\n * 0: unused\n * z: zero\n * n: alignment\n *\n * aaaaaaaa aaaatttt tttttttt 0znnnnnn\n */\n#define MALLOCX_ARENA_BITS\t12\n#define MALLOCX_TCACHE_BITS\t12\n#define MALLOCX_LG_ALIGN_BITS\t6\n#define MALLOCX_ARENA_SHIFT\t20\n#define MALLOCX_TCACHE_SHIFT\t8\n#define MALLOCX_ARENA_MASK \\\n    (((1 << MALLOCX_ARENA_BITS) - 1) << MALLOCX_ARENA_SHIFT)\n/* NB: Arena index bias decreases the maximum number of arenas by 1. */\n#define MALLOCX_ARENA_LIMIT\t((1 << MALLOCX_ARENA_BITS) - 1)\n#define MALLOCX_TCACHE_MASK \\\n    (((1 << MALLOCX_TCACHE_BITS) - 1) << MALLOCX_TCACHE_SHIFT)\n#define MALLOCX_TCACHE_MAX\t((1 << MALLOCX_TCACHE_BITS) - 3)\n#define MALLOCX_LG_ALIGN_MASK\t((1 << MALLOCX_LG_ALIGN_BITS) - 1)\n/* Use MALLOCX_ALIGN_GET() if alignment may not be specified in flags. */\n#define MALLOCX_ALIGN_GET_SPECIFIED(flags)\t\t\t\t\\\n    (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK))\n#define MALLOCX_ALIGN_GET(flags)\t\t\t\t\t\\\n    (MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX-1))\n#define MALLOCX_ZERO_GET(flags)\t\t\t\t\t\t\\\n    ((bool)(flags & MALLOCX_ZERO))\n\n#define MALLOCX_TCACHE_GET(flags)\t\t\t\t\t\\\n    (((unsigned)((flags & MALLOCX_TCACHE_MASK) >> MALLOCX_TCACHE_SHIFT)) - 2)\n#define MALLOCX_ARENA_GET(flags)\t\t\t\t\t\\\n    (((unsigned)(((unsigned)flags) >> MALLOCX_ARENA_SHIFT)) - 1)\n\n/* Smallest size class to support. */\n#define TINY_MIN\t\t(1U << LG_TINY_MIN)\n\n/*\n * Minimum allocation alignment 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#  if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))\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 __aarch64__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __hppa__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __m68k__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __mips__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __nios2__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __or1k__\n#    define LG_QUANTUM\t\t3\n#  endif\n#  ifdef __powerpc__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  if defined(__riscv) || defined(__riscv__)\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __s390__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  if (defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || \\\n\tdefined(__SH4_SINGLE_ONLY__))\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __tile__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifdef __le32__\n#    define LG_QUANTUM\t\t4\n#  endif\n#  ifndef LG_QUANTUM\n#    error \"Unknown minimum alignment for architecture; specify via \"\n\t \"--with-lg-quantum\"\n#  endif\n#endif\n\n#define QUANTUM\t\t\t((size_t)(1U << LG_QUANTUM))\n#define QUANTUM_MASK\t\t(QUANTUM - 1)\n\n/* Return the smallest quantum multiple that is >= a. */\n#define QUANTUM_CEILING(a)\t\t\t\t\t\t\\\n\t(((a) + QUANTUM_MASK) & ~QUANTUM_MASK)\n\n#define LONG\t\t\t((size_t)(1U << LG_SIZEOF_LONG))\n#define LONG_MASK\t\t(LONG - 1)\n\n/* Return the smallest long multiple that is >= a. */\n#define LONG_CEILING(a)\t\t\t\t\t\t\t\\\n\t(((a) + LONG_MASK) & ~LONG_MASK)\n\n#define SIZEOF_PTR\t\t(1U << LG_SIZEOF_PTR)\n#define PTR_MASK\t\t(SIZEOF_PTR - 1)\n\n/* Return the smallest (void *) multiple that is >= a. */\n#define PTR_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 LG_CACHELINE\t\t6\n#define CACHELINE\t\t64\n#define CACHELINE_MASK\t\t(CACHELINE - 1)\n\n/* Return the smallest cacheline multiple that is >= s. */\n#define CACHELINE_CEILING(s)\t\t\t\t\t\t\\\n\t(((s) + CACHELINE_MASK) & ~CACHELINE_MASK)\n\n/* Return the nearest aligned address at or below a. */\n#define ALIGNMENT_ADDR2BASE(a, alignment)\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ((~(alignment)) + 1)))\n\n/* Return the offset between a and the nearest aligned address at or below a. */\n#define ALIGNMENT_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 ALIGNMENT_CEILING(s, alignment)\t\t\t\t\t\\\n\t(((s) + (alignment - 1)) & ((~(alignment)) + 1))\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#    ifdef JEMALLOC_HAS_ALLOCA_H\n#      include <alloca.h>\n#    else\n#      include <stdlib.h>\n#    endif\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#endif /* JEMALLOC_INTERNAL_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in",
    "content": "#ifndef JEMALLOC_PREAMBLE_H\n#define JEMALLOC_PREAMBLE_H\n\n#include \"jemalloc_internal_defs.h\"\n#include \"jemalloc/internal/jemalloc_internal_decls.h\"\n\n#ifdef JEMALLOC_UTRACE\n#include <sys/ktrace.h>\n#endif\n\n#define JEMALLOC_NO_DEMANGLE\n#ifdef JEMALLOC_JET\n#  undef JEMALLOC_IS_MALLOC\n#  define JEMALLOC_N(n) jet_##n\n#  include \"jemalloc/internal/public_namespace.h\"\n#  define JEMALLOC_NO_RENAME\n#  include \"../jemalloc@install_suffix@.h\"\n#  undef JEMALLOC_NO_RENAME\n#else\n#  define JEMALLOC_N(n) @private_namespace@##n\n#  include \"../jemalloc@install_suffix@.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#endif\n\n#include \"jemalloc/internal/jemalloc_internal_macros.h\"\n\n/*\n * Note that the ordering matters here; the hook itself is name-mangled.  We\n * want the inclusion of hooks to happen early, so that we hook as much as\n * possible.\n */\n#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE\n#  ifndef JEMALLOC_JET\n#    include \"jemalloc/internal/private_namespace.h\"\n#  else\n#    include \"jemalloc/internal/private_namespace_jet.h\"\n#  endif\n#endif\n#include \"jemalloc/internal/hooks.h\"\n\n#ifdef JEMALLOC_DEFINE_MADVISE_FREE\n#  define JEMALLOC_MADV_FREE 8\n#endif\n\nstatic const bool config_debug =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool have_dss =\n#ifdef JEMALLOC_DSS\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool have_madvise_huge =\n#ifdef JEMALLOC_HAVE_MADVISE_HUGE\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 char * const config_malloc_conf = JEMALLOC_CONFIG_MALLOC_CONF;\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 maps_coalesce =\n#ifdef JEMALLOC_MAPS_COALESCE\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_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_xmalloc =\n#ifdef JEMALLOC_XMALLOC\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool config_cache_oblivious =\n#ifdef JEMALLOC_CACHE_OBLIVIOUS\n    true\n#else\n    false\n#endif\n    ;\n/*\n * Undocumented, for jemalloc development use only at the moment.  See the note\n * in jemalloc/internal/log.h.\n */\nstatic const bool config_log =\n#ifdef JEMALLOC_LOG\n    true\n#else\n    false\n#endif\n    ;\n#ifdef JEMALLOC_HAVE_SCHED_GETCPU\n/* Currently percpu_arena depends on sched_getcpu. */\n#define JEMALLOC_PERCPU_ARENA\n#endif\nstatic const bool have_percpu_arena =\n#ifdef JEMALLOC_PERCPU_ARENA\n    true\n#else\n    false\n#endif\n    ;\n/*\n * Undocumented, and not recommended; the application should take full\n * responsibility for tracking provenance.\n */\nstatic const bool force_ivsalloc =\n#ifdef JEMALLOC_FORCE_IVSALLOC\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool have_background_thread =\n#ifdef JEMALLOC_BACKGROUND_THREAD\n    true\n#else\n    false\n#endif\n    ;\n\n#endif /* JEMALLOC_PREAMBLE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/large_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_LARGE_EXTERNS_H\n#define JEMALLOC_INTERNAL_LARGE_EXTERNS_H\n\nvoid *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);\nvoid *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,\n    bool zero);\nbool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,\n    size_t usize_max, bool zero);\nvoid *large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache);\n\ntypedef void (large_dalloc_junk_t)(void *, size_t);\nextern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;\n\ntypedef void (large_dalloc_maybe_junk_t)(void *, size_t);\nextern large_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk;\n\nvoid large_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent);\nvoid large_dalloc_finish(tsdn_t *tsdn, extent_t *extent);\nvoid large_dalloc(tsdn_t *tsdn, extent_t *extent);\nsize_t large_salloc(tsdn_t *tsdn, const extent_t *extent);\nprof_tctx_t *large_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent);\nvoid large_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx);\nvoid large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent);\n\n#endif /* JEMALLOC_INTERNAL_LARGE_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/log.h",
    "content": "#ifndef JEMALLOC_INTERNAL_LOG_H\n#define JEMALLOC_INTERNAL_LOG_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex.h\"\n\n#ifdef JEMALLOC_LOG\n#  define JEMALLOC_LOG_VAR_BUFSIZE 1000\n#else\n#  define JEMALLOC_LOG_VAR_BUFSIZE 1\n#endif\n\n#define JEMALLOC_LOG_BUFSIZE 4096\n\n/*\n * The log malloc_conf option is a '|'-delimited list of log_var name segments\n * which should be logged.  The names are themselves hierarchical, with '.' as\n * the delimiter (a \"segment\" is just a prefix in the log namespace).  So, if\n * you have:\n *\n * log(\"arena\", \"log msg for arena\"); // 1\n * log(\"arena.a\", \"log msg for arena.a\"); // 2\n * log(\"arena.b\", \"log msg for arena.b\"); // 3\n * log(\"arena.a.a\", \"log msg for arena.a.a\"); // 4\n * log(\"extent.a\", \"log msg for extent.a\"); // 5\n * log(\"extent.b\", \"log msg for extent.b\"); // 6\n *\n * And your malloc_conf option is \"log=arena.a|extent\", then lines 2, 4, 5, and\n * 6 will print at runtime.  You can enable logging from all log vars by\n * writing \"log=.\".\n *\n * None of this should be regarded as a stable API for right now.  It's intended\n * as a debugging interface, to let us keep around some of our printf-debugging\n * statements.\n */\n\nextern char log_var_names[JEMALLOC_LOG_VAR_BUFSIZE];\nextern atomic_b_t log_init_done;\n\ntypedef struct log_var_s log_var_t;\nstruct log_var_s {\n\t/*\n\t * Lowest bit is \"inited\", second lowest is \"enabled\".  Putting them in\n\t * a single word lets us avoid any fences on weak architectures.\n\t */\n\tatomic_u_t state;\n\tconst char *name;\n};\n\n#define LOG_NOT_INITIALIZED 0U\n#define LOG_INITIALIZED_NOT_ENABLED 1U\n#define LOG_ENABLED 2U\n\n#define LOG_VAR_INIT(name_str) {ATOMIC_INIT(LOG_NOT_INITIALIZED), name_str}\n\n/*\n * Returns the value we should assume for state (which is not necessarily\n * accurate; if logging is done before logging has finished initializing, then\n * we default to doing the safe thing by logging everything).\n */\nunsigned log_var_update_state(log_var_t *log_var);\n\n/* We factor out the metadata management to allow us to test more easily. */\n#define log_do_begin(log_var)\t\t\t\t\t\t\\\nif (config_log) {\t\t\t\t\t\t\t\\\n\tunsigned log_state = atomic_load_u(&(log_var).state,\t\t\\\n\t    ATOMIC_RELAXED);\t\t\t\t\t\t\\\n\tif (unlikely(log_state == LOG_NOT_INITIALIZED)) {\t\t\\\n\t\tlog_state = log_var_update_state(&(log_var));\t\t\\\n\t\tassert(log_state != LOG_NOT_INITIALIZED);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif (log_state == LOG_ENABLED) {\t\t\t\t\t\\\n\t\t{\n\t\t\t/* User code executes here. */\n#define log_do_end(log_var)\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}\n\n/*\n * MSVC has some preprocessor bugs in its expansion of __VA_ARGS__ during\n * preprocessing.  To work around this, we take all potential extra arguments in\n * a var-args functions.  Since a varargs macro needs at least one argument in\n * the \"...\", we accept the format string there, and require that the first\n * argument in this \"...\" is a const char *.\n */\nstatic inline void\nlog_impl_varargs(const char *name, ...) {\n\tchar buf[JEMALLOC_LOG_BUFSIZE];\n\tva_list ap;\n\n\tva_start(ap, name);\n\tconst char *format = va_arg(ap, const char *);\n\tsize_t dst_offset = 0;\n\tdst_offset += malloc_snprintf(buf, JEMALLOC_LOG_BUFSIZE, \"%s: \", name);\n\tdst_offset += malloc_vsnprintf(buf + dst_offset,\n\t    JEMALLOC_LOG_BUFSIZE - dst_offset, format, ap);\n\tdst_offset += malloc_snprintf(buf + dst_offset,\n\t    JEMALLOC_LOG_BUFSIZE - dst_offset, \"\\n\");\n\tva_end(ap);\n\n\tmalloc_write(buf);\n}\n\n/* Call as log(\"log.var.str\", \"format_string %d\", arg_for_format_string); */\n#define LOG(log_var_str, ...)\t\t\t\t\t\t\\\ndo {\t\t\t\t\t\t\t\t\t\\\n\tstatic log_var_t log_var = LOG_VAR_INIT(log_var_str);\t\t\\\n\tlog_do_begin(log_var)\t\t\t\t\t\t\\\n\t\tlog_impl_varargs((log_var).name, __VA_ARGS__);\t\t\\\n\tlog_do_end(log_var)\t\t\t\t\t\t\\\n} while (0)\n\n#endif /* JEMALLOC_INTERNAL_LOG_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/malloc_io.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MALLOC_IO_H\n#define JEMALLOC_INTERNAL_MALLOC_IO_H\n\n#ifdef _WIN32\n#  ifdef _WIN64\n#    define FMT64_PREFIX \"ll\"\n#    define FMTPTR_PREFIX \"ll\"\n#  else\n#    define FMT64_PREFIX \"ll\"\n#    define FMTPTR_PREFIX \"\"\n#  endif\n#  define FMTd32 \"d\"\n#  define FMTu32 \"u\"\n#  define FMTx32 \"x\"\n#  define FMTd64 FMT64_PREFIX \"d\"\n#  define FMTu64 FMT64_PREFIX \"u\"\n#  define FMTx64 FMT64_PREFIX \"x\"\n#  define FMTdPTR FMTPTR_PREFIX \"d\"\n#  define FMTuPTR FMTPTR_PREFIX \"u\"\n#  define FMTxPTR FMTPTR_PREFIX \"x\"\n#else\n#  include <inttypes.h>\n#  define FMTd32 PRId32\n#  define FMTu32 PRIu32\n#  define FMTx32 PRIx32\n#  define FMTd64 PRId64\n#  define FMTu64 PRIu64\n#  define FMTx64 PRIx64\n#  define FMTdPTR PRIdPTR\n#  define FMTuPTR PRIuPTR\n#  define FMTxPTR PRIxPTR\n#endif\n\n/* Size of stack-allocated buffer passed to buferror(). */\n#define BUFERROR_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 MALLOC_PRINTF_BUFSIZE\t4096\n\nint buferror(int err, char *buf, size_t buflen);\nuintmax_t malloc_strtoumax(const char *restrict nptr, char **restrict endptr,\n    int base);\nvoid malloc_write(const char *s);\n\n/*\n * malloc_vsnprintf() supports a subset of snprintf(3) that avoids floating\n * point math.\n */\nsize_t malloc_vsnprintf(char *str, size_t size, const char *format,\n    va_list ap);\nsize_t malloc_snprintf(char *str, size_t size, const char *format, ...)\n    JEMALLOC_FORMAT_PRINTF(3, 4);\n/*\n * The caller can set write_cb and cbopaque to null to choose to print with the\n * je_malloc_message hook.\n */\nvoid malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, va_list ap);\nvoid malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, ...) JEMALLOC_FORMAT_PRINTF(3, 4);\nvoid malloc_printf(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);\n\nstatic inline ssize_t\nmalloc_write_fd(int fd, const void *buf, size_t count) {\n#if defined(JEMALLOC_USE_SYSCALL) && defined(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\t * syscall() returns long or int, depending on platform, so capture the\n\t * result in the widest plausible type to avoid compiler warnings.\n\t */\n\tlong result = syscall(SYS_write, fd, buf, count);\n#else\n\tssize_t result = (ssize_t)write(fd, buf,\n#ifdef _WIN32\n\t    (unsigned int)\n#endif\n\t    count);\n#endif\n\treturn (ssize_t)result;\n}\n\nstatic inline ssize_t\nmalloc_read_fd(int fd, void *buf, size_t count) {\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_read)\n\tlong result = syscall(SYS_read, fd, buf, count);\n#else\n\tssize_t result = read(fd, buf,\n#ifdef _WIN32\n\t    (unsigned int)\n#endif\n\t    count);\n#endif\n\treturn (ssize_t)result;\n}\n\n#endif /* JEMALLOC_INTERNAL_MALLOC_IO_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/mutex.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MUTEX_H\n#define JEMALLOC_INTERNAL_MUTEX_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n#include \"jemalloc/internal/tsd.h\"\n#include \"jemalloc/internal/witness.h\"\n\ntypedef enum {\n\t/* Can only acquire one mutex of a given witness rank at a time. */\n\tmalloc_mutex_rank_exclusive,\n\t/*\n\t * Can acquire multiple mutexes of the same witness rank, but in\n\t * address-ascending order only.\n\t */\n\tmalloc_mutex_address_ordered\n} malloc_mutex_lock_order_t;\n\ntypedef struct malloc_mutex_s malloc_mutex_t;\nstruct malloc_mutex_s {\n\tunion {\n\t\tstruct {\n\t\t\t/*\n\t\t\t * prof_data is defined first to reduce cacheline\n\t\t\t * bouncing: the data is not touched by the mutex holder\n\t\t\t * during unlocking, while might be modified by\n\t\t\t * contenders.  Having it before the mutex itself could\n\t\t\t * avoid prefetching a modified cacheline (for the\n\t\t\t * unlocking thread).\n\t\t\t */\n\t\t\tmutex_prof_data_t\tprof_data;\n#ifdef _WIN32\n#  if _WIN32_WINNT >= 0x0600\n\t\t\tSRWLOCK         \tlock;\n#  else\n\t\t\tCRITICAL_SECTION\tlock;\n#  endif\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\t\t\tos_unfair_lock\t\tlock;\n#elif (defined(JEMALLOC_OSSPIN))\n\t\t\tOSSpinLock\t\tlock;\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n\t\t\tpthread_mutex_t\t\tlock;\n\t\t\tmalloc_mutex_t\t\t*postponed_next;\n#else\n\t\t\tpthread_mutex_t\t\tlock;\n#endif\n\t\t};\n\t\t/*\n\t\t * We only touch witness when configured w/ debug.  However we\n\t\t * keep the field in a union when !debug so that we don't have\n\t\t * to pollute the code base with #ifdefs, while avoid paying the\n\t\t * memory cost.\n\t\t */\n#if !defined(JEMALLOC_DEBUG)\n\t\twitness_t\t\t\twitness;\n\t\tmalloc_mutex_lock_order_t\tlock_order;\n#endif\n\t};\n\n#if defined(JEMALLOC_DEBUG)\n\twitness_t\t\t\twitness;\n\tmalloc_mutex_lock_order_t\tlock_order;\n#endif\n};\n\n/*\n * Based on benchmark results, a fixed spin with this amount of retries works\n * well for our critical sections.\n */\n#define MALLOC_MUTEX_MAX_SPIN 250\n\n#ifdef _WIN32\n#  if _WIN32_WINNT >= 0x0600\n#    define MALLOC_MUTEX_LOCK(m)    AcquireSRWLockExclusive(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  ReleaseSRWLockExclusive(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!TryAcquireSRWLockExclusive(&(m)->lock))\n#  else\n#    define MALLOC_MUTEX_LOCK(m)    EnterCriticalSection(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  LeaveCriticalSection(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!TryEnterCriticalSection(&(m)->lock))\n#  endif\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n#    define MALLOC_MUTEX_LOCK(m)    os_unfair_lock_lock(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  os_unfair_lock_unlock(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!os_unfair_lock_trylock(&(m)->lock))\n#elif (defined(JEMALLOC_OSSPIN))\n#    define MALLOC_MUTEX_LOCK(m)    OSSpinLockLock(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  OSSpinLockUnlock(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (!OSSpinLockTry(&(m)->lock))\n#else\n#    define MALLOC_MUTEX_LOCK(m)    pthread_mutex_lock(&(m)->lock)\n#    define MALLOC_MUTEX_UNLOCK(m)  pthread_mutex_unlock(&(m)->lock)\n#    define MALLOC_MUTEX_TRYLOCK(m) (pthread_mutex_trylock(&(m)->lock) != 0)\n#endif\n\n#define LOCK_PROF_DATA_INITIALIZER\t\t\t\t\t\\\n    {NSTIME_ZERO_INITIALIZER, NSTIME_ZERO_INITIALIZER, 0, 0, 0,\t\t\\\n\t    ATOMIC_INIT(0), 0, NULL, 0}\n\n#ifdef _WIN32\n#  define MALLOC_MUTEX_INITIALIZER\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n#  define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n     {{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT}},\t\t\\\n      WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#elif (defined(JEMALLOC_OSSPIN))\n#  define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n     {{{LOCK_PROF_DATA_INITIALIZER, 0}},\t\t\t\t\\\n      WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#elif (defined(JEMALLOC_MUTEX_INIT_CB))\n#  define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n     {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL}},\t\\\n      WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#else\n#    define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT\n#    define MALLOC_MUTEX_INITIALIZER\t\t\t\t\t\\\n       {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}},\t\\\n        WITNESS_INITIALIZER(\"mutex\", WITNESS_RANK_OMIT)}\n#endif\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 malloc_mutex_init(malloc_mutex_t *mutex, const char *name,\n    witness_rank_t rank, malloc_mutex_lock_order_t lock_order);\nvoid malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex);\nvoid malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex);\nvoid malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex);\nbool malloc_mutex_boot(void);\nvoid malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex);\n\nvoid malloc_mutex_lock_slow(malloc_mutex_t *mutex);\n\nstatic inline void\nmalloc_mutex_lock_final(malloc_mutex_t *mutex) {\n\tMALLOC_MUTEX_LOCK(mutex);\n}\n\nstatic inline bool\nmalloc_mutex_trylock_final(malloc_mutex_t *mutex) {\n\treturn MALLOC_MUTEX_TRYLOCK(mutex);\n}\n\nstatic inline void\nmutex_owner_stats_update(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tif (config_stats) {\n\t\tmutex_prof_data_t *data = &mutex->prof_data;\n\t\tdata->n_lock_ops++;\n\t\tif (data->prev_owner != tsdn) {\n\t\t\tdata->prev_owner = tsdn;\n\t\t\tdata->n_owner_switches++;\n\t\t}\n\t}\n}\n\n/* Trylock: return false if the lock is successfully acquired. */\nstatic inline bool\nmalloc_mutex_trylock(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\tif (isthreaded) {\n\t\tif (malloc_mutex_trylock_final(mutex)) {\n\t\t\treturn true;\n\t\t}\n\t\tmutex_owner_stats_update(tsdn, mutex);\n\t}\n\twitness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\n\treturn false;\n}\n\n/* Aggregate lock prof data. */\nstatic inline void\nmalloc_mutex_prof_merge(mutex_prof_data_t *sum, mutex_prof_data_t *data) {\n\tnstime_add(&sum->tot_wait_time, &data->tot_wait_time);\n\tif (nstime_compare(&sum->max_wait_time, &data->max_wait_time) < 0) {\n\t\tnstime_copy(&sum->max_wait_time, &data->max_wait_time);\n\t}\n\n\tsum->n_wait_times += data->n_wait_times;\n\tsum->n_spin_acquired += data->n_spin_acquired;\n\n\tif (sum->max_n_thds < data->max_n_thds) {\n\t\tsum->max_n_thds = data->max_n_thds;\n\t}\n\tuint32_t cur_n_waiting_thds = atomic_load_u32(&sum->n_waiting_thds,\n\t    ATOMIC_RELAXED);\n\tuint32_t new_n_waiting_thds = cur_n_waiting_thds + atomic_load_u32(\n\t    &data->n_waiting_thds, ATOMIC_RELAXED);\n\tatomic_store_u32(&sum->n_waiting_thds, new_n_waiting_thds,\n\t    ATOMIC_RELAXED);\n\tsum->n_owner_switches += data->n_owner_switches;\n\tsum->n_lock_ops += data->n_lock_ops;\n}\n\nstatic inline void\nmalloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\tif (isthreaded) {\n\t\tif (malloc_mutex_trylock_final(mutex)) {\n\t\t\tmalloc_mutex_lock_slow(mutex);\n\t\t}\n\t\tmutex_owner_stats_update(tsdn, mutex);\n\t}\n\twitness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n}\n\nstatic inline void\nmalloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_unlock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n\tif (isthreaded) {\n\t\tMALLOC_MUTEX_UNLOCK(mutex);\n\t}\n}\n\nstatic inline void\nmalloc_mutex_assert_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n}\n\nstatic inline void\nmalloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\twitness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);\n}\n\n/* Copy the prof data from mutex for processing. */\nstatic inline void\nmalloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,\n    malloc_mutex_t *mutex) {\n\tmutex_prof_data_t *source = &mutex->prof_data;\n\t/* Can only read holding the mutex. */\n\tmalloc_mutex_assert_owner(tsdn, mutex);\n\n\t/*\n\t * Not *really* allowed (we shouldn't be doing non-atomic loads of\n\t * atomic data), but the mutex protection makes this safe, and writing\n\t * a member-for-member copy is tedious for this situation.\n\t */\n\t*data = *source;\n\t/* n_wait_thds is not reported (modified w/o locking). */\n\tatomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);\n}\n\n#endif /* JEMALLOC_INTERNAL_MUTEX_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/mutex_pool.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MUTEX_POOL_H\n#define JEMALLOC_INTERNAL_MUTEX_POOL_H\n\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/witness.h\"\n\n/* We do mod reductions by this value, so it should be kept a power of 2. */\n#define MUTEX_POOL_SIZE 256\n\ntypedef struct mutex_pool_s mutex_pool_t;\nstruct mutex_pool_s {\n\tmalloc_mutex_t mutexes[MUTEX_POOL_SIZE];\n};\n\nbool mutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank);\n\n/* Internal helper - not meant to be called outside this module. */\nstatic inline malloc_mutex_t *\nmutex_pool_mutex(mutex_pool_t *pool, uintptr_t key) {\n\tsize_t hash_result[2];\n\thash(&key, sizeof(key), 0xd50dcc1b, hash_result);\n\treturn &pool->mutexes[hash_result[0] % MUTEX_POOL_SIZE];\n}\n\nstatic inline void\nmutex_pool_assert_not_held(tsdn_t *tsdn, mutex_pool_t *pool) {\n\tfor (int i = 0; i < MUTEX_POOL_SIZE; i++) {\n\t\tmalloc_mutex_assert_not_owner(tsdn, &pool->mutexes[i]);\n\t}\n}\n\n/*\n * Note that a mutex pool doesn't work exactly the way an embdedded mutex would.\n * You're not allowed to acquire mutexes in the pool one at a time.  You have to\n * acquire all the mutexes you'll need in a single function call, and then\n * release them all in a single function call.\n */\n\nstatic inline void\nmutex_pool_lock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {\n\tmutex_pool_assert_not_held(tsdn, pool);\n\n\tmalloc_mutex_t *mutex = mutex_pool_mutex(pool, key);\n\tmalloc_mutex_lock(tsdn, mutex);\n}\n\nstatic inline void\nmutex_pool_unlock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {\n\tmalloc_mutex_t *mutex = mutex_pool_mutex(pool, key);\n\tmalloc_mutex_unlock(tsdn, mutex);\n\n\tmutex_pool_assert_not_held(tsdn, pool);\n}\n\nstatic inline void\nmutex_pool_lock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,\n    uintptr_t key2) {\n\tmutex_pool_assert_not_held(tsdn, pool);\n\n\tmalloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);\n\tmalloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);\n\tif ((uintptr_t)mutex1 < (uintptr_t)mutex2) {\n\t\tmalloc_mutex_lock(tsdn, mutex1);\n\t\tmalloc_mutex_lock(tsdn, mutex2);\n\t} else if ((uintptr_t)mutex1 == (uintptr_t)mutex2) {\n\t\tmalloc_mutex_lock(tsdn, mutex1);\n\t} else {\n\t\tmalloc_mutex_lock(tsdn, mutex2);\n\t\tmalloc_mutex_lock(tsdn, mutex1);\n\t}\n}\n\nstatic inline void\nmutex_pool_unlock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,\n    uintptr_t key2) {\n\tmalloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);\n\tmalloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);\n\tif (mutex1 == mutex2) {\n\t\tmalloc_mutex_unlock(tsdn, mutex1);\n\t} else {\n\t\tmalloc_mutex_unlock(tsdn, mutex1);\n\t\tmalloc_mutex_unlock(tsdn, mutex2);\n\t}\n\n\tmutex_pool_assert_not_held(tsdn, pool);\n}\n\nstatic inline void\nmutex_pool_assert_owner(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {\n\tmalloc_mutex_assert_owner(tsdn, mutex_pool_mutex(pool, key));\n}\n\n#endif /* JEMALLOC_INTERNAL_MUTEX_POOL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/mutex_prof.h",
    "content": "#ifndef JEMALLOC_INTERNAL_MUTEX_PROF_H\n#define JEMALLOC_INTERNAL_MUTEX_PROF_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/nstime.h\"\n#include \"jemalloc/internal/tsd_types.h\"\n\n#define MUTEX_PROF_GLOBAL_MUTEXES\t\t\t\t\t\\\n    OP(background_thread)\t\t\t\t\t\t\\\n    OP(ctl)\t\t\t\t\t\t\t\t\\\n    OP(prof)\n\ntypedef enum {\n#define OP(mtx) global_prof_mutex_##mtx,\n\tMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\tmutex_prof_num_global_mutexes\n} mutex_prof_global_ind_t;\n\n#define MUTEX_PROF_ARENA_MUTEXES\t\t\t\t\t\\\n    OP(large)\t\t\t\t\t\t\t\t\\\n    OP(extent_avail)\t\t\t\t\t\t\t\\\n    OP(extents_dirty)\t\t\t\t\t\t\t\\\n    OP(extents_muzzy)\t\t\t\t\t\t\t\\\n    OP(extents_retained)\t\t\t\t\t\t\\\n    OP(decay_dirty)\t\t\t\t\t\t\t\\\n    OP(decay_muzzy)\t\t\t\t\t\t\t\\\n    OP(base)\t\t\t\t\t\t\t\t\\\n    OP(tcache_list)\n\ntypedef enum {\n#define OP(mtx) arena_prof_mutex_##mtx,\n\tMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\tmutex_prof_num_arena_mutexes\n} mutex_prof_arena_ind_t;\n\n#define MUTEX_PROF_UINT64_COUNTERS\t\t\t\t\t\\\n    OP(num_ops, uint64_t, \"n_lock_ops\")\t\t\t\t\t\\\n    OP(num_wait, uint64_t, \"n_waiting\")\t\t\t\t\t\\\n    OP(num_spin_acq, uint64_t, \"n_spin_acq\")\t\t\t\t\\\n    OP(num_owner_switch, uint64_t, \"n_owner_switch\")\t\t\t\\\n    OP(total_wait_time, uint64_t, \"total_wait_ns\")\t\t\t\\\n    OP(max_wait_time, uint64_t, \"max_wait_ns\")\n\n#define MUTEX_PROF_UINT32_COUNTERS\t\t\t\t\t\\\n    OP(max_num_thds, uint32_t, \"max_n_thds\")\n\n#define MUTEX_PROF_COUNTERS\t\t\t\t\t\t\\\n\t\tMUTEX_PROF_UINT64_COUNTERS\t\t\t\t\\\n\t\tMUTEX_PROF_UINT32_COUNTERS\n\n#define OP(counter, type, human) mutex_counter_##counter,\n\n#define COUNTER_ENUM(counter_list, t)\t\t\t\t\t\\\n\t\ttypedef enum {\t\t\t\t\t\t\\\n\t\t\tcounter_list\t\t\t\t\t\\\n\t\t\tmutex_prof_num_##t##_counters\t\t\t\\\n\t\t} mutex_prof_##t##_counter_ind_t;\n\nCOUNTER_ENUM(MUTEX_PROF_UINT64_COUNTERS, uint64_t)\nCOUNTER_ENUM(MUTEX_PROF_UINT32_COUNTERS, uint32_t)\n\n#undef COUNTER_ENUM\n#undef OP\n\ntypedef struct {\n\t/*\n\t * Counters touched on the slow path, i.e. when there is lock\n\t * contention.  We update them once we have the lock.\n\t */\n\t/* Total time (in nano seconds) spent waiting on this mutex. */\n\tnstime_t\t\ttot_wait_time;\n\t/* Max time (in nano seconds) spent on a single lock operation. */\n\tnstime_t\t\tmax_wait_time;\n\t/* # of times have to wait for this mutex (after spinning). */\n\tuint64_t\t\tn_wait_times;\n\t/* # of times acquired the mutex through local spinning. */\n\tuint64_t\t\tn_spin_acquired;\n\t/* Max # of threads waiting for the mutex at the same time. */\n\tuint32_t\t\tmax_n_thds;\n\t/* Current # of threads waiting on the lock.  Atomic synced. */\n\tatomic_u32_t\t\tn_waiting_thds;\n\n\t/*\n\t * Data touched on the fast path.  These are modified right after we\n\t * grab the lock, so it's placed closest to the end (i.e. right before\n\t * the lock) so that we have a higher chance of them being on the same\n\t * cacheline.\n\t */\n\t/* # of times the mutex holder is different than the previous one. */\n\tuint64_t\t\tn_owner_switches;\n\t/* Previous mutex holder, to facilitate n_owner_switches. */\n\ttsdn_t\t\t\t*prev_owner;\n\t/* # of lock() operations in total. */\n\tuint64_t\t\tn_lock_ops;\n} mutex_prof_data_t;\n\n#endif /* JEMALLOC_INTERNAL_MUTEX_PROF_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/nstime.h",
    "content": "#ifndef JEMALLOC_INTERNAL_NSTIME_H\n#define JEMALLOC_INTERNAL_NSTIME_H\n\n/* Maximum supported number of seconds (~584 years). */\n#define NSTIME_SEC_MAX KQU(18446744072)\n#define NSTIME_ZERO_INITIALIZER {0}\n\ntypedef struct {\n\tuint64_t ns;\n} nstime_t;\n\nvoid nstime_init(nstime_t *time, uint64_t ns);\nvoid nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec);\nuint64_t nstime_ns(const nstime_t *time);\nuint64_t nstime_sec(const nstime_t *time);\nuint64_t nstime_msec(const nstime_t *time);\nuint64_t nstime_nsec(const nstime_t *time);\nvoid nstime_copy(nstime_t *time, const nstime_t *source);\nint nstime_compare(const nstime_t *a, const nstime_t *b);\nvoid nstime_add(nstime_t *time, const nstime_t *addend);\nvoid nstime_iadd(nstime_t *time, uint64_t addend);\nvoid nstime_subtract(nstime_t *time, const nstime_t *subtrahend);\nvoid nstime_isubtract(nstime_t *time, uint64_t subtrahend);\nvoid nstime_imultiply(nstime_t *time, uint64_t multiplier);\nvoid nstime_idivide(nstime_t *time, uint64_t divisor);\nuint64_t nstime_divide(const nstime_t *time, const nstime_t *divisor);\n\ntypedef bool (nstime_monotonic_t)(void);\nextern nstime_monotonic_t *JET_MUTABLE nstime_monotonic;\n\ntypedef bool (nstime_update_t)(nstime_t *);\nextern nstime_update_t *JET_MUTABLE nstime_update;\n\n#endif /* JEMALLOC_INTERNAL_NSTIME_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/pages.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PAGES_EXTERNS_H\n#define JEMALLOC_INTERNAL_PAGES_EXTERNS_H\n\n/* Page size.  LG_PAGE is determined by the configure script. */\n#ifdef PAGE_MASK\n#  undef PAGE_MASK\n#endif\n#define PAGE\t\t((size_t)(1U << LG_PAGE))\n#define PAGE_MASK\t((size_t)(PAGE - 1))\n/* Return the page base address for the page containing address a. */\n#define PAGE_ADDR2BASE(a)\t\t\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ~PAGE_MASK))\n/* Return the smallest pagesize multiple that is >= s. */\n#define PAGE_CEILING(s)\t\t\t\t\t\t\t\\\n\t(((s) + PAGE_MASK) & ~PAGE_MASK)\n\n/* Huge page size.  LG_HUGEPAGE is determined by the configure script. */\n#define HUGEPAGE\t((size_t)(1U << LG_HUGEPAGE))\n#define HUGEPAGE_MASK\t((size_t)(HUGEPAGE - 1))\n/* Return the huge page base address for the huge page containing address a. */\n#define HUGEPAGE_ADDR2BASE(a)\t\t\t\t\t\t\\\n\t((void *)((uintptr_t)(a) & ~HUGEPAGE_MASK))\n/* Return the smallest pagesize multiple that is >= s. */\n#define HUGEPAGE_CEILING(s)\t\t\t\t\t\t\\\n\t(((s) + HUGEPAGE_MASK) & ~HUGEPAGE_MASK)\n\n/* PAGES_CAN_PURGE_LAZY is defined if lazy purging is supported. */\n#if defined(_WIN32) || defined(JEMALLOC_PURGE_MADVISE_FREE)\n#  define PAGES_CAN_PURGE_LAZY\n#endif\n/*\n * PAGES_CAN_PURGE_FORCED is defined if forced purging is supported.\n *\n * The only supported way to hard-purge on Windows is to decommit and then\n * re-commit, but doing so is racy, and if re-commit fails it's a pain to\n * propagate the \"poisoned\" memory state.  Since we typically decommit as the\n * next step after purging on Windows anyway, there's no point in adding such\n * complexity.\n */\n#if !defined(_WIN32) && ((defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \\\n    defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)) || \\\n    defined(JEMALLOC_MAPS_COALESCE))\n#  define PAGES_CAN_PURGE_FORCED\n#endif\n\nstatic const bool pages_can_purge_lazy =\n#ifdef PAGES_CAN_PURGE_LAZY\n    true\n#else\n    false\n#endif\n    ;\nstatic const bool pages_can_purge_forced =\n#ifdef PAGES_CAN_PURGE_FORCED\n    true\n#else\n    false\n#endif\n    ;\n\ntypedef enum {\n\tthp_mode_default       = 0, /* Do not change hugepage settings. */\n\tthp_mode_always        = 1, /* Always set MADV_HUGEPAGE. */\n\tthp_mode_never         = 2, /* Always set MADV_NOHUGEPAGE. */\n\n\tthp_mode_names_limit   = 3, /* Used for option processing. */\n\tthp_mode_not_supported = 3  /* No THP support detected. */\n} thp_mode_t;\n\n#define THP_MODE_DEFAULT thp_mode_default\nextern thp_mode_t opt_thp;\nextern thp_mode_t init_system_thp_mode; /* Initial system wide state. */\nextern const char *thp_mode_names[];\n\nvoid *pages_map(void *addr, size_t size, size_t alignment, bool *commit);\nvoid pages_unmap(void *addr, size_t size);\nbool pages_commit(void *addr, size_t size);\nbool pages_decommit(void *addr, size_t size);\nbool pages_purge_lazy(void *addr, size_t size);\nbool pages_purge_forced(void *addr, size_t size);\nbool pages_huge(void *addr, size_t size);\nbool pages_nohuge(void *addr, size_t size);\nbool pages_dontdump(void *addr, size_t size);\nbool pages_dodump(void *addr, size_t size);\nbool pages_boot(void);\nvoid pages_set_thp_state (void *ptr, size_t size);\n\n#endif /* JEMALLOC_INTERNAL_PAGES_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ph.h",
    "content": "/*\n * A Pairing Heap implementation.\n *\n * \"The Pairing Heap: A New Form of Self-Adjusting Heap\"\n * https://www.cs.cmu.edu/~sleator/papers/pairing-heaps.pdf\n *\n * With auxiliary twopass list, described in a follow on paper.\n *\n * \"Pairing Heaps: Experiments and Analysis\"\n * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.2988&rep=rep1&type=pdf\n *\n *******************************************************************************\n */\n\n#ifndef PH_H_\n#define PH_H_\n\n/* Node structure. */\n#define phn(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\ta_type\t*phn_prev;\t\t\t\t\t\t\\\n\ta_type\t*phn_next;\t\t\t\t\t\t\\\n\ta_type\t*phn_lchild;\t\t\t\t\t\t\\\n}\n\n/* Root structure. */\n#define ph(a_type)\t\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\ta_type\t*ph_root;\t\t\t\t\t\t\\\n}\n\n/* Internal utility macros. */\n#define phn_lchild_get(a_type, a_field, a_phn)\t\t\t\t\\\n\t(a_phn->a_field.phn_lchild)\n#define phn_lchild_set(a_type, a_field, a_phn, a_lchild) do {\t\t\\\n\ta_phn->a_field.phn_lchild = a_lchild;\t\t\t\t\\\n} while (0)\n\n#define phn_next_get(a_type, a_field, a_phn)\t\t\t\t\\\n\t(a_phn->a_field.phn_next)\n#define phn_prev_set(a_type, a_field, a_phn, a_prev) do {\t\t\\\n\ta_phn->a_field.phn_prev = a_prev;\t\t\t\t\\\n} while (0)\n\n#define phn_prev_get(a_type, a_field, a_phn)\t\t\t\t\\\n\t(a_phn->a_field.phn_prev)\n#define phn_next_set(a_type, a_field, a_phn, a_next) do {\t\t\\\n\ta_phn->a_field.phn_next = a_next;\t\t\t\t\\\n} while (0)\n\n#define phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, a_cmp) do {\t\\\n\ta_type *phn0child;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert(a_phn0 != NULL);\t\t\t\t\t\t\\\n\tassert(a_phn1 != NULL);\t\t\t\t\t\t\\\n\tassert(a_cmp(a_phn0, a_phn1) <= 0);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tphn_prev_set(a_type, a_field, a_phn1, a_phn0);\t\t\t\\\n\tphn0child = phn_lchild_get(a_type, a_field, a_phn0);\t\t\\\n\tphn_next_set(a_type, a_field, a_phn1, phn0child);\t\t\\\n\tif (phn0child != NULL) {\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn0child, a_phn1);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tphn_lchild_set(a_type, a_field, a_phn0, a_phn1);\t\t\\\n} while (0)\n\n#define phn_merge(a_type, a_field, a_phn0, a_phn1, a_cmp, r_phn) do {\t\\\n\tif (a_phn0 == NULL) {\t\t\t\t\t\t\\\n\t\tr_phn = a_phn1;\t\t\t\t\t\t\\\n\t} else if (a_phn1 == NULL) {\t\t\t\t\t\\\n\t\tr_phn = a_phn0;\t\t\t\t\t\t\\\n\t} else if (a_cmp(a_phn0, a_phn1) < 0) {\t\t\t\t\\\n\t\tphn_merge_ordered(a_type, a_field, a_phn0, a_phn1,\t\\\n\t\t    a_cmp);\t\t\t\t\t\t\\\n\t\tr_phn = a_phn0;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tphn_merge_ordered(a_type, a_field, a_phn1, a_phn0,\t\\\n\t\t    a_cmp);\t\t\t\t\t\t\\\n\t\tr_phn = a_phn1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ph_merge_siblings(a_type, a_field, a_phn, a_cmp, r_phn) do {\t\\\n\ta_type *head = NULL;\t\t\t\t\t\t\\\n\ta_type *tail = NULL;\t\t\t\t\t\t\\\n\ta_type *phn0 = a_phn;\t\t\t\t\t\t\\\n\ta_type *phn1 = phn_next_get(a_type, a_field, phn0);\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * Multipass merge, wherein the first two elements of a FIFO\t\\\n\t * are repeatedly merged, and each result is appended to the\t\\\n\t * singly linked FIFO, until the FIFO contains only a single\t\\\n\t * element.  We start with a sibling list but no reference to\t\\\n\t * its tail, so we do a single pass over the sibling list to\t\\\n\t * populate the FIFO.\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tif (phn1 != NULL) {\t\t\t\t\t\t\\\n\t\ta_type *phnrest = phn_next_get(a_type, a_field, phn1);\t\\\n\t\tif (phnrest != NULL) {\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, phnrest, NULL);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn0, NULL);\t\t\\\n\t\tphn_next_set(a_type, a_field, phn0, NULL);\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn1, NULL);\t\t\\\n\t\tphn_next_set(a_type, a_field, phn1, NULL);\t\t\\\n\t\tphn_merge(a_type, a_field, phn0, phn1, a_cmp, phn0);\t\\\n\t\thead = tail = phn0;\t\t\t\t\t\\\n\t\tphn0 = phnrest;\t\t\t\t\t\t\\\n\t\twhile (phn0 != NULL) {\t\t\t\t\t\\\n\t\t\tphn1 = phn_next_get(a_type, a_field, phn0);\t\\\n\t\t\tif (phn1 != NULL) {\t\t\t\t\\\n\t\t\t\tphnrest = phn_next_get(a_type, a_field,\t\\\n\t\t\t\t    phn1);\t\t\t\t\\\n\t\t\t\tif (phnrest != NULL) {\t\t\t\\\n\t\t\t\t\tphn_prev_set(a_type, a_field,\t\\\n\t\t\t\t\t    phnrest, NULL);\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field, phn0,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn0,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field, phn1,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn1,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_merge(a_type, a_field, phn0, phn1,\t\\\n\t\t\t\t    a_cmp, phn0);\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, tail,\t\\\n\t\t\t\t    phn0);\t\t\t\t\\\n\t\t\t\ttail = phn0;\t\t\t\t\\\n\t\t\t\tphn0 = phnrest;\t\t\t\t\\\n\t\t\t} else {\t\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, tail,\t\\\n\t\t\t\t    phn0);\t\t\t\t\\\n\t\t\t\ttail = phn0;\t\t\t\t\\\n\t\t\t\tphn0 = NULL;\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\tphn0 = head;\t\t\t\t\t\t\\\n\t\tphn1 = phn_next_get(a_type, a_field, phn0);\t\t\\\n\t\tif (phn1 != NULL) {\t\t\t\t\t\\\n\t\t\twhile (true) {\t\t\t\t\t\\\n\t\t\t\thead = phn_next_get(a_type, a_field,\t\\\n\t\t\t\t    phn1);\t\t\t\t\\\n\t\t\t\tassert(phn_prev_get(a_type, a_field,\t\\\n\t\t\t\t    phn0) == NULL);\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn0,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tassert(phn_prev_get(a_type, a_field,\t\\\n\t\t\t\t    phn1) == NULL);\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, phn1,\t\\\n\t\t\t\t    NULL);\t\t\t\t\\\n\t\t\t\tphn_merge(a_type, a_field, phn0, phn1,\t\\\n\t\t\t\t    a_cmp, phn0);\t\t\t\\\n\t\t\t\tif (head == NULL) {\t\t\t\\\n\t\t\t\t\tbreak;\t\t\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field, tail,\t\\\n\t\t\t\t    phn0);\t\t\t\t\\\n\t\t\t\ttail = phn0;\t\t\t\t\\\n\t\t\t\tphn0 = head;\t\t\t\t\\\n\t\t\t\tphn1 = phn_next_get(a_type, a_field,\t\\\n\t\t\t\t    phn0);\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\tr_phn = phn0;\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ph_merge_aux(a_type, a_field, a_ph, a_cmp) do {\t\t\t\\\n\ta_type *phn = phn_next_get(a_type, a_field, a_ph->ph_root);\t\\\n\tif (phn != NULL) {\t\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, a_ph->ph_root, NULL);\t\\\n\t\tphn_next_set(a_type, a_field, a_ph->ph_root, NULL);\t\\\n\t\tphn_prev_set(a_type, a_field, phn, NULL);\t\t\\\n\t\tph_merge_siblings(a_type, a_field, phn, a_cmp, phn);\t\\\n\t\tassert(phn_next_get(a_type, a_field, phn) == NULL);\t\\\n\t\tphn_merge(a_type, a_field, a_ph->ph_root, phn, a_cmp,\t\\\n\t\t    a_ph->ph_root);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define ph_merge_children(a_type, a_field, a_phn, a_cmp, r_phn) do {\t\\\n\ta_type *lchild = phn_lchild_get(a_type, a_field, a_phn);\t\\\n\tif (lchild == NULL) {\t\t\t\t\t\t\\\n\t\tr_phn = NULL;\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tph_merge_siblings(a_type, a_field, lchild, a_cmp,\t\\\n\t\t    r_phn);\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/*\n * The ph_proto() macro generates function prototypes that correspond to the\n * functions generated by an equivalently parameterized call to ph_gen().\n */\n#define ph_proto(a_attr, a_prefix, a_ph_type, a_type)\t\t\t\\\na_attr void\ta_prefix##new(a_ph_type *ph);\t\t\t\t\\\na_attr bool\ta_prefix##empty(a_ph_type *ph);\t\t\t\t\\\na_attr a_type\t*a_prefix##first(a_ph_type *ph);\t\t\t\\\na_attr a_type\t*a_prefix##any(a_ph_type *ph);\t\t\t\t\\\na_attr void\ta_prefix##insert(a_ph_type *ph, a_type *phn);\t\t\\\na_attr a_type\t*a_prefix##remove_first(a_ph_type *ph);\t\t\t\\\na_attr a_type\t*a_prefix##remove_any(a_ph_type *ph);\t\t\t\\\na_attr void\ta_prefix##remove(a_ph_type *ph, a_type *phn);\n\n/*\n * The ph_gen() macro generates a type-specific pairing heap implementation,\n * based on the above cpp macros.\n */\n#define ph_gen(a_attr, a_prefix, a_ph_type, a_type, a_field, a_cmp)\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##new(a_ph_type *ph) {\t\t\t\t\t\t\\\n\tmemset(ph, 0, sizeof(ph(a_type)));\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_prefix##empty(a_ph_type *ph) {\t\t\t\t\t\\\n\treturn (ph->ph_root == NULL);\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_ph_type *ph) {\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tph_merge_aux(a_type, a_field, ph, a_cmp);\t\t\t\\\n\treturn ph->ph_root;\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##any(a_ph_type *ph) {\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_type *aux = phn_next_get(a_type, a_field, ph->ph_root);\t\\\n\tif (aux != NULL) {\t\t\t\t\t\t\\\n\t\treturn aux;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn ph->ph_root;\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_ph_type *ph, a_type *phn) {\t\t\t\t\\\n\tmemset(&phn->a_field, 0, sizeof(phn(a_type)));\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 * Treat the root as an aux list during insertion, and lazily\t\\\n\t * merge during a_prefix##remove_first().  For elements that\t\\\n\t * are inserted, then removed via a_prefix##remove() before the\t\\\n\t * aux list is ever processed, this makes insert/remove\t\t\\\n\t * constant-time, whereas eager merging would make insert\t\\\n\t * O(log n).\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\tph->ph_root = phn;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tphn_next_set(a_type, a_field, phn, phn_next_get(a_type,\t\\\n\t\t    a_field, ph->ph_root));\t\t\t\t\\\n\t\tif (phn_next_get(a_type, a_field, ph->ph_root) !=\t\\\n\t\t    NULL) {\t\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, ph->ph_root),\t\\\n\t\t\t    phn);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tphn_prev_set(a_type, a_field, phn, ph->ph_root);\t\\\n\t\tphn_next_set(a_type, a_field, ph->ph_root, phn);\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##remove_first(a_ph_type *ph) {\t\t\t\t\t\\\n\ta_type *ret;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tph_merge_aux(a_type, a_field, ph, a_cmp);\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tret = ph->ph_root;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tph_merge_children(a_type, a_field, ph->ph_root, a_cmp,\t\t\\\n\t    ph->ph_root);\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\treturn 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##remove_any(a_ph_type *ph) {\t\t\t\t\t\\\n\t/*\t\t\t\t\t\t\t\t\\\n\t * Remove the most recently inserted aux list element, or the\t\\\n\t * root if the aux list is empty.  This has the effect of\t\\\n\t * behaving as a LIFO (and insertion/removal is therefore\t\\\n\t * constant-time) if a_prefix##[remove_]first() are never\t\\\n\t * called.\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == NULL) {\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ta_type *ret = phn_next_get(a_type, a_field, ph->ph_root);\t\\\n\tif (ret != NULL) {\t\t\t\t\t\t\\\n\t\ta_type *aux = phn_next_get(a_type, a_field, ret);\t\\\n\t\tphn_next_set(a_type, a_field, ph->ph_root, aux);\t\\\n\t\tif (aux != NULL) {\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, aux,\t\t\\\n\t\t\t    ph->ph_root);\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\treturn ret;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tret = ph->ph_root;\t\t\t\t\t\t\\\n\tph_merge_children(a_type, a_field, ph->ph_root, a_cmp,\t\t\\\n\t    ph->ph_root);\t\t\t\t\t\t\\\n\treturn 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##remove(a_ph_type *ph, a_type *phn) {\t\t\t\t\\\n\ta_type *replace, *parent;\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (ph->ph_root == phn) {\t\t\t\t\t\\\n\t\t/*\t\t\t\t\t\t\t\\\n\t\t * We can delete from aux list without merging it, but\t\\\n\t\t * we need to merge if we are dealing with the root\t\\\n\t\t * node and it has children.\t\t\t\t\\\n\t\t */\t\t\t\t\t\t\t\\\n\t\tif (phn_lchild_get(a_type, a_field, phn) == NULL) {\t\\\n\t\t\tph->ph_root = phn_next_get(a_type, a_field,\t\\\n\t\t\t    phn);\t\t\t\t\t\\\n\t\t\tif (ph->ph_root != NULL) {\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field,\t\t\\\n\t\t\t\t    ph->ph_root, NULL);\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\tph_merge_aux(a_type, a_field, ph, a_cmp);\t\t\\\n\t\tif (ph->ph_root == phn) {\t\t\t\t\\\n\t\t\tph_merge_children(a_type, a_field, ph->ph_root,\t\\\n\t\t\t    a_cmp, ph->ph_root);\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\t\t\t\t\t\t\t\t\t\\\n\t/* Get parent (if phn is leftmost child) before mutating. */\t\\\n\tif ((parent = phn_prev_get(a_type, a_field, phn)) != NULL) {\t\\\n\t\tif (phn_lchild_get(a_type, a_field, parent) != phn) {\t\\\n\t\t\tparent = 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/* Find a possible replacement node, and link to parent. */\t\\\n\tph_merge_children(a_type, a_field, phn, a_cmp, replace);\t\\\n\t/* Set next/prev for sibling linked list. */\t\t\t\\\n\tif (replace != NULL) {\t\t\t\t\t\t\\\n\t\tif (parent != NULL) {\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, replace, parent);\t\\\n\t\t\tphn_lchild_set(a_type, a_field, parent,\t\t\\\n\t\t\t    replace);\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tphn_prev_set(a_type, a_field, replace,\t\t\\\n\t\t\t    phn_prev_get(a_type, a_field, phn));\t\\\n\t\t\tif (phn_prev_get(a_type, a_field, phn) !=\t\\\n\t\t\t    NULL) {\t\t\t\t\t\\\n\t\t\t\tphn_next_set(a_type, a_field,\t\t\\\n\t\t\t\t    phn_prev_get(a_type, a_field, phn),\t\\\n\t\t\t\t    replace);\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\tphn_next_set(a_type, a_field, replace,\t\t\t\\\n\t\t    phn_next_get(a_type, a_field, phn));\t\t\\\n\t\tif (phn_next_get(a_type, a_field, phn) != NULL) {\t\\\n\t\t\tphn_prev_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, phn),\t\t\\\n\t\t\t    replace);\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\tif (parent != NULL) {\t\t\t\t\t\\\n\t\t\ta_type *next = phn_next_get(a_type, a_field,\t\\\n\t\t\t    phn);\t\t\t\t\t\\\n\t\t\tphn_lchild_set(a_type, a_field, parent, next);\t\\\n\t\t\tif (next != NULL) {\t\t\t\t\\\n\t\t\t\tphn_prev_set(a_type, a_field, next,\t\\\n\t\t\t\t    parent);\t\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tassert(phn_prev_get(a_type, a_field, phn) !=\t\\\n\t\t\t    NULL);\t\t\t\t\t\\\n\t\t\tphn_next_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_prev_get(a_type, a_field, phn),\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, phn));\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (phn_next_get(a_type, a_field, phn) != NULL) {\t\\\n\t\t\tphn_prev_set(a_type, a_field,\t\t\t\\\n\t\t\t    phn_next_get(a_type, a_field, phn),\t\t\\\n\t\t\t    phn_prev_get(a_type, a_field, phn));\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n}\n\n#endif /* PH_H_ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/private_namespace.sh",
    "content": "#!/bin/sh\n\nfor symbol in `cat \"$@\"` ; do\n  echo \"#define ${symbol} JEMALLOC_N(${symbol})\"\ndone\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/private_symbols.sh",
    "content": "#!/bin/sh\n#\n# Generate private_symbols[_jet].awk.\n#\n# Usage: private_symbols.sh <sym_prefix> <sym>*\n#\n# <sym_prefix> is typically \"\" or \"_\".\n\nsym_prefix=$1\nshift\n\ncat <<EOF\n#!/usr/bin/env awk -f\n\nBEGIN {\n  sym_prefix = \"${sym_prefix}\"\n  split(\"\\\\\nEOF\n\nfor public_sym in \"$@\" ; do\n  cat <<EOF\n        ${sym_prefix}${public_sym} \\\\\nEOF\ndone\n\ncat <<\"EOF\"\n        \", exported_symbol_names)\n  # Store exported symbol names as keys in exported_symbols.\n  for (i in exported_symbol_names) {\n    exported_symbols[exported_symbol_names[i]] = 1\n  }\n}\n\n# Process 'nm -a <c_source.o>' output.\n#\n# Handle lines like:\n#   0000000000000008 D opt_junk\n#   0000000000007574 T malloc_initialized\n(NF == 3 && $2 ~ /^[ABCDGRSTVW]$/ && !($3 in exported_symbols) && $3 ~ /^[A-Za-z0-9_]+$/) {\n  print substr($3, 1+length(sym_prefix), length($3)-length(sym_prefix))\n}\n\n# Process 'dumpbin /SYMBOLS <c_source.obj>' output.\n#\n# Handle lines like:\n#   353 00008098 SECT4  notype       External     | opt_junk\n#   3F1 00000000 SECT7  notype ()    External     | malloc_initialized\n($3 ~ /^SECT[0-9]+/ && $(NF-2) == \"External\" && !($NF in exported_symbols)) {\n  print $NF\n}\nEOF\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prng.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PRNG_H\n#define JEMALLOC_INTERNAL_PRNG_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/bit_util.h\"\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\n/******************************************************************************/\n/* INTERNAL DEFINITIONS -- IGNORE */\n/******************************************************************************/\n#define PRNG_A_32\tUINT32_C(1103515241)\n#define PRNG_C_32\tUINT32_C(12347)\n\n#define PRNG_A_64\tUINT64_C(6364136223846793005)\n#define PRNG_C_64\tUINT64_C(1442695040888963407)\n\nJEMALLOC_ALWAYS_INLINE uint32_t\nprng_state_next_u32(uint32_t state) {\n\treturn (state * PRNG_A_32) + PRNG_C_32;\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nprng_state_next_u64(uint64_t state) {\n\treturn (state * PRNG_A_64) + PRNG_C_64;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nprng_state_next_zu(size_t state) {\n#if LG_SIZEOF_PTR == 2\n\treturn (state * PRNG_A_32) + PRNG_C_32;\n#elif LG_SIZEOF_PTR == 3\n\treturn (state * PRNG_A_64) + PRNG_C_64;\n#else\n#error Unsupported pointer size\n#endif\n}\n\n/******************************************************************************/\n/* BEGIN PUBLIC API */\n/******************************************************************************/\n\n/*\n * The prng_lg_range functions give a uniform int in the half-open range [0,\n * 2**lg_range).  If atomic is true, they do so safely from multiple threads.\n * Multithreaded 64-bit prngs aren't supported.\n */\n\nJEMALLOC_ALWAYS_INLINE uint32_t\nprng_lg_range_u32(atomic_u32_t *state, unsigned lg_range, bool atomic) {\n\tuint32_t ret, state0, state1;\n\n\tassert(lg_range > 0);\n\tassert(lg_range <= 32);\n\n\tstate0 = atomic_load_u32(state, ATOMIC_RELAXED);\n\n\tif (atomic) {\n\t\tdo {\n\t\t\tstate1 = prng_state_next_u32(state0);\n\t\t} while (!atomic_compare_exchange_weak_u32(state, &state0,\n\t\t    state1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n\t} else {\n\t\tstate1 = prng_state_next_u32(state0);\n\t\tatomic_store_u32(state, state1, ATOMIC_RELAXED);\n\t}\n\tret = state1 >> (32 - lg_range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nprng_lg_range_u64(uint64_t *state, unsigned lg_range) {\n\tuint64_t ret, state1;\n\n\tassert(lg_range > 0);\n\tassert(lg_range <= 64);\n\n\tstate1 = prng_state_next_u64(*state);\n\t*state = state1;\n\tret = state1 >> (64 - lg_range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nprng_lg_range_zu(atomic_zu_t *state, unsigned lg_range, bool atomic) {\n\tsize_t ret, state0, state1;\n\n\tassert(lg_range > 0);\n\tassert(lg_range <= ZU(1) << (3 + LG_SIZEOF_PTR));\n\n\tstate0 = atomic_load_zu(state, ATOMIC_RELAXED);\n\n\tif (atomic) {\n\t\tdo {\n\t\t\tstate1 = prng_state_next_zu(state0);\n\t\t} while (atomic_compare_exchange_weak_zu(state, &state0,\n\t\t    state1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n\t} else {\n\t\tstate1 = prng_state_next_zu(state0);\n\t\tatomic_store_zu(state, state1, ATOMIC_RELAXED);\n\t}\n\tret = state1 >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) - lg_range);\n\n\treturn ret;\n}\n\n/*\n * The prng_range functions behave like the prng_lg_range, but return a result\n * in [0, range) instead of [0, 2**lg_range).\n */\n\nJEMALLOC_ALWAYS_INLINE uint32_t\nprng_range_u32(atomic_u32_t *state, uint32_t range, bool atomic) {\n\tuint32_t ret;\n\tunsigned lg_range;\n\n\tassert(range > 1);\n\n\t/* Compute the ceiling of lg(range). */\n\tlg_range = ffs_u32(pow2_ceil_u32(range)) - 1;\n\n\t/* Generate a result in [0..range) via repeated trial. */\n\tdo {\n\t\tret = prng_lg_range_u32(state, lg_range, atomic);\n\t} while (ret >= range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE uint64_t\nprng_range_u64(uint64_t *state, uint64_t range) {\n\tuint64_t ret;\n\tunsigned lg_range;\n\n\tassert(range > 1);\n\n\t/* Compute the ceiling of lg(range). */\n\tlg_range = ffs_u64(pow2_ceil_u64(range)) - 1;\n\n\t/* Generate a result in [0..range) via repeated trial. */\n\tdo {\n\t\tret = prng_lg_range_u64(state, lg_range);\n\t} while (ret >= range);\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nprng_range_zu(atomic_zu_t *state, size_t range, bool atomic) {\n\tsize_t ret;\n\tunsigned lg_range;\n\n\tassert(range > 1);\n\n\t/* Compute the ceiling of lg(range). */\n\tlg_range = ffs_u64(pow2_ceil_u64(range)) - 1;\n\n\t/* Generate a result in [0..range) via repeated trial. */\n\tdo {\n\t\tret = prng_lg_range_zu(state, lg_range, atomic);\n\t} while (ret >= range);\n\n\treturn ret;\n}\n\n#endif /* JEMALLOC_INTERNAL_PRNG_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_EXTERNS_H\n#define JEMALLOC_INTERNAL_PROF_EXTERNS_H\n\n#include \"jemalloc/internal/mutex.h\"\n\nextern malloc_mutex_t\tbt2gctx_mtx;\n\nextern bool\topt_prof;\nextern bool\topt_prof_active;\nextern bool\topt_prof_thread_active_init;\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[\n    /* Minimize memory bloat for non-prof builds. */\n#ifdef JEMALLOC_PROF\n    PATH_MAX +\n#endif\n    1];\n\n/* Accessed via prof_active_[gs]et{_unlocked,}(). */\nextern bool\tprof_active;\n\n/* Accessed via prof_gdump_[gs]et{_unlocked,}(). */\nextern bool\tprof_gdump_val;\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 * Initialized as opt_lg_prof_sample, and potentially modified during profiling\n * resets.\n */\nextern size_t\tlg_prof_sample;\n\nvoid prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated);\nvoid prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,\n    prof_tctx_t *tctx);\nvoid prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx);\nvoid bt_init(prof_bt_t *bt, void **vec);\nvoid prof_backtrace(prof_bt_t *bt);\nprof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt);\n#ifdef JEMALLOC_JET\nsize_t prof_tdata_count(void);\nsize_t prof_bt_count(void);\n#endif\ntypedef int (prof_dump_open_t)(bool, const char *);\nextern prof_dump_open_t *JET_MUTABLE prof_dump_open;\n\ntypedef bool (prof_dump_header_t)(tsdn_t *, bool, const prof_cnt_t *);\nextern prof_dump_header_t *JET_MUTABLE prof_dump_header;\n#ifdef JEMALLOC_JET\nvoid prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,\n    uint64_t *accumbytes);\n#endif\nbool prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum);\nvoid prof_idump(tsdn_t *tsdn);\nbool prof_mdump(tsd_t *tsd, const char *filename);\nvoid prof_gdump(tsdn_t *tsdn);\nprof_tdata_t *prof_tdata_init(tsd_t *tsd);\nprof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata);\nvoid prof_reset(tsd_t *tsd, size_t lg_sample);\nvoid prof_tdata_cleanup(tsd_t *tsd);\nbool prof_active_get(tsdn_t *tsdn);\nbool prof_active_set(tsdn_t *tsdn, bool active);\nconst char *prof_thread_name_get(tsd_t *tsd);\nint prof_thread_name_set(tsd_t *tsd, const char *thread_name);\nbool prof_thread_active_get(tsd_t *tsd);\nbool prof_thread_active_set(tsd_t *tsd, bool active);\nbool prof_thread_active_init_get(tsdn_t *tsdn);\nbool prof_thread_active_init_set(tsdn_t *tsdn, bool active_init);\nbool prof_gdump_get(tsdn_t *tsdn);\nbool prof_gdump_set(tsdn_t *tsdn, bool active);\nvoid prof_boot0(void);\nvoid prof_boot1(void);\nbool prof_boot2(tsd_t *tsd);\nvoid prof_prefork0(tsdn_t *tsdn);\nvoid prof_prefork1(tsdn_t *tsdn);\nvoid prof_postfork_parent(tsdn_t *tsdn);\nvoid prof_postfork_child(tsdn_t *tsdn);\nvoid prof_sample_threshold_update(prof_tdata_t *tdata);\n\n#endif /* JEMALLOC_INTERNAL_PROF_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_inlines_a.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_INLINES_A_H\n#define JEMALLOC_INTERNAL_PROF_INLINES_A_H\n\n#include \"jemalloc/internal/mutex.h\"\n\nstatic inline bool\nprof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum, uint64_t accumbytes) {\n\tcassert(config_prof);\n\n\tbool overflow;\n\tuint64_t a0, a1;\n\n\t/*\n\t * If the application allocates fast enough (and/or if idump is slow\n\t * enough), extreme overflow here (a1 >= prof_interval * 2) can cause\n\t * idump trigger coalescing.  This is an intentional mechanism that\n\t * avoids rate-limiting allocation.\n\t */\n#ifdef JEMALLOC_ATOMIC_U64\n\ta0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);\n\tdo {\n\t\ta1 = a0 + accumbytes;\n\t\tassert(a1 >= a0);\n\t\toverflow = (a1 >= prof_interval);\n\t\tif (overflow) {\n\t\t\ta1 %= prof_interval;\n\t\t}\n\t} while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,\n\t    a1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n#else\n\tmalloc_mutex_lock(tsdn, &prof_accum->mtx);\n\ta0 = prof_accum->accumbytes;\n\ta1 = a0 + accumbytes;\n\toverflow = (a1 >= prof_interval);\n\tif (overflow) {\n\t\ta1 %= prof_interval;\n\t}\n\tprof_accum->accumbytes = a1;\n\tmalloc_mutex_unlock(tsdn, &prof_accum->mtx);\n#endif\n\treturn overflow;\n}\n\nstatic inline void\nprof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum, size_t usize) {\n\tcassert(config_prof);\n\n\t/*\n\t * Cancel out as much of the excessive prof_accumbytes increase as\n\t * possible without underflowing.  Interval-triggered dumps occur\n\t * slightly more often than intended as a result of incomplete\n\t * canceling.\n\t */\n\tuint64_t a0, a1;\n#ifdef JEMALLOC_ATOMIC_U64\n\ta0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);\n\tdo {\n\t\ta1 = (a0 >= LARGE_MINCLASS - usize) ?  a0 - (LARGE_MINCLASS -\n\t\t    usize) : 0;\n\t} while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,\n\t    a1, ATOMIC_RELAXED, ATOMIC_RELAXED));\n#else\n\tmalloc_mutex_lock(tsdn, &prof_accum->mtx);\n\ta0 = prof_accum->accumbytes;\n\ta1 = (a0 >= LARGE_MINCLASS - usize) ?  a0 - (LARGE_MINCLASS - usize) :\n\t    0;\n\tprof_accum->accumbytes = a1;\n\tmalloc_mutex_unlock(tsdn, &prof_accum->mtx);\n#endif\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nprof_active_get_unlocked(void) {\n\t/*\n\t * Even if opt_prof is true, sampling can be temporarily disabled by\n\t * setting prof_active to false.  No locking is used when reading\n\t * prof_active in the fast path, so there are no guarantees regarding\n\t * how long it will take for all threads to notice state changes.\n\t */\n\treturn prof_active;\n}\n\n#endif /* JEMALLOC_INTERNAL_PROF_INLINES_A_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_inlines_b.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H\n#define JEMALLOC_INTERNAL_PROF_INLINES_B_H\n\n#include \"jemalloc/internal/sz.h\"\n\nJEMALLOC_ALWAYS_INLINE bool\nprof_gdump_get_unlocked(void) {\n\t/*\n\t * No locking is used when reading prof_gdump_val in the fast path, so\n\t * there are no guarantees regarding how long it will take for all\n\t * threads to notice state changes.\n\t */\n\treturn prof_gdump_val;\n}\n\nJEMALLOC_ALWAYS_INLINE prof_tdata_t *\nprof_tdata_get(tsd_t *tsd, bool create) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\ttdata = tsd_prof_tdata_get(tsd);\n\tif (create) {\n\t\tif (unlikely(tdata == NULL)) {\n\t\t\tif (tsd_nominal(tsd)) {\n\t\t\t\ttdata = prof_tdata_init(tsd);\n\t\t\t\ttsd_prof_tdata_set(tsd, tdata);\n\t\t\t}\n\t\t} else if (unlikely(tdata->expired)) {\n\t\t\ttdata = prof_tdata_reinit(tsd, tdata);\n\t\t\ttsd_prof_tdata_set(tsd, tdata);\n\t\t}\n\t\tassert(tdata == NULL || tdata->attached);\n\t}\n\n\treturn tdata;\n}\n\nJEMALLOC_ALWAYS_INLINE prof_tctx_t *\nprof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\treturn arena_prof_tctx_get(tsdn, ptr, alloc_ctx);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,\n    alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\tarena_prof_tctx_set(tsdn, ptr, usize, alloc_ctx, tctx);\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\tarena_prof_tctx_reset(tsdn, ptr, tctx);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nprof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,\n    prof_tdata_t **tdata_out) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (unlikely((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)) {\n\t\ttdata = NULL;\n\t}\n\n\tif (tdata_out != NULL) {\n\t\t*tdata_out = tdata;\n\t}\n\n\tif (unlikely(tdata == NULL)) {\n\t\treturn true;\n\t}\n\n\tif (likely(tdata->bytes_until_sample >= usize)) {\n\t\tif (update) {\n\t\t\ttdata->bytes_until_sample -= usize;\n\t\t}\n\t\treturn true;\n\t} else {\n\t\tif (tsd_reentrancy_level_get(tsd) > 0) {\n\t\t\treturn true;\n\t\t}\n\t\t/* Compute new sample threshold. */\n\t\tif (update) {\n\t\t\tprof_sample_threshold_update(tdata);\n\t\t}\n\t\treturn !tdata->active;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE prof_tctx_t *\nprof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active, bool update) {\n\tprof_tctx_t *ret;\n\tprof_tdata_t *tdata;\n\tprof_bt_t bt;\n\n\tassert(usize == sz_s2u(usize));\n\n\tif (!prof_active || likely(prof_sample_accum_update(tsd, usize, update,\n\t    &tdata))) {\n\t\tret = (prof_tctx_t *)(uintptr_t)1U;\n\t} else {\n\t\tbt_init(&bt, tdata->vec);\n\t\tprof_backtrace(&bt);\n\t\tret = prof_lookup(tsd, &bt);\n\t}\n\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx,\n    prof_tctx_t *tctx) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(usize == isalloc(tsdn, ptr));\n\n\tif (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {\n\t\tprof_malloc_sample_object(tsdn, ptr, usize, tctx);\n\t} else {\n\t\tprof_tctx_set(tsdn, ptr, usize, alloc_ctx,\n\t\t    (prof_tctx_t *)(uintptr_t)1U);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,\n    bool prof_active, bool updated, const void *old_ptr, size_t old_usize,\n    prof_tctx_t *old_tctx) {\n\tbool sampled, old_sampled, moved;\n\n\tcassert(config_prof);\n\tassert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);\n\n\tif (prof_active && !updated && ptr != NULL) {\n\t\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\t\tif (prof_sample_accum_update(tsd, usize, true, NULL)) {\n\t\t\t/*\n\t\t\t * Don't sample.  The usize passed to prof_alloc_prep()\n\t\t\t * was larger than what actually got allocated, so a\n\t\t\t * backtrace was captured for this allocation, even\n\t\t\t * though its actual usize was insufficient to cross the\n\t\t\t * sample threshold.\n\t\t\t */\n\t\t\tprof_alloc_rollback(tsd, tctx, true);\n\t\t\ttctx = (prof_tctx_t *)(uintptr_t)1U;\n\t\t}\n\t}\n\n\tsampled = ((uintptr_t)tctx > (uintptr_t)1U);\n\told_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U);\n\tmoved = (ptr != old_ptr);\n\n\tif (unlikely(sampled)) {\n\t\tprof_malloc_sample_object(tsd_tsdn(tsd), ptr, usize, tctx);\n\t} else if (moved) {\n\t\tprof_tctx_set(tsd_tsdn(tsd), ptr, usize, NULL,\n\t\t    (prof_tctx_t *)(uintptr_t)1U);\n\t} else if (unlikely(old_sampled)) {\n\t\t/*\n\t\t * prof_tctx_set() would work for the !moved case as well, but\n\t\t * prof_tctx_reset() is slightly cheaper, and the proper thing\n\t\t * to do here in the presence of explicit knowledge re: moved\n\t\t * state.\n\t\t */\n\t\tprof_tctx_reset(tsd_tsdn(tsd), ptr, tctx);\n\t} else {\n\t\tassert((uintptr_t)prof_tctx_get(tsd_tsdn(tsd), ptr, NULL) ==\n\t\t    (uintptr_t)1U);\n\t}\n\n\t/*\n\t * The prof_free_sampled_object() call must come after the\n\t * prof_malloc_sample_object() call, because tctx and old_tctx may be\n\t * the same, in which case reversing the call order could cause the tctx\n\t * to be prematurely destroyed as a side effect of momentarily zeroed\n\t * counters.\n\t */\n\tif (unlikely(old_sampled)) {\n\t\tprof_free_sampled_object(tsd, old_usize, old_tctx);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\nprof_free(tsd_t *tsd, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx) {\n\tprof_tctx_t *tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);\n\n\tcassert(config_prof);\n\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\n\tif (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {\n\t\tprof_free_sampled_object(tsd, usize, tctx);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_PROF_INLINES_B_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_STRUCTS_H\n#define JEMALLOC_INTERNAL_PROF_STRUCTS_H\n\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/rb.h\"\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\tmax;\n} prof_unwind_data_t;\n#endif\n\nstruct prof_accum_s {\n#ifndef JEMALLOC_ATOMIC_U64\n\tmalloc_mutex_t\tmtx;\n\tuint64_t\taccumbytes;\n#else\n\tatomic_u64_t\taccumbytes;\n#endif\n};\n\nstruct prof_cnt_s {\n\t/* Profiling counters. */\n\tuint64_t\tcurobjs;\n\tuint64_t\tcurbytes;\n\tuint64_t\taccumobjs;\n\tuint64_t\taccumbytes;\n};\n\ntypedef enum {\n\tprof_tctx_state_initializing,\n\tprof_tctx_state_nominal,\n\tprof_tctx_state_dumping,\n\tprof_tctx_state_purgatory /* Dumper must finish destroying. */\n} prof_tctx_state_t;\n\nstruct prof_tctx_s {\n\t/* Thread data for thread that performed the allocation. */\n\tprof_tdata_t\t\t*tdata;\n\n\t/*\n\t * Copy of tdata->thr_{uid,discrim}, necessary because tdata may be\n\t * defunct during teardown.\n\t */\n\tuint64_t\t\tthr_uid;\n\tuint64_t\t\tthr_discrim;\n\n\t/* Profiling counters, protected by tdata->lock. */\n\tprof_cnt_t\t\tcnts;\n\n\t/* Associated global context. */\n\tprof_gctx_t\t\t*gctx;\n\n\t/*\n\t * UID that distinguishes multiple tctx's created by the same thread,\n\t * but coexisting in gctx->tctxs.  There are two ways that such\n\t * coexistence can occur:\n\t * - A dumper thread can cause a tctx to be retained in the purgatory\n\t *   state.\n\t * - Although a single \"producer\" thread must create all tctx's which\n\t *   share the same thr_uid, multiple \"consumers\" can each concurrently\n\t *   execute portions of prof_tctx_destroy().  prof_tctx_destroy() only\n\t *   gets called once each time cnts.cur{objs,bytes} drop to 0, but this\n\t *   threshold can be hit again before the first consumer finishes\n\t *   executing prof_tctx_destroy().\n\t */\n\tuint64_t\t\ttctx_uid;\n\n\t/* Linkage into gctx's tctxs. */\n\trb_node(prof_tctx_t)\ttctx_link;\n\n\t/*\n\t * True during prof_alloc_prep()..prof_malloc_sample_object(), prevents\n\t * sample vs destroy race.\n\t */\n\tbool\t\t\tprepared;\n\n\t/* Current dump-related state, protected by gctx->lock. */\n\tprof_tctx_state_t\tstate;\n\n\t/*\n\t * Copy of cnts snapshotted during early dump phase, protected by\n\t * dump_mtx.\n\t */\n\tprof_cnt_t\t\tdump_cnts;\n};\ntypedef rb_tree(prof_tctx_t) prof_tctx_tree_t;\n\nstruct prof_gctx_s {\n\t/* Protects nlimbo, cnt_summed, and tctxs. */\n\tmalloc_mutex_t\t\t*lock;\n\n\t/*\n\t * Number of threads that currently cause this gctx to be in a state of\n\t * limbo due to one of:\n\t *   - Initializing this gctx.\n\t *   - Initializing per thread counters associated with this gctx.\n\t *   - Preparing to destroy this gctx.\n\t *   - Dumping a heap profile that includes this gctx.\n\t * nlimbo must be 1 (single destroyer) in order to safely destroy the\n\t * gctx.\n\t */\n\tunsigned\t\tnlimbo;\n\n\t/*\n\t * Tree of profile counters, one for each thread that has allocated in\n\t * this context.\n\t */\n\tprof_tctx_tree_t\ttctxs;\n\n\t/* Linkage for tree of contexts to be dumped. */\n\trb_node(prof_gctx_t)\tdump_link;\n\n\t/* Temporary storage for summation during dump. */\n\tprof_cnt_t\t\tcnt_summed;\n\n\t/* Associated backtrace. */\n\tprof_bt_t\t\tbt;\n\n\t/* Backtrace vector, variable size, referred to by bt. */\n\tvoid\t\t\t*vec[1];\n};\ntypedef rb_tree(prof_gctx_t) prof_gctx_tree_t;\n\nstruct prof_tdata_s {\n\tmalloc_mutex_t\t\t*lock;\n\n\t/* Monotonically increasing unique thread identifier. */\n\tuint64_t\t\tthr_uid;\n\n\t/*\n\t * Monotonically increasing discriminator among tdata structures\n\t * associated with the same thr_uid.\n\t */\n\tuint64_t\t\tthr_discrim;\n\n\t/* Included in heap profile dumps if non-NULL. */\n\tchar\t\t\t*thread_name;\n\n\tbool\t\t\tattached;\n\tbool\t\t\texpired;\n\n\trb_node(prof_tdata_t)\ttdata_link;\n\n\t/*\n\t * Counter used to initialize prof_tctx_t's tctx_uid.  No locking is\n\t * necessary when incrementing this field, because only one thread ever\n\t * does so.\n\t */\n\tuint64_t\t\ttctx_uid_next;\n\n\t/*\n\t * Hash of (prof_bt_t *)-->(prof_tctx_t *).  Each thread tracks\n\t * backtraces for which it has non-zero allocation/deallocation counters\n\t * associated with thread-specific prof_tctx_t objects.  Other threads\n\t * may write to prof_tctx_t contents when freeing associated objects.\n\t */\n\tckh_t\t\t\tbt2tctx;\n\n\t/* Sampling state. */\n\tuint64_t\t\tprng_state;\n\tuint64_t\t\tbytes_until_sample;\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\t/*\n\t * Set to true during an early dump phase for tdata's which are\n\t * currently being dumped.  New threads' tdata's have this initialized\n\t * to false so that they aren't accidentally included in later dump\n\t * phases.\n\t */\n\tbool\t\t\tdumping;\n\n\t/*\n\t * True if profiling is active for this tdata's thread\n\t * (thread.prof.active mallctl).\n\t */\n\tbool\t\t\tactive;\n\n\t/* Temporary storage for summation during dump. */\n\tprof_cnt_t\t\tcnt_summed;\n\n\t/* Backtrace vector, used for calls to prof_backtrace(). */\n\tvoid\t\t\t*vec[PROF_BT_MAX];\n};\ntypedef rb_tree(prof_tdata_t) prof_tdata_tree_t;\n\n#endif /* JEMALLOC_INTERNAL_PROF_STRUCTS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/prof_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_PROF_TYPES_H\n#define JEMALLOC_INTERNAL_PROF_TYPES_H\n\ntypedef struct prof_bt_s prof_bt_t;\ntypedef struct prof_accum_s prof_accum_t;\ntypedef struct prof_cnt_s prof_cnt_t;\ntypedef struct prof_tctx_s prof_tctx_t;\ntypedef struct prof_gctx_s prof_gctx_t;\ntypedef struct prof_tdata_s prof_tdata_t;\n\n/* Option defaults. */\n#ifdef JEMALLOC_PROF\n#  define PROF_PREFIX_DEFAULT\t\t\"jeprof\"\n#else\n#  define PROF_PREFIX_DEFAULT\t\t\"\"\n#endif\n#define LG_PROF_SAMPLE_DEFAULT\t\t19\n#define LG_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 PROF_BT_MAX\t\t\t128\n\n/* Initial hash table size. */\n#define PROF_CKH_MINITEMS\t\t64\n\n/* Size of memory buffer to use when writing dump files. */\n#define PROF_DUMP_BUFSIZE\t\t65536\n\n/* Size of stack-allocated buffer used by prof_printf(). */\n#define PROF_PRINTF_BUFSIZE\t\t128\n\n/*\n * Number of mutexes shared among all gctx's.  No space is allocated for these\n * unless profiling is enabled, so it's okay to over-provision.\n */\n#define PROF_NCTX_LOCKS\t\t\t1024\n\n/*\n * Number of mutexes shared among all tdata's.  No space is allocated for these\n * unless profiling is enabled, so it's okay to over-provision.\n */\n#define PROF_NTDATA_LOCKS\t\t256\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 PROF_TDATA_STATE_REINCARNATED\t((prof_tdata_t *)(uintptr_t)1)\n#define PROF_TDATA_STATE_PURGATORY\t((prof_tdata_t *)(uintptr_t)2)\n#define PROF_TDATA_STATE_MAX\t\tPROF_TDATA_STATE_PURGATORY\n\n#endif /* JEMALLOC_INTERNAL_PROF_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/public_namespace.sh",
    "content": "#!/bin/sh\n\nfor nm in `cat $1` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#define je_${n} JEMALLOC_N(${n})\"\ndone\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/public_unnamespace.sh",
    "content": "#!/bin/sh\n\nfor nm in `cat $1` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#undef je_${n}\"\ndone\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ql.h",
    "content": "#ifndef JEMALLOC_INTERNAL_QL_H\n#define JEMALLOC_INTERNAL_QL_H\n\n#include \"jemalloc/internal/qr.h\"\n\n/* List definitions. */\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\n#endif /* JEMALLOC_INTERNAL_QL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/qr.h",
    "content": "#ifndef JEMALLOC_INTERNAL_QR_H\n#define JEMALLOC_INTERNAL_QR_H\n\n/* 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) do {\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_type, a_field) do {\t\t\t\\\n\ta_type *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/*\n * qr_meld() and qr_split() are functionally equivalent, so there's no need to\n * have two copies of the code.\n */\n#define qr_split(a_qr_a, a_qr_b, a_type, a_field)\t\t\t\\\n\tqr_meld((a_qr_a), (a_qr_b), a_type, 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\n#endif /* JEMALLOC_INTERNAL_QR_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 RB_H_\n\n#ifndef __PGI\n#define RB_COMPACT\n#endif\n\n#ifdef RB_COMPACT\n/* Node structure. */\n#define rb_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 rb_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 rb_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}\n\n/* Left accessors. */\n#define rbtn_left_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_left)\n#define rbtn_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 rbtn_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 rbtn_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 rbtn_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 rbtn_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 rbtn_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 rbtn_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\n/* Node initializer. */\n#define rbt_node_new(a_type, a_field, a_rbt, a_node) do {\t\t\\\n    /* Bookkeeping bit cannot be used by node pointer. */\t\t\\\n    assert(((uintptr_t)(a_node) & 0x1) == 0);\t\t\t\t\\\n    rbtn_left_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_right_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_red_set(a_type, a_field, (a_node));\t\t\t\t\\\n} while (0)\n#else\n/* Right accessors. */\n#define rbtn_right_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_right)\n#define rbtn_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 rbtn_red_get(a_type, a_field, a_node)\t\t\t\t\\\n    ((a_node)->a_field.rbn_red)\n#define rbtn_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 rbtn_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 rbtn_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\n/* Node initializer. */\n#define rbt_node_new(a_type, a_field, a_rbt, a_node) do {\t\t\\\n    rbtn_left_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_right_set(a_type, a_field, (a_node), NULL);\t\\\n    rbtn_red_set(a_type, a_field, (a_node));\t\t\t\t\\\n} while (0)\n#endif\n\n/* Tree initializer. */\n#define rb_new(a_type, a_field, a_rbt) do {\t\t\t\t\\\n    (a_rbt)->rbt_root = NULL;\t\t\t\t\t\t\\\n} while (0)\n\n/* Internal utility macros. */\n#define rbtn_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) != NULL) {\t\t\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)) != NULL;\t\t\\\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 rbtn_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) != NULL) {\t\t\t\t\t\t\\\n\tfor (; rbtn_right_get(a_type, a_field, (r_node)) != NULL;\t\\\n\t  (r_node) = rbtn_right_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 rbtn_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 rbtn_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 rb_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 bool\t\t\t\t\t\t\t\t\\\na_prefix##empty(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, const a_type *key);\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##nsearch(a_rbt_type *rbtree, const a_type *key);\t\t\\\na_attr a_type *\t\t\t\t\t\t\t\t\\\na_prefix##psearch(a_rbt_type *rbtree, const a_type *key);\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);\t\t\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *),\t\\\n  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 comparison 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 bool\n *   ex_empty(ex_t *tree);\n *       Description: Determine whether tree is empty.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *       Ret: True if tree is empty, false otherwise.\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, const 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, const ex_node_t *key);\n *   static ex_node_t *\n *   ex_psearch(ex_t *tree, const 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 *   static void\n *   ex_destroy(ex_t *tree, void (*cb)(ex_node_t *, void *), void *arg);\n *       Description: Iterate over the tree with post-order traversal, remove\n *                    each node, and run the callback if non-null.  This is\n *                    used for destroying a tree without paying the cost to\n *                    rebalance it.  The tree must not be otherwise altered\n *                    during traversal.\n *       Args:\n *         tree: Pointer to an initialized red-black tree object.\n *         cb  : Callback function, which, if non-null, is called for each node\n *               during iteration.  There is no way to stop iteration once it\n *               has begun.\n *         arg : Opaque pointer passed to cb().\n */\n#define rb_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 bool\t\t\t\t\t\t\t\t\\\na_prefix##empty(a_rbt_type *rbtree) {\t\t\t\t\t\\\n    return (rbtree->rbt_root == NULL);\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    return ret;\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##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    return ret;\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##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) != NULL) {\t\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 != NULL);\t\t\t\t\t\t\\\n\tret = NULL;\t\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 != 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    return ret;\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##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) != NULL) {\t\t\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 != NULL);\t\t\t\t\t\t\\\n\tret = NULL;\t\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 != 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    return ret;\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##search(a_rbt_type *rbtree, const a_type *key) {\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 != NULL\t\t\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    return ret;\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##nsearch(a_rbt_type *rbtree, const a_type *key) {\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 = NULL;\t\t\t\t\t\t\t\t\\\n    while (tnode != NULL) {\t\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    return ret;\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##psearch(a_rbt_type *rbtree, const a_type *key) {\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 = NULL;\t\t\t\t\t\t\t\t\\\n    while (tnode != NULL) {\t\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    return ret;\t\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 != NULL; pathp++) {\t\t\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 (leftleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  leftleft)) {\t\t\t\t\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 (left != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  left)) {\t\t\t\t\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 != NULL; pathp++) {\t\t\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 != NULL; pathp++) {\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 != NULL) {\t\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));\t\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 = NULL;\t\t\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, NULL);\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 = NULL;\t\t\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    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 (rightleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  rightleft)) {\t\t\t\t\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 (rightleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  rightleft)) {\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    /*          (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    /* 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    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 (leftrightleft != NULL && rbtn_red_get(a_type,\t\\\n\t\t  a_field, leftrightleft)) {\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    /* (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 != NULL);\t\t\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 (leftleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  leftleft)) {\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    /* (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 (leftleft != NULL && rbtn_red_get(a_type, a_field,\t\\\n\t\t  leftleft)) {\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    /*        (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));\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_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 == NULL) {\t\t\t\t\t\t\t\\\n\treturn NULL;\t\t\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)) != NULL || (ret = cb(rbtree, node,\t\\\n\t  arg)) != NULL) {\t\t\t\t\t\t\\\n\t    return ret;\t\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)) != NULL ||\t\\\n\t  (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return ret;\t\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\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    return ret;\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_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 == NULL) {\t\t\t\t\t\t\t\\\n\treturn NULL;\t\t\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)) != NULL ||\t\\\n\t  (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return ret;\t\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)) != NULL ||\t\\\n\t  (ret = cb(rbtree, node, arg)) != NULL) {\t\t\t\\\n\t    return ret;\t\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\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    return ret;\t\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##destroy_recurse(a_rbt_type *rbtree, a_type *node, void (*cb)(\t\\\n  a_type *, void *), void *arg) {\t\t\t\t\t\\\n    if (node == NULL) {\t\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    a_prefix##destroy_recurse(rbtree, rbtn_left_get(a_type, a_field,\t\\\n      node), cb, arg);\t\t\t\t\t\t\t\\\n    rbtn_left_set(a_type, a_field, (node), NULL);\t\t\t\\\n    a_prefix##destroy_recurse(rbtree, rbtn_right_get(a_type, a_field,\t\\\n      node), cb, arg);\t\t\t\t\t\t\t\\\n    rbtn_right_set(a_type, a_field, (node), NULL);\t\t\t\\\n    if (cb) {\t\t\t\t\t\t\t\t\\\n\tcb(node, arg);\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\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *),\t\\\n  void *arg) {\t\t\t\t\t\t\t\t\\\n    a_prefix##destroy_recurse(rbtree, rbtree->rbt_root, cb, arg);\t\\\n    rbtree->rbt_root = NULL;\t\t\t\t\t\t\\\n}\n\n#endif /* RB_H_ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/rtree.h",
    "content": "#ifndef JEMALLOC_INTERNAL_RTREE_H\n#define JEMALLOC_INTERNAL_RTREE_H\n\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree_tsd.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/tsd.h\"\n\n/*\n * This radix tree implementation is tailored to the singular purpose of\n * associating metadata with extents that are currently owned by jemalloc.\n *\n *******************************************************************************\n */\n\n/* Number of high insignificant bits. */\n#define RTREE_NHIB ((1U << (LG_SIZEOF_PTR+3)) - LG_VADDR)\n/* Number of low insigificant bits. */\n#define RTREE_NLIB LG_PAGE\n/* Number of significant bits. */\n#define RTREE_NSB (LG_VADDR - RTREE_NLIB)\n/* Number of levels in radix tree. */\n#if RTREE_NSB <= 10\n#  define RTREE_HEIGHT 1\n#elif RTREE_NSB <= 36\n#  define RTREE_HEIGHT 2\n#elif RTREE_NSB <= 52\n#  define RTREE_HEIGHT 3\n#else\n#  error Unsupported number of significant virtual address bits\n#endif\n/* Use compact leaf representation if virtual address encoding allows. */\n#if RTREE_NHIB >= LG_CEIL_NSIZES\n#  define RTREE_LEAF_COMPACT\n#endif\n\n/* Needed for initialization only. */\n#define RTREE_LEAFKEY_INVALID ((uintptr_t)1)\n\ntypedef struct rtree_node_elm_s rtree_node_elm_t;\nstruct rtree_node_elm_s {\n\tatomic_p_t\tchild; /* (rtree_{node,leaf}_elm_t *) */\n};\n\nstruct rtree_leaf_elm_s {\n#ifdef RTREE_LEAF_COMPACT\n\t/*\n\t * Single pointer-width field containing all three leaf element fields.\n\t * For example, on a 64-bit x64 system with 48 significant virtual\n\t * memory address bits, the index, extent, and slab fields are packed as\n\t * such:\n\t *\n\t * x: index\n\t * e: extent\n\t * b: slab\n\t *\n\t *   00000000 xxxxxxxx eeeeeeee [...] eeeeeeee eeee000b\n\t */\n\tatomic_p_t\tle_bits;\n#else\n\tatomic_p_t\tle_extent; /* (extent_t *) */\n\tatomic_u_t\tle_szind; /* (szind_t) */\n\tatomic_b_t\tle_slab; /* (bool) */\n#endif\n};\n\ntypedef struct rtree_level_s rtree_level_t;\nstruct rtree_level_s {\n\t/* Number of key bits distinguished by this level. */\n\tunsigned\t\tbits;\n\t/*\n\t * Cumulative number of key bits distinguished by traversing to\n\t * corresponding tree level.\n\t */\n\tunsigned\t\tcumbits;\n};\n\ntypedef struct rtree_s rtree_t;\nstruct rtree_s {\n\tmalloc_mutex_t\t\tinit_lock;\n\t/* Number of elements based on rtree_levels[0].bits. */\n#if RTREE_HEIGHT > 1\n\trtree_node_elm_t\troot[1U << (RTREE_NSB/RTREE_HEIGHT)];\n#else\n\trtree_leaf_elm_t\troot[1U << (RTREE_NSB/RTREE_HEIGHT)];\n#endif\n};\n\n/*\n * Split the bits into one to three partitions depending on number of\n * significant bits.  It the number of bits does not divide evenly into the\n * number of levels, place one remainder bit per level starting at the leaf\n * level.\n */\nstatic const rtree_level_t rtree_levels[] = {\n#if RTREE_HEIGHT == 1\n\t{RTREE_NSB, RTREE_NHIB + RTREE_NSB}\n#elif RTREE_HEIGHT == 2\n\t{RTREE_NSB/2, RTREE_NHIB + RTREE_NSB/2},\n\t{RTREE_NSB/2 + RTREE_NSB%2, RTREE_NHIB + RTREE_NSB}\n#elif RTREE_HEIGHT == 3\n\t{RTREE_NSB/3, RTREE_NHIB + RTREE_NSB/3},\n\t{RTREE_NSB/3 + RTREE_NSB%3/2,\n\t    RTREE_NHIB + RTREE_NSB/3*2 + RTREE_NSB%3/2},\n\t{RTREE_NSB/3 + RTREE_NSB%3 - RTREE_NSB%3/2, RTREE_NHIB + RTREE_NSB}\n#else\n#  error Unsupported rtree height\n#endif\n};\n\nbool rtree_new(rtree_t *rtree, bool zeroed);\n\ntypedef rtree_node_elm_t *(rtree_node_alloc_t)(tsdn_t *, rtree_t *, size_t);\nextern rtree_node_alloc_t *JET_MUTABLE rtree_node_alloc;\n\ntypedef rtree_leaf_elm_t *(rtree_leaf_alloc_t)(tsdn_t *, rtree_t *, size_t);\nextern rtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc;\n\ntypedef void (rtree_node_dalloc_t)(tsdn_t *, rtree_t *, rtree_node_elm_t *);\nextern rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc;\n\ntypedef void (rtree_leaf_dalloc_t)(tsdn_t *, rtree_t *, rtree_leaf_elm_t *);\nextern rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc;\n#ifdef JEMALLOC_JET\nvoid rtree_delete(tsdn_t *tsdn, rtree_t *rtree);\n#endif\nrtree_leaf_elm_t *rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_ctx_t *rtree_ctx, uintptr_t key, bool dependent, bool init_missing);\n\nJEMALLOC_ALWAYS_INLINE uintptr_t\nrtree_leafkey(uintptr_t key) {\n\tunsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);\n\tunsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -\n\t    rtree_levels[RTREE_HEIGHT-1].bits);\n\tunsigned maskbits = ptrbits - cumbits;\n\tuintptr_t mask = ~((ZU(1) << maskbits) - 1);\n\treturn (key & mask);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nrtree_cache_direct_map(uintptr_t key) {\n\tunsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);\n\tunsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -\n\t    rtree_levels[RTREE_HEIGHT-1].bits);\n\tunsigned maskbits = ptrbits - cumbits;\n\treturn (size_t)((key >> maskbits) & (RTREE_CTX_NCACHE - 1));\n}\n\nJEMALLOC_ALWAYS_INLINE uintptr_t\nrtree_subkey(uintptr_t key, unsigned level) {\n\tunsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);\n\tunsigned cumbits = rtree_levels[level].cumbits;\n\tunsigned shiftbits = ptrbits - cumbits;\n\tunsigned maskbits = rtree_levels[level].bits;\n\tuintptr_t mask = (ZU(1) << maskbits) - 1;\n\treturn ((key >> shiftbits) & mask);\n}\n\n/*\n * Atomic getters.\n *\n * dependent: Reading a value on behalf of a pointer to a valid allocation\n *            is guaranteed to be a clean read even without synchronization,\n *            because the rtree update became visible in memory before the\n *            pointer came into existence.\n * !dependent: An arbitrary read, e.g. on behalf of ivsalloc(), may not be\n *             dependent on a previous rtree write, which means a stale read\n *             could result if synchronization were omitted here.\n */\n#  ifdef RTREE_LEAF_COMPACT\nJEMALLOC_ALWAYS_INLINE uintptr_t\nrtree_leaf_elm_bits_read(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,\n    bool dependent) {\n\treturn (uintptr_t)atomic_load_p(&elm->le_bits, dependent\n\t    ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);\n}\n\nJEMALLOC_ALWAYS_INLINE extent_t *\nrtree_leaf_elm_bits_extent_get(uintptr_t bits) {\n#    ifdef __aarch64__\n\t/*\n\t * aarch64 doesn't sign extend the highest virtual address bit to set\n\t * the higher ones.  Instead, the high bits gets zeroed.\n\t */\n\tuintptr_t high_bit_mask = ((uintptr_t)1 << LG_VADDR) - 1;\n\t/* Mask off the slab bit. */\n\tuintptr_t low_bit_mask = ~(uintptr_t)1;\n\tuintptr_t mask = high_bit_mask & low_bit_mask;\n\treturn (extent_t *)(bits & mask);\n#    else\n\t/* Restore sign-extended high bits, mask slab bit. */\n\treturn (extent_t *)((uintptr_t)((intptr_t)(bits << RTREE_NHIB) >>\n\t    RTREE_NHIB) & ~((uintptr_t)0x1));\n#    endif\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nrtree_leaf_elm_bits_szind_get(uintptr_t bits) {\n\treturn (szind_t)(bits >> LG_VADDR);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_leaf_elm_bits_slab_get(uintptr_t bits) {\n\treturn (bool)(bits & (uintptr_t)0x1);\n}\n\n#  endif\n\nJEMALLOC_ALWAYS_INLINE extent_t *\nrtree_leaf_elm_extent_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool dependent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\treturn rtree_leaf_elm_bits_extent_get(bits);\n#else\n\textent_t *extent = (extent_t *)atomic_load_p(&elm->le_extent, dependent\n\t    ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);\n\treturn extent;\n#endif\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nrtree_leaf_elm_szind_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool dependent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\treturn rtree_leaf_elm_bits_szind_get(bits);\n#else\n\treturn (szind_t)atomic_load_u(&elm->le_szind, dependent ? ATOMIC_RELAXED\n\t    : ATOMIC_ACQUIRE);\n#endif\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_leaf_elm_slab_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool dependent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\treturn rtree_leaf_elm_bits_slab_get(bits);\n#else\n\treturn atomic_load_b(&elm->le_slab, dependent ? ATOMIC_RELAXED :\n\t    ATOMIC_ACQUIRE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_extent_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, extent_t *extent) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, true);\n\tuintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<\n\t    LG_VADDR) | ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1))\n\t    | ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\tatomic_store_p(&elm->le_extent, extent, ATOMIC_RELEASE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_szind_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, szind_t szind) {\n\tassert(szind <= NSIZES);\n\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,\n\t    true);\n\tuintptr_t bits = ((uintptr_t)szind << LG_VADDR) |\n\t    ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &\n\t    (((uintptr_t)0x1 << LG_VADDR) - 1)) |\n\t    ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\tatomic_store_u(&elm->le_szind, szind, ATOMIC_RELEASE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_slab_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,\n    rtree_leaf_elm_t *elm, bool slab) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,\n\t    true);\n\tuintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<\n\t    LG_VADDR) | ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &\n\t    (((uintptr_t)0x1 << LG_VADDR) - 1)) | ((uintptr_t)slab);\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\tatomic_store_b(&elm->le_slab, slab, ATOMIC_RELEASE);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,\n    extent_t *extent, szind_t szind, bool slab) {\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = ((uintptr_t)szind << LG_VADDR) |\n\t    ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1)) |\n\t    ((uintptr_t)slab);\n\tatomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);\n#else\n\trtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);\n\trtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);\n\t/*\n\t * Write extent last, since the element is atomically considered valid\n\t * as soon as the extent field is non-NULL.\n\t */\n\trtree_leaf_elm_extent_write(tsdn, rtree, elm, extent);\n#endif\n}\n\nstatic inline void\nrtree_leaf_elm_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_leaf_elm_t *elm, szind_t szind, bool slab) {\n\tassert(!slab || szind < NBINS);\n\n\t/*\n\t * The caller implicitly assures that it is the only writer to the szind\n\t * and slab fields, and that the extent field cannot currently change.\n\t */\n\trtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);\n\trtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *\nrtree_leaf_elm_lookup(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, bool init_missing) {\n\tassert(key != 0);\n\tassert(!dependent || !init_missing);\n\n\tsize_t slot = rtree_cache_direct_map(key);\n\tuintptr_t leafkey = rtree_leafkey(key);\n\tassert(leafkey != RTREE_LEAFKEY_INVALID);\n\n\t/* Fast path: L1 direct mapped cache. */\n\tif (likely(rtree_ctx->cache[slot].leafkey == leafkey)) {\n\t\trtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;\n\t\tassert(leaf != NULL);\n\t\tuintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);\n\t\treturn &leaf[subkey];\n\t}\n\t/*\n\t * Search the L2 LRU cache.  On hit, swap the matching element into the\n\t * slot in L1 cache, and move the position in L2 up by 1.\n\t */\n#define RTREE_CACHE_CHECK_L2(i) do {\t\t\t\t\t\\\n\tif (likely(rtree_ctx->l2_cache[i].leafkey == leafkey)) {\t\\\n\t\trtree_leaf_elm_t *leaf = rtree_ctx->l2_cache[i].leaf;\t\\\n\t\tassert(leaf != NULL);\t\t\t\t\t\\\n\t\tif (i > 0) {\t\t\t\t\t\t\\\n\t\t\t/* Bubble up by one. */\t\t\t\t\\\n\t\t\trtree_ctx->l2_cache[i].leafkey =\t\t\\\n\t\t\t\trtree_ctx->l2_cache[i - 1].leafkey;\t\\\n\t\t\trtree_ctx->l2_cache[i].leaf =\t\t\t\\\n\t\t\t\trtree_ctx->l2_cache[i - 1].leaf;\t\\\n\t\t\trtree_ctx->l2_cache[i - 1].leafkey =\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leafkey;\t\t\\\n\t\t\trtree_ctx->l2_cache[i - 1].leaf =\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leaf;\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\trtree_ctx->l2_cache[0].leafkey =\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leafkey;\t\t\\\n\t\t\trtree_ctx->l2_cache[0].leaf =\t\t\t\\\n\t\t\t    rtree_ctx->cache[slot].leaf;\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\trtree_ctx->cache[slot].leafkey = leafkey;\t\t\\\n\t\trtree_ctx->cache[slot].leaf = leaf;\t\t\t\\\n\t\tuintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);\t\\\n\t\treturn &leaf[subkey];\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\t/* Check the first cache entry. */\n\tRTREE_CACHE_CHECK_L2(0);\n\t/* Search the remaining cache elements. */\n\tfor (unsigned i = 1; i < RTREE_CTX_NCACHE_L2; i++) {\n\t\tRTREE_CACHE_CHECK_L2(i);\n\t}\n#undef RTREE_CACHE_CHECK_L2\n\n\treturn rtree_leaf_elm_lookup_hard(tsdn, rtree, rtree_ctx, key,\n\t    dependent, init_missing);\n}\n\nstatic inline bool\nrtree_write(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,\n    extent_t *extent, szind_t szind, bool slab) {\n\t/* Use rtree_clear() to set the extent to NULL. */\n\tassert(extent != NULL);\n\n\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,\n\t    key, false, true);\n\tif (elm == NULL) {\n\t\treturn true;\n\t}\n\n\tassert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) == NULL);\n\trtree_leaf_elm_write(tsdn, rtree, elm, extent, szind, slab);\n\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *\nrtree_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,\n    bool dependent) {\n\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,\n\t    key, dependent, false);\n\tif (!dependent && elm == NULL) {\n\t\treturn NULL;\n\t}\n\tassert(elm != NULL);\n\treturn elm;\n}\n\nJEMALLOC_ALWAYS_INLINE extent_t *\nrtree_extent_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn NULL;\n\t}\n\treturn rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nrtree_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn NSIZES;\n\t}\n\treturn rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);\n}\n\n/*\n * rtree_slab_read() is intentionally omitted because slab is always read in\n * conjunction with szind, which makes rtree_szind_slab_read() a better choice.\n */\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_extent_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, extent_t **r_extent, szind_t *r_szind) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn true;\n\t}\n\t*r_extent = rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);\n\t*r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nrtree_szind_slab_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, szind_t *r_szind, bool *r_slab) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,\n\t    dependent);\n\tif (!dependent && elm == NULL) {\n\t\treturn true;\n\t}\n#ifdef RTREE_LEAF_COMPACT\n\tuintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);\n\t*r_szind = rtree_leaf_elm_bits_szind_get(bits);\n\t*r_slab = rtree_leaf_elm_bits_slab_get(bits);\n#else\n\t*r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);\n\t*r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, dependent);\n#endif\n\treturn false;\n}\n\nstatic inline void\nrtree_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, szind_t szind, bool slab) {\n\tassert(!slab || szind < NBINS);\n\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);\n\trtree_leaf_elm_szind_slab_update(tsdn, rtree, elm, szind, slab);\n}\n\nstatic inline void\nrtree_clear(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key) {\n\trtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);\n\tassert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) !=\n\t    NULL);\n\trtree_leaf_elm_write(tsdn, rtree, elm, NULL, NSIZES, false);\n}\n\n#endif /* JEMALLOC_INTERNAL_RTREE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/rtree_tsd.h",
    "content": "#ifndef JEMALLOC_INTERNAL_RTREE_CTX_H\n#define JEMALLOC_INTERNAL_RTREE_CTX_H\n\n/*\n * Number of leafkey/leaf pairs to cache in L1 and L2 level respectively.  Each\n * entry supports an entire leaf, so the cache hit rate is typically high even\n * with a small number of entries.  In rare cases extent activity will straddle\n * the boundary between two leaf nodes.  Furthermore, an arena may use a\n * combination of dss and mmap.  Note that as memory usage grows past the amount\n * that this cache can directly cover, the cache will become less effective if\n * locality of reference is low, but the consequence is merely cache misses\n * while traversing the tree nodes.\n *\n * The L1 direct mapped cache offers consistent and low cost on cache hit.\n * However collision could affect hit rate negatively.  This is resolved by\n * combining with a L2 LRU cache, which requires linear search and re-ordering\n * on access but suffers no collision.  Note that, the cache will itself suffer\n * cache misses if made overly large, plus the cost of linear search in the LRU\n * cache.\n */\n#define RTREE_CTX_LG_NCACHE 4\n#define RTREE_CTX_NCACHE (1 << RTREE_CTX_LG_NCACHE)\n#define RTREE_CTX_NCACHE_L2 8\n\n/*\n * Zero initializer required for tsd initialization only.  Proper initialization\n * done via rtree_ctx_data_init().\n */\n#define RTREE_CTX_ZERO_INITIALIZER {{{0}}, {{0}}}\n\n\ntypedef struct rtree_leaf_elm_s rtree_leaf_elm_t;\n\ntypedef struct rtree_ctx_cache_elm_s rtree_ctx_cache_elm_t;\nstruct rtree_ctx_cache_elm_s {\n\tuintptr_t\t\tleafkey;\n\trtree_leaf_elm_t\t*leaf;\n};\n\ntypedef struct rtree_ctx_s rtree_ctx_t;\nstruct rtree_ctx_s {\n\t/* Direct mapped cache. */\n\trtree_ctx_cache_elm_t\tcache[RTREE_CTX_NCACHE];\n\t/* L2 LRU cache. */\n\trtree_ctx_cache_elm_t\tl2_cache[RTREE_CTX_NCACHE_L2];\n};\n\nvoid rtree_ctx_data_init(rtree_ctx_t *ctx);\n\n#endif /* JEMALLOC_INTERNAL_RTREE_CTX_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/size_classes.sh",
    "content": "#!/bin/sh\n#\n# Usage: size_classes.sh <lg_qarr> <lg_tmin> <lg_parr> <lg_g>\n\n# The following limits are chosen such that they cover all supported platforms.\n\n# Pointer sizes.\nlg_zarr=\"2 3\"\n\n# Quanta.\nlg_qarr=$1\n\n# The range of tiny size classes is [2^lg_tmin..2^(lg_q-1)].\nlg_tmin=$2\n\n# Maximum lookup size.\nlg_kmax=12\n\n# Page sizes.\nlg_parr=`echo $3 | tr ',' ' '`\n\n# Size class group size (number of size classes for each size doubling).\nlg_g=$4\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\nlg() {\n  x=$1\n  lg_result=0\n  while [ ${x} -gt 1 ] ; do\n    lg_result=$((${lg_result} + 1))\n    x=$((${x} / 2))\n  done\n}\n\nlg_ceil() {\n  y=$1\n  lg ${y}; lg_floor=${lg_result}\n  pow2 ${lg_floor}; pow2_floor=${pow2_result}\n  if [ ${pow2_floor} -lt ${y} ] ; then\n    lg_ceil_result=$((${lg_floor} + 1))\n  else\n    lg_ceil_result=${lg_floor}\n  fi\n}\n\nreg_size_compute() {\n  lg_grp=$1\n  lg_delta=$2\n  ndelta=$3\n\n  pow2 ${lg_grp}; grp=${pow2_result}\n  pow2 ${lg_delta}; delta=${pow2_result}\n  reg_size=$((${grp} + ${delta}*${ndelta}))\n}\n\nslab_size() {\n  lg_p=$1\n  lg_grp=$2\n  lg_delta=$3\n  ndelta=$4\n\n  pow2 ${lg_p}; p=${pow2_result}\n  reg_size_compute ${lg_grp} ${lg_delta} ${ndelta}\n\n  # Compute smallest slab size that is an integer multiple of reg_size.\n  try_slab_size=${p}\n  try_nregs=$((${try_slab_size} / ${reg_size}))\n  perfect=0\n  while [ ${perfect} -eq 0 ] ; do\n    perfect_slab_size=${try_slab_size}\n    perfect_nregs=${try_nregs}\n\n    try_slab_size=$((${try_slab_size} + ${p}))\n    try_nregs=$((${try_slab_size} / ${reg_size}))\n    if [ ${perfect_slab_size} -eq $((${perfect_nregs} * ${reg_size})) ] ; then\n      perfect=1\n    fi\n  done\n\n  slab_size_pgs=$((${perfect_slab_size} / ${p}))\n}\n\nsize_class() {\n  index=$1\n  lg_grp=$2\n  lg_delta=$3\n  ndelta=$4\n  lg_p=$5\n  lg_kmax=$6\n\n  if [ ${lg_delta} -ge ${lg_p} ] ; then\n    psz=\"yes\"\n  else\n    pow2 ${lg_p}; p=${pow2_result}\n    pow2 ${lg_grp}; grp=${pow2_result}\n    pow2 ${lg_delta}; delta=${pow2_result}\n    sz=$((${grp} + ${delta} * ${ndelta}))\n    npgs=$((${sz} / ${p}))\n    if [ ${sz} -eq $((${npgs} * ${p})) ] ; then\n      psz=\"yes\"\n    else\n      psz=\"no\"\n    fi\n  fi\n\n  lg ${ndelta}; lg_ndelta=${lg_result}; pow2 ${lg_ndelta}\n  if [ ${pow2_result} -lt ${ndelta} ] ; then\n    rem=\"yes\"\n  else\n    rem=\"no\"\n  fi\n\n  lg_size=${lg_grp}\n  if [ $((${lg_delta} + ${lg_ndelta})) -eq ${lg_grp} ] ; then\n    lg_size=$((${lg_grp} + 1))\n  else\n    lg_size=${lg_grp}\n    rem=\"yes\"\n  fi\n\n  if [ ${lg_size} -lt $((${lg_p} + ${lg_g})) ] ; then\n    bin=\"yes\"\n    slab_size ${lg_p} ${lg_grp} ${lg_delta} ${ndelta}; pgs=${slab_size_pgs}\n  else\n    bin=\"no\"\n    pgs=0\n  fi\n  if [ ${lg_size} -lt ${lg_kmax} \\\n      -o ${lg_size} -eq ${lg_kmax} -a ${rem} = \"no\" ] ; then\n    lg_delta_lookup=${lg_delta}\n  else\n    lg_delta_lookup=\"no\"\n  fi\n  printf '    SC(%3d, %6d, %8d, %6d, %3s, %3s, %3d, %2s) \\\\\\n' ${index} ${lg_grp} ${lg_delta} ${ndelta} ${psz} ${bin} ${pgs} ${lg_delta_lookup}\n  # Defined upon return:\n  # - psz (\"yes\" or \"no\")\n  # - bin (\"yes\" or \"no\")\n  # - pgs\n  # - lg_delta_lookup (${lg_delta} or \"no\")\n}\n\nsep_line() {\n  echo \"                                                         \\\\\"\n}\n\nsize_classes() {\n  lg_z=$1\n  lg_q=$2\n  lg_t=$3\n  lg_p=$4\n  lg_g=$5\n\n  pow2 $((${lg_z} + 3)); ptr_bits=${pow2_result}\n  pow2 ${lg_g}; g=${pow2_result}\n\n  echo \"#define SIZE_CLASSES \\\\\"\n  echo \"  /* index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup */ \\\\\"\n\n  ntbins=0\n  nlbins=0\n  lg_tiny_maxclass='\"NA\"'\n  nbins=0\n  npsizes=0\n\n  # Tiny size classes.\n  ndelta=0\n  index=0\n  lg_grp=${lg_t}\n  lg_delta=${lg_grp}\n  while [ ${lg_grp} -lt ${lg_q} ] ; do\n    size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n    if [ ${lg_delta_lookup} != \"no\" ] ; then\n      nlbins=$((${index} + 1))\n    fi\n    if [ ${psz} = \"yes\" ] ; then\n      npsizes=$((${npsizes} + 1))\n    fi\n    if [ ${bin} != \"no\" ] ; then\n      nbins=$((${index} + 1))\n    fi\n    ntbins=$((${ntbins} + 1))\n    lg_tiny_maxclass=${lg_grp} # Final written value is correct.\n    index=$((${index} + 1))\n    lg_delta=${lg_grp}\n    lg_grp=$((${lg_grp} + 1))\n  done\n\n  # First non-tiny group.\n  if [ ${ntbins} -gt 0 ] ; then\n    sep_line\n    # The first size class has an unusual encoding, because the size has to be\n    # split between grp and delta*ndelta.\n    lg_grp=$((${lg_grp} - 1))\n    ndelta=1\n    size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n    index=$((${index} + 1))\n    lg_grp=$((${lg_grp} + 1))\n    lg_delta=$((${lg_delta} + 1))\n    if [ ${psz} = \"yes\" ] ; then\n      npsizes=$((${npsizes} + 1))\n    fi\n  fi\n  while [ ${ndelta} -lt ${g} ] ; do\n    size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n    index=$((${index} + 1))\n    ndelta=$((${ndelta} + 1))\n    if [ ${psz} = \"yes\" ] ; then\n      npsizes=$((${npsizes} + 1))\n    fi\n  done\n\n  # All remaining groups.\n  lg_grp=$((${lg_grp} + ${lg_g}))\n  while [ ${lg_grp} -lt $((${ptr_bits} - 1)) ] ; do\n    sep_line\n    ndelta=1\n    if [ ${lg_grp} -eq $((${ptr_bits} - 2)) ] ; then\n      ndelta_limit=$((${g} - 1))\n    else\n      ndelta_limit=${g}\n    fi\n    while [ ${ndelta} -le ${ndelta_limit} ] ; do\n      size_class ${index} ${lg_grp} ${lg_delta} ${ndelta} ${lg_p} ${lg_kmax}\n      if [ ${lg_delta_lookup} != \"no\" ] ; then\n        nlbins=$((${index} + 1))\n        # Final written value is correct:\n        lookup_maxclass=\"((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))\"\n      fi\n      if [ ${psz} = \"yes\" ] ; then\n        npsizes=$((${npsizes} + 1))\n      fi\n      if [ ${bin} != \"no\" ] ; then\n        nbins=$((${index} + 1))\n        # Final written value is correct:\n        small_maxclass=\"((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))\"\n        if [ ${lg_g} -gt 0 ] ; then\n          lg_large_minclass=$((${lg_grp} + 1))\n        else\n          lg_large_minclass=$((${lg_grp} + 2))\n        fi\n      fi\n      # Final written value is correct:\n      large_maxclass=\"((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))\"\n      index=$((${index} + 1))\n      ndelta=$((${ndelta} + 1))\n    done\n    lg_grp=$((${lg_grp} + 1))\n    lg_delta=$((${lg_delta} + 1))\n  done\n  echo\n  nsizes=${index}\n  lg_ceil ${nsizes}; lg_ceil_nsizes=${lg_ceil_result}\n\n  # Defined upon completion:\n  # - ntbins\n  # - nlbins\n  # - nbins\n  # - nsizes\n  # - lg_ceil_nsizes\n  # - npsizes\n  # - lg_tiny_maxclass\n  # - lookup_maxclass\n  # - small_maxclass\n  # - lg_large_minclass\n  # - large_maxclass\n}\n\ncat <<EOF\n#ifndef JEMALLOC_INTERNAL_SIZE_CLASSES_H\n#define JEMALLOC_INTERNAL_SIZE_CLASSES_H\n\n/* This file was automatically generated by size_classes.sh. */\n\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n\n/*\n * This header file defines:\n *\n *   LG_SIZE_CLASS_GROUP: Lg of size class count for each size doubling.\n *   LG_TINY_MIN: Lg of minimum size class to support.\n *   SIZE_CLASSES: Complete table of SC(index, lg_grp, lg_delta, ndelta, psz,\n *                 bin, pgs, lg_delta_lookup) tuples.\n *     index: Size class index.\n *     lg_grp: Lg group base size (no deltas added).\n *     lg_delta: Lg delta to previous size class.\n *     ndelta: Delta multiplier.  size == 1<<lg_grp + ndelta<<lg_delta\n *     psz: 'yes' if a multiple of the page size, 'no' otherwise.\n *     bin: 'yes' if a small bin size class, 'no' otherwise.\n *     pgs: Slab page count if a small bin size class, 0 otherwise.\n *     lg_delta_lookup: Same as lg_delta if a lookup table size class, 'no'\n *                      otherwise.\n *   NTBINS: Number of tiny bins.\n *   NLBINS: Number of bins supported by the lookup table.\n *   NBINS: Number of small size class bins.\n *   NSIZES: Number of size classes.\n *   LG_CEIL_NSIZES: Number of bits required to store NSIZES.\n *   NPSIZES: Number of size classes that are a multiple of (1U << LG_PAGE).\n *   LG_TINY_MAXCLASS: Lg of maximum tiny size class.\n *   LOOKUP_MAXCLASS: Maximum size class included in lookup table.\n *   SMALL_MAXCLASS: Maximum small size class.\n *   LG_LARGE_MINCLASS: Lg of minimum large size class.\n *   LARGE_MAXCLASS: Maximum (large) size class.\n */\n\n#define LG_SIZE_CLASS_GROUP\t${lg_g}\n#define LG_TINY_MIN\t\t${lg_tmin}\n\nEOF\n\nfor lg_z in ${lg_zarr} ; do\n  for lg_q in ${lg_qarr} ; do\n    lg_t=${lg_tmin}\n    while [ ${lg_t} -le ${lg_q} ] ; do\n      # Iterate through page sizes and compute how many bins there are.\n      for lg_p in ${lg_parr} ; do\n        echo \"#if (LG_SIZEOF_PTR == ${lg_z} && LG_TINY_MIN == ${lg_t} && LG_QUANTUM == ${lg_q} && LG_PAGE == ${lg_p})\"\n        size_classes ${lg_z} ${lg_q} ${lg_t} ${lg_p} ${lg_g}\n        echo \"#define SIZE_CLASSES_DEFINED\"\n        echo \"#define NTBINS\t\t\t${ntbins}\"\n        echo \"#define NLBINS\t\t\t${nlbins}\"\n        echo \"#define NBINS\t\t\t${nbins}\"\n        echo \"#define NSIZES\t\t\t${nsizes}\"\n        echo \"#define LG_CEIL_NSIZES\t\t${lg_ceil_nsizes}\"\n        echo \"#define NPSIZES\t\t\t${npsizes}\"\n        echo \"#define LG_TINY_MAXCLASS\t${lg_tiny_maxclass}\"\n        echo \"#define LOOKUP_MAXCLASS\t\t${lookup_maxclass}\"\n        echo \"#define SMALL_MAXCLASS\t\t${small_maxclass}\"\n        echo \"#define LG_LARGE_MINCLASS\t${lg_large_minclass}\"\n        echo \"#define LARGE_MINCLASS\t\t(ZU(1) << LG_LARGE_MINCLASS)\"\n        echo \"#define LARGE_MAXCLASS\t\t${large_maxclass}\"\n        echo \"#endif\"\n        echo\n      done\n      lg_t=$((${lg_t} + 1))\n    done\n  done\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 size2index_tab lookup table uses uint8_t to encode each bin index, so we\n * cannot support more than 256 small size classes.\n */\n#if (NBINS > 256)\n#  error \"Too many small size classes\"\n#endif\n\n#endif /* JEMALLOC_INTERNAL_SIZE_CLASSES_H */\nEOF\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/smoothstep.h",
    "content": "#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H\n#define JEMALLOC_INTERNAL_SMOOTHSTEP_H\n\n/*\n * This file was generated by the following command:\n *   sh smoothstep.sh smoother 200 24 3 15\n */\n/******************************************************************************/\n\n/*\n * This header defines a precomputed table based on the smoothstep family of\n * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0\n * to 1 in 0 <= x <= 1.  The table is stored as integer fixed point values so\n * that floating point math can be avoided.\n *\n *                      3     2\n *   smoothstep(x) = -2x  + 3x\n *\n *                       5      4      3\n *   smootherstep(x) = 6x  - 15x  + 10x\n *\n *                          7      6      5      4\n *   smootheststep(x) = -20x  + 70x  - 84x  + 35x\n */\n\n#define SMOOTHSTEP_VARIANT\t\"smoother\"\n#define SMOOTHSTEP_NSTEPS\t200\n#define SMOOTHSTEP_BFP\t\t24\n#define SMOOTHSTEP \\\n /* STEP(step, h,                            x,     y) */ \\\n    STEP(   1, UINT64_C(0x0000000000000014), 0.005, 0.000001240643750) \\\n    STEP(   2, UINT64_C(0x00000000000000a5), 0.010, 0.000009850600000) \\\n    STEP(   3, UINT64_C(0x0000000000000229), 0.015, 0.000032995181250) \\\n    STEP(   4, UINT64_C(0x0000000000000516), 0.020, 0.000077619200000) \\\n    STEP(   5, UINT64_C(0x00000000000009dc), 0.025, 0.000150449218750) \\\n    STEP(   6, UINT64_C(0x00000000000010e8), 0.030, 0.000257995800000) \\\n    STEP(   7, UINT64_C(0x0000000000001aa4), 0.035, 0.000406555756250) \\\n    STEP(   8, UINT64_C(0x0000000000002777), 0.040, 0.000602214400000) \\\n    STEP(   9, UINT64_C(0x00000000000037c2), 0.045, 0.000850847793750) \\\n    STEP(  10, UINT64_C(0x0000000000004be6), 0.050, 0.001158125000000) \\\n    STEP(  11, UINT64_C(0x000000000000643c), 0.055, 0.001529510331250) \\\n    STEP(  12, UINT64_C(0x000000000000811f), 0.060, 0.001970265600000) \\\n    STEP(  13, UINT64_C(0x000000000000a2e2), 0.065, 0.002485452368750) \\\n    STEP(  14, UINT64_C(0x000000000000c9d8), 0.070, 0.003079934200000) \\\n    STEP(  15, UINT64_C(0x000000000000f64f), 0.075, 0.003758378906250) \\\n    STEP(  16, UINT64_C(0x0000000000012891), 0.080, 0.004525260800000) \\\n    STEP(  17, UINT64_C(0x00000000000160e7), 0.085, 0.005384862943750) \\\n    STEP(  18, UINT64_C(0x0000000000019f95), 0.090, 0.006341279400000) \\\n    STEP(  19, UINT64_C(0x000000000001e4dc), 0.095, 0.007398417481250) \\\n    STEP(  20, UINT64_C(0x00000000000230fc), 0.100, 0.008560000000000) \\\n    STEP(  21, UINT64_C(0x0000000000028430), 0.105, 0.009829567518750) \\\n    STEP(  22, UINT64_C(0x000000000002deb0), 0.110, 0.011210480600000) \\\n    STEP(  23, UINT64_C(0x00000000000340b1), 0.115, 0.012705922056250) \\\n    STEP(  24, UINT64_C(0x000000000003aa67), 0.120, 0.014318899200000) \\\n    STEP(  25, UINT64_C(0x0000000000041c00), 0.125, 0.016052246093750) \\\n    STEP(  26, UINT64_C(0x00000000000495a8), 0.130, 0.017908625800000) \\\n    STEP(  27, UINT64_C(0x000000000005178b), 0.135, 0.019890532631250) \\\n    STEP(  28, UINT64_C(0x000000000005a1cf), 0.140, 0.022000294400000) \\\n    STEP(  29, UINT64_C(0x0000000000063498), 0.145, 0.024240074668750) \\\n    STEP(  30, UINT64_C(0x000000000006d009), 0.150, 0.026611875000000) \\\n    STEP(  31, UINT64_C(0x000000000007743f), 0.155, 0.029117537206250) \\\n    STEP(  32, UINT64_C(0x0000000000082157), 0.160, 0.031758745600000) \\\n    STEP(  33, UINT64_C(0x000000000008d76b), 0.165, 0.034537029243750) \\\n    STEP(  34, UINT64_C(0x0000000000099691), 0.170, 0.037453764200000) \\\n    STEP(  35, UINT64_C(0x00000000000a5edf), 0.175, 0.040510175781250) \\\n    STEP(  36, UINT64_C(0x00000000000b3067), 0.180, 0.043707340800000) \\\n    STEP(  37, UINT64_C(0x00000000000c0b38), 0.185, 0.047046189818750) \\\n    STEP(  38, UINT64_C(0x00000000000cef5e), 0.190, 0.050527509400000) \\\n    STEP(  39, UINT64_C(0x00000000000ddce6), 0.195, 0.054151944356250) \\\n    STEP(  40, UINT64_C(0x00000000000ed3d8), 0.200, 0.057920000000000) \\\n    STEP(  41, UINT64_C(0x00000000000fd439), 0.205, 0.061832044393750) \\\n    STEP(  42, UINT64_C(0x000000000010de0e), 0.210, 0.065888310600000) \\\n    STEP(  43, UINT64_C(0x000000000011f158), 0.215, 0.070088898931250) \\\n    STEP(  44, UINT64_C(0x0000000000130e17), 0.220, 0.074433779200000) \\\n    STEP(  45, UINT64_C(0x0000000000143448), 0.225, 0.078922792968750) \\\n    STEP(  46, UINT64_C(0x00000000001563e7), 0.230, 0.083555655800000) \\\n    STEP(  47, UINT64_C(0x0000000000169cec), 0.235, 0.088331959506250) \\\n    STEP(  48, UINT64_C(0x000000000017df4f), 0.240, 0.093251174400000) \\\n    STEP(  49, UINT64_C(0x0000000000192b04), 0.245, 0.098312651543750) \\\n    STEP(  50, UINT64_C(0x00000000001a8000), 0.250, 0.103515625000000) \\\n    STEP(  51, UINT64_C(0x00000000001bde32), 0.255, 0.108859214081250) \\\n    STEP(  52, UINT64_C(0x00000000001d458b), 0.260, 0.114342425600000) \\\n    STEP(  53, UINT64_C(0x00000000001eb5f8), 0.265, 0.119964156118750) \\\n    STEP(  54, UINT64_C(0x0000000000202f65), 0.270, 0.125723194200000) \\\n    STEP(  55, UINT64_C(0x000000000021b1bb), 0.275, 0.131618222656250) \\\n    STEP(  56, UINT64_C(0x0000000000233ce3), 0.280, 0.137647820800000) \\\n    STEP(  57, UINT64_C(0x000000000024d0c3), 0.285, 0.143810466693750) \\\n    STEP(  58, UINT64_C(0x0000000000266d40), 0.290, 0.150104539400000) \\\n    STEP(  59, UINT64_C(0x000000000028123d), 0.295, 0.156528321231250) \\\n    STEP(  60, UINT64_C(0x000000000029bf9c), 0.300, 0.163080000000000) \\\n    STEP(  61, UINT64_C(0x00000000002b753d), 0.305, 0.169757671268750) \\\n    STEP(  62, UINT64_C(0x00000000002d32fe), 0.310, 0.176559340600000) \\\n    STEP(  63, UINT64_C(0x00000000002ef8bc), 0.315, 0.183482925806250) \\\n    STEP(  64, UINT64_C(0x000000000030c654), 0.320, 0.190526259200000) \\\n    STEP(  65, UINT64_C(0x0000000000329b9f), 0.325, 0.197687089843750) \\\n    STEP(  66, UINT64_C(0x0000000000347875), 0.330, 0.204963085800000) \\\n    STEP(  67, UINT64_C(0x0000000000365cb0), 0.335, 0.212351836381250) \\\n    STEP(  68, UINT64_C(0x0000000000384825), 0.340, 0.219850854400000) \\\n    STEP(  69, UINT64_C(0x00000000003a3aa8), 0.345, 0.227457578418750) \\\n    STEP(  70, UINT64_C(0x00000000003c340f), 0.350, 0.235169375000000) \\\n    STEP(  71, UINT64_C(0x00000000003e342b), 0.355, 0.242983540956250) \\\n    STEP(  72, UINT64_C(0x0000000000403ace), 0.360, 0.250897305600000) \\\n    STEP(  73, UINT64_C(0x00000000004247c8), 0.365, 0.258907832993750) \\\n    STEP(  74, UINT64_C(0x0000000000445ae9), 0.370, 0.267012224200000) \\\n    STEP(  75, UINT64_C(0x0000000000467400), 0.375, 0.275207519531250) \\\n    STEP(  76, UINT64_C(0x00000000004892d8), 0.380, 0.283490700800000) \\\n    STEP(  77, UINT64_C(0x00000000004ab740), 0.385, 0.291858693568750) \\\n    STEP(  78, UINT64_C(0x00000000004ce102), 0.390, 0.300308369400000) \\\n    STEP(  79, UINT64_C(0x00000000004f0fe9), 0.395, 0.308836548106250) \\\n    STEP(  80, UINT64_C(0x00000000005143bf), 0.400, 0.317440000000000) \\\n    STEP(  81, UINT64_C(0x0000000000537c4d), 0.405, 0.326115448143750) \\\n    STEP(  82, UINT64_C(0x000000000055b95b), 0.410, 0.334859570600000) \\\n    STEP(  83, UINT64_C(0x000000000057fab1), 0.415, 0.343669002681250) \\\n    STEP(  84, UINT64_C(0x00000000005a4015), 0.420, 0.352540339200000) \\\n    STEP(  85, UINT64_C(0x00000000005c894e), 0.425, 0.361470136718750) \\\n    STEP(  86, UINT64_C(0x00000000005ed622), 0.430, 0.370454915800000) \\\n    STEP(  87, UINT64_C(0x0000000000612655), 0.435, 0.379491163256250) \\\n    STEP(  88, UINT64_C(0x00000000006379ac), 0.440, 0.388575334400000) \\\n    STEP(  89, UINT64_C(0x000000000065cfeb), 0.445, 0.397703855293750) \\\n    STEP(  90, UINT64_C(0x00000000006828d6), 0.450, 0.406873125000000) \\\n    STEP(  91, UINT64_C(0x00000000006a842f), 0.455, 0.416079517831250) \\\n    STEP(  92, UINT64_C(0x00000000006ce1bb), 0.460, 0.425319385600000) \\\n    STEP(  93, UINT64_C(0x00000000006f413a), 0.465, 0.434589059868750) \\\n    STEP(  94, UINT64_C(0x000000000071a270), 0.470, 0.443884854200000) \\\n    STEP(  95, UINT64_C(0x000000000074051d), 0.475, 0.453203066406250) \\\n    STEP(  96, UINT64_C(0x0000000000766905), 0.480, 0.462539980800000) \\\n    STEP(  97, UINT64_C(0x000000000078cde7), 0.485, 0.471891870443750) \\\n    STEP(  98, UINT64_C(0x00000000007b3387), 0.490, 0.481254999400000) \\\n    STEP(  99, UINT64_C(0x00000000007d99a4), 0.495, 0.490625624981250) \\\n    STEP( 100, UINT64_C(0x0000000000800000), 0.500, 0.500000000000000) \\\n    STEP( 101, UINT64_C(0x000000000082665b), 0.505, 0.509374375018750) \\\n    STEP( 102, UINT64_C(0x000000000084cc78), 0.510, 0.518745000600000) \\\n    STEP( 103, UINT64_C(0x0000000000873218), 0.515, 0.528108129556250) \\\n    STEP( 104, UINT64_C(0x00000000008996fa), 0.520, 0.537460019200000) \\\n    STEP( 105, UINT64_C(0x00000000008bfae2), 0.525, 0.546796933593750) \\\n    STEP( 106, UINT64_C(0x00000000008e5d8f), 0.530, 0.556115145800000) \\\n    STEP( 107, UINT64_C(0x000000000090bec5), 0.535, 0.565410940131250) \\\n    STEP( 108, UINT64_C(0x0000000000931e44), 0.540, 0.574680614400000) \\\n    STEP( 109, UINT64_C(0x0000000000957bd0), 0.545, 0.583920482168750) \\\n    STEP( 110, UINT64_C(0x000000000097d729), 0.550, 0.593126875000000) \\\n    STEP( 111, UINT64_C(0x00000000009a3014), 0.555, 0.602296144706250) \\\n    STEP( 112, UINT64_C(0x00000000009c8653), 0.560, 0.611424665600000) \\\n    STEP( 113, UINT64_C(0x00000000009ed9aa), 0.565, 0.620508836743750) \\\n    STEP( 114, UINT64_C(0x0000000000a129dd), 0.570, 0.629545084200000) \\\n    STEP( 115, UINT64_C(0x0000000000a376b1), 0.575, 0.638529863281250) \\\n    STEP( 116, UINT64_C(0x0000000000a5bfea), 0.580, 0.647459660800000) \\\n    STEP( 117, UINT64_C(0x0000000000a8054e), 0.585, 0.656330997318750) \\\n    STEP( 118, UINT64_C(0x0000000000aa46a4), 0.590, 0.665140429400000) \\\n    STEP( 119, UINT64_C(0x0000000000ac83b2), 0.595, 0.673884551856250) \\\n    STEP( 120, UINT64_C(0x0000000000aebc40), 0.600, 0.682560000000000) \\\n    STEP( 121, UINT64_C(0x0000000000b0f016), 0.605, 0.691163451893750) \\\n    STEP( 122, UINT64_C(0x0000000000b31efd), 0.610, 0.699691630600000) \\\n    STEP( 123, UINT64_C(0x0000000000b548bf), 0.615, 0.708141306431250) \\\n    STEP( 124, UINT64_C(0x0000000000b76d27), 0.620, 0.716509299200000) \\\n    STEP( 125, UINT64_C(0x0000000000b98c00), 0.625, 0.724792480468750) \\\n    STEP( 126, UINT64_C(0x0000000000bba516), 0.630, 0.732987775800000) \\\n    STEP( 127, UINT64_C(0x0000000000bdb837), 0.635, 0.741092167006250) \\\n    STEP( 128, UINT64_C(0x0000000000bfc531), 0.640, 0.749102694400000) \\\n    STEP( 129, UINT64_C(0x0000000000c1cbd4), 0.645, 0.757016459043750) \\\n    STEP( 130, UINT64_C(0x0000000000c3cbf0), 0.650, 0.764830625000000) \\\n    STEP( 131, UINT64_C(0x0000000000c5c557), 0.655, 0.772542421581250) \\\n    STEP( 132, UINT64_C(0x0000000000c7b7da), 0.660, 0.780149145600000) \\\n    STEP( 133, UINT64_C(0x0000000000c9a34f), 0.665, 0.787648163618750) \\\n    STEP( 134, UINT64_C(0x0000000000cb878a), 0.670, 0.795036914200000) \\\n    STEP( 135, UINT64_C(0x0000000000cd6460), 0.675, 0.802312910156250) \\\n    STEP( 136, UINT64_C(0x0000000000cf39ab), 0.680, 0.809473740800000) \\\n    STEP( 137, UINT64_C(0x0000000000d10743), 0.685, 0.816517074193750) \\\n    STEP( 138, UINT64_C(0x0000000000d2cd01), 0.690, 0.823440659400000) \\\n    STEP( 139, UINT64_C(0x0000000000d48ac2), 0.695, 0.830242328731250) \\\n    STEP( 140, UINT64_C(0x0000000000d64063), 0.700, 0.836920000000000) \\\n    STEP( 141, UINT64_C(0x0000000000d7edc2), 0.705, 0.843471678768750) \\\n    STEP( 142, UINT64_C(0x0000000000d992bf), 0.710, 0.849895460600000) \\\n    STEP( 143, UINT64_C(0x0000000000db2f3c), 0.715, 0.856189533306250) \\\n    STEP( 144, UINT64_C(0x0000000000dcc31c), 0.720, 0.862352179200000) \\\n    STEP( 145, UINT64_C(0x0000000000de4e44), 0.725, 0.868381777343750) \\\n    STEP( 146, UINT64_C(0x0000000000dfd09a), 0.730, 0.874276805800000) \\\n    STEP( 147, UINT64_C(0x0000000000e14a07), 0.735, 0.880035843881250) \\\n    STEP( 148, UINT64_C(0x0000000000e2ba74), 0.740, 0.885657574400000) \\\n    STEP( 149, UINT64_C(0x0000000000e421cd), 0.745, 0.891140785918750) \\\n    STEP( 150, UINT64_C(0x0000000000e58000), 0.750, 0.896484375000000) \\\n    STEP( 151, UINT64_C(0x0000000000e6d4fb), 0.755, 0.901687348456250) \\\n    STEP( 152, UINT64_C(0x0000000000e820b0), 0.760, 0.906748825600000) \\\n    STEP( 153, UINT64_C(0x0000000000e96313), 0.765, 0.911668040493750) \\\n    STEP( 154, UINT64_C(0x0000000000ea9c18), 0.770, 0.916444344200000) \\\n    STEP( 155, UINT64_C(0x0000000000ebcbb7), 0.775, 0.921077207031250) \\\n    STEP( 156, UINT64_C(0x0000000000ecf1e8), 0.780, 0.925566220800000) \\\n    STEP( 157, UINT64_C(0x0000000000ee0ea7), 0.785, 0.929911101068750) \\\n    STEP( 158, UINT64_C(0x0000000000ef21f1), 0.790, 0.934111689400000) \\\n    STEP( 159, UINT64_C(0x0000000000f02bc6), 0.795, 0.938167955606250) \\\n    STEP( 160, UINT64_C(0x0000000000f12c27), 0.800, 0.942080000000000) \\\n    STEP( 161, UINT64_C(0x0000000000f22319), 0.805, 0.945848055643750) \\\n    STEP( 162, UINT64_C(0x0000000000f310a1), 0.810, 0.949472490600000) \\\n    STEP( 163, UINT64_C(0x0000000000f3f4c7), 0.815, 0.952953810181250) \\\n    STEP( 164, UINT64_C(0x0000000000f4cf98), 0.820, 0.956292659200000) \\\n    STEP( 165, UINT64_C(0x0000000000f5a120), 0.825, 0.959489824218750) \\\n    STEP( 166, UINT64_C(0x0000000000f6696e), 0.830, 0.962546235800000) \\\n    STEP( 167, UINT64_C(0x0000000000f72894), 0.835, 0.965462970756250) \\\n    STEP( 168, UINT64_C(0x0000000000f7dea8), 0.840, 0.968241254400000) \\\n    STEP( 169, UINT64_C(0x0000000000f88bc0), 0.845, 0.970882462793750) \\\n    STEP( 170, UINT64_C(0x0000000000f92ff6), 0.850, 0.973388125000000) \\\n    STEP( 171, UINT64_C(0x0000000000f9cb67), 0.855, 0.975759925331250) \\\n    STEP( 172, UINT64_C(0x0000000000fa5e30), 0.860, 0.977999705600000) \\\n    STEP( 173, UINT64_C(0x0000000000fae874), 0.865, 0.980109467368750) \\\n    STEP( 174, UINT64_C(0x0000000000fb6a57), 0.870, 0.982091374200000) \\\n    STEP( 175, UINT64_C(0x0000000000fbe400), 0.875, 0.983947753906250) \\\n    STEP( 176, UINT64_C(0x0000000000fc5598), 0.880, 0.985681100800000) \\\n    STEP( 177, UINT64_C(0x0000000000fcbf4e), 0.885, 0.987294077943750) \\\n    STEP( 178, UINT64_C(0x0000000000fd214f), 0.890, 0.988789519400000) \\\n    STEP( 179, UINT64_C(0x0000000000fd7bcf), 0.895, 0.990170432481250) \\\n    STEP( 180, UINT64_C(0x0000000000fdcf03), 0.900, 0.991440000000000) \\\n    STEP( 181, UINT64_C(0x0000000000fe1b23), 0.905, 0.992601582518750) \\\n    STEP( 182, UINT64_C(0x0000000000fe606a), 0.910, 0.993658720600000) \\\n    STEP( 183, UINT64_C(0x0000000000fe9f18), 0.915, 0.994615137056250) \\\n    STEP( 184, UINT64_C(0x0000000000fed76e), 0.920, 0.995474739200000) \\\n    STEP( 185, UINT64_C(0x0000000000ff09b0), 0.925, 0.996241621093750) \\\n    STEP( 186, UINT64_C(0x0000000000ff3627), 0.930, 0.996920065800000) \\\n    STEP( 187, UINT64_C(0x0000000000ff5d1d), 0.935, 0.997514547631250) \\\n    STEP( 188, UINT64_C(0x0000000000ff7ee0), 0.940, 0.998029734400000) \\\n    STEP( 189, UINT64_C(0x0000000000ff9bc3), 0.945, 0.998470489668750) \\\n    STEP( 190, UINT64_C(0x0000000000ffb419), 0.950, 0.998841875000000) \\\n    STEP( 191, UINT64_C(0x0000000000ffc83d), 0.955, 0.999149152206250) \\\n    STEP( 192, UINT64_C(0x0000000000ffd888), 0.960, 0.999397785600000) \\\n    STEP( 193, UINT64_C(0x0000000000ffe55b), 0.965, 0.999593444243750) \\\n    STEP( 194, UINT64_C(0x0000000000ffef17), 0.970, 0.999742004200000) \\\n    STEP( 195, UINT64_C(0x0000000000fff623), 0.975, 0.999849550781250) \\\n    STEP( 196, UINT64_C(0x0000000000fffae9), 0.980, 0.999922380800000) \\\n    STEP( 197, UINT64_C(0x0000000000fffdd6), 0.985, 0.999967004818750) \\\n    STEP( 198, UINT64_C(0x0000000000ffff5a), 0.990, 0.999990149400000) \\\n    STEP( 199, UINT64_C(0x0000000000ffffeb), 0.995, 0.999998759356250) \\\n    STEP( 200, UINT64_C(0x0000000001000000), 1.000, 1.000000000000000) \\\n\n#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/smoothstep.sh",
    "content": "#!/bin/sh\n#\n# Generate a discrete lookup table for a sigmoid function in the smoothstep\n# family (https://en.wikipedia.org/wiki/Smoothstep), where the lookup table\n# entries correspond to x in [1/nsteps, 2/nsteps, ..., nsteps/nsteps].  Encode\n# the entries using a binary fixed point representation.\n#\n# Usage: smoothstep.sh <variant> <nsteps> <bfp> <xprec> <yprec>\n#\n#        <variant> is in {smooth, smoother, smoothest}.\n#        <nsteps> must be greater than zero.\n#        <bfp> must be in [0..62]; reasonable values are roughly [10..30].\n#        <xprec> is x decimal precision.\n#        <yprec> is y decimal precision.\n\n#set -x\n\ncmd=\"sh smoothstep.sh $*\"\nvariant=$1\nnsteps=$2\nbfp=$3\nxprec=$4\nyprec=$5\n\ncase \"${variant}\" in\n  smooth)\n    ;;\n  smoother)\n    ;;\n  smoothest)\n    ;;\n  *)\n    echo \"Unsupported variant\"\n    exit 1\n    ;;\nesac\n\nsmooth() {\n  step=$1\n  y=`echo ${yprec} k ${step} ${nsteps} / sx _2 lx 3 ^ '*' 3 lx 2 ^ '*' + p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g' | tr '.' ' ' | awk '{print $1}' `\n}\n\nsmoother() {\n  step=$1\n  y=`echo ${yprec} k ${step} ${nsteps} / sx 6 lx 5 ^ '*' _15 lx 4 ^ '*' + 10 lx 3 ^ '*' + p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g' | tr '.' ' ' | awk '{print $1}' `\n}\n\nsmoothest() {\n  step=$1\n  y=`echo ${yprec} k ${step} ${nsteps} / sx _20 lx 7 ^ '*' 70 lx 6 ^ '*' + _84 lx 5 ^ '*' + 35 lx 4 ^ '*' + p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g' | tr '.' ' ' | awk '{print $1}' `\n}\n\ncat <<EOF\n#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H\n#define JEMALLOC_INTERNAL_SMOOTHSTEP_H\n\n/*\n * This file was generated by the following command:\n *   $cmd\n */\n/******************************************************************************/\n\n/*\n * This header defines a precomputed table based on the smoothstep family of\n * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0\n * to 1 in 0 <= x <= 1.  The table is stored as integer fixed point values so\n * that floating point math can be avoided.\n *\n *                      3     2\n *   smoothstep(x) = -2x  + 3x\n *\n *                       5      4      3\n *   smootherstep(x) = 6x  - 15x  + 10x\n *\n *                          7      6      5      4\n *   smootheststep(x) = -20x  + 70x  - 84x  + 35x\n */\n\n#define SMOOTHSTEP_VARIANT\t\"${variant}\"\n#define SMOOTHSTEP_NSTEPS\t${nsteps}\n#define SMOOTHSTEP_BFP\t\t${bfp}\n#define SMOOTHSTEP \\\\\n /* STEP(step, h,                            x,     y) */ \\\\\nEOF\n\ns=1\nwhile [ $s -le $nsteps ] ; do\n  $variant ${s}\n  x=`echo ${xprec} k ${s} ${nsteps} / p | dc | tr -d '\\\\\\\\\\n' | sed -e 's#^\\.#0.#g'`\n  printf '    STEP(%4d, UINT64_C(0x%016x), %s, %s) \\\\\\n' ${s} ${h} ${x} ${y}\n\n  s=$((s+1))\ndone\necho\n\ncat <<EOF\n#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */\nEOF\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/spin.h",
    "content": "#ifndef JEMALLOC_INTERNAL_SPIN_H\n#define JEMALLOC_INTERNAL_SPIN_H\n\n#define SPIN_INITIALIZER {0U}\n\ntypedef struct {\n\tunsigned iteration;\n} spin_t;\n\nstatic inline void\nspin_cpu_spinwait() {\n#  if HAVE_CPU_SPINWAIT\n\tCPU_SPINWAIT;\n#  else\n\tvolatile int x = 0;\n\tx = x;\n#  endif\n}\n\nstatic inline void\nspin_adaptive(spin_t *spin) {\n\tvolatile uint32_t i;\n\n\tif (spin->iteration < 5) {\n\t\tfor (i = 0; i < (1U << spin->iteration); i++) {\n\t\t\tspin_cpu_spinwait();\n\t\t}\n\t\tspin->iteration++;\n\t} else {\n#ifdef _WIN32\n\t\tSwitchToThread();\n#else\n\t\tsched_yield();\n#endif\n\t}\n}\n\n#undef SPIN_INLINE\n\n#endif /* JEMALLOC_INTERNAL_SPIN_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/stats.h",
    "content": "#ifndef JEMALLOC_INTERNAL_STATS_H\n#define JEMALLOC_INTERNAL_STATS_H\n\n/*  OPTION(opt,\t\tvar_name,\tdefault,\tset_value_to) */\n#define STATS_PRINT_OPTIONS\t\t\t\t\t\t\\\n    OPTION('J',\t\tjson,\t\tfalse,\t\ttrue)\t\t\\\n    OPTION('g',\t\tgeneral,\ttrue,\t\tfalse)\t\t\\\n    OPTION('m',\t\tmerged,\t\tconfig_stats,\tfalse)\t\t\\\n    OPTION('d',\t\tdestroyed,\tconfig_stats,\tfalse)\t\t\\\n    OPTION('a',\t\tunmerged,\tconfig_stats,\tfalse)\t\t\\\n    OPTION('b',\t\tbins,\t\ttrue,\t\tfalse)\t\t\\\n    OPTION('l',\t\tlarge,\t\ttrue,\t\tfalse)\t\t\\\n    OPTION('x',\t\tmutex,\t\ttrue,\t\tfalse)\n\nenum {\n#define OPTION(o, v, d, s) stats_print_option_num_##v,\n    STATS_PRINT_OPTIONS\n#undef OPTION\n    stats_print_tot_num_options\n};\n\n/* Options for stats_print. */\nextern bool opt_stats_print;\nextern char opt_stats_print_opts[stats_print_tot_num_options+1];\n\n/* Implements je_malloc_stats_print. */\nvoid stats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts);\n\n#endif /* JEMALLOC_INTERNAL_STATS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/sz.h",
    "content": "#ifndef JEMALLOC_INTERNAL_SIZE_H\n#define JEMALLOC_INTERNAL_SIZE_H\n\n#include \"jemalloc/internal/bit_util.h\"\n#include \"jemalloc/internal/pages.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/util.h\"\n\n/*\n * sz module: Size computations.\n *\n * Some abbreviations used here:\n *   p: Page\n *   ind: Index\n *   s, sz: Size\n *   u: Usable size\n *   a: Aligned\n *\n * These are not always used completely consistently, but should be enough to\n * interpret function names.  E.g. sz_psz2ind converts page size to page size\n * index; sz_sa2u converts a (size, alignment) allocation request to the usable\n * size that would result from such an allocation.\n */\n\n/*\n * sz_pind2sz_tab encodes the same information as could be computed by\n * sz_pind2sz_compute().\n */\nextern size_t const sz_pind2sz_tab[NPSIZES+1];\n/*\n * sz_index2size_tab encodes the same information as could be computed (at\n * unacceptable cost in some code paths) by sz_index2size_compute().\n */\nextern size_t const sz_index2size_tab[NSIZES];\n/*\n * sz_size2index_tab 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 sz_size2index().\n */\nextern uint8_t const sz_size2index_tab[];\n\nstatic const size_t sz_large_pad =\n#ifdef JEMALLOC_CACHE_OBLIVIOUS\n    PAGE\n#else\n    0\n#endif\n    ;\n\nJEMALLOC_ALWAYS_INLINE pszind_t\nsz_psz2ind(size_t psz) {\n\tif (unlikely(psz > LARGE_MAXCLASS)) {\n\t\treturn NPSIZES;\n\t}\n\t{\n\t\tpszind_t x = lg_floor((psz<<1)-1);\n\t\tpszind_t shift = (x < LG_SIZE_CLASS_GROUP + LG_PAGE) ? 0 : x -\n\t\t    (LG_SIZE_CLASS_GROUP + LG_PAGE);\n\t\tpszind_t grp = shift << LG_SIZE_CLASS_GROUP;\n\n\t\tpszind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?\n\t\t    LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;\n\n\t\tsize_t delta_inverse_mask = ZU(-1) << lg_delta;\n\t\tpszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &\n\t\t    ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);\n\n\t\tpszind_t ind = grp + mod;\n\t\treturn ind;\n\t}\n}\n\nstatic inline size_t\nsz_pind2sz_compute(pszind_t pind) {\n\tif (unlikely(pind == NPSIZES)) {\n\t\treturn LARGE_MAXCLASS + PAGE;\n\t}\n\t{\n\t\tsize_t grp = pind >> LG_SIZE_CLASS_GROUP;\n\t\tsize_t mod = pind & ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);\n\n\t\tsize_t grp_size_mask = ~((!!grp)-1);\n\t\tsize_t grp_size = ((ZU(1) << (LG_PAGE +\n\t\t    (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;\n\n\t\tsize_t shift = (grp == 0) ? 1 : grp;\n\t\tsize_t lg_delta = shift + (LG_PAGE-1);\n\t\tsize_t mod_size = (mod+1) << lg_delta;\n\n\t\tsize_t sz = grp_size + mod_size;\n\t\treturn sz;\n\t}\n}\n\nstatic inline size_t\nsz_pind2sz_lookup(pszind_t pind) {\n\tsize_t ret = (size_t)sz_pind2sz_tab[pind];\n\tassert(ret == sz_pind2sz_compute(pind));\n\treturn ret;\n}\n\nstatic inline size_t\nsz_pind2sz(pszind_t pind) {\n\tassert(pind < NPSIZES+1);\n\treturn sz_pind2sz_lookup(pind);\n}\n\nstatic inline size_t\nsz_psz2u(size_t psz) {\n\tif (unlikely(psz > LARGE_MAXCLASS)) {\n\t\treturn LARGE_MAXCLASS + PAGE;\n\t}\n\t{\n\t\tsize_t x = lg_floor((psz<<1)-1);\n\t\tsize_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?\n\t\t    LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;\n\t\tsize_t delta = ZU(1) << lg_delta;\n\t\tsize_t delta_mask = delta - 1;\n\t\tsize_t usize = (psz + delta_mask) & ~delta_mask;\n\t\treturn usize;\n\t}\n}\n\nstatic inline szind_t\nsz_size2index_compute(size_t size) {\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\treturn NSIZES;\n\t}\n#if (NTBINS != 0)\n\tif (size <= (ZU(1) << LG_TINY_MAXCLASS)) {\n\t\tszind_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;\n\t\tszind_t lg_ceil = lg_floor(pow2_ceil_zu(size));\n\t\treturn (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin);\n\t}\n#endif\n\t{\n\t\tszind_t x = lg_floor((size<<1)-1);\n\t\tszind_t shift = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM) ? 0 :\n\t\t    x - (LG_SIZE_CLASS_GROUP + LG_QUANTUM);\n\t\tszind_t grp = shift << LG_SIZE_CLASS_GROUP;\n\n\t\tszind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)\n\t\t    ? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;\n\n\t\tsize_t delta_inverse_mask = ZU(-1) << lg_delta;\n\t\tszind_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) &\n\t\t    ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);\n\n\t\tszind_t index = NTBINS + grp + mod;\n\t\treturn index;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nsz_size2index_lookup(size_t size) {\n\tassert(size <= LOOKUP_MAXCLASS);\n\t{\n\t\tszind_t ret = (sz_size2index_tab[(size-1) >> LG_TINY_MIN]);\n\t\tassert(ret == sz_size2index_compute(size));\n\t\treturn ret;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE szind_t\nsz_size2index(size_t size) {\n\tassert(size > 0);\n\tif (likely(size <= LOOKUP_MAXCLASS)) {\n\t\treturn sz_size2index_lookup(size);\n\t}\n\treturn sz_size2index_compute(size);\n}\n\nstatic inline size_t\nsz_index2size_compute(szind_t index) {\n#if (NTBINS > 0)\n\tif (index < NTBINS) {\n\t\treturn (ZU(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + index));\n\t}\n#endif\n\t{\n\t\tsize_t reduced_index = index - NTBINS;\n\t\tsize_t grp = reduced_index >> LG_SIZE_CLASS_GROUP;\n\t\tsize_t mod = reduced_index & ((ZU(1) << LG_SIZE_CLASS_GROUP) -\n\t\t    1);\n\n\t\tsize_t grp_size_mask = ~((!!grp)-1);\n\t\tsize_t grp_size = ((ZU(1) << (LG_QUANTUM +\n\t\t    (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;\n\n\t\tsize_t shift = (grp == 0) ? 1 : grp;\n\t\tsize_t lg_delta = shift + (LG_QUANTUM-1);\n\t\tsize_t mod_size = (mod+1) << lg_delta;\n\n\t\tsize_t usize = grp_size + mod_size;\n\t\treturn usize;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_index2size_lookup(szind_t index) {\n\tsize_t ret = (size_t)sz_index2size_tab[index];\n\tassert(ret == sz_index2size_compute(index));\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_index2size(szind_t index) {\n\tassert(index < NSIZES);\n\treturn sz_index2size_lookup(index);\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_s2u_compute(size_t size) {\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\treturn 0;\n\t}\n#if (NTBINS > 0)\n\tif (size <= (ZU(1) << LG_TINY_MAXCLASS)) {\n\t\tsize_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;\n\t\tsize_t lg_ceil = lg_floor(pow2_ceil_zu(size));\n\t\treturn (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) :\n\t\t    (ZU(1) << lg_ceil));\n\t}\n#endif\n\t{\n\t\tsize_t x = lg_floor((size<<1)-1);\n\t\tsize_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)\n\t\t    ?  LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;\n\t\tsize_t delta = ZU(1) << lg_delta;\n\t\tsize_t delta_mask = delta - 1;\n\t\tsize_t usize = (size + delta_mask) & ~delta_mask;\n\t\treturn usize;\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nsz_s2u_lookup(size_t size) {\n\tsize_t ret = sz_index2size_lookup(sz_size2index_lookup(size));\n\n\tassert(ret == sz_s2u_compute(size));\n\treturn ret;\n}\n\n/*\n * Compute usable size that would result from allocating an object with the\n * specified size.\n */\nJEMALLOC_ALWAYS_INLINE size_t\nsz_s2u(size_t size) {\n\tassert(size > 0);\n\tif (likely(size <= LOOKUP_MAXCLASS)) {\n\t\treturn sz_s2u_lookup(size);\n\t}\n\treturn sz_s2u_compute(size);\n}\n\n/*\n * Compute usable size that would result from allocating an object with the\n * specified size and alignment.\n */\nJEMALLOC_ALWAYS_INLINE size_t\nsz_sa2u(size_t size, size_t alignment) {\n\tsize_t usize;\n\n\tassert(alignment != 0 && ((alignment - 1) & alignment) == 0);\n\n\t/* Try for a small size class. */\n\tif (size <= SMALL_MAXCLASS && alignment < PAGE) {\n\t\t/*\n\t\t * Round size up to the nearest multiple of alignment.\n\t\t *\n\t\t * This done, we can take advantage of the fact that for each\n\t\t * small size class, every object is aligned at the smallest\n\t\t * power of two that is non-zero in the base two representation\n\t\t * of the size.  For example:\n\t\t *\n\t\t *   Size |   Base 2 | Minimum alignment\n\t\t *   -----+----------+------------------\n\t\t *     96 |  1100000 |  32\n\t\t *    144 | 10100000 |  32\n\t\t *    192 | 11000000 |  64\n\t\t */\n\t\tusize = sz_s2u(ALIGNMENT_CEILING(size, alignment));\n\t\tif (usize < LARGE_MINCLASS) {\n\t\t\treturn usize;\n\t\t}\n\t}\n\n\t/* Large size class.  Beware of overflow. */\n\n\tif (unlikely(alignment > LARGE_MAXCLASS)) {\n\t\treturn 0;\n\t}\n\n\t/* Make sure result is a large size class. */\n\tif (size <= LARGE_MINCLASS) {\n\t\tusize = LARGE_MINCLASS;\n\t} else {\n\t\tusize = sz_s2u(size);\n\t\tif (usize < size) {\n\t\t\t/* size_t overflow. */\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/*\n\t * Calculate the multi-page mapping that large_palloc() would need in\n\t * order to guarantee the alignment.\n\t */\n\tif (usize + sz_large_pad + PAGE_CEILING(alignment) - PAGE < usize) {\n\t\t/* size_t overflow. */\n\t\treturn 0;\n\t}\n\treturn usize;\n}\n\n#endif /* JEMALLOC_INTERNAL_SIZE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_externs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_EXTERNS_H\n#define JEMALLOC_INTERNAL_TCACHE_EXTERNS_H\n\n#include \"jemalloc/internal/size_classes.h\"\n\nextern bool\topt_tcache;\nextern ssize_t\topt_lg_tcache_max;\n\nextern cache_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 unsigned\tnhbins;\n\n/* Maximum cached size class. */\nextern size_t\ttcache_maxclass;\n\n/*\n * Explicit tcaches, managed via the tcache.{create,flush,destroy} mallctls and\n * usable via the MALLOCX_TCACHE() flag.  The automatic per thread tcaches are\n * completely disjoint from this data structure.  tcaches starts off as a sparse\n * array, so it has no physical memory footprint until individual pages are\n * touched.  This allows the entire array to be allocated the first time an\n * explicit tcache is created without a disproportionate impact on memory usage.\n */\nextern tcaches_t\t*tcaches;\n\nsize_t\ttcache_salloc(tsdn_t *tsdn, const void *ptr);\nvoid\ttcache_event_hard(tsd_t *tsd, tcache_t *tcache);\nvoid\t*tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, bool *tcache_success);\nvoid\ttcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,\n    szind_t binind, unsigned rem);\nvoid\ttcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,\n    unsigned rem, tcache_t *tcache);\nvoid\ttcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache,\n    arena_t *arena);\ntcache_t *tcache_create_explicit(tsd_t *tsd);\nvoid\ttcache_cleanup(tsd_t *tsd);\nvoid\ttcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);\nbool\ttcaches_create(tsd_t *tsd, unsigned *r_ind);\nvoid\ttcaches_flush(tsd_t *tsd, unsigned ind);\nvoid\ttcaches_destroy(tsd_t *tsd, unsigned ind);\nbool\ttcache_boot(tsdn_t *tsdn);\nvoid tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);\nvoid tcache_prefork(tsdn_t *tsdn);\nvoid tcache_postfork_parent(tsdn_t *tsdn);\nvoid tcache_postfork_child(tsdn_t *tsdn);\nvoid tcache_flush(tsd_t *tsd);\nbool tsd_tcache_data_init(tsd_t *tsd);\nbool tsd_tcache_enabled_data_init(tsd_t *tsd);\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_EXTERNS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_inlines.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_INLINES_H\n#define JEMALLOC_INTERNAL_TCACHE_INLINES_H\n\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/ticker.h\"\n#include \"jemalloc/internal/util.h\"\n\nstatic inline bool\ntcache_enabled_get(tsd_t *tsd) {\n\treturn tsd_tcache_enabled_get(tsd);\n}\n\nstatic inline void\ntcache_enabled_set(tsd_t *tsd, bool enabled) {\n\tbool was_enabled = tsd_tcache_enabled_get(tsd);\n\n\tif (!was_enabled && enabled) {\n\t\ttsd_tcache_data_init(tsd);\n\t} else if (was_enabled && !enabled) {\n\t\ttcache_cleanup(tsd);\n\t}\n\t/* Commit the state last.  Above calls check current state. */\n\ttsd_tcache_enabled_set(tsd, enabled);\n\ttsd_slow_update(tsd);\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntcache_event(tsd_t *tsd, tcache_t *tcache) {\n\tif (TCACHE_GC_INCR == 0) {\n\t\treturn;\n\t}\n\n\tif (unlikely(ticker_tick(&tcache->gc_ticker))) {\n\t\ttcache_event_hard(tsd, tcache);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void *\ntcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,\n    UNUSED size_t size, szind_t binind, bool zero, bool slow_path) {\n\tvoid *ret;\n\tcache_bin_t *bin;\n\tbool tcache_success;\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\n\tassert(binind < NBINS);\n\tbin = tcache_small_bin_get(tcache, binind);\n\tret = cache_bin_alloc_easy(bin, &tcache_success);\n\tassert(tcache_success == (ret != NULL));\n\tif (unlikely(!tcache_success)) {\n\t\tbool tcache_hard_success;\n\t\tarena = arena_choose(tsd, arena);\n\t\tif (unlikely(arena == NULL)) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\tret = tcache_alloc_small_hard(tsd_tsdn(tsd), arena, tcache,\n\t\t    bin, binind, &tcache_hard_success);\n\t\tif (tcache_hard_success == false) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tassert(ret);\n\t/*\n\t * Only compute usize if required.  The checks in the following if\n\t * statement are all static.\n\t */\n\tif (config_prof || (slow_path && config_fill) || unlikely(zero)) {\n\t\tusize = sz_index2size(binind);\n\t\tassert(tcache_salloc(tsd_tsdn(tsd), ret) == usize);\n\t}\n\n\tif (likely(!zero)) {\n\t\tif (slow_path && config_fill) {\n\t\t\tif (unlikely(opt_junk_alloc)) {\n\t\t\t\tarena_alloc_junk_small(ret, &bin_infos[binind],\n\t\t\t\t    false);\n\t\t\t} else if (unlikely(opt_zero)) {\n\t\t\t\tmemset(ret, 0, usize);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (slow_path && config_fill && unlikely(opt_junk_alloc)) {\n\t\t\tarena_alloc_junk_small(ret, &bin_infos[binind], true);\n\t\t}\n\t\tmemset(ret, 0, usize);\n\t}\n\n\tif (config_stats) {\n\t\tbin->tstats.nrequests++;\n\t}\n\tif (config_prof) {\n\t\ttcache->prof_accumbytes += usize;\n\t}\n\ttcache_event(tsd, tcache);\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\ntcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,\n    szind_t binind, bool zero, bool slow_path) {\n\tvoid *ret;\n\tcache_bin_t *bin;\n\tbool tcache_success;\n\n\tassert(binind >= NBINS &&binind < nhbins);\n\tbin = tcache_large_bin_get(tcache, binind);\n\tret = cache_bin_alloc_easy(bin, &tcache_success);\n\tassert(tcache_success == (ret != NULL));\n\tif (unlikely(!tcache_success)) {\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\tarena = arena_choose(tsd, arena);\n\t\tif (unlikely(arena == NULL)) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\tret = large_malloc(tsd_tsdn(tsd), arena, sz_s2u(size), zero);\n\t\tif (ret == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t} else {\n\t\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\n\t\t/* Only compute usize on demand */\n\t\tif (config_prof || (slow_path && config_fill) ||\n\t\t    unlikely(zero)) {\n\t\t\tusize = sz_index2size(binind);\n\t\t\tassert(usize <= tcache_maxclass);\n\t\t}\n\n\t\tif (likely(!zero)) {\n\t\t\tif (slow_path && config_fill) {\n\t\t\t\tif (unlikely(opt_junk_alloc)) {\n\t\t\t\t\tmemset(ret, JEMALLOC_ALLOC_JUNK,\n\t\t\t\t\t    usize);\n\t\t\t\t} else if (unlikely(opt_zero)) {\n\t\t\t\t\tmemset(ret, 0, usize);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tmemset(ret, 0, usize);\n\t\t}\n\n\t\tif (config_stats) {\n\t\t\tbin->tstats.nrequests++;\n\t\t}\n\t\tif (config_prof) {\n\t\t\ttcache->prof_accumbytes += usize;\n\t\t}\n\t}\n\n\ttcache_event(tsd, tcache);\n\treturn ret;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,\n    bool slow_path) {\n\tcache_bin_t *bin;\n\tcache_bin_info_t *bin_info;\n\n\tassert(tcache_salloc(tsd_tsdn(tsd), ptr) <= SMALL_MAXCLASS);\n\n\tif (slow_path && config_fill && unlikely(opt_junk_free)) {\n\t\tarena_dalloc_junk_small(ptr, &bin_infos[binind]);\n\t}\n\n\tbin = tcache_small_bin_get(tcache, binind);\n\tbin_info = &tcache_bin_info[binind];\n\tif (unlikely(bin->ncached == bin_info->ncached_max)) {\n\t\ttcache_bin_flush_small(tsd, tcache, bin, binind,\n\t\t    (bin_info->ncached_max >> 1));\n\t}\n\tassert(bin->ncached < bin_info->ncached_max);\n\tbin->ncached++;\n\t*(bin->avail - bin->ncached) = ptr;\n\n\ttcache_event(tsd, tcache);\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,\n    bool slow_path) {\n\tcache_bin_t *bin;\n\tcache_bin_info_t *bin_info;\n\n\tassert(tcache_salloc(tsd_tsdn(tsd), ptr) > SMALL_MAXCLASS);\n\tassert(tcache_salloc(tsd_tsdn(tsd), ptr) <= tcache_maxclass);\n\n\tif (slow_path && config_fill && unlikely(opt_junk_free)) {\n\t\tlarge_dalloc_junk(ptr, sz_index2size(binind));\n\t}\n\n\tbin = tcache_large_bin_get(tcache, binind);\n\tbin_info = &tcache_bin_info[binind];\n\tif (unlikely(bin->ncached == bin_info->ncached_max)) {\n\t\ttcache_bin_flush_large(tsd, bin, binind,\n\t\t    (bin_info->ncached_max >> 1), tcache);\n\t}\n\tassert(bin->ncached < bin_info->ncached_max);\n\tbin->ncached++;\n\t*(bin->avail - bin->ncached) = ptr;\n\n\ttcache_event(tsd, tcache);\n}\n\nJEMALLOC_ALWAYS_INLINE tcache_t *\ntcaches_get(tsd_t *tsd, unsigned ind) {\n\ttcaches_t *elm = &tcaches[ind];\n\tif (unlikely(elm->tcache == NULL)) {\n\t\telm->tcache = tcache_create_explicit(tsd);\n\t}\n\treturn elm->tcache;\n}\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_INLINES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_structs.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_STRUCTS_H\n#define JEMALLOC_INTERNAL_TCACHE_STRUCTS_H\n\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/cache_bin.h\"\n#include \"jemalloc/internal/ticker.h\"\n\nstruct tcache_s {\n\t/*\n\t * To minimize our cache-footprint, we put the frequently accessed data\n\t * together at the start of this struct.\n\t */\n\n\t/* Cleared after arena_prof_accum(). */\n\tuint64_t\tprof_accumbytes;\n\t/* Drives incremental GC. */\n\tticker_t\tgc_ticker;\n\t/*\n\t * The pointer stacks associated with bins follow as a contiguous array.\n\t * During tcache initialization, the avail pointer in each element of\n\t * tbins is initialized to point to the proper offset within this array.\n\t */\n\tcache_bin_t\tbins_small[NBINS];\n\n\t/*\n\t * This data is less hot; we can be a little less careful with our\n\t * footprint here.\n\t */\n\t/* Lets us track all the tcaches in an arena. */\n\tql_elm(tcache_t) link;\n\t/*\n\t * The descriptor lets the arena find our cache bins without seeing the\n\t * tcache definition.  This enables arenas to aggregate stats across\n\t * tcaches without having a tcache dependency.\n\t */\n\tcache_bin_array_descriptor_t cache_bin_array_descriptor;\n\n\t/* The arena this tcache is associated with. */\n\tarena_t\t\t*arena;\n\t/* Next bin to GC. */\n\tszind_t\t\tnext_gc_bin;\n\t/* For small bins, fill (ncached_max >> lg_fill_div). */\n\tuint8_t\t\tlg_fill_div[NBINS];\n\t/*\n\t * We put the cache bins for large size classes at the end of the\n\t * struct, since some of them might not get used.  This might end up\n\t * letting us avoid touching an extra page if we don't have to.\n\t */\n\tcache_bin_t\tbins_large[NSIZES-NBINS];\n};\n\n/* Linkage for list of available (previously used) explicit tcache IDs. */\nstruct tcaches_s {\n\tunion {\n\t\ttcache_t\t*tcache;\n\t\ttcaches_t\t*next;\n\t};\n};\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_STRUCTS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tcache_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TCACHE_TYPES_H\n#define JEMALLOC_INTERNAL_TCACHE_TYPES_H\n\n#include \"jemalloc/internal/size_classes.h\"\n\ntypedef struct tcache_s tcache_t;\ntypedef struct tcaches_s tcaches_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 TCACHE_STATE_DISABLED\t\t((tcache_t *)(uintptr_t)1)\n#define TCACHE_STATE_REINCARNATED\t((tcache_t *)(uintptr_t)2)\n#define TCACHE_STATE_PURGATORY\t\t((tcache_t *)(uintptr_t)3)\n#define TCACHE_STATE_MAX\t\tTCACHE_STATE_PURGATORY\n\n/*\n * Absolute minimum number of cache slots for each small bin.\n */\n#define TCACHE_NSLOTS_SMALL_MIN\t\t20\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 slab for this size class.\n *\n * This constant must be an even number.\n */\n#define TCACHE_NSLOTS_SMALL_MAX\t\t200\n\n/* Number of cache slots for large size classes. */\n#define TCACHE_NSLOTS_LARGE\t\t20\n\n/* (1U << opt_lg_tcache_max) is used to compute tcache_maxclass. */\n#define LG_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 TCACHE_GC_SWEEP\t\t\t8192\n\n/* Number of tcache allocation/deallocation events between incremental GCs. */\n#define TCACHE_GC_INCR\t\t\t\t\t\t\t\\\n    ((TCACHE_GC_SWEEP / NBINS) + ((TCACHE_GC_SWEEP / NBINS == 0) ? 0 : 1))\n\n/* Used in TSD static initializer only. Real init in tcache_data_init(). */\n#define TCACHE_ZERO_INITIALIZER {0}\n\n/* Used in TSD static initializer only. Will be initialized to opt_tcache. */\n#define TCACHE_ENABLED_ZERO_INITIALIZER false\n\n#endif /* JEMALLOC_INTERNAL_TCACHE_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/ticker.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TICKER_H\n#define JEMALLOC_INTERNAL_TICKER_H\n\n#include \"jemalloc/internal/util.h\"\n\n/**\n * A ticker makes it easy to count-down events until some limit.  You\n * ticker_init the ticker to trigger every nticks events.  You then notify it\n * that an event has occurred with calls to ticker_tick (or that nticks events\n * have occurred with a call to ticker_ticks), which will return true (and reset\n * the counter) if the countdown hit zero.\n */\n\ntypedef struct {\n\tint32_t tick;\n\tint32_t nticks;\n} ticker_t;\n\nstatic inline void\nticker_init(ticker_t *ticker, int32_t nticks) {\n\tticker->tick = nticks;\n\tticker->nticks = nticks;\n}\n\nstatic inline void\nticker_copy(ticker_t *ticker, const ticker_t *other) {\n\t*ticker = *other;\n}\n\nstatic inline int32_t\nticker_read(const ticker_t *ticker) {\n\treturn ticker->tick;\n}\n\n/*\n * Not intended to be a public API.  Unfortunately, on x86, neither gcc nor\n * clang seems smart enough to turn\n *   ticker->tick -= nticks;\n *   if (unlikely(ticker->tick < 0)) {\n *     fixup ticker\n *     return true;\n *   }\n *   return false;\n * into\n *   subq %nticks_reg, (%ticker_reg)\n *   js fixup ticker\n *\n * unless we force \"fixup ticker\" out of line.  In that case, gcc gets it right,\n * but clang now does worse than before.  So, on x86 with gcc, we force it out\n * of line, but otherwise let the inlining occur.  Ordinarily this wouldn't be\n * worth the hassle, but this is on the fast path of both malloc and free (via\n * tcache_event).\n */\n#if defined(__GNUC__) && !defined(__clang__)\t\t\t\t\\\n    && (defined(__x86_64__) || defined(__i386__))\nJEMALLOC_NOINLINE\n#endif\nstatic bool\nticker_fixup(ticker_t *ticker) {\n\tticker->tick = ticker->nticks;\n\treturn true;\n}\n\nstatic inline bool\nticker_ticks(ticker_t *ticker, int32_t nticks) {\n\tticker->tick -= nticks;\n\tif (unlikely(ticker->tick < 0)) {\n\t\treturn ticker_fixup(ticker);\n\t}\n\treturn false;\n}\n\nstatic inline bool\nticker_tick(ticker_t *ticker) {\n\treturn ticker_ticks(ticker, 1);\n}\n\n#endif /* JEMALLOC_INTERNAL_TICKER_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TSD_H\n#define JEMALLOC_INTERNAL_TSD_H\n\n#include \"jemalloc/internal/arena_types.h\"\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/jemalloc_internal_externs.h\"\n#include \"jemalloc/internal/prof_types.h\"\n#include \"jemalloc/internal/ql.h\"\n#include \"jemalloc/internal/rtree_tsd.h\"\n#include \"jemalloc/internal/tcache_types.h\"\n#include \"jemalloc/internal/tcache_structs.h\"\n#include \"jemalloc/internal/util.h\"\n#include \"jemalloc/internal/witness.h\"\n\n/*\n * Thread-Specific-Data layout\n * --- data accessed on tcache fast path: state, rtree_ctx, stats, prof ---\n * s: state\n * e: tcache_enabled\n * m: thread_allocated (config_stats)\n * f: thread_deallocated (config_stats)\n * p: prof_tdata (config_prof)\n * c: rtree_ctx (rtree cache accessed on deallocation)\n * t: tcache\n * --- data not accessed on tcache fast path: arena-related fields ---\n * d: arenas_tdata_bypass\n * r: reentrancy_level\n * x: narenas_tdata\n * i: iarena\n * a: arena\n * o: arenas_tdata\n * Loading TSD data is on the critical path of basically all malloc operations.\n * In particular, tcache and rtree_ctx rely on hot CPU cache to be effective.\n * Use a compact layout to reduce cache footprint.\n * +--- 64-bit and 64B cacheline; 1B each letter; First byte on the left. ---+\n * |----------------------------  1st cacheline  ----------------------------|\n * | sedrxxxx mmmmmmmm ffffffff pppppppp [c * 32  ........ ........ .......] |\n * |----------------------------  2nd cacheline  ----------------------------|\n * | [c * 64  ........ ........ ........ ........ ........ ........ .......] |\n * |----------------------------  3nd cacheline  ----------------------------|\n * | [c * 32  ........ ........ .......] iiiiiiii aaaaaaaa oooooooo [t...... |\n * +-------------------------------------------------------------------------+\n * Note: the entire tcache is embedded into TSD and spans multiple cachelines.\n *\n * The last 3 members (i, a and o) before tcache isn't really needed on tcache\n * fast path.  However we have a number of unused tcache bins and witnesses\n * (never touched unless config_debug) at the end of tcache, so we place them\n * there to avoid breaking the cachelines and possibly paging in an extra page.\n */\n#ifdef JEMALLOC_JET\ntypedef void (*test_callback_t)(int *);\n#  define MALLOC_TSD_TEST_DATA_INIT 0x72b65c10\n#  define MALLOC_TEST_TSD \\\n    O(test_data,\t\tint,\t\t\tint)\t\t\\\n    O(test_callback,\t\ttest_callback_t,\tint)\n#  define MALLOC_TEST_TSD_INITIALIZER , MALLOC_TSD_TEST_DATA_INIT, NULL\n#else\n#  define MALLOC_TEST_TSD\n#  define MALLOC_TEST_TSD_INITIALIZER\n#endif\n\n/*  O(name,\t\t\ttype,\t\t\tnullable type */\n#define MALLOC_TSD\t\t\t\t\t\t\t\\\n    O(tcache_enabled,\t\tbool,\t\t\tbool)\t\t\\\n    O(arenas_tdata_bypass,\tbool,\t\t\tbool)\t\t\\\n    O(reentrancy_level,\t\tint8_t,\t\t\tint8_t)\t\t\\\n    O(narenas_tdata,\t\tuint32_t,\t\tuint32_t)\t\\\n    O(offset_state,\t\tuint64_t,\t\tuint64_t)\t\\\n    O(thread_allocated,\t\tuint64_t,\t\tuint64_t)\t\\\n    O(thread_deallocated,\tuint64_t,\t\tuint64_t)\t\\\n    O(prof_tdata,\t\tprof_tdata_t *,\t\tprof_tdata_t *)\t\\\n    O(rtree_ctx,\t\trtree_ctx_t,\t\trtree_ctx_t)\t\\\n    O(iarena,\t\t\tarena_t *,\t\tarena_t *)\t\\\n    O(arena,\t\t\tarena_t *,\t\tarena_t *)\t\\\n    O(arenas_tdata,\t\tarena_tdata_t *,\tarena_tdata_t *)\\\n    O(tcache,\t\t\ttcache_t,\t\ttcache_t)\t\\\n    O(witness_tsd,              witness_tsd_t,\t\twitness_tsdn_t)\t\\\n    MALLOC_TEST_TSD\n\n#define TSD_INITIALIZER {\t\t\t\t\t\t\\\n    tsd_state_uninitialized,\t\t\t\t\t\t\\\n    TCACHE_ENABLED_ZERO_INITIALIZER,\t\t\t\t\t\\\n    false,\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    0,\t\t\t\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    RTREE_CTX_ZERO_INITIALIZER,\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    NULL,\t\t\t\t\t\t\t\t\\\n    TCACHE_ZERO_INITIALIZER,\t\t\t\t\t\t\\\n    WITNESS_TSD_INITIALIZER\t\t\t\t\t\t\\\n    MALLOC_TEST_TSD_INITIALIZER\t\t\t\t\t\t\\\n}\n\nenum {\n\ttsd_state_nominal = 0, /* Common case --> jnz. */\n\ttsd_state_nominal_slow = 1, /* Initialized but on slow path. */\n\t/* the above 2 nominal states should be lower values. */\n\ttsd_state_nominal_max = 1, /* used for comparison only. */\n\ttsd_state_minimal_initialized = 2,\n\ttsd_state_purgatory = 3,\n\ttsd_state_reincarnated = 4,\n\ttsd_state_uninitialized = 5\n};\n\n/* Manually limit tsd_state_t to a single byte. */\ntypedef uint8_t tsd_state_t;\n\n/* The actual tsd. */\nstruct tsd_s {\n\t/*\n\t * The contents should be treated as totally opaque outside the tsd\n\t * module.  Access any thread-local state through the getters and\n\t * setters below.\n\t */\n\ttsd_state_t\tstate;\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\n\tt use_a_getter_or_setter_instead_##n;\nMALLOC_TSD\n#undef O\n};\n\n/*\n * Wrapper around tsd_t that makes it possible to avoid implicit conversion\n * between tsd_t and tsdn_t, where tsdn_t is \"nullable\" and has to be\n * explicitly converted to tsd_t, which is non-nullable.\n */\nstruct tsdn_s {\n\ttsd_t tsd;\n};\n#define TSDN_NULL ((tsdn_t *)0)\nJEMALLOC_ALWAYS_INLINE tsdn_t *\ntsd_tsdn(tsd_t *tsd) {\n\treturn (tsdn_t *)tsd;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsdn_null(const tsdn_t *tsdn) {\n\treturn tsdn == NULL;\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsdn_tsd(tsdn_t *tsdn) {\n\tassert(!tsdn_null(tsdn));\n\n\treturn &tsdn->tsd;\n}\n\nvoid *malloc_tsd_malloc(size_t size);\nvoid malloc_tsd_dalloc(void *wrapper);\nvoid malloc_tsd_cleanup_register(bool (*f)(void));\ntsd_t *malloc_tsd_boot0(void);\nvoid malloc_tsd_boot1(void);\nvoid tsd_cleanup(void *arg);\ntsd_t *tsd_fetch_slow(tsd_t *tsd, bool internal);\nvoid tsd_slow_update(tsd_t *tsd);\n\n/*\n * We put the platform-specific data declarations and inlines into their own\n * header files to avoid cluttering this file.  They define tsd_boot0,\n * tsd_boot1, tsd_boot, tsd_booted_get, tsd_get_allocates, tsd_get, and tsd_set.\n */\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n#include \"jemalloc/internal/tsd_malloc_thread_cleanup.h\"\n#elif (defined(JEMALLOC_TLS))\n#include \"jemalloc/internal/tsd_tls.h\"\n#elif (defined(_WIN32))\n#include \"jemalloc/internal/tsd_win.h\"\n#else\n#include \"jemalloc/internal/tsd_generic.h\"\n#endif\n\n/*\n * tsd_foop_get_unsafe(tsd) returns a pointer to the thread-local instance of\n * foo.  This omits some safety checks, and so can be used during tsd\n * initialization and cleanup.\n */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE t *\t\t\t\t\t\t\\\ntsd_##n##p_get_unsafe(tsd_t *tsd) {\t\t\t\t\t\\\n\treturn &tsd->use_a_getter_or_setter_instead_##n;\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/* tsd_foop_get(tsd) returns a pointer to the thread-local instance of foo. */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE t *\t\t\t\t\t\t\\\ntsd_##n##p_get(tsd_t *tsd) {\t\t\t\t\t\t\\\n\tassert(tsd->state == tsd_state_nominal ||\t\t\t\\\n\t    tsd->state == tsd_state_nominal_slow ||\t\t\t\\\n\t    tsd->state == tsd_state_reincarnated ||\t\t\t\\\n\t    tsd->state == tsd_state_minimal_initialized);\t\t\\\n\treturn tsd_##n##p_get_unsafe(tsd);\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/*\n * tsdn_foop_get(tsdn) returns either the thread-local instance of foo (if tsdn\n * isn't NULL), or NULL (if tsdn is NULL), cast to the nullable pointer type.\n */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE nt *\t\t\t\t\t\t\\\ntsdn_##n##p_get(tsdn_t *tsdn) {\t\t\t\t\t\t\\\n\tif (tsdn_null(tsdn)) {\t\t\t\t\t\t\\\n\t\treturn NULL;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\ttsd_t *tsd = tsdn_tsd(tsdn);\t\t\t\t\t\\\n\treturn (nt *)tsd_##n##p_get(tsd);\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/* tsd_foo_get(tsd) returns the value of the thread-local instance of foo. */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE t\t\t\t\t\t\t\\\ntsd_##n##_get(tsd_t *tsd) {\t\t\t\t\t\t\\\n\treturn *tsd_##n##p_get(tsd);\t\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\n/* tsd_foo_set(tsd, val) updates the thread-local instance of foo to be val. */\n#define O(n, t, nt)\t\t\t\t\t\t\t\\\nJEMALLOC_ALWAYS_INLINE void\t\t\t\t\t\t\\\ntsd_##n##_set(tsd_t *tsd, t val) {\t\t\t\t\t\\\n\tassert(tsd->state != tsd_state_reincarnated &&\t\t\t\\\n\t    tsd->state != tsd_state_minimal_initialized);\t\t\\\n\t*tsd_##n##p_get(tsd) = val;\t\t\t\t\t\\\n}\nMALLOC_TSD\n#undef O\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_assert_fast(tsd_t *tsd) {\n\tassert(!malloc_slow && tsd_tcache_enabled_get(tsd) &&\n\t    tsd_reentrancy_level_get(tsd) == 0);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_fast(tsd_t *tsd) {\n\tbool fast = (tsd->state == tsd_state_nominal);\n\tif (fast) {\n\t\ttsd_assert_fast(tsd);\n\t}\n\n\treturn fast;\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_fetch_impl(bool init, bool minimal) {\n\ttsd_t *tsd = tsd_get(init);\n\n\tif (!init && tsd_get_allocates() && tsd == NULL) {\n\t\treturn NULL;\n\t}\n\tassert(tsd != NULL);\n\n\tif (unlikely(tsd->state != tsd_state_nominal)) {\n\t\treturn tsd_fetch_slow(tsd, minimal);\n\t}\n\tassert(tsd_fast(tsd));\n\ttsd_assert_fast(tsd);\n\n\treturn tsd;\n}\n\n/* Get a minimal TSD that requires no cleanup.  See comments in free(). */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_fetch_min(void) {\n\treturn tsd_fetch_impl(true, true);\n}\n\n/* For internal background threads use only. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_internal_fetch(void) {\n\ttsd_t *tsd = tsd_fetch_min();\n\t/* Use reincarnated state to prevent full initialization. */\n\ttsd->state = tsd_state_reincarnated;\n\n\treturn tsd;\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_fetch(void) {\n\treturn tsd_fetch_impl(true, false);\n}\n\nstatic inline bool\ntsd_nominal(tsd_t *tsd) {\n\treturn (tsd->state <= tsd_state_nominal_max);\n}\n\nJEMALLOC_ALWAYS_INLINE tsdn_t *\ntsdn_fetch(void) {\n\tif (!tsd_booted_get()) {\n\t\treturn NULL;\n\t}\n\n\treturn tsd_tsdn(tsd_fetch_impl(false, false));\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_ctx_t *\ntsd_rtree_ctx(tsd_t *tsd) {\n\treturn tsd_rtree_ctxp_get(tsd);\n}\n\nJEMALLOC_ALWAYS_INLINE rtree_ctx_t *\ntsdn_rtree_ctx(tsdn_t *tsdn, rtree_ctx_t *fallback) {\n\t/*\n\t * If tsd cannot be accessed, initialize the fallback rtree_ctx and\n\t * return a pointer to it.\n\t */\n\tif (unlikely(tsdn_null(tsdn))) {\n\t\trtree_ctx_data_init(fallback);\n\t\treturn fallback;\n\t}\n\treturn tsd_rtree_ctx(tsdn_tsd(tsdn));\n}\n\n#endif /* JEMALLOC_INTERNAL_TSD_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_generic.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_GENERIC_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_GENERIC_H\n\ntypedef struct tsd_init_block_s tsd_init_block_t;\nstruct tsd_init_block_s {\n\tql_elm(tsd_init_block_t) link;\n\tpthread_t thread;\n\tvoid *data;\n};\n\n/* Defined in tsd.c, to allow the mutex headers to have tsd dependencies. */\ntypedef struct tsd_init_head_s tsd_init_head_t;\n\ntypedef struct {\n\tbool initialized;\n\ttsd_t val;\n} tsd_wrapper_t;\n\nvoid *tsd_init_check_recursion(tsd_init_head_t *head,\n    tsd_init_block_t *block);\nvoid tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block);\n\nextern pthread_key_t tsd_tsd;\nextern tsd_init_head_t tsd_init_head;\nextern tsd_wrapper_t tsd_boot_wrapper;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE void\ntsd_cleanup_wrapper(void *arg) {\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *)arg;\n\n\tif (wrapper->initialized) {\n\t\twrapper->initialized = false;\n\t\ttsd_cleanup(&wrapper->val);\n\t\tif (wrapper->initialized) {\n\t\t\t/* Trigger another cleanup round. */\n\t\t\tif (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0)\n\t\t\t{\n\t\t\t\tmalloc_write(\"<jemalloc>: Error setting TSD\\n\");\n\t\t\t\tif (opt_abort) {\n\t\t\t\t\tabort();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\tmalloc_tsd_dalloc(wrapper);\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_wrapper_set(tsd_wrapper_t *wrapper) {\n\tif (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0) {\n\t\tmalloc_write(\"<jemalloc>: Error setting TSD\\n\");\n\t\tabort();\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_wrapper_t *\ntsd_wrapper_get(bool init) {\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *)pthread_getspecific(tsd_tsd);\n\n\tif (init && unlikely(wrapper == NULL)) {\n\t\ttsd_init_block_t block;\n\t\twrapper = (tsd_wrapper_t *)\n\t\t    tsd_init_check_recursion(&tsd_init_head, &block);\n\t\tif (wrapper) {\n\t\t\treturn wrapper;\n\t\t}\n\t\twrapper = (tsd_wrapper_t *)\n\t\t    malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\t\tblock.data = (void *)wrapper;\n\t\tif (wrapper == NULL) {\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\t\tabort();\n\t\t} else {\n\t\t\twrapper->initialized = false;\n\t\t\ttsd_t initializer = TSD_INITIALIZER;\n\t\t\twrapper->val = initializer;\n\t\t}\n\t\ttsd_wrapper_set(wrapper);\n\t\ttsd_init_finish(&tsd_init_head, &block);\n\t}\n\treturn wrapper;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\tif (pthread_key_create(&tsd_tsd, tsd_cleanup_wrapper) != 0) {\n\t\treturn true;\n\t}\n\ttsd_wrapper_set(&tsd_boot_wrapper);\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\ttsd_wrapper_t *wrapper;\n\twrapper = (tsd_wrapper_t *)malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\tif (wrapper == NULL) {\n\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\tabort();\n\t}\n\ttsd_boot_wrapper.initialized = false;\n\ttsd_cleanup(&tsd_boot_wrapper.val);\n\twrapper->initialized = false;\n\ttsd_t initializer = TSD_INITIALIZER;\n\twrapper->val = initializer;\n\ttsd_wrapper_set(wrapper);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\tif (tsd_boot0()) {\n\t\treturn true;\n\t}\n\ttsd_boot1();\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn true;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(bool init) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(init);\n\tif (tsd_get_allocates() && !init && wrapper == NULL) {\n\t\treturn NULL;\n\t}\n\treturn &wrapper->val;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(true);\n\tif (likely(&wrapper->val != val)) {\n\t\twrapper->val = *(val);\n\t}\n\twrapper->initialized = true;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H\n\nextern __thread tsd_t tsd_tls;\nextern __thread bool tsd_initialized;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE bool\ntsd_cleanup_wrapper(void) {\n\tif (tsd_initialized) {\n\t\ttsd_initialized = false;\n\t\ttsd_cleanup(&tsd_tls);\n\t}\n\treturn tsd_initialized;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\tmalloc_tsd_cleanup_register(&tsd_cleanup_wrapper);\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\t/* Do nothing. */\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\treturn tsd_boot0();\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn false;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(bool init) {\n\tassert(tsd_booted);\n\treturn &tsd_tls;\n}\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\tassert(tsd_booted);\n\tif (likely(&tsd_tls != val)) {\n\t\ttsd_tls = (*val);\n\t}\n\ttsd_initialized = true;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_tls.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_TLS_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_TLS_H\n\nextern __thread tsd_t tsd_tls;\nextern pthread_key_t tsd_tsd;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\tif (pthread_key_create(&tsd_tsd, &tsd_cleanup) != 0) {\n\t\treturn true;\n\t}\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\t/* Do nothing. */\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\treturn tsd_boot0();\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn false;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(UNUSED bool init) {\n\tassert(tsd_booted);\n\treturn &tsd_tls;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\tassert(tsd_booted);\n\tif (likely(&tsd_tls != val)) {\n\t\ttsd_tls = (*val);\n\t}\n\tif (pthread_setspecific(tsd_tsd, (void *)(&tsd_tls)) != 0) {\n\t\tmalloc_write(\"<jemalloc>: Error setting tsd.\\n\");\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_types.h",
    "content": "#ifndef JEMALLOC_INTERNAL_TSD_TYPES_H\n#define JEMALLOC_INTERNAL_TSD_TYPES_H\n\n#define MALLOC_TSD_CLEANUPS_MAX\t2\n\ntypedef struct tsd_s tsd_t;\ntypedef struct tsdn_s tsdn_t;\ntypedef bool (*malloc_tsd_cleanup_t)(void);\n\n#endif /* JEMALLOC_INTERNAL_TSD_TYPES_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/tsd_win.h",
    "content": "#ifdef JEMALLOC_INTERNAL_TSD_WIN_H\n#error This file should be included only once, by tsd.h.\n#endif\n#define JEMALLOC_INTERNAL_TSD_WIN_H\n\ntypedef struct {\n\tbool initialized;\n\ttsd_t val;\n} tsd_wrapper_t;\n\nextern DWORD tsd_tsd;\nextern tsd_wrapper_t tsd_boot_wrapper;\nextern bool tsd_booted;\n\n/* Initialization/cleanup. */\nJEMALLOC_ALWAYS_INLINE bool\ntsd_cleanup_wrapper(void) {\n\tDWORD error = GetLastError();\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *)TlsGetValue(tsd_tsd);\n\tSetLastError(error);\n\n\tif (wrapper == NULL) {\n\t\treturn false;\n\t}\n\n\tif (wrapper->initialized) {\n\t\twrapper->initialized = false;\n\t\ttsd_cleanup(&wrapper->val);\n\t\tif (wrapper->initialized) {\n\t\t\t/* Trigger another cleanup round. */\n\t\t\treturn true;\n\t\t}\n\t}\n\tmalloc_tsd_dalloc(wrapper);\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_wrapper_set(tsd_wrapper_t *wrapper) {\n\tif (!TlsSetValue(tsd_tsd, (void *)wrapper)) {\n\t\tmalloc_write(\"<jemalloc>: Error setting TSD\\n\");\n\t\tabort();\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE tsd_wrapper_t *\ntsd_wrapper_get(bool init) {\n\tDWORD error = GetLastError();\n\ttsd_wrapper_t *wrapper = (tsd_wrapper_t *) TlsGetValue(tsd_tsd);\n\tSetLastError(error);\n\n\tif (init && unlikely(wrapper == NULL)) {\n\t\twrapper = (tsd_wrapper_t *)\n\t\t    malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\t\tif (wrapper == NULL) {\n\t\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\t\tabort();\n\t\t} else {\n\t\t\twrapper->initialized = false;\n\t\t\t/* MSVC is finicky about aggregate initialization. */\n\t\t\ttsd_t tsd_initializer = TSD_INITIALIZER;\n\t\t\twrapper->val = tsd_initializer;\n\t\t}\n\t\ttsd_wrapper_set(wrapper);\n\t}\n\treturn wrapper;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot0(void) {\n\ttsd_tsd = TlsAlloc();\n\tif (tsd_tsd == TLS_OUT_OF_INDEXES) {\n\t\treturn true;\n\t}\n\tmalloc_tsd_cleanup_register(&tsd_cleanup_wrapper);\n\ttsd_wrapper_set(&tsd_boot_wrapper);\n\ttsd_booted = true;\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_boot1(void) {\n\ttsd_wrapper_t *wrapper;\n\twrapper = (tsd_wrapper_t *)\n\t    malloc_tsd_malloc(sizeof(tsd_wrapper_t));\n\tif (wrapper == NULL) {\n\t\tmalloc_write(\"<jemalloc>: Error allocating TSD\\n\");\n\t\tabort();\n\t}\n\ttsd_boot_wrapper.initialized = false;\n\ttsd_cleanup(&tsd_boot_wrapper.val);\n\twrapper->initialized = false;\n\ttsd_t initializer = TSD_INITIALIZER;\n\twrapper->val = initializer;\n\ttsd_wrapper_set(wrapper);\n}\nJEMALLOC_ALWAYS_INLINE bool\ntsd_boot(void) {\n\tif (tsd_boot0()) {\n\t\treturn true;\n\t}\n\ttsd_boot1();\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_booted_get(void) {\n\treturn tsd_booted;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\ntsd_get_allocates(void) {\n\treturn true;\n}\n\n/* Get/set. */\nJEMALLOC_ALWAYS_INLINE tsd_t *\ntsd_get(bool init) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(init);\n\tif (tsd_get_allocates() && !init && wrapper == NULL) {\n\t\treturn NULL;\n\t}\n\treturn &wrapper->val;\n}\n\nJEMALLOC_ALWAYS_INLINE void\ntsd_set(tsd_t *val) {\n\ttsd_wrapper_t *wrapper;\n\n\tassert(tsd_booted);\n\twrapper = tsd_wrapper_get(true);\n\tif (likely(&wrapper->val != val)) {\n\t\twrapper->val = *(val);\n\t}\n\twrapper->initialized = true;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/util.h",
    "content": "#ifndef JEMALLOC_INTERNAL_UTIL_H\n#define JEMALLOC_INTERNAL_UTIL_H\n\n#define UTIL_INLINE static inline\n\n/* Junk fill patterns. */\n#ifndef JEMALLOC_ALLOC_JUNK\n#  define JEMALLOC_ALLOC_JUNK\t((uint8_t)0xa5)\n#endif\n#ifndef JEMALLOC_FREE_JUNK\n#  define JEMALLOC_FREE_JUNK\t((uint8_t)0x5a)\n#endif\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_ARG_CONCAT(...) __VA_ARGS__\n\n/* cpp macro definition stringification. */\n#define STRINGIFY_HELPER(x) #x\n#define STRINGIFY(x) STRINGIFY_HELPER(x)\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#define JEMALLOC_CC_SILENCE_INIT(v) = v\n\n#ifdef __GNUC__\n#  define likely(x)   __builtin_expect(!!(x), 1)\n#  define unlikely(x) __builtin_expect(!!(x), 0)\n#else\n#  define likely(x)   !!(x)\n#  define unlikely(x) !!(x)\n#endif\n\n#if !defined(JEMALLOC_INTERNAL_UNREACHABLE)\n#  error JEMALLOC_INTERNAL_UNREACHABLE should have been defined by configure\n#endif\n\n#define unreachable() JEMALLOC_INTERNAL_UNREACHABLE()\n\n/* Set error code. */\nUTIL_INLINE void\nset_errno(int errnum) {\n#ifdef _WIN32\n\tSetLastError(errnum);\n#else\n\terrno = errnum;\n#endif\n}\n\n/* Get last error code. */\nUTIL_INLINE int\nget_errno(void) {\n#ifdef _WIN32\n\treturn GetLastError();\n#else\n\treturn errno;\n#endif\n}\n\n#undef UTIL_INLINE\n\n#endif /* JEMALLOC_INTERNAL_UTIL_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/internal/witness.h",
    "content": "#ifndef JEMALLOC_INTERNAL_WITNESS_H\n#define JEMALLOC_INTERNAL_WITNESS_H\n\n#include \"jemalloc/internal/ql.h\"\n\n/******************************************************************************/\n/* LOCK RANKS */\n/******************************************************************************/\n\n/*\n * Witnesses with rank WITNESS_RANK_OMIT are completely ignored by the witness\n * machinery.\n */\n\n#define WITNESS_RANK_OMIT\t\t0U\n\n#define WITNESS_RANK_MIN\t\t1U\n\n#define WITNESS_RANK_INIT\t\t1U\n#define WITNESS_RANK_CTL\t\t1U\n#define WITNESS_RANK_TCACHES\t\t2U\n#define WITNESS_RANK_ARENAS\t\t3U\n\n#define WITNESS_RANK_BACKGROUND_THREAD_GLOBAL\t4U\n\n#define WITNESS_RANK_PROF_DUMP\t\t5U\n#define WITNESS_RANK_PROF_BT2GCTX\t6U\n#define WITNESS_RANK_PROF_TDATAS\t7U\n#define WITNESS_RANK_PROF_TDATA\t\t8U\n#define WITNESS_RANK_PROF_GCTX\t\t9U\n\n#define WITNESS_RANK_BACKGROUND_THREAD\t10U\n\n/*\n * Used as an argument to witness_assert_depth_to_rank() in order to validate\n * depth excluding non-core locks with lower ranks.  Since the rank argument to\n * witness_assert_depth_to_rank() is inclusive rather than exclusive, this\n * definition can have the same value as the minimally ranked core lock.\n */\n#define WITNESS_RANK_CORE\t\t11U\n\n#define WITNESS_RANK_DECAY\t\t11U\n#define WITNESS_RANK_TCACHE_QL\t\t12U\n#define WITNESS_RANK_EXTENT_GROW\t13U\n#define WITNESS_RANK_EXTENTS\t\t14U\n#define WITNESS_RANK_EXTENT_AVAIL\t15U\n\n#define WITNESS_RANK_EXTENT_POOL\t16U\n#define WITNESS_RANK_RTREE\t\t17U\n#define WITNESS_RANK_BASE\t\t18U\n#define WITNESS_RANK_ARENA_LARGE\t19U\n\n#define WITNESS_RANK_LEAF\t\t0xffffffffU\n#define WITNESS_RANK_BIN\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_ARENA_STATS\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_DSS\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_ACTIVE\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_ACCUM\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_DUMP_SEQ\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_GDUMP\t\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_NEXT_THR_UID\tWITNESS_RANK_LEAF\n#define WITNESS_RANK_PROF_THREAD_ACTIVE_INIT\tWITNESS_RANK_LEAF\n\n/******************************************************************************/\n/* PER-WITNESS DATA */\n/******************************************************************************/\n#if defined(JEMALLOC_DEBUG)\n#  define WITNESS_INITIALIZER(name, rank) {name, rank, NULL, NULL, {NULL, NULL}}\n#else\n#  define WITNESS_INITIALIZER(name, rank)\n#endif\n\ntypedef struct witness_s witness_t;\ntypedef unsigned witness_rank_t;\ntypedef ql_head(witness_t) witness_list_t;\ntypedef int witness_comp_t (const witness_t *, void *, const witness_t *,\n    void *);\n\nstruct witness_s {\n\t/* Name, used for printing lock order reversal messages. */\n\tconst char\t\t*name;\n\n\t/*\n\t * Witness rank, where 0 is lowest and UINT_MAX is highest.  Witnesses\n\t * must be acquired in order of increasing rank.\n\t */\n\twitness_rank_t\t\trank;\n\n\t/*\n\t * If two witnesses are of equal rank and they have the samp comp\n\t * function pointer, it is called as a last attempt to differentiate\n\t * between witnesses of equal rank.\n\t */\n\twitness_comp_t\t\t*comp;\n\n\t/* Opaque data, passed to comp(). */\n\tvoid\t\t\t*opaque;\n\n\t/* Linkage for thread's currently owned locks. */\n\tql_elm(witness_t)\tlink;\n};\n\n/******************************************************************************/\n/* PER-THREAD DATA */\n/******************************************************************************/\ntypedef struct witness_tsd_s witness_tsd_t;\nstruct witness_tsd_s {\n\twitness_list_t witnesses;\n\tbool forking;\n};\n\n#define WITNESS_TSD_INITIALIZER { ql_head_initializer(witnesses), false }\n#define WITNESS_TSDN_NULL ((witness_tsdn_t *)0)\n\n/******************************************************************************/\n/* (PER-THREAD) NULLABILITY HELPERS */\n/******************************************************************************/\ntypedef struct witness_tsdn_s witness_tsdn_t;\nstruct witness_tsdn_s {\n\twitness_tsd_t witness_tsd;\n};\n\nJEMALLOC_ALWAYS_INLINE witness_tsdn_t *\nwitness_tsd_tsdn(witness_tsd_t *witness_tsd) {\n\treturn (witness_tsdn_t *)witness_tsd;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nwitness_tsdn_null(witness_tsdn_t *witness_tsdn) {\n\treturn witness_tsdn == NULL;\n}\n\nJEMALLOC_ALWAYS_INLINE witness_tsd_t *\nwitness_tsdn_tsd(witness_tsdn_t *witness_tsdn) {\n\tassert(!witness_tsdn_null(witness_tsdn));\n\treturn &witness_tsdn->witness_tsd;\n}\n\n/******************************************************************************/\n/* API */\n/******************************************************************************/\nvoid witness_init(witness_t *witness, const char *name, witness_rank_t rank,\n    witness_comp_t *comp, void *opaque);\n\ntypedef void (witness_lock_error_t)(const witness_list_t *, const witness_t *);\nextern witness_lock_error_t *JET_MUTABLE witness_lock_error;\n\ntypedef void (witness_owner_error_t)(const witness_t *);\nextern witness_owner_error_t *JET_MUTABLE witness_owner_error;\n\ntypedef void (witness_not_owner_error_t)(const witness_t *);\nextern witness_not_owner_error_t *JET_MUTABLE witness_not_owner_error;\n\ntypedef void (witness_depth_error_t)(const witness_list_t *,\n    witness_rank_t rank_inclusive, unsigned depth);\nextern witness_depth_error_t *JET_MUTABLE witness_depth_error;\n\nvoid witnesses_cleanup(witness_tsd_t *witness_tsd);\nvoid witness_prefork(witness_tsd_t *witness_tsd);\nvoid witness_postfork_parent(witness_tsd_t *witness_tsd);\nvoid witness_postfork_child(witness_tsd_t *witness_tsd);\n\n/* Helper, not intended for direct use. */\nstatic inline bool\nwitness_owner(witness_tsd_t *witness_tsd, const witness_t *witness) {\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tcassert(config_debug);\n\n\twitnesses = &witness_tsd->witnesses;\n\tql_foreach(w, witnesses, link) {\n\t\tif (w == witness) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic inline void\nwitness_assert_owner(witness_tsdn_t *witness_tsdn, const witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\tif (witness_owner(witness_tsd, witness)) {\n\t\treturn;\n\t}\n\twitness_owner_error(witness);\n}\n\nstatic inline void\nwitness_assert_not_owner(witness_tsdn_t *witness_tsdn,\n    const witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\twitnesses = &witness_tsd->witnesses;\n\tql_foreach(w, witnesses, link) {\n\t\tif (w == witness) {\n\t\t\twitness_not_owner_error(witness);\n\t\t}\n\t}\n}\n\nstatic inline void\nwitness_assert_depth_to_rank(witness_tsdn_t *witness_tsdn,\n    witness_rank_t rank_inclusive, unsigned depth) {\n\twitness_tsd_t *witness_tsd;\n\tunsigned d;\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\n\td = 0;\n\twitnesses = &witness_tsd->witnesses;\n\tw = ql_last(witnesses, link);\n\tif (w != NULL) {\n\t\tql_reverse_foreach(w, witnesses, link) {\n\t\t\tif (w->rank < rank_inclusive) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\td++;\n\t\t}\n\t}\n\tif (d != depth) {\n\t\twitness_depth_error(witnesses, rank_inclusive, depth);\n\t}\n}\n\nstatic inline void\nwitness_assert_depth(witness_tsdn_t *witness_tsdn, unsigned depth) {\n\twitness_assert_depth_to_rank(witness_tsdn, WITNESS_RANK_MIN, depth);\n}\n\nstatic inline void\nwitness_assert_lockless(witness_tsdn_t *witness_tsdn) {\n\twitness_assert_depth(witness_tsdn, 0);\n}\n\nstatic inline void\nwitness_lock(witness_tsdn_t *witness_tsdn, witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\twitness_list_t *witnesses;\n\twitness_t *w;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\twitness_assert_not_owner(witness_tsdn, witness);\n\n\twitnesses = &witness_tsd->witnesses;\n\tw = ql_last(witnesses, link);\n\tif (w == NULL) {\n\t\t/* No other locks; do nothing. */\n\t} else if (witness_tsd->forking && w->rank <= witness->rank) {\n\t\t/* Forking, and relaxed ranking satisfied. */\n\t} else if (w->rank > witness->rank) {\n\t\t/* Not forking, rank order reversal. */\n\t\twitness_lock_error(witnesses, witness);\n\t} else if (w->rank == witness->rank && (w->comp == NULL || w->comp !=\n\t    witness->comp || w->comp(w, w->opaque, witness, witness->opaque) >\n\t    0)) {\n\t\t/*\n\t\t * Missing/incompatible comparison function, or comparison\n\t\t * function indicates rank order reversal.\n\t\t */\n\t\twitness_lock_error(witnesses, witness);\n\t}\n\n\tql_elm_new(witness, link);\n\tql_tail_insert(witnesses, witness, link);\n}\n\nstatic inline void\nwitness_unlock(witness_tsdn_t *witness_tsdn, witness_t *witness) {\n\twitness_tsd_t *witness_tsd;\n\twitness_list_t *witnesses;\n\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\n\tif (witness_tsdn_null(witness_tsdn)) {\n\t\treturn;\n\t}\n\twitness_tsd = witness_tsdn_tsd(witness_tsdn);\n\tif (witness->rank == WITNESS_RANK_OMIT) {\n\t\treturn;\n\t}\n\n\t/*\n\t * Check whether owner before removal, rather than relying on\n\t * witness_assert_owner() to abort, so that unit tests can test this\n\t * function's failure mode without causing undefined behavior.\n\t */\n\tif (witness_owner(witness_tsd, witness)) {\n\t\twitnesses = &witness_tsd->witnesses;\n\t\tql_remove(witnesses, witness, link);\n\t} else {\n\t\twitness_assert_owner(witness_tsdn, witness);\n\t}\n}\n\n#endif /* JEMALLOC_INTERNAL_WITNESS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc.sh",
    "content": "#!/bin/sh\n\nobjroot=$1\n\ncat <<EOF\n#ifndef JEMALLOC_H_\n#define JEMALLOC_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nEOF\n\nfor hdr in jemalloc_defs.h jemalloc_rename.h jemalloc_macros.h \\\n           jemalloc_protos.h jemalloc_typedefs.h jemalloc_mangle.h ; do\n  cat \"${objroot}include/jemalloc/${hdr}\" \\\n      | grep -v 'Generated from .* by configure\\.' \\\n      | sed -e 's/ $//g'\n  echo\ndone\n\ncat <<EOF\n#ifdef __cplusplus\n}\n#endif\n#endif /* JEMALLOC_H_ */\nEOF\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_defs.h.in",
    "content": "/* Defined if __attribute__((...)) syntax is supported. */\n#undef JEMALLOC_HAVE_ATTR\n\n/* Defined if alloc_size attribute is supported. */\n#undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE\n\n/* Defined if format(gnu_printf, ...) attribute is supported. */\n#undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF\n\n/* Defined if format(printf, ...) attribute is supported. */\n#undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF\n\n/*\n * Define overrides for non-standard allocator-related functions if they are\n * 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 * If defined, specify throw() for the public function prototypes when compiling\n * with C++.  The only justification for this is to match the prototypes that\n * glibc defines.\n */\n#undef JEMALLOC_USE_CXX_THROW\n\n#ifdef _MSC_VER\n#  ifdef _WIN64\n#    define LG_SIZEOF_PTR_WIN 3\n#  else\n#    define LG_SIZEOF_PTR_WIN 2\n#  endif\n#endif\n\n/* sizeof(void *) == 2^LG_SIZEOF_PTR. */\n#undef LG_SIZEOF_PTR\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_macros.h.in",
    "content": "#include <stdlib.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <limits.h>\n#include <strings.h>\n\n#define JEMALLOC_VERSION \"@jemalloc_version@\"\n#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@\n#define JEMALLOC_VERSION_MINOR @jemalloc_version_minor@\n#define JEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@\n#define JEMALLOC_VERSION_NREV @jemalloc_version_nrev@\n#define JEMALLOC_VERSION_GID \"@jemalloc_version_gid@\"\n\n#define MALLOCX_LG_ALIGN(la)\t((int)(la))\n#if LG_SIZEOF_PTR == 2\n#  define MALLOCX_ALIGN(a)\t((int)(ffs((int)(a))-1))\n#else\n#  define MALLOCX_ALIGN(a)\t\t\t\t\t\t\\\n     ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 :\t\\\n     ffs((int)(((size_t)(a))>>32))+31))\n#endif\n#define MALLOCX_ZERO\t((int)0x40)\n/*\n * Bias tcache index bits so that 0 encodes \"automatic tcache management\", and 1\n * encodes MALLOCX_TCACHE_NONE.\n */\n#define MALLOCX_TCACHE(tc)\t((int)(((tc)+2) << 8))\n#define MALLOCX_TCACHE_NONE\tMALLOCX_TCACHE(-1)\n/*\n * Bias arena index bits so that 0 encodes \"use an automatically chosen arena\".\n */\n#define MALLOCX_ARENA(a)\t((((int)(a))+1) << 20)\n\n/*\n * Use as arena index in \"arena.<i>.{purge,decay,dss}\" and\n * \"stats.arenas.<i>.*\" mallctl interfaces to select all arenas.  This\n * definition is intentionally specified in raw decimal format to support\n * cpp-based string concatenation, e.g.\n *\n *   #define STRINGIFY_HELPER(x) #x\n *   #define STRINGIFY(x) STRINGIFY_HELPER(x)\n *\n *   mallctl(\"arena.\" STRINGIFY(MALLCTL_ARENAS_ALL) \".purge\", NULL, NULL, NULL,\n *       0);\n */\n#define MALLCTL_ARENAS_ALL\t4096\n/*\n * Use as arena index in \"stats.arenas.<i>.*\" mallctl interfaces to select\n * destroyed arenas.\n */\n#define MALLCTL_ARENAS_DESTROYED\t4097\n\n#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)\n#  define JEMALLOC_CXX_THROW throw()\n#else\n#  define JEMALLOC_CXX_THROW\n#endif\n\n#if defined(_MSC_VER)\n#  define JEMALLOC_ATTR(s)\n#  define JEMALLOC_ALIGNED(s) __declspec(align(s))\n#  define JEMALLOC_ALLOC_SIZE(s)\n#  define JEMALLOC_ALLOC_SIZE2(s1, s2)\n#  ifndef JEMALLOC_EXPORT\n#    ifdef DLLEXPORT\n#      define JEMALLOC_EXPORT __declspec(dllexport)\n#    else\n#      define JEMALLOC_EXPORT __declspec(dllimport)\n#    endif\n#  endif\n#  define JEMALLOC_FORMAT_PRINTF(s, i)\n#  define JEMALLOC_NOINLINE __declspec(noinline)\n#  ifdef __cplusplus\n#    define JEMALLOC_NOTHROW __declspec(nothrow)\n#  else\n#    define JEMALLOC_NOTHROW\n#  endif\n#  define JEMALLOC_SECTION(s) __declspec(allocate(s))\n#  define JEMALLOC_RESTRICT_RETURN __declspec(restrict)\n#  if _MSC_VER >= 1900 && !defined(__EDG__)\n#    define JEMALLOC_ALLOCATOR __declspec(allocator)\n#  else\n#    define JEMALLOC_ALLOCATOR\n#  endif\n#elif defined(JEMALLOC_HAVE_ATTR)\n#  define JEMALLOC_ATTR(s) __attribute__((s))\n#  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))\n#  ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE\n#    define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))\n#    define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))\n#  else\n#    define JEMALLOC_ALLOC_SIZE(s)\n#    define JEMALLOC_ALLOC_SIZE2(s1, s2)\n#  endif\n#  ifndef JEMALLOC_EXPORT\n#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility(\"default\"))\n#  endif\n#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF\n#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))\n#  elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)\n#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))\n#  else\n#    define JEMALLOC_FORMAT_PRINTF(s, i)\n#  endif\n#  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)\n#  define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)\n#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))\n#  define JEMALLOC_RESTRICT_RETURN\n#  define JEMALLOC_ALLOCATOR\n#else\n#  define JEMALLOC_ATTR(s)\n#  define JEMALLOC_ALIGNED(s)\n#  define JEMALLOC_ALLOC_SIZE(s)\n#  define JEMALLOC_ALLOC_SIZE2(s1, s2)\n#  define JEMALLOC_EXPORT\n#  define JEMALLOC_FORMAT_PRINTF(s, i)\n#  define JEMALLOC_NOINLINE\n#  define JEMALLOC_NOTHROW\n#  define JEMALLOC_SECTION(s)\n#  define JEMALLOC_RESTRICT_RETURN\n#  define JEMALLOC_ALLOCATOR\n#endif\n\n/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()\n * function. */\n#define JEMALLOC_FRAG_HINT\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_mangle.sh",
    "content": "#!/bin/sh -eu\n\npublic_symbols_txt=$1\nsymbol_prefix=$2\n\ncat <<EOF\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 JEMALLOC_NO_DEMANGLE\n#  endif\nEOF\n\nfor nm in `cat ${public_symbols_txt}` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#  define ${n} ${symbol_prefix}${n}\"\ndone\n\ncat <<EOF\n#endif\n\n/*\n * The ${symbol_prefix}* macros can be used as stable alternative names for the\n * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily\n * meant 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\nEOF\n\nfor nm in `cat ${public_symbols_txt}` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  echo \"#  undef ${symbol_prefix}${n}\"\ndone\n\ncat <<EOF\n#endif\nEOF\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_protos.h.in",
    "content": "/*\n * The @je_@ prefix on the following public symbol declarations is an artifact\n * of namespace management, and should be omitted in application code unless\n * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle@install_suffix@.h).\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 JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@malloc(size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@calloc(size_t num, size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@posix_memalign(void **memptr,\n    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@aligned_alloc(size_t alignment,\n    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)\n    JEMALLOC_ALLOC_SIZE(2);\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@realloc(void *ptr, size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@free(void *ptr)\n    JEMALLOC_CXX_THROW;\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@mallocx(size_t size, int flags)\n    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@rallocx(void *ptr, size_t size,\n    int flags) JEMALLOC_ALLOC_SIZE(2);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@xallocx(void *ptr, size_t size,\n    size_t extra, int flags);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@sallocx(const void *ptr,\n    int flags) JEMALLOC_ATTR(pure);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@dallocx(void *ptr, int flags);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@sdallocx(void *ptr, size_t size,\n    int flags);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@nallocx(size_t size, int flags)\n    JEMALLOC_ATTR(pure);\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@mallctl(const char *name,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen);\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@mallctlnametomib(const char *name,\n    size_t *mibp, size_t *miblenp);\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\t@je_@mallctlbymib(const size_t *mib,\n    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\t@je_@malloc_stats_print(\n    void (*write_cb)(void *, const char *), void *@je_@cbopaque,\n    const char *opts);\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\t@je_@malloc_usable_size(\n    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;\n\n#ifdef JEMALLOC_OVERRIDE_MEMALIGN\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@memalign(size_t alignment, size_t size)\n    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);\n#endif\n\n#ifdef JEMALLOC_OVERRIDE_VALLOC\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\n    void JEMALLOC_NOTHROW\t*@je_@valloc(size_t size) JEMALLOC_CXX_THROW\n    JEMALLOC_ATTR(malloc);\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_rename.sh",
    "content": "#!/bin/sh\n\npublic_symbols_txt=$1\n\ncat <<EOF\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#ifndef JEMALLOC_NO_RENAME\nEOF\n\nfor nm in `cat ${public_symbols_txt}` ; do\n  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`\n  m=`echo ${nm} |tr ':' ' ' |awk '{print $2}'`\n  echo \"#  define je_${n} ${m}\"\ndone\n\ncat <<EOF\n#endif\nEOF\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/jemalloc/jemalloc_typedefs.h.in",
    "content": "typedef struct extent_hooks_s extent_hooks_t;\n\n/*\n * void *\n * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,\n *     size_t alignment, bool *zero, bool *commit, unsigned arena_ind);\n */\ntypedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *,\n    bool *, unsigned);\n\n/*\n * bool\n * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     bool committed, unsigned arena_ind);\n */\ntypedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool,\n    unsigned);\n\n/*\n * void\n * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     bool committed, unsigned arena_ind);\n */\ntypedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool,\n    unsigned);\n\n/*\n * bool\n * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t offset, size_t length, unsigned arena_ind);\n */\ntypedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t,\n    unsigned);\n\n/*\n * bool\n * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t offset, size_t length, unsigned arena_ind);\n */\ntypedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t,\n    size_t, unsigned);\n\n/*\n * bool\n * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t offset, size_t length, unsigned arena_ind);\n */\ntypedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t,\n    unsigned);\n\n/*\n * bool\n * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size,\n *     size_t size_a, size_t size_b, bool committed, unsigned arena_ind);\n */\ntypedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t,\n    bool, unsigned);\n\n/*\n * bool\n * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,\n *     void *addr_b, size_t size_b, bool committed, unsigned arena_ind);\n */\ntypedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t,\n    bool, unsigned);\n\nstruct extent_hooks_s {\n\textent_alloc_t\t\t*alloc;\n\textent_dalloc_t\t\t*dalloc;\n\textent_destroy_t\t*destroy;\n\textent_commit_t\t\t*commit;\n\textent_decommit_t\t*decommit;\n\textent_purge_t\t\t*purge_lazy;\n\textent_purge_t\t\t*purge_forced;\n\textent_split_t\t\t*split;\n\textent_merge_t\t\t*merge;\n};\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/msvc_compat/C99/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 */\n/* Clang-cl uses MSVC headers, so needs msvc_compat, but has _Bool as\n * a built-in type. */\n#ifndef __clang__\ntypedef BOOL _Bool;\n#endif\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": "Chapter07/redis-6.0.3/deps/jemalloc/include/msvc_compat/C99/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": "Chapter07/redis-6.0.3/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#ifdef _MSC_VER\n#  include <intrin.h>\n#  pragma intrinsic(_BitScanForward)\nstatic __forceinline int ffsl(long x) {\n\tunsigned long i;\n\n\tif (_BitScanForward(&i, x)) {\n\t\treturn i + 1;\n\t}\n\treturn 0;\n}\n\nstatic __forceinline int ffs(int x) {\n\treturn ffsl(x);\n}\n\n#  ifdef  _M_X64\n#    pragma intrinsic(_BitScanForward64)\n#  endif\n\nstatic __forceinline int ffsll(unsigned __int64 x) {\n\tunsigned long i;\n#ifdef  _M_X64\n\tif (_BitScanForward64(&i, x)) {\n\t\treturn i + 1;\n\t}\n\treturn 0;\n#else\n// Fallback for 32-bit build where 64-bit version not available\n// assuming little endian\n\tunion {\n\t\tunsigned __int64 ll;\n\t\tunsigned   long l[2];\n\t} s;\n\n\ts.ll = x;\n\n\tif (_BitScanForward(&i, s.l[0])) {\n\t\treturn i + 1;\n\t} else if(_BitScanForward(&i, s.l[1])) {\n\t\treturn i + 33;\n\t}\n\treturn 0;\n#endif\n}\n\n#else\n#  define ffsll(x) __builtin_ffsll(x)\n#  define ffsl(x) __builtin_ffsl(x)\n#  define ffs(x) __builtin_ffs(x)\n#endif\n\n#endif /* strings_h */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/include/msvc_compat/windows_extra.h",
    "content": "#ifndef MSVC_COMPAT_WINDOWS_EXTRA_H\n#define MSVC_COMPAT_WINDOWS_EXTRA_H\n\n#include <errno.h>\n\n#endif /* MSVC_COMPAT_WINDOWS_EXTRA_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/jemalloc.pc.in",
    "content": "prefix=@prefix@\nexec_prefix=@exec_prefix@\nlibdir=@libdir@\nincludedir=@includedir@\ninstall_suffix=@install_suffix@\n\nName: jemalloc\nDescription: A general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.\nURL: http://jemalloc.net/\nVersion: @jemalloc_version_major@.@jemalloc_version_minor@.@jemalloc_version_bugfix@_@jemalloc_version_nrev@\nCflags: -I${includedir}\nLibs: -L${libdir} -ljemalloc${install_suffix}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4",
    "content": "# ===========================================================================\n#   http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html\n# ===========================================================================\n#\n# SYNOPSIS\n#\n#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])\n#\n# DESCRIPTION\n#\n#   Check for baseline language coverage in the compiler for the specified\n#   version of the C++ standard.  If necessary, add switches to CXX and\n#   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard)\n#   or '14' (for the C++14 standard).\n#\n#   The second argument, if specified, indicates whether you insist on an\n#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.\n#   -std=c++11).  If neither is specified, you get whatever works, with\n#   preference for an extended mode.\n#\n#   The third argument, if specified 'mandatory' or if left unspecified,\n#   indicates that baseline support for the specified C++ standard is\n#   required and that the macro should error out if no mode with that\n#   support is found.  If specified 'optional', then configuration proceeds\n#   regardless, after defining HAVE_CXX${VERSION} if and only if a\n#   supporting mode is found.\n#\n# LICENSE\n#\n#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>\n#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>\n#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>\n#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>\n#   Copyright (c) 2015 Paul Norman <penorman@mac.com>\n#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>\n#\n#   Copying and distribution of this file, with or without modification, are\n#   permitted in any medium without royalty provided the copyright notice\n#   and this notice are preserved.  This file is offered as-is, without any\n#   warranty.\n\n#serial 4\n\ndnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro\ndnl  (serial version number 13).\n\nAC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl\n  m4_if([$1], [11], [],\n        [$1], [14], [],\n        [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],\n        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl\n  m4_if([$2], [], [],\n        [$2], [ext], [],\n        [$2], [noext], [],\n        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl\n  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],\n        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],\n        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],\n        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])\n  AC_LANG_PUSH([C++])dnl\n  ac_success=no\n  AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,\n  ax_cv_cxx_compile_cxx$1,\n  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],\n    [ax_cv_cxx_compile_cxx$1=yes],\n    [ax_cv_cxx_compile_cxx$1=no])])\n  if test x$ax_cv_cxx_compile_cxx$1 = xyes; then\n    ac_success=yes\n  fi\n\n  m4_if([$2], [noext], [], [dnl\n  if test x$ac_success = xno; then\n    for switch in -std=gnu++$1 -std=gnu++0x; do\n      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])\n      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,\n                     $cachevar,\n        [ac_save_CXX=\"$CXX\"\n         CXX=\"$CXX $switch\"\n         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],\n          [eval $cachevar=yes],\n          [eval $cachevar=no])\n         CXX=\"$ac_save_CXX\"])\n      if eval test x\\$$cachevar = xyes; then\n        CXX=\"$CXX $switch\"\n        if test -n \"$CXXCPP\" ; then\n          CXXCPP=\"$CXXCPP $switch\"\n        fi\n        ac_success=yes\n        break\n      fi\n    done\n  fi])\n\n  m4_if([$2], [ext], [], [dnl\n  if test x$ac_success = xno; then\n    dnl HP's aCC needs +std=c++11 according to:\n    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf\n    dnl Cray's crayCC needs \"-h std=c++11\"\n    for switch in -std=c++$1 -std=c++0x +std=c++$1 \"-h std=c++$1\"; do\n      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])\n      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,\n                     $cachevar,\n        [ac_save_CXX=\"$CXX\"\n         CXX=\"$CXX $switch\"\n         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],\n          [eval $cachevar=yes],\n          [eval $cachevar=no])\n         CXX=\"$ac_save_CXX\"])\n      if eval test x\\$$cachevar = xyes; then\n        CXX=\"$CXX $switch\"\n        if test -n \"$CXXCPP\" ; then\n          CXXCPP=\"$CXXCPP $switch\"\n        fi\n        ac_success=yes\n        break\n      fi\n    done\n  fi])\n  AC_LANG_POP([C++])\n  if test x$ax_cxx_compile_cxx$1_required = xtrue; then\n    if test x$ac_success = xno; then\n      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])\n    fi\n  fi\n  if test x$ac_success = xno; then\n    HAVE_CXX$1=0\n    AC_MSG_NOTICE([No compiler with C++$1 support was found])\n  else\n    HAVE_CXX$1=1\n    AC_DEFINE(HAVE_CXX$1,1,\n              [define if the compiler supports basic C++$1 syntax])\n  fi\n  AC_SUBST(HAVE_CXX$1)\n])\n\n\ndnl  Test body for checking C++11 support\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],\n  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11\n)\n\n\ndnl  Test body for checking C++14 support\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],\n  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11\n  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14\n)\n\n\ndnl  Tests for new features in C++11\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[\n\n// If the compiler admits that it is not ready for C++11, why torture it?\n// Hopefully, this will speed up the test.\n\n#ifndef __cplusplus\n\n#error \"This is not a C++ compiler\"\n\n#elif __cplusplus < 201103L\n\n#error \"This is not a C++11 compiler\"\n\n#else\n\nnamespace cxx11\n{\n\n  namespace test_static_assert\n  {\n\n    template <typename T>\n    struct check\n    {\n      static_assert(sizeof(int) <= sizeof(T), \"not big enough\");\n    };\n\n  }\n\n  namespace test_final_override\n  {\n\n    struct Base\n    {\n      virtual void f() {}\n    };\n\n    struct Derived : public Base\n    {\n      virtual void f() override {}\n    };\n\n  }\n\n  namespace test_double_right_angle_brackets\n  {\n\n    template < typename T >\n    struct check {};\n\n    typedef check<void> single_type;\n    typedef check<check<void>> double_type;\n    typedef check<check<check<void>>> triple_type;\n    typedef check<check<check<check<void>>>> quadruple_type;\n\n  }\n\n  namespace test_decltype\n  {\n\n    int\n    f()\n    {\n      int a = 1;\n      decltype(a) b = 2;\n      return a + b;\n    }\n\n  }\n\n  namespace test_type_deduction\n  {\n\n    template < typename T1, typename T2 >\n    struct is_same\n    {\n      static const bool value = false;\n    };\n\n    template < typename T >\n    struct is_same<T, T>\n    {\n      static const bool value = true;\n    };\n\n    template < typename T1, typename T2 >\n    auto\n    add(T1 a1, T2 a2) -> decltype(a1 + a2)\n    {\n      return a1 + a2;\n    }\n\n    int\n    test(const int c, volatile int v)\n    {\n      static_assert(is_same<int, decltype(0)>::value == true, \"\");\n      static_assert(is_same<int, decltype(c)>::value == false, \"\");\n      static_assert(is_same<int, decltype(v)>::value == false, \"\");\n      auto ac = c;\n      auto av = v;\n      auto sumi = ac + av + 'x';\n      auto sumf = ac + av + 1.0;\n      static_assert(is_same<int, decltype(ac)>::value == true, \"\");\n      static_assert(is_same<int, decltype(av)>::value == true, \"\");\n      static_assert(is_same<int, decltype(sumi)>::value == true, \"\");\n      static_assert(is_same<int, decltype(sumf)>::value == false, \"\");\n      static_assert(is_same<int, decltype(add(c, v))>::value == true, \"\");\n      return (sumf > 0.0) ? sumi : add(c, v);\n    }\n\n  }\n\n  namespace test_noexcept\n  {\n\n    int f() { return 0; }\n    int g() noexcept { return 0; }\n\n    static_assert(noexcept(f()) == false, \"\");\n    static_assert(noexcept(g()) == true, \"\");\n\n  }\n\n  namespace test_constexpr\n  {\n\n    template < typename CharT >\n    unsigned long constexpr\n    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept\n    {\n      return *s ? strlen_c_r(s + 1, acc + 1) : acc;\n    }\n\n    template < typename CharT >\n    unsigned long constexpr\n    strlen_c(const CharT *const s) noexcept\n    {\n      return strlen_c_r(s, 0UL);\n    }\n\n    static_assert(strlen_c(\"\") == 0UL, \"\");\n    static_assert(strlen_c(\"1\") == 1UL, \"\");\n    static_assert(strlen_c(\"example\") == 7UL, \"\");\n    static_assert(strlen_c(\"another\\0example\") == 7UL, \"\");\n\n  }\n\n  namespace test_rvalue_references\n  {\n\n    template < int N >\n    struct answer\n    {\n      static constexpr int value = N;\n    };\n\n    answer<1> f(int&)       { return answer<1>(); }\n    answer<2> f(const int&) { return answer<2>(); }\n    answer<3> f(int&&)      { return answer<3>(); }\n\n    void\n    test()\n    {\n      int i = 0;\n      const int c = 0;\n      static_assert(decltype(f(i))::value == 1, \"\");\n      static_assert(decltype(f(c))::value == 2, \"\");\n      static_assert(decltype(f(0))::value == 3, \"\");\n    }\n\n  }\n\n  namespace test_uniform_initialization\n  {\n\n    struct test\n    {\n      static const int zero {};\n      static const int one {1};\n    };\n\n    static_assert(test::zero == 0, \"\");\n    static_assert(test::one == 1, \"\");\n\n  }\n\n  namespace test_lambdas\n  {\n\n    void\n    test1()\n    {\n      auto lambda1 = [](){};\n      auto lambda2 = lambda1;\n      lambda1();\n      lambda2();\n    }\n\n    int\n    test2()\n    {\n      auto a = [](int i, int j){ return i + j; }(1, 2);\n      auto b = []() -> int { return '0'; }();\n      auto c = [=](){ return a + b; }();\n      auto d = [&](){ return c; }();\n      auto e = [a, &b](int x) mutable {\n        const auto identity = [](int y){ return y; };\n        for (auto i = 0; i < a; ++i)\n          a += b--;\n        return x + identity(a + b);\n      }(0);\n      return a + b + c + d + e;\n    }\n\n    int\n    test3()\n    {\n      const auto nullary = [](){ return 0; };\n      const auto unary = [](int x){ return x; };\n      using nullary_t = decltype(nullary);\n      using unary_t = decltype(unary);\n      const auto higher1st = [](nullary_t f){ return f(); };\n      const auto higher2nd = [unary](nullary_t f1){\n        return [unary, f1](unary_t f2){ return f2(unary(f1())); };\n      };\n      return higher1st(nullary) + higher2nd(nullary)(unary);\n    }\n\n  }\n\n  namespace test_variadic_templates\n  {\n\n    template <int...>\n    struct sum;\n\n    template <int N0, int... N1toN>\n    struct sum<N0, N1toN...>\n    {\n      static constexpr auto value = N0 + sum<N1toN...>::value;\n    };\n\n    template <>\n    struct sum<>\n    {\n      static constexpr auto value = 0;\n    };\n\n    static_assert(sum<>::value == 0, \"\");\n    static_assert(sum<1>::value == 1, \"\");\n    static_assert(sum<23>::value == 23, \"\");\n    static_assert(sum<1, 2>::value == 3, \"\");\n    static_assert(sum<5, 5, 11>::value == 21, \"\");\n    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, \"\");\n\n  }\n\n  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae\n  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function\n  // because of this.\n  namespace test_template_alias_sfinae\n  {\n\n    struct foo {};\n\n    template<typename T>\n    using member = typename T::member_type;\n\n    template<typename T>\n    void func(...) {}\n\n    template<typename T>\n    void func(member<T>*) {}\n\n    void test();\n\n    void test() { func<foo>(0); }\n\n  }\n\n}  // namespace cxx11\n\n#endif  // __cplusplus >= 201103L\n\n]])\n\n\ndnl  Tests for new features in C++14\n\nm4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[\n\n// If the compiler admits that it is not ready for C++14, why torture it?\n// Hopefully, this will speed up the test.\n\n#ifndef __cplusplus\n\n#error \"This is not a C++ compiler\"\n\n#elif __cplusplus < 201402L\n\n#error \"This is not a C++14 compiler\"\n\n#else\n\nnamespace cxx14\n{\n\n  namespace test_polymorphic_lambdas\n  {\n\n    int\n    test()\n    {\n      const auto lambda = [](auto&&... args){\n        const auto istiny = [](auto x){\n          return (sizeof(x) == 1UL) ? 1 : 0;\n        };\n        const int aretiny[] = { istiny(args)... };\n        return aretiny[0];\n      };\n      return lambda(1, 1L, 1.0f, '1');\n    }\n\n  }\n\n  namespace test_binary_literals\n  {\n\n    constexpr auto ivii = 0b0000000000101010;\n    static_assert(ivii == 42, \"wrong value\");\n\n  }\n\n  namespace test_generalized_constexpr\n  {\n\n    template < typename CharT >\n    constexpr unsigned long\n    strlen_c(const CharT *const s) noexcept\n    {\n      auto length = 0UL;\n      for (auto p = s; *p; ++p)\n        ++length;\n      return length;\n    }\n\n    static_assert(strlen_c(\"\") == 0UL, \"\");\n    static_assert(strlen_c(\"x\") == 1UL, \"\");\n    static_assert(strlen_c(\"test\") == 4UL, \"\");\n    static_assert(strlen_c(\"another\\0test\") == 7UL, \"\");\n\n  }\n\n  namespace test_lambda_init_capture\n  {\n\n    int\n    test()\n    {\n      auto x = 0;\n      const auto lambda1 = [a = x](int b){ return a + b; };\n      const auto lambda2 = [a = lambda1(x)](){ return a; };\n      return lambda2();\n    }\n\n  }\n\n  namespace test_digit_seperators\n  {\n\n    constexpr auto ten_million = 100'000'000;\n    static_assert(ten_million == 100000000, \"\");\n\n  }\n\n  namespace test_return_type_deduction\n  {\n\n    auto f(int& x) { return x; }\n    decltype(auto) g(int& x) { return x; }\n\n    template < typename T1, typename T2 >\n    struct is_same\n    {\n      static constexpr auto value = false;\n    };\n\n    template < typename T >\n    struct is_same<T, T>\n    {\n      static constexpr auto value = true;\n    };\n\n    int\n    test()\n    {\n      auto x = 0;\n      static_assert(is_same<int, decltype(f(x))>::value, \"\");\n      static_assert(is_same<int&, decltype(g(x))>::value, \"\");\n      return x;\n    }\n\n  }\n\n}  // namespace cxx14\n\n#endif  // __cplusplus >= 201402L\n\n]])\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/ReadMe.txt",
    "content": "\nHow to build jemalloc for Windows\n=================================\n\n1. Install Cygwin with at least the following packages:\n   * autoconf\n   * autogen\n   * gawk\n   * grep\n   * sed\n\n2. Install Visual Studio 2015 or 2017 with Visual C++\n\n3. Add Cygwin\\bin to the PATH environment variable\n\n4. Open \"x64 Native Tools Command Prompt for VS 2017\"\n   (note: x86/x64 doesn't matter at this point)\n\n5. Generate header files:\n   sh -c \"CC=cl ./autogen.sh\"\n\n6. Now the project can be opened and built in Visual Studio:\n   msvc\\jemalloc_vc2017.sln\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/jemalloc_vc2015.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 14\nVisualStudioVersion = 14.0.24720.0\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{70A99006-6DE9-472B-8F83-4CEE6C616DF3}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tReadMe.txt = ReadMe.txt\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"jemalloc\", \"projects\\vc2015\\jemalloc\\jemalloc.vcxproj\", \"{8D6BB292-9E1C-413D-9F98-4864BDC1514A}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"test_threads\", \"projects\\vc2015\\test_threads\\test_threads.vcxproj\", \"{09028CFD-4EB7-491D-869C-0708DB97ED44}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tDebug-static|x64 = Debug-static|x64\n\t\tDebug-static|x86 = Debug-static|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\t\tRelease-static|x64 = Release-static|x64\n\t\tRelease-static|x86 = Release-static|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.Build.0 = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.Build.0 = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.ActiveCfg = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.Build.0 = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.ActiveCfg = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.Build.0 = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.Build.0 = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.Build.0 = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.Build.0 = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.ActiveCfg = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.Build.0 = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.ActiveCfg = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.Build.0 = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.Build.0 = Release-static|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/jemalloc_vc2017.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 14\nVisualStudioVersion = 14.0.24720.0\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{70A99006-6DE9-472B-8F83-4CEE6C616DF3}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tReadMe.txt = ReadMe.txt\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"jemalloc\", \"projects\\vc2017\\jemalloc\\jemalloc.vcxproj\", \"{8D6BB292-9E1C-413D-9F98-4864BDC1514A}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"test_threads\", \"projects\\vc2017\\test_threads\\test_threads.vcxproj\", \"{09028CFD-4EB7-491D-869C-0708DB97ED44}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tDebug-static|x64 = Debug-static|x64\n\t\tDebug-static|x86 = Debug-static|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\t\tRelease-static|x64 = Release-static|x64\n\t\tRelease-static|x86 = Release-static|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x64.Build.0 = Debug|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug|x86.Build.0 = Debug|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.ActiveCfg = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x64.Build.0 = Release|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.ActiveCfg = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release|x86.Build.0 = Release|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{8D6BB292-9E1C-413D-9F98-4864BDC1514A}.Release-static|x86.Build.0 = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x64.Build.0 = Debug|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug|x86.Build.0 = Debug|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.ActiveCfg = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x64.Build.0 = Debug-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.ActiveCfg = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Debug-static|x86.Build.0 = Debug-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.ActiveCfg = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x64.Build.0 = Release|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.ActiveCfg = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release|x86.Build.0 = Release|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.ActiveCfg = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x64.Build.0 = Release-static|x64\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.ActiveCfg = Release-static|Win32\n\t\t{09028CFD-4EB7-491D-869C-0708DB97ED44}.Release-static|x86.Build.0 = Release-static|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\" />\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>jemalloc</RootNamespace>\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n      <MinimalRebuild>false</MinimalRebuild>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/test_threads/test_threads.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{09028CFD-4EB7-491D-869C-0708DB97ED44}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>test_threads</RootNamespace>\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v140</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\" />\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\jemalloc\\jemalloc.vcxproj\">\n      <Project>{8d6bb292-9e1c-413d-9f98-4864bdc1514a}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2015/test_threads/test_threads.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\" />\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\" />\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>jemalloc</RootNamespace>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-$(PlatformToolset)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)d</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <TargetName>$(ProjectName)-vc$(PlatformToolsetVersion)-$(Configuration)</TargetName>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;JEMALLOC_DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;JEMALLOC_DEBUG;_REENTRANT;JEMALLOC_EXPORT=;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n      <MinimalRebuild>false</MinimalRebuild>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;_WINDLL;DLLEXPORT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <ProgramDataBaseFileName>$(OutputPath)$(TargetName).pdb</ProgramDataBaseFileName>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_NO_PRIVATE_NAMESPACE;_REENTRANT;JEMALLOC_EXPORT=;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <DisableSpecificWarnings>4090;4146;4267;4334</DisableSpecificWarnings>\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\arena.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\background_thread.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\base.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bitmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ckh.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ctl.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_dss.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\extent_mmap.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hash.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\hooks.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\jemalloc.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\large.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\malloc_io.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\mutex_pool.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\nstime.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\pages.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prng.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\prof.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\rtree.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\stats.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\sz.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tcache.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\ticker.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\tsd.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\witness.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\log.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\bin.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\..\\src\\div.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/test_threads/test_threads.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug-static|Win32\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug-static|x64\">\n      <Configuration>Debug-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|Win32\">\n      <Configuration>Release-static</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release-static|x64\">\n      <Configuration>Release-static</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{09028CFD-4EB7-491D-869C-0708DB97ED44}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>test_threads</RootNamespace>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>$(Platform)\\$(Configuration)\\</IntDir>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemallocd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-static|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-$(PlatformToolset)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-static|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\..\\..\\..\\test\\include;..\\..\\..\\..\\include;..\\..\\..\\..\\include\\msvc_compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\\$(Configuration)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>jemalloc-vc$(PlatformToolsetVersion)-$(Configuration).lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\" />\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\jemalloc\\jemalloc.vcxproj\">\n      <Project>{8d6bb292-9e1c-413d-9f98-4864bdc1514a}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/projects/vc2017/test_threads/test_threads.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\test_threads\\test_threads_main.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\test_threads\\test_threads.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/test_threads/test_threads.cpp",
    "content": "// jemalloc C++ threaded test\n// Author: Rustam Abdullaev\n// Public Domain\n\n#include <atomic>\n#include <functional>\n#include <future>\n#include <random>\n#include <thread>\n#include <vector>\n#include <stdio.h>\n#include <jemalloc/jemalloc.h>\n\nusing std::vector;\nusing std::thread;\nusing std::uniform_int_distribution;\nusing std::minstd_rand;\n\nint test_threads() {\n  je_malloc_conf = \"narenas:3\";\n  int narenas = 0;\n  size_t sz = sizeof(narenas);\n  je_mallctl(\"opt.narenas\", (void *)&narenas, &sz, NULL, 0);\n  if (narenas != 3) {\n    printf(\"Error: unexpected number of arenas: %d\\n\", narenas);\n    return 1;\n  }\n  static const int sizes[] = { 7, 16, 32, 60, 91, 100, 120, 144, 169, 199, 255, 400, 670, 900, 917, 1025, 3333, 5190, 13131, 49192, 99999, 123123, 255265, 2333111 };\n  static const int numSizes = (int)(sizeof(sizes) / sizeof(sizes[0]));\n  vector<thread> workers;\n  static const int numThreads = narenas + 1, numAllocsMax = 25, numIter1 = 50, numIter2 = 50;\n  je_malloc_stats_print(NULL, NULL, NULL);\n  size_t allocated1;\n  size_t sz1 = sizeof(allocated1);\n  je_mallctl(\"stats.active\", (void *)&allocated1, &sz1, NULL, 0);\n  printf(\"\\nPress Enter to start threads...\\n\");\n  getchar();\n  printf(\"Starting %d threads x %d x %d iterations...\\n\", numThreads, numIter1, numIter2);\n  for (int i = 0; i < numThreads; i++) {\n    workers.emplace_back([tid=i]() {\n      uniform_int_distribution<int> sizeDist(0, numSizes - 1);\n      minstd_rand rnd(tid * 17);\n      uint8_t* ptrs[numAllocsMax];\n      int ptrsz[numAllocsMax];\n      for (int i = 0; i < numIter1; ++i) {\n        thread t([&]() {\n          for (int i = 0; i < numIter2; ++i) {\n            const int numAllocs = numAllocsMax - sizeDist(rnd);\n            for (int j = 0; j < numAllocs; j += 64) {\n              const int x = sizeDist(rnd);\n              const int sz = sizes[x];\n              ptrsz[j] = sz;\n              ptrs[j] = (uint8_t*)je_malloc(sz);\n              if (!ptrs[j]) {\n                printf(\"Unable to allocate %d bytes in thread %d, iter %d, alloc %d. %d\\n\", sz, tid, i, j, x);\n                exit(1);\n              }\n              for (int k = 0; k < sz; k++)\n                ptrs[j][k] = tid + k;\n            }\n            for (int j = 0; j < numAllocs; j += 64) {\n              for (int k = 0, sz = ptrsz[j]; k < sz; k++)\n                if (ptrs[j][k] != (uint8_t)(tid + k)) {\n                  printf(\"Memory error in thread %d, iter %d, alloc %d @ %d : %02X!=%02X\\n\", tid, i, j, k, ptrs[j][k], (uint8_t)(tid + k));\n                  exit(1);\n                }\n              je_free(ptrs[j]);\n            }\n          }\n        });\n        t.join();\n      }\n    });\n  }\n  for (thread& t : workers) {\n    t.join();\n  }\n  je_malloc_stats_print(NULL, NULL, NULL);\n  size_t allocated2;\n  je_mallctl(\"stats.active\", (void *)&allocated2, &sz1, NULL, 0);\n  size_t leaked = allocated2 - allocated1;\n  printf(\"\\nDone. Leaked: %zd bytes\\n\", leaked);\n  bool failed = leaked > 65536; // in case C++ runtime allocated something (e.g. iostream locale or facet)\n  printf(\"\\nTest %s!\\n\", (failed ? \"FAILED\" : \"successful\"));\n  printf(\"\\nPress Enter to continue...\\n\");\n  getchar();\n  return failed ? 1 : 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/test_threads/test_threads.h",
    "content": "#pragma once\n\nint test_threads();\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/msvc/test_threads/test_threads_main.cpp",
    "content": "#include \"test_threads.h\"\n#include <future>\n#include <functional>\n#include <chrono>\n\nusing namespace std::chrono_literals;\n\nint main(int argc, char** argv) {\n  int rc = test_threads();\n  return rc;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/run_tests.sh",
    "content": "$(dirname \"$)\")/scripts/gen_run_tests.py | bash\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/scripts/gen_run_tests.py",
    "content": "#!/usr/bin/env python\n\nimport sys\nfrom itertools import combinations\nfrom os import uname\nfrom multiprocessing import cpu_count\n\n# Later, we want to test extended vaddr support.  Apparently, the \"real\" way of\n# checking this is flaky on OS X.\nbits_64 = sys.maxsize > 2**32\n\nnparallel = cpu_count() * 2\n\nuname = uname()[0]\n\ndef powerset(items):\n    result = []\n    for i in xrange(len(items) + 1):\n        result += combinations(items, i)\n    return result\n\npossible_compilers = [('gcc', 'g++'), ('clang', 'clang++')]\npossible_compiler_opts = [\n    '-m32',\n]\npossible_config_opts = [\n    '--enable-debug',\n    '--enable-prof',\n    '--disable-stats',\n]\nif bits_64:\n    possible_config_opts.append('--with-lg-vaddr=56')\n\npossible_malloc_conf_opts = [\n    'tcache:false',\n    'dss:primary',\n    'percpu_arena:percpu',\n    'background_thread:true',\n]\n\nprint 'set -e'\nprint 'if [ -f Makefile ] ; then make relclean ; fi'\nprint 'autoconf'\nprint 'rm -rf run_tests.out'\nprint 'mkdir run_tests.out'\nprint 'cd run_tests.out'\n\nind = 0\nfor cc, cxx in possible_compilers:\n    for compiler_opts in powerset(possible_compiler_opts):\n        for config_opts in powerset(possible_config_opts):\n            for malloc_conf_opts in powerset(possible_malloc_conf_opts):\n                if cc is 'clang' \\\n                  and '-m32' in possible_compiler_opts \\\n                  and '--enable-prof' in config_opts:\n                    continue\n                config_line = (\n                    'EXTRA_CFLAGS=-Werror EXTRA_CXXFLAGS=-Werror '\n                    + 'CC=\"{} {}\" '.format(cc, \" \".join(compiler_opts))\n                    + 'CXX=\"{} {}\" '.format(cxx, \" \".join(compiler_opts))\n                    + '../../configure '\n                    + \" \".join(config_opts) + (' --with-malloc-conf=' +\n                    \",\".join(malloc_conf_opts) if len(malloc_conf_opts) > 0\n                    else '')\n                )\n\n                # We don't want to test large vaddr spaces in 32-bit mode.\n\t\tif ('-m32' in compiler_opts and '--with-lg-vaddr=56' in\n                  config_opts):\n\t\t    continue\n\n                # Per CPU arenas are only supported on Linux.\n                linux_supported = ('percpu_arena:percpu' in malloc_conf_opts \\\n                  or 'background_thread:true' in malloc_conf_opts)\n                # Heap profiling and dss are not supported on OS X.\n                darwin_unsupported = ('--enable-prof' in config_opts or \\\n                  'dss:primary' in malloc_conf_opts)\n                if (uname == 'Linux' and linux_supported) \\\n                  or (not linux_supported and (uname != 'Darwin' or \\\n                  not darwin_unsupported)):\n                    print \"\"\"cat <<EOF > run_test_%(ind)d.sh\n#!/bin/sh\n\nset -e\n\nabort() {\n    echo \"==> Error\" >> run_test.log\n    echo \"Error; see run_tests.out/run_test_%(ind)d.out/run_test.log\"\n    exit 255 # Special exit code tells xargs to terminate.\n}\n\n# Environment variables are not supported.\nrun_cmd() {\n    echo \"==> \\$@\" >> run_test.log\n    \\$@ >> run_test.log 2>&1 || abort\n}\n\necho \"=> run_test_%(ind)d: %(config_line)s\"\nmkdir run_test_%(ind)d.out\ncd run_test_%(ind)d.out\n\necho \"==> %(config_line)s\" >> run_test.log\n%(config_line)s >> run_test.log 2>&1 || abort\n\nrun_cmd make all tests\nrun_cmd make check\nrun_cmd make distclean\nEOF\nchmod 755 run_test_%(ind)d.sh\"\"\" % {'ind': ind, 'config_line': config_line}\n                    ind += 1\n\nprint 'for i in `seq 0 %(last_ind)d` ; do echo run_test_${i}.sh ; done | xargs -P %(nparallel)d -n 1 sh' % {'last_ind': ind-1, 'nparallel': nparallel}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/scripts/gen_travis.py",
    "content": "#!/usr/bin/env python\n\nfrom itertools import combinations\n\ntravis_template = \"\"\"\\\nlanguage: generic\n\nmatrix:\n  include:\n%s\n\nbefore_script:\n  - autoconf\n  - ./configure ${COMPILER_FLAGS:+ \\\n      CC=\"$CC $COMPILER_FLAGS\" \\\n      CXX=\"$CXX $COMPILER_FLAGS\" } \\\n      $CONFIGURE_FLAGS\n  - make -j3\n  - make -j3 tests\n\nscript:\n  - make check\n\"\"\"\n\n# The 'default' configuration is gcc, on linux, with no compiler or configure\n# flags.  We also test with clang, -m32, --enable-debug, --enable-prof,\n# --disable-stats, and --with-malloc-conf=tcache:false.  To avoid abusing\n# travis though, we don't test all 2**7 = 128 possible combinations of these;\n# instead, we only test combinations of up to 2 'unusual' settings, under the\n# hope that bugs involving interactions of such settings are rare.\n# Things at once, for C(7, 0) + C(7, 1) + C(7, 2) = 29\nMAX_UNUSUAL_OPTIONS = 2\n\nos_default = 'linux'\nos_unusual = 'osx'\n\ncompilers_default = 'CC=gcc CXX=g++'\ncompilers_unusual = 'CC=clang CXX=clang++'\n\ncompiler_flag_unusuals = ['-m32']\n\nconfigure_flag_unusuals = [\n    '--enable-debug',\n    '--enable-prof',\n    '--disable-stats',\n]\n\nmalloc_conf_unusuals = [\n    'tcache:false',\n    'dss:primary',\n    'percpu_arena:percpu',\n    'background_thread:true',\n]\n\nall_unusuals = (\n    [os_unusual] + [compilers_unusual] + compiler_flag_unusuals\n    + configure_flag_unusuals + malloc_conf_unusuals\n)\n\nunusual_combinations_to_test = []\nfor i in xrange(MAX_UNUSUAL_OPTIONS + 1):\n    unusual_combinations_to_test += combinations(all_unusuals, i)\n\ninclude_rows = \"\"\nfor unusual_combination in unusual_combinations_to_test:\n    os = os_default\n    if os_unusual in unusual_combination:\n        os = os_unusual\n\n    compilers = compilers_default\n    if compilers_unusual in unusual_combination:\n        compilers = compilers_unusual\n\n    compiler_flags = [\n        x for x in unusual_combination if x in compiler_flag_unusuals]\n\n    configure_flags = [\n        x for x in unusual_combination if x in configure_flag_unusuals]\n\n    malloc_conf = [\n        x for x in unusual_combination if x in malloc_conf_unusuals]\n    # Filter out unsupported configurations on OS X.\n    if os == 'osx' and ('dss:primary' in malloc_conf or \\\n      'percpu_arena:percpu' in malloc_conf or 'background_thread:true' \\\n      in malloc_conf):\n        continue\n    if len(malloc_conf) > 0:\n        configure_flags.append('--with-malloc-conf=' + \",\".join(malloc_conf))\n\n    # Filter out an unsupported configuration - heap profiling on OS X.\n    if os == 'osx' and '--enable-prof' in configure_flags:\n        continue\n\n    # We get some spurious errors when -Warray-bounds is enabled.\n    env_string = ('{} COMPILER_FLAGS=\"{}\" CONFIGURE_FLAGS=\"{}\" '\n\t'EXTRA_CFLAGS=\"-Werror -Wno-array-bounds\"').format(\n        compilers, \" \".join(compiler_flags), \" \".join(configure_flags))\n\n    include_rows += '    - os: %s\\n' % os\n    include_rows += '      env: %s\\n' % env_string\n    if '-m32' in unusual_combination and os == 'linux':\n        include_rows += '      addons:\\n'\n\tinclude_rows += '        apt:\\n'\n\tinclude_rows += '          packages:\\n'\n\tinclude_rows += '            - gcc-multilib\\n'\n\nprint travis_template % include_rows\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/arena.c",
    "content": "#define JEMALLOC_ARENA_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/div.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/*\n * Define names for both unininitialized and initialized phases, so that\n * options and mallctl processing are straightforward.\n */\nconst char *percpu_arena_mode_names[] = {\n\t\"percpu\",\n\t\"phycpu\",\n\t\"disabled\",\n\t\"percpu\",\n\t\"phycpu\"\n};\npercpu_arena_mode_t opt_percpu_arena = PERCPU_ARENA_DEFAULT;\n\nssize_t opt_dirty_decay_ms = DIRTY_DECAY_MS_DEFAULT;\nssize_t opt_muzzy_decay_ms = MUZZY_DECAY_MS_DEFAULT;\n\nstatic atomic_zd_t dirty_decay_ms_default;\nstatic atomic_zd_t muzzy_decay_ms_default;\n\nconst uint64_t h_steps[SMOOTHSTEP_NSTEPS] = {\n#define STEP(step, h, x, y)\t\t\t\\\n\t\th,\n\t\tSMOOTHSTEP\n#undef STEP\n};\n\nstatic div_info_t arena_binind_div_info[NBINS];\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic void arena_decay_to_limit(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, extents_t *extents, bool all, size_t npages_limit,\n    size_t npages_decay_max, bool is_background_thread);\nstatic bool arena_decay_dirty(tsdn_t *tsdn, arena_t *arena,\n    bool is_background_thread, bool all);\nstatic void arena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin);\nstatic void arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin);\n\n/******************************************************************************/\n\nvoid\narena_basic_stats_merge(UNUSED tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,\n    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,\n    size_t *nactive, size_t *ndirty, size_t *nmuzzy) {\n\t*nthreads += arena_nthreads_get(arena, false);\n\t*dss = dss_prec_names[arena_dss_prec_get(arena)];\n\t*dirty_decay_ms = arena_dirty_decay_ms_get(arena);\n\t*muzzy_decay_ms = arena_muzzy_decay_ms_get(arena);\n\t*nactive += atomic_load_zu(&arena->nactive, ATOMIC_RELAXED);\n\t*ndirty += extents_npages_get(&arena->extents_dirty);\n\t*nmuzzy += extents_npages_get(&arena->extents_muzzy);\n}\n\nvoid\narena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,\n    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,\n    size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,\n    bin_stats_t *bstats, arena_stats_large_t *lstats) {\n\tcassert(config_stats);\n\n\tarena_basic_stats_merge(tsdn, arena, nthreads, dss, dirty_decay_ms,\n\t    muzzy_decay_ms, nactive, ndirty, nmuzzy);\n\n\tsize_t base_allocated, base_resident, base_mapped, metadata_thp;\n\tbase_stats_get(tsdn, arena->base, &base_allocated, &base_resident,\n\t    &base_mapped, &metadata_thp);\n\n\tarena_stats_lock(tsdn, &arena->stats);\n\n\tarena_stats_accum_zu(&astats->mapped, base_mapped\n\t    + arena_stats_read_zu(tsdn, &arena->stats, &arena->stats.mapped));\n\tarena_stats_accum_zu(&astats->retained,\n\t    extents_npages_get(&arena->extents_retained) << LG_PAGE);\n\n\tarena_stats_accum_u64(&astats->decay_dirty.npurge,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_dirty.npurge));\n\tarena_stats_accum_u64(&astats->decay_dirty.nmadvise,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_dirty.nmadvise));\n\tarena_stats_accum_u64(&astats->decay_dirty.purged,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_dirty.purged));\n\n\tarena_stats_accum_u64(&astats->decay_muzzy.npurge,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_muzzy.npurge));\n\tarena_stats_accum_u64(&astats->decay_muzzy.nmadvise,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_muzzy.nmadvise));\n\tarena_stats_accum_u64(&astats->decay_muzzy.purged,\n\t    arena_stats_read_u64(tsdn, &arena->stats,\n\t    &arena->stats.decay_muzzy.purged));\n\n\tarena_stats_accum_zu(&astats->base, base_allocated);\n\tarena_stats_accum_zu(&astats->internal, arena_internal_get(arena));\n\tarena_stats_accum_zu(&astats->metadata_thp, metadata_thp);\n\tarena_stats_accum_zu(&astats->resident, base_resident +\n\t    (((atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) +\n\t    extents_npages_get(&arena->extents_dirty) +\n\t    extents_npages_get(&arena->extents_muzzy)) << LG_PAGE)));\n\n\tfor (szind_t i = 0; i < NSIZES - NBINS; i++) {\n\t\tuint64_t nmalloc = arena_stats_read_u64(tsdn, &arena->stats,\n\t\t    &arena->stats.lstats[i].nmalloc);\n\t\tarena_stats_accum_u64(&lstats[i].nmalloc, nmalloc);\n\t\tarena_stats_accum_u64(&astats->nmalloc_large, nmalloc);\n\n\t\tuint64_t ndalloc = arena_stats_read_u64(tsdn, &arena->stats,\n\t\t    &arena->stats.lstats[i].ndalloc);\n\t\tarena_stats_accum_u64(&lstats[i].ndalloc, ndalloc);\n\t\tarena_stats_accum_u64(&astats->ndalloc_large, ndalloc);\n\n\t\tuint64_t nrequests = arena_stats_read_u64(tsdn, &arena->stats,\n\t\t    &arena->stats.lstats[i].nrequests);\n\t\tarena_stats_accum_u64(&lstats[i].nrequests,\n\t\t    nmalloc + nrequests);\n\t\tarena_stats_accum_u64(&astats->nrequests_large,\n\t\t    nmalloc + nrequests);\n\n\t\tassert(nmalloc >= ndalloc);\n\t\tassert(nmalloc - ndalloc <= SIZE_T_MAX);\n\t\tsize_t curlextents = (size_t)(nmalloc - ndalloc);\n\t\tlstats[i].curlextents += curlextents;\n\t\tarena_stats_accum_zu(&astats->allocated_large,\n\t\t    curlextents * sz_index2size(NBINS + i));\n\t}\n\n\tarena_stats_unlock(tsdn, &arena->stats);\n\n\t/* tcache_bytes counts currently cached bytes. */\n\tatomic_store_zu(&astats->tcache_bytes, 0, ATOMIC_RELAXED);\n\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\tcache_bin_array_descriptor_t *descriptor;\n\tql_foreach(descriptor, &arena->cache_bin_array_descriptor_ql, link) {\n\t\tszind_t i = 0;\n\t\tfor (; i < NBINS; i++) {\n\t\t\tcache_bin_t *tbin = &descriptor->bins_small[i];\n\t\t\tarena_stats_accum_zu(&astats->tcache_bytes,\n\t\t\t    tbin->ncached * sz_index2size(i));\n\t\t}\n\t\tfor (; i < nhbins; i++) {\n\t\t\tcache_bin_t *tbin = &descriptor->bins_large[i];\n\t\t\tarena_stats_accum_zu(&astats->tcache_bytes,\n\t\t\t    tbin->ncached * sz_index2size(i));\n\t\t}\n\t}\n\tmalloc_mutex_prof_read(tsdn,\n\t    &astats->mutex_prof_data[arena_prof_mutex_tcache_list],\n\t    &arena->tcache_ql_mtx);\n\tmalloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);\n\n#define READ_ARENA_MUTEX_PROF_DATA(mtx, ind)\t\t\t\t\\\n    malloc_mutex_lock(tsdn, &arena->mtx);\t\t\t\t\\\n    malloc_mutex_prof_read(tsdn, &astats->mutex_prof_data[ind],\t\t\\\n        &arena->mtx);\t\t\t\t\t\t\t\\\n    malloc_mutex_unlock(tsdn, &arena->mtx);\n\n\t/* Gather per arena mutex profiling data. */\n\tREAD_ARENA_MUTEX_PROF_DATA(large_mtx, arena_prof_mutex_large);\n\tREAD_ARENA_MUTEX_PROF_DATA(extent_avail_mtx,\n\t    arena_prof_mutex_extent_avail)\n\tREAD_ARENA_MUTEX_PROF_DATA(extents_dirty.mtx,\n\t    arena_prof_mutex_extents_dirty)\n\tREAD_ARENA_MUTEX_PROF_DATA(extents_muzzy.mtx,\n\t    arena_prof_mutex_extents_muzzy)\n\tREAD_ARENA_MUTEX_PROF_DATA(extents_retained.mtx,\n\t    arena_prof_mutex_extents_retained)\n\tREAD_ARENA_MUTEX_PROF_DATA(decay_dirty.mtx,\n\t    arena_prof_mutex_decay_dirty)\n\tREAD_ARENA_MUTEX_PROF_DATA(decay_muzzy.mtx,\n\t    arena_prof_mutex_decay_muzzy)\n\tREAD_ARENA_MUTEX_PROF_DATA(base->mtx,\n\t    arena_prof_mutex_base)\n#undef READ_ARENA_MUTEX_PROF_DATA\n\n\tnstime_copy(&astats->uptime, &arena->create_time);\n\tnstime_update(&astats->uptime);\n\tnstime_subtract(&astats->uptime, &arena->create_time);\n\n\tfor (szind_t i = 0; i < NBINS; i++) {\n\t\tbin_stats_merge(tsdn, &bstats[i], &arena->bins[i]);\n\t}\n}\n\nvoid\narena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textents_dalloc(tsdn, arena, r_extent_hooks, &arena->extents_dirty,\n\t    extent);\n\tif (arena_dirty_decay_ms_get(arena) == 0) {\n\t\tarena_decay_dirty(tsdn, arena, false, true);\n\t} else {\n\t\tarena_background_thread_inactivity_check(tsdn, arena, false);\n\t}\n}\n\nstatic void *\narena_slab_reg_alloc(extent_t *slab, const bin_info_t *bin_info) {\n\tvoid *ret;\n\tarena_slab_data_t *slab_data = extent_slab_data_get(slab);\n\tsize_t regind;\n\n\tassert(extent_nfree_get(slab) > 0);\n\tassert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info));\n\n\tregind = bitmap_sfu(slab_data->bitmap, &bin_info->bitmap_info);\n\tret = (void *)((uintptr_t)extent_addr_get(slab) +\n\t    (uintptr_t)(bin_info->reg_size * regind));\n\textent_nfree_dec(slab);\n\treturn ret;\n}\n\n#ifndef JEMALLOC_JET\nstatic\n#endif\nsize_t\narena_slab_regind(extent_t *slab, szind_t binind, const void *ptr) {\n\tsize_t diff, regind;\n\n\t/* Freeing a pointer outside the slab can cause assertion failure. */\n\tassert((uintptr_t)ptr >= (uintptr_t)extent_addr_get(slab));\n\tassert((uintptr_t)ptr < (uintptr_t)extent_past_get(slab));\n\t/* Freeing an interior pointer can cause assertion failure. */\n\tassert(((uintptr_t)ptr - (uintptr_t)extent_addr_get(slab)) %\n\t    (uintptr_t)bin_infos[binind].reg_size == 0);\n\n\tdiff = (size_t)((uintptr_t)ptr - (uintptr_t)extent_addr_get(slab));\n\n\t/* Avoid doing division with a variable divisor. */\n\tregind = div_compute(&arena_binind_div_info[binind], diff);\n\n\tassert(regind < bin_infos[binind].nregs);\n\n\treturn regind;\n}\n\nstatic void\narena_slab_reg_dalloc(extent_t *slab, arena_slab_data_t *slab_data, void *ptr) {\n\tszind_t binind = extent_szind_get(slab);\n\tconst bin_info_t *bin_info = &bin_infos[binind];\n\tsize_t regind = arena_slab_regind(slab, binind, ptr);\n\n\tassert(extent_nfree_get(slab) < bin_info->nregs);\n\t/* Freeing an unallocated pointer can cause assertion failure. */\n\tassert(bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, regind));\n\n\tbitmap_unset(slab_data->bitmap, &bin_info->bitmap_info, regind);\n\textent_nfree_inc(slab);\n}\n\nstatic void\narena_nactive_add(arena_t *arena, size_t add_pages) {\n\tatomic_fetch_add_zu(&arena->nactive, add_pages, ATOMIC_RELAXED);\n}\n\nstatic void\narena_nactive_sub(arena_t *arena, size_t sub_pages) {\n\tassert(atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) >= sub_pages);\n\tatomic_fetch_sub_zu(&arena->nactive, sub_pages, ATOMIC_RELAXED);\n}\n\nstatic void\narena_large_malloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {\n\tszind_t index, hindex;\n\n\tcassert(config_stats);\n\n\tif (usize < LARGE_MINCLASS) {\n\t\tusize = LARGE_MINCLASS;\n\t}\n\tindex = sz_size2index(usize);\n\thindex = (index >= NBINS) ? index - NBINS : 0;\n\n\tarena_stats_add_u64(tsdn, &arena->stats,\n\t    &arena->stats.lstats[hindex].nmalloc, 1);\n}\n\nstatic void\narena_large_dalloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {\n\tszind_t index, hindex;\n\n\tcassert(config_stats);\n\n\tif (usize < LARGE_MINCLASS) {\n\t\tusize = LARGE_MINCLASS;\n\t}\n\tindex = sz_size2index(usize);\n\thindex = (index >= NBINS) ? index - NBINS : 0;\n\n\tarena_stats_add_u64(tsdn, &arena->stats,\n\t    &arena->stats.lstats[hindex].ndalloc, 1);\n}\n\nstatic void\narena_large_ralloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t oldusize,\n    size_t usize) {\n\tarena_large_dalloc_stats_update(tsdn, arena, oldusize);\n\tarena_large_malloc_stats_update(tsdn, arena, usize);\n}\n\nextent_t *\narena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool *zero) {\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tszind_t szind = sz_size2index(usize);\n\tsize_t mapped_add;\n\tbool commit = true;\n\textent_t *extent = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_dirty, NULL, usize, sz_large_pad, alignment, false,\n\t    szind, zero, &commit);\n\tif (extent == NULL) {\n\t\textent = extents_alloc(tsdn, arena, &extent_hooks,\n\t\t    &arena->extents_muzzy, NULL, usize, sz_large_pad, alignment,\n\t\t    false, szind, zero, &commit);\n\t}\n\tsize_t size = usize + sz_large_pad;\n\tif (extent == NULL) {\n\t\textent = extent_alloc_wrapper(tsdn, arena, &extent_hooks, NULL,\n\t\t    usize, sz_large_pad, alignment, false, szind, zero,\n\t\t    &commit);\n\t\tif (config_stats) {\n\t\t\t/*\n\t\t\t * extent may be NULL on OOM, but in that case\n\t\t\t * mapped_add isn't used below, so there's no need to\n\t\t\t * conditionlly set it to 0 here.\n\t\t\t */\n\t\t\tmapped_add = size;\n\t\t}\n\t} else if (config_stats) {\n\t\tmapped_add = 0;\n\t}\n\n\tif (extent != NULL) {\n\t\tif (config_stats) {\n\t\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\t\tarena_large_malloc_stats_update(tsdn, arena, usize);\n\t\t\tif (mapped_add != 0) {\n\t\t\t\tarena_stats_add_zu(tsdn, &arena->stats,\n\t\t\t\t    &arena->stats.mapped, mapped_add);\n\t\t\t}\n\t\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t\t}\n\t\tarena_nactive_add(arena, size >> LG_PAGE);\n\t}\n\n\treturn extent;\n}\n\nvoid\narena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_large_dalloc_stats_update(tsdn, arena,\n\t\t    extent_usize_get(extent));\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\tarena_nactive_sub(arena, extent_size_get(extent) >> LG_PAGE);\n}\n\nvoid\narena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    size_t oldusize) {\n\tsize_t usize = extent_usize_get(extent);\n\tsize_t udiff = oldusize - usize;\n\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_large_ralloc_stats_update(tsdn, arena, oldusize, usize);\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\tarena_nactive_sub(arena, udiff >> LG_PAGE);\n}\n\nvoid\narena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    size_t oldusize) {\n\tsize_t usize = extent_usize_get(extent);\n\tsize_t udiff = usize - oldusize;\n\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_large_ralloc_stats_update(tsdn, arena, oldusize, usize);\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\tarena_nactive_add(arena, udiff >> LG_PAGE);\n}\n\nstatic ssize_t\narena_decay_ms_read(arena_decay_t *decay) {\n\treturn atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);\n}\n\nstatic void\narena_decay_ms_write(arena_decay_t *decay, ssize_t decay_ms) {\n\tatomic_store_zd(&decay->time_ms, decay_ms, ATOMIC_RELAXED);\n}\n\nstatic void\narena_decay_deadline_init(arena_decay_t *decay) {\n\t/*\n\t * Generate a new deadline that is uniformly random within the next\n\t * epoch after the current one.\n\t */\n\tnstime_copy(&decay->deadline, &decay->epoch);\n\tnstime_add(&decay->deadline, &decay->interval);\n\tif (arena_decay_ms_read(decay) > 0) {\n\t\tnstime_t jitter;\n\n\t\tnstime_init(&jitter, prng_range_u64(&decay->jitter_state,\n\t\t    nstime_ns(&decay->interval)));\n\t\tnstime_add(&decay->deadline, &jitter);\n\t}\n}\n\nstatic bool\narena_decay_deadline_reached(const arena_decay_t *decay, const nstime_t *time) {\n\treturn (nstime_compare(&decay->deadline, time) <= 0);\n}\n\nstatic size_t\narena_decay_backlog_npages_limit(const arena_decay_t *decay) {\n\tuint64_t sum;\n\tsize_t npages_limit_backlog;\n\tunsigned i;\n\n\t/*\n\t * For each element of decay_backlog, multiply by the corresponding\n\t * fixed-point smoothstep decay factor.  Sum the products, then divide\n\t * to round down to the nearest whole number of pages.\n\t */\n\tsum = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tsum += decay->backlog[i] * h_steps[i];\n\t}\n\tnpages_limit_backlog = (size_t)(sum >> SMOOTHSTEP_BFP);\n\n\treturn npages_limit_backlog;\n}\n\nstatic void\narena_decay_backlog_update_last(arena_decay_t *decay, size_t current_npages) {\n\tsize_t npages_delta = (current_npages > decay->nunpurged) ?\n\t    current_npages - decay->nunpurged : 0;\n\tdecay->backlog[SMOOTHSTEP_NSTEPS-1] = npages_delta;\n\n\tif (config_debug) {\n\t\tif (current_npages > decay->ceil_npages) {\n\t\t\tdecay->ceil_npages = current_npages;\n\t\t}\n\t\tsize_t npages_limit = arena_decay_backlog_npages_limit(decay);\n\t\tassert(decay->ceil_npages >= npages_limit);\n\t\tif (decay->ceil_npages > npages_limit) {\n\t\t\tdecay->ceil_npages = npages_limit;\n\t\t}\n\t}\n}\n\nstatic void\narena_decay_backlog_update(arena_decay_t *decay, uint64_t nadvance_u64,\n    size_t current_npages) {\n\tif (nadvance_u64 >= SMOOTHSTEP_NSTEPS) {\n\t\tmemset(decay->backlog, 0, (SMOOTHSTEP_NSTEPS-1) *\n\t\t    sizeof(size_t));\n\t} else {\n\t\tsize_t nadvance_z = (size_t)nadvance_u64;\n\n\t\tassert((uint64_t)nadvance_z == nadvance_u64);\n\n\t\tmemmove(decay->backlog, &decay->backlog[nadvance_z],\n\t\t    (SMOOTHSTEP_NSTEPS - nadvance_z) * sizeof(size_t));\n\t\tif (nadvance_z > 1) {\n\t\t\tmemset(&decay->backlog[SMOOTHSTEP_NSTEPS -\n\t\t\t    nadvance_z], 0, (nadvance_z-1) * sizeof(size_t));\n\t\t}\n\t}\n\n\tarena_decay_backlog_update_last(decay, current_npages);\n}\n\nstatic void\narena_decay_try_purge(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, size_t current_npages, size_t npages_limit,\n    bool is_background_thread) {\n\tif (current_npages > npages_limit) {\n\t\tarena_decay_to_limit(tsdn, arena, decay, extents, false,\n\t\t    npages_limit, current_npages - npages_limit,\n\t\t    is_background_thread);\n\t}\n}\n\nstatic void\narena_decay_epoch_advance_helper(arena_decay_t *decay, const nstime_t *time,\n    size_t current_npages) {\n\tassert(arena_decay_deadline_reached(decay, time));\n\n\tnstime_t delta;\n\tnstime_copy(&delta, time);\n\tnstime_subtract(&delta, &decay->epoch);\n\n\tuint64_t nadvance_u64 = nstime_divide(&delta, &decay->interval);\n\tassert(nadvance_u64 > 0);\n\n\t/* Add nadvance_u64 decay intervals to epoch. */\n\tnstime_copy(&delta, &decay->interval);\n\tnstime_imultiply(&delta, nadvance_u64);\n\tnstime_add(&decay->epoch, &delta);\n\n\t/* Set a new deadline. */\n\tarena_decay_deadline_init(decay);\n\n\t/* Update the backlog. */\n\tarena_decay_backlog_update(decay, nadvance_u64, current_npages);\n}\n\nstatic void\narena_decay_epoch_advance(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, const nstime_t *time, bool is_background_thread) {\n\tsize_t current_npages = extents_npages_get(extents);\n\tarena_decay_epoch_advance_helper(decay, time, current_npages);\n\n\tsize_t npages_limit = arena_decay_backlog_npages_limit(decay);\n\t/* We may unlock decay->mtx when try_purge(). Finish logging first. */\n\tdecay->nunpurged = (npages_limit > current_npages) ? npages_limit :\n\t    current_npages;\n\n\tif (!background_thread_enabled() || is_background_thread) {\n\t\tarena_decay_try_purge(tsdn, arena, decay, extents,\n\t\t    current_npages, npages_limit, is_background_thread);\n\t}\n}\n\nstatic void\narena_decay_reinit(arena_decay_t *decay, ssize_t decay_ms) {\n\tarena_decay_ms_write(decay, decay_ms);\n\tif (decay_ms > 0) {\n\t\tnstime_init(&decay->interval, (uint64_t)decay_ms *\n\t\t    KQU(1000000));\n\t\tnstime_idivide(&decay->interval, SMOOTHSTEP_NSTEPS);\n\t}\n\n\tnstime_init(&decay->epoch, 0);\n\tnstime_update(&decay->epoch);\n\tdecay->jitter_state = (uint64_t)(uintptr_t)decay;\n\tarena_decay_deadline_init(decay);\n\tdecay->nunpurged = 0;\n\tmemset(decay->backlog, 0, SMOOTHSTEP_NSTEPS * sizeof(size_t));\n}\n\nstatic bool\narena_decay_init(arena_decay_t *decay, ssize_t decay_ms,\n    arena_stats_decay_t *stats) {\n\tif (config_debug) {\n\t\tfor (size_t i = 0; i < sizeof(arena_decay_t); i++) {\n\t\t\tassert(((char *)decay)[i] == 0);\n\t\t}\n\t\tdecay->ceil_npages = 0;\n\t}\n\tif (malloc_mutex_init(&decay->mtx, \"decay\", WITNESS_RANK_DECAY,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tdecay->purging = false;\n\tarena_decay_reinit(decay, decay_ms);\n\t/* Memory is zeroed, so there is no need to clear stats. */\n\tif (config_stats) {\n\t\tdecay->stats = stats;\n\t}\n\treturn false;\n}\n\nstatic bool\narena_decay_ms_valid(ssize_t decay_ms) {\n\tif (decay_ms < -1) {\n\t\treturn false;\n\t}\n\tif (decay_ms == -1 || (uint64_t)decay_ms <= NSTIME_SEC_MAX *\n\t    KQU(1000)) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic bool\narena_maybe_decay(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, bool is_background_thread) {\n\tmalloc_mutex_assert_owner(tsdn, &decay->mtx);\n\n\t/* Purge all or nothing if the option is disabled. */\n\tssize_t decay_ms = arena_decay_ms_read(decay);\n\tif (decay_ms <= 0) {\n\t\tif (decay_ms == 0) {\n\t\t\tarena_decay_to_limit(tsdn, arena, decay, extents, false,\n\t\t\t    0, extents_npages_get(extents),\n\t\t\t    is_background_thread);\n\t\t}\n\t\treturn false;\n\t}\n\n\tnstime_t time;\n\tnstime_init(&time, 0);\n\tnstime_update(&time);\n\tif (unlikely(!nstime_monotonic() && nstime_compare(&decay->epoch, &time)\n\t    > 0)) {\n\t\t/*\n\t\t * Time went backwards.  Move the epoch back in time and\n\t\t * generate a new deadline, with the expectation that time\n\t\t * typically flows forward for long enough periods of time that\n\t\t * epochs complete.  Unfortunately, this strategy is susceptible\n\t\t * to clock jitter triggering premature epoch advances, but\n\t\t * clock jitter estimation and compensation isn't feasible here\n\t\t * because calls into this code are event-driven.\n\t\t */\n\t\tnstime_copy(&decay->epoch, &time);\n\t\tarena_decay_deadline_init(decay);\n\t} else {\n\t\t/* Verify that time does not go backwards. */\n\t\tassert(nstime_compare(&decay->epoch, &time) <= 0);\n\t}\n\n\t/*\n\t * If the deadline has been reached, advance to the current epoch and\n\t * purge to the new limit if necessary.  Note that dirty pages created\n\t * during the current epoch are not subject to purge until a future\n\t * epoch, so as a result purging only happens during epoch advances, or\n\t * being triggered by background threads (scheduled event).\n\t */\n\tbool advance_epoch = arena_decay_deadline_reached(decay, &time);\n\tif (advance_epoch) {\n\t\tarena_decay_epoch_advance(tsdn, arena, decay, extents, &time,\n\t\t    is_background_thread);\n\t} else if (is_background_thread) {\n\t\tarena_decay_try_purge(tsdn, arena, decay, extents,\n\t\t    extents_npages_get(extents),\n\t\t    arena_decay_backlog_npages_limit(decay),\n\t\t    is_background_thread);\n\t}\n\n\treturn advance_epoch;\n}\n\nstatic ssize_t\narena_decay_ms_get(arena_decay_t *decay) {\n\treturn arena_decay_ms_read(decay);\n}\n\nssize_t\narena_dirty_decay_ms_get(arena_t *arena) {\n\treturn arena_decay_ms_get(&arena->decay_dirty);\n}\n\nssize_t\narena_muzzy_decay_ms_get(arena_t *arena) {\n\treturn arena_decay_ms_get(&arena->decay_muzzy);\n}\n\nstatic bool\narena_decay_ms_set(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, ssize_t decay_ms) {\n\tif (!arena_decay_ms_valid(decay_ms)) {\n\t\treturn true;\n\t}\n\n\tmalloc_mutex_lock(tsdn, &decay->mtx);\n\t/*\n\t * Restart decay backlog from scratch, which may cause many dirty pages\n\t * to be immediately purged.  It would conceptually be possible to map\n\t * the old backlog onto the new backlog, but there is no justification\n\t * for such complexity since decay_ms changes are intended to be\n\t * infrequent, either between the {-1, 0, >0} states, or a one-time\n\t * arbitrary change during initial arena configuration.\n\t */\n\tarena_decay_reinit(decay, decay_ms);\n\tarena_maybe_decay(tsdn, arena, decay, extents, false);\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\treturn false;\n}\n\nbool\narena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena,\n    ssize_t decay_ms) {\n\treturn arena_decay_ms_set(tsdn, arena, &arena->decay_dirty,\n\t    &arena->extents_dirty, decay_ms);\n}\n\nbool\narena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena,\n    ssize_t decay_ms) {\n\treturn arena_decay_ms_set(tsdn, arena, &arena->decay_muzzy,\n\t    &arena->extents_muzzy, decay_ms);\n}\n\nstatic size_t\narena_stash_decayed(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_limit,\n\tsize_t npages_decay_max, extent_list_t *decay_extents) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\t/* Stash extents according to npages_limit. */\n\tsize_t nstashed = 0;\n\textent_t *extent;\n\twhile (nstashed < npages_decay_max &&\n\t    (extent = extents_evict(tsdn, arena, r_extent_hooks, extents,\n\t    npages_limit)) != NULL) {\n\t\textent_list_append(decay_extents, extent);\n\t\tnstashed += extent_size_get(extent) >> LG_PAGE;\n\t}\n\treturn nstashed;\n}\n\nstatic size_t\narena_decay_stashed(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, arena_decay_t *decay, extents_t *extents,\n    bool all, extent_list_t *decay_extents, bool is_background_thread) {\n\tUNUSED size_t nmadvise, nunmapped;\n\tsize_t npurged;\n\n\tif (config_stats) {\n\t\tnmadvise = 0;\n\t\tnunmapped = 0;\n\t}\n\tnpurged = 0;\n\n\tssize_t muzzy_decay_ms = arena_muzzy_decay_ms_get(arena);\n\tfor (extent_t *extent = extent_list_first(decay_extents); extent !=\n\t    NULL; extent = extent_list_first(decay_extents)) {\n\t\tif (config_stats) {\n\t\t\tnmadvise++;\n\t\t}\n\t\tsize_t npages = extent_size_get(extent) >> LG_PAGE;\n\t\tnpurged += npages;\n\t\textent_list_remove(decay_extents, extent);\n\t\tswitch (extents_state_get(extents)) {\n\t\tcase extent_state_active:\n\t\t\tnot_reached();\n\t\tcase extent_state_dirty:\n\t\t\tif (!all && muzzy_decay_ms != 0 &&\n\t\t\t    !extent_purge_lazy_wrapper(tsdn, arena,\n\t\t\t    r_extent_hooks, extent, 0,\n\t\t\t    extent_size_get(extent))) {\n\t\t\t\textents_dalloc(tsdn, arena, r_extent_hooks,\n\t\t\t\t    &arena->extents_muzzy, extent);\n\t\t\t\tarena_background_thread_inactivity_check(tsdn,\n\t\t\t\t    arena, is_background_thread);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* Fall through. */\n\t\tcase extent_state_muzzy:\n\t\t\textent_dalloc_wrapper(tsdn, arena, r_extent_hooks,\n\t\t\t    extent);\n\t\t\tif (config_stats) {\n\t\t\t\tnunmapped += npages;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase extent_state_retained:\n\t\tdefault:\n\t\t\tnot_reached();\n\t\t}\n\t}\n\n\tif (config_stats) {\n\t\tarena_stats_lock(tsdn, &arena->stats);\n\t\tarena_stats_add_u64(tsdn, &arena->stats, &decay->stats->npurge,\n\t\t    1);\n\t\tarena_stats_add_u64(tsdn, &arena->stats,\n\t\t    &decay->stats->nmadvise, nmadvise);\n\t\tarena_stats_add_u64(tsdn, &arena->stats, &decay->stats->purged,\n\t\t    npurged);\n\t\tarena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped,\n\t\t    nunmapped << LG_PAGE);\n\t\tarena_stats_unlock(tsdn, &arena->stats);\n\t}\n\n\treturn npurged;\n}\n\n/*\n * npages_limit: Decay at most npages_decay_max pages without violating the\n * invariant: (extents_npages_get(extents) >= npages_limit).  We need an upper\n * bound on number of pages in order to prevent unbounded growth (namely in\n * stashed), otherwise unbounded new pages could be added to extents during the\n * current decay run, so that the purging thread never finishes.\n */\nstatic void\narena_decay_to_limit(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, bool all, size_t npages_limit, size_t npages_decay_max,\n    bool is_background_thread) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 1);\n\tmalloc_mutex_assert_owner(tsdn, &decay->mtx);\n\n\tif (decay->purging) {\n\t\treturn;\n\t}\n\tdecay->purging = true;\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\n\textent_list_t decay_extents;\n\textent_list_init(&decay_extents);\n\n\tsize_t npurge = arena_stash_decayed(tsdn, arena, &extent_hooks, extents,\n\t    npages_limit, npages_decay_max, &decay_extents);\n\tif (npurge != 0) {\n\t\tUNUSED size_t npurged = arena_decay_stashed(tsdn, arena,\n\t\t    &extent_hooks, decay, extents, all, &decay_extents,\n\t\t    is_background_thread);\n\t\tassert(npurged == npurge);\n\t}\n\n\tmalloc_mutex_lock(tsdn, &decay->mtx);\n\tdecay->purging = false;\n}\n\nstatic bool\narena_decay_impl(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,\n    extents_t *extents, bool is_background_thread, bool all) {\n\tif (all) {\n\t\tmalloc_mutex_lock(tsdn, &decay->mtx);\n\t\tarena_decay_to_limit(tsdn, arena, decay, extents, all, 0,\n\t\t    extents_npages_get(extents), is_background_thread);\n\t\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\t\treturn false;\n\t}\n\n\tif (malloc_mutex_trylock(tsdn, &decay->mtx)) {\n\t\t/* No need to wait if another thread is in progress. */\n\t\treturn true;\n\t}\n\n\tbool epoch_advanced = arena_maybe_decay(tsdn, arena, decay, extents,\n\t    is_background_thread);\n\tUNUSED size_t npages_new;\n\tif (epoch_advanced) {\n\t\t/* Backlog is updated on epoch advance. */\n\t\tnpages_new = decay->backlog[SMOOTHSTEP_NSTEPS-1];\n\t}\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\tif (have_background_thread && background_thread_enabled() &&\n\t    epoch_advanced && !is_background_thread) {\n\t\tbackground_thread_interval_check(tsdn, arena, decay,\n\t\t    npages_new);\n\t}\n\n\treturn false;\n}\n\nstatic bool\narena_decay_dirty(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,\n    bool all) {\n\treturn arena_decay_impl(tsdn, arena, &arena->decay_dirty,\n\t    &arena->extents_dirty, is_background_thread, all);\n}\n\nstatic bool\narena_decay_muzzy(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,\n    bool all) {\n\treturn arena_decay_impl(tsdn, arena, &arena->decay_muzzy,\n\t    &arena->extents_muzzy, is_background_thread, all);\n}\n\nvoid\narena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread, bool all) {\n\tif (arena_decay_dirty(tsdn, arena, is_background_thread, all)) {\n\t\treturn;\n\t}\n\tarena_decay_muzzy(tsdn, arena, is_background_thread, all);\n}\n\nstatic void\narena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *slab) {\n\tarena_nactive_sub(arena, extent_size_get(slab) >> LG_PAGE);\n\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\tarena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, slab);\n}\n\nstatic void\narena_bin_slabs_nonfull_insert(bin_t *bin, extent_t *slab) {\n\tassert(extent_nfree_get(slab) > 0);\n\textent_heap_insert(&bin->slabs_nonfull, slab);\n}\n\nstatic void\narena_bin_slabs_nonfull_remove(bin_t *bin, extent_t *slab) {\n\textent_heap_remove(&bin->slabs_nonfull, slab);\n}\n\nstatic extent_t *\narena_bin_slabs_nonfull_tryget(bin_t *bin) {\n\textent_t *slab = extent_heap_remove_first(&bin->slabs_nonfull);\n\tif (slab == NULL) {\n\t\treturn NULL;\n\t}\n\tif (config_stats) {\n\t\tbin->stats.reslabs++;\n\t}\n\treturn slab;\n}\n\nstatic void\narena_bin_slabs_full_insert(arena_t *arena, bin_t *bin, extent_t *slab) {\n\tassert(extent_nfree_get(slab) == 0);\n\t/*\n\t *  Tracking extents is required by arena_reset, which is not allowed\n\t *  for auto arenas.  Bypass this step to avoid touching the extent\n\t *  linkage (often results in cache misses) for auto arenas.\n\t */\n\tif (arena_is_auto(arena)) {\n\t\treturn;\n\t}\n\textent_list_append(&bin->slabs_full, slab);\n}\n\nstatic void\narena_bin_slabs_full_remove(arena_t *arena, bin_t *bin, extent_t *slab) {\n\tif (arena_is_auto(arena)) {\n\t\treturn;\n\t}\n\textent_list_remove(&bin->slabs_full, slab);\n}\n\nvoid\narena_reset(tsd_t *tsd, arena_t *arena) {\n\t/*\n\t * Locking in this function is unintuitive.  The caller guarantees that\n\t * no concurrent operations are happening in this arena, but there are\n\t * still reasons that some locking is necessary:\n\t *\n\t * - Some of the functions in the transitive closure of calls assume\n\t *   appropriate locks are held, and in some cases these locks are\n\t *   temporarily dropped to avoid lock order reversal or deadlock due to\n\t *   reentry.\n\t * - mallctl(\"epoch\", ...) may concurrently refresh stats.  While\n\t *   strictly speaking this is a \"concurrent operation\", disallowing\n\t *   stats refreshes would impose an inconvenient burden.\n\t */\n\n\t/* Large allocations. */\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx);\n\n\tfor (extent_t *extent = extent_list_first(&arena->large); extent !=\n\t    NULL; extent = extent_list_first(&arena->large)) {\n\t\tvoid *ptr = extent_base_get(extent);\n\t\tsize_t usize;\n\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);\n\t\talloc_ctx_t alloc_ctx;\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\t\tassert(alloc_ctx.szind != NSIZES);\n\n\t\tif (config_stats || (config_prof && opt_prof)) {\n\t\t\tusize = sz_index2size(alloc_ctx.szind);\n\t\t\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\t\t}\n\t\t/* Remove large allocation from prof sample set. */\n\t\tif (config_prof && opt_prof) {\n\t\t\tprof_free(tsd, ptr, usize, &alloc_ctx);\n\t\t}\n\t\tlarge_dalloc(tsd_tsdn(tsd), extent);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx);\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);\n\n\t/* Bins. */\n\tfor (unsigned i = 0; i < NBINS; i++) {\n\t\textent_t *slab;\n\t\tbin_t *bin = &arena->bins[i];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\tif (bin->slabcur != NULL) {\n\t\t\tslab = bin->slabcur;\n\t\t\tbin->slabcur = NULL;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\t\tarena_slab_dalloc(tsd_tsdn(tsd), arena, slab);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\t}\n\t\twhile ((slab = extent_heap_remove_first(&bin->slabs_nonfull)) !=\n\t\t    NULL) {\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\t\tarena_slab_dalloc(tsd_tsdn(tsd), arena, slab);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\t}\n\t\tfor (slab = extent_list_first(&bin->slabs_full); slab != NULL;\n\t\t    slab = extent_list_first(&bin->slabs_full)) {\n\t\t\tarena_bin_slabs_full_remove(arena, bin, slab);\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\t\tarena_slab_dalloc(tsd_tsdn(tsd), arena, slab);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\t}\n\t\tif (config_stats) {\n\t\t\tbin->stats.curregs = 0;\n\t\t\tbin->stats.curslabs = 0;\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t}\n\n\tatomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);\n}\n\nstatic void\narena_destroy_retained(tsdn_t *tsdn, arena_t *arena) {\n\t/*\n\t * Iterate over the retained extents and destroy them.  This gives the\n\t * extent allocator underlying the extent hooks an opportunity to unmap\n\t * all retained memory without having to keep its own metadata\n\t * structures.  In practice, virtual memory for dss-allocated extents is\n\t * leaked here, so best practice is to avoid dss for arenas to be\n\t * destroyed, or provide custom extent hooks that track retained\n\t * dss-based extents for later reuse.\n\t */\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\textent_t *extent;\n\twhile ((extent = extents_evict(tsdn, arena, &extent_hooks,\n\t    &arena->extents_retained, 0)) != NULL) {\n\t\textent_destroy_wrapper(tsdn, arena, &extent_hooks, extent);\n\t}\n}\n\nvoid\narena_destroy(tsd_t *tsd, arena_t *arena) {\n\tassert(base_ind_get(arena->base) >= narenas_auto);\n\tassert(arena_nthreads_get(arena, false) == 0);\n\tassert(arena_nthreads_get(arena, true) == 0);\n\n\t/*\n\t * No allocations have occurred since arena_reset() was called.\n\t * Furthermore, the caller (arena_i_destroy_ctl()) purged all cached\n\t * extents, so only retained extents may remain.\n\t */\n\tassert(extents_npages_get(&arena->extents_dirty) == 0);\n\tassert(extents_npages_get(&arena->extents_muzzy) == 0);\n\n\t/* Deallocate retained memory. */\n\tarena_destroy_retained(tsd_tsdn(tsd), arena);\n\n\t/*\n\t * Remove the arena pointer from the arenas array.  We rely on the fact\n\t * that there is no way for the application to get a dirty read from the\n\t * arenas array unless there is an inherent race in the application\n\t * involving access of an arena being concurrently destroyed.  The\n\t * application must synchronize knowledge of the arena's validity, so as\n\t * long as we use an atomic write to update the arenas array, the\n\t * application will get a clean read any time after it synchronizes\n\t * knowledge that the arena is no longer valid.\n\t */\n\tarena_set(base_ind_get(arena->base), NULL);\n\n\t/*\n\t * Destroy the base allocator, which manages all metadata ever mapped by\n\t * this arena.\n\t */\n\tbase_delete(tsd_tsdn(tsd), arena->base);\n}\n\nstatic extent_t *\narena_slab_alloc_hard(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, const bin_info_t *bin_info,\n    szind_t szind) {\n\textent_t *slab;\n\tbool zero, commit;\n\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tzero = false;\n\tcommit = true;\n\tslab = extent_alloc_wrapper(tsdn, arena, r_extent_hooks, NULL,\n\t    bin_info->slab_size, 0, PAGE, true, szind, &zero, &commit);\n\n\tif (config_stats && slab != NULL) {\n\t\tarena_stats_mapped_add(tsdn, &arena->stats,\n\t\t    bin_info->slab_size);\n\t}\n\n\treturn slab;\n}\n\nstatic extent_t *\narena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind,\n    const bin_info_t *bin_info) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\tszind_t szind = sz_size2index(bin_info->reg_size);\n\tbool zero = false;\n\tbool commit = true;\n\textent_t *slab = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_dirty, NULL, bin_info->slab_size, 0, PAGE, true,\n\t    binind, &zero, &commit);\n\tif (slab == NULL) {\n\t\tslab = extents_alloc(tsdn, arena, &extent_hooks,\n\t\t    &arena->extents_muzzy, NULL, bin_info->slab_size, 0, PAGE,\n\t\t    true, binind, &zero, &commit);\n\t}\n\tif (slab == NULL) {\n\t\tslab = arena_slab_alloc_hard(tsdn, arena, &extent_hooks,\n\t\t    bin_info, szind);\n\t\tif (slab == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tassert(extent_slab_get(slab));\n\n\t/* Initialize slab internals. */\n\tarena_slab_data_t *slab_data = extent_slab_data_get(slab);\n\textent_nfree_set(slab, bin_info->nregs);\n\tbitmap_init(slab_data->bitmap, &bin_info->bitmap_info, false);\n\n\tarena_nactive_add(arena, extent_size_get(slab) >> LG_PAGE);\n\n\treturn slab;\n}\n\nstatic extent_t *\narena_bin_nonfull_slab_get(tsdn_t *tsdn, arena_t *arena, bin_t *bin,\n    szind_t binind) {\n\textent_t *slab;\n\tconst bin_info_t *bin_info;\n\n\t/* Look for a usable slab. */\n\tslab = arena_bin_slabs_nonfull_tryget(bin);\n\tif (slab != NULL) {\n\t\treturn slab;\n\t}\n\t/* No existing slabs have any space available. */\n\n\tbin_info = &bin_infos[binind];\n\n\t/* Allocate a new slab. */\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t/******************************/\n\tslab = arena_slab_alloc(tsdn, arena, binind, bin_info);\n\t/********************************/\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tif (slab != NULL) {\n\t\tif (config_stats) {\n\t\t\tbin->stats.nslabs++;\n\t\t\tbin->stats.curslabs++;\n\t\t}\n\t\treturn slab;\n\t}\n\n\t/*\n\t * arena_slab_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\tslab = arena_bin_slabs_nonfull_tryget(bin);\n\tif (slab != NULL) {\n\t\treturn slab;\n\t}\n\n\treturn NULL;\n}\n\n/* Re-fill bin->slabcur, then call arena_slab_reg_alloc(). */\nstatic void *\narena_bin_malloc_hard(tsdn_t *tsdn, arena_t *arena, bin_t *bin,\n    szind_t binind) {\n\tconst bin_info_t *bin_info;\n\textent_t *slab;\n\n\tbin_info = &bin_infos[binind];\n\tif (!arena_is_auto(arena) && bin->slabcur != NULL) {\n\t\tarena_bin_slabs_full_insert(arena, bin, bin->slabcur);\n\t\tbin->slabcur = NULL;\n\t}\n\tslab = arena_bin_nonfull_slab_get(tsdn, arena, bin, binind);\n\tif (bin->slabcur != NULL) {\n\t\t/*\n\t\t * Another thread updated slabcur while this one ran without the\n\t\t * bin lock in arena_bin_nonfull_slab_get().\n\t\t */\n\t\tif (extent_nfree_get(bin->slabcur) > 0) {\n\t\t\tvoid *ret = arena_slab_reg_alloc(bin->slabcur,\n\t\t\t    bin_info);\n\t\t\tif (slab != NULL) {\n\t\t\t\t/*\n\t\t\t\t * arena_slab_alloc() may have allocated slab,\n\t\t\t\t * or it may have been pulled from\n\t\t\t\t * slabs_nonfull.  Therefore it is unsafe to\n\t\t\t\t * make any assumptions about how slab has\n\t\t\t\t * previously been used, and\n\t\t\t\t * arena_bin_lower_slab() must be called, as if\n\t\t\t\t * a region were just deallocated from the slab.\n\t\t\t\t */\n\t\t\t\tif (extent_nfree_get(slab) == bin_info->nregs) {\n\t\t\t\t\tarena_dalloc_bin_slab(tsdn, arena, slab,\n\t\t\t\t\t    bin);\n\t\t\t\t} else {\n\t\t\t\t\tarena_bin_lower_slab(tsdn, arena, slab,\n\t\t\t\t\t    bin);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tarena_bin_slabs_full_insert(arena, bin, bin->slabcur);\n\t\tbin->slabcur = NULL;\n\t}\n\n\tif (slab == NULL) {\n\t\treturn NULL;\n\t}\n\tbin->slabcur = slab;\n\n\tassert(extent_nfree_get(bin->slabcur) > 0);\n\n\treturn arena_slab_reg_alloc(slab, bin_info);\n}\n\nvoid\narena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes) {\n\tunsigned i, nfill;\n\tbin_t *bin;\n\n\tassert(tbin->ncached == 0);\n\n\tif (config_prof && arena_prof_accum(tsdn, arena, prof_accumbytes)) {\n\t\tprof_idump(tsdn);\n\t}\n\tbin = &arena->bins[binind];\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tfor (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>\n\t    tcache->lg_fill_div[binind]); i < nfill; i++) {\n\t\textent_t *slab;\n\t\tvoid *ptr;\n\t\tif ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) >\n\t\t    0) {\n\t\t\tptr = arena_slab_reg_alloc(slab, &bin_infos[binind]);\n\t\t} else {\n\t\t\tptr = arena_bin_malloc_hard(tsdn, arena, bin, binind);\n\t\t}\n\t\tif (ptr == NULL) {\n\t\t\t/*\n\t\t\t * OOM.  tbin->avail isn't yet filled down to its first\n\t\t\t * element, so the successful allocations (if any) must\n\t\t\t * be moved just before tbin->avail before bailing out.\n\t\t\t */\n\t\t\tif (i > 0) {\n\t\t\t\tmemmove(tbin->avail - i, tbin->avail - nfill,\n\t\t\t\t    i * sizeof(void *));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (config_fill && unlikely(opt_junk_alloc)) {\n\t\t\tarena_alloc_junk_small(ptr, &bin_infos[binind], true);\n\t\t}\n\t\t/* Insert such that low regions get used first. */\n\t\t*(tbin->avail - nfill + i) = ptr;\n\t}\n\tif (config_stats) {\n\t\tbin->stats.nmalloc += i;\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\tbin->stats.curregs += i;\n\t\tbin->stats.nfills++;\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\ttbin->ncached = i;\n\tarena_decay_tick(tsdn, arena);\n}\n\nvoid\narena_alloc_junk_small(void *ptr, const bin_info_t *bin_info, bool zero) {\n\tif (!zero) {\n\t\tmemset(ptr, JEMALLOC_ALLOC_JUNK, bin_info->reg_size);\n\t}\n}\n\nstatic void\narena_dalloc_junk_small_impl(void *ptr, const bin_info_t *bin_info) {\n\tmemset(ptr, JEMALLOC_FREE_JUNK, bin_info->reg_size);\n}\narena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small =\n    arena_dalloc_junk_small_impl;\n\nstatic void *\narena_malloc_small(tsdn_t *tsdn, arena_t *arena, szind_t binind, bool zero) {\n\tvoid *ret;\n\tbin_t *bin;\n\tsize_t usize;\n\textent_t *slab;\n\n\tassert(binind < NBINS);\n\tbin = &arena->bins[binind];\n\tusize = sz_index2size(binind);\n\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tif ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) > 0) {\n\t\tret = arena_slab_reg_alloc(slab, &bin_infos[binind]);\n\t} else {\n\t\tret = arena_bin_malloc_hard(tsdn, arena, bin, binind);\n\t}\n\n\tif (ret == NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t\treturn NULL;\n\t}\n\n\tif (config_stats) {\n\t\tbin->stats.nmalloc++;\n\t\tbin->stats.nrequests++;\n\t\tbin->stats.curregs++;\n\t}\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\tif (config_prof && arena_prof_accum(tsdn, arena, usize)) {\n\t\tprof_idump(tsdn);\n\t}\n\n\tif (!zero) {\n\t\tif (config_fill) {\n\t\t\tif (unlikely(opt_junk_alloc)) {\n\t\t\t\tarena_alloc_junk_small(ret,\n\t\t\t\t    &bin_infos[binind], false);\n\t\t\t} else if (unlikely(opt_zero)) {\n\t\t\t\tmemset(ret, 0, usize);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (config_fill && unlikely(opt_junk_alloc)) {\n\t\t\tarena_alloc_junk_small(ret, &bin_infos[binind],\n\t\t\t    true);\n\t\t}\n\t\tmemset(ret, 0, usize);\n\t}\n\n\tarena_decay_tick(tsdn, arena);\n\treturn ret;\n}\n\nvoid *\narena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind,\n    bool zero) {\n\tassert(!tsdn_null(tsdn) || arena != NULL);\n\n\tif (likely(!tsdn_null(tsdn))) {\n\t\tarena = arena_choose(tsdn_tsd(tsdn), arena);\n\t}\n\tif (unlikely(arena == NULL)) {\n\t\treturn NULL;\n\t}\n\n\tif (likely(size <= SMALL_MAXCLASS)) {\n\t\treturn arena_malloc_small(tsdn, arena, ind, zero);\n\t}\n\treturn large_malloc(tsdn, arena, sz_index2size(ind), zero);\n}\n\nvoid *\narena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,\n    bool zero, tcache_t *tcache) {\n\tvoid *ret;\n\n\tif (usize <= SMALL_MAXCLASS && (alignment < PAGE || (alignment == PAGE\n\t    && (usize & PAGE_MASK) == 0))) {\n\t\t/* Small; alignment doesn't require special slab placement. */\n\t\tret = arena_malloc(tsdn, arena, usize, sz_size2index(usize),\n\t\t    zero, tcache, true);\n\t} else {\n\t\tif (likely(alignment <= CACHELINE)) {\n\t\t\tret = large_malloc(tsdn, arena, usize, zero);\n\t\t} else {\n\t\t\tret = large_palloc(tsdn, arena, usize, alignment, zero);\n\t\t}\n\t}\n\treturn ret;\n}\n\nvoid\narena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\tassert(isalloc(tsdn, ptr) == LARGE_MINCLASS);\n\tassert(usize <= SMALL_MAXCLASS);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent = rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true);\n\tarena_t *arena = extent_arena_get(extent);\n\n\tszind_t szind = sz_size2index(usize);\n\textent_szind_set(extent, szind);\n\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,\n\t    szind, false);\n\n\tprof_accum_cancel(tsdn, &arena->prof_accum, usize);\n\n\tassert(isalloc(tsdn, ptr) == usize);\n}\n\nstatic size_t\narena_prof_demote(tsdn_t *tsdn, extent_t *extent, const void *ptr) {\n\tcassert(config_prof);\n\tassert(ptr != NULL);\n\n\textent_szind_set(extent, NBINS);\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,\n\t    NBINS, false);\n\n\tassert(isalloc(tsdn, ptr) == LARGE_MINCLASS);\n\n\treturn LARGE_MINCLASS;\n}\n\nvoid\narena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,\n    bool slow_path) {\n\tcassert(config_prof);\n\tassert(opt_prof);\n\n\textent_t *extent = iealloc(tsdn, ptr);\n\tsize_t usize = arena_prof_demote(tsdn, extent, ptr);\n\tif (usize <= tcache_maxclass) {\n\t\ttcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,\n\t\t    sz_size2index(usize), slow_path);\n\t} else {\n\t\tlarge_dalloc(tsdn, extent);\n\t}\n}\n\nstatic void\narena_dissociate_bin_slab(arena_t *arena, extent_t *slab, bin_t *bin) {\n\t/* Dissociate slab from bin. */\n\tif (slab == bin->slabcur) {\n\t\tbin->slabcur = NULL;\n\t} else {\n\t\tszind_t binind = extent_szind_get(slab);\n\t\tconst bin_info_t *bin_info = &bin_infos[binind];\n\n\t\t/*\n\t\t * The following block's conditional is necessary because if the\n\t\t * slab only contains one region, then it never gets inserted\n\t\t * into the non-full slabs heap.\n\t\t */\n\t\tif (bin_info->nregs == 1) {\n\t\t\tarena_bin_slabs_full_remove(arena, bin, slab);\n\t\t} else {\n\t\t\tarena_bin_slabs_nonfull_remove(bin, slab);\n\t\t}\n\t}\n}\n\nstatic void\narena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin) {\n\tassert(slab != bin->slabcur);\n\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t/******************************/\n\tarena_slab_dalloc(tsdn, arena, slab);\n\t/****************************/\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tif (config_stats) {\n\t\tbin->stats.curslabs--;\n\t}\n}\n\nstatic void\narena_bin_lower_slab(UNUSED tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    bin_t *bin) {\n\tassert(extent_nfree_get(slab) > 0);\n\n\t/*\n\t * Make sure that if bin->slabcur is non-NULL, it refers to the\n\t * oldest/lowest non-full slab.  It is okay to NULL slabcur out rather\n\t * than proactively keeping it pointing at the oldest/lowest non-full\n\t * slab.\n\t */\n\tif (bin->slabcur != NULL && extent_snad_comp(bin->slabcur, slab) > 0) {\n\t\t/* Switch slabcur. */\n\t\tif (extent_nfree_get(bin->slabcur) > 0) {\n\t\t\tarena_bin_slabs_nonfull_insert(bin, bin->slabcur);\n\t\t} else {\n\t\t\tarena_bin_slabs_full_insert(arena, bin, bin->slabcur);\n\t\t}\n\t\tbin->slabcur = slab;\n\t\tif (config_stats) {\n\t\t\tbin->stats.reslabs++;\n\t\t}\n\t} else {\n\t\tarena_bin_slabs_nonfull_insert(bin, slab);\n\t}\n}\n\nstatic void\narena_dalloc_bin_locked_impl(tsdn_t *tsdn, arena_t *arena, extent_t *slab,\n    void *ptr, bool junked) {\n\tarena_slab_data_t *slab_data = extent_slab_data_get(slab);\n\tszind_t binind = extent_szind_get(slab);\n\tbin_t *bin = &arena->bins[binind];\n\tconst bin_info_t *bin_info = &bin_infos[binind];\n\n\tif (!junked && config_fill && unlikely(opt_junk_free)) {\n\t\tarena_dalloc_junk_small(ptr, bin_info);\n\t}\n\n\tarena_slab_reg_dalloc(slab, slab_data, ptr);\n\tunsigned nfree = extent_nfree_get(slab);\n\tif (nfree == bin_info->nregs) {\n\t\tarena_dissociate_bin_slab(arena, slab, bin);\n\t\tarena_dalloc_bin_slab(tsdn, arena, slab, bin);\n\t} else if (nfree == 1 && slab != bin->slabcur) {\n\t\tarena_bin_slabs_full_remove(arena, bin, slab);\n\t\tarena_bin_lower_slab(tsdn, arena, slab, bin);\n\t}\n\n\tif (config_stats) {\n\t\tbin->stats.ndalloc++;\n\t\tbin->stats.curregs--;\n\t}\n}\n\nvoid\narena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    void *ptr) {\n\tarena_dalloc_bin_locked_impl(tsdn, arena, extent, ptr, true);\n}\n\nstatic void\narena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, extent_t *extent, void *ptr) {\n\tszind_t binind = extent_szind_get(extent);\n\tbin_t *bin = &arena->bins[binind];\n\n\tmalloc_mutex_lock(tsdn, &bin->lock);\n\tarena_dalloc_bin_locked_impl(tsdn, arena, extent, ptr, false);\n\tmalloc_mutex_unlock(tsdn, &bin->lock);\n}\n\nvoid\narena_dalloc_small(tsdn_t *tsdn, void *ptr) {\n\textent_t *extent = iealloc(tsdn, ptr);\n\tarena_t *arena = extent_arena_get(extent);\n\n\tarena_dalloc_bin(tsdn, arena, extent, ptr);\n\tarena_decay_tick(tsdn, arena);\n}\n\nbool\narena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,\n    size_t extra, bool zero) {\n\t/* Calls with non-zero extra had to clamp extra. */\n\tassert(extra == 0 || size + extra <= LARGE_MAXCLASS);\n\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\treturn true;\n\t}\n\n\textent_t *extent = iealloc(tsdn, ptr);\n\tsize_t usize_min = sz_s2u(size);\n\tsize_t usize_max = sz_s2u(size + extra);\n\tif (likely(oldsize <= SMALL_MAXCLASS && usize_min <= SMALL_MAXCLASS)) {\n\t\t/*\n\t\t * Avoid moving the allocation if the size class can be left the\n\t\t * same.\n\t\t */\n\t\tassert(bin_infos[sz_size2index(oldsize)].reg_size ==\n\t\t    oldsize);\n\t\tif ((usize_max > SMALL_MAXCLASS || sz_size2index(usize_max) !=\n\t\t    sz_size2index(oldsize)) && (size > oldsize || usize_max <\n\t\t    oldsize)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\treturn false;\n\t} else if (oldsize >= LARGE_MINCLASS && usize_max >= LARGE_MINCLASS) {\n\t\treturn large_ralloc_no_move(tsdn, extent, usize_min, usize_max,\n\t\t    zero);\n\t}\n\n\treturn true;\n}\n\nstatic void *\narena_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache) {\n\tif (alignment == 0) {\n\t\treturn arena_malloc(tsdn, arena, usize, sz_size2index(usize),\n\t\t    zero, tcache, true);\n\t}\n\tusize = sz_sa2u(usize, alignment);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\treturn ipalloct(tsdn, usize, alignment, zero, tcache, arena);\n}\n\nvoid *\narena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,\n    size_t size, size_t alignment, bool zero, tcache_t *tcache) {\n\tsize_t usize = sz_s2u(size);\n\tif (unlikely(usize == 0 || size > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\n\tif (likely(usize <= SMALL_MAXCLASS)) {\n\t\t/* Try to avoid moving the allocation. */\n\t\tif (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero)) {\n\t\t\treturn ptr;\n\t\t}\n\t}\n\n\tif (oldsize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS) {\n\t\treturn large_ralloc(tsdn, arena, iealloc(tsdn, ptr), usize,\n\t\t    alignment, zero, tcache);\n\t}\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 copying.\n\t */\n\tvoid *ret = arena_ralloc_move_helper(tsdn, arena, usize, alignment,\n\t    zero, tcache);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Junk/zero-filling were already done by\n\t * ipalloc()/arena_malloc().\n\t */\n\n\tsize_t copysize = (usize < oldsize) ? usize : oldsize;\n\tmemcpy(ret, ptr, copysize);\n\tisdalloct(tsdn, ptr, oldsize, tcache, NULL, true);\n\treturn ret;\n}\n\ndss_prec_t\narena_dss_prec_get(arena_t *arena) {\n\treturn (dss_prec_t)atomic_load_u(&arena->dss_prec, ATOMIC_ACQUIRE);\n}\n\nbool\narena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) {\n\tif (!have_dss) {\n\t\treturn (dss_prec != dss_prec_disabled);\n\t}\n\tatomic_store_u(&arena->dss_prec, (unsigned)dss_prec, ATOMIC_RELEASE);\n\treturn false;\n}\n\nssize_t\narena_dirty_decay_ms_default_get(void) {\n\treturn atomic_load_zd(&dirty_decay_ms_default, ATOMIC_RELAXED);\n}\n\nbool\narena_dirty_decay_ms_default_set(ssize_t decay_ms) {\n\tif (!arena_decay_ms_valid(decay_ms)) {\n\t\treturn true;\n\t}\n\tatomic_store_zd(&dirty_decay_ms_default, decay_ms, ATOMIC_RELAXED);\n\treturn false;\n}\n\nssize_t\narena_muzzy_decay_ms_default_get(void) {\n\treturn atomic_load_zd(&muzzy_decay_ms_default, ATOMIC_RELAXED);\n}\n\nbool\narena_muzzy_decay_ms_default_set(ssize_t decay_ms) {\n\tif (!arena_decay_ms_valid(decay_ms)) {\n\t\treturn true;\n\t}\n\tatomic_store_zd(&muzzy_decay_ms_default, decay_ms, ATOMIC_RELAXED);\n\treturn false;\n}\n\nbool\narena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena, size_t *old_limit,\n    size_t *new_limit) {\n\tassert(opt_retain);\n\n\tpszind_t new_ind JEMALLOC_CC_SILENCE_INIT(0);\n\tif (new_limit != NULL) {\n\t\tsize_t limit = *new_limit;\n\t\t/* Grow no more than the new limit. */\n\t\tif ((new_ind = sz_psz2ind(limit + 1) - 1) >\n\t\t     EXTENT_GROW_MAX_PIND) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &arena->extent_grow_mtx);\n\tif (old_limit != NULL) {\n\t\t*old_limit = sz_pind2sz(arena->retain_grow_limit);\n\t}\n\tif (new_limit != NULL) {\n\t\tarena->retain_grow_limit = new_ind;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arena->extent_grow_mtx);\n\n\treturn false;\n}\n\nunsigned\narena_nthreads_get(arena_t *arena, bool internal) {\n\treturn atomic_load_u(&arena->nthreads[internal], ATOMIC_RELAXED);\n}\n\nvoid\narena_nthreads_inc(arena_t *arena, bool internal) {\n\tatomic_fetch_add_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED);\n}\n\nvoid\narena_nthreads_dec(arena_t *arena, bool internal) {\n\tatomic_fetch_sub_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED);\n}\n\nsize_t\narena_extent_sn_next(arena_t *arena) {\n\treturn atomic_fetch_add_zu(&arena->extent_sn_next, 1, ATOMIC_RELAXED);\n}\n\narena_t *\narena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tarena_t *arena;\n\tbase_t *base;\n\tunsigned i;\n\n\tif (ind == 0) {\n\t\tbase = b0get();\n\t} else {\n\t\tbase = base_new(tsdn, ind, extent_hooks);\n\t\tif (base == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tarena = (arena_t *)base_alloc(tsdn, base, sizeof(arena_t), CACHELINE);\n\tif (arena == NULL) {\n\t\tgoto label_error;\n\t}\n\n\tatomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED);\n\tatomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED);\n\tarena->last_thd = NULL;\n\n\tif (config_stats) {\n\t\tif (arena_stats_init(tsdn, &arena->stats)) {\n\t\t\tgoto label_error;\n\t\t}\n\n\t\tql_new(&arena->tcache_ql);\n\t\tql_new(&arena->cache_bin_array_descriptor_ql);\n\t\tif (malloc_mutex_init(&arena->tcache_ql_mtx, \"tcache_ql\",\n\t\t    WITNESS_RANK_TCACHE_QL, malloc_mutex_rank_exclusive)) {\n\t\t\tgoto label_error;\n\t\t}\n\t}\n\n\tif (config_prof) {\n\t\tif (prof_accum_init(tsdn, &arena->prof_accum)) {\n\t\t\tgoto label_error;\n\t\t}\n\t}\n\n\tif (config_cache_oblivious) {\n\t\t/*\n\t\t * A nondeterministic seed based on the address of arena reduces\n\t\t * the likelihood of lockstep non-uniform cache index\n\t\t * utilization among identical concurrent processes, but at the\n\t\t * cost of test repeatability.  For debug builds, instead use a\n\t\t * deterministic seed.\n\t\t */\n\t\tatomic_store_zu(&arena->offset_state, config_debug ? ind :\n\t\t    (size_t)(uintptr_t)arena, ATOMIC_RELAXED);\n\t}\n\n\tatomic_store_zu(&arena->extent_sn_next, 0, ATOMIC_RELAXED);\n\n\tatomic_store_u(&arena->dss_prec, (unsigned)extent_dss_prec_get(),\n\t    ATOMIC_RELAXED);\n\n\tatomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);\n\n\textent_list_init(&arena->large);\n\tif (malloc_mutex_init(&arena->large_mtx, \"arena_large\",\n\t    WITNESS_RANK_ARENA_LARGE, malloc_mutex_rank_exclusive)) {\n\t\tgoto label_error;\n\t}\n\n\t/*\n\t * Delay coalescing for dirty extents despite the disruptive effect on\n\t * memory layout for best-fit extent allocation, since cached extents\n\t * are likely to be reused soon after deallocation, and the cost of\n\t * merging/splitting extents is non-trivial.\n\t */\n\tif (extents_init(tsdn, &arena->extents_dirty, extent_state_dirty,\n\t    true)) {\n\t\tgoto label_error;\n\t}\n\t/*\n\t * Coalesce muzzy extents immediately, because operations on them are in\n\t * the critical path much less often than for dirty extents.\n\t */\n\tif (extents_init(tsdn, &arena->extents_muzzy, extent_state_muzzy,\n\t    false)) {\n\t\tgoto label_error;\n\t}\n\t/*\n\t * Coalesce retained extents immediately, in part because they will\n\t * never be evicted (and therefore there's no opportunity for delayed\n\t * coalescing), but also because operations on retained extents are not\n\t * in the critical path.\n\t */\n\tif (extents_init(tsdn, &arena->extents_retained, extent_state_retained,\n\t    false)) {\n\t\tgoto label_error;\n\t}\n\n\tif (arena_decay_init(&arena->decay_dirty,\n\t    arena_dirty_decay_ms_default_get(), &arena->stats.decay_dirty)) {\n\t\tgoto label_error;\n\t}\n\tif (arena_decay_init(&arena->decay_muzzy,\n\t    arena_muzzy_decay_ms_default_get(), &arena->stats.decay_muzzy)) {\n\t\tgoto label_error;\n\t}\n\n\tarena->extent_grow_next = sz_psz2ind(HUGEPAGE);\n\tarena->retain_grow_limit = EXTENT_GROW_MAX_PIND;\n\tif (malloc_mutex_init(&arena->extent_grow_mtx, \"extent_grow\",\n\t    WITNESS_RANK_EXTENT_GROW, malloc_mutex_rank_exclusive)) {\n\t\tgoto label_error;\n\t}\n\n\textent_avail_new(&arena->extent_avail);\n\tif (malloc_mutex_init(&arena->extent_avail_mtx, \"extent_avail\",\n\t    WITNESS_RANK_EXTENT_AVAIL, malloc_mutex_rank_exclusive)) {\n\t\tgoto label_error;\n\t}\n\n\t/* Initialize bins. */\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbool err = bin_init(&arena->bins[i]);\n\t\tif (err) {\n\t\t\tgoto label_error;\n\t\t}\n\t}\n\n\tarena->base = base;\n\t/* Set arena before creating background threads. */\n\tarena_set(ind, arena);\n\n\tnstime_init(&arena->create_time, 0);\n\tnstime_update(&arena->create_time);\n\n\t/* We don't support reentrancy for arena 0 bootstrapping. */\n\tif (ind != 0) {\n\t\t/*\n\t\t * If we're here, then arena 0 already exists, so bootstrapping\n\t\t * is done enough that we should have tsd.\n\t\t */\n\t\tassert(!tsdn_null(tsdn));\n\t\tpre_reentrancy(tsdn_tsd(tsdn), arena);\n\t\tif (hooks_arena_new_hook) {\n\t\t\thooks_arena_new_hook();\n\t\t}\n\t\tpost_reentrancy(tsdn_tsd(tsdn));\n\t}\n\n\treturn arena;\nlabel_error:\n\tif (ind != 0) {\n\t\tbase_delete(tsdn, base);\n\t}\n\treturn NULL;\n}\n\nvoid\narena_boot(void) {\n\tarena_dirty_decay_ms_default_set(opt_dirty_decay_ms);\n\tarena_muzzy_decay_ms_default_set(opt_muzzy_decay_ms);\n#define REGIND_bin_yes(index, reg_size) \t\t\t\t\\\n\tdiv_init(&arena_binind_div_info[(index)], (reg_size));\n#define REGIND_bin_no(index, reg_size)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs,\t\t\\\n    lg_delta_lookup)\t\t\t\t\t\t\t\\\n\tREGIND_bin_##bin(index, (1U<<lg_grp) + (ndelta << lg_delta))\n\tSIZE_CLASSES\n#undef REGIND_bin_yes\n#undef REGIND_bin_no\n#undef SC\n}\n\nvoid\narena_prefork0(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_prefork(tsdn, &arena->decay_muzzy.mtx);\n}\n\nvoid\narena_prefork1(tsdn_t *tsdn, arena_t *arena) {\n\tif (config_stats) {\n\t\tmalloc_mutex_prefork(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n\nvoid\narena_prefork2(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->extent_grow_mtx);\n}\n\nvoid\narena_prefork3(tsdn_t *tsdn, arena_t *arena) {\n\textents_prefork(tsdn, &arena->extents_dirty);\n\textents_prefork(tsdn, &arena->extents_muzzy);\n\textents_prefork(tsdn, &arena->extents_retained);\n}\n\nvoid\narena_prefork4(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->extent_avail_mtx);\n}\n\nvoid\narena_prefork5(tsdn_t *tsdn, arena_t *arena) {\n\tbase_prefork(tsdn, arena->base);\n}\n\nvoid\narena_prefork6(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_prefork(tsdn, &arena->large_mtx);\n}\n\nvoid\narena_prefork7(tsdn_t *tsdn, arena_t *arena) {\n\tfor (unsigned i = 0; i < NBINS; i++) {\n\t\tbin_prefork(tsdn, &arena->bins[i]);\n\t}\n}\n\nvoid\narena_postfork_parent(tsdn_t *tsdn, arena_t *arena) {\n\tunsigned i;\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin_postfork_parent(tsdn, &arena->bins[i]);\n\t}\n\tmalloc_mutex_postfork_parent(tsdn, &arena->large_mtx);\n\tbase_postfork_parent(tsdn, arena->base);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->extent_avail_mtx);\n\textents_postfork_parent(tsdn, &arena->extents_dirty);\n\textents_postfork_parent(tsdn, &arena->extents_muzzy);\n\textents_postfork_parent(tsdn, &arena->extents_retained);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->extent_grow_mtx);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_postfork_parent(tsdn, &arena->decay_muzzy.mtx);\n\tif (config_stats) {\n\t\tmalloc_mutex_postfork_parent(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n\nvoid\narena_postfork_child(tsdn_t *tsdn, arena_t *arena) {\n\tunsigned i;\n\n\tatomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED);\n\tatomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED);\n\tif (tsd_arena_get(tsdn_tsd(tsdn)) == arena) {\n\t\tarena_nthreads_inc(arena, false);\n\t}\n\tif (tsd_iarena_get(tsdn_tsd(tsdn)) == arena) {\n\t\tarena_nthreads_inc(arena, true);\n\t}\n\tif (config_stats) {\n\t\tql_new(&arena->tcache_ql);\n\t\tql_new(&arena->cache_bin_array_descriptor_ql);\n\t\ttcache_t *tcache = tcache_get(tsdn_tsd(tsdn));\n\t\tif (tcache != NULL && tcache->arena == arena) {\n\t\t\tql_elm_new(tcache, link);\n\t\t\tql_tail_insert(&arena->tcache_ql, tcache, link);\n\t\t\tcache_bin_array_descriptor_init(\n\t\t\t    &tcache->cache_bin_array_descriptor,\n\t\t\t    tcache->bins_small, tcache->bins_large);\n\t\t\tql_tail_insert(&arena->cache_bin_array_descriptor_ql,\n\t\t\t    &tcache->cache_bin_array_descriptor, link);\n\t\t}\n\t}\n\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin_postfork_child(tsdn, &arena->bins[i]);\n\t}\n\tmalloc_mutex_postfork_child(tsdn, &arena->large_mtx);\n\tbase_postfork_child(tsdn, arena->base);\n\tmalloc_mutex_postfork_child(tsdn, &arena->extent_avail_mtx);\n\textents_postfork_child(tsdn, &arena->extents_dirty);\n\textents_postfork_child(tsdn, &arena->extents_muzzy);\n\textents_postfork_child(tsdn, &arena->extents_retained);\n\tmalloc_mutex_postfork_child(tsdn, &arena->extent_grow_mtx);\n\tmalloc_mutex_postfork_child(tsdn, &arena->decay_dirty.mtx);\n\tmalloc_mutex_postfork_child(tsdn, &arena->decay_muzzy.mtx);\n\tif (config_stats) {\n\t\tmalloc_mutex_postfork_child(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/background_thread.c",
    "content": "#define JEMALLOC_BACKGROUND_THREAD_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/* This option should be opt-in only. */\n#define BACKGROUND_THREAD_DEFAULT false\n/* Read-only after initialization. */\nbool opt_background_thread = BACKGROUND_THREAD_DEFAULT;\nsize_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT;\n\n/* Used for thread creation, termination and stats. */\nmalloc_mutex_t background_thread_lock;\n/* Indicates global state.  Atomic because decay reads this w/o locking. */\natomic_b_t background_thread_enabled_state;\nsize_t n_background_threads;\nsize_t max_background_threads;\n/* Thread info per-index. */\nbackground_thread_info_t *background_thread_info;\n\n/* False if no necessary runtime support. */\nbool can_enable_background_thread;\n\n/******************************************************************************/\n\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\n#include <dlfcn.h>\n\nstatic int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *,\n    void *(*)(void *), void *__restrict);\n\nstatic void\npthread_create_wrapper_init(void) {\n#ifdef JEMALLOC_LAZY_LOCK\n\tif (!isthreaded) {\n\t\tisthreaded = true;\n\t}\n#endif\n}\n\nint\npthread_create_wrapper(pthread_t *__restrict thread, const pthread_attr_t *attr,\n    void *(*start_routine)(void *), void *__restrict arg) {\n\tpthread_create_wrapper_init();\n\n\treturn pthread_create_fptr(thread, attr, start_routine, arg);\n}\n#endif /* JEMALLOC_PTHREAD_CREATE_WRAPPER */\n\n#ifndef JEMALLOC_BACKGROUND_THREAD\n#define NOT_REACHED { not_reached(); }\nbool background_thread_create(tsd_t *tsd, unsigned arena_ind) NOT_REACHED\nbool background_threads_enable(tsd_t *tsd) NOT_REACHED\nbool background_threads_disable(tsd_t *tsd) NOT_REACHED\nvoid background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, size_t npages_new) NOT_REACHED\nvoid background_thread_prefork0(tsdn_t *tsdn) NOT_REACHED\nvoid background_thread_prefork1(tsdn_t *tsdn) NOT_REACHED\nvoid background_thread_postfork_parent(tsdn_t *tsdn) NOT_REACHED\nvoid background_thread_postfork_child(tsdn_t *tsdn) NOT_REACHED\nbool background_thread_stats_read(tsdn_t *tsdn,\n    background_thread_stats_t *stats) NOT_REACHED\nvoid background_thread_ctl_init(tsdn_t *tsdn) NOT_REACHED\n#undef NOT_REACHED\n#else\n\nstatic bool background_thread_enabled_at_fork;\n\nstatic void\nbackground_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) {\n\tbackground_thread_wakeup_time_set(tsdn, info, 0);\n\tinfo->npages_to_purge_new = 0;\n\tif (config_stats) {\n\t\tinfo->tot_n_runs = 0;\n\t\tnstime_init(&info->tot_sleep_time, 0);\n\t}\n}\n\nstatic inline bool\nset_current_thread_affinity(UNUSED int cpu) {\n#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)\n\tcpu_set_t cpuset;\n\tCPU_ZERO(&cpuset);\n\tCPU_SET(cpu, &cpuset);\n\tint ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);\n\n\treturn (ret != 0);\n#else\n\treturn false;\n#endif\n}\n\n/* Threshold for determining when to wake up the background thread. */\n#define BACKGROUND_THREAD_NPAGES_THRESHOLD UINT64_C(1024)\n#define BILLION UINT64_C(1000000000)\n/* Minimal sleep interval 100 ms. */\n#define BACKGROUND_THREAD_MIN_INTERVAL_NS (BILLION / 10)\n\nstatic inline size_t\ndecay_npurge_after_interval(arena_decay_t *decay, size_t interval) {\n\tsize_t i;\n\tuint64_t sum = 0;\n\tfor (i = 0; i < interval; i++) {\n\t\tsum += decay->backlog[i] * h_steps[i];\n\t}\n\tfor (; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tsum += decay->backlog[i] * (h_steps[i] - h_steps[i - interval]);\n\t}\n\n\treturn (size_t)(sum >> SMOOTHSTEP_BFP);\n}\n\nstatic uint64_t\narena_decay_compute_purge_interval_impl(tsdn_t *tsdn, arena_decay_t *decay,\n    extents_t *extents) {\n\tif (malloc_mutex_trylock(tsdn, &decay->mtx)) {\n\t\t/* Use minimal interval if decay is contended. */\n\t\treturn BACKGROUND_THREAD_MIN_INTERVAL_NS;\n\t}\n\n\tuint64_t interval;\n\tssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);\n\tif (decay_time <= 0) {\n\t\t/* Purging is eagerly done or disabled currently. */\n\t\tinterval = BACKGROUND_THREAD_INDEFINITE_SLEEP;\n\t\tgoto label_done;\n\t}\n\n\tuint64_t decay_interval_ns = nstime_ns(&decay->interval);\n\tassert(decay_interval_ns > 0);\n\tsize_t npages = extents_npages_get(extents);\n\tif (npages == 0) {\n\t\tunsigned i;\n\t\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\t\tif (decay->backlog[i] > 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == SMOOTHSTEP_NSTEPS) {\n\t\t\t/* No dirty pages recorded.  Sleep indefinitely. */\n\t\t\tinterval = BACKGROUND_THREAD_INDEFINITE_SLEEP;\n\t\t\tgoto label_done;\n\t\t}\n\t}\n\tif (npages <= BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\t/* Use max interval. */\n\t\tinterval = decay_interval_ns * SMOOTHSTEP_NSTEPS;\n\t\tgoto label_done;\n\t}\n\n\tsize_t lb = BACKGROUND_THREAD_MIN_INTERVAL_NS / decay_interval_ns;\n\tsize_t ub = SMOOTHSTEP_NSTEPS;\n\t/* Minimal 2 intervals to ensure reaching next epoch deadline. */\n\tlb = (lb < 2) ? 2 : lb;\n\tif ((decay_interval_ns * ub <= BACKGROUND_THREAD_MIN_INTERVAL_NS) ||\n\t    (lb + 2 > ub)) {\n\t\tinterval = BACKGROUND_THREAD_MIN_INTERVAL_NS;\n\t\tgoto label_done;\n\t}\n\n\tassert(lb + 2 <= ub);\n\tsize_t npurge_lb, npurge_ub;\n\tnpurge_lb = decay_npurge_after_interval(decay, lb);\n\tif (npurge_lb > BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\tinterval = decay_interval_ns * lb;\n\t\tgoto label_done;\n\t}\n\tnpurge_ub = decay_npurge_after_interval(decay, ub);\n\tif (npurge_ub < BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\tinterval = decay_interval_ns * ub;\n\t\tgoto label_done;\n\t}\n\n\tunsigned n_search = 0;\n\tsize_t target, npurge;\n\twhile ((npurge_lb + BACKGROUND_THREAD_NPAGES_THRESHOLD < npurge_ub)\n\t    && (lb + 2 < ub)) {\n\t\ttarget = (lb + ub) / 2;\n\t\tnpurge = decay_npurge_after_interval(decay, target);\n\t\tif (npurge > BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\t\tub = target;\n\t\t\tnpurge_ub = npurge;\n\t\t} else {\n\t\t\tlb = target;\n\t\t\tnpurge_lb = npurge;\n\t\t}\n\t\tassert(n_search++ < lg_floor(SMOOTHSTEP_NSTEPS) + 1);\n\t}\n\tinterval = decay_interval_ns * (ub + lb) / 2;\nlabel_done:\n\tinterval = (interval < BACKGROUND_THREAD_MIN_INTERVAL_NS) ?\n\t    BACKGROUND_THREAD_MIN_INTERVAL_NS : interval;\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\n\n\treturn interval;\n}\n\n/* Compute purge interval for background threads. */\nstatic uint64_t\narena_decay_compute_purge_interval(tsdn_t *tsdn, arena_t *arena) {\n\tuint64_t i1, i2;\n\ti1 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_dirty,\n\t    &arena->extents_dirty);\n\tif (i1 == BACKGROUND_THREAD_MIN_INTERVAL_NS) {\n\t\treturn i1;\n\t}\n\ti2 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_muzzy,\n\t    &arena->extents_muzzy);\n\n\treturn i1 < i2 ? i1 : i2;\n}\n\nstatic void\nbackground_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,\n    uint64_t interval) {\n\tif (config_stats) {\n\t\tinfo->tot_n_runs++;\n\t}\n\tinfo->npages_to_purge_new = 0;\n\n\tstruct timeval tv;\n\t/* Specific clock required by timedwait. */\n\tgettimeofday(&tv, NULL);\n\tnstime_t before_sleep;\n\tnstime_init2(&before_sleep, tv.tv_sec, tv.tv_usec * 1000);\n\n\tint ret;\n\tif (interval == BACKGROUND_THREAD_INDEFINITE_SLEEP) {\n\t\tassert(background_thread_indefinite_sleep(info));\n\t\tret = pthread_cond_wait(&info->cond, &info->mtx.lock);\n\t\tassert(ret == 0);\n\t} else {\n\t\tassert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS &&\n\t\t    interval <= BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\t\t/* We need malloc clock (can be different from tv). */\n\t\tnstime_t next_wakeup;\n\t\tnstime_init(&next_wakeup, 0);\n\t\tnstime_update(&next_wakeup);\n\t\tnstime_iadd(&next_wakeup, interval);\n\t\tassert(nstime_ns(&next_wakeup) <\n\t\t    BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\t\tbackground_thread_wakeup_time_set(tsdn, info,\n\t\t    nstime_ns(&next_wakeup));\n\n\t\tnstime_t ts_wakeup;\n\t\tnstime_copy(&ts_wakeup, &before_sleep);\n\t\tnstime_iadd(&ts_wakeup, interval);\n\t\tstruct timespec ts;\n\t\tts.tv_sec = (size_t)nstime_sec(&ts_wakeup);\n\t\tts.tv_nsec = (size_t)nstime_nsec(&ts_wakeup);\n\n\t\tassert(!background_thread_indefinite_sleep(info));\n\t\tret = pthread_cond_timedwait(&info->cond, &info->mtx.lock, &ts);\n\t\tassert(ret == ETIMEDOUT || ret == 0);\n\t\tbackground_thread_wakeup_time_set(tsdn, info,\n\t\t    BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\t}\n\tif (config_stats) {\n\t\tgettimeofday(&tv, NULL);\n\t\tnstime_t after_sleep;\n\t\tnstime_init2(&after_sleep, tv.tv_sec, tv.tv_usec * 1000);\n\t\tif (nstime_compare(&after_sleep, &before_sleep) > 0) {\n\t\t\tnstime_subtract(&after_sleep, &before_sleep);\n\t\t\tnstime_add(&info->tot_sleep_time, &after_sleep);\n\t\t}\n\t}\n}\n\nstatic bool\nbackground_thread_pause_check(tsdn_t *tsdn, background_thread_info_t *info) {\n\tif (unlikely(info->state == background_thread_paused)) {\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t\t/* Wait on global lock to update status. */\n\t\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\t\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline void\nbackground_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info, unsigned ind) {\n\tuint64_t min_interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;\n\tunsigned narenas = narenas_total_get();\n\n\tfor (unsigned i = ind; i < narenas; i += max_background_threads) {\n\t\tarena_t *arena = arena_get(tsdn, i, false);\n\t\tif (!arena) {\n\t\t\tcontinue;\n\t\t}\n\t\tarena_decay(tsdn, arena, true, false);\n\t\tif (min_interval == BACKGROUND_THREAD_MIN_INTERVAL_NS) {\n\t\t\t/* Min interval will be used. */\n\t\t\tcontinue;\n\t\t}\n\t\tuint64_t interval = arena_decay_compute_purge_interval(tsdn,\n\t\t    arena);\n\t\tassert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS);\n\t\tif (min_interval > interval) {\n\t\t\tmin_interval = interval;\n\t\t}\n\t}\n\tbackground_thread_sleep(tsdn, info, min_interval);\n}\n\nstatic bool\nbackground_threads_disable_single(tsd_t *tsd, background_thread_info_t *info) {\n\tif (info == &background_thread_info[0]) {\n\t\tmalloc_mutex_assert_owner(tsd_tsdn(tsd),\n\t\t    &background_thread_lock);\n\t} else {\n\t\tmalloc_mutex_assert_not_owner(tsd_tsdn(tsd),\n\t\t    &background_thread_lock);\n\t}\n\n\tpre_reentrancy(tsd, NULL);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\tbool has_thread;\n\tassert(info->state != background_thread_paused);\n\tif (info->state == background_thread_started) {\n\t\thas_thread = true;\n\t\tinfo->state = background_thread_stopped;\n\t\tpthread_cond_signal(&info->cond);\n\t} else {\n\t\thas_thread = false;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\n\tif (!has_thread) {\n\t\tpost_reentrancy(tsd);\n\t\treturn false;\n\t}\n\tvoid *ret;\n\tif (pthread_join(info->thread, &ret)) {\n\t\tpost_reentrancy(tsd);\n\t\treturn true;\n\t}\n\tassert(ret == NULL);\n\tn_background_threads--;\n\tpost_reentrancy(tsd);\n\n\treturn false;\n}\n\nstatic void *background_thread_entry(void *ind_arg);\n\nstatic int\nbackground_thread_create_signals_masked(pthread_t *thread,\n    const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) {\n\t/*\n\t * Mask signals during thread creation so that the thread inherits\n\t * an empty signal set.\n\t */\n\tsigset_t set;\n\tsigfillset(&set);\n\tsigset_t oldset;\n\tint mask_err = pthread_sigmask(SIG_SETMASK, &set, &oldset);\n\tif (mask_err != 0) {\n\t\treturn mask_err;\n\t}\n\tint create_err = pthread_create_wrapper(thread, attr, start_routine,\n\t    arg);\n\t/*\n\t * Restore the signal mask.  Failure to restore the signal mask here\n\t * changes program behavior.\n\t */\n\tint restore_err = pthread_sigmask(SIG_SETMASK, &oldset, NULL);\n\tif (restore_err != 0) {\n\t\tmalloc_printf(\"<jemalloc>: background thread creation \"\n\t\t    \"failed (%d), and signal mask restoration failed \"\n\t\t    \"(%d)\\n\", create_err, restore_err);\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t}\n\treturn create_err;\n}\n\nstatic bool\ncheck_background_thread_creation(tsd_t *tsd, unsigned *n_created,\n    bool *created_threads) {\n\tbool ret = false;\n\tif (likely(*n_created == n_background_threads)) {\n\t\treturn ret;\n\t}\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\tmalloc_mutex_unlock(tsdn, &background_thread_info[0].mtx);\n\tfor (unsigned i = 1; i < max_background_threads; i++) {\n\t\tif (created_threads[i]) {\n\t\t\tcontinue;\n\t\t}\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\t/*\n\t\t * In case of the background_thread_paused state because of\n\t\t * arena reset, delay the creation.\n\t\t */\n\t\tbool create = (info->state == background_thread_started);\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t\tif (!create) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tpre_reentrancy(tsd, NULL);\n\t\tint err = background_thread_create_signals_masked(&info->thread,\n\t\t    NULL, background_thread_entry, (void *)(uintptr_t)i);\n\t\tpost_reentrancy(tsd);\n\n\t\tif (err == 0) {\n\t\t\t(*n_created)++;\n\t\t\tcreated_threads[i] = true;\n\t\t} else {\n\t\t\tmalloc_printf(\"<jemalloc>: background thread \"\n\t\t\t    \"creation failed (%d)\\n\", err);\n\t\t\tif (opt_abort) {\n\t\t\t\tabort();\n\t\t\t}\n\t\t}\n\t\t/* Return to restart the loop since we unlocked. */\n\t\tret = true;\n\t\tbreak;\n\t}\n\tmalloc_mutex_lock(tsdn, &background_thread_info[0].mtx);\n\n\treturn ret;\n}\n\nstatic void\nbackground_thread0_work(tsd_t *tsd) {\n\t/* Thread0 is also responsible for launching / terminating threads. */\n\tVARIABLE_ARRAY(bool, created_threads, max_background_threads);\n\tunsigned i;\n\tfor (i = 1; i < max_background_threads; i++) {\n\t\tcreated_threads[i] = false;\n\t}\n\t/* Start working, and create more threads when asked. */\n\tunsigned n_created = 1;\n\twhile (background_thread_info[0].state != background_thread_stopped) {\n\t\tif (background_thread_pause_check(tsd_tsdn(tsd),\n\t\t    &background_thread_info[0])) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (check_background_thread_creation(tsd, &n_created,\n\t\t    (bool *)&created_threads)) {\n\t\t\tcontinue;\n\t\t}\n\t\tbackground_work_sleep_once(tsd_tsdn(tsd),\n\t\t    &background_thread_info[0], 0);\n\t}\n\n\t/*\n\t * Shut down other threads at exit.  Note that the ctl thread is holding\n\t * the global background_thread mutex (and is waiting) for us.\n\t */\n\tassert(!background_thread_enabled());\n\tfor (i = 1; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tassert(info->state != background_thread_paused);\n\t\tif (created_threads[i]) {\n\t\t\tbackground_threads_disable_single(tsd, info);\n\t\t} else {\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\t\tif (info->state != background_thread_stopped) {\n\t\t\t\t/* The thread was not created. */\n\t\t\t\tassert(info->state ==\n\t\t\t\t    background_thread_started);\n\t\t\t\tn_background_threads--;\n\t\t\t\tinfo->state = background_thread_stopped;\n\t\t\t}\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\t}\n\t}\n\tbackground_thread_info[0].state = background_thread_stopped;\n\tassert(n_background_threads == 1);\n}\n\nstatic void\nbackground_work(tsd_t *tsd, unsigned ind) {\n\tbackground_thread_info_t *info = &background_thread_info[ind];\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\tbackground_thread_wakeup_time_set(tsd_tsdn(tsd), info,\n\t    BACKGROUND_THREAD_INDEFINITE_SLEEP);\n\tif (ind == 0) {\n\t\tbackground_thread0_work(tsd);\n\t} else {\n\t\twhile (info->state != background_thread_stopped) {\n\t\t\tif (background_thread_pause_check(tsd_tsdn(tsd),\n\t\t\t    info)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbackground_work_sleep_once(tsd_tsdn(tsd), info, ind);\n\t\t}\n\t}\n\tassert(info->state == background_thread_stopped);\n\tbackground_thread_wakeup_time_set(tsd_tsdn(tsd), info, 0);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n}\n\nstatic void *\nbackground_thread_entry(void *ind_arg) {\n\tunsigned thread_ind = (unsigned)(uintptr_t)ind_arg;\n\tassert(thread_ind < max_background_threads);\n#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP\n\tpthread_setname_np(pthread_self(), \"jemalloc_bg_thd\");\n#endif\n\tif (opt_percpu_arena != percpu_arena_disabled) {\n\t\tset_current_thread_affinity((int)thread_ind);\n\t}\n\t/*\n\t * Start periodic background work.  We use internal tsd which avoids\n\t * side effects, for example triggering new arena creation (which in\n\t * turn triggers another background thread creation).\n\t */\n\tbackground_work(tsd_internal_fetch(), thread_ind);\n\tassert(pthread_equal(pthread_self(),\n\t    background_thread_info[thread_ind].thread));\n\n\treturn NULL;\n}\n\nstatic void\nbackground_thread_init(tsd_t *tsd, background_thread_info_t *info) {\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\tinfo->state = background_thread_started;\n\tbackground_thread_info_init(tsd_tsdn(tsd), info);\n\tn_background_threads++;\n}\n\n/* Create a new background thread if needed. */\nbool\nbackground_thread_create(tsd_t *tsd, unsigned arena_ind) {\n\tassert(have_background_thread);\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\n\t/* We create at most NCPUs threads. */\n\tsize_t thread_ind = arena_ind % max_background_threads;\n\tbackground_thread_info_t *info = &background_thread_info[thread_ind];\n\n\tbool need_new_thread;\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\tneed_new_thread = background_thread_enabled() &&\n\t    (info->state == background_thread_stopped);\n\tif (need_new_thread) {\n\t\tbackground_thread_init(tsd, info);\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\tif (!need_new_thread) {\n\t\treturn false;\n\t}\n\tif (arena_ind != 0) {\n\t\t/* Threads are created asynchronously by Thread 0. */\n\t\tbackground_thread_info_t *t0 = &background_thread_info[0];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &t0->mtx);\n\t\tassert(t0->state == background_thread_started);\n\t\tpthread_cond_signal(&t0->cond);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &t0->mtx);\n\n\t\treturn false;\n\t}\n\n\tpre_reentrancy(tsd, NULL);\n\t/*\n\t * To avoid complications (besides reentrancy), create internal\n\t * background threads with the underlying pthread_create.\n\t */\n\tint err = background_thread_create_signals_masked(&info->thread, NULL,\n\t    background_thread_entry, (void *)thread_ind);\n\tpost_reentrancy(tsd);\n\n\tif (err != 0) {\n\t\tmalloc_printf(\"<jemalloc>: arena 0 background thread creation \"\n\t\t    \"failed (%d)\\n\", err);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\tinfo->state = background_thread_stopped;\n\t\tn_background_threads--;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool\nbackground_threads_enable(tsd_t *tsd) {\n\tassert(n_background_threads == 0);\n\tassert(background_thread_enabled());\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\n\tVARIABLE_ARRAY(bool, marked, max_background_threads);\n\tunsigned i, nmarked;\n\tfor (i = 0; i < max_background_threads; i++) {\n\t\tmarked[i] = false;\n\t}\n\tnmarked = 0;\n\t/* Thread 0 is required and created at the end. */\n\tmarked[0] = true;\n\t/* Mark the threads we need to create for thread 0. */\n\tunsigned n = narenas_total_get();\n\tfor (i = 1; i < n; i++) {\n\t\tif (marked[i % max_background_threads] ||\n\t\t    arena_get(tsd_tsdn(tsd), i, false) == NULL) {\n\t\t\tcontinue;\n\t\t}\n\t\tbackground_thread_info_t *info = &background_thread_info[\n\t\t    i % max_background_threads];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\tassert(info->state == background_thread_stopped);\n\t\tbackground_thread_init(tsd, info);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\tmarked[i % max_background_threads] = true;\n\t\tif (++nmarked == max_background_threads) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn background_thread_create(tsd, 0);\n}\n\nbool\nbackground_threads_disable(tsd_t *tsd) {\n\tassert(!background_thread_enabled());\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);\n\n\t/* Thread 0 will be responsible for terminating other threads. */\n\tif (background_threads_disable_single(tsd,\n\t    &background_thread_info[0])) {\n\t\treturn true;\n\t}\n\tassert(n_background_threads == 0);\n\n\treturn false;\n}\n\n/* Check if we need to signal the background thread early. */\nvoid\nbackground_thread_interval_check(tsdn_t *tsdn, arena_t *arena,\n    arena_decay_t *decay, size_t npages_new) {\n\tbackground_thread_info_t *info = arena_background_thread_info_get(\n\t    arena);\n\tif (malloc_mutex_trylock(tsdn, &info->mtx)) {\n\t\t/*\n\t\t * Background thread may hold the mutex for a long period of\n\t\t * time.  We'd like to avoid the variance on application\n\t\t * threads.  So keep this non-blocking, and leave the work to a\n\t\t * future epoch.\n\t\t */\n\t\treturn;\n\t}\n\n\tif (info->state != background_thread_started) {\n\t\tgoto label_done;\n\t}\n\tif (malloc_mutex_trylock(tsdn, &decay->mtx)) {\n\t\tgoto label_done;\n\t}\n\n\tssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);\n\tif (decay_time <= 0) {\n\t\t/* Purging is eagerly done or disabled currently. */\n\t\tgoto label_done_unlock2;\n\t}\n\tuint64_t decay_interval_ns = nstime_ns(&decay->interval);\n\tassert(decay_interval_ns > 0);\n\n\tnstime_t diff;\n\tnstime_init(&diff, background_thread_wakeup_time_get(info));\n\tif (nstime_compare(&diff, &decay->epoch) <= 0) {\n\t\tgoto label_done_unlock2;\n\t}\n\tnstime_subtract(&diff, &decay->epoch);\n\tif (nstime_ns(&diff) < BACKGROUND_THREAD_MIN_INTERVAL_NS) {\n\t\tgoto label_done_unlock2;\n\t}\n\n\tif (npages_new > 0) {\n\t\tsize_t n_epoch = (size_t)(nstime_ns(&diff) / decay_interval_ns);\n\t\t/*\n\t\t * Compute how many new pages we would need to purge by the next\n\t\t * wakeup, which is used to determine if we should signal the\n\t\t * background thread.\n\t\t */\n\t\tuint64_t npurge_new;\n\t\tif (n_epoch >= SMOOTHSTEP_NSTEPS) {\n\t\t\tnpurge_new = npages_new;\n\t\t} else {\n\t\t\tuint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1];\n\t\t\tassert(h_steps_max >=\n\t\t\t    h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);\n\t\t\tnpurge_new = npages_new * (h_steps_max -\n\t\t\t    h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);\n\t\t\tnpurge_new >>= SMOOTHSTEP_BFP;\n\t\t}\n\t\tinfo->npages_to_purge_new += npurge_new;\n\t}\n\n\tbool should_signal;\n\tif (info->npages_to_purge_new > BACKGROUND_THREAD_NPAGES_THRESHOLD) {\n\t\tshould_signal = true;\n\t} else if (unlikely(background_thread_indefinite_sleep(info)) &&\n\t    (extents_npages_get(&arena->extents_dirty) > 0 ||\n\t    extents_npages_get(&arena->extents_muzzy) > 0 ||\n\t    info->npages_to_purge_new > 0)) {\n\t\tshould_signal = true;\n\t} else {\n\t\tshould_signal = false;\n\t}\n\n\tif (should_signal) {\n\t\tinfo->npages_to_purge_new = 0;\n\t\tpthread_cond_signal(&info->cond);\n\t}\nlabel_done_unlock2:\n\tmalloc_mutex_unlock(tsdn, &decay->mtx);\nlabel_done:\n\tmalloc_mutex_unlock(tsdn, &info->mtx);\n}\n\nvoid\nbackground_thread_prefork0(tsdn_t *tsdn) {\n\tmalloc_mutex_prefork(tsdn, &background_thread_lock);\n\tbackground_thread_enabled_at_fork = background_thread_enabled();\n}\n\nvoid\nbackground_thread_prefork1(tsdn_t *tsdn) {\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tmalloc_mutex_prefork(tsdn, &background_thread_info[i].mtx);\n\t}\n}\n\nvoid\nbackground_thread_postfork_parent(tsdn_t *tsdn) {\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tmalloc_mutex_postfork_parent(tsdn,\n\t\t    &background_thread_info[i].mtx);\n\t}\n\tmalloc_mutex_postfork_parent(tsdn, &background_thread_lock);\n}\n\nvoid\nbackground_thread_postfork_child(tsdn_t *tsdn) {\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tmalloc_mutex_postfork_child(tsdn,\n\t\t    &background_thread_info[i].mtx);\n\t}\n\tmalloc_mutex_postfork_child(tsdn, &background_thread_lock);\n\tif (!background_thread_enabled_at_fork) {\n\t\treturn;\n\t}\n\n\t/* Clear background_thread state (reset to disabled for child). */\n\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\tn_background_threads = 0;\n\tbackground_thread_enabled_set(tsdn, false);\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\tinfo->state = background_thread_stopped;\n\t\tint ret = pthread_cond_init(&info->cond, NULL);\n\t\tassert(ret == 0);\n\t\tbackground_thread_info_init(tsdn, info);\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t}\n\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n}\n\nbool\nbackground_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {\n\tassert(config_stats);\n\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\tif (!background_thread_enabled()) {\n\t\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\t\treturn true;\n\t}\n\n\tstats->num_threads = n_background_threads;\n\tuint64_t num_runs = 0;\n\tnstime_init(&stats->run_interval, 0);\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\tif (malloc_mutex_trylock(tsdn, &info->mtx)) {\n\t\t\t/*\n\t\t\t * Each background thread run may take a long time;\n\t\t\t * avoid waiting on the stats if the thread is active.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\tif (info->state != background_thread_stopped) {\n\t\t\tnum_runs += info->tot_n_runs;\n\t\t\tnstime_add(&stats->run_interval, &info->tot_sleep_time);\n\t\t}\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t}\n\tstats->num_runs = num_runs;\n\tif (num_runs > 0) {\n\t\tnstime_idivide(&stats->run_interval, num_runs);\n\t}\n\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\n\treturn false;\n}\n\n#undef BACKGROUND_THREAD_NPAGES_THRESHOLD\n#undef BILLION\n#undef BACKGROUND_THREAD_MIN_INTERVAL_NS\n\nstatic bool\npthread_create_fptr_init(void) {\n\tif (pthread_create_fptr != NULL) {\n\t\treturn false;\n\t}\n\tpthread_create_fptr = dlsym(RTLD_NEXT, \"pthread_create\");\n\tif (pthread_create_fptr == NULL) {\n\t\tcan_enable_background_thread = false;\n\t\tif (config_lazy_lock || opt_background_thread) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in dlsym(RTLD_NEXT, \"\n\t\t\t    \"\\\"pthread_create\\\")\\n\");\n\t\t\tabort();\n\t\t}\n\t} else {\n\t\tcan_enable_background_thread = true;\n\t}\n\n\treturn false;\n}\n\n/*\n * When lazy lock is enabled, we need to make sure setting isthreaded before\n * taking any background_thread locks.  This is called early in ctl (instead of\n * wait for the pthread_create calls to trigger) because the mutex is required\n * before creating background threads.\n */\nvoid\nbackground_thread_ctl_init(tsdn_t *tsdn) {\n\tmalloc_mutex_assert_not_owner(tsdn, &background_thread_lock);\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\n\tpthread_create_fptr_init();\n\tpthread_create_wrapper_init();\n#endif\n}\n\n#endif /* defined(JEMALLOC_BACKGROUND_THREAD) */\n\nbool\nbackground_thread_boot0(void) {\n\tif (!have_background_thread && opt_background_thread) {\n\t\tmalloc_printf(\"<jemalloc>: option background_thread currently \"\n\t\t    \"supports pthread only\\n\");\n\t\treturn true;\n\t}\n#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER\n\tif ((config_lazy_lock || opt_background_thread) &&\n\t    pthread_create_fptr_init()) {\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\nbool\nbackground_thread_boot1(tsdn_t *tsdn) {\n#ifdef JEMALLOC_BACKGROUND_THREAD\n\tassert(have_background_thread);\n\tassert(narenas_total_get() > 0);\n\n\tif (opt_max_background_threads == MAX_BACKGROUND_THREAD_LIMIT &&\n\t    ncpus < MAX_BACKGROUND_THREAD_LIMIT) {\n\t\topt_max_background_threads = ncpus;\n\t}\n\tmax_background_threads = opt_max_background_threads;\n\n\tbackground_thread_enabled_set(tsdn, opt_background_thread);\n\tif (malloc_mutex_init(&background_thread_lock,\n\t    \"background_thread_global\",\n\t    WITNESS_RANK_BACKGROUND_THREAD_GLOBAL,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\tbackground_thread_info = (background_thread_info_t *)base_alloc(tsdn,\n\t    b0get(), opt_max_background_threads *\n\t    sizeof(background_thread_info_t), CACHELINE);\n\tif (background_thread_info == NULL) {\n\t\treturn true;\n\t}\n\n\tfor (unsigned i = 0; i < max_background_threads; i++) {\n\t\tbackground_thread_info_t *info = &background_thread_info[i];\n\t\t/* Thread mutex is rank_inclusive because of thread0. */\n\t\tif (malloc_mutex_init(&info->mtx, \"background_thread\",\n\t\t    WITNESS_RANK_BACKGROUND_THREAD,\n\t\t    malloc_mutex_address_ordered)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (pthread_cond_init(&info->cond, NULL)) {\n\t\t\treturn true;\n\t\t}\n\t\tmalloc_mutex_lock(tsdn, &info->mtx);\n\t\tinfo->state = background_thread_stopped;\n\t\tbackground_thread_info_init(tsdn, info);\n\t\tmalloc_mutex_unlock(tsdn, &info->mtx);\n\t}\n#endif\n\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/base.c",
    "content": "#define JEMALLOC_BASE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/sz.h\"\n\n/******************************************************************************/\n/* Data. */\n\nstatic base_t *b0;\n\nmetadata_thp_mode_t opt_metadata_thp = METADATA_THP_DEFAULT;\n\nconst char *metadata_thp_mode_names[] = {\n\t\"disabled\",\n\t\"auto\",\n\t\"always\"\n};\n\n/******************************************************************************/\n\nstatic inline bool\nmetadata_thp_madvise(void) {\n\treturn (metadata_thp_enabled() &&\n\t    (init_system_thp_mode == thp_mode_default));\n}\n\nstatic void *\nbase_map(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, size_t size) {\n\tvoid *addr;\n\tbool zero = true;\n\tbool commit = true;\n\n\t/* Use huge page sizes and alignment regardless of opt_metadata_thp. */\n\tassert(size == HUGEPAGE_CEILING(size));\n\tsize_t alignment = HUGEPAGE;\n\tif (extent_hooks == &extent_hooks_default) {\n\t\taddr = extent_alloc_mmap(NULL, size, alignment, &zero, &commit);\n\t} else {\n\t\t/* No arena context as we are creating new arenas. */\n\t\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\t\tpre_reentrancy(tsd, NULL);\n\t\taddr = extent_hooks->alloc(extent_hooks, NULL, size, alignment,\n\t\t    &zero, &commit, ind);\n\t\tpost_reentrancy(tsd);\n\t}\n\n\treturn addr;\n}\n\nstatic void\nbase_unmap(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, void *addr,\n    size_t size) {\n\t/*\n\t * Cascade through dalloc, decommit, purge_forced, and purge_lazy,\n\t * stopping at first success.  This cascade is performed for consistency\n\t * with the cascade in extent_dalloc_wrapper() because an application's\n\t * custom hooks may not support e.g. dalloc.  This function is only ever\n\t * called as a side effect of arena destruction, so although it might\n\t * seem pointless to do anything besides dalloc here, the application\n\t * may in fact want the end state of all associated virtual memory to be\n\t * in some consistent-but-allocated state.\n\t */\n\tif (extent_hooks == &extent_hooks_default) {\n\t\tif (!extent_dalloc_mmap(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\tif (!pages_decommit(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\tif (!pages_purge_forced(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\tif (!pages_purge_lazy(addr, size)) {\n\t\t\tgoto label_done;\n\t\t}\n\t\t/* Nothing worked.  This should never happen. */\n\t\tnot_reached();\n\t} else {\n\t\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\t\tpre_reentrancy(tsd, NULL);\n\t\tif (extent_hooks->dalloc != NULL &&\n\t\t    !extent_hooks->dalloc(extent_hooks, addr, size, true,\n\t\t    ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\tif (extent_hooks->decommit != NULL &&\n\t\t    !extent_hooks->decommit(extent_hooks, addr, size, 0, size,\n\t\t    ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\tif (extent_hooks->purge_forced != NULL &&\n\t\t    !extent_hooks->purge_forced(extent_hooks, addr, size, 0,\n\t\t    size, ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\tif (extent_hooks->purge_lazy != NULL &&\n\t\t    !extent_hooks->purge_lazy(extent_hooks, addr, size, 0, size,\n\t\t    ind)) {\n\t\t\tgoto label_post_reentrancy;\n\t\t}\n\t\t/* Nothing worked.  That's the application's problem. */\n\tlabel_post_reentrancy:\n\t\tpost_reentrancy(tsd);\n\t}\nlabel_done:\n\tif (metadata_thp_madvise()) {\n\t\t/* Set NOHUGEPAGE after unmap to avoid kernel defrag. */\n\t\tassert(((uintptr_t)addr & HUGEPAGE_MASK) == 0 &&\n\t\t    (size & HUGEPAGE_MASK) == 0);\n\t\tpages_nohuge(addr, size);\n\t}\n}\n\nstatic void\nbase_extent_init(size_t *extent_sn_next, extent_t *extent, void *addr,\n    size_t size) {\n\tsize_t sn;\n\n\tsn = *extent_sn_next;\n\t(*extent_sn_next)++;\n\n\textent_binit(extent, addr, size, sn);\n}\n\nstatic size_t\nbase_get_num_blocks(base_t *base, bool with_new_block) {\n\tbase_block_t *b = base->blocks;\n\tassert(b != NULL);\n\n\tsize_t n_blocks = with_new_block ? 2 : 1;\n\twhile (b->next != NULL) {\n\t\tn_blocks++;\n\t\tb = b->next;\n\t}\n\n\treturn n_blocks;\n}\n\nstatic void\nbase_auto_thp_switch(tsdn_t *tsdn, base_t *base) {\n\tassert(opt_metadata_thp == metadata_thp_auto);\n\tmalloc_mutex_assert_owner(tsdn, &base->mtx);\n\tif (base->auto_thp_switched) {\n\t\treturn;\n\t}\n\t/* Called when adding a new block. */\n\tbool should_switch;\n\tif (base_ind_get(base) != 0) {\n\t\tshould_switch = (base_get_num_blocks(base, true) ==\n\t\t    BASE_AUTO_THP_THRESHOLD);\n\t} else {\n\t\tshould_switch = (base_get_num_blocks(base, true) ==\n\t\t    BASE_AUTO_THP_THRESHOLD_A0);\n\t}\n\tif (!should_switch) {\n\t\treturn;\n\t}\n\n\tbase->auto_thp_switched = true;\n\tassert(!config_stats || base->n_thp == 0);\n\t/* Make the initial blocks THP lazily. */\n\tbase_block_t *block = base->blocks;\n\twhile (block != NULL) {\n\t\tassert((block->size & HUGEPAGE_MASK) == 0);\n\t\tpages_huge(block, block->size);\n\t\tif (config_stats) {\n\t\t\tbase->n_thp += HUGEPAGE_CEILING(block->size -\n\t\t\t    extent_bsize_get(&block->extent)) >> LG_HUGEPAGE;\n\t\t}\n\t\tblock = block->next;\n\t\tassert(block == NULL || (base_ind_get(base) == 0));\n\t}\n}\n\nstatic void *\nbase_extent_bump_alloc_helper(extent_t *extent, size_t *gap_size, size_t size,\n    size_t alignment) {\n\tvoid *ret;\n\n\tassert(alignment == ALIGNMENT_CEILING(alignment, QUANTUM));\n\tassert(size == ALIGNMENT_CEILING(size, alignment));\n\n\t*gap_size = ALIGNMENT_CEILING((uintptr_t)extent_addr_get(extent),\n\t    alignment) - (uintptr_t)extent_addr_get(extent);\n\tret = (void *)((uintptr_t)extent_addr_get(extent) + *gap_size);\n\tassert(extent_bsize_get(extent) >= *gap_size + size);\n\textent_binit(extent, (void *)((uintptr_t)extent_addr_get(extent) +\n\t    *gap_size + size), extent_bsize_get(extent) - *gap_size - size,\n\t    extent_sn_get(extent));\n\treturn ret;\n}\n\nstatic void\nbase_extent_bump_alloc_post(base_t *base, extent_t *extent, size_t gap_size,\n    void *addr, size_t size) {\n\tif (extent_bsize_get(extent) > 0) {\n\t\t/*\n\t\t * Compute the index for the largest size class that does not\n\t\t * exceed extent's size.\n\t\t */\n\t\tszind_t index_floor =\n\t\t    sz_size2index(extent_bsize_get(extent) + 1) - 1;\n\t\textent_heap_insert(&base->avail[index_floor], extent);\n\t}\n\n\tif (config_stats) {\n\t\tbase->allocated += size;\n\t\t/*\n\t\t * Add one PAGE to base_resident for every page boundary that is\n\t\t * crossed by the new allocation. Adjust n_thp similarly when\n\t\t * metadata_thp is enabled.\n\t\t */\n\t\tbase->resident += PAGE_CEILING((uintptr_t)addr + size) -\n\t\t    PAGE_CEILING((uintptr_t)addr - gap_size);\n\t\tassert(base->allocated <= base->resident);\n\t\tassert(base->resident <= base->mapped);\n\t\tif (metadata_thp_madvise() && (opt_metadata_thp ==\n\t\t    metadata_thp_always || base->auto_thp_switched)) {\n\t\t\tbase->n_thp += (HUGEPAGE_CEILING((uintptr_t)addr + size)\n\t\t\t    - HUGEPAGE_CEILING((uintptr_t)addr - gap_size)) >>\n\t\t\t    LG_HUGEPAGE;\n\t\t\tassert(base->mapped >= base->n_thp << LG_HUGEPAGE);\n\t\t}\n\t}\n}\n\nstatic void *\nbase_extent_bump_alloc(base_t *base, extent_t *extent, size_t size,\n    size_t alignment) {\n\tvoid *ret;\n\tsize_t gap_size;\n\n\tret = base_extent_bump_alloc_helper(extent, &gap_size, size, alignment);\n\tbase_extent_bump_alloc_post(base, extent, gap_size, ret, size);\n\treturn ret;\n}\n\n/*\n * Allocate a block of virtual memory that is large enough to start with a\n * base_block_t header, followed by an object of specified size and alignment.\n * On success a pointer to the initialized base_block_t header is returned.\n */\nstatic base_block_t *\nbase_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,\n    unsigned ind, pszind_t *pind_last, size_t *extent_sn_next, size_t size,\n    size_t alignment) {\n\talignment = ALIGNMENT_CEILING(alignment, QUANTUM);\n\tsize_t usize = ALIGNMENT_CEILING(size, alignment);\n\tsize_t header_size = sizeof(base_block_t);\n\tsize_t gap_size = ALIGNMENT_CEILING(header_size, alignment) -\n\t    header_size;\n\t/*\n\t * Create increasingly larger blocks in order to limit the total number\n\t * of disjoint virtual memory ranges.  Choose the next size in the page\n\t * size class series (skipping size classes that are not a multiple of\n\t * HUGEPAGE), or a size large enough to satisfy the requested size and\n\t * alignment, whichever is larger.\n\t */\n\tsize_t min_block_size = HUGEPAGE_CEILING(sz_psz2u(header_size + gap_size\n\t    + usize));\n\tpszind_t pind_next = (*pind_last + 1 < NPSIZES) ? *pind_last + 1 :\n\t    *pind_last;\n\tsize_t next_block_size = HUGEPAGE_CEILING(sz_pind2sz(pind_next));\n\tsize_t block_size = (min_block_size > next_block_size) ? min_block_size\n\t    : next_block_size;\n\tbase_block_t *block = (base_block_t *)base_map(tsdn, extent_hooks, ind,\n\t    block_size);\n\tif (block == NULL) {\n\t\treturn NULL;\n\t}\n\n\tif (metadata_thp_madvise()) {\n\t\tvoid *addr = (void *)block;\n\t\tassert(((uintptr_t)addr & HUGEPAGE_MASK) == 0 &&\n\t\t    (block_size & HUGEPAGE_MASK) == 0);\n\t\tif (opt_metadata_thp == metadata_thp_always) {\n\t\t\tpages_huge(addr, block_size);\n\t\t} else if (opt_metadata_thp == metadata_thp_auto &&\n\t\t    base != NULL) {\n\t\t\t/* base != NULL indicates this is not a new base. */\n\t\t\tmalloc_mutex_lock(tsdn, &base->mtx);\n\t\t\tbase_auto_thp_switch(tsdn, base);\n\t\t\tif (base->auto_thp_switched) {\n\t\t\t\tpages_huge(addr, block_size);\n\t\t\t}\n\t\t\tmalloc_mutex_unlock(tsdn, &base->mtx);\n\t\t}\n\t}\n\n\t*pind_last = sz_psz2ind(block_size);\n\tblock->size = block_size;\n\tblock->next = NULL;\n\tassert(block_size >= header_size);\n\tbase_extent_init(extent_sn_next, &block->extent,\n\t    (void *)((uintptr_t)block + header_size), block_size - header_size);\n\treturn block;\n}\n\n/*\n * Allocate an extent that is at least as large as specified size, with\n * specified alignment.\n */\nstatic extent_t *\nbase_extent_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {\n\tmalloc_mutex_assert_owner(tsdn, &base->mtx);\n\n\textent_hooks_t *extent_hooks = base_extent_hooks_get(base);\n\t/*\n\t * Drop mutex during base_block_alloc(), because an extent hook will be\n\t * called.\n\t */\n\tmalloc_mutex_unlock(tsdn, &base->mtx);\n\tbase_block_t *block = base_block_alloc(tsdn, base, extent_hooks,\n\t    base_ind_get(base), &base->pind_last, &base->extent_sn_next, size,\n\t    alignment);\n\tmalloc_mutex_lock(tsdn, &base->mtx);\n\tif (block == NULL) {\n\t\treturn NULL;\n\t}\n\tblock->next = base->blocks;\n\tbase->blocks = block;\n\tif (config_stats) {\n\t\tbase->allocated += sizeof(base_block_t);\n\t\tbase->resident += PAGE_CEILING(sizeof(base_block_t));\n\t\tbase->mapped += block->size;\n\t\tif (metadata_thp_madvise() &&\n\t\t    !(opt_metadata_thp == metadata_thp_auto\n\t\t      && !base->auto_thp_switched)) {\n\t\t\tassert(base->n_thp > 0);\n\t\t\tbase->n_thp += HUGEPAGE_CEILING(sizeof(base_block_t)) >>\n\t\t\t    LG_HUGEPAGE;\n\t\t}\n\t\tassert(base->allocated <= base->resident);\n\t\tassert(base->resident <= base->mapped);\n\t\tassert(base->n_thp << LG_HUGEPAGE <= base->mapped);\n\t}\n\treturn &block->extent;\n}\n\nbase_t *\nb0get(void) {\n\treturn b0;\n}\n\nbase_t *\nbase_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tpszind_t pind_last = 0;\n\tsize_t extent_sn_next = 0;\n\tbase_block_t *block = base_block_alloc(tsdn, NULL, extent_hooks, ind,\n\t    &pind_last, &extent_sn_next, sizeof(base_t), QUANTUM);\n\tif (block == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsize_t gap_size;\n\tsize_t base_alignment = CACHELINE;\n\tsize_t base_size = ALIGNMENT_CEILING(sizeof(base_t), base_alignment);\n\tbase_t *base = (base_t *)base_extent_bump_alloc_helper(&block->extent,\n\t    &gap_size, base_size, base_alignment);\n\tbase->ind = ind;\n\tatomic_store_p(&base->extent_hooks, extent_hooks, ATOMIC_RELAXED);\n\tif (malloc_mutex_init(&base->mtx, \"base\", WITNESS_RANK_BASE,\n\t    malloc_mutex_rank_exclusive)) {\n\t\tbase_unmap(tsdn, extent_hooks, ind, block, block->size);\n\t\treturn NULL;\n\t}\n\tbase->pind_last = pind_last;\n\tbase->extent_sn_next = extent_sn_next;\n\tbase->blocks = block;\n\tbase->auto_thp_switched = false;\n\tfor (szind_t i = 0; i < NSIZES; i++) {\n\t\textent_heap_new(&base->avail[i]);\n\t}\n\tif (config_stats) {\n\t\tbase->allocated = sizeof(base_block_t);\n\t\tbase->resident = PAGE_CEILING(sizeof(base_block_t));\n\t\tbase->mapped = block->size;\n\t\tbase->n_thp = (opt_metadata_thp == metadata_thp_always) &&\n\t\t    metadata_thp_madvise() ? HUGEPAGE_CEILING(sizeof(base_block_t))\n\t\t    >> LG_HUGEPAGE : 0;\n\t\tassert(base->allocated <= base->resident);\n\t\tassert(base->resident <= base->mapped);\n\t\tassert(base->n_thp << LG_HUGEPAGE <= base->mapped);\n\t}\n\tbase_extent_bump_alloc_post(base, &block->extent, gap_size, base,\n\t    base_size);\n\n\treturn base;\n}\n\nvoid\nbase_delete(tsdn_t *tsdn, base_t *base) {\n\textent_hooks_t *extent_hooks = base_extent_hooks_get(base);\n\tbase_block_t *next = base->blocks;\n\tdo {\n\t\tbase_block_t *block = next;\n\t\tnext = block->next;\n\t\tbase_unmap(tsdn, extent_hooks, base_ind_get(base), block,\n\t\t    block->size);\n\t} while (next != NULL);\n}\n\nextent_hooks_t *\nbase_extent_hooks_get(base_t *base) {\n\treturn (extent_hooks_t *)atomic_load_p(&base->extent_hooks,\n\t    ATOMIC_ACQUIRE);\n}\n\nextent_hooks_t *\nbase_extent_hooks_set(base_t *base, extent_hooks_t *extent_hooks) {\n\textent_hooks_t *old_extent_hooks = base_extent_hooks_get(base);\n\tatomic_store_p(&base->extent_hooks, extent_hooks, ATOMIC_RELEASE);\n\treturn old_extent_hooks;\n}\n\nstatic void *\nbase_alloc_impl(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment,\n    size_t *esn) {\n\talignment = QUANTUM_CEILING(alignment);\n\tsize_t usize = ALIGNMENT_CEILING(size, alignment);\n\tsize_t asize = usize + alignment - QUANTUM;\n\n\textent_t *extent = NULL;\n\tmalloc_mutex_lock(tsdn, &base->mtx);\n\tfor (szind_t i = sz_size2index(asize); i < NSIZES; i++) {\n\t\textent = extent_heap_remove_first(&base->avail[i]);\n\t\tif (extent != NULL) {\n\t\t\t/* Use existing space. */\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (extent == NULL) {\n\t\t/* Try to allocate more space. */\n\t\textent = base_extent_alloc(tsdn, base, usize, alignment);\n\t}\n\tvoid *ret;\n\tif (extent == NULL) {\n\t\tret = NULL;\n\t\tgoto label_return;\n\t}\n\n\tret = base_extent_bump_alloc(base, extent, usize, alignment);\n\tif (esn != NULL) {\n\t\t*esn = extent_sn_get(extent);\n\t}\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &base->mtx);\n\treturn ret;\n}\n\n/*\n * base_alloc() returns zeroed memory, which is always demand-zeroed for the\n * auto arenas, in order to make multi-page sparse data structures such as radix\n * tree nodes efficient with respect to physical memory usage.  Upon success a\n * pointer to at least size bytes with specified alignment is returned.  Note\n * that size is rounded up to the nearest multiple of alignment to avoid false\n * sharing.\n */\nvoid *\nbase_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {\n\treturn base_alloc_impl(tsdn, base, size, alignment, NULL);\n}\n\nextent_t *\nbase_alloc_extent(tsdn_t *tsdn, base_t *base) {\n\tsize_t esn;\n\textent_t *extent = base_alloc_impl(tsdn, base, sizeof(extent_t),\n\t    CACHELINE, &esn);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\textent_esn_set(extent, esn);\n\treturn extent;\n}\n\nvoid\nbase_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated, size_t *resident,\n    size_t *mapped, size_t *n_thp) {\n\tcassert(config_stats);\n\n\tmalloc_mutex_lock(tsdn, &base->mtx);\n\tassert(base->allocated <= base->resident);\n\tassert(base->resident <= base->mapped);\n\t*allocated = base->allocated;\n\t*resident = base->resident;\n\t*mapped = base->mapped;\n\t*n_thp = base->n_thp;\n\tmalloc_mutex_unlock(tsdn, &base->mtx);\n}\n\nvoid\nbase_prefork(tsdn_t *tsdn, base_t *base) {\n\tmalloc_mutex_prefork(tsdn, &base->mtx);\n}\n\nvoid\nbase_postfork_parent(tsdn_t *tsdn, base_t *base) {\n\tmalloc_mutex_postfork_parent(tsdn, &base->mtx);\n}\n\nvoid\nbase_postfork_child(tsdn_t *tsdn, base_t *base) {\n\tmalloc_mutex_postfork_child(tsdn, &base->mtx);\n}\n\nbool\nbase_boot(tsdn_t *tsdn) {\n\tb0 = base_new(tsdn, 0, (extent_hooks_t *)&extent_hooks_default);\n\treturn (b0 == NULL);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/bin.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/bin.h\"\n#include \"jemalloc/internal/witness.h\"\n\nconst bin_info_t bin_infos[NBINS] = {\n#define BIN_INFO_bin_yes(reg_size, slab_size, nregs)\t\t\t\\\n\t{reg_size, slab_size, nregs, BITMAP_INFO_INITIALIZER(nregs)},\n#define BIN_INFO_bin_no(reg_size, slab_size, nregs)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs,\t\t\\\n    lg_delta_lookup)\t\t\t\t\t\t\t\\\n\tBIN_INFO_bin_##bin((1U<<lg_grp) + (ndelta<<lg_delta),\t\t\\\n\t    (pgs << LG_PAGE), (pgs << LG_PAGE) / ((1U<<lg_grp) +\t\\\n\t    (ndelta<<lg_delta)))\n\tSIZE_CLASSES\n#undef BIN_INFO_bin_yes\n#undef BIN_INFO_bin_no\n#undef SC\n};\n\nbool\nbin_init(bin_t *bin) {\n\tif (malloc_mutex_init(&bin->lock, \"bin\", WITNESS_RANK_BIN,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tbin->slabcur = NULL;\n\textent_heap_new(&bin->slabs_nonfull);\n\textent_list_init(&bin->slabs_full);\n\tif (config_stats) {\n\t\tmemset(&bin->stats, 0, sizeof(bin_stats_t));\n\t}\n\treturn false;\n}\n\nvoid\nbin_prefork(tsdn_t *tsdn, bin_t *bin) {\n\tmalloc_mutex_prefork(tsdn, &bin->lock);\n}\n\nvoid\nbin_postfork_parent(tsdn_t *tsdn, bin_t *bin) {\n\tmalloc_mutex_postfork_parent(tsdn, &bin->lock);\n}\n\nvoid\nbin_postfork_child(tsdn_t *tsdn, bin_t *bin) {\n\tmalloc_mutex_postfork_child(tsdn, &bin->lock);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/bitmap.c",
    "content": "#define JEMALLOC_BITMAP_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n/******************************************************************************/\n\n#ifdef BITMAP_USE_TREE\n\nvoid\nbitmap_info_init(bitmap_info_t *binfo, size_t nbits) {\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 = BITMAP_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 = BITMAP_BITS2GROUPS(group_count);\n\t}\n\tbinfo->levels[i].group_offset = binfo->levels[i-1].group_offset\n\t    + group_count;\n\tassert(binfo->levels[i].group_offset <= BITMAP_GROUPS_MAX);\n\tbinfo->nlevels = i;\n\tbinfo->nbits = nbits;\n}\n\nstatic size_t\nbitmap_info_ngroups(const bitmap_info_t *binfo) {\n\treturn binfo->levels[binfo->nlevels].group_offset;\n}\n\nvoid\nbitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill) {\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.\n\t */\n\n\tif (fill) {\n\t\t/* The \"filled\" bitmap starts out with all 0 bits. */\n\t\tmemset(bitmap, 0, bitmap_size(binfo));\n\t\treturn;\n\t}\n\n\t/*\n\t * The \"empty\" bitmap starts out with all 1 bits, except for trailing\n\t * unused bits (if any).  Note that each group uses bit 0 to correspond\n\t * to the first logical bit in the group, so extra bits are the most\n\t * significant bits of the last group.\n\t */\n\tmemset(bitmap, 0xffU, bitmap_size(binfo));\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\t}\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\t}\n\t}\n}\n\n#else /* BITMAP_USE_TREE */\n\nvoid\nbitmap_info_init(bitmap_info_t *binfo, size_t nbits) {\n\tassert(nbits > 0);\n\tassert(nbits <= (ZU(1) << LG_BITMAP_MAXBITS));\n\n\tbinfo->ngroups = BITMAP_BITS2GROUPS(nbits);\n\tbinfo->nbits = nbits;\n}\n\nstatic size_t\nbitmap_info_ngroups(const bitmap_info_t *binfo) {\n\treturn binfo->ngroups;\n}\n\nvoid\nbitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill) {\n\tsize_t extra;\n\n\tif (fill) {\n\t\tmemset(bitmap, 0, bitmap_size(binfo));\n\t\treturn;\n\t}\n\n\tmemset(bitmap, 0xffU, bitmap_size(binfo));\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->ngroups - 1] >>= extra;\n\t}\n}\n\n#endif /* BITMAP_USE_TREE */\n\nsize_t\nbitmap_size(const bitmap_info_t *binfo) {\n\treturn (bitmap_info_ngroups(binfo) << LG_SIZEOF_BITMAP);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 JEMALLOC_CKH_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n#include \"jemalloc/internal/ckh.h\"\n\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/prng.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic bool\tckh_grow(tsd_t *tsd, ckh_t *ckh);\nstatic void\tckh_shrink(tsd_t *tsd, 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 */\nstatic size_t\nckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key) {\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\t}\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 */\nstatic size_t\nckh_isearch(ckh_t *ckh, const void *key) {\n\tsize_t hashes[2], bucket, cell;\n\n\tassert(ckh != NULL);\n\n\tckh->hash(key, hashes);\n\n\t/* Search primary bucket. */\n\tbucket = hashes[0] & ((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\t}\n\n\t/* Search secondary bucket. */\n\tbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tcell = ckh_bucket_search(ckh, bucket, key);\n\treturn cell;\n}\n\nstatic bool\nckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key,\n    const void *data) {\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\toffset = (unsigned)prng_lg_range_u64(&ckh->prng_state,\n\t    LG_CKH_BUCKET_CELLS);\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 */\nstatic bool\nckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,\n    void const **argdata) {\n\tconst void *key, *data, *tkey, *tdata;\n\tckhc_t *cell;\n\tsize_t hashes[2], 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\ti = (unsigned)prng_lg_range_u64(&ckh->prng_state,\n\t\t    LG_CKH_BUCKET_CELLS);\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, hashes);\n\t\ttbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\t\tif (tbucket == bucket) {\n\t\t\ttbucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets)\n\t\t\t    - 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)) {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nstatic bool\nckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) {\n\tsize_t hashes[2], bucket;\n\tconst void *key = *argkey;\n\tconst void *data = *argdata;\n\n\tckh->hash(key, hashes);\n\n\t/* Try to insert in primary bucket. */\n\tbucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tif (!ckh_try_bucket_insert(ckh, bucket, key, data)) {\n\t\treturn false;\n\t}\n\n\t/* Try to insert in secondary bucket. */\n\tbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);\n\tif (!ckh_try_bucket_insert(ckh, bucket, key, data)) {\n\t\treturn false;\n\t}\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 */\nstatic bool\nckh_rebuild(ckh_t *ckh, ckhc_t *aTab) {\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(tsd_t *tsd, ckh_t *ckh) {\n\tbool ret;\n\tckhc_t *tab, *ttab;\n\tunsigned lg_prevbuckets, lg_curcells;\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 = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\ttab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE,\n\t\t    true, NULL, true, arena_ichoose(tsd, NULL));\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)) {\n\t\t\tidalloctm(tsd_tsdn(tsd), tab, NULL, NULL, true, true);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Rebuilding failed, so back out partially rebuilt table. */\n\t\tidalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true);\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(tsd_t *tsd, ckh_t *ckh) {\n\tckhc_t *tab, *ttab;\n\tsize_t usize;\n\tunsigned lg_prevbuckets, lg_curcells;\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 = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\treturn;\n\t}\n\ttab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true, NULL,\n\t    true, arena_ichoose(tsd, NULL));\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)) {\n\t\tidalloctm(tsd_tsdn(tsd), tab, NULL, NULL, true, true);\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\tidalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true);\n\tckh->tab = tab;\n\tckh->lg_curbuckets = lg_prevbuckets;\n#ifdef CKH_COUNT\n\tckh->nshrinkfails++;\n#endif\n}\n\nbool\nckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,\n    ckh_keycomp_t *keycomp) {\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 minitems\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 mincells items 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\t}\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 = sz_sa2u(sizeof(ckhc_t) << lg_mincells, CACHELINE);\n\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\tckh->tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true,\n\t    NULL, true, arena_ichoose(tsd, NULL));\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(tsd_t *tsd, ckh_t *ckh) {\n\tassert(ckh != NULL);\n\n#ifdef CKH_VERBOSE\n\tmalloc_printf(\n\t    \"%s(%p): ngrows: %\"FMTu64\", nshrinks: %\"FMTu64\",\"\n\t    \" nshrinkfails: %\"FMTu64\", ninserts: %\"FMTu64\",\"\n\t    \" nrelocs: %\"FMTu64\"\\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\tidalloctm(tsd_tsdn(tsd), ckh->tab, NULL, NULL, true, true);\n\tif (config_debug) {\n\t\tmemset(ckh, JEMALLOC_FREE_JUNK, sizeof(ckh_t));\n\t}\n}\n\nsize_t\nckh_count(ckh_t *ckh) {\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\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\t}\n\t\t\tif (data != NULL) {\n\t\t\t\t*data = (void *)ckh->tab[i].data;\n\t\t\t}\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(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data) {\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(tsd, 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(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key,\n    void **data) {\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\t}\n\t\tif (data != NULL) {\n\t\t\t*data = (void *)ckh->tab[cell].data;\n\t\t}\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(tsd, 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\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\t}\n\t\tif (data != NULL) {\n\t\t\t*data = (void *)ckh->tab[cell].data;\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid\nckh_string_hash(const void *key, size_t r_hash[2]) {\n\thash(key, strlen((const char *)key), 0x94122f33U, r_hash);\n}\n\nbool\nckh_string_keycomp(const void *k1, const void *k2) {\n\tassert(k1 != NULL);\n\tassert(k2 != NULL);\n\n\treturn !strcmp((char *)k1, (char *)k2);\n}\n\nvoid\nckh_pointer_hash(const void *key, size_t r_hash[2]) {\n\tunion {\n\t\tconst void\t*v;\n\t\tsize_t\t\ti;\n\t} u;\n\n\tassert(sizeof(u.v) == sizeof(u.i));\n\tu.v = key;\n\thash(&u.i, sizeof(u.i), 0xd983396eU, r_hash);\n}\n\nbool\nckh_pointer_keycomp(const void *k1, const void *k2) {\n\treturn (k1 == k2);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/ctl.c",
    "content": "#define JEMALLOC_CTL_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/nstime.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/*\n * ctl_mtx protects the following:\n * - ctl_stats->*\n */\nstatic malloc_mutex_t\tctl_mtx;\nstatic bool\t\tctl_initialized;\nstatic ctl_stats_t\t*ctl_stats;\nstatic ctl_arenas_t\t*ctl_arenas;\n\n/******************************************************************************/\n/* Helpers for named and indexed nodes. */\n\nstatic const ctl_named_node_t *\nctl_named_node(const ctl_node_t *node) {\n\treturn ((node->named) ? (const ctl_named_node_t *)node : NULL);\n}\n\nstatic const ctl_named_node_t *\nctl_named_children(const ctl_named_node_t *node, size_t index) {\n\tconst ctl_named_node_t *children = ctl_named_node(node->children);\n\n\treturn (children ? &children[index] : NULL);\n}\n\nstatic const ctl_indexed_node_t *\nctl_indexed_node(const ctl_node_t *node) {\n\treturn (!node->named ? (const ctl_indexed_node_t *)node : NULL);\n}\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\n#define CTL_PROTO(n)\t\t\t\t\t\t\t\\\nstatic int\tn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\t\\\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen);\n\n#define INDEX_PROTO(n)\t\t\t\t\t\t\t\\\nstatic const ctl_named_node_t\t*n##_index(tsdn_t *tsdn,\t\t\\\n    const size_t *mib, size_t miblen, size_t i);\n\nCTL_PROTO(version)\nCTL_PROTO(epoch)\nCTL_PROTO(background_thread)\nCTL_PROTO(max_background_threads)\nCTL_PROTO(thread_tcache_enabled)\nCTL_PROTO(thread_tcache_flush)\nCTL_PROTO(thread_prof_name)\nCTL_PROTO(thread_prof_active)\nCTL_PROTO(thread_arena)\nCTL_PROTO(thread_allocated)\nCTL_PROTO(thread_allocatedp)\nCTL_PROTO(thread_deallocated)\nCTL_PROTO(thread_deallocatedp)\nCTL_PROTO(config_cache_oblivious)\nCTL_PROTO(config_debug)\nCTL_PROTO(config_fill)\nCTL_PROTO(config_lazy_lock)\nCTL_PROTO(config_malloc_conf)\nCTL_PROTO(config_prof)\nCTL_PROTO(config_prof_libgcc)\nCTL_PROTO(config_prof_libunwind)\nCTL_PROTO(config_stats)\nCTL_PROTO(config_utrace)\nCTL_PROTO(config_xmalloc)\nCTL_PROTO(opt_abort)\nCTL_PROTO(opt_abort_conf)\nCTL_PROTO(opt_metadata_thp)\nCTL_PROTO(opt_retain)\nCTL_PROTO(opt_dss)\nCTL_PROTO(opt_narenas)\nCTL_PROTO(opt_percpu_arena)\nCTL_PROTO(opt_background_thread)\nCTL_PROTO(opt_max_background_threads)\nCTL_PROTO(opt_dirty_decay_ms)\nCTL_PROTO(opt_muzzy_decay_ms)\nCTL_PROTO(opt_stats_print)\nCTL_PROTO(opt_stats_print_opts)\nCTL_PROTO(opt_junk)\nCTL_PROTO(opt_zero)\nCTL_PROTO(opt_utrace)\nCTL_PROTO(opt_xmalloc)\nCTL_PROTO(opt_tcache)\nCTL_PROTO(opt_thp)\nCTL_PROTO(opt_lg_extent_max_active_fit)\nCTL_PROTO(opt_lg_tcache_max)\nCTL_PROTO(opt_prof)\nCTL_PROTO(opt_prof_prefix)\nCTL_PROTO(opt_prof_active)\nCTL_PROTO(opt_prof_thread_active_init)\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(tcache_create)\nCTL_PROTO(tcache_flush)\nCTL_PROTO(tcache_destroy)\nCTL_PROTO(arena_i_initialized)\nCTL_PROTO(arena_i_decay)\nCTL_PROTO(arena_i_purge)\nCTL_PROTO(arena_i_reset)\nCTL_PROTO(arena_i_destroy)\nCTL_PROTO(arena_i_dss)\nCTL_PROTO(arena_i_dirty_decay_ms)\nCTL_PROTO(arena_i_muzzy_decay_ms)\nCTL_PROTO(arena_i_extent_hooks)\nCTL_PROTO(arena_i_retain_grow_limit)\nINDEX_PROTO(arena_i)\nCTL_PROTO(arenas_bin_i_size)\nCTL_PROTO(arenas_bin_i_nregs)\nCTL_PROTO(arenas_bin_i_slab_size)\nINDEX_PROTO(arenas_bin_i)\nCTL_PROTO(arenas_lextent_i_size)\nINDEX_PROTO(arenas_lextent_i)\nCTL_PROTO(arenas_narenas)\nCTL_PROTO(arenas_dirty_decay_ms)\nCTL_PROTO(arenas_muzzy_decay_ms)\nCTL_PROTO(arenas_quantum)\nCTL_PROTO(arenas_page)\nCTL_PROTO(arenas_tcache_max)\nCTL_PROTO(arenas_nbins)\nCTL_PROTO(arenas_nhbins)\nCTL_PROTO(arenas_nlextents)\nCTL_PROTO(arenas_create)\nCTL_PROTO(arenas_lookup)\nCTL_PROTO(prof_thread_active_init)\nCTL_PROTO(prof_active)\nCTL_PROTO(prof_dump)\nCTL_PROTO(prof_gdump)\nCTL_PROTO(prof_reset)\nCTL_PROTO(prof_interval)\nCTL_PROTO(lg_prof_sample)\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_nmalloc)\nCTL_PROTO(stats_arenas_i_bins_j_ndalloc)\nCTL_PROTO(stats_arenas_i_bins_j_nrequests)\nCTL_PROTO(stats_arenas_i_bins_j_curregs)\nCTL_PROTO(stats_arenas_i_bins_j_nfills)\nCTL_PROTO(stats_arenas_i_bins_j_nflushes)\nCTL_PROTO(stats_arenas_i_bins_j_nslabs)\nCTL_PROTO(stats_arenas_i_bins_j_nreslabs)\nCTL_PROTO(stats_arenas_i_bins_j_curslabs)\nINDEX_PROTO(stats_arenas_i_bins_j)\nCTL_PROTO(stats_arenas_i_lextents_j_nmalloc)\nCTL_PROTO(stats_arenas_i_lextents_j_ndalloc)\nCTL_PROTO(stats_arenas_i_lextents_j_nrequests)\nCTL_PROTO(stats_arenas_i_lextents_j_curlextents)\nINDEX_PROTO(stats_arenas_i_lextents_j)\nCTL_PROTO(stats_arenas_i_nthreads)\nCTL_PROTO(stats_arenas_i_uptime)\nCTL_PROTO(stats_arenas_i_dss)\nCTL_PROTO(stats_arenas_i_dirty_decay_ms)\nCTL_PROTO(stats_arenas_i_muzzy_decay_ms)\nCTL_PROTO(stats_arenas_i_pactive)\nCTL_PROTO(stats_arenas_i_pdirty)\nCTL_PROTO(stats_arenas_i_pmuzzy)\nCTL_PROTO(stats_arenas_i_mapped)\nCTL_PROTO(stats_arenas_i_retained)\nCTL_PROTO(stats_arenas_i_dirty_npurge)\nCTL_PROTO(stats_arenas_i_dirty_nmadvise)\nCTL_PROTO(stats_arenas_i_dirty_purged)\nCTL_PROTO(stats_arenas_i_muzzy_npurge)\nCTL_PROTO(stats_arenas_i_muzzy_nmadvise)\nCTL_PROTO(stats_arenas_i_muzzy_purged)\nCTL_PROTO(stats_arenas_i_base)\nCTL_PROTO(stats_arenas_i_internal)\nCTL_PROTO(stats_arenas_i_metadata_thp)\nCTL_PROTO(stats_arenas_i_tcache_bytes)\nCTL_PROTO(stats_arenas_i_resident)\nINDEX_PROTO(stats_arenas_i)\nCTL_PROTO(stats_allocated)\nCTL_PROTO(stats_active)\nCTL_PROTO(stats_background_thread_num_threads)\nCTL_PROTO(stats_background_thread_num_runs)\nCTL_PROTO(stats_background_thread_run_interval)\nCTL_PROTO(stats_metadata)\nCTL_PROTO(stats_metadata_thp)\nCTL_PROTO(stats_resident)\nCTL_PROTO(stats_mapped)\nCTL_PROTO(stats_retained)\n\n#define MUTEX_STATS_CTL_PROTO_GEN(n)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_ops)\t\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_wait)\t\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_spin_acq)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_num_owner_switch)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_total_wait_time)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_max_wait_time)\t\t\t\t\t\\\nCTL_PROTO(stats_##n##_max_num_thds)\n\n/* Global mutexes. */\n#define OP(mtx) MUTEX_STATS_CTL_PROTO_GEN(mutexes_##mtx)\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\n/* Per arena mutexes. */\n#define OP(mtx) MUTEX_STATS_CTL_PROTO_GEN(arenas_i_mutexes_##mtx)\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\n/* Arena bin mutexes. */\nMUTEX_STATS_CTL_PROTO_GEN(arenas_i_bins_j_mutex)\n#undef MUTEX_STATS_CTL_PROTO_GEN\n\nCTL_PROTO(stats_mutexes_reset)\n\n/******************************************************************************/\n/* mallctl tree. */\n\n#define NAME(n)\t{true},\tn\n#define CHILD(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 CTL(c)\t0, NULL, c##_ctl\n\n/*\n * Only handles internal indexed nodes, since there are currently no external\n * ones.\n */\n#define INDEX(i)\t{false},\ti##_index\n\nstatic const ctl_named_node_t\tthread_tcache_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_prof_node[] = {\n\t{NAME(\"name\"),\t\tCTL(thread_prof_name)},\n\t{NAME(\"active\"),\tCTL(thread_prof_active)}\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, thread_tcache)},\n\t{NAME(\"prof\"),\t\tCHILD(named, thread_prof)}\n};\n\nstatic const ctl_named_node_t\tconfig_node[] = {\n\t{NAME(\"cache_oblivious\"), CTL(config_cache_oblivious)},\n\t{NAME(\"debug\"),\t\tCTL(config_debug)},\n\t{NAME(\"fill\"),\t\tCTL(config_fill)},\n\t{NAME(\"lazy_lock\"),\tCTL(config_lazy_lock)},\n\t{NAME(\"malloc_conf\"),\tCTL(config_malloc_conf)},\n\t{NAME(\"prof\"),\t\tCTL(config_prof)},\n\t{NAME(\"prof_libgcc\"),\tCTL(config_prof_libgcc)},\n\t{NAME(\"prof_libunwind\"), CTL(config_prof_libunwind)},\n\t{NAME(\"stats\"),\t\tCTL(config_stats)},\n\t{NAME(\"utrace\"),\tCTL(config_utrace)},\n\t{NAME(\"xmalloc\"),\tCTL(config_xmalloc)}\n};\n\nstatic const ctl_named_node_t opt_node[] = {\n\t{NAME(\"abort\"),\t\tCTL(opt_abort)},\n\t{NAME(\"abort_conf\"),\tCTL(opt_abort_conf)},\n\t{NAME(\"metadata_thp\"),\tCTL(opt_metadata_thp)},\n\t{NAME(\"retain\"),\tCTL(opt_retain)},\n\t{NAME(\"dss\"),\t\tCTL(opt_dss)},\n\t{NAME(\"narenas\"),\tCTL(opt_narenas)},\n\t{NAME(\"percpu_arena\"),\tCTL(opt_percpu_arena)},\n\t{NAME(\"background_thread\"),\tCTL(opt_background_thread)},\n\t{NAME(\"max_background_threads\"),\tCTL(opt_max_background_threads)},\n\t{NAME(\"dirty_decay_ms\"), CTL(opt_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(opt_muzzy_decay_ms)},\n\t{NAME(\"stats_print\"),\tCTL(opt_stats_print)},\n\t{NAME(\"stats_print_opts\"),\tCTL(opt_stats_print_opts)},\n\t{NAME(\"junk\"),\t\tCTL(opt_junk)},\n\t{NAME(\"zero\"),\t\tCTL(opt_zero)},\n\t{NAME(\"utrace\"),\tCTL(opt_utrace)},\n\t{NAME(\"xmalloc\"),\tCTL(opt_xmalloc)},\n\t{NAME(\"tcache\"),\tCTL(opt_tcache)},\n\t{NAME(\"thp\"),\t\tCTL(opt_thp)},\n\t{NAME(\"lg_extent_max_active_fit\"), CTL(opt_lg_extent_max_active_fit)},\n\t{NAME(\"lg_tcache_max\"),\tCTL(opt_lg_tcache_max)},\n\t{NAME(\"prof\"),\t\tCTL(opt_prof)},\n\t{NAME(\"prof_prefix\"),\tCTL(opt_prof_prefix)},\n\t{NAME(\"prof_active\"),\tCTL(opt_prof_active)},\n\t{NAME(\"prof_thread_active_init\"), CTL(opt_prof_thread_active_init)},\n\t{NAME(\"lg_prof_sample\"), CTL(opt_lg_prof_sample)},\n\t{NAME(\"lg_prof_interval\"), CTL(opt_lg_prof_interval)},\n\t{NAME(\"prof_gdump\"),\tCTL(opt_prof_gdump)},\n\t{NAME(\"prof_final\"),\tCTL(opt_prof_final)},\n\t{NAME(\"prof_leak\"),\tCTL(opt_prof_leak)},\n\t{NAME(\"prof_accum\"),\tCTL(opt_prof_accum)}\n};\n\nstatic const ctl_named_node_t\ttcache_node[] = {\n\t{NAME(\"create\"),\tCTL(tcache_create)},\n\t{NAME(\"flush\"),\t\tCTL(tcache_flush)},\n\t{NAME(\"destroy\"),\tCTL(tcache_destroy)}\n};\n\nstatic const ctl_named_node_t arena_i_node[] = {\n\t{NAME(\"initialized\"),\tCTL(arena_i_initialized)},\n\t{NAME(\"decay\"),\t\tCTL(arena_i_decay)},\n\t{NAME(\"purge\"),\t\tCTL(arena_i_purge)},\n\t{NAME(\"reset\"),\t\tCTL(arena_i_reset)},\n\t{NAME(\"destroy\"),\tCTL(arena_i_destroy)},\n\t{NAME(\"dss\"),\t\tCTL(arena_i_dss)},\n\t{NAME(\"dirty_decay_ms\"), CTL(arena_i_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(arena_i_muzzy_decay_ms)},\n\t{NAME(\"extent_hooks\"),\tCTL(arena_i_extent_hooks)},\n\t{NAME(\"retain_grow_limit\"),\tCTL(arena_i_retain_grow_limit)}\n};\nstatic const ctl_named_node_t super_arena_i_node[] = {\n\t{NAME(\"\"),\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\tCTL(arenas_bin_i_size)},\n\t{NAME(\"nregs\"),\t\tCTL(arenas_bin_i_nregs)},\n\t{NAME(\"slab_size\"),\tCTL(arenas_bin_i_slab_size)}\n};\nstatic const ctl_named_node_t super_arenas_bin_i_node[] = {\n\t{NAME(\"\"),\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_lextent_i_node[] = {\n\t{NAME(\"size\"),\t\tCTL(arenas_lextent_i_size)}\n};\nstatic const ctl_named_node_t super_arenas_lextent_i_node[] = {\n\t{NAME(\"\"),\t\tCHILD(named, arenas_lextent_i)}\n};\n\nstatic const ctl_indexed_node_t arenas_lextent_node[] = {\n\t{INDEX(arenas_lextent_i)}\n};\n\nstatic const ctl_named_node_t arenas_node[] = {\n\t{NAME(\"narenas\"),\tCTL(arenas_narenas)},\n\t{NAME(\"dirty_decay_ms\"), CTL(arenas_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(arenas_muzzy_decay_ms)},\n\t{NAME(\"quantum\"),\tCTL(arenas_quantum)},\n\t{NAME(\"page\"),\t\tCTL(arenas_page)},\n\t{NAME(\"tcache_max\"),\tCTL(arenas_tcache_max)},\n\t{NAME(\"nbins\"),\t\tCTL(arenas_nbins)},\n\t{NAME(\"nhbins\"),\tCTL(arenas_nhbins)},\n\t{NAME(\"bin\"),\t\tCHILD(indexed, arenas_bin)},\n\t{NAME(\"nlextents\"),\tCTL(arenas_nlextents)},\n\t{NAME(\"lextent\"),\tCHILD(indexed, arenas_lextent)},\n\t{NAME(\"create\"),\tCTL(arenas_create)},\n\t{NAME(\"lookup\"),\tCTL(arenas_lookup)}\n};\n\nstatic const ctl_named_node_t\tprof_node[] = {\n\t{NAME(\"thread_active_init\"), CTL(prof_thread_active_init)},\n\t{NAME(\"active\"),\tCTL(prof_active)},\n\t{NAME(\"dump\"),\t\tCTL(prof_dump)},\n\t{NAME(\"gdump\"),\t\tCTL(prof_gdump)},\n\t{NAME(\"reset\"),\t\tCTL(prof_reset)},\n\t{NAME(\"interval\"),\tCTL(prof_interval)},\n\t{NAME(\"lg_sample\"),\tCTL(lg_prof_sample)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_small_node[] = {\n\t{NAME(\"allocated\"),\tCTL(stats_arenas_i_small_allocated)},\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_small_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_small_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_small_nrequests)}\n};\n\nstatic const ctl_named_node_t stats_arenas_i_large_node[] = {\n\t{NAME(\"allocated\"),\tCTL(stats_arenas_i_large_allocated)},\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_large_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_large_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_large_nrequests)}\n};\n\n#define MUTEX_PROF_DATA_NODE(prefix)\t\t\t\t\t\\\nstatic const ctl_named_node_t stats_##prefix##_node[] = {\t\t\\\n\t{NAME(\"num_ops\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_ops)},\t\t\t\t\\\n\t{NAME(\"num_wait\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_wait)},\t\t\t\t\\\n\t{NAME(\"num_spin_acq\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_spin_acq)},\t\t\t\t\\\n\t{NAME(\"num_owner_switch\"),\t\t\t\t\t\\\n\t CTL(stats_##prefix##_num_owner_switch)},\t\t\t\\\n\t{NAME(\"total_wait_time\"),\t\t\t\t\t\\\n\t CTL(stats_##prefix##_total_wait_time)},\t\t\t\\\n\t{NAME(\"max_wait_time\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_max_wait_time)},\t\t\t\t\\\n\t{NAME(\"max_num_thds\"),\t\t\t\t\t\t\\\n\t CTL(stats_##prefix##_max_num_thds)}\t\t\t\t\\\n\t/* Note that # of current waiting thread not provided. */\t\\\n};\n\nMUTEX_PROF_DATA_NODE(arenas_i_bins_j_mutex)\n\nstatic const ctl_named_node_t stats_arenas_i_bins_j_node[] = {\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_bins_j_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_bins_j_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_bins_j_nrequests)},\n\t{NAME(\"curregs\"),\tCTL(stats_arenas_i_bins_j_curregs)},\n\t{NAME(\"nfills\"),\tCTL(stats_arenas_i_bins_j_nfills)},\n\t{NAME(\"nflushes\"),\tCTL(stats_arenas_i_bins_j_nflushes)},\n\t{NAME(\"nslabs\"),\tCTL(stats_arenas_i_bins_j_nslabs)},\n\t{NAME(\"nreslabs\"),\tCTL(stats_arenas_i_bins_j_nreslabs)},\n\t{NAME(\"curslabs\"),\tCTL(stats_arenas_i_bins_j_curslabs)},\n\t{NAME(\"mutex\"),\t\tCHILD(named, stats_arenas_i_bins_j_mutex)}\n};\n\nstatic const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = {\n\t{NAME(\"\"),\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_lextents_j_node[] = {\n\t{NAME(\"nmalloc\"),\tCTL(stats_arenas_i_lextents_j_nmalloc)},\n\t{NAME(\"ndalloc\"),\tCTL(stats_arenas_i_lextents_j_ndalloc)},\n\t{NAME(\"nrequests\"),\tCTL(stats_arenas_i_lextents_j_nrequests)},\n\t{NAME(\"curlextents\"),\tCTL(stats_arenas_i_lextents_j_curlextents)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_lextents_j_node[] = {\n\t{NAME(\"\"),\t\tCHILD(named, stats_arenas_i_lextents_j)}\n};\n\nstatic const ctl_indexed_node_t stats_arenas_i_lextents_node[] = {\n\t{INDEX(stats_arenas_i_lextents_j)}\n};\n\n#define OP(mtx)  MUTEX_PROF_DATA_NODE(arenas_i_mutexes_##mtx)\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\nstatic const ctl_named_node_t stats_arenas_i_mutexes_node[] = {\n#define OP(mtx) {NAME(#mtx), CHILD(named, stats_arenas_i_mutexes_##mtx)},\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n};\n\nstatic const ctl_named_node_t stats_arenas_i_node[] = {\n\t{NAME(\"nthreads\"),\tCTL(stats_arenas_i_nthreads)},\n\t{NAME(\"uptime\"),\tCTL(stats_arenas_i_uptime)},\n\t{NAME(\"dss\"),\t\tCTL(stats_arenas_i_dss)},\n\t{NAME(\"dirty_decay_ms\"), CTL(stats_arenas_i_dirty_decay_ms)},\n\t{NAME(\"muzzy_decay_ms\"), CTL(stats_arenas_i_muzzy_decay_ms)},\n\t{NAME(\"pactive\"),\tCTL(stats_arenas_i_pactive)},\n\t{NAME(\"pdirty\"),\tCTL(stats_arenas_i_pdirty)},\n\t{NAME(\"pmuzzy\"),\tCTL(stats_arenas_i_pmuzzy)},\n\t{NAME(\"mapped\"),\tCTL(stats_arenas_i_mapped)},\n\t{NAME(\"retained\"),\tCTL(stats_arenas_i_retained)},\n\t{NAME(\"dirty_npurge\"),\tCTL(stats_arenas_i_dirty_npurge)},\n\t{NAME(\"dirty_nmadvise\"), CTL(stats_arenas_i_dirty_nmadvise)},\n\t{NAME(\"dirty_purged\"),\tCTL(stats_arenas_i_dirty_purged)},\n\t{NAME(\"muzzy_npurge\"),\tCTL(stats_arenas_i_muzzy_npurge)},\n\t{NAME(\"muzzy_nmadvise\"), CTL(stats_arenas_i_muzzy_nmadvise)},\n\t{NAME(\"muzzy_purged\"),\tCTL(stats_arenas_i_muzzy_purged)},\n\t{NAME(\"base\"),\t\tCTL(stats_arenas_i_base)},\n\t{NAME(\"internal\"),\tCTL(stats_arenas_i_internal)},\n\t{NAME(\"metadata_thp\"),\tCTL(stats_arenas_i_metadata_thp)},\n\t{NAME(\"tcache_bytes\"),\tCTL(stats_arenas_i_tcache_bytes)},\n\t{NAME(\"resident\"),\tCTL(stats_arenas_i_resident)},\n\t{NAME(\"small\"),\t\tCHILD(named, stats_arenas_i_small)},\n\t{NAME(\"large\"),\t\tCHILD(named, stats_arenas_i_large)},\n\t{NAME(\"bins\"),\t\tCHILD(indexed, stats_arenas_i_bins)},\n\t{NAME(\"lextents\"),\tCHILD(indexed, stats_arenas_i_lextents)},\n\t{NAME(\"mutexes\"),\tCHILD(named, stats_arenas_i_mutexes)}\n};\nstatic const ctl_named_node_t super_stats_arenas_i_node[] = {\n\t{NAME(\"\"),\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_background_thread_node[] = {\n\t{NAME(\"num_threads\"),\tCTL(stats_background_thread_num_threads)},\n\t{NAME(\"num_runs\"),\tCTL(stats_background_thread_num_runs)},\n\t{NAME(\"run_interval\"),\tCTL(stats_background_thread_run_interval)}\n};\n\n#define OP(mtx) MUTEX_PROF_DATA_NODE(mutexes_##mtx)\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\nstatic const ctl_named_node_t stats_mutexes_node[] = {\n#define OP(mtx) {NAME(#mtx), CHILD(named, stats_mutexes_##mtx)},\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\t{NAME(\"reset\"),\t\tCTL(stats_mutexes_reset)}\n};\n#undef MUTEX_PROF_DATA_NODE\n\nstatic const ctl_named_node_t stats_node[] = {\n\t{NAME(\"allocated\"),\tCTL(stats_allocated)},\n\t{NAME(\"active\"),\tCTL(stats_active)},\n\t{NAME(\"metadata\"),\tCTL(stats_metadata)},\n\t{NAME(\"metadata_thp\"),\tCTL(stats_metadata_thp)},\n\t{NAME(\"resident\"),\tCTL(stats_resident)},\n\t{NAME(\"mapped\"),\tCTL(stats_mapped)},\n\t{NAME(\"retained\"),\tCTL(stats_retained)},\n\t{NAME(\"background_thread\"),\n\t CHILD(named, stats_background_thread)},\n\t{NAME(\"mutexes\"),\tCHILD(named, stats_mutexes)},\n\t{NAME(\"arenas\"),\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(\"background_thread\"),\tCTL(background_thread)},\n\t{NAME(\"max_background_threads\"),\tCTL(max_background_threads)},\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(\"tcache\"),\tCHILD(named, tcache)},\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\n/*\n * Sets *dst + *src non-atomically.  This is safe, since everything is\n * synchronized by the ctl mutex.\n */\nstatic void\nctl_accum_arena_stats_u64(arena_stats_u64_t *dst, arena_stats_u64_t *src) {\n#ifdef JEMALLOC_ATOMIC_U64\n\tuint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);\n\tuint64_t cur_src = atomic_load_u64(src, ATOMIC_RELAXED);\n\tatomic_store_u64(dst, cur_dst + cur_src, ATOMIC_RELAXED);\n#else\n\t*dst += *src;\n#endif\n}\n\n/* Likewise: with ctl mutex synchronization, reading is simple. */\nstatic uint64_t\nctl_arena_stats_read_u64(arena_stats_u64_t *p) {\n#ifdef JEMALLOC_ATOMIC_U64\n\treturn atomic_load_u64(p, ATOMIC_RELAXED);\n#else\n\treturn *p;\n#endif\n}\n\nstatic void\naccum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) {\n\tsize_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);\n\tsize_t cur_src = atomic_load_zu(src, ATOMIC_RELAXED);\n\tatomic_store_zu(dst, cur_dst + cur_src, ATOMIC_RELAXED);\n}\n\n/******************************************************************************/\n\nstatic unsigned\narenas_i2a_impl(size_t i, bool compat, bool validate) {\n\tunsigned a;\n\n\tswitch (i) {\n\tcase MALLCTL_ARENAS_ALL:\n\t\ta = 0;\n\t\tbreak;\n\tcase MALLCTL_ARENAS_DESTROYED:\n\t\ta = 1;\n\t\tbreak;\n\tdefault:\n\t\tif (compat && i == ctl_arenas->narenas) {\n\t\t\t/*\n\t\t\t * Provide deprecated backward compatibility for\n\t\t\t * accessing the merged stats at index narenas rather\n\t\t\t * than via MALLCTL_ARENAS_ALL.  This is scheduled for\n\t\t\t * removal in 6.0.0.\n\t\t\t */\n\t\t\ta = 0;\n\t\t} else if (validate && i >= ctl_arenas->narenas) {\n\t\t\ta = UINT_MAX;\n\t\t} else {\n\t\t\t/*\n\t\t\t * This function should never be called for an index\n\t\t\t * more than one past the range of indices that have\n\t\t\t * initialized ctl data.\n\t\t\t */\n\t\t\tassert(i < ctl_arenas->narenas || (!validate && i ==\n\t\t\t    ctl_arenas->narenas));\n\t\t\ta = (unsigned)i + 2;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn a;\n}\n\nstatic unsigned\narenas_i2a(size_t i) {\n\treturn arenas_i2a_impl(i, true, false);\n}\n\nstatic ctl_arena_t *\narenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) {\n\tctl_arena_t *ret;\n\n\tassert(!compat || !init);\n\n\tret = ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)];\n\tif (init && ret == NULL) {\n\t\tif (config_stats) {\n\t\t\tstruct container_s {\n\t\t\t\tctl_arena_t\t\tctl_arena;\n\t\t\t\tctl_arena_stats_t\tastats;\n\t\t\t};\n\t\t\tstruct container_s *cont =\n\t\t\t    (struct container_s *)base_alloc(tsd_tsdn(tsd),\n\t\t\t    b0get(), sizeof(struct container_s), QUANTUM);\n\t\t\tif (cont == NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tret = &cont->ctl_arena;\n\t\t\tret->astats = &cont->astats;\n\t\t} else {\n\t\t\tret = (ctl_arena_t *)base_alloc(tsd_tsdn(tsd), b0get(),\n\t\t\t    sizeof(ctl_arena_t), QUANTUM);\n\t\t\tif (ret == NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t\tret->arena_ind = (unsigned)i;\n\t\tctl_arenas->arenas[arenas_i2a_impl(i, compat, false)] = ret;\n\t}\n\n\tassert(ret == NULL || arenas_i2a(ret->arena_ind) == arenas_i2a(i));\n\treturn ret;\n}\n\nstatic ctl_arena_t *\narenas_i(size_t i) {\n\tctl_arena_t *ret = arenas_i_impl(tsd_fetch(), i, true, false);\n\tassert(ret != NULL);\n\treturn ret;\n}\n\nstatic void\nctl_arena_clear(ctl_arena_t *ctl_arena) {\n\tctl_arena->nthreads = 0;\n\tctl_arena->dss = dss_prec_names[dss_prec_limit];\n\tctl_arena->dirty_decay_ms = -1;\n\tctl_arena->muzzy_decay_ms = -1;\n\tctl_arena->pactive = 0;\n\tctl_arena->pdirty = 0;\n\tctl_arena->pmuzzy = 0;\n\tif (config_stats) {\n\t\tmemset(&ctl_arena->astats->astats, 0, sizeof(arena_stats_t));\n\t\tctl_arena->astats->allocated_small = 0;\n\t\tctl_arena->astats->nmalloc_small = 0;\n\t\tctl_arena->astats->ndalloc_small = 0;\n\t\tctl_arena->astats->nrequests_small = 0;\n\t\tmemset(ctl_arena->astats->bstats, 0, NBINS *\n\t\t    sizeof(bin_stats_t));\n\t\tmemset(ctl_arena->astats->lstats, 0, (NSIZES - NBINS) *\n\t\t    sizeof(arena_stats_large_t));\n\t}\n}\n\nstatic void\nctl_arena_stats_amerge(tsdn_t *tsdn, ctl_arena_t *ctl_arena, arena_t *arena) {\n\tunsigned i;\n\n\tif (config_stats) {\n\t\tarena_stats_merge(tsdn, arena, &ctl_arena->nthreads,\n\t\t    &ctl_arena->dss, &ctl_arena->dirty_decay_ms,\n\t\t    &ctl_arena->muzzy_decay_ms, &ctl_arena->pactive,\n\t\t    &ctl_arena->pdirty, &ctl_arena->pmuzzy,\n\t\t    &ctl_arena->astats->astats, ctl_arena->astats->bstats,\n\t\t    ctl_arena->astats->lstats);\n\n\t\tfor (i = 0; i < NBINS; i++) {\n\t\t\tctl_arena->astats->allocated_small +=\n\t\t\t    ctl_arena->astats->bstats[i].curregs *\n\t\t\t    sz_index2size(i);\n\t\t\tctl_arena->astats->nmalloc_small +=\n\t\t\t    ctl_arena->astats->bstats[i].nmalloc;\n\t\t\tctl_arena->astats->ndalloc_small +=\n\t\t\t    ctl_arena->astats->bstats[i].ndalloc;\n\t\t\tctl_arena->astats->nrequests_small +=\n\t\t\t    ctl_arena->astats->bstats[i].nrequests;\n\t\t}\n\t} else {\n\t\tarena_basic_stats_merge(tsdn, arena, &ctl_arena->nthreads,\n\t\t    &ctl_arena->dss, &ctl_arena->dirty_decay_ms,\n\t\t    &ctl_arena->muzzy_decay_ms, &ctl_arena->pactive,\n\t\t    &ctl_arena->pdirty, &ctl_arena->pmuzzy);\n\t}\n}\n\nstatic void\nctl_arena_stats_sdmerge(ctl_arena_t *ctl_sdarena, ctl_arena_t *ctl_arena,\n    bool destroyed) {\n\tunsigned i;\n\n\tif (!destroyed) {\n\t\tctl_sdarena->nthreads += ctl_arena->nthreads;\n\t\tctl_sdarena->pactive += ctl_arena->pactive;\n\t\tctl_sdarena->pdirty += ctl_arena->pdirty;\n\t\tctl_sdarena->pmuzzy += ctl_arena->pmuzzy;\n\t} else {\n\t\tassert(ctl_arena->nthreads == 0);\n\t\tassert(ctl_arena->pactive == 0);\n\t\tassert(ctl_arena->pdirty == 0);\n\t\tassert(ctl_arena->pmuzzy == 0);\n\t}\n\n\tif (config_stats) {\n\t\tctl_arena_stats_t *sdstats = ctl_sdarena->astats;\n\t\tctl_arena_stats_t *astats = ctl_arena->astats;\n\n\t\tif (!destroyed) {\n\t\t\taccum_atomic_zu(&sdstats->astats.mapped,\n\t\t\t    &astats->astats.mapped);\n\t\t\taccum_atomic_zu(&sdstats->astats.retained,\n\t\t\t    &astats->astats.retained);\n\t\t}\n\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.npurge,\n\t\t    &astats->astats.decay_dirty.npurge);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.nmadvise,\n\t\t    &astats->astats.decay_dirty.nmadvise);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.purged,\n\t\t    &astats->astats.decay_dirty.purged);\n\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.npurge,\n\t\t    &astats->astats.decay_muzzy.npurge);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.nmadvise,\n\t\t    &astats->astats.decay_muzzy.nmadvise);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.purged,\n\t\t    &astats->astats.decay_muzzy.purged);\n\n#define OP(mtx) malloc_mutex_prof_merge(\t\t\t\t\\\n\t\t    &(sdstats->astats.mutex_prof_data[\t\t\t\\\n\t\t        arena_prof_mutex_##mtx]),\t\t\t\\\n\t\t    &(astats->astats.mutex_prof_data[\t\t\t\\\n\t\t        arena_prof_mutex_##mtx]));\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\t\tif (!destroyed) {\n\t\t\taccum_atomic_zu(&sdstats->astats.base,\n\t\t\t    &astats->astats.base);\n\t\t\taccum_atomic_zu(&sdstats->astats.internal,\n\t\t\t    &astats->astats.internal);\n\t\t\taccum_atomic_zu(&sdstats->astats.resident,\n\t\t\t    &astats->astats.resident);\n\t\t\taccum_atomic_zu(&sdstats->astats.metadata_thp,\n\t\t\t    &astats->astats.metadata_thp);\n\t\t} else {\n\t\t\tassert(atomic_load_zu(\n\t\t\t    &astats->astats.internal, ATOMIC_RELAXED) == 0);\n\t\t}\n\n\t\tif (!destroyed) {\n\t\t\tsdstats->allocated_small += astats->allocated_small;\n\t\t} else {\n\t\t\tassert(astats->allocated_small == 0);\n\t\t}\n\t\tsdstats->nmalloc_small += astats->nmalloc_small;\n\t\tsdstats->ndalloc_small += astats->ndalloc_small;\n\t\tsdstats->nrequests_small += astats->nrequests_small;\n\n\t\tif (!destroyed) {\n\t\t\taccum_atomic_zu(&sdstats->astats.allocated_large,\n\t\t\t    &astats->astats.allocated_large);\n\t\t} else {\n\t\t\tassert(atomic_load_zu(&astats->astats.allocated_large,\n\t\t\t    ATOMIC_RELAXED) == 0);\n\t\t}\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.nmalloc_large,\n\t\t    &astats->astats.nmalloc_large);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.ndalloc_large,\n\t\t    &astats->astats.ndalloc_large);\n\t\tctl_accum_arena_stats_u64(&sdstats->astats.nrequests_large,\n\t\t    &astats->astats.nrequests_large);\n\n\t\taccum_atomic_zu(&sdstats->astats.tcache_bytes,\n\t\t    &astats->astats.tcache_bytes);\n\n\t\tif (ctl_arena->arena_ind == 0) {\n\t\t\tsdstats->astats.uptime = astats->astats.uptime;\n\t\t}\n\n\t\tfor (i = 0; i < NBINS; i++) {\n\t\t\tsdstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;\n\t\t\tsdstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;\n\t\t\tsdstats->bstats[i].nrequests +=\n\t\t\t    astats->bstats[i].nrequests;\n\t\t\tif (!destroyed) {\n\t\t\t\tsdstats->bstats[i].curregs +=\n\t\t\t\t    astats->bstats[i].curregs;\n\t\t\t} else {\n\t\t\t\tassert(astats->bstats[i].curregs == 0);\n\t\t\t}\n\t\t\tsdstats->bstats[i].nfills += astats->bstats[i].nfills;\n\t\t\tsdstats->bstats[i].nflushes +=\n\t\t\t    astats->bstats[i].nflushes;\n\t\t\tsdstats->bstats[i].nslabs += astats->bstats[i].nslabs;\n\t\t\tsdstats->bstats[i].reslabs += astats->bstats[i].reslabs;\n\t\t\tif (!destroyed) {\n\t\t\t\tsdstats->bstats[i].curslabs +=\n\t\t\t\t    astats->bstats[i].curslabs;\n\t\t\t} else {\n\t\t\t\tassert(astats->bstats[i].curslabs == 0);\n\t\t\t}\n\t\t\tmalloc_mutex_prof_merge(&sdstats->bstats[i].mutex_data,\n\t\t\t    &astats->bstats[i].mutex_data);\n\t\t}\n\n\t\tfor (i = 0; i < NSIZES - NBINS; i++) {\n\t\t\tctl_accum_arena_stats_u64(&sdstats->lstats[i].nmalloc,\n\t\t\t    &astats->lstats[i].nmalloc);\n\t\t\tctl_accum_arena_stats_u64(&sdstats->lstats[i].ndalloc,\n\t\t\t    &astats->lstats[i].ndalloc);\n\t\t\tctl_accum_arena_stats_u64(&sdstats->lstats[i].nrequests,\n\t\t\t    &astats->lstats[i].nrequests);\n\t\t\tif (!destroyed) {\n\t\t\t\tsdstats->lstats[i].curlextents +=\n\t\t\t\t    astats->lstats[i].curlextents;\n\t\t\t} else {\n\t\t\t\tassert(astats->lstats[i].curlextents == 0);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void\nctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, ctl_arena_t *ctl_sdarena,\n    unsigned i, bool destroyed) {\n\tctl_arena_t *ctl_arena = arenas_i(i);\n\n\tctl_arena_clear(ctl_arena);\n\tctl_arena_stats_amerge(tsdn, ctl_arena, arena);\n\t/* Merge into sum stats as well. */\n\tctl_arena_stats_sdmerge(ctl_sdarena, ctl_arena, destroyed);\n}\n\nstatic unsigned\nctl_arena_init(tsd_t *tsd, extent_hooks_t *extent_hooks) {\n\tunsigned arena_ind;\n\tctl_arena_t *ctl_arena;\n\n\tif ((ctl_arena = ql_last(&ctl_arenas->destroyed, destroyed_link)) !=\n\t    NULL) {\n\t\tql_remove(&ctl_arenas->destroyed, ctl_arena, destroyed_link);\n\t\tarena_ind = ctl_arena->arena_ind;\n\t} else {\n\t\tarena_ind = ctl_arenas->narenas;\n\t}\n\n\t/* Trigger stats allocation. */\n\tif (arenas_i_impl(tsd, arena_ind, false, true) == NULL) {\n\t\treturn UINT_MAX;\n\t}\n\n\t/* Initialize new arena. */\n\tif (arena_init(tsd_tsdn(tsd), arena_ind, extent_hooks) == NULL) {\n\t\treturn UINT_MAX;\n\t}\n\n\tif (arena_ind == ctl_arenas->narenas) {\n\t\tctl_arenas->narenas++;\n\t}\n\n\treturn arena_ind;\n}\n\nstatic void\nctl_background_thread_stats_read(tsdn_t *tsdn) {\n\tbackground_thread_stats_t *stats = &ctl_stats->background_thread;\n\tif (!have_background_thread ||\n\t    background_thread_stats_read(tsdn, stats)) {\n\t\tmemset(stats, 0, sizeof(background_thread_stats_t));\n\t\tnstime_init(&stats->run_interval, 0);\n\t}\n}\n\nstatic void\nctl_refresh(tsdn_t *tsdn) {\n\tunsigned i;\n\tctl_arena_t *ctl_sarena = arenas_i(MALLCTL_ARENAS_ALL);\n\tVARIABLE_ARRAY(arena_t *, tarenas, ctl_arenas->narenas);\n\n\t/*\n\t * Clear sum stats, since they will be merged into by\n\t * ctl_arena_refresh().\n\t */\n\tctl_arena_clear(ctl_sarena);\n\n\tfor (i = 0; i < ctl_arenas->narenas; i++) {\n\t\ttarenas[i] = arena_get(tsdn, i, false);\n\t}\n\n\tfor (i = 0; i < ctl_arenas->narenas; i++) {\n\t\tctl_arena_t *ctl_arena = arenas_i(i);\n\t\tbool initialized = (tarenas[i] != NULL);\n\n\t\tctl_arena->initialized = initialized;\n\t\tif (initialized) {\n\t\t\tctl_arena_refresh(tsdn, tarenas[i], ctl_sarena, i,\n\t\t\t    false);\n\t\t}\n\t}\n\n\tif (config_stats) {\n\t\tctl_stats->allocated = ctl_sarena->astats->allocated_small +\n\t\t    atomic_load_zu(&ctl_sarena->astats->astats.allocated_large,\n\t\t\tATOMIC_RELAXED);\n\t\tctl_stats->active = (ctl_sarena->pactive << LG_PAGE);\n\t\tctl_stats->metadata = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.base, ATOMIC_RELAXED) +\n\t\t    atomic_load_zu(&ctl_sarena->astats->astats.internal,\n\t\t\tATOMIC_RELAXED);\n\t\tctl_stats->metadata_thp = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.metadata_thp, ATOMIC_RELAXED);\n\t\tctl_stats->resident = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.resident, ATOMIC_RELAXED);\n\t\tctl_stats->mapped = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.mapped, ATOMIC_RELAXED);\n\t\tctl_stats->retained = atomic_load_zu(\n\t\t    &ctl_sarena->astats->astats.retained, ATOMIC_RELAXED);\n\n\t\tctl_background_thread_stats_read(tsdn);\n\n#define READ_GLOBAL_MUTEX_PROF_DATA(i, mtx)\t\t\t\t\\\n    malloc_mutex_lock(tsdn, &mtx);\t\t\t\t\t\\\n    malloc_mutex_prof_read(tsdn, &ctl_stats->mutex_prof_data[i], &mtx);\t\\\n    malloc_mutex_unlock(tsdn, &mtx);\n\n\t\tif (config_prof && opt_prof) {\n\t\t\tREAD_GLOBAL_MUTEX_PROF_DATA(global_prof_mutex_prof,\n\t\t\t    bt2gctx_mtx);\n\t\t}\n\t\tif (have_background_thread) {\n\t\t\tREAD_GLOBAL_MUTEX_PROF_DATA(\n\t\t\t    global_prof_mutex_background_thread,\n\t\t\t    background_thread_lock);\n\t\t} else {\n\t\t\tmemset(&ctl_stats->mutex_prof_data[\n\t\t\t    global_prof_mutex_background_thread], 0,\n\t\t\t    sizeof(mutex_prof_data_t));\n\t\t}\n\t\t/* We own ctl mutex already. */\n\t\tmalloc_mutex_prof_read(tsdn,\n\t\t    &ctl_stats->mutex_prof_data[global_prof_mutex_ctl],\n\t\t    &ctl_mtx);\n#undef READ_GLOBAL_MUTEX_PROF_DATA\n\t}\n\tctl_arenas->epoch++;\n}\n\nstatic bool\nctl_init(tsd_t *tsd) {\n\tbool ret;\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\tif (!ctl_initialized) {\n\t\tctl_arena_t *ctl_sarena, *ctl_darena;\n\t\tunsigned i;\n\n\t\t/*\n\t\t * Allocate demand-zeroed space for pointers to the full\n\t\t * range of supported arena indices.\n\t\t */\n\t\tif (ctl_arenas == NULL) {\n\t\t\tctl_arenas = (ctl_arenas_t *)base_alloc(tsdn,\n\t\t\t    b0get(), sizeof(ctl_arenas_t), QUANTUM);\n\t\t\tif (ctl_arenas == NULL) {\n\t\t\t\tret = true;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\tif (config_stats && ctl_stats == NULL) {\n\t\t\tctl_stats = (ctl_stats_t *)base_alloc(tsdn, b0get(),\n\t\t\t    sizeof(ctl_stats_t), QUANTUM);\n\t\t\tif (ctl_stats == NULL) {\n\t\t\t\tret = true;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Allocate space for the current full range of arenas\n\t\t * here rather than doing it lazily elsewhere, in order\n\t\t * to limit when OOM-caused errors can occur.\n\t\t */\n\t\tif ((ctl_sarena = arenas_i_impl(tsd, MALLCTL_ARENAS_ALL, false,\n\t\t    true)) == NULL) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\tctl_sarena->initialized = true;\n\n\t\tif ((ctl_darena = arenas_i_impl(tsd, MALLCTL_ARENAS_DESTROYED,\n\t\t    false, true)) == NULL) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t\tctl_arena_clear(ctl_darena);\n\t\t/*\n\t\t * Don't toggle ctl_darena to initialized until an arena is\n\t\t * actually destroyed, so that arena.<i>.initialized can be used\n\t\t * to query whether the stats are relevant.\n\t\t */\n\n\t\tctl_arenas->narenas = narenas_total_get();\n\t\tfor (i = 0; i < ctl_arenas->narenas; i++) {\n\t\t\tif (arenas_i_impl(tsd, i, false, true) == NULL) {\n\t\t\t\tret = true;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\tql_new(&ctl_arenas->destroyed);\n\t\tctl_refresh(tsdn);\n\n\t\tctl_initialized = true;\n\t}\n\n\tret = false;\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\nctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,\n    size_t *mibp, size_t *depthp) {\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\t}\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(tsdn, 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\t}\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(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,\n    void *newp, size_t newlen) {\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 && ctl_init(tsd)) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\tdepth = CTL_MAX_DEPTH;\n\tret = ctl_lookup(tsd_tsdn(tsd), name, nodes, mib, &depth);\n\tif (ret != 0) {\n\t\tgoto label_return;\n\t}\n\n\tnode = ctl_named_node(nodes[depth-1]);\n\tif (node != NULL && node->ctl) {\n\t\tret = node->ctl(tsd, mib, depth, oldp, oldlenp, newp, newlen);\n\t} else {\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(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp) {\n\tint ret;\n\n\tif (!ctl_initialized && ctl_init(tsd)) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\n\tret = ctl_lookup(tsd_tsdn(tsd), name, NULL, mibp, miblenp);\nlabel_return:\n\treturn(ret);\n}\n\nint\nctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tconst ctl_named_node_t *node;\n\tsize_t i;\n\n\tif (!ctl_initialized && ctl_init(tsd)) {\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(tsd_tsdn(tsd), 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(tsd, mib, miblen, oldp, oldlenp, newp, newlen);\n\t} else {\n\t\t/* Partial MIB. */\n\t\tret = ENOENT;\n\t}\n\nlabel_return:\n\treturn(ret);\n}\n\nbool\nctl_boot(void) {\n\tif (malloc_mutex_init(&ctl_mtx, \"ctl\", WITNESS_RANK_CTL,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\tctl_initialized = false;\n\n\treturn false;\n}\n\nvoid\nctl_prefork(tsdn_t *tsdn) {\n\tmalloc_mutex_prefork(tsdn, &ctl_mtx);\n}\n\nvoid\nctl_postfork_parent(tsdn_t *tsdn) {\n\tmalloc_mutex_postfork_parent(tsdn, &ctl_mtx);\n}\n\nvoid\nctl_postfork_child(tsdn_t *tsdn) {\n\tmalloc_mutex_postfork_child(tsdn, &ctl_mtx);\n}\n\n/******************************************************************************/\n/* *_ctl() functions. */\n\n#define READONLY()\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 WRITEONLY()\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 READ_XOR_WRITE()\tdo {\t\t\t\t\t\\\n\tif ((oldp != NULL && oldlenp != NULL) && (newp != NULL ||\t\\\n\t    newlen != 0)) {\t\t\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 READ(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}\t\t\t\t\t\t\t\\\n\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 WRITE(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\t(v) = *(t *)newp;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define MIB_UNSIGNED(v, i) do {\t\t\t\t\t\t\\\n\tif (mib[i] > UINT_MAX) {\t\t\t\t\t\\\n\t\tret = EFAULT;\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\tv = (unsigned)mib[i];\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 CTL_RO_CLGEN(c, l, n, v, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif (l) {\t\t\t\t\t\t\t\\\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\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\tif (l) {\t\t\t\t\t\t\t\\\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\treturn ret;\t\t\t\t\t\t\t\\\n}\n\n#define CTL_RO_CGEN(c, n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\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(tsd_tsdn(tsd), &ctl_mtx);\t\t\t\\\n\treturn ret;\t\t\t\t\t\t\t\\\n}\n\n#define CTL_RO_GEN(n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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(tsd_tsdn(tsd), &ctl_mtx);\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(tsd_tsdn(tsd), &ctl_mtx);\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 CTL_RO_NL_CGEN(c, n, v, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\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 CTL_RO_NL_GEN(n, v, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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 CTL_TSD_RO_NL_CGEN(c, n, m, t)\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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)) {\t\t\t\t\t\t\t\\\n\t\treturn ENOENT;\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 = (m(tsd));\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 CTL_RO_CONFIG_GEN(n, t)\t\t\t\t\t\t\\\nstatic int\t\t\t\t\t\t\t\t\\\nn##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\t\\\n    size_t *oldlenp, void *newp, size_t newlen) {\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 = n;\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/******************************************************************************/\n\nCTL_RO_NL_GEN(version, JEMALLOC_VERSION, const char *)\n\nstatic int\nepoch_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tUNUSED uint64_t newval;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tWRITE(newval, uint64_t);\n\tif (newp != NULL) {\n\t\tctl_refresh(tsd_tsdn(tsd));\n\t}\n\tREAD(ctl_arenas->epoch, uint64_t);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\nbackground_thread_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!have_background_thread) {\n\t\treturn ENOENT;\n\t}\n\tbackground_thread_ctl_init(tsd_tsdn(tsd));\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\tif (newp == NULL) {\n\t\toldval = background_thread_enabled();\n\t\tREAD(oldval, bool);\n\t} else {\n\t\tif (newlen != sizeof(bool)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\toldval = background_thread_enabled();\n\t\tREAD(oldval, bool);\n\n\t\tbool newval = *(bool *)newp;\n\t\tif (newval == oldval) {\n\t\t\tret = 0;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tbackground_thread_enabled_set(tsd_tsdn(tsd), newval);\n\t\tif (newval) {\n\t\t\tif (!can_enable_background_thread) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: Error in dlsym(\"\n\t\t\t            \"RTLD_NEXT, \\\"pthread_create\\\"). Cannot \"\n\t\t\t\t    \"enable background_thread\\n\");\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tif (background_threads_enable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t} else {\n\t\t\tif (background_threads_disable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\t}\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\n\treturn ret;\n}\n\nstatic int\nmax_background_threads_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tsize_t oldval;\n\n\tif (!have_background_thread) {\n\t\treturn ENOENT;\n\t}\n\tbackground_thread_ctl_init(tsd_tsdn(tsd));\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\tif (newp == NULL) {\n\t\toldval = max_background_threads;\n\t\tREAD(oldval, size_t);\n\t} else {\n\t\tif (newlen != sizeof(size_t)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\toldval = max_background_threads;\n\t\tREAD(oldval, size_t);\n\n\t\tsize_t newval = *(size_t *)newp;\n\t\tif (newval == oldval) {\n\t\t\tret = 0;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (newval > opt_max_background_threads) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tif (background_thread_enabled()) {\n\t\t\tif (!can_enable_background_thread) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: Error in dlsym(\"\n\t\t\t            \"RTLD_NEXT, \\\"pthread_create\\\"). Cannot \"\n\t\t\t\t    \"enable background_thread\\n\");\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tbackground_thread_enabled_set(tsd_tsdn(tsd), false);\n\t\t\tif (background_threads_disable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\tmax_background_threads = newval;\n\t\t\tbackground_thread_enabled_set(tsd_tsdn(tsd), true);\n\t\t\tif (background_threads_enable(tsd)) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t} else {\n\t\t\tmax_background_threads = newval;\n\t\t}\n\t}\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\n\treturn ret;\n}\n\n/******************************************************************************/\n\nCTL_RO_CONFIG_GEN(config_cache_oblivious, bool)\nCTL_RO_CONFIG_GEN(config_debug, bool)\nCTL_RO_CONFIG_GEN(config_fill, bool)\nCTL_RO_CONFIG_GEN(config_lazy_lock, bool)\nCTL_RO_CONFIG_GEN(config_malloc_conf, const char *)\nCTL_RO_CONFIG_GEN(config_prof, bool)\nCTL_RO_CONFIG_GEN(config_prof_libgcc, bool)\nCTL_RO_CONFIG_GEN(config_prof_libunwind, bool)\nCTL_RO_CONFIG_GEN(config_stats, bool)\nCTL_RO_CONFIG_GEN(config_utrace, bool)\nCTL_RO_CONFIG_GEN(config_xmalloc, bool)\n\n/******************************************************************************/\n\nCTL_RO_NL_GEN(opt_abort, opt_abort, bool)\nCTL_RO_NL_GEN(opt_abort_conf, opt_abort_conf, bool)\nCTL_RO_NL_GEN(opt_metadata_thp, metadata_thp_mode_names[opt_metadata_thp],\n    const char *)\nCTL_RO_NL_GEN(opt_retain, opt_retain, bool)\nCTL_RO_NL_GEN(opt_dss, opt_dss, const char *)\nCTL_RO_NL_GEN(opt_narenas, opt_narenas, unsigned)\nCTL_RO_NL_GEN(opt_percpu_arena, percpu_arena_mode_names[opt_percpu_arena],\n    const char *)\nCTL_RO_NL_GEN(opt_background_thread, opt_background_thread, bool)\nCTL_RO_NL_GEN(opt_max_background_threads, opt_max_background_threads, size_t)\nCTL_RO_NL_GEN(opt_dirty_decay_ms, opt_dirty_decay_ms, ssize_t)\nCTL_RO_NL_GEN(opt_muzzy_decay_ms, opt_muzzy_decay_ms, ssize_t)\nCTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)\nCTL_RO_NL_GEN(opt_stats_print_opts, opt_stats_print_opts, const char *)\nCTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *)\nCTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool)\nCTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool)\nCTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool)\nCTL_RO_NL_GEN(opt_tcache, opt_tcache, bool)\nCTL_RO_NL_GEN(opt_thp, thp_mode_names[opt_thp], const char *)\nCTL_RO_NL_GEN(opt_lg_extent_max_active_fit, opt_lg_extent_max_active_fit,\n    size_t)\nCTL_RO_NL_GEN(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_NL_CGEN(config_prof, opt_prof_active, opt_prof_active, bool)\nCTL_RO_NL_CGEN(config_prof, opt_prof_thread_active_init,\n    opt_prof_thread_active_init, bool)\nCTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t)\nCTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool)\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)\n\n/******************************************************************************/\n\nstatic int\nthread_arena_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tarena_t *oldarena;\n\tunsigned newind, oldind;\n\n\toldarena = arena_choose(tsd, NULL);\n\tif (oldarena == NULL) {\n\t\treturn EAGAIN;\n\t}\n\tnewind = oldind = arena_ind_get(oldarena);\n\tWRITE(newind, unsigned);\n\tREAD(oldind, unsigned);\n\n\tif (newind != oldind) {\n\t\tarena_t *newarena;\n\n\t\tif (newind >= narenas_total_get()) {\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\tif (have_percpu_arena &&\n\t\t    PERCPU_ARENA_ENABLED(opt_percpu_arena)) {\n\t\t\tif (newind < percpu_arena_ind_limit(opt_percpu_arena)) {\n\t\t\t\t/*\n\t\t\t\t * If perCPU arena is enabled, thread_arena\n\t\t\t\t * control is not allowed for the auto arena\n\t\t\t\t * range.\n\t\t\t\t */\n\t\t\t\tret = EPERM;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t}\n\n\t\t/* Initialize arena if necessary. */\n\t\tnewarena = arena_get(tsd_tsdn(tsd), newind, true);\n\t\tif (newarena == NULL) {\n\t\t\tret = EAGAIN;\n\t\t\tgoto label_return;\n\t\t}\n\t\t/* Set new arena/tcache associations. */\n\t\tarena_migrate(tsd, oldind, newind);\n\t\tif (tcache_available(tsd)) {\n\t\t\ttcache_arena_reassociate(tsd_tsdn(tsd),\n\t\t\t    tsd_tcachep_get(tsd), newarena);\n\t\t}\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nCTL_TSD_RO_NL_CGEN(config_stats, thread_allocated, tsd_thread_allocated_get,\n    uint64_t)\nCTL_TSD_RO_NL_CGEN(config_stats, thread_allocatedp, tsd_thread_allocatedp_get,\n    uint64_t *)\nCTL_TSD_RO_NL_CGEN(config_stats, thread_deallocated, tsd_thread_deallocated_get,\n    uint64_t)\nCTL_TSD_RO_NL_CGEN(config_stats, thread_deallocatedp,\n    tsd_thread_deallocatedp_get, uint64_t *)\n\nstatic int\nthread_tcache_enabled_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\toldval = tcache_enabled_get(tsd);\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(tsd, *(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(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\n\tif (!tcache_available(tsd)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tREADONLY();\n\tWRITEONLY();\n\n\ttcache_flush(tsd);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nthread_prof_name_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\tREAD_XOR_WRITE();\n\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(const char *)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\n\t\tif ((ret = prof_thread_name_set(tsd, *(const char **)newp)) !=\n\t\t    0) {\n\t\t\tgoto label_return;\n\t\t}\n\t} else {\n\t\tconst char *oldname = prof_thread_name_get(tsd);\n\t\tREAD(oldname, const char *);\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nthread_prof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\toldval = prof_thread_active_get(tsd);\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\tif (prof_thread_active_set(tsd, *(bool *)newp)) {\n\t\t\tret = EAGAIN;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\ntcache_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned tcache_ind;\n\n\tREADONLY();\n\tif (tcaches_create(tsd, &tcache_ind)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\tREAD(tcache_ind, unsigned);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\ntcache_flush_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned tcache_ind;\n\n\tWRITEONLY();\n\ttcache_ind = UINT_MAX;\n\tWRITE(tcache_ind, unsigned);\n\tif (tcache_ind == UINT_MAX) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\ttcaches_flush(tsd, tcache_ind);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\ntcache_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned tcache_ind;\n\n\tWRITEONLY();\n\ttcache_ind = UINT_MAX;\n\tWRITE(tcache_ind, unsigned);\n\tif (tcache_ind == UINT_MAX) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\ttcaches_destroy(tsd, tcache_ind);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\narena_i_initialized_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\tunsigned arena_ind;\n\tbool initialized;\n\n\tREADONLY();\n\tMIB_UNSIGNED(arena_ind, 1);\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\tinitialized = arenas_i(arena_ind)->initialized;\n\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\n\tREAD(initialized, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic void\narena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) {\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\t{\n\t\tunsigned narenas = ctl_arenas->narenas;\n\n\t\t/*\n\t\t * Access via index narenas is deprecated, and scheduled for\n\t\t * removal in 6.0.0.\n\t\t */\n\t\tif (arena_ind == MALLCTL_ARENAS_ALL || arena_ind == narenas) {\n\t\t\tunsigned i;\n\t\t\tVARIABLE_ARRAY(arena_t *, tarenas, narenas);\n\n\t\t\tfor (i = 0; i < narenas; i++) {\n\t\t\t\ttarenas[i] = arena_get(tsdn, i, false);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * No further need to hold ctl_mtx, since narenas and\n\t\t\t * tarenas contain everything needed below.\n\t\t\t */\n\t\t\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\n\t\t\tfor (i = 0; i < narenas; i++) {\n\t\t\t\tif (tarenas[i] != NULL) {\n\t\t\t\t\tarena_decay(tsdn, tarenas[i], false,\n\t\t\t\t\t    all);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tarena_t *tarena;\n\n\t\t\tassert(arena_ind < narenas);\n\n\t\t\ttarena = arena_get(tsdn, arena_ind, false);\n\n\t\t\t/* No further need to hold ctl_mtx. */\n\t\t\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\n\t\t\tif (tarena != NULL) {\n\t\t\t\tarena_decay(tsdn, tarena, false, all);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic int\narena_i_decay_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\n\tREADONLY();\n\tWRITEONLY();\n\tMIB_UNSIGNED(arena_ind, 1);\n\tarena_i_decay(tsd_tsdn(tsd), arena_ind, false);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_purge_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\n\tREADONLY();\n\tWRITEONLY();\n\tMIB_UNSIGNED(arena_ind, 1);\n\tarena_i_decay(tsd_tsdn(tsd), arena_ind, true);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_reset_destroy_helper(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen, unsigned *arena_ind,\n    arena_t **arena) {\n\tint ret;\n\n\tREADONLY();\n\tWRITEONLY();\n\tMIB_UNSIGNED(*arena_ind, 1);\n\n\t*arena = arena_get(tsd_tsdn(tsd), *arena_ind, false);\n\tif (*arena == NULL || arena_is_auto(*arena)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic void\narena_reset_prepare_background_thread(tsd_t *tsd, unsigned arena_ind) {\n\t/* Temporarily disable the background thread during arena reset. */\n\tif (have_background_thread) {\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\t\tif (background_thread_enabled()) {\n\t\t\tunsigned ind = arena_ind % ncpus;\n\t\t\tbackground_thread_info_t *info =\n\t\t\t    &background_thread_info[ind];\n\t\t\tassert(info->state == background_thread_started);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\t\tinfo->state = background_thread_paused;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\t}\n\t}\n}\n\nstatic void\narena_reset_finish_background_thread(tsd_t *tsd, unsigned arena_ind) {\n\tif (have_background_thread) {\n\t\tif (background_thread_enabled()) {\n\t\t\tunsigned ind = arena_ind % ncpus;\n\t\t\tbackground_thread_info_t *info =\n\t\t\t    &background_thread_info[ind];\n\t\t\tassert(info->state == background_thread_paused);\n\t\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\t\tinfo->state = background_thread_started;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\t}\n}\n\nstatic int\narena_i_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,\n\t    newp, newlen, &arena_ind, &arena);\n\tif (ret != 0) {\n\t\treturn ret;\n\t}\n\n\tarena_reset_prepare_background_thread(tsd, arena_ind);\n\tarena_reset(tsd, arena);\n\tarena_reset_finish_background_thread(tsd, arena_ind);\n\n\treturn ret;\n}\n\nstatic int\narena_i_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\tctl_arena_t *ctl_darena, *ctl_arena;\n\n\tret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,\n\t    newp, newlen, &arena_ind, &arena);\n\tif (ret != 0) {\n\t\tgoto label_return;\n\t}\n\n\tif (arena_nthreads_get(arena, false) != 0 || arena_nthreads_get(arena,\n\t    true) != 0) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tarena_reset_prepare_background_thread(tsd, arena_ind);\n\t/* Merge stats after resetting and purging arena. */\n\tarena_reset(tsd, arena);\n\tarena_decay(tsd_tsdn(tsd), arena, false, true);\n\tctl_darena = arenas_i(MALLCTL_ARENAS_DESTROYED);\n\tctl_darena->initialized = true;\n\tctl_arena_refresh(tsd_tsdn(tsd), arena, ctl_darena, arena_ind, true);\n\t/* Destroy arena. */\n\tarena_destroy(tsd, arena);\n\tctl_arena = arenas_i(arena_ind);\n\tctl_arena->initialized = false;\n\t/* Record arena index for later recycling via arenas.create. */\n\tql_elm_new(ctl_arena, destroyed_link);\n\tql_tail_insert(&ctl_arenas->destroyed, ctl_arena, destroyed_link);\n\tarena_reset_finish_background_thread(tsd, arena_ind);\n\n\tassert(ret == 0);\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_dss_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tconst char *dss = NULL;\n\tunsigned arena_ind;\n\tdss_prec_t dss_prec_old = dss_prec_limit;\n\tdss_prec_t dss_prec = dss_prec_limit;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tWRITE(dss, const char *);\n\tMIB_UNSIGNED(arena_ind, 1);\n\tif (dss != NULL) {\n\t\tint i;\n\t\tbool match = false;\n\n\t\tfor (i = 0; i < dss_prec_limit; i++) {\n\t\t\tif (strcmp(dss_prec_names[i], dss) == 0) {\n\t\t\t\tdss_prec = i;\n\t\t\t\tmatch = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!match) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\t/*\n\t * Access via index narenas is deprecated, and scheduled for removal in\n\t * 6.0.0.\n\t */\n\tif (arena_ind == MALLCTL_ARENAS_ALL || arena_ind ==\n\t    ctl_arenas->narenas) {\n\t\tif (dss_prec != dss_prec_limit &&\n\t\t    extent_dss_prec_set(dss_prec)) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t\tdss_prec_old = extent_dss_prec_get();\n\t} else {\n\t\tarena_t *arena = arena_get(tsd_tsdn(tsd), arena_ind, false);\n\t\tif (arena == NULL || (dss_prec != dss_prec_limit &&\n\t\t    arena_dss_prec_set(arena, dss_prec))) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t\tdss_prec_old = arena_dss_prec_get(arena);\n\t}\n\n\tdss = dss_prec_names[dss_prec_old];\n\tREAD(dss, const char *);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen, bool dirty) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tMIB_UNSIGNED(arena_ind, 1);\n\tarena = arena_get(tsd_tsdn(tsd), arena_ind, false);\n\tif (arena == NULL) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tif (oldp != NULL && oldlenp != NULL) {\n\t\tsize_t oldval = dirty ? arena_dirty_decay_ms_get(arena) :\n\t\t    arena_muzzy_decay_ms_get(arena);\n\t\tREAD(oldval, ssize_t);\n\t}\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(ssize_t)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (dirty ? arena_dirty_decay_ms_set(tsd_tsdn(tsd), arena,\n\t\t    *(ssize_t *)newp) : arena_muzzy_decay_ms_set(tsd_tsdn(tsd),\n\t\t    arena, *(ssize_t *)newp)) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narena_i_dirty_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arena_i_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, true);\n}\n\nstatic int\narena_i_muzzy_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arena_i_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, false);\n}\n\nstatic int\narena_i_extent_hooks_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tMIB_UNSIGNED(arena_ind, 1);\n\tif (arena_ind < narenas_total_get()) {\n\t\textent_hooks_t *old_extent_hooks;\n\t\tarena = arena_get(tsd_tsdn(tsd), arena_ind, false);\n\t\tif (arena == NULL) {\n\t\t\tif (arena_ind >= narenas_auto) {\n\t\t\t\tret = EFAULT;\n\t\t\t\tgoto label_return;\n\t\t\t}\n\t\t\told_extent_hooks =\n\t\t\t    (extent_hooks_t *)&extent_hooks_default;\n\t\t\tREAD(old_extent_hooks, extent_hooks_t *);\n\t\t\tif (newp != NULL) {\n\t\t\t\t/* Initialize a new arena as a side effect. */\n\t\t\t\textent_hooks_t *new_extent_hooks\n\t\t\t\t    JEMALLOC_CC_SILENCE_INIT(NULL);\n\t\t\t\tWRITE(new_extent_hooks, extent_hooks_t *);\n\t\t\t\tarena = arena_init(tsd_tsdn(tsd), arena_ind,\n\t\t\t\t    new_extent_hooks);\n\t\t\t\tif (arena == NULL) {\n\t\t\t\t\tret = EFAULT;\n\t\t\t\t\tgoto label_return;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (newp != NULL) {\n\t\t\t\textent_hooks_t *new_extent_hooks\n\t\t\t\t    JEMALLOC_CC_SILENCE_INIT(NULL);\n\t\t\t\tWRITE(new_extent_hooks, extent_hooks_t *);\n\t\t\t\told_extent_hooks = extent_hooks_set(tsd, arena,\n\t\t\t\t    new_extent_hooks);\n\t\t\t\tREAD(old_extent_hooks, extent_hooks_t *);\n\t\t\t} else {\n\t\t\t\told_extent_hooks = extent_hooks_get(arena);\n\t\t\t\tREAD(old_extent_hooks, extent_hooks_t *);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narena_i_retain_grow_limit_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tarena_t *arena;\n\n\tif (!opt_retain) {\n\t\t/* Only relevant when retain is enabled. */\n\t\treturn ENOENT;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tMIB_UNSIGNED(arena_ind, 1);\n\tif (arena_ind < narenas_total_get() && (arena =\n\t    arena_get(tsd_tsdn(tsd), arena_ind, false)) != NULL) {\n\t\tsize_t old_limit, new_limit;\n\t\tif (newp != NULL) {\n\t\t\tWRITE(new_limit, size_t);\n\t\t}\n\t\tbool err = arena_retain_grow_limit_get_set(tsd, arena,\n\t\t    &old_limit, newp != NULL ? &new_limit : NULL);\n\t\tif (!err) {\n\t\t\tREAD(old_limit, size_t);\n\t\t\tret = 0;\n\t\t} else {\n\t\t\tret = EFAULT;\n\t\t}\n\t} else {\n\t\tret = EFAULT;\n\t}\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic const ctl_named_node_t *\narena_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {\n\tconst ctl_named_node_t *ret;\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\tswitch (i) {\n\tcase MALLCTL_ARENAS_ALL:\n\tcase MALLCTL_ARENAS_DESTROYED:\n\t\tbreak;\n\tdefault:\n\t\tif (i > ctl_arenas->narenas) {\n\t\t\tret = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tbreak;\n\t}\n\n\tret = super_arena_i_node;\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &ctl_mtx);\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\narenas_narenas_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned narenas;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tREADONLY();\n\tif (*oldlenp != sizeof(unsigned)) {\n\t\tret = EINVAL;\n\t\tgoto label_return;\n\t}\n\tnarenas = ctl_arenas->narenas;\n\tREAD(narenas, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narenas_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen, bool dirty) {\n\tint ret;\n\n\tif (oldp != NULL && oldlenp != NULL) {\n\t\tsize_t oldval = (dirty ? arena_dirty_decay_ms_default_get() :\n\t\t    arena_muzzy_decay_ms_default_get());\n\t\tREAD(oldval, ssize_t);\n\t}\n\tif (newp != NULL) {\n\t\tif (newlen != sizeof(ssize_t)) {\n\t\t\tret = EINVAL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tif (dirty ? arena_dirty_decay_ms_default_set(*(ssize_t *)newp)\n\t\t    : arena_muzzy_decay_ms_default_set(*(ssize_t *)newp)) {\n\t\t\tret = EFAULT;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\narenas_dirty_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arenas_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, true);\n}\n\nstatic int\narenas_muzzy_decay_ms_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\treturn arenas_decay_ms_ctl_impl(tsd, mib, miblen, oldp, oldlenp, newp,\n\t    newlen, false);\n}\n\nCTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t)\nCTL_RO_NL_GEN(arenas_page, PAGE, size_t)\nCTL_RO_NL_GEN(arenas_tcache_max, tcache_maxclass, size_t)\nCTL_RO_NL_GEN(arenas_nbins, NBINS, unsigned)\nCTL_RO_NL_GEN(arenas_nhbins, nhbins, unsigned)\nCTL_RO_NL_GEN(arenas_bin_i_size, bin_infos[mib[2]].reg_size, size_t)\nCTL_RO_NL_GEN(arenas_bin_i_nregs, bin_infos[mib[2]].nregs, uint32_t)\nCTL_RO_NL_GEN(arenas_bin_i_slab_size, bin_infos[mib[2]].slab_size, size_t)\nstatic const ctl_named_node_t *\narenas_bin_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {\n\tif (i > NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_arenas_bin_i_node;\n}\n\nCTL_RO_NL_GEN(arenas_nlextents, NSIZES - NBINS, unsigned)\nCTL_RO_NL_GEN(arenas_lextent_i_size, sz_index2size(NBINS+(szind_t)mib[2]),\n    size_t)\nstatic const ctl_named_node_t *\narenas_lextent_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,\n    size_t i) {\n\tif (i > NSIZES - NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_arenas_lextent_i_node;\n}\n\nstatic int\narenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\textent_hooks_t *extent_hooks;\n\tunsigned arena_ind;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\n\textent_hooks = (extent_hooks_t *)&extent_hooks_default;\n\tWRITE(extent_hooks, extent_hooks_t *);\n\tif ((arena_ind = ctl_arena_init(tsd, extent_hooks)) == UINT_MAX) {\n\t\tret = EAGAIN;\n\t\tgoto label_return;\n\t}\n\tREAD(arena_ind, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\nstatic int\narenas_lookup_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tunsigned arena_ind;\n\tvoid *ptr;\n\textent_t *extent;\n\tarena_t *arena;\n\n\tptr = NULL;\n\tret = EINVAL;\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);\n\tWRITE(ptr, void *);\n\textent = iealloc(tsd_tsdn(tsd), ptr);\n\tif (extent == NULL)\n\t\tgoto label_return;\n\n\tarena = extent_arena_get(extent);\n\tif (arena == NULL)\n\t\tgoto label_return;\n\n\tarena_ind = arena_ind_get(arena);\n\tREAD(arena_ind, unsigned);\n\n\tret = 0;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);\n\treturn ret;\n}\n\n/******************************************************************************/\n\nstatic int\nprof_thread_active_init_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\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\toldval = prof_thread_active_init_set(tsd_tsdn(tsd),\n\t\t    *(bool *)newp);\n\t} else {\n\t\toldval = prof_thread_active_init_get(tsd_tsdn(tsd));\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\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\toldval = prof_active_set(tsd_tsdn(tsd), *(bool *)newp);\n\t} else {\n\t\toldval = prof_active_get(tsd_tsdn(tsd));\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_dump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tconst char *filename = NULL;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\tWRITEONLY();\n\tWRITE(filename, const char *);\n\n\tif (prof_mdump(tsd, filename)) {\n\t\tret = EFAULT;\n\t\tgoto label_return;\n\t}\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_gdump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tbool oldval;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\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\toldval = prof_gdump_set(tsd_tsdn(tsd), *(bool *)newp);\n\t} else {\n\t\toldval = prof_gdump_get(tsd_tsdn(tsd));\n\t}\n\tREAD(oldval, bool);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nstatic int\nprof_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen) {\n\tint ret;\n\tsize_t lg_sample = lg_prof_sample;\n\n\tif (!config_prof) {\n\t\treturn ENOENT;\n\t}\n\n\tWRITEONLY();\n\tWRITE(lg_sample, size_t);\n\tif (lg_sample >= (sizeof(uint64_t) << 3)) {\n\t\tlg_sample = (sizeof(uint64_t) << 3) - 1;\n\t}\n\n\tprof_reset(tsd, lg_sample);\n\n\tret = 0;\nlabel_return:\n\treturn ret;\n}\n\nCTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t)\nCTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t)\n\n/******************************************************************************/\n\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_metadata, ctl_stats->metadata, size_t)\nCTL_RO_CGEN(config_stats, stats_metadata_thp, ctl_stats->metadata_thp, size_t)\nCTL_RO_CGEN(config_stats, stats_resident, ctl_stats->resident, size_t)\nCTL_RO_CGEN(config_stats, stats_mapped, ctl_stats->mapped, size_t)\nCTL_RO_CGEN(config_stats, stats_retained, ctl_stats->retained, size_t)\n\nCTL_RO_CGEN(config_stats, stats_background_thread_num_threads,\n    ctl_stats->background_thread.num_threads, size_t)\nCTL_RO_CGEN(config_stats, stats_background_thread_num_runs,\n    ctl_stats->background_thread.num_runs, uint64_t)\nCTL_RO_CGEN(config_stats, stats_background_thread_run_interval,\n    nstime_ns(&ctl_stats->background_thread.run_interval), uint64_t)\n\nCTL_RO_GEN(stats_arenas_i_dss, arenas_i(mib[2])->dss, const char *)\nCTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms,\n    ssize_t)\nCTL_RO_GEN(stats_arenas_i_muzzy_decay_ms, arenas_i(mib[2])->muzzy_decay_ms,\n    ssize_t)\nCTL_RO_GEN(stats_arenas_i_nthreads, arenas_i(mib[2])->nthreads, unsigned)\nCTL_RO_GEN(stats_arenas_i_uptime,\n    nstime_ns(&arenas_i(mib[2])->astats->astats.uptime), uint64_t)\nCTL_RO_GEN(stats_arenas_i_pactive, arenas_i(mib[2])->pactive, size_t)\nCTL_RO_GEN(stats_arenas_i_pdirty, arenas_i(mib[2])->pdirty, size_t)\nCTL_RO_GEN(stats_arenas_i_pmuzzy, arenas_i(mib[2])->pmuzzy, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_mapped,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.mapped, ATOMIC_RELAXED),\n    size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_retained,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.retained, ATOMIC_RELAXED),\n    size_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_dirty_npurge,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_dirty.npurge), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_dirty_nmadvise,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_dirty.nmadvise), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_dirty_purged,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_dirty.purged), uint64_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_npurge,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_muzzy.npurge), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_nmadvise,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_muzzy.nmadvise), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_purged,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.decay_muzzy.purged), uint64_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_base,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.base, ATOMIC_RELAXED),\n    size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_internal,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.internal, ATOMIC_RELAXED),\n    size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_metadata_thp,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.metadata_thp,\n    ATOMIC_RELAXED), size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_tcache_bytes,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.tcache_bytes,\n    ATOMIC_RELAXED), size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_resident,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.resident, ATOMIC_RELAXED),\n    size_t)\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated,\n    arenas_i(mib[2])->astats->allocated_small, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc,\n    arenas_i(mib[2])->astats->nmalloc_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_ndalloc,\n    arenas_i(mib[2])->astats->ndalloc_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_small_nrequests,\n    arenas_i(mib[2])->astats->nrequests_small, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_allocated,\n    atomic_load_zu(&arenas_i(mib[2])->astats->astats.allocated_large,\n    ATOMIC_RELAXED), size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_nmalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.nmalloc_large), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.ndalloc_large), uint64_t)\n/*\n * Note: \"nmalloc\" here instead of \"nrequests\" in the read.  This is intentional.\n */\nCTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->astats.nmalloc_large), uint64_t) /* Intentional. */\n\n/* Lock profiling related APIs below. */\n#define RO_MUTEX_CTL_GEN(n, l)\t\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_ops,\t\t\t\t\\\n    l.n_lock_ops, uint64_t)\t\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_wait,\t\t\t\t\\\n    l.n_wait_times, uint64_t)\t\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_spin_acq,\t\t\t\\\n    l.n_spin_acquired, uint64_t)\t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_num_owner_switch,\t\t\t\\\n    l.n_owner_switches, uint64_t) \t\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_total_wait_time,\t\t\t\\\n    nstime_ns(&l.tot_wait_time), uint64_t)\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_max_wait_time,\t\t\t\\\n    nstime_ns(&l.max_wait_time), uint64_t)\t\t\t\t\\\nCTL_RO_CGEN(config_stats, stats_##n##_max_num_thds,\t\t\t\\\n    l.max_n_thds, uint32_t)\n\n/* Global mutexes. */\n#define OP(mtx)\t\t\t\t\t\t\t\t\\\n    RO_MUTEX_CTL_GEN(mutexes_##mtx,\t\t\t\t\t\\\n        ctl_stats->mutex_prof_data[global_prof_mutex_##mtx])\nMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n\n/* Per arena mutexes */\n#define OP(mtx) RO_MUTEX_CTL_GEN(arenas_i_mutexes_##mtx,\t\t\\\n    arenas_i(mib[2])->astats->astats.mutex_prof_data[arena_prof_mutex_##mtx])\nMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n\n/* tcache bin mutex */\nRO_MUTEX_CTL_GEN(arenas_i_bins_j_mutex,\n    arenas_i(mib[2])->astats->bstats[mib[4]].mutex_data)\n#undef RO_MUTEX_CTL_GEN\n\n/* Resets all mutex stats, including global, arena and bin mutexes. */\nstatic int\nstats_mutexes_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,\n    void *oldp, size_t *oldlenp, void *newp, size_t newlen) {\n\tif (!config_stats) {\n\t\treturn ENOENT;\n\t}\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd);\n\n#define MUTEX_PROF_RESET(mtx)\t\t\t\t\t\t\\\n    malloc_mutex_lock(tsdn, &mtx);\t\t\t\t\t\\\n    malloc_mutex_prof_data_reset(tsdn, &mtx);\t\t\t\t\\\n    malloc_mutex_unlock(tsdn, &mtx);\n\n\t/* Global mutexes: ctl and prof. */\n\tMUTEX_PROF_RESET(ctl_mtx);\n\tif (have_background_thread) {\n\t\tMUTEX_PROF_RESET(background_thread_lock);\n\t}\n\tif (config_prof && opt_prof) {\n\t\tMUTEX_PROF_RESET(bt2gctx_mtx);\n\t}\n\n\n\t/* Per arena mutexes. */\n\tunsigned n = narenas_total_get();\n\n\tfor (unsigned i = 0; i < n; i++) {\n\t\tarena_t *arena = arena_get(tsdn, i, false);\n\t\tif (!arena) {\n\t\t\tcontinue;\n\t\t}\n\t\tMUTEX_PROF_RESET(arena->large_mtx);\n\t\tMUTEX_PROF_RESET(arena->extent_avail_mtx);\n\t\tMUTEX_PROF_RESET(arena->extents_dirty.mtx);\n\t\tMUTEX_PROF_RESET(arena->extents_muzzy.mtx);\n\t\tMUTEX_PROF_RESET(arena->extents_retained.mtx);\n\t\tMUTEX_PROF_RESET(arena->decay_dirty.mtx);\n\t\tMUTEX_PROF_RESET(arena->decay_muzzy.mtx);\n\t\tMUTEX_PROF_RESET(arena->tcache_ql_mtx);\n\t\tMUTEX_PROF_RESET(arena->base->mtx);\n\n\t\tfor (szind_t i = 0; i < NBINS; i++) {\n\t\t\tbin_t *bin = &arena->bins[i];\n\t\t\tMUTEX_PROF_RESET(bin->lock);\n\t\t}\n\t}\n#undef MUTEX_PROF_RESET\n\treturn 0;\n}\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nmalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc,\n    arenas_i(mib[2])->astats->bstats[mib[4]].ndalloc, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nrequests, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].curregs, size_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nfills,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nfills, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nflushes,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nflushes, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nslabs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].nslabs, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreslabs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].reslabs, uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curslabs,\n    arenas_i(mib[2])->astats->bstats[mib[4]].curslabs, size_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_bins_j_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,\n    size_t j) {\n\tif (j > NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_stats_arenas_i_bins_j_node;\n}\n\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nmalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->lstats[mib[4]].nmalloc), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_ndalloc,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->lstats[mib[4]].ndalloc), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nrequests,\n    ctl_arena_stats_read_u64(\n    &arenas_i(mib[2])->astats->lstats[mib[4]].nrequests), uint64_t)\nCTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_curlextents,\n    arenas_i(mib[2])->astats->lstats[mib[4]].curlextents, size_t)\n\nstatic const ctl_named_node_t *\nstats_arenas_i_lextents_j_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,\n    size_t j) {\n\tif (j > NSIZES - NBINS) {\n\t\treturn NULL;\n\t}\n\treturn super_stats_arenas_i_lextents_j_node;\n}\n\nstatic const ctl_named_node_t *\nstats_arenas_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) {\n\tconst ctl_named_node_t *ret;\n\tsize_t a;\n\n\tmalloc_mutex_lock(tsdn, &ctl_mtx);\n\ta = arenas_i2a_impl(i, true, true);\n\tif (a == UINT_MAX || !ctl_arenas->arenas[a]->initialized) {\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(tsdn, &ctl_mtx);\n\treturn ret;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/div.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n#include \"jemalloc/internal/div.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n/*\n * Suppose we have n = q * d, all integers. We know n and d, and want q = n / d.\n *\n * For any k, we have (here, all division is exact; not C-style rounding):\n * floor(ceil(2^k / d) * n / 2^k) = floor((2^k + r) / d * n / 2^k), where\n * r = (-2^k) mod d.\n *\n * Expanding this out:\n * ... = floor(2^k / d * n / 2^k + r / d * n / 2^k)\n *     = floor(n / d + (r / d) * (n / 2^k)).\n *\n * The fractional part of n / d is 0 (because of the assumption that d divides n\n * exactly), so we have:\n * ... = n / d + floor((r / d) * (n / 2^k))\n *\n * So that our initial expression is equal to the quantity we seek, so long as\n * (r / d) * (n / 2^k) < 1.\n *\n * r is a remainder mod d, so r < d and r / d < 1 always. We can make\n * n / 2 ^ k < 1 by setting k = 32. This gets us a value of magic that works.\n */\n\nvoid\ndiv_init(div_info_t *div_info, size_t d) {\n\t/* Nonsensical. */\n\tassert(d != 0);\n\t/*\n\t * This would make the value of magic too high to fit into a uint32_t\n\t * (we would want magic = 2^32 exactly). This would mess with code gen\n\t * on 32-bit machines.\n\t */\n\tassert(d != 1);\n\n\tuint64_t two_to_k = ((uint64_t)1 << 32);\n\tuint32_t magic = (uint32_t)(two_to_k / d);\n\n\t/*\n\t * We want magic = ceil(2^k / d), but C gives us floor. We have to\n\t * increment it unless the result was exact (i.e. unless d is a power of\n\t * two).\n\t */\n\tif (two_to_k % d != 0) {\n\t\tmagic++;\n\t}\n\tdiv_info->magic = magic;\n#ifdef JEMALLOC_DEBUG\n\tdiv_info->d = d;\n#endif\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/extent.c",
    "content": "#define JEMALLOC_EXTENT_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/ph.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n\n/******************************************************************************/\n/* Data. */\n\nrtree_t\t\textents_rtree;\n/* Keyed by the address of the extent_t being protected. */\nmutex_pool_t\textent_mutex_pool;\n\nsize_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT;\n\nstatic const bitmap_info_t extents_bitmap_info =\n    BITMAP_INFO_INITIALIZER(NPSIZES+1);\n\nstatic void *extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit,\n    unsigned arena_ind);\nstatic bool extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic void extent_destroy_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic bool extent_commit_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool extent_commit_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained);\nstatic bool extent_decommit_default(extent_hooks_t *extent_hooks,\n    void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);\n#ifdef PAGES_CAN_PURGE_LAZY\nstatic bool extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\n#endif\nstatic bool extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained);\n#ifdef PAGES_CAN_PURGE_FORCED\nstatic bool extent_purge_forced_default(extent_hooks_t *extent_hooks,\n    void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);\n#endif\nstatic bool extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained);\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool extent_split_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t size_a, size_t size_b, bool committed,\n    unsigned arena_ind);\n#endif\nstatic extent_t *extent_split_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,\n    bool growing_retained);\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a,\n    size_t size_a, void *addr_b, size_t size_b, bool committed,\n    unsigned arena_ind);\n#endif\nstatic bool extent_merge_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,\n    bool growing_retained);\n\nconst extent_hooks_t\textent_hooks_default = {\n\textent_alloc_default,\n\textent_dalloc_default,\n\textent_destroy_default,\n\textent_commit_default,\n\textent_decommit_default\n#ifdef PAGES_CAN_PURGE_LAZY\n\t,\n\textent_purge_lazy_default\n#else\n\t,\n\tNULL\n#endif\n#ifdef PAGES_CAN_PURGE_FORCED\n\t,\n\textent_purge_forced_default\n#else\n\t,\n\tNULL\n#endif\n#ifdef JEMALLOC_MAPS_COALESCE\n\t,\n\textent_split_default,\n\textent_merge_default\n#endif\n};\n\n/* Used exclusively for gdump triggering. */\nstatic atomic_zu_t curpages;\nstatic atomic_zu_t highpages;\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic void extent_deregister(tsdn_t *tsdn, extent_t *extent);\nstatic extent_t *extent_recycle(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,\n    size_t usize, size_t pad, size_t alignment, bool slab, szind_t szind,\n    bool *zero, bool *commit, bool growing_retained);\nstatic extent_t *extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    extent_t *extent, bool *coalesced, bool growing_retained);\nstatic void extent_record(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent,\n    bool growing_retained);\n\n/******************************************************************************/\n\nph_gen(UNUSED, extent_avail_, extent_tree_t, extent_t, ph_link,\n    extent_esnead_comp)\n\ntypedef enum {\n\tlock_result_success,\n\tlock_result_failure,\n\tlock_result_no_extent\n} lock_result_t;\n\nstatic lock_result_t\nextent_rtree_leaf_elm_try_lock(tsdn_t *tsdn, rtree_leaf_elm_t *elm,\n    extent_t **result) {\n\textent_t *extent1 = rtree_leaf_elm_extent_read(tsdn, &extents_rtree,\n\t    elm, true);\n\n\tif (extent1 == NULL) {\n\t\treturn lock_result_no_extent;\n\t}\n\t/*\n\t * It's possible that the extent changed out from under us, and with it\n\t * the leaf->extent mapping.  We have to recheck while holding the lock.\n\t */\n\textent_lock(tsdn, extent1);\n\textent_t *extent2 = rtree_leaf_elm_extent_read(tsdn,\n\t    &extents_rtree, elm, true);\n\n\tif (extent1 == extent2) {\n\t\t*result = extent1;\n\t\treturn lock_result_success;\n\t} else {\n\t\textent_unlock(tsdn, extent1);\n\t\treturn lock_result_failure;\n\t}\n}\n\n/*\n * Returns a pool-locked extent_t * if there's one associated with the given\n * address, and NULL otherwise.\n */\nstatic extent_t *\nextent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr) {\n\textent_t *ret = NULL;\n\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &extents_rtree,\n\t    rtree_ctx, (uintptr_t)addr, false, false);\n\tif (elm == NULL) {\n\t\treturn NULL;\n\t}\n\tlock_result_t lock_result;\n\tdo {\n\t\tlock_result = extent_rtree_leaf_elm_try_lock(tsdn, elm, &ret);\n\t} while (lock_result == lock_result_failure);\n\treturn ret;\n}\n\nextent_t *\nextent_alloc(tsdn_t *tsdn, arena_t *arena) {\n\tmalloc_mutex_lock(tsdn, &arena->extent_avail_mtx);\n\textent_t *extent = extent_avail_first(&arena->extent_avail);\n\tif (extent == NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);\n\t\treturn base_alloc_extent(tsdn, arena->base);\n\t}\n\textent_avail_remove(&arena->extent_avail, extent);\n\tmalloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);\n\treturn extent;\n}\n\nvoid\nextent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\tmalloc_mutex_lock(tsdn, &arena->extent_avail_mtx);\n\textent_avail_insert(&arena->extent_avail, extent);\n\tmalloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);\n}\n\nextent_hooks_t *\nextent_hooks_get(arena_t *arena) {\n\treturn base_extent_hooks_get(arena->base);\n}\n\nextent_hooks_t *\nextent_hooks_set(tsd_t *tsd, arena_t *arena, extent_hooks_t *extent_hooks) {\n\tbackground_thread_info_t *info;\n\tif (have_background_thread) {\n\t\tinfo = arena_background_thread_info_get(arena);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t}\n\textent_hooks_t *ret = base_extent_hooks_set(arena->base, extent_hooks);\n\tif (have_background_thread) {\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t}\n\n\treturn ret;\n}\n\nstatic void\nextent_hooks_assure_initialized(arena_t *arena,\n    extent_hooks_t **r_extent_hooks) {\n\tif (*r_extent_hooks == EXTENT_HOOKS_INITIALIZER) {\n\t\t*r_extent_hooks = extent_hooks_get(arena);\n\t}\n}\n\n#ifndef JEMALLOC_JET\nstatic\n#endif\nsize_t\nextent_size_quantize_floor(size_t size) {\n\tsize_t ret;\n\tpszind_t pind;\n\n\tassert(size > 0);\n\tassert((size & PAGE_MASK) == 0);\n\n\tpind = sz_psz2ind(size - sz_large_pad + 1);\n\tif (pind == 0) {\n\t\t/*\n\t\t * Avoid underflow.  This short-circuit would also do the right\n\t\t * thing for all sizes in the range for which there are\n\t\t * PAGE-spaced size classes, but it's simplest to just handle\n\t\t * the one case that would cause erroneous results.\n\t\t */\n\t\treturn size;\n\t}\n\tret = sz_pind2sz(pind - 1) + sz_large_pad;\n\tassert(ret <= size);\n\treturn ret;\n}\n\n#ifndef JEMALLOC_JET\nstatic\n#endif\nsize_t\nextent_size_quantize_ceil(size_t size) {\n\tsize_t ret;\n\n\tassert(size > 0);\n\tassert(size - sz_large_pad <= LARGE_MAXCLASS);\n\tassert((size & PAGE_MASK) == 0);\n\n\tret = extent_size_quantize_floor(size);\n\tif (ret < size) {\n\t\t/*\n\t\t * Skip a quantization that may have an adequately large extent,\n\t\t * because under-sized extents may be mixed in.  This only\n\t\t * happens when an unusual size is requested, i.e. for aligned\n\t\t * allocation, and is just one of several places where linear\n\t\t * search would potentially find sufficiently aligned available\n\t\t * memory somewhere lower.\n\t\t */\n\t\tret = sz_pind2sz(sz_psz2ind(ret - sz_large_pad + 1)) +\n\t\t    sz_large_pad;\n\t}\n\treturn ret;\n}\n\n/* Generate pairing heap functions. */\nph_gen(, extent_heap_, extent_heap_t, extent_t, ph_link, extent_snad_comp)\n\nbool\nextents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,\n    bool delay_coalesce) {\n\tif (malloc_mutex_init(&extents->mtx, \"extents\", WITNESS_RANK_EXTENTS,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tfor (unsigned i = 0; i < NPSIZES+1; i++) {\n\t\textent_heap_new(&extents->heaps[i]);\n\t}\n\tbitmap_init(extents->bitmap, &extents_bitmap_info, true);\n\textent_list_init(&extents->lru);\n\tatomic_store_zu(&extents->npages, 0, ATOMIC_RELAXED);\n\textents->state = state;\n\textents->delay_coalesce = delay_coalesce;\n\treturn false;\n}\n\nextent_state_t\nextents_state_get(const extents_t *extents) {\n\treturn extents->state;\n}\n\nsize_t\nextents_npages_get(extents_t *extents) {\n\treturn atomic_load_zu(&extents->npages, ATOMIC_RELAXED);\n}\n\nstatic void\nextents_insert_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {\n\tmalloc_mutex_assert_owner(tsdn, &extents->mtx);\n\tassert(extent_state_get(extent) == extents->state);\n\n\tsize_t size = extent_size_get(extent);\n\tsize_t psz = extent_size_quantize_floor(size);\n\tpszind_t pind = sz_psz2ind(psz);\n\tif (extent_heap_empty(&extents->heaps[pind])) {\n\t\tbitmap_unset(extents->bitmap, &extents_bitmap_info,\n\t\t    (size_t)pind);\n\t}\n\textent_heap_insert(&extents->heaps[pind], extent);\n\textent_list_append(&extents->lru, extent);\n\tsize_t npages = size >> LG_PAGE;\n\t/*\n\t * All modifications to npages hold the mutex (as asserted above), so we\n\t * don't need an atomic fetch-add; we can get by with a load followed by\n\t * a store.\n\t */\n\tsize_t cur_extents_npages =\n\t    atomic_load_zu(&extents->npages, ATOMIC_RELAXED);\n\tatomic_store_zu(&extents->npages, cur_extents_npages + npages,\n\t    ATOMIC_RELAXED);\n}\n\nstatic void\nextents_remove_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {\n\tmalloc_mutex_assert_owner(tsdn, &extents->mtx);\n\tassert(extent_state_get(extent) == extents->state);\n\n\tsize_t size = extent_size_get(extent);\n\tsize_t psz = extent_size_quantize_floor(size);\n\tpszind_t pind = sz_psz2ind(psz);\n\textent_heap_remove(&extents->heaps[pind], extent);\n\tif (extent_heap_empty(&extents->heaps[pind])) {\n\t\tbitmap_set(extents->bitmap, &extents_bitmap_info,\n\t\t    (size_t)pind);\n\t}\n\textent_list_remove(&extents->lru, extent);\n\tsize_t npages = size >> LG_PAGE;\n\t/*\n\t * As in extents_insert_locked, we hold extents->mtx and so don't need\n\t * atomic operations for updating extents->npages.\n\t */\n\tsize_t cur_extents_npages =\n\t    atomic_load_zu(&extents->npages, ATOMIC_RELAXED);\n\tassert(cur_extents_npages >= npages);\n\tatomic_store_zu(&extents->npages,\n\t    cur_extents_npages - (size >> LG_PAGE), ATOMIC_RELAXED);\n}\n\n/*\n * Find an extent with size [min_size, max_size) to satisfy the alignment\n * requirement.  For each size, try only the first extent in the heap.\n */\nstatic extent_t *\nextents_fit_alignment(extents_t *extents, size_t min_size, size_t max_size,\n    size_t alignment) {\n        pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(min_size));\n        pszind_t pind_max = sz_psz2ind(extent_size_quantize_ceil(max_size));\n\n\tfor (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,\n\t    &extents_bitmap_info, (size_t)pind); i < pind_max; i =\n\t    (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,\n\t    (size_t)i+1)) {\n\t\tassert(i < NPSIZES);\n\t\tassert(!extent_heap_empty(&extents->heaps[i]));\n\t\textent_t *extent = extent_heap_first(&extents->heaps[i]);\n\t\tuintptr_t base = (uintptr_t)extent_base_get(extent);\n\t\tsize_t candidate_size = extent_size_get(extent);\n\t\tassert(candidate_size >= min_size);\n\n\t\tuintptr_t next_align = ALIGNMENT_CEILING((uintptr_t)base,\n\t\t    PAGE_CEILING(alignment));\n\t\tif (base > next_align || base + candidate_size <= next_align) {\n\t\t\t/* Overflow or not crossing the next alignment. */\n\t\t\tcontinue;\n\t\t}\n\n\t\tsize_t leadsize = next_align - base;\n\t\tif (candidate_size - leadsize >= min_size) {\n\t\t\treturn extent;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n/* Do any-best-fit extent selection, i.e. select any extent that best fits. */\nstatic extent_t *\nextents_best_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    size_t size) {\n\tpszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));\n\tpszind_t i = (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,\n\t    (size_t)pind);\n\tif (i < NPSIZES+1) {\n\t\t/*\n\t\t * In order to reduce fragmentation, avoid reusing and splitting\n\t\t * large extents for much smaller sizes.\n\t\t */\n\t\tif ((sz_pind2sz(i) >> opt_lg_extent_max_active_fit) > size) {\n\t\t\treturn NULL;\n\t\t}\n\t\tassert(!extent_heap_empty(&extents->heaps[i]));\n\t\textent_t *extent = extent_heap_first(&extents->heaps[i]);\n\t\tassert(extent_size_get(extent) >= size);\n\t\treturn extent;\n\t}\n\n\treturn NULL;\n}\n\n/*\n * Do first-fit extent selection, i.e. select the oldest/lowest extent that is\n * large enough.\n */\nstatic extent_t *\nextents_first_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    size_t size) {\n\textent_t *ret = NULL;\n\n\tpszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));\n\tfor (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,\n\t    &extents_bitmap_info, (size_t)pind); i < NPSIZES+1; i =\n\t    (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,\n\t    (size_t)i+1)) {\n\t\tassert(!extent_heap_empty(&extents->heaps[i]));\n\t\textent_t *extent = extent_heap_first(&extents->heaps[i]);\n\t\tassert(extent_size_get(extent) >= size);\n\t\tif (ret == NULL || extent_snad_comp(extent, ret) < 0) {\n\t\t\tret = extent;\n\t\t}\n\t\tif (i == NPSIZES) {\n\t\t\tbreak;\n\t\t}\n\t\tassert(i < NPSIZES);\n\t}\n\n\treturn ret;\n}\n\n/*\n * Do {best,first}-fit extent selection, where the selection policy choice is\n * based on extents->delay_coalesce.  Best-fit selection requires less\n * searching, but its layout policy is less stable and may cause higher virtual\n * memory fragmentation as a side effect.\n */\nstatic extent_t *\nextents_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    size_t esize, size_t alignment) {\n\tmalloc_mutex_assert_owner(tsdn, &extents->mtx);\n\n\tsize_t max_size = esize + PAGE_CEILING(alignment) - PAGE;\n\t/* Beware size_t wrap-around. */\n\tif (max_size < esize) {\n\t\treturn NULL;\n\t}\n\n\textent_t *extent = extents->delay_coalesce ?\n\t    extents_best_fit_locked(tsdn, arena, extents, max_size) :\n\t    extents_first_fit_locked(tsdn, arena, extents, max_size);\n\n\tif (alignment > PAGE && extent == NULL) {\n\t\t/*\n\t\t * max_size guarantees the alignment requirement but is rather\n\t\t * pessimistic.  Next we try to satisfy the aligned allocation\n\t\t * with sizes in [esize, max_size).\n\t\t */\n\t\textent = extents_fit_alignment(extents, esize, max_size,\n\t\t    alignment);\n\t}\n\n\treturn extent;\n}\n\nstatic bool\nextent_try_delayed_coalesce(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    extent_t *extent) {\n\textent_state_set(extent, extent_state_active);\n\tbool coalesced;\n\textent = extent_try_coalesce(tsdn, arena, r_extent_hooks, rtree_ctx,\n\t    extents, extent, &coalesced, false);\n\textent_state_set(extent, extents_state_get(extents));\n\n\tif (!coalesced) {\n\t\treturn true;\n\t}\n\textents_insert_locked(tsdn, extents, extent);\n\treturn false;\n}\n\nextent_t *\nextents_alloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\tassert(size + pad != 0);\n\tassert(alignment != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks, extents,\n\t    new_addr, size, pad, alignment, slab, szind, zero, commit, false);\n\tassert(extent == NULL || extent_dumpable_get(extent));\n\treturn extent;\n}\n\nvoid\nextents_dalloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *extent) {\n\tassert(extent_base_get(extent) != NULL);\n\tassert(extent_size_get(extent) != 0);\n\tassert(extent_dumpable_get(extent));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_addr_set(extent, extent_base_get(extent));\n\textent_zeroed_set(extent, false);\n\n\textent_record(tsdn, arena, r_extent_hooks, extents, extent, false);\n}\n\nextent_t *\nextents_evict(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, size_t npages_min) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\n\t/*\n\t * Get the LRU coalesced extent, if any.  If coalescing was delayed,\n\t * the loop will iterate until the LRU extent is fully coalesced.\n\t */\n\textent_t *extent;\n\twhile (true) {\n\t\t/* Get the LRU extent, if any. */\n\t\textent = extent_list_first(&extents->lru);\n\t\tif (extent == NULL) {\n\t\t\tgoto label_return;\n\t\t}\n\t\t/* Check the eviction limit. */\n\t\tsize_t extents_npages = atomic_load_zu(&extents->npages,\n\t\t    ATOMIC_RELAXED);\n\t\tif (extents_npages <= npages_min) {\n\t\t\textent = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\textents_remove_locked(tsdn, extents, extent);\n\t\tif (!extents->delay_coalesce) {\n\t\t\tbreak;\n\t\t}\n\t\t/* Try to coalesce. */\n\t\tif (extent_try_delayed_coalesce(tsdn, arena, r_extent_hooks,\n\t\t    rtree_ctx, extents, extent)) {\n\t\t\tbreak;\n\t\t}\n\t\t/*\n\t\t * The LRU extent was just coalesced and the result placed in\n\t\t * the LRU at its neighbor's position.  Start over.\n\t\t */\n\t}\n\n\t/*\n\t * Either mark the extent active or deregister it to protect against\n\t * concurrent operations.\n\t */\n\tswitch (extents_state_get(extents)) {\n\tcase extent_state_active:\n\t\tnot_reached();\n\tcase extent_state_dirty:\n\tcase extent_state_muzzy:\n\t\textent_state_set(extent, extent_state_active);\n\t\tbreak;\n\tcase extent_state_retained:\n\t\textent_deregister(tsdn, extent);\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n\nlabel_return:\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\treturn extent;\n}\n\nstatic void\nextents_leak(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *extent, bool growing_retained) {\n\t/*\n\t * Leak extent after making sure its pages have already been purged, so\n\t * that this is only a virtual memory leak.\n\t */\n\tif (extents_state_get(extents) == extent_state_dirty) {\n\t\tif (extent_purge_lazy_impl(tsdn, arena, r_extent_hooks,\n\t\t    extent, 0, extent_size_get(extent), growing_retained)) {\n\t\t\textent_purge_forced_impl(tsdn, arena, r_extent_hooks,\n\t\t\t    extent, 0, extent_size_get(extent),\n\t\t\t    growing_retained);\n\t\t}\n\t}\n\textent_dalloc(tsdn, arena, extent);\n}\n\nvoid\nextents_prefork(tsdn_t *tsdn, extents_t *extents) {\n\tmalloc_mutex_prefork(tsdn, &extents->mtx);\n}\n\nvoid\nextents_postfork_parent(tsdn_t *tsdn, extents_t *extents) {\n\tmalloc_mutex_postfork_parent(tsdn, &extents->mtx);\n}\n\nvoid\nextents_postfork_child(tsdn_t *tsdn, extents_t *extents) {\n\tmalloc_mutex_postfork_child(tsdn, &extents->mtx);\n}\n\nstatic void\nextent_deactivate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    extent_t *extent) {\n\tassert(extent_arena_get(extent) == arena);\n\tassert(extent_state_get(extent) == extent_state_active);\n\n\textent_state_set(extent, extents_state_get(extents));\n\textents_insert_locked(tsdn, extents, extent);\n}\n\nstatic void\nextent_deactivate(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    extent_t *extent) {\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\textent_deactivate_locked(tsdn, arena, extents, extent);\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n}\n\nstatic void\nextent_activate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,\n    extent_t *extent) {\n\tassert(extent_arena_get(extent) == arena);\n\tassert(extent_state_get(extent) == extents_state_get(extents));\n\n\textents_remove_locked(tsdn, extents, extent);\n\textent_state_set(extent, extent_state_active);\n}\n\nstatic bool\nextent_rtree_leaf_elms_lookup(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx,\n    const extent_t *extent, bool dependent, bool init_missing,\n    rtree_leaf_elm_t **r_elm_a, rtree_leaf_elm_t **r_elm_b) {\n\t*r_elm_a = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_base_get(extent), dependent, init_missing);\n\tif (!dependent && *r_elm_a == NULL) {\n\t\treturn true;\n\t}\n\tassert(*r_elm_a != NULL);\n\n\t*r_elm_b = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_last_get(extent), dependent, init_missing);\n\tif (!dependent && *r_elm_b == NULL) {\n\t\treturn true;\n\t}\n\tassert(*r_elm_b != NULL);\n\n\treturn false;\n}\n\nstatic void\nextent_rtree_write_acquired(tsdn_t *tsdn, rtree_leaf_elm_t *elm_a,\n    rtree_leaf_elm_t *elm_b, extent_t *extent, szind_t szind, bool slab) {\n\trtree_leaf_elm_write(tsdn, &extents_rtree, elm_a, extent, szind, slab);\n\tif (elm_b != NULL) {\n\t\trtree_leaf_elm_write(tsdn, &extents_rtree, elm_b, extent, szind,\n\t\t    slab);\n\t}\n}\n\nstatic void\nextent_interior_register(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, extent_t *extent,\n    szind_t szind) {\n\tassert(extent_slab_get(extent));\n\n\t/* Register interior. */\n\tfor (size_t i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) {\n\t\trtree_write(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)extent_base_get(extent) + (uintptr_t)(i <<\n\t\t    LG_PAGE), extent, szind, true);\n\t}\n}\n\nstatic void\nextent_gdump_add(tsdn_t *tsdn, const extent_t *extent) {\n\tcassert(config_prof);\n\t/* prof_gdump() requirement. */\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (opt_prof && extent_state_get(extent) == extent_state_active) {\n\t\tsize_t nadd = extent_size_get(extent) >> LG_PAGE;\n\t\tsize_t cur = atomic_fetch_add_zu(&curpages, nadd,\n\t\t    ATOMIC_RELAXED) + nadd;\n\t\tsize_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED);\n\t\twhile (cur > high && !atomic_compare_exchange_weak_zu(\n\t\t    &highpages, &high, cur, ATOMIC_RELAXED, ATOMIC_RELAXED)) {\n\t\t\t/*\n\t\t\t * Don't refresh cur, because it may have decreased\n\t\t\t * since this thread lost the highpages update race.\n\t\t\t * Note that high is updated in case of CAS failure.\n\t\t\t */\n\t\t}\n\t\tif (cur > high && prof_gdump_get_unlocked()) {\n\t\t\tprof_gdump(tsdn);\n\t\t}\n\t}\n}\n\nstatic void\nextent_gdump_sub(tsdn_t *tsdn, const extent_t *extent) {\n\tcassert(config_prof);\n\n\tif (opt_prof && extent_state_get(extent) == extent_state_active) {\n\t\tsize_t nsub = extent_size_get(extent) >> LG_PAGE;\n\t\tassert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub);\n\t\tatomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED);\n\t}\n}\n\nstatic bool\nextent_register_impl(tsdn_t *tsdn, extent_t *extent, bool gdump_add) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *elm_a, *elm_b;\n\n\t/*\n\t * We need to hold the lock to protect against a concurrent coalesce\n\t * operation that sees us in a partial state.\n\t */\n\textent_lock(tsdn, extent);\n\n\tif (extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, false, true,\n\t    &elm_a, &elm_b)) {\n\t\treturn true;\n\t}\n\n\tszind_t szind = extent_szind_get_maybe_invalid(extent);\n\tbool slab = extent_slab_get(extent);\n\textent_rtree_write_acquired(tsdn, elm_a, elm_b, extent, szind, slab);\n\tif (slab) {\n\t\textent_interior_register(tsdn, rtree_ctx, extent, szind);\n\t}\n\n\textent_unlock(tsdn, extent);\n\n\tif (config_prof && gdump_add) {\n\t\textent_gdump_add(tsdn, extent);\n\t}\n\n\treturn false;\n}\n\nstatic bool\nextent_register(tsdn_t *tsdn, extent_t *extent) {\n\treturn extent_register_impl(tsdn, extent, true);\n}\n\nstatic bool\nextent_register_no_gdump_add(tsdn_t *tsdn, extent_t *extent) {\n\treturn extent_register_impl(tsdn, extent, false);\n}\n\nstatic void\nextent_reregister(tsdn_t *tsdn, extent_t *extent) {\n\tbool err = extent_register(tsdn, extent);\n\tassert(!err);\n}\n\n/*\n * Removes all pointers to the given extent from the global rtree indices for\n * its interior.  This is relevant for slab extents, for which we need to do\n * metadata lookups at places other than the head of the extent.  We deregister\n * on the interior, then, when an extent moves from being an active slab to an\n * inactive state.\n */\nstatic void\nextent_interior_deregister(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx,\n    extent_t *extent) {\n\tsize_t i;\n\n\tassert(extent_slab_get(extent));\n\n\tfor (i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) {\n\t\trtree_clear(tsdn, &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)extent_base_get(extent) + (uintptr_t)(i <<\n\t\t    LG_PAGE));\n\t}\n}\n\n/*\n * Removes all pointers to the given extent from the global rtree.\n */\nstatic void\nextent_deregister_impl(tsdn_t *tsdn, extent_t *extent, bool gdump) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *elm_a, *elm_b;\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, true, false,\n\t    &elm_a, &elm_b);\n\n\textent_lock(tsdn, extent);\n\n\textent_rtree_write_acquired(tsdn, elm_a, elm_b, NULL, NSIZES, false);\n\tif (extent_slab_get(extent)) {\n\t\textent_interior_deregister(tsdn, rtree_ctx, extent);\n\t\textent_slab_set(extent, false);\n\t}\n\n\textent_unlock(tsdn, extent);\n\n\tif (config_prof && gdump) {\n\t\textent_gdump_sub(tsdn, extent);\n\t}\n}\n\nstatic void\nextent_deregister(tsdn_t *tsdn, extent_t *extent) {\n\textent_deregister_impl(tsdn, extent, true);\n}\n\nstatic void\nextent_deregister_no_gdump_sub(tsdn_t *tsdn, extent_t *extent) {\n\textent_deregister_impl(tsdn, extent, false);\n}\n\n/*\n * Tries to find and remove an extent from extents that can be used for the\n * given allocation request.\n */\nstatic extent_t *\nextent_recycle_extract(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,\n    bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\tassert(alignment > 0);\n\tif (config_debug && new_addr != NULL) {\n\t\t/*\n\t\t * Non-NULL new_addr has two use cases:\n\t\t *\n\t\t *   1) Recycle a known-extant extent, e.g. during purging.\n\t\t *   2) Perform in-place expanding reallocation.\n\t\t *\n\t\t * Regardless of use case, new_addr must either refer to a\n\t\t * non-existing extent, or to the base of an extant extent,\n\t\t * since only active slabs support interior lookups (which of\n\t\t * course cannot be recycled).\n\t\t */\n\t\tassert(PAGE_ADDR2BASE(new_addr) == new_addr);\n\t\tassert(pad == 0);\n\t\tassert(alignment <= PAGE);\n\t}\n\n\tsize_t esize = size + pad;\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\textent_t *extent;\n\tif (new_addr != NULL) {\n\t\textent = extent_lock_from_addr(tsdn, rtree_ctx, new_addr);\n\t\tif (extent != NULL) {\n\t\t\t/*\n\t\t\t * We might null-out extent to report an error, but we\n\t\t\t * still need to unlock the associated mutex after.\n\t\t\t */\n\t\t\textent_t *unlock_extent = extent;\n\t\t\tassert(extent_base_get(extent) == new_addr);\n\t\t\tif (extent_arena_get(extent) != arena ||\n\t\t\t    extent_size_get(extent) < esize ||\n\t\t\t    extent_state_get(extent) !=\n\t\t\t    extents_state_get(extents)) {\n\t\t\t\textent = NULL;\n\t\t\t}\n\t\t\textent_unlock(tsdn, unlock_extent);\n\t\t}\n\t} else {\n\t\textent = extents_fit_locked(tsdn, arena, extents, esize,\n\t\t    alignment);\n\t}\n\tif (extent == NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\t\treturn NULL;\n\t}\n\n\textent_activate_locked(tsdn, arena, extents, extent);\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\n\treturn extent;\n}\n\n/*\n * Given an allocation request and an extent guaranteed to be able to satisfy\n * it, this splits off lead and trail extents, leaving extent pointing to an\n * extent satisfying the allocation.\n * This function doesn't put lead or trail into any extents_t; it's the caller's\n * job to ensure that they can be reused.\n */\ntypedef enum {\n\t/*\n\t * Split successfully.  lead, extent, and trail, are modified to extents\n\t * describing the ranges before, in, and after the given allocation.\n\t */\n\textent_split_interior_ok,\n\t/*\n\t * The extent can't satisfy the given allocation request.  None of the\n\t * input extent_t *s are touched.\n\t */\n\textent_split_interior_cant_alloc,\n\t/*\n\t * In a potentially invalid state.  Must leak (if *to_leak is non-NULL),\n\t * and salvage what's still salvageable (if *to_salvage is non-NULL).\n\t * None of lead, extent, or trail are valid.\n\t */\n\textent_split_interior_error\n} extent_split_interior_result_t;\n\nstatic extent_split_interior_result_t\nextent_split_interior(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx,\n    /* The result of splitting, in case of success. */\n    extent_t **extent, extent_t **lead, extent_t **trail,\n    /* The mess to clean up, in case of error. */\n    extent_t **to_leak, extent_t **to_salvage,\n    void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,\n    szind_t szind, bool growing_retained) {\n\tsize_t esize = size + pad;\n\tsize_t leadsize = ALIGNMENT_CEILING((uintptr_t)extent_base_get(*extent),\n\t    PAGE_CEILING(alignment)) - (uintptr_t)extent_base_get(*extent);\n\tassert(new_addr == NULL || leadsize == 0);\n\tif (extent_size_get(*extent) < leadsize + esize) {\n\t\treturn extent_split_interior_cant_alloc;\n\t}\n\tsize_t trailsize = extent_size_get(*extent) - leadsize - esize;\n\n\t*lead = NULL;\n\t*trail = NULL;\n\t*to_leak = NULL;\n\t*to_salvage = NULL;\n\n\t/* Split the lead. */\n\tif (leadsize != 0) {\n\t\t*lead = *extent;\n\t\t*extent = extent_split_impl(tsdn, arena, r_extent_hooks,\n\t\t    *lead, leadsize, NSIZES, false, esize + trailsize, szind,\n\t\t    slab, growing_retained);\n\t\tif (*extent == NULL) {\n\t\t\t*to_leak = *lead;\n\t\t\t*lead = NULL;\n\t\t\treturn extent_split_interior_error;\n\t\t}\n\t}\n\n\t/* Split the trail. */\n\tif (trailsize != 0) {\n\t\t*trail = extent_split_impl(tsdn, arena, r_extent_hooks, *extent,\n\t\t    esize, szind, slab, trailsize, NSIZES, false,\n\t\t    growing_retained);\n\t\tif (*trail == NULL) {\n\t\t\t*to_leak = *extent;\n\t\t\t*to_salvage = *lead;\n\t\t\t*lead = NULL;\n\t\t\t*extent = NULL;\n\t\t\treturn extent_split_interior_error;\n\t\t}\n\t}\n\n\tif (leadsize == 0 && trailsize == 0) {\n\t\t/*\n\t\t * Splitting causes szind to be set as a side effect, but no\n\t\t * splitting occurred.\n\t\t */\n\t\textent_szind_set(*extent, szind);\n\t\tif (szind != NSIZES) {\n\t\t\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,\n\t\t\t    (uintptr_t)extent_addr_get(*extent), szind, slab);\n\t\t\tif (slab && extent_size_get(*extent) > PAGE) {\n\t\t\t\trtree_szind_slab_update(tsdn, &extents_rtree,\n\t\t\t\t    rtree_ctx,\n\t\t\t\t    (uintptr_t)extent_past_get(*extent) -\n\t\t\t\t    (uintptr_t)PAGE, szind, slab);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn extent_split_interior_ok;\n}\n\n/*\n * This fulfills the indicated allocation request out of the given extent (which\n * the caller should have ensured was big enough).  If there's any unused space\n * before or after the resulting allocation, that space is given its own extent\n * and put back into extents.\n */\nstatic extent_t *\nextent_recycle_split(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,\n    szind_t szind, extent_t *extent, bool growing_retained) {\n\textent_t *lead;\n\textent_t *trail;\n\textent_t *to_leak;\n\textent_t *to_salvage;\n\n\textent_split_interior_result_t result = extent_split_interior(\n\t    tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail,\n\t    &to_leak, &to_salvage, new_addr, size, pad, alignment, slab, szind,\n\t    growing_retained);\n\n\tif (result == extent_split_interior_ok) {\n\t\tif (lead != NULL) {\n\t\t\textent_deactivate(tsdn, arena, extents, lead);\n\t\t}\n\t\tif (trail != NULL) {\n\t\t\textent_deactivate(tsdn, arena, extents, trail);\n\t\t}\n\t\treturn extent;\n\t} else {\n\t\t/*\n\t\t * We should have picked an extent that was large enough to\n\t\t * fulfill our allocation request.\n\t\t */\n\t\tassert(result == extent_split_interior_error);\n\t\tif (to_salvage != NULL) {\n\t\t\textent_deregister(tsdn, to_salvage);\n\t\t}\n\t\tif (to_leak != NULL) {\n\t\t\tvoid *leak = extent_base_get(to_leak);\n\t\t\textent_deregister_no_gdump_sub(tsdn, to_leak);\n\t\t\textents_leak(tsdn, arena, r_extent_hooks, extents,\n\t\t\t    to_leak, growing_retained);\n\t\t\tassert(extent_lock_from_addr(tsdn, rtree_ctx, leak)\n\t\t\t    == NULL);\n\t\t}\n\t\treturn NULL;\n\t}\n\tunreachable();\n}\n\n/*\n * Tries to satisfy the given allocation request by reusing one of the extents\n * in the given extents_t.\n */\nstatic extent_t *\nextent_recycle(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit,\n    bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\tassert(new_addr == NULL || !slab);\n\tassert(pad == 0 || !slab);\n\tassert(!*zero || !slab);\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent = extent_recycle_extract(tsdn, arena, r_extent_hooks,\n\t    rtree_ctx, extents, new_addr, size, pad, alignment, slab,\n\t    growing_retained);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\n\textent = extent_recycle_split(tsdn, arena, r_extent_hooks, rtree_ctx,\n\t    extents, new_addr, size, pad, alignment, slab, szind, extent,\n\t    growing_retained);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\n\tif (*commit && !extent_committed_get(extent)) {\n\t\tif (extent_commit_impl(tsdn, arena, r_extent_hooks, extent,\n\t\t    0, extent_size_get(extent), growing_retained)) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks, extents,\n\t\t\t    extent, growing_retained);\n\t\t\treturn NULL;\n\t\t}\n\t\textent_zeroed_set(extent, true);\n\t}\n\n\tif (extent_committed_get(extent)) {\n\t\t*commit = true;\n\t}\n\tif (extent_zeroed_get(extent)) {\n\t\t*zero = true;\n\t}\n\n\tif (pad != 0) {\n\t\textent_addr_randomize(tsdn, extent, alignment);\n\t}\n\tassert(extent_state_get(extent) == extent_state_active);\n\tif (slab) {\n\t\textent_slab_set(extent, slab);\n\t\textent_interior_register(tsdn, rtree_ctx, extent, szind);\n\t}\n\n\tif (*zero) {\n\t\tvoid *addr = extent_base_get(extent);\n\t\tsize_t size = extent_size_get(extent);\n\t\tif (!extent_zeroed_get(extent)) {\n\t\t\tif (pages_purge_forced(addr, size)) {\n\t\t\t\tmemset(addr, 0, size);\n\t\t\t}\n\t\t} else if (config_debug) {\n\t\t\tsize_t *p = (size_t *)(uintptr_t)addr;\n\t\t\tfor (size_t i = 0; i < size / sizeof(size_t); i++) {\n\t\t\t\tassert(p[i] == 0);\n\t\t\t}\n\t\t}\n\t}\n\treturn extent;\n}\n\n/*\n * If the caller specifies (!*zero), it is still possible to receive zeroed\n * memory, in which case *zero is toggled to true.  arena_extent_alloc() takes\n * advantage of this to avoid demanding zeroed extents, but taking advantage of\n * them if they are returned.\n */\nstatic void *\nextent_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec) {\n\tvoid *ret;\n\n\tassert(size != 0);\n\tassert(alignment != 0);\n\n\t/* \"primary\" dss. */\n\tif (have_dss && dss_prec == dss_prec_primary && (ret =\n\t    extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,\n\t    commit)) != NULL) {\n\t\treturn ret;\n\t}\n\t/* mmap. */\n\tif ((ret = extent_alloc_mmap(new_addr, size, alignment, zero, commit))\n\t    != NULL) {\n\t\treturn ret;\n\t}\n\t/* \"secondary\" dss. */\n\tif (have_dss && dss_prec == dss_prec_secondary && (ret =\n\t    extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,\n\t    commit)) != NULL) {\n\t\treturn ret;\n\t}\n\n\t/* All strategies for allocation failed. */\n\treturn NULL;\n}\n\nstatic void *\nextent_alloc_default_impl(tsdn_t *tsdn, arena_t *arena, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit) {\n\tvoid *ret = extent_alloc_core(tsdn, arena, new_addr, size, alignment, zero,\n\t    commit, (dss_prec_t)atomic_load_u(&arena->dss_prec,\n\t    ATOMIC_RELAXED));\n\tif (have_madvise_huge && ret) {\n\t\tpages_set_thp_state(ret, size);\n\t}\n\treturn ret;\n}\n\nstatic void *\nextent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {\n\ttsdn_t *tsdn;\n\tarena_t *arena;\n\n\ttsdn = tsdn_fetch();\n\tarena = arena_get(tsdn, arena_ind, false);\n\t/*\n\t * The arena we're allocating on behalf of must have been initialized\n\t * already.\n\t */\n\tassert(arena != NULL);\n\n\treturn extent_alloc_default_impl(tsdn, arena, new_addr, size,\n\t    alignment, zero, commit);\n}\n\nstatic void\nextent_hook_pre_reentrancy(tsdn_t *tsdn, arena_t *arena) {\n\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\tif (arena == arena_get(tsd_tsdn(tsd), 0, false)) {\n\t\t/*\n\t\t * The only legitimate case of customized extent hooks for a0 is\n\t\t * hooks with no allocation activities.  One such example is to\n\t\t * place metadata on pre-allocated resources such as huge pages.\n\t\t * In that case, rely on reentrancy_level checks to catch\n\t\t * infinite recursions.\n\t\t */\n\t\tpre_reentrancy(tsd, NULL);\n\t} else {\n\t\tpre_reentrancy(tsd, arena);\n\t}\n}\n\nstatic void\nextent_hook_post_reentrancy(tsdn_t *tsdn) {\n\ttsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);\n\tpost_reentrancy(tsd);\n}\n\n/*\n * If virtual memory is retained, create increasingly larger extents from which\n * to split requested extents in order to limit the total number of disjoint\n * virtual memory ranges retained by each arena.\n */\nstatic extent_t *\nextent_grow_retained(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, size_t size, size_t pad, size_t alignment,\n    bool slab, szind_t szind, bool *zero, bool *commit) {\n\tmalloc_mutex_assert_owner(tsdn, &arena->extent_grow_mtx);\n\tassert(pad == 0 || !slab);\n\tassert(!*zero || !slab);\n\n\tsize_t esize = size + pad;\n\tsize_t alloc_size_min = esize + PAGE_CEILING(alignment) - PAGE;\n\t/* Beware size_t wrap-around. */\n\tif (alloc_size_min < esize) {\n\t\tgoto label_err;\n\t}\n\t/*\n\t * Find the next extent size in the series that would be large enough to\n\t * satisfy this request.\n\t */\n\tpszind_t egn_skip = 0;\n\tsize_t alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);\n\twhile (alloc_size < alloc_size_min) {\n\t\tegn_skip++;\n\t\tif (arena->extent_grow_next + egn_skip == NPSIZES) {\n\t\t\t/* Outside legal range. */\n\t\t\tgoto label_err;\n\t\t}\n\t\tassert(arena->extent_grow_next + egn_skip < NPSIZES);\n\t\talloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);\n\t}\n\n\textent_t *extent = extent_alloc(tsdn, arena);\n\tif (extent == NULL) {\n\t\tgoto label_err;\n\t}\n\tbool zeroed = false;\n\tbool committed = false;\n\n\tvoid *ptr;\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\tptr = extent_alloc_default_impl(tsdn, arena, NULL,\n\t\t    alloc_size, PAGE, &zeroed, &committed);\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\tptr = (*r_extent_hooks)->alloc(*r_extent_hooks, NULL,\n\t\t    alloc_size, PAGE, &zeroed, &committed,\n\t\t    arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\textent_init(extent, arena, ptr, alloc_size, false, NSIZES,\n\t    arena_extent_sn_next(arena), extent_state_active, zeroed,\n\t    committed, true);\n\tif (ptr == NULL) {\n\t\textent_dalloc(tsdn, arena, extent);\n\t\tgoto label_err;\n\t}\n\n\tif (extent_register_no_gdump_add(tsdn, extent)) {\n\t\textents_leak(tsdn, arena, r_extent_hooks,\n\t\t    &arena->extents_retained, extent, true);\n\t\tgoto label_err;\n\t}\n\n\tif (extent_zeroed_get(extent) && extent_committed_get(extent)) {\n\t\t*zero = true;\n\t}\n\tif (extent_committed_get(extent)) {\n\t\t*commit = true;\n\t}\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *lead;\n\textent_t *trail;\n\textent_t *to_leak;\n\textent_t *to_salvage;\n\textent_split_interior_result_t result = extent_split_interior(\n\t    tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail,\n\t    &to_leak, &to_salvage, NULL, size, pad, alignment, slab, szind,\n\t    true);\n\n\tif (result == extent_split_interior_ok) {\n\t\tif (lead != NULL) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, lead, true);\n\t\t}\n\t\tif (trail != NULL) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, trail, true);\n\t\t}\n\t} else {\n\t\t/*\n\t\t * We should have allocated a sufficiently large extent; the\n\t\t * cant_alloc case should not occur.\n\t\t */\n\t\tassert(result == extent_split_interior_error);\n\t\tif (to_salvage != NULL) {\n\t\t\tif (config_prof) {\n\t\t\t\textent_gdump_add(tsdn, to_salvage);\n\t\t\t}\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, to_salvage, true);\n\t\t}\n\t\tif (to_leak != NULL) {\n\t\t\textent_deregister_no_gdump_sub(tsdn, to_leak);\n\t\t\textents_leak(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, to_leak, true);\n\t\t}\n\t\tgoto label_err;\n\t}\n\n\tif (*commit && !extent_committed_get(extent)) {\n\t\tif (extent_commit_impl(tsdn, arena, r_extent_hooks, extent, 0,\n\t\t    extent_size_get(extent), true)) {\n\t\t\textent_record(tsdn, arena, r_extent_hooks,\n\t\t\t    &arena->extents_retained, extent, true);\n\t\t\tgoto label_err;\n\t\t}\n\t\textent_zeroed_set(extent, true);\n\t}\n\n\t/*\n\t * Increment extent_grow_next if doing so wouldn't exceed the allowed\n\t * range.\n\t */\n\tif (arena->extent_grow_next + egn_skip + 1 <=\n\t    arena->retain_grow_limit) {\n\t\tarena->extent_grow_next += egn_skip + 1;\n\t} else {\n\t\tarena->extent_grow_next = arena->retain_grow_limit;\n\t}\n\t/* All opportunities for failure are past. */\n\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\n\tif (config_prof) {\n\t\t/* Adjust gdump stats now that extent is final size. */\n\t\textent_gdump_add(tsdn, extent);\n\t}\n\tif (pad != 0) {\n\t\textent_addr_randomize(tsdn, extent, alignment);\n\t}\n\tif (slab) {\n\t\trtree_ctx_t rtree_ctx_fallback;\n\t\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,\n\t\t    &rtree_ctx_fallback);\n\n\t\textent_slab_set(extent, true);\n\t\textent_interior_register(tsdn, rtree_ctx, extent, szind);\n\t}\n\tif (*zero && !extent_zeroed_get(extent)) {\n\t\tvoid *addr = extent_base_get(extent);\n\t\tsize_t size = extent_size_get(extent);\n\t\tif (pages_purge_forced(addr, size)) {\n\t\t\tmemset(addr, 0, size);\n\t\t}\n\t}\n\n\treturn extent;\nlabel_err:\n\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\treturn NULL;\n}\n\nstatic extent_t *\nextent_alloc_retained(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\tassert(size != 0);\n\tassert(alignment != 0);\n\n\tmalloc_mutex_lock(tsdn, &arena->extent_grow_mtx);\n\n\textent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks,\n\t    &arena->extents_retained, new_addr, size, pad, alignment, slab,\n\t    szind, zero, commit, true);\n\tif (extent != NULL) {\n\t\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\t\tif (config_prof) {\n\t\t\textent_gdump_add(tsdn, extent);\n\t\t}\n\t} else if (opt_retain && new_addr == NULL) {\n\t\textent = extent_grow_retained(tsdn, arena, r_extent_hooks, size,\n\t\t    pad, alignment, slab, szind, zero, commit);\n\t\t/* extent_grow_retained() always releases extent_grow_mtx. */\n\t} else {\n\t\tmalloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);\n\t}\n\tmalloc_mutex_assert_not_owner(tsdn, &arena->extent_grow_mtx);\n\n\treturn extent;\n}\n\nstatic extent_t *\nextent_alloc_wrapper_hard(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\tsize_t esize = size + pad;\n\textent_t *extent = extent_alloc(tsdn, arena);\n\tif (extent == NULL) {\n\t\treturn NULL;\n\t}\n\tvoid *addr;\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\taddr = extent_alloc_default_impl(tsdn, arena, new_addr, esize,\n\t\t    alignment, zero, commit);\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\taddr = (*r_extent_hooks)->alloc(*r_extent_hooks, new_addr,\n\t\t    esize, alignment, zero, commit, arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\tif (addr == NULL) {\n\t\textent_dalloc(tsdn, arena, extent);\n\t\treturn NULL;\n\t}\n\textent_init(extent, arena, addr, esize, slab, szind,\n\t    arena_extent_sn_next(arena), extent_state_active, *zero, *commit,\n\t    true);\n\tif (pad != 0) {\n\t\textent_addr_randomize(tsdn, extent, alignment);\n\t}\n\tif (extent_register(tsdn, extent)) {\n\t\textents_leak(tsdn, arena, r_extent_hooks,\n\t\t    &arena->extents_retained, extent, false);\n\t\treturn NULL;\n\t}\n\n\treturn extent;\n}\n\nextent_t *\nextent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,\n    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\textent_t *extent = extent_alloc_retained(tsdn, arena, r_extent_hooks,\n\t    new_addr, size, pad, alignment, slab, szind, zero, commit);\n\tif (extent == NULL) {\n\t\tif (opt_retain && new_addr != NULL) {\n\t\t\t/*\n\t\t\t * When retain is enabled and new_addr is set, we do not\n\t\t\t * attempt extent_alloc_wrapper_hard which does mmap\n\t\t\t * that is very unlikely to succeed (unless it happens\n\t\t\t * to be at the end).\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\t\textent = extent_alloc_wrapper_hard(tsdn, arena, r_extent_hooks,\n\t\t    new_addr, size, pad, alignment, slab, szind, zero, commit);\n\t}\n\n\tassert(extent == NULL || extent_dumpable_get(extent));\n\treturn extent;\n}\n\nstatic bool\nextent_can_coalesce(arena_t *arena, extents_t *extents, const extent_t *inner,\n    const extent_t *outer) {\n\tassert(extent_arena_get(inner) == arena);\n\tif (extent_arena_get(outer) != arena) {\n\t\treturn false;\n\t}\n\n\tassert(extent_state_get(inner) == extent_state_active);\n\tif (extent_state_get(outer) != extents->state) {\n\t\treturn false;\n\t}\n\n\tif (extent_committed_get(inner) != extent_committed_get(outer)) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic bool\nextent_coalesce(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *inner, extent_t *outer, bool forward,\n    bool growing_retained) {\n\tassert(extent_can_coalesce(arena, extents, inner, outer));\n\n\textent_activate_locked(tsdn, arena, extents, outer);\n\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n\tbool err = extent_merge_impl(tsdn, arena, r_extent_hooks,\n\t    forward ? inner : outer, forward ? outer : inner, growing_retained);\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\n\tif (err) {\n\t\textent_deactivate_locked(tsdn, arena, extents, outer);\n\t}\n\n\treturn err;\n}\n\nstatic extent_t *\nextent_try_coalesce(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,\n    extent_t *extent, bool *coalesced, bool growing_retained) {\n\t/*\n\t * Continue attempting to coalesce until failure, to protect against\n\t * races with other threads that are thwarted by this one.\n\t */\n\tbool again;\n\tdo {\n\t\tagain = false;\n\n\t\t/* Try to coalesce forward. */\n\t\textent_t *next = extent_lock_from_addr(tsdn, rtree_ctx,\n\t\t    extent_past_get(extent));\n\t\tif (next != NULL) {\n\t\t\t/*\n\t\t\t * extents->mtx only protects against races for\n\t\t\t * like-state extents, so call extent_can_coalesce()\n\t\t\t * before releasing next's pool lock.\n\t\t\t */\n\t\t\tbool can_coalesce = extent_can_coalesce(arena, extents,\n\t\t\t    extent, next);\n\n\t\t\textent_unlock(tsdn, next);\n\n\t\t\tif (can_coalesce && !extent_coalesce(tsdn, arena,\n\t\t\t    r_extent_hooks, extents, extent, next, true,\n\t\t\t    growing_retained)) {\n\t\t\t\tif (extents->delay_coalesce) {\n\t\t\t\t\t/* Do minimal coalescing. */\n\t\t\t\t\t*coalesced = true;\n\t\t\t\t\treturn extent;\n\t\t\t\t}\n\t\t\t\tagain = true;\n\t\t\t}\n\t\t}\n\n\t\t/* Try to coalesce backward. */\n\t\textent_t *prev = extent_lock_from_addr(tsdn, rtree_ctx,\n\t\t    extent_before_get(extent));\n\t\tif (prev != NULL) {\n\t\t\tbool can_coalesce = extent_can_coalesce(arena, extents,\n\t\t\t    extent, prev);\n\t\t\textent_unlock(tsdn, prev);\n\n\t\t\tif (can_coalesce && !extent_coalesce(tsdn, arena,\n\t\t\t    r_extent_hooks, extents, extent, prev, false,\n\t\t\t    growing_retained)) {\n\t\t\t\textent = prev;\n\t\t\t\tif (extents->delay_coalesce) {\n\t\t\t\t\t/* Do minimal coalescing. */\n\t\t\t\t\t*coalesced = true;\n\t\t\t\t\treturn extent;\n\t\t\t\t}\n\t\t\t\tagain = true;\n\t\t\t}\n\t\t}\n\t} while (again);\n\n\tif (extents->delay_coalesce) {\n\t\t*coalesced = false;\n\t}\n\treturn extent;\n}\n\n/*\n * Does the metadata management portions of putting an unused extent into the\n * given extents_t (coalesces, deregisters slab interiors, the heap operations).\n */\nstatic void\nextent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,\n    extents_t *extents, extent_t *extent, bool growing_retained) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\tassert((extents_state_get(extents) != extent_state_dirty &&\n\t    extents_state_get(extents) != extent_state_muzzy) ||\n\t    !extent_zeroed_get(extent));\n\n\tmalloc_mutex_lock(tsdn, &extents->mtx);\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\textent_szind_set(extent, NSIZES);\n\tif (extent_slab_get(extent)) {\n\t\textent_interior_deregister(tsdn, rtree_ctx, extent);\n\t\textent_slab_set(extent, false);\n\t}\n\n\tassert(rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_base_get(extent), true) == extent);\n\n\tif (!extents->delay_coalesce) {\n\t\textent = extent_try_coalesce(tsdn, arena, r_extent_hooks,\n\t\t    rtree_ctx, extents, extent, NULL, growing_retained);\n\t} else if (extent_size_get(extent) >= LARGE_MINCLASS) {\n\t\t/* Always coalesce large extents eagerly. */\n\t\tbool coalesced;\n\t\tsize_t prev_size;\n\t\tdo {\n\t\t\tprev_size = extent_size_get(extent);\n\t\t\tassert(extent_state_get(extent) == extent_state_active);\n\t\t\textent = extent_try_coalesce(tsdn, arena,\n\t\t\t    r_extent_hooks, rtree_ctx, extents, extent,\n\t\t\t    &coalesced, growing_retained);\n\t\t} while (coalesced &&\n\t\t    extent_size_get(extent) >= prev_size + LARGE_MINCLASS);\n\t}\n\textent_deactivate_locked(tsdn, arena, extents, extent);\n\n\tmalloc_mutex_unlock(tsdn, &extents->mtx);\n}\n\nvoid\nextent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\tif (extent_register(tsdn, extent)) {\n\t\textents_leak(tsdn, arena, &extent_hooks,\n\t\t    &arena->extents_retained, extent, false);\n\t\treturn;\n\t}\n\textent_dalloc_wrapper(tsdn, arena, &extent_hooks, extent);\n}\n\nstatic bool\nextent_dalloc_default_impl(void *addr, size_t size) {\n\tif (!have_dss || !extent_in_dss(addr)) {\n\t\treturn extent_dalloc_mmap(addr, size);\n\t}\n\treturn true;\n}\n\nstatic bool\nextent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\treturn extent_dalloc_default_impl(addr, size);\n}\n\nstatic bool\nextent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\tbool err;\n\n\tassert(extent_base_get(extent) != NULL);\n\tassert(extent_size_get(extent) != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_addr_set(extent, extent_base_get(extent));\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\t/* Try to deallocate. */\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\terr = extent_dalloc_default_impl(extent_base_get(extent),\n\t\t    extent_size_get(extent));\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\terr = ((*r_extent_hooks)->dalloc == NULL ||\n\t\t    (*r_extent_hooks)->dalloc(*r_extent_hooks,\n\t\t    extent_base_get(extent), extent_size_get(extent),\n\t\t    extent_committed_get(extent), arena_ind_get(arena)));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\tif (!err) {\n\t\textent_dalloc(tsdn, arena, extent);\n\t}\n\n\treturn err;\n}\n\nvoid\nextent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\tassert(extent_dumpable_get(extent));\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\t/*\n\t * Deregister first to avoid a race with other allocating threads, and\n\t * reregister if deallocation fails.\n\t */\n\textent_deregister(tsdn, extent);\n\tif (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks, extent)) {\n\t\treturn;\n\t}\n\n\textent_reregister(tsdn, extent);\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\t/* Try to decommit; purge if that fails. */\n\tbool zeroed;\n\tif (!extent_committed_get(extent)) {\n\t\tzeroed = true;\n\t} else if (!extent_decommit_wrapper(tsdn, arena, r_extent_hooks, extent,\n\t    0, extent_size_get(extent))) {\n\t\tzeroed = true;\n\t} else if ((*r_extent_hooks)->purge_forced != NULL &&\n\t    !(*r_extent_hooks)->purge_forced(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), 0,\n\t    extent_size_get(extent), arena_ind_get(arena))) {\n\t\tzeroed = true;\n\t} else if (extent_state_get(extent) == extent_state_muzzy ||\n\t    ((*r_extent_hooks)->purge_lazy != NULL &&\n\t    !(*r_extent_hooks)->purge_lazy(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), 0,\n\t    extent_size_get(extent), arena_ind_get(arena)))) {\n\t\tzeroed = false;\n\t} else {\n\t\tzeroed = false;\n\t}\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\textent_zeroed_set(extent, zeroed);\n\n\tif (config_prof) {\n\t\textent_gdump_sub(tsdn, extent);\n\t}\n\n\textent_record(tsdn, arena, r_extent_hooks, &arena->extents_retained,\n\t    extent, false);\n}\n\nstatic void\nextent_destroy_default_impl(void *addr, size_t size) {\n\tif (!have_dss || !extent_in_dss(addr)) {\n\t\tpages_unmap(addr, size);\n\t}\n}\n\nstatic void\nextent_destroy_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\textent_destroy_default_impl(addr, size);\n}\n\nvoid\nextent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent) {\n\tassert(extent_base_get(extent) != NULL);\n\tassert(extent_size_get(extent) != 0);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\t/* Deregister first to avoid a race with other allocating threads. */\n\textent_deregister(tsdn, extent);\n\n\textent_addr_set(extent, extent_base_get(extent));\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\t/* Try to destroy; silently fail otherwise. */\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\textent_destroy_default_impl(extent_base_get(extent),\n\t\t    extent_size_get(extent));\n\t} else if ((*r_extent_hooks)->destroy != NULL) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\t(*r_extent_hooks)->destroy(*r_extent_hooks,\n\t\t    extent_base_get(extent), extent_size_get(extent),\n\t\t    extent_committed_get(extent), arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\textent_dalloc(tsdn, arena, extent);\n}\n\nstatic bool\nextent_commit_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\treturn pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset),\n\t    length);\n}\n\nstatic bool\nextent_commit_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = ((*r_extent_hooks)->commit == NULL ||\n\t    (*r_extent_hooks)->commit(*r_extent_hooks, extent_base_get(extent),\n\t    extent_size_get(extent), offset, length, arena_ind_get(arena)));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\textent_committed_set(extent, extent_committed_get(extent) || !err);\n\treturn err;\n}\n\nbool\nextent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\treturn extent_commit_impl(tsdn, arena, r_extent_hooks, extent, offset,\n\t    length, false);\n}\n\nstatic bool\nextent_decommit_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\treturn pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset),\n\t    length);\n}\n\nbool\nextent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = ((*r_extent_hooks)->decommit == NULL ||\n\t    (*r_extent_hooks)->decommit(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), offset, length,\n\t    arena_ind_get(arena)));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\textent_committed_set(extent, extent_committed_get(extent) && err);\n\treturn err;\n}\n\n#ifdef PAGES_CAN_PURGE_LAZY\nstatic bool\nextent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tassert(addr != NULL);\n\tassert((offset & PAGE_MASK) == 0);\n\tassert(length != 0);\n\tassert((length & PAGE_MASK) == 0);\n\n\treturn pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset),\n\t    length);\n}\n#endif\n\nstatic bool\nextent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->purge_lazy == NULL) {\n\t\treturn true;\n\t}\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = (*r_extent_hooks)->purge_lazy(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), offset, length,\n\t    arena_ind_get(arena));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\treturn err;\n}\n\nbool\nextent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\treturn extent_purge_lazy_impl(tsdn, arena, r_extent_hooks, extent,\n\t    offset, length, false);\n}\n\n#ifdef PAGES_CAN_PURGE_FORCED\nstatic bool\nextent_purge_forced_default(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind) {\n\tassert(addr != NULL);\n\tassert((offset & PAGE_MASK) == 0);\n\tassert(length != 0);\n\tassert((length & PAGE_MASK) == 0);\n\n\treturn pages_purge_forced((void *)((uintptr_t)addr +\n\t    (uintptr_t)offset), length);\n}\n#endif\n\nstatic bool\nextent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length, bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->purge_forced == NULL) {\n\t\treturn true;\n\t}\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = (*r_extent_hooks)->purge_forced(*r_extent_hooks,\n\t    extent_base_get(extent), extent_size_get(extent), offset, length,\n\t    arena_ind_get(arena));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\treturn err;\n}\n\nbool\nextent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,\n    size_t length) {\n\treturn extent_purge_forced_impl(tsdn, arena, r_extent_hooks, extent,\n\t    offset, length, false);\n}\n\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool\nextent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {\n\treturn !maps_coalesce;\n}\n#endif\n\n/*\n * Accepts the extent to split, and the characteristics of each side of the\n * split.  The 'a' parameters go with the 'lead' of the resulting pair of\n * extents (the lower addressed portion of the split), and the 'b' parameters go\n * with the trail (the higher addressed portion).  This makes 'extent' the lead,\n * and returns the trail (except in case of error).\n */\nstatic extent_t *\nextent_split_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,\n    bool growing_retained) {\n\tassert(extent_size_get(extent) == size_a + size_b);\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->split == NULL) {\n\t\treturn NULL;\n\t}\n\n\textent_t *trail = extent_alloc(tsdn, arena);\n\tif (trail == NULL) {\n\t\tgoto label_error_a;\n\t}\n\n\textent_init(trail, arena, (void *)((uintptr_t)extent_base_get(extent) +\n\t    size_a), size_b, slab_b, szind_b, extent_sn_get(extent),\n\t    extent_state_get(extent), extent_zeroed_get(extent),\n\t    extent_committed_get(extent), extent_dumpable_get(extent));\n\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *lead_elm_a, *lead_elm_b;\n\t{\n\t\textent_t lead;\n\n\t\textent_init(&lead, arena, extent_addr_get(extent), size_a,\n\t\t    slab_a, szind_a, extent_sn_get(extent),\n\t\t    extent_state_get(extent), extent_zeroed_get(extent),\n\t\t    extent_committed_get(extent), extent_dumpable_get(extent));\n\n\t\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, &lead, false,\n\t\t    true, &lead_elm_a, &lead_elm_b);\n\t}\n\trtree_leaf_elm_t *trail_elm_a, *trail_elm_b;\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, trail, false, true,\n\t    &trail_elm_a, &trail_elm_b);\n\n\tif (lead_elm_a == NULL || lead_elm_b == NULL || trail_elm_a == NULL\n\t    || trail_elm_b == NULL) {\n\t\tgoto label_error_b;\n\t}\n\n\textent_lock2(tsdn, extent, trail);\n\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t}\n\tbool err = (*r_extent_hooks)->split(*r_extent_hooks, extent_base_get(extent),\n\t    size_a + size_b, size_a, size_b, extent_committed_get(extent),\n\t    arena_ind_get(arena));\n\tif (*r_extent_hooks != &extent_hooks_default) {\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\tif (err) {\n\t\tgoto label_error_c;\n\t}\n\n\textent_size_set(extent, size_a);\n\textent_szind_set(extent, szind_a);\n\n\textent_rtree_write_acquired(tsdn, lead_elm_a, lead_elm_b, extent,\n\t    szind_a, slab_a);\n\textent_rtree_write_acquired(tsdn, trail_elm_a, trail_elm_b, trail,\n\t    szind_b, slab_b);\n\n\textent_unlock2(tsdn, extent, trail);\n\n\treturn trail;\nlabel_error_c:\n\textent_unlock2(tsdn, extent, trail);\nlabel_error_b:\n\textent_dalloc(tsdn, arena, trail);\nlabel_error_a:\n\treturn NULL;\n}\n\nextent_t *\nextent_split_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,\n    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b) {\n\treturn extent_split_impl(tsdn, arena, r_extent_hooks, extent, size_a,\n\t    szind_a, slab_a, size_b, szind_b, slab_b, false);\n}\n\nstatic bool\nextent_merge_default_impl(void *addr_a, void *addr_b) {\n\tif (!maps_coalesce) {\n\t\treturn true;\n\t}\n\tif (have_dss && !extent_dss_mergeable(addr_a, addr_b)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n#ifdef JEMALLOC_MAPS_COALESCE\nstatic bool\nextent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,\n    void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {\n\treturn extent_merge_default_impl(addr_a, addr_b);\n}\n#endif\n\nstatic bool\nextent_merge_impl(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,\n    bool growing_retained) {\n\twitness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),\n\t    WITNESS_RANK_CORE, growing_retained ? 1 : 0);\n\n\textent_hooks_assure_initialized(arena, r_extent_hooks);\n\n\tif ((*r_extent_hooks)->merge == NULL) {\n\t\treturn true;\n\t}\n\n\tbool err;\n\tif (*r_extent_hooks == &extent_hooks_default) {\n\t\t/* Call directly to propagate tsdn. */\n\t\terr = extent_merge_default_impl(extent_base_get(a),\n\t\t    extent_base_get(b));\n\t} else {\n\t\textent_hook_pre_reentrancy(tsdn, arena);\n\t\terr = (*r_extent_hooks)->merge(*r_extent_hooks,\n\t\t    extent_base_get(a), extent_size_get(a), extent_base_get(b),\n\t\t    extent_size_get(b), extent_committed_get(a),\n\t\t    arena_ind_get(arena));\n\t\textent_hook_post_reentrancy(tsdn);\n\t}\n\n\tif (err) {\n\t\treturn true;\n\t}\n\n\t/*\n\t * The rtree writes must happen while all the relevant elements are\n\t * owned, so the following code uses decomposed helper functions rather\n\t * than extent_{,de}register() to do things in the right order.\n\t */\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\trtree_leaf_elm_t *a_elm_a, *a_elm_b, *b_elm_a, *b_elm_b;\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, a, true, false, &a_elm_a,\n\t    &a_elm_b);\n\textent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, b, true, false, &b_elm_a,\n\t    &b_elm_b);\n\n\textent_lock2(tsdn, a, b);\n\n\tif (a_elm_b != NULL) {\n\t\trtree_leaf_elm_write(tsdn, &extents_rtree, a_elm_b, NULL,\n\t\t    NSIZES, false);\n\t}\n\tif (b_elm_b != NULL) {\n\t\trtree_leaf_elm_write(tsdn, &extents_rtree, b_elm_a, NULL,\n\t\t    NSIZES, false);\n\t} else {\n\t\tb_elm_b = b_elm_a;\n\t}\n\n\textent_size_set(a, extent_size_get(a) + extent_size_get(b));\n\textent_szind_set(a, NSIZES);\n\textent_sn_set(a, (extent_sn_get(a) < extent_sn_get(b)) ?\n\t    extent_sn_get(a) : extent_sn_get(b));\n\textent_zeroed_set(a, extent_zeroed_get(a) && extent_zeroed_get(b));\n\n\textent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, NSIZES, false);\n\n\textent_unlock2(tsdn, a, b);\n\n\textent_dalloc(tsdn, extent_arena_get(b), b);\n\n\treturn false;\n}\n\nbool\nextent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,\n    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b) {\n\treturn extent_merge_impl(tsdn, arena, r_extent_hooks, a, b, false);\n}\n\nbool\nextent_boot(void) {\n\tif (rtree_new(&extents_rtree, true)) {\n\t\treturn true;\n\t}\n\n\tif (mutex_pool_init(&extent_mutex_pool, \"extent_mutex_pool\",\n\t    WITNESS_RANK_EXTENT_POOL)) {\n\t\treturn true;\n\t}\n\n\tif (have_dss) {\n\t\textent_dss_boot();\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/extent_dss.c",
    "content": "#define JEMALLOC_EXTENT_DSS_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/spin.h\"\n\n/******************************************************************************/\n/* Data. */\n\nconst char\t*opt_dss = DSS_DEFAULT;\n\nconst char\t*dss_prec_names[] = {\n\t\"disabled\",\n\t\"primary\",\n\t\"secondary\",\n\t\"N/A\"\n};\n\n/*\n * Current dss precedence default, used when creating new arenas.  NB: This is\n * stored as unsigned rather than dss_prec_t because in principle there's no\n * guarantee that sizeof(dss_prec_t) is the same as sizeof(unsigned), and we use\n * atomic operations to synchronize the setting.\n */\nstatic atomic_u_t\tdss_prec_default = ATOMIC_INIT(\n    (unsigned)DSS_PREC_DEFAULT);\n\n/* Base address of the DSS. */\nstatic void\t\t*dss_base;\n/* Atomic boolean indicating whether a thread is currently extending DSS. */\nstatic atomic_b_t\tdss_extending;\n/* Atomic boolean indicating whether the DSS is exhausted. */\nstatic atomic_b_t\tdss_exhausted;\n/* Atomic current upper limit on DSS addresses. */\nstatic atomic_p_t\tdss_max;\n\n/******************************************************************************/\n\nstatic void *\nextent_dss_sbrk(intptr_t increment) {\n#ifdef JEMALLOC_DSS\n\treturn sbrk(increment);\n#else\n\tnot_implemented();\n\treturn NULL;\n#endif\n}\n\ndss_prec_t\nextent_dss_prec_get(void) {\n\tdss_prec_t ret;\n\n\tif (!have_dss) {\n\t\treturn dss_prec_disabled;\n\t}\n\tret = (dss_prec_t)atomic_load_u(&dss_prec_default, ATOMIC_ACQUIRE);\n\treturn ret;\n}\n\nbool\nextent_dss_prec_set(dss_prec_t dss_prec) {\n\tif (!have_dss) {\n\t\treturn (dss_prec != dss_prec_disabled);\n\t}\n\tatomic_store_u(&dss_prec_default, (unsigned)dss_prec, ATOMIC_RELEASE);\n\treturn false;\n}\n\nstatic void\nextent_dss_extending_start(void) {\n\tspin_t spinner = SPIN_INITIALIZER;\n\twhile (true) {\n\t\tbool expected = false;\n\t\tif (atomic_compare_exchange_weak_b(&dss_extending, &expected,\n\t\t    true, ATOMIC_ACQ_REL, ATOMIC_RELAXED)) {\n\t\t\tbreak;\n\t\t}\n\t\tspin_adaptive(&spinner);\n\t}\n}\n\nstatic void\nextent_dss_extending_finish(void) {\n\tassert(atomic_load_b(&dss_extending, ATOMIC_RELAXED));\n\n\tatomic_store_b(&dss_extending, false, ATOMIC_RELEASE);\n}\n\nstatic void *\nextent_dss_max_update(void *new_addr) {\n\t/*\n\t * Get the current end of the DSS as max_cur and assure that dss_max is\n\t * up to date.\n\t */\n\tvoid *max_cur = extent_dss_sbrk(0);\n\tif (max_cur == (void *)-1) {\n\t\treturn NULL;\n\t}\n\tatomic_store_p(&dss_max, max_cur, ATOMIC_RELEASE);\n\t/* Fixed new_addr can only be supported if it is at the edge of DSS. */\n\tif (new_addr != NULL && max_cur != new_addr) {\n\t\treturn NULL;\n\t}\n\treturn max_cur;\n}\n\nvoid *\nextent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit) {\n\textent_t *gap;\n\n\tcassert(have_dss);\n\tassert(size > 0);\n\tassert(alignment > 0);\n\n\t/*\n\t * sbrk() uses a signed increment argument, so take care not to\n\t * interpret a large allocation request as a negative increment.\n\t */\n\tif ((intptr_t)size < 0) {\n\t\treturn NULL;\n\t}\n\n\tgap = extent_alloc(tsdn, arena);\n\tif (gap == NULL) {\n\t\treturn NULL;\n\t}\n\n\textent_dss_extending_start();\n\tif (!atomic_load_b(&dss_exhausted, ATOMIC_ACQUIRE)) {\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\twhile (true) {\n\t\t\tvoid *max_cur = extent_dss_max_update(new_addr);\n\t\t\tif (max_cur == NULL) {\n\t\t\t\tgoto label_oom;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Compute how much page-aligned gap 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\tvoid *gap_addr_page = (void *)(PAGE_CEILING(\n\t\t\t    (uintptr_t)max_cur));\n\t\t\tvoid *ret = (void *)ALIGNMENT_CEILING(\n\t\t\t    (uintptr_t)gap_addr_page, alignment);\n\t\t\tsize_t gap_size_page = (uintptr_t)ret -\n\t\t\t    (uintptr_t)gap_addr_page;\n\t\t\tif (gap_size_page != 0) {\n\t\t\t\textent_init(gap, arena, gap_addr_page,\n\t\t\t\t    gap_size_page, false, NSIZES,\n\t\t\t\t    arena_extent_sn_next(arena),\n\t\t\t\t    extent_state_active, false, true, true);\n\t\t\t}\n\t\t\t/*\n\t\t\t * Compute the address just past the end of the desired\n\t\t\t * allocation space.\n\t\t\t */\n\t\t\tvoid *dss_next = (void *)((uintptr_t)ret + size);\n\t\t\tif ((uintptr_t)ret < (uintptr_t)max_cur ||\n\t\t\t    (uintptr_t)dss_next < (uintptr_t)max_cur) {\n\t\t\t\tgoto label_oom; /* Wrap-around. */\n\t\t\t}\n\t\t\t/* Compute the increment, including subpage bytes. */\n\t\t\tvoid *gap_addr_subpage = max_cur;\n\t\t\tsize_t gap_size_subpage = (uintptr_t)ret -\n\t\t\t    (uintptr_t)gap_addr_subpage;\n\t\t\tintptr_t incr = gap_size_subpage + size;\n\n\t\t\tassert((uintptr_t)max_cur + incr == (uintptr_t)ret +\n\t\t\t    size);\n\n\t\t\t/* Try to allocate. */\n\t\t\tvoid *dss_prev = extent_dss_sbrk(incr);\n\t\t\tif (dss_prev == max_cur) {\n\t\t\t\t/* Success. */\n\t\t\t\tatomic_store_p(&dss_max, dss_next,\n\t\t\t\t    ATOMIC_RELEASE);\n\t\t\t\textent_dss_extending_finish();\n\n\t\t\t\tif (gap_size_page != 0) {\n\t\t\t\t\textent_dalloc_gap(tsdn, arena, gap);\n\t\t\t\t} else {\n\t\t\t\t\textent_dalloc(tsdn, arena, gap);\n\t\t\t\t}\n\t\t\t\tif (!*commit) {\n\t\t\t\t\t*commit = pages_decommit(ret, size);\n\t\t\t\t}\n\t\t\t\tif (*zero && *commit) {\n\t\t\t\t\textent_hooks_t *extent_hooks =\n\t\t\t\t\t    EXTENT_HOOKS_INITIALIZER;\n\t\t\t\t\textent_t extent;\n\n\t\t\t\t\textent_init(&extent, arena, ret, size,\n\t\t\t\t\t    size, false, NSIZES,\n\t\t\t\t\t    extent_state_active, false, true,\n\t\t\t\t\t    true);\n\t\t\t\t\tif (extent_purge_forced_wrapper(tsdn,\n\t\t\t\t\t    arena, &extent_hooks, &extent, 0,\n\t\t\t\t\t    size)) {\n\t\t\t\t\t\tmemset(ret, 0, size);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\t/*\n\t\t\t * Failure, whether due to OOM or a race with a raw\n\t\t\t * sbrk() call from outside the allocator.\n\t\t\t */\n\t\t\tif (dss_prev == (void *)-1) {\n\t\t\t\t/* OOM. */\n\t\t\t\tatomic_store_b(&dss_exhausted, true,\n\t\t\t\t    ATOMIC_RELEASE);\n\t\t\t\tgoto label_oom;\n\t\t\t}\n\t\t}\n\t}\nlabel_oom:\n\textent_dss_extending_finish();\n\textent_dalloc(tsdn, arena, gap);\n\treturn NULL;\n}\n\nstatic bool\nextent_in_dss_helper(void *addr, void *max) {\n\treturn ((uintptr_t)addr >= (uintptr_t)dss_base && (uintptr_t)addr <\n\t    (uintptr_t)max);\n}\n\nbool\nextent_in_dss(void *addr) {\n\tcassert(have_dss);\n\n\treturn extent_in_dss_helper(addr, atomic_load_p(&dss_max,\n\t    ATOMIC_ACQUIRE));\n}\n\nbool\nextent_dss_mergeable(void *addr_a, void *addr_b) {\n\tvoid *max;\n\n\tcassert(have_dss);\n\n\tif ((uintptr_t)addr_a < (uintptr_t)dss_base && (uintptr_t)addr_b <\n\t    (uintptr_t)dss_base) {\n\t\treturn true;\n\t}\n\n\tmax = atomic_load_p(&dss_max, ATOMIC_ACQUIRE);\n\treturn (extent_in_dss_helper(addr_a, max) ==\n\t    extent_in_dss_helper(addr_b, max));\n}\n\nvoid\nextent_dss_boot(void) {\n\tcassert(have_dss);\n\n\tdss_base = extent_dss_sbrk(0);\n\tatomic_store_b(&dss_extending, false, ATOMIC_RELAXED);\n\tatomic_store_b(&dss_exhausted, dss_base == (void *)-1, ATOMIC_RELAXED);\n\tatomic_store_p(&dss_max, dss_base, ATOMIC_RELAXED);\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/extent_mmap.c",
    "content": "#define JEMALLOC_EXTENT_MMAP_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n\n/******************************************************************************/\n/* Data. */\n\nbool\topt_retain =\n#ifdef JEMALLOC_RETAIN\n    true\n#else\n    false\n#endif\n    ;\n\n/******************************************************************************/\n\nvoid *\nextent_alloc_mmap(void *new_addr, size_t size, size_t alignment, bool *zero,\n    bool *commit) {\n\tvoid *ret = pages_map(new_addr, size, ALIGNMENT_CEILING(alignment,\n\t    PAGE), commit);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\tassert(ret != NULL);\n\tif (*commit) {\n\t\t*zero = true;\n\t}\n\treturn ret;\n}\n\nbool\nextent_dalloc_mmap(void *addr, size_t size) {\n\tif (!opt_retain) {\n\t\tpages_unmap(addr, size);\n\t}\n\treturn opt_retain;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/hash.c",
    "content": "#define JEMALLOC_HASH_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/hooks.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n/*\n * The hooks are a little bit screwy -- they're not genuinely exported in the\n * sense that we want them available to end-users, but we do want them visible\n * from outside the generated library, so that we can use them in test code.\n */\nJEMALLOC_EXPORT\nvoid (*hooks_arena_new_hook)() = NULL;\n\nJEMALLOC_EXPORT\nvoid (*hooks_libc_hook)() = NULL;\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/jemalloc.c",
    "content": "#define JEMALLOC_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/atomic.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/extent_dss.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/jemalloc_internal_types.h\"\n#include \"jemalloc/internal/log.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/size_classes.h\"\n#include \"jemalloc/internal/spin.h\"\n#include \"jemalloc/internal/sz.h\"\n#include \"jemalloc/internal/ticker.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n/* Data. */\n\n/* Runtime configuration options. */\nconst char\t*je_malloc_conf\n#ifndef _WIN32\n    JEMALLOC_ATTR(weak)\n#endif\n    ;\nbool\topt_abort =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nbool\topt_abort_conf =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\nconst char\t*opt_junk =\n#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))\n    \"true\"\n#else\n    \"false\"\n#endif\n    ;\nbool\topt_junk_alloc =\n#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))\n    true\n#else\n    false\n#endif\n    ;\nbool\topt_junk_free =\n#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))\n    true\n#else\n    false\n#endif\n    ;\n\nbool\topt_utrace = false;\nbool\topt_xmalloc = false;\nbool\topt_zero = false;\nunsigned\topt_narenas = 0;\n\nunsigned\tncpus;\n\n/* Protects arenas initialization. */\nmalloc_mutex_t arenas_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 *\n * Points to an arena_t.\n */\nJEMALLOC_ALIGNED(CACHELINE)\natomic_p_t\t\tarenas[MALLOCX_ARENA_LIMIT];\nstatic atomic_u_t\tnarenas_total; /* Use narenas_total_*(). */\nstatic arena_t\t\t*a0; /* arenas[0]; read-only after initialization. */\nunsigned\t\tnarenas_auto; /* Read-only after initialization. */\n\ntypedef enum {\n\tmalloc_init_uninitialized\t= 3,\n\tmalloc_init_a0_initialized\t= 2,\n\tmalloc_init_recursible\t\t= 1,\n\tmalloc_init_initialized\t\t= 0 /* Common case --> jnz. */\n} malloc_init_t;\nstatic malloc_init_t\tmalloc_init_state = malloc_init_uninitialized;\n\n/* False should be the common case.  Set to true to trigger initialization. */\nbool\t\t\tmalloc_slow = true;\n\n/* When malloc_slow is true, set the corresponding bits for sanity check. */\nenum {\n\tflag_opt_junk_alloc\t= (1U),\n\tflag_opt_junk_free\t= (1U << 1),\n\tflag_opt_zero\t\t= (1U << 2),\n\tflag_opt_utrace\t\t= (1U << 3),\n\tflag_opt_xmalloc\t= (1U << 4)\n};\nstatic uint8_t\tmalloc_slow_flags;\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\n#if _WIN32_WINNT >= 0x0600\nstatic malloc_mutex_t\tinit_lock = SRWLOCK_INIT;\n#else\nstatic malloc_mutex_t\tinit_lock;\nstatic bool init_lock_initialized = false;\n\nJEMALLOC_ATTR(constructor)\nstatic void WINAPI\n_init_init_lock(void) {\n\t/*\n\t * If another constructor in the same binary is using mallctl to e.g.\n\t * set up extent hooks, it may end up running before this one, and\n\t * malloc_init_hard will crash trying to lock the uninitialized lock. So\n\t * we force an initialization of the lock in malloc_init_hard as well.\n\t * We don't try to care about atomicity of the accessed to the\n\t * init_lock_initialized boolean, since it really only matters early in\n\t * the process creation, before any separate thread normally starts\n\t * doing anything.\n\t */\n\tif (!init_lock_initialized) {\n\t\tmalloc_mutex_init(&init_lock, \"init\", WITNESS_RANK_INIT,\n\t\t    malloc_mutex_rank_exclusive);\n\t}\n\tinit_lock_initialized = true;\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#endif\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 (unlikely(opt_utrace)) {\t\t\t\t\t\\\n\t\tint utrace_serrno = errno;\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\terrno = utrace_serrno;\t\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/* Whether encountered any invalid config options. */\nstatic bool had_conf_error = false;\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic bool\tmalloc_init_hard_a0(void);\nstatic bool\tmalloc_init_hard(void);\n\n/******************************************************************************/\n/*\n * Begin miscellaneous support functions.\n */\n\nbool\nmalloc_initialized(void) {\n\treturn (malloc_init_state == malloc_init_initialized);\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nmalloc_init_a0(void) {\n\tif (unlikely(malloc_init_state == malloc_init_uninitialized)) {\n\t\treturn malloc_init_hard_a0();\n\t}\n\treturn false;\n}\n\nJEMALLOC_ALWAYS_INLINE bool\nmalloc_init(void) {\n\tif (unlikely(!malloc_initialized()) && malloc_init_hard()) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*\n * The a0*() functions are used instead of i{d,}alloc() in situations that\n * cannot tolerate TLS variable access.\n */\n\nstatic void *\na0ialloc(size_t size, bool zero, bool is_internal) {\n\tif (unlikely(malloc_init_a0())) {\n\t\treturn NULL;\n\t}\n\n\treturn iallocztm(TSDN_NULL, size, sz_size2index(size), zero, NULL,\n\t    is_internal, arena_get(TSDN_NULL, 0, true), true);\n}\n\nstatic void\na0idalloc(void *ptr, bool is_internal) {\n\tidalloctm(TSDN_NULL, ptr, NULL, NULL, is_internal, true);\n}\n\nvoid *\na0malloc(size_t size) {\n\treturn a0ialloc(size, false, true);\n}\n\nvoid\na0dalloc(void *ptr) {\n\ta0idalloc(ptr, true);\n}\n\n/*\n * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-senstive\n * situations that cannot tolerate TLS variable access (TLS allocation and very\n * early internal data structure initialization).\n */\n\nvoid *\nbootstrap_malloc(size_t size) {\n\tif (unlikely(size == 0)) {\n\t\tsize = 1;\n\t}\n\n\treturn a0ialloc(size, false, false);\n}\n\nvoid *\nbootstrap_calloc(size_t num, size_t size) {\n\tsize_t num_size;\n\n\tnum_size = num * size;\n\tif (unlikely(num_size == 0)) {\n\t\tassert(num == 0 || size == 0);\n\t\tnum_size = 1;\n\t}\n\n\treturn a0ialloc(num_size, true, false);\n}\n\nvoid\nbootstrap_free(void *ptr) {\n\tif (unlikely(ptr == NULL)) {\n\t\treturn;\n\t}\n\n\ta0idalloc(ptr, false);\n}\n\nvoid\narena_set(unsigned ind, arena_t *arena) {\n\tatomic_store_p(&arenas[ind], arena, ATOMIC_RELEASE);\n}\n\nstatic void\nnarenas_total_set(unsigned narenas) {\n\tatomic_store_u(&narenas_total, narenas, ATOMIC_RELEASE);\n}\n\nstatic void\nnarenas_total_inc(void) {\n\tatomic_fetch_add_u(&narenas_total, 1, ATOMIC_RELEASE);\n}\n\nunsigned\nnarenas_total_get(void) {\n\treturn atomic_load_u(&narenas_total, ATOMIC_ACQUIRE);\n}\n\n/* Create a new arena and insert it into the arenas array at index ind. */\nstatic arena_t *\narena_init_locked(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tarena_t *arena;\n\n\tassert(ind <= narenas_total_get());\n\tif (ind >= MALLOCX_ARENA_LIMIT) {\n\t\treturn NULL;\n\t}\n\tif (ind == narenas_total_get()) {\n\t\tnarenas_total_inc();\n\t}\n\n\t/*\n\t * Another thread may have already initialized arenas[ind] if it's an\n\t * auto arena.\n\t */\n\tarena = arena_get(tsdn, ind, false);\n\tif (arena != NULL) {\n\t\tassert(ind < narenas_auto);\n\t\treturn arena;\n\t}\n\n\t/* Actually initialize the arena. */\n\tarena = arena_new(tsdn, ind, extent_hooks);\n\n\treturn arena;\n}\n\nstatic void\narena_new_create_background_thread(tsdn_t *tsdn, unsigned ind) {\n\tif (ind == 0) {\n\t\treturn;\n\t}\n\tif (have_background_thread) {\n\t\tbool err;\n\t\tmalloc_mutex_lock(tsdn, &background_thread_lock);\n\t\terr = background_thread_create(tsdn_tsd(tsdn), ind);\n\t\tmalloc_mutex_unlock(tsdn, &background_thread_lock);\n\t\tif (err) {\n\t\t\tmalloc_printf(\"<jemalloc>: error in background thread \"\n\t\t\t\t      \"creation for arena %u. Abort.\\n\", ind);\n\t\t\tabort();\n\t\t}\n\t}\n}\n\narena_t *\narena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {\n\tarena_t *arena;\n\n\tmalloc_mutex_lock(tsdn, &arenas_lock);\n\tarena = arena_init_locked(tsdn, ind, extent_hooks);\n\tmalloc_mutex_unlock(tsdn, &arenas_lock);\n\n\tarena_new_create_background_thread(tsdn, ind);\n\n\treturn arena;\n}\n\nstatic void\narena_bind(tsd_t *tsd, unsigned ind, bool internal) {\n\tarena_t *arena = arena_get(tsd_tsdn(tsd), ind, false);\n\tarena_nthreads_inc(arena, internal);\n\n\tif (internal) {\n\t\ttsd_iarena_set(tsd, arena);\n\t} else {\n\t\ttsd_arena_set(tsd, arena);\n\t}\n}\n\nvoid\narena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind) {\n\tarena_t *oldarena, *newarena;\n\n\toldarena = arena_get(tsd_tsdn(tsd), oldind, false);\n\tnewarena = arena_get(tsd_tsdn(tsd), newind, false);\n\tarena_nthreads_dec(oldarena, false);\n\tarena_nthreads_inc(newarena, false);\n\ttsd_arena_set(tsd, newarena);\n}\n\nstatic void\narena_unbind(tsd_t *tsd, unsigned ind, bool internal) {\n\tarena_t *arena;\n\n\tarena = arena_get(tsd_tsdn(tsd), ind, false);\n\tarena_nthreads_dec(arena, internal);\n\n\tif (internal) {\n\t\ttsd_iarena_set(tsd, NULL);\n\t} else {\n\t\ttsd_arena_set(tsd, NULL);\n\t}\n}\n\narena_tdata_t *\narena_tdata_get_hard(tsd_t *tsd, unsigned ind) {\n\tarena_tdata_t *tdata, *arenas_tdata_old;\n\tarena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);\n\tunsigned narenas_tdata_old, i;\n\tunsigned narenas_tdata = tsd_narenas_tdata_get(tsd);\n\tunsigned narenas_actual = narenas_total_get();\n\n\t/*\n\t * Dissociate old tdata array (and set up for deallocation upon return)\n\t * if it's too small.\n\t */\n\tif (arenas_tdata != NULL && narenas_tdata < narenas_actual) {\n\t\tarenas_tdata_old = arenas_tdata;\n\t\tnarenas_tdata_old = narenas_tdata;\n\t\tarenas_tdata = NULL;\n\t\tnarenas_tdata = 0;\n\t\ttsd_arenas_tdata_set(tsd, arenas_tdata);\n\t\ttsd_narenas_tdata_set(tsd, narenas_tdata);\n\t} else {\n\t\tarenas_tdata_old = NULL;\n\t\tnarenas_tdata_old = 0;\n\t}\n\n\t/* Allocate tdata array if it's missing. */\n\tif (arenas_tdata == NULL) {\n\t\tbool *arenas_tdata_bypassp = tsd_arenas_tdata_bypassp_get(tsd);\n\t\tnarenas_tdata = (ind < narenas_actual) ? narenas_actual : ind+1;\n\n\t\tif (tsd_nominal(tsd) && !*arenas_tdata_bypassp) {\n\t\t\t*arenas_tdata_bypassp = true;\n\t\t\tarenas_tdata = (arena_tdata_t *)a0malloc(\n\t\t\t    sizeof(arena_tdata_t) * narenas_tdata);\n\t\t\t*arenas_tdata_bypassp = false;\n\t\t}\n\t\tif (arenas_tdata == NULL) {\n\t\t\ttdata = NULL;\n\t\t\tgoto label_return;\n\t\t}\n\t\tassert(tsd_nominal(tsd) && !*arenas_tdata_bypassp);\n\t\ttsd_arenas_tdata_set(tsd, arenas_tdata);\n\t\ttsd_narenas_tdata_set(tsd, narenas_tdata);\n\t}\n\n\t/*\n\t * Copy to tdata array.  It's possible that the actual number of arenas\n\t * has increased since narenas_total_get() was called above, but that\n\t * causes no correctness issues unless two threads concurrently execute\n\t * the arenas.create mallctl, which we trust mallctl synchronization to\n\t * prevent.\n\t */\n\n\t/* Copy/initialize tickers. */\n\tfor (i = 0; i < narenas_actual; i++) {\n\t\tif (i < narenas_tdata_old) {\n\t\t\tticker_copy(&arenas_tdata[i].decay_ticker,\n\t\t\t    &arenas_tdata_old[i].decay_ticker);\n\t\t} else {\n\t\t\tticker_init(&arenas_tdata[i].decay_ticker,\n\t\t\t    DECAY_NTICKS_PER_UPDATE);\n\t\t}\n\t}\n\tif (narenas_tdata > narenas_actual) {\n\t\tmemset(&arenas_tdata[narenas_actual], 0, sizeof(arena_tdata_t)\n\t\t    * (narenas_tdata - narenas_actual));\n\t}\n\n\t/* Read the refreshed tdata array. */\n\ttdata = &arenas_tdata[ind];\nlabel_return:\n\tif (arenas_tdata_old != NULL) {\n\t\ta0dalloc(arenas_tdata_old);\n\t}\n\treturn tdata;\n}\n\n/* Slow path, called only by arena_choose(). */\narena_t *\narena_choose_hard(tsd_t *tsd, bool internal) {\n\tarena_t *ret JEMALLOC_CC_SILENCE_INIT(NULL);\n\n\tif (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena)) {\n\t\tunsigned choose = percpu_arena_choose();\n\t\tret = arena_get(tsd_tsdn(tsd), choose, true);\n\t\tassert(ret != NULL);\n\t\tarena_bind(tsd, arena_ind_get(ret), false);\n\t\tarena_bind(tsd, arena_ind_get(ret), true);\n\n\t\treturn ret;\n\t}\n\n\tif (narenas_auto > 1) {\n\t\tunsigned i, j, choose[2], first_null;\n\t\tbool is_new_arena[2];\n\n\t\t/*\n\t\t * Determine binding for both non-internal and internal\n\t\t * allocation.\n\t\t *\n\t\t *   choose[0]: For application allocation.\n\t\t *   choose[1]: For internal metadata allocation.\n\t\t */\n\n\t\tfor (j = 0; j < 2; j++) {\n\t\t\tchoose[j] = 0;\n\t\t\tis_new_arena[j] = false;\n\t\t}\n\n\t\tfirst_null = narenas_auto;\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &arenas_lock);\n\t\tassert(arena_get(tsd_tsdn(tsd), 0, false) != NULL);\n\t\tfor (i = 1; i < narenas_auto; i++) {\n\t\t\tif (arena_get(tsd_tsdn(tsd), i, false) != 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\tfor (j = 0; j < 2; j++) {\n\t\t\t\t\tif (arena_nthreads_get(arena_get(\n\t\t\t\t\t    tsd_tsdn(tsd), i, false), !!j) <\n\t\t\t\t\t    arena_nthreads_get(arena_get(\n\t\t\t\t\t    tsd_tsdn(tsd), choose[j], false),\n\t\t\t\t\t    !!j)) {\n\t\t\t\t\t\tchoose[j] = i;\n\t\t\t\t\t}\n\t\t\t\t}\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\tfor (j = 0; j < 2; j++) {\n\t\t\tif (arena_nthreads_get(arena_get(tsd_tsdn(tsd),\n\t\t\t    choose[j], false), !!j) == 0 || first_null ==\n\t\t\t    narenas_auto) {\n\t\t\t\t/*\n\t\t\t\t * Use an unloaded arena, or the least loaded\n\t\t\t\t * arena if all arenas are already initialized.\n\t\t\t\t */\n\t\t\t\tif (!!j == internal) {\n\t\t\t\t\tret = arena_get(tsd_tsdn(tsd),\n\t\t\t\t\t    choose[j], false);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tarena_t *arena;\n\n\t\t\t\t/* Initialize a new arena. */\n\t\t\t\tchoose[j] = first_null;\n\t\t\t\tarena = arena_init_locked(tsd_tsdn(tsd),\n\t\t\t\t    choose[j],\n\t\t\t\t    (extent_hooks_t *)&extent_hooks_default);\n\t\t\t\tif (arena == NULL) {\n\t\t\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd),\n\t\t\t\t\t    &arenas_lock);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tis_new_arena[j] = true;\n\t\t\t\tif (!!j == internal) {\n\t\t\t\t\tret = arena;\n\t\t\t\t}\n\t\t\t}\n\t\t\tarena_bind(tsd, choose[j], !!j);\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &arenas_lock);\n\n\t\tfor (j = 0; j < 2; j++) {\n\t\t\tif (is_new_arena[j]) {\n\t\t\t\tassert(choose[j] > 0);\n\t\t\t\tarena_new_create_background_thread(\n\t\t\t\t    tsd_tsdn(tsd), choose[j]);\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tret = arena_get(tsd_tsdn(tsd), 0, false);\n\t\tarena_bind(tsd, 0, false);\n\t\tarena_bind(tsd, 0, true);\n\t}\n\n\treturn ret;\n}\n\nvoid\niarena_cleanup(tsd_t *tsd) {\n\tarena_t *iarena;\n\n\tiarena = tsd_iarena_get(tsd);\n\tif (iarena != NULL) {\n\t\tarena_unbind(tsd, arena_ind_get(iarena), true);\n\t}\n}\n\nvoid\narena_cleanup(tsd_t *tsd) {\n\tarena_t *arena;\n\n\tarena = tsd_arena_get(tsd);\n\tif (arena != NULL) {\n\t\tarena_unbind(tsd, arena_ind_get(arena), false);\n\t}\n}\n\nvoid\narenas_tdata_cleanup(tsd_t *tsd) {\n\tarena_tdata_t *arenas_tdata;\n\n\t/* Prevent tsd->arenas_tdata from being (re)created. */\n\t*tsd_arenas_tdata_bypassp_get(tsd) = true;\n\n\tarenas_tdata = tsd_arenas_tdata_get(tsd);\n\tif (arenas_tdata != NULL) {\n\t\ttsd_arenas_tdata_set(tsd, NULL);\n\t\ta0dalloc(arenas_tdata);\n\t}\n}\n\nstatic void\nstats_print_atexit(void) {\n\tif (config_stats) {\n\t\ttsdn_t *tsdn;\n\t\tunsigned narenas, i;\n\n\t\ttsdn = tsdn_fetch();\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 = arena_get(tsdn, i, false);\n\t\t\tif (arena != NULL) {\n\t\t\t\ttcache_t *tcache;\n\n\t\t\t\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\t\t\t\tql_foreach(tcache, &arena->tcache_ql, link) {\n\t\t\t\t\ttcache_stats_merge(tsdn, tcache, arena);\n\t\t\t\t}\n\t\t\t\tmalloc_mutex_unlock(tsdn,\n\t\t\t\t    &arena->tcache_ql_mtx);\n\t\t\t}\n\t\t}\n\t}\n\tje_malloc_stats_print(NULL, NULL, opt_stats_print_opts);\n}\n\n/*\n * Ensure that we don't hold any locks upon entry to or exit from allocator\n * code (in a \"broad\" sense that doesn't count a reentrant allocation as an\n * entrance or exit).\n */\nJEMALLOC_ALWAYS_INLINE void\ncheck_entry_exit_locking(tsdn_t *tsdn) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\tif (tsdn_null(tsdn)) {\n\t\treturn;\n\t}\n\ttsd_t *tsd = tsdn_tsd(tsdn);\n\t/*\n\t * It's possible we hold locks at entry/exit if we're in a nested\n\t * allocation.\n\t */\n\tint8_t reentrancy_level = tsd_reentrancy_level_get(tsd);\n\tif (reentrancy_level != 0) {\n\t\treturn;\n\t}\n\twitness_assert_lockless(tsdn_witness_tsdp_get(tsdn));\n}\n\n/*\n * End miscellaneous support functions.\n */\n/******************************************************************************/\n/*\n * Begin initialization functions.\n */\n\nstatic char *\njemalloc_secure_getenv(const char *name) {\n#ifdef JEMALLOC_HAVE_SECURE_GETENV\n\treturn secure_getenv(name);\n#else\n#  ifdef JEMALLOC_HAVE_ISSETUGID\n\tif (issetugid() != 0) {\n\t\treturn NULL;\n\t}\n#  endif\n\treturn getenv(name);\n#endif\n}\n\nstatic unsigned\nmalloc_ncpus(void) {\n\tlong result;\n\n#ifdef _WIN32\n\tSYSTEM_INFO si;\n\tGetSystemInfo(&si);\n\tresult = si.dwNumberOfProcessors;\n#elif defined(JEMALLOC_GLIBC_MALLOC_HOOK) && defined(CPU_COUNT)\n\t/*\n\t * glibc >= 2.6 has the CPU_COUNT macro.\n\t *\n\t * glibc's sysconf() uses isspace().  glibc allocates for the first time\n\t * *before* setting up the isspace tables.  Therefore we need a\n\t * different method to get the number of CPUs.\n\t */\n\t{\n\t\tcpu_set_t set;\n\n\t\tpthread_getaffinity_np(pthread_self(), sizeof(set), &set);\n\t\tresult = CPU_COUNT(&set);\n\t}\n#else\n\tresult = sysconf(_SC_NPROCESSORS_ONLN);\n#endif\n\treturn ((result == -1) ? 1 : (unsigned)result);\n}\n\nstatic void\ninit_opt_stats_print_opts(const char *v, size_t vlen) {\n\tsize_t opts_len = strlen(opt_stats_print_opts);\n\tassert(opts_len <= stats_print_tot_num_options);\n\n\tfor (size_t i = 0; i < vlen; i++) {\n\t\tswitch (v[i]) {\n#define OPTION(o, v, d, s) case o: break;\n\t\t\tSTATS_PRINT_OPTIONS\n#undef OPTION\n\t\tdefault: continue;\n\t\t}\n\n\t\tif (strchr(opt_stats_print_opts, v[i]) != NULL) {\n\t\t\t/* Ignore repeated. */\n\t\t\tcontinue;\n\t\t}\n\n\t\topt_stats_print_opts[opts_len++] = v[i];\n\t\topt_stats_print_opts[opts_len] = '\\0';\n\t\tassert(opts_len <= stats_print_tot_num_options);\n\t}\n\tassert(opts_len == strlen(opt_stats_print_opts));\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\tbool accept;\n\tconst char *opts = *opts_p;\n\n\t*k_p = opts;\n\n\tfor (accept = false; !accept;) {\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;) {\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_abort_invalid_conf(void) {\n\tassert(opt_abort_conf);\n\tmalloc_printf(\"<jemalloc>: Abort (abort_conf:true) on invalid conf \"\n\t    \"value (see above).\\n\");\n\tabort();\n}\n\nstatic void\nmalloc_conf_error(const char *msg, const char *k, size_t klen, const char *v,\n    size_t vlen) {\n\tmalloc_printf(\"<jemalloc>: %s: %.*s:%.*s\\n\", msg, (int)klen, k,\n\t    (int)vlen, v);\n\t/* If abort_conf is set, error out after processing all options. */\n\thad_conf_error = true;\n}\n\nstatic void\nmalloc_slow_flag_init(void) {\n\t/*\n\t * Combine the runtime options into malloc_slow for fast path.  Called\n\t * after processing all the options.\n\t */\n\tmalloc_slow_flags |= (opt_junk_alloc ? flag_opt_junk_alloc : 0)\n\t    | (opt_junk_free ? flag_opt_junk_free : 0)\n\t    | (opt_zero ? flag_opt_zero : 0)\n\t    | (opt_utrace ? flag_opt_utrace : 0)\n\t    | (opt_xmalloc ? flag_opt_xmalloc : 0);\n\n\tmalloc_slow = (malloc_slow_flags != 0);\n}\n\nstatic void\nmalloc_conf_init(void) {\n\tunsigned i;\n\tchar buf[PATH_MAX + 1];\n\tconst char *opts, *k, *v;\n\tsize_t klen, vlen;\n\n\tfor (i = 0; i < 4; i++) {\n\t\t/* Get runtime configuration. */\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\topts = config_malloc_conf;\n\t\t\tbreak;\n\t\tcase 1:\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 2: {\n\t\t\tssize_t linklen = 0;\n#ifndef _WIN32\n\t\t\tint saved_errno = errno;\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\t/*\n\t\t\t * Try to use the contents of the \"/etc/malloc.conf\"\n\t\t\t * symbolic link's name.\n\t\t\t */\n\t\t\tlinklen = readlink(linkname, buf, sizeof(buf) - 1);\n\t\t\tif (linklen == -1) {\n\t\t\t\t/* No configuration specified. */\n\t\t\t\tlinklen = 0;\n\t\t\t\t/* Restore errno. */\n\t\t\t\tset_errno(saved_errno);\n\t\t\t}\n#endif\n\t\t\tbuf[linklen] = '\\0';\n\t\t\topts = buf;\n\t\t\tbreak;\n\t\t} case 3: {\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 = jemalloc_secure_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\tnot_reached();\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)) {\n#define CONF_MATCH(n)\t\t\t\t\t\t\t\\\n\t(sizeof(n)-1 == klen && strncmp(n, k, klen) == 0)\n#define CONF_MATCH_VALUE(n)\t\t\t\t\t\t\\\n\t(sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0)\n#define CONF_HANDLE_BOOL(o, n)\t\t\t\t\t\t\\\n\t\t\tif (CONF_MATCH(n)) {\t\t\t\t\\\n\t\t\t\tif (CONF_MATCH_VALUE(\"true\")) {\t\t\\\n\t\t\t\t\to = true;\t\t\t\\\n\t\t\t\t} else if (CONF_MATCH_VALUE(\"false\")) {\t\\\n\t\t\t\t\to = false;\t\t\t\\\n\t\t\t\t} else {\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\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define CONF_MIN_no(um, min)\tfalse\n#define CONF_MIN_yes(um, min)\t((um) < (min))\n#define CONF_MAX_no(um, max)\tfalse\n#define CONF_MAX_yes(um, max)\t((um) > (max))\n#define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip)\t\\\n\t\t\tif (CONF_MATCH(n)) {\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 (clip) {\t\t\t\\\n\t\t\t\t\tif (CONF_MIN_##check_min(um,\t\\\n\t\t\t\t\t    (t)(min))) {\t\t\\\n\t\t\t\t\t\to = (t)(min);\t\t\\\n\t\t\t\t\t} else if (\t\t\t\\\n\t\t\t\t\t    CONF_MAX_##check_max(um,\t\\\n\t\t\t\t\t    (t)(max))) {\t\t\\\n\t\t\t\t\t\to = (t)(max);\t\t\\\n\t\t\t\t\t} else {\t\t\t\\\n\t\t\t\t\t\to = (t)um;\t\t\\\n\t\t\t\t\t}\t\t\t\t\\\n\t\t\t\t} else {\t\t\t\t\\\n\t\t\t\t\tif (CONF_MIN_##check_min(um,\t\\\n\t\t\t\t\t    (t)(min)) ||\t\t\\\n\t\t\t\t\t    CONF_MAX_##check_max(um,\t\\\n\t\t\t\t\t    (t)(max))) {\t\t\\\n\t\t\t\t\t\tmalloc_conf_error(\t\\\n\t\t\t\t\t\t    \"Out-of-range \"\t\\\n\t\t\t\t\t\t    \"conf value\",\t\\\n\t\t\t\t\t\t    k, klen, v, vlen);\t\\\n\t\t\t\t\t} else {\t\t\t\\\n\t\t\t\t\t\to = (t)um;\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\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max,\t\\\n    clip)\t\t\t\t\t\t\t\t\\\n\t\t\tCONF_HANDLE_T_U(unsigned, o, n, min, max,\t\\\n\t\t\t    check_min, check_max, clip)\n#define CONF_HANDLE_SIZE_T(o, n, min, max, check_min, check_max, clip)\t\\\n\t\t\tCONF_HANDLE_T_U(size_t, o, n, min, max,\t\t\\\n\t\t\t    check_min, check_max, clip)\n#define CONF_HANDLE_SSIZE_T(o, n, min, max)\t\t\t\t\\\n\t\t\tif (CONF_MATCH(n)) {\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\\\n\t\t\t\t\to = l;\t\t\t\t\\\n\t\t\t\t}\t\t\t\t\t\\\n\t\t\t\tcontinue;\t\t\t\t\\\n\t\t\t}\n#define CONF_HANDLE_CHAR_P(o, n, d)\t\t\t\t\t\\\n\t\t\tif (CONF_MATCH(n)) {\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\tCONF_HANDLE_BOOL(opt_abort_conf, \"abort_conf\")\n\t\t\tif (strncmp(\"metadata_thp\", k, klen) == 0) {\n\t\t\t\tint i;\n\t\t\t\tbool match = false;\n\t\t\t\tfor (i = 0; i < metadata_thp_mode_limit; i++) {\n\t\t\t\t\tif (strncmp(metadata_thp_mode_names[i],\n\t\t\t\t\t    v, vlen) == 0) {\n\t\t\t\t\t\topt_metadata_thp = i;\n\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!match) {\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_BOOL(opt_retain, \"retain\")\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 (extent_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) {\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_UNSIGNED(opt_narenas, \"narenas\", 1,\n\t\t\t    UINT_MAX, yes, no, false)\n\t\t\tCONF_HANDLE_SSIZE_T(opt_dirty_decay_ms,\n\t\t\t    \"dirty_decay_ms\", -1, NSTIME_SEC_MAX * KQU(1000) <\n\t\t\t    QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :\n\t\t\t    SSIZE_MAX);\n\t\t\tCONF_HANDLE_SSIZE_T(opt_muzzy_decay_ms,\n\t\t\t    \"muzzy_decay_ms\", -1, NSTIME_SEC_MAX * KQU(1000) <\n\t\t\t    QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :\n\t\t\t    SSIZE_MAX);\n\t\t\tCONF_HANDLE_BOOL(opt_stats_print, \"stats_print\")\n\t\t\tif (CONF_MATCH(\"stats_print_opts\")) {\n\t\t\t\tinit_opt_stats_print_opts(v, vlen);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (config_fill) {\n\t\t\t\tif (CONF_MATCH(\"junk\")) {\n\t\t\t\t\tif (CONF_MATCH_VALUE(\"true\")) {\n\t\t\t\t\t\topt_junk = \"true\";\n\t\t\t\t\t\topt_junk_alloc = opt_junk_free =\n\t\t\t\t\t\t    true;\n\t\t\t\t\t} else if (CONF_MATCH_VALUE(\"false\")) {\n\t\t\t\t\t\topt_junk = \"false\";\n\t\t\t\t\t\topt_junk_alloc = opt_junk_free =\n\t\t\t\t\t\t    false;\n\t\t\t\t\t} else if (CONF_MATCH_VALUE(\"alloc\")) {\n\t\t\t\t\t\topt_junk = \"alloc\";\n\t\t\t\t\t\topt_junk_alloc = true;\n\t\t\t\t\t\topt_junk_free = false;\n\t\t\t\t\t} else if (CONF_MATCH_VALUE(\"free\")) {\n\t\t\t\t\t\topt_junk = \"free\";\n\t\t\t\t\t\topt_junk_alloc = false;\n\t\t\t\t\t\topt_junk_free = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t    \"Invalid conf value\", k,\n\t\t\t\t\t\t    klen, v, vlen);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\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_xmalloc) {\n\t\t\t\tCONF_HANDLE_BOOL(opt_xmalloc, \"xmalloc\")\n\t\t\t}\n\t\t\tCONF_HANDLE_BOOL(opt_tcache, \"tcache\")\n\t\t\tCONF_HANDLE_SIZE_T(opt_lg_extent_max_active_fit,\n\t\t\t    \"lg_extent_max_active_fit\", 0,\n\t\t\t    (sizeof(size_t) << 3), yes, yes, false)\n\t\t\tCONF_HANDLE_SSIZE_T(opt_lg_tcache_max, \"lg_tcache_max\",\n\t\t\t    -1, (sizeof(size_t) << 3) - 1)\n\t\t\tif (strncmp(\"percpu_arena\", k, klen) == 0) {\n\t\t\t\tbool match = false;\n\t\t\t\tfor (int i = percpu_arena_mode_names_base; i <\n\t\t\t\t    percpu_arena_mode_names_limit; i++) {\n\t\t\t\t\tif (strncmp(percpu_arena_mode_names[i],\n\t\t\t\t\t    v, vlen) == 0) {\n\t\t\t\t\t\tif (!have_percpu_arena) {\n\t\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t\t    \"No getcpu support\",\n\t\t\t\t\t\t\t    k, klen, v, vlen);\n\t\t\t\t\t\t}\n\t\t\t\t\t\topt_percpu_arena = i;\n\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!match) {\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_BOOL(opt_background_thread,\n\t\t\t    \"background_thread\");\n\t\t\tCONF_HANDLE_SIZE_T(opt_max_background_threads,\n\t\t\t\t\t   \"max_background_threads\", 1,\n\t\t\t\t\t   opt_max_background_threads, yes, yes,\n\t\t\t\t\t   true);\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_BOOL(opt_prof_thread_active_init,\n\t\t\t\t    \"prof_thread_active_init\")\n\t\t\t\tCONF_HANDLE_SIZE_T(opt_lg_prof_sample,\n\t\t\t\t    \"lg_prof_sample\", 0, (sizeof(uint64_t) << 3)\n\t\t\t\t    - 1, no, yes, true)\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\tif (config_log) {\n\t\t\t\tif (CONF_MATCH(\"log\")) {\n\t\t\t\t\tsize_t cpylen = (\n\t\t\t\t\t    vlen <= sizeof(log_var_names) ?\n\t\t\t\t\t    vlen : sizeof(log_var_names) - 1);\n\t\t\t\t\tstrncpy(log_var_names, v, cpylen);\n\t\t\t\t\tlog_var_names[cpylen] = '\\0';\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (CONF_MATCH(\"thp\")) {\n\t\t\t\tbool match = false;\n\t\t\t\tfor (int i = 0; i < thp_mode_names_limit; i++) {\n\t\t\t\t\tif (strncmp(thp_mode_names[i],v, vlen)\n\t\t\t\t\t    == 0) {\n\t\t\t\t\t\tif (!have_madvise_huge) {\n\t\t\t\t\t\t\tmalloc_conf_error(\n\t\t\t\t\t\t\t    \"No THP support\",\n\t\t\t\t\t\t\t    k, klen, v, vlen);\n\t\t\t\t\t\t}\n\t\t\t\t\t\topt_thp = i;\n\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!match) {\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\tmalloc_conf_error(\"Invalid conf pair\", k, klen, v,\n\t\t\t    vlen);\n#undef CONF_MATCH\n#undef CONF_MATCH_VALUE\n#undef CONF_HANDLE_BOOL\n#undef CONF_MIN_no\n#undef CONF_MIN_yes\n#undef CONF_MAX_no\n#undef CONF_MAX_yes\n#undef CONF_HANDLE_T_U\n#undef CONF_HANDLE_UNSIGNED\n#undef CONF_HANDLE_SIZE_T\n#undef CONF_HANDLE_SSIZE_T\n#undef CONF_HANDLE_CHAR_P\n\t\t}\n\t\tif (opt_abort_conf && had_conf_error) {\n\t\t\tmalloc_abort_invalid_conf();\n\t\t}\n\t}\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELEASE);\n}\n\nstatic bool\nmalloc_init_hard_needed(void) {\n\tif (malloc_initialized() || (IS_INITIALIZER && malloc_init_state ==\n\t    malloc_init_recursible)) {\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\treturn false;\n\t}\n#ifdef JEMALLOC_THREADED_INIT\n\tif (malloc_initializer != NO_INITIALIZER && !IS_INITIALIZER) {\n\t\t/* Busy-wait until the initializing thread completes. */\n\t\tspin_t spinner = SPIN_INITIALIZER;\n\t\tdo {\n\t\t\tmalloc_mutex_unlock(TSDN_NULL, &init_lock);\n\t\t\tspin_adaptive(&spinner);\n\t\t\tmalloc_mutex_lock(TSDN_NULL, &init_lock);\n\t\t} while (!malloc_initialized());\n\t\treturn false;\n\t}\n#endif\n\treturn true;\n}\n\nstatic bool\nmalloc_init_hard_a0_locked() {\n\tmalloc_initializer = INITIALIZER;\n\n\tif (config_prof) {\n\t\tprof_boot0();\n\t}\n\tmalloc_conf_init();\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\t}\n\t\t}\n\t}\n\tif (pages_boot()) {\n\t\treturn true;\n\t}\n\tif (base_boot(TSDN_NULL)) {\n\t\treturn true;\n\t}\n\tif (extent_boot()) {\n\t\treturn true;\n\t}\n\tif (ctl_boot()) {\n\t\treturn true;\n\t}\n\tif (config_prof) {\n\t\tprof_boot1();\n\t}\n\tarena_boot();\n\tif (tcache_boot(TSDN_NULL)) {\n\t\treturn true;\n\t}\n\tif (malloc_mutex_init(&arenas_lock, \"arenas\", WITNESS_RANK_ARENAS,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\t/*\n\t * Create enough scaffolding to allow recursive allocation in\n\t * malloc_ncpus().\n\t */\n\tnarenas_auto = 1;\n\tmemset(arenas, 0, sizeof(arena_t *) * narenas_auto);\n\t/*\n\t * Initialize one arena here.  The rest are lazily created in\n\t * arena_choose_hard().\n\t */\n\tif (arena_init(TSDN_NULL, 0, (extent_hooks_t *)&extent_hooks_default)\n\t    == NULL) {\n\t\treturn true;\n\t}\n\ta0 = arena_get(TSDN_NULL, 0, false);\n\tmalloc_init_state = malloc_init_a0_initialized;\n\n\treturn false;\n}\n\nstatic bool\nmalloc_init_hard_a0(void) {\n\tbool ret;\n\n\tmalloc_mutex_lock(TSDN_NULL, &init_lock);\n\tret = malloc_init_hard_a0_locked();\n\tmalloc_mutex_unlock(TSDN_NULL, &init_lock);\n\treturn ret;\n}\n\n/* Initialize data structures which may trigger recursive allocation. */\nstatic bool\nmalloc_init_hard_recursible(void) {\n\tmalloc_init_state = malloc_init_recursible;\n\n\tncpus = malloc_ncpus();\n\n#if (defined(JEMALLOC_HAVE_PTHREAD_ATFORK) && !defined(JEMALLOC_MUTEX_INIT_CB) \\\n    && !defined(JEMALLOC_ZONE) && !defined(_WIN32) && \\\n    !defined(__native_client__))\n\t/* LinuxThreads' pthread_atfork() allocates. */\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\t}\n\t\treturn true;\n\t}\n#endif\n\n\tif (background_thread_boot0()) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic unsigned\nmalloc_narenas_default(void) {\n\tassert(ncpus > 0);\n\t/*\n\t * For SMP systems, create more than one arena per CPU by\n\t * default.\n\t */\n\tif (ncpus > 1) {\n\t\treturn ncpus << 2;\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nstatic percpu_arena_mode_t\npercpu_arena_as_initialized(percpu_arena_mode_t mode) {\n\tassert(!malloc_initialized());\n\tassert(mode <= percpu_arena_disabled);\n\n\tif (mode != percpu_arena_disabled) {\n\t\tmode += percpu_arena_mode_enabled_base;\n\t}\n\n\treturn mode;\n}\n\nstatic bool\nmalloc_init_narenas(void) {\n\tassert(ncpus > 0);\n\n\tif (opt_percpu_arena != percpu_arena_disabled) {\n\t\tif (!have_percpu_arena || malloc_getcpu() < 0) {\n\t\t\topt_percpu_arena = percpu_arena_disabled;\n\t\t\tmalloc_printf(\"<jemalloc>: perCPU arena getcpu() not \"\n\t\t\t    \"available. Setting narenas to %u.\\n\", opt_narenas ?\n\t\t\t    opt_narenas : malloc_narenas_default());\n\t\t\tif (opt_abort) {\n\t\t\t\tabort();\n\t\t\t}\n\t\t} else {\n\t\t\tif (ncpus >= MALLOCX_ARENA_LIMIT) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: narenas w/ percpu\"\n\t\t\t\t    \"arena beyond limit (%d)\\n\", ncpus);\n\t\t\t\tif (opt_abort) {\n\t\t\t\t\tabort();\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t/* NB: opt_percpu_arena isn't fully initialized yet. */\n\t\t\tif (percpu_arena_as_initialized(opt_percpu_arena) ==\n\t\t\t    per_phycpu_arena && ncpus % 2 != 0) {\n\t\t\t\tmalloc_printf(\"<jemalloc>: invalid \"\n\t\t\t\t    \"configuration -- per physical CPU arena \"\n\t\t\t\t    \"with odd number (%u) of CPUs (no hyper \"\n\t\t\t\t    \"threading?).\\n\", ncpus);\n\t\t\t\tif (opt_abort)\n\t\t\t\t\tabort();\n\t\t\t}\n\t\t\tunsigned n = percpu_arena_ind_limit(\n\t\t\t    percpu_arena_as_initialized(opt_percpu_arena));\n\t\t\tif (opt_narenas < n) {\n\t\t\t\t/*\n\t\t\t\t * If narenas is specified with percpu_arena\n\t\t\t\t * enabled, actual narenas is set as the greater\n\t\t\t\t * of the two. percpu_arena_choose will be free\n\t\t\t\t * to use any of the arenas based on CPU\n\t\t\t\t * id. This is conservative (at a small cost)\n\t\t\t\t * but ensures correctness.\n\t\t\t\t *\n\t\t\t\t * If for some reason the ncpus determined at\n\t\t\t\t * boot is not the actual number (e.g. because\n\t\t\t\t * of affinity setting from numactl), reserving\n\t\t\t\t * narenas this way provides a workaround for\n\t\t\t\t * percpu_arena.\n\t\t\t\t */\n\t\t\t\topt_narenas = n;\n\t\t\t}\n\t\t}\n\t}\n\tif (opt_narenas == 0) {\n\t\topt_narenas = malloc_narenas_default();\n\t}\n\tassert(opt_narenas > 0);\n\n\tnarenas_auto = opt_narenas;\n\t/*\n\t * Limit the number of arenas to the indexing range of MALLOCX_ARENA().\n\t */\n\tif (narenas_auto >= MALLOCX_ARENA_LIMIT) {\n\t\tnarenas_auto = MALLOCX_ARENA_LIMIT - 1;\n\t\tmalloc_printf(\"<jemalloc>: Reducing narenas to limit (%d)\\n\",\n\t\t    narenas_auto);\n\t}\n\tnarenas_total_set(narenas_auto);\n\n\treturn false;\n}\n\nstatic void\nmalloc_init_percpu(void) {\n\topt_percpu_arena = percpu_arena_as_initialized(opt_percpu_arena);\n}\n\nstatic bool\nmalloc_init_hard_finish(void) {\n\tif (malloc_mutex_boot()) {\n\t\treturn true;\n\t}\n\n\tmalloc_init_state = malloc_init_initialized;\n\tmalloc_slow_flag_init();\n\n\treturn false;\n}\n\nstatic void\nmalloc_init_hard_cleanup(tsdn_t *tsdn, bool reentrancy_set) {\n\tmalloc_mutex_assert_owner(tsdn, &init_lock);\n\tmalloc_mutex_unlock(tsdn, &init_lock);\n\tif (reentrancy_set) {\n\t\tassert(!tsdn_null(tsdn));\n\t\ttsd_t *tsd = tsdn_tsd(tsdn);\n\t\tassert(tsd_reentrancy_level_get(tsd) > 0);\n\t\tpost_reentrancy(tsd);\n\t}\n}\n\nstatic bool\nmalloc_init_hard(void) {\n\ttsd_t *tsd;\n\n#if defined(_WIN32) && _WIN32_WINNT < 0x0600\n\t_init_init_lock();\n#endif\n\tmalloc_mutex_lock(TSDN_NULL, &init_lock);\n\n#define UNLOCK_RETURN(tsdn, ret, reentrancy)\t\t\\\n\tmalloc_init_hard_cleanup(tsdn, reentrancy);\t\\\n\treturn ret;\n\n\tif (!malloc_init_hard_needed()) {\n\t\tUNLOCK_RETURN(TSDN_NULL, false, false)\n\t}\n\n\tif (malloc_init_state != malloc_init_a0_initialized &&\n\t    malloc_init_hard_a0_locked()) {\n\t\tUNLOCK_RETURN(TSDN_NULL, true, false)\n\t}\n\n\tmalloc_mutex_unlock(TSDN_NULL, &init_lock);\n\t/* Recursive allocation relies on functional tsd. */\n\ttsd = malloc_tsd_boot0();\n\tif (tsd == NULL) {\n\t\treturn true;\n\t}\n\tif (malloc_init_hard_recursible()) {\n\t\treturn true;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &init_lock);\n\t/* Set reentrancy level to 1 during init. */\n\tpre_reentrancy(tsd, NULL);\n\t/* Initialize narenas before prof_boot2 (for allocation). */\n\tif (malloc_init_narenas() || background_thread_boot1(tsd_tsdn(tsd))) {\n\t\tUNLOCK_RETURN(tsd_tsdn(tsd), true, true)\n\t}\n\tif (config_prof && prof_boot2(tsd)) {\n\t\tUNLOCK_RETURN(tsd_tsdn(tsd), true, true)\n\t}\n\n\tmalloc_init_percpu();\n\n\tif (malloc_init_hard_finish()) {\n\t\tUNLOCK_RETURN(tsd_tsdn(tsd), true, true)\n\t}\n\tpost_reentrancy(tsd);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &init_lock);\n\n\twitness_assert_lockless(witness_tsd_tsdn(\n\t    tsd_witness_tsdp_get_unsafe(tsd)));\n\tmalloc_tsd_boot1();\n\t/* Update TSD after tsd_boot1. */\n\ttsd = tsd_fetch();\n\tif (opt_background_thread) {\n\t\tassert(have_background_thread);\n\t\t/*\n\t\t * Need to finish init & unlock first before creating background\n\t\t * threads (pthread_create depends on malloc).  ctl_init (which\n\t\t * sets isthreaded) needs to be called without holding any lock.\n\t\t */\n\t\tbackground_thread_ctl_init(tsd_tsdn(tsd));\n\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);\n\t\tbool err = background_thread_create(tsd, 0);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);\n\t\tif (err) {\n\t\t\treturn true;\n\t\t}\n\t}\n#undef UNLOCK_RETURN\n\treturn false;\n}\n\n/*\n * End initialization functions.\n */\n/******************************************************************************/\n/*\n * Begin allocation-path internal functions and data structures.\n */\n\n/*\n * Settings determined by the documented behavior of the allocation functions.\n */\ntypedef struct static_opts_s static_opts_t;\nstruct static_opts_s {\n\t/* Whether or not allocation size may overflow. */\n\tbool may_overflow;\n\t/* Whether or not allocations of size 0 should be treated as size 1. */\n\tbool bump_empty_alloc;\n\t/*\n\t * Whether to assert that allocations are not of size 0 (after any\n\t * bumping).\n\t */\n\tbool assert_nonempty_alloc;\n\n\t/*\n\t * Whether or not to modify the 'result' argument to malloc in case of\n\t * error.\n\t */\n\tbool null_out_result_on_error;\n\t/* Whether to set errno when we encounter an error condition. */\n\tbool set_errno_on_error;\n\n\t/*\n\t * The minimum valid alignment for functions requesting aligned storage.\n\t */\n\tsize_t min_alignment;\n\n\t/* The error string to use if we oom. */\n\tconst char *oom_string;\n\t/* The error string to use if the passed-in alignment is invalid. */\n\tconst char *invalid_alignment_string;\n\n\t/*\n\t * False if we're configured to skip some time-consuming operations.\n\t *\n\t * This isn't really a malloc \"behavior\", but it acts as a useful\n\t * summary of several other static (or at least, static after program\n\t * initialization) options.\n\t */\n\tbool slow;\n};\n\nJEMALLOC_ALWAYS_INLINE void\nstatic_opts_init(static_opts_t *static_opts) {\n\tstatic_opts->may_overflow = false;\n\tstatic_opts->bump_empty_alloc = false;\n\tstatic_opts->assert_nonempty_alloc = false;\n\tstatic_opts->null_out_result_on_error = false;\n\tstatic_opts->set_errno_on_error = false;\n\tstatic_opts->min_alignment = 0;\n\tstatic_opts->oom_string = \"\";\n\tstatic_opts->invalid_alignment_string = \"\";\n\tstatic_opts->slow = false;\n}\n\n/*\n * These correspond to the macros in jemalloc/jemalloc_macros.h.  Broadly, we\n * should have one constant here per magic value there.  Note however that the\n * representations need not be related.\n */\n#define TCACHE_IND_NONE ((unsigned)-1)\n#define TCACHE_IND_AUTOMATIC ((unsigned)-2)\n#define ARENA_IND_AUTOMATIC ((unsigned)-1)\n\ntypedef struct dynamic_opts_s dynamic_opts_t;\nstruct dynamic_opts_s {\n\tvoid **result;\n\tsize_t num_items;\n\tsize_t item_size;\n\tsize_t alignment;\n\tbool zero;\n\tunsigned tcache_ind;\n\tunsigned arena_ind;\n};\n\nJEMALLOC_ALWAYS_INLINE void\ndynamic_opts_init(dynamic_opts_t *dynamic_opts) {\n\tdynamic_opts->result = NULL;\n\tdynamic_opts->num_items = 0;\n\tdynamic_opts->item_size = 0;\n\tdynamic_opts->alignment = 0;\n\tdynamic_opts->zero = false;\n\tdynamic_opts->tcache_ind = TCACHE_IND_AUTOMATIC;\n\tdynamic_opts->arena_ind = ARENA_IND_AUTOMATIC;\n}\n\n/* ind is ignored if dopts->alignment > 0. */\nJEMALLOC_ALWAYS_INLINE void *\nimalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,\n    size_t size, size_t usize, szind_t ind) {\n\ttcache_t *tcache;\n\tarena_t *arena;\n\n\t/* Fill in the tcache. */\n\tif (dopts->tcache_ind == TCACHE_IND_AUTOMATIC) {\n\t\tif (likely(!sopts->slow)) {\n\t\t\t/* Getting tcache ptr unconditionally. */\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tassert(tcache == tcache_get(tsd));\n\t\t} else {\n\t\t\ttcache = tcache_get(tsd);\n\t\t}\n\t} else if (dopts->tcache_ind == TCACHE_IND_NONE) {\n\t\ttcache = NULL;\n\t} else {\n\t\ttcache = tcaches_get(tsd, dopts->tcache_ind);\n\t}\n\n\t/* Fill in the arena. */\n\tif (dopts->arena_ind == ARENA_IND_AUTOMATIC) {\n\t\t/*\n\t\t * In case of automatic arena management, we defer arena\n\t\t * computation until as late as we can, hoping to fill the\n\t\t * allocation out of the tcache.\n\t\t */\n\t\tarena = NULL;\n\t} else {\n\t\tarena = arena_get(tsd_tsdn(tsd), dopts->arena_ind, true);\n\t}\n\n\tif (unlikely(dopts->alignment != 0)) {\n\t\treturn ipalloct(tsd_tsdn(tsd), usize, dopts->alignment,\n\t\t    dopts->zero, tcache, arena);\n\t}\n\n\treturn iallocztm(tsd_tsdn(tsd), size, ind, dopts->zero, tcache, false,\n\t    arena, sopts->slow);\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nimalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,\n    size_t usize, szind_t ind) {\n\tvoid *ret;\n\n\t/*\n\t * For small allocations, sampling bumps the usize.  If so, we allocate\n\t * from the ind_large bucket.\n\t */\n\tszind_t ind_large;\n\tsize_t bumped_usize = usize;\n\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tassert(((dopts->alignment == 0) ? sz_s2u(LARGE_MINCLASS) :\n\t\t    sz_sa2u(LARGE_MINCLASS, dopts->alignment))\n\t\t    == LARGE_MINCLASS);\n\t\tind_large = sz_size2index(LARGE_MINCLASS);\n\t\tbumped_usize = sz_s2u(LARGE_MINCLASS);\n\t\tret = imalloc_no_sample(sopts, dopts, tsd, bumped_usize,\n\t\t    bumped_usize, ind_large);\n\t\tif (unlikely(ret == NULL)) {\n\t\t\treturn NULL;\n\t\t}\n\t\tarena_prof_promote(tsd_tsdn(tsd), ret, usize);\n\t} else {\n\t\tret = imalloc_no_sample(sopts, dopts, tsd, usize, usize, ind);\n\t}\n\n\treturn ret;\n}\n\n/*\n * Returns true if the allocation will overflow, and false otherwise.  Sets\n * *size to the product either way.\n */\nJEMALLOC_ALWAYS_INLINE bool\ncompute_size_with_overflow(bool may_overflow, dynamic_opts_t *dopts,\n    size_t *size) {\n\t/*\n\t * This function is just num_items * item_size, except that we may have\n\t * to check for overflow.\n\t */\n\n\tif (!may_overflow) {\n\t\tassert(dopts->num_items == 1);\n\t\t*size = dopts->item_size;\n\t\treturn false;\n\t}\n\n\t/* A size_t with its high-half bits all set to 1. */\n\tstatic const size_t high_bits = SIZE_T_MAX << (sizeof(size_t) * 8 / 2);\n\n\t*size = dopts->item_size * dopts->num_items;\n\n\tif (unlikely(*size == 0)) {\n\t\treturn (dopts->num_items != 0 && dopts->item_size != 0);\n\t}\n\n\t/*\n\t * We got a non-zero size, but we don't know if we overflowed to get\n\t * there.  To avoid having to do a divide, we'll be clever and note that\n\t * if both A and B can be represented in N/2 bits, then their product\n\t * can be represented in N bits (without the possibility of overflow).\n\t */\n\tif (likely((high_bits & (dopts->num_items | dopts->item_size)) == 0)) {\n\t\treturn false;\n\t}\n\tif (likely(*size / dopts->item_size == dopts->num_items)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nJEMALLOC_ALWAYS_INLINE int\nimalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {\n\t/* Where the actual allocated memory will live. */\n\tvoid *allocation = NULL;\n\t/* Filled in by compute_size_with_overflow below. */\n\tsize_t size = 0;\n\t/*\n\t * For unaligned allocations, we need only ind.  For aligned\n\t * allocations, or in case of stats or profiling we need usize.\n\t *\n\t * These are actually dead stores, in that their values are reset before\n\t * any branch on their value is taken.  Sometimes though, it's\n\t * convenient to pass them as arguments before this point.  To avoid\n\t * undefined behavior then, we initialize them with dummy stores.\n\t */\n\tszind_t ind = 0;\n\tsize_t usize = 0;\n\n\t/* Reentrancy is only checked on slow path. */\n\tint8_t reentrancy_level;\n\n\t/* Compute the amount of memory the user wants. */\n\tif (unlikely(compute_size_with_overflow(sopts->may_overflow, dopts,\n\t    &size))) {\n\t\tgoto label_oom;\n\t}\n\n\t/* Validate the user input. */\n\tif (sopts->bump_empty_alloc) {\n\t\tif (unlikely(size == 0)) {\n\t\t\tsize = 1;\n\t\t}\n\t}\n\n\tif (sopts->assert_nonempty_alloc) {\n\t\tassert (size != 0);\n\t}\n\n\tif (unlikely(dopts->alignment < sopts->min_alignment\n\t    || (dopts->alignment & (dopts->alignment - 1)) != 0)) {\n\t\tgoto label_invalid_alignment;\n\t}\n\n\t/* This is the beginning of the \"core\" algorithm. */\n\n\tif (dopts->alignment == 0) {\n\t\tind = sz_size2index(size);\n\t\tif (unlikely(ind >= NSIZES)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t\tif (config_stats || (config_prof && opt_prof)) {\n\t\t\tusize = sz_index2size(ind);\n\t\t\tassert(usize > 0 && usize <= LARGE_MAXCLASS);\n\t\t}\n\t} else {\n\t\tusize = sz_sa2u(size, dopts->alignment);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t}\n\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\t/*\n\t * If we need to handle reentrancy, we can do it out of a\n\t * known-initialized arena (i.e. arena 0).\n\t */\n\treentrancy_level = tsd_reentrancy_level_get(tsd);\n\tif (sopts->slow && unlikely(reentrancy_level > 0)) {\n\t\t/*\n\t\t * We should never specify particular arenas or tcaches from\n\t\t * within our internal allocations.\n\t\t */\n\t\tassert(dopts->tcache_ind == TCACHE_IND_AUTOMATIC ||\n\t\t    dopts->tcache_ind == TCACHE_IND_NONE);\n\t\tassert(dopts->arena_ind == ARENA_IND_AUTOMATIC);\n\t\tdopts->tcache_ind = TCACHE_IND_NONE;\n\t\t/* We know that arena 0 has already been initialized. */\n\t\tdopts->arena_ind = 0;\n\t}\n\n\t/* If profiling is on, get our profiling context. */\n\tif (config_prof && opt_prof) {\n\t\t/*\n\t\t * Note that if we're going down this path, usize must have been\n\t\t * initialized in the previous if statement.\n\t\t */\n\t\tprof_tctx_t *tctx = prof_alloc_prep(\n\t\t    tsd, usize, prof_active_get_unlocked(), true);\n\n\t\talloc_ctx_t alloc_ctx;\n\t\tif (likely((uintptr_t)tctx == (uintptr_t)1U)) {\n\t\t\talloc_ctx.slab = (usize <= SMALL_MAXCLASS);\n\t\t\tallocation = imalloc_no_sample(\n\t\t\t    sopts, dopts, tsd, usize, usize, ind);\n\t\t} else if ((uintptr_t)tctx > (uintptr_t)1U) {\n\t\t\t/*\n\t\t\t * Note that ind might still be 0 here.  This is fine;\n\t\t\t * imalloc_sample ignores ind if dopts->alignment > 0.\n\t\t\t */\n\t\t\tallocation = imalloc_sample(\n\t\t\t    sopts, dopts, tsd, usize, ind);\n\t\t\talloc_ctx.slab = false;\n\t\t} else {\n\t\t\tallocation = NULL;\n\t\t}\n\n\t\tif (unlikely(allocation == NULL)) {\n\t\t\tprof_alloc_rollback(tsd, tctx, true);\n\t\t\tgoto label_oom;\n\t\t}\n\t\tprof_malloc(tsd_tsdn(tsd), allocation, usize, &alloc_ctx, tctx);\n\t} else {\n\t\t/*\n\t\t * If dopts->alignment > 0, then ind is still 0, but usize was\n\t\t * computed in the previous if statement.  Down the positive\n\t\t * alignment path, imalloc_no_sample ignores ind and size\n\t\t * (relying only on usize).\n\t\t */\n\t\tallocation = imalloc_no_sample(sopts, dopts, tsd, size, usize,\n\t\t    ind);\n\t\tif (unlikely(allocation == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t}\n\n\t/*\n\t * Allocation has been done at this point.  We still have some\n\t * post-allocation work to do though.\n\t */\n\tassert(dopts->alignment == 0\n\t    || ((uintptr_t)allocation & (dopts->alignment - 1)) == ZU(0));\n\n\tif (config_stats) {\n\t\tassert(usize == isalloc(tsd_tsdn(tsd), allocation));\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t}\n\n\tif (sopts->slow) {\n\t\tUTRACE(0, size, allocation);\n\t}\n\n\t/* Success! */\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\t*dopts->result = allocation;\n\treturn 0;\n\nlabel_oom:\n\tif (unlikely(sopts->slow) && config_xmalloc && unlikely(opt_xmalloc)) {\n\t\tmalloc_write(sopts->oom_string);\n\t\tabort();\n\t}\n\n\tif (sopts->slow) {\n\t\tUTRACE(NULL, size, NULL);\n\t}\n\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tif (sopts->set_errno_on_error) {\n\t\tset_errno(ENOMEM);\n\t}\n\n\tif (sopts->null_out_result_on_error) {\n\t\t*dopts->result = NULL;\n\t}\n\n\treturn ENOMEM;\n\n\t/*\n\t * This label is only jumped to by one goto; we move it out of line\n\t * anyways to avoid obscuring the non-error paths, and for symmetry with\n\t * the oom case.\n\t */\nlabel_invalid_alignment:\n\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\tmalloc_write(sopts->invalid_alignment_string);\n\t\tabort();\n\t}\n\n\tif (sopts->set_errno_on_error) {\n\t\tset_errno(EINVAL);\n\t}\n\n\tif (sopts->slow) {\n\t\tUTRACE(NULL, size, NULL);\n\t}\n\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tif (sopts->null_out_result_on_error) {\n\t\t*dopts->result = NULL;\n\t}\n\n\treturn EINVAL;\n}\n\n/* Returns the errno-style error code of the allocation. */\nJEMALLOC_ALWAYS_INLINE int\nimalloc(static_opts_t *sopts, dynamic_opts_t *dopts) {\n\tif (unlikely(!malloc_initialized()) && unlikely(malloc_init())) {\n\t\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\t\tmalloc_write(sopts->oom_string);\n\t\t\tabort();\n\t\t}\n\t\tUTRACE(NULL, dopts->num_items * dopts->item_size, NULL);\n\t\tset_errno(ENOMEM);\n\t\t*dopts->result = NULL;\n\n\t\treturn ENOMEM;\n\t}\n\n\t/* We always need the tsd.  Let's grab it right away. */\n\ttsd_t *tsd = tsd_fetch();\n\tassert(tsd);\n\tif (likely(tsd_fast(tsd))) {\n\t\t/* Fast and common path. */\n\t\ttsd_assert_fast(tsd);\n\t\tsopts->slow = false;\n\t\treturn imalloc_body(sopts, dopts, tsd);\n\t} else {\n\t\tsopts->slow = true;\n\t\treturn imalloc_body(sopts, dopts, tsd);\n\t}\n}\n/******************************************************************************/\n/*\n * Begin malloc(3)-compatible functions.\n */\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)\nje_malloc(size_t size) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.malloc.entry\", \"size: %zu\", size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.set_errno_on_error = true;\n\tsopts.oom_string = \"<jemalloc>: Error in malloc(): out of memory\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.malloc.exit\", \"result: %p\", ret);\n\n\treturn ret;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nJEMALLOC_ATTR(nonnull(1))\nje_posix_memalign(void **memptr, size_t alignment, size_t size) {\n\tint ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.posix_memalign.entry\", \"mem ptr: %p, alignment: %zu, \"\n\t    \"size: %zu\", memptr, alignment, size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.min_alignment = sizeof(void *);\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\n\tdopts.result = memptr;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = alignment;\n\n\tret = imalloc(&sopts, &dopts);\n\n\tLOG(\"core.posix_memalign.exit\", \"result: %d, alloc ptr: %p\", ret,\n\t    *memptr);\n\n\treturn ret;\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2)\nje_aligned_alloc(size_t alignment, size_t size) {\n\tvoid *ret;\n\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.aligned_alloc.entry\", \"alignment: %zu, size: %zu\\n\",\n\t    alignment, size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.set_errno_on_error = true;\n\tsopts.min_alignment = 1;\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = alignment;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.aligned_alloc.exit\", \"result: %p\", ret);\n\n\treturn ret;\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2)\nje_calloc(size_t num, size_t size) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.calloc.entry\", \"num: %zu, size: %zu\\n\", num, size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.may_overflow = true;\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.set_errno_on_error = true;\n\tsopts.oom_string = \"<jemalloc>: Error in calloc(): out of memory\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = num;\n\tdopts.item_size = size;\n\tdopts.zero = true;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.calloc.exit\", \"result: %p\", ret);\n\n\treturn ret;\n}\n\nstatic void *\nirealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,\n    prof_tctx_t *tctx) {\n\tvoid *p;\n\n\tif (tctx == NULL) {\n\t\treturn NULL;\n\t}\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tp = iralloc(tsd, old_ptr, old_usize, LARGE_MINCLASS, 0, false);\n\t\tif (p == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tarena_prof_promote(tsd_tsdn(tsd), p, usize);\n\t} else {\n\t\tp = iralloc(tsd, old_ptr, old_usize, usize, 0, false);\n\t}\n\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nirealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,\n   alloc_ctx_t *alloc_ctx) {\n\tvoid *p;\n\tbool prof_active;\n\tprof_tctx_t *old_tctx, *tctx;\n\n\tprof_active = prof_active_get_unlocked();\n\told_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);\n\ttctx = prof_alloc_prep(tsd, usize, prof_active, true);\n\tif (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {\n\t\tp = irealloc_prof_sample(tsd, old_ptr, old_usize, usize, tctx);\n\t} else {\n\t\tp = iralloc(tsd, old_ptr, old_usize, usize, 0, false);\n\t}\n\tif (unlikely(p == NULL)) {\n\t\tprof_alloc_rollback(tsd, tctx, true);\n\t\treturn NULL;\n\t}\n\tprof_realloc(tsd, p, usize, tctx, prof_active, true, old_ptr, old_usize,\n\t    old_tctx);\n\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void\nifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) {\n\tif (!slow_path) {\n\t\ttsd_assert_fast(tsd);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tif (tsd_reentrancy_level_get(tsd) != 0) {\n\t\tassert(slow_path);\n\t}\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\talloc_ctx_t alloc_ctx;\n\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\tassert(alloc_ctx.szind != NSIZES);\n\n\tsize_t usize;\n\tif (config_prof && opt_prof) {\n\t\tusize = sz_index2size(alloc_ctx.szind);\n\t\tprof_free(tsd, ptr, usize, &alloc_ctx);\n\t} else if (config_stats) {\n\t\tusize = sz_index2size(alloc_ctx.szind);\n\t}\n\tif (config_stats) {\n\t\t*tsd_thread_deallocatedp_get(tsd) += usize;\n\t}\n\n\tif (likely(!slow_path)) {\n\t\tidalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,\n\t\t    false);\n\t} else {\n\t\tidalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,\n\t\t    true);\n\t}\n}\n\nJEMALLOC_ALWAYS_INLINE void\nisfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache, bool slow_path) {\n\tif (!slow_path) {\n\t\ttsd_assert_fast(tsd);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tif (tsd_reentrancy_level_get(tsd) != 0) {\n\t\tassert(slow_path);\n\t}\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\talloc_ctx_t alloc_ctx, *ctx;\n\tif (!config_cache_oblivious && ((uintptr_t)ptr & PAGE_MASK) != 0) {\n\t\t/*\n\t\t * When cache_oblivious is disabled and ptr is not page aligned,\n\t\t * the allocation was not sampled -- usize can be used to\n\t\t * determine szind directly.\n\t\t */\n\t\talloc_ctx.szind = sz_size2index(usize);\n\t\talloc_ctx.slab = true;\n\t\tctx = &alloc_ctx;\n\t\tif (config_debug) {\n\t\t\talloc_ctx_t dbg_ctx;\n\t\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree,\n\t\t\t    rtree_ctx, (uintptr_t)ptr, true, &dbg_ctx.szind,\n\t\t\t    &dbg_ctx.slab);\n\t\t\tassert(dbg_ctx.szind == alloc_ctx.szind);\n\t\t\tassert(dbg_ctx.slab == alloc_ctx.slab);\n\t\t}\n\t} else if (config_prof && opt_prof) {\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\t\tassert(alloc_ctx.szind == sz_size2index(usize));\n\t\tctx = &alloc_ctx;\n\t} else {\n\t\tctx = NULL;\n\t}\n\n\tif (config_prof && opt_prof) {\n\t\tprof_free(tsd, ptr, usize, ctx);\n\t}\n\tif (config_stats) {\n\t\t*tsd_thread_deallocatedp_get(tsd) += usize;\n\t}\n\n\tif (likely(!slow_path)) {\n\t\tisdalloct(tsd_tsdn(tsd), ptr, usize, tcache, ctx, false);\n\t} else {\n\t\tisdalloct(tsd_tsdn(tsd), ptr, usize, tcache, ctx, true);\n\t}\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ALLOC_SIZE(2)\nje_realloc(void *ptr, size_t size) {\n\tvoid *ret;\n\ttsdn_t *tsdn JEMALLOC_CC_SILENCE_INIT(NULL);\n\tsize_t usize JEMALLOC_CC_SILENCE_INIT(0);\n\tsize_t old_usize = 0;\n\n\tLOG(\"core.realloc.entry\", \"ptr: %p, size: %zu\\n\", ptr, size);\n\n\tif (unlikely(size == 0)) {\n\t\tif (ptr != NULL) {\n\t\t\t/* realloc(ptr, 0) is equivalent to free(ptr). */\n\t\t\tUTRACE(ptr, 0, 0);\n\t\t\ttcache_t *tcache;\n\t\t\ttsd_t *tsd = tsd_fetch();\n\t\t\tif (tsd_reentrancy_level_get(tsd) == 0) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t} else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t\tifree(tsd, ptr, tcache, true);\n\n\t\t\tLOG(\"core.realloc.exit\", \"result: %p\", NULL);\n\t\t\treturn NULL;\n\t\t}\n\t\tsize = 1;\n\t}\n\n\tif (likely(ptr != NULL)) {\n\t\tassert(malloc_initialized() || IS_INITIALIZER);\n\t\ttsd_t *tsd = tsd_fetch();\n\n\t\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\t\talloc_ctx_t alloc_ctx;\n\t\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\t\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\t\tassert(alloc_ctx.szind != NSIZES);\n\t\told_usize = sz_index2size(alloc_ctx.szind);\n\t\tassert(old_usize == isalloc(tsd_tsdn(tsd), ptr));\n\t\tif (config_prof && opt_prof) {\n\t\t\tusize = sz_s2u(size);\n\t\t\tret = unlikely(usize == 0 || usize > LARGE_MAXCLASS) ?\n\t\t\t    NULL : irealloc_prof(tsd, ptr, old_usize, usize,\n\t\t\t    &alloc_ctx);\n\t\t} else {\n\t\t\tif (config_stats) {\n\t\t\t\tusize = sz_s2u(size);\n\t\t\t}\n\t\t\tret = iralloc(tsd, ptr, old_usize, size, 0, false);\n\t\t}\n\t\ttsdn = tsd_tsdn(tsd);\n\t} else {\n\t\t/* realloc(NULL, size) is equivalent to malloc(size). */\n\t\tvoid *ret = je_malloc(size);\n\t\tLOG(\"core.realloc.exit\", \"result: %p\", ret);\n\t\treturn ret;\n\t}\n\n\tif (unlikely(ret == NULL)) {\n\t\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\t\tmalloc_write(\"<jemalloc>: Error in realloc(): \"\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_stats && likely(ret != NULL)) {\n\t\ttsd_t *tsd;\n\n\t\tassert(usize == isalloc(tsdn, ret));\n\t\ttsd = tsdn_tsd(tsdn);\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t\t*tsd_thread_deallocatedp_get(tsd) += old_usize;\n\t}\n\tUTRACE(ptr, size, ret);\n\tcheck_entry_exit_locking(tsdn);\n\n\tLOG(\"core.realloc.exit\", \"result: %p\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_free(void *ptr) {\n\tLOG(\"core.free.entry\", \"ptr: %p\", ptr);\n\n\tUTRACE(ptr, 0, 0);\n\tif (likely(ptr != NULL)) {\n\t\t/*\n\t\t * We avoid setting up tsd fully (e.g. tcache, arena binding)\n\t\t * based on only free() calls -- other activities trigger the\n\t\t * minimal to full transition.  This is because free() may\n\t\t * happen during thread shutdown after tls deallocation: if a\n\t\t * thread never had any malloc activities until then, a\n\t\t * fully-setup tsd won't be destructed properly.\n\t\t */\n\t\ttsd_t *tsd = tsd_fetch_min();\n\t\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\t\ttcache_t *tcache;\n\t\tif (likely(tsd_fast(tsd))) {\n\t\t\ttsd_assert_fast(tsd);\n\t\t\t/* Unconditionally get tcache ptr on fast path. */\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tifree(tsd, ptr, tcache, false);\n\t\t} else {\n\t\t\tif (likely(tsd_reentrancy_level_get(tsd) == 0)) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t} else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t\tifree(tsd, ptr, tcache, true);\n\t\t}\n\t\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\t}\n\tLOG(\"core.free.exit\", \"\");\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\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc)\nje_memalign(size_t alignment, size_t size) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.memalign.entry\", \"alignment: %zu, size: %zu\\n\", alignment,\n\t    size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.min_alignment = 1;\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\tsopts.null_out_result_on_error = true;\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = alignment;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.memalign.exit\", \"result: %p\", ret);\n\treturn ret;\n}\n#endif\n\n#ifdef JEMALLOC_OVERRIDE_VALLOC\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc)\nje_valloc(size_t size) {\n\tvoid *ret;\n\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.valloc.entry\", \"size: %zu\\n\", size);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.bump_empty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.min_alignment = PAGE;\n\tsopts.oom_string =\n\t    \"<jemalloc>: Error allocating aligned memory: out of memory\\n\";\n\tsopts.invalid_alignment_string =\n\t    \"<jemalloc>: Error allocating aligned memory: invalid alignment\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tdopts.alignment = PAGE;\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.valloc.exit\", \"result: %p\\n\", ret);\n\treturn ret;\n}\n#endif\n\n#if defined(JEMALLOC_IS_MALLOC) && defined(JEMALLOC_GLIBC_MALLOC_HOOK)\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;\n#  ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK\nJEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =\n    je_memalign;\n#  endif\n\n#  ifdef CPU_COUNT\n/*\n * To enable static linking with glibc, the libc specific malloc interface must\n * be implemented also, so none of glibc's malloc.o functions are added to the\n * link.\n */\n#    define ALIAS(je_fn)\t__attribute__((alias (#je_fn), used))\n/* To force macro expansion of je_ prefix before stringification. */\n#    define PREALIAS(je_fn)\tALIAS(je_fn)\n#    ifdef JEMALLOC_OVERRIDE___LIBC_CALLOC\nvoid *__libc_calloc(size_t n, size_t size) PREALIAS(je_calloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_FREE\nvoid __libc_free(void* ptr) PREALIAS(je_free);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_MALLOC\nvoid *__libc_malloc(size_t size) PREALIAS(je_malloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_MEMALIGN\nvoid *__libc_memalign(size_t align, size_t s) PREALIAS(je_memalign);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_REALLOC\nvoid *__libc_realloc(void* ptr, size_t size) PREALIAS(je_realloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___LIBC_VALLOC\nvoid *__libc_valloc(size_t size) PREALIAS(je_valloc);\n#    endif\n#    ifdef JEMALLOC_OVERRIDE___POSIX_MEMALIGN\nint __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);\n#    endif\n#    undef PREALIAS\n#    undef ALIAS\n#  endif\n#endif\n\n/*\n * End non-standard override functions.\n */\n/******************************************************************************/\n/*\n * Begin non-standard functions.\n */\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)\nje_mallocx(size_t size, int flags) {\n\tvoid *ret;\n\tstatic_opts_t sopts;\n\tdynamic_opts_t dopts;\n\n\tLOG(\"core.mallocx.entry\", \"size: %zu, flags: %d\", size, flags);\n\n\tstatic_opts_init(&sopts);\n\tdynamic_opts_init(&dopts);\n\n\tsopts.assert_nonempty_alloc = true;\n\tsopts.null_out_result_on_error = true;\n\tsopts.oom_string = \"<jemalloc>: Error in mallocx(): out of memory\\n\";\n\n\tdopts.result = &ret;\n\tdopts.num_items = 1;\n\tdopts.item_size = size;\n\tif (unlikely(flags != 0)) {\n\t\tif ((flags & MALLOCX_LG_ALIGN_MASK) != 0) {\n\t\t\tdopts.alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags);\n\t\t}\n\n\t\tdopts.zero = MALLOCX_ZERO_GET(flags);\n\n\t\tif ((flags & MALLOCX_TCACHE_MASK) != 0) {\n\t\t\tif ((flags & MALLOCX_TCACHE_MASK)\n\t\t\t    == MALLOCX_TCACHE_NONE) {\n\t\t\t\tdopts.tcache_ind = TCACHE_IND_NONE;\n\t\t\t} else {\n\t\t\t\tdopts.tcache_ind = MALLOCX_TCACHE_GET(flags);\n\t\t\t}\n\t\t} else {\n\t\t\tdopts.tcache_ind = TCACHE_IND_AUTOMATIC;\n\t\t}\n\n\t\tif ((flags & MALLOCX_ARENA_MASK) != 0)\n\t\t\tdopts.arena_ind = MALLOCX_ARENA_GET(flags);\n\t}\n\n\timalloc(&sopts, &dopts);\n\n\tLOG(\"core.mallocx.exit\", \"result: %p\", ret);\n\treturn ret;\n}\n\nstatic void *\nirallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,\n    size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,\n    prof_tctx_t *tctx) {\n\tvoid *p;\n\n\tif (tctx == NULL) {\n\t\treturn NULL;\n\t}\n\tif (usize <= SMALL_MAXCLASS) {\n\t\tp = iralloct(tsdn, old_ptr, old_usize, LARGE_MINCLASS,\n\t\t    alignment, zero, tcache, arena);\n\t\tif (p == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tarena_prof_promote(tsdn, p, usize);\n\t} else {\n\t\tp = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,\n\t\t    tcache, arena);\n\t}\n\n\treturn p;\n}\n\nJEMALLOC_ALWAYS_INLINE void *\nirallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,\n    size_t alignment, size_t *usize, bool zero, tcache_t *tcache,\n    arena_t *arena, alloc_ctx_t *alloc_ctx) {\n\tvoid *p;\n\tbool prof_active;\n\tprof_tctx_t *old_tctx, *tctx;\n\n\tprof_active = prof_active_get_unlocked();\n\told_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);\n\ttctx = prof_alloc_prep(tsd, *usize, prof_active, false);\n\tif (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {\n\t\tp = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,\n\t\t    *usize, alignment, zero, tcache, arena, tctx);\n\t} else {\n\t\tp = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,\n\t\t    zero, tcache, arena);\n\t}\n\tif (unlikely(p == NULL)) {\n\t\tprof_alloc_rollback(tsd, tctx, false);\n\t\treturn NULL;\n\t}\n\n\tif (p == old_ptr && alignment != 0) {\n\t\t/*\n\t\t * The allocation did not move, so it is possible that the size\n\t\t * class is smaller than would guarantee the requested\n\t\t * alignment, and that the alignment constraint was\n\t\t * serendipitously satisfied.  Additionally, old_usize may not\n\t\t * be the same as the current usize because of in-place large\n\t\t * reallocation.  Therefore, query the actual value of usize.\n\t\t */\n\t\t*usize = isalloc(tsd_tsdn(tsd), p);\n\t}\n\tprof_realloc(tsd, p, *usize, tctx, prof_active, false, old_ptr,\n\t    old_usize, old_tctx);\n\n\treturn p;\n}\n\nJEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN\nvoid JEMALLOC_NOTHROW *\nJEMALLOC_ALLOC_SIZE(2)\nje_rallocx(void *ptr, size_t size, int flags) {\n\tvoid *p;\n\ttsd_t *tsd;\n\tsize_t usize;\n\tsize_t old_usize;\n\tsize_t alignment = MALLOCX_ALIGN_GET(flags);\n\tbool zero = flags & MALLOCX_ZERO;\n\tarena_t *arena;\n\ttcache_t *tcache;\n\n\tLOG(\"core.rallocx.entry\", \"ptr: %p, size: %zu, flags: %d\", ptr,\n\t    size, flags);\n\n\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tif (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {\n\t\tunsigned arena_ind = MALLOCX_ARENA_GET(flags);\n\t\tarena = arena_get(tsd_tsdn(tsd), arena_ind, true);\n\t\tif (unlikely(arena == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t} else {\n\t\tarena = NULL;\n\t}\n\n\tif (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {\n\t\tif ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {\n\t\t\ttcache = NULL;\n\t\t} else {\n\t\t\ttcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));\n\t\t}\n\t} else {\n\t\ttcache = tcache_get(tsd);\n\t}\n\n\talloc_ctx_t alloc_ctx;\n\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\tassert(alloc_ctx.szind != NSIZES);\n\told_usize = sz_index2size(alloc_ctx.szind);\n\tassert(old_usize == isalloc(tsd_tsdn(tsd), ptr));\n\tif (config_prof && opt_prof) {\n\t\tusize = (alignment == 0) ?\n\t\t    sz_s2u(size) : sz_sa2u(size, alignment);\n\t\tif (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t\tp = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize,\n\t\t    zero, tcache, arena, &alloc_ctx);\n\t\tif (unlikely(p == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t} else {\n\t\tp = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment,\n\t\t    zero, tcache, arena);\n\t\tif (unlikely(p == NULL)) {\n\t\t\tgoto label_oom;\n\t\t}\n\t\tif (config_stats) {\n\t\t\tusize = isalloc(tsd_tsdn(tsd), p);\n\t\t}\n\t}\n\tassert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));\n\n\tif (config_stats) {\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t\t*tsd_thread_deallocatedp_get(tsd) += old_usize;\n\t}\n\tUTRACE(ptr, size, p);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.rallocx.exit\", \"result: %p\", p);\n\treturn p;\nlabel_oom:\n\tif (config_xmalloc && unlikely(opt_xmalloc)) {\n\t\tmalloc_write(\"<jemalloc>: Error in rallocx(): out of memory\\n\");\n\t\tabort();\n\t}\n\tUTRACE(ptr, size, 0);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.rallocx.exit\", \"result: %p\", NULL);\n\treturn NULL;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,\n    size_t extra, size_t alignment, bool zero) {\n\tsize_t usize;\n\n\tif (ixalloc(tsdn, ptr, old_usize, size, extra, alignment, zero)) {\n\t\treturn old_usize;\n\t}\n\tusize = isalloc(tsdn, ptr);\n\n\treturn usize;\n}\n\nstatic size_t\nixallocx_prof_sample(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,\n    size_t extra, size_t alignment, bool zero, prof_tctx_t *tctx) {\n\tsize_t usize;\n\n\tif (tctx == NULL) {\n\t\treturn old_usize;\n\t}\n\tusize = ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment,\n\t    zero);\n\n\treturn usize;\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\nixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,\n    size_t extra, size_t alignment, bool zero, alloc_ctx_t *alloc_ctx) {\n\tsize_t usize_max, usize;\n\tbool prof_active;\n\tprof_tctx_t *old_tctx, *tctx;\n\n\tprof_active = prof_active_get_unlocked();\n\told_tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);\n\t/*\n\t * usize isn't knowable before ixalloc() returns when extra is non-zero.\n\t * Therefore, compute its maximum possible value and use that in\n\t * prof_alloc_prep() to decide whether to capture a backtrace.\n\t * prof_realloc() will use the actual usize to decide whether to sample.\n\t */\n\tif (alignment == 0) {\n\t\tusize_max = sz_s2u(size+extra);\n\t\tassert(usize_max > 0 && usize_max <= LARGE_MAXCLASS);\n\t} else {\n\t\tusize_max = sz_sa2u(size+extra, alignment);\n\t\tif (unlikely(usize_max == 0 || usize_max > LARGE_MAXCLASS)) {\n\t\t\t/*\n\t\t\t * usize_max is out of range, and chances are that\n\t\t\t * allocation will fail, but use the maximum possible\n\t\t\t * value and carry on with prof_alloc_prep(), just in\n\t\t\t * case allocation succeeds.\n\t\t\t */\n\t\t\tusize_max = LARGE_MAXCLASS;\n\t\t}\n\t}\n\ttctx = prof_alloc_prep(tsd, usize_max, prof_active, false);\n\n\tif (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {\n\t\tusize = ixallocx_prof_sample(tsd_tsdn(tsd), ptr, old_usize,\n\t\t    size, extra, alignment, zero, tctx);\n\t} else {\n\t\tusize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,\n\t\t    extra, alignment, zero);\n\t}\n\tif (usize == old_usize) {\n\t\tprof_alloc_rollback(tsd, tctx, false);\n\t\treturn usize;\n\t}\n\tprof_realloc(tsd, ptr, usize, tctx, prof_active, false, ptr, old_usize,\n\t    old_tctx);\n\n\treturn usize;\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nje_xallocx(void *ptr, size_t size, size_t extra, int flags) {\n\ttsd_t *tsd;\n\tsize_t usize, old_usize;\n\tsize_t alignment = MALLOCX_ALIGN_GET(flags);\n\tbool zero = flags & MALLOCX_ZERO;\n\n\tLOG(\"core.xallocx.entry\", \"ptr: %p, size: %zu, extra: %zu, \"\n\t    \"flags: %d\", ptr, size, extra, flags);\n\n\tassert(ptr != NULL);\n\tassert(size != 0);\n\tassert(SIZE_T_MAX - size >= extra);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\talloc_ctx_t alloc_ctx;\n\trtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);\n\trtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);\n\tassert(alloc_ctx.szind != NSIZES);\n\told_usize = sz_index2size(alloc_ctx.szind);\n\tassert(old_usize == isalloc(tsd_tsdn(tsd), ptr));\n\t/*\n\t * The API explicitly absolves itself of protecting against (size +\n\t * extra) numerical overflow, but we may need to clamp extra to avoid\n\t * exceeding LARGE_MAXCLASS.\n\t *\n\t * Ordinarily, size limit checking is handled deeper down, but here we\n\t * have to check as part of (size + extra) clamping, since we need the\n\t * clamped value in the above helper functions.\n\t */\n\tif (unlikely(size > LARGE_MAXCLASS)) {\n\t\tusize = old_usize;\n\t\tgoto label_not_resized;\n\t}\n\tif (unlikely(LARGE_MAXCLASS - size < extra)) {\n\t\textra = LARGE_MAXCLASS - size;\n\t}\n\n\tif (config_prof && opt_prof) {\n\t\tusize = ixallocx_prof(tsd, ptr, old_usize, size, extra,\n\t\t    alignment, zero, &alloc_ctx);\n\t} else {\n\t\tusize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,\n\t\t    extra, alignment, zero);\n\t}\n\tif (unlikely(usize == old_usize)) {\n\t\tgoto label_not_resized;\n\t}\n\n\tif (config_stats) {\n\t\t*tsd_thread_allocatedp_get(tsd) += usize;\n\t\t*tsd_thread_deallocatedp_get(tsd) += old_usize;\n\t}\nlabel_not_resized:\n\tUTRACE(ptr, size, ptr);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.xallocx.exit\", \"result: %zu\", usize);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nJEMALLOC_ATTR(pure)\nje_sallocx(const void *ptr, UNUSED int flags) {\n\tsize_t usize;\n\ttsdn_t *tsdn;\n\n\tLOG(\"core.sallocx.entry\", \"ptr: %p, flags: %d\", ptr, flags);\n\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\tassert(ptr != NULL);\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\n\tif (config_debug || force_ivsalloc) {\n\t\tusize = ivsalloc(tsdn, ptr);\n\t\tassert(force_ivsalloc || usize != 0);\n\t} else {\n\t\tusize = isalloc(tsdn, ptr);\n\t}\n\n\tcheck_entry_exit_locking(tsdn);\n\n\tLOG(\"core.sallocx.exit\", \"result: %zu\", usize);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_dallocx(void *ptr, int flags) {\n\tLOG(\"core.dallocx.entry\", \"ptr: %p, flags: %d\", ptr, flags);\n\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\ttsd_t *tsd = tsd_fetch();\n\tbool fast = tsd_fast(tsd);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\ttcache_t *tcache;\n\tif (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {\n\t\t/* Not allowed to be reentrant and specify a custom tcache. */\n\t\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\t\tif ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {\n\t\t\ttcache = NULL;\n\t\t} else {\n\t\t\ttcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));\n\t\t}\n\t} else {\n\t\tif (likely(fast)) {\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tassert(tcache == tcache_get(tsd));\n\t\t} else {\n\t\t\tif (likely(tsd_reentrancy_level_get(tsd) == 0)) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t}  else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tUTRACE(ptr, 0, 0);\n\tif (likely(fast)) {\n\t\ttsd_assert_fast(tsd);\n\t\tifree(tsd, ptr, tcache, false);\n\t} else {\n\t\tifree(tsd, ptr, tcache, true);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.dallocx.exit\", \"\");\n}\n\nJEMALLOC_ALWAYS_INLINE size_t\ninallocx(tsdn_t *tsdn, size_t size, int flags) {\n\tcheck_entry_exit_locking(tsdn);\n\n\tsize_t usize;\n\tif (likely((flags & MALLOCX_LG_ALIGN_MASK) == 0)) {\n\t\tusize = sz_s2u(size);\n\t} else {\n\t\tusize = sz_sa2u(size, MALLOCX_ALIGN_GET_SPECIFIED(flags));\n\t}\n\tcheck_entry_exit_locking(tsdn);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_sdallocx(void *ptr, size_t size, int flags) {\n\tassert(ptr != NULL);\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\tLOG(\"core.sdallocx.entry\", \"ptr: %p, size: %zu, flags: %d\", ptr,\n\t    size, flags);\n\n\ttsd_t *tsd = tsd_fetch();\n\tbool fast = tsd_fast(tsd);\n\tsize_t usize = inallocx(tsd_tsdn(tsd), size, flags);\n\tassert(usize == isalloc(tsd_tsdn(tsd), ptr));\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\ttcache_t *tcache;\n\tif (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {\n\t\t/* Not allowed to be reentrant and specify a custom tcache. */\n\t\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\t\tif ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {\n\t\t\ttcache = NULL;\n\t\t} else {\n\t\t\ttcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));\n\t\t}\n\t} else {\n\t\tif (likely(fast)) {\n\t\t\ttcache = tsd_tcachep_get(tsd);\n\t\t\tassert(tcache == tcache_get(tsd));\n\t\t} else {\n\t\t\tif (likely(tsd_reentrancy_level_get(tsd) == 0)) {\n\t\t\t\ttcache = tcache_get(tsd);\n\t\t\t} else {\n\t\t\t\ttcache = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tUTRACE(ptr, 0, 0);\n\tif (likely(fast)) {\n\t\ttsd_assert_fast(tsd);\n\t\tisfree(tsd, ptr, usize, tcache, false);\n\t} else {\n\t\tisfree(tsd, ptr, usize, tcache, true);\n\t}\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.sdallocx.exit\", \"\");\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nJEMALLOC_ATTR(pure)\nje_nallocx(size_t size, int flags) {\n\tsize_t usize;\n\ttsdn_t *tsdn;\n\n\tassert(size != 0);\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.nallocx.exit\", \"result: %zu\", ZU(0));\n\t\treturn 0;\n\t}\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\n\tusize = inallocx(tsdn, size, flags);\n\tif (unlikely(usize > LARGE_MAXCLASS)) {\n\t\tLOG(\"core.nallocx.exit\", \"result: %zu\", ZU(0));\n\t\treturn 0;\n\t}\n\n\tcheck_entry_exit_locking(tsdn);\n\tLOG(\"core.nallocx.exit\", \"result: %zu\", usize);\n\treturn usize;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nje_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,\n    size_t newlen) {\n\tint ret;\n\ttsd_t *tsd;\n\n\tLOG(\"core.mallctl.entry\", \"name: %s\", name);\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.mallctl.exit\", \"result: %d\", EAGAIN);\n\t\treturn EAGAIN;\n\t}\n\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tret = ctl_byname(tsd, name, oldp, oldlenp, newp, newlen);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.mallctl.exit\", \"result: %d\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nje_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) {\n\tint ret;\n\n\tLOG(\"core.mallctlnametomib.entry\", \"name: %s\", name);\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.mallctlnametomib.exit\", \"result: %d\", EAGAIN);\n\t\treturn EAGAIN;\n\t}\n\n\ttsd_t *tsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tret = ctl_nametomib(tsd, name, mibp, miblenp);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\n\tLOG(\"core.mallctlnametomib.exit\", \"result: %d\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nje_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,\n  void *newp, size_t newlen) {\n\tint ret;\n\ttsd_t *tsd;\n\n\tLOG(\"core.mallctlbymib.entry\", \"\");\n\n\tif (unlikely(malloc_init())) {\n\t\tLOG(\"core.mallctlbymib.exit\", \"result: %d\", EAGAIN);\n\t\treturn EAGAIN;\n\t}\n\n\ttsd = tsd_fetch();\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tret = ctl_bymib(tsd, mib, miblen, oldp, oldlenp, newp, newlen);\n\tcheck_entry_exit_locking(tsd_tsdn(tsd));\n\tLOG(\"core.mallctlbymib.exit\", \"result: %d\", ret);\n\treturn ret;\n}\n\nJEMALLOC_EXPORT void JEMALLOC_NOTHROW\nje_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts) {\n\ttsdn_t *tsdn;\n\n\tLOG(\"core.malloc_stats_print.entry\", \"\");\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\tstats_print(write_cb, cbopaque, opts);\n\tcheck_entry_exit_locking(tsdn);\n\tLOG(\"core.malloc_stats_print.exit\", \"\");\n}\n\nJEMALLOC_EXPORT size_t JEMALLOC_NOTHROW\nje_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {\n\tsize_t ret;\n\ttsdn_t *tsdn;\n\n\tLOG(\"core.malloc_usable_size.entry\", \"ptr: %p\", ptr);\n\n\tassert(malloc_initialized() || IS_INITIALIZER);\n\n\ttsdn = tsdn_fetch();\n\tcheck_entry_exit_locking(tsdn);\n\n\tif (unlikely(ptr == NULL)) {\n\t\tret = 0;\n\t} else {\n\t\tif (config_debug || force_ivsalloc) {\n\t\t\tret = ivsalloc(tsdn, ptr);\n\t\t\tassert(force_ivsalloc || ret != 0);\n\t\t} else {\n\t\t\tret = isalloc(tsdn, ptr);\n\t\t}\n\t}\n\n\tcheck_entry_exit_locking(tsdn);\n\tLOG(\"core.malloc_usable_size.exit\", \"result: %zu\", ret);\n\treturn ret;\n}\n\n/*\n * End non-standard 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 be possible\n * to trigger the deadlock described above, but doing so would involve forking\n * via a library constructor that runs before jemalloc's runs.\n */\n#ifndef JEMALLOC_JET\nJEMALLOC_ATTR(constructor)\nstatic void\njemalloc_constructor(void) {\n\tmalloc_init();\n}\n#endif\n\n#ifndef JEMALLOC_MUTEX_INIT_CB\nvoid\njemalloc_prefork(void)\n#else\nJEMALLOC_EXPORT void\n_malloc_prefork(void)\n#endif\n{\n\ttsd_t *tsd;\n\tunsigned i, j, narenas;\n\tarena_t *arena;\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tif (!malloc_initialized()) {\n\t\treturn;\n\t}\n#endif\n\tassert(malloc_initialized());\n\n\ttsd = tsd_fetch();\n\n\tnarenas = narenas_total_get();\n\n\twitness_prefork(tsd_witness_tsdp_get(tsd));\n\t/* Acquire all mutexes in a safe order. */\n\tctl_prefork(tsd_tsdn(tsd));\n\ttcache_prefork(tsd_tsdn(tsd));\n\tmalloc_mutex_prefork(tsd_tsdn(tsd), &arenas_lock);\n\tif (have_background_thread) {\n\t\tbackground_thread_prefork0(tsd_tsdn(tsd));\n\t}\n\tprof_prefork0(tsd_tsdn(tsd));\n\tif (have_background_thread) {\n\t\tbackground_thread_prefork1(tsd_tsdn(tsd));\n\t}\n\t/* Break arena prefork into stages to preserve lock order. */\n\tfor (i = 0; i < 8; i++) {\n\t\tfor (j = 0; j < narenas; j++) {\n\t\t\tif ((arena = arena_get(tsd_tsdn(tsd), j, false)) !=\n\t\t\t    NULL) {\n\t\t\t\tswitch (i) {\n\t\t\t\tcase 0:\n\t\t\t\t\tarena_prefork0(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tarena_prefork1(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tarena_prefork2(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tarena_prefork3(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tarena_prefork4(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tarena_prefork5(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\tarena_prefork6(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\tarena_prefork7(tsd_tsdn(tsd), arena);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: not_reached();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tprof_prefork1(tsd_tsdn(tsd));\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\ttsd_t *tsd;\n\tunsigned i, narenas;\n\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tif (!malloc_initialized()) {\n\t\treturn;\n\t}\n#endif\n\tassert(malloc_initialized());\n\n\ttsd = tsd_fetch();\n\n\twitness_postfork_parent(tsd_witness_tsdp_get(tsd));\n\t/* Release all mutexes, now that fork() has completed. */\n\tfor (i = 0, narenas = narenas_total_get(); i < narenas; i++) {\n\t\tarena_t *arena;\n\n\t\tif ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {\n\t\t\tarena_postfork_parent(tsd_tsdn(tsd), arena);\n\t\t}\n\t}\n\tprof_postfork_parent(tsd_tsdn(tsd));\n\tif (have_background_thread) {\n\t\tbackground_thread_postfork_parent(tsd_tsdn(tsd));\n\t}\n\tmalloc_mutex_postfork_parent(tsd_tsdn(tsd), &arenas_lock);\n\ttcache_postfork_parent(tsd_tsdn(tsd));\n\tctl_postfork_parent(tsd_tsdn(tsd));\n}\n\nvoid\njemalloc_postfork_child(void) {\n\ttsd_t *tsd;\n\tunsigned i, narenas;\n\n\tassert(malloc_initialized());\n\n\ttsd = tsd_fetch();\n\n\twitness_postfork_child(tsd_witness_tsdp_get(tsd));\n\t/* Release all mutexes, now that fork() has completed. */\n\tfor (i = 0, narenas = narenas_total_get(); i < narenas; i++) {\n\t\tarena_t *arena;\n\n\t\tif ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {\n\t\t\tarena_postfork_child(tsd_tsdn(tsd), arena);\n\t\t}\n\t}\n\tprof_postfork_child(tsd_tsdn(tsd));\n\tif (have_background_thread) {\n\t\tbackground_thread_postfork_child(tsd_tsdn(tsd));\n\t}\n\tmalloc_mutex_postfork_child(tsd_tsdn(tsd), &arenas_lock);\n\ttcache_postfork_child(tsd_tsdn(tsd));\n\tctl_postfork_child(tsd_tsdn(tsd));\n}\n\n/******************************************************************************/\n\n/* Helps the application decide if a pointer is worth re-allocating in order to reduce fragmentation.\n * returns 0 if the allocation is in the currently active run,\n * or when it is not causing any frag issue (large or huge bin)\n * returns the bin utilization and run utilization both in fixed point 16:16.\n * If the application decides to re-allocate it should use MALLOCX_TCACHE_NONE when doing so. */\nJEMALLOC_EXPORT int JEMALLOC_NOTHROW\nget_defrag_hint(void* ptr, int *bin_util, int *run_util) {\n\tassert(ptr != NULL);\n\treturn iget_defrag_hint(TSDN_NULL, ptr, bin_util, run_util);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/jemalloc_cpp.cpp",
    "content": "#include <mutex>\n#include <new>\n\n#define JEMALLOC_CPP_CPP_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#ifdef __cplusplus\n}\n#endif\n\n// All operators in this file are exported.\n\n// Possibly alias hidden versions of malloc and sdallocx to avoid an extra plt\n// thunk?\n//\n// extern __typeof (sdallocx) sdallocx_int\n//  __attribute ((alias (\"sdallocx\"),\n//\t\tvisibility (\"hidden\")));\n//\n// ... but it needs to work with jemalloc namespaces.\n\nvoid\t*operator new(std::size_t size);\nvoid\t*operator new[](std::size_t size);\nvoid\t*operator new(std::size_t size, const std::nothrow_t &) noexcept;\nvoid\t*operator new[](std::size_t size, const std::nothrow_t &) noexcept;\nvoid\toperator delete(void *ptr) noexcept;\nvoid\toperator delete[](void *ptr) noexcept;\nvoid\toperator delete(void *ptr, const std::nothrow_t &) noexcept;\nvoid\toperator delete[](void *ptr, const std::nothrow_t &) noexcept;\n\n#if __cpp_sized_deallocation >= 201309\n/* C++14's sized-delete operators. */\nvoid\toperator delete(void *ptr, std::size_t size) noexcept;\nvoid\toperator delete[](void *ptr, std::size_t size) noexcept;\n#endif\n\nJEMALLOC_NOINLINE\nstatic void *\nhandleOOM(std::size_t size, bool nothrow) {\n\tvoid *ptr = nullptr;\n\n\twhile (ptr == nullptr) {\n\t\tstd::new_handler handler;\n\t\t// GCC-4.8 and clang 4.0 do not have std::get_new_handler.\n\t\t{\n\t\t\tstatic std::mutex mtx;\n\t\t\tstd::lock_guard<std::mutex> lock(mtx);\n\n\t\t\thandler = std::set_new_handler(nullptr);\n\t\t\tstd::set_new_handler(handler);\n\t\t}\n\t\tif (handler == nullptr)\n\t\t\tbreak;\n\n\t\ttry {\n\t\t\thandler();\n\t\t} catch (const std::bad_alloc &) {\n\t\t\tbreak;\n\t\t}\n\n\t\tptr = je_malloc(size);\n\t}\n\n\tif (ptr == nullptr && !nothrow)\n\t\tstd::__throw_bad_alloc();\n\treturn ptr;\n}\n\ntemplate <bool IsNoExcept>\nJEMALLOC_ALWAYS_INLINE\nvoid *\nnewImpl(std::size_t size) noexcept(IsNoExcept) {\n\tvoid *ptr = je_malloc(size);\n\tif (likely(ptr != nullptr))\n\t\treturn ptr;\n\n\treturn handleOOM(size, IsNoExcept);\n}\n\nvoid *\noperator new(std::size_t size) {\n\treturn newImpl<false>(size);\n}\n\nvoid *\noperator new[](std::size_t size) {\n\treturn newImpl<false>(size);\n}\n\nvoid *\noperator new(std::size_t size, const std::nothrow_t &) noexcept {\n\treturn newImpl<true>(size);\n}\n\nvoid *\noperator new[](std::size_t size, const std::nothrow_t &) noexcept {\n\treturn newImpl<true>(size);\n}\n\nvoid\noperator delete(void *ptr) noexcept {\n\tje_free(ptr);\n}\n\nvoid\noperator delete[](void *ptr) noexcept {\n\tje_free(ptr);\n}\n\nvoid\noperator delete(void *ptr, const std::nothrow_t &) noexcept {\n\tje_free(ptr);\n}\n\nvoid operator delete[](void *ptr, const std::nothrow_t &) noexcept {\n\tje_free(ptr);\n}\n\n#if __cpp_sized_deallocation >= 201309\n\nvoid\noperator delete(void *ptr, std::size_t size) noexcept {\n\tif (unlikely(ptr == nullptr)) {\n\t\treturn;\n\t}\n\tje_sdallocx(ptr, size, /*flags=*/0);\n}\n\nvoid operator delete[](void *ptr, std::size_t size) noexcept {\n\tif (unlikely(ptr == nullptr)) {\n\t\treturn;\n\t}\n\tje_sdallocx(ptr, size, /*flags=*/0);\n}\n\n#endif  // __cpp_sized_deallocation\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/large.c",
    "content": "#define JEMALLOC_LARGE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n#include \"jemalloc/internal/util.h\"\n\n/******************************************************************************/\n\nvoid *\nlarge_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero) {\n\tassert(usize == sz_s2u(usize));\n\n\treturn large_palloc(tsdn, arena, usize, CACHELINE, zero);\n}\n\nvoid *\nlarge_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,\n    bool zero) {\n\tsize_t ausize;\n\textent_t *extent;\n\tbool is_zeroed;\n\tUNUSED bool idump JEMALLOC_CC_SILENCE_INIT(false);\n\n\tassert(!tsdn_null(tsdn) || arena != NULL);\n\n\tausize = sz_sa2u(usize, alignment);\n\tif (unlikely(ausize == 0 || ausize > LARGE_MAXCLASS)) {\n\t\treturn NULL;\n\t}\n\n\tif (config_fill && unlikely(opt_zero)) {\n\t\tzero = true;\n\t}\n\t/*\n\t * Copy zero into is_zeroed and pass the copy when allocating the\n\t * extent, so that it is possible to make correct junk/zero fill\n\t * decisions below, even if is_zeroed ends up true when zero is false.\n\t */\n\tis_zeroed = zero;\n\tif (likely(!tsdn_null(tsdn))) {\n\t\tarena = arena_choose(tsdn_tsd(tsdn), arena);\n\t}\n\tif (unlikely(arena == NULL) || (extent = arena_extent_alloc_large(tsdn,\n\t    arena, usize, alignment, &is_zeroed)) == NULL) {\n\t\treturn NULL;\n\t}\n\n\t/* See comments in arena_bin_slabs_full_insert(). */\n\tif (!arena_is_auto(arena)) {\n\t\t/* Insert extent into large. */\n\t\tmalloc_mutex_lock(tsdn, &arena->large_mtx);\n\t\textent_list_append(&arena->large, extent);\n\t\tmalloc_mutex_unlock(tsdn, &arena->large_mtx);\n\t}\n\tif (config_prof && arena_prof_accum(tsdn, arena, usize)) {\n\t\tprof_idump(tsdn);\n\t}\n\n\tif (zero) {\n\t\tassert(is_zeroed);\n\t} else if (config_fill && unlikely(opt_junk_alloc)) {\n\t\tmemset(extent_addr_get(extent), JEMALLOC_ALLOC_JUNK,\n\t\t    extent_usize_get(extent));\n\t}\n\n\tarena_decay_tick(tsdn, arena);\n\treturn extent_addr_get(extent);\n}\n\nstatic void\nlarge_dalloc_junk_impl(void *ptr, size_t size) {\n\tmemset(ptr, JEMALLOC_FREE_JUNK, size);\n}\nlarge_dalloc_junk_t *JET_MUTABLE large_dalloc_junk = large_dalloc_junk_impl;\n\nstatic void\nlarge_dalloc_maybe_junk_impl(void *ptr, size_t size) {\n\tif (config_fill && have_dss && unlikely(opt_junk_free)) {\n\t\t/*\n\t\t * Only bother junk filling if the extent isn't about to be\n\t\t * unmapped.\n\t\t */\n\t\tif (opt_retain || (have_dss && extent_in_dss(ptr))) {\n\t\t\tlarge_dalloc_junk(ptr, size);\n\t\t}\n\t}\n}\nlarge_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk =\n    large_dalloc_maybe_junk_impl;\n\nstatic bool\nlarge_ralloc_no_move_shrink(tsdn_t *tsdn, extent_t *extent, size_t usize) {\n\tarena_t *arena = extent_arena_get(extent);\n\tsize_t oldusize = extent_usize_get(extent);\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\tsize_t diff = extent_size_get(extent) - (usize + sz_large_pad);\n\n\tassert(oldusize > usize);\n\n\tif (extent_hooks->split == NULL) {\n\t\treturn true;\n\t}\n\n\t/* Split excess pages. */\n\tif (diff != 0) {\n\t\textent_t *trail = extent_split_wrapper(tsdn, arena,\n\t\t    &extent_hooks, extent, usize + sz_large_pad,\n\t\t    sz_size2index(usize), false, diff, NSIZES, false);\n\t\tif (trail == NULL) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (config_fill && unlikely(opt_junk_free)) {\n\t\t\tlarge_dalloc_maybe_junk(extent_addr_get(trail),\n\t\t\t    extent_size_get(trail));\n\t\t}\n\n\t\tarena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, trail);\n\t}\n\n\tarena_extent_ralloc_large_shrink(tsdn, arena, extent, oldusize);\n\n\treturn false;\n}\n\nstatic bool\nlarge_ralloc_no_move_expand(tsdn_t *tsdn, extent_t *extent, size_t usize,\n    bool zero) {\n\tarena_t *arena = extent_arena_get(extent);\n\tsize_t oldusize = extent_usize_get(extent);\n\textent_hooks_t *extent_hooks = extent_hooks_get(arena);\n\tsize_t trailsize = usize - oldusize;\n\n\tif (extent_hooks->merge == NULL) {\n\t\treturn true;\n\t}\n\n\tif (config_fill && unlikely(opt_zero)) {\n\t\tzero = true;\n\t}\n\t/*\n\t * Copy zero into is_zeroed_trail and pass the copy when allocating the\n\t * extent, so that it is possible to make correct junk/zero fill\n\t * decisions below, even if is_zeroed_trail ends up true when zero is\n\t * false.\n\t */\n\tbool is_zeroed_trail = zero;\n\tbool commit = true;\n\textent_t *trail;\n\tbool new_mapping;\n\tif ((trail = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_dirty, extent_past_get(extent), trailsize, 0,\n\t    CACHELINE, false, NSIZES, &is_zeroed_trail, &commit)) != NULL\n\t    || (trail = extents_alloc(tsdn, arena, &extent_hooks,\n\t    &arena->extents_muzzy, extent_past_get(extent), trailsize, 0,\n\t    CACHELINE, false, NSIZES, &is_zeroed_trail, &commit)) != NULL) {\n\t\tif (config_stats) {\n\t\t\tnew_mapping = false;\n\t\t}\n\t} else {\n\t\tif ((trail = extent_alloc_wrapper(tsdn, arena, &extent_hooks,\n\t\t    extent_past_get(extent), trailsize, 0, CACHELINE, false,\n\t\t    NSIZES, &is_zeroed_trail, &commit)) == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tif (config_stats) {\n\t\t\tnew_mapping = true;\n\t\t}\n\t}\n\n\tif (extent_merge_wrapper(tsdn, arena, &extent_hooks, extent, trail)) {\n\t\textent_dalloc_wrapper(tsdn, arena, &extent_hooks, trail);\n\t\treturn true;\n\t}\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\tszind_t szind = sz_size2index(usize);\n\textent_szind_set(extent, szind);\n\trtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)extent_addr_get(extent), szind, false);\n\n\tif (config_stats && new_mapping) {\n\t\tarena_stats_mapped_add(tsdn, &arena->stats, trailsize);\n\t}\n\n\tif (zero) {\n\t\tif (config_cache_oblivious) {\n\t\t\t/*\n\t\t\t * Zero the trailing bytes of the original allocation's\n\t\t\t * last page, since they are in an indeterminate state.\n\t\t\t * There will always be trailing bytes, because ptr's\n\t\t\t * offset from the beginning of the extent is a multiple\n\t\t\t * of CACHELINE in [0 .. PAGE).\n\t\t\t */\n\t\t\tvoid *zbase = (void *)\n\t\t\t    ((uintptr_t)extent_addr_get(extent) + oldusize);\n\t\t\tvoid *zpast = PAGE_ADDR2BASE((void *)((uintptr_t)zbase +\n\t\t\t    PAGE));\n\t\t\tsize_t nzero = (uintptr_t)zpast - (uintptr_t)zbase;\n\t\t\tassert(nzero > 0);\n\t\t\tmemset(zbase, 0, nzero);\n\t\t}\n\t\tassert(is_zeroed_trail);\n\t} else if (config_fill && unlikely(opt_junk_alloc)) {\n\t\tmemset((void *)((uintptr_t)extent_addr_get(extent) + oldusize),\n\t\t    JEMALLOC_ALLOC_JUNK, usize - oldusize);\n\t}\n\n\tarena_extent_ralloc_large_expand(tsdn, arena, extent, oldusize);\n\n\treturn false;\n}\n\nbool\nlarge_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,\n    size_t usize_max, bool zero) {\n\tsize_t oldusize = extent_usize_get(extent);\n\n\t/* The following should have been caught by callers. */\n\tassert(usize_min > 0 && usize_max <= LARGE_MAXCLASS);\n\t/* Both allocation sizes must be large to avoid a move. */\n\tassert(oldusize >= LARGE_MINCLASS && usize_max >= LARGE_MINCLASS);\n\n\tif (usize_max > oldusize) {\n\t\t/* Attempt to expand the allocation in-place. */\n\t\tif (!large_ralloc_no_move_expand(tsdn, extent, usize_max,\n\t\t    zero)) {\n\t\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\t\treturn false;\n\t\t}\n\t\t/* Try again, this time with usize_min. */\n\t\tif (usize_min < usize_max && usize_min > oldusize &&\n\t\t    large_ralloc_no_move_expand(tsdn, extent, usize_min,\n\t\t    zero)) {\n\t\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * Avoid moving the allocation if the existing extent size accommodates\n\t * the new size.\n\t */\n\tif (oldusize >= usize_min && oldusize <= usize_max) {\n\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\treturn false;\n\t}\n\n\t/* Attempt to shrink the allocation in-place. */\n\tif (oldusize > usize_max) {\n\t\tif (!large_ralloc_no_move_shrink(tsdn, extent, usize_max)) {\n\t\t\tarena_decay_tick(tsdn, extent_arena_get(extent));\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic void *\nlarge_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,\n    size_t alignment, bool zero) {\n\tif (alignment <= CACHELINE) {\n\t\treturn large_malloc(tsdn, arena, usize, zero);\n\t}\n\treturn large_palloc(tsdn, arena, usize, alignment, zero);\n}\n\nvoid *\nlarge_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,\n    size_t alignment, bool zero, tcache_t *tcache) {\n\tsize_t oldusize = extent_usize_get(extent);\n\n\t/* The following should have been caught by callers. */\n\tassert(usize > 0 && usize <= LARGE_MAXCLASS);\n\t/* Both allocation sizes must be large to avoid a move. */\n\tassert(oldusize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS);\n\n\t/* Try to avoid moving the allocation. */\n\tif (!large_ralloc_no_move(tsdn, extent, usize, usize, zero)) {\n\t\treturn extent_addr_get(extent);\n\t}\n\n\t/*\n\t * usize and old size 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\tvoid *ret = large_ralloc_move_helper(tsdn, arena, usize, alignment,\n\t    zero);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsize_t copysize = (usize < oldusize) ? usize : oldusize;\n\tmemcpy(ret, extent_addr_get(extent), copysize);\n\tisdalloct(tsdn, extent_addr_get(extent), oldusize, tcache, NULL, true);\n\treturn ret;\n}\n\n/*\n * junked_locked indicates whether the extent's data have been junk-filled, and\n * whether the arena's large_mtx is currently held.\n */\nstatic void\nlarge_dalloc_prep_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent,\n    bool junked_locked) {\n\tif (!junked_locked) {\n\t\t/* See comments in arena_bin_slabs_full_insert(). */\n\t\tif (!arena_is_auto(arena)) {\n\t\t\tmalloc_mutex_lock(tsdn, &arena->large_mtx);\n\t\t\textent_list_remove(&arena->large, extent);\n\t\t\tmalloc_mutex_unlock(tsdn, &arena->large_mtx);\n\t\t}\n\t\tlarge_dalloc_maybe_junk(extent_addr_get(extent),\n\t\t    extent_usize_get(extent));\n\t} else {\n\t\tmalloc_mutex_assert_owner(tsdn, &arena->large_mtx);\n\t\tif (!arena_is_auto(arena)) {\n\t\t\textent_list_remove(&arena->large, extent);\n\t\t}\n\t}\n\tarena_extent_dalloc_large_prep(tsdn, arena, extent);\n}\n\nstatic void\nlarge_dalloc_finish_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {\n\textent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;\n\tarena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, extent);\n}\n\nvoid\nlarge_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent) {\n\tlarge_dalloc_prep_impl(tsdn, extent_arena_get(extent), extent, true);\n}\n\nvoid\nlarge_dalloc_finish(tsdn_t *tsdn, extent_t *extent) {\n\tlarge_dalloc_finish_impl(tsdn, extent_arena_get(extent), extent);\n}\n\nvoid\nlarge_dalloc(tsdn_t *tsdn, extent_t *extent) {\n\tarena_t *arena = extent_arena_get(extent);\n\tlarge_dalloc_prep_impl(tsdn, arena, extent, false);\n\tlarge_dalloc_finish_impl(tsdn, arena, extent);\n\tarena_decay_tick(tsdn, arena);\n}\n\nsize_t\nlarge_salloc(tsdn_t *tsdn, const extent_t *extent) {\n\treturn extent_usize_get(extent);\n}\n\nprof_tctx_t *\nlarge_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent) {\n\treturn extent_prof_tctx_get(extent);\n}\n\nvoid\nlarge_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx) {\n\textent_prof_tctx_set(extent, tctx);\n}\n\nvoid\nlarge_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent) {\n\tlarge_prof_tctx_set(tsdn, extent, (prof_tctx_t *)(uintptr_t)1U);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/log.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/log.h\"\n\nchar log_var_names[JEMALLOC_LOG_VAR_BUFSIZE];\natomic_b_t log_init_done = ATOMIC_INIT(false);\n\n/*\n * Returns true if we were able to pick out a segment.  Fills in r_segment_end\n * with a pointer to the first character after the end of the string.\n */\nstatic const char *\nlog_var_extract_segment(const char* segment_begin) {\n\tconst char *end;\n\tfor (end = segment_begin; *end != '\\0' && *end != '|'; end++) {\n\t}\n\treturn end;\n}\n\nstatic bool\nlog_var_matches_segment(const char *segment_begin, const char *segment_end,\n    const char *log_var_begin, const char *log_var_end) {\n\tassert(segment_begin <= segment_end);\n\tassert(log_var_begin < log_var_end);\n\n\tptrdiff_t segment_len = segment_end - segment_begin;\n\tptrdiff_t log_var_len = log_var_end - log_var_begin;\n\t/* The special '.' segment matches everything. */\n\tif (segment_len == 1 && *segment_begin == '.') {\n\t\treturn true;\n\t}\n        if (segment_len == log_var_len) {\n\t\treturn strncmp(segment_begin, log_var_begin, segment_len) == 0;\n\t} else if (segment_len < log_var_len) {\n\t\treturn strncmp(segment_begin, log_var_begin, segment_len) == 0\n\t\t    && log_var_begin[segment_len] == '.';\n        } else {\n\t\treturn false;\n\t}\n}\n\nunsigned\nlog_var_update_state(log_var_t *log_var) {\n\tconst char *log_var_begin = log_var->name;\n\tconst char *log_var_end = log_var->name + strlen(log_var->name);\n\n\t/* Pointer to one before the beginning of the current segment. */\n\tconst char *segment_begin = log_var_names;\n\n\t/*\n\t * If log_init done is false, we haven't parsed the malloc conf yet.  To\n\t * avoid log-spew, we default to not displaying anything.\n\t */\n\tif (!atomic_load_b(&log_init_done, ATOMIC_ACQUIRE)) {\n\t\treturn LOG_INITIALIZED_NOT_ENABLED;\n\t}\n\n\twhile (true) {\n\t\tconst char *segment_end = log_var_extract_segment(\n\t\t    segment_begin);\n\t\tassert(segment_end < log_var_names + JEMALLOC_LOG_VAR_BUFSIZE);\n\t\tif (log_var_matches_segment(segment_begin, segment_end,\n\t\t    log_var_begin, log_var_end)) {\n\t\t\tatomic_store_u(&log_var->state, LOG_ENABLED,\n\t\t\t    ATOMIC_RELAXED);\n\t\t\treturn LOG_ENABLED;\n\t\t}\n\t\tif (*segment_end == '\\0') {\n\t\t\t/* Hit the end of the segment string with no match. */\n\t\t\tatomic_store_u(&log_var->state,\n\t\t\t    LOG_INITIALIZED_NOT_ENABLED, ATOMIC_RELAXED);\n\t\t\treturn LOG_INITIALIZED_NOT_ENABLED;\n\t\t}\n\t\t/* Otherwise, skip the delimiter and continue. */\n\t\tsegment_begin = segment_end + 1;\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/malloc_io.c",
    "content": "#define JEMALLOC_MALLOC_IO_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/util.h\"\n\n#ifdef assert\n#  undef assert\n#endif\n#ifdef not_reached\n#  undef not_reached\n#endif\n#ifdef not_implemented\n#  undef not_implemented\n#endif\n#ifdef assert_not_implemented\n#  undef assert_not_implemented\n#endif\n\n/*\n * Define simple versions of assertion macros that won't recurse in case\n * of assertion failures in malloc_*printf().\n */\n#define assert(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 not_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\tunreachable();\t\t\t\t\t\t\t\\\n} while (0)\n\n#define not_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 assert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (unlikely(config_debug && !(e))) {\t\t\t\t\\\n\t\tnot_implemented();\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic void wrtmessage(void *cbopaque, const char *s);\n#define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)\nstatic char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,\n    size_t *slen_p);\n#define D2S_BUFSIZE (1 + U2S_BUFSIZE)\nstatic char *d2s(intmax_t x, char sign, char *s, size_t *slen_p);\n#define O2S_BUFSIZE (1 + U2S_BUFSIZE)\nstatic char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);\n#define X2S_BUFSIZE (2 + U2S_BUFSIZE)\nstatic char *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\tmalloc_write_fd(STDERR_FILENO, s, strlen(s));\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\tif (je_malloc_message != NULL) {\n\t\tje_malloc_message(NULL, s);\n\t} else {\n\t\twrtmessage(NULL, s);\n\t}\n}\n\n/*\n * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so\n * provide a wrapper.\n */\nint\nbuferror(int err, char *buf, size_t buflen) {\n#ifdef _WIN32\n\tFormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,\n\t    (LPSTR)buf, (DWORD)buflen, NULL);\n\treturn 0;\n#elif defined(JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE) && defined(_GNU_SOURCE)\n\tchar *b = strerror_r(err, 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(err, buf, buflen);\n#endif\n}\n\nuintmax_t\nmalloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) {\n\tuintmax_t ret, digit;\n\tunsigned b;\n\tbool neg;\n\tconst char *p, *ns;\n\n\tp = nptr;\n\tif (base < 0 || base == 1 || base > 36) {\n\t\tns = p;\n\t\tset_errno(EINVAL);\n\t\tret = UINTMAX_MAX;\n\t\tgoto label_return;\n\t}\n\tb = base;\n\n\t/* Swallow leading whitespace and get sign, if any. */\n\tneg = false;\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\t}\n\t\t\tif (b == 8) {\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'X': case '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\t}\n\t\t\t\tif (b == 16) {\n\t\t\t\t\tp += 2;\n\t\t\t\t}\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\tp++;\n\t\t\tret = 0;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\tif (b == 0) {\n\t\tb = 10;\n\t}\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\tret = UINTMAX_MAX;\n\t\t\tgoto label_return;\n\t\t}\n\t\tp++;\n\t}\n\tif (neg) {\n\t\tret = (uintmax_t)(-((intmax_t)ret));\n\t}\n\n\tif (p == ns) {\n\t\t/* No conversion performed. */\n\t\tset_errno(EINVAL);\n\t\tret = UINTMAX_MAX;\n\t\tgoto label_return;\n\t}\n\nlabel_return:\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\t}\n\t}\n\treturn ret;\n}\n\nstatic char *\nu2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p) {\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\tbool neg;\n\n\tif ((neg = (x < 0))) {\n\t\tx = -x;\n\t}\n\ts = u2s(x, 10, false, s, slen_p);\n\tif (neg) {\n\t\tsign = '-';\n\t}\n\tswitch (sign) {\n\tcase '-':\n\t\tif (!neg) {\n\t\t\tbreak;\n\t\t}\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\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\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\nsize_t\nmalloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {\n\tsize_t i;\n\tconst char *f;\n\n#define APPEND_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\t}\t\t\t\t\t\t\t\t\\\n\ti++;\t\t\t\t\t\t\t\t\\\n} while (0)\n#define APPEND_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 APPEND_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 && 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\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\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 'j' | 0x80:\t\t\t\t\t\t\\\n\t\tval = va_arg(ap, uintmax_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:\t\t\t\t\t\t\t\\\n\t\tnot_reached();\t\t\t\t\t\t\\\n\t\tval = 0;\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\t\t\tchar *s;\n\t\t\tsize_t slen;\n\n\t\t\tf++;\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);\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);\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);\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);\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\tif (width < 0) {\n\t\t\t\t\tleft_justify = true;\n\t\t\t\t\twidth = -width;\n\t\t\t\t}\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\tbreak;\n\t\t\t} default:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* Width/precision separator. */\n\t\t\tif (*f == '.') {\n\t\t\t\tf++;\n\t\t\t} else {\n\t\t\t\tgoto label_length;\n\t\t\t}\n\t\t\t/* 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\t}\n\t\t\t\tbreak;\n\t\t\tcase 'q': case 'j': case 't': case 'z':\n\t\t\t\tlen = *f;\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\tcase '%':\n\t\t\t\t/* %% */\n\t\t\t\tAPPEND_C(*f);\n\t\t\t\tf++;\n\t\t\t\tbreak;\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 < 0) ? strlen(s) : (size_t)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} default: not_reached();\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\t} else {\n\t\tstr[size - 1] = '\\0';\n\t}\n\n#undef APPEND_C\n#undef APPEND_S\n#undef APPEND_PADDED_S\n#undef GET_ARG_NUMERIC\n\treturn i;\n}\n\nJEMALLOC_FORMAT_PRINTF(3, 4)\nsize_t\nmalloc_snprintf(char *str, size_t size, const char *format, ...) {\n\tsize_t 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\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_FORMAT_PRINTF(3, 4)\nvoid\nmalloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *format, ...) {\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_FORMAT_PRINTF(1, 2)\nvoid\nmalloc_printf(const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n}\n\n/*\n * Restore normal assertion macros, in order to make it possible to compile all\n * C files as a single concatenation.\n */\n#undef assert\n#undef not_reached\n#undef not_implemented\n#undef assert_not_implemented\n#include \"jemalloc/internal/assert.h\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/mutex.c",
    "content": "#define JEMALLOC_MUTEX_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/spin.h\"\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/******************************************************************************/\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)\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\treturn pthread_create_wrapper(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\nvoid\nmalloc_mutex_lock_slow(malloc_mutex_t *mutex) {\n\tmutex_prof_data_t *data = &mutex->prof_data;\n\tUNUSED nstime_t before = NSTIME_ZERO_INITIALIZER;\n\n\tif (ncpus == 1) {\n\t\tgoto label_spin_done;\n\t}\n\n\tint cnt = 0, max_cnt = MALLOC_MUTEX_MAX_SPIN;\n\tdo {\n\t\tspin_cpu_spinwait();\n\t\tif (!malloc_mutex_trylock_final(mutex)) {\n\t\t\tdata->n_spin_acquired++;\n\t\t\treturn;\n\t\t}\n\t} while (cnt++ < max_cnt);\n\n\tif (!config_stats) {\n\t\t/* Only spin is useful when stats is off. */\n\t\tmalloc_mutex_lock_final(mutex);\n\t\treturn;\n\t}\nlabel_spin_done:\n\tnstime_update(&before);\n\t/* Copy before to after to avoid clock skews. */\n\tnstime_t after;\n\tnstime_copy(&after, &before);\n\tuint32_t n_thds = atomic_fetch_add_u32(&data->n_waiting_thds, 1,\n\t    ATOMIC_RELAXED) + 1;\n\t/* One last try as above two calls may take quite some cycles. */\n\tif (!malloc_mutex_trylock_final(mutex)) {\n\t\tatomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED);\n\t\tdata->n_spin_acquired++;\n\t\treturn;\n\t}\n\n\t/* True slow path. */\n\tmalloc_mutex_lock_final(mutex);\n\t/* Update more slow-path only counters. */\n\tatomic_fetch_sub_u32(&data->n_waiting_thds, 1, ATOMIC_RELAXED);\n\tnstime_update(&after);\n\n\tnstime_t delta;\n\tnstime_copy(&delta, &after);\n\tnstime_subtract(&delta, &before);\n\n\tdata->n_wait_times++;\n\tnstime_add(&data->tot_wait_time, &delta);\n\tif (nstime_compare(&data->max_wait_time, &delta) < 0) {\n\t\tnstime_copy(&data->max_wait_time, &delta);\n\t}\n\tif (n_thds > data->max_n_thds) {\n\t\tdata->max_n_thds = n_thds;\n\t}\n}\n\nstatic void\nmutex_prof_data_init(mutex_prof_data_t *data) {\n\tmemset(data, 0, sizeof(mutex_prof_data_t));\n\tnstime_init(&data->max_wait_time, 0);\n\tnstime_init(&data->tot_wait_time, 0);\n\tdata->prev_owner = NULL;\n}\n\nvoid\nmalloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tmalloc_mutex_assert_owner(tsdn, mutex);\n\tmutex_prof_data_init(&mutex->prof_data);\n}\n\nstatic int\nmutex_addr_comp(const witness_t *witness1, void *mutex1,\n    const witness_t *witness2, void *mutex2) {\n\tassert(mutex1 != NULL);\n\tassert(mutex2 != NULL);\n\tuintptr_t mu1int = (uintptr_t)mutex1;\n\tuintptr_t mu2int = (uintptr_t)mutex2;\n\tif (mu1int < mu2int) {\n\t\treturn -1;\n\t} else if (mu1int == mu2int) {\n\t\treturn 0;\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nbool\nmalloc_mutex_init(malloc_mutex_t *mutex, const char *name,\n    witness_rank_t rank, malloc_mutex_lock_order_t lock_order) {\n\tmutex_prof_data_init(&mutex->prof_data);\n#ifdef _WIN32\n#  if _WIN32_WINNT >= 0x0600\n\tInitializeSRWLock(&mutex->lock);\n#  else\n\tif (!InitializeCriticalSectionAndSpinCount(&mutex->lock,\n\t    _CRT_SPINCOUNT)) {\n\t\treturn true;\n\t}\n#  endif\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tmutex->lock = OS_UNFAIR_LOCK_INIT;\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,\n\t\t    bootstrap_calloc) != 0) {\n\t\t\treturn true;\n\t\t}\n\t}\n#else\n\tpthread_mutexattr_t attr;\n\n\tif (pthread_mutexattr_init(&attr) != 0) {\n\t\treturn true;\n\t}\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\tif (config_debug) {\n\t\tmutex->lock_order = lock_order;\n\t\tif (lock_order == malloc_mutex_address_ordered) {\n\t\t\twitness_init(&mutex->witness, name, rank,\n\t\t\t    mutex_addr_comp, mutex);\n\t\t} else {\n\t\t\twitness_init(&mutex->witness, name, rank, NULL, NULL);\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid\nmalloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tmalloc_mutex_lock(tsdn, mutex);\n}\n\nvoid\nmalloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n\tmalloc_mutex_unlock(tsdn, mutex);\n}\n\nvoid\nmalloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex) {\n#ifdef JEMALLOC_MUTEX_INIT_CB\n\tmalloc_mutex_unlock(tsdn, mutex);\n#else\n\tif (malloc_mutex_init(mutex, mutex->witness.name,\n\t    mutex->witness.rank, mutex->lock_order)) {\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\t}\n\t}\n#endif\n}\n\nbool\nmalloc_mutex_boot(void) {\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    bootstrap_calloc) != 0) {\n\t\t\treturn true;\n\t\t}\n\t\tpostponed_mutexes = postponed_mutexes->postponed_next;\n\t}\n#endif\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/mutex_pool.c",
    "content": "#define JEMALLOC_MUTEX_POOL_C_\n\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_pool.h\"\n\nbool\nmutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank) {\n\tfor (int i = 0; i < MUTEX_POOL_SIZE; ++i) {\n\t\tif (malloc_mutex_init(&pool->mutexes[i], name, rank,\n\t\t    malloc_mutex_address_ordered)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/nstime.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/nstime.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n#define BILLION\tUINT64_C(1000000000)\n#define MILLION\tUINT64_C(1000000)\n\nvoid\nnstime_init(nstime_t *time, uint64_t ns) {\n\ttime->ns = ns;\n}\n\nvoid\nnstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) {\n\ttime->ns = sec * BILLION + nsec;\n}\n\nuint64_t\nnstime_ns(const nstime_t *time) {\n\treturn time->ns;\n}\n\nuint64_t\nnstime_msec(const nstime_t *time) {\n\treturn time->ns / MILLION;\n}\n\nuint64_t\nnstime_sec(const nstime_t *time) {\n\treturn time->ns / BILLION;\n}\n\nuint64_t\nnstime_nsec(const nstime_t *time) {\n\treturn time->ns % BILLION;\n}\n\nvoid\nnstime_copy(nstime_t *time, const nstime_t *source) {\n\t*time = *source;\n}\n\nint\nnstime_compare(const nstime_t *a, const nstime_t *b) {\n\treturn (a->ns > b->ns) - (a->ns < b->ns);\n}\n\nvoid\nnstime_add(nstime_t *time, const nstime_t *addend) {\n\tassert(UINT64_MAX - time->ns >= addend->ns);\n\n\ttime->ns += addend->ns;\n}\n\nvoid\nnstime_iadd(nstime_t *time, uint64_t addend) {\n\tassert(UINT64_MAX - time->ns >= addend);\n\n\ttime->ns += addend;\n}\n\nvoid\nnstime_subtract(nstime_t *time, const nstime_t *subtrahend) {\n\tassert(nstime_compare(time, subtrahend) >= 0);\n\n\ttime->ns -= subtrahend->ns;\n}\n\nvoid\nnstime_isubtract(nstime_t *time, uint64_t subtrahend) {\n\tassert(time->ns >= subtrahend);\n\n\ttime->ns -= subtrahend;\n}\n\nvoid\nnstime_imultiply(nstime_t *time, uint64_t multiplier) {\n\tassert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) <<\n\t    2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns));\n\n\ttime->ns *= multiplier;\n}\n\nvoid\nnstime_idivide(nstime_t *time, uint64_t divisor) {\n\tassert(divisor != 0);\n\n\ttime->ns /= divisor;\n}\n\nuint64_t\nnstime_divide(const nstime_t *time, const nstime_t *divisor) {\n\tassert(divisor->ns != 0);\n\n\treturn time->ns / divisor->ns;\n}\n\n#ifdef _WIN32\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tFILETIME ft;\n\tuint64_t ticks_100ns;\n\n\tGetSystemTimeAsFileTime(&ft);\n\tticks_100ns = (((uint64_t)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;\n\n\tnstime_init(time, ticks_100ns * 100);\n}\n#elif defined(JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE)\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC_COARSE, &ts);\n\tnstime_init2(time, ts.tv_sec, ts.tv_nsec);\n}\n#elif defined(JEMALLOC_HAVE_CLOCK_MONOTONIC)\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tstruct timespec ts;\n\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\n\tnstime_init2(time, ts.tv_sec, ts.tv_nsec);\n}\n#elif defined(JEMALLOC_HAVE_MACH_ABSOLUTE_TIME)\n#  define NSTIME_MONOTONIC true\nstatic void\nnstime_get(nstime_t *time) {\n\tnstime_init(time, mach_absolute_time());\n}\n#else\n#  define NSTIME_MONOTONIC false\nstatic void\nnstime_get(nstime_t *time) {\n\tstruct timeval tv;\n\n\tgettimeofday(&tv, NULL);\n\tnstime_init2(time, tv.tv_sec, tv.tv_usec * 1000);\n}\n#endif\n\nstatic bool\nnstime_monotonic_impl(void) {\n\treturn NSTIME_MONOTONIC;\n#undef NSTIME_MONOTONIC\n}\nnstime_monotonic_t *JET_MUTABLE nstime_monotonic = nstime_monotonic_impl;\n\nstatic bool\nnstime_update_impl(nstime_t *time) {\n\tnstime_t old_time;\n\n\tnstime_copy(&old_time, time);\n\tnstime_get(time);\n\n\t/* Handle non-monotonic clocks. */\n\tif (unlikely(nstime_compare(&old_time, time) > 0)) {\n\t\tnstime_copy(time, &old_time);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\nnstime_update_t *JET_MUTABLE nstime_update = nstime_update_impl;\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/pages.c",
    "content": "#define JEMALLOC_PAGES_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n\n#include \"jemalloc/internal/pages.h\"\n\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n\n#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT\n#include <sys/sysctl.h>\n#ifdef __FreeBSD__\n#include <vm/vm_param.h>\n#endif\n#endif\n\n/******************************************************************************/\n/* Data. */\n\n/* Actual operating system page size, detected during bootstrap, <= PAGE. */\nstatic size_t\tos_page;\n\n#ifndef _WIN32\n#  define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE)\n#  define PAGES_PROT_DECOMMIT (PROT_NONE)\nstatic int\tmmap_flags;\n#endif\nstatic bool\tos_overcommits;\n\nconst char *thp_mode_names[] = {\n\t\"default\",\n\t\"always\",\n\t\"never\",\n\t\"not supported\"\n};\nthp_mode_t opt_thp = THP_MODE_DEFAULT;\nthp_mode_t init_system_thp_mode;\n\n/* Runtime support for lazy purge. Irrelevant when !pages_can_purge_lazy. */\nstatic bool pages_can_purge_lazy_runtime = true;\n\n/******************************************************************************/\n/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic void os_pages_unmap(void *addr, size_t size);\n\n/******************************************************************************/\n\nstatic void *\nos_pages_map(void *addr, size_t size, size_t alignment, bool *commit) {\n\tassert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);\n\tassert(ALIGNMENT_CEILING(size, os_page) == size);\n\tassert(size != 0);\n\n\tif (os_overcommits) {\n\t\t*commit = true;\n\t}\n\n\tvoid *ret;\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_RESERVE | (*commit ? MEM_COMMIT : 0),\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\t{\n\t\tint prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;\n\n\t\tret = mmap(addr, size, prot, mmap_flags, -1, 0);\n\t}\n\tassert(ret != NULL);\n\n\tif (ret == MAP_FAILED) {\n\t\tret = NULL;\n\t} else 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\tos_pages_unmap(ret, size);\n\t\tret = NULL;\n\t}\n#endif\n\tassert(ret == NULL || (addr == NULL && ret != addr) || (addr != NULL &&\n\t    ret == addr));\n\treturn ret;\n}\n\nstatic void *\nos_pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size,\n    bool *commit) {\n\tvoid *ret = (void *)((uintptr_t)addr + leadsize);\n\n\tassert(alloc_size >= leadsize + size);\n#ifdef _WIN32\n\tos_pages_unmap(addr, alloc_size);\n\tvoid *new_addr = os_pages_map(ret, size, PAGE, commit);\n\tif (new_addr == ret) {\n\t\treturn ret;\n\t}\n\tif (new_addr != NULL) {\n\t\tos_pages_unmap(new_addr, size);\n\t}\n\treturn NULL;\n#else\n\tsize_t trailsize = alloc_size - leadsize - size;\n\n\tif (leadsize != 0) {\n\t\tos_pages_unmap(addr, leadsize);\n\t}\n\tif (trailsize != 0) {\n\t\tos_pages_unmap((void *)((uintptr_t)ret + size), trailsize);\n\t}\n\treturn ret;\n#endif\n}\n\nstatic void\nos_pages_unmap(void *addr, size_t size) {\n\tassert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);\n\tassert(ALIGNMENT_CEILING(size, os_page) == size);\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(get_errno(), 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\t}\n\t}\n}\n\nstatic void *\npages_map_slow(size_t size, size_t alignment, bool *commit) {\n\tsize_t alloc_size = size + alignment - os_page;\n\t/* Beware size_t wrap-around. */\n\tif (alloc_size < size) {\n\t\treturn NULL;\n\t}\n\n\tvoid *ret;\n\tdo {\n\t\tvoid *pages = os_pages_map(NULL, alloc_size, alignment, commit);\n\t\tif (pages == NULL) {\n\t\t\treturn NULL;\n\t\t}\n\t\tsize_t leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment)\n\t\t    - (uintptr_t)pages;\n\t\tret = os_pages_trim(pages, alloc_size, leadsize, size, commit);\n\t} while (ret == NULL);\n\n\tassert(ret != NULL);\n\tassert(PAGE_ADDR2BASE(ret) == ret);\n\treturn ret;\n}\n\nvoid *\npages_map(void *addr, size_t size, size_t alignment, bool *commit) {\n\tassert(alignment >= PAGE);\n\tassert(ALIGNMENT_ADDR2BASE(addr, alignment) == addr);\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 * os_pages_unmap(), and it can leave holes in the process's virtual\n\t * memory map if memory grows downward.\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\tvoid *ret = os_pages_map(addr, size, os_page, commit);\n\tif (ret == NULL || ret == addr) {\n\t\treturn ret;\n\t}\n\tassert(addr == NULL);\n\tif (ALIGNMENT_ADDR2OFFSET(ret, alignment) != 0) {\n\t\tos_pages_unmap(ret, size);\n\t\treturn pages_map_slow(size, alignment, commit);\n\t}\n\n\tassert(PAGE_ADDR2BASE(ret) == ret);\n\treturn ret;\n}\n\nvoid\npages_unmap(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tos_pages_unmap(addr, size);\n}\n\nstatic bool\npages_commit_impl(void *addr, size_t size, bool commit) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tif (os_overcommits) {\n\t\treturn true;\n\t}\n\n#ifdef _WIN32\n\treturn (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT,\n\t    PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT)));\n#else\n\t{\n\t\tint prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;\n\t\tvoid *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED,\n\t\t    -1, 0);\n\t\tif (result == MAP_FAILED) {\n\t\t\treturn true;\n\t\t}\n\t\tif (result != addr) {\n\t\t\t/*\n\t\t\t * We succeeded in mapping memory, but not in the right\n\t\t\t * place.\n\t\t\t */\n\t\t\tos_pages_unmap(result, size);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n#endif\n}\n\nbool\npages_commit(void *addr, size_t size) {\n\treturn pages_commit_impl(addr, size, true);\n}\n\nbool\npages_decommit(void *addr, size_t size) {\n\treturn pages_commit_impl(addr, size, false);\n}\n\nbool\npages_purge_lazy(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tif (!pages_can_purge_lazy) {\n\t\treturn true;\n\t}\n\tif (!pages_can_purge_lazy_runtime) {\n\t\t/*\n\t\t * Built with lazy purge enabled, but detected it was not\n\t\t * supported on the current system.\n\t\t */\n\t\treturn true;\n\t}\n\n#ifdef _WIN32\n\tVirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE);\n\treturn false;\n#elif defined(JEMALLOC_PURGE_MADVISE_FREE)\n\treturn (madvise(addr, size,\n#  ifdef MADV_FREE\n\t    MADV_FREE\n#  else\n\t    JEMALLOC_MADV_FREE\n#  endif\n\t    ) != 0);\n#elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \\\n    !defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)\n\treturn (madvise(addr, size, MADV_DONTNEED) != 0);\n#else\n\tnot_reached();\n#endif\n}\n\nbool\npages_purge_forced(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n\n\tif (!pages_can_purge_forced) {\n\t\treturn true;\n\t}\n\n#if defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \\\n    defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)\n\treturn (madvise(addr, size, MADV_DONTNEED) != 0);\n#elif defined(JEMALLOC_MAPS_COALESCE)\n\t/* Try to overlay a new demand-zeroed mapping. */\n\treturn pages_commit(addr, size);\n#else\n\tnot_reached();\n#endif\n}\n\nstatic bool\npages_huge_impl(void *addr, size_t size, bool aligned) {\n\tif (aligned) {\n\t\tassert(HUGEPAGE_ADDR2BASE(addr) == addr);\n\t\tassert(HUGEPAGE_CEILING(size) == size);\n\t}\n#ifdef JEMALLOC_HAVE_MADVISE_HUGE\n\treturn (madvise(addr, size, MADV_HUGEPAGE) != 0);\n#else\n\treturn true;\n#endif\n}\n\nbool\npages_huge(void *addr, size_t size) {\n\treturn pages_huge_impl(addr, size, true);\n}\n\nstatic bool\npages_huge_unaligned(void *addr, size_t size) {\n\treturn pages_huge_impl(addr, size, false);\n}\n\nstatic bool\npages_nohuge_impl(void *addr, size_t size, bool aligned) {\n\tif (aligned) {\n\t\tassert(HUGEPAGE_ADDR2BASE(addr) == addr);\n\t\tassert(HUGEPAGE_CEILING(size) == size);\n\t}\n\n#ifdef JEMALLOC_HAVE_MADVISE_HUGE\n\treturn (madvise(addr, size, MADV_NOHUGEPAGE) != 0);\n#else\n\treturn false;\n#endif\n}\n\nbool\npages_nohuge(void *addr, size_t size) {\n\treturn pages_nohuge_impl(addr, size, true);\n}\n\nstatic bool\npages_nohuge_unaligned(void *addr, size_t size) {\n\treturn pages_nohuge_impl(addr, size, false);\n}\n\nbool\npages_dontdump(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n#ifdef JEMALLOC_MADVISE_DONTDUMP\n\treturn madvise(addr, size, MADV_DONTDUMP) != 0;\n#else\n\treturn false;\n#endif\n}\n\nbool\npages_dodump(void *addr, size_t size) {\n\tassert(PAGE_ADDR2BASE(addr) == addr);\n\tassert(PAGE_CEILING(size) == size);\n#ifdef JEMALLOC_MADVISE_DONTDUMP\n\treturn madvise(addr, size, MADV_DODUMP) != 0;\n#else\n\treturn false;\n#endif\n}\n\n\nstatic size_t\nos_page_detect(void) {\n#ifdef _WIN32\n\tSYSTEM_INFO si;\n\tGetSystemInfo(&si);\n\treturn si.dwPageSize;\n#elif defined(__FreeBSD__)\n\treturn getpagesize();\n#else\n\tlong result = sysconf(_SC_PAGESIZE);\n\tif (result == -1) {\n\t\treturn LG_PAGE;\n\t}\n\treturn (size_t)result;\n#endif\n}\n\n#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT\nstatic bool\nos_overcommits_sysctl(void) {\n\tint vm_overcommit;\n\tsize_t sz;\n\n\tsz = sizeof(vm_overcommit);\n#if defined(__FreeBSD__) && defined(VM_OVERCOMMIT)\n\tint mib[2];\n\n\tmib[0] = CTL_VM;\n\tmib[1] = VM_OVERCOMMIT;\n\tif (sysctl(mib, 2, &vm_overcommit, &sz, NULL, 0) != 0) {\n\t\treturn false; /* Error. */\n\t}\n#else\n\tif (sysctlbyname(\"vm.overcommit\", &vm_overcommit, &sz, NULL, 0) != 0) {\n\t\treturn false; /* Error. */\n\t}\n#endif\n\n\treturn ((vm_overcommit & 0x3) == 0);\n}\n#endif\n\n#ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY\n/*\n * Use syscall(2) rather than {open,read,close}(2) when possible to avoid\n * reentry during bootstrapping if another library has interposed system call\n * wrappers.\n */\nstatic bool\nos_overcommits_proc(void) {\n\tint fd;\n\tchar buf[1];\n\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)\n\t#if defined(O_CLOEXEC)\n\t\tfd = (int)syscall(SYS_open, \"/proc/sys/vm/overcommit_memory\", O_RDONLY |\n\t\t\tO_CLOEXEC);\n\t#else\n\t\tfd = (int)syscall(SYS_open, \"/proc/sys/vm/overcommit_memory\", O_RDONLY);\n\t\tif (fd != -1) {\n\t\t\tfcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);\n\t\t}\n\t#endif\n#elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat)\n\t#if defined(O_CLOEXEC)\n\t\tfd = (int)syscall(SYS_openat,\n\t\t\tAT_FDCWD, \"/proc/sys/vm/overcommit_memory\", O_RDONLY | O_CLOEXEC);\n\t#else\n\t\tfd = (int)syscall(SYS_openat,\n\t\t\tAT_FDCWD, \"/proc/sys/vm/overcommit_memory\", O_RDONLY);\n\t\tif (fd != -1) {\n\t\t\tfcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);\n\t\t}\n\t#endif\n#else\n\t#if defined(O_CLOEXEC)\n\t\tfd = open(\"/proc/sys/vm/overcommit_memory\", O_RDONLY | O_CLOEXEC);\n\t#else\n\t\tfd = open(\"/proc/sys/vm/overcommit_memory\", O_RDONLY);\n\t\tif (fd != -1) {\n\t\t\tfcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);\n\t\t}\n\t#endif\n#endif\n\n\tif (fd == -1) {\n\t\treturn false; /* Error. */\n\t}\n\n\tssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf));\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close)\n\tsyscall(SYS_close, fd);\n#else\n\tclose(fd);\n#endif\n\n\tif (nread < 1) {\n\t\treturn false; /* Error. */\n\t}\n\t/*\n\t * /proc/sys/vm/overcommit_memory meanings:\n\t * 0: Heuristic overcommit.\n\t * 1: Always overcommit.\n\t * 2: Never overcommit.\n\t */\n\treturn (buf[0] == '0' || buf[0] == '1');\n}\n#endif\n\nvoid\npages_set_thp_state (void *ptr, size_t size) {\n\tif (opt_thp == thp_mode_default || opt_thp == init_system_thp_mode) {\n\t\treturn;\n\t}\n\tassert(opt_thp != thp_mode_not_supported &&\n\t    init_system_thp_mode != thp_mode_not_supported);\n\n\tif (opt_thp == thp_mode_always\n\t    && init_system_thp_mode != thp_mode_never) {\n\t\tassert(init_system_thp_mode == thp_mode_default);\n\t\tpages_huge_unaligned(ptr, size);\n\t} else if (opt_thp == thp_mode_never) {\n\t\tassert(init_system_thp_mode == thp_mode_default ||\n\t\t    init_system_thp_mode == thp_mode_always);\n\t\tpages_nohuge_unaligned(ptr, size);\n\t}\n}\n\nstatic void\ninit_thp_state(void) {\n\tif (!have_madvise_huge) {\n\t\tif (metadata_thp_enabled() && opt_abort) {\n\t\t\tmalloc_write(\"<jemalloc>: no MADV_HUGEPAGE support\\n\");\n\t\t\tabort();\n\t\t}\n\t\tgoto label_error;\n\t}\n\n\tstatic const char sys_state_madvise[] = \"always [madvise] never\\n\";\n\tstatic const char sys_state_always[] = \"[always] madvise never\\n\";\n\tstatic const char sys_state_never[] = \"always madvise [never]\\n\";\n\tchar buf[sizeof(sys_state_madvise)];\n\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)\n\tint fd = (int)syscall(SYS_open,\n\t    \"/sys/kernel/mm/transparent_hugepage/enabled\", O_RDONLY);\n#else\n\tint fd = open(\"/sys/kernel/mm/transparent_hugepage/enabled\", O_RDONLY);\n#endif\n\tif (fd == -1) {\n\t\tgoto label_error;\n\t}\n\n\tssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf));\n#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close)\n\tsyscall(SYS_close, fd);\n#else\n\tclose(fd);\n#endif\n\n\tif (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) {\n\t\tinit_system_thp_mode = thp_mode_default;\n\t} else if (strncmp(buf, sys_state_always, (size_t)nread) == 0) {\n\t\tinit_system_thp_mode = thp_mode_always;\n\t} else if (strncmp(buf, sys_state_never, (size_t)nread) == 0) {\n\t\tinit_system_thp_mode = thp_mode_never;\n\t} else {\n\t\tgoto label_error;\n\t}\n\treturn;\nlabel_error:\n\topt_thp = init_system_thp_mode = thp_mode_not_supported;\n}\n\nbool\npages_boot(void) {\n\tos_page = os_page_detect();\n\tif (os_page > PAGE) {\n\t\tmalloc_write(\"<jemalloc>: Unsupported system page size\\n\");\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t\treturn true;\n\t}\n\n#ifndef _WIN32\n\tmmap_flags = MAP_PRIVATE | MAP_ANON;\n#endif\n\n#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT\n\tos_overcommits = os_overcommits_sysctl();\n#elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY)\n\tos_overcommits = os_overcommits_proc();\n#  ifdef MAP_NORESERVE\n\tif (os_overcommits) {\n\t\tmmap_flags |= MAP_NORESERVE;\n\t}\n#  endif\n#else\n\tos_overcommits = false;\n#endif\n\n\tinit_thp_state();\n\n\t/* Detect lazy purge runtime support. */\n\tif (pages_can_purge_lazy) {\n\t\tbool committed = false;\n\t\tvoid *madv_free_page = os_pages_map(NULL, PAGE, PAGE, &committed);\n\t\tif (madv_free_page == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tassert(pages_can_purge_lazy_runtime);\n\t\tif (pages_purge_lazy(madv_free_page, PAGE)) {\n\t\t\tpages_can_purge_lazy_runtime = false;\n\t\t}\n\t\tos_pages_unmap(madv_free_page, PAGE);\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/prng.c",
    "content": "#define JEMALLOC_PRNG_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/prof.c",
    "content": "#define JEMALLOC_PROF_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/ckh.h\"\n#include \"jemalloc/internal/hash.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n#include \"jemalloc/internal/mutex.h\"\n\n/******************************************************************************/\n\n#ifdef JEMALLOC_PROF_LIBUNWIND\n#define UNW_LOCAL_ONLY\n#include <libunwind.h>\n#endif\n\n#ifdef JEMALLOC_PROF_LIBGCC\n/*\n * We have a circular dependency -- jemalloc_internal.h tells us if we should\n * use libgcc's unwinding functionality, but after we've included that, we've\n * already hooked _Unwind_Backtrace.  We'll temporarily disable hooking.\n */\n#undef _Unwind_Backtrace\n#include <unwind.h>\n#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)\n#endif\n\n/******************************************************************************/\n/* Data. */\n\nbool\t\topt_prof = false;\nbool\t\topt_prof_active = true;\nbool\t\topt_prof_thread_active_init = 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 = false;\nbool\t\topt_prof_leak = false;\nbool\t\topt_prof_accum = false;\nchar\t\topt_prof_prefix[\n    /* Minimize memory bloat for non-prof builds. */\n#ifdef JEMALLOC_PROF\n    PATH_MAX +\n#endif\n    1];\n\n/*\n * Initialized as opt_prof_active, and accessed via\n * prof_active_[gs]et{_unlocked,}().\n */\nbool\t\t\tprof_active;\nstatic malloc_mutex_t\tprof_active_mtx;\n\n/*\n * Initialized as opt_prof_thread_active_init, and accessed via\n * prof_thread_active_init_[gs]et().\n */\nstatic bool\t\tprof_thread_active_init;\nstatic malloc_mutex_t\tprof_thread_active_init_mtx;\n\n/*\n * Initialized as opt_prof_gdump, and accessed via\n * prof_gdump_[gs]et{_unlocked,}().\n */\nbool\t\t\tprof_gdump_val;\nstatic malloc_mutex_t\tprof_gdump_mtx;\n\nuint64_t\tprof_interval = 0;\n\nsize_t\t\tlg_prof_sample;\n\n/*\n * Table of mutexes that are shared among gctx's.  These are leaf locks, so\n * there is no problem with using them for more than one gctx at the same time.\n * The primary motivation for this sharing though is that gctx's are ephemeral,\n * and destroying mutexes causes complications for systems that allocate when\n * creating/destroying mutexes.\n */\nstatic malloc_mutex_t\t*gctx_locks;\nstatic atomic_u_t\tcum_gctxs; /* Atomic counter. */\n\n/*\n * Table of mutexes that are shared among tdata's.  No operations require\n * holding multiple tdata locks, so there is no problem with using them for more\n * than one tdata at the same time, even though a gctx lock may be acquired\n * while holding a tdata lock.\n */\nstatic malloc_mutex_t\t*tdata_locks;\n\n/*\n * Global hash of (prof_bt_t *)-->(prof_gctx_t *).  This is the master data\n * structure that knows about all backtraces currently captured.\n */\nstatic ckh_t\t\tbt2gctx;\n/* Non static to enable profiling. */\nmalloc_mutex_t\t\tbt2gctx_mtx;\n\n/*\n * Tree of all extant prof_tdata_t structures, regardless of state,\n * {attached,detached,expired}.\n */\nstatic prof_tdata_tree_t\ttdatas;\nstatic malloc_mutex_t\ttdatas_mtx;\n\nstatic uint64_t\t\tnext_thr_uid;\nstatic malloc_mutex_t\tnext_thr_uid_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.\n */\nstatic malloc_mutex_t\tprof_dump_mtx;\nstatic char\t\tprof_dump_buf[\n    /* Minimize memory bloat for non-prof builds. */\n#ifdef JEMALLOC_PROF\n    PROF_DUMP_BUFSIZE\n#else\n    1\n#endif\n];\nstatic size_t\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/*\n * Function prototypes for static functions that are referenced prior to\n * definition.\n */\n\nstatic bool\tprof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx);\nstatic void\tprof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx);\nstatic bool\tprof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,\n    bool even_if_attached);\nstatic void\tprof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata,\n    bool even_if_attached);\nstatic char\t*prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name);\n\n/******************************************************************************/\n/* Red-black trees. */\n\nstatic int\nprof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) {\n\tuint64_t a_thr_uid = a->thr_uid;\n\tuint64_t b_thr_uid = b->thr_uid;\n\tint ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);\n\tif (ret == 0) {\n\t\tuint64_t a_thr_discrim = a->thr_discrim;\n\t\tuint64_t b_thr_discrim = b->thr_discrim;\n\t\tret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <\n\t\t    b_thr_discrim);\n\t\tif (ret == 0) {\n\t\t\tuint64_t a_tctx_uid = a->tctx_uid;\n\t\t\tuint64_t b_tctx_uid = b->tctx_uid;\n\t\t\tret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <\n\t\t\t    b_tctx_uid);\n\t\t}\n\t}\n\treturn ret;\n}\n\nrb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,\n    tctx_link, prof_tctx_comp)\n\nstatic int\nprof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) {\n\tunsigned a_len = a->bt.len;\n\tunsigned b_len = b->bt.len;\n\tunsigned comp_len = (a_len < b_len) ? a_len : b_len;\n\tint ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *));\n\tif (ret == 0) {\n\t\tret = (a_len > b_len) - (a_len < b_len);\n\t}\n\treturn ret;\n}\n\nrb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link,\n    prof_gctx_comp)\n\nstatic int\nprof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) {\n\tint ret;\n\tuint64_t a_uid = a->thr_uid;\n\tuint64_t b_uid = b->thr_uid;\n\n\tret = ((a_uid > b_uid) - (a_uid < b_uid));\n\tif (ret == 0) {\n\t\tuint64_t a_discrim = a->thr_discrim;\n\t\tuint64_t b_discrim = b->thr_discrim;\n\n\t\tret = ((a_discrim > b_discrim) - (a_discrim < b_discrim));\n\t}\n\treturn ret;\n}\n\nrb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link,\n    prof_tdata_comp)\n\n/******************************************************************************/\n\nvoid\nprof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\tif (updated) {\n\t\t/*\n\t\t * Compute a new sample threshold.  This isn't very important in\n\t\t * practice, because this function is rarely executed, so the\n\t\t * potential for sample bias is minimal except in contrived\n\t\t * programs.\n\t\t */\n\t\ttdata = prof_tdata_get(tsd, true);\n\t\tif (tdata != NULL) {\n\t\t\tprof_sample_threshold_update(tdata);\n\t\t}\n\t}\n\n\tif ((uintptr_t)tctx > (uintptr_t)1U) {\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);\n\t\ttctx->prepared = false;\n\t\tif (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {\n\t\t\tprof_tctx_destroy(tsd, tctx);\n\t\t} else {\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);\n\t\t}\n\t}\n}\n\nvoid\nprof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,\n    prof_tctx_t *tctx) {\n\tprof_tctx_set(tsdn, ptr, usize, NULL, tctx);\n\n\tmalloc_mutex_lock(tsdn, tctx->tdata->lock);\n\ttctx->cnts.curobjs++;\n\ttctx->cnts.curbytes += usize;\n\tif (opt_prof_accum) {\n\t\ttctx->cnts.accumobjs++;\n\t\ttctx->cnts.accumbytes += usize;\n\t}\n\ttctx->prepared = false;\n\tmalloc_mutex_unlock(tsdn, tctx->tdata->lock);\n}\n\nvoid\nprof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);\n\tassert(tctx->cnts.curobjs > 0);\n\tassert(tctx->cnts.curbytes >= usize);\n\ttctx->cnts.curobjs--;\n\ttctx->cnts.curbytes -= usize;\n\n\tif (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {\n\t\tprof_tctx_destroy(tsd, tctx);\n\t} else {\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);\n\t}\n}\n\nvoid\nbt_init(prof_bt_t *bt, void **vec) {\n\tcassert(config_prof);\n\n\tbt->vec = vec;\n\tbt->len = 0;\n}\n\nstatic void\nprof_enter(tsd_t *tsd, prof_tdata_t *tdata) {\n\tcassert(config_prof);\n\tassert(tdata == prof_tdata_get(tsd, false));\n\n\tif (tdata != NULL) {\n\t\tassert(!tdata->enq);\n\t\ttdata->enq = true;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);\n}\n\nstatic void\nprof_leave(tsd_t *tsd, prof_tdata_t *tdata) {\n\tcassert(config_prof);\n\tassert(tdata == prof_tdata_get(tsd, false));\n\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);\n\n\tif (tdata != NULL) {\n\t\tbool idump, gdump;\n\n\t\tassert(tdata->enq);\n\t\ttdata->enq = false;\n\t\tidump = tdata->enq_idump;\n\t\ttdata->enq_idump = false;\n\t\tgdump = tdata->enq_gdump;\n\t\ttdata->enq_gdump = false;\n\n\t\tif (idump) {\n\t\t\tprof_idump(tsd_tsdn(tsd));\n\t\t}\n\t\tif (gdump) {\n\t\t\tprof_gdump(tsd_tsdn(tsd));\n\t\t}\n\t}\n}\n\n#ifdef JEMALLOC_PROF_LIBUNWIND\nvoid\nprof_backtrace(prof_bt_t *bt) {\n\tint nframes;\n\n\tcassert(config_prof);\n\tassert(bt->len == 0);\n\tassert(bt->vec != NULL);\n\n\tnframes = unw_backtrace(bt->vec, PROF_BT_MAX);\n\tif (nframes <= 0) {\n\t\treturn;\n\t}\n\tbt->len = nframes;\n}\n#elif (defined(JEMALLOC_PROF_LIBGCC))\nstatic _Unwind_Reason_Code\nprof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {\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\tprof_unwind_data_t *data = (prof_unwind_data_t *)arg;\n\tvoid *ip;\n\n\tcassert(config_prof);\n\n\tip = (void *)_Unwind_GetIP(context);\n\tif (ip == NULL) {\n\t\treturn _URC_END_OF_STACK;\n\t}\n\tdata->bt->vec[data->bt->len] = ip;\n\tdata->bt->len++;\n\tif (data->bt->len == data->max) {\n\t\treturn _URC_END_OF_STACK;\n\t}\n\n\treturn _URC_NO_REASON;\n}\n\nvoid\nprof_backtrace(prof_bt_t *bt) {\n\tprof_unwind_data_t data = {bt, 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) {\n#define BT_FRAME(i)\t\t\t\t\t\t\t\\\n\tif ((i) < PROF_BT_MAX) {\t\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\t}\t\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\\\n\t\t\treturn;\t\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tbt->vec[(i)] = p;\t\t\t\t\t\\\n\t\tbt->len = (i) + 1;\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}\n\n\tcassert(config_prof);\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#undef BT_FRAME\n}\n#else\nvoid\nprof_backtrace(prof_bt_t *bt) {\n\tcassert(config_prof);\n\tnot_reached();\n}\n#endif\n\nstatic malloc_mutex_t *\nprof_gctx_mutex_choose(void) {\n\tunsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED);\n\n\treturn &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS];\n}\n\nstatic malloc_mutex_t *\nprof_tdata_mutex_choose(uint64_t thr_uid) {\n\treturn &tdata_locks[thr_uid % PROF_NTDATA_LOCKS];\n}\n\nstatic prof_gctx_t *\nprof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) {\n\t/*\n\t * Create a single allocation that has space for vec of length bt->len.\n\t */\n\tsize_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *));\n\tprof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size,\n\t    sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true),\n\t    true);\n\tif (gctx == NULL) {\n\t\treturn NULL;\n\t}\n\tgctx->lock = prof_gctx_mutex_choose();\n\t/*\n\t * Set nlimbo to 1, in order to avoid a race condition with\n\t * prof_tctx_destroy()/prof_gctx_try_destroy().\n\t */\n\tgctx->nlimbo = 1;\n\ttctx_tree_new(&gctx->tctxs);\n\t/* Duplicate bt. */\n\tmemcpy(gctx->vec, bt->vec, bt->len * sizeof(void *));\n\tgctx->bt.vec = gctx->vec;\n\tgctx->bt.len = bt->len;\n\treturn gctx;\n}\n\nstatic void\nprof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx,\n    prof_tdata_t *tdata) {\n\tcassert(config_prof);\n\n\t/*\n\t * Check that gctx is still unused by any thread cache before destroying\n\t * it.  prof_lookup() increments gctx->nlimbo in order to avoid a race\n\t * condition with this function, as does prof_tctx_destroy() in order to\n\t * avoid a race between the main body of prof_tctx_destroy() and entry\n\t * into this function.\n\t */\n\tprof_enter(tsd, tdata_self);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\tassert(gctx->nlimbo != 0);\n\tif (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {\n\t\t/* Remove gctx from bt2gctx. */\n\t\tif (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) {\n\t\t\tnot_reached();\n\t\t}\n\t\tprof_leave(tsd, tdata_self);\n\t\t/* Destroy gctx. */\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\tidalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true);\n\t} else {\n\t\t/*\n\t\t * Compensate for increment in prof_tctx_destroy() or\n\t\t * prof_lookup().\n\t\t */\n\t\tgctx->nlimbo--;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\tprof_leave(tsd, tdata_self);\n\t}\n}\n\nstatic bool\nprof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx) {\n\tmalloc_mutex_assert_owner(tsdn, tctx->tdata->lock);\n\n\tif (opt_prof_accum) {\n\t\treturn false;\n\t}\n\tif (tctx->cnts.curobjs != 0) {\n\t\treturn false;\n\t}\n\tif (tctx->prepared) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool\nprof_gctx_should_destroy(prof_gctx_t *gctx) {\n\tif (opt_prof_accum) {\n\t\treturn false;\n\t}\n\tif (!tctx_tree_empty(&gctx->tctxs)) {\n\t\treturn false;\n\t}\n\tif (gctx->nlimbo != 0) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic void\nprof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) {\n\tprof_tdata_t *tdata = tctx->tdata;\n\tprof_gctx_t *gctx = tctx->gctx;\n\tbool destroy_tdata, destroy_tctx, destroy_gctx;\n\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);\n\n\tassert(tctx->cnts.curobjs == 0);\n\tassert(tctx->cnts.curbytes == 0);\n\tassert(!opt_prof_accum);\n\tassert(tctx->cnts.accumobjs == 0);\n\tassert(tctx->cnts.accumbytes == 0);\n\n\tckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL);\n\tdestroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata, false);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_nominal:\n\t\ttctx_tree_remove(&gctx->tctxs, tctx);\n\t\tdestroy_tctx = true;\n\t\tif (prof_gctx_should_destroy(gctx)) {\n\t\t\t/*\n\t\t\t * Increment gctx->nlimbo in order to keep another\n\t\t\t * thread from winning the race to destroy gctx while\n\t\t\t * this one has gctx->lock dropped.  Without this, it\n\t\t\t * would be possible for another thread to:\n\t\t\t *\n\t\t\t * 1) Sample an allocation associated with gctx.\n\t\t\t * 2) Deallocate the sampled object.\n\t\t\t * 3) Successfully prof_gctx_try_destroy(gctx).\n\t\t\t *\n\t\t\t * The result would be that gctx no longer exists by the\n\t\t\t * time this thread accesses it in\n\t\t\t * prof_gctx_try_destroy().\n\t\t\t */\n\t\t\tgctx->nlimbo++;\n\t\t\tdestroy_gctx = true;\n\t\t} else {\n\t\t\tdestroy_gctx = false;\n\t\t}\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\t\t/*\n\t\t * A dumping thread needs tctx to remain valid until dumping\n\t\t * has finished.  Change state such that the dumping thread will\n\t\t * complete destruction during a late dump iteration phase.\n\t\t */\n\t\ttctx->state = prof_tctx_state_purgatory;\n\t\tdestroy_tctx = false;\n\t\tdestroy_gctx = false;\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t\tdestroy_tctx = false;\n\t\tdestroy_gctx = false;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\tif (destroy_gctx) {\n\t\tprof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx,\n\t\t    tdata);\n\t}\n\n\tmalloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock);\n\n\tif (destroy_tdata) {\n\t\tprof_tdata_destroy(tsd, tdata, false);\n\t}\n\n\tif (destroy_tctx) {\n\t\tidalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true);\n\t}\n}\n\nstatic bool\nprof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,\n    void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) {\n\tunion {\n\t\tprof_gctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} gctx, tgctx;\n\tunion {\n\t\tprof_bt_t\t*p;\n\t\tvoid\t\t*v;\n\t} btkey;\n\tbool new_gctx;\n\n\tprof_enter(tsd, tdata);\n\tif (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {\n\t\t/* bt has never been seen before.  Insert it. */\n\t\tprof_leave(tsd, tdata);\n\t\ttgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt);\n\t\tif (tgctx.v == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tprof_enter(tsd, tdata);\n\t\tif (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {\n\t\t\tgctx.p = tgctx.p;\n\t\t\tbtkey.p = &gctx.p->bt;\n\t\t\tif (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {\n\t\t\t\t/* OOM. */\n\t\t\t\tprof_leave(tsd, tdata);\n\t\t\t\tidalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL,\n\t\t\t\t    true, true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tnew_gctx = true;\n\t\t} else {\n\t\t\tnew_gctx = false;\n\t\t}\n\t} else {\n\t\ttgctx.v = NULL;\n\t\tnew_gctx = false;\n\t}\n\n\tif (!new_gctx) {\n\t\t/*\n\t\t * Increment nlimbo, in order to avoid a race condition with\n\t\t * prof_tctx_destroy()/prof_gctx_try_destroy().\n\t\t */\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock);\n\t\tgctx.p->nlimbo++;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock);\n\t\tnew_gctx = false;\n\n\t\tif (tgctx.v != NULL) {\n\t\t\t/* Lost race to insert. */\n\t\t\tidalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true,\n\t\t\t    true);\n\t\t}\n\t}\n\tprof_leave(tsd, tdata);\n\n\t*p_btkey = btkey.v;\n\t*p_gctx = gctx.p;\n\t*p_new_gctx = new_gctx;\n\treturn false;\n}\n\nprof_tctx_t *\nprof_lookup(tsd_t *tsd, prof_bt_t *bt) {\n\tunion {\n\t\tprof_tctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} ret;\n\tprof_tdata_t *tdata;\n\tbool not_found;\n\n\tcassert(config_prof);\n\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn NULL;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);\n\tnot_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v);\n\tif (!not_found) { /* Note double negative! */\n\t\tret.p->prepared = true;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\tif (not_found) {\n\t\tvoid *btkey;\n\t\tprof_gctx_t *gctx;\n\t\tbool new_gctx, error;\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\tif (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx,\n\t\t    &new_gctx)) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/* Link a prof_tctx_t into gctx for this thread. */\n\t\tret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t),\n\t\t    sz_size2index(sizeof(prof_tctx_t)), false, NULL, true,\n\t\t    arena_ichoose(tsd, NULL), true);\n\t\tif (ret.p == NULL) {\n\t\t\tif (new_gctx) {\n\t\t\t\tprof_gctx_try_destroy(tsd, tdata, gctx, tdata);\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t\tret.p->tdata = tdata;\n\t\tret.p->thr_uid = tdata->thr_uid;\n\t\tret.p->thr_discrim = tdata->thr_discrim;\n\t\tmemset(&ret.p->cnts, 0, sizeof(prof_cnt_t));\n\t\tret.p->gctx = gctx;\n\t\tret.p->tctx_uid = tdata->tctx_uid_next++;\n\t\tret.p->prepared = true;\n\t\tret.p->state = prof_tctx_state_initializing;\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);\n\t\terror = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v);\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\t\tif (error) {\n\t\t\tif (new_gctx) {\n\t\t\t\tprof_gctx_try_destroy(tsd, tdata, gctx, tdata);\n\t\t\t}\n\t\t\tidalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true);\n\t\t\treturn NULL;\n\t\t}\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\t\tret.p->state = prof_tctx_state_nominal;\n\t\ttctx_tree_insert(&gctx->tctxs, ret.p);\n\t\tgctx->nlimbo--;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t}\n\n\treturn ret.p;\n}\n\n/*\n * The bodies of this function and prof_leakcheck() are compiled out unless heap\n * profiling is enabled, so that it is possible to compile jemalloc with\n * floating point support completely disabled.  Avoiding floating point code is\n * important on memory-constrained systems, but it also enables a workaround for\n * versions of glibc that don't properly save/restore floating point registers\n * during dynamic lazy symbol loading (which internally calls into whatever\n * malloc implementation happens to be integrated into the application).  Note\n * that some compilers (e.g.  gcc 4.8) may use floating point registers for fast\n * memory moves, so jemalloc must be compiled with such optimizations disabled\n * (e.g.\n * -mno-sse) in order for the workaround to be complete.\n */\nvoid\nprof_sample_threshold_update(prof_tdata_t *tdata) {\n#ifdef JEMALLOC_PROF\n\tuint64_t r;\n\tdouble u;\n\n\tif (!config_prof) {\n\t\treturn;\n\t}\n\n\tif (lg_prof_sample == 0) {\n\t\ttdata->bytes_until_sample = 0;\n\t\treturn;\n\t}\n\n\t/*\n\t * Compute sample interval as a geometrically distributed random\n\t * variable with mean (2^lg_prof_sample).\n\t *\n\t *                             __        __\n\t *                             |  log(u)  |                     1\n\t * tdata->bytes_until_sample = | -------- |, where p = ---------------\n\t *                             | log(1-p) |             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://luc.devroye.org/rnbookindex.html)\n\t */\n\tr = prng_lg_range_u64(&tdata->prng_state, 53);\n\tu = (double)r * (1.0/9007199254740992.0L);\n\ttdata->bytes_until_sample = (uint64_t)(log(u) /\n\t    log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))\n\t    + (uint64_t)1U;\n#endif\n}\n\n#ifdef JEMALLOC_JET\nstatic prof_tdata_t *\nprof_tdata_count_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *arg) {\n\tsize_t *tdata_count = (size_t *)arg;\n\n\t(*tdata_count)++;\n\n\treturn NULL;\n}\n\nsize_t\nprof_tdata_count(void) {\n\tsize_t tdata_count = 0;\n\ttsdn_t *tsdn;\n\n\ttsdn = tsdn_fetch();\n\tmalloc_mutex_lock(tsdn, &tdatas_mtx);\n\ttdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter,\n\t    (void *)&tdata_count);\n\tmalloc_mutex_unlock(tsdn, &tdatas_mtx);\n\n\treturn tdata_count;\n}\n\nsize_t\nprof_bt_count(void) {\n\tsize_t bt_count;\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\n\ttsd = tsd_fetch();\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn 0;\n\t}\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);\n\tbt_count = ckh_count(&bt2gctx);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);\n\n\treturn bt_count;\n}\n#endif\n\nstatic int\nprof_dump_open_impl(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tfd = creat(filename, 0644);\n\tif (fd == -1 && !propagate_err) {\n\t\tmalloc_printf(\"<jemalloc>: creat(\\\"%s\\\"), 0644) failed\\n\",\n\t\t    filename);\n\t\tif (opt_abort) {\n\t\t\tabort();\n\t\t}\n\t}\n\n\treturn fd;\n}\nprof_dump_open_t *JET_MUTABLE prof_dump_open = prof_dump_open_impl;\n\nstatic bool\nprof_dump_flush(bool propagate_err) {\n\tbool ret = false;\n\tssize_t err;\n\n\tcassert(config_prof);\n\n\terr = malloc_write_fd(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);\n\tif (err == -1) {\n\t\tif (!propagate_err) {\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\t}\n\t\t}\n\t\tret = true;\n\t}\n\tprof_dump_buf_end = 0;\n\n\treturn ret;\n}\n\nstatic bool\nprof_dump_close(bool propagate_err) {\n\tbool ret;\n\n\tassert(prof_dump_fd != -1);\n\tret = prof_dump_flush(propagate_err);\n\tclose(prof_dump_fd);\n\tprof_dump_fd = -1;\n\n\treturn ret;\n}\n\nstatic bool\nprof_dump_write(bool propagate_err, const char *s) {\n\tsize_t 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_dump_flush(propagate_err) && propagate_err) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\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_FORMAT_PRINTF(2, 3)\nstatic bool\nprof_dump_printf(bool propagate_err, const char *format, ...) {\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_dump_write(propagate_err, buf);\n\n\treturn ret;\n}\n\nstatic void\nprof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) {\n\tmalloc_mutex_assert_owner(tsdn, tctx->tdata->lock);\n\n\tmalloc_mutex_lock(tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_initializing:\n\t\tmalloc_mutex_unlock(tsdn, tctx->gctx->lock);\n\t\treturn;\n\tcase prof_tctx_state_nominal:\n\t\ttctx->state = prof_tctx_state_dumping;\n\t\tmalloc_mutex_unlock(tsdn, tctx->gctx->lock);\n\n\t\tmemcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));\n\n\t\ttdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;\n\t\ttdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;\n\t\tif (opt_prof_accum) {\n\t\t\ttdata->cnt_summed.accumobjs +=\n\t\t\t    tctx->dump_cnts.accumobjs;\n\t\t\ttdata->cnt_summed.accumbytes +=\n\t\t\t    tctx->dump_cnts.accumbytes;\n\t\t}\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\tcase prof_tctx_state_purgatory:\n\t\tnot_reached();\n\t}\n}\n\nstatic void\nprof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) {\n\tmalloc_mutex_assert_owner(tsdn, gctx->lock);\n\n\tgctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs;\n\tgctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes;\n\tif (opt_prof_accum) {\n\t\tgctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;\n\t\tgctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;\n\t}\n}\n\nstatic prof_tctx_t *\nprof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {\n\ttsdn_t *tsdn = (tsdn_t *)arg;\n\n\tmalloc_mutex_assert_owner(tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_nominal:\n\t\t/* New since dumping started; ignore. */\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\tcase prof_tctx_state_purgatory:\n\t\tprof_tctx_merge_gctx(tsdn, tctx, tctx->gctx);\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n\n\treturn NULL;\n}\n\nstruct prof_tctx_dump_iter_arg_s {\n\ttsdn_t\t*tsdn;\n\tbool\tpropagate_err;\n};\n\nstatic prof_tctx_t *\nprof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) {\n\tstruct prof_tctx_dump_iter_arg_s *arg =\n\t    (struct prof_tctx_dump_iter_arg_s *)opaque;\n\n\tmalloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_initializing:\n\tcase prof_tctx_state_nominal:\n\t\t/* Not captured by this dump. */\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\tcase prof_tctx_state_purgatory:\n\t\tif (prof_dump_printf(arg->propagate_err,\n\t\t    \"  t%\"FMTu64\": %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": \"\n\t\t    \"%\"FMTu64\"]\\n\", tctx->thr_uid, tctx->dump_cnts.curobjs,\n\t\t    tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,\n\t\t    tctx->dump_cnts.accumbytes)) {\n\t\t\treturn tctx;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n\treturn NULL;\n}\n\nstatic prof_tctx_t *\nprof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {\n\ttsdn_t *tsdn = (tsdn_t *)arg;\n\tprof_tctx_t *ret;\n\n\tmalloc_mutex_assert_owner(tsdn, tctx->gctx->lock);\n\n\tswitch (tctx->state) {\n\tcase prof_tctx_state_nominal:\n\t\t/* New since dumping started; ignore. */\n\t\tbreak;\n\tcase prof_tctx_state_dumping:\n\t\ttctx->state = prof_tctx_state_nominal;\n\t\tbreak;\n\tcase prof_tctx_state_purgatory:\n\t\tret = tctx;\n\t\tgoto label_return;\n\tdefault:\n\t\tnot_reached();\n\t}\n\n\tret = NULL;\nlabel_return:\n\treturn ret;\n}\n\nstatic void\nprof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) {\n\tcassert(config_prof);\n\n\tmalloc_mutex_lock(tsdn, gctx->lock);\n\n\t/*\n\t * Increment nlimbo so that gctx won't go away before dump.\n\t * Additionally, link gctx into the dump list so that it is included in\n\t * prof_dump()'s second pass.\n\t */\n\tgctx->nlimbo++;\n\tgctx_tree_insert(gctxs, gctx);\n\n\tmemset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t));\n\n\tmalloc_mutex_unlock(tsdn, gctx->lock);\n}\n\nstruct prof_gctx_merge_iter_arg_s {\n\ttsdn_t\t*tsdn;\n\tsize_t\tleak_ngctx;\n};\n\nstatic prof_gctx_t *\nprof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {\n\tstruct prof_gctx_merge_iter_arg_s *arg =\n\t    (struct prof_gctx_merge_iter_arg_s *)opaque;\n\n\tmalloc_mutex_lock(arg->tsdn, gctx->lock);\n\ttctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter,\n\t    (void *)arg->tsdn);\n\tif (gctx->cnt_summed.curobjs != 0) {\n\t\targ->leak_ngctx++;\n\t}\n\tmalloc_mutex_unlock(arg->tsdn, gctx->lock);\n\n\treturn NULL;\n}\n\nstatic void\nprof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) {\n\tprof_tdata_t *tdata = prof_tdata_get(tsd, false);\n\tprof_gctx_t *gctx;\n\n\t/*\n\t * Standard tree iteration won't work here, because as soon as we\n\t * decrement gctx->nlimbo and unlock gctx, another thread can\n\t * concurrently destroy it, which will corrupt the tree.  Therefore,\n\t * tear down the tree one node at a time during iteration.\n\t */\n\twhile ((gctx = gctx_tree_first(gctxs)) != NULL) {\n\t\tgctx_tree_remove(gctxs, gctx);\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);\n\t\t{\n\t\t\tprof_tctx_t *next;\n\n\t\t\tnext = NULL;\n\t\t\tdo {\n\t\t\t\tprof_tctx_t *to_destroy =\n\t\t\t\t    tctx_tree_iter(&gctx->tctxs, next,\n\t\t\t\t    prof_tctx_finish_iter,\n\t\t\t\t    (void *)tsd_tsdn(tsd));\n\t\t\t\tif (to_destroy != NULL) {\n\t\t\t\t\tnext = tctx_tree_next(&gctx->tctxs,\n\t\t\t\t\t    to_destroy);\n\t\t\t\t\ttctx_tree_remove(&gctx->tctxs,\n\t\t\t\t\t    to_destroy);\n\t\t\t\t\tidalloctm(tsd_tsdn(tsd), to_destroy,\n\t\t\t\t\t    NULL, NULL, true, true);\n\t\t\t\t} else {\n\t\t\t\t\tnext = NULL;\n\t\t\t\t}\n\t\t\t} while (next != NULL);\n\t\t}\n\t\tgctx->nlimbo--;\n\t\tif (prof_gctx_should_destroy(gctx)) {\n\t\t\tgctx->nlimbo++;\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\t\tprof_gctx_try_destroy(tsd, tdata, gctx, tdata);\n\t\t} else {\n\t\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);\n\t\t}\n\t}\n}\n\nstruct prof_tdata_merge_iter_arg_s {\n\ttsdn_t\t\t*tsdn;\n\tprof_cnt_t\tcnt_all;\n};\n\nstatic prof_tdata_t *\nprof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *opaque) {\n\tstruct prof_tdata_merge_iter_arg_s *arg =\n\t    (struct prof_tdata_merge_iter_arg_s *)opaque;\n\n\tmalloc_mutex_lock(arg->tsdn, tdata->lock);\n\tif (!tdata->expired) {\n\t\tsize_t tabind;\n\t\tunion {\n\t\t\tprof_tctx_t\t*p;\n\t\t\tvoid\t\t*v;\n\t\t} tctx;\n\n\t\ttdata->dumping = true;\n\t\tmemset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t));\n\t\tfor (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL,\n\t\t    &tctx.v);) {\n\t\t\tprof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata);\n\t\t}\n\n\t\targ->cnt_all.curobjs += tdata->cnt_summed.curobjs;\n\t\targ->cnt_all.curbytes += tdata->cnt_summed.curbytes;\n\t\tif (opt_prof_accum) {\n\t\t\targ->cnt_all.accumobjs += tdata->cnt_summed.accumobjs;\n\t\t\targ->cnt_all.accumbytes += tdata->cnt_summed.accumbytes;\n\t\t}\n\t} else {\n\t\ttdata->dumping = false;\n\t}\n\tmalloc_mutex_unlock(arg->tsdn, tdata->lock);\n\n\treturn NULL;\n}\n\nstatic prof_tdata_t *\nprof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *arg) {\n\tbool propagate_err = *(bool *)arg;\n\n\tif (!tdata->dumping) {\n\t\treturn NULL;\n\t}\n\n\tif (prof_dump_printf(propagate_err,\n\t    \"  t%\"FMTu64\": %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": %\"FMTu64\"]%s%s\\n\",\n\t    tdata->thr_uid, tdata->cnt_summed.curobjs,\n\t    tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs,\n\t    tdata->cnt_summed.accumbytes,\n\t    (tdata->thread_name != NULL) ? \" \" : \"\",\n\t    (tdata->thread_name != NULL) ? tdata->thread_name : \"\")) {\n\t\treturn tdata;\n\t}\n\treturn NULL;\n}\n\nstatic bool\nprof_dump_header_impl(tsdn_t *tsdn, bool propagate_err,\n    const prof_cnt_t *cnt_all) {\n\tbool ret;\n\n\tif (prof_dump_printf(propagate_err,\n\t    \"heap_v2/%\"FMTu64\"\\n\"\n\t    \"  t*: %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": %\"FMTu64\"]\\n\",\n\t    ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs,\n\t    cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) {\n\t\treturn true;\n\t}\n\n\tmalloc_mutex_lock(tsdn, &tdatas_mtx);\n\tret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter,\n\t    (void *)&propagate_err) != NULL);\n\tmalloc_mutex_unlock(tsdn, &tdatas_mtx);\n\treturn ret;\n}\nprof_dump_header_t *JET_MUTABLE prof_dump_header = prof_dump_header_impl;\n\nstatic bool\nprof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx,\n    const prof_bt_t *bt, prof_gctx_tree_t *gctxs) {\n\tbool ret;\n\tunsigned i;\n\tstruct prof_tctx_dump_iter_arg_s prof_tctx_dump_iter_arg;\n\n\tcassert(config_prof);\n\tmalloc_mutex_assert_owner(tsdn, gctx->lock);\n\n\t/* Avoid dumping such gctx's that have no useful data. */\n\tif ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) ||\n\t    (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) {\n\t\tassert(gctx->cnt_summed.curobjs == 0);\n\t\tassert(gctx->cnt_summed.curbytes == 0);\n\t\tassert(gctx->cnt_summed.accumobjs == 0);\n\t\tassert(gctx->cnt_summed.accumbytes == 0);\n\t\tret = false;\n\t\tgoto label_return;\n\t}\n\n\tif (prof_dump_printf(propagate_err, \"@\")) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\tfor (i = 0; i < bt->len; i++) {\n\t\tif (prof_dump_printf(propagate_err, \" %#\"FMTxPTR,\n\t\t    (uintptr_t)bt->vec[i])) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tif (prof_dump_printf(propagate_err,\n\t    \"\\n\"\n\t    \"  t*: %\"FMTu64\": %\"FMTu64\" [%\"FMTu64\": %\"FMTu64\"]\\n\",\n\t    gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes,\n\t    gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tprof_tctx_dump_iter_arg.tsdn = tsdn;\n\tprof_tctx_dump_iter_arg.propagate_err = propagate_err;\n\tif (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter,\n\t    (void *)&prof_tctx_dump_iter_arg) != NULL) {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tret = false;\nlabel_return:\n\treturn ret;\n}\n\n#ifndef _WIN32\nJEMALLOC_FORMAT_PRINTF(1, 2)\nstatic int\nprof_open_maps(const char *format, ...) {\n\tint mfd;\n\tva_list ap;\n\tchar filename[PATH_MAX + 1];\n\n\tva_start(ap, format);\n\tmalloc_vsnprintf(filename, sizeof(filename), format, ap);\n\tva_end(ap);\n\n#if defined(O_CLOEXEC)\n\tmfd = open(filename, O_RDONLY | O_CLOEXEC);\n#else\n\tmfd = open(filename, O_RDONLY);\n\tif (mfd != -1) {\n\t\tfcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);\n\t}\n#endif\n\n\treturn mfd;\n}\n#endif\n\nstatic int\nprof_getpid(void) {\n#ifdef _WIN32\n\treturn GetCurrentProcessId();\n#else\n\treturn getpid();\n#endif\n}\n\nstatic bool\nprof_dump_maps(bool propagate_err) {\n\tbool ret;\n\tint mfd;\n\n\tcassert(config_prof);\n#ifdef __FreeBSD__\n\tmfd = prof_open_maps(\"/proc/curproc/map\");\n#elif defined(_WIN32)\n\tmfd = -1; // Not implemented\n#else\n\t{\n\t\tint pid = prof_getpid();\n\n\t\tmfd = prof_open_maps(\"/proc/%d/task/%d/maps\", pid, pid);\n\t\tif (mfd == -1) {\n\t\t\tmfd = prof_open_maps(\"/proc/%d/maps\", pid);\n\t\t}\n\t}\n#endif\n\tif (mfd != -1) {\n\t\tssize_t nread;\n\n\t\tif (prof_dump_write(propagate_err, \"\\nMAPPED_LIBRARIES:\\n\") &&\n\t\t    propagate_err) {\n\t\t\tret = true;\n\t\t\tgoto label_return;\n\t\t}\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_dump_flush(propagate_err) &&\n\t\t\t\t    propagate_err) {\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\tnread = malloc_read_fd(mfd,\n\t\t\t    &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE\n\t\t\t    - prof_dump_buf_end);\n\t\t} while (nread > 0);\n\t} else {\n\t\tret = true;\n\t\tgoto label_return;\n\t}\n\n\tret = false;\nlabel_return:\n\tif (mfd != -1) {\n\t\tclose(mfd);\n\t}\n\treturn ret;\n}\n\n/*\n * See prof_sample_threshold_update() comment for why the body of this function\n * is conditionally compiled.\n */\nstatic void\nprof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx,\n    const char *filename) {\n#ifdef JEMALLOC_PROF\n\t/*\n\t * Scaling is equivalent AdjustSamples() in jeprof, but the result may\n\t * differ slightly from what jeprof reports, because here we scale the\n\t * summary values, whereas jeprof scales each context individually and\n\t * reports the sums of the scaled values.\n\t */\n\tif (cnt_all->curbytes != 0) {\n\t\tdouble sample_period = (double)((uint64_t)1 << lg_prof_sample);\n\t\tdouble ratio = (((double)cnt_all->curbytes) /\n\t\t    (double)cnt_all->curobjs) / sample_period;\n\t\tdouble scale_factor = 1.0 / (1.0 - exp(-ratio));\n\t\tuint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes)\n\t\t    * scale_factor);\n\t\tuint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) *\n\t\t    scale_factor);\n\n\t\tmalloc_printf(\"<jemalloc>: Leak approximation summary: ~%\"FMTu64\n\t\t    \" byte%s, ~%\"FMTu64\" object%s, >= %zu context%s\\n\",\n\t\t    curbytes, (curbytes != 1) ? \"s\" : \"\", curobjs, (curobjs !=\n\t\t    1) ? \"s\" : \"\", leak_ngctx, (leak_ngctx != 1) ? \"s\" : \"\");\n\t\tmalloc_printf(\n\t\t    \"<jemalloc>: Run jeprof on \\\"%s\\\" for leak detail\\n\",\n\t\t    filename);\n\t}\n#endif\n}\n\nstruct prof_gctx_dump_iter_arg_s {\n\ttsdn_t\t*tsdn;\n\tbool\tpropagate_err;\n};\n\nstatic prof_gctx_t *\nprof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {\n\tprof_gctx_t *ret;\n\tstruct prof_gctx_dump_iter_arg_s *arg =\n\t    (struct prof_gctx_dump_iter_arg_s *)opaque;\n\n\tmalloc_mutex_lock(arg->tsdn, gctx->lock);\n\n\tif (prof_dump_gctx(arg->tsdn, arg->propagate_err, gctx, &gctx->bt,\n\t    gctxs)) {\n\t\tret = gctx;\n\t\tgoto label_return;\n\t}\n\n\tret = NULL;\nlabel_return:\n\tmalloc_mutex_unlock(arg->tsdn, gctx->lock);\n\treturn ret;\n}\n\nstatic void\nprof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata,\n    struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,\n    struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,\n    prof_gctx_tree_t *gctxs) {\n\tsize_t tabind;\n\tunion {\n\t\tprof_gctx_t\t*p;\n\t\tvoid\t\t*v;\n\t} gctx;\n\n\tprof_enter(tsd, tdata);\n\n\t/*\n\t * Put gctx's in limbo and clear their counters in preparation for\n\t * summing.\n\t */\n\tgctx_tree_new(gctxs);\n\tfor (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) {\n\t\tprof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs);\n\t}\n\n\t/*\n\t * Iterate over tdatas, and for the non-expired ones snapshot their tctx\n\t * stats and merge them into the associated gctx's.\n\t */\n\tprof_tdata_merge_iter_arg->tsdn = tsd_tsdn(tsd);\n\tmemset(&prof_tdata_merge_iter_arg->cnt_all, 0, sizeof(prof_cnt_t));\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\ttdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter,\n\t    (void *)prof_tdata_merge_iter_arg);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n\n\t/* Merge tctx stats into gctx's. */\n\tprof_gctx_merge_iter_arg->tsdn = tsd_tsdn(tsd);\n\tprof_gctx_merge_iter_arg->leak_ngctx = 0;\n\tgctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter,\n\t    (void *)prof_gctx_merge_iter_arg);\n\n\tprof_leave(tsd, tdata);\n}\n\nstatic bool\nprof_dump_file(tsd_t *tsd, bool propagate_err, const char *filename,\n    bool leakcheck, prof_tdata_t *tdata,\n    struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,\n    struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,\n    struct prof_gctx_dump_iter_arg_s *prof_gctx_dump_iter_arg,\n    prof_gctx_tree_t *gctxs) {\n\t/* Create dump file. */\n\tif ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) {\n\t\treturn true;\n\t}\n\n\t/* Dump profile header. */\n\tif (prof_dump_header(tsd_tsdn(tsd), propagate_err,\n\t    &prof_tdata_merge_iter_arg->cnt_all)) {\n\t\tgoto label_write_error;\n\t}\n\n\t/* Dump per gctx profile stats. */\n\tprof_gctx_dump_iter_arg->tsdn = tsd_tsdn(tsd);\n\tprof_gctx_dump_iter_arg->propagate_err = propagate_err;\n\tif (gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter,\n\t    (void *)prof_gctx_dump_iter_arg) != NULL) {\n\t\tgoto label_write_error;\n\t}\n\n\t/* Dump /proc/<pid>/maps if possible. */\n\tif (prof_dump_maps(propagate_err)) {\n\t\tgoto label_write_error;\n\t}\n\n\tif (prof_dump_close(propagate_err)) {\n\t\treturn true;\n\t}\n\n\treturn false;\nlabel_write_error:\n\tprof_dump_close(propagate_err);\n\treturn true;\n}\n\nstatic bool\nprof_dump(tsd_t *tsd, bool propagate_err, const char *filename,\n    bool leakcheck) {\n\tcassert(config_prof);\n\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\n\tprof_tdata_t * tdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn true;\n\t}\n\n\tpre_reentrancy(tsd, NULL);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);\n\n\tprof_gctx_tree_t gctxs;\n\tstruct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;\n\tstruct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;\n\tstruct prof_gctx_dump_iter_arg_s prof_gctx_dump_iter_arg;\n\tprof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,\n\t    &prof_gctx_merge_iter_arg, &gctxs);\n\tbool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, tdata,\n\t    &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg,\n\t    &prof_gctx_dump_iter_arg, &gctxs);\n\tprof_gctx_finish(tsd, &gctxs);\n\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);\n\tpost_reentrancy(tsd);\n\n\tif (err) {\n\t\treturn true;\n\t}\n\n\tif (leakcheck) {\n\t\tprof_leakcheck(&prof_tdata_merge_iter_arg.cnt_all,\n\t\t    prof_gctx_merge_iter_arg.leak_ngctx, filename);\n\t}\n\treturn false;\n}\n\n#ifdef JEMALLOC_JET\nvoid\nprof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,\n    uint64_t *accumbytes) {\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\tstruct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;\n\tstruct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;\n\tprof_gctx_tree_t gctxs;\n\n\ttsd = tsd_fetch();\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\tif (curobjs != NULL) {\n\t\t\t*curobjs = 0;\n\t\t}\n\t\tif (curbytes != NULL) {\n\t\t\t*curbytes = 0;\n\t\t}\n\t\tif (accumobjs != NULL) {\n\t\t\t*accumobjs = 0;\n\t\t}\n\t\tif (accumbytes != NULL) {\n\t\t\t*accumbytes = 0;\n\t\t}\n\t\treturn;\n\t}\n\n\tprof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,\n\t    &prof_gctx_merge_iter_arg, &gctxs);\n\tprof_gctx_finish(tsd, &gctxs);\n\n\tif (curobjs != NULL) {\n\t\t*curobjs = prof_tdata_merge_iter_arg.cnt_all.curobjs;\n\t}\n\tif (curbytes != NULL) {\n\t\t*curbytes = prof_tdata_merge_iter_arg.cnt_all.curbytes;\n\t}\n\tif (accumobjs != NULL) {\n\t\t*accumobjs = prof_tdata_merge_iter_arg.cnt_all.accumobjs;\n\t}\n\tif (accumbytes != NULL) {\n\t\t*accumbytes = prof_tdata_merge_iter_arg.cnt_all.accumbytes;\n\t}\n}\n#endif\n\n#define DUMP_FILENAME_BUFSIZE\t(PATH_MAX + 1)\n#define VSEQ_INVALID\t\tUINT64_C(0xffffffffffffffff)\nstatic void\nprof_dump_filename(char *filename, char v, uint64_t vseq) {\n\tcassert(config_prof);\n\n\tif (vseq != VSEQ_INVALID) {\n\t        /* \"<prefix>.<pid>.<seq>.v<vseq>.heap\" */\n\t\tmalloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,\n\t\t    \"%s.%d.%\"FMTu64\".%c%\"FMTu64\".heap\",\n\t\t    opt_prof_prefix, prof_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.%\"FMTu64\".%c.heap\",\n\t\t    opt_prof_prefix, prof_getpid(), prof_dump_seq, v);\n\t}\n\tprof_dump_seq++;\n}\n\nstatic void\nprof_fdump(void) {\n\ttsd_t *tsd;\n\tchar filename[DUMP_FILENAME_BUFSIZE];\n\n\tcassert(config_prof);\n\tassert(opt_prof_final);\n\tassert(opt_prof_prefix[0] != '\\0');\n\n\tif (!prof_booted) {\n\t\treturn;\n\t}\n\ttsd = tsd_fetch();\n\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\tprof_dump_filename(filename, 'f', VSEQ_INVALID);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\tprof_dump(tsd, false, filename, opt_prof_leak);\n}\n\nbool\nprof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum) {\n\tcassert(config_prof);\n\n#ifndef JEMALLOC_ATOMIC_U64\n\tif (malloc_mutex_init(&prof_accum->mtx, \"prof_accum\",\n\t    WITNESS_RANK_PROF_ACCUM, malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\tprof_accum->accumbytes = 0;\n#else\n\tatomic_store_u64(&prof_accum->accumbytes, 0, ATOMIC_RELAXED);\n#endif\n\treturn false;\n}\n\nvoid\nprof_idump(tsdn_t *tsdn) {\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\tif (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {\n\t\treturn;\n\t}\n\ttsd = tsdn_tsd(tsdn);\n\tif (tsd_reentrancy_level_get(tsd) > 0) {\n\t\treturn;\n\t}\n\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn;\n\t}\n\tif (tdata->enq) {\n\t\ttdata->enq_idump = true;\n\t\treturn;\n\t}\n\n\tif (opt_prof_prefix[0] != '\\0') {\n\t\tchar filename[PATH_MAX + 1];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'i', prof_dump_iseq);\n\t\tprof_dump_iseq++;\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\t\tprof_dump(tsd, false, filename, false);\n\t}\n}\n\nbool\nprof_mdump(tsd_t *tsd, const char *filename) {\n\tcassert(config_prof);\n\tassert(tsd_reentrancy_level_get(tsd) == 0);\n\n\tif (!opt_prof || !prof_booted) {\n\t\treturn true;\n\t}\n\tchar filename_buf[DUMP_FILENAME_BUFSIZE];\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\t}\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &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(tsd_tsdn(tsd), &prof_dump_seq_mtx);\n\t\tfilename = filename_buf;\n\t}\n\treturn prof_dump(tsd, true, filename, false);\n}\n\nvoid\nprof_gdump(tsdn_t *tsdn) {\n\ttsd_t *tsd;\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\tif (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {\n\t\treturn;\n\t}\n\ttsd = tsdn_tsd(tsdn);\n\tif (tsd_reentrancy_level_get(tsd) > 0) {\n\t\treturn;\n\t}\n\n\ttdata = prof_tdata_get(tsd, false);\n\tif (tdata == NULL) {\n\t\treturn;\n\t}\n\tif (tdata->enq) {\n\t\ttdata->enq_gdump = true;\n\t\treturn;\n\t}\n\n\tif (opt_prof_prefix[0] != '\\0') {\n\t\tchar filename[DUMP_FILENAME_BUFSIZE];\n\t\tmalloc_mutex_lock(tsdn, &prof_dump_seq_mtx);\n\t\tprof_dump_filename(filename, 'u', prof_dump_useq);\n\t\tprof_dump_useq++;\n\t\tmalloc_mutex_unlock(tsdn, &prof_dump_seq_mtx);\n\t\tprof_dump(tsd, false, filename, false);\n\t}\n}\n\nstatic void\nprof_bt_hash(const void *key, size_t r_hash[2]) {\n\tprof_bt_t *bt = (prof_bt_t *)key;\n\n\tcassert(config_prof);\n\n\thash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);\n}\n\nstatic bool\nprof_bt_keycomp(const void *k1, const void *k2) {\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\t}\n\treturn (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);\n}\n\nstatic uint64_t\nprof_thr_uid_alloc(tsdn_t *tsdn) {\n\tuint64_t thr_uid;\n\n\tmalloc_mutex_lock(tsdn, &next_thr_uid_mtx);\n\tthr_uid = next_thr_uid;\n\tnext_thr_uid++;\n\tmalloc_mutex_unlock(tsdn, &next_thr_uid_mtx);\n\n\treturn thr_uid;\n}\n\nstatic prof_tdata_t *\nprof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,\n    char *thread_name, bool active) {\n\tprof_tdata_t *tdata;\n\n\tcassert(config_prof);\n\n\t/* Initialize an empty cache for this thread. */\n\ttdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t),\n\t    sz_size2index(sizeof(prof_tdata_t)), false, NULL, true,\n\t    arena_get(TSDN_NULL, 0, true), true);\n\tif (tdata == NULL) {\n\t\treturn NULL;\n\t}\n\n\ttdata->lock = prof_tdata_mutex_choose(thr_uid);\n\ttdata->thr_uid = thr_uid;\n\ttdata->thr_discrim = thr_discrim;\n\ttdata->thread_name = thread_name;\n\ttdata->attached = true;\n\ttdata->expired = false;\n\ttdata->tctx_uid_next = 0;\n\n\tif (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,\n\t    prof_bt_keycomp)) {\n\t\tidalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);\n\t\treturn NULL;\n\t}\n\n\ttdata->prng_state = (uint64_t)(uintptr_t)tdata;\n\tprof_sample_threshold_update(tdata);\n\n\ttdata->enq = false;\n\ttdata->enq_idump = false;\n\ttdata->enq_gdump = false;\n\n\ttdata->dumping = false;\n\ttdata->active = active;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\ttdata_tree_insert(&tdatas, tdata);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n\n\treturn tdata;\n}\n\nprof_tdata_t *\nprof_tdata_init(tsd_t *tsd) {\n\treturn prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0,\n\t    NULL, prof_thread_active_init_get(tsd_tsdn(tsd)));\n}\n\nstatic bool\nprof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) {\n\tif (tdata->attached && !even_if_attached) {\n\t\treturn false;\n\t}\n\tif (ckh_count(&tdata->bt2tctx) != 0) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool\nprof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,\n    bool even_if_attached) {\n\tmalloc_mutex_assert_owner(tsdn, tdata->lock);\n\n\treturn prof_tdata_should_destroy_unlocked(tdata, even_if_attached);\n}\n\nstatic void\nprof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,\n    bool even_if_attached) {\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx);\n\n\ttdata_tree_remove(&tdatas, tdata);\n\n\tassert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));\n\n\tif (tdata->thread_name != NULL) {\n\t\tidalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,\n\t\t    true);\n\t}\n\tckh_delete(tsd, &tdata->bt2tctx);\n\tidalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);\n}\n\nstatic void\nprof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\tprof_tdata_destroy_locked(tsd, tdata, even_if_attached);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n}\n\nstatic void\nprof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) {\n\tbool destroy_tdata;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);\n\tif (tdata->attached) {\n\t\tdestroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata,\n\t\t    true);\n\t\t/*\n\t\t * Only detach if !destroy_tdata, because detaching would allow\n\t\t * another thread to win the race to destroy tdata.\n\t\t */\n\t\tif (!destroy_tdata) {\n\t\t\ttdata->attached = false;\n\t\t}\n\t\ttsd_prof_tdata_set(tsd, NULL);\n\t} else {\n\t\tdestroy_tdata = false;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);\n\tif (destroy_tdata) {\n\t\tprof_tdata_destroy(tsd, tdata, true);\n\t}\n}\n\nprof_tdata_t *\nprof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {\n\tuint64_t thr_uid = tdata->thr_uid;\n\tuint64_t thr_discrim = tdata->thr_discrim + 1;\n\tchar *thread_name = (tdata->thread_name != NULL) ?\n\t    prof_thread_name_alloc(tsd_tsdn(tsd), tdata->thread_name) : NULL;\n\tbool active = tdata->active;\n\n\tprof_tdata_detach(tsd, tdata);\n\treturn prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name,\n\t    active);\n}\n\nstatic bool\nprof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) {\n\tbool destroy_tdata;\n\n\tmalloc_mutex_lock(tsdn, tdata->lock);\n\tif (!tdata->expired) {\n\t\ttdata->expired = true;\n\t\tdestroy_tdata = tdata->attached ? false :\n\t\t    prof_tdata_should_destroy(tsdn, tdata, false);\n\t} else {\n\t\tdestroy_tdata = false;\n\t}\n\tmalloc_mutex_unlock(tsdn, tdata->lock);\n\n\treturn destroy_tdata;\n}\n\nstatic prof_tdata_t *\nprof_tdata_reset_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,\n    void *arg) {\n\ttsdn_t *tsdn = (tsdn_t *)arg;\n\n\treturn (prof_tdata_expire(tsdn, tdata) ? tdata : NULL);\n}\n\nvoid\nprof_reset(tsd_t *tsd, size_t lg_sample) {\n\tprof_tdata_t *next;\n\n\tassert(lg_sample < (sizeof(uint64_t) << 3));\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);\n\n\tlg_prof_sample = lg_sample;\n\n\tnext = NULL;\n\tdo {\n\t\tprof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next,\n\t\t    prof_tdata_reset_iter, (void *)tsd);\n\t\tif (to_destroy != NULL) {\n\t\t\tnext = tdata_tree_next(&tdatas, to_destroy);\n\t\t\tprof_tdata_destroy_locked(tsd, to_destroy, false);\n\t\t} else {\n\t\t\tnext = NULL;\n\t\t}\n\t} while (next != NULL);\n\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);\n}\n\nvoid\nprof_tdata_cleanup(tsd_t *tsd) {\n\tprof_tdata_t *tdata;\n\n\tif (!config_prof) {\n\t\treturn;\n\t}\n\n\ttdata = tsd_prof_tdata_get(tsd);\n\tif (tdata != NULL) {\n\t\tprof_tdata_detach(tsd, tdata);\n\t}\n}\n\nbool\nprof_active_get(tsdn_t *tsdn) {\n\tbool prof_active_current;\n\n\tmalloc_mutex_lock(tsdn, &prof_active_mtx);\n\tprof_active_current = prof_active;\n\tmalloc_mutex_unlock(tsdn, &prof_active_mtx);\n\treturn prof_active_current;\n}\n\nbool\nprof_active_set(tsdn_t *tsdn, bool active) {\n\tbool prof_active_old;\n\n\tmalloc_mutex_lock(tsdn, &prof_active_mtx);\n\tprof_active_old = prof_active;\n\tprof_active = active;\n\tmalloc_mutex_unlock(tsdn, &prof_active_mtx);\n\treturn prof_active_old;\n}\n\nconst char *\nprof_thread_name_get(tsd_t *tsd) {\n\tprof_tdata_t *tdata;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn \"\";\n\t}\n\treturn (tdata->thread_name != NULL ? tdata->thread_name : \"\");\n}\n\nstatic char *\nprof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) {\n\tchar *ret;\n\tsize_t size;\n\n\tif (thread_name == NULL) {\n\t\treturn NULL;\n\t}\n\n\tsize = strlen(thread_name) + 1;\n\tif (size == 1) {\n\t\treturn \"\";\n\t}\n\n\tret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true,\n\t    arena_get(TSDN_NULL, 0, true), true);\n\tif (ret == NULL) {\n\t\treturn NULL;\n\t}\n\tmemcpy(ret, thread_name, size);\n\treturn ret;\n}\n\nint\nprof_thread_name_set(tsd_t *tsd, const char *thread_name) {\n\tprof_tdata_t *tdata;\n\tunsigned i;\n\tchar *s;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn EAGAIN;\n\t}\n\n\t/* Validate input. */\n\tif (thread_name == NULL) {\n\t\treturn EFAULT;\n\t}\n\tfor (i = 0; thread_name[i] != '\\0'; i++) {\n\t\tchar c = thread_name[i];\n\t\tif (!isgraph(c) && !isblank(c)) {\n\t\t\treturn EFAULT;\n\t\t}\n\t}\n\n\ts = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name);\n\tif (s == NULL) {\n\t\treturn EAGAIN;\n\t}\n\n\tif (tdata->thread_name != NULL) {\n\t\tidalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,\n\t\t    true);\n\t\ttdata->thread_name = NULL;\n\t}\n\tif (strlen(s) > 0) {\n\t\ttdata->thread_name = s;\n\t}\n\treturn 0;\n}\n\nbool\nprof_thread_active_get(tsd_t *tsd) {\n\tprof_tdata_t *tdata;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn false;\n\t}\n\treturn tdata->active;\n}\n\nbool\nprof_thread_active_set(tsd_t *tsd, bool active) {\n\tprof_tdata_t *tdata;\n\n\ttdata = prof_tdata_get(tsd, true);\n\tif (tdata == NULL) {\n\t\treturn true;\n\t}\n\ttdata->active = active;\n\treturn false;\n}\n\nbool\nprof_thread_active_init_get(tsdn_t *tsdn) {\n\tbool active_init;\n\n\tmalloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);\n\tactive_init = prof_thread_active_init;\n\tmalloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);\n\treturn active_init;\n}\n\nbool\nprof_thread_active_init_set(tsdn_t *tsdn, bool active_init) {\n\tbool active_init_old;\n\n\tmalloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);\n\tactive_init_old = prof_thread_active_init;\n\tprof_thread_active_init = active_init;\n\tmalloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);\n\treturn active_init_old;\n}\n\nbool\nprof_gdump_get(tsdn_t *tsdn) {\n\tbool prof_gdump_current;\n\n\tmalloc_mutex_lock(tsdn, &prof_gdump_mtx);\n\tprof_gdump_current = prof_gdump_val;\n\tmalloc_mutex_unlock(tsdn, &prof_gdump_mtx);\n\treturn prof_gdump_current;\n}\n\nbool\nprof_gdump_set(tsdn_t *tsdn, bool gdump) {\n\tbool prof_gdump_old;\n\n\tmalloc_mutex_lock(tsdn, &prof_gdump_mtx);\n\tprof_gdump_old = prof_gdump_val;\n\tprof_gdump_val = gdump;\n\tmalloc_mutex_unlock(tsdn, &prof_gdump_mtx);\n\treturn prof_gdump_old;\n}\n\nvoid\nprof_boot0(void) {\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\tcassert(config_prof);\n\n\t/*\n\t * opt_prof must be in its final state before any arenas are\n\t * initialized, so this function must be executed early.\n\t */\n\n\tif (opt_prof_leak && !opt_prof) {\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} 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}\n\t}\n}\n\nbool\nprof_boot2(tsd_t *tsd) {\n\tcassert(config_prof);\n\n\tif (opt_prof) {\n\t\tunsigned i;\n\n\t\tlg_prof_sample = opt_lg_prof_sample;\n\n\t\tprof_active = opt_prof_active;\n\t\tif (malloc_mutex_init(&prof_active_mtx, \"prof_active\",\n\t\t    WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tprof_gdump_val = opt_prof_gdump;\n\t\tif (malloc_mutex_init(&prof_gdump_mtx, \"prof_gdump\",\n\t\t    WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tprof_thread_active_init = opt_prof_thread_active_init;\n\t\tif (malloc_mutex_init(&prof_thread_active_init_mtx,\n\t\t    \"prof_thread_active_init\",\n\t\t    WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,\n\t\t    malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, prof_bt_hash,\n\t\t    prof_bt_keycomp)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (malloc_mutex_init(&bt2gctx_mtx, \"prof_bt2gctx\",\n\t\t    WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\ttdata_tree_new(&tdatas);\n\t\tif (malloc_mutex_init(&tdatas_mtx, \"prof_tdatas\",\n\t\t    WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tnext_thr_uid = 0;\n\t\tif (malloc_mutex_init(&next_thr_uid_mtx, \"prof_next_thr_uid\",\n\t\t    WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (malloc_mutex_init(&prof_dump_seq_mtx, \"prof_dump_seq\",\n\t\t    WITNESS_RANK_PROF_DUMP_SEQ, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (malloc_mutex_init(&prof_dump_mtx, \"prof_dump\",\n\t\t    WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (opt_prof_final && opt_prof_prefix[0] != '\\0' &&\n\t\t    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\t}\n\t\t}\n\n\t\tgctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),\n\t\t    b0get(), PROF_NCTX_LOCKS * sizeof(malloc_mutex_t),\n\t\t    CACHELINE);\n\t\tif (gctx_locks == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tif (malloc_mutex_init(&gctx_locks[i], \"prof_gctx\",\n\t\t\t    WITNESS_RANK_PROF_GCTX,\n\t\t\t    malloc_mutex_rank_exclusive)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\ttdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),\n\t\t    b0get(), PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t),\n\t\t    CACHELINE);\n\t\tif (tdata_locks == NULL) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tif (malloc_mutex_init(&tdata_locks[i], \"prof_tdata\",\n\t\t\t    WITNESS_RANK_PROF_TDATA,\n\t\t\t    malloc_mutex_rank_exclusive)) {\n\t\t\t\treturn true;\n\t\t\t}\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_prefork0(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_prefork(tsdn, &prof_dump_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &bt2gctx_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &tdatas_mtx);\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tmalloc_mutex_prefork(tsdn, &tdata_locks[i]);\n\t\t}\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tmalloc_mutex_prefork(tsdn, &gctx_locks[i]);\n\t\t}\n\t}\n}\n\nvoid\nprof_prefork1(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tmalloc_mutex_prefork(tsdn, &prof_active_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &prof_dump_seq_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &prof_gdump_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &next_thr_uid_mtx);\n\t\tmalloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx);\n\t}\n}\n\nvoid\nprof_postfork_parent(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_postfork_parent(tsdn,\n\t\t    &prof_thread_active_init_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_dump_seq_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_active_mtx);\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_parent(tsdn, &gctx_locks[i]);\n\t\t}\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_parent(tsdn, &tdata_locks[i]);\n\t\t}\n\t\tmalloc_mutex_postfork_parent(tsdn, &tdatas_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &bt2gctx_mtx);\n\t\tmalloc_mutex_postfork_parent(tsdn, &prof_dump_mtx);\n\t}\n}\n\nvoid\nprof_postfork_child(tsdn_t *tsdn) {\n\tif (config_prof && opt_prof) {\n\t\tunsigned i;\n\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_gdump_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_dump_seq_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_active_mtx);\n\t\tfor (i = 0; i < PROF_NCTX_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_child(tsdn, &gctx_locks[i]);\n\t\t}\n\t\tfor (i = 0; i < PROF_NTDATA_LOCKS; i++) {\n\t\t\tmalloc_mutex_postfork_child(tsdn, &tdata_locks[i]);\n\t\t}\n\t\tmalloc_mutex_postfork_child(tsdn, &tdatas_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &bt2gctx_mtx);\n\t\tmalloc_mutex_postfork_child(tsdn, &prof_dump_mtx);\n\t}\n}\n\n/******************************************************************************/\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/rtree.c",
    "content": "#define JEMALLOC_RTREE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/mutex.h\"\n\n/*\n * Only the most significant bits of keys passed to rtree_{read,write}() are\n * used.\n */\nbool\nrtree_new(rtree_t *rtree, bool zeroed) {\n#ifdef JEMALLOC_JET\n\tif (!zeroed) {\n\t\tmemset(rtree, 0, sizeof(rtree_t)); /* Clear root. */\n\t}\n#else\n\tassert(zeroed);\n#endif\n\n\tif (malloc_mutex_init(&rtree->init_lock, \"rtree\", WITNESS_RANK_RTREE,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic rtree_node_elm_t *\nrtree_node_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\treturn (rtree_node_elm_t *)base_alloc(tsdn, b0get(), nelms *\n\t    sizeof(rtree_node_elm_t), CACHELINE);\n}\nrtree_node_alloc_t *JET_MUTABLE rtree_node_alloc = rtree_node_alloc_impl;\n\nstatic void\nrtree_node_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *node) {\n\t/* Nodes are never deleted during normal operation. */\n\tnot_reached();\n}\nUNUSED rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc =\n    rtree_node_dalloc_impl;\n\nstatic rtree_leaf_elm_t *\nrtree_leaf_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\treturn (rtree_leaf_elm_t *)base_alloc(tsdn, b0get(), nelms *\n\t    sizeof(rtree_leaf_elm_t), CACHELINE);\n}\nrtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc = rtree_leaf_alloc_impl;\n\nstatic void\nrtree_leaf_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *leaf) {\n\t/* Leaves are never deleted during normal operation. */\n\tnot_reached();\n}\nUNUSED rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc =\n    rtree_leaf_dalloc_impl;\n\n#ifdef JEMALLOC_JET\n#  if RTREE_HEIGHT > 1\nstatic void\nrtree_delete_subtree(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *subtree,\n    unsigned level) {\n\tsize_t nchildren = ZU(1) << rtree_levels[level].bits;\n\tif (level + 2 < RTREE_HEIGHT) {\n\t\tfor (size_t i = 0; i < nchildren; i++) {\n\t\t\trtree_node_elm_t *node =\n\t\t\t    (rtree_node_elm_t *)atomic_load_p(&subtree[i].child,\n\t\t\t    ATOMIC_RELAXED);\n\t\t\tif (node != NULL) {\n\t\t\t\trtree_delete_subtree(tsdn, rtree, node, level +\n\t\t\t\t    1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (size_t i = 0; i < nchildren; i++) {\n\t\t\trtree_leaf_elm_t *leaf =\n\t\t\t    (rtree_leaf_elm_t *)atomic_load_p(&subtree[i].child,\n\t\t\t    ATOMIC_RELAXED);\n\t\t\tif (leaf != NULL) {\n\t\t\t\trtree_leaf_dalloc(tsdn, rtree, leaf);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (subtree != rtree->root) {\n\t\trtree_node_dalloc(tsdn, rtree, subtree);\n\t}\n}\n#  endif\n\nvoid\nrtree_delete(tsdn_t *tsdn, rtree_t *rtree) {\n#  if RTREE_HEIGHT > 1\n\trtree_delete_subtree(tsdn, rtree, rtree->root, 0);\n#  endif\n}\n#endif\n\nstatic rtree_node_elm_t *\nrtree_node_init(tsdn_t *tsdn, rtree_t *rtree, unsigned level,\n    atomic_p_t *elmp) {\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\t/*\n\t * If *elmp is non-null, then it was initialized with the init lock\n\t * held, so we can get by with 'relaxed' here.\n\t */\n\trtree_node_elm_t *node = atomic_load_p(elmp, ATOMIC_RELAXED);\n\tif (node == NULL) {\n\t\tnode = rtree_node_alloc(tsdn, rtree, ZU(1) <<\n\t\t    rtree_levels[level].bits);\n\t\tif (node == NULL) {\n\t\t\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\t\t\treturn NULL;\n\t\t}\n\t\t/*\n\t\t * Even though we hold the lock, a later reader might not; we\n\t\t * need release semantics.\n\t\t */\n\t\tatomic_store_p(elmp, node, ATOMIC_RELEASE);\n\t}\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\n\treturn node;\n}\n\nstatic rtree_leaf_elm_t *\nrtree_leaf_init(tsdn_t *tsdn, rtree_t *rtree, atomic_p_t *elmp) {\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\t/*\n\t * If *elmp is non-null, then it was initialized with the init lock\n\t * held, so we can get by with 'relaxed' here.\n\t */\n\trtree_leaf_elm_t *leaf = atomic_load_p(elmp, ATOMIC_RELAXED);\n\tif (leaf == NULL) {\n\t\tleaf = rtree_leaf_alloc(tsdn, rtree, ZU(1) <<\n\t\t    rtree_levels[RTREE_HEIGHT-1].bits);\n\t\tif (leaf == NULL) {\n\t\t\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\t\t\treturn NULL;\n\t\t}\n\t\t/*\n\t\t * Even though we hold the lock, a later reader might not; we\n\t\t * need release semantics.\n\t\t */\n\t\tatomic_store_p(elmp, leaf, ATOMIC_RELEASE);\n\t}\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\n\treturn leaf;\n}\n\nstatic bool\nrtree_node_valid(rtree_node_elm_t *node) {\n\treturn ((uintptr_t)node != (uintptr_t)0);\n}\n\nstatic bool\nrtree_leaf_valid(rtree_leaf_elm_t *leaf) {\n\treturn ((uintptr_t)leaf != (uintptr_t)0);\n}\n\nstatic rtree_node_elm_t *\nrtree_child_node_tryread(rtree_node_elm_t *elm, bool dependent) {\n\trtree_node_elm_t *node;\n\n\tif (dependent) {\n\t\tnode = (rtree_node_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_RELAXED);\n\t} else {\n\t\tnode = (rtree_node_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_ACQUIRE);\n\t}\n\n\tassert(!dependent || node != NULL);\n\treturn node;\n}\n\nstatic rtree_node_elm_t *\nrtree_child_node_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm,\n    unsigned level, bool dependent) {\n\trtree_node_elm_t *node;\n\n\tnode = rtree_child_node_tryread(elm, dependent);\n\tif (!dependent && unlikely(!rtree_node_valid(node))) {\n\t\tnode = rtree_node_init(tsdn, rtree, level + 1, &elm->child);\n\t}\n\tassert(!dependent || node != NULL);\n\treturn node;\n}\n\nstatic rtree_leaf_elm_t *\nrtree_child_leaf_tryread(rtree_node_elm_t *elm, bool dependent) {\n\trtree_leaf_elm_t *leaf;\n\n\tif (dependent) {\n\t\tleaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_RELAXED);\n\t} else {\n\t\tleaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child,\n\t\t    ATOMIC_ACQUIRE);\n\t}\n\n\tassert(!dependent || leaf != NULL);\n\treturn leaf;\n}\n\nstatic rtree_leaf_elm_t *\nrtree_child_leaf_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm,\n    unsigned level, bool dependent) {\n\trtree_leaf_elm_t *leaf;\n\n\tleaf = rtree_child_leaf_tryread(elm, dependent);\n\tif (!dependent && unlikely(!rtree_leaf_valid(leaf))) {\n\t\tleaf = rtree_leaf_init(tsdn, rtree, &elm->child);\n\t}\n\tassert(!dependent || leaf != NULL);\n\treturn leaf;\n}\n\nrtree_leaf_elm_t *\nrtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,\n    uintptr_t key, bool dependent, bool init_missing) {\n\trtree_node_elm_t *node;\n\trtree_leaf_elm_t *leaf;\n#if RTREE_HEIGHT > 1\n\tnode = rtree->root;\n#else\n\tleaf = rtree->root;\n#endif\n\n\tif (config_debug) {\n\t\tuintptr_t leafkey = rtree_leafkey(key);\n\t\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) {\n\t\t\tassert(rtree_ctx->cache[i].leafkey != leafkey);\n\t\t}\n\t\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) {\n\t\t\tassert(rtree_ctx->l2_cache[i].leafkey != leafkey);\n\t\t}\n\t}\n\n#define RTREE_GET_CHILD(level) {\t\t\t\t\t\\\n\t\tassert(level < RTREE_HEIGHT-1);\t\t\t\t\\\n\t\tif (level != 0 && !dependent &&\t\t\t\t\\\n\t\t    unlikely(!rtree_node_valid(node))) {\t\t\\\n\t\t\treturn NULL;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tuintptr_t subkey = rtree_subkey(key, level);\t\t\\\n\t\tif (level + 2 < RTREE_HEIGHT) {\t\t\t\t\\\n\t\t\tnode = init_missing ?\t\t\t\t\\\n\t\t\t    rtree_child_node_read(tsdn, rtree,\t\t\\\n\t\t\t    &node[subkey], level, dependent) :\t\t\\\n\t\t\t    rtree_child_node_tryread(&node[subkey],\t\\\n\t\t\t    dependent);\t\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tleaf = init_missing ?\t\t\t\t\\\n\t\t\t    rtree_child_leaf_read(tsdn, rtree,\t\t\\\n\t\t\t    &node[subkey], level, dependent) :\t\t\\\n\t\t\t    rtree_child_leaf_tryread(&node[subkey],\t\\\n\t\t\t    dependent);\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\n\t/*\n\t * Cache replacement upon hard lookup (i.e. L1 & L2 rtree cache miss):\n\t * (1) evict last entry in L2 cache; (2) move the collision slot from L1\n\t * cache down to L2; and 3) fill L1.\n\t */\n#define RTREE_GET_LEAF(level) {\t\t\t\t\t\t\\\n\t\tassert(level == RTREE_HEIGHT-1);\t\t\t\\\n\t\tif (!dependent && unlikely(!rtree_leaf_valid(leaf))) {\t\\\n\t\t\treturn NULL;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (RTREE_CTX_NCACHE_L2 > 1) {\t\t\t\t\\\n\t\t\tmemmove(&rtree_ctx->l2_cache[1],\t\t\\\n\t\t\t    &rtree_ctx->l2_cache[0],\t\t\t\\\n\t\t\t    sizeof(rtree_ctx_cache_elm_t) *\t\t\\\n\t\t\t    (RTREE_CTX_NCACHE_L2 - 1));\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tsize_t slot = rtree_cache_direct_map(key);\t\t\\\n\t\trtree_ctx->l2_cache[0].leafkey =\t\t\t\\\n\t\t    rtree_ctx->cache[slot].leafkey;\t\t\t\\\n\t\trtree_ctx->l2_cache[0].leaf =\t\t\t\t\\\n\t\t    rtree_ctx->cache[slot].leaf;\t\t\t\\\n\t\tuintptr_t leafkey = rtree_leafkey(key);\t\t\t\\\n\t\trtree_ctx->cache[slot].leafkey = leafkey;\t\t\\\n\t\trtree_ctx->cache[slot].leaf = leaf;\t\t\t\\\n\t\tuintptr_t subkey = rtree_subkey(key, level);\t\t\\\n\t\treturn &leaf[subkey];\t\t\t\t\t\\\n\t}\n\tif (RTREE_HEIGHT > 1) {\n\t\tRTREE_GET_CHILD(0)\n\t}\n\tif (RTREE_HEIGHT > 2) {\n\t\tRTREE_GET_CHILD(1)\n\t}\n\tif (RTREE_HEIGHT > 3) {\n\t\tfor (unsigned i = 2; i < RTREE_HEIGHT-1; i++) {\n\t\t\tRTREE_GET_CHILD(i)\n\t\t}\n\t}\n\tRTREE_GET_LEAF(RTREE_HEIGHT-1)\n#undef RTREE_GET_CHILD\n#undef RTREE_GET_LEAF\n\tnot_reached();\n}\n\nvoid\nrtree_ctx_data_init(rtree_ctx_t *ctx) {\n\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) {\n\t\trtree_ctx_cache_elm_t *cache = &ctx->cache[i];\n\t\tcache->leafkey = RTREE_LEAFKEY_INVALID;\n\t\tcache->leaf = NULL;\n\t}\n\tfor (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) {\n\t\trtree_ctx_cache_elm_t *cache = &ctx->l2_cache[i];\n\t\tcache->leafkey = RTREE_LEAFKEY_INVALID;\n\t\tcache->leaf = NULL;\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/stats.c",
    "content": "#define JEMALLOC_STATS_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/ctl.h\"\n#include \"jemalloc/internal/emitter.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/mutex_prof.h\"\n\nconst char *global_mutex_names[mutex_prof_num_global_mutexes] = {\n#define OP(mtx) #mtx,\n\tMUTEX_PROF_GLOBAL_MUTEXES\n#undef OP\n};\n\nconst char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {\n#define OP(mtx) #mtx,\n\tMUTEX_PROF_ARENA_MUTEXES\n#undef OP\n};\n\n#define CTL_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, (void *)v, &sz, NULL, 0);\t\t\t\t\\\n} while (0)\n\n#define CTL_M2_GET(n, i, v, t) do {\t\t\t\t\t\\\n\tsize_t mib[CTL_MAX_DEPTH];\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, (void *)v, &sz, NULL, 0);\t\t\\\n} while (0)\n\n#define CTL_M2_M4_GET(n, i, j, v, t) do {\t\t\t\t\\\n\tsize_t mib[CTL_MAX_DEPTH];\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, (void *)v, &sz, NULL, 0);\t\t\\\n} while (0)\n\n/******************************************************************************/\n/* Data. */\n\nbool opt_stats_print = false;\nchar opt_stats_print_opts[stats_print_tot_num_options+1] = \"\";\n\n/******************************************************************************/\n\n/* Calculate x.yyy and output a string (takes a fixed sized char array). */\nstatic bool\nget_rate_str(uint64_t dividend, uint64_t divisor, char str[6]) {\n\tif (divisor == 0 || dividend > divisor) {\n\t\t/* The rate is not supposed to be greater than 1. */\n\t\treturn true;\n\t}\n\tif (dividend > 0) {\n\t\tassert(UINT64_MAX / dividend >= 1000);\n\t}\n\n\tunsigned n = (unsigned)((dividend * 1000) / divisor);\n\tif (n < 10) {\n\t\tmalloc_snprintf(str, 6, \"0.00%u\", n);\n\t} else if (n < 100) {\n\t\tmalloc_snprintf(str, 6, \"0.0%u\", n);\n\t} else if (n < 1000) {\n\t\tmalloc_snprintf(str, 6, \"0.%u\", n);\n\t} else {\n\t\tmalloc_snprintf(str, 6, \"1\");\n\t}\n\n\treturn false;\n}\n\n#define MUTEX_CTL_STR_MAX_LENGTH 128\nstatic void\ngen_mutex_ctl_str(char *str, size_t buf_len, const char *prefix,\n    const char *mutex, const char *counter) {\n\tmalloc_snprintf(str, buf_len, \"stats.%s.%s.%s\", prefix, mutex, counter);\n}\n\nstatic void\nmutex_stats_init_cols(emitter_row_t *row, const char *table_name,\n    emitter_col_t *name,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tmutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;\n\tmutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;\n\n\temitter_col_t *col;\n\n\tif (name != NULL) {\n\t\temitter_col_init(name, row);\n\t\tname->justify = emitter_justify_left;\n\t\tname->width = 21;\n\t\tname->type = emitter_type_title;\n\t\tname->str_val = table_name;\n\t}\n\n#define WIDTH_uint32_t 12\n#define WIDTH_uint64_t 16\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tcol = &col_##counter_type[k_##counter_type];\t\t\t\\\n\t++k_##counter_type;\t\t\t\t\t\t\\\n\temitter_col_init(col, row);\t\t\t\t\t\\\n\tcol->justify = emitter_justify_right;\t\t\t\t\\\n\tcol->width = WIDTH_##counter_type;\t\t\t\t\\\n\tcol->type = emitter_type_title;\t\t\t\t\t\\\n\tcol->str_val = human;\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef WIDTH_uint32_t\n#undef WIDTH_uint64_t\n}\n\nstatic void\nmutex_stats_read_global(const char *name, emitter_col_t *col_name,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tchar cmd[MUTEX_CTL_STR_MAX_LENGTH];\n\n\tcol_name->str_val = name;\n\n\temitter_col_t *dst;\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tdst = &col_##counter_type[mutex_counter_##counter];\t\t\\\n\tdst->type = EMITTER_TYPE_##counter_type;\t\t\t\\\n\tgen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,\t\t\\\n\t    \"mutexes\", name, #counter);\t\t\t\t\t\\\n\tCTL_GET(cmd, (counter_type *)&dst->bool_val, counter_type);\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\nstatic void\nmutex_stats_read_arena(unsigned arena_ind, mutex_prof_arena_ind_t mutex_ind,\n    const char *name, emitter_col_t *col_name,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tchar cmd[MUTEX_CTL_STR_MAX_LENGTH];\n\n\tcol_name->str_val = name;\n\n\temitter_col_t *dst;\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tdst = &col_##counter_type[mutex_counter_##counter];\t\t\\\n\tdst->type = EMITTER_TYPE_##counter_type;\t\t\t\\\n\tgen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,\t\t\\\n\t    \"arenas.0.mutexes\",\tarena_mutex_names[mutex_ind], #counter);\\\n\tCTL_M2_GET(cmd, arena_ind,\t\t\t\t\t\\\n\t    (counter_type *)&dst->bool_val, counter_type);\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\nstatic void\nmutex_stats_read_arena_bin(unsigned arena_ind, unsigned bin_ind,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tchar cmd[MUTEX_CTL_STR_MAX_LENGTH];\n\temitter_col_t *dst;\n\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, counter_type, human)\t\t\t\t\\\n\tdst = &col_##counter_type[mutex_counter_##counter];\t\t\\\n\tdst->type = EMITTER_TYPE_##counter_type;\t\t\t\\\n\tgen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,\t\t\\\n\t    \"arenas.0.bins.0\",\"mutex\", #counter);\t\t\t\\\n\tCTL_M2_M4_GET(cmd, arena_ind, bin_ind,\t\t\t\t\\\n\t    (counter_type *)&dst->bool_val, counter_type);\n\tMUTEX_PROF_COUNTERS\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\n/* \"row\" can be NULL to avoid emitting in table mode. */\nstatic void\nmutex_stats_emit(emitter_t *emitter, emitter_row_t *row,\n    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],\n    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {\n\tif (row != NULL) {\n\t\temitter_table_row(emitter, row);\n\t}\n\n\tmutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;\n\tmutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;\n\n\temitter_col_t *col;\n\n#define EMITTER_TYPE_uint32_t emitter_type_uint32\n#define EMITTER_TYPE_uint64_t emitter_type_uint64\n#define OP(counter, type, human)\t\t\t\t\t\\\n\tcol = &col_##type[k_##type];\t\t\t\t\t\t\\\n\t++k_##type;\t\t\t\t\t\t\t\\\n\temitter_json_kv(emitter, #counter, EMITTER_TYPE_##type,\t\t\\\n\t    (const void *)&col->bool_val);\n\tMUTEX_PROF_COUNTERS;\n#undef OP\n#undef EMITTER_TYPE_uint32_t\n#undef EMITTER_TYPE_uint64_t\n}\n\nstatic void\nstats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {\n\tsize_t page;\n\tbool in_gap, in_gap_prev;\n\tunsigned nbins, j;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\n\temitter_row_t header_row;\n\temitter_row_init(&header_row);\n\n\temitter_row_t row;\n\temitter_row_init(&row);\n#define COL(name, left_or_right, col_width, etype)\t\t\t\\\n\temitter_col_t col_##name;\t\t\t\t\t\\\n\temitter_col_init(&col_##name, &row);\t\t\t\t\\\n\tcol_##name.justify = emitter_justify_##left_or_right;\t\t\\\n\tcol_##name.width = col_width;\t\t\t\t\t\\\n\tcol_##name.type = emitter_type_##etype;\t\t\t\t\\\n\temitter_col_t header_col_##name;\t\t\t\t\\\n\temitter_col_init(&header_col_##name, &header_row);\t\t\\\n\theader_col_##name.justify = emitter_justify_##left_or_right;\t\\\n\theader_col_##name.width = col_width;\t\t\t\t\\\n\theader_col_##name.type = emitter_type_title;\t\t\t\\\n\theader_col_##name.str_val = #name;\n\n\tCOL(size, right, 20, size)\n\tCOL(ind, right, 4, unsigned)\n\tCOL(allocated, right, 13, uint64)\n\tCOL(nmalloc, right, 13, uint64)\n\tCOL(ndalloc, right, 13, uint64)\n\tCOL(nrequests, right, 13, uint64)\n\tCOL(curregs, right, 13, size)\n\tCOL(curslabs, right, 13, size)\n\tCOL(regs, right, 5, unsigned)\n\tCOL(pgs, right, 4, size)\n\t/* To buffer a right- and left-justified column. */\n\tCOL(justify_spacer, right, 1, title)\n\tCOL(util, right, 6, title)\n\tCOL(nfills, right, 13, uint64)\n\tCOL(nflushes, right, 13, uint64)\n\tCOL(nslabs, right, 13, uint64)\n\tCOL(nreslabs, right, 13, uint64)\n#undef COL\n\n\t/* Don't want to actually print the name. */\n\theader_col_justify_spacer.str_val = \" \";\n\tcol_justify_spacer.str_val = \" \";\n\n\n\temitter_col_t col_mutex64[mutex_prof_num_uint64_t_counters];\n\temitter_col_t col_mutex32[mutex_prof_num_uint32_t_counters];\n\n\temitter_col_t header_mutex64[mutex_prof_num_uint64_t_counters];\n\temitter_col_t header_mutex32[mutex_prof_num_uint32_t_counters];\n\n\tif (mutex) {\n\t\tmutex_stats_init_cols(&row, NULL, NULL, col_mutex64,\n\t\t    col_mutex32);\n\t\tmutex_stats_init_cols(&header_row, NULL, NULL, header_mutex64,\n\t\t    header_mutex32);\n\t}\n\n\t/*\n\t * We print a \"bins:\" header as part of the table row; we need to adjust\n\t * the header size column to compensate.\n\t */\n\theader_col_size.width -=5;\n\temitter_table_printf(emitter, \"bins:\");\n\temitter_table_row(emitter, &header_row);\n\temitter_json_arr_begin(emitter, \"bins\");\n\n\tfor (j = 0, in_gap = false; j < nbins; j++) {\n\t\tuint64_t nslabs;\n\t\tsize_t reg_size, slab_size, curregs;\n\t\tsize_t curslabs;\n\t\tuint32_t nregs;\n\t\tuint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;\n\t\tuint64_t nreslabs;\n\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nslabs\", i, j, &nslabs,\n\t\t    uint64_t);\n\t\tin_gap_prev = in_gap;\n\t\tin_gap = (nslabs == 0);\n\n\t\tif (in_gap_prev && !in_gap) {\n\t\t\temitter_table_printf(emitter,\n\t\t\t    \"                     ---\\n\");\n\t\t}\n\n\t\tCTL_M2_GET(\"arenas.bin.0.size\", j, &reg_size, size_t);\n\t\tCTL_M2_GET(\"arenas.bin.0.nregs\", j, &nregs, uint32_t);\n\t\tCTL_M2_GET(\"arenas.bin.0.slab_size\", j, &slab_size, size_t);\n\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nmalloc\", i, j, &nmalloc,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.ndalloc\", i, j, &ndalloc,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.curregs\", i, j, &curregs,\n\t\t    size_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nrequests\", i, j,\n\t\t    &nrequests, uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nfills\", i, j, &nfills,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nflushes\", i, j, &nflushes,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.nreslabs\", i, j, &nreslabs,\n\t\t    uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.bins.0.curslabs\", i, j, &curslabs,\n\t\t    size_t);\n\n\t\tif (mutex) {\n\t\t\tmutex_stats_read_arena_bin(i, j, col_mutex64,\n\t\t\t    col_mutex32);\n\t\t}\n\n\t\temitter_json_arr_obj_begin(emitter);\n\t\temitter_json_kv(emitter, \"nmalloc\", emitter_type_uint64,\n\t\t    &nmalloc);\n\t\temitter_json_kv(emitter, \"ndalloc\", emitter_type_uint64,\n\t\t    &ndalloc);\n\t\temitter_json_kv(emitter, \"curregs\", emitter_type_size,\n\t\t    &curregs);\n\t\temitter_json_kv(emitter, \"nrequests\", emitter_type_uint64,\n\t\t    &nrequests);\n\t\temitter_json_kv(emitter, \"nfills\", emitter_type_uint64,\n\t\t    &nfills);\n\t\temitter_json_kv(emitter, \"nflushes\", emitter_type_uint64,\n\t\t    &nflushes);\n\t\temitter_json_kv(emitter, \"nreslabs\", emitter_type_uint64,\n\t\t    &nreslabs);\n\t\temitter_json_kv(emitter, \"curslabs\", emitter_type_size,\n\t\t    &curslabs);\n\t\tif (mutex) {\n\t\t\temitter_json_dict_begin(emitter, \"mutex\");\n\t\t\tmutex_stats_emit(emitter, NULL, col_mutex64,\n\t\t\t    col_mutex32);\n\t\t\temitter_json_dict_end(emitter);\n\t\t}\n\t\temitter_json_arr_obj_end(emitter);\n\n\t\tsize_t availregs = nregs * curslabs;\n\t\tchar util[6];\n\t\tif (get_rate_str((uint64_t)curregs, (uint64_t)availregs, util))\n\t\t{\n\t\t\tif (availregs == 0) {\n\t\t\t\tmalloc_snprintf(util, sizeof(util), \"1\");\n\t\t\t} else if (curregs > availregs) {\n\t\t\t\t/*\n\t\t\t\t * Race detected: the counters were read in\n\t\t\t\t * separate mallctl calls and concurrent\n\t\t\t\t * operations happened in between.  In this case\n\t\t\t\t * no meaningful utilization can be computed.\n\t\t\t\t */\n\t\t\t\tmalloc_snprintf(util, sizeof(util), \" race\");\n\t\t\t} else {\n\t\t\t\tnot_reached();\n\t\t\t}\n\t\t}\n\n\t\tcol_size.size_val = reg_size;\n\t\tcol_ind.unsigned_val = j;\n\t\tcol_allocated.size_val = curregs * reg_size;\n\t\tcol_nmalloc.uint64_val = nmalloc;\n\t\tcol_ndalloc.uint64_val = ndalloc;\n\t\tcol_nrequests.uint64_val = nrequests;\n\t\tcol_curregs.size_val = curregs;\n\t\tcol_curslabs.size_val = curslabs;\n\t\tcol_regs.unsigned_val = nregs;\n\t\tcol_pgs.size_val = slab_size / page;\n\t\tcol_util.str_val = util;\n\t\tcol_nfills.uint64_val = nfills;\n\t\tcol_nflushes.uint64_val = nflushes;\n\t\tcol_nslabs.uint64_val = nslabs;\n\t\tcol_nreslabs.uint64_val = nreslabs;\n\n\t\t/*\n\t\t * Note that mutex columns were initialized above, if mutex ==\n\t\t * true.\n\t\t */\n\n\t\temitter_table_row(emitter, &row);\n\t}\n\temitter_json_arr_end(emitter); /* Close \"bins\". */\n\n\tif (in_gap) {\n\t\temitter_table_printf(emitter, \"                     ---\\n\");\n\t}\n}\n\nstatic void\nstats_arena_lextents_print(emitter_t *emitter, unsigned i) {\n\tunsigned nbins, nlextents, j;\n\tbool in_gap, in_gap_prev;\n\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\tCTL_GET(\"arenas.nlextents\", &nlextents, unsigned);\n\n\temitter_row_t header_row;\n\temitter_row_init(&header_row);\n\temitter_row_t row;\n\temitter_row_init(&row);\n\n#define COL(name, left_or_right, col_width, etype)\t\t\t\\\n\temitter_col_t header_##name;\t\t\t\t\t\\\n\temitter_col_init(&header_##name, &header_row);\t\t\t\\\n\theader_##name.justify = emitter_justify_##left_or_right;\t\\\n\theader_##name.width = col_width;\t\t\t\t\\\n\theader_##name.type = emitter_type_title;\t\t\t\\\n\theader_##name.str_val = #name;\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\temitter_col_t col_##name;\t\t\t\t\t\\\n\temitter_col_init(&col_##name, &row);\t\t\t\t\\\n\tcol_##name.justify = emitter_justify_##left_or_right;\t\t\\\n\tcol_##name.width = col_width;\t\t\t\t\t\\\n\tcol_##name.type = emitter_type_##etype;\n\n\tCOL(size, right, 20, size)\n\tCOL(ind, right, 4, unsigned)\n\tCOL(allocated, right, 13, size)\n\tCOL(nmalloc, right, 13, uint64)\n\tCOL(ndalloc, right, 13, uint64)\n\tCOL(nrequests, right, 13, uint64)\n\tCOL(curlextents, right, 13, size)\n#undef COL\n\n\t/* As with bins, we label the large extents table. */\n\theader_size.width -= 6;\n\temitter_table_printf(emitter, \"large:\");\n\temitter_table_row(emitter, &header_row);\n\temitter_json_arr_begin(emitter, \"lextents\");\n\n\tfor (j = 0, in_gap = false; j < nlextents; j++) {\n\t\tuint64_t nmalloc, ndalloc, nrequests;\n\t\tsize_t lextent_size, curlextents;\n\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.nmalloc\", i, j,\n\t\t    &nmalloc, uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.ndalloc\", i, j,\n\t\t    &ndalloc, uint64_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.nrequests\", i, j,\n\t\t    &nrequests, uint64_t);\n\t\tin_gap_prev = in_gap;\n\t\tin_gap = (nrequests == 0);\n\n\t\tif (in_gap_prev && !in_gap) {\n\t\t\temitter_table_printf(emitter,\n\t\t\t    \"                     ---\\n\");\n\t\t}\n\n\t\tCTL_M2_GET(\"arenas.lextent.0.size\", j, &lextent_size, size_t);\n\t\tCTL_M2_M4_GET(\"stats.arenas.0.lextents.0.curlextents\", i, j,\n\t\t    &curlextents, size_t);\n\n\t\temitter_json_arr_obj_begin(emitter);\n\t\temitter_json_kv(emitter, \"curlextents\", emitter_type_size,\n\t\t    &curlextents);\n\t\temitter_json_arr_obj_end(emitter);\n\n\t\tcol_size.size_val = lextent_size;\n\t\tcol_ind.unsigned_val = nbins + j;\n\t\tcol_allocated.size_val = curlextents * lextent_size;\n\t\tcol_nmalloc.uint64_val = nmalloc;\n\t\tcol_ndalloc.uint64_val = ndalloc;\n\t\tcol_nrequests.uint64_val = nrequests;\n\t\tcol_curlextents.size_val = curlextents;\n\n\t\tif (!in_gap) {\n\t\t\temitter_table_row(emitter, &row);\n\t\t}\n\t}\n\temitter_json_arr_end(emitter); /* Close \"lextents\". */\n\tif (in_gap) {\n\t\temitter_table_printf(emitter, \"                     ---\\n\");\n\t}\n}\n\nstatic void\nstats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind) {\n\temitter_row_t row;\n\temitter_col_t col_name;\n\temitter_col_t col64[mutex_prof_num_uint64_t_counters];\n\temitter_col_t col32[mutex_prof_num_uint32_t_counters];\n\n\temitter_row_init(&row);\n\tmutex_stats_init_cols(&row, \"\", &col_name, col64, col32);\n\n\temitter_json_dict_begin(emitter, \"mutexes\");\n\temitter_table_row(emitter, &row);\n\n\tfor (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes;\n\t    i++) {\n\t\tconst char *name = arena_mutex_names[i];\n\t\temitter_json_dict_begin(emitter, name);\n\t\tmutex_stats_read_arena(arena_ind, i, name, &col_name, col64,\n\t\t    col32);\n\t\tmutex_stats_emit(emitter, &row, col64, col32);\n\t\temitter_json_dict_end(emitter); /* Close the mutex dict. */\n\t}\n\temitter_json_dict_end(emitter); /* End \"mutexes\". */\n}\n\nstatic void\nstats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,\n    bool mutex) {\n\tunsigned nthreads;\n\tconst char *dss;\n\tssize_t dirty_decay_ms, muzzy_decay_ms;\n\tsize_t page, pactive, pdirty, pmuzzy, mapped, retained;\n\tsize_t base, internal, resident, metadata_thp;\n\tuint64_t dirty_npurge, dirty_nmadvise, dirty_purged;\n\tuint64_t muzzy_npurge, muzzy_nmadvise, muzzy_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\tsize_t tcache_bytes;\n\tuint64_t uptime;\n\n\tCTL_GET(\"arenas.page\", &page, size_t);\n\n\tCTL_M2_GET(\"stats.arenas.0.nthreads\", i, &nthreads, unsigned);\n\temitter_kv(emitter, \"nthreads\", \"assigned threads\",\n\t    emitter_type_unsigned, &nthreads);\n\n\tCTL_M2_GET(\"stats.arenas.0.uptime\", i, &uptime, uint64_t);\n\temitter_kv(emitter, \"uptime_ns\", \"uptime\", emitter_type_uint64,\n\t    &uptime);\n\n\tCTL_M2_GET(\"stats.arenas.0.dss\", i, &dss, const char *);\n\temitter_kv(emitter, \"dss\", \"dss allocation precedence\",\n\t    emitter_type_string, &dss);\n\n\tCTL_M2_GET(\"stats.arenas.0.dirty_decay_ms\", i, &dirty_decay_ms,\n\t    ssize_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_decay_ms\", i, &muzzy_decay_ms,\n\t    ssize_t);\n\tCTL_M2_GET(\"stats.arenas.0.pactive\", i, &pactive, size_t);\n\tCTL_M2_GET(\"stats.arenas.0.pdirty\", i, &pdirty, size_t);\n\tCTL_M2_GET(\"stats.arenas.0.pmuzzy\", i, &pmuzzy, size_t);\n\tCTL_M2_GET(\"stats.arenas.0.dirty_npurge\", i, &dirty_npurge, uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.dirty_nmadvise\", i, &dirty_nmadvise,\n\t    uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.dirty_purged\", i, &dirty_purged, uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_npurge\", i, &muzzy_npurge, uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_nmadvise\", i, &muzzy_nmadvise,\n\t    uint64_t);\n\tCTL_M2_GET(\"stats.arenas.0.muzzy_purged\", i, &muzzy_purged, uint64_t);\n\n\temitter_row_t decay_row;\n\temitter_row_init(&decay_row);\n\n\t/* JSON-style emission. */\n\temitter_json_kv(emitter, \"dirty_decay_ms\", emitter_type_ssize,\n\t    &dirty_decay_ms);\n\temitter_json_kv(emitter, \"muzzy_decay_ms\", emitter_type_ssize,\n\t    &muzzy_decay_ms);\n\n\temitter_json_kv(emitter, \"pactive\", emitter_type_size, &pactive);\n\temitter_json_kv(emitter, \"pdirty\", emitter_type_size, &pdirty);\n\temitter_json_kv(emitter, \"pmuzzy\", emitter_type_size, &pmuzzy);\n\n\temitter_json_kv(emitter, \"dirty_npurge\", emitter_type_uint64,\n\t    &dirty_npurge);\n\temitter_json_kv(emitter, \"dirty_nmadvise\", emitter_type_uint64,\n\t    &dirty_nmadvise);\n\temitter_json_kv(emitter, \"dirty_purged\", emitter_type_uint64,\n\t    &dirty_purged);\n\n\temitter_json_kv(emitter, \"muzzy_npurge\", emitter_type_uint64,\n\t    &muzzy_npurge);\n\temitter_json_kv(emitter, \"muzzy_nmadvise\", emitter_type_uint64,\n\t    &muzzy_nmadvise);\n\temitter_json_kv(emitter, \"muzzy_purged\", emitter_type_uint64,\n\t    &muzzy_purged);\n\n\t/* Table-style emission. */\n\temitter_col_t decay_type;\n\temitter_col_init(&decay_type, &decay_row);\n\tdecay_type.justify = emitter_justify_right;\n\tdecay_type.width = 9;\n\tdecay_type.type = emitter_type_title;\n\tdecay_type.str_val = \"decaying:\";\n\n\temitter_col_t decay_time;\n\temitter_col_init(&decay_time, &decay_row);\n\tdecay_time.justify = emitter_justify_right;\n\tdecay_time.width = 6;\n\tdecay_time.type = emitter_type_title;\n\tdecay_time.str_val = \"time\";\n\n\temitter_col_t decay_npages;\n\temitter_col_init(&decay_npages, &decay_row);\n\tdecay_npages.justify = emitter_justify_right;\n\tdecay_npages.width = 13;\n\tdecay_npages.type = emitter_type_title;\n\tdecay_npages.str_val = \"npages\";\n\n\temitter_col_t decay_sweeps;\n\temitter_col_init(&decay_sweeps, &decay_row);\n\tdecay_sweeps.justify = emitter_justify_right;\n\tdecay_sweeps.width = 13;\n\tdecay_sweeps.type = emitter_type_title;\n\tdecay_sweeps.str_val = \"sweeps\";\n\n\temitter_col_t decay_madvises;\n\temitter_col_init(&decay_madvises, &decay_row);\n\tdecay_madvises.justify = emitter_justify_right;\n\tdecay_madvises.width = 13;\n\tdecay_madvises.type = emitter_type_title;\n\tdecay_madvises.str_val = \"madvises\";\n\n\temitter_col_t decay_purged;\n\temitter_col_init(&decay_purged, &decay_row);\n\tdecay_purged.justify = emitter_justify_right;\n\tdecay_purged.width = 13;\n\tdecay_purged.type = emitter_type_title;\n\tdecay_purged.str_val = \"purged\";\n\n\t/* Title row. */\n\temitter_table_row(emitter, &decay_row);\n\n\t/* Dirty row. */\n\tdecay_type.str_val = \"dirty:\";\n\n\tif (dirty_decay_ms >= 0) {\n\t\tdecay_time.type = emitter_type_ssize;\n\t\tdecay_time.ssize_val = dirty_decay_ms;\n\t} else {\n\t\tdecay_time.type = emitter_type_title;\n\t\tdecay_time.str_val = \"N/A\";\n\t}\n\n\tdecay_npages.type = emitter_type_size;\n\tdecay_npages.size_val = pdirty;\n\n\tdecay_sweeps.type = emitter_type_uint64;\n\tdecay_sweeps.uint64_val = dirty_npurge;\n\n\tdecay_madvises.type = emitter_type_uint64;\n\tdecay_madvises.uint64_val = dirty_nmadvise;\n\n\tdecay_purged.type = emitter_type_uint64;\n\tdecay_purged.uint64_val = dirty_purged;\n\n\temitter_table_row(emitter, &decay_row);\n\n\t/* Muzzy row. */\n\tdecay_type.str_val = \"muzzy:\";\n\n\tif (muzzy_decay_ms >= 0) {\n\t\tdecay_time.type = emitter_type_ssize;\n\t\tdecay_time.ssize_val = muzzy_decay_ms;\n\t} else {\n\t\tdecay_time.type = emitter_type_title;\n\t\tdecay_time.str_val = \"N/A\";\n\t}\n\n\tdecay_npages.type = emitter_type_size;\n\tdecay_npages.size_val = pmuzzy;\n\n\tdecay_sweeps.type = emitter_type_uint64;\n\tdecay_sweeps.uint64_val = muzzy_npurge;\n\n\tdecay_madvises.type = emitter_type_uint64;\n\tdecay_madvises.uint64_val = muzzy_nmadvise;\n\n\tdecay_purged.type = emitter_type_uint64;\n\tdecay_purged.uint64_val = muzzy_purged;\n\n\temitter_table_row(emitter, &decay_row);\n\n\t/* Small / large / total allocation counts. */\n\temitter_row_t alloc_count_row;\n\temitter_row_init(&alloc_count_row);\n\n\temitter_col_t alloc_count_title;\n\temitter_col_init(&alloc_count_title, &alloc_count_row);\n\talloc_count_title.justify = emitter_justify_left;\n\talloc_count_title.width = 25;\n\talloc_count_title.type = emitter_type_title;\n\talloc_count_title.str_val = \"\";\n\n\temitter_col_t alloc_count_allocated;\n\temitter_col_init(&alloc_count_allocated, &alloc_count_row);\n\talloc_count_allocated.justify = emitter_justify_right;\n\talloc_count_allocated.width = 12;\n\talloc_count_allocated.type = emitter_type_title;\n\talloc_count_allocated.str_val = \"allocated\";\n\n\temitter_col_t alloc_count_nmalloc;\n\temitter_col_init(&alloc_count_nmalloc, &alloc_count_row);\n\talloc_count_nmalloc.justify = emitter_justify_right;\n\talloc_count_nmalloc.width = 12;\n\talloc_count_nmalloc.type = emitter_type_title;\n\talloc_count_nmalloc.str_val = \"nmalloc\";\n\n\temitter_col_t alloc_count_ndalloc;\n\temitter_col_init(&alloc_count_ndalloc, &alloc_count_row);\n\talloc_count_ndalloc.justify = emitter_justify_right;\n\talloc_count_ndalloc.width = 12;\n\talloc_count_ndalloc.type = emitter_type_title;\n\talloc_count_ndalloc.str_val = \"ndalloc\";\n\n\temitter_col_t alloc_count_nrequests;\n\temitter_col_init(&alloc_count_nrequests, &alloc_count_row);\n\talloc_count_nrequests.justify = emitter_justify_right;\n\talloc_count_nrequests.width = 12;\n\talloc_count_nrequests.type = emitter_type_title;\n\talloc_count_nrequests.str_val = \"nrequests\";\n\n\temitter_table_row(emitter, &alloc_count_row);\n\n#define GET_AND_EMIT_ALLOC_STAT(small_or_large, name, valtype)\t\t\\\n\tCTL_M2_GET(\"stats.arenas.0.\" #small_or_large \".\" #name, i,\t\\\n\t    &small_or_large##_##name, valtype##_t);\t\t\t\\\n\temitter_json_kv(emitter, #name, emitter_type_##valtype,\t\t\\\n\t    &small_or_large##_##name);\t\t\t\t\t\\\n\talloc_count_##name.type = emitter_type_##valtype;\t\t\\\n\talloc_count_##name.valtype##_val = small_or_large##_##name;\n\n\temitter_json_dict_begin(emitter, \"small\");\n\talloc_count_title.str_val = \"small:\";\n\n\tGET_AND_EMIT_ALLOC_STAT(small, allocated, size)\n\tGET_AND_EMIT_ALLOC_STAT(small, nmalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(small, ndalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64)\n\n\temitter_table_row(emitter, &alloc_count_row);\n\temitter_json_dict_end(emitter); /* Close \"small\". */\n\n\temitter_json_dict_begin(emitter, \"large\");\n\talloc_count_title.str_val = \"large:\";\n\n\tGET_AND_EMIT_ALLOC_STAT(large, allocated, size)\n\tGET_AND_EMIT_ALLOC_STAT(large, nmalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(large, ndalloc, uint64)\n\tGET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64)\n\n\temitter_table_row(emitter, &alloc_count_row);\n\temitter_json_dict_end(emitter); /* Close \"large\". */\n\n#undef GET_AND_EMIT_ALLOC_STAT\n\n\t/* Aggregated small + large stats are emitter only in table mode. */\n\talloc_count_title.str_val = \"total:\";\n\talloc_count_allocated.size_val = small_allocated + large_allocated;\n\talloc_count_nmalloc.uint64_val = small_nmalloc + large_nmalloc;\n\talloc_count_ndalloc.uint64_val = small_ndalloc + large_ndalloc;\n\talloc_count_nrequests.uint64_val = small_nrequests + large_nrequests;\n\temitter_table_row(emitter, &alloc_count_row);\n\n\temitter_row_t mem_count_row;\n\temitter_row_init(&mem_count_row);\n\n\temitter_col_t mem_count_title;\n\temitter_col_init(&mem_count_title, &mem_count_row);\n\tmem_count_title.justify = emitter_justify_left;\n\tmem_count_title.width = 25;\n\tmem_count_title.type = emitter_type_title;\n\tmem_count_title.str_val = \"\";\n\n\temitter_col_t mem_count_val;\n\temitter_col_init(&mem_count_val, &mem_count_row);\n\tmem_count_val.justify = emitter_justify_right;\n\tmem_count_val.width = 12;\n\tmem_count_val.type = emitter_type_title;\n\tmem_count_val.str_val = \"\";\n\n\temitter_table_row(emitter, &mem_count_row);\n\tmem_count_val.type = emitter_type_size;\n\n\t/* Active count in bytes is emitted only in table mode. */\n\tmem_count_title.str_val = \"active:\";\n\tmem_count_val.size_val = pactive * page;\n\temitter_table_row(emitter, &mem_count_row);\n\n#define GET_AND_EMIT_MEM_STAT(stat)\t\t\t\t\t\\\n\tCTL_M2_GET(\"stats.arenas.0.\"#stat, i, &stat, size_t);\t\t\\\n\temitter_json_kv(emitter, #stat, emitter_type_size, &stat);\t\\\n\tmem_count_title.str_val = #stat\":\";\t\t\t\t\\\n\tmem_count_val.size_val = stat;\t\t\t\t\t\\\n\temitter_table_row(emitter, &mem_count_row);\n\n\tGET_AND_EMIT_MEM_STAT(mapped)\n\tGET_AND_EMIT_MEM_STAT(retained)\n\tGET_AND_EMIT_MEM_STAT(base)\n\tGET_AND_EMIT_MEM_STAT(internal)\n\tGET_AND_EMIT_MEM_STAT(metadata_thp)\n\tGET_AND_EMIT_MEM_STAT(tcache_bytes)\n\tGET_AND_EMIT_MEM_STAT(resident)\n#undef GET_AND_EMIT_MEM_STAT\n\n\tif (mutex) {\n\t\tstats_arena_mutexes_print(emitter, i);\n\t}\n\tif (bins) {\n\t\tstats_arena_bins_print(emitter, mutex, i);\n\t}\n\tif (large) {\n\t\tstats_arena_lextents_print(emitter, i);\n\t}\n}\n\nstatic void\nstats_general_print(emitter_t *emitter) {\n\tconst char *cpv;\n\tbool bv, bv2;\n\tunsigned uv;\n\tuint32_t u32v;\n\tuint64_t u64v;\n\tssize_t ssv, ssv2;\n\tsize_t sv, bsz, usz, ssz, sssz, cpsz;\n\n\tbsz = sizeof(bool);\n\tusz = sizeof(unsigned);\n\tssz = sizeof(size_t);\n\tsssz = sizeof(ssize_t);\n\tcpsz = sizeof(const char *);\n\n\tCTL_GET(\"version\", &cpv, const char *);\n\temitter_kv(emitter, \"version\", \"Version\", emitter_type_string, &cpv);\n\n\t/* config. */\n\temitter_dict_begin(emitter, \"config\", \"Build-time option settings\");\n#define CONFIG_WRITE_BOOL(name)\t\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tCTL_GET(\"config.\"#name, &bv, bool);\t\t\t\\\n\t\temitter_kv(emitter, #name, \"config.\"#name,\t\t\\\n\t\t    emitter_type_bool, &bv);\t\t\t\t\\\n\t} while (0)\n\n\tCONFIG_WRITE_BOOL(cache_oblivious);\n\tCONFIG_WRITE_BOOL(debug);\n\tCONFIG_WRITE_BOOL(fill);\n\tCONFIG_WRITE_BOOL(lazy_lock);\n\temitter_kv(emitter, \"malloc_conf\", \"config.malloc_conf\",\n\t    emitter_type_string, &config_malloc_conf);\n\n\tCONFIG_WRITE_BOOL(prof);\n\tCONFIG_WRITE_BOOL(prof_libgcc);\n\tCONFIG_WRITE_BOOL(prof_libunwind);\n\tCONFIG_WRITE_BOOL(stats);\n\tCONFIG_WRITE_BOOL(utrace);\n\tCONFIG_WRITE_BOOL(xmalloc);\n#undef CONFIG_WRITE_BOOL\n\temitter_dict_end(emitter); /* Close \"config\" dict. */\n\n\t/* opt. */\n#define OPT_WRITE(name, var, size, emitter_type)\t\t\t\\\n\tif (je_mallctl(\"opt.\"name, (void *)&var, &size, NULL, 0) ==\t\\\n\t    0) {\t\t\t\t\t\t\t\\\n\t\temitter_kv(emitter, name, \"opt.\"name, emitter_type,\t\\\n\t\t    &var);\t\t\t\t\t\t\\\n\t}\n\n#define OPT_WRITE_MUTABLE(name, var1, var2, size, emitter_type,\t\t\\\n    altname)\t\t\t\t\t\t\t\t\\\n\tif (je_mallctl(\"opt.\"name, (void *)&var1, &size, NULL, 0) ==\t\\\n\t    0 && je_mallctl(altname, (void *)&var2, &size, NULL, 0)\t\\\n\t    == 0) {\t\t\t\t\t\t\t\\\n\t\temitter_kv_note(emitter, name, \"opt.\"name,\t\t\\\n\t\t    emitter_type, &var1, altname, emitter_type,\t\t\\\n\t\t    &var2);\t\t\t\t\t\t\\\n\t}\n\n#define OPT_WRITE_BOOL(name) OPT_WRITE(name, bv, bsz, emitter_type_bool)\n#define OPT_WRITE_BOOL_MUTABLE(name, altname)\t\t\t\t\\\n\tOPT_WRITE_MUTABLE(name, bv, bv2, bsz, emitter_type_bool, altname)\n\n#define OPT_WRITE_UNSIGNED(name)\t\t\t\t\t\\\n\tOPT_WRITE(name, uv, usz, emitter_type_unsigned)\n\n#define OPT_WRITE_SSIZE_T(name)\t\t\t\t\t\t\\\n\tOPT_WRITE(name, ssv, sssz, emitter_type_ssize)\n#define OPT_WRITE_SSIZE_T_MUTABLE(name, altname)\t\t\t\\\n\tOPT_WRITE_MUTABLE(name, ssv, ssv2, sssz, emitter_type_ssize,\t\\\n\t    altname)\n\n#define OPT_WRITE_CHAR_P(name)\t\t\t\t\t\t\\\n\tOPT_WRITE(name, cpv, cpsz, emitter_type_string)\n\n\temitter_dict_begin(emitter, \"opt\", \"Run-time option settings\");\n\n\tOPT_WRITE_BOOL(\"abort\")\n\tOPT_WRITE_BOOL(\"abort_conf\")\n\tOPT_WRITE_BOOL(\"retain\")\n\tOPT_WRITE_CHAR_P(\"dss\")\n\tOPT_WRITE_UNSIGNED(\"narenas\")\n\tOPT_WRITE_CHAR_P(\"percpu_arena\")\n\tOPT_WRITE_CHAR_P(\"metadata_thp\")\n\tOPT_WRITE_BOOL_MUTABLE(\"background_thread\", \"background_thread\")\n\tOPT_WRITE_SSIZE_T_MUTABLE(\"dirty_decay_ms\", \"arenas.dirty_decay_ms\")\n\tOPT_WRITE_SSIZE_T_MUTABLE(\"muzzy_decay_ms\", \"arenas.muzzy_decay_ms\")\n\tOPT_WRITE_UNSIGNED(\"lg_extent_max_active_fit\")\n\tOPT_WRITE_CHAR_P(\"junk\")\n\tOPT_WRITE_BOOL(\"zero\")\n\tOPT_WRITE_BOOL(\"utrace\")\n\tOPT_WRITE_BOOL(\"xmalloc\")\n\tOPT_WRITE_BOOL(\"tcache\")\n\tOPT_WRITE_SSIZE_T(\"lg_tcache_max\")\n\tOPT_WRITE_CHAR_P(\"thp\")\n\tOPT_WRITE_BOOL(\"prof\")\n\tOPT_WRITE_CHAR_P(\"prof_prefix\")\n\tOPT_WRITE_BOOL_MUTABLE(\"prof_active\", \"prof.active\")\n\tOPT_WRITE_BOOL_MUTABLE(\"prof_thread_active_init\",\n\t    \"prof.thread_active_init\")\n\tOPT_WRITE_SSIZE_T_MUTABLE(\"lg_prof_sample\", \"prof.lg_sample\")\n\tOPT_WRITE_BOOL(\"prof_accum\")\n\tOPT_WRITE_SSIZE_T(\"lg_prof_interval\")\n\tOPT_WRITE_BOOL(\"prof_gdump\")\n\tOPT_WRITE_BOOL(\"prof_final\")\n\tOPT_WRITE_BOOL(\"prof_leak\")\n\tOPT_WRITE_BOOL(\"stats_print\")\n\tOPT_WRITE_CHAR_P(\"stats_print_opts\")\n\n\temitter_dict_end(emitter);\n\n#undef OPT_WRITE\n#undef OPT_WRITE_MUTABLE\n#undef OPT_WRITE_BOOL\n#undef OPT_WRITE_BOOL_MUTABLE\n#undef OPT_WRITE_UNSIGNED\n#undef OPT_WRITE_SSIZE_T\n#undef OPT_WRITE_SSIZE_T_MUTABLE\n#undef OPT_WRITE_CHAR_P\n\n\t/* prof. */\n\tif (config_prof) {\n\t\temitter_dict_begin(emitter, \"prof\", \"Profiling settings\");\n\n\t\tCTL_GET(\"prof.thread_active_init\", &bv, bool);\n\t\temitter_kv(emitter, \"thread_active_init\",\n\t\t    \"prof.thread_active_init\", emitter_type_bool, &bv);\n\n\t\tCTL_GET(\"prof.active\", &bv, bool);\n\t\temitter_kv(emitter, \"active\", \"prof.active\", emitter_type_bool,\n\t\t    &bv);\n\n\t\tCTL_GET(\"prof.gdump\", &bv, bool);\n\t\temitter_kv(emitter, \"gdump\", \"prof.gdump\", emitter_type_bool,\n\t\t    &bv);\n\n\t\tCTL_GET(\"prof.interval\", &u64v, uint64_t);\n\t\temitter_kv(emitter, \"interval\", \"prof.interval\",\n\t\t    emitter_type_uint64, &u64v);\n\n\t\tCTL_GET(\"prof.lg_sample\", &ssv, ssize_t);\n\t\temitter_kv(emitter, \"lg_sample\", \"prof.lg_sample\",\n\t\t    emitter_type_ssize, &ssv);\n\n\t\temitter_dict_end(emitter); /* Close \"prof\". */\n\t}\n\n\t/* arenas. */\n\t/*\n\t * The json output sticks arena info into an \"arenas\" dict; the table\n\t * output puts them at the top-level.\n\t */\n\temitter_json_dict_begin(emitter, \"arenas\");\n\n\tCTL_GET(\"arenas.narenas\", &uv, unsigned);\n\temitter_kv(emitter, \"narenas\", \"Arenas\", emitter_type_unsigned, &uv);\n\n\t/*\n\t * Decay settings are emitted only in json mode; in table mode, they're\n\t * emitted as notes with the opt output, above.\n\t */\n\tCTL_GET(\"arenas.dirty_decay_ms\", &ssv, ssize_t);\n\temitter_json_kv(emitter, \"dirty_decay_ms\", emitter_type_ssize, &ssv);\n\n\tCTL_GET(\"arenas.muzzy_decay_ms\", &ssv, ssize_t);\n\temitter_json_kv(emitter, \"muzzy_decay_ms\", emitter_type_ssize, &ssv);\n\n\tCTL_GET(\"arenas.quantum\", &sv, size_t);\n\temitter_kv(emitter, \"quantum\", \"Quantum size\", emitter_type_size, &sv);\n\n\tCTL_GET(\"arenas.page\", &sv, size_t);\n\temitter_kv(emitter, \"page\", \"Page size\", emitter_type_size, &sv);\n\n\tif (je_mallctl(\"arenas.tcache_max\", (void *)&sv, &ssz, NULL, 0) == 0) {\n\t\temitter_kv(emitter, \"tcache_max\",\n\t\t    \"Maximum thread-cached size class\", emitter_type_size, &sv);\n\t}\n\n\tunsigned nbins;\n\tCTL_GET(\"arenas.nbins\", &nbins, unsigned);\n\temitter_kv(emitter, \"nbins\", \"Number of bin size classes\",\n\t    emitter_type_unsigned, &nbins);\n\n\tunsigned nhbins;\n\tCTL_GET(\"arenas.nhbins\", &nhbins, unsigned);\n\temitter_kv(emitter, \"nhbins\", \"Number of thread-cache bin size classes\",\n\t    emitter_type_unsigned, &nhbins);\n\n\t/*\n\t * We do enough mallctls in a loop that we actually want to omit them\n\t * (not just omit the printing).\n\t */\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_arr_begin(emitter, \"bin\");\n\t\tfor (unsigned i = 0; i < nbins; i++) {\n\t\t\temitter_json_arr_obj_begin(emitter);\n\n\t\t\tCTL_M2_GET(\"arenas.bin.0.size\", i, &sv, size_t);\n\t\t\temitter_json_kv(emitter, \"size\", emitter_type_size,\n\t\t\t    &sv);\n\n\t\t\tCTL_M2_GET(\"arenas.bin.0.nregs\", i, &u32v, uint32_t);\n\t\t\temitter_json_kv(emitter, \"nregs\", emitter_type_uint32,\n\t\t\t    &u32v);\n\n\t\t\tCTL_M2_GET(\"arenas.bin.0.slab_size\", i, &sv, size_t);\n\t\t\temitter_json_kv(emitter, \"slab_size\", emitter_type_size,\n\t\t\t    &sv);\n\n\t\t\temitter_json_arr_obj_end(emitter);\n\t\t}\n\t\temitter_json_arr_end(emitter); /* Close \"bin\". */\n\t}\n\n\tunsigned nlextents;\n\tCTL_GET(\"arenas.nlextents\", &nlextents, unsigned);\n\temitter_kv(emitter, \"nlextents\", \"Number of large size classes\",\n\t    emitter_type_unsigned, &nlextents);\n\n\tif (emitter->output == emitter_output_json) {\n\t\temitter_json_arr_begin(emitter, \"lextent\");\n\t\tfor (unsigned i = 0; i < nlextents; i++) {\n\t\t\temitter_json_arr_obj_begin(emitter);\n\n\t\t\tCTL_M2_GET(\"arenas.lextent.0.size\", i, &sv, size_t);\n\t\t\temitter_json_kv(emitter, \"size\", emitter_type_size,\n\t\t\t    &sv);\n\n\t\t\temitter_json_arr_obj_end(emitter);\n\t\t}\n\t\temitter_json_arr_end(emitter); /* Close \"lextent\". */\n\t}\n\n\temitter_json_dict_end(emitter); /* Close \"arenas\" */\n}\n\nstatic void\nstats_print_helper(emitter_t *emitter, bool merged, bool destroyed,\n    bool unmerged, bool bins, bool large, bool mutex) {\n\t/*\n\t * These should be deleted.  We keep them around for a while, to aid in\n\t * the transition to the emitter code.\n\t */\n\tsize_t allocated, active, metadata, metadata_thp, resident, mapped,\n\t    retained;\n\tsize_t num_background_threads;\n\tuint64_t background_thread_num_runs, background_thread_run_interval;\n\n\tCTL_GET(\"stats.allocated\", &allocated, size_t);\n\tCTL_GET(\"stats.active\", &active, size_t);\n\tCTL_GET(\"stats.metadata\", &metadata, size_t);\n\tCTL_GET(\"stats.metadata_thp\", &metadata_thp, size_t);\n\tCTL_GET(\"stats.resident\", &resident, size_t);\n\tCTL_GET(\"stats.mapped\", &mapped, size_t);\n\tCTL_GET(\"stats.retained\", &retained, size_t);\n\n\tif (have_background_thread) {\n\t\tCTL_GET(\"stats.background_thread.num_threads\",\n\t\t    &num_background_threads, size_t);\n\t\tCTL_GET(\"stats.background_thread.num_runs\",\n\t\t    &background_thread_num_runs, uint64_t);\n\t\tCTL_GET(\"stats.background_thread.run_interval\",\n\t\t    &background_thread_run_interval, uint64_t);\n\t} else {\n\t\tnum_background_threads = 0;\n\t\tbackground_thread_num_runs = 0;\n\t\tbackground_thread_run_interval = 0;\n\t}\n\n\t/* Generic global stats. */\n\temitter_json_dict_begin(emitter, \"stats\");\n\temitter_json_kv(emitter, \"allocated\", emitter_type_size, &allocated);\n\temitter_json_kv(emitter, \"active\", emitter_type_size, &active);\n\temitter_json_kv(emitter, \"metadata\", emitter_type_size, &metadata);\n\temitter_json_kv(emitter, \"metadata_thp\", emitter_type_size,\n\t    &metadata_thp);\n\temitter_json_kv(emitter, \"resident\", emitter_type_size, &resident);\n\temitter_json_kv(emitter, \"mapped\", emitter_type_size, &mapped);\n\temitter_json_kv(emitter, \"retained\", emitter_type_size, &retained);\n\n\temitter_table_printf(emitter, \"Allocated: %zu, active: %zu, \"\n\t    \"metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, \"\n\t    \"retained: %zu\\n\", allocated, active, metadata, metadata_thp,\n\t    resident, mapped, retained);\n\n\t/* Background thread stats. */\n\temitter_json_dict_begin(emitter, \"background_thread\");\n\temitter_json_kv(emitter, \"num_threads\", emitter_type_size,\n\t    &num_background_threads);\n\temitter_json_kv(emitter, \"num_runs\", emitter_type_uint64,\n\t    &background_thread_num_runs);\n\temitter_json_kv(emitter, \"run_interval\", emitter_type_uint64,\n\t    &background_thread_run_interval);\n\temitter_json_dict_end(emitter); /* Close \"background_thread\". */\n\n\temitter_table_printf(emitter, \"Background threads: %zu, \"\n\t    \"num_runs: %\"FMTu64\", run_interval: %\"FMTu64\" ns\\n\",\n\t    num_background_threads, background_thread_num_runs,\n\t    background_thread_run_interval);\n\n\tif (mutex) {\n\t\temitter_row_t row;\n\t\temitter_col_t name;\n\t\temitter_col_t col64[mutex_prof_num_uint64_t_counters];\n\t\temitter_col_t col32[mutex_prof_num_uint32_t_counters];\n\n\t\temitter_row_init(&row);\n\t\tmutex_stats_init_cols(&row, \"\", &name, col64, col32);\n\n\t\temitter_table_row(emitter, &row);\n\t\temitter_json_dict_begin(emitter, \"mutexes\");\n\n\t\tfor (int i = 0; i < mutex_prof_num_global_mutexes; i++) {\n\t\t\tmutex_stats_read_global(global_mutex_names[i], &name,\n\t\t\t    col64, col32);\n\t\t\temitter_json_dict_begin(emitter, global_mutex_names[i]);\n\t\t\tmutex_stats_emit(emitter, &row, col64, col32);\n\t\t\temitter_json_dict_end(emitter);\n\t\t}\n\n\t\temitter_json_dict_end(emitter); /* Close \"mutexes\". */\n\t}\n\n\temitter_json_dict_end(emitter); /* Close \"stats\". */\n\n\tif (merged || destroyed || unmerged) {\n\t\tunsigned narenas;\n\n\t\temitter_json_dict_begin(emitter, \"stats.arenas\");\n\n\t\tCTL_GET(\"arenas.narenas\", &narenas, unsigned);\n\t\tsize_t mib[3];\n\t\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\t\tsize_t sz;\n\t\tVARIABLE_ARRAY(bool, initialized, narenas);\n\t\tbool destroyed_initialized;\n\t\tunsigned i, j, ninitialized;\n\n\t\txmallctlnametomib(\"arena.0.initialized\", mib, &miblen);\n\t\tfor (i = ninitialized = 0; i < narenas; i++) {\n\t\t\tmib[1] = i;\n\t\t\tsz = sizeof(bool);\n\t\t\txmallctlbymib(mib, miblen, &initialized[i], &sz,\n\t\t\t    NULL, 0);\n\t\t\tif (initialized[i]) {\n\t\t\t\tninitialized++;\n\t\t\t}\n\t\t}\n\t\tmib[1] = MALLCTL_ARENAS_DESTROYED;\n\t\tsz = sizeof(bool);\n\t\txmallctlbymib(mib, miblen, &destroyed_initialized, &sz,\n\t\t    NULL, 0);\n\n\t\t/* Merged stats. */\n\t\tif (merged && (ninitialized > 1 || !unmerged)) {\n\t\t\t/* Print merged arena stats. */\n\t\t\temitter_table_printf(emitter, \"Merged arenas stats:\\n\");\n\t\t\temitter_json_dict_begin(emitter, \"merged\");\n\t\t\tstats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins,\n\t\t\t    large, mutex);\n\t\t\temitter_json_dict_end(emitter); /* Close \"merged\". */\n\t\t}\n\n\t\t/* Destroyed stats. */\n\t\tif (destroyed_initialized && destroyed) {\n\t\t\t/* Print destroyed arena stats. */\n\t\t\temitter_table_printf(emitter,\n\t\t\t    \"Destroyed arenas stats:\\n\");\n\t\t\temitter_json_dict_begin(emitter, \"destroyed\");\n\t\t\tstats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED,\n\t\t\t    bins, large, mutex);\n\t\t\temitter_json_dict_end(emitter); /* Close \"destroyed\". */\n\t\t}\n\n\t\t/* Unmerged stats. */\n\t\tif (unmerged) {\n\t\t\tfor (i = j = 0; i < narenas; i++) {\n\t\t\t\tif (initialized[i]) {\n\t\t\t\t\tchar arena_ind_str[20];\n\t\t\t\t\tmalloc_snprintf(arena_ind_str,\n\t\t\t\t\t    sizeof(arena_ind_str), \"%u\", i);\n\t\t\t\t\temitter_json_dict_begin(emitter,\n\t\t\t\t\t    arena_ind_str);\n\t\t\t\t\temitter_table_printf(emitter,\n\t\t\t\t\t    \"arenas[%s]:\\n\", arena_ind_str);\n\t\t\t\t\tstats_arena_print(emitter, i, bins,\n\t\t\t\t\t    large, mutex);\n\t\t\t\t\t/* Close \"<arena-ind>\". */\n\t\t\t\t\temitter_json_dict_end(emitter);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\temitter_json_dict_end(emitter); /* Close \"stats.arenas\". */\n\t}\n}\n\nvoid\nstats_print(void (*write_cb)(void *, const char *), void *cbopaque,\n    const char *opts) {\n\tint err;\n\tuint64_t epoch;\n\tsize_t u64sz;\n#define OPTION(o, v, d, s) bool v = d;\n\tSTATS_PRINT_OPTIONS\n#undef OPTION\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\", (void *)&epoch, &u64sz, (void *)&epoch,\n\t    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\tfor (unsigned i = 0; opts[i] != '\\0'; i++) {\n\t\t\tswitch (opts[i]) {\n#define OPTION(o, v, d, s) case o: v = s; break;\n\t\t\t\tSTATS_PRINT_OPTIONS\n#undef OPTION\n\t\t\tdefault:;\n\t\t\t}\n\t\t}\n\t}\n\n\temitter_t emitter;\n\temitter_init(&emitter,\n\t    json ? emitter_output_json : emitter_output_table, write_cb,\n\t    cbopaque);\n\temitter_begin(&emitter);\n\temitter_table_printf(&emitter, \"___ Begin jemalloc statistics ___\\n\");\n\temitter_json_dict_begin(&emitter, \"jemalloc\");\n\n\tif (general) {\n\t\tstats_general_print(&emitter);\n\t}\n\tif (config_stats) {\n\t\tstats_print_helper(&emitter, merged, destroyed, unmerged,\n\t\t    bins, large, mutex);\n\t}\n\n\temitter_json_dict_end(&emitter); /* Closes the \"jemalloc\" dict. */\n\temitter_table_printf(&emitter, \"--- End jemalloc statistics ---\\n\");\n\temitter_end(&emitter);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/sz.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/sz.h\"\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst size_t sz_pind2sz_tab[NPSIZES+1] = {\n#define PSZ_yes(lg_grp, ndelta, lg_delta)\t\t\t\t\\\n\t(((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))),\n#define PSZ_no(lg_grp, ndelta, lg_delta)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \\\n\tPSZ_##psz(lg_grp, ndelta, lg_delta)\n\tSIZE_CLASSES\n#undef PSZ_yes\n#undef PSZ_no\n#undef SC\n\t(LARGE_MAXCLASS + PAGE)\n};\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst size_t sz_index2size_tab[NSIZES] = {\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \\\n\t((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta)),\n\tSIZE_CLASSES\n#undef SC\n};\n\nJEMALLOC_ALIGNED(CACHELINE)\nconst uint8_t sz_size2index_tab[] = {\n#if LG_TINY_MIN == 0\n/* The div module doesn't support division by 1. */\n#error \"Unsupported LG_TINY_MIN\"\n#define S2B_0(i)\ti,\n#elif LG_TINY_MIN == 1\n#warning \"Dangerous LG_TINY_MIN\"\n#define S2B_1(i)\ti,\n#elif LG_TINY_MIN == 2\n#warning \"Dangerous LG_TINY_MIN\"\n#define S2B_2(i)\ti,\n#elif LG_TINY_MIN == 3\n#define S2B_3(i)\ti,\n#elif LG_TINY_MIN == 4\n#define S2B_4(i)\ti,\n#elif LG_TINY_MIN == 5\n#define S2B_5(i)\ti,\n#elif LG_TINY_MIN == 6\n#define S2B_6(i)\ti,\n#elif LG_TINY_MIN == 7\n#define S2B_7(i)\ti,\n#elif LG_TINY_MIN == 8\n#define S2B_8(i)\ti,\n#elif LG_TINY_MIN == 9\n#define S2B_9(i)\ti,\n#elif LG_TINY_MIN == 10\n#define S2B_10(i)\ti,\n#elif LG_TINY_MIN == 11\n#define S2B_11(i)\ti,\n#else\n#error \"Unsupported LG_TINY_MIN\"\n#endif\n#if LG_TINY_MIN < 1\n#define S2B_1(i)\tS2B_0(i) S2B_0(i)\n#endif\n#if LG_TINY_MIN < 2\n#define S2B_2(i)\tS2B_1(i) S2B_1(i)\n#endif\n#if LG_TINY_MIN < 3\n#define S2B_3(i)\tS2B_2(i) S2B_2(i)\n#endif\n#if LG_TINY_MIN < 4\n#define S2B_4(i)\tS2B_3(i) S2B_3(i)\n#endif\n#if LG_TINY_MIN < 5\n#define S2B_5(i)\tS2B_4(i) S2B_4(i)\n#endif\n#if LG_TINY_MIN < 6\n#define S2B_6(i)\tS2B_5(i) S2B_5(i)\n#endif\n#if LG_TINY_MIN < 7\n#define S2B_7(i)\tS2B_6(i) S2B_6(i)\n#endif\n#if LG_TINY_MIN < 8\n#define S2B_8(i)\tS2B_7(i) S2B_7(i)\n#endif\n#if LG_TINY_MIN < 9\n#define S2B_9(i)\tS2B_8(i) S2B_8(i)\n#endif\n#if LG_TINY_MIN < 10\n#define S2B_10(i)\tS2B_9(i) S2B_9(i)\n#endif\n#if LG_TINY_MIN < 11\n#define S2B_11(i)\tS2B_10(i) S2B_10(i)\n#endif\n#define S2B_no(i)\n#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \\\n\tS2B_##lg_delta_lookup(index)\n\tSIZE_CLASSES\n#undef S2B_3\n#undef S2B_4\n#undef S2B_5\n#undef S2B_6\n#undef S2B_7\n#undef S2B_8\n#undef S2B_9\n#undef S2B_10\n#undef S2B_11\n#undef S2B_no\n#undef SC\n};\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/tcache.c",
    "content": "#define JEMALLOC_TCACHE_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/size_classes.h\"\n\n/******************************************************************************/\n/* Data. */\n\nbool\topt_tcache = true;\nssize_t\topt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT;\n\ncache_bin_info_t\t*tcache_bin_info;\nstatic unsigned\t\tstack_nelms; /* Total stack elms per tcache. */\n\nunsigned\t\tnhbins;\nsize_t\t\t\ttcache_maxclass;\n\ntcaches_t\t\t*tcaches;\n\n/* Index of first element within tcaches that has never been used. */\nstatic unsigned\t\ttcaches_past;\n\n/* Head of singly linked list tracking available tcaches elements. */\nstatic tcaches_t\t*tcaches_avail;\n\n/* Protects tcaches{,_past,_avail}. */\nstatic malloc_mutex_t\ttcaches_mtx;\n\n/******************************************************************************/\n\nsize_t\ntcache_salloc(tsdn_t *tsdn, const void *ptr) {\n\treturn arena_salloc(tsdn, ptr);\n}\n\nvoid\ntcache_event_hard(tsd_t *tsd, tcache_t *tcache) {\n\tszind_t binind = tcache->next_gc_bin;\n\n\tcache_bin_t *tbin;\n\tif (binind < NBINS) {\n\t\ttbin = tcache_small_bin_get(tcache, binind);\n\t} else {\n\t\ttbin = tcache_large_bin_get(tcache, binind);\n\t}\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(tsd, tcache, tbin, binind,\n\t\t\t    tbin->ncached - tbin->low_water + (tbin->low_water\n\t\t\t    >> 2));\n\t\t\t/*\n\t\t\t * Reduce fill count by 2X.  Limit lg_fill_div such that\n\t\t\t * the fill count is always at least 1.\n\t\t\t */\n\t\t\tcache_bin_info_t *tbin_info = &tcache_bin_info[binind];\n\t\t\tif ((tbin_info->ncached_max >>\n\t\t\t     (tcache->lg_fill_div[binind] + 1)) >= 1) {\n\t\t\t\ttcache->lg_fill_div[binind]++;\n\t\t\t}\n\t\t} else {\n\t\t\ttcache_bin_flush_large(tsd, tbin, binind, tbin->ncached\n\t\t\t    - tbin->low_water + (tbin->low_water >> 2), tcache);\n\t\t}\n\t} else if (tbin->low_water < 0) {\n\t\t/*\n\t\t * Increase fill count by 2X for small bins.  Make sure\n\t\t * lg_fill_div stays greater than 0.\n\t\t */\n\t\tif (binind < NBINS && tcache->lg_fill_div[binind] > 1) {\n\t\t\ttcache->lg_fill_div[binind]--;\n\t\t}\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\t}\n}\n\nvoid *\ntcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,\n    cache_bin_t *tbin, szind_t binind, bool *tcache_success) {\n\tvoid *ret;\n\n\tassert(tcache->arena != NULL);\n\tarena_tcache_fill_small(tsdn, arena, tcache, tbin, binind,\n\t    config_prof ? tcache->prof_accumbytes : 0);\n\tif (config_prof) {\n\t\ttcache->prof_accumbytes = 0;\n\t}\n\tret = cache_bin_alloc_easy(tbin, tcache_success);\n\n\treturn ret;\n}\n\nvoid\ntcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,\n    szind_t binind, unsigned rem) {\n\tbool merged_stats = false;\n\n\tassert(binind < NBINS);\n\tassert((cache_bin_sz_t)rem <= tbin->ncached);\n\n\tarena_t *arena = tcache->arena;\n\tassert(arena != NULL);\n\tunsigned nflush = tbin->ncached - rem;\n\tVARIABLE_ARRAY(extent_t *, item_extent, nflush);\n\t/* Look up extent once per item. */\n\tfor (unsigned i = 0 ; i < nflush; i++) {\n\t\titem_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));\n\t}\n\n\twhile (nflush > 0) {\n\t\t/* Lock the arena bin associated with the first object. */\n\t\textent_t *extent = item_extent[0];\n\t\tarena_t *bin_arena = extent_arena_get(extent);\n\t\tbin_t *bin = &bin_arena->bins[binind];\n\n\t\tif (config_prof && bin_arena == arena) {\n\t\t\tif (arena_prof_accum(tsd_tsdn(tsd), arena,\n\t\t\t    tcache->prof_accumbytes)) {\n\t\t\t\tprof_idump(tsd_tsdn(tsd));\n\t\t\t}\n\t\t\ttcache->prof_accumbytes = 0;\n\t\t}\n\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);\n\t\tif (config_stats && bin_arena == arena) {\n\t\t\tassert(!merged_stats);\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\tunsigned ndeferred = 0;\n\t\tfor (unsigned i = 0; i < nflush; i++) {\n\t\t\tvoid *ptr = *(tbin->avail - 1 - i);\n\t\t\textent = item_extent[i];\n\t\t\tassert(ptr != NULL && extent != NULL);\n\n\t\t\tif (extent_arena_get(extent) == bin_arena) {\n\t\t\t\tarena_dalloc_bin_junked_locked(tsd_tsdn(tsd),\n\t\t\t\t    bin_arena, extent, ptr);\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\t*(tbin->avail - 1 - ndeferred) = ptr;\n\t\t\t\titem_extent[ndeferred] = extent;\n\t\t\t\tndeferred++;\n\t\t\t}\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);\n\t\tarena_decay_ticks(tsd_tsdn(tsd), bin_arena, nflush - ndeferred);\n\t\tnflush = ndeferred;\n\t}\n\tif (config_stats && !merged_stats) {\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\tbin_t *bin = &arena->bins[binind];\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &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(tsd_tsdn(tsd), &bin->lock);\n\t}\n\n\tmemmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *\n\t    sizeof(void *));\n\ttbin->ncached = rem;\n\tif (tbin->ncached < tbin->low_water) {\n\t\ttbin->low_water = tbin->ncached;\n\t}\n}\n\nvoid\ntcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,\n    unsigned rem, tcache_t *tcache) {\n\tbool merged_stats = false;\n\n\tassert(binind < nhbins);\n\tassert((cache_bin_sz_t)rem <= tbin->ncached);\n\n\tarena_t *arena = tcache->arena;\n\tassert(arena != NULL);\n\tunsigned nflush = tbin->ncached - rem;\n\tVARIABLE_ARRAY(extent_t *, item_extent, nflush);\n\t/* Look up extent once per item. */\n\tfor (unsigned i = 0 ; i < nflush; i++) {\n\t\titem_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));\n\t}\n\n\twhile (nflush > 0) {\n\t\t/* Lock the arena associated with the first object. */\n\t\textent_t *extent = item_extent[0];\n\t\tarena_t *locked_arena = extent_arena_get(extent);\n\t\tUNUSED bool idump;\n\n\t\tif (config_prof) {\n\t\t\tidump = false;\n\t\t}\n\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx);\n\t\tfor (unsigned i = 0; i < nflush; i++) {\n\t\t\tvoid *ptr = *(tbin->avail - 1 - i);\n\t\t\tassert(ptr != NULL);\n\t\t\textent = item_extent[i];\n\t\t\tif (extent_arena_get(extent) == locked_arena) {\n\t\t\t\tlarge_dalloc_prep_junked_locked(tsd_tsdn(tsd),\n\t\t\t\t    extent);\n\t\t\t}\n\t\t}\n\t\tif ((config_prof || config_stats) && locked_arena == arena) {\n\t\t\tif (config_prof) {\n\t\t\t\tidump = arena_prof_accum(tsd_tsdn(tsd), 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_large_nrequests_add(tsd_tsdn(tsd),\n\t\t\t\t    &arena->stats, binind,\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\tmalloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx);\n\n\t\tunsigned ndeferred = 0;\n\t\tfor (unsigned i = 0; i < nflush; i++) {\n\t\t\tvoid *ptr = *(tbin->avail - 1 - i);\n\t\t\textent = item_extent[i];\n\t\t\tassert(ptr != NULL && extent != NULL);\n\n\t\t\tif (extent_arena_get(extent) == locked_arena) {\n\t\t\t\tlarge_dalloc_finish(tsd_tsdn(tsd), extent);\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 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\t*(tbin->avail - 1 - ndeferred) = ptr;\n\t\t\t\titem_extent[ndeferred] = extent;\n\t\t\t\tndeferred++;\n\t\t\t}\n\t\t}\n\t\tif (config_prof && idump) {\n\t\t\tprof_idump(tsd_tsdn(tsd));\n\t\t}\n\t\tarena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush -\n\t\t    ndeferred);\n\t\tnflush = ndeferred;\n\t}\n\tif (config_stats && !merged_stats) {\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_stats_large_nrequests_add(tsd_tsdn(tsd), &arena->stats,\n\t\t    binind, tbin->tstats.nrequests);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\n\tmemmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *\n\t    sizeof(void *));\n\ttbin->ncached = rem;\n\tif (tbin->ncached < tbin->low_water) {\n\t\ttbin->low_water = tbin->ncached;\n\t}\n}\n\nvoid\ntcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {\n\tassert(tcache->arena == NULL);\n\ttcache->arena = arena;\n\n\tif (config_stats) {\n\t\t/* Link into list of extant tcaches. */\n\t\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\n\t\tql_elm_new(tcache, link);\n\t\tql_tail_insert(&arena->tcache_ql, tcache, link);\n\t\tcache_bin_array_descriptor_init(\n\t\t    &tcache->cache_bin_array_descriptor, tcache->bins_small,\n\t\t    tcache->bins_large);\n\t\tql_tail_insert(&arena->cache_bin_array_descriptor_ql,\n\t\t    &tcache->cache_bin_array_descriptor, link);\n\n\t\tmalloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);\n\t}\n}\n\nstatic void\ntcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache) {\n\tarena_t *arena = tcache->arena;\n\tassert(arena != NULL);\n\tif (config_stats) {\n\t\t/* Unlink from list of extant tcaches. */\n\t\tmalloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);\n\t\tif (config_debug) {\n\t\t\tbool in_ql = false;\n\t\t\ttcache_t *iter;\n\t\t\tql_foreach(iter, &arena->tcache_ql, link) {\n\t\t\t\tif (iter == tcache) {\n\t\t\t\t\tin_ql = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert(in_ql);\n\t\t}\n\t\tql_remove(&arena->tcache_ql, tcache, link);\n\t\tql_remove(&arena->cache_bin_array_descriptor_ql,\n\t\t    &tcache->cache_bin_array_descriptor, link);\n\t\ttcache_stats_merge(tsdn, tcache, arena);\n\t\tmalloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);\n\t}\n\ttcache->arena = NULL;\n}\n\nvoid\ntcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {\n\ttcache_arena_dissociate(tsdn, tcache);\n\ttcache_arena_associate(tsdn, tcache, arena);\n}\n\nbool\ntsd_tcache_enabled_data_init(tsd_t *tsd) {\n\t/* Called upon tsd initialization. */\n\ttsd_tcache_enabled_set(tsd, opt_tcache);\n\ttsd_slow_update(tsd);\n\n\tif (opt_tcache) {\n\t\t/* Trigger tcache init. */\n\t\ttsd_tcache_data_init(tsd);\n\t}\n\n\treturn false;\n}\n\n/* Initialize auto tcache (embedded in TSD). */\nstatic void\ntcache_init(tsd_t *tsd, tcache_t *tcache, void *avail_stack) {\n\tmemset(&tcache->link, 0, sizeof(ql_elm(tcache_t)));\n\ttcache->prof_accumbytes = 0;\n\ttcache->next_gc_bin = 0;\n\ttcache->arena = NULL;\n\n\tticker_init(&tcache->gc_ticker, TCACHE_GC_INCR);\n\n\tsize_t stack_offset = 0;\n\tassert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0);\n\tmemset(tcache->bins_small, 0, sizeof(cache_bin_t) * NBINS);\n\tmemset(tcache->bins_large, 0, sizeof(cache_bin_t) * (nhbins - NBINS));\n\tunsigned i = 0;\n\tfor (; i < NBINS; i++) {\n\t\ttcache->lg_fill_div[i] = 1;\n\t\tstack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);\n\t\t/*\n\t\t * avail points past the available space.  Allocations will\n\t\t * access the slots toward higher addresses (for the benefit of\n\t\t * prefetch).\n\t\t */\n\t\ttcache_small_bin_get(tcache, i)->avail =\n\t\t    (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset);\n\t}\n\tfor (; i < nhbins; i++) {\n\t\tstack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);\n\t\ttcache_large_bin_get(tcache, i)->avail =\n\t\t    (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset);\n\t}\n\tassert(stack_offset == stack_nelms * sizeof(void *));\n}\n\n/* Initialize auto tcache (embedded in TSD). */\nbool\ntsd_tcache_data_init(tsd_t *tsd) {\n\ttcache_t *tcache = tsd_tcachep_get_unsafe(tsd);\n\tassert(tcache_small_bin_get(tcache, 0)->avail == NULL);\n\tsize_t size = stack_nelms * sizeof(void *);\n\t/* Avoid false cacheline sharing. */\n\tsize = sz_sa2u(size, CACHELINE);\n\n\tvoid *avail_array = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true,\n\t    NULL, true, arena_get(TSDN_NULL, 0, true));\n\tif (avail_array == NULL) {\n\t\treturn true;\n\t}\n\n\ttcache_init(tsd, tcache, avail_array);\n\t/*\n\t * Initialization is a bit tricky here.  After malloc init is done, all\n\t * threads can rely on arena_choose and associate tcache accordingly.\n\t * However, the thread that does actual malloc bootstrapping relies on\n\t * functional tsd, and it can only rely on a0.  In that case, we\n\t * associate its tcache to a0 temporarily, and later on\n\t * arena_choose_hard() will re-associate properly.\n\t */\n\ttcache->arena = NULL;\n\tarena_t *arena;\n\tif (!malloc_initialized()) {\n\t\t/* If in initialization, assign to a0. */\n\t\tarena = arena_get(tsd_tsdn(tsd), 0, false);\n\t\ttcache_arena_associate(tsd_tsdn(tsd), tcache, arena);\n\t} else {\n\t\tarena = arena_choose(tsd, NULL);\n\t\t/* This may happen if thread.tcache.enabled is used. */\n\t\tif (tcache->arena == NULL) {\n\t\t\ttcache_arena_associate(tsd_tsdn(tsd), tcache, arena);\n\t\t}\n\t}\n\tassert(arena == tcache->arena);\n\n\treturn false;\n}\n\n/* Created manual tcache for tcache.create mallctl. */\ntcache_t *\ntcache_create_explicit(tsd_t *tsd) {\n\ttcache_t *tcache;\n\tsize_t size, stack_offset;\n\n\tsize = sizeof(tcache_t);\n\t/* Naturally align the pointer stacks. */\n\tsize = PTR_CEILING(size);\n\tstack_offset = size;\n\tsize += stack_nelms * sizeof(void *);\n\t/* Avoid false cacheline sharing. */\n\tsize = sz_sa2u(size, CACHELINE);\n\n\ttcache = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, NULL, true,\n\t    arena_get(TSDN_NULL, 0, true));\n\tif (tcache == NULL) {\n\t\treturn NULL;\n\t}\n\n\ttcache_init(tsd, tcache,\n\t    (void *)((uintptr_t)tcache + (uintptr_t)stack_offset));\n\ttcache_arena_associate(tsd_tsdn(tsd), tcache, arena_ichoose(tsd, NULL));\n\n\treturn tcache;\n}\n\nstatic void\ntcache_flush_cache(tsd_t *tsd, tcache_t *tcache) {\n\tassert(tcache->arena != NULL);\n\n\tfor (unsigned i = 0; i < NBINS; i++) {\n\t\tcache_bin_t *tbin = tcache_small_bin_get(tcache, i);\n\t\ttcache_bin_flush_small(tsd, tcache, tbin, i, 0);\n\n\t\tif (config_stats) {\n\t\t\tassert(tbin->tstats.nrequests == 0);\n\t\t}\n\t}\n\tfor (unsigned i = NBINS; i < nhbins; i++) {\n\t\tcache_bin_t *tbin = tcache_large_bin_get(tcache, i);\n\t\ttcache_bin_flush_large(tsd, tbin, i, 0, tcache);\n\n\t\tif (config_stats) {\n\t\t\tassert(tbin->tstats.nrequests == 0);\n\t\t}\n\t}\n\n\tif (config_prof && tcache->prof_accumbytes > 0 &&\n\t    arena_prof_accum(tsd_tsdn(tsd), tcache->arena,\n\t    tcache->prof_accumbytes)) {\n\t\tprof_idump(tsd_tsdn(tsd));\n\t}\n}\n\nvoid\ntcache_flush(tsd_t *tsd) {\n\tassert(tcache_available(tsd));\n\ttcache_flush_cache(tsd, tsd_tcachep_get(tsd));\n}\n\nstatic void\ntcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) {\n\ttcache_flush_cache(tsd, tcache);\n\ttcache_arena_dissociate(tsd_tsdn(tsd), tcache);\n\n\tif (tsd_tcache) {\n\t\t/* Release the avail array for the TSD embedded auto tcache. */\n\t\tvoid *avail_array =\n\t\t    (void *)((uintptr_t)tcache_small_bin_get(tcache, 0)->avail -\n\t\t    (uintptr_t)tcache_bin_info[0].ncached_max * sizeof(void *));\n\t\tidalloctm(tsd_tsdn(tsd), avail_array, NULL, NULL, true, true);\n\t} else {\n\t\t/* Release both the tcache struct and avail array. */\n\t\tidalloctm(tsd_tsdn(tsd), tcache, NULL, NULL, true, true);\n\t}\n}\n\n/* For auto tcache (embedded in TSD) only. */\nvoid\ntcache_cleanup(tsd_t *tsd) {\n\ttcache_t *tcache = tsd_tcachep_get(tsd);\n\tif (!tcache_available(tsd)) {\n\t\tassert(tsd_tcache_enabled_get(tsd) == false);\n\t\tif (config_debug) {\n\t\t\tassert(tcache_small_bin_get(tcache, 0)->avail == NULL);\n\t\t}\n\t\treturn;\n\t}\n\tassert(tsd_tcache_enabled_get(tsd));\n\tassert(tcache_small_bin_get(tcache, 0)->avail != NULL);\n\n\ttcache_destroy(tsd, tcache, true);\n\tif (config_debug) {\n\t\ttcache_small_bin_get(tcache, 0)->avail = NULL;\n\t}\n}\n\nvoid\ntcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {\n\tunsigned i;\n\n\tcassert(config_stats);\n\n\t/* Merge and reset tcache stats. */\n\tfor (i = 0; i < NBINS; i++) {\n\t\tbin_t *bin = &arena->bins[i];\n\t\tcache_bin_t *tbin = tcache_small_bin_get(tcache, i);\n\t\tmalloc_mutex_lock(tsdn, &bin->lock);\n\t\tbin->stats.nrequests += tbin->tstats.nrequests;\n\t\tmalloc_mutex_unlock(tsdn, &bin->lock);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n\n\tfor (; i < nhbins; i++) {\n\t\tcache_bin_t *tbin = tcache_large_bin_get(tcache, i);\n\t\tarena_stats_large_nrequests_add(tsdn, &arena->stats, i,\n\t\t    tbin->tstats.nrequests);\n\t\ttbin->tstats.nrequests = 0;\n\t}\n}\n\nstatic bool\ntcaches_create_prep(tsd_t *tsd) {\n\tbool err;\n\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\n\tif (tcaches == NULL) {\n\t\ttcaches = base_alloc(tsd_tsdn(tsd), b0get(), sizeof(tcache_t *)\n\t\t    * (MALLOCX_TCACHE_MAX+1), CACHELINE);\n\t\tif (tcaches == NULL) {\n\t\t\terr = true;\n\t\t\tgoto label_return;\n\t\t}\n\t}\n\n\tif (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) {\n\t\terr = true;\n\t\tgoto label_return;\n\t}\n\n\terr = false;\nlabel_return:\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\treturn err;\n}\n\nbool\ntcaches_create(tsd_t *tsd, unsigned *r_ind) {\n\twitness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);\n\n\tbool err;\n\n\tif (tcaches_create_prep(tsd)) {\n\t\terr = true;\n\t\tgoto label_return;\n\t}\n\n\ttcache_t *tcache = tcache_create_explicit(tsd);\n\tif (tcache == NULL) {\n\t\terr = true;\n\t\tgoto label_return;\n\t}\n\n\ttcaches_t *elm;\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\tif (tcaches_avail != NULL) {\n\t\telm = tcaches_avail;\n\t\ttcaches_avail = tcaches_avail->next;\n\t\telm->tcache = tcache;\n\t\t*r_ind = (unsigned)(elm - tcaches);\n\t} else {\n\t\telm = &tcaches[tcaches_past];\n\t\telm->tcache = tcache;\n\t\t*r_ind = tcaches_past;\n\t\ttcaches_past++;\n\t}\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\n\terr = false;\nlabel_return:\n\twitness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);\n\treturn err;\n}\n\nstatic tcache_t *\ntcaches_elm_remove(tsd_t *tsd, tcaches_t *elm) {\n\tmalloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx);\n\n\tif (elm->tcache == NULL) {\n\t\treturn NULL;\n\t}\n\ttcache_t *tcache = elm->tcache;\n\telm->tcache = NULL;\n\treturn tcache;\n}\n\nvoid\ntcaches_flush(tsd_t *tsd, unsigned ind) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\ttcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind]);\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\tif (tcache != NULL) {\n\t\ttcache_destroy(tsd, tcache, false);\n\t}\n}\n\nvoid\ntcaches_destroy(tsd_t *tsd, unsigned ind) {\n\tmalloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);\n\ttcaches_t *elm = &tcaches[ind];\n\ttcache_t *tcache = tcaches_elm_remove(tsd, elm);\n\telm->next = tcaches_avail;\n\ttcaches_avail = elm;\n\tmalloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);\n\tif (tcache != NULL) {\n\t\ttcache_destroy(tsd, tcache, false);\n\t}\n}\n\nbool\ntcache_boot(tsdn_t *tsdn) {\n\t/* If necessary, clamp opt_lg_tcache_max. */\n\tif (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) <\n\t    SMALL_MAXCLASS) {\n\t\ttcache_maxclass = SMALL_MAXCLASS;\n\t} else {\n\t\ttcache_maxclass = (ZU(1) << opt_lg_tcache_max);\n\t}\n\n\tif (malloc_mutex_init(&tcaches_mtx, \"tcaches\", WITNESS_RANK_TCACHES,\n\t    malloc_mutex_rank_exclusive)) {\n\t\treturn true;\n\t}\n\n\tnhbins = sz_size2index(tcache_maxclass) + 1;\n\n\t/* Initialize tcache_bin_info. */\n\ttcache_bin_info = (cache_bin_info_t *)base_alloc(tsdn, b0get(), nhbins\n\t    * sizeof(cache_bin_info_t), CACHELINE);\n\tif (tcache_bin_info == NULL) {\n\t\treturn true;\n\t}\n\tstack_nelms = 0;\n\tunsigned i;\n\tfor (i = 0; i < NBINS; i++) {\n\t\tif ((bin_infos[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) {\n\t\t\ttcache_bin_info[i].ncached_max =\n\t\t\t    TCACHE_NSLOTS_SMALL_MIN;\n\t\t} else if ((bin_infos[i].nregs << 1) <=\n\t\t    TCACHE_NSLOTS_SMALL_MAX) {\n\t\t\ttcache_bin_info[i].ncached_max =\n\t\t\t    (bin_infos[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\nvoid\ntcache_prefork(tsdn_t *tsdn) {\n\tif (!config_prof && opt_tcache) {\n\t\tmalloc_mutex_prefork(tsdn, &tcaches_mtx);\n\t}\n}\n\nvoid\ntcache_postfork_parent(tsdn_t *tsdn) {\n\tif (!config_prof && opt_tcache) {\n\t\tmalloc_mutex_postfork_parent(tsdn, &tcaches_mtx);\n\t}\n}\n\nvoid\ntcache_postfork_child(tsdn_t *tsdn) {\n\tif (!config_prof && opt_tcache) {\n\t\tmalloc_mutex_postfork_child(tsdn, &tcaches_mtx);\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/ticker.c",
    "content": "#define JEMALLOC_TICKER_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/tsd.c",
    "content": "#define JEMALLOC_TSD_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/mutex.h\"\n#include \"jemalloc/internal/rtree.h\"\n\n/******************************************************************************/\n/* Data. */\n\nstatic unsigned ncleanups;\nstatic malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];\n\n#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP\n__thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;\n__thread bool JEMALLOC_TLS_MODEL tsd_initialized = false;\nbool tsd_booted = false;\n#elif (defined(JEMALLOC_TLS))\n__thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;\npthread_key_t tsd_tsd;\nbool tsd_booted = false;\n#elif (defined(_WIN32))\nDWORD tsd_tsd;\ntsd_wrapper_t tsd_boot_wrapper = {false, TSD_INITIALIZER};\nbool tsd_booted = false;\n#else\n\n/*\n * This contains a mutex, but it's pretty convenient to allow the mutex code to\n * have a dependency on tsd.  So we define the struct here, and only refer to it\n * by pointer in the header.\n */\nstruct tsd_init_head_s {\n\tql_head(tsd_init_block_t) blocks;\n\tmalloc_mutex_t lock;\n};\n\npthread_key_t tsd_tsd;\ntsd_init_head_t\ttsd_init_head = {\n\tql_head_initializer(blocks),\n\tMALLOC_MUTEX_INITIALIZER\n};\ntsd_wrapper_t tsd_boot_wrapper = {\n\tfalse,\n\tTSD_INITIALIZER\n};\nbool tsd_booted = false;\n#endif\n\n\n/******************************************************************************/\n\nvoid\ntsd_slow_update(tsd_t *tsd) {\n\tif (tsd_nominal(tsd)) {\n\t\tif (malloc_slow || !tsd_tcache_enabled_get(tsd) ||\n\t\t    tsd_reentrancy_level_get(tsd) > 0) {\n\t\t\ttsd->state = tsd_state_nominal_slow;\n\t\t} else {\n\t\t\ttsd->state = tsd_state_nominal;\n\t\t}\n\t}\n}\n\nstatic bool\ntsd_data_init(tsd_t *tsd) {\n\t/*\n\t * We initialize the rtree context first (before the tcache), since the\n\t * tcache initialization depends on it.\n\t */\n\trtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));\n\n\t/*\n\t * A nondeterministic seed based on the address of tsd reduces\n\t * the likelihood of lockstep non-uniform cache index\n\t * utilization among identical concurrent processes, but at the\n\t * cost of test repeatability.  For debug builds, instead use a\n\t * deterministic seed.\n\t */\n\t*tsd_offset_statep_get(tsd) = config_debug ? 0 :\n\t    (uint64_t)(uintptr_t)tsd;\n\n\treturn tsd_tcache_enabled_data_init(tsd);\n}\n\nstatic void\nassert_tsd_data_cleanup_done(tsd_t *tsd) {\n\tassert(!tsd_nominal(tsd));\n\tassert(*tsd_arenap_get_unsafe(tsd) == NULL);\n\tassert(*tsd_iarenap_get_unsafe(tsd) == NULL);\n\tassert(*tsd_arenas_tdata_bypassp_get_unsafe(tsd) == true);\n\tassert(*tsd_arenas_tdatap_get_unsafe(tsd) == NULL);\n\tassert(*tsd_tcache_enabledp_get_unsafe(tsd) == false);\n\tassert(*tsd_prof_tdatap_get_unsafe(tsd) == NULL);\n}\n\nstatic bool\ntsd_data_init_nocleanup(tsd_t *tsd) {\n\tassert(tsd->state == tsd_state_reincarnated ||\n\t    tsd->state == tsd_state_minimal_initialized);\n\t/*\n\t * During reincarnation, there is no guarantee that the cleanup function\n\t * will be called (deallocation may happen after all tsd destructors).\n\t * We set up tsd in a way that no cleanup is needed.\n\t */\n\trtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));\n\t*tsd_arenas_tdata_bypassp_get(tsd) = true;\n\t*tsd_tcache_enabledp_get_unsafe(tsd) = false;\n\t*tsd_reentrancy_levelp_get(tsd) = 1;\n\tassert_tsd_data_cleanup_done(tsd);\n\n\treturn false;\n}\n\ntsd_t *\ntsd_fetch_slow(tsd_t *tsd, bool minimal) {\n\tassert(!tsd_fast(tsd));\n\n\tif (tsd->state == tsd_state_nominal_slow) {\n\t\t/* On slow path but no work needed. */\n\t\tassert(malloc_slow || !tsd_tcache_enabled_get(tsd) ||\n\t\t    tsd_reentrancy_level_get(tsd) > 0 ||\n\t\t    *tsd_arenas_tdata_bypassp_get(tsd));\n\t} else if (tsd->state == tsd_state_uninitialized) {\n\t\tif (!minimal) {\n\t\t\ttsd->state = tsd_state_nominal;\n\t\t\ttsd_slow_update(tsd);\n\t\t\t/* Trigger cleanup handler registration. */\n\t\t\ttsd_set(tsd);\n\t\t\ttsd_data_init(tsd);\n\t\t} else {\n\t\t\ttsd->state = tsd_state_minimal_initialized;\n\t\t\ttsd_set(tsd);\n\t\t\ttsd_data_init_nocleanup(tsd);\n\t\t}\n\t} else if (tsd->state == tsd_state_minimal_initialized) {\n\t\tif (!minimal) {\n\t\t\t/* Switch to fully initialized. */\n\t\t\ttsd->state = tsd_state_nominal;\n\t\t\tassert(*tsd_reentrancy_levelp_get(tsd) >= 1);\n\t\t\t(*tsd_reentrancy_levelp_get(tsd))--;\n\t\t\ttsd_slow_update(tsd);\n\t\t\ttsd_data_init(tsd);\n\t\t} else {\n\t\t\tassert_tsd_data_cleanup_done(tsd);\n\t\t}\n\t} else if (tsd->state == tsd_state_purgatory) {\n\t\ttsd->state = tsd_state_reincarnated;\n\t\ttsd_set(tsd);\n\t\ttsd_data_init_nocleanup(tsd);\n\t} else {\n\t\tassert(tsd->state == tsd_state_reincarnated);\n\t}\n\n\treturn tsd;\n}\n\nvoid *\nmalloc_tsd_malloc(size_t size) {\n\treturn a0malloc(CACHELINE_CEILING(size));\n}\n\nvoid\nmalloc_tsd_dalloc(void *wrapper) {\n\ta0dalloc(wrapper);\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\tbool pending[MALLOC_TSD_CLEANUPS_MAX], again;\n\tunsigned i;\n\n\tfor (i = 0; i < ncleanups; i++) {\n\t\tpending[i] = true;\n\t}\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\t}\n\t\t\t}\n\t\t}\n\t} while (again);\n}\n#endif\n\nvoid\nmalloc_tsd_cleanup_register(bool (*f)(void)) {\n\tassert(ncleanups < MALLOC_TSD_CLEANUPS_MAX);\n\tcleanups[ncleanups] = f;\n\tncleanups++;\n}\n\nstatic void\ntsd_do_data_cleanup(tsd_t *tsd) {\n\tprof_tdata_cleanup(tsd);\n\tiarena_cleanup(tsd);\n\tarena_cleanup(tsd);\n\tarenas_tdata_cleanup(tsd);\n\ttcache_cleanup(tsd);\n\twitnesses_cleanup(tsd_witness_tsdp_get_unsafe(tsd));\n}\n\nvoid\ntsd_cleanup(void *arg) {\n\ttsd_t *tsd = (tsd_t *)arg;\n\n\tswitch (tsd->state) {\n\tcase tsd_state_uninitialized:\n\t\t/* Do nothing. */\n\t\tbreak;\n\tcase tsd_state_minimal_initialized:\n\t\t/* This implies the thread only did free() in its life time. */\n\t\t/* Fall through. */\n\tcase tsd_state_reincarnated:\n\t\t/*\n\t\t * Reincarnated means another destructor deallocated memory\n\t\t * after the destructor was called.  Cleanup isn't required but\n\t\t * is still called for testing and completeness.\n\t\t */\n\t\tassert_tsd_data_cleanup_done(tsd);\n\t\t/* Fall through. */\n\tcase tsd_state_nominal:\n\tcase tsd_state_nominal_slow:\n\t\ttsd_do_data_cleanup(tsd);\n\t\ttsd->state = tsd_state_purgatory;\n\t\ttsd_set(tsd);\n\t\tbreak;\n\tcase tsd_state_purgatory:\n\t\t/*\n\t\t * The previous time this destructor was called, we set the\n\t\t * state to tsd_state_purgatory so that other destructors\n\t\t * wouldn't cause re-creation of the tsd.  This time, do\n\t\t * nothing, and do not request another callback.\n\t\t */\n\t\tbreak;\n\tdefault:\n\t\tnot_reached();\n\t}\n#ifdef JEMALLOC_JET\n\ttest_callback_t test_callback = *tsd_test_callbackp_get_unsafe(tsd);\n\tint *data = tsd_test_datap_get_unsafe(tsd);\n\tif (test_callback != NULL) {\n\t\ttest_callback(data);\n\t}\n#endif\n}\n\ntsd_t *\nmalloc_tsd_boot0(void) {\n\ttsd_t *tsd;\n\n\tncleanups = 0;\n\tif (tsd_boot0()) {\n\t\treturn NULL;\n\t}\n\ttsd = tsd_fetch();\n\t*tsd_arenas_tdata_bypassp_get(tsd) = true;\n\treturn tsd;\n}\n\nvoid\nmalloc_tsd_boot1(void) {\n\ttsd_boot1();\n\ttsd_t *tsd = tsd_fetch();\n\t/* malloc_slow has been set properly.  Update tsd_slow. */\n\ttsd_slow_update(tsd);\n\t*tsd_arenas_tdata_bypassp_get(tsd) = false;\n}\n\n#ifdef _WIN32\nstatic BOOL WINAPI\n_tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {\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/*\n * We need to be able to say \"read\" here (in the \"pragma section\"), but have\n * hooked \"read\". We won't read for the rest of the file, so we can get away\n * with unhooking.\n */\n#ifdef read\n#  undef read\n#endif\n\n#ifdef _MSC_VER\n#  ifdef _M_IX86\n#    pragma comment(linker, \"/INCLUDE:__tls_used\")\n#    pragma comment(linker, \"/INCLUDE:_tls_callback\")\n#  else\n#    pragma comment(linker, \"/INCLUDE:_tls_used\")\n#    pragma comment(linker, \"/INCLUDE:tls_callback\")\n#  endif\n#  pragma section(\".CRT$XLY\",long,read)\n#endif\nJEMALLOC_SECTION(\".CRT$XLY\") JEMALLOC_ATTR(used)\nBOOL\t(WINAPI *const tls_callback)(HINSTANCE hinstDLL,\n    DWORD fdwReason, LPVOID lpvReserved) = _tls_callback;\n#endif\n\n#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \\\n    !defined(_WIN32))\nvoid *\ntsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block) {\n\tpthread_t self = pthread_self();\n\ttsd_init_block_t *iter;\n\n\t/* Check whether this thread has already inserted into the list. */\n\tmalloc_mutex_lock(TSDN_NULL, &head->lock);\n\tql_foreach(iter, &head->blocks, link) {\n\t\tif (iter->thread == self) {\n\t\t\tmalloc_mutex_unlock(TSDN_NULL, &head->lock);\n\t\t\treturn iter->data;\n\t\t}\n\t}\n\t/* Insert block into list. */\n\tql_elm_new(block, link);\n\tblock->thread = self;\n\tql_tail_insert(&head->blocks, block, link);\n\tmalloc_mutex_unlock(TSDN_NULL, &head->lock);\n\treturn NULL;\n}\n\nvoid\ntsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) {\n\tmalloc_mutex_lock(TSDN_NULL, &head->lock);\n\tql_remove(&head->blocks, block, link);\n\tmalloc_mutex_unlock(TSDN_NULL, &head->lock);\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/witness.c",
    "content": "#define JEMALLOC_WITNESS_C_\n#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n#include \"jemalloc/internal/malloc_io.h\"\n\nvoid\nwitness_init(witness_t *witness, const char *name, witness_rank_t rank,\n    witness_comp_t *comp, void *opaque) {\n\twitness->name = name;\n\twitness->rank = rank;\n\twitness->comp = comp;\n\twitness->opaque = opaque;\n}\n\nstatic void\nwitness_lock_error_impl(const witness_list_t *witnesses,\n    const witness_t *witness) {\n\twitness_t *w;\n\n\tmalloc_printf(\"<jemalloc>: Lock rank order reversal:\");\n\tql_foreach(w, witnesses, link) {\n\t\tmalloc_printf(\" %s(%u)\", w->name, w->rank);\n\t}\n\tmalloc_printf(\" %s(%u)\\n\", witness->name, witness->rank);\n\tabort();\n}\nwitness_lock_error_t *JET_MUTABLE witness_lock_error = witness_lock_error_impl;\n\nstatic void\nwitness_owner_error_impl(const witness_t *witness) {\n\tmalloc_printf(\"<jemalloc>: Should own %s(%u)\\n\", witness->name,\n\t    witness->rank);\n\tabort();\n}\nwitness_owner_error_t *JET_MUTABLE witness_owner_error =\n    witness_owner_error_impl;\n\nstatic void\nwitness_not_owner_error_impl(const witness_t *witness) {\n\tmalloc_printf(\"<jemalloc>: Should not own %s(%u)\\n\", witness->name,\n\t    witness->rank);\n\tabort();\n}\nwitness_not_owner_error_t *JET_MUTABLE witness_not_owner_error =\n    witness_not_owner_error_impl;\n\nstatic void\nwitness_depth_error_impl(const witness_list_t *witnesses,\n    witness_rank_t rank_inclusive, unsigned depth) {\n\twitness_t *w;\n\n\tmalloc_printf(\"<jemalloc>: Should own %u lock%s of rank >= %u:\", depth,\n\t    (depth != 1) ?  \"s\" : \"\", rank_inclusive);\n\tql_foreach(w, witnesses, link) {\n\t\tmalloc_printf(\" %s(%u)\", w->name, w->rank);\n\t}\n\tmalloc_printf(\"\\n\");\n\tabort();\n}\nwitness_depth_error_t *JET_MUTABLE witness_depth_error =\n    witness_depth_error_impl;\n\nvoid\nwitnesses_cleanup(witness_tsd_t *witness_tsd) {\n\twitness_assert_lockless(witness_tsd_tsdn(witness_tsd));\n\n\t/* Do nothing. */\n}\n\nvoid\nwitness_prefork(witness_tsd_t *witness_tsd) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\twitness_tsd->forking = true;\n}\n\nvoid\nwitness_postfork_parent(witness_tsd_t *witness_tsd) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n\twitness_tsd->forking = false;\n}\n\nvoid\nwitness_postfork_child(witness_tsd_t *witness_tsd) {\n\tif (!config_debug) {\n\t\treturn;\n\t}\n#ifndef JEMALLOC_MUTEX_INIT_CB\n\twitness_list_t *witnesses;\n\n\twitnesses = &witness_tsd->witnesses;\n\tql_new(witnesses);\n#endif\n\twitness_tsd->forking = false;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/src/zone.c",
    "content": "#include \"jemalloc/internal/jemalloc_preamble.h\"\n#include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n#include \"jemalloc/internal/assert.h\"\n\n#ifndef JEMALLOC_ZONE\n#  error \"This source file is for zones on Darwin (OS X).\"\n#endif\n\n/* Definitions of the following structs in malloc/malloc.h might be too old\n * for the built binary to run on newer versions of OSX. So use the newest\n * possible version of those structs.\n */\ntypedef struct _malloc_zone_t {\n\tvoid *reserved1;\n\tvoid *reserved2;\n\tsize_t (*size)(struct _malloc_zone_t *, const void *);\n\tvoid *(*malloc)(struct _malloc_zone_t *, size_t);\n\tvoid *(*calloc)(struct _malloc_zone_t *, size_t, size_t);\n\tvoid *(*valloc)(struct _malloc_zone_t *, size_t);\n\tvoid (*free)(struct _malloc_zone_t *, void *);\n\tvoid *(*realloc)(struct _malloc_zone_t *, void *, size_t);\n\tvoid (*destroy)(struct _malloc_zone_t *);\n\tconst char *zone_name;\n\tunsigned (*batch_malloc)(struct _malloc_zone_t *, size_t, void **, unsigned);\n\tvoid (*batch_free)(struct _malloc_zone_t *, void **, unsigned);\n\tstruct malloc_introspection_t *introspect;\n\tunsigned version;\n\tvoid *(*memalign)(struct _malloc_zone_t *, size_t, size_t);\n\tvoid (*free_definite_size)(struct _malloc_zone_t *, void *, size_t);\n\tsize_t (*pressure_relief)(struct _malloc_zone_t *, size_t);\n} malloc_zone_t;\n\ntypedef struct {\n\tvm_address_t address;\n\tvm_size_t size;\n} vm_range_t;\n\ntypedef struct malloc_statistics_t {\n\tunsigned blocks_in_use;\n\tsize_t size_in_use;\n\tsize_t max_size_in_use;\n\tsize_t size_allocated;\n} malloc_statistics_t;\n\ntypedef kern_return_t memory_reader_t(task_t, vm_address_t, vm_size_t, void **);\n\ntypedef void vm_range_recorder_t(task_t, void *, unsigned type, vm_range_t *, unsigned);\n\ntypedef struct malloc_introspection_t {\n\tkern_return_t (*enumerator)(task_t, void *, unsigned, vm_address_t, memory_reader_t, vm_range_recorder_t);\n\tsize_t (*good_size)(malloc_zone_t *, size_t);\n\tboolean_t (*check)(malloc_zone_t *);\n\tvoid (*print)(malloc_zone_t *, boolean_t);\n\tvoid (*log)(malloc_zone_t *, void *);\n\tvoid (*force_lock)(malloc_zone_t *);\n\tvoid (*force_unlock)(malloc_zone_t *);\n\tvoid (*statistics)(malloc_zone_t *, malloc_statistics_t *);\n\tboolean_t (*zone_locked)(malloc_zone_t *);\n\tboolean_t (*enable_discharge_checking)(malloc_zone_t *);\n\tboolean_t (*disable_discharge_checking)(malloc_zone_t *);\n\tvoid (*discharge)(malloc_zone_t *, void *);\n#ifdef __BLOCKS__\n\tvoid (*enumerate_discharged_pointers)(malloc_zone_t *, void (^)(void *, void *));\n#else\n\tvoid *enumerate_unavailable_without_blocks;\n#endif\n\tvoid (*reinit_lock)(malloc_zone_t *);\n} malloc_introspection_t;\n\nextern kern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *);\n\nextern malloc_zone_t *malloc_default_zone(void);\n\nextern void malloc_zone_register(malloc_zone_t *zone);\n\nextern void malloc_zone_unregister(malloc_zone_t *zone);\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 *default_zone, *purgeable_zone;\nstatic malloc_zone_t jemalloc_zone;\nstatic struct malloc_introspection_t jemalloc_zone_introspect;\nstatic pid_t zone_force_lock_pid = -1;\n\n/******************************************************************************/\n/* Function prototypes for non-inline static functions. */\n\nstatic size_t\tzone_size(malloc_zone_t *zone, const 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);\nstatic void\t*zone_memalign(malloc_zone_t *zone, size_t alignment,\n    size_t size);\nstatic void\tzone_free_definite_size(malloc_zone_t *zone, void *ptr,\n    size_t size);\nstatic void\tzone_destroy(malloc_zone_t *zone);\nstatic unsigned\tzone_batch_malloc(struct _malloc_zone_t *zone, size_t size,\n    void **results, unsigned num_requested);\nstatic void\tzone_batch_free(struct _malloc_zone_t *zone,\n    void **to_be_freed, unsigned num_to_be_freed);\nstatic size_t\tzone_pressure_relief(struct _malloc_zone_t *zone, size_t goal);\nstatic size_t\tzone_good_size(malloc_zone_t *zone, size_t size);\nstatic kern_return_t\tzone_enumerator(task_t task, void *data, unsigned type_mask,\n    vm_address_t zone_address, memory_reader_t reader,\n    vm_range_recorder_t recorder);\nstatic boolean_t\tzone_check(malloc_zone_t *zone);\nstatic void\tzone_print(malloc_zone_t *zone, boolean_t verbose);\nstatic void\tzone_log(malloc_zone_t *zone, void *address);\nstatic void\tzone_force_lock(malloc_zone_t *zone);\nstatic void\tzone_force_unlock(malloc_zone_t *zone);\nstatic void\tzone_statistics(malloc_zone_t *zone,\n    malloc_statistics_t *stats);\nstatic boolean_t\tzone_locked(malloc_zone_t *zone);\nstatic void\tzone_reinit_lock(malloc_zone_t *zone);\n\n/******************************************************************************/\n/*\n * Functions.\n */\n\nstatic size_t\nzone_size(malloc_zone_t *zone, const void *ptr) {\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 extent before determining size.\n\t */\n\treturn ivsalloc(tsdn_fetch(), ptr);\n}\n\nstatic void *\nzone_malloc(malloc_zone_t *zone, size_t size) {\n\treturn je_malloc(size);\n}\n\nstatic void *\nzone_calloc(malloc_zone_t *zone, size_t num, size_t size) {\n\treturn je_calloc(num, size);\n}\n\nstatic void *\nzone_valloc(malloc_zone_t *zone, size_t size) {\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\tif (ivsalloc(tsdn_fetch(), ptr) != 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\tif (ivsalloc(tsdn_fetch(), ptr) != 0) {\n\t\treturn je_realloc(ptr, size);\n\t}\n\n\treturn realloc(ptr, size);\n}\n\nstatic void *\nzone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) {\n\tvoid *ret = NULL; /* Assignment avoids useless compiler warning. */\n\n\tje_posix_memalign(&ret, alignment, size);\n\n\treturn ret;\n}\n\nstatic void\nzone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) {\n\tsize_t alloc_size;\n\n\talloc_size = ivsalloc(tsdn_fetch(), ptr);\n\tif (alloc_size != 0) {\n\t\tassert(alloc_size == size);\n\t\tje_free(ptr);\n\t\treturn;\n\t}\n\n\tfree(ptr);\n}\n\nstatic void\nzone_destroy(malloc_zone_t *zone) {\n\t/* This function should never be called. */\n\tnot_reached();\n}\n\nstatic unsigned\nzone_batch_malloc(struct _malloc_zone_t *zone, size_t size, void **results,\n    unsigned num_requested) {\n\tunsigned i;\n\n\tfor (i = 0; i < num_requested; i++) {\n\t\tresults[i] = je_malloc(size);\n\t\tif (!results[i])\n\t\t\tbreak;\n\t}\n\n\treturn i;\n}\n\nstatic void\nzone_batch_free(struct _malloc_zone_t *zone, void **to_be_freed,\n    unsigned num_to_be_freed) {\n\tunsigned i;\n\n\tfor (i = 0; i < num_to_be_freed; i++) {\n\t\tzone_free(zone, to_be_freed[i]);\n\t\tto_be_freed[i] = NULL;\n\t}\n}\n\nstatic size_t\nzone_pressure_relief(struct _malloc_zone_t *zone, size_t goal) {\n\treturn 0;\n}\n\nstatic size_t\nzone_good_size(malloc_zone_t *zone, size_t size) {\n\tif (size == 0) {\n\t\tsize = 1;\n\t}\n\treturn sz_s2u(size);\n}\n\nstatic kern_return_t\nzone_enumerator(task_t task, void *data, unsigned type_mask,\n    vm_address_t zone_address, memory_reader_t reader,\n    vm_range_recorder_t recorder) {\n\treturn KERN_SUCCESS;\n}\n\nstatic boolean_t\nzone_check(malloc_zone_t *zone) {\n\treturn true;\n}\n\nstatic void\nzone_print(malloc_zone_t *zone, boolean_t verbose) {\n}\n\nstatic void\nzone_log(malloc_zone_t *zone, void *address) {\n}\n\nstatic void\nzone_force_lock(malloc_zone_t *zone) {\n\tif (isthreaded) {\n\t\t/*\n\t\t * See the note in zone_force_unlock, below, to see why we need\n\t\t * this.\n\t\t */\n\t\tassert(zone_force_lock_pid == -1);\n\t\tzone_force_lock_pid = getpid();\n\t\tjemalloc_prefork();\n\t}\n}\n\nstatic void\nzone_force_unlock(malloc_zone_t *zone) {\n\t/*\n\t * zone_force_lock and zone_force_unlock are the entry points to the\n\t * forking machinery on OS X.  The tricky thing is, the child is not\n\t * allowed to unlock mutexes locked in the parent, even if owned by the\n\t * forking thread (and the mutex type we use in OS X will fail an assert\n\t * if we try).  In the child, we can get away with reinitializing all\n\t * the mutexes, which has the effect of unlocking them.  In the parent,\n\t * doing this would mean we wouldn't wake any waiters blocked on the\n\t * mutexes we unlock.  So, we record the pid of the current thread in\n\t * zone_force_lock, and use that to detect if we're in the parent or\n\t * child here, to decide which unlock logic we need.\n\t */\n\tif (isthreaded) {\n\t\tassert(zone_force_lock_pid != -1);\n\t\tif (getpid() == zone_force_lock_pid) {\n\t\t\tjemalloc_postfork_parent();\n\t\t} else {\n\t\t\tjemalloc_postfork_child();\n\t\t}\n\t\tzone_force_lock_pid = -1;\n\t}\n}\n\nstatic void\nzone_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {\n\t/* We make no effort to actually fill the values */\n\tstats->blocks_in_use = 0;\n\tstats->size_in_use = 0;\n\tstats->max_size_in_use = 0;\n\tstats->size_allocated = 0;\n}\n\nstatic boolean_t\nzone_locked(malloc_zone_t *zone) {\n\t/* Pretend no lock is being held */\n\treturn false;\n}\n\nstatic void\nzone_reinit_lock(malloc_zone_t *zone) {\n\t/* As of OSX 10.12, this function is only used when force_unlock would\n\t * be used if the zone version were < 9. So just use force_unlock. */\n\tzone_force_unlock(zone);\n}\n\nstatic void\nzone_init(void) {\n\tjemalloc_zone.size = zone_size;\n\tjemalloc_zone.malloc = zone_malloc;\n\tjemalloc_zone.calloc = zone_calloc;\n\tjemalloc_zone.valloc = zone_valloc;\n\tjemalloc_zone.free = zone_free;\n\tjemalloc_zone.realloc = zone_realloc;\n\tjemalloc_zone.destroy = zone_destroy;\n\tjemalloc_zone.zone_name = \"jemalloc_zone\";\n\tjemalloc_zone.batch_malloc = zone_batch_malloc;\n\tjemalloc_zone.batch_free = zone_batch_free;\n\tjemalloc_zone.introspect = &jemalloc_zone_introspect;\n\tjemalloc_zone.version = 9;\n\tjemalloc_zone.memalign = zone_memalign;\n\tjemalloc_zone.free_definite_size = zone_free_definite_size;\n\tjemalloc_zone.pressure_relief = zone_pressure_relief;\n\n\tjemalloc_zone_introspect.enumerator = zone_enumerator;\n\tjemalloc_zone_introspect.good_size = zone_good_size;\n\tjemalloc_zone_introspect.check = zone_check;\n\tjemalloc_zone_introspect.print = zone_print;\n\tjemalloc_zone_introspect.log = zone_log;\n\tjemalloc_zone_introspect.force_lock = zone_force_lock;\n\tjemalloc_zone_introspect.force_unlock = zone_force_unlock;\n\tjemalloc_zone_introspect.statistics = zone_statistics;\n\tjemalloc_zone_introspect.zone_locked = zone_locked;\n\tjemalloc_zone_introspect.enable_discharge_checking = NULL;\n\tjemalloc_zone_introspect.disable_discharge_checking = NULL;\n\tjemalloc_zone_introspect.discharge = NULL;\n#ifdef __BLOCKS__\n\tjemalloc_zone_introspect.enumerate_discharged_pointers = NULL;\n#else\n\tjemalloc_zone_introspect.enumerate_unavailable_without_blocks = NULL;\n#endif\n\tjemalloc_zone_introspect.reinit_lock = zone_reinit_lock;\n}\n\nstatic malloc_zone_t *\nzone_default_get(void) {\n\tmalloc_zone_t **zones = NULL;\n\tunsigned int num_zones = 0;\n\n\t/*\n\t * On OSX 10.12, malloc_default_zone returns a special zone that is not\n\t * present in the list of registered zones. That zone uses a \"lite zone\"\n\t * if one is present (apparently enabled when malloc stack logging is\n\t * enabled), or the first registered zone otherwise. In practice this\n\t * means unless malloc stack logging is enabled, the first registered\n\t * zone is the default.  So get the list of zones to get the first one,\n\t * instead of relying on malloc_default_zone.\n\t */\n\tif (KERN_SUCCESS != malloc_get_all_zones(0, NULL,\n\t    (vm_address_t**)&zones, &num_zones)) {\n\t\t/*\n\t\t * Reset the value in case the failure happened after it was\n\t\t * set.\n\t\t */\n\t\tnum_zones = 0;\n\t}\n\n\tif (num_zones) {\n\t\treturn zones[0];\n\t}\n\n\treturn malloc_default_zone();\n}\n\n/* As written, this function can only promote jemalloc_zone. */\nstatic void\nzone_promote(void) {\n\tmalloc_zone_t *zone;\n\n\tdo {\n\t\t/*\n\t\t * Unregister and reregister the default zone.  On OSX >= 10.6,\n\t\t * unregistering takes the last registered zone and places it\n\t\t * at the location of the specified zone.  Unregistering the\n\t\t * default zone thus makes the last registered one the default.\n\t\t * On OSX < 10.6, unregistering shifts all registered zones.\n\t\t * The first registered zone then becomes the default.\n\t\t */\n\t\tmalloc_zone_unregister(default_zone);\n\t\tmalloc_zone_register(default_zone);\n\n\t\t/*\n\t\t * On OSX 10.6, having the default purgeable zone appear before\n\t\t * the default zone makes some things crash because it thinks it\n\t\t * owns the default zone allocated pointers.  We thus\n\t\t * unregister/re-register it in order to ensure it's always\n\t\t * after the default zone.  On OSX < 10.6, there is no purgeable\n\t\t * zone, so this does nothing.  On OSX >= 10.6, unregistering\n\t\t * replaces the purgeable zone with the last registered zone\n\t\t * above, i.e. the default zone.  Registering it again then puts\n\t\t * it at the end, obviously after the default zone.\n\t\t */\n\t\tif (purgeable_zone != NULL) {\n\t\t\tmalloc_zone_unregister(purgeable_zone);\n\t\t\tmalloc_zone_register(purgeable_zone);\n\t\t}\n\n\t\tzone = zone_default_get();\n\t} while (zone != &jemalloc_zone);\n}\n\nJEMALLOC_ATTR(constructor)\nvoid\nzone_register(void) {\n\t/*\n\t * If something else replaced the system default zone allocator, don't\n\t * register jemalloc's.\n\t */\n\tdefault_zone = zone_default_get();\n\tif (!default_zone->zone_name || strcmp(default_zone->zone_name,\n\t    \"DefaultMallocZone\") != 0) {\n\t\treturn;\n\t}\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\tpurgeable_zone = (malloc_default_purgeable_zone == NULL) ? NULL :\n\t    malloc_default_purgeable_zone();\n\n\t/* Register the custom zone.  At this point it won't be the default. */\n\tzone_init();\n\tmalloc_zone_register(&jemalloc_zone);\n\n\t/* Promote the custom zone to be default. */\n\tzone_promote();\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-alti.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file SFMT-alti.h\n *\n * @brief SIMD oriented Fast Mersenne Twister(SFMT)\n * pseudorandom number generator\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * Copyright (C) 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software.\n * see LICENSE.txt\n */\n\n#ifndef SFMT_ALTI_H\n#define SFMT_ALTI_H\n\n/**\n * This function represents the recursion formula in AltiVec and BIG ENDIAN.\n * @param a a 128-bit part of the interal state array\n * @param b a 128-bit part of the interal state array\n * @param c a 128-bit part of the interal state array\n * @param d a 128-bit part of the interal state array\n * @return output\n */\nJEMALLOC_ALWAYS_INLINE\nvector unsigned int vec_recursion(vector unsigned int a,\n\t\t\t\t\t\tvector unsigned int b,\n\t\t\t\t\t\tvector unsigned int c,\n\t\t\t\t\t\tvector unsigned int d) {\n\n    const vector unsigned int sl1 = ALTI_SL1;\n    const vector unsigned int sr1 = ALTI_SR1;\n#ifdef ONLY64\n    const vector unsigned int mask = ALTI_MSK64;\n    const vector unsigned char perm_sl = ALTI_SL2_PERM64;\n    const vector unsigned char perm_sr = ALTI_SR2_PERM64;\n#else\n    const vector unsigned int mask = ALTI_MSK;\n    const vector unsigned char perm_sl = ALTI_SL2_PERM;\n    const vector unsigned char perm_sr = ALTI_SR2_PERM;\n#endif\n    vector unsigned int v, w, x, y, z;\n    x = vec_perm(a, (vector unsigned int)perm_sl, perm_sl);\n    v = a;\n    y = vec_sr(b, sr1);\n    z = vec_perm(c, (vector unsigned int)perm_sr, perm_sr);\n    w = vec_sl(d, sl1);\n    z = vec_xor(z, w);\n    y = vec_and(y, mask);\n    v = vec_xor(v, x);\n    z = vec_xor(z, y);\n    z = vec_xor(z, v);\n    return z;\n}\n\n/**\n * This function fills the internal state array with pseudorandom\n * integers.\n */\nstatic inline void gen_rand_all(sfmt_t *ctx) {\n    int i;\n    vector unsigned int r, r1, r2;\n\n    r1 = ctx->sfmt[N - 2].s;\n    r2 = ctx->sfmt[N - 1].s;\n    for (i = 0; i < N - POS1; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, ctx->sfmt[i + POS1].s, r1, r2);\n\tctx->sfmt[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, ctx->sfmt[i + POS1 - N].s, r1, r2);\n\tctx->sfmt[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n/**\n * This function fills the user-specified array with pseudorandom\n * integers.\n *\n * @param array an 128-bit array to be filled by pseudorandom numbers.\n * @param size number of 128-bit pesudorandom numbers to be generated.\n */\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size) {\n    int i, j;\n    vector unsigned int r, r1, r2;\n\n    r1 = ctx->sfmt[N - 2].s;\n    r2 = ctx->sfmt[N - 1].s;\n    for (i = 0; i < N - POS1; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, ctx->sfmt[i + POS1].s, r1, r2);\n\tarray[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = vec_recursion(ctx->sfmt[i].s, array[i + POS1 - N].s, r1, r2);\n\tarray[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    /* main loop */\n    for (; i < size - N; i++) {\n\tr = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2);\n\tarray[i].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (j = 0; j < 2 * N - size; j++) {\n\tctx->sfmt[j].s = array[j + size - N].s;\n    }\n    for (; i < size; i++) {\n\tr = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2);\n\tarray[i].s = r;\n\tctx->sfmt[j++].s = r;\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n#ifndef ONLY64\n#if defined(__APPLE__)\n#define ALTI_SWAP (vector unsigned char) \\\n\t(4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11)\n#else\n#define ALTI_SWAP {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}\n#endif\n/**\n * This function swaps high and low 32-bit of 64-bit integers in user\n * specified array.\n *\n * @param array an 128-bit array to be swaped.\n * @param size size of 128-bit array.\n */\nstatic inline void swap(w128_t *array, int size) {\n    int i;\n    const vector unsigned char perm = ALTI_SWAP;\n\n    for (i = 0; i < size; i++) {\n\tarray[i].s = vec_perm(array[i].s, (vector unsigned int)perm, perm);\n    }\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS_H\n#define SFMT_PARAMS_H\n\n#if !defined(MEXP)\n#ifdef __GNUC__\n  #warning \"MEXP is not defined. I assume MEXP is 19937.\"\n#endif\n  #define MEXP 19937\n#endif\n/*-----------------\n  BASIC DEFINITIONS\n  -----------------*/\n/** Mersenne Exponent. The period of the sequence \n *  is a multiple of 2^MEXP-1.\n * #define MEXP 19937 */\n/** SFMT generator has an internal state array of 128-bit integers,\n * and N is its size. */\n#define N (MEXP / 128 + 1)\n/** N32 is the size of internal state array when regarded as an array\n * of 32-bit integers.*/\n#define N32 (N * 4)\n/** N64 is the size of internal state array when regarded as an array\n * of 64-bit integers.*/\n#define N64 (N * 2)\n\n/*----------------------\n  the parameters of SFMT\n  following definitions are in paramsXXXX.h file.\n  ----------------------*/\n/** the pick up position of the array.\n#define POS1 122 \n*/\n\n/** the parameter of shift left as four 32-bit registers.\n#define SL1 18\n */\n\n/** the parameter of shift left as one 128-bit register. \n * The 128-bit integer is shifted by (SL2 * 8) bits. \n#define SL2 1 \n*/\n\n/** the parameter of shift right as four 32-bit registers.\n#define SR1 11\n*/\n\n/** the parameter of shift right as one 128-bit register. \n * The 128-bit integer is shifted by (SL2 * 8) bits. \n#define SR2 1 \n*/\n\n/** A bitmask, used in the recursion.  These parameters are introduced\n * to break symmetry of SIMD.\n#define MSK1 0xdfffffefU\n#define MSK2 0xddfecb7fU\n#define MSK3 0xbffaffffU\n#define MSK4 0xbffffff6U \n*/\n\n/** These definitions are part of a 128-bit period certification vector.\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0xc98e126aU\n*/\n\n#if MEXP == 607\n  #include \"test/SFMT-params607.h\"\n#elif MEXP == 1279\n  #include \"test/SFMT-params1279.h\"\n#elif MEXP == 2281\n  #include \"test/SFMT-params2281.h\"\n#elif MEXP == 4253\n  #include \"test/SFMT-params4253.h\"\n#elif MEXP == 11213\n  #include \"test/SFMT-params11213.h\"\n#elif MEXP == 19937\n  #include \"test/SFMT-params19937.h\"\n#elif MEXP == 44497\n  #include \"test/SFMT-params44497.h\"\n#elif MEXP == 86243\n  #include \"test/SFMT-params86243.h\"\n#elif MEXP == 132049\n  #include \"test/SFMT-params132049.h\"\n#elif MEXP == 216091\n  #include \"test/SFMT-params216091.h\"\n#else\n#ifdef __GNUC__\n  #error \"MEXP is not valid.\"\n  #undef MEXP\n#else\n  #undef MEXP\n#endif\n\n#endif\n\n#endif /* SFMT_PARAMS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params11213.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS11213_H\n#define SFMT_PARAMS11213_H\n\n#define POS1\t68\n#define SL1\t14\n#define SL2\t3\n#define SR1\t7\n#define SR2\t3\n#define MSK1\t0xeffff7fbU\n#define MSK2\t0xffffffefU\n#define MSK3\t0xdfdfbfffU\n#define MSK4\t0x7fffdbfdU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0xe8148000U\n#define PARITY4\t0xd0c7afa3U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12}\n    #define ALTI_SR2_PERM64\t{13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-11213:68-14-3-7-3:effff7fb-ffffffef-dfdfbfff-7fffdbfd\"\n\n#endif /* SFMT_PARAMS11213_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params1279.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS1279_H\n#define SFMT_PARAMS1279_H\n\n#define POS1\t7\n#define SL1\t14\n#define SL2\t3\n#define SR1\t5\n#define SR2\t1\n#define MSK1\t0xf7fefffdU\n#define MSK2\t0x7fefcfffU\n#define MSK3\t0xaff3ef3fU\n#define MSK4\t0xb5ffff7fU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x20000000U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-1279:7-14-3-5-1:f7fefffd-7fefcfff-aff3ef3f-b5ffff7f\"\n\n#endif /* SFMT_PARAMS1279_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params132049.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS132049_H\n#define SFMT_PARAMS132049_H\n\n#define POS1\t110\n#define SL1\t19\n#define SL2\t1\n#define SR1\t21\n#define SR2\t1\n#define MSK1\t0xffffbb5fU\n#define MSK2\t0xfb6ebf95U\n#define MSK3\t0xfffefffaU\n#define MSK4\t0xcff77fffU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0xcb520000U\n#define PARITY4\t0xc7e91c7dU\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-132049:110-19-1-21-1:ffffbb5f-fb6ebf95-fffefffa-cff77fff\"\n\n#endif /* SFMT_PARAMS132049_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params19937.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS19937_H\n#define SFMT_PARAMS19937_H\n\n#define POS1\t122\n#define SL1\t18\n#define SL2\t1\n#define SR1\t11\n#define SR2\t1\n#define MSK1\t0xdfffffefU\n#define MSK2\t0xddfecb7fU\n#define MSK3\t0xbffaffffU\n#define MSK4\t0xbffffff6U\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x13c9e684U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-19937:122-18-1-11-1:dfffffef-ddfecb7f-bffaffff-bffffff6\"\n\n#endif /* SFMT_PARAMS19937_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params216091.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS216091_H\n#define SFMT_PARAMS216091_H\n\n#define POS1\t627\n#define SL1\t11\n#define SL2\t3\n#define SR1\t10\n#define SR2\t1\n#define MSK1\t0xbff7bff7U\n#define MSK2\t0xbfffffffU\n#define MSK3\t0xbffffa7fU\n#define MSK4\t0xffddfbfbU\n#define PARITY1\t0xf8000001U\n#define PARITY2\t0x89e80709U\n#define PARITY3\t0x3bd2b64bU\n#define PARITY4\t0x0c64b1e4U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-216091:627-11-3-10-1:bff7bff7-bfffffff-bffffa7f-ffddfbfb\"\n\n#endif /* SFMT_PARAMS216091_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params2281.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS2281_H\n#define SFMT_PARAMS2281_H\n\n#define POS1\t12\n#define SL1\t19\n#define SL2\t1\n#define SR1\t5\n#define SR2\t1\n#define MSK1\t0xbff7ffbfU\n#define MSK2\t0xfdfffffeU\n#define MSK3\t0xf7ffef7fU\n#define MSK4\t0xf2f7cbbfU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x41dfa600U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-2281:12-19-1-5-1:bff7ffbf-fdfffffe-f7ffef7f-f2f7cbbf\"\n\n#endif /* SFMT_PARAMS2281_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params4253.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS4253_H\n#define SFMT_PARAMS4253_H\n\n#define POS1\t17\n#define SL1\t20\n#define SL2\t1\n#define SR1\t7\n#define SR2\t1\n#define MSK1\t0x9f7bffffU\n#define MSK2\t0x9fffff5fU\n#define MSK3\t0x3efffffbU\n#define MSK4\t0xfffff7bbU\n#define PARITY1\t0xa8000001U\n#define PARITY2\t0xaf5390a3U\n#define PARITY3\t0xb740b3f8U\n#define PARITY4\t0x6c11486dU\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}\n    #define ALTI_SL2_PERM64\t{1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-4253:17-20-1-7-1:9f7bffff-9fffff5f-3efffffb-fffff7bb\"\n\n#endif /* SFMT_PARAMS4253_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params44497.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS44497_H\n#define SFMT_PARAMS44497_H\n\n#define POS1\t330\n#define SL1\t5\n#define SL2\t3\n#define SR1\t9\n#define SR2\t3\n#define MSK1\t0xeffffffbU\n#define MSK2\t0xdfbebfffU\n#define MSK3\t0xbfbf7befU\n#define MSK4\t0x9ffd7bffU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0xa3ac4000U\n#define PARITY4\t0xecc1327aU\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12}\n    #define ALTI_SR2_PERM64\t{13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-44497:330-5-3-9-3:effffffb-dfbebfff-bfbf7bef-9ffd7bff\"\n\n#endif /* SFMT_PARAMS44497_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params607.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS607_H\n#define SFMT_PARAMS607_H\n\n#define POS1\t2\n#define SL1\t15\n#define SL2\t3\n#define SR1\t13\n#define SR2\t3\n#define MSK1\t0xfdff37ffU\n#define MSK2\t0xef7f3f7dU\n#define MSK3\t0xff777b7dU\n#define MSK4\t0x7ff7fb2fU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0x5986f054U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10}\n    #define ALTI_SL2_PERM64\t{3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2}\n    #define ALTI_SR2_PERM\t{5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12}\n    #define ALTI_SR2_PERM64\t{13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-607:2-15-3-13-3:fdff37ff-ef7f3f7d-ff777b7d-7ff7fb2f\"\n\n#endif /* SFMT_PARAMS607_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-params86243.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#ifndef SFMT_PARAMS86243_H\n#define SFMT_PARAMS86243_H\n\n#define POS1\t366\n#define SL1\t6\n#define SL2\t7\n#define SR1\t19\n#define SR2\t1\n#define MSK1\t0xfdbffbffU\n#define MSK2\t0xbff7ff3fU\n#define MSK3\t0xfd77efffU\n#define MSK4\t0xbf9ff3ffU\n#define PARITY1\t0x00000001U\n#define PARITY2\t0x00000000U\n#define PARITY3\t0x00000000U\n#define PARITY4\t0xe9528d85U\n\n\n/* PARAMETERS FOR ALTIVEC */\n#if defined(__APPLE__)\t/* For OSX */\n    #define ALTI_SL1\t(vector unsigned int)(SL1, SL1, SL1, SL1)\n    #define ALTI_SR1\t(vector unsigned int)(SR1, SR1, SR1, SR1)\n    #define ALTI_MSK\t(vector unsigned int)(MSK1, MSK2, MSK3, MSK4)\n    #define ALTI_MSK64 \\\n\t(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)\n    #define ALTI_SL2_PERM \\\n\t(vector unsigned char)(25,25,25,25,3,25,25,25,7,0,1,2,11,4,5,6)\n    #define ALTI_SL2_PERM64 \\\n\t(vector unsigned char)(7,25,25,25,25,25,25,25,15,0,1,2,3,4,5,6)\n    #define ALTI_SR2_PERM \\\n\t(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)\n    #define ALTI_SR2_PERM64 \\\n\t(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)\n#else\t/* For OTHER OSs(Linux?) */\n    #define ALTI_SL1\t{SL1, SL1, SL1, SL1}\n    #define ALTI_SR1\t{SR1, SR1, SR1, SR1}\n    #define ALTI_MSK\t{MSK1, MSK2, MSK3, MSK4}\n    #define ALTI_MSK64\t{MSK2, MSK1, MSK4, MSK3}\n    #define ALTI_SL2_PERM\t{25,25,25,25,3,25,25,25,7,0,1,2,11,4,5,6}\n    #define ALTI_SL2_PERM64\t{7,25,25,25,25,25,25,25,15,0,1,2,3,4,5,6}\n    #define ALTI_SR2_PERM\t{7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}\n    #define ALTI_SR2_PERM64\t{15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}\n#endif\t/* For OSX */\n#define IDSTR\t\"SFMT-86243:366-6-7-19-1:fdbffbff-bff7ff3f-fd77efff-bf9ff3ff\"\n\n#endif /* SFMT_PARAMS86243_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT-sse2.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file  SFMT-sse2.h\n * @brief SIMD oriented Fast Mersenne Twister(SFMT) for Intel SSE2\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * @note We assume LITTLE ENDIAN in this file\n *\n * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software, see LICENSE.txt\n */\n\n#ifndef SFMT_SSE2_H\n#define SFMT_SSE2_H\n\n/**\n * This function represents the recursion formula.\n * @param a a 128-bit part of the interal state array\n * @param b a 128-bit part of the interal state array\n * @param c a 128-bit part of the interal state array\n * @param d a 128-bit part of the interal state array\n * @param mask 128-bit mask\n * @return output\n */\nJEMALLOC_ALWAYS_INLINE __m128i mm_recursion(__m128i *a, __m128i *b,\n\t\t\t\t   __m128i c, __m128i d, __m128i mask) {\n    __m128i v, x, y, z;\n\n    x = _mm_load_si128(a);\n    y = _mm_srli_epi32(*b, SR1);\n    z = _mm_srli_si128(c, SR2);\n    v = _mm_slli_epi32(d, SL1);\n    z = _mm_xor_si128(z, x);\n    z = _mm_xor_si128(z, v);\n    x = _mm_slli_si128(x, SL2);\n    y = _mm_and_si128(y, mask);\n    z = _mm_xor_si128(z, x);\n    z = _mm_xor_si128(z, y);\n    return z;\n}\n\n/**\n * This function fills the internal state array with pseudorandom\n * integers.\n */\nstatic inline void gen_rand_all(sfmt_t *ctx) {\n    int i;\n    __m128i r, r1, r2, mask;\n    mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1);\n\n    r1 = _mm_load_si128(&ctx->sfmt[N - 2].si);\n    r2 = _mm_load_si128(&ctx->sfmt[N - 1].si);\n    for (i = 0; i < N - POS1; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &ctx->sfmt[i + POS1].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&ctx->sfmt[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &ctx->sfmt[i + POS1 - N].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&ctx->sfmt[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n/**\n * This function fills the user-specified array with pseudorandom\n * integers.\n *\n * @param array an 128-bit array to be filled by pseudorandom numbers.\n * @param size number of 128-bit pesudorandom numbers to be generated.\n */\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size) {\n    int i, j;\n    __m128i r, r1, r2, mask;\n    mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1);\n\n    r1 = _mm_load_si128(&ctx->sfmt[N - 2].si);\n    r2 = _mm_load_si128(&ctx->sfmt[N - 1].si);\n    for (i = 0; i < N - POS1; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &ctx->sfmt[i + POS1].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&array[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (; i < N; i++) {\n\tr = mm_recursion(&ctx->sfmt[i].si, &array[i + POS1 - N].si, r1, r2,\n\t  mask);\n\t_mm_store_si128(&array[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    /* main loop */\n    for (; i < size - N; i++) {\n\tr = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2,\n\t\t\t mask);\n\t_mm_store_si128(&array[i].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n    for (j = 0; j < 2 * N - size; j++) {\n\tr = _mm_load_si128(&array[j + size - N].si);\n\t_mm_store_si128(&ctx->sfmt[j].si, r);\n    }\n    for (; i < size; i++) {\n\tr = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2,\n\t\t\t mask);\n\t_mm_store_si128(&array[i].si, r);\n\t_mm_store_si128(&ctx->sfmt[j++].si, r);\n\tr1 = r2;\n\tr2 = r;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/SFMT.h",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file SFMT.h \n *\n * @brief SIMD oriented Fast Mersenne Twister(SFMT) pseudorandom\n * number generator\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software.\n * see LICENSE.txt\n *\n * @note We assume that your system has inttypes.h.  If your system\n * doesn't have inttypes.h, you have to typedef uint32_t and uint64_t,\n * and you have to define PRIu64 and PRIx64 in this file as follows:\n * @verbatim\n typedef unsigned int uint32_t\n typedef unsigned long long uint64_t  \n #define PRIu64 \"llu\"\n #define PRIx64 \"llx\"\n@endverbatim\n * uint32_t must be exactly 32-bit unsigned integer type (no more, no\n * less), and uint64_t must be exactly 64-bit unsigned integer type.\n * PRIu64 and PRIx64 are used for printf function to print 64-bit\n * unsigned int and 64-bit unsigned int in hexadecimal format.\n */\n\n#ifndef SFMT_H\n#define SFMT_H\n\ntypedef struct sfmt_s sfmt_t;\n\nuint32_t gen_rand32(sfmt_t *ctx);\nuint32_t gen_rand32_range(sfmt_t *ctx, uint32_t limit);\nuint64_t gen_rand64(sfmt_t *ctx);\nuint64_t gen_rand64_range(sfmt_t *ctx, uint64_t limit);\nvoid fill_array32(sfmt_t *ctx, uint32_t *array, int size);\nvoid fill_array64(sfmt_t *ctx, uint64_t *array, int size);\nsfmt_t *init_gen_rand(uint32_t seed);\nsfmt_t *init_by_array(uint32_t *init_key, int key_length);\nvoid fini_gen_rand(sfmt_t *ctx);\nconst char *get_idstring(void);\nint get_min_array_size32(void);\nint get_min_array_size64(void);\n\n/* These real versions are due to Isaku Wada */\n/** generates a random number on [0,1]-real-interval */\nstatic inline double to_real1(uint32_t v) {\n    return v * (1.0/4294967295.0); \n    /* divided by 2^32-1 */ \n}\n\n/** generates a random number on [0,1]-real-interval */\nstatic inline double genrand_real1(sfmt_t *ctx) {\n    return to_real1(gen_rand32(ctx));\n}\n\n/** generates a random number on [0,1)-real-interval */\nstatic inline double to_real2(uint32_t v) {\n    return v * (1.0/4294967296.0); \n    /* divided by 2^32 */\n}\n\n/** generates a random number on [0,1)-real-interval */\nstatic inline double genrand_real2(sfmt_t *ctx) {\n    return to_real2(gen_rand32(ctx));\n}\n\n/** generates a random number on (0,1)-real-interval */\nstatic inline double to_real3(uint32_t v) {\n    return (((double)v) + 0.5)*(1.0/4294967296.0); \n    /* divided by 2^32 */\n}\n\n/** generates a random number on (0,1)-real-interval */\nstatic inline double genrand_real3(sfmt_t *ctx) {\n    return to_real3(gen_rand32(ctx));\n}\n/** These real versions are due to Isaku Wada */\n\n/** generates a random number on [0,1) with 53-bit resolution*/\nstatic inline double to_res53(uint64_t v) {\n    return v * (1.0/18446744073709551616.0L);\n}\n\n/** generates a random number on [0,1) with 53-bit resolution from two\n * 32 bit integers */\nstatic inline double to_res53_mix(uint32_t x, uint32_t y) {\n    return to_res53(x | ((uint64_t)y << 32));\n}\n\n/** generates a random number on [0,1) with 53-bit resolution\n */\nstatic inline double genrand_res53(sfmt_t *ctx) {\n    return to_res53(gen_rand64(ctx));\n}\n\n/** generates a random number on [0,1) with 53-bit resolution\n    using 32bit integer.\n */\nstatic inline double genrand_res53_mix(sfmt_t *ctx) {\n    uint32_t x, y;\n\n    x = gen_rand32(ctx);\n    y = gen_rand32(ctx);\n    return to_res53_mix(x, y);\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/btalloc.h",
    "content": "/* btalloc() provides a mechanism for allocating via permuted backtraces. */\nvoid\t*btalloc(size_t size, unsigned bits);\n\n#define btalloc_n_proto(n)\t\t\t\t\t\t\\\nvoid\t*btalloc_##n(size_t size, unsigned bits);\nbtalloc_n_proto(0)\nbtalloc_n_proto(1)\n\n#define btalloc_n_gen(n)\t\t\t\t\t\t\\\nvoid *\t\t\t\t\t\t\t\t\t\\\nbtalloc_##n(size_t size, unsigned bits) {\t\t\t\t\\\n\tvoid *p;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (bits == 0) {\t\t\t\t\t\t\\\n\t\tp = mallocx(size, 0);\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tswitch (bits & 0x1U) {\t\t\t\t\t\\\n\t\tcase 0:\t\t\t\t\t\t\t\\\n\t\t\tp = (btalloc_0(size, bits >> 1));\t\t\\\n\t\t\tbreak;\t\t\t\t\t\t\\\n\t\tcase 1:\t\t\t\t\t\t\t\\\n\t\t\tp = (btalloc_1(size, bits >> 1));\t\t\\\n\t\t\tbreak;\t\t\t\t\t\t\\\n\t\tdefault: not_reached();\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/* Intentionally sabotage tail call optimization. */\t\t\\\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\t\t\\\n\treturn p;\t\t\t\t\t\t\t\\\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/extent_hooks.h",
    "content": "/*\n * Boilerplate code used for testing extent hooks via interception and\n * passthrough.\n */\n\nstatic void\t*extent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr,\n    size_t size, size_t alignment, bool *zero, bool *commit,\n    unsigned arena_ind);\nstatic bool\textent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic void\textent_destroy_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, bool committed, unsigned arena_ind);\nstatic bool\textent_commit_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_decommit_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_purge_forced_hook(extent_hooks_t *extent_hooks,\n    void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);\nstatic bool\textent_split_hook(extent_hooks_t *extent_hooks, void *addr,\n    size_t size, size_t size_a, size_t size_b, bool committed,\n    unsigned arena_ind);\nstatic bool\textent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a,\n    size_t size_a, void *addr_b, size_t size_b, bool committed,\n    unsigned arena_ind);\n\nstatic extent_hooks_t *default_hooks;\nstatic extent_hooks_t hooks = {\n\textent_alloc_hook,\n\textent_dalloc_hook,\n\textent_destroy_hook,\n\textent_commit_hook,\n\textent_decommit_hook,\n\textent_purge_lazy_hook,\n\textent_purge_forced_hook,\n\textent_split_hook,\n\textent_merge_hook\n};\n\n/* Control whether hook functions pass calls through to default hooks. */\nstatic bool try_alloc = true;\nstatic bool try_dalloc = true;\nstatic bool try_destroy = true;\nstatic bool try_commit = true;\nstatic bool try_decommit = true;\nstatic bool try_purge_lazy = true;\nstatic bool try_purge_forced = true;\nstatic bool try_split = true;\nstatic bool try_merge = true;\n\n/* Set to false prior to operations, then introspect after operations. */\nstatic bool called_alloc;\nstatic bool called_dalloc;\nstatic bool called_destroy;\nstatic bool called_commit;\nstatic bool called_decommit;\nstatic bool called_purge_lazy;\nstatic bool called_purge_forced;\nstatic bool called_split;\nstatic bool called_merge;\n\n/* Set to false prior to operations, then introspect after operations. */\nstatic bool did_alloc;\nstatic bool did_dalloc;\nstatic bool did_destroy;\nstatic bool did_commit;\nstatic bool did_decommit;\nstatic bool did_purge_lazy;\nstatic bool did_purge_forced;\nstatic bool did_split;\nstatic bool did_merge;\n\n#if 0\n#  define TRACE_HOOK(fmt, ...) malloc_printf(fmt, __VA_ARGS__)\n#else\n#  define TRACE_HOOK(fmt, ...)\n#endif\n\nstatic void *\nextent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr, size_t size,\n    size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {\n\tvoid *ret;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, new_addr=%p, size=%zu, alignment=%zu, \"\n\t    \"*zero=%s, *commit=%s, arena_ind=%u)\\n\", __func__, extent_hooks,\n\t    new_addr, size, alignment, *zero ?  \"true\" : \"false\", *commit ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->alloc, extent_alloc_hook,\n\t    \"Wrong hook function\");\n\tcalled_alloc = true;\n\tif (!try_alloc) {\n\t\treturn NULL;\n\t}\n\tret = default_hooks->alloc(default_hooks, new_addr, size, alignment,\n\t    zero, commit, 0);\n\tdid_alloc = (ret != NULL);\n\treturn ret;\n}\n\nstatic bool\nextent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, \"\n\t    \"arena_ind=%u)\\n\", __func__, extent_hooks, addr, size, committed ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->dalloc, extent_dalloc_hook,\n\t    \"Wrong hook function\");\n\tcalled_dalloc = true;\n\tif (!try_dalloc) {\n\t\treturn true;\n\t}\n\terr = default_hooks->dalloc(default_hooks, addr, size, committed, 0);\n\tdid_dalloc = !err;\n\treturn err;\n}\n\nstatic void\nextent_destroy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, \"\n\t    \"arena_ind=%u)\\n\", __func__, extent_hooks, addr, size, committed ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->destroy, extent_destroy_hook,\n\t    \"Wrong hook function\");\n\tcalled_destroy = true;\n\tif (!try_destroy) {\n\t\treturn;\n\t}\n\tdefault_hooks->destroy(default_hooks, addr, size, committed, 0);\n\tdid_destroy = true;\n}\n\nstatic bool\nextent_commit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu, arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->commit, extent_commit_hook,\n\t    \"Wrong hook function\");\n\tcalled_commit = true;\n\tif (!try_commit) {\n\t\treturn true;\n\t}\n\terr = default_hooks->commit(default_hooks, addr, size, offset, length,\n\t    0);\n\tdid_commit = !err;\n\treturn err;\n}\n\nstatic bool\nextent_decommit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu, arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->decommit, extent_decommit_hook,\n\t    \"Wrong hook function\");\n\tcalled_decommit = true;\n\tif (!try_decommit) {\n\t\treturn true;\n\t}\n\terr = default_hooks->decommit(default_hooks, addr, size, offset, length,\n\t    0);\n\tdid_decommit = !err;\n\treturn err;\n}\n\nstatic bool\nextent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->purge_lazy, extent_purge_lazy_hook,\n\t    \"Wrong hook function\");\n\tcalled_purge_lazy = true;\n\tif (!try_purge_lazy) {\n\t\treturn true;\n\t}\n\terr = default_hooks->purge_lazy == NULL ||\n\t    default_hooks->purge_lazy(default_hooks, addr, size, offset, length,\n\t    0);\n\tdid_purge_lazy = !err;\n\treturn err;\n}\n\nstatic bool\nextent_purge_forced_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t offset, size_t length, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, \"\n\t    \"length=%zu arena_ind=%u)\\n\", __func__, extent_hooks, addr, size,\n\t    offset, length, arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->purge_forced, extent_purge_forced_hook,\n\t    \"Wrong hook function\");\n\tcalled_purge_forced = true;\n\tif (!try_purge_forced) {\n\t\treturn true;\n\t}\n\terr = default_hooks->purge_forced == NULL ||\n\t    default_hooks->purge_forced(default_hooks, addr, size, offset,\n\t    length, 0);\n\tdid_purge_forced = !err;\n\treturn err;\n}\n\nstatic bool\nextent_split_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, size_a=%zu, \"\n\t    \"size_b=%zu, committed=%s, arena_ind=%u)\\n\", __func__, extent_hooks,\n\t    addr, size, size_a, size_b, committed ? \"true\" : \"false\",\n\t    arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->split, extent_split_hook,\n\t    \"Wrong hook function\");\n\tcalled_split = true;\n\tif (!try_split) {\n\t\treturn true;\n\t}\n\terr = (default_hooks->split == NULL ||\n\t    default_hooks->split(default_hooks, addr, size, size_a, size_b,\n\t    committed, 0));\n\tdid_split = !err;\n\treturn err;\n}\n\nstatic bool\nextent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,\n    void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {\n\tbool err;\n\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr_a=%p, size_a=%zu, addr_b=%p \"\n\t    \"size_b=%zu, committed=%s, arena_ind=%u)\\n\", __func__, extent_hooks,\n\t    addr_a, size_a, addr_b, size_b, committed ? \"true\" : \"false\",\n\t    arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->merge, extent_merge_hook,\n\t    \"Wrong hook function\");\n\tassert_ptr_eq((void *)((uintptr_t)addr_a + size_a), addr_b,\n\t    \"Extents not mergeable\");\n\tcalled_merge = true;\n\tif (!try_merge) {\n\t\treturn true;\n\t}\n\terr = (default_hooks->merge == NULL ||\n\t    default_hooks->merge(default_hooks, addr_a, size_a, addr_b, size_b,\n\t    committed, 0));\n\tdid_merge = !err;\n\treturn err;\n}\n\nstatic void\nextent_hooks_prep(void) {\n\tsize_t sz;\n\n\tsz = sizeof(default_hooks);\n\tassert_d_eq(mallctl(\"arena.0.extent_hooks\", (void *)&default_hooks, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() error\");\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/jemalloc_test.h.in",
    "content": "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <limits.h>\n#ifndef SIZE_T_MAX\n#  define SIZE_T_MAX\tSIZE_MAX\n#endif\n#include <stdlib.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <errno.h>\n#include <math.h>\n#include <string.h>\n#ifdef _WIN32\n#  include \"msvc_compat/strings.h\"\n#endif\n\n#ifdef _WIN32\n#  include <windows.h>\n#  include \"msvc_compat/windows_extra.h\"\n#else\n#  include <pthread.h>\n#endif\n\n#include \"test/jemalloc_test_defs.h\"\n\n#ifdef JEMALLOC_OSSPIN\n#  include <libkern/OSAtomic.h>\n#endif\n\n#if defined(HAVE_ALTIVEC) && !defined(__APPLE__)\n#  include <altivec.h>\n#endif\n#ifdef HAVE_SSE2\n#  include <emmintrin.h>\n#endif\n\n/******************************************************************************/\n/*\n * For unit tests, expose all public and private interfaces.\n */\n#ifdef JEMALLOC_UNIT_TEST\n#  define JEMALLOC_JET\n#  define JEMALLOC_MANGLE\n#  include \"jemalloc/internal/jemalloc_preamble.h\"\n#  include \"jemalloc/internal/jemalloc_internal_includes.h\"\n\n/******************************************************************************/\n/*\n * For integration tests, expose the public jemalloc interfaces, but only\n * expose the minimum necessary internal utility code (to avoid re-implementing\n * essentially identical code within the test infrastructure).\n */\n#elif defined(JEMALLOC_INTEGRATION_TEST) || \\\n    defined(JEMALLOC_INTEGRATION_CPP_TEST)\n#  define JEMALLOC_MANGLE\n#  include \"jemalloc/jemalloc@install_suffix@.h\"\n#  include \"jemalloc/internal/jemalloc_internal_defs.h\"\n#  include \"jemalloc/internal/jemalloc_internal_macros.h\"\n\nstatic const bool config_debug =\n#ifdef JEMALLOC_DEBUG\n    true\n#else\n    false\n#endif\n    ;\n\n#  define JEMALLOC_N(n) @private_namespace@##n\n#  include \"jemalloc/internal/private_namespace.h\"\n#  include \"jemalloc/internal/hooks.h\"\n\n/* Hermetic headers. */\n#  include \"jemalloc/internal/assert.h\"\n#  include \"jemalloc/internal/malloc_io.h\"\n#  include \"jemalloc/internal/nstime.h\"\n#  include \"jemalloc/internal/util.h\"\n\n/* Non-hermetic headers. */\n#  include \"jemalloc/internal/qr.h\"\n#  include \"jemalloc/internal/ql.h\"\n\n/******************************************************************************/\n/*\n * For stress tests, expose the public jemalloc interfaces with name mangling\n * so that they can be tested as e.g. malloc() and free().  Also expose the\n * public jemalloc interfaces with jet_ prefixes, so that stress tests can use\n * a separate allocator for their internal data structures.\n */\n#elif defined(JEMALLOC_STRESS_TEST)\n#  include \"jemalloc/jemalloc@install_suffix@.h\"\n\n#  include \"jemalloc/jemalloc_protos_jet.h\"\n\n#  define JEMALLOC_JET\n#  include \"jemalloc/internal/jemalloc_preamble.h\"\n#  include \"jemalloc/internal/jemalloc_internal_includes.h\"\n#  include \"jemalloc/internal/public_unnamespace.h\"\n#  undef JEMALLOC_JET\n\n#  include \"jemalloc/jemalloc_rename.h\"\n#  define JEMALLOC_MANGLE\n#  ifdef JEMALLOC_STRESS_TESTLIB\n#    include \"jemalloc/jemalloc_mangle_jet.h\"\n#  else\n#    include \"jemalloc/jemalloc_mangle.h\"\n#  endif\n\n/******************************************************************************/\n/*\n * This header does dangerous things, the effects of which only test code\n * should be subject to.\n */\n#else\n#  error \"This header cannot be included outside a testing context\"\n#endif\n\n/******************************************************************************/\n/*\n * Common test utilities.\n */\n#include \"test/btalloc.h\"\n#include \"test/math.h\"\n#include \"test/mtx.h\"\n#include \"test/mq.h\"\n#include \"test/test.h\"\n#include \"test/timer.h\"\n#include \"test/thd.h\"\n#define MEXP 19937\n#include \"test/SFMT.h\"\n\n/******************************************************************************/\n/*\n * Define always-enabled assertion macros, so that test assertions execute even\n * if assertions are disabled in the library code.\n */\n#undef assert\n#undef not_reached\n#undef not_implemented\n#undef assert_not_implemented\n\n#define assert(e) do {\t\t\t\t\t\t\t\\\n\tif (!(e)) {\t\t\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\n#define not_reached() do {\t\t\t\t\t\t\\\n\tmalloc_printf(\t\t\t\t\t\t\t\\\n\t    \"<jemalloc>: %s:%d: Unreachable code reached\\n\",\t\t\\\n\t    __FILE__, __LINE__);\t\t\t\t\t\\\n\tabort();\t\t\t\t\t\t\t\\\n} while (0)\n\n#define not_implemented() do {\t\t\t\t\t\t\\\n\tmalloc_printf(\"<jemalloc>: %s:%d: Not implemented\\n\",\t\t\\\n\t    __FILE__, __LINE__);\t\t\t\t\t\\\n\tabort();\t\t\t\t\t\t\t\\\n} while (0)\n\n#define assert_not_implemented(e) do {\t\t\t\t\t\\\n\tif (!(e)) {\t\t\t\t\t\t\t\\\n\t\tnot_implemented();\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/jemalloc_test_defs.h.in",
    "content": "#include \"jemalloc/internal/jemalloc_internal_defs.h\"\n#include \"jemalloc/internal/jemalloc_internal_decls.h\"\n\n/*\n * For use by SFMT.  configure.ac doesn't actually define HAVE_SSE2 because its\n * dependencies are notoriously unportable in practice.\n */\n#undef HAVE_SSE2\n#undef HAVE_ALTIVEC\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/math.h",
    "content": "/*\n * Compute the natural log of Gamma(x), accurate to 10 decimal places.\n *\n * This implementation is based on:\n *\n *   Pike, M.C., I.D. Hill (1966) Algorithm 291: Logarithm of Gamma function\n *   [S14].  Communications of the ACM 9(9):684.\n */\nstatic inline double\nln_gamma(double x) {\n\tdouble f, z;\n\n\tassert(x > 0.0);\n\n\tif (x < 7.0) {\n\t\tf = 1.0;\n\t\tz = x;\n\t\twhile (z < 7.0) {\n\t\t\tf *= z;\n\t\t\tz += 1.0;\n\t\t}\n\t\tx = z;\n\t\tf = -log(f);\n\t} else {\n\t\tf = 0.0;\n\t}\n\n\tz = 1.0 / (x * x);\n\n\treturn f + (x-0.5) * log(x) - x + 0.918938533204673 +\n\t    (((-0.000595238095238 * z + 0.000793650793651) * z -\n\t    0.002777777777778) * z + 0.083333333333333) / x;\n}\n\n/*\n * Compute the incomplete Gamma ratio for [0..x], where p is the shape\n * parameter, and ln_gamma_p is ln_gamma(p).\n *\n * This implementation is based on:\n *\n *   Bhattacharjee, G.P. (1970) Algorithm AS 32: The incomplete Gamma integral.\n *   Applied Statistics 19:285-287.\n */\nstatic inline double\ni_gamma(double x, double p, double ln_gamma_p) {\n\tdouble acu, factor, oflo, gin, term, rn, a, b, an, dif;\n\tdouble pn[6];\n\tunsigned i;\n\n\tassert(p > 0.0);\n\tassert(x >= 0.0);\n\n\tif (x == 0.0) {\n\t\treturn 0.0;\n\t}\n\n\tacu = 1.0e-10;\n\toflo = 1.0e30;\n\tgin = 0.0;\n\tfactor = exp(p * log(x) - x - ln_gamma_p);\n\n\tif (x <= 1.0 || x < p) {\n\t\t/* Calculation by series expansion. */\n\t\tgin = 1.0;\n\t\tterm = 1.0;\n\t\trn = p;\n\n\t\twhile (true) {\n\t\t\trn += 1.0;\n\t\t\tterm *= x / rn;\n\t\t\tgin += term;\n\t\t\tif (term <= acu) {\n\t\t\t\tgin *= factor / p;\n\t\t\t\treturn gin;\n\t\t\t}\n\t\t}\n\t} else {\n\t\t/* Calculation by continued fraction. */\n\t\ta = 1.0 - p;\n\t\tb = a + x + 1.0;\n\t\tterm = 0.0;\n\t\tpn[0] = 1.0;\n\t\tpn[1] = x;\n\t\tpn[2] = x + 1.0;\n\t\tpn[3] = x * b;\n\t\tgin = pn[2] / pn[3];\n\n\t\twhile (true) {\n\t\t\ta += 1.0;\n\t\t\tb += 2.0;\n\t\t\tterm += 1.0;\n\t\t\tan = a * term;\n\t\t\tfor (i = 0; i < 2; i++) {\n\t\t\t\tpn[i+4] = b * pn[i+2] - an * pn[i];\n\t\t\t}\n\t\t\tif (pn[5] != 0.0) {\n\t\t\t\trn = pn[4] / pn[5];\n\t\t\t\tdif = fabs(gin - rn);\n\t\t\t\tif (dif <= acu && dif <= acu * rn) {\n\t\t\t\t\tgin = 1.0 - factor * gin;\n\t\t\t\t\treturn gin;\n\t\t\t\t}\n\t\t\t\tgin = rn;\n\t\t\t}\n\t\t\tfor (i = 0; i < 4; i++) {\n\t\t\t\tpn[i] = pn[i+2];\n\t\t\t}\n\n\t\t\tif (fabs(pn[4]) >= oflo) {\n\t\t\t\tfor (i = 0; i < 4; i++) {\n\t\t\t\t\tpn[i] /= oflo;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n * Given a value p in [0..1] of the lower tail area of the normal distribution,\n * compute the limit on the definite integral from [-inf..z] that satisfies p,\n * accurate to 16 decimal places.\n *\n * This implementation is based on:\n *\n *   Wichura, M.J. (1988) Algorithm AS 241: The percentage points of the normal\n *   distribution.  Applied Statistics 37(3):477-484.\n */\nstatic inline double\npt_norm(double p) {\n\tdouble q, r, ret;\n\n\tassert(p > 0.0 && p < 1.0);\n\n\tq = p - 0.5;\n\tif (fabs(q) <= 0.425) {\n\t\t/* p close to 1/2. */\n\t\tr = 0.180625 - q * q;\n\t\treturn q * (((((((2.5090809287301226727e3 * r +\n\t\t    3.3430575583588128105e4) * r + 6.7265770927008700853e4) * r\n\t\t    + 4.5921953931549871457e4) * r + 1.3731693765509461125e4) *\n\t\t    r + 1.9715909503065514427e3) * r + 1.3314166789178437745e2)\n\t\t    * r + 3.3871328727963666080e0) /\n\t\t    (((((((5.2264952788528545610e3 * r +\n\t\t    2.8729085735721942674e4) * r + 3.9307895800092710610e4) * r\n\t\t    + 2.1213794301586595867e4) * r + 5.3941960214247511077e3) *\n\t\t    r + 6.8718700749205790830e2) * r + 4.2313330701600911252e1)\n\t\t    * r + 1.0);\n\t} else {\n\t\tif (q < 0.0) {\n\t\t\tr = p;\n\t\t} else {\n\t\t\tr = 1.0 - p;\n\t\t}\n\t\tassert(r > 0.0);\n\n\t\tr = sqrt(-log(r));\n\t\tif (r <= 5.0) {\n\t\t\t/* p neither close to 1/2 nor 0 or 1. */\n\t\t\tr -= 1.6;\n\t\t\tret = ((((((((7.74545014278341407640e-4 * r +\n\t\t\t    2.27238449892691845833e-2) * r +\n\t\t\t    2.41780725177450611770e-1) * r +\n\t\t\t    1.27045825245236838258e0) * r +\n\t\t\t    3.64784832476320460504e0) * r +\n\t\t\t    5.76949722146069140550e0) * r +\n\t\t\t    4.63033784615654529590e0) * r +\n\t\t\t    1.42343711074968357734e0) /\n\t\t\t    (((((((1.05075007164441684324e-9 * r +\n\t\t\t    5.47593808499534494600e-4) * r +\n\t\t\t    1.51986665636164571966e-2)\n\t\t\t    * r + 1.48103976427480074590e-1) * r +\n\t\t\t    6.89767334985100004550e-1) * r +\n\t\t\t    1.67638483018380384940e0) * r +\n\t\t\t    2.05319162663775882187e0) * r + 1.0));\n\t\t} else {\n\t\t\t/* p near 0 or 1. */\n\t\t\tr -= 5.0;\n\t\t\tret = ((((((((2.01033439929228813265e-7 * r +\n\t\t\t    2.71155556874348757815e-5) * r +\n\t\t\t    1.24266094738807843860e-3) * r +\n\t\t\t    2.65321895265761230930e-2) * r +\n\t\t\t    2.96560571828504891230e-1) * r +\n\t\t\t    1.78482653991729133580e0) * r +\n\t\t\t    5.46378491116411436990e0) * r +\n\t\t\t    6.65790464350110377720e0) /\n\t\t\t    (((((((2.04426310338993978564e-15 * r +\n\t\t\t    1.42151175831644588870e-7) * r +\n\t\t\t    1.84631831751005468180e-5) * r +\n\t\t\t    7.86869131145613259100e-4) * r +\n\t\t\t    1.48753612908506148525e-2) * r +\n\t\t\t    1.36929880922735805310e-1) * r +\n\t\t\t    5.99832206555887937690e-1)\n\t\t\t    * r + 1.0));\n\t\t}\n\t\tif (q < 0.0) {\n\t\t\tret = -ret;\n\t\t}\n\t\treturn ret;\n\t}\n}\n\n/*\n * Given a value p in [0..1] of the lower tail area of the Chi^2 distribution\n * with df degrees of freedom, where ln_gamma_df_2 is ln_gamma(df/2.0), compute\n * the upper limit on the definite integral from [0..z] that satisfies p,\n * accurate to 12 decimal places.\n *\n * This implementation is based on:\n *\n *   Best, D.J., D.E. Roberts (1975) Algorithm AS 91: The percentage points of\n *   the Chi^2 distribution.  Applied Statistics 24(3):385-388.\n *\n *   Shea, B.L. (1991) Algorithm AS R85: A remark on AS 91: The percentage\n *   points of the Chi^2 distribution.  Applied Statistics 40(1):233-235.\n */\nstatic inline double\npt_chi2(double p, double df, double ln_gamma_df_2) {\n\tdouble e, aa, xx, c, ch, a, q, p1, p2, t, x, b, s1, s2, s3, s4, s5, s6;\n\tunsigned i;\n\n\tassert(p >= 0.0 && p < 1.0);\n\tassert(df > 0.0);\n\n\te = 5.0e-7;\n\taa = 0.6931471805;\n\n\txx = 0.5 * df;\n\tc = xx - 1.0;\n\n\tif (df < -1.24 * log(p)) {\n\t\t/* Starting approximation for small Chi^2. */\n\t\tch = pow(p * xx * exp(ln_gamma_df_2 + xx * aa), 1.0 / xx);\n\t\tif (ch - e < 0.0) {\n\t\t\treturn ch;\n\t\t}\n\t} else {\n\t\tif (df > 0.32) {\n\t\t\tx = pt_norm(p);\n\t\t\t/*\n\t\t\t * Starting approximation using Wilson and Hilferty\n\t\t\t * estimate.\n\t\t\t */\n\t\t\tp1 = 0.222222 / df;\n\t\t\tch = df * pow(x * sqrt(p1) + 1.0 - p1, 3.0);\n\t\t\t/* Starting approximation for p tending to 1. */\n\t\t\tif (ch > 2.2 * df + 6.0) {\n\t\t\t\tch = -2.0 * (log(1.0 - p) - c * log(0.5 * ch) +\n\t\t\t\t    ln_gamma_df_2);\n\t\t\t}\n\t\t} else {\n\t\t\tch = 0.4;\n\t\t\ta = log(1.0 - p);\n\t\t\twhile (true) {\n\t\t\t\tq = ch;\n\t\t\t\tp1 = 1.0 + ch * (4.67 + ch);\n\t\t\t\tp2 = ch * (6.73 + ch * (6.66 + ch));\n\t\t\t\tt = -0.5 + (4.67 + 2.0 * ch) / p1 - (6.73 + ch\n\t\t\t\t    * (13.32 + 3.0 * ch)) / p2;\n\t\t\t\tch -= (1.0 - exp(a + ln_gamma_df_2 + 0.5 * ch +\n\t\t\t\t    c * aa) * p2 / p1) / t;\n\t\t\t\tif (fabs(q / ch - 1.0) - 0.01 <= 0.0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0; i < 20; i++) {\n\t\t/* Calculation of seven-term Taylor series. */\n\t\tq = ch;\n\t\tp1 = 0.5 * ch;\n\t\tif (p1 < 0.0) {\n\t\t\treturn -1.0;\n\t\t}\n\t\tp2 = p - i_gamma(p1, xx, ln_gamma_df_2);\n\t\tt = p2 * exp(xx * aa + ln_gamma_df_2 + p1 - c * log(ch));\n\t\tb = t / ch;\n\t\ta = 0.5 * t - b * c;\n\t\ts1 = (210.0 + a * (140.0 + a * (105.0 + a * (84.0 + a * (70.0 +\n\t\t    60.0 * a))))) / 420.0;\n\t\ts2 = (420.0 + a * (735.0 + a * (966.0 + a * (1141.0 + 1278.0 *\n\t\t    a)))) / 2520.0;\n\t\ts3 = (210.0 + a * (462.0 + a * (707.0 + 932.0 * a))) / 2520.0;\n\t\ts4 = (252.0 + a * (672.0 + 1182.0 * a) + c * (294.0 + a *\n\t\t    (889.0 + 1740.0 * a))) / 5040.0;\n\t\ts5 = (84.0 + 264.0 * a + c * (175.0 + 606.0 * a)) / 2520.0;\n\t\ts6 = (120.0 + c * (346.0 + 127.0 * c)) / 5040.0;\n\t\tch += t * (1.0 + 0.5 * t * s1 - b * c * (s1 - b * (s2 - b * (s3\n\t\t    - b * (s4 - b * (s5 - b * s6))))));\n\t\tif (fabs(q / ch - 1.0) <= e) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn ch;\n}\n\n/*\n * Given a value p in [0..1] and Gamma distribution shape and scale parameters,\n * compute the upper limit on the definite integral from [0..z] that satisfies\n * p.\n */\nstatic inline double\npt_gamma(double p, double shape, double scale, double ln_gamma_shape) {\n\treturn pt_chi2(p, shape * 2.0, ln_gamma_shape) * 0.5 * scale;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/mq.h",
    "content": "void\tmq_nanosleep(unsigned ns);\n\n/*\n * Simple templated message queue implementation that relies on only mutexes for\n * synchronization (which reduces portability issues).  Given the following\n * setup:\n *\n *   typedef struct mq_msg_s mq_msg_t;\n *   struct mq_msg_s {\n *           mq_msg(mq_msg_t) link;\n *           [message data]\n *   };\n *   mq_gen(, mq_, mq_t, mq_msg_t, link)\n *\n * The API is as follows:\n *\n *   bool mq_init(mq_t *mq);\n *   void mq_fini(mq_t *mq);\n *   unsigned mq_count(mq_t *mq);\n *   mq_msg_t *mq_tryget(mq_t *mq);\n *   mq_msg_t *mq_get(mq_t *mq);\n *   void mq_put(mq_t *mq, mq_msg_t *msg);\n *\n * The message queue linkage embedded in each message is to be treated as\n * externally opaque (no need to initialize or clean up externally).  mq_fini()\n * does not perform any cleanup of messages, since it knows nothing of their\n * payloads.\n */\n#define mq_msg(a_mq_msg_type)\tql_elm(a_mq_msg_type)\n\n#define mq_gen(a_attr, a_prefix, a_mq_type, a_mq_msg_type, a_field)\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tmtx_t\t\t\tlock;\t\t\t\t\t\\\n\tql_head(a_mq_msg_type)\tmsgs;\t\t\t\t\t\\\n\tunsigned\t\tcount;\t\t\t\t\t\\\n} a_mq_type;\t\t\t\t\t\t\t\t\\\na_attr bool\t\t\t\t\t\t\t\t\\\na_prefix##init(a_mq_type *mq) {\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tif (mtx_init(&mq->lock)) {\t\t\t\t\t\\\n\t\treturn true;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tql_new(&mq->msgs);\t\t\t\t\t\t\\\n\tmq->count = 0;\t\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\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##fini(a_mq_type *mq) {\t\t\t\t\t\t\\\n\tmtx_fini(&mq->lock);\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr unsigned\t\t\t\t\t\t\t\t\\\na_prefix##count(a_mq_type *mq) {\t\t\t\t\t\\\n\tunsigned count;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmtx_lock(&mq->lock);\t\t\t\t\t\t\\\n\tcount = mq->count;\t\t\t\t\t\t\\\n\tmtx_unlock(&mq->lock);\t\t\t\t\t\t\\\n\treturn count;\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_mq_msg_type *\t\t\t\t\t\t\t\\\na_prefix##tryget(a_mq_type *mq) {\t\t\t\t\t\\\n\ta_mq_msg_type *msg;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmtx_lock(&mq->lock);\t\t\t\t\t\t\\\n\tmsg = ql_first(&mq->msgs);\t\t\t\t\t\\\n\tif (msg != NULL) {\t\t\t\t\t\t\\\n\t\tql_head_remove(&mq->msgs, a_mq_msg_type, a_field);\t\\\n\t\tmq->count--;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tmtx_unlock(&mq->lock);\t\t\t\t\t\t\\\n\treturn msg;\t\t\t\t\t\t\t\\\n}\t\t\t\t\t\t\t\t\t\\\na_attr a_mq_msg_type *\t\t\t\t\t\t\t\\\na_prefix##get(a_mq_type *mq) {\t\t\t\t\t\t\\\n\ta_mq_msg_type *msg;\t\t\t\t\t\t\\\n\tunsigned ns;\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmsg = a_prefix##tryget(mq);\t\t\t\t\t\\\n\tif (msg != NULL) {\t\t\t\t\t\t\\\n\t\treturn msg;\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\tns = 1;\t\t\t\t\t\t\t\t\\\n\twhile (true) {\t\t\t\t\t\t\t\\\n\t\tmq_nanosleep(ns);\t\t\t\t\t\\\n\t\tmsg = a_prefix##tryget(mq);\t\t\t\t\\\n\t\tif (msg != NULL) {\t\t\t\t\t\\\n\t\t\treturn msg;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif (ns < 1000*1000*1000) {\t\t\t\t\\\n\t\t\t/* Double sleep time, up to max 1 second. */\t\\\n\t\t\tns <<= 1;\t\t\t\t\t\\\n\t\t\tif (ns > 1000*1000*1000) {\t\t\t\\\n\t\t\t\tns = 1000*1000*1000;\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\\\na_attr void\t\t\t\t\t\t\t\t\\\na_prefix##put(a_mq_type *mq, a_mq_msg_type *msg) {\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tmtx_lock(&mq->lock);\t\t\t\t\t\t\\\n\tql_elm_new(msg, a_field);\t\t\t\t\t\\\n\tql_tail_insert(&mq->msgs, msg, a_field);\t\t\t\\\n\tmq->count++;\t\t\t\t\t\t\t\\\n\tmtx_unlock(&mq->lock);\t\t\t\t\t\t\\\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/mtx.h",
    "content": "/*\n * mtx is a slightly simplified version of malloc_mutex.  This code duplication\n * is unfortunate, but there are allocator bootstrapping considerations that\n * would leak into the test infrastructure if malloc_mutex were used directly\n * in tests.\n */\n\ntypedef struct {\n#ifdef _WIN32\n\tCRITICAL_SECTION\tlock;\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tos_unfair_lock\t\tlock;\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLock\t\tlock;\n#else\n\tpthread_mutex_t\t\tlock;\n#endif\n} mtx_t;\n\nbool\tmtx_init(mtx_t *mtx);\nvoid\tmtx_fini(mtx_t *mtx);\nvoid\tmtx_lock(mtx_t *mtx);\nvoid\tmtx_unlock(mtx_t *mtx);\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/test.h",
    "content": "#define ASSERT_BUFSIZE\t256\n\n#define assert_cmp(t, a, b, cmp, neg_cmp, pri, ...) do {\t\t\\\n\tt a_ = (a);\t\t\t\t\t\t\t\\\n\tt b_ = (b);\t\t\t\t\t\t\t\\\n\tif (!(a_ cmp b_)) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) \" #cmp \" (%s) --> \"\t\t\t\t\\\n\t\t    \"%\" pri \" \" #neg_cmp \" %\" pri \": \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__,\t\t\t\\\n\t\t    #a, #b, a_, b_);\t\t\t\t\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define assert_ptr_eq(a, b, ...)\tassert_cmp(void *, a, b, ==,\t\\\n    !=, \"p\", __VA_ARGS__)\n#define assert_ptr_ne(a, b, ...)\tassert_cmp(void *, a, b, !=,\t\\\n    ==, \"p\", __VA_ARGS__)\n#define assert_ptr_null(a, ...)\t\tassert_cmp(void *, a, NULL, ==,\t\\\n    !=, \"p\", __VA_ARGS__)\n#define assert_ptr_not_null(a, ...)\tassert_cmp(void *, a, NULL, !=,\t\\\n    ==, \"p\", __VA_ARGS__)\n\n#define assert_c_eq(a, b, ...)\tassert_cmp(char, a, b, ==, !=, \"c\", __VA_ARGS__)\n#define assert_c_ne(a, b, ...)\tassert_cmp(char, a, b, !=, ==, \"c\", __VA_ARGS__)\n#define assert_c_lt(a, b, ...)\tassert_cmp(char, a, b, <, >=, \"c\", __VA_ARGS__)\n#define assert_c_le(a, b, ...)\tassert_cmp(char, a, b, <=, >, \"c\", __VA_ARGS__)\n#define assert_c_ge(a, b, ...)\tassert_cmp(char, a, b, >=, <, \"c\", __VA_ARGS__)\n#define assert_c_gt(a, b, ...)\tassert_cmp(char, a, b, >, <=, \"c\", __VA_ARGS__)\n\n#define assert_x_eq(a, b, ...)\tassert_cmp(int, a, b, ==, !=, \"#x\", __VA_ARGS__)\n#define assert_x_ne(a, b, ...)\tassert_cmp(int, a, b, !=, ==, \"#x\", __VA_ARGS__)\n#define assert_x_lt(a, b, ...)\tassert_cmp(int, a, b, <, >=, \"#x\", __VA_ARGS__)\n#define assert_x_le(a, b, ...)\tassert_cmp(int, a, b, <=, >, \"#x\", __VA_ARGS__)\n#define assert_x_ge(a, b, ...)\tassert_cmp(int, a, b, >=, <, \"#x\", __VA_ARGS__)\n#define assert_x_gt(a, b, ...)\tassert_cmp(int, a, b, >, <=, \"#x\", __VA_ARGS__)\n\n#define assert_d_eq(a, b, ...)\tassert_cmp(int, a, b, ==, !=, \"d\", __VA_ARGS__)\n#define assert_d_ne(a, b, ...)\tassert_cmp(int, a, b, !=, ==, \"d\", __VA_ARGS__)\n#define assert_d_lt(a, b, ...)\tassert_cmp(int, a, b, <, >=, \"d\", __VA_ARGS__)\n#define assert_d_le(a, b, ...)\tassert_cmp(int, a, b, <=, >, \"d\", __VA_ARGS__)\n#define assert_d_ge(a, b, ...)\tassert_cmp(int, a, b, >=, <, \"d\", __VA_ARGS__)\n#define assert_d_gt(a, b, ...)\tassert_cmp(int, a, b, >, <=, \"d\", __VA_ARGS__)\n\n#define assert_u_eq(a, b, ...)\tassert_cmp(int, a, b, ==, !=, \"u\", __VA_ARGS__)\n#define assert_u_ne(a, b, ...)\tassert_cmp(int, a, b, !=, ==, \"u\", __VA_ARGS__)\n#define assert_u_lt(a, b, ...)\tassert_cmp(int, a, b, <, >=, \"u\", __VA_ARGS__)\n#define assert_u_le(a, b, ...)\tassert_cmp(int, a, b, <=, >, \"u\", __VA_ARGS__)\n#define assert_u_ge(a, b, ...)\tassert_cmp(int, a, b, >=, <, \"u\", __VA_ARGS__)\n#define assert_u_gt(a, b, ...)\tassert_cmp(int, a, b, >, <=, \"u\", __VA_ARGS__)\n\n#define assert_ld_eq(a, b, ...)\tassert_cmp(long, a, b, ==,\t\\\n    !=, \"ld\", __VA_ARGS__)\n#define assert_ld_ne(a, b, ...)\tassert_cmp(long, a, b, !=,\t\\\n    ==, \"ld\", __VA_ARGS__)\n#define assert_ld_lt(a, b, ...)\tassert_cmp(long, a, b, <,\t\\\n    >=, \"ld\", __VA_ARGS__)\n#define assert_ld_le(a, b, ...)\tassert_cmp(long, a, b, <=,\t\\\n    >, \"ld\", __VA_ARGS__)\n#define assert_ld_ge(a, b, ...)\tassert_cmp(long, a, b, >=,\t\\\n    <, \"ld\", __VA_ARGS__)\n#define assert_ld_gt(a, b, ...)\tassert_cmp(long, a, b, >,\t\\\n    <=, \"ld\", __VA_ARGS__)\n\n#define assert_lu_eq(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, ==, !=, \"lu\", __VA_ARGS__)\n#define assert_lu_ne(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, !=, ==, \"lu\", __VA_ARGS__)\n#define assert_lu_lt(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, <, >=, \"lu\", __VA_ARGS__)\n#define assert_lu_le(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, <=, >, \"lu\", __VA_ARGS__)\n#define assert_lu_ge(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, >=, <, \"lu\", __VA_ARGS__)\n#define assert_lu_gt(a, b, ...)\tassert_cmp(unsigned long,\t\\\n    a, b, >, <=, \"lu\", __VA_ARGS__)\n\n#define assert_qd_eq(a, b, ...)\tassert_cmp(long long, a, b, ==,\t\\\n    !=, \"qd\", __VA_ARGS__)\n#define assert_qd_ne(a, b, ...)\tassert_cmp(long long, a, b, !=,\t\\\n    ==, \"qd\", __VA_ARGS__)\n#define assert_qd_lt(a, b, ...)\tassert_cmp(long long, a, b, <,\t\\\n    >=, \"qd\", __VA_ARGS__)\n#define assert_qd_le(a, b, ...)\tassert_cmp(long long, a, b, <=,\t\\\n    >, \"qd\", __VA_ARGS__)\n#define assert_qd_ge(a, b, ...)\tassert_cmp(long long, a, b, >=,\t\\\n    <, \"qd\", __VA_ARGS__)\n#define assert_qd_gt(a, b, ...)\tassert_cmp(long long, a, b, >,\t\\\n    <=, \"qd\", __VA_ARGS__)\n\n#define assert_qu_eq(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, ==, !=, \"qu\", __VA_ARGS__)\n#define assert_qu_ne(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, !=, ==, \"qu\", __VA_ARGS__)\n#define assert_qu_lt(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, <, >=, \"qu\", __VA_ARGS__)\n#define assert_qu_le(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, <=, >, \"qu\", __VA_ARGS__)\n#define assert_qu_ge(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, >=, <, \"qu\", __VA_ARGS__)\n#define assert_qu_gt(a, b, ...)\tassert_cmp(unsigned long long,\t\\\n    a, b, >, <=, \"qu\", __VA_ARGS__)\n\n#define assert_jd_eq(a, b, ...)\tassert_cmp(intmax_t, a, b, ==,\t\\\n    !=, \"jd\", __VA_ARGS__)\n#define assert_jd_ne(a, b, ...)\tassert_cmp(intmax_t, a, b, !=,\t\\\n    ==, \"jd\", __VA_ARGS__)\n#define assert_jd_lt(a, b, ...)\tassert_cmp(intmax_t, a, b, <,\t\\\n    >=, \"jd\", __VA_ARGS__)\n#define assert_jd_le(a, b, ...)\tassert_cmp(intmax_t, a, b, <=,\t\\\n    >, \"jd\", __VA_ARGS__)\n#define assert_jd_ge(a, b, ...)\tassert_cmp(intmax_t, a, b, >=,\t\\\n    <, \"jd\", __VA_ARGS__)\n#define assert_jd_gt(a, b, ...)\tassert_cmp(intmax_t, a, b, >,\t\\\n    <=, \"jd\", __VA_ARGS__)\n\n#define assert_ju_eq(a, b, ...)\tassert_cmp(uintmax_t, a, b, ==,\t\\\n    !=, \"ju\", __VA_ARGS__)\n#define assert_ju_ne(a, b, ...)\tassert_cmp(uintmax_t, a, b, !=,\t\\\n    ==, \"ju\", __VA_ARGS__)\n#define assert_ju_lt(a, b, ...)\tassert_cmp(uintmax_t, a, b, <,\t\\\n    >=, \"ju\", __VA_ARGS__)\n#define assert_ju_le(a, b, ...)\tassert_cmp(uintmax_t, a, b, <=,\t\\\n    >, \"ju\", __VA_ARGS__)\n#define assert_ju_ge(a, b, ...)\tassert_cmp(uintmax_t, a, b, >=,\t\\\n    <, \"ju\", __VA_ARGS__)\n#define assert_ju_gt(a, b, ...)\tassert_cmp(uintmax_t, a, b, >,\t\\\n    <=, \"ju\", __VA_ARGS__)\n\n#define assert_zd_eq(a, b, ...)\tassert_cmp(ssize_t, a, b, ==,\t\\\n    !=, \"zd\", __VA_ARGS__)\n#define assert_zd_ne(a, b, ...)\tassert_cmp(ssize_t, a, b, !=,\t\\\n    ==, \"zd\", __VA_ARGS__)\n#define assert_zd_lt(a, b, ...)\tassert_cmp(ssize_t, a, b, <,\t\\\n    >=, \"zd\", __VA_ARGS__)\n#define assert_zd_le(a, b, ...)\tassert_cmp(ssize_t, a, b, <=,\t\\\n    >, \"zd\", __VA_ARGS__)\n#define assert_zd_ge(a, b, ...)\tassert_cmp(ssize_t, a, b, >=,\t\\\n    <, \"zd\", __VA_ARGS__)\n#define assert_zd_gt(a, b, ...)\tassert_cmp(ssize_t, a, b, >,\t\\\n    <=, \"zd\", __VA_ARGS__)\n\n#define assert_zu_eq(a, b, ...)\tassert_cmp(size_t, a, b, ==,\t\\\n    !=, \"zu\", __VA_ARGS__)\n#define assert_zu_ne(a, b, ...)\tassert_cmp(size_t, a, b, !=,\t\\\n    ==, \"zu\", __VA_ARGS__)\n#define assert_zu_lt(a, b, ...)\tassert_cmp(size_t, a, b, <,\t\\\n    >=, \"zu\", __VA_ARGS__)\n#define assert_zu_le(a, b, ...)\tassert_cmp(size_t, a, b, <=,\t\\\n    >, \"zu\", __VA_ARGS__)\n#define assert_zu_ge(a, b, ...)\tassert_cmp(size_t, a, b, >=,\t\\\n    <, \"zu\", __VA_ARGS__)\n#define assert_zu_gt(a, b, ...)\tassert_cmp(size_t, a, b, >,\t\\\n    <=, \"zu\", __VA_ARGS__)\n\n#define assert_d32_eq(a, b, ...)\tassert_cmp(int32_t, a, b, ==,\t\\\n    !=, FMTd32, __VA_ARGS__)\n#define assert_d32_ne(a, b, ...)\tassert_cmp(int32_t, a, b, !=,\t\\\n    ==, FMTd32, __VA_ARGS__)\n#define assert_d32_lt(a, b, ...)\tassert_cmp(int32_t, a, b, <,\t\\\n    >=, FMTd32, __VA_ARGS__)\n#define assert_d32_le(a, b, ...)\tassert_cmp(int32_t, a, b, <=,\t\\\n    >, FMTd32, __VA_ARGS__)\n#define assert_d32_ge(a, b, ...)\tassert_cmp(int32_t, a, b, >=,\t\\\n    <, FMTd32, __VA_ARGS__)\n#define assert_d32_gt(a, b, ...)\tassert_cmp(int32_t, a, b, >,\t\\\n    <=, FMTd32, __VA_ARGS__)\n\n#define assert_u32_eq(a, b, ...)\tassert_cmp(uint32_t, a, b, ==,\t\\\n    !=, FMTu32, __VA_ARGS__)\n#define assert_u32_ne(a, b, ...)\tassert_cmp(uint32_t, a, b, !=,\t\\\n    ==, FMTu32, __VA_ARGS__)\n#define assert_u32_lt(a, b, ...)\tassert_cmp(uint32_t, a, b, <,\t\\\n    >=, FMTu32, __VA_ARGS__)\n#define assert_u32_le(a, b, ...)\tassert_cmp(uint32_t, a, b, <=,\t\\\n    >, FMTu32, __VA_ARGS__)\n#define assert_u32_ge(a, b, ...)\tassert_cmp(uint32_t, a, b, >=,\t\\\n    <, FMTu32, __VA_ARGS__)\n#define assert_u32_gt(a, b, ...)\tassert_cmp(uint32_t, a, b, >,\t\\\n    <=, FMTu32, __VA_ARGS__)\n\n#define assert_d64_eq(a, b, ...)\tassert_cmp(int64_t, a, b, ==,\t\\\n    !=, FMTd64, __VA_ARGS__)\n#define assert_d64_ne(a, b, ...)\tassert_cmp(int64_t, a, b, !=,\t\\\n    ==, FMTd64, __VA_ARGS__)\n#define assert_d64_lt(a, b, ...)\tassert_cmp(int64_t, a, b, <,\t\\\n    >=, FMTd64, __VA_ARGS__)\n#define assert_d64_le(a, b, ...)\tassert_cmp(int64_t, a, b, <=,\t\\\n    >, FMTd64, __VA_ARGS__)\n#define assert_d64_ge(a, b, ...)\tassert_cmp(int64_t, a, b, >=,\t\\\n    <, FMTd64, __VA_ARGS__)\n#define assert_d64_gt(a, b, ...)\tassert_cmp(int64_t, a, b, >,\t\\\n    <=, FMTd64, __VA_ARGS__)\n\n#define assert_u64_eq(a, b, ...)\tassert_cmp(uint64_t, a, b, ==,\t\\\n    !=, FMTu64, __VA_ARGS__)\n#define assert_u64_ne(a, b, ...)\tassert_cmp(uint64_t, a, b, !=,\t\\\n    ==, FMTu64, __VA_ARGS__)\n#define assert_u64_lt(a, b, ...)\tassert_cmp(uint64_t, a, b, <,\t\\\n    >=, FMTu64, __VA_ARGS__)\n#define assert_u64_le(a, b, ...)\tassert_cmp(uint64_t, a, b, <=,\t\\\n    >, FMTu64, __VA_ARGS__)\n#define assert_u64_ge(a, b, ...)\tassert_cmp(uint64_t, a, b, >=,\t\\\n    <, FMTu64, __VA_ARGS__)\n#define assert_u64_gt(a, b, ...)\tassert_cmp(uint64_t, a, b, >,\t\\\n    <=, FMTu64, __VA_ARGS__)\n\n#define assert_b_eq(a, b, ...) do {\t\t\t\t\t\\\n\tbool a_ = (a);\t\t\t\t\t\t\t\\\n\tbool b_ = (b);\t\t\t\t\t\t\t\\\n\tif (!(a_ == b_)) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) == (%s) --> %s != %s: \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__,\t\t\t\\\n\t\t    #a, #b, a_ ? \"true\" : \"false\",\t\t\t\\\n\t\t    b_ ? \"true\" : \"false\");\t\t\t\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define assert_b_ne(a, b, ...) do {\t\t\t\t\t\\\n\tbool a_ = (a);\t\t\t\t\t\t\t\\\n\tbool b_ = (b);\t\t\t\t\t\t\t\\\n\tif (!(a_ != b_)) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) != (%s) --> %s == %s: \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__,\t\t\t\\\n\t\t    #a, #b, a_ ? \"true\" : \"false\",\t\t\t\\\n\t\t    b_ ? \"true\" : \"false\");\t\t\t\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define assert_true(a, ...)\tassert_b_eq(a, true, __VA_ARGS__)\n#define assert_false(a, ...)\tassert_b_eq(a, false, __VA_ARGS__)\n\n#define assert_str_eq(a, b, ...) do {\t\t\t\t\\\n\tif (strcmp((a), (b))) {\t\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) same as (%s) --> \"\t\t\t\t\\\n\t\t    \"\\\"%s\\\" differs from \\\"%s\\\": \",\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__, #a, #b, a, b);\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n#define assert_str_ne(a, b, ...) do {\t\t\t\t\\\n\tif (!strcmp((a), (b))) {\t\t\t\t\t\\\n\t\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tchar message[ASSERT_BUFSIZE];\t\t\t\t\\\n\t\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\\\n\t\t    \"%s:%s:%d: Failed assertion: \"\t\t\t\\\n\t\t    \"(%s) differs from (%s) --> \"\t\t\t\\\n\t\t    \"\\\"%s\\\" same as \\\"%s\\\": \",\t\t\t\t\\\n\t\t    __func__, __FILE__, __LINE__, #a, #b, a, b);\t\\\n\t\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\\\n\t\tp_test_fail(prefix, message);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define assert_not_reached(...) do {\t\t\t\t\t\\\n\tchar prefix[ASSERT_BUFSIZE];\t\t\t\t\t\\\n\tchar message[ASSERT_BUFSIZE];\t\t\t\t\t\\\n\tmalloc_snprintf(prefix, sizeof(prefix),\t\t\t\t\\\n\t    \"%s:%s:%d: Unreachable code reached: \",\t\t\t\\\n\t    __func__, __FILE__, __LINE__);\t\t\t\t\\\n\tmalloc_snprintf(message, sizeof(message), __VA_ARGS__);\t\t\\\n\tp_test_fail(prefix, message);\t\t\t\t\t\\\n} while (0)\n\n/*\n * If this enum changes, corresponding changes in test/test.sh.in are also\n * necessary.\n */\ntypedef enum {\n\ttest_status_pass = 0,\n\ttest_status_skip = 1,\n\ttest_status_fail = 2,\n\n\ttest_status_count = 3\n} test_status_t;\n\ntypedef void (test_t)(void);\n\n#define TEST_BEGIN(f)\t\t\t\t\t\t\t\\\nstatic void\t\t\t\t\t\t\t\t\\\nf(void) {\t\t\t\t\t\t\t\t\\\n\tp_test_init(#f);\n\n#define TEST_END\t\t\t\t\t\t\t\\\n\tgoto label_test_end;\t\t\t\t\t\t\\\nlabel_test_end:\t\t\t\t\t\t\t\t\\\n\tp_test_fini();\t\t\t\t\t\t\t\\\n}\n\n#define test(...)\t\t\t\t\t\t\t\\\n\tp_test(__VA_ARGS__, NULL)\n\n#define test_no_reentrancy(...)\t\t\t\t\t\t\t\\\n\tp_test_no_reentrancy(__VA_ARGS__, NULL)\n\n#define test_no_malloc_init(...)\t\t\t\t\t\\\n\tp_test_no_malloc_init(__VA_ARGS__, NULL)\n\n#define test_skip_if(e) do {\t\t\t\t\t\t\\\n\tif (e) {\t\t\t\t\t\t\t\\\n\t\ttest_skip(\"%s:%s:%d: Test skipped: (%s)\",\t\t\\\n\t\t    __func__, __FILE__, __LINE__, #e);\t\t\t\\\n\t\tgoto label_test_end;\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\nbool test_is_reentrant();\n\nvoid\ttest_skip(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);\nvoid\ttest_fail(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);\n\n/* For private use by macros. */\ntest_status_t\tp_test(test_t *t, ...);\ntest_status_t\tp_test_no_reentrancy(test_t *t, ...);\ntest_status_t\tp_test_no_malloc_init(test_t *t, ...);\nvoid\tp_test_init(const char *name);\nvoid\tp_test_fini(void);\nvoid\tp_test_fail(const char *prefix, const char *message);\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/thd.h",
    "content": "/* Abstraction layer for threading in tests. */\n#ifdef _WIN32\ntypedef HANDLE thd_t;\n#else\ntypedef pthread_t thd_t;\n#endif\n\nvoid\tthd_create(thd_t *thd, void *(*proc)(void *), void *arg);\nvoid\tthd_join(thd_t thd, void **ret);\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/include/test/timer.h",
    "content": "/* Simple timer, for use in benchmark reporting. */\n\ntypedef struct {\n\tnstime_t t0;\n\tnstime_t t1;\n} timedelta_t;\n\nvoid\ttimer_start(timedelta_t *timer);\nvoid\ttimer_stop(timedelta_t *timer);\nuint64_t\ttimer_usec(const timedelta_t *timer);\nvoid\ttimer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen);\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/MALLOCX_ARENA.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS 10\n\nstatic bool have_dss =\n#ifdef JEMALLOC_DSS\n    true\n#else\n    false\n#endif\n    ;\n\nvoid *\nthd_start(void *arg) {\n\tunsigned thread_ind = (unsigned)(uintptr_t)arg;\n\tunsigned arena_ind;\n\tvoid *p;\n\tsize_t sz;\n\n\tsz = sizeof(arena_ind);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Error in arenas.create\");\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\tunsigned prec_ind = thread_ind %\n\t\t    (sizeof(dss_precs)/sizeof(char*));\n\t\tconst char *dss = dss_precs[prec_ind];\n\t\tint expected_err = (have_dss || prec_ind == 0) ? 0 : EFAULT;\n\t\tassert_d_eq(mallctlnametomib(\"arena.0.dss\", mib, &miblen), 0,\n\t\t    \"Error in mallctlnametomib()\");\n\t\tmib[1] = arena_ind;\n\t\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&dss,\n\t\t    sizeof(const char *)), expected_err,\n\t\t    \"Error in mallctlbymib()\");\n\t}\n\n\tp = mallocx(1, MALLOCX_ARENA(arena_ind));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tdallocx(p, 0);\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_MALLOCX_ARENA) {\n\tthd_t thds[NTHREADS];\n\tunsigned i;\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_create(&thds[i], thd_start,\n\t\t    (void *)(uintptr_t)i);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_MALLOCX_ARENA);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/aligned_alloc.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAXALIGN (((size_t)1) << 23)\n\n/*\n * On systems which can't merge extents, tests that call this function generate\n * a lot of dirty memory very quickly.  Purging between cycles mitigates\n * potential OOM on e.g. 32-bit Windows.\n */\nstatic void\npurge(void) {\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl error\");\n}\n\nTEST_BEGIN(test_alignment_errors) {\n\tsize_t alignment;\n\tvoid *p;\n\n\talignment = 0;\n\tset_errno(0);\n\tp = aligned_alloc(alignment, 1);\n\tassert_false(p != NULL || get_errno() != EINVAL,\n\t    \"Expected error for invalid alignment %zu\", alignment);\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\tassert_false(p != NULL || get_errno() != EINVAL,\n\t\t    \"Expected error for invalid alignment %zu\",\n\t\t    alignment + 1);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_oom_errors) {\n\tsize_t alignment, size;\n\tvoid *p;\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\tassert_false(p != NULL || get_errno() != ENOMEM,\n\t    \"Expected error for aligned_alloc(%zu, %zu)\",\n\t    alignment, size);\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsize      = UINT64_C(0xc000000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsize      = 0xc0000001LU;\n#endif\n\tset_errno(0);\n\tp = aligned_alloc(alignment, size);\n\tassert_false(p != NULL || get_errno() != ENOMEM,\n\t    \"Expected error for aligned_alloc(%zu, %zu)\",\n\t    alignment, size);\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\tassert_false(p != NULL || get_errno() != ENOMEM,\n\t    \"Expected error for aligned_alloc(&p, %zu, %zu)\",\n\t    alignment, size);\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n#define NITER 4\n\tsize_t alignment, size, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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(get_errno(), buf, sizeof(buf));\n\t\t\t\t\ttest_fail(\n\t\t\t\t\t    \"Error for alignment=%zu, \"\n\t\t\t\t\t    \"size=%zu (%#zx): %s\",\n\t\t\t\t\t    alignment, size, size, buf);\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\t}\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\tpurge();\n\t}\n#undef NITER\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_alignment_errors,\n\t    test_oom_errors,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/allocated.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic const bool config_stats =\n#ifdef JEMALLOC_STATS\n    true\n#else\n    false\n#endif\n    ;\n\nvoid *\nthd_start(void *arg) {\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\", (void *)&a0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tsz = sizeof(ap0);\n\tif ((err = mallctl(\"thread.allocatedp\", (void *)&ap0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tassert_u64_eq(*ap0, a0,\n\t    \"\\\"thread.allocatedp\\\" should provide a pointer to internal \"\n\t    \"storage\");\n\n\tsz = sizeof(d0);\n\tif ((err = mallctl(\"thread.deallocated\", (void *)&d0, &sz, NULL, 0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tsz = sizeof(dp0);\n\tif ((err = mallctl(\"thread.deallocatedp\", (void *)&dp0, &sz, NULL,\n\t    0))) {\n\t\tif (err == ENOENT) {\n\t\t\tgoto label_ENOENT;\n\t\t}\n\t\ttest_fail(\"%s(): Error in mallctl(): %s\", __func__,\n\t\t    strerror(err));\n\t}\n\tassert_u64_eq(*dp0, d0,\n\t    \"\\\"thread.deallocatedp\\\" should provide a pointer to internal \"\n\t    \"storage\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() error\");\n\n\tsz = sizeof(a1);\n\tmallctl(\"thread.allocated\", (void *)&a1, &sz, NULL, 0);\n\tsz = sizeof(ap1);\n\tmallctl(\"thread.allocatedp\", (void *)&ap1, &sz, NULL, 0);\n\tassert_u64_eq(*ap1, a1,\n\t    \"Dereferenced \\\"thread.allocatedp\\\" value should equal \"\n\t    \"\\\"thread.allocated\\\" value\");\n\tassert_ptr_eq(ap0, ap1,\n\t    \"Pointer returned by \\\"thread.allocatedp\\\" should not change\");\n\n\tusize = malloc_usable_size(p);\n\tassert_u64_le(a0 + usize, a1,\n\t    \"Allocated memory counter should increase by at least the amount \"\n\t    \"explicitly allocated\");\n\n\tfree(p);\n\n\tsz = sizeof(d1);\n\tmallctl(\"thread.deallocated\", (void *)&d1, &sz, NULL, 0);\n\tsz = sizeof(dp1);\n\tmallctl(\"thread.deallocatedp\", (void *)&dp1, &sz, NULL, 0);\n\tassert_u64_eq(*dp1, d1,\n\t    \"Dereferenced \\\"thread.deallocatedp\\\" value should equal \"\n\t    \"\\\"thread.deallocated\\\" value\");\n\tassert_ptr_eq(dp0, dp1,\n\t    \"Pointer returned by \\\"thread.deallocatedp\\\" should not change\");\n\n\tassert_u64_le(d0 + usize, d1,\n\t    \"Deallocated memory counter should increase by at least the amount \"\n\t    \"explicitly deallocated\");\n\n\treturn NULL;\nlabel_ENOENT:\n\tassert_false(config_stats,\n\t    \"ENOENT should only be returned if stats are disabled\");\n\ttest_skip(\"\\\"thread.allocated\\\" mallctl not available\");\n\treturn NULL;\n}\n\nTEST_BEGIN(test_main_thread) {\n\tthd_start(NULL);\n}\nTEST_END\n\nTEST_BEGIN(test_subthread) {\n\tthd_t thd;\n\n\tthd_create(&thd, thd_start, NULL);\n\tthd_join(thd, NULL);\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Run tests multiple times to check for bad interactions. */\n\treturn test(\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/extent.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"test/extent_hooks.h\"\n\nstatic bool\ncheck_background_thread_enabled(void) {\n\tbool enabled;\n\tsize_t sz = sizeof(bool);\n\tint ret = mallctl(\"background_thread\", (void *)&enabled, &sz, NULL,0);\n\tif (ret == ENOENT) {\n\t\treturn false;\n\t}\n\tassert_d_eq(ret, 0, \"Unexpected mallctl error\");\n\treturn enabled;\n}\n\nstatic void\ntest_extent_body(unsigned arena_ind) {\n\tvoid *p;\n\tsize_t large0, large1, large2, sz;\n\tsize_t purge_mib[3];\n\tsize_t purge_miblen;\n\tint flags;\n\tbool xallocx_success_a, xallocx_success_b, xallocx_success_c;\n\n\tflags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\n\t/* Get large size classes. */\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&large0, &sz, NULL,\n\t    0), 0, \"Unexpected arenas.lextent.0.size failure\");\n\tassert_d_eq(mallctl(\"arenas.lextent.1.size\", (void *)&large1, &sz, NULL,\n\t    0), 0, \"Unexpected arenas.lextent.1.size failure\");\n\tassert_d_eq(mallctl(\"arenas.lextent.2.size\", (void *)&large2, &sz, NULL,\n\t    0), 0, \"Unexpected arenas.lextent.2.size failure\");\n\n\t/* Test dalloc/decommit/purge cascade. */\n\tpurge_miblen = sizeof(purge_mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.purge\", purge_mib, &purge_miblen),\n\t    0, \"Unexpected mallctlnametomib() failure\");\n\tpurge_mib[1] = (size_t)arena_ind;\n\tcalled_alloc = false;\n\ttry_alloc = true;\n\ttry_dalloc = false;\n\ttry_decommit = false;\n\tp = mallocx(large0 * 2, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tassert_true(called_alloc, \"Expected alloc call\");\n\tcalled_dalloc = false;\n\tcalled_decommit = false;\n\tdid_purge_lazy = false;\n\tdid_purge_forced = false;\n\tcalled_split = false;\n\txallocx_success_a = (xallocx(p, large0, 0, flags) == large0);\n\tassert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),\n\t    0, \"Unexpected arena.%u.purge error\", arena_ind);\n\tif (xallocx_success_a) {\n\t\tassert_true(called_dalloc, \"Expected dalloc call\");\n\t\tassert_true(called_decommit, \"Expected decommit call\");\n\t\tassert_true(did_purge_lazy || did_purge_forced,\n\t\t    \"Expected purge\");\n\t}\n\tassert_true(called_split, \"Expected split call\");\n\tdallocx(p, flags);\n\ttry_dalloc = true;\n\n\t/* Test decommit/commit and observe split/merge. */\n\ttry_dalloc = false;\n\ttry_decommit = true;\n\tp = mallocx(large0 * 2, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tdid_decommit = false;\n\tdid_commit = false;\n\tcalled_split = false;\n\tdid_split = false;\n\tdid_merge = false;\n\txallocx_success_b = (xallocx(p, large0, 0, flags) == large0);\n\tassert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),\n\t    0, \"Unexpected arena.%u.purge error\", arena_ind);\n\tif (xallocx_success_b) {\n\t\tassert_true(did_split, \"Expected split\");\n\t}\n\txallocx_success_c = (xallocx(p, large0 * 2, 0, flags) == large0 * 2);\n\tif (did_split) {\n\t\tassert_b_eq(did_decommit, did_commit,\n\t\t    \"Expected decommit/commit match\");\n\t}\n\tif (xallocx_success_b && xallocx_success_c) {\n\t\tassert_true(did_merge, \"Expected merge\");\n\t}\n\tdallocx(p, flags);\n\ttry_dalloc = true;\n\ttry_decommit = false;\n\n\t/* Make sure non-large allocation succeeds. */\n\tp = mallocx(42, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tdallocx(p, flags);\n}\n\nstatic void\ntest_manual_hook_auto_arena(void) {\n\tunsigned narenas;\n\tsize_t old_size, new_size, sz;\n\tsize_t hooks_mib[3];\n\tsize_t hooks_miblen;\n\textent_hooks_t *new_hooks, *old_hooks;\n\n\textent_hooks_prep();\n\n\tsz = sizeof(unsigned);\n\t/* Get number of auto arenas. */\n\tassert_d_eq(mallctl(\"opt.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tif (narenas == 1) {\n\t\treturn;\n\t}\n\n\t/* Install custom extent hooks on arena 1 (might not be initialized). */\n\thooks_miblen = sizeof(hooks_mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.extent_hooks\", hooks_mib,\n\t    &hooks_miblen), 0, \"Unexpected mallctlnametomib() failure\");\n\thooks_mib[1] = 1;\n\told_size = sizeof(extent_hooks_t *);\n\tnew_hooks = &hooks;\n\tnew_size = sizeof(extent_hooks_t *);\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,\n\t    &old_size, (void *)&new_hooks, new_size), 0,\n\t    \"Unexpected extent_hooks error\");\n\tstatic bool auto_arena_created = false;\n\tif (old_hooks != &hooks) {\n\t\tassert_b_eq(auto_arena_created, false,\n\t\t    \"Expected auto arena 1 created only once.\");\n\t\tauto_arena_created = true;\n\t}\n}\n\nstatic void\ntest_manual_hook_body(void) {\n\tunsigned arena_ind;\n\tsize_t old_size, new_size, sz;\n\tsize_t hooks_mib[3];\n\tsize_t hooks_miblen;\n\textent_hooks_t *new_hooks, *old_hooks;\n\n\textent_hooks_prep();\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\n\t/* Install custom extent hooks. */\n\thooks_miblen = sizeof(hooks_mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.extent_hooks\", hooks_mib,\n\t    &hooks_miblen), 0, \"Unexpected mallctlnametomib() failure\");\n\thooks_mib[1] = (size_t)arena_ind;\n\told_size = sizeof(extent_hooks_t *);\n\tnew_hooks = &hooks;\n\tnew_size = sizeof(extent_hooks_t *);\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,\n\t    &old_size, (void *)&new_hooks, new_size), 0,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->alloc, extent_alloc_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->dalloc, extent_dalloc_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->commit, extent_commit_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->decommit, extent_decommit_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->purge_lazy, extent_purge_lazy_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->purge_forced, extent_purge_forced_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->split, extent_split_hook,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_ne(old_hooks->merge, extent_merge_hook,\n\t    \"Unexpected extent_hooks error\");\n\n\tif (!check_background_thread_enabled()) {\n\t\ttest_extent_body(arena_ind);\n\t}\n\n\t/* Restore extent hooks. */\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL,\n\t    (void *)&old_hooks, new_size), 0, \"Unexpected extent_hooks error\");\n\tassert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,\n\t    &old_size, NULL, 0), 0, \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks, default_hooks, \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->alloc, default_hooks->alloc,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->dalloc, default_hooks->dalloc,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->commit, default_hooks->commit,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->decommit, default_hooks->decommit,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->purge_lazy, default_hooks->purge_lazy,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->purge_forced, default_hooks->purge_forced,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->split, default_hooks->split,\n\t    \"Unexpected extent_hooks error\");\n\tassert_ptr_eq(old_hooks->merge, default_hooks->merge,\n\t    \"Unexpected extent_hooks error\");\n}\n\nTEST_BEGIN(test_extent_manual_hook) {\n\ttest_manual_hook_auto_arena();\n\ttest_manual_hook_body();\n\n\t/* Test failure paths. */\n\ttry_split = false;\n\ttest_manual_hook_body();\n\ttry_merge = false;\n\ttest_manual_hook_body();\n\ttry_purge_lazy = false;\n\ttry_purge_forced = false;\n\ttest_manual_hook_body();\n\n\ttry_split = try_merge = try_purge_lazy = try_purge_forced = true;\n}\nTEST_END\n\nTEST_BEGIN(test_extent_auto_hook) {\n\tunsigned arena_ind;\n\tsize_t new_size, sz;\n\textent_hooks_t *new_hooks;\n\n\textent_hooks_prep();\n\n\tsz = sizeof(unsigned);\n\tnew_hooks = &hooks;\n\tnew_size = sizeof(extent_hooks_t *);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz,\n\t    (void *)&new_hooks, new_size), 0, \"Unexpected mallctl() failure\");\n\n\ttest_skip_if(check_background_thread_enabled());\n\ttest_extent_body(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_extent_manual_hook,\n\t    test_extent_auto_hook);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/extent.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"junk:false\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/mallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\n/*\n * On systems which can't merge extents, tests that call this function generate\n * a lot of dirty memory very quickly.  Purging between cycles mitigates\n * potential OOM on e.g. 32-bit Windows.\n */\nstatic void\npurge(void) {\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl error\");\n}\n\nTEST_BEGIN(test_overflow) {\n\tsize_t largemax;\n\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tassert_ptr_null(mallocx(largemax+1, 0),\n\t    \"Expected OOM for mallocx(size=%#zx, 0)\", largemax+1);\n\n\tassert_ptr_null(mallocx(ZU(PTRDIFF_MAX)+1, 0),\n\t    \"Expected OOM for mallocx(size=%#zx, 0)\", ZU(PTRDIFF_MAX)+1);\n\n\tassert_ptr_null(mallocx(SIZE_T_MAX, 0),\n\t    \"Expected OOM for mallocx(size=%#zx, 0)\", SIZE_T_MAX);\n\n\tassert_ptr_null(mallocx(1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),\n\t    \"Expected OOM for mallocx(size=1, MALLOCX_ALIGN(%#zx))\",\n\t    ZU(PTRDIFF_MAX)+1);\n}\nTEST_END\n\nTEST_BEGIN(test_oom) {\n\tsize_t largemax;\n\tbool oom;\n\tvoid *ptrs[3];\n\tunsigned i;\n\n\t/*\n\t * It should be impossible to allocate three objects that each consume\n\t * nearly half the virtual address space.\n\t */\n\tlargemax = get_large_size(get_nlarge()-1);\n\toom = false;\n\tfor (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {\n\t\tptrs[i] = mallocx(largemax, 0);\n\t\tif (ptrs[i] == NULL) {\n\t\t\toom = true;\n\t\t}\n\t}\n\tassert_true(oom,\n\t    \"Expected OOM during series of calls to mallocx(size=%zu, 0)\",\n\t    largemax);\n\tfor (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {\n\t\tif (ptrs[i] != NULL) {\n\t\t\tdallocx(ptrs[i], 0);\n\t\t}\n\t}\n\tpurge();\n\n#if LG_SIZEOF_PTR == 3\n\tassert_ptr_null(mallocx(0x8000000000000000ULL,\n\t    MALLOCX_ALIGN(0x8000000000000000ULL)),\n\t    \"Expected OOM for mallocx()\");\n\tassert_ptr_null(mallocx(0x8000000000000000ULL,\n\t    MALLOCX_ALIGN(0x80000000)),\n\t    \"Expected OOM for mallocx()\");\n#else\n\tassert_ptr_null(mallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)),\n\t    \"Expected OOM for mallocx()\");\n#endif\n}\nTEST_END\n\nTEST_BEGIN(test_basic) {\n#define MAXSZ (((size_t)1) << 23)\n\tsize_t sz;\n\n\tfor (sz = 1; sz < MAXSZ; sz = nallocx(sz, 0) + 1) {\n\t\tsize_t nsz, rsz;\n\t\tvoid *p;\n\t\tnsz = nallocx(sz, 0);\n\t\tassert_zu_ne(nsz, 0, \"Unexpected nallocx() error\");\n\t\tp = mallocx(sz, 0);\n\t\tassert_ptr_not_null(p,\n\t\t    \"Unexpected mallocx(size=%zx, flags=0) error\", sz);\n\t\trsz = sallocx(p, 0);\n\t\tassert_zu_ge(rsz, sz, \"Real size smaller than expected\");\n\t\tassert_zu_eq(nsz, rsz, \"nallocx()/sallocx() size mismatch\");\n\t\tdallocx(p, 0);\n\n\t\tp = mallocx(sz, 0);\n\t\tassert_ptr_not_null(p,\n\t\t    \"Unexpected mallocx(size=%zx, flags=0) error\", sz);\n\t\tdallocx(p, 0);\n\n\t\tnsz = nallocx(sz, MALLOCX_ZERO);\n\t\tassert_zu_ne(nsz, 0, \"Unexpected nallocx() error\");\n\t\tp = mallocx(sz, MALLOCX_ZERO);\n\t\tassert_ptr_not_null(p,\n\t\t    \"Unexpected mallocx(size=%zx, flags=MALLOCX_ZERO) error\",\n\t\t    nsz);\n\t\trsz = sallocx(p, 0);\n\t\tassert_zu_eq(nsz, rsz, \"nallocx()/sallocx() rsize mismatch\");\n\t\tdallocx(p, 0);\n\t\tpurge();\n\t}\n#undef MAXSZ\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n\tconst char *percpu_arena;\n\tsize_t sz = sizeof(percpu_arena);\n\n\tif(mallctl(\"opt.percpu_arena\", (void *)&percpu_arena, &sz, NULL, 0) ||\n\t    strcmp(percpu_arena, \"disabled\") != 0) {\n\t\ttest_skip(\"test_alignment_and_size skipped: \"\n\t\t    \"not working with percpu arena.\");\n\t};\n#define MAXALIGN (((size_t)1) << 23)\n#define NITER 4\n\tsize_t nsz, rsz, alignment, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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 = nallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\tassert_zu_ne(nsz, 0,\n\t\t\t\t    \"nallocx() error for alignment=%zu, \"\n\t\t\t\t    \"size=%zu (%#zx)\", alignment, sz, sz);\n\t\t\t\tps[i] = mallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\tassert_ptr_not_null(ps[i],\n\t\t\t\t    \"mallocx() error for alignment=%zu, \"\n\t\t\t\t    \"size=%zu (%#zx)\", alignment, sz, sz);\n\t\t\t\trsz = sallocx(ps[i], 0);\n\t\t\t\tassert_zu_ge(rsz, sz,\n\t\t\t\t    \"Real size smaller than expected for \"\n\t\t\t\t    \"alignment=%zu, size=%zu\", alignment, sz);\n\t\t\t\tassert_zu_eq(nsz, rsz,\n\t\t\t\t    \"nallocx()/sallocx() size mismatch for \"\n\t\t\t\t    \"alignment=%zu, size=%zu\", alignment, sz);\n\t\t\t\tassert_ptr_null(\n\t\t\t\t    (void *)((uintptr_t)ps[i] & (alignment-1)),\n\t\t\t\t    \"%p inadequately aligned for\"\n\t\t\t\t    \" alignment=%zu, size=%zu\", ps[i],\n\t\t\t\t    alignment, sz);\n\t\t\t\ttotal += rsz;\n\t\t\t\tif (total >= (MAXALIGN << 1)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\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\tdallocx(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\tpurge();\n\t}\n#undef MAXALIGN\n#undef NITER\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_overflow,\n\t    test_oom,\n\t    test_basic,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/mallocx.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"junk:false\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/overflow.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_overflow) {\n\tunsigned nlextents;\n\tsize_t mib[4];\n\tsize_t sz, miblen, max_size_class;\n\tvoid *p;\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nlextents\", (void *)&nlextents, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() error\");\n\n\tmiblen = sizeof(mib) / sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arenas.lextent.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() error\");\n\tmib[2] = nlextents - 1;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctlbymib() error\");\n\n\tassert_ptr_null(malloc(max_size_class + 1),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tassert_ptr_null(malloc(SIZE_T_MAX),\n\t    \"Expected OOM due to over-sized allocation request\");\n\n\tassert_ptr_null(calloc(1, max_size_class + 1),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tassert_ptr_null(calloc(1, SIZE_T_MAX),\n\t    \"Expected OOM due to over-sized allocation request\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() OOM\");\n\tassert_ptr_null(realloc(p, max_size_class + 1),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tassert_ptr_null(realloc(p, SIZE_T_MAX),\n\t    \"Expected OOM due to over-sized allocation request\");\n\tfree(p);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_overflow);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/posix_memalign.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAXALIGN (((size_t)1) << 23)\n\n/*\n * On systems which can't merge extents, tests that call this function generate\n * a lot of dirty memory very quickly.  Purging between cycles mitigates\n * potential OOM on e.g. 32-bit Windows.\n */\nstatic void\npurge(void) {\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl error\");\n}\n\nTEST_BEGIN(test_alignment_errors) {\n\tsize_t alignment;\n\tvoid *p;\n\n\tfor (alignment = 0; alignment < sizeof(void *); alignment++) {\n\t\tassert_d_eq(posix_memalign(&p, alignment, 1), EINVAL,\n\t\t    \"Expected error for invalid alignment %zu\",\n\t\t    alignment);\n\t}\n\n\tfor (alignment = sizeof(size_t); alignment < MAXALIGN;\n\t    alignment <<= 1) {\n\t\tassert_d_ne(posix_memalign(&p, alignment + 1, 1), 0,\n\t\t    \"Expected error for invalid alignment %zu\",\n\t\t    alignment + 1);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_oom_errors) {\n\tsize_t alignment, size;\n\tvoid *p;\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\tassert_d_ne(posix_memalign(&p, alignment, size), 0,\n\t    \"Expected error for posix_memalign(&p, %zu, %zu)\",\n\t    alignment, size);\n\n#if LG_SIZEOF_PTR == 3\n\talignment = UINT64_C(0x4000000000000000);\n\tsize      = UINT64_C(0xc000000000000001);\n#else\n\talignment = 0x40000000LU;\n\tsize      = 0xc0000001LU;\n#endif\n\tassert_d_ne(posix_memalign(&p, alignment, size), 0,\n\t    \"Expected error for posix_memalign(&p, %zu, %zu)\",\n\t    alignment, size);\n\n\talignment = 0x10LU;\n#if LG_SIZEOF_PTR == 3\n\tsize = UINT64_C(0xfffffffffffffff0);\n#else\n\tsize = 0xfffffff0LU;\n#endif\n\tassert_d_ne(posix_memalign(&p, alignment, size), 0,\n\t    \"Expected error for posix_memalign(&p, %zu, %zu)\",\n\t    alignment, size);\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n#define NITER 4\n\tsize_t alignment, size, total;\n\tunsigned i;\n\tint err;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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\tchar buf[BUFERROR_BUF];\n\n\t\t\t\t\tbuferror(get_errno(), buf, sizeof(buf));\n\t\t\t\t\ttest_fail(\n\t\t\t\t\t    \"Error for alignment=%zu, \"\n\t\t\t\t\t    \"size=%zu (%#zx): %s\",\n\t\t\t\t\t    alignment, size, size, buf);\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\t}\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\tpurge();\n\t}\n#undef NITER\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_alignment_errors,\n\t    test_oom_errors,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/rallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\nTEST_BEGIN(test_grow_and_shrink) {\n\tvoid *p, *q;\n\tsize_t tsz;\n#define NCYCLES 3\n\tunsigned i, j;\n#define NSZS 1024\n\tsize_t szs[NSZS];\n#define MAXSZ ZU(12 * 1024 * 1024)\n\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tszs[0] = sallocx(p, 0);\n\n\tfor (i = 0; i < NCYCLES; i++) {\n\t\tfor (j = 1; j < NSZS && szs[j-1] < MAXSZ; j++) {\n\t\t\tq = rallocx(p, szs[j-1]+1, 0);\n\t\t\tassert_ptr_not_null(q,\n\t\t\t    \"Unexpected rallocx() error for size=%zu-->%zu\",\n\t\t\t    szs[j-1], szs[j-1]+1);\n\t\t\tszs[j] = sallocx(q, 0);\n\t\t\tassert_zu_ne(szs[j], szs[j-1]+1,\n\t\t\t    \"Expected size to be at least: %zu\", szs[j-1]+1);\n\t\t\tp = q;\n\t\t}\n\n\t\tfor (j--; j > 0; j--) {\n\t\t\tq = rallocx(p, szs[j-1], 0);\n\t\t\tassert_ptr_not_null(q,\n\t\t\t    \"Unexpected rallocx() error for size=%zu-->%zu\",\n\t\t\t    szs[j], szs[j-1]);\n\t\t\ttsz = sallocx(q, 0);\n\t\t\tassert_zu_eq(tsz, szs[j-1],\n\t\t\t    \"Expected size=%zu, got size=%zu\", szs[j-1], tsz);\n\t\t\tp = q;\n\t\t}\n\t}\n\n\tdallocx(p, 0);\n#undef MAXSZ\n#undef NSZS\n#undef NCYCLES\n}\nTEST_END\n\nstatic bool\nvalidate_fill(const void *p, uint8_t c, size_t offset, size_t len) {\n\tbool ret = false;\n\tconst uint8_t *buf = (const uint8_t *)p;\n\tsize_t i;\n\n\tfor (i = 0; i < len; i++) {\n\t\tuint8_t b = buf[offset+i];\n\t\tif (b != c) {\n\t\t\ttest_fail(\"Allocation at %p (len=%zu) contains %#x \"\n\t\t\t    \"rather than %#x at offset %zu\", p, len, b, c,\n\t\t\t    offset+i);\n\t\t\tret = true;\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nTEST_BEGIN(test_zero) {\n\tvoid *p, *q;\n\tsize_t psz, qsz, i, j;\n\tsize_t start_sizes[] = {1, 3*1024, 63*1024, 4095*1024};\n#define FILL_BYTE 0xaaU\n#define RANGE 2048\n\n\tfor (i = 0; i < sizeof(start_sizes)/sizeof(size_t); i++) {\n\t\tsize_t start_size = start_sizes[i];\n\t\tp = mallocx(start_size, MALLOCX_ZERO);\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\t\tpsz = sallocx(p, 0);\n\n\t\tassert_false(validate_fill(p, 0, 0, psz),\n\t\t    \"Expected zeroed memory\");\n\t\tmemset(p, FILL_BYTE, psz);\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, psz),\n\t\t    \"Expected filled memory\");\n\n\t\tfor (j = 1; j < RANGE; j++) {\n\t\t\tq = rallocx(p, start_size+j, MALLOCX_ZERO);\n\t\t\tassert_ptr_not_null(q, \"Unexpected rallocx() error\");\n\t\t\tqsz = sallocx(q, 0);\n\t\t\tif (q != p || qsz != psz) {\n\t\t\t\tassert_false(validate_fill(q, FILL_BYTE, 0,\n\t\t\t\t    psz), \"Expected filled memory\");\n\t\t\t\tassert_false(validate_fill(q, 0, psz, qsz-psz),\n\t\t\t\t    \"Expected zeroed memory\");\n\t\t\t}\n\t\t\tif (psz != qsz) {\n\t\t\t\tmemset((void *)((uintptr_t)q+psz), FILL_BYTE,\n\t\t\t\t    qsz-psz);\n\t\t\t\tpsz = qsz;\n\t\t\t}\n\t\t\tp = q;\n\t\t}\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, psz),\n\t\t    \"Expected filled memory\");\n\t\tdallocx(p, 0);\n\t}\n#undef FILL_BYTE\n}\nTEST_END\n\nTEST_BEGIN(test_align) {\n\tvoid *p, *q;\n\tsize_t align;\n#define MAX_ALIGN (ZU(1) << 25)\n\n\talign = ZU(1);\n\tp = mallocx(1, MALLOCX_ALIGN(align));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tfor (align <<= 1; align <= MAX_ALIGN; align <<= 1) {\n\t\tq = rallocx(p, 1, MALLOCX_ALIGN(align));\n\t\tassert_ptr_not_null(q,\n\t\t    \"Unexpected rallocx() error for align=%zu\", align);\n\t\tassert_ptr_null(\n\t\t    (void *)((uintptr_t)q & (align-1)),\n\t\t    \"%p inadequately aligned for align=%zu\",\n\t\t    q, align);\n\t\tp = q;\n\t}\n\tdallocx(p, 0);\n#undef MAX_ALIGN\n}\nTEST_END\n\nTEST_BEGIN(test_lg_align_and_zero) {\n\tvoid *p, *q;\n\tunsigned lg_align;\n\tsize_t sz;\n#define MAX_LG_ALIGN 25\n#define MAX_VALIDATE (ZU(1) << 22)\n\n\tlg_align = 0;\n\tp = mallocx(1, MALLOCX_LG_ALIGN(lg_align)|MALLOCX_ZERO);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tfor (lg_align++; lg_align <= MAX_LG_ALIGN; lg_align++) {\n\t\tq = rallocx(p, 1, MALLOCX_LG_ALIGN(lg_align)|MALLOCX_ZERO);\n\t\tassert_ptr_not_null(q,\n\t\t    \"Unexpected rallocx() error for lg_align=%u\", lg_align);\n\t\tassert_ptr_null(\n\t\t    (void *)((uintptr_t)q & ((ZU(1) << lg_align)-1)),\n\t\t    \"%p inadequately aligned for lg_align=%u\", q, lg_align);\n\t\tsz = sallocx(q, 0);\n\t\tif ((sz << 1) <= MAX_VALIDATE) {\n\t\t\tassert_false(validate_fill(q, 0, 0, sz),\n\t\t\t    \"Expected zeroed memory\");\n\t\t} else {\n\t\t\tassert_false(validate_fill(q, 0, 0, MAX_VALIDATE),\n\t\t\t    \"Expected zeroed memory\");\n\t\t\tassert_false(validate_fill(\n\t\t\t    (void *)((uintptr_t)q+sz-MAX_VALIDATE),\n\t\t\t    0, 0, MAX_VALIDATE), \"Expected zeroed memory\");\n\t\t}\n\t\tp = q;\n\t}\n\tdallocx(p, 0);\n#undef MAX_VALIDATE\n#undef MAX_LG_ALIGN\n}\nTEST_END\n\nTEST_BEGIN(test_overflow) {\n\tsize_t largemax;\n\tvoid *p;\n\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_ptr_null(rallocx(p, largemax+1, 0),\n\t    \"Expected OOM for rallocx(p, size=%#zx, 0)\", largemax+1);\n\n\tassert_ptr_null(rallocx(p, ZU(PTRDIFF_MAX)+1, 0),\n\t    \"Expected OOM for rallocx(p, size=%#zx, 0)\", ZU(PTRDIFF_MAX)+1);\n\n\tassert_ptr_null(rallocx(p, SIZE_T_MAX, 0),\n\t    \"Expected OOM for rallocx(p, size=%#zx, 0)\", SIZE_T_MAX);\n\n\tassert_ptr_null(rallocx(p, 1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),\n\t    \"Expected OOM for rallocx(p, size=1, MALLOCX_ALIGN(%#zx))\",\n\t    ZU(PTRDIFF_MAX)+1);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_grow_and_shrink,\n\t    test_zero,\n\t    test_align,\n\t    test_lg_align_and_zero,\n\t    test_overflow);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/sdallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAXALIGN (((size_t)1) << 22)\n#define NITER 3\n\nTEST_BEGIN(test_basic) {\n\tvoid *ptr = mallocx(64, 0);\n\tsdallocx(ptr, 64, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_alignment_and_size) {\n\tsize_t nsz, sz, alignment, total;\n\tunsigned i;\n\tvoid *ps[NITER];\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tps[i] = NULL;\n\t}\n\n\tfor (alignment = 8;\n\t    alignment <= MAXALIGN;\n\t    alignment <<= 1) {\n\t\ttotal = 0;\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 = nallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\tps[i] = mallocx(sz, MALLOCX_ALIGN(alignment) |\n\t\t\t\t    MALLOCX_ZERO);\n\t\t\t\ttotal += nsz;\n\t\t\t\tif (total >= (MAXALIGN << 1)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\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\tsdallocx(ps[i], sz,\n\t\t\t\t\t    MALLOCX_ALIGN(alignment));\n\t\t\t\t\tps[i] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_basic,\n\t    test_alignment_and_size);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/thread_arena.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS 10\n\nvoid *\nthd_start(void *arg) {\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\tassert_ptr_not_null(p, \"Error in malloc()\");\n\tfree(p);\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", (void *)&arena_ind, &size,\n\t    (void *)&main_arena_ind, sizeof(main_arena_ind)))) {\n\t\tchar buf[BUFERROR_BUF];\n\n\t\tbuferror(err, buf, sizeof(buf));\n\t\ttest_fail(\"Error in mallctl(): %s\", buf);\n\t}\n\n\tsize = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", (void *)&arena_ind, &size, NULL,\n\t    0))) {\n\t\tchar buf[BUFERROR_BUF];\n\n\t\tbuferror(err, buf, sizeof(buf));\n\t\ttest_fail(\"Error in mallctl(): %s\", buf);\n\t}\n\tassert_u_eq(arena_ind, main_arena_ind,\n\t    \"Arena index should be same as for main thread\");\n\n\treturn NULL;\n}\n\nstatic void\nmallctl_failure(int err) {\n\tchar buf[BUFERROR_BUF];\n\n\tbuferror(err, buf, sizeof(buf));\n\ttest_fail(\"Error in mallctl(): %s\", buf);\n}\n\nTEST_BEGIN(test_thread_arena) {\n\tvoid *p;\n\tint err;\n\tthd_t thds[NTHREADS];\n\tunsigned i;\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Error in malloc()\");\n\n\tunsigned arena_ind, old_arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Arena creation failure\");\n\n\tsize_t size = sizeof(arena_ind);\n\tif ((err = mallctl(\"thread.arena\", (void *)&old_arena_ind, &size,\n\t    (void *)&arena_ind, sizeof(arena_ind))) != 0) {\n\t\tmallctl_failure(err);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_create(&thds[i], thd_start,\n\t\t    (void *)&arena_ind);\n\t}\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tintptr_t join_ret;\n\t\tthd_join(thds[i], (void *)&join_ret);\n\t\tassert_zd_eq(join_ret, 0, \"Unexpected thread join error\");\n\t}\n\tfree(p);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_thread_arena);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/thread_tcache_enabled.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nvoid *\nthd_start(void *arg) {\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\tif (e0) {\n\t\te1 = false;\n\t\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\t\tassert_true(e0, \"tcache should be enabled\");\n\t}\n\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\tfree(malloc(1));\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\tfree(malloc(1));\n\te1 = true;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\tfree(malloc(1));\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_true(e0, \"tcache should be enabled\");\n\n\tfree(malloc(1));\n\te1 = false;\n\tassert_d_eq(mallctl(\"thread.tcache.enabled\", (void *)&e0, &sz,\n\t    (void *)&e1, sz), 0, \"Unexpected mallctl() error\");\n\tassert_false(e0, \"tcache should be disabled\");\n\n\tfree(malloc(1));\n\treturn NULL;\n}\n\nTEST_BEGIN(test_main_thread) {\n\tthd_start(NULL);\n}\nTEST_END\n\nTEST_BEGIN(test_subthread) {\n\tthd_t thd;\n\n\tthd_create(&thd, thd_start, NULL);\n\tthd_join(thd, NULL);\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Run tests multiple times to check for bad interactions. */\n\treturn test(\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread,\n\t    test_subthread,\n\t    test_main_thread);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/xallocx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * Use a separate arena for xallocx() extension/contraction tests so that\n * internal allocation e.g. by heap profiling can't interpose allocations where\n * xallocx() would ordinarily be able to extend.\n */\nstatic unsigned\narena_ind(void) {\n\tstatic unsigned ind = 0;\n\n\tif (ind == 0) {\n\t\tsize_t sz = sizeof(ind);\n\t\tassert_d_eq(mallctl(\"arenas.create\", (void *)&ind, &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl failure creating arena\");\n\t}\n\n\treturn ind;\n}\n\nTEST_BEGIN(test_same_size) {\n\tvoid *p;\n\tsize_t sz, tsz;\n\n\tp = mallocx(42, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tsz = sallocx(p, 0);\n\n\ttsz = xallocx(p, sz, 0, 0);\n\tassert_zu_eq(tsz, sz, \"Unexpected size change: %zu --> %zu\", sz, tsz);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_extra_no_move) {\n\tvoid *p;\n\tsize_t sz, tsz;\n\n\tp = mallocx(42, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tsz = sallocx(p, 0);\n\n\ttsz = xallocx(p, sz, sz-42, 0);\n\tassert_zu_eq(tsz, sz, \"Unexpected size change: %zu --> %zu\", sz, tsz);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_no_move_fail) {\n\tvoid *p;\n\tsize_t sz, tsz;\n\n\tp = mallocx(42, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tsz = sallocx(p, 0);\n\n\ttsz = xallocx(p, sz + 5, 0, 0);\n\tassert_zu_eq(tsz, sz, \"Unexpected size change: %zu --> %zu\", sz, tsz);\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nsmall(void) {\n\treturn get_nsizes_impl(\"arenas.nbins\");\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_small_size(size_t ind) {\n\treturn get_size_impl(\"arenas.bin.0.size\", ind);\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\nTEST_BEGIN(test_size) {\n\tsize_t small0, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmall0 = get_small_size(0);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(small0, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\t/* Test smallest supported size. */\n\tassert_zu_eq(xallocx(p, 1, 0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test largest supported size. */\n\tassert_zu_le(xallocx(p, largemax, 0, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test size overflow. */\n\tassert_zu_le(xallocx(p, largemax+1, 0, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_size_extra_overflow) {\n\tsize_t small0, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmall0 = get_small_size(0);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(small0, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\t/* Test overflows that can be resolved by clamping extra. */\n\tassert_zu_le(xallocx(p, largemax-1, 2, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, largemax, 1, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test overflow such that largemax-size underflows. */\n\tassert_zu_le(xallocx(p, largemax+1, 2, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, largemax+2, 3, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_extra_small) {\n\tsize_t small0, small1, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmall0 = get_small_size(0);\n\tsmall1 = get_small_size(1);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(small0, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tassert_zu_eq(xallocx(p, small1, 0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_eq(xallocx(p, small1, 0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\t/* Test size+extra overflow. */\n\tassert_zu_eq(xallocx(p, small0, largemax - small0 + 1, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_extra_large) {\n\tint flags = MALLOCX_ARENA(arena_ind());\n\tsize_t smallmax, large1, large2, large3, largemax;\n\tvoid *p;\n\n\t/* Get size classes. */\n\tsmallmax = get_small_size(get_nsmall()-1);\n\tlarge1 = get_large_size(1);\n\tlarge2 = get_large_size(2);\n\tlarge3 = get_large_size(3);\n\tlargemax = get_large_size(get_nlarge()-1);\n\n\tp = mallocx(large3, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\n\tassert_zu_eq(xallocx(p, large3, 0, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size decrease with zero extra. */\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_ge(xallocx(p, smallmax, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\n\tif (xallocx(p, large3, 0, flags) != large3) {\n\t\tp = rallocx(p, large3, flags);\n\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t}\n\t/* Test size decrease with non-zero extra. */\n\tassert_zu_eq(xallocx(p, large1, large3 - large1, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_eq(xallocx(p, large2, large3 - large2, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_ge(xallocx(p, large1, large2 - large1, flags), large2,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_ge(xallocx(p, smallmax, large1 - smallmax, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size increase with zero extra. */\n\tassert_zu_le(xallocx(p, large3, 0, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\tassert_zu_le(xallocx(p, largemax+1, 0, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size increase with non-zero extra. */\n\tassert_zu_le(xallocx(p, large1, SIZE_T_MAX - large1, flags), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tassert_zu_ge(xallocx(p, large1, 0, flags), large1,\n\t    \"Unexpected xallocx() behavior\");\n\t/* Test size increase with non-zero extra. */\n\tassert_zu_le(xallocx(p, large1, large3 - large1, flags), large3,\n\t    \"Unexpected xallocx() behavior\");\n\n\tif (xallocx(p, large3, 0, flags) != large3) {\n\t\tp = rallocx(p, large3, flags);\n\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t}\n\t/* Test size+extra overflow. */\n\tassert_zu_le(xallocx(p, large3, largemax - large3 + 1, flags), largemax,\n\t    \"Unexpected xallocx() behavior\");\n\n\tdallocx(p, flags);\n}\nTEST_END\n\nstatic void\nprint_filled_extents(const void *p, uint8_t c, size_t len) {\n\tconst uint8_t *pc = (const uint8_t *)p;\n\tsize_t i, range0;\n\tuint8_t c0;\n\n\tmalloc_printf(\"  p=%p, c=%#x, len=%zu:\", p, c, len);\n\trange0 = 0;\n\tc0 = pc[0];\n\tfor (i = 0; i < len; i++) {\n\t\tif (pc[i] != c0) {\n\t\t\tmalloc_printf(\" %#x[%zu..%zu)\", c0, range0, i);\n\t\t\trange0 = i;\n\t\t\tc0 = pc[i];\n\t\t}\n\t}\n\tmalloc_printf(\" %#x[%zu..%zu)\\n\", c0, range0, i);\n}\n\nstatic bool\nvalidate_fill(const void *p, uint8_t c, size_t offset, size_t len) {\n\tconst uint8_t *pc = (const uint8_t *)p;\n\tbool err;\n\tsize_t i;\n\n\tfor (i = offset, err = false; i < offset+len; i++) {\n\t\tif (pc[i] != c) {\n\t\t\terr = true;\n\t\t}\n\t}\n\n\tif (err) {\n\t\tprint_filled_extents(p, c, offset + len);\n\t}\n\n\treturn err;\n}\n\nstatic void\ntest_zero(size_t szmin, size_t szmax) {\n\tint flags = MALLOCX_ARENA(arena_ind()) | MALLOCX_ZERO;\n\tsize_t sz, nsz;\n\tvoid *p;\n#define FILL_BYTE 0x7aU\n\n\tsz = szmax;\n\tp = mallocx(sz, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() error\");\n\tassert_false(validate_fill(p, 0x00, 0, sz), \"Memory not filled: sz=%zu\",\n\t    sz);\n\n\t/*\n\t * Fill with non-zero so that non-debug builds are more likely to detect\n\t * errors.\n\t */\n\tmemset(p, FILL_BYTE, sz);\n\tassert_false(validate_fill(p, FILL_BYTE, 0, sz),\n\t    \"Memory not filled: sz=%zu\", sz);\n\n\t/* Shrink in place so that we can expect growing in place to succeed. */\n\tsz = szmin;\n\tif (xallocx(p, sz, 0, flags) != sz) {\n\t\tp = rallocx(p, sz, flags);\n\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t}\n\tassert_false(validate_fill(p, FILL_BYTE, 0, sz),\n\t    \"Memory not filled: sz=%zu\", sz);\n\n\tfor (sz = szmin; sz < szmax; sz = nsz) {\n\t\tnsz = nallocx(sz+1, flags);\n\t\tif (xallocx(p, sz+1, 0, flags) != nsz) {\n\t\t\tp = rallocx(p, sz+1, flags);\n\t\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t\t}\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, sz),\n\t\t    \"Memory not filled: sz=%zu\", sz);\n\t\tassert_false(validate_fill(p, 0x00, sz, nsz-sz),\n\t\t    \"Memory not filled: sz=%zu, nsz-sz=%zu\", sz, nsz-sz);\n\t\tmemset((void *)((uintptr_t)p + sz), FILL_BYTE, nsz-sz);\n\t\tassert_false(validate_fill(p, FILL_BYTE, 0, nsz),\n\t\t    \"Memory not filled: nsz=%zu\", nsz);\n\t}\n\n\tdallocx(p, flags);\n}\n\nTEST_BEGIN(test_zero_large) {\n\tsize_t large0, large1;\n\n\t/* Get size classes. */\n\tlarge0 = get_large_size(0);\n\tlarge1 = get_large_size(1);\n\n\ttest_zero(large1, large0 * 2);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_same_size,\n\t    test_extra_no_move,\n\t    test_no_move_fail,\n\t    test_size,\n\t    test_size_extra_overflow,\n\t    test_extra_small,\n\t    test_extra_large,\n\t    test_zero_large);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/integration/xallocx.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"junk:false\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/SFMT.c",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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 * @file  SFMT.c\n * @brief SIMD oriented Fast Mersenne Twister(SFMT)\n *\n * @author Mutsuo Saito (Hiroshima University)\n * @author Makoto Matsumoto (Hiroshima University)\n *\n * Copyright (C) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n * University. All rights reserved.\n *\n * The new BSD License is applied to this software, see LICENSE.txt\n */\n#define SFMT_C_\n#include \"test/jemalloc_test.h\"\n#include \"test/SFMT-params.h\"\n\n#if defined(JEMALLOC_BIG_ENDIAN) && !defined(BIG_ENDIAN64)\n#define BIG_ENDIAN64 1\n#endif\n#if defined(__BIG_ENDIAN__) && !defined(__amd64) && !defined(BIG_ENDIAN64)\n#define BIG_ENDIAN64 1\n#endif\n#if defined(HAVE_ALTIVEC) && !defined(BIG_ENDIAN64)\n#define BIG_ENDIAN64 1\n#endif\n#if defined(ONLY64) && !defined(BIG_ENDIAN64)\n  #if defined(__GNUC__)\n    #error \"-DONLY64 must be specified with -DBIG_ENDIAN64\"\n  #endif\n#undef ONLY64\n#endif\n/*------------------------------------------------------\n  128-bit SIMD data type for Altivec, SSE2 or standard C\n  ------------------------------------------------------*/\n#if defined(HAVE_ALTIVEC)\n/** 128-bit data structure */\nunion W128_T {\n    vector unsigned int s;\n    uint32_t u[4];\n};\n/** 128-bit data type */\ntypedef union W128_T w128_t;\n\n#elif defined(HAVE_SSE2)\n/** 128-bit data structure */\nunion W128_T {\n    __m128i si;\n    uint32_t u[4];\n};\n/** 128-bit data type */\ntypedef union W128_T w128_t;\n\n#else\n\n/** 128-bit data structure */\nstruct W128_T {\n    uint32_t u[4];\n};\n/** 128-bit data type */\ntypedef struct W128_T w128_t;\n\n#endif\n\nstruct sfmt_s {\n    /** the 128-bit internal state array */\n    w128_t sfmt[N];\n    /** index counter to the 32-bit internal state array */\n    int idx;\n    /** a flag: it is 0 if and only if the internal state is not yet\n     * initialized. */\n    int initialized;\n};\n\n/*--------------------------------------\n  FILE GLOBAL VARIABLES\n  internal state, index counter and flag\n  --------------------------------------*/\n\n/** a parity check vector which certificate the period of 2^{MEXP} */\nstatic uint32_t parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};\n\n/*----------------\n  STATIC FUNCTIONS\n  ----------------*/\nstatic inline int idxof(int i);\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\nstatic inline void rshift128(w128_t *out,  w128_t const *in, int shift);\nstatic inline void lshift128(w128_t *out,  w128_t const *in, int shift);\n#endif\nstatic inline void gen_rand_all(sfmt_t *ctx);\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size);\nstatic inline uint32_t func1(uint32_t x);\nstatic inline uint32_t func2(uint32_t x);\nstatic void period_certification(sfmt_t *ctx);\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\nstatic inline void swap(w128_t *array, int size);\n#endif\n\n#if defined(HAVE_ALTIVEC)\n  #include \"test/SFMT-alti.h\"\n#elif defined(HAVE_SSE2)\n  #include \"test/SFMT-sse2.h\"\n#endif\n\n/**\n * This function simulate a 64-bit index of LITTLE ENDIAN\n * in BIG ENDIAN machine.\n */\n#ifdef ONLY64\nstatic inline int idxof(int i) {\n    return i ^ 1;\n}\n#else\nstatic inline int idxof(int i) {\n    return i;\n}\n#endif\n/**\n * This function simulates SIMD 128-bit right shift by the standard C.\n * The 128-bit integer given in in is shifted by (shift * 8) bits.\n * This function simulates the LITTLE ENDIAN SIMD.\n * @param out the output of this function\n * @param in the 128-bit data to be shifted\n * @param shift the shift value\n */\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\n#ifdef ONLY64\nstatic inline void rshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);\n    tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);\n\n    oh = th >> (shift * 8);\n    ol = tl >> (shift * 8);\n    ol |= th << (64 - shift * 8);\n    out->u[0] = (uint32_t)(ol >> 32);\n    out->u[1] = (uint32_t)ol;\n    out->u[2] = (uint32_t)(oh >> 32);\n    out->u[3] = (uint32_t)oh;\n}\n#else\nstatic inline void rshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);\n    tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);\n\n    oh = th >> (shift * 8);\n    ol = tl >> (shift * 8);\n    ol |= th << (64 - shift * 8);\n    out->u[1] = (uint32_t)(ol >> 32);\n    out->u[0] = (uint32_t)ol;\n    out->u[3] = (uint32_t)(oh >> 32);\n    out->u[2] = (uint32_t)oh;\n}\n#endif\n/**\n * This function simulates SIMD 128-bit left shift by the standard C.\n * The 128-bit integer given in in is shifted by (shift * 8) bits.\n * This function simulates the LITTLE ENDIAN SIMD.\n * @param out the output of this function\n * @param in the 128-bit data to be shifted\n * @param shift the shift value\n */\n#ifdef ONLY64\nstatic inline void lshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);\n    tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);\n\n    oh = th << (shift * 8);\n    ol = tl << (shift * 8);\n    oh |= tl >> (64 - shift * 8);\n    out->u[0] = (uint32_t)(ol >> 32);\n    out->u[1] = (uint32_t)ol;\n    out->u[2] = (uint32_t)(oh >> 32);\n    out->u[3] = (uint32_t)oh;\n}\n#else\nstatic inline void lshift128(w128_t *out, w128_t const *in, int shift) {\n    uint64_t th, tl, oh, ol;\n\n    th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);\n    tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);\n\n    oh = th << (shift * 8);\n    ol = tl << (shift * 8);\n    oh |= tl >> (64 - shift * 8);\n    out->u[1] = (uint32_t)(ol >> 32);\n    out->u[0] = (uint32_t)ol;\n    out->u[3] = (uint32_t)(oh >> 32);\n    out->u[2] = (uint32_t)oh;\n}\n#endif\n#endif\n\n/**\n * This function represents the recursion formula.\n * @param r output\n * @param a a 128-bit part of the internal state array\n * @param b a 128-bit part of the internal state array\n * @param c a 128-bit part of the internal state array\n * @param d a 128-bit part of the internal state array\n */\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\n#ifdef ONLY64\nstatic inline void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,\n\t\t\t\tw128_t *d) {\n    w128_t x;\n    w128_t y;\n\n    lshift128(&x, a, SL2);\n    rshift128(&y, c, SR2);\n    r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0]\n\t^ (d->u[0] << SL1);\n    r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1]\n\t^ (d->u[1] << SL1);\n    r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2]\n\t^ (d->u[2] << SL1);\n    r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3]\n\t^ (d->u[3] << SL1);\n}\n#else\nstatic inline void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,\n\t\t\t\tw128_t *d) {\n    w128_t x;\n    w128_t y;\n\n    lshift128(&x, a, SL2);\n    rshift128(&y, c, SR2);\n    r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0]\n\t^ (d->u[0] << SL1);\n    r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1]\n\t^ (d->u[1] << SL1);\n    r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2]\n\t^ (d->u[2] << SL1);\n    r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3]\n\t^ (d->u[3] << SL1);\n}\n#endif\n#endif\n\n#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))\n/**\n * This function fills the internal state array with pseudorandom\n * integers.\n */\nstatic inline void gen_rand_all(sfmt_t *ctx) {\n    int i;\n    w128_t *r1, *r2;\n\n    r1 = &ctx->sfmt[N - 2];\n    r2 = &ctx->sfmt[N - 1];\n    for (i = 0; i < N - POS1; i++) {\n\tdo_recursion(&ctx->sfmt[i], &ctx->sfmt[i], &ctx->sfmt[i + POS1], r1,\n\t  r2);\n\tr1 = r2;\n\tr2 = &ctx->sfmt[i];\n    }\n    for (; i < N; i++) {\n\tdo_recursion(&ctx->sfmt[i], &ctx->sfmt[i], &ctx->sfmt[i + POS1 - N], r1,\n\t  r2);\n\tr1 = r2;\n\tr2 = &ctx->sfmt[i];\n    }\n}\n\n/**\n * This function fills the user-specified array with pseudorandom\n * integers.\n *\n * @param array an 128-bit array to be filled by pseudorandom numbers.\n * @param size number of 128-bit pseudorandom numbers to be generated.\n */\nstatic inline void gen_rand_array(sfmt_t *ctx, w128_t *array, int size) {\n    int i, j;\n    w128_t *r1, *r2;\n\n    r1 = &ctx->sfmt[N - 2];\n    r2 = &ctx->sfmt[N - 1];\n    for (i = 0; i < N - POS1; i++) {\n\tdo_recursion(&array[i], &ctx->sfmt[i], &ctx->sfmt[i + POS1], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n    }\n    for (; i < N; i++) {\n\tdo_recursion(&array[i], &ctx->sfmt[i], &array[i + POS1 - N], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n    }\n    for (; i < size - N; i++) {\n\tdo_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n    }\n    for (j = 0; j < 2 * N - size; j++) {\n\tctx->sfmt[j] = array[j + size - N];\n    }\n    for (; i < size; i++, j++) {\n\tdo_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);\n\tr1 = r2;\n\tr2 = &array[i];\n\tctx->sfmt[j] = array[i];\n    }\n}\n#endif\n\n#if defined(BIG_ENDIAN64) && !defined(ONLY64) && !defined(HAVE_ALTIVEC)\nstatic inline void swap(w128_t *array, int size) {\n    int i;\n    uint32_t x, y;\n\n    for (i = 0; i < size; i++) {\n\tx = array[i].u[0];\n\ty = array[i].u[2];\n\tarray[i].u[0] = array[i].u[1];\n\tarray[i].u[2] = array[i].u[3];\n\tarray[i].u[1] = x;\n\tarray[i].u[3] = y;\n    }\n}\n#endif\n/**\n * This function represents a function used in the initialization\n * by init_by_array\n * @param x 32-bit integer\n * @return 32-bit integer\n */\nstatic uint32_t func1(uint32_t x) {\n    return (x ^ (x >> 27)) * (uint32_t)1664525UL;\n}\n\n/**\n * This function represents a function used in the initialization\n * by init_by_array\n * @param x 32-bit integer\n * @return 32-bit integer\n */\nstatic uint32_t func2(uint32_t x) {\n    return (x ^ (x >> 27)) * (uint32_t)1566083941UL;\n}\n\n/**\n * This function certificate the period of 2^{MEXP}\n */\nstatic void period_certification(sfmt_t *ctx) {\n    int inner = 0;\n    int i, j;\n    uint32_t work;\n    uint32_t *psfmt32 = &ctx->sfmt[0].u[0];\n\n    for (i = 0; i < 4; i++)\n\tinner ^= psfmt32[idxof(i)] & parity[i];\n    for (i = 16; i > 0; i >>= 1)\n\tinner ^= inner >> i;\n    inner &= 1;\n    /* check OK */\n    if (inner == 1) {\n\treturn;\n    }\n    /* check NG, and modification */\n    for (i = 0; i < 4; i++) {\n\twork = 1;\n\tfor (j = 0; j < 32; j++) {\n\t    if ((work & parity[i]) != 0) {\n\t\tpsfmt32[idxof(i)] ^= work;\n\t\treturn;\n\t    }\n\t    work = work << 1;\n\t}\n    }\n}\n\n/*----------------\n  PUBLIC FUNCTIONS\n  ----------------*/\n/**\n * This function returns the identification string.\n * The string shows the word size, the Mersenne exponent,\n * and all parameters of this generator.\n */\nconst char *get_idstring(void) {\n    return IDSTR;\n}\n\n/**\n * This function returns the minimum size of array used for \\b\n * fill_array32() function.\n * @return minimum size of array used for fill_array32() function.\n */\nint get_min_array_size32(void) {\n    return N32;\n}\n\n/**\n * This function returns the minimum size of array used for \\b\n * fill_array64() function.\n * @return minimum size of array used for fill_array64() function.\n */\nint get_min_array_size64(void) {\n    return N64;\n}\n\n#ifndef ONLY64\n/**\n * This function generates and returns 32-bit pseudorandom number.\n * init_gen_rand or init_by_array must be called before this function.\n * @return 32-bit pseudorandom number\n */\nuint32_t gen_rand32(sfmt_t *ctx) {\n    uint32_t r;\n    uint32_t *psfmt32 = &ctx->sfmt[0].u[0];\n\n    assert(ctx->initialized);\n    if (ctx->idx >= N32) {\n\tgen_rand_all(ctx);\n\tctx->idx = 0;\n    }\n    r = psfmt32[ctx->idx++];\n    return r;\n}\n\n/* Generate a random integer in [0..limit). */\nuint32_t gen_rand32_range(sfmt_t *ctx, uint32_t limit) {\n    uint32_t ret, above;\n\n    above = 0xffffffffU - (0xffffffffU % limit);\n    while (1) {\n\tret = gen_rand32(ctx);\n\tif (ret < above) {\n\t    ret %= limit;\n\t    break;\n\t}\n    }\n    return ret;\n}\n#endif\n/**\n * This function generates and returns 64-bit pseudorandom number.\n * init_gen_rand or init_by_array must be called before this function.\n * The function gen_rand64 should not be called after gen_rand32,\n * unless an initialization is again executed.\n * @return 64-bit pseudorandom number\n */\nuint64_t gen_rand64(sfmt_t *ctx) {\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\n    uint32_t r1, r2;\n    uint32_t *psfmt32 = &ctx->sfmt[0].u[0];\n#else\n    uint64_t r;\n    uint64_t *psfmt64 = (uint64_t *)&ctx->sfmt[0].u[0];\n#endif\n\n    assert(ctx->initialized);\n    assert(ctx->idx % 2 == 0);\n\n    if (ctx->idx >= N32) {\n\tgen_rand_all(ctx);\n\tctx->idx = 0;\n    }\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\n    r1 = psfmt32[ctx->idx];\n    r2 = psfmt32[ctx->idx + 1];\n    ctx->idx += 2;\n    return ((uint64_t)r2 << 32) | r1;\n#else\n    r = psfmt64[ctx->idx / 2];\n    ctx->idx += 2;\n    return r;\n#endif\n}\n\n/* Generate a random integer in [0..limit). */\nuint64_t gen_rand64_range(sfmt_t *ctx, uint64_t limit) {\n    uint64_t ret, above;\n\n    above = KQU(0xffffffffffffffff) - (KQU(0xffffffffffffffff) % limit);\n    while (1) {\n\tret = gen_rand64(ctx);\n\tif (ret < above) {\n\t    ret %= limit;\n\t    break;\n\t}\n    }\n    return ret;\n}\n\n#ifndef ONLY64\n/**\n * This function generates pseudorandom 32-bit integers in the\n * specified array[] by one call. The number of pseudorandom integers\n * is specified by the argument size, which must be at least 624 and a\n * multiple of four.  The generation by this function is much faster\n * than the following gen_rand function.\n *\n * For initialization, init_gen_rand or init_by_array must be called\n * before the first call of this function. This function can not be\n * used after calling gen_rand function, without initialization.\n *\n * @param array an array where pseudorandom 32-bit integers are filled\n * by this function.  The pointer to the array must be \\b \"aligned\"\n * (namely, must be a multiple of 16) in the SIMD version, since it\n * refers to the address of a 128-bit integer.  In the standard C\n * version, the pointer is arbitrary.\n *\n * @param size the number of 32-bit pseudorandom integers to be\n * generated.  size must be a multiple of 4, and greater than or equal\n * to (MEXP / 128 + 1) * 4.\n *\n * @note \\b memalign or \\b posix_memalign is available to get aligned\n * memory. Mac OSX doesn't have these functions, but \\b malloc of OSX\n * returns the pointer to the aligned memory block.\n */\nvoid fill_array32(sfmt_t *ctx, uint32_t *array, int size) {\n    assert(ctx->initialized);\n    assert(ctx->idx == N32);\n    assert(size % 4 == 0);\n    assert(size >= N32);\n\n    gen_rand_array(ctx, (w128_t *)array, size / 4);\n    ctx->idx = N32;\n}\n#endif\n\n/**\n * This function generates pseudorandom 64-bit integers in the\n * specified array[] by one call. The number of pseudorandom integers\n * is specified by the argument size, which must be at least 312 and a\n * multiple of two.  The generation by this function is much faster\n * than the following gen_rand function.\n *\n * For initialization, init_gen_rand or init_by_array must be called\n * before the first call of this function. This function can not be\n * used after calling gen_rand function, without initialization.\n *\n * @param array an array where pseudorandom 64-bit integers are filled\n * by this function.  The pointer to the array must be \"aligned\"\n * (namely, must be a multiple of 16) in the SIMD version, since it\n * refers to the address of a 128-bit integer.  In the standard C\n * version, the pointer is arbitrary.\n *\n * @param size the number of 64-bit pseudorandom integers to be\n * generated.  size must be a multiple of 2, and greater than or equal\n * to (MEXP / 128 + 1) * 2\n *\n * @note \\b memalign or \\b posix_memalign is available to get aligned\n * memory. Mac OSX doesn't have these functions, but \\b malloc of OSX\n * returns the pointer to the aligned memory block.\n */\nvoid fill_array64(sfmt_t *ctx, uint64_t *array, int size) {\n    assert(ctx->initialized);\n    assert(ctx->idx == N32);\n    assert(size % 2 == 0);\n    assert(size >= N64);\n\n    gen_rand_array(ctx, (w128_t *)array, size / 2);\n    ctx->idx = N32;\n\n#if defined(BIG_ENDIAN64) && !defined(ONLY64)\n    swap((w128_t *)array, size /2);\n#endif\n}\n\n/**\n * This function initializes the internal state array with a 32-bit\n * integer seed.\n *\n * @param seed a 32-bit integer used as the seed.\n */\nsfmt_t *init_gen_rand(uint32_t seed) {\n    void *p;\n    sfmt_t *ctx;\n    int i;\n    uint32_t *psfmt32;\n\n    if (posix_memalign(&p, sizeof(w128_t), sizeof(sfmt_t)) != 0) {\n\treturn NULL;\n    }\n    ctx = (sfmt_t *)p;\n    psfmt32 = &ctx->sfmt[0].u[0];\n\n    psfmt32[idxof(0)] = seed;\n    for (i = 1; i < N32; i++) {\n\tpsfmt32[idxof(i)] = 1812433253UL * (psfmt32[idxof(i - 1)]\n\t\t\t\t\t    ^ (psfmt32[idxof(i - 1)] >> 30))\n\t    + i;\n    }\n    ctx->idx = N32;\n    period_certification(ctx);\n    ctx->initialized = 1;\n\n    return ctx;\n}\n\n/**\n * This function initializes the internal state array,\n * with an array of 32-bit integers used as the seeds\n * @param init_key the array of 32-bit integers, used as a seed.\n * @param key_length the length of init_key.\n */\nsfmt_t *init_by_array(uint32_t *init_key, int key_length) {\n    void *p;\n    sfmt_t *ctx;\n    int i, j, count;\n    uint32_t r;\n    int lag;\n    int mid;\n    int size = N * 4;\n    uint32_t *psfmt32;\n\n    if (posix_memalign(&p, sizeof(w128_t), sizeof(sfmt_t)) != 0) {\n\treturn NULL;\n    }\n    ctx = (sfmt_t *)p;\n    psfmt32 = &ctx->sfmt[0].u[0];\n\n    if (size >= 623) {\n\tlag = 11;\n    } else if (size >= 68) {\n\tlag = 7;\n    } else if (size >= 39) {\n\tlag = 5;\n    } else {\n\tlag = 3;\n    }\n    mid = (size - lag) / 2;\n\n    memset(ctx->sfmt, 0x8b, sizeof(ctx->sfmt));\n    if (key_length + 1 > N32) {\n\tcount = key_length + 1;\n    } else {\n\tcount = N32;\n    }\n    r = func1(psfmt32[idxof(0)] ^ psfmt32[idxof(mid)]\n\t      ^ psfmt32[idxof(N32 - 1)]);\n    psfmt32[idxof(mid)] += r;\n    r += key_length;\n    psfmt32[idxof(mid + lag)] += r;\n    psfmt32[idxof(0)] = r;\n\n    count--;\n    for (i = 1, j = 0; (j < count) && (j < key_length); j++) {\n\tr = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)]\n\t\t  ^ psfmt32[idxof((i + N32 - 1) % N32)]);\n\tpsfmt32[idxof((i + mid) % N32)] += r;\n\tr += init_key[j] + i;\n\tpsfmt32[idxof((i + mid + lag) % N32)] += r;\n\tpsfmt32[idxof(i)] = r;\n\ti = (i + 1) % N32;\n    }\n    for (; j < count; j++) {\n\tr = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)]\n\t\t  ^ psfmt32[idxof((i + N32 - 1) % N32)]);\n\tpsfmt32[idxof((i + mid) % N32)] += r;\n\tr += i;\n\tpsfmt32[idxof((i + mid + lag) % N32)] += r;\n\tpsfmt32[idxof(i)] = r;\n\ti = (i + 1) % N32;\n    }\n    for (j = 0; j < N32; j++) {\n\tr = func2(psfmt32[idxof(i)] + psfmt32[idxof((i + mid) % N32)]\n\t\t  + psfmt32[idxof((i + N32 - 1) % N32)]);\n\tpsfmt32[idxof((i + mid) % N32)] ^= r;\n\tr -= i;\n\tpsfmt32[idxof((i + mid + lag) % N32)] ^= r;\n\tpsfmt32[idxof(i)] = r;\n\ti = (i + 1) % N32;\n    }\n\n    ctx->idx = N32;\n    period_certification(ctx);\n    ctx->initialized = 1;\n\n    return ctx;\n}\n\nvoid fini_gen_rand(sfmt_t *ctx) {\n    assert(ctx != NULL);\n\n    ctx->initialized = 0;\n    free(ctx);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/btalloc.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nvoid *\nbtalloc(size_t size, unsigned bits) {\n\treturn btalloc_0(size, bits);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/btalloc_0.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nbtalloc_n_gen(0)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/btalloc_1.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nbtalloc_n_gen(1)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/math.c",
    "content": "#define MATH_C_\n#include \"test/jemalloc_test.h\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/mq.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * Sleep for approximately ns nanoseconds.  No lower *nor* upper bound on sleep\n * time is guaranteed.\n */\nvoid\nmq_nanosleep(unsigned ns) {\n\tassert(ns <= 1000*1000*1000);\n\n#ifdef _WIN32\n\tSleep(ns / 1000);\n#else\n\t{\n\t\tstruct timespec timeout;\n\n\t\tif (ns < 1000*1000*1000) {\n\t\t\ttimeout.tv_sec = 0;\n\t\t\ttimeout.tv_nsec = ns;\n\t\t} else {\n\t\t\ttimeout.tv_sec = 1;\n\t\t\ttimeout.tv_nsec = 0;\n\t\t}\n\t\tnanosleep(&timeout, NULL);\n\t}\n#endif\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/mtx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#ifndef _CRT_SPINCOUNT\n#define _CRT_SPINCOUNT 4000\n#endif\n\nbool\nmtx_init(mtx_t *mtx) {\n#ifdef _WIN32\n\tif (!InitializeCriticalSectionAndSpinCount(&mtx->lock,\n\t    _CRT_SPINCOUNT)) {\n\t\treturn true;\n\t}\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tmtx->lock = OS_UNFAIR_LOCK_INIT;\n#elif (defined(JEMALLOC_OSSPIN))\n\tmtx->lock = 0;\n#else\n\tpthread_mutexattr_t attr;\n\n\tif (pthread_mutexattr_init(&attr) != 0) {\n\t\treturn true;\n\t}\n\tpthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);\n\tif (pthread_mutex_init(&mtx->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\nmtx_fini(mtx_t *mtx) {\n#ifdef _WIN32\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n#elif (defined(JEMALLOC_OSSPIN))\n#else\n\tpthread_mutex_destroy(&mtx->lock);\n#endif\n}\n\nvoid\nmtx_lock(mtx_t *mtx) {\n#ifdef _WIN32\n\tEnterCriticalSection(&mtx->lock);\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tos_unfair_lock_lock(&mtx->lock);\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLockLock(&mtx->lock);\n#else\n\tpthread_mutex_lock(&mtx->lock);\n#endif\n}\n\nvoid\nmtx_unlock(mtx_t *mtx) {\n#ifdef _WIN32\n\tLeaveCriticalSection(&mtx->lock);\n#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))\n\tos_unfair_lock_unlock(&mtx->lock);\n#elif (defined(JEMALLOC_OSSPIN))\n\tOSSpinLockUnlock(&mtx->lock);\n#else\n\tpthread_mutex_unlock(&mtx->lock);\n#endif\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/test.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/* Test status state. */\n\nstatic unsigned\t\ttest_count = 0;\nstatic test_status_t\ttest_counts[test_status_count] = {0, 0, 0};\nstatic test_status_t\ttest_status = test_status_pass;\nstatic const char *\ttest_name = \"\";\n\n/* Reentrancy testing helpers. */\n\n#define NUM_REENTRANT_ALLOCS 20\ntypedef enum {\n\tnon_reentrant = 0,\n\tlibc_reentrant = 1,\n\tarena_new_reentrant = 2\n} reentrancy_t;\nstatic reentrancy_t reentrancy;\n\nstatic bool libc_hook_ran = false;\nstatic bool arena_new_hook_ran = false;\n\nstatic const char *\nreentrancy_t_str(reentrancy_t r) {\n\tswitch (r) {\n\tcase non_reentrant:\n\t\treturn \"non-reentrant\";\n\tcase libc_reentrant:\n\t\treturn \"libc-reentrant\";\n\tcase arena_new_reentrant:\n\t\treturn \"arena_new-reentrant\";\n\tdefault:\n\t\tunreachable();\n\t}\n}\n\nstatic void\ndo_hook(bool *hook_ran, void (**hook)()) {\n\t*hook_ran = true;\n\t*hook = NULL;\n\n\tsize_t alloc_size = 1;\n\tfor (int i = 0; i < NUM_REENTRANT_ALLOCS; i++) {\n\t\tfree(malloc(alloc_size));\n\t\talloc_size *= 2;\n\t}\n}\n\nstatic void\nlibc_reentrancy_hook() {\n\tdo_hook(&libc_hook_ran, &hooks_libc_hook);\n}\n\nstatic void\narena_new_reentrancy_hook() {\n\tdo_hook(&arena_new_hook_ran, &hooks_arena_new_hook);\n}\n\n/* Actual test infrastructure. */\nbool\ntest_is_reentrant() {\n\treturn reentrancy != non_reentrant;\n}\n\nJEMALLOC_FORMAT_PRINTF(1, 2)\nvoid\ntest_skip(const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n\tmalloc_printf(\"\\n\");\n\ttest_status = test_status_skip;\n}\n\nJEMALLOC_FORMAT_PRINTF(1, 2)\nvoid\ntest_fail(const char *format, ...) {\n\tva_list ap;\n\n\tva_start(ap, format);\n\tmalloc_vcprintf(NULL, NULL, format, ap);\n\tva_end(ap);\n\tmalloc_printf(\"\\n\");\n\ttest_status = test_status_fail;\n}\n\nstatic const char *\ntest_status_string(test_status_t test_status) {\n\tswitch (test_status) {\n\tcase test_status_pass: return \"pass\";\n\tcase test_status_skip: return \"skip\";\n\tcase test_status_fail: return \"fail\";\n\tdefault: not_reached();\n\t}\n}\n\nvoid\np_test_init(const char *name) {\n\ttest_count++;\n\ttest_status = test_status_pass;\n\ttest_name = name;\n}\n\nvoid\np_test_fini(void) {\n\ttest_counts[test_status]++;\n\tmalloc_printf(\"%s (%s): %s\\n\", test_name, reentrancy_t_str(reentrancy),\n\t    test_status_string(test_status));\n}\n\nstatic test_status_t\np_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) {\n\ttest_status_t ret;\n\n\tif (do_malloc_init) {\n\t\t/*\n\t\t * Make sure initialization occurs prior to running tests.\n\t\t * Tests are special because they may use internal facilities\n\t\t * prior to triggering initialization as a side effect of\n\t\t * calling into the public API.\n\t\t */\n\t\tif (nallocx(1, 0) == 0) {\n\t\t\tmalloc_printf(\"Initialization error\");\n\t\t\treturn test_status_fail;\n\t\t}\n\t}\n\n\tret = test_status_pass;\n\tfor (; t != NULL; t = va_arg(ap, test_t *)) {\n\t\t/* Non-reentrant run. */\n\t\treentrancy = non_reentrant;\n\t\thooks_arena_new_hook = hooks_libc_hook = NULL;\n\t\tt();\n\t\tif (test_status > ret) {\n\t\t\tret = test_status;\n\t\t}\n\t\t/* Reentrant run. */\n\t\tif (do_reentrant) {\n\t\t\treentrancy = libc_reentrant;\n\t\t\thooks_arena_new_hook = NULL;\n\t\t\thooks_libc_hook = &libc_reentrancy_hook;\n\t\t\tt();\n\t\t\tif (test_status > ret) {\n\t\t\t\tret = test_status;\n\t\t\t}\n\n\t\t\treentrancy = arena_new_reentrant;\n\t\t\thooks_libc_hook = NULL;\n\t\t\thooks_arena_new_hook = &arena_new_reentrancy_hook;\n\t\t\tt();\n\t\t\tif (test_status > ret) {\n\t\t\t\tret = test_status;\n\t\t\t}\n\t\t}\n\t}\n\n\tmalloc_printf(\"--- %s: %u/%u, %s: %u/%u, %s: %u/%u ---\\n\",\n\t    test_status_string(test_status_pass),\n\t    test_counts[test_status_pass], test_count,\n\t    test_status_string(test_status_skip),\n\t    test_counts[test_status_skip], test_count,\n\t    test_status_string(test_status_fail),\n\t    test_counts[test_status_fail], test_count);\n\n\treturn ret;\n}\n\ntest_status_t\np_test(test_t *t, ...) {\n\ttest_status_t ret;\n\tva_list ap;\n\n\tret = test_status_pass;\n\tva_start(ap, t);\n\tret = p_test_impl(true, true, t, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\ntest_status_t\np_test_no_reentrancy(test_t *t, ...) {\n\ttest_status_t ret;\n\tva_list ap;\n\n\tret = test_status_pass;\n\tva_start(ap, t);\n\tret = p_test_impl(true, false, t, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\ntest_status_t\np_test_no_malloc_init(test_t *t, ...) {\n\ttest_status_t ret;\n\tva_list ap;\n\n\tret = test_status_pass;\n\tva_start(ap, t);\n\t/*\n\t * We also omit reentrancy from bootstrapping tests, since we don't\n\t * (yet) care about general reentrancy during bootstrapping.\n\t */\n\tret = p_test_impl(false, false, t, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\nvoid\np_test_fail(const char *prefix, const char *message) {\n\tmalloc_cprintf(NULL, NULL, \"%s%s\\n\", prefix, message);\n\ttest_status = test_status_fail;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/thd.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#ifdef _WIN32\nvoid\nthd_create(thd_t *thd, void *(*proc)(void *), void *arg) {\n\tLPTHREAD_START_ROUTINE routine = (LPTHREAD_START_ROUTINE)proc;\n\t*thd = CreateThread(NULL, 0, routine, arg, 0, NULL);\n\tif (*thd == NULL) {\n\t\ttest_fail(\"Error in CreateThread()\\n\");\n\t}\n}\n\nvoid\nthd_join(thd_t thd, void **ret) {\n\tif (WaitForSingleObject(thd, INFINITE) == WAIT_OBJECT_0 && ret) {\n\t\tDWORD exit_code;\n\t\tGetExitCodeThread(thd, (LPDWORD) &exit_code);\n\t\t*ret = (void *)(uintptr_t)exit_code;\n\t}\n}\n\n#else\nvoid\nthd_create(thd_t *thd, void *(*proc)(void *), void *arg) {\n\tif (pthread_create(thd, NULL, proc, arg) != 0) {\n\t\ttest_fail(\"Error in pthread_create()\\n\");\n\t}\n}\n\nvoid\nthd_join(thd_t thd, void **ret) {\n\tpthread_join(thd, ret);\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/src/timer.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nvoid\ntimer_start(timedelta_t *timer) {\n\tnstime_init(&timer->t0, 0);\n\tnstime_update(&timer->t0);\n}\n\nvoid\ntimer_stop(timedelta_t *timer) {\n\tnstime_copy(&timer->t1, &timer->t0);\n\tnstime_update(&timer->t1);\n}\n\nuint64_t\ntimer_usec(const timedelta_t *timer) {\n\tnstime_t delta;\n\n\tnstime_copy(&delta, &timer->t1);\n\tnstime_subtract(&delta, &timer->t0);\n\treturn nstime_ns(&delta) / 1000;\n}\n\nvoid\ntimer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen) {\n\tuint64_t t0 = timer_usec(a);\n\tuint64_t t1 = timer_usec(b);\n\tuint64_t mult;\n\tsize_t i = 0;\n\tsize_t j, n;\n\n\t/* Whole. */\n\tn = malloc_snprintf(&buf[i], buflen-i, \"%\"FMTu64, t0 / t1);\n\ti += n;\n\tif (i >= buflen) {\n\t\treturn;\n\t}\n\tmult = 1;\n\tfor (j = 0; j < n; j++) {\n\t\tmult *= 10;\n\t}\n\n\t/* Decimal. */\n\tn = malloc_snprintf(&buf[i], buflen-i, \".\");\n\ti += n;\n\n\t/* Fraction. */\n\twhile (i < buflen-1) {\n\t\tuint64_t round = (i+1 == buflen-1 && ((t0 * mult * 10 / t1) % 10\n\t\t    >= 5)) ? 1 : 0;\n\t\tn = malloc_snprintf(&buf[i], buflen-i,\n\t\t    \"%\"FMTu64, (t0 * mult / t1) % 10 + round);\n\t\ti += n;\n\t\tmult *= 10;\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/stress/microbench.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic inline void\ntime_func(timedelta_t *timer, uint64_t nwarmup, uint64_t niter,\n    void (*func)(void)) {\n\tuint64_t i;\n\n\tfor (i = 0; i < nwarmup; i++) {\n\t\tfunc();\n\t}\n\ttimer_start(timer);\n\tfor (i = 0; i < niter; i++) {\n\t\tfunc();\n\t}\n\ttimer_stop(timer);\n}\n\nvoid\ncompare_funcs(uint64_t nwarmup, uint64_t niter, const char *name_a,\n    void (*func_a), const char *name_b, void (*func_b)) {\n\ttimedelta_t timer_a, timer_b;\n\tchar ratio_buf[6];\n\tvoid *p;\n\n\tp = mallocx(1, 0);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected mallocx() failure\");\n\t\treturn;\n\t}\n\n\ttime_func(&timer_a, nwarmup, niter, func_a);\n\ttime_func(&timer_b, nwarmup, niter, func_b);\n\n\ttimer_ratio(&timer_a, &timer_b, ratio_buf, sizeof(ratio_buf));\n\tmalloc_printf(\"%\"FMTu64\" iterations, %s=%\"FMTu64\"us, \"\n\t    \"%s=%\"FMTu64\"us, ratio=1:%s\\n\",\n\t    niter, name_a, timer_usec(&timer_a), name_b, timer_usec(&timer_b),\n\t    ratio_buf);\n\n\tdallocx(p, 0);\n}\n\nstatic void\nmalloc_free(void) {\n\t/* The compiler can optimize away free(malloc(1))! */\n\tvoid *p = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tfree(p);\n}\n\nstatic void\nmallocx_free(void) {\n\tvoid *p = mallocx(1, 0);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected mallocx() failure\");\n\t\treturn;\n\t}\n\tfree(p);\n}\n\nTEST_BEGIN(test_malloc_vs_mallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"malloc\",\n\t    malloc_free, \"mallocx\", mallocx_free);\n}\nTEST_END\n\nstatic void\nmalloc_dallocx(void) {\n\tvoid *p = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tdallocx(p, 0);\n}\n\nstatic void\nmalloc_sdallocx(void) {\n\tvoid *p = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tsdallocx(p, 1, 0);\n}\n\nTEST_BEGIN(test_free_vs_dallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"free\", malloc_free,\n\t    \"dallocx\", malloc_dallocx);\n}\nTEST_END\n\nTEST_BEGIN(test_dallocx_vs_sdallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"dallocx\", malloc_dallocx,\n\t    \"sdallocx\", malloc_sdallocx);\n}\nTEST_END\n\nstatic void\nmalloc_mus_free(void) {\n\tvoid *p;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tmalloc_usable_size(p);\n\tfree(p);\n}\n\nstatic void\nmalloc_sallocx_free(void) {\n\tvoid *p;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tif (sallocx(p, 0) < 1) {\n\t\ttest_fail(\"Unexpected sallocx() failure\");\n\t}\n\tfree(p);\n}\n\nTEST_BEGIN(test_mus_vs_sallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"malloc_usable_size\",\n\t    malloc_mus_free, \"sallocx\", malloc_sallocx_free);\n}\nTEST_END\n\nstatic void\nmalloc_nallocx_free(void) {\n\tvoid *p;\n\n\tp = malloc(1);\n\tif (p == NULL) {\n\t\ttest_fail(\"Unexpected malloc() failure\");\n\t\treturn;\n\t}\n\tif (nallocx(1, 0) < 1) {\n\t\ttest_fail(\"Unexpected nallocx() failure\");\n\t}\n\tfree(p);\n}\n\nTEST_BEGIN(test_sallocx_vs_nallocx) {\n\tcompare_funcs(10*1000*1000, 100*1000*1000, \"sallocx\",\n\t    malloc_sallocx_free, \"nallocx\", malloc_nallocx_free);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_malloc_vs_mallocx,\n\t    test_free_vs_dallocx,\n\t    test_dallocx_vs_sdallocx,\n\t    test_mus_vs_sallocx,\n\t    test_sallocx_vs_nallocx);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/test.sh.in",
    "content": "#!/bin/sh\n\ncase @abi@ in\n  macho)\n    export DYLD_FALLBACK_LIBRARY_PATH=\"@objroot@lib\"\n    ;;\n  pecoff)\n    export PATH=\"${PATH}:@objroot@lib\"\n    ;;\n  *)\n    ;;\nesac\n\n# Make a copy of the @JEMALLOC_CPREFIX@MALLOC_CONF passed in to this script, so\n# it can be repeatedly concatenated with per test settings.\nexport MALLOC_CONF_ALL=${@JEMALLOC_CPREFIX@MALLOC_CONF}\n# Concatenate the individual test's MALLOC_CONF and MALLOC_CONF_ALL.\nexport_malloc_conf() {\n  if [ \"x${MALLOC_CONF}\" != \"x\" -a \"x${MALLOC_CONF_ALL}\" != \"x\" ] ; then\n    export @JEMALLOC_CPREFIX@MALLOC_CONF=\"${MALLOC_CONF},${MALLOC_CONF_ALL}\"\n  else\n    export @JEMALLOC_CPREFIX@MALLOC_CONF=\"${MALLOC_CONF}${MALLOC_CONF_ALL}\"\n  fi\n}\n\n# Corresponds to test_status_t.\npass_code=0\nskip_code=1\nfail_code=2\n\npass_count=0\nskip_count=0\nfail_count=0\nfor t in $@; do\n  if [ $pass_count -ne 0 -o $skip_count -ne 0 -o $fail_count != 0 ] ; then\n    echo\n  fi\n  echo \"=== ${t} ===\"\n  if [ -e \"@srcroot@${t}.sh\" ] ; then\n    # Source the shell script corresponding to the test in a subshell and\n    # execute the test.  This allows the shell script to set MALLOC_CONF, which\n    # is then used to set @JEMALLOC_CPREFIX@MALLOC_CONF (thus allowing the\n    # per test shell script to ignore the @JEMALLOC_CPREFIX@ detail).\n    enable_fill=@enable_fill@ \\\n    enable_prof=@enable_prof@ \\\n    . @srcroot@${t}.sh && \\\n    export_malloc_conf && \\\n    $JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@\n  else\n    export MALLOC_CONF= && \\\n    export_malloc_conf && \\\n    $JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@\n  fi\n  result_code=$?\n  case ${result_code} in\n    ${pass_code})\n      pass_count=$((pass_count+1))\n      ;;\n    ${skip_code})\n      skip_count=$((skip_count+1))\n      ;;\n    ${fail_code})\n      fail_count=$((fail_count+1))\n      ;;\n    *)\n      echo \"Test harness error: ${t} w/ MALLOC_CONF=\\\"${MALLOC_CONF}\\\"\" 1>&2\n      echo \"Use prefix to debug, e.g. JEMALLOC_TEST_PREFIX=\\\"gdb --args\\\" sh test/test.sh ${t}\" 1>&2\n      exit 1\n  esac\ndone\n\ntotal_count=`expr ${pass_count} + ${skip_count} + ${fail_count}`\necho\necho \"Test suite summary: pass: ${pass_count}/${total_count}, skip: ${skip_count}/${total_count}, fail: ${fail_count}/${total_count}\"\n\nif [ ${fail_count} -eq 0 ] ; then\n  exit 0\nelse\n  exit 1\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/SFMT.c",
    "content": "/*\n * This file derives from SFMT 1.3.3\n * (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html), which was\n * released under the terms of the following license:\n *\n *   Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima\n *   University. 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\n *         disclaimer in the documentation and/or other materials provided\n *         with the distribution.\n *       * Neither the name of the Hiroshima University nor the names of\n *         its contributors may be used to endorse or promote products\n *         derived from this software without specific prior written\n *         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#include \"test/jemalloc_test.h\"\n\n#define BLOCK_SIZE 10000\n#define BLOCK_SIZE64 (BLOCK_SIZE / 2)\n#define COUNT_1 1000\n#define COUNT_2 700\n\nstatic const uint32_t init_gen_rand_32_expected[] = {\n\t3440181298U, 1564997079U, 1510669302U, 2930277156U, 1452439940U,\n\t3796268453U,  423124208U, 2143818589U, 3827219408U, 2987036003U,\n\t2674978610U, 1536842514U, 2027035537U, 2534897563U, 1686527725U,\n\t 545368292U, 1489013321U, 1370534252U, 4231012796U, 3994803019U,\n\t1764869045U,  824597505U,  862581900U, 2469764249U,  812862514U,\n\t 359318673U,  116957936U, 3367389672U, 2327178354U, 1898245200U,\n\t3206507879U, 2378925033U, 1040214787U, 2524778605U, 3088428700U,\n\t1417665896U,  964324147U, 2282797708U, 2456269299U,  313400376U,\n\t2245093271U, 1015729427U, 2694465011U, 3246975184U, 1992793635U,\n\t 463679346U, 3721104591U, 3475064196U,  856141236U, 1499559719U,\n\t3522818941U, 3721533109U, 1954826617U, 1282044024U, 1543279136U,\n\t1301863085U, 2669145051U, 4221477354U, 3896016841U, 3392740262U,\n\t 462466863U, 1037679449U, 1228140306U,  922298197U, 1205109853U,\n\t1872938061U, 3102547608U, 2742766808U, 1888626088U, 4028039414U,\n\t 157593879U, 1136901695U, 4038377686U, 3572517236U, 4231706728U,\n\t2997311961U, 1189931652U, 3981543765U, 2826166703U,   87159245U,\n\t1721379072U, 3897926942U, 1790395498U, 2569178939U, 1047368729U,\n\t2340259131U, 3144212906U, 2301169789U, 2442885464U, 3034046771U,\n\t3667880593U, 3935928400U, 2372805237U, 1666397115U, 2460584504U,\n\t 513866770U, 3810869743U, 2147400037U, 2792078025U, 2941761810U,\n\t3212265810U,  984692259U,  346590253U, 1804179199U, 3298543443U,\n\t 750108141U, 2880257022U,  243310542U, 1869036465U, 1588062513U,\n\t2983949551U, 1931450364U, 4034505847U, 2735030199U, 1628461061U,\n\t2539522841U,  127965585U, 3992448871U,  913388237U,  559130076U,\n\t1202933193U, 4087643167U, 2590021067U, 2256240196U, 1746697293U,\n\t1013913783U, 1155864921U, 2715773730U,  915061862U, 1948766573U,\n\t2322882854U, 3761119102U, 1343405684U, 3078711943U, 3067431651U,\n\t3245156316U, 3588354584U, 3484623306U, 3899621563U, 4156689741U,\n\t3237090058U, 3880063844U,  862416318U, 4039923869U, 2303788317U,\n\t3073590536U,  701653667U, 2131530884U, 3169309950U, 2028486980U,\n\t 747196777U, 3620218225U,  432016035U, 1449580595U, 2772266392U,\n\t 444224948U, 1662832057U, 3184055582U, 3028331792U, 1861686254U,\n\t1104864179U,  342430307U, 1350510923U, 3024656237U, 1028417492U,\n\t2870772950U,  290847558U, 3675663500U,  508431529U, 4264340390U,\n\t2263569913U, 1669302976U,  519511383U, 2706411211U, 3764615828U,\n\t3883162495U, 4051445305U, 2412729798U, 3299405164U, 3991911166U,\n\t2348767304U, 2664054906U, 3763609282U,  593943581U, 3757090046U,\n\t2075338894U, 2020550814U, 4287452920U, 4290140003U, 1422957317U,\n\t2512716667U, 2003485045U, 2307520103U, 2288472169U, 3940751663U,\n\t4204638664U, 2892583423U, 1710068300U, 3904755993U, 2363243951U,\n\t3038334120U,  547099465U,  771105860U, 3199983734U, 4282046461U,\n\t2298388363U,  934810218U, 2837827901U, 3952500708U, 2095130248U,\n\t3083335297U,   26885281U, 3932155283U, 1531751116U, 1425227133U,\n\t 495654159U, 3279634176U, 3855562207U, 3957195338U, 4159985527U,\n\t 893375062U, 1875515536U, 1327247422U, 3754140693U, 1028923197U,\n\t1729880440U,  805571298U,  448971099U, 2726757106U, 2749436461U,\n\t2485987104U,  175337042U, 3235477922U, 3882114302U, 2020970972U,\n\t 943926109U, 2762587195U, 1904195558U, 3452650564U,  108432281U,\n\t3893463573U, 3977583081U, 2636504348U, 1110673525U, 3548479841U,\n\t4258854744U,  980047703U, 4057175418U, 3890008292U,  145653646U,\n\t3141868989U, 3293216228U, 1194331837U, 1254570642U, 3049934521U,\n\t2868313360U, 2886032750U, 1110873820U,  279553524U, 3007258565U,\n\t1104807822U, 3186961098U,  315764646U, 2163680838U, 3574508994U,\n\t3099755655U,  191957684U, 3642656737U, 3317946149U, 3522087636U,\n\t 444526410U,  779157624U, 1088229627U, 1092460223U, 1856013765U,\n\t3659877367U,  368270451U,  503570716U, 3000984671U, 2742789647U,\n\t 928097709U, 2914109539U,  308843566U, 2816161253U, 3667192079U,\n\t2762679057U, 3395240989U, 2928925038U, 1491465914U, 3458702834U,\n\t3787782576U, 2894104823U, 1296880455U, 1253636503U,  989959407U,\n\t2291560361U, 2776790436U, 1913178042U, 1584677829U,  689637520U,\n\t1898406878U,  688391508U, 3385234998U,  845493284U, 1943591856U,\n\t2720472050U,  222695101U, 1653320868U, 2904632120U, 4084936008U,\n\t1080720688U, 3938032556U,  387896427U, 2650839632U,   99042991U,\n\t1720913794U, 1047186003U, 1877048040U, 2090457659U,  517087501U,\n\t4172014665U, 2129713163U, 2413533132U, 2760285054U, 4129272496U,\n\t1317737175U, 2309566414U, 2228873332U, 3889671280U, 1110864630U,\n\t3576797776U, 2074552772U,  832002644U, 3097122623U, 2464859298U,\n\t2679603822U, 1667489885U, 3237652716U, 1478413938U, 1719340335U,\n\t2306631119U,  639727358U, 3369698270U,  226902796U, 2099920751U,\n\t1892289957U, 2201594097U, 3508197013U, 3495811856U, 3900381493U,\n\t 841660320U, 3974501451U, 3360949056U, 1676829340U,  728899254U,\n\t2047809627U, 2390948962U,  670165943U, 3412951831U, 4189320049U,\n\t1911595255U, 2055363086U,  507170575U,  418219594U, 4141495280U,\n\t2692088692U, 4203630654U, 3540093932U,  791986533U, 2237921051U,\n\t2526864324U, 2956616642U, 1394958700U, 1983768223U, 1893373266U,\n\t 591653646U,  228432437U, 1611046598U, 3007736357U, 1040040725U,\n\t2726180733U, 2789804360U, 4263568405U,  829098158U, 3847722805U,\n\t1123578029U, 1804276347U,  997971319U, 4203797076U, 4185199713U,\n\t2811733626U, 2343642194U, 2985262313U, 1417930827U, 3759587724U,\n\t1967077982U, 1585223204U, 1097475516U, 1903944948U,  740382444U,\n\t1114142065U, 1541796065U, 1718384172U, 1544076191U, 1134682254U,\n\t3519754455U, 2866243923U,  341865437U,  645498576U, 2690735853U,\n\t1046963033U, 2493178460U, 1187604696U, 1619577821U,  488503634U,\n\t3255768161U, 2306666149U, 1630514044U, 2377698367U, 2751503746U,\n\t3794467088U, 1796415981U, 3657173746U,  409136296U, 1387122342U,\n\t1297726519U,  219544855U, 4270285558U,  437578827U, 1444698679U,\n\t2258519491U,  963109892U, 3982244073U, 3351535275U,  385328496U,\n\t1804784013U,  698059346U, 3920535147U,  708331212U,  784338163U,\n\t 785678147U, 1238376158U, 1557298846U, 2037809321U,  271576218U,\n\t4145155269U, 1913481602U, 2763691931U,  588981080U, 1201098051U,\n\t3717640232U, 1509206239U,  662536967U, 3180523616U, 1133105435U,\n\t2963500837U, 2253971215U, 3153642623U, 1066925709U, 2582781958U,\n\t3034720222U, 1090798544U, 2942170004U, 4036187520U,  686972531U,\n\t2610990302U, 2641437026U, 1837562420U,  722096247U, 1315333033U,\n\t2102231203U, 3402389208U, 3403698140U, 1312402831U, 2898426558U,\n\t 814384596U,  385649582U, 1916643285U, 1924625106U, 2512905582U,\n\t2501170304U, 4275223366U, 2841225246U, 1467663688U, 3563567847U,\n\t2969208552U,  884750901U,  102992576U,  227844301U, 3681442994U,\n\t3502881894U, 4034693299U, 1166727018U, 1697460687U, 1737778332U,\n\t1787161139U, 1053003655U, 1215024478U, 2791616766U, 2525841204U,\n\t1629323443U,    3233815U, 2003823032U, 3083834263U, 2379264872U,\n\t3752392312U, 1287475550U, 3770904171U, 3004244617U, 1502117784U,\n\t 918698423U, 2419857538U, 3864502062U, 1751322107U, 2188775056U,\n\t4018728324U,  983712955U,  440071928U, 3710838677U, 2001027698U,\n\t3994702151U,   22493119U, 3584400918U, 3446253670U, 4254789085U,\n\t1405447860U, 1240245579U, 1800644159U, 1661363424U, 3278326132U,\n\t3403623451U,   67092802U, 2609352193U, 3914150340U, 1814842761U,\n\t3610830847U,  591531412U, 3880232807U, 1673505890U, 2585326991U,\n\t1678544474U, 3148435887U, 3457217359U, 1193226330U, 2816576908U,\n\t 154025329U,  121678860U, 1164915738U,  973873761U,  269116100U,\n\t  52087970U,  744015362U,  498556057U,   94298882U, 1563271621U,\n\t2383059628U, 4197367290U, 3958472990U, 2592083636U, 2906408439U,\n\t1097742433U, 3924840517U,  264557272U, 2292287003U, 3203307984U,\n\t4047038857U, 3820609705U, 2333416067U, 1839206046U, 3600944252U,\n\t3412254904U,  583538222U, 2390557166U, 4140459427U, 2810357445U,\n\t 226777499U, 2496151295U, 2207301712U, 3283683112U,  611630281U,\n\t1933218215U, 3315610954U, 3889441987U, 3719454256U, 3957190521U,\n\t1313998161U, 2365383016U, 3146941060U, 1801206260U,  796124080U,\n\t2076248581U, 1747472464U, 3254365145U,  595543130U, 3573909503U,\n\t3758250204U, 2020768540U, 2439254210U,   93368951U, 3155792250U,\n\t2600232980U, 3709198295U, 3894900440U, 2971850836U, 1578909644U,\n\t1443493395U, 2581621665U, 3086506297U, 2443465861U,  558107211U,\n\t1519367835U,  249149686U,  908102264U, 2588765675U, 1232743965U,\n\t1001330373U, 3561331654U, 2259301289U, 1564977624U, 3835077093U,\n\t 727244906U, 4255738067U, 1214133513U, 2570786021U, 3899704621U,\n\t1633861986U, 1636979509U, 1438500431U,   58463278U, 2823485629U,\n\t2297430187U, 2926781924U, 3371352948U, 1864009023U, 2722267973U,\n\t1444292075U,  437703973U, 1060414512U,  189705863U,  910018135U,\n\t4077357964U,  884213423U, 2644986052U, 3973488374U, 1187906116U,\n\t2331207875U,  780463700U, 3713351662U, 3854611290U,  412805574U,\n\t2978462572U, 2176222820U,  829424696U, 2790788332U, 2750819108U,\n\t1594611657U, 3899878394U, 3032870364U, 1702887682U, 1948167778U,\n\t  14130042U,  192292500U,  947227076U,   90719497U, 3854230320U,\n\t 784028434U, 2142399787U, 1563449646U, 2844400217U,  819143172U,\n\t2883302356U, 2328055304U, 1328532246U, 2603885363U, 3375188924U,\n\t 933941291U, 3627039714U, 2129697284U, 2167253953U, 2506905438U,\n\t1412424497U, 2981395985U, 1418359660U, 2925902456U,   52752784U,\n\t3713667988U, 3924669405U,  648975707U, 1145520213U, 4018650664U,\n\t3805915440U, 2380542088U, 2013260958U, 3262572197U, 2465078101U,\n\t1114540067U, 3728768081U, 2396958768U,  590672271U,  904818725U,\n\t4263660715U,  700754408U, 1042601829U, 4094111823U, 4274838909U,\n\t2512692617U, 2774300207U, 2057306915U, 3470942453U,   99333088U,\n\t1142661026U, 2889931380U,   14316674U, 2201179167U,  415289459U,\n\t 448265759U, 3515142743U, 3254903683U,  246633281U, 1184307224U,\n\t2418347830U, 2092967314U, 2682072314U, 2558750234U, 2000352263U,\n\t1544150531U,  399010405U, 1513946097U,  499682937U,  461167460U,\n\t3045570638U, 1633669705U,  851492362U, 4052801922U, 2055266765U,\n\t 635556996U,  368266356U, 2385737383U, 3218202352U, 2603772408U,\n\t 349178792U,  226482567U, 3102426060U, 3575998268U, 2103001871U,\n\t3243137071U,  225500688U, 1634718593U, 4283311431U, 4292122923U,\n\t3842802787U,  811735523U,  105712518U,  663434053U, 1855889273U,\n\t2847972595U, 1196355421U, 2552150115U, 4254510614U, 3752181265U,\n\t3430721819U, 3828705396U, 3436287905U, 3441964937U, 4123670631U,\n\t 353001539U,  459496439U, 3799690868U, 1293777660U, 2761079737U,\n\t 498096339U, 3398433374U, 4080378380U, 2304691596U, 2995729055U,\n\t4134660419U, 3903444024U, 3576494993U,  203682175U, 3321164857U,\n\t2747963611U,   79749085U, 2992890370U, 1240278549U, 1772175713U,\n\t2111331972U, 2655023449U, 1683896345U, 2836027212U, 3482868021U,\n\t2489884874U,  756853961U, 2298874501U, 4013448667U, 4143996022U,\n\t2948306858U, 4132920035U, 1283299272U,  995592228U, 3450508595U,\n\t1027845759U, 1766942720U, 3861411826U, 1446861231U,   95974993U,\n\t3502263554U, 1487532194U,  601502472U, 4129619129U,  250131773U,\n\t2050079547U, 3198903947U, 3105589778U, 4066481316U, 3026383978U,\n\t2276901713U,  365637751U, 2260718426U, 1394775634U, 1791172338U,\n\t2690503163U, 2952737846U, 1568710462U,  732623190U, 2980358000U,\n\t1053631832U, 1432426951U, 3229149635U, 1854113985U, 3719733532U,\n\t3204031934U,  735775531U,  107468620U, 3734611984U,  631009402U,\n\t3083622457U, 4109580626U,  159373458U, 1301970201U, 4132389302U,\n\t1293255004U,  847182752U, 4170022737U,   96712900U, 2641406755U,\n\t1381727755U,  405608287U, 4287919625U, 1703554290U, 3589580244U,\n\t2911403488U,    2166565U, 2647306451U, 2330535117U, 1200815358U,\n\t1165916754U,  245060911U, 4040679071U, 3684908771U, 2452834126U,\n\t2486872773U, 2318678365U, 2940627908U, 1837837240U, 3447897409U,\n\t4270484676U, 1495388728U, 3754288477U, 4204167884U, 1386977705U,\n\t2692224733U, 3076249689U, 4109568048U, 4170955115U, 4167531356U,\n\t4020189950U, 4261855038U, 3036907575U, 3410399885U, 3076395737U,\n\t1046178638U,  144496770U,  230725846U, 3349637149U,   17065717U,\n\t2809932048U, 2054581785U, 3608424964U, 3259628808U,  134897388U,\n\t3743067463U,  257685904U, 3795656590U, 1562468719U, 3589103904U,\n\t3120404710U,  254684547U, 2653661580U, 3663904795U, 2631942758U,\n\t1063234347U, 2609732900U, 2332080715U, 3521125233U, 1180599599U,\n\t1935868586U, 4110970440U,  296706371U, 2128666368U, 1319875791U,\n\t1570900197U, 3096025483U, 1799882517U, 1928302007U, 1163707758U,\n\t1244491489U, 3533770203U,  567496053U, 2757924305U, 2781639343U,\n\t2818420107U,  560404889U, 2619609724U, 4176035430U, 2511289753U,\n\t2521842019U, 3910553502U, 2926149387U, 3302078172U, 4237118867U,\n\t 330725126U,  367400677U,  888239854U,  545570454U, 4259590525U,\n\t 134343617U, 1102169784U, 1647463719U, 3260979784U, 1518840883U,\n\t3631537963U, 3342671457U, 1301549147U, 2083739356U,  146593792U,\n\t3217959080U,  652755743U, 2032187193U, 3898758414U, 1021358093U,\n\t4037409230U, 2176407931U, 3427391950U, 2883553603U,  985613827U,\n\t3105265092U, 3423168427U, 3387507672U,  467170288U, 2141266163U,\n\t3723870208U,  916410914U, 1293987799U, 2652584950U,  769160137U,\n\t3205292896U, 1561287359U, 1684510084U, 3136055621U, 3765171391U,\n\t 639683232U, 2639569327U, 1218546948U, 4263586685U, 3058215773U,\n\t2352279820U,  401870217U, 2625822463U, 1529125296U, 2981801895U,\n\t1191285226U, 4027725437U, 3432700217U, 4098835661U,  971182783U,\n\t2443861173U, 3881457123U, 3874386651U,  457276199U, 2638294160U,\n\t4002809368U,  421169044U, 1112642589U, 3076213779U, 3387033971U,\n\t2499610950U, 3057240914U, 1662679783U,  461224431U, 1168395933U\n};\nstatic const uint32_t init_by_array_32_expected[] = {\n\t2920711183U, 3885745737U, 3501893680U,  856470934U, 1421864068U,\n\t 277361036U, 1518638004U, 2328404353U, 3355513634U,   64329189U,\n\t1624587673U, 3508467182U, 2481792141U, 3706480799U, 1925859037U,\n\t2913275699U,  882658412U,  384641219U,  422202002U, 1873384891U,\n\t2006084383U, 3924929912U, 1636718106U, 3108838742U, 1245465724U,\n\t4195470535U,  779207191U, 1577721373U, 1390469554U, 2928648150U,\n\t 121399709U, 3170839019U, 4044347501U,  953953814U, 3821710850U,\n\t3085591323U, 3666535579U, 3577837737U, 2012008410U, 3565417471U,\n\t4044408017U,  433600965U, 1637785608U, 1798509764U,  860770589U,\n\t3081466273U, 3982393409U, 2451928325U, 3437124742U, 4093828739U,\n\t3357389386U, 2154596123U,  496568176U, 2650035164U, 2472361850U,\n\t   3438299U, 2150366101U, 1577256676U, 3802546413U, 1787774626U,\n\t4078331588U, 3706103141U,  170391138U, 3806085154U, 1680970100U,\n\t1961637521U, 3316029766U,  890610272U, 1453751581U, 1430283664U,\n\t3051057411U, 3597003186U,  542563954U, 3796490244U, 1690016688U,\n\t3448752238U,  440702173U,  347290497U, 1121336647U, 2540588620U,\n\t 280881896U, 2495136428U,  213707396U,   15104824U, 2946180358U,\n\t 659000016U,  566379385U, 2614030979U, 2855760170U,  334526548U,\n\t2315569495U, 2729518615U,  564745877U, 1263517638U, 3157185798U,\n\t1604852056U, 1011639885U, 2950579535U, 2524219188U,  312951012U,\n\t1528896652U, 1327861054U, 2846910138U, 3966855905U, 2536721582U,\n\t 855353911U, 1685434729U, 3303978929U, 1624872055U, 4020329649U,\n\t3164802143U, 1642802700U, 1957727869U, 1792352426U, 3334618929U,\n\t2631577923U, 3027156164U,  842334259U, 3353446843U, 1226432104U,\n\t1742801369U, 3552852535U, 3471698828U, 1653910186U, 3380330939U,\n\t2313782701U, 3351007196U, 2129839995U, 1800682418U, 4085884420U,\n\t1625156629U, 3669701987U,  615211810U, 3294791649U, 4131143784U,\n\t2590843588U, 3207422808U, 3275066464U,  561592872U, 3957205738U,\n\t3396578098U,   48410678U, 3505556445U, 1005764855U, 3920606528U,\n\t2936980473U, 2378918600U, 2404449845U, 1649515163U,  701203563U,\n\t3705256349U,   83714199U, 3586854132U,  922978446U, 2863406304U,\n\t3523398907U, 2606864832U, 2385399361U, 3171757816U, 4262841009U,\n\t3645837721U, 1169579486U, 3666433897U, 3174689479U, 1457866976U,\n\t3803895110U, 3346639145U, 1907224409U, 1978473712U, 1036712794U,\n\t 980754888U, 1302782359U, 1765252468U,  459245755U, 3728923860U,\n\t1512894209U, 2046491914U,  207860527U,  514188684U, 2288713615U,\n\t1597354672U, 3349636117U, 2357291114U, 3995796221U,  945364213U,\n\t1893326518U, 3770814016U, 1691552714U, 2397527410U,  967486361U,\n\t 776416472U, 4197661421U,  951150819U, 1852770983U, 4044624181U,\n\t1399439738U, 4194455275U, 2284037669U, 1550734958U, 3321078108U,\n\t1865235926U, 2912129961U, 2664980877U, 1357572033U, 2600196436U,\n\t2486728200U, 2372668724U, 1567316966U, 2374111491U, 1839843570U,\n\t  20815612U, 3727008608U, 3871996229U,  824061249U, 1932503978U,\n\t3404541726U,  758428924U, 2609331364U, 1223966026U, 1299179808U,\n\t 648499352U, 2180134401U,  880821170U, 3781130950U,  113491270U,\n\t1032413764U, 4185884695U, 2490396037U, 1201932817U, 4060951446U,\n\t4165586898U, 1629813212U, 2887821158U,  415045333U,  628926856U,\n\t2193466079U, 3391843445U, 2227540681U, 1907099846U, 2848448395U,\n\t1717828221U, 1372704537U, 1707549841U, 2294058813U, 2101214437U,\n\t2052479531U, 1695809164U, 3176587306U, 2632770465U,   81634404U,\n\t1603220563U,  644238487U,  302857763U,  897352968U, 2613146653U,\n\t1391730149U, 4245717312U, 4191828749U, 1948492526U, 2618174230U,\n\t3992984522U, 2178852787U, 3596044509U, 3445573503U, 2026614616U,\n\t 915763564U, 3415689334U, 2532153403U, 3879661562U, 2215027417U,\n\t3111154986U, 2929478371U,  668346391U, 1152241381U, 2632029711U,\n\t3004150659U, 2135025926U,  948690501U, 2799119116U, 4228829406U,\n\t1981197489U, 4209064138U,  684318751U, 3459397845U,  201790843U,\n\t4022541136U, 3043635877U,  492509624U, 3263466772U, 1509148086U,\n\t 921459029U, 3198857146U,  705479721U, 3835966910U, 3603356465U,\n\t 576159741U, 1742849431U,  594214882U, 2055294343U, 3634861861U,\n\t 449571793U, 3246390646U, 3868232151U, 1479156585U, 2900125656U,\n\t2464815318U, 3960178104U, 1784261920U,   18311476U, 3627135050U,\n\t 644609697U,  424968996U,  919890700U, 2986824110U,  816423214U,\n\t4003562844U, 1392714305U, 1757384428U, 2569030598U,  995949559U,\n\t3875659880U, 2933807823U, 2752536860U, 2993858466U, 4030558899U,\n\t2770783427U, 2775406005U, 2777781742U, 1931292655U,  472147933U,\n\t3865853827U, 2726470545U, 2668412860U, 2887008249U,  408979190U,\n\t3578063323U, 3242082049U, 1778193530U,   27981909U, 2362826515U,\n\t 389875677U, 1043878156U,  581653903U, 3830568952U,  389535942U,\n\t3713523185U, 2768373359U, 2526101582U, 1998618197U, 1160859704U,\n\t3951172488U, 1098005003U,  906275699U, 3446228002U, 2220677963U,\n\t2059306445U,  132199571U,  476838790U, 1868039399U, 3097344807U,\n\t 857300945U,  396345050U, 2835919916U, 1782168828U, 1419519470U,\n\t4288137521U,  819087232U,  596301494U,  872823172U, 1526888217U,\n\t 805161465U, 1116186205U, 2829002754U, 2352620120U,  620121516U,\n\t 354159268U, 3601949785U,  209568138U, 1352371732U, 2145977349U,\n\t4236871834U, 1539414078U, 3558126206U, 3224857093U, 4164166682U,\n\t3817553440U, 3301780278U, 2682696837U, 3734994768U, 1370950260U,\n\t1477421202U, 2521315749U, 1330148125U, 1261554731U, 2769143688U,\n\t3554756293U, 4235882678U, 3254686059U, 3530579953U, 1215452615U,\n\t3574970923U, 4057131421U,  589224178U, 1000098193U,  171190718U,\n\t2521852045U, 2351447494U, 2284441580U, 2646685513U, 3486933563U,\n\t3789864960U, 1190528160U, 1702536782U, 1534105589U, 4262946827U,\n\t2726686826U, 3584544841U, 2348270128U, 2145092281U, 2502718509U,\n\t1027832411U, 3571171153U, 1287361161U, 4011474411U, 3241215351U,\n\t2419700818U,  971242709U, 1361975763U, 1096842482U, 3271045537U,\n\t  81165449U,  612438025U, 3912966678U, 1356929810U,  733545735U,\n\t 537003843U, 1282953084U,  884458241U,  588930090U, 3930269801U,\n\t2961472450U, 1219535534U, 3632251943U,  268183903U, 1441240533U,\n\t3653903360U, 3854473319U, 2259087390U, 2548293048U, 2022641195U,\n\t2105543911U, 1764085217U, 3246183186U,  482438805U,  888317895U,\n\t2628314765U, 2466219854U,  717546004U, 2322237039U,  416725234U,\n\t1544049923U, 1797944973U, 3398652364U, 3111909456U,  485742908U,\n\t2277491072U, 1056355088U, 3181001278U,  129695079U, 2693624550U,\n\t1764438564U, 3797785470U,  195503713U, 3266519725U, 2053389444U,\n\t1961527818U, 3400226523U, 3777903038U, 2597274307U, 4235851091U,\n\t4094406648U, 2171410785U, 1781151386U, 1378577117U,  654643266U,\n\t3424024173U, 3385813322U,  679385799U,  479380913U,  681715441U,\n\t3096225905U,  276813409U, 3854398070U, 2721105350U,  831263315U,\n\t3276280337U, 2628301522U, 3984868494U, 1466099834U, 2104922114U,\n\t1412672743U,  820330404U, 3491501010U,  942735832U,  710652807U,\n\t3972652090U,  679881088U,   40577009U, 3705286397U, 2815423480U,\n\t3566262429U,  663396513U, 3777887429U, 4016670678U,  404539370U,\n\t1142712925U, 1140173408U, 2913248352U, 2872321286U,  263751841U,\n\t3175196073U, 3162557581U, 2878996619U,   75498548U, 3836833140U,\n\t3284664959U, 1157523805U,  112847376U,  207855609U, 1337979698U,\n\t1222578451U,  157107174U,  901174378U, 3883717063U, 1618632639U,\n\t1767889440U, 4264698824U, 1582999313U,  884471997U, 2508825098U,\n\t3756370771U, 2457213553U, 3565776881U, 3709583214U,  915609601U,\n\t 460833524U, 1091049576U,   85522880U,    2553251U,  132102809U,\n\t2429882442U, 2562084610U, 1386507633U, 4112471229U,   21965213U,\n\t1981516006U, 2418435617U, 3054872091U, 4251511224U, 2025783543U,\n\t1916911512U, 2454491136U, 3938440891U, 3825869115U, 1121698605U,\n\t3463052265U,  802340101U, 1912886800U, 4031997367U, 3550640406U,\n\t1596096923U,  610150600U,  431464457U, 2541325046U,  486478003U,\n\t 739704936U, 2862696430U, 3037903166U, 1129749694U, 2611481261U,\n\t1228993498U,  510075548U, 3424962587U, 2458689681U,  818934833U,\n\t4233309125U, 1608196251U, 3419476016U, 1858543939U, 2682166524U,\n\t3317854285U,  631986188U, 3008214764U,  613826412U, 3567358221U,\n\t3512343882U, 1552467474U, 3316162670U, 1275841024U, 4142173454U,\n\t 565267881U,  768644821U,  198310105U, 2396688616U, 1837659011U,\n\t 203429334U,  854539004U, 4235811518U, 3338304926U, 3730418692U,\n\t3852254981U, 3032046452U, 2329811860U, 2303590566U, 2696092212U,\n\t3894665932U,  145835667U,  249563655U, 1932210840U, 2431696407U,\n\t3312636759U,  214962629U, 2092026914U, 3020145527U, 4073039873U,\n\t2739105705U, 1308336752U,  855104522U, 2391715321U,   67448785U,\n\t 547989482U,  854411802U, 3608633740U,  431731530U,  537375589U,\n\t3888005760U,  696099141U,  397343236U, 1864511780U,   44029739U,\n\t1729526891U, 1993398655U, 2010173426U, 2591546756U,  275223291U,\n\t1503900299U, 4217765081U, 2185635252U, 1122436015U, 3550155364U,\n\t 681707194U, 3260479338U,  933579397U, 2983029282U, 2505504587U,\n\t2667410393U, 2962684490U, 4139721708U, 2658172284U, 2452602383U,\n\t2607631612U, 1344296217U, 3075398709U, 2949785295U, 1049956168U,\n\t3917185129U, 2155660174U, 3280524475U, 1503827867U,  674380765U,\n\t1918468193U, 3843983676U,  634358221U, 2538335643U, 1873351298U,\n\t3368723763U, 2129144130U, 3203528633U, 3087174986U, 2691698871U,\n\t2516284287U,   24437745U, 1118381474U, 2816314867U, 2448576035U,\n\t4281989654U,  217287825U,  165872888U, 2628995722U, 3533525116U,\n\t2721669106U,  872340568U, 3429930655U, 3309047304U, 3916704967U,\n\t3270160355U, 1348884255U, 1634797670U,  881214967U, 4259633554U,\n\t 174613027U, 1103974314U, 1625224232U, 2678368291U, 1133866707U,\n\t3853082619U, 4073196549U, 1189620777U,  637238656U,  930241537U,\n\t4042750792U, 3842136042U, 2417007212U, 2524907510U, 1243036827U,\n\t1282059441U, 3764588774U, 1394459615U, 2323620015U, 1166152231U,\n\t3307479609U, 3849322257U, 3507445699U, 4247696636U,  758393720U,\n\t 967665141U, 1095244571U, 1319812152U,  407678762U, 2640605208U,\n\t2170766134U, 3663594275U, 4039329364U, 2512175520U,  725523154U,\n\t2249807004U, 3312617979U, 2414634172U, 1278482215U,  349206484U,\n\t1573063308U, 1196429124U, 3873264116U, 2400067801U,  268795167U,\n\t 226175489U, 2961367263U, 1968719665U,   42656370U, 1010790699U,\n\t 561600615U, 2422453992U, 3082197735U, 1636700484U, 3977715296U,\n\t3125350482U, 3478021514U, 2227819446U, 1540868045U, 3061908980U,\n\t1087362407U, 3625200291U,  361937537U,  580441897U, 1520043666U,\n\t2270875402U, 1009161260U, 2502355842U, 4278769785U,  473902412U,\n\t1057239083U, 1905829039U, 1483781177U, 2080011417U, 1207494246U,\n\t1806991954U, 2194674403U, 3455972205U,  807207678U, 3655655687U,\n\t 674112918U,  195425752U, 3917890095U, 1874364234U, 1837892715U,\n\t3663478166U, 1548892014U, 2570748714U, 2049929836U, 2167029704U,\n\t 697543767U, 3499545023U, 3342496315U, 1725251190U, 3561387469U,\n\t2905606616U, 1580182447U, 3934525927U, 4103172792U, 1365672522U,\n\t1534795737U, 3308667416U, 2841911405U, 3943182730U, 4072020313U,\n\t3494770452U, 3332626671U,   55327267U,  478030603U,  411080625U,\n\t3419529010U, 1604767823U, 3513468014U,  570668510U,  913790824U,\n\t2283967995U,  695159462U, 3825542932U, 4150698144U, 1829758699U,\n\t 202895590U, 1609122645U, 1267651008U, 2910315509U, 2511475445U,\n\t2477423819U, 3932081579U,  900879979U, 2145588390U, 2670007504U,\n\t 580819444U, 1864996828U, 2526325979U, 1019124258U,  815508628U,\n\t2765933989U, 1277301341U, 3006021786U,  855540956U,  288025710U,\n\t1919594237U, 2331223864U,  177452412U, 2475870369U, 2689291749U,\n\t 865194284U,  253432152U, 2628531804U, 2861208555U, 2361597573U,\n\t1653952120U, 1039661024U, 2159959078U, 3709040440U, 3564718533U,\n\t2596878672U, 2041442161U,   31164696U, 2662962485U, 3665637339U,\n\t1678115244U, 2699839832U, 3651968520U, 3521595541U,  458433303U,\n\t2423096824U,   21831741U,  380011703U, 2498168716U,  861806087U,\n\t1673574843U, 4188794405U, 2520563651U, 2632279153U, 2170465525U,\n\t4171949898U, 3886039621U, 1661344005U, 3424285243U,  992588372U,\n\t2500984144U, 2993248497U, 3590193895U, 1535327365U,  515645636U,\n\t 131633450U, 3729760261U, 1613045101U, 3254194278U,   15889678U,\n\t1493590689U,  244148718U, 2991472662U, 1401629333U,  777349878U,\n\t2501401703U, 4285518317U, 3794656178U,  955526526U, 3442142820U,\n\t3970298374U,  736025417U, 2737370764U, 1271509744U,  440570731U,\n\t 136141826U, 1596189518U,  923399175U,  257541519U, 3505774281U,\n\t2194358432U, 2518162991U, 1379893637U, 2667767062U, 3748146247U,\n\t1821712620U, 3923161384U, 1947811444U, 2392527197U, 4127419685U,\n\t1423694998U, 4156576871U, 1382885582U, 3420127279U, 3617499534U,\n\t2994377493U, 4038063986U, 1918458672U, 2983166794U, 4200449033U,\n\t 353294540U, 1609232588U,  243926648U, 2332803291U,  507996832U,\n\t2392838793U, 4075145196U, 2060984340U, 4287475136U,   88232602U,\n\t2491531140U, 4159725633U, 2272075455U,  759298618U,  201384554U,\n\t 838356250U, 1416268324U,  674476934U,   90795364U,  141672229U,\n\t3660399588U, 4196417251U, 3249270244U, 3774530247U,   59587265U,\n\t3683164208U,   19392575U, 1463123697U, 1882205379U,  293780489U,\n\t2553160622U, 2933904694U,  675638239U, 2851336944U, 1435238743U,\n\t2448730183U,  804436302U, 2119845972U,  322560608U, 4097732704U,\n\t2987802540U,  641492617U, 2575442710U, 4217822703U, 3271835300U,\n\t2836418300U, 3739921620U, 2138378768U, 2879771855U, 4294903423U,\n\t3121097946U, 2603440486U, 2560820391U, 1012930944U, 2313499967U,\n\t 584489368U, 3431165766U,  897384869U, 2062537737U, 2847889234U,\n\t3742362450U, 2951174585U, 4204621084U, 1109373893U, 3668075775U,\n\t2750138839U, 3518055702U,  733072558U, 4169325400U,  788493625U\n};\nstatic const uint64_t init_gen_rand_64_expected[] = {\n\tKQU(16924766246869039260), KQU( 8201438687333352714),\n\tKQU( 2265290287015001750), KQU(18397264611805473832),\n\tKQU( 3375255223302384358), KQU( 6345559975416828796),\n\tKQU(18229739242790328073), KQU( 7596792742098800905),\n\tKQU(  255338647169685981), KQU( 2052747240048610300),\n\tKQU(18328151576097299343), KQU(12472905421133796567),\n\tKQU(11315245349717600863), KQU(16594110197775871209),\n\tKQU(15708751964632456450), KQU(10452031272054632535),\n\tKQU(11097646720811454386), KQU( 4556090668445745441),\n\tKQU(17116187693090663106), KQU(14931526836144510645),\n\tKQU( 9190752218020552591), KQU( 9625800285771901401),\n\tKQU(13995141077659972832), KQU( 5194209094927829625),\n\tKQU( 4156788379151063303), KQU( 8523452593770139494),\n\tKQU(14082382103049296727), KQU( 2462601863986088483),\n\tKQU( 3030583461592840678), KQU( 5221622077872827681),\n\tKQU( 3084210671228981236), KQU(13956758381389953823),\n\tKQU(13503889856213423831), KQU(15696904024189836170),\n\tKQU( 4612584152877036206), KQU( 6231135538447867881),\n\tKQU(10172457294158869468), KQU( 6452258628466708150),\n\tKQU(14044432824917330221), KQU(  370168364480044279),\n\tKQU(10102144686427193359), KQU(  667870489994776076),\n\tKQU( 2732271956925885858), KQU(18027788905977284151),\n\tKQU(15009842788582923859), KQU( 7136357960180199542),\n\tKQU(15901736243475578127), KQU(16951293785352615701),\n\tKQU(10551492125243691632), KQU(17668869969146434804),\n\tKQU(13646002971174390445), KQU( 9804471050759613248),\n\tKQU( 5511670439655935493), KQU(18103342091070400926),\n\tKQU(17224512747665137533), KQU(15534627482992618168),\n\tKQU( 1423813266186582647), KQU(15821176807932930024),\n\tKQU(   30323369733607156), KQU(11599382494723479403),\n\tKQU(  653856076586810062), KQU( 3176437395144899659),\n\tKQU(14028076268147963917), KQU(16156398271809666195),\n\tKQU( 3166955484848201676), KQU( 5746805620136919390),\n\tKQU(17297845208891256593), KQU(11691653183226428483),\n\tKQU(17900026146506981577), KQU(15387382115755971042),\n\tKQU(16923567681040845943), KQU( 8039057517199388606),\n\tKQU(11748409241468629263), KQU(  794358245539076095),\n\tKQU(13438501964693401242), KQU(14036803236515618962),\n\tKQU( 5252311215205424721), KQU(17806589612915509081),\n\tKQU( 6802767092397596006), KQU(14212120431184557140),\n\tKQU( 1072951366761385712), KQU(13098491780722836296),\n\tKQU( 9466676828710797353), KQU(12673056849042830081),\n\tKQU(12763726623645357580), KQU(16468961652999309493),\n\tKQU(15305979875636438926), KQU(17444713151223449734),\n\tKQU( 5692214267627883674), KQU(13049589139196151505),\n\tKQU(  880115207831670745), KQU( 1776529075789695498),\n\tKQU(16695225897801466485), KQU(10666901778795346845),\n\tKQU( 6164389346722833869), KQU( 2863817793264300475),\n\tKQU( 9464049921886304754), KQU( 3993566636740015468),\n\tKQU( 9983749692528514136), KQU(16375286075057755211),\n\tKQU(16042643417005440820), KQU(11445419662923489877),\n\tKQU( 7999038846885158836), KQU( 6721913661721511535),\n\tKQU( 5363052654139357320), KQU( 1817788761173584205),\n\tKQU(13290974386445856444), KQU( 4650350818937984680),\n\tKQU( 8219183528102484836), KQU( 1569862923500819899),\n\tKQU( 4189359732136641860), KQU(14202822961683148583),\n\tKQU( 4457498315309429058), KQU(13089067387019074834),\n\tKQU(11075517153328927293), KQU(10277016248336668389),\n\tKQU( 7070509725324401122), KQU(17808892017780289380),\n\tKQU(13143367339909287349), KQU( 1377743745360085151),\n\tKQU( 5749341807421286485), KQU(14832814616770931325),\n\tKQU( 7688820635324359492), KQU(10960474011539770045),\n\tKQU(   81970066653179790), KQU(12619476072607878022),\n\tKQU( 4419566616271201744), KQU(15147917311750568503),\n\tKQU( 5549739182852706345), KQU( 7308198397975204770),\n\tKQU(13580425496671289278), KQU(17070764785210130301),\n\tKQU( 8202832846285604405), KQU( 6873046287640887249),\n\tKQU( 6927424434308206114), KQU( 6139014645937224874),\n\tKQU(10290373645978487639), KQU(15904261291701523804),\n\tKQU( 9628743442057826883), KQU(18383429096255546714),\n\tKQU( 4977413265753686967), KQU( 7714317492425012869),\n\tKQU( 9025232586309926193), KQU(14627338359776709107),\n\tKQU(14759849896467790763), KQU(10931129435864423252),\n\tKQU( 4588456988775014359), KQU(10699388531797056724),\n\tKQU(  468652268869238792), KQU( 5755943035328078086),\n\tKQU( 2102437379988580216), KQU( 9986312786506674028),\n\tKQU( 2654207180040945604), KQU( 8726634790559960062),\n\tKQU(  100497234871808137), KQU( 2800137176951425819),\n\tKQU( 6076627612918553487), KQU( 5780186919186152796),\n\tKQU( 8179183595769929098), KQU( 6009426283716221169),\n\tKQU( 2796662551397449358), KQU( 1756961367041986764),\n\tKQU( 6972897917355606205), KQU(14524774345368968243),\n\tKQU( 2773529684745706940), KQU( 4853632376213075959),\n\tKQU( 4198177923731358102), KQU( 8271224913084139776),\n\tKQU( 2741753121611092226), KQU(16782366145996731181),\n\tKQU(15426125238972640790), KQU(13595497100671260342),\n\tKQU( 3173531022836259898), KQU( 6573264560319511662),\n\tKQU(18041111951511157441), KQU( 2351433581833135952),\n\tKQU( 3113255578908173487), KQU( 1739371330877858784),\n\tKQU(16046126562789165480), KQU( 8072101652214192925),\n\tKQU(15267091584090664910), KQU( 9309579200403648940),\n\tKQU( 5218892439752408722), KQU(14492477246004337115),\n\tKQU(17431037586679770619), KQU( 7385248135963250480),\n\tKQU( 9580144956565560660), KQU( 4919546228040008720),\n\tKQU(15261542469145035584), KQU(18233297270822253102),\n\tKQU( 5453248417992302857), KQU( 9309519155931460285),\n\tKQU(10342813012345291756), KQU(15676085186784762381),\n\tKQU(15912092950691300645), KQU( 9371053121499003195),\n\tKQU( 9897186478226866746), KQU(14061858287188196327),\n\tKQU(  122575971620788119), KQU(12146750969116317754),\n\tKQU( 4438317272813245201), KQU( 8332576791009527119),\n\tKQU(13907785691786542057), KQU(10374194887283287467),\n\tKQU( 2098798755649059566), KQU( 3416235197748288894),\n\tKQU( 8688269957320773484), KQU( 7503964602397371571),\n\tKQU(16724977015147478236), KQU( 9461512855439858184),\n\tKQU(13259049744534534727), KQU( 3583094952542899294),\n\tKQU( 8764245731305528292), KQU(13240823595462088985),\n\tKQU(13716141617617910448), KQU(18114969519935960955),\n\tKQU( 2297553615798302206), KQU( 4585521442944663362),\n\tKQU(17776858680630198686), KQU( 4685873229192163363),\n\tKQU(  152558080671135627), KQU(15424900540842670088),\n\tKQU(13229630297130024108), KQU(17530268788245718717),\n\tKQU(16675633913065714144), KQU( 3158912717897568068),\n\tKQU(15399132185380087288), KQU( 7401418744515677872),\n\tKQU(13135412922344398535), KQU( 6385314346100509511),\n\tKQU(13962867001134161139), KQU(10272780155442671999),\n\tKQU(12894856086597769142), KQU(13340877795287554994),\n\tKQU(12913630602094607396), KQU(12543167911119793857),\n\tKQU(17343570372251873096), KQU(10959487764494150545),\n\tKQU( 6966737953093821128), KQU(13780699135496988601),\n\tKQU( 4405070719380142046), KQU(14923788365607284982),\n\tKQU( 2869487678905148380), KQU( 6416272754197188403),\n\tKQU(15017380475943612591), KQU( 1995636220918429487),\n\tKQU( 3402016804620122716), KQU(15800188663407057080),\n\tKQU(11362369990390932882), KQU(15262183501637986147),\n\tKQU(10239175385387371494), KQU( 9352042420365748334),\n\tKQU( 1682457034285119875), KQU( 1724710651376289644),\n\tKQU( 2038157098893817966), KQU( 9897825558324608773),\n\tKQU( 1477666236519164736), KQU(16835397314511233640),\n\tKQU(10370866327005346508), KQU(10157504370660621982),\n\tKQU(12113904045335882069), KQU(13326444439742783008),\n\tKQU(11302769043000765804), KQU(13594979923955228484),\n\tKQU(11779351762613475968), KQU( 3786101619539298383),\n\tKQU( 8021122969180846063), KQU(15745904401162500495),\n\tKQU(10762168465993897267), KQU(13552058957896319026),\n\tKQU(11200228655252462013), KQU( 5035370357337441226),\n\tKQU( 7593918984545500013), KQU( 5418554918361528700),\n\tKQU( 4858270799405446371), KQU( 9974659566876282544),\n\tKQU(18227595922273957859), KQU( 2772778443635656220),\n\tKQU(14285143053182085385), KQU( 9939700992429600469),\n\tKQU(12756185904545598068), KQU( 2020783375367345262),\n\tKQU(   57026775058331227), KQU(  950827867930065454),\n\tKQU( 6602279670145371217), KQU( 2291171535443566929),\n\tKQU( 5832380724425010313), KQU( 1220343904715982285),\n\tKQU(17045542598598037633), KQU(15460481779702820971),\n\tKQU(13948388779949365130), KQU(13975040175430829518),\n\tKQU(17477538238425541763), KQU(11104663041851745725),\n\tKQU(15860992957141157587), KQU(14529434633012950138),\n\tKQU( 2504838019075394203), KQU( 7512113882611121886),\n\tKQU( 4859973559980886617), KQU( 1258601555703250219),\n\tKQU(15594548157514316394), KQU( 4516730171963773048),\n\tKQU(11380103193905031983), KQU( 6809282239982353344),\n\tKQU(18045256930420065002), KQU( 2453702683108791859),\n\tKQU(  977214582986981460), KQU( 2006410402232713466),\n\tKQU( 6192236267216378358), KQU( 3429468402195675253),\n\tKQU(18146933153017348921), KQU(17369978576367231139),\n\tKQU( 1246940717230386603), KQU(11335758870083327110),\n\tKQU(14166488801730353682), KQU( 9008573127269635732),\n\tKQU(10776025389820643815), KQU(15087605441903942962),\n\tKQU( 1359542462712147922), KQU(13898874411226454206),\n\tKQU(17911176066536804411), KQU( 9435590428600085274),\n\tKQU(  294488509967864007), KQU( 8890111397567922046),\n\tKQU( 7987823476034328778), KQU(13263827582440967651),\n\tKQU( 7503774813106751573), KQU(14974747296185646837),\n\tKQU( 8504765037032103375), KQU(17340303357444536213),\n\tKQU( 7704610912964485743), KQU( 8107533670327205061),\n\tKQU( 9062969835083315985), KQU(16968963142126734184),\n\tKQU(12958041214190810180), KQU( 2720170147759570200),\n\tKQU( 2986358963942189566), KQU(14884226322219356580),\n\tKQU(  286224325144368520), KQU(11313800433154279797),\n\tKQU(18366849528439673248), KQU(17899725929482368789),\n\tKQU( 3730004284609106799), KQU( 1654474302052767205),\n\tKQU( 5006698007047077032), KQU( 8196893913601182838),\n\tKQU(15214541774425211640), KQU(17391346045606626073),\n\tKQU( 8369003584076969089), KQU( 3939046733368550293),\n\tKQU(10178639720308707785), KQU( 2180248669304388697),\n\tKQU(   62894391300126322), KQU( 9205708961736223191),\n\tKQU( 6837431058165360438), KQU( 3150743890848308214),\n\tKQU(17849330658111464583), KQU(12214815643135450865),\n\tKQU(13410713840519603402), KQU( 3200778126692046802),\n\tKQU(13354780043041779313), KQU(  800850022756886036),\n\tKQU(15660052933953067433), KQU( 6572823544154375676),\n\tKQU(11030281857015819266), KQU(12682241941471433835),\n\tKQU(11654136407300274693), KQU( 4517795492388641109),\n\tKQU( 9757017371504524244), KQU(17833043400781889277),\n\tKQU(12685085201747792227), KQU(10408057728835019573),\n\tKQU(   98370418513455221), KQU( 6732663555696848598),\n\tKQU(13248530959948529780), KQU( 3530441401230622826),\n\tKQU(18188251992895660615), KQU( 1847918354186383756),\n\tKQU( 1127392190402660921), KQU(11293734643143819463),\n\tKQU( 3015506344578682982), KQU(13852645444071153329),\n\tKQU( 2121359659091349142), KQU( 1294604376116677694),\n\tKQU( 5616576231286352318), KQU( 7112502442954235625),\n\tKQU(11676228199551561689), KQU(12925182803007305359),\n\tKQU( 7852375518160493082), KQU( 1136513130539296154),\n\tKQU( 5636923900916593195), KQU( 3221077517612607747),\n\tKQU(17784790465798152513), KQU( 3554210049056995938),\n\tKQU(17476839685878225874), KQU( 3206836372585575732),\n\tKQU( 2765333945644823430), KQU(10080070903718799528),\n\tKQU( 5412370818878286353), KQU( 9689685887726257728),\n\tKQU( 8236117509123533998), KQU( 1951139137165040214),\n\tKQU( 4492205209227980349), KQU(16541291230861602967),\n\tKQU( 1424371548301437940), KQU( 9117562079669206794),\n\tKQU(14374681563251691625), KQU(13873164030199921303),\n\tKQU( 6680317946770936731), KQU(15586334026918276214),\n\tKQU(10896213950976109802), KQU( 9506261949596413689),\n\tKQU( 9903949574308040616), KQU( 6038397344557204470),\n\tKQU(  174601465422373648), KQU(15946141191338238030),\n\tKQU(17142225620992044937), KQU( 7552030283784477064),\n\tKQU( 2947372384532947997), KQU(  510797021688197711),\n\tKQU( 4962499439249363461), KQU(   23770320158385357),\n\tKQU(  959774499105138124), KQU( 1468396011518788276),\n\tKQU( 2015698006852312308), KQU( 4149400718489980136),\n\tKQU( 5992916099522371188), KQU(10819182935265531076),\n\tKQU(16189787999192351131), KQU(  342833961790261950),\n\tKQU(12470830319550495336), KQU(18128495041912812501),\n\tKQU( 1193600899723524337), KQU( 9056793666590079770),\n\tKQU( 2154021227041669041), KQU( 4963570213951235735),\n\tKQU( 4865075960209211409), KQU( 2097724599039942963),\n\tKQU( 2024080278583179845), KQU(11527054549196576736),\n\tKQU(10650256084182390252), KQU( 4808408648695766755),\n\tKQU( 1642839215013788844), KQU(10607187948250398390),\n\tKQU( 7076868166085913508), KQU(  730522571106887032),\n\tKQU(12500579240208524895), KQU( 4484390097311355324),\n\tKQU(15145801330700623870), KQU( 8055827661392944028),\n\tKQU( 5865092976832712268), KQU(15159212508053625143),\n\tKQU( 3560964582876483341), KQU( 4070052741344438280),\n\tKQU( 6032585709886855634), KQU(15643262320904604873),\n\tKQU( 2565119772293371111), KQU(  318314293065348260),\n\tKQU(15047458749141511872), KQU( 7772788389811528730),\n\tKQU( 7081187494343801976), KQU( 6465136009467253947),\n\tKQU(10425940692543362069), KQU(  554608190318339115),\n\tKQU(14796699860302125214), KQU( 1638153134431111443),\n\tKQU(10336967447052276248), KQU( 8412308070396592958),\n\tKQU( 4004557277152051226), KQU( 8143598997278774834),\n\tKQU(16413323996508783221), KQU(13139418758033994949),\n\tKQU( 9772709138335006667), KQU( 2818167159287157659),\n\tKQU(17091740573832523669), KQU(14629199013130751608),\n\tKQU(18268322711500338185), KQU( 8290963415675493063),\n\tKQU( 8830864907452542588), KQU( 1614839084637494849),\n\tKQU(14855358500870422231), KQU( 3472996748392519937),\n\tKQU(15317151166268877716), KQU( 5825895018698400362),\n\tKQU(16730208429367544129), KQU(10481156578141202800),\n\tKQU( 4746166512382823750), KQU(12720876014472464998),\n\tKQU( 8825177124486735972), KQU(13733447296837467838),\n\tKQU( 6412293741681359625), KQU( 8313213138756135033),\n\tKQU(11421481194803712517), KQU( 7997007691544174032),\n\tKQU( 6812963847917605930), KQU( 9683091901227558641),\n\tKQU(14703594165860324713), KQU( 1775476144519618309),\n\tKQU( 2724283288516469519), KQU(  717642555185856868),\n\tKQU( 8736402192215092346), KQU(11878800336431381021),\n\tKQU( 4348816066017061293), KQU( 6115112756583631307),\n\tKQU( 9176597239667142976), KQU(12615622714894259204),\n\tKQU(10283406711301385987), KQU( 5111762509485379420),\n\tKQU( 3118290051198688449), KQU( 7345123071632232145),\n\tKQU( 9176423451688682359), KQU( 4843865456157868971),\n\tKQU(12008036363752566088), KQU(12058837181919397720),\n\tKQU( 2145073958457347366), KQU( 1526504881672818067),\n\tKQU( 3488830105567134848), KQU(13208362960674805143),\n\tKQU( 4077549672899572192), KQU( 7770995684693818365),\n\tKQU( 1398532341546313593), KQU(12711859908703927840),\n\tKQU( 1417561172594446813), KQU(17045191024194170604),\n\tKQU( 4101933177604931713), KQU(14708428834203480320),\n\tKQU(17447509264469407724), KQU(14314821973983434255),\n\tKQU(17990472271061617265), KQU( 5087756685841673942),\n\tKQU(12797820586893859939), KQU( 1778128952671092879),\n\tKQU( 3535918530508665898), KQU( 9035729701042481301),\n\tKQU(14808661568277079962), KQU(14587345077537747914),\n\tKQU(11920080002323122708), KQU( 6426515805197278753),\n\tKQU( 3295612216725984831), KQU(11040722532100876120),\n\tKQU(12305952936387598754), KQU(16097391899742004253),\n\tKQU( 4908537335606182208), KQU(12446674552196795504),\n\tKQU(16010497855816895177), KQU( 9194378874788615551),\n\tKQU( 3382957529567613384), KQU( 5154647600754974077),\n\tKQU( 9801822865328396141), KQU( 9023662173919288143),\n\tKQU(17623115353825147868), KQU( 8238115767443015816),\n\tKQU(15811444159859002560), KQU( 9085612528904059661),\n\tKQU( 6888601089398614254), KQU(  258252992894160189),\n\tKQU( 6704363880792428622), KQU( 6114966032147235763),\n\tKQU(11075393882690261875), KQU( 8797664238933620407),\n\tKQU( 5901892006476726920), KQU( 5309780159285518958),\n\tKQU(14940808387240817367), KQU(14642032021449656698),\n\tKQU( 9808256672068504139), KQU( 3670135111380607658),\n\tKQU(11211211097845960152), KQU( 1474304506716695808),\n\tKQU(15843166204506876239), KQU( 7661051252471780561),\n\tKQU(10170905502249418476), KQU( 7801416045582028589),\n\tKQU( 2763981484737053050), KQU( 9491377905499253054),\n\tKQU(16201395896336915095), KQU( 9256513756442782198),\n\tKQU( 5411283157972456034), KQU( 5059433122288321676),\n\tKQU( 4327408006721123357), KQU( 9278544078834433377),\n\tKQU( 7601527110882281612), KQU(11848295896975505251),\n\tKQU(12096998801094735560), KQU(14773480339823506413),\n\tKQU(15586227433895802149), KQU(12786541257830242872),\n\tKQU( 6904692985140503067), KQU( 5309011515263103959),\n\tKQU(12105257191179371066), KQU(14654380212442225037),\n\tKQU( 2556774974190695009), KQU( 4461297399927600261),\n\tKQU(14888225660915118646), KQU(14915459341148291824),\n\tKQU( 2738802166252327631), KQU( 6047155789239131512),\n\tKQU(12920545353217010338), KQU(10697617257007840205),\n\tKQU( 2751585253158203504), KQU(13252729159780047496),\n\tKQU(14700326134672815469), KQU(14082527904374600529),\n\tKQU(16852962273496542070), KQU(17446675504235853907),\n\tKQU(15019600398527572311), KQU(12312781346344081551),\n\tKQU(14524667935039810450), KQU( 5634005663377195738),\n\tKQU(11375574739525000569), KQU( 2423665396433260040),\n\tKQU( 5222836914796015410), KQU( 4397666386492647387),\n\tKQU( 4619294441691707638), KQU(  665088602354770716),\n\tKQU(13246495665281593610), KQU( 6564144270549729409),\n\tKQU(10223216188145661688), KQU( 3961556907299230585),\n\tKQU(11543262515492439914), KQU(16118031437285993790),\n\tKQU( 7143417964520166465), KQU(13295053515909486772),\n\tKQU(   40434666004899675), KQU(17127804194038347164),\n\tKQU( 8599165966560586269), KQU( 8214016749011284903),\n\tKQU(13725130352140465239), KQU( 5467254474431726291),\n\tKQU( 7748584297438219877), KQU(16933551114829772472),\n\tKQU( 2169618439506799400), KQU( 2169787627665113463),\n\tKQU(17314493571267943764), KQU(18053575102911354912),\n\tKQU(11928303275378476973), KQU(11593850925061715550),\n\tKQU(17782269923473589362), KQU( 3280235307704747039),\n\tKQU( 6145343578598685149), KQU(17080117031114086090),\n\tKQU(18066839902983594755), KQU( 6517508430331020706),\n\tKQU( 8092908893950411541), KQU(12558378233386153732),\n\tKQU( 4476532167973132976), KQU(16081642430367025016),\n\tKQU( 4233154094369139361), KQU( 8693630486693161027),\n\tKQU(11244959343027742285), KQU(12273503967768513508),\n\tKQU(14108978636385284876), KQU( 7242414665378826984),\n\tKQU( 6561316938846562432), KQU( 8601038474994665795),\n\tKQU(17532942353612365904), KQU(17940076637020912186),\n\tKQU( 7340260368823171304), KQU( 7061807613916067905),\n\tKQU(10561734935039519326), KQU(17990796503724650862),\n\tKQU( 6208732943911827159), KQU(  359077562804090617),\n\tKQU(14177751537784403113), KQU(10659599444915362902),\n\tKQU(15081727220615085833), KQU(13417573895659757486),\n\tKQU(15513842342017811524), KQU(11814141516204288231),\n\tKQU( 1827312513875101814), KQU( 2804611699894603103),\n\tKQU(17116500469975602763), KQU(12270191815211952087),\n\tKQU(12256358467786024988), KQU(18435021722453971267),\n\tKQU(  671330264390865618), KQU(  476504300460286050),\n\tKQU(16465470901027093441), KQU( 4047724406247136402),\n\tKQU( 1322305451411883346), KQU( 1388308688834322280),\n\tKQU( 7303989085269758176), KQU( 9323792664765233642),\n\tKQU( 4542762575316368936), KQU(17342696132794337618),\n\tKQU( 4588025054768498379), KQU(13415475057390330804),\n\tKQU(17880279491733405570), KQU(10610553400618620353),\n\tKQU( 3180842072658960139), KQU(13002966655454270120),\n\tKQU( 1665301181064982826), KQU( 7083673946791258979),\n\tKQU(  190522247122496820), KQU(17388280237250677740),\n\tKQU( 8430770379923642945), KQU(12987180971921668584),\n\tKQU( 2311086108365390642), KQU( 2870984383579822345),\n\tKQU(14014682609164653318), KQU(14467187293062251484),\n\tKQU(  192186361147413298), KQU(15171951713531796524),\n\tKQU( 9900305495015948728), KQU(17958004775615466344),\n\tKQU(14346380954498606514), KQU(18040047357617407096),\n\tKQU( 5035237584833424532), KQU(15089555460613972287),\n\tKQU( 4131411873749729831), KQU( 1329013581168250330),\n\tKQU(10095353333051193949), KQU(10749518561022462716),\n\tKQU( 9050611429810755847), KQU(15022028840236655649),\n\tKQU( 8775554279239748298), KQU(13105754025489230502),\n\tKQU(15471300118574167585), KQU(   89864764002355628),\n\tKQU( 8776416323420466637), KQU( 5280258630612040891),\n\tKQU( 2719174488591862912), KQU( 7599309137399661994),\n\tKQU(15012887256778039979), KQU(14062981725630928925),\n\tKQU(12038536286991689603), KQU( 7089756544681775245),\n\tKQU(10376661532744718039), KQU( 1265198725901533130),\n\tKQU(13807996727081142408), KQU( 2935019626765036403),\n\tKQU( 7651672460680700141), KQU( 3644093016200370795),\n\tKQU( 2840982578090080674), KQU(17956262740157449201),\n\tKQU(18267979450492880548), KQU(11799503659796848070),\n\tKQU( 9942537025669672388), KQU(11886606816406990297),\n\tKQU( 5488594946437447576), KQU( 7226714353282744302),\n\tKQU( 3784851653123877043), KQU(  878018453244803041),\n\tKQU(12110022586268616085), KQU(  734072179404675123),\n\tKQU(11869573627998248542), KQU(  469150421297783998),\n\tKQU(  260151124912803804), KQU(11639179410120968649),\n\tKQU( 9318165193840846253), KQU(12795671722734758075),\n\tKQU(15318410297267253933), KQU(  691524703570062620),\n\tKQU( 5837129010576994601), KQU(15045963859726941052),\n\tKQU( 5850056944932238169), KQU(12017434144750943807),\n\tKQU( 7447139064928956574), KQU( 3101711812658245019),\n\tKQU(16052940704474982954), KQU(18195745945986994042),\n\tKQU( 8932252132785575659), KQU(13390817488106794834),\n\tKQU(11582771836502517453), KQU( 4964411326683611686),\n\tKQU( 2195093981702694011), KQU(14145229538389675669),\n\tKQU(16459605532062271798), KQU(  866316924816482864),\n\tKQU( 4593041209937286377), KQU( 8415491391910972138),\n\tKQU( 4171236715600528969), KQU(16637569303336782889),\n\tKQU( 2002011073439212680), KQU(17695124661097601411),\n\tKQU( 4627687053598611702), KQU( 7895831936020190403),\n\tKQU( 8455951300917267802), KQU( 2923861649108534854),\n\tKQU( 8344557563927786255), KQU( 6408671940373352556),\n\tKQU(12210227354536675772), KQU(14294804157294222295),\n\tKQU(10103022425071085127), KQU(10092959489504123771),\n\tKQU( 6554774405376736268), KQU(12629917718410641774),\n\tKQU( 6260933257596067126), KQU( 2460827021439369673),\n\tKQU( 2541962996717103668), KQU(  597377203127351475),\n\tKQU( 5316984203117315309), KQU( 4811211393563241961),\n\tKQU(13119698597255811641), KQU( 8048691512862388981),\n\tKQU(10216818971194073842), KQU( 4612229970165291764),\n\tKQU(10000980798419974770), KQU( 6877640812402540687),\n\tKQU( 1488727563290436992), KQU( 2227774069895697318),\n\tKQU(11237754507523316593), KQU(13478948605382290972),\n\tKQU( 1963583846976858124), KQU( 5512309205269276457),\n\tKQU( 3972770164717652347), KQU( 3841751276198975037),\n\tKQU(10283343042181903117), KQU( 8564001259792872199),\n\tKQU(16472187244722489221), KQU( 8953493499268945921),\n\tKQU( 3518747340357279580), KQU( 4003157546223963073),\n\tKQU( 3270305958289814590), KQU( 3966704458129482496),\n\tKQU( 8122141865926661939), KQU(14627734748099506653),\n\tKQU(13064426990862560568), KQU( 2414079187889870829),\n\tKQU( 5378461209354225306), KQU(10841985740128255566),\n\tKQU(  538582442885401738), KQU( 7535089183482905946),\n\tKQU(16117559957598879095), KQU( 8477890721414539741),\n\tKQU( 1459127491209533386), KQU(17035126360733620462),\n\tKQU( 8517668552872379126), KQU(10292151468337355014),\n\tKQU(17081267732745344157), KQU(13751455337946087178),\n\tKQU(14026945459523832966), KQU( 6653278775061723516),\n\tKQU(10619085543856390441), KQU( 2196343631481122885),\n\tKQU(10045966074702826136), KQU(10082317330452718282),\n\tKQU( 5920859259504831242), KQU( 9951879073426540617),\n\tKQU( 7074696649151414158), KQU(15808193543879464318),\n\tKQU( 7385247772746953374), KQU( 3192003544283864292),\n\tKQU(18153684490917593847), KQU(12423498260668568905),\n\tKQU(10957758099756378169), KQU(11488762179911016040),\n\tKQU( 2099931186465333782), KQU(11180979581250294432),\n\tKQU( 8098916250668367933), KQU( 3529200436790763465),\n\tKQU(12988418908674681745), KQU( 6147567275954808580),\n\tKQU( 3207503344604030989), KQU(10761592604898615360),\n\tKQU(  229854861031893504), KQU( 8809853962667144291),\n\tKQU(13957364469005693860), KQU( 7634287665224495886),\n\tKQU(12353487366976556874), KQU( 1134423796317152034),\n\tKQU( 2088992471334107068), KQU( 7393372127190799698),\n\tKQU( 1845367839871058391), KQU(  207922563987322884),\n\tKQU(11960870813159944976), KQU(12182120053317317363),\n\tKQU(17307358132571709283), KQU(13871081155552824936),\n\tKQU(18304446751741566262), KQU( 7178705220184302849),\n\tKQU(10929605677758824425), KQU(16446976977835806844),\n\tKQU(13723874412159769044), KQU( 6942854352100915216),\n\tKQU( 1726308474365729390), KQU( 2150078766445323155),\n\tKQU(15345558947919656626), KQU(12145453828874527201),\n\tKQU( 2054448620739726849), KQU( 2740102003352628137),\n\tKQU(11294462163577610655), KQU(  756164283387413743),\n\tKQU(17841144758438810880), KQU(10802406021185415861),\n\tKQU( 8716455530476737846), KQU( 6321788834517649606),\n\tKQU(14681322910577468426), KQU(17330043563884336387),\n\tKQU(12701802180050071614), KQU(14695105111079727151),\n\tKQU( 5112098511654172830), KQU( 4957505496794139973),\n\tKQU( 8270979451952045982), KQU(12307685939199120969),\n\tKQU(12425799408953443032), KQU( 8376410143634796588),\n\tKQU(16621778679680060464), KQU( 3580497854566660073),\n\tKQU( 1122515747803382416), KQU(  857664980960597599),\n\tKQU( 6343640119895925918), KQU(12878473260854462891),\n\tKQU(10036813920765722626), KQU(14451335468363173812),\n\tKQU( 5476809692401102807), KQU(16442255173514366342),\n\tKQU(13060203194757167104), KQU(14354124071243177715),\n\tKQU(15961249405696125227), KQU(13703893649690872584),\n\tKQU(  363907326340340064), KQU( 6247455540491754842),\n\tKQU(12242249332757832361), KQU(  156065475679796717),\n\tKQU( 9351116235749732355), KQU( 4590350628677701405),\n\tKQU( 1671195940982350389), KQU(13501398458898451905),\n\tKQU( 6526341991225002255), KQU( 1689782913778157592),\n\tKQU( 7439222350869010334), KQU(13975150263226478308),\n\tKQU(11411961169932682710), KQU(17204271834833847277),\n\tKQU(  541534742544435367), KQU( 6591191931218949684),\n\tKQU( 2645454775478232486), KQU( 4322857481256485321),\n\tKQU( 8477416487553065110), KQU(12902505428548435048),\n\tKQU(  971445777981341415), KQU(14995104682744976712),\n\tKQU( 4243341648807158063), KQU( 8695061252721927661),\n\tKQU( 5028202003270177222), KQU( 2289257340915567840),\n\tKQU(13870416345121866007), KQU(13994481698072092233),\n\tKQU( 6912785400753196481), KQU( 2278309315841980139),\n\tKQU( 4329765449648304839), KQU( 5963108095785485298),\n\tKQU( 4880024847478722478), KQU(16015608779890240947),\n\tKQU( 1866679034261393544), KQU(  914821179919731519),\n\tKQU( 9643404035648760131), KQU( 2418114953615593915),\n\tKQU(  944756836073702374), KQU(15186388048737296834),\n\tKQU( 7723355336128442206), KQU( 7500747479679599691),\n\tKQU(18013961306453293634), KQU( 2315274808095756456),\n\tKQU(13655308255424029566), KQU(17203800273561677098),\n\tKQU( 1382158694422087756), KQU( 5090390250309588976),\n\tKQU(  517170818384213989), KQU( 1612709252627729621),\n\tKQU( 1330118955572449606), KQU(  300922478056709885),\n\tKQU(18115693291289091987), KQU(13491407109725238321),\n\tKQU(15293714633593827320), KQU( 5151539373053314504),\n\tKQU( 5951523243743139207), KQU(14459112015249527975),\n\tKQU( 5456113959000700739), KQU( 3877918438464873016),\n\tKQU(12534071654260163555), KQU(15871678376893555041),\n\tKQU(11005484805712025549), KQU(16353066973143374252),\n\tKQU( 4358331472063256685), KQU( 8268349332210859288),\n\tKQU(12485161590939658075), KQU(13955993592854471343),\n\tKQU( 5911446886848367039), KQU(14925834086813706974),\n\tKQU( 6590362597857994805), KQU( 1280544923533661875),\n\tKQU( 1637756018947988164), KQU( 4734090064512686329),\n\tKQU(16693705263131485912), KQU( 6834882340494360958),\n\tKQU( 8120732176159658505), KQU( 2244371958905329346),\n\tKQU(10447499707729734021), KQU( 7318742361446942194),\n\tKQU( 8032857516355555296), KQU(14023605983059313116),\n\tKQU( 1032336061815461376), KQU( 9840995337876562612),\n\tKQU( 9869256223029203587), KQU(12227975697177267636),\n\tKQU(12728115115844186033), KQU( 7752058479783205470),\n\tKQU(  729733219713393087), KQU(12954017801239007622)\n};\nstatic const uint64_t init_by_array_64_expected[] = {\n\tKQU( 2100341266307895239), KQU( 8344256300489757943),\n\tKQU(15687933285484243894), KQU( 8268620370277076319),\n\tKQU(12371852309826545459), KQU( 8800491541730110238),\n\tKQU(18113268950100835773), KQU( 2886823658884438119),\n\tKQU( 3293667307248180724), KQU( 9307928143300172731),\n\tKQU( 7688082017574293629), KQU(  900986224735166665),\n\tKQU( 9977972710722265039), KQU( 6008205004994830552),\n\tKQU(  546909104521689292), KQU( 7428471521869107594),\n\tKQU(14777563419314721179), KQU(16116143076567350053),\n\tKQU( 5322685342003142329), KQU( 4200427048445863473),\n\tKQU( 4693092150132559146), KQU(13671425863759338582),\n\tKQU( 6747117460737639916), KQU( 4732666080236551150),\n\tKQU( 5912839950611941263), KQU( 3903717554504704909),\n\tKQU( 2615667650256786818), KQU(10844129913887006352),\n\tKQU(13786467861810997820), KQU(14267853002994021570),\n\tKQU(13767807302847237439), KQU(16407963253707224617),\n\tKQU( 4802498363698583497), KQU( 2523802839317209764),\n\tKQU( 3822579397797475589), KQU( 8950320572212130610),\n\tKQU( 3745623504978342534), KQU(16092609066068482806),\n\tKQU( 9817016950274642398), KQU(10591660660323829098),\n\tKQU(11751606650792815920), KQU( 5122873818577122211),\n\tKQU(17209553764913936624), KQU( 6249057709284380343),\n\tKQU(15088791264695071830), KQU(15344673071709851930),\n\tKQU( 4345751415293646084), KQU( 2542865750703067928),\n\tKQU(13520525127852368784), KQU(18294188662880997241),\n\tKQU( 3871781938044881523), KQU( 2873487268122812184),\n\tKQU(15099676759482679005), KQU(15442599127239350490),\n\tKQU( 6311893274367710888), KQU( 3286118760484672933),\n\tKQU( 4146067961333542189), KQU(13303942567897208770),\n\tKQU( 8196013722255630418), KQU( 4437815439340979989),\n\tKQU(15433791533450605135), KQU( 4254828956815687049),\n\tKQU( 1310903207708286015), KQU(10529182764462398549),\n\tKQU(14900231311660638810), KQU( 9727017277104609793),\n\tKQU( 1821308310948199033), KQU(11628861435066772084),\n\tKQU( 9469019138491546924), KQU( 3145812670532604988),\n\tKQU( 9938468915045491919), KQU( 1562447430672662142),\n\tKQU(13963995266697989134), KQU( 3356884357625028695),\n\tKQU( 4499850304584309747), KQU( 8456825817023658122),\n\tKQU(10859039922814285279), KQU( 8099512337972526555),\n\tKQU(  348006375109672149), KQU(11919893998241688603),\n\tKQU( 1104199577402948826), KQU(16689191854356060289),\n\tKQU(10992552041730168078), KQU( 7243733172705465836),\n\tKQU( 5668075606180319560), KQU(18182847037333286970),\n\tKQU( 4290215357664631322), KQU( 4061414220791828613),\n\tKQU(13006291061652989604), KQU( 7140491178917128798),\n\tKQU(12703446217663283481), KQU( 5500220597564558267),\n\tKQU(10330551509971296358), KQU(15958554768648714492),\n\tKQU( 5174555954515360045), KQU( 1731318837687577735),\n\tKQU( 3557700801048354857), KQU(13764012341928616198),\n\tKQU(13115166194379119043), KQU( 7989321021560255519),\n\tKQU( 2103584280905877040), KQU( 9230788662155228488),\n\tKQU(16396629323325547654), KQU(  657926409811318051),\n\tKQU(15046700264391400727), KQU( 5120132858771880830),\n\tKQU( 7934160097989028561), KQU( 6963121488531976245),\n\tKQU(17412329602621742089), KQU(15144843053931774092),\n\tKQU(17204176651763054532), KQU(13166595387554065870),\n\tKQU( 8590377810513960213), KQU( 5834365135373991938),\n\tKQU( 7640913007182226243), KQU( 3479394703859418425),\n\tKQU(16402784452644521040), KQU( 4993979809687083980),\n\tKQU(13254522168097688865), KQU(15643659095244365219),\n\tKQU( 5881437660538424982), KQU(11174892200618987379),\n\tKQU(  254409966159711077), KQU(17158413043140549909),\n\tKQU( 3638048789290376272), KQU( 1376816930299489190),\n\tKQU( 4622462095217761923), KQU(15086407973010263515),\n\tKQU(13253971772784692238), KQU( 5270549043541649236),\n\tKQU(11182714186805411604), KQU(12283846437495577140),\n\tKQU( 5297647149908953219), KQU(10047451738316836654),\n\tKQU( 4938228100367874746), KQU(12328523025304077923),\n\tKQU( 3601049438595312361), KQU( 9313624118352733770),\n\tKQU(13322966086117661798), KQU(16660005705644029394),\n\tKQU(11337677526988872373), KQU(13869299102574417795),\n\tKQU(15642043183045645437), KQU( 3021755569085880019),\n\tKQU( 4979741767761188161), KQU(13679979092079279587),\n\tKQU( 3344685842861071743), KQU(13947960059899588104),\n\tKQU(  305806934293368007), KQU( 5749173929201650029),\n\tKQU(11123724852118844098), KQU(15128987688788879802),\n\tKQU(15251651211024665009), KQU( 7689925933816577776),\n\tKQU(16732804392695859449), KQU(17087345401014078468),\n\tKQU(14315108589159048871), KQU( 4820700266619778917),\n\tKQU(16709637539357958441), KQU( 4936227875177351374),\n\tKQU( 2137907697912987247), KQU(11628565601408395420),\n\tKQU( 2333250549241556786), KQU( 5711200379577778637),\n\tKQU( 5170680131529031729), KQU(12620392043061335164),\n\tKQU(   95363390101096078), KQU( 5487981914081709462),\n\tKQU( 1763109823981838620), KQU( 3395861271473224396),\n\tKQU( 1300496844282213595), KQU( 6894316212820232902),\n\tKQU(10673859651135576674), KQU( 5911839658857903252),\n\tKQU(17407110743387299102), KQU( 8257427154623140385),\n\tKQU(11389003026741800267), KQU( 4070043211095013717),\n\tKQU(11663806997145259025), KQU(15265598950648798210),\n\tKQU(  630585789434030934), KQU( 3524446529213587334),\n\tKQU( 7186424168495184211), KQU(10806585451386379021),\n\tKQU(11120017753500499273), KQU( 1586837651387701301),\n\tKQU(17530454400954415544), KQU( 9991670045077880430),\n\tKQU( 7550997268990730180), KQU( 8640249196597379304),\n\tKQU( 3522203892786893823), KQU(10401116549878854788),\n\tKQU(13690285544733124852), KQU( 8295785675455774586),\n\tKQU(15535716172155117603), KQU( 3112108583723722511),\n\tKQU(17633179955339271113), KQU(18154208056063759375),\n\tKQU( 1866409236285815666), KQU(13326075895396412882),\n\tKQU( 8756261842948020025), KQU( 6281852999868439131),\n\tKQU(15087653361275292858), KQU(10333923911152949397),\n\tKQU( 5265567645757408500), KQU(12728041843210352184),\n\tKQU( 6347959327507828759), KQU(  154112802625564758),\n\tKQU(18235228308679780218), KQU( 3253805274673352418),\n\tKQU( 4849171610689031197), KQU(17948529398340432518),\n\tKQU(13803510475637409167), KQU(13506570190409883095),\n\tKQU(15870801273282960805), KQU( 8451286481299170773),\n\tKQU( 9562190620034457541), KQU( 8518905387449138364),\n\tKQU(12681306401363385655), KQU( 3788073690559762558),\n\tKQU( 5256820289573487769), KQU( 2752021372314875467),\n\tKQU( 6354035166862520716), KQU( 4328956378309739069),\n\tKQU(  449087441228269600), KQU( 5533508742653090868),\n\tKQU( 1260389420404746988), KQU(18175394473289055097),\n\tKQU( 1535467109660399420), KQU( 8818894282874061442),\n\tKQU(12140873243824811213), KQU(15031386653823014946),\n\tKQU( 1286028221456149232), KQU( 6329608889367858784),\n\tKQU( 9419654354945132725), KQU( 6094576547061672379),\n\tKQU(17706217251847450255), KQU( 1733495073065878126),\n\tKQU(16918923754607552663), KQU( 8881949849954945044),\n\tKQU(12938977706896313891), KQU(14043628638299793407),\n\tKQU(18393874581723718233), KQU( 6886318534846892044),\n\tKQU(14577870878038334081), KQU(13541558383439414119),\n\tKQU(13570472158807588273), KQU(18300760537910283361),\n\tKQU(  818368572800609205), KQU( 1417000585112573219),\n\tKQU(12337533143867683655), KQU(12433180994702314480),\n\tKQU(  778190005829189083), KQU(13667356216206524711),\n\tKQU( 9866149895295225230), KQU(11043240490417111999),\n\tKQU( 1123933826541378598), KQU( 6469631933605123610),\n\tKQU(14508554074431980040), KQU(13918931242962026714),\n\tKQU( 2870785929342348285), KQU(14786362626740736974),\n\tKQU(13176680060902695786), KQU( 9591778613541679456),\n\tKQU( 9097662885117436706), KQU(  749262234240924947),\n\tKQU( 1944844067793307093), KQU( 4339214904577487742),\n\tKQU( 8009584152961946551), KQU(16073159501225501777),\n\tKQU( 3335870590499306217), KQU(17088312653151202847),\n\tKQU( 3108893142681931848), KQU(16636841767202792021),\n\tKQU(10423316431118400637), KQU( 8008357368674443506),\n\tKQU(11340015231914677875), KQU(17687896501594936090),\n\tKQU(15173627921763199958), KQU(  542569482243721959),\n\tKQU(15071714982769812975), KQU( 4466624872151386956),\n\tKQU( 1901780715602332461), KQU( 9822227742154351098),\n\tKQU( 1479332892928648780), KQU( 6981611948382474400),\n\tKQU( 7620824924456077376), KQU(14095973329429406782),\n\tKQU( 7902744005696185404), KQU(15830577219375036920),\n\tKQU(10287076667317764416), KQU(12334872764071724025),\n\tKQU( 4419302088133544331), KQU(14455842851266090520),\n\tKQU(12488077416504654222), KQU( 7953892017701886766),\n\tKQU( 6331484925529519007), KQU( 4902145853785030022),\n\tKQU(17010159216096443073), KQU(11945354668653886087),\n\tKQU(15112022728645230829), KQU(17363484484522986742),\n\tKQU( 4423497825896692887), KQU( 8155489510809067471),\n\tKQU(  258966605622576285), KQU( 5462958075742020534),\n\tKQU( 6763710214913276228), KQU( 2368935183451109054),\n\tKQU(14209506165246453811), KQU( 2646257040978514881),\n\tKQU( 3776001911922207672), KQU( 1419304601390147631),\n\tKQU(14987366598022458284), KQU( 3977770701065815721),\n\tKQU(  730820417451838898), KQU( 3982991703612885327),\n\tKQU( 2803544519671388477), KQU(17067667221114424649),\n\tKQU( 2922555119737867166), KQU( 1989477584121460932),\n\tKQU(15020387605892337354), KQU( 9293277796427533547),\n\tKQU(10722181424063557247), KQU(16704542332047511651),\n\tKQU( 5008286236142089514), KQU(16174732308747382540),\n\tKQU(17597019485798338402), KQU(13081745199110622093),\n\tKQU( 8850305883842258115), KQU(12723629125624589005),\n\tKQU( 8140566453402805978), KQU(15356684607680935061),\n\tKQU(14222190387342648650), KQU(11134610460665975178),\n\tKQU( 1259799058620984266), KQU(13281656268025610041),\n\tKQU(  298262561068153992), KQU(12277871700239212922),\n\tKQU(13911297774719779438), KQU(16556727962761474934),\n\tKQU(17903010316654728010), KQU( 9682617699648434744),\n\tKQU(14757681836838592850), KQU( 1327242446558524473),\n\tKQU(11126645098780572792), KQU( 1883602329313221774),\n\tKQU( 2543897783922776873), KQU(15029168513767772842),\n\tKQU(12710270651039129878), KQU(16118202956069604504),\n\tKQU(15010759372168680524), KQU( 2296827082251923948),\n\tKQU(10793729742623518101), KQU(13829764151845413046),\n\tKQU(17769301223184451213), KQU( 3118268169210783372),\n\tKQU(17626204544105123127), KQU( 7416718488974352644),\n\tKQU(10450751996212925994), KQU( 9352529519128770586),\n\tKQU(  259347569641110140), KQU( 8048588892269692697),\n\tKQU( 1774414152306494058), KQU(10669548347214355622),\n\tKQU(13061992253816795081), KQU(18432677803063861659),\n\tKQU( 8879191055593984333), KQU(12433753195199268041),\n\tKQU(14919392415439730602), KQU( 6612848378595332963),\n\tKQU( 6320986812036143628), KQU(10465592420226092859),\n\tKQU( 4196009278962570808), KQU( 3747816564473572224),\n\tKQU(17941203486133732898), KQU( 2350310037040505198),\n\tKQU( 5811779859134370113), KQU(10492109599506195126),\n\tKQU( 7699650690179541274), KQU( 1954338494306022961),\n\tKQU(14095816969027231152), KQU( 5841346919964852061),\n\tKQU(14945969510148214735), KQU( 3680200305887550992),\n\tKQU( 6218047466131695792), KQU( 8242165745175775096),\n\tKQU(11021371934053307357), KQU( 1265099502753169797),\n\tKQU( 4644347436111321718), KQU( 3609296916782832859),\n\tKQU( 8109807992218521571), KQU(18387884215648662020),\n\tKQU(14656324896296392902), KQU(17386819091238216751),\n\tKQU(17788300878582317152), KQU( 7919446259742399591),\n\tKQU( 4466613134576358004), KQU(12928181023667938509),\n\tKQU(13147446154454932030), KQU(16552129038252734620),\n\tKQU( 8395299403738822450), KQU(11313817655275361164),\n\tKQU(  434258809499511718), KQU( 2074882104954788676),\n\tKQU( 7929892178759395518), KQU( 9006461629105745388),\n\tKQU( 5176475650000323086), KQU(11128357033468341069),\n\tKQU(12026158851559118955), KQU(14699716249471156500),\n\tKQU(  448982497120206757), KQU( 4156475356685519900),\n\tKQU( 6063816103417215727), KQU(10073289387954971479),\n\tKQU( 8174466846138590962), KQU( 2675777452363449006),\n\tKQU( 9090685420572474281), KQU( 6659652652765562060),\n\tKQU(12923120304018106621), KQU(11117480560334526775),\n\tKQU(  937910473424587511), KQU( 1838692113502346645),\n\tKQU(11133914074648726180), KQU( 7922600945143884053),\n\tKQU(13435287702700959550), KQU( 5287964921251123332),\n\tKQU(11354875374575318947), KQU(17955724760748238133),\n\tKQU(13728617396297106512), KQU( 4107449660118101255),\n\tKQU( 1210269794886589623), KQU(11408687205733456282),\n\tKQU( 4538354710392677887), KQU(13566803319341319267),\n\tKQU(17870798107734050771), KQU( 3354318982568089135),\n\tKQU( 9034450839405133651), KQU(13087431795753424314),\n\tKQU(  950333102820688239), KQU( 1968360654535604116),\n\tKQU(16840551645563314995), KQU( 8867501803892924995),\n\tKQU(11395388644490626845), KQU( 1529815836300732204),\n\tKQU(13330848522996608842), KQU( 1813432878817504265),\n\tKQU( 2336867432693429560), KQU(15192805445973385902),\n\tKQU( 2528593071076407877), KQU(  128459777936689248),\n\tKQU( 9976345382867214866), KQU( 6208885766767996043),\n\tKQU(14982349522273141706), KQU( 3099654362410737822),\n\tKQU(13776700761947297661), KQU( 8806185470684925550),\n\tKQU( 8151717890410585321), KQU(  640860591588072925),\n\tKQU(14592096303937307465), KQU( 9056472419613564846),\n\tKQU(14861544647742266352), KQU(12703771500398470216),\n\tKQU( 3142372800384138465), KQU( 6201105606917248196),\n\tKQU(18337516409359270184), KQU(15042268695665115339),\n\tKQU(15188246541383283846), KQU(12800028693090114519),\n\tKQU( 5992859621101493472), KQU(18278043971816803521),\n\tKQU( 9002773075219424560), KQU( 7325707116943598353),\n\tKQU( 7930571931248040822), KQU( 5645275869617023448),\n\tKQU( 7266107455295958487), KQU( 4363664528273524411),\n\tKQU(14313875763787479809), KQU(17059695613553486802),\n\tKQU( 9247761425889940932), KQU(13704726459237593128),\n\tKQU( 2701312427328909832), KQU(17235532008287243115),\n\tKQU(14093147761491729538), KQU( 6247352273768386516),\n\tKQU( 8268710048153268415), KQU( 7985295214477182083),\n\tKQU(15624495190888896807), KQU( 3772753430045262788),\n\tKQU( 9133991620474991698), KQU( 5665791943316256028),\n\tKQU( 7551996832462193473), KQU(13163729206798953877),\n\tKQU( 9263532074153846374), KQU( 1015460703698618353),\n\tKQU(17929874696989519390), KQU(18257884721466153847),\n\tKQU(16271867543011222991), KQU( 3905971519021791941),\n\tKQU(16814488397137052085), KQU( 1321197685504621613),\n\tKQU( 2870359191894002181), KQU(14317282970323395450),\n\tKQU(13663920845511074366), KQU( 2052463995796539594),\n\tKQU(14126345686431444337), KQU( 1727572121947022534),\n\tKQU(17793552254485594241), KQU( 6738857418849205750),\n\tKQU( 1282987123157442952), KQU(16655480021581159251),\n\tKQU( 6784587032080183866), KQU(14726758805359965162),\n\tKQU( 7577995933961987349), KQU(12539609320311114036),\n\tKQU(10789773033385439494), KQU( 8517001497411158227),\n\tKQU(10075543932136339710), KQU(14838152340938811081),\n\tKQU( 9560840631794044194), KQU(17445736541454117475),\n\tKQU(10633026464336393186), KQU(15705729708242246293),\n\tKQU( 1117517596891411098), KQU( 4305657943415886942),\n\tKQU( 4948856840533979263), KQU(16071681989041789593),\n\tKQU(13723031429272486527), KQU( 7639567622306509462),\n\tKQU(12670424537483090390), KQU( 9715223453097197134),\n\tKQU( 5457173389992686394), KQU(  289857129276135145),\n\tKQU(17048610270521972512), KQU(  692768013309835485),\n\tKQU(14823232360546632057), KQU(18218002361317895936),\n\tKQU( 3281724260212650204), KQU(16453957266549513795),\n\tKQU( 8592711109774511881), KQU(  929825123473369579),\n\tKQU(15966784769764367791), KQU( 9627344291450607588),\n\tKQU(10849555504977813287), KQU( 9234566913936339275),\n\tKQU( 6413807690366911210), KQU(10862389016184219267),\n\tKQU(13842504799335374048), KQU( 1531994113376881174),\n\tKQU( 2081314867544364459), KQU(16430628791616959932),\n\tKQU( 8314714038654394368), KQU( 9155473892098431813),\n\tKQU(12577843786670475704), KQU( 4399161106452401017),\n\tKQU( 1668083091682623186), KQU( 1741383777203714216),\n\tKQU( 2162597285417794374), KQU(15841980159165218736),\n\tKQU( 1971354603551467079), KQU( 1206714764913205968),\n\tKQU( 4790860439591272330), KQU(14699375615594055799),\n\tKQU( 8374423871657449988), KQU(10950685736472937738),\n\tKQU(  697344331343267176), KQU(10084998763118059810),\n\tKQU(12897369539795983124), KQU(12351260292144383605),\n\tKQU( 1268810970176811234), KQU( 7406287800414582768),\n\tKQU(  516169557043807831), KQU( 5077568278710520380),\n\tKQU( 3828791738309039304), KQU( 7721974069946943610),\n\tKQU( 3534670260981096460), KQU( 4865792189600584891),\n\tKQU(16892578493734337298), KQU( 9161499464278042590),\n\tKQU(11976149624067055931), KQU(13219479887277343990),\n\tKQU(14161556738111500680), KQU(14670715255011223056),\n\tKQU( 4671205678403576558), KQU(12633022931454259781),\n\tKQU(14821376219869187646), KQU(  751181776484317028),\n\tKQU( 2192211308839047070), KQU(11787306362361245189),\n\tKQU(10672375120744095707), KQU( 4601972328345244467),\n\tKQU(15457217788831125879), KQU( 8464345256775460809),\n\tKQU(10191938789487159478), KQU( 6184348739615197613),\n\tKQU(11425436778806882100), KQU( 2739227089124319793),\n\tKQU(  461464518456000551), KQU( 4689850170029177442),\n\tKQU( 6120307814374078625), KQU(11153579230681708671),\n\tKQU( 7891721473905347926), KQU(10281646937824872400),\n\tKQU( 3026099648191332248), KQU( 8666750296953273818),\n\tKQU(14978499698844363232), KQU(13303395102890132065),\n\tKQU( 8182358205292864080), KQU(10560547713972971291),\n\tKQU(11981635489418959093), KQU( 3134621354935288409),\n\tKQU(11580681977404383968), KQU(14205530317404088650),\n\tKQU( 5997789011854923157), KQU(13659151593432238041),\n\tKQU(11664332114338865086), KQU( 7490351383220929386),\n\tKQU( 7189290499881530378), KQU(15039262734271020220),\n\tKQU( 2057217285976980055), KQU(  555570804905355739),\n\tKQU(11235311968348555110), KQU(13824557146269603217),\n\tKQU(16906788840653099693), KQU( 7222878245455661677),\n\tKQU( 5245139444332423756), KQU( 4723748462805674292),\n\tKQU(12216509815698568612), KQU(17402362976648951187),\n\tKQU(17389614836810366768), KQU( 4880936484146667711),\n\tKQU( 9085007839292639880), KQU(13837353458498535449),\n\tKQU(11914419854360366677), KQU(16595890135313864103),\n\tKQU( 6313969847197627222), KQU(18296909792163910431),\n\tKQU(10041780113382084042), KQU( 2499478551172884794),\n\tKQU(11057894246241189489), KQU( 9742243032389068555),\n\tKQU(12838934582673196228), KQU(13437023235248490367),\n\tKQU(13372420669446163240), KQU( 6752564244716909224),\n\tKQU( 7157333073400313737), KQU(12230281516370654308),\n\tKQU( 1182884552219419117), KQU( 2955125381312499218),\n\tKQU(10308827097079443249), KQU( 1337648572986534958),\n\tKQU(16378788590020343939), KQU(  108619126514420935),\n\tKQU( 3990981009621629188), KQU( 5460953070230946410),\n\tKQU( 9703328329366531883), KQU(13166631489188077236),\n\tKQU( 1104768831213675170), KQU( 3447930458553877908),\n\tKQU( 8067172487769945676), KQU( 5445802098190775347),\n\tKQU( 3244840981648973873), KQU(17314668322981950060),\n\tKQU( 5006812527827763807), KQU(18158695070225526260),\n\tKQU( 2824536478852417853), KQU(13974775809127519886),\n\tKQU( 9814362769074067392), KQU(17276205156374862128),\n\tKQU(11361680725379306967), KQU( 3422581970382012542),\n\tKQU(11003189603753241266), KQU(11194292945277862261),\n\tKQU( 6839623313908521348), KQU(11935326462707324634),\n\tKQU( 1611456788685878444), KQU(13112620989475558907),\n\tKQU(  517659108904450427), KQU(13558114318574407624),\n\tKQU(15699089742731633077), KQU( 4988979278862685458),\n\tKQU( 8111373583056521297), KQU( 3891258746615399627),\n\tKQU( 8137298251469718086), KQU(12748663295624701649),\n\tKQU( 4389835683495292062), KQU( 5775217872128831729),\n\tKQU( 9462091896405534927), KQU( 8498124108820263989),\n\tKQU( 8059131278842839525), KQU(10503167994254090892),\n\tKQU(11613153541070396656), KQU(18069248738504647790),\n\tKQU(  570657419109768508), KQU( 3950574167771159665),\n\tKQU( 5514655599604313077), KQU( 2908460854428484165),\n\tKQU(10777722615935663114), KQU(12007363304839279486),\n\tKQU( 9800646187569484767), KQU( 8795423564889864287),\n\tKQU(14257396680131028419), KQU( 6405465117315096498),\n\tKQU( 7939411072208774878), KQU(17577572378528990006),\n\tKQU(14785873806715994850), KQU(16770572680854747390),\n\tKQU(18127549474419396481), KQU(11637013449455757750),\n\tKQU(14371851933996761086), KQU( 3601181063650110280),\n\tKQU( 4126442845019316144), KQU(10198287239244320669),\n\tKQU(18000169628555379659), KQU(18392482400739978269),\n\tKQU( 6219919037686919957), KQU( 3610085377719446052),\n\tKQU( 2513925039981776336), KQU(16679413537926716955),\n\tKQU(12903302131714909434), KQU( 5581145789762985009),\n\tKQU(12325955044293303233), KQU(17216111180742141204),\n\tKQU( 6321919595276545740), KQU( 3507521147216174501),\n\tKQU( 9659194593319481840), KQU(11473976005975358326),\n\tKQU(14742730101435987026), KQU(  492845897709954780),\n\tKQU(16976371186162599676), KQU(17712703422837648655),\n\tKQU( 9881254778587061697), KQU( 8413223156302299551),\n\tKQU( 1563841828254089168), KQU( 9996032758786671975),\n\tKQU(  138877700583772667), KQU(13003043368574995989),\n\tKQU( 4390573668650456587), KQU( 8610287390568126755),\n\tKQU(15126904974266642199), KQU( 6703637238986057662),\n\tKQU( 2873075592956810157), KQU( 6035080933946049418),\n\tKQU(13382846581202353014), KQU( 7303971031814642463),\n\tKQU(18418024405307444267), KQU( 5847096731675404647),\n\tKQU( 4035880699639842500), KQU(11525348625112218478),\n\tKQU( 3041162365459574102), KQU( 2604734487727986558),\n\tKQU(15526341771636983145), KQU(14556052310697370254),\n\tKQU(12997787077930808155), KQU( 9601806501755554499),\n\tKQU(11349677952521423389), KQU(14956777807644899350),\n\tKQU(16559736957742852721), KQU(12360828274778140726),\n\tKQU( 6685373272009662513), KQU(16932258748055324130),\n\tKQU(15918051131954158508), KQU( 1692312913140790144),\n\tKQU(  546653826801637367), KQU( 5341587076045986652),\n\tKQU(14975057236342585662), KQU(12374976357340622412),\n\tKQU(10328833995181940552), KQU(12831807101710443149),\n\tKQU(10548514914382545716), KQU( 2217806727199715993),\n\tKQU(12627067369242845138), KQU( 4598965364035438158),\n\tKQU(  150923352751318171), KQU(14274109544442257283),\n\tKQU( 4696661475093863031), KQU( 1505764114384654516),\n\tKQU(10699185831891495147), KQU( 2392353847713620519),\n\tKQU( 3652870166711788383), KQU( 8640653276221911108),\n\tKQU( 3894077592275889704), KQU( 4918592872135964845),\n\tKQU(16379121273281400789), KQU(12058465483591683656),\n\tKQU(11250106829302924945), KQU( 1147537556296983005),\n\tKQU( 6376342756004613268), KQU(14967128191709280506),\n\tKQU(18007449949790627628), KQU( 9497178279316537841),\n\tKQU( 7920174844809394893), KQU(10037752595255719907),\n\tKQU(15875342784985217697), KQU(15311615921712850696),\n\tKQU( 9552902652110992950), KQU(14054979450099721140),\n\tKQU( 5998709773566417349), KQU(18027910339276320187),\n\tKQU( 8223099053868585554), KQU( 7842270354824999767),\n\tKQU( 4896315688770080292), KQU(12969320296569787895),\n\tKQU( 2674321489185759961), KQU( 4053615936864718439),\n\tKQU(11349775270588617578), KQU( 4743019256284553975),\n\tKQU( 5602100217469723769), KQU(14398995691411527813),\n\tKQU( 7412170493796825470), KQU(  836262406131744846),\n\tKQU( 8231086633845153022), KQU( 5161377920438552287),\n\tKQU( 8828731196169924949), KQU(16211142246465502680),\n\tKQU( 3307990879253687818), KQU( 5193405406899782022),\n\tKQU( 8510842117467566693), KQU( 6070955181022405365),\n\tKQU(14482950231361409799), KQU(12585159371331138077),\n\tKQU( 3511537678933588148), KQU( 2041849474531116417),\n\tKQU(10944936685095345792), KQU(18303116923079107729),\n\tKQU( 2720566371239725320), KQU( 4958672473562397622),\n\tKQU( 3032326668253243412), KQU(13689418691726908338),\n\tKQU( 1895205511728843996), KQU( 8146303515271990527),\n\tKQU(16507343500056113480), KQU(  473996939105902919),\n\tKQU( 9897686885246881481), KQU(14606433762712790575),\n\tKQU( 6732796251605566368), KQU( 1399778120855368916),\n\tKQU(  935023885182833777), KQU(16066282816186753477),\n\tKQU( 7291270991820612055), KQU(17530230393129853844),\n\tKQU(10223493623477451366), KQU(15841725630495676683),\n\tKQU(17379567246435515824), KQU( 8588251429375561971),\n\tKQU(18339511210887206423), KQU(17349587430725976100),\n\tKQU(12244876521394838088), KQU( 6382187714147161259),\n\tKQU(12335807181848950831), KQU(16948885622305460665),\n\tKQU(13755097796371520506), KQU(14806740373324947801),\n\tKQU( 4828699633859287703), KQU( 8209879281452301604),\n\tKQU(12435716669553736437), KQU(13970976859588452131),\n\tKQU( 6233960842566773148), KQU(12507096267900505759),\n\tKQU( 1198713114381279421), KQU(14989862731124149015),\n\tKQU(15932189508707978949), KQU( 2526406641432708722),\n\tKQU(   29187427817271982), KQU( 1499802773054556353),\n\tKQU(10816638187021897173), KQU( 5436139270839738132),\n\tKQU( 6659882287036010082), KQU( 2154048955317173697),\n\tKQU(10887317019333757642), KQU(16281091802634424955),\n\tKQU(10754549879915384901), KQU(10760611745769249815),\n\tKQU( 2161505946972504002), KQU( 5243132808986265107),\n\tKQU(10129852179873415416), KQU(  710339480008649081),\n\tKQU( 7802129453068808528), KQU(17967213567178907213),\n\tKQU(15730859124668605599), KQU(13058356168962376502),\n\tKQU( 3701224985413645909), KQU(14464065869149109264),\n\tKQU( 9959272418844311646), KQU(10157426099515958752),\n\tKQU(14013736814538268528), KQU(17797456992065653951),\n\tKQU(17418878140257344806), KQU(15457429073540561521),\n\tKQU( 2184426881360949378), KQU( 2062193041154712416),\n\tKQU( 8553463347406931661), KQU( 4913057625202871854),\n\tKQU( 2668943682126618425), KQU(17064444737891172288),\n\tKQU( 4997115903913298637), KQU(12019402608892327416),\n\tKQU(17603584559765897352), KQU(11367529582073647975),\n\tKQU( 8211476043518436050), KQU( 8676849804070323674),\n\tKQU(18431829230394475730), KQU(10490177861361247904),\n\tKQU( 9508720602025651349), KQU( 7409627448555722700),\n\tKQU( 5804047018862729008), KQU(11943858176893142594),\n\tKQU(11908095418933847092), KQU( 5415449345715887652),\n\tKQU( 1554022699166156407), KQU( 9073322106406017161),\n\tKQU( 7080630967969047082), KQU(18049736940860732943),\n\tKQU(12748714242594196794), KQU( 1226992415735156741),\n\tKQU(17900981019609531193), KQU(11720739744008710999),\n\tKQU( 3006400683394775434), KQU(11347974011751996028),\n\tKQU( 3316999628257954608), KQU( 8384484563557639101),\n\tKQU(18117794685961729767), KQU( 1900145025596618194),\n\tKQU(17459527840632892676), KQU( 5634784101865710994),\n\tKQU( 7918619300292897158), KQU( 3146577625026301350),\n\tKQU( 9955212856499068767), KQU( 1873995843681746975),\n\tKQU( 1561487759967972194), KQU( 8322718804375878474),\n\tKQU(11300284215327028366), KQU( 4667391032508998982),\n\tKQU( 9820104494306625580), KQU(17922397968599970610),\n\tKQU( 1784690461886786712), KQU(14940365084341346821),\n\tKQU( 5348719575594186181), KQU(10720419084507855261),\n\tKQU(14210394354145143274), KQU( 2426468692164000131),\n\tKQU(16271062114607059202), KQU(14851904092357070247),\n\tKQU( 6524493015693121897), KQU( 9825473835127138531),\n\tKQU(14222500616268569578), KQU(15521484052007487468),\n\tKQU(14462579404124614699), KQU(11012375590820665520),\n\tKQU(11625327350536084927), KQU(14452017765243785417),\n\tKQU( 9989342263518766305), KQU( 3640105471101803790),\n\tKQU( 4749866455897513242), KQU(13963064946736312044),\n\tKQU(10007416591973223791), KQU(18314132234717431115),\n\tKQU( 3286596588617483450), KQU( 7726163455370818765),\n\tKQU( 7575454721115379328), KQU( 5308331576437663422),\n\tKQU(18288821894903530934), KQU( 8028405805410554106),\n\tKQU(15744019832103296628), KQU(  149765559630932100),\n\tKQU( 6137705557200071977), KQU(14513416315434803615),\n\tKQU(11665702820128984473), KQU(  218926670505601386),\n\tKQU( 6868675028717769519), KQU(15282016569441512302),\n\tKQU( 5707000497782960236), KQU( 6671120586555079567),\n\tKQU( 2194098052618985448), KQU(16849577895477330978),\n\tKQU(12957148471017466283), KQU( 1997805535404859393),\n\tKQU( 1180721060263860490), KQU(13206391310193756958),\n\tKQU(12980208674461861797), KQU( 3825967775058875366),\n\tKQU(17543433670782042631), KQU( 1518339070120322730),\n\tKQU(16344584340890991669), KQU( 2611327165318529819),\n\tKQU(11265022723283422529), KQU( 4001552800373196817),\n\tKQU(14509595890079346161), KQU( 3528717165416234562),\n\tKQU(18153222571501914072), KQU( 9387182977209744425),\n\tKQU(10064342315985580021), KQU(11373678413215253977),\n\tKQU( 2308457853228798099), KQU( 9729042942839545302),\n\tKQU( 7833785471140127746), KQU( 6351049900319844436),\n\tKQU(14454610627133496067), KQU(12533175683634819111),\n\tKQU(15570163926716513029), KQU(13356980519185762498)\n};\n\nTEST_BEGIN(test_gen_rand_32) {\n\tuint32_t array32[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tuint32_t array32_2[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint32_t r32;\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size32(), BLOCK_SIZE,\n\t    \"Array size too small\");\n\tctx = init_gen_rand(1234);\n\tfill_array32(ctx, array32, BLOCK_SIZE);\n\tfill_array32(ctx, array32_2, BLOCK_SIZE);\n\tfini_gen_rand(ctx);\n\n\tctx = init_gen_rand(1234);\n\tfor (i = 0; i < BLOCK_SIZE; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u32_eq(array32[i], init_gen_rand_32_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32[i],\n\t\t    \"Mismatch at array32[%d]=%x, gen=%x\", i, array32[i], r32);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32_2[i],\n\t\t    \"Mismatch at array32_2[%d]=%x, gen=%x\", i, array32_2[i],\n\t\t    r32);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nTEST_BEGIN(test_by_array_32) {\n\tuint32_t array32[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tuint32_t array32_2[BLOCK_SIZE] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint32_t ini[4] = {0x1234, 0x5678, 0x9abc, 0xdef0};\n\tuint32_t r32;\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size32(), BLOCK_SIZE,\n\t    \"Array size too small\");\n\tctx = init_by_array(ini, 4);\n\tfill_array32(ctx, array32, BLOCK_SIZE);\n\tfill_array32(ctx, array32_2, BLOCK_SIZE);\n\tfini_gen_rand(ctx);\n\n\tctx = init_by_array(ini, 4);\n\tfor (i = 0; i < BLOCK_SIZE; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u32_eq(array32[i], init_by_array_32_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32[i],\n\t\t    \"Mismatch at array32[%d]=%x, gen=%x\", i, array32[i], r32);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr32 = gen_rand32(ctx);\n\t\tassert_u32_eq(r32, array32_2[i],\n\t\t    \"Mismatch at array32_2[%d]=%x, gen=%x\", i, array32_2[i],\n\t\t    r32);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nTEST_BEGIN(test_gen_rand_64) {\n\tuint64_t array64[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tuint64_t array64_2[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint64_t r;\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size64(), BLOCK_SIZE64,\n\t    \"Array size too small\");\n\tctx = init_gen_rand(4321);\n\tfill_array64(ctx, array64, BLOCK_SIZE64);\n\tfill_array64(ctx, array64_2, BLOCK_SIZE64);\n\tfini_gen_rand(ctx);\n\n\tctx = init_gen_rand(4321);\n\tfor (i = 0; i < BLOCK_SIZE64; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u64_eq(array64[i], init_gen_rand_64_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64[i],\n\t\t    \"Mismatch at array64[%d]=%\"FMTx64\", gen=%\"FMTx64, i,\n\t\t    array64[i], r);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64_2[i],\n\t\t    \"Mismatch at array64_2[%d]=%\"FMTx64\" gen=%\"FMTx64\"\", i,\n\t\t    array64_2[i], r);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nTEST_BEGIN(test_by_array_64) {\n\tuint64_t array64[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tuint64_t array64_2[BLOCK_SIZE64] JEMALLOC_ATTR(aligned(16));\n\tint i;\n\tuint64_t r;\n\tuint32_t ini[] = {5, 4, 3, 2, 1};\n\tsfmt_t *ctx;\n\n\tassert_d_le(get_min_array_size64(), BLOCK_SIZE64,\n\t    \"Array size too small\");\n\tctx = init_by_array(ini, 5);\n\tfill_array64(ctx, array64, BLOCK_SIZE64);\n\tfill_array64(ctx, array64_2, BLOCK_SIZE64);\n\tfini_gen_rand(ctx);\n\n\tctx = init_by_array(ini, 5);\n\tfor (i = 0; i < BLOCK_SIZE64; i++) {\n\t\tif (i < COUNT_1) {\n\t\t\tassert_u64_eq(array64[i], init_by_array_64_expected[i],\n\t\t\t    \"Output mismatch for i=%d\", i);\n\t\t}\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64[i],\n\t\t    \"Mismatch at array64[%d]=%\"FMTx64\" gen=%\"FMTx64, i,\n\t\t    array64[i], r);\n\t}\n\tfor (i = 0; i < COUNT_2; i++) {\n\t\tr = gen_rand64(ctx);\n\t\tassert_u64_eq(r, array64_2[i],\n\t\t    \"Mismatch at array64_2[%d]=%\"FMTx64\" gen=%\"FMTx64, i,\n\t\t    array64_2[i], r);\n\t}\n\tfini_gen_rand(ctx);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_gen_rand_32,\n\t    test_by_array_32,\n\t    test_gen_rand_64,\n\t    test_by_array_64);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/a0.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_a0) {\n\tvoid *p;\n\n\tp = a0malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected a0malloc() error\");\n\ta0dalloc(p);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_malloc_init(\n\t    test_a0);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/arena_reset.c",
    "content": "#ifndef ARENA_RESET_PROF_C_\n#include \"test/jemalloc_test.h\"\n#endif\n\n#include \"jemalloc/internal/extent_mmap.h\"\n#include \"jemalloc/internal/rtree.h\"\n\n#include \"test/extent_hooks.h\"\n\nstatic unsigned\nget_nsizes_impl(const char *cmd) {\n\tunsigned ret;\n\tsize_t z;\n\n\tz = sizeof(unsigned);\n\tassert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,\n\t    \"Unexpected mallctl(\\\"%s\\\", ...) failure\", cmd);\n\n\treturn ret;\n}\n\nstatic unsigned\nget_nsmall(void) {\n\treturn get_nsizes_impl(\"arenas.nbins\");\n}\n\nstatic unsigned\nget_nlarge(void) {\n\treturn get_nsizes_impl(\"arenas.nlextents\");\n}\n\nstatic size_t\nget_size_impl(const char *cmd, size_t ind) {\n\tsize_t ret;\n\tsize_t z;\n\tsize_t mib[4];\n\tsize_t miblen = 4;\n\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = ind;\n\tz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\", %zu], ...) failure\", cmd, ind);\n\n\treturn ret;\n}\n\nstatic size_t\nget_small_size(size_t ind) {\n\treturn get_size_impl(\"arenas.bin.0.size\", ind);\n}\n\nstatic size_t\nget_large_size(size_t ind) {\n\treturn get_size_impl(\"arenas.lextent.0.size\", ind);\n}\n\n/* Like ivsalloc(), but safe to call on discarded allocations. */\nstatic size_t\nvsalloc(tsdn_t *tsdn, const void *ptr) {\n\trtree_ctx_t rtree_ctx_fallback;\n\trtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);\n\n\textent_t *extent;\n\tszind_t szind;\n\tif (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,\n\t    (uintptr_t)ptr, false, &extent, &szind)) {\n\t\treturn 0;\n\t}\n\n\tif (extent == NULL) {\n\t\treturn 0;\n\t}\n\tif (extent_state_get(extent) != extent_state_active) {\n\t\treturn 0;\n\t}\n\n\tif (szind == NSIZES) {\n\t\treturn 0;\n\t}\n\n\treturn sz_index2size(szind);\n}\n\nstatic unsigned\ndo_arena_create(extent_hooks_t *h) {\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz,\n\t    (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,\n\t    \"Unexpected mallctl() failure\");\n\treturn arena_ind;\n}\n\nstatic void\ndo_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs) {\n#define NLARGE\t32\n\tunsigned nsmall, nlarge, i;\n\tsize_t sz;\n\tint flags;\n\ttsdn_t *tsdn;\n\n\tflags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\n\tnsmall = get_nsmall();\n\tnlarge = get_nlarge() > NLARGE ? NLARGE : get_nlarge();\n\t*nptrs = nsmall + nlarge;\n\t*ptrs = (void **)malloc(*nptrs * sizeof(void *));\n\tassert_ptr_not_null(*ptrs, \"Unexpected malloc() failure\");\n\n\t/* Allocate objects with a wide range of sizes. */\n\tfor (i = 0; i < nsmall; i++) {\n\t\tsz = get_small_size(i);\n\t\t(*ptrs)[i] = mallocx(sz, flags);\n\t\tassert_ptr_not_null((*ptrs)[i],\n\t\t    \"Unexpected mallocx(%zu, %#x) failure\", sz, flags);\n\t}\n\tfor (i = 0; i < nlarge; i++) {\n\t\tsz = get_large_size(i);\n\t\t(*ptrs)[nsmall + i] = mallocx(sz, flags);\n\t\tassert_ptr_not_null((*ptrs)[i],\n\t\t    \"Unexpected mallocx(%zu, %#x) failure\", sz, flags);\n\t}\n\n\ttsdn = tsdn_fetch();\n\n\t/* Verify allocations. */\n\tfor (i = 0; i < *nptrs; i++) {\n\t\tassert_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0,\n\t\t    \"Allocation should have queryable size\");\n\t}\n}\n\nstatic void\ndo_arena_reset_post(void **ptrs, unsigned nptrs, unsigned arena_ind) {\n\ttsdn_t *tsdn;\n\tunsigned i;\n\n\ttsdn = tsdn_fetch();\n\n\tif (have_background_thread) {\n\t\tmalloc_mutex_lock(tsdn,\n\t\t    &background_thread_info[arena_ind % ncpus].mtx);\n\t}\n\t/* Verify allocations no longer exist. */\n\tfor (i = 0; i < nptrs; i++) {\n\t\tassert_zu_eq(vsalloc(tsdn, ptrs[i]), 0,\n\t\t    \"Allocation should no longer exist\");\n\t}\n\tif (have_background_thread) {\n\t\tmalloc_mutex_unlock(tsdn,\n\t\t    &background_thread_info[arena_ind % ncpus].mtx);\n\t}\n\n\tfree(ptrs);\n}\n\nstatic void\ndo_arena_reset_destroy(const char *name, unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(name, mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nstatic void\ndo_arena_reset(unsigned arena_ind) {\n\tdo_arena_reset_destroy(\"arena.0.reset\", arena_ind);\n}\n\nstatic void\ndo_arena_destroy(unsigned arena_ind) {\n\tdo_arena_reset_destroy(\"arena.0.destroy\", arena_ind);\n}\n\nTEST_BEGIN(test_arena_reset) {\n\tunsigned arena_ind;\n\tvoid **ptrs;\n\tunsigned nptrs;\n\n\tarena_ind = do_arena_create(NULL);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\tdo_arena_reset(arena_ind);\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n}\nTEST_END\n\nstatic bool\narena_i_initialized(unsigned arena_ind, bool refresh) {\n\tbool initialized;\n\tsize_t mib[3];\n\tsize_t miblen, sz;\n\n\tif (refresh) {\n\t\tuint64_t epoch = 1;\n\t\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t\t    sizeof(epoch)), 0, \"Unexpected mallctl() failure\");\n\t}\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.initialized\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tsz = sizeof(initialized);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL,\n\t    0), 0, \"Unexpected mallctlbymib() failure\");\n\n\treturn initialized;\n}\n\nTEST_BEGIN(test_arena_destroy_initial) {\n\tassert_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),\n\t    \"Destroyed arena stats should not be initialized\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_destroy_hooks_default) {\n\tunsigned arena_ind, arena_ind_another, arena_ind_prev;\n\tvoid **ptrs;\n\tunsigned nptrs;\n\n\tarena_ind = do_arena_create(NULL);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\n\tassert_false(arena_i_initialized(arena_ind, false),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should be initialized\");\n\n\t/*\n\t * Create another arena before destroying one, to better verify arena\n\t * index reuse.\n\t */\n\tarena_ind_another = do_arena_create(NULL);\n\n\tdo_arena_destroy(arena_ind);\n\n\tassert_false(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),\n\t    \"Destroyed arena stats should be initialized\");\n\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n\n\tarena_ind_prev = arena_ind;\n\tarena_ind = do_arena_create(NULL);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\tassert_u_eq(arena_ind, arena_ind_prev,\n\t    \"Arena index should have been recycled\");\n\tdo_arena_destroy(arena_ind);\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n\n\tdo_arena_destroy(arena_ind_another);\n}\nTEST_END\n\n/*\n * Actually unmap extents, regardless of opt_retain, so that attempts to access\n * a destroyed arena's memory will segfault.\n */\nstatic bool\nextent_dalloc_unmap(extent_hooks_t *extent_hooks, void *addr, size_t size,\n    bool committed, unsigned arena_ind) {\n\tTRACE_HOOK(\"%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, \"\n\t    \"arena_ind=%u)\\n\", __func__, extent_hooks, addr, size, committed ?\n\t    \"true\" : \"false\", arena_ind);\n\tassert_ptr_eq(extent_hooks, &hooks,\n\t    \"extent_hooks should be same as pointer used to set hooks\");\n\tassert_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap,\n\t    \"Wrong hook function\");\n\tcalled_dalloc = true;\n\tif (!try_dalloc) {\n\t\treturn true;\n\t}\n\tpages_unmap(addr, size);\n\tdid_dalloc = true;\n\treturn false;\n}\n\nstatic extent_hooks_t hooks_orig;\n\nstatic extent_hooks_t hooks_unmap = {\n\textent_alloc_hook,\n\textent_dalloc_unmap, /* dalloc */\n\textent_destroy_hook,\n\textent_commit_hook,\n\textent_decommit_hook,\n\textent_purge_lazy_hook,\n\textent_purge_forced_hook,\n\textent_split_hook,\n\textent_merge_hook\n};\n\nTEST_BEGIN(test_arena_destroy_hooks_unmap) {\n\tunsigned arena_ind;\n\tvoid **ptrs;\n\tunsigned nptrs;\n\n\textent_hooks_prep();\n\ttry_decommit = false;\n\tmemcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));\n\tmemcpy(&hooks, &hooks_unmap, sizeof(extent_hooks_t));\n\n\tdid_alloc = false;\n\tarena_ind = do_arena_create(&hooks);\n\tdo_arena_reset_pre(arena_ind, &ptrs, &nptrs);\n\n\tassert_true(did_alloc, \"Expected alloc\");\n\n\tassert_false(arena_i_initialized(arena_ind, false),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should be initialized\");\n\n\tdid_dalloc = false;\n\tdo_arena_destroy(arena_ind);\n\tassert_true(did_dalloc, \"Expected dalloc\");\n\n\tassert_false(arena_i_initialized(arena_ind, true),\n\t    \"Arena stats should not be initialized\");\n\tassert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),\n\t    \"Destroyed arena stats should be initialized\");\n\n\tdo_arena_reset_post(ptrs, nptrs, arena_ind);\n\n\tmemcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_arena_reset,\n\t    test_arena_destroy_initial,\n\t    test_arena_destroy_hooks_default,\n\t    test_arena_destroy_hooks_unmap);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/arena_reset_prof.c",
    "content": "#include \"test/jemalloc_test.h\"\n#define ARENA_RESET_PROF_C_\n\n#include \"arena_reset.c\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/arena_reset_prof.sh",
    "content": "#!/bin/sh\n\nexport MALLOC_CONF=\"prof:true,lg_prof_sample:0\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/atomic.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * We *almost* have consistent short names (e.g. \"u32\" for uint32_t, \"b\" for\n * bool, etc.  The one exception is that the short name for void * is \"p\" in\n * some places and \"ptr\" in others.  In the long run it would be nice to unify\n * these, but in the short run we'll use this shim.\n */\n#define assert_p_eq assert_ptr_eq\n\n/*\n * t: the non-atomic type, like \"uint32_t\".\n * ta: the short name for the type, like \"u32\".\n * val[1,2,3]: Values of the given type.  The CAS tests use val2 for expected,\n * and val3 for desired.\n */\n\n#define DO_TESTS(t, ta, val1, val2, val3) do {\t\t\t\t\\\n\tt val;\t\t\t\t\t\t\t\t\\\n\tt expected;\t\t\t\t\t\t\t\\\n\tbool success;\t\t\t\t\t\t\t\\\n\t/* This (along with the load below) also tests ATOMIC_LOAD. */\t\\\n\tatomic_##ta##_t atom = ATOMIC_INIT(val1);\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* ATOMIC_INIT and load. */\t\t\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1, val, \"Load or init failed\");\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Store. */\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tatomic_store_##ta(&atom, val2, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val2, val, \"Store failed\");\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Exchange. */\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_exchange_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val, \"Exchange returned invalid value\");\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val2, val, \"Exchange store invalid value\");\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* \t\t\t\t\t\t\t\t\\\n\t * Weak CAS.  Spurious failures are allowed, so we loop a few\t\\\n\t * times.\t\t\t\t\t\t\t\\\n\t */\t\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tsuccess = false;\t\t\t\t\t\t\\\n\tfor (int i = 0; i < 10 && !success; i++) {\t\t\t\\\n\t\texpected = val2;\t\t\t\t\t\\\n\t\tsuccess = atomic_compare_exchange_weak_##ta(&atom,\t\\\n\t\t    &expected, val3, ATOMIC_RELAXED, ATOMIC_RELAXED);\t\\\n\t\tassert_##ta##_eq(val1, expected, \t\t\t\\\n\t\t    \"CAS should update expected\");\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tassert_b_eq(val1 == val2, success,\t\t\t\t\\\n\t    \"Weak CAS did the wrong state update\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tif (success) {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val3, val,\t\t\t\t\\\n\t\t    \"Successful CAS should update atomic\");\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val1, val,\t\t\t\t\\\n\t\t    \"Unsuccessful CAS should not update atomic\");\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Strong CAS. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\texpected = val2;\t\t\t\t\t\t\\\n\tsuccess = atomic_compare_exchange_strong_##ta(&atom, &expected,\t\\\n\t    val3, ATOMIC_RELAXED, ATOMIC_RELAXED);\t\t\t\\\n\tassert_b_eq(val1 == val2, success,\t\t\t\t\\\n\t    \"Strong CAS did the wrong state update\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tif (success) {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val3, val,\t\t\t\t\\\n\t\t    \"Successful CAS should update atomic\");\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tassert_##ta##_eq(val1, val,\t\t\t\t\\\n\t\t    \"Unsuccessful CAS should not update atomic\");\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\n#define DO_INTEGER_TESTS(t, ta, val1, val2) do {\t\t\t\\\n\tatomic_##ta##_t atom;\t\t\t\t\t\t\\\n\tt val;\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-add. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_add_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-add should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 + val2, val,\t\t\t\t\\\n\t    \"Fetch-add should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-sub. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_sub_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-sub should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 - val2, val,\t\t\t\t\\\n\t    \"Fetch-sub should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-and. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_and_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-and should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 & val2, val,\t\t\t\t\\\n\t    \"Fetch-and should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-or. */\t\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_or_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-or should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 | val2, val,\t\t\t\t\\\n\t    \"Fetch-or should update atomic\");\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t/* Fetch-xor. */\t\t\t\t\t\t\\\n\tatomic_store_##ta(&atom, val1, ATOMIC_RELAXED);\t\t\t\\\n\tval = atomic_fetch_xor_##ta(&atom, val2, ATOMIC_RELAXED);\t\\\n\tassert_##ta##_eq(val1, val,\t\t\t\t\t\\\n\t    \"Fetch-xor should return previous value\");\t\t\t\\\n\tval = atomic_load_##ta(&atom, ATOMIC_RELAXED);\t\t\t\\\n\tassert_##ta##_eq(val1 ^ val2, val,\t\t\t\t\\\n\t    \"Fetch-xor should update atomic\");\t\t\t\t\\\n} while (0)\n\n#define TEST_STRUCT(t, ta)\t\t\t\t\t\t\\\ntypedef struct {\t\t\t\t\t\t\t\\\n\tt val1;\t\t\t\t\t\t\t\t\\\n\tt val2;\t\t\t\t\t\t\t\t\\\n\tt val3;\t\t\t\t\t\t\t\t\\\n} ta##_test_t;\n\n#define TEST_CASES(t) {\t\t\t\t\t\t\t\\\n\t{(t)-1, (t)-1, (t)-2},\t\t\t\t\t\t\\\n\t{(t)-1, (t) 0, (t)-2},\t\t\t\t\t\t\\\n\t{(t)-1, (t) 1, (t)-2},\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t{(t) 0, (t)-1, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 0, (t) 0, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 0, (t) 1, (t)-2},\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t{(t) 1, (t)-1, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 1, (t) 0, (t)-2},\t\t\t\t\t\t\\\n\t{(t) 1, (t) 1, (t)-2},\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t{(t)0, (t)-(1 << 22), (t)-2},\t\t\t\t\t\\\n\t{(t)0, (t)(1 << 22), (t)-2},\t\t\t\t\t\\\n\t{(t)(1 << 22), (t)-(1 << 22), (t)-2},\t\t\t\t\\\n\t{(t)(1 << 22), (t)(1 << 22), (t)-2}\t\t\t\t\\\n}\n\n#define TEST_BODY(t, ta) do {\t\t\t\t\t\t\\\n\tconst ta##_test_t tests[] = TEST_CASES(t);\t\t\t\\\n\tfor (unsigned i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {\t\\\n\t\tta##_test_t test = tests[i];\t\t\t\t\\\n\t\tDO_TESTS(t, ta, test.val1, test.val2, test.val3);\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define INTEGER_TEST_BODY(t, ta) do {\t\t\t\t\t\\\n\tconst ta##_test_t tests[] = TEST_CASES(t);\t\t\t\\\n\tfor (unsigned i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {\t\\\n\t\tta##_test_t test = tests[i];\t\t\t\t\\\n\t\tDO_TESTS(t, ta, test.val1, test.val2, test.val3);\t\\\n\t\tDO_INTEGER_TESTS(t, ta, test.val1, test.val2);\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\nTEST_STRUCT(uint64_t, u64);\nTEST_BEGIN(test_atomic_u64) {\n#if !(LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)\n\ttest_skip(\"64-bit atomic operations not supported\");\n#else\n\tINTEGER_TEST_BODY(uint64_t, u64);\n#endif\n}\nTEST_END\n\n\nTEST_STRUCT(uint32_t, u32);\nTEST_BEGIN(test_atomic_u32) {\n\tINTEGER_TEST_BODY(uint32_t, u32);\n}\nTEST_END\n\nTEST_STRUCT(void *, p);\nTEST_BEGIN(test_atomic_p) {\n\tTEST_BODY(void *, p);\n}\nTEST_END\n\nTEST_STRUCT(size_t, zu);\nTEST_BEGIN(test_atomic_zu) {\n\tINTEGER_TEST_BODY(size_t, zu);\n}\nTEST_END\n\nTEST_STRUCT(ssize_t, zd);\nTEST_BEGIN(test_atomic_zd) {\n\tINTEGER_TEST_BODY(ssize_t, zd);\n}\nTEST_END\n\n\nTEST_STRUCT(unsigned, u);\nTEST_BEGIN(test_atomic_u) {\n\tINTEGER_TEST_BODY(unsigned, u);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_atomic_u64,\n\t    test_atomic_u32,\n\t    test_atomic_p,\n\t    test_atomic_zu,\n\t    test_atomic_zd,\n\t    test_atomic_u);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/background_thread.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\nstatic void\ntest_switch_background_thread_ctl(bool new_val) {\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\n\te1 = new_val;\n\tassert_d_eq(mallctl(\"background_thread\", (void *)&e0, &sz,\n\t    &e1, sz), 0, \"Unexpected mallctl() failure\");\n\tassert_b_eq(e0, !e1,\n\t    \"background_thread should be %d before.\\n\", !e1);\n\tif (e1) {\n\t\tassert_zu_gt(n_background_threads, 0,\n\t\t    \"Number of background threads should be non zero.\\n\");\n\t} else {\n\t\tassert_zu_eq(n_background_threads, 0,\n\t\t    \"Number of background threads should be zero.\\n\");\n\t}\n}\n\nstatic void\ntest_repeat_background_thread_ctl(bool before) {\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\n\te1 = before;\n\tassert_d_eq(mallctl(\"background_thread\", (void *)&e0, &sz,\n\t    &e1, sz), 0, \"Unexpected mallctl() failure\");\n\tassert_b_eq(e0, before,\n\t    \"background_thread should be %d.\\n\", before);\n\tif (e1) {\n\t\tassert_zu_gt(n_background_threads, 0,\n\t\t    \"Number of background threads should be non zero.\\n\");\n\t} else {\n\t\tassert_zu_eq(n_background_threads, 0,\n\t\t    \"Number of background threads should be zero.\\n\");\n\t}\n}\n\nTEST_BEGIN(test_background_thread_ctl) {\n\ttest_skip_if(!have_background_thread);\n\n\tbool e0, e1;\n\tsize_t sz = sizeof(bool);\n\n\tassert_d_eq(mallctl(\"opt.background_thread\", (void *)&e0, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctl(\"background_thread\", (void *)&e1, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\tassert_b_eq(e0, e1,\n\t    \"Default and opt.background_thread does not match.\\n\");\n\tif (e0) {\n\t\ttest_switch_background_thread_ctl(false);\n\t}\n\tassert_zu_eq(n_background_threads, 0,\n\t    \"Number of background threads should be 0.\\n\");\n\n\tfor (unsigned i = 0; i < 4; i++) {\n\t\ttest_switch_background_thread_ctl(true);\n\t\ttest_repeat_background_thread_ctl(true);\n\t\ttest_repeat_background_thread_ctl(true);\n\n\t\ttest_switch_background_thread_ctl(false);\n\t\ttest_repeat_background_thread_ctl(false);\n\t\ttest_repeat_background_thread_ctl(false);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_background_thread_running) {\n\ttest_skip_if(!have_background_thread);\n\ttest_skip_if(!config_stats);\n\n#if defined(JEMALLOC_BACKGROUND_THREAD)\n\ttsd_t *tsd = tsd_fetch();\n\tbackground_thread_info_t *info = &background_thread_info[0];\n\n\ttest_repeat_background_thread_ctl(false);\n\ttest_switch_background_thread_ctl(true);\n\tassert_b_eq(info->state, background_thread_started,\n\t    \"Background_thread did not start.\\n\");\n\n\tnstime_t start, now;\n\tnstime_init(&start, 0);\n\tnstime_update(&start);\n\n\tbool ran = false;\n\twhile (true) {\n\t\tmalloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);\n\t\tif (info->tot_n_runs > 0) {\n\t\t\tran = true;\n\t\t}\n\t\tmalloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);\n\t\tif (ran) {\n\t\t\tbreak;\n\t\t}\n\n\t\tnstime_init(&now, 0);\n\t\tnstime_update(&now);\n\t\tnstime_subtract(&now, &start);\n\t\tassert_u64_lt(nstime_sec(&now), 1000,\n\t\t    \"Background threads did not run for 1000 seconds.\");\n\t\tsleep(1);\n\t}\n\ttest_switch_background_thread_ctl(false);\n#endif\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Background_thread creation tests reentrancy naturally. */\n\treturn test_no_reentrancy(\n\t    test_background_thread_ctl,\n\t    test_background_thread_running);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/background_thread_enable.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nconst char *malloc_conf = \"background_thread:false,narenas:1,max_background_threads:20\";\n\nTEST_BEGIN(test_deferred) {\n\ttest_skip_if(!have_background_thread);\n\n\tunsigned id;\n\tsize_t sz_u = sizeof(unsigned);\n\n\t/*\n\t * 10 here is somewhat arbitrary, except insofar as we want to ensure\n\t * that the number of background threads is smaller than the number of\n\t * arenas.  I'll ragequit long before we have to spin up 10 threads per\n\t * cpu to handle background purging, so this is a conservative\n\t * approximation.\n\t */\n\tfor (unsigned i = 0; i < 10 * ncpus; i++) {\n\t\tassert_d_eq(mallctl(\"arenas.create\", &id, &sz_u, NULL, 0), 0,\n\t\t    \"Failed to create arena\");\n\t}\n\n\tbool enable = true;\n\tsize_t sz_b = sizeof(bool);\n\tassert_d_eq(mallctl(\"background_thread\", NULL, NULL, &enable, sz_b), 0,\n\t    \"Failed to enable background threads\");\n\tenable = false;\n\tassert_d_eq(mallctl(\"background_thread\", NULL, NULL, &enable, sz_b), 0,\n\t    \"Failed to disable background threads\");\n}\nTEST_END\n\nTEST_BEGIN(test_max_background_threads) {\n\ttest_skip_if(!have_background_thread);\n\n\tsize_t maxt;\n\tsize_t opt_maxt;\n\tsize_t sz_m = sizeof(maxt);\n\tassert_d_eq(mallctl(\"opt.max_background_threads\",\n\t\t\t    &opt_maxt, &sz_m, NULL, 0), 0,\n\t\t\t    \"Failed to get opt.max_background_threads\");\n\tassert_d_eq(mallctl(\"max_background_threads\", &maxt, &sz_m, NULL, 0), 0,\n\t\t    \"Failed to get max background threads\");\n\tassert_zu_eq(20, maxt, \"should be ncpus\");\n\tassert_zu_eq(opt_maxt, maxt,\n\t\t     \"max_background_threads and \"\n\t\t     \"opt.max_background_threads should match\");\n\tassert_d_eq(mallctl(\"max_background_threads\", NULL, NULL, &maxt, sz_m),\n\t\t    0, \"Failed to set max background threads\");\n\n\tunsigned id;\n\tsize_t sz_u = sizeof(unsigned);\n\n\tfor (unsigned i = 0; i < 10 * ncpus; i++) {\n\t\tassert_d_eq(mallctl(\"arenas.create\", &id, &sz_u, NULL, 0), 0,\n\t\t    \"Failed to create arena\");\n\t}\n\n\tbool enable = true;\n\tsize_t sz_b = sizeof(bool);\n\tassert_d_eq(mallctl(\"background_thread\", NULL, NULL, &enable, sz_b), 0,\n\t    \"Failed to enable background threads\");\n\tassert_zu_eq(n_background_threads, maxt,\n\t\t     \"Number of background threads should be 3.\\n\");\n\tmaxt = 10;\n\tassert_d_eq(mallctl(\"max_background_threads\", NULL, NULL, &maxt, sz_m),\n\t\t    0, \"Failed to set max background threads\");\n\tassert_zu_eq(n_background_threads, maxt,\n\t\t     \"Number of background threads should be 10.\\n\");\n\tmaxt = 3;\n\tassert_d_eq(mallctl(\"max_background_threads\", NULL, NULL, &maxt, sz_m),\n\t\t    0, \"Failed to set max background threads\");\n\tassert_zu_eq(n_background_threads, maxt,\n\t\t     \"Number of background threads should be 3.\\n\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t\ttest_deferred,\n\t\ttest_max_background_threads);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/base.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"test/extent_hooks.h\"\n\nstatic extent_hooks_t hooks_null = {\n\textent_alloc_hook,\n\tNULL, /* dalloc */\n\tNULL, /* destroy */\n\tNULL, /* commit */\n\tNULL, /* decommit */\n\tNULL, /* purge_lazy */\n\tNULL, /* purge_forced */\n\tNULL, /* split */\n\tNULL /* merge */\n};\n\nstatic extent_hooks_t hooks_not_null = {\n\textent_alloc_hook,\n\textent_dalloc_hook,\n\textent_destroy_hook,\n\tNULL, /* commit */\n\textent_decommit_hook,\n\textent_purge_lazy_hook,\n\textent_purge_forced_hook,\n\tNULL, /* split */\n\tNULL /* merge */\n};\n\nTEST_BEGIN(test_base_hooks_default) {\n\tbase_t *base;\n\tsize_t allocated0, allocated1, resident, mapped, n_thp;\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd_fetch());\n\tbase = base_new(tsdn, 0, (extent_hooks_t *)&extent_hooks_default);\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated0, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated0, sizeof(base_t),\n\t\t    \"Base header should count as allocated\");\n\t\tif (opt_metadata_thp == metadata_thp_always) {\n\t\t\tassert_zu_gt(n_thp, 0,\n\t\t\t    \"Base should have 1 THP at least.\");\n\t\t}\n\t}\n\n\tassert_ptr_not_null(base_alloc(tsdn, base, 42, 1),\n\t    \"Unexpected base_alloc() failure\");\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated1, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated1 - allocated0, 42,\n\t\t    \"At least 42 bytes were allocated by base_alloc()\");\n\t}\n\n\tbase_delete(tsdn, base);\n}\nTEST_END\n\nTEST_BEGIN(test_base_hooks_null) {\n\textent_hooks_t hooks_orig;\n\tbase_t *base;\n\tsize_t allocated0, allocated1, resident, mapped, n_thp;\n\n\textent_hooks_prep();\n\ttry_dalloc = false;\n\ttry_destroy = true;\n\ttry_decommit = false;\n\ttry_purge_lazy = false;\n\ttry_purge_forced = false;\n\tmemcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));\n\tmemcpy(&hooks, &hooks_null, sizeof(extent_hooks_t));\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd_fetch());\n\tbase = base_new(tsdn, 0, &hooks);\n\tassert_ptr_not_null(base, \"Unexpected base_new() failure\");\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated0, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated0, sizeof(base_t),\n\t\t    \"Base header should count as allocated\");\n\t\tif (opt_metadata_thp == metadata_thp_always) {\n\t\t\tassert_zu_gt(n_thp, 0,\n\t\t\t    \"Base should have 1 THP at least.\");\n\t\t}\n\t}\n\n\tassert_ptr_not_null(base_alloc(tsdn, base, 42, 1),\n\t    \"Unexpected base_alloc() failure\");\n\n\tif (config_stats) {\n\t\tbase_stats_get(tsdn, base, &allocated1, &resident, &mapped,\n\t\t    &n_thp);\n\t\tassert_zu_ge(allocated1 - allocated0, 42,\n\t\t    \"At least 42 bytes were allocated by base_alloc()\");\n\t}\n\n\tbase_delete(tsdn, base);\n\n\tmemcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));\n}\nTEST_END\n\nTEST_BEGIN(test_base_hooks_not_null) {\n\textent_hooks_t hooks_orig;\n\tbase_t *base;\n\tvoid *p, *q, *r, *r_exp;\n\n\textent_hooks_prep();\n\ttry_dalloc = false;\n\ttry_destroy = true;\n\ttry_decommit = false;\n\ttry_purge_lazy = false;\n\ttry_purge_forced = false;\n\tmemcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));\n\tmemcpy(&hooks, &hooks_not_null, sizeof(extent_hooks_t));\n\n\ttsdn_t *tsdn = tsd_tsdn(tsd_fetch());\n\tdid_alloc = false;\n\tbase = base_new(tsdn, 0, &hooks);\n\tassert_ptr_not_null(base, \"Unexpected base_new() failure\");\n\tassert_true(did_alloc, \"Expected alloc\");\n\n\t/*\n\t * Check for tight packing at specified alignment under simple\n\t * conditions.\n\t */\n\t{\n\t\tconst size_t alignments[] = {\n\t\t\t1,\n\t\t\tQUANTUM,\n\t\t\tQUANTUM << 1,\n\t\t\tCACHELINE,\n\t\t\tCACHELINE << 1,\n\t\t};\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < sizeof(alignments) / sizeof(size_t); i++) {\n\t\t\tsize_t alignment = alignments[i];\n\t\t\tsize_t align_ceil = ALIGNMENT_CEILING(alignment,\n\t\t\t    QUANTUM);\n\t\t\tp = base_alloc(tsdn, base, 1, alignment);\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq(p,\n\t\t\t    (void *)(ALIGNMENT_CEILING((uintptr_t)p,\n\t\t\t    alignment)), \"Expected quantum alignment\");\n\t\t\tq = base_alloc(tsdn, base, alignment, alignment);\n\t\t\tassert_ptr_not_null(q,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq((void *)((uintptr_t)p + align_ceil), q,\n\t\t\t    \"Minimal allocation should take up %zu bytes\",\n\t\t\t    align_ceil);\n\t\t\tr = base_alloc(tsdn, base, 1, alignment);\n\t\t\tassert_ptr_not_null(r,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq((void *)((uintptr_t)q + align_ceil), r,\n\t\t\t    \"Minimal allocation should take up %zu bytes\",\n\t\t\t    align_ceil);\n\t\t}\n\t}\n\n\t/*\n\t * Allocate an object that cannot fit in the first block, then verify\n\t * that the first block's remaining space is considered for subsequent\n\t * allocation.\n\t */\n\tassert_zu_ge(extent_bsize_get(&base->blocks->extent), QUANTUM,\n\t    \"Remainder insufficient for test\");\n\t/* Use up all but one quantum of block. */\n\twhile (extent_bsize_get(&base->blocks->extent) > QUANTUM) {\n\t\tp = base_alloc(tsdn, base, QUANTUM, QUANTUM);\n\t\tassert_ptr_not_null(p, \"Unexpected base_alloc() failure\");\n\t}\n\tr_exp = extent_addr_get(&base->blocks->extent);\n\tassert_zu_eq(base->extent_sn_next, 1, \"One extant block expected\");\n\tq = base_alloc(tsdn, base, QUANTUM + 1, QUANTUM);\n\tassert_ptr_not_null(q, \"Unexpected base_alloc() failure\");\n\tassert_ptr_ne(q, r_exp, \"Expected allocation from new block\");\n\tassert_zu_eq(base->extent_sn_next, 2, \"Two extant blocks expected\");\n\tr = base_alloc(tsdn, base, QUANTUM, QUANTUM);\n\tassert_ptr_not_null(r, \"Unexpected base_alloc() failure\");\n\tassert_ptr_eq(r, r_exp, \"Expected allocation from first block\");\n\tassert_zu_eq(base->extent_sn_next, 2, \"Two extant blocks expected\");\n\n\t/*\n\t * Check for proper alignment support when normal blocks are too small.\n\t */\n\t{\n\t\tconst size_t alignments[] = {\n\t\t\tHUGEPAGE,\n\t\t\tHUGEPAGE << 1\n\t\t};\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < sizeof(alignments) / sizeof(size_t); i++) {\n\t\t\tsize_t alignment = alignments[i];\n\t\t\tp = base_alloc(tsdn, base, QUANTUM, alignment);\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected base_alloc() failure\");\n\t\t\tassert_ptr_eq(p,\n\t\t\t    (void *)(ALIGNMENT_CEILING((uintptr_t)p,\n\t\t\t    alignment)), \"Expected %zu-byte alignment\",\n\t\t\t    alignment);\n\t\t}\n\t}\n\n\tcalled_dalloc = called_destroy = called_decommit = called_purge_lazy =\n\t    called_purge_forced = false;\n\tbase_delete(tsdn, base);\n\tassert_true(called_dalloc, \"Expected dalloc call\");\n\tassert_true(!called_destroy, \"Unexpected destroy call\");\n\tassert_true(called_decommit, \"Expected decommit call\");\n\tassert_true(called_purge_lazy, \"Expected purge_lazy call\");\n\tassert_true(called_purge_forced, \"Expected purge_forced call\");\n\n\ttry_dalloc = true;\n\ttry_destroy = true;\n\ttry_decommit = true;\n\ttry_purge_lazy = true;\n\ttry_purge_forced = true;\n\tmemcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_base_hooks_default,\n\t    test_base_hooks_null,\n\t    test_base_hooks_not_null);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/bit_util.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/bit_util.h\"\n\n#define TEST_POW2_CEIL(t, suf, pri) do {\t\t\t\t\\\n\tunsigned i, pow2;\t\t\t\t\t\t\\\n\tt x;\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tassert_##suf##_eq(pow2_ceil_##suf(0), 0, \"Unexpected result\");\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tfor (i = 0; i < sizeof(t) * 8; i++) {\t\t\t\t\\\n\t\tassert_##suf##_eq(pow2_ceil_##suf(((t)1) << i), ((t)1)\t\\\n\t\t    << i, \"Unexpected result\");\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\tfor (i = 2; i < sizeof(t) * 8; i++) {\t\t\t\t\\\n\t\tassert_##suf##_eq(pow2_ceil_##suf((((t)1) << i) - 1),\t\\\n\t\t    ((t)1) << i, \"Unexpected result\");\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tfor (i = 0; i < sizeof(t) * 8 - 1; i++) {\t\t\t\\\n\t\tassert_##suf##_eq(pow2_ceil_##suf((((t)1) << i) + 1),\t\\\n\t\t    ((t)1) << (i+1), \"Unexpected result\");\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tfor (pow2 = 1; pow2 < 25; pow2++) {\t\t\t\t\\\n\t\tfor (x = (((t)1) << (pow2-1)) + 1; x <= ((t)1) << pow2;\t\\\n\t\t    x++) {\t\t\t\t\t\t\\\n\t\t\tassert_##suf##_eq(pow2_ceil_##suf(x),\t\t\\\n\t\t\t    ((t)1) << pow2,\t\t\t\t\\\n\t\t\t    \"Unexpected result, x=%\"pri, x);\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\nTEST_BEGIN(test_pow2_ceil_u64) {\n\tTEST_POW2_CEIL(uint64_t, u64, FMTu64);\n}\nTEST_END\n\nTEST_BEGIN(test_pow2_ceil_u32) {\n\tTEST_POW2_CEIL(uint32_t, u32, FMTu32);\n}\nTEST_END\n\nTEST_BEGIN(test_pow2_ceil_zu) {\n\tTEST_POW2_CEIL(size_t, zu, \"zu\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_pow2_ceil_u64,\n\t    test_pow2_ceil_u32,\n\t    test_pow2_ceil_zu);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/bitmap.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NBITS_TAB \\\n    NB( 1) \\\n    NB( 2) \\\n    NB( 3) \\\n    NB( 4) \\\n    NB( 5) \\\n    NB( 6) \\\n    NB( 7) \\\n    NB( 8) \\\n    NB( 9) \\\n    NB(10) \\\n    NB(11) \\\n    NB(12) \\\n    NB(13) \\\n    NB(14) \\\n    NB(15) \\\n    NB(16) \\\n    NB(17) \\\n    NB(18) \\\n    NB(19) \\\n    NB(20) \\\n    NB(21) \\\n    NB(22) \\\n    NB(23) \\\n    NB(24) \\\n    NB(25) \\\n    NB(26) \\\n    NB(27) \\\n    NB(28) \\\n    NB(29) \\\n    NB(30) \\\n    NB(31) \\\n    NB(32) \\\n    \\\n    NB(33) \\\n    NB(34) \\\n    NB(35) \\\n    NB(36) \\\n    NB(37) \\\n    NB(38) \\\n    NB(39) \\\n    NB(40) \\\n    NB(41) \\\n    NB(42) \\\n    NB(43) \\\n    NB(44) \\\n    NB(45) \\\n    NB(46) \\\n    NB(47) \\\n    NB(48) \\\n    NB(49) \\\n    NB(50) \\\n    NB(51) \\\n    NB(52) \\\n    NB(53) \\\n    NB(54) \\\n    NB(55) \\\n    NB(56) \\\n    NB(57) \\\n    NB(58) \\\n    NB(59) \\\n    NB(60) \\\n    NB(61) \\\n    NB(62) \\\n    NB(63) \\\n    NB(64) \\\n    NB(65) \\\n    \\\n    NB(126) \\\n    NB(127) \\\n    NB(128) \\\n    NB(129) \\\n    NB(130) \\\n    \\\n    NB(254) \\\n    NB(255) \\\n    NB(256) \\\n    NB(257) \\\n    NB(258) \\\n    \\\n    NB(510) \\\n    NB(511) \\\n    NB(512) \\\n    NB(513) \\\n    NB(514) \\\n    \\\n    NB(1024) \\\n    NB(2048) \\\n    NB(4096) \\\n    NB(8192) \\\n    NB(16384) \\\n\nstatic void\ntest_bitmap_initializer_body(const bitmap_info_t *binfo, size_t nbits) {\n\tbitmap_info_t binfo_dyn;\n\tbitmap_info_init(&binfo_dyn, nbits);\n\n\tassert_zu_eq(bitmap_size(binfo), bitmap_size(&binfo_dyn),\n\t    \"Unexpected difference between static and dynamic initialization, \"\n\t    \"nbits=%zu\", nbits);\n\tassert_zu_eq(binfo->nbits, binfo_dyn.nbits,\n\t    \"Unexpected difference between static and dynamic initialization, \"\n\t    \"nbits=%zu\", nbits);\n#ifdef BITMAP_USE_TREE\n\tassert_u_eq(binfo->nlevels, binfo_dyn.nlevels,\n\t    \"Unexpected difference between static and dynamic initialization, \"\n\t    \"nbits=%zu\", nbits);\n\t{\n\t\tunsigned i;\n\n\t\tfor (i = 0; i < binfo->nlevels; i++) {\n\t\t\tassert_zu_eq(binfo->levels[i].group_offset,\n\t\t\t    binfo_dyn.levels[i].group_offset,\n\t\t\t    \"Unexpected difference between static and dynamic \"\n\t\t\t    \"initialization, nbits=%zu, level=%u\", nbits, i);\n\t\t}\n\t}\n#else\n\tassert_zu_eq(binfo->ngroups, binfo_dyn.ngroups,\n\t    \"Unexpected difference between static and dynamic initialization\");\n#endif\n}\n\nTEST_BEGIN(test_bitmap_initializer) {\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tif (nbits <= BITMAP_MAXBITS) {\t\t\t\t\\\n\t\t\tbitmap_info_t binfo =\t\t\t\t\\\n\t\t\t    BITMAP_INFO_INITIALIZER(nbits);\t\t\\\n\t\t\ttest_bitmap_initializer_body(&binfo, nbits);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic size_t\ntest_bitmap_size_body(const bitmap_info_t *binfo, size_t nbits,\n    size_t prev_size) {\n\tsize_t size = bitmap_size(binfo);\n\tassert_zu_ge(size, (nbits >> 3),\n\t    \"Bitmap size is smaller than expected\");\n\tassert_zu_ge(size, prev_size, \"Bitmap size is smaller than expected\");\n\treturn size;\n}\n\nTEST_BEGIN(test_bitmap_size) {\n\tsize_t nbits, prev_size;\n\n\tprev_size = 0;\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\tprev_size = test_bitmap_size_body(&binfo, nbits, prev_size);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\tprev_size = test_bitmap_size_body(&binfo, nbits,\t\\\n\t\t    prev_size);\t\t\t\t\t\t\\\n\t}\n\tprev_size = 0;\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_init_body(const bitmap_info_t *binfo, size_t nbits) {\n\tsize_t i;\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\n\tbitmap_init(bitmap, binfo, false);\n\tfor (i = 0; i < nbits; i++) {\n\t\tassert_false(bitmap_get(bitmap, binfo, i),\n\t\t    \"Bit should be unset\");\n\t}\n\n\tbitmap_init(bitmap, binfo, true);\n\tfor (i = 0; i < nbits; i++) {\n\t\tassert_true(bitmap_get(bitmap, binfo, i), \"Bit should be set\");\n\t}\n\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_init) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_init_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_init_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_set_body(const bitmap_info_t *binfo, size_t nbits) {\n\tsize_t i;\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\tbitmap_init(bitmap, binfo, false);\n\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i);\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_set) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_set_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_set_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_unset_body(const bitmap_info_t *binfo, size_t nbits) {\n\tsize_t i;\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\tbitmap_init(bitmap, binfo, false);\n\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i);\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_unset(bitmap, binfo, i);\n\t}\n\tfor (i = 0; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i);\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_unset) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_unset_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_unset_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nstatic void\ntest_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {\n\tbitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));\n\tassert_ptr_not_null(bitmap, \"Unexpected malloc() failure\");\n\tbitmap_init(bitmap, binfo, false);\n\n\t/* Iteratively set bits starting at the beginning. */\n\tfor (size_t i = 0; i < nbits; i++) {\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t    \"First unset bit should be just after previous first unset \"\n\t\t    \"bit\");\n\t}\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\n\t/*\n\t * Iteratively unset bits starting at the end, and verify that\n\t * bitmap_sfu() reaches the unset bits.\n\t */\n\tfor (size_t i = nbits - 1; i < nbits; i--) { /* (nbits..0] */\n\t\tbitmap_unset(bitmap, binfo, i);\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t    \"First unset bit should the bit previously unset\");\n\t\tbitmap_unset(bitmap, binfo, i);\n\t}\n\tassert_false(bitmap_get(bitmap, binfo, 0), \"Bit should be unset\");\n\n\t/*\n\t * Iteratively set bits starting at the beginning, and verify that\n\t * bitmap_sfu() looks past them.\n\t */\n\tfor (size_t i = 1; i < nbits; i++) {\n\t\tbitmap_set(bitmap, binfo, i - 1);\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t    \"First unset bit should be just after the bit previously \"\n\t\t    \"set\");\n\t\tbitmap_unset(bitmap, binfo, i);\n\t}\n\tassert_zu_eq(bitmap_ffu(bitmap, binfo, 0), nbits - 1,\n\t    \"First unset bit should be the last bit\");\n\tassert_zu_eq(bitmap_ffu(bitmap, binfo, (nbits > 1) ? nbits-2 : nbits-1),\n\t    nbits - 1, \"First unset bit should be the last bit\");\n\tassert_zu_eq(bitmap_ffu(bitmap, binfo, nbits - 1), nbits - 1,\n\t    \"First unset bit should be the last bit\");\n\tassert_zu_eq(bitmap_sfu(bitmap, binfo), nbits - 1,\n\t    \"First unset bit should be the last bit\");\n\tassert_true(bitmap_full(bitmap, binfo), \"All bits should be set\");\n\n\t/*\n\t * Bubble a \"usu\" pattern through the bitmap and verify that\n\t * bitmap_ffu() finds the correct bit for all five min_bit cases.\n\t */\n\tif (nbits >= 3) {\n\t\tfor (size_t i = 0; i < nbits-2; i++) {\n\t\t\tbitmap_unset(bitmap, binfo, i);\n\t\t\tbitmap_unset(bitmap, binfo, i+2);\n\t\t\tif (i > 0) {\n\t\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,\n\t\t\t\t    \"Unexpected first unset bit\");\n\t\t\t}\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+1), i+2,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+2), i+2,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tif (i + 3 < nbits) {\n\t\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+3),\n\t\t\t\t    nbits, \"Unexpected first unset bit\");\n\t\t\t}\n\t\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i+2,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t}\n\t}\n\n\t/*\n\t * Unset the last bit, bubble another unset bit through the bitmap, and\n\t * verify that bitmap_ffu() finds the correct bit for all four min_bit\n\t * cases.\n\t */\n\tif (nbits >= 3) {\n\t\tbitmap_unset(bitmap, binfo, nbits-1);\n\t\tfor (size_t i = 0; i < nbits-1; i++) {\n\t\t\tbitmap_unset(bitmap, binfo, i);\n\t\t\tif (i > 0) {\n\t\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,\n\t\t\t\t    \"Unexpected first unset bit\");\n\t\t\t}\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, i+1), nbits-1,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t\tassert_zu_eq(bitmap_ffu(bitmap, binfo, nbits-1),\n\t\t\t    nbits-1, \"Unexpected first unset bit\");\n\n\t\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), i,\n\t\t\t    \"Unexpected first unset bit\");\n\t\t}\n\t\tassert_zu_eq(bitmap_sfu(bitmap, binfo), nbits-1,\n\t\t    \"Unexpected first unset bit\");\n\t}\n\n\tfree(bitmap);\n}\n\nTEST_BEGIN(test_bitmap_xfu) {\n\tsize_t nbits;\n\n\tfor (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {\n\t\tbitmap_info_t binfo;\n\t\tbitmap_info_init(&binfo, nbits);\n\t\ttest_bitmap_xfu_body(&binfo, nbits);\n\t}\n#define NB(nbits) {\t\t\t\t\t\t\t\\\n\t\tbitmap_info_t binfo = BITMAP_INFO_INITIALIZER(nbits);\t\\\n\t\ttest_bitmap_xfu_body(&binfo, nbits);\t\t\t\\\n\t}\n\tNBITS_TAB\n#undef NB\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_bitmap_initializer,\n\t    test_bitmap_size,\n\t    test_bitmap_init,\n\t    test_bitmap_set,\n\t    test_bitmap_unset,\n\t    test_bitmap_xfu);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/ckh.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_new_delete) {\n\ttsd_t *tsd;\n\tckh_t ckh;\n\n\ttsd = tsd_fetch();\n\n\tassert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,\n\t    ckh_string_keycomp), \"Unexpected ckh_new() error\");\n\tckh_delete(tsd, &ckh);\n\n\tassert_false(ckh_new(tsd, &ckh, 3, ckh_pointer_hash,\n\t    ckh_pointer_keycomp), \"Unexpected ckh_new() error\");\n\tckh_delete(tsd, &ckh);\n}\nTEST_END\n\nTEST_BEGIN(test_count_insert_search_remove) {\n\ttsd_t *tsd;\n\tckh_t ckh;\n\tconst char *strs[] = {\n\t    \"a string\",\n\t    \"A string\",\n\t    \"a string.\",\n\t    \"A string.\"\n\t};\n\tconst char *missing = \"A string not in the hash table.\";\n\tsize_t i;\n\n\ttsd = tsd_fetch();\n\n\tassert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,\n\t    ckh_string_keycomp), \"Unexpected ckh_new() error\");\n\tassert_zu_eq(ckh_count(&ckh), 0,\n\t    \"ckh_count() should return %zu, but it returned %zu\", ZU(0),\n\t    ckh_count(&ckh));\n\n\t/* Insert. */\n\tfor (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {\n\t\tckh_insert(tsd, &ckh, strs[i], strs[i]);\n\t\tassert_zu_eq(ckh_count(&ckh), i+1,\n\t\t    \"ckh_count() should return %zu, but it returned %zu\", i+1,\n\t\t    ckh_count(&ckh));\n\t}\n\n\t/* Search. */\n\tfor (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {\n\t\tunion {\n\t\t\tvoid *p;\n\t\t\tconst char *s;\n\t\t} k, v;\n\t\tvoid **kp, **vp;\n\t\tconst char *ks, *vs;\n\n\t\tkp = (i & 1) ? &k.p : NULL;\n\t\tvp = (i & 2) ? &v.p : NULL;\n\t\tk.p = NULL;\n\t\tv.p = NULL;\n\t\tassert_false(ckh_search(&ckh, strs[i], kp, vp),\n\t\t    \"Unexpected ckh_search() error\");\n\n\t\tks = (i & 1) ? strs[i] : (const char *)NULL;\n\t\tvs = (i & 2) ? strs[i] : (const char *)NULL;\n\t\tassert_ptr_eq((void *)ks, (void *)k.s, \"Key mismatch, i=%zu\",\n\t\t    i);\n\t\tassert_ptr_eq((void *)vs, (void *)v.s, \"Value mismatch, i=%zu\",\n\t\t    i);\n\t}\n\tassert_true(ckh_search(&ckh, missing, NULL, NULL),\n\t    \"Unexpected ckh_search() success\");\n\n\t/* Remove. */\n\tfor (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {\n\t\tunion {\n\t\t\tvoid *p;\n\t\t\tconst char *s;\n\t\t} k, v;\n\t\tvoid **kp, **vp;\n\t\tconst char *ks, *vs;\n\n\t\tkp = (i & 1) ? &k.p : NULL;\n\t\tvp = (i & 2) ? &v.p : NULL;\n\t\tk.p = NULL;\n\t\tv.p = NULL;\n\t\tassert_false(ckh_remove(tsd, &ckh, strs[i], kp, vp),\n\t\t    \"Unexpected ckh_remove() error\");\n\n\t\tks = (i & 1) ? strs[i] : (const char *)NULL;\n\t\tvs = (i & 2) ? strs[i] : (const char *)NULL;\n\t\tassert_ptr_eq((void *)ks, (void *)k.s, \"Key mismatch, i=%zu\",\n\t\t    i);\n\t\tassert_ptr_eq((void *)vs, (void *)v.s, \"Value mismatch, i=%zu\",\n\t\t    i);\n\t\tassert_zu_eq(ckh_count(&ckh),\n\t\t    sizeof(strs)/sizeof(const char *) - i - 1,\n\t\t    \"ckh_count() should return %zu, but it returned %zu\",\n\t\t        sizeof(strs)/sizeof(const char *) - i - 1,\n\t\t    ckh_count(&ckh));\n\t}\n\n\tckh_delete(tsd, &ckh);\n}\nTEST_END\n\nTEST_BEGIN(test_insert_iter_remove) {\n#define NITEMS ZU(1000)\n\ttsd_t *tsd;\n\tckh_t ckh;\n\tvoid **p[NITEMS];\n\tvoid *q, *r;\n\tsize_t i;\n\n\ttsd = tsd_fetch();\n\n\tassert_false(ckh_new(tsd, &ckh, 2, ckh_pointer_hash,\n\t    ckh_pointer_keycomp), \"Unexpected ckh_new() error\");\n\n\tfor (i = 0; i < NITEMS; i++) {\n\t\tp[i] = mallocx(i+1, 0);\n\t\tassert_ptr_not_null(p[i], \"Unexpected mallocx() failure\");\n\t}\n\n\tfor (i = 0; i < NITEMS; i++) {\n\t\tsize_t j;\n\n\t\tfor (j = i; j < NITEMS; j++) {\n\t\t\tassert_false(ckh_insert(tsd, &ckh, p[j], p[j]),\n\t\t\t    \"Unexpected ckh_insert() failure\");\n\t\t\tassert_false(ckh_search(&ckh, p[j], &q, &r),\n\t\t\t    \"Unexpected ckh_search() failure\");\n\t\t\tassert_ptr_eq(p[j], q, \"Key pointer mismatch\");\n\t\t\tassert_ptr_eq(p[j], r, \"Value pointer mismatch\");\n\t\t}\n\n\t\tassert_zu_eq(ckh_count(&ckh), NITEMS,\n\t\t    \"ckh_count() should return %zu, but it returned %zu\",\n\t\t    NITEMS, ckh_count(&ckh));\n\n\t\tfor (j = i + 1; j < NITEMS; j++) {\n\t\t\tassert_false(ckh_search(&ckh, p[j], NULL, NULL),\n\t\t\t    \"Unexpected ckh_search() failure\");\n\t\t\tassert_false(ckh_remove(tsd, &ckh, p[j], &q, &r),\n\t\t\t    \"Unexpected ckh_remove() failure\");\n\t\t\tassert_ptr_eq(p[j], q, \"Key pointer mismatch\");\n\t\t\tassert_ptr_eq(p[j], r, \"Value pointer mismatch\");\n\t\t\tassert_true(ckh_search(&ckh, p[j], NULL, NULL),\n\t\t\t    \"Unexpected ckh_search() success\");\n\t\t\tassert_true(ckh_remove(tsd, &ckh, p[j], &q, &r),\n\t\t\t    \"Unexpected ckh_remove() success\");\n\t\t}\n\n\t\t{\n\t\t\tbool seen[NITEMS];\n\t\t\tsize_t tabind;\n\n\t\t\tmemset(seen, 0, sizeof(seen));\n\n\t\t\tfor (tabind = 0; !ckh_iter(&ckh, &tabind, &q, &r);) {\n\t\t\t\tsize_t k;\n\n\t\t\t\tassert_ptr_eq(q, r, \"Key and val not equal\");\n\n\t\t\t\tfor (k = 0; k < NITEMS; k++) {\n\t\t\t\t\tif (p[k] == q) {\n\t\t\t\t\t\tassert_false(seen[k],\n\t\t\t\t\t\t    \"Item %zu already seen\", k);\n\t\t\t\t\t\tseen[k] = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (j = 0; j < i + 1; j++) {\n\t\t\t\tassert_true(seen[j], \"Item %zu not seen\", j);\n\t\t\t}\n\t\t\tfor (; j < NITEMS; j++) {\n\t\t\t\tassert_false(seen[j], \"Item %zu seen\", j);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0; i < NITEMS; i++) {\n\t\tassert_false(ckh_search(&ckh, p[i], NULL, NULL),\n\t\t    \"Unexpected ckh_search() failure\");\n\t\tassert_false(ckh_remove(tsd, &ckh, p[i], &q, &r),\n\t\t    \"Unexpected ckh_remove() failure\");\n\t\tassert_ptr_eq(p[i], q, \"Key pointer mismatch\");\n\t\tassert_ptr_eq(p[i], r, \"Value pointer mismatch\");\n\t\tassert_true(ckh_search(&ckh, p[i], NULL, NULL),\n\t\t    \"Unexpected ckh_search() success\");\n\t\tassert_true(ckh_remove(tsd, &ckh, p[i], &q, &r),\n\t\t    \"Unexpected ckh_remove() success\");\n\t\tdallocx(p[i], 0);\n\t}\n\n\tassert_zu_eq(ckh_count(&ckh), 0,\n\t    \"ckh_count() should return %zu, but it returned %zu\",\n\t    ZU(0), ckh_count(&ckh));\n\tckh_delete(tsd, &ckh);\n#undef NITEMS\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_new_delete,\n\t    test_count_insert_search_remove,\n\t    test_insert_iter_remove);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/decay.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ticker.h\"\n\nstatic nstime_monotonic_t *nstime_monotonic_orig;\nstatic nstime_update_t *nstime_update_orig;\n\nstatic unsigned nupdates_mock;\nstatic nstime_t time_mock;\nstatic bool monotonic_mock;\n\nstatic bool\ncheck_background_thread_enabled(void) {\n\tbool enabled;\n\tsize_t sz = sizeof(bool);\n\tint ret = mallctl(\"background_thread\", (void *)&enabled, &sz, NULL,0);\n\tif (ret == ENOENT) {\n\t\treturn false;\n\t}\n\tassert_d_eq(ret, 0, \"Unexpected mallctl error\");\n\treturn enabled;\n}\n\nstatic bool\nnstime_monotonic_mock(void) {\n\treturn monotonic_mock;\n}\n\nstatic bool\nnstime_update_mock(nstime_t *time) {\n\tnupdates_mock++;\n\tif (monotonic_mock) {\n\t\tnstime_copy(time, &time_mock);\n\t}\n\treturn !monotonic_mock;\n}\n\nstatic unsigned\ndo_arena_create(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.dirty_decay_ms\", mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(dirty_decay_ms)), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.muzzy_decay_ms\", mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(muzzy_decay_ms)), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\treturn arena_ind;\n}\n\nstatic void\ndo_arena_destroy(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.destroy\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nvoid\ndo_epoch(void) {\n\tuint64_t epoch = 1;\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n}\n\nvoid\ndo_purge(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.purge\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nvoid\ndo_decay(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.decay\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nstatic uint64_t\nget_arena_npurge_impl(const char *mibname, unsigned arena_ind) {\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(mibname, mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[2] = (size_t)arena_ind;\n\tuint64_t npurge = 0;\n\tsize_t sz = sizeof(npurge);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&npurge, &sz, NULL, 0),\n\t    config_stats ? 0 : ENOENT, \"Unexpected mallctlbymib() failure\");\n\treturn npurge;\n}\n\nstatic uint64_t\nget_arena_dirty_npurge(unsigned arena_ind) {\n\tdo_epoch();\n\treturn get_arena_npurge_impl(\"stats.arenas.0.dirty_npurge\", arena_ind);\n}\n\nstatic uint64_t\nget_arena_muzzy_npurge(unsigned arena_ind) {\n\tdo_epoch();\n\treturn get_arena_npurge_impl(\"stats.arenas.0.muzzy_npurge\", arena_ind);\n}\n\nstatic uint64_t\nget_arena_npurge(unsigned arena_ind) {\n\tdo_epoch();\n\treturn get_arena_npurge_impl(\"stats.arenas.0.dirty_npurge\", arena_ind) +\n\t    get_arena_npurge_impl(\"stats.arenas.0.muzzy_npurge\", arena_ind);\n}\n\nstatic size_t\nget_arena_pdirty(unsigned arena_ind) {\n\tdo_epoch();\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"stats.arenas.0.pdirty\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[2] = (size_t)arena_ind;\n\tsize_t pdirty;\n\tsize_t sz = sizeof(pdirty);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&pdirty, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\treturn pdirty;\n}\n\nstatic size_t\nget_arena_pmuzzy(unsigned arena_ind) {\n\tdo_epoch();\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"stats.arenas.0.pmuzzy\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[2] = (size_t)arena_ind;\n\tsize_t pmuzzy;\n\tsize_t sz = sizeof(pmuzzy);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&pmuzzy, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\treturn pmuzzy;\n}\n\nstatic void *\ndo_mallocx(size_t size, int flags) {\n\tvoid *p = mallocx(size, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\treturn p;\n}\n\nstatic void\ngenerate_dirty(unsigned arena_ind, size_t size) {\n\tint flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\tvoid *p = do_mallocx(size, flags);\n\tdallocx(p, flags);\n}\n\nTEST_BEGIN(test_decay_ticks) {\n\ttest_skip_if(check_background_thread_enabled());\n\n\tticker_t *decay_ticker;\n\tunsigned tick0, tick1, arena_ind;\n\tsize_t sz, large0;\n\tvoid *p;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&large0, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\t/* Set up a manually managed arena for test. */\n\tarena_ind = do_arena_create(0, 0);\n\n\t/* Migrate to the new arena, and get the ticker. */\n\tunsigned old_arena_ind;\n\tsize_t sz_arena_ind = sizeof(old_arena_ind);\n\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind,\n\t    &sz_arena_ind, (void *)&arena_ind, sizeof(arena_ind)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tdecay_ticker = decay_ticker_get(tsd_fetch(), arena_ind);\n\tassert_ptr_not_null(decay_ticker,\n\t    \"Unexpected failure getting decay ticker\");\n\n\t/*\n\t * Test the standard APIs using a large size class, since we can't\n\t * control tcache interactions for small size classes (except by\n\t * completely disabling tcache for the entire test program).\n\t */\n\n\t/* malloc(). */\n\ttick0 = ticker_read(decay_ticker);\n\tp = malloc(large0);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during malloc()\");\n\t/* free(). */\n\ttick0 = ticker_read(decay_ticker);\n\tfree(p);\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during free()\");\n\n\t/* calloc(). */\n\ttick0 = ticker_read(decay_ticker);\n\tp = calloc(1, large0);\n\tassert_ptr_not_null(p, \"Unexpected calloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during calloc()\");\n\tfree(p);\n\n\t/* posix_memalign(). */\n\ttick0 = ticker_read(decay_ticker);\n\tassert_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0,\n\t    \"Unexpected posix_memalign() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0,\n\t    \"Expected ticker to tick during posix_memalign()\");\n\tfree(p);\n\n\t/* aligned_alloc(). */\n\ttick0 = ticker_read(decay_ticker);\n\tp = aligned_alloc(sizeof(size_t), large0);\n\tassert_ptr_not_null(p, \"Unexpected aligned_alloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0,\n\t    \"Expected ticker to tick during aligned_alloc()\");\n\tfree(p);\n\n\t/* realloc(). */\n\t/* Allocate. */\n\ttick0 = ticker_read(decay_ticker);\n\tp = realloc(NULL, large0);\n\tassert_ptr_not_null(p, \"Unexpected realloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during realloc()\");\n\t/* Reallocate. */\n\ttick0 = ticker_read(decay_ticker);\n\tp = realloc(p, large0);\n\tassert_ptr_not_null(p, \"Unexpected realloc() failure\");\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during realloc()\");\n\t/* Deallocate. */\n\ttick0 = ticker_read(decay_ticker);\n\trealloc(p, 0);\n\ttick1 = ticker_read(decay_ticker);\n\tassert_u32_ne(tick1, tick0, \"Expected ticker to tick during realloc()\");\n\n\t/*\n\t * Test the *allocx() APIs using large and small size classes, with\n\t * tcache explicitly disabled.\n\t */\n\t{\n\t\tunsigned i;\n\t\tsize_t allocx_sizes[2];\n\t\tallocx_sizes[0] = large0;\n\t\tallocx_sizes[1] = 1;\n\n\t\tfor (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {\n\t\t\tsz = allocx_sizes[i];\n\n\t\t\t/* mallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tp = mallocx(sz, MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during mallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* rallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tp = rallocx(p, sz, MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p, \"Unexpected rallocx() failure\");\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during rallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* xallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\txallocx(p, sz, 0, MALLOCX_TCACHE_NONE);\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during xallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* dallocx(). */\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tdallocx(p, MALLOCX_TCACHE_NONE);\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during dallocx() (sz=%zu)\",\n\t\t\t    sz);\n\t\t\t/* sdallocx(). */\n\t\t\tp = mallocx(sz, MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\t\ttick0 = ticker_read(decay_ticker);\n\t\t\tsdallocx(p, sz, MALLOCX_TCACHE_NONE);\n\t\t\ttick1 = ticker_read(decay_ticker);\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during sdallocx() \"\n\t\t\t    \"(sz=%zu)\", sz);\n\t\t}\n\t}\n\n\t/*\n\t * Test tcache fill/flush interactions for large and small size classes,\n\t * using an explicit tcache.\n\t */\n\tunsigned tcache_ind, i;\n\tsize_t tcache_sizes[2];\n\ttcache_sizes[0] = large0;\n\ttcache_sizes[1] = 1;\n\n\tsize_t tcache_max, sz_tcache_max;\n\tsz_tcache_max = sizeof(tcache_max);\n\tassert_d_eq(mallctl(\"arenas.tcache_max\", (void *)&tcache_max,\n\t    &sz_tcache_max, NULL, 0), 0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"tcache.create\", (void *)&tcache_ind, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl failure\");\n\n\tfor (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {\n\t\tsz = tcache_sizes[i];\n\n\t\t/* tcache fill. */\n\t\ttick0 = ticker_read(decay_ticker);\n\t\tp = mallocx(sz, MALLOCX_TCACHE(tcache_ind));\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\ttick1 = ticker_read(decay_ticker);\n\t\tassert_u32_ne(tick1, tick0,\n\t\t    \"Expected ticker to tick during tcache fill \"\n\t\t    \"(sz=%zu)\", sz);\n\t\t/* tcache flush. */\n\t\tdallocx(p, MALLOCX_TCACHE(tcache_ind));\n\t\ttick0 = ticker_read(decay_ticker);\n\t\tassert_d_eq(mallctl(\"tcache.flush\", NULL, NULL,\n\t\t    (void *)&tcache_ind, sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl failure\");\n\t\ttick1 = ticker_read(decay_ticker);\n\n\t\t/* Will only tick if it's in tcache. */\n\t\tif (sz <= tcache_max) {\n\t\t\tassert_u32_ne(tick1, tick0,\n\t\t\t    \"Expected ticker to tick during tcache \"\n\t\t\t    \"flush (sz=%zu)\", sz);\n\t\t} else {\n\t\t\tassert_u32_eq(tick1, tick0,\n\t\t\t    \"Unexpected ticker tick during tcache \"\n\t\t\t    \"flush (sz=%zu)\", sz);\n\t\t}\n\t}\n}\nTEST_END\n\nstatic void\ndecay_ticker_helper(unsigned arena_ind, int flags, bool dirty, ssize_t dt,\n    uint64_t dirty_npurge0, uint64_t muzzy_npurge0, bool terminate_asap) {\n#define NINTERVALS 101\n\tnstime_t time, update_interval, decay_ms, deadline;\n\n\tnstime_init(&time, 0);\n\tnstime_update(&time);\n\n\tnstime_init2(&decay_ms, dt, 0);\n\tnstime_copy(&deadline, &time);\n\tnstime_add(&deadline, &decay_ms);\n\n\tnstime_init2(&update_interval, dt, 0);\n\tnstime_idivide(&update_interval, NINTERVALS);\n\n\t/*\n\t * Keep q's slab from being deallocated during the looping below.  If a\n\t * cached slab were to repeatedly come and go during looping, it could\n\t * prevent the decay backlog ever becoming empty.\n\t */\n\tvoid *p = do_mallocx(1, flags);\n\tuint64_t dirty_npurge1, muzzy_npurge1;\n\tdo {\n\t\tfor (unsigned i = 0; i < DECAY_NTICKS_PER_UPDATE / 2;\n\t\t    i++) {\n\t\t\tvoid *q = do_mallocx(1, flags);\n\t\t\tdallocx(q, flags);\n\t\t}\n\t\tdirty_npurge1 = get_arena_dirty_npurge(arena_ind);\n\t\tmuzzy_npurge1 = get_arena_muzzy_npurge(arena_ind);\n\n\t\tnstime_add(&time_mock, &update_interval);\n\t\tnstime_update(&time);\n\t} while (nstime_compare(&time, &deadline) <= 0 && ((dirty_npurge1 ==\n\t    dirty_npurge0 && muzzy_npurge1 == muzzy_npurge0) ||\n\t    !terminate_asap));\n\tdallocx(p, flags);\n\n\tif (config_stats) {\n\t\tassert_u64_gt(dirty_npurge1 + muzzy_npurge1, dirty_npurge0 +\n\t\t    muzzy_npurge0, \"Expected purging to occur\");\n\t}\n#undef NINTERVALS\n}\n\nTEST_BEGIN(test_decay_ticker) {\n\ttest_skip_if(check_background_thread_enabled());\n#define NPS 2048\n\tssize_t ddt = opt_dirty_decay_ms;\n\tssize_t mdt = opt_muzzy_decay_ms;\n\tunsigned arena_ind = do_arena_create(ddt, mdt);\n\tint flags = (MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE);\n\tvoid *ps[NPS];\n\tsize_t large;\n\n\t/*\n\t * Allocate a bunch of large objects, pause the clock, deallocate every\n\t * other object (to fragment virtual memory), restore the clock, then\n\t * [md]allocx() in a tight loop while advancing time rapidly to verify\n\t * the ticker triggers purging.\n\t */\n\n\tsize_t tcache_max;\n\tsize_t sz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.tcache_max\", (void *)&tcache_max, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\tlarge = nallocx(tcache_max + 1, flags);\n\n\tdo_purge(arena_ind);\n\tuint64_t dirty_npurge0 = get_arena_dirty_npurge(arena_ind);\n\tuint64_t muzzy_npurge0 = get_arena_muzzy_npurge(arena_ind);\n\n\tfor (unsigned i = 0; i < NPS; i++) {\n\t\tps[i] = do_mallocx(large, flags);\n\t}\n\n\tnupdates_mock = 0;\n\tnstime_init(&time_mock, 0);\n\tnstime_update(&time_mock);\n\tmonotonic_mock = true;\n\n\tnstime_monotonic_orig = nstime_monotonic;\n\tnstime_update_orig = nstime_update;\n\tnstime_monotonic = nstime_monotonic_mock;\n\tnstime_update = nstime_update_mock;\n\n\tfor (unsigned i = 0; i < NPS; i += 2) {\n\t\tdallocx(ps[i], flags);\n\t\tunsigned nupdates0 = nupdates_mock;\n\t\tdo_decay(arena_ind);\n\t\tassert_u_gt(nupdates_mock, nupdates0,\n\t\t    \"Expected nstime_update() to be called\");\n\t}\n\n\tdecay_ticker_helper(arena_ind, flags, true, ddt, dirty_npurge0,\n\t    muzzy_npurge0, true);\n\tdecay_ticker_helper(arena_ind, flags, false, ddt+mdt, dirty_npurge0,\n\t    muzzy_npurge0, false);\n\n\tdo_arena_destroy(arena_ind);\n\n\tnstime_monotonic = nstime_monotonic_orig;\n\tnstime_update = nstime_update_orig;\n#undef NPS\n}\nTEST_END\n\nTEST_BEGIN(test_decay_nonmonotonic) {\n\ttest_skip_if(check_background_thread_enabled());\n#define NPS (SMOOTHSTEP_NSTEPS + 1)\n\tint flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);\n\tvoid *ps[NPS];\n\tuint64_t npurge0 = 0;\n\tuint64_t npurge1 = 0;\n\tsize_t sz, large0;\n\tunsigned i, nupdates0;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&large0, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl failure\");\n\tdo_epoch();\n\tsz = sizeof(uint64_t);\n\tnpurge0 = get_arena_npurge(0);\n\n\tnupdates_mock = 0;\n\tnstime_init(&time_mock, 0);\n\tnstime_update(&time_mock);\n\tmonotonic_mock = false;\n\n\tnstime_monotonic_orig = nstime_monotonic;\n\tnstime_update_orig = nstime_update;\n\tnstime_monotonic = nstime_monotonic_mock;\n\tnstime_update = nstime_update_mock;\n\n\tfor (i = 0; i < NPS; i++) {\n\t\tps[i] = mallocx(large0, flags);\n\t\tassert_ptr_not_null(ps[i], \"Unexpected mallocx() failure\");\n\t}\n\n\tfor (i = 0; i < NPS; i++) {\n\t\tdallocx(ps[i], flags);\n\t\tnupdates0 = nupdates_mock;\n\t\tassert_d_eq(mallctl(\"arena.0.decay\", NULL, NULL, NULL, 0), 0,\n\t\t    \"Unexpected arena.0.decay failure\");\n\t\tassert_u_gt(nupdates_mock, nupdates0,\n\t\t    \"Expected nstime_update() to be called\");\n\t}\n\n\tdo_epoch();\n\tsz = sizeof(uint64_t);\n\tnpurge1 = get_arena_npurge(0);\n\n\tif (config_stats) {\n\t\tassert_u64_eq(npurge0, npurge1, \"Unexpected purging occurred\");\n\t}\n\n\tnstime_monotonic = nstime_monotonic_orig;\n\tnstime_update = nstime_update_orig;\n#undef NPS\n}\nTEST_END\n\nTEST_BEGIN(test_decay_now) {\n\ttest_skip_if(check_background_thread_enabled());\n\n\tunsigned arena_ind = do_arena_create(0, 0);\n\tassert_zu_eq(get_arena_pdirty(arena_ind), 0, \"Unexpected dirty pages\");\n\tassert_zu_eq(get_arena_pmuzzy(arena_ind), 0, \"Unexpected muzzy pages\");\n\tsize_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};\n\t/* Verify that dirty/muzzy pages never linger after deallocation. */\n\tfor (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {\n\t\tsize_t size = sizes[i];\n\t\tgenerate_dirty(arena_ind, size);\n\t\tassert_zu_eq(get_arena_pdirty(arena_ind), 0,\n\t\t    \"Unexpected dirty pages\");\n\t\tassert_zu_eq(get_arena_pmuzzy(arena_ind), 0,\n\t\t    \"Unexpected muzzy pages\");\n\t}\n\tdo_arena_destroy(arena_ind);\n}\nTEST_END\n\nTEST_BEGIN(test_decay_never) {\n\ttest_skip_if(check_background_thread_enabled());\n\n\tunsigned arena_ind = do_arena_create(-1, -1);\n\tint flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;\n\tassert_zu_eq(get_arena_pdirty(arena_ind), 0, \"Unexpected dirty pages\");\n\tassert_zu_eq(get_arena_pmuzzy(arena_ind), 0, \"Unexpected muzzy pages\");\n\tsize_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};\n\tvoid *ptrs[sizeof(sizes)/sizeof(size_t)];\n\tfor (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {\n\t\tptrs[i] = do_mallocx(sizes[i], flags);\n\t}\n\t/* Verify that each deallocation generates additional dirty pages. */\n\tsize_t pdirty_prev = get_arena_pdirty(arena_ind);\n\tsize_t pmuzzy_prev = get_arena_pmuzzy(arena_ind);\n\tassert_zu_eq(pdirty_prev, 0, \"Unexpected dirty pages\");\n\tassert_zu_eq(pmuzzy_prev, 0, \"Unexpected muzzy pages\");\n\tfor (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {\n\t\tdallocx(ptrs[i], flags);\n\t\tsize_t pdirty = get_arena_pdirty(arena_ind);\n\t\tsize_t pmuzzy = get_arena_pmuzzy(arena_ind);\n\t\tassert_zu_gt(pdirty, pdirty_prev,\n\t\t    \"Expected dirty pages to increase.\");\n\t\tassert_zu_eq(pmuzzy, 0, \"Unexpected muzzy pages\");\n\t\tpdirty_prev = pdirty;\n\t}\n\tdo_arena_destroy(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_decay_ticks,\n\t    test_decay_ticker,\n\t    test_decay_nonmonotonic,\n\t    test_decay_now,\n\t    test_decay_never);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/decay.sh",
    "content": "#!/bin/sh\n\nexport MALLOC_CONF=\"dirty_decay_ms:1000,muzzy_decay_ms:1000,lg_tcache_max:0\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/div.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/div.h\"\n\nTEST_BEGIN(test_div_exhaustive) {\n\tfor (size_t divisor = 2; divisor < 1000 * 1000; ++divisor) {\n\t\tdiv_info_t div_info;\n\t\tdiv_init(&div_info, divisor);\n\t\tsize_t max = 1000 * divisor;\n\t\tif (max < 1000 * 1000) {\n\t\t\tmax = 1000 * 1000;\n\t\t}\n\t\tfor (size_t dividend = 0; dividend < 1000 * divisor;\n\t\t    dividend += divisor) {\n\t\t\tsize_t quotient = div_compute(\n\t\t\t    &div_info, dividend);\n\t\t\tassert_zu_eq(dividend, quotient * divisor,\n\t\t\t    \"With divisor = %zu, dividend = %zu, \"\n\t\t\t    \"got quotient %zu\", divisor, dividend, quotient);\n\t\t}\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_div_exhaustive);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/emitter.c",
    "content": "#include \"test/jemalloc_test.h\"\n#include \"jemalloc/internal/emitter.h\"\n\n/*\n * This is so useful for debugging and feature work, we'll leave printing\n * functionality committed but disabled by default.\n */\n/* Print the text as it will appear. */\nstatic bool print_raw = false;\n/* Print the text escaped, so it can be copied back into the test case. */\nstatic bool print_escaped = false;\n\ntypedef struct buf_descriptor_s buf_descriptor_t;\nstruct buf_descriptor_s {\n\tchar *buf;\n\tsize_t len;\n\tbool mid_quote;\n};\n\n/*\n * Forwards all writes to the passed-in buf_v (which should be cast from a\n * buf_descriptor_t *).\n */\nstatic void\nforwarding_cb(void *buf_descriptor_v, const char *str) {\n\tbuf_descriptor_t *buf_descriptor = (buf_descriptor_t *)buf_descriptor_v;\n\n\tif (print_raw) {\n\t\tmalloc_printf(\"%s\", str);\n\t}\n\tif (print_escaped) {\n\t\tconst char *it = str;\n\t\twhile (*it != '\\0') {\n\t\t\tif (!buf_descriptor->mid_quote) {\n\t\t\t\tmalloc_printf(\"\\\"\");\n\t\t\t\tbuf_descriptor->mid_quote = true;\n\t\t\t}\n\t\t\tswitch (*it) {\n\t\t\tcase '\\\\':\n\t\t\t\tmalloc_printf(\"\\\\\");\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\tmalloc_printf(\"\\\\\\\"\");\n\t\t\t\tbreak;\n\t\t\tcase '\\t':\n\t\t\t\tmalloc_printf(\"\\\\t\");\n\t\t\t\tbreak;\n\t\t\tcase '\\n':\n\t\t\t\tmalloc_printf(\"\\\\n\\\"\\n\");\n\t\t\t\tbuf_descriptor->mid_quote = false;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tmalloc_printf(\"%c\", *it);\n\t\t\t}\n\t\t\tit++;\n\t\t}\n\t}\n\n\tsize_t written = malloc_snprintf(buf_descriptor->buf,\n\t    buf_descriptor->len, \"%s\", str);\n\tassert_zu_eq(written, strlen(str), \"Buffer overflow!\");\n\tbuf_descriptor->buf += written;\n\tbuf_descriptor->len -= written;\n\tassert_zu_gt(buf_descriptor->len, 0, \"Buffer out of space!\");\n}\n\nstatic void\nassert_emit_output(void (*emit_fn)(emitter_t *),\n    const char *expected_json_output, const char *expected_table_output) {\n\temitter_t emitter;\n\tchar buf[MALLOC_PRINTF_BUFSIZE];\n\tbuf_descriptor_t buf_descriptor;\n\n\tbuf_descriptor.buf = buf;\n\tbuf_descriptor.len = MALLOC_PRINTF_BUFSIZE;\n\tbuf_descriptor.mid_quote = false;\n\n\temitter_init(&emitter, emitter_output_json, &forwarding_cb,\n\t    &buf_descriptor);\n\t(*emit_fn)(&emitter);\n\tassert_str_eq(expected_json_output, buf, \"json output failure\");\n\n\tbuf_descriptor.buf = buf;\n\tbuf_descriptor.len = MALLOC_PRINTF_BUFSIZE;\n\tbuf_descriptor.mid_quote = false;\n\n\temitter_init(&emitter, emitter_output_table, &forwarding_cb,\n\t    &buf_descriptor);\n\t(*emit_fn)(&emitter);\n\tassert_str_eq(expected_table_output, buf, \"table output failure\");\n}\n\nstatic void\nemit_dict(emitter_t *emitter) {\n\tbool b_false = false;\n\tbool b_true = true;\n\tint i_123 = 123;\n\tconst char *str = \"a string\";\n\n\temitter_begin(emitter);\n\temitter_dict_begin(emitter, \"foo\", \"This is the foo table:\");\n\temitter_kv(emitter, \"abc\", \"ABC\", emitter_type_bool, &b_false);\n\temitter_kv(emitter, \"def\", \"DEF\", emitter_type_bool, &b_true);\n\temitter_kv_note(emitter, \"ghi\", \"GHI\", emitter_type_int, &i_123,\n\t    \"note_key1\", emitter_type_string, &str);\n\temitter_kv_note(emitter, \"jkl\", \"JKL\", emitter_type_string, &str,\n\t    \"note_key2\", emitter_type_bool, &b_false);\n\temitter_dict_end(emitter);\n\temitter_end(emitter);\n}\nstatic const char *dict_json =\n\"{\\n\"\n\"\\t\\\"foo\\\": {\\n\"\n\"\\t\\t\\\"abc\\\": false,\\n\"\n\"\\t\\t\\\"def\\\": true,\\n\"\n\"\\t\\t\\\"ghi\\\": 123,\\n\"\n\"\\t\\t\\\"jkl\\\": \\\"a string\\\"\\n\"\n\"\\t}\\n\"\n\"}\\n\";\nstatic const char *dict_table =\n\"This is the foo table:\\n\"\n\"  ABC: false\\n\"\n\"  DEF: true\\n\"\n\"  GHI: 123 (note_key1: \\\"a string\\\")\\n\"\n\"  JKL: \\\"a string\\\" (note_key2: false)\\n\";\n\nTEST_BEGIN(test_dict) {\n\tassert_emit_output(&emit_dict, dict_json, dict_table);\n}\nTEST_END\n\nstatic void\nemit_table_printf(emitter_t *emitter) {\n\temitter_begin(emitter);\n\temitter_table_printf(emitter, \"Table note 1\\n\");\n\temitter_table_printf(emitter, \"Table note 2 %s\\n\",\n\t    \"with format string\");\n\temitter_end(emitter);\n}\n\nstatic const char *table_printf_json =\n\"{\\n\"\n\"}\\n\";\n\nstatic const char *table_printf_table =\n\"Table note 1\\n\"\n\"Table note 2 with format string\\n\";\n\nTEST_BEGIN(test_table_printf) {\n\tassert_emit_output(&emit_table_printf, table_printf_json,\n\t    table_printf_table);\n}\nTEST_END\n\nstatic void emit_nested_dict(emitter_t *emitter) {\n\tint val = 123;\n\temitter_begin(emitter);\n\temitter_dict_begin(emitter, \"json1\", \"Dict 1\");\n\temitter_dict_begin(emitter, \"json2\", \"Dict 2\");\n\temitter_kv(emitter, \"primitive\", \"A primitive\", emitter_type_int, &val);\n\temitter_dict_end(emitter); /* Close 2 */\n\temitter_dict_begin(emitter, \"json3\", \"Dict 3\");\n\temitter_dict_end(emitter); /* Close 3 */\n\temitter_dict_end(emitter); /* Close 1 */\n\temitter_dict_begin(emitter, \"json4\", \"Dict 4\");\n\temitter_kv(emitter, \"primitive\", \"Another primitive\",\n\t    emitter_type_int, &val);\n\temitter_dict_end(emitter); /* Close 4 */\n\temitter_end(emitter);\n}\n\nstatic const char *nested_dict_json =\n\"{\\n\"\n\"\\t\\\"json1\\\": {\\n\"\n\"\\t\\t\\\"json2\\\": {\\n\"\n\"\\t\\t\\t\\\"primitive\\\": 123\\n\"\n\"\\t\\t},\\n\"\n\"\\t\\t\\\"json3\\\": {\\n\"\n\"\\t\\t}\\n\"\n\"\\t},\\n\"\n\"\\t\\\"json4\\\": {\\n\"\n\"\\t\\t\\\"primitive\\\": 123\\n\"\n\"\\t}\\n\"\n\"}\\n\";\n\nstatic const char *nested_dict_table =\n\"Dict 1\\n\"\n\"  Dict 2\\n\"\n\"    A primitive: 123\\n\"\n\"  Dict 3\\n\"\n\"Dict 4\\n\"\n\"  Another primitive: 123\\n\";\n\nTEST_BEGIN(test_nested_dict) {\n\tassert_emit_output(&emit_nested_dict, nested_dict_json,\n\t    nested_dict_table);\n}\nTEST_END\n\nstatic void\nemit_types(emitter_t *emitter) {\n\tbool b = false;\n\tint i = -123;\n\tunsigned u = 123;\n\tssize_t zd = -456;\n\tsize_t zu = 456;\n\tconst char *str = \"string\";\n\tuint32_t u32 = 789;\n\tuint64_t u64 = 10000000000ULL;\n\n\temitter_begin(emitter);\n\temitter_kv(emitter, \"k1\", \"K1\", emitter_type_bool, &b);\n\temitter_kv(emitter, \"k2\", \"K2\", emitter_type_int, &i);\n\temitter_kv(emitter, \"k3\", \"K3\", emitter_type_unsigned, &u);\n\temitter_kv(emitter, \"k4\", \"K4\", emitter_type_ssize, &zd);\n\temitter_kv(emitter, \"k5\", \"K5\", emitter_type_size, &zu);\n\temitter_kv(emitter, \"k6\", \"K6\", emitter_type_string, &str);\n\temitter_kv(emitter, \"k7\", \"K7\", emitter_type_uint32, &u32);\n\temitter_kv(emitter, \"k8\", \"K8\", emitter_type_uint64, &u64);\n\t/*\n\t * We don't test the title type, since it's only used for tables.  It's\n\t * tested in the emitter_table_row tests.\n\t */\n\temitter_end(emitter);\n}\n\nstatic const char *types_json =\n\"{\\n\"\n\"\\t\\\"k1\\\": false,\\n\"\n\"\\t\\\"k2\\\": -123,\\n\"\n\"\\t\\\"k3\\\": 123,\\n\"\n\"\\t\\\"k4\\\": -456,\\n\"\n\"\\t\\\"k5\\\": 456,\\n\"\n\"\\t\\\"k6\\\": \\\"string\\\",\\n\"\n\"\\t\\\"k7\\\": 789,\\n\"\n\"\\t\\\"k8\\\": 10000000000\\n\"\n\"}\\n\";\n\nstatic const char *types_table =\n\"K1: false\\n\"\n\"K2: -123\\n\"\n\"K3: 123\\n\"\n\"K4: -456\\n\"\n\"K5: 456\\n\"\n\"K6: \\\"string\\\"\\n\"\n\"K7: 789\\n\"\n\"K8: 10000000000\\n\";\n\nTEST_BEGIN(test_types) {\n\tassert_emit_output(&emit_types, types_json, types_table);\n}\nTEST_END\n\nstatic void\nemit_modal(emitter_t *emitter) {\n\tint val = 123;\n\temitter_begin(emitter);\n\temitter_dict_begin(emitter, \"j0\", \"T0\");\n\temitter_json_dict_begin(emitter, \"j1\");\n\temitter_kv(emitter, \"i1\", \"I1\", emitter_type_int, &val);\n\temitter_json_kv(emitter, \"i2\", emitter_type_int, &val);\n\temitter_table_kv(emitter, \"I3\", emitter_type_int, &val);\n\temitter_table_dict_begin(emitter, \"T1\");\n\temitter_kv(emitter, \"i4\", \"I4\", emitter_type_int, &val);\n\temitter_json_dict_end(emitter); /* Close j1 */\n\temitter_kv(emitter, \"i5\", \"I5\", emitter_type_int, &val);\n\temitter_table_dict_end(emitter); /* Close T1 */\n\temitter_kv(emitter, \"i6\", \"I6\", emitter_type_int, &val);\n\temitter_dict_end(emitter); /* Close j0 / T0 */\n\temitter_end(emitter);\n}\n\nconst char *modal_json =\n\"{\\n\"\n\"\\t\\\"j0\\\": {\\n\"\n\"\\t\\t\\\"j1\\\": {\\n\"\n\"\\t\\t\\t\\\"i1\\\": 123,\\n\"\n\"\\t\\t\\t\\\"i2\\\": 123,\\n\"\n\"\\t\\t\\t\\\"i4\\\": 123\\n\"\n\"\\t\\t},\\n\"\n\"\\t\\t\\\"i5\\\": 123,\\n\"\n\"\\t\\t\\\"i6\\\": 123\\n\"\n\"\\t}\\n\"\n\"}\\n\";\n\nconst char *modal_table =\n\"T0\\n\"\n\"  I1: 123\\n\"\n\"  I3: 123\\n\"\n\"  T1\\n\"\n\"    I4: 123\\n\"\n\"    I5: 123\\n\"\n\"  I6: 123\\n\";\n\nTEST_BEGIN(test_modal) {\n\tassert_emit_output(&emit_modal, modal_json, modal_table);\n}\nTEST_END\n\nstatic void\nemit_json_arr(emitter_t *emitter) {\n\tint ival = 123;\n\n\temitter_begin(emitter);\n\temitter_json_dict_begin(emitter, \"dict\");\n\temitter_json_arr_begin(emitter, \"arr\");\n\temitter_json_arr_obj_begin(emitter);\n\temitter_json_kv(emitter, \"foo\", emitter_type_int, &ival);\n\temitter_json_arr_obj_end(emitter); /* Close arr[0] */\n\t/* arr[1] and arr[2] are primitives. */\n\temitter_json_arr_value(emitter, emitter_type_int, &ival);\n\temitter_json_arr_value(emitter, emitter_type_int, &ival);\n\temitter_json_arr_obj_begin(emitter);\n\temitter_json_kv(emitter, \"bar\", emitter_type_int, &ival);\n\temitter_json_kv(emitter, \"baz\", emitter_type_int, &ival);\n\temitter_json_arr_obj_end(emitter); /* Close arr[3]. */\n\temitter_json_arr_end(emitter); /* Close arr. */\n\temitter_json_dict_end(emitter); /* Close dict. */\n\temitter_end(emitter);\n}\n\nstatic const char *json_arr_json =\n\"{\\n\"\n\"\\t\\\"dict\\\": {\\n\"\n\"\\t\\t\\\"arr\\\": [\\n\"\n\"\\t\\t\\t{\\n\"\n\"\\t\\t\\t\\t\\\"foo\\\": 123\\n\"\n\"\\t\\t\\t},\\n\"\n\"\\t\\t\\t123,\\n\"\n\"\\t\\t\\t123,\\n\"\n\"\\t\\t\\t{\\n\"\n\"\\t\\t\\t\\t\\\"bar\\\": 123,\\n\"\n\"\\t\\t\\t\\t\\\"baz\\\": 123\\n\"\n\"\\t\\t\\t}\\n\"\n\"\\t\\t]\\n\"\n\"\\t}\\n\"\n\"}\\n\";\n\nstatic const char *json_arr_table = \"\";\n\nTEST_BEGIN(test_json_arr) {\n\tassert_emit_output(&emit_json_arr, json_arr_json, json_arr_table);\n}\nTEST_END\n\nstatic void\nemit_table_row(emitter_t *emitter) {\n\temitter_begin(emitter);\n\temitter_row_t row;\n\temitter_col_t abc = {emitter_justify_left, 10, emitter_type_title};\n\tabc.str_val = \"ABC title\";\n\temitter_col_t def = {emitter_justify_right, 15, emitter_type_title};\n\tdef.str_val = \"DEF title\";\n\temitter_col_t ghi = {emitter_justify_right, 5, emitter_type_title};\n\tghi.str_val = \"GHI\";\n\n\temitter_row_init(&row);\n\temitter_col_init(&abc, &row);\n\temitter_col_init(&def, &row);\n\temitter_col_init(&ghi, &row);\n\n\temitter_table_row(emitter, &row);\n\n\tabc.type = emitter_type_int;\n\tdef.type = emitter_type_bool;\n\tghi.type = emitter_type_int;\n\n\tabc.int_val = 123;\n\tdef.bool_val = true;\n\tghi.int_val = 456;\n\temitter_table_row(emitter, &row);\n\n\tabc.int_val = 789;\n\tdef.bool_val = false;\n\tghi.int_val = 1011;\n\temitter_table_row(emitter, &row);\n\n\tabc.type = emitter_type_string;\n\tabc.str_val = \"a string\";\n\tdef.bool_val = false;\n\tghi.type = emitter_type_title;\n\tghi.str_val = \"ghi\";\n\temitter_table_row(emitter, &row);\n\n\temitter_end(emitter);\n}\n\nstatic const char *table_row_json =\n\"{\\n\"\n\"}\\n\";\n\nstatic const char *table_row_table =\n\"ABC title       DEF title  GHI\\n\"\n\"123                  true  456\\n\"\n\"789                 false 1011\\n\"\n\"\\\"a string\\\"          false  ghi\\n\";\n\nTEST_BEGIN(test_table_row) {\n\tassert_emit_output(&emit_table_row, table_row_json, table_row_table);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_dict,\n\t    test_table_printf,\n\t    test_nested_dict,\n\t    test_types,\n\t    test_modal,\n\t    test_json_arr,\n\t    test_table_row);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/extent_quantize.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_small_extent_size) {\n\tunsigned nbins, i;\n\tsize_t sz, extent_size;\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\n\t/*\n\t * Iterate over all small size classes, get their extent sizes, and\n\t * verify that the quantized size is the same as the extent size.\n\t */\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nbins\", (void *)&nbins, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.slab_size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib failure\");\n\tfor (i = 0; i < nbins; i++) {\n\t\tmib[2] = i;\n\t\tsz = sizeof(size_t);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&extent_size, &sz,\n\t\t    NULL, 0), 0, \"Unexpected mallctlbymib failure\");\n\t\tassert_zu_eq(extent_size,\n\t\t    extent_size_quantize_floor(extent_size),\n\t\t    \"Small extent quantization should be a no-op \"\n\t\t    \"(extent_size=%zu)\", extent_size);\n\t\tassert_zu_eq(extent_size,\n\t\t    extent_size_quantize_ceil(extent_size),\n\t\t    \"Small extent quantization should be a no-op \"\n\t\t    \"(extent_size=%zu)\", extent_size);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_large_extent_size) {\n\tbool cache_oblivious;\n\tunsigned nlextents, i;\n\tsize_t sz, extent_size_prev, ceil_prev;\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\n\t/*\n\t * Iterate over all large size classes, get their extent sizes, and\n\t * verify that the quantized size is the same as the extent size.\n\t */\n\n\tsz = sizeof(bool);\n\tassert_d_eq(mallctl(\"config.cache_oblivious\", (void *)&cache_oblivious,\n\t    &sz, NULL, 0), 0, \"Unexpected mallctl failure\");\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nlextents\", (void *)&nlextents, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arenas.lextent.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib failure\");\n\tfor (i = 0; i < nlextents; i++) {\n\t\tsize_t lextent_size, extent_size, floor, ceil;\n\n\t\tmib[2] = i;\n\t\tsz = sizeof(size_t);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&lextent_size,\n\t\t    &sz, NULL, 0), 0, \"Unexpected mallctlbymib failure\");\n\t\textent_size = cache_oblivious ? lextent_size + PAGE :\n\t\t    lextent_size;\n\t\tfloor = extent_size_quantize_floor(extent_size);\n\t\tceil = extent_size_quantize_ceil(extent_size);\n\n\t\tassert_zu_eq(extent_size, floor,\n\t\t    \"Extent quantization should be a no-op for precise size \"\n\t\t    \"(lextent_size=%zu, extent_size=%zu)\", lextent_size,\n\t\t    extent_size);\n\t\tassert_zu_eq(extent_size, ceil,\n\t\t    \"Extent quantization should be a no-op for precise size \"\n\t\t    \"(lextent_size=%zu, extent_size=%zu)\", lextent_size,\n\t\t    extent_size);\n\n\t\tif (i > 0) {\n\t\t\tassert_zu_eq(extent_size_prev,\n\t\t\t    extent_size_quantize_floor(extent_size - PAGE),\n\t\t\t    \"Floor should be a precise size\");\n\t\t\tif (extent_size_prev < ceil_prev) {\n\t\t\t\tassert_zu_eq(ceil_prev, extent_size,\n\t\t\t\t    \"Ceiling should be a precise size \"\n\t\t\t\t    \"(extent_size_prev=%zu, ceil_prev=%zu, \"\n\t\t\t\t    \"extent_size=%zu)\", extent_size_prev,\n\t\t\t\t    ceil_prev, extent_size);\n\t\t\t}\n\t\t}\n\t\tif (i + 1 < nlextents) {\n\t\t\textent_size_prev = floor;\n\t\t\tceil_prev = extent_size_quantize_ceil(extent_size +\n\t\t\t    PAGE);\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_monotonic) {\n#define SZ_MAX\tZU(4 * 1024 * 1024)\n\tunsigned i;\n\tsize_t floor_prev, ceil_prev;\n\n\tfloor_prev = 0;\n\tceil_prev = 0;\n\tfor (i = 1; i <= SZ_MAX >> LG_PAGE; i++) {\n\t\tsize_t extent_size, floor, ceil;\n\n\t\textent_size = i << LG_PAGE;\n\t\tfloor = extent_size_quantize_floor(extent_size);\n\t\tceil = extent_size_quantize_ceil(extent_size);\n\n\t\tassert_zu_le(floor, extent_size,\n\t\t    \"Floor should be <= (floor=%zu, extent_size=%zu, ceil=%zu)\",\n\t\t    floor, extent_size, ceil);\n\t\tassert_zu_ge(ceil, extent_size,\n\t\t    \"Ceiling should be >= (floor=%zu, extent_size=%zu, \"\n\t\t    \"ceil=%zu)\", floor, extent_size, ceil);\n\n\t\tassert_zu_le(floor_prev, floor, \"Floor should be monotonic \"\n\t\t    \"(floor_prev=%zu, floor=%zu, extent_size=%zu, ceil=%zu)\",\n\t\t    floor_prev, floor, extent_size, ceil);\n\t\tassert_zu_le(ceil_prev, ceil, \"Ceiling should be monotonic \"\n\t\t    \"(floor=%zu, extent_size=%zu, ceil_prev=%zu, ceil=%zu)\",\n\t\t    floor, extent_size, ceil_prev, ceil);\n\n\t\tfloor_prev = floor;\n\t\tceil_prev = ceil;\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_small_extent_size,\n\t    test_large_extent_size,\n\t    test_monotonic);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/fork.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#ifndef _WIN32\n#include <sys/wait.h>\n#endif\n\n#ifndef _WIN32\nstatic void\nwait_for_child_exit(int pid) {\n\tint status;\n\twhile (true) {\n\t\tif (waitpid(pid, &status, 0) == -1) {\n\t\t\ttest_fail(\"Unexpected waitpid() failure.\");\n\t\t}\n\t\tif (WIFSIGNALED(status)) {\n\t\t\ttest_fail(\"Unexpected child termination due to \"\n\t\t\t    \"signal %d\", WTERMSIG(status));\n\t\t\tbreak;\n\t\t}\n\t\tif (WIFEXITED(status)) {\n\t\t\tif (WEXITSTATUS(status) != 0) {\n\t\t\t\ttest_fail(\"Unexpected child exit value %d\",\n\t\t\t\t    WEXITSTATUS(status));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\nTEST_BEGIN(test_fork) {\n#ifndef _WIN32\n\tvoid *p;\n\tpid_t pid;\n\n\t/* Set up a manually managed arena for test. */\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\n\t/* Migrate to the new arena. */\n\tunsigned old_arena_ind;\n\tsz = sizeof(old_arena_ind);\n\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t    (void *)&arena_ind, sizeof(arena_ind)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\tpid = fork();\n\n\tfree(p);\n\n\tp = malloc(64);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\tfree(p);\n\n\tif (pid == -1) {\n\t\t/* Error. */\n\t\ttest_fail(\"Unexpected fork() failure\");\n\t} else if (pid == 0) {\n\t\t/* Child. */\n\t\t_exit(0);\n\t} else {\n\t\twait_for_child_exit(pid);\n\t}\n#else\n\ttest_skip(\"fork(2) is irrelevant to Windows\");\n#endif\n}\nTEST_END\n\n#ifndef _WIN32\nstatic void *\ndo_fork_thd(void *arg) {\n\tmalloc(1);\n\tint pid = fork();\n\tif (pid == -1) {\n\t\t/* Error. */\n\t\ttest_fail(\"Unexpected fork() failure\");\n\t} else if (pid == 0) {\n\t\t/* Child. */\n\t\tchar *args[] = {\"true\", NULL};\n\t\texecvp(args[0], args);\n\t\ttest_fail(\"Exec failed\");\n\t} else {\n\t\t/* Parent */\n\t\twait_for_child_exit(pid);\n\t}\n\treturn NULL;\n}\n#endif\n\n#ifndef _WIN32\nstatic void\ndo_test_fork_multithreaded() {\n\tthd_t child;\n\tthd_create(&child, do_fork_thd, NULL);\n\tdo_fork_thd(NULL);\n\tthd_join(child, NULL);\n}\n#endif\n\nTEST_BEGIN(test_fork_multithreaded) {\n#ifndef _WIN32\n\t/*\n\t * We've seen bugs involving hanging on arenas_lock (though the same\n\t * class of bugs can happen on any mutex).  The bugs are intermittent\n\t * though, so we want to run the test multiple times.  Since we hold the\n\t * arenas lock only early in the process lifetime, we can't just run\n\t * this test in a loop (since, after all the arenas are initialized, we\n\t * won't acquire arenas_lock any further).  We therefore repeat the test\n\t * with multiple processes.\n\t */\n\tfor (int i = 0; i < 100; i++) {\n\t\tint pid = fork();\n\t\tif (pid == -1) {\n\t\t\t/* Error. */\n\t\t\ttest_fail(\"Unexpected fork() failure,\");\n\t\t} else if (pid == 0) {\n\t\t\t/* Child. */\n\t\t\tdo_test_fork_multithreaded();\n\t\t\t_exit(0);\n\t\t} else {\n\t\t\twait_for_child_exit(pid);\n\t\t}\n\t}\n#else\n\ttest_skip(\"fork(2) is irrelevant to Windows\");\n#endif\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_fork,\n\t    test_fork_multithreaded);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/hash.c",
    "content": "/*\n * This file is based on code that is part of SMHasher\n * (https://code.google.com/p/smhasher/), and is subject to the MIT license\n * (http://www.opensource.org/licenses/mit-license.php).  Both email addresses\n * associated with the source code's revision history belong to Austin Appleby,\n * and the revision history ranges from 2010 to 2012.  Therefore the copyright\n * and license are here taken to be:\n *\n * Copyright (c) 2010-2012 Austin Appleby\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"test/jemalloc_test.h\"\n#include \"jemalloc/internal/hash.h\"\n\ntypedef enum {\n\thash_variant_x86_32,\n\thash_variant_x86_128,\n\thash_variant_x64_128\n} hash_variant_t;\n\nstatic int\nhash_variant_bits(hash_variant_t variant) {\n\tswitch (variant) {\n\tcase hash_variant_x86_32: return 32;\n\tcase hash_variant_x86_128: return 128;\n\tcase hash_variant_x64_128: return 128;\n\tdefault: not_reached();\n\t}\n}\n\nstatic const char *\nhash_variant_string(hash_variant_t variant) {\n\tswitch (variant) {\n\tcase hash_variant_x86_32: return \"hash_x86_32\";\n\tcase hash_variant_x86_128: return \"hash_x86_128\";\n\tcase hash_variant_x64_128: return \"hash_x64_128\";\n\tdefault: not_reached();\n\t}\n}\n\n#define KEY_SIZE\t256\nstatic void\nhash_variant_verify_key(hash_variant_t variant, uint8_t *key) {\n\tconst int hashbytes = hash_variant_bits(variant) / 8;\n\tconst int hashes_size = hashbytes * 256;\n\tVARIABLE_ARRAY(uint8_t, hashes, hashes_size);\n\tVARIABLE_ARRAY(uint8_t, final, hashbytes);\n\tunsigned i;\n\tuint32_t computed, expected;\n\n\tmemset(key, 0, KEY_SIZE);\n\tmemset(hashes, 0, hashes_size);\n\tmemset(final, 0, hashbytes);\n\n\t/*\n\t * Hash keys of the form {0}, {0,1}, {0,1,2}, ..., {0,1,...,255} as the\n\t * seed.\n\t */\n\tfor (i = 0; i < 256; i++) {\n\t\tkey[i] = (uint8_t)i;\n\t\tswitch (variant) {\n\t\tcase hash_variant_x86_32: {\n\t\t\tuint32_t out;\n\t\t\tout = hash_x86_32(key, i, 256-i);\n\t\t\tmemcpy(&hashes[i*hashbytes], &out, hashbytes);\n\t\t\tbreak;\n\t\t} case hash_variant_x86_128: {\n\t\t\tuint64_t out[2];\n\t\t\thash_x86_128(key, i, 256-i, out);\n\t\t\tmemcpy(&hashes[i*hashbytes], out, hashbytes);\n\t\t\tbreak;\n\t\t} case hash_variant_x64_128: {\n\t\t\tuint64_t out[2];\n\t\t\thash_x64_128(key, i, 256-i, out);\n\t\t\tmemcpy(&hashes[i*hashbytes], out, hashbytes);\n\t\t\tbreak;\n\t\t} default: not_reached();\n\t\t}\n\t}\n\n\t/* Hash the result array. */\n\tswitch (variant) {\n\tcase hash_variant_x86_32: {\n\t\tuint32_t out = hash_x86_32(hashes, hashes_size, 0);\n\t\tmemcpy(final, &out, sizeof(out));\n\t\tbreak;\n\t} case hash_variant_x86_128: {\n\t\tuint64_t out[2];\n\t\thash_x86_128(hashes, hashes_size, 0, out);\n\t\tmemcpy(final, out, sizeof(out));\n\t\tbreak;\n\t} case hash_variant_x64_128: {\n\t\tuint64_t out[2];\n\t\thash_x64_128(hashes, hashes_size, 0, out);\n\t\tmemcpy(final, out, sizeof(out));\n\t\tbreak;\n\t} default: not_reached();\n\t}\n\n\tcomputed = (final[0] << 0) | (final[1] << 8) | (final[2] << 16) |\n\t    (final[3] << 24);\n\n\tswitch (variant) {\n#ifdef JEMALLOC_BIG_ENDIAN\n\tcase hash_variant_x86_32: expected = 0x6213303eU; break;\n\tcase hash_variant_x86_128: expected = 0x266820caU; break;\n\tcase hash_variant_x64_128: expected = 0xcc622b6fU; break;\n#else\n\tcase hash_variant_x86_32: expected = 0xb0f57ee3U; break;\n\tcase hash_variant_x86_128: expected = 0xb3ece62aU; break;\n\tcase hash_variant_x64_128: expected = 0x6384ba69U; break;\n#endif\n\tdefault: not_reached();\n\t}\n\n\tassert_u32_eq(computed, expected,\n\t    \"Hash mismatch for %s(): expected %#x but got %#x\",\n\t    hash_variant_string(variant), expected, computed);\n}\n\nstatic void\nhash_variant_verify(hash_variant_t variant) {\n#define MAX_ALIGN\t16\n\tuint8_t key[KEY_SIZE + (MAX_ALIGN - 1)];\n\tunsigned i;\n\n\tfor (i = 0; i < MAX_ALIGN; i++) {\n\t\thash_variant_verify_key(variant, &key[i]);\n\t}\n#undef MAX_ALIGN\n}\n#undef KEY_SIZE\n\nTEST_BEGIN(test_hash_x86_32) {\n\thash_variant_verify(hash_variant_x86_32);\n}\nTEST_END\n\nTEST_BEGIN(test_hash_x86_128) {\n\thash_variant_verify(hash_variant_x86_128);\n}\nTEST_END\n\nTEST_BEGIN(test_hash_x64_128) {\n\thash_variant_verify(hash_variant_x64_128);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_hash_x86_32,\n\t    test_hash_x86_128,\n\t    test_hash_x64_128);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/hooks.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic bool hook_called = false;\n\nstatic void\nhook() {\n\thook_called = true;\n}\n\nstatic int\nfunc_to_hook(int arg1, int arg2) {\n\treturn arg1 + arg2;\n}\n\n#define func_to_hook JEMALLOC_HOOK(func_to_hook, hooks_libc_hook)\n\nTEST_BEGIN(unhooked_call) {\n\thooks_libc_hook = NULL;\n\thook_called = false;\n\tassert_d_eq(3, func_to_hook(1, 2), \"Hooking changed return value.\");\n\tassert_false(hook_called, \"Nulling out hook didn't take.\");\n}\nTEST_END\n\nTEST_BEGIN(hooked_call) {\n\thooks_libc_hook = &hook;\n\thook_called = false;\n\tassert_d_eq(3, func_to_hook(1, 2), \"Hooking changed return value.\");\n\tassert_true(hook_called, \"Hook should have executed.\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    unhooked_call,\n\t    hooked_call);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/junk.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\nstatic arena_dalloc_junk_small_t *arena_dalloc_junk_small_orig;\nstatic large_dalloc_junk_t *large_dalloc_junk_orig;\nstatic large_dalloc_maybe_junk_t *large_dalloc_maybe_junk_orig;\nstatic void *watch_for_junking;\nstatic bool saw_junking;\n\nstatic void\nwatch_junking(void *p) {\n\twatch_for_junking = p;\n\tsaw_junking = false;\n}\n\nstatic void\narena_dalloc_junk_small_intercept(void *ptr, const bin_info_t *bin_info) {\n\tsize_t i;\n\n\tarena_dalloc_junk_small_orig(ptr, bin_info);\n\tfor (i = 0; i < bin_info->reg_size; i++) {\n\t\tassert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,\n\t\t    \"Missing junk fill for byte %zu/%zu of deallocated region\",\n\t\t    i, bin_info->reg_size);\n\t}\n\tif (ptr == watch_for_junking) {\n\t\tsaw_junking = true;\n\t}\n}\n\nstatic void\nlarge_dalloc_junk_intercept(void *ptr, size_t usize) {\n\tsize_t i;\n\n\tlarge_dalloc_junk_orig(ptr, usize);\n\tfor (i = 0; i < usize; i++) {\n\t\tassert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,\n\t\t    \"Missing junk fill for byte %zu/%zu of deallocated region\",\n\t\t    i, usize);\n\t}\n\tif (ptr == watch_for_junking) {\n\t\tsaw_junking = true;\n\t}\n}\n\nstatic void\nlarge_dalloc_maybe_junk_intercept(void *ptr, size_t usize) {\n\tlarge_dalloc_maybe_junk_orig(ptr, usize);\n\tif (ptr == watch_for_junking) {\n\t\tsaw_junking = true;\n\t}\n}\n\nstatic void\ntest_junk(size_t sz_min, size_t sz_max) {\n\tuint8_t *s;\n\tsize_t sz_prev, sz, i;\n\n\tif (opt_junk_free) {\n\t\tarena_dalloc_junk_small_orig = arena_dalloc_junk_small;\n\t\tarena_dalloc_junk_small = arena_dalloc_junk_small_intercept;\n\t\tlarge_dalloc_junk_orig = large_dalloc_junk;\n\t\tlarge_dalloc_junk = large_dalloc_junk_intercept;\n\t\tlarge_dalloc_maybe_junk_orig = large_dalloc_maybe_junk;\n\t\tlarge_dalloc_maybe_junk = large_dalloc_maybe_junk_intercept;\n\t}\n\n\tsz_prev = 0;\n\ts = (uint8_t *)mallocx(sz_min, 0);\n\tassert_ptr_not_null((void *)s, \"Unexpected mallocx() failure\");\n\n\tfor (sz = sallocx(s, 0); sz <= sz_max;\n\t    sz_prev = sz, sz = sallocx(s, 0)) {\n\t\tif (sz_prev > 0) {\n\t\t\tassert_u_eq(s[0], 'a',\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    ZU(0), sz_prev);\n\t\t\tassert_u_eq(s[sz_prev-1], 'a',\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    sz_prev-1, sz_prev);\n\t\t}\n\n\t\tfor (i = sz_prev; i < sz; i++) {\n\t\t\tif (opt_junk_alloc) {\n\t\t\t\tassert_u_eq(s[i], JEMALLOC_ALLOC_JUNK,\n\t\t\t\t    \"Newly allocated byte %zu/%zu isn't \"\n\t\t\t\t    \"junk-filled\", i, sz);\n\t\t\t}\n\t\t\ts[i] = 'a';\n\t\t}\n\n\t\tif (xallocx(s, sz+1, 0, 0) == sz) {\n\t\t\tuint8_t *t;\n\t\t\twatch_junking(s);\n\t\t\tt = (uint8_t *)rallocx(s, sz+1, 0);\n\t\t\tassert_ptr_not_null((void *)t,\n\t\t\t    \"Unexpected rallocx() failure\");\n\t\t\tassert_zu_ge(sallocx(t, 0), sz+1,\n\t\t\t    \"Unexpectedly small rallocx() result\");\n\t\t\tif (!background_thread_enabled()) {\n\t\t\t\tassert_ptr_ne(s, t,\n\t\t\t\t    \"Unexpected in-place rallocx()\");\n\t\t\t\tassert_true(!opt_junk_free || saw_junking,\n\t\t\t\t    \"Expected region of size %zu to be \"\n\t\t\t\t    \"junk-filled\", sz);\n\t\t\t}\n\t\t\ts = t;\n\t\t}\n\t}\n\n\twatch_junking(s);\n\tdallocx(s, 0);\n\tassert_true(!opt_junk_free || saw_junking,\n\t    \"Expected region of size %zu to be junk-filled\", sz);\n\n\tif (opt_junk_free) {\n\t\tarena_dalloc_junk_small = arena_dalloc_junk_small_orig;\n\t\tlarge_dalloc_junk = large_dalloc_junk_orig;\n\t\tlarge_dalloc_maybe_junk = large_dalloc_maybe_junk_orig;\n\t}\n}\n\nTEST_BEGIN(test_junk_small) {\n\ttest_skip_if(!config_fill);\n\ttest_junk(1, SMALL_MAXCLASS-1);\n}\nTEST_END\n\nTEST_BEGIN(test_junk_large) {\n\ttest_skip_if(!config_fill);\n\ttest_junk(SMALL_MAXCLASS+1, (1U << (LG_LARGE_MINCLASS+1)));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_junk_small,\n\t    test_junk_large);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/junk.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,zero:false,junk:true\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/junk_alloc.c",
    "content": "#include \"junk.c\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/junk_alloc.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,zero:false,junk:alloc\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/junk_free.c",
    "content": "#include \"junk.c\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/junk_free.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,zero:false,junk:free\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/log.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/log.h\"\n\nstatic void\nexpect_no_logging(const char *names) {\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\tlog_var_t log_l2 = LOG_VAR_INIT(\"l2\");\n\tlog_var_t log_l2_a = LOG_VAR_INIT(\"l2.a\");\n\n\tstrcpy(log_var_names, names);\n\n\tint count = 0;\n\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1)\n\n\t\tlog_do_begin(log_l2)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2)\n\n\t\tlog_do_begin(log_l2_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_a)\n\t}\n\tassert_d_eq(count, 0, \"Disabled logging not ignored!\");\n}\n\nTEST_BEGIN(test_log_disabled) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\texpect_no_logging(\"\");\n\texpect_no_logging(\"abc\");\n\texpect_no_logging(\"a.b.c\");\n\texpect_no_logging(\"l12\");\n\texpect_no_logging(\"l123|a456|b789\");\n\texpect_no_logging(\"|||\");\n}\nTEST_END\n\nTEST_BEGIN(test_log_enabled_direct) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\tlog_var_t log_l1_a = LOG_VAR_INIT(\"l1.a\");\n\tlog_var_t log_l2 = LOG_VAR_INIT(\"l2\");\n\n\tint count;\n\n\tcount = 0;\n\tstrcpy(log_var_names, \"l1\");\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1)\n\t}\n\tassert_d_eq(count, 10, \"Mis-logged!\");\n\n\tcount = 0;\n\tstrcpy(log_var_names, \"l1.a\");\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1_a)\n\t}\n\tassert_d_eq(count, 10, \"Mis-logged!\");\n\n\tcount = 0;\n\tstrcpy(log_var_names, \"l1.a|abc|l2|def\");\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1_a)\n\n\t\tlog_do_begin(log_l2)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2)\n\t}\n\tassert_d_eq(count, 20, \"Mis-logged!\");\n}\nTEST_END\n\nTEST_BEGIN(test_log_enabled_indirect) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\tstrcpy(log_var_names, \"l0|l1|abc|l2.b|def\");\n\n\t/* On. */\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\t/* Off. */\n\tlog_var_t log_l1a = LOG_VAR_INIT(\"l1a\");\n\t/* On. */\n\tlog_var_t log_l1_a = LOG_VAR_INIT(\"l1.a\");\n\t/* Off. */\n\tlog_var_t log_l2_a = LOG_VAR_INIT(\"l2.a\");\n\t/* On. */\n\tlog_var_t log_l2_b_a = LOG_VAR_INIT(\"l2.b.a\");\n\t/* On. */\n\tlog_var_t log_l2_b_b = LOG_VAR_INIT(\"l2.b.b\");\n\n\t/* 4 are on total, so should sum to 40. */\n\tint count = 0;\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1)\n\n\t\tlog_do_begin(log_l1a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1a)\n\n\t\tlog_do_begin(log_l1_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l1_a)\n\n\t\tlog_do_begin(log_l2_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_a)\n\n\t\tlog_do_begin(log_l2_b_a)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_b_a)\n\n\t\tlog_do_begin(log_l2_b_b)\n\t\t\tcount++;\n\t\tlog_do_end(log_l2_b_b)\n\t}\n\n\tassert_d_eq(count, 40, \"Mis-logged!\");\n}\nTEST_END\n\nTEST_BEGIN(test_log_enabled_global) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, true, ATOMIC_RELAXED);\n\tstrcpy(log_var_names, \"abc|.|def\");\n\n\tlog_var_t log_l1 = LOG_VAR_INIT(\"l1\");\n\tlog_var_t log_l2_a_a = LOG_VAR_INIT(\"l2.a.a\");\n\n\tint count = 0;\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(log_l1)\n\t\t    count++;\n\t\tlog_do_end(log_l1)\n\n\t\tlog_do_begin(log_l2_a_a)\n\t\t    count++;\n\t\tlog_do_end(log_l2_a_a)\n\t}\n\tassert_d_eq(count, 20, \"Mis-logged!\");\n}\nTEST_END\n\nTEST_BEGIN(test_logs_if_no_init) {\n\ttest_skip_if(!config_log);\n\tatomic_store_b(&log_init_done, false, ATOMIC_RELAXED);\n\n\tlog_var_t l = LOG_VAR_INIT(\"definitely.not.enabled\");\n\n\tint count = 0;\n\tfor (int i = 0; i < 10; i++) {\n\t\tlog_do_begin(l)\n\t\t\tcount++;\n\t\tlog_do_end(l)\n\t}\n\tassert_d_eq(count, 0, \"Logging shouldn't happen if not initialized.\");\n}\nTEST_END\n\n/*\n * This really just checks to make sure that this usage compiles; we don't have\n * any test code to run.\n */\nTEST_BEGIN(test_log_only_format_string) {\n\tif (false) {\n\t\tLOG(\"log_str\", \"No arguments follow this format string.\");\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_log_disabled,\n\t    test_log_enabled_direct,\n\t    test_log_enabled_indirect,\n\t    test_log_enabled_global,\n\t    test_logs_if_no_init,\n\t    test_log_only_format_string);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/mallctl.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\nTEST_BEGIN(test_mallctl_errors) {\n\tuint64_t epoch;\n\tsize_t sz;\n\n\tassert_d_eq(mallctl(\"no_such_name\", NULL, NULL, NULL, 0), ENOENT,\n\t    \"mallctl() should return ENOENT for non-existent names\");\n\n\tassert_d_eq(mallctl(\"version\", NULL, NULL, \"0.0.0\", strlen(\"0.0.0\")),\n\t    EPERM, \"mallctl() should return EPERM on attempt to write \"\n\t    \"read-only value\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)-1), EINVAL,\n\t    \"mallctl() should return EINVAL for input size mismatch\");\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)+1), EINVAL,\n\t    \"mallctl() should return EINVAL for input size mismatch\");\n\n\tsz = sizeof(epoch)-1;\n\tassert_d_eq(mallctl(\"epoch\", (void *)&epoch, &sz, NULL, 0), EINVAL,\n\t    \"mallctl() should return EINVAL for output size mismatch\");\n\tsz = sizeof(epoch)+1;\n\tassert_d_eq(mallctl(\"epoch\", (void *)&epoch, &sz, NULL, 0), EINVAL,\n\t    \"mallctl() should return EINVAL for output size mismatch\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctlnametomib_errors) {\n\tsize_t mib[1];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"no_such_name\", mib, &miblen), ENOENT,\n\t    \"mallctlnametomib() should return ENOENT for non-existent names\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctlbymib_errors) {\n\tuint64_t epoch;\n\tsize_t sz;\n\tsize_t mib[1];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"version\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, \"0.0.0\",\n\t    strlen(\"0.0.0\")), EPERM, \"mallctl() should return EPERM on \"\n\t    \"attempt to write read-only value\");\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"epoch\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)-1), EINVAL,\n\t    \"mallctlbymib() should return EINVAL for input size mismatch\");\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)+1), EINVAL,\n\t    \"mallctlbymib() should return EINVAL for input size mismatch\");\n\n\tsz = sizeof(epoch)-1;\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),\n\t    EINVAL,\n\t    \"mallctlbymib() should return EINVAL for output size mismatch\");\n\tsz = sizeof(epoch)+1;\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),\n\t    EINVAL,\n\t    \"mallctlbymib() should return EINVAL for output size mismatch\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctl_read_write) {\n\tuint64_t old_epoch, new_epoch;\n\tsize_t sz = sizeof(old_epoch);\n\n\t/* Blind. */\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n\n\t/* Read. */\n\tassert_d_eq(mallctl(\"epoch\", (void *)&old_epoch, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n\n\t/* Write. */\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&new_epoch,\n\t    sizeof(new_epoch)), 0, \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n\n\t/* Read+write. */\n\tassert_d_eq(mallctl(\"epoch\", (void *)&old_epoch, &sz,\n\t    (void *)&new_epoch, sizeof(new_epoch)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(sz, sizeof(old_epoch), \"Unexpected output size\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctlnametomib_short_mib) {\n\tsize_t mib[4];\n\tsize_t miblen;\n\n\tmiblen = 3;\n\tmib[3] = 42;\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.nregs\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tassert_zu_eq(miblen, 3, \"Unexpected mib output length\");\n\tassert_zu_eq(mib[3], 42,\n\t    \"mallctlnametomib() wrote past the end of the input mib\");\n}\nTEST_END\n\nTEST_BEGIN(test_mallctl_config) {\n#define TEST_MALLCTL_CONFIG(config, t) do {\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(oldval);\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"config.\"#config, (void *)&oldval, &sz,\t\\\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n\tassert_b_eq(oldval, config_##config, \"Incorrect config value\");\t\\\n\tassert_zu_eq(sz, sizeof(oldval), \"Unexpected output size\");\t\\\n} while (0)\n\n\tTEST_MALLCTL_CONFIG(cache_oblivious, bool);\n\tTEST_MALLCTL_CONFIG(debug, bool);\n\tTEST_MALLCTL_CONFIG(fill, bool);\n\tTEST_MALLCTL_CONFIG(lazy_lock, bool);\n\tTEST_MALLCTL_CONFIG(malloc_conf, const char *);\n\tTEST_MALLCTL_CONFIG(prof, bool);\n\tTEST_MALLCTL_CONFIG(prof_libgcc, bool);\n\tTEST_MALLCTL_CONFIG(prof_libunwind, bool);\n\tTEST_MALLCTL_CONFIG(stats, bool);\n\tTEST_MALLCTL_CONFIG(utrace, bool);\n\tTEST_MALLCTL_CONFIG(xmalloc, bool);\n\n#undef TEST_MALLCTL_CONFIG\n}\nTEST_END\n\nTEST_BEGIN(test_mallctl_opt) {\n\tbool config_always = true;\n\n#define TEST_MALLCTL_OPT(t, opt, config) do {\t\t\t\t\\\n\tt oldval;\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(oldval);\t\t\t\t\t\\\n\tint expected = config_##config ? 0 : ENOENT;\t\t\t\\\n\tint result = mallctl(\"opt.\"#opt, (void *)&oldval, &sz, NULL,\t\\\n\t    0);\t\t\t\t\t\t\t\t\\\n\tassert_d_eq(result, expected,\t\t\t\t\t\\\n\t    \"Unexpected mallctl() result for opt.\"#opt);\t\t\\\n\tassert_zu_eq(sz, sizeof(oldval), \"Unexpected output size\");\t\\\n} while (0)\n\n\tTEST_MALLCTL_OPT(bool, abort, always);\n\tTEST_MALLCTL_OPT(bool, abort_conf, always);\n\tTEST_MALLCTL_OPT(const char *, metadata_thp, always);\n\tTEST_MALLCTL_OPT(bool, retain, always);\n\tTEST_MALLCTL_OPT(const char *, dss, always);\n\tTEST_MALLCTL_OPT(unsigned, narenas, always);\n\tTEST_MALLCTL_OPT(const char *, percpu_arena, always);\n\tTEST_MALLCTL_OPT(bool, background_thread, always);\n\tTEST_MALLCTL_OPT(ssize_t, dirty_decay_ms, always);\n\tTEST_MALLCTL_OPT(ssize_t, muzzy_decay_ms, always);\n\tTEST_MALLCTL_OPT(bool, stats_print, always);\n\tTEST_MALLCTL_OPT(const char *, junk, fill);\n\tTEST_MALLCTL_OPT(bool, zero, fill);\n\tTEST_MALLCTL_OPT(bool, utrace, utrace);\n\tTEST_MALLCTL_OPT(bool, xmalloc, xmalloc);\n\tTEST_MALLCTL_OPT(bool, tcache, always);\n\tTEST_MALLCTL_OPT(size_t, lg_extent_max_active_fit, always);\n\tTEST_MALLCTL_OPT(size_t, lg_tcache_max, always);\n\tTEST_MALLCTL_OPT(const char *, thp, always);\n\tTEST_MALLCTL_OPT(bool, prof, prof);\n\tTEST_MALLCTL_OPT(const char *, prof_prefix, prof);\n\tTEST_MALLCTL_OPT(bool, prof_active, prof);\n\tTEST_MALLCTL_OPT(ssize_t, lg_prof_sample, prof);\n\tTEST_MALLCTL_OPT(bool, prof_accum, prof);\n\tTEST_MALLCTL_OPT(ssize_t, lg_prof_interval, prof);\n\tTEST_MALLCTL_OPT(bool, prof_gdump, prof);\n\tTEST_MALLCTL_OPT(bool, prof_final, prof);\n\tTEST_MALLCTL_OPT(bool, prof_leak, prof);\n\n#undef TEST_MALLCTL_OPT\n}\nTEST_END\n\nTEST_BEGIN(test_manpage_example) {\n\tunsigned nbins, i;\n\tsize_t mib[4];\n\tsize_t len, miblen;\n\n\tlen = sizeof(nbins);\n\tassert_d_eq(mallctl(\"arenas.nbins\", (void *)&nbins, &len, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tmiblen = 4;\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tfor (i = 0; i < nbins; i++) {\n\t\tsize_t bin_size;\n\n\t\tmib[2] = i;\n\t\tlen = sizeof(bin_size);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&bin_size, &len,\n\t\t    NULL, 0), 0, \"Unexpected mallctlbymib() failure\");\n\t\t/* Do something with bin_size... */\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_tcache_none) {\n\ttest_skip_if(!opt_tcache);\n\n\t/* Allocate p and q. */\n\tvoid *p0 = mallocx(42, 0);\n\tassert_ptr_not_null(p0, \"Unexpected mallocx() failure\");\n\tvoid *q = mallocx(42, 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\n\t/* Deallocate p and q, but bypass the tcache for q. */\n\tdallocx(p0, 0);\n\tdallocx(q, MALLOCX_TCACHE_NONE);\n\n\t/* Make sure that tcache-based allocation returns p, not q. */\n\tvoid *p1 = mallocx(42, 0);\n\tassert_ptr_not_null(p1, \"Unexpected mallocx() failure\");\n\tassert_ptr_eq(p0, p1, \"Expected tcache to allocate cached region\");\n\n\t/* Clean up. */\n\tdallocx(p1, MALLOCX_TCACHE_NONE);\n}\nTEST_END\n\nTEST_BEGIN(test_tcache) {\n#define NTCACHES\t10\n\tunsigned tis[NTCACHES];\n\tvoid *ps[NTCACHES];\n\tvoid *qs[NTCACHES];\n\tunsigned i;\n\tsize_t sz, psz, qsz;\n\n\tpsz = 42;\n\tqsz = nallocx(psz, 0) + 1;\n\n\t/* Create tcaches. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tsz = sizeof(unsigned);\n\t\tassert_d_eq(mallctl(\"tcache.create\", (void *)&tis[i], &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n\n\t/* Exercise tcache ID recycling. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.destroy\", NULL, NULL,\n\t\t    (void *)&tis[i], sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tsz = sizeof(unsigned);\n\t\tassert_d_eq(mallctl(\"tcache.create\", (void *)&tis[i], &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n\n\t/* Flush empty tcaches. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.flush\", NULL, NULL, (void *)&tis[i],\n\t\t    sizeof(unsigned)), 0, \"Unexpected mallctl() failure, i=%u\",\n\t\t    i);\n\t}\n\n\t/* Cache some allocations. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(ps[i], \"Unexpected mallocx() failure, i=%u\",\n\t\t    i);\n\t\tdallocx(ps[i], MALLOCX_TCACHE(tis[i]));\n\n\t\tqs[i] = mallocx(qsz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(qs[i], \"Unexpected mallocx() failure, i=%u\",\n\t\t    i);\n\t\tdallocx(qs[i], MALLOCX_TCACHE(tis[i]));\n\t}\n\n\t/* Verify that tcaches allocate cached regions. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tvoid *p0 = ps[i];\n\t\tps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(ps[i], \"Unexpected mallocx() failure, i=%u\",\n\t\t    i);\n\t\tassert_ptr_eq(ps[i], p0,\n\t\t    \"Expected mallocx() to allocate cached region, i=%u\", i);\n\t}\n\n\t/* Verify that reallocation uses cached regions. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tvoid *q0 = qs[i];\n\t\tqs[i] = rallocx(ps[i], qsz, MALLOCX_TCACHE(tis[i]));\n\t\tassert_ptr_not_null(qs[i], \"Unexpected rallocx() failure, i=%u\",\n\t\t    i);\n\t\tassert_ptr_eq(qs[i], q0,\n\t\t    \"Expected rallocx() to allocate cached region, i=%u\", i);\n\t\t/* Avoid undefined behavior in case of test failure. */\n\t\tif (qs[i] == NULL) {\n\t\t\tqs[i] = ps[i];\n\t\t}\n\t}\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tdallocx(qs[i], MALLOCX_TCACHE(tis[i]));\n\t}\n\n\t/* Flush some non-empty tcaches. */\n\tfor (i = 0; i < NTCACHES/2; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.flush\", NULL, NULL, (void *)&tis[i],\n\t\t    sizeof(unsigned)), 0, \"Unexpected mallctl() failure, i=%u\",\n\t\t    i);\n\t}\n\n\t/* Destroy tcaches. */\n\tfor (i = 0; i < NTCACHES; i++) {\n\t\tassert_d_eq(mallctl(\"tcache.destroy\", NULL, NULL,\n\t\t    (void *)&tis[i], sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure, i=%u\", i);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_thread_arena) {\n\tunsigned old_arena_ind, new_arena_ind, narenas;\n\n\tconst char *opa;\n\tsize_t sz = sizeof(opa);\n\tassert_d_eq(mallctl(\"opt.percpu_arena\", (void *)&opa, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_u_eq(narenas, opt_narenas, \"Number of arenas incorrect\");\n\n\tif (strcmp(opa, \"disabled\") == 0) {\n\t\tnew_arena_ind = narenas - 1;\n\t\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t\t    (void *)&new_arena_ind, sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure\");\n\t\tnew_arena_ind = 0;\n\t\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t\t    (void *)&new_arena_ind, sizeof(unsigned)), 0,\n\t\t    \"Unexpected mallctl() failure\");\n\t} else {\n\t\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\t\tnew_arena_ind = percpu_arena_ind_limit(opt_percpu_arena) - 1;\n\t\tif (old_arena_ind != new_arena_ind) {\n\t\t\tassert_d_eq(mallctl(\"thread.arena\",\n\t\t\t    (void *)&old_arena_ind, &sz, (void *)&new_arena_ind,\n\t\t\t    sizeof(unsigned)), EPERM, \"thread.arena ctl \"\n\t\t\t    \"should not be allowed with percpu arena\");\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_initialized) {\n\tunsigned narenas, i;\n\tsize_t sz;\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\tbool initialized;\n\n\tsz = sizeof(narenas);\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.initialized\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tfor (i = 0; i < narenas; i++) {\n\t\tmib[1] = i;\n\t\tsz = sizeof(initialized);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctl() failure\");\n\t}\n\n\tmib[1] = MALLCTL_ARENAS_ALL;\n\tsz = sizeof(initialized);\n\tassert_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_true(initialized,\n\t    \"Merged arena statistics should always be initialized\");\n\n\t/* Equivalent to the above but using mallctl() directly. */\n\tsz = sizeof(initialized);\n\tassert_d_eq(mallctl(\n\t    \"arena.\" STRINGIFY(MALLCTL_ARENAS_ALL) \".initialized\",\n\t    (void *)&initialized, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_true(initialized,\n\t    \"Merged arena statistics should always be initialized\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_dirty_decay_ms) {\n\tssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\",\n\t    (void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tdirty_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tdirty_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tfor (prev_dirty_decay_ms = dirty_decay_ms, dirty_decay_ms = -1;\n\t    dirty_decay_ms < 20; prev_dirty_decay_ms = dirty_decay_ms,\n\t    dirty_decay_ms++) {\n\t\tssize_t old_dirty_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arena.0.dirty_decay_ms\",\n\t\t    (void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,\n\t\t    \"Unexpected old arena.0.dirty_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_muzzy_decay_ms) {\n\tssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\",\n\t    (void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tmuzzy_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tmuzzy_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tfor (prev_muzzy_decay_ms = muzzy_decay_ms, muzzy_decay_ms = -1;\n\t    muzzy_decay_ms < 20; prev_muzzy_decay_ms = muzzy_decay_ms,\n\t    muzzy_decay_ms++) {\n\t\tssize_t old_muzzy_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arena.0.muzzy_decay_ms\",\n\t\t    (void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,\n\t\t    \"Unexpected old arena.0.muzzy_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_purge) {\n\tunsigned narenas;\n\tsize_t sz = sizeof(unsigned);\n\tsize_t mib[3];\n\tsize_t miblen = 3;\n\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlnametomib(\"arena.0.purge\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = narenas;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\tmib[1] = MALLCTL_ARENAS_ALL;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_decay) {\n\tunsigned narenas;\n\tsize_t sz = sizeof(unsigned);\n\tsize_t mib[3];\n\tsize_t miblen = 3;\n\n\tassert_d_eq(mallctl(\"arena.0.decay\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlnametomib(\"arena.0.decay\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = narenas;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n\n\tmib[1] = MALLCTL_ARENAS_ALL;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_dss) {\n\tconst char *dss_prec_old, *dss_prec_new;\n\tsize_t sz = sizeof(dss_prec_old);\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.dss\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() error\");\n\n\tdss_prec_new = \"disabled\";\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,\n\t    (void *)&dss_prec_new, sizeof(dss_prec_new)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected default for dss precedence\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,\n\t    (void *)&dss_prec_old, sizeof(dss_prec_old)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected value for dss precedence\");\n\n\tmib[1] = narenas_total_get();\n\tdss_prec_new = \"disabled\";\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,\n\t    (void *)&dss_prec_new, sizeof(dss_prec_new)), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected default for dss precedence\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,\n\t    (void *)&dss_prec_old, sizeof(dss_prec_new)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\tassert_str_ne(dss_prec_old, \"primary\",\n\t    \"Unexpected value for dss precedence\");\n}\nTEST_END\n\nTEST_BEGIN(test_arena_i_retain_grow_limit) {\n\tsize_t old_limit, new_limit, default_limit;\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tbool retain_enabled;\n\tsize_t sz = sizeof(retain_enabled);\n\tassert_d_eq(mallctl(\"opt.retain\", &retain_enabled, &sz, NULL, 0),\n\t    0, \"Unexpected mallctl() failure\");\n\ttest_skip_if(!retain_enabled);\n\n\tsz = sizeof(default_limit);\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.retain_grow_limit\", mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib() error\");\n\n\tassert_d_eq(mallctlbymib(mib, miblen, &default_limit, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(default_limit, sz_pind2sz(EXTENT_GROW_MAX_PIND),\n\t    \"Unexpected default for retain_grow_limit\");\n\n\tnew_limit = PAGE - 1;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,\n\t    sizeof(new_limit)), EFAULT, \"Unexpected mallctl() success\");\n\n\tnew_limit = PAGE + 1;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,\n\t    sizeof(new_limit)), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(old_limit, PAGE,\n\t    \"Unexpected value for retain_grow_limit\");\n\n\t/* Expect grow less than psize class 10. */\n\tnew_limit = sz_pind2sz(10) - 1;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,\n\t    sizeof(new_limit)), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_zu_eq(old_limit, sz_pind2sz(9),\n\t    \"Unexpected value for retain_grow_limit\");\n\n\t/* Restore to default. */\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &default_limit,\n\t    sizeof(default_limit)), 0, \"Unexpected mallctl() failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_dirty_decay_ms) {\n\tssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\",\n\t    (void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tdirty_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tdirty_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\", NULL, NULL,\n\t    (void *)&dirty_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Expected mallctl() failure\");\n\n\tfor (prev_dirty_decay_ms = dirty_decay_ms, dirty_decay_ms = -1;\n\t    dirty_decay_ms < 20; prev_dirty_decay_ms = dirty_decay_ms,\n\t    dirty_decay_ms++) {\n\t\tssize_t old_dirty_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arenas.dirty_decay_ms\",\n\t\t    (void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,\n\t\t    \"Unexpected old arenas.dirty_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_muzzy_decay_ms) {\n\tssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;\n\tsize_t sz = sizeof(ssize_t);\n\n\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\",\n\t    (void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tmuzzy_decay_ms = -2;\n\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,\n\t    \"Unexpected mallctl() success\");\n\n\tmuzzy_decay_ms = 0x7fffffff;\n\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\", NULL, NULL,\n\t    (void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,\n\t    \"Expected mallctl() failure\");\n\n\tfor (prev_muzzy_decay_ms = muzzy_decay_ms, muzzy_decay_ms = -1;\n\t    muzzy_decay_ms < 20; prev_muzzy_decay_ms = muzzy_decay_ms,\n\t    muzzy_decay_ms++) {\n\t\tssize_t old_muzzy_decay_ms;\n\n\t\tassert_d_eq(mallctl(\"arenas.muzzy_decay_ms\",\n\t\t    (void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,\n\t\t    sizeof(ssize_t)), 0, \"Unexpected mallctl() failure\");\n\t\tassert_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,\n\t\t    \"Unexpected old arenas.muzzy_decay_ms\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_constants) {\n#define TEST_ARENAS_CONSTANT(t, name, expected) do {\t\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"arenas.\"#name, (void *)&name, &sz, NULL,\t\\\n\t    0), 0, \"Unexpected mallctl() failure\");\t\t\t\\\n\tassert_zu_eq(name, expected, \"Incorrect \"#name\" size\");\t\t\\\n} while (0)\n\n\tTEST_ARENAS_CONSTANT(size_t, quantum, QUANTUM);\n\tTEST_ARENAS_CONSTANT(size_t, page, PAGE);\n\tTEST_ARENAS_CONSTANT(unsigned, nbins, NBINS);\n\tTEST_ARENAS_CONSTANT(unsigned, nlextents, NSIZES - NBINS);\n\n#undef TEST_ARENAS_CONSTANT\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_bin_constants) {\n#define TEST_ARENAS_BIN_CONSTANT(t, name, expected) do {\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"arenas.bin.0.\"#name, (void *)&name, &sz,\t\\\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n\tassert_zu_eq(name, expected, \"Incorrect \"#name\" size\");\t\t\\\n} while (0)\n\n\tTEST_ARENAS_BIN_CONSTANT(size_t, size, bin_infos[0].reg_size);\n\tTEST_ARENAS_BIN_CONSTANT(uint32_t, nregs, bin_infos[0].nregs);\n\tTEST_ARENAS_BIN_CONSTANT(size_t, slab_size,\n\t    bin_infos[0].slab_size);\n\n#undef TEST_ARENAS_BIN_CONSTANT\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_lextent_constants) {\n#define TEST_ARENAS_LEXTENT_CONSTANT(t, name, expected) do {\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"arenas.lextent.0.\"#name, (void *)&name,\t\\\n\t    &sz, NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n\tassert_zu_eq(name, expected, \"Incorrect \"#name\" size\");\t\t\\\n} while (0)\n\n\tTEST_ARENAS_LEXTENT_CONSTANT(size_t, size, LARGE_MINCLASS);\n\n#undef TEST_ARENAS_LEXTENT_CONSTANT\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_create) {\n\tunsigned narenas_before, arena, narenas_after;\n\tsize_t sz = sizeof(unsigned);\n\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas_before, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tassert_d_eq(mallctl(\"arenas.narenas\", (void *)&narenas_after, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\n\tassert_u_eq(narenas_before+1, narenas_after,\n\t    \"Unexpected number of arenas before versus after extension\");\n\tassert_u_eq(arena, narenas_after-1, \"Unexpected arena index\");\n}\nTEST_END\n\nTEST_BEGIN(test_arenas_lookup) {\n\tunsigned arena, arena1;\n\tvoid *ptr;\n\tsize_t sz = sizeof(unsigned);\n\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\tptr = mallocx(42, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE);\n\tassert_ptr_not_null(ptr, \"Unexpected mallocx() failure\");\n\tassert_d_eq(mallctl(\"arenas.lookup\", &arena1, &sz, &ptr, sizeof(ptr)),\n\t    0, \"Unexpected mallctl() failure\");\n\tassert_u_eq(arena, arena1, \"Unexpected arena index\");\n\tdallocx(ptr, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas) {\n#define TEST_STATS_ARENAS(t, name) do {\t\t\t\t\t\\\n\tt name;\t\t\t\t\t\t\t\t\\\n\tsize_t sz = sizeof(t);\t\t\t\t\t\t\\\n\tassert_d_eq(mallctl(\"stats.arenas.0.\"#name, (void *)&name, &sz,\t\\\n\t    NULL, 0), 0, \"Unexpected mallctl() failure\");\t\t\\\n} while (0)\n\n\tTEST_STATS_ARENAS(unsigned, nthreads);\n\tTEST_STATS_ARENAS(const char *, dss);\n\tTEST_STATS_ARENAS(ssize_t, dirty_decay_ms);\n\tTEST_STATS_ARENAS(ssize_t, muzzy_decay_ms);\n\tTEST_STATS_ARENAS(size_t, pactive);\n\tTEST_STATS_ARENAS(size_t, pdirty);\n\n#undef TEST_STATS_ARENAS\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_mallctl_errors,\n\t    test_mallctlnametomib_errors,\n\t    test_mallctlbymib_errors,\n\t    test_mallctl_read_write,\n\t    test_mallctlnametomib_short_mib,\n\t    test_mallctl_config,\n\t    test_mallctl_opt,\n\t    test_manpage_example,\n\t    test_tcache_none,\n\t    test_tcache,\n\t    test_thread_arena,\n\t    test_arena_i_initialized,\n\t    test_arena_i_dirty_decay_ms,\n\t    test_arena_i_muzzy_decay_ms,\n\t    test_arena_i_purge,\n\t    test_arena_i_decay,\n\t    test_arena_i_dss,\n\t    test_arena_i_retain_grow_limit,\n\t    test_arenas_dirty_decay_ms,\n\t    test_arenas_muzzy_decay_ms,\n\t    test_arenas_constants,\n\t    test_arenas_bin_constants,\n\t    test_arenas_lextent_constants,\n\t    test_arenas_create,\n\t    test_arenas_lookup,\n\t    test_stats_arenas);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/malloc_io.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_malloc_strtoumax_no_endptr) {\n\tint err;\n\n\tset_errno(0);\n\tassert_ju_eq(malloc_strtoumax(\"0\", NULL, 0), 0, \"Unexpected result\");\n\terr = get_errno();\n\tassert_d_eq(err, 0, \"Unexpected failure\");\n}\nTEST_END\n\nTEST_BEGIN(test_malloc_strtoumax) {\n\tstruct test_s {\n\t\tconst char *input;\n\t\tconst char *expected_remainder;\n\t\tint base;\n\t\tint expected_errno;\n\t\tconst char *expected_errno_name;\n\t\tuintmax_t expected_x;\n\t};\n#define ERR(e)\t\te, #e\n#define KUMAX(x)\t((uintmax_t)x##ULL)\n#define KSMAX(x)\t((uintmax_t)(intmax_t)x##LL)\n\tstruct test_s tests[] = {\n\t\t{\"0\",\t\t\"0\",\t-1,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"0\",\t\t\"0\",\t1,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"0\",\t\t\"0\",\t37,\tERR(EINVAL),\tUINTMAX_MAX},\n\n\t\t{\"\",\t\t\"\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"+\",\t\t\"+\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"++3\",\t\t\"++3\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\t\t{\"-\",\t\t\"-\",\t0,\tERR(EINVAL),\tUINTMAX_MAX},\n\n\t\t{\"42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"+42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"-42\",\t\t\"\",\t0,\tERR(0),\t\tKSMAX(-42)},\n\t\t{\"042\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(042)},\n\t\t{\"+042\",\t\"\",\t0,\tERR(0),\t\tKUMAX(042)},\n\t\t{\"-042\",\t\"\",\t0,\tERR(0),\t\tKSMAX(-042)},\n\t\t{\"0x42\",\t\"\",\t0,\tERR(0),\t\tKUMAX(0x42)},\n\t\t{\"+0x42\",\t\"\",\t0,\tERR(0),\t\tKUMAX(0x42)},\n\t\t{\"-0x42\",\t\"\",\t0,\tERR(0),\t\tKSMAX(-0x42)},\n\n\t\t{\"0\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"1\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(1)},\n\n\t\t{\"42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\" 42\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"42 \",\t\t\" \",\t0,\tERR(0),\t\tKUMAX(42)},\n\t\t{\"0x\",\t\t\"x\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"42x\",\t\t\"x\",\t0,\tERR(0),\t\tKUMAX(42)},\n\n\t\t{\"07\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(7)},\n\t\t{\"010\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(8)},\n\t\t{\"08\",\t\t\"8\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0_\",\t\t\"_\",\t0,\tERR(0),\t\tKUMAX(0)},\n\n\t\t{\"0x\",\t\t\"x\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0X\",\t\t\"X\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0xg\",\t\t\"xg\",\t0,\tERR(0),\t\tKUMAX(0)},\n\t\t{\"0XA\",\t\t\"\",\t0,\tERR(0),\t\tKUMAX(10)},\n\n\t\t{\"010\",\t\t\"\",\t10,\tERR(0),\t\tKUMAX(10)},\n\t\t{\"0x3\",\t\t\"x3\",\t10,\tERR(0),\t\tKUMAX(0)},\n\n\t\t{\"12\",\t\t\"2\",\t2,\tERR(0),\t\tKUMAX(1)},\n\t\t{\"78\",\t\t\"8\",\t8,\tERR(0),\t\tKUMAX(7)},\n\t\t{\"9a\",\t\t\"a\",\t10,\tERR(0),\t\tKUMAX(9)},\n\t\t{\"9A\",\t\t\"A\",\t10,\tERR(0),\t\tKUMAX(9)},\n\t\t{\"fg\",\t\t\"g\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"FG\",\t\t\"G\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"0xfg\",\t\"g\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"0XFG\",\t\"G\",\t16,\tERR(0),\t\tKUMAX(15)},\n\t\t{\"z_\",\t\t\"_\",\t36,\tERR(0),\t\tKUMAX(35)},\n\t\t{\"Z_\",\t\t\"_\",\t36,\tERR(0),\t\tKUMAX(35)}\n\t};\n#undef ERR\n#undef KUMAX\n#undef KSMAX\n\tunsigned i;\n\n\tfor (i = 0; i < sizeof(tests)/sizeof(struct test_s); i++) {\n\t\tstruct test_s *test = &tests[i];\n\t\tint err;\n\t\tuintmax_t result;\n\t\tchar *remainder;\n\n\t\tset_errno(0);\n\t\tresult = malloc_strtoumax(test->input, &remainder, test->base);\n\t\terr = get_errno();\n\t\tassert_d_eq(err, test->expected_errno,\n\t\t    \"Expected errno %s for \\\"%s\\\", base %d\",\n\t\t    test->expected_errno_name, test->input, test->base);\n\t\tassert_str_eq(remainder, test->expected_remainder,\n\t\t    \"Unexpected remainder for \\\"%s\\\", base %d\",\n\t\t    test->input, test->base);\n\t\tif (err == 0) {\n\t\t\tassert_ju_eq(result, test->expected_x,\n\t\t\t    \"Unexpected result for \\\"%s\\\", base %d\",\n\t\t\t    test->input, test->base);\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_malloc_snprintf_truncated) {\n#define BUFLEN\t15\n\tchar buf[BUFLEN];\n\tsize_t result;\n\tsize_t len;\n#define TEST(expected_str_untruncated, ...) do {\t\t\t\\\n\tresult = malloc_snprintf(buf, len, __VA_ARGS__);\t\t\\\n\tassert_d_eq(strncmp(buf, expected_str_untruncated, len-1), 0,\t\\\n\t    \"Unexpected string inequality (\\\"%s\\\" vs \\\"%s\\\")\",\t\t\\\n\t    buf, expected_str_untruncated);\t\t\t\t\\\n\tassert_zu_eq(result, strlen(expected_str_untruncated),\t\t\\\n\t    \"Unexpected result\");\t\t\t\t\t\\\n} while (0)\n\n\tfor (len = 1; len < BUFLEN; len++) {\n\t\tTEST(\"012346789\",\t\"012346789\");\n\t\tTEST(\"a0123b\",\t\t\"a%sb\", \"0123\");\n\t\tTEST(\"a01234567\",\t\"a%s%s\", \"0123\", \"4567\");\n\t\tTEST(\"a0123  \",\t\t\"a%-6s\", \"0123\");\n\t\tTEST(\"a  0123\",\t\t\"a%6s\", \"0123\");\n\t\tTEST(\"a   012\",\t\t\"a%6.3s\", \"0123\");\n\t\tTEST(\"a   012\",\t\t\"a%*.*s\", 6, 3, \"0123\");\n\t\tTEST(\"a 123b\",\t\t\"a% db\", 123);\n\t\tTEST(\"a123b\",\t\t\"a%-db\", 123);\n\t\tTEST(\"a-123b\",\t\t\"a%-db\", -123);\n\t\tTEST(\"a+123b\",\t\t\"a%+db\", 123);\n\t}\n#undef BUFLEN\n#undef TEST\n}\nTEST_END\n\nTEST_BEGIN(test_malloc_snprintf) {\n#define BUFLEN\t128\n\tchar buf[BUFLEN];\n\tsize_t result;\n#define TEST(expected_str, ...) do {\t\t\t\t\t\\\n\tresult = malloc_snprintf(buf, sizeof(buf), __VA_ARGS__);\t\\\n\tassert_str_eq(buf, expected_str, \"Unexpected output\");\t\t\\\n\tassert_zu_eq(result, strlen(expected_str), \"Unexpected result\");\\\n} while (0)\n\n\tTEST(\"hello\", \"hello\");\n\n\tTEST(\"50%, 100%\", \"50%%, %d%%\", 100);\n\n\tTEST(\"a0123b\", \"a%sb\", \"0123\");\n\n\tTEST(\"a 0123b\", \"a%5sb\", \"0123\");\n\tTEST(\"a 0123b\", \"a%*sb\", 5, \"0123\");\n\n\tTEST(\"a0123 b\", \"a%-5sb\", \"0123\");\n\tTEST(\"a0123b\", \"a%*sb\", -1, \"0123\");\n\tTEST(\"a0123 b\", \"a%*sb\", -5, \"0123\");\n\tTEST(\"a0123 b\", \"a%-*sb\", -5, \"0123\");\n\n\tTEST(\"a012b\", \"a%.3sb\", \"0123\");\n\tTEST(\"a012b\", \"a%.*sb\", 3, \"0123\");\n\tTEST(\"a0123b\", \"a%.*sb\", -3, \"0123\");\n\n\tTEST(\"a  012b\", \"a%5.3sb\", \"0123\");\n\tTEST(\"a  012b\", \"a%5.*sb\", 3, \"0123\");\n\tTEST(\"a  012b\", \"a%*.3sb\", 5, \"0123\");\n\tTEST(\"a  012b\", \"a%*.*sb\", 5, 3, \"0123\");\n\tTEST(\"a 0123b\", \"a%*.*sb\", 5, -3, \"0123\");\n\n\tTEST(\"_abcd_\", \"_%x_\", 0xabcd);\n\tTEST(\"_0xabcd_\", \"_%#x_\", 0xabcd);\n\tTEST(\"_1234_\", \"_%o_\", 01234);\n\tTEST(\"_01234_\", \"_%#o_\", 01234);\n\tTEST(\"_1234_\", \"_%u_\", 1234);\n\n\tTEST(\"_1234_\", \"_%d_\", 1234);\n\tTEST(\"_ 1234_\", \"_% d_\", 1234);\n\tTEST(\"_+1234_\", \"_%+d_\", 1234);\n\tTEST(\"_-1234_\", \"_%d_\", -1234);\n\tTEST(\"_-1234_\", \"_% d_\", -1234);\n\tTEST(\"_-1234_\", \"_%+d_\", -1234);\n\n\tTEST(\"_-1234_\", \"_%d_\", -1234);\n\tTEST(\"_1234_\", \"_%d_\", 1234);\n\tTEST(\"_-1234_\", \"_%i_\", -1234);\n\tTEST(\"_1234_\", \"_%i_\", 1234);\n\tTEST(\"_01234_\", \"_%#o_\", 01234);\n\tTEST(\"_1234_\", \"_%u_\", 1234);\n\tTEST(\"_0x1234abc_\", \"_%#x_\", 0x1234abc);\n\tTEST(\"_0X1234ABC_\", \"_%#X_\", 0x1234abc);\n\tTEST(\"_c_\", \"_%c_\", 'c');\n\tTEST(\"_string_\", \"_%s_\", \"string\");\n\tTEST(\"_0x42_\", \"_%p_\", ((void *)0x42));\n\n\tTEST(\"_-1234_\", \"_%ld_\", ((long)-1234));\n\tTEST(\"_1234_\", \"_%ld_\", ((long)1234));\n\tTEST(\"_-1234_\", \"_%li_\", ((long)-1234));\n\tTEST(\"_1234_\", \"_%li_\", ((long)1234));\n\tTEST(\"_01234_\", \"_%#lo_\", ((long)01234));\n\tTEST(\"_1234_\", \"_%lu_\", ((long)1234));\n\tTEST(\"_0x1234abc_\", \"_%#lx_\", ((long)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#lX_\", ((long)0x1234ABC));\n\n\tTEST(\"_-1234_\", \"_%lld_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%lld_\", ((long long)1234));\n\tTEST(\"_-1234_\", \"_%lli_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%lli_\", ((long long)1234));\n\tTEST(\"_01234_\", \"_%#llo_\", ((long long)01234));\n\tTEST(\"_1234_\", \"_%llu_\", ((long long)1234));\n\tTEST(\"_0x1234abc_\", \"_%#llx_\", ((long long)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#llX_\", ((long long)0x1234ABC));\n\n\tTEST(\"_-1234_\", \"_%qd_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%qd_\", ((long long)1234));\n\tTEST(\"_-1234_\", \"_%qi_\", ((long long)-1234));\n\tTEST(\"_1234_\", \"_%qi_\", ((long long)1234));\n\tTEST(\"_01234_\", \"_%#qo_\", ((long long)01234));\n\tTEST(\"_1234_\", \"_%qu_\", ((long long)1234));\n\tTEST(\"_0x1234abc_\", \"_%#qx_\", ((long long)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#qX_\", ((long long)0x1234ABC));\n\n\tTEST(\"_-1234_\", \"_%jd_\", ((intmax_t)-1234));\n\tTEST(\"_1234_\", \"_%jd_\", ((intmax_t)1234));\n\tTEST(\"_-1234_\", \"_%ji_\", ((intmax_t)-1234));\n\tTEST(\"_1234_\", \"_%ji_\", ((intmax_t)1234));\n\tTEST(\"_01234_\", \"_%#jo_\", ((intmax_t)01234));\n\tTEST(\"_1234_\", \"_%ju_\", ((intmax_t)1234));\n\tTEST(\"_0x1234abc_\", \"_%#jx_\", ((intmax_t)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#jX_\", ((intmax_t)0x1234ABC));\n\n\tTEST(\"_1234_\", \"_%td_\", ((ptrdiff_t)1234));\n\tTEST(\"_-1234_\", \"_%td_\", ((ptrdiff_t)-1234));\n\tTEST(\"_1234_\", \"_%ti_\", ((ptrdiff_t)1234));\n\tTEST(\"_-1234_\", \"_%ti_\", ((ptrdiff_t)-1234));\n\n\tTEST(\"_-1234_\", \"_%zd_\", ((ssize_t)-1234));\n\tTEST(\"_1234_\", \"_%zd_\", ((ssize_t)1234));\n\tTEST(\"_-1234_\", \"_%zi_\", ((ssize_t)-1234));\n\tTEST(\"_1234_\", \"_%zi_\", ((ssize_t)1234));\n\tTEST(\"_01234_\", \"_%#zo_\", ((ssize_t)01234));\n\tTEST(\"_1234_\", \"_%zu_\", ((ssize_t)1234));\n\tTEST(\"_0x1234abc_\", \"_%#zx_\", ((ssize_t)0x1234abc));\n\tTEST(\"_0X1234ABC_\", \"_%#zX_\", ((ssize_t)0x1234ABC));\n#undef BUFLEN\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_malloc_strtoumax_no_endptr,\n\t    test_malloc_strtoumax,\n\t    test_malloc_snprintf_truncated,\n\t    test_malloc_snprintf);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/math.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define MAX_REL_ERR 1.0e-9\n#define MAX_ABS_ERR 1.0e-9\n\n#include <float.h>\n\n#ifdef __PGI\n#undef INFINITY\n#endif\n\n#ifndef INFINITY\n#define INFINITY (DBL_MAX + DBL_MAX)\n#endif\n\nstatic bool\ndouble_eq_rel(double a, double b, double max_rel_err, double max_abs_err) {\n\tdouble rel_err;\n\n\tif (fabs(a - b) < max_abs_err) {\n\t\treturn true;\n\t}\n\trel_err = (fabs(b) > fabs(a)) ? fabs((a-b)/b) : fabs((a-b)/a);\n\treturn (rel_err < max_rel_err);\n}\n\nstatic uint64_t\nfactorial(unsigned x) {\n\tuint64_t ret = 1;\n\tunsigned i;\n\n\tfor (i = 2; i <= x; i++) {\n\t\tret *= (uint64_t)i;\n\t}\n\n\treturn ret;\n}\n\nTEST_BEGIN(test_ln_gamma_factorial) {\n\tunsigned x;\n\n\t/* exp(ln_gamma(x)) == (x-1)! for integer x. */\n\tfor (x = 1; x <= 21; x++) {\n\t\tassert_true(double_eq_rel(exp(ln_gamma(x)),\n\t\t    (double)factorial(x-1), MAX_REL_ERR, MAX_ABS_ERR),\n\t\t    \"Incorrect factorial result for x=%u\", x);\n\t}\n}\nTEST_END\n\n/* Expected ln_gamma([0.0..100.0] increment=0.25). */\nstatic const double ln_gamma_misc_expected[] = {\n\tINFINITY,\n\t1.28802252469807743, 0.57236494292470008, 0.20328095143129538,\n\t0.00000000000000000, -0.09827183642181320, -0.12078223763524518,\n\t-0.08440112102048555, 0.00000000000000000, 0.12487171489239651,\n\t0.28468287047291918, 0.47521466691493719, 0.69314718055994529,\n\t0.93580193110872523, 1.20097360234707429, 1.48681557859341718,\n\t1.79175946922805496, 2.11445692745037128, 2.45373657084244234,\n\t2.80857141857573644, 3.17805383034794575, 3.56137591038669710,\n\t3.95781396761871651, 4.36671603662228680, 4.78749174278204581,\n\t5.21960398699022932, 5.66256205985714178, 6.11591589143154568,\n\t6.57925121201010121, 7.05218545073853953, 7.53436423675873268,\n\t8.02545839631598312, 8.52516136106541467, 9.03318691960512332,\n\t9.54926725730099690, 10.07315123968123949, 10.60460290274525086,\n\t11.14340011995171231, 11.68933342079726856, 12.24220494005076176,\n\t12.80182748008146909, 13.36802367147604720, 13.94062521940376342,\n\t14.51947222506051816, 15.10441257307551943, 15.69530137706046524,\n\t16.29200047656724237, 16.89437797963419285, 17.50230784587389010,\n\t18.11566950571089407, 18.73434751193644843, 19.35823122022435427,\n\t19.98721449566188468, 20.62119544270163018, 21.26007615624470048,\n\t21.90376249182879320, 22.55216385312342098, 23.20519299513386002,\n\t23.86276584168908954, 24.52480131594137802, 25.19122118273868338,\n\t25.86194990184851861, 26.53691449111561340, 27.21604439872720604,\n\t27.89927138384089389, 28.58652940490193828, 29.27775451504081516,\n\t29.97288476399884871, 30.67186010608067548, 31.37462231367769050,\n\t32.08111489594735843, 32.79128302226991565, 33.50507345013689076,\n\t34.22243445715505317, 34.94331577687681545, 35.66766853819134298,\n\t36.39544520803305261, 37.12659953718355865, 37.86108650896109395,\n\t38.59886229060776230, 39.33988418719949465, 40.08411059791735198,\n\t40.83150097453079752, 41.58201578195490100, 42.33561646075348506,\n\t43.09226539146988699, 43.85192586067515208, 44.61456202863158893,\n\t45.38013889847690052, 46.14862228684032885, 46.91997879580877395,\n\t47.69417578616628361, 48.47118135183522014, 49.25096429545256882,\n\t50.03349410501914463, 50.81874093156324790, 51.60667556776436982,\n\t52.39726942748592364, 53.19049452616926743, 53.98632346204390586,\n\t54.78472939811231157, 55.58568604486942633, 56.38916764371992940,\n\t57.19514895105859864, 58.00360522298051080, 58.81451220059079787,\n\t59.62784609588432261, 60.44358357816834371, 61.26170176100199427,\n\t62.08217818962842927, 62.90499082887649962, 63.73011805151035958,\n\t64.55753862700632340, 65.38723171073768015, 66.21917683354901385,\n\t67.05335389170279825, 67.88974313718154008, 68.72832516833013017,\n\t69.56908092082363737, 70.41199165894616385, 71.25703896716800045,\n\t72.10420474200799390, 72.95347118416940191, 73.80482079093779646,\n\t74.65823634883015814, 75.51370092648485866, 76.37119786778275454,\n\t77.23071078519033961, 78.09222355331530707, 78.95572030266725960,\n\t79.82118541361435859, 80.68860351052903468, 81.55795945611502873,\n\t82.42923834590904164, 83.30242550295004378, 84.17750647261028973,\n\t85.05446701758152983, 85.93329311301090456, 86.81397094178107920,\n\t87.69648688992882057, 88.58082754219766741, 89.46697967771913795,\n\t90.35493026581838194, 91.24466646193963015, 92.13617560368709292,\n\t93.02944520697742803, 93.92446296229978486, 94.82121673107967297,\n\t95.71969454214321615, 96.61988458827809723, 97.52177522288820910,\n\t98.42535495673848800, 99.33061245478741341, 100.23753653310367895,\n\t101.14611615586458981, 102.05634043243354370, 102.96819861451382394,\n\t103.88168009337621811, 104.79677439715833032, 105.71347118823287303,\n\t106.63176026064346047, 107.55163153760463501, 108.47307506906540198,\n\t109.39608102933323153, 110.32063971475740516, 111.24674154146920557,\n\t112.17437704317786995, 113.10353686902013237, 114.03421178146170689,\n\t114.96639265424990128, 115.90007047041454769, 116.83523632031698014,\n\t117.77188139974506953, 118.70999700805310795, 119.64957454634490830,\n\t120.59060551569974962, 121.53308151543865279, 122.47699424143097247,\n\t123.42233548443955726, 124.36909712850338394, 125.31727114935689826,\n\t126.26684961288492559, 127.21782467361175861, 128.17018857322420899,\n\t129.12393363912724453, 130.07905228303084755, 131.03553699956862033,\n\t131.99338036494577864, 132.95257503561629164, 133.91311374698926784,\n\t134.87498931216194364, 135.83819462068046846, 136.80272263732638294,\n\t137.76856640092901785, 138.73571902320256299, 139.70417368760718091,\n\t140.67392364823425055, 141.64496222871400732, 142.61728282114600574,\n\t143.59087888505104047, 144.56574394634486680, 145.54187159633210058,\n\t146.51925549072063859, 147.49788934865566148, 148.47776695177302031,\n\t149.45888214327129617, 150.44122882700193600, 151.42480096657754984,\n\t152.40959258449737490, 153.39559776128982094, 154.38281063467164245,\n\t155.37122539872302696, 156.36083630307879844, 157.35163765213474107,\n\t158.34362380426921391, 159.33678917107920370, 160.33112821663092973,\n\t161.32663545672428995, 162.32330545817117695, 163.32113283808695314,\n\t164.32011226319519892, 165.32023844914485267, 166.32150615984036790,\n\t167.32391020678358018, 168.32744544842768164, 169.33210678954270634,\n\t170.33788918059275375, 171.34478761712384198, 172.35279713916281707,\n\t173.36191283062726143, 174.37212981874515094, 175.38344327348534080,\n\t176.39584840699734514, 177.40934047306160437, 178.42391476654847793,\n\t179.43956662288721304, 180.45629141754378111, 181.47408456550741107,\n\t182.49294152078630304, 183.51285777591152737, 184.53382886144947861,\n\t185.55585034552262869, 186.57891783333786861, 187.60302696672312095,\n\t188.62817342367162610, 189.65435291789341932, 190.68156119837468054,\n\t191.70979404894376330, 192.73904728784492590, 193.76931676731820176,\n\t194.80059837318714244, 195.83288802445184729, 196.86618167288995096,\n\t197.90047530266301123, 198.93576492992946214, 199.97204660246373464,\n\t201.00931639928148797, 202.04757043027063901, 203.08680483582807597,\n\t204.12701578650228385, 205.16819948264117102, 206.21035215404597807,\n\t207.25347005962987623, 208.29754948708190909, 209.34258675253678916,\n\t210.38857820024875878, 211.43552020227099320, 212.48340915813977858,\n\t213.53224149456323744, 214.58201366511514152, 215.63272214993284592,\n\t216.68436345542014010, 217.73693411395422004, 218.79043068359703739,\n\t219.84484974781133815, 220.90018791517996988, 221.95644181913033322,\n\t223.01360811766215875, 224.07168349307951871, 225.13066465172661879,\n\t226.19054832372759734, 227.25133126272962159, 228.31301024565024704,\n\t229.37558207242807384, 230.43904356577689896, 231.50339157094342113,\n\t232.56862295546847008, 233.63473460895144740, 234.70172344281823484,\n\t235.76958639009222907, 236.83832040516844586, 237.90792246359117712,\n\t238.97838956183431947, 240.04971871708477238, 241.12190696702904802,\n\t242.19495136964280846, 243.26884900298270509, 244.34359696498191283,\n\t245.41919237324782443, 246.49563236486270057, 247.57291409618682110,\n\t248.65103474266476269, 249.72999149863338175, 250.80978157713354904,\n\t251.89040220972316320, 252.97185064629374551, 254.05412415488834199,\n\t255.13722002152300661, 256.22113555000953511, 257.30586806178126835,\n\t258.39141489572085675, 259.47777340799029844, 260.56494097186322279,\n\t261.65291497755913497, 262.74169283208021852, 263.83127195904967266,\n\t264.92164979855277807, 266.01282380697938379, 267.10479145686849733,\n\t268.19755023675537586, 269.29109765101975427, 270.38543121973674488,\n\t271.48054847852881721, 272.57644697842033565, 273.67312428569374561,\n\t274.77057798174683967, 275.86880566295326389, 276.96780494052313770,\n\t278.06757344036617496, 279.16810880295668085, 280.26940868320008349,\n\t281.37147075030043197, 282.47429268763045229, 283.57787219260217171,\n\t284.68220697654078322, 285.78729476455760050, 286.89313329542699194,\n\t287.99972032146268930, 289.10705360839756395, 290.21513093526289140,\n\t291.32395009427028754, 292.43350889069523646, 293.54380514276073200,\n\t294.65483668152336350, 295.76660135076059532, 296.87909700685889902,\n\t297.99232151870342022, 299.10627276756946458, 300.22094864701409733,\n\t301.33634706277030091, 302.45246593264130297, 303.56930318639643929,\n\t304.68685676566872189, 305.80512462385280514, 306.92410472600477078,\n\t308.04379504874236773, 309.16419358014690033, 310.28529831966631036,\n\t311.40710727801865687, 312.52961847709792664, 313.65282994987899201,\n\t314.77673974032603610, 315.90134590329950015, 317.02664650446632777,\n\t318.15263962020929966, 319.27932333753892635, 320.40669575400545455,\n\t321.53475497761127144, 322.66349912672620803, 323.79292633000159185,\n\t324.92303472628691452, 326.05382246454587403, 327.18528770377525916,\n\t328.31742861292224234, 329.45024337080525356, 330.58373016603343331,\n\t331.71788719692847280, 332.85271267144611329, 333.98820480709991898,\n\t335.12436183088397001, 336.26118197919845443, 337.39866349777429377,\n\t338.53680464159958774, 339.67560367484657036, 340.81505887079896411,\n\t341.95516851178109619, 343.09593088908627578, 344.23734430290727460,\n\t345.37940706226686416, 346.52211748494903532, 347.66547389743118401,\n\t348.80947463481720661, 349.95411804077025408, 351.09940246744753267,\n\t352.24532627543504759, 353.39188783368263103, 354.53908551944078908,\n\t355.68691771819692349, 356.83538282361303118, 357.98447923746385868,\n\t359.13420536957539753\n};\n\nTEST_BEGIN(test_ln_gamma_misc) {\n\tunsigned i;\n\n\tfor (i = 1; i < sizeof(ln_gamma_misc_expected)/sizeof(double); i++) {\n\t\tdouble x = (double)i * 0.25;\n\t\tassert_true(double_eq_rel(ln_gamma(x),\n\t\t    ln_gamma_misc_expected[i], MAX_REL_ERR, MAX_ABS_ERR),\n\t\t    \"Incorrect ln_gamma result for i=%u\", i);\n\t}\n}\nTEST_END\n\n/* Expected pt_norm([0.01..0.99] increment=0.01). */\nstatic const double pt_norm_expected[] = {\n\t-INFINITY,\n\t-2.32634787404084076, -2.05374891063182252, -1.88079360815125085,\n\t-1.75068607125216946, -1.64485362695147264, -1.55477359459685305,\n\t-1.47579102817917063, -1.40507156030963221, -1.34075503369021654,\n\t-1.28155156554460081, -1.22652812003661049, -1.17498679206608991,\n\t-1.12639112903880045, -1.08031934081495606, -1.03643338949378938,\n\t-0.99445788320975281, -0.95416525314619416, -0.91536508784281390,\n\t-0.87789629505122846, -0.84162123357291418, -0.80642124701824025,\n\t-0.77219321418868492, -0.73884684918521371, -0.70630256284008752,\n\t-0.67448975019608171, -0.64334540539291685, -0.61281299101662701,\n\t-0.58284150727121620, -0.55338471955567281, -0.52440051270804067,\n\t-0.49585034734745320, -0.46769879911450812, -0.43991316567323380,\n\t-0.41246312944140462, -0.38532046640756751, -0.35845879325119373,\n\t-0.33185334643681652, -0.30548078809939738, -0.27931903444745404,\n\t-0.25334710313579978, -0.22754497664114931, -0.20189347914185077,\n\t-0.17637416478086135, -0.15096921549677725, -0.12566134685507399,\n\t-0.10043372051146975, -0.07526986209982976, -0.05015358346473352,\n\t-0.02506890825871106, 0.00000000000000000, 0.02506890825871106,\n\t0.05015358346473366, 0.07526986209982990, 0.10043372051146990,\n\t0.12566134685507413, 0.15096921549677739, 0.17637416478086146,\n\t0.20189347914185105, 0.22754497664114931, 0.25334710313579978,\n\t0.27931903444745404, 0.30548078809939738, 0.33185334643681652,\n\t0.35845879325119373, 0.38532046640756762, 0.41246312944140484,\n\t0.43991316567323391, 0.46769879911450835, 0.49585034734745348,\n\t0.52440051270804111, 0.55338471955567303, 0.58284150727121620,\n\t0.61281299101662701, 0.64334540539291685, 0.67448975019608171,\n\t0.70630256284008752, 0.73884684918521371, 0.77219321418868492,\n\t0.80642124701824036, 0.84162123357291441, 0.87789629505122879,\n\t0.91536508784281423, 0.95416525314619460, 0.99445788320975348,\n\t1.03643338949378938, 1.08031934081495606, 1.12639112903880045,\n\t1.17498679206608991, 1.22652812003661049, 1.28155156554460081,\n\t1.34075503369021654, 1.40507156030963265, 1.47579102817917085,\n\t1.55477359459685394, 1.64485362695147308, 1.75068607125217102,\n\t1.88079360815125041, 2.05374891063182208, 2.32634787404084076\n};\n\nTEST_BEGIN(test_pt_norm) {\n\tunsigned i;\n\n\tfor (i = 1; i < sizeof(pt_norm_expected)/sizeof(double); i++) {\n\t\tdouble p = (double)i * 0.01;\n\t\tassert_true(double_eq_rel(pt_norm(p), pt_norm_expected[i],\n\t\t    MAX_REL_ERR, MAX_ABS_ERR),\n\t\t    \"Incorrect pt_norm result for i=%u\", i);\n\t}\n}\nTEST_END\n\n/*\n * Expected pt_chi2(p=[0.01..0.99] increment=0.07,\n *                  df={0.1, 1.1, 10.1, 100.1, 1000.1}).\n */\nstatic const double pt_chi2_df[] = {0.1, 1.1, 10.1, 100.1, 1000.1};\nstatic const double pt_chi2_expected[] = {\n\t1.168926411457320e-40, 1.347680397072034e-22, 3.886980416666260e-17,\n\t8.245951724356564e-14, 2.068936347497604e-11, 1.562561743309233e-09,\n\t5.459543043426564e-08, 1.114775688149252e-06, 1.532101202364371e-05,\n\t1.553884683726585e-04, 1.239396954915939e-03, 8.153872320255721e-03,\n\t4.631183739647523e-02, 2.473187311701327e-01, 2.175254800183617e+00,\n\n\t0.0003729887888876379, 0.0164409238228929513, 0.0521523015190650113,\n\t0.1064701372271216612, 0.1800913735793082115, 0.2748704281195626931,\n\t0.3939246282787986497, 0.5420727552260817816, 0.7267265822221973259,\n\t0.9596554296000253670, 1.2607440376386165326, 1.6671185084541604304,\n\t2.2604828984738705167, 3.2868613342148607082, 6.9298574921692139839,\n\n\t2.606673548632508, 4.602913725294877, 5.646152813924212,\n\t6.488971315540869, 7.249823275816285, 7.977314231410841,\n\t8.700354939944047, 9.441728024225892, 10.224338321374127,\n\t11.076435368801061, 12.039320937038386, 13.183878752697167,\n\t14.657791935084575, 16.885728216339373, 23.361991680031817,\n\n\t70.14844087392152, 80.92379498849355, 85.53325420085891,\n\t88.94433120715347, 91.83732712857017, 94.46719943606301,\n\t96.96896479994635, 99.43412843510363, 101.94074719829733,\n\t104.57228644307247, 107.43900093448734, 110.71844673417287,\n\t114.76616819871325, 120.57422505959563, 135.92318818757556,\n\n\t899.0072447849649, 937.9271278858220, 953.8117189560207,\n\t965.3079371501154, 974.8974061207954, 983.4936235182347,\n\t991.5691170518946, 999.4334123954690, 1007.3391826856553,\n\t1015.5445154999951, 1024.3777075619569, 1034.3538789836223,\n\t1046.4872561869577, 1063.5717461999654, 1107.0741966053859\n};\n\nTEST_BEGIN(test_pt_chi2) {\n\tunsigned i, j;\n\tunsigned e = 0;\n\n\tfor (i = 0; i < sizeof(pt_chi2_df)/sizeof(double); i++) {\n\t\tdouble df = pt_chi2_df[i];\n\t\tdouble ln_gamma_df = ln_gamma(df * 0.5);\n\t\tfor (j = 1; j < 100; j += 7) {\n\t\t\tdouble p = (double)j * 0.01;\n\t\t\tassert_true(double_eq_rel(pt_chi2(p, df, ln_gamma_df),\n\t\t\t    pt_chi2_expected[e], MAX_REL_ERR, MAX_ABS_ERR),\n\t\t\t    \"Incorrect pt_chi2 result for i=%u, j=%u\", i, j);\n\t\t\te++;\n\t\t}\n\t}\n}\nTEST_END\n\n/*\n * Expected pt_gamma(p=[0.1..0.99] increment=0.07,\n *                   shape=[0.5..3.0] increment=0.5).\n */\nstatic const double pt_gamma_shape[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0};\nstatic const double pt_gamma_expected[] = {\n\t7.854392895485103e-05, 5.043466107888016e-03, 1.788288957794883e-02,\n\t3.900956150232906e-02, 6.913847560638034e-02, 1.093710833465766e-01,\n\t1.613412523825817e-01, 2.274682115597864e-01, 3.114117323127083e-01,\n\t4.189466220207417e-01, 5.598106789059246e-01, 7.521856146202706e-01,\n\t1.036125427911119e+00, 1.532450860038180e+00, 3.317448300510606e+00,\n\n\t0.01005033585350144, 0.08338160893905107, 0.16251892949777497,\n\t0.24846135929849966, 0.34249030894677596, 0.44628710262841947,\n\t0.56211891815354142, 0.69314718055994529, 0.84397007029452920,\n\t1.02165124753198167, 1.23787435600161766, 1.51412773262977574,\n\t1.89711998488588196, 2.52572864430825783, 4.60517018598809091,\n\n\t0.05741590094955853, 0.24747378084860744, 0.39888572212236084,\n\t0.54394139997444901, 0.69048812513915159, 0.84311389861296104,\n\t1.00580622221479898, 1.18298694218766931, 1.38038096305861213,\n\t1.60627736383027453, 1.87396970522337947, 2.20749220408081070,\n\t2.65852391865854942, 3.37934630984842244, 5.67243336507218476,\n\n\t0.1485547402532659, 0.4657458011640391, 0.6832386130709406,\n\t0.8794297834672100, 1.0700752852474524, 1.2629614217350744,\n\t1.4638400448580779, 1.6783469900166610, 1.9132338090606940,\n\t2.1778589228618777, 2.4868823970010991, 2.8664695666264195,\n\t3.3724415436062114, 4.1682658512758071, 6.6383520679938108,\n\n\t0.2771490383641385, 0.7195001279643727, 0.9969081732265243,\n\t1.2383497880608061, 1.4675206597269927, 1.6953064251816552,\n\t1.9291243435606809, 2.1757300955477641, 2.4428032131216391,\n\t2.7406534569230616, 3.0851445039665513, 3.5043101122033367,\n\t4.0575997065264637, 4.9182956424675286, 7.5431362346944937,\n\n\t0.4360451650782932, 0.9983600902486267, 1.3306365880734528,\n\t1.6129750834753802, 1.8767241606994294, 2.1357032436097660,\n\t2.3988853336865565, 2.6740603137235603, 2.9697561737517959,\n\t3.2971457713883265, 3.6731795898504660, 4.1275751617770631,\n\t4.7230515633946677, 5.6417477865306020, 8.4059469148854635\n};\n\nTEST_BEGIN(test_pt_gamma_shape) {\n\tunsigned i, j;\n\tunsigned e = 0;\n\n\tfor (i = 0; i < sizeof(pt_gamma_shape)/sizeof(double); i++) {\n\t\tdouble shape = pt_gamma_shape[i];\n\t\tdouble ln_gamma_shape = ln_gamma(shape);\n\t\tfor (j = 1; j < 100; j += 7) {\n\t\t\tdouble p = (double)j * 0.01;\n\t\t\tassert_true(double_eq_rel(pt_gamma(p, shape, 1.0,\n\t\t\t    ln_gamma_shape), pt_gamma_expected[e], MAX_REL_ERR,\n\t\t\t    MAX_ABS_ERR),\n\t\t\t    \"Incorrect pt_gamma result for i=%u, j=%u\", i, j);\n\t\t\te++;\n\t\t}\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_pt_gamma_scale) {\n\tdouble shape = 1.0;\n\tdouble ln_gamma_shape = ln_gamma(shape);\n\n\tassert_true(double_eq_rel(\n\t    pt_gamma(0.5, shape, 1.0, ln_gamma_shape) * 10.0,\n\t    pt_gamma(0.5, shape, 10.0, ln_gamma_shape), MAX_REL_ERR,\n\t    MAX_ABS_ERR),\n\t    \"Scale should be trivially equivalent to external multiplication\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ln_gamma_factorial,\n\t    test_ln_gamma_misc,\n\t    test_pt_norm,\n\t    test_pt_chi2,\n\t    test_pt_gamma_shape,\n\t    test_pt_gamma_scale);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/mq.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NSENDERS\t3\n#define NMSGS\t\t100000\n\ntypedef struct mq_msg_s mq_msg_t;\nstruct mq_msg_s {\n\tmq_msg(mq_msg_t)\tlink;\n};\nmq_gen(static, mq_, mq_t, mq_msg_t, link)\n\nTEST_BEGIN(test_mq_basic) {\n\tmq_t mq;\n\tmq_msg_t msg;\n\n\tassert_false(mq_init(&mq), \"Unexpected mq_init() failure\");\n\tassert_u_eq(mq_count(&mq), 0, \"mq should be empty\");\n\tassert_ptr_null(mq_tryget(&mq),\n\t    \"mq_tryget() should fail when the queue is empty\");\n\n\tmq_put(&mq, &msg);\n\tassert_u_eq(mq_count(&mq), 1, \"mq should contain one message\");\n\tassert_ptr_eq(mq_tryget(&mq), &msg, \"mq_tryget() should return msg\");\n\n\tmq_put(&mq, &msg);\n\tassert_ptr_eq(mq_get(&mq), &msg, \"mq_get() should return msg\");\n\n\tmq_fini(&mq);\n}\nTEST_END\n\nstatic void *\nthd_receiver_start(void *arg) {\n\tmq_t *mq = (mq_t *)arg;\n\tunsigned i;\n\n\tfor (i = 0; i < (NSENDERS * NMSGS); i++) {\n\t\tmq_msg_t *msg = mq_get(mq);\n\t\tassert_ptr_not_null(msg, \"mq_get() should never return NULL\");\n\t\tdallocx(msg, 0);\n\t}\n\treturn NULL;\n}\n\nstatic void *\nthd_sender_start(void *arg) {\n\tmq_t *mq = (mq_t *)arg;\n\tunsigned i;\n\n\tfor (i = 0; i < NMSGS; i++) {\n\t\tmq_msg_t *msg;\n\t\tvoid *p;\n\t\tp = mallocx(sizeof(mq_msg_t), 0);\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\t\tmsg = (mq_msg_t *)p;\n\t\tmq_put(mq, msg);\n\t}\n\treturn NULL;\n}\n\nTEST_BEGIN(test_mq_threaded) {\n\tmq_t mq;\n\tthd_t receiver;\n\tthd_t senders[NSENDERS];\n\tunsigned i;\n\n\tassert_false(mq_init(&mq), \"Unexpected mq_init() failure\");\n\n\tthd_create(&receiver, thd_receiver_start, (void *)&mq);\n\tfor (i = 0; i < NSENDERS; i++) {\n\t\tthd_create(&senders[i], thd_sender_start, (void *)&mq);\n\t}\n\n\tthd_join(receiver, NULL);\n\tfor (i = 0; i < NSENDERS; i++) {\n\t\tthd_join(senders[i], NULL);\n\t}\n\n\tmq_fini(&mq);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_mq_basic,\n\t    test_mq_threaded);\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/mtx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS\t2\n#define NINCRS\t\t2000000\n\nTEST_BEGIN(test_mtx_basic) {\n\tmtx_t mtx;\n\n\tassert_false(mtx_init(&mtx), \"Unexpected mtx_init() failure\");\n\tmtx_lock(&mtx);\n\tmtx_unlock(&mtx);\n\tmtx_fini(&mtx);\n}\nTEST_END\n\ntypedef struct {\n\tmtx_t\t\tmtx;\n\tunsigned\tx;\n} thd_start_arg_t;\n\nstatic void *\nthd_start(void *varg) {\n\tthd_start_arg_t *arg = (thd_start_arg_t *)varg;\n\tunsigned i;\n\n\tfor (i = 0; i < NINCRS; i++) {\n\t\tmtx_lock(&arg->mtx);\n\t\targ->x++;\n\t\tmtx_unlock(&arg->mtx);\n\t}\n\treturn NULL;\n}\n\nTEST_BEGIN(test_mtx_race) {\n\tthd_start_arg_t arg;\n\tthd_t thds[NTHREADS];\n\tunsigned i;\n\n\tassert_false(mtx_init(&arg.mtx), \"Unexpected mtx_init() failure\");\n\targ.x = 0;\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_create(&thds[i], thd_start, (void *)&arg);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n\tassert_u_eq(arg.x, NTHREADS * NINCRS,\n\t    \"Race-related counter corruption\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_mtx_basic,\n\t    test_mtx_race);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/nstime.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define BILLION\tUINT64_C(1000000000)\n\nTEST_BEGIN(test_nstime_init) {\n\tnstime_t nst;\n\n\tnstime_init(&nst, 42000000043);\n\tassert_u64_eq(nstime_ns(&nst), 42000000043, \"ns incorrectly read\");\n\tassert_u64_eq(nstime_sec(&nst), 42, \"sec incorrectly read\");\n\tassert_u64_eq(nstime_nsec(&nst), 43, \"nsec incorrectly read\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_init2) {\n\tnstime_t nst;\n\n\tnstime_init2(&nst, 42, 43);\n\tassert_u64_eq(nstime_sec(&nst), 42, \"sec incorrectly read\");\n\tassert_u64_eq(nstime_nsec(&nst), 43, \"nsec incorrectly read\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_copy) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_init(&nstb, 0);\n\tnstime_copy(&nstb, &nsta);\n\tassert_u64_eq(nstime_sec(&nstb), 42, \"sec incorrectly copied\");\n\tassert_u64_eq(nstime_nsec(&nstb), 43, \"nsec incorrectly copied\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_compare) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0, \"Times should be equal\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), 0, \"Times should be equal\");\n\n\tnstime_init2(&nstb, 42, 42);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 1,\n\t    \"nsta should be greater than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), -1,\n\t    \"nstb should be less than nsta\");\n\n\tnstime_init2(&nstb, 42, 44);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), -1,\n\t    \"nsta should be less than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), 1,\n\t    \"nstb should be greater than nsta\");\n\n\tnstime_init2(&nstb, 41, BILLION - 1);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 1,\n\t    \"nsta should be greater than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), -1,\n\t    \"nstb should be less than nsta\");\n\n\tnstime_init2(&nstb, 43, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), -1,\n\t    \"nsta should be less than nstb\");\n\tassert_d_eq(nstime_compare(&nstb, &nsta), 1,\n\t    \"nstb should be greater than nsta\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_add) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_add(&nsta, &nstb);\n\tnstime_init2(&nstb, 84, 86);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n\n\tnstime_init2(&nsta, 42, BILLION - 1);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_add(&nsta, &nstb);\n\tnstime_init2(&nstb, 85, BILLION - 2);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_iadd) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, BILLION - 1);\n\tnstime_iadd(&nsta, 1);\n\tnstime_init2(&nstb, 43, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n\n\tnstime_init2(&nsta, 42, 1);\n\tnstime_iadd(&nsta, BILLION + 1);\n\tnstime_init2(&nstb, 43, 2);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect addition result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_subtract) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_subtract(&nsta, &nstb);\n\tnstime_init(&nstb, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_init2(&nstb, 41, 44);\n\tnstime_subtract(&nsta, &nstb);\n\tnstime_init2(&nstb, 0, BILLION - 1);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_isubtract) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_isubtract(&nsta, 42*BILLION + 43);\n\tnstime_init(&nstb, 0);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_isubtract(&nsta, 41*BILLION + 44);\n\tnstime_init2(&nstb, 0, BILLION - 1);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect subtraction result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_imultiply) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_init2(&nstb, 420, 430);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect multiplication result\");\n\n\tnstime_init2(&nsta, 42, 666666666);\n\tnstime_imultiply(&nsta, 3);\n\tnstime_init2(&nstb, 127, 999999998);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect multiplication result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_idivide) {\n\tnstime_t nsta, nstb;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_idivide(&nsta, 10);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect division result\");\n\n\tnstime_init2(&nsta, 42, 666666666);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 3);\n\tnstime_idivide(&nsta, 3);\n\tassert_d_eq(nstime_compare(&nsta, &nstb), 0,\n\t    \"Incorrect division result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_divide) {\n\tnstime_t nsta, nstb, nstc;\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tassert_u64_eq(nstime_divide(&nsta, &nstb), 10,\n\t    \"Incorrect division result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_init(&nstc, 1);\n\tnstime_add(&nsta, &nstc);\n\tassert_u64_eq(nstime_divide(&nsta, &nstb), 10,\n\t    \"Incorrect division result\");\n\n\tnstime_init2(&nsta, 42, 43);\n\tnstime_copy(&nstb, &nsta);\n\tnstime_imultiply(&nsta, 10);\n\tnstime_init(&nstc, 1);\n\tnstime_subtract(&nsta, &nstc);\n\tassert_u64_eq(nstime_divide(&nsta, &nstb), 9,\n\t    \"Incorrect division result\");\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_monotonic) {\n\tnstime_monotonic();\n}\nTEST_END\n\nTEST_BEGIN(test_nstime_update) {\n\tnstime_t nst;\n\n\tnstime_init(&nst, 0);\n\n\tassert_false(nstime_update(&nst), \"Basic time update failed.\");\n\n\t/* Only Rip Van Winkle sleeps this long. */\n\t{\n\t\tnstime_t addend;\n\t\tnstime_init2(&addend, 631152000, 0);\n\t\tnstime_add(&nst, &addend);\n\t}\n\t{\n\t\tnstime_t nst0;\n\t\tnstime_copy(&nst0, &nst);\n\t\tassert_true(nstime_update(&nst),\n\t\t    \"Update should detect time roll-back.\");\n\t\tassert_d_eq(nstime_compare(&nst, &nst0), 0,\n\t\t    \"Time should not have been modified\");\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_nstime_init,\n\t    test_nstime_init2,\n\t    test_nstime_copy,\n\t    test_nstime_compare,\n\t    test_nstime_add,\n\t    test_nstime_iadd,\n\t    test_nstime_subtract,\n\t    test_nstime_isubtract,\n\t    test_nstime_imultiply,\n\t    test_nstime_idivide,\n\t    test_nstime_divide,\n\t    test_nstime_monotonic,\n\t    test_nstime_update);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/pack.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n/*\n * Size class that is a divisor of the page size, ideally 4+ regions per run.\n */\n#if LG_PAGE <= 14\n#define SZ\t(ZU(1) << (LG_PAGE - 2))\n#else\n#define SZ\tZU(4096)\n#endif\n\n/*\n * Number of slabs to consume at high water mark.  Should be at least 2 so that\n * if mmap()ed memory grows downward, downward growth of mmap()ed memory is\n * tested.\n */\n#define NSLABS\t8\n\nstatic unsigned\nbinind_compute(void) {\n\tsize_t sz;\n\tunsigned nbins, i;\n\n\tsz = sizeof(nbins);\n\tassert_d_eq(mallctl(\"arenas.nbins\", (void *)&nbins, &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl failure\");\n\n\tfor (i = 0; i < nbins; i++) {\n\t\tsize_t mib[4];\n\t\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\t\tsize_t size;\n\n\t\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.size\", mib,\n\t\t    &miblen), 0, \"Unexpected mallctlnametomb failure\");\n\t\tmib[2] = (size_t)i;\n\n\t\tsz = sizeof(size);\n\t\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &sz, NULL,\n\t\t    0), 0, \"Unexpected mallctlbymib failure\");\n\t\tif (size == SZ) {\n\t\t\treturn i;\n\t\t}\n\t}\n\n\ttest_fail(\"Unable to compute nregs_per_run\");\n\treturn 0;\n}\n\nstatic size_t\nnregs_per_run_compute(void) {\n\tuint32_t nregs;\n\tsize_t sz;\n\tunsigned binind = binind_compute();\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(\"arenas.bin.0.nregs\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomb failure\");\n\tmib[2] = (size_t)binind;\n\tsz = sizeof(nregs);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&nregs, &sz, NULL,\n\t    0), 0, \"Unexpected mallctlbymib failure\");\n\treturn nregs;\n}\n\nstatic unsigned\narenas_create_mallctl(void) {\n\tunsigned arena_ind;\n\tsize_t sz;\n\n\tsz = sizeof(arena_ind);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Error in arenas.create\");\n\n\treturn arena_ind;\n}\n\nstatic void\narena_reset_mallctl(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(\"arena.0.reset\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nTEST_BEGIN(test_pack) {\n\tbool prof_enabled;\n\tsize_t sz = sizeof(prof_enabled);\n\tif (mallctl(\"opt.prof\", (void *)&prof_enabled, &sz, NULL, 0) == 0) {\n\t\ttest_skip_if(prof_enabled);\n\t}\n\n\tunsigned arena_ind = arenas_create_mallctl();\n\tsize_t nregs_per_run = nregs_per_run_compute();\n\tsize_t nregs = nregs_per_run * NSLABS;\n\tVARIABLE_ARRAY(void *, ptrs, nregs);\n\tsize_t i, j, offset;\n\n\t/* Fill matrix. */\n\tfor (i = offset = 0; i < NSLABS; i++) {\n\t\tfor (j = 0; j < nregs_per_run; j++) {\n\t\t\tvoid *p = mallocx(SZ, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected mallocx(%zu, MALLOCX_ARENA(%u) |\"\n\t\t\t    \" MALLOCX_TCACHE_NONE) failure, run=%zu, reg=%zu\",\n\t\t\t    SZ, arena_ind, i, j);\n\t\t\tptrs[(i * nregs_per_run) + j] = p;\n\t\t}\n\t}\n\n\t/*\n\t * Free all but one region of each run, but rotate which region is\n\t * preserved, so that subsequent allocations exercise the within-run\n\t * layout policy.\n\t */\n\toffset = 0;\n\tfor (i = offset = 0;\n\t    i < NSLABS;\n\t    i++, offset = (offset + 1) % nregs_per_run) {\n\t\tfor (j = 0; j < nregs_per_run; j++) {\n\t\t\tvoid *p = ptrs[(i * nregs_per_run) + j];\n\t\t\tif (offset == j) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tdallocx(p, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE);\n\t\t}\n\t}\n\n\t/*\n\t * Logically refill matrix, skipping preserved regions and verifying\n\t * that the matrix is unmodified.\n\t */\n\toffset = 0;\n\tfor (i = offset = 0;\n\t    i < NSLABS;\n\t    i++, offset = (offset + 1) % nregs_per_run) {\n\t\tfor (j = 0; j < nregs_per_run; j++) {\n\t\t\tvoid *p;\n\n\t\t\tif (offset == j) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tp = mallocx(SZ, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE);\n\t\t\tassert_ptr_eq(p, ptrs[(i * nregs_per_run) + j],\n\t\t\t    \"Unexpected refill discrepancy, run=%zu, reg=%zu\\n\",\n\t\t\t    i, j);\n\t\t}\n\t}\n\n\t/* Clean up. */\n\tarena_reset_mallctl(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_pack);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/pack.sh",
    "content": "#!/bin/sh\n\n# Immediately purge to minimize fragmentation.\nexport MALLOC_CONF=\"dirty_decay_ms:0,muzzy_decay_ms:0\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/pages.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_pages_huge) {\n\tsize_t alloc_size;\n\tbool commit;\n\tvoid *pages, *hugepage;\n\n\talloc_size = HUGEPAGE * 2 - PAGE;\n\tcommit = true;\n\tpages = pages_map(NULL, alloc_size, PAGE, &commit);\n\tassert_ptr_not_null(pages, \"Unexpected pages_map() error\");\n\n\tif (init_system_thp_mode == thp_mode_default) {\n\t    hugepage = (void *)(ALIGNMENT_CEILING((uintptr_t)pages, HUGEPAGE));\n\t    assert_b_ne(pages_huge(hugepage, HUGEPAGE), have_madvise_huge,\n\t        \"Unexpected pages_huge() result\");\n\t    assert_false(pages_nohuge(hugepage, HUGEPAGE),\n\t        \"Unexpected pages_nohuge() result\");\n\t}\n\n\tpages_unmap(pages, alloc_size);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_pages_huge);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/ph.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ph.h\"\n\ntypedef struct node_s node_t;\n\nstruct node_s {\n#define NODE_MAGIC 0x9823af7e\n\tuint32_t magic;\n\tphn(node_t) link;\n\tuint64_t key;\n};\n\nstatic int\nnode_cmp(const node_t *a, const node_t *b) {\n\tint ret;\n\n\tret = (a->key > b->key) - (a->key < b->key);\n\tif (ret == 0) {\n\t\t/*\n\t\t * Duplicates are not allowed in the heap, so force an\n\t\t * arbitrary ordering for non-identical items with equal keys.\n\t\t */\n\t\tret = (((uintptr_t)a) > ((uintptr_t)b))\n\t\t    - (((uintptr_t)a) < ((uintptr_t)b));\n\t}\n\treturn ret;\n}\n\nstatic int\nnode_cmp_magic(const node_t *a, const node_t *b) {\n\n\tassert_u32_eq(a->magic, NODE_MAGIC, \"Bad magic\");\n\tassert_u32_eq(b->magic, NODE_MAGIC, \"Bad magic\");\n\n\treturn node_cmp(a, b);\n}\n\ntypedef ph(node_t) heap_t;\nph_gen(static, heap_, heap_t, node_t, link, node_cmp_magic);\n\nstatic void\nnode_print(const node_t *node, unsigned depth) {\n\tunsigned i;\n\tnode_t *leftmost_child, *sibling;\n\n\tfor (i = 0; i < depth; i++) {\n\t\tmalloc_printf(\"\\t\");\n\t}\n\tmalloc_printf(\"%2\"FMTu64\"\\n\", node->key);\n\n\tleftmost_child = phn_lchild_get(node_t, link, node);\n\tif (leftmost_child == NULL) {\n\t\treturn;\n\t}\n\tnode_print(leftmost_child, depth + 1);\n\n\tfor (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=\n\t    NULL; sibling = phn_next_get(node_t, link, sibling)) {\n\t\tnode_print(sibling, depth + 1);\n\t}\n}\n\nstatic void\nheap_print(const heap_t *heap) {\n\tnode_t *auxelm;\n\n\tmalloc_printf(\"vvv heap %p vvv\\n\", heap);\n\tif (heap->ph_root == NULL) {\n\t\tgoto label_return;\n\t}\n\n\tnode_print(heap->ph_root, 0);\n\n\tfor (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;\n\t    auxelm = phn_next_get(node_t, link, auxelm)) {\n\t\tassert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,\n\t\t    link, auxelm)), auxelm,\n\t\t    \"auxelm's prev doesn't link to auxelm\");\n\t\tnode_print(auxelm, 0);\n\t}\n\nlabel_return:\n\tmalloc_printf(\"^^^ heap %p ^^^\\n\", heap);\n}\n\nstatic unsigned\nnode_validate(const node_t *node, const node_t *parent) {\n\tunsigned nnodes = 1;\n\tnode_t *leftmost_child, *sibling;\n\n\tif (parent != NULL) {\n\t\tassert_d_ge(node_cmp_magic(node, parent), 0,\n\t\t    \"Child is less than parent\");\n\t}\n\n\tleftmost_child = phn_lchild_get(node_t, link, node);\n\tif (leftmost_child == NULL) {\n\t\treturn nnodes;\n\t}\n\tassert_ptr_eq((void *)phn_prev_get(node_t, link, leftmost_child),\n\t    (void *)node, \"Leftmost child does not link to node\");\n\tnnodes += node_validate(leftmost_child, node);\n\n\tfor (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=\n\t    NULL; sibling = phn_next_get(node_t, link, sibling)) {\n\t\tassert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,\n\t\t    link, sibling)), sibling,\n\t\t    \"sibling's prev doesn't link to sibling\");\n\t\tnnodes += node_validate(sibling, node);\n\t}\n\treturn nnodes;\n}\n\nstatic unsigned\nheap_validate(const heap_t *heap) {\n\tunsigned nnodes = 0;\n\tnode_t *auxelm;\n\n\tif (heap->ph_root == NULL) {\n\t\tgoto label_return;\n\t}\n\n\tnnodes += node_validate(heap->ph_root, NULL);\n\n\tfor (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;\n\t    auxelm = phn_next_get(node_t, link, auxelm)) {\n\t\tassert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,\n\t\t    link, auxelm)), auxelm,\n\t\t    \"auxelm's prev doesn't link to auxelm\");\n\t\tnnodes += node_validate(auxelm, NULL);\n\t}\n\nlabel_return:\n\tif (false) {\n\t\theap_print(heap);\n\t}\n\treturn nnodes;\n}\n\nTEST_BEGIN(test_ph_empty) {\n\theap_t heap;\n\n\theap_new(&heap);\n\tassert_true(heap_empty(&heap), \"Heap should be empty\");\n\tassert_ptr_null(heap_first(&heap), \"Unexpected node\");\n\tassert_ptr_null(heap_any(&heap), \"Unexpected node\");\n}\nTEST_END\n\nstatic void\nnode_remove(heap_t *heap, node_t *node) {\n\theap_remove(heap, node);\n\n\tnode->magic = 0;\n}\n\nstatic node_t *\nnode_remove_first(heap_t *heap) {\n\tnode_t *node = heap_remove_first(heap);\n\tnode->magic = 0;\n\treturn node;\n}\n\nstatic node_t *\nnode_remove_any(heap_t *heap) {\n\tnode_t *node = heap_remove_any(heap);\n\tnode->magic = 0;\n\treturn node;\n}\n\nTEST_BEGIN(test_ph_random) {\n#define NNODES 25\n#define NBAGS 250\n#define SEED 42\n\tsfmt_t *sfmt;\n\tuint64_t bag[NNODES];\n\theap_t heap;\n\tnode_t nodes[NNODES];\n\tunsigned i, j, k;\n\n\tsfmt = init_gen_rand(SEED);\n\tfor (i = 0; i < NBAGS; i++) {\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\t/* Insert in order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = j;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t/* Insert in reverse order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = NNODES - j - 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = gen_rand64_range(sfmt, NNODES);\n\t\t\t}\n\t\t}\n\n\t\tfor (j = 1; j <= NNODES; j++) {\n\t\t\t/* Initialize heap and nodes. */\n\t\t\theap_new(&heap);\n\t\t\tassert_u_eq(heap_validate(&heap), 0,\n\t\t\t    \"Incorrect node count\");\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\tnodes[k].magic = NODE_MAGIC;\n\t\t\t\tnodes[k].key = bag[k];\n\t\t\t}\n\n\t\t\t/* Insert nodes. */\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\theap_insert(&heap, &nodes[k]);\n\t\t\t\tif (i % 13 == 12) {\n\t\t\t\t\tassert_ptr_not_null(heap_any(&heap),\n\t\t\t\t\t    \"Heap should not be empty\");\n\t\t\t\t\t/* Trigger merging. */\n\t\t\t\t\tassert_ptr_not_null(heap_first(&heap),\n\t\t\t\t\t    \"Heap should not be empty\");\n\t\t\t\t}\n\t\t\t\tassert_u_eq(heap_validate(&heap), k + 1,\n\t\t\t\t    \"Incorrect node count\");\n\t\t\t}\n\n\t\t\tassert_false(heap_empty(&heap),\n\t\t\t    \"Heap should not be empty\");\n\n\t\t\t/* Remove nodes. */\n\t\t\tswitch (i % 6) {\n\t\t\tcase 0:\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t\tnode_remove(&heap, &nodes[k]);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tfor (k = j; k > 0; k--) {\n\t\t\t\t\tnode_remove(&heap, &nodes[k-1]);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), k - 1,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2: {\n\t\t\t\tnode_t *prev = NULL;\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_t *node = node_remove_first(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t\tif (prev != NULL) {\n\t\t\t\t\t\tassert_d_ge(node_cmp(node,\n\t\t\t\t\t\t    prev), 0,\n\t\t\t\t\t\t    \"Bad removal order\");\n\t\t\t\t\t}\n\t\t\t\t\tprev = node;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} case 3: {\n\t\t\t\tnode_t *prev = NULL;\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_t *node = heap_first(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t\tif (prev != NULL) {\n\t\t\t\t\t\tassert_d_ge(node_cmp(node,\n\t\t\t\t\t\t    prev), 0,\n\t\t\t\t\t\t    \"Bad removal order\");\n\t\t\t\t\t}\n\t\t\t\t\tnode_remove(&heap, node);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t\tprev = node;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} case 4: {\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_remove_any(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} case 5: {\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_t *node = heap_any(&heap);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k,\n\t\t\t\t\t    \"Incorrect node count\");\n\t\t\t\t\tnode_remove(&heap, node);\n\t\t\t\t\tassert_u_eq(heap_validate(&heap), j - k\n\t\t\t\t\t    - 1, \"Incorrect node count\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} default:\n\t\t\t\tnot_reached();\n\t\t\t}\n\n\t\t\tassert_ptr_null(heap_first(&heap),\n\t\t\t    \"Heap should be empty\");\n\t\t\tassert_ptr_null(heap_any(&heap),\n\t\t\t    \"Heap should be empty\");\n\t\t\tassert_true(heap_empty(&heap), \"Heap should be empty\");\n\t\t}\n\t}\n\tfini_gen_rand(sfmt);\n#undef NNODES\n#undef SEED\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ph_empty,\n\t    test_ph_random);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prng.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\ntest_prng_lg_range_u32(bool atomic) {\n\tatomic_u32_t sa, sb;\n\tuint32_t ra, rb;\n\tunsigned lg_range;\n\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_u32(&sa, 32, atomic);\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_u32(&sa, 32, atomic);\n\tassert_u32_eq(ra, rb,\n\t    \"Repeated generation should produce repeated results\");\n\n\tatomic_store_u32(&sb, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_u32(&sb, 32, atomic);\n\tassert_u32_eq(ra, rb,\n\t    \"Equivalent generation should produce equivalent results\");\n\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_u32(&sa, 32, atomic);\n\trb = prng_lg_range_u32(&sa, 32, atomic);\n\tassert_u32_ne(ra, rb,\n\t    \"Full-width results must not immediately repeat\");\n\n\tatomic_store_u32(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_u32(&sa, 32, atomic);\n\tfor (lg_range = 31; lg_range > 0; lg_range--) {\n\t\tatomic_store_u32(&sb, 42, ATOMIC_RELAXED);\n\t\trb = prng_lg_range_u32(&sb, lg_range, atomic);\n\t\tassert_u32_eq((rb & (UINT32_C(0xffffffff) << lg_range)),\n\t\t    0, \"High order bits should be 0, lg_range=%u\", lg_range);\n\t\tassert_u32_eq(rb, (ra >> (32 - lg_range)),\n\t\t    \"Expected high order bits of full-width result, \"\n\t\t    \"lg_range=%u\", lg_range);\n\t}\n}\n\nstatic void\ntest_prng_lg_range_u64(void) {\n\tuint64_t sa, sb, ra, rb;\n\tunsigned lg_range;\n\n\tsa = 42;\n\tra = prng_lg_range_u64(&sa, 64);\n\tsa = 42;\n\trb = prng_lg_range_u64(&sa, 64);\n\tassert_u64_eq(ra, rb,\n\t    \"Repeated generation should produce repeated results\");\n\n\tsb = 42;\n\trb = prng_lg_range_u64(&sb, 64);\n\tassert_u64_eq(ra, rb,\n\t    \"Equivalent generation should produce equivalent results\");\n\n\tsa = 42;\n\tra = prng_lg_range_u64(&sa, 64);\n\trb = prng_lg_range_u64(&sa, 64);\n\tassert_u64_ne(ra, rb,\n\t    \"Full-width results must not immediately repeat\");\n\n\tsa = 42;\n\tra = prng_lg_range_u64(&sa, 64);\n\tfor (lg_range = 63; lg_range > 0; lg_range--) {\n\t\tsb = 42;\n\t\trb = prng_lg_range_u64(&sb, lg_range);\n\t\tassert_u64_eq((rb & (UINT64_C(0xffffffffffffffff) << lg_range)),\n\t\t    0, \"High order bits should be 0, lg_range=%u\", lg_range);\n\t\tassert_u64_eq(rb, (ra >> (64 - lg_range)),\n\t\t    \"Expected high order bits of full-width result, \"\n\t\t    \"lg_range=%u\", lg_range);\n\t}\n}\n\nstatic void\ntest_prng_lg_range_zu(bool atomic) {\n\tatomic_zu_t sa, sb;\n\tsize_t ra, rb;\n\tunsigned lg_range;\n\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tassert_zu_eq(ra, rb,\n\t    \"Repeated generation should produce repeated results\");\n\n\tatomic_store_zu(&sb, 42, ATOMIC_RELAXED);\n\trb = prng_lg_range_zu(&sb, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tassert_zu_eq(ra, rb,\n\t    \"Equivalent generation should produce equivalent results\");\n\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\trb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tassert_zu_ne(ra, rb,\n\t    \"Full-width results must not immediately repeat\");\n\n\tatomic_store_zu(&sa, 42, ATOMIC_RELAXED);\n\tra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);\n\tfor (lg_range = (ZU(1) << (3 + LG_SIZEOF_PTR)) - 1; lg_range > 0;\n\t    lg_range--) {\n\t\tatomic_store_zu(&sb, 42, ATOMIC_RELAXED);\n\t\trb = prng_lg_range_zu(&sb, lg_range, atomic);\n\t\tassert_zu_eq((rb & (SIZE_T_MAX << lg_range)),\n\t\t    0, \"High order bits should be 0, lg_range=%u\", lg_range);\n\t\tassert_zu_eq(rb, (ra >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) -\n\t\t    lg_range)), \"Expected high order bits of full-width \"\n\t\t    \"result, lg_range=%u\", lg_range);\n\t}\n}\n\nTEST_BEGIN(test_prng_lg_range_u32_nonatomic) {\n\ttest_prng_lg_range_u32(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_u32_atomic) {\n\ttest_prng_lg_range_u32(true);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_u64_nonatomic) {\n\ttest_prng_lg_range_u64();\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_zu_nonatomic) {\n\ttest_prng_lg_range_zu(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_lg_range_zu_atomic) {\n\ttest_prng_lg_range_zu(true);\n}\nTEST_END\n\nstatic void\ntest_prng_range_u32(bool atomic) {\n\tuint32_t range;\n#define MAX_RANGE\t10000000\n#define RANGE_STEP\t97\n#define NREPS\t\t10\n\n\tfor (range = 2; range < MAX_RANGE; range += RANGE_STEP) {\n\t\tatomic_u32_t s;\n\t\tunsigned rep;\n\n\t\tatomic_store_u32(&s, range, ATOMIC_RELAXED);\n\t\tfor (rep = 0; rep < NREPS; rep++) {\n\t\t\tuint32_t r = prng_range_u32(&s, range, atomic);\n\n\t\t\tassert_u32_lt(r, range, \"Out of range\");\n\t\t}\n\t}\n}\n\nstatic void\ntest_prng_range_u64(void) {\n\tuint64_t range;\n#define MAX_RANGE\t10000000\n#define RANGE_STEP\t97\n#define NREPS\t\t10\n\n\tfor (range = 2; range < MAX_RANGE; range += RANGE_STEP) {\n\t\tuint64_t s;\n\t\tunsigned rep;\n\n\t\ts = range;\n\t\tfor (rep = 0; rep < NREPS; rep++) {\n\t\t\tuint64_t r = prng_range_u64(&s, range);\n\n\t\t\tassert_u64_lt(r, range, \"Out of range\");\n\t\t}\n\t}\n}\n\nstatic void\ntest_prng_range_zu(bool atomic) {\n\tsize_t range;\n#define MAX_RANGE\t10000000\n#define RANGE_STEP\t97\n#define NREPS\t\t10\n\n\tfor (range = 2; range < MAX_RANGE; range += RANGE_STEP) {\n\t\tatomic_zu_t s;\n\t\tunsigned rep;\n\n\t\tatomic_store_zu(&s, range, ATOMIC_RELAXED);\n\t\tfor (rep = 0; rep < NREPS; rep++) {\n\t\t\tsize_t r = prng_range_zu(&s, range, atomic);\n\n\t\t\tassert_zu_lt(r, range, \"Out of range\");\n\t\t}\n\t}\n}\n\nTEST_BEGIN(test_prng_range_u32_nonatomic) {\n\ttest_prng_range_u32(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_u32_atomic) {\n\ttest_prng_range_u32(true);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_u64_nonatomic) {\n\ttest_prng_range_u64();\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_zu_nonatomic) {\n\ttest_prng_range_zu(false);\n}\nTEST_END\n\nTEST_BEGIN(test_prng_range_zu_atomic) {\n\ttest_prng_range_zu(true);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_prng_lg_range_u32_nonatomic,\n\t    test_prng_lg_range_u32_atomic,\n\t    test_prng_lg_range_u64_nonatomic,\n\t    test_prng_lg_range_zu_nonatomic,\n\t    test_prng_lg_range_zu_atomic,\n\t    test_prng_range_u32_nonatomic,\n\t    test_prng_range_u32_atomic,\n\t    test_prng_range_u64_nonatomic,\n\t    test_prng_range_zu_nonatomic,\n\t    test_prng_range_zu_atomic);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_accum.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#define NTHREADS\t\t4\n#define NALLOCS_PER_THREAD\t50\n#define DUMP_INTERVAL\t\t1\n#define BT_COUNT_CHECK_INTERVAL\t5\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nstatic void *\nalloc_from_permuted_backtrace(unsigned thd_ind, unsigned iteration) {\n\treturn btalloc(1, thd_ind*NALLOCS_PER_THREAD + iteration);\n}\n\nstatic void *\nthd_start(void *varg) {\n\tunsigned thd_ind = *(unsigned *)varg;\n\tsize_t bt_count_prev, bt_count;\n\tunsigned i_prev, i;\n\n\ti_prev = 0;\n\tbt_count_prev = 0;\n\tfor (i = 0; i < NALLOCS_PER_THREAD; i++) {\n\t\tvoid *p = alloc_from_permuted_backtrace(thd_ind, i);\n\t\tdallocx(p, 0);\n\t\tif (i % DUMP_INTERVAL == 0) {\n\t\t\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t\t\t    0, \"Unexpected error while dumping heap profile\");\n\t\t}\n\n\t\tif (i % BT_COUNT_CHECK_INTERVAL == 0 ||\n\t\t    i+1 == NALLOCS_PER_THREAD) {\n\t\t\tbt_count = prof_bt_count();\n\t\t\tassert_zu_le(bt_count_prev+(i-i_prev), bt_count,\n\t\t\t    \"Expected larger backtrace count increase\");\n\t\t\ti_prev = i;\n\t\t\tbt_count_prev = bt_count;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_idump) {\n\tbool active;\n\tthd_t thds[NTHREADS];\n\tunsigned thd_args[NTHREADS];\n\tunsigned i;\n\n\ttest_skip_if(!config_prof);\n\n\tactive = true;\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0,\n\t    \"Unexpected mallctl failure while activating profiling\");\n\n\tprof_dump_open = prof_dump_open_intercept;\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_args[i] = i;\n\t\tthd_create(&thds[i], thd_start, (void *)&thd_args[i]);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_idump);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_accum.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_accum:true,prof_active:false,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_active.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\nmallctl_bool_get(const char *name, bool expected, const char *func, int line) {\n\tbool old;\n\tsize_t sz;\n\n\tsz = sizeof(old);\n\tassert_d_eq(mallctl(name, (void *)&old, &sz, NULL, 0), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading %s\", func, line, name);\n\tassert_b_eq(old, expected, \"%s():%d: Unexpected %s value\", func, line,\n\t    name);\n}\n\nstatic void\nmallctl_bool_set(const char *name, bool old_expected, bool val_new,\n    const char *func, int line) {\n\tbool old;\n\tsize_t sz;\n\n\tsz = sizeof(old);\n\tassert_d_eq(mallctl(name, (void *)&old, &sz, (void *)&val_new,\n\t    sizeof(val_new)), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading/writing %s\", func,\n\t    line, name);\n\tassert_b_eq(old, old_expected, \"%s():%d: Unexpected %s value\", func,\n\t    line, name);\n}\n\nstatic void\nmallctl_prof_active_get_impl(bool prof_active_old_expected, const char *func,\n    int line) {\n\tmallctl_bool_get(\"prof.active\", prof_active_old_expected, func, line);\n}\n#define mallctl_prof_active_get(a)\t\t\t\t\t\\\n\tmallctl_prof_active_get_impl(a, __func__, __LINE__)\n\nstatic void\nmallctl_prof_active_set_impl(bool prof_active_old_expected,\n    bool prof_active_new, const char *func, int line) {\n\tmallctl_bool_set(\"prof.active\", prof_active_old_expected,\n\t    prof_active_new, func, line);\n}\n#define mallctl_prof_active_set(a, b)\t\t\t\t\t\\\n\tmallctl_prof_active_set_impl(a, b, __func__, __LINE__)\n\nstatic void\nmallctl_thread_prof_active_get_impl(bool thread_prof_active_old_expected,\n    const char *func, int line) {\n\tmallctl_bool_get(\"thread.prof.active\", thread_prof_active_old_expected,\n\t    func, line);\n}\n#define mallctl_thread_prof_active_get(a)\t\t\t\t\\\n\tmallctl_thread_prof_active_get_impl(a, __func__, __LINE__)\n\nstatic void\nmallctl_thread_prof_active_set_impl(bool thread_prof_active_old_expected,\n    bool thread_prof_active_new, const char *func, int line) {\n\tmallctl_bool_set(\"thread.prof.active\", thread_prof_active_old_expected,\n\t    thread_prof_active_new, func, line);\n}\n#define mallctl_thread_prof_active_set(a, b)\t\t\t\t\\\n\tmallctl_thread_prof_active_set_impl(a, b, __func__, __LINE__)\n\nstatic void\nprof_sampling_probe_impl(bool expect_sample, const char *func, int line) {\n\tvoid *p;\n\tsize_t expected_backtraces = expect_sample ? 1 : 0;\n\n\tassert_zu_eq(prof_bt_count(), 0, \"%s():%d: Expected 0 backtraces\", func,\n\t    line);\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tassert_zu_eq(prof_bt_count(), expected_backtraces,\n\t    \"%s():%d: Unexpected backtrace count\", func, line);\n\tdallocx(p, 0);\n}\n#define prof_sampling_probe(a)\t\t\t\t\t\t\\\n\tprof_sampling_probe_impl(a, __func__, __LINE__)\n\nTEST_BEGIN(test_prof_active) {\n\ttest_skip_if(!config_prof);\n\n\tmallctl_prof_active_get(true);\n\tmallctl_thread_prof_active_get(false);\n\n\tmallctl_prof_active_set(true, true);\n\tmallctl_thread_prof_active_set(false, false);\n\t/* prof.active, !thread.prof.active. */\n\tprof_sampling_probe(false);\n\n\tmallctl_prof_active_set(true, false);\n\tmallctl_thread_prof_active_set(false, false);\n\t/* !prof.active, !thread.prof.active. */\n\tprof_sampling_probe(false);\n\n\tmallctl_prof_active_set(false, false);\n\tmallctl_thread_prof_active_set(false, true);\n\t/* !prof.active, thread.prof.active. */\n\tprof_sampling_probe(false);\n\n\tmallctl_prof_active_set(false, true);\n\tmallctl_thread_prof_active_set(true, true);\n\t/* prof.active, thread.prof.active. */\n\tprof_sampling_probe(true);\n\n\t/* Restore settings. */\n\tmallctl_prof_active_set(true, true);\n\tmallctl_thread_prof_active_set(true, false);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_prof_active);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_active.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_thread_active_init:false,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_gdump.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic bool did_prof_dump_open;\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tdid_prof_dump_open = true;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nTEST_BEGIN(test_gdump) {\n\tbool active, gdump, gdump_old;\n\tvoid *p, *q, *r, *s;\n\tsize_t sz;\n\n\ttest_skip_if(!config_prof);\n\n\tactive = true;\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0,\n\t    \"Unexpected mallctl failure while activating profiling\");\n\n\tprof_dump_open = prof_dump_open_intercept;\n\n\tdid_prof_dump_open = false;\n\tp = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n\n\tdid_prof_dump_open = false;\n\tq = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n\n\tgdump = false;\n\tsz = sizeof(gdump_old);\n\tassert_d_eq(mallctl(\"prof.gdump\", (void *)&gdump_old, &sz,\n\t    (void *)&gdump, sizeof(gdump)), 0,\n\t    \"Unexpected mallctl failure while disabling prof.gdump\");\n\tassert(gdump_old);\n\tdid_prof_dump_open = false;\n\tr = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\tassert_false(did_prof_dump_open, \"Unexpected profile dump\");\n\n\tgdump = true;\n\tsz = sizeof(gdump_old);\n\tassert_d_eq(mallctl(\"prof.gdump\", (void *)&gdump_old, &sz,\n\t    (void *)&gdump, sizeof(gdump)), 0,\n\t    \"Unexpected mallctl failure while enabling prof.gdump\");\n\tassert(!gdump_old);\n\tdid_prof_dump_open = false;\n\ts = mallocx((1U << LG_LARGE_MINCLASS), 0);\n\tassert_ptr_not_null(q, \"Unexpected mallocx() failure\");\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n\n\tdallocx(p, 0);\n\tdallocx(q, 0);\n\tdallocx(r, 0);\n\tdallocx(s, 0);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_gdump);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_gdump.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_active:false,prof_gdump:true\"\nfi\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_idump.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic bool did_prof_dump_open;\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tdid_prof_dump_open = true;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nTEST_BEGIN(test_idump) {\n\tbool active;\n\tvoid *p;\n\n\ttest_skip_if(!config_prof);\n\n\tactive = true;\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0,\n\t    \"Unexpected mallctl failure while activating profiling\");\n\n\tprof_dump_open = prof_dump_open_intercept;\n\n\tdid_prof_dump_open = false;\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tdallocx(p, 0);\n\tassert_true(did_prof_dump_open, \"Expected a profile dump\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_idump);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_idump.sh",
    "content": "#!/bin/sh\n\nexport MALLOC_CONF=\"tcache:false\"\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"${MALLOC_CONF},prof:true,prof_accum:true,prof_active:false,lg_prof_sample:0,lg_prof_interval:0\"\nfi\n\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_reset.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic int\nprof_dump_open_intercept(bool propagate_err, const char *filename) {\n\tint fd;\n\n\tfd = open(\"/dev/null\", O_WRONLY);\n\tassert_d_ne(fd, -1, \"Unexpected open() failure\");\n\n\treturn fd;\n}\n\nstatic void\nset_prof_active(bool active) {\n\tassert_d_eq(mallctl(\"prof.active\", NULL, NULL, (void *)&active,\n\t    sizeof(active)), 0, \"Unexpected mallctl failure\");\n}\n\nstatic size_t\nget_lg_prof_sample(void) {\n\tsize_t lg_prof_sample;\n\tsize_t sz = sizeof(size_t);\n\n\tassert_d_eq(mallctl(\"prof.lg_sample\", (void *)&lg_prof_sample, &sz,\n\t    NULL, 0), 0,\n\t    \"Unexpected mallctl failure while reading profiling sample rate\");\n\treturn lg_prof_sample;\n}\n\nstatic void\ndo_prof_reset(size_t lg_prof_sample) {\n\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL,\n\t    (void *)&lg_prof_sample, sizeof(size_t)), 0,\n\t    \"Unexpected mallctl failure while resetting profile data\");\n\tassert_zu_eq(lg_prof_sample, get_lg_prof_sample(),\n\t    \"Expected profile sample rate change\");\n}\n\nTEST_BEGIN(test_prof_reset_basic) {\n\tsize_t lg_prof_sample_orig, lg_prof_sample, lg_prof_sample_next;\n\tsize_t sz;\n\tunsigned i;\n\n\ttest_skip_if(!config_prof);\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"opt.lg_prof_sample\", (void *)&lg_prof_sample_orig,\n\t    &sz, NULL, 0), 0,\n\t    \"Unexpected mallctl failure while reading profiling sample rate\");\n\tassert_zu_eq(lg_prof_sample_orig, 0,\n\t    \"Unexpected profiling sample rate\");\n\tlg_prof_sample = get_lg_prof_sample();\n\tassert_zu_eq(lg_prof_sample_orig, lg_prof_sample,\n\t    \"Unexpected disagreement between \\\"opt.lg_prof_sample\\\" and \"\n\t    \"\\\"prof.lg_sample\\\"\");\n\n\t/* Test simple resets. */\n\tfor (i = 0; i < 2; i++) {\n\t\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0), 0,\n\t\t    \"Unexpected mallctl failure while resetting profile data\");\n\t\tlg_prof_sample = get_lg_prof_sample();\n\t\tassert_zu_eq(lg_prof_sample_orig, lg_prof_sample,\n\t\t    \"Unexpected profile sample rate change\");\n\t}\n\n\t/* Test resets with prof.lg_sample changes. */\n\tlg_prof_sample_next = 1;\n\tfor (i = 0; i < 2; i++) {\n\t\tdo_prof_reset(lg_prof_sample_next);\n\t\tlg_prof_sample = get_lg_prof_sample();\n\t\tassert_zu_eq(lg_prof_sample, lg_prof_sample_next,\n\t\t    \"Expected profile sample rate change\");\n\t\tlg_prof_sample_next = lg_prof_sample_orig;\n\t}\n\n\t/* Make sure the test code restored prof.lg_sample. */\n\tlg_prof_sample = get_lg_prof_sample();\n\tassert_zu_eq(lg_prof_sample_orig, lg_prof_sample,\n\t    \"Unexpected disagreement between \\\"opt.lg_prof_sample\\\" and \"\n\t    \"\\\"prof.lg_sample\\\"\");\n}\nTEST_END\n\nbool prof_dump_header_intercepted = false;\nprof_cnt_t cnt_all_copy = {0, 0, 0, 0};\nstatic bool\nprof_dump_header_intercept(tsdn_t *tsdn, bool propagate_err,\n    const prof_cnt_t *cnt_all) {\n\tprof_dump_header_intercepted = true;\n\tmemcpy(&cnt_all_copy, cnt_all, sizeof(prof_cnt_t));\n\n\treturn false;\n}\n\nTEST_BEGIN(test_prof_reset_cleanup) {\n\tvoid *p;\n\tprof_dump_header_t *prof_dump_header_orig;\n\n\ttest_skip_if(!config_prof);\n\n\tset_prof_active(true);\n\n\tassert_zu_eq(prof_bt_count(), 0, \"Expected 0 backtraces\");\n\tp = mallocx(1, 0);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\tassert_zu_eq(prof_bt_count(), 1, \"Expected 1 backtrace\");\n\n\tprof_dump_header_orig = prof_dump_header;\n\tprof_dump_header = prof_dump_header_intercept;\n\tassert_false(prof_dump_header_intercepted, \"Unexpected intercept\");\n\n\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t    0, \"Unexpected error while dumping heap profile\");\n\tassert_true(prof_dump_header_intercepted, \"Expected intercept\");\n\tassert_u64_eq(cnt_all_copy.curobjs, 1, \"Expected 1 allocation\");\n\n\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected error while resetting heap profile data\");\n\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t    0, \"Unexpected error while dumping heap profile\");\n\tassert_u64_eq(cnt_all_copy.curobjs, 0, \"Expected 0 allocations\");\n\tassert_zu_eq(prof_bt_count(), 1, \"Expected 1 backtrace\");\n\n\tprof_dump_header = prof_dump_header_orig;\n\n\tdallocx(p, 0);\n\tassert_zu_eq(prof_bt_count(), 0, \"Expected 0 backtraces\");\n\n\tset_prof_active(false);\n}\nTEST_END\n\n#define NTHREADS\t\t4\n#define NALLOCS_PER_THREAD\t(1U << 13)\n#define OBJ_RING_BUF_COUNT\t1531\n#define RESET_INTERVAL\t\t(1U << 10)\n#define DUMP_INTERVAL\t\t3677\nstatic void *\nthd_start(void *varg) {\n\tunsigned thd_ind = *(unsigned *)varg;\n\tunsigned i;\n\tvoid *objs[OBJ_RING_BUF_COUNT];\n\n\tmemset(objs, 0, sizeof(objs));\n\n\tfor (i = 0; i < NALLOCS_PER_THREAD; i++) {\n\t\tif (i % RESET_INTERVAL == 0) {\n\t\t\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0),\n\t\t\t    0, \"Unexpected error while resetting heap profile \"\n\t\t\t    \"data\");\n\t\t}\n\n\t\tif (i % DUMP_INTERVAL == 0) {\n\t\t\tassert_d_eq(mallctl(\"prof.dump\", NULL, NULL, NULL, 0),\n\t\t\t    0, \"Unexpected error while dumping heap profile\");\n\t\t}\n\n\t\t{\n\t\t\tvoid **pp = &objs[i % OBJ_RING_BUF_COUNT];\n\t\t\tif (*pp != NULL) {\n\t\t\t\tdallocx(*pp, 0);\n\t\t\t\t*pp = NULL;\n\t\t\t}\n\t\t\t*pp = btalloc(1, thd_ind*NALLOCS_PER_THREAD + i);\n\t\t\tassert_ptr_not_null(*pp,\n\t\t\t    \"Unexpected btalloc() failure\");\n\t\t}\n\t}\n\n\t/* Clean up any remaining objects. */\n\tfor (i = 0; i < OBJ_RING_BUF_COUNT; i++) {\n\t\tvoid **pp = &objs[i % OBJ_RING_BUF_COUNT];\n\t\tif (*pp != NULL) {\n\t\t\tdallocx(*pp, 0);\n\t\t\t*pp = NULL;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_prof_reset) {\n\tsize_t lg_prof_sample_orig;\n\tthd_t thds[NTHREADS];\n\tunsigned thd_args[NTHREADS];\n\tunsigned i;\n\tsize_t bt_count, tdata_count;\n\n\ttest_skip_if(!config_prof);\n\n\tbt_count = prof_bt_count();\n\tassert_zu_eq(bt_count, 0,\n\t    \"Unexpected pre-existing tdata structures\");\n\ttdata_count = prof_tdata_count();\n\n\tlg_prof_sample_orig = get_lg_prof_sample();\n\tdo_prof_reset(5);\n\n\tset_prof_active(true);\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_args[i] = i;\n\t\tthd_create(&thds[i], thd_start, (void *)&thd_args[i]);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n\n\tassert_zu_eq(prof_bt_count(), bt_count,\n\t    \"Unexpected bactrace count change\");\n\tassert_zu_eq(prof_tdata_count(), tdata_count,\n\t    \"Unexpected remaining tdata structures\");\n\n\tset_prof_active(false);\n\n\tdo_prof_reset(lg_prof_sample_orig);\n}\nTEST_END\n#undef NTHREADS\n#undef NALLOCS_PER_THREAD\n#undef OBJ_RING_BUF_COUNT\n#undef RESET_INTERVAL\n#undef DUMP_INTERVAL\n\n/* Test sampling at the same allocation site across resets. */\n#define NITER 10\nTEST_BEGIN(test_xallocx) {\n\tsize_t lg_prof_sample_orig;\n\tunsigned i;\n\tvoid *ptrs[NITER];\n\n\ttest_skip_if(!config_prof);\n\n\tlg_prof_sample_orig = get_lg_prof_sample();\n\tset_prof_active(true);\n\n\t/* Reset profiling. */\n\tdo_prof_reset(0);\n\n\tfor (i = 0; i < NITER; i++) {\n\t\tvoid *p;\n\t\tsize_t sz, nsz;\n\n\t\t/* Reset profiling. */\n\t\tdo_prof_reset(0);\n\n\t\t/* Allocate small object (which will be promoted). */\n\t\tp = ptrs[i] = mallocx(1, 0);\n\t\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\t\t/* Reset profiling. */\n\t\tdo_prof_reset(0);\n\n\t\t/* Perform successful xallocx(). */\n\t\tsz = sallocx(p, 0);\n\t\tassert_zu_eq(xallocx(p, sz, 0, 0), sz,\n\t\t    \"Unexpected xallocx() failure\");\n\n\t\t/* Perform unsuccessful xallocx(). */\n\t\tnsz = nallocx(sz+1, 0);\n\t\tassert_zu_eq(xallocx(p, nsz, 0, 0), sz,\n\t\t    \"Unexpected xallocx() success\");\n\t}\n\n\tfor (i = 0; i < NITER; i++) {\n\t\t/* dallocx. */\n\t\tdallocx(ptrs[i], 0);\n\t}\n\n\tset_prof_active(false);\n\tdo_prof_reset(lg_prof_sample_orig);\n}\nTEST_END\n#undef NITER\n\nint\nmain(void) {\n\t/* Intercept dumping prior to running any tests. */\n\tprof_dump_open = prof_dump_open_intercept;\n\n\treturn test_no_reentrancy(\n\t    test_prof_reset_basic,\n\t    test_prof_reset_cleanup,\n\t    test_prof_reset,\n\t    test_xallocx);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_reset.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_active:false,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_tctx.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_prof_realloc) {\n\ttsdn_t *tsdn;\n\tint flags;\n\tvoid *p, *q;\n\tprof_tctx_t *tctx_p, *tctx_q;\n\tuint64_t curobjs_0, curobjs_1, curobjs_2, curobjs_3;\n\n\ttest_skip_if(!config_prof);\n\n\ttsdn = tsdn_fetch();\n\tflags = MALLOCX_TCACHE_NONE;\n\n\tprof_cnt_all(&curobjs_0, NULL, NULL, NULL);\n\tp = mallocx(1024, flags);\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\ttctx_p = prof_tctx_get(tsdn, p, NULL);\n\tassert_ptr_ne(tctx_p, (prof_tctx_t *)(uintptr_t)1U,\n\t    \"Expected valid tctx\");\n\tprof_cnt_all(&curobjs_1, NULL, NULL, NULL);\n\tassert_u64_eq(curobjs_0 + 1, curobjs_1,\n\t    \"Allocation should have increased sample size\");\n\n\tq = rallocx(p, 2048, flags);\n\tassert_ptr_ne(p, q, \"Expected move\");\n\tassert_ptr_not_null(p, \"Unexpected rmallocx() failure\");\n\ttctx_q = prof_tctx_get(tsdn, q, NULL);\n\tassert_ptr_ne(tctx_q, (prof_tctx_t *)(uintptr_t)1U,\n\t    \"Expected valid tctx\");\n\tprof_cnt_all(&curobjs_2, NULL, NULL, NULL);\n\tassert_u64_eq(curobjs_1, curobjs_2,\n\t    \"Reallocation should not have changed sample size\");\n\n\tdallocx(q, flags);\n\tprof_cnt_all(&curobjs_3, NULL, NULL, NULL);\n\tassert_u64_eq(curobjs_0, curobjs_3,\n\t    \"Sample size should have returned to base level\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_prof_realloc);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_tctx.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,lg_prof_sample:0\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_thread_name.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\nmallctl_thread_name_get_impl(const char *thread_name_expected, const char *func,\n    int line) {\n\tconst char *thread_name_old;\n\tsize_t sz;\n\n\tsz = sizeof(thread_name_old);\n\tassert_d_eq(mallctl(\"thread.prof.name\", (void *)&thread_name_old, &sz,\n\t    NULL, 0), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading thread.prof.name\",\n\t    func, line);\n\tassert_str_eq(thread_name_old, thread_name_expected,\n\t    \"%s():%d: Unexpected thread.prof.name value\", func, line);\n}\n#define mallctl_thread_name_get(a)\t\t\t\t\t\\\n\tmallctl_thread_name_get_impl(a, __func__, __LINE__)\n\nstatic void\nmallctl_thread_name_set_impl(const char *thread_name, const char *func,\n    int line) {\n\tassert_d_eq(mallctl(\"thread.prof.name\", NULL, NULL,\n\t    (void *)&thread_name, sizeof(thread_name)), 0,\n\t    \"%s():%d: Unexpected mallctl failure reading thread.prof.name\",\n\t    func, line);\n\tmallctl_thread_name_get_impl(thread_name, func, line);\n}\n#define mallctl_thread_name_set(a)\t\t\t\t\t\\\n\tmallctl_thread_name_set_impl(a, __func__, __LINE__)\n\nTEST_BEGIN(test_prof_thread_name_validation) {\n\tconst char *thread_name;\n\n\ttest_skip_if(!config_prof);\n\n\tmallctl_thread_name_get(\"\");\n\tmallctl_thread_name_set(\"hi there\");\n\n\t/* NULL input shouldn't be allowed. */\n\tthread_name = NULL;\n\tassert_d_eq(mallctl(\"thread.prof.name\", NULL, NULL,\n\t    (void *)&thread_name, sizeof(thread_name)), EFAULT,\n\t    \"Unexpected mallctl result writing \\\"%s\\\" to thread.prof.name\",\n\t    thread_name);\n\n\t/* '\\n' shouldn't be allowed. */\n\tthread_name = \"hi\\nthere\";\n\tassert_d_eq(mallctl(\"thread.prof.name\", NULL, NULL,\n\t    (void *)&thread_name, sizeof(thread_name)), EFAULT,\n\t    \"Unexpected mallctl result writing \\\"%s\\\" to thread.prof.name\",\n\t    thread_name);\n\n\t/* Simultaneous read/write shouldn't be allowed. */\n\t{\n\t\tconst char *thread_name_old;\n\t\tsize_t sz;\n\n\t\tsz = sizeof(thread_name_old);\n\t\tassert_d_eq(mallctl(\"thread.prof.name\",\n\t\t    (void *)&thread_name_old, &sz, (void *)&thread_name,\n\t\t    sizeof(thread_name)), EPERM,\n\t\t    \"Unexpected mallctl result writing \\\"%s\\\" to \"\n\t\t    \"thread.prof.name\", thread_name);\n\t}\n\n\tmallctl_thread_name_set(\"\");\n}\nTEST_END\n\n#define NTHREADS\t4\n#define NRESET\t\t25\nstatic void *\nthd_start(void *varg) {\n\tunsigned thd_ind = *(unsigned *)varg;\n\tchar thread_name[16] = \"\";\n\tunsigned i;\n\n\tmalloc_snprintf(thread_name, sizeof(thread_name), \"thread %u\", thd_ind);\n\n\tmallctl_thread_name_get(\"\");\n\tmallctl_thread_name_set(thread_name);\n\n\tfor (i = 0; i < NRESET; i++) {\n\t\tassert_d_eq(mallctl(\"prof.reset\", NULL, NULL, NULL, 0), 0,\n\t\t    \"Unexpected error while resetting heap profile data\");\n\t\tmallctl_thread_name_get(thread_name);\n\t}\n\n\tmallctl_thread_name_set(thread_name);\n\tmallctl_thread_name_set(\"\");\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_prof_thread_name_threaded) {\n\tthd_t thds[NTHREADS];\n\tunsigned thd_args[NTHREADS];\n\tunsigned i;\n\n\ttest_skip_if(!config_prof);\n\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_args[i] = i;\n\t\tthd_create(&thds[i], thd_start, (void *)&thd_args[i]);\n\t}\n\tfor (i = 0; i < NTHREADS; i++) {\n\t\tthd_join(thds[i], NULL);\n\t}\n}\nTEST_END\n#undef NTHREADS\n#undef NRESET\n\nint\nmain(void) {\n\treturn test(\n\t    test_prof_thread_name_validation,\n\t    test_prof_thread_name_threaded);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/prof_thread_name.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_prof}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"prof:true,prof_active:false\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/ql.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ql.h\"\n\n/* Number of ring entries, in [2..26]. */\n#define NENTRIES 9\n\ntypedef struct list_s list_t;\ntypedef ql_head(list_t) list_head_t;\n\nstruct list_s {\n\tql_elm(list_t) link;\n\tchar id;\n};\n\nstatic void\ntest_empty_list(list_head_t *head) {\n\tlist_t *t;\n\tunsigned i;\n\n\tassert_ptr_null(ql_first(head), \"Unexpected element for empty list\");\n\tassert_ptr_null(ql_last(head, link),\n\t    \"Unexpected element for empty list\");\n\n\ti = 0;\n\tql_foreach(t, head, link) {\n\t\ti++;\n\t}\n\tassert_u_eq(i, 0, \"Unexpected element for empty list\");\n\n\ti = 0;\n\tql_reverse_foreach(t, head, link) {\n\t\ti++;\n\t}\n\tassert_u_eq(i, 0, \"Unexpected element for empty list\");\n}\n\nTEST_BEGIN(test_ql_empty) {\n\tlist_head_t head;\n\n\tql_new(&head);\n\ttest_empty_list(&head);\n}\nTEST_END\n\nstatic void\ninit_entries(list_t *entries, unsigned nentries) {\n\tunsigned i;\n\n\tfor (i = 0; i < nentries; i++) {\n\t\tentries[i].id = 'a' + i;\n\t\tql_elm_new(&entries[i], link);\n\t}\n}\n\nstatic void\ntest_entries_list(list_head_t *head, list_t *entries, unsigned nentries) {\n\tlist_t *t;\n\tunsigned i;\n\n\tassert_c_eq(ql_first(head)->id, entries[0].id, \"Element id mismatch\");\n\tassert_c_eq(ql_last(head, link)->id, entries[nentries-1].id,\n\t    \"Element id mismatch\");\n\n\ti = 0;\n\tql_foreach(t, head, link) {\n\t\tassert_c_eq(t->id, entries[i].id, \"Element id mismatch\");\n\t\ti++;\n\t}\n\n\ti = 0;\n\tql_reverse_foreach(t, head, link) {\n\t\tassert_c_eq(t->id, entries[nentries-i-1].id,\n\t\t    \"Element id mismatch\");\n\t\ti++;\n\t}\n\n\tfor (i = 0; i < nentries-1; i++) {\n\t\tt = ql_next(head, &entries[i], link);\n\t\tassert_c_eq(t->id, entries[i+1].id, \"Element id mismatch\");\n\t}\n\tassert_ptr_null(ql_next(head, &entries[nentries-1], link),\n\t    \"Unexpected element\");\n\n\tassert_ptr_null(ql_prev(head, &entries[0], link), \"Unexpected element\");\n\tfor (i = 1; i < nentries; i++) {\n\t\tt = ql_prev(head, &entries[i], link);\n\t\tassert_c_eq(t->id, entries[i-1].id, \"Element id mismatch\");\n\t}\n}\n\nTEST_BEGIN(test_ql_tail_insert) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_tail_insert(&head, &entries[i], link);\n\t}\n\n\ttest_entries_list(&head, entries, NENTRIES);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_tail_remove) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_tail_insert(&head, &entries[i], link);\n\t}\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\ttest_entries_list(&head, entries, NENTRIES-i);\n\t\tql_tail_remove(&head, list_t, link);\n\t}\n\ttest_empty_list(&head);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_head_insert) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_head_insert(&head, &entries[NENTRIES-i-1], link);\n\t}\n\n\ttest_entries_list(&head, entries, NENTRIES);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_head_remove) {\n\tlist_head_t head;\n\tlist_t entries[NENTRIES];\n\tunsigned i;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tql_head_insert(&head, &entries[NENTRIES-i-1], link);\n\t}\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\ttest_entries_list(&head, &entries[i], NENTRIES-i);\n\t\tql_head_remove(&head, list_t, link);\n\t}\n\ttest_empty_list(&head);\n}\nTEST_END\n\nTEST_BEGIN(test_ql_insert) {\n\tlist_head_t head;\n\tlist_t entries[8];\n\tlist_t *a, *b, *c, *d, *e, *f, *g, *h;\n\n\tql_new(&head);\n\tinit_entries(entries, sizeof(entries)/sizeof(list_t));\n\ta = &entries[0];\n\tb = &entries[1];\n\tc = &entries[2];\n\td = &entries[3];\n\te = &entries[4];\n\tf = &entries[5];\n\tg = &entries[6];\n\th = &entries[7];\n\n\t/*\n\t * ql_remove(), ql_before_insert(), and ql_after_insert() are used\n\t * internally by other macros that are already tested, so there's no\n\t * need to test them completely.  However, insertion/deletion from the\n\t * middle of lists is not otherwise tested; do so here.\n\t */\n\tql_tail_insert(&head, f, link);\n\tql_before_insert(&head, f, b, link);\n\tql_before_insert(&head, f, c, link);\n\tql_after_insert(f, h, link);\n\tql_after_insert(f, g, link);\n\tql_before_insert(&head, b, a, link);\n\tql_after_insert(c, d, link);\n\tql_before_insert(&head, f, e, link);\n\n\ttest_entries_list(&head, entries, sizeof(entries)/sizeof(list_t));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ql_empty,\n\t    test_ql_tail_insert,\n\t    test_ql_tail_remove,\n\t    test_ql_head_insert,\n\t    test_ql_head_remove,\n\t    test_ql_insert);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/qr.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/qr.h\"\n\n/* Number of ring entries, in [2..26]. */\n#define NENTRIES 9\n/* Split index, in [1..NENTRIES). */\n#define SPLIT_INDEX 5\n\ntypedef struct ring_s ring_t;\n\nstruct ring_s {\n\tqr(ring_t) link;\n\tchar id;\n};\n\nstatic void\ninit_entries(ring_t *entries) {\n\tunsigned i;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tqr_new(&entries[i], link);\n\t\tentries[i].id = 'a' + i;\n\t}\n}\n\nstatic void\ntest_independent_entries(ring_t *entries) {\n\tring_t *t;\n\tunsigned i, j;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tj++;\n\t\t}\n\t\tassert_u_eq(j, 1,\n\t\t    \"Iteration over single-element ring should visit precisely \"\n\t\t    \"one element\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tj++;\n\t\t}\n\t\tassert_u_eq(j, 1,\n\t\t    \"Iteration over single-element ring should visit precisely \"\n\t\t    \"one element\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_next(&entries[i], link);\n\t\tassert_ptr_eq(t, &entries[i],\n\t\t    \"Next element in single-element ring should be same as \"\n\t\t    \"current element\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_prev(&entries[i], link);\n\t\tassert_ptr_eq(t, &entries[i],\n\t\t    \"Previous element in single-element ring should be same as \"\n\t\t    \"current element\");\n\t}\n}\n\nTEST_BEGIN(test_qr_one) {\n\tring_t entries[NENTRIES];\n\n\tinit_entries(entries);\n\ttest_independent_entries(entries);\n}\nTEST_END\n\nstatic void\ntest_entries_ring(ring_t *entries) {\n\tring_t *t;\n\tunsigned i, j;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(i+j) % NENTRIES].id,\n\t\t\t    \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(NENTRIES+i-j-1) %\n\t\t\t    NENTRIES].id, \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_next(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(i+1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_prev(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n}\n\nTEST_BEGIN(test_qr_after_insert) {\n\tring_t entries[NENTRIES];\n\tunsigned i;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_after_insert(&entries[i - 1], &entries[i], link);\n\t}\n\ttest_entries_ring(entries);\n}\nTEST_END\n\nTEST_BEGIN(test_qr_remove) {\n\tring_t entries[NENTRIES];\n\tring_t *t;\n\tunsigned i, j;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_after_insert(&entries[i - 1], &entries[i], link);\n\t}\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[i+j].id,\n\t\t\t    \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[NENTRIES - 1 - j].id,\n\t\t\t\"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t\tqr_remove(&entries[i], link);\n\t}\n\ttest_independent_entries(entries);\n}\nTEST_END\n\nTEST_BEGIN(test_qr_before_insert) {\n\tring_t entries[NENTRIES];\n\tring_t *t;\n\tunsigned i, j;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_before_insert(&entries[i - 1], &entries[i], link);\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(NENTRIES+i-j) %\n\t\t\t    NENTRIES].id, \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_reverse_foreach(t, &entries[i], link) {\n\t\t\tassert_c_eq(t->id, entries[(i+j+1) % NENTRIES].id,\n\t\t\t    \"Element id mismatch\");\n\t\t\tj++;\n\t\t}\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_next(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tt = qr_prev(&entries[i], link);\n\t\tassert_c_eq(t->id, entries[(i+1) % NENTRIES].id,\n\t\t    \"Element id mismatch\");\n\t}\n}\nTEST_END\n\nstatic void\ntest_split_entries(ring_t *entries) {\n\tring_t *t;\n\tunsigned i, j;\n\n\tfor (i = 0; i < NENTRIES; i++) {\n\t\tj = 0;\n\t\tqr_foreach(t, &entries[i], link) {\n\t\t\tif (i < SPLIT_INDEX) {\n\t\t\t\tassert_c_eq(t->id,\n\t\t\t\t    entries[(i+j) % SPLIT_INDEX].id,\n\t\t\t\t    \"Element id mismatch\");\n\t\t\t} else {\n\t\t\t\tassert_c_eq(t->id, entries[(i+j-SPLIT_INDEX) %\n\t\t\t\t    (NENTRIES-SPLIT_INDEX) + SPLIT_INDEX].id,\n\t\t\t\t    \"Element id mismatch\");\n\t\t\t}\n\t\t\tj++;\n\t\t}\n\t}\n}\n\nTEST_BEGIN(test_qr_meld_split) {\n\tring_t entries[NENTRIES];\n\tunsigned i;\n\n\tinit_entries(entries);\n\tfor (i = 1; i < NENTRIES; i++) {\n\t\tqr_after_insert(&entries[i - 1], &entries[i], link);\n\t}\n\n\tqr_split(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_split_entries(entries);\n\n\tqr_meld(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_entries_ring(entries);\n\n\tqr_meld(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_split_entries(entries);\n\n\tqr_split(&entries[0], &entries[SPLIT_INDEX], ring_t, link);\n\ttest_entries_ring(entries);\n\n\tqr_split(&entries[0], &entries[0], ring_t, link);\n\ttest_entries_ring(entries);\n\n\tqr_meld(&entries[0], &entries[0], ring_t, link);\n\ttest_entries_ring(entries);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_qr_one,\n\t    test_qr_after_insert,\n\t    test_qr_remove,\n\t    test_qr_before_insert,\n\t    test_qr_meld_split);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/rb.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/rb.h\"\n\n#define rbtn_black_height(a_type, a_field, a_rbt, r_height) do {\t\\\n\ta_type *rbp_bh_t;\t\t\t\t\t\t\\\n\tfor (rbp_bh_t = (a_rbt)->rbt_root, (r_height) = 0; rbp_bh_t !=\t\\\n\t    NULL; rbp_bh_t = rbtn_left_get(a_type, a_field,\t\t\\\n\t    rbp_bh_t)) {\t\t\t\t\t\t\\\n\t\tif (!rbtn_red_get(a_type, a_field, rbp_bh_t)) {\t\t\\\n\t\t(r_height)++;\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\ntypedef struct node_s node_t;\n\nstruct node_s {\n#define NODE_MAGIC 0x9823af7e\n\tuint32_t magic;\n\trb_node(node_t) link;\n\tuint64_t key;\n};\n\nstatic int\nnode_cmp(const node_t *a, const node_t *b) {\n\tint ret;\n\n\tassert_u32_eq(a->magic, NODE_MAGIC, \"Bad magic\");\n\tassert_u32_eq(b->magic, NODE_MAGIC, \"Bad magic\");\n\n\tret = (a->key > b->key) - (a->key < b->key);\n\tif (ret == 0) {\n\t\t/*\n\t\t * Duplicates are not allowed in the tree, so force an\n\t\t * arbitrary ordering for non-identical items with equal keys.\n\t\t */\n\t\tret = (((uintptr_t)a) > ((uintptr_t)b))\n\t\t    - (((uintptr_t)a) < ((uintptr_t)b));\n\t}\n\treturn ret;\n}\n\ntypedef rb_tree(node_t) tree_t;\nrb_gen(static, tree_, tree_t, node_t, link, node_cmp);\n\nTEST_BEGIN(test_rb_empty) {\n\ttree_t tree;\n\tnode_t key;\n\n\ttree_new(&tree);\n\n\tassert_true(tree_empty(&tree), \"Tree should be empty\");\n\tassert_ptr_null(tree_first(&tree), \"Unexpected node\");\n\tassert_ptr_null(tree_last(&tree), \"Unexpected node\");\n\n\tkey.key = 0;\n\tkey.magic = NODE_MAGIC;\n\tassert_ptr_null(tree_search(&tree, &key), \"Unexpected node\");\n\n\tkey.key = 0;\n\tkey.magic = NODE_MAGIC;\n\tassert_ptr_null(tree_nsearch(&tree, &key), \"Unexpected node\");\n\n\tkey.key = 0;\n\tkey.magic = NODE_MAGIC;\n\tassert_ptr_null(tree_psearch(&tree, &key), \"Unexpected node\");\n}\nTEST_END\n\nstatic unsigned\ntree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {\n\tunsigned ret = 0;\n\tnode_t *left_node;\n\tnode_t *right_node;\n\n\tif (node == NULL) {\n\t\treturn ret;\n\t}\n\n\tleft_node = rbtn_left_get(node_t, link, node);\n\tright_node = rbtn_right_get(node_t, link, node);\n\n\tif (!rbtn_red_get(node_t, link, node)) {\n\t\tblack_depth++;\n\t}\n\n\t/* Red nodes must be interleaved with black nodes. */\n\tif (rbtn_red_get(node_t, link, node)) {\n\t\tif (left_node != NULL) {\n\t\t\tassert_false(rbtn_red_get(node_t, link, left_node),\n\t\t\t\t\"Node should be black\");\n\t\t}\n\t\tif (right_node != NULL) {\n\t\t\tassert_false(rbtn_red_get(node_t, link, right_node),\n\t\t\t    \"Node should be black\");\n\t\t}\n\t}\n\n\t/* Self. */\n\tassert_u32_eq(node->magic, NODE_MAGIC, \"Bad magic\");\n\n\t/* Left subtree. */\n\tif (left_node != NULL) {\n\t\tret += tree_recurse(left_node, black_height, black_depth);\n\t} else {\n\t\tret += (black_depth != black_height);\n\t}\n\n\t/* Right subtree. */\n\tif (right_node != NULL) {\n\t\tret += tree_recurse(right_node, black_height, black_depth);\n\t} else {\n\t\tret += (black_depth != black_height);\n\t}\n\n\treturn ret;\n}\n\nstatic node_t *\ntree_iterate_cb(tree_t *tree, node_t *node, void *data) {\n\tunsigned *i = (unsigned *)data;\n\tnode_t *search_node;\n\n\tassert_u32_eq(node->magic, NODE_MAGIC, \"Bad magic\");\n\n\t/* Test rb_search(). */\n\tsearch_node = tree_search(tree, node);\n\tassert_ptr_eq(search_node, node,\n\t    \"tree_search() returned unexpected node\");\n\n\t/* Test rb_nsearch(). */\n\tsearch_node = tree_nsearch(tree, node);\n\tassert_ptr_eq(search_node, node,\n\t    \"tree_nsearch() returned unexpected node\");\n\n\t/* Test rb_psearch(). */\n\tsearch_node = tree_psearch(tree, node);\n\tassert_ptr_eq(search_node, node,\n\t    \"tree_psearch() returned unexpected node\");\n\n\t(*i)++;\n\n\treturn NULL;\n}\n\nstatic unsigned\ntree_iterate(tree_t *tree) {\n\tunsigned i;\n\n\ti = 0;\n\ttree_iter(tree, NULL, tree_iterate_cb, (void *)&i);\n\n\treturn i;\n}\n\nstatic unsigned\ntree_iterate_reverse(tree_t *tree) {\n\tunsigned i;\n\n\ti = 0;\n\ttree_reverse_iter(tree, NULL, tree_iterate_cb, (void *)&i);\n\n\treturn i;\n}\n\nstatic void\nnode_remove(tree_t *tree, node_t *node, unsigned nnodes) {\n\tnode_t *search_node;\n\tunsigned black_height, imbalances;\n\n\ttree_remove(tree, node);\n\n\t/* Test rb_nsearch(). */\n\tsearch_node = tree_nsearch(tree, node);\n\tif (search_node != NULL) {\n\t\tassert_u64_ge(search_node->key, node->key,\n\t\t    \"Key ordering error\");\n\t}\n\n\t/* Test rb_psearch(). */\n\tsearch_node = tree_psearch(tree, node);\n\tif (search_node != NULL) {\n\t\tassert_u64_le(search_node->key, node->key,\n\t\t    \"Key ordering error\");\n\t}\n\n\tnode->magic = 0;\n\n\trbtn_black_height(node_t, link, tree, black_height);\n\timbalances = tree_recurse(tree->rbt_root, black_height, 0);\n\tassert_u_eq(imbalances, 0, \"Tree is unbalanced\");\n\tassert_u_eq(tree_iterate(tree), nnodes-1,\n\t    \"Unexpected node iteration count\");\n\tassert_u_eq(tree_iterate_reverse(tree), nnodes-1,\n\t    \"Unexpected node iteration count\");\n}\n\nstatic node_t *\nremove_iterate_cb(tree_t *tree, node_t *node, void *data) {\n\tunsigned *nnodes = (unsigned *)data;\n\tnode_t *ret = tree_next(tree, node);\n\n\tnode_remove(tree, node, *nnodes);\n\n\treturn ret;\n}\n\nstatic node_t *\nremove_reverse_iterate_cb(tree_t *tree, node_t *node, void *data) {\n\tunsigned *nnodes = (unsigned *)data;\n\tnode_t *ret = tree_prev(tree, node);\n\n\tnode_remove(tree, node, *nnodes);\n\n\treturn ret;\n}\n\nstatic void\ndestroy_cb(node_t *node, void *data) {\n\tunsigned *nnodes = (unsigned *)data;\n\n\tassert_u_gt(*nnodes, 0, \"Destruction removed too many nodes\");\n\t(*nnodes)--;\n}\n\nTEST_BEGIN(test_rb_random) {\n#define NNODES 25\n#define NBAGS 250\n#define SEED 42\n\tsfmt_t *sfmt;\n\tuint64_t bag[NNODES];\n\ttree_t tree;\n\tnode_t nodes[NNODES];\n\tunsigned i, j, k, black_height, imbalances;\n\n\tsfmt = init_gen_rand(SEED);\n\tfor (i = 0; i < NBAGS; i++) {\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\t/* Insert in order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = j;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t/* Insert in reverse order. */\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = NNODES - j - 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfor (j = 0; j < NNODES; j++) {\n\t\t\t\tbag[j] = gen_rand64_range(sfmt, NNODES);\n\t\t\t}\n\t\t}\n\n\t\tfor (j = 1; j <= NNODES; j++) {\n\t\t\t/* Initialize tree and nodes. */\n\t\t\ttree_new(&tree);\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\tnodes[k].magic = NODE_MAGIC;\n\t\t\t\tnodes[k].key = bag[k];\n\t\t\t}\n\n\t\t\t/* Insert nodes. */\n\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\ttree_insert(&tree, &nodes[k]);\n\n\t\t\t\trbtn_black_height(node_t, link, &tree,\n\t\t\t\t    black_height);\n\t\t\t\timbalances = tree_recurse(tree.rbt_root,\n\t\t\t\t    black_height, 0);\n\t\t\t\tassert_u_eq(imbalances, 0,\n\t\t\t\t    \"Tree is unbalanced\");\n\n\t\t\t\tassert_u_eq(tree_iterate(&tree), k+1,\n\t\t\t\t    \"Unexpected node iteration count\");\n\t\t\t\tassert_u_eq(tree_iterate_reverse(&tree), k+1,\n\t\t\t\t    \"Unexpected node iteration count\");\n\n\t\t\t\tassert_false(tree_empty(&tree),\n\t\t\t\t    \"Tree should not be empty\");\n\t\t\t\tassert_ptr_not_null(tree_first(&tree),\n\t\t\t\t    \"Tree should not be empty\");\n\t\t\t\tassert_ptr_not_null(tree_last(&tree),\n\t\t\t\t    \"Tree should not be empty\");\n\n\t\t\t\ttree_next(&tree, &nodes[k]);\n\t\t\t\ttree_prev(&tree, &nodes[k]);\n\t\t\t}\n\n\t\t\t/* Remove nodes. */\n\t\t\tswitch (i % 5) {\n\t\t\tcase 0:\n\t\t\t\tfor (k = 0; k < j; k++) {\n\t\t\t\t\tnode_remove(&tree, &nodes[k], j - k);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tfor (k = j; k > 0; k--) {\n\t\t\t\t\tnode_remove(&tree, &nodes[k-1], k);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2: {\n\t\t\t\tnode_t *start;\n\t\t\t\tunsigned nnodes = j;\n\n\t\t\t\tstart = NULL;\n\t\t\t\tdo {\n\t\t\t\t\tstart = tree_iter(&tree, start,\n\t\t\t\t\t    remove_iterate_cb, (void *)&nnodes);\n\t\t\t\t\tnnodes--;\n\t\t\t\t} while (start != NULL);\n\t\t\t\tassert_u_eq(nnodes, 0,\n\t\t\t\t    \"Removal terminated early\");\n\t\t\t\tbreak;\n\t\t\t} case 3: {\n\t\t\t\tnode_t *start;\n\t\t\t\tunsigned nnodes = j;\n\n\t\t\t\tstart = NULL;\n\t\t\t\tdo {\n\t\t\t\t\tstart = tree_reverse_iter(&tree, start,\n\t\t\t\t\t    remove_reverse_iterate_cb,\n\t\t\t\t\t    (void *)&nnodes);\n\t\t\t\t\tnnodes--;\n\t\t\t\t} while (start != NULL);\n\t\t\t\tassert_u_eq(nnodes, 0,\n\t\t\t\t    \"Removal terminated early\");\n\t\t\t\tbreak;\n\t\t\t} case 4: {\n\t\t\t\tunsigned nnodes = j;\n\t\t\t\ttree_destroy(&tree, destroy_cb, &nnodes);\n\t\t\t\tassert_u_eq(nnodes, 0,\n\t\t\t\t    \"Destruction terminated early\");\n\t\t\t\tbreak;\n\t\t\t} default:\n\t\t\t\tnot_reached();\n\t\t\t}\n\t\t}\n\t}\n\tfini_gen_rand(sfmt);\n#undef NNODES\n#undef NBAGS\n#undef SEED\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_rb_empty,\n\t    test_rb_random);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/retained.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/spin.h\"\n\nstatic unsigned\t\tarena_ind;\nstatic size_t\t\tsz;\nstatic size_t\t\tesz;\n#define NEPOCHS\t\t8\n#define PER_THD_NALLOCS\t1\nstatic atomic_u_t\tepoch;\nstatic atomic_u_t\tnfinished;\n\nstatic unsigned\ndo_arena_create(extent_hooks_t *h) {\n\tunsigned arena_ind;\n\tsize_t sz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz,\n\t    (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,\n\t    \"Unexpected mallctl() failure\");\n\treturn arena_ind;\n}\n\nstatic void\ndo_arena_destroy(unsigned arena_ind) {\n\tsize_t mib[3];\n\tsize_t miblen;\n\n\tmiblen = sizeof(mib)/sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arena.0.destroy\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() failure\");\n\tmib[1] = (size_t)arena_ind;\n\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctlbymib() failure\");\n}\n\nstatic void\ndo_refresh(void) {\n\tuint64_t epoch = 1;\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch,\n\t    sizeof(epoch)), 0, \"Unexpected mallctl() failure\");\n}\n\nstatic size_t\ndo_get_size_impl(const char *cmd, unsigned arena_ind) {\n\tsize_t mib[4];\n\tsize_t miblen = sizeof(mib) / sizeof(size_t);\n\tsize_t z = sizeof(size_t);\n\n\tassert_d_eq(mallctlnametomib(cmd, mib, &miblen),\n\t    0, \"Unexpected mallctlnametomib(\\\"%s\\\", ...) failure\", cmd);\n\tmib[2] = arena_ind;\n\tsize_t size;\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &z, NULL, 0),\n\t    0, \"Unexpected mallctlbymib([\\\"%s\\\"], ...) failure\", cmd);\n\n\treturn size;\n}\n\nstatic size_t\ndo_get_active(unsigned arena_ind) {\n\treturn do_get_size_impl(\"stats.arenas.0.pactive\", arena_ind) * PAGE;\n}\n\nstatic size_t\ndo_get_mapped(unsigned arena_ind) {\n\treturn do_get_size_impl(\"stats.arenas.0.mapped\", arena_ind);\n}\n\nstatic void *\nthd_start(void *arg) {\n\tfor (unsigned next_epoch = 1; next_epoch < NEPOCHS; next_epoch++) {\n\t\t/* Busy-wait for next epoch. */\n\t\tunsigned cur_epoch;\n\t\tspin_t spinner = SPIN_INITIALIZER;\n\t\twhile ((cur_epoch = atomic_load_u(&epoch, ATOMIC_ACQUIRE)) !=\n\t\t    next_epoch) {\n\t\t\tspin_adaptive(&spinner);\n\t\t}\n\t\tassert_u_eq(cur_epoch, next_epoch, \"Unexpected epoch\");\n\n\t\t/*\n\t\t * Allocate.  The main thread will reset the arena, so there's\n\t\t * no need to deallocate.\n\t\t */\n\t\tfor (unsigned i = 0; i < PER_THD_NALLOCS; i++) {\n\t\t\tvoid *p = mallocx(sz, MALLOCX_ARENA(arena_ind) |\n\t\t\t    MALLOCX_TCACHE_NONE\n\t\t\t    );\n\t\t\tassert_ptr_not_null(p,\n\t\t\t    \"Unexpected mallocx() failure\\n\");\n\t\t}\n\n\t\t/* Let the main thread know we've finished this iteration. */\n\t\tatomic_fetch_add_u(&nfinished, 1, ATOMIC_RELEASE);\n\t}\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_retained) {\n\ttest_skip_if(!config_stats);\n\n\tarena_ind = do_arena_create(NULL);\n\tsz = nallocx(HUGEPAGE, 0);\n\tesz = sz + sz_large_pad;\n\n\tatomic_store_u(&epoch, 0, ATOMIC_RELAXED);\n\n\tunsigned nthreads = ncpus * 2;\n\tVARIABLE_ARRAY(thd_t, threads, nthreads);\n\tfor (unsigned i = 0; i < nthreads; i++) {\n\t\tthd_create(&threads[i], thd_start, NULL);\n\t}\n\n\tfor (unsigned e = 1; e < NEPOCHS; e++) {\n\t\tatomic_store_u(&nfinished, 0, ATOMIC_RELEASE);\n\t\tatomic_store_u(&epoch, e, ATOMIC_RELEASE);\n\n\t\t/* Wait for threads to finish allocating. */\n\t\tspin_t spinner = SPIN_INITIALIZER;\n\t\twhile (atomic_load_u(&nfinished, ATOMIC_ACQUIRE) < nthreads) {\n\t\t\tspin_adaptive(&spinner);\n\t\t}\n\n\t\t/*\n\t\t * Assert that retained is no more than the sum of size classes\n\t\t * that should have been used to satisfy the worker threads'\n\t\t * requests, discounting per growth fragmentation.\n\t\t */\n\t\tdo_refresh();\n\n\t\tsize_t allocated = esz * nthreads * PER_THD_NALLOCS;\n\t\tsize_t active = do_get_active(arena_ind);\n\t\tassert_zu_le(allocated, active, \"Unexpected active memory\");\n\t\tsize_t mapped = do_get_mapped(arena_ind);\n\t\tassert_zu_le(active, mapped, \"Unexpected mapped memory\");\n\n\t\tarena_t *arena = arena_get(tsdn_fetch(), arena_ind, false);\n\t\tsize_t usable = 0;\n\t\tsize_t fragmented = 0;\n\t\tfor (pszind_t pind = sz_psz2ind(HUGEPAGE); pind <\n\t\t    arena->extent_grow_next; pind++) {\n\t\t\tsize_t psz = sz_pind2sz(pind);\n\t\t\tsize_t psz_fragmented = psz % esz;\n\t\t\tsize_t psz_usable = psz - psz_fragmented;\n\t\t\t/*\n\t\t\t * Only consider size classes that wouldn't be skipped.\n\t\t\t */\n\t\t\tif (psz_usable > 0) {\n\t\t\t\tassert_zu_lt(usable, allocated,\n\t\t\t\t    \"Excessive retained memory \"\n\t\t\t\t    \"(%#zx[+%#zx] > %#zx)\", usable, psz_usable,\n\t\t\t\t    allocated);\n\t\t\t\tfragmented += psz_fragmented;\n\t\t\t\tusable += psz_usable;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Clean up arena.  Destroying and recreating the arena\n\t\t * is simpler that specifying extent hooks that deallocate\n\t\t * (rather than retaining) during reset.\n\t\t */\n\t\tdo_arena_destroy(arena_ind);\n\t\tassert_u_eq(do_arena_create(NULL), arena_ind,\n\t\t    \"Unexpected arena index\");\n\t}\n\n\tfor (unsigned i = 0; i < nthreads; i++) {\n\t\tthd_join(threads[i], NULL);\n\t}\n\n\tdo_arena_destroy(arena_ind);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_retained);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/rtree.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/rtree.h\"\n\nrtree_node_alloc_t *rtree_node_alloc_orig;\nrtree_node_dalloc_t *rtree_node_dalloc_orig;\nrtree_leaf_alloc_t *rtree_leaf_alloc_orig;\nrtree_leaf_dalloc_t *rtree_leaf_dalloc_orig;\n\n/* Potentially too large to safely place on the stack. */\nrtree_t test_rtree;\n\nstatic rtree_node_elm_t *\nrtree_node_alloc_intercept(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\trtree_node_elm_t *node;\n\n\tif (rtree != &test_rtree) {\n\t\treturn rtree_node_alloc_orig(tsdn, rtree, nelms);\n\t}\n\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\tnode = (rtree_node_elm_t *)calloc(nelms, sizeof(rtree_node_elm_t));\n\tassert_ptr_not_null(node, \"Unexpected calloc() failure\");\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\n\treturn node;\n}\n\nstatic void\nrtree_node_dalloc_intercept(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_node_elm_t *node) {\n\tif (rtree != &test_rtree) {\n\t\trtree_node_dalloc_orig(tsdn, rtree, node);\n\t\treturn;\n\t}\n\n\tfree(node);\n}\n\nstatic rtree_leaf_elm_t *\nrtree_leaf_alloc_intercept(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {\n\trtree_leaf_elm_t *leaf;\n\n\tif (rtree != &test_rtree) {\n\t\treturn rtree_leaf_alloc_orig(tsdn, rtree, nelms);\n\t}\n\n\tmalloc_mutex_unlock(tsdn, &rtree->init_lock);\n\tleaf = (rtree_leaf_elm_t *)calloc(nelms, sizeof(rtree_leaf_elm_t));\n\tassert_ptr_not_null(leaf, \"Unexpected calloc() failure\");\n\tmalloc_mutex_lock(tsdn, &rtree->init_lock);\n\n\treturn leaf;\n}\n\nstatic void\nrtree_leaf_dalloc_intercept(tsdn_t *tsdn, rtree_t *rtree,\n    rtree_leaf_elm_t *leaf) {\n\tif (rtree != &test_rtree) {\n\t\trtree_leaf_dalloc_orig(tsdn, rtree, leaf);\n\t\treturn;\n\t}\n\n\tfree(leaf);\n}\n\nTEST_BEGIN(test_rtree_read_empty) {\n\ttsdn_t *tsdn;\n\n\ttsdn = tsdn_fetch();\n\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx, PAGE,\n\t    false), \"rtree_extent_read() should return NULL for empty tree\");\n\trtree_delete(tsdn, rtree);\n}\nTEST_END\n\n#undef NTHREADS\n#undef NITERS\n#undef SEED\n\nTEST_BEGIN(test_rtree_extrema) {\n\textent_t extent_a, extent_b;\n\textent_init(&extent_a, NULL, NULL, LARGE_MINCLASS, false,\n\t    sz_size2index(LARGE_MINCLASS), 0, extent_state_active, false,\n\t    false, true);\n\textent_init(&extent_b, NULL, NULL, 0, false, NSIZES, 0,\n\t    extent_state_active, false, false, true);\n\n\ttsdn_t *tsdn = tsdn_fetch();\n\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\n\tassert_false(rtree_write(tsdn, rtree, &rtree_ctx, PAGE, &extent_a,\n\t    extent_szind_get(&extent_a), extent_slab_get(&extent_a)),\n\t    \"Unexpected rtree_write() failure\");\n\trtree_szind_slab_update(tsdn, rtree, &rtree_ctx, PAGE,\n\t    extent_szind_get(&extent_a), extent_slab_get(&extent_a));\n\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx, PAGE, true),\n\t    &extent_a,\n\t    \"rtree_extent_read() should return previously set value\");\n\n\tassert_false(rtree_write(tsdn, rtree, &rtree_ctx, ~((uintptr_t)0),\n\t    &extent_b, extent_szind_get_maybe_invalid(&extent_b),\n\t    extent_slab_get(&extent_b)), \"Unexpected rtree_write() failure\");\n\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t    ~((uintptr_t)0), true), &extent_b,\n\t    \"rtree_extent_read() should return previously set value\");\n\n\trtree_delete(tsdn, rtree);\n}\nTEST_END\n\nTEST_BEGIN(test_rtree_bits) {\n\ttsdn_t *tsdn = tsdn_fetch();\n\n\tuintptr_t keys[] = {PAGE, PAGE + 1,\n\t    PAGE + (((uintptr_t)1) << LG_PAGE) - 1};\n\n\textent_t extent;\n\textent_init(&extent, NULL, NULL, 0, false, NSIZES, 0,\n\t    extent_state_active, false, false, true);\n\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\n\tfor (unsigned i = 0; i < sizeof(keys)/sizeof(uintptr_t); i++) {\n\t\tassert_false(rtree_write(tsdn, rtree, &rtree_ctx, keys[i],\n\t\t    &extent, NSIZES, false),\n\t\t    \"Unexpected rtree_write() failure\");\n\t\tfor (unsigned j = 0; j < sizeof(keys)/sizeof(uintptr_t); j++) {\n\t\t\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t\t    keys[j], true), &extent,\n\t\t\t    \"rtree_extent_read() should return previously set \"\n\t\t\t    \"value and ignore insignificant key bits; i=%u, \"\n\t\t\t    \"j=%u, set key=%#\"FMTxPTR\", get key=%#\"FMTxPTR, i,\n\t\t\t    j, keys[i], keys[j]);\n\t\t}\n\t\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    (((uintptr_t)2) << LG_PAGE), false),\n\t\t    \"Only leftmost rtree leaf should be set; i=%u\", i);\n\t\trtree_clear(tsdn, rtree, &rtree_ctx, keys[i]);\n\t}\n\n\trtree_delete(tsdn, rtree);\n}\nTEST_END\n\nTEST_BEGIN(test_rtree_random) {\n#define NSET 16\n#define SEED 42\n\tsfmt_t *sfmt = init_gen_rand(SEED);\n\ttsdn_t *tsdn = tsdn_fetch();\n\tuintptr_t keys[NSET];\n\trtree_t *rtree = &test_rtree;\n\trtree_ctx_t rtree_ctx;\n\trtree_ctx_data_init(&rtree_ctx);\n\n\textent_t extent;\n\textent_init(&extent, NULL, NULL, 0, false, NSIZES, 0,\n\t    extent_state_active, false, false, true);\n\n\tassert_false(rtree_new(rtree, false), \"Unexpected rtree_new() failure\");\n\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\tkeys[i] = (uintptr_t)gen_rand64(sfmt);\n\t\trtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree,\n\t\t    &rtree_ctx, keys[i], false, true);\n\t\tassert_ptr_not_null(elm,\n\t\t    \"Unexpected rtree_leaf_elm_lookup() failure\");\n\t\trtree_leaf_elm_write(tsdn, rtree, elm, &extent, NSIZES, false);\n\t\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true), &extent,\n\t\t    \"rtree_extent_read() should return previously set value\");\n\t}\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\tassert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true), &extent,\n\t\t    \"rtree_extent_read() should return previously set value, \"\n\t\t    \"i=%u\", i);\n\t}\n\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\trtree_clear(tsdn, rtree, &rtree_ctx, keys[i]);\n\t\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true),\n\t\t   \"rtree_extent_read() should return previously set value\");\n\t}\n\tfor (unsigned i = 0; i < NSET; i++) {\n\t\tassert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,\n\t\t    keys[i], true),\n\t\t    \"rtree_extent_read() should return previously set value\");\n\t}\n\n\trtree_delete(tsdn, rtree);\n\tfini_gen_rand(sfmt);\n#undef NSET\n#undef SEED\n}\nTEST_END\n\nint\nmain(void) {\n\trtree_node_alloc_orig = rtree_node_alloc;\n\trtree_node_alloc = rtree_node_alloc_intercept;\n\trtree_node_dalloc_orig = rtree_node_dalloc;\n\trtree_node_dalloc = rtree_node_dalloc_intercept;\n\trtree_leaf_alloc_orig = rtree_leaf_alloc;\n\trtree_leaf_alloc = rtree_leaf_alloc_intercept;\n\trtree_leaf_dalloc_orig = rtree_leaf_dalloc;\n\trtree_leaf_dalloc = rtree_leaf_dalloc_intercept;\n\n\treturn test(\n\t    test_rtree_read_empty,\n\t    test_rtree_extrema,\n\t    test_rtree_bits,\n\t    test_rtree_random);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/size_classes.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic size_t\nget_max_size_class(void) {\n\tunsigned nlextents;\n\tsize_t mib[4];\n\tsize_t sz, miblen, max_size_class;\n\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.nlextents\", (void *)&nlextents, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() error\");\n\n\tmiblen = sizeof(mib) / sizeof(size_t);\n\tassert_d_eq(mallctlnametomib(\"arenas.lextent.0.size\", mib, &miblen), 0,\n\t    \"Unexpected mallctlnametomib() error\");\n\tmib[2] = nlextents - 1;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,\n\t    NULL, 0), 0, \"Unexpected mallctlbymib() error\");\n\n\treturn max_size_class;\n}\n\nTEST_BEGIN(test_size_classes) {\n\tsize_t size_class, max_size_class;\n\tszind_t index, max_index;\n\n\tmax_size_class = get_max_size_class();\n\tmax_index = sz_size2index(max_size_class);\n\n\tfor (index = 0, size_class = sz_index2size(index); index < max_index ||\n\t    size_class < max_size_class; index++, size_class =\n\t    sz_index2size(index)) {\n\t\tassert_true(index < max_index,\n\t\t    \"Loop conditionals should be equivalent; index=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", index, size_class, size_class);\n\t\tassert_true(size_class < max_size_class,\n\t\t    \"Loop conditionals should be equivalent; index=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", index, size_class, size_class);\n\n\t\tassert_u_eq(index, sz_size2index(size_class),\n\t\t    \"sz_size2index() does not reverse sz_index2size(): index=%u\"\n\t\t    \" --> size_class=%zu --> index=%u --> size_class=%zu\",\n\t\t    index, size_class, sz_size2index(size_class),\n\t\t    sz_index2size(sz_size2index(size_class)));\n\t\tassert_zu_eq(size_class,\n\t\t    sz_index2size(sz_size2index(size_class)),\n\t\t    \"sz_index2size() does not reverse sz_size2index(): index=%u\"\n\t\t    \" --> size_class=%zu --> index=%u --> size_class=%zu\",\n\t\t    index, size_class, sz_size2index(size_class),\n\t\t    sz_index2size(sz_size2index(size_class)));\n\n\t\tassert_u_eq(index+1, sz_size2index(size_class+1),\n\t\t    \"Next size_class does not round up properly\");\n\n\t\tassert_zu_eq(size_class, (index > 0) ?\n\t\t    sz_s2u(sz_index2size(index-1)+1) : sz_s2u(1),\n\t\t    \"sz_s2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_s2u(size_class-1),\n\t\t    \"sz_s2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_s2u(size_class),\n\t\t    \"sz_s2u() does not compute same size class\");\n\t\tassert_zu_eq(sz_s2u(size_class+1), sz_index2size(index+1),\n\t\t    \"sz_s2u() does not round up to next size class\");\n\t}\n\n\tassert_u_eq(index, sz_size2index(sz_index2size(index)),\n\t    \"sz_size2index() does not reverse sz_index2size()\");\n\tassert_zu_eq(max_size_class, sz_index2size(\n\t    sz_size2index(max_size_class)),\n\t    \"sz_index2size() does not reverse sz_size2index()\");\n\n\tassert_zu_eq(size_class, sz_s2u(sz_index2size(index-1)+1),\n\t    \"sz_s2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_s2u(size_class-1),\n\t    \"sz_s2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_s2u(size_class),\n\t    \"sz_s2u() does not compute same size class\");\n}\nTEST_END\n\nTEST_BEGIN(test_psize_classes) {\n\tsize_t size_class, max_psz;\n\tpszind_t pind, max_pind;\n\n\tmax_psz = get_max_size_class() + PAGE;\n\tmax_pind = sz_psz2ind(max_psz);\n\n\tfor (pind = 0, size_class = sz_pind2sz(pind);\n\t    pind < max_pind || size_class < max_psz;\n\t    pind++, size_class = sz_pind2sz(pind)) {\n\t\tassert_true(pind < max_pind,\n\t\t    \"Loop conditionals should be equivalent; pind=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", pind, size_class, size_class);\n\t\tassert_true(size_class < max_psz,\n\t\t    \"Loop conditionals should be equivalent; pind=%u, \"\n\t\t    \"size_class=%zu (%#zx)\", pind, size_class, size_class);\n\n\t\tassert_u_eq(pind, sz_psz2ind(size_class),\n\t\t    \"sz_psz2ind() does not reverse sz_pind2sz(): pind=%u -->\"\n\t\t    \" size_class=%zu --> pind=%u --> size_class=%zu\", pind,\n\t\t    size_class, sz_psz2ind(size_class),\n\t\t    sz_pind2sz(sz_psz2ind(size_class)));\n\t\tassert_zu_eq(size_class, sz_pind2sz(sz_psz2ind(size_class)),\n\t\t    \"sz_pind2sz() does not reverse sz_psz2ind(): pind=%u -->\"\n\t\t    \" size_class=%zu --> pind=%u --> size_class=%zu\", pind,\n\t\t    size_class, sz_psz2ind(size_class),\n\t\t    sz_pind2sz(sz_psz2ind(size_class)));\n\n\t\tassert_u_eq(pind+1, sz_psz2ind(size_class+1),\n\t\t    \"Next size_class does not round up properly\");\n\n\t\tassert_zu_eq(size_class, (pind > 0) ?\n\t\t    sz_psz2u(sz_pind2sz(pind-1)+1) : sz_psz2u(1),\n\t\t    \"sz_psz2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_psz2u(size_class-1),\n\t\t    \"sz_psz2u() does not round up to size class\");\n\t\tassert_zu_eq(size_class, sz_psz2u(size_class),\n\t\t    \"sz_psz2u() does not compute same size class\");\n\t\tassert_zu_eq(sz_psz2u(size_class+1), sz_pind2sz(pind+1),\n\t\t    \"sz_psz2u() does not round up to next size class\");\n\t}\n\n\tassert_u_eq(pind, sz_psz2ind(sz_pind2sz(pind)),\n\t    \"sz_psz2ind() does not reverse sz_pind2sz()\");\n\tassert_zu_eq(max_psz, sz_pind2sz(sz_psz2ind(max_psz)),\n\t    \"sz_pind2sz() does not reverse sz_psz2ind()\");\n\n\tassert_zu_eq(size_class, sz_psz2u(sz_pind2sz(pind-1)+1),\n\t    \"sz_psz2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_psz2u(size_class-1),\n\t    \"sz_psz2u() does not round up to size class\");\n\tassert_zu_eq(size_class, sz_psz2u(size_class),\n\t    \"sz_psz2u() does not compute same size class\");\n}\nTEST_END\n\nTEST_BEGIN(test_overflow) {\n\tsize_t max_size_class, max_psz;\n\n\tmax_size_class = get_max_size_class();\n\tmax_psz = max_size_class + PAGE;\n\n\tassert_u_eq(sz_size2index(max_size_class+1), NSIZES,\n\t    \"sz_size2index() should return NSIZES on overflow\");\n\tassert_u_eq(sz_size2index(ZU(PTRDIFF_MAX)+1), NSIZES,\n\t    \"sz_size2index() should return NSIZES on overflow\");\n\tassert_u_eq(sz_size2index(SIZE_T_MAX), NSIZES,\n\t    \"sz_size2index() should return NSIZES on overflow\");\n\n\tassert_zu_eq(sz_s2u(max_size_class+1), 0,\n\t    \"sz_s2u() should return 0 for unsupported size\");\n\tassert_zu_eq(sz_s2u(ZU(PTRDIFF_MAX)+1), 0,\n\t    \"sz_s2u() should return 0 for unsupported size\");\n\tassert_zu_eq(sz_s2u(SIZE_T_MAX), 0,\n\t    \"sz_s2u() should return 0 on overflow\");\n\n\tassert_u_eq(sz_psz2ind(max_size_class+1), NPSIZES,\n\t    \"sz_psz2ind() should return NPSIZES on overflow\");\n\tassert_u_eq(sz_psz2ind(ZU(PTRDIFF_MAX)+1), NPSIZES,\n\t    \"sz_psz2ind() should return NPSIZES on overflow\");\n\tassert_u_eq(sz_psz2ind(SIZE_T_MAX), NPSIZES,\n\t    \"sz_psz2ind() should return NPSIZES on overflow\");\n\n\tassert_zu_eq(sz_psz2u(max_size_class+1), max_psz,\n\t    \"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) for unsupported\"\n\t    \" size\");\n\tassert_zu_eq(sz_psz2u(ZU(PTRDIFF_MAX)+1), max_psz,\n\t    \"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) for unsupported \"\n\t    \"size\");\n\tassert_zu_eq(sz_psz2u(SIZE_T_MAX), max_psz,\n\t    \"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) on overflow\");\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_size_classes,\n\t    test_psize_classes,\n\t    test_overflow);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/slab.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_arena_slab_regind) {\n\tszind_t binind;\n\n\tfor (binind = 0; binind < NBINS; binind++) {\n\t\tsize_t regind;\n\t\textent_t slab;\n\t\tconst bin_info_t *bin_info = &bin_infos[binind];\n\t\textent_init(&slab, NULL, mallocx(bin_info->slab_size,\n\t\t    MALLOCX_LG_ALIGN(LG_PAGE)), bin_info->slab_size, true,\n\t\t    binind, 0, extent_state_active, false, true, true);\n\t\tassert_ptr_not_null(extent_addr_get(&slab),\n\t\t    \"Unexpected malloc() failure\");\n\t\tfor (regind = 0; regind < bin_info->nregs; regind++) {\n\t\t\tvoid *reg = (void *)((uintptr_t)extent_addr_get(&slab) +\n\t\t\t    (bin_info->reg_size * regind));\n\t\t\tassert_zu_eq(arena_slab_regind(&slab, binind, reg),\n\t\t\t    regind,\n\t\t\t    \"Incorrect region index computed for size %zu\",\n\t\t\t    bin_info->reg_size);\n\t\t}\n\t\tfree(extent_addr_get(&slab));\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_arena_slab_regind);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/smoothstep.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic const uint64_t smoothstep_tab[] = {\n#define STEP(step, h, x, y)\t\t\t\\\n\th,\n\tSMOOTHSTEP\n#undef STEP\n};\n\nTEST_BEGIN(test_smoothstep_integral) {\n\tuint64_t sum, min, max;\n\tunsigned i;\n\n\t/*\n\t * The integral of smoothstep in the [0..1] range equals 1/2.  Verify\n\t * that the fixed point representation's integral is no more than\n\t * rounding error distant from 1/2.  Regarding rounding, each table\n\t * element is rounded down to the nearest fixed point value, so the\n\t * integral may be off by as much as SMOOTHSTEP_NSTEPS ulps.\n\t */\n\tsum = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tsum += smoothstep_tab[i];\n\t}\n\n\tmax = (KQU(1) << (SMOOTHSTEP_BFP-1)) * (SMOOTHSTEP_NSTEPS+1);\n\tmin = max - SMOOTHSTEP_NSTEPS;\n\n\tassert_u64_ge(sum, min,\n\t    \"Integral too small, even accounting for truncation\");\n\tassert_u64_le(sum, max, \"Integral exceeds 1/2\");\n\tif (false) {\n\t\tmalloc_printf(\"%\"FMTu64\" ulps under 1/2 (limit %d)\\n\",\n\t\t    max - sum, SMOOTHSTEP_NSTEPS);\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_smoothstep_monotonic) {\n\tuint64_t prev_h;\n\tunsigned i;\n\n\t/*\n\t * The smoothstep function is monotonic in [0..1], i.e. its slope is\n\t * non-negative.  In practice we want to parametrize table generation\n\t * such that piecewise slope is greater than zero, but do not require\n\t * that here.\n\t */\n\tprev_h = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {\n\t\tuint64_t h = smoothstep_tab[i];\n\t\tassert_u64_ge(h, prev_h, \"Piecewise non-monotonic, i=%u\", i);\n\t\tprev_h = h;\n\t}\n\tassert_u64_eq(smoothstep_tab[SMOOTHSTEP_NSTEPS-1],\n\t    (KQU(1) << SMOOTHSTEP_BFP), \"Last step must equal 1\");\n}\nTEST_END\n\nTEST_BEGIN(test_smoothstep_slope) {\n\tuint64_t prev_h, prev_delta;\n\tunsigned i;\n\n\t/*\n\t * The smoothstep slope strictly increases until x=0.5, and then\n\t * strictly decreases until x=1.0.  Verify the slightly weaker\n\t * requirement of monotonicity, so that inadequate table precision does\n\t * not cause false test failures.\n\t */\n\tprev_h = 0;\n\tprev_delta = 0;\n\tfor (i = 0; i < SMOOTHSTEP_NSTEPS / 2 + SMOOTHSTEP_NSTEPS % 2; i++) {\n\t\tuint64_t h = smoothstep_tab[i];\n\t\tuint64_t delta = h - prev_h;\n\t\tassert_u64_ge(delta, prev_delta,\n\t\t    \"Slope must monotonically increase in 0.0 <= x <= 0.5, \"\n\t\t    \"i=%u\", i);\n\t\tprev_h = h;\n\t\tprev_delta = delta;\n\t}\n\n\tprev_h = KQU(1) << SMOOTHSTEP_BFP;\n\tprev_delta = 0;\n\tfor (i = SMOOTHSTEP_NSTEPS-1; i >= SMOOTHSTEP_NSTEPS / 2; i--) {\n\t\tuint64_t h = smoothstep_tab[i];\n\t\tuint64_t delta = prev_h - h;\n\t\tassert_u64_ge(delta, prev_delta,\n\t\t    \"Slope must monotonically decrease in 0.5 <= x <= 1.0, \"\n\t\t    \"i=%u\", i);\n\t\tprev_h = h;\n\t\tprev_delta = delta;\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_smoothstep_integral,\n\t    test_smoothstep_monotonic,\n\t    test_smoothstep_slope);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/spin.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/spin.h\"\n\nTEST_BEGIN(test_spin) {\n\tspin_t spinner = SPIN_INITIALIZER;\n\n\tfor (unsigned i = 0; i < 100; i++) {\n\t\tspin_adaptive(&spinner);\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_spin);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/stats.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nTEST_BEGIN(test_stats_summary) {\n\tsize_t sz, allocated, active, resident, mapped;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.allocated\", (void *)&allocated, &sz, NULL,\n\t    0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.active\", (void *)&active, &sz, NULL, 0),\n\t    expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.resident\", (void *)&resident, &sz, NULL, 0),\n\t    expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.mapped\", (void *)&mapped, &sz, NULL, 0),\n\t    expected, \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_le(allocated, active,\n\t\t    \"allocated should be no larger than active\");\n\t\tassert_zu_lt(active, resident,\n\t\t    \"active should be less than resident\");\n\t\tassert_zu_lt(active, mapped,\n\t\t    \"active should be less than mapped\");\n\t}\n}\nTEST_END\n\nTEST_BEGIN(test_stats_large) {\n\tvoid *p;\n\tuint64_t epoch;\n\tsize_t allocated;\n\tuint64_t nmalloc, ndalloc, nrequests;\n\tsize_t sz;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tp = mallocx(SMALL_MAXCLASS+1, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.allocated\",\n\t    (void *)&allocated, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.nmalloc\", (void *)&nmalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.ndalloc\", (void *)&ndalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.nrequests\",\n\t    (void *)&nrequests, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_gt(allocated, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_le(nmalloc, nrequests,\n\t\t    \"nmalloc should no larger than nrequests\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas_summary) {\n\tvoid *little, *large;\n\tuint64_t epoch;\n\tsize_t sz;\n\tint expected = config_stats ? 0 : ENOENT;\n\tsize_t mapped;\n\tuint64_t dirty_npurge, dirty_nmadvise, dirty_purged;\n\tuint64_t muzzy_npurge, muzzy_nmadvise, muzzy_purged;\n\n\tlittle = mallocx(SMALL_MAXCLASS, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(little, \"Unexpected mallocx() failure\");\n\tlarge = mallocx((1U << LG_LARGE_MINCLASS), MALLOCX_ARENA(0));\n\tassert_ptr_not_null(large, \"Unexpected mallocx() failure\");\n\n\tdallocx(little, 0);\n\tdallocx(large, 0);\n\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"arena.0.purge\", NULL, NULL, NULL, 0), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.mapped\", (void *)&mapped, &sz, NULL,\n\t    0), expected, \"Unexepected mallctl() result\");\n\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.dirty_npurge\",\n\t    (void *)&dirty_npurge, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.dirty_nmadvise\",\n\t    (void *)&dirty_nmadvise, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.dirty_purged\",\n\t    (void *)&dirty_purged, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.muzzy_npurge\",\n\t    (void *)&muzzy_npurge, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.muzzy_nmadvise\",\n\t    (void *)&muzzy_nmadvise, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.muzzy_purged\",\n\t    (void *)&muzzy_purged, &sz, NULL, 0), expected,\n\t    \"Unexepected mallctl() result\");\n\n\tif (config_stats) {\n\t\tif (!background_thread_enabled()) {\n\t\t\tassert_u64_gt(dirty_npurge + muzzy_npurge, 0,\n\t\t\t    \"At least one purge should have occurred\");\n\t\t}\n\t\tassert_u64_le(dirty_nmadvise, dirty_purged,\n\t\t    \"dirty_nmadvise should be no greater than dirty_purged\");\n\t\tassert_u64_le(muzzy_nmadvise, muzzy_purged,\n\t\t    \"muzzy_nmadvise should be no greater than muzzy_purged\");\n\t}\n}\nTEST_END\n\nvoid *\nthd_start(void *arg) {\n\treturn NULL;\n}\n\nstatic void\nno_lazy_lock(void) {\n\tthd_t thd;\n\n\tthd_create(&thd, thd_start, NULL);\n\tthd_join(thd, NULL);\n}\n\nTEST_BEGIN(test_stats_arenas_small) {\n\tvoid *p;\n\tsize_t sz, allocated;\n\tuint64_t epoch, nmalloc, ndalloc, nrequests;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tno_lazy_lock(); /* Lazy locking would dodge tcache testing. */\n\n\tp = mallocx(SMALL_MAXCLASS, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.allocated\",\n\t    (void *)&allocated, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.nmalloc\", (void *)&nmalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.ndalloc\", (void *)&ndalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.small.nrequests\",\n\t    (void *)&nrequests, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_gt(allocated, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be no greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_gt(nrequests, 0,\n\t\t    \"nrequests should be greater than zero\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas_large) {\n\tvoid *p;\n\tsize_t sz, allocated;\n\tuint64_t epoch, nmalloc, ndalloc;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tp = mallocx((1U << LG_LARGE_MINCLASS), MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.allocated\",\n\t    (void *)&allocated, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.nmalloc\", (void *)&nmalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.large.ndalloc\", (void *)&ndalloc,\n\t    &sz, NULL, 0), expected, \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_zu_gt(allocated, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nstatic void\ngen_mallctl_str(char *cmd, char *name, unsigned arena_ind) {\n\tsprintf(cmd, \"stats.arenas.%u.bins.0.%s\", arena_ind, name);\n}\n\nTEST_BEGIN(test_stats_arenas_bins) {\n\tvoid *p;\n\tsize_t sz, curslabs, curregs;\n\tuint64_t epoch, nmalloc, ndalloc, nrequests, nfills, nflushes;\n\tuint64_t nslabs, nreslabs;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\t/* Make sure allocation below isn't satisfied by tcache. */\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\n\tunsigned arena_ind, old_arena_ind;\n\tsz = sizeof(unsigned);\n\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind, &sz, NULL, 0),\n\t    0, \"Arena creation failure\");\n\tsz = sizeof(arena_ind);\n\tassert_d_eq(mallctl(\"thread.arena\", (void *)&old_arena_ind, &sz,\n\t    (void *)&arena_ind, sizeof(arena_ind)), 0,\n\t    \"Unexpected mallctl() failure\");\n\n\tp = malloc(bin_infos[0].reg_size);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\tassert_d_eq(mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0),\n\t    opt_tcache ? 0 : EFAULT, \"Unexpected mallctl() result\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tchar cmd[128];\n\tsz = sizeof(uint64_t);\n\tgen_mallctl_str(cmd, \"nmalloc\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nmalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"ndalloc\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&ndalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"nrequests\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nrequests, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(size_t);\n\tgen_mallctl_str(cmd, \"curregs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&curregs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tsz = sizeof(uint64_t);\n\tgen_mallctl_str(cmd, \"nfills\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nfills, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"nflushes\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nflushes, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tgen_mallctl_str(cmd, \"nslabs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nslabs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tgen_mallctl_str(cmd, \"nreslabs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&nreslabs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(size_t);\n\tgen_mallctl_str(cmd, \"curslabs\", arena_ind);\n\tassert_d_eq(mallctl(cmd, (void *)&curslabs, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_gt(nrequests, 0,\n\t\t    \"nrequests should be greater than zero\");\n\t\tassert_zu_gt(curregs, 0,\n\t\t    \"allocated should be greater than zero\");\n\t\tif (opt_tcache) {\n\t\t\tassert_u64_gt(nfills, 0,\n\t\t\t    \"At least one fill should have occurred\");\n\t\t\tassert_u64_gt(nflushes, 0,\n\t\t\t    \"At least one flush should have occurred\");\n\t\t}\n\t\tassert_u64_gt(nslabs, 0,\n\t\t    \"At least one slab should have been allocated\");\n\t\tassert_zu_gt(curslabs, 0,\n\t\t    \"At least one slab should be currently allocated\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_stats_arenas_lextents) {\n\tvoid *p;\n\tuint64_t epoch, nmalloc, ndalloc;\n\tsize_t curlextents, sz, hsize;\n\tint expected = config_stats ? 0 : ENOENT;\n\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"arenas.lextent.0.size\", (void *)&hsize, &sz, NULL,\n\t    0), 0, \"Unexpected mallctl() failure\");\n\n\tp = mallocx(hsize, MALLOCX_ARENA(0));\n\tassert_ptr_not_null(p, \"Unexpected mallocx() failure\");\n\n\tassert_d_eq(mallctl(\"epoch\", NULL, NULL, (void *)&epoch, sizeof(epoch)),\n\t    0, \"Unexpected mallctl() failure\");\n\n\tsz = sizeof(uint64_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.lextents.0.nmalloc\",\n\t    (void *)&nmalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tassert_d_eq(mallctl(\"stats.arenas.0.lextents.0.ndalloc\",\n\t    (void *)&ndalloc, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\tsz = sizeof(size_t);\n\tassert_d_eq(mallctl(\"stats.arenas.0.lextents.0.curlextents\",\n\t    (void *)&curlextents, &sz, NULL, 0), expected,\n\t    \"Unexpected mallctl() result\");\n\n\tif (config_stats) {\n\t\tassert_u64_gt(nmalloc, 0,\n\t\t    \"nmalloc should be greater than zero\");\n\t\tassert_u64_ge(nmalloc, ndalloc,\n\t\t    \"nmalloc should be at least as large as ndalloc\");\n\t\tassert_u64_gt(curlextents, 0,\n\t\t    \"At least one extent should be currently allocated\");\n\t}\n\n\tdallocx(p, 0);\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test_no_reentrancy(\n\t    test_stats_summary,\n\t    test_stats_large,\n\t    test_stats_arenas_summary,\n\t    test_stats_arenas_small,\n\t    test_stats_arenas_large,\n\t    test_stats_arenas_bins,\n\t    test_stats_arenas_lextents);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/stats_print.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/util.h\"\n\ntypedef enum {\n\tTOKEN_TYPE_NONE,\n\tTOKEN_TYPE_ERROR,\n\tTOKEN_TYPE_EOI,\n\tTOKEN_TYPE_NULL,\n\tTOKEN_TYPE_FALSE,\n\tTOKEN_TYPE_TRUE,\n\tTOKEN_TYPE_LBRACKET,\n\tTOKEN_TYPE_RBRACKET,\n\tTOKEN_TYPE_LBRACE,\n\tTOKEN_TYPE_RBRACE,\n\tTOKEN_TYPE_COLON,\n\tTOKEN_TYPE_COMMA,\n\tTOKEN_TYPE_STRING,\n\tTOKEN_TYPE_NUMBER\n} token_type_t;\n\ntypedef struct parser_s parser_t;\ntypedef struct {\n\tparser_t\t*parser;\n\ttoken_type_t\ttoken_type;\n\tsize_t\t\tpos;\n\tsize_t\t\tlen;\n\tsize_t\t\tline;\n\tsize_t\t\tcol;\n} token_t;\n\nstruct parser_s {\n\tbool verbose;\n\tchar\t*buf; /* '\\0'-terminated. */\n\tsize_t\tlen; /* Number of characters preceding '\\0' in buf. */\n\tsize_t\tpos;\n\tsize_t\tline;\n\tsize_t\tcol;\n\ttoken_t\ttoken;\n};\n\nstatic void\ntoken_init(token_t *token, parser_t *parser, token_type_t token_type,\n    size_t pos, size_t len, size_t line, size_t col) {\n\ttoken->parser = parser;\n\ttoken->token_type = token_type;\n\ttoken->pos = pos;\n\ttoken->len = len;\n\ttoken->line = line;\n\ttoken->col = col;\n}\n\nstatic void\ntoken_error(token_t *token) {\n\tif (!token->parser->verbose) {\n\t\treturn;\n\t}\n\tswitch (token->token_type) {\n\tcase TOKEN_TYPE_NONE:\n\t\tnot_reached();\n\tcase TOKEN_TYPE_ERROR:\n\t\tmalloc_printf(\"%zu:%zu: Unexpected character in token: \",\n\t\t    token->line, token->col);\n\t\tbreak;\n\tdefault:\n\t\tmalloc_printf(\"%zu:%zu: Unexpected token: \", token->line,\n\t\t    token->col);\n\t\tbreak;\n\t}\n\tUNUSED ssize_t err = malloc_write_fd(STDERR_FILENO,\n\t    &token->parser->buf[token->pos], token->len);\n\tmalloc_printf(\"\\n\");\n}\n\nstatic void\nparser_init(parser_t *parser, bool verbose) {\n\tparser->verbose = verbose;\n\tparser->buf = NULL;\n\tparser->len = 0;\n\tparser->pos = 0;\n\tparser->line = 1;\n\tparser->col = 0;\n}\n\nstatic void\nparser_fini(parser_t *parser) {\n\tif (parser->buf != NULL) {\n\t\tdallocx(parser->buf, MALLOCX_TCACHE_NONE);\n\t}\n}\n\nstatic bool\nparser_append(parser_t *parser, const char *str) {\n\tsize_t len = strlen(str);\n\tchar *buf = (parser->buf == NULL) ? mallocx(len + 1,\n\t    MALLOCX_TCACHE_NONE) : rallocx(parser->buf, parser->len + len + 1,\n\t    MALLOCX_TCACHE_NONE);\n\tif (buf == NULL) {\n\t\treturn true;\n\t}\n\tmemcpy(&buf[parser->len], str, len + 1);\n\tparser->buf = buf;\n\tparser->len += len;\n\treturn false;\n}\n\nstatic bool\nparser_tokenize(parser_t *parser) {\n\tenum {\n\t\tSTATE_START,\n\t\tSTATE_EOI,\n\t\tSTATE_N, STATE_NU, STATE_NUL, STATE_NULL,\n\t\tSTATE_F, STATE_FA, STATE_FAL, STATE_FALS, STATE_FALSE,\n\t\tSTATE_T, STATE_TR, STATE_TRU, STATE_TRUE,\n\t\tSTATE_LBRACKET,\n\t\tSTATE_RBRACKET,\n\t\tSTATE_LBRACE,\n\t\tSTATE_RBRACE,\n\t\tSTATE_COLON,\n\t\tSTATE_COMMA,\n\t\tSTATE_CHARS,\n\t\tSTATE_CHAR_ESCAPE,\n\t\tSTATE_CHAR_U, STATE_CHAR_UD, STATE_CHAR_UDD, STATE_CHAR_UDDD,\n\t\tSTATE_STRING,\n\t\tSTATE_MINUS,\n\t\tSTATE_LEADING_ZERO,\n\t\tSTATE_DIGITS,\n\t\tSTATE_DECIMAL,\n\t\tSTATE_FRAC_DIGITS,\n\t\tSTATE_EXP,\n\t\tSTATE_EXP_SIGN,\n\t\tSTATE_EXP_DIGITS,\n\t\tSTATE_ACCEPT\n\t} state = STATE_START;\n\tsize_t token_pos JEMALLOC_CC_SILENCE_INIT(0);\n\tsize_t token_line JEMALLOC_CC_SILENCE_INIT(1);\n\tsize_t token_col JEMALLOC_CC_SILENCE_INIT(0);\n\n\tassert_zu_le(parser->pos, parser->len,\n\t    \"Position is past end of buffer\");\n\n\twhile (state != STATE_ACCEPT) {\n\t\tchar c = parser->buf[parser->pos];\n\n\t\tswitch (state) {\n\t\tcase STATE_START:\n\t\t\ttoken_pos = parser->pos;\n\t\t\ttoken_line = parser->line;\n\t\t\ttoken_col = parser->col;\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\t\tbreak;\n\t\t\tcase '\\0':\n\t\t\t\tstate = STATE_EOI;\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\tstate = STATE_N;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\t\tstate = STATE_F;\n\t\t\t\tbreak;\n\t\t\tcase 't':\n\t\t\t\tstate = STATE_T;\n\t\t\t\tbreak;\n\t\t\tcase '[':\n\t\t\t\tstate = STATE_LBRACKET;\n\t\t\t\tbreak;\n\t\t\tcase ']':\n\t\t\t\tstate = STATE_RBRACKET;\n\t\t\t\tbreak;\n\t\t\tcase '{':\n\t\t\t\tstate = STATE_LBRACE;\n\t\t\t\tbreak;\n\t\t\tcase '}':\n\t\t\t\tstate = STATE_RBRACE;\n\t\t\t\tbreak;\n\t\t\tcase ':':\n\t\t\t\tstate = STATE_COLON;\n\t\t\t\tbreak;\n\t\t\tcase ',':\n\t\t\t\tstate = STATE_COMMA;\n\t\t\t\tbreak;\n\t\t\tcase '\"':\n\t\t\t\tstate = STATE_CHARS;\n\t\t\t\tbreak;\n\t\t\tcase '-':\n\t\t\t\tstate = STATE_MINUS;\n\t\t\t\tbreak;\n\t\t\tcase '0':\n\t\t\t\tstate = STATE_LEADING_ZERO;\n\t\t\t\tbreak;\n\t\t\tcase '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9':\n\t\t\t\tstate = STATE_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EOI:\n\t\t\ttoken_init(&parser->token, parser,\n\t\t\t    TOKEN_TYPE_EOI, token_pos, parser->pos -\n\t\t\t    token_pos, token_line, token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_N:\n\t\t\tswitch (c) {\n\t\t\tcase 'u':\n\t\t\t\tstate = STATE_NU;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_NU:\n\t\t\tswitch (c) {\n\t\t\tcase 'l':\n\t\t\t\tstate = STATE_NUL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_NUL:\n\t\t\tswitch (c) {\n\t\t\tcase 'l':\n\t\t\t\tstate = STATE_NULL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_NULL:\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\tcase '\\0':\n\t\t\tcase '[': case ']': case '{': case '}': case ':':\n\t\t\tcase ',':\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_NULL,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_F:\n\t\t\tswitch (c) {\n\t\t\tcase 'a':\n\t\t\t\tstate = STATE_FA;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FA:\n\t\t\tswitch (c) {\n\t\t\tcase 'l':\n\t\t\t\tstate = STATE_FAL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FAL:\n\t\t\tswitch (c) {\n\t\t\tcase 's':\n\t\t\t\tstate = STATE_FALS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FALS:\n\t\t\tswitch (c) {\n\t\t\tcase 'e':\n\t\t\t\tstate = STATE_FALSE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FALSE:\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\tcase '\\0':\n\t\t\tcase '[': case ']': case '{': case '}': case ':':\n\t\t\tcase ',':\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttoken_init(&parser->token, parser,\n\t\t\t    TOKEN_TYPE_FALSE, token_pos, parser->pos -\n\t\t\t    token_pos, token_line, token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_T:\n\t\t\tswitch (c) {\n\t\t\tcase 'r':\n\t\t\t\tstate = STATE_TR;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_TR:\n\t\t\tswitch (c) {\n\t\t\tcase 'u':\n\t\t\t\tstate = STATE_TRU;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_TRU:\n\t\t\tswitch (c) {\n\t\t\tcase 'e':\n\t\t\t\tstate = STATE_TRUE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_TRUE:\n\t\t\tswitch (c) {\n\t\t\tcase ' ': case '\\b': case '\\n': case '\\r': case '\\t':\n\t\t\tcase '\\0':\n\t\t\tcase '[': case ']': case '{': case '}': case ':':\n\t\t\tcase ',':\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_TRUE,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_LBRACKET:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_LBRACKET,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_RBRACKET:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_RBRACKET,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_LBRACE:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_LBRACE,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_RBRACE:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_RBRACE,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_COLON:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_COLON,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_COMMA:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_COMMA,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_CHARS:\n\t\t\tswitch (c) {\n\t\t\tcase '\\\\':\n\t\t\t\tstate = STATE_CHAR_ESCAPE;\n\t\t\t\tbreak;\n\t\t\tcase '\"':\n\t\t\t\tstate = STATE_STRING;\n\t\t\t\tbreak;\n\t\t\tcase 0x00: case 0x01: case 0x02: case 0x03: case 0x04:\n\t\t\tcase 0x05: case 0x06: case 0x07: case 0x08: case 0x09:\n\t\t\tcase 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e:\n\t\t\tcase 0x0f: case 0x10: case 0x11: case 0x12: case 0x13:\n\t\t\tcase 0x14: case 0x15: case 0x16: case 0x17: case 0x18:\n\t\t\tcase 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d:\n\t\t\tcase 0x1e: case 0x1f:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_ESCAPE:\n\t\t\tswitch (c) {\n\t\t\tcase '\"': case '\\\\': case '/': case 'b': case 'n':\n\t\t\tcase 'r': case 't':\n\t\t\t\tstate = STATE_CHARS;\n\t\t\t\tbreak;\n\t\t\tcase 'u':\n\t\t\t\tstate = STATE_CHAR_U;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_U:\n\t\t\tswitch (c) {\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\tstate = STATE_CHAR_UD;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_UD:\n\t\t\tswitch (c) {\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\tstate = STATE_CHAR_UDD;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_UDD:\n\t\t\tswitch (c) {\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\tstate = STATE_CHAR_UDDD;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_CHAR_UDDD:\n\t\t\tswitch (c) {\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\tstate = STATE_CHARS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_STRING:\n\t\t\ttoken_init(&parser->token, parser, TOKEN_TYPE_STRING,\n\t\t\t    token_pos, parser->pos - token_pos, token_line,\n\t\t\t    token_col);\n\t\t\tstate = STATE_ACCEPT;\n\t\t\tbreak;\n\t\tcase STATE_MINUS:\n\t\t\tswitch (c) {\n\t\t\tcase '0':\n\t\t\t\tstate = STATE_LEADING_ZERO;\n\t\t\t\tbreak;\n\t\t\tcase '1': case '2': case '3': case '4':\n\t\t\tcase '5': case '6': case '7': case '8': case '9':\n\t\t\t\tstate = STATE_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_LEADING_ZERO:\n\t\t\tswitch (c) {\n\t\t\tcase '.':\n\t\t\t\tstate = STATE_DECIMAL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_DIGITS:\n\t\t\tswitch (c) {\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\tbreak;\n\t\t\tcase '.':\n\t\t\t\tstate = STATE_DECIMAL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_DECIMAL:\n\t\t\tswitch (c) {\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\tstate = STATE_FRAC_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_FRAC_DIGITS:\n\t\t\tswitch (c) {\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\tbreak;\n\t\t\tcase 'e': case 'E':\n\t\t\t\tstate = STATE_EXP;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EXP:\n\t\t\tswitch (c) {\n\t\t\tcase '-': case '+':\n\t\t\t\tstate = STATE_EXP_SIGN;\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\tstate = STATE_EXP_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EXP_SIGN:\n\t\t\tswitch (c) {\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\tstate = STATE_EXP_DIGITS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1\n\t\t\t\t    - token_pos, token_line, token_col);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase STATE_EXP_DIGITS:\n\t\t\tswitch (c) {\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\tbreak;\n\t\t\tdefault:\n\t\t\t\ttoken_init(&parser->token, parser,\n\t\t\t\t    TOKEN_TYPE_NUMBER, token_pos, parser->pos -\n\t\t\t\t    token_pos, token_line, token_col);\n\t\t\t\tstate = STATE_ACCEPT;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tnot_reached();\n\t\t}\n\n\t\tif (state != STATE_ACCEPT) {\n\t\t\tif (c == '\\n') {\n\t\t\t\tparser->line++;\n\t\t\t\tparser->col = 0;\n\t\t\t} else {\n\t\t\t\tparser->col++;\n\t\t\t}\n\t\t\tparser->pos++;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic bool\tparser_parse_array(parser_t *parser);\nstatic bool\tparser_parse_object(parser_t *parser);\n\nstatic bool\nparser_parse_value(parser_t *parser) {\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_NULL:\n\tcase TOKEN_TYPE_FALSE:\n\tcase TOKEN_TYPE_TRUE:\n\tcase TOKEN_TYPE_STRING:\n\tcase TOKEN_TYPE_NUMBER:\n\t\treturn false;\n\tcase TOKEN_TYPE_LBRACE:\n\t\treturn parser_parse_object(parser);\n\tcase TOKEN_TYPE_LBRACKET:\n\t\treturn parser_parse_array(parser);\n\tdefault:\n\t\treturn true;\n\t}\n\tnot_reached();\n}\n\nstatic bool\nparser_parse_pair(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,\n\t    \"Pair should start with string\");\n\tif (parser_tokenize(parser)) {\n\t\treturn true;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_COLON:\n\t\tif (parser_tokenize(parser)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn parser_parse_value(parser);\n\tdefault:\n\t\treturn true;\n\t}\n}\n\nstatic bool\nparser_parse_values(parser_t *parser) {\n\tif (parser_parse_value(parser)) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tif (parser_tokenize(parser)) {\n\t\t\treturn true;\n\t\t}\n\t\tswitch (parser->token.token_type) {\n\t\tcase TOKEN_TYPE_COMMA:\n\t\t\tif (parser_tokenize(parser)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (parser_parse_value(parser)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TOKEN_TYPE_RBRACKET:\n\t\t\treturn false;\n\t\tdefault:\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\nstatic bool\nparser_parse_array(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACKET,\n\t    \"Array should start with [\");\n\tif (parser_tokenize(parser)) {\n\t\treturn true;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_RBRACKET:\n\t\treturn false;\n\tdefault:\n\t\treturn parser_parse_values(parser);\n\t}\n\tnot_reached();\n}\n\nstatic bool\nparser_parse_pairs(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,\n\t    \"Object should start with string\");\n\tif (parser_parse_pair(parser)) {\n\t\treturn true;\n\t}\n\n\twhile (true) {\n\t\tif (parser_tokenize(parser)) {\n\t\t\treturn true;\n\t\t}\n\t\tswitch (parser->token.token_type) {\n\t\tcase TOKEN_TYPE_COMMA:\n\t\t\tif (parser_tokenize(parser)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tswitch (parser->token.token_type) {\n\t\t\tcase TOKEN_TYPE_STRING:\n\t\t\t\tif (parser_parse_pair(parser)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TOKEN_TYPE_RBRACE:\n\t\t\treturn false;\n\t\tdefault:\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\nstatic bool\nparser_parse_object(parser_t *parser) {\n\tassert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACE,\n\t    \"Object should start with {\");\n\tif (parser_tokenize(parser)) {\n\t\treturn true;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_STRING:\n\t\treturn parser_parse_pairs(parser);\n\tcase TOKEN_TYPE_RBRACE:\n\t\treturn false;\n\tdefault:\n\t\treturn true;\n\t}\n\tnot_reached();\n}\n\nstatic bool\nparser_parse(parser_t *parser) {\n\tif (parser_tokenize(parser)) {\n\t\tgoto label_error;\n\t}\n\tif (parser_parse_value(parser)) {\n\t\tgoto label_error;\n\t}\n\n\tif (parser_tokenize(parser)) {\n\t\tgoto label_error;\n\t}\n\tswitch (parser->token.token_type) {\n\tcase TOKEN_TYPE_EOI:\n\t\treturn false;\n\tdefault:\n\t\tgoto label_error;\n\t}\n\tnot_reached();\n\nlabel_error:\n\ttoken_error(&parser->token);\n\treturn true;\n}\n\nTEST_BEGIN(test_json_parser) {\n\tsize_t i;\n\tconst char *invalid_inputs[] = {\n\t\t/* Tokenizer error case tests. */\n\t\t\"{ \\\"string\\\": X }\",\n\t\t\"{ \\\"string\\\": nXll }\",\n\t\t\"{ \\\"string\\\": nuXl }\",\n\t\t\"{ \\\"string\\\": nulX }\",\n\t\t\"{ \\\"string\\\": nullX }\",\n\t\t\"{ \\\"string\\\": fXlse }\",\n\t\t\"{ \\\"string\\\": faXse }\",\n\t\t\"{ \\\"string\\\": falXe }\",\n\t\t\"{ \\\"string\\\": falsX }\",\n\t\t\"{ \\\"string\\\": falseX }\",\n\t\t\"{ \\\"string\\\": tXue }\",\n\t\t\"{ \\\"string\\\": trXe }\",\n\t\t\"{ \\\"string\\\": truX }\",\n\t\t\"{ \\\"string\\\": trueX }\",\n\t\t\"{ \\\"string\\\": \\\"\\n\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\z\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\uX000\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\u0X00\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\u00X0\\\" }\",\n\t\t\"{ \\\"string\\\": \\\"\\\\u000X\\\" }\",\n\t\t\"{ \\\"string\\\": -X }\",\n\t\t\"{ \\\"string\\\": 0.X }\",\n\t\t\"{ \\\"string\\\": 0.0eX }\",\n\t\t\"{ \\\"string\\\": 0.0e+X }\",\n\n\t\t/* Parser error test cases. */\n\t\t\"{\\\"string\\\": }\",\n\t\t\"{\\\"string\\\" }\",\n\t\t\"{\\\"string\\\": [ 0 }\",\n\t\t\"{\\\"string\\\": {\\\"a\\\":0, 1 } }\",\n\t\t\"{\\\"string\\\": {\\\"a\\\":0: } }\",\n\t\t\"{\",\n\t\t\"{}{\",\n\t};\n\tconst char *valid_inputs[] = {\n\t\t/* Token tests. */\n\t\t\"null\",\n\t\t\"false\",\n\t\t\"true\",\n\t\t\"{}\",\n\t\t\"{\\\"a\\\": 0}\",\n\t\t\"[]\",\n\t\t\"[0, 1]\",\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"10\",\n\t\t\"-10\",\n\t\t\"10.23\",\n\t\t\"10.23e4\",\n\t\t\"10.23e-4\",\n\t\t\"10.23e+4\",\n\t\t\"10.23E4\",\n\t\t\"10.23E-4\",\n\t\t\"10.23E+4\",\n\t\t\"-10.23\",\n\t\t\"-10.23e4\",\n\t\t\"-10.23e-4\",\n\t\t\"-10.23e+4\",\n\t\t\"-10.23E4\",\n\t\t\"-10.23E-4\",\n\t\t\"-10.23E+4\",\n\t\t\"\\\"value\\\"\",\n\t\t\"\\\" \\\\\\\" \\\\/ \\\\b \\\\n \\\\r \\\\t \\\\u0abc \\\\u1DEF \\\"\",\n\n\t\t/* Parser test with various nesting. */\n\t\t\"{\\\"a\\\":null, \\\"b\\\":[1,[{\\\"c\\\":2},3]], \\\"d\\\":{\\\"e\\\":true}}\",\n\t};\n\n\tfor (i = 0; i < sizeof(invalid_inputs)/sizeof(const char *); i++) {\n\t\tconst char *input = invalid_inputs[i];\n\t\tparser_t parser;\n\t\tparser_init(&parser, false);\n\t\tassert_false(parser_append(&parser, input),\n\t\t    \"Unexpected input appending failure\");\n\t\tassert_true(parser_parse(&parser),\n\t\t    \"Unexpected parse success for input: %s\", input);\n\t\tparser_fini(&parser);\n\t}\n\n\tfor (i = 0; i < sizeof(valid_inputs)/sizeof(const char *); i++) {\n\t\tconst char *input = valid_inputs[i];\n\t\tparser_t parser;\n\t\tparser_init(&parser, true);\n\t\tassert_false(parser_append(&parser, input),\n\t\t    \"Unexpected input appending failure\");\n\t\tassert_false(parser_parse(&parser),\n\t\t    \"Unexpected parse error for input: %s\", input);\n\t\tparser_fini(&parser);\n\t}\n}\nTEST_END\n\nvoid\nwrite_cb(void *opaque, const char *str) {\n\tparser_t *parser = (parser_t *)opaque;\n\tif (parser_append(parser, str)) {\n\t\ttest_fail(\"Unexpected input appending failure\");\n\t}\n}\n\nTEST_BEGIN(test_stats_print_json) {\n\tconst char *opts[] = {\n\t\t\"J\",\n\t\t\"Jg\",\n\t\t\"Jm\",\n\t\t\"Jd\",\n\t\t\"Jmd\",\n\t\t\"Jgd\",\n\t\t\"Jgm\",\n\t\t\"Jgmd\",\n\t\t\"Ja\",\n\t\t\"Jb\",\n\t\t\"Jl\",\n\t\t\"Jx\",\n\t\t\"Jbl\",\n\t\t\"Jal\",\n\t\t\"Jab\",\n\t\t\"Jabl\",\n\t\t\"Jax\",\n\t\t\"Jbx\",\n\t\t\"Jlx\",\n\t\t\"Jablx\",\n\t\t\"Jgmdablx\",\n\t};\n\tunsigned arena_ind, i;\n\n\tfor (i = 0; i < 3; i++) {\n\t\tunsigned j;\n\n\t\tswitch (i) {\n\t\tcase 0:\n\t\t\tbreak;\n\t\tcase 1: {\n\t\t\tsize_t sz = sizeof(arena_ind);\n\t\t\tassert_d_eq(mallctl(\"arenas.create\", (void *)&arena_ind,\n\t\t\t    &sz, NULL, 0), 0, \"Unexpected mallctl failure\");\n\t\t\tbreak;\n\t\t} case 2: {\n\t\t\tsize_t mib[3];\n\t\t\tsize_t miblen = sizeof(mib)/sizeof(size_t);\n\t\t\tassert_d_eq(mallctlnametomib(\"arena.0.destroy\",\n\t\t\t    mib, &miblen), 0,\n\t\t\t    \"Unexpected mallctlnametomib failure\");\n\t\t\tmib[1] = arena_ind;\n\t\t\tassert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL,\n\t\t\t    0), 0, \"Unexpected mallctlbymib failure\");\n\t\t\tbreak;\n\t\t} default:\n\t\t\tnot_reached();\n\t\t}\n\n\t\tfor (j = 0; j < sizeof(opts)/sizeof(const char *); j++) {\n\t\t\tparser_t parser;\n\n\t\t\tparser_init(&parser, true);\n\t\t\tmalloc_stats_print(write_cb, (void *)&parser, opts[j]);\n\t\t\tassert_false(parser_parse(&parser),\n\t\t\t    \"Unexpected parse error, opts=\\\"%s\\\"\", opts[j]);\n\t\t\tparser_fini(&parser);\n\t\t}\n\t}\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_json_parser,\n\t    test_stats_print_json);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/ticker.c",
    "content": "#include \"test/jemalloc_test.h\"\n\n#include \"jemalloc/internal/ticker.h\"\n\nTEST_BEGIN(test_ticker_tick) {\n#define NREPS 2\n#define NTICKS 3\n\tticker_t ticker;\n\tint32_t i, j;\n\n\tticker_init(&ticker, NTICKS);\n\tfor (i = 0; i < NREPS; i++) {\n\t\tfor (j = 0; j < NTICKS; j++) {\n\t\t\tassert_u_eq(ticker_read(&ticker), NTICKS - j,\n\t\t\t    \"Unexpected ticker value (i=%d, j=%d)\", i, j);\n\t\t\tassert_false(ticker_tick(&ticker),\n\t\t\t    \"Unexpected ticker fire (i=%d, j=%d)\", i, j);\n\t\t}\n\t\tassert_u32_eq(ticker_read(&ticker), 0,\n\t\t    \"Expected ticker depletion\");\n\t\tassert_true(ticker_tick(&ticker),\n\t\t    \"Expected ticker fire (i=%d)\", i);\n\t\tassert_u32_eq(ticker_read(&ticker), NTICKS,\n\t\t    \"Expected ticker reset\");\n\t}\n#undef NTICKS\n}\nTEST_END\n\nTEST_BEGIN(test_ticker_ticks) {\n#define NTICKS 3\n\tticker_t ticker;\n\n\tticker_init(&ticker, NTICKS);\n\n\tassert_u_eq(ticker_read(&ticker), NTICKS, \"Unexpected ticker value\");\n\tassert_false(ticker_ticks(&ticker, NTICKS), \"Unexpected ticker fire\");\n\tassert_u_eq(ticker_read(&ticker), 0, \"Unexpected ticker value\");\n\tassert_true(ticker_ticks(&ticker, NTICKS), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&ticker), NTICKS, \"Unexpected ticker value\");\n\n\tassert_true(ticker_ticks(&ticker, NTICKS + 1), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&ticker), NTICKS, \"Unexpected ticker value\");\n#undef NTICKS\n}\nTEST_END\n\nTEST_BEGIN(test_ticker_copy) {\n#define NTICKS 3\n\tticker_t ta, tb;\n\n\tticker_init(&ta, NTICKS);\n\tticker_copy(&tb, &ta);\n\tassert_u_eq(ticker_read(&tb), NTICKS, \"Unexpected ticker value\");\n\tassert_true(ticker_ticks(&tb, NTICKS + 1), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&tb), NTICKS, \"Unexpected ticker value\");\n\n\tticker_tick(&ta);\n\tticker_copy(&tb, &ta);\n\tassert_u_eq(ticker_read(&tb), NTICKS - 1, \"Unexpected ticker value\");\n\tassert_true(ticker_ticks(&tb, NTICKS), \"Expected ticker fire\");\n\tassert_u_eq(ticker_read(&tb), NTICKS, \"Unexpected ticker value\");\n#undef NTICKS\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_ticker_tick,\n\t    test_ticker_ticks,\n\t    test_ticker_copy);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/tsd.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic int data_cleanup_count;\n\nvoid\ndata_cleanup(int *data) {\n\tif (data_cleanup_count == 0) {\n\t\tassert_x_eq(*data, MALLOC_TSD_TEST_DATA_INIT,\n\t\t    \"Argument passed into cleanup function should match tsd \"\n\t\t    \"value\");\n\t}\n\t++data_cleanup_count;\n\n\t/*\n\t * Allocate during cleanup for two rounds, in order to assure that\n\t * jemalloc's internal tsd reinitialization happens.\n\t */\n\tbool reincarnate = false;\n\tswitch (*data) {\n\tcase MALLOC_TSD_TEST_DATA_INIT:\n\t\t*data = 1;\n\t\treincarnate = true;\n\t\tbreak;\n\tcase 1:\n\t\t*data = 2;\n\t\treincarnate = true;\n\t\tbreak;\n\tcase 2:\n\t\treturn;\n\tdefault:\n\t\tnot_reached();\n\t}\n\n\tif (reincarnate) {\n\t\tvoid *p = mallocx(1, 0);\n\t\tassert_ptr_not_null(p, \"Unexpeced mallocx() failure\");\n\t\tdallocx(p, 0);\n\t}\n}\n\nstatic void *\nthd_start(void *arg) {\n\tint d = (int)(uintptr_t)arg;\n\tvoid *p;\n\n\ttsd_t *tsd = tsd_fetch();\n\tassert_x_eq(tsd_test_data_get(tsd), MALLOC_TSD_TEST_DATA_INIT,\n\t    \"Initial tsd get should return initialization value\");\n\n\tp = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\ttsd_test_data_set(tsd, d);\n\tassert_x_eq(tsd_test_data_get(tsd), d,\n\t    \"After tsd set, tsd get should return value that was set\");\n\n\td = 0;\n\tassert_x_eq(tsd_test_data_get(tsd), (int)(uintptr_t)arg,\n\t    \"Resetting local data should have no effect on tsd\");\n\n\ttsd_test_callback_set(tsd, &data_cleanup);\n\n\tfree(p);\n\treturn NULL;\n}\n\nTEST_BEGIN(test_tsd_main_thread) {\n\tthd_start((void *)(uintptr_t)0xa5f3e329);\n}\nTEST_END\n\nTEST_BEGIN(test_tsd_sub_thread) {\n\tthd_t thd;\n\n\tdata_cleanup_count = 0;\n\tthd_create(&thd, thd_start, (void *)MALLOC_TSD_TEST_DATA_INIT);\n\tthd_join(thd, NULL);\n\t/*\n\t * We reincarnate twice in the data cleanup, so it should execute at\n\t * least 3 times.\n\t */\n\tassert_x_ge(data_cleanup_count, 3,\n\t    \"Cleanup function should have executed multiple times.\");\n}\nTEST_END\n\nstatic void *\nthd_start_reincarnated(void *arg) {\n\ttsd_t *tsd = tsd_fetch();\n\tassert(tsd);\n\n\tvoid *p = malloc(1);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\n\t/* Manually trigger reincarnation. */\n\tassert_ptr_not_null(tsd_arena_get(tsd),\n\t    \"Should have tsd arena set.\");\n\ttsd_cleanup((void *)tsd);\n\tassert_ptr_null(*tsd_arenap_get_unsafe(tsd),\n\t    \"TSD arena should have been cleared.\");\n\tassert_u_eq(tsd->state, tsd_state_purgatory,\n\t    \"TSD state should be purgatory\\n\");\n\n\tfree(p);\n\tassert_u_eq(tsd->state, tsd_state_reincarnated,\n\t    \"TSD state should be reincarnated\\n\");\n\tp = mallocx(1, MALLOCX_TCACHE_NONE);\n\tassert_ptr_not_null(p, \"Unexpected malloc() failure\");\n\tassert_ptr_null(*tsd_arenap_get_unsafe(tsd),\n\t    \"Should not have tsd arena set after reincarnation.\");\n\n\tfree(p);\n\ttsd_cleanup((void *)tsd);\n\tassert_ptr_null(*tsd_arenap_get_unsafe(tsd),\n\t    \"TSD arena should have been cleared after 2nd cleanup.\");\n\n\treturn NULL;\n}\n\nTEST_BEGIN(test_tsd_reincarnation) {\n\tthd_t thd;\n\tthd_create(&thd, thd_start_reincarnated, NULL);\n\tthd_join(thd, NULL);\n}\nTEST_END\n\nint\nmain(void) {\n\t/* Ensure tsd bootstrapped. */\n\tif (nallocx(1, 0) == 0) {\n\t\tmalloc_printf(\"Initialization error\");\n\t\treturn test_status_fail;\n\t}\n\n\treturn test_no_reentrancy(\n\t    test_tsd_main_thread,\n\t    test_tsd_sub_thread,\n\t    test_tsd_reincarnation);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/witness.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic witness_lock_error_t *witness_lock_error_orig;\nstatic witness_owner_error_t *witness_owner_error_orig;\nstatic witness_not_owner_error_t *witness_not_owner_error_orig;\nstatic witness_depth_error_t *witness_depth_error_orig;\n\nstatic bool saw_lock_error;\nstatic bool saw_owner_error;\nstatic bool saw_not_owner_error;\nstatic bool saw_depth_error;\n\nstatic void\nwitness_lock_error_intercept(const witness_list_t *witnesses,\n    const witness_t *witness) {\n\tsaw_lock_error = true;\n}\n\nstatic void\nwitness_owner_error_intercept(const witness_t *witness) {\n\tsaw_owner_error = true;\n}\n\nstatic void\nwitness_not_owner_error_intercept(const witness_t *witness) {\n\tsaw_not_owner_error = true;\n}\n\nstatic void\nwitness_depth_error_intercept(const witness_list_t *witnesses,\n    witness_rank_t rank_inclusive, unsigned depth) {\n\tsaw_depth_error = true;\n}\n\nstatic int\nwitness_comp(const witness_t *a, void *oa, const witness_t *b, void *ob) {\n\tassert_u_eq(a->rank, b->rank, \"Witnesses should have equal rank\");\n\n\tassert(oa == (void *)a);\n\tassert(ob == (void *)b);\n\n\treturn strcmp(a->name, b->name);\n}\n\nstatic int\nwitness_comp_reverse(const witness_t *a, void *oa, const witness_t *b,\n    void *ob) {\n\tassert_u_eq(a->rank, b->rank, \"Witnesses should have equal rank\");\n\n\tassert(oa == (void *)a);\n\tassert(ob == (void *)b);\n\n\treturn -strcmp(a->name, b->name);\n}\n\nTEST_BEGIN(test_witness) {\n\twitness_t a, b;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 0);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\twitness_assert_not_owner(&witness_tsdn, &a);\n\twitness_lock(&witness_tsdn, &a);\n\twitness_assert_owner(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)2U, 0);\n\n\twitness_init(&b, \"b\", 2, NULL, NULL);\n\twitness_assert_not_owner(&witness_tsdn, &b);\n\twitness_lock(&witness_tsdn, &b);\n\twitness_assert_owner(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 2);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 2);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)2U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)3U, 0);\n\n\twitness_unlock(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)2U, 1);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)3U, 0);\n\twitness_unlock(&witness_tsdn, &b);\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\twitness_assert_depth_to_rank(&witness_tsdn, (witness_rank_t)1U, 0);\n}\nTEST_END\n\nTEST_BEGIN(test_witness_comp) {\n\twitness_t a, b, c, d;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, witness_comp, &a);\n\twitness_assert_not_owner(&witness_tsdn, &a);\n\twitness_lock(&witness_tsdn, &a);\n\twitness_assert_owner(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\twitness_init(&b, \"b\", 1, witness_comp, &b);\n\twitness_assert_not_owner(&witness_tsdn, &b);\n\twitness_lock(&witness_tsdn, &b);\n\twitness_assert_owner(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 2);\n\twitness_unlock(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\twitness_lock_error_orig = witness_lock_error;\n\twitness_lock_error = witness_lock_error_intercept;\n\tsaw_lock_error = false;\n\n\twitness_init(&c, \"c\", 1, witness_comp_reverse, &c);\n\twitness_assert_not_owner(&witness_tsdn, &c);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\twitness_lock(&witness_tsdn, &c);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\twitness_unlock(&witness_tsdn, &c);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\tsaw_lock_error = false;\n\n\twitness_init(&d, \"d\", 1, NULL, NULL);\n\twitness_assert_not_owner(&witness_tsdn, &d);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\twitness_lock(&witness_tsdn, &d);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\twitness_unlock(&witness_tsdn, &d);\n\twitness_assert_depth(&witness_tsdn, 1);\n\n\twitness_unlock(&witness_tsdn, &a);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_lock_error = witness_lock_error_orig;\n}\nTEST_END\n\nTEST_BEGIN(test_witness_reversal) {\n\twitness_t a, b;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_lock_error_orig = witness_lock_error;\n\twitness_lock_error = witness_lock_error_intercept;\n\tsaw_lock_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\twitness_init(&b, \"b\", 2, NULL, NULL);\n\n\twitness_lock(&witness_tsdn, &b);\n\twitness_assert_depth(&witness_tsdn, 1);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\twitness_lock(&witness_tsdn, &a);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\n\twitness_unlock(&witness_tsdn, &a);\n\twitness_assert_depth(&witness_tsdn, 1);\n\twitness_unlock(&witness_tsdn, &b);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_lock_error = witness_lock_error_orig;\n}\nTEST_END\n\nTEST_BEGIN(test_witness_recursive) {\n\twitness_t a;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_not_owner_error_orig = witness_not_owner_error;\n\twitness_not_owner_error = witness_not_owner_error_intercept;\n\tsaw_not_owner_error = false;\n\n\twitness_lock_error_orig = witness_lock_error;\n\twitness_lock_error = witness_lock_error_intercept;\n\tsaw_lock_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\n\twitness_lock(&witness_tsdn, &a);\n\tassert_false(saw_lock_error, \"Unexpected witness lock error\");\n\tassert_false(saw_not_owner_error, \"Unexpected witness not owner error\");\n\twitness_lock(&witness_tsdn, &a);\n\tassert_true(saw_lock_error, \"Expected witness lock error\");\n\tassert_true(saw_not_owner_error, \"Expected witness not owner error\");\n\n\twitness_unlock(&witness_tsdn, &a);\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_owner_error = witness_owner_error_orig;\n\twitness_lock_error = witness_lock_error_orig;\n\n}\nTEST_END\n\nTEST_BEGIN(test_witness_unlock_not_owned) {\n\twitness_t a;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_owner_error_orig = witness_owner_error;\n\twitness_owner_error = witness_owner_error_intercept;\n\tsaw_owner_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\n\tassert_false(saw_owner_error, \"Unexpected owner error\");\n\twitness_unlock(&witness_tsdn, &a);\n\tassert_true(saw_owner_error, \"Expected owner error\");\n\n\twitness_assert_lockless(&witness_tsdn);\n\n\twitness_owner_error = witness_owner_error_orig;\n}\nTEST_END\n\nTEST_BEGIN(test_witness_depth) {\n\twitness_t a;\n\twitness_tsdn_t witness_tsdn = { WITNESS_TSD_INITIALIZER };\n\n\ttest_skip_if(!config_debug);\n\n\twitness_depth_error_orig = witness_depth_error;\n\twitness_depth_error = witness_depth_error_intercept;\n\tsaw_depth_error = false;\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\n\twitness_init(&a, \"a\", 1, NULL, NULL);\n\n\tassert_false(saw_depth_error, \"Unexpected depth error\");\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\n\twitness_lock(&witness_tsdn, &a);\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\tassert_true(saw_depth_error, \"Expected depth error\");\n\n\twitness_unlock(&witness_tsdn, &a);\n\n\twitness_assert_lockless(&witness_tsdn);\n\twitness_assert_depth(&witness_tsdn, 0);\n\n\twitness_depth_error = witness_depth_error_orig;\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_witness,\n\t    test_witness_comp,\n\t    test_witness_reversal,\n\t    test_witness_recursive,\n\t    test_witness_unlock_not_owned,\n\t    test_witness_depth);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/zero.c",
    "content": "#include \"test/jemalloc_test.h\"\n\nstatic void\ntest_zero(size_t sz_min, size_t sz_max) {\n\tuint8_t *s;\n\tsize_t sz_prev, sz, i;\n#define MAGIC\t((uint8_t)0x61)\n\n\tsz_prev = 0;\n\ts = (uint8_t *)mallocx(sz_min, 0);\n\tassert_ptr_not_null((void *)s, \"Unexpected mallocx() failure\");\n\n\tfor (sz = sallocx(s, 0); sz <= sz_max;\n\t    sz_prev = sz, sz = sallocx(s, 0)) {\n\t\tif (sz_prev > 0) {\n\t\t\tassert_u_eq(s[0], MAGIC,\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    ZU(0), sz_prev);\n\t\t\tassert_u_eq(s[sz_prev-1], MAGIC,\n\t\t\t    \"Previously allocated byte %zu/%zu is corrupted\",\n\t\t\t    sz_prev-1, sz_prev);\n\t\t}\n\n\t\tfor (i = sz_prev; i < sz; i++) {\n\t\t\tassert_u_eq(s[i], 0x0,\n\t\t\t    \"Newly allocated byte %zu/%zu isn't zero-filled\",\n\t\t\t    i, sz);\n\t\t\ts[i] = MAGIC;\n\t\t}\n\n\t\tif (xallocx(s, sz+1, 0, 0) == sz) {\n\t\t\ts = (uint8_t *)rallocx(s, sz+1, 0);\n\t\t\tassert_ptr_not_null((void *)s,\n\t\t\t    \"Unexpected rallocx() failure\");\n\t\t}\n\t}\n\n\tdallocx(s, 0);\n#undef MAGIC\n}\n\nTEST_BEGIN(test_zero_small) {\n\ttest_skip_if(!config_fill);\n\ttest_zero(1, SMALL_MAXCLASS-1);\n}\nTEST_END\n\nTEST_BEGIN(test_zero_large) {\n\ttest_skip_if(!config_fill);\n\ttest_zero(SMALL_MAXCLASS+1, (1U << (LG_LARGE_MINCLASS+1)));\n}\nTEST_END\n\nint\nmain(void) {\n\treturn test(\n\t    test_zero_small,\n\t    test_zero_large);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/jemalloc/test/unit/zero.sh",
    "content": "#!/bin/sh\n\nif [ \"x${enable_fill}\" = \"x1\" ] ; then\n  export MALLOC_CONF=\"abort:false,junk:false,zero:true\"\nfi\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/linenoise/.gitignore",
    "content": "linenoise_example\n*.dSYM\nhistory.txt\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/linenoise/README.markdown",
    "content": "# Linenoise\n\nA minimal, zero-config, BSD licensed, readline replacement used in Redis,\nMongoDB, and Android.\n\n* Single and multi line editing mode with the usual key bindings implemented.\n* History handling.\n* Completion.\n* Hints (suggestions at the right of the prompt as you type).\n* About 1,100 lines of BSD license source code.\n* Only uses a subset of VT100 escapes (ANSI.SYS compatible).\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 (Real 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, supporting 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 basic VT100 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, and now can work even on ANSI.SYS compatible terminals, since no\nVT220 specific sequences are used anymore.\n\nThe library is currently about 1100 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 * Linux Buildroot ($TERM = vt100)\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 * ANSI.SYS\n * Emacs comint mode ($TERM = dumb)\n\nPlease test it everywhere you can and report back!\n\n## Let's push this forward!\n\nPatches should be provided in the respect of Linenoise sensibility for small\neasy to understand code.\n\nSend feedbacks to antirez at gmail\n\n# The API\n\nLinenoise is very easy to use, and reading the example shipped with the\nlibrary should get you up to speed ASAP. Here is a list of API calls\nand how to use them.\n\n    char *linenoise(const char *prompt);\n\nThis is the main Linenoise call: it shows the user a prompt with line editing\nand history capabilities. The prompt you specify is used as a prompt, that is,\nit will be printed to the left of the cursor. The library returns a buffer\nwith the line composed by the user, or NULL on end of file or when there\nis an out of memory condition.\n\nWhen a tty is detected (the user is actually typing into a terminal session)\nthe maximum editable line length is `LINENOISE_MAX_LINE`. When instead the\nstandard input is not a tty, which happens every time you redirect a file\nto a program, or use it in an Unix pipeline, there are no limits to the\nlength of the line that can be returned.\n\nThe returned line should be freed with the `free()` standard system call.\nHowever sometimes it could happen that your program uses a different dynamic\nallocation library, so you may also used `linenoiseFree` to make sure the\nline is freed with the same allocator it was created.\n\nThe canonical loop used by a program using Linenoise will be something like\nthis:\n\n    while((line = linenoise(\"hello> \")) != NULL) {\n        printf(\"You wrote: %s\\n\", line);\n        linenoiseFree(line); /* Or just free(line) if you use libc malloc. */\n    }\n\n## Single line VS multi line editing\n\nBy default, Linenoise uses single line editing, that is, a single row on the\nscreen will be used, and as the user types more, the text will scroll towards\nleft to make room. This works if your program is one where the user is\nunlikely to write a lot of text, otherwise multi line editing, where multiple\nscreens rows are used, can be a lot more comfortable.\n\nIn order to enable multi line editing use the following API call:\n\n    linenoiseSetMultiLine(1);\n\nYou can disable it using `0` as argument.\n\n## History\n\nLinenoise supporst history, so that the user does not have to retype\nagain and again the same things, but can use the down and up arrows in order\nto search and re-edit already inserted lines of text.\n\nThe followings are the history API calls:\n\n    int linenoiseHistoryAdd(const char *line);\n    int linenoiseHistorySetMaxLen(int len);\n    int linenoiseHistorySave(const char *filename);\n    int linenoiseHistoryLoad(const char *filename);\n\nUse `linenoiseHistoryAdd` every time you want to add a new element\nto the top of the history (it will be the first the user will see when\nusing the up arrow).\n\nNote that for history to work, you have to set a length for the history\n(which is zero by default, so history will be disabled if you don't set\na proper one). This is accomplished using the `linenoiseHistorySetMaxLen`\nfunction.\n\nLinenoise has direct support for persisting the history into an history\nfile. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do\njust that. Both functions return -1 on error and 0 on success.\n\n## Mask mode\n\nSometimes it is useful to allow the user to type passwords or other\nsecrets that should not be displayed. For such situations linenoise supports\na \"mask mode\" that will just replace the characters the user is typing \nwith `*` characters, like in the following example:\n\n    $ ./linenoise_example\n    hello> get mykey\n    echo: 'get mykey'\n    hello> /mask\n    hello> *********\n\nYou can enable and disable mask mode using the following two functions:\n\n    void linenoiseMaskModeEnable(void);\n    void linenoiseMaskModeDisable(void);\n\n## Completion\n\nLinenoise supports completion, which is the ability to complete the user\ninput when she or he presses the `<TAB>` key.\n\nIn order to use completion, you need to register a completion callback, which\nis called every time the user presses `<TAB>`. Your callback will return a\nlist of items that are completions for the current string.\n\nThe following is an example of registering a completion callback:\n\n    linenoiseSetCompletionCallback(completion);\n\nThe completion must be a function returning `void` and getting as input\na `const char` pointer, which is the line the user has typed so far, and\na `linenoiseCompletions` object pointer, which is used as argument of\n`linenoiseAddCompletion` in order to add completions inside the callback.\nAn example will make it more clear:\n\n    void completion(const char *buf, linenoiseCompletions *lc) {\n        if (buf[0] == 'h') {\n            linenoiseAddCompletion(lc,\"hello\");\n            linenoiseAddCompletion(lc,\"hello there\");\n        }\n    }\n\nBasically in your completion callback, you inspect the input, and return\na list of items that are good completions by using `linenoiseAddCompletion`.\n\nIf you want to test the completion feature, compile the example program\nwith `make`, run it, type `h` and press `<TAB>`.\n\n## Hints\n\nLinenoise has a feature called *hints* which is very useful when you\nuse Linenoise in order to implement a REPL (Read Eval Print Loop) for\na program that accepts commands and arguments, but may also be useful in\nother conditions.\n\nThe feature shows, on the right of the cursor, as the user types, hints that\nmay be useful. The hints can be displayed using a different color compared\nto the color the user is typing, and can also be bold.\n\nFor example as the user starts to type `\"git remote add\"`, with hints it's\npossible to show on the right of the prompt a string `<name> <url>`.\n\nThe feature works similarly to the history feature, using a callback.\nTo register the callback we use:\n\n    linenoiseSetHintsCallback(hints);\n\nThe callback itself is implemented like this:\n\n    char *hints(const char *buf, int *color, int *bold) {\n        if (!strcasecmp(buf,\"git remote add\")) {\n            *color = 35;\n            *bold = 0;\n            return \" <name> <url>\";\n        }\n        return NULL;\n    }\n\nThe callback function returns the string that should be displayed or NULL\nif no hint is available for the text the user currently typed. The returned\nstring will be trimmed as needed depending on the number of columns available\non the screen.\n\nIt is possible to return a string allocated in dynamic way, by also registering\na function to deallocate the hint string once used:\n\n    void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);\n\nThe free hint callback will just receive the pointer and free the string\nas needed (depending on how the hits callback allocated it).\n\nAs you can see in the example above, a `color` (in xterm color terminal codes)\ncan be provided together with a `bold` attribute. If no color is set, the\ncurrent terminal foreground color is used. If no bold attribute is set,\nnon-bold text is printed.\n\nColor codes are:\n\n    red = 31\n    green = 32\n    yellow = 33\n    blue = 34\n    magenta = 35\n    cyan = 36\n    white = 37;\n\n## Screen handling\n\nSometimes you may want to clear the screen as a result of something the\nuser typed. You can do this by calling the following function:\n\n    void linenoiseClearScreen(void);\n\n## Related projects\n\n* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language.\n* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift.\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/linenoise/example.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.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\nchar *hints(const char *buf, int *color, int *bold) {\n    if (!strcasecmp(buf,\"hello\")) {\n        *color = 35;\n        *bold = 0;\n        return \" World\";\n    }\n    return NULL;\n}\n\nint main(int argc, char **argv) {\n    char *line;\n    char *prgname = argv[0];\n\n    /* Parse options, with --multiline we enable multi line editing. */\n    while(argc > 1) {\n        argc--;\n        argv++;\n        if (!strcmp(*argv,\"--multiline\")) {\n            linenoiseSetMultiLine(1);\n            printf(\"Multi-line mode enabled.\\n\");\n        } else if (!strcmp(*argv,\"--keycodes\")) {\n            linenoisePrintKeyCodes();\n            exit(0);\n        } else {\n            fprintf(stderr, \"Usage: %s [--multiline] [--keycodes]\\n\", prgname);\n            exit(1);\n        }\n    }\n\n    /* Set the completion callback. This will be called every time the\n     * user uses the <tab> key. */\n    linenoiseSetCompletionCallback(completion);\n    linenoiseSetHintsCallback(hints);\n\n    /* Load history from file. The history file is just a plain text file\n     * where entries are separated by newlines. */\n    linenoiseHistoryLoad(\"history.txt\"); /* Load the history at startup */\n\n    /* Now this is the main loop of the typical linenoise-based application.\n     * The call to linenoise() will block as long as the user types something\n     * and presses enter.\n     *\n     * The typed string is returned as a malloc() allocated string by\n     * linenoise, so the user needs to free() it. */\n    \n    while((line = linenoise(\"hello> \")) != NULL) {\n        /* Do something with the string. */\n        if (line[0] != '\\0' && line[0] != '/') {\n            printf(\"echo: '%s'\\n\", line);\n            linenoiseHistoryAdd(line); /* Add to the history. */\n            linenoiseHistorySave(\"history.txt\"); /* Save the history on disk. */\n        } else if (!strncmp(line,\"/historylen\",11)) {\n            /* The \"/historylen\" command will change the history len. */\n            int len = atoi(line+11);\n            linenoiseHistorySetMaxLen(len);\n        } else if (!strncmp(line, \"/mask\", 5)) {\n            linenoiseMaskModeEnable();\n        } else if (!strncmp(line, \"/unmask\", 7)) {\n            linenoiseMaskModeDisable();\n        } else if (line[0] == '/') {\n            printf(\"Unreconized command: %s\\n\", line);\n        }\n        free(line);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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-2016, 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 * 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 n chars\n *\n * CUB (CUrsor Backward)\n *    Sequence: ESC [ n D\n *    Effect: moves cursor backward n chars\n *\n * The following is used to get the terminal width if getting\n * the width with the TIOCGWINSZ ioctl fails\n *\n * DSR (Device Status Report)\n *    Sequence: ESC [ 6 n\n *    Effect: reports the current cusor position as ESC [ n ; m R\n *            where n is the row and m is the column\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 * When linenoiseClearScreen() is called, two additional escape sequences\n * are used in order to clear the screen and position the cursor at home\n * position.\n *\n * CUP (Cursor position)\n *    Sequence: ESC [ H\n *    Effect: moves the cursor to upper left corner\n *\n * ED (Erase display)\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/stat.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;\nstatic linenoiseHintsCallback *hintsCallback = NULL;\nstatic linenoiseFreeHintsCallback *freeHintsCallback = NULL;\n\nstatic struct termios orig_termios; /* In order to restore at exit.*/\nstatic int maskmode = 0; /* Show \"***\" instead of input. For passwords. */\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/* Enable \"mask mode\". When it is enabled, instead of the input that\n * the user is typing, the terminal will just display a corresponding\n * number of asterisks, like \"****\". This is useful for passwords and other\n * secrets that should not be displayed. */\nvoid linenoiseMaskModeEnable(void) {\n    maskmode = 1;\n}\n\n/* Disable mask mode. */\nvoid linenoiseMaskModeDisable(void) {\n    maskmode = 0;\n}\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/* Register a hits function to be called to show hits to the user at the\n * right of the prompt. */\nvoid linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {\n    hintsCallback = fn;\n}\n\n/* Register a function to free the hints returned by the hints callback\n * registered with linenoiseSetHintsCallback(). */\nvoid linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {\n    freeHintsCallback = 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/* Helper of refreshSingleLine() and refreshMultiLine() to show hints\n * to the right of the prompt. */\nvoid refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {\n    char seq[64];\n    if (hintsCallback && plen+l->len < l->cols) {\n        int color = -1, bold = 0;\n        char *hint = hintsCallback(l->buf,&color,&bold);\n        if (hint) {\n            int hintlen = strlen(hint);\n            int hintmaxlen = l->cols-(plen+l->len);\n            if (hintlen > hintmaxlen) hintlen = hintmaxlen;\n            if (bold == 1 && color == -1) color = 37;\n            if (color != -1 || bold != 0)\n                snprintf(seq,64,\"\\033[%d;%d;49m\",bold,color);\n            else\n                seq[0] = '\\0';\n            abAppend(ab,seq,strlen(seq));\n            abAppend(ab,hint,hintlen);\n            if (color != -1 || bold != 0)\n                abAppend(ab,\"\\033[0m\",4);\n            /* Call the function to free the hint returned. */\n            if (freeHintsCallback) freeHintsCallback(hint);\n        }\n    }\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,\"\\r\");\n    abAppend(&ab,seq,strlen(seq));\n    /* Write the prompt and the current buffer content */\n    abAppend(&ab,l->prompt,strlen(l->prompt));\n    if (maskmode == 1) {\n        while (len--) abAppend(&ab,\"*\",1);\n    } else {\n        abAppend(&ab,buf,len);\n    }\n    /* Show hits if any. */\n    refreshShowHints(&ab,l,plen);\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,\"\\r\\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 col; /* colum position, zero-based. */\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,\"\\r\\x1b[0K\\x1b[1A\");\n        abAppend(&ab,seq,strlen(seq));\n    }\n\n    /* Clean the top line. */\n    lndebug(\"clear\");\n    snprintf(seq,64,\"\\r\\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    if (maskmode == 1) {\n        unsigned int i;\n        for (i = 0; i < l->len; i++) abAppend(&ab,\"*\",1);\n    } else {\n        abAppend(&ab,l->buf,l->len);\n    }\n\n    /* Show hits if any. */\n    refreshShowHints(&ab,l,plen);\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,\"\\r\");\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    col = (plen+(int)l->pos) % (int)l->cols;\n    lndebug(\"set col %d\", 1+col);\n    if (col)\n        snprintf(seq,64,\"\\r\\x1b[%dC\", col);\n    else\n        snprintf(seq,64,\"\\r\");\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 && !hintsCallback)) {\n                /* Avoid a full update of the line in the\n                 * trivial case. */\n                char d = (maskmode==1) ? '*' : c;\n                if (write(l->ofd,&d,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            if (mlmode) linenoiseEditMoveEnd(&l);\n            if (hintsCallback) {\n                /* Force a refresh without hints to leave the previous\n                 * line as the user typed it after a newline. */\n                linenoiseHintsCallback *hc = hintsCallback;\n                hintsCallback = NULL;\n                refreshLine(&l);\n                hintsCallback = hc;\n            }\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 if 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(\"\\r\"); /* 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\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    return count;\n}\n\n/* This function is called when linenoise() is called with the standard\n * input file descriptor not attached to a TTY. So for example when the\n * program using linenoise is called in pipe or with a file redirected\n * to its standard input. In this case, we want to be able to return the\n * line regardless of its length (by default we are limited to 4k). */\nstatic char *linenoiseNoTTY(void) {\n    char *line = NULL;\n    size_t len = 0, maxlen = 0;\n\n    while(1) {\n        if (len == maxlen) {\n            if (maxlen == 0) maxlen = 16;\n            maxlen *= 2;\n            char *oldval = line;\n            line = realloc(line,maxlen);\n            if (line == NULL) {\n                if (oldval) free(oldval);\n                return NULL;\n            }\n        }\n        int c = fgetc(stdin);\n        if (c == EOF || c == '\\n') {\n            if (c == EOF && len == 0) {\n                free(line);\n                return NULL;\n            } else {\n                line[len] = '\\0';\n                return line;\n            }\n        } else {\n            line[len] = c;\n            len++;\n        }\n    }\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 (!isatty(STDIN_FILENO)) {\n        /* Not a tty: read from file / pipe. In this mode we don't want any\n         * limit to the line size, so we call a function to handle that. */\n        return linenoiseNoTTY();\n    } else 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/* This is just a wrapper the user may want to call in order to make sure\n * the linenoise returned buffer is freed with the same allocator it was\n * created with. Useful when the main program is using an alternative\n * allocator. */\nvoid linenoiseFree(void *ptr) {\n    free(ptr);\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    mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);\n    FILE *fp;\n    int j;\n\n    fp = fopen(filename,\"w\");\n    umask(old_umask);\n    if (fp == NULL) return -1;\n    chmod(filename,S_IRUSR|S_IWUSR);\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": "Chapter07/redis-6.0.3/deps/linenoise/linenoise.h",
    "content": "/* linenoise.h -- VERSION 1.0\n *\n * Guerrilla line editing library against the idea that a line editing lib\n * needs to be 20,000 lines of C code.\n *\n * See linenoise.c for more information.\n *\n * ------------------------------------------------------------------------\n *\n * Copyright (c) 2010-2014, 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#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 *);\ntypedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);\ntypedef void(linenoiseFreeHintsCallback)(void *);\nvoid linenoiseSetCompletionCallback(linenoiseCompletionCallback *);\nvoid linenoiseSetHintsCallback(linenoiseHintsCallback *);\nvoid linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);\nvoid linenoiseAddCompletion(linenoiseCompletions *, const char *);\n\nchar *linenoise(const char *prompt);\nvoid linenoiseFree(void *ptr);\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);\nvoid linenoiseMaskModeEnable(void);\nvoid linenoiseMaskModeDisable(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __LINENOISE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 fpconv.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\tlua_bit.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": "Chapter07/redis-6.0.3/deps/lua/src/fpconv.c",
    "content": "/* fpconv - Floating point conversion routines\n *\n * Copyright (c) 2011-2012  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/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries\n * with locale support will break when the decimal separator is a comma.\n *\n * fpconv_* will around these issues with a translation buffer if required.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <string.h>\n\n#include \"fpconv.h\"\n\n/* Lua CJSON assumes the locale is the same for all threads within a\n * process and doesn't change after initialisation.\n *\n * This avoids the need for per thread storage or expensive checks\n * for call. */\nstatic char locale_decimal_point = '.';\n\n/* In theory multibyte decimal_points are possible, but\n * Lua CJSON only supports UTF-8 and known locales only have\n * single byte decimal points ([.,]).\n *\n * localconv() may not be thread safe (=>crash), and nl_langinfo() is\n * not supported on some platforms. Use sprintf() instead - if the\n * locale does change, at least Lua CJSON won't crash. */\nstatic void fpconv_update_locale()\n{\n    char buf[8];\n\n    snprintf(buf, sizeof(buf), \"%g\", 0.5);\n\n    /* Failing this test might imply the platform has a buggy dtoa\n     * implementation or wide characters */\n    if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {\n        fprintf(stderr, \"Error: wide characters found or printf() bug.\");\n        abort();\n    }\n\n    locale_decimal_point = buf[1];\n}\n\n/* Check for a valid number character: [-+0-9a-yA-Y.]\n * Eg: -0.6e+5, infinity, 0xF0.F0pF0\n *\n * Used to find the probable end of a number. It doesn't matter if\n * invalid characters are counted - strtod() will find the valid\n * number if it exists.  The risk is that slightly more memory might\n * be allocated before a parse error occurs. */\nstatic inline int valid_number_character(char ch)\n{\n    char lower_ch;\n\n    if ('0' <= ch && ch <= '9')\n        return 1;\n    if (ch == '-' || ch == '+' || ch == '.')\n        return 1;\n\n    /* Hex digits, exponent (e), base (p), \"infinity\",.. */\n    lower_ch = ch | 0x20;\n    if ('a' <= lower_ch && lower_ch <= 'y')\n        return 1;\n\n    return 0;\n}\n\n/* Calculate the size of the buffer required for a strtod locale\n * conversion. */\nstatic int strtod_buffer_size(const char *s)\n{\n    const char *p = s;\n\n    while (valid_number_character(*p))\n        p++;\n\n    return p - s;\n}\n\n/* Similar to strtod(), but must be passed the current locale's decimal point\n * character. Guaranteed to be called at the start of any valid number in a string */\ndouble fpconv_strtod(const char *nptr, char **endptr)\n{\n    char localbuf[FPCONV_G_FMT_BUFSIZE];\n    char *buf, *endbuf, *dp;\n    int buflen;\n    double value;\n\n    /* System strtod() is fine when decimal point is '.' */\n    if (locale_decimal_point == '.')\n        return strtod(nptr, endptr);\n\n    buflen = strtod_buffer_size(nptr);\n    if (!buflen) {\n        /* No valid characters found, standard strtod() return */\n        *endptr = (char *)nptr;\n        return 0;\n    }\n\n    /* Duplicate number into buffer */\n    if (buflen >= FPCONV_G_FMT_BUFSIZE) {\n        /* Handle unusually large numbers */\n        buf = malloc(buflen + 1);\n        if (!buf) {\n            fprintf(stderr, \"Out of memory\");\n            abort();\n        }\n    } else {\n        /* This is the common case.. */\n        buf = localbuf;\n    }\n    memcpy(buf, nptr, buflen);\n    buf[buflen] = 0;\n\n    /* Update decimal point character if found */\n    dp = strchr(buf, '.');\n    if (dp)\n        *dp = locale_decimal_point;\n\n    value = strtod(buf, &endbuf);\n    *endptr = (char *)&nptr[endbuf - buf];\n    if (buflen >= FPCONV_G_FMT_BUFSIZE)\n        free(buf);\n\n    return value;\n}\n\n/* \"fmt\" must point to a buffer of at least 6 characters */\nstatic void set_number_format(char *fmt, int precision)\n{\n    int d1, d2, i;\n\n    assert(1 <= precision && precision <= 14);\n\n    /* Create printf format (%.14g) from precision */\n    d1 = precision / 10;\n    d2 = precision % 10;\n    fmt[0] = '%';\n    fmt[1] = '.';\n    i = 2;\n    if (d1) {\n        fmt[i++] = '0' + d1;\n    }\n    fmt[i++] = '0' + d2;\n    fmt[i++] = 'g';\n    fmt[i] = 0;\n}\n\n/* Assumes there is always at least 32 characters available in the target buffer */\nint fpconv_g_fmt(char *str, double num, int precision)\n{\n    char buf[FPCONV_G_FMT_BUFSIZE];\n    char fmt[6];\n    int len;\n    char *b;\n\n    set_number_format(fmt, precision);\n\n    /* Pass through when decimal point character is dot. */\n    if (locale_decimal_point == '.')\n        return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);\n\n    /* snprintf() to a buffer then translate for other decimal point characters */\n    len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);\n\n    /* Copy into target location. Translate decimal point if required */\n    b = buf;\n    do {\n        *str++ = (*b == locale_decimal_point ? '.' : *b);\n    } while(*b++);\n\n    return len;\n}\n\nvoid fpconv_init()\n{\n    fpconv_update_locale();\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/lua/src/fpconv.h",
    "content": "/* Lua CJSON floating point conversion routines */\n\n/* Buffer required to store the largest string representation of a double.\n *\n * Longest double printed with %.14g is 21 characters long:\n * -1.7976931348623e+308 */\n# define FPCONV_G_FMT_BUFSIZE   32\n\n#ifdef USE_INTERNAL_FPCONV\nstatic inline void fpconv_init()\n{\n    /* Do nothing - not required */\n}\n#else\nextern void fpconv_init();\n#endif\n\nextern int fpconv_g_fmt(char*, double, int);\nextern double fpconv_strtod(const char*, char**);\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 = (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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/lua/src/lua_bit.c",
    "content": "/*\n** Lua BitOp -- a bit operations library for Lua 5.1/5.2.\n** http://bitop.luajit.org/\n**\n** Copyright (C) 2008-2012 Mike Pall. 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** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]\n*/\n\n#define LUA_BITOP_VERSION\t\"1.0.2\"\n\n#define LUA_LIB\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#ifdef _MSC_VER\n/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef unsigned __int64 uint64_t;\n#else\n#include <stdint.h>\n#endif\n\ntypedef int32_t SBits;\ntypedef uint32_t UBits;\n\ntypedef union {\n  lua_Number n;\n#ifdef LUA_NUMBER_DOUBLE\n  uint64_t b;\n#else\n  UBits b;\n#endif\n} BitNum;\n\n/* Convert argument to bit type. */\nstatic UBits barg(lua_State *L, int idx)\n{\n  BitNum bn;\n  UBits b;\n#if LUA_VERSION_NUM < 502\n  bn.n = lua_tonumber(L, idx);\n#else\n  bn.n = luaL_checknumber(L, idx);\n#endif\n#if defined(LUA_NUMBER_DOUBLE)\n  bn.n += 6755399441055744.0;  /* 2^52+2^51 */\n#ifdef SWAPPED_DOUBLE\n  b = (UBits)(bn.b >> 32);\n#else\n  b = (UBits)bn.b;\n#endif\n#elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \\\n      defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \\\n      defined(LUA_NUMBER_LLONG)\n  if (sizeof(UBits) == sizeof(lua_Number))\n    b = bn.b;\n  else\n    b = (UBits)(SBits)bn.n;\n#elif defined(LUA_NUMBER_FLOAT)\n#error \"A 'float' lua_Number type is incompatible with this library\"\n#else\n#error \"Unknown number type, check LUA_NUMBER_* in luaconf.h\"\n#endif\n#if LUA_VERSION_NUM < 502\n  if (b == 0 && !lua_isnumber(L, idx)) {\n    luaL_typerror(L, idx, \"number\");\n  }\n#endif\n  return b;\n}\n\n/* Return bit type. */\n#define BRET(b)  lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;\n\nstatic int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }\nstatic int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }\n\n#define BIT_OP(func, opr) \\\n  static int func(lua_State *L) { int i; UBits b = barg(L, 1); \\\n    for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) }\nBIT_OP(bit_band, &=)\nBIT_OP(bit_bor, |=)\nBIT_OP(bit_bxor, ^=)\n\n#define bshl(b, n)  (b << n)\n#define bshr(b, n)  (b >> n)\n#define bsar(b, n)  ((SBits)b >> n)\n#define brol(b, n)  ((b << n) | (b >> (32-n)))\n#define bror(b, n)  ((b << (32-n)) | (b >> n))\n#define BIT_SH(func, fn) \\\n  static int func(lua_State *L) { \\\n    UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }\nBIT_SH(bit_lshift, bshl)\nBIT_SH(bit_rshift, bshr)\nBIT_SH(bit_arshift, bsar)\nBIT_SH(bit_rol, brol)\nBIT_SH(bit_ror, bror)\n\nstatic int bit_bswap(lua_State *L)\n{\n  UBits b = barg(L, 1);\n  b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);\n  BRET(b)\n}\n\nstatic int bit_tohex(lua_State *L)\n{\n  UBits b = barg(L, 1);\n  SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);\n  const char *hexdigits = \"0123456789abcdef\";\n  char buf[8];\n  int i;\n  if (n < 0) { n = -n; hexdigits = \"0123456789ABCDEF\"; }\n  if (n > 8) n = 8;\n  for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }\n  lua_pushlstring(L, buf, (size_t)n);\n  return 1;\n}\n\nstatic const struct luaL_Reg bit_funcs[] = {\n  { \"tobit\",\tbit_tobit },\n  { \"bnot\",\tbit_bnot },\n  { \"band\",\tbit_band },\n  { \"bor\",\tbit_bor },\n  { \"bxor\",\tbit_bxor },\n  { \"lshift\",\tbit_lshift },\n  { \"rshift\",\tbit_rshift },\n  { \"arshift\",\tbit_arshift },\n  { \"rol\",\tbit_rol },\n  { \"ror\",\tbit_ror },\n  { \"bswap\",\tbit_bswap },\n  { \"tohex\",\tbit_tohex },\n  { NULL, NULL }\n};\n\n/* Signed right-shifts are implementation-defined per C89/C99.\n** But the de facto standard are arithmetic right-shifts on two's\n** complement CPUs. This behaviour is required here, so test for it.\n*/\n#define BAD_SAR\t\t(bsar(-8, 2) != (SBits)-2)\n\nLUALIB_API int luaopen_bit(lua_State *L)\n{\n  UBits b;\n  lua_pushnumber(L, (lua_Number)1437217655L);\n  b = barg(L, -1);\n  if (b != (UBits)1437217655L || BAD_SAR) {  /* Perform a simple self-test. */\n    const char *msg = \"compiled with incompatible luaconf.h\";\n#ifdef LUA_NUMBER_DOUBLE\n#ifdef _WIN32\n    if (b == (UBits)1610612736L)\n      msg = \"use D3DCREATE_FPU_PRESERVE with DirectX\";\n#endif\n    if (b == (UBits)1127743488L)\n      msg = \"not compiled with SWAPPED_DOUBLE\";\n#endif\n    if (BAD_SAR)\n      msg = \"arithmetic right-shift broken\";\n    luaL_error(L, \"bit library self-test failed (%s)\", msg);\n  }\n#if LUA_VERSION_NUM < 502\n  luaL_register(L, \"bit\", bit_funcs);\n#else\n  luaL_newlib(L, bit_funcs);\n#endif\n  return 1;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/deps/lua/src/lua_cjson.c",
    "content": "/* Lua CJSON - JSON support for Lua\n *\n * Copyright (c) 2010-2012  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 <limits.h>\n#include \"lua.h\"\n#include \"lauxlib.h\"\n\n#include \"strbuf.h\"\n#include \"fpconv.h\"\n\n#include \"../../../src/solarisfixes.h\"\n\n#ifndef CJSON_MODNAME\n#define CJSON_MODNAME   \"cjson\"\n#endif\n\n#ifndef CJSON_VERSION\n#define CJSON_VERSION   \"2.1.0\"\n#endif\n\n/* Workaround for Solaris platforms missing isinf() */\n#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(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_ENCODE_MAX_DEPTH 1000\n#define DEFAULT_DECODE_MAX_DEPTH 1000\n#define DEFAULT_ENCODE_INVALID_NUMBERS 0\n#define DEFAULT_DECODE_INVALID_NUMBERS 1\n#define DEFAULT_ENCODE_KEEP_BUFFER 1\n#define DEFAULT_ENCODE_NUMBER_PRECISION 14\n\n#ifdef DISABLE_INVALID_NUMBERS\n#undef DEFAULT_DECODE_INVALID_NUMBERS\n#define DEFAULT_DECODE_INVALID_NUMBERS 0\n#endif\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\n    /* encode_buf is only allocated and used when\n     * encode_keep_buffer is set */\n    strbuf_t encode_buf;\n\n    int encode_sparse_convert;\n    int encode_sparse_ratio;\n    int encode_sparse_safe;\n    int encode_max_depth;\n    int encode_invalid_numbers;     /* 2 => Encode as \"null\" */\n    int encode_number_precision;\n    int encode_keep_buffer;\n\n    int decode_invalid_numbers;\n    int decode_max_depth;\n} json_config_t;\n\ntypedef struct {\n    const char *data;\n    const char *ptr;\n    strbuf_t *tmp;    /* Temporary storage for strings */\n    json_config_t *cfg;\n    int current_depth;\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\n/* ===== CONFIGURATION ===== */\n\nstatic json_config_t *json_fetch_config(lua_State *l)\n{\n    json_config_t *cfg;\n\n    cfg = lua_touserdata(l, lua_upvalueindex(1));\n    if (!cfg)\n        luaL_error(l, \"BUG: Unable to fetch CJSON configuration\");\n\n    return cfg;\n}\n\n/* Ensure the correct number of arguments have been provided.\n * Pad with nil to allow other functions to simply check arg[i]\n * to find whether an argument was provided */\nstatic json_config_t *json_arg_init(lua_State *l, int args)\n{\n    luaL_argcheck(l, lua_gettop(l) <= args, args + 1,\n                  \"found too many arguments\");\n\n    while (lua_gettop(l) < args)\n        lua_pushnil(l);\n\n    return json_fetch_config(l);\n}\n\n/* Process integer options for configuration functions */\nstatic int json_integer_option(lua_State *l, int optindex, int *setting,\n                               int min, int max)\n{\n    char errmsg[64];\n    int value;\n\n    if (!lua_isnil(l, optindex)) {\n        value = luaL_checkinteger(l, optindex);\n        snprintf(errmsg, sizeof(errmsg), \"expected integer between %d and %d\", min, max);\n        luaL_argcheck(l, min <= value && value <= max, 1, errmsg);\n        *setting = value;\n    }\n\n    lua_pushinteger(l, *setting);\n\n    return 1;\n}\n\n/* Process enumerated arguments for a configuration function */\nstatic int json_enum_option(lua_State *l, int optindex, int *setting,\n                            const char **options, int bool_true)\n{\n    static const char *bool_options[] = { \"off\", \"on\", NULL };\n\n    if (!options) {\n        options = bool_options;\n        bool_true = 1;\n    }\n\n    if (!lua_isnil(l, optindex)) {\n        if (bool_true && lua_isboolean(l, optindex))\n            *setting = lua_toboolean(l, optindex) * bool_true;\n        else\n            *setting = luaL_checkoption(l, optindex, NULL, options);\n    }\n\n    if (bool_true && (*setting == 0 || *setting == bool_true))\n        lua_pushboolean(l, *setting);\n    else\n        lua_pushstring(l, options[*setting]);\n\n    return 1;\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 = json_arg_init(l, 3);\n\n    json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1);\n    json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX);\n    json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX);\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 = json_arg_init(l, 1);\n\n    return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX);\n}\n\n/* Configures the maximum number of nested arrays/objects allowed when\n * encoding */\nstatic int json_cfg_decode_max_depth(lua_State *l)\n{\n    json_config_t *cfg = json_arg_init(l, 1);\n\n    return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX);\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 = json_arg_init(l, 1);\n\n    return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14);\n}\n\n/* Configures JSON encoding buffer persistence */\nstatic int json_cfg_encode_keep_buffer(lua_State *l)\n{\n    json_config_t *cfg = json_arg_init(l, 1);\n    int old_value;\n\n    old_value = cfg->encode_keep_buffer;\n\n    json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1);\n\n    /* Init / free the buffer if the setting has changed */\n    if (old_value ^ cfg->encode_keep_buffer) {\n        if (cfg->encode_keep_buffer)\n            strbuf_init(&cfg->encode_buf, 0);\n        else\n            strbuf_free(&cfg->encode_buf);\n    }\n\n    return 1;\n}\n\n#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV)\nvoid json_verify_invalid_number_setting(lua_State *l, int *setting)\n{\n    if (*setting == 1) {\n        *setting = 0;\n        luaL_error(l, \"Infinity, NaN, and/or hexadecimal numbers are not supported.\");\n    }\n}\n#else\n#define json_verify_invalid_number_setting(l, s)    do { } while(0)\n#endif\n\nstatic int json_cfg_encode_invalid_numbers(lua_State *l)\n{\n    static const char *options[] = { \"off\", \"on\", \"null\", NULL };\n    json_config_t *cfg = json_arg_init(l, 1);\n\n    json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1);\n\n    json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);\n\n    return 1;\n}\n\nstatic int json_cfg_decode_invalid_numbers(lua_State *l)\n{\n    json_config_t *cfg = json_arg_init(l, 1);\n\n    json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1);\n\n    json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);\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    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_ENCODE_MAX_DEPTH;\n    cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH;\n    cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS;\n    cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS;\n    cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER;\n    cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION;\n\n#if DEFAULT_ENCODE_KEEP_BUFFER > 0\n    strbuf_init(&cfg->encode_buf, 0);\n#endif\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/* ===== ENCODING ===== */\n\nstatic void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex,\n                                  const char *reason)\n{\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(json);\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, strbuf_t *json)\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, json, -1, \"excessively sparse array\");\n\n        return -1;\n    }\n\n    return max;\n}\n\nstatic void json_check_encode_depth(lua_State *l, json_config_t *cfg,\n                                    int current_depth, strbuf_t *json)\n{\n    /* Ensure there are enough slots free to traverse a table (key,\n     * value) and push a string for a potential error message.\n     *\n     * Unlike \"decode\", the key and value are still on the stack when\n     * lua_checkstack() is called.  Hence an extra slot for luaL_error()\n     * below is required just in case the next check to lua_checkstack()\n     * fails.\n     *\n     * While this won't cause a crash due to the EXTRA_STACK reserve\n     * slots, it would still be an improper use of the API. */\n    if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3))\n        return;\n\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(json);\n\n    luaL_error(l, \"Cannot serialise, excessive nesting (%d)\",\n               current_depth);\n}\n\nstatic void json_append_data(lua_State *l, json_config_t *cfg,\n                             int current_depth, 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, int current_depth,\n                              strbuf_t *json, int array_length)\n{\n    int comma, i;\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, current_depth, json);\n        lua_pop(l, 1);\n    }\n\n    strbuf_append_char(json, ']');\n}\n\nstatic void json_append_number(lua_State *l, json_config_t *cfg,\n                               strbuf_t *json, int lindex)\n{\n    double num = lua_tonumber(l, lindex);\n    int len;\n\n    if (cfg->encode_invalid_numbers == 0) {\n        /* Prevent encoding invalid numbers */\n        if (isinf(num) || isnan(num))\n            json_encode_exception(l, cfg, json, lindex, \"must not be NaN or Inf\");\n    } else if (cfg->encode_invalid_numbers == 1) {\n        /* Encode invalid numbers, but handle \"nan\" separately\n         * since some platforms may encode as \"-nan\". */\n        if (isnan(num)) {\n            strbuf_append_mem(json, \"nan\", 3);\n            return;\n        }\n    } else {\n        /* Encode invalid numbers as \"null\" */\n        if (isinf(num) || isnan(num)) {\n            strbuf_append_mem(json, \"null\", 4);\n            return;\n        }\n    }\n\n    strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE);\n    len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision);\n    strbuf_extend_length(json, len);\n}\n\nstatic void json_append_object(lua_State *l, json_config_t *cfg,\n                               int current_depth, strbuf_t *json)\n{\n    int comma, keytype;\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, cfg, json, -2);\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, json, -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, current_depth, json);\n        lua_pop(l, 1);\n        /* table, key */\n    }\n\n    strbuf_append_char(json, '}');\n}\n\n/* Serialise Lua data into JSON string. */\nstatic void json_append_data(lua_State *l, json_config_t *cfg,\n                             int current_depth, 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, cfg, json, -1);\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        current_depth++;\n        json_check_encode_depth(l, cfg, current_depth, json);\n        len = lua_array_length(l, cfg, json);\n        if (len > 0)\n            json_append_array(l, cfg, current_depth, json, len);\n        else\n            json_append_object(l, cfg, current_depth, 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, json, -1, \"type not supported\");\n        /* never returns */\n    }\n}\n\nstatic int json_encode(lua_State *l)\n{\n    json_config_t *cfg = json_fetch_config(l);\n    strbuf_t local_encode_buf;\n    strbuf_t *encode_buf;\n    char *json;\n    int len;\n\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    if (!cfg->encode_keep_buffer) {\n        /* Use private buffer */\n        encode_buf = &local_encode_buf;\n        strbuf_init(encode_buf, 0);\n    } else {\n        /* Reuse existing buffer */\n        encode_buf = &cfg->encode_buf;\n        strbuf_reset(encode_buf);\n    }\n\n    json_append_data(l, cfg, 0, encode_buf);\n    json = strbuf_string(encode_buf, &len);\n\n    lua_pushlstring(l, json, len);\n\n    if (!cfg->encode_keep_buffer)\n        strbuf_free(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->ptr + 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->ptr + escape_len) != '\\\\' ||\n            *(json->ptr + escape_len + 1) != 'u') {\n            return -1;\n        }\n\n        /* Fetch the next codepoint */\n        surrogate_low = decode_hex4(json->ptr + 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->ptr += 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->ptr - json->data;\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->ptr == '\"');\n\n    /* Skip \" */\n    json->ptr++;\n\n    /* json->tmp is the temporary strbuf used to accumulate the\n     * decoded string value.\n     * json->tmp is sized to handle JSON containing only a string value.\n     */\n    strbuf_reset(json->tmp);\n\n    while ((ch = *json->ptr) != '\"') {\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->ptr + 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->ptr++;\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->ptr++;\n    }\n    json->ptr++;    /* 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 * - hexadecimal 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    const char *p = json->ptr;\n\n    /* Reject numbers starting with + */\n    if (*p == '+')\n        return 1;\n\n    /* Skip minus sign if it exists */\n    if (*p == '-')\n        p++;\n\n    /* Reject numbers starting with 0x, or leading zeros */\n    if (*p == '0') {\n        int ch2 = *(p + 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 (*p <= '9') {\n        return 0;                           /* Ordinary number */\n    }\n\n    /* Reject inf/nan */\n    if (!strncasecmp(p, \"inf\", 3))\n        return 1;\n    if (!strncasecmp(p, \"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    char *endptr;\n\n    token->type = T_NUMBER;\n    token->value.number = fpconv_strtod(json->ptr, &endptr);\n    if (json->ptr == endptr)\n        json_set_token_error(token, json, \"invalid number\");\n    else\n        json->ptr = endptr;     /* 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->ptr pointer at the error.\n */\nstatic void json_next_token(json_parse_t *json, json_token_t *token)\n{\n    const json_token_type_t *ch2token = json->cfg->ch2token;\n    int ch;\n\n    /* Eat whitespace. */\n    while (1) {\n        ch = (unsigned char)*(json->ptr);\n        token->type = ch2token[ch];\n        if (token->type != T_WHITESPACE)\n            break;\n        json->ptr++;\n    }\n\n    /* Store location of new token. Required when throwing errors\n     * for unexpected tokens (syntax errors). */\n    token->index = json->ptr - json->data;\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->ptr++;\n        return;\n    }\n\n    /* Process characters which triggered T_UNKNOWN\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_invalid_numbers && 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->ptr, \"true\", 4)) {\n        token->type = T_BOOLEAN;\n        token->value.boolean = 1;\n        json->ptr += 4;\n        return;\n    } else if (!strncmp(json->ptr, \"false\", 5)) {\n        token->type = T_BOOLEAN;\n        token->value.boolean = 0;\n        json->ptr += 5;\n        return;\n    } else if (!strncmp(json->ptr, \"null\", 4)) {\n        token->type = T_NULL;\n        json->ptr += 4;\n        return;\n    } else if (json->cfg->decode_invalid_numbers &&\n               json_is_invalid_number(json)) {\n        /* When decode_invalid_numbers is enabled, 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 inline void json_decode_ascend(json_parse_t *json)\n{\n    json->current_depth--;\n}\n\nstatic void json_decode_descend(lua_State *l, json_parse_t *json, int slots)\n{\n    json->current_depth++;\n\n    if (json->current_depth <= json->cfg->decode_max_depth &&\n        lua_checkstack(l, slots)) {\n        return;\n    }\n\n    strbuf_free(json->tmp);\n    luaL_error(l, \"Found too many nested data structures (%d) at character %d\",\n        json->current_depth, json->ptr - json->data);\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_descend(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        json_decode_ascend(json);\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            json_decode_ascend(json);\n            return;\n        }\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_descend(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        json_decode_ascend(json);\n        return;\n    }\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            json_decode_ascend(json);\n            return;\n        }\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\nstatic int json_decode(lua_State *l)\n{\n    json_parse_t json;\n    json_token_t token;\n    size_t json_len;\n\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    json.cfg = json_fetch_config(l);\n    json.data = luaL_checklstring(l, 1, &json_len);\n    json.current_depth = 0;\n    json.ptr = json.data;\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 (json_len >= 2 && (!json.data[0] || !json.data[1]))\n        luaL_error(l, \"JSON parser does not support UTF-16 or UTF-32\");\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    return 1;\n}\n\n/* ===== INITIALISATION ===== */\n\n#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502\n/* Compatibility for Lua 5.1.\n *\n * luaL_setfuncs() is used to create a module table where the functions have\n * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */\nstatic void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup)\n{\n    int i;\n\n    luaL_checkstack(l, nup, \"too many upvalues\");\n    for (; reg->name != NULL; reg++) {  /* fill the table with given functions */\n        for (i = 0; i < nup; i++)  /* copy upvalues to the top */\n            lua_pushvalue(l, -nup);\n        lua_pushcclosure(l, reg->func, nup);  /* closure with those upvalues */\n        lua_setfield(l, -(nup + 2), reg->name);\n    }\n    lua_pop(l, nup);  /* remove upvalues */\n}\n#endif\n\n/* Call target function in protected mode with all supplied args.\n * Assumes target function only returns a single non-nil value.\n * Convert and return thrown errors as: nil, \"error message\" */\nstatic int json_protect_conversion(lua_State *l)\n{\n    int err;\n\n    /* Deliberately throw an error for invalid arguments */\n    luaL_argcheck(l, lua_gettop(l) == 1, 1, \"expected 1 argument\");\n\n    /* pcall() the function stored as upvalue(1) */\n    lua_pushvalue(l, lua_upvalueindex(1));\n    lua_insert(l, 1);\n    err = lua_pcall(l, 1, 1, 0);\n    if (!err)\n        return 1;\n\n    if (err == LUA_ERRRUN) {\n        lua_pushnil(l);\n        lua_insert(l, -2);\n        return 2;\n    }\n\n    /* Since we are not using a custom error handler, the only remaining\n     * errors are memory related */\n    return luaL_error(l, \"Memory allocation error in CJSON protected call\");\n}\n\n/* Return cjson module table */\nstatic int lua_cjson_new(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        { \"decode_max_depth\", json_cfg_decode_max_depth },\n        { \"encode_number_precision\", json_cfg_encode_number_precision },\n        { \"encode_keep_buffer\", json_cfg_encode_keep_buffer },\n        { \"encode_invalid_numbers\", json_cfg_encode_invalid_numbers },\n        { \"decode_invalid_numbers\", json_cfg_decode_invalid_numbers },\n        { \"new\", lua_cjson_new },\n        { NULL, NULL }\n    };\n\n    /* Initialise number conversions */\n    fpconv_init();\n\n    /* cjson module table */\n    lua_newtable(l);\n\n    /* Register functions with config data as upvalue */\n    json_create_config(l);\n    luaL_setfuncs(l, reg, 1);\n\n    /* Set cjson.null */\n    lua_pushlightuserdata(l, NULL);\n    lua_setfield(l, -2, \"null\");\n\n    /* Set module name / version fields */\n    lua_pushliteral(l, CJSON_MODNAME);\n    lua_setfield(l, -2, \"_NAME\");\n    lua_pushliteral(l, CJSON_VERSION);\n    lua_setfield(l, -2, \"_VERSION\");\n\n    return 1;\n}\n\n/* Return cjson.safe module table */\nstatic int lua_cjson_safe_new(lua_State *l)\n{\n    const char *func[] = { \"decode\", \"encode\", NULL };\n    int i;\n\n    lua_cjson_new(l);\n\n    /* Fix new() method */\n    lua_pushcfunction(l, lua_cjson_safe_new);\n    lua_setfield(l, -2, \"new\");\n\n    for (i = 0; func[i]; i++) {\n        lua_getfield(l, -1, func[i]);\n        lua_pushcclosure(l, json_protect_conversion, 1);\n        lua_setfield(l, -2, func[i]);\n    }\n\n    return 1;\n}\n\nint luaopen_cjson(lua_State *l)\n{\n    lua_cjson_new(l);\n\n#ifdef ENABLE_CJSON_GLOBAL\n    /* Register a global \"cjson\" table. */\n    lua_pushvalue(l, -1);\n    lua_setglobal(l, CJSON_MODNAME);\n#endif\n\n    /* Return cjson table */\n    return 1;\n}\n\nint luaopen_cjson_safe(lua_State *l)\n{\n    lua_cjson_safe_new(l);\n\n    /* Return cjson.safe table */\n    return 1;\n}\n\n/* vi:ai et sw=4 ts=4:\n */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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_NAME        \"cmsgpack\"\n#define LUACMSGPACK_SAFE_NAME   \"cmsgpack_safe\"\n#define LUACMSGPACK_VERSION     \"lua-cmsgpack 0.4.0\"\n#define LUACMSGPACK_COPYRIGHT   \"Copyright (C) 2012, Salvatore Sanfilippo\"\n#define LUACMSGPACK_DESCRIPTION \"MessagePack C implementation for Lua\"\n\n/* Allows a preprocessor directive to override MAX_NESTING */\n#ifndef LUACMSGPACK_MAX_NESTING\n    #define LUACMSGPACK_MAX_NESTING  16 /* Max tables nesting. */\n#endif\n\n/* Check if float or double can be an integer without loss of precision */\n#define IS_INT_TYPE_EQUIVALENT(x, T) (!isinf(x) && (T)(x) == (x))\n\n#define IS_INT64_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int64_t)\n#define IS_INT_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int)\n\n/* If size of pointer is equal to a 4 byte integer, we're on 32 bits. */\n#if UINTPTR_MAX == UINT_MAX\n    #define BITS_32 1\n#else\n    #define BITS_32 0\n#endif\n\n#if BITS_32\n    #define lua_pushunsigned(L, n) lua_pushnumber(L, n)\n#else\n    #define lua_pushunsigned(L, n) lua_pushinteger(L, n)\n#endif\n\n/* =============================================================================\n * MessagePack implementation and bindings for Lua 5.1/5.2.\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 * 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix.\n * 07-Apr-2014 (ver 0.4.0): Multiple pack/unpack, lua allocator, efficiency.\n * ========================================================================== */\n\n/* -------------------------- Endian conversion --------------------------------\n * We use it only for floats and doubles, all the other conversions performed\n * in an endian independent fashion. So the only thing we need is a function\n * that swaps a binary string if 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 check for endianess at runtime.\n * The performance difference should be acceptable. */\nvoid memrevifle(void *ptr, size_t len) {\n    unsigned char   *p = (unsigned char *)ptr,\n                    *e = (unsigned char *)p+len-1,\n                    aux;\n    int test = 1;\n    unsigned char *testp = (unsigned char*) &test;\n\n    if (testp[0] == 0) return; /* Big endian, nothing 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 operation\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\nvoid *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {\n    void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL;\n    void *ud;\n\n    local_realloc = lua_getallocf(L, &ud);\n\n    return local_realloc(ud, target, osize, nsize);\n}\n\nmp_buf *mp_buf_new(lua_State *L) {\n    mp_buf *buf = NULL;\n\n    /* Old size = 0; new size = sizeof(*buf) */\n    buf = (mp_buf*)mp_realloc(L, NULL, 0, sizeof(*buf));\n\n    buf->b = NULL;\n    buf->len = buf->free = 0;\n    return buf;\n}\n\nvoid mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {\n    if (buf->free < len) {\n        size_t newsize = (buf->len+len)*2;\n\n        buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);\n        buf->free = newsize - buf->len;\n    }\n    memcpy(buf->b+buf->len,s,len);\n    buf->len += len;\n    buf->free -= len;\n}\n\nvoid mp_buf_free(lua_State *L, mp_buf *buf) {\n    mp_realloc(L, buf->b, buf->len + buf->free, 0); /* realloc to 0 = free */\n    mp_realloc(L, buf, sizeof(*buf), 0);\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 operation. */\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\nvoid mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {\n    cursor->p = s;\n    cursor->left = len;\n    cursor->err = MP_CUR_ERROR_NONE;\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\nvoid mp_encode_bytes(lua_State *L, 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 <= 0xff) {\n        hdr[0] = 0xd9;\n        hdr[1] = len;\n        hdrlen = 2;\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(L,buf,hdr,hdrlen);\n    mp_buf_append(L,buf,s,len);\n}\n\n/* we assume IEEE 754 internal format for single and double precision floats. */\nvoid mp_encode_double(lua_State *L, 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(L,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(L,buf,b,9);\n    }\n}\n\nvoid mp_encode_int(lua_State *L, 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] = ((signed 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(L,buf,b,enclen);\n}\n\nvoid mp_encode_array(lua_State *L, 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(L,buf,b,enclen);\n}\n\nvoid mp_encode_map(lua_State *L, 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(L,buf,b,enclen);\n}\n\n/* --------------------------- Lua types encoding --------------------------- */\n\nvoid 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(L,buf,(const unsigned char*)s,len);\n}\n\nvoid mp_encode_lua_bool(lua_State *L, mp_buf *buf) {\n    unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;\n    mp_buf_append(L,buf,&b,1);\n}\n\n/* Lua 5.3 has a built in 64-bit integer type */\nvoid mp_encode_lua_integer(lua_State *L, mp_buf *buf) {\n#if (LUA_VERSION_NUM < 503) && BITS_32\n    lua_Number i = lua_tonumber(L,-1);\n#else\n    lua_Integer i = lua_tointeger(L,-1);\n#endif\n    mp_encode_int(L, buf, (int64_t)i);\n}\n\n/* Lua 5.2 and lower only has 64-bit doubles, so we need to\n * detect if the double may be representable as an int\n * for Lua < 5.3 */\nvoid mp_encode_lua_number(lua_State *L, mp_buf *buf) {\n    lua_Number n = lua_tonumber(L,-1);\n\n    if (IS_INT64_EQUIVALENT(n)) {\n        mp_encode_lua_integer(L, buf);\n    } else {\n        mp_encode_double(L,buf,(double)n);\n    }\n}\n\nvoid mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);\n\n/* Convert a lua table into a message pack list. */\nvoid mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {\n#if LUA_VERSION_NUM < 502\n    size_t len = lua_objlen(L,-1), j;\n#else\n    size_t len = lua_rawlen(L,-1), j;\n#endif\n\n    mp_encode_array(L,buf,len);\n    luaL_checkstack(L, 1, \"in function mp_encode_lua_table_as_array\");\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. */\nvoid 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 hackish for this lib. */\n    luaL_checkstack(L, 3, \"in function mp_encode_lua_table_as_map\");\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(L,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. */\nint table_is_an_array(lua_State *L) {\n    int count = 0, max = 0;\n#if LUA_VERSION_NUM < 503\n    lua_Number n;\n#else\n    lua_Integer n;\n#endif\n\n    /* Stack top on function entry */\n    int stacktop;\n\n    stacktop = lua_gettop(L);\n\n    lua_pushnil(L);\n    while(lua_next(L,-2)) {\n        /* Stack: ... key value */\n        lua_pop(L,1); /* Stack: ... key */\n        /* The <= 0 check is valid here because we're comparing indexes. */\n#if LUA_VERSION_NUM < 503\n        if ((LUA_TNUMBER != lua_type(L,-1)) || (n = lua_tonumber(L, -1)) <= 0 ||\n            !IS_INT_EQUIVALENT(n))\n#else\n        if (!lua_isinteger(L,-1) || (n = lua_tointeger(L, -1)) <= 0)\n#endif\n        {\n            lua_settop(L, stacktop);\n            return 0;\n        }\n        max = (n > max ? n : max);\n        count++;\n    }\n    /* We have the total number of elements in \"count\". Also we have\n     * the max index encountered in \"max\". 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 max==count you are sure\n     * that there are all the keys form 1 to count (both included). */\n    lua_settop(L, stacktop);\n    return max == count;\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. */\nvoid 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\nvoid mp_encode_lua_null(lua_State *L, mp_buf *buf) {\n    unsigned char b[1];\n\n    b[0] = 0xc0;\n    mp_buf_append(L,buf,b,1);\n}\n\nvoid 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 specified 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:\n    #if LUA_VERSION_NUM < 503\n        mp_encode_lua_number(L,buf); break;\n    #else\n        if (lua_isinteger(L, -1)) {\n            mp_encode_lua_integer(L, buf);\n        } else {\n            mp_encode_lua_number(L, buf);\n        }\n        break;\n    #endif\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\n/*\n * Packs all arguments as a stream for multiple upacking later.\n * Returns error if no arguments provided.\n */\nint mp_pack(lua_State *L) {\n    int nargs = lua_gettop(L);\n    int i;\n    mp_buf *buf;\n\n    if (nargs == 0)\n        return luaL_argerror(L, 0, \"MessagePack pack needs input.\");\n\n    if (!lua_checkstack(L, nargs))\n        return luaL_argerror(L, 0, \"Too many arguments for MessagePack pack.\");\n\n    buf = mp_buf_new(L);\n    for(i = 1; i <= nargs; i++) {\n        /* Copy argument i to top of stack for _encode processing;\n         * the encode function pops it from the stack when complete. */\n        luaL_checkstack(L, 1, \"in function mp_check\");\n        lua_pushvalue(L, i);\n\n        mp_encode_lua_type(L,buf,0);\n\n        lua_pushlstring(L,(char*)buf->b,buf->len);\n\n        /* Reuse the buffer for the next operation by\n         * setting its free count to the total buffer size\n         * and the current position to zero. */\n        buf->free += buf->len;\n        buf->len = 0;\n    }\n    mp_buf_free(L, buf);\n\n    /* Concatenate all nargs buffers together */\n    lua_concat(L, nargs);\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    assert(len <= UINT_MAX);\n    int index = 1;\n\n    lua_newtable(L);\n    luaL_checkstack(L, 1, \"in function mp_decode_to_lua_array\");\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    assert(len <= UINT_MAX);\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\n    /* If we return more than 18 elements, we must resize the stack to\n     * fit all our return values.  But, there is no way to\n     * determine how many objects a msgpack will unpack to up front, so\n     * we request a +1 larger stack on each iteration (noop if stack is\n     * big enough, and when stack does require resize it doubles in size) */\n    luaL_checkstack(L, 1,\n        \"too many return values at once; \"\n        \"use unpack_one or unpack_limit instead.\");\n\n    switch(c->p[0]) {\n    case 0xcc:  /* uint 8 */\n        mp_cur_need(c,2);\n        lua_pushunsigned(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_pushinteger(L,(signed 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_pushunsigned(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_pushinteger(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_pushunsigned(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_pushinteger(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_pushunsigned(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#if LUA_VERSION_NUM < 503\n        lua_pushnumber(L,\n#else\n        lua_pushinteger(L,\n#endif\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 0xd9:  /* raw 8 */\n        mp_cur_need(c,2);\n        {\n            size_t l = c->p[1];\n            mp_cur_need(c,2+l);\n            lua_pushlstring(L,(char*)c->p+2,l);\n            mp_cur_consume(c,2+l);\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 = ((size_t)c->p[1] << 24) |\n                       ((size_t)c->p[2] << 16) |\n                       ((size_t)c->p[3] << 8) |\n                       (size_t)c->p[4];\n            mp_cur_consume(c,5);\n            mp_cur_need(c,l);\n            lua_pushlstring(L,(char*)c->p,l);\n            mp_cur_consume(c,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 = ((size_t)c->p[1] << 24) |\n                       ((size_t)c->p[2] << 16) |\n                       ((size_t)c->p[3] << 8) |\n                       (size_t)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 = ((size_t)c->p[1] << 24) |\n                       ((size_t)c->p[2] << 16) |\n                       ((size_t)c->p[3] << 8) |\n                       (size_t)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_pushunsigned(L,c->p[0]);\n            mp_cur_consume(c,1);\n        } else if ((c->p[0] & 0xe0) == 0xe0) {  /* negative fixnum */\n            lua_pushinteger(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\nint mp_unpack_full(lua_State *L, int limit, int offset) {\n    size_t len;\n    const char *s;\n    mp_cur c;\n    int cnt; /* Number of objects unpacked */\n    int decode_all = (!limit && !offset);\n\n    s = luaL_checklstring(L,1,&len); /* if no match, exits */\n\n    if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */\n        return luaL_error(L,\n            \"Invalid request to unpack with offset of %d and limit of %d.\",\n            offset, len);\n    else if (offset > len)\n        return luaL_error(L,\n            \"Start offset %d greater than input length %d.\", offset, len);\n\n    if (decode_all) limit = INT_MAX;\n\n    mp_cur_init(&c,(const unsigned char *)s+offset,len-offset);\n\n    /* We loop over the decode because this could be a stream\n     * of multiple top-level values serialized together */\n    for(cnt = 0; c.left > 0 && cnt < limit; cnt++) {\n        mp_decode_to_lua_type(L,&c);\n\n        if (c.err == MP_CUR_ERROR_EOF) {\n            return luaL_error(L,\"Missing bytes in input.\");\n        } else if (c.err == MP_CUR_ERROR_BADFMT) {\n            return luaL_error(L,\"Bad data format in input.\");\n        }\n    }\n\n    if (!decode_all) {\n        /* c->left is the remaining size of the input buffer.\n         * subtract the entire buffer size from the unprocessed size\n         * to get our next start offset */\n        int offset = len - c.left;\n\n        luaL_checkstack(L, 1, \"in function mp_unpack_full\");\n\n        /* Return offset -1 when we have have processed the entire buffer. */\n        lua_pushinteger(L, c.left == 0 ? -1 : offset);\n        /* Results are returned with the arg elements still\n         * in place. Lua takes care of only returning\n         * elements above the args for us.\n         * In this case, we have one arg on the stack\n         * for this function, so we insert our first return\n         * value at position 2. */\n        lua_insert(L, 2);\n        cnt += 1; /* increase return count by one to make room for offset */\n    }\n\n    return cnt;\n}\n\nint mp_unpack(lua_State *L) {\n    return mp_unpack_full(L, 0, 0);\n}\n\nint mp_unpack_one(lua_State *L) {\n    int offset = luaL_optinteger(L, 2, 0);\n    /* Variable pop because offset may not exist */\n    lua_pop(L, lua_gettop(L)-1);\n    return mp_unpack_full(L, 1, offset);\n}\n\nint mp_unpack_limit(lua_State *L) {\n    int limit = luaL_checkinteger(L, 2);\n    int offset = luaL_optinteger(L, 3, 0);\n    /* Variable pop because offset may not exist */\n    lua_pop(L, lua_gettop(L)-1);\n\n    return mp_unpack_full(L, limit, offset);\n}\n\nint mp_safe(lua_State *L) {\n    int argc, err, total_results;\n\n    argc = lua_gettop(L);\n\n    /* This adds our function to the bottom of the stack\n     * (the \"call this function\" position) */\n    lua_pushvalue(L, lua_upvalueindex(1));\n    lua_insert(L, 1);\n\n    err = lua_pcall(L, argc, LUA_MULTRET, 0);\n    total_results = lua_gettop(L);\n\n    if (!err) {\n        return total_results;\n    } else {\n        lua_pushnil(L);\n        lua_insert(L,-2);\n        return 2;\n    }\n}\n\n/* -------------------------------------------------------------------------- */\nconst struct luaL_Reg cmds[] = {\n    {\"pack\", mp_pack},\n    {\"unpack\", mp_unpack},\n    {\"unpack_one\", mp_unpack_one},\n    {\"unpack_limit\", mp_unpack_limit},\n    {0}\n};\n\nint luaopen_create(lua_State *L) {\n    int i;\n    /* Manually construct our module table instead of\n     * relying on _register or _newlib */\n    lua_newtable(L);\n\n    for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {\n        lua_pushcfunction(L, cmds[i].func);\n        lua_setfield(L, -2, cmds[i].name);\n    }\n\n    /* Add metadata */\n    lua_pushliteral(L, LUACMSGPACK_NAME);\n    lua_setfield(L, -2, \"_NAME\");\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\nLUALIB_API int luaopen_cmsgpack(lua_State *L) {\n    luaopen_create(L);\n\n#if LUA_VERSION_NUM < 502\n    /* Register name globally for 5.1 */\n    lua_pushvalue(L, -1);\n    lua_setglobal(L, LUACMSGPACK_NAME);\n#endif\n\n    return 1;\n}\n\nLUALIB_API int luaopen_cmsgpack_safe(lua_State *L) {\n    int i;\n\n    luaopen_cmsgpack(L);\n\n    /* Wrap all functions in the safe handler */\n    for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {\n        lua_getfield(L, -1, cmds[i].name);\n        lua_pushcclosure(L, mp_safe, 1);\n        lua_setfield(L, -2, cmds[i].name);\n    }\n\n#if LUA_VERSION_NUM < 502\n    /* Register name globally for 5.1 */\n    lua_pushvalue(L, -1);\n    lua_setglobal(L, LUACMSGPACK_SAFE_NAME);\n#endif\n\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": "Chapter07/redis-6.0.3/deps/lua/src/lua_struct.c",
    "content": "/*\n** {======================================================\n** Library for packing/unpacking structures.\n** $Id: struct.c,v 1.7 2018/05/11 22:04:31 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 (lua_State *L, 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      if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))\n        luaL_error(L, \"integral size overflow\");\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(L, fmt, 1);\n    case 'i': case 'I': {\n      int sz = getnum(L, 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(L, 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);\n  luaL_argcheck(L, pos > 0, 3, \"offset must be 1 or greater\");\n  pos--; /* Lua indexes are 1-based, but here we want 0-based for C\n          * pointer math. */\n  int n = 0;  /* number of results */\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    luaL_argcheck(L, size <= ld && pos <= ld - size,\n                   2, \"data string too short\");\n    /* stack space for item + next position */\n    luaL_checkstack(L, 2, \"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++;\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++;\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++;\n        break;\n      }\n      case 'c': {\n        if (size == 0) {\n          if (n == 0 || !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--;\n          luaL_argcheck(L, size <= ld && pos <= ld - size,\n                           2, \"data string too short\");\n        }\n        lua_pushlstring(L, data+pos, size); n++;\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++;\n        break;\n      }\n      default: controloptions(L, opt, &fmt, &h);\n    }\n    pos += size;\n  }\n  lua_pushinteger(L, pos + 1);  /* next position */\n  return n + 1;\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-2018 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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/lua/src/strbuf.c",
    "content": "/* strbuf - String buffer routines\n *\n * Copyright (c) 2010-2012  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\nstatic void 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": "Chapter07/redis-6.0.3/deps/lua/src/strbuf.h",
    "content": "/* strbuf - String buffer routines\n *\n * Copyright (c) 2010-2012  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);\nstatic char *strbuf_empty_ptr(strbuf_t *s);\nstatic void strbuf_extend_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 char *strbuf_empty_ptr(strbuf_t *s)\n{\n    return s->buf + s->length;\n}\n\nstatic inline void strbuf_extend_length(strbuf_t *s, int len)\n{\n    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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/lua/test/hello.lua",
    "content": "-- the first program in every language\n\nio.write(\"Hello world, from \",_VERSION,\"!\\n\")\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/deps/update-jemalloc.sh",
    "content": "#!/bin/bash\nVER=$1\nURL=\"http://www.canonware.com/download/jemalloc/jemalloc-${VER}.tar.bz2\"\necho \"Downloading $URL\"\ncurl $URL > /tmp/jemalloc.tar.bz2\ntar xvjf /tmp/jemalloc.tar.bz2\nrm -rf jemalloc\nmv jemalloc-${VER} jemalloc\necho \"Use git status, add all files and commit changes.\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/redis.conf",
    "content": "# Redis configuration file example.\n#\n# Note that in order to read the configuration file, Redis must be\n# started with the file path as first argument:\n#\n# ./redis-server /path/to/redis.conf\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 servers 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################################## MODULES #####################################\n\n# Load modules at startup. If the server is not able to load modules\n# it will abort. It is possible to use multiple loadmodule directives.\n#\n# loadmodule /path/to/my_module.so\n# loadmodule /path/to/other_module.so\n\n################################## NETWORK #####################################\n\n# By default, if no \"bind\" configuration directive is specified, Redis listens\n# for connections from all the network interfaces available on the server.\n# It is possible to listen to just one or multiple selected interfaces using\n# the \"bind\" configuration directive, followed by one or more IP addresses.\n#\n# Examples:\n#\n# bind 192.168.1.100 10.0.0.1\n# bind 127.0.0.1 ::1\n#\n# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the\n# internet, binding to all the interfaces is dangerous and will expose the\n# instance to everybody on the internet. So by default we uncomment the\n# following bind directive, that will force Redis to listen only into\n# the IPv4 loopback interface address (this means Redis will be able to\n# accept connections only from clients running into the same computer it\n# is running).\n#\n# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES\n# JUST COMMENT THE FOLLOWING LINE.\n# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nbind 127.0.0.1\n\n# Protected mode is a layer of security protection, in order to avoid that\n# Redis instances left open on the internet are accessed and exploited.\n#\n# When protected mode is on and if:\n#\n# 1) The server is not binding explicitly to a set of addresses using the\n#    \"bind\" directive.\n# 2) No password is configured.\n#\n# The server only accepts connections from clients connecting from the\n# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain\n# sockets.\n#\n# By default protected mode is enabled. You should disable it only if\n# you are sure you want clients from other hosts to connect to Redis\n# even if no authentication is configured, nor a specific set of interfaces\n# are explicitly listed using the \"bind\" directive.\nprotected-mode yes\n\n# Accept connections on the specified port, default is 6379 (IANA #815344).\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# Unix socket.\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 700\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 300 seconds, which is the new\n# Redis default starting with Redis 3.2.1.\ntcp-keepalive 300\n\n################################# TLS/SSL #####################################\n\n# By default, TLS/SSL is disabled. To enable it, the \"tls-port\" configuration\n# directive can be used to define TLS-listening ports. To enable TLS on the\n# default port, use:\n#\n# port 0\n# tls-port 6379\n\n# Configure a X.509 certificate and private key to use for authenticating the\n# server to connected clients, masters or cluster peers.  These files should be\n# PEM formatted.\n#\n# tls-cert-file redis.crt \n# tls-key-file redis.key\n\n# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:\n#\n# tls-dh-params-file redis.dh\n\n# Configure a CA certificate(s) bundle or directory to authenticate TLS/SSL\n# clients and peers.  Redis requires an explicit configuration of at least one\n# of these, and will not implicitly use the system wide configuration.\n#\n# tls-ca-cert-file ca.crt\n# tls-ca-cert-dir /etc/ssl/certs\n\n# By default, clients (including replica servers) on a TLS port are required\n# to authenticate using valid client side certificates.\n#\n# It is possible to disable authentication using this directive.\n#\n# tls-auth-clients no\n\n# By default, a Redis replica does not attempt to establish a TLS connection\n# with its master.\n#\n# Use the following directive to enable TLS on replication links.\n#\n# tls-replication yes\n\n# By default, the Redis Cluster bus uses a plain TCP connection. To enable\n# TLS for the bus protocol, use the following directive:\n#\n# tls-cluster yes\n\n# Explicitly specify TLS versions to support. Allowed values are case insensitive\n# and include \"TLSv1\", \"TLSv1.1\", \"TLSv1.2\", \"TLSv1.3\" (OpenSSL >= 1.1.1) \n#\n# tls-protocols TLSv1.2\n\n# Configure allowed ciphers.  See the ciphers(1ssl) manpage for more information\n# about the syntax of this string.\n#\n# Note: this configuration applies only to <= TLSv1.2.\n#\n# tls-ciphers DEFAULT:!MEDIUM\n\n# Configure allowed TLSv1.3 ciphersuites.  See the ciphers(1ssl) manpage for more\n# information about the syntax of this string, and specifically for TLSv1.3\n# ciphersuites.\n#\n# tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256\n\n# When choosing a cipher, use the server's preference instead of the client\n# preference. By default, the server follows the client's preference.\n#\n# tls-prefer-server-ciphers yes\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# If you run Redis from upstart or systemd, Redis can interact with your\n# supervision tree. Options:\n#   supervised no      - no supervision interaction\n#   supervised upstart - signal upstart by putting Redis into SIGSTOP mode\n#   supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET\n#   supervised auto    - detect upstart or systemd method based on\n#                        UPSTART_JOB or NOTIFY_SOCKET environment variables\n# Note: these supervision methods only signal \"process is ready.\"\n#       They do not enable continuous liveness pings back to your supervisor.\nsupervised no\n\n# If a pid file is specified, Redis writes it where specified at startup\n# and removes it at exit.\n#\n# When the server runs non daemonized, no pid file is created if none is\n# specified in the configuration. When the server is daemonized, the pid file\n# is used even if not specified, defaulting to \"/var/run/redis.pid\".\n#\n# Creating a pid file is best effort: if Redis is not able to create it\n# nothing bad happens, the server will start and run normally.\npidfile /var/run/redis_6379.pid\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# By default Redis shows an ASCII art logo only when started to log to the\n# standard output and if the standard output is a TTY. Basically this means\n# that normally a logo is displayed only in interactive sessions.\n#\n# However it is possible to force the pre-4.0 behavior and always show a\n# ASCII art logo in startup logs by setting the following option to yes.\nalways-show-logo yes\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 completely by commenting out all \"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# Remove RDB files used by replication in instances without persistence\n# enabled. By default this option is disabled, however there are environments\n# where for regulations or other security concerns, RDB files persisted on\n# disk by masters in order to feed replicas, or stored on disk by replicas\n# in order to load them for the initial synchronization, should be deleted\n# ASAP. Note that this option ONLY WORKS in instances that have both AOF\n# and RDB persistence disabled, otherwise is completely ignored.\n#\n# An alternative (and sometimes better) way to obtain the same effect is\n# to use diskless replication on both master and replicas instances. However\n# in the case of replicas, diskless is not always an option.\nrdb-del-sync-files no\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-Replica replication. Use replicaof to make a Redis instance a copy of\n# another Redis server. A few things to understand ASAP about Redis replication.\n#\n#   +------------------+      +---------------+\n#   |      Master      | ---> |    Replica    |\n#   | (receive writes) |      |  (exact copy) |\n#   +------------------+      +---------------+\n#\n# 1) Redis replication is asynchronous, but you can configure a master to\n#    stop accepting writes if it appears to be not connected with at least\n#    a given number of replicas.\n# 2) Redis replicas are able to perform a partial resynchronization with the\n#    master if the replication link is lost for a relatively small amount of\n#    time. You may want to configure the replication backlog size (see the next\n#    sections of this file) with a sensible value depending on your needs.\n# 3) Replication is automatic and does not need user intervention. After a\n#    network partition replicas automatically try to reconnect to masters\n#    and resynchronize with them.\n#\n# replicaof <masterip> <masterport>\n\n# If the master is password protected (using the \"requirepass\" configuration\n# directive below) it is possible to tell the replica to authenticate before\n# starting the replication synchronization process, otherwise the master will\n# refuse the replica request.\n#\n# masterauth <master-password>\n#\n# However this is not enough if you are using Redis ACLs (for Redis version\n# 6 or greater), and the default user is not capable of running the PSYNC\n# command and/or other commands needed for replication. In this case it's\n# better to configure a special user to use with replication, and specify the\n# masteruser configuration as such:\n#\n# masteruser <username>\n#\n# When masteruser is specified, the replica will authenticate against its\n# master using the new AUTH form: AUTH <username> <password>.\n\n# When a replica loses its connection with the master, or when the replication\n# is still in progress, the replica can act in two different ways:\n#\n# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica 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 replica-serve-stale-data is set to 'no' the replica will reply with\n#    an error \"SYNC with master in progress\" to all the kind of commands\n#    but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,\n#    SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB,\n#    COMMAND, POST, HOST: and LATENCY.\n#\nreplica-serve-stale-data yes\n\n# You can configure a replica instance to accept writes or not. Writing against\n# a replica instance may be useful to store some ephemeral data (because data\n# written on a replica 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 replicas are read-only.\n#\n# Note: read only replicas 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 replica 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 replicas using 'rename-command' to shadow all the\n# administrative / dangerous commands.\nreplica-read-only yes\n\n# Replication SYNC strategy: disk or socket.\n#\n# New replicas and reconnecting replicas that are not able to continue the\n# replication process just receiving differences, need to do what is called a\n# \"full synchronization\". An RDB file is transmitted from the master to the\n# replicas.\n#\n# The transmission can happen in two different ways:\n#\n# 1) Disk-backed: The Redis master creates a new process that writes the RDB\n#                 file on disk. Later the file is transferred by the parent\n#                 process to the replicas incrementally.\n# 2) Diskless: The Redis master creates a new process that directly writes the\n#              RDB file to replica sockets, without touching the disk at all.\n#\n# With disk-backed replication, while the RDB file is generated, more replicas\n# can be queued and served with the RDB file as soon as the current child\n# producing the RDB file finishes its work. With diskless replication instead\n# once the transfer starts, new replicas arriving will be queued and a new\n# transfer will start when the current one terminates.\n#\n# When diskless replication is used, the master waits a configurable amount of\n# time (in seconds) before starting the transfer in the hope that multiple\n# replicas will arrive and the transfer can be parallelized.\n#\n# With slow disks and fast (large bandwidth) networks, diskless replication\n# works better.\nrepl-diskless-sync no\n\n# When diskless replication is enabled, it is possible to configure the delay\n# the server waits in order to spawn the child that transfers the RDB via socket\n# to the replicas.\n#\n# This is important since once the transfer starts, it is not possible to serve\n# new replicas arriving, that will be queued for the next RDB transfer, so the\n# server waits a delay in order to let more replicas arrive.\n#\n# The delay is specified in seconds, and by default is 5 seconds. To disable\n# it entirely just set it to 0 seconds and the transfer will start ASAP.\nrepl-diskless-sync-delay 5\n\n# -----------------------------------------------------------------------------\n# WARNING: RDB diskless load is experimental. Since in this setup the replica\n# does not immediately store an RDB on disk, it may cause data loss during\n# failovers. RDB diskless load + Redis modules not handling I/O reads may also\n# cause Redis to abort in case of I/O errors during the initial synchronization\n# stage with the master. Use only if your do what you are doing.\n# -----------------------------------------------------------------------------\n#\n# Replica can load the RDB it reads from the replication link directly from the\n# socket, or store the RDB to a file and read that file after it was completely\n# recived from the master.\n#\n# In many cases the disk is slower than the network, and storing and loading\n# the RDB file may increase replication time (and even increase the master's\n# Copy on Write memory and salve buffers).\n# However, parsing the RDB file directly from the socket may mean that we have\n# to flush the contents of the current database before the full rdb was\n# received. For this reason we have the following options:\n#\n# \"disabled\"    - Don't use diskless load (store the rdb file to the disk first)\n# \"on-empty-db\" - Use diskless load only when it is completely safe.\n# \"swapdb\"      - Keep a copy of the current db contents in RAM while parsing\n#                 the data directly from the socket. note that this requires\n#                 sufficient memory, if you don't have it, you risk an OOM kill.\nrepl-diskless-load disabled\n\n# Replicas send PINGs to server in a predefined interval. It's possible to\n# change this interval with the repl_ping_replica_period option. The default\n# value is 10 seconds.\n#\n# repl-ping-replica-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 replica.\n# 2) Master timeout from the point of view of replicas (data, pings).\n# 3) Replica 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-replica-period otherwise a timeout will be detected\n# every time there is low traffic between the master and the replica.\n#\n# repl-timeout 60\n\n# Disable TCP_NODELAY on the replica 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 replicas. But this can add a delay for\n# the data to appear on the replica 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 replica 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 replicas 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# replica data when replicas are disconnected for some time, so that when a\n# replica wants to reconnect again, often a full resync is not needed, but a\n# partial resync is enough, just passing the portion of data the replica\n# missed while disconnected.\n#\n# The bigger the replication backlog, the longer the time the replica 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 replica connected.\n#\n# repl-backlog-size 1mb\n\n# After a master has no longer connected replicas 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 replica disconnected, for\n# the backlog buffer to be freed.\n#\n# Note that replicas never free the backlog for timeout, since they may be\n# promoted to masters later, and should be able to correctly \"partially\n# resynchronize\" with the replicas: hence they should always accumulate backlog.\n#\n# A value of 0 means to never release the backlog.\n#\n# repl-backlog-ttl 3600\n\n# The replica priority is an integer number published by Redis in the INFO\n# output. It is used by Redis Sentinel in order to select a replica to promote\n# into a master if the master is no longer working correctly.\n#\n# A replica with a low priority number is considered better for promotion, so\n# for instance if there are three replicas with priority 10, 100, 25 Sentinel\n# will pick the one with priority 10, that is the lowest.\n#\n# However a special priority of 0 marks the replica as not able to perform the\n# role of master, so a replica with priority of 0 will never be selected by\n# Redis Sentinel for promotion.\n#\n# By default the priority is 100.\nreplica-priority 100\n\n# It is possible for a master to stop accepting writes if there are less than\n# N replicas connected, having a lag less or equal than M seconds.\n#\n# The N replicas 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 replica, that is usually sent every second.\n#\n# This option does not GUARANTEE that N replicas will accept the write, but\n# will limit the window of exposure for lost writes in case not enough replicas\n# are available, to the specified number of seconds.\n#\n# For example to require at least 3 replicas with a lag <= 10 seconds use:\n#\n# min-replicas-to-write 3\n# min-replicas-max-lag 10\n#\n# Setting one or the other to 0 disables the feature.\n#\n# By default min-replicas-to-write is set to 0 (feature disabled) and\n# min-replicas-max-lag is set to 10.\n\n# A Redis master is able to list the address and port of the attached\n# replicas in different ways. For example the \"INFO replication\" section\n# offers this information, which is used, among other tools, by\n# Redis Sentinel in order to discover replica instances.\n# Another place where this info is available is in the output of the\n# \"ROLE\" command of a master.\n#\n# The listed IP and address normally reported by a replica is obtained\n# in the following way:\n#\n#   IP: The address is auto detected by checking the peer address\n#   of the socket used by the replica to connect with the master.\n#\n#   Port: The port is communicated by the replica during the replication\n#   handshake, and is normally the port that the replica is using to\n#   listen for connections.\n#\n# However when port forwarding or Network Address Translation (NAT) is\n# used, the replica may be actually reachable via different IP and port\n# pairs. The following two options can be used by a replica in order to\n# report to its master a specific set of IP and port, so that both INFO\n# and ROLE will report those values.\n#\n# There is no need to use both the options if you need to override just\n# the port or the IP address.\n#\n# replica-announce-ip 5.5.5.5\n# replica-announce-port 1234\n\n############################### KEYS TRACKING #################################\n\n# Redis implements server assisted support for client side caching of values.\n# This is implemented using an invalidation table that remembers, using\n# 16 millions of slots, what clients may have certain subsets of keys. In turn\n# this is used in order to send invalidation messages to clients. Please\n# to understand more about the feature check this page:\n#\n#   https://redis.io/topics/client-side-caching\n#\n# When tracking is enabled for a client, all the read only queries are assumed\n# to be cached: this will force Redis to store information in the invalidation\n# table. When keys are modified, such information is flushed away, and\n# invalidation messages are sent to the clients. However if the workload is\n# heavily dominated by reads, Redis could use more and more memory in order\n# to track the keys fetched by many clients.\n#\n# For this reason it is possible to configure a maximum fill value for the\n# invalidation table. By default it is set to 1M of keys, and once this limit\n# is reached, Redis will start to evict keys in the invalidation table\n# even if they were not modified, just to reclaim memory: this will in turn\n# force the clients to invalidate the cached values. Basically the table\n# maximum size is a trade off between the memory you want to spend server\n# side to track information about who cached what, and the ability of clients\n# to retain cached objects in memory.\n#\n# If you set the value to 0, it means there are no limits, and Redis will\n# retain as many keys as needed in the invalidation table.\n# In the \"stats\" INFO section, you can find information about the number of\n# keys in the invalidation table at every given moment.\n#\n# Note: when key tracking is used in broadcasting mode, no memory is used\n# in the server side so this setting is useless.\n#\n# tracking-table-max-keys 1000000\n\n################################## SECURITY ###################################\n\n# Warning: since Redis is pretty fast an outside user can try up to\n# 1 million passwords per second against a modern box. This means that you\n# should use very strong passwords, otherwise they will be very easy to break.\n# Note that because the password is really a shared secret between the client\n# and the server, and should not be memorized by any human, the password\n# can be easily a long string from /dev/urandom or whatever, so by using a\n# long and unguessable password no brute force attack will be possible.\n\n# Redis ACL users are defined in the following format:\n#\n#   user <username> ... acl rules ...\n#\n# For example:\n#\n#   user worker +@list +@connection ~jobs:* on >ffa9203c493aa99\n#\n# The special username \"default\" is used for new connections. If this user\n# has the \"nopass\" rule, then new connections will be immediately authenticated\n# as the \"default\" user without the need of any password provided via the\n# AUTH command. Otherwise if the \"default\" user is not flagged with \"nopass\"\n# the connections will start in not authenticated state, and will require\n# AUTH (or the HELLO command AUTH option) in order to be authenticated and\n# start to work.\n#\n# The ACL rules that describe what an user can do are the following:\n#\n#  on           Enable the user: it is possible to authenticate as this user.\n#  off          Disable the user: it's no longer possible to authenticate\n#               with this user, however the already authenticated connections\n#               will still work.\n#  +<command>   Allow the execution of that command\n#  -<command>   Disallow the execution of that command\n#  +@<category> Allow the execution of all the commands in such category\n#               with valid categories are like @admin, @set, @sortedset, ...\n#               and so forth, see the full list in the server.c file where\n#               the Redis command table is described and defined.\n#               The special category @all means all the commands, but currently\n#               present in the server, and that will be loaded in the future\n#               via modules.\n#  +<command>|subcommand    Allow a specific subcommand of an otherwise\n#                           disabled command. Note that this form is not\n#                           allowed as negative like -DEBUG|SEGFAULT, but\n#                           only additive starting with \"+\".\n#  allcommands  Alias for +@all. Note that it implies the ability to execute\n#               all the future commands loaded via the modules system.\n#  nocommands   Alias for -@all.\n#  ~<pattern>   Add a pattern of keys that can be mentioned as part of\n#               commands. For instance ~* allows all the keys. The pattern\n#               is a glob-style pattern like the one of KEYS.\n#               It is possible to specify multiple patterns.\n#  allkeys      Alias for ~*\n#  resetkeys    Flush the list of allowed keys patterns.\n#  ><password>  Add this passowrd to the list of valid password for the user.\n#               For example >mypass will add \"mypass\" to the list.\n#               This directive clears the \"nopass\" flag (see later).\n#  <<password>  Remove this password from the list of valid passwords.\n#  nopass       All the set passwords of the user are removed, and the user\n#               is flagged as requiring no password: it means that every\n#               password will work against this user. If this directive is\n#               used for the default user, every new connection will be\n#               immediately authenticated with the default user without\n#               any explicit AUTH command required. Note that the \"resetpass\"\n#               directive will clear this condition.\n#  resetpass    Flush the list of allowed passwords. Moreover removes the\n#               \"nopass\" status. After \"resetpass\" the user has no associated\n#               passwords and there is no way to authenticate without adding\n#               some password (or setting it as \"nopass\" later).\n#  reset        Performs the following actions: resetpass, resetkeys, off,\n#               -@all. The user returns to the same state it has immediately\n#               after its creation.\n#\n# ACL rules can be specified in any order: for instance you can start with\n# passwords, then flags, or key patterns. However note that the additive\n# and subtractive rules will CHANGE MEANING depending on the ordering.\n# For instance see the following example:\n#\n#   user alice on +@all -DEBUG ~* >somepassword\n#\n# This will allow \"alice\" to use all the commands with the exception of the\n# DEBUG command, since +@all added all the commands to the set of the commands\n# alice can use, and later DEBUG was removed. However if we invert the order\n# of two ACL rules the result will be different:\n#\n#   user alice on -DEBUG +@all ~* >somepassword\n#\n# Now DEBUG was removed when alice had yet no commands in the set of allowed\n# commands, later all the commands are added, so the user will be able to\n# execute everything.\n#\n# Basically ACL rules are processed left-to-right.\n#\n# For more information about ACL configuration please refer to\n# the Redis web site at https://redis.io/topics/acl\n\n# ACL LOG\n#\n# The ACL Log tracks failed commands and authentication events associated\n# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked \n# by ACLs. The ACL Log is stored in and consumes memory. There is no limit\n# to its length.You can reclaim memory with ACL LOG RESET or set a maximum\n# length below.\nacllog-max-len 128\n\n# Using an external ACL file\n#\n# Instead of configuring users here in this file, it is possible to use\n# a stand-alone file just listing users. The two methods cannot be mixed:\n# if you configure users here and at the same time you activate the exteranl\n# ACL file, the server will refuse to start.\n#\n# The format of the external ACL user file is exactly the same as the\n# format that is used inside redis.conf to describe users.\n#\n# aclfile /etc/redis/users.acl\n\n# IMPORTANT NOTE: starting with Redis 6 \"requirepass\" is just a compatiblity\n# layer on top of the new ACL system. The option effect will be just setting\n# the password for the default user. Clients will still authenticate using\n# AUTH <password> as usually, or more explicitly with AUTH default <password>\n# if they follow the new protocol: both will work.\n#\n# requirepass foobared\n\n# Command renaming (DEPRECATED).\n#\n# ------------------------------------------------------------------------\n# WARNING: avoid using this option if possible. Instead use ACLs to remove\n# commands from the default user, and put them only in some admin user you\n# create for administrative purposes.\n# ------------------------------------------------------------------------\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 replicas may cause problems.\n\n################################### CLIENTS ####################################\n\n# Set the max number of connected clients at the same time. By default\n# this limit is set to 10000 clients, however if the Redis server is not\n# able to configure the process file limit to allow for the specified limit\n# the max number of allowed clients is set to the current file limit\n# minus 32 (as Redis reserves a few file descriptors for internal uses).\n#\n# Once the limit is reached Redis will close all the new connections sending\n# an error 'max number of clients reached'.\n#\n# maxclients 10000\n\n############################## MEMORY MANAGEMENT ################################\n\n# Set a memory usage limit to 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 or LFU cache, or to\n# set a hard memory limit for an instance (using the 'noeviction' policy).\n#\n# WARNING: If you have replicas attached to an instance with maxmemory on,\n# the size of the output buffers needed to feed the replicas 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 replicas 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 replicas attached it is suggested that you set a lower\n# limit for maxmemory so that there is some free RAM on the system for replica\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 one from the following behaviors:\n#\n# volatile-lru -> Evict using approximated LRU, only keys with an expire set.\n# allkeys-lru -> Evict any key using approximated LRU.\n# volatile-lfu -> Evict using approximated LFU, only keys with an expire set.\n# allkeys-lfu -> Evict any key using approximated LFU.\n# volatile-random -> Remove a random key having 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 evict anything, just return an error on write operations.\n#\n# LRU means Least Recently Used\n# LFU means Least Frequently Used\n#\n# Both LRU, LFU and volatile-ttl are implemented using approximated\n# randomized algorithms.\n#\n# Note: with any of the above policies, Redis will return an error on write\n#       operations, when there are no suitable keys for eviction.\n#\n#       At the date of writing these commands are: set setnx setex append\n#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd\n#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby\n#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby\n#       getset mset msetnx exec sort\n#\n# The default is:\n#\n# maxmemory-policy noeviction\n\n# LRU, LFU 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 more CPU. 3 is faster but not very accurate.\n#\n# maxmemory-samples 5\n\n# Starting from Redis 5, by default a replica will ignore its maxmemory setting\n# (unless it is promoted to master after a failover or manually). It means\n# that the eviction of keys will be just handled by the master, sending the\n# DEL commands to the replica as keys evict in the master side.\n#\n# This behavior ensures that masters and replicas stay consistent, and is usually\n# what you want, however if your replica is writable, or you want the replica\n# to have a different memory setting, and you are sure all the writes performed\n# to the replica are idempotent, then you may change this default (but be sure\n# to understand what you are doing).\n#\n# Note that since the replica by default does not evict, it may end using more\n# memory than the one set via maxmemory (there are certain buffers that may\n# be larger on the replica, or data structures may sometimes take more memory\n# and so forth). So make sure you monitor your replicas and make sure they\n# have enough memory to never hit a real out-of-memory condition before the\n# master hits the configured maxmemory setting.\n#\n# replica-ignore-maxmemory yes\n\n# Redis reclaims expired keys in two ways: upon access when those keys are\n# found to be expired, and also in background, in what is called the\n# \"active expire key\". The key space is slowly and interactively scanned\n# looking for expired keys to reclaim, so that it is possible to free memory\n# of keys that are expired and will never be accessed again in a short time.\n#\n# The default effort of the expire cycle will try to avoid having more than\n# ten percent of expired keys still in memory, and will try to avoid consuming\n# more than 25% of total memory and to add latency to the system. However\n# it is possible to increase the expire \"effort\" that is normally set to\n# \"1\", to a greater value, up to the value \"10\". At its maximum value the\n# system will use more CPU, longer cycles (and technically may introduce\n# more latency), and will tollerate less already expired keys still present\n# in the system. It's a tradeoff betweeen memory, CPU and latecy.\n#\n# active-expire-effort 1\n\n############################# LAZY FREEING ####################################\n\n# Redis has two primitives to delete keys. One is called DEL and is a blocking\n# deletion of the object. It means that the server stops processing new commands\n# in order to reclaim all the memory associated with an object in a synchronous\n# way. If the key deleted is associated with a small object, the time needed\n# in order to execute the DEL command is very small and comparable to most other\n# O(1) or O(log_N) commands in Redis. However if the key is associated with an\n# aggregated value containing millions of elements, the server can block for\n# a long time (even seconds) in order to complete the operation.\n#\n# For the above reasons Redis also offers non blocking deletion primitives\n# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and\n# FLUSHDB commands, in order to reclaim memory in background. Those commands\n# are executed in constant time. Another thread will incrementally free the\n# object in the background as fast as possible.\n#\n# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled.\n# It's up to the design of the application to understand when it is a good\n# idea to use one or the other. However the Redis server sometimes has to\n# delete keys or flush the whole database as a side effect of other operations.\n# Specifically Redis deletes objects independently of a user call in the\n# following scenarios:\n#\n# 1) On eviction, because of the maxmemory and maxmemory policy configurations,\n#    in order to make room for new data, without going over the specified\n#    memory limit.\n# 2) Because of expire: when a key with an associated time to live (see the\n#    EXPIRE command) must be deleted from memory.\n# 3) Because of a side effect of a command that stores data on a key that may\n#    already exist. For example the RENAME command may delete the old key\n#    content when it is replaced with another one. Similarly SUNIONSTORE\n#    or SORT with STORE option may delete existing keys. The SET command\n#    itself removes any old content of the specified key in order to replace\n#    it with the specified string.\n# 4) During replication, when a replica performs a full resynchronization with\n#    its master, the content of the whole database is removed in order to\n#    load the RDB file just transferred.\n#\n# In all the above cases the default is to delete objects in a blocking way,\n# like if DEL was called. However you can configure each case specifically\n# in order to instead release memory in a non-blocking way like if UNLINK\n# was called, using the following configuration directives.\n\nlazyfree-lazy-eviction no\nlazyfree-lazy-expire no\nlazyfree-lazy-server-del no\nreplica-lazy-flush no\n\n# It is also possible, for the case when to replace the user code DEL calls\n# with UNLINK calls is not easy, to modify the default behavior of the DEL\n# command to act exactly like UNLINK, using the following configuration\n# directive:\n\nlazyfree-lazy-user-del no\n\n################################ THREADED I/O #################################\n\n# Redis is mostly single threaded, however there are certain threaded\n# operations such as UNLINK, slow I/O accesses and other things that are\n# performed on side threads.\n#\n# Now it is also possible to handle Redis clients socket reads and writes\n# in different I/O threads. Since especially writing is so slow, normally\n# Redis users use pipelining in order to speedup the Redis performances per\n# core, and spawn multiple instances in order to scale more. Using I/O\n# threads it is possible to easily speedup two times Redis without resorting\n# to pipelining nor sharding of the instance.\n#\n# By default threading is disabled, we suggest enabling it only in machines\n# that have at least 4 or more cores, leaving at least one spare core.\n# Using more than 8 threads is unlikely to help much. We also recommend using\n# threaded I/O only if you actually have performance problems, with Redis\n# instances being able to use a quite big percentage of CPU time, otherwise\n# there is no point in using this feature.\n#\n# So for instance if you have a four cores boxes, try to use 2 or 3 I/O\n# threads, if you have a 8 cores, try to use 6 threads. In order to\n# enable I/O threads use the following configuration directive:\n#\n# io-threads 4\n#\n# Setting io-threads to 1 will just use the main thread as usually.\n# When I/O threads are enabled, we only use threads for writes, that is\n# to thread the write(2) syscall and transfer the client buffers to the\n# socket. However it is also possible to enable threading of reads and\n# protocol parsing using the following configuration directive, by setting\n# it to yes:\n#\n# io-threads-do-reads no\n#\n# Usually threading reads doesn't help much.\n#\n# NOTE 1: This configuration directive cannot be changed at runtime via\n# CONFIG SET. Aso this feature currently does not work when SSL is\n# enabled.\n#\n# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make\n# sure you also run the benchmark itself in threaded mode, using the\n# --threads option to match the number of Redis theads, otherwise you'll not\n# be able to notice the improvements.\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 of waiting 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# An AOF file may be found to be truncated at the end during the Redis\n# startup process, when the AOF data gets loaded back into memory.\n# This may happen when the system where Redis is running\n# crashes, especially when an ext4 filesystem is mounted without the\n# data=ordered option (however this can't happen when Redis itself\n# crashes or aborts but the operating system still works correctly).\n#\n# Redis can either exit with an error when this happens, or load as much\n# data as possible (the default now) and start if the AOF file is found\n# to be truncated at the end. The following option controls this behavior.\n#\n# If aof-load-truncated is set to yes, a truncated AOF file is loaded and\n# the Redis server starts emitting a log to inform the user of the event.\n# Otherwise if the option is set to no, the server aborts with an error\n# and refuses to start. When the option is set to no, the user requires\n# to fix the AOF file using the \"redis-check-aof\" utility before to restart\n# the server.\n#\n# Note that if the AOF file will be found to be corrupted in the middle\n# the server will still exit with an error. This option only applies when\n# Redis will try to read more data from the AOF file but not enough bytes\n# will be found.\naof-load-truncated yes\n\n# When rewriting the AOF file, Redis is able to use an RDB preamble in the\n# AOF file for faster rewrites and recoveries. When this option is turned\n# on the rewritten AOF file is composed of two different stanzas:\n#\n#   [RDB file][AOF tail]\n#\n# When loading Redis recognizes that the AOF file starts with the \"REDIS\"\n# string and loads the prefixed RDB file, and continues loading the AOF\n# tail.\naof-use-rdb-preamble yes\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 exceeds 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 command was\n# already issued by the script but the user doesn'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 do 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# A replica of a failing master will avoid to start a failover if its data\n# looks too old.\n#\n# There is no simple way for a replica to actually have an exact measure of\n# its \"data age\", so the following two checks are performed:\n#\n# 1) If there are multiple replicas able to failover, they exchange messages\n#    in order to try to give an advantage to the replica with the best\n#    replication offset (more data from the master processed).\n#    Replicas will try to get their rank by offset, and apply to the start\n#    of the failover a delay proportional to their rank.\n#\n# 2) Every single replica computes the time of the last interaction with\n#    its master. This can be the last ping or command received (if the master\n#    is still in the \"connected\" state), or the time that elapsed since the\n#    disconnection with the master (if the replication link is currently down).\n#    If the last interaction is too old, the replica will not try to failover\n#    at all.\n#\n# The point \"2\" can be tuned by user. Specifically a replica will not perform\n# the failover if, since the last interaction with the master, the time\n# elapsed is greater than:\n#\n#   (node-timeout * replica-validity-factor) + repl-ping-replica-period\n#\n# So for example if node-timeout is 30 seconds, and the replica-validity-factor\n# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the\n# replica will not try to failover if it was not able to talk with the master\n# for longer than 310 seconds.\n#\n# A large replica-validity-factor may allow replicas with too old data to failover\n# a master, while a too small value may prevent the cluster from being able to\n# elect a replica at all.\n#\n# For maximum availability, it is possible to set the replica-validity-factor\n# to a value of 0, which means, that replicas will always try to failover the\n# master regardless of the last time they interacted with the master.\n# (However they'll always try to apply a delay proportional to their\n# offset rank).\n#\n# Zero is the only value able to guarantee that when all the partitions heal\n# the cluster will always be able to continue.\n#\n# cluster-replica-validity-factor 10\n\n# Cluster replicas are able to migrate to orphaned masters, that are masters\n# that are left without working replicas. 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 replicas.\n#\n# Replicas migrate to orphaned masters only if there are still at least a\n# given number of other working replicas for their old master. This number\n# is the \"migration barrier\". A migration barrier of 1 means that a replica\n# will migrate only if there is at least 1 other working replica for its master\n# and so forth. It usually reflects the number of replicas you want for every\n# master in your cluster.\n#\n# Default is 1 (replicas migrate only if their masters remain with at least\n# one replica). 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# By default Redis Cluster nodes stop accepting queries if they detect there\n# is at least an hash slot uncovered (no available node is serving it).\n# This way if the cluster is partially down (for example a range of hash slots\n# are no longer covered) all the cluster becomes, eventually, unavailable.\n# It automatically returns available as soon as all the slots are covered again.\n#\n# However sometimes you want the subset of the cluster which is working,\n# to continue to accept queries for the part of the key space that is still\n# covered. In order to do so, just set the cluster-require-full-coverage\n# option to no.\n#\n# cluster-require-full-coverage yes\n\n# This option, when set to yes, prevents replicas from trying to failover its\n# master during master failures. However the master can still perform a\n# manual failover, if forced to do so.\n#\n# This is useful in different scenarios, especially in the case of multiple\n# data center operations, where we want one side to never be promoted if not\n# in the case of a total DC failure.\n#\n# cluster-replica-no-failover no\n\n# This option, when set to yes, allows nodes to serve read traffic while the\n# the cluster is in a down state, as long as it believes it owns the slots. \n#\n# This is useful for two cases.  The first case is for when an application \n# doesn't require consistency of data during node failures or network partitions.\n# One example of this is a cache, where as long as the node has the data it\n# should be able to serve it. \n#\n# The second use case is for configurations that don't meet the recommended  \n# three shards but want to enable cluster mode and scale later. A \n# master outage in a 1 or 2 shard configuration causes a read/write outage to the\n# entire cluster without this option set, with it set there is only a write outage.\n# Without a quorum of masters, slot ownership will not change automatically. \n#\n# cluster-allow-reads-when-down no\n\n# In order to setup your cluster make sure to read the documentation\n# available at http://redis.io web site.\n\n########################## CLUSTER DOCKER/NAT support  ########################\n\n# In certain deployments, Redis Cluster nodes address discovery fails, because\n# addresses are NAT-ted or because ports are forwarded (the typical case is\n# Docker and other containers).\n#\n# In order to make Redis Cluster working in such environments, a static\n# configuration where each node knows its public address is needed. The\n# following two options are used for this scope, and are:\n#\n# * cluster-announce-ip\n# * cluster-announce-port\n# * cluster-announce-bus-port\n#\n# Each instruct the node about its address, client port, and cluster message\n# bus port. The information is then published in the header of the bus packets\n# so that other nodes will be able to correctly map the address of the node\n# publishing the information.\n#\n# If the above options are not used, the normal Redis Cluster auto-detection\n# will be used instead.\n#\n# Note that when remapped, the bus port may not be at the fixed offset of\n# clients port + 10000, so you can specify any port and bus-port depending\n# on how they get remapped. If the bus-port is not set, a fixed offset of\n# 10000 will be used as usually.\n#\n# Example:\n#\n# cluster-announce-ip 10.1.1.5\n# cluster-announce-port 6379\n# cluster-announce-bus-port 6380\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################################ LATENCY MONITOR ##############################\n\n# The Redis latency monitoring subsystem samples different operations\n# at runtime in order to collect data related to possible sources of\n# latency of a Redis instance.\n#\n# Via the LATENCY command this information is available to the user that can\n# print graphs and obtain reports.\n#\n# The system only logs operations that were performed in a time equal or\n# greater than the amount of milliseconds specified via the\n# latency-monitor-threshold configuration directive. When its value is set\n# to zero, the latency monitor is turned off.\n#\n# By default latency monitoring is disabled since it is mostly not needed\n# if you don't have latency issues, and collecting data has a performance\n# impact, that while very small, can be measured under big load. Latency\n# monitoring can easily be enabled at runtime using the command\n# \"CONFIG SET latency-monitor-threshold <milliseconds>\" if needed.\nlatency-monitor-threshold 0\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/notifications\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#  t     Stream commands\n#  m     Key-miss events (Note: It is not included in the 'A' class)\n#  A     Alias for g$lshzxet, so that the \"AKE\" string means all the events\n#        (Except key-miss events which are excluded from 'A' due to their\n#         unique nature).\n#\n#  The \"notify-keyspace-events\" takes as argument a string that is composed\n#  of zero or multiple characters. The empty string means that notifications\n#  are disabled.\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############################### GOPHER SERVER #################################\n\n# Redis contains an implementation of the Gopher protocol, as specified in\n# the RFC 1436 (https://www.ietf.org/rfc/rfc1436.txt).\n#\n# The Gopher protocol was very popular in the late '90s. It is an alternative\n# to the web, and the implementation both server and client side is so simple\n# that the Redis server has just 100 lines of code in order to implement this\n# support.\n#\n# What do you do with Gopher nowadays? Well Gopher never *really* died, and\n# lately there is a movement in order for the Gopher more hierarchical content\n# composed of just plain text documents to be resurrected. Some want a simpler\n# internet, others believe that the mainstream internet became too much\n# controlled, and it's cool to create an alternative space for people that\n# want a bit of fresh air.\n#\n# Anyway for the 10nth birthday of the Redis, we gave it the Gopher protocol\n# as a gift.\n#\n# --- HOW IT WORKS? ---\n#\n# The Redis Gopher support uses the inline protocol of Redis, and specifically\n# two kind of inline requests that were anyway illegal: an empty request\n# or any request that starts with \"/\" (there are no Redis commands starting\n# with such a slash). Normal RESP2/RESP3 requests are completely out of the\n# path of the Gopher protocol implementation and are served as usually as well.\n#\n# If you open a connection to Redis when Gopher is enabled and send it\n# a string like \"/foo\", if there is a key named \"/foo\" it is served via the\n# Gopher protocol.\n#\n# In order to create a real Gopher \"hole\" (the name of a Gopher site in Gopher\n# talking), you likely need a script like the following:\n#\n#   https://github.com/antirez/gopher2redis\n#\n# --- SECURITY WARNING ---\n#\n# If you plan to put Redis on the internet in a publicly accessible address\n# to server Gopher pages MAKE SURE TO SET A PASSWORD to the instance.\n# Once a password is set:\n#\n#   1. The Gopher server (when enabled, not by default) will still serve\n#      content via Gopher.\n#   2. However other commands cannot be called before the client will\n#      authenticate.\n#\n# So use the 'requirepass' option to protect your instance.\n#\n# To enable Gopher support uncomment the following line and set\n# the option from no (the default) to yes.\n#\n# gopher-enabled no\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# Lists are also encoded in a special way to save a lot of space.\n# The number of entries allowed per internal list node can be specified\n# as a fixed maximum size or a maximum number of elements.\n# For a fixed maximum size, use -5 through -1, meaning:\n# -5: max size: 64 Kb  <-- not recommended for normal workloads\n# -4: max size: 32 Kb  <-- not recommended\n# -3: max size: 16 Kb  <-- probably not recommended\n# -2: max size: 8 Kb   <-- good\n# -1: max size: 4 Kb   <-- good\n# Positive numbers mean store up to _exactly_ that number of elements\n# per list node.\n# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),\n# but if your use case is unique, adjust the settings as necessary.\nlist-max-ziplist-size -2\n\n# Lists may also be compressed.\n# Compress depth is the number of quicklist ziplist nodes from *each* side of\n# the list to *exclude* from compression.  The head and tail of the list\n# are always uncompressed for fast push/pop operations.  Settings are:\n# 0: disable all list compression\n# 1: depth 1 means \"don't start compressing until after 1 node into the list,\n#    going from either the head or tail\"\n#    So: [head]->node->node->...->node->[tail]\n#    [head], [tail] will always be uncompressed; inner nodes will compress.\n# 2: [head]->[next]->node->node->...->node->[prev]->[tail]\n#    2 here means: don't compress head or head->next or tail->prev or tail,\n#    but compress all nodes between them.\n# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]\n# etc.\nlist-compress-depth 0\n\n# Sets have a special encoding in just one case: when a set is composed\n# of just strings that happen 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# Streams macro node max size / items. The stream data structure is a radix\n# tree of big nodes that encode multiple items inside. Using this configuration\n# it is possible to configure how big a single node can be in bytes, and the\n# maximum number of items it may contain before switching to a new node when\n# appending new stream entries. If any of the following settings are set to\n# zero, the limit is ignored, so for instance it is possible to set just a\n# max entires limit by setting max-bytes to 0 and max-entries to the desired\n# value.\nstream-node-max-bytes 4096\nstream-node-max-entries 100\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# actively rehash 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 from 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 including MONITOR clients\n# replica  -> replica 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 replica clients, since\n# subscribers and replicas 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 replica 256mb 64mb 60\nclient-output-buffer-limit pubsub 32mb 8mb 60\n\n# Client query buffers accumulate new commands. They are limited to a fixed\n# amount by default in order to avoid that a protocol desynchronization (for\n# instance due to a bug in the client) will lead to unbound memory usage in\n# the query buffer. However you can configure it here if you have very special\n# needs, such us huge multi/exec requests or alike.\n#\n# client-query-buffer-limit 1gb\n\n# In the Redis protocol, bulk requests, that are, elements representing single\n# strings, are normally limited ot 512 mb. However you can change this limit\n# here.\n#\n# proto-max-bulk-len 512mb\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 according 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# Normally it is useful to have an HZ value which is proportional to the\n# number of clients connected. This is useful in order, for instance, to\n# avoid too many clients are processed for each background task invocation\n# in order to avoid latency spikes.\n#\n# Since the default HZ value by default is conservatively set to 10, Redis\n# offers, and enables by default, the ability to use an adaptive HZ value\n# which will temporary raise when there are many connected clients.\n#\n# When dynamic HZ is enabled, the actual configured HZ will be used\n# as a baseline, but multiples of the configured HZ value will be actually\n# used as needed once more clients are connected. In this way an idle\n# instance will use very little CPU time while a busy instance will be\n# more responsive.\ndynamic-hz yes\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# When redis saves RDB 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.\nrdb-save-incremental-fsync yes\n\n# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good\n# idea to start with the default settings and only change them after investigating\n# how to improve the performances and how the keys LFU change over time, which\n# is possible to inspect via the OBJECT FREQ command.\n#\n# There are two tunable parameters in the Redis LFU implementation: the\n# counter logarithm factor and the counter decay time. It is important to\n# understand what the two parameters mean before changing them.\n#\n# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis\n# uses a probabilistic increment with logarithmic behavior. Given the value\n# of the old counter, when a key is accessed, the counter is incremented in\n# this way:\n#\n# 1. A random number R between 0 and 1 is extracted.\n# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1).\n# 3. The counter is incremented only if R < P.\n#\n# The default lfu-log-factor is 10. This is a table of how the frequency\n# counter changes with a different number of accesses with different\n# logarithmic factors:\n#\n# +--------+------------+------------+------------+------------+------------+\n# | factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |\n# +--------+------------+------------+------------+------------+------------+\n# | 0      | 104        | 255        | 255        | 255        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n# | 1      | 18         | 49         | 255        | 255        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n# | 10     | 10         | 18         | 142        | 255        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n# | 100    | 8          | 11         | 49         | 143        | 255        |\n# +--------+------------+------------+------------+------------+------------+\n#\n# NOTE: The above table was obtained by running the following commands:\n#\n#   redis-benchmark -n 1000000 incr foo\n#   redis-cli object freq foo\n#\n# NOTE 2: The counter initial value is 5 in order to give new objects a chance\n# to accumulate hits.\n#\n# The counter decay time is the time, in minutes, that must elapse in order\n# for the key counter to be divided by two (or decremented if it has a value\n# less <= 10).\n#\n# The default value for the lfu-decay-time is 1. A Special value of 0 means to\n# decay the counter every time it happens to be scanned.\n#\n# lfu-log-factor 10\n# lfu-decay-time 1\n\n########################### ACTIVE DEFRAGMENTATION #######################\n#\n# What is active defragmentation?\n# -------------------------------\n#\n# Active (online) defragmentation allows a Redis server to compact the\n# spaces left between small allocations and deallocations of data in memory,\n# thus allowing to reclaim back memory.\n#\n# Fragmentation is a natural process that happens with every allocator (but\n# less so with Jemalloc, fortunately) and certain workloads. Normally a server\n# restart is needed in order to lower the fragmentation, or at least to flush\n# away all the data and create it again. However thanks to this feature\n# implemented by Oran Agra for Redis 4.0 this process can happen at runtime\n# in an \"hot\" way, while the server is running.\n#\n# Basically when the fragmentation is over a certain level (see the\n# configuration options below) Redis will start to create new copies of the\n# values in contiguous memory regions by exploiting certain specific Jemalloc\n# features (in order to understand if an allocation is causing fragmentation\n# and to allocate it in a better place), and at the same time, will release the\n# old copies of the data. This process, repeated incrementally for all the keys\n# will cause the fragmentation to drop back to normal values.\n#\n# Important things to understand:\n#\n# 1. This feature is disabled by default, and only works if you compiled Redis\n#    to use the copy of Jemalloc we ship with the source code of Redis.\n#    This is the default with Linux builds.\n#\n# 2. You never need to enable this feature if you don't have fragmentation\n#    issues.\n#\n# 3. Once you experience fragmentation, you can enable this feature when\n#    needed with the command \"CONFIG SET activedefrag yes\".\n#\n# The configuration parameters are able to fine tune the behavior of the\n# defragmentation process. If you are not sure about what they mean it is\n# a good idea to leave the defaults untouched.\n\n# Enabled active defragmentation\n# activedefrag no\n\n# Minimum amount of fragmentation waste to start active defrag\n# active-defrag-ignore-bytes 100mb\n\n# Minimum percentage of fragmentation to start active defrag\n# active-defrag-threshold-lower 10\n\n# Maximum percentage of fragmentation at which we use maximum effort\n# active-defrag-threshold-upper 100\n\n# Minimal effort for defrag in CPU percentage, to be used when the lower\n# threshold is reached\n# active-defrag-cycle-min 1\n\n# Maximal effort for defrag in CPU percentage, to be used when the upper\n# threshold is reached\n# active-defrag-cycle-max 25\n\n# Maximum number of set/hash/zset/list fields that will be processed from\n# the main dictionary scan\n# active-defrag-max-scan-fields 1000\n\n# Jemalloc background thread for purging will be enabled by default\njemalloc-bg-thread yes\n\n# It is possible to pin different threads and processes of Redis to specific\n# CPUs in your system, in order to maximize the performances of the server.\n# This is useful both in order to pin different Redis threads in different\n# CPUs, but also in order to make sure that multiple Redis instances running\n# in the same host will be pinned to different CPUs.\n#\n# Normally you can do this using the \"taskset\" command, however it is also\n# possible to this via Redis configuration directly, both in Linux and FreeBSD.\n#\n# You can pin the server/IO threads, bio threads, aof rewrite child process, and\n# the bgsave child process. The syntax to specify the cpu list is the same as\n# the taskset command:\n#\n# Set redis server/io threads to cpu affinity 0,2,4,6:\n# server_cpulist 0-7:2\n#\n# Set bio threads to cpu affinity 1,3:\n# bio_cpulist 1,3\n#\n# Set aof rewrite child process to cpu affinity 8,9,10,11:\n# aof_rewrite_cpulist 8-11\n#\n# Set bgsave child process to cpu affinity 1,10,11\n# bgsave_cpulist 1,10-11\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/runtest-moduleapi",
    "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\nmake -C tests/modules && \\\n$TCLSH tests/test_helper.tcl \\\n--single unit/moduleapi/commandfilter \\\n--single unit/moduleapi/fork \\\n--single unit/moduleapi/testrdb \\\n--single unit/moduleapi/infotest \\\n--single unit/moduleapi/propagate \\\n--single unit/moduleapi/hooks \\\n--single unit/moduleapi/misc \\\n--single unit/moduleapi/blockonkeys \\\n--single unit/moduleapi/scan \\\n--single unit/moduleapi/datatype \\\n--single unit/moduleapi/auth \\\n\"${@}\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/sentinel.conf",
    "content": "# Example sentinel.conf\n\n# *** IMPORTANT ***\n#\n# By default Sentinel will not be reachable from interfaces different than\n# localhost, either use the 'bind' directive to bind to a list of network\n# interfaces, or disable protected mode with \"protected-mode no\" by\n# adding it to this configuration file.\n#\n# Before doing that MAKE SURE the instance is protected from the outside\n# world via firewalling or other means.\n#\n# For example you may use one of the following:\n#\n# bind 127.0.0.1 192.168.1.1\n#\n# protected-mode no\n\n# port <sentinel-port>\n# The port that this sentinel instance will run on\nport 26379\n\n# By default Redis Sentinel 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-sentinel.pid when\n# daemonized.\ndaemonize no\n\n# When running daemonized, Redis Sentinel writes a pid file in\n# /var/run/redis-sentinel.pid by default. You can specify a custom pid file\n# location here.\npidfile /var/run/redis-sentinel.pid\n\n# Specify the log file name. Also the empty string can be used to force\n# Sentinel 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# sentinel announce-ip <ip>\n# sentinel announce-port <port>\n#\n# The above two configuration directives are useful in environments where,\n# because of NAT, Sentinel is reachable from outside via a non-local address.\n#\n# When announce-ip is provided, the Sentinel will claim the specified IP address\n# in HELLO messages used to gossip its presence, instead of auto-detecting the\n# local address as it usually does.\n#\n# Similarly when announce-port is provided and is valid and non-zero, Sentinel\n# will announce the specified TCP port.\n#\n# The two options don't need to be used together, if only announce-ip is\n# provided, the Sentinel will announce the specified IP and the server port\n# as specified by the \"port\" option. If only announce-port is provided, the\n# Sentinel will announce the auto-detected local IP and the specified port.\n#\n# Example:\n#\n# sentinel announce-ip 1.2.3.4\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 interfere 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# Replicas are auto-discovered, so you don't need to specify replicas in\n# any way. Sentinel itself will rewrite this configuration file adding\n# the replicas using additional configuration options.\n# Also note that the configuration file is rewritten when a\n# replica is promoted to master.\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 replicas.\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 replicas, so it is not\n# possible to set a different password in masters and replicas 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 auth-user <master-name> <username>\n#\n# This is useful in order to authenticate to instances having ACL capabilities,\n# that is, running Redis 6.0 or greater. When just auth-pass is provided the\n# Sentinel instance will authenticate to Redis using the old \"AUTH <pass>\"\n# method. When also an username is provided, it will use \"AUTH <user> <pass>\".\n# In the Redis servers side, the ACL to provide just minimal access to\n# Sentinel instances, should be configured along the following lines:\n#\n#     user sentinel-user >somepassword +client +subscribe +publish \\\n#                        +ping +info +multi +slaveof +config +client +exec on\n\n# sentinel down-after-milliseconds <master-name> <milliseconds>\n#\n# Number of milliseconds the master (or any attached replica 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# requirepass <password>\n#\n# You can configure Sentinel itself to require a password, however when doing\n# so Sentinel will try to authenticate with the same password to all the\n# other Sentinels. So you need to configure all your Sentinels in a given\n# group with the same \"requirepass\" password. Check the following documentation\n# for more info: https://redis.io/topics/sentinel\n\n# sentinel parallel-syncs <master-name> <numreplicas>\n#\n# How many replicas we can reconfigure to point to the new replica simultaneously\n# during the failover. Use a low number if you use the replicas to serve query\n# to avoid that all the replicas 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 replica 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 replica).\n#\n# - The maximum time a failover in progress waits for all the replicas to be\n#   reconfigured as replicas of the new master. However even after this time\n#   the replicas 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 replica\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# SECURITY\n#\n# By default SENTINEL SET will not be able to change the notification-script\n# and client-reconfig-script at runtime. This avoids a trivial security issue\n# where clients can set the script to anything and trigger a failover in order\n# to get the program executed.\n\nsentinel deny-scripts-reconfig yes\n\n# REDIS COMMANDS RENAMING\n#\n# Sometimes the Redis server has certain commands, that are needed for Sentinel\n# to work correctly, renamed to unguessable strings. This is often the case\n# of CONFIG and SLAVEOF in the context of providers that provide Redis as\n# a service, and don't want the customers to reconfigure the instances outside\n# of the administration console.\n#\n# In such case it is possible to tell Sentinel to use different command names\n# instead of the normal ones. For example if the master \"mymaster\", and the\n# associated replicas, have \"CONFIG\" all renamed to \"GUESSME\", I could use:\n#\n# SENTINEL rename-command mymaster CONFIG GUESSME\n#\n# After such configuration is set, every time Sentinel would use CONFIG it will\n# use GUESSME instead. Note that there is no actual need to respect the command\n# case, so writing \"config guessme\" is the same in the example above.\n#\n# SENTINEL SET can also be used in order to perform this configuration at runtime.\n#\n# In order to set a command back to its original name (undo the renaming), it\n# is possible to just rename a command to itsef:\n#\n# SENTINEL rename-command mymaster CONFIG CONFIG\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/.gitignore",
    "content": "*.gcda\n*.gcno\n*.gcov\nredis.info\nlcov-html\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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')\nuname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')\nOPTIMIZATION?=-O2\nDEPENDENCY_TARGETS=hiredis linenoise lua\nNODEPS:=clean distclean\n\n# Default settings\nSTD=-std=c11 -pedantic -DREDIS_STATIC=''\nifneq (,$(findstring clang,$(CC)))\nifneq (,$(findstring FreeBSD,$(uname_S)))\n  STD+=-Wno-c11-extensions\nendif\nendif\nWARN=-Wall -W -Wno-missing-field-initializers\nOPT=$(OPTIMIZATION)\n\nPREFIX?=/usr/local\nINSTALL_BIN=$(PREFIX)/bin\nINSTALL=install\nPKG_CONFIG?=pkg-config\n\n# Default allocator defaults to Jemalloc if it's not an ARM\nMALLOC=libc\nifneq ($(uname_M),armv6l)\nifneq ($(uname_M),armv7l)\nifeq ($(uname_S),Linux)\n\tMALLOC=jemalloc\nendif\nendif\nendif\n\n# To get ARM stack traces if Redis crashes we need a special C flag.\nifneq (,$(filter aarch64 armv,$(uname_M)))\n        CFLAGS+=-funwind-tables\nelse\nifneq (,$(findstring armv,$(uname_M)))\n        CFLAGS+=-funwind-tables\nendif\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\nifeq ($(USE_JEMALLOC),no)\n\tMALLOC=libc\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\n# Linux ARM needs -latomic at linking time\nifneq (,$(filter aarch64 armv,$(uname_M)))\n        FINAL_LIBS+=-latomic\nelse\nifneq (,$(findstring armv,$(uname_M)))\n        FINAL_LIBS+=-latomic\nendif\nendif\n\nifeq ($(uname_S),SunOS)\n\t# SunOS\n        ifneq ($(@@),32bit)\n\t\tCFLAGS+= -m64\n\t\tLDFLAGS+= -m64\n\tendif\n\tDEBUG=-g\n\tDEBUG_FLAGS=-g\n\texport CFLAGS LDFLAGS DEBUG DEBUG_FLAGS\n\tINSTALL=cp -pf\n\tFINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6\n\tFINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt\nelse\nifeq ($(uname_S),Darwin)\n\t# Darwin\n\tFINAL_LIBS+= -ldl\n\tOPENSSL_CFLAGS=-I/usr/local/opt/openssl/include\n\tOPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib\nelse\nifeq ($(uname_S),AIX)\n        # AIX\n        FINAL_LDFLAGS+= -Wl,-bexpall\n        FINAL_LIBS+=-ldl -pthread -lcrypt -lbsd\nelse\nifeq ($(uname_S),OpenBSD)\n\t# OpenBSD\n\tFINAL_LIBS+= -lpthread\n\tifeq ($(USE_BACKTRACE),yes)\n\t    FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/local/include\n\t    FINAL_LDFLAGS+= -L/usr/local/lib\n\t    FINAL_LIBS+= -lexecinfo\n    \tendif\n\nelse\nifeq ($(uname_S),FreeBSD)\n\t# FreeBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\nifeq ($(uname_S),DragonFly)\n\t# FreeBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\nifeq ($(uname_S),OpenBSD)\n\t# OpenBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\nifeq ($(uname_S),NetBSD)\n\t# NetBSD\n\tFINAL_LIBS+= -lpthread -lexecinfo\nelse\n\t# All the other OSes (notably Linux)\n\tFINAL_LDFLAGS+= -rdynamic\n\tFINAL_LIBS+=-ldl -pthread -lrt\nendif\nendif\nendif\nendif\nendif\nendif\nendif\nendif\n# Include paths to dependencies\nFINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src\n\n# Determine systemd support and/or build preference (defaulting to auto-detection)\nBUILD_WITH_SYSTEMD=no\n# If 'USE_SYSTEMD' in the environment is neither \"no\" nor \"yes\", try to\n# auto-detect libsystemd's presence and link accordingly.\nifneq ($(USE_SYSTEMD),no)\n\tLIBSYSTEMD_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libsystemd && echo $$?)\n# If libsystemd cannot be detected, continue building without support for it\n# (unless a later check tells us otherwise)\nifeq ($(LIBSYSTEMD_PKGCONFIG),0)\n\tBUILD_WITH_SYSTEMD=yes\nendif\nendif\nifeq ($(USE_SYSTEMD),yes)\nifneq ($(LIBSYSTEMD_PKGCONFIG),0)\n$(error USE_SYSTEMD is set to \"$(USE_SYSTEMD)\", but $(PKG_CONFIG) cannot find libsystemd)\nendif\n# Force building with libsystemd\n\tBUILD_WITH_SYSTEMD=yes\nendif\nifeq ($(BUILD_WITH_SYSTEMD),yes)\n\tFINAL_LIBS+=$(shell $(PKG_CONFIG) --libs libsystemd)\n\tFINAL_CFLAGS+= -DHAVE_LIBSYSTEMD\nendif\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 $(FINAL_LIBS)\nendif\n\nifeq ($(BUILD_TLS),yes)\n    FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS)\n    FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)\n    FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto\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 quicklist.o ae.o anet.o dict.o server.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 crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o\nREDIS_CLI_NAME=redis-cli\nREDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o\nREDIS_BENCHMARK_NAME=redis-benchmark\nREDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o\nREDIS_CHECK_RDB_NAME=redis-check-rdb\nREDIS_CHECK_AOF_NAME=redis-check-aof\n\nall: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME)\n\t@echo \"\"\n\t@echo \"Hint: It's a good idea to run 'make test' ;)\"\n\t@echo \"\"\n\nMakefile.dep:\n\t-$(REDIS_CC) -MM *.c > Makefile.dep 2> /dev/null || true\n\nifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))\n-include Makefile.dep\nendif\n\n.PHONY: all\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-check-rdb\n$(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME)\n\t$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME)\n\n# redis-check-aof\n$(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)\n\t$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_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\ndict-benchmark: dict.c zmalloc.c sds.c siphash.c\n\t$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)\n\nDEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)\n-include $(DEP)\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) -MMD -o $@ -c $<\n\nclean:\n\trm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark\n\trm -f $(DEP)\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\ntest-sds: sds.c sds.h\n\t$(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test\n\t/tmp/sds_test\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\nhelgrind:\n\t$(MAKE) OPTIMIZATION=\"-O0\" MALLOC=\"libc\" CFLAGS=\"-D__ATOMIC_VAR_FORCE_SYNC_MACROS\"\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_RDB_NAME) $(INSTALL_BIN)\n\t$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)\n\t@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)\n\nuninstall:\n\trm -f $(INSTALL_BIN)/{$(REDIS_SERVER_NAME),$(REDIS_BENCHMARK_NAME),$(REDIS_CLI_NAME),$(REDIS_CHECK_RDB_NAME),$(REDIS_CHECK_AOF_NAME),$(REDIS_SENTINEL_NAME)}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/acl.c",
    "content": "/*\n * Copyright (c) 2018, 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 \"server.h\"\n#include \"sha256.h\"\n#include <fcntl.h>\n#include <ctype.h>\n\n/* =============================================================================\n * Global state for ACLs\n * ==========================================================================*/\n\nrax *Users; /* Table mapping usernames to user structures. */\n\nuser *DefaultUser;  /* Global reference to the default user.\n                       Every new connection is associated to it, if no\n                       AUTH or HELLO is used to authenticate with a\n                       different user. */\n\nlist *UsersToLoad;  /* This is a list of users found in the configuration file\n                       that we'll need to load in the final stage of Redis\n                       initialization, after all the modules are already\n                       loaded. Every list element is a NULL terminated\n                       array of SDS pointers: the first is the user name,\n                       all the remaining pointers are ACL rules in the same\n                       format as ACLSetUser(). */\nlist *ACLLog;       /* Our security log, the user is able to inspect that\n                       using the ACL LOG command .*/\n\nstruct ACLCategoryItem {\n    const char *name;\n    uint64_t flag;\n} ACLCommandCategories[] = {\n    {\"keyspace\", CMD_CATEGORY_KEYSPACE},\n    {\"read\", CMD_CATEGORY_READ},\n    {\"write\", CMD_CATEGORY_WRITE},\n    {\"set\", CMD_CATEGORY_SET},\n    {\"sortedset\", CMD_CATEGORY_SORTEDSET},\n    {\"list\", CMD_CATEGORY_LIST},\n    {\"hash\", CMD_CATEGORY_HASH},\n    {\"string\", CMD_CATEGORY_STRING},\n    {\"bitmap\", CMD_CATEGORY_BITMAP},\n    {\"hyperloglog\", CMD_CATEGORY_HYPERLOGLOG},\n    {\"geo\", CMD_CATEGORY_GEO},\n    {\"stream\", CMD_CATEGORY_STREAM},\n    {\"pubsub\", CMD_CATEGORY_PUBSUB},\n    {\"admin\", CMD_CATEGORY_ADMIN},\n    {\"fast\", CMD_CATEGORY_FAST},\n    {\"slow\", CMD_CATEGORY_SLOW},\n    {\"blocking\", CMD_CATEGORY_BLOCKING},\n    {\"dangerous\", CMD_CATEGORY_DANGEROUS},\n    {\"connection\", CMD_CATEGORY_CONNECTION},\n    {\"transaction\", CMD_CATEGORY_TRANSACTION},\n    {\"scripting\", CMD_CATEGORY_SCRIPTING},\n    {NULL,0} /* Terminator. */\n};\n\nstruct ACLUserFlag {\n    const char *name;\n    uint64_t flag;\n} ACLUserFlags[] = {\n    {\"on\", USER_FLAG_ENABLED},\n    {\"off\", USER_FLAG_DISABLED},\n    {\"allkeys\", USER_FLAG_ALLKEYS},\n    {\"allcommands\", USER_FLAG_ALLCOMMANDS},\n    {\"nopass\", USER_FLAG_NOPASS},\n    {NULL,0} /* Terminator. */\n};\n\nvoid ACLResetSubcommandsForCommand(user *u, unsigned long id);\nvoid ACLResetSubcommands(user *u);\nvoid ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub);\nvoid ACLFreeLogEntry(void *le);\n\n/* The length of the string representation of a hashed password. */\n#define HASH_PASSWORD_LEN SHA256_BLOCK_SIZE*2\n\n/* =============================================================================\n * Helper functions for the rest of the ACL implementation\n * ==========================================================================*/\n\n/* Return zero if strings are the same, non-zero if they are not.\n * The comparison is performed in a way that prevents an attacker to obtain\n * information about the nature of the strings just monitoring the execution\n * time of the function.\n *\n * Note that limiting the comparison length to strings up to 512 bytes we\n * can avoid leaking any information about the password length and any\n * possible branch misprediction related leak.\n */\nint time_independent_strcmp(char *a, char *b) {\n    char bufa[CONFIG_AUTHPASS_MAX_LEN], bufb[CONFIG_AUTHPASS_MAX_LEN];\n    /* The above two strlen perform len(a) + len(b) operations where either\n     * a or b are fixed (our password) length, and the difference is only\n     * relative to the length of the user provided string, so no information\n     * leak is possible in the following two lines of code. */\n    unsigned int alen = strlen(a);\n    unsigned int blen = strlen(b);\n    unsigned int j;\n    int diff = 0;\n\n    /* We can't compare strings longer than our static buffers.\n     * Note that this will never pass the first test in practical circumstances\n     * so there is no info leak. */\n    if (alen > sizeof(bufa) || blen > sizeof(bufb)) return 1;\n\n    memset(bufa,0,sizeof(bufa));        /* Constant time. */\n    memset(bufb,0,sizeof(bufb));        /* Constant time. */\n    /* Again the time of the following two copies is proportional to\n     * len(a) + len(b) so no info is leaked. */\n    memcpy(bufa,a,alen);\n    memcpy(bufb,b,blen);\n\n    /* Always compare all the chars in the two buffers without\n     * conditional expressions. */\n    for (j = 0; j < sizeof(bufa); j++) {\n        diff |= (bufa[j] ^ bufb[j]);\n    }\n    /* Length must be equal as well. */\n    diff |= alen ^ blen;\n    return diff; /* If zero strings are the same. */\n}\n\n/* Given an SDS string, returns the SHA256 hex representation as a\n * new SDS string. */\nsds ACLHashPassword(unsigned char *cleartext, size_t len) {\n    SHA256_CTX ctx;\n    unsigned char hash[SHA256_BLOCK_SIZE];\n    char hex[HASH_PASSWORD_LEN];\n    char *cset = \"0123456789abcdef\";\n\n    sha256_init(&ctx);\n    sha256_update(&ctx,(unsigned char*)cleartext,len);\n    sha256_final(&ctx,hash);\n\n    for (int j = 0; j < SHA256_BLOCK_SIZE; j++) {\n        hex[j*2] = cset[((hash[j]&0xF0)>>4)];\n        hex[j*2+1] = cset[(hash[j]&0xF)];\n    }\n    return sdsnewlen(hex,HASH_PASSWORD_LEN);\n}\n\n/* =============================================================================\n * Low level ACL API\n * ==========================================================================*/\n\n/* Return 1 if the specified string contains spaces or null characters.\n * We do this for usernames and key patterns for simpler rewriting of\n * ACL rules, presentation on ACL list, and to avoid subtle security bugs\n * that may arise from parsing the rules in presence of escapes.\n * The function returns 0 if the string has no spaces. */\nint ACLStringHasSpaces(const char *s, size_t len) {\n    for (size_t i = 0; i < len; i++) {\n        if (isspace(s[i]) || s[i] == 0) return 1;\n    }\n    return 0;\n}\n\n/* Given the category name the command returns the corresponding flag, or\n * zero if there is no match. */\nuint64_t ACLGetCommandCategoryFlagByName(const char *name) {\n    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {\n        if (!strcasecmp(name,ACLCommandCategories[j].name)) {\n            return ACLCommandCategories[j].flag;\n        }\n    }\n    return 0; /* No match. */\n}\n\n/* Method for passwords/pattern comparison used for the user->passwords list\n * so that we can search for items with listSearchKey(). */\nint ACLListMatchSds(void *a, void *b) {\n    return sdscmp(a,b) == 0;\n}\n\n/* Method to free list elements from ACL users password/patterns lists. */\nvoid ACLListFreeSds(void *item) {\n    sdsfree(item);\n}\n\n/* Method to duplicate list elements from ACL users password/patterns lists. */\nvoid *ACLListDupSds(void *item) {\n    return sdsdup(item);\n}\n\n/* Create a new user with the specified name, store it in the list\n * of users (the Users global radix tree), and returns a reference to\n * the structure representing the user.\n *\n * If the user with such name already exists NULL is returned. */\nuser *ACLCreateUser(const char *name, size_t namelen) {\n    if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;\n    user *u = zmalloc(sizeof(*u));\n    u->name = sdsnewlen(name,namelen);\n    u->flags = USER_FLAG_DISABLED;\n    u->allowed_subcommands = NULL;\n    u->passwords = listCreate();\n    u->patterns = listCreate();\n    listSetMatchMethod(u->passwords,ACLListMatchSds);\n    listSetFreeMethod(u->passwords,ACLListFreeSds);\n    listSetDupMethod(u->passwords,ACLListDupSds);\n    listSetMatchMethod(u->patterns,ACLListMatchSds);\n    listSetFreeMethod(u->patterns,ACLListFreeSds);\n    listSetDupMethod(u->patterns,ACLListDupSds);\n    memset(u->allowed_commands,0,sizeof(u->allowed_commands));\n    raxInsert(Users,(unsigned char*)name,namelen,u,NULL);\n    return u;\n}\n\n/* This function should be called when we need an unlinked \"fake\" user\n * we can use in order to validate ACL rules or for other similar reasons.\n * The user will not get linked to the Users radix tree. The returned\n * user should be released with ACLFreeUser() as usually. */\nuser *ACLCreateUnlinkedUser(void) {\n    char username[64];\n    for (int j = 0; ; j++) {\n        snprintf(username,sizeof(username),\"__fakeuser:%d__\",j);\n        user *fakeuser = ACLCreateUser(username,strlen(username));\n        if (fakeuser == NULL) continue;\n        int retval = raxRemove(Users,(unsigned char*) username,\n                               strlen(username),NULL);\n        serverAssert(retval != 0);\n        return fakeuser;\n    }\n}\n\n/* Release the memory used by the user structure. Note that this function\n * will not remove the user from the Users global radix tree. */\nvoid ACLFreeUser(user *u) {\n    sdsfree(u->name);\n    listRelease(u->passwords);\n    listRelease(u->patterns);\n    ACLResetSubcommands(u);\n    zfree(u);\n}\n\n/* When a user is deleted we need to cycle the active\n * connections in order to kill all the pending ones that\n * are authenticated with such user. */\nvoid ACLFreeUserAndKillClients(user *u) {\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client *c = listNodeValue(ln);\n        if (c->user == u) {\n            /* We'll free the conenction asynchronously, so\n             * in theory to set a different user is not needed.\n             * However if there are bugs in Redis, soon or later\n             * this may result in some security hole: it's much\n             * more defensive to set the default user and put\n             * it in non authenticated mode. */\n            c->user = DefaultUser;\n            c->authenticated = 0;\n            freeClientAsync(c);\n        }\n    }\n    ACLFreeUser(u);\n}\n\n/* Copy the user ACL rules from the source user 'src' to the destination\n * user 'dst' so that at the end of the process they'll have exactly the\n * same rules (but the names will continue to be the original ones). */\nvoid ACLCopyUser(user *dst, user *src) {\n    listRelease(dst->passwords);\n    listRelease(dst->patterns);\n    dst->passwords = listDup(src->passwords);\n    dst->patterns = listDup(src->patterns);\n    memcpy(dst->allowed_commands,src->allowed_commands,\n           sizeof(dst->allowed_commands));\n    dst->flags = src->flags;\n    ACLResetSubcommands(dst);\n    /* Copy the allowed subcommands array of array of SDS strings. */\n    if (src->allowed_subcommands) {\n        for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {\n            if (src->allowed_subcommands[j]) {\n                for (int i = 0; src->allowed_subcommands[j][i]; i++)\n                {\n                    ACLAddAllowedSubcommand(dst, j,\n                        src->allowed_subcommands[j][i]);\n                }\n            }\n        }\n    }\n}\n\n/* Free all the users registered in the radix tree 'users' and free the\n * radix tree itself. */\nvoid ACLFreeUsersSet(rax *users) {\n    raxFreeWithCallback(users,(void(*)(void*))ACLFreeUserAndKillClients);\n}\n\n/* Given a command ID, this function set by reference 'word' and 'bit'\n * so that user->allowed_commands[word] will address the right word\n * where the corresponding bit for the provided ID is stored, and\n * so that user->allowed_commands[word]&bit will identify that specific\n * bit. The function returns C_ERR in case the specified ID overflows\n * the bitmap in the user representation. */\nint ACLGetCommandBitCoordinates(uint64_t id, uint64_t *word, uint64_t *bit) {\n    if (id >= USER_COMMAND_BITS_COUNT) return C_ERR;\n    *word = id / sizeof(uint64_t) / 8;\n    *bit = 1ULL << (id % (sizeof(uint64_t) * 8));\n    return C_OK;\n}\n\n/* Check if the specified command bit is set for the specified user.\n * The function returns 1 is the bit is set or 0 if it is not.\n * Note that this function does not check the ALLCOMMANDS flag of the user\n * but just the lowlevel bitmask.\n *\n * If the bit overflows the user internal representation, zero is returned\n * in order to disallow the execution of the command in such edge case. */\nint ACLGetUserCommandBit(user *u, unsigned long id) {\n    uint64_t word, bit;\n    if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0;\n    return (u->allowed_commands[word] & bit) != 0;\n}\n\n/* When +@all or allcommands is given, we set a reserved bit as well that we\n * can later test, to see if the user has the right to execute \"future commands\",\n * that is, commands loaded later via modules. */\nint ACLUserCanExecuteFutureCommands(user *u) {\n    return ACLGetUserCommandBit(u,USER_COMMAND_BITS_COUNT-1);\n}\n\n/* Set the specified command bit for the specified user to 'value' (0 or 1).\n * If the bit overflows the user internal representation, no operation\n * is performed. As a side effect of calling this function with a value of\n * zero, the user flag ALLCOMMANDS is cleared since it is no longer possible\n * to skip the command bit explicit test. */\nvoid ACLSetUserCommandBit(user *u, unsigned long id, int value) {\n    uint64_t word, bit;\n    if (value == 0) u->flags &= ~USER_FLAG_ALLCOMMANDS;\n    if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return;\n    if (value)\n        u->allowed_commands[word] |= bit;\n    else\n        u->allowed_commands[word] &= ~bit;\n}\n\n/* This is like ACLSetUserCommandBit(), but instead of setting the specified\n * ID, it will check all the commands in the category specified as argument,\n * and will set all the bits corresponding to such commands to the specified\n * value. Since the category passed by the user may be non existing, the\n * function returns C_ERR if the category was not found, or C_OK if it was\n * found and the operation was performed. */\nint ACLSetUserCommandBitsForCategory(user *u, const char *category, int value) {\n    uint64_t cflag = ACLGetCommandCategoryFlagByName(category);\n    if (!cflag) return C_ERR;\n    dictIterator *di = dictGetIterator(server.orig_commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        if (cmd->flags & CMD_MODULE) continue; /* Ignore modules commands. */\n        if (cmd->flags & cflag) {\n            ACLSetUserCommandBit(u,cmd->id,value);\n            ACLResetSubcommandsForCommand(u,cmd->id);\n        }\n    }\n    dictReleaseIterator(di);\n    return C_OK;\n}\n\n/* Return the number of commands allowed (on) and denied (off) for the user 'u'\n * in the subset of commands flagged with the specified category name.\n * If the category name is not valid, C_ERR is returned, otherwise C_OK is\n * returned and on and off are populated by reference. */\nint ACLCountCategoryBitsForUser(user *u, unsigned long *on, unsigned long *off,\n                                const char *category)\n{\n    uint64_t cflag = ACLGetCommandCategoryFlagByName(category);\n    if (!cflag) return C_ERR;\n\n    *on = *off = 0;\n    dictIterator *di = dictGetIterator(server.orig_commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        if (cmd->flags & cflag) {\n            if (ACLGetUserCommandBit(u,cmd->id))\n                (*on)++;\n            else\n                (*off)++;\n        }\n    }\n    dictReleaseIterator(di);\n    return C_OK;\n}\n\n/* This function returns an SDS string representing the specified user ACL\n * rules related to command execution, in the same format you could set them\n * back using ACL SETUSER. The function will return just the set of rules needed\n * to recreate the user commands bitmap, without including other user flags such\n * as on/off, passwords and so forth. The returned string always starts with\n * the +@all or -@all rule, depending on the user bitmap, and is followed, if\n * needed, by the other rules needed to narrow or extend what the user can do. */\nsds ACLDescribeUserCommandRules(user *u) {\n    sds rules = sdsempty();\n    int additive;   /* If true we start from -@all and add, otherwise if\n                       false we start from +@all and remove. */\n\n    /* This code is based on a trick: as we generate the rules, we apply\n     * them to a fake user, so that as we go we still know what are the\n     * bit differences we should try to address by emitting more rules. */\n    user fu = {0};\n    user *fakeuser = &fu;\n\n    /* Here we want to understand if we should start with +@all and remove\n     * the commands corresponding to the bits that are not set in the user\n     * commands bitmap, or the contrary. Note that semantically the two are\n     * different. For instance starting with +@all and subtracting, the user\n     * will be able to execute future commands, while -@all and adding will just\n     * allow the user the run the selected commands and/or categories.\n     * How do we test for that? We use the trick of a reserved command ID bit\n     * that is set only by +@all (and its alias \"allcommands\"). */\n    if (ACLUserCanExecuteFutureCommands(u)) {\n        additive = 0;\n        rules = sdscat(rules,\"+@all \");\n        ACLSetUser(fakeuser,\"+@all\",-1);\n    } else {\n        additive = 1;\n        rules = sdscat(rules,\"-@all \");\n        ACLSetUser(fakeuser,\"-@all\",-1);\n    }\n\n    /* Try to add or subtract each category one after the other. Often a\n     * single category will not perfectly match the set of commands into\n     * it, so at the end we do a final pass adding/removing the single commands\n     * needed to make the bitmap exactly match. */\n    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {\n        unsigned long on, off;\n        ACLCountCategoryBitsForUser(u,&on,&off,ACLCommandCategories[j].name);\n        if ((additive && on > off) || (!additive && off > on)) {\n            sds op = sdsnewlen(additive ? \"+@\" : \"-@\", 2);\n            op = sdscat(op,ACLCommandCategories[j].name);\n            ACLSetUser(fakeuser,op,-1);\n            rules = sdscatsds(rules,op);\n            rules = sdscatlen(rules,\" \",1);\n            sdsfree(op);\n        }\n    }\n\n    /* Fix the final ACLs with single commands differences. */\n    dictIterator *di = dictGetIterator(server.orig_commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        int userbit = ACLGetUserCommandBit(u,cmd->id);\n        int fakebit = ACLGetUserCommandBit(fakeuser,cmd->id);\n        if (userbit != fakebit) {\n            rules = sdscatlen(rules, userbit ? \"+\" : \"-\", 1);\n            rules = sdscat(rules,cmd->name);\n            rules = sdscatlen(rules,\" \",1);\n            ACLSetUserCommandBit(fakeuser,cmd->id,userbit);\n        }\n\n        /* Emit the subcommands if there are any. */\n        if (userbit == 0 && u->allowed_subcommands &&\n            u->allowed_subcommands[cmd->id])\n        {\n            for (int j = 0; u->allowed_subcommands[cmd->id][j]; j++) {\n                rules = sdscatlen(rules,\"+\",1);\n                rules = sdscat(rules,cmd->name);\n                rules = sdscatlen(rules,\"|\",1);\n                rules = sdscatsds(rules,u->allowed_subcommands[cmd->id][j]);\n                rules = sdscatlen(rules,\" \",1);\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Trim the final useless space. */\n    sdsrange(rules,0,-2);\n\n    /* This is technically not needed, but we want to verify that now the\n     * predicted bitmap is exactly the same as the user bitmap, and abort\n     * otherwise, because aborting is better than a security risk in this\n     * code path. */\n    if (memcmp(fakeuser->allowed_commands,\n                        u->allowed_commands,\n                        sizeof(u->allowed_commands)) != 0)\n    {\n        serverLog(LL_WARNING,\n            \"CRITICAL ERROR: User ACLs don't match final bitmap: '%s'\",\n            rules);\n        serverPanic(\"No bitmap match in ACLDescribeUserCommandRules()\");\n    }\n    return rules;\n}\n\n/* This is similar to ACLDescribeUserCommandRules(), however instead of\n * describing just the user command rules, everything is described: user\n * flags, keys, passwords and finally the command rules obtained via\n * the ACLDescribeUserCommandRules() function. This is the function we call\n * when we want to rewrite the configuration files describing ACLs and\n * in order to show users with ACL LIST. */\nsds ACLDescribeUser(user *u) {\n    sds res = sdsempty();\n\n    /* Flags. */\n    for (int j = 0; ACLUserFlags[j].flag; j++) {\n        /* Skip the allcommands and allkeys flags because they'll be emitted\n         * later as ~* and +@all. */\n        if (ACLUserFlags[j].flag == USER_FLAG_ALLKEYS ||\n            ACLUserFlags[j].flag == USER_FLAG_ALLCOMMANDS) continue;\n        if (u->flags & ACLUserFlags[j].flag) {\n            res = sdscat(res,ACLUserFlags[j].name);\n            res = sdscatlen(res,\" \",1);\n        }\n    }\n\n    /* Passwords. */\n    listIter li;\n    listNode *ln;\n    listRewind(u->passwords,&li);\n    while((ln = listNext(&li))) {\n        sds thispass = listNodeValue(ln);\n        res = sdscatlen(res,\"#\",1);\n        res = sdscatsds(res,thispass);\n        res = sdscatlen(res,\" \",1);\n    }\n\n    /* Key patterns. */\n    if (u->flags & USER_FLAG_ALLKEYS) {\n        res = sdscatlen(res,\"~* \",3);\n    } else {\n        listRewind(u->patterns,&li);\n        while((ln = listNext(&li))) {\n            sds thispat = listNodeValue(ln);\n            res = sdscatlen(res,\"~\",1);\n            res = sdscatsds(res,thispat);\n            res = sdscatlen(res,\" \",1);\n        }\n    }\n\n    /* Command rules. */\n    sds rules = ACLDescribeUserCommandRules(u);\n    res = sdscatsds(res,rules);\n    sdsfree(rules);\n    return res;\n}\n\n/* Get a command from the original command table, that is not affected\n * by the command renaming operations: we base all the ACL work from that\n * table, so that ACLs are valid regardless of command renaming. */\nstruct redisCommand *ACLLookupCommand(const char *name) {\n    struct redisCommand *cmd;\n    sds sdsname = sdsnew(name);\n    cmd = dictFetchValue(server.orig_commands, sdsname);\n    sdsfree(sdsname);\n    return cmd;\n}\n\n/* Flush the array of allowed subcommands for the specified user\n * and command ID. */\nvoid ACLResetSubcommandsForCommand(user *u, unsigned long id) {\n    if (u->allowed_subcommands && u->allowed_subcommands[id]) {\n        for (int i = 0; u->allowed_subcommands[id][i]; i++)\n            sdsfree(u->allowed_subcommands[id][i]);\n        zfree(u->allowed_subcommands[id]);\n        u->allowed_subcommands[id] = NULL;\n    }\n}\n\n/* Flush the entire table of subcommands. This is useful on +@all, -@all\n * or similar to return back to the minimal memory usage (and checks to do)\n * for the user. */\nvoid ACLResetSubcommands(user *u) {\n    if (u->allowed_subcommands == NULL) return;\n    for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {\n        if (u->allowed_subcommands[j]) {\n            for (int i = 0; u->allowed_subcommands[j][i]; i++)\n                sdsfree(u->allowed_subcommands[j][i]);\n            zfree(u->allowed_subcommands[j]);\n        }\n    }\n    zfree(u->allowed_subcommands);\n    u->allowed_subcommands = NULL;\n}\n\n\n/* Add a subcommand to the list of subcommands for the user 'u' and\n * the command id specified. */\nvoid ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {\n    /* If this is the first subcommand to be configured for\n     * this user, we have to allocate the subcommands array. */\n    if (u->allowed_subcommands == NULL) {\n        u->allowed_subcommands = zcalloc(USER_COMMAND_BITS_COUNT *\n                                 sizeof(sds*));\n    }\n\n    /* We also need to enlarge the allocation pointing to the\n     * null terminated SDS array, to make space for this one.\n     * To start check the current size, and while we are here\n     * make sure the subcommand is not already specified inside. */\n    long items = 0;\n    if (u->allowed_subcommands[id]) {\n        while(u->allowed_subcommands[id][items]) {\n            /* If it's already here do not add it again. */\n            if (!strcasecmp(u->allowed_subcommands[id][items],sub)) return;\n            items++;\n        }\n    }\n\n    /* Now we can make space for the new item (and the null term). */\n    items += 2;\n    u->allowed_subcommands[id] = zrealloc(u->allowed_subcommands[id],\n                                 sizeof(sds)*items);\n    u->allowed_subcommands[id][items-2] = sdsnew(sub);\n    u->allowed_subcommands[id][items-1] = NULL;\n}\n\n/* Set user properties according to the string \"op\". The following\n * is a description of what different strings will do:\n *\n * on           Enable the user: it is possible to authenticate as this user.\n * off          Disable the user: it's no longer possible to authenticate\n *              with this user, however the already authenticated connections\n *              will still work.\n * +<command>   Allow the execution of that command\n * -<command>   Disallow the execution of that command\n * +@<category> Allow the execution of all the commands in such category\n *              with valid categories are like @admin, @set, @sortedset, ...\n *              and so forth, see the full list in the server.c file where\n *              the Redis command table is described and defined.\n *              The special category @all means all the commands, but currently\n *              present in the server, and that will be loaded in the future\n *              via modules.\n * +<command>|subcommand    Allow a specific subcommand of an otherwise\n *                          disabled command. Note that this form is not\n *                          allowed as negative like -DEBUG|SEGFAULT, but\n *                          only additive starting with \"+\".\n * allcommands  Alias for +@all. Note that it implies the ability to execute\n *              all the future commands loaded via the modules system.\n * nocommands   Alias for -@all.\n * ~<pattern>   Add a pattern of keys that can be mentioned as part of\n *              commands. For instance ~* allows all the keys. The pattern\n *              is a glob-style pattern like the one of KEYS.\n *              It is possible to specify multiple patterns.\n * allkeys      Alias for ~*\n * resetkeys    Flush the list of allowed keys patterns.\n * ><password>  Add this password to the list of valid password for the user.\n *              For example >mypass will add \"mypass\" to the list.\n *              This directive clears the \"nopass\" flag (see later).\n * #<hash>      Add this password hash to the list of valid hashes for\n *              the user. This is useful if you have previously computed\n *              the hash, and don't want to store it in plaintext.\n *              This directive clears the \"nopass\" flag (see later).\n * <<password>  Remove this password from the list of valid passwords.\n * !<hash>      Remove this hashed password from the list of valid passwords.\n *              This is useful when you want to remove a password just by\n *              hash without knowing its plaintext version at all.\n * nopass       All the set passwords of the user are removed, and the user\n *              is flagged as requiring no password: it means that every\n *              password will work against this user. If this directive is\n *              used for the default user, every new connection will be\n *              immediately authenticated with the default user without\n *              any explicit AUTH command required. Note that the \"resetpass\"\n *              directive will clear this condition.\n * resetpass    Flush the list of allowed passwords. Moreover removes the\n *              \"nopass\" status. After \"resetpass\" the user has no associated\n *              passwords and there is no way to authenticate without adding\n *              some password (or setting it as \"nopass\" later).\n * reset        Performs the following actions: resetpass, resetkeys, off,\n *              -@all. The user returns to the same state it has immediately\n *              after its creation.\n *\n * The 'op' string must be null terminated. The 'oplen' argument should\n * specify the length of the 'op' string in case the caller requires to pass\n * binary data (for instance the >password form may use a binary password).\n * Otherwise the field can be set to -1 and the function will use strlen()\n * to determine the length.\n *\n * The function returns C_OK if the action to perform was understood because\n * the 'op' string made sense. Otherwise C_ERR is returned if the operation\n * is unknown or has some syntax error.\n *\n * When an error is returned, errno is set to the following values:\n *\n * EINVAL: The specified opcode is not understood or the key pattern is\n *         invalid (contains non allowed characters).\n * ENOENT: The command name or command category provided with + or - is not\n *         known.\n * EBUSY:  The subcommand you want to add is about a command that is currently\n *         fully added.\n * EEXIST: You are adding a key pattern after \"*\" was already added. This is\n *         almost surely an error on the user side.\n * ENODEV: The password you are trying to remove from the user does not exist.\n * EBADMSG: The hash you are trying to add is not a valid hash. \n */\nint ACLSetUser(user *u, const char *op, ssize_t oplen) {\n    if (oplen == -1) oplen = strlen(op);\n    if (!strcasecmp(op,\"on\")) {\n        u->flags |= USER_FLAG_ENABLED;\n        u->flags &= ~USER_FLAG_DISABLED;\n    } else if (!strcasecmp(op,\"off\")) {\n        u->flags |= USER_FLAG_DISABLED;\n        u->flags &= ~USER_FLAG_ENABLED;\n    } else if (!strcasecmp(op,\"allkeys\") ||\n               !strcasecmp(op,\"~*\"))\n    {\n        u->flags |= USER_FLAG_ALLKEYS;\n        listEmpty(u->patterns);\n    } else if (!strcasecmp(op,\"resetkeys\")) {\n        u->flags &= ~USER_FLAG_ALLKEYS;\n        listEmpty(u->patterns);\n    } else if (!strcasecmp(op,\"allcommands\") ||\n               !strcasecmp(op,\"+@all\"))\n    {\n        memset(u->allowed_commands,255,sizeof(u->allowed_commands));\n        u->flags |= USER_FLAG_ALLCOMMANDS;\n        ACLResetSubcommands(u);\n    } else if (!strcasecmp(op,\"nocommands\") ||\n               !strcasecmp(op,\"-@all\"))\n    {\n        memset(u->allowed_commands,0,sizeof(u->allowed_commands));\n        u->flags &= ~USER_FLAG_ALLCOMMANDS;\n        ACLResetSubcommands(u);\n    } else if (!strcasecmp(op,\"nopass\")) {\n        u->flags |= USER_FLAG_NOPASS;\n        listEmpty(u->passwords);\n    } else if (!strcasecmp(op,\"resetpass\")) {\n        u->flags &= ~USER_FLAG_NOPASS;\n        listEmpty(u->passwords);\n    } else if (op[0] == '>' || op[0] == '#') {\n        sds newpass;\n        if (op[0] == '>') {\n            newpass = ACLHashPassword((unsigned char*)op+1,oplen-1);\n        } else {\n            if (oplen != HASH_PASSWORD_LEN + 1) {\n                errno = EBADMSG;\n                return C_ERR;\n            }\n\n            /* Password hashes can only be characters that represent\n             * hexadecimal values, which are numbers and lowercase\n             * characters 'a' through 'f'.\n             */\n            for(int i = 1; i < HASH_PASSWORD_LEN + 1; i++) {\n                char c = op[i];\n                if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {\n                    errno = EBADMSG;\n                    return C_ERR;\n                }\n            }\n            newpass = sdsnewlen(op+1,oplen-1);\n        }\n\n        listNode *ln = listSearchKey(u->passwords,newpass);\n        /* Avoid re-adding the same password multiple times. */\n        if (ln == NULL)\n            listAddNodeTail(u->passwords,newpass);\n        else\n            sdsfree(newpass);\n        u->flags &= ~USER_FLAG_NOPASS;\n    } else if (op[0] == '<' || op[0] == '!') {\n        sds delpass;\n        if (op[0] == '<') {\n            delpass = ACLHashPassword((unsigned char*)op+1,oplen-1);\n        } else {\n            if (oplen != HASH_PASSWORD_LEN + 1) {\n                errno = EBADMSG;\n                return C_ERR;\n            }\n            delpass = sdsnewlen(op+1,oplen-1);\n        }\n        listNode *ln = listSearchKey(u->passwords,delpass);\n        sdsfree(delpass);\n        if (ln) {\n            listDelNode(u->passwords,ln);\n        } else {\n            errno = ENODEV;\n            return C_ERR;\n        }\n    } else if (op[0] == '~') {\n        if (u->flags & USER_FLAG_ALLKEYS) {\n            errno = EEXIST;\n            return C_ERR;\n        }\n        if (ACLStringHasSpaces(op+1,oplen-1)) {\n            errno = EINVAL;\n            return C_ERR;\n        }\n        sds newpat = sdsnewlen(op+1,oplen-1);\n        listNode *ln = listSearchKey(u->patterns,newpat);\n        /* Avoid re-adding the same pattern multiple times. */\n        if (ln == NULL)\n            listAddNodeTail(u->patterns,newpat);\n        else\n            sdsfree(newpat);\n        u->flags &= ~USER_FLAG_ALLKEYS;\n    } else if (op[0] == '+' && op[1] != '@') {\n        if (strchr(op,'|') == NULL) {\n            if (ACLLookupCommand(op+1) == NULL) {\n                errno = ENOENT;\n                return C_ERR;\n            }\n            unsigned long id = ACLGetCommandID(op+1);\n            ACLSetUserCommandBit(u,id,1);\n            ACLResetSubcommandsForCommand(u,id);\n        } else {\n            /* Split the command and subcommand parts. */\n            char *copy = zstrdup(op+1);\n            char *sub = strchr(copy,'|');\n            sub[0] = '\\0';\n            sub++;\n\n            /* Check if the command exists. We can't check the\n             * subcommand to see if it is valid. */\n            if (ACLLookupCommand(copy) == NULL) {\n                zfree(copy);\n                errno = ENOENT;\n                return C_ERR;\n            }\n            unsigned long id = ACLGetCommandID(copy);\n\n            /* The subcommand cannot be empty, so things like DEBUG|\n             * are syntax errors of course. */\n            if (strlen(sub) == 0) {\n                zfree(copy);\n                errno = EINVAL;\n                return C_ERR;\n            }\n\n            /* The command should not be set right now in the command\n             * bitmap, because adding a subcommand of a fully added\n             * command is probably an error on the user side. */\n            if (ACLGetUserCommandBit(u,id) == 1) {\n                zfree(copy);\n                errno = EBUSY;\n                return C_ERR;\n            }\n\n            /* Add the subcommand to the list of valid ones. */\n            ACLAddAllowedSubcommand(u,id,sub);\n\n            /* We have to clear the command bit so that we force the\n             * subcommand check. */\n            ACLSetUserCommandBit(u,id,0);\n            zfree(copy);\n        }\n    } else if (op[0] == '-' && op[1] != '@') {\n        if (ACLLookupCommand(op+1) == NULL) {\n            errno = ENOENT;\n            return C_ERR;\n        }\n        unsigned long id = ACLGetCommandID(op+1);\n        ACLSetUserCommandBit(u,id,0);\n        ACLResetSubcommandsForCommand(u,id);\n    } else if ((op[0] == '+' || op[0] == '-') && op[1] == '@') {\n        int bitval = op[0] == '+' ? 1 : 0;\n        if (ACLSetUserCommandBitsForCategory(u,op+2,bitval) == C_ERR) {\n            errno = ENOENT;\n            return C_ERR;\n        }\n    } else if (!strcasecmp(op,\"reset\")) {\n        serverAssert(ACLSetUser(u,\"resetpass\",-1) == C_OK);\n        serverAssert(ACLSetUser(u,\"resetkeys\",-1) == C_OK);\n        serverAssert(ACLSetUser(u,\"off\",-1) == C_OK);\n        serverAssert(ACLSetUser(u,\"-@all\",-1) == C_OK);\n    } else {\n        errno = EINVAL;\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* Return a description of the error that occurred in ACLSetUser() according to\n * the errno value set by the function on error. */\nchar *ACLSetUserStringError(void) {\n    char *errmsg = \"Wrong format\";\n    if (errno == ENOENT)\n        errmsg = \"Unknown command or category name in ACL\";\n    else if (errno == EINVAL)\n        errmsg = \"Syntax error\";\n    else if (errno == EBUSY)\n        errmsg = \"Adding a subcommand of a command already fully \"\n                 \"added is not allowed. Remove the command to start. \"\n                 \"Example: -DEBUG +DEBUG|DIGEST\";\n    else if (errno == EEXIST)\n        errmsg = \"Adding a pattern after the * pattern (or the \"\n                 \"'allkeys' flag) is not valid and does not have any \"\n                 \"effect. Try 'resetkeys' to start with an empty \"\n                 \"list of patterns\";\n    else if (errno == ENODEV)\n        errmsg = \"The password you are trying to remove from the user does \"\n                 \"not exist\";\n    else if (errno == EBADMSG)\n        errmsg = \"The password hash must be exactly 64 characters and contain \"\n                 \"only lowercase hexadecimal characters\";\n    return errmsg;\n}\n\n/* Initialize the default user, that will always exist for all the process\n * lifetime. */\nvoid ACLInitDefaultUser(void) {\n    DefaultUser = ACLCreateUser(\"default\",7);\n    ACLSetUser(DefaultUser,\"+@all\",-1);\n    ACLSetUser(DefaultUser,\"~*\",-1);\n    ACLSetUser(DefaultUser,\"on\",-1);\n    ACLSetUser(DefaultUser,\"nopass\",-1);\n}\n\n/* Initialization of the ACL subsystem. */\nvoid ACLInit(void) {\n    Users = raxNew();\n    UsersToLoad = listCreate();\n    ACLLog = listCreate();\n    ACLInitDefaultUser();\n    server.requirepass = NULL; /* Only used for backward compatibility. */\n}\n\n/* Check the username and password pair and return C_OK if they are valid,\n * otherwise C_ERR is returned and errno is set to:\n *\n *  EINVAL: if the username-password do not match.\n *  ENONENT: if the specified user does not exist at all.\n */\nint ACLCheckUserCredentials(robj *username, robj *password) {\n    user *u = ACLGetUserByName(username->ptr,sdslen(username->ptr));\n    if (u == NULL) {\n        errno = ENOENT;\n        return C_ERR;\n    }\n\n    /* Disabled users can't login. */\n    if (u->flags & USER_FLAG_DISABLED) {\n        errno = EINVAL;\n        return C_ERR;\n    }\n\n    /* If the user is configured to don't require any password, we\n     * are already fine here. */\n    if (u->flags & USER_FLAG_NOPASS) return C_OK;\n\n    /* Check all the user passwords for at least one to match. */\n    listIter li;\n    listNode *ln;\n    listRewind(u->passwords,&li);\n    sds hashed = ACLHashPassword(password->ptr,sdslen(password->ptr));\n    while((ln = listNext(&li))) {\n        sds thispass = listNodeValue(ln);\n        if (!time_independent_strcmp(hashed, thispass)) {\n            sdsfree(hashed);\n            return C_OK;\n        }\n    }\n    sdsfree(hashed);\n\n    /* If we reached this point, no password matched. */\n    errno = EINVAL;\n    return C_ERR;\n}\n\n/* This is like ACLCheckUserCredentials(), however if the user/pass\n * are correct, the connection is put in authenticated state and the\n * connection user reference is populated.\n *\n * The return value is C_OK or C_ERR with the same meaning as\n * ACLCheckUserCredentials(). */\nint ACLAuthenticateUser(client *c, robj *username, robj *password) {\n    if (ACLCheckUserCredentials(username,password) == C_OK) {\n        c->authenticated = 1;\n        c->user = ACLGetUserByName(username->ptr,sdslen(username->ptr));\n        moduleNotifyUserChanged(c);\n        return C_OK;\n    } else {\n        addACLLogEntry(c,ACL_DENIED_AUTH,0,username->ptr);\n        return C_ERR;\n    }\n}\n\n/* For ACL purposes, every user has a bitmap with the commands that such\n * user is allowed to execute. In order to populate the bitmap, every command\n * should have an assigned ID (that is used to index the bitmap). This function\n * creates such an ID: it uses sequential IDs, reusing the same ID for the same\n * command name, so that a command retains the same ID in case of modules that\n * are unloaded and later reloaded. */\nunsigned long ACLGetCommandID(const char *cmdname) {\n    static rax *map = NULL;\n    static unsigned long nextid = 0;\n\n    sds lowername = sdsnew(cmdname);\n    sdstolower(lowername);\n    if (map == NULL) map = raxNew();\n    void *id = raxFind(map,(unsigned char*)lowername,sdslen(lowername));\n    if (id != raxNotFound) {\n        sdsfree(lowername);\n        return (unsigned long)id;\n    }\n    raxInsert(map,(unsigned char*)lowername,strlen(lowername),\n              (void*)nextid,NULL);\n    sdsfree(lowername);\n    unsigned long thisid = nextid;\n    nextid++;\n\n    /* We never assign the last bit in the user commands bitmap structure,\n     * this way we can later check if this bit is set, understanding if the\n     * current ACL for the user was created starting with a +@all to add all\n     * the possible commands and just subtracting other single commands or\n     * categories, or if, instead, the ACL was created just adding commands\n     * and command categories from scratch, not allowing future commands by\n     * default (loaded via modules). This is useful when rewriting the ACLs\n     * with ACL SAVE. */\n    if (nextid == USER_COMMAND_BITS_COUNT-1) nextid++;\n    return thisid;\n}\n\n/* Return an username by its name, or NULL if the user does not exist. */\nuser *ACLGetUserByName(const char *name, size_t namelen) {\n    void *myuser = raxFind(Users,(unsigned char*)name,namelen);\n    if (myuser == raxNotFound) return NULL;\n    return myuser;\n}\n\n/* Check if the command is ready to be executed in the client 'c', already\n * referenced by c->cmd, and can be executed by this client according to the\n * ACLs associated to the client user c->user.\n *\n * If the user can execute the command ACL_OK is returned, otherwise\n * ACL_DENIED_CMD or ACL_DENIED_KEY is returned: the first in case the\n * command cannot be executed because the user is not allowed to run such\n * command, the second if the command is denied because the user is trying\n * to access keys that are not among the specified patterns. */\nint ACLCheckCommandPerm(client *c, int *keyidxptr) {\n    user *u = c->user;\n    uint64_t id = c->cmd->id;\n\n    /* If there is no associated user, the connection can run anything. */\n    if (u == NULL) return ACL_OK;\n\n    /* Check if the user can execute this command. */\n    if (!(u->flags & USER_FLAG_ALLCOMMANDS) &&\n        c->cmd->proc != authCommand)\n    {\n        /* If the bit is not set we have to check further, in case the\n         * command is allowed just with that specific subcommand. */\n        if (ACLGetUserCommandBit(u,id) == 0) {\n            /* Check if the subcommand matches. */\n            if (c->argc < 2 ||\n                u->allowed_subcommands == NULL ||\n                u->allowed_subcommands[id] == NULL)\n            {\n                return ACL_DENIED_CMD;\n            }\n\n            long subid = 0;\n            while (1) {\n                if (u->allowed_subcommands[id][subid] == NULL)\n                    return ACL_DENIED_CMD;\n                if (!strcasecmp(c->argv[1]->ptr,\n                                u->allowed_subcommands[id][subid]))\n                    break; /* Subcommand match found. Stop here. */\n                subid++;\n            }\n        }\n    }\n\n    /* Check if the user can execute commands explicitly touching the keys\n     * mentioned in the command arguments. */\n    if (!(c->user->flags & USER_FLAG_ALLKEYS) &&\n        (c->cmd->getkeys_proc || c->cmd->firstkey))\n    {\n        int numkeys;\n        int *keyidx = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);\n        for (int j = 0; j < numkeys; j++) {\n            listIter li;\n            listNode *ln;\n            listRewind(u->patterns,&li);\n\n            /* Test this key against every pattern. */\n            int match = 0;\n            while((ln = listNext(&li))) {\n                sds pattern = listNodeValue(ln);\n                size_t plen = sdslen(pattern);\n                int idx = keyidx[j];\n                if (stringmatchlen(pattern,plen,c->argv[idx]->ptr,\n                                   sdslen(c->argv[idx]->ptr),0))\n                {\n                    match = 1;\n                    break;\n                }\n            }\n            if (!match) {\n                if (keyidxptr) *keyidxptr = keyidx[j];\n                getKeysFreeResult(keyidx);\n                return ACL_DENIED_KEY;\n            }\n        }\n        getKeysFreeResult(keyidx);\n    }\n\n    /* If we survived all the above checks, the user can execute the\n     * command. */\n    return ACL_OK;\n}\n\n/* =============================================================================\n * ACL loading / saving functions\n * ==========================================================================*/\n\n/* Given an argument vector describing a user in the form:\n *\n *      user <username> ... ACL rules and flags ...\n *\n * this function validates, and if the syntax is valid, appends\n * the user definition to a list for later loading.\n *\n * The rules are tested for validity and if there obvious syntax errors\n * the function returns C_ERR and does nothing, otherwise C_OK is returned\n * and the user is appended to the list.\n *\n * Note that this function cannot stop in case of commands that are not found\n * and, in that case, the error will be emitted later, because certain\n * commands may be defined later once modules are loaded.\n *\n * When an error is detected and C_ERR is returned, the function populates\n * by reference (if not set to NULL) the argc_err argument with the index\n * of the argv vector that caused the error. */\nint ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {\n    if (argc < 2 || strcasecmp(argv[0],\"user\")) {\n        if (argc_err) *argc_err = 0;\n        return C_ERR;\n    }\n\n    /* Try to apply the user rules in a fake user to see if they\n     * are actually valid. */\n    user *fakeuser = ACLCreateUnlinkedUser();\n\n    for (int j = 2; j < argc; j++) {\n        if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) == C_ERR) {\n            if (errno != ENOENT) {\n                ACLFreeUser(fakeuser);\n                if (argc_err) *argc_err = j;\n                return C_ERR;\n            }\n        }\n    }\n\n    /* Rules look valid, let's append the user to the list. */\n    sds *copy = zmalloc(sizeof(sds)*argc);\n    for (int j = 1; j < argc; j++) copy[j-1] = sdsdup(argv[j]);\n    copy[argc-1] = NULL;\n    listAddNodeTail(UsersToLoad,copy);\n    ACLFreeUser(fakeuser);\n    return C_OK;\n}\n\n/* This function will load the configured users appended to the server\n * configuration via ACLAppendUserForLoading(). On loading errors it will\n * log an error and return C_ERR, otherwise C_OK will be returned. */\nint ACLLoadConfiguredUsers(void) {\n    listIter li;\n    listNode *ln;\n    listRewind(UsersToLoad,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        sds *aclrules = listNodeValue(ln);\n        sds username = aclrules[0];\n\n        if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) {\n            serverLog(LL_WARNING,\"Spaces not allowed in ACL usernames\");\n            return C_ERR;\n        }\n\n        user *u = ACLCreateUser(username,sdslen(username));\n        if (!u) {\n            u = ACLGetUserByName(username,sdslen(username));\n            serverAssert(u != NULL);\n            ACLSetUser(u,\"reset\",-1);\n        }\n\n        /* Load every rule defined for this user. */\n        for (int j = 1; aclrules[j]; j++) {\n            if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {\n                char *errmsg = ACLSetUserStringError();\n                serverLog(LL_WARNING,\"Error loading ACL rule '%s' for \"\n                                     \"the user named '%s': %s\",\n                          aclrules[j],aclrules[0],errmsg);\n                return C_ERR;\n            }\n        }\n\n        /* Having a disabled user in the configuration may be an error,\n         * warn about it without returning any error to the caller. */\n        if (u->flags & USER_FLAG_DISABLED) {\n            serverLog(LL_NOTICE, \"The user '%s' is disabled (there is no \"\n                                 \"'on' modifier in the user description). Make \"\n                                 \"sure this is not a configuration error.\",\n                      aclrules[0]);\n        }\n    }\n    return C_OK;\n}\n\n/* This function loads the ACL from the specified filename: every line\n * is validated and should be either empty or in the format used to specify\n * users in the redis.conf configuration or in the ACL file, that is:\n *\n *  user <username> ... rules ...\n *\n * Note that this function considers comments starting with '#' as errors\n * because the ACL file is meant to be rewritten, and comments would be\n * lost after the rewrite. Yet empty lines are allowed to avoid being too\n * strict.\n *\n * One important part of implementing ACL LOAD, that uses this function, is\n * to avoid ending with broken rules if the ACL file is invalid for some\n * reason, so the function will attempt to validate the rules before loading\n * each user. For every line that will be found broken the function will\n * collect an error message.\n *\n * IMPORTANT: If there is at least a single error, nothing will be loaded\n * and the rules will remain exactly as they were.\n *\n * At the end of the process, if no errors were found in the whole file then\n * NULL is returned. Otherwise an SDS string describing in a single line\n * a description of all the issues found is returned. */\nsds ACLLoadFromFile(const char *filename) {\n    FILE *fp;\n    char buf[1024];\n\n    /* Open the ACL file. */\n    if ((fp = fopen(filename,\"r\")) == NULL) {\n        sds errors = sdscatprintf(sdsempty(),\n            \"Error loading ACLs, opening file '%s': %s\",\n            filename, strerror(errno));\n        return errors;\n    }\n\n    /* Load the whole file as a single string in memory. */\n    sds acls = sdsempty();\n    while(fgets(buf,sizeof(buf),fp) != NULL)\n        acls = sdscat(acls,buf);\n    fclose(fp);\n\n    /* Split the file into lines and attempt to load each line. */\n    int totlines;\n    sds *lines, errors = sdsempty();\n    lines = sdssplitlen(acls,strlen(acls),\"\\n\",1,&totlines);\n    sdsfree(acls);\n\n    /* We need a fake user to validate the rules before making changes\n     * to the real user mentioned in the ACL line. */\n    user *fakeuser = ACLCreateUnlinkedUser();\n\n    /* We do all the loading in a fresh instance of the Users radix tree,\n     * so if there are errors loading the ACL file we can rollback to the\n     * old version. */\n    rax *old_users = Users;\n    user *old_default_user = DefaultUser;\n    Users = raxNew();\n    ACLInitDefaultUser();\n\n    /* Load each line of the file. */\n    for (int i = 0; i < totlines; i++) {\n        sds *argv;\n        int argc;\n        int linenum = i+1;\n\n        lines[i] = sdstrim(lines[i],\" \\t\\r\\n\");\n\n        /* Skip blank lines */\n        if (lines[i][0] == '\\0') continue;\n\n        /* Split into arguments */\n        argv = sdssplitargs(lines[i],&argc);\n        if (argv == NULL) {\n            errors = sdscatprintf(errors,\n                     \"%s:%d: unbalanced quotes in acl line. \",\n                     server.acl_filename, linenum);\n            continue;\n        }\n\n        /* Skip this line if the resulting command vector is empty. */\n        if (argc == 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* The line should start with the \"user\" keyword. */\n        if (strcmp(argv[0],\"user\") || argc < 2) {\n            errors = sdscatprintf(errors,\n                     \"%s:%d should start with user keyword followed \"\n                     \"by the username. \", server.acl_filename,\n                     linenum);\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* Spaces are not allowed in usernames. */\n        if (ACLStringHasSpaces(argv[1],sdslen(argv[1]))) {\n            errors = sdscatprintf(errors,\n                     \"'%s:%d: username '%s' contains invalid characters. \",\n                     server.acl_filename, linenum, argv[1]);\n            continue;\n        }\n\n        /* Try to process the line using the fake user to validate iif\n         * the rules are able to apply cleanly. */\n        ACLSetUser(fakeuser,\"reset\",-1);\n        int j;\n        for (j = 2; j < argc; j++) {\n            if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) != C_OK) {\n                char *errmsg = ACLSetUserStringError();\n                errors = sdscatprintf(errors,\n                         \"%s:%d: %s. \",\n                         server.acl_filename, linenum, errmsg);\n                continue;\n            }\n        }\n\n        /* Apply the rule to the new users set only if so far there\n         * are no errors, otherwise it's useless since we are going\n         * to discard the new users set anyway. */\n        if (sdslen(errors) != 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* We can finally lookup the user and apply the rule. If the\n         * user already exists we always reset it to start. */\n        user *u = ACLCreateUser(argv[1],sdslen(argv[1]));\n        if (!u) {\n            u = ACLGetUserByName(argv[1],sdslen(argv[1]));\n            serverAssert(u != NULL);\n            ACLSetUser(u,\"reset\",-1);\n        }\n\n        /* Note that the same rules already applied to the fake user, so\n         * we just assert that everything goes well: it should. */\n        for (j = 2; j < argc; j++)\n            serverAssert(ACLSetUser(u,argv[j],sdslen(argv[j])) == C_OK);\n\n        sdsfreesplitres(argv,argc);\n    }\n\n    ACLFreeUser(fakeuser);\n    sdsfreesplitres(lines,totlines);\n    DefaultUser = old_default_user; /* This pointer must never change. */\n\n    /* Check if we found errors and react accordingly. */\n    if (sdslen(errors) == 0) {\n        /* The default user pointer is referenced in different places: instead\n         * of replacing such occurrences it is much simpler to copy the new\n         * default user configuration in the old one. */\n        user *new = ACLGetUserByName(\"default\",7);\n        serverAssert(new != NULL);\n        ACLCopyUser(DefaultUser,new);\n        ACLFreeUser(new);\n        raxInsert(Users,(unsigned char*)\"default\",7,DefaultUser,NULL);\n        raxRemove(old_users,(unsigned char*)\"default\",7,NULL);\n        ACLFreeUsersSet(old_users);\n        sdsfree(errors);\n        return NULL;\n    } else {\n        ACLFreeUsersSet(Users);\n        Users = old_users;\n        errors = sdscat(errors,\"WARNING: ACL errors detected, no change to the previously active ACL rules was performed\");\n        return errors;\n    }\n}\n\n/* Generate a copy of the ACLs currently in memory in the specified filename.\n * Returns C_OK on success or C_ERR if there was an error during the I/O.\n * When C_ERR is returned a log is produced with hints about the issue. */\nint ACLSaveToFile(const char *filename) {\n    sds acl = sdsempty();\n    int fd = -1;\n    sds tmpfilename = NULL;\n    int retval = C_ERR;\n\n    /* Let's generate an SDS string containing the new version of the\n     * ACL file. */\n    raxIterator ri;\n    raxStart(&ri,Users);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        user *u = ri.data;\n        /* Return information in the configuration file format. */\n        sds user = sdsnew(\"user \");\n        user = sdscatsds(user,u->name);\n        user = sdscatlen(user,\" \",1);\n        sds descr = ACLDescribeUser(u);\n        user = sdscatsds(user,descr);\n        sdsfree(descr);\n        acl = sdscatsds(acl,user);\n        acl = sdscatlen(acl,\"\\n\",1);\n        sdsfree(user);\n    }\n    raxStop(&ri);\n\n    /* Create a temp file with the new content. */\n    tmpfilename = sdsnew(filename);\n    tmpfilename = sdscatfmt(tmpfilename,\".tmp-%i-%I\",\n        (int)getpid(),(int)mstime());\n    if ((fd = open(tmpfilename,O_WRONLY|O_CREAT,0644)) == -1) {\n        serverLog(LL_WARNING,\"Opening temp ACL file for ACL SAVE: %s\",\n            strerror(errno));\n        goto cleanup;\n    }\n\n    /* Write it. */\n    if (write(fd,acl,sdslen(acl)) != (ssize_t)sdslen(acl)) {\n        serverLog(LL_WARNING,\"Writing ACL file for ACL SAVE: %s\",\n            strerror(errno));\n        goto cleanup;\n    }\n    close(fd); fd = -1;\n\n    /* Let's replace the new file with the old one. */\n    if (rename(tmpfilename,filename) == -1) {\n        serverLog(LL_WARNING,\"Renaming ACL file for ACL SAVE: %s\",\n            strerror(errno));\n        goto cleanup;\n    }\n    sdsfree(tmpfilename); tmpfilename = NULL;\n    retval = C_OK; /* If we reached this point, everything is fine. */\n\ncleanup:\n    if (fd != -1) close(fd);\n    if (tmpfilename) unlink(tmpfilename);\n    sdsfree(tmpfilename);\n    sdsfree(acl);\n    return retval;\n}\n\n/* This function is called once the server is already running, modules are\n * loaded, and we are ready to start, in order to load the ACLs either from\n * the pending list of users defined in redis.conf, or from the ACL file.\n * The function will just exit with an error if the user is trying to mix\n * both the loading methods. */\nvoid ACLLoadUsersAtStartup(void) {\n    if (server.acl_filename[0] != '\\0' && listLength(UsersToLoad) != 0) {\n        serverLog(LL_WARNING,\n            \"Configuring Redis with users defined in redis.conf and at \"\n            \"the same setting an ACL file path is invalid. This setup \"\n            \"is very likely to lead to configuration errors and security \"\n            \"holes, please define either an ACL file or declare users \"\n            \"directly in your redis.conf, but not both.\");\n        exit(1);\n    }\n\n    if (ACLLoadConfiguredUsers() == C_ERR) {\n        serverLog(LL_WARNING,\n            \"Critical error while loading ACLs. Exiting.\");\n        exit(1);\n    }\n\n    if (server.acl_filename[0] != '\\0') {\n        sds errors = ACLLoadFromFile(server.acl_filename);\n        if (errors) {\n            serverLog(LL_WARNING,\n                \"Aborting Redis startup because of ACL errors: %s\", errors);\n            sdsfree(errors);\n            exit(1);\n        }\n    }\n}\n\n/* =============================================================================\n * ACL log\n * ==========================================================================*/\n\n#define ACL_LOG_CTX_TOPLEVEL 0\n#define ACL_LOG_CTX_LUA 1\n#define ACL_LOG_CTX_MULTI 2\n#define ACL_LOG_GROUPING_MAX_TIME_DELTA 60000\n\n/* This structure defines an entry inside the ACL log. */\ntypedef struct ACLLogEntry {\n    uint64_t count;     /* Number of times this happened recently. */\n    int reason;         /* Reason for denying the command. ACL_DENIED_*. */\n    int context;        /* Toplevel, Lua or MULTI/EXEC? ACL_LOG_CTX_*. */\n    sds object;         /* The key name or command name. */\n    sds username;       /* User the client is authenticated with. */\n    mstime_t ctime;     /* Milliseconds time of last update to this entry. */\n    sds cinfo;          /* Client info (last client if updated). */\n} ACLLogEntry;\n\n/* This function will check if ACL entries 'a' and 'b' are similar enough\n * that we should actually update the existing entry in our ACL log instead\n * of creating a new one. */\nint ACLLogMatchEntry(ACLLogEntry *a, ACLLogEntry *b) {\n    if (a->reason != b->reason) return 0;\n    if (a->context != b->context) return 0;\n    mstime_t delta = a->ctime - b->ctime;\n    if (delta < 0) delta = -delta;\n    if (delta > ACL_LOG_GROUPING_MAX_TIME_DELTA) return 0;\n    if (sdscmp(a->object,b->object) != 0) return 0;\n    if (sdscmp(a->username,b->username) != 0) return 0;\n    return 1;\n}\n\n/* Release an ACL log entry. */\nvoid ACLFreeLogEntry(void *leptr) {\n    ACLLogEntry *le = leptr;\n    sdsfree(le->object);\n    sdsfree(le->username);\n    sdsfree(le->cinfo);\n    zfree(le);\n}\n\n/* Adds a new entry in the ACL log, making sure to delete the old entry\n * if we reach the maximum length allowed for the log. This function attempts\n * to find similar entries in the current log in order to bump the counter of\n * the log entry instead of creating many entries for very similar ACL\n * rules issues.\n *\n * The keypos argument is only used when the reason is ACL_DENIED_KEY, since\n * it allows the function to log the key name that caused the problem.\n * Similarly the username is only passed when we failed to authenticate the\n * user with AUTH or HELLO, for the ACL_DENIED_AUTH reason. Otherwise\n * it will just be NULL.\n */\nvoid addACLLogEntry(client *c, int reason, int keypos, sds username) {\n    /* Create a new entry. */\n    struct ACLLogEntry *le = zmalloc(sizeof(*le));\n    le->count = 1;\n    le->reason = reason;\n    le->username = sdsdup(reason == ACL_DENIED_AUTH ? username : c->user->name);\n    le->ctime = mstime();\n\n    switch(reason) {\n    case ACL_DENIED_CMD: le->object = sdsnew(c->cmd->name); break;\n    case ACL_DENIED_KEY: le->object = sdsnew(c->argv[keypos]->ptr); break;\n    case ACL_DENIED_AUTH: le->object = sdsnew(c->argv[0]->ptr); break;\n    default: le->object = sdsempty();\n    }\n\n    client *realclient = c;\n    if (realclient->flags & CLIENT_LUA) realclient = server.lua_caller;\n\n    le->cinfo = catClientInfoString(sdsempty(),realclient);\n    if (c->flags & CLIENT_MULTI) {\n        le->context = ACL_LOG_CTX_MULTI;\n    } else if (c->flags & CLIENT_LUA) {\n        le->context = ACL_LOG_CTX_LUA;\n    } else {\n        le->context = ACL_LOG_CTX_TOPLEVEL;\n    }\n\n    /* Try to match this entry with past ones, to see if we can just\n     * update an existing entry instead of creating a new one. */\n    long toscan = 10; /* Do a limited work trying to find duplicated. */\n    listIter li;\n    listNode *ln;\n    listRewind(ACLLog,&li);\n    ACLLogEntry *match = NULL;\n    while (toscan-- && (ln = listNext(&li)) != NULL) {\n        ACLLogEntry *current = listNodeValue(ln);\n        if (ACLLogMatchEntry(current,le)) {\n            match = current;\n            listDelNode(ACLLog,ln);\n            listAddNodeHead(ACLLog,current);\n            break;\n        }\n    }\n\n    /* If there is a match update the entry, otherwise add it as a\n     * new one. */\n    if (match) {\n        /* We update a few fields of the existing entry and bump the\n         * counter of events for this entry. */\n        sdsfree(match->cinfo);\n        match->cinfo = le->cinfo;\n        match->ctime = le->ctime;\n        match->count++;\n\n        /* Release the old entry. */\n        le->cinfo = NULL;\n        ACLFreeLogEntry(le);\n    } else {\n        /* Add it to our list of entires. We'll have to trim the list\n         * to its maximum size. */\n        listAddNodeHead(ACLLog, le);\n        while(listLength(ACLLog) > server.acllog_max_len) {\n            listNode *ln = listLast(ACLLog);\n            ACLLogEntry *le = listNodeValue(ln);\n            ACLFreeLogEntry(le);\n            listDelNode(ACLLog,ln);\n        }\n    }\n}\n\n/* =============================================================================\n * ACL related commands\n * ==========================================================================*/\n\n/* ACL -- show and modify the configuration of ACL users.\n * ACL HELP\n * ACL LOAD\n * ACL SAVE\n * ACL LIST\n * ACL USERS\n * ACL CAT [<category>]\n * ACL SETUSER <username> ... acl rules ...\n * ACL DELUSER <username> [...]\n * ACL GETUSER <username>\n * ACL GENPASS [<bits>]\n * ACL WHOAMI\n * ACL LOG [<count> | RESET]\n */\nvoid aclCommand(client *c) {\n    char *sub = c->argv[1]->ptr;\n    if (!strcasecmp(sub,\"setuser\") && c->argc >= 3) {\n        sds username = c->argv[2]->ptr;\n        /* Check username validity. */\n        if (ACLStringHasSpaces(username,sdslen(username))) {\n            addReplyErrorFormat(c,\n                \"Usernames can't contain spaces or null characters\");\n            return;\n        }\n\n        /* Create a temporary user to validate and stage all changes against\n         * before applying to an existing user or creating a new user. If all\n         * arguments are valid the user parameters will all be applied together.\n         * If there are any errors then none of the changes will be applied. */\n        user *tempu = ACLCreateUnlinkedUser();\n        user *u = ACLGetUserByName(username,sdslen(username));\n        if (u) ACLCopyUser(tempu, u);\n\n        for (int j = 3; j < c->argc; j++) {\n            if (ACLSetUser(tempu,c->argv[j]->ptr,sdslen(c->argv[j]->ptr)) != C_OK) {\n                char *errmsg = ACLSetUserStringError();\n                addReplyErrorFormat(c,\n                    \"Error in ACL SETUSER modifier '%s': %s\",\n                    (char*)c->argv[j]->ptr, errmsg);\n\n                ACLFreeUser(tempu);\n                return;\n            }\n        }\n\n        /* Overwrite the user with the temporary user we modified above. */\n        if (!u) u = ACLCreateUser(username,sdslen(username));\n        serverAssert(u != NULL);\n        ACLCopyUser(u, tempu);\n        ACLFreeUser(tempu);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(sub,\"deluser\") && c->argc >= 3) {\n        int deleted = 0;\n        for (int j = 2; j < c->argc; j++) {\n            sds username = c->argv[j]->ptr;\n            if (!strcmp(username,\"default\")) {\n                addReplyError(c,\"The 'default' user cannot be removed\");\n                return;\n            }\n        }\n\n        for (int j = 2; j < c->argc; j++) {\n            sds username = c->argv[j]->ptr;\n            user *u;\n            if (raxRemove(Users,(unsigned char*)username,\n                          sdslen(username),\n                          (void**)&u))\n            {\n                ACLFreeUserAndKillClients(u);\n                deleted++;\n            }\n        }\n        addReplyLongLong(c,deleted);\n    } else if (!strcasecmp(sub,\"getuser\") && c->argc == 3) {\n        user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));\n        if (u == NULL) {\n            addReplyNull(c);\n            return;\n        }\n\n        addReplyMapLen(c,4);\n\n        /* Flags */\n        addReplyBulkCString(c,\"flags\");\n        void *deflen = addReplyDeferredLen(c);\n        int numflags = 0;\n        for (int j = 0; ACLUserFlags[j].flag; j++) {\n            if (u->flags & ACLUserFlags[j].flag) {\n                addReplyBulkCString(c,ACLUserFlags[j].name);\n                numflags++;\n            }\n        }\n        setDeferredSetLen(c,deflen,numflags);\n\n        /* Passwords */\n        addReplyBulkCString(c,\"passwords\");\n        addReplyArrayLen(c,listLength(u->passwords));\n        listIter li;\n        listNode *ln;\n        listRewind(u->passwords,&li);\n        while((ln = listNext(&li))) {\n            sds thispass = listNodeValue(ln);\n            addReplyBulkCBuffer(c,thispass,sdslen(thispass));\n        }\n\n        /* Commands */\n        addReplyBulkCString(c,\"commands\");\n        sds cmddescr = ACLDescribeUserCommandRules(u);\n        addReplyBulkSds(c,cmddescr);\n\n        /* Key patterns */\n        addReplyBulkCString(c,\"keys\");\n        if (u->flags & USER_FLAG_ALLKEYS) {\n            addReplyArrayLen(c,1);\n            addReplyBulkCBuffer(c,\"*\",1);\n        } else {\n            addReplyArrayLen(c,listLength(u->patterns));\n            listIter li;\n            listNode *ln;\n            listRewind(u->patterns,&li);\n            while((ln = listNext(&li))) {\n                sds thispat = listNodeValue(ln);\n                addReplyBulkCBuffer(c,thispat,sdslen(thispat));\n            }\n        }\n    } else if ((!strcasecmp(sub,\"list\") || !strcasecmp(sub,\"users\")) &&\n               c->argc == 2)\n    {\n        int justnames = !strcasecmp(sub,\"users\");\n        addReplyArrayLen(c,raxSize(Users));\n        raxIterator ri;\n        raxStart(&ri,Users);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            user *u = ri.data;\n            if (justnames) {\n                addReplyBulkCBuffer(c,u->name,sdslen(u->name));\n            } else {\n                /* Return information in the configuration file format. */\n                sds config = sdsnew(\"user \");\n                config = sdscatsds(config,u->name);\n                config = sdscatlen(config,\" \",1);\n                sds descr = ACLDescribeUser(u);\n                config = sdscatsds(config,descr);\n                sdsfree(descr);\n                addReplyBulkSds(c,config);\n            }\n        }\n        raxStop(&ri);\n    } else if (!strcasecmp(sub,\"whoami\") && c->argc == 2) {\n        if (c->user != NULL) {\n            addReplyBulkCBuffer(c,c->user->name,sdslen(c->user->name));\n        } else {\n            addReplyNull(c);\n        }\n    } else if (server.acl_filename[0] == '\\0' &&\n               (!strcasecmp(sub,\"load\") || !strcasecmp(sub,\"save\")))\n    {\n        addReplyError(c,\"This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.\");\n        return;\n    } else if (!strcasecmp(sub,\"load\") && c->argc == 2) {\n        sds errors = ACLLoadFromFile(server.acl_filename);\n        if (errors == NULL) {\n            addReply(c,shared.ok);\n        } else {\n            addReplyError(c,errors);\n            sdsfree(errors);\n        }\n    } else if (!strcasecmp(sub,\"save\") && c->argc == 2) {\n        if (ACLSaveToFile(server.acl_filename) == C_OK) {\n            addReply(c,shared.ok);\n        } else {\n            addReplyError(c,\"There was an error trying to save the ACLs. \"\n                            \"Please check the server logs for more \"\n                            \"information\");\n        }\n    } else if (!strcasecmp(sub,\"cat\") && c->argc == 2) {\n        void *dl = addReplyDeferredLen(c);\n        int j;\n        for (j = 0; ACLCommandCategories[j].flag != 0; j++)\n            addReplyBulkCString(c,ACLCommandCategories[j].name);\n        setDeferredArrayLen(c,dl,j);\n    } else if (!strcasecmp(sub,\"cat\") && c->argc == 3) {\n        uint64_t cflag = ACLGetCommandCategoryFlagByName(c->argv[2]->ptr);\n        if (cflag == 0) {\n            addReplyErrorFormat(c, \"Unknown category '%s'\", (char*)c->argv[2]->ptr);\n            return;\n        }\n        int arraylen = 0;\n        void *dl = addReplyDeferredLen(c);\n        dictIterator *di = dictGetIterator(server.orig_commands);\n        dictEntry *de;\n        while ((de = dictNext(di)) != NULL) {\n            struct redisCommand *cmd = dictGetVal(de);\n            if (cmd->flags & CMD_MODULE) continue;\n            if (cmd->flags & cflag) {\n                addReplyBulkCString(c,cmd->name);\n                arraylen++;\n            }\n        }\n        dictReleaseIterator(di);\n        setDeferredArrayLen(c,dl,arraylen);\n    } else if (!strcasecmp(sub,\"genpass\") && (c->argc == 2 || c->argc == 3)) {\n        #define GENPASS_MAX_BITS 4096\n        char pass[GENPASS_MAX_BITS/8*2]; /* Hex representation. */\n        long bits = 256; /* By default generate 256 bits passwords. */\n\n        if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&bits,NULL)\n            != C_OK) return;\n\n        if (bits <= 0 || bits > GENPASS_MAX_BITS) {\n            addReplyErrorFormat(c,\n                \"ACL GENPASS argument must be the number of \"\n                \"bits for the output password, a positive number \"\n                \"up to %d\",GENPASS_MAX_BITS);\n            return;\n        }\n\n        long chars = (bits+3)/4; /* Round to number of characters to emit. */\n        getRandomHexChars(pass,chars);\n        addReplyBulkCBuffer(c,pass,chars);\n    } else if (!strcasecmp(sub,\"log\") && (c->argc == 2 || c->argc ==3)) {\n        long count = 10; /* Number of entries to emit by default. */\n\n        /* Parse the only argument that LOG may have: it could be either\n         * the number of entries the user wants to display, or alternatively\n         * the \"RESET\" command in order to flush the old entries. */\n        if (c->argc == 3) {\n            if (!strcasecmp(c->argv[2]->ptr,\"reset\")) {\n                listSetFreeMethod(ACLLog,ACLFreeLogEntry);\n                listEmpty(ACLLog);\n                listSetFreeMethod(ACLLog,NULL);\n                addReply(c,shared.ok);\n                return;\n            } else if (getLongFromObjectOrReply(c,c->argv[2],&count,NULL)\n                       != C_OK)\n            {\n                return;\n            }\n            if (count < 0) count = 0;\n        }\n\n        /* Fix the count according to the number of entries we got. */\n        if ((size_t)count > listLength(ACLLog))\n            count = listLength(ACLLog);\n\n        addReplyArrayLen(c,count);\n        listIter li;\n        listNode *ln;\n        listRewind(ACLLog,&li);\n        mstime_t now = mstime();\n        while (count-- && (ln = listNext(&li)) != NULL) {\n            ACLLogEntry *le = listNodeValue(ln);\n            addReplyMapLen(c,7);\n            addReplyBulkCString(c,\"count\");\n            addReplyLongLong(c,le->count);\n\n            addReplyBulkCString(c,\"reason\");\n            char *reasonstr;\n            switch(le->reason) {\n            case ACL_DENIED_CMD: reasonstr=\"command\"; break;\n            case ACL_DENIED_KEY: reasonstr=\"key\"; break;\n            case ACL_DENIED_AUTH: reasonstr=\"auth\"; break;\n            default: reasonstr=\"unknown\";\n            }\n            addReplyBulkCString(c,reasonstr);\n\n            addReplyBulkCString(c,\"context\");\n            char *ctxstr;\n            switch(le->context) {\n            case ACL_LOG_CTX_TOPLEVEL: ctxstr=\"toplevel\"; break;\n            case ACL_LOG_CTX_MULTI: ctxstr=\"multi\"; break;\n            case ACL_LOG_CTX_LUA: ctxstr=\"lua\"; break;\n            default: ctxstr=\"unknown\";\n            }\n            addReplyBulkCString(c,ctxstr);\n\n            addReplyBulkCString(c,\"object\");\n            addReplyBulkCBuffer(c,le->object,sdslen(le->object));\n            addReplyBulkCString(c,\"username\");\n            addReplyBulkCBuffer(c,le->username,sdslen(le->username));\n            addReplyBulkCString(c,\"age-seconds\");\n            double age = (double)(now - le->ctime)/1000;\n            addReplyDouble(c,age);\n            addReplyBulkCString(c,\"client-info\");\n            addReplyBulkCBuffer(c,le->cinfo,sdslen(le->cinfo));\n        }\n    } else if (!strcasecmp(sub,\"help\")) {\n        const char *help[] = {\n\"LOAD                             -- Reload users from the ACL file.\",\n\"SAVE                             -- Save the current config to the ACL file.\",\n\"LIST                             -- Show user details in config file format.\",\n\"USERS                            -- List all the registered usernames.\",\n\"SETUSER <username> [attribs ...] -- Create or modify a user.\",\n\"GETUSER <username>               -- Get the user details.\",\n\"DELUSER <username> [...]         -- Delete a list of users.\",\n\"CAT                              -- List available categories.\",\n\"CAT <category>                   -- List commands inside category.\",\n\"GENPASS [<bits>]                 -- Generate a secure user password.\",\n\"WHOAMI                           -- Return the current connection username.\",\n\"LOG [<count> | RESET]            -- Show the ACL log entries.\",\nNULL\n        };\n        addReplyHelp(c,help);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\nvoid addReplyCommandCategories(client *c, struct redisCommand *cmd) {\n    int flagcount = 0;\n    void *flaglen = addReplyDeferredLen(c);\n    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {\n        if (cmd->flags & ACLCommandCategories[j].flag) {\n            addReplyStatusFormat(c, \"@%s\", ACLCommandCategories[j].name);\n            flagcount++;\n        }\n    }\n    setDeferredSetLen(c, flaglen, flagcount);\n}\n\n/* AUTH <password>\n * AUTH <username> <password> (Redis >= 6.0 form)\n *\n * When the user is omitted it means that we are trying to authenticate\n * against the default user. */\nvoid authCommand(client *c) {\n    /* Only two or three argument forms are allowed. */\n    if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Handle the two different forms here. The form with two arguments\n     * will just use \"default\" as username. */\n    robj *username, *password;\n    if (c->argc == 2) {\n        /* Mimic the old behavior of giving an error for the two commands\n         * from if no password is configured. */\n        if (DefaultUser->flags & USER_FLAG_NOPASS) {\n            addReplyError(c,\"AUTH <password> called without any password \"\n                            \"configured for the default user. Are you sure \"\n                            \"your configuration is correct?\");\n            return;\n        }\n\n        username = createStringObject(\"default\",7);\n        password = c->argv[1];\n    } else {\n        username = c->argv[1];\n        password = c->argv[2];\n    }\n\n    if (ACLAuthenticateUser(c,username,password) == C_OK) {\n        addReply(c,shared.ok);\n    } else {\n        addReplyError(c,\"-WRONGPASS invalid username-password pair\");\n    }\n\n    /* Free the \"default\" string object we created for the two\n     * arguments form. */\n    if (c->argc == 2) decrRefCount(username);\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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. */\nlist *listCreate(void)\n{\n    struct list *list;\n\n    if ((list = zmalloc(sizeof(*list))) == NULL)\n        return NULL;\n    list->head = list->tail = NULL;\n    list->len = 0;\n    list->dup = NULL;\n    list->free = NULL;\n    list->match = NULL;\n    return list;\n}\n\n/* Remove all the elements from the list without destroying the list itself. */\nvoid listEmpty(list *list)\n{\n    unsigned long len;\n    listNode *current, *next;\n\n    current = list->head;\n    len = list->len;\n    while(len--) {\n        next = current->next;\n        if (list->free) list->free(current->value);\n        zfree(current);\n        current = next;\n    }\n    list->head = list->tail = NULL;\n    list->len = 0;\n}\n\n/* Free the whole list.\n *\n * This function can't fail. */\nvoid listRelease(list *list)\n{\n    listEmpty(list);\n    zfree(list);\n}\n\n/* Add a new node to the list, to head, containing the specified 'value'\n * pointer as value.\n *\n * On error, NULL is returned and no operation is performed (i.e. the\n * list remains unaltered).\n * On success the 'list' pointer you pass to the function is returned. */\nlist *listAddNodeHead(list *list, void *value)\n{\n    listNode *node;\n\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    } else {\n        node->prev = NULL;\n        node->next = list->head;\n        list->head->prev = node;\n        list->head = node;\n    }\n    list->len++;\n    return list;\n}\n\n/* Add a new node to the list, to tail, containing the specified 'value'\n * pointer as value.\n *\n * On error, NULL is returned and no operation is performed (i.e. the\n * list remains unaltered).\n * On success the 'list' pointer you pass to the function is returned. */\nlist *listAddNodeTail(list *list, void *value)\n{\n    listNode *node;\n\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (list->len == 0) {\n        list->head = list->tail = node;\n        node->prev = node->next = NULL;\n    } else {\n        node->prev = list->tail;\n        node->next = NULL;\n        list->tail->next = node;\n        list->tail = node;\n    }\n    list->len++;\n    return list;\n}\n\nlist *listInsertNode(list *list, listNode *old_node, void *value, int after) {\n    listNode *node;\n\n    if ((node = zmalloc(sizeof(*node))) == NULL)\n        return NULL;\n    node->value = value;\n    if (after) {\n        node->prev = old_node;\n        node->next = old_node->next;\n        if (list->tail == old_node) {\n            list->tail = node;\n        }\n    } else {\n        node->next = old_node;\n        node->prev = old_node->prev;\n        if (list->head == old_node) {\n            list->head = node;\n        }\n    }\n    if (node->prev != NULL) {\n        node->prev->next = node;\n    }\n    if (node->next != NULL) {\n        node->next->prev = node;\n    }\n    list->len++;\n    return list;\n}\n\n/* Remove the specified node from the specified list.\n * It's up to the caller to free the private value of the node.\n *\n * This function can't fail. */\nvoid listDelNode(list *list, listNode *node)\n{\n    if (node->prev)\n        node->prev->next = node->next;\n    else\n        list->head = node->next;\n    if (node->next)\n        node->next->prev = node->prev;\n    else\n        list->tail = node->prev;\n    if (list->free) list->free(node->value);\n    zfree(node);\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. */\nlistIter *listGetIterator(list *list, int direction)\n{\n    listIter *iter;\n\n    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;\n    if (direction == AL_START_HEAD)\n        iter->next = list->head;\n    else\n        iter->next = list->tail;\n    iter->direction = direction;\n    return iter;\n}\n\n/* Release the iterator memory */\nvoid listReleaseIterator(listIter *iter) {\n    zfree(iter);\n}\n\n/* Create an iterator in the list private iterator structure */\nvoid listRewind(list *list, listIter *li) {\n    li->next = list->head;\n    li->direction = AL_START_HEAD;\n}\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 * */\nlistNode *listNext(listIter *iter)\n{\n    listNode *current = iter->next;\n\n    if (current != NULL) {\n        if (iter->direction == AL_START_HEAD)\n            iter->next = current->next;\n        else\n            iter->next = current->prev;\n    }\n    return current;\n}\n\n/* Duplicate the whole list. On out of memory NULL is returned.\n * On success a copy of the original list is returned.\n *\n * The 'Dup' method set with listSetDupMethod() function is used\n * to copy the node value. Otherwise the same pointer value of\n * the original node is used as value of the copied node.\n *\n * The original list both on success or error is never modified. */\nlist *listDup(list *orig)\n{\n    list *copy;\n    listIter iter;\n    listNode *node;\n\n    if ((copy = listCreate()) == NULL)\n        return NULL;\n    copy->dup = orig->dup;\n    copy->free = orig->free;\n    copy->match = orig->match;\n    listRewind(orig, &iter);\n    while((node = listNext(&iter)) != NULL) {\n        void *value;\n\n        if (copy->dup) {\n            value = copy->dup(node->value);\n            if (value == NULL) {\n                listRelease(copy);\n                return NULL;\n            }\n        } else\n            value = node->value;\n        if (listAddNodeTail(copy, value) == NULL) {\n            listRelease(copy);\n            return NULL;\n        }\n    }\n    return copy;\n}\n\n/* Search the list for a node matching a given key.\n * The match is performed using the 'match' method\n * set with listSetMatchMethod(). If no 'match' method\n * is set, the 'value' pointer of every node is directly\n * compared with the 'key' pointer.\n *\n * On success the first matching node pointer is returned\n * (search starts from head). If no matching node exists\n * NULL is returned. */\nlistNode *listSearchKey(list *list, void *key)\n{\n    listIter iter;\n    listNode *node;\n\n    listRewind(list, &iter);\n    while((node = listNext(&iter)) != NULL) {\n        if (list->match) {\n            if (list->match(node->value, key)) {\n                return node;\n            }\n        } else {\n            if (key == node->value) {\n                return node;\n            }\n        }\n    }\n    return NULL;\n}\n\n/* Return the element at the specified zero-based index\n * where 0 is the head, 1 is the element next to head\n * and so on. Negative integers are used in order to count\n * from the tail, -1 is the last element, -2 the penultimate\n * and so on. If the index is out of range NULL is returned. */\nlistNode *listIndex(list *list, long index) {\n    listNode *n;\n\n    if (index < 0) {\n        index = (-index)-1;\n        n = list->tail;\n        while(index-- && n) n = n->prev;\n    } else {\n        n = list->head;\n        while(index-- && n) n = n->next;\n    }\n    return n;\n}\n\n/* Rotate the list removing the tail node and inserting it to the head. */\nvoid listRotateTailToHead(list *list) {\n    if (listLength(list) <= 1) return;\n\n    /* Detach current tail */\n    listNode *tail = list->tail;\n    list->tail = tail->prev;\n    list->tail->next = NULL;\n    /* Move it as head */\n    list->head->prev = tail;\n    tail->prev = NULL;\n    tail->next = list->head;\n    list->head = tail;\n}\n\n/* Rotate the list removing the head node and inserting it to the tail. */\nvoid listRotateHeadToTail(list *list) {\n    if (listLength(list) <= 1) return;\n\n    listNode *head = list->head;\n    /* Detach current head */\n    list->head = head->next;\n    list->head->prev = NULL;\n    /* Move it as tail */\n    list->tail->next = head;\n    head->next = NULL;\n    head->prev = list->tail;\n    list->tail = head;\n}\n\n/* Add all the elements of the list 'o' at the end of the\n * list 'l'. The list 'other' remains empty but otherwise valid. */\nvoid listJoin(list *l, list *o) {\n    if (o->head)\n        o->head->prev = l->tail;\n\n    if (l->tail)\n        l->tail->next = o->head;\n    else\n        l->head = o->head;\n\n    if (o->tail) l->tail = o->tail;\n    l->len += o->len;\n\n    /* Setup other as an empty list. */\n    o->head = o->tail = NULL;\n    o->len = 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\ntypedef struct listNode {\n    struct listNode *prev;\n    struct listNode *next;\n    void *value;\n} listNode;\n\ntypedef struct listIter {\n    listNode *next;\n    int direction;\n} listIter;\n\ntypedef struct list {\n    listNode *head;\n    listNode *tail;\n    void *(*dup)(void *ptr);\n    void (*free)(void *ptr);\n    int (*match)(void *ptr, void *key);\n    unsigned long len;\n} list;\n\n/* Functions implemented as macros */\n#define listLength(l) ((l)->len)\n#define listFirst(l) ((l)->head)\n#define listLast(l) ((l)->tail)\n#define listPrevNode(n) ((n)->prev)\n#define listNextNode(n) ((n)->next)\n#define listNodeValue(n) ((n)->value)\n\n#define listSetDupMethod(l,m) ((l)->dup = (m))\n#define listSetFreeMethod(l,m) ((l)->free = (m))\n#define listSetMatchMethod(l,m) ((l)->match = (m))\n\n#define listGetDupMethod(l) ((l)->dup)\n#define listGetFreeMethod(l) ((l)->free)\n#define listGetMatchMethod(l) ((l)->match)\n\n/* Prototypes */\nlist *listCreate(void);\nvoid listRelease(list *list);\nvoid listEmpty(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 listRotateTailToHead(list *list);\nvoid listRotateHeadToTail(list *list);\nvoid listJoin(list *l, list *o);\n\n/* Directions for iterators */\n#define AL_START_HEAD 0\n#define AL_START_TAIL 1\n\n#endif /* __ADLIST_H__ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\naeEventLoop *aeCreateEventLoop(int setsize) {\n    aeEventLoop *eventLoop;\n    int i;\n\n    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;\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    eventLoop->setsize = setsize;\n    eventLoop->lastTime = time(NULL);\n    eventLoop->timeEventHead = NULL;\n    eventLoop->timeEventNextId = 0;\n    eventLoop->stop = 0;\n    eventLoop->maxfd = -1;\n    eventLoop->beforesleep = NULL;\n    eventLoop->aftersleep = NULL;\n    eventLoop->flags = 0;\n    if (aeApiCreate(eventLoop) == -1) goto err;\n    /* Events with mask == AE_NONE are not set. So let's initialize the\n     * vector with it. */\n    for (i = 0; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n    return eventLoop;\n\nerr:\n    if (eventLoop) {\n        zfree(eventLoop->events);\n        zfree(eventLoop->fired);\n        zfree(eventLoop);\n    }\n    return NULL;\n}\n\n/* Return the current set size. */\nint aeGetSetSize(aeEventLoop *eventLoop) {\n    return eventLoop->setsize;\n}\n\n/* Tells the next iteration/s of the event processing to set timeout of 0. */\nvoid aeSetDontWait(aeEventLoop *eventLoop, int noWait) {\n    if (noWait)\n        eventLoop->flags |= AE_DONT_WAIT;\n    else\n        eventLoop->flags &= ~AE_DONT_WAIT;\n}\n\n/* Resize the maximum set size of the event loop.\n * If the requested set size is smaller than the current set size, but\n * there is already a file descriptor in use that is >= the requested\n * set size minus one, AE_ERR is returned and the operation is not\n * performed at all.\n *\n * Otherwise AE_OK is returned and the operation is successful. */\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {\n    int i;\n\n    if (setsize == eventLoop->setsize) return AE_OK;\n    if (eventLoop->maxfd >= setsize) return AE_ERR;\n    if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;\n\n    eventLoop->events = 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\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop) {\n    aeApiFree(eventLoop);\n    zfree(eventLoop->events);\n    zfree(eventLoop->fired);\n\n    /* Free the time events list. */\n    aeTimeEvent *next_te, *te = eventLoop->timeEventHead;\n    while (te) {\n        next_te = te->next;\n        zfree(te);\n        te = next_te;\n    }\n    zfree(eventLoop);\n}\n\nvoid aeStop(aeEventLoop *eventLoop) {\n    eventLoop->stop = 1;\n}\n\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData)\n{\n    if (fd >= eventLoop->setsize) {\n        errno = ERANGE;\n        return AE_ERR;\n    }\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    if (aeApiAddEvent(eventLoop, fd, mask) == -1)\n        return AE_ERR;\n    fe->mask |= mask;\n    if (mask & AE_READABLE) fe->rfileProc = proc;\n    if (mask & AE_WRITABLE) fe->wfileProc = proc;\n    fe->clientData = clientData;\n    if (fd > eventLoop->maxfd)\n        eventLoop->maxfd = fd;\n    return AE_OK;\n}\n\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)\n{\n    if (fd >= eventLoop->setsize) return;\n    aeFileEvent *fe = &eventLoop->events[fd];\n    if (fe->mask == AE_NONE) return;\n\n    /* We want to always remove AE_BARRIER if set when AE_WRITABLE\n     * is removed. */\n    if (mask & AE_WRITABLE) mask |= AE_BARRIER;\n\n    aeApiDelEvent(eventLoop, fd, mask);\n    fe->mask = fe->mask & (~mask);\n    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {\n        /* Update the max fd */\n        int j;\n\n        for (j = eventLoop->maxfd-1; j >= 0; j--)\n            if (eventLoop->events[j].mask != AE_NONE) break;\n        eventLoop->maxfd = j;\n    }\n}\n\nint aeGetFileEvents(aeEventLoop *eventLoop, int fd) {\n    if (fd >= eventLoop->setsize) return 0;\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    return fe->mask;\n}\n\nstatic void aeGetTime(long *seconds, long *milliseconds)\n{\n    struct timeval tv;\n\n    gettimeofday(&tv, NULL);\n    *seconds = tv.tv_sec;\n    *milliseconds = tv.tv_usec/1000;\n}\n\nstatic void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {\n    long cur_sec, cur_ms, when_sec, when_ms;\n\n    aeGetTime(&cur_sec, &cur_ms);\n    when_sec = cur_sec + milliseconds/1000;\n    when_ms = cur_ms + milliseconds%1000;\n    if (when_ms >= 1000) {\n        when_sec ++;\n        when_ms -= 1000;\n    }\n    *sec = when_sec;\n    *ms = when_ms;\n}\n\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc)\n{\n    long long id = eventLoop->timeEventNextId++;\n    aeTimeEvent *te;\n\n    te = zmalloc(sizeof(*te));\n    if (te == NULL) return AE_ERR;\n    te->id = id;\n    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);\n    te->timeProc = proc;\n    te->finalizerProc = finalizerProc;\n    te->clientData = clientData;\n    te->prev = NULL;\n    te->next = eventLoop->timeEventHead;\n    te->refcount = 0;\n    if (te->next)\n        te->next->prev = te;\n    eventLoop->timeEventHead = te;\n    return id;\n}\n\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    while(te) {\n        if (te->id == id) {\n            te->id = AE_DELETED_EVENT_ID;\n            return AE_OK;\n        }\n        te = te->next;\n    }\n    return AE_ERR; /* NO event with the specified ID found */\n}\n\n/* Search the first timer to fire.\n * This operation is useful to know how many time the select can be\n * put in sleep without to delay any event.\n * If there are no timers NULL is returned.\n *\n * Note that's O(N) since time events are unsorted.\n * Possible optimizations (not needed by Redis so far, but...):\n * 1) Insert the event in order, so that the nearest is just the head.\n *    Much better but still insertion or deletion of timers is O(N).\n * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).\n */\nstatic aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    aeTimeEvent *nearest = NULL;\n\n    while(te) {\n        if (!nearest || te->when_sec < nearest->when_sec ||\n                (te->when_sec == nearest->when_sec &&\n                 te->when_ms < nearest->when_ms))\n            nearest = te;\n        te = te->next;\n    }\n    return nearest;\n}\n\n/* Process time events */\nstatic int processTimeEvents(aeEventLoop *eventLoop) {\n    int processed = 0;\n    aeTimeEvent *te;\n    long long maxId;\n    time_t now = time(NULL);\n\n    /* If the system clock is moved to the future, and then set back to the\n     * right value, time events may be delayed in a random way. Often this\n     * means that scheduled operations will not be performed soon enough.\n     *\n     * Here we try to detect system clock skews, and force all the time\n     * events to be processed ASAP when this happens: the idea is that\n     * processing events earlier is less dangerous than delaying them\n     * indefinitely, and practice suggests it is. */\n    if (now < eventLoop->lastTime) {\n        te = eventLoop->timeEventHead;\n        while(te) {\n            te->when_sec = 0;\n            te = te->next;\n        }\n    }\n    eventLoop->lastTime = now;\n\n    te = eventLoop->timeEventHead;\n    maxId = eventLoop->timeEventNextId-1;\n    while(te) {\n        long now_sec, now_ms;\n        long long id;\n\n        /* Remove events scheduled for deletion. */\n        if (te->id == AE_DELETED_EVENT_ID) {\n            aeTimeEvent *next = te->next;\n            /* If a reference exists for this timer event,\n             * don't free it. This is currently incremented\n             * for recursive timerProc calls */\n            if (te->refcount) {\n                te = next;\n                continue;\n            }\n            if (te->prev)\n                te->prev->next = te->next;\n            else\n                eventLoop->timeEventHead = te->next;\n            if (te->next)\n                te->next->prev = te->prev;\n            if (te->finalizerProc)\n                te->finalizerProc(eventLoop, te->clientData);\n            zfree(te);\n            te = next;\n            continue;\n        }\n\n        /* Make sure we don't process time events created by time events in\n         * this iteration. Note that this check is currently useless: we always\n         * add new timers on the head, however if we change the implementation\n         * detail, this check may be useful again: we keep it here for future\n         * defense. */\n        if (te->id > maxId) {\n            te = te->next;\n            continue;\n        }\n        aeGetTime(&now_sec, &now_ms);\n        if (now_sec > te->when_sec ||\n            (now_sec == te->when_sec && now_ms >= te->when_ms))\n        {\n            int retval;\n\n            id = te->id;\n            te->refcount++;\n            retval = te->timeProc(eventLoop, id, te->clientData);\n            te->refcount--;\n            processed++;\n            if (retval != AE_NOMORE) {\n                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);\n            } else {\n                te->id = AE_DELETED_EVENT_ID;\n            }\n        }\n        te = te->next;\n    }\n    return processed;\n}\n\n/* Process every pending time event, then every pending file event\n * (that may be registered by time event callbacks just processed).\n * Without special flags the function sleeps until some file event\n * fires, or when the next time event occurs (if any).\n *\n * If flags is 0, the function does nothing and returns.\n * if flags has AE_ALL_EVENTS set, all the kind of events are processed.\n * if flags has AE_FILE_EVENTS set, file events are processed.\n * if flags has AE_TIME_EVENTS set, time events are processed.\n * if flags has AE_DONT_WAIT set the function returns ASAP until all\n * the events that's possible to process without to wait are processed.\n * if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called.\n * if flags has AE_CALL_BEFORE_SLEEP set, the beforesleep callback is called.\n *\n * The function returns the number of events processed. */\nint aeProcessEvents(aeEventLoop *eventLoop, int flags)\n{\n    int processed = 0, numevents;\n\n    /* Nothing to do? return ASAP */\n    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;\n\n    /* Note that we want call select() even if there are no\n     * file events to process as long as we want to process time\n     * events, in order to sleep until the next time event is ready\n     * to fire. */\n    if (eventLoop->maxfd != -1 ||\n        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {\n        int j;\n        aeTimeEvent *shortest = NULL;\n        struct timeval tv, *tvp;\n\n        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))\n            shortest = aeSearchNearestTimer(eventLoop);\n        if (shortest) {\n            long now_sec, now_ms;\n\n            aeGetTime(&now_sec, &now_ms);\n            tvp = &tv;\n\n            /* How many milliseconds we need to wait for the next\n             * time event to fire? */\n            long long ms =\n                (shortest->when_sec - now_sec)*1000 +\n                shortest->when_ms - now_ms;\n\n            if (ms > 0) {\n                tvp->tv_sec = ms/1000;\n                tvp->tv_usec = (ms % 1000)*1000;\n            } else {\n                tvp->tv_sec = 0;\n                tvp->tv_usec = 0;\n            }\n        } else {\n            /* If we have to check for events but need to return\n             * ASAP because of AE_DONT_WAIT we need to set the timeout\n             * to zero */\n            if (flags & AE_DONT_WAIT) {\n                tv.tv_sec = tv.tv_usec = 0;\n                tvp = &tv;\n            } else {\n                /* Otherwise we can block */\n                tvp = NULL; /* wait forever */\n            }\n        }\n\n        if (eventLoop->flags & AE_DONT_WAIT) {\n            tv.tv_sec = tv.tv_usec = 0;\n            tvp = &tv;\n        }\n\n        if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)\n            eventLoop->beforesleep(eventLoop);\n\n        /* Call the multiplexing API, will return only on timeout or when\n         * some event fires. */\n        numevents = aeApiPoll(eventLoop, tvp);\n\n        /* After sleep callback. */\n        if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)\n            eventLoop->aftersleep(eventLoop);\n\n        for (j = 0; j < numevents; j++) {\n            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];\n            int mask = eventLoop->fired[j].mask;\n            int fd = eventLoop->fired[j].fd;\n            int fired = 0; /* Number of events fired for current fd. */\n\n            /* Normally we execute the readable event first, and the writable\n             * event laster. This is useful as sometimes we may be able\n             * to serve the reply of a query immediately after processing the\n             * query.\n             *\n             * However if AE_BARRIER is set in the mask, our application is\n             * asking us to do the reverse: never fire the writable event\n             * after the readable. In such a case, we invert the calls.\n             * This is useful when, for instance, we want to do things\n             * in the beforeSleep() hook, like fsynching a file to disk,\n             * before replying to a client. */\n            int invert = fe->mask & AE_BARRIER;\n\n            /* Note the \"fe->mask & mask & ...\" code: maybe an already\n             * processed event removed an element that fired and we still\n             * didn't processed, so we check if the event is still valid.\n             *\n             * Fire the readable event if the call sequence is not\n             * inverted. */\n            if (!invert && fe->mask & mask & AE_READABLE) {\n                fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n                fired++;\n                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */\n            }\n\n            /* Fire the writable event. */\n            if (fe->mask & mask & AE_WRITABLE) {\n                if (!fired || fe->wfileProc != fe->rfileProc) {\n                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);\n                    fired++;\n                }\n            }\n\n            /* If we have to invert the call, fire the readable event now\n             * after the writable one. */\n            if (invert) {\n                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */\n                if ((fe->mask & mask & AE_READABLE) &&\n                    (!fired || fe->wfileProc != fe->rfileProc))\n                {\n                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n                    fired++;\n                }\n            }\n\n            processed++;\n        }\n    }\n    /* Check time events */\n    if (flags & AE_TIME_EVENTS)\n        processed += processTimeEvents(eventLoop);\n\n    return processed; /* return the number of processed file/time events */\n}\n\n/* Wait for milliseconds until the given file descriptor becomes\n * writable/readable/exception */\nint aeWait(int fd, int mask, long long milliseconds) {\n    struct pollfd pfd;\n    int retmask = 0, retval;\n\n    memset(&pfd, 0, sizeof(pfd));\n    pfd.fd = fd;\n    if (mask & AE_READABLE) pfd.events |= POLLIN;\n    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;\n\n    if ((retval = poll(&pfd, 1, milliseconds))== 1) {\n        if (pfd.revents & POLLIN) retmask |= AE_READABLE;\n        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;\n        if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;\n        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;\n        return retmask;\n    } else {\n        return retval;\n    }\n}\n\nvoid aeMain(aeEventLoop *eventLoop) {\n    eventLoop->stop = 0;\n    while (!eventLoop->stop) {\n        aeProcessEvents(eventLoop, AE_ALL_EVENTS|\n                                   AE_CALL_BEFORE_SLEEP|\n                                   AE_CALL_AFTER_SLEEP);\n    }\n}\n\nchar *aeGetApiName(void) {\n    return aeApiName();\n}\n\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {\n    eventLoop->beforesleep = beforesleep;\n}\n\nvoid aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) {\n    eventLoop->aftersleep = aftersleep;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <time.h>\n\n#define AE_OK 0\n#define AE_ERR -1\n\n#define AE_NONE 0       /* No events registered. */\n#define AE_READABLE 1   /* Fire when descriptor is readable. */\n#define AE_WRITABLE 2   /* Fire when descriptor is writable. */\n#define AE_BARRIER 4    /* With WRITABLE, never fire the event if the\n                           READABLE event already fired in the same event\n                           loop iteration. Useful when you want to persist\n                           things to disk before sending replies, and want\n                           to do that in a group fashion. */\n\n#define AE_FILE_EVENTS (1<<0)\n#define AE_TIME_EVENTS (1<<1)\n#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)\n#define AE_DONT_WAIT (1<<2)\n#define AE_CALL_BEFORE_SLEEP (1<<3)\n#define AE_CALL_AFTER_SLEEP (1<<4)\n\n#define AE_NOMORE -1\n#define AE_DELETED_EVENT_ID -1\n\n/* Macros */\n#define AE_NOTUSED(V) ((void) V)\n\nstruct aeEventLoop;\n\n/* Types and data structures */\ntypedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);\ntypedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);\ntypedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);\ntypedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);\n\n/* File event structure */\ntypedef struct aeFileEvent {\n    int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */\n    aeFileProc *rfileProc;\n    aeFileProc *wfileProc;\n    void *clientData;\n} aeFileEvent;\n\n/* Time event structure */\ntypedef struct aeTimeEvent {\n    long long id; /* time event identifier. */\n    long when_sec; /* seconds */\n    long when_ms; /* milliseconds */\n    aeTimeProc *timeProc;\n    aeEventFinalizerProc *finalizerProc;\n    void *clientData;\n    struct aeTimeEvent *prev;\n    struct aeTimeEvent *next;\n    int refcount; /* refcount to prevent timer events from being\n  \t\t   * freed in recursive time event calls. */\n} aeTimeEvent;\n\n/* A fired event */\ntypedef struct aeFiredEvent {\n    int fd;\n    int mask;\n} aeFiredEvent;\n\n/* State of an event based program */\ntypedef struct aeEventLoop {\n    int maxfd;   /* highest file descriptor currently registered */\n    int setsize; /* max number of file descriptors tracked */\n    long long timeEventNextId;\n    time_t lastTime;     /* Used to detect system clock skew */\n    aeFileEvent *events; /* Registered events */\n    aeFiredEvent *fired; /* Fired events */\n    aeTimeEvent *timeEventHead;\n    int stop;\n    void *apidata; /* This is used for polling API specific data */\n    aeBeforeSleepProc *beforesleep;\n    aeBeforeSleepProc *aftersleep;\n    int flags;\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);\nvoid aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep);\nint aeGetSetSize(aeEventLoop *eventLoop);\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize);\nvoid aeSetDontWait(aeEventLoop *eventLoop, int noWait);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\ntypedef struct aeApiState {\n    int epfd;\n    struct epoll_event *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 epoll_event)*eventLoop->setsize);\n    if (!state->events) {\n        zfree(state);\n        return -1;\n    }\n    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */\n    if (state->epfd == -1) {\n        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 epoll_event)*setsize);\n    return 0;\n}\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\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    /* If the fd was already monitored for some event, we need a MOD\n     * operation. Otherwise we need an ADD operation. */\n    int op = eventLoop->events[fd].mask == AE_NONE ?\n            EPOLL_CTL_ADD : EPOLL_CTL_MOD;\n\n    ee.events = 0;\n    mask |= eventLoop->events[fd].mask; /* Merge old events */\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    int mask = eventLoop->events[fd].mask & (~delmask);\n\n    ee.events = 0;\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (mask != AE_NONE) {\n        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);\n    } else {\n        /* Note, Kernel < 2.6.9 requires a non null event pointer even for\n         * EPOLL_CTL_DEL. */\n        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,\n            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);\n    if (retval > 0) {\n        int j;\n\n        numevents = retval;\n        for (j = 0; j < numevents; j++) {\n            int mask = 0;\n            struct epoll_event *e = state->events+j;\n\n            if (e->events & EPOLLIN) mask |= AE_READABLE;\n            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;\n            if (e->events & EPOLLERR) mask |= AE_WRITABLE|AE_READABLE;\n            if (e->events & EPOLLHUP) mask |= AE_WRITABLE|AE_READABLE;\n            eventLoop->fired[j].fd = e->data.fd;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"epoll\";\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 <sys/select.h>\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": "Chapter07/redis-6.0.3/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 <sys/time.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\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\nint anetSetBlock(char *err, int fd, int non_block) {\n    int flags;\n\n    /* Set the socket blocking (if non_block is zero) or 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\n    if (non_block)\n        flags |= O_NONBLOCK;\n    else\n        flags &= ~O_NONBLOCK;\n\n    if (fcntl(fd, F_SETFL, flags) == -1) {\n        anetSetError(err, \"fcntl(F_SETFL,O_NONBLOCK): %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\nint anetNonBlock(char *err, int fd) {\n    return anetSetBlock(err,fd,1);\n}\n\nint anetBlock(char *err, int fd) {\n    return anetSetBlock(err,fd,0);\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. */\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#else\n    ((void) interval); /* Avoid unused var warning for non Linux systems. */\n#endif\n\n    return ANET_OK;\n}\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\nint anetEnableTcpNoDelay(char *err, int fd)\n{\n    return anetSetTcpNoDelay(err, fd, 1);\n}\n\nint anetDisableTcpNoDelay(char *err, int fd)\n{\n    return anetSetTcpNoDelay(err, fd, 0);\n}\n\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\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/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified\n * number of milliseconds, or disable it if the 'ms' argument is zero. */\nint anetSendTimeout(char *err, int fd, long long ms) {\n    struct timeval tv;\n\n    tv.tv_sec = ms/1000;\n    tv.tv_usec = (ms%1000)*1000;\n    if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {\n        anetSetError(err, \"setsockopt SO_SNDTIMEO: %s\", strerror(errno));\n        return ANET_ERR;\n    }\n    return ANET_OK;\n}\n\n/* Set the socket receive timeout (SO_RCVTIMEO socket option) to the specified\n * number of milliseconds, or disable it if the 'ms' argument is zero. */\nint anetRecvTimeout(char *err, int fd, long long ms) {\n    struct timeval tv;\n\n    tv.tv_sec = ms/1000;\n    tv.tv_usec = (ms%1000)*1000;\n    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {\n        anetSetError(err, \"setsockopt SO_RCVTIMEO: %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. */\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\nstatic int anetSetReuseAddr(char *err, int fd) {\n    int yes = 1;\n    /* Make sure connection-intensive things like the redis benchmark\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\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#define ANET_CONNECT_NONE 0\n#define ANET_CONNECT_NONBLOCK 1\n#define ANET_CONNECT_BE_BINDING 2 /* Best effort binding. */\nstatic int anetTcpGenericConnect(char *err, const char *addr, int port,\n                                 const 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            {\n                anetSetError(err, \"%s\", gai_strerror(rv));\n                goto error;\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            freeaddrinfo(bservinfo);\n            if (!bound) {\n                anetSetError(err, \"bind: %s\", strerror(errno));\n                goto error;\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    }\n\nend:\n    freeaddrinfo(servinfo);\n\n    /* Handle best effort binding: if a binding address was used, but it is\n     * not possible to create a socket, try again without a binding address. */\n    if (s == ANET_ERR && source_addr && (flags & ANET_CONNECT_BE_BINDING)) {\n        return anetTcpGenericConnect(err,addr,port,NULL,flags);\n    } else {\n        return s;\n    }\n}\n\nint anetTcpConnect(char *err, const char *addr, int port)\n{\n    return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);\n}\n\nint anetTcpNonBlockConnect(char *err, const char *addr, int port)\n{\n    return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);\n}\n\nint anetTcpNonBlockBindConnect(char *err, const char *addr, int port,\n                               const char *source_addr)\n{\n    return anetTcpGenericConnect(err,addr,port,source_addr,\n            ANET_CONNECT_NONBLOCK);\n}\n\nint anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,\n                                         const char *source_addr)\n{\n    return anetTcpGenericConnect(err,addr,port,source_addr,\n            ANET_CONNECT_NONBLOCK|ANET_CONNECT_BE_BINDING);\n}\n\nint anetUnixGenericConnect(char *err, const 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            close(s);\n            return ANET_ERR;\n        }\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\nint anetUnixConnect(char *err, const char *path)\n{\n    return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);\n}\n\nint anetUnixNonBlockConnect(char *err, const 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) */\nint anetRead(int fd, char *buf, int count)\n{\n    ssize_t 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 written before to return\n * (unless error is encountered) */\nint anetWrite(int fd, char *buf, int count)\n{\n    ssize_t 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\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 = -1, 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) s = ANET_ERR;\n        goto end;\n    }\n    if (p == NULL) {\n        anetSetError(err, \"unable to bind socket, errno: %d\", errno);\n        goto error;\n    }\n\nerror:\n    if (s != -1) close(s);\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\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\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\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\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) goto error;\n    if (ip_len == 0) goto error;\n\n    if (sa.ss_family == AF_INET) {\n        struct sockaddr_in *s = (struct sockaddr_in *)&sa;\n        if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);\n        if (port) *port = ntohs(s->sin_port);\n    } else if (sa.ss_family == AF_INET6) {\n        struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;\n        if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);\n        if (port) *port = ntohs(s->sin6_port);\n    } else if (sa.ss_family == AF_UNIX) {\n        if (ip) strncpy(ip,\"/unixsocket\",ip_len);\n        if (port) *port = 0;\n    } else {\n        goto error;\n    }\n    return 0;\n\nerror:\n    if (ip) {\n        if (ip_len >= 2) {\n            ip[0] = '?';\n            ip[1] = '\\0';\n        } else if (ip_len == 1) {\n            ip[0] = '\\0';\n        }\n    }\n    if (port) *port = 0;\n    return -1;\n}\n\n/* Format an IP,port pair into something easy to parse. If IP is IPv6\n * (matches for \":\"), the ip is surrounded by []. IP and port are just\n * separated by colons. This the standard to display addresses within Redis. */\nint anetFormatAddr(char *buf, size_t buf_len, char *ip, int port) {\n    return snprintf(buf,buf_len, strchr(ip,':') ?\n           \"[%s]:%d\" : \"%s:%d\", ip, port);\n}\n\n/* Like anetFormatAddr() but extract ip and port from the socket's peer. */\nint anetFormatPeer(int fd, char *buf, size_t buf_len) {\n    char ip[INET6_ADDRSTRLEN];\n    int port;\n\n    anetPeerToString(fd,ip,sizeof(ip),&port);\n    return anetFormatAddr(buf, buf_len, ip, port);\n}\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\nint anetFormatSock(int fd, char *fmt, size_t fmt_len) {\n    char ip[INET6_ADDRSTRLEN];\n    int port;\n\n    anetSockName(fd,ip,sizeof(ip),&port);\n    return anetFormatAddr(fmt, fmt_len, ip, port);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <sys/types.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) || defined(_AIX)\n#define AF_LOCAL AF_UNIX\n#endif\n\n#ifdef _AIX\n#undef ip_len\n#endif\n\nint anetTcpConnect(char *err, const char *addr, int port);\nint anetTcpNonBlockConnect(char *err, const char *addr, int port);\nint anetTcpNonBlockBindConnect(char *err, const char *addr, int port, const char *source_addr);\nint anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr);\nint anetUnixConnect(char *err, const char *path);\nint anetUnixNonBlockConnect(char *err, const 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 anetBlock(char *err, int fd);\nint anetEnableTcpNoDelay(char *err, int fd);\nint anetDisableTcpNoDelay(char *err, int fd);\nint anetTcpKeepAlive(char *err, int fd);\nint anetSendTimeout(char *err, int fd, long long ms);\nint anetRecvTimeout(char *err, int fd, long long ms);\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);\nint anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port);\nint anetFormatPeer(int fd, char *fmt, size_t fmt_len);\nint anetFormatSock(int fd, char *fmt, size_t fmt_len);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.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#include <sys/param.h>\n\nvoid aofUpdateCurrentSize(void);\nvoid aofClosePipes(void);\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite buffer implementation.\n *\n * The following code implement a simple buffer used in order to accumulate\n * changes while the background process is rewriting the AOF file.\n *\n * We only need to append, but can't just use realloc with a large block\n * because 'huge' reallocs are not always handled as one could expect\n * (via remapping of pages at OS level) but may involve copying data.\n *\n * For this reason we use a list of blocks, every block is\n * AOF_RW_BUF_BLOCK_SIZE bytes.\n * ------------------------------------------------------------------------- */\n\n#define AOF_RW_BUF_BLOCK_SIZE (1024*1024*10)    /* 10 MB per block */\n\ntypedef struct aofrwblock {\n    unsigned long used, free;\n    char buf[AOF_RW_BUF_BLOCK_SIZE];\n} aofrwblock;\n\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. */\nvoid aofRewriteBufferReset(void) {\n    if (server.aof_rewrite_buf_blocks)\n        listRelease(server.aof_rewrite_buf_blocks);\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 rewrite buffer. */\nunsigned long aofRewriteBufferSize(void) {\n    listNode *ln;\n    listIter li;\n    unsigned long size = 0;\n\n    listRewind(server.aof_rewrite_buf_blocks,&li);\n    while((ln = listNext(&li))) {\n        aofrwblock *block = listNodeValue(ln);\n        size += block->used;\n    }\n    return size;\n}\n\n/* Event handler used to send data to the child process doing the AOF\n * rewrite. We send pieces of our AOF differences buffer so that the final\n * write when the child finishes the rewrite will be small. */\nvoid aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {\n    listNode *ln;\n    aofrwblock *block;\n    ssize_t nwritten;\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(privdata);\n    UNUSED(mask);\n\n    while(1) {\n        ln = listFirst(server.aof_rewrite_buf_blocks);\n        block = ln ? ln->value : NULL;\n        if (server.aof_stop_sending_diff || !block) {\n            aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child,\n                              AE_WRITABLE);\n            return;\n        }\n        if (block->used > 0) {\n            nwritten = write(server.aof_pipe_write_data_to_child,\n                             block->buf,block->used);\n            if (nwritten <= 0) return;\n            memmove(block->buf,block->buf+nwritten,block->used-nwritten);\n            block->used -= nwritten;\n            block->free += nwritten;\n        }\n        if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks,ln);\n    }\n}\n\n/* Append data to the AOF rewrite buffer, allocating new blocks if needed. */\nvoid aofRewriteBufferAppend(unsigned char *s, unsigned long len) {\n    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        if (block) {\n            unsigned long thislen = (block->free < len) ? block->free : len;\n            if (thislen) {  /* The current block is not already full. */\n                memcpy(block->buf+block->used, s, thislen);\n                block->used += thislen;\n                block->free -= thislen;\n                s += thislen;\n                len -= thislen;\n            }\n        }\n\n        if (len) { /* First block to allocate, or need another block. */\n            int numblocks;\n\n            block = zmalloc(sizeof(*block));\n            block->free = AOF_RW_BUF_BLOCK_SIZE;\n            block->used = 0;\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            numblocks = listLength(server.aof_rewrite_buf_blocks);\n            if (((numblocks+1) % 10) == 0) {\n                int level = ((numblocks+1) % 100) == 0 ? LL_WARNING :\n                                                         LL_NOTICE;\n                serverLog(level,\"Background AOF buffer size: %lu MB\",\n                    aofRewriteBufferSize()/(1024*1024));\n            }\n        }\n    }\n\n    /* Install a file event to send data to the rewrite child if there is\n     * not one already. */\n    if (aeGetFileEvents(server.el,server.aof_pipe_write_data_to_child) == 0) {\n        aeCreateFileEvent(server.el, server.aof_pipe_write_data_to_child,\n            AE_WRITABLE, aofChildWriteDiffData, NULL);\n    }\n}\n\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. */\nssize_t aofRewriteBufferWrite(int fd) {\n    listNode *ln;\n    listIter li;\n    ssize_t count = 0;\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            nwritten = write(fd,block->buf,block->used);\n            if (nwritten != (ssize_t)block->used) {\n                if (nwritten == 0) errno = EIO;\n                return -1;\n            }\n            count += nwritten;\n        }\n    }\n    return count;\n}\n\n/* ----------------------------------------------------------------------------\n * AOF file implementation\n * ------------------------------------------------------------------------- */\n\n/* Return true if an AOf fsync is currently already in progress in a\n * BIO thread. */\nint aofFsyncInProgress(void) {\n    return bioPendingJobsOfType(BIO_AOF_FSYNC) != 0;\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. */\nvoid aof_background_fsync(int fd) {\n    bioCreateBackgroundJob(BIO_AOF_FSYNC,(void*)(long)fd,NULL,NULL);\n}\n\n/* Kills an AOFRW child process if exists */\nvoid killAppendOnlyChild(void) {\n    int statloc;\n    /* No AOFRW child? return. */\n    if (server.aof_child_pid == -1) return;\n    /* Kill AOFRW child, wait for child exit. */\n    serverLog(LL_NOTICE,\"Killing running AOF rewrite child: %ld\",\n        (long) server.aof_child_pid);\n    if (kill(server.aof_child_pid,SIGUSR1) != -1) {\n        while(wait3(&statloc,0,NULL) != server.aof_child_pid);\n    }\n    /* Reset the buffer accumulating changes while the child saves. */\n    aofRewriteBufferReset();\n    aofRemoveTempFile(server.aof_child_pid);\n    server.aof_child_pid = -1;\n    server.aof_rewrite_time_start = -1;\n    /* Close pipes used for IPC between the two processes. */\n    aofClosePipes();\n    closeChildInfoPipe();\n    updateDictResizePolicy();\n}\n\n/* Called when the user switches from \"appendonly yes\" to \"appendonly no\"\n * at runtime using the CONFIG command. */\nvoid stopAppendOnly(void) {\n    serverAssert(server.aof_state != AOF_OFF);\n    flushAppendOnlyFile(1);\n    redis_fsync(server.aof_fd);\n    close(server.aof_fd);\n\n    server.aof_fd = -1;\n    server.aof_selected_db = -1;\n    server.aof_state = AOF_OFF;\n    server.aof_rewrite_scheduled = 0;\n    killAppendOnlyChild();\n}\n\n/* Called when the user switches from \"appendonly no\" to \"appendonly yes\"\n * at runtime using the CONFIG command. */\nint startAppendOnly(void) {\n    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */\n    int newfd;\n\n    newfd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);\n    serverAssert(server.aof_state == AOF_OFF);\n    if (newfd == -1) {\n        char *cwdp = getcwd(cwd,MAXPATHLEN);\n\n        serverLog(LL_WARNING,\n            \"Redis needs to enable the AOF but can't open the \"\n            \"append only file %s (in server root dir %s): %s\",\n            server.aof_filename,\n            cwdp ? cwdp : \"unknown\",\n            strerror(errno));\n        return C_ERR;\n    }\n    if (hasActiveChildProcess() && server.aof_child_pid == -1) {\n        server.aof_rewrite_scheduled = 1;\n        serverLog(LL_WARNING,\"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.\");\n    } else {\n        /* If there is a pending AOF rewrite, we need to switch it off and\n         * start a new one: the old one cannot be reused because it is not\n         * accumulating the AOF buffer. */\n        if (server.aof_child_pid != -1) {\n            serverLog(LL_WARNING,\"AOF was enabled but there is already an AOF rewriting in background. Stopping background AOF and starting a rewrite now.\");\n            killAppendOnlyChild();\n        }\n        if (rewriteAppendOnlyFileBackground() == C_ERR) {\n            close(newfd);\n            serverLog(LL_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 C_ERR;\n        }\n    }\n    /* We correctly switched on AOF, now wait for the rewrite to be complete\n     * in order to append data on disk. */\n    server.aof_state = AOF_WAIT_REWRITE;\n    server.aof_last_fsync = server.unixtime;\n    server.aof_fd = newfd;\n    return C_OK;\n}\n\n/* This is a wrapper to the write syscall in order to retry on short writes\n * or if the syscall gets interrupted. It could look strange that we retry\n * on short writes given that we are writing to a block device: normally if\n * the first call is short, there is a end-of-space condition, so the next\n * is likely to fail. However apparently in modern systems this is no longer\n * true, and in general it looks just more resilient to retry the write. If\n * there is an actual error condition we'll get it at the next try. */\nssize_t aofWrite(int fd, const char *buf, size_t len) {\n    ssize_t nwritten = 0, totwritten = 0;\n\n    while(len) {\n        nwritten = write(fd, buf, len);\n\n        if (nwritten < 0) {\n            if (errno == EINTR) continue;\n            return totwritten ? totwritten : -1;\n        }\n\n        len -= nwritten;\n        buf += nwritten;\n        totwritten += nwritten;\n    }\n\n    return totwritten;\n}\n\n/* Write the append only file buffer on disk.\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 * About the 'force' argument:\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 * 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 * However if force is set to 1 we'll write regardless of the background\n * fsync. */\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    mstime_t latency;\n\n    if (sdslen(server.aof_buf) == 0) {\n        /* Check if we need to do fsync even the aof buffer is empty,\n         * because previously in AOF_FSYNC_EVERYSEC mode, fsync is\n         * called only when aof buffer is not empty, so if users\n         * stop write commands before fsync called in one second,\n         * the data in page cache cannot be flushed in time. */\n        if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&\n            server.aof_fsync_offset != server.aof_current_size &&\n            server.unixtime > server.aof_last_fsync &&\n            !(sync_in_progress = aofFsyncInProgress())) {\n            goto try_fsync;\n        } else {\n            return;\n        }\n    }\n\n    if (server.aof_fsync == AOF_FSYNC_EVERYSEC)\n        sync_in_progress = aofFsyncInProgress();\n\n    if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {\n        /* With this append fsync policy we do background fsyncing.\n         * If the fsync is still in progress we can try to delay\n         * the write for a couple of seconds. */\n        if (sync_in_progress) {\n            if (server.aof_flush_postponed_start == 0) {\n                /* No previous write postponing, remember that we are\n                 * postponing the flush and return. */\n                server.aof_flush_postponed_start = server.unixtime;\n                return;\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                return;\n            }\n            /* Otherwise fall trough, and go write since we can't wait\n             * over two seconds. */\n            server.aof_delayed_fsync++;\n            serverLog(LL_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    /* 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     * 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    if (server.aof_flush_sleep && sdslen(server.aof_buf)) {\n        usleep(server.aof_flush_sleep);\n    }\n\n    latencyStartMonitor(latency);\n    nwritten = aofWrite(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));\n    latencyEndMonitor(latency);\n    /* We want to capture different events for delayed writes:\n     * when the delay happens with a pending fsync, or with a saving child\n     * active, and when the above two conditions are missing.\n     * We also use an additional event name to save all samples which is\n     * useful for graphing / monitoring purposes. */\n    if (sync_in_progress) {\n        latencyAddSampleIfNeeded(\"aof-write-pending-fsync\",latency);\n    } else if (hasActiveChildProcess()) {\n        latencyAddSampleIfNeeded(\"aof-write-active-child\",latency);\n    } else {\n        latencyAddSampleIfNeeded(\"aof-write-alone\",latency);\n    }\n    latencyAddSampleIfNeeded(\"aof-write\",latency);\n\n    /* We performed the write so reset the postponed flush sentinel to zero. */\n    server.aof_flush_postponed_start = 0;\n\n    if (nwritten != (ssize_t)sdslen(server.aof_buf)) {\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        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        /* Log the AOF write error and record the error code. */\n        if (nwritten == -1) {\n            if (can_log) {\n                serverLog(LL_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                serverLog(LL_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            if (ftruncate(server.aof_fd, server.aof_current_size) == -1) {\n                if (can_log) {\n                    serverLog(LL_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 ftruncate() 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        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 synced on disk. */\n            serverLog(LL_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 = C_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        if (server.aof_last_write_status == C_ERR) {\n            serverLog(LL_WARNING,\n                \"AOF write error looks solved, Redis can write again.\");\n            server.aof_last_write_status = C_OK;\n        }\n    }\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    if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {\n        sdsclear(server.aof_buf);\n    } else {\n        sdsfree(server.aof_buf);\n        server.aof_buf = sdsempty();\n    }\n\ntry_fsync:\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    if (server.aof_no_fsync_on_rewrite && hasActiveChildProcess())\n        return;\n\n    /* Perform the fsync if needed. */\n    if (server.aof_fsync == AOF_FSYNC_ALWAYS) {\n        /* redis_fsync is defined as fdatasync() for Linux in order to avoid\n         * flushing metadata. */\n        latencyStartMonitor(latency);\n        redis_fsync(server.aof_fd); /* Let's try to get this data on the disk */\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"aof-fsync-always\",latency);\n        server.aof_fsync_offset = server.aof_current_size;\n        server.aof_last_fsync = server.unixtime;\n    } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&\n                server.unixtime > server.aof_last_fsync)) {\n        if (!sync_in_progress) {\n            aof_background_fsync(server.aof_fd);\n            server.aof_fsync_offset = server.aof_current_size;\n        }\n        server.aof_last_fsync = server.unixtime;\n    }\n}\n\nsds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) {\n    char buf[32];\n    int len, j;\n    robj *o;\n\n    buf[0] = '*';\n    len = 1+ll2string(buf+1,sizeof(buf)-1,argc);\n    buf[len++] = '\\r';\n    buf[len++] = '\\n';\n    dst = sdscatlen(dst,buf,len);\n\n    for (j = 0; j < argc; j++) {\n        o = getDecodedObject(argv[j]);\n        buf[0] = '$';\n        len = 1+ll2string(buf+1,sizeof(buf)-1,sdslen(o->ptr));\n        buf[len++] = '\\r';\n        buf[len++] = '\\n';\n        dst = sdscatlen(dst,buf,len);\n        dst = sdscatlen(dst,o->ptr,sdslen(o->ptr));\n        dst = sdscatlen(dst,\"\\r\\n\",2);\n        decrRefCount(o);\n    }\n    return dst;\n}\n\n/* Create the sds representation of an PEXPIREAT command, using\n * 'seconds' as time to live and 'cmd' to understand what command\n * we are translating into a PEXPIREAT.\n *\n * This command is used in order to translate EXPIRE and PEXPIRE commands\n * into PEXPIREAT command so that we retain precision in the append only\n * file, and the time is always absolute and not relative. */\nsds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, robj *seconds) {\n    long long when;\n    robj *argv[3];\n\n    /* Make sure we can use strtoll */\n    seconds = getDecodedObject(seconds);\n    when = strtoll(seconds->ptr,NULL,10);\n    /* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT */\n    if (cmd->proc == expireCommand || cmd->proc == setexCommand ||\n        cmd->proc == expireatCommand)\n    {\n        when *= 1000;\n    }\n    /* Convert into absolute time for EXPIRE, PEXPIRE, SETEX, PSETEX */\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == setexCommand || cmd->proc == psetexCommand)\n    {\n        when += mstime();\n    }\n    decrRefCount(seconds);\n\n    argv[0] = createStringObject(\"PEXPIREAT\",9);\n    argv[1] = key;\n    argv[2] = createStringObjectFromLongLong(when);\n    buf = catAppendOnlyGenericCommand(buf, 3, argv);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[2]);\n    return buf;\n}\n\nvoid feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {\n    sds buf = sdsempty();\n    robj *tmpargv[3];\n\n    /* The DB this command was targeting is not the same as the last command\n     * we appended. To issue a SELECT command is needed. */\n    if (dictid != server.aof_selected_db) {\n        char seldb[64];\n\n        snprintf(seldb,sizeof(seldb),\"%d\",dictid);\n        buf = sdscatprintf(buf,\"*2\\r\\n$6\\r\\nSELECT\\r\\n$%lu\\r\\n%s\\r\\n\",\n            (unsigned long)strlen(seldb),seldb);\n        server.aof_selected_db = dictid;\n    }\n\n    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||\n        cmd->proc == expireatCommand) {\n        /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n    } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {\n        /* Translate SETEX/PSETEX to SET and PEXPIREAT */\n        tmpargv[0] = createStringObject(\"SET\",3);\n        tmpargv[1] = argv[1];\n        tmpargv[2] = argv[3];\n        buf = catAppendOnlyGenericCommand(buf,3,tmpargv);\n        decrRefCount(tmpargv[0]);\n        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);\n    } else if (cmd->proc == setCommand && argc > 3) {\n        int i;\n        robj *exarg = NULL, *pxarg = NULL;\n        /* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */\n        buf = catAppendOnlyGenericCommand(buf,3,argv);\n        for (i = 3; i < argc; i ++) {\n            if (!strcasecmp(argv[i]->ptr, \"ex\")) exarg = argv[i+1];\n            if (!strcasecmp(argv[i]->ptr, \"px\")) pxarg = argv[i+1];\n        }\n        serverAssert(!(exarg && pxarg));\n        if (exarg)\n            buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],\n                                               exarg);\n        if (pxarg)\n            buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],\n                                               pxarg);\n    } else {\n        /* All the other commands don't need translation or need the\n         * same translation already operated in the command vector\n         * for the replication itself. */\n        buf = catAppendOnlyGenericCommand(buf,argc,argv);\n    }\n\n    /* Append to the AOF buffer. This will be flushed on disk just before\n     * of re-entering the event loop, so before the client will get a\n     * positive reply about the operation performed. */\n    if (server.aof_state == AOF_ON)\n        server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf));\n\n    /* If a background append only file rewriting is in progress we want to\n     * accumulate the differences between the child DB and the current one\n     * in a buffer, so that when the child process will do its work we\n     * can append the differences to the new append only file. */\n    if (server.aof_child_pid != -1)\n        aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));\n\n    sdsfree(buf);\n}\n\n/* ----------------------------------------------------------------------------\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. */\nstruct client *createAOFClient(void) {\n    struct client *c = zmalloc(sizeof(*c));\n\n    selectDb(c,0);\n    c->id = CLIENT_ID_AOF; /* So modules can identify it's the AOF client. */\n    c->conn = NULL;\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 = 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    c->replstate = SLAVE_STATE_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    c->resp = 2;\n    c->user = NULL;\n    listSetFreeMethod(c->reply,freeClientReplyValue);\n    listSetDupMethod(c->reply,dupClientReplyValue);\n    initClientMultiState(c);\n    return c;\n}\n\nvoid freeFakeClientArgv(struct client *c) {\n    int j;\n\n    for (j = 0; j < c->argc; j++)\n        decrRefCount(c->argv[j]);\n    zfree(c->argv);\n}\n\nvoid freeFakeClient(struct client *c) {\n    sdsfree(c->querybuf);\n    listRelease(c->reply);\n    listRelease(c->watched_keys);\n    freeClientMultiState(c);\n    zfree(c);\n}\n\n/* Replay the append log file. On success C_OK is returned. On non fatal\n * error (the append only file is zero-length) C_ERR is returned. On\n * fatal error an error message is logged and the program exists. */\nint loadAppendOnlyFile(char *filename) {\n    struct client *fakeClient;\n    FILE *fp = fopen(filename,\"r\");\n    struct redis_stat sb;\n    int old_aof_state = server.aof_state;\n    long loops = 0;\n    off_t valid_up_to = 0; /* Offset of latest well-formed command loaded. */\n    off_t valid_before_multi = 0; /* Offset before MULTI command loaded. */\n\n    if (fp == NULL) {\n        serverLog(LL_WARNING,\"Fatal error: can't open the append log file for reading: %s\",strerror(errno));\n        exit(1);\n    }\n\n    /* Handle a zero-length AOF file as a special case. An empty AOF file\n     * is a valid AOF because an empty server with AOF enabled will create\n     * a zero length file at startup, that will remain like that if no write\n     * operation is received. */\n    if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {\n        server.aof_current_size = 0;\n        server.aof_fsync_offset = server.aof_current_size;\n        fclose(fp);\n        return C_ERR;\n    }\n\n    /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI\n     * to the same file we're about to read. */\n    server.aof_state = AOF_OFF;\n\n    fakeClient = createAOFClient();\n    startLoadingFile(fp, filename, RDBFLAGS_AOF_PREAMBLE);\n\n    /* Check if this AOF file has an RDB preamble. In that case we need to\n     * load the RDB file and later continue loading the AOF tail. */\n    char sig[5]; /* \"REDIS\" */\n    if (fread(sig,1,5,fp) != 5 || memcmp(sig,\"REDIS\",5) != 0) {\n        /* No RDB preamble, seek back at 0 offset. */\n        if (fseek(fp,0,SEEK_SET) == -1) goto readerr;\n    } else {\n        /* RDB preamble. Pass loading the RDB functions. */\n        rio rdb;\n\n        serverLog(LL_NOTICE,\"Reading RDB preamble from AOF file...\");\n        if (fseek(fp,0,SEEK_SET) == -1) goto readerr;\n        rioInitWithFile(&rdb,fp);\n        if (rdbLoadRio(&rdb,RDBFLAGS_AOF_PREAMBLE,NULL) != C_OK) {\n            serverLog(LL_WARNING,\"Error reading the RDB preamble of the AOF file, AOF loading aborted\");\n            goto readerr;\n        } else {\n            serverLog(LL_NOTICE,\"Reading the remaining AOF tail...\");\n        }\n    }\n\n    /* Read the actual AOF file, in REPL format, command by command. */\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        if (!(loops++ % 1000)) {\n            loadingProgress(ftello(fp));\n            processEventsWhileBlocked();\n            processModuleLoadingProgressEvent(1);\n        }\n\n        if (fgets(buf,sizeof(buf),fp) == NULL) {\n            if (feof(fp))\n                break;\n            else\n                goto readerr;\n        }\n        if (buf[0] != '*') goto fmterr;\n        if (buf[1] == '\\0') goto readerr;\n        argc = atoi(buf+1);\n        if (argc < 1) goto fmterr;\n\n        /* Load the next command in the AOF as our fake client\n         * argv. */\n        argv = zmalloc(sizeof(robj*)*argc);\n        fakeClient->argc = argc;\n        fakeClient->argv = argv;\n\n        for (j = 0; j < argc; j++) {\n            /* Parse the argument len. */\n            char *readres = fgets(buf,sizeof(buf),fp);\n            if (readres == NULL || buf[0] != '$') {\n                fakeClient->argc = j; /* Free up to j-1. */\n                freeFakeClientArgv(fakeClient);\n                if (readres == NULL)\n                    goto readerr;\n                else\n                    goto fmterr;\n            }\n            len = strtol(buf+1,NULL,10);\n\n            /* Read it into a string object. */\n            argsds = sdsnewlen(SDS_NOINIT,len);\n            if (len && fread(argsds,len,1,fp) == 0) {\n                sdsfree(argsds);\n                fakeClient->argc = j; /* Free up to j-1. */\n                freeFakeClientArgv(fakeClient);\n                goto readerr;\n            }\n            argv[j] = createObject(OBJ_STRING,argsds);\n\n            /* Discard CRLF. */\n            if (fread(buf,2,1,fp) == 0) {\n                fakeClient->argc = j+1; /* Free up to j. */\n                freeFakeClientArgv(fakeClient);\n                goto readerr;\n            }\n        }\n\n        /* Command lookup */\n        cmd = lookupCommand(argv[0]->ptr);\n        if (!cmd) {\n            serverLog(LL_WARNING,\n                \"Unknown command '%s' reading the append only file\",\n                (char*)argv[0]->ptr);\n            exit(1);\n        }\n\n        if (cmd == server.multiCommand) valid_before_multi = valid_up_to;\n\n        /* Run the command in the context of a fake client */\n        fakeClient->cmd = fakeClient->lastcmd = cmd;\n        if (fakeClient->flags & CLIENT_MULTI &&\n            fakeClient->cmd->proc != execCommand)\n        {\n            queueMultiCommand(fakeClient);\n        } else {\n            cmd->proc(fakeClient);\n        }\n\n        /* The fake client should not have a reply */\n        serverAssert(fakeClient->bufpos == 0 &&\n                     listLength(fakeClient->reply) == 0);\n\n        /* The fake client should never get blocked */\n        serverAssert((fakeClient->flags & CLIENT_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        freeFakeClientArgv(fakeClient);\n        fakeClient->cmd = NULL;\n        if (server.aof_load_truncated) valid_up_to = ftello(fp);\n        if (server.key_load_delay)\n            usleep(server.key_load_delay);\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, handle it as it was\n     * a short read, even if technically the protocol is correct: we want\n     * to remove the unprocessed tail and continue. */\n    if (fakeClient->flags & CLIENT_MULTI) {\n        serverLog(LL_WARNING,\n            \"Revert incomplete MULTI/EXEC transaction in AOF file\");\n        valid_up_to = valid_before_multi;\n        goto uxeof;\n    }\n\nloaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */\n    fclose(fp);\n    freeFakeClient(fakeClient);\n    server.aof_state = old_aof_state;\n    stopLoading(1);\n    aofUpdateCurrentSize();\n    server.aof_rewrite_base_size = server.aof_current_size;\n    server.aof_fsync_offset = server.aof_current_size;\n    return C_OK;\n\nreaderr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */\n    if (!feof(fp)) {\n        if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */\n        fclose(fp);\n        serverLog(LL_WARNING,\"Unrecoverable error reading the append only file: %s\", strerror(errno));\n        exit(1);\n    }\n\nuxeof: /* Unexpected AOF end of file. */\n    if (server.aof_load_truncated) {\n        serverLog(LL_WARNING,\"!!! Warning: short read while loading the AOF file !!!\");\n        serverLog(LL_WARNING,\"!!! Truncating the AOF at offset %llu !!!\",\n            (unsigned long long) valid_up_to);\n        if (valid_up_to == -1 || truncate(filename,valid_up_to) == -1) {\n            if (valid_up_to == -1) {\n                serverLog(LL_WARNING,\"Last valid command offset is invalid\");\n            } else {\n                serverLog(LL_WARNING,\"Error truncating the AOF file: %s\",\n                    strerror(errno));\n            }\n        } else {\n            /* Make sure the AOF file descriptor points to the end of the\n             * file after the truncate call. */\n            if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) {\n                serverLog(LL_WARNING,\"Can't seek the end of the AOF file: %s\",\n                    strerror(errno));\n            } else {\n                serverLog(LL_WARNING,\n                    \"AOF loaded anyway because aof-load-truncated is enabled\");\n                goto loaded_ok;\n            }\n        }\n    }\n    if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */\n    fclose(fp);\n    serverLog(LL_WARNING,\"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.\");\n    exit(1);\n\nfmterr: /* Format error. */\n    if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */\n    fclose(fp);\n    serverLog(LL_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 server.h dependency. */\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 == OBJ_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        serverPanic(\"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. */\nint rewriteListObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = listTypeLength(o);\n\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklist *list = o->ptr;\n        quicklistIter *li = quicklistGetIterator(list, AL_START_HEAD);\n        quicklistEntry entry;\n\n        while (quicklistNext(li,&entry)) {\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    AOF_REWRITE_ITEMS_PER_CMD : items;\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 (entry.value) {\n                if (rioWriteBulkString(r,(char*)entry.value,entry.sz) == 0) return 0;\n            } else {\n                if (rioWriteBulkLongLong(r,entry.longval) == 0) return 0;\n            }\n            if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        quicklistReleaseIterator(li);\n    } else {\n        serverPanic(\"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. */\nint rewriteSetObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = setTypeSize(o);\n\n    if (o->encoding == OBJ_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 > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        dictIterator *di = dictGetIterator(o->ptr);\n        dictEntry *de;\n\n        while((de = dictNext(di)) != NULL) {\n            sds ele = dictGetKey(de);\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;\n            if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        dictReleaseIterator(di);\n    } else {\n        serverPanic(\"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. */\nint rewriteSortedSetObject(rio *r, robj *key, robj *o) {\n    long long count = 0, items = zsetLength(o);\n\n    if (o->encoding == OBJ_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        serverAssert(eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        while (eptr != NULL) {\n            serverAssert(ziplistGet(eptr,&vstr,&vlen,&vll));\n            score = zzlGetScore(sptr);\n\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n    } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = o->ptr;\n        dictIterator *di = dictGetIterator(zs->dict);\n        dictEntry *de;\n\n        while((de = dictNext(di)) != NULL) {\n            sds ele = dictGetKey(de);\n            double *score = dictGetVal(de);\n\n            if (count == 0) {\n                int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                    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 (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;\n            if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n            items--;\n        }\n        dictReleaseIterator(di);\n    } else {\n        serverPanic(\"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 * The 'hi' argument passes a valid Redis hash iterator.\n * The 'what' filed specifies if to write a key or a value and can be\n * either OBJ_HASH_KEY or OBJ_HASH_VALUE.\n *\n * The function returns 0 on error, non-zero on success. */\nstatic int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n        if (vstr)\n            return rioWriteBulkString(r, (char*)vstr, vlen);\n        else\n            return rioWriteBulkLongLong(r, vll);\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        sds value = hashTypeCurrentFromHashTable(hi, what);\n        return rioWriteBulkString(r, value, sdslen(value));\n    }\n\n    serverPanic(\"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. */\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) != C_ERR) {\n        if (count == 0) {\n            int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?\n                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, OBJ_HASH_KEY) == 0) return 0;\n        if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE) == 0) return 0;\n        if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;\n        items--;\n    }\n\n    hashTypeReleaseIterator(hi);\n\n    return 1;\n}\n\n/* Helper for rewriteStreamObject() that generates a bulk string into the\n * AOF representing the ID 'id'. */\nint rioWriteBulkStreamID(rio *r,streamID *id) {\n    int retval;\n\n    sds replyid = sdscatfmt(sdsempty(),\"%U-%U\",id->ms,id->seq);\n    retval = rioWriteBulkString(r,replyid,sdslen(replyid));\n    sdsfree(replyid);\n    return retval;\n}\n\n/* Helper for rewriteStreamObject(): emit the XCLAIM needed in order to\n * add the message described by 'nack' having the id 'rawid', into the pending\n * list of the specified consumer. All this in the context of the specified\n * key and group. */\nint rioWriteStreamPendingEntry(rio *r, robj *key, const char *groupname, size_t groupname_len, streamConsumer *consumer, unsigned char *rawid, streamNACK *nack) {\n     /* XCLAIM <key> <group> <consumer> 0 <id> TIME <milliseconds-unix-time>\n               RETRYCOUNT <count> JUSTID FORCE. */\n    streamID id;\n    streamDecodeID(rawid,&id);\n    if (rioWriteBulkCount(r,'*',12) == 0) return 0;\n    if (rioWriteBulkString(r,\"XCLAIM\",6) == 0) return 0;\n    if (rioWriteBulkObject(r,key) == 0) return 0;\n    if (rioWriteBulkString(r,groupname,groupname_len) == 0) return 0;\n    if (rioWriteBulkString(r,consumer->name,sdslen(consumer->name)) == 0) return 0;\n    if (rioWriteBulkString(r,\"0\",1) == 0) return 0;\n    if (rioWriteBulkStreamID(r,&id) == 0) return 0;\n    if (rioWriteBulkString(r,\"TIME\",4) == 0) return 0;\n    if (rioWriteBulkLongLong(r,nack->delivery_time) == 0) return 0;\n    if (rioWriteBulkString(r,\"RETRYCOUNT\",10) == 0) return 0;\n    if (rioWriteBulkLongLong(r,nack->delivery_count) == 0) return 0;\n    if (rioWriteBulkString(r,\"JUSTID\",6) == 0) return 0;\n    if (rioWriteBulkString(r,\"FORCE\",5) == 0) return 0;\n    return 1;\n}\n\n/* Emit the commands needed to rebuild a stream object.\n * The function returns 0 on error, 1 on success. */\nint rewriteStreamObject(rio *r, robj *key, robj *o) {\n    stream *s = o->ptr;\n    streamIterator si;\n    streamIteratorStart(&si,s,NULL,NULL,0);\n    streamID id;\n    int64_t numfields;\n\n    if (s->length) {\n        /* Reconstruct the stream data using XADD commands. */\n        while(streamIteratorGetID(&si,&id,&numfields)) {\n            /* Emit a two elements array for each item. The first is\n             * the ID, the second is an array of field-value pairs. */\n\n            /* Emit the XADD <key> <id> ...fields... command. */\n            if (rioWriteBulkCount(r,'*',3+numfields*2) == 0) return 0;\n            if (rioWriteBulkString(r,\"XADD\",4) == 0) return 0;\n            if (rioWriteBulkObject(r,key) == 0) return 0;\n            if (rioWriteBulkStreamID(r,&id) == 0) return 0;\n            while(numfields--) {\n                unsigned char *field, *value;\n                int64_t field_len, value_len;\n                streamIteratorGetField(&si,&field,&value,&field_len,&value_len);\n                if (rioWriteBulkString(r,(char*)field,field_len) == 0) return 0;\n                if (rioWriteBulkString(r,(char*)value,value_len) == 0) return 0;\n            }\n        }\n    } else {\n        /* Use the XADD MAXLEN 0 trick to generate an empty stream if\n         * the key we are serializing is an empty string, which is possible\n         * for the Stream type. */\n        id.ms = 0; id.seq = 1; \n        if (rioWriteBulkCount(r,'*',7) == 0) return 0;\n        if (rioWriteBulkString(r,\"XADD\",4) == 0) return 0;\n        if (rioWriteBulkObject(r,key) == 0) return 0;\n        if (rioWriteBulkString(r,\"MAXLEN\",6) == 0) return 0;\n        if (rioWriteBulkString(r,\"0\",1) == 0) return 0;\n        if (rioWriteBulkStreamID(r,&id) == 0) return 0;\n        if (rioWriteBulkString(r,\"x\",1) == 0) return 0;\n        if (rioWriteBulkString(r,\"y\",1) == 0) return 0;\n    }\n\n    /* Append XSETID after XADD, make sure lastid is correct,\n     * in case of XDEL lastid. */\n    if (rioWriteBulkCount(r,'*',3) == 0) return 0;\n    if (rioWriteBulkString(r,\"XSETID\",6) == 0) return 0;\n    if (rioWriteBulkObject(r,key) == 0) return 0;\n    if (rioWriteBulkStreamID(r,&s->last_id) == 0) return 0;\n\n\n    /* Create all the stream consumer groups. */\n    if (s->cgroups) {\n        raxIterator ri;\n        raxStart(&ri,s->cgroups);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            streamCG *group = ri.data;\n            /* Emit the XGROUP CREATE in order to create the group. */\n            if (rioWriteBulkCount(r,'*',5) == 0) return 0;\n            if (rioWriteBulkString(r,\"XGROUP\",6) == 0) return 0;\n            if (rioWriteBulkString(r,\"CREATE\",6) == 0) return 0;\n            if (rioWriteBulkObject(r,key) == 0) return 0;\n            if (rioWriteBulkString(r,(char*)ri.key,ri.key_len) == 0) return 0;\n            if (rioWriteBulkStreamID(r,&group->last_id) == 0) return 0;\n\n            /* Generate XCLAIMs for each consumer that happens to\n             * have pending entries. Empty consumers have no semantical\n             * value so they are discarded. */\n            raxIterator ri_cons;\n            raxStart(&ri_cons,group->consumers);\n            raxSeek(&ri_cons,\"^\",NULL,0);\n            while(raxNext(&ri_cons)) {\n                streamConsumer *consumer = ri_cons.data;\n                /* For the current consumer, iterate all the PEL entries\n                 * to emit the XCLAIM protocol. */\n                raxIterator ri_pel;\n                raxStart(&ri_pel,consumer->pel);\n                raxSeek(&ri_pel,\"^\",NULL,0);\n                while(raxNext(&ri_pel)) {\n                    streamNACK *nack = ri_pel.data;\n                    if (rioWriteStreamPendingEntry(r,key,(char*)ri.key,\n                                                   ri.key_len,consumer,\n                                                   ri_pel.key,nack) == 0)\n                    {\n                        return 0;\n                    }\n                }\n                raxStop(&ri_pel);\n            }\n            raxStop(&ri_cons);\n        }\n        raxStop(&ri);\n    }\n\n    streamIteratorStop(&si);\n    return 1;\n}\n\n/* Call the module type callback in order to rewrite a data type\n * that is exported by a module and is not handled by Redis itself.\n * The function returns 0 on error, 1 on success. */\nint rewriteModuleObject(rio *r, robj *key, robj *o) {\n    RedisModuleIO io;\n    moduleValue *mv = o->ptr;\n    moduleType *mt = mv->type;\n    moduleInitIOContext(io,mt,r,key);\n    mt->aof_rewrite(&io,key,mv->value);\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    return io.error ? 0 : 1;\n}\n\n/* This function is called by the child rewriting the AOF file to read\n * the difference accumulated from the parent into a buffer, that is\n * concatenated at the end of the rewrite. */\nssize_t aofReadDiffFromParent(void) {\n    char buf[65536]; /* Default pipe buffer size on most Linux systems. */\n    ssize_t nread, total = 0;\n\n    while ((nread =\n            read(server.aof_pipe_read_data_from_parent,buf,sizeof(buf))) > 0) {\n        server.aof_child_diff = sdscatlen(server.aof_child_diff,buf,nread);\n        total += nread;\n    }\n    return total;\n}\n\nint rewriteAppendOnlyFileRio(rio *aof) {\n    dictIterator *di = NULL;\n    dictEntry *de;\n    size_t processed = 0;\n    int j;\n\n    for (j = 0; j < server.dbnum; j++) {\n        char selectcmd[] = \"*2\\r\\n$6\\r\\nSELECT\\r\\n\";\n        redisDb *db = server.db+j;\n        dict *d = db->dict;\n        if (dictSize(d) == 0) continue;\n        di = dictGetSafeIterator(d);\n\n        /* SELECT the new DB */\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        while((de = dictNext(di)) != NULL) {\n            sds keystr;\n            robj key, *o;\n            long long expiretime;\n\n            keystr = dictGetKey(de);\n            o = dictGetVal(de);\n            initStaticStringObject(key,keystr);\n\n            expiretime = getExpire(db,&key);\n\n            /* Save the key and associated value */\n            if (o->type == OBJ_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 == OBJ_LIST) {\n                if (rewriteListObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_SET) {\n                if (rewriteSetObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_ZSET) {\n                if (rewriteSortedSetObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_HASH) {\n                if (rewriteHashObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_STREAM) {\n                if (rewriteStreamObject(aof,&key,o) == 0) goto werr;\n            } else if (o->type == OBJ_MODULE) {\n                if (rewriteModuleObject(aof,&key,o) == 0) goto werr;\n            } else {\n                serverPanic(\"Unknown object type\");\n            }\n            /* Save the expire time */\n            if (expiretime != -1) {\n                char cmd[]=\"*3\\r\\n$9\\r\\nPEXPIREAT\\r\\n\";\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            /* Read some diff from the parent process from time to time. */\n            if (aof->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES) {\n                processed = aof->processed_bytes;\n                aofReadDiffFromParent();\n            }\n        }\n        dictReleaseIterator(di);\n        di = NULL;\n    }\n    return C_OK;\n\nwerr:\n    if (di) dictReleaseIterator(di);\n    return C_ERR;\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 * 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 AOF_REWRITE_ITEMS_PER_CMD items per time\n * are inserted using a single command. */\nint rewriteAppendOnlyFile(char *filename) {\n    rio aof;\n    FILE *fp;\n    char tmpfile[256];\n    char byte;\n\n    /* Note that we have to use a different temp name here compared to the\n     * one used by rewriteAppendOnlyFileBackground() function. */\n    snprintf(tmpfile,256,\"temp-rewriteaof-%d.aof\", (int) getpid());\n    fp = fopen(tmpfile,\"w\");\n    if (!fp) {\n        serverLog(LL_WARNING, \"Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s\", strerror(errno));\n        return C_ERR;\n    }\n\n    server.aof_child_diff = sdsempty();\n    rioInitWithFile(&aof,fp);\n\n    if (server.aof_rewrite_incremental_fsync)\n        rioSetAutoSync(&aof,REDIS_AUTOSYNC_BYTES);\n\n    startSaving(RDBFLAGS_AOF_PREAMBLE);\n\n    if (server.aof_use_rdb_preamble) {\n        int error;\n        if (rdbSaveRio(&aof,&error,RDBFLAGS_AOF_PREAMBLE,NULL) == C_ERR) {\n            errno = error;\n            goto werr;\n        }\n    } else {\n        if (rewriteAppendOnlyFileRio(&aof) == C_ERR) goto werr;\n    }\n\n    /* Do an initial slow fsync here while the parent is still sending\n     * data, in order to make the next final fsync faster. */\n    if (fflush(fp) == EOF) goto werr;\n    if (fsync(fileno(fp)) == -1) goto werr;\n\n    /* Read again a few times to get more data from the parent.\n     * We can't read forever (the server may receive data from clients\n     * faster than it is able to send data to the child), so we try to read\n     * some more data in a loop as soon as there is a good chance more data\n     * will come. If it looks like we are wasting time, we abort (this\n     * happens after 20 ms without new data). */\n    int nodata = 0;\n    mstime_t start = mstime();\n    while(mstime()-start < 1000 && nodata < 20) {\n        if (aeWait(server.aof_pipe_read_data_from_parent, AE_READABLE, 1) <= 0)\n        {\n            nodata++;\n            continue;\n        }\n        nodata = 0; /* Start counting from zero, we stop on N *contiguous*\n                       timeouts. */\n        aofReadDiffFromParent();\n    }\n\n    /* Ask the master to stop sending diffs. */\n    if (write(server.aof_pipe_write_ack_to_parent,\"!\",1) != 1) goto werr;\n    if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK)\n        goto werr;\n    /* We read the ACK from the server using a 10 seconds timeout. Normally\n     * it should reply ASAP, but just in case we lose its reply, we are sure\n     * the child will eventually get terminated. */\n    if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||\n        byte != '!') goto werr;\n    serverLog(LL_NOTICE,\"Parent agreed to stop sending diffs. Finalizing AOF...\");\n\n    /* Read the final diff if any. */\n    aofReadDiffFromParent();\n\n    /* Write the received diff to the file. */\n    serverLog(LL_NOTICE,\n        \"Concatenating %.2f MB of AOF diff received from parent.\",\n        (double) sdslen(server.aof_child_diff) / (1024*1024));\n    if (rioWrite(&aof,server.aof_child_diff,sdslen(server.aof_child_diff)) == 0)\n        goto werr;\n\n    /* Make sure data will not remain on the OS's output buffers */\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    if (rename(tmpfile,filename) == -1) {\n        serverLog(LL_WARNING,\"Error moving temp append only file on the final destination: %s\", strerror(errno));\n        unlink(tmpfile);\n        stopSaving(0);\n        return C_ERR;\n    }\n    serverLog(LL_NOTICE,\"SYNC append only file rewrite performed\");\n    stopSaving(1);\n    return C_OK;\n\nwerr:\n    serverLog(LL_WARNING,\"Write error writing append only file on disk: %s\", strerror(errno));\n    fclose(fp);\n    unlink(tmpfile);\n    stopSaving(0);\n    return C_ERR;\n}\n\n/* ----------------------------------------------------------------------------\n * AOF rewrite pipes for IPC\n * -------------------------------------------------------------------------- */\n\n/* This event handler is called when the AOF rewriting child sends us a\n * single '!' char to signal we should stop sending buffer diffs. The\n * parent sends a '!' as well to acknowledge. */\nvoid aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {\n    char byte;\n    UNUSED(el);\n    UNUSED(privdata);\n    UNUSED(mask);\n\n    if (read(fd,&byte,1) == 1 && byte == '!') {\n        serverLog(LL_NOTICE,\"AOF rewrite child asks to stop sending diffs.\");\n        server.aof_stop_sending_diff = 1;\n        if (write(server.aof_pipe_write_ack_to_child,\"!\",1) != 1) {\n            /* If we can't send the ack, inform the user, but don't try again\n             * since in the other side the children will use a timeout if the\n             * kernel can't buffer our write, or, the children was\n             * terminated. */\n            serverLog(LL_WARNING,\"Can't send ACK to AOF child: %s\",\n                strerror(errno));\n        }\n    }\n    /* Remove the handler since this can be called only one time during a\n     * rewrite. */\n    aeDeleteFileEvent(server.el,server.aof_pipe_read_ack_from_child,AE_READABLE);\n}\n\n/* Create the pipes used for parent - child process IPC during rewrite.\n * We have a data pipe used to send AOF incremental diffs to the child,\n * and two other pipes used by the children to signal it finished with\n * the rewrite so no more data should be written, and another for the\n * parent to acknowledge it understood this new condition. */\nint aofCreatePipes(void) {\n    int fds[6] = {-1, -1, -1, -1, -1, -1};\n    int j;\n\n    if (pipe(fds) == -1) goto error; /* parent -> children data. */\n    if (pipe(fds+2) == -1) goto error; /* children -> parent ack. */\n    if (pipe(fds+4) == -1) goto error; /* parent -> children ack. */\n    /* Parent -> children data is non blocking. */\n    if (anetNonBlock(NULL,fds[0]) != ANET_OK) goto error;\n    if (anetNonBlock(NULL,fds[1]) != ANET_OK) goto error;\n    if (aeCreateFileEvent(server.el, fds[2], AE_READABLE, aofChildPipeReadable, NULL) == AE_ERR) goto error;\n\n    server.aof_pipe_write_data_to_child = fds[1];\n    server.aof_pipe_read_data_from_parent = fds[0];\n    server.aof_pipe_write_ack_to_parent = fds[3];\n    server.aof_pipe_read_ack_from_child = fds[2];\n    server.aof_pipe_write_ack_to_child = fds[5];\n    server.aof_pipe_read_ack_from_parent = fds[4];\n    server.aof_stop_sending_diff = 0;\n    return C_OK;\n\nerror:\n    serverLog(LL_WARNING,\"Error opening /setting AOF rewrite IPC pipes: %s\",\n        strerror(errno));\n    for (j = 0; j < 6; j++) if(fds[j] != -1) close(fds[j]);\n    return C_ERR;\n}\n\nvoid aofClosePipes(void) {\n    aeDeleteFileEvent(server.el,server.aof_pipe_read_ack_from_child,AE_READABLE);\n    aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child,AE_WRITABLE);\n    close(server.aof_pipe_write_data_to_child);\n    close(server.aof_pipe_read_data_from_parent);\n    close(server.aof_pipe_write_ack_to_parent);\n    close(server.aof_pipe_read_ack_from_child);\n    close(server.aof_pipe_write_ack_to_child);\n    close(server.aof_pipe_read_ack_from_parent);\n}\n\n/* ----------------------------------------------------------------------------\n * AOF background rewrite\n * ------------------------------------------------------------------------- */\n\n/* This is how rewriting of the append only file in background works:\n *\n * 1) The user calls BGREWRITEAOF\n * 2) Redis calls this function, that forks():\n *    2a) the child rewrite the append only file in a temp file.\n *    2b) the parent accumulates differences in server.aof_rewrite_buf.\n * 3) When the child finished '2a' exists.\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 */\nint rewriteAppendOnlyFileBackground(void) {\n    pid_t childpid;\n\n    if (hasActiveChildProcess()) return C_ERR;\n    if (aofCreatePipes() != C_OK) return C_ERR;\n    openChildInfoPipe();\n    if ((childpid = redisFork()) == 0) {\n        char tmpfile[256];\n\n        /* Child */\n        redisSetProcTitle(\"redis-aof-rewrite\");\n        redisSetCpuAffinity(server.aof_rewrite_cpulist);\n        snprintf(tmpfile,256,\"temp-rewriteaof-bg-%d.aof\", (int) getpid());\n        if (rewriteAppendOnlyFile(tmpfile) == C_OK) {\n            sendChildCOWInfo(CHILD_INFO_TYPE_AOF, \"AOF rewrite\");\n            exitFromChild(0);\n        } else {\n            exitFromChild(1);\n        }\n    } else {\n        /* Parent */\n        if (childpid == -1) {\n            closeChildInfoPipe();\n            serverLog(LL_WARNING,\n                \"Can't rewrite append only file in background: fork: %s\",\n                strerror(errno));\n            aofClosePipes();\n            return C_ERR;\n        }\n        serverLog(LL_NOTICE,\n            \"Background append only file rewriting started by pid %d\",childpid);\n        server.aof_rewrite_scheduled = 0;\n        server.aof_rewrite_time_start = time(NULL);\n        server.aof_child_pid = childpid;\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        server.aof_selected_db = -1;\n        replicationScriptCacheFlush();\n        return C_OK;\n    }\n    return C_OK; /* unreached */\n}\n\nvoid bgrewriteaofCommand(client *c) {\n    if (server.aof_child_pid != -1) {\n        addReplyError(c,\"Background append only file rewriting already in progress\");\n    } else if (hasActiveChildProcess()) {\n        server.aof_rewrite_scheduled = 1;\n        addReplyStatus(c,\"Background append only file rewriting scheduled\");\n    } else if (rewriteAppendOnlyFileBackground() == C_OK) {\n        addReplyStatus(c,\"Background append only file rewriting started\");\n    } else {\n        addReplyError(c,\"Can't execute an AOF background rewriting. \"\n                        \"Please check the server logs for more information.\");\n    }\n}\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    snprintf(tmpfile,256,\"temp-rewriteaof-%d.aof\", (int) childpid);\n    unlink(tmpfile);\n}\n\n/* Update the server.aof_current_size field 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. */\nvoid aofUpdateCurrentSize(void) {\n    struct redis_stat sb;\n    mstime_t latency;\n\n    latencyStartMonitor(latency);\n    if (redis_fstat(server.aof_fd,&sb) == -1) {\n        serverLog(LL_WARNING,\"Unable to obtain the AOF file length. stat: %s\",\n            strerror(errno));\n    } else {\n        server.aof_current_size = sb.st_size;\n    }\n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"aof-fstat\",latency);\n}\n\n/* A background append only file rewriting (BGREWRITEAOF) terminated its work.\n * Handle this. */\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        mstime_t latency;\n\n        serverLog(LL_NOTICE,\n            \"Background AOF rewrite terminated with success\");\n\n        /* Flush the differences accumulated by the parent to the\n         * rewritten AOF. */\n        latencyStartMonitor(latency);\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            serverLog(LL_WARNING,\n                \"Unable to open the temporary AOF produced by the child: %s\", strerror(errno));\n            goto cleanup;\n        }\n\n        if (aofRewriteBufferWrite(newfd) == -1) {\n            serverLog(LL_WARNING,\n                \"Error trying to flush the parent diff to the rewritten AOF: %s\", strerror(errno));\n            close(newfd);\n            goto cleanup;\n        }\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"aof-rewrite-diff-write\",latency);\n\n        serverLog(LL_NOTICE,\n            \"Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)\", (double) aofRewriteBufferSize() / (1024*1024));\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         * There are two possible scenarios:\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         * 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         * 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        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        latencyStartMonitor(latency);\n        if (rename(tmpfile,server.aof_filename) == -1) {\n            serverLog(LL_WARNING,\n                \"Error trying to rename the temporary AOF file %s into %s: %s\",\n                tmpfile,\n                server.aof_filename,\n                strerror(errno));\n            close(newfd);\n            if (oldfd != -1) close(oldfd);\n            goto cleanup;\n        }\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"aof-rename\",latency);\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            close(newfd);\n        } else {\n            /* AOF enabled, replace the old fd with the new one. */\n            oldfd = server.aof_fd;\n            server.aof_fd = newfd;\n            if (server.aof_fsync == AOF_FSYNC_ALWAYS)\n                redis_fsync(newfd);\n            else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)\n                aof_background_fsync(newfd);\n            server.aof_selected_db = -1; /* Make sure SELECT is re-issued */\n            aofUpdateCurrentSize();\n            server.aof_rewrite_base_size = server.aof_current_size;\n            server.aof_fsync_offset = 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            sdsfree(server.aof_buf);\n            server.aof_buf = sdsempty();\n        }\n\n        server.aof_lastbgrewrite_status = C_OK;\n\n        serverLog(LL_NOTICE, \"Background AOF rewrite finished successfully\");\n        /* Change state from WAIT_REWRITE to ON if needed */\n        if (server.aof_state == AOF_WAIT_REWRITE)\n            server.aof_state = AOF_ON;\n\n        /* Asynchronously close the overwritten AOF. */\n        if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);\n\n        serverLog(LL_VERBOSE,\n            \"Background AOF rewrite signal handler took %lldus\", ustime()-now);\n    } else if (!bysignal && exitcode != 0) {\n        server.aof_lastbgrewrite_status = C_ERR;\n\n        serverLog(LL_WARNING,\n            \"Background AOF rewrite terminated with error\");\n    } else {\n        /* SIGUSR1 is whitelisted, so we have a way to kill a child without\n         * tirggering an error condition. */\n        if (bysignal != SIGUSR1)\n            server.aof_lastbgrewrite_status = C_ERR;\n\n        serverLog(LL_WARNING,\n            \"Background AOF rewrite terminated by signal %d\", bysignal);\n    }\n\ncleanup:\n    aofClosePipes();\n    aofRewriteBufferReset();\n    aofRemoveTempFile(server.aof_child_pid);\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    /* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */\n    if (server.aof_state == AOF_WAIT_REWRITE)\n        server.aof_rewrite_scheduled = 1;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\nconst char *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": "Chapter07/redis-6.0.3/src/atomicvar.h",
    "content": "/* This file implements atomic counters using __atomic or __sync macros if\n * available, otherwise synchronizing different threads using a mutex.\n *\n * The exported interface is composed of three macros:\n *\n * atomicIncr(var,count) -- Increment the atomic counter\n * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter\n * atomicDecr(var,count) -- Decrement the atomic counter\n * atomicGet(var,dstvar) -- Fetch the atomic counter value\n * atomicSet(var,value)  -- Set the atomic counter value\n *\n * The variable 'var' should also have a declared mutex with the same\n * name and the \"_mutex\" postfix, for instance:\n *\n *  long myvar;\n *  pthread_mutex_t myvar_mutex;\n *  atomicSet(myvar,12345);\n *\n * If atomic primitives are available (tested in config.h) the mutex\n * is not used.\n *\n * Never use return value from the macros, instead use the AtomicGetIncr()\n * if you need to get the current value and increment it atomically, like\n * in the followign example:\n *\n *  long oldvalue;\n *  atomicGetIncr(myvar,oldvalue,1);\n *  doSomethingWith(oldvalue);\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2015, 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 <pthread.h>\n\n#ifndef __ATOMIC_VAR_H\n#define __ATOMIC_VAR_H\n\n/* To test Redis with Helgrind (a Valgrind tool) it is useful to define\n * the following macro, so that __sync macros are used: those can be detected\n * by Helgrind (even if they are less efficient) so that no false positive\n * is reported. */\n// #define __ATOMIC_VAR_FORCE_SYNC_MACROS\n\n#if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)\n/* Implementation using __atomic macros. */\n\n#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \\\n} while(0)\n#define atomicDecr(var,count) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED)\n#define atomicGet(var,dstvar) do { \\\n    dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \\\n} while(0)\n#define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED)\n#define REDIS_ATOMIC_API \"atomic-builtin\"\n\n#elif defined(HAVE_ATOMIC)\n/* Implementation using __sync macros. */\n\n#define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    oldvalue_var = __sync_fetch_and_add(&var,(count)); \\\n} while(0)\n#define atomicDecr(var,count) __sync_sub_and_fetch(&var,(count))\n#define atomicGet(var,dstvar) do { \\\n    dstvar = __sync_sub_and_fetch(&var,0); \\\n} while(0)\n#define atomicSet(var,value) do { \\\n    while(!__sync_bool_compare_and_swap(&var,var,value)); \\\n} while(0)\n#define REDIS_ATOMIC_API \"sync-builtin\"\n\n#else\n/* Implementation using pthread mutex. */\n\n#define atomicIncr(var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var += (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    oldvalue_var = var; \\\n    var += (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicDecr(var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var -= (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicGet(var,dstvar) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    dstvar = var; \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicSet(var,value) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var = value; \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define REDIS_ATOMIC_API \"pthread-mutex\"\n\n#endif\n#endif /* __ATOMIC_VAR_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/bio.c",
    "content": "/* Background I/O service for Redis.\n *\n * This file implements operations that we need to perform in the background.\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 * 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 * 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 waits for new jobs in its queue, and process every job\n * sequentially.\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 * 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 * 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 \"server.h\"\n#include \"bio.h\"\n\nstatic pthread_t bio_threads[BIO_NUM_OPS];\nstatic pthread_mutex_t bio_mutex[BIO_NUM_OPS];\nstatic pthread_cond_t bio_newjob_cond[BIO_NUM_OPS];\nstatic pthread_cond_t bio_step_cond[BIO_NUM_OPS];\nstatic list *bio_jobs[BIO_NUM_OPS];\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. */\nstatic unsigned long long bio_pending[BIO_NUM_OPS];\n\n/* This structure represents a background Job. It is only used locally to this\n * file as the API does not expose the internals at all. */\nstruct bio_job {\n    time_t time; /* Time at which the job was created. */\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    void *arg1, *arg2, *arg3;\n};\n\nvoid *bioProcessBackgroundJobs(void *arg);\nvoid lazyfreeFreeObjectFromBioThread(robj *o);\nvoid lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2);\nvoid lazyfreeFreeSlotsMapFromBioThread(zskiplist *sl);\n\n/* Make sure we have enough stack to perform all the things we do in the\n * main thread. */\n#define REDIS_THREAD_STACK_SIZE (1024*1024*4)\n\n/* Initialize the background system, spawning the thread. */\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    for (j = 0; j < BIO_NUM_OPS; j++) {\n        pthread_mutex_init(&bio_mutex[j],NULL);\n        pthread_cond_init(&bio_newjob_cond[j],NULL);\n        pthread_cond_init(&bio_step_cond[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    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    for (j = 0; j < BIO_NUM_OPS; j++) {\n        void *arg = (void*)(unsigned long) j;\n        if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {\n            serverLog(LL_WARNING,\"Fatal: Can't initialize Background Jobs.\");\n            exit(1);\n        }\n        bio_threads[j] = thread;\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    pthread_mutex_lock(&bio_mutex[type]);\n    listAddNodeTail(bio_jobs[type],job);\n    bio_pending[type]++;\n    pthread_cond_signal(&bio_newjob_cond[type]);\n    pthread_mutex_unlock(&bio_mutex[type]);\n}\n\nvoid *bioProcessBackgroundJobs(void *arg) {\n    struct bio_job *job;\n    unsigned long type = (unsigned long) arg;\n    sigset_t sigset;\n\n    /* Check that the type is within the right interval. */\n    if (type >= BIO_NUM_OPS) {\n        serverLog(LL_WARNING,\n            \"Warning: bio thread started with wrong type %lu\",type);\n        return NULL;\n    }\n\n    switch (type) {\n    case BIO_CLOSE_FILE:\n        redis_set_thread_title(\"bio_close_file\");\n        break;\n    case BIO_AOF_FSYNC:\n        redis_set_thread_title(\"bio_aof_fsync\");\n        break;\n    case BIO_LAZY_FREE:\n        redis_set_thread_title(\"bio_lazy_free\");\n        break;\n    }\n\n    redisSetCpuAffinity(server.bio_cpulist);\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        serverLog(LL_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_newjob_cond[type],&bio_mutex[type]);\n            continue;\n        }\n        /* Pop the job from the queue. */\n        ln = listFirst(bio_jobs[type]);\n        job = ln->value;\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        if (type == BIO_CLOSE_FILE) {\n            close((long)job->arg1);\n        } else if (type == BIO_AOF_FSYNC) {\n            redis_fsync((long)job->arg1);\n        } else if (type == BIO_LAZY_FREE) {\n            /* What we free changes depending on what arguments are set:\n             * arg1 -> free the object at pointer.\n             * arg2 & arg3 -> free two dictionaries (a Redis DB).\n             * only arg3 -> free the skiplist. */\n            if (job->arg1)\n                lazyfreeFreeObjectFromBioThread(job->arg1);\n            else if (job->arg2 && job->arg3)\n                lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);\n            else if (job->arg3)\n                lazyfreeFreeSlotsMapFromBioThread(job->arg3);\n        } else {\n            serverPanic(\"Wrong job type in bioProcessBackgroundJobs().\");\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        listDelNode(bio_jobs[type],ln);\n        bio_pending[type]--;\n\n        /* Unblock threads blocked on bioWaitStepOfType() if any. */\n        pthread_cond_broadcast(&bio_step_cond[type]);\n    }\n}\n\n/* Return the number of pending jobs of the specified type. */\nunsigned long long bioPendingJobsOfType(int type) {\n    unsigned long long val;\n    pthread_mutex_lock(&bio_mutex[type]);\n    val = bio_pending[type];\n    pthread_mutex_unlock(&bio_mutex[type]);\n    return val;\n}\n\n/* If there are pending jobs for the specified type, the function blocks\n * and waits that the next job was processed. Otherwise the function\n * does not block and returns ASAP.\n *\n * The function returns the number of jobs still to process of the\n * requested type.\n *\n * This function is useful when from another thread, we want to wait\n * a bio.c thread to do more work in a blocking way.\n */\nunsigned long long bioWaitStepOfType(int type) {\n    unsigned long long val;\n    pthread_mutex_lock(&bio_mutex[type]);\n    val = bio_pending[type];\n    if (val != 0) {\n        pthread_cond_wait(&bio_step_cond[type],&bio_mutex[type]);\n        val = bio_pending[type];\n    }\n    pthread_mutex_unlock(&bio_mutex[type]);\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 * 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. */\nvoid bioKillThreads(void) {\n    int err, j;\n\n    for (j = 0; j < BIO_NUM_OPS; j++) {\n        if (bio_threads[j] && pthread_cancel(bio_threads[j]) == 0) {\n            if ((err = pthread_join(bio_threads[j],NULL)) != 0) {\n                serverLog(LL_WARNING,\n                    \"Bio thread for job type #%d can be joined: %s\",\n                        j, strerror(err));\n            } else {\n                serverLog(LL_WARNING,\n                    \"Bio thread for job type #%d terminated\",j);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#ifndef __BIO_H\n#define __BIO_H\n\n/* Exported API */\nvoid bioInit(void);\nvoid bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);\nunsigned long long bioPendingJobsOfType(int type);\nunsigned long long bioWaitStepOfType(int type);\ntime_t bioOlderJobOfType(int type);\nvoid bioKillThreads(void);\n\n/* Background job opcodes */\n#define BIO_CLOSE_FILE    0 /* Deferred close(2) syscall. */\n#define BIO_AOF_FSYNC     1 /* Deferred AOF fsync. */\n#define BIO_LAZY_FREE     2 /* Deferred objects freeing. */\n#define BIO_NUM_OPS       3\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n\n/* -----------------------------------------------------------------------------\n * Helpers and low level bit functions.\n * -------------------------------------------------------------------------- */\n\n/* Count number of bits set in the binary array pointed by 's' and long\n * 'count' bytes. The implementation of this function is required to\n * work with a input string length up to 512 MB. */\nsize_t redisPopcount(void *s, long count) {\n    size_t bits = 0;\n    unsigned char *p = s;\n    uint32_t *p4;\n    static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};\n\n    /* Count initial bytes not aligned to 32 bit. */\n    while((unsigned long)p & 3 && count) {\n        bits += bitsinbyte[*p++];\n        count--;\n    }\n\n    /* Count bits 28 bytes at a time */\n    p4 = (uint32_t*)p;\n    while(count>=28) {\n        uint32_t aux1, aux2, aux3, aux4, aux5, aux6, aux7;\n\n        aux1 = *p4++;\n        aux2 = *p4++;\n        aux3 = *p4++;\n        aux4 = *p4++;\n        aux5 = *p4++;\n        aux6 = *p4++;\n        aux7 = *p4++;\n        count -= 28;\n\n        aux1 = aux1 - ((aux1 >> 1) & 0x55555555);\n        aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333);\n        aux2 = aux2 - ((aux2 >> 1) & 0x55555555);\n        aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333);\n        aux3 = aux3 - ((aux3 >> 1) & 0x55555555);\n        aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333);\n        aux4 = aux4 - ((aux4 >> 1) & 0x55555555);\n        aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333);\n        aux5 = aux5 - ((aux5 >> 1) & 0x55555555);\n        aux5 = (aux5 & 0x33333333) + ((aux5 >> 2) & 0x33333333);\n        aux6 = aux6 - ((aux6 >> 1) & 0x55555555);\n        aux6 = (aux6 & 0x33333333) + ((aux6 >> 2) & 0x33333333);\n        aux7 = aux7 - ((aux7 >> 1) & 0x55555555);\n        aux7 = (aux7 & 0x33333333) + ((aux7 >> 2) & 0x33333333);\n        bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) +\n                    ((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) +\n                    ((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) +\n                    ((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) +\n                    ((aux5 + (aux5 >> 4)) & 0x0F0F0F0F) +\n                    ((aux6 + (aux6 >> 4)) & 0x0F0F0F0F) +\n                    ((aux7 + (aux7 >> 4)) & 0x0F0F0F0F))* 0x01010101) >> 24;\n    }\n    /* Count the remaining bytes. */\n    p = (unsigned char*)p4;\n    while(count--) bits += bitsinbyte[*p++];\n    return bits;\n}\n\n/* Return the position of the first bit set to one (if 'bit' is 1) or\n * zero (if 'bit' is 0) in the bitmap starting at 's' and long 'count' bytes.\n *\n * The function is guaranteed to return a value >= 0 if 'bit' is 0 since if\n * no zero bit is found, it returns count*8 assuming the string is zero\n * padded on the right. However if 'bit' is 1 it is possible that there is\n * not a single set bit in the bitmap. In this special case -1 is returned. */\nlong redisBitpos(void *s, unsigned long count, int bit) {\n    unsigned long *l;\n    unsigned char *c;\n    unsigned long skipval, word = 0, one;\n    long pos = 0; /* Position of bit, to return to the caller. */\n    unsigned long j;\n    int found;\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    found = 0;\n    while((unsigned long)c & (sizeof(*l)-1) && count) {\n        if (*c != skipval) {\n            found = 1;\n            break;\n        }\n        c++;\n        count--;\n        pos += 8;\n    }\n\n    /* Skip bits with full word step. */\n    l = (unsigned long*) c;\n    if (!found) {\n        skipval = bit ? 0 : ULONG_MAX;\n        while (count >= sizeof(*l)) {\n            if (*l != skipval) break;\n            l++;\n            count -= sizeof(*l);\n            pos += sizeof(*l)*8;\n        }\n    }\n\n    /* Load bytes into \"word\" considering the first byte as the most significant\n     * (we basically consider it as written in big endian, since we consider the\n     * string as a set of bits from left to right, with the first bit at position\n     * zero.\n     *\n     * Note that the loading is designed to work even when the bytes left\n     * (count) are less than a full word. We pad it with zero on the right. */\n    c = (unsigned char*)l;\n    for (j = 0; j < sizeof(*l); j++) {\n        word <<= 8;\n        if (count) {\n            word |= *c;\n            c++;\n            count--;\n        }\n    }\n\n    /* Special case:\n     * If bits in the string are all zero and we are looking for one,\n     * return -1 to signal that there is not a single \"1\" in the whole\n     * string. This can't happen when we are looking for \"0\" as we assume\n     * that the right of the string is zero padded. */\n    if (bit == 1 && word == 0) return -1;\n\n    /* Last word left, scan bit by bit. The first thing we need is to\n     * have a single \"1\" set in the most significant position in an\n     * unsigned long. We don't know the size of the long so we use a\n     * simple trick. */\n    one = ULONG_MAX; /* All bits set to 1.*/\n    one >>= 1;       /* All bits set to 1 but the MSB. */\n    one = ~one;      /* All bits set to 0 but the MSB. */\n\n    while(one) {\n        if (((one & word) != 0) == bit) return pos;\n        pos++;\n        one >>= 1;\n    }\n\n    /* If we reached this point, there is a bug in the algorithm, since\n     * the case of no match is handled as a special case before. */\n    serverPanic(\"End of redisBitpos() reached.\");\n    return 0; /* Just to avoid warnings. */\n}\n\n/* The following set.*Bitfield and get.*Bitfield functions implement setting\n * and getting arbitrary size (up to 64 bits) signed and unsigned integers\n * at arbitrary positions into a bitmap.\n *\n * The representation considers the bitmap as having the bit number 0 to be\n * the most significant bit of the first byte, and so forth, so for example\n * setting a 5 bits unsigned integer to value 23 at offset 7 into a bitmap\n * previously set to all zeroes, will produce the following representation:\n *\n * +--------+--------+\n * |00000001|01110000|\n * +--------+--------+\n *\n * When offsets and integer sizes are aligned to bytes boundaries, this is the\n * same as big endian, however when such alignment does not exist, its important\n * to also understand how the bits inside a byte are ordered.\n *\n * Note that this format follows the same convention as SETBIT and related\n * commands.\n */\n\nvoid setUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, uint64_t value) {\n    uint64_t byte, bit, byteval, bitval, j;\n\n    for (j = 0; j < bits; j++) {\n        bitval = (value & ((uint64_t)1<<(bits-1-j))) != 0;\n        byte = offset >> 3;\n        bit = 7 - (offset & 0x7);\n        byteval = p[byte];\n        byteval &= ~(1 << bit);\n        byteval |= bitval << bit;\n        p[byte] = byteval & 0xff;\n        offset++;\n    }\n}\n\nvoid setSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits, int64_t value) {\n    uint64_t uv = value; /* Casting will add UINT64_MAX + 1 if v is negative. */\n    setUnsignedBitfield(p,offset,bits,uv);\n}\n\nuint64_t getUnsignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {\n    uint64_t byte, bit, byteval, bitval, j, value = 0;\n\n    for (j = 0; j < bits; j++) {\n        byte = offset >> 3;\n        bit = 7 - (offset & 0x7);\n        byteval = p[byte];\n        bitval = (byteval >> bit) & 1;\n        value = (value<<1) | bitval;\n        offset++;\n    }\n    return value;\n}\n\nint64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {\n    int64_t value;\n    union {uint64_t u; int64_t i;} conv;\n\n    /* Converting from unsigned to signed is undefined when the value does\n     * not fit, however here we assume two's complement and the original value\n     * was obtained from signed -> unsigned conversion, so we'll find the\n     * most significant bit set if the original value was negative.\n     *\n     * Note that two's complement is mandatory for exact-width types\n     * according to the C99 standard. */\n    conv.u = getUnsignedBitfield(p,offset,bits);\n    value = conv.i;\n\n    /* If the top significant bit is 1, propagate it to all the\n     * higher bits for two's complement representation of signed\n     * integers. */\n    if (value & ((uint64_t)1 << (bits-1)))\n        value |= ((uint64_t)-1) << bits;\n    return value;\n}\n\n/* The following two functions detect overflow of a value in the context\n * of storing it as an unsigned or signed integer with the specified\n * number of bits. The functions both take the value and a possible increment.\n * If no overflow could happen and the value+increment fit inside the limits,\n * then zero is returned, otherwise in case of overflow, 1 is returned,\n * otherwise in case of underflow, -1 is returned.\n *\n * When non-zero is returned (overflow or underflow), if not NULL, *limit is\n * set to the value the operation should result when an overflow happens,\n * depending on the specified overflow semantics:\n *\n * For BFOVERFLOW_SAT if 1 is returned, *limit it is set maximum value that\n * you can store in that integer. when -1 is returned, *limit is set to the\n * minimum value that an integer of that size can represent.\n *\n * For BFOVERFLOW_WRAP *limit is set by performing the operation in order to\n * \"wrap\" around towards zero for unsigned integers, or towards the most\n * negative number that is possible to represent for signed integers. */\n\n#define BFOVERFLOW_WRAP 0\n#define BFOVERFLOW_SAT 1\n#define BFOVERFLOW_FAIL 2 /* Used by the BITFIELD command implementation. */\n\nint checkUnsignedBitfieldOverflow(uint64_t value, int64_t incr, uint64_t bits, int owtype, uint64_t *limit) {\n    uint64_t max = (bits == 64) ? UINT64_MAX : (((uint64_t)1<<bits)-1);\n    int64_t maxincr = max-value;\n    int64_t minincr = -value;\n\n    if (value > max || (incr > 0 && incr > maxincr)) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = max;\n            }\n        }\n        return 1;\n    } else if (incr < 0 && incr < minincr) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = 0;\n            }\n        }\n        return -1;\n    }\n    return 0;\n\nhandle_wrap:\n    {\n        uint64_t mask = ((uint64_t)-1) << bits;\n        uint64_t res = value+incr;\n\n        res &= ~mask;\n        *limit = res;\n    }\n    return 1;\n}\n\nint checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit) {\n    int64_t max = (bits == 64) ? INT64_MAX : (((int64_t)1<<(bits-1))-1);\n    int64_t min = (-max)-1;\n\n    /* Note that maxincr and minincr could overflow, but we use the values\n     * only after checking 'value' range, so when we use it no overflow\n     * happens. */\n    int64_t maxincr = max-value;\n    int64_t minincr = min-value;\n\n    if (value > max || (bits != 64 && incr > maxincr) || (value >= 0 && incr > 0 && incr > maxincr))\n    {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = max;\n            }\n        }\n        return 1;\n    } else if (value < min || (bits != 64 && incr < minincr) || (value < 0 && incr < 0 && incr < minincr)) {\n        if (limit) {\n            if (owtype == BFOVERFLOW_WRAP) {\n                goto handle_wrap;\n            } else if (owtype == BFOVERFLOW_SAT) {\n                *limit = min;\n            }\n        }\n        return -1;\n    }\n    return 0;\n\nhandle_wrap:\n    {\n        uint64_t mask = ((uint64_t)-1) << bits;\n        uint64_t msb = (uint64_t)1 << (bits-1);\n        uint64_t a = value, b = incr, c;\n        c = a+b; /* Perform addition as unsigned so that's defined. */\n\n        /* If the sign bit is set, propagate to all the higher order\n         * bits, to cap the negative value. If it's clear, mask to\n         * the positive integer limit. */\n        if (c & msb) {\n            c |= mask;\n        } else {\n            c &= ~mask;\n        }\n        *limit = c;\n    }\n    return 1;\n}\n\n/* Debugging function. Just show bits in the specified bitmap. Not used\n * but here for not having to rewrite it when debugging is needed. */\nvoid printBits(unsigned char *p, unsigned long count) {\n    unsigned long j, i, byte;\n\n    for (j = 0; j < count; j++) {\n        byte = p[j];\n        for (i = 0x80; i > 0; i /= 2)\n            printf(\"%c\", (byte & i) ? '1' : '0');\n        printf(\"|\");\n    }\n    printf(\"\\n\");\n}\n\n/* -----------------------------------------------------------------------------\n * Bits related string commands: GETBIT, SETBIT, BITCOUNT, BITOP.\n * -------------------------------------------------------------------------- */\n\n#define BITOP_AND   0\n#define BITOP_OR    1\n#define BITOP_XOR   2\n#define BITOP_NOT   3\n\n#define BITFIELDOP_GET 0\n#define BITFIELDOP_SET 1\n#define BITFIELDOP_INCRBY 2\n\n/* This helper function used by GETBIT / SETBIT parses the bit offset argument\n * making sure an error is returned if it is negative or if it overflows\n * Redis 512 MB limit for the string value.\n *\n * If the 'hash' argument is true, and 'bits is positive, then the command\n * will also parse bit offsets prefixed by \"#\". In such a case the offset\n * is multiplied by 'bits'. This is useful for the BITFIELD command. */\nint getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int bits) {\n    long long loffset;\n    char *err = \"bit offset is not an integer or out of range\";\n    char *p = o->ptr;\n    size_t plen = sdslen(p);\n    int usehash = 0;\n\n    /* Handle #<offset> form. */\n    if (p[0] == '#' && hash && bits > 0) usehash = 1;\n\n    if (string2ll(p+usehash,plen-usehash,&loffset) == 0) {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n\n    /* Adjust the offset by 'bits' for #<offset> form. */\n    if (usehash) loffset *= bits;\n\n    /* Limit offset to 512MB in bytes */\n    if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))\n    {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n\n    *offset = (size_t)loffset;\n    return C_OK;\n}\n\n/* This helper function for BITFIELD parses a bitfield type in the form\n * <sign><bits> where sign is 'u' or 'i' for unsigned and signed, and\n * the bits is a value between 1 and 64. However 64 bits unsigned integers\n * are reported as an error because of current limitations of Redis protocol\n * to return unsigned integer values greater than INT64_MAX.\n *\n * On error C_ERR is returned and an error is sent to the client. */\nint getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {\n    char *p = o->ptr;\n    char *err = \"Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\";\n    long long llbits;\n\n    if (p[0] == 'i') {\n        *sign = 1;\n    } else if (p[0] == 'u') {\n        *sign = 0;\n    } else {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n\n    if ((string2ll(p+1,strlen(p+1),&llbits)) == 0 ||\n        llbits < 1 ||\n        (*sign == 1 && llbits > 64) ||\n        (*sign == 0 && llbits > 63))\n    {\n        addReplyError(c,err);\n        return C_ERR;\n    }\n    *bits = llbits;\n    return C_OK;\n}\n\n/* This is an helper function for commands implementations that need to write\n * bits to a string object. The command creates or pad with zeroes the string\n * so that the 'maxbit' bit can be addressed. The object is finally\n * returned. Otherwise if the key holds a wrong type NULL is returned and\n * an error is sent to the client. */\nrobj *lookupStringForBitCommand(client *c, size_t maxbit) {\n    size_t byte = maxbit >> 3;\n    robj *o = lookupKeyWrite(c->db,c->argv[1]);\n\n    if (o == NULL) {\n        o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        if (checkType(c,o,OBJ_STRING)) return NULL;\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n        o->ptr = sdsgrowzero(o->ptr,byte+1);\n    }\n    return o;\n}\n\n/* Return a pointer to the string object content, and stores its length\n * in 'len'. The user is required to pass (likely stack allocated) buffer\n * 'llbuf' of at least LONG_STR_SIZE bytes. Such a buffer is used in the case\n * the object is integer encoded in order to provide the representation\n * without usign heap allocation.\n *\n * The function returns the pointer to the object array of bytes representing\n * the string it contains, that may be a pointer to 'llbuf' or to the\n * internal object representation. As a side effect 'len' is filled with\n * the length of such buffer.\n *\n * If the source object is NULL the function is guaranteed to return NULL\n * and set 'len' to 0. */\nunsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) {\n    serverAssert(o->type == OBJ_STRING);\n    unsigned char *p = NULL;\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 && o->encoding == OBJ_ENCODING_INT) {\n        p = (unsigned char*) llbuf;\n        if (len) *len = ll2string(llbuf,LONG_STR_SIZE,(long)o->ptr);\n    } else if (o) {\n        p = (unsigned char*) o->ptr;\n        if (len) *len = sdslen(o->ptr);\n    } else {\n        if (len) *len = 0;\n    }\n    return p;\n}\n\n/* SETBIT key offset bitvalue */\nvoid setbitCommand(client *c) {\n    robj *o;\n    char *err = \"bit is not an integer or out of range\";\n    size_t bitoffset;\n    ssize_t byte, bit;\n    int byteval, bitval;\n    long on;\n\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)\n        return;\n\n    if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != C_OK)\n        return;\n\n    /* Bits can only be set or cleared... */\n    if (on & ~1) {\n        addReplyError(c,err);\n        return;\n    }\n\n    if ((o = lookupStringForBitCommand(c,bitoffset)) == NULL) return;\n\n    /* Get current values */\n    byte = bitoffset >> 3;\n    byteval = ((uint8_t*)o->ptr)[byte];\n    bit = 7 - (bitoffset & 0x7);\n    bitval = byteval & (1 << bit);\n\n    /* Update byte with new bit value and return original value */\n    byteval &= ~(1 << bit);\n    byteval |= ((on & 0x1) << bit);\n    ((uint8_t*)o->ptr)[byte] = byteval;\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c, bitval ? shared.cone : shared.czero);\n}\n\n/* GETBIT key offset */\nvoid getbitCommand(client *c) {\n    robj *o;\n    char llbuf[32];\n    size_t bitoffset;\n    size_t byte, bit;\n    size_t bitval = 0;\n\n    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)\n        return;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n\n    byte = bitoffset >> 3;\n    bit = 7 - (bitoffset & 0x7);\n    if (sdsEncodedObject(o)) {\n        if (byte < sdslen(o->ptr))\n            bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);\n    } else {\n        if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))\n            bitval = llbuf[byte] & (1 << bit);\n    }\n\n    addReply(c, bitval ? shared.cone : shared.czero);\n}\n\n/* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */\nvoid bitopCommand(client *c) {\n    char *opname = c->argv[1]->ptr;\n    robj *o, *targetkey = c->argv[2];\n    unsigned long op, j, numkeys;\n    robj **objects;      /* Array of source objects. */\n    unsigned char **src; /* Array of source strings pointers. */\n    unsigned long *len, maxlen = 0; /* Array of length of src strings,\n                                       and max len. */\n    unsigned long minlen = 0;    /* Min len among the input keys. */\n    unsigned char *res = NULL; /* Resulting string. */\n\n    /* Parse the operation name. */\n    if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,\"and\"))\n        op = BITOP_AND;\n    else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,\"or\"))\n        op = BITOP_OR;\n    else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,\"xor\"))\n        op = BITOP_XOR;\n    else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,\"not\"))\n        op = BITOP_NOT;\n    else {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Sanity check: NOT accepts only a single key argument. */\n    if (op == BITOP_NOT && c->argc != 4) {\n        addReplyError(c,\"BITOP NOT must be called with a single source key.\");\n        return;\n    }\n\n    /* Lookup keys, and store pointers to the string objects into an array. */\n    numkeys = c->argc - 3;\n    src = zmalloc(sizeof(unsigned char*) * numkeys);\n    len = zmalloc(sizeof(long) * numkeys);\n    objects = zmalloc(sizeof(robj*) * numkeys);\n    for (j = 0; j < numkeys; j++) {\n        o = lookupKeyRead(c->db,c->argv[j+3]);\n        /* Handle non-existing keys as empty strings. */\n        if (o == NULL) {\n            objects[j] = NULL;\n            src[j] = NULL;\n            len[j] = 0;\n            minlen = 0;\n            continue;\n        }\n        /* Return an error if one of the keys is not a string. */\n        if (checkType(c,o,OBJ_STRING)) {\n            unsigned long i;\n            for (i = 0; i < j; i++) {\n                if (objects[i])\n                    decrRefCount(objects[i]);\n            }\n            zfree(src);\n            zfree(len);\n            zfree(objects);\n            return;\n        }\n        objects[j] = getDecodedObject(o);\n        src[j] = objects[j]->ptr;\n        len[j] = sdslen(objects[j]->ptr);\n        if (len[j] > maxlen) maxlen = len[j];\n        if (j == 0 || len[j] < minlen) minlen = len[j];\n    }\n\n    /* Compute the bit operation, if at least one string is not empty. */\n    if (maxlen) {\n        res = (unsigned char*) sdsnewlen(NULL,maxlen);\n        unsigned char output, byte;\n        unsigned long i;\n\n        /* Fast path: as far as we have data for all the input bitmaps we\n         * can take a fast path that performs much better than the\n         * vanilla algorithm. On ARM we skip the fast path since it will\n         * result in GCC compiling the code using multiple-words load/store\n         * operations that are not supported even in ARM >= v6. */\n        j = 0;\n        #ifndef USE_ALIGNED_ACCESS\n        if (minlen >= sizeof(unsigned long)*4 && numkeys <= 16) {\n            unsigned long *lp[16];\n            unsigned long *lres = (unsigned long*) res;\n\n            /* Note: sds pointer is always aligned to 8 byte boundary. */\n            memcpy(lp,src,sizeof(unsigned long*)*numkeys);\n            memcpy(res,src[0],minlen);\n\n            /* Different branches per different operations for speed (sorry). */\n            if (op == BITOP_AND) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] &= lp[i][0];\n                        lres[1] &= lp[i][1];\n                        lres[2] &= lp[i][2];\n                        lres[3] &= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_OR) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] |= lp[i][0];\n                        lres[1] |= lp[i][1];\n                        lres[2] |= lp[i][2];\n                        lres[3] |= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_XOR) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    for (i = 1; i < numkeys; i++) {\n                        lres[0] ^= lp[i][0];\n                        lres[1] ^= lp[i][1];\n                        lres[2] ^= lp[i][2];\n                        lres[3] ^= lp[i][3];\n                        lp[i]+=4;\n                    }\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            } else if (op == BITOP_NOT) {\n                while(minlen >= sizeof(unsigned long)*4) {\n                    lres[0] = ~lres[0];\n                    lres[1] = ~lres[1];\n                    lres[2] = ~lres[2];\n                    lres[3] = ~lres[3];\n                    lres+=4;\n                    j += sizeof(unsigned long)*4;\n                    minlen -= sizeof(unsigned long)*4;\n                }\n            }\n        }\n        #endif\n\n        /* j is set to the next byte to process by the previous loop. */\n        for (; j < maxlen; j++) {\n            output = (len[0] <= j) ? 0 : src[0][j];\n            if (op == BITOP_NOT) output = ~output;\n            for (i = 1; i < numkeys; i++) {\n                byte = (len[i] <= j) ? 0 : src[i][j];\n                switch(op) {\n                case BITOP_AND: output &= byte; break;\n                case BITOP_OR:  output |= byte; break;\n                case BITOP_XOR: output ^= byte; break;\n                }\n            }\n            res[j] = output;\n        }\n    }\n    for (j = 0; j < numkeys; j++) {\n        if (objects[j])\n            decrRefCount(objects[j]);\n    }\n    zfree(src);\n    zfree(len);\n    zfree(objects);\n\n    /* Store the computed value into the target key */\n    if (maxlen) {\n        o = createObject(OBJ_STRING,res);\n        setKey(c,c->db,targetkey,o);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",targetkey,c->db->id);\n        decrRefCount(o);\n    } else if (dbDelete(c->db,targetkey)) {\n        signalModifiedKey(c,c->db,targetkey);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",targetkey,c->db->id);\n    }\n    server.dirty++;\n    addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */\n}\n\n/* BITCOUNT key [start end] */\nvoid bitcountCommand(client *c) {\n    robj *o;\n    long start, end, strlen;\n    unsigned char *p;\n    char llbuf[LONG_STR_SIZE];\n\n    /* Lookup, check for type, and return 0 for non existing keys. */\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n    p = getObjectReadOnlyString(o,&strlen,llbuf);\n\n    /* Parse start/end range if any. */\n    if (c->argc == 4) {\n        if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)\n            return;\n        if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)\n            return;\n        /* Convert negative indexes */\n        if (start < 0 && end < 0 && start > end) {\n            addReply(c,shared.czero);\n            return;\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 (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        addReply(c,shared.czero);\n    } else {\n        long bytes = end-start+1;\n\n        addReplyLongLong(c,redisPopcount(p+start,bytes));\n    }\n}\n\n/* BITPOS key bit [start [end]] */\nvoid bitposCommand(client *c) {\n    robj *o;\n    long bit, start, end, strlen;\n    unsigned char *p;\n    char llbuf[LONG_STR_SIZE];\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) != C_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,OBJ_STRING)) return;\n    p = getObjectReadOnlyString(o,&strlen,llbuf);\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) != C_OK)\n            return;\n        if (c->argc == 5) {\n            if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != C_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\n/* BITFIELD key subcommmand-1 arg ... subcommand-2 arg ... subcommand-N ...\n *\n * Supported subcommands:\n *\n * GET <type> <offset>\n * SET <type> <offset> <value>\n * INCRBY <type> <offset> <increment>\n * OVERFLOW [WRAP|SAT|FAIL]\n */\n\n#define BITFIELD_FLAG_NONE      0\n#define BITFIELD_FLAG_READONLY  (1<<0)\n\nstruct bitfieldOp {\n    uint64_t offset;    /* Bitfield offset. */\n    int64_t i64;        /* Increment amount (INCRBY) or SET value */\n    int opcode;         /* Operation id. */\n    int owtype;         /* Overflow type to use. */\n    int bits;           /* Integer bitfield bits width. */\n    int sign;           /* True if signed, otherwise unsigned op. */\n};\n\n/* This implements both the BITFIELD command and the BITFIELD_RO command\n * when flags is set to BITFIELD_FLAG_READONLY: in this case only the\n * GET subcommand is allowed, other subcommands will return an error. */\nvoid bitfieldGeneric(client *c, int flags) {\n    robj *o;\n    size_t bitoffset;\n    int j, numops = 0, changes = 0;\n    struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */\n    int owtype = BFOVERFLOW_WRAP; /* Overflow type. */\n    int readonly = 1;\n    size_t highest_write_offset = 0;\n\n    for (j = 2; j < c->argc; j++) {\n        int remargs = c->argc-j-1; /* Remaining args other than current. */\n        char *subcmd = c->argv[j]->ptr; /* Current command name. */\n        int opcode; /* Current operation code. */\n        long long i64 = 0;  /* Signed SET value. */\n        int sign = 0; /* Signed or unsigned type? */\n        int bits = 0; /* Bitfield width in bits. */\n\n        if (!strcasecmp(subcmd,\"get\") && remargs >= 2)\n            opcode = BITFIELDOP_GET;\n        else if (!strcasecmp(subcmd,\"set\") && remargs >= 3)\n            opcode = BITFIELDOP_SET;\n        else if (!strcasecmp(subcmd,\"incrby\") && remargs >= 3)\n            opcode = BITFIELDOP_INCRBY;\n        else if (!strcasecmp(subcmd,\"overflow\") && remargs >= 1) {\n            char *owtypename = c->argv[j+1]->ptr;\n            j++;\n            if (!strcasecmp(owtypename,\"wrap\"))\n                owtype = BFOVERFLOW_WRAP;\n            else if (!strcasecmp(owtypename,\"sat\"))\n                owtype = BFOVERFLOW_SAT;\n            else if (!strcasecmp(owtypename,\"fail\"))\n                owtype = BFOVERFLOW_FAIL;\n            else {\n                addReplyError(c,\"Invalid OVERFLOW type specified\");\n                zfree(ops);\n                return;\n            }\n            continue;\n        } else {\n            addReply(c,shared.syntaxerr);\n            zfree(ops);\n            return;\n        }\n\n        /* Get the type and offset arguments, common to all the ops. */\n        if (getBitfieldTypeFromArgument(c,c->argv[j+1],&sign,&bits) != C_OK) {\n            zfree(ops);\n            return;\n        }\n\n        if (getBitOffsetFromArgument(c,c->argv[j+2],&bitoffset,1,bits) != C_OK){\n            zfree(ops);\n            return;\n        }\n\n        if (opcode != BITFIELDOP_GET) {\n            readonly = 0;\n            if (highest_write_offset < bitoffset + bits - 1)\n                highest_write_offset = bitoffset + bits - 1;\n            /* INCRBY and SET require another argument. */\n            if (getLongLongFromObjectOrReply(c,c->argv[j+3],&i64,NULL) != C_OK){\n                zfree(ops);\n                return;\n            }\n        }\n\n        /* Populate the array of operations we'll process. */\n        ops = zrealloc(ops,sizeof(*ops)*(numops+1));\n        ops[numops].offset = bitoffset;\n        ops[numops].i64 = i64;\n        ops[numops].opcode = opcode;\n        ops[numops].owtype = owtype;\n        ops[numops].bits = bits;\n        ops[numops].sign = sign;\n        numops++;\n\n        j += 3 - (opcode == BITFIELDOP_GET);\n    }\n\n    if (readonly) {\n        /* Lookup for read is ok if key doesn't exit, but errors\n         * if it's not a string. */\n        o = lookupKeyRead(c->db,c->argv[1]);\n        if (o != NULL && checkType(c,o,OBJ_STRING)) {\n            zfree(ops);\n            return;\n        }\n    } else {\n        if (flags & BITFIELD_FLAG_READONLY) {\n            zfree(ops);\n            addReplyError(c, \"BITFIELD_RO only supports the GET subcommand\");\n            return;\n        }\n\n        /* Lookup by making room up to the farest bit reached by\n         * this operation. */\n        if ((o = lookupStringForBitCommand(c,\n            highest_write_offset)) == NULL) {\n            zfree(ops);\n            return;\n        }\n    }\n\n    addReplyArrayLen(c,numops);\n\n    /* Actually process the operations. */\n    for (j = 0; j < numops; j++) {\n        struct bitfieldOp *thisop = ops+j;\n\n        /* Execute the operation. */\n        if (thisop->opcode == BITFIELDOP_SET ||\n            thisop->opcode == BITFIELDOP_INCRBY)\n        {\n            /* SET and INCRBY: We handle both with the same code path\n             * for simplicity. SET return value is the previous value so\n             * we need fetch & store as well. */\n\n            /* We need two different but very similar code paths for signed\n             * and unsigned operations, since the set of functions to get/set\n             * the integers and the used variables types are different. */\n            if (thisop->sign) {\n                int64_t oldval, newval, wrapped, retval;\n                int overflow;\n\n                oldval = getSignedBitfield(o->ptr,thisop->offset,\n                        thisop->bits);\n\n                if (thisop->opcode == BITFIELDOP_INCRBY) {\n                    newval = oldval + thisop->i64;\n                    overflow = checkSignedBitfieldOverflow(oldval,\n                            thisop->i64,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = newval;\n                } else {\n                    newval = thisop->i64;\n                    overflow = checkSignedBitfieldOverflow(newval,\n                            0,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = oldval;\n                }\n\n                /* On overflow of type is \"FAIL\", don't write and return\n                 * NULL to signal the condition. */\n                if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {\n                    addReplyLongLong(c,retval);\n                    setSignedBitfield(o->ptr,thisop->offset,\n                                      thisop->bits,newval);\n                } else {\n                    addReplyNull(c);\n                }\n            } else {\n                uint64_t oldval, newval, wrapped, retval;\n                int overflow;\n\n                oldval = getUnsignedBitfield(o->ptr,thisop->offset,\n                        thisop->bits);\n\n                if (thisop->opcode == BITFIELDOP_INCRBY) {\n                    newval = oldval + thisop->i64;\n                    overflow = checkUnsignedBitfieldOverflow(oldval,\n                            thisop->i64,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = newval;\n                } else {\n                    newval = thisop->i64;\n                    overflow = checkUnsignedBitfieldOverflow(newval,\n                            0,thisop->bits,thisop->owtype,&wrapped);\n                    if (overflow) newval = wrapped;\n                    retval = oldval;\n                }\n                /* On overflow of type is \"FAIL\", don't write and return\n                 * NULL to signal the condition. */\n                if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {\n                    addReplyLongLong(c,retval);\n                    setUnsignedBitfield(o->ptr,thisop->offset,\n                                        thisop->bits,newval);\n                } else {\n                    addReplyNull(c);\n                }\n            }\n            changes++;\n        } else {\n            /* GET */\n            unsigned char buf[9];\n            long strlen = 0;\n            unsigned char *src = NULL;\n            char llbuf[LONG_STR_SIZE];\n\n            if (o != NULL)\n                src = getObjectReadOnlyString(o,&strlen,llbuf);\n\n            /* For GET we use a trick: before executing the operation\n             * copy up to 9 bytes to a local buffer, so that we can easily\n             * execute up to 64 bit operations that are at actual string\n             * object boundaries. */\n            memset(buf,0,9);\n            int i;\n            size_t byte = thisop->offset >> 3;\n            for (i = 0; i < 9; i++) {\n                if (src == NULL || i+byte >= (size_t)strlen) break;\n                buf[i] = src[i+byte];\n            }\n\n            /* Now operate on the copied buffer which is guaranteed\n             * to be zero-padded. */\n            if (thisop->sign) {\n                int64_t val = getSignedBitfield(buf,thisop->offset-(byte*8),\n                                            thisop->bits);\n                addReplyLongLong(c,val);\n            } else {\n                uint64_t val = getUnsignedBitfield(buf,thisop->offset-(byte*8),\n                                            thisop->bits);\n                addReplyLongLong(c,val);\n            }\n        }\n    }\n\n    if (changes) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"setbit\",c->argv[1],c->db->id);\n        server.dirty += changes;\n    }\n    zfree(ops);\n}\n\nvoid bitfieldCommand(client *c) {\n    bitfieldGeneric(c, BITFIELD_FLAG_NONE);\n}\n\nvoid bitfieldroCommand(client *c) {\n    bitfieldGeneric(c, BITFIELD_FLAG_READONLY);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 * blockClient() set the CLIENT_BLOCKED flag in the client, and set the\n * specified block type 'btype' filed to one of 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 CLIENT_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 CLIENT_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 * If the blocking operation waits for certain keys to change state, the\n * clusterRedirectBlockedClientIfNeeded() function should also be updated.\n */\n\n#include \"server.h\"\n\nint serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);\n\n/* This structure represents the blocked key information that we store\n * in the client structure. Each client blocked on keys, has a\n * client->bpop.keys hash table. The keys of the hash table are Redis\n * keys pointers to 'robj' structures. The value is this structure.\n * The structure has two goals: firstly we store the list node that this\n * client uses to be listed in the database \"blocked clients for this key\"\n * list, so we can later unblock in O(1) without a list scan.\n * Secondly for certain blocking types, we have additional info. Right now\n * the only use for additional info we have is when clients are blocked\n * on streams, as we have to remember the ID it blocked for. */\ntypedef struct bkinfo {\n    listNode *listnode;     /* List node for db->blocking_keys[key] list. */\n    streamID stream_id;     /* Stream ID if we blocked in a stream. */\n} bkinfo;\n\n/* Block a client for the specific operation type. Once the CLIENT_BLOCKED\n * flag is set client query buffer is not longer processed, but accumulated,\n * and will be processed when the client is unblocked. */\nvoid blockClient(client *c, int btype) {\n    c->flags |= CLIENT_BLOCKED;\n    c->btype = btype;\n    server.blocked_clients++;\n    server.blocked_clients_by_type[btype]++;\n    addClientToTimeoutTable(c);\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. */\nvoid processUnblockedClients(void) {\n    listNode *ln;\n    client *c;\n\n    while (listLength(server.unblocked_clients)) {\n        ln = listFirst(server.unblocked_clients);\n        serverAssert(ln != NULL);\n        c = ln->value;\n        listDelNode(server.unblocked_clients,ln);\n        c->flags &= ~CLIENT_UNBLOCKED;\n\n        /* Process remaining data in the input buffer, unless the client\n         * is blocked again. Actually processInputBuffer() checks that the\n         * client is not blocked before to proceed, but things may change and\n         * the code is conceptually more correct this way. */\n        if (!(c->flags & CLIENT_BLOCKED)) {\n            if (c->querybuf && sdslen(c->querybuf) > 0) {\n                processInputBuffer(c);\n            }\n        }\n    }\n}\n\n/* This function will schedule the client for reprocessing at a safe time.\n *\n * This is useful when a client was blocked for some reason (blocking opeation,\n * CLIENT PAUSE, or whatever), because it may end with some accumulated query\n * buffer that needs to be processed ASAP:\n *\n * 1. When a client is blocked, its readable handler is still active.\n * 2. However in this case it only gets data into the query buffer, but the\n *    query is not parsed or executed once there is enough to proceed as\n *    usually (because the client is blocked... so we can't execute commands).\n * 3. When the client is unblocked, without this function, the client would\n *    have to write some query in order for the readable handler to finally\n *    call processQueryBuffer*() on it.\n * 4. With this function instead we can put the client in a queue that will\n *    process it for queries ready to be executed at a safe time.\n */\nvoid queueClientForReprocessing(client *c) {\n    /* The client may already be into the unblocked list because of a previous\n     * blocking operation, don't add back it into the list multiple times. */\n    if (!(c->flags & CLIENT_UNBLOCKED)) {\n        c->flags |= CLIENT_UNBLOCKED;\n        listAddNodeTail(server.unblocked_clients,c);\n    }\n}\n\n/* Unblock a client calling the right function depending on the kind\n * of operation the client is blocking for. */\nvoid unblockClient(client *c) {\n    if (c->btype == BLOCKED_LIST ||\n        c->btype == BLOCKED_ZSET ||\n        c->btype == BLOCKED_STREAM) {\n        unblockClientWaitingData(c);\n    } else if (c->btype == BLOCKED_WAIT) {\n        unblockClientWaitingReplicas(c);\n    } else if (c->btype == BLOCKED_MODULE) {\n        if (moduleClientIsBlockedOnKeys(c)) unblockClientWaitingData(c);\n        unblockClientFromModule(c);\n    } else {\n        serverPanic(\"Unknown btype in unblockClient().\");\n    }\n    /* Clear the flags, and put the client in the unblocked list so that\n     * we'll process new commands in its query buffer ASAP. */\n    server.blocked_clients--;\n    server.blocked_clients_by_type[c->btype]--;\n    c->flags &= ~CLIENT_BLOCKED;\n    c->btype = BLOCKED_NONE;\n    removeClientFromTimeoutTable(c);\n    queueClientForReprocessing(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. After this function is called,\n * unblockClient() will be called with the same client as argument. */\nvoid replyToBlockedClientTimedOut(client *c) {\n    if (c->btype == BLOCKED_LIST ||\n        c->btype == BLOCKED_ZSET ||\n        c->btype == BLOCKED_STREAM) {\n        addReplyNullArray(c);\n    } else if (c->btype == BLOCKED_WAIT) {\n        addReplyLongLong(c,replicationCountAcksByOffset(c->bpop.reploffset));\n    } else if (c->btype == BLOCKED_MODULE) {\n        moduleBlockedClientTimedOut(c);\n    } else {\n        serverPanic(\"Unknown btype in replyToBlockedClientTimedOut().\");\n    }\n}\n\n/* Mass-unblock clients because something changed in the instance that makes\n * blocking no longer safe. For example clients blocked in list operations\n * in an instance which turns from master to slave is unsafe, so this function\n * is called when a master turns into a slave.\n *\n * The semantics is to send an -UNBLOCKED error to the client, disconnecting\n * it at the same time. */\nvoid disconnectAllBlockedClients(void) {\n    listNode *ln;\n    listIter li;\n\n    listRewind(server.clients,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n\n        if (c->flags & CLIENT_BLOCKED) {\n            addReplySds(c,sdsnew(\n                \"-UNBLOCKED force unblock from blocking operation, \"\n                \"instance state changed (master -> replica?)\\r\\n\"));\n            unblockClient(c);\n            c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n        }\n    }\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * when there may be clients blocked on a list key, and there may be new\n * data to fetch (the key is ready). */\nvoid serveClientsBlockedOnListKey(robj *o, readyList *rl) {\n    /* We serve clients in the same order they blocked for\n     * this key, from the first blocked to the last. */\n    dictEntry *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            listNode *clientnode = listFirst(clients);\n            client *receiver = clientnode->value;\n\n            if (receiver->btype != BLOCKED_LIST) {\n                /* Put at the tail, so that at the next call\n                 * we'll not run into it again. */\n                listRotateHeadToTail(clients);\n                continue;\n            }\n\n            robj *dstkey = receiver->bpop.target;\n            int where = (receiver->lastcmd &&\n                         receiver->lastcmd->proc == blpopCommand) ?\n                         LIST_HEAD : LIST_TAIL;\n            robj *value = listTypePop(o,where);\n\n            if (value) {\n                /* Protect receiver->bpop.target, that will be\n                 * freed by the next unblockClient()\n                 * call. */\n                if (dstkey) incrRefCount(dstkey);\n                unblockClient(receiver);\n\n                if (serveClientBlockedOnList(receiver,\n                    rl->key,dstkey,rl->db,value,\n                    where) == C_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                break;\n            }\n        }\n    }\n\n    if (listTypeLength(o) == 0) {\n        dbDelete(rl->db,rl->key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",rl->key,rl->db->id);\n    }\n    /* We don't call signalModifiedKey() as it was already called\n     * when an element was pushed on the list. */\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * when there may be clients blocked on a sorted set key, and there may be new\n * data to fetch (the key is ready). */\nvoid serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {\n    /* We serve clients in the same order they blocked for\n     * this key, from the first blocked to the last. */\n    dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);\n    if (de) {\n        list *clients = dictGetVal(de);\n        int numclients = listLength(clients);\n        unsigned long zcard = zsetLength(o);\n\n        while(numclients-- && zcard) {\n            listNode *clientnode = listFirst(clients);\n            client *receiver = clientnode->value;\n\n            if (receiver->btype != BLOCKED_ZSET) {\n                /* Put at the tail, so that at the next call\n                 * we'll not run into it again. */\n                listRotateHeadToTail(clients);\n                continue;\n            }\n\n            int where = (receiver->lastcmd &&\n                         receiver->lastcmd->proc == bzpopminCommand)\n                         ? ZSET_MIN : ZSET_MAX;\n            unblockClient(receiver);\n            genericZpopCommand(receiver,&rl->key,1,where,1,NULL);\n            zcard--;\n\n            /* Replicate the command. */\n            robj *argv[2];\n            struct redisCommand *cmd = where == ZSET_MIN ?\n                                       server.zpopminCommand :\n                                       server.zpopmaxCommand;\n            argv[0] = createStringObject(cmd->name,strlen(cmd->name));\n            argv[1] = rl->key;\n            incrRefCount(rl->key);\n            propagate(cmd,receiver->db->id,\n                      argv,2,PROPAGATE_AOF|PROPAGATE_REPL);\n            decrRefCount(argv[0]);\n            decrRefCount(argv[1]);\n        }\n    }\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * when there may be clients blocked on a stream key, and there may be new\n * data to fetch (the key is ready). */\nvoid serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {\n    dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);\n    stream *s = o->ptr;\n\n    /* We need to provide the new data arrived on the stream\n     * to all the clients that are waiting for an offset smaller\n     * than the current top item. */\n    if (de) {\n        list *clients = dictGetVal(de);\n        listNode *ln;\n        listIter li;\n        listRewind(clients,&li);\n\n        while((ln = listNext(&li))) {\n            client *receiver = listNodeValue(ln);\n            if (receiver->btype != BLOCKED_STREAM) continue;\n            bkinfo *bki = dictFetchValue(receiver->bpop.keys,rl->key);\n            streamID *gt = &bki->stream_id;\n\n            /* If we blocked in the context of a consumer\n             * group, we need to resolve the group and update the\n             * last ID the client is blocked for: this is needed\n             * because serving other clients in the same consumer\n             * group will alter the \"last ID\" of the consumer\n             * group, and clients blocked in a consumer group are\n             * always blocked for the \">\" ID: we need to deliver\n             * only new messages and avoid unblocking the client\n             * otherwise. */\n            streamCG *group = NULL;\n            if (receiver->bpop.xread_group) {\n                group = streamLookupCG(s,\n                        receiver->bpop.xread_group->ptr);\n                /* If the group was not found, send an error\n                 * to the consumer. */\n                if (!group) {\n                    addReplyError(receiver,\n                        \"-NOGROUP the consumer group this client \"\n                        \"was blocked on no longer exists\");\n                    unblockClient(receiver);\n                    continue;\n                } else {\n                    *gt = group->last_id;\n                }\n            }\n\n            if (streamCompareID(&s->last_id, gt) > 0) {\n                streamID start = *gt;\n                streamIncrID(&start);\n\n                /* Lookup the consumer for the group, if any. */\n                streamConsumer *consumer = NULL;\n                int noack = 0;\n\n                if (group) {\n                    consumer =\n                        streamLookupConsumer(group,\n                                             receiver->bpop.xread_consumer->ptr,\n                                             SLC_NONE);\n                    noack = receiver->bpop.xread_group_noack;\n                }\n\n                /* Emit the two elements sub-array consisting of\n                 * the name of the stream and the data we\n                 * extracted from it. Wrapped in a single-item\n                 * array, since we have just one key. */\n                if (receiver->resp == 2) {\n                    addReplyArrayLen(receiver,1);\n                    addReplyArrayLen(receiver,2);\n                } else {\n                    addReplyMapLen(receiver,1);\n                }\n                addReplyBulk(receiver,rl->key);\n\n                streamPropInfo pi = {\n                    rl->key,\n                    receiver->bpop.xread_group\n                };\n                streamReplyWithRange(receiver,s,&start,NULL,\n                                     receiver->bpop.xread_count,\n                                     0, group, consumer, noack, &pi);\n\n                /* Note that after we unblock the client, 'gt'\n                 * and other receiver->bpop stuff are no longer\n                 * valid, so we must do the setup above before\n                 * this call. */\n                unblockClient(receiver);\n            }\n        }\n    }\n}\n\n/* Helper function for handleClientsBlockedOnKeys(). This function is called\n * in order to check if we can serve clients blocked by modules using\n * RM_BlockClientOnKeys(), when the corresponding key was signaled as ready:\n * our goal here is to call the RedisModuleBlockedClient reply() callback to\n * see if the key is really able to serve the client, and in that case,\n * unblock it. */\nvoid serveClientsBlockedOnKeyByModule(readyList *rl) {\n    dictEntry *de;\n\n    /* We serve clients in the same order they blocked for\n     * this key, from the first blocked to the last. */\n    de = dictFind(rl->db->blocking_keys,rl->key);\n    if (de) {\n        list *clients = dictGetVal(de);\n        int numclients = listLength(clients);\n\n        while(numclients--) {\n            listNode *clientnode = listFirst(clients);\n            client *receiver = clientnode->value;\n\n            /* Put at the tail, so that at the next call\n             * we'll not run into it again: clients here may not be\n             * ready to be served, so they'll remain in the list\n             * sometimes. We want also be able to skip clients that are\n             * not blocked for the MODULE type safely. */\n            listRotateHeadToTail(clients);\n\n            if (receiver->btype != BLOCKED_MODULE) continue;\n\n            /* Note that if *this* client cannot be served by this key,\n             * it does not mean that another client that is next into the\n             * list cannot be served as well: they may be blocked by\n             * different modules with different triggers to consider if a key\n             * is ready or not. This means we can't exit the loop but need\n             * to continue after the first failure. */\n            if (!moduleTryServeClientBlockedOnKey(receiver, rl->key)) continue;\n\n            moduleUnblockClient(receiver);\n        }\n    }\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. It handles serving clients blocked in\n * lists, streams, and sorted sets, via a blocking commands.\n *\n * All the keys with at least one client blocked that received at least\n * one new element via some write 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 * This function is normally \"fair\", that is, it will server clients\n * using a FIFO behavior. However this fairness is violated in certain\n * edge cases, that is, when we have clients blocked at the same time\n * in a sorted set and in a list, for the same key (a very odd thing to\n * do client side, indeed!). Because mismatching clients (blocking for\n * a different type compared to the current key type) are moved in the\n * other side of the linked list. However as long as the key starts to\n * be used only for a single type, like virtually any Redis application will\n * do, the function is already fair. */\nvoid handleClientsBlockedOnKeys(void) {\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         * signalKeyAsReady() that may push new elements in server.ready_keys\n         * when handling clients blocked into BRPOPLPUSH. */\n        l = server.ready_keys;\n        server.ready_keys = listCreate();\n\n        while(listLength(l) != 0) {\n            listNode *ln = listFirst(l);\n            readyList *rl = ln->value;\n\n            /* First of all remove this key from db->ready_keys so that\n             * we can safely call signalKeyAsReady() against this key. */\n            dictDelete(rl->db->ready_keys,rl->key);\n\n            /* Even if we are not inside call(), increment the call depth\n             * in order to make sure that keys are expired against a fixed\n             * reference time, and not against the wallclock time. This\n             * way we can lookup an object multiple times (BRPOPLPUSH does\n             * that) without the risk of it being freed in the second\n             * lookup, invalidating the first one.\n             * See https://github.com/antirez/redis/pull/6554. */\n            server.fixed_time_expire++;\n            updateCachedTime(0);\n\n            /* Serve clients blocked on list key. */\n            robj *o = lookupKeyWrite(rl->db,rl->key);\n\n            if (o != NULL) {\n                if (o->type == OBJ_LIST)\n                    serveClientsBlockedOnListKey(o,rl);\n                else if (o->type == OBJ_ZSET)\n                    serveClientsBlockedOnSortedSetKey(o,rl);\n                else if (o->type == OBJ_STREAM)\n                    serveClientsBlockedOnStreamKey(o,rl);\n                /* We want to serve clients blocked on module keys\n                 * regardless of the object type: we don't know what the\n                 * module is trying to accomplish right now. */\n                serveClientsBlockedOnKeyByModule(rl);\n            }\n            server.fixed_time_expire--;\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/* This is how the current blocking lists/sorted sets/streams work, we use\n * BLPOP as example, but the concept is the same for other list ops, sorted\n * sets and XREAD.\n * - If the user calls BLPOP and the key exists and contains a non empty list\n *   then LPOP is called instead. So BLPOP is semantically the same as LPOP\n *   if blocking is not required.\n * - If instead BLPOP is called and the key does not exists or the list is\n *   empty we need to block. In order to do so we remove the notification for\n *   new data to read in the client socket (so that we'll not serve new\n *   requests if the blocking request is not served). Also we put the client\n *   in a dictionary (db->blocking_keys) mapping keys to a list of clients\n *   blocking for this keys.\n * - If a PUSH operation against a key with blocked clients waiting is\n *   performed, we mark this key as \"ready\", and after the current command,\n *   MULTI/EXEC block, or script, is executed, we serve all the clients waiting\n *   for this list, from the one that blocked first, to the last, accordingly\n *   to the number of elements we have in the ready list.\n */\n\n/* Set a client in blocking mode for the specified key (list, zset or stream),\n * with the specified timeout. The 'type' argument is BLOCKED_LIST,\n * BLOCKED_ZSET or BLOCKED_STREAM depending on the kind of operation we are\n * waiting for an empty key in order to awake the client. The client is blocked\n * for all the 'numkeys' keys as in the 'keys' argument. When we block for\n * stream keys, we also provide an array of streamID structures: clients will\n * be unblocked only when items with an ID greater or equal to the specified\n * one is appended to the stream. */\nvoid blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids) {\n    dictEntry *de;\n    list *l;\n    int j;\n\n    c->bpop.timeout = timeout;\n    c->bpop.target = target;\n\n    if (target != NULL) incrRefCount(target);\n\n    for (j = 0; j < numkeys; j++) {\n        /* Allocate our bkinfo structure, associated to each key the client\n         * is blocked for. */\n        bkinfo *bki = zmalloc(sizeof(*bki));\n        if (btype == BLOCKED_STREAM)\n            bki->stream_id = ids[j];\n\n        /* If the key already exists in the dictionary ignore it. */\n        if (dictAdd(c->bpop.keys,keys[j],bki) != DICT_OK) {\n            zfree(bki);\n            continue;\n        }\n        incrRefCount(keys[j]);\n\n        /* And in the other \"side\", to map keys -> clients */\n        de = dictFind(c->db->blocking_keys,keys[j]);\n        if (de == NULL) {\n            int retval;\n\n            /* For every key we take a list of clients blocked for it */\n            l = listCreate();\n            retval = dictAdd(c->db->blocking_keys,keys[j],l);\n            incrRefCount(keys[j]);\n            serverAssertWithInfo(c,keys[j],retval == DICT_OK);\n        } else {\n            l = dictGetVal(de);\n        }\n        listAddNodeTail(l,c);\n        bki->listnode = listLast(l);\n    }\n    blockClient(c,btype);\n}\n\n/* Unblock a client that's waiting in a blocking operation such as BLPOP.\n * You should never call this function directly, but unblockClient() instead. */\nvoid unblockClientWaitingData(client *c) {\n    dictEntry *de;\n    dictIterator *di;\n    list *l;\n\n    serverAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0);\n    di = dictGetIterator(c->bpop.keys);\n    /* The client may wait for multiple keys, so unblock it for every key. */\n    while((de = dictNext(di)) != NULL) {\n        robj *key = dictGetKey(de);\n        bkinfo *bki = dictGetVal(de);\n\n        /* Remove this client from the list of clients waiting for this key. */\n        l = dictFetchValue(c->db->blocking_keys,key);\n        serverAssertWithInfo(c,key,l != NULL);\n        listDelNode(l,bki->listnode);\n        /* If the list is empty we need to remove it to avoid wasting memory */\n        if (listLength(l) == 0)\n            dictDelete(c->db->blocking_keys,key);\n    }\n    dictReleaseIterator(di);\n\n    /* Cleanup the client structure */\n    dictEmpty(c->bpop.keys,NULL);\n    if (c->bpop.target) {\n        decrRefCount(c->bpop.target);\n        c->bpop.target = NULL;\n    }\n    if (c->bpop.xread_group) {\n        decrRefCount(c->bpop.xread_group);\n        decrRefCount(c->bpop.xread_consumer);\n        c->bpop.xread_group = NULL;\n        c->bpop.xread_consumer = NULL;\n    }\n}\n\n/* If the specified key has clients blocked waiting for list pushes, this\n * function will put the key reference into the server.ready_keys list.\n * Note that db->ready_keys is a hash table that allows us to avoid putting\n * the same key again and again in the list in case of multiple pushes\n * made by a script or in the context of MULTI/EXEC.\n *\n * The list will be finally processed by handleClientsBlockedOnKeys() */\nvoid signalKeyAsReady(redisDb *db, robj *key) {\n    readyList *rl;\n\n    /* No clients blocking for this key? No need to queue it. */\n    if (dictFind(db->blocking_keys,key) == NULL) return;\n\n    /* Key was already signaled? No need to queue it again. */\n    if (dictFind(db->ready_keys,key) != NULL) return;\n\n    /* Ok, we need to queue this key into server.ready_keys. */\n    rl = zmalloc(sizeof(*rl));\n    rl->key = key;\n    rl->db = 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    incrRefCount(key);\n    serverAssert(dictAdd(db->ready_keys,key,NULL) == DICT_OK);\n}\n\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/childinfo.c",
    "content": "/*\n * Copyright (c) 2016, 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 \"server.h\"\n#include <unistd.h>\n\n/* Open a child-parent channel used in order to move information about the\n * RDB / AOF saving process from the child to the parent (for instance\n * the amount of copy on write memory used) */\nvoid openChildInfoPipe(void) {\n    if (pipe(server.child_info_pipe) == -1) {\n        /* On error our two file descriptors should be still set to -1,\n         * but we call anyway cloesChildInfoPipe() since can't hurt. */\n        closeChildInfoPipe();\n    } else if (anetNonBlock(NULL,server.child_info_pipe[0]) != ANET_OK) {\n        closeChildInfoPipe();\n    } else {\n        memset(&server.child_info_data,0,sizeof(server.child_info_data));\n    }\n}\n\n/* Close the pipes opened with openChildInfoPipe(). */\nvoid closeChildInfoPipe(void) {\n    if (server.child_info_pipe[0] != -1 ||\n        server.child_info_pipe[1] != -1)\n    {\n        close(server.child_info_pipe[0]);\n        close(server.child_info_pipe[1]);\n        server.child_info_pipe[0] = -1;\n        server.child_info_pipe[1] = -1;\n    }\n}\n\n/* Send COW data to parent. The child should call this function after populating\n * the corresponding fields it want to sent (according to the process type). */\nvoid sendChildInfo(int ptype) {\n    if (server.child_info_pipe[1] == -1) return;\n    server.child_info_data.magic = CHILD_INFO_MAGIC;\n    server.child_info_data.process_type = ptype;\n    ssize_t wlen = sizeof(server.child_info_data);\n    if (write(server.child_info_pipe[1],&server.child_info_data,wlen) != wlen) {\n        /* Nothing to do on error, this will be detected by the other side. */\n    }\n}\n\n/* Receive COW data from parent. */\nvoid receiveChildInfo(void) {\n    if (server.child_info_pipe[0] == -1) return;\n    ssize_t wlen = sizeof(server.child_info_data);\n    if (read(server.child_info_pipe[0],&server.child_info_data,wlen) == wlen &&\n        server.child_info_data.magic == CHILD_INFO_MAGIC)\n    {\n        if (server.child_info_data.process_type == CHILD_INFO_TYPE_RDB) {\n            server.stat_rdb_cow_bytes = server.child_info_data.cow_size;\n        } else if (server.child_info_data.process_type == CHILD_INFO_TYPE_AOF) {\n            server.stat_aof_cow_bytes = server.child_info_data.cow_size;\n        } else if (server.child_info_data.process_type == CHILD_INFO_TYPE_MODULE) {\n            server.stat_module_cow_bytes = server.child_info_data.cow_size;\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.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/stat.h>\n#include <sys/file.h>\n#include <math.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. */\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(connection *conn);\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(const 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);\nsds representClusterNodeFlags(sds ci, uint16_t flags);\nuint64_t clusterGetMaxEpoch(void);\nint clusterBumpConfigEpochWithoutConsensus(void);\nvoid moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len);\n\n/* -----------------------------------------------------------------------------\n * Initialization\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), C_ERR is returned.\n * If the configuration was loaded from the file, C_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 C_ERR;\n        } else {\n            serverLog(LL_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 C_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 C_ERR;\n    }\n\n    /* Parse the file. Note that single lines of the cluster config file can\n     * be really long as they include all the hash slots of the node.\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     * To simplify we allocate 1024+CLUSTER_SLOTS*128 bytes per line. */\n    maxline = 1024+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' || line[0] == '\\0') 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            if (!(argc % 2)) goto fmterr;\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                    serverLog(LL_WARNING,\n                        \"Skipping unknown cluster config variable '%s'\",\n                        argv[j]);\n                }\n            }\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* Regular config lines have at least eight fields */\n        if (argc < 8) {\n            sdsfreesplitres(argv,argc);\n            goto fmterr;\n        }\n\n        /* Create this node if it does not exist */\n        n = clusterLookupNode(argv[0]);\n        if (!n) {\n            n = createClusterNode(argv[0],0);\n            clusterAddNode(n);\n        }\n        /* Address and port */\n        if ((p = strrchr(argv[1],':')) == NULL) {\n            sdsfreesplitres(argv,argc);\n            goto fmterr;\n        }\n        *p = '\\0';\n        memcpy(n->ip,argv[1],strlen(argv[1])+1);\n        char *port = p+1;\n        char *busp = strchr(port,'@');\n        if (busp) {\n            *busp = '\\0';\n            busp++;\n        }\n        n->port = atoi(port);\n        /* In older versions of nodes.conf the \"@busport\" part is missing.\n         * In this case we set it to the default offset of 10000 from the\n         * base port. */\n        n->cport = busp ? atoi(busp) : n->port + CLUSTER_PORT_INCR;\n\n        /* Parse flags */\n        p = s = argv[2];\n        while(p) {\n            p = strchr(s,',');\n            if (p) *p = '\\0';\n            if (!strcasecmp(s,\"myself\")) {\n                serverAssert(server.cluster->myself == NULL);\n                myself = server.cluster->myself = n;\n                n->flags |= CLUSTER_NODE_MYSELF;\n            } else if (!strcasecmp(s,\"master\")) {\n                n->flags |= CLUSTER_NODE_MASTER;\n            } else if (!strcasecmp(s,\"slave\")) {\n                n->flags |= CLUSTER_NODE_SLAVE;\n            } else if (!strcasecmp(s,\"fail?\")) {\n                n->flags |= CLUSTER_NODE_PFAIL;\n            } else if (!strcasecmp(s,\"fail\")) {\n                n->flags |= CLUSTER_NODE_FAIL;\n                n->fail_time = mstime();\n            } else if (!strcasecmp(s,\"handshake\")) {\n                n->flags |= CLUSTER_NODE_HANDSHAKE;\n            } else if (!strcasecmp(s,\"noaddr\")) {\n                n->flags |= CLUSTER_NODE_NOADDR;\n            } else if (!strcasecmp(s,\"nofailover\")) {\n                n->flags |= CLUSTER_NODE_NOFAILOVER;\n            } else if (!strcasecmp(s,\"noflags\")) {\n                /* nothing to do */\n            } else {\n                serverPanic(\"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        if (argv[3][0] != '-') {\n            master = clusterLookupNode(argv[3]);\n            if (!master) {\n                master = createClusterNode(argv[3],0);\n                clusterAddNode(master);\n            }\n            n->slaveof = master;\n            clusterNodeAddSlave(master,n);\n        }\n\n        /* Set ping sent / pong received timestamps */\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->configEpoch = strtoull(argv[6],NULL,10);\n\n        /* Populate hash slots served by this instance. */\n        for (j = 8; j < argc; j++) {\n            int start, stop;\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                serverAssert(p != NULL);\n                *p = '\\0';\n                direction = p[1]; /* Either '>' or '<' */\n                slot = atoi(argv[j]+1);\n                if (slot < 0 || slot >= CLUSTER_SLOTS) {\n                    sdsfreesplitres(argv,argc);\n                    goto fmterr;\n                }\n                p += 3;\n                cn = clusterLookupNode(p);\n                if (!cn) {\n                    cn = createClusterNode(p,0);\n                    clusterAddNode(cn);\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            } else if ((p = strchr(argv[j],'-')) != NULL) {\n                *p = '\\0';\n                start = atoi(argv[j]);\n                stop = atoi(p+1);\n            } else {\n                start = stop = atoi(argv[j]);\n            }\n            if (start < 0 || start >= CLUSTER_SLOTS ||\n                stop < 0 || stop >= CLUSTER_SLOTS)\n            {\n                sdsfreesplitres(argv,argc);\n                goto fmterr;\n            }\n            while(start <= stop) clusterAddSlot(n, start++);\n        }\n\n        sdsfreesplitres(argv,argc);\n    }\n    /* Config sanity check */\n    if (server.cluster->myself == NULL) goto fmterr;\n\n    zfree(line);\n    fclose(fp);\n\n    serverLog(LL_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 C_OK;\n\nfmterr:\n    serverLog(LL_WARNING,\n        \"Unrecoverable error: corrupted cluster config file.\");\n    zfree(line);\n    if (fp) 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. */\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(CLUSTER_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 > (off_t)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\nvoid clusterSaveConfigOrDie(int do_fsync) {\n    if (clusterSaveConfig(do_fsync) == -1) {\n        serverLog(LL_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 C_OK is returned, otherwise an error is logged and\n * the function returns C_ERR to signal a lock was not acquired. */\nint clusterLockConfig(char *filename) {\n/* flock() does not exist on Solaris\n * and a fcntl-based solution won't help, as we constantly re-open that file,\n * which will release _all_ locks anyway\n */\n#if !defined(__sun)\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        serverLog(LL_WARNING,\n            \"Can't open %s in order to acquire a lock: %s\",\n            filename, strerror(errno));\n        return C_ERR;\n    }\n\n    if (flock(fd,LOCK_EX|LOCK_NB) == -1) {\n        if (errno == EWOULDBLOCK) {\n            serverLog(LL_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            serverLog(LL_WARNING,\n                \"Impossible to lock %s: %s\", filename, strerror(errno));\n        }\n        close(fd);\n        return C_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#endif /* __sun */\n\n    return C_OK;\n}\n\n/* Some flags (currently just the NOFAILOVER flag) may need to be updated\n * in the \"myself\" node based on the current configuration of the node,\n * that may change at runtime via CONFIG SET. This function changes the\n * set of flags in myself->flags accordingly. */\nvoid clusterUpdateMyselfFlags(void) {\n    int oldflags = myself->flags;\n    int nofailover = server.cluster_slave_no_failover ?\n                     CLUSTER_NODE_NOFAILOVER : 0;\n    myself->flags &= ~CLUSTER_NODE_NOFAILOVER;\n    myself->flags |= nofailover;\n    if (myself->flags != oldflags) {\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                             CLUSTER_TODO_UPDATE_STATE);\n    }\n}\n\nvoid clusterInit(void) {\n    int saveconf = 0;\n\n    server.cluster = zmalloc(sizeof(clusterState));\n    server.cluster->myself = NULL;\n    server.cluster->currentEpoch = 0;\n    server.cluster->state = 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->cant_failover_reason = CLUSTER_CANT_FAILOVER_NONE;\n    server.cluster->lastVoteEpoch = 0;\n    for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {\n        server.cluster->stats_bus_messages_sent[i] = 0;\n        server.cluster->stats_bus_messages_received[i] = 0;\n    }\n    server.cluster->stats_pfail_nodes = 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) == C_ERR)\n        exit(1);\n\n    /* Load or create a new nodes configuration. */\n    if (clusterLoadConfig(server.cluster_configfile) == C_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,CLUSTER_NODE_MYSELF|CLUSTER_NODE_MASTER);\n        serverLog(LL_NOTICE,\"No cluster configuration found, I'm %.40s\",\n            myself->name);\n        clusterAddNode(myself);\n        saveconf = 1;\n    }\n    if (saveconf) clusterSaveConfigOrDie(1);\n\n    /* We need a listening TCP port for our cluster messaging needs. */\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    int port = server.tls_cluster ? server.tls_port : server.port;\n    if (port > (65535-CLUSTER_PORT_INCR)) {\n        serverLog(LL_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    if (listenToPort(port+CLUSTER_PORT_INCR,\n        server.cfd,&server.cfd_count) == C_ERR)\n    {\n        exit(1);\n    } else {\n        int j;\n\n        for (j = 0; j < server.cfd_count; j++) {\n            if (aeCreateFileEvent(server.el, server.cfd[j], AE_READABLE,\n                clusterAcceptHandler, NULL) == AE_ERR)\n                    serverPanic(\"Unrecoverable error creating Redis Cluster \"\n                                \"file event.\");\n        }\n    }\n\n    /* The slots -> keys map is a radix tree. Initialize it here. */\n    server.cluster->slots_to_keys = raxNew();\n    memset(server.cluster->slots_keys_count,0,\n           sizeof(server.cluster->slots_keys_count));\n\n    /* Set myself->port / cport to my listening ports, we'll just need to\n     * discover the IP address via MEET messages. */\n    myself->port = port;\n    myself->cport = port+CLUSTER_PORT_INCR;\n    if (server.cluster_announce_port)\n        myself->port = server.cluster_announce_port;\n    if (server.cluster_announce_bus_port)\n        myself->cport = server.cluster_announce_bus_port;\n\n    server.cluster->mf_end = 0;\n    resetManualFailover();\n    clusterUpdateMyselfFlags();\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.\n * 8) If the node was a slave, the whole data set is flushed away. */\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        emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);\n    }\n\n    /* Close slots, reset manual failover state. */\n    clusterCloseAllSlots();\n    resetManualFailover();\n\n    /* Unassign all the slots. */\n    for (j = 0; j < 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        serverLog(LL_WARNING, \"configEpoch set to 0 via CLUSTER RESET HARD\");\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, CLUSTER_NAMELEN);\n        dictDelete(server.cluster->nodes,oldname);\n        sdsfree(oldname);\n        getRandomHexChars(myself->name, CLUSTER_NAMELEN);\n        clusterAddNode(myself);\n        serverLog(LL_NOTICE,\"Node hard reset, now I'm %.40s\", myself->name);\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\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->conn = NULL;\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. */\nvoid freeClusterLink(clusterLink *link) {\n    if (link->conn) {\n        connClose(link->conn);\n        link->conn = NULL;\n    }\n    sdsfree(link->sndbuf);\n    sdsfree(link->rcvbuf);\n    if (link->node)\n        link->node->link = NULL;\n    zfree(link);\n}\n\nstatic void clusterConnAcceptHandler(connection *conn) {\n    clusterLink *link;\n\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_VERBOSE,\n                \"Error accepting cluster node connection: %s\", connGetLastError(conn));\n        connClose(conn);\n        return;\n    }\n\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->conn = conn;\n    connSetPrivateData(conn, link);\n\n    /* Register read handler */\n    connSetReadHandler(conn, clusterReadHandler);\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[NET_IP_STR_LEN];\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(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                serverLog(LL_VERBOSE,\n                    \"Error accepting cluster node: %s\", server.neterr);\n            return;\n        }\n\n        connection *conn = server.tls_cluster ? connCreateAcceptedTLS(cfd,1) : connCreateAcceptedSocket(cfd);\n        connNonBlock(conn);\n        connEnableTcpNoDelay(conn);\n\n        /* Use non-blocking I/O for cluster messages. */\n        serverLog(LL_VERBOSE,\"Accepting cluster node connection from %s:%d\", cip, cport);\n\n        /* Accept the connection now.  connAccept() may call our handler directly\n         * or schedule it for later depending on connection implementation.\n         */\n        if (connAccept(conn, clusterConnAcceptHandler) == C_ERR) {\n            if (connGetState(conn) == CONN_STATE_ERROR)\n                serverLog(LL_VERBOSE,\n                        \"Error accepting cluster node connection: %s\",\n                        connGetLastError(conn));\n            connClose(conn);\n            return;\n        }\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). */\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 between {} ? 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 * 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 * The node is created and returned to the user, but it is not automatically\n * added to the nodes hash table. */\nclusterNode *createClusterNode(char *nodename, int flags) {\n    clusterNode *node = zmalloc(sizeof(*node));\n\n    if (nodename)\n        memcpy(node->name, nodename, CLUSTER_NAMELEN);\n    else\n        getRandomHexChars(node->name, CLUSTER_NAMELEN);\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->data_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->cport = 0;\n    node->fail_reports = listCreate();\n    node->voted_time = 0;\n    node->orphaned_time = 0;\n    node->repl_offset_time = 0;\n    node->repl_offset = 0;\n    listSetFreeMethod(node->fail_reports,zfree);\n    return node;\n}\n\n/* This function is called every time we get a failure report from a node.\n * The side effect is to populate the fail_reports list (or to update\n * the timestamp of an existing report).\n *\n * 'failing' is the node that is in failure state according to the\n * 'sender' node.\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. */\nint clusterNodeAddFailureReport(clusterNode *failing, clusterNode *sender) {\n    list *l = failing->fail_reports;\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    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        if (fr->node == sender) {\n            fr->time = mstime();\n            return 0;\n        }\n    }\n\n    /* Otherwise create a new report. */\n    fr = zmalloc(sizeof(*fr));\n    fr->node = sender;\n    fr->time = mstime();\n    listAddNodeTail(l,fr);\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. */\nvoid clusterNodeCleanupFailureReports(clusterNode *node) {\n    list *l = node->fail_reports;\n    listNode *ln;\n    listIter li;\n    clusterNodeFailReport *fr;\n    mstime_t maxtime = server.cluster_node_timeout *\n                     CLUSTER_FAIL_REPORT_VALIDITY_MULT;\n    mstime_t now = mstime();\n\n    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\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 * 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 * The function returns 1 if the failure report was found and removed.\n * Otherwise 0 is returned. */\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    listRewind(l,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        fr = ln->value;\n        if (fr->node == sender) break;\n    }\n    if (!ln) return 0; /* No failure report from this sender. */\n\n    /* Remove the failure report. */\n    listDelNode(l,ln);\n    clusterNodeCleanupFailureReports(node);\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. */\nint clusterNodeFailureReportsCount(clusterNode *node) {\n    clusterNodeCleanupFailureReports(node);\n    return listLength(node->fail_reports);\n}\n\nint clusterNodeRemoveSlave(clusterNode *master, clusterNode *slave) {\n    int j;\n\n    for (j = 0; j < master->numslaves; j++) {\n        if (master->slaves[j] == slave) {\n            if ((j+1) < master->numslaves) {\n                int remaining_slaves = (master->numslaves - j) - 1;\n                memmove(master->slaves+j,master->slaves+(j+1),\n                        (sizeof(*master->slaves) * remaining_slaves));\n            }\n            master->numslaves--;\n            if (master->numslaves == 0)\n                master->flags &= ~CLUSTER_NODE_MIGRATE_TO;\n            return C_OK;\n        }\n    }\n    return C_ERR;\n}\n\nint clusterNodeAddSlave(clusterNode *master, clusterNode *slave) {\n    int j;\n\n    /* If it's already a slave, don't add it again. */\n    for (j = 0; j < master->numslaves; j++)\n        if (master->slaves[j] == slave) return C_ERR;\n    master->slaves = zrealloc(master->slaves,\n        sizeof(clusterNode*)*(master->numslaves+1));\n    master->slaves[master->numslaves] = slave;\n    master->numslaves++;\n    master->flags |= CLUSTER_NODE_MIGRATE_TO;\n    return C_OK;\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/* Low level cleanup of the node structure. Only called by clusterDelNode(). */\nvoid freeClusterNode(clusterNode *n) {\n    sds nodename;\n    int j;\n\n    /* If the node has associated slaves, we have to set\n     * all the slaves->slaveof fields to NULL (unknown). */\n    for (j = 0; j < n->numslaves; j++)\n        n->slaves[j]->slaveof = NULL;\n\n    /* Remove this node from the list of slaves of its master. */\n    if (nodeIsSlave(n) && n->slaveof) clusterNodeRemoveSlave(n->slaveof,n);\n\n    /* Unlink from the set of nodes. */\n    nodename = sdsnewlen(n->name, CLUSTER_NAMELEN);\n    serverAssert(dictDelete(server.cluster->nodes,nodename) == DICT_OK);\n    sdsfree(nodename);\n\n    /* Release link and associated data structures. */\n    if (n->link) freeClusterLink(n->link);\n    listRelease(n->fail_reports);\n    zfree(n->slaves);\n    zfree(n);\n}\n\n/* Add a node to the nodes hash table */\nint clusterAddNode(clusterNode *node) {\n    int retval;\n\n    retval = dictAdd(server.cluster->nodes,\n            sdsnewlen(node->name,CLUSTER_NAMELEN), node);\n    return (retval == DICT_OK) ? C_OK : C_ERR;\n}\n\n/* Remove a node from the cluster. The function performs the high level\n * cleanup, calling freeClusterNode() for the low level cleanup.\n * Here we do the following:\n *\n * 1) Mark all the slots handled by it as unassigned.\n * 2) Remove all the failure reports sent by this node and referenced by\n *    other nodes.\n * 3) Free the node with freeClusterNode() that will in turn remove it\n *    from the hash table and from the list of slaves of its master, if\n *    it is a slave node.\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 < CLUSTER_SLOTS; j++) {\n        if (server.cluster->importing_slots_from[j] == delnode)\n            server.cluster->importing_slots_from[j] = NULL;\n        if (server.cluster->migrating_slots_to[j] == delnode)\n            server.cluster->migrating_slots_to[j] = NULL;\n        if (server.cluster->slots[j] == delnode)\n            clusterDelSlot(j);\n    }\n\n    /* 2) Remove failure reports. */\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) Free the node, unlinking it from the cluster. */\n    freeClusterNode(delnode);\n}\n\n/* Node lookup by name */\nclusterNode *clusterLookupNode(const char *name) {\n    sds s = sdsnewlen(name, 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. */\nvoid clusterRenameNode(clusterNode *node, char *newname) {\n    int retval;\n    sds s = sdsnewlen(node->name, CLUSTER_NAMELEN);\n\n    serverLog(LL_DEBUG,\"Renaming node %.40s into %.40s\",\n        node->name, newname);\n    retval = dictDelete(server.cluster->nodes, s);\n    sdsfree(s);\n    serverAssert(retval == DICT_OK);\n    memcpy(node->name, newname, CLUSTER_NAMELEN);\n    clusterAddNode(node);\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER config epoch handling\n * -------------------------------------------------------------------------- */\n\n/* Return the greatest configEpoch found in the cluster, or the current\n * epoch if greater than any node configEpoch. */\nuint64_t clusterGetMaxEpoch(void) {\n    uint64_t max = 0;\n    dictIterator *di;\n    dictEntry *de;\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/* If this node epoch is zero or is not already the greatest across the\n * cluster (from the POV of the local configuration), this function will:\n *\n * 1) Generate a new config epoch, incrementing the current epoch.\n * 2) Assign the new epoch to this node, WITHOUT any consensus.\n * 3) Persist the configuration on disk before sending packets with the\n *    new configuration.\n *\n * If the new config epoch is generated and assigend, C_OK is returned,\n * otherwise C_ERR is returned (since the node has already the greatest\n * configuration around) and no operation is performed.\n *\n * Important note: this function violates the principle that config epochs\n * should be generated with consensus and should be unique across the cluster.\n * However Redis Cluster uses this auto-generated new config epochs in two\n * cases:\n *\n * 1) When slots are closed after importing. Otherwise resharding would be\n *    too expensive.\n * 2) When CLUSTER FAILOVER is called with options that force a slave to\n *    failover its master even if there is not master majority able to\n *    create a new configuration epoch.\n *\n * Redis Cluster will not explode using this function, even in the case of\n * a collision between this node and another node, generating the same\n * configuration epoch unilaterally, because the config epoch conflict\n * resolution algorithm will eventually move colliding nodes to different\n * config epochs. However using this function may violate the \"last failover\n * wins\" rule, so should only be used with care. */\nint clusterBumpConfigEpochWithoutConsensus(void) {\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_SAVE_CONFIG|\n                             CLUSTER_TODO_FSYNC_CONFIG);\n        serverLog(LL_WARNING,\n            \"New configEpoch set to %llu\",\n            (unsigned long long) myself->configEpoch);\n        return C_OK;\n    } else {\n        return C_ERR;\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,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    serverLog(LL_VERBOSE,\n        \"WARNING: configEpoch collision with node %.40s.\"\n        \" configEpoch set to %llu\",\n        sender->name,\n        (unsigned long long) myself->configEpoch);\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER nodes blacklist\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 CLUSTER_BLACKLIST_TTL).\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 * Currently the 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 * 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#define 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 * However without the cleanup during long uptimes and with some automated\n * node add/removal procedures, entries could accumulate. */\nvoid clusterBlacklistCleanup(void) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(server.cluster->nodes_black_list);\n    while((de = dictNext(di)) != NULL) {\n        int64_t expire = dictGetUnsignedIntegerVal(de);\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. */\nvoid clusterBlacklistAddNode(clusterNode *node) {\n    dictEntry *de;\n    sds id = sdsnewlen(node->name,CLUSTER_NAMELEN);\n\n    clusterBlacklistCleanup();\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    de = dictFind(server.cluster->nodes_black_list,id);\n    dictSetUnsignedIntegerVal(de,time(NULL)+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. */\nint clusterBlacklistExists(char *nodeid) {\n    sds id = sdsnewlen(nodeid,CLUSTER_NAMELEN);\n    int retval;\n\n    clusterBlacklistCleanup();\n    retval = dictFind(server.cluster->nodes_black_list,id) != NULL;\n    sdsfree(id);\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 * 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 * 2) We believe this node is in PFAIL state.\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 * 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 * 1) Either we reach the majority and eventually the FAIL state will propagate\n *    to all the cluster.\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 */\nvoid markNodeAsFailingIfNeeded(clusterNode *node) {\n    int failures;\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    failures = clusterNodeFailureReportsCount(node);\n    /* Also count myself as a voter if I'm a master. */\n    if (nodeIsMaster(myself)) failures++;\n    if (failures < needed_quorum) return; /* No weak agreement from masters. */\n\n    serverLog(LL_NOTICE,\n        \"Marking node %.40s as failing (quorum reached).\", node->name);\n\n    /* Mark the node as failing. */\n    node->flags &= ~CLUSTER_NODE_PFAIL;\n    node->flags |= CLUSTER_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    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. */\nvoid clearNodeFailureIfNeeded(clusterNode *node) {\n    mstime_t now = mstime();\n\n    serverAssert(nodeFailed(node));\n\n    /* For slaves we always clear the FAIL flag if we can contact the\n     * node again. */\n    if (nodeIsSlave(node) || node->numslots == 0) {\n        serverLog(LL_NOTICE,\n            \"Clear FAIL state for node %.40s: %s is reachable again.\",\n                node->name,\n                nodeIsSlave(node) ? \"replica\" : \"master without slots\");\n        node->flags &= ~CLUSTER_NODE_FAIL;\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n    }\n\n    /* If it is a master and...\n     * 1) The FAIL state is old enough.\n     * 2) It is yet serving slots from our point of view (not failed over).\n     * Apparently no one is going to fix these slots, clear the FAIL flag. */\n    if (nodeIsMaster(node) && node->numslots > 0 &&\n        (now - node->fail_time) >\n        (server.cluster_node_timeout * CLUSTER_FAIL_UNDO_TIME_MULT))\n    {\n        serverLog(LL_NOTICE,\n            \"Clear FAIL state for node %.40s: is reachable again and nobody is serving its slots after some time.\",\n                node->name);\n        node->flags &= ~CLUSTER_NODE_FAIL;\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. */\nint clusterHandshakeInProgress(char *ip, int port, int cport) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (!nodeInHandshake(node)) continue;\n        if (!strcasecmp(node->ip,ip) &&\n            node->port == port &&\n            node->cport == cport) break;\n    }\n    dictReleaseIterator(di);\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 * EAGAIN - There is already an handshake in progress for this address.\n * EINVAL - IP or port are not valid. */\nint clusterStartHandshake(char *ip, int port, int cport) {\n    clusterNode *n;\n    char norm_ip[NET_IP_STR_LEN];\n    struct sockaddr_storage sa;\n\n    /* IP sanity check */\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    if (port <= 0 || port > 65535 || cport <= 0 || cport > 65535) {\n        errno = EINVAL;\n        return 0;\n    }\n\n    /* Set norm_ip as the normalized string representation of the node\n     * IP address. */\n    memset(norm_ip,0,NET_IP_STR_LEN);\n    if (sa.ss_family == AF_INET)\n        inet_ntop(AF_INET,\n            (void*)&(((struct sockaddr_in *)&sa)->sin_addr),\n            norm_ip,NET_IP_STR_LEN);\n    else\n        inet_ntop(AF_INET6,\n            (void*)&(((struct sockaddr_in6 *)&sa)->sin6_addr),\n            norm_ip,NET_IP_STR_LEN);\n\n    if (clusterHandshakeInProgress(norm_ip,port,cport)) {\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     * handshake. */\n    n = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE|CLUSTER_NODE_MEET);\n    memcpy(n->ip,norm_ip,sizeof(n->ip));\n    n->port = port;\n    n->cport = cport;\n    clusterAddNode(n);\n    return 1;\n}\n\n/* Process the gossip section of PING or PONG packets.\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. */\nvoid clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {\n    uint16_t count = ntohs(hdr->count);\n    clusterMsgDataGossip *g = (clusterMsgDataGossip*) hdr->data.ping.gossip;\n    clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender);\n\n    while(count--) {\n        uint16_t flags = ntohs(g->flags);\n        clusterNode *node;\n        sds ci;\n\n        if (server.verbosity == LL_DEBUG) {\n            ci = representClusterNodeFlags(sdsempty(), flags);\n            serverLog(LL_DEBUG,\"GOSSIP %.40s %s:%d@%d %s\",\n                g->nodename,\n                g->ip,\n                ntohs(g->port),\n                ntohs(g->cport),\n                ci);\n            sdsfree(ci);\n        }\n\n        /* Update our state accordingly to the gossip sections */\n        node = clusterLookupNode(g->nodename);\n        if (node) {\n            /* We already know this node.\n               Handle failure reports, only when the sender is a master. */\n            if (sender && nodeIsMaster(sender) && node != myself) {\n                if (flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) {\n                    if (clusterNodeAddFailureReport(node,sender)) {\n                        serverLog(LL_VERBOSE,\n                            \"Node %.40s reported node %.40s as not reachable.\",\n                            sender->name, node->name);\n                    }\n                    markNodeAsFailingIfNeeded(node);\n                } else {\n                    if (clusterNodeDelFailureReport(node,sender)) {\n                        serverLog(LL_VERBOSE,\n                            \"Node %.40s reported node %.40s is back online.\",\n                            sender->name, node->name);\n                    }\n                }\n            }\n\n            /* If from our POV the node is up (no failure flags are set),\n             * we have no pending ping for the node, nor we have failure\n             * reports for this node, update the last pong time with the\n             * one we see from the other nodes. */\n            if (!(flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) &&\n                node->ping_sent == 0 &&\n                clusterNodeFailureReportsCount(node) == 0)\n            {\n                mstime_t pongtime = ntohl(g->pong_received);\n                pongtime *= 1000; /* Convert back to milliseconds. */\n\n                /* Replace the pong time with the received one only if\n                 * it's greater than our view but is not in the future\n                 * (with 500 milliseconds tolerance) from the POV of our\n                 * clock. */\n                if (pongtime <= (server.mstime+500) &&\n                    pongtime > node->pong_received)\n                {\n                    node->pong_received = pongtime;\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 of a node that\n             * can talk with this other node, update the address, disconnect\n             * the old link if any, so that we'll attempt to connect with the\n             * new address. */\n            if (node->flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL) &&\n                !(flags & CLUSTER_NODE_NOADDR) &&\n                !(flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) &&\n                (strcasecmp(node->ip,g->ip) ||\n                 node->port != ntohs(g->port) ||\n                 node->cport != ntohs(g->cport)))\n            {\n                if (node->link) freeClusterLink(node->link);\n                memcpy(node->ip,g->ip,NET_IP_STR_LEN);\n                node->port = ntohs(g->port);\n                node->cport = ntohs(g->cport);\n                node->flags &= ~CLUSTER_NODE_NOADDR;\n            }\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             * 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            if (sender &&\n                !(flags & CLUSTER_NODE_NOADDR) &&\n                !clusterBlacklistExists(g->nodename))\n            {\n                clusterStartHandshake(g->ip,ntohs(g->port),ntohs(g->cport));\n            }\n        }\n\n        /* Next node */\n        g++;\n    }\n}\n\n/* IP -> string conversion. 'buf' is supposed to at least be 46 bytes.\n * If 'announced_ip' length is non-zero, it is used instead of extracting\n * the IP from the socket peer address. */\nvoid nodeIp2String(char *buf, clusterLink *link, char *announced_ip) {\n    if (announced_ip[0] != '\\0') {\n        memcpy(buf,announced_ip,NET_IP_STR_LEN);\n        buf[NET_IP_STR_LEN-1] = '\\0'; /* We are not sure the input is sane. */\n    } else {\n        connPeerToString(link->conn, buf, NET_IP_STR_LEN, NULL);\n    }\n}\n\n/* Update the node address to the IP address that can be extracted\n * from link->fd, or if hdr->myip is non empty, to the address the node\n * is announcing us. The port is taken from the packet header as well.\n *\n * If the address or port changed, disconnect the node link so that we'll\n * connect again to the new address.\n *\n * If the ip/port pair are already correct no operation is performed at\n * all.\n *\n * The function returns 0 if the node address is still the same,\n * otherwise 1 is returned. */\nint nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,\n                              clusterMsg *hdr)\n{\n    char ip[NET_IP_STR_LEN] = {0};\n    int port = ntohs(hdr->port);\n    int cport = ntohs(hdr->cport);\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    if (link == node->link) return 0;\n\n    nodeIp2String(ip,link,hdr->myip);\n    if (node->port == port && node->cport == cport &&\n        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    node->cport = cport;\n    if (node->link) freeClusterLink(node->link);\n    node->flags &= ~CLUSTER_NODE_NOADDR;\n    serverLog(LL_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    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. */\nvoid clusterSetNodeAsMaster(clusterNode *n) {\n    if (nodeIsMaster(n)) return;\n\n    if (n->slaveof) {\n        clusterNodeRemoveSlave(n->slaveof,n);\n        if (n != myself) n->flags |= CLUSTER_NODE_MIGRATE_TO;\n    }\n    n->flags &= ~CLUSTER_NODE_SLAVE;\n    n->flags |= CLUSTER_NODE_MASTER;\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 * 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 * The 'sender' is the node for which we received a configuration update.\n * Sometimes it is not actually the \"Sender\" of the information, like in the\n * case we receive the info via an UPDATE packet. */\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[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    curmaster = nodeIsMaster(myself) ? myself : myself->slaveof;\n\n    if (sender == myself) {\n        serverLog(LL_WARNING,\"Discarding UPDATE message about myself.\");\n        return;\n    }\n\n    for (j = 0; j < CLUSTER_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                if (server.cluster->slots[j] == curmaster)\n                    newmaster = sender;\n                clusterDelSlot(j);\n                clusterAddSlot(sender,j);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE|\n                                     CLUSTER_TODO_FSYNC_CONFIG);\n            }\n        }\n    }\n\n    /* After updating the slots configuration, don't do any actual change\n     * in the state of the server if a module disabled Redis Cluster\n     * keys redirections. */\n    if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)\n        return;\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     * 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     * 2) We are a slave and our master is left without slots. We need\n     *    to replicate to the new slots owner. */\n    if (newmaster && curmaster->numslots == 0) {\n        serverLog(LL_WARNING,\n            \"Configuration change detected. Reconfiguring myself \"\n            \"as a replica of %.40s\", sender->name);\n        clusterSetMaster(sender);\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/* 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 * 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). */\nint clusterProcessPacket(clusterLink *link) {\n    clusterMsg *hdr = (clusterMsg*) link->rcvbuf;\n    uint32_t totlen = ntohl(hdr->totlen);\n    uint16_t type = ntohs(hdr->type);\n    mstime_t now = mstime();\n\n    if (type < CLUSTERMSG_TYPE_COUNT)\n        server.cluster->stats_bus_messages_received[type]++;\n    serverLog(LL_DEBUG,\"--- Processing packet of type %d, %lu bytes\",\n        type, (unsigned long) totlen);\n\n    /* Perform sanity checks */\n    if (totlen < 16) return 1; /* At least signature, version, totlen, count. */\n    if (totlen > sdslen(link->rcvbuf)) return 1;\n\n    if (ntohs(hdr->ver) != CLUSTER_PROTO_VER) {\n        /* Can't handle messages of different versions. */\n        return 1;\n    }\n\n    uint16_t flags = ntohs(hdr->flags);\n    uint64_t senderCurrentEpoch = 0, senderConfigEpoch = 0;\n    clusterNode *sender;\n\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                8 +\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    } else if (type == CLUSTERMSG_TYPE_MODULE) {\n        uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n\n        explen += sizeof(clusterMsgDataPublish) -\n                3 + ntohl(hdr->data.module.msg.len);\n        if (totlen != explen) return 1;\n    }\n\n    /* Check if the sender is a known node. Note that for incoming connections\n     * we don't store link->node information, but resolve the node by the\n     * ID in the header each time in the current implementation. */\n    sender = clusterLookupNode(hdr->sender);\n\n    /* Update the last time we saw any data from this node. We\n     * use this in order to avoid detecting a timeout from a node that\n     * is just sending a lot of data in the cluster bus, for instance\n     * because of Pub/Sub. */\n    if (sender) sender->data_received = now;\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 = now;\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            serverLog(LL_WARNING,\n                \"Received replication offset for paused \"\n                \"master manual failover: %lld\",\n                server.cluster->mf_master_offset);\n        }\n    }\n\n    /* Initial processing of PING and MEET requests replying with a PONG. */\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_MEET) {\n        serverLog(LL_DEBUG,\"Ping packet received: %p\", (void*)link->node);\n\n        /* We use incoming MEET messages in order to set the address\n         * for 'myself', since only other cluster nodes will send us\n         * MEET messages on handshakes, when the cluster joins, or\n         * later if we changed address, and those nodes will use our\n         * official address to connect to us. So by obtaining this address\n         * from the socket is a simple way to discover / update our own\n         * address in the cluster without it being hardcoded in the config.\n         *\n         * However if we don't have an address at all, we update the address\n         * even with a normal PING packet. If it's wrong it will be fixed\n         * by MEET later. */\n        if ((type == CLUSTERMSG_TYPE_MEET || myself->ip[0] == '\\0') &&\n            server.cluster_announce_ip == NULL)\n        {\n            char ip[NET_IP_STR_LEN];\n\n            if (connSockName(link->conn,ip,sizeof(ip),NULL) != -1 &&\n                strcmp(ip,myself->ip))\n            {\n                memcpy(myself->ip,ip,NET_IP_STR_LEN);\n                serverLog(LL_WARNING,\"IP address for this node updated to %s\",\n                    myself->ip);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n            }\n        }\n\n        /* Add this node if it is new for us and the msg type is MEET.\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        if (!sender && type == CLUSTERMSG_TYPE_MEET) {\n            clusterNode *node;\n\n            node = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE);\n            nodeIp2String(node->ip,link,hdr->myip);\n            node->port = ntohs(hdr->port);\n            node->cport = ntohs(hdr->cport);\n            clusterAddNode(node);\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n        }\n\n        /* If this is a MEET packet from an unknown node, we still process\n         * the gossip section here since we have to trust the sender because\n         * of the message type. */\n        if (!sender && type == CLUSTERMSG_TYPE_MEET)\n            clusterProcessGossipSection(hdr,link);\n\n        /* Anyway reply with a PONG */\n        clusterSendPing(link,CLUSTERMSG_TYPE_PONG);\n    }\n\n    /* PING, PONG, MEET: process config information. */\n    if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_PONG ||\n        type == CLUSTERMSG_TYPE_MEET)\n    {\n        serverLog(LL_DEBUG,\"%s packet received: %p\",\n            type == CLUSTERMSG_TYPE_PING ? \"ping\" : \"pong\",\n            (void*)link->node);\n        if (link->node) {\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                    serverLog(LL_VERBOSE,\n                        \"Handshake: we already know node %.40s, \"\n                        \"updating the address if needed.\", sender->name);\n                    if (nodeUpdateAddressIfNeeded(sender,link,hdr))\n                    {\n                        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                             CLUSTER_TODO_UPDATE_STATE);\n                    }\n                    /* Free this node as we already have it. This will\n                     * cause the link to be freed as well. */\n                    clusterDelNode(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                clusterRenameNode(link->node, hdr->sender);\n                serverLog(LL_DEBUG,\"Handshake with node %.40s completed.\",\n                    link->node->name);\n                link->node->flags &= ~CLUSTER_NODE_HANDSHAKE;\n                link->node->flags |= flags&(CLUSTER_NODE_MASTER|CLUSTER_NODE_SLAVE);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n            } else if (memcmp(link->node->name,hdr->sender,\n                        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                serverLog(LL_DEBUG,\"PONG contains mismatching sender ID. About node %.40s added %d ms ago, having flags %d\",\n                    link->node->name,\n                    (int)(now-(link->node->ctime)),\n                    link->node->flags);\n                link->node->flags |= CLUSTER_NODE_NOADDR;\n                link->node->ip[0] = '\\0';\n                link->node->port = 0;\n                link->node->cport = 0;\n                freeClusterLink(link);\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);\n                return 0;\n            }\n        }\n\n        /* Copy the CLUSTER_NODE_NOFAILOVER flag from what the sender\n         * announced. This is a dynamic flag that we receive from the\n         * sender, and the latest status must be trusted. We need it to\n         * be propagated because the slave ranking used to understand the\n         * delay of each slave in the voting process, needs to know\n         * what are the instances really competing. */\n        if (sender) {\n            int nofailover = flags & CLUSTER_NODE_NOFAILOVER;\n            sender->flags &= ~CLUSTER_NODE_NOFAILOVER;\n            sender->flags |= nofailover;\n        }\n\n        /* Update the node address if it changed. */\n        if (sender && type == CLUSTERMSG_TYPE_PING &&\n            !nodeInHandshake(sender) &&\n            nodeUpdateAddressIfNeeded(sender,link,hdr))\n        {\n            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                 CLUSTER_TODO_UPDATE_STATE);\n        }\n\n        /* Update our info about the node */\n        if (link->node && type == CLUSTERMSG_TYPE_PONG) {\n            link->node->pong_received = now;\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             * The FAIL condition is also reversible under specific\n             * conditions detected by clearNodeFailureIfNeeded(). */\n            if (nodeTimedOut(link->node)) {\n                link->node->flags &= ~CLUSTER_NODE_PFAIL;\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE);\n            } else if (nodeFailed(link->node)) {\n                clearNodeFailureIfNeeded(link->node);\n            }\n        }\n\n        /* Check for role switch: slave -> master or master -> slave. */\n        if (sender) {\n            if (!memcmp(hdr->slaveof,CLUSTER_NODE_NULL_NAME,\n                sizeof(hdr->slaveof)))\n            {\n                /* Node is a master. */\n                clusterSetNodeAsMaster(sender);\n            } else {\n                /* Node is a slave. */\n                clusterNode *master = clusterLookupNode(hdr->slaveof);\n\n                if (nodeIsMaster(sender)) {\n                    /* Master turned into a slave! Reconfigure the node. */\n                    clusterDelNodeSlots(sender);\n                    sender->flags &= ~(CLUSTER_NODE_MASTER|\n                                       CLUSTER_NODE_MIGRATE_TO);\n                    sender->flags |= CLUSTER_NODE_SLAVE;\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                if (master && sender->slaveof != master) {\n                    if (sender->slaveof)\n                        clusterNodeRemoveSlave(sender->slaveof,sender);\n                    clusterNodeAddSlave(master,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         * Note: this MUST happen after we update the master/slave state\n         * so that CLUSTER_NODE_MASTER flag will be set. */\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        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         * 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         * A and B are master and slave for slots 1,2,3.\n         * A is partitioned away, B gets promoted.\n         * B is partitioned away, and A returns available.\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        if (sender && dirty_slots) {\n            int j;\n\n            for (j = 0; j < CLUSTER_SLOTS; j++) {\n                if (bitmapTestBit(hdr->myslots,j)) {\n                    if (server.cluster->slots[j] == sender ||\n                        server.cluster->slots[j] == NULL) continue;\n                    if (server.cluster->slots[j]->configEpoch >\n                        senderConfigEpoch)\n                    {\n                        serverLog(LL_VERBOSE,\n                            \"Node %.40s has old slots configuration, sending \"\n                            \"an UPDATE message about %.40s\",\n                                sender->name, server.cluster->slots[j]->name);\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        if (sender) clusterProcessGossipSection(hdr,link);\n    } else if (type == CLUSTERMSG_TYPE_FAIL) {\n        clusterNode *failing;\n\n        if (sender) {\n            failing = clusterLookupNode(hdr->data.fail.about.nodename);\n            if (failing &&\n                !(failing->flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_MYSELF)))\n            {\n                serverLog(LL_NOTICE,\n                    \"FAIL message received from %.40s about %.40s\",\n                    hdr->sender, hdr->data.fail.about.nodename);\n                failing->flags |= CLUSTER_NODE_FAIL;\n                failing->fail_time = now;\n                failing->flags &= ~CLUSTER_NODE_PFAIL;\n                clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|\n                                     CLUSTER_TODO_UPDATE_STATE);\n            }\n        } else {\n            serverLog(LL_NOTICE,\n                \"Ignoring FAIL message from unknown node %.40s about %.40s\",\n                hdr->sender, hdr->data.fail.about.nodename);\n        }\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        if (dictSize(server.pubsub_channels) ||\n           listLength(server.pubsub_patterns))\n        {\n            channel_len = ntohl(hdr->data.publish.msg.channel_len);\n            message_len = ntohl(hdr->data.publish.msg.message_len);\n            channel = createStringObject(\n                        (char*)hdr->data.publish.msg.bulk_data,channel_len);\n            message = createStringObject(\n                        (char*)hdr->data.publish.msg.bulk_data+channel_len,\n                        message_len);\n            pubsubPublishMessage(channel,message);\n            decrRefCount(channel);\n            decrRefCount(message);\n        }\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST) {\n        if (!sender) return 1;  /* We don't know that node. */\n        clusterSendFailoverAuthIfNeeded(sender,hdr);\n    } else if (type == CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK) {\n        if (!sender) return 1;  /* We don't know that node. */\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        if (nodeIsMaster(sender) && sender->numslots > 0 &&\n            senderCurrentEpoch >= server.cluster->failover_auth_epoch)\n        {\n            server.cluster->failover_auth_count++;\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    } 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 = now + CLUSTER_MF_TIMEOUT;\n        server.cluster->mf_slave = sender;\n        pauseClients(now+(CLUSTER_MF_TIMEOUT*CLUSTER_MF_PAUSE_MULT));\n        serverLog(LL_WARNING,\"Manual failover requested by replica %.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 = clusterLookupNode(hdr->data.update.nodecfg.nodename);\n        if (!n) return 1;   /* We don't know the reported node. */\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        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 update our\n         * config accordingly. */\n        clusterUpdateSlotsConfigWith(n,reportedConfigEpoch,\n            hdr->data.update.nodecfg.slots);\n    } else if (type == CLUSTERMSG_TYPE_MODULE) {\n        if (!sender) return 1;  /* Protect the module from unknown nodes. */\n        /* We need to route this message back to the right module subscribed\n         * for the right message type. */\n        uint64_t module_id = hdr->data.module.msg.module_id; /* Endian-safe ID */\n        uint32_t len = ntohl(hdr->data.module.msg.len);\n        uint8_t type = hdr->data.module.msg.type;\n        unsigned char *payload = hdr->data.module.msg.bulk_data;\n        moduleCallClusterReceivers(sender->name,module_id,type,payload,len);\n    } else {\n        serverLog(LL_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   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   Instead if the node is a temporary node used to accept a query, we\n   completely free the node on error. */\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. */\nvoid clusterWriteHandler(connection *conn) {\n    clusterLink *link = connGetPrivateData(conn);\n    ssize_t nwritten;\n\n    nwritten = connWrite(conn, link->sndbuf, sdslen(link->sndbuf));\n    if (nwritten <= 0) {\n        serverLog(LL_DEBUG,\"I/O error writing to node link: %s\",\n            (nwritten == -1) ? connGetLastError(conn) : \"short write\");\n        handleLinkIOError(link);\n        return;\n    }\n    sdsrange(link->sndbuf,nwritten,-1);\n    if (sdslen(link->sndbuf) == 0)\n        connSetWriteHandler(link->conn, NULL);\n}\n\n/* A connect handler that gets called when a connection to another node\n * gets established.\n */\nvoid clusterLinkConnectHandler(connection *conn) {\n    clusterLink *link = connGetPrivateData(conn);\n    clusterNode *node = link->node;\n\n    /* Check if connection succeeded */\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_VERBOSE, \"Connection with Node %.40s at %s:%d failed: %s\",\n                node->name, node->ip, node->cport,\n                connGetLastError(conn));\n        freeClusterLink(link);\n        return;\n    }\n\n    /* Register a read handler from now on */\n    connSetReadHandler(conn, clusterReadHandler);\n\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    mstime_t old_ping_sent = node->ping_sent;\n    clusterSendPing(link, node->flags & CLUSTER_NODE_MEET ?\n            CLUSTERMSG_TYPE_MEET : CLUSTERMSG_TYPE_PING);\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    /* We can clear the flag after the first packet is sent.\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    node->flags &= ~CLUSTER_NODE_MEET;\n\n    serverLog(LL_DEBUG,\"Connecting with Node %.40s at %s:%d\",\n            node->name, node->ip, node->cport);\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. */\nvoid clusterReadHandler(connection *conn) {\n    clusterMsg buf[1];\n    ssize_t nread;\n    clusterMsg *hdr;\n    clusterLink *link = connGetPrivateData(conn);\n    unsigned int readlen, rcvbuflen;\n\n    while(1) { /* Read as long as there is data to read. */\n        rcvbuflen = sdslen(link->rcvbuf);\n        if (rcvbuflen < 8) {\n            /* First, obtain the first 8 bytes to get the full message\n             * length. */\n            readlen = 8 - rcvbuflen;\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                    serverLog(LL_WARNING,\n                        \"Bad message length or signature received \"\n                        \"from Cluster bus.\");\n                    handleLinkIOError(link);\n                    return;\n                }\n            }\n            readlen = ntohl(hdr->totlen) - rcvbuflen;\n            if (readlen > sizeof(buf)) readlen = sizeof(buf);\n        }\n\n        nread = connRead(conn,buf,readlen);\n        if (nread == -1 && (connGetState(conn) == CONN_STATE_CONNECTED)) return; /* No more data ready. */\n\n        if (nread <= 0) {\n            /* I/O error... */\n            serverLog(LL_DEBUG,\"I/O error reading from node link: %s\",\n                (nread == 0) ? \"connection closed\" : connGetLastError(conn));\n            handleLinkIOError(link);\n            return;\n        } else {\n            /* Read data and recast the pointer to the new buffer. */\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        if (rcvbuflen >= 8 && rcvbuflen == ntohl(hdr->totlen)) {\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 * 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. */\nvoid clusterSendMessage(clusterLink *link, unsigned char *msg, size_t msglen) {\n    if (sdslen(link->sndbuf) == 0 && msglen != 0)\n        connSetWriteHandlerWithBarrier(link->conn, clusterWriteHandler, 1);\n\n    link->sndbuf = sdscatlen(link->sndbuf, msg, msglen);\n\n    /* Populate sent messages stats. */\n    clusterMsg *hdr = (clusterMsg*) msg;\n    uint16_t type = ntohs(hdr->type);\n    if (type < CLUSTERMSG_TYPE_COUNT)\n        server.cluster->stats_bus_messages_sent[type]++;\n}\n\n/* Send a message to all the nodes that are part of the cluster having\n * a connected link.\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    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (!node->link) continue;\n        if (node->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_HANDSHAKE))\n            continue;\n        clusterSendMessage(node->link,buf,len);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Build the message header. hdr must point to a buffer at least\n * sizeof(clusterMsg) in bytes. */\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     * 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    master = (nodeIsSlave(myself) && myself->slaveof) ?\n              myself->slaveof : myself;\n\n    memset(hdr,0,sizeof(*hdr));\n    hdr->ver = htons(CLUSTER_PROTO_VER);\n    hdr->sig[0] = 'R';\n    hdr->sig[1] = 'C';\n    hdr->sig[2] = 'm';\n    hdr->sig[3] = 'b';\n    hdr->type = htons(type);\n    memcpy(hdr->sender,myself->name,CLUSTER_NAMELEN);\n\n    /* If cluster-announce-ip option is enabled, force the receivers of our\n     * packets to use the specified address for this node. Otherwise if the\n     * first byte is zero, they'll do auto discovery. */\n    memset(hdr->myip,0,NET_IP_STR_LEN);\n    if (server.cluster_announce_ip) {\n        strncpy(hdr->myip,server.cluster_announce_ip,NET_IP_STR_LEN);\n        hdr->myip[NET_IP_STR_LEN-1] = '\\0';\n    }\n\n    /* Handle cluster-announce-port as well. */\n    int port = server.tls_cluster ? server.tls_port : server.port;\n    int announced_port = server.cluster_announce_port ?\n                         server.cluster_announce_port : port;\n    int announced_cport = server.cluster_announce_bus_port ?\n                          server.cluster_announce_bus_port :\n                          (port + CLUSTER_PORT_INCR);\n\n    memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots));\n    memset(hdr->slaveof,0,CLUSTER_NAMELEN);\n    if (myself->slaveof != NULL)\n        memcpy(hdr->slaveof,myself->slaveof->name, CLUSTER_NAMELEN);\n    hdr->port = htons(announced_port);\n    hdr->cport = htons(announced_cport);\n    hdr->flags = htons(myself->flags);\n    hdr->state = server.cluster->state;\n\n    /* Set the currentEpoch and configEpochs. */\n    hdr->currentEpoch = htonu64(server.cluster->currentEpoch);\n    hdr->configEpoch = htonu64(master->configEpoch);\n\n    /* Set the replication offset. */\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    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    hdr->totlen = htonl(totlen);\n    /* For PING, PONG, and MEET, fixing the totlen field is up to the caller. */\n}\n\n/* Return non zero if the node is already present in the gossip section of the\n * message pointed by 'hdr' and having 'count' gossip entries. Otherwise\n * zero is returned. Helper for clusterSendPing(). */\nint clusterNodeIsInGossipSection(clusterMsg *hdr, int count, clusterNode *n) {\n    int j;\n    for (j = 0; j < count; j++) {\n        if (memcmp(hdr->data.ping.gossip[j].nodename,n->name,\n                CLUSTER_NAMELEN) == 0) break;\n    }\n    return j != count;\n}\n\n/* Set the i-th entry of the gossip section in the message pointed by 'hdr'\n * to the info of the specified node 'n'. */\nvoid clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {\n    clusterMsgDataGossip *gossip;\n    gossip = &(hdr->data.ping.gossip[i]);\n    memcpy(gossip->nodename,n->name,CLUSTER_NAMELEN);\n    gossip->ping_sent = htonl(n->ping_sent/1000);\n    gossip->pong_received = htonl(n->pong_received/1000);\n    memcpy(gossip->ip,n->ip,sizeof(n->ip));\n    gossip->port = htons(n->port);\n    gossip->cport = htons(n->cport);\n    gossip->flags = htons(n->flags);\n    gossip->notused1 = 0;\n}\n\n/* Send a PING or PONG packet to the specified node, making sure to add enough\n * gossip informations. */\nvoid clusterSendPing(clusterLink *link, int type) {\n    unsigned char *buf;\n    clusterMsg *hdr;\n    int gossipcount = 0; /* Number of gossip sections added so far. */\n    int wanted; /* Number of gossip sections we want to append if possible. */\n    int totlen; /* Total packet length. */\n    /* freshnodes is the max number of nodes we can hope to append at all:\n     * nodes available minus two (ourself and the node we are sending the\n     * message to). However practically there may be less valid nodes since\n     * nodes in handshake state, disconnected, are not considered. */\n    int freshnodes = dictSize(server.cluster->nodes)-2;\n\n    /* How many gossip sections we want to add? 1/10 of the number of nodes\n     * and anyway at least 3. Why 1/10?\n     *\n     * If we have N masters, with N/10 entries, and we consider that in\n     * node_timeout we exchange with each other node at least 4 packets\n     * (we ping in the worst case in node_timeout/2 time, and we also\n     * receive two pings from the host), we have a total of 8 packets\n     * in the node_timeout*2 falure reports validity time. So we have\n     * that, for a single PFAIL node, we can expect to receive the following\n     * number of failure reports (in the specified window of time):\n     *\n     * PROB * GOSSIP_ENTRIES_PER_PACKET * TOTAL_PACKETS:\n     *\n     * PROB = probability of being featured in a single gossip entry,\n     *        which is 1 / NUM_OF_NODES.\n     * ENTRIES = 10.\n     * TOTAL_PACKETS = 2 * 4 * NUM_OF_MASTERS.\n     *\n     * If we assume we have just masters (so num of nodes and num of masters\n     * is the same), with 1/10 we always get over the majority, and specifically\n     * 80% of the number of nodes, to account for many masters failing at the\n     * same time.\n     *\n     * Since we have non-voting slaves that lower the probability of an entry\n     * to feature our node, we set the number of entries per packet as\n     * 10% of the total nodes we have. */\n    wanted = floor(dictSize(server.cluster->nodes)/10);\n    if (wanted < 3) wanted = 3;\n    if (wanted > freshnodes) wanted = freshnodes;\n\n    /* Include all the nodes in PFAIL state, so that failure reports are\n     * faster to propagate to go from PFAIL to FAIL state. */\n    int pfail_wanted = server.cluster->stats_pfail_nodes;\n\n    /* Compute the maxium totlen to allocate our buffer. We'll fix the totlen\n     * later according to the number of gossip sections we really were able\n     * to put inside the packet. */\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += (sizeof(clusterMsgDataGossip)*(wanted+pfail_wanted));\n    /* Note: clusterBuildMessageHdr() expects the buffer to be always at least\n     * sizeof(clusterMsg) or more. */\n    if (totlen < (int)sizeof(clusterMsg)) totlen = sizeof(clusterMsg);\n    buf = zcalloc(totlen);\n    hdr = (clusterMsg*) buf;\n\n    /* Populate the header. */\n    if (link->node && type == CLUSTERMSG_TYPE_PING)\n        link->node->ping_sent = mstime();\n    clusterBuildMessageHdr(hdr,type);\n\n    /* Populate the gossip fields */\n    int maxiterations = wanted*3;\n    while(freshnodes > 0 && gossipcount < wanted && maxiterations--) {\n        dictEntry *de = dictGetRandomKey(server.cluster->nodes);\n        clusterNode *this = dictGetVal(de);\n\n        /* Don't include this node: the whole packet header is about us\n         * already, so we just gossip about other nodes. */\n        if (this == myself) continue;\n\n        /* PFAIL nodes will be added later. */\n        if (this->flags & CLUSTER_NODE_PFAIL) continue;\n\n        /* In the gossip section don't include:\n         * 1) Nodes in HANDSHAKE state.\n         * 3) Nodes with the NOADDR flag set.\n         * 4) Disconnected nodes if they don't have configured slots.\n         */\n        if (this->flags & (CLUSTER_NODE_HANDSHAKE|CLUSTER_NODE_NOADDR) ||\n            (this->link == NULL && this->numslots == 0))\n        {\n            freshnodes--; /* Tecnically not correct, but saves CPU. */\n            continue;\n        }\n\n        /* Do not add a node we already have. */\n        if (clusterNodeIsInGossipSection(hdr,gossipcount,this)) continue;\n\n        /* Add it */\n        clusterSetGossipEntry(hdr,gossipcount,this);\n        freshnodes--;\n        gossipcount++;\n    }\n\n    /* If there are PFAIL nodes, add them at the end. */\n    if (pfail_wanted) {\n        dictIterator *di;\n        dictEntry *de;\n\n        di = dictGetSafeIterator(server.cluster->nodes);\n        while((de = dictNext(di)) != NULL && pfail_wanted > 0) {\n            clusterNode *node = dictGetVal(de);\n            if (node->flags & CLUSTER_NODE_HANDSHAKE) continue;\n            if (node->flags & CLUSTER_NODE_NOADDR) continue;\n            if (!(node->flags & CLUSTER_NODE_PFAIL)) continue;\n            clusterSetGossipEntry(hdr,gossipcount,node);\n            freshnodes--;\n            gossipcount++;\n            /* We take the count of the slots we allocated, since the\n             * PFAIL stats may not match perfectly with the current number\n             * of PFAIL nodes. */\n            pfail_wanted--;\n        }\n        dictReleaseIterator(di);\n    }\n\n    /* Ready to send... fix the totlen fiend and queue the message in the\n     * output buffer. */\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += (sizeof(clusterMsgDataGossip)*gossipcount);\n    hdr->count = htons(gossipcount);\n    hdr->totlen = htonl(totlen);\n    clusterSendMessage(link,buf,totlen);\n    zfree(buf);\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 * 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 *\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    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\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        clusterSendPing(node->link,CLUSTERMSG_TYPE_PONG);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Send a PUBLISH message.\n *\n * If link is NULL, then the message is broadcasted to the whole cluster. */\nvoid clusterSendPublish(clusterLink *link, robj *channel, robj *message) {\n    unsigned char *payload;\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n    uint32_t channel_len, message_len;\n\n    channel = getDecodedObject(channel);\n    message = getDecodedObject(message);\n    channel_len = sdslen(channel->ptr);\n    message_len = sdslen(message->ptr);\n\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_PUBLISH);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += sizeof(clusterMsgDataPublish) - 8 + 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 = (unsigned char*)buf;\n    } else {\n        payload = zmalloc(totlen);\n        memcpy(payload,hdr,sizeof(*hdr));\n        hdr = (clusterMsg*) payload;\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    if (link)\n        clusterSendMessage(link,payload,totlen);\n    else\n        clusterBroadcastMessage(payload,totlen);\n\n    decrRefCount(channel);\n    decrRefCount(message);\n    if (payload != (unsigned char*)buf) zfree(payload);\n}\n\n/* Send a FAIL message to all the nodes we are able to contact.\n * The FAIL message is sent when we detect that a node is failing\n * (CLUSTER_NODE_PFAIL) and we also receive a gossip confirmation of this:\n * we switch the node state to CLUSTER_NODE_FAIL and ask all the other\n * nodes to do the same ASAP. */\nvoid clusterSendFail(char *nodename) {\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_FAIL);\n    memcpy(hdr->data.fail.about.nodename,nodename,CLUSTER_NAMELEN);\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. */\nvoid clusterSendUpdate(clusterLink *link, clusterNode *node) {\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n\n    if (link == NULL) return;\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_UPDATE);\n    memcpy(hdr->data.update.nodecfg.nodename,node->name,CLUSTER_NAMELEN);\n    hdr->data.update.nodecfg.configEpoch = htonu64(node->configEpoch);\n    memcpy(hdr->data.update.nodecfg.slots,node->slots,sizeof(node->slots));\n    clusterSendMessage(link,(unsigned char*)buf,ntohl(hdr->totlen));\n}\n\n/* Send a MODULE message.\n *\n * If link is NULL, then the message is broadcasted to the whole cluster. */\nvoid clusterSendModule(clusterLink *link, uint64_t module_id, uint8_t type,\n                       unsigned char *payload, uint32_t len) {\n    unsigned char *heapbuf;\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\n\n    clusterBuildMessageHdr(hdr,CLUSTERMSG_TYPE_MODULE);\n    totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);\n    totlen += sizeof(clusterMsgModule) - 3 + len;\n\n    hdr->data.module.msg.module_id = module_id; /* Already endian adjusted. */\n    hdr->data.module.msg.type = type;\n    hdr->data.module.msg.len = htonl(len);\n    hdr->totlen = htonl(totlen);\n\n    /* Try to use the local buffer if possible */\n    if (totlen < sizeof(buf)) {\n        heapbuf = (unsigned char*)buf;\n    } else {\n        heapbuf = zmalloc(totlen);\n        memcpy(heapbuf,hdr,sizeof(*hdr));\n        hdr = (clusterMsg*) heapbuf;\n    }\n    memcpy(hdr->data.module.msg.bulk_data,payload,len);\n\n    if (link)\n        clusterSendMessage(link,heapbuf,totlen);\n    else\n        clusterBroadcastMessage(heapbuf,totlen);\n\n    if (heapbuf != (unsigned char*)buf) zfree(heapbuf);\n}\n\n/* This function gets a cluster node ID string as target, the same way the nodes\n * addresses are represented in the modules side, resolves the node, and sends\n * the message. If the target is NULL the message is broadcasted.\n *\n * The function returns C_OK if the target is valid, otherwise C_ERR is\n * returned. */\nint clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, unsigned char *payload, uint32_t len) {\n    clusterNode *node = NULL;\n\n    if (target != NULL) {\n        node = clusterLookupNode(target);\n        if (node == NULL || node->link == NULL) return C_ERR;\n    }\n\n    clusterSendModule(target ? node->link : NULL,\n                      module_id, type, payload, len);\n    return C_OK;\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 * -------------------------------------------------------------------------- */\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 * 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. */\nvoid clusterRequestFailoverAuth(void) {\n    clusterMsg buf[1];\n    clusterMsg *hdr = (clusterMsg*) buf;\n    uint32_t totlen;\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    clusterBroadcastMessage(buf,totlen);\n}\n\n/* Send a FAILOVER_AUTH_ACK message to the specified node. */\nvoid clusterSendFailoverAuth(clusterNode *node) {\n    clusterMsg buf[1];\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,(unsigned char*)buf,totlen);\n}\n\n/* Send a MFSTART message to the specified node. */\nvoid clusterSendMFStart(clusterNode *node) {\n    clusterMsg buf[1];\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,(unsigned char*)buf,totlen);\n}\n\n/* Vote for the node asking for our vote if there are the conditions. */\nvoid clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request) {\n    clusterNode *master = node->slaveof;\n    uint64_t requestCurrentEpoch = ntohu64(request->currentEpoch);\n    uint64_t requestConfigEpoch = ntohu64(request->configEpoch);\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    if (nodeIsSlave(myself) || myself->numslots == 0) return;\n\n    /* Request epoch must be >= our currentEpoch.\n     * Note that it is impossible for it to actually be greater since\n     * our currentEpoch was updated as a side effect of receiving this\n     * request, if the request epoch was greater. */\n    if (requestCurrentEpoch < server.cluster->currentEpoch) {\n        serverLog(LL_WARNING,\n            \"Failover auth denied to %.40s: reqEpoch (%llu) < curEpoch(%llu)\",\n            node->name,\n            (unsigned long long) requestCurrentEpoch,\n            (unsigned long long) server.cluster->currentEpoch);\n        return;\n    }\n\n    /* I already voted for this epoch? Return ASAP. */\n    if (server.cluster->lastVoteEpoch == server.cluster->currentEpoch) {\n        serverLog(LL_WARNING,\n                \"Failover auth denied to %.40s: already voted for epoch %llu\",\n                node->name,\n                (unsigned long long) server.cluster->currentEpoch);\n        return;\n    }\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))\n    {\n        if (nodeIsMaster(node)) {\n            serverLog(LL_WARNING,\n                    \"Failover auth denied to %.40s: it is a master node\",\n                    node->name);\n        } else if (master == NULL) {\n            serverLog(LL_WARNING,\n                    \"Failover auth denied to %.40s: I don't know its master\",\n                    node->name);\n        } else if (!nodeFailed(master)) {\n            serverLog(LL_WARNING,\n                    \"Failover auth denied to %.40s: its master is up\",\n                    node->name);\n        }\n        return;\n    }\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    if (mstime() - node->slaveof->voted_time < server.cluster_node_timeout * 2)\n    {\n        serverLog(LL_WARNING,\n                \"Failover auth denied to %.40s: \"\n                \"can't vote about this master before %lld milliseconds\",\n                node->name,\n                (long long) ((server.cluster_node_timeout*2)-\n                             (mstime() - node->slaveof->voted_time)));\n        return;\n    }\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 < CLUSTER_SLOTS; j++) {\n        if (bitmapTestBit(claimed_slots, j) == 0) continue;\n        if (server.cluster->slots[j] == NULL ||\n            server.cluster->slots[j]->configEpoch <= requestConfigEpoch)\n        {\n            continue;\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        serverLog(LL_WARNING,\n                \"Failover auth denied to %.40s: \"\n                \"slot %d epoch (%llu) > reqEpoch (%llu)\",\n                node->name, j,\n                (unsigned long long) server.cluster->slots[j]->configEpoch,\n                (unsigned long long) requestConfigEpoch);\n        return;\n    }\n\n    /* We can vote for this slave. */\n    server.cluster->lastVoteEpoch = server.cluster->currentEpoch;\n    node->slaveof->voted_time = mstime();\n    clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|CLUSTER_TODO_FSYNC_CONFIG);\n    clusterSendFailoverAuth(node);\n    serverLog(LL_WARNING, \"Failover auth granted to %.40s for epoch %llu\",\n        node->name, (unsigned long long) server.cluster->currentEpoch);\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    serverAssert(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            !nodeCantFailover(master->slaves[j]) &&\n            master->slaves[j]->repl_offset > myoffset) rank++;\n    return rank;\n}\n\n/* This function is called by clusterHandleSlaveFailover() in order to\n * let the slave log why it is not able to failover. Sometimes there are\n * not the conditions, but since the failover function is called again and\n * again, we can't log the same things continuously.\n *\n * This function works by logging only if a given set of conditions are\n * true:\n *\n * 1) The reason for which the failover can't be initiated changed.\n *    The reasons also include a NONE reason we reset the state to\n *    when the slave finds that its master is fine (no FAIL flag).\n * 2) Also, the log is emitted again if the master is still down and\n *    the reason for not failing over is still the same, but more than\n *    CLUSTER_CANT_FAILOVER_RELOG_PERIOD seconds elapsed.\n * 3) Finally, the function only logs if the slave is down for more than\n *    five seconds + NODE_TIMEOUT. This way nothing is logged when a\n *    failover starts in a reasonable time.\n *\n * The function is called with the reason why the slave can't failover\n * which is one of the integer macros CLUSTER_CANT_FAILOVER_*.\n *\n * The function is guaranteed to be called only if 'myself' is a slave. */\nvoid clusterLogCantFailover(int reason) {\n    char *msg;\n    static time_t lastlog_time = 0;\n    mstime_t nolog_fail_time = server.cluster_node_timeout + 5000;\n\n    /* Don't log if we have the same reason for some time. */\n    if (reason == server.cluster->cant_failover_reason &&\n        time(NULL)-lastlog_time < CLUSTER_CANT_FAILOVER_RELOG_PERIOD)\n        return;\n\n    server.cluster->cant_failover_reason = reason;\n\n    /* We also don't emit any log if the master failed no long ago, the\n     * goal of this function is to log slaves in a stalled condition for\n     * a long time. */\n    if (myself->slaveof &&\n        nodeFailed(myself->slaveof) &&\n        (mstime() - myself->slaveof->fail_time) < nolog_fail_time) return;\n\n    switch(reason) {\n    case CLUSTER_CANT_FAILOVER_DATA_AGE:\n        msg = \"Disconnected from master for longer than allowed. \"\n              \"Please check the 'cluster-replica-validity-factor' configuration \"\n              \"option.\";\n        break;\n    case CLUSTER_CANT_FAILOVER_WAITING_DELAY:\n        msg = \"Waiting the delay before I can start a new failover.\";\n        break;\n    case CLUSTER_CANT_FAILOVER_EXPIRED:\n        msg = \"Failover attempt expired.\";\n        break;\n    case CLUSTER_CANT_FAILOVER_WAITING_VOTES:\n        msg = \"Waiting for votes, but majority still not reached.\";\n        break;\n    default:\n        msg = \"Unknown reason code.\";\n        break;\n    }\n    lastlog_time = time(NULL);\n    serverLog(LL_WARNING,\"Currently unable to failover: %s\", msg);\n}\n\n/* This function implements the final part of automatic and manual failovers,\n * where the slave grabs its master's hash slots, and propagates the new\n * configuration.\n *\n * Note that it's up to the caller to be sure that the node got a new\n * configuration epoch already. */\nvoid clusterFailoverReplaceYourMaster(void) {\n    int j;\n    clusterNode *oldmaster = myself->slaveof;\n\n    if (nodeIsMaster(myself) || oldmaster == NULL) return;\n\n    /* 1) Turn this node into a master. */\n    clusterSetNodeAsMaster(myself);\n    replicationUnsetMaster();\n\n    /* 2) Claim all the slots assigned to our master. */\n    for (j = 0; j < CLUSTER_SLOTS; j++) {\n        if (clusterNodeGetSlotBit(oldmaster,j)) {\n            clusterDelSlot(j);\n            clusterAddSlot(myself,j);\n        }\n    }\n\n    /* 3) Update state and save config. */\n    clusterUpdateState();\n    clusterSaveConfigOrDie(1);\n\n    /* 4) Pong all the other nodes so that they can update the state\n     *    accordingly and detect that we switched to master role. */\n    clusterBroadcastPong(CLUSTER_BROADCAST_ALL);\n\n    /* 5) If there was a manual failover in progress, clear the state. */\n    resetManualFailover();\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 * The gaol of this function is:\n * 1) To check if we are able to perform a failover, is our data updated?\n * 2) Try to get elected by masters.\n * 3) Perform the failover informing all the other nodes.\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    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 trying to get voted again).\n     *\n     * Timeout is MAX(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) We don't have the no failover configuration set, and this is\n     *    not a manual failover.\n     * 4) It is serving slots. */\n    if (nodeIsMaster(myself) ||\n        myself->slaveof == NULL ||\n        (!nodeFailed(myself->slaveof) && !manual_failover) ||\n        (server.cluster_slave_no_failover && !manual_failover) ||\n        myself->slaveof->numslots == 0)\n    {\n        /* There are no reasons to failover, so we set the reason why we\n         * are returning without failing over to NONE. */\n        server.cluster->cant_failover_reason = CLUSTER_CANT_FAILOVER_NONE;\n        return;\n    }\n\n    /* Set data_age to the number of seconds we are disconnected from\n     * the master. */\n    if (server.repl_state == REPL_STATE_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    if (data_age > server.cluster_node_timeout)\n        data_age -= server.cluster_node_timeout;\n\n    /* Check if our data is recent enough according to the slave validity\n     * factor configured by the user.\n     *\n     * Check bypassed for manual failovers. */\n    if (server.cluster_slave_validity_factor &&\n        data_age >\n        (((mstime_t)server.repl_ping_slave_period * 1000) +\n         (server.cluster_node_timeout * server.cluster_slave_validity_factor)))\n    {\n        if (!manual_failover) {\n            clusterLogCantFailover(CLUSTER_CANT_FAILOVER_DATA_AGE);\n            return;\n        }\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\t    clusterDoBeforeSleep(CLUSTER_TODO_HANDLE_FAILOVER);\n        }\n        serverLog(LL_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            serverLog(LL_WARNING,\n                \"Replica 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    if (mstime() < server.cluster->failover_auth_time) {\n        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_WAITING_DELAY);\n        return;\n    }\n\n    /* Return ASAP if the election is too old to be valid. */\n    if (auth_age > auth_timeout) {\n        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_EXPIRED);\n        return;\n    }\n\n    /* Ask for votes if needed. */\n    if (server.cluster->failover_auth_sent == 0) {\n        server.cluster->currentEpoch++;\n        server.cluster->failover_auth_epoch = server.cluster->currentEpoch;\n        serverLog(LL_WARNING,\"Starting a failover election for epoch %llu.\",\n            (unsigned long long) server.cluster->currentEpoch);\n        clusterRequestFailoverAuth();\n        server.cluster->failover_auth_sent = 1;\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    if (server.cluster->failover_auth_count >= needed_quorum) {\n        /* We have the quorum, we can finally failover the master. */\n\n        serverLog(LL_WARNING,\n            \"Failover election won: I'm the new master.\");\n\n        /* Update my configEpoch to the epoch of the election. */\n        if (myself->configEpoch < server.cluster->failover_auth_epoch) {\n            myself->configEpoch = server.cluster->failover_auth_epoch;\n            serverLog(LL_WARNING,\n                \"configEpoch set to %llu after successful failover\",\n                (unsigned long long) myself->configEpoch);\n        }\n\n        /* Take responsibility for the cluster slots. */\n        clusterFailoverReplaceYourMaster();\n    } else {\n        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_WAITING_VOTES);\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 != 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: Identify 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     * smallest node ID (the \"candidate slave\").\n     *\n     * Note: this means that eventually a replica migration will occur\n     * since slaves that are reachable again always have their FAIL flag\n     * cleared, so eventually there must be a candidate. At the same time\n     * this does not mean that there are no race conditions possible (two\n     * slaves migrating at the same time), but this is unlikely to\n     * happen, and harmless when happens. */\n    candidate = myself;\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        int okslaves = 0, is_orphaned = 1;\n\n        /* We want to migrate only if this master is working, orphaned, and\n         * used to have slaves or if failed over a master that had slaves\n         * (MIGRATE_TO flag). This way we only migrate to instances that were\n         * supposed to have replicas. */\n        if (nodeIsSlave(node) || nodeFailed(node)) is_orphaned = 0;\n        if (!(node->flags & CLUSTER_NODE_MIGRATE_TO)) is_orphaned = 0;\n\n        /* Check number of working slaves. */\n        if (nodeIsMaster(node)) okslaves = clusterCountNonFailingSlaves(node);\n        if (okslaves > 0) is_orphaned = 0;\n\n        if (is_orphaned) {\n            if (!target && node->numslots > 0) target = node;\n\n            /* Track the starting time of the orphaned condition for this\n             * master. */\n            if (!node->orphaned_time) node->orphaned_time = mstime();\n        } else {\n            node->orphaned_time = 0;\n        }\n\n        /* Check if I'm the slave candidate for the migration: attached\n         * to a master with the maximum number of slaves and with the smallest\n         * node ID. */\n        if (okslaves == max_slaves) {\n            for (j = 0; j < node->numslaves; j++) {\n                if (memcmp(node->slaves[j]->name,\n                           candidate->name,\n                           CLUSTER_NAMELEN) < 0)\n                {\n                    candidate = node->slaves[j];\n                }\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Step 4: perform the migration if there is a target, and if I'm the\n     * candidate, but only if the master is continuously orphaned for a\n     * couple of seconds, so that during failovers, we give some time to\n     * the natural slaves of this instance to advertise their switch from\n     * the old master to the new one. */\n    if (target && candidate == myself &&\n        (mstime()-target->orphaned_time) > CLUSTER_SLAVE_MIGRATION_DELAY &&\n       !(server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_FAILOVER))\n    {\n        serverLog(LL_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 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 * The function can be used both to initialize the manual failover state at\n * startup or to abort a manual failover in progress. */\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        serverLog(LL_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 already 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        serverLog(LL_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 */\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    static unsigned long long iteration = 0;\n    mstime_t handshake_timeout;\n\n    iteration++; /* Number of times this function was called so far. */\n\n    /* We want to take myself->ip in sync with the cluster-announce-ip option.\n     * The option can be set at runtime via CONFIG SET, so we periodically check\n     * if the option changed to reflect this into myself->ip. */\n    {\n        static char *prev_ip = NULL;\n        char *curr_ip = server.cluster_announce_ip;\n        int changed = 0;\n\n        if (prev_ip == NULL && curr_ip != NULL) changed = 1;\n        else if (prev_ip != NULL && curr_ip == NULL) changed = 1;\n        else if (prev_ip && curr_ip && strcmp(prev_ip,curr_ip)) changed = 1;\n\n        if (changed) {\n            if (prev_ip) zfree(prev_ip);\n            prev_ip = curr_ip;\n\n            if (curr_ip) {\n                /* We always take a copy of the previous IP address, by\n                 * duplicating the string. This way later we can check if\n                 * the address really changed. */\n                prev_ip = zstrdup(prev_ip);\n                strncpy(myself->ip,server.cluster_announce_ip,NET_IP_STR_LEN);\n                myself->ip[NET_IP_STR_LEN-1] = '\\0';\n            } else {\n                myself->ip[0] = '\\0'; /* Force autodetection. */\n            }\n        }\n    }\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_timeout = server.cluster_node_timeout;\n    if (handshake_timeout < 1000) handshake_timeout = 1000;\n\n    /* Update myself flags. */\n    clusterUpdateMyselfFlags();\n\n    /* Check if we have disconnected nodes and re-establish the connection.\n     * Also update a few stats while we are here, that can be used to make\n     * better decisions in other part of the code. */\n    di = dictGetSafeIterator(server.cluster->nodes);\n    server.cluster->stats_pfail_nodes = 0;\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        /* Not interested in reconnecting the link with myself or nodes\n         * for which we have no address. */\n        if (node->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_NOADDR)) continue;\n\n        if (node->flags & CLUSTER_NODE_PFAIL)\n            server.cluster->stats_pfail_nodes++;\n\n        /* A Node in HANDSHAKE state has a limited lifespan equal to the\n         * configured node timeout. */\n        if (nodeInHandshake(node) && now - node->ctime > handshake_timeout) {\n            clusterDelNode(node);\n            continue;\n        }\n\n        if (node->link == NULL) {\n            clusterLink *link = createClusterLink(node);\n            link->conn = server.tls_cluster ? connCreateTLS() : connCreateSocket();\n            connSetPrivateData(link->conn, link);\n            if (connConnect(link->conn, node->ip, node->cport, NET_FIRST_BIND_ADDR,\n                        clusterLinkConnectHandler) == -1) {\n                /* We got a synchronous error from connect before\n                 * clusterSendPing() had a chance to be called.\n                 * If node->ping_sent is zero, failure detection can't work,\n                 * so we claim we actually sent a ping now (that will\n                 * be really sent as soon as the link is obtained). */\n                if (node->ping_sent == 0) node->ping_sent = mstime();\n                serverLog(LL_DEBUG, \"Unable to connect to \"\n                    \"Cluster Node [%s]:%d -> %s\", node->ip,\n                    node->cport, server.neterr);\n\n                freeClusterLink(link);\n                continue;\n            }\n            node->link = link;\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    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        for (j = 0; j < 5; j++) {\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            if (this->link == NULL || this->ping_sent != 0) continue;\n            if (this->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_HANDSHAKE))\n                continue;\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        if (min_pong_node) {\n            serverLog(LL_DEBUG,\"Pinging node %.40s\", min_pong_node->name);\n            clusterSendPing(min_pong_node->link, CLUSTERMSG_TYPE_PING);\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\n        if (node->flags &\n            (CLUSTER_NODE_MYSELF|CLUSTER_NODE_NOADDR|CLUSTER_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            /* A master is orphaned if it is serving a non-zero number of\n             * slots, have no working slaves, but used to have at least one\n             * slave, or failed over a master that used to have slaves. */\n            if (okslaves == 0 && node->numslots > 0 &&\n                node->flags & CLUSTER_NODE_MIGRATE_TO)\n            {\n                orphaned_masters++;\n            }\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 not receiving any data for more than half the cluster\n         * timeout, reconnect the link: maybe there is a connection\n         * issue even if the node is alive. */\n        mstime_t ping_delay = now - node->ping_sent;\n        mstime_t data_delay = now - node->data_received;\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            ping_delay > server.cluster_node_timeout/2 &&\n            /* and in such interval we are not seeing any traffic at all. */\n            data_delay > server.cluster_node_timeout/2)\n        {\n            /* Disconnect the link, it will be reconnected automatically. */\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        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        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        if (node->ping_sent == 0) continue;\n\n        /* Check if this node looks unreachable.\n         * Note that if we already received the PONG, then node->ping_sent\n         * is zero, so can't reach this code at all, so we don't risk of\n         * checking for a PONG delay if we didn't sent the PING.\n         *\n         * We also consider every incoming data as proof of liveness, since\n         * our cluster bus link is also used for data: under heavy data\n         * load pong delays are possible. */\n        mstime_t node_delay = (ping_delay < data_delay) ? ping_delay :\n                                                          data_delay;\n\n        if (node_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 & (CLUSTER_NODE_PFAIL|CLUSTER_NODE_FAIL))) {\n                serverLog(LL_DEBUG,\"*** NODE %.40s possibly failing\",\n                    node->name);\n                node->flags |= CLUSTER_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    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        if (!(server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_FAILOVER))\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    if (update_state || server.cluster->state == 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. */\nvoid clusterBeforeSleep(void) {\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    if (server.cluster->todo_before_sleep & CLUSTER_TODO_HANDLE_FAILOVER)\n        clusterHandleSlaveFailover();\n\n    /* Update the cluster state. */\n    if (server.cluster->todo_before_sleep & CLUSTER_TODO_UPDATE_STATE)\n        clusterUpdateState();\n\n    /* Save the config, possibly using fsync. */\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\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. */\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. */\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. */\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/* Return non-zero if there is at least one master with slaves in the cluster.\n * Otherwise zero is returned. Used by clusterNodeSetSlotBit() to set the\n * MIGRATE_TO flag the when a master gets the first slot. */\nint clusterMastersHaveSlaves(void) {\n    dictIterator *di = dictGetSafeIterator(server.cluster->nodes);\n    dictEntry *de;\n    int slaves = 0;\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (nodeIsSlave(node)) continue;\n        slaves += node->numslaves;\n    }\n    dictReleaseIterator(di);\n    return slaves != 0;\n}\n\n/* Set the slot bit and return the old value. */\nint clusterNodeSetSlotBit(clusterNode *n, int slot) {\n    int old = bitmapTestBit(n->slots,slot);\n    bitmapSetBit(n->slots,slot);\n    if (!old) {\n        n->numslots++;\n        /* When a master gets its first slot, even if it has no slaves,\n         * it gets flagged with MIGRATE_TO, that is, the master is a valid\n         * target for replicas migration, if and only if at least one of\n         * the other masters has slaves right now.\n         *\n         * Normally masters are valid targerts of replica migration if:\n         * 1. The used to have slaves (but no longer have).\n         * 2. They are slaves failing over a master that used to have slaves.\n         *\n         * However new masters with slots assigned are considered valid\n         * migration tagets if the rest of the cluster is not a slave-less.\n         *\n         * See https://github.com/antirez/redis/issues/3043 for more info. */\n        if (n->numslots == 1 && clusterMastersHaveSlaves())\n            n->flags |= CLUSTER_NODE_MIGRATE_TO;\n    }\n    return old;\n}\n\n/* Clear the slot bit and return the old value. */\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. */\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 C_OK if the operation ended with success.\n * If the slot is already assigned to another instance this is considered\n * an error and C_ERR is returned. */\nint clusterAddSlot(clusterNode *n, int slot) {\n    if (server.cluster->slots[slot]) return C_ERR;\n    clusterNodeSetSlotBit(n,slot);\n    server.cluster->slots[slot] = n;\n    return C_OK;\n}\n\n/* Delete the specified slot marking it as unassigned.\n * Returns C_OK if the slot was assigned, otherwise if the slot was\n * already unassigned C_ERR is returned. */\nint clusterDelSlot(int slot) {\n    clusterNode *n = server.cluster->slots[slot];\n\n    if (!n) return C_ERR;\n    serverAssert(clusterNodeClearSlotBit(n,slot) == 1);\n    server.cluster->slots[slot] = NULL;\n    return C_OK;\n}\n\n/* Delete all the slots associated with the specified node.\n * The number of deleted slots is returned. */\nint clusterDelNodeSlots(clusterNode *node) {\n    int deleted = 0, j;\n\n    for (j = 0; j < CLUSTER_SLOTS; j++) {\n        if (clusterNodeGetSlotBit(node,j)) {\n            clusterDelSlot(j);\n            deleted++;\n        }\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. */\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. Actually 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 CLUSTER_MAX_REJOIN_DELAY 5000\n#define CLUSTER_MIN_REJOIN_DELAY 500\n#define CLUSTER_WRITABLE_DELAY 2000\n\nvoid clusterUpdateState(void) {\n    int j, new_state;\n    int reachable_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        server.cluster->state == CLUSTER_FAIL &&\n        mstime() - first_call_time < 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    new_state = CLUSTER_OK;\n\n    /* Check if all the slots are covered. */\n    if (server.cluster_require_full_coverage) {\n        for (j = 0; j < CLUSTER_SLOTS; j++) {\n            if (server.cluster->slots[j] == NULL ||\n                server.cluster->slots[j]->flags & (CLUSTER_NODE_FAIL))\n            {\n                new_state = CLUSTER_FAIL;\n                break;\n            }\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 reachable masters having\n     * at least one slot. */\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 & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) == 0)\n                    reachable_masters++;\n            }\n        }\n        dictReleaseIterator(di);\n    }\n\n    /* If we are in a minority partition, change the cluster state\n     * to FAIL. */\n    {\n        int needed_quorum = (server.cluster->size / 2) + 1;\n\n        if (reachable_masters < needed_quorum) {\n            new_state = CLUSTER_FAIL;\n            among_minority_time = mstime();\n        }\n    }\n\n    /* Log a state change */\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 > CLUSTER_MAX_REJOIN_DELAY)\n            rejoin_delay = CLUSTER_MAX_REJOIN_DELAY;\n        if (rejoin_delay < CLUSTER_MIN_REJOIN_DELAY)\n            rejoin_delay = CLUSTER_MIN_REJOIN_DELAY;\n\n        if (new_state == 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        serverLog(LL_WARNING,\"Cluster state changed: %s\",\n            new_state == CLUSTER_OK ? \"ok\" : \"fail\");\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 C_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 C_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, C_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. */\nint verifyClusterConfigWithData(void) {\n    int j;\n    int update_config = 0;\n\n    /* Return ASAP if a module disabled cluster redirections. In that case\n     * every master can store keys about every possible hash slot. */\n    if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)\n        return C_OK;\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    if (nodeIsSlave(myself)) return C_OK;\n\n    /* Make sure we only have keys in DB0. */\n    for (j = 1; j < server.dbnum; j++) {\n        if (dictSize(server.db[j].dict)) return C_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    for (j = 0; j < 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        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 responsibility for it. */\n        if (server.cluster->slots[j] == NULL) {\n            serverLog(LL_WARNING, \"I have keys for unassigned slot %d. \"\n                                    \"Taking responsibility for it.\",j);\n            clusterAddSlot(myself,j);\n        } else {\n            serverLog(LL_WARNING, \"I have keys for slot %d, but the slot is \"\n                                    \"assigned to another node. \"\n                                    \"Setting it to importing state.\",j);\n            server.cluster->importing_slots_from[j] = server.cluster->slots[j];\n        }\n    }\n    if (update_config) clusterSaveConfigOrDie(1);\n    return C_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. */\nvoid clusterSetMaster(clusterNode *n) {\n    serverAssert(n != myself);\n    serverAssert(myself->numslots == 0);\n\n    if (nodeIsMaster(myself)) {\n        myself->flags &= ~(CLUSTER_NODE_MASTER|CLUSTER_NODE_MIGRATE_TO);\n        myself->flags |= CLUSTER_NODE_SLAVE;\n        clusterCloseAllSlots();\n    } else {\n        if (myself->slaveof)\n            clusterNodeRemoveSlave(myself->slaveof,myself);\n    }\n    myself->slaveof = n;\n    clusterNodeAddSlave(n,myself);\n    replicationSetMaster(n->ip, n->port);\n    resetManualFailover();\n}\n\n/* -----------------------------------------------------------------------------\n * Nodes to string representation functions.\n * -------------------------------------------------------------------------- */\n\nstruct redisNodeFlags {\n    uint16_t flag;\n    char *name;\n};\n\nstatic struct redisNodeFlags redisNodeFlagsTable[] = {\n    {CLUSTER_NODE_MYSELF,       \"myself,\"},\n    {CLUSTER_NODE_MASTER,       \"master,\"},\n    {CLUSTER_NODE_SLAVE,        \"slave,\"},\n    {CLUSTER_NODE_PFAIL,        \"fail?,\"},\n    {CLUSTER_NODE_FAIL,         \"fail,\"},\n    {CLUSTER_NODE_HANDSHAKE,    \"handshake,\"},\n    {CLUSTER_NODE_NOADDR,       \"noaddr,\"},\n    {CLUSTER_NODE_NOFAILOVER,   \"nofailover,\"}\n};\n\n/* Concatenate the comma separated list of node flags to the given SDS\n * string 'ci'. */\nsds representClusterNodeFlags(sds ci, uint16_t flags) {\n    size_t orig_len = sdslen(ci);\n    int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);\n    for (i = 0; i < size; i++) {\n        struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;\n        if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);\n    }\n    /* If no flag was added, add the \"noflags\" special flag. */\n    if (sdslen(ci) == orig_len) ci = sdscat(ci,\"noflags,\");\n    sdsIncrLen(ci,-1); /* Remove trailing comma. */\n    return ci;\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. */\nsds clusterGenNodeDescription(clusterNode *node) {\n    int j, start;\n    sds ci;\n\n    /* Node coordinates */\n    ci = sdscatprintf(sdsempty(),\"%.40s %s:%d@%d \",\n        node->name,\n        node->ip,\n        node->port,\n        node->cport);\n\n    /* Flags */\n    ci = representClusterNodeFlags(ci, node->flags);\n\n    /* Slave of... or just \"-\" */\n    if (node->slaveof)\n        ci = sdscatprintf(ci,\" %.40s \",node->slaveof->name);\n    else\n        ci = sdscatlen(ci,\" - \",3);\n\n    /* Latency from the POV of this node, config epoch, 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 & CLUSTER_NODE_MYSELF) ?\n                    \"connected\" : \"disconnected\");\n\n    /* Slots served by this instance */\n    start = -1;\n    for (j = 0; j < 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 == CLUSTER_SLOTS-1)) {\n            if (bit && j == 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 & CLUSTER_NODE_MYSELF) {\n        for (j = 0; j < 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 * 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 * 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. */\nsds clusterGenNodesDescription(int filter) {\n    sds ci = sdsempty(), ni;\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n\n        if (node->flags & filter) continue;\n        ni = clusterGenNodeDescription(node);\n        ci = sdscatsds(ci,ni);\n        sdsfree(ni);\n        ci = sdscatlen(ci,\"\\n\",1);\n    }\n    dictReleaseIterator(di);\n    return ci;\n}\n\n/* -----------------------------------------------------------------------------\n * CLUSTER command\n * -------------------------------------------------------------------------- */\n\nconst char *clusterGetMessageTypeString(int type) {\n    switch(type) {\n    case CLUSTERMSG_TYPE_PING: return \"ping\";\n    case CLUSTERMSG_TYPE_PONG: return \"pong\";\n    case CLUSTERMSG_TYPE_MEET: return \"meet\";\n    case CLUSTERMSG_TYPE_FAIL: return \"fail\";\n    case CLUSTERMSG_TYPE_PUBLISH: return \"publish\";\n    case CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST: return \"auth-req\";\n    case CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK: return \"auth-ack\";\n    case CLUSTERMSG_TYPE_UPDATE: return \"update\";\n    case CLUSTERMSG_TYPE_MFSTART: return \"mfstart\";\n    case CLUSTERMSG_TYPE_MODULE: return \"module\";\n    }\n    return \"unknown\";\n}\n\nint getSlotOrReply(client *c, robj *o) {\n    long long slot;\n\n    if (getLongLongFromObject(o,&slot) != C_OK ||\n        slot < 0 || slot >= CLUSTER_SLOTS)\n    {\n        addReplyError(c,\"Invalid or out of range slot\");\n        return -1;\n    }\n    return (int) slot;\n}\n\nvoid clusterReplyMultiBulkSlots(client *c) {\n    /* Format: 1) 1) start slot\n     *            2) end slot\n     *            3) 1) master IP\n     *               2) master port\n     *               3) node ID\n     *            4) 1) replica IP\n     *               2) replica port\n     *               3) node ID\n     *           ... continued until done\n     */\n\n    int num_masters = 0;\n    void *slot_replylen = addReplyDeferredLen(c);\n\n    dictEntry *de;\n    dictIterator *di = dictGetSafeIterator(server.cluster->nodes);\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        int j = 0, start = -1;\n        int i, nested_elements = 0;\n\n        /* Skip slaves (that are iterated when producing the output of their\n         * master) and  masters not serving any slot. */\n        if (!nodeIsMaster(node) || node->numslots == 0) continue;\n\n        for(i = 0; i < node->numslaves; i++) {\n            if (nodeFailed(node->slaves[i])) continue;\n            nested_elements++;\n        }\n\n        for (j = 0; j < CLUSTER_SLOTS; j++) {\n            int bit, i;\n\n            if ((bit = clusterNodeGetSlotBit(node,j)) != 0) {\n                if (start == -1) start = j;\n            }\n            if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) {\n                addReplyArrayLen(c, nested_elements + 3); /* slots (2) + master addr (1). */\n\n                if (bit && j == CLUSTER_SLOTS-1) j++;\n\n                /* If slot exists in output map, add to it's list.\n                 * else, create a new output map for this slot */\n                if (start == j-1) {\n                    addReplyLongLong(c, start); /* only one slot; low==high */\n                    addReplyLongLong(c, start);\n                } else {\n                    addReplyLongLong(c, start); /* low */\n                    addReplyLongLong(c, j-1);   /* high */\n                }\n                start = -1;\n\n                /* First node reply position is always the master */\n                addReplyArrayLen(c, 3);\n                addReplyBulkCString(c, node->ip);\n                addReplyLongLong(c, node->port);\n                addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);\n\n                /* Remaining nodes in reply are replicas for slot range */\n                for (i = 0; i < node->numslaves; i++) {\n                    /* This loop is copy/pasted from clusterGenNodeDescription()\n                     * with modifications for per-slot node aggregation */\n                    if (nodeFailed(node->slaves[i])) continue;\n                    addReplyArrayLen(c, 3);\n                    addReplyBulkCString(c, node->slaves[i]->ip);\n                    addReplyLongLong(c, node->slaves[i]->port);\n                    addReplyBulkCBuffer(c, node->slaves[i]->name, CLUSTER_NAMELEN);\n                }\n                num_masters++;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n    setDeferredArrayLen(c, slot_replylen, num_masters);\n}\n\nvoid clusterCommand(client *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ADDSLOTS <slot> [slot ...] -- Assign slots to current node.\",\n\"BUMPEPOCH -- Advance the cluster config epoch.\",\n\"COUNT-failure-reports <node-id> -- Return number of failure reports for <node-id>.\",\n\"COUNTKEYSINSLOT <slot> - Return the number of keys in <slot>.\",\n\"DELSLOTS <slot> [slot ...] -- Delete slots information from current node.\",\n\"FAILOVER [force|takeover] -- Promote current replica node to being a master.\",\n\"FORGET <node-id> -- Remove a node from the cluster.\",\n\"GETKEYSINSLOT <slot> <count> -- Return key names stored by current node in a slot.\",\n\"FLUSHSLOTS -- Delete current node own slots information.\",\n\"INFO - Return information about the cluster.\",\n\"KEYSLOT <key> -- Return the hash slot for <key>.\",\n\"MEET <ip> <port> [bus-port] -- Connect nodes into a working cluster.\",\n\"MYID -- Return the node id.\",\n\"NODES -- Return cluster configuration seen by node. Output format:\",\n\"    <id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ... <slot>\",\n\"REPLICATE <node-id> -- Configure current node as replica to <node-id>.\",\n\"RESET [hard|soft] -- Reset current node (default: soft).\",\n\"SET-config-epoch <epoch> - Set config epoch of current node.\",\n\"SETSLOT <slot> (importing|migrating|stable|node <node-id>) -- Set slot state.\",\n\"REPLICAS <node-id> -- Return <node-id> replicas.\",\n\"SAVECONFIG - Force saving cluster configuration on disk.\",\n\"SLOTS -- Return information about slots range mappings. Each range is made of:\",\n\"    start, end, master and replicas IP addresses, ports and ids\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"meet\") && (c->argc == 4 || c->argc == 5)) {\n        /* CLUSTER MEET <ip> <port> [cport] */\n        long long port, cport;\n\n        if (getLongLongFromObject(c->argv[3], &port) != C_OK) {\n            addReplyErrorFormat(c,\"Invalid TCP base port specified: %s\",\n                                (char*)c->argv[3]->ptr);\n            return;\n        }\n\n        if (c->argc == 5) {\n            if (getLongLongFromObject(c->argv[4], &cport) != C_OK) {\n                addReplyErrorFormat(c,\"Invalid TCP bus port specified: %s\",\n                                    (char*)c->argv[4]->ptr);\n                return;\n            }\n        } else {\n            cport = port + CLUSTER_PORT_INCR;\n        }\n\n        if (clusterStartHandshake(c->argv[2]->ptr,port,cport) == 0 &&\n            errno == EINVAL)\n        {\n            addReplyErrorFormat(c,\"Invalid node address specified: %s:%s\",\n                            (char*)c->argv[2]->ptr, (char*)c->argv[3]->ptr);\n        } else {\n            addReply(c,shared.ok);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"nodes\") && c->argc == 2) {\n        /* CLUSTER NODES */\n        sds nodes = clusterGenNodesDescription(0);\n        addReplyVerbatim(c,nodes,sdslen(nodes),\"txt\");\n        sdsfree(nodes);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"myid\") && c->argc == 2) {\n        /* CLUSTER MYID */\n        addReplyBulkCBuffer(c,myself->name, CLUSTER_NAMELEN);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"slots\") && c->argc == 2) {\n        /* CLUSTER SLOTS */\n        clusterReplyMultiBulkSlots(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"flushslots\") && c->argc == 2) {\n        /* CLUSTER FLUSHSLOTS */\n        if (dictSize(server.db[0].dict) != 0) {\n            addReplyError(c,\"DB must be empty to perform CLUSTER FLUSHSLOTS.\");\n            return;\n        }\n        clusterDelNodeSlots(myself);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\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        /* CLUSTER DELSLOTS <slot> [slot] ... */\n        int j, slot;\n        unsigned char *slots = zmalloc(CLUSTER_SLOTS);\n        int del = !strcasecmp(c->argv[1]->ptr,\"delslots\");\n\n        memset(slots,0,CLUSTER_SLOTS);\n        /* Check that all the arguments are parseable and that all the\n         * slots are not already busy. */\n        for (j = 2; j < c->argc; j++) {\n            if ((slot = getSlotOrReply(c,c->argv[j])) == -1) {\n                zfree(slots);\n                return;\n            }\n            if (del && server.cluster->slots[slot] == NULL) {\n                addReplyErrorFormat(c,\"Slot %d is already unassigned\", slot);\n                zfree(slots);\n                return;\n            } else if (!del && server.cluster->slots[slot]) {\n                addReplyErrorFormat(c,\"Slot %d is already busy\", slot);\n                zfree(slots);\n                return;\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        for (j = 0; j < 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                if (server.cluster->importing_slots_from[j])\n                    server.cluster->importing_slots_from[j] = NULL;\n\n                retval = del ? clusterDelSlot(j) :\n                               clusterAddSlot(myself,j);\n                serverAssertWithInfo(c,NULL,retval == C_OK);\n            }\n        }\n        zfree(slots);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\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        if (nodeIsSlave(myself)) {\n            addReplyError(c,\"Please use SETSLOT only with masters.\");\n            return;\n        }\n\n        if ((slot = getSlotOrReply(c,c->argv[2])) == -1) return;\n\n        if (!strcasecmp(c->argv[3]->ptr,\"migrating\") && c->argc == 5) {\n            if (server.cluster->slots[slot] != myself) {\n                addReplyErrorFormat(c,\"I'm not the owner of hash slot %u\",slot);\n                return;\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            server.cluster->migrating_slots_to[slot] = n;\n        } else if (!strcasecmp(c->argv[3]->ptr,\"importing\") && c->argc == 5) {\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            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            server.cluster->importing_slots_from[slot] = n;\n        } else if (!strcasecmp(c->argv[3]->ptr,\"stable\") && c->argc == 4) {\n            /* CLUSTER SETSLOT <SLOT> STABLE */\n            server.cluster->importing_slots_from[slot] = NULL;\n            server.cluster->migrating_slots_to[slot] = NULL;\n        } else if (!strcasecmp(c->argv[3]->ptr,\"node\") && c->argc == 5) {\n            /* CLUSTER SETSLOT <SLOT> NODE <NODE ID> */\n            clusterNode *n = clusterLookupNode(c->argv[4]->ptr);\n\n            if (!n) {\n                addReplyErrorFormat(c,\"Unknown node %s\",\n                    (char*)c->argv[4]->ptr);\n                return;\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            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            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                if (clusterBumpConfigEpochWithoutConsensus() == C_OK) {\n                    serverLog(LL_WARNING,\n                        \"configEpoch updated after importing slot %d\", slot);\n                }\n                server.cluster->importing_slots_from[slot] = NULL;\n            }\n            clusterDelSlot(slot);\n            clusterAddSlot(n,slot);\n        } else {\n            addReplyError(c,\n                \"Invalid CLUSTER SETSLOT action or number of arguments. Try CLUSTER HELP\");\n            return;\n        }\n        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|CLUSTER_TODO_UPDATE_STATE);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"bumpepoch\") && c->argc == 2) {\n        /* CLUSTER BUMPEPOCH */\n        int retval = clusterBumpConfigEpochWithoutConsensus();\n        sds reply = sdscatprintf(sdsempty(),\"+%s %llu\\r\\n\",\n                (retval == C_OK) ? \"BUMPED\" : \"STILL\",\n                (unsigned long long) myself->configEpoch);\n        addReplySds(c,reply);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"info\") && c->argc == 2) {\n        /* CLUSTER INFO */\n        char *statestr[] = {\"ok\",\"fail\",\"needhelp\"};\n        int slots_assigned = 0, slots_ok = 0, slots_pfail = 0, slots_fail = 0;\n        uint64_t myepoch;\n        int j;\n\n        for (j = 0; j < CLUSTER_SLOTS; j++) {\n            clusterNode *n = server.cluster->slots[j];\n\n            if (n == NULL) continue;\n            slots_assigned++;\n            if (nodeFailed(n)) {\n                slots_fail++;\n            } else if (nodeTimedOut(n)) {\n                slots_pfail++;\n            } else {\n                slots_ok++;\n            }\n        }\n\n        myepoch = (nodeIsSlave(myself) && myself->slaveof) ?\n                  myself->slaveof->configEpoch : myself->configEpoch;\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_my_epoch:%llu\\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            (unsigned long long) myepoch\n        );\n\n        /* Show stats about messages sent and received. */\n        long long tot_msg_sent = 0;\n        long long tot_msg_received = 0;\n\n        for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {\n            if (server.cluster->stats_bus_messages_sent[i] == 0) continue;\n            tot_msg_sent += server.cluster->stats_bus_messages_sent[i];\n            info = sdscatprintf(info,\n                \"cluster_stats_messages_%s_sent:%lld\\r\\n\",\n                clusterGetMessageTypeString(i),\n                server.cluster->stats_bus_messages_sent[i]);\n        }\n        info = sdscatprintf(info,\n            \"cluster_stats_messages_sent:%lld\\r\\n\", tot_msg_sent);\n\n        for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {\n            if (server.cluster->stats_bus_messages_received[i] == 0) continue;\n            tot_msg_received += server.cluster->stats_bus_messages_received[i];\n            info = sdscatprintf(info,\n                \"cluster_stats_messages_%s_received:%lld\\r\\n\",\n                clusterGetMessageTypeString(i),\n                server.cluster->stats_bus_messages_received[i]);\n        }\n        info = sdscatprintf(info,\n            \"cluster_stats_messages_received:%lld\\r\\n\", tot_msg_received);\n\n        /* Produce the reply protocol. */\n        addReplyVerbatim(c,info,sdslen(info),\"txt\");\n        sdsfree(info);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"saveconfig\") && c->argc == 2) {\n        int retval = clusterSaveConfig(1);\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    } else if (!strcasecmp(c->argv[1]->ptr,\"keyslot\") && c->argc == 3) {\n        /* CLUSTER KEYSLOT <key> */\n        sds key = c->argv[2]->ptr;\n\n        addReplyLongLong(c,keyHashSlot(key,sdslen(key)));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"countkeysinslot\") && c->argc == 3) {\n        /* CLUSTER COUNTKEYSINSLOT <slot> */\n        long long slot;\n\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&slot,NULL) != C_OK)\n            return;\n        if (slot < 0 || slot >= CLUSTER_SLOTS) {\n            addReplyError(c,\"Invalid slot\");\n            return;\n        }\n        addReplyLongLong(c,countKeysInSlot(slot));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getkeysinslot\") && c->argc == 4) {\n        /* CLUSTER GETKEYSINSLOT <slot> <count> */\n        long long maxkeys, slot;\n        unsigned int numkeys, j;\n        robj **keys;\n\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&slot,NULL) != C_OK)\n            return;\n        if (getLongLongFromObjectOrReply(c,c->argv[3],&maxkeys,NULL)\n            != C_OK)\n            return;\n        if (slot < 0 || slot >= CLUSTER_SLOTS || maxkeys < 0) {\n            addReplyError(c,\"Invalid slot or number of keys\");\n            return;\n        }\n\n        /* Avoid allocating more than needed in case of large COUNT argument\n         * and smaller actual number of keys. */\n        unsigned int keys_in_slot = countKeysInSlot(slot);\n        if (maxkeys > keys_in_slot) maxkeys = keys_in_slot;\n\n        keys = zmalloc(sizeof(robj*)*maxkeys);\n        numkeys = getKeysInSlot(slot, keys, maxkeys);\n        addReplyArrayLen(c,numkeys);\n        for (j = 0; j < numkeys; j++) {\n            addReplyBulk(c,keys[j]);\n            decrRefCount(keys[j]);\n        }\n        zfree(keys);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"forget\") && c->argc == 3) {\n        /* CLUSTER FORGET <NODE ID> */\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\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        clusterBlacklistAddNode(n);\n        clusterDelNode(n);\n        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|\n                             CLUSTER_TODO_SAVE_CONFIG);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"replicate\") && c->argc == 3) {\n        /* CLUSTER REPLICATE <NODE ID> */\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        if (n == myself) {\n            addReplyError(c,\"Can't replicate myself\");\n            return;\n        }\n\n        /* Can't replicate a slave. */\n        if (nodeIsSlave(n)) {\n            addReplyError(c,\"I can only replicate a master, not a replica.\");\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        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        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\") ||\n                !strcasecmp(c->argv[1]->ptr,\"replicas\")) && c->argc == 3) {\n        /* CLUSTER SLAVES <NODE ID> */\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        addReplyArrayLen(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,\"count-failure-reports\") &&\n               c->argc == 3)\n    {\n        /* CLUSTER COUNT-FAILURE-REPORTS <NODE ID> */\n        clusterNode *n = clusterLookupNode(c->argv[2]->ptr);\n\n        if (!n) {\n            addReplyErrorFormat(c,\"Unknown node %s\", (char*)c->argv[2]->ptr);\n            return;\n        } else {\n            addReplyLongLong(c,clusterNodeFailureReportsCount(n));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"failover\") &&\n               (c->argc == 2 || c->argc == 3))\n    {\n        /* CLUSTER FAILOVER [FORCE|TAKEOVER] */\n        int force = 0, takeover = 0;\n\n        if (c->argc == 3) {\n            if (!strcasecmp(c->argv[2]->ptr,\"force\")) {\n                force = 1;\n            } else if (!strcasecmp(c->argv[2]->ptr,\"takeover\")) {\n                takeover = 1;\n                force = 1; /* Takeover also implies force. */\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n\n        /* Check preconditions. */\n        if (nodeIsMaster(myself)) {\n            addReplyError(c,\"You should send CLUSTER FAILOVER to a replica\");\n            return;\n        } else if (myself->slaveof == NULL) {\n            addReplyError(c,\"I'm a replica but my master is unknown to me\");\n            return;\n        } else if (!force &&\n                   (nodeFailed(myself->slaveof) ||\n                    myself->slaveof->link == NULL))\n        {\n            addReplyError(c,\"Master is down or failed, \"\n                            \"please use CLUSTER FAILOVER FORCE\");\n            return;\n        }\n        resetManualFailover();\n        server.cluster->mf_end = mstime() + CLUSTER_MF_TIMEOUT;\n\n        if (takeover) {\n            /* A takeover does not perform any initial check. It just\n             * generates a new configuration epoch for this node without\n             * consensus, claims the master's slots, and broadcast the new\n             * configuration. */\n            serverLog(LL_WARNING,\"Taking over the master (user request).\");\n            clusterBumpConfigEpochWithoutConsensus();\n            clusterFailoverReplaceYourMaster();\n        } else if (force) {\n            /* If this is a forced failover, we don't need to talk with our\n             * master to agree about the offset. We just failover taking over\n             * it without coordination. */\n            serverLog(LL_WARNING,\"Forced failover user request accepted.\");\n            server.cluster->mf_can_start = 1;\n        } else {\n            serverLog(LL_WARNING,\"Manual failover user request accepted.\");\n            clusterSendMFStart(myself->slaveof);\n        }\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) != C_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            serverLog(LL_WARNING,\n                \"configEpoch set to %llu via CLUSTER SET-CONFIG-EPOCH\",\n                (unsigned long long) myself->configEpoch);\n\n            if (server.cluster->currentEpoch < (uint64_t)epoch)\n                server.cluster->currentEpoch = 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        addReplySubcommandSyntaxError(c);\n        return;\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. */\nvoid createDumpPayload(rio *payload, robj *o, robj *key) {\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    rioInitWithBuffer(payload,sdsempty());\n    serverAssert(rdbSaveObjectType(payload,o));\n    serverAssert(rdbSaveObject(payload,o,key));\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    buf[0] = RDB_VERSION & 0xff;\n    buf[1] = (RDB_VERSION >> 8) & 0xff;\n    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,buf,2);\n\n    /* CRC64 */\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/* Verify that the RDB version of the dump payload matches the one of this Redis\n * instance and that the checksum is ok.\n * If the DUMP payload looks valid C_OK is returned, otherwise C_ERR\n * is returned. */\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    if (len < 10) return C_ERR;\n    footer = p+(len-10);\n\n    /* Verify RDB version */\n    rdbver = (footer[1] << 8) | footer[0];\n    if (rdbver > RDB_VERSION) return C_ERR;\n\n    /* Verify CRC64 */\n    crc = crc64(0,p,len-8);\n    memrev64ifbe(&crc);\n    return (memcmp(&crc,footer+2,8) == 0) ? C_OK : C_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(client *c) {\n    robj *o;\n    rio payload;\n\n    /* Check if the key is here. */\n    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {\n        addReplyNull(c);\n        return;\n    }\n\n    /* Create the DUMP encoded representation. */\n    createDumpPayload(&payload,o,c->argv[1]);\n\n    /* Transfer to the client */\n    addReplyBulkSds(c,payload.io.buffer.ptr);\n    return;\n}\n\n/* RESTORE key ttl serialized-value [REPLACE] */\nvoid restoreCommand(client *c) {\n    long long ttl, lfu_freq = -1, lru_idle = -1, lru_clock = -1;\n    rio payload;\n    int j, type, replace = 0, absttl = 0;\n    robj *obj;\n\n    /* Parse additional options */\n    for (j = 4; j < c->argc; j++) {\n        int additional = c->argc-j-1;\n        if (!strcasecmp(c->argv[j]->ptr,\"replace\")) {\n            replace = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"absttl\")) {\n            absttl = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"idletime\") && additional >= 1 &&\n                   lfu_freq == -1)\n        {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&lru_idle,NULL)\n                    != C_OK) return;\n            if (lru_idle < 0) {\n                addReplyError(c,\"Invalid IDLETIME value, must be >= 0\");\n                return;\n            }\n            lru_clock = LRU_CLOCK();\n            j++; /* Consume additional arg. */\n        } else if (!strcasecmp(c->argv[j]->ptr,\"freq\") && additional >= 1 &&\n                   lru_idle == -1)\n        {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&lfu_freq,NULL)\n                    != C_OK) return;\n            if (lfu_freq < 0 || lfu_freq > 255) {\n                addReplyError(c,\"Invalid FREQ value, must be >= 0 and <= 255\");\n                return;\n            }\n            j++; /* Consume additional arg. */\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Make sure this key does not already exist here... */\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    if (getLongLongFromObjectOrReply(c,c->argv[2],&ttl,NULL) != C_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    if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == C_ERR)\n    {\n        addReplyError(c,\"DUMP payload version or checksum are wrong\");\n        return;\n    }\n\n    rioInitWithBuffer(&payload,c->argv[3]->ptr);\n    if (((type = rdbLoadObjectType(&payload)) == -1) ||\n        ((obj = rdbLoadObject(type,&payload,c->argv[1]->ptr)) == NULL))\n    {\n        addReplyError(c,\"Bad data format\");\n        return;\n    }\n\n    /* Remove the old key if needed. */\n    if (replace) dbDelete(c->db,c->argv[1]);\n\n    /* Create the key and set the TTL if any */\n    dbAdd(c->db,c->argv[1],obj);\n    if (ttl) {\n        if (!absttl) ttl+=mstime();\n        setExpire(c,c->db,c->argv[1],ttl);\n    }\n    objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock,1000);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"restore\",c->argv[1],c->db->id);\n    addReply(c,shared.ok);\n    server.dirty++;\n}\n\n/* MIGRATE socket cache implementation.\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 * 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#define MIGRATE_SOCKET_CACHE_ITEMS 64 /* max num of items in the cache. */\n#define MIGRATE_SOCKET_CACHE_TTL 10 /* close cached sockets after 10 sec. */\n\ntypedef struct migrateCachedSocket {\n    connection *conn;\n    long last_dbid;\n    time_t last_use_time;\n} migrateCachedSocket;\n\n/* Return a migrateCachedSocket containing a TCP socket connected with the\n * target instance, possibly returning a cached one.\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 * If the caller detects an error while using the socket, migrateCloseSocket()\n * should be called so that the connection will be created from scratch\n * the next time. */\nmigrateCachedSocket* migrateGetSocket(client *c, robj *host, robj *port, long timeout) {\n    connection *conn;\n    sds name = sdsempty();\n    migrateCachedSocket *cs;\n\n    /* Check if we have an already cached socket for this ip:port pair. */\n    name = sdscatlen(name,host->ptr,sdslen(host->ptr));\n    name = sdscatlen(name,\":\",1);\n    name = sdscatlen(name,port->ptr,sdslen(port->ptr));\n    cs = dictFetchValue(server.migrate_cached_sockets,name);\n    if (cs) {\n        sdsfree(name);\n        cs->last_use_time = server.unixtime;\n        return cs;\n    }\n\n    /* No cached socket, create one. */\n    if (dictSize(server.migrate_cached_sockets) == MIGRATE_SOCKET_CACHE_ITEMS) {\n        /* Too many items, drop one at random. */\n        dictEntry *de = dictGetRandomKey(server.migrate_cached_sockets);\n        cs = dictGetVal(de);\n        connClose(cs->conn);\n        zfree(cs);\n        dictDelete(server.migrate_cached_sockets,dictGetKey(de));\n    }\n\n    /* Create the socket */\n    conn = server.tls_cluster ? connCreateTLS() : connCreateSocket();\n    if (connBlockingConnect(conn, c->argv[1]->ptr, atoi(c->argv[2]->ptr), timeout)\n            != C_OK) {\n        addReplySds(c,\n            sdsnew(\"-IOERR error or timeout connecting to the client\\r\\n\"));\n        connClose(conn);\n        sdsfree(name);\n        return NULL;\n    }\n    connEnableTcpNoDelay(conn);\n\n    /* Add to the cache and return it to the caller. */\n    cs = zmalloc(sizeof(*cs));\n    cs->conn = conn;\n\n    cs->last_dbid = -1;\n    cs->last_use_time = server.unixtime;\n    dictAdd(server.migrate_cached_sockets,name,cs);\n    return cs;\n}\n\n/* Free a migrate cached connection. */\nvoid migrateCloseSocket(robj *host, robj *port) {\n    sds name = sdsempty();\n    migrateCachedSocket *cs;\n\n    name = sdscatlen(name,host->ptr,sdslen(host->ptr));\n    name = sdscatlen(name,\":\",1);\n    name = sdscatlen(name,port->ptr,sdslen(port->ptr));\n    cs = dictFetchValue(server.migrate_cached_sockets,name);\n    if (!cs) {\n        sdsfree(name);\n        return;\n    }\n\n    connClose(cs->conn);\n    zfree(cs);\n    dictDelete(server.migrate_cached_sockets,name);\n    sdsfree(name);\n}\n\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        if ((server.unixtime - cs->last_use_time) > MIGRATE_SOCKET_CACHE_TTL) {\n            connClose(cs->conn);\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 | AUTH password |\n *         AUTH2 username password]\n *\n * On in the multiple keys form:\n *\n * MIGRATE host port \"\" dbid timeout [COPY | REPLACE | AUTH password |\n *         AUTH2 username password] KEYS key1 key2 ... keyN */\nvoid migrateCommand(client *c) {\n    migrateCachedSocket *cs;\n    int copy = 0, replace = 0, j;\n    char *username = NULL;\n    char *password = NULL;\n    long timeout;\n    long dbid;\n    robj **ov = NULL; /* Objects to migrate. */\n    robj **kv = NULL; /* Key names. */\n    robj **newargv = NULL; /* Used to rewrite the command as DEL ... keys ... */\n    rio cmd, payload;\n    int may_retry = 1;\n    int write_error = 0;\n    int argv_rewritten = 0;\n\n    /* To support the KEYS option we need the following additional state. */\n    int first_key = 3; /* Argument index of the first key. */\n    int num_keys = 1;  /* By default only migrate the 'key' argument. */\n\n    /* Parse additional options */\n    for (j = 6; j < c->argc; j++) {\n        int moreargs = (c->argc-1) - 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 if (!strcasecmp(c->argv[j]->ptr,\"auth\")) {\n            if (!moreargs) {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n            j++;\n            password = c->argv[j]->ptr;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"auth2\")) {\n            if (moreargs < 2) {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n            username = c->argv[++j]->ptr;\n            password = c->argv[++j]->ptr;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"keys\")) {\n            if (sdslen(c->argv[3]->ptr) != 0) {\n                addReplyError(c,\n                    \"When using MIGRATE KEYS option, the key argument\"\n                    \" must be set to the empty string\");\n                return;\n            }\n            first_key = j+1;\n            num_keys = c->argc - j - 1;\n            break; /* All the remaining args are keys. */\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Sanity check */\n    if (getLongFromObjectOrReply(c,c->argv[5],&timeout,NULL) != C_OK ||\n        getLongFromObjectOrReply(c,c->argv[4],&dbid,NULL) != C_OK)\n    {\n        return;\n    }\n    if (timeout <= 0) timeout = 1000;\n\n    /* Check if the keys are here. If at least one key is to migrate, do it\n     * otherwise if all the keys are missing reply with \"NOKEY\" to signal\n     * the caller there was nothing to migrate. We don't return an error in\n     * this case, since often this is due to a normal condition like the key\n     * expiring in the meantime. */\n    ov = zrealloc(ov,sizeof(robj*)*num_keys);\n    kv = zrealloc(kv,sizeof(robj*)*num_keys);\n    int oi = 0;\n\n    for (j = 0; j < num_keys; j++) {\n        if ((ov[oi] = lookupKeyRead(c->db,c->argv[first_key+j])) != NULL) {\n            kv[oi] = c->argv[first_key+j];\n            oi++;\n        }\n    }\n    num_keys = oi;\n    if (num_keys == 0) {\n        zfree(ov); zfree(kv);\n        addReplySds(c,sdsnew(\"+NOKEY\\r\\n\"));\n        return;\n    }\n\ntry_again:\n    write_error = 0;\n\n    /* Connect */\n    cs = migrateGetSocket(c,c->argv[1],c->argv[2],timeout);\n    if (cs == NULL) {\n        zfree(ov); zfree(kv);\n        return; /* error sent to the client by migrateGetSocket() */\n    }\n\n    rioInitWithBuffer(&cmd,sdsempty());\n\n    /* Authentication */\n    if (password) {\n        int arity = username ? 3 : 2;\n        serverAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',arity));\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"AUTH\",4));\n        if (username) {\n            serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,username,\n                                 sdslen(username)));\n        }\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,password,\n            sdslen(password)));\n    }\n\n    /* Send the SELECT command if the current DB is not already selected. */\n    int select = cs->last_dbid != dbid; /* Should we emit SELECT? */\n    if (select) {\n        serverAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"SELECT\",6));\n        serverAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));\n    }\n\n    int non_expired = 0; /* Number of keys that we'll find non expired.\n                            Note that serializing large keys may take some time\n                            so certain keys that were found non expired by the\n                            lookupKey() function, may be expired later. */\n\n    /* Create RESTORE payload and generate the protocol to call the command. */\n    for (j = 0; j < num_keys; j++) {\n        long long ttl = 0;\n        long long expireat = getExpire(c->db,kv[j]);\n\n        if (expireat != -1) {\n            ttl = expireat-mstime();\n            if (ttl < 0) {\n                continue;\n            }\n            if (ttl < 1) ttl = 1;\n        }\n\n        /* Relocate valid (non expired) keys into the array in successive\n         * positions to remove holes created by the keys that were present\n         * in the first lookup but are now expired after the second lookup. */\n        kv[non_expired++] = kv[j];\n\n        serverAssertWithInfo(c,NULL,\n            rioWriteBulkCount(&cmd,'*',replace ? 5 : 4));\n\n        if (server.cluster_enabled)\n            serverAssertWithInfo(c,NULL,\n                rioWriteBulkString(&cmd,\"RESTORE-ASKING\",14));\n        else\n            serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"RESTORE\",7));\n        serverAssertWithInfo(c,NULL,sdsEncodedObject(kv[j]));\n        serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,kv[j]->ptr,\n                sdslen(kv[j]->ptr)));\n        serverAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,ttl));\n\n        /* Emit the payload argument, that is the serialized object using\n         * the DUMP format. */\n        createDumpPayload(&payload,ov[j],kv[j]);\n        serverAssertWithInfo(c,NULL,\n            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        if (replace)\n            serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,\"REPLACE\",7));\n    }\n\n    /* Fix the actual number of keys we are migrating. */\n    num_keys = non_expired;\n\n    /* Transfer the query to the other node in 64K chunks. */\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 = connSyncWrite(cs->conn,buf+pos,towrite,timeout);\n            if (nwritten != (signed)towrite) {\n                write_error = 1;\n                goto socket_err;\n            }\n            pos += nwritten;\n        }\n    }\n\n    char buf0[1024]; /* Auth reply. */\n    char buf1[1024]; /* Select reply. */\n    char buf2[1024]; /* Restore reply. */\n\n    /* Read the AUTH reply if needed. */\n    if (password && connSyncReadLine(cs->conn, buf0, sizeof(buf0), timeout) <= 0)\n        goto socket_err;\n\n    /* Read the SELECT reply if needed. */\n    if (select && connSyncReadLine(cs->conn, buf1, sizeof(buf1), timeout) <= 0)\n        goto socket_err;\n\n    /* Read the RESTORE replies. */\n    int error_from_target = 0;\n    int socket_error = 0;\n    int del_idx = 1; /* Index of the key argument for the replicated DEL op. */\n\n    /* Allocate the new argument vector that will replace the current command,\n     * to propagate the MIGRATE as a DEL command (if no COPY option was given).\n     * We allocate num_keys+1 because the additional argument is for \"DEL\"\n     * command name itself. */\n    if (!copy) newargv = zmalloc(sizeof(robj*)*(num_keys+1));\n\n    for (j = 0; j < num_keys; j++) {\n        if (connSyncReadLine(cs->conn, buf2, sizeof(buf2), timeout) <= 0) {\n            socket_error = 1;\n            break;\n        }\n        if ((password && buf0[0] == '-') ||\n            (select && buf1[0] == '-') ||\n            buf2[0] == '-')\n        {\n            /* On error assume that last_dbid is no longer valid. */\n            if (!error_from_target) {\n                cs->last_dbid = -1;\n                char *errbuf;\n                if (password && buf0[0] == '-') errbuf = buf0;\n                else if (select && buf1[0] == '-') errbuf = buf1;\n                else errbuf = buf2;\n\n                error_from_target = 1;\n                addReplyErrorFormat(c,\"Target instance replied with error: %s\",\n                    errbuf+1);\n            }\n        } else {\n            if (!copy) {\n                /* No COPY option: remove the local key, signal the change. */\n                dbDelete(c->db,kv[j]);\n                signalModifiedKey(c,c->db,kv[j]);\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",kv[j],c->db->id);\n                server.dirty++;\n\n                /* Populate the argument vector to replace the old one. */\n                newargv[del_idx++] = kv[j];\n                incrRefCount(kv[j]);\n            }\n        }\n    }\n\n    /* On socket error, if we want to retry, do it now before rewriting the\n     * command vector. We only retry if we are sure nothing was processed\n     * and we failed to read the first reply (j == 0 test). */\n    if (!error_from_target && socket_error && j == 0 && may_retry &&\n        errno != ETIMEDOUT)\n    {\n        goto socket_err; /* A retry is guaranteed because of tested conditions.*/\n    }\n\n    /* On socket errors, close the migration socket now that we still have\n     * the original host/port in the ARGV. Later the original command may be\n     * rewritten to DEL and will be too later. */\n    if (socket_error) migrateCloseSocket(c->argv[1],c->argv[2]);\n\n    if (!copy) {\n        /* Translate MIGRATE as DEL for replication/AOF. Note that we do\n         * this only for the keys for which we received an acknowledgement\n         * from the receiving Redis server, by using the del_idx index. */\n        if (del_idx > 1) {\n            newargv[0] = createStringObject(\"DEL\",3);\n            /* Note that the following call takes ownership of newargv. */\n            replaceClientCommandVector(c,del_idx,newargv);\n            argv_rewritten = 1;\n        } else {\n            /* No key transfer acknowledged, no need to rewrite as DEL. */\n            zfree(newargv);\n        }\n        newargv = NULL; /* Make it safe to call zfree() on it in the future. */\n    }\n\n    /* If we are here and a socket error happened, we don't want to retry.\n     * Just signal the problem to the client, but only do it if we did not\n     * already queue a different error reported by the destination server. */\n    if (!error_from_target && socket_error) {\n        may_retry = 0;\n        goto socket_err;\n    }\n\n    if (!error_from_target) {\n        /* Success! Update the last_dbid in migrateCachedSocket, so that we can\n         * avoid SELECT the next time if the target DB is the same. Reply +OK.\n         *\n         * Note: If we reached this point, even if socket_error is true\n         * still the SELECT command succeeded (otherwise the code jumps to\n         * socket_err label. */\n        cs->last_dbid = dbid;\n        addReply(c,shared.ok);\n    } else {\n        /* On error we already sent it in the for loop above, and set\n         * the currently selected socket to -1 to force SELECT the next time. */\n    }\n\n    sdsfree(cmd.io.buffer.ptr);\n    zfree(ov); zfree(kv); zfree(newargv);\n    return;\n\n/* On socket errors we try to close the cached socket and try again.\n * It is very common for the cached socket to get closed, if just reopening\n * it works it's a shame to notify the error to the caller. */\nsocket_err:\n    /* Cleanup we want to perform in both the retry and no retry case.\n     * Note: Closing the migrate socket will also force SELECT next time. */\n    sdsfree(cmd.io.buffer.ptr);\n\n    /* If the command was rewritten as DEL and there was a socket error,\n     * we already closed the socket earlier. While migrateCloseSocket()\n     * is idempotent, the host/port arguments are now gone, so don't do it\n     * again. */\n    if (!argv_rewritten) migrateCloseSocket(c->argv[1],c->argv[2]);\n    zfree(newargv);\n    newargv = NULL; /* This will get reallocated on retry. */\n\n    /* Retry only if it's not a timeout and we never attempted a retry\n     * (or the code jumping here did not set may_retry to zero). */\n    if (errno != ETIMEDOUT && may_retry) {\n        may_retry = 0;\n        goto try_again;\n    }\n\n    /* Cleanup we want to do if no retry is attempted. */\n    zfree(ov); zfree(kv);\n    addReplySds(c,\n        sdscatprintf(sdsempty(),\n            \"-IOERR error or timeout %s to target instance\\r\\n\",\n            write_error ? \"writing\" : \"reading\"));\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 * 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. */\nvoid askingCommand(client *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n    c->flags |= CLIENT_ASKING;\n    addReply(c,shared.ok);\n}\n\n/* The READONLY command is used 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(client *c) {\n    if (server.cluster_enabled == 0) {\n        addReplyError(c,\"This instance has cluster support disabled\");\n        return;\n    }\n    c->flags |= CLIENT_READONLY;\n    addReply(c,shared.ok);\n}\n\n/* The READWRITE command just clears the READONLY command state. */\nvoid readwriteCommand(client *c) {\n    c->flags &= ~CLIENT_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 CLUSTER_REDIR_ASK or\n * CLUSTER_REDIR_MOVED.\n *\n * When the node is 'myself' 'error_code' is set to 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 * CLUSTER_REDIR_CROSS_SLOT if the request contains multiple keys that\n * don't belong to the same hash slot.\n *\n * CLUSTER_REDIR_UNSTABLE if the request contains multiple 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).\n *\n * CLUSTER_REDIR_DOWN_UNBOUND if the request addresses a slot which is\n * not bound to any node. In this case the cluster global state should be\n * already \"down\" but it is fragile to rely on the update of the global state,\n * so we also handle it here.\n *\n * CLUSTER_REDIR_DOWN_STATE and CLUSTER_REDIR_DOWN_RO_STATE if the cluster is\n * down but the user attempts to execute a command that addresses one or more keys. */\nclusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *error_code) {\n    clusterNode *n = NULL;\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    /* Allow any key to be set if a module disabled cluster redirections. */\n    if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)\n        return myself;\n\n    /* Set error code optimistically for the base case. */\n    if (error_code) *error_code = CLUSTER_REDIR_NONE;\n\n    /* Modules can turn off Redis Cluster redirection: this is useful\n     * when writing a module that implements a completely different\n     * distributed system. */\n\n    /* We handle all the cases as if they were EXEC commands, so we have\n     * a common code path for everything */\n    if (cmd->proc == execCommand) {\n        /* If CLIENT_MULTI flag is not set EXEC is just going to return an\n         * error. */\n        if (!(c->flags & CLIENT_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        keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);\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                /* 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\n                /* Error: If a slot is not served, we are in \"cluster down\"\n                 * state. However the state is yet to be updated, so this was\n                 * not trapped earlier in processCommand(). Report the same\n                 * error to the client. */\n                if (n == NULL) {\n                    getKeysFreeResult(keyindex);\n                    if (error_code)\n                        *error_code = CLUSTER_REDIR_DOWN_UNBOUND;\n                    return NULL;\n                }\n\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 = 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 in all the cases. */\n    if (n == NULL) return myself;\n\n    /* Cluster is globally down but we got keys? We only serve the request\n     * if it is a read command and when allow_reads_when_down is enabled. */\n    if (server.cluster->state != CLUSTER_OK) {\n        if (!server.cluster_allow_reads_when_down) {\n            /* The cluster is configured to block commands when the\n             * cluster is down. */\n            if (error_code) *error_code = CLUSTER_REDIR_DOWN_STATE;\n            return NULL;\n        } else if (!(cmd->flags & CMD_READONLY) && !(cmd->proc == evalCommand)\n                && !(cmd->proc == evalShaCommand))\n        {\n            /* The cluster is configured to allow read only commands\n             * but this command is neither readonly, nor EVAL or\n             * EVALSHA. */\n            if (error_code) *error_code = CLUSTER_REDIR_DOWN_RO_STATE;\n            return NULL;\n        } else {\n            /* Fall through and allow the command to be executed:\n             * this happens when server.cluster_allow_reads_when_down is\n             * true and the command is a readonly command or EVAL / EVALSHA. */\n        }\n    }\n\n    /* Return the hashslot by reference. */\n    if (hashslot) *hashslot = slot;\n\n    /* MIGRATE always works in the context of the local node if the slot\n     * is open (migrating or importing state). We need to be able to freely\n     * move keys among instances in this case. */\n    if ((migrating_slot || importing_slot) && cmd->proc == migrateCommand)\n        return myself;\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 = 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 & CLIENT_ASKING || cmd->flags & CMD_ASKING))\n    {\n        if (multiple_keys && missing_keys) {\n            if (error_code) *error_code = 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 & CLIENT_READONLY &&\n        (cmd->flags & CMD_READONLY || cmd->proc == evalCommand ||\n         cmd->proc == evalShaCommand) &&\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 = CLUSTER_REDIR_MOVED;\n    return n;\n}\n\n/* Send the client the right redirection code, according to error_code\n * that should be set to one of CLUSTER_REDIR_* macros.\n *\n * If CLUSTER_REDIR_ASK or CLUSTER_REDIR_MOVED error codes\n * are used, then the node 'n' should not be NULL, but should be the\n * node we want to mention in the redirection. Moreover hashslot should\n * be set to the hash slot that caused the redirection. */\nvoid clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code) {\n    if (error_code == 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 == CLUSTER_REDIR_UNSTABLE) {\n        /* The request spawns multiple 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 if (error_code == CLUSTER_REDIR_DOWN_STATE) {\n        addReplySds(c,sdsnew(\"-CLUSTERDOWN The cluster is down\\r\\n\"));\n    } else if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) {\n        addReplySds(c,sdsnew(\"-CLUSTERDOWN The cluster is down and only accepts read commands\\r\\n\"));\n    } else if (error_code == CLUSTER_REDIR_DOWN_UNBOUND) {\n        addReplySds(c,sdsnew(\"-CLUSTERDOWN Hash slot not served\\r\\n\"));\n    } else if (error_code == CLUSTER_REDIR_MOVED ||\n               error_code == CLUSTER_REDIR_ASK)\n    {\n        addReplySds(c,sdscatprintf(sdsempty(),\n            \"-%s %d %s:%d\\r\\n\",\n            (error_code == CLUSTER_REDIR_ASK) ? \"ASK\" : \"MOVED\",\n            hashslot,n->ip,n->port));\n    } else {\n        serverPanic(\"getNodeByQuery() unknown error.\");\n    }\n}\n\n/* This function is called by the function processing clients incrementally\n * to detect timeouts, in order to handle the following case:\n *\n * 1) A client blocks with BLPOP or similar blocking operation.\n * 2) The master migrates the hash slot elsewhere or turns into a slave.\n * 3) The client may remain blocked forever (or up to the max timeout time)\n *    waiting for a key change that will never happen.\n *\n * If the client is found to be blocked into an hash slot this node no\n * longer handles, the client is sent a redirection error, and the function\n * returns 1. Otherwise 0 is returned and no operation is performed. */\nint clusterRedirectBlockedClientIfNeeded(client *c) {\n    if (c->flags & CLIENT_BLOCKED &&\n        (c->btype == BLOCKED_LIST ||\n         c->btype == BLOCKED_ZSET ||\n         c->btype == BLOCKED_STREAM))\n    {\n        dictEntry *de;\n        dictIterator *di;\n\n        /* If the cluster is down, unblock the client with the right error.\n         * If the cluster is configured to allow reads on cluster down, we\n         * still want to emit this error since a write will be required\n         * to unblock them which may never come.  */\n        if (server.cluster->state == CLUSTER_FAIL) {\n            clusterRedirectClient(c,NULL,0,CLUSTER_REDIR_DOWN_STATE);\n            return 1;\n        }\n\n        /* All keys must belong to the same slot, so check first key only. */\n        di = dictGetIterator(c->bpop.keys);\n        if ((de = dictNext(di)) != NULL) {\n            robj *key = dictGetKey(de);\n            int slot = keyHashSlot((char*)key->ptr, sdslen(key->ptr));\n            clusterNode *node = server.cluster->slots[slot];\n\n            /* We send an error and unblock the client if:\n             * 1) The slot is unassigned, emitting a cluster down error.\n             * 2) The slot is not handled by this node, nor being imported. */\n            if (node != myself &&\n                server.cluster->importing_slots_from[slot] == NULL)\n            {\n                if (node == NULL) {\n                    clusterRedirectClient(c,NULL,0,\n                        CLUSTER_REDIR_DOWN_UNBOUND);\n                } else {\n                    clusterRedirectClient(c,node,slot,\n                        CLUSTER_REDIR_MOVED);\n                }\n                dictReleaseIterator(di);\n                return 1;\n            }\n        }\n        dictReleaseIterator(di);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/cluster.h",
    "content": "#ifndef __CLUSTER_H\n#define __CLUSTER_H\n\n/*-----------------------------------------------------------------------------\n * Redis cluster data structures, defines, exported API.\n *----------------------------------------------------------------------------*/\n\n#define CLUSTER_SLOTS 16384\n#define CLUSTER_OK 0          /* Everything looks ok */\n#define CLUSTER_FAIL 1        /* The cluster can't work */\n#define CLUSTER_NAMELEN 40    /* sha1 hex length */\n#define CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */\n\n/* The following defines are amount of time, sometimes expressed as\n * multiplicators of the node timeout value (when ending with MULT). */\n#define CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */\n#define CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if master is back. */\n#define CLUSTER_FAIL_UNDO_TIME_ADD 10 /* Some additional time. */\n#define CLUSTER_FAILOVER_DELAY 5 /* Seconds */\n#define CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */\n#define CLUSTER_MF_PAUSE_MULT 2 /* Master pause manual failover mult. */\n#define CLUSTER_SLAVE_MIGRATION_DELAY 5000 /* Delay for slave migration. */\n\n/* Redirection errors returned by getNodeByQuery(). */\n#define CLUSTER_REDIR_NONE 0          /* Node can serve the request. */\n#define CLUSTER_REDIR_CROSS_SLOT 1    /* -CROSSSLOT request. */\n#define CLUSTER_REDIR_UNSTABLE 2      /* -TRYAGAIN redirection required */\n#define CLUSTER_REDIR_ASK 3           /* -ASK redirection required. */\n#define CLUSTER_REDIR_MOVED 4         /* -MOVED redirection required. */\n#define CLUSTER_REDIR_DOWN_STATE 5    /* -CLUSTERDOWN, global state. */\n#define CLUSTER_REDIR_DOWN_UNBOUND 6  /* -CLUSTERDOWN, unbound slot. */\n#define CLUSTER_REDIR_DOWN_RO_STATE 7 /* -CLUSTERDOWN, allow reads. */\n\nstruct clusterNode;\n\n/* clusterLink encapsulates everything needed to talk with a remote node. */\ntypedef struct clusterLink {\n    mstime_t ctime;             /* Link creation time */\n    connection *conn;           /* Connection to remote node */\n    sds sndbuf;                 /* Packet send buffer */\n    sds rcvbuf;                 /* Packet reception buffer */\n    struct clusterNode *node;   /* Node related to this link if any, or NULL */\n} clusterLink;\n\n/* Cluster node flags and macros. */\n#define CLUSTER_NODE_MASTER 1     /* The node is a master */\n#define CLUSTER_NODE_SLAVE 2      /* The node is a slave */\n#define CLUSTER_NODE_PFAIL 4      /* Failure? Need acknowledge */\n#define CLUSTER_NODE_FAIL 8       /* The node is believed to be malfunctioning */\n#define CLUSTER_NODE_MYSELF 16    /* This node is myself */\n#define CLUSTER_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */\n#define CLUSTER_NODE_NOADDR   64  /* We don't know the address of this node */\n#define CLUSTER_NODE_MEET 128     /* Send a MEET message to this node */\n#define CLUSTER_NODE_MIGRATE_TO 256 /* Master elegible for replica migration. */\n#define CLUSTER_NODE_NOFAILOVER 512 /* Slave will not try to failver. */\n#define CLUSTER_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#define nodeIsMaster(n) ((n)->flags & CLUSTER_NODE_MASTER)\n#define nodeIsSlave(n) ((n)->flags & CLUSTER_NODE_SLAVE)\n#define nodeInHandshake(n) ((n)->flags & CLUSTER_NODE_HANDSHAKE)\n#define nodeHasAddr(n) (!((n)->flags & CLUSTER_NODE_NOADDR))\n#define nodeWithoutAddr(n) ((n)->flags & CLUSTER_NODE_NOADDR)\n#define nodeTimedOut(n) ((n)->flags & CLUSTER_NODE_PFAIL)\n#define nodeFailed(n) ((n)->flags & CLUSTER_NODE_FAIL)\n#define nodeCantFailover(n) ((n)->flags & CLUSTER_NODE_NOFAILOVER)\n\n/* Reasons why a slave is not able to failover. */\n#define CLUSTER_CANT_FAILOVER_NONE 0\n#define CLUSTER_CANT_FAILOVER_DATA_AGE 1\n#define CLUSTER_CANT_FAILOVER_WAITING_DELAY 2\n#define CLUSTER_CANT_FAILOVER_EXPIRED 3\n#define CLUSTER_CANT_FAILOVER_WAITING_VOTES 4\n#define CLUSTER_CANT_FAILOVER_RELOG_PERIOD (60*5) /* seconds. */\n\n/* clusterState todo_before_sleep flags. */\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/* Message types.\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#define CLUSTERMSG_TYPE_PING 0          /* Ping */\n#define CLUSTERMSG_TYPE_PONG 1          /* Pong (reply to Ping) */\n#define CLUSTERMSG_TYPE_MEET 2          /* Meet \"let's join\" message */\n#define CLUSTERMSG_TYPE_FAIL 3          /* Mark node xxx as failing */\n#define CLUSTERMSG_TYPE_PUBLISH 4       /* Pub/Sub Publish propagation */\n#define CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 5 /* May I failover? */\n#define CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 6     /* Yes, you have my vote */\n#define CLUSTERMSG_TYPE_UPDATE 7        /* Another node slots configuration */\n#define CLUSTERMSG_TYPE_MFSTART 8       /* Pause clients for manual failover */\n#define CLUSTERMSG_TYPE_MODULE 9        /* Module cluster API message. */\n#define CLUSTERMSG_TYPE_COUNT 10        /* Total number of message types. */\n\n/* Flags that a module can set in order to prevent certain Redis Cluster\n * features to be enabled. Useful when implementing a different distributed\n * system on top of Redis Cluster message bus, using modules. */\n#define CLUSTER_MODULE_FLAG_NONE 0\n#define CLUSTER_MODULE_FLAG_NO_FAILOVER (1<<1)\n#define CLUSTER_MODULE_FLAG_NO_REDIRECTION (1<<2)\n\n/* This structure represent elements of node->fail_reports. */\ntypedef struct clusterNodeFailReport {\n    struct clusterNode *node;  /* Node reporting the failure condition. */\n    mstime_t time;             /* Time of the last report from this node. */\n} clusterNodeFailReport;\n\ntypedef struct clusterNode {\n    mstime_t ctime; /* Node object creation time. */\n    char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */\n    int flags;      /* CLUSTER_NODE_... */\n    uint64_t configEpoch; /* Last configEpoch observed for this node */\n    unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */\n    int numslots;   /* Number of slots handled by this node */\n    int numslaves;  /* Number of slave nodes, if this is a master */\n    struct clusterNode **slaves; /* pointers to slave nodes */\n    struct clusterNode *slaveof; /* pointer to the master node. Note that it\n                                    may be NULL even if the node is a slave\n                                    if we don't have the master node in our\n                                    tables. */\n    mstime_t ping_sent;      /* Unix time we sent latest ping */\n    mstime_t pong_received;  /* Unix time we received the pong */\n    mstime_t data_received;  /* Unix time we received any data */\n    mstime_t fail_time;      /* Unix time when FAIL flag was set */\n    mstime_t voted_time;     /* Last time we voted for a slave of this master */\n    mstime_t repl_offset_time;  /* Unix time we received offset for this node */\n    mstime_t orphaned_time;     /* Starting time of orphaned master condition */\n    long long repl_offset;      /* Last known repl offset for this node. */\n    char ip[NET_IP_STR_LEN];  /* Latest known IP address of this node */\n    int port;                   /* Latest known clients port of this node */\n    int cport;                  /* Latest known cluster port of this node. */\n    clusterLink *link;          /* TCP/IP link with this node */\n    list *fail_reports;         /* List of nodes signaling this as failing */\n} clusterNode;\n\ntypedef struct clusterState {\n    clusterNode *myself;  /* This node */\n    uint64_t currentEpoch;\n    int state;            /* CLUSTER_OK, CLUSTER_FAIL, ... */\n    int size;             /* Num of master nodes with at least one slot */\n    dict *nodes;          /* Hash table of name -> clusterNode structures */\n    dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */\n    clusterNode *migrating_slots_to[CLUSTER_SLOTS];\n    clusterNode *importing_slots_from[CLUSTER_SLOTS];\n    clusterNode *slots[CLUSTER_SLOTS];\n    uint64_t slots_keys_count[CLUSTER_SLOTS];\n    rax *slots_to_keys;\n    /* The following fields are used to take the slave state on elections. */\n    mstime_t failover_auth_time; /* Time of previous or next election. */\n    int failover_auth_count;    /* Number of votes received so far. */\n    int failover_auth_sent;     /* True if we already asked for votes. */\n    int failover_auth_rank;     /* This slave rank for current auth request. */\n    uint64_t failover_auth_epoch; /* Epoch of the current election. */\n    int cant_failover_reason;   /* Why a slave is currently not able to\n                                   failover. See the CANT_FAILOVER_* macros. */\n    /* Manual failover state in common. */\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    clusterNode *mf_slave;      /* Slave performing the manual failover. */\n    /* Manual failover state of slave. */\n    long long mf_master_offset; /* Master offset the slave needs to start MF\n                                   or zero if stil not received. */\n    int mf_can_start;           /* If non-zero signal that the manual failover\n                                   can start requesting masters vote. */\n    /* The followign fields are used by masters to take state on elections. */\n    uint64_t lastVoteEpoch;     /* Epoch of the last vote granted. */\n    int todo_before_sleep; /* Things to do in clusterBeforeSleep(). */\n    /* Messages received and sent by type. */\n    long long stats_bus_messages_sent[CLUSTERMSG_TYPE_COUNT];\n    long long stats_bus_messages_received[CLUSTERMSG_TYPE_COUNT];\n    long long stats_pfail_nodes;    /* Number of nodes in PFAIL status,\n                                       excluding nodes without address. */\n} clusterState;\n\n/* Redis cluster messages header */\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. */\ntypedef struct {\n    char nodename[CLUSTER_NAMELEN];\n    uint32_t ping_sent;\n    uint32_t pong_received;\n    char ip[NET_IP_STR_LEN];  /* IP address last time it was seen */\n    uint16_t port;              /* base port last time it was seen */\n    uint16_t cport;             /* cluster port last time it was seen */\n    uint16_t flags;             /* node->flags copy */\n    uint32_t notused1;\n} clusterMsgDataGossip;\n\ntypedef struct {\n    char nodename[CLUSTER_NAMELEN];\n} clusterMsgDataFail;\n\ntypedef struct {\n    uint32_t channel_len;\n    uint32_t message_len;\n    unsigned char bulk_data[8]; /* 8 bytes just as placeholder. */\n} clusterMsgDataPublish;\n\ntypedef struct {\n    uint64_t configEpoch; /* Config epoch of the specified instance. */\n    char nodename[CLUSTER_NAMELEN]; /* Name of the slots owner. */\n    unsigned char slots[CLUSTER_SLOTS/8]; /* Slots bitmap. */\n} clusterMsgDataUpdate;\n\ntypedef struct {\n    uint64_t module_id;     /* ID of the sender module. */\n    uint32_t len;           /* ID of the sender module. */\n    uint8_t type;           /* Type from 0 to 255. */\n    unsigned char bulk_data[3]; /* 3 bytes just as placeholder. */\n} clusterMsgModule;\n\nunion clusterMsgData {\n    /* PING, MEET and PONG */\n    struct {\n        /* Array of N clusterMsgDataGossip structures */\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    /* MODULE */\n    struct {\n        clusterMsgModule msg;\n    } module;\n};\n\n#define CLUSTER_PROTO_VER 1 /* Cluster bus protocol version. */\n\ntypedef struct {\n    char sig[4];        /* Signature \"RCmb\" (Redis Cluster message bus). */\n    uint32_t totlen;    /* Total length of this message */\n    uint16_t ver;       /* Protocol version, currently set to 1. */\n    uint16_t port;      /* TCP base port number. */\n    uint16_t type;      /* Message type */\n    uint16_t count;     /* Only used for some kind of messages. */\n    uint64_t currentEpoch;  /* The epoch accordingly to the sending node. */\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    uint64_t offset;    /* Master replication offset if node is a master or\n                           processed replication offset if node is a slave. */\n    char sender[CLUSTER_NAMELEN]; /* Name of the sender node */\n    unsigned char myslots[CLUSTER_SLOTS/8];\n    char slaveof[CLUSTER_NAMELEN];\n    char myip[NET_IP_STR_LEN];    /* Sender IP, if not all zeroed. */\n    char notused1[34];  /* 34 bytes reserved for future usage. */\n    uint16_t cport;      /* Sender TCP cluster bus port */\n    uint16_t flags;      /* Sender node flags */\n    unsigned char state; /* Cluster state from the POV of the sender */\n    unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */\n    union clusterMsgData data;\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(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);\nint clusterRedirectBlockedClientIfNeeded(client *c);\nvoid clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);\n\n#endif /* __CLUSTER_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"cluster.h\"\n\n#include <fcntl.h>\n#include <sys/stat.h>\n\n/*-----------------------------------------------------------------------------\n * Config file name-value maps.\n *----------------------------------------------------------------------------*/\n\ntypedef struct configEnum {\n    const char *name;\n    const int val;\n} configEnum;\n\nconfigEnum maxmemory_policy_enum[] = {\n    {\"volatile-lru\", MAXMEMORY_VOLATILE_LRU},\n    {\"volatile-lfu\", MAXMEMORY_VOLATILE_LFU},\n    {\"volatile-random\",MAXMEMORY_VOLATILE_RANDOM},\n    {\"volatile-ttl\",MAXMEMORY_VOLATILE_TTL},\n    {\"allkeys-lru\",MAXMEMORY_ALLKEYS_LRU},\n    {\"allkeys-lfu\",MAXMEMORY_ALLKEYS_LFU},\n    {\"allkeys-random\",MAXMEMORY_ALLKEYS_RANDOM},\n    {\"noeviction\",MAXMEMORY_NO_EVICTION},\n    {NULL, 0}\n};\n\nconfigEnum syslog_facility_enum[] = {\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\nconfigEnum loglevel_enum[] = {\n    {\"debug\", LL_DEBUG},\n    {\"verbose\", LL_VERBOSE},\n    {\"notice\", LL_NOTICE},\n    {\"warning\", LL_WARNING},\n    {NULL,0}\n};\n\nconfigEnum supervised_mode_enum[] = {\n    {\"upstart\", SUPERVISED_UPSTART},\n    {\"systemd\", SUPERVISED_SYSTEMD},\n    {\"auto\", SUPERVISED_AUTODETECT},\n    {\"no\", SUPERVISED_NONE},\n    {NULL, 0}\n};\n\nconfigEnum aof_fsync_enum[] = {\n    {\"everysec\", AOF_FSYNC_EVERYSEC},\n    {\"always\", AOF_FSYNC_ALWAYS},\n    {\"no\", AOF_FSYNC_NO},\n    {NULL, 0}\n};\n\nconfigEnum repl_diskless_load_enum[] = {\n    {\"disabled\", REPL_DISKLESS_LOAD_DISABLED},\n    {\"on-empty-db\", REPL_DISKLESS_LOAD_WHEN_DB_EMPTY},\n    {\"swapdb\", REPL_DISKLESS_LOAD_SWAPDB},\n    {NULL, 0}\n};\n\n/* Output buffer limits presets. */\nclientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {\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/* Generic config infrastructure function pointers\n * int is_valid_fn(val, err)\n *     Return 1 when val is valid, and 0 when invalid.\n *     Optionally set err to a static error string.\n * int update_fn(val, prev, err)\n *     This function is called only for CONFIG SET command (not at config file parsing)\n *     It is called after the actual config is applied,\n *     Return 1 for success, and 0 for failure.\n *     Optionally set err to a static error string.\n *     On failure the config change will be reverted.\n */\n\n/* Configuration values that require no special handling to set, get, load or\n * rewrite. */\ntypedef struct boolConfigData {\n    int *config; /* The pointer to the server config this value is stored in */\n    const int default_value; /* The default value of the config on rewrite */\n    int (*is_valid_fn)(int val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(int val, int prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n} boolConfigData;\n\ntypedef struct stringConfigData {\n    char **config; /* Pointer to the server config this value is stored in. */\n    const char *default_value; /* Default value of the config on rewrite. */\n    int (*is_valid_fn)(char* val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(char* val, char* prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n    int convert_empty_to_null; /* Boolean indicating if empty strings should\n                                  be stored as a NULL value. */\n} stringConfigData;\n\ntypedef struct enumConfigData {\n    int *config; /* The pointer to the server config this value is stored in */\n    configEnum *enum_value; /* The underlying enum type this data represents */\n    const int default_value; /* The default value of the config on rewrite */\n    int (*is_valid_fn)(int val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(int val, int prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n} enumConfigData;\n\ntypedef enum numericType {\n    NUMERIC_TYPE_INT,\n    NUMERIC_TYPE_UINT,\n    NUMERIC_TYPE_LONG,\n    NUMERIC_TYPE_ULONG,\n    NUMERIC_TYPE_LONG_LONG,\n    NUMERIC_TYPE_ULONG_LONG,\n    NUMERIC_TYPE_SIZE_T,\n    NUMERIC_TYPE_SSIZE_T,\n    NUMERIC_TYPE_OFF_T,\n    NUMERIC_TYPE_TIME_T,\n} numericType;\n\ntypedef struct numericConfigData {\n    union {\n        int *i;\n        unsigned int *ui;\n        long *l;\n        unsigned long *ul;\n        long long *ll;\n        unsigned long long *ull;\n        size_t *st;\n        ssize_t *sst;\n        off_t *ot;\n        time_t *tt;\n    } config; /* The pointer to the numeric config this value is stored in */\n    int is_memory; /* Indicates if this value can be loaded as a memory value */\n    numericType numeric_type; /* An enum indicating the type of this value */\n    long long lower_bound; /* The lower bound of this numeric value */\n    long long upper_bound; /* The upper bound of this numeric value */\n    const long long default_value; /* The default value of the config on rewrite */\n    int (*is_valid_fn)(long long val, char **err); /* Optional function to check validity of new value (generic doc above) */\n    int (*update_fn)(long long val, long long prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */\n} numericConfigData;\n\ntypedef union typeData {\n    boolConfigData yesno;\n    stringConfigData string;\n    enumConfigData enumd;\n    numericConfigData numeric;\n} typeData;\n\ntypedef struct typeInterface {\n    /* Called on server start, to init the server with default value */\n    void (*init)(typeData data);\n    /* Called on server start, should return 1 on success, 0 on error and should set err */\n    int (*load)(typeData data, sds *argc, int argv, char **err);\n    /* Called on server startup and CONFIG SET, returns 1 on success, 0 on error\n     * and can set a verbose err string, update is true when called from CONFIG SET */\n    int (*set)(typeData data, sds value, int update, char **err);\n    /* Called on CONFIG GET, required to add output to the client */\n    void (*get)(client *c, typeData data);\n    /* Called on CONFIG REWRITE, required to rewrite the config state */\n    void (*rewrite)(typeData data, const char *name, struct rewriteConfigState *state);\n} typeInterface;\n\ntypedef struct standardConfig {\n    const char *name; /* The user visible name of this config */\n    const char *alias; /* An alias that can also be used for this config */\n    const int modifiable; /* Can this value be updated by CONFIG SET? */\n    typeInterface interface; /* The function pointers that define the type interface */\n    typeData data; /* The type specific data exposed used by the interface */\n} standardConfig;\n\nstandardConfig configs[];\n\n/*-----------------------------------------------------------------------------\n * Enum access functions\n *----------------------------------------------------------------------------*/\n\n/* Get enum value from name. If there is no match INT_MIN is returned. */\nint configEnumGetValue(configEnum *ce, char *name) {\n    while(ce->name != NULL) {\n        if (!strcasecmp(ce->name,name)) return ce->val;\n        ce++;\n    }\n    return INT_MIN;\n}\n\n/* Get enum name from value. If no match is found NULL is returned. */\nconst char *configEnumGetName(configEnum *ce, int val) {\n    while(ce->name != NULL) {\n        if (ce->val == val) return ce->name;\n        ce++;\n    }\n    return NULL;\n}\n\n/* Wrapper for configEnumGetName() returning \"unknown\" instead of NULL if\n * there is no match. */\nconst char *configEnumGetNameOrUnknown(configEnum *ce, int val) {\n    const char *name = configEnumGetName(ce,val);\n    return name ? name : \"unknown\";\n}\n\n/* Used for INFO generation. */\nconst char *evictPolicyToString(void) {\n    return configEnumGetNameOrUnknown(maxmemory_policy_enum,server.maxmemory_policy);\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(void) {\n    zfree(server.saveparams);\n    server.saveparams = NULL;\n    server.saveparamslen = 0;\n}\n\nvoid queueLoadModule(sds path, sds *argv, int argc) {\n    int i;\n    struct moduleLoadQueueEntry *loadmod;\n\n    loadmod = zmalloc(sizeof(struct moduleLoadQueueEntry));\n    loadmod->argv = zmalloc(sizeof(robj*)*argc);\n    loadmod->path = sdsnew(path);\n    loadmod->argc = argc;\n    for (i = 0; i < argc; i++) {\n        loadmod->argv[i] = createRawStringObject(argv[i],sdslen(argv[i]));\n    }\n    listAddNodeTail(server.loadmodule_queue,loadmod);\n}\n\nvoid initConfigValues() {\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        config->interface.init(config->data);\n    }\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        lines[i] = sdstrim(lines[i],\" \\t\\r\\n\");\n\n        /* Skip comments and blank lines */\n        if (lines[i][0] == '#' || lines[i][0] == '\\0') continue;\n\n        /* Split into arguments */\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        if (argc == 0) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n        sdstolower(argv[0]);\n\n        /* Iterate the configs that are standard */\n        int match = 0;\n        for (standardConfig *config = configs; config->name != NULL; config++) {\n            if ((!strcasecmp(argv[0],config->name) ||\n                (config->alias && !strcasecmp(argv[0],config->alias))))\n            {\n                if (argc != 2) {\n                    err = \"wrong number of arguments\";\n                    goto loaderr;\n                }\n                if (!config->interface.set(config->data, argv[1], 0, &err)) {\n                    goto loaderr;\n                }\n\n                match = 1;\n                break;\n            }\n        }\n\n        if (match) {\n            sdsfreesplitres(argv,argc);\n            continue;\n        }\n\n        /* Execute config directives */\n        if (!strcasecmp(argv[0],\"bind\") && argc >= 2) {\n            int j, addresses = argc-1;\n\n            if (addresses > CONFIG_BINDADDR_MAX) {\n                err = \"Too many bind addresses specified\"; goto loaderr;\n            }\n            /* Free old bind addresses */\n            for (j = 0; j < server.bindaddr_count; j++) {\n                zfree(server.bindaddr[j]);\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],\"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                serverLog(LL_WARNING,\"Can't chdir to '%s': %s\",\n                    argv[1], strerror(errno));\n                exit(1);\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],\"include\") && argc == 2) {\n            loadServerConfig(argv[1],NULL);\n        } else if ((!strcasecmp(argv[0],\"client-query-buffer-limit\")) && argc == 2) {\n             server.client_max_querybuf_len = memtoll(argv[1],NULL);\n        } else if ((!strcasecmp(argv[0],\"slaveof\") ||\n                    !strcasecmp(argv[0],\"replicaof\")) && argc == 3) {\n            slaveof_linenum = linenum;\n            server.masterhost = sdsnew(argv[1]);\n            server.masterport = atoi(argv[2]);\n            server.repl_state = REPL_STATE_CONNECT;\n        } else if (!strcasecmp(argv[0],\"requirepass\") && argc == 2) {\n            if (strlen(argv[1]) > CONFIG_AUTHPASS_MAX_LEN) {\n                err = \"Password is longer than CONFIG_AUTHPASS_MAX_LEN\";\n                goto loaderr;\n            }\n            /* The old \"requirepass\" directive just translates to setting\n             * a password to the default user. The only thing we do\n             * additionally is to remember the cleartext password in this\n             * case, for backward compatibility with Redis <= 5. */\n            ACLSetUser(DefaultUser,\"resetpass\",-1);\n            sds aclop = sdscatprintf(sdsempty(),\">%s\",argv[1]);\n            ACLSetUser(DefaultUser,aclop,sdslen(aclop));\n            sdsfree(aclop);\n            sdsfree(server.requirepass);\n            server.requirepass = sdsnew(argv[1]);\n        } else if (!strcasecmp(argv[0],\"list-max-ziplist-entries\") && argc == 2){\n            /* DEAD OPTION */\n        } else if (!strcasecmp(argv[0],\"list-max-ziplist-value\") && argc == 2) {\n            /* DEAD OPTION */\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            serverAssert(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-config-file\") && argc == 2) {\n            zfree(server.cluster_configfile);\n            server.cluster_configfile = zstrdup(argv[1]);\n        } else if (!strcasecmp(argv[0],\"client-output-buffer-limit\") &&\n                   argc == 5)\n        {\n            int class = getClientTypeByName(argv[1]);\n            unsigned long long hard, soft;\n            int soft_seconds;\n\n            if (class == -1 || class == CLIENT_TYPE_MASTER) {\n                err = \"Unrecognized client limit class: the user specified \"\n                \"an invalid one, or 'master' which has no buffer limits.\";\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],\"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],\"user\") && argc >= 2) {\n            int argc_err;\n            if (ACLAppendUserForLoading(argv,argc,&argc_err) == C_ERR) {\n                char buf[1024];\n                char *errmsg = ACLSetUserStringError();\n                snprintf(buf,sizeof(buf),\"Error in user declaration '%s': %s\",\n                    argv[argc_err],errmsg);\n                err = buf;\n                goto loaderr;\n            }\n        } else if (!strcasecmp(argv[0],\"loadmodule\") && argc >= 2) {\n            queueLoadModule(argv[1],&argv[2],argc-2);\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            if (argc != 1) {\n                if (!server.sentinel_mode) {\n                    err = \"sentinel directive while not in sentinel mode\";\n                    goto loaderr;\n                }\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 = \"replicaof 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 (Redis %s) ***\\n\",\n        REDIS_VERSION);\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 * The function appends the additional configuration directives stored\n * in the 'options' string to the config file before loading.\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. */\nvoid loadServerConfig(char *filename, char *options) {\n    sds config = sdsempty();\n    char buf[CONFIG_MAX_LINE+1];\n\n    /* Load the file content */\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                serverLog(LL_WARNING,\n                    \"Fatal error, can't open config file '%s'\", filename);\n                exit(1);\n            }\n        }\n        while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL)\n            config = sdscat(config,buf);\n        if (fp != stdin) fclose(fp);\n    }\n    /* Append the additional options */\n    if (options) {\n        config = sdscat(config,\"\\n\");\n        config = sdscat(config,options);\n    }\n    loadServerConfigFromString(config);\n    sdsfree(config);\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG SET implementation\n *----------------------------------------------------------------------------*/\n\n#define config_set_bool_field(_name,_var) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \\\n        int yn = yesnotoi(o->ptr); \\\n        if (yn == -1) goto badfmt; \\\n        _var = yn;\n\n#define config_set_numerical_field(_name,_var,min,max) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \\\n        if (getLongLongFromObject(o,&ll) == C_ERR) goto badfmt; \\\n        if (min != LLONG_MIN && ll < min) goto badfmt; \\\n        if (max != LLONG_MAX && ll > max) goto badfmt; \\\n        _var = ll;\n\n#define config_set_memory_field(_name,_var) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \\\n        ll = memtoll(o->ptr,&err); \\\n        if (err || ll < 0) goto badfmt; \\\n        _var = ll;\n\n#define config_set_special_field(_name) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name)) {\n\n#define config_set_special_field_with_alias(_name1,_name2) \\\n    } else if (!strcasecmp(c->argv[2]->ptr,_name1) || \\\n               !strcasecmp(c->argv[2]->ptr,_name2)) {\n\n#define config_set_else } else\n\nvoid configSetCommand(client *c) {\n    robj *o;\n    long long ll;\n    int err;\n    char *errstr = NULL;\n    serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));\n    serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));\n    o = c->argv[3];\n\n    /* Iterate the configs that are standard */\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        if(config->modifiable && (!strcasecmp(c->argv[2]->ptr,config->name) ||\n            (config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))\n        {\n            if (!config->interface.set(config->data,o->ptr,1,&errstr)) {\n                goto badfmt;\n            }\n            addReply(c,shared.ok);\n            return;\n        }\n    }\n\n    if (0) { /* this starts the config_set macros else-if chain. */\n\n    /* Special fields that can't be handled with general macros. */\n    config_set_special_field(\"requirepass\") {\n        if (sdslen(o->ptr) > CONFIG_AUTHPASS_MAX_LEN) goto badfmt;\n        /* The old \"requirepass\" directive just translates to setting\n         * a password to the default user. The only thing we do\n         * additionally is to remember the cleartext password in this\n         * case, for backward compatibility with Redis <= 5. */\n        ACLSetUser(DefaultUser,\"resetpass\",-1);\n        sds aclop = sdscatprintf(sdsempty(),\">%s\",(char*)o->ptr);\n        ACLSetUser(DefaultUser,aclop,sdslen(aclop));\n        sdsfree(aclop);\n        sdsfree(server.requirepass);\n        server.requirepass = sdsnew(o->ptr);\n    } config_set_special_field(\"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    } config_set_special_field(\"dir\") {\n        if (chdir((char*)o->ptr) == -1) {\n            addReplyErrorFormat(c,\"Changing directory: %s\", strerror(errno));\n            return;\n        }\n    } config_set_special_field(\"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            long val;\n\n            if ((j % 4) == 0) {\n                int class = getClientTypeByName(v[j]);\n                if (class == -1 || class == CLIENT_TYPE_MASTER) {\n                    sdsfreesplitres(v,vlen);\n                    goto badfmt;\n                }\n            } else {\n                val = memtoll(v[j], &err);\n                if (err || 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 = getClientTypeByName(v[j]);\n            hard = memtoll(v[j+1],NULL);\n            soft = memtoll(v[j+2],NULL);\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    } config_set_special_field(\"notify-keyspace-events\") {\n        int flags = keyspaceEventsStringToFlags(o->ptr);\n\n        if (flags == -1) goto badfmt;\n        server.notify_keyspace_events = flags;\n    /* Numerical fields.\n     * config_set_numerical_field(name,var,min,max) */\n    } config_set_numerical_field(\n      \"watchdog-period\",ll,0,INT_MAX) {\n        if (ll)\n            enableWatchdog(ll);\n        else\n            disableWatchdog();\n    /* Memory fields.\n     * config_set_memory_field(name,var) */\n    } config_set_memory_field(\n      \"client-query-buffer-limit\",server.client_max_querybuf_len) {\n    /* Everything else is an error... */\n    } config_set_else {\n        addReplyErrorFormat(c,\"Unsupported CONFIG parameter: %s\",\n            (char*)c->argv[2]->ptr);\n        return;\n    }\n\n    /* On success we just return a generic OK for all the options. */\n    addReply(c,shared.ok);\n    return;\n\nbadfmt: /* Bad format errors */\n    if (errstr) {\n        addReplyErrorFormat(c,\"Invalid argument '%s' for CONFIG SET '%s' - %s\",\n                (char*)o->ptr,\n                (char*)c->argv[2]->ptr,\n                errstr);\n    } else {\n        addReplyErrorFormat(c,\"Invalid argument '%s' for CONFIG SET '%s'\",\n                (char*)o->ptr,\n                (char*)c->argv[2]->ptr);\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * CONFIG GET implementation\n *----------------------------------------------------------------------------*/\n\n#define config_get_string_field(_name,_var) do { \\\n    if (stringmatch(pattern,_name,1)) { \\\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,1)) { \\\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,1)) { \\\n        ll2string(buf,sizeof(buf),_var); \\\n        addReplyBulkCString(c,_name); \\\n        addReplyBulkCString(c,buf); \\\n        matches++; \\\n    } \\\n} while(0);\n\n\nvoid configGetCommand(client *c) {\n    robj *o = c->argv[2];\n    void *replylen = addReplyDeferredLen(c);\n    char *pattern = o->ptr;\n    char buf[128];\n    int matches = 0;\n    serverAssertWithInfo(c,o,sdsEncodedObject(o));\n\n    /* Iterate the configs that are standard */\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        if (stringmatch(pattern,config->name,1)) {\n            addReplyBulkCString(c,config->name);\n            config->interface.get(c,config->data);\n            matches++;\n        }\n        if (config->alias && stringmatch(pattern,config->alias,1)) {\n            addReplyBulkCString(c,config->alias);\n            config->interface.get(c,config->data);\n            matches++;\n        }\n    }\n\n    /* String values */\n    config_get_string_field(\"logfile\",server.logfile);\n\n    /* Numerical values */\n    config_get_numerical_field(\"client-query-buffer-limit\",server.client_max_querybuf_len);\n    config_get_numerical_field(\"watchdog-period\",server.watchdog_period);\n\n    /* Everything we can't handle with macros follows. */\n\n    if (stringmatch(pattern,\"dir\",1)) {\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,\"save\",1)) {\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,\"client-output-buffer-limit\",1)) {\n        sds buf = sdsempty();\n        int j;\n\n        for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {\n            buf = sdscatprintf(buf,\"%s %llu %llu %ld\",\n                    getClientTypeName(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 != CLIENT_TYPE_OBUF_COUNT-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\",1)) {\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\",1) ||\n        stringmatch(pattern,\"replicaof\",1))\n    {\n        char *optname = stringmatch(pattern,\"slaveof\",1) ?\n                        \"slaveof\" : \"replicaof\";\n        char buf[256];\n\n        addReplyBulkCString(c,optname);\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\",1)) {\n        sds flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);\n\n        addReplyBulkCString(c,\"notify-keyspace-events\");\n        addReplyBulkSds(c,flags);\n        matches++;\n    }\n    if (stringmatch(pattern,\"bind\",1)) {\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    if (stringmatch(pattern,\"requirepass\",1)) {\n        addReplyBulkCString(c,\"requirepass\");\n        sds password = server.requirepass;\n        if (password) {\n            addReplyBulkCBuffer(c,password,sdslen(password));\n        } else {\n            addReplyBulkCString(c,\"\");\n        }\n        matches++;\n    }\n\n    setDeferredMapLen(c,replylen,matches);\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). */\nuint64_t 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, const 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    if (fp == NULL && errno != ENOENT) return NULL;\n\n    char buf[CONFIG_MAX_LINE+1];\n    int linenum = -1;\n    struct rewriteConfigState *state = zmalloc(sizeof(*state));\n    state->option_to_line = dictCreate(&optionToLineDictType,NULL);\n    state->rewritten = dictCreate(&optionSetDictType,NULL);\n    state->numlines = 0;\n    state->lines = NULL;\n    state->has_tail = 0;\n    if (fp == NULL) return state;\n\n    /* Read the old file line by line, populate the state. */\n    while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) {\n        int argc;\n        sds *argv;\n        sds line = sdstrim(sdsnew(buf),\"\\r\\n\\t \");\n\n        linenum++; /* Zero based, so we init at -1 */\n\n        /* Handle comments and empty lines. */\n        if (line[0] == '#' || line[0] == '\\0') {\n            if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))\n                state->has_tail = 1;\n            rewriteConfigAppendLine(state,line);\n            continue;\n        }\n\n        /* Not a comment, split into arguments. */\n        argv = sdssplitargs(line,&argc);\n        if (argv == NULL) {\n            /* Apparently the line is unparsable for some reason, for\n             * instance it may have unbalanced quotes. Load it as a\n             * comment. */\n            sds aux = sdsnew(\"# ??? \");\n            aux = sdscatsds(aux,line);\n            sdsfree(line);\n            rewriteConfigAppendLine(state,aux);\n            continue;\n        }\n\n        sdstolower(argv[0]); /* We only want lowercase config directives. */\n\n        /* Now we populate the state according to the content of this line.\n         * Append the line and populate the option -> line numbers map. */\n        rewriteConfigAppendLine(state,line);\n\n        /* Translate options using the word \"slave\" to the corresponding name\n         * \"replica\", before adding such option to the config name -> lines\n         * mapping. */\n        char *p = strstr(argv[0],\"slave\");\n        if (p) {\n            sds alt = sdsempty();\n            alt = sdscatlen(alt,argv[0],p-argv[0]);;\n            alt = sdscatlen(alt,\"replica\",7);\n            alt = sdscatlen(alt,p+5,strlen(p+5));\n            sdsfree(argv[0]);\n            argv[0] = alt;\n        }\n        rewriteConfigAddLineNumberToOption(state,argv[0],linenum);\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, const 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, const 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, const 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, const char *option, char *value, const 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    /* Set force to zero if the value is set to its default. */\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, const 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. It takes as usually state and option name,\n * and in addition the enumeration array and the default value for the\n * option. */\nvoid rewriteConfigEnumOption(struct rewriteConfigState *state, const char *option, int value, configEnum *ce, int defval) {\n    sds line;\n    const char *name = configEnumGetNameOrUnknown(ce,value);\n    int force = value != defval;\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 user option. */\nvoid rewriteConfigUserOption(struct rewriteConfigState *state) {\n    /* If there is a user file defined we just mark this configuration\n     * directive as processed, so that all the lines containing users\n     * inside the config file gets discarded. */\n    if (server.acl_filename[0] != '\\0') {\n        rewriteConfigMarkAsProcessed(state,\"user\");\n        return;\n    }\n\n    /* Otherwise scan the list of users and rewrite every line. Note that\n     * in case the list here is empty, the effect will just be to comment\n     * all the users directive inside the config file. */\n    raxIterator ri;\n    raxStart(&ri,Users);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        user *u = ri.data;\n        sds line = sdsnew(\"user \");\n        line = sdscatsds(line,u->name);\n        line = sdscatlen(line,\" \",1);\n        sds descr = ACLDescribeUser(u);\n        line = sdscatsds(line,descr);\n        sdsfree(descr);\n        rewriteConfigRewriteLine(state,\"user\",line,1);\n    }\n    raxStop(&ri);\n\n    /* Mark \"user\" as processed in case there are no defined users. */\n    rewriteConfigMarkAsProcessed(state,\"user\");\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, char *option) {\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,option);\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 < CLIENT_TYPE_OBUF_COUNT; 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        char *typename = getClientTypeName(j);\n        if (!strcmp(typename,\"slave\")) typename = \"replica\";\n        line = sdscatprintf(sdsempty(),\"%s %s %s %s %ld\",\n                option, typename, 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/* Rewrite the requirepass option. */\nvoid rewriteConfigRequirepassOption(struct rewriteConfigState *state, char *option) {\n    int force = 1;\n    sds line;\n    sds password = server.requirepass;\n\n    /* If there is no password set, we don't want the requirepass option\n     * to be present in the configuration at all. */\n    if (password == NULL) {\n        rewriteConfigMarkAsProcessed(state,option);\n        return;\n    }\n\n    line = sdsnew(option);\n    line = sdscatlen(line, \" \", 1);\n    line = sdscatsds(line, password);\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            serverLog(LL_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    /* Iterate the configs that are standard */\n    for (standardConfig *config = configs; config->name != NULL; config++) {\n        config->interface.rewrite(config->data, config->name, state);\n    }\n\n    rewriteConfigBindOption(state);\n    rewriteConfigOctalOption(state,\"unixsocketperm\",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);\n    rewriteConfigStringOption(state,\"logfile\",server.logfile,CONFIG_DEFAULT_LOGFILE);\n    rewriteConfigSaveOption(state);\n    rewriteConfigUserOption(state);\n    rewriteConfigDirOption(state);\n    rewriteConfigSlaveofOption(state,\"replicaof\");\n    rewriteConfigRequirepassOption(state,\"requirepass\");\n    rewriteConfigBytesOption(state,\"client-query-buffer-limit\",server.client_max_querybuf_len,PROTO_MAX_QUERYBUF_LEN);\n    rewriteConfigStringOption(state,\"cluster-config-file\",server.cluster_configfile,CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);\n    rewriteConfigNotifykeyspaceeventsOption(state);\n    rewriteConfigClientoutputbufferlimitOption(state);\n\n    /* Rewrite Sentinel config if in Sentinel mode. */\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 * Configs that fit one of the major types and require no special handling\n *----------------------------------------------------------------------------*/\n#define LOADBUF_SIZE 256\nstatic char loadbuf[LOADBUF_SIZE];\n\n#define MODIFIABLE_CONFIG 1\n#define IMMUTABLE_CONFIG 0\n\n#define embedCommonConfig(config_name, config_alias, is_modifiable) \\\n    .name = (config_name), \\\n    .alias = (config_alias), \\\n    .modifiable = (is_modifiable),\n\n#define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \\\n    .init = (initfn), \\\n    .set = (setfn), \\\n    .get = (getfn), \\\n    .rewrite = (rewritefn) \\\n},\n\n/* What follows is the generic config types that are supported. To add a new\n * config with one of these types, add it to the standardConfig table with\n * the creation macro for each type.\n *\n * Each type contains the following:\n * * A function defining how to load this type on startup.\n * * A function defining how to update this type on CONFIG SET.\n * * A function defining how to serialize this type on CONFIG SET.\n * * A function defining how to rewrite this type on CONFIG REWRITE.\n * * A Macro defining how to create this type.\n */\n\n/* Bool Configs */\nstatic void boolConfigInit(typeData data) {\n    *data.yesno.config = data.yesno.default_value;\n}\n\nstatic int boolConfigSet(typeData data, sds value, int update, char **err) {\n    int yn = yesnotoi(value);\n    if (yn == -1) {\n        *err = \"argument must be 'yes' or 'no'\";\n        return 0;\n    }\n    if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))\n        return 0;\n    int prev = *(data.yesno.config);\n    *(data.yesno.config) = yn;\n    if (update && data.yesno.update_fn && !data.yesno.update_fn(yn, prev, err)) {\n        *(data.yesno.config) = prev;\n        return 0;\n    }\n    return 1;\n}\n\nstatic void boolConfigGet(client *c, typeData data) {\n    addReplyBulkCString(c, *data.yesno.config ? \"yes\" : \"no\");\n}\n\nstatic void boolConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value);\n}\n\n#define createBoolConfig(name, alias, modifiable, config_addr, default, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \\\n    .data.yesno = { \\\n        .config = &(config_addr), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n    } \\\n}\n\n/* String Configs */\nstatic void stringConfigInit(typeData data) {\n    if (data.string.convert_empty_to_null) {\n        *data.string.config = data.string.default_value ? zstrdup(data.string.default_value) : NULL;\n    } else {\n        *data.string.config = zstrdup(data.string.default_value);\n    }\n}\n\nstatic int stringConfigSet(typeData data, sds value, int update, char **err) {\n    if (data.string.is_valid_fn && !data.string.is_valid_fn(value, err))\n        return 0;\n    char *prev = *data.string.config;\n    if (data.string.convert_empty_to_null) {\n        *data.string.config = value[0] ? zstrdup(value) : NULL;\n    } else {\n        *data.string.config = zstrdup(value);\n    }\n    if (update && data.string.update_fn && !data.string.update_fn(*data.string.config, prev, err)) {\n        zfree(*data.string.config);\n        *data.string.config = prev;\n        return 0;\n    }\n    zfree(prev);\n    return 1;\n}\n\nstatic void stringConfigGet(client *c, typeData data) {\n    addReplyBulkCString(c, *data.string.config ? *data.string.config : \"\");\n}\n\nstatic void stringConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    rewriteConfigStringOption(state, name,*(data.string.config), data.string.default_value);\n}\n\n#define ALLOW_EMPTY_STRING 0\n#define EMPTY_STRING_IS_NULL 1\n\n#define createStringConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \\\n    .data.string = { \\\n        .config = &(config_addr), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n        .convert_empty_to_null = (empty_to_null), \\\n    } \\\n}\n\n/* Enum configs */\nstatic void enumConfigInit(typeData data) {\n    *data.enumd.config = data.enumd.default_value;\n}\n\nstatic int enumConfigSet(typeData data, sds value, int update, char **err) {\n    int enumval = configEnumGetValue(data.enumd.enum_value, value);\n    if (enumval == INT_MIN) {\n        sds enumerr = sdsnew(\"argument must be one of the following: \");\n        configEnum *enumNode = data.enumd.enum_value;\n        while(enumNode->name != NULL) {\n            enumerr = sdscatlen(enumerr, enumNode->name,\n                                strlen(enumNode->name));\n            enumerr = sdscatlen(enumerr, \", \", 2);\n            enumNode++;\n        }\n        sdsrange(enumerr,0,-3); /* Remove final \", \". */\n\n        strncpy(loadbuf, enumerr, LOADBUF_SIZE);\n        loadbuf[LOADBUF_SIZE - 1] = '\\0';\n\n        sdsfree(enumerr);\n        *err = loadbuf;\n        return 0;\n    }\n    if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))\n        return 0;\n    int prev = *(data.enumd.config);\n    *(data.enumd.config) = enumval;\n    if (update && data.enumd.update_fn && !data.enumd.update_fn(enumval, prev, err)) {\n        *(data.enumd.config) = prev;\n        return 0;\n    }\n    return 1;\n}\n\nstatic void enumConfigGet(client *c, typeData data) {\n    addReplyBulkCString(c, configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));\n}\n\nstatic void enumConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value);\n}\n\n#define createEnumConfig(name, alias, modifiable, enum, config_addr, default, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \\\n    .data.enumd = { \\\n        .config = &(config_addr), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n        .enum_value = (enum), \\\n    } \\\n}\n\n/* Gets a 'long long val' and sets it into the union, using a macro to get\n * compile time type check. */\n#define SET_NUMERIC_TYPE(val) \\\n    if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \\\n        *(data.numeric.config.i) = (int) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \\\n        *(data.numeric.config.ui) = (unsigned int) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \\\n        *(data.numeric.config.l) = (long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \\\n        *(data.numeric.config.ul) = (unsigned long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \\\n        *(data.numeric.config.ll) = (long long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \\\n        *(data.numeric.config.ull) = (unsigned long long) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \\\n        *(data.numeric.config.st) = (size_t) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \\\n        *(data.numeric.config.sst) = (ssize_t) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \\\n        *(data.numeric.config.ot) = (off_t) val; \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \\\n        *(data.numeric.config.tt) = (time_t) val; \\\n    }\n\n/* Gets a 'long long val' and sets it with the value from the union, using a\n * macro to get compile time type check. */\n#define GET_NUMERIC_TYPE(val) \\\n    if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \\\n        val = *(data.numeric.config.i); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \\\n        val = *(data.numeric.config.ui); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \\\n        val = *(data.numeric.config.l); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \\\n        val = *(data.numeric.config.ul); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \\\n        val = *(data.numeric.config.ll); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \\\n        val = *(data.numeric.config.ull); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \\\n        val = *(data.numeric.config.st); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \\\n        val = *(data.numeric.config.sst); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \\\n        val = *(data.numeric.config.ot); \\\n    } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \\\n        val = *(data.numeric.config.tt); \\\n    }\n\n/* Numeric configs */\nstatic void numericConfigInit(typeData data) {\n    SET_NUMERIC_TYPE(data.numeric.default_value)\n}\n\nstatic int numericBoundaryCheck(typeData data, long long ll, char **err) {\n    if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG ||\n        data.numeric.numeric_type == NUMERIC_TYPE_UINT ||\n        data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {\n        /* Boundary check for unsigned types */\n        unsigned long long ull = ll;\n        unsigned long long upper_bound = data.numeric.upper_bound;\n        unsigned long long lower_bound = data.numeric.lower_bound;\n        if (ull > upper_bound || ull < lower_bound) {\n            snprintf(loadbuf, LOADBUF_SIZE,\n                \"argument must be between %llu and %llu inclusive\",\n                lower_bound,\n                upper_bound);\n            *err = loadbuf;\n            return 0;\n        }\n    } else {\n        /* Boundary check for signed types */\n        if (ll > data.numeric.upper_bound || ll < data.numeric.lower_bound) {\n            snprintf(loadbuf, LOADBUF_SIZE,\n                \"argument must be between %lld and %lld inclusive\",\n                data.numeric.lower_bound,\n                data.numeric.upper_bound);\n            *err = loadbuf;\n            return 0;\n        }\n    }\n    return 1;\n}\n\nstatic int numericConfigSet(typeData data, sds value, int update, char **err) {\n    long long ll, prev = 0;\n    if (data.numeric.is_memory) {\n        int memerr;\n        ll = memtoll(value, &memerr);\n        if (memerr || ll < 0) {\n            *err = \"argument must be a memory value\";\n            return 0;\n        }\n    } else {\n        if (!string2ll(value, sdslen(value),&ll)) {\n            *err = \"argument couldn't be parsed into an integer\" ;\n            return 0;\n        }\n    }\n\n    if (!numericBoundaryCheck(data, ll, err))\n        return 0;\n\n    if (data.numeric.is_valid_fn && !data.numeric.is_valid_fn(ll, err))\n        return 0;\n\n    GET_NUMERIC_TYPE(prev)\n    SET_NUMERIC_TYPE(ll)\n\n    if (update && data.numeric.update_fn && !data.numeric.update_fn(ll, prev, err)) {\n        SET_NUMERIC_TYPE(prev)\n        return 0;\n    }\n    return 1;\n}\n\nstatic void numericConfigGet(client *c, typeData data) {\n    char buf[128];\n    long long value = 0;\n\n    GET_NUMERIC_TYPE(value)\n\n    ll2string(buf, sizeof(buf), value);\n    addReplyBulkCString(c, buf);\n}\n\nstatic void numericConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {\n    long long value = 0;\n\n    GET_NUMERIC_TYPE(value)\n\n    if (data.numeric.is_memory) {\n        rewriteConfigBytesOption(state, name, value, data.numeric.default_value);\n    } else {\n        rewriteConfigNumericalOption(state, name, value, data.numeric.default_value);\n    }\n}\n\n#define INTEGER_CONFIG 0\n#define MEMORY_CONFIG 1\n\n#define embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) { \\\n    embedCommonConfig(name, alias, modifiable) \\\n    embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite) \\\n    .data.numeric = { \\\n        .lower_bound = (lower), \\\n        .upper_bound = (upper), \\\n        .default_value = (default), \\\n        .is_valid_fn = (is_valid), \\\n        .update_fn = (update), \\\n        .is_memory = (memory),\n\n#define createIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_INT, \\\n        .config.i = &(config_addr) \\\n    } \\\n}\n\n#define createUIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_UINT, \\\n        .config.ui = &(config_addr) \\\n    } \\\n}\n\n#define createLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_LONG, \\\n        .config.l = &(config_addr) \\\n    } \\\n}\n\n#define createULongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_ULONG, \\\n        .config.ul = &(config_addr) \\\n    } \\\n}\n\n#define createLongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_LONG_LONG, \\\n        .config.ll = &(config_addr) \\\n    } \\\n}\n\n#define createULongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_ULONG_LONG, \\\n        .config.ull = &(config_addr) \\\n    } \\\n}\n\n#define createSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_SIZE_T, \\\n        .config.st = &(config_addr) \\\n    } \\\n}\n\n#define createSSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_SSIZE_T, \\\n        .config.sst = &(config_addr) \\\n    } \\\n}\n\n#define createTimeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_TIME_T, \\\n        .config.tt = &(config_addr) \\\n    } \\\n}\n\n#define createOffTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n    embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \\\n        .numeric_type = NUMERIC_TYPE_OFF_T, \\\n        .config.ot = &(config_addr) \\\n    } \\\n}\n\nstatic int isValidActiveDefrag(int val, char **err) {\n#ifndef HAVE_DEFRAG\n    if (val) {\n        *err = \"Active defragmentation cannot be enabled: it \"\n               \"requires a Redis server compiled with a modified Jemalloc \"\n               \"like the one shipped by default with the Redis source \"\n               \"distribution\";\n        return 0;\n    }\n#else\n    UNUSED(val);\n    UNUSED(err);\n#endif\n    return 1;\n}\n\nstatic int isValidDBfilename(char *val, char **err) {\n    if (!pathIsBaseName(val)) {\n        *err = \"dbfilename can't be a path, just a filename\";\n        return 0;\n    }\n    return 1;\n}\n\nstatic int isValidAOFfilename(char *val, char **err) {\n    if (!pathIsBaseName(val)) {\n        *err = \"appendfilename can't be a path, just a filename\";\n        return 0;\n    }\n    return 1;\n}\n\nstatic int updateHZ(long long val, long long prev, char **err) {\n    UNUSED(prev);\n    UNUSED(err);\n    /* Hz is more an hint from the user, so we accept values out of range\n     * but cap them to reasonable values. */\n    server.config_hz = val;\n    if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;\n    if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;\n    server.hz = server.config_hz;\n    return 1;\n}\n\nstatic int updateJemallocBgThread(int val, int prev, char **err) {\n    UNUSED(prev);\n    UNUSED(err);\n    set_jemalloc_bg_thread(val);\n    return 1;\n}\n\nstatic int updateReplBacklogSize(long long val, long long prev, char **err) {\n    /* resizeReplicationBacklog sets server.repl_backlog_size, and relies on\n     * being able to tell when the size changes, so restore prev becore calling it. */\n    UNUSED(err);\n    server.repl_backlog_size = prev;\n    resizeReplicationBacklog(val);\n    return 1;\n}\n\nstatic int updateMaxmemory(long long val, long long prev, char **err) {\n    UNUSED(prev);\n    UNUSED(err);\n    if (val) {\n        size_t used = zmalloc_used_memory()-freeMemoryGetNotCountedMemory();\n        if ((unsigned long long)val < used) {\n            serverLog(LL_WARNING,\"WARNING: the new maxmemory value set via CONFIG SET (%llu) is smaller than the current memory usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.\", server.maxmemory, used);\n        }\n        freeMemoryIfNeededAndSafe();\n    }\n    return 1;\n}\n\nstatic int updateGoodSlaves(long long val, long long prev, char **err) {\n    UNUSED(val);\n    UNUSED(prev);\n    UNUSED(err);\n    refreshGoodSlavesCount();\n    return 1;\n}\n\nstatic int updateAppendonly(int val, int prev, char **err) {\n    UNUSED(prev);\n    if (val == 0 && server.aof_state != AOF_OFF) {\n        stopAppendOnly();\n    } else if (val && server.aof_state == AOF_OFF) {\n        if (startAppendOnly() == C_ERR) {\n            *err = \"Unable to turn on AOF. Check server logs.\";\n            return 0;\n        }\n    }\n    return 1;\n}\n\nstatic int updateMaxclients(long long val, long long prev, char **err) {\n    /* Try to check if the OS is capable of supporting so many FDs. */\n    if (val > prev) {\n        adjustOpenFilesLimit();\n        if (server.maxclients != val) {\n            static char msg[128];\n            sprintf(msg, \"The operating system is not able to handle the specified number of clients, try with %d\", server.maxclients);\n            *err = msg;\n            if (server.maxclients > prev) {\n                server.maxclients = prev;\n                adjustOpenFilesLimit();\n            }\n            return 0;\n        }\n        if ((unsigned int) aeGetSetSize(server.el) <\n            server.maxclients + CONFIG_FDSET_INCR)\n        {\n            if (aeResizeSetSize(server.el,\n                server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)\n            {\n                *err = \"The event loop API used by Redis is not able to handle the specified number of clients\";\n                return 0;\n            }\n        }\n    }\n    return 1;\n}\n\n#ifdef USE_OPENSSL\nstatic int updateTlsCfg(char *val, char *prev, char **err) {\n    UNUSED(val);\n    UNUSED(prev);\n    UNUSED(err);\n    if (tlsConfigure(&server.tls_ctx_config) == C_ERR) {\n        *err = \"Unable to configure tls-cert-file. Check server logs.\";\n        return 0;\n    }\n    return 1;\n}\nstatic int updateTlsCfgBool(int val, int prev, char **err) {\n    UNUSED(val);\n    UNUSED(prev);\n    return updateTlsCfg(NULL, NULL, err);\n}\n#endif  /* USE_OPENSSL */\n\nstandardConfig configs[] = {\n    /* Bool configs */\n    createBoolConfig(\"rdbchecksum\", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL),\n    createBoolConfig(\"daemonize\", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL),\n    createBoolConfig(\"io-threads-do-reads\", NULL, IMMUTABLE_CONFIG, server.io_threads_do_reads, 0,NULL, NULL), /* Read + parse from threads? */\n    createBoolConfig(\"lua-replicate-commands\", NULL, MODIFIABLE_CONFIG, server.lua_always_replicate_commands, 1, NULL, NULL),\n    createBoolConfig(\"always-show-logo\", NULL, IMMUTABLE_CONFIG, server.always_show_logo, 0, NULL, NULL),\n    createBoolConfig(\"protected-mode\", NULL, MODIFIABLE_CONFIG, server.protected_mode, 1, NULL, NULL),\n    createBoolConfig(\"rdbcompression\", NULL, MODIFIABLE_CONFIG, server.rdb_compression, 1, NULL, NULL),\n    createBoolConfig(\"rdb-del-sync-files\", NULL, MODIFIABLE_CONFIG, server.rdb_del_sync_files, 0, NULL, NULL),\n    createBoolConfig(\"activerehashing\", NULL, MODIFIABLE_CONFIG, server.activerehashing, 1, NULL, NULL),\n    createBoolConfig(\"stop-writes-on-bgsave-error\", NULL, MODIFIABLE_CONFIG, server.stop_writes_on_bgsave_err, 1, NULL, NULL),\n    createBoolConfig(\"dynamic-hz\", NULL, MODIFIABLE_CONFIG, server.dynamic_hz, 1, NULL, NULL), /* Adapt hz to # of clients.*/\n    createBoolConfig(\"lazyfree-lazy-eviction\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_eviction, 0, NULL, NULL),\n    createBoolConfig(\"lazyfree-lazy-expire\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_expire, 0, NULL, NULL),\n    createBoolConfig(\"lazyfree-lazy-server-del\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_server_del, 0, NULL, NULL),\n    createBoolConfig(\"lazyfree-lazy-user-del\", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_user_del , 0, NULL, NULL),\n    createBoolConfig(\"repl-disable-tcp-nodelay\", NULL, MODIFIABLE_CONFIG, server.repl_disable_tcp_nodelay, 0, NULL, NULL),\n    createBoolConfig(\"repl-diskless-sync\", NULL, MODIFIABLE_CONFIG, server.repl_diskless_sync, 0, NULL, NULL),\n    createBoolConfig(\"gopher-enabled\", NULL, MODIFIABLE_CONFIG, server.gopher_enabled, 0, NULL, NULL),\n    createBoolConfig(\"aof-rewrite-incremental-fsync\", NULL, MODIFIABLE_CONFIG, server.aof_rewrite_incremental_fsync, 1, NULL, NULL),\n    createBoolConfig(\"no-appendfsync-on-rewrite\", NULL, MODIFIABLE_CONFIG, server.aof_no_fsync_on_rewrite, 0, NULL, NULL),\n    createBoolConfig(\"cluster-require-full-coverage\", NULL, MODIFIABLE_CONFIG, server.cluster_require_full_coverage, 1, NULL, NULL),\n    createBoolConfig(\"rdb-save-incremental-fsync\", NULL, MODIFIABLE_CONFIG, server.rdb_save_incremental_fsync, 1, NULL, NULL),\n    createBoolConfig(\"aof-load-truncated\", NULL, MODIFIABLE_CONFIG, server.aof_load_truncated, 1, NULL, NULL),\n    createBoolConfig(\"aof-use-rdb-preamble\", NULL, MODIFIABLE_CONFIG, server.aof_use_rdb_preamble, 1, NULL, NULL),\n    createBoolConfig(\"cluster-replica-no-failover\", \"cluster-slave-no-failover\", MODIFIABLE_CONFIG, server.cluster_slave_no_failover, 0, NULL, NULL), /* Failover by default. */\n    createBoolConfig(\"replica-lazy-flush\", \"slave-lazy-flush\", MODIFIABLE_CONFIG, server.repl_slave_lazy_flush, 0, NULL, NULL),\n    createBoolConfig(\"replica-serve-stale-data\", \"slave-serve-stale-data\", MODIFIABLE_CONFIG, server.repl_serve_stale_data, 1, NULL, NULL),\n    createBoolConfig(\"replica-read-only\", \"slave-read-only\", MODIFIABLE_CONFIG, server.repl_slave_ro, 1, NULL, NULL),\n    createBoolConfig(\"replica-ignore-maxmemory\", \"slave-ignore-maxmemory\", MODIFIABLE_CONFIG, server.repl_slave_ignore_maxmemory, 1, NULL, NULL),\n    createBoolConfig(\"jemalloc-bg-thread\", NULL, MODIFIABLE_CONFIG, server.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread),\n    createBoolConfig(\"activedefrag\", NULL, MODIFIABLE_CONFIG, server.active_defrag_enabled, 0, isValidActiveDefrag, NULL),\n    createBoolConfig(\"syslog-enabled\", NULL, IMMUTABLE_CONFIG, server.syslog_enabled, 0, NULL, NULL),\n    createBoolConfig(\"cluster-enabled\", NULL, IMMUTABLE_CONFIG, server.cluster_enabled, 0, NULL, NULL),\n    createBoolConfig(\"appendonly\", NULL, MODIFIABLE_CONFIG, server.aof_enabled, 0, NULL, updateAppendonly),\n    createBoolConfig(\"cluster-allow-reads-when-down\", NULL, MODIFIABLE_CONFIG, server.cluster_allow_reads_when_down, 0, NULL, NULL),\n\n\n    /* String Configs */\n    createStringConfig(\"aclfile\", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, \"\", NULL, NULL),\n    createStringConfig(\"unixsocket\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.unixsocket, NULL, NULL, NULL),\n    createStringConfig(\"pidfile\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.pidfile, NULL, NULL, NULL),\n    createStringConfig(\"replica-announce-ip\", \"slave-announce-ip\", MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.slave_announce_ip, NULL, NULL, NULL),\n    createStringConfig(\"masteruser\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masteruser, NULL, NULL, NULL),\n    createStringConfig(\"masterauth\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL),\n    createStringConfig(\"cluster-announce-ip\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_ip, NULL, NULL, NULL),\n    createStringConfig(\"syslog-ident\", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, \"redis\", NULL, NULL),\n    createStringConfig(\"dbfilename\", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, \"dump.rdb\", isValidDBfilename, NULL),\n    createStringConfig(\"appendfilename\", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.aof_filename, \"appendonly.aof\", isValidAOFfilename, NULL),\n    createStringConfig(\"server_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.server_cpulist, NULL, NULL, NULL),\n    createStringConfig(\"bio_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bio_cpulist, NULL, NULL, NULL),\n    createStringConfig(\"aof_rewrite_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.aof_rewrite_cpulist, NULL, NULL, NULL),\n    createStringConfig(\"bgsave_cpulist\", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),\n\n    /* Enum Configs */\n    createEnumConfig(\"supervised\", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),\n    createEnumConfig(\"syslog-facility\", NULL, IMMUTABLE_CONFIG, syslog_facility_enum, server.syslog_facility, LOG_LOCAL0, NULL, NULL),\n    createEnumConfig(\"repl-diskless-load\", NULL, MODIFIABLE_CONFIG, repl_diskless_load_enum, server.repl_diskless_load, REPL_DISKLESS_LOAD_DISABLED, NULL, NULL),\n    createEnumConfig(\"loglevel\", NULL, MODIFIABLE_CONFIG, loglevel_enum, server.verbosity, LL_NOTICE, NULL, NULL),\n    createEnumConfig(\"maxmemory-policy\", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),\n    createEnumConfig(\"appendfsync\", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),\n\n    /* Integer configs */\n    createIntConfig(\"databases\", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, server.dbnum, 16, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"port\", NULL, IMMUTABLE_CONFIG, 0, 65535, server.port, 6379, INTEGER_CONFIG, NULL, NULL), /* TCP port. */\n    createIntConfig(\"io-threads\", NULL, IMMUTABLE_CONFIG, 1, 128, server.io_threads_num, 1, INTEGER_CONFIG, NULL, NULL), /* Single threaded by default */\n    createIntConfig(\"auto-aof-rewrite-percentage\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.aof_rewrite_perc, 100, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"cluster-replica-validity-factor\", \"cluster-slave-validity-factor\", MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_slave_validity_factor, 10, INTEGER_CONFIG, NULL, NULL), /* Slave max data age factor. */\n    createIntConfig(\"list-max-ziplist-size\", NULL, MODIFIABLE_CONFIG, INT_MIN, INT_MAX, server.list_max_ziplist_size, -2, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"tcp-keepalive\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tcpkeepalive, 300, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"cluster-migration-barrier\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_migration_barrier, 1, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"active-defrag-cycle-min\", NULL, MODIFIABLE_CONFIG, 1, 99, server.active_defrag_cycle_min, 1, INTEGER_CONFIG, NULL, NULL), /* Default: 1% CPU min (at lower threshold) */\n    createIntConfig(\"active-defrag-cycle-max\", NULL, MODIFIABLE_CONFIG, 1, 99, server.active_defrag_cycle_max, 25, INTEGER_CONFIG, NULL, NULL), /* Default: 25% CPU max (at upper threshold) */\n    createIntConfig(\"active-defrag-threshold-lower\", NULL, MODIFIABLE_CONFIG, 0, 1000, server.active_defrag_threshold_lower, 10, INTEGER_CONFIG, NULL, NULL), /* Default: don't defrag when fragmentation is below 10% */\n    createIntConfig(\"active-defrag-threshold-upper\", NULL, MODIFIABLE_CONFIG, 0, 1000, server.active_defrag_threshold_upper, 100, INTEGER_CONFIG, NULL, NULL), /* Default: maximum defrag force at 100% fragmentation */\n    createIntConfig(\"lfu-log-factor\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_log_factor, 10, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"lfu-decay-time\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"replica-priority\", \"slave-priority\", MODIFIABLE_CONFIG, 0, INT_MAX, server.slave_priority, 100, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"repl-diskless-sync-delay\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"maxmemory-samples\", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"timeout\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */\n    createIntConfig(\"replica-announce-port\", \"slave-announce-port\", MODIFIABLE_CONFIG, 0, 65535, server.slave_announce_port, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"tcp-backlog\", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, server.tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */\n    createIntConfig(\"cluster-announce-bus-port\", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_bus_port, 0, INTEGER_CONFIG, NULL, NULL), /* Default: Use +10000 offset. */\n    createIntConfig(\"cluster-announce-port\", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.port */\n    createIntConfig(\"repl-timeout\", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_timeout, 60, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"repl-ping-replica-period\", \"repl-ping-slave-period\", MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_ping_slave_period, 10, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"list-compress-depth\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"rdb-key-save-delay\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"key-load-delay\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.key_load_delay, 0, INTEGER_CONFIG, NULL, NULL),\n    createIntConfig(\"active-expire-effort\", NULL, MODIFIABLE_CONFIG, 1, 10, server.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */\n    createIntConfig(\"hz\", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),\n    createIntConfig(\"min-replicas-to-write\", \"min-slaves-to-write\", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),\n    createIntConfig(\"min-replicas-max-lag\", \"min-slaves-max-lag\", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves),\n\n    /* Unsigned int configs */\n    createUIntConfig(\"maxclients\", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, server.maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),\n\n    /* Unsigned Long configs */\n    createULongConfig(\"active-defrag-max-scan-fields\", NULL, MODIFIABLE_CONFIG, 1, LONG_MAX, server.active_defrag_max_scan_fields, 1000, INTEGER_CONFIG, NULL, NULL), /* Default: keys with more than 1000 fields will be processed separately */\n    createULongConfig(\"slowlog-max-len\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.slowlog_max_len, 128, INTEGER_CONFIG, NULL, NULL),\n    createULongConfig(\"acllog-max-len\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.acllog_max_len, 128, INTEGER_CONFIG, NULL, NULL),\n\n    /* Long Long configs */\n    createLongLongConfig(\"lua-time-limit\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.lua_time_limit, 5000, INTEGER_CONFIG, NULL, NULL),/* milliseconds */\n    createLongLongConfig(\"cluster-node-timeout\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"slowlog-log-slower-than\", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, server.slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"latency-monitor-threshold\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"proto-max-bulk-len\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */\n    createLongLongConfig(\"stream-node-max-entries\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),\n    createLongLongConfig(\"repl-backlog-size\", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */\n\n    /* Unsigned Long Long configs */\n    createULongLongConfig(\"maxmemory\", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),\n\n    /* Size_t configs */\n    createSizeTConfig(\"hash-max-ziplist-entries\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_ziplist_entries, 512, INTEGER_CONFIG, NULL, NULL),\n    createSizeTConfig(\"set-max-intset-entries\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.set_max_intset_entries, 512, INTEGER_CONFIG, NULL, NULL),\n    createSizeTConfig(\"zset-max-ziplist-entries\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_entries, 128, INTEGER_CONFIG, NULL, NULL),\n    createSizeTConfig(\"active-defrag-ignore-bytes\", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.active_defrag_ignore_bytes, 100<<20, MEMORY_CONFIG, NULL, NULL), /* Default: don't defrag if frag overhead is below 100mb */\n    createSizeTConfig(\"hash-max-ziplist-value\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"stream-node-max-bytes\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.stream_node_max_bytes, 4096, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"zset-max-ziplist-value\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"hll-sparse-max-bytes\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hll_sparse_max_bytes, 3000, MEMORY_CONFIG, NULL, NULL),\n    createSizeTConfig(\"tracking-table-max-keys\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.tracking_table_max_keys, 1000000, INTEGER_CONFIG, NULL, NULL), /* Default: 1 million keys max. */\n\n    /* Other configs */\n    createTimeTConfig(\"repl-backlog-ttl\", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.repl_backlog_time_limit, 60*60, INTEGER_CONFIG, NULL, NULL), /* Default: 1 hour */\n    createOffTConfig(\"auto-aof-rewrite-min-size\", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.aof_rewrite_min_size, 64*1024*1024, MEMORY_CONFIG, NULL, NULL),\n\n#ifdef USE_OPENSSL\n    createIntConfig(\"tls-port\", NULL, IMMUTABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, NULL), /* TCP port. */\n    createBoolConfig(\"tls-cluster\", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, NULL),\n    createBoolConfig(\"tls-replication\", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, NULL),\n    createBoolConfig(\"tls-auth-clients\", NULL, MODIFIABLE_CONFIG, server.tls_auth_clients, 1, NULL, NULL),\n    createBoolConfig(\"tls-prefer-server-ciphers\", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),\n    createStringConfig(\"tls-cert-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-key-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-dh-params-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ca-cert-file\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ca-cert-dir\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-protocols\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ciphers\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, updateTlsCfg),\n    createStringConfig(\"tls-ciphersuites\", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, updateTlsCfg),\n#endif\n\n    /* NULL Terminator */\n    {NULL}\n};\n\n/*-----------------------------------------------------------------------------\n * CONFIG command entry point\n *----------------------------------------------------------------------------*/\n\nvoid configCommand(client *c) {\n    /* Only allow CONFIG GET while loading. */\n    if (server.loading && strcasecmp(c->argv[1]->ptr,\"get\")) {\n        addReplyError(c,\"Only CONFIG GET is allowed during loading\");\n        return;\n    }\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"GET <pattern> -- Return parameters matching the glob-like <pattern> and their values.\",\n\"SET <parameter> <value> -- Set parameter to value.\",\n\"RESETSTAT -- Reset statistics reported by INFO.\",\n\"REWRITE -- Rewrite the configuration file.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set\") && c->argc == 4) {\n        configSetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"get\") && c->argc == 3) {\n        configGetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"resetstat\") && c->argc == 2) {\n        resetServerStats();\n        resetCommandTableStats();\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"rewrite\") && c->argc == 2) {\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            serverLog(LL_WARNING,\"CONFIG REWRITE failed: %s\", strerror(errno));\n            addReplyErrorFormat(c,\"Rewriting config file: %s\", strerror(errno));\n        } else {\n            serverLog(LL_WARNING,\"CONFIG REWRITE executed with success.\");\n            addReply(c,shared.ok);\n        }\n    } else {\n        addReplySubcommandSyntaxError(c);\n        return;\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#ifdef __linux__\n#include <linux/version.h>\n#include <features.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#define HAVE_PROC_SOMAXCONN 1\n#endif\n\n/* Test for task_info() */\n#if defined(__APPLE__)\n#define HAVE_TASKINFO 1\n#endif\n\n/* Test for backtrace() */\n#if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__)) || \\\n    defined(__FreeBSD__) || (defined(__OpenBSD__) && defined(USE_BACKTRACE))\\\n || defined(__DragonFly__)\n#define HAVE_BACKTRACE 1\n#endif\n\n/* MSG_NOSIGNAL. */\n#ifdef __linux__\n#define HAVE_MSG_NOSIGNAL 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 redis_fsync to fdatasync() in Linux and fsync() for all the rest */\n#ifdef __linux__\n#define redis_fsync fdatasync\n#else\n#define redis_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#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(__GLIBC__)) || 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 || __powerpc__) && __GNUC__\n#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)\n#if defined(__clang__)\n#define HAVE_ATOMIC\n#endif\n#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))\n#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6))\n#define HAVE_ATOMIC\n#endif\n#endif\n#endif\n\n/* Make sure we can test for ARM just checking for __arm__, since sometimes\n * __arm is defined but __arm__ is not. */\n#if defined(__arm) && !defined(__arm__)\n#define __arm__\n#endif\n#if defined (__aarch64__) && !defined(__arm64__)\n#define __arm64__\n#endif\n\n/* Make sure we can test for SPARC just checking for __sparc__. */\n#if defined(__sparc) && !defined(__sparc__)\n#define __sparc__\n#endif\n\n#if defined(__sparc__) || defined(__arm__)\n#define USE_ALIGNED_ACCESS\n#endif\n\n/* Define for redis_set_thread_title */\n#ifdef __linux__\n#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name)\n#else\n#if (defined __FreeBSD__ || defined __OpenBSD__)\n#include <pthread_np.h>\n#define redis_set_thread_title(name) pthread_set_name_np(pthread_self(), name)\n#elif defined __NetBSD__\n#include <pthread.h>\n#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name, NULL)\n#else\n#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7))\nint pthread_setname_np(const char *name);\n#include <pthread.h>\n#define redis_set_thread_title(name) pthread_setname_np(name)\n#else\n#define redis_set_thread_title(name)\n#endif\n#endif\n#endif\n\n/* Check if we can use setcpuaffinity(). */\n#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__)\n#define USE_SETCPUAFFINITY\nvoid setcpuaffinity(const char *cpulist);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/connection.c",
    "content": "/*\n * Copyright (c) 2019, Redis Labs\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 \"server.h\"\n#include \"connhelpers.h\"\n\n/* The connections module provides a lean abstraction of network connections\n * to avoid direct socket and async event management across the Redis code base.\n *\n * It does NOT provide advanced connection features commonly found in similar\n * libraries such as complete in/out buffer management, throttling, etc. These\n * functions remain in networking.c.\n *\n * The primary goal is to allow transparent handling of TCP and TLS based\n * connections. To do so, connections have the following properties:\n *\n * 1. A connection may live before its corresponding socket exists.  This\n *    allows various context and configuration setting to be handled before\n *    establishing the actual connection.\n * 2. The caller may register/unregister logical read/write handlers to be\n *    called when the connection has data to read from/can accept writes.\n *    These logical handlers may or may not correspond to actual AE events,\n *    depending on the implementation (for TCP they are; for TLS they aren't).\n */\n\nConnectionType CT_Socket;\n\n/* When a connection is created we must know its type already, but the\n * underlying socket may or may not exist:\n *\n * - For accepted connections, it exists as we do not model the listen/accept\n *   part; So caller calls connCreateSocket() followed by connAccept().\n * - For outgoing connections, the socket is created by the connection module\n *   itself; So caller calls connCreateSocket() followed by connConnect(),\n *   which registers a connect callback that fires on connected/error state\n *   (and after any transport level handshake was done).\n *\n * NOTE: An earlier version relied on connections being part of other structs\n * and not independently allocated. This could lead to further optimizations\n * like using container_of(), etc.  However it was discontinued in favor of\n * this approach for these reasons:\n *\n * 1. In some cases conns are created/handled outside the context of the\n * containing struct, in which case it gets a bit awkward to copy them.\n * 2. Future implementations may wish to allocate arbitrary data for the\n * connection.\n * 3. The container_of() approach is anyway risky because connections may\n * be embedded in different structs, not just client.\n */\n\nconnection *connCreateSocket() {\n    connection *conn = zcalloc(sizeof(connection));\n    conn->type = &CT_Socket;\n    conn->fd = -1;\n\n    return conn;\n}\n\n/* Create a new socket-type connection that is already associated with\n * an accepted connection.\n *\n * The socket is not read for I/O until connAccept() was called and\n * invoked the connection-level accept handler.\n */\nconnection *connCreateAcceptedSocket(int fd) {\n    connection *conn = connCreateSocket();\n    conn->fd = fd;\n    conn->state = CONN_STATE_ACCEPTING;\n    return conn;\n}\n\nstatic int connSocketConnect(connection *conn, const char *addr, int port, const char *src_addr,\n        ConnectionCallbackFunc connect_handler) {\n    int fd = anetTcpNonBlockBestEffortBindConnect(NULL,addr,port,src_addr);\n    if (fd == -1) {\n        conn->state = CONN_STATE_ERROR;\n        conn->last_errno = errno;\n        return C_ERR;\n    }\n\n    conn->fd = fd;\n    conn->state = CONN_STATE_CONNECTING;\n\n    conn->conn_handler = connect_handler;\n    aeCreateFileEvent(server.el, conn->fd, AE_WRITABLE,\n            conn->type->ae_handler, conn);\n\n    return C_OK;\n}\n\n/* Returns true if a write handler is registered */\nint connHasWriteHandler(connection *conn) {\n    return conn->write_handler != NULL;\n}\n\n/* Returns true if a read handler is registered */\nint connHasReadHandler(connection *conn) {\n    return conn->read_handler != NULL;\n}\n\n/* Associate a private data pointer with the connection */\nvoid connSetPrivateData(connection *conn, void *data) {\n    conn->private_data = data;\n}\n\n/* Get the associated private data pointer */\nvoid *connGetPrivateData(connection *conn) {\n    return conn->private_data;\n}\n\n/* ------ Pure socket connections ------- */\n\n/* A very incomplete list of implementation-specific calls.  Much of the above shall\n * move here as we implement additional connection types.\n */\n\n/* Close the connection and free resources. */\nstatic void connSocketClose(connection *conn) {\n    if (conn->fd != -1) {\n        aeDeleteFileEvent(server.el,conn->fd,AE_READABLE);\n        aeDeleteFileEvent(server.el,conn->fd,AE_WRITABLE);\n        close(conn->fd);\n        conn->fd = -1;\n    }\n\n    /* If called from within a handler, schedule the close but\n     * keep the connection until the handler returns.\n     */\n    if (connHasRefs(conn)) {\n        conn->flags |= CONN_FLAG_CLOSE_SCHEDULED;\n        return;\n    }\n\n    zfree(conn);\n}\n\nstatic int connSocketWrite(connection *conn, const void *data, size_t data_len) {\n    int ret = write(conn->fd, data, data_len);\n    if (ret < 0 && errno != EAGAIN) {\n        conn->last_errno = errno;\n        conn->state = CONN_STATE_ERROR;\n    }\n\n    return ret;\n}\n\nstatic int connSocketRead(connection *conn, void *buf, size_t buf_len) {\n    int ret = read(conn->fd, buf, buf_len);\n    if (!ret) {\n        conn->state = CONN_STATE_CLOSED;\n    } else if (ret < 0 && errno != EAGAIN) {\n        conn->last_errno = errno;\n        conn->state = CONN_STATE_ERROR;\n    }\n\n    return ret;\n}\n\nstatic int connSocketAccept(connection *conn, ConnectionCallbackFunc accept_handler) {\n    int ret = C_OK;\n\n    if (conn->state != CONN_STATE_ACCEPTING) return C_ERR;\n    conn->state = CONN_STATE_CONNECTED;\n\n    connIncrRefs(conn);\n    if (!callHandler(conn, accept_handler)) ret = C_ERR;\n    connDecrRefs(conn);\n\n    return ret;\n}\n\n/* Register a write handler, to be called when the connection is writable.\n * If NULL, the existing handler is removed.\n *\n * The barrier flag indicates a write barrier is requested, resulting with\n * CONN_FLAG_WRITE_BARRIER set. This will ensure that the write handler is\n * always called before and not after the read handler in a single event\n * loop.\n */\nstatic int connSocketSetWriteHandler(connection *conn, ConnectionCallbackFunc func, int barrier) {\n    if (func == conn->write_handler) return C_OK;\n\n    conn->write_handler = func;\n    if (barrier)\n        conn->flags |= CONN_FLAG_WRITE_BARRIER;\n    else\n        conn->flags &= ~CONN_FLAG_WRITE_BARRIER;\n    if (!conn->write_handler)\n        aeDeleteFileEvent(server.el,conn->fd,AE_WRITABLE);\n    else\n        if (aeCreateFileEvent(server.el,conn->fd,AE_WRITABLE,\n                    conn->type->ae_handler,conn) == AE_ERR) return C_ERR;\n    return C_OK;\n}\n\n/* Register a read handler, to be called when the connection is readable.\n * If NULL, the existing handler is removed.\n */\nstatic int connSocketSetReadHandler(connection *conn, ConnectionCallbackFunc func) {\n    if (func == conn->read_handler) return C_OK;\n\n    conn->read_handler = func;\n    if (!conn->read_handler)\n        aeDeleteFileEvent(server.el,conn->fd,AE_READABLE);\n    else\n        if (aeCreateFileEvent(server.el,conn->fd,\n                    AE_READABLE,conn->type->ae_handler,conn) == AE_ERR) return C_ERR;\n    return C_OK;\n}\n\nstatic const char *connSocketGetLastError(connection *conn) {\n    return strerror(conn->last_errno);\n}\n\nstatic void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask)\n{\n    UNUSED(el);\n    UNUSED(fd);\n    connection *conn = clientData;\n\n    if (conn->state == CONN_STATE_CONNECTING &&\n            (mask & AE_WRITABLE) && conn->conn_handler) {\n\n        if (connGetSocketError(conn)) {\n            conn->last_errno = errno;\n            conn->state = CONN_STATE_ERROR;\n        } else {\n            conn->state = CONN_STATE_CONNECTED;\n        }\n\n        if (!conn->write_handler) aeDeleteFileEvent(server.el,conn->fd,AE_WRITABLE);\n\n        if (!callHandler(conn, conn->conn_handler)) return;\n        conn->conn_handler = NULL;\n    }\n\n    /* Normally we execute the readable event first, and the writable\n     * event later. This is useful as sometimes we may be able\n     * to serve the reply of a query immediately after processing the\n     * query.\n     *\n     * However if WRITE_BARRIER is set in the mask, our application is\n     * asking us to do the reverse: never fire the writable event\n     * after the readable. In such a case, we invert the calls.\n     * This is useful when, for instance, we want to do things\n     * in the beforeSleep() hook, like fsync'ing a file to disk,\n     * before replying to a client. */\n    int invert = conn->flags & CONN_FLAG_WRITE_BARRIER;\n\n    int call_write = (mask & AE_WRITABLE) && conn->write_handler;\n    int call_read = (mask & AE_READABLE) && conn->read_handler;\n\n    /* Handle normal I/O flows */\n    if (!invert && call_read) {\n        if (!callHandler(conn, conn->read_handler)) return;\n    }\n    /* Fire the writable event. */\n    if (call_write) {\n        if (!callHandler(conn, conn->write_handler)) return;\n    }\n    /* If we have to invert the call, fire the readable event now\n     * after the writable one. */\n    if (invert && call_read) {\n        if (!callHandler(conn, conn->read_handler)) return;\n    }\n}\n\nstatic int connSocketBlockingConnect(connection *conn, const char *addr, int port, long long timeout) {\n    int fd = anetTcpNonBlockConnect(NULL,addr,port);\n    if (fd == -1) {\n        conn->state = CONN_STATE_ERROR;\n        conn->last_errno = errno;\n        return C_ERR;\n    }\n\n    if ((aeWait(fd, AE_WRITABLE, timeout) & AE_WRITABLE) == 0) {\n        conn->state = CONN_STATE_ERROR;\n        conn->last_errno = ETIMEDOUT;\n    }\n\n    conn->fd = fd;\n    conn->state = CONN_STATE_CONNECTED;\n    return C_OK;\n}\n\n/* Connection-based versions of syncio.c functions.\n * NOTE: This should ideally be refactored out in favor of pure async work.\n */\n\nstatic ssize_t connSocketSyncWrite(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return syncWrite(conn->fd, ptr, size, timeout);\n}\n\nstatic ssize_t connSocketSyncRead(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return syncRead(conn->fd, ptr, size, timeout);\n}\n\nstatic ssize_t connSocketSyncReadLine(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return syncReadLine(conn->fd, ptr, size, timeout);\n}\n\n\nConnectionType CT_Socket = {\n    .ae_handler = connSocketEventHandler,\n    .close = connSocketClose,\n    .write = connSocketWrite,\n    .read = connSocketRead,\n    .accept = connSocketAccept,\n    .connect = connSocketConnect,\n    .set_write_handler = connSocketSetWriteHandler,\n    .set_read_handler = connSocketSetReadHandler,\n    .get_last_error = connSocketGetLastError,\n    .blocking_connect = connSocketBlockingConnect,\n    .sync_write = connSocketSyncWrite,\n    .sync_read = connSocketSyncRead,\n    .sync_readline = connSocketSyncReadLine\n};\n\n\nint connGetSocketError(connection *conn) {\n    int sockerr = 0;\n    socklen_t errlen = sizeof(sockerr);\n\n    if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)\n        sockerr = errno;\n    return sockerr;\n}\n\nint connPeerToString(connection *conn, char *ip, size_t ip_len, int *port) {\n    return anetPeerToString(conn ? conn->fd : -1, ip, ip_len, port);\n}\n\nint connFormatPeer(connection *conn, char *buf, size_t buf_len) {\n    return anetFormatPeer(conn ? conn->fd : -1, buf, buf_len);\n}\n\nint connSockName(connection *conn, char *ip, size_t ip_len, int *port) {\n    return anetSockName(conn->fd, ip, ip_len, port);\n}\n\nint connBlock(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetBlock(NULL, conn->fd);\n}\n\nint connNonBlock(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetNonBlock(NULL, conn->fd);\n}\n\nint connEnableTcpNoDelay(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetEnableTcpNoDelay(NULL, conn->fd);\n}\n\nint connDisableTcpNoDelay(connection *conn) {\n    if (conn->fd == -1) return C_ERR;\n    return anetDisableTcpNoDelay(NULL, conn->fd);\n}\n\nint connKeepAlive(connection *conn, int interval) {\n    if (conn->fd == -1) return C_ERR;\n    return anetKeepAlive(NULL, conn->fd, interval);\n}\n\nint connSendTimeout(connection *conn, long long ms) {\n    return anetSendTimeout(NULL, conn->fd, ms);\n}\n\nint connRecvTimeout(connection *conn, long long ms) {\n    return anetRecvTimeout(NULL, conn->fd, ms);\n}\n\nint connGetState(connection *conn) {\n    return conn->state;\n}\n\n/* Return a text that describes the connection, suitable for inclusion\n * in CLIENT LIST and similar outputs.\n *\n * For sockets, we always return \"fd=<fdnum>\" to maintain compatibility.\n */\nconst char *connGetInfo(connection *conn, char *buf, size_t buf_len) {\n    snprintf(buf, buf_len-1, \"fd=%i\", conn->fd);\n    return buf;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/connection.h",
    "content": "\n/*\n * Copyright (c) 2019, Redis Labs\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_CONNECTION_H\n#define __REDIS_CONNECTION_H\n\n#define CONN_INFO_LEN   32\n\nstruct aeEventLoop;\ntypedef struct connection connection;\n\ntypedef enum {\n    CONN_STATE_NONE = 0,\n    CONN_STATE_CONNECTING,\n    CONN_STATE_ACCEPTING,\n    CONN_STATE_CONNECTED,\n    CONN_STATE_CLOSED,\n    CONN_STATE_ERROR\n} ConnectionState;\n\n#define CONN_FLAG_CLOSE_SCHEDULED   (1<<0)      /* Closed scheduled by a handler */\n#define CONN_FLAG_WRITE_BARRIER     (1<<1)      /* Write barrier requested */\n\ntypedef void (*ConnectionCallbackFunc)(struct connection *conn);\n\ntypedef struct ConnectionType {\n    void (*ae_handler)(struct aeEventLoop *el, int fd, void *clientData, int mask);\n    int (*connect)(struct connection *conn, const char *addr, int port, const char *source_addr, ConnectionCallbackFunc connect_handler);\n    int (*write)(struct connection *conn, const void *data, size_t data_len);\n    int (*read)(struct connection *conn, void *buf, size_t buf_len);\n    void (*close)(struct connection *conn);\n    int (*accept)(struct connection *conn, ConnectionCallbackFunc accept_handler);\n    int (*set_write_handler)(struct connection *conn, ConnectionCallbackFunc handler, int barrier);\n    int (*set_read_handler)(struct connection *conn, ConnectionCallbackFunc handler);\n    const char *(*get_last_error)(struct connection *conn);\n    int (*blocking_connect)(struct connection *conn, const char *addr, int port, long long timeout);\n    ssize_t (*sync_write)(struct connection *conn, char *ptr, ssize_t size, long long timeout);\n    ssize_t (*sync_read)(struct connection *conn, char *ptr, ssize_t size, long long timeout);\n    ssize_t (*sync_readline)(struct connection *conn, char *ptr, ssize_t size, long long timeout);\n} ConnectionType;\n\nstruct connection {\n    ConnectionType *type;\n    ConnectionState state;\n    short int flags;\n    short int refs;\n    int last_errno;\n    void *private_data;\n    ConnectionCallbackFunc conn_handler;\n    ConnectionCallbackFunc write_handler;\n    ConnectionCallbackFunc read_handler;\n    int fd;\n};\n\n/* The connection module does not deal with listening and accepting sockets,\n * so we assume we have a socket when an incoming connection is created.\n *\n * The fd supplied should therefore be associated with an already accept()ed\n * socket.\n *\n * connAccept() may directly call accept_handler(), or return and call it\n * at a later time. This behavior is a bit awkward but aims to reduce the need\n * to wait for the next event loop, if no additional handshake is required.\n *\n * IMPORTANT: accept_handler may decide to close the connection, calling connClose().\n * To make this safe, the connection is only marked with CONN_FLAG_CLOSE_SCHEDULED\n * in this case, and connAccept() returns with an error.\n *\n * connAccept() callers must always check the return value and on error (C_ERR)\n * a connClose() must be called.\n */\n\nstatic inline int connAccept(connection *conn, ConnectionCallbackFunc accept_handler) {\n    return conn->type->accept(conn, accept_handler);\n}\n\n/* Establish a connection.  The connect_handler will be called when the connection\n * is established, or if an error has occured.\n *\n * The connection handler will be responsible to set up any read/write handlers\n * as needed.\n *\n * If C_ERR is returned, the operation failed and the connection handler shall\n * not be expected.\n */\nstatic inline int connConnect(connection *conn, const char *addr, int port, const char *src_addr,\n        ConnectionCallbackFunc connect_handler) {\n    return conn->type->connect(conn, addr, port, src_addr, connect_handler);\n}\n\n/* Blocking connect.\n *\n * NOTE: This is implemented in order to simplify the transition to the abstract\n * connections, but should probably be refactored out of cluster.c and replication.c,\n * in favor of a pure async implementation.\n */\nstatic inline int connBlockingConnect(connection *conn, const char *addr, int port, long long timeout) {\n    return conn->type->blocking_connect(conn, addr, port, timeout);\n}\n\n/* Write to connection, behaves the same as write(2).\n *\n * Like write(2), a short write is possible. A -1 return indicates an error.\n *\n * The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use\n * connGetState() to see if the connection state is still CONN_STATE_CONNECTED.\n */\nstatic inline int connWrite(connection *conn, const void *data, size_t data_len) {\n    return conn->type->write(conn, data, data_len);\n}\n\n/* Read from the connection, behaves the same as read(2).\n * \n * Like read(2), a short read is possible.  A return value of 0 will indicate the\n * connection was closed, and -1 will indicate an error.\n *\n * The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use\n * connGetState() to see if the connection state is still CONN_STATE_CONNECTED.\n */\nstatic inline int connRead(connection *conn, void *buf, size_t buf_len) {\n    return conn->type->read(conn, buf, buf_len);\n}\n\n/* Register a write handler, to be called when the connection is writable.\n * If NULL, the existing handler is removed.\n */\nstatic inline int connSetWriteHandler(connection *conn, ConnectionCallbackFunc func) {\n    return conn->type->set_write_handler(conn, func, 0);\n}\n\n/* Register a read handler, to be called when the connection is readable.\n * If NULL, the existing handler is removed.\n */\nstatic inline int connSetReadHandler(connection *conn, ConnectionCallbackFunc func) {\n    return conn->type->set_read_handler(conn, func);\n}\n\n/* Set a write handler, and possibly enable a write barrier, this flag is\n * cleared when write handler is changed or removed.\n * With barroer enabled, we never fire the event if the read handler already\n * fired in the same event loop iteration. Useful when you want to persist\n * things to disk before sending replies, and want to do that in a group fashion. */\nstatic inline int connSetWriteHandlerWithBarrier(connection *conn, ConnectionCallbackFunc func, int barrier) {\n    return conn->type->set_write_handler(conn, func, barrier);\n}\n\nstatic inline void connClose(connection *conn) {\n    conn->type->close(conn);\n}\n\n/* Returns the last error encountered by the connection, as a string.  If no error,\n * a NULL is returned.\n */\nstatic inline const char *connGetLastError(connection *conn) {\n    return conn->type->get_last_error(conn);\n}\n\nstatic inline ssize_t connSyncWrite(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return conn->type->sync_write(conn, ptr, size, timeout);\n}\n\nstatic inline ssize_t connSyncRead(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return conn->type->sync_read(conn, ptr, size, timeout);\n}\n\nstatic inline ssize_t connSyncReadLine(connection *conn, char *ptr, ssize_t size, long long timeout) {\n    return conn->type->sync_readline(conn, ptr, size, timeout);\n}\n\nconnection *connCreateSocket();\nconnection *connCreateAcceptedSocket(int fd);\n\nconnection *connCreateTLS();\nconnection *connCreateAcceptedTLS(int fd, int require_auth);\n\nvoid connSetPrivateData(connection *conn, void *data);\nvoid *connGetPrivateData(connection *conn);\nint connGetState(connection *conn);\nint connHasWriteHandler(connection *conn);\nint connHasReadHandler(connection *conn);\nint connGetSocketError(connection *conn);\n\n/* anet-style wrappers to conns */\nint connBlock(connection *conn);\nint connNonBlock(connection *conn);\nint connEnableTcpNoDelay(connection *conn);\nint connDisableTcpNoDelay(connection *conn);\nint connKeepAlive(connection *conn, int interval);\nint connSendTimeout(connection *conn, long long ms);\nint connRecvTimeout(connection *conn, long long ms);\nint connPeerToString(connection *conn, char *ip, size_t ip_len, int *port);\nint connFormatPeer(connection *conn, char *buf, size_t buf_len);\nint connSockName(connection *conn, char *ip, size_t ip_len, int *port);\nconst char *connGetInfo(connection *conn, char *buf, size_t buf_len);\n\n/* Helpers for tls special considerations */\nint tlsHasPendingData();\nint tlsProcessPendingData();\n\n#endif  /* __REDIS_CONNECTION_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/connhelpers.h",
    "content": "\n/*\n * Copyright (c) 2019, Redis Labs\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_CONNHELPERS_H\n#define __REDIS_CONNHELPERS_H\n\n#include \"connection.h\"\n\n/* These are helper functions that are common to different connection\n * implementations (currently sockets in connection.c and TLS in tls.c).\n *\n * Currently helpers implement the mechanisms for invoking connection\n * handlers and tracking connection references, to allow safe destruction\n * of connections from within a handler.\n */\n\n/* Incremenet connection references.\n *\n * Inside a connection handler, we guarantee refs >= 1 so it is always\n * safe to connClose().\n *\n * In other cases where we don't want to prematurely lose the connection,\n * it can go beyond 1 as well; currently it is only done by connAccept().\n */\nstatic inline void connIncrRefs(connection *conn) {\n    conn->refs++;\n}\n\n/* Decrement connection references.\n *\n * Note that this is not intended to provide any automatic free logic!\n * callHandler() takes care of that for the common flows, and anywhere an\n * explicit connIncrRefs() is used, the caller is expected to take care of\n * that.\n */\n\nstatic inline void connDecrRefs(connection *conn) {\n    conn->refs--;\n}\n\nstatic inline int connHasRefs(connection *conn) {\n    return conn->refs;\n}\n\n/* Helper for connection implementations to call handlers:\n * 1. Increment refs to protect the connection.\n * 2. Execute the handler (if set).\n * 3. Decrement refs and perform deferred close, if refs==0.\n */\nstatic inline int callHandler(connection *conn, ConnectionCallbackFunc handler) {\n    connIncrRefs(conn);\n    if (handler) handler(conn);\n    connDecrRefs(conn);\n    if (conn->flags & CONN_FLAG_CLOSE_SCHEDULED) {\n        if (!connHasRefs(conn)) connClose(conn);\n        return 0;\n    }\n    return 1;\n}\n\n#endif  /* __REDIS_CONNHELPERS_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/crc16.c",
    "content": "#include \"server.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": "Chapter07/redis-6.0.3/src/crc16_slottable.h",
    "content": "#ifndef _CRC16_TABLE_H__\n#define _CRC16_TABLE_H__\n\n/* A table of the shortest possible alphanumeric string that is mapped by redis' crc16\n * to any given redis cluster slot. \n * \n * The array indexes are slot numbers, so that given a desired slot, this string is guaranteed\n * to make redis cluster route a request to the shard holding this slot \n */\n\nconst char *crc16_slot_table[] = {\n\"06S\", \"Qi\", \"5L5\", \"4Iu\", \"4gY\", \"460\", \"1Y7\", \"1LV\", \"0QG\", \"ru\", \"7Ok\", \"4ji\", \"4DE\", \"65n\", \"2JH\", \"I8\", \"F9\", \"SX\", \"7nF\", \"4KD\", \n\"4eh\", \"6PK\", \"2ke\", \"1Ng\", \"0Sv\", \"4L\", \"491\", \"4hX\", \"4Ft\", \"5C4\", \"2Hy\", \"09R\", \"021\", \"0cX\", \"4Xv\", \"6mU\", \"6Cy\", \"42R\", \"0Mt\", \"nF\", \n\"cv\", \"1Pe\", \"5kK\", \"6NI\", \"74L\", \"4UF\", \"0nh\", \"MZ\", \"2TJ\", \"0ai\", \"4ZG\", \"6od\", \"6AH\", \"40c\", \"0OE\", \"lw\", \"aG\", \"0Bu\", \"5iz\", \"6Lx\", \n\"5R7\", \"4Ww\", \"0lY\", \"Ok\", \"5n3\", \"4ks\", \"8YE\", \"7g\", \"2KR\", \"1nP\", \"714\", \"64t\", \"69D\", \"4Ho\", \"07I\", \"Ps\", \"2hN\", \"1ML\", \"4fC\", \"7CA\", \n\"avs\", \"4iB\", \"0Rl\", \"5V\", \"2Ic\", \"08H\", \"4Gn\", \"66E\", \"aUo\", \"b4e\", \"05x\", \"RB\", \"8f\", \"8VD\", \"4dr\", \"5a2\", \"4zp\", \"6OS\", \"bl\", \"355\", \n\"0or\", \"1j2\", \"75V\", \"bno\", \"4Yl\", \"6lO\", \"Ap\", \"0bB\", \"0Ln\", \"2yM\", \"6Bc\", \"43H\", \"4xA\", \"6Mb\", \"22D\", \"14\", \"0mC\", \"Nq\", \"6cN\", \"4Vm\", \n\"ban\", \"aDl\", \"CA\", \"14Z\", \"8GG\", \"mm\", \"549\", \"41y\", \"53t\", \"464\", \"1Y3\", \"1LR\", \"06W\", \"Qm\", \"5L1\", \"4Iq\", \"4DA\", \"65j\", \"2JL\", \"1oN\", \n\"0QC\", \"6y\", \"7Oo\", \"4jm\", \"4el\", \"6PO\", \"9x\", \"1Nc\", \"04f\", \"2EM\", \"7nB\", \"bqs\", \"4Fp\", \"5C0\", \"d6F\", \"09V\", \"0Sr\", \"4H\", \"495\", \"bRo\", \n\"aio\", \"42V\", \"0Mp\", \"nB\", \"025\", \"17u\", \"4Xr\", \"6mQ\", \"74H\", \"4UB\", \"0nl\", \"3Kn\", \"cr\", \"1Pa\", \"5kO\", \"6NM\", \"6AL\", \"40g\", \"0OA\", \"ls\", \n\"2TN\", \"0am\", \"4ZC\", \"aEr\", \"5R3\", \"4Ws\", \"18t\", \"Oo\", \"aC\", \"0Bq\", \"bCl\", \"afn\", \"2KV\", \"1nT\", \"5Uz\", \"64p\", \"5n7\", \"4kw\", \"0PY\", \"7c\", \n\"2hJ\", \"1MH\", \"4fG\", \"6Sd\", \"7mi\", \"4Hk\", \"07M\", \"Pw\", \"2Ig\", \"08L\", \"4Gj\", \"66A\", \"7LD\", \"4iF\", \"0Rh\", \"5R\", \"8b\", \"1Oy\", \"4dv\", \"5a6\", \n\"7oX\", \"4JZ\", \"0qt\", \"RF\", \"0ov\", \"LD\", \"4A9\", \"4TX\", \"4zt\", \"6OW\", \"bh\", \"0AZ\", \"z9\", \"oX\", \"6Bg\", \"43L\", \"4Yh\", \"6lK\", \"At\", \"0bF\", \n\"0mG\", \"Nu\", \"6cJ\", \"4Vi\", \"4xE\", \"6Mf\", \"2vH\", \"10\", \"8GC\", \"mi\", \"5p5\", \"4uu\", \"5Kx\", \"4N8\", \"CE\", \"1pV\", \"0QO\", \"6u\", \"7Oc\", \"4ja\", \n\"4DM\", \"65f\", \"3Za\", \"I0\", \"0rS\", \"Qa\", \"68V\", \"b7F\", \"4gQ\", \"468\", \"dSo\", \"285\", \"274\", \"4D\", \"499\", \"4hP\", \"b8G\", \"67W\", \"0h3\", \"09Z\", \n\"F1\", \"SP\", \"7nN\", \"4KL\", \"51I\", \"6PC\", \"9t\", \"1No\", \"21g\", \"1Pm\", \"5kC\", \"6NA\", \"74D\", \"4UN\", \"X3\", \"MR\", \"029\", \"0cP\", \"bbM\", \"79t\", \n\"4c3\", \"42Z\", \"8Dd\", \"nN\", \"aO\", \"8Ke\", \"4yS\", \"4l2\", \"76u\", \"635\", \"0lQ\", \"Oc\", \"BS\", \"W2\", \"4ZO\", \"6ol\", \"7Qa\", \"40k\", \"0OM\", \"2zn\", \n\"69L\", \"4Hg\", \"07A\", \"2Fj\", \"2hF\", \"k6\", \"4fK\", \"6Sh\", \"7Ny\", \"6K9\", \"0PU\", \"7o\", \"2KZ\", \"1nX\", \"4EW\", \"4P6\", \"7oT\", \"4JV\", \"05p\", \"RJ\", \n\"8n\", \"1Ou\", \"4dz\", \"6QY\", \"7LH\", \"4iJ\", \"d7\", \"qV\", \"2Ik\", \"1li\", \"4Gf\", \"66M\", \"4Yd\", \"6lG\", \"Ax\", \"0bJ\", \"z5\", \"oT\", \"6Bk\", \"4wH\", \n\"4zx\", \"aeI\", \"bd\", \"0AV\", \"0oz\", \"LH\", \"4A5\", \"4TT\", \"5Kt\", \"4N4\", \"CI\", \"14R\", \"0NW\", \"me\", \"541\", \"41q\", \"4xI\", \"6Mj\", \"22L\", \"u4\", \n\"0mK\", \"Ny\", \"6cF\", \"4Ve\", \"4DI\", \"65b\", \"2JD\", \"I4\", \"0QK\", \"6q\", \"7Og\", \"4je\", \"4gU\", \"4r4\", \"2iX\", \"1LZ\", \"0rW\", \"Qe\", \"5L9\", \"4Iy\", \n\"4Fx\", \"5C8\", \"0h7\", \"1mw\", \"0Sz\", \"pH\", \"7MV\", \"4hT\", \"4ed\", \"6PG\", \"9p\", \"1Nk\", \"F5\", \"ST\", \"7nJ\", \"4KH\", \"7pH\", \"4UJ\", \"X7\", \"MV\", \n\"cz\", \"1Pi\", \"5kG\", \"6NE\", \"4c7\", \"4vV\", \"0Mx\", \"nJ\", \"0v5\", \"0cT\", \"4Xz\", \"6mY\", \"6bX\", \"5GZ\", \"0lU\", \"Og\", \"aK\", \"0By\", \"4yW\", \"4l6\", \n\"6AD\", \"40o\", \"0OI\", \"2zj\", \"BW\", \"W6\", \"4ZK\", \"6oh\", \"2hB\", \"k2\", \"4fO\", \"6Sl\", \"69H\", \"4Hc\", \"07E\", \"2Fn\", \"d5e\", \"83m\", \"4ES\", \"4P2\", \n\"a0F\", \"bQL\", \"0PQ\", \"7k\", \"8j\", \"1Oq\", \"50W\", \"hbv\", \"7oP\", \"4JR\", \"05t\", \"RN\", \"2Io\", \"08D\", \"4Gb\", \"66I\", \"7LL\", \"4iN\", \"d3\", \"5Z\", \n\"z1\", \"oP\", \"6Bo\", \"43D\", \"5IA\", \"6lC\", \"2Wm\", \"0bN\", \"8ff\", \"LL\", \"4A1\", \"4TP\", \"cPn\", \"aeM\", \"0T3\", \"0AR\", \"0NS\", \"ma\", \"545\", \"41u\", \n\"5Kp\", \"4N0\", \"CM\", \"14V\", \"0mO\", \"2Xl\", \"6cB\", \"4Va\", \"4xM\", \"6Mn\", \"22H\", \"18\", \"04s\", \"SI\", \"7nW\", \"4KU\", \"4ey\", \"6PZ\", \"9m\", \"1Nv\", \n\"e4\", \"pU\", \"7MK\", \"4hI\", \"4Fe\", \"67N\", \"2Hh\", \"09C\", \"06B\", \"Qx\", \"68O\", \"4Id\", \"4gH\", \"6Rk\", \"2iE\", \"j5\", \"0QV\", \"6l\", \"5o8\", \"4jx\", \n\"4DT\", \"4Q5\", \"2JY\", \"82j\", \"BJ\", \"0ax\", \"4ZV\", \"4O7\", \"552\", \"40r\", \"0OT\", \"lf\", \"aV\", \"t7\", \"4yJ\", \"6Li\", \"6bE\", \"4Wf\", \"0lH\", \"Oz\", \n\"2Vj\", \"0cI\", \"4Xg\", \"6mD\", \"6Ch\", \"42C\", \"0Me\", \"nW\", \"cg\", \"1Pt\", \"5kZ\", \"6NX\", \"7pU\", \"4UW\", \"0ny\", \"MK\", \"7LQ\", \"4iS\", \"267\", \"5G\", \n\"0i0\", \"08Y\", \"b9D\", \"66T\", \"7oM\", \"4JO\", \"G2\", \"RS\", \"8w\", \"1Ol\", \"4dc\", \"7Aa\", \"atS\", \"4kb\", \"0PL\", \"7v\", \"2KC\", \"H3\", \"4EN\", \"64e\", \n\"69U\", \"b6E\", \"07X\", \"Pb\", \"dRl\", \"296\", \"4fR\", \"4s3\", \"4xP\", \"4m1\", \"22U\", \"8Jf\", \"0mR\", \"0x3\", \"77v\", \"626\", \"5Km\", \"6no\", \"CP\", \"V1\", \n\"0NN\", \"3kL\", \"7Pb\", \"41h\", \"4za\", \"6OB\", \"20d\", \"0AO\", \"Y0\", \"LQ\", \"6an\", \"4TM\", \"bcN\", \"78w\", \"Aa\", \"0bS\", \"8Eg\", \"oM\", \"4b0\", \"43Y\", \n\"51T\", \"azL\", \"9i\", \"1Nr\", \"04w\", \"SM\", \"7nS\", \"4KQ\", \"4Fa\", \"67J\", \"2Hl\", \"09G\", \"e0\", \"4Y\", \"7MO\", \"4hM\", \"4gL\", \"6Ro\", \"2iA\", \"j1\", \n\"06F\", \"2Gm\", \"68K\", \"5YA\", \"4DP\", \"4Q1\", \"d4f\", \"82n\", \"0QR\", \"6h\", \"a1E\", \"bPO\", \"556\", \"40v\", \"0OP\", \"lb\", \"BN\", \"15U\", \"4ZR\", \"4O3\", \n\"6bA\", \"4Wb\", \"0lL\", \"2Yo\", \"aR\", \"t3\", \"4yN\", \"6Lm\", \"6Cl\", \"42G\", \"0Ma\", \"nS\", \"2Vn\", \"0cM\", \"4Xc\", \"79i\", \"74Y\", \"4US\", \"8ge\", \"MO\", \n\"cc\", \"1Pp\", \"bAL\", \"adN\", \"0i4\", \"1lt\", \"5WZ\", \"66P\", \"7LU\", \"4iW\", \"0Ry\", \"5C\", \"8s\", \"1Oh\", \"4dg\", \"6QD\", \"7oI\", \"4JK\", \"G6\", \"RW\", \n\"2KG\", \"H7\", \"4EJ\", \"64a\", \"7Nd\", \"4kf\", \"0PH\", \"7r\", \"1X8\", \"1MY\", \"4fV\", \"4s7\", \"69Q\", \"4Hz\", \"0sT\", \"Pf\", \"0mV\", \"Nd\", \"5S8\", \"4Vx\", \n\"4xT\", \"4m5\", \"22Q\", \"0Cz\", \"0NJ\", \"mx\", \"7Pf\", \"41l\", \"5Ki\", \"6nk\", \"CT\", \"V5\", \"Y4\", \"LU\", \"6aj\", \"4TI\", \"4ze\", \"6OF\", \"by\", \"0AK\", \n\"2l9\", \"oI\", \"4b4\", \"4wU\", \"4Yy\", \"6lZ\", \"Ae\", \"0bW\", \"0So\", \"4U\", \"7MC\", \"4hA\", \"4Fm\", \"67F\", \"3XA\", \"09K\", \"0ps\", \"SA\", \"aTl\", \"b5f\", \n\"4eq\", \"6PR\", \"9e\", \"8WG\", \"8XF\", \"6d\", \"5o0\", \"4jp\", \"707\", \"65w\", \"1z2\", \"1oS\", \"06J\", \"Qp\", \"68G\", \"4Il\", \"53i\", \"6Rc\", \"2iM\", \"1LO\", \n\"23G\", \"07\", \"4yB\", \"6La\", \"6bM\", \"4Wn\", \"18i\", \"Or\", \"BB\", \"0ap\", \"c4D\", \"aEo\", \"5q2\", \"40z\", \"8FD\", \"ln\", \"co\", \"346\", \"5kR\", \"6NP\", \n\"74U\", \"bol\", \"0nq\", \"MC\", \"2Vb\", \"0cA\", \"4Xo\", \"6mL\", \"7SA\", \"42K\", \"0Mm\", \"2xN\", \"7oE\", \"4JG\", \"05a\", \"2DJ\", \"2jf\", \"1Od\", \"4dk\", \"6QH\", \n\"482\", \"5yz\", \"0Ru\", \"5O\", \"0i8\", \"08Q\", \"4Gw\", \"5B7\", \"5M6\", \"4Hv\", \"07P\", \"Pj\", \"1X4\", \"1MU\", \"4fZ\", \"473\", \"7Nh\", \"4kj\", \"0PD\", \"sv\", \n\"2KK\", \"1nI\", \"4EF\", \"64m\", \"5Ke\", \"6ng\", \"CX\", \"V9\", \"0NF\", \"mt\", \"7Pj\", \"4uh\", \"4xX\", \"4m9\", \"1F6\", \"0Cv\", \"0mZ\", \"Nh\", \"5S4\", \"4Vt\", \n\"4Yu\", \"6lV\", \"Ai\", \"16r\", \"0Lw\", \"oE\", \"4b8\", \"43Q\", \"4zi\", \"6OJ\", \"bu\", \"0AG\", \"Y8\", \"LY\", \"6af\", \"4TE\", \"4Fi\", \"67B\", \"2Hd\", \"09O\", \n\"e8\", \"4Q\", \"7MG\", \"4hE\", \"4eu\", \"6PV\", \"9a\", \"1Nz\", \"0pw\", \"SE\", \"aTh\", \"4KY\", \"4DX\", \"4Q9\", \"1z6\", \"1oW\", \"0QZ\", \"rh\", \"5o4\", \"4jt\", \n\"4gD\", \"6Rg\", \"2iI\", \"j9\", \"06N\", \"Qt\", \"68C\", \"4Ih\", \"6bI\", \"4Wj\", \"0lD\", \"Ov\", \"aZ\", \"03\", \"4yF\", \"6Le\", \"5q6\", \"4tv\", \"0OX\", \"lj\", \n\"BF\", \"0at\", \"4ZZ\", \"6oy\", \"74Q\", \"5Ez\", \"0nu\", \"MG\", \"ck\", \"1Px\", \"5kV\", \"6NT\", \"6Cd\", \"42O\", \"0Mi\", \"2xJ\", \"2Vf\", \"0cE\", \"4Xk\", \"6mH\", \n\"2jb\", \"8VY\", \"4do\", \"6QL\", \"7oA\", \"4JC\", \"05e\", \"2DN\", \"d7E\", \"08U\", \"4Gs\", \"5B3\", \"486\", \"bSl\", \"0Rq\", \"5K\", \"1X0\", \"1MQ\", \"52w\", \"477\", \n\"5M2\", \"4Hr\", \"07T\", \"Pn\", \"2KO\", \"1nM\", \"4EB\", \"64i\", \"7Nl\", \"4kn\", \"8YX\", \"7z\", \"0NB\", \"mp\", \"7Pn\", \"41d\", \"5Ka\", \"6nc\", \"2UM\", \"14G\", \n\"19w\", \"Nl\", \"5S0\", \"4Vp\", \"bBo\", \"agm\", \"1F2\", \"0Cr\", \"0Ls\", \"oA\", \"ahl\", \"43U\", \"4Yq\", \"6lR\", \"Am\", \"16v\", \"0oo\", \"2ZL\", \"6ab\", \"4TA\", \n\"4zm\", \"6ON\", \"bq\", \"0AC\", \"2VY\", \"0cz\", \"4XT\", \"4M5\", \"570\", \"42p\", \"0MV\", \"nd\", \"cT\", \"v5\", \"5ki\", \"6Nk\", \"74n\", \"4Ud\", \"0nJ\", \"Mx\", \n\"By\", \"0aK\", \"4Ze\", \"6oF\", \"6Aj\", \"40A\", \"y4\", \"lU\", \"ae\", \"0BW\", \"4yy\", \"581\", \"4B4\", \"4WU\", \"18R\", \"OI\", \"06q\", \"QK\", \"7lU\", \"4IW\", \n\"53R\", \"6RX\", \"0I4\", \"1Lt\", \"g6\", \"rW\", \"7OI\", \"4jK\", \"4Dg\", \"65L\", \"2Jj\", \"1oh\", \"0pH\", \"Sz\", \"7nd\", \"4Kf\", \"4eJ\", \"6Pi\", \"2kG\", \"h7\", \n\"0ST\", \"4n\", \"7Mx\", \"4hz\", \"4FV\", \"4S7\", \"1x8\", \"09p\", \"4zR\", \"4o3\", \"bN\", \"8Hd\", \"0oP\", \"Lb\", \"75t\", \"604\", \"4YN\", \"6lm\", \"AR\", \"T3\", \n\"0LL\", \"2yo\", \"6BA\", \"43j\", \"4xc\", \"agR\", \"22f\", \"0CM\", \"0ma\", \"NS\", \"6cl\", \"4VO\", \"baL\", \"aDN\", \"Cc\", \"14x\", \"8Ge\", \"mO\", \"7PQ\", \"4uS\", \n\"7NS\", \"4kQ\", \"245\", \"7E\", \"0k2\", \"1nr\", \"coo\", \"64V\", \"69f\", \"4HM\", \"E0\", \"PQ\", \"2hl\", \"1Mn\", \"4fa\", \"6SB\", \"7Lb\", \"5yA\", \"0RN\", \"5t\", \n\"2IA\", \"J1\", \"4GL\", \"66g\", \"aUM\", \"b4G\", \"05Z\", \"0d3\", \"8D\", \"8Vf\", \"4dP\", \"459\", \"574\", \"42t\", \"0MR\", \"0X3\", \"dln\", \"17W\", \"4XP\", \"4M1\", \n\"74j\", \"5EA\", \"0nN\", \"3KL\", \"cP\", \"29\", \"5km\", \"6No\", \"6An\", \"40E\", \"y0\", \"lQ\", \"2Tl\", \"0aO\", \"4Za\", \"6oB\", \"4B0\", \"4WQ\", \"18V\", \"OM\", \n\"aa\", \"0BS\", \"bCN\", \"585\", \"53V\", \"axN\", \"0I0\", \"1Lp\", \"06u\", \"QO\", \"68x\", \"4IS\", \"4Dc\", \"65H\", \"2Jn\", \"1ol\", \"g2\", \"rS\", \"7OM\", \"4jO\", \n\"4eN\", \"6Pm\", \"9Z\", \"h3\", \"04D\", \"2Eo\", \"aTS\", \"4Kb\", \"4FR\", \"4S3\", \"d6d\", \"09t\", \"0SP\", \"4j\", \"a3G\", \"bRM\", \"0oT\", \"Lf\", \"6aY\", \"4Tz\", \n\"4zV\", \"4o7\", \"bJ\", \"0Ax\", \"0LH\", \"oz\", \"6BE\", \"43n\", \"4YJ\", \"6li\", \"AV\", \"T7\", \"0me\", \"NW\", \"6ch\", \"4VK\", \"4xg\", \"6MD\", \"22b\", \"0CI\", \n\"0Ny\", \"mK\", \"7PU\", \"4uW\", \"5KZ\", \"6nX\", \"Cg\", \"1pt\", \"0k6\", \"1nv\", \"4Ey\", \"64R\", \"7NW\", \"4kU\", \"241\", \"7A\", \"2hh\", \"1Mj\", \"4fe\", \"6SF\", \n\"69b\", \"4HI\", \"E4\", \"PU\", \"2IE\", \"J5\", \"4GH\", \"66c\", \"7Lf\", \"4id\", \"0RJ\", \"5p\", \"2jY\", \"8Vb\", \"4dT\", \"4q5\", \"5O8\", \"4Jx\", \"0qV\", \"Rd\", \n\"21E\", \"25\", \"5ka\", \"6Nc\", \"74f\", \"4Ul\", \"0nB\", \"Mp\", \"1f2\", \"0cr\", \"bbo\", \"79V\", \"578\", \"42x\", \"395\", \"nl\", \"am\", \"364\", \"4yq\", \"589\", \n\"76W\", \"bmn\", \"0ls\", \"OA\", \"Bq\", \"0aC\", \"4Zm\", \"6oN\", \"6Ab\", \"40I\", \"0Oo\", \"2zL\", \"0Qm\", \"6W\", \"7OA\", \"4jC\", \"4Do\", \"65D\", \"2Jb\", \"82Q\", \n\"06y\", \"QC\", \"68t\", \"b7d\", \"4gs\", \"5b3\", \"dSM\", \"8UE\", \"8ZD\", \"4f\", \"5m2\", \"4hr\", \"725\", \"67u\", \"1x0\", \"09x\", \"04H\", \"Sr\", \"7nl\", \"4Kn\", \n\"4eB\", \"6Pa\", \"9V\", \"1NM\", \"4YF\", \"6le\", \"AZ\", \"0bh\", \"0LD\", \"ov\", \"6BI\", \"43b\", \"4zZ\", \"6Oy\", \"bF\", \"0At\", \"0oX\", \"Lj\", \"5Q6\", \"4Tv\", \n\"5KV\", \"6nT\", \"Ck\", \"14p\", \"0Nu\", \"mG\", \"7PY\", \"41S\", \"4xk\", \"6MH\", \"22n\", \"0CE\", \"0mi\", \"2XJ\", \"6cd\", \"4VG\", \"69n\", \"4HE\", \"E8\", \"PY\", \n\"2hd\", \"1Mf\", \"4fi\", \"6SJ\", \"ath\", \"4kY\", \"0Pw\", \"7M\", \"2Kx\", \"1nz\", \"4Eu\", \"6pV\", \"5O4\", \"4Jt\", \"05R\", \"Rh\", \"8L\", \"1OW\", \"4dX\", \"451\", \n\"7Lj\", \"4ih\", \"0RF\", \"qt\", \"2II\", \"J9\", \"4GD\", \"66o\", \"74b\", \"4Uh\", \"0nF\", \"Mt\", \"cX\", \"21\", \"5ke\", \"6Ng\", \"5s4\", \"4vt\", \"0MZ\", \"nh\", \n\"1f6\", \"0cv\", \"4XX\", \"4M9\", \"4B8\", \"4WY\", \"0lw\", \"OE\", \"ai\", \"1Rz\", \"4yu\", \"6LV\", \"6Af\", \"40M\", \"y8\", \"lY\", \"Bu\", \"0aG\", \"4Zi\", \"6oJ\", \n\"4Dk\", \"6qH\", \"2Jf\", \"1od\", \"0Qi\", \"6S\", \"7OE\", \"4jG\", \"4gw\", \"5b7\", \"0I8\", \"1Lx\", \"0ru\", \"QG\", \"68p\", \"5Yz\", \"4FZ\", \"67q\", \"1x4\", \"1mU\", \n\"0SX\", \"4b\", \"5m6\", \"4hv\", \"4eF\", \"6Pe\", \"9R\", \"1NI\", \"04L\", \"Sv\", \"7nh\", \"4Kj\", \"8EX\", \"or\", \"6BM\", \"43f\", \"4YB\", \"6la\", \"2WO\", \"0bl\", \n\"8fD\", \"Ln\", \"5Q2\", \"4Tr\", \"cPL\", \"aeo\", \"bB\", \"0Ap\", \"0Nq\", \"mC\", \"ajn\", \"41W\", \"5KR\", \"6nP\", \"Co\", \"14t\", \"0mm\", \"2XN\", \"77I\", \"4VC\", \n\"4xo\", \"6ML\", \"22j\", \"0CA\", \"3xA\", \"1Mb\", \"4fm\", \"6SN\", \"69j\", \"4HA\", \"07g\", \"2FL\", \"d5G\", \"83O\", \"4Eq\", \"64Z\", \"a0d\", \"bQn\", \"0Ps\", \"7I\", \n\"8H\", \"1OS\", \"50u\", \"455\", \"5O0\", \"4Jp\", \"05V\", \"Rl\", \"2IM\", \"08f\", \"5Wa\", \"66k\", \"7Ln\", \"4il\", \"0RB\", \"5x\", \"Bh\", \"0aZ\", \"4Zt\", \"6oW\", \n\"4a9\", \"40P\", \"0Ov\", \"lD\", \"at\", \"0BF\", \"4yh\", \"6LK\", \"6bg\", \"4WD\", \"Z9\", \"OX\", \"2VH\", \"U8\", \"4XE\", \"6mf\", \"6CJ\", \"42a\", \"0MG\", \"nu\", \n\"cE\", \"1PV\", \"5kx\", \"4n8\", \"5P5\", \"4Uu\", \"8gC\", \"Mi\", \"04Q\", \"Sk\", \"5N7\", \"4Kw\", \"51r\", \"442\", \"9O\", \"1NT\", \"0SE\", \"pw\", \"7Mi\", \"4hk\", \n\"4FG\", \"67l\", \"2HJ\", \"09a\", \"3\", \"QZ\", \"68m\", \"4IF\", \"4gj\", \"6RI\", \"2ig\", \"1Le\", \"0Qt\", \"6N\", \"7OX\", \"4jZ\", \"4Dv\", \"5A6\", \"0j9\", \"1oy\", \n\"4xr\", \"6MQ\", \"22w\", \"377\", \"0mp\", \"NB\", \"77T\", \"blm\", \"5KO\", \"6nM\", \"Cr\", \"14i\", \"0Nl\", \"3kn\", \"ajs\", \"41J\", \"4zC\", \"aer\", \"20F\", \"36\", \n\"0oA\", \"Ls\", \"6aL\", \"4To\", \"bcl\", \"78U\", \"AC\", \"0bq\", \"386\", \"oo\", \"5r3\", \"4ws\", \"5l1\", \"4iq\", \"9Kf\", \"5e\", \"1y3\", \"1lR\", \"736\", \"66v\", \n\"7oo\", \"4Jm\", \"05K\", \"Rq\", \"8U\", \"1ON\", \"4dA\", \"6Qb\", \"7NB\", \"bQs\", \"0Pn\", \"7T\", \"2Ka\", \"1nc\", \"4El\", \"64G\", \"69w\", \"b6g\", \"07z\", \"1v2\", \n\"dRN\", \"8TF\", \"4fp\", \"5c0\", \"akm\", \"40T\", \"0Or\", \"1J2\", \"Bl\", \"15w\", \"4Zp\", \"6oS\", \"6bc\", \"5Ga\", \"0ln\", \"2YM\", \"ap\", \"0BB\", \"4yl\", \"6LO\", \n\"6CN\", \"42e\", \"0MC\", \"nq\", \"2VL\", \"0co\", \"4XA\", \"6mb\", \"5P1\", \"4Uq\", \"8gG\", \"Mm\", \"cA\", \"1PR\", \"bAn\", \"adl\", \"51v\", \"446\", \"9K\", \"1NP\", \n\"04U\", \"So\", \"5N3\", \"4Ks\", \"4FC\", \"67h\", \"2HN\", \"09e\", \"0SA\", \"ps\", \"7Mm\", \"4ho\", \"4gn\", \"6RM\", \"2ic\", \"1La\", \"7\", \"2GO\", \"68i\", \"4IB\", \n\"4Dr\", \"5A2\", \"d4D\", \"82L\", \"0Qp\", \"6J\", \"a1g\", \"bPm\", \"0mt\", \"NF\", \"6cy\", \"4VZ\", \"4xv\", \"6MU\", \"0V9\", \"0CX\", \"0Nh\", \"mZ\", \"7PD\", \"41N\", \n\"5KK\", \"6nI\", \"Cv\", \"14m\", \"0oE\", \"Lw\", \"6aH\", \"4Tk\", \"4zG\", \"6Od\", \"20B\", \"32\", \"0LY\", \"ok\", \"5r7\", \"4ww\", \"5Iz\", \"6lx\", \"AG\", \"0bu\", \n\"1y7\", \"1lV\", \"4GY\", \"4R8\", \"5l5\", \"4iu\", \"1Bz\", \"5a\", \"8Q\", \"i8\", \"4dE\", \"6Qf\", \"7ok\", \"4Ji\", \"05O\", \"Ru\", \"2Ke\", \"1ng\", \"4Eh\", \"64C\", \n\"7NF\", \"4kD\", \"f9\", \"7P\", \"2hy\", \"3m9\", \"4ft\", \"5c4\", \"69s\", \"4HX\", \"0sv\", \"PD\", \"23e\", \"0BN\", \"5iA\", \"6LC\", \"6bo\", \"4WL\", \"Z1\", \"OP\", \n\"0t3\", \"0aR\", \"c4f\", \"aEM\", \"4a1\", \"40X\", \"8Ff\", \"lL\", \"cM\", \"8Ig\", \"5kp\", \"4n0\", \"74w\", \"617\", \"0nS\", \"Ma\", \"3Fa\", \"U0\", \"4XM\", \"6mn\", \n\"6CB\", \"42i\", \"0MO\", \"2xl\", \"0SM\", \"4w\", \"7Ma\", \"4hc\", \"4FO\", \"67d\", \"2HB\", \"K2\", \"04Y\", \"Sc\", \"aTN\", \"b5D\", \"4eS\", \"4p2\", \"9G\", \"8We\", \n\"256\", \"6F\", \"7OP\", \"4jR\", \"cnl\", \"65U\", \"0j1\", \"1oq\", \"D3\", \"QR\", \"68e\", \"4IN\", \"4gb\", \"6RA\", \"2io\", \"1Lm\", \"5KG\", \"6nE\", \"Cz\", \"14a\", \n\"x7\", \"mV\", \"7PH\", \"41B\", \"4xz\", \"592\", \"0V5\", \"0CT\", \"0mx\", \"NJ\", \"4C7\", \"4VV\", \"4YW\", \"4L6\", \"AK\", \"0by\", \"0LU\", \"og\", \"563\", \"43s\", \n\"4zK\", \"6Oh\", \"bW\", \"w6\", \"0oI\", \"2Zj\", \"6aD\", \"4Tg\", \"7og\", \"4Je\", \"05C\", \"Ry\", \"2jD\", \"i4\", \"4dI\", \"6Qj\", \"5l9\", \"4iy\", \"0RW\", \"5m\", \n\"2IX\", \"08s\", \"4GU\", \"4R4\", \"7mV\", \"4HT\", \"07r\", \"PH\", \"0H7\", \"1Mw\", \"4fx\", \"5c8\", \"7NJ\", \"4kH\", \"f5\", \"sT\", \"2Ki\", \"1nk\", \"4Ed\", \"64O\", \n\"6bk\", \"4WH\", \"Z5\", \"OT\", \"ax\", \"0BJ\", \"4yd\", \"6LG\", \"4a5\", \"4tT\", \"0Oz\", \"lH\", \"Bd\", \"0aV\", \"4Zx\", \"aEI\", \"5P9\", \"4Uy\", \"0nW\", \"Me\", \n\"cI\", \"1PZ\", \"5kt\", \"4n4\", \"6CF\", \"42m\", \"0MK\", \"ny\", \"2VD\", \"U4\", \"4XI\", \"6mj\", \"4FK\", \"6sh\", \"2HF\", \"K6\", \"0SI\", \"4s\", \"7Me\", \"4hg\", \n\"4eW\", \"4p6\", \"9C\", \"1NX\", \"0pU\", \"Sg\", \"7ny\", \"6k9\", \"4Dz\", \"65Q\", \"0j5\", \"1ou\", \"0Qx\", \"6B\", \"7OT\", \"4jV\", \"4gf\", \"6RE\", \"2ik\", \"1Li\", \n\"D7\", \"QV\", \"68a\", \"4IJ\", \"x3\", \"mR\", \"7PL\", \"41F\", \"5KC\", \"6nA\", \"2Uo\", \"14e\", \"19U\", \"NN\", \"4C3\", \"4VR\", \"bBM\", \"596\", \"0V1\", \"0CP\", \n\"0LQ\", \"oc\", \"567\", \"43w\", \"4YS\", \"4L2\", \"AO\", \"16T\", \"0oM\", \"2Zn\", \"75i\", \"4Tc\", \"4zO\", \"6Ol\", \"bS\", \"w2\", \"8Y\", \"i0\", \"4dM\", \"6Qn\", \n\"7oc\", \"4Ja\", \"05G\", \"2Dl\", \"d7g\", \"08w\", \"4GQ\", \"4R0\", \"a2D\", \"bSN\", \"0RS\", \"5i\", \"0H3\", \"1Ms\", \"52U\", \"ayM\", \"7mR\", \"4HP\", \"07v\", \"PL\", \n\"2Km\", \"1no\", \"5UA\", \"64K\", \"7NN\", \"4kL\", \"f1\", \"7X\", \"5nw\", \"4k7\", \"fJ\", \"0Ex\", \"0kT\", \"Hf\", \"6eY\", \"4Pz\", \"5Mk\", \"6hi\", \"EV\", \"P7\", \n\"0HH\", \"kz\", \"6FE\", \"47n\", \"48o\", \"6ID\", \"26b\", \"0GI\", \"0ie\", \"JW\", \"6gh\", \"4RK\", \"5OZ\", \"6jX\", \"Gg\", \"0dU\", \"0Jy\", \"iK\", \"4d6\", \"4qW\", \n\"4z4\", \"4oU\", \"1DZ\", \"3A\", \"Ye\", \"0zW\", \"4Ay\", \"5D9\", \"6yj\", \"4LI\", \"A4\", \"TU\", \"zy\", \"0YK\", \"4be\", \"6WF\", \"6XG\", \"4md\", \"0VJ\", \"1p\", \n\"2ME\", \"N5\", \"4CH\", \"62c\", \"5K8\", \"4Nx\", \"0uV\", \"Vd\", \"xH\", \"8Rb\", \"5pu\", \"4u5\", \"D\", \"13W\", \"5Lq\", \"4I1\", \"534\", \"46t\", \"0IR\", \"28y\", \n\"gP\", \"69\", \"5om\", \"6Jo\", \"6dC\", \"5AA\", \"0jN\", \"3OL\", \"2Pl\", \"0eO\", \"aT1\", \"6kB\", \"6En\", \"44E\", \"98\", \"hQ\", \"ea\", \"0FS\", \"49u\", \"abL\", \n\"4F0\", \"4SQ\", \"8ag\", \"KM\", \"02u\", \"UO\", \"4X2\", \"4MS\", \"57V\", \"a8F\", \"0M0\", \"0XQ\", \"c2\", \"vS\", \"7KM\", \"4nO\", \"5PB\", \"61H\", \"2Nn\", \"1kl\", \n\"00D\", \"2Ao\", \"6zA\", \"4Ob\", \"4aN\", \"6Tm\", \"yR\", \"l3\", \"0WP\", \"0j\", \"a7G\", \"58W\", \"4BR\", \"4W3\", \"ZN\", \"84l\", \"0kP\", \"Hb\", \"71t\", \"644\", \n\"5ns\", \"4k3\", \"fN\", \"8Ld\", \"0HL\", \"29g\", \"6FA\", \"47j\", \"5Mo\", \"6hm\", \"ER\", \"P3\", \"0ia\", \"JS\", \"6gl\", \"4RO\", \"48k\", \"7Ya\", \"26f\", \"0GM\", \n\"8Ce\", \"iO\", \"4d2\", \"4qS\", \"beL\", \"hYw\", \"Gc\", \"0dQ\", \"Ya\", \"0zS\", \"cko\", \"60V\", \"4z0\", \"4oQ\", \"205\", \"3E\", \"2ll\", \"0YO\", \"4ba\", \"6WB\", \n\"6yn\", \"4LM\", \"A0\", \"TQ\", \"2MA\", \"N1\", \"4CL\", \"62g\", \"6XC\", \"59I\", \"0VN\", \"1t\", \"xL\", \"8Rf\", \"54y\", \"419\", \"aQM\", \"b0G\", \"01Z\", \"3PP\", \n\"530\", \"46p\", \"0IV\", \"jd\", \"DH\", \"0gz\", \"5Lu\", \"4I5\", \"6dG\", \"4Qd\", \"0jJ\", \"Ix\", \"gT\", \"r5\", \"5oi\", \"6Jk\", \"6Ej\", \"44A\", \"0Kg\", \"hU\", \n\"Fy\", \"0eK\", \"5ND\", \"6kF\", \"4F4\", \"4SU\", \"1xZ\", \"KI\", \"ee\", \"0FW\", \"49q\", \"5x9\", \"57R\", \"6VX\", \"0M4\", \"0XU\", \"02q\", \"UK\", \"4X6\", \"4MW\", \n\"5PF\", \"61L\", \"2Nj\", \"1kh\", \"c6\", \"vW\", \"7KI\", \"4nK\", \"4aJ\", \"6Ti\", \"yV\", \"l7\", \"0tH\", \"Wz\", \"6zE\", \"4Of\", \"4BV\", \"4W7\", \"ZJ\", \"0yx\", \n\"0WT\", \"0n\", \"6YY\", \"4lz\", \"5Mc\", \"6ha\", \"2SO\", \"0fl\", \"1Xa\", \"kr\", \"6FM\", \"47f\", \"bDm\", \"aao\", \"fB\", \"0Ep\", \"8bD\", \"Hn\", \"5U2\", \"4Pr\", \n\"5OR\", \"5Z3\", \"Go\", \"10t\", \"0Jq\", \"iC\", \"ann\", \"45W\", \"48g\", \"6IL\", \"ds\", \"0GA\", \"0im\", \"3Lo\", \"73I\", \"4RC\", \"6yb\", \"4LA\", \"03g\", \"2BL\", \n\"zq\", \"0YC\", \"4bm\", \"6WN\", \"a4d\", \"bUn\", \"0Ts\", \"3I\", \"Ym\", \"87O\", \"4Aq\", \"5D1\", \"5K0\", \"4Np\", \"01V\", \"Vl\", \"2nQ\", \"1KS\", \"54u\", \"415\", \n\"6XO\", \"4ml\", \"0VB\", \"1x\", \"2MM\", \"0xn\", \"5Sa\", \"62k\", \"gX\", \"61\", \"5oe\", \"6Jg\", \"6dK\", \"4Qh\", \"0jF\", \"It\", \"L\", \"0gv\", \"5Ly\", \"4I9\", \n\"5w4\", \"4rt\", \"0IZ\", \"jh\", \"ei\", \"1Vz\", \"5mT\", \"5x5\", \"4F8\", \"4SY\", \"0hw\", \"KE\", \"Fu\", \"0eG\", \"5NH\", \"6kJ\", \"6Ef\", \"44M\", \"90\", \"hY\", \n\"0Ui\", \"2S\", \"7KE\", \"4nG\", \"5PJ\", \"6uH\", \"Xw\", \"1kd\", \"0vu\", \"UG\", \"6xx\", \"790\", \"4cw\", \"5f7\", \"0M8\", \"0XY\", \"0WX\", \"0b\", \"5i6\", \"4lv\", \n\"4BZ\", \"63q\", \"ZF\", \"0yt\", \"00L\", \"Wv\", \"6zI\", \"4Oj\", \"4aF\", \"6Te\", \"yZ\", \"0Zh\", \"0HD\", \"kv\", \"6FI\", \"47b\", \"5Mg\", \"6he\", \"EZ\", \"0fh\", \n\"0kX\", \"Hj\", \"5U6\", \"4Pv\", \"7N9\", \"6Ky\", \"fF\", \"0Et\", \"0Ju\", \"iG\", \"6Dx\", \"45S\", \"5OV\", \"5Z7\", \"Gk\", \"0dY\", \"0ii\", \"3Lk\", \"6gd\", \"4RG\", \n\"48c\", \"6IH\", \"dw\", \"0GE\", \"zu\", \"0YG\", \"4bi\", \"6WJ\", \"6yf\", \"4LE\", \"A8\", \"TY\", \"Yi\", \"1jz\", \"4Au\", \"5D5\", \"4z8\", \"4oY\", \"0Tw\", \"3M\", \n\"xD\", \"1KW\", \"54q\", \"411\", \"5K4\", \"4Nt\", \"01R\", \"Vh\", \"2MI\", \"N9\", \"4CD\", \"62o\", \"6XK\", \"4mh\", \"0VF\", \"ut\", \"6dO\", \"4Ql\", \"0jB\", \"Ip\", \n\"25E\", \"65\", \"5oa\", \"6Jc\", \"538\", \"46x\", \"9Pg\", \"jl\", \"H\", \"0gr\", \"bfo\", \"aCm\", \"72W\", \"bin\", \"0hs\", \"KA\", \"em\", \"324\", \"49y\", \"5x1\", \n\"6Eb\", \"44I\", \"94\", \"3nm\", \"Fq\", \"0eC\", \"5NL\", \"6kN\", \"5PN\", \"61D\", \"Xs\", \"86Q\", \"0Um\", \"2W\", \"7KA\", \"4nC\", \"4cs\", \"5f3\", \"39W\", \"8QE\", \n\"02y\", \"UC\", \"aRn\", \"794\", \"765\", \"63u\", \"ZB\", \"0yp\", \"9Ne\", \"0f\", \"5i2\", \"4lr\", \"4aB\", \"6Ta\", \"2oO\", \"0Zl\", \"00H\", \"Wr\", \"6zM\", \"4On\", \n\"5lW\", \"5y6\", \"dj\", \"0GX\", \"0it\", \"JF\", \"6gy\", \"4RZ\", \"5OK\", \"6jI\", \"Gv\", \"0dD\", \"83\", \"iZ\", \"6De\", \"45N\", \"5nf\", \"6Kd\", \"24B\", \"72\", \n\"0kE\", \"Hw\", \"6eH\", \"4Pk\", \"5Mz\", \"6hx\", \"EG\", \"0fu\", \"0HY\", \"kk\", \"5v7\", \"4sw\", \"5h5\", \"4mu\", \"1Fz\", \"1a\", \"2MT\", \"0xw\", \"4CY\", \"4V8\", \n\"7kk\", \"4Ni\", \"01O\", \"Vu\", \"xY\", \"m8\", \"54l\", \"6Uf\", \"6Zg\", \"4oD\", \"b9\", \"3P\", \"Yt\", \"0zF\", \"4Ah\", \"60C\", \"4Y9\", \"4LX\", \"0wv\", \"TD\", \n\"zh\", \"0YZ\", \"4bt\", \"5g4\", \"Fl\", \"11w\", \"5NQ\", \"6kS\", \"aom\", \"44T\", \"0Kr\", \"1N2\", \"ep\", \"0FB\", \"49d\", \"6HO\", \"6fc\", \"5Ca\", \"0hn\", \"3Ml\", \n\"U\", \"0go\", \"bfr\", \"6ib\", \"6GN\", \"46e\", \"0IC\", \"jq\", \"gA\", \"0Ds\", \"bEn\", \"hyU\", \"5T1\", \"4Qq\", \"8cG\", \"Im\", \"00U\", \"Wo\", \"5J3\", \"4Os\", \n\"55v\", \"406\", \"yC\", \"0Zq\", \"0WA\", \"ts\", \"6YL\", \"4lo\", \"4BC\", \"63h\", \"2LN\", \"0ym\", \"02d\", \"2CO\", \"6xa\", \"4MB\", \"4cn\", \"6VM\", \"2mc\", \"1Ha\", \n\"0Up\", \"2J\", \"a5g\", \"bTm\", \"5PS\", \"5E2\", \"Xn\", \"86L\", \"0ip\", \"JB\", \"73T\", \"bhm\", \"48z\", \"5y2\", \"dn\", \"337\", \"87\", \"3on\", \"6Da\", \"45J\", \n\"5OO\", \"6jM\", \"Gr\", \"10i\", \"0kA\", \"Hs\", \"6eL\", \"4Po\", \"5nb\", \"aar\", \"24F\", \"76\", \"8AE\", \"ko\", \"5v3\", \"4ss\", \"bgl\", \"aBn\", \"EC\", \"0fq\", \n\"2MP\", \"0xs\", \"776\", \"62v\", \"5h1\", \"4mq\", \"9Of\", \"1e\", \"2nL\", \"1KN\", \"54h\", \"6Ub\", \"7ko\", \"4Nm\", \"01K\", \"Vq\", \"Yp\", \"0zB\", \"4Al\", \"60G\", \n\"6Zc\", \"bUs\", \"0Tn\", \"3T\", \"zl\", \"8PF\", \"4bp\", \"5g0\", \"aSm\", \"787\", \"03z\", \"1r2\", \"4e9\", \"44P\", \"0Kv\", \"hD\", \"Fh\", \"0eZ\", \"5NU\", \"6kW\", \n\"6fg\", \"4SD\", \"0hj\", \"KX\", \"et\", \"0FF\", \"5mI\", \"6HK\", \"6GJ\", \"46a\", \"0IG\", \"ju\", \"Q\", \"Q8\", \"5Ld\", \"6if\", \"5T5\", \"4Qu\", \"1zz\", \"Ii\", \n\"gE\", \"0Dw\", \"5ox\", \"4j8\", \"55r\", \"402\", \"yG\", \"0Zu\", \"00Q\", \"Wk\", \"5J7\", \"4Ow\", \"4BG\", \"63l\", \"2LJ\", \"0yi\", \"0WE\", \"tw\", \"6YH\", \"4lk\", \n\"4cj\", \"6VI\", \"2mg\", \"0XD\", \"0vh\", \"UZ\", \"6xe\", \"4MF\", \"5PW\", \"5E6\", \"Xj\", \"1ky\", \"0Ut\", \"2N\", \"7KX\", \"4nZ\", \"5OC\", \"6jA\", \"2Qo\", \"0dL\", \n\"1ZA\", \"iR\", \"6Dm\", \"45F\", \"48v\", \"acO\", \"db\", \"0GP\", \"94M\", \"JN\", \"4G3\", \"4RR\", \"5Mr\", \"4H2\", \"EO\", \"12T\", \"0HQ\", \"kc\", \"527\", \"47w\", \n\"5nn\", \"6Kl\", \"fS\", \"s2\", \"0kM\", \"3NO\", \"71i\", \"4Pc\", \"7kc\", \"4Na\", \"01G\", \"3PM\", \"xQ\", \"m0\", \"54d\", \"6Un\", \"a6D\", \"59T\", \"0VS\", \"1i\", \n\"197\", \"85o\", \"4CQ\", \"4V0\", \"4Y1\", \"4LP\", \"03v\", \"TL\", \"0L3\", \"0YR\", \"56U\", \"a9E\", \"6Zo\", \"4oL\", \"b1\", \"3X\", \"2Om\", \"0zN\", \"5QA\", \"60K\", \n\"ex\", \"0FJ\", \"49l\", \"6HG\", \"6fk\", \"4SH\", \"0hf\", \"KT\", \"Fd\", \"0eV\", \"5NY\", \"aAI\", \"4e5\", \"4pT\", \"0Kz\", \"hH\", \"gI\", \"1TZ\", \"5ot\", \"4j4\", \n\"5T9\", \"4Qy\", \"0jW\", \"Ie\", \"DU\", \"Q4\", \"5Lh\", \"6ij\", \"6GF\", \"46m\", \"0IK\", \"jy\", \"0WI\", \"0s\", \"6YD\", \"4lg\", \"4BK\", \"6wh\", \"ZW\", \"O6\", \n\"0tU\", \"Wg\", \"6zX\", \"6o9\", \"4aW\", \"4t6\", \"yK\", \"0Zy\", \"0Ux\", \"2B\", \"7KT\", \"4nV\", \"bzI\", \"61Q\", \"Xf\", \"1ku\", \"02l\", \"UV\", \"6xi\", \"4MJ\", \n\"4cf\", \"6VE\", \"2mk\", \"0XH\", \"0Jd\", \"iV\", \"6Di\", \"45B\", \"5OG\", \"6jE\", \"Gz\", \"0dH\", \"0ix\", \"JJ\", \"4G7\", \"4RV\", \"48r\", \"6IY\", \"df\", \"0GT\", \n\"0HU\", \"kg\", \"523\", \"47s\", \"5Mv\", \"4H6\", \"EK\", \"0fy\", \"0kI\", \"3NK\", \"6eD\", \"4Pg\", \"5nj\", \"6Kh\", \"fW\", \"s6\", \"xU\", \"m4\", \"5ph\", \"6Uj\", \n\"7kg\", \"4Ne\", \"01C\", \"Vy\", \"193\", \"1hZ\", \"4CU\", \"4V4\", \"5h9\", \"4my\", \"0VW\", \"1m\", \"zd\", \"0YV\", \"4bx\", \"5g8\", \"4Y5\", \"4LT\", \"03r\", \"TH\", \n\"Yx\", \"0zJ\", \"4Ad\", \"60O\", \"6Zk\", \"4oH\", \"b5\", \"wT\", \"6fo\", \"4SL\", \"0hb\", \"KP\", \"27e\", \"0FN\", \"49h\", \"6HC\", \"4e1\", \"44X\", \"8Bf\", \"hL\", \n\"0p3\", \"0eR\", \"bdO\", \"aAM\", \"70w\", \"657\", \"0jS\", \"Ia\", \"gM\", \"8Mg\", \"5op\", \"4j0\", \"6GB\", \"46i\", \"0IO\", \"28d\", \"Y\", \"Q0\", \"5Ll\", \"6in\", \n\"4BO\", \"63d\", \"ZS\", \"O2\", \"0WM\", \"0w\", \"7Ia\", \"4lc\", \"4aS\", \"4t2\", \"yO\", \"8Se\", \"00Y\", \"Wc\", \"aPN\", \"b1D\", \"bzM\", \"61U\", \"Xb\", \"1kq\", \n\"216\", \"2F\", \"7KP\", \"4nR\", \"4cb\", \"6VA\", \"2mo\", \"0XL\", \"02h\", \"UR\", \"6xm\", \"4MN\", \"5j7\", \"4ow\", \"0TY\", \"3c\", \"YG\", \"0zu\", \"5Qz\", \"60p\", \n\"6yH\", \"4Lk\", \"03M\", \"Tw\", \"2lJ\", \"0Yi\", \"4bG\", \"6Wd\", \"6Xe\", \"4mF\", \"0Vh\", \"1R\", \"2Mg\", \"0xD\", \"4Cj\", \"62A\", \"7kX\", \"4NZ\", \"0ut\", \"VF\", \n\"xj\", \"1Ky\", \"5pW\", \"5e6\", \"5nU\", \"6KW\", \"fh\", \"0EZ\", \"0kv\", \"HD\", \"4E9\", \"4PX\", \"5MI\", \"6hK\", \"Et\", \"0fF\", \"0Hj\", \"kX\", \"6Fg\", \"47L\", \n\"48M\", \"6If\", \"dY\", \"50\", \"0iG\", \"Ju\", \"6gJ\", \"4Ri\", \"5Ox\", \"4J8\", \"GE\", \"0dw\", \"1Zz\", \"ii\", \"5t5\", \"4qu\", \"02W\", \"Um\", \"5H1\", \"4Mq\", \n\"57t\", \"424\", \"2mP\", \"0Xs\", \"0UC\", \"2y\", \"7Ko\", \"4nm\", \"bzr\", \"61j\", \"2NL\", \"1kN\", \"00f\", \"2AM\", \"6zc\", \"bus\", \"4al\", \"6TO\", \"yp\", \"0ZB\", \n\"0Wr\", \"0H\", \"a7e\", \"58u\", \"4Bp\", \"5G0\", \"Zl\", \"84N\", \"f\", \"13u\", \"5LS\", \"5Y2\", \"amo\", \"46V\", \"0Ip\", \"jB\", \"gr\", \"1Ta\", \"5oO\", \"6JM\", \n\"6da\", \"4QB\", \"0jl\", \"3On\", \"2PN\", \"0em\", \"5Nb\", \"aAr\", \"6EL\", \"44g\", \"0KA\", \"hs\", \"eC\", \"0Fq\", \"49W\", \"abn\", \"5V3\", \"4Ss\", \"8aE\", \"Ko\", \n\"YC\", \"0zq\", \"754\", \"60t\", \"5j3\", \"4os\", \"9Md\", \"3g\", \"2lN\", \"0Ym\", \"4bC\", \"7GA\", \"6yL\", \"4Lo\", \"03I\", \"Ts\", \"2Mc\", \"1ha\", \"4Cn\", \"62E\", \n\"6Xa\", \"4mB\", \"0Vl\", \"1V\", \"xn\", \"8RD\", \"5pS\", \"5e2\", \"aQo\", \"b0e\", \"01x\", \"VB\", \"0kr\", \"1n2\", \"71V\", \"bjo\", \"5nQ\", \"6KS\", \"fl\", \"315\", \n\"0Hn\", \"29E\", \"6Fc\", \"47H\", \"5MM\", \"6hO\", \"Ep\", \"0fB\", \"0iC\", \"Jq\", \"6gN\", \"4Rm\", \"48I\", \"6Ib\", \"26D\", \"54\", \"8CG\", \"im\", \"509\", \"45y\", \n\"ben\", \"hYU\", \"GA\", \"0ds\", \"4cY\", \"420\", \"2mT\", \"0Xw\", \"02S\", \"Ui\", \"5H5\", \"4Mu\", \"5Pd\", \"61n\", \"XY\", \"M8\", \"0UG\", \"vu\", \"7Kk\", \"4ni\", \n\"4ah\", \"6TK\", \"yt\", \"0ZF\", \"B9\", \"WX\", \"6zg\", \"4OD\", \"4Bt\", \"5G4\", \"Zh\", \"0yZ\", \"0Wv\", \"0L\", \"4y9\", \"4lX\", \"6Gy\", \"46R\", \"0It\", \"jF\", \n\"b\", \"0gX\", \"5LW\", \"5Y6\", \"6de\", \"4QF\", \"0jh\", \"IZ\", \"gv\", \"0DD\", \"5oK\", \"6JI\", \"6EH\", \"44c\", \"0KE\", \"hw\", \"2PJ\", \"0ei\", \"5Nf\", \"6kd\", \n\"5V7\", \"4Sw\", \"0hY\", \"Kk\", \"eG\", \"0Fu\", \"49S\", \"6Hx\", \"7ia\", \"4Lc\", \"03E\", \"2Bn\", \"zS\", \"o2\", \"4bO\", \"6Wl\", \"a4F\", \"bUL\", \"0TQ\", \"3k\", \n\"YO\", \"87m\", \"4AS\", \"4T2\", \"7kP\", \"4NR\", \"01t\", \"VN\", \"xb\", \"1Kq\", \"54W\", \"hfv\", \"6Xm\", \"4mN\", \"1FA\", \"1Z\", \"2Mo\", \"0xL\", \"4Cb\", \"62I\", \n\"5MA\", \"6hC\", \"2Sm\", \"0fN\", \"0Hb\", \"kP\", \"6Fo\", \"47D\", \"bDO\", \"aaM\", \"0P3\", \"0ER\", \"8bf\", \"HL\", \"4E1\", \"4PP\", \"5Op\", \"4J0\", \"GM\", \"10V\", \n\"0JS\", \"ia\", \"505\", \"45u\", \"48E\", \"6In\", \"dQ\", \"58\", \"0iO\", \"3LM\", \"6gB\", \"4Ra\", \"0UK\", \"2q\", \"7Kg\", \"4ne\", \"5Ph\", \"61b\", \"XU\", \"M4\", \n\"0vW\", \"Ue\", \"5H9\", \"4My\", \"4cU\", \"4v4\", \"2mX\", \"1HZ\", \"0Wz\", \"tH\", \"4y5\", \"4lT\", \"4Bx\", \"5G8\", \"Zd\", \"0yV\", \"B5\", \"WT\", \"6zk\", \"4OH\", \n\"4ad\", \"6TG\", \"yx\", \"0ZJ\", \"gz\", \"0DH\", \"5oG\", \"6JE\", \"6di\", \"4QJ\", \"0jd\", \"IV\", \"n\", \"0gT\", \"680\", \"6iY\", \"4g7\", \"4rV\", \"0Ix\", \"jJ\", \n\"eK\", \"0Fy\", \"5mv\", \"4h6\", \"6fX\", \"5CZ\", \"0hU\", \"Kg\", \"FW\", \"S6\", \"5Nj\", \"6kh\", \"6ED\", \"44o\", \"0KI\", \"3nK\", \"zW\", \"o6\", \"4bK\", \"6Wh\", \n\"6yD\", \"4Lg\", \"03A\", \"2Bj\", \"YK\", \"0zy\", \"4AW\", \"4T6\", \"6ZX\", \"6O9\", \"0TU\", \"3o\", \"xf\", \"1Ku\", \"54S\", \"6UY\", \"7kT\", \"4NV\", \"01p\", \"VJ\", \n\"2Mk\", \"0xH\", \"4Cf\", \"62M\", \"6Xi\", \"4mJ\", \"0Vd\", \"uV\", \"0Hf\", \"kT\", \"6Fk\", \"4sH\", \"5ME\", \"6hG\", \"Ex\", \"0fJ\", \"0kz\", \"HH\", \"4E5\", \"4PT\", \n\"5nY\", \"aaI\", \"fd\", \"0EV\", \"0JW\", \"ie\", \"501\", \"45q\", \"5Ot\", \"4J4\", \"GI\", \"10R\", \"0iK\", \"Jy\", \"6gF\", \"4Re\", \"48A\", \"6Ij\", \"dU\", \"q4\", \n\"5Pl\", \"61f\", \"XQ\", \"M0\", \"0UO\", \"2u\", \"7Kc\", \"4na\", \"4cQ\", \"428\", \"39u\", \"8Qg\", \"0vS\", \"Ua\", \"aRL\", \"b3F\", \"bxO\", \"63W\", \"0l3\", \"0yR\", \n\"234\", \"0D\", \"4y1\", \"4lP\", \"55I\", \"6TC\", \"2om\", \"0ZN\", \"B1\", \"WP\", \"6zo\", \"4OL\", \"6dm\", \"4QN\", \"1zA\", \"IR\", \"25g\", \"0DL\", \"5oC\", \"6JA\", \n\"4g3\", \"46Z\", \"9PE\", \"jN\", \"j\", \"0gP\", \"684\", \"aCO\", \"72u\", \"675\", \"0hQ\", \"Kc\", \"eO\", \"8Oe\", \"5mr\", \"4h2\", \"7Ua\", \"44k\", \"0KM\", \"3nO\", \n\"FS\", \"S2\", \"5Nn\", \"6kl\", \"4x6\", \"4mW\", \"0Vy\", \"1C\", \"0m4\", \"0xU\", \"5SZ\", \"62P\", \"7kI\", \"4NK\", \"C6\", \"VW\", \"2nj\", \"1Kh\", \"54N\", \"6UD\", \n\"6ZE\", \"4of\", \"0TH\", \"3r\", \"YV\", \"L7\", \"4AJ\", \"60a\", \"6yY\", \"4Lz\", \"0wT\", \"Tf\", \"zJ\", \"0Yx\", \"4bV\", \"4w7\", \"5lu\", \"4i5\", \"dH\", \"0Gz\", \n\"0iV\", \"Jd\", \"5W8\", \"4Rx\", \"5Oi\", \"6jk\", \"GT\", \"R5\", \"0JJ\", \"ix\", \"6DG\", \"45l\", \"5nD\", \"6KF\", \"fy\", \"0EK\", \"0kg\", \"HU\", \"6ej\", \"4PI\", \n\"5MX\", \"5X9\", \"Ee\", \"0fW\", \"1XZ\", \"kI\", \"4f4\", \"4sU\", \"00w\", \"WM\", \"4Z0\", \"4OQ\", \"55T\", \"hgu\", \"ya\", \"0ZS\", \"a0\", \"0Y\", \"6Yn\", \"4lM\", \n\"4Ba\", \"63J\", \"2Ll\", \"0yO\", \"02F\", \"2Cm\", \"6xC\", \"aG0\", \"4cL\", \"6Vo\", \"2mA\", \"n1\", \"0UR\", \"2h\", \"a5E\", \"bTO\", \"5Pq\", \"4U1\", \"XL\", \"86n\", \n\"FN\", \"11U\", \"5Ns\", \"4K3\", \"516\", \"44v\", \"0KP\", \"hb\", \"eR\", \"p3\", \"49F\", \"6Hm\", \"6fA\", \"4Sb\", \"0hL\", \"3MN\", \"w\", \"0gM\", \"5LB\", \"7ya\", \n\"6Gl\", \"46G\", \"0Ia\", \"jS\", \"gc\", \"0DQ\", \"bEL\", \"hyw\", \"4D2\", \"4QS\", \"8ce\", \"IO\", \"0m0\", \"0xQ\", \"byL\", \"62T\", \"4x2\", \"4mS\", \"227\", \"1G\", \n\"2nn\", \"1Kl\", \"54J\", \"7Ea\", \"7kM\", \"4NO\", \"C2\", \"VS\", \"YR\", \"L3\", \"4AN\", \"60e\", \"6ZA\", \"4ob\", \"0TL\", \"3v\", \"zN\", \"8Pd\", \"4bR\", \"4w3\", \n\"aSO\", \"b2E\", \"03X\", \"Tb\", \"0iR\", \"3LP\", \"73v\", \"666\", \"48X\", \"4i1\", \"dL\", \"8Nf\", \"0JN\", \"3oL\", \"6DC\", \"45h\", \"5Om\", \"6jo\", \"GP\", \"R1\", \n\"0kc\", \"HQ\", \"6en\", \"4PM\", \"a09\", \"6KB\", \"24d\", \"0EO\", \"8Ag\", \"kM\", \"4f0\", \"47Y\", \"697\", \"aBL\", \"Ea\", \"0fS\", \"4ay\", \"5d9\", \"ye\", \"0ZW\", \n\"00s\", \"WI\", \"4Z4\", \"4OU\", \"4Be\", \"63N\", \"Zy\", \"0yK\", \"a4\", \"tU\", \"6Yj\", \"4lI\", \"4cH\", \"6Vk\", \"2mE\", \"n5\", \"02B\", \"Ux\", \"6xG\", \"4Md\", \n\"5Pu\", \"4U5\", \"XH\", \"86j\", \"0UV\", \"2l\", \"5k8\", \"4nx\", \"512\", \"44r\", \"0KT\", \"hf\", \"FJ\", \"0ex\", \"5Nw\", \"4K7\", \"6fE\", \"4Sf\", \"0hH\", \"Kz\", \n\"eV\", \"p7\", \"49B\", \"6Hi\", \"6Gh\", \"46C\", \"0Ie\", \"jW\", \"s\", \"0gI\", \"5LF\", \"6iD\", \"4D6\", \"4QW\", \"0jy\", \"IK\", \"gg\", \"0DU\", \"5oZ\", \"6JX\", \n\"7kA\", \"4NC\", \"01e\", \"3Po\", \"xs\", \"8RY\", \"54F\", \"6UL\", \"a6f\", \"59v\", \"0Vq\", \"1K\", \"d3E\", \"85M\", \"4Cs\", \"5F3\", \"5I2\", \"4Lr\", \"03T\", \"Tn\", \n\"zB\", \"0Yp\", \"56w\", \"437\", \"6ZM\", \"4on\", \"1Da\", \"3z\", \"2OO\", \"0zl\", \"4AB\", \"60i\", \"5Oa\", \"6jc\", \"2QM\", \"0dn\", \"0JB\", \"ip\", \"6DO\", \"45d\", \n\"48T\", \"acm\", \"1B2\", \"0Gr\", \"94o\", \"Jl\", \"5W0\", \"4Rp\", \"5MP\", \"5X1\", \"Em\", \"12v\", \"0Hs\", \"kA\", \"all\", \"47U\", \"5nL\", \"6KN\", \"fq\", \"0EC\", \n\"0ko\", \"3Nm\", \"6eb\", \"4PA\", \"a8\", \"0Q\", \"6Yf\", \"4lE\", \"4Bi\", \"63B\", \"Zu\", \"0yG\", \"0tw\", \"WE\", \"4Z8\", \"4OY\", \"4au\", \"5d5\", \"yi\", \"1Jz\", \n\"0UZ\", \"vh\", \"5k4\", \"4nt\", \"5Py\", \"4U9\", \"XD\", \"1kW\", \"02N\", \"Ut\", \"6xK\", \"4Mh\", \"4cD\", \"6Vg\", \"2mI\", \"n9\", \"eZ\", \"43\", \"49N\", \"6He\", \n\"6fI\", \"4Sj\", \"0hD\", \"Kv\", \"FF\", \"0et\", \"7n9\", \"6ky\", \"5u6\", \"4pv\", \"0KX\", \"hj\", \"gk\", \"0DY\", \"5oV\", \"5z7\", \"6dx\", \"5Az\", \"0ju\", \"IG\", \n\"Dw\", \"0gE\", \"5LJ\", \"6iH\", \"6Gd\", \"46O\", \"0Ii\", \"28B\", \"xw\", \"1Kd\", \"54B\", \"6UH\", \"7kE\", \"4NG\", \"01a\", \"3Pk\", \"0m8\", \"0xY\", \"4Cw\", \"5F7\", \n\"6Xx\", \"59r\", \"0Vu\", \"1O\", \"zF\", \"0Yt\", \"4bZ\", \"433\", \"5I6\", \"4Lv\", \"03P\", \"Tj\", \"YZ\", \"0zh\", \"4AF\", \"60m\", \"6ZI\", \"4oj\", \"0TD\", \"wv\", \n\"0JF\", \"it\", \"6DK\", \"4qh\", \"5Oe\", \"6jg\", \"GX\", \"R9\", \"0iZ\", \"Jh\", \"5W4\", \"4Rt\", \"48P\", \"4i9\", \"dD\", \"0Gv\", \"0Hw\", \"kE\", \"4f8\", \"47Q\", \n\"5MT\", \"5X5\", \"Ei\", \"12r\", \"0kk\", \"HY\", \"6ef\", \"4PE\", \"5nH\", \"6KJ\", \"fu\", \"0EG\", \"4Bm\", \"63F\", \"Zq\", \"0yC\", \"0Wo\", \"0U\", \"6Yb\", \"4lA\", \n\"4aq\", \"5d1\", \"ym\", \"8SG\", \"0ts\", \"WA\", \"aPl\", \"b1f\", \"747\", \"61w\", \"2NQ\", \"1kS\", \"9Lg\", \"2d\", \"5k0\", \"4np\", \"57i\", \"6Vc\", \"2mM\", \"0Xn\", \n\"02J\", \"Up\", \"6xO\", \"4Ml\", \"6fM\", \"4Sn\", \"1xa\", \"Kr\", \"27G\", \"47\", \"49J\", \"6Ha\", \"5u2\", \"44z\", \"8BD\", \"hn\", \"FB\", \"0ep\", \"bdm\", \"aAo\", \n\"70U\", \"bkl\", \"0jq\", \"IC\", \"go\", \"306\", \"5oR\", \"5z3\", \"7WA\", \"46K\", \"0Im\", \"28F\", \"Ds\", \"0gA\", \"5LN\", \"6iL\", \"0cY\", \"020\", \"6mT\", \"4Xw\", \n\"42S\", \"6Cx\", \"nG\", \"0Mu\", \"1Pd\", \"cw\", \"6NH\", \"5kJ\", \"4UG\", \"74M\", \"3Kk\", \"0ni\", \"0ah\", \"BZ\", \"6oe\", \"4ZF\", \"40b\", \"6AI\", \"lv\", \"0OD\", \n\"0Bt\", \"aF\", \"6Ly\", \"4yZ\", \"4Wv\", \"5R6\", \"Oj\", \"0lX\", \"Qh\", \"06R\", \"4It\", \"5L4\", \"461\", \"4gX\", \"1LW\", \"1Y6\", \"rt\", \"0QF\", \"4jh\", \"7Oj\", \n\"65o\", \"4DD\", \"I9\", \"2JI\", \"SY\", \"F8\", \"4KE\", \"7nG\", \"6PJ\", \"4ei\", \"1Nf\", \"2kd\", \"4M\", \"0Sw\", \"4hY\", \"490\", \"5C5\", \"4Fu\", \"09S\", \"2Hx\", \n\"6OR\", \"4zq\", \"354\", \"bm\", \"LA\", \"0os\", \"bnn\", \"75W\", \"6lN\", \"4Ym\", \"0bC\", \"Aq\", \"2yL\", \"0Lo\", \"43I\", \"6Bb\", \"6Mc\", \"5ha\", \"15\", \"22E\", \n\"Np\", \"0mB\", \"4Vl\", \"6cO\", \"aDm\", \"bao\", \"1pS\", \"1e2\", \"ml\", \"8GF\", \"41x\", \"548\", \"4kr\", \"5n2\", \"7f\", \"8YD\", \"1nQ\", \"2KS\", \"64u\", \"715\", \n\"4Hn\", \"69E\", \"Pr\", \"07H\", \"1MM\", \"2hO\", \"6Sa\", \"4fB\", \"4iC\", \"7LA\", \"5W\", \"0Rm\", \"08I\", \"2Ib\", \"66D\", \"4Go\", \"b4d\", \"aUn\", \"RC\", \"05y\", \n\"8VE\", \"8g\", \"5a3\", \"4ds\", \"42W\", \"ain\", \"nC\", \"0Mq\", \"17t\", \"024\", \"6mP\", \"4Xs\", \"4UC\", \"74I\", \"3Ko\", \"0nm\", \"8IY\", \"cs\", \"6NL\", \"5kN\", \n\"40f\", \"6AM\", \"lr\", \"8FX\", \"0al\", \"2TO\", \"6oa\", \"4ZB\", \"4Wr\", \"5R2\", \"On\", \"18u\", \"0Bp\", \"aB\", \"afo\", \"bCm\", \"465\", \"53u\", \"1LS\", \"1Y2\", \n\"Ql\", \"06V\", \"4Ip\", \"5L0\", \"65k\", \"5Ta\", \"1oO\", \"2JM\", \"6x\", \"0QB\", \"4jl\", \"7On\", \"6PN\", \"4em\", \"1Nb\", \"9y\", \"2EL\", \"04g\", \"4KA\", \"7nC\", \n\"5C1\", \"4Fq\", \"09W\", \"d6G\", \"4I\", \"0Ss\", \"bRn\", \"494\", \"LE\", \"0ow\", \"4TY\", \"4A8\", \"6OV\", \"4zu\", \"1Qz\", \"bi\", \"oY\", \"z8\", \"43M\", \"6Bf\", \n\"6lJ\", \"4Yi\", \"0bG\", \"Au\", \"Nt\", \"0mF\", \"4Vh\", \"6cK\", \"6Mg\", \"4xD\", \"11\", \"22A\", \"mh\", \"0NZ\", \"4ut\", \"5p4\", \"4N9\", \"5Ky\", \"1pW\", \"CD\", \n\"1nU\", \"2KW\", \"64q\", \"4EZ\", \"4kv\", \"5n6\", \"7b\", \"0PX\", \"1MI\", \"2hK\", \"6Se\", \"4fF\", \"4Hj\", \"69A\", \"Pv\", \"07L\", \"08M\", \"2If\", \"6rH\", \"4Gk\", \n\"4iG\", \"7LE\", \"5S\", \"0Ri\", \"1Ox\", \"8c\", \"5a7\", \"4dw\", \"5Zz\", \"7oY\", \"RG\", \"0qu\", \"1Pl\", \"21f\", \"adR\", \"5kB\", \"4UO\", \"74E\", \"MS\", \"X2\", \n\"0cQ\", \"028\", \"79u\", \"bbL\", \"4vS\", \"4c2\", \"nO\", \"8De\", \"8Kd\", \"aN\", \"4l3\", \"4yR\", \"634\", \"76t\", \"Ob\", \"0lP\", \"W3\", \"BR\", \"6om\", \"4ZN\", \n\"40j\", \"6AA\", \"2zo\", \"0OL\", \"6t\", \"0QN\", \"5zA\", \"7Ob\", \"65g\", \"4DL\", \"I1\", \"2JA\", \"0g3\", \"06Z\", \"b7G\", \"68W\", \"469\", \"4gP\", \"284\", \"dSn\", \n\"4E\", \"275\", \"4hQ\", \"498\", \"67V\", \"b8F\", \"1mr\", \"0h2\", \"SQ\", \"F0\", \"4KM\", \"7nO\", \"6PB\", \"4ea\", \"1Nn\", \"9u\", \"6lF\", \"4Ye\", \"0bK\", \"Ay\", \n\"oU\", \"z4\", \"43A\", \"6Bj\", \"6OZ\", \"4zy\", \"0AW\", \"be\", \"LI\", \"2O9\", \"4TU\", \"4A4\", \"4N5\", \"5Ku\", \"14S\", \"CH\", \"md\", \"0NV\", \"41p\", \"540\", \n\"6Mk\", \"4xH\", \"u5\", \"22M\", \"Nx\", \"0mJ\", \"4Vd\", \"6cG\", \"4Hf\", \"69M\", \"Pz\", \"0sH\", \"k7\", \"2hG\", \"6Si\", \"4fJ\", \"4kz\", \"7Nx\", \"7n\", \"0PT\", \n\"1nY\", \"dqh\", \"4P7\", \"4EV\", \"4JW\", \"7oU\", \"RK\", \"05q\", \"1Ot\", \"8o\", \"6QX\", \"50R\", \"4iK\", \"7LI\", \"qW\", \"d6\", \"08A\", \"2Ij\", \"66L\", \"4Gg\", \n\"4UK\", \"74A\", \"MW\", \"X6\", \"1Ph\", \"21b\", \"6ND\", \"5kF\", \"4vW\", \"4c6\", \"nK\", \"0My\", \"0cU\", \"0v4\", \"6mX\", \"5HZ\", \"4Wz\", \"6bY\", \"Of\", \"0lT\", \n\"0Bx\", \"aJ\", \"4l7\", \"4yV\", \"40n\", \"6AE\", \"lz\", \"0OH\", \"W7\", \"BV\", \"6oi\", \"4ZJ\", \"65c\", \"4DH\", \"I5\", \"2JE\", \"6p\", \"0QJ\", \"4jd\", \"7Of\", \n\"4r5\", \"4gT\", \"280\", \"2iY\", \"Qd\", \"0rV\", \"4Ix\", \"5L8\", \"5C9\", \"4Fy\", \"1mv\", \"0h6\", \"4A\", \"1CZ\", \"4hU\", \"7MW\", \"6PF\", \"4ee\", \"1Nj\", \"9q\", \n\"SU\", \"F4\", \"4KI\", \"7nK\", \"oQ\", \"z0\", \"43E\", \"6Bn\", \"6lB\", \"4Ya\", \"0bO\", \"2Wl\", \"LM\", \"8fg\", \"4TQ\", \"4A0\", \"aeL\", \"cPo\", \"0AS\", \"ba\", \n\"3kP\", \"0NR\", \"41t\", \"544\", \"4N1\", \"5Kq\", \"14W\", \"CL\", \"2Xm\", \"0mN\", \"5FA\", \"6cC\", \"6Mo\", \"4xL\", \"19\", \"22I\", \"k3\", \"2hC\", \"6Sm\", \"4fN\", \n\"4Hb\", \"69I\", \"2Fo\", \"07D\", \"83l\", \"d5d\", \"4P3\", \"4ER\", \"bQM\", \"a0G\", \"7j\", \"0PP\", \"1Op\", \"8k\", \"hbw\", \"50V\", \"4JS\", \"7oQ\", \"RO\", \"05u\", \n\"08E\", \"2In\", \"66H\", \"4Gc\", \"4iO\", \"7LM\", \"qS\", \"d2\", \"0ay\", \"BK\", \"4O6\", \"4ZW\", \"40s\", \"553\", \"lg\", \"0OU\", \"t6\", \"aW\", \"6Lh\", \"4yK\", \n\"4Wg\", \"6bD\", \"2Yj\", \"0lI\", \"0cH\", \"2Vk\", \"6mE\", \"4Xf\", \"42B\", \"6Ci\", \"nV\", \"0Md\", \"1Pu\", \"cf\", \"6NY\", \"bAI\", \"4UV\", \"7pT\", \"MJ\", \"0nx\", \n\"SH\", \"04r\", \"4KT\", \"7nV\", \"azI\", \"4ex\", \"1Nw\", \"9l\", \"pT\", \"e5\", \"4hH\", \"7MJ\", \"67O\", \"4Fd\", \"09B\", \"2Hi\", \"Qy\", \"06C\", \"4Ie\", \"68N\", \n\"6Rj\", \"4gI\", \"j4\", \"2iD\", \"6m\", \"0QW\", \"4jy\", \"5o9\", \"4Q4\", \"4DU\", \"1oZ\", \"2JX\", \"4m0\", \"4xQ\", \"8Jg\", \"22T\", \"Na\", \"0mS\", \"627\", \"77w\", \n\"6nn\", \"5Kl\", \"V0\", \"CQ\", \"3kM\", \"0NO\", \"41i\", \"7Pc\", \"6OC\", \"5jA\", \"0AN\", \"20e\", \"LP\", \"Y1\", \"4TL\", \"6ao\", \"78v\", \"bcO\", \"0bR\", \"0w3\", \n\"oL\", \"8Ef\", \"43X\", \"4b1\", \"4iR\", \"7LP\", \"5F\", \"266\", \"08X\", \"0i1\", \"66U\", \"b9E\", \"4JN\", \"7oL\", \"RR\", \"G3\", \"1Om\", \"8v\", \"6QA\", \"4db\", \n\"4kc\", \"7Na\", \"7w\", \"0PM\", \"H2\", \"2KB\", \"64d\", \"4EO\", \"b6D\", \"69T\", \"Pc\", \"07Y\", \"297\", \"dRm\", \"4s2\", \"4fS\", \"40w\", \"557\", \"lc\", \"0OQ\", \n\"15T\", \"BO\", \"4O2\", \"4ZS\", \"4Wc\", \"76i\", \"2Yn\", \"0lM\", \"t2\", \"aS\", \"6Ll\", \"4yO\", \"42F\", \"6Cm\", \"nR\", \"8Dx\", \"0cL\", \"2Vo\", \"6mA\", \"4Xb\", \n\"4UR\", \"74X\", \"MN\", \"8gd\", \"1Pq\", \"cb\", \"adO\", \"bAM\", \"azM\", \"51U\", \"1Ns\", \"9h\", \"SL\", \"04v\", \"4KP\", \"7nR\", \"67K\", \"5VA\", \"09F\", \"2Hm\", \n\"4X\", \"e1\", \"4hL\", \"7MN\", \"6Rn\", \"4gM\", \"j0\", \"3ya\", \"2Gl\", \"06G\", \"4Ia\", \"68J\", \"4Q0\", \"4DQ\", \"82o\", \"d4g\", \"6i\", \"0QS\", \"bPN\", \"a1D\", \n\"Ne\", \"0mW\", \"4Vy\", \"5S9\", \"4m4\", \"4xU\", \"1SZ\", \"22P\", \"my\", \"0NK\", \"41m\", \"7Pg\", \"6nj\", \"5Kh\", \"V4\", \"CU\", \"LT\", \"Y5\", \"4TH\", \"6ak\", \n\"6OG\", \"4zd\", \"0AJ\", \"bx\", \"oH\", \"0Lz\", \"4wT\", \"4b5\", \"78r\", \"4Yx\", \"0bV\", \"Ad\", \"1lu\", \"0i5\", \"66Q\", \"4Gz\", \"4iV\", \"7LT\", \"5B\", \"0Rx\", \n\"1Oi\", \"8r\", \"6QE\", \"4df\", \"4JJ\", \"7oH\", \"RV\", \"G7\", \"H6\", \"2KF\", \"6ph\", \"4EK\", \"4kg\", \"7Ne\", \"7s\", \"0PI\", \"1MX\", \"1X9\", \"4s6\", \"4fW\", \n\"5XZ\", \"69P\", \"Pg\", \"0sU\", \"06\", \"23F\", \"afr\", \"4yC\", \"4Wo\", \"6bL\", \"Os\", \"0lA\", \"0aq\", \"BC\", \"aEn\", \"c4E\", \"4ts\", \"5q3\", \"lo\", \"8FE\", \n\"347\", \"cn\", \"6NQ\", \"5kS\", \"bom\", \"74T\", \"MB\", \"0np\", \"17i\", \"2Vc\", \"6mM\", \"4Xn\", \"42J\", \"6Ca\", \"2xO\", \"0Ml\", \"4T\", \"0Sn\", \"5xa\", \"7MB\", \n\"67G\", \"4Fl\", \"09J\", \"2Ha\", \"1u2\", \"04z\", \"b5g\", \"aTm\", \"6PS\", \"4ep\", \"8WF\", \"9d\", \"6e\", \"8XG\", \"4jq\", \"5o1\", \"65v\", \"706\", \"1oR\", \"1z3\", \n\"Qq\", \"06K\", \"4Im\", \"68F\", \"6Rb\", \"4gA\", \"1LN\", \"2iL\", \"6nf\", \"5Kd\", \"V8\", \"CY\", \"mu\", \"0NG\", \"41a\", \"7Pk\", \"4m8\", \"4xY\", \"0Cw\", \"1F7\", \n\"Ni\", \"19r\", \"4Vu\", \"5S5\", \"6lW\", \"4Yt\", \"0bZ\", \"Ah\", \"oD\", \"0Lv\", \"43P\", \"4b9\", \"6OK\", \"4zh\", \"0AF\", \"bt\", \"LX\", \"Y9\", \"4TD\", \"6ag\", \n\"4JF\", \"7oD\", \"RZ\", \"0qh\", \"1Oe\", \"2jg\", \"6QI\", \"4dj\", \"4iZ\", \"483\", \"5N\", \"0Rt\", \"08P\", \"0i9\", \"5B6\", \"4Gv\", \"4Hw\", \"5M7\", \"Pk\", \"07Q\", \n\"1MT\", \"1X5\", \"472\", \"52r\", \"4kk\", \"7Ni\", \"sw\", \"0PE\", \"1nH\", \"2KJ\", \"64l\", \"4EG\", \"4Wk\", \"6bH\", \"Ow\", \"0lE\", \"02\", \"23B\", \"6Ld\", \"4yG\", \n\"4tw\", \"5q7\", \"lk\", \"0OY\", \"0au\", \"BG\", \"6ox\", \"5Jz\", \"4UZ\", \"74P\", \"MF\", \"0nt\", \"1Py\", \"cj\", \"6NU\", \"5kW\", \"42N\", \"6Ce\", \"nZ\", \"0Mh\", \n\"0cD\", \"2Vg\", \"6mI\", \"4Xj\", \"67C\", \"4Fh\", \"09N\", \"2He\", \"4P\", \"e9\", \"4hD\", \"7MF\", \"6PW\", \"4et\", \"3n9\", \"2ky\", \"SD\", \"0pv\", \"4KX\", \"7nZ\", \n\"4Q8\", \"4DY\", \"1oV\", \"1z7\", \"6a\", \"1Az\", \"4ju\", \"5o5\", \"6Rf\", \"4gE\", \"j8\", \"2iH\", \"Qu\", \"06O\", \"4Ii\", \"68B\", \"mq\", \"0NC\", \"41e\", \"7Po\", \n\"6nb\", \"bar\", \"14F\", \"2UL\", \"Nm\", \"19v\", \"4Vq\", \"5S1\", \"agl\", \"bBn\", \"0Cs\", \"1F3\", \"1I2\", \"0Lr\", \"43T\", \"ahm\", \"6lS\", \"4Yp\", \"16w\", \"Al\", \n\"2ZM\", \"0on\", \"5Da\", \"6ac\", \"6OO\", \"4zl\", \"0AB\", \"bp\", \"1Oa\", \"8z\", \"6QM\", \"4dn\", \"4JB\", \"aUs\", \"2DO\", \"05d\", \"08T\", \"d7D\", \"5B2\", \"4Gr\", \n\"bSm\", \"487\", \"5J\", \"0Rp\", \"1MP\", \"1X1\", \"476\", \"52v\", \"4Hs\", \"5M3\", \"Po\", \"07U\", \"1nL\", \"2KN\", \"64h\", \"4EC\", \"4ko\", \"7Nm\", \"ss\", \"0PA\", \n\"QJ\", \"06p\", \"4IV\", \"7lT\", \"6RY\", \"4gz\", \"1Lu\", \"0I5\", \"rV\", \"g7\", \"4jJ\", \"7OH\", \"65M\", \"4Df\", \"1oi\", \"2Jk\", \"2Ej\", \"04A\", \"4Kg\", \"7ne\", \n\"6Ph\", \"4eK\", \"h6\", \"2kF\", \"4o\", \"0SU\", \"5xZ\", \"7My\", \"4S6\", \"4FW\", \"09q\", \"1x9\", \"17R\", \"2VX\", \"4M4\", \"4XU\", \"42q\", \"571\", \"ne\", \"0MW\", \n\"v4\", \"cU\", \"6Nj\", \"5kh\", \"4Ue\", \"74o\", \"My\", \"0nK\", \"0aJ\", \"Bx\", \"6oG\", \"4Zd\", \"4tH\", \"6Ak\", \"lT\", \"y5\", \"0BV\", \"ad\", \"580\", \"4yx\", \n\"4WT\", \"4B5\", \"OH\", \"0lz\", \"4kP\", \"7NR\", \"7D\", \"244\", \"1ns\", \"0k3\", \"64W\", \"con\", \"4HL\", \"69g\", \"PP\", \"E1\", \"1Mo\", \"2hm\", \"6SC\", \"52I\", \n\"4ia\", \"7Lc\", \"5u\", \"0RO\", \"J0\", \"3Ya\", \"66f\", \"4GM\", \"b4F\", \"aUL\", \"Ra\", \"0qS\", \"8Vg\", \"8E\", \"458\", \"4dQ\", \"4o2\", \"4zS\", \"8He\", \"bO\", \n\"Lc\", \"0oQ\", \"605\", \"75u\", \"6ll\", \"4YO\", \"T2\", \"AS\", \"2yn\", \"0LM\", \"43k\", \"7Ra\", \"6MA\", \"4xb\", \"0CL\", \"22g\", \"NR\", \"19I\", \"4VN\", \"6cm\", \n\"aDO\", \"baM\", \"14y\", \"Cb\", \"mN\", \"8Gd\", \"41Z\", \"7PP\", \"axO\", \"53W\", \"1Lq\", \"0I1\", \"QN\", \"06t\", \"4IR\", \"68y\", \"65I\", \"4Db\", \"1om\", \"2Jo\", \n\"6Z\", \"g3\", \"4jN\", \"7OL\", \"6Pl\", \"4eO\", \"h2\", \"2kB\", \"2En\", \"04E\", \"4Kc\", \"7na\", \"4S2\", \"4FS\", \"09u\", \"d6e\", \"4k\", \"0SQ\", \"bRL\", \"a3F\", \n\"42u\", \"575\", \"na\", \"0MS\", \"17V\", \"dlo\", \"4M0\", \"4XQ\", \"4Ua\", \"74k\", \"3KM\", \"0nO\", \"28\", \"cQ\", \"6Nn\", \"5kl\", \"40D\", \"6Ao\", \"lP\", \"y1\", \n\"0aN\", \"2Tm\", \"6oC\", \"5JA\", \"4WP\", \"4B1\", \"OL\", \"18W\", \"0BR\", \"0W3\", \"584\", \"bCO\", \"1nw\", \"0k7\", \"64S\", \"4Ex\", \"4kT\", \"7NV\", \"sH\", \"0Pz\", \n\"1Mk\", \"2hi\", \"6SG\", \"4fd\", \"4HH\", \"69c\", \"PT\", \"E5\", \"J4\", \"2ID\", \"66b\", \"4GI\", \"4ie\", \"7Lg\", \"5q\", \"0RK\", \"1OZ\", \"8A\", \"4q4\", \"4dU\", \n\"4Jy\", \"5O9\", \"Re\", \"0qW\", \"Lg\", \"0oU\", \"5DZ\", \"6aX\", \"4o6\", \"4zW\", \"0Ay\", \"bK\", \"2yj\", \"0LI\", \"43o\", \"6BD\", \"6lh\", \"4YK\", \"T6\", \"AW\", \n\"NV\", \"0md\", \"4VJ\", \"6ci\", \"6ME\", \"4xf\", \"0CH\", \"22c\", \"mJ\", \"0Nx\", \"4uV\", \"7PT\", \"6nY\", \"baI\", \"1pu\", \"Cf\", \"6V\", \"0Ql\", \"4jB\", \"aus\", \n\"65E\", \"4Dn\", \"1oa\", \"2Jc\", \"QB\", \"06x\", \"b7e\", \"68u\", \"5b2\", \"4gr\", \"8UD\", \"dSL\", \"4g\", \"8ZE\", \"4hs\", \"5m3\", \"67t\", \"724\", \"09y\", \"1x1\", \n\"Ss\", \"04I\", \"4Ko\", \"7nm\", \"azr\", \"4eC\", \"1NL\", \"9W\", \"24\", \"21D\", \"6Nb\", \"bAr\", \"4Um\", \"74g\", \"Mq\", \"0nC\", \"0cs\", \"1f3\", \"79W\", \"bbn\", \n\"42y\", \"579\", \"nm\", \"394\", \"365\", \"al\", \"588\", \"4yp\", \"bmo\", \"76V\", \"1i2\", \"0lr\", \"0aB\", \"Bp\", \"6oO\", \"4Zl\", \"40H\", \"6Ac\", \"2zM\", \"0On\", \n\"4HD\", \"69o\", \"PX\", \"E9\", \"1Mg\", \"2he\", \"6SK\", \"4fh\", \"4kX\", \"7NZ\", \"7L\", \"0Pv\", \"3N9\", \"2Ky\", \"6pW\", \"4Et\", \"4Ju\", \"5O5\", \"Ri\", \"05S\", \n\"1OV\", \"8M\", \"450\", \"4dY\", \"4ii\", \"7Lk\", \"qu\", \"0RG\", \"J8\", \"2IH\", \"66n\", \"4GE\", \"6ld\", \"4YG\", \"0bi\", \"2WJ\", \"ow\", \"0LE\", \"43c\", \"6BH\", \n\"6Ox\", \"5jz\", \"0Au\", \"bG\", \"Lk\", \"0oY\", \"4Tw\", \"5Q7\", \"6nU\", \"5KW\", \"14q\", \"Cj\", \"mF\", \"0Nt\", \"41R\", \"7PX\", \"6MI\", \"4xj\", \"0CD\", \"22o\", \n\"NZ\", \"0mh\", \"4VF\", \"6ce\", \"65A\", \"4Dj\", \"1oe\", \"2Jg\", \"6R\", \"0Qh\", \"4jF\", \"7OD\", \"5b6\", \"4gv\", \"1Ly\", \"0I9\", \"QF\", \"0rt\", \"4IZ\", \"68q\", \n\"67p\", \"5Vz\", \"1mT\", \"1x5\", \"4c\", \"0SY\", \"4hw\", \"5m7\", \"6Pd\", \"4eG\", \"1NH\", \"9S\", \"Sw\", \"04M\", \"4Kk\", \"7ni\", \"4Ui\", \"74c\", \"Mu\", \"0nG\", \n\"20\", \"cY\", \"6Nf\", \"5kd\", \"4vu\", \"5s5\", \"ni\", \"390\", \"0cw\", \"1f7\", \"4M8\", \"4XY\", \"4WX\", \"4B9\", \"OD\", \"0lv\", \"0BZ\", \"ah\", \"6LW\", \"4yt\", \n\"40L\", \"6Ag\", \"lX\", \"y9\", \"0aF\", \"Bt\", \"6oK\", \"4Zh\", \"1Mc\", \"2ha\", \"6SO\", \"4fl\", \"5Xa\", \"69k\", \"2FM\", \"07f\", \"83N\", \"d5F\", \"6pS\", \"4Ep\", \n\"bQo\", \"a0e\", \"7H\", \"0Pr\", \"1OR\", \"8I\", \"454\", \"50t\", \"4Jq\", \"5O1\", \"Rm\", \"05W\", \"08g\", \"2IL\", \"66j\", \"4GA\", \"4im\", \"7Lo\", \"5y\", \"0RC\", \n\"os\", \"0LA\", \"43g\", \"6BL\", \"78I\", \"4YC\", \"0bm\", \"2WN\", \"Lo\", \"8fE\", \"4Ts\", \"5Q3\", \"aen\", \"cPM\", \"0Aq\", \"bC\", \"mB\", \"0Np\", \"41V\", \"ajo\", \n\"6nQ\", \"5KS\", \"14u\", \"Cn\", \"2XO\", \"0ml\", \"4VB\", \"6ca\", \"6MM\", \"4xn\", \"1Sa\", \"22k\", \"Sj\", \"04P\", \"4Kv\", \"5N6\", \"443\", \"4eZ\", \"1NU\", \"9N\", \n\"pv\", \"0SD\", \"4hj\", \"7Mh\", \"67m\", \"4FF\", \"1mI\", \"2HK\", \"2GJ\", \"2\", \"4IG\", \"68l\", \"6RH\", \"4gk\", \"1Ld\", \"2if\", \"6O\", \"0Qu\", \"5zz\", \"7OY\", \n\"5A7\", \"4Dw\", \"1ox\", \"0j8\", \"15r\", \"Bi\", \"6oV\", \"4Zu\", \"40Q\", \"4a8\", \"lE\", \"0Ow\", \"0BG\", \"au\", \"6LJ\", \"4yi\", \"4WE\", \"6bf\", \"OY\", \"Z8\", \n\"U9\", \"2VI\", \"6mg\", \"4XD\", \"4vh\", \"6CK\", \"nt\", \"0MF\", \"1PW\", \"cD\", \"4n9\", \"5ky\", \"4Ut\", \"5P4\", \"Mh\", \"0nZ\", \"4ip\", \"5l0\", \"5d\", \"9Kg\", \n\"08z\", \"1y2\", \"66w\", \"737\", \"4Jl\", \"7on\", \"Rp\", \"05J\", \"1OO\", \"8T\", \"6Qc\", \"50i\", \"4kA\", \"7NC\", \"7U\", \"0Po\", \"1nb\", \"dqS\", \"64F\", \"4Em\", \n\"b6f\", \"69v\", \"PA\", \"0ss\", \"8TG\", \"dRO\", \"5c1\", \"4fq\", \"6MP\", \"4xs\", \"376\", \"22v\", \"NC\", \"0mq\", \"bll\", \"77U\", \"6nL\", \"5KN\", \"14h\", \"Cs\", \n\"3ko\", \"0Nm\", \"41K\", \"7PA\", \"6Oa\", \"4zB\", \"37\", \"20G\", \"Lr\", \"8fX\", \"4Tn\", \"6aM\", \"78T\", \"bcm\", \"0bp\", \"AB\", \"on\", \"387\", \"43z\", \"5r2\", \n\"447\", \"51w\", \"1NQ\", \"9J\", \"Sn\", \"04T\", \"4Kr\", \"5N2\", \"67i\", \"4FB\", \"09d\", \"2HO\", \"4z\", \"1Ca\", \"4hn\", \"7Ml\", \"6RL\", \"4go\", \"8UY\", \"2ib\", \n\"2GN\", \"6\", \"4IC\", \"68h\", \"5A3\", \"4Ds\", \"82M\", \"d4E\", \"6K\", \"0Qq\", \"bPl\", \"a1f\", \"40U\", \"akl\", \"lA\", \"0Os\", \"15v\", \"Bm\", \"6oR\", \"4Zq\", \n\"4WA\", \"6bb\", \"2YL\", \"0lo\", \"0BC\", \"aq\", \"6LN\", \"4ym\", \"42d\", \"6CO\", \"np\", \"0MB\", \"0cn\", \"2VM\", \"6mc\", \"5Ha\", \"4Up\", \"5P0\", \"Ml\", \"8gF\", \n\"1PS\", \"1E2\", \"adm\", \"bAo\", \"1lW\", \"1y6\", \"4R9\", \"4GX\", \"4it\", \"5l4\", \"qh\", \"0RZ\", \"i9\", \"8P\", \"6Qg\", \"4dD\", \"4Jh\", \"7oj\", \"Rt\", \"05N\", \n\"1nf\", \"2Kd\", \"64B\", \"4Ei\", \"4kE\", \"7NG\", \"7Q\", \"f8\", \"1Mz\", \"2hx\", \"5c5\", \"4fu\", \"4HY\", \"69r\", \"PE\", \"0sw\", \"NG\", \"0mu\", \"5Fz\", \"6cx\", \n\"6MT\", \"4xw\", \"0CY\", \"0V8\", \"3kk\", \"0Ni\", \"41O\", \"7PE\", \"6nH\", \"5KJ\", \"14l\", \"Cw\", \"Lv\", \"0oD\", \"4Tj\", \"6aI\", \"6Oe\", \"4zF\", \"33\", \"bZ\", \n\"oj\", \"0LX\", \"4wv\", \"5r6\", \"6ly\", \"4YZ\", \"0bt\", \"AF\", \"4v\", \"0SL\", \"4hb\", \"awS\", \"67e\", \"4FN\", \"K3\", \"2HC\", \"Sb\", \"04X\", \"b5E\", \"aTO\", \n\"4p3\", \"4eR\", \"8Wd\", \"9F\", \"6G\", \"257\", \"4jS\", \"7OQ\", \"65T\", \"cnm\", \"1op\", \"0j0\", \"QS\", \"D2\", \"4IO\", \"68d\", \"7Ba\", \"4gc\", \"1Ll\", \"2in\", \n\"0BO\", \"23d\", \"6LB\", \"4ya\", \"4WM\", \"6bn\", \"OQ\", \"Z0\", \"0aS\", \"Ba\", \"aEL\", \"c4g\", \"40Y\", \"4a0\", \"lM\", \"8Fg\", \"8If\", \"cL\", \"4n1\", \"5kq\", \n\"616\", \"74v\", \"3KP\", \"0nR\", \"U1\", \"2VA\", \"6mo\", \"4XL\", \"42h\", \"6CC\", \"2xm\", \"0MN\", \"4Jd\", \"7of\", \"Rx\", \"05B\", \"i5\", \"2jE\", \"6Qk\", \"4dH\", \n\"4ix\", \"5l8\", \"5l\", \"0RV\", \"08r\", \"2IY\", \"4R5\", \"4GT\", \"4HU\", \"7mW\", \"PI\", \"07s\", \"1Mv\", \"0H6\", \"5c9\", \"4fy\", \"4kI\", \"7NK\", \"sU\", \"f4\", \n\"1nj\", \"2Kh\", \"64N\", \"4Ee\", \"6nD\", \"5KF\", \"1ph\", \"2Uj\", \"mW\", \"x6\", \"41C\", \"7PI\", \"593\", \"5hZ\", \"0CU\", \"0V4\", \"NK\", \"0my\", \"4VW\", \"4C6\", \n\"4L7\", \"4YV\", \"0bx\", \"AJ\", \"of\", \"0LT\", \"43r\", \"562\", \"6Oi\", \"4zJ\", \"w7\", \"bV\", \"Lz\", \"0oH\", \"4Tf\", \"6aE\", \"67a\", \"4FJ\", \"K7\", \"2HG\", \n\"4r\", \"0SH\", \"4hf\", \"7Md\", \"4p7\", \"4eV\", \"1NY\", \"9B\", \"Sf\", \"0pT\", \"4Kz\", \"7nx\", \"65P\", \"5TZ\", \"1ot\", \"0j4\", \"6C\", \"0Qy\", \"4jW\", \"7OU\", \n\"6RD\", \"4gg\", \"1Lh\", \"2ij\", \"QW\", \"D6\", \"4IK\", \"7lI\", \"4WI\", \"6bj\", \"OU\", \"Z4\", \"0BK\", \"ay\", \"6LF\", \"4ye\", \"4tU\", \"4a4\", \"lI\", \"2o9\", \n\"0aW\", \"Be\", \"6oZ\", \"4Zy\", \"4Ux\", \"5P8\", \"Md\", \"0nV\", \"8Ib\", \"cH\", \"4n5\", \"5ku\", \"42l\", \"6CG\", \"nx\", \"0MJ\", \"U5\", \"2VE\", \"6mk\", \"4XH\", \n\"i1\", \"8X\", \"6Qo\", \"4dL\", \"5ZA\", \"7ob\", \"2Dm\", \"05F\", \"08v\", \"d7f\", \"4R1\", \"4GP\", \"bSO\", \"a2E\", \"5h\", \"0RR\", \"1Mr\", \"0H2\", \"ayL\", \"52T\", \n\"4HQ\", \"69z\", \"PM\", \"07w\", \"1nn\", \"2Kl\", \"64J\", \"4Ea\", \"4kM\", \"7NO\", \"7Y\", \"f0\", \"mS\", \"x2\", \"41G\", \"7PM\", \"aDR\", \"5KB\", \"14d\", \"2Un\", \n\"NO\", \"19T\", \"4VS\", \"4C2\", \"597\", \"bBL\", \"0CQ\", \"0V0\", \"ob\", \"0LP\", \"43v\", \"566\", \"4L3\", \"4YR\", \"16U\", \"AN\", \"2Zo\", \"0oL\", \"4Tb\", \"6aA\", \n\"6Om\", \"4zN\", \"w3\", \"bR\", \"4oT\", \"4z5\", \"wH\", \"0Tz\", \"0zV\", \"Yd\", \"5D8\", \"4Ax\", \"4LH\", \"6yk\", \"TT\", \"A5\", \"0YJ\", \"zx\", \"6WG\", \"4bd\", \n\"4me\", \"6XF\", \"1q\", \"0VK\", \"N4\", \"2MD\", \"62b\", \"4CI\", \"4Ny\", \"5K9\", \"Ve\", \"0uW\", \"1KZ\", \"xI\", \"4u4\", \"5pt\", \"4k6\", \"5nv\", \"0Ey\", \"fK\", \n\"Hg\", \"0kU\", \"641\", \"6eX\", \"6hh\", \"5Mj\", \"P6\", \"EW\", \"29b\", \"0HI\", \"47o\", \"6FD\", \"6IE\", \"48n\", \"0GH\", \"dz\", \"JV\", \"0id\", \"4RJ\", \"6gi\", \n\"6jY\", \"beI\", \"0dT\", \"Gf\", \"iJ\", \"0Jx\", \"4qV\", \"4d7\", \"UN\", \"02t\", \"4MR\", \"4X3\", \"a8G\", \"57W\", \"0XP\", \"0M1\", \"2Z\", \"c3\", \"4nN\", \"7KL\", \n\"61I\", \"5PC\", \"1km\", \"2No\", \"2An\", \"00E\", \"4Oc\", \"7ja\", \"6Tl\", \"4aO\", \"l2\", \"yS\", \"0k\", \"0WQ\", \"58V\", \"a7F\", \"4W2\", \"4BS\", \"84m\", \"ZO\", \n\"13V\", \"E\", \"4I0\", \"5Lp\", \"46u\", \"535\", \"ja\", \"0IS\", \"68\", \"gQ\", \"6Jn\", \"5ol\", \"4Qa\", \"6dB\", \"3OM\", \"0jO\", \"0eN\", \"2Pm\", \"6kC\", \"5NA\", \n\"44D\", \"6Eo\", \"hP\", \"99\", \"0FR\", \"0S3\", \"abM\", \"49t\", \"4SP\", \"4F1\", \"KL\", \"8af\", \"0zR\", \"0o3\", \"60W\", \"ckn\", \"4oP\", \"4z1\", \"3D\", \"204\", \n\"0YN\", \"2lm\", \"6WC\", \"56I\", \"4LL\", \"6yo\", \"TP\", \"A1\", \"N0\", \"903\", \"62f\", \"4CM\", \"4ma\", \"6XB\", \"1u\", \"0VO\", \"8Rg\", \"xM\", \"418\", \"54x\", \n\"b0F\", \"aQL\", \"Va\", \"0uS\", \"Hc\", \"0kQ\", \"645\", \"71u\", \"4k2\", \"5nr\", \"8Le\", \"fO\", \"29f\", \"0HM\", \"47k\", \"7Va\", \"6hl\", \"5Mn\", \"P2\", \"ES\", \n\"JR\", \"1yA\", \"4RN\", \"6gm\", \"6IA\", \"48j\", \"0GL\", \"26g\", \"iN\", \"8Cd\", \"45Z\", \"4d3\", \"hYv\", \"beM\", \"0dP\", \"Gb\", \"6VY\", \"4cz\", \"0XT\", \"0M5\", \n\"UJ\", \"02p\", \"4MV\", \"4X7\", \"61M\", \"5PG\", \"1ki\", \"Xz\", \"vV\", \"c7\", \"4nJ\", \"7KH\", \"6Th\", \"4aK\", \"l6\", \"yW\", \"2Aj\", \"00A\", \"4Og\", \"6zD\", \n\"4W6\", \"4BW\", \"0yy\", \"ZK\", \"0o\", \"0WU\", \"58R\", \"6YX\", \"46q\", \"531\", \"je\", \"0IW\", \"13R\", \"A\", \"4I4\", \"5Lt\", \"4Qe\", \"6dF\", \"Iy\", \"0jK\", \n\"r4\", \"gU\", \"6Jj\", \"5oh\", \"4pH\", \"6Ek\", \"hT\", \"0Kf\", \"0eJ\", \"Fx\", \"6kG\", \"5NE\", \"4ST\", \"4F5\", \"KH\", \"0hz\", \"0FV\", \"ed\", \"5x8\", \"49p\", \n\"bvs\", \"6yc\", \"2BM\", \"03f\", \"0YB\", \"zp\", \"6WO\", \"4bl\", \"bUo\", \"a4e\", \"3H\", \"0Tr\", \"87N\", \"Yl\", \"5D0\", \"4Ap\", \"4Nq\", \"5K1\", \"Vm\", \"01W\", \n\"1KR\", \"xA\", \"414\", \"54t\", \"4mm\", \"6XN\", \"1y\", \"0VC\", \"0xo\", \"2ML\", \"62j\", \"4CA\", \"7xA\", \"5Mb\", \"0fm\", \"2SN\", \"ks\", \"0HA\", \"47g\", \"6FL\", \n\"aan\", \"bDl\", \"0Eq\", \"fC\", \"Ho\", \"8bE\", \"4Ps\", \"5U3\", \"5Z2\", \"5OS\", \"10u\", \"Gn\", \"iB\", \"0Jp\", \"45V\", \"ano\", \"6IM\", \"48f\", \"1Wa\", \"dr\", \n\"3Ln\", \"0il\", \"4RB\", \"6ga\", \"2R\", \"0Uh\", \"4nF\", \"7KD\", \"61A\", \"5PK\", \"1ke\", \"Xv\", \"UF\", \"0vt\", \"4MZ\", \"6xy\", \"5f6\", \"4cv\", \"0XX\", \"0M9\", \n\"0c\", \"0WY\", \"4lw\", \"5i7\", \"63p\", \"5Rz\", \"0yu\", \"ZG\", \"Ww\", \"00M\", \"4Ok\", \"6zH\", \"6Td\", \"4aG\", \"0Zi\", \"2oJ\", \"60\", \"gY\", \"6Jf\", \"5od\", \n\"4Qi\", \"6dJ\", \"Iu\", \"0jG\", \"0gw\", \"M\", \"4I8\", \"5Lx\", \"4ru\", \"5w5\", \"ji\", \"1Yz\", \"0FZ\", \"eh\", \"5x4\", \"5mU\", \"4SX\", \"4F9\", \"KD\", \"0hv\", \n\"0eF\", \"Ft\", \"6kK\", \"5NI\", \"44L\", \"6Eg\", \"hX\", \"91\", \"0YF\", \"zt\", \"6WK\", \"4bh\", \"4LD\", \"6yg\", \"TX\", \"A9\", \"0zZ\", \"Yh\", \"5D4\", \"4At\", \n\"4oX\", \"4z9\", \"3L\", \"0Tv\", \"1KV\", \"xE\", \"410\", \"54p\", \"4Nu\", \"5K5\", \"Vi\", \"01S\", \"N8\", \"2MH\", \"62n\", \"4CE\", \"4mi\", \"6XJ\", \"uu\", \"0VG\", \n\"kw\", \"0HE\", \"47c\", \"6FH\", \"6hd\", \"5Mf\", \"0fi\", \"2SJ\", \"Hk\", \"0kY\", \"4Pw\", \"5U7\", \"6Kx\", \"5nz\", \"0Eu\", \"fG\", \"iF\", \"0Jt\", \"45R\", \"6Dy\", \n\"5Z6\", \"5OW\", \"0dX\", \"Gj\", \"JZ\", \"0ih\", \"4RF\", \"6ge\", \"6II\", \"48b\", \"0GD\", \"dv\", \"61E\", \"5PO\", \"1ka\", \"Xr\", \"2V\", \"0Ul\", \"4nB\", \"aqs\", \n\"5f2\", \"4cr\", \"8QD\", \"39V\", \"UB\", \"02x\", \"795\", \"aRo\", \"63t\", \"764\", \"0yq\", \"ZC\", \"0g\", \"9Nd\", \"4ls\", \"5i3\", \"7DA\", \"4aC\", \"0Zm\", \"2oN\", \n\"Ws\", \"00I\", \"4Oo\", \"6zL\", \"4Qm\", \"6dN\", \"Iq\", \"0jC\", \"64\", \"25D\", \"6Jb\", \"bEr\", \"46y\", \"539\", \"jm\", \"9Pf\", \"0gs\", \"I\", \"aCl\", \"bfn\", \n\"bio\", \"72V\", \"1m2\", \"0hr\", \"325\", \"el\", \"5x0\", \"49x\", \"44H\", \"6Ec\", \"3nl\", \"95\", \"0eB\", \"Fp\", \"6kO\", \"5NM\", \"4mt\", \"5h4\", \"uh\", \"0VZ\", \n\"0xv\", \"2MU\", \"4V9\", \"4CX\", \"4Nh\", \"7kj\", \"Vt\", \"01N\", \"m9\", \"xX\", \"6Ug\", \"54m\", \"4oE\", \"6Zf\", \"3Q\", \"b8\", \"0zG\", \"Yu\", \"60B\", \"4Ai\", \n\"4LY\", \"4Y8\", \"TE\", \"0ww\", \"1Iz\", \"zi\", \"5g5\", \"4bu\", \"5y7\", \"5lV\", \"0GY\", \"dk\", \"JG\", \"0iu\", \"5Bz\", \"6gx\", \"6jH\", \"5OJ\", \"0dE\", \"Gw\", \n\"3ok\", \"82\", \"45O\", \"6Dd\", \"6Ke\", \"5ng\", \"73\", \"fZ\", \"Hv\", \"0kD\", \"4Pj\", \"6eI\", \"6hy\", \"7m9\", \"0ft\", \"EF\", \"kj\", \"0HX\", \"4sv\", \"5v6\", \n\"Wn\", \"00T\", \"4Or\", \"5J2\", \"407\", \"55w\", \"0Zp\", \"yB\", \"0z\", \"1Ga\", \"4ln\", \"6YM\", \"63i\", \"4BB\", \"0yl\", \"2LO\", \"2CN\", \"02e\", \"4MC\", \"7hA\", \n\"6VL\", \"4co\", \"0XA\", \"2mb\", \"2K\", \"0Uq\", \"bTl\", \"a5f\", \"5E3\", \"5PR\", \"86M\", \"Xo\", \"11v\", \"Fm\", \"6kR\", \"5NP\", \"44U\", \"aol\", \"hA\", \"0Ks\", \n\"0FC\", \"eq\", \"6HN\", \"49e\", \"4SA\", \"6fb\", \"3Mm\", \"0ho\", \"0gn\", \"T\", \"6ic\", \"5La\", \"46d\", \"6GO\", \"jp\", \"0IB\", \"0Dr\", \"1A2\", \"hyT\", \"bEo\", \n\"4Qp\", \"5T0\", \"Il\", \"8cF\", \"0xr\", \"2MQ\", \"62w\", \"777\", \"4mp\", \"5h0\", \"1d\", \"9Og\", \"1KO\", \"2nM\", \"6Uc\", \"54i\", \"4Nl\", \"7kn\", \"Vp\", \"01J\", \n\"0zC\", \"Yq\", \"60F\", \"4Am\", \"4oA\", \"6Zb\", \"3U\", \"0To\", \"8PG\", \"zm\", \"5g1\", \"4bq\", \"786\", \"aSl\", \"TA\", \"0ws\", \"JC\", \"0iq\", \"bhl\", \"73U\", \n\"5y3\", \"5lR\", \"336\", \"do\", \"3oo\", \"86\", \"45K\", \"7TA\", \"6jL\", \"5ON\", \"0dA\", \"Gs\", \"Hr\", \"8bX\", \"4Pn\", \"6eM\", \"6Ka\", \"5nc\", \"77\", \"24G\", \n\"kn\", \"8AD\", \"47z\", \"5v2\", \"aBo\", \"bgm\", \"0fp\", \"EB\", \"403\", \"4aZ\", \"0Zt\", \"yF\", \"Wj\", \"00P\", \"4Ov\", \"5J6\", \"63m\", \"4BF\", \"0yh\", \"ZZ\", \n\"tv\", \"0WD\", \"4lj\", \"6YI\", \"6VH\", \"4ck\", \"0XE\", \"2mf\", \"2CJ\", \"02a\", \"4MG\", \"6xd\", \"5E7\", \"5PV\", \"1kx\", \"Xk\", \"2O\", \"0Uu\", \"bTh\", \"7KY\", \n\"44Q\", \"4e8\", \"hE\", \"0Kw\", \"11r\", \"Fi\", \"6kV\", \"5NT\", \"4SE\", \"6ff\", \"KY\", \"0hk\", \"0FG\", \"eu\", \"6HJ\", \"49a\", \"4rh\", \"6GK\", \"jt\", \"0IF\", \n\"Q9\", \"P\", \"6ig\", \"5Le\", \"4Qt\", \"5T4\", \"Ih\", \"0jZ\", \"0Dv\", \"gD\", \"4j9\", \"5oy\", \"aD0\", \"7kb\", \"3PL\", \"01F\", \"m1\", \"xP\", \"6Uo\", \"54e\", \n\"59U\", \"a6E\", \"1h\", \"0VR\", \"85n\", \"196\", \"4V1\", \"4CP\", \"4LQ\", \"4Y0\", \"TM\", \"03w\", \"0YS\", \"za\", \"a9D\", \"56T\", \"4oM\", \"6Zn\", \"3Y\", \"b0\", \n\"0zO\", \"2Ol\", \"60J\", \"4Aa\", \"7za\", \"5OB\", \"0dM\", \"2Qn\", \"iS\", \"0Ja\", \"45G\", \"6Dl\", \"acN\", \"48w\", \"0GQ\", \"dc\", \"JO\", \"94L\", \"4RS\", \"4G2\", \n\"4H3\", \"5Ms\", \"12U\", \"EN\", \"kb\", \"0HP\", \"47v\", \"526\", \"6Km\", \"5no\", \"s3\", \"fR\", \"3NN\", \"0kL\", \"4Pb\", \"6eA\", \"0r\", \"0WH\", \"4lf\", \"6YE\", \n\"63a\", \"4BJ\", \"O7\", \"ZV\", \"Wf\", \"0tT\", \"4Oz\", \"6zY\", \"4t7\", \"4aV\", \"0Zx\", \"yJ\", \"2C\", \"0Uy\", \"4nW\", \"7KU\", \"61P\", \"5PZ\", \"1kt\", \"Xg\", \n\"UW\", \"02m\", \"4MK\", \"6xh\", \"6VD\", \"4cg\", \"0XI\", \"2mj\", \"0FK\", \"ey\", \"6HF\", \"49m\", \"4SI\", \"6fj\", \"KU\", \"0hg\", \"0eW\", \"Fe\", \"6kZ\", \"5NX\", \n\"4pU\", \"4e4\", \"hI\", \"2k9\", \"0Dz\", \"gH\", \"4j5\", \"5ou\", \"4Qx\", \"5T8\", \"Id\", \"0jV\", \"Q5\", \"DT\", \"6ik\", \"5Li\", \"46l\", \"6GG\", \"jx\", \"0IJ\", \n\"m5\", \"xT\", \"6Uk\", \"54a\", \"4Nd\", \"7kf\", \"Vx\", \"01B\", \"0xz\", \"192\", \"4V5\", \"4CT\", \"4mx\", \"5h8\", \"1l\", \"0VV\", \"0YW\", \"ze\", \"5g9\", \"4by\", \n\"4LU\", \"4Y4\", \"TI\", \"03s\", \"0zK\", \"Yy\", \"60N\", \"4Ae\", \"4oI\", \"6Zj\", \"wU\", \"b4\", \"iW\", \"0Je\", \"45C\", \"6Dh\", \"6jD\", \"5OF\", \"0dI\", \"2Qj\", \n\"JK\", \"0iy\", \"4RW\", \"4G6\", \"6IX\", \"48s\", \"0GU\", \"dg\", \"kf\", \"0HT\", \"47r\", \"522\", \"4H7\", \"5Mw\", \"0fx\", \"EJ\", \"Hz\", \"0kH\", \"4Pf\", \"6eE\", \n\"6Ki\", \"5nk\", \"s7\", \"fV\", \"63e\", \"4BN\", \"O3\", \"ZR\", \"0v\", \"0WL\", \"4lb\", \"6YA\", \"4t3\", \"4aR\", \"8Sd\", \"yN\", \"Wb\", \"00X\", \"b1E\", \"aPO\", \n\"61T\", \"bzL\", \"1kp\", \"Xc\", \"2G\", \"217\", \"4nS\", \"7KQ\", \"7Fa\", \"4cc\", \"0XM\", \"2mn\", \"US\", \"02i\", \"4MO\", \"6xl\", \"4SM\", \"6fn\", \"KQ\", \"0hc\", \n\"0FO\", \"27d\", \"6HB\", \"49i\", \"44Y\", \"4e0\", \"hM\", \"8Bg\", \"0eS\", \"Fa\", \"aAL\", \"bdN\", \"656\", \"70v\", \"3OP\", \"0jR\", \"8Mf\", \"gL\", \"4j1\", \"5oq\", \n\"46h\", \"6GC\", \"28e\", \"0IN\", \"Q1\", \"X\", \"6io\", \"5Lm\", \"6KV\", \"5nT\", \"1Uz\", \"fi\", \"HE\", \"0kw\", \"4PY\", \"4E8\", \"6hJ\", \"5MH\", \"0fG\", \"Eu\", \n\"kY\", \"0Hk\", \"47M\", \"6Ff\", \"6Ig\", \"48L\", \"51\", \"dX\", \"Jt\", \"0iF\", \"4Rh\", \"6gK\", \"4J9\", \"5Oy\", \"0dv\", \"GD\", \"ih\", \"0JZ\", \"4qt\", \"5t4\", \n\"4ov\", \"5j6\", \"3b\", \"0TX\", \"0zt\", \"YF\", \"60q\", \"4AZ\", \"4Lj\", \"6yI\", \"Tv\", \"03L\", \"0Yh\", \"zZ\", \"6We\", \"4bF\", \"4mG\", \"6Xd\", \"1S\", \"0Vi\", \n\"0xE\", \"2Mf\", \"6vH\", \"4Ck\", \"bth\", \"7kY\", \"VG\", \"0uu\", \"1Kx\", \"xk\", \"5e7\", \"5pV\", \"13t\", \"g\", \"5Y3\", \"5LR\", \"46W\", \"amn\", \"jC\", \"0Iq\", \n\"0DA\", \"gs\", \"6JL\", \"5oN\", \"4QC\", \"70I\", \"3Oo\", \"0jm\", \"0el\", \"2PO\", \"6ka\", \"5Nc\", \"44f\", \"6EM\", \"hr\", \"8BX\", \"0Fp\", \"eB\", \"abo\", \"49V\", \n\"4Sr\", \"5V2\", \"Kn\", \"8aD\", \"Ul\", \"02V\", \"4Mp\", \"5H0\", \"425\", \"57u\", \"0Xr\", \"2mQ\", \"2x\", \"0UB\", \"4nl\", \"7Kn\", \"61k\", \"5Pa\", \"1kO\", \"2NM\", \n\"2AL\", \"00g\", \"4OA\", \"6zb\", \"6TN\", \"4am\", \"0ZC\", \"yq\", \"0I\", \"0Ws\", \"58t\", \"a7d\", \"5G1\", \"4Bq\", \"84O\", \"Zm\", \"HA\", \"0ks\", \"bjn\", \"71W\", \n\"6KR\", \"5nP\", \"314\", \"fm\", \"29D\", \"0Ho\", \"47I\", \"6Fb\", \"6hN\", \"5ML\", \"0fC\", \"Eq\", \"Jp\", \"0iB\", \"4Rl\", \"6gO\", \"6Ic\", \"48H\", \"55\", \"26E\", \n\"il\", \"8CF\", \"45x\", \"508\", \"hYT\", \"beo\", \"0dr\", \"1a2\", \"0zp\", \"YB\", \"60u\", \"755\", \"4or\", \"5j2\", \"3f\", \"9Me\", \"0Yl\", \"2lO\", \"6Wa\", \"4bB\", \n\"4Ln\", \"6yM\", \"Tr\", \"03H\", \"0xA\", \"2Mb\", \"62D\", \"4Co\", \"4mC\", \"7HA\", \"1W\", \"0Vm\", \"8RE\", \"xo\", \"5e3\", \"54Z\", \"b0d\", \"aQn\", \"VC\", \"01y\", \n\"46S\", \"6Gx\", \"jG\", \"0Iu\", \"0gY\", \"c\", \"5Y7\", \"5LV\", \"4QG\", \"6dd\", \"3Ok\", \"0ji\", \"0DE\", \"gw\", \"6JH\", \"5oJ\", \"44b\", \"6EI\", \"hv\", \"0KD\", \n\"0eh\", \"FZ\", \"6ke\", \"5Ng\", \"4Sv\", \"5V6\", \"Kj\", \"0hX\", \"0Ft\", \"eF\", \"6Hy\", \"49R\", \"421\", \"4cX\", \"0Xv\", \"2mU\", \"Uh\", \"02R\", \"4Mt\", \"5H4\", \n\"61o\", \"5Pe\", \"M9\", \"XX\", \"vt\", \"0UF\", \"4nh\", \"7Kj\", \"6TJ\", \"4ai\", \"0ZG\", \"yu\", \"WY\", \"B8\", \"4OE\", \"6zf\", \"5G5\", \"4Bu\", \"1iz\", \"Zi\", \n\"0M\", \"0Ww\", \"4lY\", \"4y8\", \"6hB\", \"aW1\", \"0fO\", \"2Sl\", \"kQ\", \"0Hc\", \"47E\", \"6Fn\", \"aaL\", \"bDN\", \"0ES\", \"fa\", \"HM\", \"8bg\", \"4PQ\", \"4E0\", \n\"4J1\", \"5Oq\", \"10W\", \"GL\", \"3oP\", \"0JR\", \"45t\", \"504\", \"6Io\", \"48D\", \"59\", \"dP\", \"3LL\", \"0iN\", \"5BA\", \"6gC\", \"4Lb\", \"6yA\", \"2Bo\", \"03D\", \n\"o3\", \"zR\", \"6Wm\", \"4bN\", \"bUM\", \"a4G\", \"3j\", \"0TP\", \"87l\", \"YN\", \"4T3\", \"4AR\", \"4NS\", \"7kQ\", \"VO\", \"01u\", \"1Kp\", \"xc\", \"hfw\", \"54V\", \n\"4mO\", \"6Xl\", \"uS\", \"0Va\", \"0xM\", \"2Mn\", \"62H\", \"4Cc\", \"0DI\", \"25b\", \"6JD\", \"5oF\", \"4QK\", \"6dh\", \"IW\", \"0je\", \"0gU\", \"o\", \"6iX\", \"5LZ\", \n\"4rW\", \"4g6\", \"jK\", \"0Iy\", \"0Fx\", \"eJ\", \"4h7\", \"5mw\", \"4Sz\", \"6fY\", \"Kf\", \"0hT\", \"S7\", \"FV\", \"6ki\", \"5Nk\", \"44n\", \"6EE\", \"hz\", \"0KH\", \n\"2p\", \"0UJ\", \"4nd\", \"7Kf\", \"61c\", \"5Pi\", \"M5\", \"XT\", \"Ud\", \"0vV\", \"4Mx\", \"5H8\", \"4v5\", \"4cT\", \"0Xz\", \"2mY\", \"0A\", \"1GZ\", \"4lU\", \"4y4\", \n\"5G9\", \"4By\", \"0yW\", \"Ze\", \"WU\", \"B4\", \"4OI\", \"6zj\", \"6TF\", \"4ae\", \"0ZK\", \"yy\", \"kU\", \"0Hg\", \"47A\", \"6Fj\", \"6hF\", \"5MD\", \"0fK\", \"Ey\", \n\"HI\", \"2K9\", \"4PU\", \"4E4\", \"6KZ\", \"5nX\", \"0EW\", \"fe\", \"id\", \"0JV\", \"45p\", \"500\", \"4J5\", \"5Ou\", \"0dz\", \"GH\", \"Jx\", \"0iJ\", \"4Rd\", \"6gG\", \n\"6Ik\", \"5li\", \"q5\", \"dT\", \"o7\", \"zV\", \"6Wi\", \"4bJ\", \"4Lf\", \"6yE\", \"Tz\", \"0wH\", \"0zx\", \"YJ\", \"4T7\", \"4AV\", \"4oz\", \"6ZY\", \"3n\", \"0TT\", \n\"1Kt\", \"xg\", \"6UX\", \"54R\", \"4NW\", \"7kU\", \"VK\", \"01q\", \"0xI\", \"2Mj\", \"62L\", \"4Cg\", \"4mK\", \"6Xh\", \"uW\", \"0Ve\", \"4QO\", \"6dl\", \"IS\", \"0ja\", \n\"0DM\", \"25f\", \"7Za\", \"5oB\", \"4rS\", \"4g2\", \"jO\", \"9PD\", \"0gQ\", \"k\", \"aCN\", \"685\", \"674\", \"72t\", \"Kb\", \"0hP\", \"8Od\", \"eN\", \"4h3\", \"49Z\", \n\"44j\", \"6EA\", \"3nN\", \"0KL\", \"S3\", \"FR\", \"6km\", \"5No\", \"61g\", \"5Pm\", \"M1\", \"XP\", \"2t\", \"0UN\", \"ad0\", \"7Kb\", \"429\", \"4cP\", \"8Qf\", \"39t\", \n\"0c3\", \"02Z\", \"b3G\", \"aRM\", \"63V\", \"bxN\", \"0yS\", \"Za\", \"0E\", \"235\", \"4lQ\", \"4y0\", \"6TB\", \"4aa\", \"0ZO\", \"2ol\", \"WQ\", \"B0\", \"4OM\", \"6zn\", \n\"4i4\", \"5lt\", \"1WZ\", \"dI\", \"Je\", \"0iW\", \"4Ry\", \"5W9\", \"6jj\", \"5Oh\", \"R4\", \"GU\", \"iy\", \"0JK\", \"45m\", \"6DF\", \"6KG\", \"5nE\", \"0EJ\", \"fx\", \n\"HT\", \"0kf\", \"4PH\", \"6ek\", \"5X8\", \"5MY\", \"0fV\", \"Ed\", \"kH\", \"0Hz\", \"4sT\", \"4f5\", \"4mV\", \"4x7\", \"1B\", \"0Vx\", \"0xT\", \"0m5\", \"62Q\", \"4Cz\", \n\"4NJ\", \"7kH\", \"VV\", \"C7\", \"1Ki\", \"xz\", \"6UE\", \"54O\", \"4og\", \"6ZD\", \"3s\", \"0TI\", \"L6\", \"YW\", \"6th\", \"4AK\", \"6l9\", \"6yX\", \"Tg\", \"0wU\", \n\"0Yy\", \"zK\", \"4w6\", \"4bW\", \"11T\", \"FO\", \"4K2\", \"5Nr\", \"44w\", \"517\", \"hc\", \"0KQ\", \"p2\", \"eS\", \"6Hl\", \"49G\", \"4Sc\", \"72i\", \"3MO\", \"0hM\", \n\"0gL\", \"v\", \"6iA\", \"5LC\", \"46F\", \"6Gm\", \"jR\", \"1YA\", \"0DP\", \"gb\", \"hyv\", \"bEM\", \"4QR\", \"4D3\", \"IN\", \"8cd\", \"WL\", \"00v\", \"4OP\", \"4Z1\", \n\"hgt\", \"55U\", \"0ZR\", \"0O3\", \"0X\", \"a1\", \"4lL\", \"6Yo\", \"63K\", \"5RA\", \"0yN\", \"2Lm\", \"2Cl\", \"02G\", \"4Ma\", \"6xB\", \"6Vn\", \"4cM\", \"n0\", \"39i\", \n\"2i\", \"0US\", \"bTN\", \"a5D\", \"4U0\", \"5Pp\", \"86o\", \"XM\", \"Ja\", \"0iS\", \"667\", \"73w\", \"4i0\", \"48Y\", \"8Ng\", \"dM\", \"3oM\", \"0JO\", \"45i\", \"6DB\", \n\"6jn\", \"5Ol\", \"R0\", \"GQ\", \"HP\", \"0kb\", \"4PL\", \"6eo\", \"6KC\", \"5nA\", \"0EN\", \"24e\", \"kL\", \"8Af\", \"47X\", \"4f1\", \"aBM\", \"696\", \"0fR\", \"0s3\", \n\"0xP\", \"0m1\", \"62U\", \"byM\", \"4mR\", \"4x3\", \"1F\", \"226\", \"1Km\", \"2no\", \"6UA\", \"54K\", \"4NN\", \"7kL\", \"VR\", \"C3\", \"L2\", \"YS\", \"60d\", \"4AO\", \n\"4oc\", \"7Ja\", \"3w\", \"0TM\", \"8Pe\", \"zO\", \"4w2\", \"4bS\", \"b2D\", \"aSN\", \"Tc\", \"03Y\", \"44s\", \"513\", \"hg\", \"0KU\", \"0ey\", \"FK\", \"4K6\", \"5Nv\", \n\"4Sg\", \"6fD\", \"3MK\", \"0hI\", \"p6\", \"eW\", \"6Hh\", \"49C\", \"46B\", \"6Gi\", \"jV\", \"0Id\", \"0gH\", \"r\", \"6iE\", \"5LG\", \"4QV\", \"4D7\", \"IJ\", \"0jx\", \n\"0DT\", \"gf\", \"6JY\", \"bEI\", \"5d8\", \"4ax\", \"0ZV\", \"yd\", \"WH\", \"00r\", \"4OT\", \"4Z5\", \"63O\", \"4Bd\", \"0yJ\", \"Zx\", \"tT\", \"a5\", \"4lH\", \"6Yk\", \n\"6Vj\", \"4cI\", \"n4\", \"2mD\", \"Uy\", \"02C\", \"4Me\", \"6xF\", \"4U4\", \"5Pt\", \"1kZ\", \"XI\", \"2m\", \"0UW\", \"4ny\", \"5k9\", \"6jb\", \"ber\", \"0do\", \"2QL\", \n\"iq\", \"0JC\", \"45e\", \"6DN\", \"acl\", \"48U\", \"0Gs\", \"dA\", \"Jm\", \"94n\", \"4Rq\", \"5W1\", \"5X0\", \"5MQ\", \"12w\", \"El\", \"1M2\", \"0Hr\", \"47T\", \"alm\", \n\"6KO\", \"5nM\", \"0EB\", \"fp\", \"3Nl\", \"0kn\", \"bjs\", \"6ec\", \"4NB\", \"aQs\", \"3Pn\", \"01d\", \"1Ka\", \"xr\", \"6UM\", \"54G\", \"59w\", \"a6g\", \"1J\", \"0Vp\", \n\"85L\", \"d3D\", \"5F2\", \"4Cr\", \"4Ls\", \"5I3\", \"To\", \"03U\", \"0Yq\", \"zC\", \"436\", \"56v\", \"4oo\", \"6ZL\", \"ws\", \"0TA\", \"0zm\", \"2ON\", \"60h\", \"4AC\", \n\"42\", \"27B\", \"6Hd\", \"49O\", \"4Sk\", \"6fH\", \"Kw\", \"0hE\", \"0eu\", \"FG\", \"6kx\", \"5Nz\", \"4pw\", \"5u7\", \"hk\", \"0KY\", \"0DX\", \"gj\", \"5z6\", \"5oW\", \n\"4QZ\", \"6dy\", \"IF\", \"0jt\", \"0gD\", \"Dv\", \"6iI\", \"5LK\", \"46N\", \"6Ge\", \"jZ\", \"0Ih\", \"0P\", \"a9\", \"4lD\", \"6Yg\", \"63C\", \"4Bh\", \"0yF\", \"Zt\", \n\"WD\", \"0tv\", \"4OX\", \"4Z9\", \"5d4\", \"4at\", \"0ZZ\", \"yh\", \"2a\", \"1Ez\", \"4nu\", \"5k5\", \"4U8\", \"5Px\", \"1kV\", \"XE\", \"Uu\", \"02O\", \"4Mi\", \"6xJ\", \n\"6Vf\", \"4cE\", \"n8\", \"2mH\", \"iu\", \"0JG\", \"45a\", \"6DJ\", \"6jf\", \"5Od\", \"R8\", \"GY\", \"Ji\", \"1yz\", \"4Ru\", \"5W5\", \"4i8\", \"48Q\", \"0Gw\", \"dE\", \n\"kD\", \"0Hv\", \"47P\", \"4f9\", \"5X4\", \"5MU\", \"0fZ\", \"Eh\", \"HX\", \"0kj\", \"4PD\", \"6eg\", \"6KK\", \"5nI\", \"0EF\", \"ft\", \"1Ke\", \"xv\", \"6UI\", \"54C\", \n\"4NF\", \"7kD\", \"VZ\", \"0uh\", \"0xX\", \"0m9\", \"5F6\", \"4Cv\", \"4mZ\", \"6Xy\", \"1N\", \"0Vt\", \"0Yu\", \"zG\", \"432\", \"56r\", \"4Lw\", \"5I7\", \"Tk\", \"03Q\", \n\"0zi\", \"2OJ\", \"60l\", \"4AG\", \"4ok\", \"6ZH\", \"ww\", \"0TE\", \"4So\", \"6fL\", \"Ks\", \"0hA\", \"46\", \"27F\", \"7XA\", \"49K\", \"4ps\", \"5u3\", \"ho\", \"8BE\", \n\"0eq\", \"FC\", \"aAn\", \"bdl\", \"bkm\", \"70T\", \"IB\", \"0jp\", \"307\", \"gn\", \"5z2\", \"5oS\", \"46J\", \"6Ga\", \"28G\", \"0Il\", \"13i\", \"z\", \"6iM\", \"5LO\", \n\"63G\", \"4Bl\", \"0yB\", \"Zp\", \"0T\", \"0Wn\", \"58i\", \"6Yc\", \"5d0\", \"4ap\", \"8SF\", \"yl\", \"1q2\", \"00z\", \"b1g\", \"aPm\", \"61v\", \"746\", \"1kR\", \"XA\", \n\"2e\", \"9Lf\", \"4nq\", \"5k1\", \"6Vb\", \"4cA\", \"0Xo\", \"2mL\", \"Uq\", \"02K\", \"4Mm\", \"6xN\", \"8YG\", \"7e\", \"5n1\", \"4kq\", \"716\", \"64v\", \"2KP\", \"1nR\", \n\"07K\", \"Pq\", \"69F\", \"4Hm\", \"4fA\", \"6Sb\", \"2hL\", \"1MN\", \"0Rn\", \"5T\", \"7LB\", \"5ya\", \"4Gl\", \"66G\", \"2Ia\", \"08J\", \"05z\", \"1t2\", \"aUm\", \"b4g\", \n\"4dp\", \"5a0\", \"8d\", \"8VF\", \"bn\", \"357\", \"4zr\", \"6OQ\", \"75T\", \"bnm\", \"0op\", \"LB\", \"Ar\", \"16i\", \"4Yn\", \"6lM\", \"6Ba\", \"43J\", \"0Ll\", \"2yO\", \n\"22F\", \"16\", \"4xC\", \"agr\", \"6cL\", \"4Vo\", \"0mA\", \"Ns\", \"CC\", \"14X\", \"bal\", \"aDn\", \"5p3\", \"4us\", \"8GE\", \"mo\", \"5L7\", \"4Iw\", \"06Q\", \"Qk\", \n\"1Y5\", \"1LT\", \"53r\", \"462\", \"7Oi\", \"4jk\", \"0QE\", \"rw\", \"2JJ\", \"1oH\", \"4DG\", \"65l\", \"7nD\", \"4KF\", \"0ph\", \"SZ\", \"2kg\", \"1Ne\", \"4ej\", \"6PI\", \n\"493\", \"4hZ\", \"0St\", \"4N\", \"0h9\", \"09P\", \"4Fv\", \"5C6\", \"4Xt\", \"6mW\", \"023\", \"0cZ\", \"0Mv\", \"nD\", \"4c9\", \"42P\", \"5kI\", \"6NK\", \"ct\", \"1Pg\", \n\"X9\", \"MX\", \"74N\", \"4UD\", \"4ZE\", \"6of\", \"BY\", \"W8\", \"0OG\", \"lu\", \"6AJ\", \"40a\", \"4yY\", \"4l8\", \"aE\", \"0Bw\", \"18r\", \"Oi\", \"5R5\", \"4Wu\", \n\"4EY\", \"4P8\", \"2KT\", \"1nV\", \"8YC\", \"7a\", \"5n5\", \"4ku\", \"4fE\", \"6Sf\", \"2hH\", \"k8\", \"07O\", \"Pu\", \"69B\", \"4Hi\", \"4Gh\", \"66C\", \"2Ie\", \"08N\", \n\"d9\", \"5P\", \"7LF\", \"4iD\", \"4dt\", \"5a4\", \"2jy\", \"3o9\", \"0qv\", \"RD\", \"7oZ\", \"4JX\", \"6ay\", \"4TZ\", \"0ot\", \"LF\", \"bj\", \"0AX\", \"4zv\", \"6OU\", \n\"6Be\", \"43N\", \"0Lh\", \"oZ\", \"Av\", \"0bD\", \"4Yj\", \"6lI\", \"6cH\", \"4Vk\", \"0mE\", \"Nw\", \"22B\", \"12\", \"4xG\", \"6Md\", \"5p7\", \"4uw\", \"0NY\", \"mk\", \n\"CG\", \"1pT\", \"5Kz\", \"6nx\", \"1Y1\", \"1LP\", \"53v\", \"466\", \"5L3\", \"4Is\", \"06U\", \"Qo\", \"2JN\", \"1oL\", \"4DC\", \"65h\", \"7Om\", \"4jo\", \"0QA\", \"rs\", \n\"9z\", \"1Na\", \"4en\", \"6PM\", \"aTs\", \"4KB\", \"04d\", \"2EO\", \"d6D\", \"09T\", \"4Fr\", \"5C2\", \"497\", \"bRm\", \"0Sp\", \"4J\", \"0Mr\", \"1H2\", \"aim\", \"42T\", \n\"4Xp\", \"6mS\", \"027\", \"17w\", \"0nn\", \"3Kl\", \"74J\", \"5Ea\", \"5kM\", \"6NO\", \"cp\", \"1Pc\", \"0OC\", \"lq\", \"6AN\", \"40e\", \"4ZA\", \"6ob\", \"2TL\", \"0ao\", \n\"18v\", \"Om\", \"5R1\", \"4Wq\", \"bCn\", \"afl\", \"aA\", \"0Bs\", \"07C\", \"Py\", \"69N\", \"4He\", \"4fI\", \"6Sj\", \"2hD\", \"k4\", \"0PW\", \"7m\", \"5n9\", \"4ky\", \n\"4EU\", \"4P4\", \"2KX\", \"1nZ\", \"05r\", \"RH\", \"7oV\", \"4JT\", \"4dx\", \"5a8\", \"8l\", \"1Ow\", \"d5\", \"qT\", \"7LJ\", \"4iH\", \"4Gd\", \"66O\", \"2Ii\", \"08B\", \n\"Az\", \"0bH\", \"4Yf\", \"6lE\", \"6Bi\", \"43B\", \"z7\", \"oV\", \"bf\", \"0AT\", \"4zz\", \"6OY\", \"4A7\", \"4TV\", \"0ox\", \"LJ\", \"CK\", \"14P\", \"5Kv\", \"4N6\", \n\"543\", \"41s\", \"0NU\", \"mg\", \"22N\", \"u6\", \"4xK\", \"6Mh\", \"6cD\", \"4Vg\", \"0mI\", \"2Xj\", \"7Oa\", \"4jc\", \"0QM\", \"6w\", \"2JB\", \"I2\", \"4DO\", \"65d\", \n\"68T\", \"b7D\", \"06Y\", \"Qc\", \"dSm\", \"287\", \"4gS\", \"4r2\", \"7MP\", \"4hR\", \"276\", \"4F\", \"0h1\", \"09X\", \"b8E\", \"67U\", \"7nL\", \"4KN\", \"F3\", \"SR\", \n\"9v\", \"1Nm\", \"4eb\", \"6PA\", \"5kA\", \"6NC\", \"21e\", \"1Po\", \"X1\", \"MP\", \"74F\", \"4UL\", \"bbO\", \"79v\", \"0v3\", \"0cR\", \"8Df\", \"nL\", \"4c1\", \"42X\", \n\"4yQ\", \"4l0\", \"aM\", \"8Kg\", \"0lS\", \"Oa\", \"76w\", \"637\", \"4ZM\", \"6on\", \"BQ\", \"W0\", \"0OO\", \"2zl\", \"6AB\", \"40i\", \"4fM\", \"6Sn\", \"3xa\", \"k0\", \n\"07G\", \"2Fl\", \"69J\", \"4Ha\", \"4EQ\", \"4P0\", \"d5g\", \"83o\", \"0PS\", \"7i\", \"a0D\", \"bQN\", \"50U\", \"hbt\", \"8h\", \"1Os\", \"05v\", \"RL\", \"7oR\", \"4JP\", \n\"5WA\", \"66K\", \"2Im\", \"08F\", \"d1\", \"5X\", \"7LN\", \"4iL\", \"6Bm\", \"43F\", \"z3\", \"oR\", \"2Wo\", \"0bL\", \"4Yb\", \"6lA\", \"4A3\", \"4TR\", \"8fd\", \"LN\", \n\"bb\", \"0AP\", \"cPl\", \"aeO\", \"547\", \"41w\", \"0NQ\", \"mc\", \"CO\", \"14T\", \"5Kr\", \"4N2\", \"77i\", \"4Vc\", \"0mM\", \"2Xn\", \"22J\", \"u2\", \"4xO\", \"6Ml\", \n\"2JF\", \"I6\", \"4DK\", \"6qh\", \"7Oe\", \"4jg\", \"0QI\", \"6s\", \"1Y9\", \"1LX\", \"4gW\", \"4r6\", \"68P\", \"5YZ\", \"0rU\", \"Qg\", \"0h5\", \"1mu\", \"4Fz\", \"67Q\", \n\"7MT\", \"4hV\", \"0Sx\", \"4B\", \"9r\", \"1Ni\", \"4ef\", \"6PE\", \"7nH\", \"4KJ\", \"F7\", \"SV\", \"X5\", \"MT\", \"74B\", \"4UH\", \"5kE\", \"6NG\", \"cx\", \"1Pk\", \n\"0Mz\", \"nH\", \"4c5\", \"4vT\", \"4Xx\", \"79r\", \"0v7\", \"0cV\", \"0lW\", \"Oe\", \"5R9\", \"4Wy\", \"4yU\", \"4l4\", \"aI\", \"1RZ\", \"0OK\", \"ly\", \"6AF\", \"40m\", \n\"4ZI\", \"6oj\", \"BU\", \"W4\", \"265\", \"5E\", \"488\", \"4iQ\", \"b9F\", \"66V\", \"0i2\", \"1lr\", \"G0\", \"RQ\", \"7oO\", \"4JM\", \"4da\", \"6QB\", \"8u\", \"1On\", \n\"0PN\", \"7t\", \"7Nb\", \"aa0\", \"4EL\", \"64g\", \"2KA\", \"H1\", \"07Z\", \"0f3\", \"69W\", \"b6G\", \"4fP\", \"479\", \"dRn\", \"294\", \"22W\", \"8Jd\", \"4xR\", \"4m3\", \n\"77t\", \"624\", \"0mP\", \"Nb\", \"CR\", \"V3\", \"5Ko\", \"6nm\", \"ajS\", \"41j\", \"0NL\", \"3kN\", \"20f\", \"0AM\", \"4zc\", \"aeR\", \"6al\", \"4TO\", \"Y2\", \"LS\", \n\"Ac\", \"0bQ\", \"bcL\", \"78u\", \"4b2\", \"4wS\", \"8Ee\", \"oO\", \"7nU\", \"4KW\", \"04q\", \"SK\", \"9o\", \"1Nt\", \"51R\", \"6PX\", \"7MI\", \"4hK\", \"e6\", \"pW\", \n\"2Hj\", \"09A\", \"4Fg\", \"67L\", \"68M\", \"4If\", \"0rH\", \"Qz\", \"2iG\", \"j7\", \"4gJ\", \"6Ri\", \"7Ox\", \"4jz\", \"0QT\", \"6n\", \"1z8\", \"1oY\", \"4DV\", \"4Q7\", \n\"4ZT\", \"4O5\", \"BH\", \"0az\", \"0OV\", \"ld\", \"550\", \"40p\", \"4yH\", \"6Lk\", \"aT\", \"t5\", \"0lJ\", \"Ox\", \"6bG\", \"4Wd\", \"4Xe\", \"6mF\", \"2Vh\", \"0cK\", \n\"0Mg\", \"nU\", \"6Cj\", \"42A\", \"5kX\", \"6NZ\", \"ce\", \"1Pv\", \"2N9\", \"MI\", \"7pW\", \"4UU\", \"4Gy\", \"5B9\", \"0i6\", \"1lv\", \"1BZ\", \"5A\", \"7LW\", \"4iU\", \n\"4de\", \"6QF\", \"8q\", \"1Oj\", \"G4\", \"RU\", \"7oK\", \"4JI\", \"4EH\", \"64c\", \"2KE\", \"H5\", \"0PJ\", \"7p\", \"7Nf\", \"4kd\", \"4fT\", \"4s5\", \"2hY\", \"290\", \n\"0sV\", \"Pd\", \"5M8\", \"4Hx\", \"6cY\", \"4Vz\", \"0mT\", \"Nf\", \"1F8\", \"0Cx\", \"4xV\", \"4m7\", \"7Pd\", \"41n\", \"0NH\", \"mz\", \"CV\", \"V7\", \"5Kk\", \"6ni\", \n\"6ah\", \"4TK\", \"Y6\", \"LW\", \"20b\", \"0AI\", \"4zg\", \"6OD\", \"4b6\", \"4wW\", \"0Ly\", \"oK\", \"Ag\", \"0bU\", \"5IZ\", \"6lX\", \"9k\", \"1Np\", \"51V\", \"azN\", \n\"7nQ\", \"4KS\", \"04u\", \"SO\", \"2Hn\", \"09E\", \"4Fc\", \"67H\", \"7MM\", \"4hO\", \"e2\", \"pS\", \"2iC\", \"j3\", \"4gN\", \"6Rm\", \"68I\", \"4Ib\", \"06D\", \"2Go\", \n\"d4d\", \"82l\", \"4DR\", \"4Q3\", \"a1G\", \"bPM\", \"0QP\", \"6j\", \"0OR\", \"0Z3\", \"554\", \"40t\", \"4ZP\", \"4O1\", \"BL\", \"15W\", \"0lN\", \"2Ym\", \"6bC\", \"5GA\", \n\"4yL\", \"6Lo\", \"aP\", \"09\", \"0Mc\", \"nQ\", \"6Cn\", \"42E\", \"4Xa\", \"6mB\", \"2Vl\", \"0cO\", \"8gg\", \"MM\", \"7pS\", \"4UQ\", \"bAN\", \"adL\", \"ca\", \"1Pr\", \n\"G8\", \"RY\", \"7oG\", \"4JE\", \"4di\", \"6QJ\", \"2jd\", \"1Of\", \"0Rw\", \"5M\", \"480\", \"4iY\", \"4Gu\", \"5B5\", \"2Ix\", \"08S\", \"07R\", \"Ph\", \"5M4\", \"4Ht\", \n\"4fX\", \"471\", \"1X6\", \"1MW\", \"0PF\", \"st\", \"7Nj\", \"4kh\", \"4ED\", \"64o\", \"2KI\", \"H9\", \"CZ\", \"14A\", \"5Kg\", \"6ne\", \"7Ph\", \"41b\", \"0ND\", \"mv\", \n\"1F4\", \"0Ct\", \"4xZ\", \"6My\", \"5S6\", \"4Vv\", \"0mX\", \"Nj\", \"Ak\", \"0bY\", \"4Yw\", \"6lT\", \"6Bx\", \"43S\", \"0Lu\", \"oG\", \"bw\", \"0AE\", \"4zk\", \"6OH\", \n\"6ad\", \"4TG\", \"0oi\", \"2ZJ\", \"7MA\", \"4hC\", \"0Sm\", \"4W\", \"2Hb\", \"09I\", \"4Fo\", \"67D\", \"aTn\", \"b5d\", \"04y\", \"SC\", \"9g\", \"8WE\", \"4es\", \"6PP\", \n\"5o2\", \"4jr\", \"8XD\", \"6f\", \"1z0\", \"1oQ\", \"705\", \"65u\", \"68E\", \"4In\", \"06H\", \"Qr\", \"2iO\", \"1LM\", \"4gB\", \"6Ra\", \"5ia\", \"6Lc\", \"23E\", \"05\", \n\"0lB\", \"Op\", \"6bO\", \"4Wl\", \"c4F\", \"aEm\", \"1d2\", \"0ar\", \"8FF\", \"ll\", \"558\", \"40x\", \"5kP\", \"6NR\", \"cm\", \"344\", \"0ns\", \"MA\", \"74W\", \"bon\", \n\"4Xm\", \"6mN\", \"3FA\", \"0cC\", \"0Mo\", \"2xL\", \"6Cb\", \"42I\", \"4dm\", \"6QN\", \"8y\", \"1Ob\", \"05g\", \"2DL\", \"7oC\", \"4JA\", \"4Gq\", \"5B1\", \"d7G\", \"08W\", \n\"0Rs\", \"5I\", \"484\", \"bSn\", \"52u\", \"475\", \"1X2\", \"1MS\", \"07V\", \"Pl\", \"5M0\", \"4Hp\", \"5Ua\", \"64k\", \"2KM\", \"1nO\", \"0PB\", \"7x\", \"7Nn\", \"4kl\", \n\"7Pl\", \"41f\", \"8GX\", \"mr\", \"2UO\", \"14E\", \"5Kc\", \"6na\", \"5S2\", \"4Vr\", \"19u\", \"Nn\", \"1F0\", \"0Cp\", \"bBm\", \"ago\", \"ahn\", \"43W\", \"0Lq\", \"oC\", \n\"Ao\", \"16t\", \"4Ys\", \"6lP\", \"75I\", \"4TC\", \"0om\", \"2ZN\", \"bs\", \"0AA\", \"4zo\", \"6OL\", \"2Hf\", \"09M\", \"4Fk\", \"6sH\", \"7ME\", \"4hG\", \"0Si\", \"4S\", \n\"9c\", \"1Nx\", \"4ew\", \"6PT\", \"7nY\", \"bqh\", \"0pu\", \"SG\", \"1z4\", \"1oU\", \"4DZ\", \"65q\", \"5o6\", \"4jv\", \"0QX\", \"6b\", \"2iK\", \"1LI\", \"4gF\", \"6Re\", \n\"68A\", \"4Ij\", \"06L\", \"Qv\", \"0lF\", \"Ot\", \"6bK\", \"4Wh\", \"4yD\", \"6Lg\", \"aX\", \"01\", \"0OZ\", \"lh\", \"5q4\", \"4tt\", \"4ZX\", \"4O9\", \"BD\", \"0av\", \n\"0nw\", \"ME\", \"74S\", \"4UY\", \"5kT\", \"6NV\", \"ci\", \"1Pz\", \"0Mk\", \"nY\", \"6Cf\", \"42M\", \"4Xi\", \"6mJ\", \"2Vd\", \"0cG\", \"bL\", \"8Hf\", \"4zP\", \"4o1\", \n\"75v\", \"606\", \"0oR\", \"0z3\", \"AP\", \"T1\", \"4YL\", \"6lo\", \"6BC\", \"43h\", \"0LN\", \"2ym\", \"22d\", \"0CO\", \"4xa\", \"6MB\", \"6cn\", \"4VM\", \"0mc\", \"NQ\", \n\"Ca\", \"14z\", \"baN\", \"aDL\", \"7PS\", \"41Y\", \"8Gg\", \"mM\", \"247\", \"7G\", \"7NQ\", \"4kS\", \"com\", \"64T\", \"0k0\", \"1np\", \"E2\", \"PS\", \"69d\", \"4HO\", \n\"4fc\", \"7Ca\", \"2hn\", \"1Ml\", \"0RL\", \"5v\", \"avS\", \"4ib\", \"4GN\", \"66e\", \"2IC\", \"J3\", \"05X\", \"Rb\", \"aUO\", \"b4E\", \"4dR\", \"4q3\", \"8F\", \"8Vd\", \n\"4XV\", \"4M7\", \"1f8\", \"0cx\", \"0MT\", \"nf\", \"572\", \"42r\", \"5kk\", \"6Ni\", \"cV\", \"v7\", \"0nH\", \"Mz\", \"74l\", \"4Uf\", \"4Zg\", \"6oD\", \"2Tj\", \"0aI\", \n\"y6\", \"lW\", \"6Ah\", \"40C\", \"5iZ\", \"583\", \"ag\", \"0BU\", \"0ly\", \"OK\", \"4B6\", \"4WW\", \"7lW\", \"4IU\", \"06s\", \"QI\", \"0I6\", \"1Lv\", \"4gy\", \"5b9\", \n\"7OK\", \"4jI\", \"g4\", \"rU\", \"2Jh\", \"1oj\", \"4De\", \"65N\", \"7nf\", \"4Kd\", \"04B\", \"Sx\", \"2kE\", \"h5\", \"4eH\", \"6Pk\", \"5m8\", \"4hx\", \"0SV\", \"4l\", \n\"2HY\", \"09r\", \"4FT\", \"4S5\", \"5Q8\", \"4Tx\", \"0oV\", \"Ld\", \"bH\", \"0Az\", \"4zT\", \"4o5\", \"6BG\", \"43l\", \"0LJ\", \"ox\", \"AT\", \"T5\", \"4YH\", \"6lk\", \n\"6cj\", \"4VI\", \"0mg\", \"NU\", \"2vh\", \"0CK\", \"4xe\", \"6MF\", \"7PW\", \"4uU\", \"2n9\", \"mI\", \"Ce\", \"1pv\", \"5KX\", \"6nZ\", \"5UZ\", \"64P\", \"0k4\", \"1nt\", \n\"0Py\", \"7C\", \"7NU\", \"4kW\", \"4fg\", \"6SD\", \"2hj\", \"1Mh\", \"E6\", \"PW\", \"7mI\", \"4HK\", \"4GJ\", \"66a\", \"2IG\", \"J7\", \"0RH\", \"5r\", \"7Ld\", \"4if\", \n\"4dV\", \"4q7\", \"8B\", \"1OY\", \"0qT\", \"Rf\", \"7ox\", \"4Jz\", \"0MP\", \"nb\", \"576\", \"42v\", \"4XR\", \"4M3\", \"dll\", \"17U\", \"0nL\", \"3KN\", \"74h\", \"4Ub\", \n\"5ko\", \"6Nm\", \"cR\", \"v3\", \"y2\", \"lS\", \"6Al\", \"40G\", \"4Zc\", \"aER\", \"2Tn\", \"0aM\", \"18T\", \"OO\", \"4B2\", \"4WS\", \"bCL\", \"587\", \"ac\", \"0BQ\", \n\"0I2\", \"1Lr\", \"53T\", \"axL\", \"68z\", \"4IQ\", \"06w\", \"QM\", \"2Jl\", \"1on\", \"4Da\", \"65J\", \"7OO\", \"4jM\", \"g0\", \"6Y\", \"9X\", \"h1\", \"4eL\", \"6Po\", \n\"7nb\", \"aA0\", \"04F\", \"2Em\", \"d6f\", \"09v\", \"4FP\", \"4S1\", \"a3E\", \"bRO\", \"0SR\", \"4h\", \"AX\", \"T9\", \"4YD\", \"6lg\", \"6BK\", \"4wh\", \"0LF\", \"ot\", \n\"bD\", \"0Av\", \"4zX\", \"4o9\", \"5Q4\", \"4Tt\", \"0oZ\", \"Lh\", \"Ci\", \"14r\", \"5KT\", \"6nV\", \"ajh\", \"41Q\", \"0Nw\", \"mE\", \"22l\", \"0CG\", \"4xi\", \"6MJ\", \n\"6cf\", \"4VE\", \"0mk\", \"NY\", \"07a\", \"2FJ\", \"69l\", \"4HG\", \"4fk\", \"6SH\", \"2hf\", \"1Md\", \"0Pu\", \"7O\", \"7NY\", \"bQh\", \"4Ew\", \"6pT\", \"0k8\", \"1nx\", \n\"05P\", \"Rj\", \"5O6\", \"4Jv\", \"4dZ\", \"453\", \"8N\", \"1OU\", \"0RD\", \"qv\", \"7Lh\", \"4ij\", \"4GF\", \"66m\", \"2IK\", \"1lI\", \"5kc\", \"6Na\", \"21G\", \"27\", \n\"8gX\", \"Mr\", \"74d\", \"4Un\", \"bbm\", \"79T\", \"1f0\", \"0cp\", \"397\", \"nn\", \"5s2\", \"42z\", \"4ys\", \"6LP\", \"ao\", \"366\", \"0lq\", \"OC\", \"76U\", \"bml\", \n\"4Zo\", \"6oL\", \"Bs\", \"0aA\", \"0Om\", \"2zN\", \"7QA\", \"40K\", \"7OC\", \"4jA\", \"0Qo\", \"6U\", \"3ZA\", \"1ob\", \"4Dm\", \"65F\", \"68v\", \"b7f\", \"0rs\", \"QA\", \n\"dSO\", \"8UG\", \"4gq\", \"5b1\", \"5m0\", \"4hp\", \"8ZF\", \"4d\", \"1x2\", \"09z\", \"727\", \"67w\", \"7nn\", \"4Kl\", \"04J\", \"Sp\", \"9T\", \"1NO\", \"51i\", \"6Pc\", \n\"6BO\", \"43d\", \"0LB\", \"op\", \"2WM\", \"0bn\", \"5Ia\", \"6lc\", \"5Q0\", \"4Tp\", \"8fF\", \"Ll\", \"1D2\", \"0Ar\", \"cPN\", \"aem\", \"ajl\", \"41U\", \"0Ns\", \"mA\", \n\"Cm\", \"14v\", \"5KP\", \"6nR\", \"6cb\", \"4VA\", \"0mo\", \"2XL\", \"22h\", \"0CC\", \"4xm\", \"6MN\", \"4fo\", \"6SL\", \"2hb\", \"8TY\", \"07e\", \"2FN\", \"69h\", \"4HC\", \n\"4Es\", \"64X\", \"d5E\", \"83M\", \"0Pq\", \"7K\", \"a0f\", \"bQl\", \"50w\", \"457\", \"8J\", \"1OQ\", \"05T\", \"Rn\", \"5O2\", \"4Jr\", \"4GB\", \"66i\", \"2IO\", \"08d\", \n\"1Ba\", \"5z\", \"7Ll\", \"4in\", \"0nD\", \"Mv\", \"7ph\", \"4Uj\", \"5kg\", \"6Ne\", \"cZ\", \"23\", \"0MX\", \"nj\", \"5s6\", \"4vv\", \"4XZ\", \"6my\", \"1f4\", \"0ct\", \n\"0lu\", \"OG\", \"6bx\", \"5Gz\", \"4yw\", \"6LT\", \"ak\", \"0BY\", \"0Oi\", \"2zJ\", \"6Ad\", \"40O\", \"4Zk\", \"6oH\", \"Bw\", \"0aE\", \"2Jd\", \"1of\", \"4Di\", \"65B\", \n\"7OG\", \"4jE\", \"g8\", \"6Q\", \"2ix\", \"1Lz\", \"4gu\", \"5b5\", \"68r\", \"4IY\", \"0rw\", \"QE\", \"1x6\", \"1mW\", \"4FX\", \"4S9\", \"5m4\", \"4ht\", \"0SZ\", \"ph\", \n\"9P\", \"h9\", \"4eD\", \"6Pg\", \"7nj\", \"4Kh\", \"04N\", \"St\", \"22u\", \"375\", \"4xp\", \"598\", \"77V\", \"blo\", \"0mr\", \"1h2\", \"Cp\", \"14k\", \"5KM\", \"6nO\", \n\"7PB\", \"41H\", \"0Nn\", \"3kl\", \"20D\", \"34\", \"4zA\", \"6Ob\", \"6aN\", \"4Tm\", \"0oC\", \"Lq\", \"AA\", \"0bs\", \"bcn\", \"78W\", \"569\", \"43y\", \"384\", \"om\", \n\"9Kd\", \"5g\", \"5l3\", \"4is\", \"734\", \"66t\", \"1y1\", \"08y\", \"05I\", \"Rs\", \"7om\", \"4Jo\", \"4dC\", \"7AA\", \"8W\", \"1OL\", \"0Pl\", \"7V\", \"ats\", \"4kB\", \n\"4En\", \"64E\", \"2Kc\", \"1na\", \"07x\", \"PB\", \"69u\", \"b6e\", \"4fr\", \"5c2\", \"dRL\", \"8TD\", \"4Zv\", \"6oU\", \"Bj\", \"0aX\", \"0Ot\", \"lF\", \"6Ay\", \"40R\", \n\"4yj\", \"6LI\", \"av\", \"0BD\", \"0lh\", \"OZ\", \"6be\", \"4WF\", \"4XG\", \"6md\", \"2VJ\", \"0ci\", \"0ME\", \"nw\", \"6CH\", \"42c\", \"5kz\", \"6Nx\", \"cG\", \"1PT\", \n\"0nY\", \"Mk\", \"5P7\", \"4Uw\", \"5N5\", \"4Ku\", \"04S\", \"Si\", \"9M\", \"1NV\", \"4eY\", \"440\", \"7Mk\", \"4hi\", \"0SG\", \"pu\", \"2HH\", \"K8\", \"4FE\", \"67n\", \n\"68o\", \"4ID\", \"1\", \"QX\", \"2ie\", \"1Lg\", \"4gh\", \"6RK\", \"7OZ\", \"4jX\", \"0Qv\", \"6L\", \"2Jy\", \"3O9\", \"4Dt\", \"5A4\", \"4C9\", \"4VX\", \"0mv\", \"ND\", \n\"22q\", \"0CZ\", \"4xt\", \"6MW\", \"7PF\", \"41L\", \"x9\", \"mX\", \"Ct\", \"14o\", \"5KI\", \"6nK\", \"6aJ\", \"4Ti\", \"0oG\", \"Lu\", \"bY\", \"30\", \"4zE\", \"6Of\", \n\"5r5\", \"4wu\", \"380\", \"oi\", \"AE\", \"0bw\", \"4YY\", \"4L8\", \"5Wz\", \"66p\", \"1y5\", \"1lT\", \"0RY\", \"5c\", \"5l7\", \"4iw\", \"4dG\", \"6Qd\", \"8S\", \"1OH\", \n\"05M\", \"Rw\", \"7oi\", \"4Jk\", \"4Ej\", \"64A\", \"2Kg\", \"1ne\", \"0Ph\", \"7R\", \"7ND\", \"4kF\", \"4fv\", \"5c6\", \"0H9\", \"1My\", \"0st\", \"PF\", \"69q\", \"4HZ\", \n\"0Op\", \"lB\", \"ako\", \"40V\", \"4Zr\", \"6oQ\", \"Bn\", \"15u\", \"0ll\", \"2YO\", \"6ba\", \"4WB\", \"4yn\", \"6LM\", \"ar\", \"1Ra\", \"0MA\", \"ns\", \"6CL\", \"42g\", \n\"4XC\", \"79I\", \"2VN\", \"0cm\", \"8gE\", \"Mo\", \"5P3\", \"4Us\", \"bAl\", \"adn\", \"cC\", \"1PP\", \"9I\", \"1NR\", \"51t\", \"444\", \"5N1\", \"4Kq\", \"04W\", \"Sm\", \n\"2HL\", \"09g\", \"4FA\", \"67j\", \"7Mo\", \"4hm\", \"0SC\", \"4y\", \"2ia\", \"1Lc\", \"4gl\", \"6RO\", \"68k\", \"5Ya\", \"5\", \"2GM\", \"d4F\", \"82N\", \"4Dp\", \"5A0\", \n\"a1e\", \"bPo\", \"0Qr\", \"6H\", \"Cx\", \"14c\", \"5KE\", \"6nG\", \"7PJ\", \"4uH\", \"x5\", \"mT\", \"0V7\", \"0CV\", \"4xx\", \"590\", \"4C5\", \"4VT\", \"0mz\", \"NH\", \n\"AI\", \"16R\", \"4YU\", \"4L4\", \"561\", \"43q\", \"0LW\", \"oe\", \"bU\", \"w4\", \"4zI\", \"6Oj\", \"6aF\", \"4Te\", \"0oK\", \"Ly\", \"05A\", \"2Dj\", \"7oe\", \"4Jg\", \n\"4dK\", \"6Qh\", \"2jF\", \"i6\", \"0RU\", \"5o\", \"7Ly\", \"5yZ\", \"4GW\", \"4R6\", \"1y9\", \"08q\", \"07p\", \"PJ\", \"7mT\", \"4HV\", \"4fz\", \"6SY\", \"0H5\", \"1Mu\", \n\"f7\", \"sV\", \"7NH\", \"4kJ\", \"4Ef\", \"64M\", \"2Kk\", \"1ni\", \"4yb\", \"6LA\", \"23g\", \"0BL\", \"Z3\", \"OR\", \"6bm\", \"4WN\", \"c4d\", \"aEO\", \"Bb\", \"0aP\", \n\"8Fd\", \"lN\", \"4a3\", \"40Z\", \"5kr\", \"4n2\", \"cO\", \"8Ie\", \"0nQ\", \"Mc\", \"74u\", \"615\", \"4XO\", \"6ml\", \"2VB\", \"U2\", \"0MM\", \"2xn\", \"7Sa\", \"42k\", \n\"7Mc\", \"4ha\", \"0SO\", \"4u\", \"3Xa\", \"K0\", \"4FM\", \"67f\", \"aTL\", \"b5F\", \"0pS\", \"Sa\", \"9E\", \"8Wg\", \"4eQ\", \"448\", \"7OR\", \"4jP\", \"254\", \"6D\", \n\"0j3\", \"1os\", \"cnn\", \"65W\", \"68g\", \"4IL\", \"9\", \"QP\", \"2im\", \"1Lo\", \"53I\", \"6RC\", \"7PN\", \"41D\", \"x1\", \"mP\", \"2Um\", \"14g\", \"5KA\", \"6nC\", \n\"4C1\", \"4VP\", \"19W\", \"NL\", \"0V3\", \"0CR\", \"bBO\", \"594\", \"565\", \"43u\", \"0LS\", \"oa\", \"AM\", \"16V\", \"4YQ\", \"4L0\", \"6aB\", \"4Ta\", \"0oO\", \"2Zl\", \n\"bQ\", \"38\", \"4zM\", \"6On\", \"4dO\", \"6Ql\", \"2jB\", \"i2\", \"05E\", \"2Dn\", \"7oa\", \"4Jc\", \"4GS\", \"4R2\", \"d7e\", \"08u\", \"0RQ\", \"5k\", \"a2F\", \"bSL\", \n\"52W\", \"ayO\", \"0H1\", \"1Mq\", \"07t\", \"PN\", \"69y\", \"4HR\", \"4Eb\", \"64I\", \"2Ko\", \"1nm\", \"f3\", \"7Z\", \"7NL\", \"4kN\", \"Z7\", \"OV\", \"6bi\", \"4WJ\", \n\"4yf\", \"6LE\", \"az\", \"0BH\", \"0Ox\", \"lJ\", \"4a7\", \"4tV\", \"4Zz\", \"6oY\", \"Bf\", \"0aT\", \"0nU\", \"Mg\", \"74q\", \"5EZ\", \"5kv\", \"4n6\", \"cK\", \"1PX\", \n\"0MI\", \"2xj\", \"6CD\", \"42o\", \"4XK\", \"6mh\", \"2VF\", \"U6\", \"2HD\", \"K4\", \"4FI\", \"67b\", \"7Mg\", \"4he\", \"0SK\", \"4q\", \"9A\", \"1NZ\", \"4eU\", \"4p4\", \n\"5N9\", \"4Ky\", \"0pW\", \"Se\", \"0j7\", \"1ow\", \"4Dx\", \"5A8\", \"7OV\", \"4jT\", \"0Qz\", \"rH\", \"2ii\", \"1Lk\", \"4gd\", \"6RG\", \"68c\", \"4IH\", \"D5\", \"QT\", \n\"5Ls\", \"4I3\", \"F\", \"13U\", \"0IP\", \"jb\", \"536\", \"46v\", \"5oo\", \"6Jm\", \"gR\", \"r3\", \"0jL\", \"3ON\", \"6dA\", \"4Qb\", \"5NB\", \"aAR\", \"2Pn\", \"0eM\", \n\"0Ka\", \"hS\", \"6El\", \"44G\", \"49w\", \"abN\", \"ec\", \"0FQ\", \"8ae\", \"KO\", \"4F2\", \"4SS\", \"4X0\", \"4MQ\", \"02w\", \"UM\", \"0M2\", \"0XS\", \"57T\", \"a8D\", \n\"7KO\", \"4nM\", \"c0\", \"2Y\", \"2Nl\", \"1kn\", \"aJ1\", \"61J\", \"6zC\", \"aE0\", \"00F\", \"2Am\", \"yP\", \"l1\", \"4aL\", \"6To\", \"a7E\", \"58U\", \"0WR\", \"0h\", \n\"ZL\", \"84n\", \"4BP\", \"4W1\", \"fH\", \"0Ez\", \"5nu\", \"4k5\", \"5U8\", \"4Px\", \"0kV\", \"Hd\", \"ET\", \"P5\", \"5Mi\", \"6hk\", \"6FG\", \"47l\", \"0HJ\", \"kx\", \n\"dy\", \"0GK\", \"48m\", \"6IF\", \"6gj\", \"4RI\", \"0ig\", \"JU\", \"Ge\", \"0dW\", \"5OX\", \"5Z9\", \"4d4\", \"4qU\", \"1ZZ\", \"iI\", \"0Ty\", \"3C\", \"4z6\", \"4oW\", \n\"5QZ\", \"60P\", \"Yg\", \"0zU\", \"A6\", \"TW\", \"6yh\", \"4LK\", \"4bg\", \"6WD\", \"2lj\", \"0YI\", \"0VH\", \"1r\", \"6XE\", \"4mf\", \"4CJ\", \"62a\", \"2MG\", \"N7\", \n\"0uT\", \"Vf\", \"7kx\", \"4Nz\", \"5pw\", \"4u7\", \"xJ\", \"1KY\", \"0IT\", \"jf\", \"532\", \"46r\", \"5Lw\", \"4I7\", \"B\", \"0gx\", \"0jH\", \"Iz\", \"6dE\", \"4Qf\", \n\"5ok\", \"6Ji\", \"gV\", \"r7\", \"0Ke\", \"hW\", \"6Eh\", \"44C\", \"5NF\", \"6kD\", \"2Pj\", \"0eI\", \"0hy\", \"KK\", \"4F6\", \"4SW\", \"49s\", \"6HX\", \"eg\", \"0FU\", \n\"0M6\", \"0XW\", \"4cy\", \"5f9\", \"4X4\", \"4MU\", \"02s\", \"UI\", \"Xy\", \"1kj\", \"5PD\", \"61N\", \"7KK\", \"4nI\", \"c4\", \"vU\", \"yT\", \"l5\", \"4aH\", \"6Tk\", \n\"6zG\", \"4Od\", \"00B\", \"Wx\", \"ZH\", \"0yz\", \"4BT\", \"4W5\", \"5i8\", \"4lx\", \"0WV\", \"0l\", \"71v\", \"646\", \"0kR\", \"3NP\", \"fL\", \"8Lf\", \"5nq\", \"4k1\", \n\"6FC\", \"47h\", \"0HN\", \"29e\", \"EP\", \"P1\", \"5Mm\", \"6ho\", \"6gn\", \"4RM\", \"0ic\", \"JQ\", \"26d\", \"0GO\", \"48i\", \"6IB\", \"4d0\", \"45Y\", \"8Cg\", \"iM\", \n\"Ga\", \"0dS\", \"beN\", \"hYu\", \"ckm\", \"60T\", \"Yc\", \"0zQ\", \"207\", \"3G\", \"4z2\", \"4oS\", \"4bc\", \"7Ga\", \"2ln\", \"0YM\", \"A2\", \"TS\", \"6yl\", \"4LO\", \n\"4CN\", \"62e\", \"2MC\", \"N3\", \"0VL\", \"1v\", \"6XA\", \"4mb\", \"5ps\", \"4u3\", \"xN\", \"8Rd\", \"01X\", \"Vb\", \"aQO\", \"b0E\", \"5og\", \"6Je\", \"gZ\", \"63\", \n\"0jD\", \"Iv\", \"6dI\", \"4Qj\", \"7l9\", \"6iy\", \"N\", \"0gt\", \"0IX\", \"jj\", \"5w6\", \"4rv\", \"5mV\", \"5x7\", \"ek\", \"0FY\", \"0hu\", \"KG\", \"6fx\", \"5Cz\", \n\"5NJ\", \"6kH\", \"Fw\", \"0eE\", \"92\", \"3nk\", \"6Ed\", \"44O\", \"7KG\", \"4nE\", \"c8\", \"2Q\", \"Xu\", \"1kf\", \"5PH\", \"61B\", \"4X8\", \"4MY\", \"0vw\", \"UE\", \n\"2mx\", \"1Hz\", \"4cu\", \"5f5\", \"5i4\", \"4lt\", \"0WZ\", \"th\", \"ZD\", \"0yv\", \"4BX\", \"4W9\", \"6zK\", \"4Oh\", \"00N\", \"Wt\", \"yX\", \"l9\", \"4aD\", \"6Tg\", \n\"2SM\", \"0fn\", \"5Ma\", \"6hc\", \"6FO\", \"47d\", \"0HB\", \"kp\", \"24Y\", \"0Er\", \"bDo\", \"aam\", \"5U0\", \"4Pp\", \"8bF\", \"Hl\", \"Gm\", \"10v\", \"5OP\", \"5Z1\", \n\"anl\", \"45U\", \"0Js\", \"iA\", \"dq\", \"0GC\", \"48e\", \"6IN\", \"6gb\", \"4RA\", \"0io\", \"3Lm\", \"03e\", \"2BN\", \"7iA\", \"4LC\", \"4bo\", \"6WL\", \"zs\", \"0YA\", \n\"0Tq\", \"3K\", \"a4f\", \"bUl\", \"4As\", \"5D3\", \"Yo\", \"87M\", \"01T\", \"Vn\", \"5K2\", \"4Nr\", \"54w\", \"417\", \"xB\", \"1KQ\", \"1Fa\", \"1z\", \"6XM\", \"4mn\", \n\"4CB\", \"62i\", \"2MO\", \"0xl\", \"1za\", \"Ir\", \"6dM\", \"4Qn\", \"5oc\", \"6Ja\", \"25G\", \"67\", \"9Pe\", \"jn\", \"5w2\", \"46z\", \"bfm\", \"aCo\", \"J\", \"0gp\", \n\"0hq\", \"KC\", \"72U\", \"bil\", \"5mR\", \"5x3\", \"eo\", \"326\", \"96\", \"3no\", \"7UA\", \"44K\", \"5NN\", \"6kL\", \"Fs\", \"0eA\", \"Xq\", \"1kb\", \"5PL\", \"61F\", \n\"7KC\", \"4nA\", \"0Uo\", \"2U\", \"39U\", \"8QG\", \"4cq\", \"5f1\", \"aRl\", \"796\", \"0vs\", \"UA\", \"2LQ\", \"0yr\", \"767\", \"63w\", \"5i0\", \"4lp\", \"9Ng\", \"0d\", \n\"2oM\", \"0Zn\", \"55i\", \"6Tc\", \"6zO\", \"4Ol\", \"00J\", \"Wp\", \"6FK\", \"4sh\", \"0HF\", \"kt\", \"EX\", \"P9\", \"5Me\", \"6hg\", \"5U4\", \"4Pt\", \"0kZ\", \"Hh\", \n\"fD\", \"0Ev\", \"5ny\", \"4k9\", \"4d8\", \"45Q\", \"0Jw\", \"iE\", \"Gi\", \"10r\", \"5OT\", \"5Z5\", \"6gf\", \"4RE\", \"0ik\", \"JY\", \"du\", \"0GG\", \"48a\", \"6IJ\", \n\"4bk\", \"6WH\", \"zw\", \"0YE\", \"03a\", \"2BJ\", \"6yd\", \"4LG\", \"4Aw\", \"5D7\", \"Yk\", \"0zY\", \"0Tu\", \"3O\", \"6Zx\", \"bUh\", \"54s\", \"413\", \"xF\", \"1KU\", \n\"01P\", \"Vj\", \"5K6\", \"4Nv\", \"4CF\", \"62m\", \"2MK\", \"0xh\", \"0VD\", \"uv\", \"6XI\", \"4mj\", \"5NS\", \"6kQ\", \"Fn\", \"11u\", \"0Kp\", \"hB\", \"aoo\", \"44V\", \n\"49f\", \"6HM\", \"er\", \"1Va\", \"0hl\", \"3Mn\", \"6fa\", \"4SB\", \"5Lb\", \"7yA\", \"W\", \"0gm\", \"0IA\", \"js\", \"6GL\", \"46g\", \"bEl\", \"hyW\", \"gC\", \"0Dq\", \n\"8cE\", \"Io\", \"5T3\", \"4Qs\", \"5J1\", \"4Oq\", \"00W\", \"Wm\", \"yA\", \"0Zs\", \"55t\", \"404\", \"6YN\", \"4lm\", \"0WC\", \"0y\", \"2LL\", \"0yo\", \"4BA\", \"63j\", \n\"6xc\", \"bws\", \"02f\", \"2CM\", \"2ma\", \"0XB\", \"4cl\", \"6VO\", \"a5e\", \"bTo\", \"0Ur\", \"2H\", \"Xl\", \"86N\", \"5PQ\", \"5E0\", \"dh\", \"0GZ\", \"5lU\", \"5y4\", \n\"4G9\", \"4RX\", \"0iv\", \"JD\", \"Gt\", \"0dF\", \"5OI\", \"6jK\", \"6Dg\", \"45L\", \"81\", \"iX\", \"fY\", \"70\", \"5nd\", \"6Kf\", \"6eJ\", \"4Pi\", \"0kG\", \"Hu\", \n\"EE\", \"0fw\", \"5Mx\", \"4H8\", \"5v5\", \"4su\", \"1Xz\", \"ki\", \"0VY\", \"1c\", \"5h7\", \"4mw\", \"5Sz\", \"62p\", \"2MV\", \"0xu\", \"01M\", \"Vw\", \"7ki\", \"4Nk\", \n\"54n\", \"6Ud\", \"2nJ\", \"1KH\", \"0Th\", \"3R\", \"6Ze\", \"4oF\", \"4Aj\", \"60A\", \"Yv\", \"0zD\", \"0wt\", \"TF\", \"6yy\", \"4LZ\", \"4bv\", \"5g6\", \"zj\", \"0YX\", \n\"0Kt\", \"hF\", \"6Ey\", \"44R\", \"5NW\", \"6kU\", \"Fj\", \"0eX\", \"0hh\", \"KZ\", \"6fe\", \"4SF\", \"49b\", \"6HI\", \"ev\", \"0FD\", \"0IE\", \"jw\", \"6GH\", \"46c\", \n\"5Lf\", \"6id\", \"S\", \"0gi\", \"0jY\", \"Ik\", \"5T7\", \"4Qw\", \"5oz\", \"6Jx\", \"gG\", \"0Du\", \"yE\", \"0Zw\", \"4aY\", \"400\", \"5J5\", \"4Ou\", \"00S\", \"Wi\", \n\"ZY\", \"O8\", \"4BE\", \"63n\", \"6YJ\", \"4li\", \"0WG\", \"tu\", \"2me\", \"0XF\", \"4ch\", \"6VK\", \"6xg\", \"4MD\", \"02b\", \"UX\", \"Xh\", \"3K9\", \"5PU\", \"5E4\", \n\"7KZ\", \"4nX\", \"0Uv\", \"2L\", \"73V\", \"bho\", \"0ir\", \"1l2\", \"dl\", \"335\", \"48x\", \"5y0\", \"6Dc\", \"45H\", \"85\", \"3ol\", \"Gp\", \"0dB\", \"5OM\", \"6jO\", \n\"6eN\", \"4Pm\", \"0kC\", \"Hq\", \"24D\", \"74\", \"bDr\", \"6Kb\", \"529\", \"47y\", \"8AG\", \"km\", \"EA\", \"0fs\", \"bgn\", \"aBl\", \"774\", \"62t\", \"199\", \"0xq\", \n\"9Od\", \"1g\", \"5h3\", \"4ms\", \"54j\", \"7EA\", \"2nN\", \"1KL\", \"01I\", \"Vs\", \"7km\", \"4No\", \"4An\", \"60E\", \"Yr\", \"1ja\", \"0Tl\", \"3V\", \"6Za\", \"4oB\", \n\"4br\", \"5g2\", \"zn\", \"8PD\", \"03x\", \"TB\", \"aSo\", \"785\", \"49n\", \"6HE\", \"ez\", \"0FH\", \"0hd\", \"KV\", \"6fi\", \"4SJ\", \"bdI\", \"6kY\", \"Ff\", \"0eT\", \n\"0Kx\", \"hJ\", \"4e7\", \"4pV\", \"5ov\", \"4j6\", \"gK\", \"0Dy\", \"0jU\", \"Ig\", \"6dX\", \"5AZ\", \"5Lj\", \"6ih\", \"DW\", \"Q6\", \"0II\", \"28b\", \"6GD\", \"46o\", \n\"6YF\", \"4le\", \"0WK\", \"0q\", \"ZU\", \"O4\", \"4BI\", \"63b\", \"5J9\", \"4Oy\", \"0tW\", \"We\", \"yI\", \"1JZ\", \"4aU\", \"4t4\", \"7KV\", \"4nT\", \"0Uz\", \"vH\", \n\"Xd\", \"1kw\", \"5PY\", \"5E8\", \"6xk\", \"4MH\", \"02n\", \"UT\", \"2mi\", \"0XJ\", \"4cd\", \"6VG\", \"2Qm\", \"0dN\", \"5OA\", \"6jC\", \"6Do\", \"45D\", \"89\", \"iP\", \n\"0R3\", \"0GR\", \"48t\", \"acM\", \"4G1\", \"4RP\", \"94O\", \"JL\", \"EM\", \"12V\", \"5Mp\", \"4H0\", \"525\", \"47u\", \"0HS\", \"ka\", \"fQ\", \"78\", \"5nl\", \"6Kn\", \n\"6eB\", \"4Pa\", \"0kO\", \"3NM\", \"01E\", \"3PO\", \"7ka\", \"4Nc\", \"54f\", \"6Ul\", \"xS\", \"m2\", \"0VQ\", \"1k\", \"a6F\", \"59V\", \"4CS\", \"4V2\", \"195\", \"85m\", \n\"03t\", \"TN\", \"4Y3\", \"4LR\", \"56W\", \"a9G\", \"zb\", \"0YP\", \"b3\", \"3Z\", \"6Zm\", \"4oN\", \"4Ab\", \"60I\", \"2Oo\", \"0zL\", \"1xA\", \"KR\", \"6fm\", \"4SN\", \n\"49j\", \"6HA\", \"27g\", \"0FL\", \"8Bd\", \"hN\", \"4e3\", \"44Z\", \"bdM\", \"aAO\", \"Fb\", \"0eP\", \"0jQ\", \"Ic\", \"70u\", \"655\", \"5or\", \"4j2\", \"gO\", \"8Me\", \n\"0IM\", \"28f\", \"7Wa\", \"46k\", \"5Ln\", \"6il\", \"DS\", \"Q2\", \"ZQ\", \"O0\", \"4BM\", \"63f\", \"6YB\", \"4la\", \"0WO\", \"0u\", \"yM\", \"8Sg\", \"4aQ\", \"408\", \n\"aPL\", \"b1F\", \"0tS\", \"Wa\", \"0n3\", \"1ks\", \"bzO\", \"61W\", \"7KR\", \"4nP\", \"214\", \"2D\", \"2mm\", \"0XN\", \"57I\", \"6VC\", \"6xo\", \"4ML\", \"02j\", \"UP\", \n\"6Dk\", \"4qH\", \"0Jf\", \"iT\", \"Gx\", \"0dJ\", \"5OE\", \"6jG\", \"4G5\", \"4RT\", \"0iz\", \"JH\", \"dd\", \"0GV\", \"48p\", \"5y8\", \"521\", \"47q\", \"0HW\", \"ke\", \n\"EI\", \"12R\", \"5Mt\", \"4H4\", \"6eF\", \"4Pe\", \"0kK\", \"Hy\", \"fU\", \"s4\", \"5nh\", \"6Kj\", \"54b\", \"6Uh\", \"xW\", \"m6\", \"01A\", \"3PK\", \"7ke\", \"4Ng\", \n\"4CW\", \"4V6\", \"191\", \"0xy\", \"0VU\", \"1o\", \"6XX\", \"59R\", \"4bz\", \"6WY\", \"zf\", \"0YT\", \"03p\", \"TJ\", \"4Y7\", \"4LV\", \"4Af\", \"60M\", \"Yz\", \"0zH\", \n\"b7\", \"wV\", \"6Zi\", \"4oJ\", \"5H3\", \"4Ms\", \"02U\", \"Uo\", \"2mR\", \"0Xq\", \"57v\", \"426\", \"7Km\", \"4no\", \"0UA\", \"vs\", \"2NN\", \"1kL\", \"5Pb\", \"61h\", \n\"6za\", \"4OB\", \"00d\", \"2AO\", \"yr\", \"1Ja\", \"4an\", \"6TM\", \"a7g\", \"58w\", \"0Wp\", \"0J\", \"Zn\", \"84L\", \"4Br\", \"5G2\", \"5LQ\", \"5Y0\", \"d\", \"13w\", \n\"0Ir\", \"1L2\", \"amm\", \"46T\", \"5oM\", \"6JO\", \"gp\", \"0DB\", \"0jn\", \"3Ol\", \"6dc\", \"5Aa\", \"bdr\", \"6kb\", \"2PL\", \"0eo\", \"0KC\", \"hq\", \"6EN\", \"44e\", \n\"49U\", \"abl\", \"eA\", \"0Fs\", \"8aG\", \"Km\", \"5V1\", \"4Sq\", \"1Dz\", \"3a\", \"5j5\", \"4ou\", \"4AY\", \"4T8\", \"YE\", \"0zw\", \"03O\", \"Tu\", \"6yJ\", \"4Li\", \n\"4bE\", \"6Wf\", \"zY\", \"o8\", \"0Vj\", \"1P\", \"6Xg\", \"4mD\", \"4Ch\", \"62C\", \"2Me\", \"0xF\", \"0uv\", \"VD\", \"7kZ\", \"4NX\", \"5pU\", \"5e4\", \"xh\", \"3k9\", \n\"fj\", \"0EX\", \"5nW\", \"6KU\", \"6ey\", \"4PZ\", \"0kt\", \"HF\", \"Ev\", \"0fD\", \"5MK\", \"6hI\", \"6Fe\", \"47N\", \"0Hh\", \"kZ\", \"26B\", \"52\", \"48O\", \"6Id\", \n\"6gH\", \"4Rk\", \"0iE\", \"Jw\", \"GG\", \"0du\", \"5Oz\", \"6jx\", \"5t7\", \"4qw\", \"0JY\", \"ik\", \"2mV\", \"0Xu\", \"57r\", \"422\", \"5H7\", \"4Mw\", \"02Q\", \"Uk\", \n\"2NJ\", \"1kH\", \"5Pf\", \"61l\", \"7Ki\", \"4nk\", \"0UE\", \"vw\", \"yv\", \"0ZD\", \"4aj\", \"6TI\", \"6ze\", \"4OF\", \"0th\", \"WZ\", \"Zj\", \"0yX\", \"4Bv\", \"5G6\", \n\"6Yy\", \"4lZ\", \"0Wt\", \"0N\", \"0Iv\", \"jD\", \"4g9\", \"46P\", \"5LU\", \"5Y4\", \"Dh\", \"0gZ\", \"0jj\", \"IX\", \"6dg\", \"4QD\", \"5oI\", \"6JK\", \"gt\", \"0DF\", \n\"0KG\", \"hu\", \"6EJ\", \"44a\", \"5Nd\", \"6kf\", \"FY\", \"S8\", \"1xz\", \"Ki\", \"5V5\", \"4Su\", \"49Q\", \"4h8\", \"eE\", \"0Fw\", \"756\", \"60v\", \"YA\", \"0zs\", \n\"9Mf\", \"3e\", \"5j1\", \"4oq\", \"4bA\", \"6Wb\", \"2lL\", \"0Yo\", \"03K\", \"Tq\", \"6yN\", \"4Lm\", \"4Cl\", \"62G\", \"2Ma\", \"0xB\", \"0Vn\", \"1T\", \"6Xc\", \"59i\", \n\"54Y\", \"5e0\", \"xl\", \"8RF\", \"01z\", \"1p2\", \"aQm\", \"b0g\", \"71T\", \"bjm\", \"0kp\", \"HB\", \"fn\", \"317\", \"5nS\", \"6KQ\", \"6Fa\", \"47J\", \"0Hl\", \"29G\", \n\"Er\", \"12i\", \"5MO\", \"6hM\", \"6gL\", \"4Ro\", \"0iA\", \"Js\", \"26F\", \"56\", \"48K\", \"7YA\", \"5t3\", \"4qs\", \"8CE\", \"io\", \"GC\", \"0dq\", \"bel\", \"hYW\", \n\"7Ke\", \"4ng\", \"0UI\", \"2s\", \"XW\", \"M6\", \"5Pj\", \"6uh\", \"6xX\", \"6m9\", \"0vU\", \"Ug\", \"2mZ\", \"0Xy\", \"4cW\", \"4v6\", \"4y7\", \"4lV\", \"0Wx\", \"0B\", \n\"Zf\", \"0yT\", \"4Bz\", \"63Q\", \"6zi\", \"4OJ\", \"B7\", \"WV\", \"yz\", \"0ZH\", \"4af\", \"6TE\", \"5oE\", \"6JG\", \"gx\", \"0DJ\", \"0jf\", \"IT\", \"6dk\", \"4QH\", \n\"5LY\", \"5Y8\", \"l\", \"0gV\", \"0Iz\", \"jH\", \"4g5\", \"4rT\", \"5mt\", \"4h4\", \"eI\", \"1VZ\", \"0hW\", \"Ke\", \"5V9\", \"4Sy\", \"5Nh\", \"6kj\", \"FU\", \"S4\", \n\"0KK\", \"hy\", \"6EF\", \"44m\", \"03G\", \"2Bl\", \"6yB\", \"4La\", \"4bM\", \"6Wn\", \"zQ\", \"o0\", \"0TS\", \"3i\", \"a4D\", \"bUN\", \"4AQ\", \"4T0\", \"YM\", \"87o\", \n\"01v\", \"VL\", \"7kR\", \"4NP\", \"54U\", \"hft\", \"0N3\", \"1Ks\", \"0Vb\", \"1X\", \"6Xo\", \"4mL\", \"5SA\", \"62K\", \"2Mm\", \"0xN\", \"2So\", \"0fL\", \"5MC\", \"6hA\", \n\"6Fm\", \"47F\", \"1XA\", \"kR\", \"fb\", \"0EP\", \"bDM\", \"aaO\", \"4E3\", \"4PR\", \"8bd\", \"HN\", \"GO\", \"10T\", \"5Or\", \"4J2\", \"507\", \"45w\", \"0JQ\", \"ic\", \n\"dS\", \"q2\", \"48G\", \"6Il\", \"73i\", \"4Rc\", \"0iM\", \"3LO\", \"XS\", \"M2\", \"5Pn\", \"61d\", \"7Ka\", \"4nc\", \"0UM\", \"2w\", \"39w\", \"8Qe\", \"4cS\", \"4v2\", \n\"aRN\", \"b3D\", \"02Y\", \"Uc\", \"Zb\", \"0yP\", \"bxM\", \"63U\", \"4y3\", \"4lR\", \"236\", \"0F\", \"2oo\", \"0ZL\", \"4ab\", \"6TA\", \"6zm\", \"4ON\", \"B3\", \"WR\", \n\"0jb\", \"IP\", \"6do\", \"4QL\", \"5oA\", \"6JC\", \"25e\", \"0DN\", \"9PG\", \"jL\", \"4g1\", \"46X\", \"686\", \"aCM\", \"h\", \"0gR\", \"0hS\", \"Ka\", \"72w\", \"677\", \n\"49Y\", \"4h0\", \"eM\", \"8Og\", \"0KO\", \"3nM\", \"6EB\", \"44i\", \"5Nl\", \"6kn\", \"FQ\", \"S0\", \"4bI\", \"6Wj\", \"zU\", \"o4\", \"03C\", \"Ty\", \"6yF\", \"4Le\", \n\"4AU\", \"4T4\", \"YI\", \"1jZ\", \"0TW\", \"3m\", \"5j9\", \"4oy\", \"54Q\", \"5e8\", \"xd\", \"1Kw\", \"01r\", \"VH\", \"7kV\", \"4NT\", \"4Cd\", \"62O\", \"2Mi\", \"0xJ\", \n\"0Vf\", \"uT\", \"6Xk\", \"4mH\", \"6Fi\", \"47B\", \"0Hd\", \"kV\", \"Ez\", \"0fH\", \"5MG\", \"6hE\", \"4E7\", \"4PV\", \"0kx\", \"HJ\", \"ff\", \"0ET\", \"bDI\", \"6KY\", \n\"503\", \"45s\", \"0JU\", \"ig\", \"GK\", \"0dy\", \"5Ov\", \"4J6\", \"6gD\", \"4Rg\", \"0iI\", \"3LK\", \"dW\", \"q6\", \"48C\", \"6Ih\", \"4Z2\", \"4OS\", \"00u\", \"WO\", \n\"yc\", \"0ZQ\", \"55V\", \"hgw\", \"6Yl\", \"4lO\", \"a2\", \"tS\", \"2Ln\", \"0yM\", \"4Bc\", \"63H\", \"6xA\", \"4Mb\", \"02D\", \"2Co\", \"2mC\", \"n3\", \"4cN\", \"6Vm\", \n\"a5G\", \"bTM\", \"0UP\", \"2j\", \"XN\", \"86l\", \"5Ps\", \"4U3\", \"5Nq\", \"4K1\", \"FL\", \"11W\", \"0KR\", \"3nP\", \"514\", \"44t\", \"49D\", \"6Ho\", \"eP\", \"49\", \n\"0hN\", \"3ML\", \"6fC\", \"5CA\", \"aV1\", \"6iB\", \"u\", \"0gO\", \"0Ic\", \"jQ\", \"6Gn\", \"46E\", \"bEN\", \"hyu\", \"ga\", \"0DS\", \"8cg\", \"IM\", \"4D0\", \"4QQ\", \n\"1FZ\", \"1A\", \"4x4\", \"4mU\", \"4Cy\", \"5F9\", \"0m6\", \"0xW\", \"C4\", \"VU\", \"7kK\", \"4NI\", \"54L\", \"6UF\", \"xy\", \"1Kj\", \"0TJ\", \"3p\", \"6ZG\", \"4od\", \n\"4AH\", \"60c\", \"YT\", \"L5\", \"0wV\", \"Td\", \"5I8\", \"4Lx\", \"4bT\", \"4w5\", \"zH\", \"0Yz\", \"dJ\", \"0Gx\", \"5lw\", \"4i7\", \"6gY\", \"4Rz\", \"0iT\", \"Jf\", \n\"GV\", \"R7\", \"5Ok\", \"6ji\", \"6DE\", \"45n\", \"0JH\", \"iz\", \"24b\", \"0EI\", \"5nF\", \"6KD\", \"6eh\", \"4PK\", \"0ke\", \"HW\", \"Eg\", \"0fU\", \"5MZ\", \"6hX\", \n\"4f6\", \"4sW\", \"0Hy\", \"kK\", \"yg\", \"0ZU\", \"55R\", \"6TX\", \"4Z6\", \"4OW\", \"00q\", \"WK\", \"2Lj\", \"0yI\", \"4Bg\", \"63L\", \"6Yh\", \"4lK\", \"a6\", \"tW\", \n\"2mG\", \"n7\", \"4cJ\", \"6Vi\", \"6xE\", \"4Mf\", \"0vH\", \"Uz\", \"XJ\", \"1kY\", \"5Pw\", \"4U7\", \"7Kx\", \"4nz\", \"0UT\", \"2n\", \"0KV\", \"hd\", \"510\", \"44p\", \n\"5Nu\", \"4K5\", \"FH\", \"0ez\", \"0hJ\", \"Kx\", \"6fG\", \"4Sd\", \"5mi\", \"6Hk\", \"eT\", \"p5\", \"0Ig\", \"jU\", \"6Gj\", \"46A\", \"5LD\", \"6iF\", \"q\", \"0gK\", \n\"1zZ\", \"II\", \"4D4\", \"4QU\", \"5oX\", \"5z9\", \"ge\", \"0DW\", \"byN\", \"62V\", \"0m2\", \"0xS\", \"225\", \"1E\", \"4x0\", \"4mQ\", \"54H\", \"6UB\", \"2nl\", \"1Kn\", \n\"C0\", \"VQ\", \"7kO\", \"4NM\", \"4AL\", \"60g\", \"YP\", \"L1\", \"0TN\", \"3t\", \"6ZC\", \"ae0\", \"4bP\", \"439\", \"zL\", \"8Pf\", \"03Z\", \"0b3\", \"aSM\", \"b2G\", \n\"73t\", \"664\", \"0iP\", \"Jb\", \"dN\", \"8Nd\", \"48Z\", \"4i3\", \"6DA\", \"45j\", \"0JL\", \"3oN\", \"GR\", \"R3\", \"5Oo\", \"6jm\", \"6el\", \"4PO\", \"0ka\", \"HS\", \n\"24f\", \"0EM\", \"5nB\", \"aaR\", \"4f2\", \"4sS\", \"8Ae\", \"kO\", \"Ec\", \"0fQ\", \"695\", \"aBN\", \"6Yd\", \"4lG\", \"0Wi\", \"0S\", \"Zw\", \"0yE\", \"4Bk\", \"6wH\", \n\"6zx\", \"buh\", \"0tu\", \"WG\", \"yk\", \"0ZY\", \"4aw\", \"5d7\", \"5k6\", \"4nv\", \"0UX\", \"2b\", \"XF\", \"1kU\", \"741\", \"61q\", \"6xI\", \"4Mj\", \"02L\", \"Uv\", \n\"2mK\", \"0Xh\", \"4cF\", \"6Ve\", \"49L\", \"6Hg\", \"eX\", \"41\", \"0hF\", \"Kt\", \"6fK\", \"4Sh\", \"5Ny\", \"4K9\", \"FD\", \"0ev\", \"0KZ\", \"hh\", \"5u4\", \"4pt\", \n\"5oT\", \"5z5\", \"gi\", \"1Tz\", \"0jw\", \"IE\", \"4D8\", \"4QY\", \"5LH\", \"6iJ\", \"Du\", \"0gG\", \"0Ik\", \"jY\", \"6Gf\", \"46M\", \"01g\", \"3Pm\", \"7kC\", \"4NA\", \n\"54D\", \"6UN\", \"xq\", \"1Kb\", \"0Vs\", \"1I\", \"a6d\", \"59t\", \"4Cq\", \"5F1\", \"d3G\", \"85O\", \"03V\", \"Tl\", \"5I0\", \"4Lp\", \"56u\", \"435\", \"2lQ\", \"0Yr\", \n\"0TB\", \"3x\", \"6ZO\", \"4ol\", \"5Qa\", \"60k\", \"2OM\", \"0zn\", \"2QO\", \"0dl\", \"5Oc\", \"6ja\", \"6DM\", \"45f\", \"1Za\", \"ir\", \"dB\", \"0Gp\", \"48V\", \"aco\", \n\"5W2\", \"4Rr\", \"94m\", \"Jn\", \"Eo\", \"12t\", \"5MR\", \"5X3\", \"aln\", \"47W\", \"0Hq\", \"kC\", \"fs\", \"0EA\", \"5nN\", \"6KL\", \"71I\", \"4PC\", \"0km\", \"3No\", \n\"Zs\", \"0yA\", \"4Bo\", \"63D\", \"7IA\", \"4lC\", \"0Wm\", \"0W\", \"yo\", \"8SE\", \"4as\", \"5d3\", \"aPn\", \"b1d\", \"00y\", \"WC\", \"XB\", \"1kQ\", \"745\", \"61u\", \n\"5k2\", \"4nr\", \"9Le\", \"2f\", \"2mO\", \"0Xl\", \"4cB\", \"6Va\", \"6xM\", \"4Mn\", \"02H\", \"Ur\", \"0hB\", \"Kp\", \"6fO\", \"4Sl\", \"49H\", \"6Hc\", \"27E\", \"45\", \n\"8BF\", \"hl\", \"518\", \"44x\", \"bdo\", \"aAm\", \"2PQ\", \"0er\", \"0js\", \"IA\", \"70W\", \"bkn\", \"5oP\", \"5z1\", \"gm\", \"304\", \"0Io\", \"28D\", \"6Gb\", \"46I\", \n\"5LL\", \"6iN\", \"y\", \"0gC\", \"5pH\", \"6UJ\", \"xu\", \"1Kf\", \"C8\", \"VY\", \"7kG\", \"4NE\", \"4Cu\", \"5F5\", \"2Mx\", \"1hz\", \"0Vw\", \"1M\", \"4x8\", \"4mY\", \n\"4bX\", \"431\", \"zD\", \"0Yv\", \"03R\", \"Th\", \"5I4\", \"4Lt\", \"4AD\", \"60o\", \"YX\", \"L9\", \"0TF\", \"wt\", \"6ZK\", \"4oh\", \"6DI\", \"45b\", \"0JD\", \"iv\", \n\"GZ\", \"0dh\", \"5Og\", \"6je\", \"5W6\", \"4Rv\", \"0iX\", \"Jj\", \"dF\", \"0Gt\", \"48R\", \"6Iy\", \"6Fx\", \"47S\", \"0Hu\", \"kG\", \"Ek\", \"0fY\", \"5MV\", \"5X7\", \n\"6ed\", \"4PG\", \"0ki\", \"3Nk\", \"fw\", \"0EE\", \"5nJ\", \"6KH\", \"356\", \"bo\", \"6OP\", \"4zs\", \"bnl\", \"75U\", \"LC\", \"0oq\", \"0bA\", \"As\", \"6lL\", \"4Yo\", \n\"43K\", \"7RA\", \"2yN\", \"0Lm\", \"17\", \"22G\", \"6Ma\", \"4xB\", \"4Vn\", \"6cM\", \"Nr\", \"19i\", \"14Y\", \"CB\", \"aDo\", \"bam\", \"41z\", \"5p2\", \"mn\", \"8GD\", \n\"7d\", \"8YF\", \"4kp\", \"5n0\", \"64w\", \"717\", \"1nS\", \"2KQ\", \"Pp\", \"07J\", \"4Hl\", \"69G\", \"6Sc\", \"52i\", \"1MO\", \"2hM\", \"5U\", \"0Ro\", \"4iA\", \"7LC\", \n\"66F\", \"4Gm\", \"08K\", \"3YA\", \"RA\", \"0qs\", \"b4f\", \"aUl\", \"5a1\", \"4dq\", \"8VG\", \"8e\", \"6mV\", \"4Xu\", \"17r\", \"022\", \"nE\", \"0Mw\", \"42Q\", \"4c8\", \n\"6NJ\", \"5kH\", \"1Pf\", \"cu\", \"MY\", \"X8\", \"4UE\", \"74O\", \"6og\", \"4ZD\", \"W9\", \"BX\", \"lt\", \"0OF\", \"4th\", \"6AK\", \"4l9\", \"4yX\", \"0Bv\", \"aD\", \n\"Oh\", \"0lZ\", \"4Wt\", \"5R4\", \"4Iv\", \"5L6\", \"Qj\", \"06P\", \"1LU\", \"1Y4\", \"463\", \"4gZ\", \"4jj\", \"7Oh\", \"rv\", \"0QD\", \"1oI\", \"2JK\", \"65m\", \"4DF\", \n\"4KG\", \"7nE\", \"2EJ\", \"04a\", \"1Nd\", \"2kf\", \"6PH\", \"4ek\", \"5xz\", \"492\", \"4O\", \"0Su\", \"09Q\", \"0h8\", \"5C7\", \"4Fw\", \"5Dz\", \"6ax\", \"LG\", \"0ou\", \n\"0AY\", \"bk\", \"6OT\", \"4zw\", \"43O\", \"6Bd\", \"2yJ\", \"0Li\", \"0bE\", \"Aw\", \"6lH\", \"4Yk\", \"4Vj\", \"6cI\", \"Nv\", \"0mD\", \"13\", \"22C\", \"6Me\", \"4xF\", \n\"4uv\", \"5p6\", \"mj\", \"0NX\", \"1pU\", \"CF\", \"6ny\", \"7k9\", \"4P9\", \"4EX\", \"1nW\", \"2KU\", \"sh\", \"0PZ\", \"4kt\", \"5n4\", \"6Sg\", \"4fD\", \"k9\", \"2hI\", \n\"Pt\", \"07N\", \"4Hh\", \"69C\", \"66B\", \"4Gi\", \"08O\", \"2Id\", \"5Q\", \"d8\", \"4iE\", \"7LG\", \"5a5\", \"4du\", \"1Oz\", \"8a\", \"RE\", \"0qw\", \"4JY\", \"aUh\", \n\"nA\", \"0Ms\", \"42U\", \"ail\", \"6mR\", \"4Xq\", \"17v\", \"026\", \"3Km\", \"0no\", \"4UA\", \"74K\", \"6NN\", \"5kL\", \"1Pb\", \"cq\", \"lp\", \"0OB\", \"40d\", \"6AO\", \n\"6oc\", \"5Ja\", \"0an\", \"2TM\", \"Ol\", \"18w\", \"4Wp\", \"5R0\", \"afm\", \"bCo\", \"0Br\", \"1G2\", \"1LQ\", \"1Y0\", \"467\", \"53w\", \"4Ir\", \"5L2\", \"Qn\", \"06T\", \n\"1oM\", \"2JO\", \"65i\", \"4DB\", \"4jn\", \"7Ol\", \"6z\", \"1Aa\", \"8WY\", \"2kb\", \"6PL\", \"4eo\", \"4KC\", \"7nA\", \"2EN\", \"04e\", \"09U\", \"d6E\", \"5C3\", \"4Fs\", \n\"bRl\", \"496\", \"4K\", \"0Sq\", \"0bI\", \"2Wj\", \"6lD\", \"4Yg\", \"43C\", \"6Bh\", \"oW\", \"z6\", \"0AU\", \"bg\", \"6OX\", \"5jZ\", \"4TW\", \"4A6\", \"LK\", \"0oy\", \n\"14Q\", \"CJ\", \"4N7\", \"5Kw\", \"41r\", \"542\", \"mf\", \"0NT\", \"u7\", \"22O\", \"6Mi\", \"4xJ\", \"4Vf\", \"6cE\", \"Nz\", \"0mH\", \"Px\", \"07B\", \"4Hd\", \"69O\", \n\"6Sk\", \"4fH\", \"k5\", \"2hE\", \"7l\", \"0PV\", \"4kx\", \"5n8\", \"4P5\", \"4ET\", \"83j\", \"2KY\", \"RI\", \"05s\", \"4JU\", \"7oW\", \"5a9\", \"4dy\", \"1Ov\", \"8m\", \n\"qU\", \"d4\", \"4iI\", \"7LK\", \"66N\", \"4Ge\", \"08C\", \"2Ih\", \"6NB\", \"a59\", \"1Pn\", \"21d\", \"MQ\", \"X0\", \"4UM\", \"74G\", \"79w\", \"bbN\", \"0cS\", \"0v2\", \n\"nM\", \"8Dg\", \"42Y\", \"4c0\", \"4l1\", \"4yP\", \"8Kf\", \"aL\", \"0y3\", \"0lR\", \"636\", \"76v\", \"6oo\", \"4ZL\", \"W1\", \"BP\", \"2zm\", \"0ON\", \"40h\", \"6AC\", \n\"4jb\", \"auS\", \"6v\", \"0QL\", \"I3\", \"2JC\", \"65e\", \"4DN\", \"b7E\", \"68U\", \"Qb\", \"06X\", \"286\", \"dSl\", \"4r3\", \"4gR\", \"4hS\", \"7MQ\", \"4G\", \"277\", \n\"09Y\", \"0h0\", \"67T\", \"b8D\", \"4KO\", \"7nM\", \"SS\", \"F2\", \"1Nl\", \"9w\", \"azR\", \"4ec\", \"43G\", \"6Bl\", \"oS\", \"z2\", \"0bM\", \"2Wn\", \"78i\", \"4Yc\", \n\"4TS\", \"4A2\", \"LO\", \"8fe\", \"0AQ\", \"bc\", \"aeN\", \"cPm\", \"41v\", \"546\", \"mb\", \"0NP\", \"14U\", \"CN\", \"4N3\", \"5Ks\", \"4Vb\", \"6cA\", \"2Xo\", \"0mL\", \n\"u3\", \"22K\", \"6Mm\", \"4xN\", \"6So\", \"4fL\", \"k1\", \"2hA\", \"2Fm\", \"07F\", \"5XA\", \"69K\", \"4P1\", \"4EP\", \"83n\", \"d5f\", \"7h\", \"0PR\", \"bQO\", \"a0E\", \n\"hbu\", \"50T\", \"1Or\", \"8i\", \"RM\", \"05w\", \"4JQ\", \"7oS\", \"66J\", \"4Ga\", \"08G\", \"2Il\", \"5Y\", \"d0\", \"4iM\", \"7LO\", \"MU\", \"X4\", \"4UI\", \"74C\", \n\"6NF\", \"5kD\", \"1Pj\", \"cy\", \"nI\", \"2m9\", \"4vU\", \"4c4\", \"6mZ\", \"4Xy\", \"0cW\", \"0v6\", \"Od\", \"0lV\", \"4Wx\", \"5R8\", \"4l5\", \"4yT\", \"0Bz\", \"aH\", \n\"lx\", \"0OJ\", \"40l\", \"6AG\", \"6ok\", \"4ZH\", \"W5\", \"BT\", \"I7\", \"2JG\", \"65a\", \"4DJ\", \"4jf\", \"7Od\", \"6r\", \"0QH\", \"1LY\", \"1Y8\", \"4r7\", \"4gV\", \n\"4Iz\", \"68Q\", \"Qf\", \"0rT\", \"1mt\", \"0h4\", \"67P\", \"5VZ\", \"4hW\", \"7MU\", \"4C\", \"0Sy\", \"1Nh\", \"9s\", \"6PD\", \"4eg\", \"4KK\", \"7nI\", \"SW\", \"F6\", \n\"8Je\", \"22V\", \"4m2\", \"4xS\", \"625\", \"77u\", \"Nc\", \"0mQ\", \"V2\", \"CS\", \"6nl\", \"5Kn\", \"41k\", \"7Pa\", \"3kO\", \"0NM\", \"0AL\", \"20g\", \"6OA\", \"4zb\", \n\"4TN\", \"6am\", \"LR\", \"Y3\", \"0bP\", \"Ab\", \"78t\", \"bcM\", \"43Z\", \"4b3\", \"oN\", \"8Ed\", \"5D\", \"264\", \"4iP\", \"489\", \"66W\", \"b9G\", \"08Z\", \"0i3\", \n\"RP\", \"G1\", \"4JL\", \"7oN\", \"6QC\", \"50I\", \"1Oo\", \"8t\", \"7u\", \"0PO\", \"4ka\", \"7Nc\", \"64f\", \"4EM\", \"H0\", \"963\", \"Pa\", \"0sS\", \"b6F\", \"69V\", \n\"478\", \"4fQ\", \"295\", \"dRo\", \"4O4\", \"4ZU\", \"15R\", \"BI\", \"le\", \"0OW\", \"40q\", \"551\", \"6Lj\", \"4yI\", \"t4\", \"aU\", \"Oy\", \"0lK\", \"4We\", \"6bF\", \n\"6mG\", \"4Xd\", \"0cJ\", \"2Vi\", \"nT\", \"0Mf\", \"4vH\", \"6Ck\", \"adI\", \"5kY\", \"1Pw\", \"cd\", \"MH\", \"0nz\", \"4UT\", \"7pV\", \"4KV\", \"7nT\", \"SJ\", \"04p\", \n\"1Nu\", \"9n\", \"6PY\", \"4ez\", \"4hJ\", \"7MH\", \"pV\", \"e7\", \"1mi\", \"2Hk\", \"67M\", \"4Ff\", \"4Ig\", \"68L\", \"2Gj\", \"06A\", \"j6\", \"2iF\", \"6Rh\", \"4gK\", \n\"5zZ\", \"7Oy\", \"6o\", \"0QU\", \"1oX\", \"1z9\", \"4Q6\", \"4DW\", \"5FZ\", \"6cX\", \"Ng\", \"0mU\", \"0Cy\", \"1F9\", \"4m6\", \"4xW\", \"41o\", \"7Pe\", \"3kK\", \"0NI\", \n\"V6\", \"CW\", \"6nh\", \"5Kj\", \"4TJ\", \"6ai\", \"LV\", \"Y7\", \"0AH\", \"bz\", \"6OE\", \"4zf\", \"4wV\", \"4b7\", \"oJ\", \"0Lx\", \"0bT\", \"Af\", \"6lY\", \"4Yz\", \n\"5B8\", \"4Gx\", \"1lw\", \"0i7\", \"qH\", \"0Rz\", \"4iT\", \"7LV\", \"6QG\", \"4dd\", \"1Ok\", \"8p\", \"RT\", \"G5\", \"4JH\", \"7oJ\", \"64b\", \"4EI\", \"H4\", \"2KD\", \n\"7q\", \"0PK\", \"4ke\", \"7Ng\", \"4s4\", \"4fU\", \"1MZ\", \"2hX\", \"Pe\", \"0sW\", \"4Hy\", \"5M9\", \"la\", \"0OS\", \"40u\", \"555\", \"4O0\", \"4ZQ\", \"15V\", \"BM\", \n\"2Yl\", \"0lO\", \"4Wa\", \"6bB\", \"6Ln\", \"4yM\", \"08\", \"aQ\", \"nP\", \"0Mb\", \"42D\", \"6Co\", \"6mC\", \"5HA\", \"0cN\", \"2Vm\", \"ML\", \"8gf\", \"4UP\", \"74Z\", \n\"adM\", \"bAO\", \"1Ps\", \"0U3\", \"1Nq\", \"9j\", \"azO\", \"51W\", \"4KR\", \"7nP\", \"SN\", \"04t\", \"09D\", \"2Ho\", \"67I\", \"4Fb\", \"4hN\", \"7ML\", \"4Z\", \"e3\", \n\"j2\", \"2iB\", \"6Rl\", \"4gO\", \"4Ic\", \"68H\", \"2Gn\", \"06E\", \"82m\", \"d4e\", \"4Q2\", \"4DS\", \"bPL\", \"a1F\", \"6k\", \"0QQ\", \"1pH\", \"2UJ\", \"6nd\", \"5Kf\", \n\"41c\", \"7Pi\", \"mw\", \"0NE\", \"0Cu\", \"1F5\", \"6Mx\", \"5hz\", \"4Vw\", \"5S7\", \"Nk\", \"0mY\", \"0bX\", \"Aj\", \"6lU\", \"4Yv\", \"43R\", \"6By\", \"oF\", \"0Lt\", \n\"0AD\", \"bv\", \"6OI\", \"4zj\", \"4TF\", \"6ae\", \"LZ\", \"0oh\", \"RX\", \"G9\", \"4JD\", \"7oF\", \"6QK\", \"4dh\", \"1Og\", \"2je\", \"5L\", \"0Rv\", \"4iX\", \"481\", \n\"5B4\", \"4Gt\", \"08R\", \"2Iy\", \"Pi\", \"07S\", \"4Hu\", \"5M5\", \"470\", \"4fY\", \"1MV\", \"1X7\", \"su\", \"0PG\", \"4ki\", \"7Nk\", \"64n\", \"4EE\", \"H8\", \"2KH\", \n\"6Lb\", \"4yA\", \"04\", \"23D\", \"Oq\", \"0lC\", \"4Wm\", \"6bN\", \"aEl\", \"c4G\", \"0as\", \"BA\", \"lm\", \"8FG\", \"40y\", \"559\", \"6NS\", \"5kQ\", \"345\", \"cl\", \n\"1k2\", \"0nr\", \"boo\", \"74V\", \"6mO\", \"4Xl\", \"0cB\", \"2Va\", \"2xM\", \"0Mn\", \"42H\", \"6Cc\", \"4hB\", \"aws\", \"4V\", \"0Sl\", \"09H\", \"2Hc\", \"67E\", \"4Fn\", \n\"b5e\", \"aTo\", \"SB\", \"04x\", \"8WD\", \"9f\", \"6PQ\", \"4er\", \"4js\", \"5o3\", \"6g\", \"8XE\", \"1oP\", \"1z1\", \"65t\", \"704\", \"4Io\", \"68D\", \"Qs\", \"06I\", \n\"1LL\", \"2iN\", \"7BA\", \"4gC\", \"41g\", \"7Pm\", \"ms\", \"0NA\", \"14D\", \"2UN\", \"aDr\", \"5Kb\", \"4Vs\", \"5S3\", \"No\", \"19t\", \"0Cq\", \"1F1\", \"agn\", \"bBl\", \n\"43V\", \"aho\", \"oB\", \"0Lp\", \"16u\", \"An\", \"6lQ\", \"4Yr\", \"4TB\", \"6aa\", \"2ZO\", \"0ol\", \"1Qa\", \"br\", \"6OM\", \"4zn\", \"6QO\", \"4dl\", \"1Oc\", \"8x\", \n\"2DM\", \"05f\", \"5Za\", \"7oB\", \"5B0\", \"4Gp\", \"08V\", \"d7F\", \"5H\", \"0Rr\", \"bSo\", \"485\", \"474\", \"52t\", \"1MR\", \"1X3\", \"Pm\", \"07W\", \"4Hq\", \"5M1\", \n\"64j\", \"4EA\", \"1nN\", \"2KL\", \"7y\", \"0PC\", \"4km\", \"7No\", \"Ou\", \"0lG\", \"4Wi\", \"6bJ\", \"6Lf\", \"4yE\", \"00\", \"aY\", \"li\", \"8FC\", \"4tu\", \"5q5\", \n\"4O8\", \"4ZY\", \"0aw\", \"BE\", \"MD\", \"0nv\", \"4UX\", \"74R\", \"6NW\", \"5kU\", \"341\", \"ch\", \"nX\", \"0Mj\", \"42L\", \"6Cg\", \"6mK\", \"4Xh\", \"0cF\", \"2Ve\", \n\"09L\", \"2Hg\", \"67A\", \"4Fj\", \"4hF\", \"7MD\", \"4R\", \"0Sh\", \"1Ny\", \"9b\", \"6PU\", \"4ev\", \"4KZ\", \"7nX\", \"SF\", \"0pt\", \"1oT\", \"1z5\", \"65p\", \"5Tz\", \n\"4jw\", \"5o7\", \"6c\", \"0QY\", \"1LH\", \"2iJ\", \"6Rd\", \"4gG\", \"4Ik\", \"7li\", \"Qw\", \"06M\", \"7F\", \"246\", \"4kR\", \"7NP\", \"64U\", \"col\", \"1nq\", \"0k1\", \n\"PR\", \"E3\", \"4HN\", \"69e\", \"6SA\", \"4fb\", \"1Mm\", \"2ho\", \"5w\", \"0RM\", \"4ic\", \"7La\", \"66d\", \"4GO\", \"J2\", \"2IB\", \"Rc\", \"05Y\", \"b4D\", \"aUN\", \n\"4q2\", \"4dS\", \"8Ve\", \"8G\", \"8Hg\", \"bM\", \"4o0\", \"4zQ\", \"607\", \"75w\", \"La\", \"0oS\", \"T0\", \"AQ\", \"6ln\", \"4YM\", \"43i\", \"6BB\", \"2yl\", \"0LO\", \n\"0CN\", \"22e\", \"6MC\", \"5hA\", \"4VL\", \"6co\", \"NP\", \"0mb\", \"1ps\", \"0u3\", \"aDM\", \"baO\", \"41X\", \"7PR\", \"mL\", \"8Gf\", \"4IT\", \"7lV\", \"QH\", \"06r\", \n\"1Lw\", \"0I7\", \"5b8\", \"4gx\", \"4jH\", \"7OJ\", \"rT\", \"g5\", \"1ok\", \"2Ji\", \"65O\", \"4Dd\", \"4Ke\", \"7ng\", \"Sy\", \"04C\", \"h4\", \"2kD\", \"6Pj\", \"4eI\", \n\"4hy\", \"5m9\", \"4m\", \"0SW\", \"09s\", \"2HX\", \"4S4\", \"4FU\", \"4M6\", \"4XW\", \"0cy\", \"1f9\", \"ng\", \"0MU\", \"42s\", \"573\", \"6Nh\", \"5kj\", \"v6\", \"cW\", \n\"3KK\", \"0nI\", \"4Ug\", \"74m\", \"6oE\", \"4Zf\", \"0aH\", \"Bz\", \"lV\", \"y7\", \"40B\", \"6Ai\", \"582\", \"4yz\", \"0BT\", \"af\", \"OJ\", \"0lx\", \"4WV\", \"4B7\", \n\"64Q\", \"4Ez\", \"1nu\", \"0k5\", \"7B\", \"0Px\", \"4kV\", \"7NT\", \"6SE\", \"4ff\", \"1Mi\", \"2hk\", \"PV\", \"E7\", \"4HJ\", \"69a\", \"6rh\", \"4GK\", \"J6\", \"2IF\", \n\"5s\", \"0RI\", \"4ig\", \"7Le\", \"4q6\", \"4dW\", \"1OX\", \"8C\", \"Rg\", \"0qU\", \"5ZZ\", \"7oy\", \"4Ty\", \"5Q9\", \"Le\", \"0oW\", \"1QZ\", \"bI\", \"4o4\", \"4zU\", \n\"43m\", \"6BF\", \"oy\", \"0LK\", \"T4\", \"AU\", \"6lj\", \"4YI\", \"4VH\", \"6ck\", \"NT\", \"0mf\", \"0CJ\", \"22a\", \"6MG\", \"4xd\", \"4uT\", \"7PV\", \"mH\", \"0Nz\", \n\"1pw\", \"Cd\", \"aDI\", \"5KY\", \"1Ls\", \"0I3\", \"axM\", \"53U\", \"4IP\", \"7lR\", \"QL\", \"06v\", \"1oo\", \"2Jm\", \"65K\", \"5TA\", \"4jL\", \"7ON\", \"6X\", \"g1\", \n\"h0\", \"9Y\", \"6Pn\", \"4eM\", \"4Ka\", \"7nc\", \"2El\", \"04G\", \"09w\", \"d6g\", \"4S0\", \"4FQ\", \"bRN\", \"a3D\", \"4i\", \"0SS\", \"nc\", \"0MQ\", \"42w\", \"577\", \n\"4M2\", \"4XS\", \"17T\", \"dlm\", \"3KO\", \"0nM\", \"4Uc\", \"74i\", \"6Nl\", \"5kn\", \"v2\", \"cS\", \"lR\", \"y3\", \"40F\", \"6Am\", \"6oA\", \"4Zb\", \"0aL\", \"2To\", \n\"ON\", \"18U\", \"4WR\", \"4B3\", \"586\", \"bCM\", \"0BP\", \"ab\", \"PZ\", \"0sh\", \"4HF\", \"69m\", \"6SI\", \"4fj\", \"1Me\", \"2hg\", \"7N\", \"0Pt\", \"4kZ\", \"7NX\", \n\"6pU\", \"4Ev\", \"1ny\", \"0k9\", \"Rk\", \"05Q\", \"4Jw\", \"5O7\", \"452\", \"50r\", \"1OT\", \"8O\", \"qw\", \"0RE\", \"4ik\", \"7Li\", \"66l\", \"4GG\", \"08a\", \"2IJ\", \n\"T8\", \"AY\", \"6lf\", \"4YE\", \"43a\", \"6BJ\", \"ou\", \"0LG\", \"0Aw\", \"bE\", \"4o8\", \"4zY\", \"4Tu\", \"5Q5\", \"Li\", \"8fC\", \"14s\", \"Ch\", \"6nW\", \"5KU\", \n\"41P\", \"7PZ\", \"mD\", \"0Nv\", \"0CF\", \"22m\", \"6MK\", \"4xh\", \"4VD\", \"6cg\", \"NX\", \"0mj\", \"5za\", \"7OB\", \"6T\", \"0Qn\", \"1oc\", \"2Ja\", \"65G\", \"4Dl\", \n\"b7g\", \"68w\", \"1w2\", \"06z\", \"8UF\", \"dSN\", \"5b0\", \"4gp\", \"4hq\", \"5m1\", \"4e\", \"8ZG\", \"1mR\", \"1x3\", \"67v\", \"726\", \"4Km\", \"7no\", \"Sq\", \"04K\", \n\"1NN\", \"9U\", \"6Pb\", \"4eA\", \"adr\", \"5kb\", \"26\", \"21F\", \"Ms\", \"0nA\", \"4Uo\", \"74e\", \"79U\", \"bbl\", \"0cq\", \"1f1\", \"no\", \"396\", \"4vs\", \"5s3\", \n\"6LQ\", \"4yr\", \"367\", \"an\", \"OB\", \"0lp\", \"bmm\", \"76T\", \"6oM\", \"4Zn\", \"15i\", \"Br\", \"2zO\", \"0Ol\", \"40J\", \"6Aa\", \"6SM\", \"4fn\", \"1Ma\", \"2hc\", \n\"2FO\", \"07d\", \"4HB\", \"69i\", \"64Y\", \"4Er\", \"83L\", \"d5D\", \"7J\", \"0Pp\", \"bQm\", \"a0g\", \"456\", \"50v\", \"1OP\", \"8K\", \"Ro\", \"05U\", \"4Js\", \"5O3\", \n\"66h\", \"4GC\", \"08e\", \"2IN\", \"qs\", \"0RA\", \"4io\", \"7Lm\", \"43e\", \"6BN\", \"oq\", \"0LC\", \"0bo\", \"2WL\", \"6lb\", \"4YA\", \"4Tq\", \"5Q1\", \"Lm\", \"8fG\", \n\"0As\", \"bA\", \"ael\", \"cPO\", \"41T\", \"ajm\", \"1K2\", \"0Nr\", \"14w\", \"Cl\", \"6nS\", \"5KQ\", \"5Fa\", \"6cc\", \"2XM\", \"0mn\", \"0CB\", \"22i\", \"6MO\", \"4xl\", \n\"1og\", \"2Je\", \"65C\", \"4Dh\", \"4jD\", \"7OF\", \"6P\", \"g9\", \"3l9\", \"2iy\", \"5b4\", \"4gt\", \"4IX\", \"68s\", \"QD\", \"0rv\", \"1mV\", \"1x7\", \"4S8\", \"4FY\", \n\"4hu\", \"5m5\", \"4a\", \"1Cz\", \"h8\", \"9Q\", \"6Pf\", \"4eE\", \"4Ki\", \"7nk\", \"Su\", \"04O\", \"Mw\", \"0nE\", \"4Uk\", \"74a\", \"6Nd\", \"5kf\", \"22\", \"21B\", \n\"nk\", \"0MY\", \"4vw\", \"5s7\", \"6mx\", \"5Hz\", \"0cu\", \"1f5\", \"OF\", \"0lt\", \"4WZ\", \"6by\", \"6LU\", \"4yv\", \"0BX\", \"aj\", \"lZ\", \"0Oh\", \"40N\", \"6Ae\", \n\"6oI\", \"4Zj\", \"0aD\", \"Bv\", \"5f\", \"9Ke\", \"4ir\", \"5l2\", \"66u\", \"735\", \"08x\", \"1y0\", \"Rr\", \"05H\", \"4Jn\", \"7ol\", \"6Qa\", \"4dB\", \"1OM\", \"8V\", \n\"7W\", \"0Pm\", \"4kC\", \"7NA\", \"64D\", \"4Eo\", \"83Q\", \"2Kb\", \"PC\", \"07y\", \"b6d\", \"69t\", \"5c3\", \"4fs\", \"8TE\", \"dRM\", \"374\", \"22t\", \"599\", \"4xq\", \n\"bln\", \"77W\", \"NA\", \"0ms\", \"14j\", \"Cq\", \"6nN\", \"5KL\", \"41I\", \"7PC\", \"3km\", \"0No\", \"35\", \"20E\", \"6Oc\", \"5ja\", \"4Tl\", \"6aO\", \"Lp\", \"0oB\", \n\"0br\", \"1g2\", \"78V\", \"bco\", \"43x\", \"568\", \"ol\", \"385\", \"4Kt\", \"5N4\", \"Sh\", \"04R\", \"1NW\", \"9L\", \"441\", \"4eX\", \"4hh\", \"7Mj\", \"pt\", \"0SF\", \n\"K9\", \"2HI\", \"67o\", \"4FD\", \"4IE\", \"68n\", \"QY\", \"0\", \"1Lf\", \"2id\", \"6RJ\", \"4gi\", \"4jY\", \"auh\", \"6M\", \"0Qw\", \"1oz\", \"2Jx\", \"5A5\", \"4Du\", \n\"6oT\", \"4Zw\", \"0aY\", \"Bk\", \"lG\", \"0Ou\", \"40S\", \"6Ax\", \"6LH\", \"4yk\", \"0BE\", \"aw\", \"2YJ\", \"0li\", \"4WG\", \"6bd\", \"6me\", \"4XF\", \"0ch\", \"2VK\", \n\"nv\", \"0MD\", \"42b\", \"6CI\", \"6Ny\", \"7K9\", \"1PU\", \"cF\", \"Mj\", \"0nX\", \"4Uv\", \"5P6\", \"66q\", \"4GZ\", \"1lU\", \"1y4\", \"5b\", \"0RX\", \"4iv\", \"5l6\", \n\"6Qe\", \"4dF\", \"1OI\", \"8R\", \"Rv\", \"05L\", \"4Jj\", \"7oh\", \"6pH\", \"4Ek\", \"1nd\", \"2Kf\", \"7S\", \"0Pi\", \"4kG\", \"7NE\", \"5c7\", \"4fw\", \"1Mx\", \"0H8\", \n\"PG\", \"0su\", \"5Xz\", \"69p\", \"4VY\", \"4C8\", \"NE\", \"0mw\", \"1Sz\", \"22p\", \"6MV\", \"4xu\", \"41M\", \"7PG\", \"mY\", \"x8\", \"14n\", \"Cu\", \"6nJ\", \"5KH\", \n\"4Th\", \"6aK\", \"Lt\", \"0oF\", \"31\", \"bX\", \"6Og\", \"4zD\", \"4wt\", \"5r4\", \"oh\", \"0LZ\", \"0bv\", \"AD\", \"4L9\", \"4YX\", \"1NS\", \"9H\", \"445\", \"51u\", \n\"4Kp\", \"5N0\", \"Sl\", \"04V\", \"09f\", \"2HM\", \"67k\", \"5Va\", \"4hl\", \"7Mn\", \"4x\", \"0SB\", \"1Lb\", \"3yA\", \"6RN\", \"4gm\", \"4IA\", \"68j\", \"2GL\", \"4\", \n\"82O\", \"d4G\", \"5A1\", \"4Dq\", \"bPn\", \"a1d\", \"6I\", \"0Qs\", \"lC\", \"0Oq\", \"40W\", \"akn\", \"6oP\", \"4Zs\", \"15t\", \"Bo\", \"2YN\", \"0lm\", \"4WC\", \"76I\", \n\"6LL\", \"4yo\", \"0BA\", \"as\", \"nr\", \"8DX\", \"42f\", \"6CM\", \"6ma\", \"4XB\", \"0cl\", \"2VO\", \"Mn\", \"8gD\", \"4Ur\", \"5P2\", \"ado\", \"bAm\", \"1PQ\", \"cB\", \n\"Rz\", \"0qH\", \"4Jf\", \"7od\", \"6Qi\", \"4dJ\", \"i7\", \"2jG\", \"5n\", \"0RT\", \"4iz\", \"7Lx\", \"4R7\", \"4GV\", \"08p\", \"1y8\", \"PK\", \"07q\", \"4HW\", \"7mU\", \n\"6SX\", \"52R\", \"1Mt\", \"0H4\", \"sW\", \"f6\", \"4kK\", \"7NI\", \"64L\", \"4Eg\", \"1nh\", \"2Kj\", \"14b\", \"Cy\", \"6nF\", \"5KD\", \"41A\", \"7PK\", \"mU\", \"x4\", \n\"0CW\", \"0V6\", \"591\", \"4xy\", \"4VU\", \"4C4\", \"NI\", \"19R\", \"0bz\", \"AH\", \"4L5\", \"4YT\", \"43p\", \"560\", \"od\", \"0LV\", \"w5\", \"bT\", \"6Ok\", \"4zH\", \n\"4Td\", \"6aG\", \"Lx\", \"0oJ\", \"5xA\", \"7Mb\", \"4t\", \"0SN\", \"K1\", \"2HA\", \"67g\", \"4FL\", \"b5G\", \"aTM\", \"0e3\", \"04Z\", \"8Wf\", \"9D\", \"449\", \"4eP\", \n\"4jQ\", \"7OS\", \"6E\", \"255\", \"1or\", \"0j2\", \"65V\", \"cno\", \"4IM\", \"68f\", \"QQ\", \"8\", \"1Ln\", \"2il\", \"6RB\", \"4ga\", \"afR\", \"4yc\", \"0BM\", \"23f\", \n\"OS\", \"Z2\", \"4WO\", \"6bl\", \"aEN\", \"c4e\", \"0aQ\", \"Bc\", \"lO\", \"8Fe\", \"4tS\", \"4a2\", \"4n3\", \"5ks\", \"8Id\", \"cN\", \"Mb\", \"0nP\", \"614\", \"74t\", \n\"6mm\", \"4XN\", \"U3\", \"2VC\", \"2xo\", \"0ML\", \"42j\", \"6CA\", \"6Qm\", \"4dN\", \"i3\", \"8Z\", \"2Do\", \"05D\", \"4Jb\", \"aUS\", \"4R3\", \"4GR\", \"08t\", \"d7d\", \n\"5j\", \"0RP\", \"bSM\", \"a2G\", \"ayN\", \"52V\", \"1Mp\", \"0H0\", \"PO\", \"07u\", \"4HS\", \"69x\", \"64H\", \"4Ec\", \"1nl\", \"2Kn\", \"sS\", \"f2\", \"4kO\", \"7NM\", \n\"41E\", \"7PO\", \"mQ\", \"x0\", \"14f\", \"2Ul\", \"6nB\", \"aQ1\", \"4VQ\", \"4C0\", \"NM\", \"19V\", \"0CS\", \"0V2\", \"595\", \"bBN\", \"43t\", \"564\", \"0Y3\", \"0LR\", \n\"16W\", \"AL\", \"4L1\", \"4YP\", \"5DA\", \"6aC\", \"2Zm\", \"0oN\", \"39\", \"bP\", \"6Oo\", \"4zL\", \"K5\", \"2HE\", \"67c\", \"4FH\", \"4hd\", \"7Mf\", \"4p\", \"0SJ\", \n\"8Wb\", \"2kY\", \"4p5\", \"4eT\", \"4Kx\", \"5N8\", \"Sd\", \"0pV\", \"1ov\", \"0j6\", \"5A9\", \"4Dy\", \"4jU\", \"7OW\", \"6A\", \"1AZ\", \"1Lj\", \"2ih\", \"6RF\", \"4ge\", \n\"4II\", \"68b\", \"QU\", \"D4\", \"OW\", \"Z6\", \"4WK\", \"6bh\", \"6LD\", \"4yg\", \"0BI\", \"23b\", \"lK\", \"0Oy\", \"4tW\", \"4a6\", \"6oX\", \"5JZ\", \"0aU\", \"Bg\", \n\"Mf\", \"0nT\", \"4Uz\", \"74p\", \"4n7\", \"5kw\", \"1PY\", \"cJ\", \"nz\", \"0MH\", \"42n\", \"6CE\", \"6mi\", \"4XJ\", \"U7\", \"2VG\", \"4MP\", \"4X1\", \"UL\", \"02v\", \n\"0XR\", \"0M3\", \"a8E\", \"57U\", \"4nL\", \"7KN\", \"2X\", \"c1\", \"1ko\", \"2Nm\", \"61K\", \"5PA\", \"4Oa\", \"6zB\", \"2Al\", \"00G\", \"l0\", \"yQ\", \"6Tn\", \"4aM\", \n\"58T\", \"a7D\", \"0i\", \"0WS\", \"84o\", \"ZM\", \"4W0\", \"4BQ\", \"4I2\", \"5Lr\", \"13T\", \"G\", \"jc\", \"0IQ\", \"46w\", \"537\", \"6Jl\", \"5on\", \"r2\", \"gS\", \n\"3OO\", \"0jM\", \"4Qc\", \"70i\", \"6kA\", \"5NC\", \"0eL\", \"2Po\", \"hR\", \"8Bx\", \"44F\", \"6Em\", \"abO\", \"49v\", \"0FP\", \"eb\", \"KN\", \"8ad\", \"4SR\", \"4F3\", \n\"3B\", \"0Tx\", \"4oV\", \"4z7\", \"60Q\", \"4Az\", \"0zT\", \"Yf\", \"TV\", \"A7\", \"4LJ\", \"6yi\", \"6WE\", \"4bf\", \"0YH\", \"zz\", \"1s\", \"0VI\", \"4mg\", \"6XD\", \n\"6vh\", \"4CK\", \"N6\", \"2MF\", \"Vg\", \"0uU\", \"6n9\", \"7ky\", \"4u6\", \"5pv\", \"1KX\", \"xK\", \"1UZ\", \"fI\", \"4k4\", \"5nt\", \"4Py\", \"5U9\", \"He\", \"0kW\", \n\"P4\", \"EU\", \"6hj\", \"5Mh\", \"47m\", \"6FF\", \"ky\", \"0HK\", \"0GJ\", \"dx\", \"6IG\", \"48l\", \"4RH\", \"6gk\", \"JT\", \"0if\", \"0dV\", \"Gd\", \"5Z8\", \"5OY\", \n\"4qT\", \"4d5\", \"iH\", \"0Jz\", \"0XV\", \"0M7\", \"5f8\", \"4cx\", \"4MT\", \"4X5\", \"UH\", \"02r\", \"1kk\", \"Xx\", \"61O\", \"5PE\", \"4nH\", \"7KJ\", \"vT\", \"c5\", \n\"l4\", \"yU\", \"6Tj\", \"4aI\", \"4Oe\", \"6zF\", \"Wy\", \"00C\", \"1iZ\", \"ZI\", \"4W4\", \"4BU\", \"4ly\", \"5i9\", \"0m\", \"0WW\", \"jg\", \"0IU\", \"46s\", \"533\", \n\"4I6\", \"5Lv\", \"0gy\", \"C\", \"3OK\", \"0jI\", \"4Qg\", \"6dD\", \"6Jh\", \"5oj\", \"r6\", \"gW\", \"hV\", \"0Kd\", \"44B\", \"6Ei\", \"6kE\", \"5NG\", \"0eH\", \"Fz\", \n\"KJ\", \"0hx\", \"4SV\", \"4F7\", \"6HY\", \"49r\", \"0FT\", \"ef\", \"60U\", \"ckl\", \"0zP\", \"Yb\", \"3F\", \"206\", \"4oR\", \"4z3\", \"6WA\", \"4bb\", \"0YL\", \"2lo\", \n\"TR\", \"A3\", \"4LN\", \"6ym\", \"62d\", \"4CO\", \"N2\", \"2MB\", \"1w\", \"0VM\", \"4mc\", \"7Ha\", \"4u2\", \"54z\", \"8Re\", \"xO\", \"Vc\", \"01Y\", \"b0D\", \"aQN\", \n\"647\", \"71w\", \"Ha\", \"0kS\", \"8Lg\", \"fM\", \"4k0\", \"5np\", \"47i\", \"6FB\", \"29d\", \"0HO\", \"P0\", \"EQ\", \"6hn\", \"5Ml\", \"4RL\", \"6go\", \"JP\", \"0ib\", \n\"0GN\", \"26e\", \"6IC\", \"48h\", \"45X\", \"4d1\", \"iL\", \"8Cf\", \"0dR\", \"0q3\", \"hYt\", \"beO\", \"4nD\", \"7KF\", \"2P\", \"c9\", \"1kg\", \"Xt\", \"61C\", \"5PI\", \n\"4MX\", \"4X9\", \"UD\", \"0vv\", \"0XZ\", \"2my\", \"5f4\", \"4ct\", \"4lu\", \"5i5\", \"0a\", \"1Gz\", \"0yw\", \"ZE\", \"4W8\", \"4BY\", \"4Oi\", \"6zJ\", \"Wu\", \"00O\", \n\"l8\", \"yY\", \"6Tf\", \"4aE\", \"6Jd\", \"5of\", \"62\", \"25B\", \"Iw\", \"0jE\", \"4Qk\", \"6dH\", \"6ix\", \"5Lz\", \"0gu\", \"O\", \"jk\", \"0IY\", \"4rw\", \"5w7\", \n\"5x6\", \"5mW\", \"0FX\", \"ej\", \"KF\", \"0ht\", \"4SZ\", \"6fy\", \"6kI\", \"5NK\", \"0eD\", \"Fv\", \"hZ\", \"93\", \"44N\", \"6Ee\", \"2BO\", \"03d\", \"4LB\", \"6ya\", \n\"6WM\", \"4bn\", \"1Ia\", \"zr\", \"3J\", \"0Tp\", \"bUm\", \"a4g\", \"5D2\", \"4Ar\", \"87L\", \"Yn\", \"Vo\", \"01U\", \"4Ns\", \"5K3\", \"416\", \"54v\", \"1KP\", \"xC\", \n\"us\", \"0VA\", \"4mo\", \"6XL\", \"62h\", \"4CC\", \"0xm\", \"2MN\", \"0fo\", \"2SL\", \"6hb\", \"bgr\", \"47e\", \"6FN\", \"kq\", \"0HC\", \"0Es\", \"fA\", \"aal\", \"bDn\", \n\"4Pq\", \"5U1\", \"Hm\", \"8bG\", \"10w\", \"Gl\", \"5Z0\", \"5OQ\", \"45T\", \"anm\", \"1O2\", \"0Jr\", \"0GB\", \"dp\", \"6IO\", \"48d\", \"5Ba\", \"6gc\", \"3Ll\", \"0in\", \n\"1kc\", \"Xp\", \"61G\", \"5PM\", \"bTs\", \"7KB\", \"2T\", \"0Un\", \"8QF\", \"39T\", \"5f0\", \"4cp\", \"797\", \"aRm\", \"1s2\", \"02z\", \"0ys\", \"ZA\", \"63v\", \"766\", \n\"4lq\", \"5i1\", \"0e\", \"9Nf\", \"0Zo\", \"2oL\", \"6Tb\", \"4aA\", \"4Om\", \"6zN\", \"Wq\", \"00K\", \"Is\", \"0jA\", \"4Qo\", \"6dL\", \"7ZA\", \"5ob\", \"66\", \"25F\", \n\"jo\", \"9Pd\", \"4rs\", \"5w3\", \"aCn\", \"bfl\", \"0gq\", \"K\", \"KB\", \"0hp\", \"bim\", \"72T\", \"5x2\", \"49z\", \"327\", \"en\", \"3nn\", \"97\", \"44J\", \"6Ea\", \n\"6kM\", \"5NO\", \"11i\", \"Fr\", \"6WI\", \"4bj\", \"0YD\", \"zv\", \"TZ\", \"0wh\", \"4LF\", \"6ye\", \"5D6\", \"4Av\", \"0zX\", \"Yj\", \"3N\", \"0Tt\", \"4oZ\", \"6Zy\", \n\"412\", \"54r\", \"1KT\", \"xG\", \"Vk\", \"01Q\", \"4Nw\", \"5K7\", \"62l\", \"4CG\", \"0xi\", \"2MJ\", \"uw\", \"0VE\", \"4mk\", \"6XH\", \"47a\", \"6FJ\", \"ku\", \"0HG\", \n\"P8\", \"EY\", \"6hf\", \"5Md\", \"4Pu\", \"5U5\", \"Hi\", \"8bC\", \"0Ew\", \"fE\", \"4k8\", \"5nx\", \"45P\", \"4d9\", \"iD\", \"0Jv\", \"0dZ\", \"Gh\", \"5Z4\", \"5OU\", \n\"4RD\", \"6gg\", \"JX\", \"0ij\", \"0GF\", \"dt\", \"6IK\", \"5lI\", \"4Op\", \"5J0\", \"Wl\", \"00V\", \"0Zr\", \"2oQ\", \"405\", \"55u\", \"4ll\", \"6YO\", \"0x\", \"0WB\", \n\"0yn\", \"2LM\", \"63k\", \"5Ra\", \"4MA\", \"6xb\", \"2CL\", \"02g\", \"0XC\", \"39I\", \"6VN\", \"4cm\", \"bTn\", \"a5d\", \"2I\", \"0Us\", \"86O\", \"Xm\", \"5E1\", \"5PP\", \n\"6kP\", \"5NR\", \"11t\", \"Fo\", \"hC\", \"0Kq\", \"44W\", \"aon\", \"6HL\", \"49g\", \"0FA\", \"es\", \"3Mo\", \"0hm\", \"4SC\", \"72I\", \"6ia\", \"5Lc\", \"0gl\", \"V\", \n\"jr\", \"1Ya\", \"46f\", \"6GM\", \"hyV\", \"bEm\", \"0Dp\", \"gB\", \"In\", \"8cD\", \"4Qr\", \"5T2\", \"1b\", \"0VX\", \"4mv\", \"5h6\", \"62q\", \"4CZ\", \"0xt\", \"2MW\", \n\"Vv\", \"01L\", \"4Nj\", \"7kh\", \"6Ue\", \"54o\", \"1KI\", \"xZ\", \"3S\", \"0Ti\", \"4oG\", \"6Zd\", \"6tH\", \"4Ak\", \"0zE\", \"Yw\", \"TG\", \"0wu\", \"780\", \"6yx\", \n\"5g7\", \"4bw\", \"0YY\", \"zk\", \"1Wz\", \"di\", \"5y5\", \"5lT\", \"4RY\", \"4G8\", \"JE\", \"0iw\", \"0dG\", \"Gu\", \"6jJ\", \"5OH\", \"45M\", \"6Df\", \"iY\", \"80\", \n\"71\", \"fX\", \"6Kg\", \"5ne\", \"4Ph\", \"6eK\", \"Ht\", \"0kF\", \"0fv\", \"ED\", \"4H9\", \"5My\", \"4st\", \"5v4\", \"kh\", \"0HZ\", \"0Zv\", \"yD\", \"401\", \"4aX\", \n\"4Ot\", \"5J4\", \"Wh\", \"00R\", \"O9\", \"ZX\", \"63o\", \"4BD\", \"4lh\", \"6YK\", \"tt\", \"0WF\", \"0XG\", \"2md\", \"6VJ\", \"4ci\", \"4ME\", \"6xf\", \"UY\", \"02c\", \n\"1kz\", \"Xi\", \"5E5\", \"5PT\", \"4nY\", \"aqh\", \"2M\", \"0Uw\", \"hG\", \"0Ku\", \"44S\", \"6Ex\", \"6kT\", \"5NV\", \"0eY\", \"Fk\", \"3Mk\", \"0hi\", \"4SG\", \"6fd\", \n\"6HH\", \"49c\", \"0FE\", \"ew\", \"jv\", \"0ID\", \"46b\", \"6GI\", \"6ie\", \"5Lg\", \"0gh\", \"R\", \"Ij\", \"0jX\", \"4Qv\", \"5T6\", \"6Jy\", \"7O9\", \"0Dt\", \"gF\", \n\"62u\", \"775\", \"0xp\", \"198\", \"1f\", \"9Oe\", \"4mr\", \"5h2\", \"6Ua\", \"54k\", \"1KM\", \"2nO\", \"Vr\", \"01H\", \"4Nn\", \"7kl\", \"60D\", \"4Ao\", \"0zA\", \"Ys\", \n\"3W\", \"0Tm\", \"4oC\", \"7JA\", \"5g3\", \"4bs\", \"8PE\", \"zo\", \"TC\", \"03y\", \"784\", \"aSn\", \"bhn\", \"73W\", \"JA\", \"0is\", \"334\", \"dm\", \"5y1\", \"48y\", \n\"45I\", \"6Db\", \"3om\", \"84\", \"0dC\", \"Gq\", \"6jN\", \"5OL\", \"4Pl\", \"6eO\", \"Hp\", \"0kB\", \"75\", \"24E\", \"6Kc\", \"5na\", \"47x\", \"528\", \"kl\", \"8AF\", \n\"0fr\", \"1c2\", \"aBm\", \"bgo\", \"4ld\", \"6YG\", \"0p\", \"0WJ\", \"O5\", \"ZT\", \"63c\", \"4BH\", \"4Ox\", \"5J8\", \"Wd\", \"0tV\", \"0Zz\", \"yH\", \"4t5\", \"4aT\", \n\"4nU\", \"7KW\", \"2A\", \"1EZ\", \"1kv\", \"Xe\", \"5E9\", \"5PX\", \"4MI\", \"6xj\", \"UU\", \"02o\", \"0XK\", \"2mh\", \"6VF\", \"4ce\", \"6HD\", \"49o\", \"0FI\", \"27b\", \n\"KW\", \"0he\", \"4SK\", \"6fh\", \"6kX\", \"5NZ\", \"0eU\", \"Fg\", \"hK\", \"0Ky\", \"4pW\", \"4e6\", \"4j7\", \"5ow\", \"0Dx\", \"gJ\", \"If\", \"0jT\", \"4Qz\", \"6dY\", \n\"6ii\", \"5Lk\", \"Q7\", \"DV\", \"jz\", \"0IH\", \"46n\", \"6GE\", \"3PN\", \"01D\", \"4Nb\", \"aQS\", \"6Um\", \"54g\", \"m3\", \"xR\", \"1j\", \"0VP\", \"59W\", \"a6G\", \n\"4V3\", \"4CR\", \"85l\", \"194\", \"TO\", \"03u\", \"4LS\", \"4Y2\", \"a9F\", \"56V\", \"0YQ\", \"zc\", \"wS\", \"b2\", \"4oO\", \"6Zl\", \"60H\", \"4Ac\", \"0zM\", \"2On\", \n\"0dO\", \"2Ql\", \"6jB\", \"aU1\", \"45E\", \"6Dn\", \"iQ\", \"88\", \"0GS\", \"da\", \"acL\", \"48u\", \"4RQ\", \"4G0\", \"JM\", \"94N\", \"12W\", \"EL\", \"4H1\", \"5Mq\", \n\"47t\", \"524\", \"29y\", \"0HR\", \"79\", \"fP\", \"6Ko\", \"5nm\", \"aZ0\", \"6eC\", \"3NL\", \"0kN\", \"O1\", \"ZP\", \"63g\", \"4BL\", \"58I\", \"6YC\", \"0t\", \"0WN\", \n\"8Sf\", \"yL\", \"409\", \"4aP\", \"b1G\", \"aPM\", \"0a3\", \"00Z\", \"1kr\", \"Xa\", \"61V\", \"bzN\", \"4nQ\", \"7KS\", \"2E\", \"215\", \"0XO\", \"2ml\", \"6VB\", \"4ca\", \n\"4MM\", \"6xn\", \"UQ\", \"02k\", \"KS\", \"0ha\", \"4SO\", \"6fl\", \"7Xa\", \"49k\", \"0FM\", \"27f\", \"hO\", \"8Be\", \"4pS\", \"4e2\", \"aAN\", \"bdL\", \"0eQ\", \"Fc\", \n\"Ib\", \"0jP\", \"654\", \"70t\", \"4j3\", \"5os\", \"8Md\", \"gN\", \"28g\", \"0IL\", \"46j\", \"6GA\", \"6im\", \"5Lo\", \"Q3\", \"Z\", \"6Ui\", \"54c\", \"m7\", \"xV\", \n\"Vz\", \"0uH\", \"4Nf\", \"7kd\", \"4V7\", \"4CV\", \"0xx\", \"190\", \"1n\", \"0VT\", \"4mz\", \"6XY\", \"6WX\", \"56R\", \"0YU\", \"zg\", \"TK\", \"03q\", \"4LW\", \"4Y6\", \n\"60L\", \"4Ag\", \"0zI\", \"2Oj\", \"wW\", \"b6\", \"4oK\", \"6Zh\", \"45A\", \"6Dj\", \"iU\", \"0Jg\", \"0dK\", \"Gy\", \"6jF\", \"5OD\", \"4RU\", \"4G4\", \"JI\", \"1yZ\", \n\"0GW\", \"de\", \"5y9\", \"48q\", \"47p\", \"520\", \"kd\", \"0HV\", \"0fz\", \"EH\", \"4H5\", \"5Mu\", \"4Pd\", \"6eG\", \"Hx\", \"0kJ\", \"s5\", \"fT\", \"6Kk\", \"5ni\", \n\"5Y1\", \"5LP\", \"13v\", \"e\", \"jA\", \"0Is\", \"46U\", \"aml\", \"6JN\", \"5oL\", \"0DC\", \"gq\", \"3Om\", \"0jo\", \"4QA\", \"6db\", \"6kc\", \"5Na\", \"0en\", \"2PM\", \n\"hp\", \"0KB\", \"44d\", \"6EO\", \"abm\", \"49T\", \"0Fr\", \"1C2\", \"Kl\", \"8aF\", \"4Sp\", \"5V0\", \"4Mr\", \"5H2\", \"Un\", \"02T\", \"0Xp\", \"2mS\", \"427\", \"57w\", \n\"4nn\", \"7Kl\", \"2z\", \"1Ea\", \"1kM\", \"2NO\", \"61i\", \"5Pc\", \"4OC\", \"7jA\", \"2AN\", \"00e\", \"0ZA\", \"ys\", \"6TL\", \"4ao\", \"58v\", \"a7f\", \"0K\", \"0Wq\", \n\"84M\", \"Zo\", \"5G3\", \"4Bs\", \"0EY\", \"fk\", \"6KT\", \"5nV\", \"bjh\", \"6ex\", \"HG\", \"0ku\", \"0fE\", \"Ew\", \"6hH\", \"5MJ\", \"47O\", \"6Fd\", \"29B\", \"0Hi\", \n\"53\", \"dZ\", \"6Ie\", \"48N\", \"4Rj\", \"6gI\", \"Jv\", \"0iD\", \"0dt\", \"GF\", \"6jy\", \"7o9\", \"4qv\", \"5t6\", \"ij\", \"0JX\", \"wh\", \"0TZ\", \"4ot\", \"5j4\", \n\"4T9\", \"4AX\", \"0zv\", \"YD\", \"Tt\", \"03N\", \"4Lh\", \"6yK\", \"6Wg\", \"4bD\", \"o9\", \"zX\", \"1Q\", \"0Vk\", \"4mE\", \"6Xf\", \"62B\", \"4Ci\", \"0xG\", \"2Md\", \n\"VE\", \"0uw\", \"4NY\", \"aQh\", \"5e5\", \"5pT\", \"1Kz\", \"xi\", \"jE\", \"0Iw\", \"46Q\", \"4g8\", \"5Y5\", \"5LT\", \"13r\", \"a\", \"IY\", \"0jk\", \"4QE\", \"6df\", \n\"6JJ\", \"5oH\", \"0DG\", \"gu\", \"ht\", \"0KF\", \"4ph\", \"6EK\", \"6kg\", \"5Ne\", \"S9\", \"FX\", \"Kh\", \"0hZ\", \"4St\", \"5V4\", \"4h9\", \"49P\", \"0Fv\", \"eD\", \n\"0Xt\", \"2mW\", \"423\", \"4cZ\", \"4Mv\", \"5H6\", \"Uj\", \"02P\", \"1kI\", \"XZ\", \"61m\", \"5Pg\", \"4nj\", \"7Kh\", \"vv\", \"0UD\", \"0ZE\", \"yw\", \"6TH\", \"4ak\", \n\"4OG\", \"6zd\", \"2AJ\", \"00a\", \"0yY\", \"Zk\", \"5G7\", \"4Bw\", \"58r\", \"6Yx\", \"0O\", \"0Wu\", \"bjl\", \"71U\", \"HC\", \"0kq\", \"316\", \"fo\", \"6KP\", \"5nR\", \n\"47K\", \"7VA\", \"29F\", \"0Hm\", \"0fA\", \"Es\", \"6hL\", \"5MN\", \"4Rn\", \"6gM\", \"Jr\", \"1ya\", \"57\", \"26G\", \"6Ia\", \"48J\", \"45z\", \"5t2\", \"in\", \"8CD\", \n\"0dp\", \"GB\", \"hYV\", \"bem\", \"60w\", \"757\", \"0zr\", \"2OQ\", \"3d\", \"9Mg\", \"4op\", \"5j0\", \"6Wc\", \"56i\", \"0Yn\", \"2lM\", \"Tp\", \"03J\", \"4Ll\", \"6yO\", \n\"62F\", \"4Cm\", \"0xC\", \"dwS\", \"1U\", \"0Vo\", \"4mA\", \"6Xb\", \"5e1\", \"54X\", \"8RG\", \"xm\", \"VA\", \"0us\", \"b0f\", \"aQl\", \"6JF\", \"5oD\", \"0DK\", \"gy\", \n\"IU\", \"0jg\", \"4QI\", \"6dj\", \"5Y9\", \"5LX\", \"0gW\", \"m\", \"jI\", \"1YZ\", \"4rU\", \"4g4\", \"4h5\", \"5mu\", \"0Fz\", \"eH\", \"Kd\", \"0hV\", \"4Sx\", \"5V8\", \n\"6kk\", \"5Ni\", \"S5\", \"FT\", \"hx\", \"0KJ\", \"44l\", \"6EG\", \"4nf\", \"7Kd\", \"2r\", \"0UH\", \"M7\", \"XV\", \"61a\", \"5Pk\", \"4Mz\", \"6xY\", \"Uf\", \"0vT\", \n\"0Xx\", \"39r\", \"4v7\", \"4cV\", \"4lW\", \"4y6\", \"0C\", \"0Wy\", \"0yU\", \"Zg\", \"63P\", \"5RZ\", \"4OK\", \"6zh\", \"WW\", \"B6\", \"0ZI\", \"2oj\", \"6TD\", \"4ag\", \n\"0fM\", \"2Sn\", \"7xa\", \"5MB\", \"47G\", \"6Fl\", \"kS\", \"0Ha\", \"0EQ\", \"fc\", \"aaN\", \"bDL\", \"4PS\", \"4E2\", \"HO\", \"8be\", \"10U\", \"GN\", \"4J3\", \"5Os\", \n\"45v\", \"506\", \"ib\", \"0JP\", \"q3\", \"dR\", \"6Im\", \"48F\", \"4Rb\", \"6gA\", \"3LN\", \"0iL\", \"2Bm\", \"03F\", \"aF0\", \"6yC\", \"6Wo\", \"4bL\", \"o1\", \"zP\", \n\"3h\", \"0TR\", \"bUO\", \"a4E\", \"4T1\", \"4AP\", \"87n\", \"YL\", \"VM\", \"01w\", \"4NQ\", \"7kS\", \"hfu\", \"54T\", \"1Kr\", \"xa\", \"1Y\", \"0Vc\", \"4mM\", \"6Xn\", \n\"62J\", \"4Ca\", \"0xO\", \"2Ml\", \"IQ\", \"0jc\", \"4QM\", \"6dn\", \"6JB\", \"a19\", \"0DO\", \"25d\", \"jM\", \"9PF\", \"46Y\", \"4g0\", \"aCL\", \"687\", \"0gS\", \"i\", \n\"3MP\", \"0hR\", \"676\", \"72v\", \"4h1\", \"49X\", \"8Of\", \"eL\", \"3nL\", \"0KN\", \"44h\", \"6EC\", \"6ko\", \"5Nm\", \"S1\", \"FP\", \"M3\", \"XR\", \"61e\", \"5Po\", \n\"4nb\", \"aqS\", \"2v\", \"0UL\", \"8Qd\", \"39v\", \"4v3\", \"4cR\", \"b3E\", \"aRO\", \"Ub\", \"02X\", \"0yQ\", \"Zc\", \"63T\", \"bxL\", \"4lS\", \"4y2\", \"0G\", \"237\", \n\"0ZM\", \"2on\", \"7Da\", \"4ac\", \"4OO\", \"6zl\", \"WS\", \"B2\", \"47C\", \"6Fh\", \"kW\", \"0He\", \"0fI\", \"2Sj\", \"6hD\", \"5MF\", \"4PW\", \"4E6\", \"HK\", \"0ky\", \n\"0EU\", \"fg\", \"6KX\", \"5nZ\", \"45r\", \"502\", \"if\", \"0JT\", \"0dx\", \"GJ\", \"4J7\", \"5Ow\", \"4Rf\", \"6gE\", \"Jz\", \"0iH\", \"q7\", \"dV\", \"6Ii\", \"48B\", \n\"6Wk\", \"4bH\", \"o5\", \"zT\", \"Tx\", \"03B\", \"4Ld\", \"6yG\", \"4T5\", \"4AT\", \"0zz\", \"YH\", \"3l\", \"0TV\", \"4ox\", \"5j8\", \"5e9\", \"54P\", \"1Kv\", \"xe\", \n\"VI\", \"01s\", \"4NU\", \"7kW\", \"62N\", \"4Ce\", \"0xK\", \"2Mh\", \"uU\", \"0Vg\", \"4mI\", \"6Xj\", \"4K0\", \"5Np\", \"11V\", \"FM\", \"ha\", \"0KS\", \"44u\", \"515\", \n\"6Hn\", \"49E\", \"48\", \"eQ\", \"3MM\", \"0hO\", \"4Sa\", \"6fB\", \"6iC\", \"5LA\", \"0gN\", \"t\", \"jP\", \"0Ib\", \"46D\", \"6Go\", \"hyt\", \"bEO\", \"0DR\", \"0Q3\", \n\"IL\", \"8cf\", \"4QP\", \"4D1\", \"4OR\", \"4Z3\", \"WN\", \"00t\", \"0ZP\", \"yb\", \"hgv\", \"55W\", \"4lN\", \"6Ym\", \"0Z\", \"a3\", \"0yL\", \"2Lo\", \"63I\", \"4Bb\", \n\"4Mc\", \"7ha\", \"2Cn\", \"02E\", \"n2\", \"2mB\", \"6Vl\", \"4cO\", \"bTL\", \"a5F\", \"2k\", \"0UQ\", \"86m\", \"XO\", \"4U2\", \"5Pr\", \"0Gy\", \"dK\", \"4i6\", \"5lv\", \n\"5BZ\", \"6gX\", \"Jg\", \"0iU\", \"R6\", \"GW\", \"6jh\", \"5Oj\", \"45o\", \"6DD\", \"3oK\", \"0JI\", \"0EH\", \"fz\", \"6KE\", \"5nG\", \"4PJ\", \"6ei\", \"HV\", \"0kd\", \n\"0fT\", \"Ef\", \"6hY\", \"690\", \"4sV\", \"4f7\", \"kJ\", \"0Hx\", \"uH\", \"0Vz\", \"4mT\", \"4x5\", \"5F8\", \"4Cx\", \"0xV\", \"0m7\", \"VT\", \"C5\", \"4NH\", \"7kJ\", \n\"6UG\", \"54M\", \"1Kk\", \"xx\", \"3q\", \"0TK\", \"4oe\", \"6ZF\", \"60b\", \"4AI\", \"L4\", \"YU\", \"Te\", \"0wW\", \"4Ly\", \"5I9\", \"4w4\", \"4bU\", \"1IZ\", \"zI\", \n\"he\", \"0KW\", \"44q\", \"511\", \"4K4\", \"5Nt\", \"11R\", \"FI\", \"Ky\", \"0hK\", \"4Se\", \"6fF\", \"6Hj\", \"49A\", \"p4\", \"eU\", \"jT\", \"0If\", \"4rH\", \"6Gk\", \n\"6iG\", \"5LE\", \"0gJ\", \"p\", \"IH\", \"0jz\", \"4QT\", \"4D5\", \"5z8\", \"5oY\", \"0DV\", \"gd\", \"0ZT\", \"yf\", \"6TY\", \"4az\", \"4OV\", \"4Z7\", \"WJ\", \"00p\", \n\"0yH\", \"Zz\", \"63M\", \"4Bf\", \"4lJ\", \"6Yi\", \"tV\", \"a7\", \"n6\", \"2mF\", \"6Vh\", \"4cK\", \"4Mg\", \"6xD\", \"2Cj\", \"02A\", \"1kX\", \"XK\", \"4U6\", \"5Pv\", \n\"6N9\", \"7Ky\", \"2o\", \"0UU\", \"665\", \"73u\", \"Jc\", \"0iQ\", \"8Ne\", \"dO\", \"4i2\", \"5lr\", \"45k\", \"7Ta\", \"3oO\", \"0JM\", \"R2\", \"GS\", \"6jl\", \"5On\", \n\"4PN\", \"6em\", \"HR\", \"8bx\", \"0EL\", \"24g\", \"6KA\", \"5nC\", \"47Z\", \"4f3\", \"kN\", \"8Ad\", \"0fP\", \"Eb\", \"aBO\", \"694\", \"62W\", \"byO\", \"0xR\", \"0m3\", \n\"1D\", \"224\", \"4mP\", \"4x1\", \"6UC\", \"54I\", \"1Ko\", \"2nm\", \"VP\", \"C1\", \"4NL\", \"7kN\", \"60f\", \"4AM\", \"L0\", \"YQ\", \"3u\", \"0TO\", \"4oa\", \"6ZB\", \n\"438\", \"4bQ\", \"8Pg\", \"zM\", \"Ta\", \"0wS\", \"b2F\", \"aSL\", \"6Hf\", \"49M\", \"40\", \"eY\", \"Ku\", \"0hG\", \"4Si\", \"6fJ\", \"4K8\", \"5Nx\", \"0ew\", \"FE\", \n\"hi\", \"8BC\", \"4pu\", \"5u5\", \"5z4\", \"5oU\", \"0DZ\", \"gh\", \"ID\", \"0jv\", \"4QX\", \"4D9\", \"6iK\", \"5LI\", \"0gF\", \"Dt\", \"jX\", \"0Ij\", \"46L\", \"6Gg\", \n\"4lF\", \"6Ye\", \"0R\", \"0Wh\", \"0yD\", \"Zv\", \"63A\", \"4Bj\", \"4OZ\", \"6zy\", \"WF\", \"0tt\", \"0ZX\", \"yj\", \"5d6\", \"4av\", \"4nw\", \"5k7\", \"2c\", \"0UY\", \n\"1kT\", \"XG\", \"61p\", \"5Pz\", \"4Mk\", \"6xH\", \"Uw\", \"02M\", \"0Xi\", \"2mJ\", \"6Vd\", \"4cG\", \"0dm\", \"2QN\", \"7zA\", \"5Ob\", \"45g\", \"6DL\", \"is\", \"0JA\", \n\"0Gq\", \"dC\", \"acn\", \"48W\", \"4Rs\", \"5W3\", \"Jo\", \"94l\", \"12u\", \"En\", \"5X2\", \"5MS\", \"47V\", \"alo\", \"kB\", \"0Hp\", \"1Ua\", \"fr\", \"6KM\", \"5nO\", \n\"4PB\", \"6ea\", \"3Nn\", \"0kl\", \"3Pl\", \"01f\", \"bts\", \"7kB\", \"6UO\", \"54E\", \"1Kc\", \"xp\", \"1H\", \"0Vr\", \"59u\", \"a6e\", \"5F0\", \"4Cp\", \"85N\", \"d3F\", \n\"Tm\", \"03W\", \"4Lq\", \"5I1\", \"434\", \"56t\", \"0Ys\", \"zA\", \"3y\", \"0TC\", \"4om\", \"6ZN\", \"60j\", \"4AA\", \"0zo\", \"2OL\", \"Kq\", \"0hC\", \"4Sm\", \"6fN\", \n\"6Hb\", \"49I\", \"44\", \"27D\", \"hm\", \"8BG\", \"44y\", \"519\", \"aAl\", \"bdn\", \"0es\", \"FA\", \"1o2\", \"0jr\", \"bko\", \"70V\", \"5z0\", \"5oQ\", \"305\", \"gl\", \n\"28E\", \"0In\", \"46H\", \"6Gc\", \"6iO\", \"5LM\", \"0gB\", \"x\", \"1ia\", \"Zr\", \"63E\", \"4Bn\", \"4lB\", \"6Ya\", \"0V\", \"0Wl\", \"8SD\", \"yn\", \"5d2\", \"4ar\", \n\"b1e\", \"aPo\", \"WB\", \"00x\", \"1kP\", \"XC\", \"61t\", \"744\", \"4ns\", \"5k3\", \"2g\", \"9Ld\", \"0Xm\", \"2mN\", \"7FA\", \"4cC\", \"4Mo\", \"6xL\", \"Us\", \"02I\", \n\"45c\", \"6DH\", \"iw\", \"0JE\", \"0di\", \"2QJ\", \"6jd\", \"5Of\", \"4Rw\", \"5W7\", \"Jk\", \"0iY\", \"0Gu\", \"dG\", \"6Ix\", \"48S\", \"47R\", \"6Fy\", \"kF\", \"0Ht\", \n\"0fX\", \"Ej\", \"5X6\", \"5MW\", \"4PF\", \"6ee\", \"HZ\", \"0kh\", \"0ED\", \"fv\", \"6KI\", \"5nK\", \"6UK\", \"54A\", \"1Kg\", \"xt\", \"VX\", \"C9\", \"4ND\", \"7kF\", \n\"5F4\", \"4Ct\", \"0xZ\", \"2My\", \"1L\", \"0Vv\", \"4mX\", \"4x9\", \"430\", \"4bY\", \"0Yw\", \"zE\", \"Ti\", \"03S\", \"4Lu\", \"5I5\", \"60n\", \"4AE\", \"L8\", \"YY\", \n\"wu\", \"0TG\", \"4oi\", \"6ZJ\" };\n\n\n#endif\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/crc64.c",
    "content": "/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>\n * Copyright (c) 2020, Amazon Web Services\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 \"crc64.h\"\n#include \"crcspeed.h\"\nstatic uint64_t crc64_table[8][256] = {{0}};\n\n#define POLY UINT64_C(0xad93d23594c935a9)\n/******************** BEGIN GENERATED PYCRC FUNCTIONS ********************/\n/**\n * Generated on Sun Dec 21 14:14:07 2014,\n * by pycrc v0.8.2, https://www.tty1.net/pycrc/\n *\n * LICENSE ON GENERATED CODE:\n * ==========================\n * As of version 0.6, pycrc is released under the terms of the MIT licence.\n * The code generated by pycrc is not considered a substantial portion of the\n * software, therefore the author of pycrc will not claim any copyright on\n * the generated code.\n * ==========================\n *\n * CRC configuration:\n *    Width        = 64\n *    Poly         = 0xad93d23594c935a9\n *    XorIn        = 0xffffffffffffffff\n *    ReflectIn    = True\n *    XorOut       = 0x0000000000000000\n *    ReflectOut   = True\n *    Algorithm    = bit-by-bit-fast\n *\n * Modifications after generation (by matt):\n *   - included finalize step in-line with update for single-call generation\n *   - re-worked some inner variable architectures\n *   - adjusted function parameters to match expected prototypes.\n *****************************************************************************/\n\n/**\n * Reflect all bits of a \\a data word of \\a data_len bytes.\n *\n * \\param data         The data word to be reflected.\n * \\param data_len     The width of \\a data expressed in number of bits.\n * \\return             The reflected data.\n *****************************************************************************/\nstatic inline uint_fast64_t crc_reflect(uint_fast64_t data, size_t data_len) {\n    uint_fast64_t ret = data & 0x01;\n\n    for (size_t i = 1; i < data_len; i++) {\n        data >>= 1;\n        ret = (ret << 1) | (data & 0x01);\n    }\n\n    return ret;\n}\n\n/**\n *  Update the crc value with new data.\n *\n * \\param crc      The current crc value.\n * \\param data     Pointer to a buffer of \\a data_len bytes.\n * \\param data_len Number of bytes in the \\a data buffer.\n * \\return         The updated crc value.\n ******************************************************************************/\nuint64_t _crc64(uint_fast64_t crc, const void *in_data, const uint64_t len) {\n    const uint8_t *data = in_data;\n    unsigned long long bit;\n\n    for (uint64_t offset = 0; offset < len; offset++) {\n        uint8_t c = data[offset];\n        for (uint_fast8_t i = 0x01; i & 0xff; i <<= 1) {\n            bit = crc & 0x8000000000000000;\n            if (c & i) {\n                bit = !bit;\n            }\n\n            crc <<= 1;\n            if (bit) {\n                crc ^= POLY;\n            }\n        }\n\n        crc &= 0xffffffffffffffff;\n    }\n\n    crc = crc & 0xffffffffffffffff;\n    return crc_reflect(crc, 64) ^ 0x0000000000000000;\n}\n\n/******************** END GENERATED PYCRC FUNCTIONS ********************/\n\n/* Initializes the 16KB lookup tables. */\nvoid crc64_init(void) {\n    crcspeed64native_init(_crc64, crc64_table);\n}\n\n/* Compute crc64 */\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {\n    return crcspeed64native(crc64_table, crc, (void *) s, l);\n}\n\n/* Test main */\n#ifdef REDIS_TEST\n#include <stdio.h>\n\n#define UNUSED(x) (void)(x)\nint crc64Test(int argc, char *argv[]) {\n    UNUSED(argc);\n    UNUSED(argv);\n    crc64_init();\n    printf(\"[calcula]: e9c6d914c4b8d9ca == %016\" PRIx64 \"\\n\",\n           (uint64_t)_crc64(0, \"123456789\", 9));\n    printf(\"[64speed]: e9c6d914c4b8d9ca == %016\" PRIx64 \"\\n\",\n           (uint64_t)crc64(0, \"123456789\", 9));\n    char li[] = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed \"\n                \"do eiusmod tempor incididunt ut labore et dolore magna \"\n                \"aliqua. Ut enim ad minim veniam, quis nostrud exercitation \"\n                \"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis \"\n                \"aute irure dolor in reprehenderit in voluptate velit esse \"\n                \"cillum dolore eu fugiat nulla pariatur. Excepteur sint \"\n                \"occaecat cupidatat non proident, sunt in culpa qui officia \"\n                \"deserunt mollit anim id est laborum.\";\n    printf(\"[calcula]: c7794709e69683b3 == %016\" PRIx64 \"\\n\",\n           (uint64_t)_crc64(0, li, sizeof(li)));\n    printf(\"[64speed]: c7794709e69683b3 == %016\" PRIx64 \"\\n\",\n           (uint64_t)crc64(0, li, sizeof(li)));\n    return 0;\n}\n\n#endif\n\n#ifdef REDIS_TEST_MAIN\nint main(int argc, char *argv[]) {\n    return crc64Test(argc, argv);\n}\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/crc64.h",
    "content": "#ifndef CRC64_H\n#define CRC64_H\n\n#include <stdint.h>\n\nvoid crc64_init(void);\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);\n\n#ifdef REDIS_TEST\nint crc64Test(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/crcspeed.c",
    "content": "/*\n * Copyright (C) 2013 Mark Adler\n * Originally by: crc64.c Version 1.4  16 Dec 2013  Mark Adler\n * Modifications by Matt Stancliff <matt@genges.com>:\n *   - removed CRC64-specific behavior\n *   - added generation of lookup tables by parameters\n *   - removed inversion of CRC input/result\n *   - removed automatic initialization in favor of explicit initialization\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the author be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n\n  Mark Adler\n  madler@alumni.caltech.edu\n */\n\n#include \"crcspeed.h\"\n\n/* Fill in a CRC constants table. */\nvoid crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {\n    uint64_t crc;\n\n    /* generate CRCs for all single byte sequences */\n    for (int n = 0; n < 256; n++) {\n        table[0][n] = crcfn(0, &n, 1);\n    }\n\n    /* generate nested CRC table for future slice-by-8 lookup */\n    for (int n = 0; n < 256; n++) {\n        crc = table[0][n];\n        for (int k = 1; k < 8; k++) {\n            crc = table[0][crc & 0xff] ^ (crc >> 8);\n            table[k][n] = crc;\n        }\n    }\n}\n\nvoid crcspeed16little_init(crcfn16 crcfn, uint16_t table[8][256]) {\n    uint16_t crc;\n\n    /* generate CRCs for all single byte sequences */\n    for (int n = 0; n < 256; n++) {\n        table[0][n] = crcfn(0, &n, 1);\n    }\n\n    /* generate nested CRC table for future slice-by-8 lookup */\n    for (int n = 0; n < 256; n++) {\n        crc = table[0][n];\n        for (int k = 1; k < 8; k++) {\n            crc = table[0][(crc >> 8) & 0xff] ^ (crc << 8);\n            table[k][n] = crc;\n        }\n    }\n}\n\n/* Reverse the bytes in a 64-bit word. */\nstatic inline uint64_t rev8(uint64_t a) {\n#if defined(__GNUC__) || defined(__clang__)\n    return __builtin_bswap64(a);\n#else\n    uint64_t m;\n\n    m = UINT64_C(0xff00ff00ff00ff);\n    a = ((a >> 8) & m) | (a & m) << 8;\n    m = UINT64_C(0xffff0000ffff);\n    a = ((a >> 16) & m) | (a & m) << 16;\n    return a >> 32 | a << 32;\n#endif\n}\n\n/* This function is called once to initialize the CRC table for use on a\n   big-endian architecture. */\nvoid crcspeed64big_init(crcfn64 fn, uint64_t big_table[8][256]) {\n    /* Create the little endian table then reverse all the entires. */\n    crcspeed64little_init(fn, big_table);\n    for (int k = 0; k < 8; k++) {\n        for (int n = 0; n < 256; n++) {\n            big_table[k][n] = rev8(big_table[k][n]);\n        }\n    }\n}\n\nvoid crcspeed16big_init(crcfn16 fn, uint16_t big_table[8][256]) {\n    /* Create the little endian table then reverse all the entires. */\n    crcspeed16little_init(fn, big_table);\n    for (int k = 0; k < 8; k++) {\n        for (int n = 0; n < 256; n++) {\n            big_table[k][n] = rev8(big_table[k][n]);\n        }\n    }\n}\n\n/* Calculate a non-inverted CRC multiple bytes at a time on a little-endian\n * architecture. If you need inverted CRC, invert *before* calling and invert\n * *after* calling.\n * 64 bit crc = process 8 bytes at once;\n */\nuint64_t crcspeed64little(uint64_t little_table[8][256], uint64_t crc,\n                          void *buf, size_t len) {\n    unsigned char *next = buf;\n\n    /* process individual bytes until we reach an 8-byte aligned pointer */\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    /* fast middle processing, 8 bytes (aligned!) per loop */\n    while (len >= 8) {\n        crc ^= *(uint64_t *)next;\n        crc = little_table[7][crc & 0xff] ^\n              little_table[6][(crc >> 8) & 0xff] ^\n              little_table[5][(crc >> 16) & 0xff] ^\n              little_table[4][(crc >> 24) & 0xff] ^\n              little_table[3][(crc >> 32) & 0xff] ^\n              little_table[2][(crc >> 40) & 0xff] ^\n              little_table[1][(crc >> 48) & 0xff] ^\n              little_table[0][crc >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    /* process remaining bytes (can't be larger than 8) */\n    while (len) {\n        crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    return crc;\n}\n\nuint16_t crcspeed16little(uint16_t little_table[8][256], uint16_t crc,\n                          void *buf, size_t len) {\n    unsigned char *next = buf;\n\n    /* process individual bytes until we reach an 8-byte aligned pointer */\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);\n        len--;\n    }\n\n    /* fast middle processing, 8 bytes (aligned!) per loop */\n    while (len >= 8) {\n        uint64_t n = *(uint64_t *)next;\n        crc = little_table[7][(n & 0xff) ^ ((crc >> 8) & 0xff)] ^\n              little_table[6][((n >> 8) & 0xff) ^ (crc & 0xff)] ^\n              little_table[5][(n >> 16) & 0xff] ^\n              little_table[4][(n >> 24) & 0xff] ^\n              little_table[3][(n >> 32) & 0xff] ^\n              little_table[2][(n >> 40) & 0xff] ^\n              little_table[1][(n >> 48) & 0xff] ^\n              little_table[0][n >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    /* process remaining bytes (can't be larger than 8) */\n    while (len) {\n        crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);\n        len--;\n    }\n\n    return crc;\n}\n\n/* Calculate a non-inverted CRC eight bytes at a time on a big-endian\n * architecture.\n */\nuint64_t crcspeed64big(uint64_t big_table[8][256], uint64_t crc, void *buf,\n                       size_t len) {\n    unsigned char *next = buf;\n\n    crc = rev8(crc);\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);\n        len--;\n    }\n\n    while (len >= 8) {\n        crc ^= *(uint64_t *)next;\n        crc = big_table[0][crc & 0xff] ^\n              big_table[1][(crc >> 8) & 0xff] ^\n              big_table[2][(crc >> 16) & 0xff] ^\n              big_table[3][(crc >> 24) & 0xff] ^\n              big_table[4][(crc >> 32) & 0xff] ^\n              big_table[5][(crc >> 40) & 0xff] ^\n              big_table[6][(crc >> 48) & 0xff] ^\n              big_table[7][crc >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    while (len) {\n        crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);\n        len--;\n    }\n\n    return rev8(crc);\n}\n\n/* WARNING: Completely untested on big endian architecture.  Possibly broken. */\nuint16_t crcspeed16big(uint16_t big_table[8][256], uint16_t crc_in, void *buf,\n                       size_t len) {\n    unsigned char *next = buf;\n    uint64_t crc = crc_in;\n\n    crc = rev8(crc);\n    while (len && ((uintptr_t)next & 7) != 0) {\n        crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    while (len >= 8) {\n        uint64_t n = *(uint64_t *)next;\n        crc = big_table[0][(n & 0xff) ^ ((crc >> (56 - 8)) & 0xff)] ^\n              big_table[1][((n >> 8) & 0xff) ^ (crc & 0xff)] ^\n              big_table[2][(n >> 16) & 0xff] ^\n              big_table[3][(n >> 24) & 0xff] ^\n              big_table[4][(n >> 32) & 0xff] ^\n              big_table[5][(n >> 40) & 0xff] ^\n              big_table[6][(n >> 48) & 0xff] ^\n              big_table[7][n >> 56];\n        next += 8;\n        len -= 8;\n    }\n\n    while (len) {\n        crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);\n        len--;\n    }\n\n    return rev8(crc);\n}\n\n/* Return the CRC of buf[0..len-1] with initial crc, processing eight bytes\n   at a time using passed-in lookup table.\n   This selects one of two routines depending on the endianess of\n   the architecture. */\nuint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,\n                          size_t len) {\n    uint64_t n = 1;\n\n    return *(char *)&n ? crcspeed64little(table, crc, buf, len)\n                       : crcspeed64big(table, crc, buf, len);\n}\n\nuint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,\n                          size_t len) {\n    uint64_t n = 1;\n\n    return *(char *)&n ? crcspeed16little(table, crc, buf, len)\n                       : crcspeed16big(table, crc, buf, len);\n}\n\n/* Initialize CRC lookup table in architecture-dependent manner. */\nvoid crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]) {\n    uint64_t n = 1;\n\n    *(char *)&n ? crcspeed64little_init(fn, table)\n                : crcspeed64big_init(fn, table);\n}\n\nvoid crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]) {\n    uint64_t n = 1;\n\n    *(char *)&n ? crcspeed16little_init(fn, table)\n                : crcspeed16big_init(fn, table);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/crcspeed.h",
    "content": "/* Copyright (c) 2014, Matt Stancliff <matt@genges.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#ifndef CRCSPEED_H\n#define CRCSPEED_H\n\n#include <inttypes.h>\n#include <stdio.h>\n\ntypedef uint64_t (*crcfn64)(uint64_t, const void *, const uint64_t);\ntypedef uint16_t (*crcfn16)(uint16_t, const void *, const uint64_t);\n\n/* CRC-64 */\nvoid crcspeed64little_init(crcfn64 fn, uint64_t table[8][256]);\nvoid crcspeed64big_init(crcfn64 fn, uint64_t table[8][256]);\nvoid crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]);\n\nuint64_t crcspeed64little(uint64_t table[8][256], uint64_t crc, void *buf,\n                          size_t len);\nuint64_t crcspeed64big(uint64_t table[8][256], uint64_t crc, void *buf,\n                       size_t len);\nuint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,\n                          size_t len);\n\n/* CRC-16 */\nvoid crcspeed16little_init(crcfn16 fn, uint16_t table[8][256]);\nvoid crcspeed16big_init(crcfn16 fn, uint16_t table[8][256]);\nvoid crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]);\n\nuint16_t crcspeed16little(uint16_t table[8][256], uint16_t crc, void *buf,\n                          size_t len);\nuint16_t crcspeed16big(uint16_t table[8][256], uint16_t crc, void *buf,\n                       size_t len);\nuint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,\n                          size_t len);\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"cluster.h\"\n#include \"atomicvar.h\"\n\n#include <signal.h>\n#include <ctype.h>\n\n/*-----------------------------------------------------------------------------\n * C-level DB API\n *----------------------------------------------------------------------------*/\n\nint keyIsExpired(redisDb *db, robj *key);\n\n/* Update LFU when an object is accessed.\n * Firstly, decrement the counter if the decrement time is reached.\n * Then logarithmically increment the counter, and update the access time. */\nvoid updateLFU(robj *val) {\n    unsigned long counter = LFUDecrAndReturn(val);\n    counter = LFULogIncr(counter);\n    val->lru = (LFUGetTimeInMinutes()<<8) | counter;\n}\n\n/* Low level key lookup API, not actually called directly from commands\n * implementations that should instead rely on lookupKeyRead(),\n * lookupKeyWrite() and lookupKeyReadWithFlags(). */\nrobj *lookupKey(redisDb *db, robj *key, int flags) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n    if (de) {\n        robj *val = dictGetVal(de);\n\n        /* Update the access time for the ageing algorithm.\n         * Don't do it if we have a saving child, as this will trigger\n         * a copy on write madness. */\n        if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH)){\n            if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n                updateLFU(val);\n            } else {\n                val->lru = LRU_CLOCK();\n            }\n        }\n        return val;\n    } else {\n        return NULL;\n    }\n}\n\n/* Lookup a key for read operations, or return NULL if the key is not found\n * in the specified DB.\n *\n * As a side effect of calling this function:\n * 1. A key gets expired if it reached it's TTL.\n * 2. The key last access time is updated.\n * 3. The global keys hits/misses stats are updated (reported in INFO).\n * 4. If keyspace notifications are enabled, a \"keymiss\" notification is fired.\n *\n * This API should not be used when we write to the key after obtaining\n * the object linked to the key, but only for read only operations.\n *\n * Flags change the behavior of this command:\n *\n *  LOOKUP_NONE (or zero): no special flags are passed.\n *  LOOKUP_NOTOUCH: don't alter the last access time of the key.\n *\n * Note: this function also returns NULL if the key is logically expired\n * but still existing, in case this is a slave, since this API is called only\n * for read operations. Even if the key expiry is master-driven, we can\n * correctly report a key is expired on slaves even if the master is lagging\n * expiring our key via DELs in the replication link. */\nrobj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {\n    robj *val;\n\n    if (expireIfNeeded(db,key) == 1) {\n        /* Key expired. If we are in the context of a master, expireIfNeeded()\n         * returns 0 only when the key does not exist at all, so it's safe\n         * to return NULL ASAP. */\n        if (server.masterhost == NULL) {\n            server.stat_keyspace_misses++;\n            notifyKeyspaceEvent(NOTIFY_KEY_MISS, \"keymiss\", key, db->id);\n            return NULL;\n        }\n\n        /* However if we are in the context of a slave, expireIfNeeded() will\n         * not really try to expire the key, it only returns information\n         * about the \"logical\" status of the key: key expiring is up to the\n         * master in order to have a consistent view of master's data set.\n         *\n         * However, if the command caller is not the master, and as additional\n         * safety measure, the command invoked is a read-only command, we can\n         * safely return NULL here, and provide a more consistent behavior\n         * to clients accessign expired values in a read-only fashion, that\n         * will say the key as non existing.\n         *\n         * Notably this covers GETs when slaves are used to scale reads. */\n        if (server.current_client &&\n            server.current_client != server.master &&\n            server.current_client->cmd &&\n            server.current_client->cmd->flags & CMD_READONLY)\n        {\n            server.stat_keyspace_misses++;\n            notifyKeyspaceEvent(NOTIFY_KEY_MISS, \"keymiss\", key, db->id);\n            return NULL;\n        }\n    }\n    val = lookupKey(db,key,flags);\n    if (val == NULL) {\n        server.stat_keyspace_misses++;\n        notifyKeyspaceEvent(NOTIFY_KEY_MISS, \"keymiss\", key, db->id);\n    }\n    else\n        server.stat_keyspace_hits++;\n    return val;\n}\n\n/* Like lookupKeyReadWithFlags(), but does not use any flag, which is the\n * common case. */\nrobj *lookupKeyRead(redisDb *db, robj *key) {\n    return lookupKeyReadWithFlags(db,key,LOOKUP_NONE);\n}\n\n/* Lookup a key for write operations, and as a side effect, if needed, expires\n * the key if its TTL is reached.\n *\n * Returns the linked value object if the key exists or NULL if the key\n * does not exist in the specified DB. */\nrobj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) {\n    expireIfNeeded(db,key);\n    return lookupKey(db,key,flags);\n}\n\nrobj *lookupKeyWrite(redisDb *db, robj *key) {\n    return lookupKeyWriteWithFlags(db, key, LOOKUP_NONE);\n}\n\nrobj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {\n    robj *o = lookupKeyRead(c->db, key);\n    if (!o) addReply(c,reply);\n    return o;\n}\n\nrobj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) {\n    robj *o = lookupKeyWrite(c->db, key);\n    if (!o) addReply(c,reply);\n    return o;\n}\n\n/* Add the key to the DB. It's up to the caller to increment the reference\n * counter of the value if needed.\n *\n * The program is aborted if the key already exists. */\nvoid dbAdd(redisDb *db, robj *key, robj *val) {\n    sds copy = sdsdup(key->ptr);\n    int retval = dictAdd(db->dict, copy, val);\n\n    serverAssertWithInfo(NULL,key,retval == DICT_OK);\n    if (val->type == OBJ_LIST ||\n        val->type == OBJ_ZSET ||\n        val->type == OBJ_STREAM)\n        signalKeyAsReady(db, key);\n    if (server.cluster_enabled) slotToKeyAdd(key->ptr);\n}\n\n/* This is a special version of dbAdd() that is used only when loading\n * keys from the RDB file: the key is passed as an SDS string that is\n * retained by the function (and not freed by the caller).\n *\n * Moreover this function will not abort if the key is already busy, to\n * give more control to the caller, nor will signal the key as ready\n * since it is not useful in this context.\n *\n * The function returns 1 if the key was added to the database, taking\n * ownership of the SDS string, otherwise 0 is returned, and is up to the\n * caller to free the SDS string. */\nint dbAddRDBLoad(redisDb *db, sds key, robj *val) {\n    int retval = dictAdd(db->dict, key, val);\n    if (retval != DICT_OK) return 0;\n    if (server.cluster_enabled) slotToKeyAdd(key);\n    return 1;\n}\n\n/* Overwrite an existing key with a new value. Incrementing the reference\n * count of the new value is up to the caller.\n * This function does not modify the expire time of the existing key.\n *\n * The program is aborted if the key was not already present. */\nvoid dbOverwrite(redisDb *db, robj *key, robj *val) {\n    dictEntry *de = dictFind(db->dict,key->ptr);\n\n    serverAssertWithInfo(NULL,key,de != NULL);\n    dictEntry auxentry = *de;\n    robj *old = dictGetVal(de);\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        val->lru = old->lru;\n    }\n    dictSetVal(db->dict, de, val);\n\n    if (server.lazyfree_lazy_server_del) {\n        freeObjAsync(old);\n        dictSetVal(db->dict, &auxentry, NULL);\n    }\n\n    dictFreeVal(db->dict, &auxentry);\n}\n\n/* High level Set operation. This function can be used in order to set\n * a key, whatever it was existing or not, to a new object.\n *\n * 1) The ref count of the value object is incremented.\n * 2) clients WATCHing for the destination key notified.\n * 3) The expire time of the key is reset (the key is made persistent),\n *    unless 'keepttl' is true.\n *\n * All the new keys in the database should be created via this interface.\n * The client 'c' argument may be set to NULL if the operation is performed\n * in a context where there is no clear client performing the operation. */\nvoid genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) {\n    if (lookupKeyWrite(db,key) == NULL) {\n        dbAdd(db,key,val);\n    } else {\n        dbOverwrite(db,key,val);\n    }\n    incrRefCount(val);\n    if (!keepttl) removeExpire(db,key);\n    if (signal) signalModifiedKey(c,db,key);\n}\n\n/* Common case for genericSetKey() where the TTL is not retained. */\nvoid setKey(client *c, redisDb *db, robj *key, robj *val) {\n    genericSetKey(c,db,key,val,0,1);\n}\n\n/* Return true if the specified key exists in the specified database.\n * LRU/LFU info is not updated in any way. */\nint dbExists(redisDb *db, robj *key) {\n    return dictFind(db->dict,key->ptr) != NULL;\n}\n\n/* Return a random key, in form of a Redis object.\n * If there are no keys, NULL is returned.\n *\n * The function makes sure to return keys not already expired. */\nrobj *dbRandomKey(redisDb *db) {\n    dictEntry *de;\n    int maxtries = 100;\n    int allvolatile = dictSize(db->dict) == dictSize(db->expires);\n\n    while(1) {\n        sds key;\n        robj *keyobj;\n\n        de = dictGetFairRandomKey(db->dict);\n        if (de == NULL) return NULL;\n\n        key = dictGetKey(de);\n        keyobj = createStringObject(key,sdslen(key));\n        if (dictFind(db->expires,key)) {\n            if (allvolatile && server.masterhost && --maxtries == 0) {\n                /* If the DB is composed only of keys with an expire set,\n                 * it could happen that all the keys are already logically\n                 * expired in the slave, so the function cannot stop because\n                 * expireIfNeeded() is false, nor it can stop because\n                 * dictGetRandomKey() returns NULL (there are keys to return).\n                 * To prevent the infinite loop we do some tries, but if there\n                 * are the conditions for an infinite loop, eventually we\n                 * return a key name that may be already expired. */\n                return keyobj;\n            }\n            if (expireIfNeeded(db,keyobj)) {\n                decrRefCount(keyobj);\n                continue; /* search for another key. This expired. */\n            }\n        }\n        return keyobj;\n    }\n}\n\n/* Delete a key, value, and associated expiration entry if any, from the DB */\nint dbSyncDelete(redisDb *db, robj *key) {\n    /* Deleting an entry from the expires dict will not free the sds of\n     * the key, because it is shared with the main dictionary. */\n    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);\n    if (dictDelete(db->dict,key->ptr) == DICT_OK) {\n        if (server.cluster_enabled) slotToKeyDel(key->ptr);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* This is a wrapper whose behavior depends on the Redis lazy free\n * configuration. Deletes the key synchronously or asynchronously. */\nint dbDelete(redisDb *db, robj *key) {\n    return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key) :\n                                             dbSyncDelete(db,key);\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,OBJ_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    serverAssert(o->type == OBJ_STRING);\n    if (o->refcount != 1 || o->encoding != OBJ_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/* Remove all keys from all the databases in a Redis server.\n * If callback is given the function is called from time to time to\n * signal that work is in progress.\n *\n * The dbnum can be -1 if all the DBs should be flushed, or the specified\n * DB number if we want to flush only a single Redis database number.\n *\n * Flags are be EMPTYDB_NO_FLAGS if no special flags are specified or\n * 1. EMPTYDB_ASYNC if we want the memory to be freed in a different thread.\n * 2. EMPTYDB_BACKUP if we want to empty the backup dictionaries created by\n *    disklessLoadMakeBackups. In that case we only free memory and avoid\n *    firing module events.\n * and the function to return ASAP.\n *\n * On success the fuction returns the number of keys removed from the\n * database(s). Otherwise -1 is returned in the specific case the\n * DB number is out of range, and errno is set to EINVAL. */\nlong long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*)) {\n    int async = (flags & EMPTYDB_ASYNC);\n    int backup = (flags & EMPTYDB_BACKUP); /* Just free the memory, nothing else */\n    RedisModuleFlushInfoV1 fi = {REDISMODULE_FLUSHINFO_VERSION,!async,dbnum};\n    long long removed = 0;\n\n    if (dbnum < -1 || dbnum >= server.dbnum) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    /* Pre-flush actions */\n    if (!backup) {\n        /* Fire the flushdb modules event. */\n        moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,\n                              REDISMODULE_SUBEVENT_FLUSHDB_START,\n                              &fi);\n\n        /* Make sure the WATCHed keys are affected by the FLUSH* commands.\n         * Note that we need to call the function while the keys are still\n         * there. */\n        signalFlushedDb(dbnum);\n    }\n\n    int startdb, enddb;\n    if (dbnum == -1) {\n        startdb = 0;\n        enddb = server.dbnum-1;\n    } else {\n        startdb = enddb = dbnum;\n    }\n\n    for (int j = startdb; j <= enddb; j++) {\n        removed += dictSize(dbarray[j].dict);\n        if (async) {\n            emptyDbAsync(&dbarray[j]);\n        } else {\n            dictEmpty(dbarray[j].dict,callback);\n            dictEmpty(dbarray[j].expires,callback);\n        }\n    }\n\n    /* Post-flush actions */\n    if (!backup) {\n        if (server.cluster_enabled) {\n            if (async) {\n                slotToKeyFlushAsync();\n            } else {\n                slotToKeyFlush();\n            }\n        }\n        if (dbnum == -1) flushSlaveKeysWithExpireList();\n\n        /* Also fire the end event. Note that this event will fire almost\n         * immediately after the start event if the flush is asynchronous. */\n        moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,\n                              REDISMODULE_SUBEVENT_FLUSHDB_END,\n                              &fi);\n    }\n\n    return removed;\n}\n\nlong long emptyDb(int dbnum, int flags, void(callback)(void*)) {\n    return emptyDbGeneric(server.db, dbnum, flags, callback);\n}\n\nint selectDb(client *c, int id) {\n    if (id < 0 || id >= server.dbnum)\n        return C_ERR;\n    c->db = &server.db[id];\n    return C_OK;\n}\n\nlong long dbTotalServerKeyCount() {\n    long long total = 0;\n    int j;\n    for (j = 0; j < server.dbnum; j++) {\n        total += dictSize(server.db[j].dict);\n    }\n    return total;\n}\n\n/*-----------------------------------------------------------------------------\n * Hooks for key space changes.\n *\n * Every time a key in the database is modified the function\n * signalModifiedKey() is called.\n *\n * Every time a DB is flushed the function signalFlushDb() is called.\n *----------------------------------------------------------------------------*/\n\n/* Note that the 'c' argument may be NULL if the key was modified out of\n * a context of a client. */\nvoid signalModifiedKey(client *c, redisDb *db, robj *key) {\n    touchWatchedKey(db,key);\n    trackingInvalidateKey(c,key);\n}\n\nvoid signalFlushedDb(int dbid) {\n    touchWatchedKeysOnFlush(dbid);\n    trackingInvalidateKeysOnFlush(dbid);\n}\n\n/*-----------------------------------------------------------------------------\n * Type agnostic commands operating on the key space\n *----------------------------------------------------------------------------*/\n\n/* Return the set of flags to use for the emptyDb() call for FLUSHALL\n * and FLUSHDB commands.\n *\n * Currently the command just attempts to parse the \"ASYNC\" option. It\n * also checks if the command arity is wrong.\n *\n * On success C_OK is returned and the flags are stored in *flags, otherwise\n * C_ERR is returned and the function sends an error to the client. */\nint getFlushCommandFlags(client *c, int *flags) {\n    /* Parse the optional ASYNC option. */\n    if (c->argc > 1) {\n        if (c->argc > 2 || strcasecmp(c->argv[1]->ptr,\"async\")) {\n            addReply(c,shared.syntaxerr);\n            return C_ERR;\n        }\n        *flags = EMPTYDB_ASYNC;\n    } else {\n        *flags = EMPTYDB_NO_FLAGS;\n    }\n    return C_OK;\n}\n\n/* Flushes the whole server data set. */\nvoid flushAllDataAndResetRDB(int flags) {\n    server.dirty += emptyDb(-1,flags,NULL);\n    if (server.rdb_child_pid != -1) killRDBChild();\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        int saved_dirty = server.dirty;\n        rdbSaveInfo rsi, *rsiptr;\n        rsiptr = rdbPopulateSaveInfo(&rsi);\n        rdbSave(server.rdb_filename,rsiptr);\n        server.dirty = saved_dirty;\n    }\n    server.dirty++;\n#if defined(USE_JEMALLOC)\n    /* jemalloc 5 doesn't release pages back to the OS when there's no traffic.\n     * for large databases, flushdb blocks for long anyway, so a bit more won't\n     * harm and this way the flush and purge will be synchroneus. */\n    if (!(flags & EMPTYDB_ASYNC))\n        jemalloc_purge();\n#endif\n}\n\n/* FLUSHDB [ASYNC]\n *\n * Flushes the currently SELECTed Redis DB. */\nvoid flushdbCommand(client *c) {\n    int flags;\n\n    if (getFlushCommandFlags(c,&flags) == C_ERR) return;\n    server.dirty += emptyDb(c->db->id,flags,NULL);\n    addReply(c,shared.ok);\n#if defined(USE_JEMALLOC)\n    /* jemalloc 5 doesn't release pages back to the OS when there's no traffic.\n     * for large databases, flushdb blocks for long anyway, so a bit more won't\n     * harm and this way the flush and purge will be synchroneus. */\n    if (!(flags & EMPTYDB_ASYNC))\n        jemalloc_purge();\n#endif\n}\n\n/* FLUSHALL [ASYNC]\n *\n * Flushes the whole server data set. */\nvoid flushallCommand(client *c) {\n    int flags;\n    if (getFlushCommandFlags(c,&flags) == C_ERR) return;\n    flushAllDataAndResetRDB(flags);\n    addReply(c,shared.ok);\n}\n\n/* This command implements DEL and LAZYDEL. */\nvoid delGenericCommand(client *c, int lazy) {\n    int numdel = 0, j;\n\n    for (j = 1; j < c->argc; j++) {\n        expireIfNeeded(c->db,c->argv[j]);\n        int deleted  = lazy ? dbAsyncDelete(c->db,c->argv[j]) :\n                              dbSyncDelete(c->db,c->argv[j]);\n        if (deleted) {\n            signalModifiedKey(c,c->db,c->argv[j]);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"del\",c->argv[j],c->db->id);\n            server.dirty++;\n            numdel++;\n        }\n    }\n    addReplyLongLong(c,numdel);\n}\n\nvoid delCommand(client *c) {\n    delGenericCommand(c,server.lazyfree_lazy_user_del);\n}\n\nvoid unlinkCommand(client *c) {\n    delGenericCommand(c,1);\n}\n\n/* EXISTS key1 key2 ... key_N.\n * Return value is the number of keys existing. */\nvoid existsCommand(client *c) {\n    long long count = 0;\n    int j;\n\n    for (j = 1; j < c->argc; j++) {\n        if (lookupKeyRead(c->db,c->argv[j])) count++;\n    }\n    addReplyLongLong(c,count);\n}\n\nvoid selectCommand(client *c) {\n    long id;\n\n    if (getLongFromObjectOrReply(c, c->argv[1], &id,\n        \"invalid DB index\") != C_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    if (selectDb(c,id) == C_ERR) {\n        addReplyError(c,\"DB index is out of range\");\n    } else {\n        addReply(c,shared.ok);\n    }\n}\n\nvoid randomkeyCommand(client *c) {\n    robj *key;\n\n    if ((key = dbRandomKey(c->db)) == NULL) {\n        addReplyNull(c);\n        return;\n    }\n\n    addReplyBulk(c,key);\n    decrRefCount(key);\n}\n\nvoid keysCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n    sds pattern = c->argv[1]->ptr;\n    int plen = sdslen(pattern), allkeys;\n    unsigned long numkeys = 0;\n    void *replylen = addReplyDeferredLen(c);\n\n    di = dictGetSafeIterator(c->db->dict);\n    allkeys = (pattern[0] == '*' && plen == 1);\n    while((de = dictNext(di)) != NULL) {\n        sds key = dictGetKey(de);\n        robj *keyobj;\n\n        if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {\n            keyobj = createStringObject(key,sdslen(key));\n            if (!keyIsExpired(c->db,keyobj)) {\n                addReplyBulk(c,keyobj);\n                numkeys++;\n            }\n            decrRefCount(keyobj);\n        }\n    }\n    dictReleaseIterator(di);\n    setDeferredArrayLen(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 == OBJ_SET) {\n        sds keysds = dictGetKey(de);\n        key = createStringObject(keysds,sdslen(keysds));\n    } else if (o->type == OBJ_HASH) {\n        sds sdskey = dictGetKey(de);\n        sds sdsval = dictGetVal(de);\n        key = createStringObject(sdskey,sdslen(sdskey));\n        val = createStringObject(sdsval,sdslen(sdsval));\n    } else if (o->type == OBJ_ZSET) {\n        sds sdskey = dictGetKey(de);\n        key = createStringObject(sdskey,sdslen(sdskey));\n        val = createStringObjectFromLongDouble(*(double*)dictGetVal(de),0);\n    } else {\n        serverPanic(\"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 C_OK. Otherwise return C_ERR and send an error to the\n * client. */\nint parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor) {\n    char *eptr;\n\n    /* Use strtoul() because we need an *unsigned* long, so\n     * getLongLongFromObject() does not cover the whole cursor space. */\n    errno = 0;\n    *cursor = strtoul(o->ptr, &eptr, 10);\n    if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\\0' || errno == ERANGE)\n    {\n        addReplyError(c, \"invalid cursor\");\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* This command implements SCAN, HSCAN and SSCAN commands.\n * If object 'o' is passed, then it must be a Hash, Set or Zset object, otherwise\n * if 'o' is NULL the command will operate on the dictionary associated with\n * the current database.\n *\n * When 'o' is not NULL the function assumes that the first argument in\n * the client arguments vector is a key so it skips it before iterating\n * in order to parse options.\n *\n * In the case of a Hash object the function returns both the field and value\n * of every element on the Hash. */\nvoid scanGenericCommand(client *c, robj *o, unsigned long cursor) {\n    int i, j;\n    list *keys = listCreate();\n    listNode *node, *nextnode;\n    long count = 10;\n    sds pat = NULL;\n    sds typename = NULL;\n    int patlen = 0, 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    serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH ||\n                o->type == OBJ_ZSET);\n\n    /* Set i to the first option argument. The previous one is the cursor. */\n    i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */\n\n    /* Step 1: Parse options. */\n    while (i < c->argc) {\n        j = c->argc - i;\n        if (!strcasecmp(c->argv[i]->ptr, \"count\") && j >= 2) {\n            if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL)\n                != C_OK)\n            {\n                goto cleanup;\n            }\n\n            if (count < 1) {\n                addReply(c,shared.syntaxerr);\n                goto cleanup;\n            }\n\n            i += 2;\n        } else if (!strcasecmp(c->argv[i]->ptr, \"match\") && j >= 2) {\n            pat = c->argv[i+1]->ptr;\n            patlen = sdslen(pat);\n\n            /* The pattern always matches if it is exactly \"*\", so it is\n             * equivalent to disabling it. */\n            use_pattern = !(pat[0] == '*' && patlen == 1);\n\n            i += 2;\n        } else if (!strcasecmp(c->argv[i]->ptr, \"type\") && o == NULL && j >= 2) {\n            /* SCAN for a particular type only applies to the db dict */\n            typename = c->argv[i+1]->ptr;\n            i+= 2;\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\n    /* Handle the case of a hash table. */\n    ht = NULL;\n    if (o == NULL) {\n        ht = c->db->dict;\n    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {\n        ht = o->ptr;\n    } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) {\n        ht = o->ptr;\n        count *= 2; /* We return key / value for this type. */\n    } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = o->ptr;\n        ht = zs->dict;\n        count *= 2; /* We return key / value for this type. */\n    }\n\n    if (ht) {\n        void *privdata[2];\n        /* We set the max number of iterations to ten times the specified\n         * COUNT, so if the hash table is in a pathological state (very\n         * sparsely populated) we avoid to block too much time at the cost\n         * of returning no or very few elements. */\n        long maxiterations = count*10;\n\n        /* We pass two pointers to the callback: the list to which it will\n         * add new elements, and the object containing the dictionary so that\n         * it is possible to fetch more data in a type-dependent way. */\n        privdata[0] = keys;\n        privdata[1] = o;\n        do {\n            cursor = dictScan(ht, cursor, scanCallback, NULL, privdata);\n        } while (cursor &&\n              maxiterations-- &&\n              listLength(keys) < (unsigned long)count);\n    } else if (o->type == OBJ_SET) {\n        int pos = 0;\n        int64_t ll;\n\n        while(intsetGet(o->ptr,pos++,&ll))\n            listAddNodeTail(keys,createStringObjectFromLongLong(ll));\n        cursor = 0;\n    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {\n        unsigned char *p = ziplistIndex(o->ptr,0);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vll;\n\n        while(p) {\n            ziplistGet(p,&vstr,&vlen,&vll);\n            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        serverPanic(\"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[LONG_STR_SIZE];\n                int len;\n\n                serverAssert(kobj->encoding == OBJ_ENCODING_INT);\n                len = ll2string(buf,sizeof(buf),(long)kobj->ptr);\n                if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1;\n            }\n        }\n\n        /* Filter an element if it isn't the type we want. */\n        if (!filter && o == NULL && typename){\n            robj* typecheck = lookupKeyReadWithFlags(c->db, kobj, LOOKUP_NOTOUCH);\n            char* type = getObjectTypeName(typecheck);\n            if (strcasecmp((char*) typename, type)) filter = 1;\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 == OBJ_ZSET || o->type == OBJ_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    addReplyArrayLen(c, 2);\n    addReplyBulkLongLong(c,cursor);\n\n    addReplyArrayLen(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(client *c) {\n    unsigned long cursor;\n    if (parseScanCursorOrReply(c,c->argv[1],&cursor) == C_ERR) return;\n    scanGenericCommand(c,NULL,cursor);\n}\n\nvoid dbsizeCommand(client *c) {\n    addReplyLongLong(c,dictSize(c->db->dict));\n}\n\nvoid lastsaveCommand(client *c) {\n    addReplyLongLong(c,server.lastsave);\n}\n\nchar* getObjectTypeName(robj *o) {\n    char* type;\n    if (o == NULL) {\n        type = \"none\";\n    } else {\n        switch(o->type) {\n        case OBJ_STRING: type = \"string\"; break;\n        case OBJ_LIST: type = \"list\"; break;\n        case OBJ_SET: type = \"set\"; break;\n        case OBJ_ZSET: type = \"zset\"; break;\n        case OBJ_HASH: type = \"hash\"; break;\n        case OBJ_STREAM: type = \"stream\"; break;\n        case OBJ_MODULE: {\n            moduleValue *mv = o->ptr;\n            type = mv->type->name;\n        }; break;\n        default: type = \"unknown\"; break;\n        }\n    }\n    return type;\n}\n\nvoid typeCommand(client *c) {\n    robj *o;\n    o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH);\n    addReplyStatus(c, getObjectTypeName(o));\n}\n\nvoid shutdownCommand(client *c) {\n    int flags = 0;\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    } else if (c->argc == 2) {\n        if (!strcasecmp(c->argv[1]->ptr,\"nosave\")) {\n            flags |= SHUTDOWN_NOSAVE;\n        } else if (!strcasecmp(c->argv[1]->ptr,\"save\")) {\n            flags |= SHUTDOWN_SAVE;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n    /* When SHUTDOWN is called while the server is loading a dataset in\n     * memory we need to make sure no attempt is performed to save\n     * the dataset on shutdown (otherwise it could overwrite the current DB\n     * with half-read data).\n     *\n     * Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */\n    if (server.loading || server.sentinel_mode)\n        flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;\n    if (prepareForShutdown(flags) == C_OK) exit(0);\n    addReplyError(c,\"Errors trying to SHUTDOWN. Check logs.\");\n}\n\nvoid renameGenericCommand(client *c, int nx) {\n    robj *o;\n    long long expire;\n    int samekey = 0;\n\n    /* When source and dest key is the same, no operation is performed,\n     * if the key exists, however we still return an error on unexisting key. */\n    if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) samekey = 1;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)\n        return;\n\n    if (samekey) {\n        addReply(c,nx ? shared.czero : shared.ok);\n        return;\n    }\n\n    incrRefCount(o);\n    expire = getExpire(c->db,c->argv[1]);\n    if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {\n        if (nx) {\n            decrRefCount(o);\n            addReply(c,shared.czero);\n            return;\n        }\n        /* Overwrite: delete the old key before creating the new one\n         * with the same name. */\n        dbDelete(c->db,c->argv[2]);\n    }\n    dbAdd(c->db,c->argv[2],o);\n    if (expire != -1) setExpire(c,c->db,c->argv[2],expire);\n    dbDelete(c->db,c->argv[1]);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    signalModifiedKey(c,c->db,c->argv[2]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"rename_from\",\n        c->argv[1],c->db->id);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\"rename_to\",\n        c->argv[2],c->db->id);\n    server.dirty++;\n    addReply(c,nx ? shared.cone : shared.ok);\n}\n\nvoid renameCommand(client *c) {\n    renameGenericCommand(c,0);\n}\n\nvoid renamenxCommand(client *c) {\n    renameGenericCommand(c,1);\n}\n\nvoid moveCommand(client *c) {\n    robj *o;\n    redisDb *src, *dst;\n    int srcid;\n    long long dbid, expire;\n\n    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    src = c->db;\n    srcid = c->db->id;\n\n    if (getLongLongFromObject(c->argv[2],&dbid) == C_ERR ||\n        dbid < INT_MIN || dbid > INT_MAX ||\n        selectDb(c,dbid) == C_ERR)\n    {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n    dst = c->db;\n    selectDb(c,srcid); /* Back to the source DB */\n\n    /* If the user is moving using as target the same\n     * DB as the source DB it is probably an error. */\n    if (src == dst) {\n        addReply(c,shared.sameobjecterr);\n        return;\n    }\n\n    /* Check if the element exists and get a reference */\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (!o) {\n        addReply(c,shared.czero);\n        return;\n    }\n    expire = getExpire(c->db,c->argv[1]);\n\n    /* Return zero if the key already exists in the target DB */\n    if (lookupKeyWrite(dst,c->argv[1]) != NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n    dbAdd(dst,c->argv[1],o);\n    if (expire != -1) setExpire(c,dst,c->argv[1],expire);\n    incrRefCount(o);\n\n    /* OK! key moved, free the entry in the source DB */\n    dbDelete(src,c->argv[1]);\n    signalModifiedKey(c,src,c->argv[1]);\n    signalModifiedKey(c,dst,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"move_from\",c->argv[1],src->id);\n    notifyKeyspaceEvent(NOTIFY_GENERIC,\n                \"move_to\",c->argv[1],dst->id);\n\n    server.dirty++;\n    addReply(c,shared.cone);\n}\n\n/* Helper function for dbSwapDatabases(): scans the list of keys that have\n * one or more blocked clients for B[LR]POP or other blocking commands\n * and signal the keys as ready if they are of the right type. See the comment\n * where the function is used for more info. */\nvoid scanDatabaseForReadyLists(redisDb *db) {\n    dictEntry *de;\n    dictIterator *di = dictGetSafeIterator(db->blocking_keys);\n    while((de = dictNext(di)) != NULL) {\n        robj *key = dictGetKey(de);\n        robj *value = lookupKey(db,key,LOOKUP_NOTOUCH);\n        if (value && (value->type == OBJ_LIST ||\n                      value->type == OBJ_STREAM ||\n                      value->type == OBJ_ZSET))\n            signalKeyAsReady(db, key);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Swap two databases at runtime so that all clients will magically see\n * the new database even if already connected. Note that the client\n * structure c->db points to a given DB, so we need to be smarter and\n * swap the underlying referenced structures, otherwise we would need\n * to fix all the references to the Redis DB structure.\n *\n * Returns C_ERR if at least one of the DB ids are out of range, otherwise\n * C_OK is returned. */\nint dbSwapDatabases(long id1, long id2) {\n    if (id1 < 0 || id1 >= server.dbnum ||\n        id2 < 0 || id2 >= server.dbnum) return C_ERR;\n    if (id1 == id2) return C_OK;\n    redisDb aux = server.db[id1];\n    redisDb *db1 = &server.db[id1], *db2 = &server.db[id2];\n\n    /* Swap hash tables. Note that we don't swap blocking_keys,\n     * ready_keys and watched_keys, since we want clients to\n     * remain in the same DB they were. */\n    db1->dict = db2->dict;\n    db1->expires = db2->expires;\n    db1->avg_ttl = db2->avg_ttl;\n    db1->expires_cursor = db2->expires_cursor;\n\n    db2->dict = aux.dict;\n    db2->expires = aux.expires;\n    db2->avg_ttl = aux.avg_ttl;\n    db2->expires_cursor = aux.expires_cursor;\n\n    /* Now we need to handle clients blocked on lists: as an effect\n     * of swapping the two DBs, a client that was waiting for list\n     * X in a given DB, may now actually be unblocked if X happens\n     * to exist in the new version of the DB, after the swap.\n     *\n     * However normally we only do this check for efficiency reasons\n     * in dbAdd() when a list is created. So here we need to rescan\n     * the list of clients blocked on lists and signal lists as ready\n     * if needed. */\n    scanDatabaseForReadyLists(db1);\n    scanDatabaseForReadyLists(db2);\n    return C_OK;\n}\n\n/* SWAPDB db1 db2 */\nvoid swapdbCommand(client *c) {\n    long id1, id2;\n\n    /* Not allowed in cluster mode: we have just DB 0 there. */\n    if (server.cluster_enabled) {\n        addReplyError(c,\"SWAPDB is not allowed in cluster mode\");\n        return;\n    }\n\n    /* Get the two DBs indexes. */\n    if (getLongFromObjectOrReply(c, c->argv[1], &id1,\n        \"invalid first DB index\") != C_OK)\n        return;\n\n    if (getLongFromObjectOrReply(c, c->argv[2], &id2,\n        \"invalid second DB index\") != C_OK)\n        return;\n\n    /* Swap... */\n    if (dbSwapDatabases(id1,id2) == C_ERR) {\n        addReplyError(c,\"DB index is out of range\");\n        return;\n    } else {\n        server.dirty++;\n        addReply(c,shared.ok);\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Expires API\n *----------------------------------------------------------------------------*/\n\nint removeExpire(redisDb *db, robj *key) {\n    /* An expire may only be removed if there is a corresponding entry in the\n     * main dict. Otherwise, the key will never be freed. */\n    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n    return dictDelete(db->expires,key->ptr) == DICT_OK;\n}\n\n/* Set an expire to the specified key. If the expire is set in the context\n * of an user calling a command 'c' is the client, otherwise 'c' is set\n * to NULL. The 'when' parameter is the absolute unix time in milliseconds\n * after which the key will no longer be considered valid. */\nvoid setExpire(client *c, redisDb *db, robj *key, long long when) {\n    dictEntry *kde, *de;\n\n    /* Reuse the sds from the main dict in the expire dict */\n    kde = dictFind(db->dict,key->ptr);\n    serverAssertWithInfo(NULL,key,kde != NULL);\n    de = dictAddOrFind(db->expires,dictGetKey(kde));\n    dictSetSignedIntegerVal(de,when);\n\n    int writable_slave = server.masterhost && server.repl_slave_ro == 0;\n    if (c && writable_slave && !(c->flags & CLIENT_MASTER))\n        rememberSlaveKeyWithExpire(db,key);\n}\n\n/* Return the expire time of the specified key, or -1 if no expire\n * is associated with this key (i.e. the key is non volatile) */\nlong long getExpire(redisDb *db, robj *key) {\n    dictEntry *de;\n\n    /* No expire? return ASAP */\n    if (dictSize(db->expires) == 0 ||\n       (de = dictFind(db->expires,key->ptr)) == NULL) return -1;\n\n    /* The entry was found in the expire dict, this means it should also\n     * be present in the main dict (safety check). */\n    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);\n    return dictGetSignedIntegerVal(de);\n}\n\n/* Propagate expires into slaves and the AOF file.\n * When a key expires in the master, a DEL operation for this key is sent\n * to all the slaves and the AOF file if enabled.\n *\n * This way the key expiry is centralized in one place, and since both\n * AOF and the master->slave link guarantee operation ordering, everything\n * will be consistent even if we allow write operations against expiring\n * keys. */\nvoid propagateExpire(redisDb *db, robj *key, int lazy) {\n    robj *argv[2];\n\n    argv[0] = lazy ? shared.unlink : shared.del;\n    argv[1] = key;\n    incrRefCount(argv[0]);\n    incrRefCount(argv[1]);\n\n    if (server.aof_state != AOF_OFF)\n        feedAppendOnlyFile(server.delCommand,db->id,argv,2);\n    replicationFeedSlaves(server.slaves,db->id,argv,2);\n\n    decrRefCount(argv[0]);\n    decrRefCount(argv[1]);\n}\n\n/* Check if the key is expired. */\nint keyIsExpired(redisDb *db, robj *key) {\n    mstime_t when = getExpire(db,key);\n    mstime_t now;\n\n    if (when < 0) return 0; /* No expire for this key */\n\n    /* Don't expire anything while loading. It will be done later. */\n    if (server.loading) return 0;\n\n    /* If we are in the context of a Lua script, we pretend 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    if (server.lua_caller) {\n        now = server.lua_time_start;\n    }\n    /* If we are in the middle of a command execution, we still want to use\n     * a reference time that does not change: in that case we just use the\n     * cached time, that we update before each call in the call() function.\n     * This way we avoid that commands such as RPOPLPUSH or similar, that\n     * may re-open the same key multiple times, can invalidate an already\n     * open object in a next call, if the next call will see the key expired,\n     * while the first did not. */\n    else if (server.fixed_time_expire > 0) {\n        now = server.mstime;\n    }\n    /* For the other cases, we want to use the most fresh time we have. */\n    else {\n        now = mstime();\n    }\n\n    /* The key expired if the current (virtual or real) time is greater\n     * than the expire time of the key. */\n    return now > when;\n}\n\n/* This function is called when we are going to perform some operation\n * in a given key, but such key may be already logically expired even if\n * it still exists in the database. The main way this function is called\n * is via lookupKey*() family of functions.\n *\n * The behavior of the function depends on the replication role of the\n * instance, because slave instances do not expire keys, they wait\n * for DELs from the master for consistency matters. However even\n * slaves will try to have a coherent return value for the function,\n * so that read commands executed in the slave side will be able to\n * behave like if the key is expired even if still present (because the\n * master has yet to propagate the DEL).\n *\n * In masters as a side effect of finding a key which is expired, such\n * key will be evicted from the database. Also this may trigger the\n * propagation of a DEL/UNLINK command in AOF / replication stream.\n *\n * The return value of the function is 0 if the key is still valid,\n * otherwise the function returns 1 if the key is expired. */\nint expireIfNeeded(redisDb *db, robj *key) {\n    if (!keyIsExpired(db,key)) return 0;\n\n    /* If we are running in the context of a slave, instead of\n     * evicting the expired key from the database, we return ASAP:\n     * the slave key expiration is controlled by the master that will\n     * send us synthesized DEL operations for expired keys.\n     *\n     * Still we try to return the right information to the caller,\n     * that is, 0 if we think the key should be still valid, 1 if\n     * we think the key is expired at this time. */\n    if (server.masterhost != NULL) return 1;\n\n    /* Delete the key */\n    server.stat_expiredkeys++;\n    propagateExpire(db,key,server.lazyfree_lazy_expire);\n    notifyKeyspaceEvent(NOTIFY_EXPIRED,\n        \"expired\",key,db->id);\n    int retval = server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :\n                                               dbSyncDelete(db,key);\n    if (retval) signalModifiedKey(NULL,db,key);\n    return retval;\n}\n\n/* -----------------------------------------------------------------------------\n * API to get key arguments from commands\n * ---------------------------------------------------------------------------*/\n#define MAX_KEYS_BUFFER 256\nstatic int getKeysTempBuffer[MAX_KEYS_BUFFER];\n\n/* The base case is to use the keys position as given in the command table\n * (firstkey, lastkey, step). */\nint *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {\n    int j, i = 0, last, *keys;\n    UNUSED(argv);\n\n    if (cmd->firstkey == 0) {\n        *numkeys = 0;\n        return NULL;\n    }\n\n    last = cmd->lastkey;\n    if (last < 0) last = argc+last;\n\n    int count = ((last - cmd->firstkey)+1);\n    keys = getKeysTempBuffer;\n    if (count > MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int)*count);\n\n    for (j = cmd->firstkey; j <= last; j += cmd->keystep) {\n        if (j >= argc) {\n            /* Modules commands, and standard commands with a not fixed number\n             * of arguments (negative arity parameter) do not have dispatch\n             * time arity checks, so we need to handle the case where the user\n             * passed an invalid number of arguments here. In this case we\n             * return no keys and expect the command implementation to report\n             * an arity or syntax error. */\n            if (cmd->flags & CMD_MODULE || cmd->arity < 0) {\n                getKeysFreeResult(keys);\n                *numkeys = 0;\n                return NULL;\n            } else {\n                serverPanic(\"Redis built-in command declared keys positions not matching the arity requirements.\");\n            }\n        }\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->flags & CMD_MODULE_GETKEYS) {\n        return moduleGetCommandKeysViaAPI(cmd,argv,argc,numkeys);\n    } else if (!(cmd->flags & CMD_MODULE) && 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    if (result != getKeysTempBuffer)\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    UNUSED(cmd);\n\n    num = atoi(argv[2]->ptr);\n    /* Sanity check. Don't return any key if the command is going to\n     * reply with syntax error. */\n    if (num < 1 || 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 = getKeysTempBuffer;\n    if (num+1>MAX_KEYS_BUFFER)\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    UNUSED(cmd);\n\n    num = atoi(argv[2]->ptr);\n    /* Sanity check. Don't return any key if the command is going to\n     * reply with syntax error. */\n    if (num <= 0 || num > (argc-3)) {\n        *numkeys = 0;\n        return NULL;\n    }\n\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int)*num);\n\n    *numkeys = num;\n\n    /* Add all key positions for argv[3...n] to keys[] */\n    for (i = 0; i < num; i++) keys[i] = 3+i;\n\n    return keys;\n}\n\n/* Helper function to extract keys from the SORT command.\n *\n * SORT <sort-key> ... STORE <store-key> ...\n *\n * The first argument of SORT is always a key, however a list of options\n * follow in SQL-alike style. Here we parse just the minimum in order to\n * correctly identify keys in the \"STORE\" option. */\nint *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, j, num, *keys, found_store = 0;\n    UNUSED(cmd);\n\n    num = 0;\n    keys = getKeysTempBuffer; /* Alloc 2 places for the worst case. */\n\n    keys[num++] = 1; /* <sort-key> is always present. */\n\n    /* Search for STORE option. By default we consider options to don't\n     * have arguments, so if we find an unknown option name we scan the\n     * next. However there are options with 1 or 2 arguments, so we\n     * provide a list here in order to skip the right number of args. */\n    struct {\n        char *name;\n        int skip;\n    } skiplist[] = {\n        {\"limit\", 2},\n        {\"get\", 1},\n        {\"by\", 1},\n        {NULL, 0} /* End of elements. */\n    };\n\n    for (i = 2; i < argc; i++) {\n        for (j = 0; skiplist[j].name != NULL; j++) {\n            if (!strcasecmp(argv[i]->ptr,skiplist[j].name)) {\n                i += skiplist[j].skip;\n                break;\n            } else if (!strcasecmp(argv[i]->ptr,\"store\") && i+1 < argc) {\n                /* Note: we don't increment \"num\" here and continue the loop\n                 * to be sure to process the *last* \"STORE\" option if multiple\n                 * ones are provided. This is same behavior as SORT. */\n                found_store = 1;\n                keys[num] = i+1; /* <store-key> */\n                break;\n            }\n        }\n    }\n    *numkeys = num + found_store;\n    return keys;\n}\n\nint *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, first, *keys;\n    UNUSED(cmd);\n\n    /* Assume the obvious form. */\n    first = 3;\n    num = 1;\n\n    /* But check for the extended one with the KEYS option. */\n    if (argc > 6) {\n        for (i = 6; i < argc; i++) {\n            if (!strcasecmp(argv[i]->ptr,\"keys\") &&\n                sdslen(argv[3]->ptr) == 0)\n            {\n                first = i+1;\n                num = argc-first;\n                break;\n            }\n        }\n    }\n\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int)*num);\n\n    for (i = 0; i < num; i++) keys[i] = first+i;\n    *numkeys = num;\n    return keys;\n}\n\n/* Helper function to extract keys from following commands:\n * GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]\n *                             [COUNT count] [STORE key] [STOREDIST key]\n * GEORADIUSBYMEMBER key member radius unit ... options ... */\nint *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num, *keys;\n    UNUSED(cmd);\n\n    /* Check for the presence of the stored key in the command */\n    int stored_key = -1;\n    for (i = 5; i < argc; i++) {\n        char *arg = argv[i]->ptr;\n        /* For the case when user specifies both \"store\" and \"storedist\" options, the\n         * second key specified would override the first key. This behavior is kept\n         * the same as in georadiusCommand method.\n         */\n        if ((!strcasecmp(arg, \"store\") || !strcasecmp(arg, \"storedist\")) && ((i+1) < argc)) {\n            stored_key = i+1;\n            i++;\n        }\n    }\n    num = 1 + (stored_key == -1 ? 0 : 1);\n\n    /* Keys in the command come from two places:\n     * argv[1] = key,\n     * argv[5...n] = stored key if present\n     */\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int) * num);\n\n    /* Add all key positions to keys[] */\n    keys[0] = 1;\n    if(num > 1) {\n         keys[1] = stored_key;\n    }\n    *numkeys = num;\n    return keys;\n}\n\n/* LCS ... [KEYS <key1> <key2>] ... */\nint *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)\n{\n    int i;\n    int *keys = getKeysTempBuffer;\n    UNUSED(cmd);\n\n    /* We need to parse the options of the command in order to check for the\n     * \"KEYS\" argument before the \"STRINGS\" argument. */\n    for (i = 1; i < argc; i++) {\n        char *arg = argv[i]->ptr;\n        int moreargs = (argc-1) - i;\n\n        if (!strcasecmp(arg, \"strings\")) {\n            break;\n        } else if (!strcasecmp(arg, \"keys\") && moreargs >= 2) {\n            keys[0] = i+1;\n            keys[1] = i+2;\n            *numkeys = 2;\n            return keys;\n        }\n    }\n    *numkeys = 0;\n    return keys;\n}\n\n/* Helper function to extract keys from memory command.\n * MEMORY USAGE <key> */\nint *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int *keys;\n    UNUSED(cmd);\n\n    if (argc >= 3 && !strcasecmp(argv[1]->ptr,\"usage\")) {\n        keys = getKeysTempBuffer;\n        keys[0] = 2;\n        *numkeys = 1;\n        return keys;\n    }\n    *numkeys = 0;\n    return NULL;\n}\n\n/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]\n *       STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */\nint *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    int i, num = 0, *keys;\n    UNUSED(cmd);\n\n    /* We need to parse the options of the command in order to seek the first\n     * \"STREAMS\" string which is actually the option. This is needed because\n     * \"STREAMS\" could also be the name of the consumer group and even the\n     * name of the stream key. */\n    int streams_pos = -1;\n    for (i = 1; i < argc; i++) {\n        char *arg = argv[i]->ptr;\n        if (!strcasecmp(arg, \"block\")) {\n            i++; /* Skip option argument. */\n        } else if (!strcasecmp(arg, \"count\")) {\n            i++; /* Skip option argument. */\n        } else if (!strcasecmp(arg, \"group\")) {\n            i += 2; /* Skip option argument. */\n        } else if (!strcasecmp(arg, \"noack\")) {\n            /* Nothing to do. */\n        } else if (!strcasecmp(arg, \"streams\")) {\n            streams_pos = i;\n            break;\n        } else {\n            break; /* Syntax error. */\n        }\n    }\n    if (streams_pos != -1) num = argc - streams_pos - 1;\n\n    /* Syntax error. */\n    if (streams_pos == -1 || num == 0 || num % 2 != 0) {\n        *numkeys = 0;\n        return NULL;\n    }\n    num /= 2; /* We have half the keys as there are arguments because\n                 there are also the IDs, one per key. */\n\n    keys = getKeysTempBuffer;\n    if (num>MAX_KEYS_BUFFER)\n        keys = zmalloc(sizeof(int) * num);\n\n    for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i;\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 and in other conditions when we need to\n * understand if we have keys for a given hash slot. */\nvoid slotToKeyUpdateKey(sds key, int add) {\n    size_t keylen = sdslen(key);\n    unsigned int hashslot = keyHashSlot(key,keylen);\n    unsigned char buf[64];\n    unsigned char *indexed = buf;\n\n    server.cluster->slots_keys_count[hashslot] += add ? 1 : -1;\n    if (keylen+2 > 64) indexed = zmalloc(keylen+2);\n    indexed[0] = (hashslot >> 8) & 0xff;\n    indexed[1] = hashslot & 0xff;\n    memcpy(indexed+2,key,keylen);\n    if (add) {\n        raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL,NULL);\n    } else {\n        raxRemove(server.cluster->slots_to_keys,indexed,keylen+2,NULL);\n    }\n    if (indexed != buf) zfree(indexed);\n}\n\nvoid slotToKeyAdd(sds key) {\n    slotToKeyUpdateKey(key,1);\n}\n\nvoid slotToKeyDel(sds key) {\n    slotToKeyUpdateKey(key,0);\n}\n\nvoid slotToKeyFlush(void) {\n    raxFree(server.cluster->slots_to_keys);\n    server.cluster->slots_to_keys = raxNew();\n    memset(server.cluster->slots_keys_count,0,\n           sizeof(server.cluster->slots_keys_count));\n}\n\n/* Pupulate the specified array of objects with keys in the specified slot.\n * New objects are returned to represent keys, it's up to the caller to\n * decrement the reference count to release the keys names. */\nunsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count) {\n    raxIterator iter;\n    int j = 0;\n    unsigned char indexed[2];\n\n    indexed[0] = (hashslot >> 8) & 0xff;\n    indexed[1] = hashslot & 0xff;\n    raxStart(&iter,server.cluster->slots_to_keys);\n    raxSeek(&iter,\">=\",indexed,2);\n    while(count-- && raxNext(&iter)) {\n        if (iter.key[0] != indexed[0] || iter.key[1] != indexed[1]) break;\n        keys[j++] = createStringObject((char*)iter.key+2,iter.key_len-2);\n    }\n    raxStop(&iter);\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    raxIterator iter;\n    int j = 0;\n    unsigned char indexed[2];\n\n    indexed[0] = (hashslot >> 8) & 0xff;\n    indexed[1] = hashslot & 0xff;\n    raxStart(&iter,server.cluster->slots_to_keys);\n    while(server.cluster->slots_keys_count[hashslot]) {\n        raxSeek(&iter,\">=\",indexed,2);\n        raxNext(&iter);\n\n        robj *key = createStringObject((char*)iter.key+2,iter.key_len-2);\n        dbDelete(&server.db[0],key);\n        decrRefCount(key);\n        j++;\n    }\n    raxStop(&iter);\n    return j;\n}\n\nunsigned int countKeysInSlot(unsigned int hashslot) {\n    return server.cluster->slots_keys_count[hashslot];\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.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#include <dlfcn.h>\n\n#ifdef HAVE_BACKTRACE\n#include <execinfo.h>\n#ifndef __OpenBSD__\n#include <ucontext.h>\n#else\ntypedef ucontext_t sigcontext_t;\n#endif\n#include <fcntl.h>\n#include \"bio.h\"\n#include <unistd.h>\n#endif /* HAVE_BACKTRACE */\n\n#ifdef __CYGWIN__\n#ifndef SA_ONSTACK\n#define SA_ONSTACK 0x08000000\n#endif\n#endif\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 xorStringObjectDigest(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 mixStringObjectDigest(unsigned char *digest, robj *o) {\n    o = getDecodedObject(o);\n    mixDigest(digest,o->ptr,sdslen(o->ptr));\n    decrRefCount(o);\n}\n\n/* This function computes the digest of a data structure stored in the\n * object 'o'. It is the core of the DEBUG DIGEST command: when taking the\n * digest of a whole dataset, we take the digest of the key and the value\n * pair, and xor all those together.\n *\n * Note that this function does not reset the initial 'digest' passed, it\n * will continue mixing this object digest to anything that was already\n * present. */\nvoid xorObjectDigest(redisDb *db, robj *keyobj, unsigned char *digest, robj *o) {\n    uint32_t aux = htonl(o->type);\n    mixDigest(digest,&aux,sizeof(aux));\n    long long expiretime = getExpire(db,keyobj);\n    char buf[128];\n\n    /* Save the key and associated value */\n    if (o->type == OBJ_STRING) {\n        mixStringObjectDigest(digest,o);\n    } else if (o->type == OBJ_LIST) {\n        listTypeIterator *li = listTypeInitIterator(o,0,LIST_TAIL);\n        listTypeEntry entry;\n        while(listTypeNext(li,&entry)) {\n            robj *eleobj = listTypeGet(&entry);\n            mixStringObjectDigest(digest,eleobj);\n            decrRefCount(eleobj);\n        }\n        listTypeReleaseIterator(li);\n    } else if (o->type == OBJ_SET) {\n        setTypeIterator *si = setTypeInitIterator(o);\n        sds sdsele;\n        while((sdsele = setTypeNextObject(si)) != NULL) {\n            xorDigest(digest,sdsele,sdslen(sdsele));\n            sdsfree(sdsele);\n        }\n        setTypeReleaseIterator(si);\n    } else if (o->type == OBJ_ZSET) {\n        unsigned char eledigest[20];\n\n        if (o->encoding == OBJ_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            serverAssert(eptr != NULL);\n            sptr = ziplistNext(zl,eptr);\n            serverAssert(sptr != NULL);\n\n            while (eptr != NULL) {\n                serverAssert(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 == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = o->ptr;\n            dictIterator *di = dictGetIterator(zs->dict);\n            dictEntry *de;\n\n            while((de = dictNext(di)) != NULL) {\n                sds sdsele = dictGetKey(de);\n                double *score = dictGetVal(de);\n\n                snprintf(buf,sizeof(buf),\"%.17g\",*score);\n                memset(eledigest,0,20);\n                mixDigest(eledigest,sdsele,sdslen(sdsele));\n                mixDigest(eledigest,buf,strlen(buf));\n                xorDigest(digest,eledigest,20);\n            }\n            dictReleaseIterator(di);\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (o->type == OBJ_HASH) {\n        hashTypeIterator *hi = hashTypeInitIterator(o);\n        while (hashTypeNext(hi) != C_ERR) {\n            unsigned char eledigest[20];\n            sds sdsele;\n\n            memset(eledigest,0,20);\n            sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);\n            mixDigest(eledigest,sdsele,sdslen(sdsele));\n            sdsfree(sdsele);\n            sdsele = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);\n            mixDigest(eledigest,sdsele,sdslen(sdsele));\n            sdsfree(sdsele);\n            xorDigest(digest,eledigest,20);\n        }\n        hashTypeReleaseIterator(hi);\n    } else if (o->type == OBJ_STREAM) {\n        streamIterator si;\n        streamIteratorStart(&si,o->ptr,NULL,NULL,0);\n        streamID id;\n        int64_t numfields;\n\n        while(streamIteratorGetID(&si,&id,&numfields)) {\n            sds itemid = sdscatfmt(sdsempty(),\"%U.%U\",id.ms,id.seq);\n            mixDigest(digest,itemid,sdslen(itemid));\n            sdsfree(itemid);\n\n            while(numfields--) {\n                unsigned char *field, *value;\n                int64_t field_len, value_len;\n                streamIteratorGetField(&si,&field,&value,\n                                           &field_len,&value_len);\n                mixDigest(digest,field,field_len);\n                mixDigest(digest,value,value_len);\n            }\n        }\n        streamIteratorStop(&si);\n    } else if (o->type == OBJ_MODULE) {\n        RedisModuleDigest md;\n        moduleValue *mv = o->ptr;\n        moduleType *mt = mv->type;\n        moduleInitDigestContext(md);\n        if (mt->digest) {\n            mt->digest(&md,mv->value);\n            xorDigest(digest,md.x,sizeof(md.x));\n        }\n    } else {\n        serverPanic(\"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}\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    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 = dictGetSafeIterator(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\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            xorObjectDigest(db,keyobj,digest,o);\n\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\n#ifdef USE_JEMALLOC\nvoid mallctl_int(client *c, robj **argv, int argc) {\n    int ret;\n    /* start with the biggest size (int64), and if that fails, try smaller sizes (int32, bool) */\n    int64_t old = 0, val;\n    if (argc > 1) {\n        long long ll;\n        if (getLongLongFromObjectOrReply(c, argv[1], &ll, NULL) != C_OK)\n            return;\n        val = ll;\n    }\n    size_t sz = sizeof(old);\n    while (sz > 0) {\n        if ((ret=je_mallctl(argv[0]->ptr, &old, &sz, argc > 1? &val: NULL, argc > 1?sz: 0))) {\n            if (ret==EINVAL) {\n                /* size might be wrong, try a smaller one */\n                sz /= 2;\n#if BYTE_ORDER == BIG_ENDIAN\n                val <<= 8*sz;\n#endif\n                continue;\n            }\n            addReplyErrorFormat(c,\"%s\", strerror(ret));\n            return;\n        } else {\n#if BYTE_ORDER == BIG_ENDIAN\n            old >>= 64 - 8*sz;\n#endif\n            addReplyLongLong(c, old);\n            return;\n        }\n    }\n    addReplyErrorFormat(c,\"%s\", strerror(EINVAL));\n}\n\nvoid mallctl_string(client *c, robj **argv, int argc) {\n    int ret;\n    char *old;\n    size_t sz = sizeof(old);\n    /* for strings, it seems we need to first get the old value, before overriding it. */\n    if ((ret=je_mallctl(argv[0]->ptr, &old, &sz, NULL, 0))) {\n        addReplyErrorFormat(c,\"%s\", strerror(ret));\n        return;\n    }\n    addReplyBulkCString(c, old);\n    if(argc > 1)\n        je_mallctl(argv[0]->ptr, NULL, 0, &argv[1]->ptr, sizeof(char*));\n}\n#endif\n\nvoid debugCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ASSERT -- Crash by assertion failed.\",\n\"CHANGE-REPL-ID -- Change the replication IDs of the instance. Dangerous, should be used only for testing the replication subsystem.\",\n\"CRASH-AND-RECOVER <milliseconds> -- Hard crash and restart after <milliseconds> delay.\",\n\"DIGEST -- Output a hex signature representing the current DB content.\",\n\"DIGEST-VALUE <key-1> ... <key-N>-- Output a hex signature of the values of all the specified keys.\",\n\"DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false]\",\n\"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.\",\n\"LOG <message> -- write message to the server log.\",\n\"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.\",\n\"HTSTATS-KEY <key> -- Like htstats but for the hash table stored as key's value.\",\n\"LOADAOF -- Flush the AOF buffers on disk and reload the AOF in memory.\",\n\"LUA-ALWAYS-REPLICATE-COMMANDS <0|1> -- Setting it to 1 makes Lua replication defaulting to replicating single commands, without the script having to enable effects replication.\",\n\"OBJECT <key> -- Show low level info about key and associated value.\",\n\"OOM -- Crash the server simulating an out-of-memory error.\",\n\"PANIC -- Crash the server simulating a panic.\",\n\"POPULATE <count> [prefix] [size] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix.\",\n\"RELOAD [MERGE] [NOFLUSH] [NOSAVE] -- Save the RDB on disk and reload it back in memory. By default it will save the RDB file and load it back. With the NOFLUSH option the current database is not removed before loading the new one, but conficts in keys will kill the server with an exception. When MERGE is used, conflicting keys will be loaded (the key in the loaded RDB file will win). When NOSAVE is used, the server will not save the current dataset in the RDB file before loading. Use DEBUG RELOAD NOSAVE when you want just to load the RDB file you placed in the Redis working directory in order to replace the current dataset in memory. Use DEBUG RELOAD NOSAVE NOFLUSH MERGE when you want to add what is in the current RDB file placed in the Redis current directory, with the current memory content. Use DEBUG RELOAD when you want to verify Redis is able to persist the current dataset in the RDB file, flush the memory content, and load it back.\",\n\"RESTART -- Graceful restart: save config, db, restart.\",\n\"SDSLEN <key> -- Show low level SDS string info representing key and value.\",\n\"SEGFAULT -- Crash the server with sigsegv.\",\n\"SET-ACTIVE-EXPIRE <0|1> -- Setting it to 0 disables expiring keys in background when they are not accessed (otherwise the Redis behavior). Setting it to 1 reenables back the default.\",\n\"AOF-FLUSH-SLEEP <microsec> -- Server will sleep before flushing the AOF, this is used for testing\",\n\"SLEEP <seconds> -- Stop the server for <seconds>. Decimals allowed.\",\n\"STRUCTSIZE -- Return the size of different Redis core C structures.\",\n\"ZIPLIST <key> -- Show low level info about the ziplist encoding.\",\n\"STRINGMATCH-TEST -- Run a fuzz tester against the stringmatchlen() function.\",\n#ifdef USE_JEMALLOC\n\"MALLCTL <key> [<val>] -- Get or set a malloc tunning integer.\",\n\"MALLCTL-STR <key> [<val>] -- Get or set a malloc tunning string.\",\n#endif\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"segfault\")) {\n        *((char*)-1) = 'x';\n    } else if (!strcasecmp(c->argv[1]->ptr,\"panic\")) {\n        serverPanic(\"DEBUG PANIC called at Unix time %ld\", time(NULL));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"restart\") ||\n               !strcasecmp(c->argv[1]->ptr,\"crash-and-recover\"))\n    {\n        long long delay = 0;\n        if (c->argc >= 3) {\n            if (getLongLongFromObjectOrReply(c, c->argv[2], &delay, NULL)\n                != C_OK) return;\n            if (delay < 0) delay = 0;\n        }\n        int flags = !strcasecmp(c->argv[1]->ptr,\"restart\") ?\n            (RESTART_SERVER_GRACEFULLY|RESTART_SERVER_CONFIG_REWRITE) :\n             RESTART_SERVER_NONE;\n        restartServer(flags,delay);\n        addReplyError(c,\"failed to restart the server. Check server logs.\");\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        serverAssertWithInfo(c,c->argv[0],1 == 2);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"log\") && c->argc == 3) {\n        serverLog(LL_WARNING, \"DEBUG LOG: %s\", (char*)c->argv[2]->ptr);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reload\")) {\n        int flush = 1, save = 1;\n        int flags = RDBFLAGS_NONE;\n\n        /* Parse the additional options that modify the RELOAD\n         * behavior. */\n        for (int j = 2; j < c->argc; j++) {\n            char *opt = c->argv[j]->ptr;\n            if (!strcasecmp(opt,\"MERGE\")) {\n                flags |= RDBFLAGS_ALLOW_DUP;\n            } else if (!strcasecmp(opt,\"NOFLUSH\")) {\n                flush = 0;\n            } else if (!strcasecmp(opt,\"NOSAVE\")) {\n                save = 0;\n            } else {\n                addReplyError(c,\"DEBUG RELOAD only supports the \"\n                                \"MERGE, NOFLUSH and NOSAVE options.\");\n                return;\n            }\n        }\n\n        /* The default beahvior is to save the RDB file before loading\n         * it back. */\n        if (save) {\n            rdbSaveInfo rsi, *rsiptr;\n            rsiptr = rdbPopulateSaveInfo(&rsi);\n            if (rdbSave(server.rdb_filename,rsiptr) != C_OK) {\n                addReply(c,shared.err);\n                return;\n            }\n        }\n\n        /* The default behavior is to remove the current dataset from\n         * memory before loading the RDB file, however when MERGE is\n         * used together with NOFLUSH, we are able to merge two datasets. */\n        if (flush) emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);\n\n        protectClient(c);\n        int ret = rdbLoad(server.rdb_filename,NULL,flags);\n        unprotectClient(c);\n        if (ret != C_OK) {\n            addReplyError(c,\"Error trying to load the RDB dump\");\n            return;\n        }\n        serverLog(LL_WARNING,\"DB reloaded by DEBUG RELOAD\");\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"loadaof\")) {\n        if (server.aof_state != AOF_OFF) flushAppendOnlyFile(1);\n        emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);\n        protectClient(c);\n        int ret = loadAppendOnlyFile(server.aof_filename);\n        unprotectClient(c);\n        if (ret != C_OK) {\n            addReply(c,shared.err);\n            return;\n        }\n        server.dirty = 0; /* Prevent AOF / replication */\n        serverLog(LL_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        char extra[138] = {0};\n        if (val->encoding == OBJ_ENCODING_QUICKLIST) {\n            char *nextra = extra;\n            int remaining = sizeof(extra);\n            quicklist *ql = val->ptr;\n            /* Add number of quicklist nodes */\n            int used = snprintf(nextra, remaining, \" ql_nodes:%lu\", ql->len);\n            nextra += used;\n            remaining -= used;\n            /* Add average quicklist fill factor */\n            double avg = (double)ql->count/ql->len;\n            used = snprintf(nextra, remaining, \" ql_avg_node:%.2f\", avg);\n            nextra += used;\n            remaining -= used;\n            /* Add quicklist fill level / max ziplist size */\n            used = snprintf(nextra, remaining, \" ql_ziplist_max:%d\", ql->fill);\n            nextra += used;\n            remaining -= used;\n            /* Add isCompressed? */\n            int compressed = ql->compress != 0;\n            used = snprintf(nextra, remaining, \" ql_compressed:%d\", compressed);\n            nextra += used;\n            remaining -= used;\n            /* Add total uncompressed size */\n            unsigned long sz = 0;\n            for (quicklistNode *node = ql->head; node; node = node->next) {\n                sz += node->sz;\n            }\n            used = snprintf(nextra, remaining, \" ql_uncompressed_size:%lu\", sz);\n            nextra += used;\n            remaining -= used;\n        }\n\n        addReplyStatusFormat(c,\n            \"Value at:%p refcount:%d \"\n            \"encoding:%s serializedlength:%zu \"\n            \"lru:%d lru_seconds_idle:%llu%s\",\n            (void*)val, val->refcount,\n            strenc, rdbSavedObjectLen(val, c->argv[2]),\n            val->lru, estimateObjectIdleTime(val)/1000, extra);\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 != OBJ_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, key_zmalloc: %lld, \"\n                \"val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld\",\n                (long long) sdslen(key),\n                (long long) sdsavail(key),\n                (long long) sdsZmallocSize(key),\n                (long long) sdslen(val->ptr),\n                (long long) sdsavail(val->ptr),\n                (long long) getStringObjectSdsUsedMemory(val));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"ziplist\") && c->argc == 3) {\n        robj *o;\n\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))\n                == NULL) return;\n\n        if (o->encoding != OBJ_ENCODING_ZIPLIST) {\n            addReplyError(c,\"Not an sds encoded string.\");\n        } else {\n            ziplistRepr(o->ptr);\n            addReplyStatus(c,\"Ziplist structure printed on stdout\");\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"populate\") &&\n               c->argc >= 3 && c->argc <= 5) {\n        long keys, j;\n        robj *key, *val;\n        char buf[128];\n\n        if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)\n            return;\n        dictExpand(c->db->dict,keys);\n        for (j = 0; j < keys; j++) {\n            long valsize = 0;\n            snprintf(buf,sizeof(buf),\"%s:%lu\",\n                (c->argc == 3) ? \"key\" : (char*)c->argv[3]->ptr, j);\n            key = createStringObject(buf,strlen(buf));\n            if (c->argc == 5)\n                if (getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK)\n                    return;\n            if (lookupKeyWrite(c->db,key) != NULL) {\n                decrRefCount(key);\n                continue;\n            }\n            snprintf(buf,sizeof(buf),\"value:%lu\",j);\n            if (valsize==0)\n                val = createStringObject(buf,strlen(buf));\n            else {\n                int buflen = strlen(buf);\n                val = createStringObject(NULL,valsize);\n                memcpy(val->ptr, buf, valsize<=buflen? valsize: buflen);\n            }\n            dbAdd(c->db,key,val);\n            signalModifiedKey(c,c->db,key);\n            decrRefCount(key);\n        }\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"digest\") && c->argc == 2) {\n        /* DEBUG DIGEST (form without keys specified) */\n        unsigned char digest[20];\n        sds d = sdsempty();\n\n        computeDatasetDigest(digest);\n        for (int i = 0; i < 20; i++) d = sdscatprintf(d, \"%02x\",digest[i]);\n        addReplyStatus(c,d);\n        sdsfree(d);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"digest-value\") && c->argc >= 2) {\n        /* DEBUG DIGEST-VALUE key key key ... key. */\n        addReplyArrayLen(c,c->argc-2);\n        for (int j = 2; j < c->argc; j++) {\n            unsigned char digest[20];\n            memset(digest,0,20); /* Start with a clean result */\n            robj *o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);\n            if (o) xorObjectDigest(c->db,c->argv[j],digest,o);\n\n            sds d = sdsempty();\n            for (int i = 0; i < 20; i++) d = sdscatprintf(d, \"%02x\",digest[i]);\n            addReplyStatus(c,d);\n            sdsfree(d);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"protocol\") && c->argc == 3) {\n        /* DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|\n         *                 attrib|push|verbatim|true|false] */\n        char *name = c->argv[2]->ptr;\n        if (!strcasecmp(name,\"string\")) {\n            addReplyBulkCString(c,\"Hello World\");\n        } else if (!strcasecmp(name,\"integer\")) {\n            addReplyLongLong(c,12345);\n        } else if (!strcasecmp(name,\"double\")) {\n            addReplyDouble(c,3.14159265359);\n        } else if (!strcasecmp(name,\"bignum\")) {\n            addReplyProto(c,\"(1234567999999999999999999999999999999\\r\\n\",40);\n        } else if (!strcasecmp(name,\"null\")) {\n            addReplyNull(c);\n        } else if (!strcasecmp(name,\"array\")) {\n            addReplyArrayLen(c,3);\n            for (int j = 0; j < 3; j++) addReplyLongLong(c,j);\n        } else if (!strcasecmp(name,\"set\")) {\n            addReplySetLen(c,3);\n            for (int j = 0; j < 3; j++) addReplyLongLong(c,j);\n        } else if (!strcasecmp(name,\"map\")) {\n            addReplyMapLen(c,3);\n            for (int j = 0; j < 3; j++) {\n                addReplyLongLong(c,j);\n                addReplyBool(c, j == 1);\n            }\n        } else if (!strcasecmp(name,\"attrib\")) {\n            addReplyAttributeLen(c,1);\n            addReplyBulkCString(c,\"key-popularity\");\n            addReplyArrayLen(c,2);\n            addReplyBulkCString(c,\"key:123\");\n            addReplyLongLong(c,90);\n            /* Attributes are not real replies, so a well formed reply should\n             * also have a normal reply type after the attribute. */\n            addReplyBulkCString(c,\"Some real reply following the attribute\");\n        } else if (!strcasecmp(name,\"push\")) {\n            addReplyPushLen(c,2);\n            addReplyBulkCString(c,\"server-cpu-usage\");\n            addReplyLongLong(c,42);\n            /* Push replies are not synchronous replies, so we emit also a\n             * normal reply in order for blocking clients just discarding the\n             * push reply, to actually consume the reply and continue. */\n            addReplyBulkCString(c,\"Some real reply following the push reply\");\n        } else if (!strcasecmp(name,\"true\")) {\n            addReplyBool(c,1);\n        } else if (!strcasecmp(name,\"false\")) {\n            addReplyBool(c,0);\n        } else if (!strcasecmp(name,\"verbatim\")) {\n            addReplyVerbatim(c,\"This is a verbatim\\nstring\",25,\"txt\");\n        } else {\n            addReplyError(c,\"Wrong protocol type name. Please use one of the following: string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false\");\n        }\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,\"aof-flush-sleep\") &&\n               c->argc == 3)\n    {\n        server.aof_flush_sleep = atoi(c->argv[2]->ptr);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"lua-always-replicate-commands\") &&\n               c->argc == 3)\n    {\n        server.lua_always_replicate_commands = atoi(c->argv[2]->ptr);\n        addReply(c,shared.ok);\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 if (!strcasecmp(c->argv[1]->ptr,\"structsize\") && c->argc == 2) {\n        sds sizes = sdsempty();\n        sizes = sdscatprintf(sizes,\"bits:%d \",(sizeof(void*) == 8)?64:32);\n        sizes = sdscatprintf(sizes,\"robj:%d \",(int)sizeof(robj));\n        sizes = sdscatprintf(sizes,\"dictentry:%d \",(int)sizeof(dictEntry));\n        sizes = sdscatprintf(sizes,\"sdshdr5:%d \",(int)sizeof(struct sdshdr5));\n        sizes = sdscatprintf(sizes,\"sdshdr8:%d \",(int)sizeof(struct sdshdr8));\n        sizes = sdscatprintf(sizes,\"sdshdr16:%d \",(int)sizeof(struct sdshdr16));\n        sizes = sdscatprintf(sizes,\"sdshdr32:%d \",(int)sizeof(struct sdshdr32));\n        sizes = sdscatprintf(sizes,\"sdshdr64:%d \",(int)sizeof(struct sdshdr64));\n        addReplyBulkSds(c,sizes);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"htstats\") && c->argc == 3) {\n        long dbid;\n        sds stats = sdsempty();\n        char buf[4096];\n\n        if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) {\n            sdsfree(stats);\n            return;\n        }\n        if (dbid < 0 || dbid >= server.dbnum) {\n            sdsfree(stats);\n            addReplyError(c,\"Out of range database\");\n            return;\n        }\n\n        stats = sdscatprintf(stats,\"[Dictionary HT]\\n\");\n        dictGetStats(buf,sizeof(buf),server.db[dbid].dict);\n        stats = sdscat(stats,buf);\n\n        stats = sdscatprintf(stats,\"[Expires HT]\\n\");\n        dictGetStats(buf,sizeof(buf),server.db[dbid].expires);\n        stats = sdscat(stats,buf);\n\n        addReplyVerbatim(c,stats,sdslen(stats),\"txt\");\n        sdsfree(stats);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"htstats-key\") && c->argc == 3) {\n        robj *o;\n        dict *ht = NULL;\n\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))\n                == NULL) return;\n\n        /* Get the hash table reference from the object, if possible. */\n        switch (o->encoding) {\n        case OBJ_ENCODING_SKIPLIST:\n            {\n                zset *zs = o->ptr;\n                ht = zs->dict;\n            }\n            break;\n        case OBJ_ENCODING_HT:\n            ht = o->ptr;\n            break;\n        }\n\n        if (ht == NULL) {\n            addReplyError(c,\"The value stored at the specified key is not \"\n                            \"represented using an hash table\");\n        } else {\n            char buf[4096];\n            dictGetStats(buf,sizeof(buf),ht);\n            addReplyVerbatim(c,buf,strlen(buf),\"txt\");\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"change-repl-id\") && c->argc == 2) {\n        serverLog(LL_WARNING,\"Changing replication IDs after receiving DEBUG change-repl-id\");\n        changeReplicationId();\n        clearReplicationId2();\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"stringmatch-test\") && c->argc == 2)\n    {\n        stringmatchlen_fuzz_test();\n        addReplyStatus(c,\"Apparently Redis did not crash: test passed\");\n#ifdef USE_JEMALLOC\n    } else if(!strcasecmp(c->argv[1]->ptr,\"mallctl\") && c->argc >= 3) {\n        mallctl_int(c, c->argv+2, c->argc-2);\n        return;\n    } else if(!strcasecmp(c->argv[1]->ptr,\"mallctl-str\") && c->argc >= 3) {\n        mallctl_string(c, c->argv+2, c->argc-2);\n        return;\n#endif\n    } else {\n        addReplySubcommandSyntaxError(c);\n        return;\n    }\n}\n\n/* =========================== Crash handling  ============================== */\n\nvoid _serverAssert(const char *estr, const char *file, int line) {\n    bugReportStart();\n    serverLog(LL_WARNING,\"=== ASSERTION FAILED ===\");\n    serverLog(LL_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    serverLog(LL_WARNING,\"(forcing SIGSEGV to print the bug report.)\");\n#endif\n    *((char*)-1) = 'x';\n}\n\nvoid _serverAssertPrintClientInfo(const client *c) {\n    int j;\n    char conninfo[CONN_INFO_LEN];\n\n    bugReportStart();\n    serverLog(LL_WARNING,\"=== ASSERTION FAILED CLIENT CONTEXT ===\");\n    serverLog(LL_WARNING,\"client->flags = %llu\", (unsigned long long) c->flags);\n    serverLog(LL_WARNING,\"client->conn = %s\", connGetInfo(c->conn, conninfo, sizeof(conninfo)));\n    serverLog(LL_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 == OBJ_STRING && sdsEncodedObject(c->argv[j])) {\n            arg = (char*) c->argv[j]->ptr;\n        } else {\n            snprintf(buf,sizeof(buf),\"Object type: %u, encoding: %u\",\n                c->argv[j]->type, c->argv[j]->encoding);\n            arg = buf;\n        }\n        serverLog(LL_WARNING,\"client->argv[%d] = \\\"%s\\\" (refcount: %d)\",\n            j, arg, c->argv[j]->refcount);\n    }\n}\n\nvoid serverLogObjectDebugInfo(const robj *o) {\n    serverLog(LL_WARNING,\"Object type: %d\", o->type);\n    serverLog(LL_WARNING,\"Object encoding: %d\", o->encoding);\n    serverLog(LL_WARNING,\"Object refcount: %d\", o->refcount);\n    if (o->type == OBJ_STRING && sdsEncodedObject(o)) {\n        serverLog(LL_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            serverLog(LL_WARNING,\"Object raw string content: %s\", repr);\n            sdsfree(repr);\n        }\n    } else if (o->type == OBJ_LIST) {\n        serverLog(LL_WARNING,\"List length: %d\", (int) listTypeLength(o));\n    } else if (o->type == OBJ_SET) {\n        serverLog(LL_WARNING,\"Set size: %d\", (int) setTypeSize(o));\n    } else if (o->type == OBJ_HASH) {\n        serverLog(LL_WARNING,\"Hash size: %d\", (int) hashTypeLength(o));\n    } else if (o->type == OBJ_ZSET) {\n        serverLog(LL_WARNING,\"Sorted set size: %d\", (int) zsetLength(o));\n        if (o->encoding == OBJ_ENCODING_SKIPLIST)\n            serverLog(LL_WARNING,\"Skiplist level: %d\", (int) ((const zset*)o->ptr)->zsl->level);\n    } else if (o->type == OBJ_STREAM) {\n        serverLog(LL_WARNING,\"Stream size: %d\", (int) streamLength(o));\n    }\n}\n\nvoid _serverAssertPrintObject(const robj *o) {\n    bugReportStart();\n    serverLog(LL_WARNING,\"=== ASSERTION FAILED OBJECT CONTEXT ===\");\n    serverLogObjectDebugInfo(o);\n}\n\nvoid _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line) {\n    if (c) _serverAssertPrintClientInfo(c);\n    if (o) _serverAssertPrintObject(o);\n    _serverAssert(estr,file,line);\n}\n\nvoid _serverPanic(const char *file, int line, const char *msg, ...) {\n    va_list ap;\n    va_start(ap,msg);\n    char fmtmsg[256];\n    vsnprintf(fmtmsg,sizeof(fmtmsg),msg,ap);\n    va_end(ap);\n\n    bugReportStart();\n    serverLog(LL_WARNING,\"------------------------------------------------\");\n    serverLog(LL_WARNING,\"!!! Software Failure. Press left mouse button to continue\");\n    serverLog(LL_WARNING,\"Guru Meditation: %s #%s:%d\",fmtmsg,file,line);\n#ifdef HAVE_BACKTRACE\n    serverLog(LL_WARNING,\"(forcing SIGSEGV in order to print the stack trace)\");\n#endif\n    serverLog(LL_WARNING,\"------------------------------------------------\");\n    *((char*)-1) = 'x';\n}\n\nvoid bugReportStart(void) {\n    if (server.bug_report_start == 0) {\n        serverLogRaw(LL_WARNING|LL_RAW,\n        \"\\n\\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\\n\");\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__) || defined(__ILP32__)\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    #elif defined(__arm__) /* Linux ARM */\n    return (void*) uc->uc_mcontext.arm_pc;\n    #elif defined(__aarch64__) /* Linux AArch64 */\n    return (void*) uc->uc_mcontext.pc;\n    #endif\n#elif defined(__FreeBSD__)\n    /* FreeBSD */\n    #if defined(__i386__)\n    return (void*) uc->uc_mcontext.mc_eip;\n    #elif defined(__x86_64__)\n    return (void*) uc->uc_mcontext.mc_rip;\n    #endif\n#elif defined(__OpenBSD__)\n    /* OpenBSD */\n    #if defined(__i386__)\n    return (void*) uc->sc_eip;\n    #elif defined(__x86_64__)\n    return (void*) uc->sc_rip;\n    #endif\n#elif defined(__DragonFly__)\n    return (void*) uc->uc_mcontext.mc_rip;\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            serverLog(LL_WARNING, \"(%08lx) -> %08lx\", addr, val);\n        else\n            serverLog(LL_WARNING, \"(%016lx) -> %016lx\", addr, val);\n    }\n}\n\nvoid logRegisters(ucontext_t *uc) {\n    serverLog(LL_WARNING|LL_RAW, \"\\n------ REGISTERS ------\\n\");\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    serverLog(LL_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    serverLog(LL_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__) || defined(__ILP32__)\n    serverLog(LL_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    serverLog(LL_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    #elif defined(__aarch64__) /* Linux AArch64 */\n    serverLog(LL_WARNING,\n\t      \"\\n\"\n\t      \"X18:%016lx X19:%016lx\\nX20:%016lx X21:%016lx\\n\"\n\t      \"X22:%016lx X23:%016lx\\nX24:%016lx X25:%016lx\\n\"\n\t      \"X26:%016lx X27:%016lx\\nX28:%016lx X29:%016lx\\n\"\n\t      \"X30:%016lx\\n\"\n\t      \"pc:%016lx sp:%016lx\\npstate:%016lx fault_address:%016lx\\n\",\n\t      (unsigned long) uc->uc_mcontext.regs[18],\n\t      (unsigned long) uc->uc_mcontext.regs[19],\n\t      (unsigned long) uc->uc_mcontext.regs[20],\n\t      (unsigned long) uc->uc_mcontext.regs[21],\n\t      (unsigned long) uc->uc_mcontext.regs[22],\n\t      (unsigned long) uc->uc_mcontext.regs[23],\n\t      (unsigned long) uc->uc_mcontext.regs[24],\n\t      (unsigned long) uc->uc_mcontext.regs[25],\n\t      (unsigned long) uc->uc_mcontext.regs[26],\n\t      (unsigned long) uc->uc_mcontext.regs[27],\n\t      (unsigned long) uc->uc_mcontext.regs[28],\n\t      (unsigned long) uc->uc_mcontext.regs[29],\n\t      (unsigned long) uc->uc_mcontext.regs[30],\n\t      (unsigned long) uc->uc_mcontext.pc,\n\t      (unsigned long) uc->uc_mcontext.sp,\n\t      (unsigned long) uc->uc_mcontext.pstate,\n\t      (unsigned long) uc->uc_mcontext.fault_address\n\t\t      );\n\t      logStackContent((void**)uc->uc_mcontext.sp);\n    #elif defined(__arm__) /* Linux ARM */\n    serverLog(LL_WARNING,\n\t      \"\\n\"\n\t      \"R10:%016lx R9 :%016lx\\nR8 :%016lx R7 :%016lx\\n\"\n\t      \"R6 :%016lx R5 :%016lx\\nR4 :%016lx R3 :%016lx\\n\"\n\t      \"R2 :%016lx R1 :%016lx\\nR0 :%016lx EC :%016lx\\n\"\n\t      \"fp: %016lx ip:%016lx\\n\",\n\t      \"pc:%016lx sp:%016lx\\ncpsr:%016lx fault_address:%016lx\\n\",\n\t      (unsigned long) uc->uc_mcontext.arm_r10,\n\t      (unsigned long) uc->uc_mcontext.arm_r9,\n\t      (unsigned long) uc->uc_mcontext.arm_r8,\n\t      (unsigned long) uc->uc_mcontext.arm_r7,\n\t      (unsigned long) uc->uc_mcontext.arm_r6,\n\t      (unsigned long) uc->uc_mcontext.arm_r5,\n\t      (unsigned long) uc->uc_mcontext.arm_r4,\n\t      (unsigned long) uc->uc_mcontext.arm_r3,\n\t      (unsigned long) uc->uc_mcontext.arm_r2,\n\t      (unsigned long) uc->uc_mcontext.arm_r1,\n\t      (unsigned long) uc->uc_mcontext.arm_r0,\n\t      (unsigned long) uc->uc_mcontext.error_code,\n\t      (unsigned long) uc->uc_mcontext.arm_fp,\n\t      (unsigned long) uc->uc_mcontext.arm_ip,\n\t      (unsigned long) uc->uc_mcontext.arm_pc,\n\t      (unsigned long) uc->uc_mcontext.arm_sp,\n\t      (unsigned long) uc->uc_mcontext.arm_cpsr,\n\t      (unsigned long) uc->uc_mcontext.fault_address\n\t\t      );\n\t      logStackContent((void**)uc->uc_mcontext.arm_sp);\n    #endif\n#elif defined(__FreeBSD__)\n    #if defined(__x86_64__)\n    serverLog(LL_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.mc_rax,\n        (unsigned long) uc->uc_mcontext.mc_rbx,\n        (unsigned long) uc->uc_mcontext.mc_rcx,\n        (unsigned long) uc->uc_mcontext.mc_rdx,\n        (unsigned long) uc->uc_mcontext.mc_rdi,\n        (unsigned long) uc->uc_mcontext.mc_rsi,\n        (unsigned long) uc->uc_mcontext.mc_rbp,\n        (unsigned long) uc->uc_mcontext.mc_rsp,\n        (unsigned long) uc->uc_mcontext.mc_r8,\n        (unsigned long) uc->uc_mcontext.mc_r9,\n        (unsigned long) uc->uc_mcontext.mc_r10,\n        (unsigned long) uc->uc_mcontext.mc_r11,\n        (unsigned long) uc->uc_mcontext.mc_r12,\n        (unsigned long) uc->uc_mcontext.mc_r13,\n        (unsigned long) uc->uc_mcontext.mc_r14,\n        (unsigned long) uc->uc_mcontext.mc_r15,\n        (unsigned long) uc->uc_mcontext.mc_rip,\n        (unsigned long) uc->uc_mcontext.mc_rflags,\n        (unsigned long) uc->uc_mcontext.mc_cs\n    );\n    logStackContent((void**)uc->uc_mcontext.mc_rsp);\n    #elif defined(__i386__)\n    serverLog(LL_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.mc_eax,\n        (unsigned long) uc->uc_mcontext.mc_ebx,\n        (unsigned long) uc->uc_mcontext.mc_ebx,\n        (unsigned long) uc->uc_mcontext.mc_edx,\n        (unsigned long) uc->uc_mcontext.mc_edi,\n        (unsigned long) uc->uc_mcontext.mc_esi,\n        (unsigned long) uc->uc_mcontext.mc_ebp,\n        (unsigned long) uc->uc_mcontext.mc_esp,\n        (unsigned long) uc->uc_mcontext.mc_ss,\n        (unsigned long) uc->uc_mcontext.mc_eflags,\n        (unsigned long) uc->uc_mcontext.mc_eip,\n        (unsigned long) uc->uc_mcontext.mc_cs,\n        (unsigned long) uc->uc_mcontext.mc_es,\n        (unsigned long) uc->uc_mcontext.mc_fs,\n        (unsigned long) uc->uc_mcontext.mc_gs\n    );\n    logStackContent((void**)uc->uc_mcontext.mc_esp);\n    #endif\n#elif defined(__OpenBSD__)\n    #if defined(__x86_64__)\n    serverLog(LL_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->sc_rax,\n        (unsigned long) uc->sc_rbx,\n        (unsigned long) uc->sc_rcx,\n        (unsigned long) uc->sc_rdx,\n        (unsigned long) uc->sc_rdi,\n        (unsigned long) uc->sc_rsi,\n        (unsigned long) uc->sc_rbp,\n        (unsigned long) uc->sc_rsp,\n        (unsigned long) uc->sc_r8,\n        (unsigned long) uc->sc_r9,\n        (unsigned long) uc->sc_r10,\n        (unsigned long) uc->sc_r11,\n        (unsigned long) uc->sc_r12,\n        (unsigned long) uc->sc_r13,\n        (unsigned long) uc->sc_r14,\n        (unsigned long) uc->sc_r15,\n        (unsigned long) uc->sc_rip,\n        (unsigned long) uc->sc_rflags,\n        (unsigned long) uc->sc_cs\n    );\n    logStackContent((void**)uc->sc_rsp);\n    #elif defined(__i386__)\n    serverLog(LL_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->sc_eax,\n        (unsigned long) uc->sc_ebx,\n        (unsigned long) uc->sc_ebx,\n        (unsigned long) uc->sc_edx,\n        (unsigned long) uc->sc_edi,\n        (unsigned long) uc->sc_esi,\n        (unsigned long) uc->sc_ebp,\n        (unsigned long) uc->sc_esp,\n        (unsigned long) uc->sc_ss,\n        (unsigned long) uc->sc_eflags,\n        (unsigned long) uc->sc_eip,\n        (unsigned long) uc->sc_cs,\n        (unsigned long) uc->sc_es,\n        (unsigned long) uc->sc_fs,\n        (unsigned long) uc->sc_gs\n    );\n    logStackContent((void**)uc->sc_esp);\n    #endif\n#elif defined(__DragonFly__)\n    serverLog(LL_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.mc_rax,\n        (unsigned long) uc->uc_mcontext.mc_rbx,\n        (unsigned long) uc->uc_mcontext.mc_rcx,\n        (unsigned long) uc->uc_mcontext.mc_rdx,\n        (unsigned long) uc->uc_mcontext.mc_rdi,\n        (unsigned long) uc->uc_mcontext.mc_rsi,\n        (unsigned long) uc->uc_mcontext.mc_rbp,\n        (unsigned long) uc->uc_mcontext.mc_rsp,\n        (unsigned long) uc->uc_mcontext.mc_r8,\n        (unsigned long) uc->uc_mcontext.mc_r9,\n        (unsigned long) uc->uc_mcontext.mc_r10,\n        (unsigned long) uc->uc_mcontext.mc_r11,\n        (unsigned long) uc->uc_mcontext.mc_r12,\n        (unsigned long) uc->uc_mcontext.mc_r13,\n        (unsigned long) uc->uc_mcontext.mc_r14,\n        (unsigned long) uc->uc_mcontext.mc_r15,\n        (unsigned long) uc->uc_mcontext.mc_rip,\n        (unsigned long) uc->uc_mcontext.mc_rflags,\n        (unsigned long) uc->uc_mcontext.mc_cs\n    );\n    logStackContent((void**)uc->uc_mcontext.mc_rsp);\n#else\n    serverLog(LL_WARNING,\n        \"  Dumping of registers not supported for this OS/arch\");\n#endif\n}\n\n/* Return a file descriptor to write directly to the Redis log with the\n * write(2) syscall, that can be used in critical sections of the code\n * where the rest of Redis can't be trusted (for example during the memory\n * test) or when an API call requires a raw fd.\n *\n * Close it with closeDirectLogFiledes(). */\nint openDirectLogFiledes(void) {\n    int log_to_stdout = server.logfile[0] == '\\0';\n    int fd = log_to_stdout ?\n        STDOUT_FILENO :\n        open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);\n    return fd;\n}\n\n/* Used to close what closeDirectLogFiledes() returns. */\nvoid closeDirectLogFiledes(int fd) {\n    int log_to_stdout = server.logfile[0] == '\\0';\n    if (!log_to_stdout) close(fd);\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[101];\n    int trace_size = 0, fd = openDirectLogFiledes();\n\n    if (fd == -1) return; /* If we can't log there is anything to do. */\n\n    /* Generate the stack trace */\n    trace_size = backtrace(trace+1, 100);\n\n    if (getMcontextEip(uc) != NULL) {\n        char *msg1 = \"EIP:\\n\";\n        char *msg2 = \"\\nBacktrace:\\n\";\n        if (write(fd,msg1,strlen(msg1)) == -1) {/* Avoid warning. */};\n        trace[0] = getMcontextEip(uc);\n        backtrace_symbols_fd(trace, 1, fd);\n        if (write(fd,msg2,strlen(msg2)) == -1) {/* Avoid warning. */};\n    }\n\n    /* Write symbols to log file */\n    backtrace_symbols_fd(trace+1, trace_size, fd);\n\n    /* Cleanup */\n    closeDirectLogFiledes(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    client *cc = server.current_client;\n    sds client;\n    int j;\n\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ CURRENT CLIENT INFO ------\\n\");\n    client = catClientInfoString(sdsempty(),cc);\n    serverLog(LL_WARNING|LL_RAW,\"%s\\n\", client);\n    sdsfree(client);\n    for (j = 0; j < cc->argc; j++) {\n        robj *decoded;\n\n        decoded = getDecodedObject(cc->argv[j]);\n        serverLog(LL_WARNING|LL_RAW,\"argv[%d]: '%s'\\n\", j,\n            (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            serverLog(LL_WARNING,\"key '%s' found in DB containing the following object:\", (char*)key->ptr);\n            serverLogObjectDebugInfo(val);\n        }\n        decrRefCount(key);\n    }\n}\n\n#if defined(HAVE_PROC_MAPS)\n\n#define MEMTEST_MAX_REGIONS 128\n\n/* A non destructive memory test executed during segfauls. */\nint memtest_test_linux_anonymous_maps(void) {\n    FILE *fp;\n    char line[1024];\n    char logbuf[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\n    int fd = openDirectLogFiledes();\n    if (!fd) return 0;\n\n    fp = fopen(\"/proc/self/maps\",\"r\");\n    if (!fp) return 0;\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        snprintf(logbuf,sizeof(logbuf),\n            \"*** Preparing to test memory region %lx (%lu bytes)\\n\",\n                (unsigned long) start_vect[regions],\n                (unsigned long) size_vect[regions]);\n        if (write(fd,logbuf,strlen(logbuf)) == -1) { /* Nothing to do. */ }\n        regions++;\n    }\n\n    int errors = 0;\n    for (j = 0; j < regions; j++) {\n        if (write(fd,\".\",1) == -1) { /* Nothing to do. */ }\n        errors += memtest_preserving_test((void*)start_vect[j],size_vect[j],1);\n        if (write(fd, errors ? \"E\" : \"O\",1) == -1) { /* Nothing to do. */ }\n    }\n    if (write(fd,\"\\n\",1) == -1) { /* Nothing to do. */ }\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    closeDirectLogFiledes(fd);\n    return errors;\n}\n#endif\n\n/* Scans the (assumed) x86 code starting at addr, for a max of `len`\n * bytes, searching for E8 (callq) opcodes, and dumping the symbols\n * and the call offset if they appear to be valid. */\nvoid dumpX86Calls(void *addr, size_t len) {\n    size_t j;\n    unsigned char *p = addr;\n    Dl_info info;\n    /* Hash table to best-effort avoid printing the same symbol\n     * multiple times. */\n    unsigned long ht[256] = {0};\n\n    if (len < 5) return;\n    for (j = 0; j < len-4; j++) {\n        if (p[j] != 0xE8) continue; /* Not an E8 CALL opcode. */\n        unsigned long target = (unsigned long)addr+j+5;\n        target += *((int32_t*)(p+j+1));\n        if (dladdr((void*)target, &info) != 0 && info.dli_sname != NULL) {\n            if (ht[target&0xff] != target) {\n                printf(\"Function at 0x%lx is %s\\n\",target,info.dli_sname);\n                ht[target&0xff] = target;\n            }\n            j += 4; /* Skip the 32 bit immediate. */\n        }\n    }\n}\n\nvoid sigsegvHandler(int sig, siginfo_t *info, void *secret) {\n    ucontext_t *uc = (ucontext_t*) secret;\n    void *eip = getMcontextEip(uc);\n    sds infostring, clients;\n    struct sigaction act;\n    UNUSED(info);\n\n    bugReportStart();\n    serverLog(LL_WARNING,\n        \"Redis %s crashed by signal: %d\", REDIS_VERSION, sig);\n    if (eip != NULL) {\n        serverLog(LL_WARNING,\n        \"Crashed running the instruction at: %p\", eip);\n    }\n    if (sig == SIGSEGV || sig == SIGBUS) {\n        serverLog(LL_WARNING,\n        \"Accessing address: %p\", (void*)info->si_addr);\n    }\n    serverLog(LL_WARNING,\n        \"Failed assertion: %s (%s:%d)\", server.assert_failed,\n                        server.assert_file, server.assert_line);\n\n    /* Log the stack trace */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ STACK TRACE ------\\n\");\n    logStackTrace(uc);\n\n    /* Log INFO and CLIENT LIST */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ INFO OUTPUT ------\\n\");\n    infostring = genRedisInfoString(\"all\");\n    serverLogRaw(LL_WARNING|LL_RAW, infostring);\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ CLIENT LIST OUTPUT ------\\n\");\n    clients = getAllClientsInfoString(-1);\n    serverLogRaw(LL_WARNING|LL_RAW, 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    /* Log Modules INFO */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ MODULES INFO OUTPUT ------\\n\");\n    infostring = modulesCollectInfo(sdsempty(), NULL, 1, 0);\n    serverLogRaw(LL_WARNING|LL_RAW, infostring);\n    sdsfree(infostring);\n\n#if defined(HAVE_PROC_MAPS)\n    /* Test memory */\n    serverLogRaw(LL_WARNING|LL_RAW, \"\\n------ FAST MEMORY TEST ------\\n\");\n    bioKillThreads();\n    if (memtest_test_linux_anonymous_maps()) {\n        serverLogRaw(LL_WARNING|LL_RAW,\n            \"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\\n\");\n    } else {\n        serverLogRaw(LL_WARNING|LL_RAW,\n            \"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.\\n\");\n    }\n#endif\n\n    if (eip != NULL) {\n        Dl_info info;\n        if (dladdr(eip, &info) != 0) {\n            serverLog(LL_WARNING|LL_RAW,\n                \"\\n------ DUMPING CODE AROUND EIP ------\\n\"\n                \"Symbol: %s (base: %p)\\n\"\n                \"Module: %s (base %p)\\n\"\n                \"$ xxd -r -p /tmp/dump.hex /tmp/dump.bin\\n\"\n                \"$ objdump --adjust-vma=%p -D -b binary -m i386:x86-64 /tmp/dump.bin\\n\"\n                \"------\\n\",\n                info.dli_sname, info.dli_saddr, info.dli_fname, info.dli_fbase,\n                info.dli_saddr);\n            size_t len = (long)eip - (long)info.dli_saddr;\n            unsigned long sz = sysconf(_SC_PAGESIZE);\n            if (len < 1<<13) { /* we don't have functions over 8k (verified) */\n                /* Find the address of the next page, which is our \"safety\"\n                 * limit when dumping. Then try to dump just 128 bytes more\n                 * than EIP if there is room, or stop sooner. */\n                unsigned long next = ((unsigned long)eip + sz) & ~(sz-1);\n                unsigned long end = (unsigned long)eip + 128;\n                if (end > next) end = next;\n                len = end - (unsigned long)info.dli_saddr;\n                serverLogHexDump(LL_WARNING, \"dump of function\",\n                    info.dli_saddr ,len);\n                dumpX86Calls(info.dli_saddr,len);\n            }\n        }\n    }\n\n    serverLogRaw(LL_WARNING|LL_RAW,\n\"\\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\\n\\n\"\n\"       Please report the crash by 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 verify it.\\n\\n\"\n);\n\n    /* free(messages); Don't call free() with possibly corrupted memory. */\n    if (server.daemonize && server.supervised == 0) 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 serverLogHexDump(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    serverLog(level,\"%s (hexdump of %zu bytes):\", descr, len);\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            serverLogRaw(level|LL_RAW,buf);\n            b = buf;\n        }\n    }\n    serverLogRaw(level|LL_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#else\n    (void)secret;\n#endif\n    UNUSED(info);\n    UNUSED(sig);\n\n    serverLogFromHandler(LL_WARNING,\"\\n--- WATCHDOG TIMER EXPIRED ---\");\n#ifdef HAVE_BACKTRACE\n    logStackTrace(uc);\n#else\n    serverLogFromHandler(LL_WARNING,\"Sorry: no support for backtrace().\");\n#endif\n    serverLogFromHandler(LL_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_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": "Chapter07/redis-6.0.3/src/debugmacro.h",
    "content": "/* This file contains debugging macros to be used when investigating issues.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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#define D(...)                                                               \\\n    do {                                                                     \\\n        FILE *fp = fopen(\"/tmp/log.txt\",\"a\");                                \\\n        fprintf(fp,\"%s:%s:%d:\\t\", __FILE__, __func__, __LINE__);             \\\n        fprintf(fp,__VA_ARGS__);                                             \\\n        fprintf(fp,\"\\n\");                                                    \\\n        fclose(fp);                                                          \\\n    } while (0);\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/defrag.c",
    "content": "/* \n * Active memory defragmentation\n * Try to find key / value allocations that need to be re-allocated in order \n * to reduce external fragmentation.\n * We do that by scanning the keyspace and for each pointer we have, we can try to\n * ask the allocator if moving it to a new address will help reduce fragmentation.\n *\n * Copyright (c) 2020, Oran Agra\n * Copyright (c) 2020, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"server.h\"\n#include <time.h>\n#include <assert.h>\n#include <stddef.h>\n\n#ifdef HAVE_DEFRAG\n\n/* this method was added to jemalloc in order to help us understand which\n * pointers are worthwhile moving and which aren't */\nint je_get_defrag_hint(void* ptr, int *bin_util, int *run_util);\n\n/* forward declarations*/\nvoid defragDictBucketCallback(void *privdata, dictEntry **bucketref);\ndictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged);\n\n/* Defrag helper for generic allocations.\n *\n * returns NULL in case the allocatoin wasn't moved.\n * when it returns a non-null value, the old pointer was already released\n * and should NOT be accessed. */\nvoid* activeDefragAlloc(void *ptr) {\n    int bin_util, run_util;\n    size_t size;\n    void *newptr;\n    if(!je_get_defrag_hint(ptr, &bin_util, &run_util)) {\n        server.stat_active_defrag_misses++;\n        return NULL;\n    }\n    /* if this run is more utilized than the average utilization in this bin\n     * (or it is full), skip it. This will eventually move all the allocations\n     * from relatively empty runs into relatively full runs. */\n    if (run_util > bin_util || run_util == 1<<16) {\n        server.stat_active_defrag_misses++;\n        return NULL;\n    }\n    /* move this allocation to a new allocation.\n     * make sure not to use the thread cache. so that we don't get back the same\n     * pointers we try to free */\n    size = zmalloc_size(ptr);\n    newptr = zmalloc_no_tcache(size);\n    memcpy(newptr, ptr, size);\n    zfree_no_tcache(ptr);\n    return newptr;\n}\n\n/*Defrag helper for sds strings\n *\n * returns NULL in case the allocatoin wasn't moved.\n * when it returns a non-null value, the old pointer was already released\n * and should NOT be accessed. */\nsds activeDefragSds(sds sdsptr) {\n    void* ptr = sdsAllocPtr(sdsptr);\n    void* newptr = activeDefragAlloc(ptr);\n    if (newptr) {\n        size_t offset = sdsptr - (char*)ptr;\n        sdsptr = (char*)newptr + offset;\n        return sdsptr;\n    }\n    return NULL;\n}\n\n/* Defrag helper for robj and/or string objects\n *\n * returns NULL in case the allocatoin wasn't moved.\n * when it returns a non-null value, the old pointer was already released\n * and should NOT be accessed. */\nrobj *activeDefragStringOb(robj* ob, long *defragged) {\n    robj *ret = NULL;\n    if (ob->refcount!=1)\n        return NULL;\n\n    /* try to defrag robj (only if not an EMBSTR type (handled below). */\n    if (ob->type!=OBJ_STRING || ob->encoding!=OBJ_ENCODING_EMBSTR) {\n        if ((ret = activeDefragAlloc(ob))) {\n            ob = ret;\n            (*defragged)++;\n        }\n    }\n\n    /* try to defrag string object */\n    if (ob->type == OBJ_STRING) {\n        if(ob->encoding==OBJ_ENCODING_RAW) {\n            sds newsds = activeDefragSds((sds)ob->ptr);\n            if (newsds) {\n                ob->ptr = newsds;\n                (*defragged)++;\n            }\n        } else if (ob->encoding==OBJ_ENCODING_EMBSTR) {\n            /* The sds is embedded in the object allocation, calculate the\n             * offset and update the pointer in the new allocation. */\n            long ofs = (intptr_t)ob->ptr - (intptr_t)ob;\n            if ((ret = activeDefragAlloc(ob))) {\n                ret->ptr = (void*)((intptr_t)ret + ofs);\n                (*defragged)++;\n            }\n        } else if (ob->encoding!=OBJ_ENCODING_INT) {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    return ret;\n}\n\n/* Defrag helper for dictEntries to be used during dict iteration (called on\n * each step). Teturns a stat of how many pointers were moved. */\nlong dictIterDefragEntry(dictIterator *iter) {\n    /* This function is a little bit dirty since it messes with the internals\n     * of the dict and it's iterator, but the benefit is that it is very easy\n     * to use, and require no other chagnes in the dict. */\n    long defragged = 0;\n    dictht *ht;\n    /* Handle the next entry (if there is one), and update the pointer in the\n     * current entry. */\n    if (iter->nextEntry) {\n        dictEntry *newde = activeDefragAlloc(iter->nextEntry);\n        if (newde) {\n            defragged++;\n            iter->nextEntry = newde;\n            iter->entry->next = newde;\n        }\n    }\n    /* handle the case of the first entry in the hash bucket. */\n    ht = &iter->d->ht[iter->table];\n    if (ht->table[iter->index] == iter->entry) {\n        dictEntry *newde = activeDefragAlloc(iter->entry);\n        if (newde) {\n            iter->entry = newde;\n            ht->table[iter->index] = newde;\n            defragged++;\n        }\n    }\n    return defragged;\n}\n\n/* Defrag helper for dict main allocations (dict struct, and hash tables).\n * receives a pointer to the dict* and implicitly updates it when the dict\n * struct itself was moved. Returns a stat of how many pointers were moved. */\nlong dictDefragTables(dict* d) {\n    dictEntry **newtable;\n    long defragged = 0;\n    /* handle the first hash table */\n    newtable = activeDefragAlloc(d->ht[0].table);\n    if (newtable)\n        defragged++, d->ht[0].table = newtable;\n    /* handle the second hash table */\n    if (d->ht[1].table) {\n        newtable = activeDefragAlloc(d->ht[1].table);\n        if (newtable)\n            defragged++, d->ht[1].table = newtable;\n    }\n    return defragged;\n}\n\n/* Internal function used by zslDefrag */\nvoid zslUpdateNode(zskiplist *zsl, zskiplistNode *oldnode, zskiplistNode *newnode, zskiplistNode **update) {\n    int i;\n    for (i = 0; i < zsl->level; i++) {\n        if (update[i]->level[i].forward == oldnode)\n            update[i]->level[i].forward = newnode;\n    }\n    serverAssert(zsl->header!=oldnode);\n    if (newnode->level[0].forward) {\n        serverAssert(newnode->level[0].forward->backward==oldnode);\n        newnode->level[0].forward->backward = newnode;\n    } else {\n        serverAssert(zsl->tail==oldnode);\n        zsl->tail = newnode;\n    }\n}\n\n/* Defrag helper for sorted set.\n * Update the robj pointer, defrag the skiplist struct and return the new score\n * reference. We may not access oldele pointer (not even the pointer stored in\n * the skiplist), as it was already freed. Newele may be null, in which case we\n * only need to defrag the skiplist, but not update the obj pointer.\n * When return value is non-NULL, it is the score reference that must be updated\n * in the dict record. */\ndouble *zslDefrag(zskiplist *zsl, double score, sds oldele, sds newele) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x, *newx;\n    int i;\n    sds ele = newele? newele: oldele;\n\n    /* find the skiplist node referring to the object that was moved,\n     * and all pointers that need to be updated if we'll end up moving the skiplist node. */\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            x->level[i].forward->ele != oldele && /* make sure not to access the\n                                                     ->obj pointer if it matches\n                                                     oldele */\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                sdscmp(x->level[i].forward->ele,ele) < 0)))\n            x = x->level[i].forward;\n        update[i] = x;\n    }\n\n    /* update the robj pointer inside the skip list record. */\n    x = x->level[0].forward;\n    serverAssert(x && score == x->score && x->ele==oldele);\n    if (newele)\n        x->ele = newele;\n\n    /* try to defrag the skiplist record itself */\n    newx = activeDefragAlloc(x);\n    if (newx) {\n        zslUpdateNode(zsl, x, newx, update);\n        return &newx->score;\n    }\n    return NULL;\n}\n\n/* Defrag helpler for sorted set.\n * Defrag a single dict entry key name, and corresponding skiplist struct */\nlong activeDefragZsetEntry(zset *zs, dictEntry *de) {\n    sds newsds;\n    double* newscore;\n    long defragged = 0;\n    sds sdsele = dictGetKey(de);\n    if ((newsds = activeDefragSds(sdsele)))\n        defragged++, de->key = newsds;\n    newscore = zslDefrag(zs->zsl, *(double*)dictGetVal(de), sdsele, newsds);\n    if (newscore) {\n        dictSetVal(zs->dict, de, newscore);\n        defragged++;\n    }\n    return defragged;\n}\n\n#define DEFRAG_SDS_DICT_NO_VAL 0\n#define DEFRAG_SDS_DICT_VAL_IS_SDS 1\n#define DEFRAG_SDS_DICT_VAL_IS_STROB 2\n#define DEFRAG_SDS_DICT_VAL_VOID_PTR 3\n\n/* Defrag a dict with sds key and optional value (either ptr, sds or robj string) */\nlong activeDefragSdsDict(dict* d, int val_type) {\n    dictIterator *di;\n    dictEntry *de;\n    long defragged = 0;\n    di = dictGetIterator(d);\n    while((de = dictNext(di)) != NULL) {\n        sds sdsele = dictGetKey(de), newsds;\n        if ((newsds = activeDefragSds(sdsele)))\n            de->key = newsds, defragged++;\n        /* defrag the value */\n        if (val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) {\n            sdsele = dictGetVal(de);\n            if ((newsds = activeDefragSds(sdsele)))\n                de->v.val = newsds, defragged++;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) {\n            robj *newele, *ele = dictGetVal(de);\n            if ((newele = activeDefragStringOb(ele, &defragged)))\n                de->v.val = newele;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) {\n            void *newptr, *ptr = dictGetVal(de);\n            if ((newptr = activeDefragAlloc(ptr)))\n                de->v.val = newptr, defragged++;\n        }\n        defragged += dictIterDefragEntry(di);\n    }\n    dictReleaseIterator(di);\n    return defragged;\n}\n\n/* Defrag a list of ptr, sds or robj string values */\nlong activeDefragList(list *l, int val_type) {\n    long defragged = 0;\n    listNode *ln, *newln;\n    for (ln = l->head; ln; ln = ln->next) {\n        if ((newln = activeDefragAlloc(ln))) {\n            if (newln->prev)\n                newln->prev->next = newln;\n            else\n                l->head = newln;\n            if (newln->next)\n                newln->next->prev = newln;\n            else\n                l->tail = newln;\n            ln = newln;\n            defragged++;\n        }\n        if (val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) {\n            sds newsds, sdsele = ln->value;\n            if ((newsds = activeDefragSds(sdsele)))\n                ln->value = newsds, defragged++;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) {\n            robj *newele, *ele = ln->value;\n            if ((newele = activeDefragStringOb(ele, &defragged)))\n                ln->value = newele;\n        } else if (val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) {\n            void *newptr, *ptr = ln->value;\n            if ((newptr = activeDefragAlloc(ptr)))\n                ln->value = newptr, defragged++;\n        }\n    }\n    return defragged;\n}\n\n/* Defrag a list of sds values and a dict with the same sds keys */\nlong activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {\n    long defragged = 0;\n    sds newsds, sdsele;\n    listNode *ln, *newln;\n    dictIterator *di;\n    dictEntry *de;\n    /* Defrag the list and it's sds values */\n    for (ln = l->head; ln; ln = ln->next) {\n        if ((newln = activeDefragAlloc(ln))) {\n            if (newln->prev)\n                newln->prev->next = newln;\n            else\n                l->head = newln;\n            if (newln->next)\n                newln->next->prev = newln;\n            else\n                l->tail = newln;\n            ln = newln;\n            defragged++;\n        }\n        sdsele = ln->value;\n        if ((newsds = activeDefragSds(sdsele))) {\n            /* When defragging an sds value, we need to update the dict key */\n            uint64_t hash = dictGetHash(d, sdsele);\n            replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);\n            ln->value = newsds;\n            defragged++;\n        }\n    }\n\n    /* Defrag the dict values (keys were already handled) */\n    di = dictGetIterator(d);\n    while((de = dictNext(di)) != NULL) {\n        if (dict_val_type == DEFRAG_SDS_DICT_VAL_IS_SDS) {\n            sds newsds, sdsele = dictGetVal(de);\n            if ((newsds = activeDefragSds(sdsele)))\n                de->v.val = newsds, defragged++;\n        } else if (dict_val_type == DEFRAG_SDS_DICT_VAL_IS_STROB) {\n            robj *newele, *ele = dictGetVal(de);\n            if ((newele = activeDefragStringOb(ele, &defragged)))\n                de->v.val = newele, defragged++;\n        } else if (dict_val_type == DEFRAG_SDS_DICT_VAL_VOID_PTR) {\n            void *newptr, *ptr = dictGetVal(de);\n            if ((newptr = activeDefragAlloc(ptr)))\n                ln->value = newptr, defragged++;\n        }\n        defragged += dictIterDefragEntry(di);\n    }\n    dictReleaseIterator(di);\n\n    return defragged;\n}\n\n/* Utility function that replaces an old key pointer in the dictionary with a\n * new pointer. Additionally, we try to defrag the dictEntry in that dict.\n * Oldkey mey be a dead pointer and should not be accessed (we get a\n * pre-calculated hash value). Newkey may be null if the key pointer wasn't\n * moved. Return value is the the dictEntry if found, or NULL if not found.\n * NOTE: this is very ugly code, but it let's us avoid the complication of\n * doing a scan on another dict. */\ndictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged) {\n    dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash);\n    if (deref) {\n        dictEntry *de = *deref;\n        dictEntry *newde = activeDefragAlloc(de);\n        if (newde) {\n            de = *deref = newde;\n            (*defragged)++;\n        }\n        if (newkey)\n            de->key = newkey;\n        return de;\n    }\n    return NULL;\n}\n\nlong activeDefragQuickListNode(quicklist *ql, quicklistNode **node_ref) {\n    quicklistNode *newnode, *node = *node_ref;\n    long defragged = 0;\n    unsigned char *newzl;\n    if ((newnode = activeDefragAlloc(node))) {\n        if (newnode->prev)\n            newnode->prev->next = newnode;\n        else\n            ql->head = newnode;\n        if (newnode->next)\n            newnode->next->prev = newnode;\n        else\n            ql->tail = newnode;\n        *node_ref = node = newnode;\n        defragged++;\n    }\n    if ((newzl = activeDefragAlloc(node->zl)))\n        defragged++, node->zl = newzl;\n    return defragged;\n}\n\nlong activeDefragQuickListNodes(quicklist *ql) {\n    quicklistNode *node = ql->head;\n    long defragged = 0;\n    while (node) {\n        defragged += activeDefragQuickListNode(ql, &node);\n        node = node->next;\n    }\n    return defragged;\n}\n\n/* when the value has lots of elements, we want to handle it later and not as\n * oart of the main dictionary scan. this is needed in order to prevent latency\n * spikes when handling large items */\nvoid defragLater(redisDb *db, dictEntry *kde) {\n    sds key = sdsdup(dictGetKey(kde));\n    listAddNodeTail(db->defrag_later, key);\n}\n\n/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */\nlong scanLaterList(robj *ob, unsigned long *cursor, long long endtime, long long *defragged) {\n    quicklist *ql = ob->ptr;\n    quicklistNode *node;\n    long iterations = 0;\n    int bookmark_failed = 0;\n    if (ob->type != OBJ_LIST || ob->encoding != OBJ_ENCODING_QUICKLIST)\n        return 0;\n\n    if (*cursor == 0) {\n        /* if cursor is 0, we start new iteration */\n        node = ql->head;\n    } else {\n        node = quicklistBookmarkFind(ql, \"_AD\");\n        if (!node) {\n            /* if the bookmark was deleted, it means we reached the end. */\n            *cursor = 0;\n            return 0;\n        }\n        node = node->next;\n    }\n\n    (*cursor)++;\n    while (node) {\n        (*defragged) += activeDefragQuickListNode(ql, &node);\n        server.stat_active_defrag_scanned++;\n        if (++iterations > 128 && !bookmark_failed) {\n            if (ustime() > endtime) {\n                if (!quicklistBookmarkCreate(&ql, \"_AD\", node)) {\n                    bookmark_failed = 1;\n                } else {\n                    ob->ptr = ql; /* bookmark creation may have re-allocated the quicklist */\n                    return 1;\n                }\n            }\n            iterations = 0;\n        }\n        node = node->next;\n    }\n    quicklistBookmarkDelete(ql, \"_AD\");\n    *cursor = 0;\n    return bookmark_failed? 1: 0;\n}\n\ntypedef struct {\n    zset *zs;\n    long defragged;\n} scanLaterZsetData;\n\nvoid scanLaterZsetCallback(void *privdata, const dictEntry *_de) {\n    dictEntry *de = (dictEntry*)_de;\n    scanLaterZsetData *data = privdata;\n    data->defragged += activeDefragZsetEntry(data->zs, de);\n    server.stat_active_defrag_scanned++;\n}\n\nlong scanLaterZset(robj *ob, unsigned long *cursor) {\n    if (ob->type != OBJ_ZSET || ob->encoding != OBJ_ENCODING_SKIPLIST)\n        return 0;\n    zset *zs = (zset*)ob->ptr;\n    dict *d = zs->dict;\n    scanLaterZsetData data = {zs, 0};\n    *cursor = dictScan(d, *cursor, scanLaterZsetCallback, defragDictBucketCallback, &data);\n    return data.defragged;\n}\n\nvoid scanLaterSetCallback(void *privdata, const dictEntry *_de) {\n    dictEntry *de = (dictEntry*)_de;\n    long *defragged = privdata;\n    sds sdsele = dictGetKey(de), newsds;\n    if ((newsds = activeDefragSds(sdsele)))\n        (*defragged)++, de->key = newsds;\n    server.stat_active_defrag_scanned++;\n}\n\nlong scanLaterSet(robj *ob, unsigned long *cursor) {\n    long defragged = 0;\n    if (ob->type != OBJ_SET || ob->encoding != OBJ_ENCODING_HT)\n        return 0;\n    dict *d = ob->ptr;\n    *cursor = dictScan(d, *cursor, scanLaterSetCallback, defragDictBucketCallback, &defragged);\n    return defragged;\n}\n\nvoid scanLaterHashCallback(void *privdata, const dictEntry *_de) {\n    dictEntry *de = (dictEntry*)_de;\n    long *defragged = privdata;\n    sds sdsele = dictGetKey(de), newsds;\n    if ((newsds = activeDefragSds(sdsele)))\n        (*defragged)++, de->key = newsds;\n    sdsele = dictGetVal(de);\n    if ((newsds = activeDefragSds(sdsele)))\n        (*defragged)++, de->v.val = newsds;\n    server.stat_active_defrag_scanned++;\n}\n\nlong scanLaterHash(robj *ob, unsigned long *cursor) {\n    long defragged = 0;\n    if (ob->type != OBJ_HASH || ob->encoding != OBJ_ENCODING_HT)\n        return 0;\n    dict *d = ob->ptr;\n    *cursor = dictScan(d, *cursor, scanLaterHashCallback, defragDictBucketCallback, &defragged);\n    return defragged;\n}\n\nlong defragQuicklist(redisDb *db, dictEntry *kde) {\n    robj *ob = dictGetVal(kde);\n    long defragged = 0;\n    quicklist *ql = ob->ptr, *newql;\n    serverAssert(ob->type == OBJ_LIST && ob->encoding == OBJ_ENCODING_QUICKLIST);\n    if ((newql = activeDefragAlloc(ql)))\n        defragged++, ob->ptr = ql = newql;\n    if (ql->len > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else\n        defragged += activeDefragQuickListNodes(ql);\n    return defragged;\n}\n\nlong defragZsetSkiplist(redisDb *db, dictEntry *kde) {\n    robj *ob = dictGetVal(kde);\n    long defragged = 0;\n    zset *zs = (zset*)ob->ptr;\n    zset *newzs;\n    zskiplist *newzsl;\n    dict *newdict;\n    dictEntry *de;\n    struct zskiplistNode *newheader;\n    serverAssert(ob->type == OBJ_ZSET && ob->encoding == OBJ_ENCODING_SKIPLIST);\n    if ((newzs = activeDefragAlloc(zs)))\n        defragged++, ob->ptr = zs = newzs;\n    if ((newzsl = activeDefragAlloc(zs->zsl)))\n        defragged++, zs->zsl = newzsl;\n    if ((newheader = activeDefragAlloc(zs->zsl->header)))\n        defragged++, zs->zsl->header = newheader;\n    if (dictSize(zs->dict) > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else {\n        dictIterator *di = dictGetIterator(zs->dict);\n        while((de = dictNext(di)) != NULL) {\n            defragged += activeDefragZsetEntry(zs, de);\n        }\n        dictReleaseIterator(di);\n    }\n    /* handle the dict struct */\n    if ((newdict = activeDefragAlloc(zs->dict)))\n        defragged++, zs->dict = newdict;\n    /* defrag the dict tables */\n    defragged += dictDefragTables(zs->dict);\n    return defragged;\n}\n\nlong defragHash(redisDb *db, dictEntry *kde) {\n    long defragged = 0;\n    robj *ob = dictGetVal(kde);\n    dict *d, *newd;\n    serverAssert(ob->type == OBJ_HASH && ob->encoding == OBJ_ENCODING_HT);\n    d = ob->ptr;\n    if (dictSize(d) > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else\n        defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_VAL_IS_SDS);\n    /* handle the dict struct */\n    if ((newd = activeDefragAlloc(ob->ptr)))\n        defragged++, ob->ptr = newd;\n    /* defrag the dict tables */\n    defragged += dictDefragTables(ob->ptr);\n    return defragged;\n}\n\nlong defragSet(redisDb *db, dictEntry *kde) {\n    long defragged = 0;\n    robj *ob = dictGetVal(kde);\n    dict *d, *newd;\n    serverAssert(ob->type == OBJ_SET && ob->encoding == OBJ_ENCODING_HT);\n    d = ob->ptr;\n    if (dictSize(d) > server.active_defrag_max_scan_fields)\n        defragLater(db, kde);\n    else\n        defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_NO_VAL);\n    /* handle the dict struct */\n    if ((newd = activeDefragAlloc(ob->ptr)))\n        defragged++, ob->ptr = newd;\n    /* defrag the dict tables */\n    defragged += dictDefragTables(ob->ptr);\n    return defragged;\n}\n\n/* Defrag callback for radix tree iterator, called for each node,\n * used in order to defrag the nodes allocations. */\nint defragRaxNode(raxNode **noderef) {\n    raxNode *newnode = activeDefragAlloc(*noderef);\n    if (newnode) {\n        *noderef = newnode;\n        return 1;\n    }\n    return 0;\n}\n\n/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */\nint scanLaterStraemListpacks(robj *ob, unsigned long *cursor, long long endtime, long long *defragged) {\n    static unsigned char last[sizeof(streamID)];\n    raxIterator ri;\n    long iterations = 0;\n    if (ob->type != OBJ_STREAM || ob->encoding != OBJ_ENCODING_STREAM) {\n        *cursor = 0;\n        return 0;\n    }\n\n    stream *s = ob->ptr;\n    raxStart(&ri,s->rax);\n    if (*cursor == 0) {\n        /* if cursor is 0, we start new iteration */\n        defragRaxNode(&s->rax->head);\n        /* assign the iterator node callback before the seek, so that the\n         * initial nodes that are processed till the first item are covered */\n        ri.node_cb = defragRaxNode;\n        raxSeek(&ri,\"^\",NULL,0);\n    } else {\n        /* if cursor is non-zero, we seek to the static 'last' */\n        if (!raxSeek(&ri,\">\", last, sizeof(last))) {\n            *cursor = 0;\n            return 0;\n        }\n        /* assign the iterator node callback after the seek, so that the\n         * initial nodes that are processed till now aren't covered */\n        ri.node_cb = defragRaxNode;\n    }\n\n    (*cursor)++;\n    while (raxNext(&ri)) {\n        void *newdata = activeDefragAlloc(ri.data);\n        if (newdata)\n            raxSetData(ri.node, ri.data=newdata), (*defragged)++;\n        server.stat_active_defrag_scanned++;\n        if (++iterations > 128) {\n            if (ustime() > endtime) {\n                serverAssert(ri.key_len==sizeof(last));\n                memcpy(last,ri.key,ri.key_len);\n                raxStop(&ri);\n                return 1;\n            }\n            iterations = 0;\n        }\n    }\n    raxStop(&ri);\n    *cursor = 0;\n    return 0;\n}\n\n/* optional callback used defrag each rax element (not including the element pointer itself) */\ntypedef void *(raxDefragFunction)(raxIterator *ri, void *privdata, long *defragged);\n\n/* defrag radix tree including:\n * 1) rax struct\n * 2) rax nodes\n * 3) rax entry data (only if defrag_data is specified)\n * 4) call a callback per element, and allow the callback to return a new pointer for the element */\nlong defragRadixTree(rax **raxref, int defrag_data, raxDefragFunction *element_cb, void *element_cb_data) {\n    long defragged = 0;\n    raxIterator ri;\n    rax* rax;\n    if ((rax = activeDefragAlloc(*raxref)))\n        defragged++, *raxref = rax;\n    rax = *raxref;\n    raxStart(&ri,rax);\n    ri.node_cb = defragRaxNode;\n    defragRaxNode(&rax->head);\n    raxSeek(&ri,\"^\",NULL,0);\n    while (raxNext(&ri)) {\n        void *newdata = NULL;\n        if (element_cb)\n            newdata = element_cb(&ri, element_cb_data, &defragged);\n        if (defrag_data && !newdata)\n            newdata = activeDefragAlloc(ri.data);\n        if (newdata)\n            raxSetData(ri.node, ri.data=newdata), defragged++;\n    }\n    raxStop(&ri);\n    return defragged;\n}\n\ntypedef struct {\n    streamCG *cg;\n    streamConsumer *c;\n} PendingEntryContext;\n\nvoid* defragStreamConsumerPendingEntry(raxIterator *ri, void *privdata, long *defragged) {\n    UNUSED(defragged);\n    PendingEntryContext *ctx = privdata;\n    streamNACK *nack = ri->data, *newnack;\n    nack->consumer = ctx->c; /* update nack pointer to consumer */\n    newnack = activeDefragAlloc(nack);\n    if (newnack) {\n        /* update consumer group pointer to the nack */\n        void *prev;\n        raxInsert(ctx->cg->pel, ri->key, ri->key_len, newnack, &prev);\n        serverAssert(prev==nack);\n        /* note: we don't increment 'defragged' that's done by the caller */\n    }\n    return newnack;\n}\n\nvoid* defragStreamConsumer(raxIterator *ri, void *privdata, long *defragged) {\n    streamConsumer *c = ri->data;\n    streamCG *cg = privdata;\n    void *newc = activeDefragAlloc(c);\n    if (newc) {\n        /* note: we don't increment 'defragged' that's done by the caller */\n        c = newc;\n    }\n    sds newsds = activeDefragSds(c->name);\n    if (newsds)\n        (*defragged)++, c->name = newsds;\n    if (c->pel) {\n        PendingEntryContext pel_ctx = {cg, c};\n        *defragged += defragRadixTree(&c->pel, 0, defragStreamConsumerPendingEntry, &pel_ctx);\n    }\n    return newc; /* returns NULL if c was not defragged */\n}\n\nvoid* defragStreamConsumerGroup(raxIterator *ri, void *privdata, long *defragged) {\n    streamCG *cg = ri->data;\n    UNUSED(privdata);\n    if (cg->consumers)\n        *defragged += defragRadixTree(&cg->consumers, 0, defragStreamConsumer, cg);\n    if (cg->pel)\n        *defragged += defragRadixTree(&cg->pel, 0, NULL, NULL);\n    return NULL;\n}\n\nlong defragStream(redisDb *db, dictEntry *kde) {\n    long defragged = 0;\n    robj *ob = dictGetVal(kde);\n    serverAssert(ob->type == OBJ_STREAM && ob->encoding == OBJ_ENCODING_STREAM);\n    stream *s = ob->ptr, *news;\n\n    /* handle the main struct */\n    if ((news = activeDefragAlloc(s)))\n        defragged++, ob->ptr = s = news;\n\n    if (raxSize(s->rax) > server.active_defrag_max_scan_fields) {\n        rax *newrax = activeDefragAlloc(s->rax);\n        if (newrax)\n            defragged++, s->rax = newrax;\n        defragLater(db, kde);\n    } else\n        defragged += defragRadixTree(&s->rax, 1, NULL, NULL);\n\n    if (s->cgroups)\n        defragged += defragRadixTree(&s->cgroups, 1, defragStreamConsumerGroup, NULL);\n    return defragged;\n}\n\n/* for each key we scan in the main dict, this function will attempt to defrag\n * all the various pointers it has. Returns a stat of how many pointers were\n * moved. */\nlong defragKey(redisDb *db, dictEntry *de) {\n    sds keysds = dictGetKey(de);\n    robj *newob, *ob;\n    unsigned char *newzl;\n    long defragged = 0;\n    sds newsds;\n\n    /* Try to defrag the key name. */\n    newsds = activeDefragSds(keysds);\n    if (newsds)\n        defragged++, de->key = newsds;\n    if (dictSize(db->expires)) {\n         /* Dirty code:\n          * I can't search in db->expires for that key after i already released\n          * the pointer it holds it won't be able to do the string compare */\n        uint64_t hash = dictGetHash(db->dict, de->key);\n        replaceSateliteDictKeyPtrAndOrDefragDictEntry(db->expires, keysds, newsds, hash, &defragged);\n    }\n\n    /* Try to defrag robj and / or string value. */\n    ob = dictGetVal(de);\n    if ((newob = activeDefragStringOb(ob, &defragged))) {\n        de->v.val = newob;\n        ob = newob;\n    }\n\n    if (ob->type == OBJ_STRING) {\n        /* Already handled in activeDefragStringOb. */\n    } else if (ob->type == OBJ_LIST) {\n        if (ob->encoding == OBJ_ENCODING_QUICKLIST) {\n            defragged += defragQuicklist(db, de);\n        } else if (ob->encoding == OBJ_ENCODING_ZIPLIST) {\n            if ((newzl = activeDefragAlloc(ob->ptr)))\n                defragged++, ob->ptr = newzl;\n        } else {\n            serverPanic(\"Unknown list encoding\");\n        }\n    } else if (ob->type == OBJ_SET) {\n        if (ob->encoding == OBJ_ENCODING_HT) {\n            defragged += defragSet(db, de);\n        } else if (ob->encoding == OBJ_ENCODING_INTSET) {\n            intset *newis, *is = ob->ptr;\n            if ((newis = activeDefragAlloc(is)))\n                defragged++, ob->ptr = newis;\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (ob->type == OBJ_ZSET) {\n        if (ob->encoding == OBJ_ENCODING_ZIPLIST) {\n            if ((newzl = activeDefragAlloc(ob->ptr)))\n                defragged++, ob->ptr = newzl;\n        } else if (ob->encoding == OBJ_ENCODING_SKIPLIST) {\n            defragged += defragZsetSkiplist(db, de);\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (ob->type == OBJ_HASH) {\n        if (ob->encoding == OBJ_ENCODING_ZIPLIST) {\n            if ((newzl = activeDefragAlloc(ob->ptr)))\n                defragged++, ob->ptr = newzl;\n        } else if (ob->encoding == OBJ_ENCODING_HT) {\n            defragged += defragHash(db, de);\n        } else {\n            serverPanic(\"Unknown hash encoding\");\n        }\n    } else if (ob->type == OBJ_STREAM) {\n        defragged += defragStream(db, de);\n    } else if (ob->type == OBJ_MODULE) {\n        /* Currently defragmenting modules private data types\n         * is not supported. */\n    } else {\n        serverPanic(\"Unknown object type\");\n    }\n    return defragged;\n}\n\n/* Defrag scan callback for the main db dictionary. */\nvoid defragScanCallback(void *privdata, const dictEntry *de) {\n    long defragged = defragKey((redisDb*)privdata, (dictEntry*)de);\n    server.stat_active_defrag_hits += defragged;\n    if(defragged)\n        server.stat_active_defrag_key_hits++;\n    else\n        server.stat_active_defrag_key_misses++;\n    server.stat_active_defrag_scanned++;\n}\n\n/* Defrag scan callback for each hash table bicket,\n * used in order to defrag the dictEntry allocations. */\nvoid defragDictBucketCallback(void *privdata, dictEntry **bucketref) {\n    UNUSED(privdata); /* NOTE: this function is also used by both activeDefragCycle and scanLaterHash, etc. don't use privdata */\n    while(*bucketref) {\n        dictEntry *de = *bucketref, *newde;\n        if ((newde = activeDefragAlloc(de))) {\n            *bucketref = newde;\n        }\n        bucketref = &(*bucketref)->next;\n    }\n}\n\n/* Utility function to get the fragmentation ratio from jemalloc.\n * It is critical to do that by comparing only heap maps that belong to\n * jemalloc, and skip ones the jemalloc keeps as spare. Since we use this\n * fragmentation ratio in order to decide if a defrag action should be taken\n * or not, a false detection can cause the defragmenter to waste a lot of CPU\n * without the possibility of getting any results. */\nfloat getAllocatorFragmentation(size_t *out_frag_bytes) {\n    size_t resident, active, allocated;\n    zmalloc_get_allocator_info(&allocated, &active, &resident);\n    float frag_pct = ((float)active / allocated)*100 - 100;\n    size_t frag_bytes = active - allocated;\n    float rss_pct = ((float)resident / allocated)*100 - 100;\n    size_t rss_bytes = resident - allocated;\n    if(out_frag_bytes)\n        *out_frag_bytes = frag_bytes;\n    serverLog(LL_DEBUG,\n        \"allocated=%zu, active=%zu, resident=%zu, frag=%.0f%% (%.0f%% rss), frag_bytes=%zu (%zu rss)\",\n        allocated, active, resident, frag_pct, rss_pct, frag_bytes, rss_bytes);\n    return frag_pct;\n}\n\n/* We may need to defrag other globals, one small allcation can hold a full allocator run.\n * so although small, it is still important to defrag these */\nlong defragOtherGlobals() {\n    long defragged = 0;\n\n    /* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc.\n     * but we assume most of these are short lived, we only need to defrag allocations\n     * that remain static for a long time */\n    defragged += activeDefragSdsDict(server.lua_scripts, DEFRAG_SDS_DICT_VAL_IS_STROB);\n    defragged += activeDefragSdsListAndDict(server.repl_scriptcache_fifo, server.repl_scriptcache_dict, DEFRAG_SDS_DICT_NO_VAL);\n    return defragged;\n}\n\n/* returns 0 more work may or may not be needed (see non-zero cursor),\n * and 1 if time is up and more work is needed. */\nint defragLaterItem(dictEntry *de, unsigned long *cursor, long long endtime) {\n    if (de) {\n        robj *ob = dictGetVal(de);\n        if (ob->type == OBJ_LIST) {\n            return scanLaterList(ob, cursor, endtime, &server.stat_active_defrag_hits);\n        } else if (ob->type == OBJ_SET) {\n            server.stat_active_defrag_hits += scanLaterSet(ob, cursor);\n        } else if (ob->type == OBJ_ZSET) {\n            server.stat_active_defrag_hits += scanLaterZset(ob, cursor);\n        } else if (ob->type == OBJ_HASH) {\n            server.stat_active_defrag_hits += scanLaterHash(ob, cursor);\n        } else if (ob->type == OBJ_STREAM) {\n            return scanLaterStraemListpacks(ob, cursor, endtime, &server.stat_active_defrag_hits);\n        } else {\n            *cursor = 0; /* object type may have changed since we schedule it for later */\n        }\n    } else {\n        *cursor = 0; /* object may have been deleted already */\n    }\n    return 0;\n}\n\n/* static variables serving defragLaterStep to continue scanning a key from were we stopped last time. */\nstatic sds defrag_later_current_key = NULL;\nstatic unsigned long defrag_later_cursor = 0;\n\n/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */\nint defragLaterStep(redisDb *db, long long endtime) {\n    unsigned int iterations = 0;\n    unsigned long long prev_defragged = server.stat_active_defrag_hits;\n    unsigned long long prev_scanned = server.stat_active_defrag_scanned;\n    long long key_defragged;\n\n    do {\n        /* if we're not continuing a scan from the last call or loop, start a new one */\n        if (!defrag_later_cursor) {\n            listNode *head = listFirst(db->defrag_later);\n\n            /* Move on to next key */\n            if (defrag_later_current_key) {\n                serverAssert(defrag_later_current_key == head->value);\n                listDelNode(db->defrag_later, head);\n                defrag_later_cursor = 0;\n                defrag_later_current_key = NULL;\n            }\n\n            /* stop if we reached the last one. */\n            head = listFirst(db->defrag_later);\n            if (!head)\n                return 0;\n\n            /* start a new key */\n            defrag_later_current_key = head->value;\n            defrag_later_cursor = 0;\n        }\n\n        /* each time we enter this function we need to fetch the key from the dict again (if it still exists) */\n        dictEntry *de = dictFind(db->dict, defrag_later_current_key);\n        key_defragged = server.stat_active_defrag_hits;\n        do {\n            int quit = 0;\n            if (defragLaterItem(de, &defrag_later_cursor, endtime))\n                quit = 1; /* time is up, we didn't finish all the work */\n\n            /* Once in 16 scan iterations, 512 pointer reallocations, or 64 fields\n             * (if we have a lot of pointers in one hash bucket, or rehashing),\n             * check if we reached the time limit. */\n            if (quit || (++iterations > 16 ||\n                            server.stat_active_defrag_hits - prev_defragged > 512 ||\n                            server.stat_active_defrag_scanned - prev_scanned > 64)) {\n                if (quit || ustime() > endtime) {\n                    if(key_defragged != server.stat_active_defrag_hits)\n                        server.stat_active_defrag_key_hits++;\n                    else\n                        server.stat_active_defrag_key_misses++;\n                    return 1;\n                }\n                iterations = 0;\n                prev_defragged = server.stat_active_defrag_hits;\n                prev_scanned = server.stat_active_defrag_scanned;\n            }\n        } while(defrag_later_cursor);\n        if(key_defragged != server.stat_active_defrag_hits)\n            server.stat_active_defrag_key_hits++;\n        else\n            server.stat_active_defrag_key_misses++;\n    } while(1);\n}\n\n#define INTERPOLATE(x, x1, x2, y1, y2) ( (y1) + ((x)-(x1)) * ((y2)-(y1)) / ((x2)-(x1)) )\n#define LIMIT(y, min, max) ((y)<(min)? min: ((y)>(max)? max: (y)))\n\n/* decide if defrag is needed, and at what CPU effort to invest in it */\nvoid computeDefragCycles() {\n    size_t frag_bytes;\n    float frag_pct = getAllocatorFragmentation(&frag_bytes);\n    /* If we're not already running, and below the threshold, exit. */\n    if (!server.active_defrag_running) {\n        if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes)\n            return;\n    }\n\n    /* Calculate the adaptive aggressiveness of the defrag */\n    int cpu_pct = INTERPOLATE(frag_pct,\n            server.active_defrag_threshold_lower,\n            server.active_defrag_threshold_upper,\n            server.active_defrag_cycle_min,\n            server.active_defrag_cycle_max);\n    cpu_pct = LIMIT(cpu_pct,\n            server.active_defrag_cycle_min,\n            server.active_defrag_cycle_max);\n     /* We allow increasing the aggressiveness during a scan, but don't\n      * reduce it. */\n    if (!server.active_defrag_running ||\n        cpu_pct > server.active_defrag_running)\n    {\n        server.active_defrag_running = cpu_pct;\n        serverLog(LL_VERBOSE,\n            \"Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%\",\n            frag_pct, frag_bytes, cpu_pct);\n    }\n}\n\n/* Perform incremental defragmentation work from the serverCron.\n * This works in a similar way to activeExpireCycle, in the sense that\n * we do incremental work across calls. */\nvoid activeDefragCycle(void) {\n    static int current_db = -1;\n    static unsigned long cursor = 0;\n    static redisDb *db = NULL;\n    static long long start_scan, start_stat;\n    unsigned int iterations = 0;\n    unsigned long long prev_defragged = server.stat_active_defrag_hits;\n    unsigned long long prev_scanned = server.stat_active_defrag_scanned;\n    long long start, timelimit, endtime;\n    mstime_t latency;\n    int quit = 0;\n\n    if (!server.active_defrag_enabled) {\n        if (server.active_defrag_running) {\n            /* if active defrag was disabled mid-run, start from fresh next time. */\n            server.active_defrag_running = 0;\n            if (db)\n                listEmpty(db->defrag_later);\n            defrag_later_current_key = NULL;\n            defrag_later_cursor = 0;\n            current_db = -1;\n            cursor = 0;\n            db = NULL;\n        }\n        return;\n    }\n\n    if (hasActiveChildProcess())\n        return; /* Defragging memory while there's a fork will just do damage. */\n\n    /* Once a second, check if we the fragmentation justfies starting a scan\n     * or making it more aggressive. */\n    run_with_period(1000) {\n        computeDefragCycles();\n    }\n    if (!server.active_defrag_running)\n        return;\n\n    /* See activeExpireCycle for how timelimit is handled. */\n    start = ustime();\n    timelimit = 1000000*server.active_defrag_running/server.hz/100;\n    if (timelimit <= 0) timelimit = 1;\n    endtime = start + timelimit;\n    latencyStartMonitor(latency);\n\n    do {\n        /* if we're not continuing a scan from the last call or loop, start a new one */\n        if (!cursor) {\n            /* finish any leftovers from previous db before moving to the next one */\n            if (db && defragLaterStep(db, endtime)) {\n                quit = 1; /* time is up, we didn't finish all the work */\n                break; /* this will exit the function and we'll continue on the next cycle */\n            }\n\n            /* Move on to next database, and stop if we reached the last one. */\n            if (++current_db >= server.dbnum) {\n                /* defrag other items not part of the db / keys */\n                defragOtherGlobals();\n\n                long long now = ustime();\n                size_t frag_bytes;\n                float frag_pct = getAllocatorFragmentation(&frag_bytes);\n                serverLog(LL_VERBOSE,\n                    \"Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu\",\n                    (int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);\n\n                start_scan = now;\n                current_db = -1;\n                cursor = 0;\n                db = NULL;\n                server.active_defrag_running = 0;\n\n                computeDefragCycles(); /* if another scan is needed, start it right away */\n                if (server.active_defrag_running != 0 && ustime() < endtime)\n                    continue;\n                break;\n            }\n            else if (current_db==0) {\n                /* Start a scan from the first database. */\n                start_scan = ustime();\n                start_stat = server.stat_active_defrag_hits;\n            }\n\n            db = &server.db[current_db];\n            cursor = 0;\n        }\n\n        do {\n            /* before scanning the next bucket, see if we have big keys left from the previous bucket to scan */\n            if (defragLaterStep(db, endtime)) {\n                quit = 1; /* time is up, we didn't finish all the work */\n                break; /* this will exit the function and we'll continue on the next cycle */\n            }\n\n            cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db);\n\n            /* Once in 16 scan iterations, 512 pointer reallocations. or 64 keys\n             * (if we have a lot of pointers in one hash bucket or rehasing),\n             * check if we reached the time limit.\n             * But regardless, don't start a new db in this loop, this is because after\n             * the last db we call defragOtherGlobals, which must be done in once cycle */\n            if (!cursor || (++iterations > 16 ||\n                            server.stat_active_defrag_hits - prev_defragged > 512 ||\n                            server.stat_active_defrag_scanned - prev_scanned > 64)) {\n                if (!cursor || ustime() > endtime) {\n                    quit = 1;\n                    break;\n                }\n                iterations = 0;\n                prev_defragged = server.stat_active_defrag_hits;\n                prev_scanned = server.stat_active_defrag_scanned;\n            }\n        } while(cursor && !quit);\n    } while(!quit);\n\n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"active-defrag-cycle\",latency);\n}\n\n#else /* HAVE_DEFRAG */\n\nvoid activeDefragCycle(void) {\n    /* Not implemented yet. */\n}\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 <stdint.h>\n#include <string.h>\n#include <stdarg.h>\n#include <limits.h>\n#include <sys/time.h>\n\n#include \"dict.h\"\n#include \"zmalloc.h\"\n#ifndef DICT_BENCHMARK_MAIN\n#include \"redisassert.h\"\n#else\n#include <assert.h>\n#endif\n\n/* Using dictEnableResize() / dictDisableResize() we make possible to\n * enable/disable resizing of the hash table as needed. This is very important\n * for Redis, as we use copy-on-write and don't want to move too much memory\n * around when there is a child performing saving operations.\n *\n * Note that even when dict_can_resize is set to 0, not all resizes are\n * prevented: a hash table is still allowed to grow if the ratio between\n * the number of elements and the buckets > dict_force_resize_ratio. */\nstatic int dict_can_resize = 1;\nstatic unsigned int dict_force_resize_ratio = 5;\n\n/* -------------------------- private prototypes ---------------------------- */\n\nstatic int _dictExpandIfNeeded(dict *ht);\nstatic unsigned long _dictNextPower(unsigned long size);\nstatic long _dictKeyIndex(dict *ht, const void *key, uint64_t hash, dictEntry **existing);\nstatic int _dictInit(dict *ht, dictType *type, void *privDataPtr);\n\n/* -------------------------- hash functions -------------------------------- */\n\nstatic uint8_t dict_hash_function_seed[16];\n\nvoid dictSetHashFunctionSeed(uint8_t *seed) {\n    memcpy(dict_hash_function_seed,seed,sizeof(dict_hash_function_seed));\n}\n\nuint8_t *dictGetHashFunctionSeed(void) {\n    return dict_hash_function_seed;\n}\n\n/* The default hashing function uses SipHash implementation\n * in siphash.c. */\n\nuint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k);\nuint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k);\n\nuint64_t dictGenHashFunction(const void *key, int len) {\n    return siphash(key,len,dict_hash_function_seed);\n}\n\nuint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) {\n    return siphash_nocase(buf,len,dict_hash_function_seed);\n}\n\n/* ----------------------------- API implementation ------------------------- */\n\n/* Reset a hash table already initialized with ht_init().\n * NOTE: This function should only be called by ht_destroy(). */\nstatic void _dictReset(dictht *ht)\n{\n    ht->table = NULL;\n    ht->size = 0;\n    ht->sizemask = 0;\n    ht->used = 0;\n}\n\n/* Create a new hash table */\ndict *dictCreate(dictType *type,\n        void *privDataPtr)\n{\n    dict *d = zmalloc(sizeof(*d));\n\n    _dictInit(d,type,privDataPtr);\n    return d;\n}\n\n/* Initialize the hash table */\nint _dictInit(dict *d, dictType *type,\n        void *privDataPtr)\n{\n    _dictReset(&d->ht[0]);\n    _dictReset(&d->ht[1]);\n    d->type = type;\n    d->privdata = privDataPtr;\n    d->rehashidx = -1;\n    d->iterators = 0;\n    return DICT_OK;\n}\n\n/* Resize the table to the minimal size that contains all the elements,\n * but with the invariant of a USED/BUCKETS ratio near to <= 1 */\nint dictResize(dict *d)\n{\n    unsigned long minimal;\n\n    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;\n    minimal = d->ht[0].used;\n    if (minimal < DICT_HT_INITIAL_SIZE)\n        minimal = DICT_HT_INITIAL_SIZE;\n    return dictExpand(d, minimal);\n}\n\n/* Expand or create the hash table */\nint dictExpand(dict *d, unsigned long size)\n{\n    /* the size is invalid if it is smaller than the number of\n     * elements already inside the hash table */\n    if (dictIsRehashing(d) || d->ht[0].used > size)\n        return DICT_ERR;\n\n    dictht n; /* the new hash table */\n    unsigned long realsize = _dictNextPower(size);\n\n    /* Rehashing to the same table size is not useful. */\n    if (realsize == d->ht[0].size) return DICT_ERR;\n\n    /* Allocate the new hash table and initialize all pointers to NULL */\n    n.size = realsize;\n    n.sizemask = realsize-1;\n    n.table = 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    if (d->ht[0].table == NULL) {\n        d->ht[0] = n;\n        return DICT_OK;\n    }\n\n    /* Prepare a second hash table for incremental rehashing */\n    d->ht[1] = n;\n    d->rehashidx = 0;\n    return DICT_OK;\n}\n\n/* Performs N steps of incremental rehashing. Returns 1 if there are still\n * keys to move from the old to the new hash table, otherwise 0 is returned.\n *\n * Note that a rehashing step consists in moving a bucket (that may have more\n * than one key as we use chaining) from the old to the new hash table, however\n * since part of the hash table may be composed of empty spaces, it is not\n * guaranteed that this function will rehash even a single bucket, since it\n * will visit at max N*10 empty buckets in total, otherwise the amount of\n * work it does would be unbound and the function may block for a long time. */\nint dictRehash(dict *d, int n) {\n    int empty_visits = n*10; /* Max number of empty buckets to visit. */\n    if (!dictIsRehashing(d)) return 0;\n\n    while(n-- && d->ht[0].used != 0) {\n        dictEntry *de, *nextde;\n\n        /* Note that rehashidx can't overflow as we are sure there are more\n         * elements because ht[0].used != 0 */\n        assert(d->ht[0].size > (unsigned long)d->rehashidx);\n        while(d->ht[0].table[d->rehashidx] == NULL) {\n            d->rehashidx++;\n            if (--empty_visits == 0) return 1;\n        }\n        de = d->ht[0].table[d->rehashidx];\n        /* Move all the keys in this bucket from the old to the new hash HT */\n        while(de) {\n            uint64_t h;\n\n            nextde = de->next;\n            /* Get the index in the new hash table */\n            h = dictHashKey(d, de->key) & d->ht[1].sizemask;\n            de->next = d->ht[1].table[h];\n            d->ht[1].table[h] = de;\n            d->ht[0].used--;\n            d->ht[1].used++;\n            de = nextde;\n        }\n        d->ht[0].table[d->rehashidx] = NULL;\n        d->rehashidx++;\n    }\n\n    /* Check if we already rehashed the whole table... */\n    if (d->ht[0].used == 0) {\n        zfree(d->ht[0].table);\n        d->ht[0] = d->ht[1];\n        _dictReset(&d->ht[1]);\n        d->rehashidx = -1;\n        return 0;\n    }\n\n    /* More to rehash... */\n    return 1;\n}\n\nlong long timeInMilliseconds(void) {\n    struct timeval tv;\n\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);\n}\n\n/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */\nint dictRehashMilliseconds(dict *d, int ms) {\n    long long start = timeInMilliseconds();\n    int rehashes = 0;\n\n    while(dictRehash(d,100)) {\n        rehashes += 100;\n        if (timeInMilliseconds()-start > ms) break;\n    }\n    return rehashes;\n}\n\n/* This function performs just a step of rehashing, and only if there are\n * no safe iterators bound to our hash table. When we have iterators in the\n * middle of a rehashing we can't mess with the two hash tables otherwise\n * some element can be missed or duplicated.\n *\n * This function is called by common lookup or update operations in the\n * dictionary so that the hash table automatically migrates from H1 to H2\n * while it is actively used. */\nstatic void _dictRehashStep(dict *d) {\n    if (d->iterators == 0) dictRehash(d,1);\n}\n\n/* Add an element to the target hash table */\nint dictAdd(dict *d, void *key, void *val)\n{\n    dictEntry *entry = dictAddRaw(d,key,NULL);\n\n    if (!entry) return DICT_ERR;\n    dictSetVal(d, entry, val);\n    return DICT_OK;\n}\n\n/* Low level add or find:\n * This function adds the entry but instead of setting a value returns the\n * dictEntry structure to the user, that will make sure to fill the value\n * field as he wishes.\n *\n * This function is also directly exposed to the user API to be called\n * mainly in order to store non-pointers inside the hash value, example:\n *\n * entry = dictAddRaw(dict,mykey,NULL);\n * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);\n *\n * Return values:\n *\n * If key already exists NULL is returned, and \"*existing\" is populated\n * with the existing entry if existing is not NULL.\n *\n * If key was added, the hash entry is returned to be manipulated by the caller.\n */\ndictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)\n{\n    long index;\n    dictEntry *entry;\n    dictht *ht;\n\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n\n    /* Get the index of the new element, or -1 if\n     * the element already exists. */\n    if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)\n        return NULL;\n\n    /* Allocate the memory and store the new entry.\n     * Insert the element in top, with the assumption that in a database\n     * system it is more likely that recently added entries are accessed\n     * more frequently. */\n    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];\n    entry = zmalloc(sizeof(*entry));\n    entry->next = ht->table[index];\n    ht->table[index] = entry;\n    ht->used++;\n\n    /* Set the hash entry fields. */\n    dictSetKey(d, entry, key);\n    return entry;\n}\n\n/* Add or Overwrite:\n * Add an element, discarding the old value if the key already exists.\n * Return 1 if the key was added from scratch, 0 if there was already an\n * element with such key and dictReplace() just performed a value update\n * operation. */\nint dictReplace(dict *d, void *key, void *val)\n{\n    dictEntry *entry, *existing, auxentry;\n\n    /* Try to add the element. If the key\n     * does not exists dictAdd will succeed. */\n    entry = dictAddRaw(d,key,&existing);\n    if (entry) {\n        dictSetVal(d, entry, val);\n        return 1;\n    }\n\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 = *existing;\n    dictSetVal(d, existing, val);\n    dictFreeVal(d, &auxentry);\n    return 0;\n}\n\n/* Add or Find:\n * dictAddOrFind() is simply a version of dictAddRaw() that always\n * returns the hash entry of the specified key, even if the key already\n * exists and can't be added (in that case the entry of the already\n * existing key is returned.)\n *\n * See dictAddRaw() for more information. */\ndictEntry *dictAddOrFind(dict *d, void *key) {\n    dictEntry *entry, *existing;\n    entry = dictAddRaw(d,key,&existing);\n    return entry ? entry : existing;\n}\n\n/* Search and remove an element. This is an helper function for\n * dictDelete() and dictUnlink(), please check the top comment\n * of those functions. */\nstatic dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {\n    uint64_t h, idx;\n    dictEntry *he, *prevHe;\n    int table;\n\n    if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;\n\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    h = dictHashKey(d, key);\n\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        he = d->ht[table].table[idx];\n        prevHe = NULL;\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key)) {\n                /* Unlink the element from the list */\n                if (prevHe)\n                    prevHe->next = he->next;\n                else\n                    d->ht[table].table[idx] = he->next;\n                if (!nofree) {\n                    dictFreeKey(d, he);\n                    dictFreeVal(d, he);\n                    zfree(he);\n                }\n                d->ht[table].used--;\n                return he;\n            }\n            prevHe = he;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) break;\n    }\n    return NULL; /* not found */\n}\n\n/* Remove an element, returning DICT_OK on success or DICT_ERR if the\n * element was not found. */\nint dictDelete(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,0) ? DICT_OK : DICT_ERR;\n}\n\n/* Remove an element from the table, but without actually releasing\n * the key, value and dictionary entry. The dictionary entry is returned\n * if the element was found (and unlinked from the table), and the user\n * should later call `dictFreeUnlinkedEntry()` with it in order to release it.\n * Otherwise if the key is not found, NULL is returned.\n *\n * This function is useful when we want to remove something from the hash\n * table but want to use its value before actually deleting the entry.\n * Without this function the pattern would require two lookups:\n *\n *  entry = dictFind(...);\n *  // Do something with entry\n *  dictDelete(dictionary,entry);\n *\n * Thanks to this function it is possible to avoid this, and use\n * instead:\n *\n * entry = dictUnlink(dictionary,entry);\n * // Do something with entry\n * dictFreeUnlinkedEntry(entry); // <- This does not need to lookup again.\n */\ndictEntry *dictUnlink(dict *ht, const void *key) {\n    return dictGenericDelete(ht,key,1);\n}\n\n/* You need to call this function to really free the entry after a call\n * to dictUnlink(). It's safe to call this function with 'he' = NULL. */\nvoid dictFreeUnlinkedEntry(dict *d, dictEntry *he) {\n    if (he == NULL) return;\n    dictFreeKey(d, he);\n    dictFreeVal(d, he);\n    zfree(he);\n}\n\n/* Destroy an entire dictionary */\nint _dictClear(dict *d, dictht *ht, void(callback)(void *)) {\n    unsigned long i;\n\n    /* Free all the elements */\n    for (i = 0; i < ht->size && ht->used > 0; i++) {\n        dictEntry *he, *nextHe;\n\n        if (callback && (i & 65535) == 0) callback(d->privdata);\n\n        if ((he = ht->table[i]) == NULL) continue;\n        while(he) {\n            nextHe = he->next;\n            dictFreeKey(d, he);\n            dictFreeVal(d, he);\n            zfree(he);\n            ht->used--;\n            he = nextHe;\n        }\n    }\n    /* Free the table and the allocated cache structure */\n    zfree(ht->table);\n    /* Re-initialize the table */\n    _dictReset(ht);\n    return DICT_OK; /* never fails */\n}\n\n/* Clear & Release the hash table */\nvoid dictRelease(dict *d)\n{\n    _dictClear(d,&d->ht[0],NULL);\n    _dictClear(d,&d->ht[1],NULL);\n    zfree(d);\n}\n\ndictEntry *dictFind(dict *d, const void *key)\n{\n    dictEntry *he;\n    uint64_t h, idx, table;\n\n    if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    h = dictHashKey(d, key);\n    for (table = 0; table <= 1; table++) {\n        idx = h & d->ht[table].sizemask;\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key))\n                return he;\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) return NULL;\n    }\n    return NULL;\n}\n\nvoid *dictFetchValue(dict *d, const void *key) {\n    dictEntry *he;\n\n    he = dictFind(d,key);\n    return he ? dictGetVal(he) : NULL;\n}\n\n/* A fingerprint is a 64 bit number that represents the state of the dictionary\n * at a given time, it's just a few dict properties xored together.\n * When an unsafe iterator is initialized, we get the dict fingerprint, and check\n * the fingerprint again when the iterator is released.\n * If the two fingerprints are different it means that the user of the iterator\n * performed forbidden operations against the dictionary while iterating. */\nlong long dictFingerprint(dict *d) {\n    long long integers[6], hash = 0;\n    int j;\n\n    integers[0] = (long) d->ht[0].table;\n    integers[1] = d->ht[0].size;\n    integers[2] = d->ht[0].used;\n    integers[3] = (long) d->ht[1].table;\n    integers[4] = d->ht[1].size;\n    integers[5] = d->ht[1].used;\n\n    /* We hash N integers by summing every successive integer with the integer\n     * hashing of the previous sum. Basically:\n     *\n     * Result = hash(hash(hash(int1)+int2)+int3) ...\n     *\n     * This way the same set of integers in a different order will (likely) hash\n     * to a different number. */\n    for (j = 0; j < 6; j++) {\n        hash += integers[j];\n        /* For the hashing step we use Tomas Wang's 64 bit integer hash. */\n        hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;\n        hash = hash ^ (hash >> 24);\n        hash = (hash + (hash << 3)) + (hash << 8); // hash * 265\n        hash = hash ^ (hash >> 14);\n        hash = (hash + (hash << 2)) + (hash << 4); // hash * 21\n        hash = hash ^ (hash >> 28);\n        hash = hash + (hash << 31);\n    }\n    return hash;\n}\n\ndictIterator *dictGetIterator(dict *d)\n{\n    dictIterator *iter = 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    return iter;\n}\n\ndictIterator *dictGetSafeIterator(dict *d) {\n    dictIterator *i = dictGetIterator(d);\n\n    i->safe = 1;\n    return i;\n}\n\ndictEntry *dictNext(dictIterator *iter)\n{\n    while (1) {\n        if (iter->entry == NULL) {\n            dictht *ht = &iter->d->ht[iter->table];\n            if (iter->index == -1 && iter->table == 0) {\n                if (iter->safe)\n                    iter->d->iterators++;\n                else\n                    iter->fingerprint = dictFingerprint(iter->d);\n            }\n            iter->index++;\n            if (iter->index >= (long) ht->size) {\n                if (dictIsRehashing(iter->d) && iter->table == 0) {\n                    iter->table++;\n                    iter->index = 0;\n                    ht = &iter->d->ht[1];\n                } else {\n                    break;\n                }\n            }\n            iter->entry = ht->table[iter->index];\n        } else {\n            iter->entry = iter->nextEntry;\n        }\n        if (iter->entry) {\n            /* We need to save the 'next' here, the iterator user\n             * may delete the entry we are returning. */\n            iter->nextEntry = iter->entry->next;\n            return iter->entry;\n        }\n    }\n    return NULL;\n}\n\nvoid dictReleaseIterator(dictIterator *iter)\n{\n    if (!(iter->index == -1 && iter->table == 0)) {\n        if (iter->safe)\n            iter->d->iterators--;\n        else\n            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 */\ndictEntry *dictGetRandomKey(dict *d)\n{\n    dictEntry *he, *orighe;\n    unsigned long h;\n    int listlen, listele;\n\n    if (dictSize(d) == 0) return NULL;\n    if (dictIsRehashing(d)) _dictRehashStep(d);\n    if (dictIsRehashing(d)) {\n        do {\n            /* We are sure there are no elements in indexes from 0\n             * to rehashidx-1 */\n            h = d->rehashidx + (random() % (d->ht[0].size +\n                                            d->ht[1].size -\n                                            d->rehashidx));\n            he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :\n                                      d->ht[0].table[h];\n        } while(he == NULL);\n    } else {\n        do {\n            h = random() & d->ht[0].sizemask;\n            he = d->ht[0].table[h];\n        } while(he == NULL);\n    }\n\n    /* Now we found a non empty bucket, but it is a linked\n     * list and we need to get a random element from the list.\n     * The only sane way to do so is counting the elements and\n     * select a random index. */\n    listlen = 0;\n    orighe = he;\n    while(he) {\n        he = he->next;\n        listlen++;\n    }\n    listele = random() % listlen;\n    he = orighe;\n    while(listele--) he = he->next;\n    return he;\n}\n\n/* This function samples the dictionary to return a few keys from random\n * locations.\n *\n * It does not guarantee to return all the keys specified in 'count', nor\n * it does guarantee to return non-duplicated elements, however it will make\n * some effort to do both things.\n *\n * Returned pointers to hash table entries are stored into 'des' that\n * points to an array of dictEntry pointers. The array must have room for\n * at least 'count' elements, that is the argument we pass to the function\n * to tell how many random elements we need.\n *\n * The function returns the number of items stored into 'des', that may\n * be less than 'count' if the hash table has less than 'count' elements\n * inside, or if not enough elements were found in a reasonable amount of\n * steps.\n *\n * Note that this function is not suitable when you need a good distribution\n * of the returned items, but only when you need to \"sample\" a given number\n * of continuous elements to run some kind of algorithm or to produce\n * statistics. However the function is much faster than dictGetRandomKey()\n * at producing N elements. */\nunsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {\n    unsigned long j; /* internal hash table id, 0 or 1. */\n    unsigned long tables; /* 1 or 2 tables? */\n    unsigned long stored = 0, maxsizemask;\n    unsigned long maxsteps;\n\n    if (dictSize(d) < count) count = dictSize(d);\n    maxsteps = count*10;\n\n    /* Try to do a rehashing work proportional to 'count'. */\n    for (j = 0; j < count; j++) {\n        if (dictIsRehashing(d))\n            _dictRehashStep(d);\n        else\n            break;\n    }\n\n    tables = dictIsRehashing(d) ? 2 : 1;\n    maxsizemask = d->ht[0].sizemask;\n    if (tables > 1 && maxsizemask < d->ht[1].sizemask)\n        maxsizemask = d->ht[1].sizemask;\n\n    /* Pick a random point inside the larger table. */\n    unsigned long i = random() & maxsizemask;\n    unsigned long emptylen = 0; /* Continuous empty entries so far. */\n    while(stored < count && maxsteps--) {\n        for (j = 0; j < tables; j++) {\n            /* Invariant of the dict.c rehashing: up to the indexes already\n             * visited in ht[0] during the rehashing, there are no populated\n             * buckets, so we can skip ht[0] for indexes between 0 and idx-1. */\n            if (tables == 2 && j == 0 && i < (unsigned long) d->rehashidx) {\n                /* Moreover, if we are currently out of range in the second\n                 * table, there will be no elements in both tables up to\n                 * the current rehashing index, so we jump if possible.\n                 * (this happens when going from big to small table). */\n                if (i >= d->ht[1].size)\n                    i = d->rehashidx;\n                else\n                    continue;\n            }\n            if (i >= d->ht[j].size) continue; /* Out of range for this table. */\n            dictEntry *he = d->ht[j].table[i];\n\n            /* Count contiguous empty buckets, and jump to other\n             * locations if they reach 'count' (with a minimum of 5). */\n            if (he == NULL) {\n                emptylen++;\n                if (emptylen >= 5 && emptylen > count) {\n                    i = random() & maxsizemask;\n                    emptylen = 0;\n                }\n            } else {\n                emptylen = 0;\n                while (he) {\n                    /* Collect all the elements of the buckets found non\n                     * empty while iterating. */\n                    *des = he;\n                    des++;\n                    he = he->next;\n                    stored++;\n                    if (stored == count) return stored;\n                }\n            }\n        }\n        i = (i+1) & maxsizemask;\n    }\n    return stored;\n}\n\n/* This is like dictGetRandomKey() from the POV of the API, but will do more\n * work to ensure a better distribution of the returned element.\n *\n * This function improves the distribution because the dictGetRandomKey()\n * problem is that it selects a random bucket, then it selects a random\n * element from the chain in the bucket. However elements being in different\n * chain lengths will have different probabilities of being reported. With\n * this function instead what we do is to consider a \"linear\" range of the table\n * that may be constituted of N buckets with chains of different lengths\n * appearing one after the other. Then we report a random element in the range.\n * In this way we smooth away the problem of different chain lenghts. */\n#define GETFAIR_NUM_ENTRIES 15\ndictEntry *dictGetFairRandomKey(dict *d) {\n    dictEntry *entries[GETFAIR_NUM_ENTRIES];\n    unsigned int count = dictGetSomeKeys(d,entries,GETFAIR_NUM_ENTRIES);\n    /* Note that dictGetSomeKeys() may return zero elements in an unlucky\n     * run() even if there are actually elements inside the hash table. So\n     * when we get zero, we call the true dictGetRandomKey() that will always\n     * yeld the element if the hash table has at least one. */\n    if (count == 0) return dictGetRandomKey(d);\n    unsigned int idx = rand() % count;\n    return entries[idx];\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 = CHAR_BIT * sizeof(v); // bit size; must be power of 2\n    unsigned long mask = ~0UL;\n    while ((s >>= 1) > 0) {\n        mask ^= (mask << s);\n        v = ((v >> s) & mask) | ((v << s) & ~mask);\n    }\n    return v;\n}\n\n/* dictScan() is used to iterate over the elements of a dictionary.\n *\n * Iterating works the following way:\n *\n * 1) Initially you call the function using a cursor (v) value of 0.\n * 2) The function performs one step of the iteration, and returns the\n *    new cursor value you must use in the next call.\n * 3) When the returned cursor is 0, the iteration is complete.\n *\n * The function guarantees all elements present in the\n * dictionary get returned between the start and end of the iteration.\n * However it is possible some elements get returned multiple times.\n *\n * For every element returned, the callback argument 'fn' is\n * called with 'privdata' as first argument and the dictionary entry\n * 'de' as second argument.\n *\n * HOW IT WORKS.\n *\n * The iteration algorithm was designed by Pieter Noordhuis.\n * The main idea is to increment a cursor starting from the higher order\n * bits. That is, instead of incrementing the cursor normally, the bits\n * of the cursor are reversed, then the cursor is incremented, and finally\n * the bits are reversed again.\n *\n * This strategy is needed because the hash table may be resized between\n * iteration calls.\n *\n * dict.c hash tables are always power of two in size, and they\n * use chaining, so the position of an element in a given table is given\n * by computing the bitwise AND between Hash(key) and SIZE-1\n * (where SIZE-1 is always the mask that is equivalent to taking the rest\n *  of the division between the Hash of the key and SIZE).\n *\n * For example if the current hash table size is 16, the mask is\n * (in binary) 1111. The position of a key in the hash table will always be\n * the last four bits of the hash output, and so forth.\n *\n * WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?\n *\n * If the hash table grows, elements can go anywhere in one multiple of\n * the old bucket: for example let's say we already iterated with\n * a 4 bit cursor 1100 (the mask is 1111 because hash table size = 16).\n *\n * If the hash table will be resized to 64 elements, then the new mask will\n * be 111111. The new buckets you obtain by substituting in ??1100\n * with either 0 or 1 can be targeted only by keys we already visited\n * when scanning the bucket 1100 in the smaller hash table.\n *\n * By iterating the higher bits first, because of the inverted counter, the\n * cursor does not need to restart if the table size gets bigger. It will\n * continue iterating using cursors without '1100' at the end, and also\n * without any other combination of the final 4 bits already explored.\n *\n * Similarly when the table size shrinks over time, for example going from\n * 16 to 8, if a combination of the lower three bits (the mask for size 8\n * is 111) were already completely explored, it would not be visited again\n * because we are sure we tried, for example, both 0111 and 1111 (all the\n * variations of the higher bit) so we don't need to test it again.\n *\n * WAIT... YOU HAVE *TWO* TABLES DURING REHASHING!\n *\n * Yes, this is true, but we always iterate the smaller table first, then\n * we test all the expansions of the current cursor into the larger\n * table. For example if the current cursor is 101 and we also have a\n * larger table of size 16, we also test (0)101 and (1)101 inside the larger\n * table. This reduces the problem back to having only one table, where\n * the larger one, if it exists, is just an expansion of the smaller one.\n *\n * LIMITATIONS\n *\n * This iterator is completely stateless, and this is a huge advantage,\n * including no additional memory used.\n *\n * The disadvantages resulting from this design are:\n *\n * 1) It is possible we return elements more than once. However this is usually\n *    easy to deal with in the application level.\n * 2) The iterator must return multiple elements per call, as it needs to always\n *    return all the keys chained in a given bucket, and all the expansions, so\n *    we are sure we don't miss keys moving during rehashing.\n * 3) The reverse cursor is somewhat hard to understand at first, but this\n *    comment is supposed to help.\n */\nunsigned long dictScan(dict *d,\n                       unsigned long v,\n                       dictScanFunction *fn,\n                       dictScanBucketFunction* bucketfn,\n                       void *privdata)\n{\n    dictht *t0, *t1;\n    const dictEntry *de, *next;\n    unsigned long m0, m1;\n\n    if (dictSize(d) == 0) return 0;\n\n    /* Having a safe iterator means no rehashing can happen, see _dictRehashStep.\n     * This is needed in case the scan callback tries to do dictFind or alike. */\n    d->iterators++;\n\n    if (!dictIsRehashing(d)) {\n        t0 = &(d->ht[0]);\n        m0 = t0->sizemask;\n\n        /* Emit entries at cursor */\n        if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);\n        de = t0->table[v & m0];\n        while (de) {\n            next = de->next;\n            fn(privdata, de);\n            de = next;\n        }\n\n        /* Set unmasked bits so incrementing the reversed cursor\n         * operates on the masked bits */\n        v |= ~m0;\n\n        /* Increment the reverse cursor */\n        v = rev(v);\n        v++;\n        v = rev(v);\n\n    } else {\n        t0 = &d->ht[0];\n        t1 = &d->ht[1];\n\n        /* Make sure t0 is the smaller and t1 is the bigger table */\n        if (t0->size > t1->size) {\n            t0 = &d->ht[1];\n            t1 = &d->ht[0];\n        }\n\n        m0 = t0->sizemask;\n        m1 = t1->sizemask;\n\n        /* Emit entries at cursor */\n        if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);\n        de = t0->table[v & m0];\n        while (de) {\n            next = de->next;\n            fn(privdata, de);\n            de = next;\n        }\n\n        /* Iterate over indices in larger table that are the expansion\n         * of the index pointed to by the cursor in the smaller table */\n        do {\n            /* Emit entries at cursor */\n            if (bucketfn) bucketfn(privdata, &t1->table[v & m1]);\n            de = t1->table[v & m1];\n            while (de) {\n                next = de->next;\n                fn(privdata, de);\n                de = next;\n            }\n\n            /* Increment the reverse cursor not covered by the smaller mask.*/\n            v |= ~m1;\n            v = rev(v);\n            v++;\n            v = rev(v);\n\n            /* Continue while bits covered by mask difference is non-zero */\n        } while (v & (m0 ^ m1));\n    }\n\n    /* undo the ++ at the top */\n    d->iterators--;\n\n    return v;\n}\n\n/* ------------------------- private functions ------------------------------ */\n\n/* Expand the hash table if needed */\nstatic int _dictExpandIfNeeded(dict *d)\n{\n    /* Incremental rehashing already in progress. Return. */\n    if (dictIsRehashing(d)) return DICT_OK;\n\n    /* If the hash table is empty expand it to the initial size. */\n    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);\n\n    /* If we reached the 1:1 ratio, and we are allowed to resize the hash\n     * table (global setting) or we should avoid it but the ratio between\n     * elements/buckets is over the \"safe\" threshold, we resize doubling\n     * the number of buckets. */\n    if (d->ht[0].used >= d->ht[0].size &&\n        (dict_can_resize ||\n         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))\n    {\n        return dictExpand(d, d->ht[0].used*2);\n    }\n    return DICT_OK;\n}\n\n/* Our hash table capability is a power of two */\nstatic unsigned long _dictNextPower(unsigned long size)\n{\n    unsigned long i = DICT_HT_INITIAL_SIZE;\n\n    if (size >= LONG_MAX) return LONG_MAX + 1LU;\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 * and the optional output parameter may be filled.\n *\n * Note that if we are in the process of rehashing the hash table, the\n * index is always returned in the context of the second (new) hash table. */\nstatic long _dictKeyIndex(dict *d, const void *key, uint64_t hash, dictEntry **existing)\n{\n    unsigned long idx, table;\n    dictEntry *he;\n    if (existing) *existing = NULL;\n\n    /* Expand the hash table if needed */\n    if (_dictExpandIfNeeded(d) == DICT_ERR)\n        return -1;\n    for (table = 0; table <= 1; table++) {\n        idx = hash & d->ht[table].sizemask;\n        /* Search if this slot does not already contain the given key */\n        he = d->ht[table].table[idx];\n        while(he) {\n            if (key==he->key || dictCompareKeys(d, key, he->key)) {\n                if (existing) *existing = he;\n                return -1;\n            }\n            he = he->next;\n        }\n        if (!dictIsRehashing(d)) break;\n    }\n    return idx;\n}\n\nvoid dictEmpty(dict *d, void(callback)(void*)) {\n    _dictClear(d,&d->ht[0],callback);\n    _dictClear(d,&d->ht[1],callback);\n    d->rehashidx = -1;\n    d->iterators = 0;\n}\n\nvoid dictEnableResize(void) {\n    dict_can_resize = 1;\n}\n\nvoid dictDisableResize(void) {\n    dict_can_resize = 0;\n}\n\nuint64_t dictGetHash(dict *d, const void *key) {\n    return dictHashKey(d, key);\n}\n\n/* Finds the dictEntry reference by using pointer and pre-calculated hash.\n * oldkey is a dead pointer and should not be accessed.\n * the hash value should be provided using dictGetHash.\n * no string / key comparison is performed.\n * return value is the reference to the dictEntry if found, or NULL if not found. */\ndictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash) {\n    dictEntry *he, **heref;\n    unsigned long idx, table;\n\n    if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */\n    for (table = 0; table <= 1; table++) {\n        idx = hash & d->ht[table].sizemask;\n        heref = &d->ht[table].table[idx];\n        he = *heref;\n        while(he) {\n            if (oldptr==he->key)\n                return heref;\n            heref = &he->next;\n            he = *heref;\n        }\n        if (!dictIsRehashing(d)) return NULL;\n    }\n    return NULL;\n}\n\n/* ------------------------------- Debugging ---------------------------------*/\n\n#define DICT_STATS_VECTLEN 50\nsize_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {\n    unsigned long i, slots = 0, chainlen, maxchainlen = 0;\n    unsigned long totchainlen = 0;\n    unsigned long clvector[DICT_STATS_VECTLEN];\n    size_t l = 0;\n\n    if (ht->used == 0) {\n        return snprintf(buf,bufsize,\n            \"No stats available for empty dictionaries\\n\");\n    }\n\n    /* Compute stats. */\n    for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;\n    for (i = 0; i < ht->size; i++) {\n        dictEntry *he;\n\n        if (ht->table[i] == NULL) {\n            clvector[0]++;\n            continue;\n        }\n        slots++;\n        /* For each hash entry on this slot... */\n        chainlen = 0;\n        he = ht->table[i];\n        while(he) {\n            chainlen++;\n            he = he->next;\n        }\n        clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++;\n        if (chainlen > maxchainlen) maxchainlen = chainlen;\n        totchainlen += chainlen;\n    }\n\n    /* Generate human readable stats. */\n    l += snprintf(buf+l,bufsize-l,\n        \"Hash table %d stats (%s):\\n\"\n        \" table size: %ld\\n\"\n        \" number of elements: %ld\\n\"\n        \" different slots: %ld\\n\"\n        \" max chain length: %ld\\n\"\n        \" avg chain length (counted): %.02f\\n\"\n        \" avg chain length (computed): %.02f\\n\"\n        \" Chain length distribution:\\n\",\n        tableid, (tableid == 0) ? \"main hash table\" : \"rehashing target\",\n        ht->size, ht->used, slots, maxchainlen,\n        (float)totchainlen/slots, (float)ht->used/slots);\n\n    for (i = 0; i < DICT_STATS_VECTLEN-1; i++) {\n        if (clvector[i] == 0) continue;\n        if (l >= bufsize) break;\n        l += snprintf(buf+l,bufsize-l,\n            \"   %s%ld: %ld (%.02f%%)\\n\",\n            (i == DICT_STATS_VECTLEN-1)?\">= \":\"\",\n            i, clvector[i], ((float)clvector[i]/ht->size)*100);\n    }\n\n    /* Unlike snprintf(), teturn the number of characters actually written. */\n    if (bufsize) buf[bufsize-1] = '\\0';\n    return strlen(buf);\n}\n\nvoid dictGetStats(char *buf, size_t bufsize, dict *d) {\n    size_t l;\n    char *orig_buf = buf;\n    size_t orig_bufsize = bufsize;\n\n    l = _dictGetStatsHt(buf,bufsize,&d->ht[0],0);\n    buf += l;\n    bufsize -= l;\n    if (dictIsRehashing(d) && bufsize > 0) {\n        _dictGetStatsHt(buf,bufsize,&d->ht[1],1);\n    }\n    /* Make sure there is a NULL term at the end. */\n    if (orig_bufsize) orig_buf[orig_bufsize-1] = '\\0';\n}\n\n/* ------------------------------- Benchmark ---------------------------------*/\n\n#ifdef DICT_BENCHMARK_MAIN\n\n#include \"sds.h\"\n\nuint64_t hashCallback(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nint compareCallback(void *privdata, const void *key1, const void *key2) {\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\nvoid freeCallback(void *privdata, void *val) {\n    DICT_NOTUSED(privdata);\n\n    sdsfree(val);\n}\n\ndictType BenchmarkDictType = {\n    hashCallback,\n    NULL,\n    NULL,\n    compareCallback,\n    freeCallback,\n    NULL\n};\n\n#define start_benchmark() start = timeInMilliseconds()\n#define end_benchmark(msg) do { \\\n    elapsed = timeInMilliseconds()-start; \\\n    printf(msg \": %ld items in %lld ms\\n\", count, elapsed); \\\n} while(0);\n\n/* dict-benchmark [count] */\nint main(int argc, char **argv) {\n    long j;\n    long long start, elapsed;\n    dict *dict = dictCreate(&BenchmarkDictType,NULL);\n    long count = 0;\n\n    if (argc == 2) {\n        count = strtol(argv[1],NULL,10);\n    } else {\n        count = 5000000;\n    }\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        int retval = dictAdd(dict,sdsfromlonglong(j),(void*)j);\n        assert(retval == DICT_OK);\n    }\n    end_benchmark(\"Inserting\");\n    assert((long)dictSize(dict) == count);\n\n    /* Wait for rehashing. */\n    while (dictIsRehashing(dict)) {\n        dictRehashMilliseconds(dict,100);\n    }\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(j);\n        dictEntry *de = dictFind(dict,key);\n        assert(de != NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Linear access of existing elements\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(j);\n        dictEntry *de = dictFind(dict,key);\n        assert(de != NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Linear access of existing elements (2nd round)\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(rand() % count);\n        dictEntry *de = dictFind(dict,key);\n        assert(de != NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Random access of existing elements\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(rand() % count);\n        key[0] = 'X';\n        dictEntry *de = dictFind(dict,key);\n        assert(de == NULL);\n        sdsfree(key);\n    }\n    end_benchmark(\"Accessing missing\");\n\n    start_benchmark();\n    for (j = 0; j < count; j++) {\n        sds key = sdsfromlonglong(j);\n        int retval = dictDelete(dict,key);\n        assert(retval == DICT_OK);\n        key[0] += 17; /* Change first number to letter. */\n        retval = dictAdd(dict,key,(void*)j);\n        assert(retval == DICT_OK);\n    }\n    end_benchmark(\"Removing and adding\");\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 * 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#define DICT_OK 0\n#define DICT_ERR 1\n\n/* Unused arguments generate annoying warnings... */\n#define DICT_NOTUSED(V) ((void) V)\n\ntypedef struct dictEntry {\n    void *key;\n    union {\n        void *val;\n        uint64_t u64;\n        int64_t s64;\n        double d;\n    } v;\n    struct dictEntry *next;\n} dictEntry;\n\ntypedef struct dictType {\n    uint64_t (*hashFunction)(const void *key);\n    void *(*keyDup)(void *privdata, const void *key);\n    void *(*valDup)(void *privdata, const void *obj);\n    int (*keyCompare)(void *privdata, const void *key1, const void *key2);\n    void (*keyDestructor)(void *privdata, void *key);\n    void (*valDestructor)(void *privdata, void *obj);\n} dictType;\n\n/* This is our hash table structure. Every dictionary has two of this as we\n * implement incremental rehashing, for the old to the new table. */\ntypedef struct dictht {\n    dictEntry **table;\n    unsigned long size;\n    unsigned long sizemask;\n    unsigned long used;\n} dictht;\n\ntypedef struct dict {\n    dictType *type;\n    void *privdata;\n    dictht ht[2];\n    long rehashidx; /* rehashing not in progress if rehashidx == -1 */\n    unsigned long iterators; /* number of iterators currently running */\n} dict;\n\n/* If safe is set to 1 this is a safe iterator, that means, you can call\n * dictAdd, dictFind, and other functions against the dictionary even while\n * iterating. Otherwise it is a non safe iterator, and only dictNext()\n * should be called while iterating. */\ntypedef struct dictIterator {\n    dict *d;\n    long index;\n    int table, safe;\n    dictEntry *entry, *nextEntry;\n    /* unsafe iterator fingerprint for misuse detection. */\n    long long fingerprint;\n} dictIterator;\n\ntypedef void (dictScanFunction)(void *privdata, const dictEntry *de);\ntypedef void (dictScanBucketFunction)(void *privdata, dictEntry **bucketref);\n\n/* This is the initial size of every hash table */\n#define DICT_HT_INITIAL_SIZE     4\n\n/* ------------------------------- Macros ------------------------------------*/\n#define dictFreeVal(d, entry) \\\n    if ((d)->type->valDestructor) \\\n        (d)->type->valDestructor((d)->privdata, (entry)->v.val)\n\n#define dictSetVal(d, entry, _val_) do { \\\n    if ((d)->type->valDup) \\\n        (entry)->v.val = (d)->type->valDup((d)->privdata, _val_); \\\n    else \\\n        (entry)->v.val = (_val_); \\\n} while(0)\n\n#define dictSetSignedIntegerVal(entry, _val_) \\\n    do { (entry)->v.s64 = _val_; } while(0)\n\n#define dictSetUnsignedIntegerVal(entry, _val_) \\\n    do { (entry)->v.u64 = _val_; } while(0)\n\n#define dictSetDoubleVal(entry, _val_) \\\n    do { (entry)->v.d = _val_; } while(0)\n\n#define dictFreeKey(d, entry) \\\n    if ((d)->type->keyDestructor) \\\n        (d)->type->keyDestructor((d)->privdata, (entry)->key)\n\n#define dictSetKey(d, entry, _key_) do { \\\n    if ((d)->type->keyDup) \\\n        (entry)->key = (d)->type->keyDup((d)->privdata, _key_); \\\n    else \\\n        (entry)->key = (_key_); \\\n} while(0)\n\n#define dictCompareKeys(d, key1, key2) \\\n    (((d)->type->keyCompare) ? \\\n        (d)->type->keyCompare((d)->privdata, key1, key2) : \\\n        (key1) == (key2))\n\n#define dictHashKey(d, key) (d)->type->hashFunction(key)\n#define dictGetKey(he) ((he)->key)\n#define dictGetVal(he) ((he)->v.val)\n#define dictGetSignedIntegerVal(he) ((he)->v.s64)\n#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)\n#define dictGetDoubleVal(he) ((he)->v.d)\n#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)\n#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)\n#define dictIsRehashing(d) ((d)->rehashidx != -1)\n\n/* API */\ndict *dictCreate(dictType *type, void *privDataPtr);\nint dictExpand(dict *d, unsigned long size);\nint dictAdd(dict *d, void *key, void *val);\ndictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing);\ndictEntry *dictAddOrFind(dict *d, void *key);\nint dictReplace(dict *d, void *key, void *val);\nint dictDelete(dict *d, const void *key);\ndictEntry *dictUnlink(dict *ht, const void *key);\nvoid dictFreeUnlinkedEntry(dict *d, dictEntry *he);\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);\ndictEntry *dictGetFairRandomKey(dict *d);\nunsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);\nvoid dictGetStats(char *buf, size_t bufsize, dict *d);\nuint64_t dictGenHashFunction(const void *key, int len);\nuint64_t 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(uint8_t *seed);\nuint8_t *dictGetHashFunctionSeed(void);\nunsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanBucketFunction *bucketfn, void *privdata);\nuint64_t dictGetHash(dict *d, const void *key);\ndictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash);\n\n/* Hash table types */\nextern dictType dictTypeHeapStringCopyKey;\nextern dictType dictTypeHeapStrings;\nextern dictType dictTypeHeapStringCopyKeyValue;\n\n#endif /* __DICT_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 REDIS_TEST\n#include <stdio.h>\n\n#define UNUSED(x) (void)(x)\nint endianconvTest(int argc, char *argv[]) {\n    char buf[32];\n\n    UNUSED(argc);\n    UNUSED(argv);\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": "Chapter07/redis-6.0.3/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 conversion only if the target\n * host is big endian */\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define memrev16ifbe(p) ((void)(0))\n#define memrev32ifbe(p) ((void)(0))\n#define memrev64ifbe(p) ((void)(0))\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#ifdef REDIS_TEST\nint endianconvTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/evict.c",
    "content": "/* Maxmemory directive handling (LRU eviction and other policies).\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2016, 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 \"server.h\"\n#include \"bio.h\"\n#include \"atomicvar.h\"\n\n/* ----------------------------------------------------------------------------\n * Data structures\n * --------------------------------------------------------------------------*/\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 * When an LFU policy is used instead, a reverse frequency indication is used\n * instead of the idle time, so that we still evict by larger value (larger\n * inverse frequency means to evict keys with the least frequent accesses).\n *\n * Empty entries have the key pointer set to NULL. */\n#define EVPOOL_SIZE 16\n#define EVPOOL_CACHED_SDS_SIZE 255\nstruct evictionPoolEntry {\n    unsigned long long idle;    /* Object idle time (inverse frequency for LFU) */\n    sds key;                    /* Key name. */\n    sds cached;                 /* Cached SDS object for key name. */\n    int dbid;                   /* Key DB number. */\n};\n\nstatic struct evictionPoolEntry *EvictionPoolLRU;\n\n/* ----------------------------------------------------------------------------\n * Implementation of eviction, aging and LRU\n * --------------------------------------------------------------------------*/\n\n/* Return the LRU clock, based on the clock resolution. This is a time\n * in a reduced-bits format that can be used to set and check the\n * object->lru field of redisObject structures. */\nunsigned int getLRUClock(void) {\n    return (mstime()/LRU_CLOCK_RESOLUTION) & LRU_CLOCK_MAX;\n}\n\n/* This function is 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 system call. */\nunsigned int LRU_CLOCK(void) {\n    unsigned int lruclock;\n    if (1000/server.hz <= LRU_CLOCK_RESOLUTION) {\n        lruclock = server.lruclock;\n    } else {\n        lruclock = getLRUClock();\n    }\n    return lruclock;\n}\n\n/* Given an object returns the min number of milliseconds the object was never\n * requested, using an approximated LRU algorithm. */\nunsigned long long estimateObjectIdleTime(robj *o) {\n    unsigned long long lruclock = LRU_CLOCK();\n    if (lruclock >= o->lru) {\n        return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;\n    } else {\n        return (lruclock + (LRU_CLOCK_MAX - o->lru)) *\n                    LRU_CLOCK_RESOLUTION;\n    }\n}\n\n/* 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 * The goal of the function is to free enough memory to keep Redis under the\n * configured memory limit.\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 * If all the bytes needed to return back under the limit were freed the\n * function returns C_OK, otherwise C_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 * ------------------------------------------------------------------------\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 EVPOOL_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. */\nvoid evictionPoolAlloc(void) {\n    struct evictionPoolEntry *ep;\n    int j;\n\n    ep = zmalloc(sizeof(*ep)*EVPOOL_SIZE);\n    for (j = 0; j < EVPOOL_SIZE; j++) {\n        ep[j].idle = 0;\n        ep[j].key = NULL;\n        ep[j].cached = sdsnewlen(NULL,EVPOOL_CACHED_SDS_SIZE);\n        ep[j].dbid = 0;\n    }\n    EvictionPoolLRU = 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\nvoid evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {\n    int j, k, count;\n    dictEntry *samples[server.maxmemory_samples];\n\n    count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples);\n    for (j = 0; j < count; j++) {\n        unsigned long long idle;\n        sds key;\n        robj *o;\n        dictEntry *de;\n\n        de = samples[j];\n        key = dictGetKey(de);\n\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 (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {\n            if (sampledict != keydict) de = dictFind(keydict, key);\n            o = dictGetVal(de);\n        }\n\n        /* Calculate the idle time according to the policy. This is called\n         * idle just because the code initially handled LRU, but is in fact\n         * just a score where an higher score means better candidate. */\n        if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {\n            idle = estimateObjectIdleTime(o);\n        } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n            /* When we use an LRU policy, we sort the keys by idle time\n             * so that we expire keys starting from greater idle time.\n             * However when the policy is an LFU one, we have a frequency\n             * estimation, and we want to evict keys with lower frequency\n             * first. So inside the pool we put objects using the inverted\n             * frequency subtracting the actual frequency to the maximum\n             * frequency of 255. */\n            idle = 255-LFUDecrAndReturn(o);\n        } else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {\n            /* In this case the sooner the expire the better. */\n            idle = ULLONG_MAX - (long)dictGetVal(de);\n        } else {\n            serverPanic(\"Unknown eviction policy in evictionPoolPopulate()\");\n        }\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 < EVPOOL_SIZE &&\n               pool[k].key &&\n               pool[k].idle < idle) k++;\n        if (k == 0 && pool[EVPOOL_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 < EVPOOL_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[EVPOOL_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\n                /* Save SDS before overwriting. */\n                sds cached = pool[EVPOOL_SIZE-1].cached;\n                memmove(pool+k+1,pool+k,\n                    sizeof(pool[0])*(EVPOOL_SIZE-k-1));\n                pool[k].cached = cached;\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                sds cached = pool[0].cached; /* Save SDS before overwriting. */\n                if (pool[0].key != pool[0].cached) sdsfree(pool[0].key);\n                memmove(pool,pool+1,sizeof(pool[0])*k);\n                pool[k].cached = cached;\n            }\n        }\n\n        /* Try to reuse the cached SDS string allocated in the pool entry,\n         * because allocating and deallocating this object is costly\n         * (according to the profiler, not my fantasy. Remember:\n         * premature optimizbla bla bla bla. */\n        int klen = sdslen(key);\n        if (klen > EVPOOL_CACHED_SDS_SIZE) {\n            pool[k].key = sdsdup(key);\n        } else {\n            memcpy(pool[k].cached,key,klen+1);\n            sdssetlen(pool[k].cached,klen);\n            pool[k].key = pool[k].cached;\n        }\n        pool[k].idle = idle;\n        pool[k].dbid = dbid;\n    }\n}\n\n/* ----------------------------------------------------------------------------\n * LFU (Least Frequently Used) implementation.\n\n * We have 24 total bits of space in each object in order to implement\n * an LFU (Least Frequently Used) eviction policy, since we re-use the\n * LRU field for this purpose.\n *\n * We split the 24 bits into two fields:\n *\n *          16 bits      8 bits\n *     +----------------+--------+\n *     + Last decr time | LOG_C  |\n *     +----------------+--------+\n *\n * LOG_C is a logarithmic counter that provides an indication of the access\n * frequency. However this field must also be decremented otherwise what used\n * to be a frequently accessed key in the past, will remain ranked like that\n * forever, while we want the algorithm to adapt to access pattern changes.\n *\n * So the remaining 16 bits are used in order to store the \"decrement time\",\n * a reduced-precision Unix time (we take 16 bits of the time converted\n * in minutes since we don't care about wrapping around) where the LOG_C\n * counter is halved if it has an high value, or just decremented if it\n * has a low value.\n *\n * New keys don't start at zero, in order to have the ability to collect\n * some accesses before being trashed away, so they start at COUNTER_INIT_VAL.\n * The logarithmic increment performed on LOG_C takes care of COUNTER_INIT_VAL\n * when incrementing the key, so that keys starting at COUNTER_INIT_VAL\n * (or having a smaller value) have a very high chance of being incremented\n * on access.\n *\n * During decrement, the value of the logarithmic counter is halved if\n * its current value is greater than two times the COUNTER_INIT_VAL, otherwise\n * it is just decremented by one.\n * --------------------------------------------------------------------------*/\n\n/* Return the current time in minutes, just taking the least significant\n * 16 bits. The returned time is suitable to be stored as LDT (last decrement\n * time) for the LFU implementation. */\nunsigned long LFUGetTimeInMinutes(void) {\n    return (server.unixtime/60) & 65535;\n}\n\n/* Given an object last access time, compute the minimum number of minutes\n * that elapsed since the last access. Handle overflow (ldt greater than\n * the current 16 bits minutes time) considering the time as wrapping\n * exactly once. */\nunsigned long LFUTimeElapsed(unsigned long ldt) {\n    unsigned long now = LFUGetTimeInMinutes();\n    if (now >= ldt) return now-ldt;\n    return 65535-ldt+now;\n}\n\n/* Logarithmically increment a counter. The greater is the current counter value\n * the less likely is that it gets really implemented. Saturate it at 255. */\nuint8_t LFULogIncr(uint8_t counter) {\n    if (counter == 255) return 255;\n    double r = (double)rand()/RAND_MAX;\n    double baseval = counter - LFU_INIT_VAL;\n    if (baseval < 0) baseval = 0;\n    double p = 1.0/(baseval*server.lfu_log_factor+1);\n    if (r < p) counter++;\n    return counter;\n}\n\n/* If the object decrement time is reached decrement the LFU counter but\n * do not update LFU fields of the object, we update the access time\n * and counter in an explicit way when the object is really accessed.\n * And we will times halve the counter according to the times of\n * elapsed time than server.lfu_decay_time.\n * Return the object frequency counter.\n *\n * This function is used in order to scan the dataset for the best object\n * to fit: as we check for the candidate, we incrementally decrement the\n * counter of the scanned objects if needed. */\nunsigned long LFUDecrAndReturn(robj *o) {\n    unsigned long ldt = o->lru >> 8;\n    unsigned long counter = o->lru & 255;\n    unsigned long num_periods = server.lfu_decay_time ? LFUTimeElapsed(ldt) / server.lfu_decay_time : 0;\n    if (num_periods)\n        counter = (num_periods > counter) ? 0 : counter - num_periods;\n    return counter;\n}\n\n/* ----------------------------------------------------------------------------\n * The external API for eviction: freeMemroyIfNeeded() is called by the\n * server when there is data to add in order to make space if needed.\n * --------------------------------------------------------------------------*/\n\n/* We don't want to count AOF buffers and slaves output buffers as\n * used memory: the eviction should use mostly data size. This function\n * returns the sum of AOF and slaves buffer. */\nsize_t freeMemoryGetNotCountedMemory(void) {\n    size_t overhead = 0;\n    int slaves = listLength(server.slaves);\n\n    if (slaves) {\n        listIter li;\n        listNode *ln;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = listNodeValue(ln);\n            overhead += getClientOutputBufferMemoryUsage(slave);\n        }\n    }\n    if (server.aof_state != AOF_OFF) {\n        overhead += sdsalloc(server.aof_buf)+aofRewriteBufferSize();\n    }\n    return overhead;\n}\n\n/* Get the memory status from the point of view of the maxmemory directive:\n * if the memory used is under the maxmemory setting then C_OK is returned.\n * Otherwise, if we are over the memory limit, the function returns\n * C_ERR.\n *\n * The function may return additional info via reference, only if the\n * pointers to the respective arguments is not NULL. Certain fields are\n * populated only when C_ERR is returned:\n *\n *  'total'     total amount of bytes used.\n *              (Populated both for C_ERR and C_OK)\n *\n *  'logical'   the amount of memory used minus the slaves/AOF buffers.\n *              (Populated when C_ERR is returned)\n *\n *  'tofree'    the amount of memory that should be released\n *              in order to return back into the memory limits.\n *              (Populated when C_ERR is returned)\n *\n *  'level'     this usually ranges from 0 to 1, and reports the amount of\n *              memory currently used. May be > 1 if we are over the memory\n *              limit.\n *              (Populated both for C_ERR and C_OK)\n */\nint getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level) {\n    size_t mem_reported, mem_used, mem_tofree;\n\n    /* Check if we are over the memory usage limit. If we are not, no need\n     * to subtract the slaves output buffers. We can just return ASAP. */\n    mem_reported = zmalloc_used_memory();\n    if (total) *total = mem_reported;\n\n    /* We may return ASAP if there is no need to compute the level. */\n    int return_ok_asap = !server.maxmemory || mem_reported <= server.maxmemory;\n    if (return_ok_asap && !level) return C_OK;\n\n    /* Remove the size of slaves output buffers and AOF buffer from the\n     * count of used memory. */\n    mem_used = mem_reported;\n    size_t overhead = freeMemoryGetNotCountedMemory();\n    mem_used = (mem_used > overhead) ? mem_used-overhead : 0;\n\n    /* Compute the ratio of memory usage. */\n    if (level) {\n        if (!server.maxmemory) {\n            *level = 0;\n        } else {\n            *level = (float)mem_used / (float)server.maxmemory;\n        }\n    }\n\n    if (return_ok_asap) return C_OK;\n\n    /* Check if we are still over the memory limit. */\n    if (mem_used <= server.maxmemory) return C_OK;\n\n    /* Compute how much memory we need to free. */\n    mem_tofree = mem_used - server.maxmemory;\n\n    if (logical) *logical = mem_used;\n    if (tofree) *tofree = mem_tofree;\n\n    return C_ERR;\n}\n\n/* This function is periodically called to see if there is memory to free\n * according to the current \"maxmemory\" settings. In case we are over the\n * memory limit, the function will try to free some memory to return back\n * under the limit.\n *\n * The function returns C_OK if we are under the memory limit or if we\n * were over the limit, but the attempt to free memory was successful.\n * Otehrwise if we are over the memory limit, but not enough memory\n * was freed to return back under the limit, the function returns C_ERR. */\nint freeMemoryIfNeeded(void) {\n    int keys_freed = 0;\n    /* By default replicas should ignore maxmemory\n     * and just be masters exact copies. */\n    if (server.masterhost && server.repl_slave_ignore_maxmemory) return C_OK;\n\n    size_t mem_reported, mem_tofree, mem_freed;\n    mstime_t latency, eviction_latency, lazyfree_latency;\n    long long delta;\n    int slaves = listLength(server.slaves);\n    int result = C_ERR;\n\n    /* When clients are paused the dataset should be static not just from the\n     * POV of clients not being able to write, but also from the POV of\n     * expires and evictions of keys not being performed. */\n    if (clientsArePaused()) return C_OK;\n    if (getMaxmemoryState(&mem_reported,NULL,&mem_tofree,NULL) == C_OK)\n        return C_OK;\n\n    mem_freed = 0;\n\n    latencyStartMonitor(latency);\n    if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)\n        goto cant_free; /* We need to free memory, but policy forbids. */\n\n    while (mem_freed < mem_tofree) {\n        int j, k, i;\n        static unsigned int next_db = 0;\n        sds bestkey = NULL;\n        int bestdbid;\n        redisDb *db;\n        dict *dict;\n        dictEntry *de;\n\n        if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||\n            server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)\n        {\n            struct evictionPoolEntry *pool = EvictionPoolLRU;\n\n            while(bestkey == NULL) {\n                unsigned long total_keys = 0, keys;\n\n                /* We don't want to make local-db choices when expiring keys,\n                 * so to start populate the eviction pool sampling keys from\n                 * every DB. */\n                for (i = 0; i < server.dbnum; i++) {\n                    db = server.db+i;\n                    dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?\n                            db->dict : db->expires;\n                    if ((keys = dictSize(dict)) != 0) {\n                        evictionPoolPopulate(i, dict, db->dict, pool);\n                        total_keys += keys;\n                    }\n                }\n                if (!total_keys) break; /* No keys to evict. */\n\n                /* Go backward from best to worst element to evict. */\n                for (k = EVPOOL_SIZE-1; k >= 0; k--) {\n                    if (pool[k].key == NULL) continue;\n                    bestdbid = pool[k].dbid;\n\n                    if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {\n                        de = dictFind(server.db[pool[k].dbid].dict,\n                            pool[k].key);\n                    } else {\n                        de = dictFind(server.db[pool[k].dbid].expires,\n                            pool[k].key);\n                    }\n\n                    /* Remove the entry from the pool. */\n                    if (pool[k].key != pool[k].cached)\n                        sdsfree(pool[k].key);\n                    pool[k].key = NULL;\n                    pool[k].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... Iterate again. */\n                    }\n                }\n            }\n        }\n\n        /* volatile-random and allkeys-random policy */\n        else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||\n                 server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)\n        {\n            /* When evicting a random key, we try to evict a key for\n             * each DB, so we use the static 'next_db' variable to\n             * incrementally visit all DBs. */\n            for (i = 0; i < server.dbnum; i++) {\n                j = (++next_db) % server.dbnum;\n                db = server.db+j;\n                dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) ?\n                        db->dict : db->expires;\n                if (dictSize(dict) != 0) {\n                    de = dictGetRandomKey(dict);\n                    bestkey = dictGetKey(de);\n                    bestdbid = j;\n                    break;\n                }\n            }\n        }\n\n        /* Finally remove the selected key. */\n        if (bestkey) {\n            db = server.db+bestdbid;\n            robj *keyobj = createStringObject(bestkey,sdslen(bestkey));\n            propagateExpire(db,keyobj,server.lazyfree_lazy_eviction);\n            /* We compute the amount of memory freed by db*Delete() 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            delta = (long long) zmalloc_used_memory();\n            latencyStartMonitor(eviction_latency);\n            if (server.lazyfree_lazy_eviction)\n                dbAsyncDelete(db,keyobj);\n            else\n                dbSyncDelete(db,keyobj);\n            signalModifiedKey(NULL,db,keyobj);\n            latencyEndMonitor(eviction_latency);\n            latencyAddSampleIfNeeded(\"eviction-del\",eviction_latency);\n            delta -= (long long) zmalloc_used_memory();\n            mem_freed += delta;\n            server.stat_evictedkeys++;\n            notifyKeyspaceEvent(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            /* Normally our stop condition is the ability to release\n             * a fixed, pre-computed amount of memory. However when we\n             * are deleting objects in another thread, it's better to\n             * check, from time to time, if we already reached our target\n             * memory, since the \"mem_freed\" amount is computed only\n             * across the dbAsyncDelete() call, while the thread can\n             * release the memory all the time. */\n            if (server.lazyfree_lazy_eviction && !(keys_freed % 16)) {\n                if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {\n                    /* Let's satisfy our stop condition. */\n                    mem_freed = mem_tofree;\n                }\n            }\n        } else {\n            goto cant_free; /* nothing to free... */\n        }\n    }\n    result = C_OK;\n\ncant_free:\n    /* We are here if we are not able to reclaim memory. There is only one\n     * last thing we can try: check if the lazyfree thread has jobs in queue\n     * and wait... */\n    if (result != C_OK) {\n        latencyStartMonitor(lazyfree_latency);\n        while(bioPendingJobsOfType(BIO_LAZY_FREE)) {\n            if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {\n                result = C_OK;\n                break;\n            }\n            usleep(1000);\n        }\n        latencyEndMonitor(lazyfree_latency);\n        latencyAddSampleIfNeeded(\"eviction-lazyfree\",lazyfree_latency);\n    }\n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"eviction-cycle\",latency);\n    return result;\n}\n\n/* This is a wrapper for freeMemoryIfNeeded() that only really calls the\n * function if right now there are the conditions to do so safely:\n *\n * - There must be no script in timeout condition.\n * - Nor we are loading data right now.\n *\n */\nint freeMemoryIfNeededAndSafe(void) {\n    if (server.lua_timedout || server.loading) return C_OK;\n    return freeMemoryIfNeeded();\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/expire.c",
    "content": "/* Implementation of EXPIRE (keys with fixed time to live).\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2016, 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 \"server.h\"\n\n/*-----------------------------------------------------------------------------\n * Incremental collection of expired keys.\n *\n * When keys are accessed they are expired on-access. However we need a\n * mechanism in order to ensure keys are eventually removed when expired even\n * if no access is performed on them.\n *----------------------------------------------------------------------------*/\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 * 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 * 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. */\nint activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {\n    long long t = dictGetSignedIntegerVal(de);\n    if (now > t) {\n        sds key = dictGetKey(de);\n        robj *keyobj = createStringObject(key,sdslen(key));\n\n        propagateExpire(db,keyobj,server.lazyfree_lazy_expire);\n        if (server.lazyfree_lazy_expire)\n            dbAsyncDelete(db,keyobj);\n        else\n            dbSyncDelete(db,keyobj);\n        notifyKeyspaceEvent(NOTIFY_EXPIRED,\n            \"expired\",keyobj,db->id);\n        trackingInvalidateKey(NULL,keyobj);\n        decrRefCount(keyobj);\n        server.stat_expiredkeys++;\n        return 1;\n    } else {\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 * Every expire cycle tests multiple databases: the next call will start\n * again from the next db, with the exception of exists for time limit: in that\n * case we restart again from the last database we were processing. Anyway\n * no more than CRON_DBS_PER_CALL databases are tested at every iteration.\n *\n * The function can perform more or less work, depending on the \"type\"\n * argument. It can execute a \"fast cycle\" or a \"slow cycle\". The slow\n * cycle is the main way we collect expired cycles: this happens with\n * the \"server.hz\" frequency (usually 10 hertz).\n *\n * However the slow cycle can exit for timeout, since it used too much time.\n * For this reason the function is also invoked to perform a fast cycle\n * at every event loop cycle, in the beforeSleep() function. The fast cycle\n * will try to perform less work, but will do it much more often.\n *\n * The following are the details of the two expire cycles and their stop\n * conditions:\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 * The cycle will also refuse to run at all if the latest slow cycle did not\n * terminate because of a time limit condition.\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 ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC define. In the\n * fast cycle, the check of every database is interrupted once the number\n * of already expired keys in the database is estimated to be lower than\n * a given percentage, in order to avoid doing too much work to gain too\n * little memory.\n *\n * The configured expire \"effort\" will modify the baseline parameters in\n * order to do more work in both the fast and slow expire cycles.\n */\n\n#define ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP 20 /* Keys for each DB loop. */\n#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds. */\n#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* Max % of CPU to use. */\n#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10 /* % of stale keys after which\n                                                   we do extra efforts. */\n\nvoid activeExpireCycle(int type) {\n    /* Adjust the running parameters according to the configured expire\n     * effort. The default effort is 1, and the maximum configurable effort\n     * is 10. */\n    unsigned long\n    effort = server.active_expire_effort-1, /* Rescale from 0 to 9. */\n    config_keys_per_loop = ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP +\n                           ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP/4*effort,\n    config_cycle_fast_duration = ACTIVE_EXPIRE_CYCLE_FAST_DURATION +\n                                 ACTIVE_EXPIRE_CYCLE_FAST_DURATION/4*effort,\n    config_cycle_slow_time_perc = ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC +\n                                  2*effort,\n    config_cycle_acceptable_stale = ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE-\n                                    effort;\n\n    /* This function has some global state in order to continue the work\n     * incrementally across calls. */\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    int j, iteration = 0;\n    int dbs_per_call = CRON_DBS_PER_CALL;\n    long long start = ustime(), timelimit, elapsed;\n\n    /* When clients are paused the dataset should be static not just from the\n     * POV of clients not being able to write, but also from the POV of\n     * expires and evictions of keys not being performed. */\n    if (clientsArePaused()) return;\n\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST) {\n        /* Don't start a fast cycle if the previous cycle did not exit\n         * for time limit, unless the percentage of estimated stale keys is\n         * too high. Also never repeat a fast cycle for the same period\n         * as the fast cycle total duration itself. */\n        if (!timelimit_exit &&\n            server.stat_expired_stale_perc < config_cycle_acceptable_stale)\n            return;\n\n        if (start < last_fast_cycle + (long long)config_cycle_fast_duration*2)\n            return;\n\n        last_fast_cycle = start;\n    }\n\n    /* We usually should test CRON_DBS_PER_CALL per iteration, with\n     * two exceptions:\n     *\n     * 1) Don't test more DBs than we have.\n     * 2) If last time we hit the time limit, we want to scan all DBs\n     * in this iteration, as there is work to do in some DB and we don't want\n     * expired keys to use memory for too much time. */\n    if (dbs_per_call > server.dbnum || timelimit_exit)\n        dbs_per_call = server.dbnum;\n\n    /* We can use at max 'config_cycle_slow_time_perc' percentage of CPU\n     * time per iteration. Since this function gets called with a frequency of\n     * server.hz times per second, the following is the max amount of\n     * microseconds we can spend in this function. */\n    timelimit = config_cycle_slow_time_perc*1000000/server.hz/100;\n    timelimit_exit = 0;\n    if (timelimit <= 0) timelimit = 1;\n\n    if (type == ACTIVE_EXPIRE_CYCLE_FAST)\n        timelimit = config_cycle_fast_duration; /* in microseconds. */\n\n    /* Accumulate some global stats as we expire keys, to have some idea\n     * about the number of keys that are already logically expired, but still\n     * existing inside the database. */\n    long total_sampled = 0;\n    long total_expired = 0;\n\n    for (j = 0; j < dbs_per_call && timelimit_exit == 0; j++) {\n        /* Expired and checked in a single loop. */\n        unsigned long expired, sampled;\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        current_db++;\n\n        /* Continue to expire if at the end of the cycle there are still\n         * a big percentage of keys to expire, compared to the number of keys\n         * we scanned. The percentage, stored in config_cycle_acceptable_stale\n         * is not fixed, but depends on the Redis configured \"expire effort\". */\n        do {\n            unsigned long num, slots;\n            long long now, ttl_sum;\n            int ttl_samples;\n            iteration++;\n\n            /* If there is nothing to expire try next DB ASAP. */\n            if ((num = dictSize(db->expires)) == 0) {\n                db->avg_ttl = 0;\n                break;\n            }\n            slots = dictSlots(db->expires);\n            now = mstime();\n\n            /* When there are less than 1% filled slots, sampling the key\n             * space is expensive, so stop here waiting for better times...\n             * The dictionary will be resized asap. */\n            if (num && slots > DICT_HT_INITIAL_SIZE &&\n                (num*100/slots < 1)) break;\n\n            /* The main collection cycle. Sample random keys among keys\n             * with an expire set, checking for expired ones. */\n            expired = 0;\n            sampled = 0;\n            ttl_sum = 0;\n            ttl_samples = 0;\n\n            if (num > config_keys_per_loop)\n                num = config_keys_per_loop;\n\n            /* Here we access the low level representation of the hash table\n             * for speed concerns: this makes this code coupled with dict.c,\n             * but it hardly changed in ten years.\n             *\n             * Note that certain places of the hash table may be empty,\n             * so we want also a stop condition about the number of\n             * buckets that we scanned. However scanning for free buckets\n             * is very fast: we are in the cache line scanning a sequential\n             * array of NULL pointers, so we can scan a lot more buckets\n             * than keys in the same time. */\n            long max_buckets = num*20;\n            long checked_buckets = 0;\n\n            while (sampled < num && checked_buckets < max_buckets) {\n                for (int table = 0; table < 2; table++) {\n                    if (table == 1 && !dictIsRehashing(db->expires)) break;\n\n                    unsigned long idx = db->expires_cursor;\n                    idx &= db->expires->ht[table].sizemask;\n                    dictEntry *de = db->expires->ht[table].table[idx];\n                    long long ttl;\n\n                    /* Scan the current bucket of the current table. */\n                    checked_buckets++;\n                    while(de) {\n                        /* Get the next entry now since this entry may get\n                         * deleted. */\n                        dictEntry *e = de;\n                        de = de->next;\n\n                        ttl = dictGetSignedIntegerVal(e)-now;\n                        if (activeExpireCycleTryExpire(db,e,now)) expired++;\n                        if (ttl > 0) {\n                            /* We want the average TTL of keys yet\n                             * not expired. */\n                            ttl_sum += ttl;\n                            ttl_samples++;\n                        }\n                        sampled++;\n                    }\n                }\n                db->expires_cursor++;\n            }\n            total_expired += expired;\n            total_sampled += sampled;\n\n            /* Update the average TTL stats for this database. */\n            if (ttl_samples) {\n                long long avg_ttl = ttl_sum/ttl_samples;\n\n                /* Do a simple running average with a few samples.\n                 * We just use the current estimate with a weight of 2%\n                 * and the previous estimate with a weight of 98%. */\n                if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;\n                db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);\n            }\n\n            /* We can't block forever here even if there are many keys to\n             * expire. So after a given amount of milliseconds return to the\n             * caller waiting for the other active expire cycle. */\n            if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */\n                elapsed = ustime()-start;\n                if (elapsed > timelimit) {\n                    timelimit_exit = 1;\n                    server.stat_expired_time_cap_reached_count++;\n                    break;\n                }\n            }\n            /* We don't repeat the cycle for the current database if there are\n             * an acceptable amount of stale keys (logically expired but yet\n             * not reclaimed). */\n        } while (sampled == 0 ||\n                 (expired*100/sampled) > config_cycle_acceptable_stale);\n    }\n\n    elapsed = ustime()-start;\n    server.stat_expire_cycle_time_used += elapsed;\n    latencyAddSampleIfNeeded(\"expire-cycle\",elapsed/1000);\n\n    /* Update our estimate of keys existing but yet to be expired.\n     * Running average with this sample accounting for 5%. */\n    double current_perc;\n    if (total_sampled) {\n        current_perc = (double)total_expired/total_sampled;\n    } else\n        current_perc = 0;\n    server.stat_expired_stale_perc = (current_perc*0.05)+\n                                     (server.stat_expired_stale_perc*0.95);\n}\n\n/*-----------------------------------------------------------------------------\n * Expires of keys created in writable slaves\n *\n * Normally slaves do not process expires: they wait the masters to synthesize\n * DEL operations in order to retain consistency. However writable slaves are\n * an exception: if a key is created in the slave and an expire is assigned\n * to it, we need a way to expire such a key, since the master does not know\n * anything about such a key.\n *\n * In order to do so, we track keys created in the slave side with an expire\n * set, and call the expireSlaveKeys() function from time to time in order to\n * reclaim the keys if they already expired.\n *\n * Note that the use case we are trying to cover here, is a popular one where\n * slaves are put in writable mode in order to compute slow operations in\n * the slave side that are mostly useful to actually read data in a more\n * processed way. Think at sets intersections in a tmp key, with an expire so\n * that it is also used as a cache to avoid intersecting every time.\n *\n * This implementation is currently not perfect but a lot better than leaking\n * the keys as implemented in 3.2.\n *----------------------------------------------------------------------------*/\n\n/* The dictionary where we remember key names and database ID of keys we may\n * want to expire from the slave. Since this function is not often used we\n * don't even care to initialize the database at startup. We'll do it once\n * the feature is used the first time, that is, when rememberSlaveKeyWithExpire()\n * is called.\n *\n * The dictionary has an SDS string representing the key as the hash table\n * key, while the value is a 64 bit unsigned integer with the bits corresponding\n * to the DB where the keys may exist set to 1. Currently the keys created\n * with a DB id > 63 are not expired, but a trivial fix is to set the bitmap\n * to the max 64 bit unsigned value when we know there is a key with a DB\n * ID greater than 63, and check all the configured DBs in such a case. */\ndict *slaveKeysWithExpire = NULL;\n\n/* Check the set of keys created by the master with an expire set in order to\n * check if they should be evicted. */\nvoid expireSlaveKeys(void) {\n    if (slaveKeysWithExpire == NULL ||\n        dictSize(slaveKeysWithExpire) == 0) return;\n\n    int cycles = 0, noexpire = 0;\n    mstime_t start = mstime();\n    while(1) {\n        dictEntry *de = dictGetRandomKey(slaveKeysWithExpire);\n        sds keyname = dictGetKey(de);\n        uint64_t dbids = dictGetUnsignedIntegerVal(de);\n        uint64_t new_dbids = 0;\n\n        /* Check the key against every database corresponding to the\n         * bits set in the value bitmap. */\n        int dbid = 0;\n        while(dbids && dbid < server.dbnum) {\n            if ((dbids & 1) != 0) {\n                redisDb *db = server.db+dbid;\n                dictEntry *expire = dictFind(db->expires,keyname);\n                int expired = 0;\n\n                if (expire &&\n                    activeExpireCycleTryExpire(server.db+dbid,expire,start))\n                {\n                    expired = 1;\n                }\n\n                /* If the key was not expired in this DB, we need to set the\n                 * corresponding bit in the new bitmap we set as value.\n                 * At the end of the loop if the bitmap is zero, it means we\n                 * no longer need to keep track of this key. */\n                if (expire && !expired) {\n                    noexpire++;\n                    new_dbids |= (uint64_t)1 << dbid;\n                }\n            }\n            dbid++;\n            dbids >>= 1;\n        }\n\n        /* Set the new bitmap as value of the key, in the dictionary\n         * of keys with an expire set directly in the writable slave. Otherwise\n         * if the bitmap is zero, we no longer need to keep track of it. */\n        if (new_dbids)\n            dictSetUnsignedIntegerVal(de,new_dbids);\n        else\n            dictDelete(slaveKeysWithExpire,keyname);\n\n        /* Stop conditions: found 3 keys we cna't expire in a row or\n         * time limit was reached. */\n        cycles++;\n        if (noexpire > 3) break;\n        if ((cycles % 64) == 0 && mstime()-start > 1) break;\n        if (dictSize(slaveKeysWithExpire) == 0) break;\n    }\n}\n\n/* Track keys that received an EXPIRE or similar command in the context\n * of a writable slave. */\nvoid rememberSlaveKeyWithExpire(redisDb *db, robj *key) {\n    if (slaveKeysWithExpire == NULL) {\n        static dictType dt = {\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        slaveKeysWithExpire = dictCreate(&dt,NULL);\n    }\n    if (db->id > 63) return;\n\n    dictEntry *de = dictAddOrFind(slaveKeysWithExpire,key->ptr);\n    /* If the entry was just created, set it to a copy of the SDS string\n     * representing the key: we don't want to need to take those keys\n     * in sync with the main DB. The keys will be removed by expireSlaveKeys()\n     * as it scans to find keys to remove. */\n    if (de->key == key->ptr) {\n        de->key = sdsdup(key->ptr);\n        dictSetUnsignedIntegerVal(de,0);\n    }\n\n    uint64_t dbids = dictGetUnsignedIntegerVal(de);\n    dbids |= (uint64_t)1 << db->id;\n    dictSetUnsignedIntegerVal(de,dbids);\n}\n\n/* Return the number of keys we are tracking. */\nsize_t getSlaveKeyWithExpireCount(void) {\n    if (slaveKeysWithExpire == NULL) return 0;\n    return dictSize(slaveKeysWithExpire);\n}\n\n/* Remove the keys in the hash table. We need to do that when data is\n * flushed from the server. We may receive new keys from the master with\n * the same name/db and it is no longer a good idea to expire them.\n *\n * Note: technically we should handle the case of a single DB being flushed\n * but it is not worth it since anyway race conditions using the same set\n * of key names in a wriatable slave and in its master will lead to\n * inconsistencies. This is just a best-effort thing we do. */\nvoid flushSlaveKeysWithExpireList(void) {\n    if (slaveKeysWithExpire) {\n        dictRelease(slaveKeysWithExpire);\n        slaveKeysWithExpire = NULL;\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Expires Commands\n *----------------------------------------------------------------------------*/\n\n/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT\n * and PEXPIREAT. Because the commad second argument may be relative or absolute\n * the \"basetime\" argument is used to signal what the base time is (either 0\n * for *AT variants of the command, or the current time for relative expires).\n *\n * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for\n * the argv[2] parameter. The basetime is always specified in milliseconds. */\nvoid expireGenericCommand(client *c, long long basetime, int unit) {\n    robj *key = c->argv[1], *param = c->argv[2];\n    long long when; /* unix time in milliseconds when the key will expire. */\n\n    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != C_OK)\n        return;\n\n    if (unit == UNIT_SECONDS) when *= 1000;\n    when += basetime;\n\n    /* No key, return zero. */\n    if (lookupKeyWrite(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     * Instead we take the other branch of the IF statement setting an expire\n     * (possibly in the past) and wait for an explicit DEL from the master. */\n    if (when <= mstime() && !server.loading && !server.masterhost) {\n        robj *aux;\n\n        int deleted = server.lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :\n                                                    dbSyncDelete(c->db,key);\n        serverAssertWithInfo(c,key,deleted);\n        server.dirty++;\n\n        /* Replicate/AOF this as an explicit DEL or UNLINK. */\n        aux = server.lazyfree_lazy_expire ? shared.unlink : shared.del;\n        rewriteClientCommandVector(c,2,aux,key);\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n        addReply(c, shared.cone);\n        return;\n    } else {\n        setExpire(c,c->db,key,when);\n        addReply(c,shared.cone);\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"expire\",key,c->db->id);\n        server.dirty++;\n        return;\n    }\n}\n\n/* EXPIRE key seconds */\nvoid expireCommand(client *c) {\n    expireGenericCommand(c,mstime(),UNIT_SECONDS);\n}\n\n/* EXPIREAT key time */\nvoid expireatCommand(client *c) {\n    expireGenericCommand(c,0,UNIT_SECONDS);\n}\n\n/* PEXPIRE key milliseconds */\nvoid pexpireCommand(client *c) {\n    expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);\n}\n\n/* PEXPIREAT key ms_time */\nvoid pexpireatCommand(client *c) {\n    expireGenericCommand(c,0,UNIT_MILLISECONDS);\n}\n\n/* Implements TTL and PTTL */\nvoid ttlGenericCommand(client *c, int output_ms) {\n    long long expire, ttl = -1;\n\n    /* If the key does not exist at all, return -2 */\n    if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == NULL) {\n        addReplyLongLong(c,-2);\n        return;\n    }\n    /* The key exists. Return -1 if it has no expire, or the actual\n     * TTL value otherwise. */\n    expire = getExpire(c->db,c->argv[1]);\n    if (expire != -1) {\n        ttl = expire-mstime();\n        if (ttl < 0) ttl = 0;\n    }\n    if (ttl == -1) {\n        addReplyLongLong(c,-1);\n    } else {\n        addReplyLongLong(c,output_ms ? ttl : ((ttl+500)/1000));\n    }\n}\n\n/* TTL key */\nvoid ttlCommand(client *c) {\n    ttlGenericCommand(c, 0);\n}\n\n/* PTTL key */\nvoid pttlCommand(client *c) {\n    ttlGenericCommand(c, 1);\n}\n\n/* PERSIST key */\nvoid persistCommand(client *c) {\n    if (lookupKeyWrite(c->db,c->argv[1])) {\n        if (removeExpire(c->db,c->argv[1])) {\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"persist\",c->argv[1],c->db->id);\n            addReply(c,shared.cone);\n            server.dirty++;\n        } else {\n            addReply(c,shared.czero);\n        }\n    } else {\n        addReply(c,shared.czero);\n    }\n}\n\n/* TOUCH key1 [key2 key3 ... keyN] */\nvoid touchCommand(client *c) {\n    int touched = 0;\n    for (int j = 1; j < c->argc; j++)\n        if (lookupKeyRead(c->db,c->argv[j]) != NULL) touched++;\n    addReplyLongLong(c,touched);\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#define _DEFAULT_SOURCE\n#endif\n\n#if defined(_AIX)\n#define _ALL_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#if defined(__sun)\n#define _POSIX_C_SOURCE 199506L\n#endif\n\n#define _LARGEFILE_SOURCE\n#define _FILE_OFFSET_BITS 64\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/geo.c",
    "content": "/*\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@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#include \"geo.h\"\n#include \"geohash_helper.h\"\n#include \"debugmacro.h\"\n\n/* Things exported from t_zset.c only for geo.c, since it is the only other\n * part of Redis that requires close zset introspection. */\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);\nint zslValueLteMax(double value, zrangespec *spec);\n\n/* ====================================================================\n * This file implements the following commands:\n *\n *   - geoadd - add coordinates for value to geoset\n *   - georadius - search radius by coordinates in geoset\n *   - georadiusbymember - search radius based on geoset member position\n * ==================================================================== */\n\n/* ====================================================================\n * geoArray implementation\n * ==================================================================== */\n\n/* Create a new array of geoPoints. */\ngeoArray *geoArrayCreate(void) {\n    geoArray *ga = zmalloc(sizeof(*ga));\n    /* It gets allocated on first geoArrayAppend() call. */\n    ga->array = NULL;\n    ga->buckets = 0;\n    ga->used = 0;\n    return ga;\n}\n\n/* Add a new entry and return its pointer so that the caller can populate\n * it with data. */\ngeoPoint *geoArrayAppend(geoArray *ga) {\n    if (ga->used == ga->buckets) {\n        ga->buckets = (ga->buckets == 0) ? 8 : ga->buckets*2;\n        ga->array = zrealloc(ga->array,sizeof(geoPoint)*ga->buckets);\n    }\n    geoPoint *gp = ga->array+ga->used;\n    ga->used++;\n    return gp;\n}\n\n/* Destroy a geoArray created with geoArrayCreate(). */\nvoid geoArrayFree(geoArray *ga) {\n    size_t i;\n    for (i = 0; i < ga->used; i++) sdsfree(ga->array[i].member);\n    zfree(ga->array);\n    zfree(ga);\n}\n\n/* ====================================================================\n * Helpers\n * ==================================================================== */\nint decodeGeohash(double bits, double *xy) {\n    GeoHashBits hash = { .bits = (uint64_t)bits, .step = GEO_STEP_MAX };\n    return geohashDecodeToLongLatWGS84(hash, xy);\n}\n\n/* Input Argument Helper */\n/* Take a pointer to the latitude arg then use the next arg for longitude.\n * On parse error C_ERR is returned, otherwise C_OK. */\nint extractLongLatOrReply(client *c, robj **argv, double *xy) {\n    int i;\n    for (i = 0; i < 2; i++) {\n        if (getDoubleFromObjectOrReply(c, argv[i], xy + i, NULL) !=\n            C_OK) {\n            return C_ERR;\n        }\n    }\n    if (xy[0] < GEO_LONG_MIN || xy[0] > GEO_LONG_MAX ||\n        xy[1] < GEO_LAT_MIN  || xy[1] > GEO_LAT_MAX) {\n        addReplySds(c, sdscatprintf(sdsempty(),\n            \"-ERR invalid longitude,latitude pair %f,%f\\r\\n\",xy[0],xy[1]));\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* Input Argument Helper */\n/* Decode lat/long from a zset member's score.\n * Returns C_OK on successful decoding, otherwise C_ERR is returned. */\nint longLatFromMember(robj *zobj, robj *member, double *xy) {\n    double score = 0;\n\n    if (zsetScore(zobj, member->ptr, &score) == C_ERR) return C_ERR;\n    if (!decodeGeohash(score, xy)) return C_ERR;\n    return C_OK;\n}\n\n/* Check that the unit argument matches one of the known units, and returns\n * the conversion factor to meters (you need to divide meters by the conversion\n * factor to convert to the right unit).\n *\n * If the unit is not valid, an error is reported to the client, and a value\n * less than zero is returned. */\ndouble extractUnitOrReply(client *c, robj *unit) {\n    char *u = unit->ptr;\n\n    if (!strcmp(u, \"m\")) {\n        return 1;\n    } else if (!strcmp(u, \"km\")) {\n        return 1000;\n    } else if (!strcmp(u, \"ft\")) {\n        return 0.3048;\n    } else if (!strcmp(u, \"mi\")) {\n        return 1609.34;\n    } else {\n        addReplyError(c,\n            \"unsupported unit provided. please use m, km, ft, mi\");\n        return -1;\n    }\n}\n\n/* Input Argument Helper.\n * Extract the dinstance from the specified two arguments starting at 'argv'\n * that shouldbe in the form: <number> <unit> and return the dinstance in the\n * specified unit on success. *conversions is populated with the coefficient\n * to use in order to convert meters to the unit.\n *\n * On error a value less than zero is returned. */\ndouble extractDistanceOrReply(client *c, robj **argv,\n                                     double *conversion) {\n    double distance;\n    if (getDoubleFromObjectOrReply(c, argv[0], &distance,\n                                   \"need numeric radius\") != C_OK) {\n        return -1;\n    }\n\n    if (distance < 0) {\n        addReplyError(c,\"radius cannot be negative\");\n        return -1;\n    }\n\n    double to_meters = extractUnitOrReply(c,argv[1]);\n    if (to_meters < 0) {\n        return -1;\n    }\n\n    if (conversion) *conversion = to_meters;\n    return distance * to_meters;\n}\n\n/* The default addReplyDouble has too much accuracy.  We use this\n * for returning location distances. \"5.2145 meters away\" is nicer\n * than \"5.2144992818115 meters away.\" We provide 4 digits after the dot\n * so that the returned value is decently accurate even when the unit is\n * the kilometer. */\nvoid addReplyDoubleDistance(client *c, double d) {\n    char dbuf[128];\n    int dlen = snprintf(dbuf, sizeof(dbuf), \"%.4f\", d);\n    addReplyBulkCBuffer(c, dbuf, dlen);\n}\n\n/* Helper function for geoGetPointsInRange(): given a sorted set score\n * representing a point, and another point (the center of our search) and\n * a radius, appends this entry as a geoPoint into the specified geoArray\n * only if the point is within the search area.\n *\n * returns C_OK if the point is included, or REIDS_ERR if it is outside. */\nint geoAppendIfWithinRadius(geoArray *ga, double lon, double lat, double radius, double score, sds member) {\n    double distance, xy[2];\n\n    if (!decodeGeohash(score,xy)) return C_ERR; /* Can't decode. */\n    /* Note that geohashGetDistanceIfInRadiusWGS84() takes arguments in\n     * reverse order: longitude first, latitude later. */\n    if (!geohashGetDistanceIfInRadiusWGS84(lon,lat, xy[0], xy[1],\n                                           radius, &distance))\n    {\n        return C_ERR;\n    }\n\n    /* Append the new element. */\n    geoPoint *gp = geoArrayAppend(ga);\n    gp->longitude = xy[0];\n    gp->latitude = xy[1];\n    gp->dist = distance;\n    gp->member = member;\n    gp->score = score;\n    return C_OK;\n}\n\n/* Query a Redis sorted set to extract all the elements between 'min' and\n * 'max', appending them into the array of geoPoint structures 'gparray'.\n * The command returns the number of elements added to the array.\n *\n * Elements which are farest than 'radius' from the specified 'x' and 'y'\n * coordinates are not included.\n *\n * The ability of this function to append to an existing set of points is\n * important for good performances because querying by radius is performed\n * using multiple queries to the sorted set, that we later need to sort\n * via qsort. Similarly we need to be able to reject points outside the search\n * radius area ASAP in order to allocate and process more points than needed. */\nint geoGetPointsInRange(robj *zobj, double min, double max, double lon, double lat, double radius, geoArray *ga) {\n    /* minex 0 = include min in range; maxex 1 = exclude max in range */\n    /* That's: min <= val < max */\n    zrangespec range = { .min = min, .max = max, .minex = 0, .maxex = 1 };\n    size_t origincount = ga->used;\n    sds member;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr = NULL;\n        unsigned int vlen = 0;\n        long long vlong = 0;\n        double score = 0;\n\n        if ((eptr = zzlFirstInRange(zl, &range)) == NULL) {\n            /* Nothing exists starting at our min.  No results. */\n            return 0;\n        }\n\n        sptr = ziplistNext(zl, eptr);\n        while (eptr) {\n            score = zzlGetScore(sptr);\n\n            /* If we fell out of range, break. */\n            if (!zslValueLteMax(score, &range))\n                break;\n\n            /* We know the element exists. ziplistGet should always succeed */\n            ziplistGet(eptr, &vstr, &vlen, &vlong);\n            member = (vstr == NULL) ? sdsfromlonglong(vlong) :\n                                      sdsnewlen(vstr,vlen);\n            if (geoAppendIfWithinRadius(ga,lon,lat,radius,score,member)\n                == C_ERR) sdsfree(member);\n            zzlNext(zl, &eptr, &sptr);\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        if ((ln = zslFirstInRange(zsl, &range)) == NULL) {\n            /* Nothing exists starting at our min.  No results. */\n            return 0;\n        }\n\n        while (ln) {\n            sds ele = ln->ele;\n            /* Abort when the node is no longer in range. */\n            if (!zslValueLteMax(ln->score, &range))\n                break;\n\n            ele = sdsdup(ele);\n            if (geoAppendIfWithinRadius(ga,lon,lat,radius,ln->score,ele)\n                == C_ERR) sdsfree(ele);\n            ln = ln->level[0].forward;\n        }\n    }\n    return ga->used - origincount;\n}\n\n/* Compute the sorted set scores min (inclusive), max (exclusive) we should\n * query in order to retrieve all the elements inside the specified area\n * 'hash'. The two scores are returned by reference in *min and *max. */\nvoid scoresOfGeoHashBox(GeoHashBits hash, GeoHashFix52Bits *min, GeoHashFix52Bits *max) {\n    /* We want to compute the sorted set scores that will include all the\n     * elements inside the specified Geohash 'hash', which has as many\n     * bits as specified by hash.step * 2.\n     *\n     * So if step is, for example, 3, and the hash value in binary\n     * is 101010, since our score is 52 bits we want every element which\n     * is in binary: 101010?????????????????????????????????????????????\n     * Where ? can be 0 or 1.\n     *\n     * To get the min score we just use the initial hash value left\n     * shifted enough to get the 52 bit value. Later we increment the\n     * 6 bit prefis (see the hash.bits++ statement), and get the new\n     * prefix: 101011, which we align again to 52 bits to get the maximum\n     * value (which is excluded from the search). So we get everything\n     * between the two following scores (represented in binary):\n     *\n     * 1010100000000000000000000000000000000000000000000000 (included)\n     * and\n     * 1010110000000000000000000000000000000000000000000000 (excluded).\n     */\n    *min = geohashAlign52Bits(hash);\n    hash.bits++;\n    *max = geohashAlign52Bits(hash);\n}\n\n/* Obtain all members between the min/max of this geohash bounding box.\n * Populate a geoArray of GeoPoints by calling geoGetPointsInRange().\n * Return the number of points added to the array. */\nint membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon, double lat, double radius) {\n    GeoHashFix52Bits min, max;\n\n    scoresOfGeoHashBox(hash,&min,&max);\n    return geoGetPointsInRange(zobj, min, max, lon, lat, radius, ga);\n}\n\n/* Search all eight neighbors + self geohash box */\nint membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) {\n    GeoHashBits neighbors[9];\n    unsigned int i, count = 0, last_processed = 0;\n    int debugmsg = 0;\n\n    neighbors[0] = n.hash;\n    neighbors[1] = n.neighbors.north;\n    neighbors[2] = n.neighbors.south;\n    neighbors[3] = n.neighbors.east;\n    neighbors[4] = n.neighbors.west;\n    neighbors[5] = n.neighbors.north_east;\n    neighbors[6] = n.neighbors.north_west;\n    neighbors[7] = n.neighbors.south_east;\n    neighbors[8] = n.neighbors.south_west;\n\n    /* For each neighbor (*and* our own hashbox), get all the matching\n     * members and add them to the potential result list. */\n    for (i = 0; i < sizeof(neighbors) / sizeof(*neighbors); i++) {\n        if (HASHISZERO(neighbors[i])) {\n            if (debugmsg) D(\"neighbors[%d] is zero\",i);\n            continue;\n        }\n\n        /* Debugging info. */\n        if (debugmsg) {\n            GeoHashRange long_range, lat_range;\n            geohashGetCoordRange(&long_range,&lat_range);\n            GeoHashArea myarea = {{0}};\n            geohashDecode(long_range, lat_range, neighbors[i], &myarea);\n\n            /* Dump center square. */\n            D(\"neighbors[%d]:\\n\",i);\n            D(\"area.longitude.min: %f\\n\", myarea.longitude.min);\n            D(\"area.longitude.max: %f\\n\", myarea.longitude.max);\n            D(\"area.latitude.min: %f\\n\", myarea.latitude.min);\n            D(\"area.latitude.max: %f\\n\", myarea.latitude.max);\n            D(\"\\n\");\n        }\n\n        /* When a huge Radius (in the 5000 km range or more) is used,\n         * adjacent neighbors can be the same, leading to duplicated\n         * elements. Skip every range which is the same as the one\n         * processed previously. */\n        if (last_processed &&\n            neighbors[i].bits == neighbors[last_processed].bits &&\n            neighbors[i].step == neighbors[last_processed].step)\n        {\n            if (debugmsg)\n                D(\"Skipping processing of %d, same as previous\\n\",i);\n            continue;\n        }\n        count += membersOfGeoHashBox(zobj, neighbors[i], ga, lon, lat, radius);\n        last_processed = i;\n    }\n    return count;\n}\n\n/* Sort comparators for qsort() */\nstatic int sort_gp_asc(const void *a, const void *b) {\n    const struct geoPoint *gpa = a, *gpb = b;\n    /* We can't do adist - bdist because they are doubles and\n     * the comparator returns an int. */\n    if (gpa->dist > gpb->dist)\n        return 1;\n    else if (gpa->dist == gpb->dist)\n        return 0;\n    else\n        return -1;\n}\n\nstatic int sort_gp_desc(const void *a, const void *b) {\n    return -sort_gp_asc(a, b);\n}\n\n/* ====================================================================\n * Commands\n * ==================================================================== */\n\n/* GEOADD key long lat name [long2 lat2 name2 ... longN latN nameN] */\nvoid geoaddCommand(client *c) {\n    /* Check arguments number for sanity. */\n    if ((c->argc - 2) % 3 != 0) {\n        /* Need an odd number of arguments if we got this far... */\n        addReplyError(c, \"syntax error. Try GEOADD key [x1] [y1] [name1] \"\n                         \"[x2] [y2] [name2] ... \");\n        return;\n    }\n\n    int elements = (c->argc - 2) / 3;\n    int argc = 2+elements*2; /* ZADD key score ele ... */\n    robj **argv = zcalloc(argc*sizeof(robj*));\n    argv[0] = createRawStringObject(\"zadd\",4);\n    argv[1] = c->argv[1]; /* key */\n    incrRefCount(argv[1]);\n\n    /* Create the argument vector to call ZADD in order to add all\n     * the score,value pairs to the requested zset, where score is actually\n     * an encoded version of lat,long. */\n    int i;\n    for (i = 0; i < elements; i++) {\n        double xy[2];\n\n        if (extractLongLatOrReply(c, (c->argv+2)+(i*3),xy) == C_ERR) {\n            for (i = 0; i < argc; i++)\n                if (argv[i]) decrRefCount(argv[i]);\n            zfree(argv);\n            return;\n        }\n\n        /* Turn the coordinates into the score of the element. */\n        GeoHashBits hash;\n        geohashEncodeWGS84(xy[0], xy[1], GEO_STEP_MAX, &hash);\n        GeoHashFix52Bits bits = geohashAlign52Bits(hash);\n        robj *score = createObject(OBJ_STRING, sdsfromlonglong(bits));\n        robj *val = c->argv[2 + i * 3 + 2];\n        argv[2+i*2] = score;\n        argv[3+i*2] = val;\n        incrRefCount(val);\n    }\n\n    /* Finally call ZADD that will do the work for us. */\n    replaceClientCommandVector(c,argc,argv);\n    zaddCommand(c);\n}\n\n#define SORT_NONE 0\n#define SORT_ASC 1\n#define SORT_DESC 2\n\n#define RADIUS_COORDS (1<<0)    /* Search around coordinates. */\n#define RADIUS_MEMBER (1<<1)    /* Search around member. */\n#define RADIUS_NOSTORE (1<<2)   /* Do not acceot STORE/STOREDIST option. */\n\n/* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]\n *                               [COUNT count] [STORE key] [STOREDIST key]\n * GEORADIUSBYMEMBER key member radius unit ... options ... */\nvoid georadiusGeneric(client *c, int flags) {\n    robj *key = c->argv[1];\n    robj *storekey = NULL;\n    int storedist = 0; /* 0 for STORE, 1 for STOREDIST. */\n\n    /* Look up the requested zset */\n    robj *zobj = NULL;\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.emptyarray)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET)) {\n        return;\n    }\n\n    /* Find long/lat to use for radius search based on inquiry type */\n    int base_args;\n    double xy[2] = { 0 };\n    if (flags & RADIUS_COORDS) {\n        base_args = 6;\n        if (extractLongLatOrReply(c, c->argv + 2, xy) == C_ERR)\n            return;\n    } else if (flags & RADIUS_MEMBER) {\n        base_args = 5;\n        robj *member = c->argv[2];\n        if (longLatFromMember(zobj, member, xy) == C_ERR) {\n            addReplyError(c, \"could not decode requested zset member\");\n            return;\n        }\n    } else {\n        addReplyError(c, \"Unknown georadius search type\");\n        return;\n    }\n\n    /* Extract radius and units from arguments */\n    double radius_meters = 0, conversion = 1;\n    if ((radius_meters = extractDistanceOrReply(c, c->argv + base_args - 2,\n                                                &conversion)) < 0) {\n        return;\n    }\n\n    /* Discover and populate all optional parameters. */\n    int withdist = 0, withhash = 0, withcoords = 0;\n    int sort = SORT_NONE;\n    long long count = 0;\n    if (c->argc > base_args) {\n        int remaining = c->argc - base_args;\n        for (int i = 0; i < remaining; i++) {\n            char *arg = c->argv[base_args + i]->ptr;\n            if (!strcasecmp(arg, \"withdist\")) {\n                withdist = 1;\n            } else if (!strcasecmp(arg, \"withhash\")) {\n                withhash = 1;\n            } else if (!strcasecmp(arg, \"withcoord\")) {\n                withcoords = 1;\n            } else if (!strcasecmp(arg, \"asc\")) {\n                sort = SORT_ASC;\n            } else if (!strcasecmp(arg, \"desc\")) {\n                sort = SORT_DESC;\n            } else if (!strcasecmp(arg, \"count\") && (i+1) < remaining) {\n                if (getLongLongFromObjectOrReply(c, c->argv[base_args+i+1],\n                    &count, NULL) != C_OK) return;\n                if (count <= 0) {\n                    addReplyError(c,\"COUNT must be > 0\");\n                    return;\n                }\n                i++;\n            } else if (!strcasecmp(arg, \"store\") &&\n                       (i+1) < remaining &&\n                       !(flags & RADIUS_NOSTORE))\n            {\n                storekey = c->argv[base_args+i+1];\n                storedist = 0;\n                i++;\n            } else if (!strcasecmp(arg, \"storedist\") &&\n                       (i+1) < remaining &&\n                       !(flags & RADIUS_NOSTORE))\n            {\n                storekey = c->argv[base_args+i+1];\n                storedist = 1;\n                i++;\n            } else {\n                addReply(c, shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* Trap options not compatible with STORE and STOREDIST. */\n    if (storekey && (withdist || withhash || withcoords)) {\n        addReplyError(c,\n            \"STORE option in GEORADIUS is not compatible with \"\n            \"WITHDIST, WITHHASH and WITHCOORDS options\");\n        return;\n    }\n\n    /* COUNT without ordering does not make much sense, force ASC\n     * ordering if COUNT was specified but no sorting was requested. */\n    if (count != 0 && sort == SORT_NONE) sort = SORT_ASC;\n\n    /* Get all neighbor geohash boxes for our radius search */\n    GeoHashRadius georadius =\n        geohashGetAreasByRadiusWGS84(xy[0], xy[1], radius_meters);\n\n    /* Search the zset for all matching points */\n    geoArray *ga = geoArrayCreate();\n    membersOfAllNeighbors(zobj, georadius, xy[0], xy[1], radius_meters, ga);\n\n    /* If no matching results, the user gets an empty reply. */\n    if (ga->used == 0 && storekey == NULL) {\n        addReply(c,shared.emptyarray);\n        geoArrayFree(ga);\n        return;\n    }\n\n    long result_length = ga->used;\n    long returned_items = (count == 0 || result_length < count) ?\n                          result_length : count;\n    long option_length = 0;\n\n    /* Process [optional] requested sorting */\n    if (sort == SORT_ASC) {\n        qsort(ga->array, result_length, sizeof(geoPoint), sort_gp_asc);\n    } else if (sort == SORT_DESC) {\n        qsort(ga->array, result_length, sizeof(geoPoint), sort_gp_desc);\n    }\n\n    if (storekey == NULL) {\n        /* No target key, return results to user. */\n\n        /* Our options are self-contained nested multibulk replies, so we\n         * only need to track how many of those nested replies we return. */\n        if (withdist)\n            option_length++;\n\n        if (withcoords)\n            option_length++;\n\n        if (withhash)\n            option_length++;\n\n        /* The array len we send is exactly result_length. The result is\n         * either all strings of just zset members  *or* a nested multi-bulk\n         * reply containing the zset member string _and_ all the additional\n         * options the user enabled for this request. */\n        addReplyArrayLen(c, returned_items);\n\n        /* Finally send results back to the caller */\n        int i;\n        for (i = 0; i < returned_items; i++) {\n            geoPoint *gp = ga->array+i;\n            gp->dist /= conversion; /* Fix according to unit. */\n\n            /* If we have options in option_length, return each sub-result\n             * as a nested multi-bulk.  Add 1 to account for result value\n             * itself. */\n            if (option_length)\n                addReplyArrayLen(c, option_length + 1);\n\n            addReplyBulkSds(c,gp->member);\n            gp->member = NULL;\n\n            if (withdist)\n                addReplyDoubleDistance(c, gp->dist);\n\n            if (withhash)\n                addReplyLongLong(c, gp->score);\n\n            if (withcoords) {\n                addReplyArrayLen(c, 2);\n                addReplyHumanLongDouble(c, gp->longitude);\n                addReplyHumanLongDouble(c, gp->latitude);\n            }\n        }\n    } else {\n        /* Target key, create a sorted set with the results. */\n        robj *zobj;\n        zset *zs;\n        int i;\n        size_t maxelelen = 0;\n\n        if (returned_items) {\n            zobj = createZsetObject();\n            zs = zobj->ptr;\n        }\n\n        for (i = 0; i < returned_items; i++) {\n            zskiplistNode *znode;\n            geoPoint *gp = ga->array+i;\n            gp->dist /= conversion; /* Fix according to unit. */\n            double score = storedist ? gp->dist : gp->score;\n            size_t elelen = sdslen(gp->member);\n\n            if (maxelelen < elelen) maxelelen = elelen;\n            znode = zslInsert(zs->zsl,score,gp->member);\n            serverAssert(dictAdd(zs->dict,gp->member,&znode->score) == DICT_OK);\n            gp->member = NULL;\n        }\n\n        if (returned_items) {\n            zsetConvertToZiplistIfNeeded(zobj,maxelelen);\n            setKey(c,c->db,storekey,zobj);\n            decrRefCount(zobj);\n            notifyKeyspaceEvent(NOTIFY_ZSET,\"georadiusstore\",storekey,\n                                c->db->id);\n            server.dirty += returned_items;\n        } else if (dbDelete(c->db,storekey)) {\n            signalModifiedKey(c,c->db,storekey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",storekey,c->db->id);\n            server.dirty++;\n        }\n        addReplyLongLong(c, returned_items);\n    }\n    geoArrayFree(ga);\n}\n\n/* GEORADIUS wrapper function. */\nvoid georadiusCommand(client *c) {\n    georadiusGeneric(c, RADIUS_COORDS);\n}\n\n/* GEORADIUSBYMEMBER wrapper function. */\nvoid georadiusbymemberCommand(client *c) {\n    georadiusGeneric(c, RADIUS_MEMBER);\n}\n\n/* GEORADIUS_RO wrapper function. */\nvoid georadiusroCommand(client *c) {\n    georadiusGeneric(c, RADIUS_COORDS|RADIUS_NOSTORE);\n}\n\n/* GEORADIUSBYMEMBER_RO wrapper function. */\nvoid georadiusbymemberroCommand(client *c) {\n    georadiusGeneric(c, RADIUS_MEMBER|RADIUS_NOSTORE);\n}\n\n/* GEOHASH key ele1 ele2 ... eleN\n *\n * Returns an array with an 11 characters geohash representation of the\n * position of the specified elements. */\nvoid geohashCommand(client *c) {\n    char *geoalphabet= \"0123456789bcdefghjkmnpqrstuvwxyz\";\n    int j;\n\n    /* Look up the requested zset */\n    robj *zobj = lookupKeyRead(c->db, c->argv[1]);\n    if (zobj && checkType(c, zobj, OBJ_ZSET)) return;\n\n    /* Geohash elements one after the other, using a null bulk reply for\n     * missing elements. */\n    addReplyArrayLen(c,c->argc-2);\n    for (j = 2; j < c->argc; j++) {\n        double score;\n        if (!zobj || zsetScore(zobj, c->argv[j]->ptr, &score) == C_ERR) {\n            addReplyNull(c);\n        } else {\n            /* The internal format we use for geocoding is a bit different\n             * than the standard, since we use as initial latitude range\n             * -85,85, while the normal geohashing algorithm uses -90,90.\n             * So we have to decode our position and re-encode using the\n             * standard ranges in order to output a valid geohash string. */\n\n            /* Decode... */\n            double xy[2];\n            if (!decodeGeohash(score,xy)) {\n                addReplyNull(c);\n                continue;\n            }\n\n            /* Re-encode */\n            GeoHashRange r[2];\n            GeoHashBits hash;\n            r[0].min = -180;\n            r[0].max = 180;\n            r[1].min = -90;\n            r[1].max = 90;\n            geohashEncode(&r[0],&r[1],xy[0],xy[1],26,&hash);\n\n            char buf[12];\n            int i;\n            for (i = 0; i < 11; i++) {\n                int idx;\n                if (i == 10) {\n                    /* We have just 52 bits, but the API used to output\n                     * an 11 bytes geohash. For compatibility we assume\n                     * zero. */\n                    idx = 0;\n                } else {\n                    idx = (hash.bits >> (52-((i+1)*5))) & 0x1f;\n                }\n                buf[i] = geoalphabet[idx];\n            }\n            buf[11] = '\\0';\n            addReplyBulkCBuffer(c,buf,11);\n        }\n    }\n}\n\n/* GEOPOS key ele1 ele2 ... eleN\n *\n * Returns an array of two-items arrays representing the x,y position of each\n * element specified in the arguments. For missing elements NULL is returned. */\nvoid geoposCommand(client *c) {\n    int j;\n\n    /* Look up the requested zset */\n    robj *zobj = lookupKeyRead(c->db, c->argv[1]);\n    if (zobj && checkType(c, zobj, OBJ_ZSET)) return;\n\n    /* Report elements one after the other, using a null bulk reply for\n     * missing elements. */\n    addReplyArrayLen(c,c->argc-2);\n    for (j = 2; j < c->argc; j++) {\n        double score;\n        if (!zobj || zsetScore(zobj, c->argv[j]->ptr, &score) == C_ERR) {\n            addReplyNullArray(c);\n        } else {\n            /* Decode... */\n            double xy[2];\n            if (!decodeGeohash(score,xy)) {\n                addReplyNullArray(c);\n                continue;\n            }\n            addReplyArrayLen(c,2);\n            addReplyHumanLongDouble(c,xy[0]);\n            addReplyHumanLongDouble(c,xy[1]);\n        }\n    }\n}\n\n/* GEODIST key ele1 ele2 [unit]\n *\n * Return the distance, in meters by default, otherwise accordig to \"unit\",\n * between points ele1 and ele2. If one or more elements are missing NULL\n * is returned. */\nvoid geodistCommand(client *c) {\n    double to_meter = 1;\n\n    /* Check if there is the unit to extract, otherwise assume meters. */\n    if (c->argc == 5) {\n        to_meter = extractUnitOrReply(c,c->argv[4]);\n        if (to_meter < 0) return;\n    } else if (c->argc > 5) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Look up the requested zset */\n    robj *zobj = NULL;\n    if ((zobj = lookupKeyReadOrReply(c, c->argv[1], shared.null[c->resp]))\n        == NULL || checkType(c, zobj, OBJ_ZSET)) return;\n\n    /* Get the scores. We need both otherwise NULL is returned. */\n    double score1, score2, xyxy[4];\n    if (zsetScore(zobj, c->argv[2]->ptr, &score1) == C_ERR ||\n        zsetScore(zobj, c->argv[3]->ptr, &score2) == C_ERR)\n    {\n        addReplyNull(c);\n        return;\n    }\n\n    /* Decode & compute the distance. */\n    if (!decodeGeohash(score1,xyxy) || !decodeGeohash(score2,xyxy+2))\n        addReplyNull(c);\n    else\n        addReplyDoubleDistance(c,\n            geohashGetDistance(xyxy[0],xyxy[1],xyxy[2],xyxy[3]) / to_meter);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/geo.h",
    "content": "#ifndef __GEO_H__\n#define __GEO_H__\n\n#include \"server.h\"\n\n/* Structures used inside geo.c in order to represent points and array of\n * points on the earth. */\ntypedef struct geoPoint {\n    double longitude;\n    double latitude;\n    double dist;\n    double score;\n    char *member;\n} geoPoint;\n\ntypedef struct geoArray {\n    struct geoPoint *array;\n    size_t buckets;\n    size_t used;\n} geoArray;\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/geohash.c",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n#include \"geohash.h\"\n\n/**\n * Hashing works like this:\n * Divide the world into 4 buckets.  Label each one as such:\n *  -----------------\n *  |       |       |\n *  |       |       |\n *  | 0,1   | 1,1   |\n *  -----------------\n *  |       |       |\n *  |       |       |\n *  | 0,0   | 1,0   |\n *  -----------------\n */\n\n/* Interleave lower bits of x and y, so the bits of x\n * are in the even positions and bits from y in the odd;\n * x and y must initially be less than 2**32 (65536).\n * From:  https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN\n */\nstatic inline uint64_t interleave64(uint32_t xlo, uint32_t ylo) {\n    static const uint64_t B[] = {0x5555555555555555ULL, 0x3333333333333333ULL,\n                                 0x0F0F0F0F0F0F0F0FULL, 0x00FF00FF00FF00FFULL,\n                                 0x0000FFFF0000FFFFULL};\n    static const unsigned int S[] = {1, 2, 4, 8, 16};\n\n    uint64_t x = xlo;\n    uint64_t y = ylo;\n\n    x = (x | (x << S[4])) & B[4];\n    y = (y | (y << S[4])) & B[4];\n\n    x = (x | (x << S[3])) & B[3];\n    y = (y | (y << S[3])) & B[3];\n\n    x = (x | (x << S[2])) & B[2];\n    y = (y | (y << S[2])) & B[2];\n\n    x = (x | (x << S[1])) & B[1];\n    y = (y | (y << S[1])) & B[1];\n\n    x = (x | (x << S[0])) & B[0];\n    y = (y | (y << S[0])) & B[0];\n\n    return x | (y << 1);\n}\n\n/* reverse the interleave process\n * derived from http://stackoverflow.com/questions/4909263\n */\nstatic inline uint64_t deinterleave64(uint64_t interleaved) {\n    static const uint64_t B[] = {0x5555555555555555ULL, 0x3333333333333333ULL,\n                                 0x0F0F0F0F0F0F0F0FULL, 0x00FF00FF00FF00FFULL,\n                                 0x0000FFFF0000FFFFULL, 0x00000000FFFFFFFFULL};\n    static const unsigned int S[] = {0, 1, 2, 4, 8, 16};\n\n    uint64_t x = interleaved;\n    uint64_t y = interleaved >> 1;\n\n    x = (x | (x >> S[0])) & B[0];\n    y = (y | (y >> S[0])) & B[0];\n\n    x = (x | (x >> S[1])) & B[1];\n    y = (y | (y >> S[1])) & B[1];\n\n    x = (x | (x >> S[2])) & B[2];\n    y = (y | (y >> S[2])) & B[2];\n\n    x = (x | (x >> S[3])) & B[3];\n    y = (y | (y >> S[3])) & B[3];\n\n    x = (x | (x >> S[4])) & B[4];\n    y = (y | (y >> S[4])) & B[4];\n\n    x = (x | (x >> S[5])) & B[5];\n    y = (y | (y >> S[5])) & B[5];\n\n    return x | (y << 32);\n}\n\nvoid geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range) {\n    /* These are constraints from EPSG:900913 / EPSG:3785 / OSGEO:41001 */\n    /* We can't geocode at the north/south pole. */\n    long_range->max = GEO_LONG_MAX;\n    long_range->min = GEO_LONG_MIN;\n    lat_range->max = GEO_LAT_MAX;\n    lat_range->min = GEO_LAT_MIN;\n}\n\nint geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,\n                  double longitude, double latitude, uint8_t step,\n                  GeoHashBits *hash) {\n    /* Check basic arguments sanity. */\n    if (hash == NULL || step > 32 || step == 0 ||\n        RANGEPISZERO(lat_range) || RANGEPISZERO(long_range)) return 0;\n\n    /* Return an error when trying to index outside the supported\n     * constraints. */\n    if (longitude > GEO_LONG_MAX || longitude < GEO_LONG_MIN ||\n        latitude > GEO_LAT_MAX || latitude < GEO_LAT_MIN) return 0;\n\n    hash->bits = 0;\n    hash->step = step;\n\n    if (latitude < lat_range->min || latitude > lat_range->max ||\n        longitude < long_range->min || longitude > long_range->max) {\n        return 0;\n    }\n\n    double lat_offset =\n        (latitude - lat_range->min) / (lat_range->max - lat_range->min);\n    double long_offset =\n        (longitude - long_range->min) / (long_range->max - long_range->min);\n\n    /* convert to fixed point based on the step size */\n    lat_offset *= (1ULL << step);\n    long_offset *= (1ULL << step);\n    hash->bits = interleave64(lat_offset, long_offset);\n    return 1;\n}\n\nint geohashEncodeType(double longitude, double latitude, uint8_t step, GeoHashBits *hash) {\n    GeoHashRange r[2] = {{0}};\n    geohashGetCoordRange(&r[0], &r[1]);\n    return geohashEncode(&r[0], &r[1], longitude, latitude, step, hash);\n}\n\nint geohashEncodeWGS84(double longitude, double latitude, uint8_t step,\n                       GeoHashBits *hash) {\n    return geohashEncodeType(longitude, latitude, step, hash);\n}\n\nint geohashDecode(const GeoHashRange long_range, const GeoHashRange lat_range,\n                   const GeoHashBits hash, GeoHashArea *area) {\n    if (HASHISZERO(hash) || NULL == area || RANGEISZERO(lat_range) ||\n        RANGEISZERO(long_range)) {\n        return 0;\n    }\n\n    area->hash = hash;\n    uint8_t step = hash.step;\n    uint64_t hash_sep = deinterleave64(hash.bits); /* hash = [LAT][LONG] */\n\n    double lat_scale = lat_range.max - lat_range.min;\n    double long_scale = long_range.max - long_range.min;\n\n    uint32_t ilato = hash_sep;       /* get lat part of deinterleaved hash */\n    uint32_t ilono = hash_sep >> 32; /* shift over to get long part of hash */\n\n    /* divide by 2**step.\n     * Then, for 0-1 coordinate, multiply times scale and add\n       to the min to get the absolute coordinate. */\n    area->latitude.min =\n        lat_range.min + (ilato * 1.0 / (1ull << step)) * lat_scale;\n    area->latitude.max =\n        lat_range.min + ((ilato + 1) * 1.0 / (1ull << step)) * lat_scale;\n    area->longitude.min =\n        long_range.min + (ilono * 1.0 / (1ull << step)) * long_scale;\n    area->longitude.max =\n        long_range.min + ((ilono + 1) * 1.0 / (1ull << step)) * long_scale;\n\n    return 1;\n}\n\nint geohashDecodeType(const GeoHashBits hash, GeoHashArea *area) {\n    GeoHashRange r[2] = {{0}};\n    geohashGetCoordRange(&r[0], &r[1]);\n    return geohashDecode(r[0], r[1], hash, area);\n}\n\nint geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area) {\n    return geohashDecodeType(hash, area);\n}\n\nint geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy) {\n    if (!xy) return 0;\n    xy[0] = (area->longitude.min + area->longitude.max) / 2;\n    if (xy[0] > GEO_LONG_MAX) xy[0] = GEO_LONG_MAX;\n    if (xy[0] < GEO_LONG_MIN) xy[0] = GEO_LONG_MIN;\n    xy[1] = (area->latitude.min + area->latitude.max) / 2;\n    if (xy[1] > GEO_LAT_MAX) xy[1] = GEO_LAT_MAX;\n    if (xy[1] < GEO_LAT_MIN) xy[1] = GEO_LAT_MIN;\n    return 1;\n}\n\nint geohashDecodeToLongLatType(const GeoHashBits hash, double *xy) {\n    GeoHashArea area = {{0}};\n    if (!xy || !geohashDecodeType(hash, &area))\n        return 0;\n    return geohashDecodeAreaToLongLat(&area, xy);\n}\n\nint geohashDecodeToLongLatWGS84(const GeoHashBits hash, double *xy) {\n    return geohashDecodeToLongLatType(hash, xy);\n}\n\nstatic void geohash_move_x(GeoHashBits *hash, int8_t d) {\n    if (d == 0)\n        return;\n\n    uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaULL;\n    uint64_t y = hash->bits & 0x5555555555555555ULL;\n\n    uint64_t zz = 0x5555555555555555ULL >> (64 - hash->step * 2);\n\n    if (d > 0) {\n        x = x + (zz + 1);\n    } else {\n        x = x | zz;\n        x = x - (zz + 1);\n    }\n\n    x &= (0xaaaaaaaaaaaaaaaaULL >> (64 - hash->step * 2));\n    hash->bits = (x | y);\n}\n\nstatic void geohash_move_y(GeoHashBits *hash, int8_t d) {\n    if (d == 0)\n        return;\n\n    uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaULL;\n    uint64_t y = hash->bits & 0x5555555555555555ULL;\n\n    uint64_t zz = 0xaaaaaaaaaaaaaaaaULL >> (64 - hash->step * 2);\n    if (d > 0) {\n        y = y + (zz + 1);\n    } else {\n        y = y | zz;\n        y = y - (zz + 1);\n    }\n    y &= (0x5555555555555555ULL >> (64 - hash->step * 2));\n    hash->bits = (x | y);\n}\n\nvoid geohashNeighbors(const GeoHashBits *hash, GeoHashNeighbors *neighbors) {\n    neighbors->east = *hash;\n    neighbors->west = *hash;\n    neighbors->north = *hash;\n    neighbors->south = *hash;\n    neighbors->south_east = *hash;\n    neighbors->south_west = *hash;\n    neighbors->north_east = *hash;\n    neighbors->north_west = *hash;\n\n    geohash_move_x(&neighbors->east, 1);\n    geohash_move_y(&neighbors->east, 0);\n\n    geohash_move_x(&neighbors->west, -1);\n    geohash_move_y(&neighbors->west, 0);\n\n    geohash_move_x(&neighbors->south, 0);\n    geohash_move_y(&neighbors->south, -1);\n\n    geohash_move_x(&neighbors->north, 0);\n    geohash_move_y(&neighbors->north, 1);\n\n    geohash_move_x(&neighbors->north_west, -1);\n    geohash_move_y(&neighbors->north_west, 1);\n\n    geohash_move_x(&neighbors->north_east, 1);\n    geohash_move_y(&neighbors->north_east, 1);\n\n    geohash_move_x(&neighbors->south_east, 1);\n    geohash_move_y(&neighbors->south_east, -1);\n\n    geohash_move_x(&neighbors->south_west, -1);\n    geohash_move_y(&neighbors->south_west, -1);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/geohash.h",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef GEOHASH_H_\n#define GEOHASH_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdint.h>\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n#define HASHISZERO(r) (!(r).bits && !(r).step)\n#define RANGEISZERO(r) (!(r).max && !(r).min)\n#define RANGEPISZERO(r) (r == NULL || RANGEISZERO(*r))\n\n#define GEO_STEP_MAX 26 /* 26*2 = 52 bits. */\n\n/* Limits from EPSG:900913 / EPSG:3785 / OSGEO:41001 */\n#define GEO_LAT_MIN -85.05112878\n#define GEO_LAT_MAX 85.05112878\n#define GEO_LONG_MIN -180\n#define GEO_LONG_MAX 180\n\ntypedef enum {\n    GEOHASH_NORTH = 0,\n    GEOHASH_EAST,\n    GEOHASH_WEST,\n    GEOHASH_SOUTH,\n    GEOHASH_SOUTH_WEST,\n    GEOHASH_SOUTH_EAST,\n    GEOHASH_NORT_WEST,\n    GEOHASH_NORT_EAST\n} GeoDirection;\n\ntypedef struct {\n    uint64_t bits;\n    uint8_t step;\n} GeoHashBits;\n\ntypedef struct {\n    double min;\n    double max;\n} GeoHashRange;\n\ntypedef struct {\n    GeoHashBits hash;\n    GeoHashRange longitude;\n    GeoHashRange latitude;\n} GeoHashArea;\n\ntypedef struct {\n    GeoHashBits north;\n    GeoHashBits east;\n    GeoHashBits west;\n    GeoHashBits south;\n    GeoHashBits north_east;\n    GeoHashBits south_east;\n    GeoHashBits north_west;\n    GeoHashBits south_west;\n} GeoHashNeighbors;\n\n/*\n * 0:success\n * -1:failed\n */\nvoid geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range);\nint geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,\n                  double longitude, double latitude, uint8_t step,\n                  GeoHashBits *hash);\nint geohashEncodeType(double longitude, double latitude,\n                      uint8_t step, GeoHashBits *hash);\nint geohashEncodeWGS84(double longitude, double latitude, uint8_t step,\n                       GeoHashBits *hash);\nint geohashDecode(const GeoHashRange long_range, const GeoHashRange lat_range,\n                  const GeoHashBits hash, GeoHashArea *area);\nint geohashDecodeType(const GeoHashBits hash, GeoHashArea *area);\nint geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area);\nint geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy);\nint geohashDecodeToLongLatType(const GeoHashBits hash, double *xy);\nint geohashDecodeToLongLatWGS84(const GeoHashBits hash, double *xy);\nint geohashDecodeToLongLatMercator(const GeoHashBits hash, double *xy);\nvoid geohashNeighbors(const GeoHashBits *hash, GeoHashNeighbors *neighbors);\n\n#if defined(__cplusplus)\n}\n#endif\n#endif /* GEOHASH_H_ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/geohash_helper.c",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* This is a C++ to C conversion from the ardb project.\n * This file started out as:\n * https://github.com/yinqiwen/ardb/blob/d42503/src/geo/geohash_helper.cpp\n */\n\n#include \"fmacros.h\"\n#include \"geohash_helper.h\"\n#include \"debugmacro.h\"\n#include <math.h>\n\n#define D_R (M_PI / 180.0)\n#define R_MAJOR 6378137.0\n#define R_MINOR 6356752.3142\n#define RATIO (R_MINOR / R_MAJOR)\n#define ECCENT (sqrt(1.0 - (RATIO *RATIO)))\n#define COM (0.5 * ECCENT)\n\n/// @brief The usual PI/180 constant\nconst double DEG_TO_RAD = 0.017453292519943295769236907684886;\n/// @brief Earth's quatratic mean radius for WGS-84\nconst double EARTH_RADIUS_IN_METERS = 6372797.560856;\n\nconst double MERCATOR_MAX = 20037726.37;\nconst double MERCATOR_MIN = -20037726.37;\n\nstatic inline double deg_rad(double ang) { return ang * D_R; }\nstatic inline double rad_deg(double ang) { return ang / D_R; }\n\n/* This function is used in order to estimate the step (bits precision)\n * of the 9 search area boxes during radius queries. */\nuint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {\n    if (range_meters == 0) return 26;\n    int step = 1;\n    while (range_meters < MERCATOR_MAX) {\n        range_meters *= 2;\n        step++;\n    }\n    step -= 2; /* Make sure range is included in most of the base cases. */\n\n    /* Wider range torwards the poles... Note: it is possible to do better\n     * than this approximation by computing the distance between meridians\n     * at this latitude, but this does the trick for now. */\n    if (lat > 66 || lat < -66) {\n        step--;\n        if (lat > 80 || lat < -80) step--;\n    }\n\n    /* Frame to valid range. */\n    if (step < 1) step = 1;\n    if (step > 26) step = 26;\n    return step;\n}\n\n/* Return the bounding box of the search area centered at latitude,longitude\n * having a radius of radius_meter. bounds[0] - bounds[2] is the minimum\n * and maxium longitude, while bounds[1] - bounds[3] is the minimum and\n * maximum latitude.\n *\n * This function does not behave correctly with very large radius values, for\n * instance for the coordinates 81.634948934258375 30.561509253718668 and a\n * radius of 7083 kilometers, it reports as bounding boxes:\n *\n * min_lon 7.680495, min_lat -33.119473, max_lon 155.589402, max_lat 94.242491\n *\n * However, for instance, a min_lon of 7.680495 is not correct, because the\n * point -1.27579540014266968 61.33421815228281559 is at less than 7000\n * kilometers away.\n *\n * Since this function is currently only used as an optimization, the\n * optimization is not used for very big radiuses, however the function\n * should be fixed. */\nint geohashBoundingBox(double longitude, double latitude, double radius_meters,\n                       double *bounds) {\n    if (!bounds) return 0;\n\n    bounds[0] = longitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude)));\n    bounds[2] = longitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude)));\n    bounds[1] = latitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS);\n    bounds[3] = latitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS);\n    return 1;\n}\n\n/* Return a set of areas (center + 8) that are able to cover a range query\n * for the specified position and radius. */\nGeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double radius_meters) {\n    GeoHashRange long_range, lat_range;\n    GeoHashRadius radius;\n    GeoHashBits hash;\n    GeoHashNeighbors neighbors;\n    GeoHashArea area;\n    double min_lon, max_lon, min_lat, max_lat;\n    double bounds[4];\n    int steps;\n\n    geohashBoundingBox(longitude, latitude, radius_meters, bounds);\n    min_lon = bounds[0];\n    min_lat = bounds[1];\n    max_lon = bounds[2];\n    max_lat = bounds[3];\n\n    steps = geohashEstimateStepsByRadius(radius_meters,latitude);\n\n    geohashGetCoordRange(&long_range,&lat_range);\n    geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);\n    geohashNeighbors(&hash,&neighbors);\n    geohashDecode(long_range,lat_range,hash,&area);\n\n    /* Check if the step is enough at the limits of the covered area.\n     * Sometimes when the search area is near an edge of the\n     * area, the estimated step is not small enough, since one of the\n     * north / south / west / east square is too near to the search area\n     * to cover everything. */\n    int decrease_step = 0;\n    {\n        GeoHashArea north, south, east, west;\n\n        geohashDecode(long_range, lat_range, neighbors.north, &north);\n        geohashDecode(long_range, lat_range, neighbors.south, &south);\n        geohashDecode(long_range, lat_range, neighbors.east, &east);\n        geohashDecode(long_range, lat_range, neighbors.west, &west);\n\n        if (geohashGetDistance(longitude,latitude,longitude,north.latitude.max)\n            < radius_meters) decrease_step = 1;\n        if (geohashGetDistance(longitude,latitude,longitude,south.latitude.min)\n            < radius_meters) decrease_step = 1;\n        if (geohashGetDistance(longitude,latitude,east.longitude.max,latitude)\n            < radius_meters) decrease_step = 1;\n        if (geohashGetDistance(longitude,latitude,west.longitude.min,latitude)\n            < radius_meters) decrease_step = 1;\n    }\n\n    if (steps > 1 && decrease_step) {\n        steps--;\n        geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);\n        geohashNeighbors(&hash,&neighbors);\n        geohashDecode(long_range,lat_range,hash,&area);\n    }\n\n    /* Exclude the search areas that are useless. */\n    if (steps >= 2) {\n        if (area.latitude.min < min_lat) {\n            GZERO(neighbors.south);\n            GZERO(neighbors.south_west);\n            GZERO(neighbors.south_east);\n        }\n        if (area.latitude.max > max_lat) {\n            GZERO(neighbors.north);\n            GZERO(neighbors.north_east);\n            GZERO(neighbors.north_west);\n        }\n        if (area.longitude.min < min_lon) {\n            GZERO(neighbors.west);\n            GZERO(neighbors.south_west);\n            GZERO(neighbors.north_west);\n        }\n        if (area.longitude.max > max_lon) {\n            GZERO(neighbors.east);\n            GZERO(neighbors.south_east);\n            GZERO(neighbors.north_east);\n        }\n    }\n    radius.hash = hash;\n    radius.neighbors = neighbors;\n    radius.area = area;\n    return radius;\n}\n\nGeoHashRadius geohashGetAreasByRadiusWGS84(double longitude, double latitude,\n                                           double radius_meters) {\n    return geohashGetAreasByRadius(longitude, latitude, radius_meters);\n}\n\nGeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash) {\n    uint64_t bits = hash.bits;\n    bits <<= (52 - hash.step * 2);\n    return bits;\n}\n\n/* Calculate distance using haversin great circle distance formula. */\ndouble geohashGetDistance(double lon1d, double lat1d, double lon2d, double lat2d) {\n    double lat1r, lon1r, lat2r, lon2r, u, v;\n    lat1r = deg_rad(lat1d);\n    lon1r = deg_rad(lon1d);\n    lat2r = deg_rad(lat2d);\n    lon2r = deg_rad(lon2d);\n    u = sin((lat2r - lat1r) / 2);\n    v = sin((lon2r - lon1r) / 2);\n    return 2.0 * EARTH_RADIUS_IN_METERS *\n           asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v));\n}\n\nint geohashGetDistanceIfInRadius(double x1, double y1,\n                                 double x2, double y2, double radius,\n                                 double *distance) {\n    *distance = geohashGetDistance(x1, y1, x2, y2);\n    if (*distance > radius) return 0;\n    return 1;\n}\n\nint geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2,\n                                      double y2, double radius,\n                                      double *distance) {\n    return geohashGetDistanceIfInRadius(x1, y1, x2, y2, radius, distance);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/geohash_helper.h",
    "content": "/*\n * Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>\n * Copyright (c) 2014, Matt Stancliff <matt@genges.com>.\n * Copyright (c) 2015, Salvatore Sanfilippo <antirez@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\n * BE 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\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef GEOHASH_HELPER_HPP_\n#define GEOHASH_HELPER_HPP_\n\n#include \"geohash.h\"\n\n#define GZERO(s) s.bits = s.step = 0;\n#define GISZERO(s) (!s.bits && !s.step)\n#define GISNOTZERO(s) (s.bits || s.step)\n\ntypedef uint64_t GeoHashFix52Bits;\ntypedef uint64_t GeoHashVarBits;\n\ntypedef struct {\n    GeoHashBits hash;\n    GeoHashArea area;\n    GeoHashNeighbors neighbors;\n} GeoHashRadius;\n\nint GeoHashBitsComparator(const GeoHashBits *a, const GeoHashBits *b);\nuint8_t geohashEstimateStepsByRadius(double range_meters, double lat);\nint geohashBoundingBox(double longitude, double latitude, double radius_meters,\n                        double *bounds);\nGeoHashRadius geohashGetAreasByRadius(double longitude,\n                                      double latitude, double radius_meters);\nGeoHashRadius geohashGetAreasByRadiusWGS84(double longitude, double latitude,\n                                           double radius_meters);\nGeoHashRadius geohashGetAreasByRadiusMercator(double longitude, double latitude,\n                                              double radius_meters);\nGeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash);\ndouble geohashGetDistance(double lon1d, double lat1d,\n                          double lon2d, double lat2d);\nint geohashGetDistanceIfInRadius(double x1, double y1,\n                                 double x2, double y2, double radius,\n                                 double *distance);\nint geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2,\n                                      double y2, double radius,\n                                      double *distance);\n\n#endif /* GEOHASH_HELPER_HPP_ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/gopher.c",
    "content": "/*\n * Copyright (c) 2019, 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 \"server.h\"\n\n/* Emit an item in Gopher directory listing format:\n * <type><descr><TAB><selector><TAB><hostname><TAB><port>\n * If descr or selector are NULL, then the \"(NULL)\" string is used instead. */\nvoid addReplyGopherItem(client *c, const char *type, const char *descr,\n                        const char *selector, const char *hostname, int port)\n{\n    sds item = sdscatfmt(sdsempty(),\"%s%s\\t%s\\t%s\\t%i\\r\\n\",\n                         type, descr,\n                         selector ? selector : \"(NULL)\",\n                         hostname ? hostname : \"(NULL)\",\n                         port);\n    addReplyProto(c,item,sdslen(item));\n    sdsfree(item);\n}\n\n/* This is called by processInputBuffer() when an inline request is processed\n * with Gopher mode enabled, and the request happens to have zero or just one\n * argument. In such case we get the relevant key and reply using the Gopher\n * protocol. */\nvoid processGopherRequest(client *c) {\n    robj *keyname = c->argc == 0 ? createStringObject(\"/\",1) : c->argv[0];\n    robj *o = lookupKeyRead(c->db,keyname);\n\n    /* If there is no such key, return with a Gopher error. */\n    if (o == NULL || o->type != OBJ_STRING) {\n        char *errstr;\n        if (o == NULL)\n            errstr = \"Error: no content at the specified key\";\n        else\n            errstr = \"Error: selected key type is invalid \"\n                     \"for Gopher output\";\n        addReplyGopherItem(c,\"i\",errstr,NULL,NULL,0);\n        addReplyGopherItem(c,\"i\",\"Redis Gopher server\",NULL,NULL,0);\n    } else {\n        addReply(c,o);\n    }\n\n    /* Cleanup, also make sure to emit the final \".CRLF\" line. Note that\n     * the connection will be closed immediately after this because the client\n     * will be flagged with CLIENT_CLOSE_AFTER_REPLY, in accordance with the\n     * Gopher protocol. */\n    if (c->argc == 0) decrRefCount(keyname);\n\n    /* Note that in theory we should terminate the Gopher request with\n     * \".<CR><LF>\" (called Lastline in the RFC) like that:\n     *\n     * addReplyProto(c,\".\\r\\n\",3);\n     *\n     * However after examining the current clients landscape, it's probably\n     * going to do more harm than good for several reasons:\n     *\n     * 1. Clients should not have any issue with missing .<CR><LF> as for\n     *    specification, and in the real world indeed certain servers\n     *    implementations never used to send the terminator.\n     *\n     * 2. Redis does not know if it's serving a text file or a binary file:\n     *    at the same time clients will not remove the \".<CR><LF>\" bytes at\n     *    tne end when downloading a binary file from the server, so adding\n     *    the \"Lastline\" terminator without knowing the content is just\n     *    dangerous.\n     *\n     * 3. The utility gopher2redis.rb that we provide for Redis, and any\n     *    other similar tool you may use as Gopher authoring system for\n     *    Redis, can just add the \"Lastline\" when needed.\n     */\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/help.h",
    "content": "/* Automatically generated by 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    \"cluster\",\n    \"geo\",\n    \"stream\"\n};\n\nstruct commandHelp {\n  char *name;\n  char *params;\n  char *summary;\n  int group;\n  char *since;\n} commandHelp[] = {\n    { \"ACL CAT\",\n    \"[categoryname]\",\n    \"List the ACL categories or the commands inside a category\",\n    9,\n    \"6.0.0\" },\n    { \"ACL DELUSER\",\n    \"username [username ...]\",\n    \"Remove the specified ACL users and the associated rules\",\n    9,\n    \"6.0.0\" },\n    { \"ACL GENPASS\",\n    \"[bits]\",\n    \"Generate a pseudorandom secure password to use for ACL users\",\n    9,\n    \"6.0.0\" },\n    { \"ACL LIST\",\n    \"-\",\n    \"List the current ACL rules in ACL config file format\",\n    9,\n    \"6.0.0\" },\n    { \"ACL LOAD\",\n    \"-\",\n    \"Reload the ACLs from the configured ACL file\",\n    9,\n    \"6.0.0\" },\n    { \"ACL LOG\",\n    \"[count or RESET]\",\n    \"List latest events denied because of ACLs in place\",\n    9,\n    \"6.0.0\" },\n    { \"ACL SAVE\",\n    \"-\",\n    \"Save the current ACL rules in the configured ACL file\",\n    9,\n    \"6.0.0\" },\n    { \"ACL SETUSER\",\n    \"rule [rule ...]\",\n    \"Modify or create the rules for a specific ACL user\",\n    9,\n    \"6.0.0\" },\n    { \"ACL USERS\",\n    \"-\",\n    \"List the username of all the configured ACL rules\",\n    9,\n    \"6.0.0\" },\n    { \"ACL WHOAMI\",\n    \"-\",\n    \"Return the name of the user associated to the current connection\",\n    9,\n    \"6.0.0\" },\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    \"[SCHEDULE]\",\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    { \"BITFIELD\",\n    \"key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]\",\n    \"Perform arbitrary bitfield integer operations on strings\",\n    1,\n    \"3.2.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 an element from a list, push it to another list and return it; or block until one is available\",\n    2,\n    \"2.2.0\" },\n    { \"BZPOPMAX\",\n    \"key [key ...] timeout\",\n    \"Remove and return the member with the highest score from one or more sorted sets, or block until one is available\",\n    4,\n    \"5.0.0\" },\n    { \"BZPOPMIN\",\n    \"key [key ...] timeout\",\n    \"Remove and return the member with the lowest score from one or more sorted sets, or block until one is available\",\n    4,\n    \"5.0.0\" },\n    { \"CLIENT CACHING\",\n    \"YES|NO\",\n    \"Instruct the server about tracking or not keys in the next request\",\n    8,\n    \"6.0.0\" },\n    { \"CLIENT GETNAME\",\n    \"-\",\n    \"Get the current connection name\",\n    8,\n    \"2.6.9\" },\n    { \"CLIENT GETREDIR\",\n    \"-\",\n    \"Get tracking notifications redirection client ID if any\",\n    8,\n    \"6.0.0\" },\n    { \"CLIENT ID\",\n    \"-\",\n    \"Returns the client ID for the current connection\",\n    8,\n    \"5.0.0\" },\n    { \"CLIENT KILL\",\n    \"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [ADDR ip:port] [SKIPME yes/no]\",\n    \"Kill the connection of a client\",\n    8,\n    \"2.4.0\" },\n    { \"CLIENT LIST\",\n    \"[TYPE normal|master|replica|pubsub]\",\n    \"Get the list of client connections\",\n    8,\n    \"2.4.0\" },\n    { \"CLIENT PAUSE\",\n    \"timeout\",\n    \"Stop processing commands from clients for some time\",\n    8,\n    \"2.9.50\" },\n    { \"CLIENT REPLY\",\n    \"ON|OFF|SKIP\",\n    \"Instruct the server whether to reply to commands\",\n    8,\n    \"3.2\" },\n    { \"CLIENT SETNAME\",\n    \"connection-name\",\n    \"Set the current connection name\",\n    8,\n    \"2.6.9\" },\n    { \"CLIENT TRACKING\",\n    \"ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]\",\n    \"Enable or disable server assisted client side caching support\",\n    8,\n    \"6.0.0\" },\n    { \"CLIENT UNBLOCK\",\n    \"client-id [TIMEOUT|ERROR]\",\n    \"Unblock a client blocked in a blocking command from a different connection\",\n    8,\n    \"5.0.0\" },\n    { \"CLUSTER ADDSLOTS\",\n    \"slot [slot ...]\",\n    \"Assign new hash slots to receiving node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER BUMPEPOCH\",\n    \"-\",\n    \"Advance the cluster config epoch\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER COUNT-FAILURE-REPORTS\",\n    \"node-id\",\n    \"Return the number of failure reports active for a given node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER COUNTKEYSINSLOT\",\n    \"slot\",\n    \"Return the number of local keys in the specified hash slot\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER DELSLOTS\",\n    \"slot [slot ...]\",\n    \"Set hash slots as unbound in receiving node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER FAILOVER\",\n    \"[FORCE|TAKEOVER]\",\n    \"Forces a replica to perform a manual failover of its master.\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER FLUSHSLOTS\",\n    \"-\",\n    \"Delete a node's own slots information\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER FORGET\",\n    \"node-id\",\n    \"Remove a node from the nodes table\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER GETKEYSINSLOT\",\n    \"slot count\",\n    \"Return local key names in the specified hash slot\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER INFO\",\n    \"-\",\n    \"Provides info about Redis Cluster node state\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER KEYSLOT\",\n    \"key\",\n    \"Returns the hash slot of the specified key\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER MEET\",\n    \"ip port\",\n    \"Force a node cluster to handshake with another node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER MYID\",\n    \"-\",\n    \"Return the node id\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER NODES\",\n    \"-\",\n    \"Get Cluster config for the node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER REPLICAS\",\n    \"node-id\",\n    \"List replica nodes of the specified master node\",\n    12,\n    \"5.0.0\" },\n    { \"CLUSTER REPLICATE\",\n    \"node-id\",\n    \"Reconfigure a node as a replica of the specified master node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER RESET\",\n    \"[HARD|SOFT]\",\n    \"Reset a Redis Cluster node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SAVECONFIG\",\n    \"-\",\n    \"Forces the node to save cluster state on disk\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SET-CONFIG-EPOCH\",\n    \"config-epoch\",\n    \"Set the configuration epoch in a new node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SETSLOT\",\n    \"slot IMPORTING|MIGRATING|STABLE|NODE [node-id]\",\n    \"Bind a hash slot to a specific node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SLAVES\",\n    \"node-id\",\n    \"List replica nodes of the specified master node\",\n    12,\n    \"3.0.0\" },\n    { \"CLUSTER SLOTS\",\n    \"-\",\n    \"Get array of Cluster slot to node mappings\",\n    12,\n    \"3.0.0\" },\n    { \"COMMAND\",\n    \"-\",\n    \"Get array of Redis command details\",\n    9,\n    \"2.8.13\" },\n    { \"COMMAND COUNT\",\n    \"-\",\n    \"Get total number of Redis commands\",\n    9,\n    \"2.8.13\" },\n    { \"COMMAND GETKEYS\",\n    \"-\",\n    \"Extract keys given a full Redis command\",\n    9,\n    \"2.8.13\" },\n    { \"COMMAND INFO\",\n    \"command-name [command-name ...]\",\n    \"Get array of specific Redis command details\",\n    9,\n    \"2.8.13\" },\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 [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    \"[ASYNC]\",\n    \"Remove all keys from all databases\",\n    9,\n    \"1.0.0\" },\n    { \"FLUSHDB\",\n    \"[ASYNC]\",\n    \"Remove all keys from the current database\",\n    9,\n    \"1.0.0\" },\n    { \"GEOADD\",\n    \"key longitude latitude member [longitude latitude member ...]\",\n    \"Add one or more geospatial items in the geospatial index represented using a sorted set\",\n    13,\n    \"3.2.0\" },\n    { \"GEODIST\",\n    \"key member1 member2 [m|km|ft|mi]\",\n    \"Returns the distance between two members of a geospatial index\",\n    13,\n    \"3.2.0\" },\n    { \"GEOHASH\",\n    \"key member [member ...]\",\n    \"Returns members of a geospatial index as standard geohash strings\",\n    13,\n    \"3.2.0\" },\n    { \"GEOPOS\",\n    \"key member [member ...]\",\n    \"Returns longitude and latitude of members of a geospatial index\",\n    13,\n    \"3.2.0\" },\n    { \"GEORADIUS\",\n    \"key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]\",\n    \"Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point\",\n    13,\n    \"3.2.0\" },\n    { \"GEORADIUSBYMEMBER\",\n    \"key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]\",\n    \"Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member\",\n    13,\n    \"3.2.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    { \"HELLO\",\n    \"protover [AUTH username password] [SETNAME clientname]\",\n    \"switch Redis protocol\",\n    8,\n    \"6.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 [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    { \"HSTRLEN\",\n    \"key field\",\n    \"Get the length of the value of a hash field\",\n    5,\n    \"3.2.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    { \"LATENCY DOCTOR\",\n    \"-\",\n    \"Return a human readable latency analysis report.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY GRAPH\",\n    \"event\",\n    \"Return a latency graph for the event.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY HELP\",\n    \"-\",\n    \"Show helpful text about the different subcommands.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY HISTORY\",\n    \"event\",\n    \"Return timestamp-latency samples for the event.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY LATEST\",\n    \"-\",\n    \"Return the latest latency samples for all events.\",\n    9,\n    \"2.8.13\" },\n    { \"LATENCY RESET\",\n    \"[event]\",\n    \"Reset latency data for one or more events.\",\n    9,\n    \"2.8.13\" },\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 element\",\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    { \"LOLWUT\",\n    \"[VERSION version]\",\n    \"Display some computer art and the Redis version\",\n    9,\n    \"5.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 element [element ...]\",\n    \"Prepend one or multiple elements to a list\",\n    2,\n    \"1.0.0\" },\n    { \"LPUSHX\",\n    \"key element [element ...]\",\n    \"Prepend an element 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 element\",\n    \"Remove elements from a list\",\n    2,\n    \"1.0.0\" },\n    { \"LSET\",\n    \"key index element\",\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    { \"MEMORY DOCTOR\",\n    \"-\",\n    \"Outputs memory problems report\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY HELP\",\n    \"-\",\n    \"Show helpful text about the different subcommands\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY MALLOC-STATS\",\n    \"-\",\n    \"Show allocator internal stats\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY PURGE\",\n    \"-\",\n    \"Ask the allocator to release memory\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY STATS\",\n    \"-\",\n    \"Show memory usage details\",\n    9,\n    \"4.0.0\" },\n    { \"MEMORY USAGE\",\n    \"key [SAMPLES count]\",\n    \"Estimate the memory usage of a key\",\n    9,\n    \"4.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] [AUTH password] [KEYS key]\",\n    \"Atomically transfer a key from a Redis instance to another one.\",\n    0,\n    \"2.6.0\" },\n    { \"MODULE LIST\",\n    \"-\",\n    \"List all modules loaded by the server\",\n    9,\n    \"4.0.0\" },\n    { \"MODULE LOAD\",\n    \"path [arg]\",\n    \"Load a module\",\n    9,\n    \"4.0.0\" },\n    { \"MODULE UNLOAD\",\n    \"name\",\n    \"Unload a module\",\n    9,\n    \"4.0.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    \"[message]\",\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    { \"PSYNC\",\n    \"replicationid offset\",\n    \"Internal command used for replication\",\n    9,\n    \"2.8.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    { \"READONLY\",\n    \"-\",\n    \"Enables read queries for a connection to a cluster replica node\",\n    12,\n    \"3.0.0\" },\n    { \"READWRITE\",\n    \"-\",\n    \"Disables read queries for a connection to a cluster replica node\",\n    12,\n    \"3.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    { \"REPLICAOF\",\n    \"host port\",\n    \"Make the server a replica of another instance, or promote it as master.\",\n    9,\n    \"5.0.0\" },\n    { \"RESTORE\",\n    \"key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]\",\n    \"Create a key using the provided serialized value, previously obtained using DUMP.\",\n    0,\n    \"2.6.0\" },\n    { \"ROLE\",\n    \"-\",\n    \"Return the role of the instance in the context of replication\",\n    9,\n    \"2.8.12\" },\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, prepend it to another list and return it\",\n    2,\n    \"1.2.0\" },\n    { \"RPUSH\",\n    \"key element [element ...]\",\n    \"Append one or multiple elements to a list\",\n    2,\n    \"1.0.0\" },\n    { \"RPUSHX\",\n    \"key element [element ...]\",\n    \"Append an element 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] [TYPE type]\",\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 DEBUG\",\n    \"YES|SYNC|NO\",\n    \"Set the debug mode for executed scripts.\",\n    10,\n    \"3.2.0\" },\n    { \"SCRIPT EXISTS\",\n    \"sha1 [sha1 ...]\",\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] [KEEPTTL]\",\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 replica of another instance, or promote it as master. Deprecated starting with Redis 5. Use REPLICAOF instead.\",\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 [count]\",\n    \"Remove and return one or multiple random members 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    { \"STRALGO\",\n    \"LCS algo-specific-argument [algo-specific-argument ...]\",\n    \"Run algorithms (currently LCS) against strings\",\n    1,\n    \"6.0.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    { \"SWAPDB\",\n    \"index1 index2\",\n    \"Swaps two Redis databases\",\n    9,\n    \"4.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    { \"TOUCH\",\n    \"key [key ...]\",\n    \"Alters the last access time of a key(s). Returns the number of existing keys specified.\",\n    0,\n    \"3.2.1\" },\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    { \"UNLINK\",\n    \"key [key ...]\",\n    \"Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.\",\n    0,\n    \"4.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    { \"WAIT\",\n    \"numreplicas timeout\",\n    \"Wait for the synchronous replication of all the write commands sent in the context of the current connection\",\n    0,\n    \"3.0.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    { \"XACK\",\n    \"key group ID [ID ...]\",\n    \"Marks a pending message as correctly processed, effectively removing it from the pending entries list of the consumer group. Return value of the command is the number of messages successfully acknowledged, that is, the IDs we were actually able to resolve in the PEL.\",\n    14,\n    \"5.0.0\" },\n    { \"XADD\",\n    \"key ID field value [field value ...]\",\n    \"Appends a new entry to a stream\",\n    14,\n    \"5.0.0\" },\n    { \"XCLAIM\",\n    \"key group consumer min-idle-time ID [ID ...] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [force] [justid]\",\n    \"Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.\",\n    14,\n    \"5.0.0\" },\n    { \"XDEL\",\n    \"key ID [ID ...]\",\n    \"Removes the specified entries from the stream. Returns the number of items actually deleted, that may be different from the number of IDs passed in case certain IDs do not exist.\",\n    14,\n    \"5.0.0\" },\n    { \"XGROUP\",\n    \"[CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]\",\n    \"Create, destroy, and manage consumer groups.\",\n    14,\n    \"5.0.0\" },\n    { \"XINFO\",\n    \"[CONSUMERS key groupname] [GROUPS key] [STREAM key] [HELP]\",\n    \"Get information on streams and consumer groups\",\n    14,\n    \"5.0.0\" },\n    { \"XLEN\",\n    \"key\",\n    \"Return the number of entires in a stream\",\n    14,\n    \"5.0.0\" },\n    { \"XPENDING\",\n    \"key group [start end count] [consumer]\",\n    \"Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.\",\n    14,\n    \"5.0.0\" },\n    { \"XRANGE\",\n    \"key start end [COUNT count]\",\n    \"Return a range of elements in a stream, with IDs matching the specified IDs interval\",\n    14,\n    \"5.0.0\" },\n    { \"XREAD\",\n    \"[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]\",\n    \"Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.\",\n    14,\n    \"5.0.0\" },\n    { \"XREADGROUP\",\n    \"GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]\",\n    \"Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.\",\n    14,\n    \"5.0.0\" },\n    { \"XREVRANGE\",\n    \"key end start [COUNT count]\",\n    \"Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE\",\n    14,\n    \"5.0.0\" },\n    { \"XTRIM\",\n    \"key MAXLEN [~] count\",\n    \"Trims the stream to (approximately if '~' is passed) a certain size\",\n    14,\n    \"5.0.0\" },\n    { \"ZADD\",\n    \"key [NX|XX] [CH] [INCR] 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    { \"ZPOPMAX\",\n    \"key [count]\",\n    \"Remove and return members with the highest scores in a sorted set\",\n    4,\n    \"5.0.0\" },\n    { \"ZPOPMIN\",\n    \"key [count]\",\n    \"Remove and return members with the lowest scores in a sorted set\",\n    4,\n    \"5.0.0\" },\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    { \"ZREVRANGEBYLEX\",\n    \"key max min [LIMIT offset count]\",\n    \"Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.\",\n    4,\n    \"2.8.9\" },\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": "Chapter07/redis-6.0.3/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 \"server.h\"\n\n#include <stdint.h>\n#include <math.h>\n\n/* The Redis HyperLogLog implementation is based on the following ideas:\n *\n * * The use of a 64 bit hash function as proposed in [1], in order to don't\n *   limited to cardinalities up to 10^9, at the cost of just 1 additional\n *   bit per register.\n * * The use of 16384 6-bit registers for a great level of accuracy, using\n *   a total of 12k per key.\n * * The use of the Redis string data type. No new type is introduced.\n * * No attempt is made to compress the data structure as in [1]. Also the\n *   algorithm used is the original HyperLogLog Algorithm as in [2], with\n *   the only difference that a 64 bit hash function is used, so no correction\n *   is performed for values near 2^32 as in [1].\n *\n * [1] Heule, Nunkesser, Hall: HyperLogLog in Practice: Algorithmic\n *     Engineering of a State of The Art Cardinality Estimation Algorithm.\n *\n * [2] P. Flajolet, Éric Fusy, O. Gandouet, and F. Meunier. Hyperloglog: The\n *     analysis of a near-optimal cardinality estimation algorithm.\n *\n * Redis uses two representations:\n *\n * 1) A \"dense\" representation where every entry is represented by\n *    a 6-bit integer.\n * 2) A \"sparse\" representation using run length compression suitable\n *    for representing HyperLogLogs with many registers set to 0 in\n *    a memory efficient way.\n *\n *\n * HLL header\n * ===\n *\n * Both the dense and sparse representation have a 16 byte header as follows:\n *\n * +------+---+-----+----------+\n * | HYLL | E | N/U | Cardin.  |\n * +------+---+-----+----------+\n *\n * The first 4 bytes are a magic string set to the bytes \"HYLL\".\n * \"E\" is one byte encoding, currently set to HLL_DENSE or\n * HLL_SPARSE. N/U are three not used bytes.\n *\n * The \"Cardin.\" field is a 64 bit integer stored in little endian format\n * with the latest cardinality computed that can be reused if the data\n * structure was not modified since the last computation (this is useful\n * because there are high probabilities that HLLADD operations don't\n * modify the actual data structure and hence the approximated cardinality).\n *\n * When the most significant bit in the most significant byte of the cached\n * cardinality is set, it means that the data structure was modified and\n * we can't reuse the cached value that must be recomputed.\n *\n * Dense representation\n * ===\n *\n * The dense representation used by Redis is the following:\n *\n * +--------+--------+--------+------//      //--+\n * |11000000|22221111|33333322|55444444 ....     |\n * +--------+--------+--------+------//      //--+\n *\n * The 6 bits counters are encoded one after the other starting from the\n * LSB to the MSB, and using the next bytes as needed.\n *\n * Sparse representation\n * ===\n *\n * The sparse representation encodes registers using a run length\n * encoding composed of three opcodes, two using one byte, and one using\n * of two bytes. The opcodes are called ZERO, XZERO and VAL.\n *\n * ZERO opcode is represented as 00xxxxxx. The 6-bit integer represented\n * by the six bits 'xxxxxx', plus 1, means that there are N registers set\n * to 0. This opcode can represent from 1 to 64 contiguous registers set\n * to the value of 0.\n *\n * XZERO opcode is represented by two bytes 01xxxxxx yyyyyyyy. The 14-bit\n * integer represented by the bits 'xxxxxx' as most significant bits and\n * 'yyyyyyyy' as least significant bits, plus 1, means that there are N\n * registers set to 0. This opcode can represent from 0 to 16384 contiguous\n * registers set to the value of 0.\n *\n * VAL opcode is represented as 1vvvvvxx. It contains a 5-bit integer\n * representing the value of a register, and a 2-bit integer representing\n * the number of contiguous registers set to that value 'vvvvv'.\n * To obtain the value and run length, the integers vvvvv and xx must be\n * incremented by one. This opcode can represent values from 1 to 32,\n * repeated from 1 to 4 times.\n *\n * The sparse representation can't represent registers with a value greater\n * than 32, however it is very unlikely that we find such a register in an\n * HLL with a cardinality where the sparse representation is still more\n * memory efficient than the dense representation. When this happens the\n * HLL is converted to the dense representation.\n *\n * The sparse representation is purely positional. For example a sparse\n * representation of an empty HLL is just: XZERO:16384.\n *\n * An HLL having only 3 non-zero registers at position 1000, 1020, 1021\n * respectively set to 2, 3, 3, is represented by the following three\n * opcodes:\n *\n * XZERO:1000 (Registers 0-999 are set to 0)\n * VAL:2,1    (1 register set to value 2, that is register 1000)\n * ZERO:19    (Registers 1001-1019 set to 0)\n * VAL:3,2    (2 registers set to value 3, that is registers 1020,1021)\n * XZERO:15362 (Registers 1022-16383 set to 0)\n *\n * In the example the sparse representation used just 7 bytes instead\n * of 12k in order to represent the HLL registers. In general for low\n * cardinality there is a big win in terms of space efficiency, traded\n * with CPU time since the sparse representation is slower to access:\n *\n * The following table shows average cardinality vs bytes used, 100\n * samples per cardinality (when the set was not representable because\n * of registers with too big value, the dense representation size was used\n * as a sample).\n *\n * 100 267\n * 200 485\n * 300 678\n * 400 859\n * 500 1033\n * 600 1205\n * 700 1375\n * 800 1544\n * 900 1713\n * 1000 1882\n * 2000 3480\n * 3000 4879\n * 4000 6089\n * 5000 7138\n * 6000 8042\n * 7000 8823\n * 8000 9500\n * 9000 10088\n * 10000 10591\n *\n * The dense representation uses 12288 bytes, so there is a big win up to\n * a cardinality of ~2000-3000. For bigger cardinalities the constant times\n * involved in updating the sparse representation is not justified by the\n * memory savings. The exact maximum length of the sparse representation\n * when this implementation switches to the dense representation is\n * configured via the define server.hll_sparse_max_bytes.\n */\n\nstruct hllhdr {\n    char magic[4];      /* \"HYLL\" */\n    uint8_t encoding;   /* HLL_DENSE or HLL_SPARSE. */\n    uint8_t notused[3]; /* Reserved for future use, must be zero. */\n    uint8_t card[8];    /* Cached cardinality, little endian. */\n    uint8_t registers[]; /* Data bytes. */\n};\n\n/* The cached cardinality MSB is used to signal validity of the cached value. */\n#define HLL_INVALIDATE_CACHE(hdr) (hdr)->card[7] |= (1<<7)\n#define HLL_VALID_CACHE(hdr) (((hdr)->card[7] & (1<<7)) == 0)\n\n#define HLL_P 14 /* The greater is P, the smaller the error. */\n#define HLL_Q (64-HLL_P) /* The number of bits of the hash value used for\n                            determining the number of leading zeros. */\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#define HLL_ALPHA_INF 0.721347520444481703680 /* constant for 0.5/ln(2) */\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    #ifdef USE_ALIGNED_ACCESS\n        memcpy(&k,data,sizeof(uint64_t));\n    #else\n        k = *((uint64_t*)data);\n    #endif\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; /* fall-thru */\n    case 6: h ^= (uint64_t)data[5] << 40; /* fall-thru */\n    case 5: h ^= (uint64_t)data[4] << 32; /* fall-thru */\n    case 4: h ^= (uint64_t)data[3] << 24; /* fall-thru */\n    case 3: h ^= (uint64_t)data[2] << 16; /* fall-thru */\n    case 2: h ^= (uint64_t)data[1] << 8; /* fall-thru */\n    case 1: h ^= (uint64_t)data[0];\n            h *= m; /* fall-thru */\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 = Q+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 >>= HLL_P; /* Remove bits used to address the register. */\n    hash |= ((uint64_t)1<<HLL_Q); /* Make sure the loop terminates\n                                     and count will be <= Q+1. */\n    bit = 1;\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/* Low level function to set the dense HLL register at 'index' to the\n * specified value if the current value is smaller than 'count'.\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 hllDenseSet(uint8_t *registers, long index, uint8_t count) {\n    uint8_t oldcount;\n\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/* \"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 * This is just a wrapper to hllDenseSet(), performing the hashing of the\n * element in order to retrieve the index and zero-run count. */\nint hllDenseAdd(uint8_t *registers, unsigned char *ele, size_t elesize) {\n    long index;\n    uint8_t count = hllPatLen(ele,elesize,&index);\n    /* Update the register if this element produced a longer run of zeroes. */\n    return hllDenseSet(registers,index,count);\n}\n\n/* Compute the register histogram in the dense representation. */\nvoid hllDenseRegHisto(uint8_t *registers, int* reghisto) {\n    int j;\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;\n            r1 = (r[0] >> 6 | r[1] << 2) & 63;\n            r2 = (r[1] >> 4 | r[2] << 4) & 63;\n            r3 = (r[2] >> 2) & 63;\n            r4 = r[3] & 63;\n            r5 = (r[3] >> 6 | r[4] << 2) & 63;\n            r6 = (r[4] >> 4 | r[5] << 4) & 63;\n            r7 = (r[5] >> 2) & 63;\n            r8 = r[6] & 63;\n            r9 = (r[6] >> 6 | r[7] << 2) & 63;\n            r10 = (r[7] >> 4 | r[8] << 4) & 63;\n            r11 = (r[8] >> 2) & 63;\n            r12 = r[9] & 63;\n            r13 = (r[9] >> 6 | r[10] << 2) & 63;\n            r14 = (r[10] >> 4 | r[11] << 4) & 63;\n            r15 = (r[11] >> 2) & 63;\n\n            reghisto[r0]++;\n            reghisto[r1]++;\n            reghisto[r2]++;\n            reghisto[r3]++;\n            reghisto[r4]++;\n            reghisto[r5]++;\n            reghisto[r6]++;\n            reghisto[r7]++;\n            reghisto[r8]++;\n            reghisto[r9]++;\n            reghisto[r10]++;\n            reghisto[r11]++;\n            reghisto[r12]++;\n            reghisto[r13]++;\n            reghisto[r14]++;\n            reghisto[r15]++;\n\n            r += 12;\n        }\n    } else {\n        for(j = 0; j < HLL_REGISTERS; j++) {\n            unsigned long reg;\n            HLL_DENSE_GET_REGISTER(reg,registers,j);\n            reghisto[reg]++;\n        }\n    }\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 C_OK if the sparse representation was valid,\n * otherwise C_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 C_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            if ((runlen + idx) > HLL_REGISTERS) break; /* Overflow. */\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 C_ERR;\n    }\n\n    /* Free the old representation and set the new one. */\n    sdsfree(o->ptr);\n    o->ptr = dense;\n    return C_OK;\n}\n\n/* Low level function to set the sparse HLL register at 'index' to the\n * specified value if the current value is smaller than 'count'.\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 hllSparseSet(robj *o, long index, uint8_t count) {\n    struct hllhdr *hdr;\n    uint8_t oldcount, *sparse, *end, *p, *prev, *next;\n    long first, span;\n    long is_zero = 0, is_xzero = 0, is_val = 0, runlen = 0;\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 previous 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 || p >= end) 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 multiple\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) == C_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     * conversion, it will be performed in all the slaves as well. */\n    int dense_retval = hllDenseSet(hdr->registers,index,count);\n    serverAssert(dense_retval == 1);\n    return dense_retval;\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 * This function is actually a wrapper for hllSparseSet(), it only performs\n * the hashshing of the elmenet to obtain the index and zeros run length. */\nint hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) {\n    long index;\n    uint8_t count = hllPatLen(ele,elesize,&index);\n    /* Update the register if this element produced a longer run of zeroes. */\n    return hllSparseSet(o,index,count);\n}\n\n/* Compute the register histogram in the sparse representation. */\nvoid hllSparseRegHisto(uint8_t *sparse, int sparselen, int *invalid, int* reghisto) {\n    int 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            reghisto[0] += runlen;\n            p++;\n        } else if (HLL_SPARSE_IS_XZERO(p)) {\n            runlen = HLL_SPARSE_XZERO_LEN(p);\n            idx += runlen;\n            reghisto[0] += runlen;\n            p += 2;\n        } else {\n            runlen = HLL_SPARSE_VAL_LEN(p);\n            regval = HLL_SPARSE_VAL_VALUE(p);\n            idx += runlen;\n            reghisto[regval] += runlen;\n            p++;\n        }\n    }\n    if (idx != HLL_REGISTERS && invalid) *invalid = 1;\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 hllDenseRegHisto() and hllSparseRegHisto()\n * functions as helpers to compute histogram of register values part of the\n * computation, which is representation-specific, while all the rest is common. */\n\n/* Implements the register histogram calculation for uint8_t data type\n * which is only used internally as speedup for PFCOUNT with multiple keys. */\nvoid hllRawRegHisto(uint8_t *registers, int* reghisto) {\n    uint64_t *word = (uint64_t*) registers;\n    uint8_t *bytes;\n    int j;\n\n    for (j = 0; j < HLL_REGISTERS/8; j++) {\n        if (*word == 0) {\n            reghisto[0] += 8;\n        } else {\n            bytes = (uint8_t*) word;\n            reghisto[bytes[0]]++;\n            reghisto[bytes[1]]++;\n            reghisto[bytes[2]]++;\n            reghisto[bytes[3]]++;\n            reghisto[bytes[4]]++;\n            reghisto[bytes[5]]++;\n            reghisto[bytes[6]]++;\n            reghisto[bytes[7]]++;\n        }\n        word++;\n    }\n}\n\n/* Helper function sigma as defined in\n * \"New cardinality estimation algorithms for HyperLogLog sketches\"\n * Otmar Ertl, arXiv:1702.01284 */\ndouble hllSigma(double x) {\n    if (x == 1.) return INFINITY;\n    double zPrime;\n    double y = 1;\n    double z = x;\n    do {\n        x *= x;\n        zPrime = z;\n        z += x * y;\n        y += y;\n    } while(zPrime != z);\n    return z;\n}\n\n/* Helper function tau as defined in\n * \"New cardinality estimation algorithms for HyperLogLog sketches\"\n * Otmar Ertl, arXiv:1702.01284 */\ndouble hllTau(double x) {\n    if (x == 0. || x == 1.) return 0.;\n    double zPrime;\n    double y = 1.0;\n    double z = 1 - x;\n    do {\n        x = sqrt(x);\n        zPrime = z;\n        y *= 0.5;\n        z -= pow(1 - x, 2)*y;\n    } while(zPrime != z);\n    return z / 3;\n}\n\n/* Return the approximated cardinality of the set based on the harmonic\n * mean of the registers values. 'hdr' points to the start of the SDS\n * representing the String object holding the HLL representation.\n *\n * If the sparse representation of the HLL object is not valid, the integer\n * pointed by 'invalid' is set to non-zero, otherwise it is left untouched.\n *\n * hllCount() supports a special internal-only encoding of HLL_RAW, that\n * is, hdr->registers will point to an uint8_t array of HLL_REGISTERS element.\n * This is useful in order to speedup PFCOUNT when called against multiple\n * keys (no need to work with 6-bit integers encoding). */\nuint64_t hllCount(struct hllhdr *hdr, int *invalid) {\n    double m = HLL_REGISTERS;\n    double E;\n    int j;\n    /* Note that reghisto size could be just HLL_Q+2, becuase HLL_Q+1 is\n     * the maximum frequency of the \"000...1\" sequence the hash function is\n     * able to return. However it is slow to check for sanity of the\n     * input: instead we history array at a safe size: overflows will\n     * just write data to wrong, but correctly allocated, places. */\n    int reghisto[64] = {0};\n\n    /* Compute register histogram */\n    if (hdr->encoding == HLL_DENSE) {\n        hllDenseRegHisto(hdr->registers,reghisto);\n    } else if (hdr->encoding == HLL_SPARSE) {\n        hllSparseRegHisto(hdr->registers,\n                         sdslen((sds)hdr)-HLL_HDR_SIZE,invalid,reghisto);\n    } else if (hdr->encoding == HLL_RAW) {\n        hllRawRegHisto(hdr->registers,reghisto);\n    } else {\n        serverPanic(\"Unknown HyperLogLog encoding in hllCount()\");\n    }\n\n    /* Estimate cardinality form register histogram. See:\n     * \"New cardinality estimation algorithms for HyperLogLog sketches\"\n     * Otmar Ertl, arXiv:1702.01284 */\n    double z = m * hllTau((m-reghisto[HLL_Q+1])/(double)m);\n    for (j = HLL_Q; j >= 1; --j) {\n        z += reghisto[j];\n        z *= 0.5;\n    }\n    z += m * hllSigma(reghisto[0]/(double)m);\n    E = llroundl(HLL_ALPHA_INF*m*m/z);\n\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, C_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                if ((runlen + i) > HLL_REGISTERS) break; /* Overflow. */\n                while(runlen--) {\n                    if (regval > max[i]) max[i] = regval;\n                    i++;\n                }\n                p++;\n            }\n        }\n        if (i != HLL_REGISTERS) return C_ERR;\n    }\n    return C_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    serverAssert((p-(uint8_t*)s) == sparselen);\n\n    /* Create the actual object. */\n    o = createObject(OBJ_STRING,s);\n    hdr = o->ptr;\n    memcpy(hdr->magic,\"HYLL\",4);\n    hdr->encoding = HLL_SPARSE;\n    return o;\n}\n\n/* Check if the object is a String with a valid HLL representation.\n * Return C_OK if this is true, otherwise reply to the client\n * with an error and return C_ERR. */\nint isHLLObjectOrReply(client *c, robj *o) {\n    struct hllhdr *hdr;\n\n    /* Key exists, check type */\n    if (checkType(c,o,OBJ_STRING))\n        return C_ERR; /* Error already sent. */\n\n    if (!sdsEncodedObject(o)) goto invalid;\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 C_OK;\n\ninvalid:\n    addReplySds(c,\n        sdsnew(\"-WRONGTYPE Key is not a valid \"\n               \"HyperLogLog string value.\\r\\n\"));\n    return C_ERR;\n}\n\n/* PFADD var ele ele ele ... ele => :0 or :1 */\nvoid pfaddCommand(client *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) != C_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,c->db,c->argv[1]);\n        notifyKeyspaceEvent(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(client *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) != C_OK) return;\n\n            /* Merge with this HLL with our 'max' HLL by setting max[i]\n             * to MAX(max[i],hll[i]). */\n            if (hllMerge(registers,o) == C_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 = lookupKeyWrite(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) != C_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,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(client *c) {\n    uint8_t max[HLL_REGISTERS];\n    struct hllhdr *hdr;\n    int j;\n    int use_dense = 0; /* Use dense representation as target? */\n\n    /* Compute an HLL with M[i] = MAX(M[i]_j).\n     * We store 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) != C_OK) return;\n\n        /* If at least one involved HLL is dense, use the dense representation\n         * as target ASAP to save time and avoid the conversion step. */\n        hdr = o->ptr;\n        if (hdr->encoding == HLL_DENSE) use_dense = 1;\n\n        /* Merge with this HLL with our 'max' HLL by setting max[i]\n         * to MAX(max[i],hll[i]). */\n        if (hllMerge(max,o) == C_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    /* Convert the destination object to dense representation if at least\n     * one of the inputs was dense. */\n    if (use_dense && hllSparseToDense(o) == C_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    for (j = 0; j < HLL_REGISTERS; j++) {\n        if (max[j] == 0) continue;\n        hdr = o->ptr;\n        switch(hdr->encoding) {\n        case HLL_DENSE: hllDenseSet(hdr->registers,j,max[j]); break;\n        case HLL_SPARSE: hllSparseSet(o,j,max[j]); break;\n        }\n    }\n    hdr = o->ptr; /* o->ptr may be different now, as a side effect of\n                     last hllSparseSet() call. */\n    HLL_INVALIDATE_CACHE(hdr);\n\n    signalModifiedKey(c,c->db,c->argv[1]);\n    /* We generate a PFADD event for PFMERGE for semantical simplicity\n     * since in theory this is a mass-add of elements. */\n    notifyKeyspaceEvent(NOTIFY_STRING,\"pfadd\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c,shared.ok);\n}\n\n/* ========================== Testing / Debugging  ========================== */\n\n/* PFSELFTEST\n * This command performs a self-test of the HLL registers implementation.\n * Something that is not easy to test from within the outside. */\n#define HLL_TEST_CYCLES 1000\nvoid pfselftestCommand(client *c) {\n    unsigned int j, i;\n    sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE);\n    struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2;\n    robj *o = NULL;\n    uint8_t bytecounters[HLL_REGISTERS];\n\n    /* Test 1: access registers.\n     * The test is conceived to test that the different counters of our data\n     * structure are accessible and that setting their values both result in\n     * the correct value to be retained and not affect adjacent values. */\n    for (j = 0; j < HLL_TEST_CYCLES; j++) {\n        /* Set the HLL counters and an array of unsigned byes of the\n         * same size to the same set of random values. */\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            unsigned int r = rand() & HLL_REGISTER_MAX;\n\n            bytecounters[i] = r;\n            HLL_DENSE_SET_REGISTER(hdr->registers,i,r);\n        }\n        /* Check that we are able to retrieve the same values. */\n        for (i = 0; i < HLL_REGISTERS; i++) {\n            unsigned int val;\n\n            HLL_DENSE_GET_REGISTER(val,hdr->registers,i);\n            if (val != bytecounters[i]) {\n                addReplyErrorFormat(c,\n                    \"TESTFAILED Register %d should be %d but is %d\",\n                    i, (int) bytecounters[i], (int) val);\n                goto cleanup;\n            }\n        }\n    }\n\n    /* Test 2: approximation error.\n     * The test adds unique elements and check that the estimated value\n     * is always reasonable bounds.\n     *\n     * We check that the error is smaller than a few times than the expected\n     * standard error, to make it very unlikely for the test to fail because\n     * of a \"bad\" run.\n     *\n     * The test is performed with both dense and sparse HLLs at the same\n     * time also verifying that the computed cardinality is the same. */\n    memset(hdr->registers,0,HLL_DENSE_SIZE-HLL_HDR_SIZE);\n    o = createHLLObject();\n    double relerr = 1.04/sqrt(HLL_REGISTERS);\n    int64_t checkpoint = 1;\n    uint64_t seed = (uint64_t)rand() | (uint64_t)rand() << 32;\n    uint64_t ele;\n    for (j = 1; j <= 10000000; j++) {\n        ele = j ^ seed;\n        hllDenseAdd(hdr->registers,(unsigned char*)&ele,sizeof(ele));\n        hllAdd(o,(unsigned char*)&ele,sizeof(ele));\n\n        /* Make sure that for small cardinalities we use sparse\n         * encoding. */\n        if (j == checkpoint && j < server.hll_sparse_max_bytes/2) {\n            hdr2 = o->ptr;\n            if (hdr2->encoding != HLL_SPARSE) {\n                addReplyError(c, \"TESTFAILED sparse encoding not used\");\n                goto cleanup;\n            }\n        }\n\n        /* Check that dense and sparse representations agree. */\n        if (j == checkpoint && hllCount(hdr,NULL) != hllCount(o->ptr,NULL)) {\n                addReplyError(c, \"TESTFAILED dense/sparse disagree\");\n                goto cleanup;\n        }\n\n        /* Check error. */\n        if (j == checkpoint) {\n            int64_t abserr = checkpoint - (int64_t)hllCount(hdr,NULL);\n            uint64_t maxerr = ceil(relerr*6*checkpoint);\n\n            /* Adjust the max error we expect for cardinality 10\n             * since from time to time it is statistically likely to get\n             * much higher error due to collision, resulting into a false\n             * positive. */\n            if (j == 10) maxerr = 1;\n\n            if (abserr < 0) abserr = -abserr;\n            if (abserr > (int64_t)maxerr) {\n                addReplyErrorFormat(c,\n                    \"TESTFAILED Too big error. card:%llu abserr:%llu\",\n                    (unsigned long long) checkpoint,\n                    (unsigned long long) abserr);\n                goto cleanup;\n            }\n            checkpoint *= 10;\n        }\n    }\n\n    /* Success! */\n    addReply(c,shared.ok);\n\ncleanup:\n    sdsfree(bitcounters);\n    if (o) decrRefCount(o);\n}\n\n/* PFDEBUG <subcommand> <key> ... args ...\n * Different debugging related operations about the HLL implementation. */\nvoid pfdebugCommand(client *c) {\n    char *cmd = c->argv[1]->ptr;\n    struct hllhdr *hdr;\n    robj *o;\n    int j;\n\n    o = lookupKeyWrite(c->db,c->argv[2]);\n    if (o == NULL) {\n        addReplyError(c,\"The specified key does not exist\");\n        return;\n    }\n    if (isHLLObjectOrReply(c,o) != C_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) == C_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        addReplyArrayLen(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            sdsfree(decoded);\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) == C_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": "Chapter07/redis-6.0.3/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#define INTSET_ENC_INT16 (sizeof(int16_t))\n#define INTSET_ENC_INT32 (sizeof(int32_t))\n#define INTSET_ENC_INT64 (sizeof(int64_t))\n\n/* Return the required encoding for the provided value. */\nstatic uint8_t _intsetValueEncoding(int64_t v) {\n    if (v < INT32_MIN || v > INT32_MAX)\n        return INTSET_ENC_INT64;\n    else if (v < INT16_MIN || v > INT16_MAX)\n        return INTSET_ENC_INT32;\n    else\n        return INTSET_ENC_INT16;\n}\n\n/* Return the value at pos, given an encoding. */\nstatic int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {\n    int64_t v64;\n    int32_t v32;\n    int16_t v16;\n\n    if (enc == INTSET_ENC_INT64) {\n        memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));\n        memrev64ifbe(&v64);\n        return v64;\n    } else if (enc == INTSET_ENC_INT32) {\n        memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));\n        memrev32ifbe(&v32);\n        return v32;\n    } else {\n        memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));\n        memrev16ifbe(&v16);\n        return v16;\n    }\n}\n\n/* Return the value at pos, using the configured encoding. */\nstatic int64_t _intsetGet(intset *is, int pos) {\n    return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding));\n}\n\n/* Set the value at pos, using the configured encoding. */\nstatic void _intsetSet(intset *is, int pos, int64_t value) {\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        ((int64_t*)is->contents)[pos] = value;\n        memrev64ifbe(((int64_t*)is->contents)+pos);\n    } else if (encoding == INTSET_ENC_INT32) {\n        ((int32_t*)is->contents)[pos] = value;\n        memrev32ifbe(((int32_t*)is->contents)+pos);\n    } else {\n        ((int16_t*)is->contents)[pos] = value;\n        memrev16ifbe(((int16_t*)is->contents)+pos);\n    }\n}\n\n/* Create an empty intset. */\nintset *intsetNew(void) {\n    intset *is = zmalloc(sizeof(intset));\n    is->encoding = intrev32ifbe(INTSET_ENC_INT16);\n    is->length = 0;\n    return is;\n}\n\n/* Resize the intset */\nstatic intset *intsetResize(intset *is, uint32_t len) {\n    uint32_t size = len*intrev32ifbe(is->encoding);\n    is = zrealloc(is,sizeof(intset)+size);\n    return is;\n}\n\n/* Search for the position of \"value\". Return 1 when the value was found and\n * sets \"pos\" to the position of the value within the intset. Return 0 when\n * the value is not present in the intset and sets \"pos\" to the position\n * where \"value\" can be inserted. */\nstatic uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {\n    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;\n    int64_t cur = -1;\n\n    /* The value can never be found when the set is empty */\n    if (intrev32ifbe(is->length) == 0) {\n        if (pos) *pos = 0;\n        return 0;\n    } else {\n        /* Check for the case where we know we cannot find the value,\n         * but do know the insert position. */\n        if (value > _intsetGet(is,max)) {\n            if (pos) *pos = intrev32ifbe(is->length);\n            return 0;\n        } else if (value < _intsetGet(is,0)) {\n            if (pos) *pos = 0;\n            return 0;\n        }\n    }\n\n    while(max >= min) {\n        mid = ((unsigned int)min + (unsigned int)max) >> 1;\n        cur = _intsetGet(is,mid);\n        if (value > cur) {\n            min = mid+1;\n        } else if (value < cur) {\n            max = mid-1;\n        } else {\n            break;\n        }\n    }\n\n    if (value == cur) {\n        if (pos) *pos = mid;\n        return 1;\n    } else {\n        if (pos) *pos = min;\n        return 0;\n    }\n}\n\n/* Upgrades the intset to a larger encoding and inserts the given integer. */\nstatic intset *intsetUpgradeAndAdd(intset *is, int64_t value) {\n    uint8_t curenc = intrev32ifbe(is->encoding);\n    uint8_t newenc = _intsetValueEncoding(value);\n    int length = intrev32ifbe(is->length);\n    int prepend = value < 0 ? 1 : 0;\n\n    /* First set new encoding and resize */\n    is->encoding = intrev32ifbe(newenc);\n    is = intsetResize(is,intrev32ifbe(is->length)+1);\n\n    /* Upgrade back-to-front so we don't overwrite values.\n     * Note that the \"prepend\" variable is used to make sure we have an empty\n     * space at either the beginning or the end of the intset. */\n    while(length--)\n        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));\n\n    /* Set the value at the beginning or the end. */\n    if (prepend)\n        _intsetSet(is,0,value);\n    else\n        _intsetSet(is,intrev32ifbe(is->length),value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\nstatic void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {\n    void *src, *dst;\n    uint32_t bytes = intrev32ifbe(is->length)-from;\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        src = (int64_t*)is->contents+from;\n        dst = (int64_t*)is->contents+to;\n        bytes *= sizeof(int64_t);\n    } else if (encoding == INTSET_ENC_INT32) {\n        src = (int32_t*)is->contents+from;\n        dst = (int32_t*)is->contents+to;\n        bytes *= sizeof(int32_t);\n    } else {\n        src = (int16_t*)is->contents+from;\n        dst = (int16_t*)is->contents+to;\n        bytes *= sizeof(int16_t);\n    }\n    memmove(dst,src,bytes);\n}\n\n/* Insert an integer in the intset */\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 1;\n\n    /* Upgrade encoding if necessary. If we need to upgrade, we know that\n     * this value should be either appended (if > 0) or prepended (if < 0),\n     * because it lies outside the range of existing values. */\n    if (valenc > intrev32ifbe(is->encoding)) {\n        /* This always succeeds, so we don't need to curry *success. */\n        return intsetUpgradeAndAdd(is,value);\n    } else {\n        /* Abort if the value is already present in the set.\n         * This call will populate \"pos\" with the right position to insert\n         * the value when it cannot be found. */\n        if (intsetSearch(is,value,&pos)) {\n            if (success) *success = 0;\n            return is;\n        }\n\n        is = intsetResize(is,intrev32ifbe(is->length)+1);\n        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);\n    }\n\n    _intsetSet(is,pos,value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\n/* Delete integer from intset */\nintset *intsetRemove(intset *is, int64_t value, int *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 0;\n\n    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {\n        uint32_t len = intrev32ifbe(is->length);\n\n        /* We know we can delete */\n        if (success) *success = 1;\n\n        /* Overwrite value with tail and update length */\n        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);\n        is = intsetResize(is,len-1);\n        is->length = intrev32ifbe(len-1);\n    }\n    return is;\n}\n\n/* Determine whether a value belongs to this set */\nuint8_t intsetFind(intset *is, int64_t value) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);\n}\n\n/* Return random member */\nint64_t intsetRandom(intset *is) {\n    return _intsetGet(is,rand()%intrev32ifbe(is->length));\n}\n\n/* Get the value at the given position. When this position is\n * out of range the function returns 0, when in range it returns 1. */\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {\n    if (pos < intrev32ifbe(is->length)) {\n        *value = _intsetGet(is,pos);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return intset length */\nuint32_t intsetLen(const intset *is) {\n    return intrev32ifbe(is->length);\n}\n\n/* Return intset blob size in bytes. */\nsize_t intsetBlobLen(intset *is) {\n    return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);\n}\n\n#ifdef REDIS_TEST\n#include <sys/time.h>\n#include <time.h>\n\n#if 0\nstatic void intsetRepr(intset *is) {\n    for (uint32_t i = 0; i < intrev32ifbe(is->length); i++) {\n        printf(\"%lld\\n\", (uint64_t)_intsetGet(is,i));\n    }\n    printf(\"\\n\");\n}\n\nstatic void error(char *err) {\n    printf(\"%s\\n\", err);\n    exit(1);\n}\n#endif\n\nstatic void ok(void) {\n    printf(\"OK\\n\");\n}\n\nstatic long long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\n#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))\nstatic void _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\nstatic intset *createSet(int bits, int size) {\n    uint64_t mask = (1<<bits)-1;\n    uint64_t value;\n    intset *is = intsetNew();\n\n    for (int 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\nstatic void checkConsistency(intset *is) {\n    for (uint32_t 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\n#define UNUSED(x) (void)(x)\nint intsetTest(int argc, char **argv) {\n    uint8_t success;\n    int i;\n    intset *is;\n    srand(time(NULL));\n\n    UNUSED(argc);\n    UNUSED(argv);\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) ==\n                    INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(+9223372036854775807ull) ==\n                    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        uint32_t 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\",\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    return 0;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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    uint32_t encoding;\n    uint32_t length;\n    int8_t contents[];\n} intset;\n\nintset *intsetNew(void);\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success);\nintset *intsetRemove(intset *is, int64_t value, int *success);\nuint8_t intsetFind(intset *is, int64_t value);\nint64_t intsetRandom(intset *is);\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);\nuint32_t intsetLen(const intset *is);\nsize_t intsetBlobLen(intset *is);\n\n#ifdef REDIS_TEST\nint intsetTest(int argc, char *argv[]);\n#endif\n\n#endif // __INTSET_H\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/latency.c",
    "content": "/* The latency monitor allows to easily observe the sources of latency\n * in a Redis instance using the LATENCY command. Different latency\n * sources are monitored, like disk I/O, execution of commands, fork\n * system call, and so forth.\n *\n * ----------------------------------------------------------------------------\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 \"server.h\"\n\n/* Dictionary type for latency events. */\nint dictStringKeyCompare(void *privdata, const void *key1, const void *key2) {\n    UNUSED(privdata);\n    return strcmp(key1,key2) == 0;\n}\n\nuint64_t dictStringHash(const void *key) {\n    return dictGenHashFunction(key, strlen(key));\n}\n\nvoid dictVanillaFree(void *privdata, void *val);\n\ndictType latencyTimeSeriesDictType = {\n    dictStringHash,             /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictStringKeyCompare,       /* key compare */\n    dictVanillaFree,            /* key destructor */\n    dictVanillaFree             /* val destructor */\n};\n\n/* ------------------------- Utility functions ------------------------------ */\n\n#ifdef __linux__\n/* Returns 1 if Transparent Huge Pages support is enabled in the kernel.\n * Otherwise (or if we are unable to check) 0 is returned. */\nint THPIsEnabled(void) {\n    char buf[1024];\n\n    FILE *fp = fopen(\"/sys/kernel/mm/transparent_hugepage/enabled\",\"r\");\n    if (!fp) return 0;\n    if (fgets(buf,sizeof(buf),fp) == NULL) {\n        fclose(fp);\n        return 0;\n    }\n    fclose(fp);\n    return (strstr(buf,\"[never]\") == NULL) ? 1 : 0;\n}\n#endif\n\n/* Report the amount of AnonHugePages in smap, in bytes. If the return\n * value of the function is non-zero, the process is being targeted by\n * THP support, and is likely to have memory usage / latency issues. */\nint THPGetAnonHugePagesSize(void) {\n    return zmalloc_get_smap_bytes_by_field(\"AnonHugePages:\",-1);\n}\n\n/* ---------------------------- Latency API --------------------------------- */\n\n/* Latency monitor initialization. We just need to create the dictionary\n * of time series, each time serie is created on demand in order to avoid\n * having a fixed list to maintain. */\nvoid latencyMonitorInit(void) {\n    server.latency_events = dictCreate(&latencyTimeSeriesDictType,NULL);\n}\n\n/* Add the specified sample to the specified time series \"event\".\n * This function is usually called via latencyAddSampleIfNeeded(), that\n * is a macro that only adds the sample if the latency is higher than\n * server.latency_monitor_threshold. */\nvoid latencyAddSample(const char *event, mstime_t latency) {\n    struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);\n    time_t now = time(NULL);\n    int prev;\n\n    /* Create the time series if it does not exist. */\n    if (ts == NULL) {\n        ts = zmalloc(sizeof(*ts));\n        ts->idx = 0;\n        ts->max = 0;\n        memset(ts->samples,0,sizeof(ts->samples));\n        dictAdd(server.latency_events,zstrdup(event),ts);\n    }\n\n    if (latency > ts->max) ts->max = latency;\n\n    /* If the previous sample is in the same second, we update our old sample\n     * if this latency is > of the old one, or just return. */\n    prev = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;\n    if (ts->samples[prev].time == now) {\n        if (latency > ts->samples[prev].latency)\n            ts->samples[prev].latency = latency;\n        return;\n    }\n\n    ts->samples[ts->idx].time = time(NULL);\n    ts->samples[ts->idx].latency = latency;\n\n    ts->idx++;\n    if (ts->idx == LATENCY_TS_LEN) ts->idx = 0;\n}\n\n/* Reset data for the specified event, or all the events data if 'event' is\n * NULL.\n *\n * Note: this is O(N) even when event_to_reset is not NULL because makes\n * the code simpler and we have a small fixed max number of events. */\nint latencyResetEvent(char *event_to_reset) {\n    dictIterator *di;\n    dictEntry *de;\n    int resets = 0;\n\n    di = dictGetSafeIterator(server.latency_events);\n    while((de = dictNext(di)) != NULL) {\n        char *event = dictGetKey(de);\n\n        if (event_to_reset == NULL || strcasecmp(event,event_to_reset) == 0) {\n            dictDelete(server.latency_events, event);\n            resets++;\n        }\n    }\n    dictReleaseIterator(di);\n    return resets;\n}\n\n/* ------------------------ Latency reporting (doctor) ---------------------- */\n\n/* Analyze the samples available for a given event and return a structure\n * populate with different metrics, average, MAD, min, max, and so forth.\n * Check latency.h definition of struct latenctStat for more info.\n * If the specified event has no elements the structure is populate with\n * zero values. */\nvoid analyzeLatencyForEvent(char *event, struct latencyStats *ls) {\n    struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);\n    int j;\n    uint64_t sum;\n\n    ls->all_time_high = ts ? ts->max : 0;\n    ls->avg = 0;\n    ls->min = 0;\n    ls->max = 0;\n    ls->mad = 0;\n    ls->samples = 0;\n    ls->period = 0;\n    if (!ts) return;\n\n    /* First pass, populate everything but the MAD. */\n    sum = 0;\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        if (ts->samples[j].time == 0) continue;\n        ls->samples++;\n        if (ls->samples == 1) {\n            ls->min = ls->max = ts->samples[j].latency;\n        } else {\n            if (ls->min > ts->samples[j].latency)\n                ls->min = ts->samples[j].latency;\n            if (ls->max < ts->samples[j].latency)\n                ls->max = ts->samples[j].latency;\n        }\n        sum += ts->samples[j].latency;\n\n        /* Track the oldest event time in ls->period. */\n        if (ls->period == 0 || ts->samples[j].time < ls->period)\n            ls->period = ts->samples[j].time;\n    }\n\n    /* So far avg is actually the sum of the latencies, and period is\n     * the oldest event time. We need to make the first an average and\n     * the second a range of seconds. */\n    if (ls->samples) {\n        ls->avg = sum / ls->samples;\n        ls->period = time(NULL) - ls->period;\n        if (ls->period == 0) ls->period = 1;\n    }\n\n    /* Second pass, compute MAD. */\n    sum = 0;\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        int64_t delta;\n\n        if (ts->samples[j].time == 0) continue;\n        delta = (int64_t)ls->avg - ts->samples[j].latency;\n        if (delta < 0) delta = -delta;\n        sum += delta;\n    }\n    if (ls->samples) ls->mad = sum / ls->samples;\n}\n\n/* Create a human readable report of latency events for this Redis instance. */\nsds createLatencyReport(void) {\n    sds report = sdsempty();\n    int advise_better_vm = 0;       /* Better virtual machines. */\n    int advise_slowlog_enabled = 0; /* Enable slowlog. */\n    int advise_slowlog_tuning = 0;  /* Reconfigure slowlog. */\n    int advise_slowlog_inspect = 0; /* Check your slowlog. */\n    int advise_disk_contention = 0; /* Try to lower disk contention. */\n    int advise_scheduler = 0;       /* Intrinsic latency. */\n    int advise_data_writeback = 0;  /* data=writeback. */\n    int advise_no_appendfsync = 0;  /* don't fsync during rewrites. */\n    int advise_local_disk = 0;      /* Avoid remote disks. */\n    int advise_ssd = 0;             /* Use an SSD drive. */\n    int advise_write_load_info = 0; /* Print info about AOF and write load. */\n    int advise_hz = 0;              /* Use higher HZ. */\n    int advise_large_objects = 0;   /* Deletion of large objects. */\n    int advise_mass_eviction = 0;   /* Avoid mass eviction of keys. */\n    int advise_relax_fsync_policy = 0; /* appendfsync always is slow. */\n    int advise_disable_thp = 0;     /* AnonHugePages detected. */\n    int advices = 0;\n\n    /* Return ASAP if the latency engine is disabled and it looks like it\n     * was never enabled so far. */\n    if (dictSize(server.latency_events) == 0 &&\n        server.latency_monitor_threshold == 0)\n    {\n        report = sdscat(report,\"I'm sorry, Dave, I can't do that. Latency monitoring is disabled in this Redis instance. You may use \\\"CONFIG SET latency-monitor-threshold <milliseconds>.\\\" in order to enable it. If we weren't in a deep space mission I'd suggest to take a look at http://redis.io/topics/latency-monitor.\\n\");\n        return report;\n    }\n\n    /* Show all the events stats and add for each event some event-related\n     * comment depending on the values. */\n    dictIterator *di;\n    dictEntry *de;\n    int eventnum = 0;\n\n    di = dictGetSafeIterator(server.latency_events);\n    while((de = dictNext(di)) != NULL) {\n        char *event = dictGetKey(de);\n        struct latencyTimeSeries *ts = dictGetVal(de);\n        struct latencyStats ls;\n\n        if (ts == NULL) continue;\n        eventnum++;\n        if (eventnum == 1) {\n            report = sdscat(report,\"Dave, I have observed latency spikes in this Redis instance. You don't mind talking about it, do you Dave?\\n\\n\");\n        }\n        analyzeLatencyForEvent(event,&ls);\n\n        report = sdscatprintf(report,\n            \"%d. %s: %d latency spikes (average %lums, mean deviation %lums, period %.2f sec). Worst all time event %lums.\",\n            eventnum, event,\n            ls.samples,\n            (unsigned long) ls.avg,\n            (unsigned long) ls.mad,\n            (double) ls.period/ls.samples,\n            (unsigned long) ts->max);\n\n        /* Fork */\n        if (!strcasecmp(event,\"fork\")) {\n            char *fork_quality;\n            if (server.stat_fork_rate < 10) {\n                fork_quality = \"terrible\";\n                advise_better_vm = 1;\n                advices++;\n            } else if (server.stat_fork_rate < 25) {\n                fork_quality = \"poor\";\n                advise_better_vm = 1;\n                advices++;\n            } else if (server.stat_fork_rate < 100) {\n                fork_quality = \"good\";\n            } else {\n                fork_quality = \"excellent\";\n            }\n            report = sdscatprintf(report,\n                \" Fork rate is %.2f GB/sec (%s).\", server.stat_fork_rate,\n                fork_quality);\n        }\n\n        /* Potentially commands. */\n        if (!strcasecmp(event,\"command\")) {\n            if (server.slowlog_log_slower_than < 0) {\n                advise_slowlog_enabled = 1;\n                advices++;\n            } else if (server.slowlog_log_slower_than/1000 >\n                       server.latency_monitor_threshold)\n            {\n                advise_slowlog_tuning = 1;\n                advices++;\n            }\n            advise_slowlog_inspect = 1;\n            advise_large_objects = 1;\n            advices += 2;\n        }\n\n        /* fast-command. */\n        if (!strcasecmp(event,\"fast-command\")) {\n            advise_scheduler = 1;\n            advices++;\n        }\n\n        /* AOF and I/O. */\n        if (!strcasecmp(event,\"aof-write-pending-fsync\")) {\n            advise_local_disk = 1;\n            advise_disk_contention = 1;\n            advise_ssd = 1;\n            advise_data_writeback = 1;\n            advices += 4;\n        }\n\n        if (!strcasecmp(event,\"aof-write-active-child\")) {\n            advise_no_appendfsync = 1;\n            advise_data_writeback = 1;\n            advise_ssd = 1;\n            advices += 3;\n        }\n\n        if (!strcasecmp(event,\"aof-write-alone\")) {\n            advise_local_disk = 1;\n            advise_data_writeback = 1;\n            advise_ssd = 1;\n            advices += 3;\n        }\n\n        if (!strcasecmp(event,\"aof-fsync-always\")) {\n            advise_relax_fsync_policy = 1;\n            advices++;\n        }\n\n        if (!strcasecmp(event,\"aof-fstat\") ||\n            !strcasecmp(event,\"rdb-unlik-temp-file\")) {\n            advise_disk_contention = 1;\n            advise_local_disk = 1;\n            advices += 2;\n        }\n\n        if (!strcasecmp(event,\"aof-rewrite-diff-write\") ||\n            !strcasecmp(event,\"aof-rename\")) {\n            advise_write_load_info = 1;\n            advise_data_writeback = 1;\n            advise_ssd = 1;\n            advise_local_disk = 1;\n            advices += 4;\n        }\n\n        /* Expire cycle. */\n        if (!strcasecmp(event,\"expire-cycle\")) {\n            advise_hz = 1;\n            advise_large_objects = 1;\n            advices += 2;\n        }\n\n        /* Eviction cycle. */\n        if (!strcasecmp(event,\"eviction-del\")) {\n            advise_large_objects = 1;\n            advices++;\n        }\n\n        if (!strcasecmp(event,\"eviction-cycle\")) {\n            advise_mass_eviction = 1;\n            advices++;\n        }\n\n        report = sdscatlen(report,\"\\n\",1);\n    }\n    dictReleaseIterator(di);\n\n    /* Add non event based advices. */\n    if (THPGetAnonHugePagesSize() > 0) {\n        advise_disable_thp = 1;\n        advices++;\n    }\n\n    if (eventnum == 0 && advices == 0) {\n        report = sdscat(report,\"Dave, no latency spike was observed during the lifetime of this Redis instance, not in the slightest bit. I honestly think you ought to sit down calmly, take a stress pill, and think things over.\\n\");\n    } else if (eventnum > 0 && advices == 0) {\n        report = sdscat(report,\"\\nWhile there are latency events logged, I'm not able to suggest any easy fix. Please use the Redis community to get some help, providing this report in your help request.\\n\");\n    } else {\n        /* Add all the suggestions accumulated so far. */\n\n        /* Better VM. */\n        report = sdscat(report,\"\\nI have a few advices for you:\\n\\n\");\n        if (advise_better_vm) {\n            report = sdscat(report,\"- If you are using a virtual machine, consider upgrading it with a faster one using an hypervisior that provides less latency during fork() calls. Xen is known to have poor fork() performance. Even in the context of the same VM provider, certain kinds of instances can execute fork faster than others.\\n\");\n        }\n\n        /* Slow log. */\n        if (advise_slowlog_enabled) {\n            report = sdscatprintf(report,\"- There are latency issues with potentially slow commands you are using. Try to enable the Slow Log Redis feature using the command 'CONFIG SET slowlog-log-slower-than %llu'. If the Slow log is disabled Redis is not able to log slow commands execution for you.\\n\", (unsigned long long)server.latency_monitor_threshold*1000);\n        }\n\n        if (advise_slowlog_tuning) {\n            report = sdscatprintf(report,\"- Your current Slow Log configuration only logs events that are slower than your configured latency monitor threshold. Please use 'CONFIG SET slowlog-log-slower-than %llu'.\\n\", (unsigned long long)server.latency_monitor_threshold*1000);\n        }\n\n        if (advise_slowlog_inspect) {\n            report = sdscat(report,\"- Check your Slow Log to understand what are the commands you are running which are too slow to execute. Please check http://redis.io/commands/slowlog for more information.\\n\");\n        }\n\n        /* Intrinsic latency. */\n        if (advise_scheduler) {\n            report = sdscat(report,\"- The system is slow to execute Redis code paths not containing system calls. This usually means the system does not provide Redis CPU time to run for long periods. You should try to:\\n\"\n            \"  1) Lower the system load.\\n\"\n            \"  2) Use a computer / VM just for Redis if you are running other softawre in the same system.\\n\"\n            \"  3) Check if you have a \\\"noisy neighbour\\\" problem.\\n\"\n            \"  4) Check with 'redis-cli --intrinsic-latency 100' what is the intrinsic latency in your system.\\n\"\n            \"  5) Check if the problem is allocator-related by recompiling Redis with MALLOC=libc, if you are using Jemalloc. However this may create fragmentation problems.\\n\");\n        }\n\n        /* AOF / Disk latency. */\n        if (advise_local_disk) {\n            report = sdscat(report,\"- It is strongly advised to use local disks for persistence, especially if you are using AOF. Remote disks provided by platform-as-a-service providers are known to be slow.\\n\");\n        }\n\n        if (advise_ssd) {\n            report = sdscat(report,\"- SSD disks are able to reduce fsync latency, and total time needed for snapshotting and AOF log rewriting (resulting in smaller memory usage and smaller final AOF rewrite buffer flushes). With extremely high write load SSD disks can be a good option. However Redis should perform reasonably with high load using normal disks. Use this advice as a last resort.\\n\");\n        }\n\n        if (advise_data_writeback) {\n            report = sdscat(report,\"- Mounting ext3/4 filesystems with data=writeback can provide a performance boost compared to data=ordered, however this mode of operation provides less guarantees, and sometimes it can happen that after a hard crash the AOF file will have an half-written command at the end and will require to be repaired before Redis restarts.\\n\");\n        }\n\n        if (advise_disk_contention) {\n            report = sdscat(report,\"- Try to lower the disk contention. This is often caused by other disk intensive processes running in the same computer (including other Redis instances).\\n\");\n        }\n\n        if (advise_no_appendfsync) {\n            report = sdscat(report,\"- Assuming from the point of view of data safety this is viable in your environment, you could try to enable the 'no-appendfsync-on-rewrite' option, so that fsync will not be performed while there is a child rewriting the AOF file or producing an RDB file (the moment where there is high disk contention).\\n\");\n        }\n\n        if (advise_relax_fsync_policy && server.aof_fsync == AOF_FSYNC_ALWAYS) {\n            report = sdscat(report,\"- Your fsync policy is set to 'always'. It is very hard to get good performances with such a setup, if possible try to relax the fsync policy to 'onesec'.\\n\");\n        }\n\n        if (advise_write_load_info) {\n            report = sdscat(report,\"- Latency during the AOF atomic rename operation or when the final difference is flushed to the AOF file at the end of the rewrite, sometimes is caused by very high write load, causing the AOF buffer to get very large. If possible try to send less commands to accomplish the same work, or use Lua scripts to group multiple operations into a single EVALSHA call.\\n\");\n        }\n\n        if (advise_hz && server.hz < 100) {\n            report = sdscat(report,\"- In order to make the Redis keys expiring process more incremental, try to set the 'hz' configuration parameter to 100 using 'CONFIG SET hz 100'.\\n\");\n        }\n\n        if (advise_large_objects) {\n            report = sdscat(report,\"- Deleting, expiring or evicting (because of maxmemory policy) large objects is a blocking operation. If you have very large objects that are often deleted, expired, or evicted, try to fragment those objects into multiple smaller objects.\\n\");\n        }\n\n        if (advise_mass_eviction) {\n            report = sdscat(report,\"- Sudden changes to the 'maxmemory' setting via 'CONFIG SET', or allocation of large objects via sets or sorted sets intersections, STORE option of SORT, Redis Cluster large keys migrations (RESTORE command), may create sudden memory pressure forcing the server to block trying to evict keys. \\n\");\n        }\n\n        if (advise_disable_thp) {\n            report = sdscat(report,\"- I detected a non zero amount of anonymous huge pages used by your process. This creates very serious latency events in different conditions, especially when Redis is persisting on disk. To disable THP support use the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled', make sure to also add it into /etc/rc.local so that the command will be executed again after a reboot. Note that even if you have already disabled THP, you still need to restart the Redis process to get rid of the huge pages already created.\\n\");\n        }\n    }\n\n    return report;\n}\n\n/* ---------------------- Latency command implementation -------------------- */\n\n/* latencyCommand() helper to produce a time-delay reply for all the samples\n * in memory for the specified time series. */\nvoid latencyCommandReplyWithSamples(client *c, struct latencyTimeSeries *ts) {\n    void *replylen = addReplyDeferredLen(c);\n    int samples = 0, j;\n\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        int i = (ts->idx + j) % LATENCY_TS_LEN;\n\n        if (ts->samples[i].time == 0) continue;\n        addReplyArrayLen(c,2);\n        addReplyLongLong(c,ts->samples[i].time);\n        addReplyLongLong(c,ts->samples[i].latency);\n        samples++;\n    }\n    setDeferredArrayLen(c,replylen,samples);\n}\n\n/* latencyCommand() helper to produce the reply for the LATEST subcommand,\n * listing the last latency sample for every event type registered so far. */\nvoid latencyCommandReplyWithLatestEvents(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    addReplyArrayLen(c,dictSize(server.latency_events));\n    di = dictGetIterator(server.latency_events);\n    while((de = dictNext(di)) != NULL) {\n        char *event = dictGetKey(de);\n        struct latencyTimeSeries *ts = dictGetVal(de);\n        int last = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;\n\n        addReplyArrayLen(c,4);\n        addReplyBulkCString(c,event);\n        addReplyLongLong(c,ts->samples[last].time);\n        addReplyLongLong(c,ts->samples[last].latency);\n        addReplyLongLong(c,ts->max);\n    }\n    dictReleaseIterator(di);\n}\n\n#define LATENCY_GRAPH_COLS 80\nsds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {\n    int j;\n    struct sequence *seq = createSparklineSequence();\n    sds graph = sdsempty();\n    uint32_t min = 0, max = 0;\n\n    for (j = 0; j < LATENCY_TS_LEN; j++) {\n        int i = (ts->idx + j) % LATENCY_TS_LEN;\n        int elapsed;\n        char buf[64];\n\n        if (ts->samples[i].time == 0) continue;\n        /* Update min and max. */\n        if (seq->length == 0) {\n            min = max = ts->samples[i].latency;\n        } else {\n            if (ts->samples[i].latency > max) max = ts->samples[i].latency;\n            if (ts->samples[i].latency < min) min = ts->samples[i].latency;\n        }\n        /* Use as label the number of seconds / minutes / hours / days\n         * ago the event happened. */\n        elapsed = time(NULL) - ts->samples[i].time;\n        if (elapsed < 60)\n            snprintf(buf,sizeof(buf),\"%ds\",elapsed);\n        else if (elapsed < 3600)\n            snprintf(buf,sizeof(buf),\"%dm\",elapsed/60);\n        else if (elapsed < 3600*24)\n            snprintf(buf,sizeof(buf),\"%dh\",elapsed/3600);\n        else\n            snprintf(buf,sizeof(buf),\"%dd\",elapsed/(3600*24));\n        sparklineSequenceAddSample(seq,ts->samples[i].latency,buf);\n    }\n\n    graph = sdscatprintf(graph,\n        \"%s - high %lu ms, low %lu ms (all time high %lu ms)\\n\", event,\n        (unsigned long) max, (unsigned long) min, (unsigned long) ts->max);\n    for (j = 0; j < LATENCY_GRAPH_COLS; j++)\n        graph = sdscatlen(graph,\"-\",1);\n    graph = sdscatlen(graph,\"\\n\",1);\n    graph = sparklineRender(graph,seq,LATENCY_GRAPH_COLS,4,SPARKLINE_FILL);\n    freeSparklineSequence(seq);\n    return graph;\n}\n\n/* LATENCY command implementations.\n *\n * LATENCY HISTORY: return time-latency samples for the specified event.\n * LATENCY LATEST: return the latest latency for all the events classes.\n * LATENCY DOCTOR: returns a human readable analysis of instance latency.\n * LATENCY GRAPH: provide an ASCII graph of the latency of the specified event.\n * LATENCY RESET: reset data of a specified event or all the data if no event provided.\n */\nvoid latencyCommand(client *c) {\n    const char *help[] = {\n\"DOCTOR              -- Returns a human readable latency analysis report.\",\n\"GRAPH   <event>     -- Returns an ASCII latency graph for the event class.\",\n\"HISTORY <event>     -- Returns time-latency samples for the event class.\",\n\"LATEST              -- Returns the latest latency samples for all events.\",\n\"RESET   [event ...] -- Resets latency data of one or more event classes.\",\n\"                       (default: reset all data for all event classes)\",\n\"HELP                -- Prints this help.\",\nNULL\n    };\n    struct latencyTimeSeries *ts;\n\n    if (!strcasecmp(c->argv[1]->ptr,\"history\") && c->argc == 3) {\n        /* LATENCY HISTORY <event> */\n        ts = dictFetchValue(server.latency_events,c->argv[2]->ptr);\n        if (ts == NULL) {\n            addReplyArrayLen(c,0);\n        } else {\n            latencyCommandReplyWithSamples(c,ts);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"graph\") && c->argc == 3) {\n        /* LATENCY GRAPH <event> */\n        sds graph;\n        dictEntry *de;\n        char *event;\n\n        de = dictFind(server.latency_events,c->argv[2]->ptr);\n        if (de == NULL) goto nodataerr;\n        ts = dictGetVal(de);\n        event = dictGetKey(de);\n\n        graph = latencyCommandGenSparkeline(event,ts);\n        addReplyVerbatim(c,graph,sdslen(graph),\"txt\");\n        sdsfree(graph);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"latest\") && c->argc == 2) {\n        /* LATENCY LATEST */\n        latencyCommandReplyWithLatestEvents(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"doctor\") && c->argc == 2) {\n        /* LATENCY DOCTOR */\n        sds report = createLatencyReport();\n\n        addReplyVerbatim(c,report,sdslen(report),\"txt\");\n        sdsfree(report);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reset\") && c->argc >= 2) {\n        /* LATENCY RESET */\n        if (c->argc == 2) {\n            addReplyLongLong(c,latencyResetEvent(NULL));\n        } else {\n            int j, resets = 0;\n\n            for (j = 2; j < c->argc; j++)\n                resets += latencyResetEvent(c->argv[j]->ptr);\n            addReplyLongLong(c,resets);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"help\") && c->argc >= 2) {\n        addReplyHelp(c, help);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n    return;\n\nnodataerr:\n    /* Common error when the user asks for an event we have no latency\n     * information about. */\n    addReplyErrorFormat(c,\n        \"No samples available for event '%s'\", (char*) c->argv[2]->ptr);\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/latency.h",
    "content": "/* latency.h -- latency monitor API header file\n * See latency.c for more information.\n *\n * ----------------------------------------------------------------------------\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#ifndef __LATENCY_H\n#define __LATENCY_H\n\n#define LATENCY_TS_LEN 160 /* History length for every monitored event. */\n\n/* Representation of a latency sample: the sampling time and the latency\n * observed in milliseconds. */\nstruct latencySample {\n    int32_t time; /* We don't use time_t to force 4 bytes usage everywhere. */\n    uint32_t latency; /* Latency in milliseconds. */\n};\n\n/* The latency time series for a given event. */\nstruct latencyTimeSeries {\n    int idx; /* Index of the next sample to store. */\n    uint32_t max; /* Max latency observed for this event. */\n    struct latencySample samples[LATENCY_TS_LEN]; /* Latest history. */\n};\n\n/* Latency statistics structure. */\nstruct latencyStats {\n    uint32_t all_time_high; /* Absolute max observed since latest reset. */\n    uint32_t avg;           /* Average of current samples. */\n    uint32_t min;           /* Min of current samples. */\n    uint32_t max;           /* Max of current samples. */\n    uint32_t mad;           /* Mean absolute deviation. */\n    uint32_t samples;       /* Number of non-zero samples. */\n    time_t period;          /* Number of seconds since first event and now. */\n};\n\nvoid latencyMonitorInit(void);\nvoid latencyAddSample(const char *event, mstime_t latency);\nint THPIsEnabled(void);\n\n/* Latency monitoring macros. */\n\n/* Start monitoring an event. We just set the current time. */\n#define latencyStartMonitor(var) if (server.latency_monitor_threshold) { \\\n    var = mstime(); \\\n} else { \\\n    var = 0; \\\n}\n\n/* End monitoring an event, compute the difference with the current time\n * to check the amount of time elapsed. */\n#define latencyEndMonitor(var) if (server.latency_monitor_threshold) { \\\n    var = mstime() - var; \\\n}\n\n/* Add the sample only if the elapsed time is >= to the configured threshold. */\n#define latencyAddSampleIfNeeded(event,var) \\\n    if (server.latency_monitor_threshold && \\\n        (var) >= server.latency_monitor_threshold) \\\n          latencyAddSample((event),(var));\n\n/* Remove time from a nested event. */\n#define latencyRemoveNestedEvent(event_var,nested_var) \\\n    event_var += nested_var;\n\n#endif /* __LATENCY_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lazyfree.c",
    "content": "#include \"server.h\"\n#include \"bio.h\"\n#include \"atomicvar.h\"\n#include \"cluster.h\"\n\nstatic size_t lazyfree_objects = 0;\npthread_mutex_t lazyfree_objects_mutex = PTHREAD_MUTEX_INITIALIZER;\n\n/* Return the number of currently pending objects to free. */\nsize_t lazyfreeGetPendingObjectsCount(void) {\n    size_t aux;\n    atomicGet(lazyfree_objects,aux);\n    return aux;\n}\n\n/* Return the amount of work needed in order to free an object.\n * The return value is not always the actual number of allocations the\n * object is compoesd of, but a number proportional to it.\n *\n * For strings the function always returns 1.\n *\n * For aggregated objects represented by hash tables or other data structures\n * the function just returns the number of elements the object is composed of.\n *\n * Objects composed of single allocations are always reported as having a\n * single item even if they are actually logical composed of multiple\n * elements.\n *\n * For lists the function returns the number of elements in the quicklist\n * representing the list. */\nsize_t lazyfreeGetFreeEffort(robj *obj) {\n    if (obj->type == OBJ_LIST) {\n        quicklist *ql = obj->ptr;\n        return ql->len;\n    } else if (obj->type == OBJ_SET && obj->encoding == OBJ_ENCODING_HT) {\n        dict *ht = obj->ptr;\n        return dictSize(ht);\n    } else if (obj->type == OBJ_ZSET && obj->encoding == OBJ_ENCODING_SKIPLIST){\n        zset *zs = obj->ptr;\n        return zs->zsl->length;\n    } else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) {\n        dict *ht = obj->ptr;\n        return dictSize(ht);\n    } else {\n        return 1; /* Everything else is a single allocation. */\n    }\n}\n\n/* Delete a key, value, and associated expiration entry if any, from the DB.\n * If there are enough allocations to free the value object may be put into\n * a lazy free list instead of being freed synchronously. The lazy free list\n * will be reclaimed in a different bio.c thread. */\n#define LAZYFREE_THRESHOLD 64\nint dbAsyncDelete(redisDb *db, robj *key) {\n    /* Deleting an entry from the expires dict will not free the sds of\n     * the key, because it is shared with the main dictionary. */\n    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);\n\n    /* If the value is composed of a few allocations, to free in a lazy way\n     * is actually just slower... So under a certain limit we just free\n     * the object synchronously. */\n    dictEntry *de = dictUnlink(db->dict,key->ptr);\n    if (de) {\n        robj *val = dictGetVal(de);\n        size_t free_effort = lazyfreeGetFreeEffort(val);\n\n        /* If releasing the object is too much work, do it in the background\n         * by adding the object to the lazy free list.\n         * Note that if the object is shared, to reclaim it now it is not\n         * possible. This rarely happens, however sometimes the implementation\n         * of parts of the Redis core may call incrRefCount() to protect\n         * objects, and then call dbDelete(). In this case we'll fall\n         * through and reach the dictFreeUnlinkedEntry() call, that will be\n         * equivalent to just calling decrRefCount(). */\n        if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) {\n            atomicIncr(lazyfree_objects,1);\n            bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);\n            dictSetVal(db->dict,de,NULL);\n        }\n    }\n\n    /* Release the key-val pair, or just the key if we set the val\n     * field to NULL in order to lazy free it later. */\n    if (de) {\n        dictFreeUnlinkedEntry(db->dict,de);\n        if (server.cluster_enabled) slotToKeyDel(key->ptr);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Free an object, if the object is huge enough, free it in async way. */\nvoid freeObjAsync(robj *o) {\n    size_t free_effort = lazyfreeGetFreeEffort(o);\n    if (free_effort > LAZYFREE_THRESHOLD && o->refcount == 1) {\n        atomicIncr(lazyfree_objects,1);\n        bioCreateBackgroundJob(BIO_LAZY_FREE,o,NULL,NULL);\n    } else {\n        decrRefCount(o);\n    }\n}\n\n/* Empty a Redis DB asynchronously. What the function does actually is to\n * create a new empty set of hash tables and scheduling the old ones for\n * lazy freeing. */\nvoid emptyDbAsync(redisDb *db) {\n    dict *oldht1 = db->dict, *oldht2 = db->expires;\n    db->dict = dictCreate(&dbDictType,NULL);\n    db->expires = dictCreate(&keyptrDictType,NULL);\n    atomicIncr(lazyfree_objects,dictSize(oldht1));\n    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,oldht2);\n}\n\n/* Empty the slots-keys map of Redis CLuster by creating a new empty one\n * and scheduiling the old for lazy freeing. */\nvoid slotToKeyFlushAsync(void) {\n    rax *old = server.cluster->slots_to_keys;\n\n    server.cluster->slots_to_keys = raxNew();\n    memset(server.cluster->slots_keys_count,0,\n           sizeof(server.cluster->slots_keys_count));\n    atomicIncr(lazyfree_objects,old->numele);\n    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,old);\n}\n\n/* Release objects from the lazyfree thread. It's just decrRefCount()\n * updating the count of objects to release. */\nvoid lazyfreeFreeObjectFromBioThread(robj *o) {\n    decrRefCount(o);\n    atomicDecr(lazyfree_objects,1);\n}\n\n/* Release a database from the lazyfree thread. The 'db' pointer is the\n * database which was substitutied with a fresh one in the main thread\n * when the database was logically deleted. 'sl' is a skiplist used by\n * Redis Cluster in order to take the hash slots -> keys mapping. This\n * may be NULL if Redis Cluster is disabled. */\nvoid lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2) {\n    size_t numkeys = dictSize(ht1);\n    dictRelease(ht1);\n    dictRelease(ht2);\n    atomicDecr(lazyfree_objects,numkeys);\n}\n\n/* Release the skiplist mapping Redis Cluster keys to slots in the\n * lazyfree thread. */\nvoid lazyfreeFreeSlotsMapFromBioThread(rax *rt) {\n    size_t len = rt->numele;\n    raxFree(rt);\n    atomicDecr(lazyfree_objects,len);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/listpack.c",
    "content": "/* Listpack -- A lists of strings serialization format\n *\n * This file implements the specification you can find at:\n *\n *  https://github.com/antirez/listpack\n *\n * Copyright (c) 2017, 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#include <limits.h>\n#include <sys/types.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"listpack.h\"\n#include \"listpack_malloc.h\"\n\n#define LP_HDR_SIZE 6       /* 32 bit total len + 16 bit number of elements. */\n#define LP_HDR_NUMELE_UNKNOWN UINT16_MAX\n#define LP_MAX_INT_ENCODING_LEN 9\n#define LP_MAX_BACKLEN_SIZE 5\n#define LP_MAX_ENTRY_BACKLEN 34359738367ULL\n#define LP_ENCODING_INT 0\n#define LP_ENCODING_STRING 1\n\n#define LP_ENCODING_7BIT_UINT 0\n#define LP_ENCODING_7BIT_UINT_MASK 0x80\n#define LP_ENCODING_IS_7BIT_UINT(byte) (((byte)&LP_ENCODING_7BIT_UINT_MASK)==LP_ENCODING_7BIT_UINT)\n\n#define LP_ENCODING_6BIT_STR 0x80\n#define LP_ENCODING_6BIT_STR_MASK 0xC0\n#define LP_ENCODING_IS_6BIT_STR(byte) (((byte)&LP_ENCODING_6BIT_STR_MASK)==LP_ENCODING_6BIT_STR)\n\n#define LP_ENCODING_13BIT_INT 0xC0\n#define LP_ENCODING_13BIT_INT_MASK 0xE0\n#define LP_ENCODING_IS_13BIT_INT(byte) (((byte)&LP_ENCODING_13BIT_INT_MASK)==LP_ENCODING_13BIT_INT)\n\n#define LP_ENCODING_12BIT_STR 0xE0\n#define LP_ENCODING_12BIT_STR_MASK 0xF0\n#define LP_ENCODING_IS_12BIT_STR(byte) (((byte)&LP_ENCODING_12BIT_STR_MASK)==LP_ENCODING_12BIT_STR)\n\n#define LP_ENCODING_16BIT_INT 0xF1\n#define LP_ENCODING_16BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_16BIT_INT(byte) (((byte)&LP_ENCODING_16BIT_INT_MASK)==LP_ENCODING_16BIT_INT)\n\n#define LP_ENCODING_24BIT_INT 0xF2\n#define LP_ENCODING_24BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_24BIT_INT(byte) (((byte)&LP_ENCODING_24BIT_INT_MASK)==LP_ENCODING_24BIT_INT)\n\n#define LP_ENCODING_32BIT_INT 0xF3\n#define LP_ENCODING_32BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_32BIT_INT(byte) (((byte)&LP_ENCODING_32BIT_INT_MASK)==LP_ENCODING_32BIT_INT)\n\n#define LP_ENCODING_64BIT_INT 0xF4\n#define LP_ENCODING_64BIT_INT_MASK 0xFF\n#define LP_ENCODING_IS_64BIT_INT(byte) (((byte)&LP_ENCODING_64BIT_INT_MASK)==LP_ENCODING_64BIT_INT)\n\n#define LP_ENCODING_32BIT_STR 0xF0\n#define LP_ENCODING_32BIT_STR_MASK 0xFF\n#define LP_ENCODING_IS_32BIT_STR(byte) (((byte)&LP_ENCODING_32BIT_STR_MASK)==LP_ENCODING_32BIT_STR)\n\n#define LP_EOF 0xFF\n\n#define LP_ENCODING_6BIT_STR_LEN(p) ((p)[0] & 0x3F)\n#define LP_ENCODING_12BIT_STR_LEN(p) ((((p)[0] & 0xF) << 8) | (p)[1])\n#define LP_ENCODING_32BIT_STR_LEN(p) (((uint32_t)(p)[1]<<0) | \\\n                                      ((uint32_t)(p)[2]<<8) | \\\n                                      ((uint32_t)(p)[3]<<16) | \\\n                                      ((uint32_t)(p)[4]<<24))\n\n#define lpGetTotalBytes(p)           (((uint32_t)(p)[0]<<0) | \\\n                                      ((uint32_t)(p)[1]<<8) | \\\n                                      ((uint32_t)(p)[2]<<16) | \\\n                                      ((uint32_t)(p)[3]<<24))\n\n#define lpGetNumElements(p)          (((uint32_t)(p)[4]<<0) | \\\n                                      ((uint32_t)(p)[5]<<8))\n#define lpSetTotalBytes(p,v) do { \\\n    (p)[0] = (v)&0xff; \\\n    (p)[1] = ((v)>>8)&0xff; \\\n    (p)[2] = ((v)>>16)&0xff; \\\n    (p)[3] = ((v)>>24)&0xff; \\\n} while(0)\n\n#define lpSetNumElements(p,v) do { \\\n    (p)[4] = (v)&0xff; \\\n    (p)[5] = ((v)>>8)&0xff; \\\n} while(0)\n\n/* Convert a string into a signed 64 bit integer.\n * The function returns 1 if the string could be parsed into a (non-overflowing)\n * signed 64 bit int, 0 otherwise. The 'value' will be set to the parsed value\n * when the function returns success.\n *\n * Note that this function demands that the string strictly represents\n * a int64 value: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. *\n *\n * -----------------------------------------------------------------------------\n *\n * Credits: this function was adapted from the Redis source code, file\n * \"utils.c\", function string2ll(), and is copyright:\n *\n * Copyright(C) 2011, Pieter Noordhuis\n * Copyright(C) 2011, Salvatore Sanfilippo\n *\n * The function is released under the BSD 3-clause license.\n */\nint lpStringToInt64(const char *s, unsigned long slen, int64_t *value) {\n    const char *p = s;\n    unsigned long plen = 0;\n    int negative = 0;\n    uint64_t 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 > (UINT64_MAX / 10)) /* Overflow. */\n            return 0;\n        v *= 10;\n\n        if (v > (UINT64_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 > ((uint64_t)(-(INT64_MIN+1))+1)) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > INT64_MAX) /* Overflow. */\n            return 0;\n        if (value != NULL) *value = v;\n    }\n    return 1;\n}\n\n/* Create a new, empty listpack.\n * On success the new listpack is returned, otherwise an error is returned. */\nunsigned char *lpNew(void) {\n    unsigned char *lp = lp_malloc(LP_HDR_SIZE+1);\n    if (lp == NULL) return NULL;\n    lpSetTotalBytes(lp,LP_HDR_SIZE+1);\n    lpSetNumElements(lp,0);\n    lp[LP_HDR_SIZE] = LP_EOF;\n    return lp;\n}\n\n/* Free the specified listpack. */\nvoid lpFree(unsigned char *lp) {\n    lp_free(lp);\n}\n\n/* Given an element 'ele' of size 'size', determine if the element can be\n * represented inside the listpack encoded as integer, and returns\n * LP_ENCODING_INT if so. Otherwise returns LP_ENCODING_STR if no integer\n * encoding is possible.\n *\n * If the LP_ENCODING_INT is returned, the function stores the integer encoded\n * representation of the element in the 'intenc' buffer.\n *\n * Regardless of the returned encoding, 'enclen' is populated by reference to\n * the number of bytes that the string or integer encoded element will require\n * in order to be represented. */\nint lpEncodeGetType(unsigned char *ele, uint32_t size, unsigned char *intenc, uint64_t *enclen) {\n    int64_t v;\n    if (lpStringToInt64((const char*)ele, size, &v)) {\n        if (v >= 0 && v <= 127) {\n            /* Single byte 0-127 integer. */\n            intenc[0] = v;\n            *enclen = 1;\n        } else if (v >= -4096 && v <= 4095) {\n            /* 13 bit integer. */\n            if (v < 0) v = ((int64_t)1<<13)+v;\n            intenc[0] = (v>>8)|LP_ENCODING_13BIT_INT;\n            intenc[1] = v&0xff;\n            *enclen = 2;\n        } else if (v >= -32768 && v <= 32767) {\n            /* 16 bit integer. */\n            if (v < 0) v = ((int64_t)1<<16)+v;\n            intenc[0] = LP_ENCODING_16BIT_INT;\n            intenc[1] = v&0xff;\n            intenc[2] = v>>8;\n            *enclen = 3;\n        } else if (v >= -8388608 && v <= 8388607) {\n            /* 24 bit integer. */\n            if (v < 0) v = ((int64_t)1<<24)+v;\n            intenc[0] = LP_ENCODING_24BIT_INT;\n            intenc[1] = v&0xff;\n            intenc[2] = (v>>8)&0xff;\n            intenc[3] = v>>16;\n            *enclen = 4;\n        } else if (v >= -2147483648 && v <= 2147483647) {\n            /* 32 bit integer. */\n            if (v < 0) v = ((int64_t)1<<32)+v;\n            intenc[0] = LP_ENCODING_32BIT_INT;\n            intenc[1] = v&0xff;\n            intenc[2] = (v>>8)&0xff;\n            intenc[3] = (v>>16)&0xff;\n            intenc[4] = v>>24;\n            *enclen = 5;\n        } else {\n            /* 64 bit integer. */\n            uint64_t uv = v;\n            intenc[0] = LP_ENCODING_64BIT_INT;\n            intenc[1] = uv&0xff;\n            intenc[2] = (uv>>8)&0xff;\n            intenc[3] = (uv>>16)&0xff;\n            intenc[4] = (uv>>24)&0xff;\n            intenc[5] = (uv>>32)&0xff;\n            intenc[6] = (uv>>40)&0xff;\n            intenc[7] = (uv>>48)&0xff;\n            intenc[8] = uv>>56;\n            *enclen = 9;\n        }\n        return LP_ENCODING_INT;\n    } else {\n        if (size < 64) *enclen = 1+size;\n        else if (size < 4096) *enclen = 2+size;\n        else *enclen = 5+size;\n        return LP_ENCODING_STRING;\n    }\n}\n\n/* Store a reverse-encoded variable length field, representing the length\n * of the previous element of size 'l', in the target buffer 'buf'.\n * The function returns the number of bytes used to encode it, from\n * 1 to 5. If 'buf' is NULL the function just returns the number of bytes\n * needed in order to encode the backlen. */\nunsigned long lpEncodeBacklen(unsigned char *buf, uint64_t l) {\n    if (l <= 127) {\n        if (buf) buf[0] = l;\n        return 1;\n    } else if (l < 16383) {\n        if (buf) {\n            buf[0] = l>>7;\n            buf[1] = (l&127)|128;\n        }\n        return 2;\n    } else if (l < 2097151) {\n        if (buf) {\n            buf[0] = l>>14;\n            buf[1] = ((l>>7)&127)|128;\n            buf[2] = (l&127)|128;\n        }\n        return 3;\n    } else if (l < 268435455) {\n        if (buf) {\n            buf[0] = l>>21;\n            buf[1] = ((l>>14)&127)|128;\n            buf[2] = ((l>>7)&127)|128;\n            buf[3] = (l&127)|128;\n        }\n        return 4;\n    } else {\n        if (buf) {\n            buf[0] = l>>28;\n            buf[1] = ((l>>21)&127)|128;\n            buf[2] = ((l>>14)&127)|128;\n            buf[3] = ((l>>7)&127)|128;\n            buf[4] = (l&127)|128;\n        }\n        return 5;\n    }\n}\n\n/* Decode the backlen and returns it. If the encoding looks invalid (more than\n * 5 bytes are used), UINT64_MAX is returned to report the problem. */\nuint64_t lpDecodeBacklen(unsigned char *p) {\n    uint64_t val = 0;\n    uint64_t shift = 0;\n    do {\n        val |= (uint64_t)(p[0] & 127) << shift;\n        if (!(p[0] & 128)) break;\n        shift += 7;\n        p--;\n        if (shift > 28) return UINT64_MAX;\n    } while(1);\n    return val;\n}\n\n/* Encode the string element pointed by 's' of size 'len' in the target\n * buffer 's'. The function should be called with 'buf' having always enough\n * space for encoding the string. This is done by calling lpEncodeGetType()\n * before calling this function. */\nvoid lpEncodeString(unsigned char *buf, unsigned char *s, uint32_t len) {\n    if (len < 64) {\n        buf[0] = len | LP_ENCODING_6BIT_STR;\n        memcpy(buf+1,s,len);\n    } else if (len < 4096) {\n        buf[0] = (len >> 8) | LP_ENCODING_12BIT_STR;\n        buf[1] = len & 0xff;\n        memcpy(buf+2,s,len);\n    } else {\n        buf[0] = LP_ENCODING_32BIT_STR;\n        buf[1] = len & 0xff;\n        buf[2] = (len >> 8) & 0xff;\n        buf[3] = (len >> 16) & 0xff;\n        buf[4] = (len >> 24) & 0xff;\n        memcpy(buf+5,s,len);\n    }\n}\n\n/* Return the encoded length of the listpack element pointed by 'p'. If the\n * element encoding is wrong then 0 is returned. */\nuint32_t lpCurrentEncodedSize(unsigned char *p) {\n    if (LP_ENCODING_IS_7BIT_UINT(p[0])) return 1;\n    if (LP_ENCODING_IS_6BIT_STR(p[0])) return 1+LP_ENCODING_6BIT_STR_LEN(p);\n    if (LP_ENCODING_IS_13BIT_INT(p[0])) return 2;\n    if (LP_ENCODING_IS_16BIT_INT(p[0])) return 3;\n    if (LP_ENCODING_IS_24BIT_INT(p[0])) return 4;\n    if (LP_ENCODING_IS_32BIT_INT(p[0])) return 5;\n    if (LP_ENCODING_IS_64BIT_INT(p[0])) return 9;\n    if (LP_ENCODING_IS_12BIT_STR(p[0])) return 2+LP_ENCODING_12BIT_STR_LEN(p);\n    if (LP_ENCODING_IS_32BIT_STR(p[0])) return 5+LP_ENCODING_32BIT_STR_LEN(p);\n    if (p[0] == LP_EOF) return 1;\n    return 0;\n}\n\n/* Skip the current entry returning the next. It is invalid to call this\n * function if the current element is the EOF element at the end of the\n * listpack, however, while this function is used to implement lpNext(),\n * it does not return NULL when the EOF element is encountered. */\nunsigned char *lpSkip(unsigned char *p) {\n    unsigned long entrylen = lpCurrentEncodedSize(p);\n    entrylen += lpEncodeBacklen(NULL,entrylen);\n    p += entrylen;\n    return p;\n}\n\n/* If 'p' points to an element of the listpack, calling lpNext() will return\n * the pointer to the next element (the one on the right), or NULL if 'p'\n * already pointed to the last element of the listpack. */\nunsigned char *lpNext(unsigned char *lp, unsigned char *p) {\n    ((void) lp); /* lp is not used for now. However lpPrev() uses it. */\n    p = lpSkip(p);\n    if (p[0] == LP_EOF) return NULL;\n    return p;\n}\n\n/* If 'p' points to an element of the listpack, calling lpPrev() will return\n * the pointer to the preivous element (the one on the left), or NULL if 'p'\n * already pointed to the first element of the listpack. */\nunsigned char *lpPrev(unsigned char *lp, unsigned char *p) {\n    if (p-lp == LP_HDR_SIZE) return NULL;\n    p--; /* Seek the first backlen byte of the last element. */\n    uint64_t prevlen = lpDecodeBacklen(p);\n    prevlen += lpEncodeBacklen(NULL,prevlen);\n    return p-prevlen+1; /* Seek the first byte of the previous entry. */\n}\n\n/* Return a pointer to the first element of the listpack, or NULL if the\n * listpack has no elements. */\nunsigned char *lpFirst(unsigned char *lp) {\n    lp += LP_HDR_SIZE; /* Skip the header. */\n    if (lp[0] == LP_EOF) return NULL;\n    return lp;\n}\n\n/* Return a pointer to the last element of the listpack, or NULL if the\n * listpack has no elements. */\nunsigned char *lpLast(unsigned char *lp) {\n    unsigned char *p = lp+lpGetTotalBytes(lp)-1; /* Seek EOF element. */\n    return lpPrev(lp,p); /* Will return NULL if EOF is the only element. */\n}\n\n/* Return the number of elements inside the listpack. This function attempts\n * to use the cached value when within range, otherwise a full scan is\n * needed. As a side effect of calling this function, the listpack header\n * could be modified, because if the count is found to be already within\n * the 'numele' header field range, the new value is set. */\nuint32_t lpLength(unsigned char *lp) {\n    uint32_t numele = lpGetNumElements(lp);\n    if (numele != LP_HDR_NUMELE_UNKNOWN) return numele;\n\n    /* Too many elements inside the listpack. We need to scan in order\n     * to get the total number. */\n    uint32_t count = 0;\n    unsigned char *p = lpFirst(lp);\n    while(p) {\n        count++;\n        p = lpNext(lp,p);\n    }\n\n    /* If the count is again within range of the header numele field,\n     * set it. */\n    if (count < LP_HDR_NUMELE_UNKNOWN) lpSetNumElements(lp,count);\n    return count;\n}\n\n/* Return the listpack element pointed by 'p'.\n *\n * The function changes behavior depending on the passed 'intbuf' value.\n * Specifically, if 'intbuf' is NULL:\n *\n * If the element is internally encoded as an integer, the function returns\n * NULL and populates the integer value by reference in 'count'. Otherwise if\n * the element is encoded as a string a pointer to the string (pointing inside\n * the listpack itself) is returned, and 'count' is set to the length of the\n * string.\n *\n * If instead 'intbuf' points to a buffer passed by the caller, that must be\n * at least LP_INTBUF_SIZE bytes, the function always returns the element as\n * it was a string (returning the pointer to the string and setting the\n * 'count' argument to the string length by reference). However if the element\n * is encoded as an integer, the 'intbuf' buffer is used in order to store\n * the string representation.\n *\n * The user should use one or the other form depending on what the value will\n * be used for. If there is immediate usage for an integer value returned\n * by the function, than to pass a buffer (and convert it back to a number)\n * is of course useless.\n *\n * If the function is called against a badly encoded ziplist, so that there\n * is no valid way to parse it, the function returns like if there was an\n * integer encoded with value 12345678900000000 + <unrecognized byte>, this may\n * be an hint to understand that something is wrong. To crash in this case is\n * not sensible because of the different requirements of the application using\n * this lib.\n *\n * Similarly, there is no error returned since the listpack normally can be\n * assumed to be valid, so that would be a very high API cost. However a function\n * in order to check the integrity of the listpack at load time is provided,\n * check lpIsValid(). */\nunsigned char *lpGet(unsigned char *p, int64_t *count, unsigned char *intbuf) {\n    int64_t val;\n    uint64_t uval, negstart, negmax;\n\n    if (LP_ENCODING_IS_7BIT_UINT(p[0])) {\n        negstart = UINT64_MAX; /* 7 bit ints are always positive. */\n        negmax = 0;\n        uval = p[0] & 0x7f;\n    } else if (LP_ENCODING_IS_6BIT_STR(p[0])) {\n        *count = LP_ENCODING_6BIT_STR_LEN(p);\n        return p+1;\n    } else if (LP_ENCODING_IS_13BIT_INT(p[0])) {\n        uval = ((p[0]&0x1f)<<8) | p[1];\n        negstart = (uint64_t)1<<12;\n        negmax = 8191;\n    } else if (LP_ENCODING_IS_16BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8;\n        negstart = (uint64_t)1<<15;\n        negmax = UINT16_MAX;\n    } else if (LP_ENCODING_IS_24BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8 |\n               (uint64_t)p[3]<<16;\n        negstart = (uint64_t)1<<23;\n        negmax = UINT32_MAX>>8;\n    } else if (LP_ENCODING_IS_32BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8 |\n               (uint64_t)p[3]<<16 |\n               (uint64_t)p[4]<<24;\n        negstart = (uint64_t)1<<31;\n        negmax = UINT32_MAX;\n    } else if (LP_ENCODING_IS_64BIT_INT(p[0])) {\n        uval = (uint64_t)p[1] |\n               (uint64_t)p[2]<<8 |\n               (uint64_t)p[3]<<16 |\n               (uint64_t)p[4]<<24 |\n               (uint64_t)p[5]<<32 |\n               (uint64_t)p[6]<<40 |\n               (uint64_t)p[7]<<48 |\n               (uint64_t)p[8]<<56;\n        negstart = (uint64_t)1<<63;\n        negmax = UINT64_MAX;\n    } else if (LP_ENCODING_IS_12BIT_STR(p[0])) {\n        *count = LP_ENCODING_12BIT_STR_LEN(p);\n        return p+2;\n    } else if (LP_ENCODING_IS_32BIT_STR(p[0])) {\n        *count = LP_ENCODING_32BIT_STR_LEN(p);\n        return p+5;\n    } else {\n        uval = 12345678900000000ULL + p[0];\n        negstart = UINT64_MAX;\n        negmax = 0;\n    }\n\n    /* We reach this code path only for integer encodings.\n     * Convert the unsigned value to the signed one using two's complement\n     * rule. */\n    if (uval >= negstart) {\n        /* This three steps conversion should avoid undefined behaviors\n         * in the unsigned -> signed conversion. */\n        uval = negmax-uval;\n        val = uval;\n        val = -val-1;\n    } else {\n        val = uval;\n    }\n\n    /* Return the string representation of the integer or the value itself\n     * depending on intbuf being NULL or not. */\n    if (intbuf) {\n        *count = snprintf((char*)intbuf,LP_INTBUF_SIZE,\"%lld\",(long long)val);\n        return intbuf;\n    } else {\n        *count = val;\n        return NULL;\n    }\n}\n\n/* Insert, delete or replace the specified element 'ele' of length 'len' at\n * the specified position 'p', with 'p' being a listpack element pointer\n * obtained with lpFirst(), lpLast(), lpIndex(), lpNext(), lpPrev() or\n * lpSeek().\n *\n * The element is inserted before, after, or replaces the element pointed\n * by 'p' depending on the 'where' argument, that can be LP_BEFORE, LP_AFTER\n * or LP_REPLACE.\n *\n * If 'ele' is set to NULL, the function removes the element pointed by 'p'\n * instead of inserting one.\n *\n * Returns NULL on out of memory or when the listpack total length would exceed\n * the max allowed size of 2^32-1, otherwise the new pointer to the listpack\n * holding the new element is returned (and the old pointer passed is no longer\n * considered valid)\n *\n * If 'newp' is not NULL, at the end of a successful call '*newp' will be set\n * to the address of the element just added, so that it will be possible to\n * continue an interation with lpNext() and lpPrev().\n *\n * For deletion operations ('ele' set to NULL) 'newp' is set to the next\n * element, on the right of the deleted one, or to NULL if the deleted element\n * was the last one. */\nunsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp) {\n    unsigned char intenc[LP_MAX_INT_ENCODING_LEN];\n    unsigned char backlen[LP_MAX_BACKLEN_SIZE];\n\n    uint64_t enclen; /* The length of the encoded element. */\n\n    /* An element pointer set to NULL means deletion, which is conceptually\n     * replacing the element with a zero-length element. So whatever we\n     * get passed as 'where', set it to LP_REPLACE. */\n    if (ele == NULL) where = LP_REPLACE;\n\n    /* If we need to insert after the current element, we just jump to the\n     * next element (that could be the EOF one) and handle the case of\n     * inserting before. So the function will actually deal with just two\n     * cases: LP_BEFORE and LP_REPLACE. */\n    if (where == LP_AFTER) {\n        p = lpSkip(p);\n        where = LP_BEFORE;\n    }\n\n    /* Store the offset of the element 'p', so that we can obtain its\n     * address again after a reallocation. */\n    unsigned long poff = p-lp;\n\n    /* Calling lpEncodeGetType() results into the encoded version of the\n     * element to be stored into 'intenc' in case it is representable as\n     * an integer: in that case, the function returns LP_ENCODING_INT.\n     * Otherwise if LP_ENCODING_STR is returned, we'll have to call\n     * lpEncodeString() to actually write the encoded string on place later.\n     *\n     * Whatever the returned encoding is, 'enclen' is populated with the\n     * length of the encoded element. */\n    int enctype;\n    if (ele) {\n        enctype = lpEncodeGetType(ele,size,intenc,&enclen);\n    } else {\n        enctype = -1;\n        enclen = 0;\n    }\n\n    /* We need to also encode the backward-parsable length of the element\n     * and append it to the end: this allows to traverse the listpack from\n     * the end to the start. */\n    unsigned long backlen_size = ele ? lpEncodeBacklen(backlen,enclen) : 0;\n    uint64_t old_listpack_bytes = lpGetTotalBytes(lp);\n    uint32_t replaced_len  = 0;\n    if (where == LP_REPLACE) {\n        replaced_len = lpCurrentEncodedSize(p);\n        replaced_len += lpEncodeBacklen(NULL,replaced_len);\n    }\n\n    uint64_t new_listpack_bytes = old_listpack_bytes + enclen + backlen_size\n                                  - replaced_len;\n    if (new_listpack_bytes > UINT32_MAX) return NULL;\n\n    /* We now need to reallocate in order to make space or shrink the\n     * allocation (in case 'when' value is LP_REPLACE and the new element is\n     * smaller). However we do that before memmoving the memory to\n     * make room for the new element if the final allocation will get\n     * larger, or we do it after if the final allocation will get smaller. */\n\n    unsigned char *dst = lp + poff; /* May be updated after reallocation. */\n\n    /* Realloc before: we need more room. */\n    if (new_listpack_bytes > old_listpack_bytes) {\n        if ((lp = lp_realloc(lp,new_listpack_bytes)) == NULL) return NULL;\n        dst = lp + poff;\n    }\n\n    /* Setup the listpack relocating the elements to make the exact room\n     * we need to store the new one. */\n    if (where == LP_BEFORE) {\n        memmove(dst+enclen+backlen_size,dst,old_listpack_bytes-poff);\n    } else { /* LP_REPLACE. */\n        long lendiff = (enclen+backlen_size)-replaced_len;\n        memmove(dst+replaced_len+lendiff,\n                dst+replaced_len,\n                old_listpack_bytes-poff-replaced_len);\n    }\n\n    /* Realloc after: we need to free space. */\n    if (new_listpack_bytes < old_listpack_bytes) {\n        if ((lp = lp_realloc(lp,new_listpack_bytes)) == NULL) return NULL;\n        dst = lp + poff;\n    }\n\n    /* Store the entry. */\n    if (newp) {\n        *newp = dst;\n        /* In case of deletion, set 'newp' to NULL if the next element is\n         * the EOF element. */\n        if (!ele && dst[0] == LP_EOF) *newp = NULL;\n    }\n    if (ele) {\n        if (enctype == LP_ENCODING_INT) {\n            memcpy(dst,intenc,enclen);\n        } else {\n            lpEncodeString(dst,ele,size);\n        }\n        dst += enclen;\n        memcpy(dst,backlen,backlen_size);\n        dst += backlen_size;\n    }\n\n    /* Update header. */\n    if (where != LP_REPLACE || ele == NULL) {\n        uint32_t num_elements = lpGetNumElements(lp);\n        if (num_elements != LP_HDR_NUMELE_UNKNOWN) {\n            if (ele)\n                lpSetNumElements(lp,num_elements+1);\n            else\n                lpSetNumElements(lp,num_elements-1);\n        }\n    }\n    lpSetTotalBytes(lp,new_listpack_bytes);\n\n#if 0\n    /* This code path is normally disabled: what it does is to force listpack\n     * to return *always* a new pointer after performing some modification to\n     * the listpack, even if the previous allocation was enough. This is useful\n     * in order to spot bugs in code using listpacks: by doing so we can find\n     * if the caller forgets to set the new pointer where the listpack reference\n     * is stored, after an update. */\n    unsigned char *oldlp = lp;\n    lp = lp_malloc(new_listpack_bytes);\n    memcpy(lp,oldlp,new_listpack_bytes);\n    if (newp) {\n        unsigned long offset = (*newp)-oldlp;\n        *newp = lp + offset;\n    }\n    /* Make sure the old allocation contains garbage. */\n    memset(oldlp,'A',new_listpack_bytes);\n    lp_free(oldlp);\n#endif\n\n    return lp;\n}\n\n/* Append the specified element 'ele' of length 'len' at the end of the\n * listpack. It is implemented in terms of lpInsert(), so the return value is\n * the same as lpInsert(). */\nunsigned char *lpAppend(unsigned char *lp, unsigned char *ele, uint32_t size) {\n    uint64_t listpack_bytes = lpGetTotalBytes(lp);\n    unsigned char *eofptr = lp + listpack_bytes - 1;\n    return lpInsert(lp,ele,size,eofptr,LP_BEFORE,NULL);\n}\n\n/* Remove the element pointed by 'p', and return the resulting listpack.\n * If 'newp' is not NULL, the next element pointer (to the right of the\n * deleted one) is returned by reference. If the deleted element was the\n * last one, '*newp' is set to NULL. */\nunsigned char *lpDelete(unsigned char *lp, unsigned char *p, unsigned char **newp) {\n    return lpInsert(lp,NULL,0,p,LP_REPLACE,newp);\n}\n\n/* Return the total number of bytes the listpack is composed of. */\nuint32_t lpBytes(unsigned char *lp) {\n    return lpGetTotalBytes(lp);\n}\n\n/* Seek the specified element and returns the pointer to the seeked element.\n * Positive indexes specify the zero-based element to seek from the head to\n * the tail, negative indexes specify elements starting from the tail, where\n * -1 means the last element, -2 the penultimate and so forth. If the index\n * is out of range, NULL is returned. */\nunsigned char *lpSeek(unsigned char *lp, long index) {\n    int forward = 1; /* Seek forward by default. */\n\n    /* We want to seek from left to right or the other way around\n     * depending on the listpack length and the element position.\n     * However if the listpack length cannot be obtained in constant time,\n     * we always seek from left to right. */\n    uint32_t numele = lpGetNumElements(lp);\n    if (numele != LP_HDR_NUMELE_UNKNOWN) {\n        if (index < 0) index = (long)numele+index;\n        if (index < 0) return NULL; /* Index still < 0 means out of range. */\n        if (index >= numele) return NULL; /* Out of range the other side. */\n        /* We want to scan right-to-left if the element we are looking for\n         * is past the half of the listpack. */\n        if (index > numele/2) {\n            forward = 0;\n            /* Left to right scanning always expects a negative index. Convert\n             * our index to negative form. */\n            index -= numele;\n        }\n    } else {\n        /* If the listpack length is unspecified, for negative indexes we\n         * want to always scan left-to-right. */\n        if (index < 0) forward = 0;\n    }\n\n    /* Forward and backward scanning is trivially based on lpNext()/lpPrev(). */\n    if (forward) {\n        unsigned char *ele = lpFirst(lp);\n        while (index > 0 && ele) {\n            ele = lpNext(lp,ele);\n            index--;\n        }\n        return ele;\n    } else {\n        unsigned char *ele = lpLast(lp);\n        while (index < -1 && ele) {\n            ele = lpPrev(lp,ele);\n            index++;\n        }\n        return ele;\n    }\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/listpack.h",
    "content": "/* Listpack -- A lists of strings serialization format\n *\n * This file implements the specification you can find at:\n *\n *  https://github.com/antirez/listpack\n *\n * Copyright (c) 2017, 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 __LISTPACK_H\n#define __LISTPACK_H\n\n#include <stdint.h>\n\n#define LP_INTBUF_SIZE 21 /* 20 digits of -2^63 + 1 null term = 21. */\n\n/* lpInsert() where argument possible values: */\n#define LP_BEFORE 0\n#define LP_AFTER 1\n#define LP_REPLACE 2\n\nunsigned char *lpNew(void);\nvoid lpFree(unsigned char *lp);\nunsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp);\nunsigned char *lpAppend(unsigned char *lp, unsigned char *ele, uint32_t size);\nunsigned char *lpDelete(unsigned char *lp, unsigned char *p, unsigned char **newp);\nuint32_t lpLength(unsigned char *lp);\nunsigned char *lpGet(unsigned char *p, int64_t *count, unsigned char *intbuf);\nunsigned char *lpFirst(unsigned char *lp);\nunsigned char *lpLast(unsigned char *lp);\nunsigned char *lpNext(unsigned char *lp, unsigned char *p);\nunsigned char *lpPrev(unsigned char *lp, unsigned char *p);\nuint32_t lpBytes(unsigned char *lp);\nunsigned char *lpSeek(unsigned char *lp, long index);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/listpack_malloc.h",
    "content": "/* Listpack -- A lists of strings serialization format\n * https://github.com/antirez/listpack\n *\n * Copyright (c) 2017, 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/* Allocator selection.\n *\n * This file is used in order to change the Rax allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#ifndef LISTPACK_ALLOC_H\n#define LISTPACK_ALLOC_H\n#include \"zmalloc.h\"\n#define lp_malloc zmalloc\n#define lp_realloc zrealloc\n#define lp_free zfree\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/localtime.c",
    "content": "/*\n * Copyright (c) 2018, 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 <time.h>\n\n/* This is a safe version of localtime() which contains no locks and is\n * fork() friendly. Even the _r version of localtime() cannot be used safely\n * in Redis. Another thread may be calling localtime() while the main thread\n * forks(). Later when the child process calls localtime() again, for instance\n * in order to log something to the Redis log, it may deadlock: in the copy\n * of the address space of the forked process the lock will never be released.\n *\n * This function takes the timezone 'tz' as argument, and the 'dst' flag is\n * used to check if daylight saving time is currently in effect. The caller\n * of this function should obtain such information calling tzset() ASAP in the\n * main() function to obtain the timezone offset from the 'timezone' global\n * variable. To obtain the daylight information, if it is currently active or not,\n * one trick is to call localtime() in main() ASAP as well, and get the\n * information from the tm_isdst field of the tm structure. However the daylight\n * time may switch in the future for long running processes, so this information\n * should be refreshed at safe times.\n *\n * Note that this function does not work for dates < 1/1/1970, it is solely\n * designed to work with what time(NULL) may return, and to support Redis\n * logging of the dates, it's not really a complete implementation. */\nstatic int is_leap_year(time_t year) {\n    if (year % 4) return 0;         /* A year not divisible by 4 is not leap. */\n    else if (year % 100) return 1;  /* If div by 4 and not 100 is surely leap. */\n    else if (year % 400) return 0;  /* If div by 100 *and* not by 400 is not leap. */\n    else return 1;                  /* If div by 100 and 400 is leap. */\n}\n\nvoid nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst) {\n    const time_t secs_min = 60;\n    const time_t secs_hour = 3600;\n    const time_t secs_day = 3600*24;\n\n    t -= tz;                            /* Adjust for timezone. */\n    t += 3600*dst;                      /* Adjust for daylight time. */\n    time_t days = t / secs_day;         /* Days passed since epoch. */\n    time_t seconds = t % secs_day;      /* Remaining seconds. */\n\n    tmp->tm_isdst = dst;\n    tmp->tm_hour = seconds / secs_hour;\n    tmp->tm_min = (seconds % secs_hour) / secs_min;\n    tmp->tm_sec = (seconds % secs_hour) % secs_min;\n\n    /* 1/1/1970 was a Thursday, that is, day 4 from the POV of the tm structure\n     * where sunday = 0, so to calculate the day of the week we have to add 4\n     * and take the modulo by 7. */\n    tmp->tm_wday = (days+4)%7;\n\n    /* Calculate the current year. */\n    tmp->tm_year = 1970;\n    while(1) {\n        /* Leap years have one day more. */\n        time_t days_this_year = 365 + is_leap_year(tmp->tm_year);\n        if (days_this_year > days) break;\n        days -= days_this_year;\n        tmp->tm_year++;\n    }\n    tmp->tm_yday = days;  /* Number of day of the current year. */\n\n    /* We need to calculate in which month and day of the month we are. To do\n     * so we need to skip days according to how many days there are in each\n     * month, and adjust for the leap year that has one more day in February. */\n    int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};\n    mdays[1] += is_leap_year(tmp->tm_year);\n\n    tmp->tm_mon = 0;\n    while(days >= mdays[tmp->tm_mon]) {\n        days -= mdays[tmp->tm_mon];\n        tmp->tm_mon++;\n    }\n\n    tmp->tm_mday = days+1;  /* Add 1 since our 'days' is zero-based. */\n    tmp->tm_year -= 1900;   /* Surprisingly tm_year is year-1900. */\n}\n\n#ifdef LOCALTIME_TEST_MAIN\n#include <stdio.h>\n\nint main(void) {\n    /* Obtain timezone and daylight info. */\n    tzset(); /* Now 'timezome' global is populated. */\n    time_t t = time(NULL);\n    struct tm *aux = localtime(&t);\n    int daylight_active = aux->tm_isdst;\n\n    struct tm tm;\n    char buf[1024];\n\n    nolocks_localtime(&tm,t,timezone,daylight_active);\n    strftime(buf,sizeof(buf),\"%d %b %H:%M:%S\",&tm);\n    printf(\"[timezone: %d, dl: %d] %s\\n\", (int)timezone, (int)daylight_active, buf);\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lolwut.c",
    "content": "/*\n * Copyright (c) 2018, 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 * This file implements the LOLWUT command. The command should do something\n * fun and interesting, and should be replaced by a new implementation at\n * each new version of Redis.\n */\n\n#include \"server.h\"\n#include \"lolwut.h\"\n#include <math.h>\n\nvoid lolwut5Command(client *c);\nvoid lolwut6Command(client *c);\n\n/* The default target for LOLWUT if no matching version was found.\n * This is what unstable versions of Redis will display. */\nvoid lolwutUnstableCommand(client *c) {\n    sds rendered = sdsnew(\"Redis ver. \");\n    rendered = sdscat(rendered,REDIS_VERSION);\n    rendered = sdscatlen(rendered,\"\\n\",1);\n    addReplyVerbatim(c,rendered,sdslen(rendered),\"txt\");\n    sdsfree(rendered);\n}\n\n/* LOLWUT [VERSION <version>] [... version specific arguments ...] */\nvoid lolwutCommand(client *c) {\n    char *v = REDIS_VERSION;\n    char verstr[64];\n\n    if (c->argc >= 3 && !strcasecmp(c->argv[1]->ptr,\"version\")) {\n        long ver;\n        if (getLongFromObjectOrReply(c,c->argv[2],&ver,NULL) != C_OK) return;\n        snprintf(verstr,sizeof(verstr),\"%u.0.0\",(unsigned int)ver);\n        v = verstr;\n\n        /* Adjust argv/argc to filter the \"VERSION ...\" option, since the\n         * specific LOLWUT version implementations don't know about it\n         * and expect their arguments. */\n        c->argv += 2;\n        c->argc -= 2;\n    }\n\n    if ((v[0] == '5' && v[1] == '.' && v[2] != '9') ||\n        (v[0] == '4' && v[1] == '.' && v[2] == '9'))\n        lolwut5Command(c);\n    else if ((v[0] == '6' && v[1] == '.' && v[2] != '9') ||\n             (v[0] == '5' && v[1] == '.' && v[2] == '9'))\n        lolwut6Command(c);\n    else\n        lolwutUnstableCommand(c);\n\n    /* Fix back argc/argv in case of VERSION argument. */\n    if (v == verstr) {\n        c->argv -= 2;\n        c->argc += 2;\n    }\n}\n\n/* ========================== LOLWUT Canvase ===============================\n * Many LOWUT versions will likely print some computer art to the screen.\n * This is the case with LOLWUT 5 and LOLWUT 6, so here there is a generic\n * canvas implementation that can be reused.  */\n\n/* Allocate and return a new canvas of the specified size. */\nlwCanvas *lwCreateCanvas(int width, int height, int bgcolor) {\n    lwCanvas *canvas = zmalloc(sizeof(*canvas));\n    canvas->width = width;\n    canvas->height = height;\n    canvas->pixels = zmalloc(width*height);\n    memset(canvas->pixels,bgcolor,width*height);\n    return canvas;\n}\n\n/* Free the canvas created by lwCreateCanvas(). */\nvoid lwFreeCanvas(lwCanvas *canvas) {\n    zfree(canvas->pixels);\n    zfree(canvas);\n}\n\n/* Set a pixel to the specified color. Color is 0 or 1, where zero means no\n * dot will be displyed, and 1 means dot will be displayed.\n * Coordinates are arranged so that left-top corner is 0,0. You can write\n * out of the size of the canvas without issues. */\nvoid lwDrawPixel(lwCanvas *canvas, int x, int y, int color) {\n    if (x < 0 || x >= canvas->width ||\n        y < 0 || y >= canvas->height) return;\n    canvas->pixels[x+y*canvas->width] = color;\n}\n\n/* Return the value of the specified pixel on the canvas. */\nint lwGetPixel(lwCanvas *canvas, int x, int y) {\n    if (x < 0 || x >= canvas->width ||\n        y < 0 || y >= canvas->height) return 0;\n    return canvas->pixels[x+y*canvas->width];\n}\n\n/* Draw a line from x1,y1 to x2,y2 using the Bresenham algorithm. */\nvoid lwDrawLine(lwCanvas *canvas, int x1, int y1, int x2, int y2, int color) {\n    int dx = abs(x2-x1);\n    int dy = abs(y2-y1);\n    int sx = (x1 < x2) ? 1 : -1;\n    int sy = (y1 < y2) ? 1 : -1;\n    int err = dx-dy, e2;\n\n    while(1) {\n        lwDrawPixel(canvas,x1,y1,color);\n        if (x1 == x2 && y1 == y2) break;\n        e2 = err*2;\n        if (e2 > -dy) {\n            err -= dy;\n            x1 += sx;\n        }\n        if (e2 < dx) {\n            err += dx;\n            y1 += sy;\n        }\n    }\n}\n\n/* Draw a square centered at the specified x,y coordinates, with the specified\n * rotation angle and size. In order to write a rotated square, we use the\n * trivial fact that the parametric equation:\n *\n *  x = sin(k)\n *  y = cos(k)\n *\n * Describes a circle for values going from 0 to 2*PI. So basically if we start\n * at 45 degrees, that is k = PI/4, with the first point, and then we find\n * the other three points incrementing K by PI/2 (90 degrees), we'll have the\n * points of the square. In order to rotate the square, we just start with\n * k = PI/4 + rotation_angle, and we are done.\n *\n * Of course the vanilla equations above will describe the square inside a\n * circle of radius 1, so in order to draw larger squares we'll have to\n * multiply the obtained coordinates, and then translate them. However this\n * is much simpler than implementing the abstract concept of 2D shape and then\n * performing the rotation/translation transformation, so for LOLWUT it's\n * a good approach. */\nvoid lwDrawSquare(lwCanvas *canvas, int x, int y, float size, float angle, int color) {\n    int px[4], py[4];\n\n    /* Adjust the desired size according to the fact that the square inscribed\n     * into a circle of radius 1 has the side of length SQRT(2). This way\n     * size becomes a simple multiplication factor we can use with our\n     * coordinates to magnify them. */\n    size /= 1.4142135623;\n    size = round(size);\n\n    /* Compute the four points. */\n    float k = M_PI/4 + angle;\n    for (int j = 0; j < 4; j++) {\n        px[j] = round(sin(k) * size + x);\n        py[j] = round(cos(k) * size + y);\n        k += M_PI/2;\n    }\n\n    /* Draw the square. */\n    for (int j = 0; j < 4; j++)\n        lwDrawLine(canvas,px[j],py[j],px[(j+1)%4],py[(j+1)%4],color);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lolwut.h",
    "content": "/*\n * Copyright (c) 2018-2019, 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/* This structure represents our canvas. Drawing functions will take a pointer\n * to a canvas to write to it. Later the canvas can be rendered to a string\n * suitable to be printed on the screen, using unicode Braille characters. */\n\n/* This represents a very simple generic canvas in order to draw stuff.\n * It's up to each LOLWUT versions to translate what they draw to the\n * screen, depending on the result to accomplish. */\n\n#ifndef __LOLWUT_H\n#define __LOLWUT_H\n\ntypedef struct lwCanvas {\n    int width;\n    int height;\n    char *pixels;\n} lwCanvas;\n\n/* Drawing functions implemented inside lolwut.c. */\nlwCanvas *lwCreateCanvas(int width, int height, int bgcolor);\nvoid lwFreeCanvas(lwCanvas *canvas);\nvoid lwDrawPixel(lwCanvas *canvas, int x, int y, int color);\nint lwGetPixel(lwCanvas *canvas, int x, int y);\nvoid lwDrawLine(lwCanvas *canvas, int x1, int y1, int x2, int y2, int color);\nvoid lwDrawSquare(lwCanvas *canvas, int x, int y, float size, float angle, int color);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lolwut5.c",
    "content": "/*\n * Copyright (c) 2018, 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 * This file implements the LOLWUT command. The command should do something\n * fun and interesting, and should be replaced by a new implementation at\n * each new version of Redis.\n */\n\n#include \"server.h\"\n#include \"lolwut.h\"\n#include <math.h>\n\n/* Translate a group of 8 pixels (2x4 vertical rectangle) to the corresponding\n * braille character. The byte should correspond to the pixels arranged as\n * follows, where 0 is the least significant bit, and 7 the most significant\n * bit:\n *\n *   0 3\n *   1 4\n *   2 5\n *   6 7\n *\n * The corresponding utf8 encoded character is set into the three bytes\n * pointed by 'output'.\n */\n#include <stdio.h>\nvoid lwTranslatePixelsGroup(int byte, char *output) {\n    int code = 0x2800 + byte;\n    /* Convert to unicode. This is in the U0800-UFFFF range, so we need to\n     * emit it like this in three bytes:\n     * 1110xxxx 10xxxxxx 10xxxxxx. */\n    output[0] = 0xE0 | (code >> 12);          /* 1110-xxxx */\n    output[1] = 0x80 | ((code >> 6) & 0x3F);  /* 10-xxxxxx */\n    output[2] = 0x80 | (code & 0x3F);         /* 10-xxxxxx */\n}\n\n/* Schotter, the output of LOLWUT of Redis 5, is a computer graphic art piece\n * generated by Georg Nees in the 60s. It explores the relationship between\n * caos and order.\n *\n * The function creates the canvas itself, depending on the columns available\n * in the output display and the number of squares per row and per column\n * requested by the caller. */\nlwCanvas *lwDrawSchotter(int console_cols, int squares_per_row, int squares_per_col) {\n    /* Calculate the canvas size. */\n    int canvas_width = console_cols*2;\n    int padding = canvas_width > 4 ? 2 : 0;\n    float square_side = (float)(canvas_width-padding*2) / squares_per_row;\n    int canvas_height = square_side * squares_per_col + padding*2;\n    lwCanvas *canvas = lwCreateCanvas(canvas_width, canvas_height, 0);\n\n    for (int y = 0; y < squares_per_col; y++) {\n        for (int x = 0; x < squares_per_row; x++) {\n            int sx = x * square_side + square_side/2 + padding;\n            int sy = y * square_side + square_side/2 + padding;\n            /* Rotate and translate randomly as we go down to lower\n             * rows. */\n            float angle = 0;\n            if (y > 1) {\n                float r1 = (float)rand() / RAND_MAX / squares_per_col * y;\n                float r2 = (float)rand() / RAND_MAX / squares_per_col * y;\n                float r3 = (float)rand() / RAND_MAX / squares_per_col * y;\n                if (rand() % 2) r1 = -r1;\n                if (rand() % 2) r2 = -r2;\n                if (rand() % 2) r3 = -r3;\n                angle = r1;\n                sx += r2*square_side/3;\n                sy += r3*square_side/3;\n            }\n            lwDrawSquare(canvas,sx,sy,square_side,angle,1);\n        }\n    }\n\n    return canvas;\n}\n\n/* Converts the canvas to an SDS string representing the UTF8 characters to\n * print to the terminal in order to obtain a graphical representaiton of the\n * logical canvas. The actual returned string will require a terminal that is\n * width/2 large and height/4 tall in order to hold the whole image without\n * overflowing or scrolling, since each Barille character is 2x4. */\nstatic sds renderCanvas(lwCanvas *canvas) {\n    sds text = sdsempty();\n    for (int y = 0; y < canvas->height; y += 4) {\n        for (int x = 0; x < canvas->width; x += 2) {\n            /* We need to emit groups of 8 bits according to a specific\n             * arrangement. See lwTranslatePixelsGroup() for more info. */\n            int byte = 0;\n            if (lwGetPixel(canvas,x,y)) byte |= (1<<0);\n            if (lwGetPixel(canvas,x,y+1)) byte |= (1<<1);\n            if (lwGetPixel(canvas,x,y+2)) byte |= (1<<2);\n            if (lwGetPixel(canvas,x+1,y)) byte |= (1<<3);\n            if (lwGetPixel(canvas,x+1,y+1)) byte |= (1<<4);\n            if (lwGetPixel(canvas,x+1,y+2)) byte |= (1<<5);\n            if (lwGetPixel(canvas,x,y+3)) byte |= (1<<6);\n            if (lwGetPixel(canvas,x+1,y+3)) byte |= (1<<7);\n            char unicode[3];\n            lwTranslatePixelsGroup(byte,unicode);\n            text = sdscatlen(text,unicode,3);\n        }\n        if (y != canvas->height-1) text = sdscatlen(text,\"\\n\",1);\n    }\n    return text;\n}\n\n/* The LOLWUT command:\n *\n * LOLWUT [terminal columns] [squares-per-row] [squares-per-col]\n *\n * By default the command uses 66 columns, 8 squares per row, 12 squares\n * per column.\n */\nvoid lolwut5Command(client *c) {\n    long cols = 66;\n    long squares_per_row = 8;\n    long squares_per_col = 12;\n\n    /* Parse the optional arguments if any. */\n    if (c->argc > 1 &&\n        getLongFromObjectOrReply(c,c->argv[1],&cols,NULL) != C_OK)\n        return;\n\n    if (c->argc > 2 &&\n        getLongFromObjectOrReply(c,c->argv[2],&squares_per_row,NULL) != C_OK)\n        return;\n\n    if (c->argc > 3 &&\n        getLongFromObjectOrReply(c,c->argv[3],&squares_per_col,NULL) != C_OK)\n        return;\n\n    /* Limits. We want LOLWUT to be always reasonably fast and cheap to execute\n     * so we have maximum number of columns, rows, and output resulution. */\n    if (cols < 1) cols = 1;\n    if (cols > 1000) cols = 1000;\n    if (squares_per_row < 1) squares_per_row = 1;\n    if (squares_per_row > 200) squares_per_row = 200;\n    if (squares_per_col < 1) squares_per_col = 1;\n    if (squares_per_col > 200) squares_per_col = 200;\n\n    /* Generate some computer art and reply. */\n    lwCanvas *canvas = lwDrawSchotter(cols,squares_per_row,squares_per_col);\n    sds rendered = renderCanvas(canvas);\n    rendered = sdscat(rendered,\n        \"\\nGeorg Nees - schotter, plotter on paper, 1968. Redis ver. \");\n    rendered = sdscat(rendered,REDIS_VERSION);\n    rendered = sdscatlen(rendered,\"\\n\",1);\n    addReplyVerbatim(c,rendered,sdslen(rendered),\"txt\");\n    sdsfree(rendered);\n    lwFreeCanvas(canvas);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lolwut6.c",
    "content": "/*\n * Copyright (c) 2019, 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 * This file implements the LOLWUT command. The command should do something\n * fun and interesting, and should be replaced by a new implementation at\n * each new version of Redis.\n *\n * Thanks to Michele Hiki Falcone for the original image that ispired\n * the image, part of his game, Plaguemon.\n *\n * Thanks to the Shhh computer art collective for the help in tuning the\n * output to have a better artistic effect.\n */\n\n#include \"server.h\"\n#include \"lolwut.h\"\n\n/* Render the canvas using the four gray levels of the standard color\n * terminal: they match very well to the grayscale display of the gameboy. */\nstatic sds renderCanvas(lwCanvas *canvas) {\n    sds text = sdsempty();\n    for (int y = 0; y < canvas->height; y++) {\n        for (int x = 0; x < canvas->width; x++) {\n            int color = lwGetPixel(canvas,x,y);\n            char *ce; /* Color escape sequence. */\n\n            /* Note that we set both the foreground and background color.\n             * This way we are able to get a more consistent result among\n             * different terminals implementations. */\n            switch(color) {\n            case 0: ce = \"0;30;40m\"; break;    /* Black */\n            case 1: ce = \"0;90;100m\"; break;   /* Gray 1 */\n            case 2: ce = \"0;37;47m\"; break;    /* Gray 2 */\n            case 3: ce = \"0;97;107m\"; break;   /* White */\n            default: ce = \"0;30;40m\"; break;   /* Just for safety. */\n            }\n            text = sdscatprintf(text,\"\\033[%s \\033[0m\",ce);\n        }\n        if (y != canvas->height-1) text = sdscatlen(text,\"\\n\",1);\n    }\n    return text;\n}\n\n/* Draw a skyscraper on the canvas, according to the parameters in the\n * 'skyscraper' structure. Window colors are random and are always one\n * of the two grays. */\nstruct skyscraper {\n    int xoff;       /* X offset. */\n    int width;      /* Pixels width. */\n    int height;     /* Pixels height. */\n    int windows;    /* Draw windows if true. */\n    int color;      /* Color of the skyscraper. */\n};\n\nvoid generateSkyscraper(lwCanvas *canvas, struct skyscraper *si) {\n    int starty = canvas->height-1;\n    int endy = starty - si->height + 1;\n    for (int y = starty; y >= endy; y--) {\n        for (int x = si->xoff; x < si->xoff+si->width; x++) {\n            /* The roof is four pixels less wide. */\n            if (y == endy && (x <= si->xoff+1 || x >= si->xoff+si->width-2))\n                continue;\n            int color = si->color;\n            /* Alter the color if this is a place where we want to\n             * draw a window. We check that we are in the inner part of the\n             * skyscraper, so that windows are far from the borders. */\n            if (si->windows &&\n                x > si->xoff+1 &&\n                x < si->xoff+si->width-2 &&\n                y > endy+1 &&\n                y < starty-1)\n            {\n                /* Calculate the x,y position relative to the start of\n                 * the window area. */\n                int relx = x - (si->xoff+1);\n                int rely = y - (endy+1);\n\n                /* Note that we want the windows to be two pixels wide\n                 * but just one pixel tall, because terminal \"pixels\"\n                 * (characters) are not square. */\n                if (relx/2 % 2 && rely % 2) {\n                    do {\n                        color = 1 + rand() % 2;\n                    } while (color == si->color);\n                    /* Except we want adjacent pixels creating the same\n                     * window to be the same color. */\n                    if (relx % 2) color = lwGetPixel(canvas,x-1,y);\n                }\n            }\n            lwDrawPixel(canvas,x,y,color);\n        }\n    }\n}\n\n/* Generate a skyline inspired by the parallax backgrounds of 8 bit games. */\nvoid generateSkyline(lwCanvas *canvas) {\n    struct skyscraper si;\n\n    /* First draw the background skyscraper without windows, using the\n     * two different grays. We use two passes to make sure that the lighter\n     * ones are always in the background. */\n    for (int color = 2; color >= 1; color--) {\n        si.color = color;\n        for (int offset = -10; offset < canvas->width;) {\n            offset += rand() % 8;\n            si.xoff = offset;\n            si.width = 10 + rand()%9;\n            if (color == 2)\n                si.height = canvas->height/2 + rand()%canvas->height/2;\n            else\n                si.height = canvas->height/2 + rand()%canvas->height/3;\n            si.windows = 0;\n            generateSkyscraper(canvas, &si);\n            if (color == 2)\n                offset += si.width/2;\n            else\n                offset += si.width+1;\n        }\n    }\n\n    /* Now draw the foreground skyscraper with the windows. */\n    si.color = 0;\n    for (int offset = -10; offset < canvas->width;) {\n        offset += rand() % 8;\n        si.xoff = offset;\n        si.width = 5 + rand()%14;\n        if (si.width % 4) si.width += (si.width % 3);\n        si.height = canvas->height/3 + rand()%canvas->height/2;\n        si.windows = 1;\n        generateSkyscraper(canvas, &si);\n        offset += si.width+5;\n    }\n}\n\n/* The LOLWUT 6 command:\n *\n * LOLWUT [columns] [rows]\n *\n * By default the command uses 80 columns, 40 squares per row\n * per column.\n */\nvoid lolwut6Command(client *c) {\n    long cols = 80;\n    long rows = 20;\n\n    /* Parse the optional arguments if any. */\n    if (c->argc > 1 &&\n        getLongFromObjectOrReply(c,c->argv[1],&cols,NULL) != C_OK)\n        return;\n\n    if (c->argc > 2 &&\n        getLongFromObjectOrReply(c,c->argv[2],&rows,NULL) != C_OK)\n        return;\n\n    /* Limits. We want LOLWUT to be always reasonably fast and cheap to execute\n     * so we have maximum number of columns, rows, and output resulution. */\n    if (cols < 1) cols = 1;\n    if (cols > 1000) cols = 1000;\n    if (rows < 1) rows = 1;\n    if (rows > 1000) rows = 1000;\n\n    /* Generate the city skyline and reply. */\n    lwCanvas *canvas = lwCreateCanvas(cols,rows,3);\n    generateSkyline(canvas);\n    sds rendered = renderCanvas(canvas);\n    rendered = sdscat(rendered,\n        \"\\nDedicated to the 8 bit game developers of past and present.\\n\"\n        \"Original 8 bit image from Plaguemon by hikikomori. Redis ver. \");\n    rendered = sdscat(rendered,REDIS_VERSION);\n    rendered = sdscatlen(rendered,\"\\n\",1);\n    addReplyVerbatim(c,rendered,sdslen(rendered),\"txt\");\n    sdsfree(rendered);\n    lwFreeCanvas(canvas);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 22).\n */\n#ifndef HLOG\n# define HLOG 16\n#endif\n\n/*\n * Sacrifice very little compression quality in favour of compression speed.\n * This gives almost the same compression as the default code, and is\n * (very roughly) 15% faster. This is the preferred mode of operation.\n */\n#ifndef VERY_FAST\n# define VERY_FAST 1\n#endif\n\n/*\n * Sacrifice some more compression quality in favour of compression speed.\n * (roughly 1-2% worse compression for large blocks and\n * 9-10% for small, redundant, blocks and >>20% better speed in both cases)\n * In short: when in need for speed, enable this for binary data,\n * possibly disable this for text data.\n */\n#ifndef ULTRA_FAST\n# define ULTRA_FAST 0\n#endif\n\n/*\n * Unconditionally aligning does not cost very much, so do it if unsure\n */\n#ifndef STRICT_ALIGN\n# if !(defined(__i386) || defined (__amd64))\n#  define STRICT_ALIGN 1\n# else\n#  define STRICT_ALIGN 0\n# endif\n#endif\n\n/*\n * You may choose to pre-set the hash table (might be faster on some\n * modern cpus and large (>>64k) blocks, and also makes compression\n * deterministic/repeatable when the configuration otherwise is the same).\n */\n#ifndef INIT_HTAB\n# define INIT_HTAB 0\n#endif\n\n/*\n * Avoid assigning values to errno variable? for some embedding purposes\n * (linux kernel for example), this is necessary. NOTE: this breaks\n * the documentation in lzf.h. Avoiding errno has no speed impact.\n */\n#ifndef AVOID_ERRNO\n# define AVOID_ERRNO 0\n#endif\n\n/*\n * Whether to pass the LZF_STATE variable as argument, or allocate it\n * on the stack. For small-stack environments, define this to 1.\n * NOTE: this breaks the prototype in lzf.h.\n */\n#ifndef LZF_STATE_ARG\n# define LZF_STATE_ARG 0\n#endif\n\n/*\n * Whether to add extra checks for input validity in lzf_decompress\n * and return EINVAL if the input stream has been corrupted. This\n * only shields against overflowing the input buffer and will not\n * detect most corrupted streams.\n * This check is not normally noticeable on modern hardware\n * (<1% slowdown), but might slow down older cpus considerably.\n */\n#ifndef CHECK_INPUT\n# define CHECK_INPUT 1\n#endif\n\n/*\n * Whether to store pointers or offsets inside the hash table. On\n * 64 bit architetcures, pointers take up twice as much space,\n * and might also be slower. Default is to autodetect.\n */\n/*#define LZF_USER_OFFSETS autodetect */\n\n/*****************************************************************************/\n/* nothing should be changed below */\n\n#ifdef __cplusplus\n# include <cstring>\n# include <climits>\nusing namespace std;\n#else\n# include <string.h>\n# include <limits.h>\n#endif\n\n#ifndef LZF_USE_OFFSETS\n# if defined (WIN32)\n#  define LZF_USE_OFFSETS defined(_M_X64)\n# else\n#  if __cplusplus > 199711L\n#   include <cstdint>\n#  else\n#   include <stdint.h>\n#  endif\n#  define LZF_USE_OFFSETS (UINTPTR_MAX > 0xffffffffU)\n# endif\n#endif\n\ntypedef unsigned char u8;\n\n#if LZF_USE_OFFSETS\n# define LZF_HSLOT_BIAS ((const u8 *)in_data)\n  typedef unsigned int LZF_HSLOT;\n#else\n# define LZF_HSLOT_BIAS 0\n  typedef const u8 *LZF_HSLOT;\n#endif\n\ntypedef LZF_HSLOT LZF_STATE[1 << (HLOG)];\n\n#if !STRICT_ALIGN\n/* for unaligned accesses we need a 16 bit datatype. */\n# if USHRT_MAX == 65535\n    typedef unsigned short u16;\n# elif UINT_MAX == 65535\n    typedef unsigned int u16;\n# else\n#  undef STRICT_ALIGN\n#  define STRICT_ALIGN 1\n# endif\n#endif\n\n#if ULTRA_FAST\n# undef VERY_FAST\n#endif\n\n#endif\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lzf_c.c",
    "content": "/*\n * Copyright (c) 2000-2010 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 * the data format is not dependent on the hash function.\n * the hash function might seem strange, just believe me,\n * it works ;)\n */\n#ifndef FRST\n# define FRST(p) (((p[0]) << 8) | p[1])\n# define NEXT(v,p) (((v) << 8) | p[2])\n# if ULTRA_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h  ) & (HSIZE - 1))\n# elif VERY_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# else\n#  define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# endif\n#endif\n/*\n * IDX works because it is very similar to a multiplicative hash, e.g.\n * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1))\n * the latter is also quite fast on newer CPUs, and compresses similarly.\n *\n * the next one is also quite good, albeit slow ;)\n * (int)(cos(h & 0xffffff) * 1e6)\n */\n\n#if 0\n/* original lzv-like hash function, much worse and thus slower */\n# define FRST(p) (p[0] << 5) ^ p[1]\n# define NEXT(v,p) ((v) << 5) ^ p[2]\n# define IDX(h) ((h) & (HSIZE - 1))\n#endif\n\n#define        MAX_LIT        (1 <<  5)\n#define        MAX_OFF        (1 << 13)\n#define        MAX_REF        ((1 << 8) + (1 << 3))\n\n#if __GNUC__ >= 3\n# define expect(expr,value)         __builtin_expect ((expr),(value))\n# define inline                     inline\n#else\n# define expect(expr,value)         (expr)\n# define inline                     static\n#endif\n\n#define expect_false(expr) expect ((expr) != 0, 0)\n#define expect_true(expr)  expect ((expr) != 0, 1)\n\n/*\n * compressed format\n *\n * 000LLLLL <L+1>    ; literal, L+1=1..33 octets\n * LLLooooo oooooooo ; backref L+1=1..7 octets, o+1=1..4096 offset\n * 111ooooo LLLLLLLL oooooooo ; backref L+8 octets, o+1=1..4096 offset\n *\n */\n\nunsigned int\nlzf_compress (const void *const in_data, unsigned int in_len,\n\t      void *out_data, unsigned int out_len\n#if LZF_STATE_ARG\n              , LZF_STATE htab\n#endif\n              )\n{\n#if !LZF_STATE_ARG\n  LZF_STATE htab;\n#endif\n  const u8 *ip = (const u8 *)in_data;\n        u8 *op = (u8 *)out_data;\n  const u8 *in_end  = ip + in_len;\n        u8 *out_end = op + out_len;\n  const u8 *ref;\n\n  /* off requires a type wide enough to hold a general pointer difference.\n   * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only\n   * works for differences within a single object). We also assume that no\n   * no bit pattern traps. Since the only platform that is both non-POSIX\n   * and fails to support both assumptions is windows 64 bit, we make a\n   * special workaround for it.\n   */\n#if defined (WIN32) && defined (_M_X64)\n  unsigned _int64 off; /* workaround for missing POSIX compliance */\n#else\n  unsigned long off;\n#endif\n  unsigned int hval;\n  int lit;\n\n  if (!in_len || !out_len)\n    return 0;\n\n#if INIT_HTAB\n  memset (htab, 0, sizeof (htab));\n#endif\n\n  lit = 0; op++; /* start run */\n\n  hval = FRST (ip);\n  while (ip < in_end - 2)\n    {\n      LZF_HSLOT *hslot;\n\n      hval = NEXT (hval, ip);\n      hslot = htab + IDX (hval);\n      ref = *hslot + LZF_HSLOT_BIAS; *hslot = ip - LZF_HSLOT_BIAS;\n\n      if (1\n#if INIT_HTAB\n          && ref < ip /* the next test will actually take care of this, but this is faster */\n#endif\n          && (off = ip - ref - 1) < MAX_OFF\n          && ref > (u8 *)in_data\n          && ref[2] == ip[2]\n#if STRICT_ALIGN\n          && ((ref[1] << 8) | ref[0]) == ((ip[1] << 8) | ip[0])\n#else\n          && *(u16 *)ref == *(u16 *)ip\n#endif\n        )\n        {\n          /* match found at *ref++ */\n          unsigned int len = 2;\n          unsigned int maxlen = in_end - ip - len;\n          maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;\n\n          if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */\n            if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */\n              return 0;\n\n          op [- lit - 1] = lit - 1; /* stop run */\n          op -= !lit; /* undo run if length is zero */\n\n          for (;;)\n            {\n              if (expect_true (maxlen > 16))\n                {\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                }\n\n              do\n                len++;\n              while (len < maxlen && ref[len] == ip[len]);\n\n              break;\n            }\n\n          len -= 2; /* len is now #octets - 1 */\n          ip++;\n\n          if (len < 7)\n            {\n              *op++ = (off >> 8) + (len << 5);\n            }\n          else\n            {\n              *op++ = (off >> 8) + (  7 << 5);\n              *op++ = len - 7;\n            }\n\n          *op++ = off;\n\n          lit = 0; op++; /* start run */\n\n          ip += len + 1;\n\n          if (expect_false (ip >= in_end - 2))\n            break;\n\n#if ULTRA_FAST || VERY_FAST\n          --ip;\n# if VERY_FAST && !ULTRA_FAST\n          --ip;\n# endif\n          hval = FRST (ip);\n\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n          ip++;\n\n# if VERY_FAST && !ULTRA_FAST\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n          ip++;\n# endif\n#else\n          ip -= len + 1;\n\n          do\n            {\n              hval = NEXT (hval, ip);\n              htab[IDX (hval)] = ip - LZF_HSLOT_BIAS;\n              ip++;\n            }\n          while (len--);\n#endif\n        }\n      else\n        {\n          /* one more literal byte we must copy */\n          if (expect_false (op >= out_end))\n            return 0;\n\n          lit++; *op++ = *ip++;\n\n          if (expect_false (lit == MAX_LIT))\n            {\n              op [- lit - 1] = lit - 1; /* stop run */\n              lit = 0; op++; /* start run */\n            }\n        }\n    }\n\n  if (op + 3 > out_end) /* at most 3 bytes can be missing here */\n    return 0;\n\n  while (ip < in_end)\n    {\n      lit++; *op++ = *ip++;\n\n      if (expect_false (lit == MAX_LIT))\n        {\n          op [- lit - 1] = lit - 1; /* stop run */\n          lit = 0; op++; /* start run */\n        }\n    }\n\n  op [- lit - 1] = lit - 1; /* end run */\n  op -= !lit; /* undo run if length is zero */\n\n  return op - (u8 *)out_data;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/lzf_d.c",
    "content": "/*\n * Copyright (c) 2000-2010 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#if USE_REP_MOVSB /* small win on amd, big loss on intel */\n#if (__i386 || __amd64) && __GNUC__ >= 3\n# define lzf_movsb(dst, src, len)                \\\n   asm (\"rep movsb\"                              \\\n        : \"=D\" (dst), \"=S\" (src), \"=c\" (len)     \\\n        :  \"0\" (dst),  \"1\" (src),  \"2\" (len));\n#endif\n#endif\n\n#if defined(__GNUC__) && __GNUC__ >= 5\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wimplicit-fallthrough\"\n#endif\nunsigned int\nlzf_decompress (const void *const in_data,  unsigned int in_len,\n                void             *out_data, unsigned int out_len)\n{\n  u8 const *ip = (const u8 *)in_data;\n  u8       *op = (u8 *)out_data;\n  u8 const *const in_end  = ip + in_len;\n  u8       *const out_end = op + out_len;\n\n  do\n    {\n      unsigned int ctrl = *ip++;\n\n      if (ctrl < (1 << 5)) /* literal run */\n        {\n          ctrl++;\n\n          if (op + ctrl > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n#if CHECK_INPUT\n          if (ip + ctrl > in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n\n#ifdef lzf_movsb\n          lzf_movsb (op, ip, ctrl);\n#else\n          switch (ctrl)\n            {\n              case 32: *op++ = *ip++; case 31: *op++ = *ip++; case 30: *op++ = *ip++; case 29: *op++ = *ip++;\n              case 28: *op++ = *ip++; case 27: *op++ = *ip++; case 26: *op++ = *ip++; case 25: *op++ = *ip++;\n              case 24: *op++ = *ip++; case 23: *op++ = *ip++; case 22: *op++ = *ip++; case 21: *op++ = *ip++;\n              case 20: *op++ = *ip++; case 19: *op++ = *ip++; case 18: *op++ = *ip++; case 17: *op++ = *ip++;\n              case 16: *op++ = *ip++; case 15: *op++ = *ip++; case 14: *op++ = *ip++; case 13: *op++ = *ip++;\n              case 12: *op++ = *ip++; case 11: *op++ = *ip++; case 10: *op++ = *ip++; case  9: *op++ = *ip++;\n              case  8: *op++ = *ip++; case  7: *op++ = *ip++; case  6: *op++ = *ip++; case  5: *op++ = *ip++;\n              case  4: *op++ = *ip++; case  3: *op++ = *ip++; case  2: *op++ = *ip++; case  1: *op++ = *ip++;\n            }\n#endif\n        }\n      else /* back reference */\n        {\n          unsigned int len = ctrl >> 5;\n\n          u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;\n\n#if CHECK_INPUT\n          if (ip >= in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n          if (len == 7)\n            {\n              len += *ip++;\n#if CHECK_INPUT\n              if (ip >= in_end)\n                {\n                  SET_ERRNO (EINVAL);\n                  return 0;\n                }\n#endif\n            }\n\n          ref -= *ip++;\n\n          if (op + len + 2 > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n          if (ref < (u8 *)out_data)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n\n#ifdef lzf_movsb\n          len += 2;\n          lzf_movsb (op, ref, len);\n#else\n          switch (len)\n            {\n              default:\n                len += 2;\n\n                if (op >= ref + len)\n                  {\n                    /* disjunct areas */\n                    memcpy (op, ref, len);\n                    op += len;\n                  }\n                else\n                  {\n                    /* overlapping, use octte by octte copying */\n                    do\n                      *op++ = *ref++;\n                    while (--len);\n                  }\n\n                break;\n\n              case 9: *op++ = *ref++; /* fall-thru */\n              case 8: *op++ = *ref++; /* fall-thru */\n              case 7: *op++ = *ref++; /* fall-thru */\n              case 6: *op++ = *ref++; /* fall-thru */\n              case 5: *op++ = *ref++; /* fall-thru */\n              case 4: *op++ = *ref++; /* fall-thru */\n              case 3: *op++ = *ref++; /* fall-thru */\n              case 2: *op++ = *ref++; /* fall-thru */\n              case 1: *op++ = *ref++; /* fall-thru */\n              case 0: *op++ = *ref++; /* two octets more */\n                      *op++ = *ref++; /* fall-thru */\n            }\n#endif\n        }\n    }\n  while (ip < in_end);\n\n  return op - (u8 *)out_data;\n}\n#if defined(__GNUC__) && __GNUC__ >= 5\n#pragma GCC diagnostic pop\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <stdint.h>\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#if defined(__sun)\n#include <stropts.h>\n#endif\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. */\nint memtest_addressing(unsigned long *l, size_t bytes, int interactive) {\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 && interactive)\n            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            if (interactive) {\n                printf(\"\\n*** MEMORY ADDRESSING ERROR: %p contains %lu\\n\",\n                    (void*) p, *p);\n                exit(1);\n            }\n            return 1;\n        }\n        p++;\n        if ((j & 0xffff) == 0 && interactive)\n            memtest_progress_step(j+words,words*2,'A');\n    }\n    return 0;\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.\n *\n * In this test we can't call rand() since the system may be completely\n * unable to handle library calls, so we have to resort to our own\n * PRNG that only uses local state. We use an xorshift* PRNG. */\n#define xorshift64star_next() do { \\\n        rseed ^= rseed >> 12; \\\n        rseed ^= rseed << 25; \\\n        rseed ^= rseed >> 27; \\\n        rout = rseed * UINT64_C(2685821657736338717); \\\n} while(0)\n\nvoid memtest_fill_random(unsigned long *l, size_t bytes, int interactive) {\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    uint64_t rseed = UINT64_C(0xd13133de9afdb566); /* Just a random seed. */\n    uint64_t rout = 0;\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            xorshift64star_next();\n            *l1 = *l2 = (unsigned long) rout;\n            l1 += step;\n            l2 += step;\n            if ((w & 0xffff) == 0 && interactive)\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, int interactive)\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 && interactive)\n                memtest_progress_step(w+iwords*off,words,sym);\n        }\n    }\n}\n\nint memtest_compare(unsigned long *l, size_t bytes, int interactive) {\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            if (interactive) {\n                printf(\"\\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\\n\",\n                    (void*)l1, (void*)l2, *l1, *l2);\n                exit(1);\n            }\n            return 1;\n        }\n        l1 ++;\n        l2 ++;\n        if ((w & 0xffff) == 0 && interactive)\n            memtest_progress_step(w,words,'=');\n    }\n    return 0;\n}\n\nint memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times,\n                          int interactive)\n{\n    int j;\n    int errors = 0;\n\n    for (j = 0; j < times; j++) {\n        if (interactive) memtest_progress_start(\"Compare\",pass);\n        errors += memtest_compare(m,bytes,interactive);\n        if (interactive) memtest_progress_end();\n    }\n    return errors;\n}\n\n/* Test the specified memory. The number of bytes must be multiple of 4096.\n * If interactive is true the program exists with an error and prints\n * ASCII arts to show progresses. Instead when interactive is 0, it can\n * be used as an API call, and returns 1 if memory errors were found or\n * 0 if there were no errors detected. */\nint memtest_test(unsigned long *m, size_t bytes, int passes, int interactive) {\n    int pass = 0;\n    int errors = 0;\n\n    while (pass != passes) {\n        pass++;\n\n        if (interactive) memtest_progress_start(\"Addressing test\",pass);\n        errors += memtest_addressing(m,bytes,interactive);\n        if (interactive) memtest_progress_end();\n\n        if (interactive) memtest_progress_start(\"Random fill\",pass);\n        memtest_fill_random(m,bytes,interactive);\n        if (interactive) memtest_progress_end();\n        errors += memtest_compare_times(m,bytes,pass,4,interactive);\n\n        if (interactive) memtest_progress_start(\"Solid fill\",pass);\n        memtest_fill_value(m,bytes,0,(unsigned long)-1,'S',interactive);\n        if (interactive) memtest_progress_end();\n        errors += memtest_compare_times(m,bytes,pass,4,interactive);\n\n        if (interactive) memtest_progress_start(\"Checkerboard fill\",pass);\n        memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C',interactive);\n        if (interactive) memtest_progress_end();\n        errors += memtest_compare_times(m,bytes,pass,4,interactive);\n    }\n    return errors;\n}\n\n/* A version of memtest_test() that tests memory in small pieces\n * in order to restore the memory content at exit.\n *\n * One problem we have with this approach, is that the cache can avoid\n * real memory accesses, and we can't test big chunks of memory at the\n * same time, because we need to backup them on the stack (the allocator\n * may not be usable or we may be already in an out of memory condition).\n * So what we do is to try to trash the cache with useless memory accesses\n * between the fill and compare cycles. */\n#define MEMTEST_BACKUP_WORDS (1024*(1024/sizeof(long)))\n/* Random accesses of MEMTEST_DECACHE_SIZE are performed at the start and\n * end of the region between fill and compare cycles in order to trash\n * the cache. */\n#define MEMTEST_DECACHE_SIZE (1024*8)\nint memtest_preserving_test(unsigned long *m, size_t bytes, int passes) {\n    unsigned long backup[MEMTEST_BACKUP_WORDS];\n    unsigned long *p = m;\n    unsigned long *end = (unsigned long*) (((unsigned char*)m)+(bytes-MEMTEST_DECACHE_SIZE));\n    size_t left = bytes;\n    int errors = 0;\n\n    if (bytes & 4095) return 0; /* Can't test across 4k page boundaries. */\n    if (bytes < 4096*2) return 0; /* Can't test a single page. */\n\n    while(left) {\n        /* If we have to test a single final page, go back a single page\n         * so that we can test two pages, since the code can't test a single\n         * page but at least two. */\n        if (left == 4096) {\n            left += 4096;\n            p -= 4096/sizeof(unsigned long);\n        }\n\n        int pass = 0;\n        size_t len = (left > sizeof(backup)) ? sizeof(backup) : left;\n\n        /* Always test an even number of pages. */\n        if (len/4096 % 2) len -= 4096;\n\n        memcpy(backup,p,len); /* Backup. */\n        while(pass != passes) {\n            pass++;\n            errors += memtest_addressing(p,len,0);\n            memtest_fill_random(p,len,0);\n            if (bytes >= MEMTEST_DECACHE_SIZE) {\n                memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);\n                memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);\n            }\n            errors += memtest_compare_times(p,len,pass,4,0);\n            memtest_fill_value(p,len,0,(unsigned long)-1,'S',0);\n            if (bytes >= MEMTEST_DECACHE_SIZE) {\n                memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);\n                memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);\n            }\n            errors += memtest_compare_times(p,len,pass,4,0);\n            memtest_fill_value(p,len,ULONG_ONEZERO,ULONG_ZEROONE,'C',0);\n            if (bytes >= MEMTEST_DECACHE_SIZE) {\n                memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);\n                memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);\n            }\n            errors += memtest_compare_times(p,len,pass,4,0);\n        }\n        memcpy(p,backup,len); /* Restore. */\n        left -= len;\n        p += len/sizeof(unsigned long);\n    }\n    return errors;\n}\n\n/* Perform an interactive test allocating the specified number of megabytes. */\nvoid memtest_alloc_and_test(size_t megabytes, int passes) {\n    size_t bytes = megabytes*1024*1024;\n    unsigned long *m = malloc(bytes);\n\n    if (m == NULL) {\n        fprintf(stderr,\"Unable to allocate %zu megabytes: %s\",\n            megabytes, strerror(errno));\n        exit(1);\n    }\n    memtest_test(m,bytes,passes,1);\n    free(m);\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_alloc_and_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": "Chapter07/redis-6.0.3/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`\nif [ -n \"$SOURCE_DATE_EPOCH\" ]; then\n  BUILD_ID=$(date -u -d \"@$SOURCE_DATE_EPOCH\" +%s 2>/dev/null || date -u -r \"$SOURCE_DATE_EPOCH\" +%s 2>/dev/null || date -u +%s)\nfi\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": "Chapter07/redis-6.0.3/src/module.c",
    "content": "/*\n * Copyright (c) 2016, 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 \"server.h\"\n#include \"cluster.h\"\n#include \"rdb.h\"\n#include <dlfcn.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n\n/* --------------------------------------------------------------------------\n * Private data structures used by the modules system. Those are data\n * structures that are never exposed to Redis Modules, if not as void\n * pointers that have an API the module can call with them)\n * -------------------------------------------------------------------------- */\n\ntypedef struct RedisModuleInfoCtx {\n    struct RedisModule *module;\n    const char *requested_section;\n    sds info;           /* info string we collected so far */\n    int sections;       /* number of sections we collected so far */\n    int in_section;     /* indication if we're in an active section or not */\n    int in_dict_field;  /* indication that we're curreintly appending to a dict */\n} RedisModuleInfoCtx;\n\ntypedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);\n\n/* This structure represents a module inside the system. */\nstruct RedisModule {\n    void *handle;   /* Module dlopen() handle. */\n    char *name;     /* Module name. */\n    int ver;        /* Module version. We use just progressive integers. */\n    int apiver;     /* Module API version as requested during initialization.*/\n    list *types;    /* Module data types. */\n    list *usedby;   /* List of modules using APIs from this one. */\n    list *using;    /* List of modules we use some APIs of. */\n    list *filters;  /* List of filters the module has registered. */\n    int in_call;    /* RM_Call() nesting level */\n    int in_hook;    /* Hooks callback nesting level for this module (0 or 1). */\n    int options;    /* Module options and capabilities. */\n    int blocked_clients;         /* Count of RedisModuleBlockedClient in this module. */\n    RedisModuleInfoFunc info_cb; /* Callback for module to add INFO fields. */\n};\ntypedef struct RedisModule RedisModule;\n\n/* This represents a shared API. Shared APIs will be used to populate\n * the server.sharedapi dictionary, mapping names of APIs exported by\n * modules for other modules to use, to their structure specifying the\n * function pointer that can be called. */\nstruct RedisModuleSharedAPI {\n    void *func;\n    RedisModule *module;\n};\ntypedef struct RedisModuleSharedAPI RedisModuleSharedAPI;\n\nstatic dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/\n\n/* Entries in the context->amqueue array, representing objects to free\n * when the callback returns. */\nstruct AutoMemEntry {\n    void *ptr;\n    int type;\n};\n\n/* AutMemEntry type field values. */\n#define REDISMODULE_AM_KEY 0\n#define REDISMODULE_AM_STRING 1\n#define REDISMODULE_AM_REPLY 2\n#define REDISMODULE_AM_FREED 3 /* Explicitly freed by user already. */\n#define REDISMODULE_AM_DICT 4\n#define REDISMODULE_AM_INFO 5\n\n/* The pool allocator block. Redis Modules can allocate memory via this special\n * allocator that will automatically release it all once the callback returns.\n * This means that it can only be used for ephemeral allocations. However\n * there are two advantages for modules to use this API:\n *\n * 1) The memory is automatically released when the callback returns.\n * 2) This allocator is faster for many small allocations since whole blocks\n *    are allocated, and small pieces returned to the caller just advancing\n *    the index of the allocation.\n *\n * Allocations are always rounded to the size of the void pointer in order\n * to always return aligned memory chunks. */\n\n#define REDISMODULE_POOL_ALLOC_MIN_SIZE (1024*8)\n#define REDISMODULE_POOL_ALLOC_ALIGN (sizeof(void*))\n\ntypedef struct RedisModulePoolAllocBlock {\n    uint32_t size;\n    uint32_t used;\n    struct RedisModulePoolAllocBlock *next;\n    char memory[];\n} RedisModulePoolAllocBlock;\n\n/* This structure represents the context in which Redis modules operate.\n * Most APIs module can access, get a pointer to the context, so that the API\n * implementation can hold state across calls, or remember what to free after\n * the call and so forth.\n *\n * Note that not all the context structure is always filled with actual values\n * but only the fields needed in a given context. */\n\nstruct RedisModuleBlockedClient;\n\nstruct RedisModuleCtx {\n    void *getapifuncptr;            /* NOTE: Must be the first field. */\n    struct RedisModule *module;     /* Module reference. */\n    client *client;                 /* Client calling a command. */\n    struct RedisModuleBlockedClient *blocked_client; /* Blocked client for\n                                                        thread safe context. */\n    struct AutoMemEntry *amqueue;   /* Auto memory queue of objects to free. */\n    int amqueue_len;                /* Number of slots in amqueue. */\n    int amqueue_used;               /* Number of used slots in amqueue. */\n    int flags;                      /* REDISMODULE_CTX_... flags. */\n    void **postponed_arrays;        /* To set with RM_ReplySetArrayLength(). */\n    int postponed_arrays_count;     /* Number of entries in postponed_arrays. */\n    void *blocked_privdata;         /* Privdata set when unblocking a client. */\n    RedisModuleString *blocked_ready_key; /* Key ready when the reply callback\n                                             gets called for clients blocked\n                                             on keys. */\n\n    /* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST flag set. */\n    int *keys_pos;\n    int keys_count;\n\n    struct RedisModulePoolAllocBlock *pa_head;\n    redisOpArray saved_oparray;    /* When propagating commands in a callback\n                                      we reallocate the \"also propagate\" op\n                                      array. Here we save the old one to\n                                      restore it later. */\n};\ntypedef struct RedisModuleCtx RedisModuleCtx;\n\n#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, {0}}\n#define REDISMODULE_CTX_MULTI_EMITTED (1<<0)\n#define REDISMODULE_CTX_AUTO_MEMORY (1<<1)\n#define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<2)\n#define REDISMODULE_CTX_BLOCKED_REPLY (1<<3)\n#define REDISMODULE_CTX_BLOCKED_TIMEOUT (1<<4)\n#define REDISMODULE_CTX_THREAD_SAFE (1<<5)\n#define REDISMODULE_CTX_BLOCKED_DISCONNECTED (1<<6)\n#define REDISMODULE_CTX_MODULE_COMMAND_CALL (1<<7)\n\n/* This represents a Redis key opened with RM_OpenKey(). */\nstruct RedisModuleKey {\n    RedisModuleCtx *ctx;\n    redisDb *db;\n    robj *key;      /* Key name object. */\n    robj *value;    /* Value object, or NULL if the key was not found. */\n    void *iter;     /* Iterator. */\n    int mode;       /* Opening mode. */\n\n    /* Zset iterator. */\n    uint32_t ztype;         /* REDISMODULE_ZSET_RANGE_* */\n    zrangespec zrs;         /* Score range. */\n    zlexrangespec zlrs;     /* Lex range. */\n    uint32_t zstart;        /* Start pos for positional ranges. */\n    uint32_t zend;          /* End pos for positional ranges. */\n    void *zcurrent;         /* Zset iterator current node. */\n    int zer;                /* Zset iterator end reached flag\n                               (true if end was reached). */\n};\ntypedef struct RedisModuleKey RedisModuleKey;\n\n/* RedisModuleKey 'ztype' values. */\n#define REDISMODULE_ZSET_RANGE_NONE 0       /* This must always be 0. */\n#define REDISMODULE_ZSET_RANGE_LEX 1\n#define REDISMODULE_ZSET_RANGE_SCORE 2\n#define REDISMODULE_ZSET_RANGE_POS 3\n\n/* Function pointer type of a function representing a command inside\n * a Redis module. */\nstruct RedisModuleBlockedClient;\ntypedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, void **argv, int argc);\ntypedef void (*RedisModuleDisconnectFunc) (RedisModuleCtx *ctx, struct RedisModuleBlockedClient *bc);\n\n/* This struct holds the information about a command registered by a module.*/\nstruct RedisModuleCommandProxy {\n    struct RedisModule *module;\n    RedisModuleCmdFunc func;\n    struct redisCommand *rediscmd;\n};\ntypedef struct RedisModuleCommandProxy RedisModuleCommandProxy;\n\n#define REDISMODULE_REPLYFLAG_NONE 0\n#define REDISMODULE_REPLYFLAG_TOPARSE (1<<0) /* Protocol must be parsed. */\n#define REDISMODULE_REPLYFLAG_NESTED (1<<1)  /* Nested reply object. No proto\n                                                or struct free. */\n\n/* Reply of RM_Call() function. The function is filled in a lazy\n * way depending on the function called on the reply structure. By default\n * only the type, proto and protolen are filled. */\ntypedef struct RedisModuleCallReply {\n    RedisModuleCtx *ctx;\n    int type;       /* REDISMODULE_REPLY_... */\n    int flags;      /* REDISMODULE_REPLYFLAG_...  */\n    size_t len;     /* Len of strings or num of elements of arrays. */\n    char *proto;    /* Raw reply protocol. An SDS string at top-level object. */\n    size_t protolen;/* Length of protocol. */\n    union {\n        const char *str; /* String pointer for string and error replies. This\n                            does not need to be freed, always points inside\n                            a reply->proto buffer of the reply object or, in\n                            case of array elements, of parent reply objects. */\n        long long ll;    /* Reply value for integer reply. */\n        struct RedisModuleCallReply *array; /* Array of sub-reply elements. */\n    } val;\n} RedisModuleCallReply;\n\n/* Structure representing a blocked client. We get a pointer to such\n * an object when blocking from modules. */\ntypedef struct RedisModuleBlockedClient {\n    client *client;  /* Pointer to the blocked client. or NULL if the client\n                        was destroyed during the life of this object. */\n    RedisModule *module;    /* Module blocking the client. */\n    RedisModuleCmdFunc reply_callback; /* Reply callback on normal completion.*/\n    RedisModuleCmdFunc timeout_callback; /* Reply callback on timeout. */\n    RedisModuleDisconnectFunc disconnect_callback; /* Called on disconnection.*/\n    void (*free_privdata)(RedisModuleCtx*,void*);/* privdata cleanup callback.*/\n    void *privdata;     /* Module private data that may be used by the reply\n                           or timeout callback. It is set via the\n                           RedisModule_UnblockClient() API. */\n    client *reply_client;           /* Fake client used to accumulate replies\n                                       in thread safe contexts. */\n    int dbid;           /* Database number selected by the original client. */\n    int blocked_on_keys;    /* If blocked via RM_BlockClientOnKeys(). */\n    int unblocked;          /* Already on the moduleUnblocked list. */\n} RedisModuleBlockedClient;\n\nstatic pthread_mutex_t moduleUnblockedClientsMutex = PTHREAD_MUTEX_INITIALIZER;\nstatic list *moduleUnblockedClients;\n\n/* We need a mutex that is unlocked / relocked in beforeSleep() in order to\n * allow thread safe contexts to execute commands at a safe moment. */\nstatic pthread_mutex_t moduleGIL = PTHREAD_MUTEX_INITIALIZER;\n\n\n/* Function pointer type for keyspace event notification subscriptions from modules. */\ntypedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);\n\n/* Keyspace notification subscriber information.\n * See RM_SubscribeToKeyspaceEvents() for more information. */\ntypedef struct RedisModuleKeyspaceSubscriber {\n    /* The module subscribed to the event */\n    RedisModule *module;\n    /* Notification callback in the module*/\n    RedisModuleNotificationFunc notify_callback;\n    /* A bit mask of the events the module is interested in */\n    int event_mask;\n    /* Active flag set on entry, to avoid reentrant subscribers\n     * calling themselves */\n    int active;\n} RedisModuleKeyspaceSubscriber;\n\n/* The module keyspace notification subscribers list */\nstatic list *moduleKeyspaceSubscribers;\n\n/* Static client recycled for when we need to provide a context with a client\n * in a situation where there is no client to provide. This avoidsallocating\n * a new client per round. For instance this is used in the keyspace\n * notifications, timers and cluster messages callbacks. */\nstatic client *moduleFreeContextReusedClient;\n\n/* Data structures related to the exported dictionary data structure. */\ntypedef struct RedisModuleDict {\n    rax *rax;                       /* The radix tree. */\n} RedisModuleDict;\n\ntypedef struct RedisModuleDictIter {\n    RedisModuleDict *dict;\n    raxIterator ri;\n} RedisModuleDictIter;\n\ntypedef struct RedisModuleCommandFilterCtx {\n    RedisModuleString **argv;\n    int argc;\n} RedisModuleCommandFilterCtx;\n\ntypedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);\n\ntypedef struct RedisModuleCommandFilter {\n    /* The module that registered the filter */\n    RedisModule *module;\n    /* Filter callback function */\n    RedisModuleCommandFilterFunc callback;\n    /* REDISMODULE_CMDFILTER_* flags */\n    int flags;\n} RedisModuleCommandFilter;\n\n/* Registered filters */\nstatic list *moduleCommandFilters;\n\ntypedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);\n\nstatic struct RedisModuleForkInfo {\n    RedisModuleForkDoneHandler done_handler;\n    void* done_handler_user_data;\n} moduleForkInfo = {0};\n\ntypedef struct RedisModuleServerInfoData {\n    rax *rax;                       /* parsed info data. */\n} RedisModuleServerInfoData;\n\n/* Flags for moduleCreateArgvFromUserFormat(). */\n#define REDISMODULE_ARGV_REPLICATE (1<<0)\n#define REDISMODULE_ARGV_NO_AOF (1<<1)\n#define REDISMODULE_ARGV_NO_REPLICAS (1<<2)\n\n/* Determine whether Redis should signalModifiedKey implicitly.\n * In case 'ctx' has no 'module' member (and therefore no module->options),\n * we assume default behavior, that is, Redis signals.\n * (see RM_GetThreadSafeContext) */\n#define SHOULD_SIGNAL_MODIFIED_KEYS(ctx) \\\n    ctx->module? !(ctx->module->options & REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED) : 1\n\n/* Server events hooks data structures and defines: this modules API\n * allow modules to subscribe to certain events in Redis, such as\n * the start and end of an RDB or AOF save, the change of role in replication,\n * and similar other events. */\n\ntypedef struct RedisModuleEventListener {\n    RedisModule *module;\n    RedisModuleEvent event;\n    RedisModuleEventCallback callback;\n} RedisModuleEventListener;\n\nlist *RedisModule_EventListeners; /* Global list of all the active events. */\nunsigned long long ModulesInHooks = 0; /* Total number of modules in hooks\n                                          callbacks right now. */\n\n/* Data structures related to the redis module users */\n\n/* This callback type is called by moduleNotifyUserChanged() every time\n * a user authenticated via the module API is associated with a different\n * user or gets disconnected. */\ntypedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);\n\n/* This is the object returned by RM_CreateModuleUser(). The module API is\n * able to create users, set ACLs to such users, and later authenticate\n * clients using such newly created users. */\ntypedef struct RedisModuleUser {\n    user *user; /* Reference to the real redis user */\n} RedisModuleUser;\n\n\n/* --------------------------------------------------------------------------\n * Prototypes\n * -------------------------------------------------------------------------- */\n\nvoid RM_FreeCallReply(RedisModuleCallReply *reply);\nvoid RM_CloseKey(RedisModuleKey *key);\nvoid autoMemoryCollect(RedisModuleCtx *ctx);\nrobj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap);\nvoid moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx);\nvoid RM_ZsetRangeStop(RedisModuleKey *kp);\nstatic void zsetKeyReset(RedisModuleKey *key);\nvoid RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d);\nvoid RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);\n\n/* --------------------------------------------------------------------------\n * Heap allocation raw functions\n * -------------------------------------------------------------------------- */\n\n/* Use like malloc(). Memory allocated with this function is reported in\n * Redis INFO memory, used for keys eviction according to maxmemory settings\n * and in general is taken into account as memory allocated by Redis.\n * You should avoid using malloc(). */\nvoid *RM_Alloc(size_t bytes) {\n    return zmalloc(bytes);\n}\n\n/* Use like calloc(). Memory allocated with this function is reported in\n * Redis INFO memory, used for keys eviction according to maxmemory settings\n * and in general is taken into account as memory allocated by Redis.\n * You should avoid using calloc() directly. */\nvoid *RM_Calloc(size_t nmemb, size_t size) {\n    return zcalloc(nmemb*size);\n}\n\n/* Use like realloc() for memory obtained with RedisModule_Alloc(). */\nvoid* RM_Realloc(void *ptr, size_t bytes) {\n    return zrealloc(ptr,bytes);\n}\n\n/* Use like free() for memory obtained by RedisModule_Alloc() and\n * RedisModule_Realloc(). However you should never try to free with\n * RedisModule_Free() memory allocated with malloc() inside your module. */\nvoid RM_Free(void *ptr) {\n    zfree(ptr);\n}\n\n/* Like strdup() but returns memory allocated with RedisModule_Alloc(). */\nchar *RM_Strdup(const char *str) {\n    return zstrdup(str);\n}\n\n/* --------------------------------------------------------------------------\n * Pool allocator\n * -------------------------------------------------------------------------- */\n\n/* Release the chain of blocks used for pool allocations. */\nvoid poolAllocRelease(RedisModuleCtx *ctx) {\n    RedisModulePoolAllocBlock *head = ctx->pa_head, *next;\n\n    while(head != NULL) {\n        next = head->next;\n        zfree(head);\n        head = next;\n    }\n    ctx->pa_head = NULL;\n}\n\n/* Return heap allocated memory that will be freed automatically when the\n * module callback function returns. Mostly suitable for small allocations\n * that are short living and must be released when the callback returns\n * anyway. The returned memory is aligned to the architecture word size\n * if at least word size bytes are requested, otherwise it is just\n * aligned to the next power of two, so for example a 3 bytes request is\n * 4 bytes aligned while a 2 bytes request is 2 bytes aligned.\n *\n * There is no realloc style function since when this is needed to use the\n * pool allocator is not a good idea.\n *\n * The function returns NULL if `bytes` is 0. */\nvoid *RM_PoolAlloc(RedisModuleCtx *ctx, size_t bytes) {\n    if (bytes == 0) return NULL;\n    RedisModulePoolAllocBlock *b = ctx->pa_head;\n    size_t left = b ? b->size - b->used : 0;\n\n    /* Fix alignment. */\n    if (left >= bytes) {\n        size_t alignment = REDISMODULE_POOL_ALLOC_ALIGN;\n        while (bytes < alignment && alignment/2 >= bytes) alignment /= 2;\n        if (b->used % alignment)\n            b->used += alignment - (b->used % alignment);\n        left = (b->used > b->size) ? 0 : b->size - b->used;\n    }\n\n    /* Create a new block if needed. */\n    if (left < bytes) {\n        size_t blocksize = REDISMODULE_POOL_ALLOC_MIN_SIZE;\n        if (blocksize < bytes) blocksize = bytes;\n        b = zmalloc(sizeof(*b) + blocksize);\n        b->size = blocksize;\n        b->used = 0;\n        b->next = ctx->pa_head;\n        ctx->pa_head = b;\n    }\n\n    char *retval = b->memory + b->used;\n    b->used += bytes;\n    return retval;\n}\n\n/* --------------------------------------------------------------------------\n * Helpers for modules API implementation\n * -------------------------------------------------------------------------- */\n\n/* Create an empty key of the specified type. 'kp' must point to a key object\n * opened for writing where the .value member is set to NULL because the\n * key was found to be non existing.\n *\n * On success REDISMODULE_OK is returned and the key is populated with\n * the value of the specified type. The function fails and returns\n * REDISMODULE_ERR if:\n *\n * 1) The key is not open for writing.\n * 2) The key is not empty.\n * 3) The specified type is unknown.\n */\nint moduleCreateEmptyKey(RedisModuleKey *key, int type) {\n    robj *obj;\n\n    /* The key must be open for writing and non existing to proceed. */\n    if (!(key->mode & REDISMODULE_WRITE) || key->value)\n        return REDISMODULE_ERR;\n\n    switch(type) {\n    case REDISMODULE_KEYTYPE_LIST:\n        obj = createQuicklistObject();\n        quicklistSetOptions(obj->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n        break;\n    case REDISMODULE_KEYTYPE_ZSET:\n        obj = createZsetZiplistObject();\n        break;\n    case REDISMODULE_KEYTYPE_HASH:\n        obj = createHashObject();\n        break;\n    default: return REDISMODULE_ERR;\n    }\n    dbAdd(key->db,key->key,obj);\n    key->value = obj;\n    return REDISMODULE_OK;\n}\n\n/* This function is called in low-level API implementation functions in order\n * to check if the value associated with the key remained empty after an\n * operation that removed elements from an aggregate data type.\n *\n * If this happens, the key is deleted from the DB and the key object state\n * is set to the right one in order to be targeted again by write operations\n * possibly recreating the key if needed.\n *\n * The function returns 1 if the key value object is found empty and is\n * deleted, otherwise 0 is returned. */\nint moduleDelKeyIfEmpty(RedisModuleKey *key) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL) return 0;\n    int isempty;\n    robj *o = key->value;\n\n    switch(o->type) {\n    case OBJ_LIST: isempty = listTypeLength(o) == 0; break;\n    case OBJ_SET: isempty = setTypeSize(o) == 0; break;\n    case OBJ_ZSET: isempty = zsetLength(o) == 0; break;\n    case OBJ_HASH: isempty = hashTypeLength(o) == 0; break;\n    case OBJ_STREAM: isempty = streamLength(o) == 0; break;\n    default: isempty = 0;\n    }\n\n    if (isempty) {\n        dbDelete(key->db,key->key);\n        key->value = NULL;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Service API exported to modules\n *\n * Note that all the exported APIs are called RM_<funcname> in the core\n * and RedisModule_<funcname> in the module side (defined as function\n * pointers in redismodule.h). In this way the dynamic linker does not\n * mess with our global function pointers, overriding it with the symbols\n * defined in the main executable having the same names.\n * -------------------------------------------------------------------------- */\n\n/* Lookup the requested module API and store the function pointer into the\n * target pointer. The function returns REDISMODULE_ERR if there is no such\n * named API, otherwise REDISMODULE_OK.\n *\n * This function is not meant to be used by modules developer, it is only\n * used implicitly by including redismodule.h. */\nint RM_GetApi(const char *funcname, void **targetPtrPtr) {\n    dictEntry *he = dictFind(server.moduleapi, funcname);\n    if (!he) return REDISMODULE_ERR;\n    *targetPtrPtr = dictGetVal(he);\n    return REDISMODULE_OK;\n}\n\n/* Helper function for when a command callback is called, in order to handle\n * details needed to correctly replicate commands. */\nvoid moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) {\n    client *c = ctx->client;\n\n    /* We don't need to do anything here if the context was never used\n     * in order to propagate commands. */\n    if (!(ctx->flags & REDISMODULE_CTX_MULTI_EMITTED)) return;\n\n    if (c->flags & CLIENT_LUA) return;\n\n    /* Handle the replication of the final EXEC, since whatever a command\n     * emits is always wrapped around MULTI/EXEC. */\n    alsoPropagate(server.execCommand,c->db->id,&shared.exec,1,\n        PROPAGATE_AOF|PROPAGATE_REPL);\n\n    /* If this is not a module command context (but is instead a simple\n     * callback context), we have to handle directly the \"also propagate\"\n     * array and emit it. In a module command call this will be handled\n     * directly by call(). */\n    if (!(ctx->flags & REDISMODULE_CTX_MODULE_COMMAND_CALL) &&\n        server.also_propagate.numops)\n    {\n        for (int j = 0; j < server.also_propagate.numops; j++) {\n            redisOp *rop = &server.also_propagate.ops[j];\n            int target = rop->target;\n            if (target)\n                propagate(rop->cmd,rop->dbid,rop->argv,rop->argc,target);\n        }\n        redisOpArrayFree(&server.also_propagate);\n        /* Restore the previous oparray in case of nexted use of the API. */\n        server.also_propagate = ctx->saved_oparray;\n    }\n}\n\n/* Free the context after the user function was called. */\nvoid moduleFreeContext(RedisModuleCtx *ctx) {\n    moduleHandlePropagationAfterCommandCallback(ctx);\n    autoMemoryCollect(ctx);\n    poolAllocRelease(ctx);\n    if (ctx->postponed_arrays) {\n        zfree(ctx->postponed_arrays);\n        ctx->postponed_arrays_count = 0;\n        serverLog(LL_WARNING,\n            \"API misuse detected in module %s: \"\n            \"RedisModule_ReplyWithArray(REDISMODULE_POSTPONED_ARRAY_LEN) \"\n            \"not matched by the same number of RedisModule_SetReplyArrayLen() \"\n            \"calls.\",\n            ctx->module->name);\n    }\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) freeClient(ctx->client);\n}\n\n/* This Redis command binds the normal Redis command invocation with commands\n * exported by modules. */\nvoid RedisModuleCommandDispatcher(client *c) {\n    RedisModuleCommandProxy *cp = (void*)(unsigned long)c->cmd->getkeys_proc;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n\n    ctx.flags |= REDISMODULE_CTX_MODULE_COMMAND_CALL;\n    ctx.module = cp->module;\n    ctx.client = c;\n    cp->func(&ctx,(void**)c->argv,c->argc);\n    moduleFreeContext(&ctx);\n\n    /* In some cases processMultibulkBuffer uses sdsMakeRoomFor to\n     * expand the query buffer, and in order to avoid a big object copy\n     * the query buffer SDS may be used directly as the SDS string backing\n     * the client argument vectors: sometimes this will result in the SDS\n     * string having unused space at the end. Later if a module takes ownership\n     * of the RedisString, such space will be wasted forever. Inside the\n     * Redis core this is not a problem because tryObjectEncoding() is called\n     * before storing strings in the key space. Here we need to do it\n     * for the module. */\n    for (int i = 0; i < c->argc; i++) {\n        /* Only do the work if the module took ownership of the object:\n         * in that case the refcount is no longer 1. */\n        if (c->argv[i]->refcount > 1)\n            trimStringObjectIfNeeded(c->argv[i]);\n    }\n}\n\n/* This function returns the list of keys, with the same interface as the\n * 'getkeys' function of the native commands, for module commands that exported\n * the \"getkeys-api\" flag during the registration. This is done when the\n * list of keys are not at fixed positions, so that first/last/step cannot\n * be used.\n *\n * In order to accomplish its work, the module command is called, flagging\n * the context in a way that the command can recognize this is a special\n * \"get keys\" call by calling RedisModule_IsKeysPositionRequest(ctx). */\nint *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {\n    RedisModuleCommandProxy *cp = (void*)(unsigned long)cmd->getkeys_proc;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n\n    ctx.module = cp->module;\n    ctx.client = NULL;\n    ctx.flags |= REDISMODULE_CTX_KEYS_POS_REQUEST;\n    cp->func(&ctx,(void**)argv,argc);\n    int *res = ctx.keys_pos;\n    if (numkeys) *numkeys = ctx.keys_count;\n    moduleFreeContext(&ctx);\n    return res;\n}\n\n/* Return non-zero if a module command, that was declared with the\n * flag \"getkeys-api\", is called in a special way to get the keys positions\n * and not to get executed. Otherwise zero is returned. */\nint RM_IsKeysPositionRequest(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) != 0;\n}\n\n/* When a module command is called in order to obtain the position of\n * keys, since it was flagged as \"getkeys-api\" during the registration,\n * the command implementation checks for this special call using the\n * RedisModule_IsKeysPositionRequest() API and uses this function in\n * order to report keys, like in the following example:\n *\n *     if (RedisModule_IsKeysPositionRequest(ctx)) {\n *         RedisModule_KeyAtPos(ctx,1);\n *         RedisModule_KeyAtPos(ctx,2);\n *     }\n *\n *  Note: in the example below the get keys API would not be needed since\n *  keys are at fixed positions. This interface is only used for commands\n *  with a more complex structure. */\nvoid RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {\n    if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST)) return;\n    if (pos <= 0) return;\n    ctx->keys_pos = zrealloc(ctx->keys_pos,sizeof(int)*(ctx->keys_count+1));\n    ctx->keys_pos[ctx->keys_count++] = pos;\n}\n\n/* Helper for RM_CreateCommand(). Turns a string representing command\n * flags into the command flags used by the Redis core.\n *\n * It returns the set of flags, or -1 if unknown flags are found. */\nint64_t commandFlagsFromString(char *s) {\n    int count, j;\n    int64_t flags = 0;\n    sds *tokens = sdssplitlen(s,strlen(s),\" \",1,&count);\n    for (j = 0; j < count; j++) {\n        char *t = tokens[j];\n        if (!strcasecmp(t,\"write\")) flags |= CMD_WRITE;\n        else if (!strcasecmp(t,\"readonly\")) flags |= CMD_READONLY;\n        else if (!strcasecmp(t,\"admin\")) flags |= CMD_ADMIN;\n        else if (!strcasecmp(t,\"deny-oom\")) flags |= CMD_DENYOOM;\n        else if (!strcasecmp(t,\"deny-script\")) flags |= CMD_NOSCRIPT;\n        else if (!strcasecmp(t,\"allow-loading\")) flags |= CMD_LOADING;\n        else if (!strcasecmp(t,\"pubsub\")) flags |= CMD_PUBSUB;\n        else if (!strcasecmp(t,\"random\")) flags |= CMD_RANDOM;\n        else if (!strcasecmp(t,\"allow-stale\")) flags |= CMD_STALE;\n        else if (!strcasecmp(t,\"no-monitor\")) flags |= CMD_SKIP_MONITOR;\n        else if (!strcasecmp(t,\"no-slowlog\")) flags |= CMD_SKIP_SLOWLOG;\n        else if (!strcasecmp(t,\"fast\")) flags |= CMD_FAST;\n        else if (!strcasecmp(t,\"no-auth\")) flags |= CMD_NO_AUTH;\n        else if (!strcasecmp(t,\"getkeys-api\")) flags |= CMD_MODULE_GETKEYS;\n        else if (!strcasecmp(t,\"no-cluster\")) flags |= CMD_MODULE_NO_CLUSTER;\n        else break;\n    }\n    sdsfreesplitres(tokens,count);\n    if (j != count) return -1; /* Some token not processed correctly. */\n    return flags;\n}\n\n/* Register a new command in the Redis server, that will be handled by\n * calling the function pointer 'func' using the RedisModule calling\n * convention. The function returns REDISMODULE_ERR if the specified command\n * name is already busy or a set of invalid flags were passed, otherwise\n * REDISMODULE_OK is returned and the new command is registered.\n *\n * This function must be called during the initialization of the module\n * inside the RedisModule_OnLoad() function. Calling this function outside\n * of the initialization function is not defined.\n *\n * The command function type is the following:\n *\n *      int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);\n *\n * And is supposed to always return REDISMODULE_OK.\n *\n * The set of flags 'strflags' specify the behavior of the command, and should\n * be passed as a C string composed of space separated words, like for\n * example \"write deny-oom\". The set of flags are:\n *\n * * **\"write\"**:     The command may modify the data set (it may also read\n *                    from it).\n * * **\"readonly\"**:  The command returns data from keys but never writes.\n * * **\"admin\"**:     The command is an administrative command (may change\n *                    replication or perform similar tasks).\n * * **\"deny-oom\"**:  The command may use additional memory and should be\n *                    denied during out of memory conditions.\n * * **\"deny-script\"**:   Don't allow this command in Lua scripts.\n * * **\"allow-loading\"**: Allow this command while the server is loading data.\n *                        Only commands not interacting with the data set\n *                        should be allowed to run in this mode. If not sure\n *                        don't use this flag.\n * * **\"pubsub\"**:    The command publishes things on Pub/Sub channels.\n * * **\"random\"**:    The command may have different outputs even starting\n *                    from the same input arguments and key values.\n * * **\"allow-stale\"**: The command is allowed to run on slaves that don't\n *                      serve stale data. Don't use if you don't know what\n *                      this means.\n * * **\"no-monitor\"**: Don't propagate the command on monitor. Use this if\n *                     the command has sensible data among the arguments.\n * * **\"no-slowlog\"**: Don't log this command in the slowlog. Use this if\n *                     the command has sensible data among the arguments.\n * * **\"fast\"**:      The command time complexity is not greater\n *                    than O(log(N)) where N is the size of the collection or\n *                    anything else representing the normal scalability\n *                    issue with the command.\n * * **\"getkeys-api\"**: The command implements the interface to return\n *                      the arguments that are keys. Used when start/stop/step\n *                      is not enough because of the command syntax.\n * * **\"no-cluster\"**: The command should not register in Redis Cluster\n *                     since is not designed to work with it because, for\n *                     example, is unable to report the position of the\n *                     keys, programmatically creates key names, or any\n *                     other reason.\n * * **\"no-auth\"**:    This command can be run by an un-authenticated client.\n *                     Normally this is used by a command that is used\n *                     to authenticate a client. \n */\nint RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {\n    int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0;\n    if (flags == -1) return REDISMODULE_ERR;\n    if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)\n        return REDISMODULE_ERR;\n\n    struct redisCommand *rediscmd;\n    RedisModuleCommandProxy *cp;\n    sds cmdname = sdsnew(name);\n\n    /* Check if the command name is busy. */\n    if (lookupCommand(cmdname) != NULL) {\n        sdsfree(cmdname);\n        return REDISMODULE_ERR;\n    }\n\n    /* Create a command \"proxy\", which is a structure that is referenced\n     * in the command table, so that the generic command that works as\n     * binding between modules and Redis, can know what function to call\n     * and what the module is.\n     *\n     * Note that we use the Redis command table 'getkeys_proc' in order to\n     * pass a reference to the command proxy structure. */\n    cp = zmalloc(sizeof(*cp));\n    cp->module = ctx->module;\n    cp->func = cmdfunc;\n    cp->rediscmd = zmalloc(sizeof(*rediscmd));\n    cp->rediscmd->name = cmdname;\n    cp->rediscmd->proc = RedisModuleCommandDispatcher;\n    cp->rediscmd->arity = -1;\n    cp->rediscmd->flags = flags | CMD_MODULE;\n    cp->rediscmd->getkeys_proc = (redisGetKeysProc*)(unsigned long)cp;\n    cp->rediscmd->firstkey = firstkey;\n    cp->rediscmd->lastkey = lastkey;\n    cp->rediscmd->keystep = keystep;\n    cp->rediscmd->microseconds = 0;\n    cp->rediscmd->calls = 0;\n    dictAdd(server.commands,sdsdup(cmdname),cp->rediscmd);\n    dictAdd(server.orig_commands,sdsdup(cmdname),cp->rediscmd);\n    cp->rediscmd->id = ACLGetCommandID(cmdname); /* ID used for ACL. */\n    return REDISMODULE_OK;\n}\n\n/* Called by RM_Init() to setup the `ctx->module` structure.\n *\n * This is an internal function, Redis modules developers don't need\n * to use it. */\nvoid RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {\n    RedisModule *module;\n\n    if (ctx->module != NULL) return;\n    module = zmalloc(sizeof(*module));\n    module->name = sdsnew((char*)name);\n    module->ver = ver;\n    module->apiver = apiver;\n    module->types = listCreate();\n    module->usedby = listCreate();\n    module->using = listCreate();\n    module->filters = listCreate();\n    module->in_call = 0;\n    module->in_hook = 0;\n    module->options = 0;\n    module->info_cb = 0;\n    ctx->module = module;\n}\n\n/* Return non-zero if the module name is busy.\n * Otherwise zero is returned. */\nint RM_IsModuleNameBusy(const char *name) {\n    sds modulename = sdsnew(name);\n    dictEntry *de = dictFind(modules,modulename);\n    sdsfree(modulename);\n    return de != NULL;\n}\n\n/* Return the current UNIX time in milliseconds. */\nlong long RM_Milliseconds(void) {\n    return mstime();\n}\n\n/* Set flags defining capabilities or behavior bit flags.\n *\n * REDISMODULE_OPTIONS_HANDLE_IO_ERRORS:\n * Generally, modules don't need to bother with this, as the process will just\n * terminate if a read error happens, however, setting this flag would allow\n * repl-diskless-load to work if enabled.\n * The module should use RedisModule_IsIOError after reads, before using the\n * data that was read, and in case of error, propagate it upwards, and also be\n * able to release the partially populated value and all it's allocations. */\nvoid RM_SetModuleOptions(RedisModuleCtx *ctx, int options) {\n    ctx->module->options = options;\n}\n\n/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH\n * and client side caching). */\nint RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) {\n    signalModifiedKey(ctx->client,ctx->client->db,keyname);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Automatic memory management for modules\n * -------------------------------------------------------------------------- */\n\n/* Enable automatic memory management. See API.md for more information.\n *\n * The function must be called as the first function of a command implementation\n * that wants to use automatic memory. */\nvoid RM_AutoMemory(RedisModuleCtx *ctx) {\n    ctx->flags |= REDISMODULE_CTX_AUTO_MEMORY;\n}\n\n/* Add a new object to release automatically when the callback returns. */\nvoid autoMemoryAdd(RedisModuleCtx *ctx, int type, void *ptr) {\n    if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return;\n    if (ctx->amqueue_used == ctx->amqueue_len) {\n        ctx->amqueue_len *= 2;\n        if (ctx->amqueue_len < 16) ctx->amqueue_len = 16;\n        ctx->amqueue = zrealloc(ctx->amqueue,sizeof(struct AutoMemEntry)*ctx->amqueue_len);\n    }\n    ctx->amqueue[ctx->amqueue_used].type = type;\n    ctx->amqueue[ctx->amqueue_used].ptr = ptr;\n    ctx->amqueue_used++;\n}\n\n/* Mark an object as freed in the auto release queue, so that users can still\n * free things manually if they want.\n *\n * The function returns 1 if the object was actually found in the auto memory\n * pool, otherwise 0 is returned. */\nint autoMemoryFreed(RedisModuleCtx *ctx, int type, void *ptr) {\n    if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return 0;\n\n    int count = (ctx->amqueue_used+1)/2;\n    for (int j = 0; j < count; j++) {\n        for (int side = 0; side < 2; side++) {\n            /* For side = 0 check right side of the array, for\n             * side = 1 check the left side instead (zig-zag scanning). */\n            int i = (side == 0) ? (ctx->amqueue_used - 1 - j) : j;\n            if (ctx->amqueue[i].type == type &&\n                ctx->amqueue[i].ptr == ptr)\n            {\n                ctx->amqueue[i].type = REDISMODULE_AM_FREED;\n\n                /* Switch the freed element and the last element, to avoid growing\n                 * the queue unnecessarily if we allocate/free in a loop */\n                if (i != ctx->amqueue_used-1) {\n                    ctx->amqueue[i] = ctx->amqueue[ctx->amqueue_used-1];\n                }\n\n                /* Reduce the size of the queue because we either moved the top\n                 * element elsewhere or freed it */\n                ctx->amqueue_used--;\n                return 1;\n            }\n        }\n    }\n    return 0;\n}\n\n/* Release all the objects in queue. */\nvoid autoMemoryCollect(RedisModuleCtx *ctx) {\n    if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return;\n    /* Clear the AUTO_MEMORY flag from the context, otherwise the functions\n     * we call to free the resources, will try to scan the auto release\n     * queue to mark the entries as freed. */\n    ctx->flags &= ~REDISMODULE_CTX_AUTO_MEMORY;\n    int j;\n    for (j = 0; j < ctx->amqueue_used; j++) {\n        void *ptr = ctx->amqueue[j].ptr;\n        switch(ctx->amqueue[j].type) {\n        case REDISMODULE_AM_STRING: decrRefCount(ptr); break;\n        case REDISMODULE_AM_REPLY: RM_FreeCallReply(ptr); break;\n        case REDISMODULE_AM_KEY: RM_CloseKey(ptr); break;\n        case REDISMODULE_AM_DICT: RM_FreeDict(NULL,ptr); break;\n        case REDISMODULE_AM_INFO: RM_FreeServerInfo(NULL,ptr); break;\n        }\n    }\n    ctx->flags |= REDISMODULE_CTX_AUTO_MEMORY;\n    zfree(ctx->amqueue);\n    ctx->amqueue = NULL;\n    ctx->amqueue_len = 0;\n    ctx->amqueue_used = 0;\n}\n\n/* --------------------------------------------------------------------------\n * String objects APIs\n * -------------------------------------------------------------------------- */\n\n/* Create a new module string object. The returned string must be freed\n * with RedisModule_FreeString(), unless automatic memory is enabled.\n *\n * The string is created by copying the `len` bytes starting\n * at `ptr`. No reference is retained to the passed buffer.\n *\n * The module context 'ctx' is optional and may be NULL if you want to create\n * a string out of the context scope. However in that case, the automatic\n * memory management will not be available, and the string memory must be\n * managed manually. */\nRedisModuleString *RM_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len) {\n    RedisModuleString *o = createStringObject(ptr,len);\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n    return o;\n}\n\n/* Create a new module string object from a printf format and arguments.\n * The returned string must be freed with RedisModule_FreeString(), unless\n * automatic memory is enabled.\n *\n * The string is created using the sds formatter function sdscatvprintf().\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringPrintf(RedisModuleCtx *ctx, const char *fmt, ...) {\n    sds s = sdsempty();\n\n    va_list ap;\n    va_start(ap, fmt);\n    s = sdscatvprintf(s, fmt, ap);\n    va_end(ap);\n\n    RedisModuleString *o = createObject(OBJ_STRING, s);\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n\n    return o;\n}\n\n\n/* Like RedisModule_CreatString(), but creates a string starting from a long long\n * integer instead of taking a buffer and its length.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management.\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll) {\n    char buf[LONG_STR_SIZE];\n    size_t len = ll2string(buf,sizeof(buf),ll);\n    return RM_CreateString(ctx,buf,len);\n}\n\n/* Like RedisModule_CreatString(), but creates a string starting from a double\n * integer instead of taking a buffer and its length.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management. */\nRedisModuleString *RM_CreateStringFromDouble(RedisModuleCtx *ctx, double d) {\n    char buf[128];\n    size_t len = d2string(buf,sizeof(buf),d);\n    return RM_CreateString(ctx,buf,len);\n}\n\n/* Like RedisModule_CreatString(), but creates a string starting from a long\n * double.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management.\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringFromLongDouble(RedisModuleCtx *ctx, long double ld, int humanfriendly) {\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    size_t len = ld2string(buf,sizeof(buf),ld,\n        (humanfriendly ? LD_STR_HUMAN : LD_STR_AUTO));\n    return RM_CreateString(ctx,buf,len);\n}\n\n/* Like RedisModule_CreatString(), but creates a string starting from another\n * RedisModuleString.\n *\n * The returned string must be released with RedisModule_FreeString() or by\n * enabling automatic memory management.\n *\n * The passed context 'ctx' may be NULL if necessary, see the\n * RedisModule_CreateString() documentation for more info. */\nRedisModuleString *RM_CreateStringFromString(RedisModuleCtx *ctx, const RedisModuleString *str) {\n    RedisModuleString *o = dupStringObject(str);\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n    return o;\n}\n\n/* Free a module string object obtained with one of the Redis modules API calls\n * that return new string objects.\n *\n * It is possible to call this function even when automatic memory management\n * is enabled. In that case the string will be released ASAP and removed\n * from the pool of string to release at the end.\n *\n * If the string was created with a NULL context 'ctx', it is also possible to\n * pass ctx as NULL when releasing the string (but passing a context will not\n * create any issue). Strings created with a context should be freed also passing\n * the context, so if you want to free a string out of context later, make sure\n * to create it using a NULL context. */\nvoid RM_FreeString(RedisModuleCtx *ctx, RedisModuleString *str) {\n    decrRefCount(str);\n    if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_STRING,str);\n}\n\n/* Every call to this function, will make the string 'str' requiring\n * an additional call to RedisModule_FreeString() in order to really\n * free the string. Note that the automatic freeing of the string obtained\n * enabling modules automatic memory management counts for one\n * RedisModule_FreeString() call (it is just executed automatically).\n *\n * Normally you want to call this function when, at the same time\n * the following conditions are true:\n *\n * 1) You have automatic memory management enabled.\n * 2) You want to create string objects.\n * 3) Those string objects you create need to live *after* the callback\n *    function(for example a command implementation) creating them returns.\n *\n * Usually you want this in order to store the created string object\n * into your own data structure, for example when implementing a new data\n * type.\n *\n * Note that when memory management is turned off, you don't need\n * any call to RetainString() since creating a string will always result\n * into a string that lives after the callback function returns, if\n * no FreeString() call is performed.\n *\n * It is possible to call this function with a NULL context. */\nvoid RM_RetainString(RedisModuleCtx *ctx, RedisModuleString *str) {\n    if (ctx == NULL || !autoMemoryFreed(ctx,REDISMODULE_AM_STRING,str)) {\n        /* Increment the string reference counting only if we can't\n         * just remove the object from the list of objects that should\n         * be reclaimed. Why we do that, instead of just incrementing\n         * the refcount in any case, and let the automatic FreeString()\n         * call at the end to bring the refcount back at the desired\n         * value? Because this way we ensure that the object refcount\n         * value is 1 (instead of going to 2 to be dropped later to 1)\n         * after the call to this function. This is needed for functions\n         * like RedisModule_StringAppendBuffer() to work. */\n        incrRefCount(str);\n    }\n}\n\n/* Given a string module object, this function returns the string pointer\n * and length of the string. The returned pointer and length should only\n * be used for read only accesses and never modified. */\nconst char *RM_StringPtrLen(const RedisModuleString *str, size_t *len) {\n    if (str == NULL) {\n        const char *errmsg = \"(NULL string reply referenced in module)\";\n        if (len) *len = strlen(errmsg);\n        return errmsg;\n    }\n    if (len) *len = sdslen(str->ptr);\n    return str->ptr;\n}\n\n/* --------------------------------------------------------------------------\n * Higher level string operations\n * ------------------------------------------------------------------------- */\n\n/* Convert the string into a long long integer, storing it at `*ll`.\n * Returns REDISMODULE_OK on success. If the string can't be parsed\n * as a valid, strict long long (no spaces before/after), REDISMODULE_ERR\n * is returned. */\nint RM_StringToLongLong(const RedisModuleString *str, long long *ll) {\n    return string2ll(str->ptr,sdslen(str->ptr),ll) ? REDISMODULE_OK :\n                                                     REDISMODULE_ERR;\n}\n\n/* Convert the string into a double, storing it at `*d`.\n * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is\n * not a valid string representation of a double value. */\nint RM_StringToDouble(const RedisModuleString *str, double *d) {\n    int retval = getDoubleFromObject(str,d);\n    return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Convert the string into a long double, storing it at `*ld`.\n * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is\n * not a valid string representation of a double value. */\nint RM_StringToLongDouble(const RedisModuleString *str, long double *ld) {\n    int retval = string2ld(str->ptr,sdslen(str->ptr),ld);\n    return retval ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Compare two string objects, returning -1, 0 or 1 respectively if\n * a < b, a == b, a > b. Strings are compared byte by byte as two\n * binary blobs without any encoding care / collation attempt. */\nint RM_StringCompare(RedisModuleString *a, RedisModuleString *b) {\n    return compareStringObjects(a,b);\n}\n\n/* Return the (possibly modified in encoding) input 'str' object if\n * the string is unshared, otherwise NULL is returned. */\nRedisModuleString *moduleAssertUnsharedString(RedisModuleString *str) {\n    if (str->refcount != 1) {\n        serverLog(LL_WARNING,\n            \"Module attempted to use an in-place string modify operation \"\n            \"with a string referenced multiple times. Please check the code \"\n            \"for API usage correctness.\");\n        return NULL;\n    }\n    if (str->encoding == OBJ_ENCODING_EMBSTR) {\n        /* Note: here we \"leak\" the additional allocation that was\n         * used in order to store the embedded string in the object. */\n        str->ptr = sdsnewlen(str->ptr,sdslen(str->ptr));\n        str->encoding = OBJ_ENCODING_RAW;\n    } else if (str->encoding == OBJ_ENCODING_INT) {\n        /* Convert the string from integer to raw encoding. */\n        str->ptr = sdsfromlonglong((long)str->ptr);\n        str->encoding = OBJ_ENCODING_RAW;\n    }\n    return str;\n}\n\n/* Append the specified buffer to the string 'str'. The string must be a\n * string created by the user that is referenced only a single time, otherwise\n * REDISMODULE_ERR is returned and the operation is not performed. */\nint RM_StringAppendBuffer(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) {\n    UNUSED(ctx);\n    str = moduleAssertUnsharedString(str);\n    if (str == NULL) return REDISMODULE_ERR;\n    str->ptr = sdscatlen(str->ptr,buf,len);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Reply APIs\n *\n * Most functions always return REDISMODULE_OK so you can use it with\n * 'return' in order to return from the command implementation with:\n *\n *     if (... some condition ...)\n *         return RM_ReplyWithLongLong(ctx,mycount);\n * -------------------------------------------------------------------------- */\n\n/* Send an error about the number of arguments given to the command,\n * citing the command name in the error message.\n *\n * Example:\n *\n *     if (argc != 3) return RedisModule_WrongArity(ctx);\n */\nint RM_WrongArity(RedisModuleCtx *ctx) {\n    addReplyErrorFormat(ctx->client,\n        \"wrong number of arguments for '%s' command\",\n        (char*)ctx->client->argv[0]->ptr);\n    return REDISMODULE_OK;\n}\n\n/* Return the client object the `RM_Reply*` functions should target.\n * Normally this is just `ctx->client`, that is the client that called\n * the module command, however in the case of thread safe contexts there\n * is no directly associated client (since it would not be safe to access\n * the client from a thread), so instead the blocked client object referenced\n * in the thread safe context, has a fake client that we just use to accumulate\n * the replies. Later, when the client is unblocked, the accumulated replies\n * are appended to the actual client.\n *\n * The function returns the client pointer depending on the context, or\n * NULL if there is no potential client. This happens when we are in the\n * context of a thread safe context that was not initialized with a blocked\n * client object. Other contexts without associated clients are the ones\n * initialized to run the timers callbacks. */\nclient *moduleGetReplyClient(RedisModuleCtx *ctx) {\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) {\n        if (ctx->blocked_client)\n            return ctx->blocked_client->reply_client;\n        else\n            return NULL;\n    } else {\n        /* If this is a non thread safe context, just return the client\n         * that is running the command if any. This may be NULL as well\n         * in the case of contexts that are not executed with associated\n         * clients, like timer contexts. */\n        return ctx->client;\n    }\n}\n\n/* Send an integer reply to the client, with the specified long long value.\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyLongLong(c,ll);\n    return REDISMODULE_OK;\n}\n\n/* Reply with an error or simple string (status message). Used to implement\n * ReplyWithSimpleString() and ReplyWithError().\n * The function always returns REDISMODULE_OK. */\nint replyWithStatus(RedisModuleCtx *ctx, const char *msg, char *prefix) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyProto(c,prefix,strlen(prefix));\n    addReplyProto(c,msg,strlen(msg));\n    addReplyProto(c,\"\\r\\n\",2);\n    return REDISMODULE_OK;\n}\n\n/* Reply with the error 'err'.\n *\n * Note that 'err' must contain all the error, including\n * the initial error code. The function only provides the initial \"-\", so\n * the usage is, for example:\n *\n *     RedisModule_ReplyWithError(ctx,\"ERR Wrong Type\");\n *\n * and not just:\n *\n *     RedisModule_ReplyWithError(ctx,\"Wrong Type\");\n *\n * The function always returns REDISMODULE_OK.\n */\nint RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) {\n    return replyWithStatus(ctx,err,\"-\");\n}\n\n/* Reply with a simple string (+... \\r\\n in RESP protocol). This replies\n * are suitable only when sending a small non-binary string with small\n * overhead, like \"OK\" or similar replies.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg) {\n    return replyWithStatus(ctx,msg,\"+\");\n}\n\n/* Reply with an array type of 'len' elements. However 'len' other calls\n * to `ReplyWith*` style functions must follow in order to emit the elements\n * of the array.\n *\n * When producing arrays with a number of element that is not known beforehand\n * the function can be called with the special count\n * REDISMODULE_POSTPONED_ARRAY_LEN, and the actual number of elements can be\n * later set with RedisModule_ReplySetArrayLength() (which will set the\n * latest \"open\" count if there are multiple ones).\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithArray(RedisModuleCtx *ctx, long len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    if (len == REDISMODULE_POSTPONED_ARRAY_LEN) {\n        ctx->postponed_arrays = zrealloc(ctx->postponed_arrays,sizeof(void*)*\n                (ctx->postponed_arrays_count+1));\n        ctx->postponed_arrays[ctx->postponed_arrays_count] =\n            addReplyDeferredLen(c);\n        ctx->postponed_arrays_count++;\n    } else {\n        addReplyArrayLen(c,len);\n    }\n    return REDISMODULE_OK;\n}\n\n/* Reply to the client with a null array, simply null in RESP3 \n * null array in RESP2.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithNullArray(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyNullArray(c);\n    return REDISMODULE_OK;\n}\n\n/* Reply to the client with an empty array. \n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithEmptyArray(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReply(c,shared.emptyarray);\n    return REDISMODULE_OK;\n}\n\n/* When RedisModule_ReplyWithArray() is used with the argument\n * REDISMODULE_POSTPONED_ARRAY_LEN, because we don't know beforehand the number\n * of items we are going to output as elements of the array, this function\n * will take care to set the array length.\n *\n * Since it is possible to have multiple array replies pending with unknown\n * length, this function guarantees to always set the latest array length\n * that was created in a postponed way.\n *\n * For example in order to output an array like [1,[10,20,30]] we\n * could write:\n *\n *      RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n *      RedisModule_ReplyWithLongLong(ctx,1);\n *      RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n *      RedisModule_ReplyWithLongLong(ctx,10);\n *      RedisModule_ReplyWithLongLong(ctx,20);\n *      RedisModule_ReplyWithLongLong(ctx,30);\n *      RedisModule_ReplySetArrayLength(ctx,3); // Set len of 10,20,30 array.\n *      RedisModule_ReplySetArrayLength(ctx,2); // Set len of top array\n *\n * Note that in the above example there is no reason to postpone the array\n * length, since we produce a fixed number of elements, but in the practice\n * the code may use an iterator or other ways of creating the output so\n * that is not easy to calculate in advance the number of elements.\n */\nvoid RM_ReplySetArrayLength(RedisModuleCtx *ctx, long len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return;\n    if (ctx->postponed_arrays_count == 0) {\n        serverLog(LL_WARNING,\n            \"API misuse detected in module %s: \"\n            \"RedisModule_ReplySetArrayLength() called without previous \"\n            \"RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN) \"\n            \"call.\", ctx->module->name);\n            return;\n    }\n    ctx->postponed_arrays_count--;\n    setDeferredArrayLen(c,\n            ctx->postponed_arrays[ctx->postponed_arrays_count],\n            len);\n    if (ctx->postponed_arrays_count == 0) {\n        zfree(ctx->postponed_arrays);\n        ctx->postponed_arrays = NULL;\n    }\n}\n\n/* Reply with a bulk string, taking in input a C buffer pointer and length.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyBulkCBuffer(c,(char*)buf,len);\n    return REDISMODULE_OK;\n}\n\n/* Reply with a bulk string, taking in input a C buffer pointer that is\n * assumed to be null-terminated.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithCString(RedisModuleCtx *ctx, const char *buf) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyBulkCString(c,(char*)buf);\n    return REDISMODULE_OK;\n}\n\n/* Reply with a bulk string, taking in input a RedisModuleString object.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyBulk(c,str);\n    return REDISMODULE_OK;\n}\n\n/* Reply with an empty string.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithEmptyString(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReply(c,shared.emptybulk);\n    return REDISMODULE_OK;\n}\n\n/* Reply with a binary safe string, which should not be escaped or filtered \n * taking in input a C buffer pointer and length.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithVerbatimString(RedisModuleCtx *ctx, const char *buf, size_t len) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyVerbatim(c, buf, len, \"txt\");\n    return REDISMODULE_OK;\n}\n\n/* Reply to the client with a NULL.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithNull(RedisModuleCtx *ctx) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyNull(c);\n    return REDISMODULE_OK;\n}\n\n/* Reply exactly what a Redis command returned us with RedisModule_Call().\n * This function is useful when we use RedisModule_Call() in order to\n * execute some command, as we want to reply to the client exactly the\n * same reply we obtained by the command.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    sds proto = sdsnewlen(reply->proto, reply->protolen);\n    addReplySds(c,proto);\n    return REDISMODULE_OK;\n}\n\n/* Send a string reply obtained converting the double 'd' into a bulk string.\n * This function is basically equivalent to converting a double into\n * a string into a C buffer, and then calling the function\n * RedisModule_ReplyWithStringBuffer() with the buffer and length.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithDouble(RedisModuleCtx *ctx, double d) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyDouble(c,d);\n    return REDISMODULE_OK;\n}\n\n/* Send a string reply obtained converting the long double 'ld' into a bulk\n * string. This function is basically equivalent to converting a long double\n * into a string into a C buffer, and then calling the function\n * RedisModule_ReplyWithStringBuffer() with the buffer and length.\n * The double string uses human readable formatting (see\n * `addReplyHumanLongDouble` in networking.c).\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld) {\n    client *c = moduleGetReplyClient(ctx);\n    if (c == NULL) return REDISMODULE_OK;\n    addReplyHumanLongDouble(c, ld);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Commands replication API\n * -------------------------------------------------------------------------- */\n\n/* Helper function to replicate MULTI the first time we replicate something\n * in the context of a command execution. EXEC will be handled by the\n * RedisModuleCommandDispatcher() function. */\nvoid moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) {\n    /* Skip this if client explicitly wrap the command with MULTI, or if\n     * the module command was called by a script. */\n    if (ctx->client->flags & (CLIENT_MULTI|CLIENT_LUA)) return;\n    /* If we already emitted MULTI return ASAP. */\n    if (ctx->flags & REDISMODULE_CTX_MULTI_EMITTED) return;\n    /* If this is a thread safe context, we do not want to wrap commands\n     * executed into MULTI/EXEC, they are executed as single commands\n     * from an external client in essence. */\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) return;\n    /* If this is a callback context, and not a module command execution\n     * context, we have to setup the op array for the \"also propagate\" API\n     * so that RM_Replicate() will work. */\n    if (!(ctx->flags & REDISMODULE_CTX_MODULE_COMMAND_CALL)) {\n        ctx->saved_oparray = server.also_propagate;\n        redisOpArrayInit(&server.also_propagate);\n    }\n    execCommandPropagateMulti(ctx->client);\n    ctx->flags |= REDISMODULE_CTX_MULTI_EMITTED;\n}\n\n/* Replicate the specified command and arguments to slaves and AOF, as effect\n * of execution of the calling command implementation.\n *\n * The replicated commands are always wrapped into the MULTI/EXEC that\n * contains all the commands replicated in a given module command\n * execution. However the commands replicated with RedisModule_Call()\n * are the first items, the ones replicated with RedisModule_Replicate()\n * will all follow before the EXEC.\n *\n * Modules should try to use one interface or the other.\n *\n * This command follows exactly the same interface of RedisModule_Call(),\n * so a set of format specifiers must be passed, followed by arguments\n * matching the provided format specifiers.\n *\n * Please refer to RedisModule_Call() for more information.\n *\n * Using the special \"A\" and \"R\" modifiers, the caller can exclude either\n * the AOF or the replicas from the propagation of the specified command.\n * Otherwise, by default, the command will be propagated in both channels.\n *\n * ## Note about calling this function from a thread safe context:\n *\n * Normally when you call this function from the callback implementing a\n * module command, or any other callback provided by the Redis Module API,\n * Redis will accumulate all the calls to this function in the context of\n * the callback, and will propagate all the commands wrapped in a MULTI/EXEC\n * transaction. However when calling this function from a threaded safe context\n * that can live an undefined amount of time, and can be locked/unlocked in\n * at will, the behavior is different: MULTI/EXEC wrapper is not emitted\n * and the command specified is inserted in the AOF and replication stream\n * immediately.\n *\n * ## Return value\n *\n * The command returns REDISMODULE_ERR if the format specifiers are invalid\n * or the command name does not belong to a known command. */\nint RM_Replicate(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) {\n    struct redisCommand *cmd;\n    robj **argv = NULL;\n    int argc = 0, flags = 0, j;\n    va_list ap;\n\n    cmd = lookupCommandByCString((char*)cmdname);\n    if (!cmd) return REDISMODULE_ERR;\n\n    /* Create the client and dispatch the command. */\n    va_start(ap, fmt);\n    argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);\n    va_end(ap);\n    if (argv == NULL) return REDISMODULE_ERR;\n\n    /* Select the propagation target. Usually is AOF + replicas, however\n     * the caller can exclude one or the other using the \"A\" or \"R\"\n     * modifiers. */\n    int target = 0;\n    if (!(flags & REDISMODULE_ARGV_NO_AOF)) target |= PROPAGATE_AOF;\n    if (!(flags & REDISMODULE_ARGV_NO_REPLICAS)) target |= PROPAGATE_REPL;\n\n    /* Replicate! When we are in a threaded context, we want to just insert\n     * the replicated command ASAP, since it is not clear when the context\n     * will stop being used, so accumulating stuff does not make much sense,\n     * nor we could easily use the alsoPropagate() API from threads. */\n    if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) {\n        propagate(cmd,ctx->client->db->id,argv,argc,target);\n    } else {\n        moduleReplicateMultiIfNeeded(ctx);\n        alsoPropagate(cmd,ctx->client->db->id,argv,argc,target);\n    }\n\n    /* Release the argv. */\n    for (j = 0; j < argc; j++) decrRefCount(argv[j]);\n    zfree(argv);\n    server.dirty++;\n    return REDISMODULE_OK;\n}\n\n/* This function will replicate the command exactly as it was invoked\n * by the client. Note that this function will not wrap the command into\n * a MULTI/EXEC stanza, so it should not be mixed with other replication\n * commands.\n *\n * Basically this form of replication is useful when you want to propagate\n * the command to the slaves and AOF file exactly as it was called, since\n * the command can just be re-executed to deterministically re-create the\n * new state starting from the old one.\n *\n * The function always returns REDISMODULE_OK. */\nint RM_ReplicateVerbatim(RedisModuleCtx *ctx) {\n    alsoPropagate(ctx->client->cmd,ctx->client->db->id,\n        ctx->client->argv,ctx->client->argc,\n        PROPAGATE_AOF|PROPAGATE_REPL);\n    server.dirty++;\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * DB and Key APIs -- Generic API\n * -------------------------------------------------------------------------- */\n\n/* Return the ID of the current client calling the currently active module\n * command. The returned ID has a few guarantees:\n *\n * 1. The ID is different for each different client, so if the same client\n *    executes a module command multiple times, it can be recognized as\n *    having the same ID, otherwise the ID will be different.\n * 2. The ID increases monotonically. Clients connecting to the server later\n *    are guaranteed to get IDs greater than any past ID previously seen.\n *\n * Valid IDs are from 1 to 2^64-1. If 0 is returned it means there is no way\n * to fetch the ID in the context the function was currently called.\n *\n * After obtaining the ID, it is possible to check if the command execution\n * is actually happening in the context of AOF loading, using this macro:\n *\n *      if (RedisModule_IsAOFClient(RedisModule_GetClientId(ctx)) {\n *          // Handle it differently.\n *      }\n */\nunsigned long long RM_GetClientId(RedisModuleCtx *ctx) {\n    if (ctx->client == NULL) return 0;\n    return ctx->client->id;\n}\n\n/* This is an helper for RM_GetClientInfoById() and other functions: given\n * a client, it populates the client info structure with the appropriate\n * fields depending on the version provided. If the version is not valid\n * then REDISMODULE_ERR is returned. Otherwise the function returns\n * REDISMODULE_OK and the structure pointed by 'ci' gets populated. */\n\nint modulePopulateClientInfoStructure(void *ci, client *client, int structver) {\n    if (structver != 1) return REDISMODULE_ERR;\n\n    RedisModuleClientInfoV1 *ci1 = ci;\n    memset(ci1,0,sizeof(*ci1));\n    ci1->version = structver;\n    if (client->flags & CLIENT_MULTI)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_MULTI;\n    if (client->flags & CLIENT_PUBSUB)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_PUBSUB;\n    if (client->flags & CLIENT_UNIX_SOCKET)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET;\n    if (client->flags & CLIENT_TRACKING)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_TRACKING;\n    if (client->flags & CLIENT_BLOCKED)\n        ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_BLOCKED;\n\n    int port;\n    connPeerToString(client->conn,ci1->addr,sizeof(ci1->addr),&port);\n    ci1->port = port;\n    ci1->db = client->db->id;\n    ci1->id = client->id;\n    return REDISMODULE_OK;\n}\n\n/* This is an helper for moduleFireServerEvent() and other functions:\n * It populates the replication info structure with the appropriate\n * fields depending on the version provided. If the version is not valid\n * then REDISMODULE_ERR is returned. Otherwise the function returns\n * REDISMODULE_OK and the structure pointed by 'ri' gets populated. */\nint modulePopulateReplicationInfoStructure(void *ri, int structver) {\n    if (structver != 1) return REDISMODULE_ERR;\n\n    RedisModuleReplicationInfoV1 *ri1 = ri;\n    memset(ri1,0,sizeof(*ri1));\n    ri1->version = structver;\n    ri1->master = server.masterhost==NULL;\n    ri1->masterhost = server.masterhost? server.masterhost: \"\";\n    ri1->masterport = server.masterport;\n    ri1->replid1 = server.replid;\n    ri1->replid2 = server.replid2;\n    ri1->repl1_offset = server.master_repl_offset;\n    ri1->repl2_offset = server.second_replid_offset;\n    return REDISMODULE_OK;\n}\n\n/* Return information about the client with the specified ID (that was\n * previously obtained via the RedisModule_GetClientId() API). If the\n * client exists, REDISMODULE_OK is returned, otherwise REDISMODULE_ERR\n * is returned.\n *\n * When the client exist and the `ci` pointer is not NULL, but points to\n * a structure of type RedisModuleClientInfo, previously initialized with\n * the correct REDISMODULE_CLIENTINFO_INITIALIZER, the structure is populated\n * with the following fields:\n *\n *      uint64_t flags;         // REDISMODULE_CLIENTINFO_FLAG_*\n *      uint64_t id;            // Client ID\n *      char addr[46];          // IPv4 or IPv6 address.\n *      uint16_t port;          // TCP port.\n *      uint16_t db;            // Selected DB.\n *\n * Note: the client ID is useless in the context of this call, since we\n *       already know, however the same structure could be used in other\n *       contexts where we don't know the client ID, yet the same structure\n *       is returned.\n *\n * With flags having the following meaning:\n *\n *     REDISMODULE_CLIENTINFO_FLAG_SSL          Client using SSL connection.\n *     REDISMODULE_CLIENTINFO_FLAG_PUBSUB       Client in Pub/Sub mode.\n *     REDISMODULE_CLIENTINFO_FLAG_BLOCKED      Client blocked in command.\n *     REDISMODULE_CLIENTINFO_FLAG_TRACKING     Client with keys tracking on.\n *     REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET   Client using unix domain socket.\n *     REDISMODULE_CLIENTINFO_FLAG_MULTI        Client in MULTI state.\n *\n * However passing NULL is a way to just check if the client exists in case\n * we are not interested in any additional information.\n *\n * This is the correct usage when we want the client info structure\n * returned:\n *\n *      RedisModuleClientInfo ci = REDISMODULE_CLIENTINFO_INITIALIZER;\n *      int retval = RedisModule_GetClientInfoById(&ci,client_id);\n *      if (retval == REDISMODULE_OK) {\n *          printf(\"Address: %s\\n\", ci.addr);\n *      }\n */\nint RM_GetClientInfoById(void *ci, uint64_t id) {\n    client *client = lookupClientByID(id);\n    if (client == NULL) return REDISMODULE_ERR;\n    if (ci == NULL) return REDISMODULE_OK;\n\n    /* Fill the info structure if passed. */\n    uint64_t structver = ((uint64_t*)ci)[0];\n    return modulePopulateClientInfoStructure(ci,client,structver);\n}\n\n/* Publish a message to subscribers (see PUBLISH command). */\nint RM_PublishMessage(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) {\n    UNUSED(ctx);\n    int receivers = pubsubPublishMessage(channel, message);\n    if (server.cluster_enabled)\n        clusterPropagatePublish(channel, message);\n    return receivers;\n}\n\n/* Return the currently selected DB. */\nint RM_GetSelectedDb(RedisModuleCtx *ctx) {\n    return ctx->client->db->id;\n}\n\n\n/* Return the current context's flags. The flags provide information on the\n * current request context (whether the client is a Lua script or in a MULTI),\n * and about the Redis instance in general, i.e replication and persistence.\n *\n * It is possible to call this function even with a NULL context, however\n * in this case the following flags will not be reported:\n *\n *  * LUA, MULTI, REPLICATED, DIRTY (see below for more info).\n *\n * Available flags and their meaning:\n *\n *  * REDISMODULE_CTX_FLAGS_LUA: The command is running in a Lua script\n *\n *  * REDISMODULE_CTX_FLAGS_MULTI: The command is running inside a transaction\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICATED: The command was sent over the replication\n *    link by the MASTER\n *\n *  * REDISMODULE_CTX_FLAGS_MASTER: The Redis instance is a master\n *\n *  * REDISMODULE_CTX_FLAGS_SLAVE: The Redis instance is a slave\n *\n *  * REDISMODULE_CTX_FLAGS_READONLY: The Redis instance is read-only\n *\n *  * REDISMODULE_CTX_FLAGS_CLUSTER: The Redis instance is in cluster mode\n *\n *  * REDISMODULE_CTX_FLAGS_AOF: The Redis instance has AOF enabled\n *\n *  * REDISMODULE_CTX_FLAGS_RDB: The instance has RDB enabled\n *\n *  * REDISMODULE_CTX_FLAGS_MAXMEMORY:  The instance has Maxmemory set\n *\n *  * REDISMODULE_CTX_FLAGS_EVICT:  Maxmemory is set and has an eviction\n *    policy that may delete keys\n *\n *  * REDISMODULE_CTX_FLAGS_OOM: Redis is out of memory according to the\n *    maxmemory setting.\n *\n *  * REDISMODULE_CTX_FLAGS_OOM_WARNING: Less than 25% of memory remains before\n *                                       reaching the maxmemory level.\n *\n *  * REDISMODULE_CTX_FLAGS_LOADING: Server is loading RDB/AOF\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE: No active link with the master.\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING: The replica is trying to\n *                                                 connect with the master.\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING: Master -> Replica RDB\n *                                                   transfer is in progress.\n *\n *  * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE: The replica has an active link\n *                                             with its master. This is the\n *                                             contrary of STALE state.\n *\n *  * REDISMODULE_CTX_FLAGS_ACTIVE_CHILD: There is currently some background\n *                                        process active (RDB, AUX or module).\n */\nint RM_GetContextFlags(RedisModuleCtx *ctx) {\n\n    int flags = 0;\n    /* Client specific flags */\n    if (ctx) {\n        if (ctx->client) {\n            if (ctx->client->flags & CLIENT_LUA)\n             flags |= REDISMODULE_CTX_FLAGS_LUA;\n            if (ctx->client->flags & CLIENT_MULTI)\n             flags |= REDISMODULE_CTX_FLAGS_MULTI;\n            /* Module command recieved from MASTER, is replicated. */\n            if (ctx->client->flags & CLIENT_MASTER)\n             flags |= REDISMODULE_CTX_FLAGS_REPLICATED;\n        }\n\n        /* For DIRTY flags, we need the blocked client if used */\n        client *c = ctx->blocked_client ? ctx->blocked_client->client : ctx->client;\n        if (c && (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC))) {\n            flags |= REDISMODULE_CTX_FLAGS_MULTI_DIRTY;\n        }\n    }\n\n    if (server.cluster_enabled)\n        flags |= REDISMODULE_CTX_FLAGS_CLUSTER;\n\n    if (server.loading)\n        flags |= REDISMODULE_CTX_FLAGS_LOADING;\n\n    /* Maxmemory and eviction policy */\n    if (server.maxmemory > 0) {\n        flags |= REDISMODULE_CTX_FLAGS_MAXMEMORY;\n\n        if (server.maxmemory_policy != MAXMEMORY_NO_EVICTION)\n            flags |= REDISMODULE_CTX_FLAGS_EVICT;\n    }\n\n    /* Persistence flags */\n    if (server.aof_state != AOF_OFF)\n        flags |= REDISMODULE_CTX_FLAGS_AOF;\n    if (server.saveparamslen > 0)\n        flags |= REDISMODULE_CTX_FLAGS_RDB;\n\n    /* Replication flags */\n    if (server.masterhost == NULL) {\n        flags |= REDISMODULE_CTX_FLAGS_MASTER;\n    } else {\n        flags |= REDISMODULE_CTX_FLAGS_SLAVE;\n        if (server.repl_slave_ro)\n            flags |= REDISMODULE_CTX_FLAGS_READONLY;\n\n        /* Replica state flags. */\n        if (server.repl_state == REPL_STATE_CONNECT ||\n            server.repl_state == REPL_STATE_CONNECTING)\n        {\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING;\n        } else if (server.repl_state == REPL_STATE_TRANSFER) {\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING;\n        } else if (server.repl_state == REPL_STATE_CONNECTED) {\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE;\n        }\n\n        if (server.repl_state != REPL_STATE_CONNECTED)\n            flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE;\n    }\n\n    /* OOM flag. */\n    float level;\n    int retval = getMaxmemoryState(NULL,NULL,NULL,&level);\n    if (retval == C_ERR) flags |= REDISMODULE_CTX_FLAGS_OOM;\n    if (level > 0.75) flags |= REDISMODULE_CTX_FLAGS_OOM_WARNING;\n\n    /* Presence of children processes. */\n    if (hasActiveChildProcess()) flags |= REDISMODULE_CTX_FLAGS_ACTIVE_CHILD;\n\n    return flags;\n}\n\n/* Returns true if some client sent the CLIENT PAUSE command to the server or\n * if Redis Cluster is doing a manual failover, and paused tue clients.\n * This is needed when we have a master with replicas, and want to write,\n * without adding further data to the replication channel, that the replicas\n * replication offset, match the one of the master. When this happens, it is\n * safe to failover the master without data loss.\n *\n * However modules may generate traffic by calling RedisModule_Call() with\n * the \"!\" flag, or by calling RedisModule_Replicate(), in a context outside\n * commands execution, for instance in timeout callbacks, threads safe\n * contexts, and so forth. When modules will generate too much traffic, it\n * will be hard for the master and replicas offset to match, because there\n * is more data to send in the replication channel.\n *\n * So modules may want to try to avoid very heavy background work that has\n * the effect of creating data to the replication channel, when this function\n * returns true. This is mostly useful for modules that have background\n * garbage collection tasks, or that do writes and replicate such writes\n * periodically in timer callbacks or other periodic callbacks.\n */\nint RM_AvoidReplicaTraffic() {\n    return clientsArePaused();\n}\n\n/* Change the currently selected DB. Returns an error if the id\n * is out of range.\n *\n * Note that the client will retain the currently selected DB even after\n * the Redis command implemented by the module calling this function\n * returns.\n *\n * If the module command wishes to change something in a different DB and\n * returns back to the original one, it should call RedisModule_GetSelectedDb()\n * before in order to restore the old DB number before returning. */\nint RM_SelectDb(RedisModuleCtx *ctx, int newid) {\n    int retval = selectDb(ctx->client,newid);\n    return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Initialize a RedisModuleKey struct */\nstatic void moduleInitKey(RedisModuleKey *kp, RedisModuleCtx *ctx, robj *keyname, robj *value, int mode){\n    kp->ctx = ctx;\n    kp->db = ctx->client->db;\n    kp->key = keyname;\n    incrRefCount(keyname);\n    kp->value = value;\n    kp->iter = NULL;\n    kp->mode = mode;\n    zsetKeyReset(kp);\n}\n\n/* Return an handle representing a Redis key, so that it is possible\n * to call other APIs with the key handle as argument to perform\n * operations on the key.\n *\n * The return value is the handle representing the key, that must be\n * closed with RM_CloseKey().\n *\n * If the key does not exist and WRITE mode is requested, the handle\n * is still returned, since it is possible to perform operations on\n * a yet not existing key (that will be created, for example, after\n * a list push operation). If the mode is just READ instead, and the\n * key does not exist, NULL is returned. However it is still safe to\n * call RedisModule_CloseKey() and RedisModule_KeyType() on a NULL\n * value. */\nvoid *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {\n    RedisModuleKey *kp;\n    robj *value;\n    int flags = mode & REDISMODULE_OPEN_KEY_NOTOUCH? LOOKUP_NOTOUCH: 0;\n\n    if (mode & REDISMODULE_WRITE) {\n        value = lookupKeyWriteWithFlags(ctx->client->db,keyname, flags);\n    } else {\n        value = lookupKeyReadWithFlags(ctx->client->db,keyname, flags);\n        if (value == NULL) {\n            return NULL;\n        }\n    }\n\n    /* Setup the key handle. */\n    kp = zmalloc(sizeof(*kp));\n    moduleInitKey(kp, ctx, keyname, value, mode);\n    autoMemoryAdd(ctx,REDISMODULE_AM_KEY,kp);\n    return (void*)kp;\n}\n\n/* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */\nstatic void moduleCloseKey(RedisModuleKey *key) {\n    int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);\n    if ((key->mode & REDISMODULE_WRITE) && signal)\n        signalModifiedKey(key->ctx->client,key->db,key->key);\n    /* TODO: if (key->iter) RM_KeyIteratorStop(kp); */\n    RM_ZsetRangeStop(key);\n    decrRefCount(key->key);\n}\n\n/* Close a key handle. */\nvoid RM_CloseKey(RedisModuleKey *key) {\n    if (key == NULL) return;\n    moduleCloseKey(key);\n    autoMemoryFreed(key->ctx,REDISMODULE_AM_KEY,key);\n    zfree(key);\n}\n\n/* Return the type of the key. If the key pointer is NULL then\n * REDISMODULE_KEYTYPE_EMPTY is returned. */\nint RM_KeyType(RedisModuleKey *key) {\n    if (key == NULL || key->value ==  NULL) return REDISMODULE_KEYTYPE_EMPTY;\n    /* We map between defines so that we are free to change the internal\n     * defines as desired. */\n    switch(key->value->type) {\n    case OBJ_STRING: return REDISMODULE_KEYTYPE_STRING;\n    case OBJ_LIST: return REDISMODULE_KEYTYPE_LIST;\n    case OBJ_SET: return REDISMODULE_KEYTYPE_SET;\n    case OBJ_ZSET: return REDISMODULE_KEYTYPE_ZSET;\n    case OBJ_HASH: return REDISMODULE_KEYTYPE_HASH;\n    case OBJ_MODULE: return REDISMODULE_KEYTYPE_MODULE;\n    case OBJ_STREAM: return REDISMODULE_KEYTYPE_STREAM;\n    default: return 0;\n    }\n}\n\n/* Return the length of the value associated with the key.\n * For strings this is the length of the string. For all the other types\n * is the number of elements (just counting keys for hashes).\n *\n * If the key pointer is NULL or the key is empty, zero is returned. */\nsize_t RM_ValueLength(RedisModuleKey *key) {\n    if (key == NULL || key->value == NULL) return 0;\n    switch(key->value->type) {\n    case OBJ_STRING: return stringObjectLen(key->value);\n    case OBJ_LIST: return listTypeLength(key->value);\n    case OBJ_SET: return setTypeSize(key->value);\n    case OBJ_ZSET: return zsetLength(key->value);\n    case OBJ_HASH: return hashTypeLength(key->value);\n    case OBJ_STREAM: return streamLength(key->value);\n    default: return 0;\n    }\n}\n\n/* If the key is open for writing, remove it, and setup the key to\n * accept new writes as an empty key (that will be created on demand).\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing REDISMODULE_ERR is returned. */\nint RM_DeleteKey(RedisModuleKey *key) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value) {\n        dbDelete(key->db,key->key);\n        key->value = NULL;\n    }\n    return REDISMODULE_OK;\n}\n\n/* If the key is open for writing, unlink it (that is delete it in a\n * non-blocking way, not reclaiming memory immediately) and setup the key to\n * accept new writes as an empty key (that will be created on demand).\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing REDISMODULE_ERR is returned. */\nint RM_UnlinkKey(RedisModuleKey *key) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value) {\n        dbAsyncDelete(key->db,key->key);\n        key->value = NULL;\n    }\n    return REDISMODULE_OK;\n}\n\n/* Return the key expire value, as milliseconds of remaining TTL.\n * If no TTL is associated with the key or if the key is empty,\n * REDISMODULE_NO_EXPIRE is returned. */\nmstime_t RM_GetExpire(RedisModuleKey *key) {\n    mstime_t expire = getExpire(key->db,key->key);\n    if (expire == -1 || key->value == NULL) return -1;\n    expire -= mstime();\n    return expire >= 0 ? expire : 0;\n}\n\n/* Set a new expire for the key. If the special expire\n * REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was\n * one (the same as the PERSIST command).\n *\n * Note that the expire must be provided as a positive integer representing\n * the number of milliseconds of TTL the key should have.\n *\n * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if\n * the key was not open for writing or is an empty key. */\nint RM_SetExpire(RedisModuleKey *key, mstime_t expire) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL)\n        return REDISMODULE_ERR;\n    if (expire != REDISMODULE_NO_EXPIRE) {\n        expire += mstime();\n        setExpire(key->ctx->client,key->db,key->key,expire);\n    } else {\n        removeExpire(key->db,key->key);\n    }\n    return REDISMODULE_OK;\n}\n\n/* Performs similar operation to FLUSHALL, and optionally start a new AOF file (if enabled)\n * If restart_aof is true, you must make sure the command that triggered this call is not\n * propagated to the AOF file.\n * When async is set to true, db contents will be freed by a background thread. */\nvoid RM_ResetDataset(int restart_aof, int async) {\n    if (restart_aof && server.aof_state != AOF_OFF) stopAppendOnly();\n    flushAllDataAndResetRDB(async? EMPTYDB_ASYNC: EMPTYDB_NO_FLAGS);\n    if (server.aof_enabled && restart_aof) restartAOFAfterSYNC();\n}\n\n/* Returns the number of keys in the current db. */\nunsigned long long RM_DbSize(RedisModuleCtx *ctx) {\n    return dictSize(ctx->client->db->dict);\n}\n\n/* Returns a name of a random key, or NULL if current db is empty. */\nRedisModuleString *RM_RandomKey(RedisModuleCtx *ctx) {\n    robj *key = dbRandomKey(ctx->client->db);\n    autoMemoryAdd(ctx,REDISMODULE_AM_STRING,key);\n    return key;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for String type\n * -------------------------------------------------------------------------- */\n\n/* If the key is open for writing, set the specified string 'str' as the\n * value of the key, deleting the old value if any.\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing or there is an active iterator, REDISMODULE_ERR is returned. */\nint RM_StringSet(RedisModuleKey *key, RedisModuleString *str) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;\n    RM_DeleteKey(key);\n    genericSetKey(key->ctx->client,key->db,key->key,str,0,0);\n    key->value = str;\n    return REDISMODULE_OK;\n}\n\n/* Prepare the key associated string value for DMA access, and returns\n * a pointer and size (by reference), that the user can use to read or\n * modify the string in-place accessing it directly via pointer.\n *\n * The 'mode' is composed by bitwise OR-ing the following flags:\n *\n *     REDISMODULE_READ -- Read access\n *     REDISMODULE_WRITE -- Write access\n *\n * If the DMA is not requested for writing, the pointer returned should\n * only be accessed in a read-only fashion.\n *\n * On error (wrong type) NULL is returned.\n *\n * DMA access rules:\n *\n * 1. No other key writing function should be called since the moment\n * the pointer is obtained, for all the time we want to use DMA access\n * to read or modify the string.\n *\n * 2. Each time RM_StringTruncate() is called, to continue with the DMA\n * access, RM_StringDMA() should be called again to re-obtain\n * a new pointer and length.\n *\n * 3. If the returned pointer is not NULL, but the length is zero, no\n * byte can be touched (the string is empty, or the key itself is empty)\n * so a RM_StringTruncate() call should be used if there is to enlarge\n * the string, and later call StringDMA() again to get the pointer.\n */\nchar *RM_StringDMA(RedisModuleKey *key, size_t *len, int mode) {\n    /* We need to return *some* pointer for empty keys, we just return\n     * a string literal pointer, that is the advantage to be mapped into\n     * a read only memory page, so the module will segfault if a write\n     * attempt is performed. */\n    char *emptystring = \"<dma-empty-string>\";\n    if (key->value == NULL) {\n        *len = 0;\n        return emptystring;\n    }\n\n    if (key->value->type != OBJ_STRING) return NULL;\n\n    /* For write access, and even for read access if the object is encoded,\n     * we unshare the string (that has the side effect of decoding it). */\n    if ((mode & REDISMODULE_WRITE) || key->value->encoding != OBJ_ENCODING_RAW)\n        key->value = dbUnshareStringValue(key->db, key->key, key->value);\n\n    *len = sdslen(key->value->ptr);\n    return key->value->ptr;\n}\n\n/* If the string is open for writing and is of string type, resize it, padding\n * with zero bytes if the new length is greater than the old one.\n *\n * After this call, RM_StringDMA() must be called again to continue\n * DMA access with the new pointer.\n *\n * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR on\n * error, that is, the key is not open for writing, is not a string\n * or resizing for more than 512 MB is requested.\n *\n * If the key is empty, a string key is created with the new string value\n * unless the new length value requested is zero. */\nint RM_StringTruncate(RedisModuleKey *key, size_t newlen) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_STRING) return REDISMODULE_ERR;\n    if (newlen > 512*1024*1024) return REDISMODULE_ERR;\n\n    /* Empty key and new len set to 0. Just return REDISMODULE_OK without\n     * doing anything. */\n    if (key->value == NULL && newlen == 0) return REDISMODULE_OK;\n\n    if (key->value == NULL) {\n        /* Empty key: create it with the new size. */\n        robj *o = createObject(OBJ_STRING,sdsnewlen(NULL, newlen));\n        genericSetKey(key->ctx->client,key->db,key->key,o,0,0);\n        key->value = o;\n        decrRefCount(o);\n    } else {\n        /* Unshare and resize. */\n        key->value = dbUnshareStringValue(key->db, key->key, key->value);\n        size_t curlen = sdslen(key->value->ptr);\n        if (newlen > curlen) {\n            key->value->ptr = sdsgrowzero(key->value->ptr,newlen);\n        } else if (newlen < curlen) {\n            sdsrange(key->value->ptr,0,newlen-1);\n            /* If the string is too wasteful, reallocate it. */\n            if (sdslen(key->value->ptr) < sdsavail(key->value->ptr))\n                key->value->ptr = sdsRemoveFreeSpace(key->value->ptr);\n        }\n    }\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for List type\n * -------------------------------------------------------------------------- */\n\n/* Push an element into a list, on head or tail depending on 'where' argument.\n * If the key pointer is about an empty key opened for writing, the key\n * is created. On error (key opened for read-only operations or of the wrong\n * type) REDISMODULE_ERR is returned, otherwise REDISMODULE_OK is returned. */\nint RM_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_LIST) return REDISMODULE_ERR;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_LIST);\n    listTypePush(key->value, ele,\n        (where == REDISMODULE_LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL);\n    return REDISMODULE_OK;\n}\n\n/* Pop an element from the list, and returns it as a module string object\n * that the user should be free with RM_FreeString() or by enabling\n * automatic memory. 'where' specifies if the element should be popped from\n * head or tail. The command returns NULL if:\n * 1) The list is empty.\n * 2) The key was not open for writing.\n * 3) The key is not a list. */\nRedisModuleString *RM_ListPop(RedisModuleKey *key, int where) {\n    if (!(key->mode & REDISMODULE_WRITE) ||\n        key->value == NULL ||\n        key->value->type != OBJ_LIST) return NULL;\n    robj *ele = listTypePop(key->value,\n        (where == REDISMODULE_LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL);\n    robj *decoded = getDecodedObject(ele);\n    decrRefCount(ele);\n    moduleDelKeyIfEmpty(key);\n    autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,decoded);\n    return decoded;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for Sorted Set type\n * -------------------------------------------------------------------------- */\n\n/* Conversion from/to public flags of the Modules API and our private flags,\n * so that we have everything decoupled. */\nint RM_ZsetAddFlagsToCoreFlags(int flags) {\n    int retflags = 0;\n    if (flags & REDISMODULE_ZADD_XX) retflags |= ZADD_XX;\n    if (flags & REDISMODULE_ZADD_NX) retflags |= ZADD_NX;\n    return retflags;\n}\n\n/* See previous function comment. */\nint RM_ZsetAddFlagsFromCoreFlags(int flags) {\n    int retflags = 0;\n    if (flags & ZADD_ADDED) retflags |= REDISMODULE_ZADD_ADDED;\n    if (flags & ZADD_UPDATED) retflags |= REDISMODULE_ZADD_UPDATED;\n    if (flags & ZADD_NOP) retflags |= REDISMODULE_ZADD_NOP;\n    return retflags;\n}\n\n/* Add a new element into a sorted set, with the specified 'score'.\n * If the element already exists, the score is updated.\n *\n * A new sorted set is created at value if the key is an empty open key\n * setup for writing.\n *\n * Additional flags can be passed to the function via a pointer, the flags\n * are both used to receive input and to communicate state when the function\n * returns. 'flagsptr' can be NULL if no special flags are used.\n *\n * The input flags are:\n *\n *     REDISMODULE_ZADD_XX: Element must already exist. Do nothing otherwise.\n *     REDISMODULE_ZADD_NX: Element must not exist. Do nothing otherwise.\n *\n * The output flags are:\n *\n *     REDISMODULE_ZADD_ADDED: The new element was added to the sorted set.\n *     REDISMODULE_ZADD_UPDATED: The score of the element was updated.\n *     REDISMODULE_ZADD_NOP: No operation was performed because XX or NX flags.\n *\n * On success the function returns REDISMODULE_OK. On the following errors\n * REDISMODULE_ERR is returned:\n *\n * * The key was not opened for writing.\n * * The key is of the wrong type.\n * * 'score' double value is not a number (NaN).\n */\nint RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) {\n    int flags = 0;\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET);\n    if (flagsptr) flags = RM_ZsetAddFlagsToCoreFlags(*flagsptr);\n    if (zsetAdd(key->value,score,ele->ptr,&flags,NULL) == 0) {\n        if (flagsptr) *flagsptr = 0;\n        return REDISMODULE_ERR;\n    }\n    if (flagsptr) *flagsptr = RM_ZsetAddFlagsFromCoreFlags(flags);\n    return REDISMODULE_OK;\n}\n\n/* This function works exactly like RM_ZsetAdd(), but instead of setting\n * a new score, the score of the existing element is incremented, or if the\n * element does not already exist, it is added assuming the old score was\n * zero.\n *\n * The input and output flags, and the return value, have the same exact\n * meaning, with the only difference that this function will return\n * REDISMODULE_ERR even when 'score' is a valid double number, but adding it\n * to the existing score results into a NaN (not a number) condition.\n *\n * This function has an additional field 'newscore', if not NULL is filled\n * with the new score of the element after the increment, if no error\n * is returned. */\nint RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) {\n    int flags = 0;\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET);\n    if (flagsptr) flags = RM_ZsetAddFlagsToCoreFlags(*flagsptr);\n    flags |= ZADD_INCR;\n    if (zsetAdd(key->value,score,ele->ptr,&flags,newscore) == 0) {\n        if (flagsptr) *flagsptr = 0;\n        return REDISMODULE_ERR;\n    }\n    /* zsetAdd() may signal back that the resulting score is not a number. */\n    if (flagsptr && (*flagsptr & ZADD_NAN)) {\n        *flagsptr = 0;\n        return REDISMODULE_ERR;\n    }\n    if (flagsptr) *flagsptr = RM_ZsetAddFlagsFromCoreFlags(flags);\n    return REDISMODULE_OK;\n}\n\n/* Remove the specified element from the sorted set.\n * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR\n * on one of the following conditions:\n *\n * * The key was not opened for writing.\n * * The key is of the wrong type.\n *\n * The return value does NOT indicate the fact the element was really\n * removed (since it existed) or not, just if the function was executed\n * with success.\n *\n * In order to know if the element was removed, the additional argument\n * 'deleted' must be passed, that populates the integer by reference\n * setting it to 1 or 0 depending on the outcome of the operation.\n * The 'deleted' argument can be NULL if the caller is not interested\n * to know if the element was really removed.\n *\n * Empty keys will be handled correctly by doing nothing. */\nint RM_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted) {\n    if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR;\n    if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (key->value != NULL && zsetDel(key->value,ele->ptr)) {\n        if (deleted) *deleted = 1;\n    } else {\n        if (deleted) *deleted = 0;\n    }\n    return REDISMODULE_OK;\n}\n\n/* On success retrieve the double score associated at the sorted set element\n * 'ele' and returns REDISMODULE_OK. Otherwise REDISMODULE_ERR is returned\n * to signal one of the following conditions:\n *\n * * There is no such element 'ele' in the sorted set.\n * * The key is not a sorted set.\n * * The key is an open empty key.\n */\nint RM_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score) {\n    if (key->value == NULL) return REDISMODULE_ERR;\n    if (key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n    if (zsetScore(key->value,ele->ptr,score) == C_ERR) return REDISMODULE_ERR;\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Key API for Sorted Set iterator\n * -------------------------------------------------------------------------- */\n\nvoid zsetKeyReset(RedisModuleKey *key) {\n    key->ztype = REDISMODULE_ZSET_RANGE_NONE;\n    key->zcurrent = NULL;\n    key->zer = 1;\n}\n\n/* Stop a sorted set iteration. */\nvoid RM_ZsetRangeStop(RedisModuleKey *key) {\n    /* Free resources if needed. */\n    if (key->ztype == REDISMODULE_ZSET_RANGE_LEX)\n        zslFreeLexRange(&key->zlrs);\n    /* Setup sensible values so that misused iteration API calls when an\n     * iterator is not active will result into something more sensible\n     * than crashing. */\n    zsetKeyReset(key);\n}\n\n/* Return the \"End of range\" flag value to signal the end of the iteration. */\nint RM_ZsetRangeEndReached(RedisModuleKey *key) {\n    return key->zer;\n}\n\n/* Helper function for RM_ZsetFirstInScoreRange() and RM_ZsetLastInScoreRange().\n * Setup the sorted set iteration according to the specified score range\n * (see the functions calling it for more info). If 'first' is true the\n * first element in the range is used as a starting point for the iterator\n * otherwise the last. Return REDISMODULE_OK on success otherwise\n * REDISMODULE_ERR. */\nint zsetInitScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex, int first) {\n    if (!key->value || key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n\n    RM_ZsetRangeStop(key);\n    key->ztype = REDISMODULE_ZSET_RANGE_SCORE;\n    key->zer = 0;\n\n    /* Setup the range structure used by the sorted set core implementation\n     * in order to seek at the specified element. */\n    zrangespec *zrs = &key->zrs;\n    zrs->min = min;\n    zrs->max = max;\n    zrs->minex = minex;\n    zrs->maxex = maxex;\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        key->zcurrent = first ? zzlFirstInRange(key->value->ptr,zrs) :\n                                zzlLastInRange(key->value->ptr,zrs);\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = key->value->ptr;\n        zskiplist *zsl = zs->zsl;\n        key->zcurrent = first ? zslFirstInRange(zsl,zrs) :\n                                zslLastInRange(zsl,zrs);\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n    if (key->zcurrent == NULL) key->zer = 1;\n    return REDISMODULE_OK;\n}\n\n/* Setup a sorted set iterator seeking the first element in the specified\n * range. Returns REDISMODULE_OK if the iterator was correctly initialized\n * otherwise REDISMODULE_ERR is returned in the following conditions:\n *\n * 1. The value stored at key is not a sorted set or the key is empty.\n *\n * The range is specified according to the two double values 'min' and 'max'.\n * Both can be infinite using the following two macros:\n *\n * REDISMODULE_POSITIVE_INFINITE for positive infinite value\n * REDISMODULE_NEGATIVE_INFINITE for negative infinite value\n *\n * 'minex' and 'maxex' parameters, if true, respectively setup a range\n * where the min and max value are exclusive (not included) instead of\n * inclusive. */\nint RM_ZsetFirstInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) {\n    return zsetInitScoreRange(key,min,max,minex,maxex,1);\n}\n\n/* Exactly like RedisModule_ZsetFirstInScoreRange() but the last element of\n * the range is selected for the start of the iteration instead. */\nint RM_ZsetLastInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) {\n    return zsetInitScoreRange(key,min,max,minex,maxex,0);\n}\n\n/* Helper function for RM_ZsetFirstInLexRange() and RM_ZsetLastInLexRange().\n * Setup the sorted set iteration according to the specified lexicographical\n * range (see the functions calling it for more info). If 'first' is true the\n * first element in the range is used as a starting point for the iterator\n * otherwise the last. Return REDISMODULE_OK on success otherwise\n * REDISMODULE_ERR.\n *\n * Note that this function takes 'min' and 'max' in the same form of the\n * Redis ZRANGEBYLEX command. */\nint zsetInitLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max, int first) {\n    if (!key->value || key->value->type != OBJ_ZSET) return REDISMODULE_ERR;\n\n    RM_ZsetRangeStop(key);\n    key->zer = 0;\n\n    /* Setup the range structure used by the sorted set core implementation\n     * in order to seek at the specified element. */\n    zlexrangespec *zlrs = &key->zlrs;\n    if (zslParseLexRange(min, max, zlrs) == C_ERR) return REDISMODULE_ERR;\n\n    /* Set the range type to lex only after successfully parsing the range,\n     * otherwise we don't want the zlexrangespec to be freed. */\n    key->ztype = REDISMODULE_ZSET_RANGE_LEX;\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        key->zcurrent = first ? zzlFirstInLexRange(key->value->ptr,zlrs) :\n                                zzlLastInLexRange(key->value->ptr,zlrs);\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = key->value->ptr;\n        zskiplist *zsl = zs->zsl;\n        key->zcurrent = first ? zslFirstInLexRange(zsl,zlrs) :\n                                zslLastInLexRange(zsl,zlrs);\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n    if (key->zcurrent == NULL) key->zer = 1;\n\n    return REDISMODULE_OK;\n}\n\n/* Setup a sorted set iterator seeking the first element in the specified\n * lexicographical range. Returns REDISMODULE_OK if the iterator was correctly\n * initialized otherwise REDISMODULE_ERR is returned in the\n * following conditions:\n *\n * 1. The value stored at key is not a sorted set or the key is empty.\n * 2. The lexicographical range 'min' and 'max' format is invalid.\n *\n * 'min' and 'max' should be provided as two RedisModuleString objects\n * in the same format as the parameters passed to the ZRANGEBYLEX command.\n * The function does not take ownership of the objects, so they can be released\n * ASAP after the iterator is setup. */\nint RM_ZsetFirstInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) {\n    return zsetInitLexRange(key,min,max,1);\n}\n\n/* Exactly like RedisModule_ZsetFirstInLexRange() but the last element of\n * the range is selected for the start of the iteration instead. */\nint RM_ZsetLastInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) {\n    return zsetInitLexRange(key,min,max,0);\n}\n\n/* Return the current sorted set element of an active sorted set iterator\n * or NULL if the range specified in the iterator does not include any\n * element. */\nRedisModuleString *RM_ZsetRangeCurrentElement(RedisModuleKey *key, double *score) {\n    RedisModuleString *str;\n\n    if (key->zcurrent == NULL) return NULL;\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr, *sptr;\n        eptr = key->zcurrent;\n        sds ele = ziplistGetObject(eptr);\n        if (score) {\n            sptr = ziplistNext(key->value->ptr,eptr);\n            *score = zzlGetScore(sptr);\n        }\n        str = createObject(OBJ_STRING,ele);\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zskiplistNode *ln = key->zcurrent;\n        if (score) *score = ln->score;\n        str = createStringObject(ln->ele,sdslen(ln->ele));\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n    autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,str);\n    return str;\n}\n\n/* Go to the next element of the sorted set iterator. Returns 1 if there was\n * a next element, 0 if we are already at the latest element or the range\n * does not include any item at all. */\nint RM_ZsetRangeNext(RedisModuleKey *key) {\n    if (!key->ztype || !key->zcurrent) return 0; /* No active iterator. */\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = key->value->ptr;\n        unsigned char *eptr = key->zcurrent;\n        unsigned char *next;\n        next = ziplistNext(zl,eptr); /* Skip element. */\n        if (next) next = ziplistNext(zl,next); /* Skip score. */\n        if (next == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE) {\n                /* Fetch the next element score for the\n                 * range check. */\n                unsigned char *saved_next = next;\n                next = ziplistNext(zl,next); /* Skip next element. */\n                double score = zzlGetScore(next); /* Obtain the next score. */\n                if (!zslValueLteMax(score,&key->zrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n                next = saved_next;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zzlLexValueLteMax(next,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = next;\n            return 1;\n        }\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zskiplistNode *ln = key->zcurrent, *next = ln->level[0].forward;\n        if (next == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE &&\n                !zslValueLteMax(next->score,&key->zrs))\n            {\n                key->zer = 1;\n                return 0;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zslLexValueLteMax(next->ele,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = next;\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n}\n\n/* Go to the previous element of the sorted set iterator. Returns 1 if there was\n * a previous element, 0 if we are already at the first element or the range\n * does not include any item at all. */\nint RM_ZsetRangePrev(RedisModuleKey *key) {\n    if (!key->ztype || !key->zcurrent) return 0; /* No active iterator. */\n\n    if (key->value->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = key->value->ptr;\n        unsigned char *eptr = key->zcurrent;\n        unsigned char *prev;\n        prev = ziplistPrev(zl,eptr); /* Go back to previous score. */\n        if (prev) prev = ziplistPrev(zl,prev); /* Back to previous ele. */\n        if (prev == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE) {\n                /* Fetch the previous element score for the\n                 * range check. */\n                unsigned char *saved_prev = prev;\n                prev = ziplistNext(zl,prev); /* Skip element to get the score.*/\n                double score = zzlGetScore(prev); /* Obtain the prev score. */\n                if (!zslValueGteMin(score,&key->zrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n                prev = saved_prev;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zzlLexValueGteMin(prev,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = prev;\n            return 1;\n        }\n    } else if (key->value->encoding == OBJ_ENCODING_SKIPLIST) {\n        zskiplistNode *ln = key->zcurrent, *prev = ln->backward;\n        if (prev == NULL) {\n            key->zer = 1;\n            return 0;\n        } else {\n            /* Are we still within the range? */\n            if (key->ztype == REDISMODULE_ZSET_RANGE_SCORE &&\n                !zslValueGteMin(prev->score,&key->zrs))\n            {\n                key->zer = 1;\n                return 0;\n            } else if (key->ztype == REDISMODULE_ZSET_RANGE_LEX) {\n                if (!zslLexValueGteMin(prev->ele,&key->zlrs)) {\n                    key->zer = 1;\n                    return 0;\n                }\n            }\n            key->zcurrent = prev;\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unsupported zset encoding\");\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Key API for Hash type\n * -------------------------------------------------------------------------- */\n\n/* Set the field of the specified hash field to the specified value.\n * If the key is an empty key open for writing, it is created with an empty\n * hash value, in order to set the specified field.\n *\n * The function is variadic and the user must specify pairs of field\n * names and values, both as RedisModuleString pointers (unless the\n * CFIELD option is set, see later). At the end of the field/value-ptr pairs, \n * NULL must be specified as last argument to signal the end of the arguments \n * in the variadic function.\n *\n * Example to set the hash argv[1] to the value argv[2]:\n *\n *      RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],argv[2],NULL);\n *\n * The function can also be used in order to delete fields (if they exist)\n * by setting them to the specified value of REDISMODULE_HASH_DELETE:\n *\n *      RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],\n *                          REDISMODULE_HASH_DELETE,NULL);\n *\n * The behavior of the command changes with the specified flags, that can be\n * set to REDISMODULE_HASH_NONE if no special behavior is needed.\n *\n *     REDISMODULE_HASH_NX: The operation is performed only if the field was not\n *                          already existing in the hash.\n *     REDISMODULE_HASH_XX: The operation is performed only if the field was\n *                          already existing, so that a new value could be\n *                          associated to an existing filed, but no new fields\n *                          are created.\n *     REDISMODULE_HASH_CFIELDS: The field names passed are null terminated C\n *                               strings instead of RedisModuleString objects.\n *\n * Unless NX is specified, the command overwrites the old field value with\n * the new one.\n *\n * When using REDISMODULE_HASH_CFIELDS, field names are reported using\n * normal C strings, so for example to delete the field \"foo\" the following\n * code can be used:\n *\n *      RedisModule_HashSet(key,REDISMODULE_HASH_CFIELDS,\"foo\",\n *                          REDISMODULE_HASH_DELETE,NULL);\n *\n * Return value:\n *\n * The number of fields updated (that may be less than the number of fields\n * specified because of the XX or NX options).\n *\n * In the following case the return value is always zero:\n *\n * * The key was not open for writing.\n * * The key was associated with a non Hash value.\n */\nint RM_HashSet(RedisModuleKey *key, int flags, ...) {\n    va_list ap;\n    if (!(key->mode & REDISMODULE_WRITE)) return 0;\n    if (key->value && key->value->type != OBJ_HASH) return 0;\n    if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_HASH);\n\n    int updated = 0;\n    va_start(ap, flags);\n    while(1) {\n        RedisModuleString *field, *value;\n        /* Get the field and value objects. */\n        if (flags & REDISMODULE_HASH_CFIELDS) {\n            char *cfield = va_arg(ap,char*);\n            if (cfield == NULL) break;\n            field = createRawStringObject(cfield,strlen(cfield));\n        } else {\n            field = va_arg(ap,RedisModuleString*);\n            if (field == NULL) break;\n        }\n        value = va_arg(ap,RedisModuleString*);\n\n        /* Handle XX and NX */\n        if (flags & (REDISMODULE_HASH_XX|REDISMODULE_HASH_NX)) {\n            int exists = hashTypeExists(key->value, field->ptr);\n            if (((flags & REDISMODULE_HASH_XX) && !exists) ||\n                ((flags & REDISMODULE_HASH_NX) && exists))\n            {\n                if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field);\n                continue;\n            }\n        }\n\n        /* Handle deletion if value is REDISMODULE_HASH_DELETE. */\n        if (value == REDISMODULE_HASH_DELETE) {\n            updated += hashTypeDelete(key->value, field->ptr);\n            if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field);\n            continue;\n        }\n\n        int low_flags = HASH_SET_COPY;\n        /* If CFIELDS is active, we can pass the ownership of the\n         * SDS object to the low level function that sets the field\n         * to avoid a useless copy. */\n        if (flags & REDISMODULE_HASH_CFIELDS)\n            low_flags |= HASH_SET_TAKE_FIELD;\n\n        robj *argv[2] = {field,value};\n        hashTypeTryConversion(key->value,argv,0,1);\n        updated += hashTypeSet(key->value, field->ptr, value->ptr, low_flags);\n\n        /* If CFIELDS is active, SDS string ownership is now of hashTypeSet(),\n         * however we still have to release the 'field' object shell. */\n        if (flags & REDISMODULE_HASH_CFIELDS) {\n           field->ptr = NULL; /* Prevent the SDS string from being freed. */\n           decrRefCount(field);\n        }\n    }\n    va_end(ap);\n    moduleDelKeyIfEmpty(key);\n    return updated;\n}\n\n/* Get fields from an hash value. This function is called using a variable\n * number of arguments, alternating a field name (as a StringRedisModule\n * pointer) with a pointer to a StringRedisModule pointer, that is set to the\n * value of the field if the field exist, or NULL if the field did not exist.\n * At the end of the field/value-ptr pairs, NULL must be specified as last\n * argument to signal the end of the arguments in the variadic function.\n *\n * This is an example usage:\n *\n *      RedisModuleString *first, *second;\n *      RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first,\n *                      argv[2],&second,NULL);\n *\n * As with RedisModule_HashSet() the behavior of the command can be specified\n * passing flags different than REDISMODULE_HASH_NONE:\n *\n * REDISMODULE_HASH_CFIELD: field names as null terminated C strings.\n *\n * REDISMODULE_HASH_EXISTS: instead of setting the value of the field\n * expecting a RedisModuleString pointer to pointer, the function just\n * reports if the field exists or not and expects an integer pointer\n * as the second element of each pair.\n *\n * Example of REDISMODULE_HASH_CFIELD:\n *\n *      RedisModuleString *username, *hashedpass;\n *      RedisModule_HashGet(mykey,\"username\",&username,\"hp\",&hashedpass, NULL);\n *\n * Example of REDISMODULE_HASH_EXISTS:\n *\n *      int exists;\n *      RedisModule_HashGet(mykey,argv[1],&exists,NULL);\n *\n * The function returns REDISMODULE_OK on success and REDISMODULE_ERR if\n * the key is not an hash value.\n *\n * Memory management:\n *\n * The returned RedisModuleString objects should be released with\n * RedisModule_FreeString(), or by enabling automatic memory management.\n */\nint RM_HashGet(RedisModuleKey *key, int flags, ...) {\n    va_list ap;\n    if (key->value && key->value->type != OBJ_HASH) return REDISMODULE_ERR;\n\n    va_start(ap, flags);\n    while(1) {\n        RedisModuleString *field, **valueptr;\n        int *existsptr;\n        /* Get the field object and the value pointer to pointer. */\n        if (flags & REDISMODULE_HASH_CFIELDS) {\n            char *cfield = va_arg(ap,char*);\n            if (cfield == NULL) break;\n            field = createRawStringObject(cfield,strlen(cfield));\n        } else {\n            field = va_arg(ap,RedisModuleString*);\n            if (field == NULL) break;\n        }\n\n        /* Query the hash for existence or value object. */\n        if (flags & REDISMODULE_HASH_EXISTS) {\n            existsptr = va_arg(ap,int*);\n            if (key->value)\n                *existsptr = hashTypeExists(key->value,field->ptr);\n            else\n                *existsptr = 0;\n        } else {\n            valueptr = va_arg(ap,RedisModuleString**);\n            if (key->value) {\n                *valueptr = hashTypeGetValueObject(key->value,field->ptr);\n                if (*valueptr) {\n                    robj *decoded = getDecodedObject(*valueptr);\n                    decrRefCount(*valueptr);\n                    *valueptr = decoded;\n                }\n                if (*valueptr)\n                    autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,*valueptr);\n            } else {\n                *valueptr = NULL;\n            }\n        }\n\n        /* Cleanup */\n        if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field);\n    }\n    va_end(ap);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Redis <-> Modules generic Call() API\n * -------------------------------------------------------------------------- */\n\n/* Create a new RedisModuleCallReply object. The processing of the reply\n * is lazy, the object is just populated with the raw protocol and later\n * is processed as needed. Initially we just make sure to set the right\n * reply type, which is extremely cheap to do. */\nRedisModuleCallReply *moduleCreateCallReplyFromProto(RedisModuleCtx *ctx, sds proto) {\n    RedisModuleCallReply *reply = zmalloc(sizeof(*reply));\n    reply->ctx = ctx;\n    reply->proto = proto;\n    reply->protolen = sdslen(proto);\n    reply->flags = REDISMODULE_REPLYFLAG_TOPARSE; /* Lazy parsing. */\n    switch(proto[0]) {\n    case '$':\n    case '+': reply->type = REDISMODULE_REPLY_STRING; break;\n    case '-': reply->type = REDISMODULE_REPLY_ERROR; break;\n    case ':': reply->type = REDISMODULE_REPLY_INTEGER; break;\n    case '*': reply->type = REDISMODULE_REPLY_ARRAY; break;\n    default: reply->type = REDISMODULE_REPLY_UNKNOWN; break;\n    }\n    if ((proto[0] == '*' || proto[0] == '$') && proto[1] == '-')\n        reply->type = REDISMODULE_REPLY_NULL;\n    return reply;\n}\n\nvoid moduleParseCallReply_Int(RedisModuleCallReply *reply);\nvoid moduleParseCallReply_BulkString(RedisModuleCallReply *reply);\nvoid moduleParseCallReply_SimpleString(RedisModuleCallReply *reply);\nvoid moduleParseCallReply_Array(RedisModuleCallReply *reply);\n\n/* Do nothing if REDISMODULE_REPLYFLAG_TOPARSE is false, otherwise\n * use the protcol of the reply in reply->proto in order to fill the\n * reply with parsed data according to the reply type. */\nvoid moduleParseCallReply(RedisModuleCallReply *reply) {\n    if (!(reply->flags & REDISMODULE_REPLYFLAG_TOPARSE)) return;\n    reply->flags &= ~REDISMODULE_REPLYFLAG_TOPARSE;\n\n    switch(reply->proto[0]) {\n    case ':': moduleParseCallReply_Int(reply); break;\n    case '$': moduleParseCallReply_BulkString(reply); break;\n    case '-': /* handled by next item. */\n    case '+': moduleParseCallReply_SimpleString(reply); break;\n    case '*': moduleParseCallReply_Array(reply); break;\n    }\n}\n\nvoid moduleParseCallReply_Int(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n\n    string2ll(proto+1,p-proto-1,&reply->val.ll);\n    reply->protolen = p-proto+2;\n    reply->type = REDISMODULE_REPLY_INTEGER;\n}\n\nvoid moduleParseCallReply_BulkString(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n    long long bulklen;\n\n    string2ll(proto+1,p-proto-1,&bulklen);\n    if (bulklen == -1) {\n        reply->protolen = p-proto+2;\n        reply->type = REDISMODULE_REPLY_NULL;\n    } else {\n        reply->val.str = p+2;\n        reply->len = bulklen;\n        reply->protolen = p-proto+2+bulklen+2;\n        reply->type = REDISMODULE_REPLY_STRING;\n    }\n}\n\nvoid moduleParseCallReply_SimpleString(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n\n    reply->val.str = proto+1;\n    reply->len = p-proto-1;\n    reply->protolen = p-proto+2;\n    reply->type = proto[0] == '+' ? REDISMODULE_REPLY_STRING :\n                                    REDISMODULE_REPLY_ERROR;\n}\n\nvoid moduleParseCallReply_Array(RedisModuleCallReply *reply) {\n    char *proto = reply->proto;\n    char *p = strchr(proto+1,'\\r');\n    long long arraylen, j;\n\n    string2ll(proto+1,p-proto-1,&arraylen);\n    p += 2;\n\n    if (arraylen == -1) {\n        reply->protolen = p-proto;\n        reply->type = REDISMODULE_REPLY_NULL;\n        return;\n    }\n\n    reply->val.array = zmalloc(sizeof(RedisModuleCallReply)*arraylen);\n    reply->len = arraylen;\n    for (j = 0; j < arraylen; j++) {\n        RedisModuleCallReply *ele = reply->val.array+j;\n        ele->flags = REDISMODULE_REPLYFLAG_NESTED |\n                     REDISMODULE_REPLYFLAG_TOPARSE;\n        ele->proto = p;\n        ele->ctx = reply->ctx;\n        moduleParseCallReply(ele);\n        p += ele->protolen;\n    }\n    reply->protolen = p-proto;\n    reply->type = REDISMODULE_REPLY_ARRAY;\n}\n\n/* Free a Call reply and all the nested replies it contains if it's an\n * array. */\nvoid RM_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested){\n    /* Don't free nested replies by default: the user must always free the\n     * toplevel reply. However be gentle and don't crash if the module\n     * misuses the API. */\n    if (!freenested && reply->flags & REDISMODULE_REPLYFLAG_NESTED) return;\n\n    if (!(reply->flags & REDISMODULE_REPLYFLAG_TOPARSE)) {\n        if (reply->type == REDISMODULE_REPLY_ARRAY) {\n            size_t j;\n            for (j = 0; j < reply->len; j++)\n                RM_FreeCallReply_Rec(reply->val.array+j,1);\n            zfree(reply->val.array);\n        }\n    }\n\n    /* For nested replies, we don't free reply->proto (which if not NULL\n     * references the parent reply->proto buffer), nor the structure\n     * itself which is allocated as an array of structures, and is freed\n     * when the array value is released. */\n    if (!(reply->flags & REDISMODULE_REPLYFLAG_NESTED)) {\n        if (reply->proto) sdsfree(reply->proto);\n        zfree(reply);\n    }\n}\n\n/* Wrapper for the recursive free reply function. This is needed in order\n * to have the first level function to return on nested replies, but only\n * if called by the module API. */\nvoid RM_FreeCallReply(RedisModuleCallReply *reply) {\n\n    RedisModuleCtx *ctx = reply->ctx;\n    RM_FreeCallReply_Rec(reply,0);\n    autoMemoryFreed(ctx,REDISMODULE_AM_REPLY,reply);\n}\n\n/* Return the reply type. */\nint RM_CallReplyType(RedisModuleCallReply *reply) {\n    if (!reply) return REDISMODULE_REPLY_UNKNOWN;\n    return reply->type;\n}\n\n/* Return the reply type length, where applicable. */\nsize_t RM_CallReplyLength(RedisModuleCallReply *reply) {\n    moduleParseCallReply(reply);\n    switch(reply->type) {\n    case REDISMODULE_REPLY_STRING:\n    case REDISMODULE_REPLY_ERROR:\n    case REDISMODULE_REPLY_ARRAY:\n        return reply->len;\n    default:\n        return 0;\n    }\n}\n\n/* Return the 'idx'-th nested call reply element of an array reply, or NULL\n * if the reply type is wrong or the index is out of range. */\nRedisModuleCallReply *RM_CallReplyArrayElement(RedisModuleCallReply *reply, size_t idx) {\n    moduleParseCallReply(reply);\n    if (reply->type != REDISMODULE_REPLY_ARRAY) return NULL;\n    if (idx >= reply->len) return NULL;\n    return reply->val.array+idx;\n}\n\n/* Return the long long of an integer reply. */\nlong long RM_CallReplyInteger(RedisModuleCallReply *reply) {\n    moduleParseCallReply(reply);\n    if (reply->type != REDISMODULE_REPLY_INTEGER) return LLONG_MIN;\n    return reply->val.ll;\n}\n\n/* Return the pointer and length of a string or error reply. */\nconst char *RM_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len) {\n    moduleParseCallReply(reply);\n    if (reply->type != REDISMODULE_REPLY_STRING &&\n        reply->type != REDISMODULE_REPLY_ERROR) return NULL;\n    if (len) *len = reply->len;\n    return reply->val.str;\n}\n\n/* Return a new string object from a call reply of type string, error or\n * integer. Otherwise (wrong reply type) return NULL. */\nRedisModuleString *RM_CreateStringFromCallReply(RedisModuleCallReply *reply) {\n    moduleParseCallReply(reply);\n    switch(reply->type) {\n    case REDISMODULE_REPLY_STRING:\n    case REDISMODULE_REPLY_ERROR:\n        return RM_CreateString(reply->ctx,reply->val.str,reply->len);\n    case REDISMODULE_REPLY_INTEGER: {\n        char buf[64];\n        int len = ll2string(buf,sizeof(buf),reply->val.ll);\n        return RM_CreateString(reply->ctx,buf,len);\n        }\n    default: return NULL;\n    }\n}\n\n/* Returns an array of robj pointers, and populates *argc with the number\n * of items, by parsing the format specifier \"fmt\" as described for\n * the RM_Call(), RM_Replicate() and other module APIs.\n *\n * The integer pointed by 'flags' is populated with flags according\n * to special modifiers in \"fmt\". For now only one exists:\n *\n *     \"!\" -> REDISMODULE_ARGV_REPLICATE\n *     \"A\" -> REDISMODULE_ARGV_NO_AOF\n *     \"R\" -> REDISMODULE_ARGV_NO_REPLICAS\n *\n * On error (format specifier error) NULL is returned and nothing is\n * allocated. On success the argument vector is returned. */\nrobj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap) {\n    int argc = 0, argv_size, j;\n    robj **argv = NULL;\n\n    /* As a first guess to avoid useless reallocations, size argv to\n     * hold one argument for each char specifier in 'fmt'. */\n    argv_size = strlen(fmt)+1; /* +1 because of the command name. */\n    argv = zrealloc(argv,sizeof(robj*)*argv_size);\n\n    /* Build the arguments vector based on the format specifier. */\n    argv[0] = createStringObject(cmdname,strlen(cmdname));\n    argc++;\n\n    /* Create the client and dispatch the command. */\n    const char *p = fmt;\n    while(*p) {\n        if (*p == 'c') {\n            char *cstr = va_arg(ap,char*);\n            argv[argc++] = createStringObject(cstr,strlen(cstr));\n        } else if (*p == 's') {\n            robj *obj = va_arg(ap,void*);\n            argv[argc++] = obj;\n            incrRefCount(obj);\n        } else if (*p == 'b') {\n            char *buf = va_arg(ap,char*);\n            size_t len = va_arg(ap,size_t);\n            argv[argc++] = createStringObject(buf,len);\n        } else if (*p == 'l') {\n            long long ll = va_arg(ap,long long);\n            argv[argc++] = createObject(OBJ_STRING,sdsfromlonglong(ll));\n        } else if (*p == 'v') {\n             /* A vector of strings */\n             robj **v = va_arg(ap, void*);\n             size_t vlen = va_arg(ap, size_t);\n\n             /* We need to grow argv to hold the vector's elements.\n              * We resize by vector_len-1 elements, because we held\n              * one element in argv for the vector already */\n             argv_size += vlen-1;\n             argv = zrealloc(argv,sizeof(robj*)*argv_size);\n\n             size_t i = 0;\n             for (i = 0; i < vlen; i++) {\n                 incrRefCount(v[i]);\n                 argv[argc++] = v[i];\n             }\n        } else if (*p == '!') {\n            if (flags) (*flags) |= REDISMODULE_ARGV_REPLICATE;\n        } else if (*p == 'A') {\n            if (flags) (*flags) |= REDISMODULE_ARGV_NO_AOF;\n        } else if (*p == 'R') {\n            if (flags) (*flags) |= REDISMODULE_ARGV_NO_REPLICAS;\n        } else {\n            goto fmterr;\n        }\n        p++;\n    }\n    *argcp = argc;\n    return argv;\n\nfmterr:\n    for (j = 0; j < argc; j++)\n        decrRefCount(argv[j]);\n    zfree(argv);\n    return NULL;\n}\n\n/* Exported API to call any Redis command from modules.\n * On success a RedisModuleCallReply object is returned, otherwise\n * NULL is returned and errno is set to the following values:\n *\n * EBADF: wrong format specifier.\n * EINVAL: wrong command arity.\n * ENOENT: command does not exist.\n * EPERM:  operation in Cluster instance with key in non local slot.\n * EROFS:  operation in Cluster instance when a write command is sent\n *         in a readonly state.\n * ENETDOWN: operation in Cluster instance when cluster is down.\n *\n * This API is documented here: https://redis.io/topics/modules-intro\n */\nRedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) {\n    struct redisCommand *cmd;\n    client *c = NULL;\n    robj **argv = NULL;\n    int argc = 0, flags = 0;\n    va_list ap;\n    RedisModuleCallReply *reply = NULL;\n    int replicate = 0; /* Replicate this command? */\n\n    /* Create the client and dispatch the command. */\n    va_start(ap, fmt);\n    c = createClient(NULL);\n    c->user = NULL; /* Root user. */\n    argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);\n    replicate = flags & REDISMODULE_ARGV_REPLICATE;\n    va_end(ap);\n\n    /* Setup our fake client for command execution. */\n    c->flags |= CLIENT_MODULE;\n    c->db = ctx->client->db;\n    c->argv = argv;\n    c->argc = argc;\n    if (ctx->module) ctx->module->in_call++;\n\n    /* We handle the above format error only when the client is setup so that\n     * we can free it normally. */\n    if (argv == NULL) {\n        errno = EBADF;\n        goto cleanup;\n    }\n\n    /* Call command filters */\n    moduleCallCommandFilters(c);\n\n    /* Lookup command now, after filters had a chance to make modifications\n     * if necessary.\n     */\n    cmd = lookupCommand(c->argv[0]->ptr);\n    if (!cmd) {\n        errno = ENOENT;\n        goto cleanup;\n    }\n    c->cmd = c->lastcmd = cmd;\n\n    /* Basic arity checks. */\n    if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) {\n        errno = EINVAL;\n        goto cleanup;\n    }\n\n    /* If this is a Redis Cluster node, we need to make sure the module is not\n     * trying to access non-local keys, with the exception of commands\n     * received from our master. */\n    if (server.cluster_enabled && !(ctx->client->flags & CLIENT_MASTER)) {\n        int error_code;\n        /* Duplicate relevant flags in the module client. */\n        c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);\n        c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);\n        if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,&error_code) !=\n                           server.cluster->myself)\n        {\n            if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) { \n                errno = EROFS;\n            } else if (error_code == CLUSTER_REDIR_DOWN_STATE) { \n                errno = ENETDOWN;\n            } else {\n                errno = EPERM;\n            }\n            goto cleanup;\n        }\n    }\n\n    /* If we are using single commands replication, we need to wrap what\n     * we propagate into a MULTI/EXEC block, so that it will be atomic like\n     * a Lua script in the context of AOF and slaves. */\n    if (replicate) moduleReplicateMultiIfNeeded(ctx);\n\n    /* Run the command */\n    int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_NOWRAP;\n    if (replicate) {\n        if (!(flags & REDISMODULE_ARGV_NO_AOF))\n            call_flags |= CMD_CALL_PROPAGATE_AOF;\n        if (!(flags & REDISMODULE_ARGV_NO_REPLICAS))\n            call_flags |= CMD_CALL_PROPAGATE_REPL;\n    }\n    call(c,call_flags);\n\n    /* Convert the result of the Redis command into a module reply. */\n    sds proto = sdsnewlen(c->buf,c->bufpos);\n    c->bufpos = 0;\n    while(listLength(c->reply)) {\n        clientReplyBlock *o = listNodeValue(listFirst(c->reply));\n\n        proto = sdscatlen(proto,o->buf,o->used);\n        listDelNode(c->reply,listFirst(c->reply));\n    }\n    reply = moduleCreateCallReplyFromProto(ctx,proto);\n    autoMemoryAdd(ctx,REDISMODULE_AM_REPLY,reply);\n\ncleanup:\n    if (ctx->module) ctx->module->in_call--;\n    freeClient(c);\n    return reply;\n}\n\n/* Return a pointer, and a length, to the protocol returned by the command\n * that returned the reply object. */\nconst char *RM_CallReplyProto(RedisModuleCallReply *reply, size_t *len) {\n    if (reply->proto) *len = sdslen(reply->proto);\n    return reply->proto;\n}\n\n/* --------------------------------------------------------------------------\n * Modules data types\n *\n * When String DMA or using existing data structures is not enough, it is\n * possible to create new data types from scratch and export them to\n * Redis. The module must provide a set of callbacks for handling the\n * new values exported (for example in order to provide RDB saving/loading,\n * AOF rewrite, and so forth). In this section we define this API.\n * -------------------------------------------------------------------------- */\n\n/* Turn a 9 chars name in the specified charset and a 10 bit encver into\n * a single 64 bit unsigned integer that represents this exact module name\n * and version. This final number is called a \"type ID\" and is used when\n * writing module exported values to RDB files, in order to re-associate the\n * value to the right module to load them during RDB loading.\n *\n * If the string is not of the right length or the charset is wrong, or\n * if encver is outside the unsigned 10 bit integer range, 0 is returned,\n * otherwise the function returns the right type ID.\n *\n * The resulting 64 bit integer is composed as follows:\n *\n *     (high order bits) 6|6|6|6|6|6|6|6|6|10 (low order bits)\n *\n * The first 6 bits value is the first character, name[0], while the last\n * 6 bits value, immediately before the 10 bits integer, is name[8].\n * The last 10 bits are the encoding version.\n *\n * Note that a name and encver combo of \"AAAAAAAAA\" and 0, will produce\n * zero as return value, that is the same we use to signal errors, thus\n * this combination is invalid, and also useless since type names should\n * try to be vary to avoid collisions. */\n\nconst char *ModuleTypeNameCharSet =\n             \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n             \"abcdefghijklmnopqrstuvwxyz\"\n             \"0123456789-_\";\n\nuint64_t moduleTypeEncodeId(const char *name, int encver) {\n    /* We use 64 symbols so that we can map each character into 6 bits\n     * of the final output. */\n    const char *cset = ModuleTypeNameCharSet;\n    if (strlen(name) != 9) return 0;\n    if (encver < 0 || encver > 1023) return 0;\n\n    uint64_t id = 0;\n    for (int j = 0; j < 9; j++) {\n        char *p = strchr(cset,name[j]);\n        if (!p) return 0;\n        unsigned long pos = p-cset;\n        id = (id << 6) | pos;\n    }\n    id = (id << 10) | encver;\n    return id;\n}\n\n/* Search, in the list of exported data types of all the modules registered,\n * a type with the same name as the one given. Returns the moduleType\n * structure pointer if such a module is found, or NULL otherwise. */\nmoduleType *moduleTypeLookupModuleByName(const char *name) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        listIter li;\n        listNode *ln;\n\n        listRewind(module->types,&li);\n        while((ln = listNext(&li))) {\n            moduleType *mt = ln->value;\n            if (memcmp(name,mt->name,sizeof(mt->name)) == 0) {\n                dictReleaseIterator(di);\n                return mt;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n    return NULL;\n}\n\n/* Lookup a module by ID, with caching. This function is used during RDB\n * loading. Modules exporting data types should never be able to unload, so\n * our cache does not need to expire. */\n#define MODULE_LOOKUP_CACHE_SIZE 3\n\nmoduleType *moduleTypeLookupModuleByID(uint64_t id) {\n    static struct {\n        uint64_t id;\n        moduleType *mt;\n    } cache[MODULE_LOOKUP_CACHE_SIZE];\n\n    /* Search in cache to start. */\n    int j;\n    for (j = 0; j < MODULE_LOOKUP_CACHE_SIZE && cache[j].mt != NULL; j++)\n        if (cache[j].id == id) return cache[j].mt;\n\n    /* Slow module by module lookup. */\n    moduleType *mt = NULL;\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL && mt == NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        listIter li;\n        listNode *ln;\n\n        listRewind(module->types,&li);\n        while((ln = listNext(&li))) {\n            moduleType *this_mt = ln->value;\n            /* Compare only the 54 bit module identifier and not the\n             * encoding version. */\n            if (this_mt->id >> 10 == id >> 10) {\n                mt = this_mt;\n                break;\n            }\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Add to cache if possible. */\n    if (mt && j < MODULE_LOOKUP_CACHE_SIZE) {\n        cache[j].id = id;\n        cache[j].mt = mt;\n    }\n    return mt;\n}\n\n/* Turn an (unresolved) module ID into a type name, to show the user an\n * error when RDB files contain module data we can't load.\n * The buffer pointed by 'name' must be 10 bytes at least. The function will\n * fill it with a null terminated module name. */\nvoid moduleTypeNameByID(char *name, uint64_t moduleid) {\n    const char *cset = ModuleTypeNameCharSet;\n\n    name[9] = '\\0';\n    char *p = name+8;\n    moduleid >>= 10;\n    for (int j = 0; j < 9; j++) {\n        *p-- = cset[moduleid & 63];\n        moduleid >>= 6;\n    }\n}\n\n/* Register a new data type exported by the module. The parameters are the\n * following. Please for in depth documentation check the modules API\n * documentation, especially the TYPES.md file.\n *\n * * **name**: A 9 characters data type name that MUST be unique in the Redis\n *   Modules ecosystem. Be creative... and there will be no collisions. Use\n *   the charset A-Z a-z 9-0, plus the two \"-_\" characters. A good\n *   idea is to use, for example `<typename>-<vendor>`. For example\n *   \"tree-AntZ\" may mean \"Tree data structure by @antirez\". To use both\n *   lower case and upper case letters helps in order to prevent collisions.\n * * **encver**: Encoding version, which is, the version of the serialization\n *   that a module used in order to persist data. As long as the \"name\"\n *   matches, the RDB loading will be dispatched to the type callbacks\n *   whatever 'encver' is used, however the module can understand if\n *   the encoding it must load are of an older version of the module.\n *   For example the module \"tree-AntZ\" initially used encver=0. Later\n *   after an upgrade, it started to serialize data in a different format\n *   and to register the type with encver=1. However this module may\n *   still load old data produced by an older version if the rdb_load\n *   callback is able to check the encver value and act accordingly.\n *   The encver must be a positive value between 0 and 1023.\n * * **typemethods_ptr** is a pointer to a RedisModuleTypeMethods structure\n *   that should be populated with the methods callbacks and structure\n *   version, like in the following example:\n *\n *      RedisModuleTypeMethods tm = {\n *          .version = REDISMODULE_TYPE_METHOD_VERSION,\n *          .rdb_load = myType_RDBLoadCallBack,\n *          .rdb_save = myType_RDBSaveCallBack,\n *          .aof_rewrite = myType_AOFRewriteCallBack,\n *          .free = myType_FreeCallBack,\n *\n *          // Optional fields\n *          .digest = myType_DigestCallBack,\n *          .mem_usage = myType_MemUsageCallBack,\n *          .aux_load = myType_AuxRDBLoadCallBack,\n *          .aux_save = myType_AuxRDBSaveCallBack,\n *      }\n *\n * * **rdb_load**: A callback function pointer that loads data from RDB files.\n * * **rdb_save**: A callback function pointer that saves data to RDB files.\n * * **aof_rewrite**: A callback function pointer that rewrites data as commands.\n * * **digest**: A callback function pointer that is used for `DEBUG DIGEST`.\n * * **free**: A callback function pointer that can free a type value.\n * * **aux_save**: A callback function pointer that saves out of keyspace data to RDB files.\n *   'when' argument is either REDISMODULE_AUX_BEFORE_RDB or REDISMODULE_AUX_AFTER_RDB.\n * * **aux_load**: A callback function pointer that loads out of keyspace data from RDB files.\n *   Similar to aux_save, returns REDISMODULE_OK on success, and ERR otherwise.\n *\n * The **digest* and **mem_usage** methods should currently be omitted since\n * they are not yet implemented inside the Redis modules core.\n *\n * Note: the module name \"AAAAAAAAA\" is reserved and produces an error, it\n * happens to be pretty lame as well.\n *\n * If there is already a module registering a type with the same name,\n * and if the module name or encver is invalid, NULL is returned.\n * Otherwise the new type is registered into Redis, and a reference of\n * type RedisModuleType is returned: the caller of the function should store\n * this reference into a gobal variable to make future use of it in the\n * modules type API, since a single module may register multiple types.\n * Example code fragment:\n *\n *      static RedisModuleType *BalancedTreeType;\n *\n *      int RedisModule_OnLoad(RedisModuleCtx *ctx) {\n *          // some code here ...\n *          BalancedTreeType = RM_CreateDataType(...);\n *      }\n */\nmoduleType *RM_CreateDataType(RedisModuleCtx *ctx, const char *name, int encver, void *typemethods_ptr) {\n    uint64_t id = moduleTypeEncodeId(name,encver);\n    if (id == 0) return NULL;\n    if (moduleTypeLookupModuleByName(name) != NULL) return NULL;\n\n    long typemethods_version = ((long*)typemethods_ptr)[0];\n    if (typemethods_version == 0) return NULL;\n\n    struct typemethods {\n        uint64_t version;\n        moduleTypeLoadFunc rdb_load;\n        moduleTypeSaveFunc rdb_save;\n        moduleTypeRewriteFunc aof_rewrite;\n        moduleTypeMemUsageFunc mem_usage;\n        moduleTypeDigestFunc digest;\n        moduleTypeFreeFunc free;\n        struct {\n            moduleTypeAuxLoadFunc aux_load;\n            moduleTypeAuxSaveFunc aux_save;\n            int aux_save_triggers;\n        } v2;\n    } *tms = (struct typemethods*) typemethods_ptr;\n\n    moduleType *mt = zcalloc(sizeof(*mt));\n    mt->id = id;\n    mt->module = ctx->module;\n    mt->rdb_load = tms->rdb_load;\n    mt->rdb_save = tms->rdb_save;\n    mt->aof_rewrite = tms->aof_rewrite;\n    mt->mem_usage = tms->mem_usage;\n    mt->digest = tms->digest;\n    mt->free = tms->free;\n    if (tms->version >= 2) {\n        mt->aux_load = tms->v2.aux_load;\n        mt->aux_save = tms->v2.aux_save;\n        mt->aux_save_triggers = tms->v2.aux_save_triggers;\n    }\n    memcpy(mt->name,name,sizeof(mt->name));\n    listAddNodeTail(ctx->module->types,mt);\n    return mt;\n}\n\n/* If the key is open for writing, set the specified module type object\n * as the value of the key, deleting the old value if any.\n * On success REDISMODULE_OK is returned. If the key is not open for\n * writing or there is an active iterator, REDISMODULE_ERR is returned. */\nint RM_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;\n    RM_DeleteKey(key);\n    robj *o = createModuleObject(mt,value);\n    genericSetKey(key->ctx->client,key->db,key->key,o,0,0);\n    decrRefCount(o);\n    key->value = o;\n    return REDISMODULE_OK;\n}\n\n/* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on\n * the key, returns the module type pointer of the value stored at key.\n *\n * If the key is NULL, is not associated with a module type, or is empty,\n * then NULL is returned instead. */\nmoduleType *RM_ModuleTypeGetType(RedisModuleKey *key) {\n    if (key == NULL ||\n        key->value == NULL ||\n        RM_KeyType(key) != REDISMODULE_KEYTYPE_MODULE) return NULL;\n    moduleValue *mv = key->value->ptr;\n    return mv->type;\n}\n\n/* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on\n * the key, returns the module type low-level value stored at key, as\n * it was set by the user via RedisModule_ModuleTypeSet().\n *\n * If the key is NULL, is not associated with a module type, or is empty,\n * then NULL is returned instead. */\nvoid *RM_ModuleTypeGetValue(RedisModuleKey *key) {\n    if (key == NULL ||\n        key->value == NULL ||\n        RM_KeyType(key) != REDISMODULE_KEYTYPE_MODULE) return NULL;\n    moduleValue *mv = key->value->ptr;\n    return mv->value;\n}\n\n/* --------------------------------------------------------------------------\n * RDB loading and saving functions\n * -------------------------------------------------------------------------- */\n\n/* Called when there is a load error in the context of a module. On some\n * modules this cannot be recovered, but if the module declared capability\n * to handle errors, we'll raise a flag rather than exiting. */\nvoid moduleRDBLoadError(RedisModuleIO *io) {\n    if (io->type->module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS) {\n        io->error = 1;\n        return;\n    }\n    serverPanic(\n        \"Error loading data from RDB (short read or EOF). \"\n        \"Read performed by module '%s' about type '%s' \"\n        \"after reading '%llu' bytes of a value \"\n        \"for key named: '%s'.\",\n        io->type->module->name,\n        io->type->name,\n        (unsigned long long)io->bytes,\n        io->key? (char*)io->key->ptr: \"(null)\");\n}\n\n/* Returns 0 if there's at least one registered data type that did not declare\n * REDISMODULE_OPTIONS_HANDLE_IO_ERRORS, in which case diskless loading should\n * be avoided since it could cause data loss. */\nint moduleAllDatatypesHandleErrors() {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        if (listLength(module->types) &&\n            !(module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS))\n        {\n            dictReleaseIterator(di);\n            return 0;\n        }\n    }\n    dictReleaseIterator(di);\n    return 1;\n}\n\n/* Returns true if any previous IO API failed.\n * for Load* APIs the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag must be set with\n * RediModule_SetModuleOptions first. */\nint RM_IsIOError(RedisModuleIO *io) {\n    return io->error;\n}\n\n/* Save an unsigned 64 bit value into the RDB file. This function should only\n * be called in the context of the rdb_save method of modules implementing new\n * data types. */\nvoid RM_SaveUnsigned(RedisModuleIO *io, uint64_t value) {\n    if (io->error) return;\n    /* Save opcode. */\n    int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_UINT);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveLen(io->rio, value);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* Load an unsigned 64 bit value from the RDB file. This function should only\n * be called in the context of the rdb_load method of modules implementing\n * new data types. */\nuint64_t RM_LoadUnsigned(RedisModuleIO *io) {\n    if (io->error) return 0;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_UINT) goto loaderr;\n    }\n    uint64_t value;\n    int retval = rdbLoadLenByRef(io->rio, NULL, &value);\n    if (retval == -1) goto loaderr;\n    return value;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return 0;\n}\n\n/* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */\nvoid RM_SaveSigned(RedisModuleIO *io, int64_t value) {\n    union {uint64_t u; int64_t i;} conv;\n    conv.i = value;\n    RM_SaveUnsigned(io,conv.u);\n}\n\n/* Like RedisModule_LoadUnsigned() but for signed 64 bit values. */\nint64_t RM_LoadSigned(RedisModuleIO *io) {\n    union {uint64_t u; int64_t i;} conv;\n    conv.u = RM_LoadUnsigned(io);\n    return conv.i;\n}\n\n/* In the context of the rdb_save method of a module type, saves a\n * string into the RDB file taking as input a RedisModuleString.\n *\n * The string can be later loaded with RedisModule_LoadString() or\n * other Load family functions expecting a serialized string inside\n * the RDB file. */\nvoid RM_SaveString(RedisModuleIO *io, RedisModuleString *s) {\n    if (io->error) return;\n    /* Save opcode. */\n    ssize_t retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_STRING);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveStringObject(io->rio, s);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* Like RedisModule_SaveString() but takes a raw C pointer and length\n * as input. */\nvoid RM_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len) {\n    if (io->error) return;\n    /* Save opcode. */\n    ssize_t retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_STRING);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveRawString(io->rio, (unsigned char*)str,len);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* Implements RM_LoadString() and RM_LoadStringBuffer() */\nvoid *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) {\n    if (io->error) return NULL;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_STRING) goto loaderr;\n    }\n    void *s = rdbGenericLoadStringObject(io->rio,\n              plain ? RDB_LOAD_PLAIN : RDB_LOAD_NONE, lenptr);\n    if (s == NULL) goto loaderr;\n    return s;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return NULL;\n}\n\n/* In the context of the rdb_load method of a module data type, loads a string\n * from the RDB file, that was previously saved with RedisModule_SaveString()\n * functions family.\n *\n * The returned string is a newly allocated RedisModuleString object, and\n * the user should at some point free it with a call to RedisModule_FreeString().\n *\n * If the data structure does not store strings as RedisModuleString objects,\n * the similar function RedisModule_LoadStringBuffer() could be used instead. */\nRedisModuleString *RM_LoadString(RedisModuleIO *io) {\n    return moduleLoadString(io,0,NULL);\n}\n\n/* Like RedisModule_LoadString() but returns an heap allocated string that\n * was allocated with RedisModule_Alloc(), and can be resized or freed with\n * RedisModule_Realloc() or RedisModule_Free().\n *\n * The size of the string is stored at '*lenptr' if not NULL.\n * The returned string is not automatically NULL terminated, it is loaded\n * exactly as it was stored inisde the RDB file. */\nchar *RM_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr) {\n    return moduleLoadString(io,1,lenptr);\n}\n\n/* In the context of the rdb_save method of a module data type, saves a double\n * value to the RDB file. The double can be a valid number, a NaN or infinity.\n * It is possible to load back the value with RedisModule_LoadDouble(). */\nvoid RM_SaveDouble(RedisModuleIO *io, double value) {\n    if (io->error) return;\n    /* Save opcode. */\n    int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_DOUBLE);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveBinaryDoubleValue(io->rio, value);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* In the context of the rdb_save method of a module data type, loads back the\n * double value saved by RedisModule_SaveDouble(). */\ndouble RM_LoadDouble(RedisModuleIO *io) {\n    if (io->error) return 0;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_DOUBLE) goto loaderr;\n    }\n    double value;\n    int retval = rdbLoadBinaryDoubleValue(io->rio, &value);\n    if (retval == -1) goto loaderr;\n    return value;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return 0;\n}\n\n/* In the context of the rdb_save method of a module data type, saves a float\n * value to the RDB file. The float can be a valid number, a NaN or infinity.\n * It is possible to load back the value with RedisModule_LoadFloat(). */\nvoid RM_SaveFloat(RedisModuleIO *io, float value) {\n    if (io->error) return;\n    /* Save opcode. */\n    int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_FLOAT);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    /* Save value. */\n    retval = rdbSaveBinaryFloatValue(io->rio, value);\n    if (retval == -1) goto saveerr;\n    io->bytes += retval;\n    return;\n\nsaveerr:\n    io->error = 1;\n}\n\n/* In the context of the rdb_save method of a module data type, loads back the\n * float value saved by RedisModule_SaveFloat(). */\nfloat RM_LoadFloat(RedisModuleIO *io) {\n    if (io->error) return 0;\n    if (io->ver == 2) {\n        uint64_t opcode = rdbLoadLen(io->rio,NULL);\n        if (opcode != RDB_MODULE_OPCODE_FLOAT) goto loaderr;\n    }\n    float value;\n    int retval = rdbLoadBinaryFloatValue(io->rio, &value);\n    if (retval == -1) goto loaderr;\n    return value;\n\nloaderr:\n    moduleRDBLoadError(io);\n    return 0;\n}\n\n/* In the context of the rdb_save method of a module data type, saves a long double\n * value to the RDB file. The double can be a valid number, a NaN or infinity.\n * It is possible to load back the value with RedisModule_LoadLongDouble(). */\nvoid RM_SaveLongDouble(RedisModuleIO *io, long double value) {\n    if (io->error) return;\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    /* Long double has different number of bits in different platforms, so we\n     * save it as a string type. */\n    size_t len = ld2string(buf,sizeof(buf),value,LD_STR_HEX);\n    RM_SaveStringBuffer(io,buf,len);\n}\n\n/* In the context of the rdb_save method of a module data type, loads back the\n * long double value saved by RedisModule_SaveLongDouble(). */\nlong double RM_LoadLongDouble(RedisModuleIO *io) {\n    if (io->error) return 0;\n    long double value;\n    size_t len;\n    char* str = RM_LoadStringBuffer(io,&len);\n    if (!str) return 0;\n    string2ld(str,len,&value);\n    RM_Free(str);\n    return value;\n}\n\n/* Iterate over modules, and trigger rdb aux saving for the ones modules types\n * who asked for it. */\nssize_t rdbSaveModulesAux(rio *rdb, int when) {\n    size_t total_written = 0;\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        listIter li;\n        listNode *ln;\n\n        listRewind(module->types,&li);\n        while((ln = listNext(&li))) {\n            moduleType *mt = ln->value;\n            if (!mt->aux_save || !(mt->aux_save_triggers & when))\n                continue;\n            ssize_t ret = rdbSaveSingleModuleAux(rdb, when, mt);\n            if (ret==-1) {\n                dictReleaseIterator(di);\n                return -1;\n            }\n            total_written += ret;\n        }\n    }\n\n    dictReleaseIterator(di);\n    return total_written;\n}\n\n/* --------------------------------------------------------------------------\n * Key digest API (DEBUG DIGEST interface for modules types)\n * -------------------------------------------------------------------------- */\n\n/* Add a new element to the digest. This function can be called multiple times\n * one element after the other, for all the elements that constitute a given\n * data structure. The function call must be followed by the call to\n * `RedisModule_DigestEndSequence` eventually, when all the elements that are\n * always in a given order are added. See the Redis Modules data types\n * documentation for more info. However this is a quick example that uses Redis\n * data types as an example.\n *\n * To add a sequence of unordered elements (for example in the case of a Redis\n * Set), the pattern to use is:\n *\n *     foreach element {\n *         AddElement(element);\n *         EndSequence();\n *     }\n *\n * Because Sets are not ordered, so every element added has a position that\n * does not depend from the other. However if instead our elements are\n * ordered in pairs, like field-value pairs of an Hash, then one should\n * use:\n *\n *     foreach key,value {\n *         AddElement(key);\n *         AddElement(value);\n *         EndSquence();\n *     }\n *\n * Because the key and value will be always in the above order, while instead\n * the single key-value pairs, can appear in any position into a Redis hash.\n *\n * A list of ordered elements would be implemented with:\n *\n *     foreach element {\n *         AddElement(element);\n *     }\n *     EndSequence();\n *\n */\nvoid RM_DigestAddStringBuffer(RedisModuleDigest *md, unsigned char *ele, size_t len) {\n    mixDigest(md->o,ele,len);\n}\n\n/* Like `RedisModule_DigestAddStringBuffer()` but takes a long long as input\n * that gets converted into a string before adding it to the digest. */\nvoid RM_DigestAddLongLong(RedisModuleDigest *md, long long ll) {\n    char buf[LONG_STR_SIZE];\n    size_t len = ll2string(buf,sizeof(buf),ll);\n    mixDigest(md->o,buf,len);\n}\n\n/* See the documentation for `RedisModule_DigestAddElement()`. */\nvoid RM_DigestEndSequence(RedisModuleDigest *md) {\n    xorDigest(md->x,md->o,sizeof(md->o));\n    memset(md->o,0,sizeof(md->o));\n}\n\n/* Decode a serialized representation of a module data type 'mt' from string\n * 'str' and return a newly allocated value, or NULL if decoding failed.\n *\n * This call basically reuses the 'rdb_load' callback which module data types\n * implement in order to allow a module to arbitrarily serialize/de-serialize\n * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.\n *\n * Modules should generally use the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag and\n * make sure the de-serialization code properly checks and handles IO errors\n * (freeing allocated buffers and returning a NULL).\n *\n * If this is NOT done, Redis will handle corrupted (or just truncated) serialized\n * data by producing an error message and terminating the process.\n */\n\nvoid *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType *mt) {\n    rio payload;\n    RedisModuleIO io;\n    void *ret;\n\n    rioInitWithBuffer(&payload, str->ptr);\n    moduleInitIOContext(io,(moduleType *)mt,&payload,NULL);\n\n    /* All RM_Save*() calls always write a version 2 compatible format, so we\n     * need to make sure we read the same.\n     */\n    io.ver = 2;\n    ret = mt->rdb_load(&io,0);\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    return ret;\n}\n\n/* Encode a module data type 'mt' value 'data' into serialized form, and return it\n * as a newly allocated RedisModuleString.\n *\n * This call basically reuses the 'rdb_save' callback which module data types\n * implement in order to allow a module to arbitrarily serialize/de-serialize\n * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.\n */\n\nRedisModuleString *RM_SaveDataTypeToString(RedisModuleCtx *ctx, void *data, const moduleType *mt) {\n    rio payload;\n    RedisModuleIO io;\n\n    rioInitWithBuffer(&payload,sdsempty());\n    moduleInitIOContext(io,(moduleType *)mt,&payload,NULL);\n    mt->rdb_save(&io,data);\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    if (io.error) {\n        return NULL;\n    } else {\n        robj *str = createObject(OBJ_STRING,payload.io.buffer.ptr);\n        if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,str);\n        return str;\n    }\n}\n\n/* --------------------------------------------------------------------------\n * AOF API for modules data types\n * -------------------------------------------------------------------------- */\n\n/* Emits a command into the AOF during the AOF rewriting process. This function\n * is only called in the context of the aof_rewrite method of data types exported\n * by a module. The command works exactly like RedisModule_Call() in the way\n * the parameters are passed, but it does not return anything as the error\n * handling is performed by Redis itself. */\nvoid RM_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) {\n    if (io->error) return;\n    struct redisCommand *cmd;\n    robj **argv = NULL;\n    int argc = 0, flags = 0, j;\n    va_list ap;\n\n    cmd = lookupCommandByCString((char*)cmdname);\n    if (!cmd) {\n        serverLog(LL_WARNING,\n            \"Fatal: AOF method for module data type '%s' tried to \"\n            \"emit unknown command '%s'\",\n            io->type->name, cmdname);\n        io->error = 1;\n        errno = EINVAL;\n        return;\n    }\n\n    /* Emit the arguments into the AOF in Redis protocol format. */\n    va_start(ap, fmt);\n    argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);\n    va_end(ap);\n    if (argv == NULL) {\n        serverLog(LL_WARNING,\n            \"Fatal: AOF method for module data type '%s' tried to \"\n            \"call RedisModule_EmitAOF() with wrong format specifiers '%s'\",\n            io->type->name, fmt);\n        io->error = 1;\n        errno = EINVAL;\n        return;\n    }\n\n    /* Bulk count. */\n    if (!io->error && rioWriteBulkCount(io->rio,'*',argc) == 0)\n        io->error = 1;\n\n    /* Arguments. */\n    for (j = 0; j < argc; j++) {\n        if (!io->error && rioWriteBulkObject(io->rio,argv[j]) == 0)\n            io->error = 1;\n        decrRefCount(argv[j]);\n    }\n    zfree(argv);\n    return;\n}\n\n/* --------------------------------------------------------------------------\n * IO context handling\n * -------------------------------------------------------------------------- */\n\nRedisModuleCtx *RM_GetContextFromIO(RedisModuleIO *io) {\n    if (io->ctx) return io->ctx; /* Can't have more than one... */\n    RedisModuleCtx ctxtemplate = REDISMODULE_CTX_INIT;\n    io->ctx = zmalloc(sizeof(RedisModuleCtx));\n    *(io->ctx) = ctxtemplate;\n    io->ctx->module = io->type->module;\n    io->ctx->client = NULL;\n    return io->ctx;\n}\n\n/* Returns a RedisModuleString with the name of the key currently saving or\n * loading, when an IO data type callback is called.  There is no guarantee\n * that the key name is always available, so this may return NULL.\n */\nconst RedisModuleString *RM_GetKeyNameFromIO(RedisModuleIO *io) {\n    return io->key;\n}\n\n/* Returns a RedisModuleString with the name of the key from RedisModuleKey */\nconst RedisModuleString *RM_GetKeyNameFromModuleKey(RedisModuleKey *key) {\n    return key ? key->key : NULL;\n}\n\n/* --------------------------------------------------------------------------\n * Logging\n * -------------------------------------------------------------------------- */\n\n/* This is the low level function implementing both:\n *\n *      RM_Log()\n *      RM_LogIOError()\n *\n */\nvoid RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_list ap) {\n    char msg[LOG_MAX_LEN];\n    size_t name_len;\n    int level;\n\n    if (!strcasecmp(levelstr,\"debug\")) level = LL_DEBUG;\n    else if (!strcasecmp(levelstr,\"verbose\")) level = LL_VERBOSE;\n    else if (!strcasecmp(levelstr,\"notice\")) level = LL_NOTICE;\n    else if (!strcasecmp(levelstr,\"warning\")) level = LL_WARNING;\n    else level = LL_VERBOSE; /* Default. */\n\n    if (level < server.verbosity) return;\n\n    name_len = snprintf(msg, sizeof(msg),\"<%s> \", module? module->name: \"module\");\n    vsnprintf(msg + name_len, sizeof(msg) - name_len, fmt, ap);\n    serverLogRaw(level,msg);\n}\n\n/* Produces a log message to the standard Redis log, the format accepts\n * printf-alike specifiers, while level is a string describing the log\n * level to use when emitting the log, and must be one of the following:\n *\n * * \"debug\"\n * * \"verbose\"\n * * \"notice\"\n * * \"warning\"\n *\n * If the specified log level is invalid, verbose is used by default.\n * There is a fixed limit to the length of the log line this function is able\n * to emit, this limit is not specified but is guaranteed to be more than\n * a few lines of text.\n *\n * The ctx argument may be NULL if cannot be provided in the context of the\n * caller for instance threads or callbacks, in which case a generic \"module\"\n * will be used instead of the module name.\n */\nvoid RM_Log(RedisModuleCtx *ctx, const char *levelstr, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    RM_LogRaw(ctx? ctx->module: NULL,levelstr,fmt,ap);\n    va_end(ap);\n}\n\n/* Log errors from RDB / AOF serialization callbacks.\n *\n * This function should be used when a callback is returning a critical\n * error to the caller since cannot load or save the data for some\n * critical reason. */\nvoid RM_LogIOError(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    RM_LogRaw(io->type->module,levelstr,fmt,ap);\n    va_end(ap);\n}\n\n/* Redis-like assert function.\n *\n * A failed assertion will shut down the server and produce logging information\n * that looks identical to information generated by Redis itself.\n */\nvoid RM__Assert(const char *estr, const char *file, int line) {\n    _serverAssert(estr, file, line);\n}\n\n/* Allows adding event to the latency monitor to be observed by the LATENCY\n * command. The call is skipped if the latency is smaller than the configured\n * latency-monitor-threshold. */\nvoid RM_LatencyAddSample(const char *event, mstime_t latency) {\n    if (latency >= server.latency_monitor_threshold)\n        latencyAddSample(event, latency);\n}\n\n/* --------------------------------------------------------------------------\n * Blocking clients from modules\n * -------------------------------------------------------------------------- */\n\n/* Readable handler for the awake pipe. We do nothing here, the awake bytes\n * will be actually read in a more appropriate place in the\n * moduleHandleBlockedClients() function that is where clients are actually\n * served. */\nvoid moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {\n    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n    UNUSED(privdata);\n}\n\n/* This is called from blocked.c in order to unblock a client: may be called\n * for multiple reasons while the client is in the middle of being blocked\n * because the client is terminated, but is also called for cleanup when a\n * client is unblocked in a clean way after replaying.\n *\n * What we do here is just to set the client to NULL in the redis module\n * blocked client handle. This way if the client is terminated while there\n * is a pending threaded operation involving the blocked client, we'll know\n * that the client no longer exists and no reply callback should be called.\n *\n * The structure RedisModuleBlockedClient will be always deallocated when\n * running the list of clients blocked by a module that need to be unblocked. */\nvoid unblockClientFromModule(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n\n    /* Call the disconnection callback if any. Note that\n     * bc->disconnect_callback is set to NULL if the client gets disconnected\n     * by the module itself or because of a timeout, so the callback will NOT\n     * get called if this is not an actual disconnection event. */\n    if (bc->disconnect_callback) {\n        RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n        ctx.blocked_privdata = bc->privdata;\n        ctx.module = bc->module;\n        ctx.client = bc->client;\n        bc->disconnect_callback(&ctx,bc);\n        moduleFreeContext(&ctx);\n    }\n\n    /* If we made it here and client is still blocked it means that the command\n     * timed-out, client was killed or disconnected and disconnect_callback was\n     * not implemented (or it was, but RM_UnblockClient was not called from\n     * within it, as it should).\n     * We must call moduleUnblockClient in order to free privdata and\n     * RedisModuleBlockedClient.\n     *\n     * Note that we only do that for clients that are blocked on keys, for which\n     * the contract is that the module should not call RM_UnblockClient under\n     * normal circumstances.\n     * Clients implementing threads and working with private data should be\n     * aware that calling RM_UnblockClient for every blocked client is their\n     * responsibility, and if they fail to do so memory may leak. Ideally they\n     * should implement the disconnect and timeout callbacks and call\n     * RM_UnblockClient, but any other way is also acceptable. */\n    if (bc->blocked_on_keys && !bc->unblocked)\n        moduleUnblockClient(c);\n\n    bc->client = NULL;\n    /* Reset the client for a new query since, for blocking commands implemented\n     * into modules, we do not it immediately after the command returns (and\n     * the client blocks) in order to be still able to access the argument\n     * vector from callbacks. */\n    resetClient(c);\n}\n\n/* Block a client in the context of a module: this function implements both\n * RM_BlockClient() and RM_BlockClientOnKeys() depending on the fact the\n * keys are passed or not.\n *\n * When not blocking for keys, the keys, numkeys, and privdata parameters are\n * not needed. The privdata in that case must be NULL, since later is\n * RM_UnblockClient() that will provide some private data that the reply\n * callback will receive.\n *\n * Instead when blocking for keys, normally RM_UnblockClient() will not be\n * called (because the client will unblock when the key is modified), so\n * 'privdata' should be provided in that case, so that once the client is\n * unlocked and the reply callback is called, it will receive its associated\n * private data.\n *\n * Even when blocking on keys, RM_UnblockClient() can be called however, but\n * in that case the privdata argument is disregarded, because we pass the\n * reply callback the privdata that is set here while blocking.\n */\nRedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) {\n    client *c = ctx->client;\n    int islua = c->flags & CLIENT_LUA;\n    int ismulti = c->flags & CLIENT_MULTI;\n\n    c->bpop.module_blocked_handle = zmalloc(sizeof(RedisModuleBlockedClient));\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    ctx->module->blocked_clients++;\n\n    /* We need to handle the invalid operation of calling modules blocking\n     * commands from Lua or MULTI. We actually create an already aborted\n     * (client set to NULL) blocked client handle, and actually reply with\n     * an error. */\n    mstime_t timeout = timeout_ms ? (mstime()+timeout_ms) : 0;\n    bc->client = (islua || ismulti) ? NULL : c;\n    bc->module = ctx->module;\n    bc->reply_callback = reply_callback;\n    bc->timeout_callback = timeout_callback;\n    bc->disconnect_callback = NULL; /* Set by RM_SetDisconnectCallback() */\n    bc->free_privdata = free_privdata;\n    bc->privdata = privdata;\n    bc->reply_client = createClient(NULL);\n    bc->reply_client->flags |= CLIENT_MODULE;\n    bc->dbid = c->db->id;\n    bc->blocked_on_keys = keys != NULL;\n    bc->unblocked = 0;\n    c->bpop.timeout = timeout;\n\n    if (islua || ismulti) {\n        c->bpop.module_blocked_handle = NULL;\n        addReplyError(c, islua ?\n            \"Blocking module command called from Lua script\" :\n            \"Blocking module command called from transaction\");\n    } else {\n        if (keys) {\n            blockForKeys(c,BLOCKED_MODULE,keys,numkeys,timeout,NULL,NULL);\n        } else {\n            blockClient(c,BLOCKED_MODULE);\n        }\n    }\n    return bc;\n}\n\n/* This function is called from module.c in order to check if a module\n * blocked for BLOCKED_MODULE and subtype 'on keys' (bc->blocked_on_keys true)\n * can really be unblocked, since the module was able to serve the client.\n * If the callback returns REDISMODULE_OK, then the client can be unblocked,\n * otherwise the client remains blocked and we'll retry again when one of\n * the keys it blocked for becomes \"ready\" again.\n * This function returns 1 if client was served (and should be unblocked) */\nint moduleTryServeClientBlockedOnKey(client *c, robj *key) {\n    int served = 0;\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n\n    /* Protect against re-processing: don't serve clients that are already\n     * in the unblocking list for any reason (including RM_UnblockClient()\n     * explicit call). See #6798. */\n    if (bc->unblocked) return 0;\n\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n    ctx.flags |= REDISMODULE_CTX_BLOCKED_REPLY;\n    ctx.blocked_ready_key = key;\n    ctx.blocked_privdata = bc->privdata;\n    ctx.module = bc->module;\n    ctx.client = bc->client;\n    ctx.blocked_client = bc;\n    if (bc->reply_callback(&ctx,(void**)c->argv,c->argc) == REDISMODULE_OK)\n        served = 1;\n    moduleFreeContext(&ctx);\n    return served;\n}\n\n/* Block a client in the context of a blocking command, returning an handle\n * which will be used, later, in order to unblock the client with a call to\n * RedisModule_UnblockClient(). The arguments specify callback functions\n * and a timeout after which the client is unblocked.\n *\n * The callbacks are called in the following contexts:\n *\n *     reply_callback:  called after a successful RedisModule_UnblockClient()\n *                      call in order to reply to the client and unblock it.\n *\n *     reply_timeout:   called when the timeout is reached in order to send an\n *                      error to the client.\n *\n *     free_privdata:   called in order to free the private data that is passed\n *                      by RedisModule_UnblockClient() call.\n *\n * Note: RedisModule_UnblockClient should be called for every blocked client,\n *       even if client was killed, timed-out or disconnected. Failing to do so\n *       will result in memory leaks.\n */\nRedisModuleBlockedClient *RM_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) {\n    return moduleBlockClient(ctx,reply_callback,timeout_callback,free_privdata,timeout_ms, NULL,0,NULL);\n}\n\n/* This call is similar to RedisModule_BlockClient(), however in this case we\n * don't just block the client, but also ask Redis to unblock it automatically\n * once certain keys become \"ready\", that is, contain more data.\n *\n * Basically this is similar to what a typical Redis command usually does,\n * like BLPOP or ZPOPMAX: the client blocks if it cannot be served ASAP,\n * and later when the key receives new data (a list push for instance), the\n * client is unblocked and served.\n *\n * However in the case of this module API, when the client is unblocked?\n *\n * 1. If you block ok a key of a type that has blocking operations associated,\n *    like a list, a sorted set, a stream, and so forth, the client may be\n *    unblocked once the relevant key is targeted by an operation that normally\n *    unblocks the native blocking operations for that type. So if we block\n *    on a list key, an RPUSH command may unblock our client and so forth.\n * 2. If you are implementing your native data type, or if you want to add new\n *    unblocking conditions in addition to \"1\", you can call the modules API\n *    RedisModule_SignalKeyAsReady().\n *\n * Anyway we can't be sure if the client should be unblocked just because the\n * key is signaled as ready: for instance a successive operation may change the\n * key, or a client in queue before this one can be served, modifying the key\n * as well and making it empty again. So when a client is blocked with\n * RedisModule_BlockClientOnKeys() the reply callback is not called after\n * RM_UnblockCLient() is called, but every time a key is signaled as ready:\n * if the reply callback can serve the client, it returns REDISMODULE_OK\n * and the client is unblocked, otherwise it will return REDISMODULE_ERR\n * and we'll try again later.\n *\n * The reply callback can access the key that was signaled as ready by\n * calling the API RedisModule_GetBlockedClientReadyKey(), that returns\n * just the string name of the key as a RedisModuleString object.\n *\n * Thanks to this system we can setup complex blocking scenarios, like\n * unblocking a client only if a list contains at least 5 items or other\n * more fancy logics.\n *\n * Note that another difference with RedisModule_BlockClient(), is that here\n * we pass the private data directly when blocking the client: it will\n * be accessible later in the reply callback. Normally when blocking with\n * RedisModule_BlockClient() the private data to reply to the client is\n * passed when calling RedisModule_UnblockClient() but here the unblocking\n * is performed by Redis itself, so we need to have some private data before\n * hand. The private data is used to store any information about the specific\n * unblocking operation that you are implementing. Such information will be\n * freed using the free_privdata callback provided by the user.\n *\n * However the reply callback will be able to access the argument vector of\n * the command, so the private data is often not needed.\n *\n * Note: Under normal circumstances RedisModule_UnblockClient should not be\n *       called for clients that are blocked on keys (Either the key will\n *       become ready or a timeout will occur). If for some reason you do want\n *       to call RedisModule_UnblockClient it is possible: Client will be\n *       handled as if it were timed-out (You must implement the timeout\n *       callback in that case).\n */\nRedisModuleBlockedClient *RM_BlockClientOnKeys(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) {\n    return moduleBlockClient(ctx,reply_callback,timeout_callback,free_privdata,timeout_ms, keys,numkeys,privdata);\n}\n\n/* This function is used in order to potentially unblock a client blocked\n * on keys with RedisModule_BlockClientOnKeys(). When this function is called,\n * all the clients blocked for this key will get their reply callback called,\n * and if the callback returns REDISMODULE_OK the client will be unblocked. */\nvoid RM_SignalKeyAsReady(RedisModuleCtx *ctx, RedisModuleString *key) {\n    signalKeyAsReady(ctx->client->db, key);\n}\n\n/* Implements RM_UnblockClient() and moduleUnblockClient(). */\nint moduleUnblockClientByHandle(RedisModuleBlockedClient *bc, void *privdata) {\n    pthread_mutex_lock(&moduleUnblockedClientsMutex);\n    if (!bc->blocked_on_keys) bc->privdata = privdata;\n    bc->unblocked = 1;\n    listAddNodeTail(moduleUnblockedClients,bc);\n    if (write(server.module_blocked_pipe[1],\"A\",1) != 1) {\n        /* Ignore the error, this is best-effort. */\n    }\n    pthread_mutex_unlock(&moduleUnblockedClientsMutex);\n    return REDISMODULE_OK;\n}\n\n/* This API is used by the Redis core to unblock a client that was blocked\n * by a module. */\nvoid moduleUnblockClient(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    moduleUnblockClientByHandle(bc,NULL);\n}\n\n/* Return true if the client 'c' was blocked by a module using\n * RM_BlockClientOnKeys(). */\nint moduleClientIsBlockedOnKeys(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    return bc->blocked_on_keys;\n}\n\n/* Unblock a client blocked by `RedisModule_BlockedClient`. This will trigger\n * the reply callbacks to be called in order to reply to the client.\n * The 'privdata' argument will be accessible by the reply callback, so\n * the caller of this function can pass any value that is needed in order to\n * actually reply to the client.\n *\n * A common usage for 'privdata' is a thread that computes something that\n * needs to be passed to the client, included but not limited some slow\n * to compute reply or some reply obtained via networking.\n *\n * Note 1: this function can be called from threads spawned by the module.\n *\n * Note 2: when we unblock a client that is blocked for keys using\n * the API RedisModule_BlockClientOnKeys(), the privdata argument here is\n * not used, and the reply callback is called with the privdata pointer that\n * was passed when blocking the client.\n *\n * Unblocking a client that was blocked for keys using this API will still\n * require the client to get some reply, so the function will use the\n * \"timeout\" handler in order to do so. */\nint RM_UnblockClient(RedisModuleBlockedClient *bc, void *privdata) {\n    if (bc->blocked_on_keys) {\n        /* In theory the user should always pass the timeout handler as an\n         * argument, but better to be safe than sorry. */\n        if (bc->timeout_callback == NULL) return REDISMODULE_ERR;\n        if (bc->unblocked) return REDISMODULE_OK;\n        if (bc->client) moduleBlockedClientTimedOut(bc->client);\n    }\n    moduleUnblockClientByHandle(bc,privdata);\n    return REDISMODULE_OK;\n}\n\n/* Abort a blocked client blocking operation: the client will be unblocked\n * without firing any callback. */\nint RM_AbortBlock(RedisModuleBlockedClient *bc) {\n    bc->reply_callback = NULL;\n    bc->disconnect_callback = NULL;\n    return RM_UnblockClient(bc,NULL);\n}\n\n/* Set a callback that will be called if a blocked client disconnects\n * before the module has a chance to call RedisModule_UnblockClient()\n *\n * Usually what you want to do there, is to cleanup your module state\n * so that you can call RedisModule_UnblockClient() safely, otherwise\n * the client will remain blocked forever if the timeout is large.\n *\n * Notes:\n *\n * 1. It is not safe to call Reply* family functions here, it is also\n *    useless since the client is gone.\n *\n * 2. This callback is not called if the client disconnects because of\n *    a timeout. In such a case, the client is unblocked automatically\n *    and the timeout callback is called.\n */\nvoid RM_SetDisconnectCallback(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) {\n    bc->disconnect_callback = callback;\n}\n\n/* This function will check the moduleUnblockedClients queue in order to\n * call the reply callback and really unblock the client.\n *\n * Clients end into this list because of calls to RM_UnblockClient(),\n * however it is possible that while the module was doing work for the\n * blocked client, it was terminated by Redis (for timeout or other reasons).\n * When this happens the RedisModuleBlockedClient structure in the queue\n * will have the 'client' field set to NULL. */\nvoid moduleHandleBlockedClients(void) {\n    listNode *ln;\n    RedisModuleBlockedClient *bc;\n\n    pthread_mutex_lock(&moduleUnblockedClientsMutex);\n    /* Here we unblock all the pending clients blocked in modules operations\n     * so we can read every pending \"awake byte\" in the pipe. */\n    char buf[1];\n    while (read(server.module_blocked_pipe[0],buf,1) == 1);\n    while (listLength(moduleUnblockedClients)) {\n        ln = listFirst(moduleUnblockedClients);\n        bc = ln->value;\n        client *c = bc->client;\n        listDelNode(moduleUnblockedClients,ln);\n        pthread_mutex_unlock(&moduleUnblockedClientsMutex);\n\n        /* Release the lock during the loop, as long as we don't\n         * touch the shared list. */\n\n        /* Call the reply callback if the client is valid and we have\n         * any callback. However the callback is not called if the client\n         * was blocked on keys (RM_BlockClientOnKeys()), because we already\n         * called such callback in moduleTryServeClientBlockedOnKey() when\n         * the key was signaled as ready. */\n        if (c && !bc->blocked_on_keys && bc->reply_callback) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.flags |= REDISMODULE_CTX_BLOCKED_REPLY;\n            ctx.blocked_privdata = bc->privdata;\n            ctx.blocked_ready_key = NULL;\n            ctx.module = bc->module;\n            ctx.client = bc->client;\n            ctx.blocked_client = bc;\n            bc->reply_callback(&ctx,(void**)c->argv,c->argc);\n            moduleFreeContext(&ctx);\n        }\n\n        /* Free privdata if any. */\n        if (bc->privdata && bc->free_privdata) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            if (c == NULL)\n                ctx.flags |= REDISMODULE_CTX_BLOCKED_DISCONNECTED;\n            ctx.blocked_privdata = bc->privdata;\n            ctx.module = bc->module;\n            ctx.client = bc->client;\n            bc->free_privdata(&ctx,bc->privdata);\n            moduleFreeContext(&ctx);\n        }\n\n        /* It is possible that this blocked client object accumulated\n         * replies to send to the client in a thread safe context.\n         * We need to glue such replies to the client output buffer and\n         * free the temporary client we just used for the replies. */\n        if (c) AddReplyFromClient(c, bc->reply_client);\n        freeClient(bc->reply_client);\n\n        if (c != NULL) {\n            /* Before unblocking the client, set the disconnect callback\n             * to NULL, because if we reached this point, the client was\n             * properly unblocked by the module. */\n            bc->disconnect_callback = NULL;\n            unblockClient(c);\n            /* Put the client in the list of clients that need to write\n             * if there are pending replies here. This is needed since\n             * during a non blocking command the client may receive output. */\n            if (clientHasPendingReplies(c) &&\n                !(c->flags & CLIENT_PENDING_WRITE))\n            {\n                c->flags |= CLIENT_PENDING_WRITE;\n                listAddNodeHead(server.clients_pending_write,c);\n            }\n        }\n\n        /* Free 'bc' only after unblocking the client, since it is\n         * referenced in the client blocking context, and must be valid\n         * when calling unblockClient(). */\n        bc->module->blocked_clients--;\n        zfree(bc);\n\n        /* Lock again before to iterate the loop. */\n        pthread_mutex_lock(&moduleUnblockedClientsMutex);\n    }\n    pthread_mutex_unlock(&moduleUnblockedClientsMutex);\n}\n\n/* Called when our client timed out. After this function unblockClient()\n * is called, and it will invalidate the blocked client. So this function\n * does not need to do any cleanup. Eventually the module will call the\n * API to unblock the client and the memory will be released. */\nvoid moduleBlockedClientTimedOut(client *c) {\n    RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n    ctx.flags |= REDISMODULE_CTX_BLOCKED_TIMEOUT;\n    ctx.module = bc->module;\n    ctx.client = bc->client;\n    ctx.blocked_client = bc;\n    bc->timeout_callback(&ctx,(void**)c->argv,c->argc);\n    moduleFreeContext(&ctx);\n    /* For timeout events, we do not want to call the disconnect callback,\n     * because the blocked client will be automatically disconnected in\n     * this case, and the user can still hook using the timeout callback. */\n    bc->disconnect_callback = NULL;\n}\n\n/* Return non-zero if a module command was called in order to fill the\n * reply for a blocked client. */\nint RM_IsBlockedReplyRequest(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_BLOCKED_REPLY) != 0;\n}\n\n/* Return non-zero if a module command was called in order to fill the\n * reply for a blocked client that timed out. */\nint RM_IsBlockedTimeoutRequest(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_BLOCKED_TIMEOUT) != 0;\n}\n\n/* Get the private data set by RedisModule_UnblockClient() */\nvoid *RM_GetBlockedClientPrivateData(RedisModuleCtx *ctx) {\n    return ctx->blocked_privdata;\n}\n\n/* Get the key that is ready when the reply callback is called in the context\n * of a client blocked by RedisModule_BlockClientOnKeys(). */\nRedisModuleString *RM_GetBlockedClientReadyKey(RedisModuleCtx *ctx) {\n    return ctx->blocked_ready_key;\n}\n\n/* Get the blocked client associated with a given context.\n * This is useful in the reply and timeout callbacks of blocked clients,\n * before sometimes the module has the blocked client handle references\n * around, and wants to cleanup it. */\nRedisModuleBlockedClient *RM_GetBlockedClientHandle(RedisModuleCtx *ctx) {\n    return ctx->blocked_client;\n}\n\n/* Return true if when the free callback of a blocked client is called,\n * the reason for the client to be unblocked is that it disconnected\n * while it was blocked. */\nint RM_BlockedClientDisconnected(RedisModuleCtx *ctx) {\n    return (ctx->flags & REDISMODULE_CTX_BLOCKED_DISCONNECTED) != 0;\n}\n\n/* --------------------------------------------------------------------------\n * Thread Safe Contexts\n * -------------------------------------------------------------------------- */\n\n/* Return a context which can be used inside threads to make Redis context\n * calls with certain modules APIs. If 'bc' is not NULL then the module will\n * be bound to a blocked client, and it will be possible to use the\n * `RedisModule_Reply*` family of functions to accumulate a reply for when the\n * client will be unblocked. Otherwise the thread safe context will be\n * detached by a specific client.\n *\n * To call non-reply APIs, the thread safe context must be prepared with:\n *\n *     RedisModule_ThreadSafeContextLock(ctx);\n *     ... make your call here ...\n *     RedisModule_ThreadSafeContextUnlock(ctx);\n *\n * This is not needed when using `RedisModule_Reply*` functions, assuming\n * that a blocked client was used when the context was created, otherwise\n * no RedisModule_Reply* call should be made at all.\n *\n * TODO: thread safe contexts do not inherit the blocked client\n * selected database. */\nRedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) {\n    RedisModuleCtx *ctx = zmalloc(sizeof(*ctx));\n    RedisModuleCtx empty = REDISMODULE_CTX_INIT;\n    memcpy(ctx,&empty,sizeof(empty));\n    if (bc) {\n        ctx->blocked_client = bc;\n        ctx->module = bc->module;\n    }\n    ctx->flags |= REDISMODULE_CTX_THREAD_SAFE;\n    /* Even when the context is associated with a blocked client, we can't\n     * access it safely from another thread, so we create a fake client here\n     * in order to keep things like the currently selected database and similar\n     * things. */\n    ctx->client = createClient(NULL);\n    if (bc) {\n        selectDb(ctx->client,bc->dbid);\n        if (bc->client) ctx->client->id = bc->client->id;\n    }\n    return ctx;\n}\n\n/* Release a thread safe context. */\nvoid RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {\n    moduleFreeContext(ctx);\n    zfree(ctx);\n}\n\n/* Acquire the server lock before executing a thread safe API call.\n * This is not needed for `RedisModule_Reply*` calls when there is\n * a blocked client connected to the thread safe context. */\nvoid RM_ThreadSafeContextLock(RedisModuleCtx *ctx) {\n    UNUSED(ctx);\n    moduleAcquireGIL();\n}\n\n/* Release the server lock after a thread safe API call was executed. */\nvoid RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) {\n    UNUSED(ctx);\n    moduleReleaseGIL();\n}\n\nvoid moduleAcquireGIL(void) {\n    pthread_mutex_lock(&moduleGIL);\n}\n\nvoid moduleReleaseGIL(void) {\n    pthread_mutex_unlock(&moduleGIL);\n}\n\n\n/* --------------------------------------------------------------------------\n * Module Keyspace Notifications API\n * -------------------------------------------------------------------------- */\n\n/* Subscribe to keyspace notifications. This is a low-level version of the\n * keyspace-notifications API. A module can register callbacks to be notified\n * when keyspce events occur.\n *\n * Notification events are filtered by their type (string events, set events,\n * etc), and the subscriber callback receives only events that match a specific\n * mask of event types.\n *\n * When subscribing to notifications with RedisModule_SubscribeToKeyspaceEvents \n * the module must provide an event type-mask, denoting the events the subscriber\n * is interested in. This can be an ORed mask of any of the following flags:\n *\n *  - REDISMODULE_NOTIFY_GENERIC: Generic commands like DEL, EXPIRE, RENAME\n *  - REDISMODULE_NOTIFY_STRING: String events\n *  - REDISMODULE_NOTIFY_LIST: List events\n *  - REDISMODULE_NOTIFY_SET: Set events\n *  - REDISMODULE_NOTIFY_HASH: Hash events\n *  - REDISMODULE_NOTIFY_ZSET: Sorted Set events\n *  - REDISMODULE_NOTIFY_EXPIRED: Expiration events\n *  - REDISMODULE_NOTIFY_EVICTED: Eviction events\n *  - REDISMODULE_NOTIFY_STREAM: Stream events\n *  - REDISMODULE_NOTIFY_KEYMISS: Key-miss events\n *  - REDISMODULE_NOTIFY_ALL: All events (Excluding REDISMODULE_NOTIFY_KEYMISS)\n *\n * We do not distinguish between key events and keyspace events, and it is up\n * to the module to filter the actions taken based on the key.\n *\n * The subscriber signature is:\n *\n *   int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type,\n *                                       const char *event,\n *                                       RedisModuleString *key);\n *\n * `type` is the event type bit, that must match the mask given at registration\n * time. The event string is the actual command being executed, and key is the\n * relevant Redis key.\n *\n * Notification callback gets executed with a redis context that can not be\n * used to send anything to the client, and has the db number where the event\n * occurred as its selected db number.\n *\n * Notice that it is not necessary to enable notifications in redis.conf for\n * module notifications to work.\n *\n * Warning: the notification callbacks are performed in a synchronous manner,\n * so notification callbacks must to be fast, or they would slow Redis down.\n * If you need to take long actions, use threads to offload them.\n *\n * See https://redis.io/topics/notifications for more information.\n */\nint RM_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc callback) {\n    RedisModuleKeyspaceSubscriber *sub = zmalloc(sizeof(*sub));\n    sub->module = ctx->module;\n    sub->event_mask = types;\n    sub->notify_callback = callback;\n    sub->active = 0;\n\n    listAddNodeTail(moduleKeyspaceSubscribers, sub);\n    return REDISMODULE_OK;\n}\n\n/* Get the configured bitmap of notify-keyspace-events (Could be used\n * for additional filtering in RedisModuleNotificationFunc) */\nint RM_GetNotifyKeyspaceEvents() {\n    return server.notify_keyspace_events;\n}\n\n/* Expose notifyKeyspaceEvent to modules */\nint RM_NotifyKeyspaceEvent(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) {\n    if (!ctx || !ctx->client)\n        return REDISMODULE_ERR;\n    notifyKeyspaceEvent(type, (char *)event, key, ctx->client->db->id);\n    return REDISMODULE_OK;\n}\n\n/* Dispatcher for keyspace notifications to module subscriber functions.\n * This gets called  only if at least one module requested to be notified on\n * keyspace notifications */\nvoid moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid) {\n    /* Don't do anything if there aren't any subscribers */\n    if (listLength(moduleKeyspaceSubscribers) == 0) return;\n\n    listIter li;\n    listNode *ln;\n    listRewind(moduleKeyspaceSubscribers,&li);\n\n    /* Remove irrelevant flags from the type mask */\n    type &= ~(NOTIFY_KEYEVENT | NOTIFY_KEYSPACE);\n\n    while((ln = listNext(&li))) {\n        RedisModuleKeyspaceSubscriber *sub = ln->value;\n        /* Only notify subscribers on events matching they registration,\n         * and avoid subscribers triggering themselves */\n        if ((sub->event_mask & type) && sub->active == 0) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.module = sub->module;\n            ctx.client = moduleFreeContextReusedClient;\n            selectDb(ctx.client, dbid);\n\n            /* mark the handler as active to avoid reentrant loops.\n             * If the subscriber performs an action triggering itself,\n             * it will not be notified about it. */\n            sub->active = 1;\n            sub->notify_callback(&ctx, type, event, key);\n            sub->active = 0;\n            moduleFreeContext(&ctx);\n        }\n    }\n}\n\n/* Unsubscribe any notification subscribers this module has upon unloading */\nvoid moduleUnsubscribeNotifications(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    listRewind(moduleKeyspaceSubscribers,&li);\n    while((ln = listNext(&li))) {\n        RedisModuleKeyspaceSubscriber *sub = ln->value;\n        if (sub->module == module) {\n            listDelNode(moduleKeyspaceSubscribers, ln);\n            zfree(sub);\n        }\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Modules Cluster API\n * -------------------------------------------------------------------------- */\n\n/* The Cluster message callback function pointer type. */\ntypedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len);\n\n/* This structure identifies a registered caller: it must match a given module\n * ID, for a given message type. The callback function is just the function\n * that was registered as receiver. */\ntypedef struct moduleClusterReceiver {\n    uint64_t module_id;\n    RedisModuleClusterMessageReceiver callback;\n    struct RedisModule *module;\n    struct moduleClusterReceiver *next;\n} moduleClusterReceiver;\n\ntypedef struct moduleClusterNodeInfo {\n    int flags;\n    char ip[NET_IP_STR_LEN];\n    int port;\n    char master_id[40]; /* Only if flags & REDISMODULE_NODE_MASTER is true. */\n} mdouleClusterNodeInfo;\n\n/* We have an array of message types: each bucket is a linked list of\n * configured receivers. */\nstatic moduleClusterReceiver *clusterReceivers[UINT8_MAX];\n\n/* Dispatch the message to the right module receiver. */\nvoid moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len) {\n    moduleClusterReceiver *r = clusterReceivers[type];\n    while(r) {\n        if (r->module_id == module_id) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.module = r->module;\n            ctx.client = moduleFreeContextReusedClient;\n            selectDb(ctx.client, 0);\n            r->callback(&ctx,sender_id,type,payload,len);\n            moduleFreeContext(&ctx);\n            return;\n        }\n        r = r->next;\n    }\n}\n\n/* Register a callback receiver for cluster messages of type 'type'. If there\n * was already a registered callback, this will replace the callback function\n * with the one provided, otherwise if the callback is set to NULL and there\n * is already a callback for this function, the callback is unregistered\n * (so this API call is also used in order to delete the receiver). */\nvoid RM_RegisterClusterMessageReceiver(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) {\n    if (!server.cluster_enabled) return;\n\n    uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);\n    moduleClusterReceiver *r = clusterReceivers[type], *prev = NULL;\n    while(r) {\n        if (r->module_id == module_id) {\n            /* Found! Set or delete. */\n            if (callback) {\n                r->callback = callback;\n            } else {\n                /* Delete the receiver entry if the user is setting\n                 * it to NULL. Just unlink the receiver node from the\n                 * linked list. */\n                if (prev)\n                    prev->next = r->next;\n                else\n                    clusterReceivers[type]->next = r->next;\n                zfree(r);\n            }\n            return;\n        }\n        prev = r;\n        r = r->next;\n    }\n\n    /* Not found, let's add it. */\n    if (callback) {\n        r = zmalloc(sizeof(*r));\n        r->module_id = module_id;\n        r->module = ctx->module;\n        r->callback = callback;\n        r->next = clusterReceivers[type];\n        clusterReceivers[type] = r;\n    }\n}\n\n/* Send a message to all the nodes in the cluster if `target` is NULL, otherwise\n * at the specified target, which is a REDISMODULE_NODE_ID_LEN bytes node ID, as\n * returned by the receiver callback or by the nodes iteration functions.\n *\n * The function returns REDISMODULE_OK if the message was successfully sent,\n * otherwise if the node is not connected or such node ID does not map to any\n * known cluster node, REDISMODULE_ERR is returned. */\nint RM_SendClusterMessage(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len) {\n    if (!server.cluster_enabled) return REDISMODULE_ERR;\n    uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);\n    if (clusterSendModuleMessageToTarget(target_id,module_id,type,msg,len) == C_OK)\n        return REDISMODULE_OK;\n    else\n        return REDISMODULE_ERR;\n}\n\n/* Return an array of string pointers, each string pointer points to a cluster\n * node ID of exactly REDISMODULE_NODE_ID_SIZE bytes (without any null term).\n * The number of returned node IDs is stored into `*numnodes`.\n * However if this function is called by a module not running an a Redis\n * instance with Redis Cluster enabled, NULL is returned instead.\n *\n * The IDs returned can be used with RedisModule_GetClusterNodeInfo() in order\n * to get more information about single nodes.\n *\n * The array returned by this function must be freed using the function\n * RedisModule_FreeClusterNodesList().\n *\n * Example:\n *\n *     size_t count, j;\n *     char **ids = RedisModule_GetClusterNodesList(ctx,&count);\n *     for (j = 0; j < count; j++) {\n *         RedisModule_Log(\"notice\",\"Node %.*s\",\n *             REDISMODULE_NODE_ID_LEN,ids[j]);\n *     }\n *     RedisModule_FreeClusterNodesList(ids);\n */\nchar **RM_GetClusterNodesList(RedisModuleCtx *ctx, size_t *numnodes) {\n    UNUSED(ctx);\n\n    if (!server.cluster_enabled) return NULL;\n    size_t count = dictSize(server.cluster->nodes);\n    char **ids = zmalloc((count+1)*REDISMODULE_NODE_ID_LEN);\n    dictIterator *di = dictGetIterator(server.cluster->nodes);\n    dictEntry *de;\n    int j = 0;\n    while((de = dictNext(di)) != NULL) {\n        clusterNode *node = dictGetVal(de);\n        if (node->flags & (CLUSTER_NODE_NOADDR|CLUSTER_NODE_HANDSHAKE)) continue;\n        ids[j] = zmalloc(REDISMODULE_NODE_ID_LEN);\n        memcpy(ids[j],node->name,REDISMODULE_NODE_ID_LEN);\n        j++;\n    }\n    *numnodes = j;\n    ids[j] = NULL; /* Null term so that FreeClusterNodesList does not need\n                    * to also get the count argument. */\n    dictReleaseIterator(di);\n    return ids;\n}\n\n/* Free the node list obtained with RedisModule_GetClusterNodesList. */\nvoid RM_FreeClusterNodesList(char **ids) {\n    if (ids == NULL) return;\n    for (int j = 0; ids[j]; j++) zfree(ids[j]);\n    zfree(ids);\n}\n\n/* Return this node ID (REDISMODULE_CLUSTER_ID_LEN bytes) or NULL if the cluster\n * is disabled. */\nconst char *RM_GetMyClusterID(void) {\n    if (!server.cluster_enabled) return NULL;\n    return server.cluster->myself->name;\n}\n\n/* Return the number of nodes in the cluster, regardless of their state\n * (handshake, noaddress, ...) so that the number of active nodes may actually\n * be smaller, but not greater than this number. If the instance is not in\n * cluster mode, zero is returned. */\nsize_t RM_GetClusterSize(void) {\n    if (!server.cluster_enabled) return 0;\n    return dictSize(server.cluster->nodes);\n}\n\n/* Populate the specified info for the node having as ID the specified 'id',\n * then returns REDISMODULE_OK. Otherwise if the node ID does not exist from\n * the POV of this local node, REDISMODULE_ERR is returned.\n *\n * The arguments ip, master_id, port and flags can be NULL in case we don't\n * need to populate back certain info. If an ip and master_id (only populated\n * if the instance is a slave) are specified, they point to buffers holding\n * at least REDISMODULE_NODE_ID_LEN bytes. The strings written back as ip\n * and master_id are not null terminated.\n *\n * The list of flags reported is the following:\n *\n * * REDISMODULE_NODE_MYSELF        This node\n * * REDISMODULE_NODE_MASTER        The node is a master\n * * REDISMODULE_NODE_SLAVE         The node is a replica\n * * REDISMODULE_NODE_PFAIL         We see the node as failing\n * * REDISMODULE_NODE_FAIL          The cluster agrees the node is failing\n * * REDISMODULE_NODE_NOFAILOVER    The slave is configured to never failover\n */\n\nclusterNode *clusterLookupNode(const char *name); /* We need access to internals */\n\nint RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) {\n    UNUSED(ctx);\n\n    clusterNode *node = clusterLookupNode(id);\n    if (node == NULL ||\n        node->flags & (CLUSTER_NODE_NOADDR|CLUSTER_NODE_HANDSHAKE))\n    {\n        return REDISMODULE_ERR;\n    }\n\n    if (ip) strncpy(ip,node->ip,NET_IP_STR_LEN);\n\n    if (master_id) {\n        /* If the information is not available, the function will set the\n         * field to zero bytes, so that when the field can't be populated the\n         * function kinda remains predictable. */\n        if (node->flags & CLUSTER_NODE_MASTER && node->slaveof)\n            memcpy(master_id,node->slaveof->name,REDISMODULE_NODE_ID_LEN);\n        else\n            memset(master_id,0,REDISMODULE_NODE_ID_LEN);\n    }\n    if (port) *port = node->port;\n\n    /* As usually we have to remap flags for modules, in order to ensure\n     * we can provide binary compatibility. */\n    if (flags) {\n        *flags = 0;\n        if (node->flags & CLUSTER_NODE_MYSELF) *flags |= REDISMODULE_NODE_MYSELF;\n        if (node->flags & CLUSTER_NODE_MASTER) *flags |= REDISMODULE_NODE_MASTER;\n        if (node->flags & CLUSTER_NODE_SLAVE) *flags |= REDISMODULE_NODE_SLAVE;\n        if (node->flags & CLUSTER_NODE_PFAIL) *flags |= REDISMODULE_NODE_PFAIL;\n        if (node->flags & CLUSTER_NODE_FAIL) *flags |= REDISMODULE_NODE_FAIL;\n        if (node->flags & CLUSTER_NODE_NOFAILOVER) *flags |= REDISMODULE_NODE_NOFAILOVER;\n    }\n    return REDISMODULE_OK;\n}\n\n/* Set Redis Cluster flags in order to change the normal behavior of\n * Redis Cluster, especially with the goal of disabling certain functions.\n * This is useful for modules that use the Cluster API in order to create\n * a different distributed system, but still want to use the Redis Cluster\n * message bus. Flags that can be set:\n *\n *  CLUSTER_MODULE_FLAG_NO_FAILOVER\n *  CLUSTER_MODULE_FLAG_NO_REDIRECTION\n *\n * With the following effects:\n *\n *  NO_FAILOVER: prevent Redis Cluster slaves to failover a failing master.\n *               Also disables the replica migration feature.\n *\n *  NO_REDIRECTION: Every node will accept any key, without trying to perform\n *                  partitioning according to the user Redis Cluster algorithm.\n *                  Slots informations will still be propagated across the\n *                  cluster, but without effects. */\nvoid RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) {\n    UNUSED(ctx);\n    if (flags & REDISMODULE_CLUSTER_FLAG_NO_FAILOVER)\n        server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_FAILOVER;\n    if (flags & REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION)\n        server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_REDIRECTION;\n}\n\n/* --------------------------------------------------------------------------\n * Modules Timers API\n *\n * Module timers are an high precision \"green timers\" abstraction where\n * every module can register even millions of timers without problems, even if\n * the actual event loop will just have a single timer that is used to awake the\n * module timers subsystem in order to process the next event.\n *\n * All the timers are stored into a radix tree, ordered by expire time, when\n * the main Redis event loop timer callback is called, we try to process all\n * the timers already expired one after the other. Then we re-enter the event\n * loop registering a timer that will expire when the next to process module\n * timer will expire.\n *\n * Every time the list of active timers drops to zero, we unregister the\n * main event loop timer, so that there is no overhead when such feature is\n * not used.\n * -------------------------------------------------------------------------- */\n\nstatic rax *Timers;     /* The radix tree of all the timers sorted by expire. */\nlong long aeTimer = -1; /* Main event loop (ae.c) timer identifier. */\n\ntypedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);\n\n/* The timer descriptor, stored as value in the radix tree. */\ntypedef struct RedisModuleTimer {\n    RedisModule *module;                /* Module reference. */\n    RedisModuleTimerProc callback;      /* The callback to invoke on expire. */\n    void *data;                         /* Private data for the callback. */\n    int dbid;                           /* Database number selected by the original client. */\n} RedisModuleTimer;\n\n/* This is the timer handler that is called by the main event loop. We schedule\n * this timer to be called when the nearest of our module timers will expire. */\nint moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n\n    /* To start let's try to fire all the timers already expired. */\n    raxIterator ri;\n    raxStart(&ri,Timers);\n    uint64_t now = ustime();\n    long long next_period = 0;\n    while(1) {\n        raxSeek(&ri,\"^\",NULL,0);\n        if (!raxNext(&ri)) break;\n        uint64_t expiretime;\n        memcpy(&expiretime,ri.key,sizeof(expiretime));\n        expiretime = ntohu64(expiretime);\n        if (now >= expiretime) {\n            RedisModuleTimer *timer = ri.data;\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n\n            ctx.module = timer->module;\n            ctx.client = moduleFreeContextReusedClient;\n            selectDb(ctx.client, timer->dbid);\n            timer->callback(&ctx,timer->data);\n            moduleFreeContext(&ctx);\n            raxRemove(Timers,(unsigned char*)ri.key,ri.key_len,NULL);\n            zfree(timer);\n        } else {\n            next_period = (expiretime-now)/1000; /* Scale to milliseconds. */\n            break;\n        }\n    }\n    raxStop(&ri);\n\n    /* Reschedule the next timer or cancel it. */\n    if (next_period <= 0) next_period = 1;\n    return (raxSize(Timers) > 0) ? next_period : AE_NOMORE;\n}\n\n/* Create a new timer that will fire after `period` milliseconds, and will call\n * the specified function using `data` as argument. The returned timer ID can be\n * used to get information from the timer or to stop it before it fires. */\nRedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) {\n    RedisModuleTimer *timer = zmalloc(sizeof(*timer));\n    timer->module = ctx->module;\n    timer->callback = callback;\n    timer->data = data;\n    timer->dbid = ctx->client ? ctx->client->db->id : 0;\n    uint64_t expiretime = ustime()+period*1000;\n    uint64_t key;\n\n    while(1) {\n        key = htonu64(expiretime);\n        if (raxFind(Timers, (unsigned char*)&key,sizeof(key)) == raxNotFound) {\n            raxInsert(Timers,(unsigned char*)&key,sizeof(key),timer,NULL);\n            break;\n        } else {\n            expiretime++;\n        }\n    }\n\n    /* We need to install the main event loop timer if it's not already\n     * installed, or we may need to refresh its period if we just installed\n     * a timer that will expire sooner than any other else. */\n    if (aeTimer != -1) {\n        raxIterator ri;\n        raxStart(&ri,Timers);\n        raxSeek(&ri,\"^\",NULL,0);\n        raxNext(&ri);\n        if (memcmp(ri.key,&key,sizeof(key)) == 0) {\n            /* This is the first key, we need to re-install the timer according\n             * to the just added event. */\n            aeDeleteTimeEvent(server.el,aeTimer);\n            aeTimer = -1;\n        }\n        raxStop(&ri);\n    }\n\n    /* If we have no main timer (the old one was invalidated, or this is the\n     * first module timer we have), install one. */\n    if (aeTimer == -1)\n        aeTimer = aeCreateTimeEvent(server.el,period,moduleTimerHandler,NULL,NULL);\n\n    return key;\n}\n\n/* Stop a timer, returns REDISMODULE_OK if the timer was found, belonged to the\n * calling module, and was stopped, otherwise REDISMODULE_ERR is returned.\n * If not NULL, the data pointer is set to the value of the data argument when\n * the timer was created. */\nint RM_StopTimer(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) {\n    RedisModuleTimer *timer = raxFind(Timers,(unsigned char*)&id,sizeof(id));\n    if (timer == raxNotFound || timer->module != ctx->module)\n        return REDISMODULE_ERR;\n    if (data) *data = timer->data;\n    raxRemove(Timers,(unsigned char*)&id,sizeof(id),NULL);\n    zfree(timer);\n    return REDISMODULE_OK;\n}\n\n/* Obtain information about a timer: its remaining time before firing\n * (in milliseconds), and the private data pointer associated with the timer.\n * If the timer specified does not exist or belongs to a different module\n * no information is returned and the function returns REDISMODULE_ERR, otherwise\n * REDISMODULE_OK is returned. The arguments remaining or data can be NULL if\n * the caller does not need certain information. */\nint RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) {\n    RedisModuleTimer *timer = raxFind(Timers,(unsigned char*)&id,sizeof(id));\n    if (timer == raxNotFound || timer->module != ctx->module)\n        return REDISMODULE_ERR;\n    if (remaining) {\n        int64_t rem = ntohu64(id)-ustime();\n        if (rem < 0) rem = 0;\n        *remaining = rem/1000; /* Scale to milliseconds. */\n    }\n    if (data) *data = timer->data;\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Modules ACL API\n *\n * Implements a hook into the authentication and authorization within Redis. \n * --------------------------------------------------------------------------*/\n\n/* This function is called when a client's user has changed and invokes the\n * client's user changed callback if it was set. This callback should\n * cleanup any state the module was tracking about this client. \n * \n * A client's user can be changed through the AUTH command, module \n * authentication, and when a client is freed. */\nvoid moduleNotifyUserChanged(client *c) {\n    if (c->auth_callback) {\n        c->auth_callback(c->id, c->auth_callback_privdata);\n\n        /* The callback will fire exactly once, even if the user remains \n         * the same. It is expected to completely clean up the state\n         * so all references are cleared here. */\n        c->auth_callback = NULL;\n        c->auth_callback_privdata = NULL;\n        c->auth_module = NULL;\n    }\n}\n\nvoid revokeClientAuthentication(client *c) {\n    /* Freeing the client would result in moduleNotifyUserChanged() to be\n     * called later, however since we use revokeClientAuthentication() also\n     * in moduleFreeAuthenticatedClients() to implement module unloading, we\n     * do this action ASAP: this way if the module is unloaded, when the client\n     * is eventually freed we don't rely on the module to still exist. */\n    moduleNotifyUserChanged(c);\n\n    c->user = DefaultUser;\n    c->authenticated = 0;\n    freeClientAsync(c);\n}\n\n/* Cleanup all clients that have been authenticated with this module. This\n * is called from onUnload() to give the module a chance to cleanup any\n * resources associated with clients it has authenticated. */\nstatic void moduleFreeAuthenticatedClients(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client *c = listNodeValue(ln);\n        if (!c->auth_module) continue;\n\n        RedisModule *auth_module = (RedisModule *) c->auth_module;\n        if (auth_module == module) { \n            revokeClientAuthentication(c);\n        }\n    }\n}\n\n/* Creates a Redis ACL user that the module can use to authenticate a client.\n * After obtaining the user, the module should set what such user can do\n * using the RM_SetUserACL() function. Once configured, the user\n * can be used in order to authenticate a connection, with the specified\n * ACL rules, using the RedisModule_AuthClientWithUser() function.\n *\n * Note that:\n *\n * * Users created here are not listed by the ACL command.\n * * Users created here are not checked for duplicated name, so it's up to\n *   the module calling this function to take care of not creating users\n *   with the same name.\n * * The created user can be used to authenticate multiple Redis connections.\n *\n * The caller can later free the user using the function\n * RM_FreeModuleUser(). When this function is called, if there are\n * still clients authenticated with this user, they are disconnected.\n * The function to free the user should only be used when the caller really\n * wants to invalidate the user to define a new one with different\n * capabilities. */\nRedisModuleUser *RM_CreateModuleUser(const char *name) {\n    RedisModuleUser *new_user = zmalloc(sizeof(RedisModuleUser));\n    new_user->user = ACLCreateUnlinkedUser();\n\n    /* Free the previous temporarily assigned name to assign the new one */\n    sdsfree(new_user->user->name);\n    new_user->user->name = sdsnew(name);\n    return new_user;\n}\n\n/* Frees a given user and disconnects all of the clients that have been\n * authenticated with it. See RM_CreateModuleUser for detailed usage.*/\nint RM_FreeModuleUser(RedisModuleUser *user) {\n    ACLFreeUserAndKillClients(user->user);\n    zfree(user);\n    return REDISMODULE_OK;\n}\n\n/* Sets the permissions of a user created through the redis module \n * interface. The syntax is the same as ACL SETUSER, so refer to the \n * documentation in acl.c for more information. See RM_CreateModuleUser\n * for detailed usage.\n * \n * Returns REDISMODULE_OK on success and REDISMODULE_ERR on failure\n * and will set an errno describing why the operation failed. */\nint RM_SetModuleUserACL(RedisModuleUser *user, const char* acl) {\n    return ACLSetUser(user->user, acl, -1);\n}\n\n/* Authenticate the client associated with the context with \n * the provided user. Returns REDISMODULE_OK on success and\n * REDISMODULE_ERR on error.\n * \n * This authentication can be tracked with the optional callback and private\n * data fields. The callback will be called whenever the user of the client\n * changes. This callback should be used to cleanup any state that is being\n * kept in the module related to the client authentication. It will only be\n * called once, even when the user hasn't changed, in order to allow for a\n * new callback to be specified. If this authentication does not need to be\n * tracked, pass in NULL for the callback and privdata. \n * \n * If client_id is not NULL, it will be filled with the id of the client\n * that was authenticated. This can be used with the \n * RM_DeauthenticateAndCloseClient() API in order to deauthenticate a \n * previously authenticated client if the authentication is no longer valid. \n * \n * For expensive authentication operations, it is recommended to block the\n * client and do the authentication in the background and then attach the user\n * to the client in a threadsafe context.  */\nstatic int authenticateClientWithUser(RedisModuleCtx *ctx, user *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) {\n    if (user->flags & USER_FLAG_DISABLED) {\n        return REDISMODULE_ERR;\n    }\n\n    moduleNotifyUserChanged(ctx->client);\n\n    ctx->client->user = user;\n    ctx->client->authenticated = 1;\n\n    if (callback) {\n        ctx->client->auth_callback = callback;\n        ctx->client->auth_callback_privdata = privdata;\n        ctx->client->auth_module = ctx->module;\n    }\n\n    if (client_id) {\n        *client_id = ctx->client->id;\n    }\n\n    return REDISMODULE_OK;\n}\n\n\n/* Authenticate the current context's user with the provided redis acl user. \n * Returns REDISMODULE_ERR if the user is disabled.\n * \n * See authenticateClientWithUser for information about callback, client_id,\n * and general usage for authentication. */\nint RM_AuthenticateClientWithUser(RedisModuleCtx *ctx, RedisModuleUser *module_user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) {\n    return authenticateClientWithUser(ctx, module_user->user, callback, privdata, client_id);\n}\n\n/* Authenticate the current context's user with the provided redis acl user. \n * Returns REDISMODULE_ERR if the user is disabled or the user does not exist.\n * \n * See authenticateClientWithUser for information about callback, client_id,\n * and general usage for authentication. */\nint RM_AuthenticateClientWithACLUser(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) {\n    user *acl_user = ACLGetUserByName(name, len);\n\n    if (!acl_user) {\n        return REDISMODULE_ERR;\n    }\n    return authenticateClientWithUser(ctx, acl_user, callback, privdata, client_id);\n}\n\n/* Deauthenticate and close the client. The client resources will not be\n * be immediately freed, but will be cleaned up in a background job. This is \n * the recommended way to deauthenicate a client since most clients can't \n * handle users becomming deauthenticated. Returns REDISMODULE_ERR when the \n * client doesn't exist and REDISMODULE_OK when the operation was successful. \n * \n * The client ID is returned from the RM_AuthenticateClientWithUser and\n * RM_AuthenticateClientWithACLUser APIs, but can be obtained through\n * the CLIENT api or through server events. \n * \n * This function is not thread safe, and must be executed within the context\n * of a command or thread safe context. */\nint RM_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id) {\n    UNUSED(ctx);\n    client *c = lookupClientByID(client_id);\n    if (c == NULL) return REDISMODULE_ERR;\n\n    /* Revoke also marks client to be closed ASAP */\n    revokeClientAuthentication(c);\n    return REDISMODULE_OK;\n}\n\n/* --------------------------------------------------------------------------\n * Modules Dictionary API\n *\n * Implements a sorted dictionary (actually backed by a radix tree) with\n * the usual get / set / del / num-items API, together with an iterator\n * capable of going back and forth.\n * -------------------------------------------------------------------------- */\n\n/* Create a new dictionary. The 'ctx' pointer can be the current module context\n * or NULL, depending on what you want. Please follow the following rules:\n *\n * 1. Use a NULL context if you plan to retain a reference to this dictionary\n *    that will survive the time of the module callback where you created it.\n * 2. Use a NULL context if no context is available at the time you are creating\n *    the dictionary (of course...).\n * 3. However use the current callback context as 'ctx' argument if the\n *    dictionary time to live is just limited to the callback scope. In this\n *    case, if enabled, you can enjoy the automatic memory management that will\n *    reclaim the dictionary memory, as well as the strings returned by the\n *    Next / Prev dictionary iterator calls.\n */\nRedisModuleDict *RM_CreateDict(RedisModuleCtx *ctx) {\n    struct RedisModuleDict *d = zmalloc(sizeof(*d));\n    d->rax = raxNew();\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_DICT,d);\n    return d;\n}\n\n/* Free a dictionary created with RM_CreateDict(). You need to pass the\n * context pointer 'ctx' only if the dictionary was created using the\n * context instead of passing NULL. */\nvoid RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d) {\n    if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_DICT,d);\n    raxFree(d->rax);\n    zfree(d);\n}\n\n/* Return the size of the dictionary (number of keys). */\nuint64_t RM_DictSize(RedisModuleDict *d) {\n    return raxSize(d->rax);\n}\n\n/* Store the specified key into the dictionary, setting its value to the\n * pointer 'ptr'. If the key was added with success, since it did not\n * already exist, REDISMODULE_OK is returned. Otherwise if the key already\n * exists the function returns REDISMODULE_ERR. */\nint RM_DictSetC(RedisModuleDict *d, void *key, size_t keylen, void *ptr) {\n    int retval = raxTryInsert(d->rax,key,keylen,ptr,NULL);\n    return (retval == 1) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictSetC() but will replace the key with the new\n * value if the key already exists. */\nint RM_DictReplaceC(RedisModuleDict *d, void *key, size_t keylen, void *ptr) {\n    int retval = raxInsert(d->rax,key,keylen,ptr,NULL);\n    return (retval == 1) ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictSetC() but takes the key as a RedisModuleString. */\nint RM_DictSet(RedisModuleDict *d, RedisModuleString *key, void *ptr) {\n    return RM_DictSetC(d,key->ptr,sdslen(key->ptr),ptr);\n}\n\n/* Like RedisModule_DictReplaceC() but takes the key as a RedisModuleString. */\nint RM_DictReplace(RedisModuleDict *d, RedisModuleString *key, void *ptr) {\n    return RM_DictReplaceC(d,key->ptr,sdslen(key->ptr),ptr);\n}\n\n/* Return the value stored at the specified key. The function returns NULL\n * both in the case the key does not exist, or if you actually stored\n * NULL at key. So, optionally, if the 'nokey' pointer is not NULL, it will\n * be set by reference to 1 if the key does not exist, or to 0 if the key\n * exists. */\nvoid *RM_DictGetC(RedisModuleDict *d, void *key, size_t keylen, int *nokey) {\n    void *res = raxFind(d->rax,key,keylen);\n    if (nokey) *nokey = (res == raxNotFound);\n    return (res == raxNotFound) ? NULL : res;\n}\n\n/* Like RedisModule_DictGetC() but takes the key as a RedisModuleString. */\nvoid *RM_DictGet(RedisModuleDict *d, RedisModuleString *key, int *nokey) {\n    return RM_DictGetC(d,key->ptr,sdslen(key->ptr),nokey);\n}\n\n/* Remove the specified key from the dictionary, returning REDISMODULE_OK if\n * the key was found and delted, or REDISMODULE_ERR if instead there was\n * no such key in the dictionary. When the operation is successful, if\n * 'oldval' is not NULL, then '*oldval' is set to the value stored at the\n * key before it was deleted. Using this feature it is possible to get\n * a pointer to the value (for instance in order to release it), without\n * having to call RedisModule_DictGet() before deleting the key. */\nint RM_DictDelC(RedisModuleDict *d, void *key, size_t keylen, void *oldval) {\n    int retval = raxRemove(d->rax,key,keylen,oldval);\n    return retval ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictDelC() but gets the key as a RedisModuleString. */\nint RM_DictDel(RedisModuleDict *d, RedisModuleString *key, void *oldval) {\n    return RM_DictDelC(d,key->ptr,sdslen(key->ptr),oldval);\n}\n\n/* Return an interator, setup in order to start iterating from the specified\n * key by applying the operator 'op', which is just a string specifying the\n * comparison operator to use in order to seek the first element. The\n * operators avalable are:\n *\n * \"^\"   -- Seek the first (lexicographically smaller) key.\n * \"$\"   -- Seek the last  (lexicographically biffer) key.\n * \">\"   -- Seek the first element greter than the specified key.\n * \">=\"  -- Seek the first element greater or equal than the specified key.\n * \"<\"   -- Seek the first element smaller than the specified key.\n * \"<=\"  -- Seek the first element smaller or equal than the specified key.\n * \"==\"  -- Seek the first element matching exactly the specified key.\n *\n * Note that for \"^\" and \"$\" the passed key is not used, and the user may\n * just pass NULL with a length of 0.\n *\n * If the element to start the iteration cannot be seeked based on the\n * key and operator passed, RedisModule_DictNext() / Prev() will just return\n * REDISMODULE_ERR at the first call, otherwise they'll produce elements.\n */\nRedisModuleDictIter *RM_DictIteratorStartC(RedisModuleDict *d, const char *op, void *key, size_t keylen) {\n    RedisModuleDictIter *di = zmalloc(sizeof(*di));\n    di->dict = d;\n    raxStart(&di->ri,d->rax);\n    raxSeek(&di->ri,op,key,keylen);\n    return di;\n}\n\n/* Exactly like RedisModule_DictIteratorStartC, but the key is passed as a\n * RedisModuleString. */\nRedisModuleDictIter *RM_DictIteratorStart(RedisModuleDict *d, const char *op, RedisModuleString *key) {\n    return RM_DictIteratorStartC(d,op,key->ptr,sdslen(key->ptr));\n}\n\n/* Release the iterator created with RedisModule_DictIteratorStart(). This call\n * is mandatory otherwise a memory leak is introduced in the module. */\nvoid RM_DictIteratorStop(RedisModuleDictIter *di) {\n    raxStop(&di->ri);\n    zfree(di);\n}\n\n/* After its creation with RedisModule_DictIteratorStart(), it is possible to\n * change the currently selected element of the iterator by using this\n * API call. The result based on the operator and key is exactly like\n * the function RedisModule_DictIteratorStart(), however in this case the\n * return value is just REDISMODULE_OK in case the seeked element was found,\n * or REDISMODULE_ERR in case it was not possible to seek the specified\n * element. It is possible to reseek an iterator as many times as you want. */\nint RM_DictIteratorReseekC(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) {\n    return raxSeek(&di->ri,op,key,keylen);\n}\n\n/* Like RedisModule_DictIteratorReseekC() but takes the key as as a\n * RedisModuleString. */\nint RM_DictIteratorReseek(RedisModuleDictIter *di, const char *op, RedisModuleString *key) {\n    return RM_DictIteratorReseekC(di,op,key->ptr,sdslen(key->ptr));\n}\n\n/* Return the current item of the dictionary iterator 'di' and steps to the\n * next element. If the iterator already yield the last element and there\n * are no other elements to return, NULL is returned, otherwise a pointer\n * to a string representing the key is provided, and the '*keylen' length\n * is set by reference (if keylen is not NULL). The '*dataptr', if not NULL\n * is set to the value of the pointer stored at the returned key as auxiliary\n * data (as set by the RedisModule_DictSet API).\n *\n * Usage example:\n *\n *      ... create the iterator here ...\n *      char *key;\n *      void *data;\n *      while((key = RedisModule_DictNextC(iter,&keylen,&data)) != NULL) {\n *          printf(\"%.*s %p\\n\", (int)keylen, key, data);\n *      }\n *\n * The returned pointer is of type void because sometimes it makes sense\n * to cast it to a char* sometimes to an unsigned char* depending on the\n * fact it contains or not binary data, so this API ends being more\n * comfortable to use.\n *\n * The validity of the returned pointer is until the next call to the\n * next/prev iterator step. Also the pointer is no longer valid once the\n * iterator is released. */\nvoid *RM_DictNextC(RedisModuleDictIter *di, size_t *keylen, void **dataptr) {\n    if (!raxNext(&di->ri)) return NULL;\n    if (keylen) *keylen = di->ri.key_len;\n    if (dataptr) *dataptr = di->ri.data;\n    return di->ri.key;\n}\n\n/* This function is exactly like RedisModule_DictNext() but after returning\n * the currently selected element in the iterator, it selects the previous\n * element (laxicographically smaller) instead of the next one. */\nvoid *RM_DictPrevC(RedisModuleDictIter *di, size_t *keylen, void **dataptr) {\n    if (!raxPrev(&di->ri)) return NULL;\n    if (keylen) *keylen = di->ri.key_len;\n    if (dataptr) *dataptr = di->ri.data;\n    return di->ri.key;\n}\n\n/* Like RedisModuleNextC(), but instead of returning an internally allocated\n * buffer and key length, it returns directly a module string object allocated\n * in the specified context 'ctx' (that may be NULL exactly like for the main\n * API RedisModule_CreateString).\n *\n * The returned string object should be deallocated after use, either manually\n * or by using a context that has automatic memory management active. */\nRedisModuleString *RM_DictNext(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) {\n    size_t keylen;\n    void *key = RM_DictNextC(di,&keylen,dataptr);\n    if (key == NULL) return NULL;\n    return RM_CreateString(ctx,key,keylen);\n}\n\n/* Like RedisModule_DictNext() but after returning the currently selected\n * element in the iterator, it selects the previous element (laxicographically\n * smaller) instead of the next one. */\nRedisModuleString *RM_DictPrev(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) {\n    size_t keylen;\n    void *key = RM_DictPrevC(di,&keylen,dataptr);\n    if (key == NULL) return NULL;\n    return RM_CreateString(ctx,key,keylen);\n}\n\n/* Compare the element currently pointed by the iterator to the specified\n * element given by key/keylen, according to the operator 'op' (the set of\n * valid operators are the same valid for RedisModule_DictIteratorStart).\n * If the comparision is successful the command returns REDISMODULE_OK\n * otherwise REDISMODULE_ERR is returned.\n *\n * This is useful when we want to just emit a lexicographical range, so\n * in the loop, as we iterate elements, we can also check if we are still\n * on range.\n *\n * The function returne REDISMODULE_ERR if the iterator reached the\n * end of elements condition as well. */\nint RM_DictCompareC(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) {\n    if (raxEOF(&di->ri)) return REDISMODULE_ERR;\n    int res = raxCompare(&di->ri,op,key,keylen);\n    return res ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n/* Like RedisModule_DictCompareC but gets the key to compare with the current\n * iterator key as a RedisModuleString. */\nint RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *key) {\n    if (raxEOF(&di->ri)) return REDISMODULE_ERR;\n    int res = raxCompare(&di->ri,op,key->ptr,sdslen(key->ptr));\n    return res ? REDISMODULE_OK : REDISMODULE_ERR;\n}\n\n\n\n\n/* --------------------------------------------------------------------------\n * Modules Info fields\n * -------------------------------------------------------------------------- */\n\nint RM_InfoEndDictField(RedisModuleInfoCtx *ctx);\n\n/* Used to start a new section, before adding any fields. the section name will\n * be prefixed by \"<modulename>_\" and must only include A-Z,a-z,0-9.\n * NULL or empty string indicates the default section (only <modulename>) is used.\n * When return value is REDISMODULE_ERR, the section should and will be skipped. */\nint RM_InfoAddSection(RedisModuleInfoCtx *ctx, char *name) {\n    sds full_name = sdsdup(ctx->module->name);\n    if (name != NULL && strlen(name) > 0)\n        full_name = sdscatfmt(full_name, \"_%s\", name);\n\n    /* Implicitly end dicts, instead of returning an error which is likely un checked. */\n    if (ctx->in_dict_field)\n        RM_InfoEndDictField(ctx);\n\n    /* proceed only if:\n     * 1) no section was requested (emit all)\n     * 2) the module name was requested (emit all)\n     * 3) this specific section was requested. */\n    if (ctx->requested_section) {\n        if (strcasecmp(ctx->requested_section, full_name) &&\n            strcasecmp(ctx->requested_section, ctx->module->name)) {\n            sdsfree(full_name);\n            ctx->in_section = 0;\n            return REDISMODULE_ERR;\n        }\n    }\n    if (ctx->sections++) ctx->info = sdscat(ctx->info,\"\\r\\n\");\n    ctx->info = sdscatfmt(ctx->info, \"# %S\\r\\n\", full_name);\n    ctx->in_section = 1;\n    sdsfree(full_name);\n    return REDISMODULE_OK;\n}\n\n/* Starts a dict field, similar to the ones in INFO KEYSPACE. Use normal\n * RedisModule_InfoAddField* functions to add the items to this field, and\n * terminate with RedisModule_InfoEndDictField. */\nint RM_InfoBeginDictField(RedisModuleInfoCtx *ctx, char *name) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    /* Implicitly end dicts, instead of returning an error which is likely un checked. */\n    if (ctx->in_dict_field)\n        RM_InfoEndDictField(ctx);\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:\",\n        ctx->module->name,\n        name);\n    ctx->in_dict_field = 1;\n    return REDISMODULE_OK;\n}\n\n/* Ends a dict field, see RedisModule_InfoBeginDictField */\nint RM_InfoEndDictField(RedisModuleInfoCtx *ctx) {\n    if (!ctx->in_dict_field)\n        return REDISMODULE_ERR;\n    /* trim the last ',' if found. */\n    if (ctx->info[sdslen(ctx->info)-1]==',')\n        sdsIncrLen(ctx->info, -1);\n    ctx->info = sdscat(ctx->info, \"\\r\\n\");\n    ctx->in_dict_field = 0;\n    return REDISMODULE_OK;\n}\n\n/* Used by RedisModuleInfoFunc to add info fields.\n * Each field will be automatically prefixed by \"<modulename>_\".\n * Field names or values must not include \\r\\n of \":\" */\nint RM_InfoAddFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%S,\",\n            field,\n            (sds)value->ptr);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%S\\r\\n\",\n        ctx->module->name,\n        field,\n        (sds)value->ptr);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%s,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%s\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatprintf(ctx->info,\n            \"%s=%.17g,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatprintf(ctx->info,\n        \"%s_%s:%.17g\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%I,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%I\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) {\n    if (!ctx->in_section)\n        return REDISMODULE_ERR;\n    if (ctx->in_dict_field) {\n        ctx->info = sdscatfmt(ctx->info,\n            \"%s=%U,\",\n            field,\n            value);\n        return REDISMODULE_OK;\n    }\n    ctx->info = sdscatfmt(ctx->info,\n        \"%s_%s:%U\\r\\n\",\n        ctx->module->name,\n        field,\n        value);\n    return REDISMODULE_OK;\n}\n\nint RM_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) {\n    ctx->module->info_cb = cb;\n    return REDISMODULE_OK;\n}\n\nsds modulesCollectInfo(sds info, const char *section, int for_crash_report, int sections) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        struct RedisModule *module = dictGetVal(de);\n        if (!module->info_cb)\n            continue;\n        RedisModuleInfoCtx info_ctx = {module, section, info, sections, 0, 0};\n        module->info_cb(&info_ctx, for_crash_report);\n        /* Implicitly end dicts (no way to handle errors, and we must add the newline). */\n        if (info_ctx.in_dict_field)\n            RM_InfoEndDictField(&info_ctx);\n        info = info_ctx.info;\n        sections = info_ctx.sections;\n    }\n    dictReleaseIterator(di);\n    return info;\n}\n\n/* Get information about the server similar to the one that returns from the\n * INFO command. This function takes an optional 'section' argument that may\n * be NULL. The return value holds the output and can be used with\n * RedisModule_ServerInfoGetField and alike to get the individual fields.\n * When done, it needs to be freed with RedisModule_FreeServerInfo or with the\n * automatic memory management mechanism if enabled. */\nRedisModuleServerInfoData *RM_GetServerInfo(RedisModuleCtx *ctx, const char *section) {\n    struct RedisModuleServerInfoData *d = zmalloc(sizeof(*d));\n    d->rax = raxNew();\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_INFO,d);\n    sds info = genRedisInfoString(section);\n    int totlines, i;\n    sds *lines = sdssplitlen(info, sdslen(info), \"\\r\\n\", 2, &totlines);\n    for(i=0; i<totlines; i++) {\n        sds line = lines[i];\n        if (line[0]=='#') continue;\n        char *sep = strchr(line, ':');\n        if (!sep) continue;\n        unsigned char *key = (unsigned char*)line;\n        size_t keylen = (intptr_t)sep-(intptr_t)line;\n        sds val = sdsnewlen(sep+1,sdslen(line)-((intptr_t)sep-(intptr_t)line)-1);\n        if (!raxTryInsert(d->rax,key,keylen,val,NULL))\n            sdsfree(val);\n    }\n    sdsfree(info);\n    sdsfreesplitres(lines,totlines);\n    return d;\n}\n\n/* Free data created with RM_GetServerInfo(). You need to pass the\n * context pointer 'ctx' only if the dictionary was created using the\n * context instead of passing NULL. */\nvoid RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) {\n    if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_INFO,data);\n    raxIterator ri;\n    raxStart(&ri,data->rax);\n    while(1) {\n        raxSeek(&ri,\"^\",NULL,0);\n        if (!raxNext(&ri)) break;\n        raxRemove(data->rax,(unsigned char*)ri.key,ri.key_len,NULL);\n        sdsfree(ri.data);\n    }\n    raxStop(&ri);\n    raxFree(data->rax);\n    zfree(data);\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). You\n * need to pass the context pointer 'ctx' only if you want to use auto memory\n * mechanism to release the returned string. Return value will be NULL if the\n * field was not found. */\nRedisModuleString *RM_ServerInfoGetField(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) {\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) return NULL;\n    RedisModuleString *o = createStringObject(val,sdslen(val));\n    if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);\n    return o;\n}\n\n/* Similar to RM_ServerInfoGetField, but returns a char* which should not be freed but the caller. */\nconst char *RM_ServerInfoGetFieldC(RedisModuleServerInfoData *data, const char* field) {\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) return NULL;\n    return val;\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). If the\n * field is not found, or is not numerical or out of range, return value will be\n * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */\nlong long RM_ServerInfoGetFieldSigned(RedisModuleServerInfoData *data, const char* field, int *out_err) {\n    long long ll;\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (!string2ll(val,sdslen(val),&ll)) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (out_err) *out_err = REDISMODULE_OK;\n    return ll;\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). If the\n * field is not found, or is not numerical or out of range, return value will be\n * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */\nunsigned long long RM_ServerInfoGetFieldUnsigned(RedisModuleServerInfoData *data, const char* field, int *out_err) {\n    unsigned long long ll;\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (!string2ull(val,&ll)) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (out_err) *out_err = REDISMODULE_OK;\n    return ll;\n}\n\n/* Get the value of a field from data collected with RM_GetServerInfo(). If the\n * field is not found, or is not a double, return value will be 0, and the\n * optional out_err argument will be set to REDISMODULE_ERR. */\ndouble RM_ServerInfoGetFieldDouble(RedisModuleServerInfoData *data, const char* field, int *out_err) {\n    double dbl;\n    sds val = raxFind(data->rax, (unsigned char *)field, strlen(field));\n    if (val == raxNotFound) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (!string2d(val,sdslen(val),&dbl)) {\n        if (out_err) *out_err = REDISMODULE_ERR;\n        return 0;\n    }\n    if (out_err) *out_err = REDISMODULE_OK;\n    return dbl;\n}\n\n/* --------------------------------------------------------------------------\n * Modules utility APIs\n * -------------------------------------------------------------------------- */\n\n/* Return random bytes using SHA1 in counter mode with a /dev/urandom\n * initialized seed. This function is fast so can be used to generate\n * many bytes without any effect on the operating system entropy pool.\n * Currently this function is not thread safe. */\nvoid RM_GetRandomBytes(unsigned char *dst, size_t len) {\n    getRandomBytes(dst,len);\n}\n\n/* Like RedisModule_GetRandomBytes() but instead of setting the string to\n * random bytes the string is set to random characters in the in the\n * hex charset [0-9a-f]. */\nvoid RM_GetRandomHexChars(char *dst, size_t len) {\n    getRandomHexChars(dst,len);\n}\n\n/* --------------------------------------------------------------------------\n * Modules API exporting / importing\n * -------------------------------------------------------------------------- */\n\n/* This function is called by a module in order to export some API with a\n * given name. Other modules will be able to use this API by calling the\n * symmetrical function RM_GetSharedAPI() and casting the return value to\n * the right function pointer.\n *\n * The function will return REDISMODULE_OK if the name is not already taken,\n * otherwise REDISMODULE_ERR will be returned and no operation will be\n * performed.\n *\n * IMPORTANT: the apiname argument should be a string literal with static\n * lifetime. The API relies on the fact that it will always be valid in\n * the future. */\nint RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {\n    RedisModuleSharedAPI *sapi = zmalloc(sizeof(*sapi));\n    sapi->module = ctx->module;\n    sapi->func = func;\n    if (dictAdd(server.sharedapi, (char*)apiname, sapi) != DICT_OK) {\n        zfree(sapi);\n        return REDISMODULE_ERR;\n    }\n    return REDISMODULE_OK;\n}\n\n/* Request an exported API pointer. The return value is just a void pointer\n * that the caller of this function will be required to cast to the right\n * function pointer, so this is a private contract between modules.\n *\n * If the requested API is not available then NULL is returned. Because\n * modules can be loaded at different times with different order, this\n * function calls should be put inside some module generic API registering\n * step, that is called every time a module attempts to execute a\n * command that requires external APIs: if some API cannot be resolved, the\n * command should return an error.\n *\n * Here is an exmaple:\n *\n *     int ... myCommandImplementation() {\n *        if (getExternalAPIs() == 0) {\n *             reply with an error here if we cannot have the APIs\n *        }\n *        // Use the API:\n *        myFunctionPointer(foo);\n *     }\n *\n * And the function registerAPI() is:\n *\n *     int getExternalAPIs(void) {\n *         static int api_loaded = 0;\n *         if (api_loaded != 0) return 1; // APIs already resolved.\n *\n *         myFunctionPointer = RedisModule_GetOtherModuleAPI(\"...\");\n *         if (myFunctionPointer == NULL) return 0;\n *\n *         return 1;\n *     }\n */\nvoid *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) {\n    dictEntry *de = dictFind(server.sharedapi, apiname);\n    if (de == NULL) return NULL;\n    RedisModuleSharedAPI *sapi = dictGetVal(de);\n    if (listSearchKey(sapi->module->usedby,ctx->module) == NULL) {\n        listAddNodeTail(sapi->module->usedby,ctx->module);\n        listAddNodeTail(ctx->module->using,sapi->module);\n    }\n    return sapi->func;\n}\n\n/* Remove all the APIs registered by the specified module. Usually you\n * want this when the module is going to be unloaded. This function\n * assumes that's caller responsibility to make sure the APIs are not\n * used by other modules.\n *\n * The number of unregistered APIs is returned. */\nint moduleUnregisterSharedAPI(RedisModule *module) {\n    int count = 0;\n    dictIterator *di = dictGetSafeIterator(server.sharedapi);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        const char *apiname = dictGetKey(de);\n        RedisModuleSharedAPI *sapi = dictGetVal(de);\n        if (sapi->module == module) {\n            dictDelete(server.sharedapi,apiname);\n            zfree(sapi);\n            count++;\n        }\n    }\n    dictReleaseIterator(di);\n    return count;\n}\n\n/* Remove the specified module as an user of APIs of ever other module.\n * This is usually called when a module is unloaded.\n *\n * Returns the number of modules this module was using APIs from. */\nint moduleUnregisterUsedAPI(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    int count = 0;\n\n    listRewind(module->using,&li);\n    while((ln = listNext(&li))) {\n        RedisModule *used = ln->value;\n        listNode *ln = listSearchKey(used->usedby,module);\n        if (ln) {\n            listDelNode(used->usedby,ln);\n            count++;\n        }\n    }\n    return count;\n}\n\n/* Unregister all filters registered by a module.\n * This is called when a module is being unloaded.\n * \n * Returns the number of filters unregistered. */\nint moduleUnregisterFilters(RedisModule *module) {\n    listIter li;\n    listNode *ln;\n    int count = 0;\n\n    listRewind(module->filters,&li);\n    while((ln = listNext(&li))) {\n        RedisModuleCommandFilter *filter = ln->value;\n        listNode *ln = listSearchKey(moduleCommandFilters,filter);\n        if (ln) {\n            listDelNode(moduleCommandFilters,ln);\n            count++;\n        }\n        zfree(filter);\n    }\n    return count;\n}\n\n/* --------------------------------------------------------------------------\n * Module Command Filter API\n * -------------------------------------------------------------------------- */\n\n/* Register a new command filter function.\n *\n * Command filtering makes it possible for modules to extend Redis by plugging\n * into the execution flow of all commands.\n *\n * A registered filter gets called before Redis executes *any* command.  This\n * includes both core Redis commands and commands registered by any module.  The\n * filter applies in all execution paths including:\n *\n * 1. Invocation by a client.\n * 2. Invocation through `RedisModule_Call()` by any module.\n * 3. Invocation through Lua 'redis.call()`.\n * 4. Replication of a command from a master.\n *\n * The filter executes in a special filter context, which is different and more\n * limited than a RedisModuleCtx.  Because the filter affects any command, it\n * must be implemented in a very efficient way to reduce the performance impact\n * on Redis.  All Redis Module API calls that require a valid context (such as\n * `RedisModule_Call()`, `RedisModule_OpenKey()`, etc.) are not supported in a\n * filter context.\n *\n * The `RedisModuleCommandFilterCtx` can be used to inspect or modify the\n * executed command and its arguments.  As the filter executes before Redis\n * begins processing the command, any change will affect the way the command is\n * processed.  For example, a module can override Redis commands this way:\n *\n * 1. Register a `MODULE.SET` command which implements an extended version of\n *    the Redis `SET` command.\n * 2. Register a command filter which detects invocation of `SET` on a specific\n *    pattern of keys.  Once detected, the filter will replace the first\n *    argument from `SET` to `MODULE.SET`.\n * 3. When filter execution is complete, Redis considers the new command name\n *    and therefore executes the module's own command.\n *\n * Note that in the above use case, if `MODULE.SET` itself uses\n * `RedisModule_Call()` the filter will be applied on that call as well.  If\n * that is not desired, the `REDISMODULE_CMDFILTER_NOSELF` flag can be set when\n * registering the filter.\n *\n * The `REDISMODULE_CMDFILTER_NOSELF` flag prevents execution flows that\n * originate from the module's own `RM_Call()` from reaching the filter.  This\n * flag is effective for all execution flows, including nested ones, as long as\n * the execution begins from the module's command context or a thread-safe\n * context that is associated with a blocking command.\n *\n * Detached thread-safe contexts are *not* associated with the module and cannot\n * be protected by this flag.\n *\n * If multiple filters are registered (by the same or different modules), they\n * are executed in the order of registration.\n */\n\nRedisModuleCommandFilter *RM_RegisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback, int flags) {\n    RedisModuleCommandFilter *filter = zmalloc(sizeof(*filter));\n    filter->module = ctx->module;\n    filter->callback = callback;\n    filter->flags = flags;\n\n    listAddNodeTail(moduleCommandFilters, filter);\n    listAddNodeTail(ctx->module->filters, filter);\n    return filter;\n}\n\n/* Unregister a command filter.\n */\nint RM_UnregisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) {\n    listNode *ln;\n\n    /* A module can only remove its own filters */\n    if (filter->module != ctx->module) return REDISMODULE_ERR;\n\n    ln = listSearchKey(moduleCommandFilters,filter);\n    if (!ln) return REDISMODULE_ERR;\n    listDelNode(moduleCommandFilters,ln);\n\n    ln = listSearchKey(ctx->module->filters,filter);\n    if (!ln) return REDISMODULE_ERR;    /* Shouldn't happen */\n    listDelNode(ctx->module->filters,ln);\n\n    zfree(filter);\n\n    return REDISMODULE_OK;\n}\n\nvoid moduleCallCommandFilters(client *c) {\n    if (listLength(moduleCommandFilters) == 0) return;\n\n    listIter li;\n    listNode *ln;\n    listRewind(moduleCommandFilters,&li);\n\n    RedisModuleCommandFilterCtx filter = {\n        .argv = c->argv,\n        .argc = c->argc\n    };\n\n    while((ln = listNext(&li))) {\n        RedisModuleCommandFilter *f = ln->value;\n\n        /* Skip filter if REDISMODULE_CMDFILTER_NOSELF is set and module is\n         * currently processing a command.\n         */\n        if ((f->flags & REDISMODULE_CMDFILTER_NOSELF) && f->module->in_call) continue;\n\n        /* Call filter */\n        f->callback(&filter);\n    }\n\n    c->argv = filter.argv;\n    c->argc = filter.argc;\n}\n\n/* Return the number of arguments a filtered command has.  The number of\n * arguments include the command itself.\n */\nint RM_CommandFilterArgsCount(RedisModuleCommandFilterCtx *fctx)\n{\n    return fctx->argc;\n}\n\n/* Return the specified command argument.  The first argument (position 0) is\n * the command itself, and the rest are user-provided args.\n */\nconst RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *fctx, int pos)\n{\n    if (pos < 0 || pos >= fctx->argc) return NULL;\n    return fctx->argv[pos];\n}\n\n/* Modify the filtered command by inserting a new argument at the specified\n * position.  The specified RedisModuleString argument may be used by Redis\n * after the filter context is destroyed, so it must not be auto-memory\n * allocated, freed or used elsewhere.\n */\n\nint RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)\n{\n    int i;\n\n    if (pos < 0 || pos > fctx->argc) return REDISMODULE_ERR;\n\n    fctx->argv = zrealloc(fctx->argv, (fctx->argc+1)*sizeof(RedisModuleString *));\n    for (i = fctx->argc; i > pos; i--) {\n        fctx->argv[i] = fctx->argv[i-1];\n    }\n    fctx->argv[pos] = arg;\n    fctx->argc++;\n\n    return REDISMODULE_OK;\n}\n\n/* Modify the filtered command by replacing an existing argument with a new one.\n * The specified RedisModuleString argument may be used by Redis after the\n * filter context is destroyed, so it must not be auto-memory allocated, freed\n * or used elsewhere.\n */\n\nint RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)\n{\n    if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR;\n\n    decrRefCount(fctx->argv[pos]);\n    fctx->argv[pos] = arg;\n\n    return REDISMODULE_OK;\n}\n\n/* Modify the filtered command by deleting an argument at the specified\n * position.\n */\nint RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx, int pos)\n{\n    int i;\n    if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR;\n\n    decrRefCount(fctx->argv[pos]);\n    for (i = pos; i < fctx->argc-1; i++) {\n        fctx->argv[i] = fctx->argv[i+1];\n    }\n    fctx->argc--;\n\n    return REDISMODULE_OK;\n}\n\n/* For a given pointer allocated via RedisModule_Alloc() or\n * RedisModule_Realloc(), return the amount of memory allocated for it.\n * Note that this may be different (larger) than the memory we allocated\n * with the allocation calls, since sometimes the underlying allocator\n * will allocate more memory.\n */\nsize_t RM_MallocSize(void* ptr){\n    return zmalloc_size(ptr);\n}\n\n/* Return the a number between 0 to 1 indicating the amount of memory\n * currently used, relative to the Redis \"maxmemory\" configuration.\n *\n * 0 - No memory limit configured.\n * Between 0 and 1 - The percentage of the memory used normalized in 0-1 range.\n * Exactly 1 - Memory limit reached.\n * Greater 1 - More memory used than the configured limit.\n */\nfloat RM_GetUsedMemoryRatio(){\n    float level;\n    getMaxmemoryState(NULL, NULL, NULL, &level);\n    return level;\n}\n\n/* --------------------------------------------------------------------------\n * Scanning keyspace and hashes\n * -------------------------------------------------------------------------- */\n\ntypedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata);\ntypedef struct {\n    RedisModuleCtx *ctx;\n    void* user_data;\n    RedisModuleScanCB fn;\n} ScanCBData;\n\ntypedef struct RedisModuleScanCursor{\n    int cursor;\n    int done;\n}RedisModuleScanCursor;\n\nstatic void moduleScanCallback(void *privdata, const dictEntry *de) {\n    ScanCBData *data = privdata;\n    sds key = dictGetKey(de);\n    robj* val = dictGetVal(de);\n    RedisModuleString *keyname = createObject(OBJ_STRING,sdsdup(key));\n\n    /* Setup the key handle. */\n    RedisModuleKey kp = {0};\n    moduleInitKey(&kp, data->ctx, keyname, val, REDISMODULE_READ);\n\n    data->fn(data->ctx, keyname, &kp, data->user_data);\n\n    moduleCloseKey(&kp);\n    decrRefCount(keyname);\n}\n\n/* Create a new cursor to be used with RedisModule_Scan */\nRedisModuleScanCursor *RM_ScanCursorCreate() {\n    RedisModuleScanCursor* cursor = zmalloc(sizeof(*cursor));\n    cursor->cursor = 0;\n    cursor->done = 0;\n    return cursor;\n}\n\n/* Restart an existing cursor. The keys will be rescanned. */\nvoid RM_ScanCursorRestart(RedisModuleScanCursor *cursor) {\n    cursor->cursor = 0;\n    cursor->done = 0;\n}\n\n/* Destroy the cursor struct. */\nvoid RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) {\n    zfree(cursor);\n}\n\n/* Scan API that allows a module to scan all the keys and value in\n * the selected db.\n *\n * Callback for scan implementation.\n * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname,\n *                    RedisModuleKey *key, void *privdata);\n * ctx - the redis module context provided to for the scan.\n * keyname - owned by the caller and need to be retained if used after this\n * function.\n *\n * key - holds info on the key and value, it is provided as best effort, in\n * some cases it might be NULL, in which case the user should (can) use\n * RedisModule_OpenKey (and CloseKey too).\n * when it is provided, it is owned by the caller and will be free when the\n * callback returns.\n *\n * privdata - the user data provided to RedisModule_Scan.\n *\n * The way it should be used:\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      while(RedisModule_Scan(ctx, c, callback, privateData));\n *      RedisModule_ScanCursorDestroy(c);\n *\n * It is also possible to use this API from another thread while the lock\n * is acquired durring the actuall call to RM_Scan:\n *\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      RedisModule_ThreadSafeContextLock(ctx);\n *      while(RedisModule_Scan(ctx, c, callback, privateData)){\n *          RedisModule_ThreadSafeContextUnlock(ctx);\n *          // do some background job\n *          RedisModule_ThreadSafeContextLock(ctx);\n *      }\n *      RedisModule_ScanCursorDestroy(c);\n *\n * The function will return 1 if there are more elements to scan and\n * 0 otherwise, possibly setting errno if the call failed.\n *\n * It is also possible to restart and existing cursor using RM_CursorRestart.\n *\n * IMPORTANT: This API is very similar to the Redis SCAN command from the\n * point of view of the guarantees it provides. This means that the API\n * may report duplicated keys, but guarantees to report at least one time\n * every key that was there from the start to the end of the scanning process.\n *\n * NOTE: If you do database changes within the callback, you should be aware\n * that the internal state of the database may change. For instance it is safe\n * to delete or modify the current key, but may not be safe to delete any\n * other key.\n * Moreover playing with the Redis keyspace while iterating may have the\n * effect of returning more duplicates. A safe pattern is to store the keys\n * names you want to modify elsewhere, and perform the actions on the keys\n * later when the iteration is complete. Howerver this can cost a lot of\n * memory, so it may make sense to just operate on the current key when\n * possible during the iteration, given that this is safe. */\nint RM_Scan(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) {\n    if (cursor->done) {\n        errno = ENOENT;\n        return 0;\n    }\n    int ret = 1;\n    ScanCBData data = { ctx, privdata, fn };\n    cursor->cursor = dictScan(ctx->client->db->dict, cursor->cursor, moduleScanCallback, NULL, &data);\n    if (cursor->cursor == 0) {\n        cursor->done = 1;\n        ret = 0;\n    }\n    errno = 0;\n    return ret;\n}\n\ntypedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata);\ntypedef struct {\n    RedisModuleKey *key;\n    void* user_data;\n    RedisModuleScanKeyCB fn;\n} ScanKeyCBData;\n\nstatic void moduleScanKeyCallback(void *privdata, const dictEntry *de) {\n    ScanKeyCBData *data = privdata;\n    sds key = dictGetKey(de);\n    robj *o = data->key->value;\n    robj *field = createStringObject(key, sdslen(key));\n    robj *value = NULL;\n    if (o->type == OBJ_SET) {\n        value = NULL;\n    } else if (o->type == OBJ_HASH) {\n        sds val = dictGetVal(de);\n        value = createStringObject(val, sdslen(val));\n    } else if (o->type == OBJ_ZSET) {\n        double *val = (double*)dictGetVal(de);\n        value = createStringObjectFromLongDouble(*val, 0);\n    }\n\n    data->fn(data->key, field, value, data->user_data);\n    decrRefCount(field);\n    if (value) decrRefCount(value);\n}\n\n/* Scan api that allows a module to scan the elements in a hash, set or sorted set key\n *\n * Callback for scan implementation.\n * void scan_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata);\n * - key - the redis key context provided to for the scan.\n * - field - field name, owned by the caller and need to be retained if used\n *   after this function.\n * - value - value string or NULL for set type, owned by the caller and need to\n *   be retained if used after this function.\n * - privdata - the user data provided to RedisModule_ScanKey.\n *\n * The way it should be used:\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      RedisModuleKey *key = RedisModule_OpenKey(...)\n *      while(RedisModule_ScanKey(key, c, callback, privateData));\n *      RedisModule_CloseKey(key);\n *      RedisModule_ScanCursorDestroy(c);\n *\n * It is also possible to use this API from another thread while the lock is acquired durring\n * the actuall call to RM_Scan, and re-opening the key each time:\n *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();\n *      RedisModule_ThreadSafeContextLock(ctx);\n *      RedisModuleKey *key = RedisModule_OpenKey(...)\n *      while(RedisModule_ScanKey(ctx, c, callback, privateData)){\n *          RedisModule_CloseKey(key);\n *          RedisModule_ThreadSafeContextUnlock(ctx);\n *          // do some background job\n *          RedisModule_ThreadSafeContextLock(ctx);\n *          RedisModuleKey *key = RedisModule_OpenKey(...)\n *      }\n *      RedisModule_CloseKey(key);\n *      RedisModule_ScanCursorDestroy(c);\n *\n * The function will return 1 if there are more elements to scan and 0 otherwise,\n * possibly setting errno if the call failed.\n * It is also possible to restart and existing cursor using RM_CursorRestart.\n *\n * NOTE: Certain operations are unsafe while iterating the object. For instance\n * while the API guarantees to return at least one time all the elements that\n * are present in the data structure consistently from the start to the end\n * of the iteration (see HSCAN and similar commands documentation), the more\n * you play with the elements, the more duplicates you may get. In general\n * deleting the current element of the data structure is safe, while removing\n * the key you are iterating is not safe. */\nint RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) {\n    if (key == NULL || key->value == NULL) {\n        errno = EINVAL;\n        return 0;\n    }\n    dict *ht = NULL;\n    robj *o = key->value;\n    if (o->type == OBJ_SET) {\n        if (o->encoding == OBJ_ENCODING_HT)\n            ht = o->ptr;\n    } else if (o->type == OBJ_HASH) {\n        if (o->encoding == OBJ_ENCODING_HT)\n            ht = o->ptr;\n    } else if (o->type == OBJ_ZSET) {\n        if (o->encoding == OBJ_ENCODING_SKIPLIST)\n            ht = ((zset *)o->ptr)->dict;\n    } else {\n        errno = EINVAL;\n        return 0;\n    }\n    if (cursor->done) {\n        errno = ENOENT;\n        return 0;\n    }\n    int ret = 1;\n    if (ht) {\n        ScanKeyCBData data = { key, privdata, fn };\n        cursor->cursor = dictScan(ht, cursor->cursor, moduleScanKeyCallback, NULL, &data);\n        if (cursor->cursor == 0) {\n            cursor->done = 1;\n            ret = 0;\n        }\n    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_INTSET) {\n        int pos = 0;\n        int64_t ll;\n        while(intsetGet(o->ptr,pos++,&ll)) {\n            robj *field = createStringObjectFromLongLong(ll);\n            fn(key, field, NULL, privdata);\n            decrRefCount(field);\n        }\n        cursor->cursor = 1;\n        cursor->done = 1;\n        ret = 0;\n    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {\n        unsigned char *p = ziplistIndex(o->ptr,0);\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vll;\n        while(p) {\n            ziplistGet(p,&vstr,&vlen,&vll);\n            robj *field = (vstr != NULL) ?\n                createStringObject((char*)vstr,vlen) :\n                createStringObjectFromLongLong(vll);\n            p = ziplistNext(o->ptr,p);\n            ziplistGet(p,&vstr,&vlen,&vll);\n            robj *value = (vstr != NULL) ?\n                createStringObject((char*)vstr,vlen) :\n                createStringObjectFromLongLong(vll);\n            fn(key, field, value, privdata);\n            p = ziplistNext(o->ptr,p);\n            decrRefCount(field);\n            decrRefCount(value);\n        }\n        cursor->cursor = 1;\n        cursor->done = 1;\n        ret = 0;\n    }\n    errno = 0;\n    return ret;\n}\n\n\n/* --------------------------------------------------------------------------\n * Module fork API\n * -------------------------------------------------------------------------- */\n\n/* Create a background child process with the current frozen snaphost of the\n * main process where you can do some processing in the background without\n * affecting / freezing the traffic and no need for threads and GIL locking.\n * Note that Redis allows for only one concurrent fork.\n * When the child wants to exit, it should call RedisModule_ExitFromChild.\n * If the parent wants to kill the child it should call RedisModule_KillForkChild\n * The done handler callback will be executed on the parent process when the\n * child existed (but not when killed)\n * Return: -1 on failure, on success the parent process will get a positive PID\n * of the child, and the child process will get 0.\n */\nint RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) {\n    pid_t childpid;\n    if (hasActiveChildProcess()) {\n        return -1;\n    }\n\n    openChildInfoPipe();\n    if ((childpid = redisFork()) == 0) {\n        /* Child */\n        redisSetProcTitle(\"redis-module-fork\");\n    } else if (childpid == -1) {\n        closeChildInfoPipe();\n        serverLog(LL_WARNING,\"Can't fork for module: %s\", strerror(errno));\n    } else {\n        /* Parent */\n        server.module_child_pid = childpid;\n        moduleForkInfo.done_handler = cb;\n        moduleForkInfo.done_handler_user_data = user_data;\n        serverLog(LL_VERBOSE, \"Module fork started pid: %d \", childpid);\n    }\n    return childpid;\n}\n\n/* Call from the child process when you want to terminate it.\n * retcode will be provided to the done handler executed on the parent process.\n */\nint RM_ExitFromChild(int retcode) {\n    sendChildCOWInfo(CHILD_INFO_TYPE_MODULE, \"Module fork\");\n    exitFromChild(retcode);\n    return REDISMODULE_OK;\n}\n\n/* Kill the active module forked child, if there is one active and the\n * pid matches, and returns C_OK. Otherwise if there is no active module\n * child or the pid does not match, return C_ERR without doing anything. */\nint TerminateModuleForkChild(int child_pid, int wait) {\n    /* Module child should be active and pid should match. */\n    if (server.module_child_pid == -1 ||\n        server.module_child_pid != child_pid) return C_ERR;\n\n    int statloc;\n    serverLog(LL_VERBOSE,\"Killing running module fork child: %ld\",\n        (long) server.module_child_pid);\n    if (kill(server.module_child_pid,SIGUSR1) != -1 && wait) {\n        while(wait4(server.module_child_pid,&statloc,0,NULL) !=\n              server.module_child_pid);\n    }\n    /* Reset the buffer accumulating changes while the child saves. */\n    server.module_child_pid = -1;\n    moduleForkInfo.done_handler = NULL;\n    moduleForkInfo.done_handler_user_data = NULL;\n    closeChildInfoPipe();\n    updateDictResizePolicy();\n    return C_OK;\n}\n\n/* Can be used to kill the forked child process from the parent process.\n * child_pid whould be the return value of RedisModule_Fork. */\nint RM_KillForkChild(int child_pid) {\n    /* Kill module child, wait for child exit. */\n    if (TerminateModuleForkChild(child_pid,1) == C_OK)\n        return REDISMODULE_OK;\n    else\n        return REDISMODULE_ERR;\n}\n\nvoid ModuleForkDoneHandler(int exitcode, int bysignal) {\n    serverLog(LL_NOTICE,\n        \"Module fork exited pid: %d, retcode: %d, bysignal: %d\",\n        server.module_child_pid, exitcode, bysignal);\n    if (moduleForkInfo.done_handler) {\n        moduleForkInfo.done_handler(exitcode, bysignal,\n            moduleForkInfo.done_handler_user_data);\n    }\n    server.module_child_pid = -1;\n    moduleForkInfo.done_handler = NULL;\n    moduleForkInfo.done_handler_user_data = NULL;\n}\n\n/* --------------------------------------------------------------------------\n * Server hooks implementation\n * -------------------------------------------------------------------------- */\n\n/* Register to be notified, via a callback, when the specified server event\n * happens. The callback is called with the event as argument, and an additional\n * argument which is a void pointer and should be cased to a specific type\n * that is event-specific (but many events will just use NULL since they do not\n * have additional information to pass to the callback).\n *\n * If the callback is NULL and there was a previous subscription, the module\n * will be unsubscribed. If there was a previous subscription and the callback\n * is not null, the old callback will be replaced with the new one.\n *\n * The callback must be of this type:\n *\n *  int (*RedisModuleEventCallback)(RedisModuleCtx *ctx,\n *                                  RedisModuleEvent eid,\n *                                  uint64_t subevent,\n *                                  void *data);\n *\n * The 'ctx' is a normal Redis module context that the callback can use in\n * order to call other modules APIs. The 'eid' is the event itself, this\n * is only useful in the case the module subscribed to multiple events: using\n * the 'id' field of this structure it is possible to check if the event\n * is one of the events we registered with this callback. The 'subevent' field\n * depends on the event that fired.\n *\n * Finally the 'data' pointer may be populated, only for certain events, with\n * more relevant data.\n *\n * Here is a list of events you can use as 'eid' and related sub events:\n *\n *      RedisModuleEvent_ReplicationRoleChanged\n *\n *          This event is called when the instance switches from master\n *          to replica or the other way around, however the event is\n *          also called when the replica remains a replica but starts to\n *          replicate with a different master.\n *\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER\n *              REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA\n *\n *          The 'data' field can be casted by the callback to a\n *          RedisModuleReplicationInfo structure with the following fields:\n *\n *              int master; // true if master, false if replica\n *              char *masterhost; // master instance hostname for NOW_REPLICA\n *              int masterport; // master instance port for NOW_REPLICA\n *              char *replid1; // Main replication ID\n *              char *replid2; // Secondary replication ID\n *              uint64_t repl1_offset; // Main replication offset\n *              uint64_t repl2_offset; // Offset of replid2 validity\n *\n *      RedisModuleEvent_Persistence\n *\n *          This event is called when RDB saving or AOF rewriting starts\n *          and ends. The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START\n *              REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START\n *              REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START\n *              REDISMODULE_SUBEVENT_PERSISTENCE_ENDED\n *              REDISMODULE_SUBEVENT_PERSISTENCE_FAILED\n *\n *          The above events are triggered not just when the user calls the\n *          relevant commands like BGSAVE, but also when a saving operation\n *          or AOF rewriting occurs because of internal server triggers.\n *          The SYNC_RDB_START sub events are happening in the forground due to\n *          SAVE command, FLUSHALL, or server shutdown, and the other RDB and\n *          AOF sub events are executed in a background fork child, so any\n *          action the module takes can only affect the generated AOF or RDB,\n *          but will not be reflected in the parent process and affect connected\n *          clients and commands. Also note that the AOF_START sub event may end\n *          up saving RDB content in case of an AOF with rdb-preamble.\n *\n *      RedisModuleEvent_FlushDB\n *\n *          The FLUSHALL, FLUSHDB or an internal flush (for instance\n *          because of replication, after the replica synchronization)\n *          happened. The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_FLUSHDB_START\n *              REDISMODULE_SUBEVENT_FLUSHDB_END\n *\n *          The data pointer can be casted to a RedisModuleFlushInfo\n *          structure with the following fields:\n *\n *              int32_t async;  // True if the flush is done in a thread.\n *                                 See for instance FLUSHALL ASYNC.\n *                                 In this case the END callback is invoked\n *                                 immediately after the database is put\n *                                 in the free list of the thread.\n *              int32_t dbnum;  // Flushed database number, -1 for all the DBs\n *                                 in the case of the FLUSHALL operation.\n *\n *          The start event is called *before* the operation is initated, thus\n *          allowing the callback to call DBSIZE or other operation on the\n *          yet-to-free keyspace.\n *\n *      RedisModuleEvent_Loading\n *\n *          Called on loading operations: at startup when the server is\n *          started, but also after a first synchronization when the\n *          replica is loading the RDB file from the master.\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_LOADING_RDB_START\n *              REDISMODULE_SUBEVENT_LOADING_AOF_START\n *              REDISMODULE_SUBEVENT_LOADING_REPL_START\n *              REDISMODULE_SUBEVENT_LOADING_ENDED\n *              REDISMODULE_SUBEVENT_LOADING_FAILED\n *\n *          Note that AOF loading may start with an RDB data in case of\n *          rdb-preamble, in which case you'll only recieve an AOF_START event.\n *\n *\n *      RedisModuleEvent_ClientChange\n *\n *          Called when a client connects or disconnects.\n *          The data pointer can be casted to a RedisModuleClientInfo\n *          structure, documented in RedisModule_GetClientInfoById().\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED\n *              REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED\n *\n *      RedisModuleEvent_Shutdown\n *\n *          The server is shutting down. No subevents are available.\n *\n *  RedisModuleEvent_ReplicaChange\n *\n *          This event is called when the instance (that can be both a\n *          master or a replica) get a new online replica, or lose a\n *          replica since it gets disconnected.\n *          The following sub events are availble:\n *\n *              REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE\n *              REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE\n *\n *          No additional information is available so far: future versions\n *          of Redis will have an API in order to enumerate the replicas\n *          connected and their state.\n *\n *  RedisModuleEvent_CronLoop\n *\n *          This event is called every time Redis calls the serverCron()\n *          function in order to do certain bookkeeping. Modules that are\n *          required to do operations from time to time may use this callback.\n *          Normally Redis calls this function 10 times per second, but\n *          this changes depending on the \"hz\" configuration.\n *          No sub events are available.\n *\n *          The data pointer can be casted to a RedisModuleCronLoop\n *          structure with the following fields:\n *\n *              int32_t hz;  // Approximate number of events per second.\n *\n *  RedisModuleEvent_MasterLinkChange\n *\n *          This is called for replicas in order to notify when the\n *          replication link becomes functional (up) with our master,\n *          or when it goes down. Note that the link is not considered\n *          up when we just connected to the master, but only if the\n *          replication is happening correctly.\n *          The following sub events are available:\n *\n *              REDISMODULE_SUBEVENT_MASTER_LINK_UP\n *              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN\n *\n *  RedisModuleEvent_ModuleChange\n *\n *          This event is called when a new module is loaded or one is unloaded.\n *          The following sub events are availble:\n *\n *              REDISMODULE_SUBEVENT_MODULE_LOADED\n *              REDISMODULE_SUBEVENT_MODULE_UNLOADED\n *\n *          The data pointer can be casted to a RedisModuleModuleChange\n *          structure with the following fields:\n *\n *              const char* module_name;  // Name of module loaded or unloaded.\n *              int32_t module_version;  // Module version.\n *\n *  RedisModuleEvent_LoadingProgress\n *\n *          This event is called repeatedly called while an RDB or AOF file\n *          is being loaded.\n *          The following sub events are availble:\n *\n *              REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB\n *              REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF\n *\n *          The data pointer can be casted to a RedisModuleLoadingProgress\n *          structure with the following fields:\n *\n *              int32_t hz;  // Approximate number of events per second.\n *              int32_t progress;  // Approximate progress between 0 and 1024,\n *                                    or -1 if unknown.\n *\n * The function returns REDISMODULE_OK if the module was successfully subscrived\n * for the specified event. If the API is called from a wrong context then\n * REDISMODULE_ERR is returned. */\nint RM_SubscribeToServerEvent(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) {\n    RedisModuleEventListener *el;\n\n    /* Protect in case of calls from contexts without a module reference. */\n    if (ctx->module == NULL) return REDISMODULE_ERR;\n\n    /* Search an event matching this module and event ID. */\n    listIter li;\n    listNode *ln;\n    listRewind(RedisModule_EventListeners,&li);\n    while((ln = listNext(&li))) {\n        el = ln->value;\n        if (el->module == ctx->module && el->event.id == event.id)\n            break; /* Matching event found. */\n    }\n\n    /* Modify or remove the event listener if we already had one. */\n    if (ln) {\n        if (callback == NULL) {\n            listDelNode(RedisModule_EventListeners,ln);\n            zfree(el);\n        } else {\n            el->callback = callback; /* Update the callback with the new one. */\n        }\n        return REDISMODULE_OK;\n    }\n\n    /* No event found, we need to add a new one. */\n    el = zmalloc(sizeof(*el));\n    el->module = ctx->module;\n    el->event = event;\n    el->callback = callback;\n    listAddNodeTail(RedisModule_EventListeners,el);\n    return REDISMODULE_OK;\n}\n\n/* This is called by the Redis internals every time we want to fire an\n * event that can be interceppted by some module. The pointer 'data' is useful\n * in order to populate the event-specific structure when needed, in order\n * to return the structure with more information to the callback.\n *\n * 'eid' and 'subid' are just the main event ID and the sub event associated\n * with the event, depending on what exactly happened. */\nvoid moduleFireServerEvent(uint64_t eid, int subid, void *data) {\n    /* Fast path to return ASAP if there is nothing to do, avoiding to\n     * setup the iterator and so forth: we want this call to be extremely\n     * cheap if there are no registered modules. */\n    if (listLength(RedisModule_EventListeners) == 0) return;\n\n    listIter li;\n    listNode *ln;\n    listRewind(RedisModule_EventListeners,&li);\n    while((ln = listNext(&li))) {\n        RedisModuleEventListener *el = ln->value;\n        if (el->event.id == eid) {\n            RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n            ctx.module = el->module;\n\n            if (ModulesInHooks == 0) {\n                ctx.client = moduleFreeContextReusedClient;\n            } else {\n                ctx.client = createClient(NULL);\n                ctx.client->flags |= CLIENT_MODULE;\n                ctx.client->user = NULL; /* Root user. */\n            }\n\n            void *moduledata = NULL;\n            RedisModuleClientInfoV1 civ1;\n            RedisModuleReplicationInfoV1 riv1;\n            RedisModuleModuleChangeV1 mcv1;\n            /* Start at DB zero by default when calling the handler. It's\n             * up to the specific event setup to change it when it makes\n             * sense. For instance for FLUSHDB events we select the correct\n             * DB automatically. */\n            selectDb(ctx.client, 0);\n\n            /* Event specific context and data pointer setup. */\n            if (eid == REDISMODULE_EVENT_CLIENT_CHANGE) {\n                modulePopulateClientInfoStructure(&civ1,data,\n                                                  el->event.dataver);\n                moduledata = &civ1;\n            } else if (eid == REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED) {\n                modulePopulateReplicationInfoStructure(&riv1,el->event.dataver);\n                moduledata = &riv1;\n            } else if (eid == REDISMODULE_EVENT_FLUSHDB) {\n                moduledata = data;\n                RedisModuleFlushInfoV1 *fi = data;\n                if (fi->dbnum != -1)\n                    selectDb(ctx.client, fi->dbnum);\n            } else if (eid == REDISMODULE_EVENT_MODULE_CHANGE) {\n                RedisModule *m = data;\n                if (m == el->module)\n                    continue;\n                mcv1.version = REDISMODULE_MODULE_CHANGE_VERSION;\n                mcv1.module_name = m->name;\n                mcv1.module_version = m->ver;\n                moduledata = &mcv1;\n            } else if (eid == REDISMODULE_EVENT_LOADING_PROGRESS) {\n                moduledata = data;\n            } else if (eid == REDISMODULE_EVENT_CRON_LOOP) {\n                moduledata = data;\n            }\n\n            ModulesInHooks++;\n            el->module->in_hook++;\n            el->callback(&ctx,el->event,subid,moduledata);\n            el->module->in_hook--;\n            ModulesInHooks--;\n\n            if (ModulesInHooks != 0) freeClient(ctx.client);\n            moduleFreeContext(&ctx);\n        }\n    }\n}\n\n/* Remove all the listeners for this module: this is used before unloading\n * a module. */\nvoid moduleUnsubscribeAllServerEvents(RedisModule *module) {\n    RedisModuleEventListener *el;\n    listIter li;\n    listNode *ln;\n    listRewind(RedisModule_EventListeners,&li);\n\n    while((ln = listNext(&li))) {\n        el = ln->value;\n        if (el->module == module) {\n            listDelNode(RedisModule_EventListeners,ln);\n            zfree(el);\n        }\n    }\n}\n\nvoid processModuleLoadingProgressEvent(int is_aof) {\n    long long now = ustime();\n    static long long next_event = 0;\n    if (now >= next_event) {\n        /* Fire the loading progress modules end event. */\n        int progress = -1;\n        if (server.loading_total_bytes)\n            progress = (server.loading_total_bytes<<10) / server.loading_total_bytes;\n        RedisModuleFlushInfoV1 fi = {REDISMODULE_LOADING_PROGRESS_VERSION,\n                                     server.hz,\n                                     progress};\n        moduleFireServerEvent(REDISMODULE_EVENT_LOADING_PROGRESS,\n                              is_aof?\n                                REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF:\n                                REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB,\n                              &fi);\n        /* decide when the next event should fire. */\n        next_event = now + 1000000 / server.hz;\n    }\n}\n\n/* --------------------------------------------------------------------------\n * Modules API internals\n * -------------------------------------------------------------------------- */\n\n/* server.moduleapi dictionary type. Only uses plain C strings since\n * this gets queries from modules. */\n\nuint64_t dictCStringKeyHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, strlen((char*)key));\n}\n\nint dictCStringKeyCompare(void *privdata, const void *key1, const void *key2) {\n    UNUSED(privdata);\n    return strcmp(key1,key2) == 0;\n}\n\ndictType moduleAPIDictType = {\n    dictCStringKeyHash,        /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictCStringKeyCompare,     /* key compare */\n    NULL,                      /* key destructor */\n    NULL                       /* val destructor */\n};\n\nint moduleRegisterApi(const char *funcname, void *funcptr) {\n    return dictAdd(server.moduleapi, (char*)funcname, funcptr);\n}\n\n#define REGISTER_API(name) \\\n    moduleRegisterApi(\"RedisModule_\" #name, (void *)(unsigned long)RM_ ## name)\n\n/* Global initialization at Redis startup. */\nvoid moduleRegisterCoreAPI(void);\n\nvoid moduleInitModulesSystem(void) {\n    moduleUnblockedClients = listCreate();\n    server.loadmodule_queue = listCreate();\n    modules = dictCreate(&modulesDictType,NULL);\n\n    /* Set up the keyspace notification susbscriber list and static client */\n    moduleKeyspaceSubscribers = listCreate();\n    moduleFreeContextReusedClient = createClient(NULL);\n    moduleFreeContextReusedClient->flags |= CLIENT_MODULE;\n    moduleFreeContextReusedClient->user = NULL; /* root user. */\n\n    /* Set up filter list */\n    moduleCommandFilters = listCreate();\n\n    moduleRegisterCoreAPI();\n    if (pipe(server.module_blocked_pipe) == -1) {\n        serverLog(LL_WARNING,\n            \"Can't create the pipe for module blocking commands: %s\",\n            strerror(errno));\n        exit(1);\n    }\n    /* Make the pipe non blocking. This is just a best effort aware mechanism\n     * and we do not want to block not in the read nor in the write half. */\n    anetNonBlock(NULL,server.module_blocked_pipe[0]);\n    anetNonBlock(NULL,server.module_blocked_pipe[1]);\n\n    /* Create the timers radix tree. */\n    Timers = raxNew();\n\n    /* Setup the event listeners data structures. */\n    RedisModule_EventListeners = listCreate();\n\n    /* Our thread-safe contexts GIL must start with already locked:\n     * it is just unlocked when it's safe. */\n    pthread_mutex_lock(&moduleGIL);\n}\n\n/* Load all the modules in the server.loadmodule_queue list, which is\n * populated by `loadmodule` directives in the configuration file.\n * We can't load modules directly when processing the configuration file\n * because the server must be fully initialized before loading modules.\n *\n * The function aborts the server on errors, since to start with missing\n * modules is not considered sane: clients may rely on the existence of\n * given commands, loading AOF also may need some modules to exist, and\n * if this instance is a slave, it must understand commands from master. */\nvoid moduleLoadFromQueue(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(server.loadmodule_queue,&li);\n    while((ln = listNext(&li))) {\n        struct moduleLoadQueueEntry *loadmod = ln->value;\n        if (moduleLoad(loadmod->path,(void **)loadmod->argv,loadmod->argc)\n            == C_ERR)\n        {\n            serverLog(LL_WARNING,\n                \"Can't load module from %s: server aborting\",\n                loadmod->path);\n            exit(1);\n        }\n    }\n}\n\nvoid moduleFreeModuleStructure(struct RedisModule *module) {\n    listRelease(module->types);\n    listRelease(module->filters);\n    listRelease(module->usedby);\n    listRelease(module->using);\n    sdsfree(module->name);\n    zfree(module);\n}\n\nvoid moduleUnregisterCommands(struct RedisModule *module) {\n    /* Unregister all the commands registered by this module. */\n    dictIterator *di = dictGetSafeIterator(server.commands);\n    dictEntry *de;\n    while ((de = dictNext(di)) != NULL) {\n        struct redisCommand *cmd = dictGetVal(de);\n        if (cmd->proc == RedisModuleCommandDispatcher) {\n            RedisModuleCommandProxy *cp =\n                (void*)(unsigned long)cmd->getkeys_proc;\n            sds cmdname = cp->rediscmd->name;\n            if (cp->module == module) {\n                dictDelete(server.commands,cmdname);\n                dictDelete(server.orig_commands,cmdname);\n                sdsfree(cmdname);\n                zfree(cp->rediscmd);\n                zfree(cp);\n            }\n        }\n    }\n    dictReleaseIterator(di);\n}\n\n/* Load a module and initialize it. On success C_OK is returned, otherwise\n * C_ERR is returned. */\nint moduleLoad(const char *path, void **module_argv, int module_argc) {\n    int (*onload)(void *, void **, int);\n    void *handle;\n    RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n    ctx.client = moduleFreeContextReusedClient;\n    selectDb(ctx.client, 0);\n\n    struct stat st;\n    if (stat(path, &st) == 0)\n    {   // this check is best effort\n        if (!(st.st_mode & (S_IXUSR  | S_IXGRP | S_IXOTH))) {\n            serverLog(LL_WARNING, \"Module %s failed to load: It does not have execute permissions.\", path);\n            return C_ERR;\n        }\n    }\n\n    handle = dlopen(path,RTLD_NOW|RTLD_LOCAL);\n    if (handle == NULL) {\n        serverLog(LL_WARNING, \"Module %s failed to load: %s\", path, dlerror());\n        return C_ERR;\n    }\n    onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,\"RedisModule_OnLoad\");\n    if (onload == NULL) {\n        dlclose(handle);\n        serverLog(LL_WARNING,\n            \"Module %s does not export RedisModule_OnLoad() \"\n            \"symbol. Module not loaded.\",path);\n        return C_ERR;\n    }\n    if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) {\n        if (ctx.module) {\n            moduleUnregisterCommands(ctx.module);\n            moduleUnregisterSharedAPI(ctx.module);\n            moduleUnregisterUsedAPI(ctx.module);\n            moduleFreeModuleStructure(ctx.module);\n        }\n        dlclose(handle);\n        serverLog(LL_WARNING,\n            \"Module %s initialization failed. Module not loaded\",path);\n        return C_ERR;\n    }\n\n    /* Redis module loaded! Register it. */\n    dictAdd(modules,ctx.module->name,ctx.module);\n    ctx.module->blocked_clients = 0;\n    ctx.module->handle = handle;\n    serverLog(LL_NOTICE,\"Module '%s' loaded from %s\",ctx.module->name,path);\n    /* Fire the loaded modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_MODULE_CHANGE,\n                          REDISMODULE_SUBEVENT_MODULE_LOADED,\n                          ctx.module);\n\n    moduleFreeContext(&ctx);\n    return C_OK;\n}\n\n\n/* Unload the module registered with the specified name. On success\n * C_OK is returned, otherwise C_ERR is returned and errno is set\n * to the following values depending on the type of error:\n *\n * * ENONET: No such module having the specified name.\n * * EBUSY: The module exports a new data type and can only be reloaded. */\nint moduleUnload(sds name) {\n    struct RedisModule *module = dictFetchValue(modules,name);\n\n    if (module == NULL) {\n        errno = ENOENT;\n        return REDISMODULE_ERR;\n    } else if (listLength(module->types)) {\n        errno = EBUSY;\n        return REDISMODULE_ERR;\n    } else if (listLength(module->usedby)) {\n        errno = EPERM;\n        return REDISMODULE_ERR;\n    } else if (module->blocked_clients) {\n        errno = EAGAIN;\n        return REDISMODULE_ERR;\n    }\n\n    /* Give module a chance to clean up. */\n    int (*onunload)(void *);\n    onunload = (int (*)(void *))(unsigned long) dlsym(module->handle, \"RedisModule_OnUnload\");\n    if (onunload) {\n        RedisModuleCtx ctx = REDISMODULE_CTX_INIT;\n        ctx.module = module;\n        ctx.client = moduleFreeContextReusedClient;\n        int unload_status = onunload((void*)&ctx);\n        moduleFreeContext(&ctx);\n\n        if (unload_status == REDISMODULE_ERR) {\n            serverLog(LL_WARNING, \"Module %s OnUnload failed.  Unload canceled.\", name);\n            errno = ECANCELED;\n            return REDISMODULE_ERR;\n        }\n    }\n\n    moduleFreeAuthenticatedClients(module);\n    moduleUnregisterCommands(module);\n    moduleUnregisterSharedAPI(module);\n    moduleUnregisterUsedAPI(module);\n    moduleUnregisterFilters(module);\n\n    /* Remove any notification subscribers this module might have */\n    moduleUnsubscribeNotifications(module);\n    moduleUnsubscribeAllServerEvents(module);\n\n    /* Unload the dynamic library. */\n    if (dlclose(module->handle) == -1) {\n        char *error = dlerror();\n        if (error == NULL) error = \"Unknown error\";\n        serverLog(LL_WARNING,\"Error when trying to close the %s module: %s\",\n            module->name, error);\n    }\n\n    /* Fire the unloaded modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_MODULE_CHANGE,\n                          REDISMODULE_SUBEVENT_MODULE_UNLOADED,\n                          module);\n\n    /* Remove from list of modules. */\n    serverLog(LL_NOTICE,\"Module %s unloaded\",module->name);\n    dictDelete(modules,module->name);\n    module->name = NULL; /* The name was already freed by dictDelete(). */\n    moduleFreeModuleStructure(module);\n\n    return REDISMODULE_OK;\n}\n\n/* Helper function for the MODULE and HELLO command: send the list of the\n * loaded modules to the client. */\nvoid addReplyLoadedModules(client *c) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    addReplyArrayLen(c,dictSize(modules));\n    while ((de = dictNext(di)) != NULL) {\n        sds name = dictGetKey(de);\n        struct RedisModule *module = dictGetVal(de);\n        addReplyMapLen(c,2);\n        addReplyBulkCString(c,\"name\");\n        addReplyBulkCBuffer(c,name,sdslen(name));\n        addReplyBulkCString(c,\"ver\");\n        addReplyLongLong(c,module->ver);\n    }\n    dictReleaseIterator(di);\n}\n\n/* Helper for genModulesInfoString(): given a list of modules, return\n * am SDS string in the form \"[modulename|modulename2|...]\" */\nsds genModulesInfoStringRenderModulesList(list *l) {\n    listIter li;\n    listNode *ln;\n    listRewind(l,&li);\n    sds output = sdsnew(\"[\");\n    while((ln = listNext(&li))) {\n        RedisModule *module = ln->value;\n        output = sdscat(output,module->name);\n    }\n    output = sdstrim(output,\"|\");\n    output = sdscat(output,\"]\");\n    return output;\n}\n\n/* Helper for genModulesInfoString(): render module options as an SDS string. */\nsds genModulesInfoStringRenderModuleOptions(struct RedisModule *module) {\n    sds output = sdsnew(\"[\");\n    if (module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS)\n        output = sdscat(output,\"handle-io-errors|\");\n    output = sdstrim(output,\"|\");\n    output = sdscat(output,\"]\");\n    return output;\n}\n\n\n/* Helper function for the INFO command: adds loaded modules as to info's\n * output.\n *\n * After the call, the passed sds info string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds genModulesInfoString(sds info) {\n    dictIterator *di = dictGetIterator(modules);\n    dictEntry *de;\n\n    while ((de = dictNext(di)) != NULL) {\n        sds name = dictGetKey(de);\n        struct RedisModule *module = dictGetVal(de);\n\n        sds usedby = genModulesInfoStringRenderModulesList(module->usedby);\n        sds using = genModulesInfoStringRenderModulesList(module->using);\n        sds options = genModulesInfoStringRenderModuleOptions(module);\n        info = sdscatfmt(info,\n            \"module:name=%S,ver=%i,api=%i,filters=%i,\"\n            \"usedby=%S,using=%S,options=%S\\r\\n\",\n                name, module->ver, module->apiver,\n                (int)listLength(module->filters), usedby, using, options);\n        sdsfree(usedby);\n        sdsfree(using);\n        sdsfree(options);\n    }\n    dictReleaseIterator(di);\n    return info;\n}\n\n/* Redis MODULE command.\n *\n * MODULE LOAD <path> [args...] */\nvoid moduleCommand(client *c) {\n    char *subcmd = c->argv[1]->ptr;\n    if (c->argc == 2 && !strcasecmp(subcmd,\"help\")) {\n        const char *help[] = {\n\"LIST -- Return a list of loaded modules.\",\n\"LOAD <path> [arg ...] -- Load a module library from <path>.\",\n\"UNLOAD <name> -- Unload a module.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else\n    if (!strcasecmp(subcmd,\"load\") && c->argc >= 3) {\n        robj **argv = NULL;\n        int argc = 0;\n\n        if (c->argc > 3) {\n            argc = c->argc - 3;\n            argv = &c->argv[3];\n        }\n\n        if (moduleLoad(c->argv[2]->ptr,(void **)argv,argc) == C_OK)\n            addReply(c,shared.ok);\n        else\n            addReplyError(c,\n                \"Error loading the extension. Please check the server logs.\");\n    } else if (!strcasecmp(subcmd,\"unload\") && c->argc == 3) {\n        if (moduleUnload(c->argv[2]->ptr) == C_OK)\n            addReply(c,shared.ok);\n        else {\n            char *errmsg;\n            switch(errno) {\n            case ENOENT:\n                errmsg = \"no such module with that name\";\n                break;\n            case EBUSY:\n                errmsg = \"the module exports one or more module-side data \"\n                         \"types, can't unload\";\n                break;\n            case EPERM:\n                errmsg = \"the module exports APIs used by other modules. \"\n                         \"Please unload them first and try again\";\n                break;\n            case EAGAIN:\n                errmsg = \"the module has blocked clients. \"\n                         \"Please wait them unblocked and try again\";\n                break;\n            default:\n                errmsg = \"operation not possible.\";\n                break;\n            }\n            addReplyErrorFormat(c,\"Error unloading module: %s\",errmsg);\n        }\n    } else if (!strcasecmp(subcmd,\"list\") && c->argc == 2) {\n        addReplyLoadedModules(c);\n    } else {\n        addReplySubcommandSyntaxError(c);\n        return;\n    }\n}\n\n/* Return the number of registered modules. */\nsize_t moduleCount(void) {\n    return dictSize(modules);\n}\n\n/* Set the key last access time for LRU based eviction. not relevent if the\n * servers's maxmemory policy is LFU based. Value is idle time in milliseconds.\n * returns REDISMODULE_OK if the LRU was updated, REDISMODULE_ERR otherwise. */\nint RM_SetLRU(RedisModuleKey *key, mstime_t lru_idle) {\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (objectSetLRUOrLFU(key->value, -1, lru_idle, lru_idle>=0 ? LRU_CLOCK() : 0, 1))\n        return REDISMODULE_OK;\n    return REDISMODULE_ERR;\n}\n\n/* Gets the key last access time.\n * Value is idletime in milliseconds or -1 if the server's eviction policy is\n * LFU based.\n * returns REDISMODULE_OK if when key is valid. */\nint RM_GetLRU(RedisModuleKey *key, mstime_t *lru_idle) {\n    *lru_idle = -1;\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU)\n        return REDISMODULE_OK;\n    *lru_idle = estimateObjectIdleTime(key->value);\n    return REDISMODULE_OK;\n}\n\n/* Set the key access frequency. only relevant if the server's maxmemory policy\n * is LFU based.\n * The frequency is a logarithmic counter that provides an indication of\n * the access frequencyonly (must be <= 255).\n * returns REDISMODULE_OK if the LFU was updated, REDISMODULE_ERR otherwise. */\nint RM_SetLFU(RedisModuleKey *key, long long lfu_freq) {\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (objectSetLRUOrLFU(key->value, lfu_freq, -1, 0, 1))\n        return REDISMODULE_OK;\n    return REDISMODULE_ERR;\n}\n\n/* Gets the key access frequency or -1 if the server's eviction policy is not\n * LFU based.\n * returns REDISMODULE_OK if when key is valid. */\nint RM_GetLFU(RedisModuleKey *key, long long *lfu_freq) {\n    *lfu_freq = -1;\n    if (!key->value)\n        return REDISMODULE_ERR;\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU)\n        *lfu_freq = LFUDecrAndReturn(key->value);\n    return REDISMODULE_OK;\n}\n\n/* Replace the value assigned to a module type.\n *\n * The key must be open for writing, have an existing value, and have a moduleType\n * that matches the one specified by the caller.\n *\n * Unlike RM_ModuleTypeSetValue() which will free the old value, this function\n * simply swaps the old value with the new value.\n *\n * The function returns REDISMODULE_OK on success, REDISMODULE_ERR on errors\n * such as:\n *\n * 1. Key is not opened for writing.\n * 2. Key is not a module data type key.\n * 3. Key is a module datatype other than 'mt'.\n *\n * If old_value is non-NULL, the old value is returned by reference.\n */\nint RM_ModuleTypeReplaceValue(RedisModuleKey *key, moduleType *mt, void *new_value, void **old_value) {\n    if (!(key->mode & REDISMODULE_WRITE) || key->iter)\n        return REDISMODULE_ERR;\n    if (!key->value || key->value->type != OBJ_MODULE)\n        return REDISMODULE_ERR;\n\n    moduleValue *mv = key->value->ptr;\n    if (mv->type != mt)\n        return REDISMODULE_ERR;\n\n    if (old_value)\n        *old_value = mv->value;\n    mv->value = new_value;\n\n    return REDISMODULE_OK;\n}\n\n/* Register all the APIs we export. Keep this function at the end of the\n * file so that's easy to seek it to add new entries. */\nvoid moduleRegisterCoreAPI(void) {\n    server.moduleapi = dictCreate(&moduleAPIDictType,NULL);\n    server.sharedapi = dictCreate(&moduleAPIDictType,NULL);\n    REGISTER_API(Alloc);\n    REGISTER_API(Calloc);\n    REGISTER_API(Realloc);\n    REGISTER_API(Free);\n    REGISTER_API(Strdup);\n    REGISTER_API(CreateCommand);\n    REGISTER_API(SetModuleAttribs);\n    REGISTER_API(IsModuleNameBusy);\n    REGISTER_API(WrongArity);\n    REGISTER_API(ReplyWithLongLong);\n    REGISTER_API(ReplyWithError);\n    REGISTER_API(ReplyWithSimpleString);\n    REGISTER_API(ReplyWithArray);\n    REGISTER_API(ReplyWithNullArray);\n    REGISTER_API(ReplyWithEmptyArray);\n    REGISTER_API(ReplySetArrayLength);\n    REGISTER_API(ReplyWithString);\n    REGISTER_API(ReplyWithEmptyString);\n    REGISTER_API(ReplyWithVerbatimString);\n    REGISTER_API(ReplyWithStringBuffer);\n    REGISTER_API(ReplyWithCString);\n    REGISTER_API(ReplyWithNull);\n    REGISTER_API(ReplyWithCallReply);\n    REGISTER_API(ReplyWithDouble);\n    REGISTER_API(ReplyWithLongDouble);\n    REGISTER_API(GetSelectedDb);\n    REGISTER_API(SelectDb);\n    REGISTER_API(OpenKey);\n    REGISTER_API(CloseKey);\n    REGISTER_API(KeyType);\n    REGISTER_API(ValueLength);\n    REGISTER_API(ListPush);\n    REGISTER_API(ListPop);\n    REGISTER_API(StringToLongLong);\n    REGISTER_API(StringToDouble);\n    REGISTER_API(StringToLongDouble);\n    REGISTER_API(Call);\n    REGISTER_API(CallReplyProto);\n    REGISTER_API(FreeCallReply);\n    REGISTER_API(CallReplyInteger);\n    REGISTER_API(CallReplyType);\n    REGISTER_API(CallReplyLength);\n    REGISTER_API(CallReplyArrayElement);\n    REGISTER_API(CallReplyStringPtr);\n    REGISTER_API(CreateStringFromCallReply);\n    REGISTER_API(CreateString);\n    REGISTER_API(CreateStringFromLongLong);\n    REGISTER_API(CreateStringFromDouble);\n    REGISTER_API(CreateStringFromLongDouble);\n    REGISTER_API(CreateStringFromString);\n    REGISTER_API(CreateStringPrintf);\n    REGISTER_API(FreeString);\n    REGISTER_API(StringPtrLen);\n    REGISTER_API(AutoMemory);\n    REGISTER_API(Replicate);\n    REGISTER_API(ReplicateVerbatim);\n    REGISTER_API(DeleteKey);\n    REGISTER_API(UnlinkKey);\n    REGISTER_API(StringSet);\n    REGISTER_API(StringDMA);\n    REGISTER_API(StringTruncate);\n    REGISTER_API(SetExpire);\n    REGISTER_API(GetExpire);\n    REGISTER_API(ResetDataset);\n    REGISTER_API(DbSize);\n    REGISTER_API(RandomKey);\n    REGISTER_API(ZsetAdd);\n    REGISTER_API(ZsetIncrby);\n    REGISTER_API(ZsetScore);\n    REGISTER_API(ZsetRem);\n    REGISTER_API(ZsetRangeStop);\n    REGISTER_API(ZsetFirstInScoreRange);\n    REGISTER_API(ZsetLastInScoreRange);\n    REGISTER_API(ZsetFirstInLexRange);\n    REGISTER_API(ZsetLastInLexRange);\n    REGISTER_API(ZsetRangeCurrentElement);\n    REGISTER_API(ZsetRangeNext);\n    REGISTER_API(ZsetRangePrev);\n    REGISTER_API(ZsetRangeEndReached);\n    REGISTER_API(HashSet);\n    REGISTER_API(HashGet);\n    REGISTER_API(IsKeysPositionRequest);\n    REGISTER_API(KeyAtPos);\n    REGISTER_API(GetClientId);\n    REGISTER_API(GetContextFlags);\n    REGISTER_API(AvoidReplicaTraffic);\n    REGISTER_API(PoolAlloc);\n    REGISTER_API(CreateDataType);\n    REGISTER_API(ModuleTypeSetValue);\n    REGISTER_API(ModuleTypeReplaceValue);\n    REGISTER_API(ModuleTypeGetType);\n    REGISTER_API(ModuleTypeGetValue);\n    REGISTER_API(IsIOError);\n    REGISTER_API(SetModuleOptions);\n    REGISTER_API(SignalModifiedKey);\n    REGISTER_API(SaveUnsigned);\n    REGISTER_API(LoadUnsigned);\n    REGISTER_API(SaveSigned);\n    REGISTER_API(LoadSigned);\n    REGISTER_API(SaveString);\n    REGISTER_API(SaveStringBuffer);\n    REGISTER_API(LoadString);\n    REGISTER_API(LoadStringBuffer);\n    REGISTER_API(SaveDouble);\n    REGISTER_API(LoadDouble);\n    REGISTER_API(SaveFloat);\n    REGISTER_API(LoadFloat);\n    REGISTER_API(SaveLongDouble);\n    REGISTER_API(LoadLongDouble);\n    REGISTER_API(SaveDataTypeToString);\n    REGISTER_API(LoadDataTypeFromString);\n    REGISTER_API(EmitAOF);\n    REGISTER_API(Log);\n    REGISTER_API(LogIOError);\n    REGISTER_API(_Assert);\n    REGISTER_API(LatencyAddSample);\n    REGISTER_API(StringAppendBuffer);\n    REGISTER_API(RetainString);\n    REGISTER_API(StringCompare);\n    REGISTER_API(GetContextFromIO);\n    REGISTER_API(GetKeyNameFromIO);\n    REGISTER_API(GetKeyNameFromModuleKey);\n    REGISTER_API(BlockClient);\n    REGISTER_API(UnblockClient);\n    REGISTER_API(IsBlockedReplyRequest);\n    REGISTER_API(IsBlockedTimeoutRequest);\n    REGISTER_API(GetBlockedClientPrivateData);\n    REGISTER_API(AbortBlock);\n    REGISTER_API(Milliseconds);\n    REGISTER_API(GetThreadSafeContext);\n    REGISTER_API(FreeThreadSafeContext);\n    REGISTER_API(ThreadSafeContextLock);\n    REGISTER_API(ThreadSafeContextUnlock);\n    REGISTER_API(DigestAddStringBuffer);\n    REGISTER_API(DigestAddLongLong);\n    REGISTER_API(DigestEndSequence);\n    REGISTER_API(NotifyKeyspaceEvent);\n    REGISTER_API(GetNotifyKeyspaceEvents);\n    REGISTER_API(SubscribeToKeyspaceEvents);\n    REGISTER_API(RegisterClusterMessageReceiver);\n    REGISTER_API(SendClusterMessage);\n    REGISTER_API(GetClusterNodeInfo);\n    REGISTER_API(GetClusterNodesList);\n    REGISTER_API(FreeClusterNodesList);\n    REGISTER_API(CreateTimer);\n    REGISTER_API(StopTimer);\n    REGISTER_API(GetTimerInfo);\n    REGISTER_API(GetMyClusterID);\n    REGISTER_API(GetClusterSize);\n    REGISTER_API(GetRandomBytes);\n    REGISTER_API(GetRandomHexChars);\n    REGISTER_API(BlockedClientDisconnected);\n    REGISTER_API(SetDisconnectCallback);\n    REGISTER_API(GetBlockedClientHandle);\n    REGISTER_API(SetClusterFlags);\n    REGISTER_API(CreateDict);\n    REGISTER_API(FreeDict);\n    REGISTER_API(DictSize);\n    REGISTER_API(DictSetC);\n    REGISTER_API(DictReplaceC);\n    REGISTER_API(DictSet);\n    REGISTER_API(DictReplace);\n    REGISTER_API(DictGetC);\n    REGISTER_API(DictGet);\n    REGISTER_API(DictDelC);\n    REGISTER_API(DictDel);\n    REGISTER_API(DictIteratorStartC);\n    REGISTER_API(DictIteratorStart);\n    REGISTER_API(DictIteratorStop);\n    REGISTER_API(DictIteratorReseekC);\n    REGISTER_API(DictIteratorReseek);\n    REGISTER_API(DictNextC);\n    REGISTER_API(DictPrevC);\n    REGISTER_API(DictNext);\n    REGISTER_API(DictPrev);\n    REGISTER_API(DictCompareC);\n    REGISTER_API(DictCompare);\n    REGISTER_API(ExportSharedAPI);\n    REGISTER_API(GetSharedAPI);\n    REGISTER_API(RegisterCommandFilter);\n    REGISTER_API(UnregisterCommandFilter);\n    REGISTER_API(CommandFilterArgsCount);\n    REGISTER_API(CommandFilterArgGet);\n    REGISTER_API(CommandFilterArgInsert);\n    REGISTER_API(CommandFilterArgReplace);\n    REGISTER_API(CommandFilterArgDelete);\n    REGISTER_API(Fork);\n    REGISTER_API(ExitFromChild);\n    REGISTER_API(KillForkChild);\n    REGISTER_API(RegisterInfoFunc);\n    REGISTER_API(InfoAddSection);\n    REGISTER_API(InfoBeginDictField);\n    REGISTER_API(InfoEndDictField);\n    REGISTER_API(InfoAddFieldString);\n    REGISTER_API(InfoAddFieldCString);\n    REGISTER_API(InfoAddFieldDouble);\n    REGISTER_API(InfoAddFieldLongLong);\n    REGISTER_API(InfoAddFieldULongLong);\n    REGISTER_API(GetServerInfo);\n    REGISTER_API(FreeServerInfo);\n    REGISTER_API(ServerInfoGetField);\n    REGISTER_API(ServerInfoGetFieldC);\n    REGISTER_API(ServerInfoGetFieldSigned);\n    REGISTER_API(ServerInfoGetFieldUnsigned);\n    REGISTER_API(ServerInfoGetFieldDouble);\n    REGISTER_API(GetClientInfoById);\n    REGISTER_API(PublishMessage);\n    REGISTER_API(SubscribeToServerEvent);\n    REGISTER_API(SetLRU);\n    REGISTER_API(GetLRU);\n    REGISTER_API(SetLFU);\n    REGISTER_API(GetLFU);\n    REGISTER_API(BlockClientOnKeys);\n    REGISTER_API(SignalKeyAsReady);\n    REGISTER_API(GetBlockedClientReadyKey);\n    REGISTER_API(GetUsedMemoryRatio);\n    REGISTER_API(MallocSize);\n    REGISTER_API(ScanCursorCreate);\n    REGISTER_API(ScanCursorDestroy);\n    REGISTER_API(ScanCursorRestart);\n    REGISTER_API(Scan);\n    REGISTER_API(ScanKey);\n    REGISTER_API(CreateModuleUser);\n    REGISTER_API(SetModuleUserACL);\n    REGISTER_API(FreeModuleUser);\n    REGISTER_API(DeauthenticateAndCloseClient);\n    REGISTER_API(AuthenticateClientWithACLUser);\n    REGISTER_API(AuthenticateClientWithUser);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/.gitignore",
    "content": "*.so\n*.xo\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/Makefile",
    "content": "\n# find the OS\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\n# Compile flags for linux / osx\nifeq ($(uname_S),Linux)\n\tSHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -shared\nelse\n\tSHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup\nendif\n\n.SUFFIXES: .c .so .xo .o\n\nall: helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so hellohook.so helloacl.so\n\n.c.xo:\n\t$(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@\n\nhelloworld.xo: ../redismodule.h\n\nhelloworld.so: helloworld.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellotype.xo: ../redismodule.h\n\nhellotype.so: hellotype.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhelloblock.xo: ../redismodule.h\n\nhelloblock.so: helloblock.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lpthread -lc\n\nhellocluster.xo: ../redismodule.h\n\nhellocluster.so: hellocluster.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellotimer.xo: ../redismodule.h\n\nhellotimer.so: hellotimer.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellodict.xo: ../redismodule.h\n\nhellodict.so: hellodict.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhellohook.xo: ../redismodule.h\n\nhellohook.so: hellohook.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nhelloacl.xo: ../redismodule.h\n\nhelloacl.so: helloacl.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\ntestmodule.xo: ../redismodule.h\n\ntestmodule.so: testmodule.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\nclean:\n\trm -rf *.xo *.so\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/gendoc.rb",
    "content": "# gendoc.rb -- Converts the top-comments inside module.c to modules API\n#              reference documentation in markdown format.\n\n# Convert the C comment to markdown\ndef markdown(s)\n    s = s.gsub(/\\*\\/$/,\"\")\n    s = s.gsub(/^ \\* {0,1}/,\"\")\n    s = s.gsub(/^\\/\\* /,\"\")\n    s.chop! while s[-1] == \"\\n\" || s[-1] == \" \"\n    lines = s.split(\"\\n\")\n    newlines = []\n    lines.each{|l|\n        if l[0] != ' '\n            l = l.gsub(/RM_[A-z()]+/){|x| \"`#{x}`\"}\n            l = l.gsub(/RedisModule_[A-z()]+/){|x| \"`#{x}`\"}\n            l = l.gsub(/REDISMODULE_[A-z]+/){|x| \"`#{x}`\"}\n        end\n        newlines << l\n    }\n    return newlines.join(\"\\n\")\nend\n\n# Given the source code array and the index at which an exported symbol was\n# detected, extracts and outputs the documentation.\ndef docufy(src,i)\n    m = /RM_[A-z0-9]+/.match(src[i])\n    name = m[0]\n    name = name.sub(\"RM_\",\"RedisModule_\")\n    proto = src[i].sub(\"{\",\"\").strip+\";\\n\"\n    proto = proto.sub(\"RM_\",\"RedisModule_\")\n    puts \"## `#{name}`\\n\\n\"\n    puts \"    #{proto}\\n\"\n    comment = \"\"\n    while true\n        i = i-1\n        comment = src[i]+comment\n        break if src[i] =~ /\\/\\*/\n    end\n    comment = markdown(comment)\n    puts comment+\"\\n\\n\"\nend\n\nputs \"# Modules API reference\\n\\n\"\nsrc = File.open(\"../module.c\").to_a\nsrc.each_with_index{|line,i|\n    if line =~ /RM_/ && line[0] != ' ' && line[0] != '#' && line[0] != '/'\n        if src[i-1] =~ /\\*\\//\n            docufy(src,i)\n        end\n    end\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/helloacl.c",
    "content": "/* ACL API example - An example for performing custom synchronous and\n * asynchronous password authentication.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright 2019 Amazon.com, Inc. or its affiliates.\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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <pthread.h>\n#include <unistd.h>\n\n// A simple global user\nstatic RedisModuleUser *global;\nstatic uint64_t global_auth_client_id = 0;\n\n/* HELLOACL.REVOKE \n * Synchronously revoke access from a user. */\nint RevokeCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (global_auth_client_id) {\n        RedisModule_DeauthenticateAndCloseClient(ctx, global_auth_client_id);\n        return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n    } else {\n        return RedisModule_ReplyWithError(ctx, \"Global user currently not used\");    \n    }\n}\n\n/* HELLOACL.RESET \n * Synchronously delete and re-create a module user. */\nint ResetCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_FreeModuleUser(global);\n    global = RedisModule_CreateModuleUser(\"global\");\n    RedisModule_SetModuleUserACL(global, \"allcommands\");\n    RedisModule_SetModuleUserACL(global, \"allkeys\");\n    RedisModule_SetModuleUserACL(global, \"on\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* Callback handler for user changes, use this to notify a module of \n * changes to users authenticated by the module */\nvoid HelloACL_UserChanged(uint64_t client_id, void *privdata) {\n    REDISMODULE_NOT_USED(privdata);\n    REDISMODULE_NOT_USED(client_id);\n    global_auth_client_id = 0;\n}\n\n/* HELLOACL.AUTHGLOBAL \n * Synchronously assigns a module user to the current context. */\nint AuthGlobalCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (global_auth_client_id) {\n        return RedisModule_ReplyWithError(ctx, \"Global user currently used\");    \n    }\n\n    RedisModule_AuthenticateClientWithUser(ctx, global, HelloACL_UserChanged, NULL, &global_auth_client_id);\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n#define TIMEOUT_TIME 1000\n\n/* Reply callback for auth command HELLOACL.AUTHASYNC */\nint HelloACL_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    size_t length;\n\n    RedisModuleString *user_string = RedisModule_GetBlockedClientPrivateData(ctx);\n    const char *name = RedisModule_StringPtrLen(user_string, &length);\n\n    if (RedisModule_AuthenticateClientWithACLUser(ctx, name, length, NULL, NULL, NULL) == \n            REDISMODULE_ERR) {\n        return RedisModule_ReplyWithError(ctx, \"Invalid Username or password\");    \n    }\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* Timeout callback for auth command HELLOACL.AUTHASYNC */\nint HelloACL_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\n/* Private data frees data for HELLOACL.AUTHASYNC command. */\nvoid HelloACL_FreeData(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_FreeString(NULL, privdata);\n}\n\n/* Background authentication can happen here. */\nvoid *HelloACL_ThreadMain(void *args) {\n    void **targs = args;\n    RedisModuleBlockedClient *bc = targs[0];\n    RedisModuleString *user = targs[1];\n    RedisModule_Free(targs);\n\n    RedisModule_UnblockClient(bc,user);\n    return NULL;\n}\n\n/* HELLOACL.AUTHASYNC \n * Asynchronously assigns an ACL user to the current context. */\nint AuthAsyncCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    pthread_t tid;\n    RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, HelloACL_Reply, HelloACL_Timeout, HelloACL_FreeData, TIMEOUT_TIME);\n    \n\n    void **targs = RedisModule_Alloc(sizeof(void*)*2);\n    targs[0] = bc;\n    targs[1] = RedisModule_CreateStringFromString(NULL, argv[1]);\n\n    if (pthread_create(&tid, NULL, HelloACL_ThreadMain, targs) != 0) {\n        RedisModule_AbortBlock(bc);\n        return RedisModule_ReplyWithError(ctx, \"-ERR Can't start thread\");\n    }\n\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"helloacl\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.reset\",\n        ResetCommand_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.revoke\",\n        RevokeCommand_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.authglobal\",\n        AuthGlobalCommand_RedisCommand,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"helloacl.authasync\",\n        AuthAsyncCommand_RedisCommand,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    global = RedisModule_CreateModuleUser(\"global\");\n    RedisModule_SetModuleUserACL(global, \"allcommands\");\n    RedisModule_SetModuleUserACL(global, \"allkeys\");\n    RedisModule_SetModuleUserACL(global, \"on\");\n\n    global_auth_client_id = 0;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/helloblock.c",
    "content": "/* Helloblock module -- An example of blocking command implementation\n * with threads.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <pthread.h>\n#include <unistd.h>\n\n/* Reply callback for blocking command HELLO.BLOCK */\nint HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    int *myint = RedisModule_GetBlockedClientPrivateData(ctx);\n    return RedisModule_ReplyWithLongLong(ctx,*myint);\n}\n\n/* Timeout callback for blocking command HELLO.BLOCK */\nint HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx,\"Request timedout\");\n}\n\n/* Private data freeing callback for HELLO.BLOCK command. */\nvoid HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_Free(privdata);\n}\n\n/* The thread entry point that actually executes the blocking part\n * of the command HELLO.BLOCK. */\nvoid *HelloBlock_ThreadMain(void *arg) {\n    void **targ = arg;\n    RedisModuleBlockedClient *bc = targ[0];\n    long long delay = (unsigned long)targ[1];\n    RedisModule_Free(targ);\n\n    sleep(delay);\n    int *r = RedisModule_Alloc(sizeof(int));\n    *r = rand();\n    RedisModule_UnblockClient(bc,r);\n    return NULL;\n}\n\n/* An example blocked client disconnection callback.\n *\n * Note that in the case of the HELLO.BLOCK command, the blocked client is now\n * owned by the thread calling sleep(). In this specific case, there is not\n * much we can do, however normally we could instead implement a way to\n * signal the thread that the client disconnected, and sleep the specified\n * amount of seconds with a while loop calling sleep(1), so that once we\n * detect the client disconnection, we can terminate the thread ASAP. */\nvoid HelloBlock_Disconnected(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc) {\n    RedisModule_Log(ctx,\"warning\",\"Blocked client %p disconnected!\",\n        (void*)bc);\n\n    /* Here you should cleanup your state / threads, and if possible\n     * call RedisModule_UnblockClient(), or notify the thread that will\n     * call the function ASAP. */\n}\n\n/* HELLO.BLOCK <delay> <timeout> -- Block for <count> seconds, then reply with\n * a random number. Timeout is the command timeout, so that you can test\n * what happens when the delay is greater than the timeout. */\nint HelloBlock_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n    long long delay;\n    long long timeout;\n\n    if (RedisModule_StringToLongLong(argv[1],&delay) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    pthread_t tid;\n    RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout);\n\n    /* Here we set a disconnection handler, however since this module will\n     * block in sleep() in a thread, there is not much we can do in the\n     * callback, so this is just to show you the API. */\n    RedisModule_SetDisconnectCallback(bc,HelloBlock_Disconnected);\n\n    /* Now that we setup a blocking client, we need to pass the control\n     * to the thread. However we need to pass arguments to the thread:\n     * the delay and a reference to the blocked client handle. */\n    void **targ = RedisModule_Alloc(sizeof(void*)*2);\n    targ[0] = bc;\n    targ[1] = (void*)(unsigned long) delay;\n\n    if (pthread_create(&tid,NULL,HelloBlock_ThreadMain,targ) != 0) {\n        RedisModule_AbortBlock(bc);\n        return RedisModule_ReplyWithError(ctx,\"-ERR Can't start thread\");\n    }\n    return REDISMODULE_OK;\n}\n\n/* The thread entry point that actually executes the blocking part\n * of the command HELLO.KEYS.\n *\n * Note: this implementation is very simple on purpose, so no duplicated\n * keys (returned by SCAN) are filtered. However adding such a functionality\n * would be trivial just using any data structure implementing a dictionary\n * in order to filter the duplicated items. */\nvoid *HelloKeys_ThreadMain(void *arg) {\n    RedisModuleBlockedClient *bc = arg;\n    RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bc);\n    long long cursor = 0;\n    size_t replylen = 0;\n\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    do {\n        RedisModule_ThreadSafeContextLock(ctx);\n        RedisModuleCallReply *reply = RedisModule_Call(ctx,\n            \"SCAN\",\"l\",(long long)cursor);\n        RedisModule_ThreadSafeContextUnlock(ctx);\n\n        RedisModuleCallReply *cr_cursor =\n            RedisModule_CallReplyArrayElement(reply,0);\n        RedisModuleCallReply *cr_keys =\n            RedisModule_CallReplyArrayElement(reply,1);\n\n        RedisModuleString *s = RedisModule_CreateStringFromCallReply(cr_cursor);\n        RedisModule_StringToLongLong(s,&cursor);\n        RedisModule_FreeString(ctx,s);\n\n        size_t items = RedisModule_CallReplyLength(cr_keys);\n        for (size_t j = 0; j < items; j++) {\n            RedisModuleCallReply *ele =\n                RedisModule_CallReplyArrayElement(cr_keys,j);\n            RedisModule_ReplyWithCallReply(ctx,ele);\n            replylen++;\n        }\n        RedisModule_FreeCallReply(reply);\n    } while (cursor != 0);\n    RedisModule_ReplySetArrayLength(ctx,replylen);\n\n    RedisModule_FreeThreadSafeContext(ctx);\n    RedisModule_UnblockClient(bc,NULL);\n    return NULL;\n}\n\n/* HELLO.KEYS -- Return all the keys in the current database without blocking\n * the server. The keys do not represent a point-in-time state so only the keys\n * that were in the database from the start to the end are guaranteed to be\n * there. */\nint HelloKeys_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    if (argc != 1) return RedisModule_WrongArity(ctx);\n\n    pthread_t tid;\n\n    /* Note that when blocking the client we do not set any callback: no\n     * timeout is possible since we passed '0', nor we need a reply callback\n     * because we'll use the thread safe context to accumulate a reply. */\n    RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,NULL,NULL,NULL,0);\n\n    /* Now that we setup a blocking client, we need to pass the control\n     * to the thread. However we need to pass arguments to the thread:\n     * the reference to the blocked client handle. */\n    if (pthread_create(&tid,NULL,HelloKeys_ThreadMain,bc) != 0) {\n        RedisModule_AbortBlock(bc);\n        return RedisModule_ReplyWithError(ctx,\"-ERR Can't start thread\");\n    }\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"helloblock\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.block\",\n        HelloBlock_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"hello.keys\",\n        HelloKeys_RedisCommand,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/hellocluster.c",
    "content": "/* Helloworld cluster -- A ping/pong cluster API example.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2018, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n#define MSGTYPE_PING 1\n#define MSGTYPE_PONG 2\n\n/* HELLOCLUSTER.PINGALL */\nint PingallCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_SendClusterMessage(ctx,NULL,MSGTYPE_PING,(unsigned char*)\"Hey\",3);\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* HELLOCLUSTER.LIST */\nint ListCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    size_t numnodes;\n    char **ids = RedisModule_GetClusterNodesList(ctx,&numnodes);\n    if (ids == NULL) {\n        return RedisModule_ReplyWithError(ctx,\"Cluster not enabled\");\n    }\n\n    RedisModule_ReplyWithArray(ctx,numnodes);\n    for (size_t j = 0; j < numnodes; j++) {\n        int port;\n        RedisModule_GetClusterNodeInfo(ctx,ids[j],NULL,NULL,&port,NULL);\n        RedisModule_ReplyWithArray(ctx,2);\n        RedisModule_ReplyWithStringBuffer(ctx,ids[j],REDISMODULE_NODE_ID_LEN);\n        RedisModule_ReplyWithLongLong(ctx,port);\n    }\n    RedisModule_FreeClusterNodesList(ids);\n    return REDISMODULE_OK;\n}\n\n/* Callback for message MSGTYPE_PING */\nvoid PingReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {\n    RedisModule_Log(ctx,\"notice\",\"PING (type %d) RECEIVED from %.*s: '%.*s'\",\n        type,REDISMODULE_NODE_ID_LEN,sender_id,(int)len, payload);\n    RedisModule_SendClusterMessage(ctx,NULL,MSGTYPE_PONG,(unsigned char*)\"Ohi!\",4);\n    RedisModule_Call(ctx, \"INCR\", \"c\", \"pings_received\");\n}\n\n/* Callback for message MSGTYPE_PONG. */\nvoid PongReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {\n    RedisModule_Log(ctx,\"notice\",\"PONG (type %d) RECEIVED from %.*s: '%.*s'\",\n        type,REDISMODULE_NODE_ID_LEN,sender_id,(int)len, payload);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellocluster\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellocluster.pingall\",\n        PingallCommand_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellocluster.list\",\n        ListCommand_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    /* Disable Redis Cluster sharding and redirections. This way every node\n     * will be able to access every possible key, regardless of the hash slot.\n     * This way the PING message handler will be able to increment a specific\n     * variable. Normally you do that in order for the distributed system\n     * you create as a module to have total freedom in the keyspace\n     * manipulation. */\n    RedisModule_SetClusterFlags(ctx,REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION);\n\n    /* Register our handlers for different message types. */\n    RedisModule_RegisterClusterMessageReceiver(ctx,MSGTYPE_PING,PingReceiver);\n    RedisModule_RegisterClusterMessageReceiver(ctx,MSGTYPE_PONG,PongReceiver);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/hellodict.c",
    "content": "/* Hellodict -- An example of modules dictionary API\n *\n * This module implements a volatile key-value store on top of the\n * dictionary exported by the Redis modules API.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2018, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\nstatic RedisModuleDict *Keyspace;\n\n/* HELLODICT.SET <key> <value>\n *\n * Set the specified key to the specified value. */\nint cmd_SET(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n    RedisModule_DictSet(Keyspace,argv[1],argv[2]);\n    /* We need to keep a reference to the value stored at the key, otherwise\n     * it would be freed when this callback returns. */\n    RedisModule_RetainString(NULL,argv[2]);\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* HELLODICT.GET <key>\n *\n * Return the value of the specified key, or a null reply if the key\n * is not defined. */\nint cmd_GET(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n    RedisModuleString *val = RedisModule_DictGet(Keyspace,argv[1],NULL);\n    if (val == NULL) {\n        return RedisModule_ReplyWithNull(ctx);\n    } else {\n        return RedisModule_ReplyWithString(ctx, val);\n    }\n}\n\n/* HELLODICT.KEYRANGE <startkey> <endkey> <count>\n *\n * Return a list of matching keys, lexicographically between startkey\n * and endkey. No more than 'count' items are emitted. */\nint cmd_KEYRANGE(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    /* Parse the count argument. */\n    long long count;\n    if (RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    /* Seek the iterator. */\n    RedisModuleDictIter *iter = RedisModule_DictIteratorStart(\n        Keyspace, \">=\", argv[1]);\n\n    /* Reply with the matching items. */\n    char *key;\n    size_t keylen;\n    long long replylen = 0; /* Keep track of the amitted array len. */\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    while((key = RedisModule_DictNextC(iter,&keylen,NULL)) != NULL) {\n        if (replylen >= count) break;\n        if (RedisModule_DictCompare(iter,\"<=\",argv[2]) == REDISMODULE_ERR)\n            break;\n        RedisModule_ReplyWithStringBuffer(ctx,key,keylen);\n        replylen++;\n    }\n    RedisModule_ReplySetArrayLength(ctx,replylen);\n\n    /* Cleanup. */\n    RedisModule_DictIteratorStop(iter);\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellodict\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellodict.set\",\n        cmd_SET,\"write deny-oom\",1,1,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellodict.get\",\n        cmd_GET,\"readonly\",1,1,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellodict.keyrange\",\n        cmd_KEYRANGE,\"readonly\",1,1,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    /* Create our global dictionray. Here we'll set our keys and values. */\n    Keyspace = RedisModule_CreateDict(NULL);\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/hellohook.c",
    "content": "/* Server hooks API example\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2019, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n/* Client state change callback. */\nvoid clientChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(ctx);\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleClientInfo *ci = data;\n    printf(\"Client %s event for client #%llu %s:%d\\n\",\n        (sub == REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED) ?\n            \"connection\" : \"disconnection\",\n        ci->id,ci->addr,ci->port);\n}\n\nvoid flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(ctx);\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleFlushInfo *fi = data;\n    if (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) {\n        if (fi->dbnum != -1) {\n            RedisModuleCallReply *reply;\n            reply = RedisModule_Call(ctx,\"DBSIZE\",\"\");\n            long long numkeys = RedisModule_CallReplyInteger(reply);\n            printf(\"FLUSHDB event of database %d started (%lld keys in DB)\\n\",\n                fi->dbnum, numkeys);\n            RedisModule_FreeCallReply(reply);\n        } else {\n            printf(\"FLUSHALL event started\\n\");\n        }\n    } else {\n        if (fi->dbnum != -1) {\n            printf(\"FLUSHDB event of database %d ended\\n\",fi->dbnum);\n        } else {\n            printf(\"FLUSHALL event ended\\n\");\n        }\n    }\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellohook\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ClientChange, clientChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_FlushDB, flushdbCallback);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/hellotimer.c",
    "content": "/* Timer API example -- Register and handle timer events\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2018, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n/* Timer callback. */\nvoid timerHandler(RedisModuleCtx *ctx, void *data) {\n    REDISMODULE_NOT_USED(ctx);\n    printf(\"Fired %s!\\n\", data);\n    RedisModule_Free(data);\n}\n\n/* HELLOTIMER.TIMER*/\nint TimerCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    for (int j = 0; j < 10; j++) {\n        int delay = rand() % 5000;\n        char *buf = RedisModule_Alloc(256);\n        snprintf(buf,256,\"After %d\", delay);\n        RedisModuleTimerID tid = RedisModule_CreateTimer(ctx,delay,timerHandler,buf);\n        REDISMODULE_NOT_USED(tid);\n    }\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellotimer\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotimer.timer\",\n        TimerCommand_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/hellotype.c",
    "content": "/* This file implements a new module native data type called \"HELLOTYPE\".\n * The data structure implemented is a very simple ordered linked list of\n * 64 bit integers, in order to have something that is real world enough, but\n * at the same time, extremely simple to understand, to show how the API\n * works, how a new data type is created, and how to write basic methods\n * for RDB loading, saving and AOF rewriting.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n#include <stdint.h>\n\nstatic RedisModuleType *HelloType;\n\n/* ========================== Internal data structure  =======================\n * This is just a linked list of 64 bit integers where elements are inserted\n * in-place, so it's ordered. There is no pop/push operation but just insert\n * because it is enough to show the implementation of new data types without\n * making things complex. */\n\nstruct HelloTypeNode {\n    int64_t value;\n    struct HelloTypeNode *next;\n};\n\nstruct HelloTypeObject {\n    struct HelloTypeNode *head;\n    size_t len; /* Number of elements added. */\n};\n\nstruct HelloTypeObject *createHelloTypeObject(void) {\n    struct HelloTypeObject *o;\n    o = RedisModule_Alloc(sizeof(*o));\n    o->head = NULL;\n    o->len = 0;\n    return o;\n}\n\nvoid HelloTypeInsert(struct HelloTypeObject *o, int64_t ele) {\n    struct HelloTypeNode *next = o->head, *newnode, *prev = NULL;\n\n    while(next && next->value < ele) {\n        prev = next;\n        next = next->next;\n    }\n    newnode = RedisModule_Alloc(sizeof(*newnode));\n    newnode->value = ele;\n    newnode->next = next;\n    if (prev) {\n        prev->next = newnode;\n    } else {\n        o->head = newnode;\n    }\n    o->len++;\n}\n\nvoid HelloTypeReleaseObject(struct HelloTypeObject *o) {\n    struct HelloTypeNode *cur, *next;\n    cur = o->head;\n    while(cur) {\n        next = cur->next;\n        RedisModule_Free(cur);\n        cur = next;\n    }\n    RedisModule_Free(o);\n}\n\n/* ========================= \"hellotype\" type commands ======================= */\n\n/* HELLOTYPE.INSERT key value */\nint HelloTypeInsert_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long value;\n    if ((RedisModule_StringToLongLong(argv[2],&value) != REDISMODULE_OK)) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid value: must be a signed 64 bit integer\");\n    }\n\n    /* Create an empty value object if the key is currently empty. */\n    struct HelloTypeObject *hto;\n    if (type == REDISMODULE_KEYTYPE_EMPTY) {\n        hto = createHelloTypeObject();\n        RedisModule_ModuleTypeSetValue(key,HelloType,hto);\n    } else {\n        hto = RedisModule_ModuleTypeGetValue(key);\n    }\n\n    /* Insert the new element. */\n    HelloTypeInsert(hto,value);\n    RedisModule_SignalKeyAsReady(ctx,argv[1]);\n\n    RedisModule_ReplyWithLongLong(ctx,hto->len);\n    RedisModule_ReplicateVerbatim(ctx);\n    return REDISMODULE_OK;\n}\n\n/* HELLOTYPE.RANGE key first count */\nint HelloTypeRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long first, count;\n    if (RedisModule_StringToLongLong(argv[2],&first) != REDISMODULE_OK ||\n        RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK ||\n        first < 0 || count < 0)\n    {\n        return RedisModule_ReplyWithError(ctx,\n            \"ERR invalid first or count parameters\");\n    }\n\n    struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key);\n    struct HelloTypeNode *node = hto ? hto->head : NULL;\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    long long arraylen = 0;\n    while(node && count--) {\n        RedisModule_ReplyWithLongLong(ctx,node->value);\n        arraylen++;\n        node = node->next;\n    }\n    RedisModule_ReplySetArrayLength(ctx,arraylen);\n    return REDISMODULE_OK;\n}\n\n/* HELLOTYPE.LEN key */\nint HelloTypeLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    struct HelloTypeObject *hto = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_ReplyWithLongLong(ctx,hto ? hto->len : 0);\n    return REDISMODULE_OK;\n}\n\n/* ====================== Example of a blocking command ==================== */\n\n/* Reply callback for blocking command HELLOTYPE.BRANGE, this will get\n * called when the key we blocked for is ready: we need to check if we\n * can really serve the client, and reply OK or ERR accordingly. */\nint HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_READ);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_MODULE ||\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        RedisModule_CloseKey(key);\n        return REDISMODULE_ERR;\n    }\n\n    /* In case the key is able to serve our blocked client, let's directly\n     * use our original command implementation to make this example simpler. */\n    RedisModule_CloseKey(key);\n    return HelloTypeRange_RedisCommand(ctx,argv,argc-1);\n}\n\n/* Timeout callback for blocking command HELLOTYPE.BRANGE */\nint HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx,\"Request timedout\");\n}\n\n/* Private data freeing callback for HELLOTYPE.BRANGE command. */\nvoid HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_Free(privdata);\n}\n\n/* HELLOTYPE.BRANGE key first count timeout -- This is a blocking verison of\n * the RANGE operation, in order to show how to use the API\n * RedisModule_BlockClientOnKeys(). */\nint HelloTypeBRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 5) return RedisModule_WrongArity(ctx);\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != HelloType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    /* Parse the timeout before even trying to serve the client synchronously,\n     * so that we always fail ASAP on syntax errors. */\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[4],&timeout) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\n            \"ERR invalid timeout parameter\");\n    }\n\n    /* Can we serve the reply synchronously? */\n    if (type != REDISMODULE_KEYTYPE_EMPTY) {\n        return HelloTypeRange_RedisCommand(ctx,argv,argc-1);\n    }\n\n    /* Otherwise let's block on the key. */\n    void *privdata = RedisModule_Alloc(100);\n    RedisModule_BlockClientOnKeys(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout,argv+1,1,privdata);\n    return REDISMODULE_OK;\n}\n\n/* ========================== \"hellotype\" type methods ======================= */\n\nvoid *HelloTypeRdbLoad(RedisModuleIO *rdb, int encver) {\n    if (encver != 0) {\n        /* RedisModule_Log(\"warning\",\"Can't load data with version %d\", encver);*/\n        return NULL;\n    }\n    uint64_t elements = RedisModule_LoadUnsigned(rdb);\n    struct HelloTypeObject *hto = createHelloTypeObject();\n    while(elements--) {\n        int64_t ele = RedisModule_LoadSigned(rdb);\n        HelloTypeInsert(hto,ele);\n    }\n    return hto;\n}\n\nvoid HelloTypeRdbSave(RedisModuleIO *rdb, void *value) {\n    struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    RedisModule_SaveUnsigned(rdb,hto->len);\n    while(node) {\n        RedisModule_SaveSigned(rdb,node->value);\n        node = node->next;\n    }\n}\n\nvoid HelloTypeAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) {\n    struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    while(node) {\n        RedisModule_EmitAOF(aof,\"HELLOTYPE.INSERT\",\"sl\",key,node->value);\n        node = node->next;\n    }\n}\n\n/* The goal of this function is to return the amount of memory used by\n * the HelloType value. */\nsize_t HelloTypeMemUsage(const void *value) {\n    const struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    return sizeof(*hto) + sizeof(*node)*hto->len;\n}\n\nvoid HelloTypeFree(void *value) {\n    HelloTypeReleaseObject(value);\n}\n\nvoid HelloTypeDigest(RedisModuleDigest *md, void *value) {\n    struct HelloTypeObject *hto = value;\n    struct HelloTypeNode *node = hto->head;\n    while(node) {\n        RedisModule_DigestAddLongLong(md,node->value);\n        node = node->next;\n    }\n    RedisModule_DigestEndSequence(md);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"hellotype\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    RedisModuleTypeMethods tm = {\n        .version = REDISMODULE_TYPE_METHOD_VERSION,\n        .rdb_load = HelloTypeRdbLoad,\n        .rdb_save = HelloTypeRdbSave,\n        .aof_rewrite = HelloTypeAofRewrite,\n        .mem_usage = HelloTypeMemUsage,\n        .free = HelloTypeFree,\n        .digest = HelloTypeDigest\n    };\n\n    HelloType = RedisModule_CreateDataType(ctx,\"hellotype\",0,&tm);\n    if (HelloType == NULL) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.insert\",\n        HelloTypeInsert_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.range\",\n        HelloTypeRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.len\",\n        HelloTypeLen_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hellotype.brange\",\n        HelloTypeBRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/helloworld.c",
    "content": "/* Helloworld module -- A few examples of the Redis Modules API in the form\n * of commands showing how to accomplish common tasks.\n *\n * This module does not do anything useful, if not for a few commands. The\n * examples are designed in order to show the API.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 \"../redismodule.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <string.h>\n\n/* HELLO.SIMPLE is among the simplest commands you can implement.\n * It just returns the currently selected DB id, a functionality which is\n * missing in Redis. The command uses two important API calls: one to\n * fetch the currently selected DB, the other in order to send the client\n * an integer reply as response. */\nint HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModule_ReplyWithLongLong(ctx,RedisModule_GetSelectedDb(ctx));\n    return REDISMODULE_OK;\n}\n\n/* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API\n * where you can \"open\" keys, make low level operations, create new keys by\n * pushing elements into non-existing keys, and so forth.\n *\n * You'll find this command to be roughly as fast as the actual RPUSH\n * command. */\nint HelloPushNative_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    RedisModule_ListPush(key,REDISMODULE_LIST_TAIL,argv[2]);\n    size_t newlen = RedisModule_ValueLength(key);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithLongLong(ctx,newlen);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.PUSH.CALL implements RPUSH using an higher level approach, calling\n * a Redis command instead of working with the key in a low level way. This\n * approach is useful when you need to call Redis commands that are not\n * available as low level APIs, or when you don't need the maximum speed\n * possible but instead prefer implementation simplicity. */\nint HelloPushCall_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    RedisModuleCallReply *reply;\n\n    reply = RedisModule_Call(ctx,\"RPUSH\",\"ss\",argv[1],argv[2]);\n    long long len = RedisModule_CallReplyInteger(reply);\n    RedisModule_FreeCallReply(reply);\n    RedisModule_ReplyWithLongLong(ctx,len);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.PUSH.CALL2\n * This is exaxctly as HELLO.PUSH.CALL, but shows how we can reply to the\n * client using directly a reply object that Call() returned. */\nint HelloPushCall2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    RedisModuleCallReply *reply;\n\n    reply = RedisModule_Call(ctx,\"RPUSH\",\"ss\",argv[1],argv[2]);\n    RedisModule_ReplyWithCallReply(ctx,reply);\n    RedisModule_FreeCallReply(reply);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LIST.SUM.LEN returns the total length of all the items inside\n * a Redis list, by using the high level Call() API.\n * This command is an example of the array reply access. */\nint HelloListSumLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    RedisModuleCallReply *reply;\n\n    reply = RedisModule_Call(ctx,\"LRANGE\",\"sll\",argv[1],(long long)0,(long long)-1);\n    size_t strlen = 0;\n    size_t items = RedisModule_CallReplyLength(reply);\n    size_t j;\n    for (j = 0; j < items; j++) {\n        RedisModuleCallReply *ele = RedisModule_CallReplyArrayElement(reply,j);\n        strlen += RedisModule_CallReplyLength(ele);\n    }\n    RedisModule_FreeCallReply(reply);\n    RedisModule_ReplyWithLongLong(ctx,strlen);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LIST.SPLICE srclist dstlist count\n * Moves 'count' elements from the tail of 'srclist' to the head of\n * 'dstlist'. If less than count elements are available, it moves as much\n * elements as possible. */\nint HelloListSplice_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    /* Src and dst key must be empty or lists. */\n    if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||\n        (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))\n    {\n        RedisModule_CloseKey(srckey);\n        RedisModule_CloseKey(dstkey);\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long count;\n    if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||\n        (count < 0)) {\n        RedisModule_CloseKey(srckey);\n        RedisModule_CloseKey(dstkey);\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    while(count-- > 0) {\n        RedisModuleString *ele;\n\n        ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);\n        if (ele == NULL) break;\n        RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);\n        RedisModule_FreeString(ctx,ele);\n    }\n\n    size_t len = RedisModule_ValueLength(srckey);\n    RedisModule_CloseKey(srckey);\n    RedisModule_CloseKey(dstkey);\n    RedisModule_ReplyWithLongLong(ctx,len);\n    return REDISMODULE_OK;\n}\n\n/* Like the HELLO.LIST.SPLICE above, but uses automatic memory management\n * in order to avoid freeing stuff. */\nint HelloListSpliceAuto_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    RedisModule_AutoMemory(ctx);\n\n    RedisModuleKey *srckey = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    RedisModuleKey *dstkey = RedisModule_OpenKey(ctx,argv[2],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    /* Src and dst key must be empty or lists. */\n    if ((RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(srckey) != REDISMODULE_KEYTYPE_EMPTY) ||\n        (RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_LIST &&\n         RedisModule_KeyType(dstkey) != REDISMODULE_KEYTYPE_EMPTY))\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    long long count;\n    if ((RedisModule_StringToLongLong(argv[3],&count) != REDISMODULE_OK) ||\n        (count < 0))\n    {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n    }\n\n    while(count-- > 0) {\n        RedisModuleString *ele;\n\n        ele = RedisModule_ListPop(srckey,REDISMODULE_LIST_TAIL);\n        if (ele == NULL) break;\n        RedisModule_ListPush(dstkey,REDISMODULE_LIST_HEAD,ele);\n    }\n\n    size_t len = RedisModule_ValueLength(srckey);\n    RedisModule_ReplyWithLongLong(ctx,len);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.RAND.ARRAY <count>\n * Shows how to generate arrays as commands replies.\n * It just outputs <count> random numbers. */\nint HelloRandArray_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n    long long count;\n    if (RedisModule_StringToLongLong(argv[1],&count) != REDISMODULE_OK ||\n        count < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid count\");\n\n    /* To reply with an array, we call RedisModule_ReplyWithArray() followed\n     * by other \"count\" calls to other reply functions in order to generate\n     * the elements of the array. */\n    RedisModule_ReplyWithArray(ctx,count);\n    while(count--) RedisModule_ReplyWithLongLong(ctx,rand());\n    return REDISMODULE_OK;\n}\n\n/* This is a simple command to test replication. Because of the \"!\" modified\n * in the RedisModule_Call() call, the two INCRs get replicated.\n * Also note how the ECHO is replicated in an unexpected position (check\n * comments the function implementation). */\nint HelloRepl1_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModule_AutoMemory(ctx);\n\n    /* This will be replicated *after* the two INCR statements, since\n     * the Call() replication has precedence, so the actual replication\n     * stream will be:\n     *\n     * MULTI\n     * INCR foo\n     * INCR bar\n     * ECHO c foo\n     * EXEC\n     */\n    RedisModule_Replicate(ctx,\"ECHO\",\"c\",\"foo\");\n\n    /* Using the \"!\" modifier we replicate the command if it\n     * modified the dataset in some way. */\n    RedisModule_Call(ctx,\"INCR\",\"c!\",\"foo\");\n    RedisModule_Call(ctx,\"INCR\",\"c!\",\"bar\");\n\n    RedisModule_ReplyWithLongLong(ctx,0);\n\n    return REDISMODULE_OK;\n}\n\n/* Another command to show replication. In this case, we call\n * RedisModule_ReplicateVerbatim() to mean we want just the command to be\n * propagated to slaves / AOF exactly as it was called by the user.\n *\n * This command also shows how to work with string objects.\n * It takes a list, and increments all the elements (that must have\n * a numerical value) by 1, returning the sum of all the elements\n * as reply.\n *\n * Usage: HELLO.REPL2 <list-key> */\nint HelloRepl2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST)\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n\n    size_t listlen = RedisModule_ValueLength(key);\n    long long sum = 0;\n\n    /* Rotate and increment. */\n    while(listlen--) {\n        RedisModuleString *ele = RedisModule_ListPop(key,REDISMODULE_LIST_TAIL);\n        long long val;\n        if (RedisModule_StringToLongLong(ele,&val) != REDISMODULE_OK) val = 0;\n        val++;\n        sum += val;\n        RedisModuleString *newele = RedisModule_CreateStringFromLongLong(ctx,val);\n        RedisModule_ListPush(key,REDISMODULE_LIST_HEAD,newele);\n    }\n    RedisModule_ReplyWithLongLong(ctx,sum);\n    RedisModule_ReplicateVerbatim(ctx);\n    return REDISMODULE_OK;\n}\n\n/* This is an example of strings DMA access. Given a key containing a string\n * it toggles the case of each character from lower to upper case or the\n * other way around.\n *\n * No automatic memory management is used in this example (for the sake\n * of variety).\n *\n * HELLO.TOGGLE.CASE key */\nint HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    int keytype = RedisModule_KeyType(key);\n    if (keytype != REDISMODULE_KEYTYPE_STRING &&\n        keytype != REDISMODULE_KEYTYPE_EMPTY)\n    {\n        RedisModule_CloseKey(key);\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    if (keytype == REDISMODULE_KEYTYPE_STRING) {\n        size_t len, j;\n        char *s = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);\n        for (j = 0; j < len; j++) {\n            if (isupper(s[j])) {\n                s[j] = tolower(s[j]);\n            } else {\n                s[j] = toupper(s[j]);\n            }\n        }\n    }\n\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    RedisModule_ReplicateVerbatim(ctx);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.MORE.EXPIRE key milliseconds.\n *\n * If they key has already an associated TTL, extends it by \"milliseconds\"\n * milliseconds. Otherwise no operation is performed. */\nint HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n    mstime_t addms, expire;\n\n    if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid expire time\");\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    expire = RedisModule_GetExpire(key);\n    if (expire != REDISMODULE_NO_EXPIRE) {\n        expire += addms;\n        RedisModule_SetExpire(key,expire);\n    }\n    return RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n}\n\n/* HELLO.ZSUMRANGE key startscore endscore\n * Return the sum of all the scores elements between startscore and endscore.\n *\n * The computation is performed two times, one time from start to end and\n * another time backward. The two scores, returned as a two element array,\n * should match.*/\nint HelloZsumRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    double score_start, score_end;\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    if (RedisModule_StringToDouble(argv[2],&score_start) != REDISMODULE_OK ||\n        RedisModule_StringToDouble(argv[3],&score_end) != REDISMODULE_OK)\n    {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid range\");\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    double scoresum_a = 0;\n    double scoresum_b = 0;\n\n    RedisModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0);\n    while(!RedisModule_ZsetRangeEndReached(key)) {\n        double score;\n        RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);\n        RedisModule_FreeString(ctx,ele);\n        scoresum_a += score;\n        RedisModule_ZsetRangeNext(key);\n    }\n    RedisModule_ZsetRangeStop(key);\n\n    RedisModule_ZsetLastInScoreRange(key,score_start,score_end,0,0);\n    while(!RedisModule_ZsetRangeEndReached(key)) {\n        double score;\n        RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);\n        RedisModule_FreeString(ctx,ele);\n        scoresum_b += score;\n        RedisModule_ZsetRangePrev(key);\n    }\n\n    RedisModule_ZsetRangeStop(key);\n\n    RedisModule_CloseKey(key);\n\n    RedisModule_ReplyWithArray(ctx,2);\n    RedisModule_ReplyWithDouble(ctx,scoresum_a);\n    RedisModule_ReplyWithDouble(ctx,scoresum_b);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LEXRANGE key min_lex max_lex min_age max_age\n * This command expects a sorted set stored at key in the following form:\n * - All the elements have score 0.\n * - Elements are pairs of \"<name>:<age>\", for example \"Anna:52\".\n * The command will return all the sorted set items that are lexicographically\n * between the specified range (using the same format as ZRANGEBYLEX)\n * and having an age between min_age and max_age. */\nint HelloLexRange_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 6) return RedisModule_WrongArity(ctx);\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    if (RedisModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDISMODULE_OK) {\n        return RedisModule_ReplyWithError(ctx,\"invalid range\");\n    }\n\n    int arraylen = 0;\n    RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);\n    while(!RedisModule_ZsetRangeEndReached(key)) {\n        double score;\n        RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key,&score);\n        RedisModule_ReplyWithString(ctx,ele);\n        RedisModule_FreeString(ctx,ele);\n        RedisModule_ZsetRangeNext(key);\n        arraylen++;\n    }\n    RedisModule_ZsetRangeStop(key);\n    RedisModule_ReplySetArrayLength(ctx,arraylen);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.HCOPY key srcfield dstfield\n * This is just an example command that sets the hash field dstfield to the\n * same value of srcfield. If srcfield does not exist no operation is\n * performed.\n *\n * The command returns 1 if the copy is performed (srcfield exists) otherwise\n * 0 is returned. */\nint HelloHCopy_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_HASH &&\n        type != REDISMODULE_KEYTYPE_EMPTY)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\n    /* Get the old field value. */\n    RedisModuleString *oldval;\n    RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&oldval,NULL);\n    if (oldval) {\n        RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[3],oldval,NULL);\n    }\n    RedisModule_ReplyWithLongLong(ctx,oldval != NULL);\n    return REDISMODULE_OK;\n}\n\n/* HELLO.LEFTPAD str len ch\n * This is an implementation of the infamous LEFTPAD function, that\n * was at the center of an issue with the npm modules system in March 2016.\n *\n * LEFTPAD is a good example of using a Redis Modules API called\n * \"pool allocator\", that was a famous way to allocate memory in yet another\n * open source project, the Apache web server.\n *\n * The concept is very simple: there is memory that is useful to allocate\n * only in the context of serving a request, and must be freed anyway when\n * the callback implementing the command returns. So in that case the module\n * does not need to retain a reference to these allocations, it is just\n * required to free the memory before returning. When this is the case the\n * module can call RedisModule_PoolAlloc() instead, that works like malloc()\n * but will automatically free the memory when the module callback returns.\n *\n * Note that PoolAlloc() does not necessarily require AutoMemory to be\n * active. */\nint HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx); /* Use automatic memory management. */\n    long long padlen;\n\n    if (argc != 4) return RedisModule_WrongArity(ctx);\n\n    if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) ||\n        (padlen< 0)) {\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid padding length\");\n    }\n    size_t strlen, chlen;\n    const char *str = RedisModule_StringPtrLen(argv[1], &strlen);\n    const char *ch = RedisModule_StringPtrLen(argv[3], &chlen);\n\n    /* If the string is already larger than the target len, just return\n     * the string itself. */\n    if (strlen >= (size_t)padlen)\n        return RedisModule_ReplyWithString(ctx,argv[1]);\n\n    /* Padding must be a single character in this simple implementation. */\n    if (chlen != 1)\n        return RedisModule_ReplyWithError(ctx,\n            \"ERR padding must be a single char\");\n\n    /* Here we use our pool allocator, for our throw-away allocation. */\n    padlen -= strlen;\n    char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen);\n    for (long long j = 0; j < padlen; j++) buf[j] = *ch;\n    memcpy(buf+padlen,str,strlen);\n\n    RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen);\n    return REDISMODULE_OK;\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (RedisModule_Init(ctx,\"helloworld\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    /* Log the list of parameters passing loading the module. */\n    for (int j = 0; j < argc; j++) {\n        const char *s = RedisModule_StringPtrLen(argv[j],NULL);\n        printf(\"Module loaded with ARGV[%d] = %s\\n\", j, s);\n    }\n\n    if (RedisModule_CreateCommand(ctx,\"hello.simple\",\n        HelloSimple_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.push.native\",\n        HelloPushNative_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.push.call\",\n        HelloPushCall_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.push.call2\",\n        HelloPushCall2_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.list.sum.len\",\n        HelloListSumLen_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.list.splice\",\n        HelloListSplice_RedisCommand,\"write deny-oom\",1,2,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.list.splice.auto\",\n        HelloListSpliceAuto_RedisCommand,\n        \"write deny-oom\",1,2,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.rand.array\",\n        HelloRandArray_RedisCommand,\"readonly\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.repl1\",\n        HelloRepl1_RedisCommand,\"write\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.repl2\",\n        HelloRepl2_RedisCommand,\"write\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.toggle.case\",\n        HelloToggleCase_RedisCommand,\"write\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.more.expire\",\n        HelloMoreExpire_RedisCommand,\"write\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.zsumrange\",\n        HelloZsumRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.lexrange\",\n        HelloLexRange_RedisCommand,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.hcopy\",\n        HelloHCopy_RedisCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"hello.leftpad\",\n        HelloLeftPad_RedisCommand,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/modules/testmodule.c",
    "content": "/* Module designed to test the Redis modules subsystem.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2016, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"../redismodule.h\"\n#include <string.h>\n\n/* --------------------------------- Helpers -------------------------------- */\n\n/* Return true if the reply and the C null term string matches. */\nint TestMatchReply(RedisModuleCallReply *reply, char *str) {\n    RedisModuleString *mystr;\n    mystr = RedisModule_CreateStringFromCallReply(reply);\n    if (!mystr) return 0;\n    const char *ptr = RedisModule_StringPtrLen(mystr,NULL);\n    return strcmp(ptr,str) == 0;\n}\n\n/* ------------------------------- Test units ------------------------------- */\n\n/* TEST.CALL -- Test Call() API. */\nint TestCall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_AutoMemory(ctx);\n    RedisModuleCallReply *reply;\n\n    RedisModule_Call(ctx,\"DEL\",\"c\",\"mylist\");\n    RedisModuleString *mystr = RedisModule_CreateString(ctx,\"foo\",3);\n    RedisModule_Call(ctx,\"RPUSH\",\"csl\",\"mylist\",mystr,(long long)1234);\n    reply = RedisModule_Call(ctx,\"LRANGE\",\"ccc\",\"mylist\",\"0\",\"-1\");\n    long long items = RedisModule_CallReplyLength(reply);\n    if (items != 2) goto fail;\n\n    RedisModuleCallReply *item0, *item1;\n\n    item0 = RedisModule_CallReplyArrayElement(reply,0);\n    item1 = RedisModule_CallReplyArrayElement(reply,1);\n    if (!TestMatchReply(item0,\"foo\")) goto fail;\n    if (!TestMatchReply(item1,\"1234\")) goto fail;\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n\nfail:\n    RedisModule_ReplyWithSimpleString(ctx,\"ERR\");\n    return REDISMODULE_OK;\n}\n\n/* TEST.STRING.APPEND -- Test appending to an existing string object. */\nint TestStringAppend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleString *s = RedisModule_CreateString(ctx,\"foo\",3);\n    RedisModule_StringAppendBuffer(ctx,s,\"bar\",3);\n    RedisModule_ReplyWithString(ctx,s);\n    RedisModule_FreeString(ctx,s);\n    return REDISMODULE_OK;\n}\n\n/* TEST.STRING.APPEND.AM -- Test append with retain when auto memory is on. */\nint TestStringAppendAM(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_AutoMemory(ctx);\n    RedisModuleString *s = RedisModule_CreateString(ctx,\"foo\",3);\n    RedisModule_RetainString(ctx,s);\n    RedisModule_StringAppendBuffer(ctx,s,\"bar\",3);\n    RedisModule_ReplyWithString(ctx,s);\n    RedisModule_FreeString(ctx,s);\n    return REDISMODULE_OK;\n}\n\n/* TEST.STRING.PRINTF -- Test string formatting. */\nint TestStringPrintf(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx);\n    if (argc < 3) {\n        return RedisModule_WrongArity(ctx);\n    }\n    RedisModuleString *s = RedisModule_CreateStringPrintf(ctx,\n        \"Got %d args. argv[1]: %s, argv[2]: %s\",\n        argc,\n        RedisModule_StringPtrLen(argv[1], NULL),\n        RedisModule_StringPtrLen(argv[2], NULL)\n    );\n\n    RedisModule_ReplyWithString(ctx,s);\n\n    return REDISMODULE_OK;\n}\n\nint failTest(RedisModuleCtx *ctx, const char *msg) {\n    RedisModule_ReplyWithError(ctx, msg);\n    return REDISMODULE_ERR;\n}\n\nint TestUnlink(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    RedisModule_AutoMemory(ctx);\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleKey *k = RedisModule_OpenKey(ctx, RedisModule_CreateStringPrintf(ctx, \"unlinked\"), REDISMODULE_WRITE | REDISMODULE_READ);\n    if (!k) return failTest(ctx, \"Could not create key\");\n\n    if (REDISMODULE_ERR == RedisModule_StringSet(k, RedisModule_CreateStringPrintf(ctx, \"Foobar\"))) {\n        return failTest(ctx, \"Could not set string value\");\n    }\n\n    RedisModuleCallReply *rep = RedisModule_Call(ctx, \"EXISTS\", \"c\", \"unlinked\");\n    if (!rep || RedisModule_CallReplyInteger(rep) != 1) {\n        return failTest(ctx, \"Key does not exist before unlink\");\n    }\n\n    if (REDISMODULE_ERR == RedisModule_UnlinkKey(k)) {\n        return failTest(ctx, \"Could not unlink key\");\n    }\n\n    rep = RedisModule_Call(ctx, \"EXISTS\", \"c\", \"unlinked\");\n    if (!rep || RedisModule_CallReplyInteger(rep) != 0) {\n        return failTest(ctx, \"Could not verify key to be unlinked\");\n    }\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n\n}\n\nint NotifyCallback(RedisModuleCtx *ctx, int type, const char *event,\n                   RedisModuleString *key) {\n  /* Increment a counter on the notifications: for each key notified we\n   * increment a counter */\n  RedisModule_Log(ctx, \"notice\", \"Got event type %d, event %s, key %s\", type,\n                  event, RedisModule_StringPtrLen(key, NULL));\n\n  RedisModule_Call(ctx, \"HINCRBY\", \"csc\", \"notifications\", key, \"1\");\n  return REDISMODULE_OK;\n}\n\n/* TEST.NOTIFICATIONS -- Test Keyspace Notifications. */\nint TestNotifications(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n#define FAIL(msg, ...)                                                                       \\\n    {                                                                                        \\\n        RedisModule_Log(ctx, \"warning\", \"Failed NOTIFY Test. Reason: \" #msg, ##__VA_ARGS__); \\\n        goto err;                                                                            \\\n    }\n    RedisModule_Call(ctx, \"FLUSHDB\", \"\");\n\n    RedisModule_Call(ctx, \"SET\", \"cc\", \"foo\", \"bar\");\n    RedisModule_Call(ctx, \"SET\", \"cc\", \"foo\", \"baz\");\n    RedisModule_Call(ctx, \"SADD\", \"cc\", \"bar\", \"x\");\n    RedisModule_Call(ctx, \"SADD\", \"cc\", \"bar\", \"y\");\n\n    RedisModule_Call(ctx, \"HSET\", \"ccc\", \"baz\", \"x\", \"y\");\n    /* LPUSH should be ignored and not increment any counters */\n    RedisModule_Call(ctx, \"LPUSH\", \"cc\", \"l\", \"y\");\n    RedisModule_Call(ctx, \"LPUSH\", \"cc\", \"l\", \"y\");\n\n    /* Miss some keys intentionally so we will get a \"keymiss\" notification. */\n    RedisModule_Call(ctx, \"GET\", \"c\", \"nosuchkey\");\n    RedisModule_Call(ctx, \"SMEMBERS\", \"c\", \"nosuchkey\");\n\n    size_t sz;\n    const char *rep;\n    RedisModuleCallReply *r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"foo\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for foo\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '2') {\n            FAIL(\"Got reply '%s'. expected '2'\", RedisModule_CallReplyStringPtr(r, NULL));\n        }\n    }\n\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"bar\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for bar\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '2') {\n            FAIL(\"Got reply '%s'. expected '2'\", rep);\n        }\n    }\n\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"baz\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for baz\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '1') {\n            FAIL(\"Got reply '%.*s'. expected '1'\", sz, rep);\n        }\n    }\n    /* For l we expect nothing since we didn't subscribe to list events */\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"l\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_NULL) {\n        FAIL(\"Wrong reply for l\");\n    }\n\n    r = RedisModule_Call(ctx, \"HGET\", \"cc\", \"notifications\", \"nosuchkey\");\n    if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {\n        FAIL(\"Wrong or no reply for nosuchkey\");\n    } else {\n        rep = RedisModule_CallReplyStringPtr(r, &sz);\n        if (sz != 1 || *rep != '2') {\n            FAIL(\"Got reply '%.*s'. expected '2'\", sz, rep);\n        }\n    }\n\n    RedisModule_Call(ctx, \"FLUSHDB\", \"\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\nerr:\n    RedisModule_Call(ctx, \"FLUSHDB\", \"\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"ERR\");\n}\n\n/* TEST.CTXFLAGS -- Test GetContextFlags. */\nint TestCtxFlags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argc);\n    REDISMODULE_NOT_USED(argv);\n\n    RedisModule_AutoMemory(ctx);\n\n    int ok = 1;\n    const char *errString = NULL;\n#undef FAIL\n#define FAIL(msg)        \\\n    {                    \\\n        ok = 0;          \\\n        errString = msg; \\\n        goto end;        \\\n    }\n\n    int flags = RedisModule_GetContextFlags(ctx);\n    if (flags == 0) {\n        FAIL(\"Got no flags\");\n    }\n\n    if (flags & REDISMODULE_CTX_FLAGS_LUA) FAIL(\"Lua flag was set\");\n    if (flags & REDISMODULE_CTX_FLAGS_MULTI) FAIL(\"Multi flag was set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_AOF) FAIL(\"AOF Flag was set\")\n    /* Enable AOF to test AOF flags */\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"appendonly\", \"yes\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_AOF)) FAIL(\"AOF Flag not set after config set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_RDB) FAIL(\"RDB Flag was set\");\n    /* Enable RDB to test RDB flags */\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"save\", \"900 1\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_RDB)) FAIL(\"RDB Flag was not set after config set\");\n\n    if (!(flags & REDISMODULE_CTX_FLAGS_MASTER)) FAIL(\"Master flag was not set\");\n    if (flags & REDISMODULE_CTX_FLAGS_SLAVE) FAIL(\"Slave flag was set\");\n    if (flags & REDISMODULE_CTX_FLAGS_READONLY) FAIL(\"Read-only flag was set\");\n    if (flags & REDISMODULE_CTX_FLAGS_CLUSTER) FAIL(\"Cluster flag was set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_MAXMEMORY) FAIL(\"Maxmemory flag was set\");\n\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory\", \"100000000\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_MAXMEMORY))\n        FAIL(\"Maxmemory flag was not set after config set\");\n\n    if (flags & REDISMODULE_CTX_FLAGS_EVICT) FAIL(\"Eviction flag was set\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory-policy\", \"allkeys-lru\");\n    flags = RedisModule_GetContextFlags(ctx);\n    if (!(flags & REDISMODULE_CTX_FLAGS_EVICT)) FAIL(\"Eviction flag was not set after config set\");\n\nend:\n    /* Revert config changes */\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"appendonly\", \"no\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"save\", \"\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory\", \"0\");\n    RedisModule_Call(ctx, \"config\", \"ccc\", \"set\", \"maxmemory-policy\", \"noeviction\");\n\n    if (!ok) {\n        RedisModule_Log(ctx, \"warning\", \"Failed CTXFLAGS Test. Reason: %s\", errString);\n        return RedisModule_ReplyWithSimpleString(ctx, \"ERR\");\n    }\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\n/* ----------------------------- Test framework ----------------------------- */\n\n/* Return 1 if the reply matches the specified string, otherwise log errors\n * in the server log and return 0. */\nint TestAssertStringReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) {\n    RedisModuleString *mystr, *expected;\n\n    if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) {\n        RedisModule_Log(ctx,\"warning\",\"Unexpected reply type %d\",\n            RedisModule_CallReplyType(reply));\n        return 0;\n    }\n    mystr = RedisModule_CreateStringFromCallReply(reply);\n    expected = RedisModule_CreateString(ctx,str,len);\n    if (RedisModule_StringCompare(mystr,expected) != 0) {\n        const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL);\n        const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL);\n        RedisModule_Log(ctx,\"warning\",\n            \"Unexpected string reply '%s' (instead of '%s')\",\n            mystr_ptr, expected_ptr);\n        return 0;\n    }\n    return 1;\n}\n\n/* Return 1 if the reply matches the specified integer, otherwise log errors\n * in the server log and return 0. */\nint TestAssertIntegerReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, long long expected) {\n    if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_INTEGER) {\n        RedisModule_Log(ctx,\"warning\",\"Unexpected reply type %d\",\n            RedisModule_CallReplyType(reply));\n        return 0;\n    }\n    long long val = RedisModule_CallReplyInteger(reply);\n    if (val != expected) {\n        RedisModule_Log(ctx,\"warning\",\n            \"Unexpected integer reply '%lld' (instead of '%lld')\",\n            val, expected);\n        return 0;\n    }\n    return 1;\n}\n\n#define T(name,...) \\\n    do { \\\n        RedisModule_Log(ctx,\"warning\",\"Testing %s\", name); \\\n        reply = RedisModule_Call(ctx,name,__VA_ARGS__); \\\n    } while (0);\n\n/* TEST.IT -- Run all the tests. */\nint TestIt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModule_AutoMemory(ctx);\n    RedisModuleCallReply *reply;\n\n    /* Make sure the DB is empty before to proceed. */\n    T(\"dbsize\",\"\");\n    if (!TestAssertIntegerReply(ctx,reply,0)) goto fail;\n\n    T(\"ping\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"PONG\",4)) goto fail;\n\n    T(\"test.call\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    T(\"test.ctxflags\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    T(\"test.string.append\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"foobar\",6)) goto fail;\n\n    T(\"test.unlink\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    T(\"test.string.append.am\",\"\");\n    if (!TestAssertStringReply(ctx,reply,\"foobar\",6)) goto fail;\n\n    T(\"test.string.printf\", \"cc\", \"foo\", \"bar\");\n    if (!TestAssertStringReply(ctx,reply,\"Got 3 args. argv[1]: foo, argv[2]: bar\",38)) goto fail;\n\n    T(\"test.notify\", \"\");\n    if (!TestAssertStringReply(ctx,reply,\"OK\",2)) goto fail;\n\n    RedisModule_ReplyWithSimpleString(ctx,\"ALL TESTS PASSED\");\n    return REDISMODULE_OK;\n\nfail:\n    RedisModule_ReplyWithSimpleString(ctx,\n        \"SOME TEST NOT PASSED! Check server logs\");\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"test\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.call\",\n        TestCall,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.string.append\",\n        TestStringAppend,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.string.append.am\",\n        TestStringAppendAM,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.string.printf\",\n        TestStringPrintf,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.ctxflags\",\n        TestCtxFlags,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.unlink\",\n        TestUnlink,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.it\",\n        TestIt,\"readonly\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModule_SubscribeToKeyspaceEvents(ctx,\n                                            REDISMODULE_NOTIFY_HASH |\n                                            REDISMODULE_NOTIFY_SET |\n                                            REDISMODULE_NOTIFY_STRING |\n                                            REDISMODULE_NOTIFY_KEY_MISS,\n                                        NotifyCallback);\n    if (RedisModule_CreateCommand(ctx,\"test.notify\",\n        TestNotifications,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n\n/* ================================ MULTI/EXEC ============================== */\n\n/* Client state initialization for MULTI/EXEC */\nvoid initClientMultiState(client *c) {\n    c->mstate.commands = NULL;\n    c->mstate.count = 0;\n    c->mstate.cmd_flags = 0;\n}\n\n/* Release all the resources associated with MULTI/EXEC state */\nvoid freeClientMultiState(client *c) {\n    int j;\n\n    for (j = 0; j < c->mstate.count; j++) {\n        int i;\n        multiCmd *mc = c->mstate.commands+j;\n\n        for (i = 0; i < mc->argc; i++)\n            decrRefCount(mc->argv[i]);\n        zfree(mc->argv);\n    }\n    zfree(c->mstate.commands);\n}\n\n/* Add a new command into the MULTI commands queue */\nvoid queueMultiCommand(client *c) {\n    multiCmd *mc;\n    int j;\n\n    c->mstate.commands = zrealloc(c->mstate.commands,\n            sizeof(multiCmd)*(c->mstate.count+1));\n    mc = c->mstate.commands+c->mstate.count;\n    mc->cmd = c->cmd;\n    mc->argc = c->argc;\n    mc->argv = 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    c->mstate.count++;\n    c->mstate.cmd_flags |= c->cmd->flags;\n}\n\nvoid discardTransaction(client *c) {\n    freeClientMultiState(c);\n    initClientMultiState(c);\n    c->flags &= ~(CLIENT_MULTI|CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC);\n    unwatchAllKeys(c);\n}\n\n/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.\n * Should be called every time there is an error while queueing a command. */\nvoid flagTransaction(client *c) {\n    if (c->flags & CLIENT_MULTI)\n        c->flags |= CLIENT_DIRTY_EXEC;\n}\n\nvoid multiCommand(client *c) {\n    if (c->flags & CLIENT_MULTI) {\n        addReplyError(c,\"MULTI calls can not be nested\");\n        return;\n    }\n    c->flags |= CLIENT_MULTI;\n    addReply(c,shared.ok);\n}\n\nvoid discardCommand(client *c) {\n    if (!(c->flags & CLIENT_MULTI)) {\n        addReplyError(c,\"DISCARD without MULTI\");\n        return;\n    }\n    discardTransaction(c);\n    addReply(c,shared.ok);\n}\n\n/* Send a MULTI command to all the slaves and AOF file. Check the execCommand\n * implementation for more information. */\nvoid execCommandPropagateMulti(client *c) {\n    propagate(server.multiCommand,c->db->id,&shared.multi,1,\n              PROPAGATE_AOF|PROPAGATE_REPL);\n}\n\nvoid execCommandPropagateExec(client *c) {\n    propagate(server.execCommand,c->db->id,&shared.exec,1,\n              PROPAGATE_AOF|PROPAGATE_REPL);\n}\n\nvoid execCommand(client *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    int was_master = server.masterhost == NULL;\n\n    if (!(c->flags & CLIENT_MULTI)) {\n        addReplyError(c,\"EXEC without MULTI\");\n        return;\n    }\n\n    /* Check if we need to abort the EXEC because:\n     * 1) Some WATCHed key was touched.\n     * 2) There was a previous error while queueing commands.\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    if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC)) {\n        addReply(c, c->flags & CLIENT_DIRTY_EXEC ? shared.execaborterr :\n                                                   shared.nullarray[c->resp]);\n        discardTransaction(c);\n        goto handle_monitor;\n    }\n\n    /* If there are write commands inside the transaction, and this is a read\n     * only slave, we want to send an error. This happens when the transaction\n     * was initiated when the instance was a master or a writable replica and\n     * then the configuration changed (for example instance was turned into\n     * a replica). */\n    if (!server.loading && server.masterhost && server.repl_slave_ro &&\n        !(c->flags & CLIENT_MASTER) && c->mstate.cmd_flags & CMD_WRITE)\n    {\n        addReplyError(c,\n            \"Transaction contains write commands but instance \"\n            \"is now a read-only replica. EXEC aborted.\");\n        discardTransaction(c);\n        goto handle_monitor;\n    }\n\n    /* Exec all the queued commands */\n    unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */\n    orig_argv = c->argv;\n    orig_argc = c->argc;\n    orig_cmd = c->cmd;\n    addReplyArrayLen(c,c->mstate.count);\n    for (j = 0; j < c->mstate.count; j++) {\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 command which\n         * is not readonly nor an administrative one.\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        if (!must_propagate &&\n            !server.loading &&\n            !(c->cmd->flags & (CMD_READONLY|CMD_ADMIN)))\n        {\n            execCommandPropagateMulti(c);\n            must_propagate = 1;\n        }\n\n        int acl_keypos;\n        int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);\n        if (acl_retval != ACL_OK) {\n            addACLLogEntry(c,acl_retval,acl_keypos,NULL);\n            addReplyErrorFormat(c,\n                \"-NOPERM ACLs rules changed between the moment the \"\n                \"transaction was accumulated and the EXEC call. \"\n                \"This command is no longer allowed for the \"\n                \"following reason: %s\",\n                (acl_retval == ACL_DENIED_CMD) ?\n                \"no permission to execute the command or subcommand\" :\n                \"no permission to touch the specified keys\");\n        } else {\n            call(c,server.loading ? CMD_CALL_NONE : CMD_CALL_FULL);\n        }\n\n        /* Commands may alter argc/argv, restore mstate. */\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    c->argv = orig_argv;\n    c->argc = orig_argc;\n    c->cmd = orig_cmd;\n    discardTransaction(c);\n\n    /* Make sure the EXEC command will be propagated as well if MULTI\n     * was already propagated. */\n    if (must_propagate) {\n        int is_master = server.masterhost == NULL;\n        server.dirty++;\n        /* If inside the MULTI/EXEC block this instance was suddenly\n         * switched from master to slave (using the SLAVEOF command), the\n         * initial MULTI was propagated into the replication backlog, but the\n         * rest was not. We need to make sure to at least terminate the\n         * backlog with the final EXEC. */\n        if (server.repl_backlog && was_master && !is_master) {\n            char *execcmd = \"*1\\r\\n$4\\r\\nEXEC\\r\\n\";\n            feedReplicationBacklog(execcmd,strlen(execcmd));\n        }\n    }\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 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 * Also every client contains a list of WATCHed keys so that's possible to\n * un-watch such keys when the client is freed or when UNWATCH is called. */\n\n/* In the client->watched_keys list we need to use watchedKey structures\n * as in order to identify a key in Redis we need both the key name and the\n * DB */\ntypedef struct watchedKey {\n    robj *key;\n    redisDb *db;\n} watchedKey;\n\n/* Watch for the specified key */\nvoid watchForKey(client *c, robj *key) {\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    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    /* This key is not already watched in this DB. Let's add it */\n    clients = dictFetchValue(c->db->watched_keys,key);\n    if (!clients) {\n        clients = listCreate();\n        dictAdd(c->db->watched_keys,key,clients);\n        incrRefCount(key);\n    }\n    listAddNodeTail(clients,c);\n    /* Add the new key to the list of keys watched by this client */\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. */\nvoid unwatchAllKeys(client *c) {\n    listIter li;\n    listNode *ln;\n\n    if (listLength(c->watched_keys) == 0) return;\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        wk = listNodeValue(ln);\n        clients = dictFetchValue(wk->db->watched_keys, wk->key);\n        serverAssertWithInfo(c,NULL,clients != NULL);\n        listDelNode(clients,listSearchKey(clients,c));\n        /* Kill the entry at all if this was the only client */\n        if (listLength(clients) == 0)\n            dictDelete(wk->db->watched_keys, wk->key);\n        /* Remove this watched key from the client->watched list */\n        listDelNode(c->watched_keys,ln);\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. */\nvoid touchWatchedKey(redisDb *db, robj *key) {\n    list *clients;\n    listIter li;\n    listNode *ln;\n\n    if (dictSize(db->watched_keys) == 0) return;\n    clients = dictFetchValue(db->watched_keys, key);\n    if (!clients) return;\n\n    /* Mark all the clients watching this key as CLIENT_DIRTY_CAS */\n    /* Check if we are already watching for this key */\n    listRewind(clients,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n\n        c->flags |= CLIENT_DIRTY_CAS;\n    }\n}\n\n/* On FLUSHDB or FLUSHALL all the watched keys that are present before the\n * flush but will be deleted as effect of the flushing operation should\n * be touched. \"dbid\" is the DB that's getting the flush. -1 if it is\n * a FLUSHALL operation (all the DBs flushed). */\nvoid touchWatchedKeysOnFlush(int dbid) {\n    listIter li1, li2;\n    listNode *ln;\n\n    /* For every client, check all the waited keys */\n    listRewind(server.clients,&li1);\n    while((ln = listNext(&li1))) {\n        client *c = listNodeValue(ln);\n        listRewind(c->watched_keys,&li2);\n        while((ln = listNext(&li2))) {\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            if (dbid == -1 || wk->db->id == dbid) {\n                if (dictFind(wk->db->dict, wk->key->ptr) != NULL)\n                    c->flags |= CLIENT_DIRTY_CAS;\n            }\n        }\n    }\n}\n\nvoid watchCommand(client *c) {\n    int j;\n\n    if (c->flags & CLIENT_MULTI) {\n        addReplyError(c,\"WATCH inside MULTI is not allowed\");\n        return;\n    }\n    for (j = 1; j < c->argc; j++)\n        watchForKey(c,c->argv[j]);\n    addReply(c,shared.ok);\n}\n\nvoid unwatchCommand(client *c) {\n    unwatchAllKeys(c);\n    c->flags &= (~CLIENT_DIRTY_CAS);\n    addReply(c,shared.ok);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"atomicvar.h\"\n#include <sys/socket.h>\n#include <sys/uio.h>\n#include <math.h>\n#include <ctype.h>\n\nstatic void setProtocolError(const char *errstr, client *c);\nint postponeClientRead(client *c);\nint ProcessingEventsWhileBlocked = 0; /* See processEventsWhileBlocked(). */\n\n/* Return the size consumed from the allocator, for the specified SDS string,\n * including internal fragmentation. This function is used in order to compute\n * the client output buffer size. */\nsize_t sdsZmallocSize(sds s) {\n    void *sh = sdsAllocPtr(s);\n    return zmalloc_size(sh);\n}\n\n/* Return the amount of memory used by the sds string at object->ptr\n * for a string object. */\nsize_t getStringObjectSdsUsedMemory(robj *o) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    switch(o->encoding) {\n    case OBJ_ENCODING_RAW: return sdsZmallocSize(o->ptr);\n    case OBJ_ENCODING_EMBSTR: return zmalloc_size(o)-sizeof(robj);\n    default: return 0; /* Just integer encoding for now. */\n    }\n}\n\n/* Client.reply list dup and free methods. */\nvoid *dupClientReplyValue(void *o) {\n    clientReplyBlock *old = o;\n    clientReplyBlock *buf = zmalloc(sizeof(clientReplyBlock) + old->size);\n    memcpy(buf, o, sizeof(clientReplyBlock) + old->size);\n    return buf;\n}\n\nvoid freeClientReplyValue(void *o) {\n    zfree(o);\n}\n\nint listMatchObjects(void *a, void *b) {\n    return equalStringObjects(a,b);\n}\n\n/* This function links the client to the global linked list of clients.\n * unlinkClient() does the opposite, among other things. */\nvoid linkClient(client *c) {\n    listAddNodeTail(server.clients,c);\n    /* Note that we remember the linked list node where the client is stored,\n     * this way removing the client in unlinkClient() will not require\n     * a linear scan, but just a constant time operation. */\n    c->client_list_node = listLast(server.clients);\n    uint64_t id = htonu64(c->id);\n    raxInsert(server.clients_index,(unsigned char*)&id,sizeof(id),c,NULL);\n}\n\nclient *createClient(connection *conn) {\n    client *c = zmalloc(sizeof(client));\n\n    /* passing NULL as conn it is possible to create a non connected client.\n     * This is useful since all the commands needs to be executed\n     * in the context of a client. When commands are executed in other\n     * contexts (for instance a Lua script) we need a non connected client. */\n    if (conn) {\n        connNonBlock(conn);\n        connEnableTcpNoDelay(conn);\n        if (server.tcpkeepalive)\n            connKeepAlive(conn,server.tcpkeepalive);\n        connSetReadHandler(conn, readQueryFromClient);\n        connSetPrivateData(conn, c);\n    }\n\n    selectDb(c,0);\n    uint64_t client_id = ++server.next_client_id;\n    c->id = client_id;\n    c->resp = 2;\n    c->conn = conn;\n    c->name = NULL;\n    c->bufpos = 0;\n    c->qb_pos = 0;\n    c->querybuf = sdsempty();\n    c->pending_querybuf = sdsempty();\n    c->querybuf_peak = 0;\n    c->reqtype = 0;\n    c->argc = 0;\n    c->argv = NULL;\n    c->cmd = c->lastcmd = NULL;\n    c->user = DefaultUser;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n    c->sentlen = 0;\n    c->flags = 0;\n    c->ctime = c->lastinteraction = server.unixtime;\n    /* If the default user does not require authentication, the user is\n     * directly authenticated. */\n    c->authenticated = (c->user->flags & USER_FLAG_NOPASS) &&\n                       !(c->user->flags & USER_FLAG_DISABLED);\n    c->replstate = REPL_STATE_NONE;\n    c->repl_put_online_on_ack = 0;\n    c->reploff = 0;\n    c->read_reploff = 0;\n    c->repl_ack_off = 0;\n    c->repl_ack_time = 0;\n    c->slave_listening_port = 0;\n    c->slave_ip[0] = '\\0';\n    c->slave_capa = SLAVE_CAPA_NONE;\n    c->reply = listCreate();\n    c->reply_bytes = 0;\n    c->obuf_soft_limit_reached_time = 0;\n    listSetFreeMethod(c->reply,freeClientReplyValue);\n    listSetDupMethod(c->reply,dupClientReplyValue);\n    c->btype = BLOCKED_NONE;\n    c->bpop.timeout = 0;\n    c->bpop.keys = dictCreate(&objectKeyHeapPointerValueDictType,NULL);\n    c->bpop.target = NULL;\n    c->bpop.xread_group = NULL;\n    c->bpop.xread_consumer = NULL;\n    c->bpop.xread_group_noack = 0;\n    c->bpop.numreplicas = 0;\n    c->bpop.reploffset = 0;\n    c->woff = 0;\n    c->watched_keys = listCreate();\n    c->pubsub_channels = dictCreate(&objectKeyPointerValueDictType,NULL);\n    c->pubsub_patterns = listCreate();\n    c->peerid = NULL;\n    c->client_list_node = NULL;\n    c->client_tracking_redirection = 0;\n    c->client_tracking_prefixes = NULL;\n    c->client_cron_last_memory_usage = 0;\n    c->client_cron_last_memory_type = CLIENT_TYPE_NORMAL;\n    c->auth_callback = NULL;\n    c->auth_callback_privdata = NULL;\n    c->auth_module = NULL;\n    listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);\n    listSetMatchMethod(c->pubsub_patterns,listMatchObjects);\n    if (conn) linkClient(c);\n    initClientMultiState(c);\n    return c;\n}\n\n/* This funciton puts the client in the queue of clients that should write\n * their output buffers to the socket. Note that it does not *yet* install\n * the write handler, to start clients are put in a queue of clients that need\n * to write, so we try to do that before returning in the event loop (see the\n * handleClientsWithPendingWrites() function).\n * If we fail and there is more data to write, compared to what the socket\n * buffers can hold, then we'll really install the handler. */\nvoid clientInstallWriteHandler(client *c) {\n    /* Schedule the client to write the output buffers to the socket only\n     * if not already done and, for slaves, if the slave can actually receive\n     * writes at this stage. */\n    if (!(c->flags & CLIENT_PENDING_WRITE) &&\n        (c->replstate == REPL_STATE_NONE ||\n         (c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack)))\n    {\n        /* Here instead of installing the write handler, we just flag the\n         * client and put it into a list of clients that have something\n         * to write to the socket. This way before re-entering the event\n         * loop, we can try to directly write to the client sockets avoiding\n         * a system call. We'll only really install the write handler if\n         * we'll not be able to write the whole reply at once. */\n        c->flags |= CLIENT_PENDING_WRITE;\n        listAddNodeHead(server.clients_pending_write,c);\n    }\n}\n\n/* This function is called every time we are going to transmit new data\n * to the client. The behavior is the following:\n *\n * If the client should receive new data (normal clients will) the function\n * returns C_OK, and make sure to install the write handler in our event\n * loop so that when the socket is writable new data gets written.\n *\n * If the client should not receive new data, because it is a fake client\n * (used to load AOF in memory), a master or because the setup of the write\n * handler failed, the function returns C_ERR.\n *\n * The function may return C_OK without actually installing the write\n * event handler in the following cases:\n *\n * 1) The event handler should already be installed since the output buffer\n *    already contains something.\n * 2) The client is a slave but not yet online, so we want to just accumulate\n *    writes in the buffer but not actually sending them yet.\n *\n * Typically gets called every time a reply is built, before adding more\n * data to the clients output buffers. If the function returns C_ERR no\n * data should be appended to the output buffers. */\nint prepareClientToWrite(client *c) {\n    /* If it's the Lua client we always return ok without installing any\n     * handler since there is no socket at all. */\n    if (c->flags & (CLIENT_LUA|CLIENT_MODULE)) return C_OK;\n\n    /* CLIENT REPLY OFF / SKIP handling: don't send replies. */\n    if (c->flags & (CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP)) return C_ERR;\n\n    /* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag\n     * is set. */\n    if ((c->flags & CLIENT_MASTER) &&\n        !(c->flags & CLIENT_MASTER_FORCE_REPLY)) return C_ERR;\n\n    if (!c->conn) return C_ERR; /* Fake client for AOF loading. */\n\n    /* Schedule the client to write the output buffers to the socket, unless\n     * it should already be setup to do so (it has already pending data). */\n    if (!clientHasPendingReplies(c)) clientInstallWriteHandler(c);\n\n    /* Authorize the caller to queue in the output buffer of this client. */\n    return C_OK;\n}\n\n/* -----------------------------------------------------------------------------\n * Low level functions to add more data to output buffers.\n * -------------------------------------------------------------------------- */\n\nint _addReplyToBuffer(client *c, const char *s, size_t len) {\n    size_t available = sizeof(c->buf)-c->bufpos;\n\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return C_OK;\n\n    /* If there already are entries in the reply list, we cannot\n     * add anything more to the static buffer. */\n    if (listLength(c->reply) > 0) return C_ERR;\n\n    /* Check that the buffer has enough space available for this string. */\n    if (len > available) return C_ERR;\n\n    memcpy(c->buf+c->bufpos,s,len);\n    c->bufpos+=len;\n    return C_OK;\n}\n\nvoid _addReplyProtoToList(client *c, const char *s, size_t len) {\n    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;\n\n    listNode *ln = listLast(c->reply);\n    clientReplyBlock *tail = ln? listNodeValue(ln): NULL;\n\n    /* Note that 'tail' may be NULL even if we have a tail node, becuase when\n     * addDeferredMultiBulkLength() is used, it sets a dummy node to NULL just\n     * fo fill it later, when the size of the bulk length is set. */\n\n    /* Append to tail string when possible. */\n    if (tail) {\n        /* Copy the part we can fit into the tail, and leave the rest for a\n         * new node */\n        size_t avail = tail->size - tail->used;\n        size_t copy = avail >= len? len: avail;\n        memcpy(tail->buf + tail->used, s, copy);\n        tail->used += copy;\n        s += copy;\n        len -= copy;\n    }\n    if (len) {\n        /* Create a new node, make sure it is allocated to at\n         * least PROTO_REPLY_CHUNK_BYTES */\n        size_t size = len < PROTO_REPLY_CHUNK_BYTES? PROTO_REPLY_CHUNK_BYTES: len;\n        tail = zmalloc(size + sizeof(clientReplyBlock));\n        /* take over the allocation's internal fragmentation */\n        tail->size = zmalloc_usable(tail) - sizeof(clientReplyBlock);\n        tail->used = len;\n        memcpy(tail->buf, s, len);\n        listAddNodeTail(c->reply, tail);\n        c->reply_bytes += tail->size;\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\n/* Add the object 'obj' string representation to the client output buffer. */\nvoid addReply(client *c, robj *obj) {\n    if (prepareClientToWrite(c) != C_OK) return;\n\n    if (sdsEncodedObject(obj)) {\n        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)\n            _addReplyProtoToList(c,obj->ptr,sdslen(obj->ptr));\n    } else if (obj->encoding == OBJ_ENCODING_INT) {\n        /* For integer encoded strings we just convert it into a string\n         * using our optimized function, and attach the resulting string\n         * to the output buffer. */\n        char buf[32];\n        size_t len = ll2string(buf,sizeof(buf),(long)obj->ptr);\n        if (_addReplyToBuffer(c,buf,len) != C_OK)\n            _addReplyProtoToList(c,buf,len);\n    } else {\n        serverPanic(\"Wrong obj->encoding in addReply()\");\n    }\n}\n\n/* Add the SDS 's' string to the client output buffer, as a side effect\n * the SDS string is freed. */\nvoid addReplySds(client *c, sds s) {\n    if (prepareClientToWrite(c) != C_OK) {\n        /* The caller expects the sds to be free'd. */\n        sdsfree(s);\n        return;\n    }\n    if (_addReplyToBuffer(c,s,sdslen(s)) != C_OK)\n        _addReplyProtoToList(c,s,sdslen(s));\n    sdsfree(s);\n}\n\n/* This low level function just adds whatever protocol you send it to the\n * client buffer, trying the static buffer initially, and using the string\n * of objects if not possible.\n *\n * It is efficient because does not create an SDS object nor an Redis object\n * if not needed. The object will only be created by calling\n * _addReplyProtoToList() if we fail to extend the existing tail object\n * in the list of objects. */\nvoid addReplyProto(client *c, const char *s, size_t len) {\n    if (prepareClientToWrite(c) != C_OK) return;\n    if (_addReplyToBuffer(c,s,len) != C_OK)\n        _addReplyProtoToList(c,s,len);\n}\n\n/* Low level function called by the addReplyError...() functions.\n * It emits the protocol for a Redis error, in the form:\n *\n * -ERRORCODE Error Message<CR><LF>\n *\n * If the error code is already passed in the string 's', the error\n * code provided is used, otherwise the string \"-ERR \" for the generic\n * error code is automatically added. */\nvoid addReplyErrorLength(client *c, const char *s, size_t len) {\n    /* If the string already starts with \"-...\" then the error code\n     * is provided by the caller. Otherwise we use \"-ERR\". */\n    if (!len || s[0] != '-') addReplyProto(c,\"-ERR \",5);\n    addReplyProto(c,s,len);\n    addReplyProto(c,\"\\r\\n\",2);\n\n    /* Sometimes it could be normal that a slave replies to a master with\n     * an error and this function gets called. Actually the error will never\n     * be sent because addReply*() against master clients has no effect...\n     * A notable example is:\n     *\n     *    EVAL 'redis.call(\"incr\",KEYS[1]); redis.call(\"nonexisting\")' 1 x\n     *\n     * Where the master must propagate the first change even if the second\n     * will produce an error. However it is useful to log such events since\n     * they are rare and may hint at errors in a script or a bug in Redis. */\n    int ctype = getClientType(c);\n    if (ctype == CLIENT_TYPE_MASTER || ctype == CLIENT_TYPE_SLAVE || c->id == CLIENT_ID_AOF) {\n        char *to, *from;\n\n        if (c->id == CLIENT_ID_AOF) {\n            to = \"AOF-loading-client\";\n            from = \"server\";\n        } else if (ctype == CLIENT_TYPE_MASTER) {\n            to = \"master\";\n            from = \"replica\";\n        } else {\n            to = \"replica\";\n            from = \"master\";\n        }\n\n        char *cmdname = c->lastcmd ? c->lastcmd->name : \"<unknown>\";\n        serverLog(LL_WARNING,\"== CRITICAL == This %s is sending an error \"\n                             \"to its %s: '%s' after processing the command \"\n                             \"'%s'\", from, to, s, cmdname);\n        if (ctype == CLIENT_TYPE_MASTER && server.repl_backlog &&\n            server.repl_backlog_histlen > 0)\n        {\n            long long dumplen = 256;\n            if (server.repl_backlog_histlen < dumplen)\n                dumplen = server.repl_backlog_histlen;\n\n            /* Identify the first byte to dump. */\n            long long idx =\n              (server.repl_backlog_idx + (server.repl_backlog_size - dumplen)) %\n               server.repl_backlog_size;\n\n            /* Scan the circular buffer to collect 'dumplen' bytes. */\n            sds dump = sdsempty();\n            while(dumplen) {\n                long long thislen =\n                    ((server.repl_backlog_size - idx) < dumplen) ?\n                    (server.repl_backlog_size - idx) : dumplen;\n\n                dump = sdscatrepr(dump,server.repl_backlog+idx,thislen);\n                dumplen -= thislen;\n                idx = 0;\n            }\n\n            /* Finally log such bytes: this is vital debugging info to\n             * understand what happened. */\n            serverLog(LL_WARNING,\"Latest backlog is: '%s'\", dump);\n            sdsfree(dump);\n        }\n        server.stat_unexpected_error_replies++;\n    }\n}\n\nvoid addReplyError(client *c, const char *err) {\n    addReplyErrorLength(c,err,strlen(err));\n}\n\nvoid addReplyErrorFormat(client *c, const char *fmt, ...) {\n    size_t l, j;\n    va_list ap;\n    va_start(ap,fmt);\n    sds s = sdscatvprintf(sdsempty(),fmt,ap);\n    va_end(ap);\n    /* Make sure there are no newlines in the string, otherwise invalid protocol\n     * is emitted. */\n    l = sdslen(s);\n    for (j = 0; j < l; j++) {\n        if (s[j] == '\\r' || s[j] == '\\n') s[j] = ' ';\n    }\n    addReplyErrorLength(c,s,sdslen(s));\n    sdsfree(s);\n}\n\nvoid addReplyStatusLength(client *c, const char *s, size_t len) {\n    addReplyProto(c,\"+\",1);\n    addReplyProto(c,s,len);\n    addReplyProto(c,\"\\r\\n\",2);\n}\n\nvoid addReplyStatus(client *c, const char *status) {\n    addReplyStatusLength(c,status,strlen(status));\n}\n\nvoid addReplyStatusFormat(client *c, const char *fmt, ...) {\n    va_list ap;\n    va_start(ap,fmt);\n    sds s = sdscatvprintf(sdsempty(),fmt,ap);\n    va_end(ap);\n    addReplyStatusLength(c,s,sdslen(s));\n    sdsfree(s);\n}\n\n/* Sometimes we are forced to create a new reply node, and we can't append to\n * the previous one, when that happens, we wanna try to trim the unused space\n * at the end of the last reply node which we won't use anymore. */\nvoid trimReplyUnusedTailSpace(client *c) {\n    listNode *ln = listLast(c->reply);\n    clientReplyBlock *tail = ln? listNodeValue(ln): NULL;\n\n    /* Note that 'tail' may be NULL even if we have a tail node, becuase when\n     * addDeferredMultiBulkLength() is used */\n    if (!tail) return;\n\n    /* We only try to trim the space is relatively high (more than a 1/4 of the\n     * allocation), otherwise there's a high chance realloc will NOP.\n     * Also, to avoid large memmove which happens as part of realloc, we only do\n     * that if the used part is small.  */\n    if (tail->size - tail->used > tail->size / 4 &&\n        tail->used < PROTO_REPLY_CHUNK_BYTES)\n    {\n        size_t old_size = tail->size;\n        tail = zrealloc(tail, tail->used + sizeof(clientReplyBlock));\n        /* take over the allocation's internal fragmentation (at least for\n         * memory usage tracking) */\n        tail->size = zmalloc_usable(tail) - sizeof(clientReplyBlock);\n        c->reply_bytes += tail->size - old_size;\n        listNodeValue(ln) = tail;\n    }\n}\n\n/* Adds an empty object to the reply list that will contain the multi bulk\n * length, which is not known when this function is called. */\nvoid *addReplyDeferredLen(client *c) {\n    /* Note that we install the write event here even if the object is not\n     * ready to be sent, since we are sure that before returning to the\n     * event loop setDeferredAggregateLen() will be called. */\n    if (prepareClientToWrite(c) != C_OK) return NULL;\n    trimReplyUnusedTailSpace(c);\n    listAddNodeTail(c->reply,NULL); /* NULL is our placeholder. */\n    return listLast(c->reply);\n}\n\n/* Populate the length object and try gluing it to the next chunk. */\nvoid setDeferredAggregateLen(client *c, void *node, long length, char prefix) {\n    listNode *ln = (listNode*)node;\n    clientReplyBlock *next;\n    char lenstr[128];\n    size_t lenstr_len = sprintf(lenstr, \"%c%ld\\r\\n\", prefix, length);\n\n    /* Abort when *node is NULL: when the client should not accept writes\n     * we return NULL in addReplyDeferredLen() */\n    if (node == NULL) return;\n    serverAssert(!listNodeValue(ln));\n\n    /* Normally we fill this dummy NULL node, added by addReplyDeferredLen(),\n     * with a new buffer structure containing the protocol needed to specify\n     * the length of the array following. However sometimes when there is\n     * little memory to move, we may instead remove this NULL node, and prefix\n     * our protocol in the node immediately after to it, in order to save a\n     * write(2) syscall later. Conditions needed to do it:\n     *\n     * - The next node is non-NULL,\n     * - It has enough room already allocated\n     * - And not too large (avoid large memmove) */\n    if (ln->next != NULL && (next = listNodeValue(ln->next)) &&\n        next->size - next->used >= lenstr_len &&\n        next->used < PROTO_REPLY_CHUNK_BYTES * 4) {\n        memmove(next->buf + lenstr_len, next->buf, next->used);\n        memcpy(next->buf, lenstr, lenstr_len);\n        next->used += lenstr_len;\n        listDelNode(c->reply,ln);\n    } else {\n        /* Create a new node */\n        clientReplyBlock *buf = zmalloc(lenstr_len + sizeof(clientReplyBlock));\n        /* Take over the allocation's internal fragmentation */\n        buf->size = zmalloc_usable(buf) - sizeof(clientReplyBlock);\n        buf->used = lenstr_len;\n        memcpy(buf->buf, lenstr, lenstr_len);\n        listNodeValue(ln) = buf;\n        c->reply_bytes += buf->size;\n    }\n    asyncCloseClientOnOutputBufferLimitReached(c);\n}\n\nvoid setDeferredArrayLen(client *c, void *node, long length) {\n    setDeferredAggregateLen(c,node,length,'*');\n}\n\nvoid setDeferredMapLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '%';\n    if (c->resp == 2) length *= 2;\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\nvoid setDeferredSetLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '~';\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\nvoid setDeferredAttributeLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '|';\n    if (c->resp == 2) length *= 2;\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\nvoid setDeferredPushLen(client *c, void *node, long length) {\n    int prefix = c->resp == 2 ? '*' : '>';\n    setDeferredAggregateLen(c,node,length,prefix);\n}\n\n/* Add a double as a bulk reply */\nvoid addReplyDouble(client *c, double d) {\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        if (c->resp == 2) {\n            addReplyBulkCString(c, d > 0 ? \"inf\" : \"-inf\");\n        } else {\n            addReplyProto(c, d > 0 ? \",inf\\r\\n\" : \",-inf\\r\\n\",\n                              d > 0 ? 6 : 7);\n        }\n    } else {\n        char dbuf[MAX_LONG_DOUBLE_CHARS+3],\n             sbuf[MAX_LONG_DOUBLE_CHARS+32];\n        int dlen, slen;\n        if (c->resp == 2) {\n            dlen = snprintf(dbuf,sizeof(dbuf),\"%.17g\",d);\n            slen = snprintf(sbuf,sizeof(sbuf),\"$%d\\r\\n%s\\r\\n\",dlen,dbuf);\n            addReplyProto(c,sbuf,slen);\n        } else {\n            dlen = snprintf(dbuf,sizeof(dbuf),\",%.17g\\r\\n\",d);\n            addReplyProto(c,dbuf,dlen);\n        }\n    }\n}\n\n/* Add a long double as a bulk reply, but uses a human readable formatting\n * of the double instead of exposing the crude behavior of doubles to the\n * dear user. */\nvoid addReplyHumanLongDouble(client *c, long double d) {\n    if (c->resp == 2) {\n        robj *o = createStringObjectFromLongDouble(d,1);\n        addReplyBulk(c,o);\n        decrRefCount(o);\n    } else {\n        char buf[MAX_LONG_DOUBLE_CHARS];\n        int len = ld2string(buf,sizeof(buf),d,LD_STR_HUMAN);\n        addReplyProto(c,\",\",1);\n        addReplyProto(c,buf,len);\n        addReplyProto(c,\"\\r\\n\",2);\n    }\n}\n\n/* Add a long long as integer reply or bulk len / multi bulk count.\n * Basically this is used to output <prefix><long long><crlf>. */\nvoid addReplyLongLongWithPrefix(client *c, long long ll, char prefix) {\n    char buf[128];\n    int len;\n\n    /* Things like $3\\r\\n or *2\\r\\n are emitted very often by the protocol\n     * so we have a few shared objects to use if the integer is small\n     * like it is most of the times. */\n    if (prefix == '*' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {\n        addReply(c,shared.mbulkhdr[ll]);\n        return;\n    } else if (prefix == '$' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {\n        addReply(c,shared.bulkhdr[ll]);\n        return;\n    }\n\n    buf[0] = prefix;\n    len = ll2string(buf+1,sizeof(buf)-1,ll);\n    buf[len+1] = '\\r';\n    buf[len+2] = '\\n';\n    addReplyProto(c,buf,len+3);\n}\n\nvoid addReplyLongLong(client *c, long long ll) {\n    if (ll == 0)\n        addReply(c,shared.czero);\n    else if (ll == 1)\n        addReply(c,shared.cone);\n    else\n        addReplyLongLongWithPrefix(c,ll,':');\n}\n\nvoid addReplyAggregateLen(client *c, long length, int prefix) {\n    if (prefix == '*' && length < OBJ_SHARED_BULKHDR_LEN)\n        addReply(c,shared.mbulkhdr[length]);\n    else\n        addReplyLongLongWithPrefix(c,length,prefix);\n}\n\nvoid addReplyArrayLen(client *c, long length) {\n    addReplyAggregateLen(c,length,'*');\n}\n\nvoid addReplyMapLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '%';\n    if (c->resp == 2) length *= 2;\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplySetLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '~';\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplyAttributeLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '|';\n    if (c->resp == 2) length *= 2;\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplyPushLen(client *c, long length) {\n    int prefix = c->resp == 2 ? '*' : '>';\n    addReplyAggregateLen(c,length,prefix);\n}\n\nvoid addReplyNull(client *c) {\n    if (c->resp == 2) {\n        addReplyProto(c,\"$-1\\r\\n\",5);\n    } else {\n        addReplyProto(c,\"_\\r\\n\",3);\n    }\n}\n\nvoid addReplyBool(client *c, int b) {\n    if (c->resp == 2) {\n        addReply(c, b ? shared.cone : shared.czero);\n    } else {\n        addReplyProto(c, b ? \"#t\\r\\n\" : \"#f\\r\\n\",4);\n    }\n}\n\n/* A null array is a concept that no longer exists in RESP3. However\n * RESP2 had it, so API-wise we have this call, that will emit the correct\n * RESP2 protocol, however for RESP3 the reply will always be just the\n * Null type \"_\\r\\n\". */\nvoid addReplyNullArray(client *c) {\n    if (c->resp == 2) {\n        addReplyProto(c,\"*-1\\r\\n\",5);\n    } else {\n        addReplyProto(c,\"_\\r\\n\",3);\n    }\n}\n\n/* Create the length prefix of a bulk reply, example: $2234 */\nvoid addReplyBulkLen(client *c, robj *obj) {\n    size_t len = stringObjectLen(obj);\n\n    if (len < OBJ_SHARED_BULKHDR_LEN)\n        addReply(c,shared.bulkhdr[len]);\n    else\n        addReplyLongLongWithPrefix(c,len,'$');\n}\n\n/* Add a Redis Object as a bulk reply */\nvoid addReplyBulk(client *c, robj *obj) {\n    addReplyBulkLen(c,obj);\n    addReply(c,obj);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C buffer as bulk reply */\nvoid addReplyBulkCBuffer(client *c, const void *p, size_t len) {\n    addReplyLongLongWithPrefix(c,len,'$');\n    addReplyProto(c,p,len);\n    addReply(c,shared.crlf);\n}\n\n/* Add sds to reply (takes ownership of sds and frees it) */\nvoid addReplyBulkSds(client *c, sds s)  {\n    addReplyLongLongWithPrefix(c,sdslen(s),'$');\n    addReplySds(c,s);\n    addReply(c,shared.crlf);\n}\n\n/* Add a C null term string as bulk reply */\nvoid addReplyBulkCString(client *c, const char *s) {\n    if (s == NULL) {\n        addReplyNull(c);\n    } else {\n        addReplyBulkCBuffer(c,s,strlen(s));\n    }\n}\n\n/* Add a long long as a bulk reply */\nvoid addReplyBulkLongLong(client *c, long long ll) {\n    char buf[64];\n    int len;\n\n    len = ll2string(buf,64,ll);\n    addReplyBulkCBuffer(c,buf,len);\n}\n\n/* Reply with a verbatim type having the specified extension.\n *\n * The 'ext' is the \"extension\" of the file, actually just a three\n * character type that describes the format of the verbatim string.\n * For instance \"txt\" means it should be interpreted as a text only\n * file by the receiver, \"md \" as markdown, and so forth. Only the\n * three first characters of the extension are used, and if the\n * provided one is shorter than that, the remaining is filled with\n * spaces. */\nvoid addReplyVerbatim(client *c, const char *s, size_t len, const char *ext) {\n    if (c->resp == 2) {\n        addReplyBulkCBuffer(c,s,len);\n    } else {\n        char buf[32];\n        size_t preflen = snprintf(buf,sizeof(buf),\"=%zu\\r\\nxxx:\",len+4);\n        char *p = buf+preflen-4;\n        for (int i = 0; i < 3; i++) {\n            if (*ext == '\\0') {\n                p[i] = ' ';\n            } else {\n                p[i] = *ext++;\n            }\n        }\n        addReplyProto(c,buf,preflen);\n        addReplyProto(c,s,len);\n        addReplyProto(c,\"\\r\\n\",2);\n    }\n}\n\n/* Add an array of C strings as status replies with a heading.\n * This function is typically invoked by from commands that support\n * subcommands in response to the 'help' subcommand. The help array\n * is terminated by NULL sentinel. */\nvoid addReplyHelp(client *c, const char **help) {\n    sds cmd = sdsnew((char*) c->argv[0]->ptr);\n    void *blenp = addReplyDeferredLen(c);\n    int blen = 0;\n\n    sdstoupper(cmd);\n    addReplyStatusFormat(c,\n        \"%s <subcommand> arg arg ... arg. Subcommands are:\",cmd);\n    sdsfree(cmd);\n\n    while (help[blen]) addReplyStatus(c,help[blen++]);\n\n    blen++;  /* Account for the header line(s). */\n    setDeferredArrayLen(c,blenp,blen);\n}\n\n/* Add a suggestive error reply.\n * This function is typically invoked by from commands that support\n * subcommands in response to an unknown subcommand or argument error. */\nvoid addReplySubcommandSyntaxError(client *c) {\n    sds cmd = sdsnew((char*) c->argv[0]->ptr);\n    sdstoupper(cmd);\n    addReplyErrorFormat(c,\n        \"Unknown subcommand or wrong number of arguments for '%s'. Try %s HELP.\",\n        (char*)c->argv[1]->ptr,cmd);\n    sdsfree(cmd);\n}\n\n/* Append 'src' client output buffers into 'dst' client output buffers. \n * This function clears the output buffers of 'src' */\nvoid AddReplyFromClient(client *dst, client *src) {\n    if (prepareClientToWrite(dst) != C_OK)\n        return;\n    addReplyProto(dst,src->buf, src->bufpos);\n    if (listLength(src->reply))\n        listJoin(dst->reply,src->reply);\n    dst->reply_bytes += src->reply_bytes;\n    src->reply_bytes = 0;\n    src->bufpos = 0;\n}\n\n/* Copy 'src' client output buffers into 'dst' client output buffers.\n * The function takes care of freeing the old output buffers of the\n * destination client. */\nvoid copyClientOutputBuffer(client *dst, client *src) {\n    listRelease(dst->reply);\n    dst->sentlen = 0;\n    dst->reply = listDup(src->reply);\n    memcpy(dst->buf,src->buf,src->bufpos);\n    dst->bufpos = src->bufpos;\n    dst->reply_bytes = src->reply_bytes;\n}\n\n/* Return true if the specified client has pending reply buffers to write to\n * the socket. */\nint clientHasPendingReplies(client *c) {\n    return c->bufpos || listLength(c->reply);\n}\n\nvoid clientAcceptHandler(connection *conn) {\n    client *c = connGetPrivateData(conn);\n\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_WARNING,\n                \"Error accepting a client connection: %s\",\n                connGetLastError(conn));\n        freeClientAsync(c);\n        return;\n    }\n\n    /* If the server is running in protected mode (the default) and there\n     * is no password set, nor a specific interface is bound, we don't accept\n     * requests from non loopback interfaces. Instead we try to explain the\n     * user what to do to fix it if needed. */\n    if (server.protected_mode &&\n        server.bindaddr_count == 0 &&\n        DefaultUser->flags & USER_FLAG_NOPASS &&\n        !(c->flags & CLIENT_UNIX_SOCKET))\n    {\n        char cip[NET_IP_STR_LEN+1] = { 0 };\n        connPeerToString(conn, cip, sizeof(cip)-1, NULL);\n\n        if (strcmp(cip,\"127.0.0.1\") && strcmp(cip,\"::1\")) {\n            char *err =\n                \"-DENIED Redis is running in protected mode because protected \"\n                \"mode is enabled, no bind address was specified, no \"\n                \"authentication password is requested to clients. In this mode \"\n                \"connections are only accepted from the loopback interface. \"\n                \"If you want to connect from external computers to Redis you \"\n                \"may adopt one of the following solutions: \"\n                \"1) Just disable protected mode sending the command \"\n                \"'CONFIG SET protected-mode no' from the loopback interface \"\n                \"by connecting to Redis from the same host the server is \"\n                \"running, however MAKE SURE Redis is not publicly accessible \"\n                \"from internet if you do so. Use CONFIG REWRITE to make this \"\n                \"change permanent. \"\n                \"2) Alternatively you can just disable the protected mode by \"\n                \"editing the Redis configuration file, and setting the protected \"\n                \"mode option to 'no', and then restarting the server. \"\n                \"3) If you started the server manually just for testing, restart \"\n                \"it with the '--protected-mode no' option. \"\n                \"4) Setup a bind address or an authentication password. \"\n                \"NOTE: You only need to do one of the above things in order for \"\n                \"the server to start accepting connections from the outside.\\r\\n\";\n            if (connWrite(c->conn,err,strlen(err)) == -1) {\n                /* Nothing to do, Just to avoid the warning... */\n            }\n            server.stat_rejected_conn++;\n            freeClientAsync(c);\n            return;\n        }\n    }\n\n    server.stat_numconnections++;\n    moduleFireServerEvent(REDISMODULE_EVENT_CLIENT_CHANGE,\n                          REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED,\n                          c);\n}\n\n#define MAX_ACCEPTS_PER_CALL 1000\nstatic void acceptCommonHandler(connection *conn, int flags, char *ip) {\n    client *c;\n    UNUSED(ip);\n\n    /* Admission control will happen before a client is created and connAccept()\n     * called, because we don't want to even start transport-level negotiation\n     * if rejected.\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         * Note that for TLS connections, no handshake was done yet so nothing is written\n         * and the connection will just drop.\n         */\n        if (connWrite(conn,err,strlen(err)) == -1) {\n            /* Nothing to do, Just to avoid the warning... */\n        }\n        server.stat_rejected_conn++;\n        connClose(conn);\n        return;\n    }\n\n    /* Create connection and client */\n    if ((c = createClient(conn)) == NULL) {\n        char conninfo[100];\n        serverLog(LL_WARNING,\n            \"Error registering fd event for the new client: %s (conn: %s)\",\n            connGetLastError(conn),\n            connGetInfo(conn, conninfo, sizeof(conninfo)));\n        connClose(conn); /* May be already closed, just ignore errors */\n        return;\n    }\n\n    /* Last chance to keep flags */\n    c->flags |= flags;\n\n    /* Initiate accept.\n     *\n     * Note that connAccept() is free to do two things here:\n     * 1. Call clientAcceptHandler() immediately;\n     * 2. Schedule a future call to clientAcceptHandler().\n     *\n     * Because of that, we must do nothing else afterwards.\n     */\n    if (connAccept(conn, clientAcceptHandler) == C_ERR) {\n        char conninfo[100];\n        if (connGetState(conn) == CONN_STATE_ERROR)\n            serverLog(LL_WARNING,\n                    \"Error accepting a client connection: %s (conn: %s)\",\n                    connGetLastError(conn), connGetInfo(conn, conninfo, sizeof(conninfo)));\n        freeClient(connGetPrivateData(conn));\n        return;\n    }\n}\n\nvoid acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;\n    char cip[NET_IP_STR_LEN];\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(privdata);\n\n    while(max--) {\n        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                serverLog(LL_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        serverLog(LL_VERBOSE,\"Accepted %s:%d\", cip, cport);\n        acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip);\n    }\n}\n\nvoid acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;\n    char cip[NET_IP_STR_LEN];\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(privdata);\n\n    while(max--) {\n        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                serverLog(LL_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        serverLog(LL_VERBOSE,\"Accepted %s:%d\", cip, cport);\n        acceptCommonHandler(connCreateAcceptedTLS(cfd, server.tls_auth_clients),0,cip);\n    }\n}\n\nvoid acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {\n    int cfd, max = MAX_ACCEPTS_PER_CALL;\n    UNUSED(el);\n    UNUSED(mask);\n    UNUSED(privdata);\n\n    while(max--) {\n        cfd = anetUnixAccept(server.neterr, fd);\n        if (cfd == ANET_ERR) {\n            if (errno != EWOULDBLOCK)\n                serverLog(LL_WARNING,\n                    \"Accepting client connection: %s\", server.neterr);\n            return;\n        }\n        serverLog(LL_VERBOSE,\"Accepted connection to %s\", server.unixsocket);\n        acceptCommonHandler(connCreateAcceptedSocket(cfd),CLIENT_UNIX_SOCKET,NULL);\n    }\n}\n\nstatic void freeClientArgv(client *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. */\nvoid disconnectSlaves(void) {\n    while (listLength(server.slaves)) {\n        listNode *ln = listFirst(server.slaves);\n        freeClient((client*)ln->value);\n    }\n}\n\n/* Remove the specified client from global lists where the client could\n * be referenced, not including the Pub/Sub channels.\n * This is used by freeClient() and replicationCacheMaster(). */\nvoid unlinkClient(client *c) {\n    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    /* Certain operations must be done only if the client has an active connection.\n     * If the client was already unlinked or if it's a \"fake client\" the\n     * conn is already set to NULL. */\n    if (c->conn) {\n        /* Remove from the list of active clients. */\n        if (c->client_list_node) {\n            uint64_t id = htonu64(c->id);\n            raxRemove(server.clients_index,(unsigned char*)&id,sizeof(id),NULL);\n            listDelNode(server.clients,c->client_list_node);\n            c->client_list_node = NULL;\n        }\n\n        /* Check if this is a replica waiting for diskless replication (rdb pipe),\n         * in which case it needs to be cleaned from that list */\n        if (c->flags & CLIENT_SLAVE &&\n            c->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&\n            server.rdb_pipe_conns)\n        {\n            int i;\n            for (i=0; i < server.rdb_pipe_numconns; i++) {\n                if (server.rdb_pipe_conns[i] == c->conn) {\n                    rdbPipeWriteHandlerConnRemoved(c->conn);\n                    server.rdb_pipe_conns[i] = NULL;\n                    break;\n                }\n            }\n        }\n        connClose(c->conn);\n        c->conn = NULL;\n    }\n\n    /* Remove from the list of pending writes if needed. */\n    if (c->flags & CLIENT_PENDING_WRITE) {\n        ln = listSearchKey(server.clients_pending_write,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.clients_pending_write,ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n    }\n\n    /* Remove from the list of pending reads if needed. */\n    if (c->flags & CLIENT_PENDING_READ) {\n        ln = listSearchKey(server.clients_pending_read,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.clients_pending_read,ln);\n        c->flags &= ~CLIENT_PENDING_READ;\n    }\n\n    /* When client was just unblocked because of a blocking operation,\n     * remove it from the list of unblocked clients. */\n    if (c->flags & CLIENT_UNBLOCKED) {\n        ln = listSearchKey(server.unblocked_clients,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.unblocked_clients,ln);\n        c->flags &= ~CLIENT_UNBLOCKED;\n    }\n\n    /* Clear the tracking status. */\n    if (c->flags & CLIENT_TRACKING) disableTracking(c);\n}\n\nvoid freeClient(client *c) {\n    listNode *ln;\n\n    /* If a client is protected, yet we need to free it right now, make sure\n     * to at least use asynchronous freeing. */\n    if (c->flags & CLIENT_PROTECTED) {\n        freeClientAsync(c);\n        return;\n    }\n\n    /* For connected clients, call the disconnection event of modules hooks. */\n    if (c->conn) {\n        moduleFireServerEvent(REDISMODULE_EVENT_CLIENT_CHANGE,\n                              REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED,\n                              c);\n    }\n\n    /* Notify module system that this client auth status changed. */\n    moduleNotifyUserChanged(c);\n\n    /* If this client was scheduled for async freeing we need to remove it\n     * from the queue. Note that we need to do this here, because later\n     * we may call replicationCacheMaster() and the client should already\n     * be removed from the list of clients to free. */\n    if (c->flags & CLIENT_CLOSE_ASAP) {\n        ln = listSearchKey(server.clients_to_close,c);\n        serverAssert(ln != NULL);\n        listDelNode(server.clients_to_close,ln);\n    }\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 & CLIENT_MASTER) {\n        serverLog(LL_WARNING,\"Connection with master lost.\");\n        if (!(c->flags & (CLIENT_PROTOCOL_ERROR|CLIENT_BLOCKED))) {\n            c->flags &= ~(CLIENT_CLOSE_ASAP|CLIENT_CLOSE_AFTER_REPLY);\n            replicationCacheMaster(c);\n            return;\n        }\n    }\n\n    /* Log link disconnection with slave */\n    if (getClientType(c) == CLIENT_TYPE_SLAVE) {\n        serverLog(LL_WARNING,\"Connection with replica %s lost.\",\n            replicationGetSlaveName(c));\n    }\n\n    /* Free the query buffer */\n    sdsfree(c->querybuf);\n    sdsfree(c->pending_querybuf);\n    c->querybuf = NULL;\n\n    /* Deallocate structures used to block on blocking ops. */\n    if (c->flags & CLIENT_BLOCKED) unblockClient(c);\n    dictRelease(c->bpop.keys);\n\n    /* UNWATCH all the keys */\n    unwatchAllKeys(c);\n    listRelease(c->watched_keys);\n\n    /* Unsubscribe from all the pubsub channels */\n    pubsubUnsubscribeAllChannels(c,0);\n    pubsubUnsubscribeAllPatterns(c,0);\n    dictRelease(c->pubsub_channels);\n    listRelease(c->pubsub_patterns);\n\n    /* Free data structures. */\n    listRelease(c->reply);\n    freeClientArgv(c);\n\n    /* Unlink the client: this will close the socket, remove the I/O\n     * handlers, and remove references of the client from different\n     * places where active clients may be referenced. */\n    unlinkClient(c);\n\n    /* Master/slave cleanup Case 1:\n     * we lost the connection with a slave. */\n    if (c->flags & CLIENT_SLAVE) {\n        if (c->replstate == SLAVE_STATE_SEND_BULK) {\n            if (c->repldbfd != -1) close(c->repldbfd);\n            if (c->replpreamble) sdsfree(c->replpreamble);\n        }\n        list *l = (c->flags & CLIENT_MONITOR) ? server.monitors : server.slaves;\n        ln = listSearchKey(l,c);\n        serverAssert(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 (getClientType(c) == CLIENT_TYPE_SLAVE && listLength(server.slaves) == 0)\n            server.repl_no_slaves_since = server.unixtime;\n        refreshGoodSlavesCount();\n        /* Fire the replica change modules event. */\n        if (c->replstate == SLAVE_STATE_ONLINE)\n            moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,\n                                  REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE,\n                                  NULL);\n    }\n\n    /* Master/slave cleanup Case 2:\n     * we lost the connection with the master. */\n    if (c->flags & CLIENT_MASTER) replicationHandleMasterDisconnection();\n\n   /* Remove the contribution that this client gave to our\n     * incrementally computed memory usage. */\n    server.stat_clients_type_memory[c->client_cron_last_memory_type] -=\n        c->client_cron_last_memory_usage;\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    zfree(c->argv);\n    freeClientMultiState(c);\n    sdsfree(c->peerid);\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. */\nvoid freeClientAsync(client *c) {\n    /* We need to handle concurrent access to the server.clients_to_close list\n     * only in the freeClientAsync() function, since it's the only function that\n     * may access the list while Redis uses I/O threads. All the other accesses\n     * are in the context of the main thread while the other threads are\n     * idle. */\n    if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;\n    c->flags |= CLIENT_CLOSE_ASAP;\n    if (server.io_threads_num == 1) {\n        /* no need to bother with locking if there's just one thread (the main thread) */\n        listAddNodeTail(server.clients_to_close,c);\n        return;\n    }\n    static pthread_mutex_t async_free_queue_mutex = PTHREAD_MUTEX_INITIALIZER;\n    pthread_mutex_lock(&async_free_queue_mutex);\n    listAddNodeTail(server.clients_to_close,c);\n    pthread_mutex_unlock(&async_free_queue_mutex);\n}\n\n/* Free the clietns marked as CLOSE_ASAP, return the number of clients\n * freed. */\nint freeClientsInAsyncFreeQueue(void) {\n    int freed = listLength(server.clients_to_close);\n    while (listLength(server.clients_to_close)) {\n        listNode *ln = listFirst(server.clients_to_close);\n        client *c = listNodeValue(ln);\n\n        c->flags &= ~CLIENT_CLOSE_ASAP;\n        freeClient(c);\n        listDelNode(server.clients_to_close,ln);\n    }\n    return freed;\n}\n\n/* Return a client by ID, or NULL if the client ID is not in the set\n * of registered clients. Note that \"fake clients\", created with -1 as FD,\n * are not registered clients. */\nclient *lookupClientByID(uint64_t id) {\n    id = htonu64(id);\n    client *c = raxFind(server.clients_index,(unsigned char*)&id,sizeof(id));\n    return (c == raxNotFound) ? NULL : c;\n}\n\n/* Write data in output buffers to client. Return C_OK if the client\n * is still valid after the call, C_ERR if it was freed because of some\n * error.  If handler_installed is set, it will attempt to clear the\n * write event.\n *\n * This function is called by threads, but always with handler_installed\n * set to 0. So when handler_installed is set to 0 the function must be\n * thread safe. */\nint writeToClient(client *c, int handler_installed) {\n    ssize_t nwritten = 0, totwritten = 0;\n    size_t objlen;\n    clientReplyBlock *o;\n\n    while(clientHasPendingReplies(c)) {\n        if (c->bufpos > 0) {\n            nwritten = connWrite(c->conn,c->buf+c->sentlen,c->bufpos-c->sentlen);\n            if (nwritten <= 0) break;\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If the buffer was sent, set bufpos to zero to continue with\n             * the remainder of the reply. */\n            if ((int)c->sentlen == c->bufpos) {\n                c->bufpos = 0;\n                c->sentlen = 0;\n            }\n        } else {\n            o = listNodeValue(listFirst(c->reply));\n            objlen = o->used;\n\n            if (objlen == 0) {\n                c->reply_bytes -= o->size;\n                listDelNode(c->reply,listFirst(c->reply));\n                continue;\n            }\n\n            nwritten = connWrite(c->conn, o->buf + c->sentlen, objlen - c->sentlen);\n            if (nwritten <= 0) break;\n            c->sentlen += nwritten;\n            totwritten += nwritten;\n\n            /* If we fully sent the object on head go to the next one */\n            if (c->sentlen == objlen) {\n                c->reply_bytes -= o->size;\n                listDelNode(c->reply,listFirst(c->reply));\n                c->sentlen = 0;\n                /* If there are no longer objects in the list, we expect\n                 * the count of reply bytes to be exactly zero. */\n                if (listLength(c->reply) == 0)\n                    serverAssert(c->reply_bytes == 0);\n            }\n        }\n        /* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT\n         * bytes, in a single threaded server it's a good idea to serve\n         * other clients as well, even if a very large request comes from\n         * super fast link that is always able to accept data (in real world\n         * scenario think about 'KEYS *' against the loopback interface).\n         *\n         * However if we are over the maxmemory limit we ignore that and\n         * just deliver as much data as it is possible to deliver.\n         *\n         * Moreover, we also send as much as possible if the client is\n         * a slave or a monitor (otherwise, on high-speed traffic, the\n         * replication/output buffer will grow indefinitely) */\n        if (totwritten > NET_MAX_WRITES_PER_EVENT &&\n            (server.maxmemory == 0 ||\n             zmalloc_used_memory() < server.maxmemory) &&\n            !(c->flags & CLIENT_SLAVE)) break;\n    }\n    server.stat_net_output_bytes += totwritten;\n    if (nwritten == -1) {\n        if (connGetState(c->conn) == CONN_STATE_CONNECTED) {\n            nwritten = 0;\n        } else {\n            serverLog(LL_VERBOSE,\n                \"Error writing to client: %s\", connGetLastError(c->conn));\n            freeClientAsync(c);\n            return C_ERR;\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 & CLIENT_MASTER)) c->lastinteraction = server.unixtime;\n    }\n    if (!clientHasPendingReplies(c)) {\n        c->sentlen = 0;\n        /* Note that writeToClient() is called in a threaded way, but\n         * adDeleteFileEvent() is not thread safe: however writeToClient()\n         * is always called with handler_installed set to 0 from threads\n         * so we are fine. */\n        if (handler_installed) connSetWriteHandler(c->conn, NULL);\n\n        /* Close connection after entire reply has been sent. */\n        if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {\n            freeClientAsync(c);\n            return C_ERR;\n        }\n    }\n    return C_OK;\n}\n\n/* Write event handler. Just send data to the client. */\nvoid sendReplyToClient(connection *conn) {\n    client *c = connGetPrivateData(conn);\n    writeToClient(c,1);\n}\n\n/* This function is called just before entering the event loop, in the hope\n * we can just write the replies to the client output buffer without any\n * need to use a syscall in order to install the writable event handler,\n * get it called, and so forth. */\nint handleClientsWithPendingWrites(void) {\n    listIter li;\n    listNode *ln;\n    int processed = listLength(server.clients_pending_write);\n\n    listRewind(server.clients_pending_write,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n        listDelNode(server.clients_pending_write,ln);\n\n        /* If a client is protected, don't do anything,\n         * that may trigger write error or recreate handler. */\n        if (c->flags & CLIENT_PROTECTED) continue;\n\n        /* Try to write buffers to the client socket. */\n        if (writeToClient(c,0) == C_ERR) continue;\n\n        /* If after the synchronous writes above we still have data to\n         * output to the client, we need to install the writable handler. */\n        if (clientHasPendingReplies(c)) {\n            int ae_barrier = 0;\n            /* For the fsync=always policy, we want that a given FD is never\n             * served for reading and writing in the same event loop iteration,\n             * so that in the middle of receiving the query, and serving it\n             * to the client, we'll call beforeSleep() that will do the\n             * actual fsync of AOF to disk. the write barrier ensures that. */\n            if (server.aof_state == AOF_ON &&\n                server.aof_fsync == AOF_FSYNC_ALWAYS)\n            {\n                ae_barrier = 1;\n            }\n            if (connSetWriteHandlerWithBarrier(c->conn, sendReplyToClient, ae_barrier) == C_ERR) {\n                freeClientAsync(c);\n            }\n        }\n    }\n    return processed;\n}\n\n/* resetClient prepare the client to process the next command */\nvoid resetClient(client *c) {\n    redisCommandProc *prevcmd = c->cmd ? c->cmd->proc : NULL;\n\n    freeClientArgv(c);\n    c->reqtype = 0;\n    c->multibulklen = 0;\n    c->bulklen = -1;\n\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 & CLIENT_MULTI) && prevcmd != askingCommand)\n        c->flags &= ~CLIENT_ASKING;\n\n    /* We do the same for the CACHING command as well. It also affects\n     * the next command or transaction executed, in a way very similar\n     * to ASKING. */\n    if (!(c->flags & CLIENT_MULTI) && prevcmd != clientCommand)\n        c->flags &= ~CLIENT_TRACKING_CACHING;\n\n    /* Remove the CLIENT_REPLY_SKIP flag if any so that the reply\n     * to the next command will be sent, but set the flag if the command\n     * we just processed was \"CLIENT REPLY SKIP\". */\n    c->flags &= ~CLIENT_REPLY_SKIP;\n    if (c->flags & CLIENT_REPLY_SKIP_NEXT) {\n        c->flags |= CLIENT_REPLY_SKIP;\n        c->flags &= ~CLIENT_REPLY_SKIP_NEXT;\n    }\n}\n\n/* This funciton is used when we want to re-enter the event loop but there\n * is the risk that the client we are dealing with will be freed in some\n * way. This happens for instance in:\n *\n * * DEBUG RELOAD and similar.\n * * When a Lua script is in -BUSY state.\n *\n * So the function will protect the client by doing two things:\n *\n * 1) It removes the file events. This way it is not possible that an\n *    error is signaled on the socket, freeing the client.\n * 2) Moreover it makes sure that if the client is freed in a different code\n *    path, it is not really released, but only marked for later release. */\nvoid protectClient(client *c) {\n    c->flags |= CLIENT_PROTECTED;\n    connSetReadHandler(c->conn,NULL);\n    connSetWriteHandler(c->conn,NULL);\n}\n\n/* This will undo the client protection done by protectClient() */\nvoid unprotectClient(client *c) {\n    if (c->flags & CLIENT_PROTECTED) {\n        c->flags &= ~CLIENT_PROTECTED;\n        connSetReadHandler(c->conn,readQueryFromClient);\n        if (clientHasPendingReplies(c)) clientInstallWriteHandler(c);\n    }\n}\n\n/* Like processMultibulkBuffer(), but for the inline protocol instead of RESP,\n * this function consumes the client query buffer and creates a command ready\n * to be executed inside the client structure. Returns C_OK if the command\n * is ready to be executed, or C_ERR if there is still protocol to read to\n * have a well formed command. The function also returns C_ERR when there is\n * a protocol error: in such a case the client structure is setup to reply\n * with the error and close the connection. */\nint processInlineBuffer(client *c) {\n    char *newline;\n    int argc, j, linefeed_chars = 1;\n    sds *argv, aux;\n    size_t querylen;\n\n    /* Search for end of line */\n    newline = strchr(c->querybuf+c->qb_pos,'\\n');\n\n    /* Nothing to do without a \\r\\n */\n    if (newline == NULL) {\n        if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {\n            addReplyError(c,\"Protocol error: too big inline request\");\n            setProtocolError(\"too big inline request\",c);\n        }\n        return C_ERR;\n    }\n\n    /* Handle the \\r\\n case. */\n    if (newline && newline != c->querybuf+c->qb_pos && *(newline-1) == '\\r')\n        newline--, linefeed_chars++;\n\n    /* Split the input buffer up to the \\r\\n */\n    querylen = newline-(c->querybuf+c->qb_pos);\n    aux = sdsnewlen(c->querybuf+c->qb_pos,querylen);\n    argv = sdssplitargs(aux,&argc);\n    sdsfree(aux);\n    if (argv == NULL) {\n        addReplyError(c,\"Protocol error: unbalanced quotes in request\");\n        setProtocolError(\"unbalanced quotes in inline request\",c);\n        return C_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 && getClientType(c) == CLIENT_TYPE_SLAVE)\n        c->repl_ack_time = server.unixtime;\n\n    /* Move querybuffer position to the next query in the buffer. */\n    c->qb_pos += querylen+linefeed_chars;\n\n    /* Setup argv array on client structure */\n    if (argc) {\n        if (c->argv) zfree(c->argv);\n        c->argv = zmalloc(sizeof(robj*)*argc);\n    }\n\n    /* Create redis objects for all arguments. */\n    for (c->argc = 0, j = 0; j < argc; j++) {\n        c->argv[c->argc] = createObject(OBJ_STRING,argv[j]);\n        c->argc++;\n    }\n    zfree(argv);\n    return C_OK;\n}\n\n/* Helper function. Record protocol erro details in server log,\n * and set the client as CLIENT_CLOSE_AFTER_REPLY and\n * CLIENT_PROTOCOL_ERROR. */\n#define PROTO_DUMP_LEN 128\nstatic void setProtocolError(const char *errstr, client *c) {\n    if (server.verbosity <= LL_VERBOSE) {\n        sds client = catClientInfoString(sdsempty(),c);\n\n        /* Sample some protocol to given an idea about what was inside. */\n        char buf[256];\n        if (sdslen(c->querybuf)-c->qb_pos < PROTO_DUMP_LEN) {\n            snprintf(buf,sizeof(buf),\"Query buffer during protocol error: '%s'\", c->querybuf+c->qb_pos);\n        } else {\n            snprintf(buf,sizeof(buf),\"Query buffer during protocol error: '%.*s' (... more %zu bytes ...) '%.*s'\", PROTO_DUMP_LEN/2, c->querybuf+c->qb_pos, sdslen(c->querybuf)-c->qb_pos-PROTO_DUMP_LEN, PROTO_DUMP_LEN/2, c->querybuf+sdslen(c->querybuf)-PROTO_DUMP_LEN/2);\n        }\n\n        /* Remove non printable chars. */\n        char *p = buf;\n        while (*p != '\\0') {\n            if (!isprint(*p)) *p = '.';\n            p++;\n        }\n\n        /* Log all the client and protocol info. */\n        serverLog(LL_VERBOSE,\n            \"Protocol error (%s) from client: %s. %s\", errstr, client, buf);\n        sdsfree(client);\n    }\n    c->flags |= (CLIENT_CLOSE_AFTER_REPLY|CLIENT_PROTOCOL_ERROR);\n}\n\n/* Process the query buffer for client 'c', setting up the client argument\n * vector for command execution. Returns C_OK if after running the function\n * the client has a well-formed ready to be processed command, otherwise\n * C_ERR if there is still to read more buffer to get the full command.\n * The function also returns C_ERR when there is a protocol error: in such a\n * case the client structure is setup to reply with the error and close\n * the connection.\n *\n * This function is called if processInputBuffer() detects that the next\n * command is in RESP format, so the first byte in the command is found\n * to be '*'. Otherwise for inline commands processInlineBuffer() is called. */\nint processMultibulkBuffer(client *c) {\n    char *newline = NULL;\n    int ok;\n    long long ll;\n\n    if (c->multibulklen == 0) {\n        /* The client should have been reset */\n        serverAssertWithInfo(c,NULL,c->argc == 0);\n\n        /* Multi bulk length cannot be read without a \\r\\n */\n        newline = strchr(c->querybuf+c->qb_pos,'\\r');\n        if (newline == NULL) {\n            if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {\n                addReplyError(c,\"Protocol error: too big mbulk count string\");\n                setProtocolError(\"too big mbulk count string\",c);\n            }\n            return C_ERR;\n        }\n\n        /* Buffer should also contain \\n */\n        if (newline-(c->querybuf+c->qb_pos) > (ssize_t)(sdslen(c->querybuf)-c->qb_pos-2))\n            return C_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        serverAssertWithInfo(c,NULL,c->querybuf[c->qb_pos] == '*');\n        ok = string2ll(c->querybuf+1+c->qb_pos,newline-(c->querybuf+1+c->qb_pos),&ll);\n        if (!ok || ll > 1024*1024) {\n            addReplyError(c,\"Protocol error: invalid multibulk length\");\n            setProtocolError(\"invalid mbulk count\",c);\n            return C_ERR;\n        }\n\n        c->qb_pos = (newline-c->querybuf)+2;\n\n        if (ll <= 0) return C_OK;\n\n        c->multibulklen = ll;\n\n        /* Setup argv array on client structure */\n        if (c->argv) zfree(c->argv);\n        c->argv = zmalloc(sizeof(robj*)*c->multibulklen);\n    }\n\n    serverAssertWithInfo(c,NULL,c->multibulklen > 0);\n    while(c->multibulklen) {\n        /* Read bulk length if unknown */\n        if (c->bulklen == -1) {\n            newline = strchr(c->querybuf+c->qb_pos,'\\r');\n            if (newline == NULL) {\n                if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {\n                    addReplyError(c,\n                        \"Protocol error: too big bulk count string\");\n                    setProtocolError(\"too big bulk count string\",c);\n                    return C_ERR;\n                }\n                break;\n            }\n\n            /* Buffer should also contain \\n */\n            if (newline-(c->querybuf+c->qb_pos) > (ssize_t)(sdslen(c->querybuf)-c->qb_pos-2))\n                break;\n\n            if (c->querybuf[c->qb_pos] != '$') {\n                addReplyErrorFormat(c,\n                    \"Protocol error: expected '$', got '%c'\",\n                    c->querybuf[c->qb_pos]);\n                setProtocolError(\"expected $ but got something else\",c);\n                return C_ERR;\n            }\n\n            ok = string2ll(c->querybuf+c->qb_pos+1,newline-(c->querybuf+c->qb_pos+1),&ll);\n            if (!ok || ll < 0 || ll > server.proto_max_bulk_len) {\n                addReplyError(c,\"Protocol error: invalid bulk length\");\n                setProtocolError(\"invalid bulk length\",c);\n                return C_ERR;\n            }\n\n            c->qb_pos = newline-c->querybuf+2;\n            if (ll >= PROTO_MBULK_BIG_ARG) {\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                 *\n                 * But only when the data we have not parsed is less than\n                 * or equal to ll+2. If the data length is greater than\n                 * ll+2, trimming querybuf is just a waste of time, because\n                 * at this time the querybuf contains not only our bulk. */\n                if (sdslen(c->querybuf)-c->qb_pos <= (size_t)ll+2) {\n                    sdsrange(c->querybuf,c->qb_pos,-1);\n                    c->qb_pos = 0;\n                    /* Hint the sds library about the amount of bytes this string is\n                     * going to contain. */\n                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2);\n                }\n            }\n            c->bulklen = ll;\n        }\n\n        /* Read bulk argument */\n        if (sdslen(c->querybuf)-c->qb_pos < (size_t)(c->bulklen+2)) {\n            /* Not enough data (+2 == trailing \\r\\n) */\n            break;\n        } else {\n            /* Optimization: if the buffer contains JUST our bulk element\n             * instead of creating a new object by *copying* the sds we\n             * just use the current sds string. */\n            if (c->qb_pos == 0 &&\n                c->bulklen >= PROTO_MBULK_BIG_ARG &&\n                sdslen(c->querybuf) == (size_t)(c->bulklen+2))\n            {\n                c->argv[c->argc++] = createObject(OBJ_STRING,c->querybuf);\n                sdsIncrLen(c->querybuf,-2); /* remove CRLF */\n                /* Assume that if we saw a fat argument we'll see another one\n                 * likely... */\n                c->querybuf = sdsnewlen(SDS_NOINIT,c->bulklen+2);\n                sdsclear(c->querybuf);\n            } else {\n                c->argv[c->argc++] =\n                    createStringObject(c->querybuf+c->qb_pos,c->bulklen);\n                c->qb_pos += c->bulklen+2;\n            }\n            c->bulklen = -1;\n            c->multibulklen--;\n        }\n    }\n\n    /* We're done when c->multibulk == 0 */\n    if (c->multibulklen == 0) return C_OK;\n\n    /* Still not ready to process the command */\n    return C_ERR;\n}\n\n/* Perform necessary tasks after a command was executed:\n *\n * 1. The client is reset unless there are reasons to avoid doing it.\n * 2. In the case of master clients, the replication offset is updated.\n * 3. Propagate commands we got from our master to replicas down the line. */\nvoid commandProcessed(client *c) {\n    int cmd_is_ping = c->cmd && c->cmd->proc == pingCommand;\n    long long prev_offset = c->reploff;\n    if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {\n        /* Update the applied replication offset of our master. */\n        c->reploff = c->read_reploff - sdslen(c->querybuf) + c->qb_pos;\n    }\n\n    /* Don't reset the client structure for clients blocked in a\n     * module blocking command, so that the reply callback will\n     * still be able to access the client argv and argc field.\n     * The client will be reset in unblockClientFromModule(). */\n    if (!(c->flags & CLIENT_BLOCKED) ||\n        c->btype != BLOCKED_MODULE)\n    {\n        resetClient(c);\n    }\n\n    /* If the client is a master we need to compute the difference\n     * between the applied offset before and after processing the buffer,\n     * to understand how much of the replication stream was actually\n     * applied to the master state: this quantity, and its corresponding\n     * part of the replication stream, will be propagated to the\n     * sub-replicas and to the replication backlog. */\n    if (c->flags & CLIENT_MASTER) {\n        long long applied = c->reploff - prev_offset;\n        long long prev_master_repl_meaningful_offset = server.master_repl_meaningful_offset;\n        if (applied) {\n            replicationFeedSlavesFromMasterStream(server.slaves,\n                    c->pending_querybuf, applied);\n            sdsrange(c->pending_querybuf,applied,-1);\n        }\n        /* The server.master_repl_meaningful_offset variable represents\n         * the offset of the replication stream without the pending PINGs. */\n        if (cmd_is_ping)\n            server.master_repl_meaningful_offset = prev_master_repl_meaningful_offset;\n    }\n}\n\n/* This function calls processCommand(), but also performs a few sub tasks\n * for the client that are useful in that context:\n *\n * 1. It sets the current client to the client 'c'.\n * 2. calls commandProcessed() if the command was handled.\n *\n * The function returns C_ERR in case the client was freed as a side effect\n * of processing the command, otherwise C_OK is returned. */\nint processCommandAndResetClient(client *c) {\n    int deadclient = 0;\n    server.current_client = c;\n    if (processCommand(c) == C_OK) {\n        commandProcessed(c);\n    }\n    if (server.current_client == NULL) deadclient = 1;\n    server.current_client = NULL;\n    /* freeMemoryIfNeeded may flush slave output buffers. This may\n     * result into a slave, that may be the active client, to be\n     * freed. */\n    return deadclient ? C_ERR : C_OK;\n}\n\n/* This function is called every time, in the client structure 'c', there is\n * more query buffer to process, because we read more data from the socket\n * or because a client was blocked and later reactivated, so there could be\n * pending query buffer, already representing a full command, to process. */\nvoid processInputBuffer(client *c) {\n    /* Keep processing while there is something in the input buffer */\n    while(c->qb_pos < sdslen(c->querybuf)) {\n        /* Return if clients are paused. */\n        if (!(c->flags & CLIENT_SLAVE) && clientsArePaused()) break;\n\n        /* Immediately abort if the client is in the middle of something. */\n        if (c->flags & CLIENT_BLOCKED) break;\n\n        /* Don't process more buffers from clients that have already pending\n         * commands to execute in c->argv. */\n        if (c->flags & CLIENT_PENDING_COMMAND) break;\n\n        /* Don't process input from the master while there is a busy script\n         * condition on the slave. We want just to accumulate the replication\n         * stream (instead of replying -BUSY like we do with other clients) and\n         * later resume the processing. */\n        if (server.lua_timedout && c->flags & CLIENT_MASTER) break;\n\n        /* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is\n         * written to the client. Make sure to not let the reply grow after\n         * this flag has been set (i.e. don't process more commands).\n         *\n         * The same applies for clients we want to terminate ASAP. */\n        if (c->flags & (CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP)) break;\n\n        /* Determine request type when unknown. */\n        if (!c->reqtype) {\n            if (c->querybuf[c->qb_pos] == '*') {\n                c->reqtype = PROTO_REQ_MULTIBULK;\n            } else {\n                c->reqtype = PROTO_REQ_INLINE;\n            }\n        }\n\n        if (c->reqtype == PROTO_REQ_INLINE) {\n            if (processInlineBuffer(c) != C_OK) break;\n            /* If the Gopher mode and we got zero or one argument, process\n             * the request in Gopher mode. */\n            if (server.gopher_enabled &&\n                ((c->argc == 1 && ((char*)(c->argv[0]->ptr))[0] == '/') ||\n                  c->argc == 0))\n            {\n                processGopherRequest(c);\n                resetClient(c);\n                c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n                break;\n            }\n        } else if (c->reqtype == PROTO_REQ_MULTIBULK) {\n            if (processMultibulkBuffer(c) != C_OK) break;\n        } else {\n            serverPanic(\"Unknown request type\");\n        }\n\n        /* Multibulk processing could see a <= 0 length. */\n        if (c->argc == 0) {\n            resetClient(c);\n        } else {\n            /* If we are in the context of an I/O thread, we can't really\n             * execute the command here. All we can do is to flag the client\n             * as one that needs to process the command. */\n            if (c->flags & CLIENT_PENDING_READ) {\n                c->flags |= CLIENT_PENDING_COMMAND;\n                break;\n            }\n\n            /* We are finally ready to execute the command. */\n            if (processCommandAndResetClient(c) == C_ERR) {\n                /* If the client is no longer valid, we avoid exiting this\n                 * loop and trimming the client buffer later. So we return\n                 * ASAP in that case. */\n                return;\n            }\n        }\n    }\n\n    /* Trim to pos */\n    if (c->qb_pos) {\n        sdsrange(c->querybuf,c->qb_pos,-1);\n        c->qb_pos = 0;\n    }\n}\n\nvoid readQueryFromClient(connection *conn) {\n    client *c = connGetPrivateData(conn);\n    int nread, readlen;\n    size_t qblen;\n\n    /* Check if we want to read from the client later when exiting from\n     * the event loop. This is the case if threaded I/O is enabled. */\n    if (postponeClientRead(c)) return;\n\n    readlen = PROTO_IOBUF_LEN;\n    /* If this is a multi bulk request, and we are processing a bulk reply\n     * that is large enough, try to maximize the probability that the query\n     * buffer contains exactly the SDS string representing the object, even\n     * at the risk of requiring more read(2) calls. This way the function\n     * processMultiBulkBuffer() can avoid copying buffers to create the\n     * Redis Object representing the argument. */\n    if (c->reqtype == PROTO_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1\n        && c->bulklen >= PROTO_MBULK_BIG_ARG)\n    {\n        ssize_t remaining = (size_t)(c->bulklen+2)-sdslen(c->querybuf);\n\n        /* Note that the 'remaining' variable may be zero in some edge case,\n         * for example once we resume a blocked client after CLIENT PAUSE. */\n        if (remaining > 0 && remaining < readlen) readlen = remaining;\n    }\n\n    qblen = sdslen(c->querybuf);\n    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;\n    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);\n    nread = connRead(c->conn, c->querybuf+qblen, readlen);\n    if (nread == -1) {\n        if (connGetState(conn) == CONN_STATE_CONNECTED) {\n            return;\n        } else {\n            serverLog(LL_VERBOSE, \"Reading from client: %s\",connGetLastError(c->conn));\n            freeClientAsync(c);\n            return;\n        }\n    } else if (nread == 0) {\n        serverLog(LL_VERBOSE, \"Client closed connection\");\n        freeClientAsync(c);\n        return;\n    } else if (c->flags & CLIENT_MASTER) {\n        /* Append the query buffer to the pending (not applied) buffer\n         * of the master. We'll use this buffer later in order to have a\n         * copy of the string applied by the last command executed. */\n        c->pending_querybuf = sdscatlen(c->pending_querybuf,\n                                        c->querybuf+qblen,nread);\n    }\n\n    sdsIncrLen(c->querybuf,nread);\n    c->lastinteraction = server.unixtime;\n    if (c->flags & CLIENT_MASTER) c->read_reploff += nread;\n    server.stat_net_input_bytes += nread;\n    if (sdslen(c->querybuf) > server.client_max_querybuf_len) {\n        sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();\n\n        bytes = sdscatrepr(bytes,c->querybuf,64);\n        serverLog(LL_WARNING,\"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)\", ci, bytes);\n        sdsfree(ci);\n        sdsfree(bytes);\n        freeClientAsync(c);\n        return;\n    }\n\n    /* There is more data in the client input buffer, continue parsing it\n     * in case to check if there is a full command to execute. */\n     processInputBuffer(c);\n}\n\nvoid getClientsMaxBuffers(unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer) {\n    client *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/* A Redis \"Peer ID\" is a colon separated ip:port pair.\n * For IPv4 it's in the form x.y.z.k:port, example: \"127.0.0.1:1234\".\n * For IPv6 addresses we use [] around the IP part, like in \"[::1]:1234\".\n * For Unix sockets we use path:0, like in \"/tmp/redis:0\".\n *\n * A Peer ID always fits inside a buffer of NET_PEER_ID_LEN bytes, including\n * the null term.\n *\n * On failure the function still populates 'peerid' with the \"?:0\" string\n * in case you want to relax error checking or need to display something\n * anyway (see anetPeerToString implementation for more info). */\nvoid genClientPeerId(client *client, char *peerid,\n                            size_t peerid_len) {\n    if (client->flags & CLIENT_UNIX_SOCKET) {\n        /* Unix socket client. */\n        snprintf(peerid,peerid_len,\"%s:0\",server.unixsocket);\n    } else {\n        /* TCP client. */\n        connFormatPeer(client->conn,peerid,peerid_len);\n    }\n}\n\n/* This function returns the client peer id, by creating and caching it\n * if client->peerid is NULL, otherwise returning the cached value.\n * The Peer ID never changes during the life of the client, however it\n * is expensive to compute. */\nchar *getClientPeerId(client *c) {\n    char peerid[NET_PEER_ID_LEN];\n\n    if (c->peerid == NULL) {\n        genClientPeerId(c,peerid,sizeof(peerid));\n        c->peerid = sdsnew(peerid);\n    }\n    return c->peerid;\n}\n\n/* Concatenate a string representing the state of a client in an human\n * readable format, into the sds string 's'. */\nsds catClientInfoString(sds s, client *client) {\n    char flags[16], events[3], conninfo[CONN_INFO_LEN], *p;\n\n    p = flags;\n    if (client->flags & CLIENT_SLAVE) {\n        if (client->flags & CLIENT_MONITOR)\n            *p++ = 'O';\n        else\n            *p++ = 'S';\n    }\n    if (client->flags & CLIENT_MASTER) *p++ = 'M';\n    if (client->flags & CLIENT_PUBSUB) *p++ = 'P';\n    if (client->flags & CLIENT_MULTI) *p++ = 'x';\n    if (client->flags & CLIENT_BLOCKED) *p++ = 'b';\n    if (client->flags & CLIENT_TRACKING) *p++ = 't';\n    if (client->flags & CLIENT_TRACKING_BROKEN_REDIR) *p++ = 'R';\n    if (client->flags & CLIENT_DIRTY_CAS) *p++ = 'd';\n    if (client->flags & CLIENT_CLOSE_AFTER_REPLY) *p++ = 'c';\n    if (client->flags & CLIENT_UNBLOCKED) *p++ = 'u';\n    if (client->flags & CLIENT_CLOSE_ASAP) *p++ = 'A';\n    if (client->flags & CLIENT_UNIX_SOCKET) *p++ = 'U';\n    if (client->flags & CLIENT_READONLY) *p++ = 'r';\n    if (p == flags) *p++ = 'N';\n    *p++ = '\\0';\n\n    p = events;\n    if (client->conn) {\n        if (connHasReadHandler(client->conn)) *p++ = 'r';\n        if (connHasWriteHandler(client->conn)) *p++ = 'w';\n    }\n    *p = '\\0';\n    return sdscatfmt(s,\n        \"id=%U addr=%s %s 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 user=%s\",\n        (unsigned long long) client->id,\n        getClientPeerId(client),\n        connGetInfo(client->conn, conninfo, sizeof(conninfo)),\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 & CLIENT_MULTI) ? client->mstate.count : -1,\n        (unsigned long long) sdslen(client->querybuf),\n        (unsigned long long) sdsavail(client->querybuf),\n        (unsigned long long) client->bufpos,\n        (unsigned long long) listLength(client->reply),\n        (unsigned long long) getClientOutputBufferMemoryUsage(client),\n        events,\n        client->lastcmd ? client->lastcmd->name : \"NULL\",\n        client->user ? client->user->name : \"(superuser)\");\n}\n\nsds getAllClientsInfoString(int type) {\n    listNode *ln;\n    listIter li;\n    client *client;\n    sds o = sdsnewlen(SDS_NOINIT,200*listLength(server.clients));\n    sdsclear(o);\n    listRewind(server.clients,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        client = listNodeValue(ln);\n        if (type != -1 && getClientType(client) != type) continue;\n        o = catClientInfoString(o,client);\n        o = sdscatlen(o,\"\\n\",1);\n    }\n    return o;\n}\n\n/* This function implements CLIENT SETNAME, including replying to the\n * user with an error if the charset is wrong (in that case C_ERR is\n * returned). If the function succeeeded C_OK is returned, and it's up\n * to the caller to send a reply if needed.\n *\n * Setting an empty string as name has the effect of unsetting the\n * currently set name: the client will remain unnamed.\n *\n * This function is also used to implement the HELLO SETNAME option. */\nint clientSetNameOrReply(client *c, robj *name) {\n    int len = sdslen(name->ptr);\n    char *p = name->ptr;\n\n    /* Setting the client name to an empty string actually removes\n     * the current name. */\n    if (len == 0) {\n        if (c->name) decrRefCount(c->name);\n        c->name = NULL;\n        return C_OK;\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 (int 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 C_ERR;\n        }\n    }\n    if (c->name) decrRefCount(c->name);\n    c->name = name;\n    incrRefCount(name);\n    return C_OK;\n}\n\nvoid clientCommand(client *c) {\n    listNode *ln;\n    listIter li;\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ID                     -- Return the ID of the current connection.\",\n\"GETNAME                -- Return the name of the current connection.\",\n\"KILL <ip:port>         -- Kill connection made from <ip:port>.\",\n\"KILL <option> <value> [option value ...] -- Kill connections. Options are:\",\n\"     ADDR <ip:port>                      -- Kill connection made from <ip:port>\",\n\"     TYPE (normal|master|replica|pubsub) -- Kill connections by type.\",\n\"     USER <username>   -- Kill connections authenticated with such user.\",\n\"     SKIPME (yes|no)   -- Skip killing current connection (default: yes).\",\n\"LIST [options ...]     -- Return information about client connections. Options:\",\n\"     TYPE (normal|master|replica|pubsub) -- Return clients of specified type.\",\n\"PAUSE <timeout>        -- Suspend all Redis clients for <timout> milliseconds.\",\n\"REPLY (on|off|skip)    -- Control the replies sent to the current connection.\",\n\"SETNAME <name>         -- Assign the name <name> to the current connection.\",\n\"UNBLOCK <clientid> [TIMEOUT|ERROR] -- Unblock the specified blocked client.\",\n\"TRACKING (on|off) [REDIRECT <id>] [BCAST] [PREFIX first] [PREFIX second] [OPTIN] [OPTOUT]... -- Enable client keys tracking for client side caching.\",\n\"GETREDIR               -- Return the client ID we are redirecting to when tracking is enabled.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"id\") && c->argc == 2) {\n        /* CLIENT ID */\n        addReplyLongLong(c,c->id);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"list\")) {\n        /* CLIENT LIST */\n        int type = -1;\n        if (c->argc == 4 && !strcasecmp(c->argv[2]->ptr,\"type\")) {\n            type = getClientTypeByName(c->argv[3]->ptr);\n            if (type == -1) {\n                addReplyErrorFormat(c,\"Unknown client type '%s'\",\n                    (char*) c->argv[3]->ptr);\n                return;\n             }\n        } else if (c->argc != 2) {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n        sds o = getAllClientsInfoString(type);\n        addReplyVerbatim(c,o,sdslen(o),\"txt\");\n        sdsfree(o);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"reply\") && c->argc == 3) {\n        /* CLIENT REPLY ON|OFF|SKIP */\n        if (!strcasecmp(c->argv[2]->ptr,\"on\")) {\n            c->flags &= ~(CLIENT_REPLY_SKIP|CLIENT_REPLY_OFF);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"off\")) {\n            c->flags |= CLIENT_REPLY_OFF;\n        } else if (!strcasecmp(c->argv[2]->ptr,\"skip\")) {\n            if (!(c->flags & CLIENT_REPLY_OFF))\n                c->flags |= CLIENT_REPLY_SKIP_NEXT;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"kill\")) {\n        /* CLIENT KILL <ip:port>\n         * CLIENT KILL <option> [value] ... <option> [value] */\n        char *addr = NULL;\n        user *user = NULL;\n        int type = -1;\n        uint64_t id = 0;\n        int skipme = 1;\n        int killed = 0, close_this_client = 0;\n\n        if (c->argc == 3) {\n            /* Old style syntax: CLIENT KILL <addr> */\n            addr = c->argv[2]->ptr;\n            skipme = 0; /* With the old form, you can kill yourself. */\n        } else if (c->argc > 3) {\n            int i = 2; /* Next option index. */\n\n            /* New style syntax: parse options. */\n            while(i < c->argc) {\n                int moreargs = c->argc > i+1;\n\n                if (!strcasecmp(c->argv[i]->ptr,\"id\") && moreargs) {\n                    long long tmp;\n\n                    if (getLongLongFromObjectOrReply(c,c->argv[i+1],&tmp,NULL)\n                        != C_OK) return;\n                    id = tmp;\n                } else if (!strcasecmp(c->argv[i]->ptr,\"type\") && moreargs) {\n                    type = getClientTypeByName(c->argv[i+1]->ptr);\n                    if (type == -1) {\n                        addReplyErrorFormat(c,\"Unknown client type '%s'\",\n                            (char*) c->argv[i+1]->ptr);\n                        return;\n                    }\n                } else if (!strcasecmp(c->argv[i]->ptr,\"addr\") && moreargs) {\n                    addr = c->argv[i+1]->ptr;\n                } else if (!strcasecmp(c->argv[i]->ptr,\"user\") && moreargs) {\n                    user = ACLGetUserByName(c->argv[i+1]->ptr,\n                                            sdslen(c->argv[i+1]->ptr));\n                    if (user == NULL) {\n                        addReplyErrorFormat(c,\"No such user '%s'\",\n                            (char*) c->argv[i+1]->ptr);\n                        return;\n                    }\n                } else if (!strcasecmp(c->argv[i]->ptr,\"skipme\") && moreargs) {\n                    if (!strcasecmp(c->argv[i+1]->ptr,\"yes\")) {\n                        skipme = 1;\n                    } else if (!strcasecmp(c->argv[i+1]->ptr,\"no\")) {\n                        skipme = 0;\n                    } else {\n                        addReply(c,shared.syntaxerr);\n                        return;\n                    }\n                } else {\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                i += 2;\n            }\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n\n        /* Iterate clients killing all the matching clients. */\n        listRewind(server.clients,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            client *client = listNodeValue(ln);\n            if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;\n            if (type != -1 && getClientType(client) != type) continue;\n            if (id != 0 && client->id != id) continue;\n            if (user && client->user != user) continue;\n            if (c == client && skipme) continue;\n\n            /* Kill it. */\n            if (c == client) {\n                close_this_client = 1;\n            } else {\n                freeClient(client);\n            }\n            killed++;\n        }\n\n        /* Reply according to old/new format. */\n        if (c->argc == 3) {\n            if (killed == 0)\n                addReplyError(c,\"No such client\");\n            else\n                addReply(c,shared.ok);\n        } else {\n            addReplyLongLong(c,killed);\n        }\n\n        /* If this client has to be closed, flag it as CLOSE_AFTER_REPLY\n         * only after we queued the reply to its output buffers. */\n        if (close_this_client) c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"unblock\") && (c->argc == 3 ||\n                                                          c->argc == 4))\n    {\n        /* CLIENT UNBLOCK <id> [timeout|error] */\n        long long id;\n        int unblock_error = 0;\n\n        if (c->argc == 4) {\n            if (!strcasecmp(c->argv[3]->ptr,\"timeout\")) {\n                unblock_error = 0;\n            } else if (!strcasecmp(c->argv[3]->ptr,\"error\")) {\n                unblock_error = 1;\n            } else {\n                addReplyError(c,\n                    \"CLIENT UNBLOCK reason should be TIMEOUT or ERROR\");\n                return;\n            }\n        }\n        if (getLongLongFromObjectOrReply(c,c->argv[2],&id,NULL)\n            != C_OK) return;\n        struct client *target = lookupClientByID(id);\n        if (target && target->flags & CLIENT_BLOCKED) {\n            if (unblock_error)\n                addReplyError(target,\n                    \"-UNBLOCKED client unblocked via CLIENT UNBLOCK\");\n            else\n                replyToBlockedClientTimedOut(target);\n            unblockClient(target);\n            addReply(c,shared.cone);\n        } else {\n            addReply(c,shared.czero);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"setname\") && c->argc == 3) {\n        /* CLIENT SETNAME */\n        if (clientSetNameOrReply(c,c->argv[2]) == C_OK)\n            addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getname\") && c->argc == 2) {\n        /* CLIENT GETNAME */\n        if (c->name)\n            addReplyBulk(c,c->name);\n        else\n            addReplyNull(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"pause\") && c->argc == 3) {\n        /* CLIENT PAUSE */\n        long long duration;\n\n        if (getTimeoutFromObjectOrReply(c,c->argv[2],&duration,\n                UNIT_MILLISECONDS) != C_OK) return;\n        pauseClients(duration);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"tracking\") && c->argc >= 3) {\n        /* CLIENT TRACKING (on|off) [REDIRECT <id>] [BCAST] [PREFIX first]\n         *                          [PREFIX second] [OPTIN] [OPTOUT] ... */\n        long long redir = 0;\n        uint64_t options = 0;\n        robj **prefix = NULL;\n        size_t numprefix = 0;\n\n        /* Parse the options. */\n        for (int j = 3; j < c->argc; j++) {\n            int moreargs = (c->argc-1) - j;\n\n            if (!strcasecmp(c->argv[j]->ptr,\"redirect\") && moreargs) {\n                j++;\n                if (redir != 0) {\n                    addReplyError(c,\"A client can only redirect to a single \"\n                                    \"other client\");\n                    zfree(prefix);\n                    return;\n                }\n\n                if (getLongLongFromObjectOrReply(c,c->argv[j],&redir,NULL) !=\n                    C_OK)\n                {\n                    zfree(prefix);\n                    return;\n                }\n                /* We will require the client with the specified ID to exist\n                 * right now, even if it is possible that it gets disconnected\n                 * later. Still a valid sanity check. */\n                if (lookupClientByID(redir) == NULL) {\n                    addReplyError(c,\"The client ID you want redirect to \"\n                                    \"does not exist\");\n                    zfree(prefix);\n                    return;\n                }\n            } else if (!strcasecmp(c->argv[j]->ptr,\"bcast\")) {\n                options |= CLIENT_TRACKING_BCAST;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"optin\")) {\n                options |= CLIENT_TRACKING_OPTIN;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"optout\")) {\n                options |= CLIENT_TRACKING_OPTOUT;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"noloop\")) {\n                options |= CLIENT_TRACKING_NOLOOP;\n            } else if (!strcasecmp(c->argv[j]->ptr,\"prefix\") && moreargs) {\n                j++;\n                prefix = zrealloc(prefix,sizeof(robj*)*(numprefix+1));\n                prefix[numprefix++] = c->argv[j];\n            } else {\n                zfree(prefix);\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n\n        /* Options are ok: enable or disable the tracking for this client. */\n        if (!strcasecmp(c->argv[2]->ptr,\"on\")) {\n            /* Before enabling tracking, make sure options are compatible\n             * among each other and with the current state of the client. */\n            if (!(options & CLIENT_TRACKING_BCAST) && numprefix) {\n                addReplyError(c,\n                    \"PREFIX option requires BCAST mode to be enabled\");\n                zfree(prefix);\n                return;\n            }\n\n            if (c->flags & CLIENT_TRACKING) {\n                int oldbcast = !!(c->flags & CLIENT_TRACKING_BCAST);\n                int newbcast = !!(options & CLIENT_TRACKING_BCAST);\n                if (oldbcast != newbcast) {\n                    addReplyError(c,\n                    \"You can't switch BCAST mode on/off before disabling \"\n                    \"tracking for this client, and then re-enabling it with \"\n                    \"a different mode.\");\n                    zfree(prefix);\n                    return;\n                }\n            }\n\n            if (options & CLIENT_TRACKING_BCAST &&\n                options & (CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT))\n            {\n                addReplyError(c,\n                \"OPTIN and OPTOUT are not compatible with BCAST\");\n                zfree(prefix);\n                return;\n            }\n\n            if (options & CLIENT_TRACKING_OPTIN && options & CLIENT_TRACKING_OPTOUT)\n            {\n                addReplyError(c,\n                \"You can't specify both OPTIN mode and OPTOUT mode\");\n                zfree(prefix);\n                return;\n            }\n\n            if ((options & CLIENT_TRACKING_OPTIN && c->flags & CLIENT_TRACKING_OPTOUT) ||\n                (options & CLIENT_TRACKING_OPTOUT && c->flags & CLIENT_TRACKING_OPTIN))\n            {\n                addReplyError(c,\n                \"You can't switch OPTIN/OPTOUT mode before disabling \"\n                \"tracking for this client, and then re-enabling it with \"\n                \"a different mode.\");\n                zfree(prefix);\n                return;\n            }\n\n            enableTracking(c,redir,options,prefix,numprefix);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"off\")) {\n            disableTracking(c);\n        } else {\n            zfree(prefix);\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n        zfree(prefix);\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"caching\") && c->argc >= 3) {\n        if (!(c->flags & CLIENT_TRACKING)) {\n            addReplyError(c,\"CLIENT CACHING can be called only when the \"\n                            \"client is in tracking mode with OPTIN or \"\n                            \"OPTOUT mode enabled\");\n            return;\n        }\n\n        char *opt = c->argv[2]->ptr;\n        if (!strcasecmp(opt,\"yes\")) {\n            if (c->flags & CLIENT_TRACKING_OPTIN) {\n                c->flags |= CLIENT_TRACKING_CACHING;\n            } else {\n                addReplyError(c,\"CLIENT CACHING YES is only valid when tracking is enabled in OPTIN mode.\");\n                return;\n            }\n        } else if (!strcasecmp(opt,\"no\")) {\n            if (c->flags & CLIENT_TRACKING_OPTOUT) {\n                c->flags |= CLIENT_TRACKING_CACHING;\n            } else {\n                addReplyError(c,\"CLIENT CACHING NO is only valid when tracking is enabled in OPTOUT mode.\");\n                return;\n            }\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n\n        /* Common reply for when we succeeded. */\n        addReply(c,shared.ok);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getredir\") && c->argc == 2) {\n        /* CLIENT GETREDIR */\n        if (c->flags & CLIENT_TRACKING) {\n            addReplyLongLong(c,c->client_tracking_redirection);\n        } else {\n            addReplyLongLong(c,-1);\n        }\n    } else {\n        addReplyErrorFormat(c, \"Unknown subcommand or wrong number of arguments for '%s'. Try CLIENT HELP\", (char*)c->argv[1]->ptr);\n    }\n}\n\n/* HELLO <protocol-version> [AUTH <user> <password>] [SETNAME <name>] */\nvoid helloCommand(client *c) {\n    long long ver;\n\n    if (getLongLongFromObject(c->argv[1],&ver) != C_OK ||\n        ver < 2 || ver > 3)\n    {\n        addReplyError(c,\"-NOPROTO unsupported protocol version\");\n        return;\n    }\n\n    for (int j = 2; j < c->argc; j++) {\n        int moreargs = (c->argc-1) - j;\n        const char *opt = c->argv[j]->ptr;\n        if (!strcasecmp(opt,\"AUTH\") && moreargs >= 2) {\n            if (ACLAuthenticateUser(c, c->argv[j+1], c->argv[j+2]) == C_ERR) {\n                addReplyError(c,\"-WRONGPASS invalid username-password pair\");\n                return;\n            }\n            j += 2;\n        } else if (!strcasecmp(opt,\"SETNAME\") && moreargs) {\n            if (clientSetNameOrReply(c, c->argv[j+1]) == C_ERR) return;\n            j++;\n        } else {\n            addReplyErrorFormat(c,\"Syntax error in HELLO option '%s'\",opt);\n            return;\n        }\n    }\n\n    /* At this point we need to be authenticated to continue. */\n    if (!c->authenticated) {\n        addReplyError(c,\"-NOAUTH HELLO must be called with the client already \"\n                        \"authenticated, otherwise the HELLO AUTH <user> <pass> \"\n                        \"option can be used to authenticate the client and \"\n                        \"select the RESP protocol version at the same time\");\n        return;\n    }\n\n    /* Let's switch to the specified RESP mode. */\n    c->resp = ver;\n    addReplyMapLen(c,6 + !server.sentinel_mode);\n\n    addReplyBulkCString(c,\"server\");\n    addReplyBulkCString(c,\"redis\");\n\n    addReplyBulkCString(c,\"version\");\n    addReplyBulkCString(c,REDIS_VERSION);\n\n    addReplyBulkCString(c,\"proto\");\n    addReplyLongLong(c,3);\n\n    addReplyBulkCString(c,\"id\");\n    addReplyLongLong(c,c->id);\n\n    addReplyBulkCString(c,\"mode\");\n    if (server.sentinel_mode) addReplyBulkCString(c,\"sentinel\");\n    else if (server.cluster_enabled) addReplyBulkCString(c,\"cluster\");\n    else addReplyBulkCString(c,\"standalone\");\n\n    if (!server.sentinel_mode) {\n        addReplyBulkCString(c,\"role\");\n        addReplyBulkCString(c,server.masterhost ? \"replica\" : \"master\");\n    }\n\n    addReplyBulkCString(c,\"modules\");\n    addReplyLoadedModules(c);\n}\n\n/* This callback is bound to POST and \"Host:\" command names. Those are not\n * really commands, but are used in security attacks in order to talk to\n * Redis instances via HTTP, with a technique called \"cross protocol scripting\"\n * which exploits the fact that services like Redis will discard invalid\n * HTTP headers and will process what follows.\n *\n * As a protection against this attack, Redis will terminate the connection\n * when a POST or \"Host:\" header is seen, and will log the event from\n * time to time (to avoid creating a DOS as a result of too many logs). */\nvoid securityWarningCommand(client *c) {\n    static time_t logged_time;\n    time_t now = time(NULL);\n\n    if (labs(now-logged_time) > 60) {\n        serverLog(LL_WARNING,\"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.\");\n        logged_time = now;\n    }\n    freeClientAsync(c);\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. */\nvoid rewriteClientCommandVector(client *c, int argc, ...) {\n    va_list ap;\n    int j;\n    robj **argv; /* The new argument vector */\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    for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]);\n    zfree(c->argv);\n    /* Replace argv and argc with our new versions. */\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    serverAssertWithInfo(c,NULL,c->cmd != NULL);\n    va_end(ap);\n}\n\n/* Completely replace the client command vector with the provided one. */\nvoid replaceClientCommandVector(client *c, int argc, robj **argv) {\n    freeClientArgv(c);\n    zfree(c->argv);\n    c->argv = argv;\n    c->argc = argc;\n    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);\n    serverAssertWithInfo(c,NULL,c->cmd != NULL);\n}\n\n/* Rewrite a single item in the command vector.\n * The new val ref count is incremented, and the old decremented.\n *\n * It is possible to specify an argument over the current size of the\n * argument vector: in this case the array of objects gets reallocated\n * and c->argc set to the max value. However it's up to the caller to\n *\n * 1. Make sure there are no \"holes\" and all the arguments are set.\n * 2. If the original argument vector was longer than the one we\n *    want to end with, it's up to the caller to set c->argc and\n *    free the no longer used objects on c->argv. */\nvoid rewriteClientCommandArgument(client *c, int i, robj *newval) {\n    robj *oldval;\n\n    if (i >= c->argc) {\n        c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1));\n        c->argc = i+1;\n        c->argv[i] = NULL;\n    }\n    oldval = c->argv[i];\n    c->argv[i] = newval;\n    incrRefCount(newval);\n    if (oldval) 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        serverAssertWithInfo(c,NULL,c->cmd != NULL);\n    }\n}\n\n/* This function returns the number of bytes that Redis is\n * using to store the reply still not read by the client.\n *\n * Note: this function is very fast so can be called as many time as\n * the caller wishes. The main usage of this function currently is\n * enforcing the client output length limits. */\nunsigned long getClientOutputBufferMemoryUsage(client *c) {\n    unsigned long list_item_size = sizeof(listNode) + sizeof(clientReplyBlock);\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 * The function will return one of the following:\n * CLIENT_TYPE_NORMAL -> Normal client\n * CLIENT_TYPE_SLAVE  -> Slave\n * CLIENT_TYPE_PUBSUB -> Client subscribed to Pub/Sub channels\n * CLIENT_TYPE_MASTER -> The client representing our replication master.\n */\nint getClientType(client *c) {\n    if (c->flags & CLIENT_MASTER) return CLIENT_TYPE_MASTER;\n    /* Even though MONITOR clients are marked as replicas, we\n     * want the expose them as normal clients. */\n    if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR))\n        return CLIENT_TYPE_SLAVE;\n    if (c->flags & CLIENT_PUBSUB) return CLIENT_TYPE_PUBSUB;\n    return CLIENT_TYPE_NORMAL;\n}\n\nint getClientTypeByName(char *name) {\n    if (!strcasecmp(name,\"normal\")) return CLIENT_TYPE_NORMAL;\n    else if (!strcasecmp(name,\"slave\")) return CLIENT_TYPE_SLAVE;\n    else if (!strcasecmp(name,\"replica\")) return CLIENT_TYPE_SLAVE;\n    else if (!strcasecmp(name,\"pubsub\")) return CLIENT_TYPE_PUBSUB;\n    else if (!strcasecmp(name,\"master\")) return CLIENT_TYPE_MASTER;\n    else return -1;\n}\n\nchar *getClientTypeName(int class) {\n    switch(class) {\n    case CLIENT_TYPE_NORMAL: return \"normal\";\n    case CLIENT_TYPE_SLAVE:  return \"slave\";\n    case CLIENT_TYPE_PUBSUB: return \"pubsub\";\n    case CLIENT_TYPE_MASTER: return \"master\";\n    default:                       return NULL;\n    }\n}\n\n/* The function checks if the client reached output buffer soft or hard\n * limit, and also update the state needed to check the soft limit as\n * a side effect.\n *\n * Return value: non-zero if the client reached the soft or the hard limit.\n *               Otherwise zero is returned. */\nint checkClientOutputBufferLimits(client *c) {\n    int soft = 0, hard = 0, class;\n    unsigned long used_mem = getClientOutputBufferMemoryUsage(c);\n\n    class = getClientType(c);\n    /* For the purpose of output buffer limiting, masters are handled\n     * like normal clients. */\n    if (class == CLIENT_TYPE_MASTER) class = CLIENT_TYPE_NORMAL;\n\n    if (server.client_obuf_limits[class].hard_limit_bytes &&\n        used_mem >= server.client_obuf_limits[class].hard_limit_bytes)\n        hard = 1;\n    if (server.client_obuf_limits[class].soft_limit_bytes &&\n        used_mem >= server.client_obuf_limits[class].soft_limit_bytes)\n        soft = 1;\n\n    /* We need to check if the soft limit is reached continuously for the\n     * specified amount of seconds. */\n    if (soft) {\n        if (c->obuf_soft_limit_reached_time == 0) {\n            c->obuf_soft_limit_reached_time = server.unixtime;\n            soft = 0; /* First time we see the soft limit reached */\n        } else {\n            time_t elapsed = server.unixtime - c->obuf_soft_limit_reached_time;\n\n            if (elapsed <=\n                server.client_obuf_limits[class].soft_limit_seconds) {\n                soft = 0; /* The client still did not reached the max number of\n                             seconds for the soft limit to be considered\n                             reached. */\n            }\n        }\n    } else {\n        c->obuf_soft_limit_reached_time = 0;\n    }\n    return soft || hard;\n}\n\n/* Asynchronously close a client if soft or hard limit is reached on the\n * output buffer size. The caller can check if the client will be closed\n * checking if the client CLIENT_CLOSE_ASAP flag is set.\n *\n * Note: we need to close the client asynchronously because this function is\n * called from contexts where the client can't be freed safely, i.e. from the\n * lower level functions pushing data inside the client output buffers. */\nvoid asyncCloseClientOnOutputBufferLimitReached(client *c) {\n    if (!c->conn) return; /* It is unsafe to free fake clients. */\n    serverAssert(c->reply_bytes < SIZE_MAX-(1024*64));\n    if (c->reply_bytes == 0 || c->flags & CLIENT_CLOSE_ASAP) return;\n    if (checkClientOutputBufferLimits(c)) {\n        sds client = catClientInfoString(sdsempty(),c);\n\n        freeClientAsync(c);\n        serverLog(LL_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 * This is also called by SHUTDOWN for a best-effort attempt to send\n * slaves the latest writes. */\nvoid flushSlavesOutputBuffers(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = listNodeValue(ln);\n        int can_receive_writes = connHasWriteHandler(slave->conn) ||\n                                 (slave->flags & CLIENT_PENDING_WRITE);\n\n        /* We don't want to send the pending data to the replica in a few\n         * cases:\n         *\n         * 1. For some reason there is neither the write handler installed\n         *    nor the client is flagged as to have pending writes: for some\n         *    reason this replica may not be set to receive data. This is\n         *    just for the sake of defensive programming.\n         *\n         * 2. The put_online_on_ack flag is true. To know why we don't want\n         *    to send data to the replica in this case, please grep for the\n         *    flag for this flag.\n         *\n         * 3. Obviously if the slave is not ONLINE.\n         */\n        if (slave->replstate == SLAVE_STATE_ONLINE &&\n            can_receive_writes &&\n            !slave->repl_put_online_on_ack &&\n            clientHasPendingReplies(slave))\n        {\n            writeToClient(slave,0);\n        }\n    }\n}\n\n/* Pause clients up to the specified unixtime (in ms). While clients\n * are paused no command is processed from clients, so the data set can't\n * change during that time.\n *\n * However while this function pauses normal and Pub/Sub clients, slaves are\n * still served, so this function can be used on server upgrades where it is\n * required that slaves process the latest bytes from the replication stream\n * before being turned to masters.\n *\n * This function is also internally used by Redis Cluster for the manual\n * failover procedure implemented by CLUSTER FAILOVER.\n *\n * The function always succeed, even if there is already a pause in progress.\n * In such a case, the pause is extended if the duration is more than the\n * time left for the previous duration. However if the duration is smaller\n * than the time left for the previous pause, no change is made to the\n * left duration. */\nvoid pauseClients(mstime_t end) {\n    if (!server.clients_paused || end > server.clients_pause_end_time)\n        server.clients_pause_end_time = end;\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. */\nint clientsArePaused(void) {\n    if (server.clients_paused &&\n        server.clients_pause_end_time < server.mstime)\n    {\n        listNode *ln;\n        listIter li;\n        client *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            /* Don't touch slaves and blocked clients.\n             * The latter pending requests will be processed when unblocked. */\n            if (c->flags & (CLIENT_SLAVE|CLIENT_BLOCKED)) continue;\n            queueClientForReprocessing(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 4 times as long as we receive acknowledge that\n * some event was processed, in order to go forward with the accept, read,\n * write, close sequence needed to serve a client.\n *\n * The function returns the total number of events processed. */\nvoid processEventsWhileBlocked(void) {\n    int iterations = 4; /* See the function top-comment. */\n\n    /* Note: when we are processing events while blocked (for instance during\n     * busy Lua scripts), we set a global flag. When such flag is set, we\n     * avoid handling the read part of clients using threaded I/O.\n     * See https://github.com/antirez/redis/issues/6988 for more info. */\n    ProcessingEventsWhileBlocked = 1;\n    while (iterations--) {\n        long long startval = server.events_processed_while_blocked;\n        long long ae_events = aeProcessEvents(server.el,\n            AE_FILE_EVENTS|AE_DONT_WAIT|\n            AE_CALL_BEFORE_SLEEP|AE_CALL_AFTER_SLEEP);\n        /* Note that server.events_processed_while_blocked will also get\n         * incremeted by callbacks called by the event loop handlers. */\n        server.events_processed_while_blocked += ae_events;\n        long long events = server.events_processed_while_blocked - startval;\n        if (!events) break;\n    }\n    ProcessingEventsWhileBlocked = 0;\n}\n\n/* ==========================================================================\n * Threaded I/O\n * ========================================================================== */\n\nint tio_debug = 0;\n\n#define IO_THREADS_MAX_NUM 128\n#define IO_THREADS_OP_READ 0\n#define IO_THREADS_OP_WRITE 1\n\npthread_t io_threads[IO_THREADS_MAX_NUM];\npthread_mutex_t io_threads_mutex[IO_THREADS_MAX_NUM];\n_Atomic unsigned long io_threads_pending[IO_THREADS_MAX_NUM];\nint io_threads_active;  /* Are the threads currently spinning waiting I/O? */\nint io_threads_op;      /* IO_THREADS_OP_WRITE or IO_THREADS_OP_READ. */\n\n/* This is the list of clients each thread will serve when threaded I/O is\n * used. We spawn io_threads_num-1 threads, since one is the main thread\n * itself. */\nlist *io_threads_list[IO_THREADS_MAX_NUM];\n\nvoid *IOThreadMain(void *myid) {\n    /* The ID is the thread number (from 0 to server.iothreads_num-1), and is\n     * used by the thread to just manipulate a single sub-array of clients. */\n    long id = (unsigned long)myid;\n    char thdname[16];\n\n    snprintf(thdname, sizeof(thdname), \"io_thd_%ld\", id);\n    redis_set_thread_title(thdname);\n    redisSetCpuAffinity(server.server_cpulist);\n\n    while(1) {\n        /* Wait for start */\n        for (int j = 0; j < 1000000; j++) {\n            if (io_threads_pending[id] != 0) break;\n        }\n\n        /* Give the main thread a chance to stop this thread. */\n        if (io_threads_pending[id] == 0) {\n            pthread_mutex_lock(&io_threads_mutex[id]);\n            pthread_mutex_unlock(&io_threads_mutex[id]);\n            continue;\n        }\n\n        serverAssert(io_threads_pending[id] != 0);\n\n        if (tio_debug) printf(\"[%ld] %d to handle\\n\", id, (int)listLength(io_threads_list[id]));\n\n        /* Process: note that the main thread will never touch our list\n         * before we drop the pending count to 0. */\n        listIter li;\n        listNode *ln;\n        listRewind(io_threads_list[id],&li);\n        while((ln = listNext(&li))) {\n            client *c = listNodeValue(ln);\n            if (io_threads_op == IO_THREADS_OP_WRITE) {\n                writeToClient(c,0);\n            } else if (io_threads_op == IO_THREADS_OP_READ) {\n                readQueryFromClient(c->conn);\n            } else {\n                serverPanic(\"io_threads_op value is unknown\");\n            }\n        }\n        listEmpty(io_threads_list[id]);\n        io_threads_pending[id] = 0;\n\n        if (tio_debug) printf(\"[%ld] Done\\n\", id);\n    }\n}\n\n/* Initialize the data structures needed for threaded I/O. */\nvoid initThreadedIO(void) {\n    io_threads_active = 0; /* We start with threads not active. */\n\n    /* Don't spawn any thread if the user selected a single thread:\n     * we'll handle I/O directly from the main thread. */\n    if (server.io_threads_num == 1) return;\n\n    if (server.io_threads_num > IO_THREADS_MAX_NUM) {\n        serverLog(LL_WARNING,\"Fatal: too many I/O threads configured. \"\n                             \"The maximum number is %d.\", IO_THREADS_MAX_NUM);\n        exit(1);\n    }\n\n    /* Spawn and initialize the I/O threads. */\n    for (int i = 0; i < server.io_threads_num; i++) {\n        /* Things we do for all the threads including the main thread. */\n        io_threads_list[i] = listCreate();\n        if (i == 0) continue; /* Thread 0 is the main thread. */\n\n        /* Things we do only for the additional threads. */\n        pthread_t tid;\n        pthread_mutex_init(&io_threads_mutex[i],NULL);\n        io_threads_pending[i] = 0;\n        pthread_mutex_lock(&io_threads_mutex[i]); /* Thread will be stopped. */\n        if (pthread_create(&tid,NULL,IOThreadMain,(void*)(long)i) != 0) {\n            serverLog(LL_WARNING,\"Fatal: Can't initialize IO thread.\");\n            exit(1);\n        }\n        io_threads[i] = tid;\n    }\n}\n\nvoid startThreadedIO(void) {\n    if (tio_debug) { printf(\"S\"); fflush(stdout); }\n    if (tio_debug) printf(\"--- STARTING THREADED IO ---\\n\");\n    serverAssert(io_threads_active == 0);\n    for (int j = 1; j < server.io_threads_num; j++)\n        pthread_mutex_unlock(&io_threads_mutex[j]);\n    io_threads_active = 1;\n}\n\nvoid stopThreadedIO(void) {\n    /* We may have still clients with pending reads when this function\n     * is called: handle them before stopping the threads. */\n    handleClientsWithPendingReadsUsingThreads();\n    if (tio_debug) { printf(\"E\"); fflush(stdout); }\n    if (tio_debug) printf(\"--- STOPPING THREADED IO [R%d] [W%d] ---\\n\",\n        (int) listLength(server.clients_pending_read),\n        (int) listLength(server.clients_pending_write));\n    serverAssert(io_threads_active == 1);\n    for (int j = 1; j < server.io_threads_num; j++)\n        pthread_mutex_lock(&io_threads_mutex[j]);\n    io_threads_active = 0;\n}\n\n/* This function checks if there are not enough pending clients to justify\n * taking the I/O threads active: in that case I/O threads are stopped if\n * currently active. We track the pending writes as a measure of clients\n * we need to handle in parallel, however the I/O threading is disabled\n * globally for reads as well if we have too little pending clients.\n *\n * The function returns 0 if the I/O threading should be used becuase there\n * are enough active threads, otherwise 1 is returned and the I/O threads\n * could be possibly stopped (if already active) as a side effect. */\nint stopThreadedIOIfNeeded(void) {\n    int pending = listLength(server.clients_pending_write);\n\n    /* Return ASAP if IO threads are disabled (single threaded mode). */\n    if (server.io_threads_num == 1) return 1;\n\n    if (pending < (server.io_threads_num*2)) {\n        if (io_threads_active) stopThreadedIO();\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nint handleClientsWithPendingWritesUsingThreads(void) {\n    int processed = listLength(server.clients_pending_write);\n    if (processed == 0) return 0; /* Return ASAP if there are no clients. */\n\n    /* If I/O threads are disabled or we have few clients to serve, don't\n     * use I/O threads, but thejboring synchronous code. */\n    if (server.io_threads_num == 1 || stopThreadedIOIfNeeded()) {\n        return handleClientsWithPendingWrites();\n    }\n\n    /* Start threads if needed. */\n    if (!io_threads_active) startThreadedIO();\n\n    if (tio_debug) printf(\"%d TOTAL WRITE pending clients\\n\", processed);\n\n    /* Distribute the clients across N different lists. */\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients_pending_write,&li);\n    int item_id = 0;\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_WRITE;\n        int target_id = item_id % server.io_threads_num;\n        listAddNodeTail(io_threads_list[target_id],c);\n        item_id++;\n    }\n\n    /* Give the start condition to the waiting threads, by setting the\n     * start condition atomic var. */\n    io_threads_op = IO_THREADS_OP_WRITE;\n    for (int j = 1; j < server.io_threads_num; j++) {\n        int count = listLength(io_threads_list[j]);\n        io_threads_pending[j] = count;\n    }\n\n    /* Also use the main thread to process a slice of clients. */\n    listRewind(io_threads_list[0],&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        writeToClient(c,0);\n    }\n    listEmpty(io_threads_list[0]);\n\n    /* Wait for all the other threads to end their work. */\n    while(1) {\n        unsigned long pending = 0;\n        for (int j = 1; j < server.io_threads_num; j++)\n            pending += io_threads_pending[j];\n        if (pending == 0) break;\n    }\n    if (tio_debug) printf(\"I/O WRITE All threads finshed\\n\");\n\n    /* Run the list of clients again to install the write handler where\n     * needed. */\n    listRewind(server.clients_pending_write,&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n\n        /* Install the write handler if there are pending writes in some\n         * of the clients. */\n        if (clientHasPendingReplies(c) &&\n                connSetWriteHandler(c->conn, sendReplyToClient) == AE_ERR)\n        {\n            freeClientAsync(c);\n        }\n    }\n    listEmpty(server.clients_pending_write);\n    return processed;\n}\n\n/* Return 1 if we want to handle the client read later using threaded I/O.\n * This is called by the readable handler of the event loop.\n * As a side effect of calling this function the client is put in the\n * pending read clients and flagged as such. */\nint postponeClientRead(client *c) {\n    if (io_threads_active &&\n        server.io_threads_do_reads &&\n        !ProcessingEventsWhileBlocked &&\n        !(c->flags & (CLIENT_MASTER|CLIENT_SLAVE|CLIENT_PENDING_READ)))\n    {\n        c->flags |= CLIENT_PENDING_READ;\n        listAddNodeHead(server.clients_pending_read,c);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* When threaded I/O is also enabled for the reading + parsing side, the\n * readable handler will just put normal clients into a queue of clients to\n * process (instead of serving them synchronously). This function runs\n * the queue using the I/O threads, and process them in order to accumulate\n * the reads in the buffers, and also parse the first command available\n * rendering it in the client structures. */\nint handleClientsWithPendingReadsUsingThreads(void) {\n    if (!io_threads_active || !server.io_threads_do_reads) return 0;\n    int processed = listLength(server.clients_pending_read);\n    if (processed == 0) return 0;\n\n    if (tio_debug) printf(\"%d TOTAL READ pending clients\\n\", processed);\n\n    /* Distribute the clients across N different lists. */\n    listIter li;\n    listNode *ln;\n    listRewind(server.clients_pending_read,&li);\n    int item_id = 0;\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        int target_id = item_id % server.io_threads_num;\n        listAddNodeTail(io_threads_list[target_id],c);\n        item_id++;\n    }\n\n    /* Give the start condition to the waiting threads, by setting the\n     * start condition atomic var. */\n    io_threads_op = IO_THREADS_OP_READ;\n    for (int j = 1; j < server.io_threads_num; j++) {\n        int count = listLength(io_threads_list[j]);\n        io_threads_pending[j] = count;\n    }\n\n    /* Also use the main thread to process a slice of clients. */\n    listRewind(io_threads_list[0],&li);\n    while((ln = listNext(&li))) {\n        client *c = listNodeValue(ln);\n        readQueryFromClient(c->conn);\n    }\n    listEmpty(io_threads_list[0]);\n\n    /* Wait for all the other threads to end their work. */\n    while(1) {\n        unsigned long pending = 0;\n        for (int j = 1; j < server.io_threads_num; j++)\n            pending += io_threads_pending[j];\n        if (pending == 0) break;\n    }\n    if (tio_debug) printf(\"I/O READ All threads finshed\\n\");\n\n    /* Run the list of clients again to process the new buffers. */\n    while(listLength(server.clients_pending_read)) {\n        ln = listFirst(server.clients_pending_read);\n        client *c = listNodeValue(ln);\n        c->flags &= ~CLIENT_PENDING_READ;\n        listDelNode(server.clients_pending_read,ln);\n\n        if (c->flags & CLIENT_PENDING_COMMAND) {\n            c->flags &= ~CLIENT_PENDING_COMMAND;\n            if (processCommandAndResetClient(c) == C_ERR) {\n                /* If the client is no longer valid, we avoid\n                 * processing the client later. So we just go\n                 * to the next. */\n                continue;\n            }\n        }\n        processInputBuffer(c);\n    }\n    return processed;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n\n/* This file implements keyspace events notification via Pub/Sub and\n * described at https://redis.io/topics/notifications. */\n\n/* Turn a string representing notification classes into an integer\n * representing notification classes flags xored.\n *\n * The function returns -1 if the input contains characters not mapping to\n * any class. */\nint keyspaceEventsStringToFlags(char *classes) {\n    char *p = classes;\n    int c, flags = 0;\n\n    while((c = *p++) != '\\0') {\n        switch(c) {\n        case 'A': flags |= NOTIFY_ALL; break;\n        case 'g': flags |= NOTIFY_GENERIC; break;\n        case '$': flags |= NOTIFY_STRING; break;\n        case 'l': flags |= NOTIFY_LIST; break;\n        case 's': flags |= NOTIFY_SET; break;\n        case 'h': flags |= NOTIFY_HASH; break;\n        case 'z': flags |= NOTIFY_ZSET; break;\n        case 'x': flags |= NOTIFY_EXPIRED; break;\n        case 'e': flags |= NOTIFY_EVICTED; break;\n        case 'K': flags |= NOTIFY_KEYSPACE; break;\n        case 'E': flags |= NOTIFY_KEYEVENT; break;\n        case 't': flags |= NOTIFY_STREAM; break;\n        case 'm': flags |= NOTIFY_KEY_MISS; break;\n        default: return -1;\n        }\n    }\n    return flags;\n}\n\n/* This function does exactly the revese of the function above: it gets\n * as input an integer with the xored flags and returns a string representing\n * the selected classes. The string returned is an sds string that needs to\n * be released with sdsfree(). */\nsds keyspaceEventsFlagsToString(int flags) {\n    sds res;\n\n    res = sdsempty();\n    if ((flags & NOTIFY_ALL) == NOTIFY_ALL) {\n        res = sdscatlen(res,\"A\",1);\n    } else {\n        if (flags & NOTIFY_GENERIC) res = sdscatlen(res,\"g\",1);\n        if (flags & NOTIFY_STRING) res = sdscatlen(res,\"$\",1);\n        if (flags & NOTIFY_LIST) res = sdscatlen(res,\"l\",1);\n        if (flags & NOTIFY_SET) res = sdscatlen(res,\"s\",1);\n        if (flags & NOTIFY_HASH) res = sdscatlen(res,\"h\",1);\n        if (flags & NOTIFY_ZSET) res = sdscatlen(res,\"z\",1);\n        if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,\"x\",1);\n        if (flags & NOTIFY_EVICTED) res = sdscatlen(res,\"e\",1);\n        if (flags & NOTIFY_STREAM) res = sdscatlen(res,\"t\",1);\n    }\n    if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,\"K\",1);\n    if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,\"E\",1);\n    if (flags & NOTIFY_KEY_MISS) res = sdscatlen(res,\"m\",1);\n    return res;\n}\n\n/* The API provided to the rest of the Redis core is a simple function:\n *\n * notifyKeyspaceEvent(char *event, robj *key, int dbid);\n *\n * 'event' is a C string representing the event name.\n * 'key' is a Redis object representing the key name.\n * 'dbid' is the database ID where the key lives.  */\nvoid notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {\n    sds chan;\n    robj *chanobj, *eventobj;\n    int len = -1;\n    char buf[24];\n\n    /* If any modules are interested in events, notify the module system now.\n     * This bypasses the notifications configuration, but the module engine\n     * will only call event subscribers if the event type matches the types\n     * they are interested in. */\n     moduleNotifyKeyspaceEvent(type, event, key, dbid);\n\n    /* If notifications for this class of events are off, return ASAP. */\n    if (!(server.notify_keyspace_events & type)) return;\n\n    eventobj = createStringObject(event,strlen(event));\n\n    /* __keyspace@<db>__:<key> <event> notifications. */\n    if (server.notify_keyspace_events & NOTIFY_KEYSPACE) {\n        chan = sdsnewlen(\"__keyspace@\",11);\n        len = ll2string(buf,sizeof(buf),dbid);\n        chan = sdscatlen(chan, buf, len);\n        chan = sdscatlen(chan, \"__:\", 3);\n        chan = sdscatsds(chan, key->ptr);\n        chanobj = createObject(OBJ_STRING, chan);\n        pubsubPublishMessage(chanobj, eventobj);\n        decrRefCount(chanobj);\n    }\n\n    /* __keyevent@<db>__:<event> <key> notifications. */\n    if (server.notify_keyspace_events & NOTIFY_KEYEVENT) {\n        chan = sdsnewlen(\"__keyevent@\",11);\n        if (len == -1) len = ll2string(buf,sizeof(buf),dbid);\n        chan = sdscatlen(chan, buf, len);\n        chan = sdscatlen(chan, \"__:\", 3);\n        chan = sdscatsds(chan, eventobj->ptr);\n        chanobj = createObject(OBJ_STRING, chan);\n        pubsubPublishMessage(chanobj, key);\n        decrRefCount(chanobj);\n    }\n    decrRefCount(eventobj);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include <math.h>\n#include <ctype.h>\n\n#ifdef __CYGWIN__\n#define strtold(a,b) ((long double)strtod((a),(b)))\n#endif\n\n/* ===================== Creation and parsing of objects ==================== */\n\nrobj *createObject(int type, void *ptr) {\n    robj *o = zmalloc(sizeof(*o));\n    o->type = type;\n    o->encoding = OBJ_ENCODING_RAW;\n    o->ptr = ptr;\n    o->refcount = 1;\n\n    /* Set the LRU to the current lruclock (minutes resolution), or\n     * alternatively the LFU counter. */\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;\n    } else {\n        o->lru = LRU_CLOCK();\n    }\n    return o;\n}\n\n/* Set a special refcount in the object to make it \"shared\":\n * incrRefCount and decrRefCount() will test for this special refcount\n * and will not touch the object. This way it is free to access shared\n * objects such as small integers from different threads without any\n * mutex.\n *\n * A common patter to create shared objects:\n *\n * robj *myobject = makeObjectShared(createObject(...));\n *\n */\nrobj *makeObjectShared(robj *o) {\n    serverAssert(o->refcount == 1);\n    o->refcount = OBJ_SHARED_REFCOUNT;\n    return o;\n}\n\n/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain\n * string object where o->ptr points to a proper sds string. */\nrobj *createRawStringObject(const char *ptr, size_t len) {\n    return createObject(OBJ_STRING, sdsnewlen(ptr,len));\n}\n\n/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is\n * an object where the sds string is actually an unmodifiable string\n * allocated in the same chunk as the object itself. */\nrobj *createEmbeddedStringObject(const char *ptr, size_t len) {\n    robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);\n    struct sdshdr8 *sh = (void*)(o+1);\n\n    o->type = OBJ_STRING;\n    o->encoding = OBJ_ENCODING_EMBSTR;\n    o->ptr = sh+1;\n    o->refcount = 1;\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;\n    } else {\n        o->lru = LRU_CLOCK();\n    }\n\n    sh->len = len;\n    sh->alloc = len;\n    sh->flags = SDS_TYPE_8;\n    if (ptr == SDS_NOINIT)\n        sh->buf[len] = '\\0';\n    else 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 * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is\n * used.\n *\n * The current limit of 44 is chosen so that the biggest string object\n * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */\n#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44\nrobj *createStringObject(const char *ptr, size_t len) {\n    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)\n        return createEmbeddedStringObject(ptr,len);\n    else\n        return createRawStringObject(ptr,len);\n}\n\n/* Create a string object from a long long value. When possible returns a\n * shared integer object, or at least an integer encoded one.\n *\n * If valueobj is non zero, the function avoids returning a a shared\n * integer, because the object is going to be used as value in the Redis key\n * space (for instance when the INCR command is used), so we want LFU/LRU\n * values specific for each key. */\nrobj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {\n    robj *o;\n\n    if (server.maxmemory == 0 ||\n        !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))\n    {\n        /* If the maxmemory policy permits, we can still return shared integers\n         * even if valueobj is true. */\n        valueobj = 0;\n    }\n\n    if (value >= 0 && value < OBJ_SHARED_INTEGERS && valueobj == 0) {\n        incrRefCount(shared.integers[value]);\n        o = shared.integers[value];\n    } else {\n        if (value >= LONG_MIN && value <= LONG_MAX) {\n            o = createObject(OBJ_STRING, NULL);\n            o->encoding = OBJ_ENCODING_INT;\n            o->ptr = (void*)((long)value);\n        } else {\n            o = createObject(OBJ_STRING,sdsfromlonglong(value));\n        }\n    }\n    return o;\n}\n\n/* Wrapper for createStringObjectFromLongLongWithOptions() always demanding\n * to create a shared object if possible. */\nrobj *createStringObjectFromLongLong(long long value) {\n    return createStringObjectFromLongLongWithOptions(value,0);\n}\n\n/* Wrapper for createStringObjectFromLongLongWithOptions() avoiding a shared\n * object when LFU/LRU info are needed, that is, when the object is used\n * as a value in the key space, and Redis is configured to evict based on\n * LFU/LRU. */\nrobj *createStringObjectFromLongLongForValue(long long value) {\n    return createStringObjectFromLongLongWithOptions(value,1);\n}\n\n/* Create a string object from a long double. If humanfriendly is non-zero\n * it does not use exponential format and trims trailing zeroes at the end,\n * however this results in loss of precision. Otherwise exp format is used\n * and the output of snprintf() is not modified.\n *\n * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */\nrobj *createStringObjectFromLongDouble(long double value, int humanfriendly) {\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    int len = ld2string(buf,sizeof(buf),value,humanfriendly? LD_STR_HUMAN: LD_STR_AUTO);\n    return createStringObject(buf,len);\n}\n\n/* Duplicate a string object, with the guarantee that the returned object\n * has the same encoding as the original one.\n *\n * This function also guarantees that duplicating a small integer object\n * (or a string object that contains a representation of a small integer)\n * will always result in a fresh object that is unshared (refcount == 1).\n *\n * The resulting object always has refcount set to 1. */\nrobj *dupStringObject(const robj *o) {\n    robj *d;\n\n    serverAssert(o->type == OBJ_STRING);\n\n    switch(o->encoding) {\n    case OBJ_ENCODING_RAW:\n        return createRawStringObject(o->ptr,sdslen(o->ptr));\n    case OBJ_ENCODING_EMBSTR:\n        return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));\n    case OBJ_ENCODING_INT:\n        d = createObject(OBJ_STRING, NULL);\n        d->encoding = OBJ_ENCODING_INT;\n        d->ptr = o->ptr;\n        return d;\n    default:\n        serverPanic(\"Wrong encoding.\");\n        break;\n    }\n}\n\nrobj *createQuicklistObject(void) {\n    quicklist *l = quicklistCreate();\n    robj *o = createObject(OBJ_LIST,l);\n    o->encoding = OBJ_ENCODING_QUICKLIST;\n    return o;\n}\n\nrobj *createZiplistObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_LIST,zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createSetObject(void) {\n    dict *d = dictCreate(&setDictType,NULL);\n    robj *o = createObject(OBJ_SET,d);\n    o->encoding = OBJ_ENCODING_HT;\n    return o;\n}\n\nrobj *createIntsetObject(void) {\n    intset *is = intsetNew();\n    robj *o = createObject(OBJ_SET,is);\n    o->encoding = OBJ_ENCODING_INTSET;\n    return o;\n}\n\nrobj *createHashObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_HASH, zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createZsetObject(void) {\n    zset *zs = zmalloc(sizeof(*zs));\n    robj *o;\n\n    zs->dict = dictCreate(&zsetDictType,NULL);\n    zs->zsl = zslCreate();\n    o = createObject(OBJ_ZSET,zs);\n    o->encoding = OBJ_ENCODING_SKIPLIST;\n    return o;\n}\n\nrobj *createZsetZiplistObject(void) {\n    unsigned char *zl = ziplistNew();\n    robj *o = createObject(OBJ_ZSET,zl);\n    o->encoding = OBJ_ENCODING_ZIPLIST;\n    return o;\n}\n\nrobj *createStreamObject(void) {\n    stream *s = streamNew();\n    robj *o = createObject(OBJ_STREAM,s);\n    o->encoding = OBJ_ENCODING_STREAM;\n    return o;\n}\n\nrobj *createModuleObject(moduleType *mt, void *value) {\n    moduleValue *mv = zmalloc(sizeof(*mv));\n    mv->type = mt;\n    mv->value = value;\n    return createObject(OBJ_MODULE,mv);\n}\n\nvoid freeStringObject(robj *o) {\n    if (o->encoding == OBJ_ENCODING_RAW) {\n        sdsfree(o->ptr);\n    }\n}\n\nvoid freeListObject(robj *o) {\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistRelease(o->ptr);\n    } else {\n        serverPanic(\"Unknown list encoding type\");\n    }\n}\n\nvoid freeSetObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n    case OBJ_ENCODING_INTSET:\n        zfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown set encoding type\");\n    }\n}\n\nvoid freeZsetObject(robj *o) {\n    zset *zs;\n    switch (o->encoding) {\n    case OBJ_ENCODING_SKIPLIST:\n        zs = o->ptr;\n        dictRelease(zs->dict);\n        zslFree(zs->zsl);\n        zfree(zs);\n        break;\n    case OBJ_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid freeHashObject(robj *o) {\n    switch (o->encoding) {\n    case OBJ_ENCODING_HT:\n        dictRelease((dict*) o->ptr);\n        break;\n    case OBJ_ENCODING_ZIPLIST:\n        zfree(o->ptr);\n        break;\n    default:\n        serverPanic(\"Unknown hash encoding type\");\n        break;\n    }\n}\n\nvoid freeModuleObject(robj *o) {\n    moduleValue *mv = o->ptr;\n    mv->type->free(mv->value);\n    zfree(mv);\n}\n\nvoid freeStreamObject(robj *o) {\n    freeStream(o->ptr);\n}\n\nvoid incrRefCount(robj *o) {\n    if (o->refcount < OBJ_FIRST_SPECIAL_REFCOUNT) {\n        o->refcount++;\n    } else {\n        if (o->refcount == OBJ_SHARED_REFCOUNT) {\n            /* Nothing to do: this refcount is immutable. */\n        } else if (o->refcount == OBJ_STATIC_REFCOUNT) {\n            serverPanic(\"You tried to retain an object allocated in the stack\");\n        }\n    }\n}\n\nvoid decrRefCount(robj *o) {\n    if (o->refcount == 1) {\n        switch(o->type) {\n        case OBJ_STRING: freeStringObject(o); break;\n        case OBJ_LIST: freeListObject(o); break;\n        case OBJ_SET: freeSetObject(o); break;\n        case OBJ_ZSET: freeZsetObject(o); break;\n        case OBJ_HASH: freeHashObject(o); break;\n        case OBJ_MODULE: freeModuleObject(o); break;\n        case OBJ_STREAM: freeStreamObject(o); break;\n        default: serverPanic(\"Unknown object type\"); break;\n        }\n        zfree(o);\n    } else {\n        if (o->refcount <= 0) serverPanic(\"decrRefCount against refcount <= 0\");\n        if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;\n    }\n}\n\n/* This variant of decrRefCount() gets its argument as void, and is useful\n * as free method in data structures that expect a 'void free_object(void*)'\n * prototype for the free method. */\nvoid decrRefCountVoid(void *o) {\n    decrRefCount(o);\n}\n\n/* This function set the ref count to zero without freeing the object.\n * It is useful in order to pass a new object to functions incrementing\n * the ref count of the received object. Example:\n *\n *    functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));\n *\n * Otherwise you need to resort to the less elegant pattern:\n *\n *    *obj = createObject(...);\n *    functionThatWillIncrementRefCount(obj);\n *    decrRefCount(obj);\n */\nrobj *resetRefCount(robj *obj) {\n    obj->refcount = 0;\n    return obj;\n}\n\nint checkType(client *c, robj *o, int type) {\n    if (o->type != type) {\n        addReply(c,shared.wrongtypeerr);\n        return 1;\n    }\n    return 0;\n}\n\nint isSdsRepresentableAsLongLong(sds s, long long *llval) {\n    return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;\n}\n\nint isObjectRepresentableAsLongLong(robj *o, long long *llval) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    if (o->encoding == OBJ_ENCODING_INT) {\n        if (llval) *llval = (long) o->ptr;\n        return C_OK;\n    } else {\n        return isSdsRepresentableAsLongLong(o->ptr,llval);\n    }\n}\n\n/* Optimize the SDS string inside the string object to require little space,\n * in case there is more than 10% of free space at the end of the SDS\n * string. This happens because SDS strings tend to overallocate to avoid\n * wasting too much time in allocations when appending to the string. */\nvoid trimStringObjectIfNeeded(robj *o) {\n    if (o->encoding == OBJ_ENCODING_RAW &&\n        sdsavail(o->ptr) > sdslen(o->ptr)/10)\n    {\n        o->ptr = sdsRemoveFreeSpace(o->ptr);\n    }\n}\n\n/* Try to encode a string object in order to save space */\nrobj *tryObjectEncoding(robj *o) {\n    long value;\n    sds s = o->ptr;\n    size_t len;\n\n    /* Make sure this is a string object, the only type we encode\n     * in this function. Other types use encoded memory efficient\n     * representations but are handled by the commands implementing\n     * the type. */\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n\n    /* We try some specialized encoding only for objects that are\n     * RAW or EMBSTR encoded, in other words objects that are still\n     * in represented by an actually array of chars. */\n    if (!sdsEncodedObject(o)) return o;\n\n    /* It's 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     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 20 chars is not\n     * representable as a 32 nor 64 bit integer. */\n    len = sdslen(s);\n    if (len <= 20 && 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            !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) &&\n            value >= 0 &&\n            value < OBJ_SHARED_INTEGERS)\n        {\n            decrRefCount(o);\n            incrRefCount(shared.integers[value]);\n            return shared.integers[value];\n        } else {\n            if (o->encoding == OBJ_ENCODING_RAW) {\n                sdsfree(o->ptr);\n                o->encoding = OBJ_ENCODING_INT;\n                o->ptr = (void*) value;\n                return o;\n            } else if (o->encoding == OBJ_ENCODING_EMBSTR) {\n                decrRefCount(o);\n                return createStringObjectFromLongLongForValue(value);\n            }\n        }\n    }\n\n    /* If the string is small and is still RAW encoded,\n     * try the EMBSTR encoding which is more efficient.\n     * In this representation the object and the SDS string are allocated\n     * in the same chunk of memory to save space and cache misses. */\n    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {\n        robj *emb;\n\n        if (o->encoding == OBJ_ENCODING_EMBSTR) return o;\n        emb = createEmbeddedStringObject(s,sdslen(s));\n        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     * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */\n    trimStringObjectIfNeeded(o);\n\n    /* Return the original object. */\n    return o;\n}\n\n/* Get a decoded version of an encoded object (returned as a new object).\n * If the object is already raw-encoded just increment the ref count. */\nrobj *getDecodedObject(robj *o) {\n    robj *dec;\n\n    if (sdsEncodedObject(o)) {\n        incrRefCount(o);\n        return o;\n    }\n    if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {\n        char buf[32];\n\n        ll2string(buf,32,(long)o->ptr);\n        dec = createStringObject(buf,strlen(buf));\n        return dec;\n    } else {\n        serverPanic(\"Unknown encoding type\");\n    }\n}\n\n/* Compare two string objects via strcmp() or strcoll() depending on flags.\n * Note that the objects may be integer-encoded. In such a case we\n * use ll2string() to get a string representation of the numbers on the stack\n * and compare the strings, it's much faster than calling getDecodedObject().\n *\n * Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison\n * is used. */\n\n#define REDIS_COMPARE_BINARY (1<<0)\n#define REDIS_COMPARE_COLL (1<<1)\n\nint compareStringObjectsWithFlags(robj *a, robj *b, int flags) {\n    serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING);\n    char bufa[128], bufb[128], *astr, *bstr;\n    size_t alen, blen, minlen;\n\n    if (a == b) return 0;\n    if (sdsEncodedObject(a)) {\n        astr = a->ptr;\n        alen = sdslen(astr);\n    } else {\n        alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);\n        astr = bufa;\n    }\n    if (sdsEncodedObject(b)) {\n        bstr = b->ptr;\n        blen = sdslen(bstr);\n    } else {\n        blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);\n        bstr = bufb;\n    }\n    if (flags & REDIS_COMPARE_COLL) {\n        return strcoll(astr,bstr);\n    } else {\n        int cmp;\n\n        minlen = (alen < blen) ? alen : blen;\n        cmp = memcmp(astr,bstr,minlen);\n        if (cmp == 0) return alen-blen;\n        return cmp;\n    }\n}\n\n/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */\nint compareStringObjects(robj *a, robj *b) {\n    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);\n}\n\n/* Wrapper for compareStringObjectsWithFlags() using collation. */\nint collateStringObjects(robj *a, robj *b) {\n    return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);\n}\n\n/* Equal string objects return 1 if the two objects are the same from the\n * point of view of a string comparison, otherwise 0 is returned. Note that\n * this function is faster then checking for (compareStringObject(a,b) == 0)\n * because it can perform some more optimization. */\nint equalStringObjects(robj *a, robj *b) {\n    if (a->encoding == OBJ_ENCODING_INT &&\n        b->encoding == OBJ_ENCODING_INT){\n        /* If both strings are integer encoded just check if the stored\n         * long is the same. */\n        return a->ptr == b->ptr;\n    } else {\n        return compareStringObjects(a,b) == 0;\n    }\n}\n\nsize_t stringObjectLen(robj *o) {\n    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n    if (sdsEncodedObject(o)) {\n        return sdslen(o->ptr);\n    } else {\n        return sdigits10((long)o->ptr);\n    }\n}\n\nint getDoubleFromObject(const robj *o, double *target) {\n    double value;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            if (!string2d(o->ptr, sdslen(o->ptr), &value))\n                return C_ERR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg) {\n    double value;\n    if (getDoubleFromObject(o, &value) != C_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongDoubleFromObject(robj *o, long double *target) {\n    long double value;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            if (!string2ld(o->ptr, sdslen(o->ptr), &value))\n                return C_ERR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg) {\n    long double value;\n    if (getLongDoubleFromObject(o, &value) != C_OK) {\n        if (msg != NULL) {\n            addReplyError(c,(char*)msg);\n        } else {\n            addReplyError(c,\"value is not a valid float\");\n        }\n        return C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongLongFromObject(robj *o, long long *target) {\n    long long value;\n\n    if (o == NULL) {\n        value = 0;\n    } else {\n        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);\n        if (sdsEncodedObject(o)) {\n            if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;\n        } else if (o->encoding == OBJ_ENCODING_INT) {\n            value = (long)o->ptr;\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    }\n    if (target) *target = value;\n    return C_OK;\n}\n\nint getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {\n    long long value;\n    if (getLongLongFromObject(o, &value) != C_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 C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nint getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {\n    long long value;\n\n    if (getLongLongFromObjectOrReply(c, o, &value, msg) != C_OK) return C_ERR;\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 C_ERR;\n    }\n    *target = value;\n    return C_OK;\n}\n\nchar *strEncoding(int encoding) {\n    switch(encoding) {\n    case OBJ_ENCODING_RAW: return \"raw\";\n    case OBJ_ENCODING_INT: return \"int\";\n    case OBJ_ENCODING_HT: return \"hashtable\";\n    case OBJ_ENCODING_QUICKLIST: return \"quicklist\";\n    case OBJ_ENCODING_ZIPLIST: return \"ziplist\";\n    case OBJ_ENCODING_INTSET: return \"intset\";\n    case OBJ_ENCODING_SKIPLIST: return \"skiplist\";\n    case OBJ_ENCODING_EMBSTR: return \"embstr\";\n    default: return \"unknown\";\n    }\n}\n\n/* =========================== Memory introspection ========================= */\n\n\n/* This is an helper function with the goal of estimating the memory\n * size of a radix tree that is used to store Stream IDs.\n *\n * Note: to guess the size of the radix tree is not trivial, so we\n * approximate it considering 16 bytes of data overhead for each\n * key (the ID), and then adding the number of bare nodes, plus some\n * overhead due by the data and child pointers. This secret recipe\n * was obtained by checking the average radix tree created by real\n * workloads, and then adjusting the constants to get numbers that\n * more or less match the real memory usage.\n *\n * Actually the number of nodes and keys may be different depending\n * on the insertion speed and thus the ability of the radix tree\n * to compress prefixes. */\nsize_t streamRadixTreeMemoryUsage(rax *rax) {\n    size_t size;\n    size = rax->numele * sizeof(streamID);\n    size += rax->numnodes * sizeof(raxNode);\n    /* Add a fixed overhead due to the aux data pointer, children, ... */\n    size += rax->numnodes * sizeof(long)*30;\n    return size;\n}\n\n/* Returns the size in bytes consumed by the key's value in RAM.\n * Note that the returned value is just an approximation, especially in the\n * case of aggregated data types where only \"sample_size\" elements\n * are checked and averaged to estimate the total size. */\n#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */\nsize_t objectComputeSize(robj *o, size_t sample_size) {\n    sds ele, ele2;\n    dict *d;\n    dictIterator *di;\n    struct dictEntry *de;\n    size_t asize = 0, elesize = 0, samples = 0;\n\n    if (o->type == OBJ_STRING) {\n        if(o->encoding == OBJ_ENCODING_INT) {\n            asize = sizeof(*o);\n        } else if(o->encoding == OBJ_ENCODING_RAW) {\n            asize = sdsAllocSize(o->ptr)+sizeof(*o);\n        } else if(o->encoding == OBJ_ENCODING_EMBSTR) {\n            asize = sdslen(o->ptr)+2+sizeof(*o);\n        } else {\n            serverPanic(\"Unknown string encoding\");\n        }\n    } else if (o->type == OBJ_LIST) {\n        if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n            quicklist *ql = o->ptr;\n            quicklistNode *node = ql->head;\n            asize = sizeof(*o)+sizeof(quicklist);\n            do {\n                elesize += sizeof(quicklistNode)+ziplistBlobLen(node->zl);\n                samples++;\n            } while ((node = node->next) && samples < sample_size);\n            asize += (double)elesize/samples*ql->len;\n        } else if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            asize = sizeof(*o)+ziplistBlobLen(o->ptr);\n        } else {\n            serverPanic(\"Unknown list encoding\");\n        }\n    } else if (o->type == OBJ_SET) {\n        if (o->encoding == OBJ_ENCODING_HT) {\n            d = o->ptr;\n            di = dictGetIterator(d);\n            asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));\n            while((de = dictNext(di)) != NULL && samples < sample_size) {\n                ele = dictGetKey(de);\n                elesize += sizeof(struct dictEntry) + sdsAllocSize(ele);\n                samples++;\n            }\n            dictReleaseIterator(di);\n            if (samples) asize += (double)elesize/samples*dictSize(d);\n        } else if (o->encoding == OBJ_ENCODING_INTSET) {\n            intset *is = o->ptr;\n            asize = sizeof(*o)+sizeof(*is)+is->encoding*is->length;\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (o->type == OBJ_ZSET) {\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            asize = sizeof(*o)+(ziplistBlobLen(o->ptr));\n        } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {\n            d = ((zset*)o->ptr)->dict;\n            zskiplist *zsl = ((zset*)o->ptr)->zsl;\n            zskiplistNode *znode = zsl->header->level[0].forward;\n            asize = sizeof(*o)+sizeof(zset)+sizeof(zskiplist)+sizeof(dict)+\n                    (sizeof(struct dictEntry*)*dictSlots(d))+\n                    zmalloc_size(zsl->header);\n            while(znode != NULL && samples < sample_size) {\n                elesize += sdsAllocSize(znode->ele);\n                elesize += sizeof(struct dictEntry) + zmalloc_size(znode);\n                samples++;\n                znode = znode->level[0].forward;\n            }\n            if (samples) asize += (double)elesize/samples*dictSize(d);\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (o->type == OBJ_HASH) {\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            asize = sizeof(*o)+(ziplistBlobLen(o->ptr));\n        } else if (o->encoding == OBJ_ENCODING_HT) {\n            d = o->ptr;\n            di = dictGetIterator(d);\n            asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));\n            while((de = dictNext(di)) != NULL && samples < sample_size) {\n                ele = dictGetKey(de);\n                ele2 = dictGetVal(de);\n                elesize += sdsAllocSize(ele) + sdsAllocSize(ele2);\n                elesize += sizeof(struct dictEntry);\n                samples++;\n            }\n            dictReleaseIterator(di);\n            if (samples) asize += (double)elesize/samples*dictSize(d);\n        } else {\n            serverPanic(\"Unknown hash encoding\");\n        }\n    } else if (o->type == OBJ_STREAM) {\n        stream *s = o->ptr;\n        asize = sizeof(*o);\n        asize += streamRadixTreeMemoryUsage(s->rax);\n\n        /* Now we have to add the listpacks. The last listpack is often non\n         * complete, so we estimate the size of the first N listpacks, and\n         * use the average to compute the size of the first N-1 listpacks, and\n         * finally add the real size of the last node. */\n        raxIterator ri;\n        raxStart(&ri,s->rax);\n        raxSeek(&ri,\"^\",NULL,0);\n        size_t lpsize = 0, samples = 0;\n        while(samples < sample_size && raxNext(&ri)) {\n            unsigned char *lp = ri.data;\n            lpsize += lpBytes(lp);\n            samples++;\n        }\n        if (s->rax->numele <= samples) {\n            asize += lpsize;\n        } else {\n            if (samples) lpsize /= samples; /* Compute the average. */\n            asize += lpsize * (s->rax->numele-1);\n            /* No need to check if seek succeeded, we enter this branch only\n             * if there are a few elements in the radix tree. */\n            raxSeek(&ri,\"$\",NULL,0);\n            raxNext(&ri);\n            asize += lpBytes(ri.data);\n        }\n        raxStop(&ri);\n\n        /* Consumer groups also have a non trivial memory overhead if there\n         * are many consumers and many groups, let's count at least the\n         * overhead of the pending entries in the groups and consumers\n         * PELs. */\n        if (s->cgroups) {\n            raxStart(&ri,s->cgroups);\n            raxSeek(&ri,\"^\",NULL,0);\n            while(raxNext(&ri)) {\n                streamCG *cg = ri.data;\n                asize += sizeof(*cg);\n                asize += streamRadixTreeMemoryUsage(cg->pel);\n                asize += sizeof(streamNACK)*raxSize(cg->pel);\n\n                /* For each consumer we also need to add the basic data\n                 * structures and the PEL memory usage. */\n                raxIterator cri;\n                raxStart(&cri,cg->consumers);\n                raxSeek(&cri,\"^\",NULL,0);\n                while(raxNext(&cri)) {\n                    streamConsumer *consumer = cri.data;\n                    asize += sizeof(*consumer);\n                    asize += sdslen(consumer->name);\n                    asize += streamRadixTreeMemoryUsage(consumer->pel);\n                    /* Don't count NACKs again, they are shared with the\n                     * consumer group PEL. */\n                }\n                raxStop(&cri);\n            }\n            raxStop(&ri);\n        }\n    } else if (o->type == OBJ_MODULE) {\n        moduleValue *mv = o->ptr;\n        moduleType *mt = mv->type;\n        if (mt->mem_usage != NULL) {\n            asize = mt->mem_usage(mv->value);\n        } else {\n            asize = 0;\n        }\n    } else {\n        serverPanic(\"Unknown object type\");\n    }\n    return asize;\n}\n\n/* Release data obtained with getMemoryOverheadData(). */\nvoid freeMemoryOverheadData(struct redisMemOverhead *mh) {\n    zfree(mh->db);\n    zfree(mh);\n}\n\n/* Return a struct redisMemOverhead filled with memory overhead\n * information used for the MEMORY OVERHEAD and INFO command. The returned\n * structure pointer should be freed calling freeMemoryOverheadData(). */\nstruct redisMemOverhead *getMemoryOverheadData(void) {\n    int j;\n    size_t mem_total = 0;\n    size_t mem = 0;\n    size_t zmalloc_used = zmalloc_used_memory();\n    struct redisMemOverhead *mh = zcalloc(sizeof(*mh));\n\n    mh->total_allocated = zmalloc_used;\n    mh->startup_allocated = server.initial_memory_usage;\n    mh->peak_allocated = server.stat_peak_memory;\n    mh->total_frag =\n        (float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.zmalloc_used;\n    mh->total_frag_bytes =\n        server.cron_malloc_stats.process_rss - server.cron_malloc_stats.zmalloc_used;\n    mh->allocator_frag =\n        (float)server.cron_malloc_stats.allocator_active / server.cron_malloc_stats.allocator_allocated;\n    mh->allocator_frag_bytes =\n        server.cron_malloc_stats.allocator_active - server.cron_malloc_stats.allocator_allocated;\n    mh->allocator_rss =\n        (float)server.cron_malloc_stats.allocator_resident / server.cron_malloc_stats.allocator_active;\n    mh->allocator_rss_bytes =\n        server.cron_malloc_stats.allocator_resident - server.cron_malloc_stats.allocator_active;\n    mh->rss_extra =\n        (float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.allocator_resident;\n    mh->rss_extra_bytes =\n        server.cron_malloc_stats.process_rss - server.cron_malloc_stats.allocator_resident;\n\n    mem_total += server.initial_memory_usage;\n\n    mem = 0;\n    if (server.repl_backlog)\n        mem += zmalloc_size(server.repl_backlog);\n    mh->repl_backlog = mem;\n    mem_total += mem;\n\n    /* Computing the memory used by the clients would be O(N) if done\n     * here online. We use our values computed incrementally by\n     * clientsCronTrackClientsMemUsage(). */\n    mh->clients_slaves = server.stat_clients_type_memory[CLIENT_TYPE_SLAVE];\n    mh->clients_normal = server.stat_clients_type_memory[CLIENT_TYPE_MASTER]+\n                         server.stat_clients_type_memory[CLIENT_TYPE_PUBSUB]+\n                         server.stat_clients_type_memory[CLIENT_TYPE_NORMAL];\n    mem_total += mh->clients_slaves;\n    mem_total += mh->clients_normal;\n\n    mem = 0;\n    if (server.aof_state != AOF_OFF) {\n        mem += sdsalloc(server.aof_buf);\n        mem += aofRewriteBufferSize();\n    }\n    mh->aof_buffer = mem;\n    mem_total+=mem;\n\n    mem = server.lua_scripts_mem;\n    mem += dictSize(server.lua_scripts) * sizeof(dictEntry) +\n        dictSlots(server.lua_scripts) * sizeof(dictEntry*);\n    mem += dictSize(server.repl_scriptcache_dict) * sizeof(dictEntry) +\n        dictSlots(server.repl_scriptcache_dict) * sizeof(dictEntry*);\n    if (listLength(server.repl_scriptcache_fifo) > 0) {\n        mem += listLength(server.repl_scriptcache_fifo) * (sizeof(listNode) + \n            sdsZmallocSize(listNodeValue(listFirst(server.repl_scriptcache_fifo))));\n    }\n    mh->lua_caches = mem;\n    mem_total+=mem;\n\n    for (j = 0; j < server.dbnum; j++) {\n        redisDb *db = server.db+j;\n        long long keyscount = dictSize(db->dict);\n        if (keyscount==0) continue;\n\n        mh->total_keys += keyscount;\n        mh->db = zrealloc(mh->db,sizeof(mh->db[0])*(mh->num_dbs+1));\n        mh->db[mh->num_dbs].dbid = j;\n\n        mem = dictSize(db->dict) * sizeof(dictEntry) +\n              dictSlots(db->dict) * sizeof(dictEntry*) +\n              dictSize(db->dict) * sizeof(robj);\n        mh->db[mh->num_dbs].overhead_ht_main = mem;\n        mem_total+=mem;\n\n        mem = dictSize(db->expires) * sizeof(dictEntry) +\n              dictSlots(db->expires) * sizeof(dictEntry*);\n        mh->db[mh->num_dbs].overhead_ht_expires = mem;\n        mem_total+=mem;\n\n        mh->num_dbs++;\n    }\n\n    mh->overhead_total = mem_total;\n    mh->dataset = zmalloc_used - mem_total;\n    mh->peak_perc = (float)zmalloc_used*100/mh->peak_allocated;\n\n    /* Metrics computed after subtracting the startup memory from\n     * the total memory. */\n    size_t net_usage = 1;\n    if (zmalloc_used > mh->startup_allocated)\n        net_usage = zmalloc_used - mh->startup_allocated;\n    mh->dataset_perc = (float)mh->dataset*100/net_usage;\n    mh->bytes_per_key = mh->total_keys ? (net_usage / mh->total_keys) : 0;\n\n    return mh;\n}\n\n/* Helper for \"MEMORY allocator-stats\", used as a callback for the jemalloc\n * stats output. */\nvoid inputCatSds(void *result, const char *str) {\n    /* result is actually a (sds *), so re-cast it here */\n    sds *info = (sds *)result;\n    *info = sdscat(*info, str);\n}\n\n/* This implements MEMORY DOCTOR. An human readable analysis of the Redis\n * memory condition. */\nsds getMemoryDoctorReport(void) {\n    int empty = 0;          /* Instance is empty or almost empty. */\n    int big_peak = 0;       /* Memory peak is much larger than used mem. */\n    int high_frag = 0;      /* High fragmentation. */\n    int high_alloc_frag = 0;/* High allocator fragmentation. */\n    int high_proc_rss = 0;  /* High process rss overhead. */\n    int high_alloc_rss = 0; /* High rss overhead. */\n    int big_slave_buf = 0;  /* Slave buffers are too big. */\n    int big_client_buf = 0; /* Client buffers are too big. */\n    int many_scripts = 0;   /* Script cache has too many scripts. */\n    int num_reports = 0;\n    struct redisMemOverhead *mh = getMemoryOverheadData();\n\n    if (mh->total_allocated < (1024*1024*5)) {\n        empty = 1;\n        num_reports++;\n    } else {\n        /* Peak is > 150% of current used memory? */\n        if (((float)mh->peak_allocated / mh->total_allocated) > 1.5) {\n            big_peak = 1;\n            num_reports++;\n        }\n\n        /* Fragmentation is higher than 1.4 and 10MB ?*/\n        if (mh->total_frag > 1.4 && mh->total_frag_bytes > 10<<20) {\n            high_frag = 1;\n            num_reports++;\n        }\n\n        /* External fragmentation is higher than 1.1 and 10MB? */\n        if (mh->allocator_frag > 1.1 && mh->allocator_frag_bytes > 10<<20) {\n            high_alloc_frag = 1;\n            num_reports++;\n        }\n\n        /* Allocator rss is higher than 1.1 and 10MB ? */\n        if (mh->allocator_rss > 1.1 && mh->allocator_rss_bytes > 10<<20) {\n            high_alloc_rss = 1;\n            num_reports++;\n        }\n\n        /* Non-Allocator rss is higher than 1.1 and 10MB ? */\n        if (mh->rss_extra > 1.1 && mh->rss_extra_bytes > 10<<20) {\n            high_proc_rss = 1;\n            num_reports++;\n        }\n\n        /* Clients using more than 200k each average? */\n        long numslaves = listLength(server.slaves);\n        long numclients = listLength(server.clients)-numslaves;\n        if (mh->clients_normal / numclients > (1024*200)) {\n            big_client_buf = 1;\n            num_reports++;\n        }\n\n        /* Slaves using more than 10 MB each? */\n        if (numslaves > 0 && mh->clients_slaves / numslaves > (1024*1024*10)) {\n            big_slave_buf = 1;\n            num_reports++;\n        }\n\n        /* Too many scripts are cached? */\n        if (dictSize(server.lua_scripts) > 1000) {\n            many_scripts = 1;\n            num_reports++;\n        }\n    }\n\n    sds s;\n    if (num_reports == 0) {\n        s = sdsnew(\n        \"Hi Sam, I can't find any memory issue in your instance. \"\n        \"I can only account for what occurs on this base.\\n\");\n    } else if (empty == 1) {\n        s = sdsnew(\n        \"Hi Sam, this instance is empty or is using very little memory, \"\n        \"my issues detector can't be used in these conditions. \"\n        \"Please, leave for your mission on Earth and fill it with some data. \"\n        \"The new Sam and I will be back to our programming as soon as I \"\n        \"finished rebooting.\\n\");\n    } else {\n        s = sdsnew(\"Sam, I detected a few issues in this Redis instance memory implants:\\n\\n\");\n        if (big_peak) {\n            s = sdscat(s,\" * Peak memory: In the past this instance used more than 150% the memory that is currently using. The allocator is normally not able to release memory after a peak, so you can expect to see a big fragmentation ratio, however this is actually harmless and is only due to the memory peak, and if the Redis instance Resident Set Size (RSS) is currently bigger than expected, the memory will be used as soon as you fill the Redis instance with more data. If the memory peak was only occasional and you want to try to reclaim memory, please try the MEMORY PURGE command, otherwise the only other option is to shutdown and restart the instance.\\n\\n\");\n        }\n        if (high_frag) {\n            s = sdscatprintf(s,\" * High total RSS: This instance has a memory fragmentation and RSS overhead greater than 1.4 (this means that the Resident Set Size of the Redis process is much larger than the sum of the logical allocations Redis performed). This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. If the problem is a large peak memory, then there is no issue. Otherwise, make sure you are using the Jemalloc allocator and not the default libc malloc. Note: The currently used allocator is \\\"%s\\\".\\n\\n\", ZMALLOC_LIB);\n        }\n        if (high_alloc_frag) {\n            s = sdscatprintf(s,\" * High allocator fragmentation: This instance has an allocator external fragmentation greater than 1.1. This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. You can try enabling 'activedefrag' config option.\\n\\n\");\n        }\n        if (high_alloc_rss) {\n            s = sdscatprintf(s,\" * High allocator RSS overhead: This instance has an RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the allocator is much larger than the sum what the allocator actually holds). This problem is usually due to a large peak memory (check if there is a peak memory entry above in the report), you can try the MEMORY PURGE command to reclaim it.\\n\\n\");\n        }\n        if (high_proc_rss) {\n            s = sdscatprintf(s,\" * High process RSS overhead: This instance has non-allocator RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the Redis process is much larger than the RSS the allocator holds). This problem may be due to Lua scripts or Modules.\\n\\n\");\n        }\n        if (big_slave_buf) {\n            s = sdscat(s,\" * Big replica buffers: The replica output buffers in this instance are greater than 10MB for each replica (on average). This likely means that there is some replica instance that is struggling receiving data, either because it is too slow or because of networking issues. As a result, data piles on the master output buffers. Please try to identify what replica is not receiving data correctly and why. You can use the INFO output in order to check the replicas delays and the CLIENT LIST command to check the output buffers of each replica.\\n\\n\");\n        }\n        if (big_client_buf) {\n            s = sdscat(s,\" * Big client buffers: The clients output buffers in this instance are greater than 200K per client (on average). This may result from different causes, like Pub/Sub clients subscribed to channels bot not receiving data fast enough, so that data piles on the Redis instance output buffer, or clients sending commands with large replies or very large sequences of commands in the same pipeline. Please use the CLIENT LIST command in order to investigate the issue if it causes problems in your instance, or to understand better why certain clients are using a big amount of memory.\\n\\n\");\n        }\n        if (many_scripts) {\n            s = sdscat(s,\" * Many scripts: There seem to be many cached scripts in this instance (more than 1000). This may be because scripts are generated and `EVAL`ed, instead of being parameterized (with KEYS and ARGV), `SCRIPT LOAD`ed and `EVALSHA`ed. Unless `SCRIPT FLUSH` is called periodically, the scripts' caches may end up consuming most of your memory.\\n\\n\");\n        }\n        s = sdscat(s,\"I'm here to keep you safe, Sam. I want to help you.\\n\");\n    }\n    freeMemoryOverheadData(mh);\n    return s;\n}\n\n/* Set the object LRU/LFU depending on server.maxmemory_policy.\n * The lfu_freq arg is only relevant if policy is MAXMEMORY_FLAG_LFU.\n * The lru_idle and lru_clock args are only relevant if policy\n * is MAXMEMORY_FLAG_LRU.\n * Either or both of them may be <0, in that case, nothing is set. */\nint objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,\n                       long long lru_clock, int lru_multiplier) {\n    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n        if (lfu_freq >= 0) {\n            serverAssert(lfu_freq <= 255);\n            val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;\n            return 1;\n        }\n    } else if (lru_idle >= 0) {\n        /* Provided LRU idle time is in seconds. Scale\n         * according to the LRU clock resolution this Redis\n         * instance was compiled with (normally 1000 ms, so the\n         * below statement will expand to lru_idle*1000/1000. */\n        lru_idle = lru_idle*lru_multiplier/LRU_CLOCK_RESOLUTION;\n        long lru_abs = lru_clock - lru_idle; /* Absolute access time. */\n        /* If the LRU field underflows (since LRU it is a wrapping\n         * clock), the best we can do is to provide a large enough LRU\n         * that is half-way in the circlular LRU clock we use: this way\n         * the computed idle time for this object will stay high for quite\n         * some time. */\n        if (lru_abs < 0)\n            lru_abs = (lru_clock+(LRU_CLOCK_MAX/2)) % LRU_CLOCK_MAX;\n        val->lru = lru_abs;\n        return 1;\n    }\n    return 0;\n}\n\n/* ======================= The OBJECT and MEMORY commands =================== */\n\n/* This is a helper function for the OBJECT command. We need to lookup keys\n * without any modification of LRU or other parameters. */\nrobj *objectCommandLookup(client *c, robj *key) {\n    dictEntry *de;\n\n    if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;\n    return (robj*) dictGetVal(de);\n}\n\nrobj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {\n    robj *o = objectCommandLookup(c,key);\n\n    if (!o) addReply(c, reply);\n    return o;\n}\n\n/* Object command allows to inspect the internals of an Redis Object.\n * Usage: OBJECT <refcount|encoding|idletime|freq> <key> */\nvoid objectCommand(client *c) {\n    robj *o;\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.\",\n\"FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.\",\n\"IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.\",\n\"REFCOUNT <key> -- Return the number of references of the value associated with the specified key.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"refcount\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        addReplyLongLong(c,o->refcount);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"encoding\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        addReplyBulkCString(c,strEncoding(o->encoding));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"idletime\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {\n            addReplyError(c,\"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.\");\n            return;\n        }\n        addReplyLongLong(c,estimateObjectIdleTime(o)/1000);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"freq\") && c->argc == 3) {\n        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))\n                == NULL) return;\n        if (!(server.maxmemory_policy & MAXMEMORY_FLAG_LFU)) {\n            addReplyError(c,\"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.\");\n            return;\n        }\n        /* LFUDecrAndReturn should be called\n         * in case of the key has not been accessed for a long time,\n         * because we update the access time only\n         * when the key is read or overwritten. */\n        addReplyLongLong(c,LFUDecrAndReturn(o));\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\n/* The memory command will eventually be a complete interface for the\n * memory introspection capabilities of Redis.\n *\n * Usage: MEMORY usage <key> */\nvoid memoryCommand(client *c) {\n    if (!strcasecmp(c->argv[1]->ptr,\"help\") && c->argc == 2) {\n        const char *help[] = {\n\"DOCTOR - Return memory problems reports.\",\n\"MALLOC-STATS -- Return internal statistics report from the memory allocator.\",\n\"PURGE -- Attempt to purge dirty pages for reclamation by the allocator.\",\n\"STATS -- Return information about the memory usage of the server.\",\n\"USAGE <key> [SAMPLES <count>] -- Return memory in bytes used by <key> and its value. Nested values are sampled up to <count> times (default: 5).\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"usage\") && c->argc >= 3) {\n        dictEntry *de;\n        long long samples = OBJ_COMPUTE_SIZE_DEF_SAMPLES;\n        for (int j = 3; j < c->argc; j++) {\n            if (!strcasecmp(c->argv[j]->ptr,\"samples\") &&\n                j+1 < c->argc)\n            {\n                if (getLongLongFromObjectOrReply(c,c->argv[j+1],&samples,NULL)\n                     == C_ERR) return;\n                if (samples < 0) {\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                if (samples == 0) samples = LLONG_MAX;;\n                j++; /* skip option argument. */\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n        if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {\n            addReplyNull(c);\n            return;\n        }\n        size_t usage = objectComputeSize(dictGetVal(de),samples);\n        usage += sdsAllocSize(dictGetKey(de));\n        usage += sizeof(dictEntry);\n        addReplyLongLong(c,usage);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"stats\") && c->argc == 2) {\n        struct redisMemOverhead *mh = getMemoryOverheadData();\n\n        addReplyMapLen(c,25+mh->num_dbs);\n\n        addReplyBulkCString(c,\"peak.allocated\");\n        addReplyLongLong(c,mh->peak_allocated);\n\n        addReplyBulkCString(c,\"total.allocated\");\n        addReplyLongLong(c,mh->total_allocated);\n\n        addReplyBulkCString(c,\"startup.allocated\");\n        addReplyLongLong(c,mh->startup_allocated);\n\n        addReplyBulkCString(c,\"replication.backlog\");\n        addReplyLongLong(c,mh->repl_backlog);\n\n        addReplyBulkCString(c,\"clients.slaves\");\n        addReplyLongLong(c,mh->clients_slaves);\n\n        addReplyBulkCString(c,\"clients.normal\");\n        addReplyLongLong(c,mh->clients_normal);\n\n        addReplyBulkCString(c,\"aof.buffer\");\n        addReplyLongLong(c,mh->aof_buffer);\n\n        addReplyBulkCString(c,\"lua.caches\");\n        addReplyLongLong(c,mh->lua_caches);\n\n        for (size_t j = 0; j < mh->num_dbs; j++) {\n            char dbname[32];\n            snprintf(dbname,sizeof(dbname),\"db.%zd\",mh->db[j].dbid);\n            addReplyBulkCString(c,dbname);\n            addReplyMapLen(c,2);\n\n            addReplyBulkCString(c,\"overhead.hashtable.main\");\n            addReplyLongLong(c,mh->db[j].overhead_ht_main);\n\n            addReplyBulkCString(c,\"overhead.hashtable.expires\");\n            addReplyLongLong(c,mh->db[j].overhead_ht_expires);\n        }\n\n        addReplyBulkCString(c,\"overhead.total\");\n        addReplyLongLong(c,mh->overhead_total);\n\n        addReplyBulkCString(c,\"keys.count\");\n        addReplyLongLong(c,mh->total_keys);\n\n        addReplyBulkCString(c,\"keys.bytes-per-key\");\n        addReplyLongLong(c,mh->bytes_per_key);\n\n        addReplyBulkCString(c,\"dataset.bytes\");\n        addReplyLongLong(c,mh->dataset);\n\n        addReplyBulkCString(c,\"dataset.percentage\");\n        addReplyDouble(c,mh->dataset_perc);\n\n        addReplyBulkCString(c,\"peak.percentage\");\n        addReplyDouble(c,mh->peak_perc);\n\n        addReplyBulkCString(c,\"allocator.allocated\");\n        addReplyLongLong(c,server.cron_malloc_stats.allocator_allocated);\n\n        addReplyBulkCString(c,\"allocator.active\");\n        addReplyLongLong(c,server.cron_malloc_stats.allocator_active);\n\n        addReplyBulkCString(c,\"allocator.resident\");\n        addReplyLongLong(c,server.cron_malloc_stats.allocator_resident);\n\n        addReplyBulkCString(c,\"allocator-fragmentation.ratio\");\n        addReplyDouble(c,mh->allocator_frag);\n\n        addReplyBulkCString(c,\"allocator-fragmentation.bytes\");\n        addReplyLongLong(c,mh->allocator_frag_bytes);\n\n        addReplyBulkCString(c,\"allocator-rss.ratio\");\n        addReplyDouble(c,mh->allocator_rss);\n\n        addReplyBulkCString(c,\"allocator-rss.bytes\");\n        addReplyLongLong(c,mh->allocator_rss_bytes);\n\n        addReplyBulkCString(c,\"rss-overhead.ratio\");\n        addReplyDouble(c,mh->rss_extra);\n\n        addReplyBulkCString(c,\"rss-overhead.bytes\");\n        addReplyLongLong(c,mh->rss_extra_bytes);\n\n        addReplyBulkCString(c,\"fragmentation\"); /* this is the total RSS overhead, including fragmentation */\n        addReplyDouble(c,mh->total_frag); /* it is kept here for backwards compatibility */\n\n        addReplyBulkCString(c,\"fragmentation.bytes\");\n        addReplyLongLong(c,mh->total_frag_bytes);\n\n        freeMemoryOverheadData(mh);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"malloc-stats\") && c->argc == 2) {\n#if defined(USE_JEMALLOC)\n        sds info = sdsempty();\n        je_malloc_stats_print(inputCatSds, &info, NULL);\n        addReplyVerbatim(c,info,sdslen(info),\"txt\");\n        sdsfree(info);\n#else\n        addReplyBulkCString(c,\"Stats not supported for the current allocator\");\n#endif\n    } else if (!strcasecmp(c->argv[1]->ptr,\"doctor\") && c->argc == 2) {\n        sds report = getMemoryDoctorReport();\n        addReplyVerbatim(c,report,sdslen(report),\"txt\");\n        sdsfree(report);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"purge\") && c->argc == 2) {\n        if (jemalloc_purge() == 0)\n            addReply(c, shared.ok);\n        else\n            addReplyError(c, \"Error purging dirty pages\");\n    } else {\n        addReplyErrorFormat(c, \"Unknown subcommand or wrong number of arguments for '%s'. Try MEMORY HELP\", (char*)c->argv[1]->ptr);\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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 \"server.h\"\n\nint clientSubscriptionsCount(client *c);\n\n/*-----------------------------------------------------------------------------\n * Pubsub client replies API\n *----------------------------------------------------------------------------*/\n\n/* Send a pubsub message of type \"message\" to the client.\n * Normally 'msg' is a Redis object containing the string to send as\n * message. However if the caller sets 'msg' as NULL, it will be able\n * to send a special message (for instance an Array type) by using the\n * addReply*() API family. */\nvoid addReplyPubsubMessage(client *c, robj *channel, robj *msg) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.messagebulk);\n    addReplyBulk(c,channel);\n    if (msg) addReplyBulk(c,msg);\n}\n\n/* Send a pubsub message of type \"pmessage\" to the client. The difference\n * with the \"message\" type delivered by addReplyPubsubMessage() is that\n * this message format also includes the pattern that matched the message. */\nvoid addReplyPubsubPatMessage(client *c, robj *pat, robj *channel, robj *msg) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[4]);\n    else\n        addReplyPushLen(c,4);\n    addReply(c,shared.pmessagebulk);\n    addReplyBulk(c,pat);\n    addReplyBulk(c,channel);\n    addReplyBulk(c,msg);\n}\n\n/* Send the pubsub subscription notification to the client. */\nvoid addReplyPubsubSubscribed(client *c, robj *channel) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.subscribebulk);\n    addReplyBulk(c,channel);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/* Send the pubsub unsubscription notification to the client.\n * Channel can be NULL: this is useful when the client sends a mass\n * unsubscribe command but there are no channels to unsubscribe from: we\n * still send a notification. */\nvoid addReplyPubsubUnsubscribed(client *c, robj *channel) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.unsubscribebulk);\n    if (channel)\n        addReplyBulk(c,channel);\n    else\n        addReplyNull(c);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/* Send the pubsub pattern subscription notification to the client. */\nvoid addReplyPubsubPatSubscribed(client *c, robj *pattern) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.psubscribebulk);\n    addReplyBulk(c,pattern);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/* Send the pubsub pattern unsubscription notification to the client.\n * Pattern can be NULL: this is useful when the client sends a mass\n * punsubscribe command but there are no pattern to unsubscribe from: we\n * still send a notification. */\nvoid addReplyPubsubPatUnsubscribed(client *c, robj *pattern) {\n    if (c->resp == 2)\n        addReply(c,shared.mbulkhdr[3]);\n    else\n        addReplyPushLen(c,3);\n    addReply(c,shared.punsubscribebulk);\n    if (pattern)\n        addReplyBulk(c,pattern);\n    else\n        addReplyNull(c);\n    addReplyLongLong(c,clientSubscriptionsCount(c));\n}\n\n/*-----------------------------------------------------------------------------\n * Pubsub low level API\n *----------------------------------------------------------------------------*/\n\nvoid freePubsubPattern(void *p) {\n    pubsubPattern *pat = p;\n\n    decrRefCount(pat->pattern);\n    zfree(pat);\n}\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/* Return the number of channels + patterns a client is subscribed to. */\nint clientSubscriptionsCount(client *c) {\n    return dictSize(c->pubsub_channels)+\n           listLength(c->pubsub_patterns);\n}\n\n/* Subscribe a client to a channel. Returns 1 if the operation succeeded, or\n * 0 if the client was already subscribed to that channel. */\nint pubsubSubscribeChannel(client *c, robj *channel) {\n    dictEntry *de;\n    list *clients = NULL;\n    int retval = 0;\n\n    /* Add the channel to the client -> channels hash table */\n    if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {\n        retval = 1;\n        incrRefCount(channel);\n        /* Add the client to the channel -> list of clients hash table */\n        de = dictFind(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        listAddNodeTail(clients,c);\n    }\n    /* Notify the client */\n    addReplyPubsubSubscribed(c,channel);\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. */\nint pubsubUnsubscribeChannel(client *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    incrRefCount(channel); /* channel may be just a pointer to the same object\n                            we have in the hash tables. Protect it... */\n    if (dictDelete(c->pubsub_channels,channel) == DICT_OK) {\n        retval = 1;\n        /* Remove the client from the channel -> clients list hash table */\n        de = dictFind(server.pubsub_channels,channel);\n        serverAssertWithInfo(c,NULL,de != NULL);\n        clients = dictGetVal(de);\n        ln = listSearchKey(clients,c);\n        serverAssertWithInfo(c,NULL,ln != NULL);\n        listDelNode(clients,ln);\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    /* Notify the client */\n    if (notify) addReplyPubsubUnsubscribed(c,channel);\n    decrRefCount(channel); /* it is finally safe to release it */\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. */\nint pubsubSubscribePattern(client *c, robj *pattern) {\n    dictEntry *de;\n    list *clients;\n    int retval = 0;\n\n    if (listSearchKey(c->pubsub_patterns,pattern) == NULL) {\n        retval = 1;\n        pubsubPattern *pat;\n        listAddNodeTail(c->pubsub_patterns,pattern);\n        incrRefCount(pattern);\n        pat = zmalloc(sizeof(*pat));\n        pat->pattern = getDecodedObject(pattern);\n        pat->client = c;\n        listAddNodeTail(server.pubsub_patterns,pat);\n        /* Add the client to the pattern -> list of clients hash table */\n        de = dictFind(server.pubsub_patterns_dict,pattern);\n        if (de == NULL) {\n            clients = listCreate();\n            dictAdd(server.pubsub_patterns_dict,pattern,clients);\n            incrRefCount(pattern);\n        } else {\n            clients = dictGetVal(de);\n        }\n        listAddNodeTail(clients,c);\n    }\n    /* Notify the client */\n    addReplyPubsubPatSubscribed(c,pattern);\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. */\nint pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {\n    dictEntry *de;\n    list *clients;\n    listNode *ln;\n    pubsubPattern pat;\n    int retval = 0;\n\n    incrRefCount(pattern); /* Protect the object. May be the same we remove */\n    if ((ln = listSearchKey(c->pubsub_patterns,pattern)) != NULL) {\n        retval = 1;\n        listDelNode(c->pubsub_patterns,ln);\n        pat.client = c;\n        pat.pattern = pattern;\n        ln = listSearchKey(server.pubsub_patterns,&pat);\n        listDelNode(server.pubsub_patterns,ln);\n        /* Remove the client from the pattern -> clients list hash table */\n        de = dictFind(server.pubsub_patterns_dict,pattern);\n        serverAssertWithInfo(c,NULL,de != NULL);\n        clients = dictGetVal(de);\n        ln = listSearchKey(clients,c);\n        serverAssertWithInfo(c,NULL,ln != NULL);\n        listDelNode(clients,ln);\n        if (listLength(clients) == 0) {\n            /* Free the list and associated hash entry at all if this was\n             * the latest client. */\n            dictDelete(server.pubsub_patterns_dict,pattern);\n        }\n    }\n    /* Notify the client */\n    if (notify) addReplyPubsubPatUnsubscribed(c,pattern);\n    decrRefCount(pattern);\n    return retval;\n}\n\n/* Unsubscribe from all the channels. Return the number of channels the\n * client was subscribed to. */\nint pubsubUnsubscribeAllChannels(client *c, int notify) {\n    dictIterator *di = dictGetSafeIterator(c->pubsub_channels);\n    dictEntry *de;\n    int count = 0;\n\n    while((de = dictNext(di)) != NULL) {\n        robj *channel = dictGetKey(de);\n\n        count += pubsubUnsubscribeChannel(c,channel,notify);\n    }\n    /* We were subscribed to nothing? Still reply to the client. */\n    if (notify && count == 0) addReplyPubsubUnsubscribed(c,NULL);\n    dictReleaseIterator(di);\n    return count;\n}\n\n/* Unsubscribe from all the patterns. Return the number of patterns the\n * client was subscribed from. */\nint pubsubUnsubscribeAllPatterns(client *c, int notify) {\n    listNode *ln;\n    listIter li;\n    int count = 0;\n\n    listRewind(c->pubsub_patterns,&li);\n    while ((ln = listNext(&li)) != NULL) {\n        robj *pattern = ln->value;\n\n        count += pubsubUnsubscribePattern(c,pattern,notify);\n    }\n    if (notify && count == 0) addReplyPubsubPatUnsubscribed(c,NULL);\n    return count;\n}\n\n/* Publish a message */\nint pubsubPublishMessage(robj *channel, robj *message) {\n    int receivers = 0;\n    dictEntry *de;\n    dictIterator *di;\n    listNode *ln;\n    listIter li;\n\n    /* Send to clients listening for that channel */\n    de = dictFind(server.pubsub_channels,channel);\n    if (de) {\n        list *list = dictGetVal(de);\n        listNode *ln;\n        listIter li;\n\n        listRewind(list,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            client *c = ln->value;\n            addReplyPubsubMessage(c,channel,message);\n            receivers++;\n        }\n    }\n    /* Send to clients listening to matching channels */\n    di = dictGetIterator(server.pubsub_patterns_dict);\n    if (di) {\n        channel = getDecodedObject(channel);\n        while((de = dictNext(di)) != NULL) {\n            robj *pattern = dictGetKey(de);\n            list *clients = dictGetVal(de);\n            if (!stringmatchlen((char*)pattern->ptr,\n                                sdslen(pattern->ptr),\n                                (char*)channel->ptr,\n                                sdslen(channel->ptr),0)) continue;\n\n            listRewind(clients,&li);\n            while ((ln = listNext(&li)) != NULL) {\n                client *c = listNodeValue(ln);\n                addReplyPubsubPatMessage(c,pattern,channel,message);\n                receivers++;\n            }\n        }\n        decrRefCount(channel);\n        dictReleaseIterator(di);\n    }\n    return receivers;\n}\n\n/*-----------------------------------------------------------------------------\n * Pubsub commands implementation\n *----------------------------------------------------------------------------*/\n\nvoid subscribeCommand(client *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribeChannel(c,c->argv[j]);\n    c->flags |= CLIENT_PUBSUB;\n}\n\nvoid unsubscribeCommand(client *c) {\n    if (c->argc == 1) {\n        pubsubUnsubscribeAllChannels(c,1);\n    } else {\n        int j;\n\n        for (j = 1; j < c->argc; j++)\n            pubsubUnsubscribeChannel(c,c->argv[j],1);\n    }\n    if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;\n}\n\nvoid psubscribeCommand(client *c) {\n    int j;\n\n    for (j = 1; j < c->argc; j++)\n        pubsubSubscribePattern(c,c->argv[j]);\n    c->flags |= CLIENT_PUBSUB;\n}\n\nvoid punsubscribeCommand(client *c) {\n    if (c->argc == 1) {\n        pubsubUnsubscribeAllPatterns(c,1);\n    } else {\n        int j;\n\n        for (j = 1; j < c->argc; j++)\n            pubsubUnsubscribePattern(c,c->argv[j],1);\n    }\n    if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;\n}\n\nvoid publishCommand(client *c) {\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,PROPAGATE_REPL);\n    addReplyLongLong(c,receivers);\n}\n\n/* PUBSUB command for Pub/Sub introspection. */\nvoid pubsubCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"CHANNELS [<pattern>] -- Return the currently active channels matching a pattern (default: all).\",\n\"NUMPAT -- Return number of subscriptions to patterns.\",\n\"NUMSUB [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none).\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"channels\") &&\n        (c->argc == 2 || c->argc == 3))\n    {\n        /* PUBSUB CHANNELS [<pattern>] */\n        sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;\n        dictIterator *di = dictGetIterator(server.pubsub_channels);\n        dictEntry *de;\n        long mblen = 0;\n        void *replylen;\n\n        replylen = addReplyDeferredLen(c);\n        while((de = dictNext(di)) != NULL) {\n            robj *cobj = dictGetKey(de);\n            sds channel = cobj->ptr;\n\n            if (!pat || stringmatchlen(pat, sdslen(pat),\n                                       channel, sdslen(channel),0))\n            {\n                addReplyBulk(c,cobj);\n                mblen++;\n            }\n        }\n        dictReleaseIterator(di);\n        setDeferredArrayLen(c,replylen,mblen);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"numsub\") && c->argc >= 2) {\n        /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */\n        int j;\n\n        addReplyArrayLen(c,(c->argc-2)*2);\n        for (j = 2; j < c->argc; j++) {\n            list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);\n\n            addReplyBulk(c,c->argv[j]);\n            addReplyLongLong(c,l ? listLength(l) : 0);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"numpat\") && c->argc == 2) {\n        /* PUBSUB NUMPAT */\n        addReplyLongLong(c,listLength(server.pubsub_patterns));\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/quicklist.c",
    "content": "/* quicklist.c - A doubly linked list of ziplists\n *\n * Copyright (c) 2014, Matt Stancliff <matt@genges.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 start the above copyright notice,\n *     this quicklist of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this quicklist 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 <string.h> /* for memcpy */\n#include \"quicklist.h\"\n#include \"zmalloc.h\"\n#include \"ziplist.h\"\n#include \"util.h\" /* for ll2string */\n#include \"lzf.h\"\n\n#if defined(REDIS_TEST) || defined(REDIS_TEST_VERBOSE)\n#include <stdio.h> /* for printf (debug printing), snprintf (genstr) */\n#endif\n\n#ifndef REDIS_STATIC\n#define REDIS_STATIC static\n#endif\n\n/* Optimization levels for size-based filling */\nstatic const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536};\n\n/* Maximum size in bytes of any multi-element ziplist.\n * Larger values will live in their own isolated ziplists. */\n#define SIZE_SAFETY_LIMIT 8192\n\n/* Minimum ziplist size in bytes for attempting compression. */\n#define MIN_COMPRESS_BYTES 48\n\n/* Minimum size reduction in bytes to store compressed quicklistNode data.\n * This also prevents us from storing compression if the compression\n * resulted in a larger size than the original data. */\n#define MIN_COMPRESS_IMPROVE 8\n\n/* If not verbose testing, remove all debug printing. */\n#ifndef REDIS_TEST_VERBOSE\n#define D(...)\n#else\n#define D(...)                                                                 \\\n    do {                                                                       \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(__VA_ARGS__);                                                   \\\n        printf(\"\\n\");                                                          \\\n    } while (0);\n#endif\n\n/* Bookmarks forward declarations */\n#define QL_MAX_BM ((1 << QL_BM_BITS)-1)\nquicklistBookmark *_quicklistBookmarkFindByName(quicklist *ql, const char *name);\nquicklistBookmark *_quicklistBookmarkFindByNode(quicklist *ql, quicklistNode *node);\nvoid _quicklistBookmarkDelete(quicklist *ql, quicklistBookmark *bm);\n\n/* Simple way to give quicklistEntry structs default values with one call. */\n#define initEntry(e)                                                           \\\n    do {                                                                       \\\n        (e)->zi = (e)->value = NULL;                                           \\\n        (e)->longval = -123456789;                                             \\\n        (e)->quicklist = NULL;                                                 \\\n        (e)->node = NULL;                                                      \\\n        (e)->offset = 123456789;                                               \\\n        (e)->sz = 0;                                                           \\\n    } while (0)\n\n#if __GNUC__ >= 3\n#define likely(x) __builtin_expect(!!(x), 1)\n#define unlikely(x) __builtin_expect(!!(x), 0)\n#else\n#define likely(x) (x)\n#define unlikely(x) (x)\n#endif\n\n/* Create a new quicklist.\n * Free with quicklistRelease(). */\nquicklist *quicklistCreate(void) {\n    struct quicklist *quicklist;\n\n    quicklist = zmalloc(sizeof(*quicklist));\n    quicklist->head = quicklist->tail = NULL;\n    quicklist->len = 0;\n    quicklist->count = 0;\n    quicklist->compress = 0;\n    quicklist->fill = -2;\n    quicklist->bookmark_count = 0;\n    return quicklist;\n}\n\n#define COMPRESS_MAX ((1 << QL_COMP_BITS)-1)\nvoid quicklistSetCompressDepth(quicklist *quicklist, int compress) {\n    if (compress > COMPRESS_MAX) {\n        compress = COMPRESS_MAX;\n    } else if (compress < 0) {\n        compress = 0;\n    }\n    quicklist->compress = compress;\n}\n\n#define FILL_MAX ((1 << (QL_FILL_BITS-1))-1)\nvoid quicklistSetFill(quicklist *quicklist, int fill) {\n    if (fill > FILL_MAX) {\n        fill = FILL_MAX;\n    } else if (fill < -5) {\n        fill = -5;\n    }\n    quicklist->fill = fill;\n}\n\nvoid quicklistSetOptions(quicklist *quicklist, int fill, int depth) {\n    quicklistSetFill(quicklist, fill);\n    quicklistSetCompressDepth(quicklist, depth);\n}\n\n/* Create a new quicklist with some default parameters. */\nquicklist *quicklistNew(int fill, int compress) {\n    quicklist *quicklist = quicklistCreate();\n    quicklistSetOptions(quicklist, fill, compress);\n    return quicklist;\n}\n\nREDIS_STATIC quicklistNode *quicklistCreateNode(void) {\n    quicklistNode *node;\n    node = zmalloc(sizeof(*node));\n    node->zl = NULL;\n    node->count = 0;\n    node->sz = 0;\n    node->next = node->prev = NULL;\n    node->encoding = QUICKLIST_NODE_ENCODING_RAW;\n    node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST;\n    node->recompress = 0;\n    return node;\n}\n\n/* Return cached quicklist count */\nunsigned long quicklistCount(const quicklist *ql) { return ql->count; }\n\n/* Free entire quicklist. */\nvoid quicklistRelease(quicklist *quicklist) {\n    unsigned long len;\n    quicklistNode *current, *next;\n\n    current = quicklist->head;\n    len = quicklist->len;\n    while (len--) {\n        next = current->next;\n\n        zfree(current->zl);\n        quicklist->count -= current->count;\n\n        zfree(current);\n\n        quicklist->len--;\n        current = next;\n    }\n    quicklistBookmarksClear(quicklist);\n    zfree(quicklist);\n}\n\n/* Compress the ziplist in 'node' and update encoding details.\n * Returns 1 if ziplist compressed successfully.\n * Returns 0 if compression failed or if ziplist too small to compress. */\nREDIS_STATIC int __quicklistCompressNode(quicklistNode *node) {\n#ifdef REDIS_TEST\n    node->attempted_compress = 1;\n#endif\n\n    /* Don't bother compressing small values */\n    if (node->sz < MIN_COMPRESS_BYTES)\n        return 0;\n\n    quicklistLZF *lzf = zmalloc(sizeof(*lzf) + node->sz);\n\n    /* Cancel if compression fails or doesn't compress small enough */\n    if (((lzf->sz = lzf_compress(node->zl, node->sz, lzf->compressed,\n                                 node->sz)) == 0) ||\n        lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) {\n        /* lzf_compress aborts/rejects compression if value not compressable. */\n        zfree(lzf);\n        return 0;\n    }\n    lzf = zrealloc(lzf, sizeof(*lzf) + lzf->sz);\n    zfree(node->zl);\n    node->zl = (unsigned char *)lzf;\n    node->encoding = QUICKLIST_NODE_ENCODING_LZF;\n    node->recompress = 0;\n    return 1;\n}\n\n/* Compress only uncompressed nodes. */\n#define quicklistCompressNode(_node)                                           \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_RAW) {     \\\n            __quicklistCompressNode((_node));                                  \\\n        }                                                                      \\\n    } while (0)\n\n/* Uncompress the ziplist in 'node' and update encoding details.\n * Returns 1 on successful decode, 0 on failure to decode. */\nREDIS_STATIC int __quicklistDecompressNode(quicklistNode *node) {\n#ifdef REDIS_TEST\n    node->attempted_compress = 0;\n#endif\n\n    void *decompressed = zmalloc(node->sz);\n    quicklistLZF *lzf = (quicklistLZF *)node->zl;\n    if (lzf_decompress(lzf->compressed, lzf->sz, decompressed, node->sz) == 0) {\n        /* Someone requested decompress, but we can't decompress.  Not good. */\n        zfree(decompressed);\n        return 0;\n    }\n    zfree(lzf);\n    node->zl = decompressed;\n    node->encoding = QUICKLIST_NODE_ENCODING_RAW;\n    return 1;\n}\n\n/* Decompress only compressed nodes. */\n#define quicklistDecompressNode(_node)                                         \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) {     \\\n            __quicklistDecompressNode((_node));                                \\\n        }                                                                      \\\n    } while (0)\n\n/* Force node to not be immediately re-compresable */\n#define quicklistDecompressNodeForUse(_node)                                   \\\n    do {                                                                       \\\n        if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_LZF) {     \\\n            __quicklistDecompressNode((_node));                                \\\n            (_node)->recompress = 1;                                           \\\n        }                                                                      \\\n    } while (0)\n\n/* Extract the raw LZF data from this quicklistNode.\n * Pointer to LZF data is assigned to '*data'.\n * Return value is the length of compressed LZF data. */\nsize_t quicklistGetLzf(const quicklistNode *node, void **data) {\n    quicklistLZF *lzf = (quicklistLZF *)node->zl;\n    *data = lzf->compressed;\n    return lzf->sz;\n}\n\n#define quicklistAllowsCompression(_ql) ((_ql)->compress != 0)\n\n/* Force 'quicklist' to meet compression guidelines set by compress depth.\n * The only way to guarantee interior nodes get compressed is to iterate\n * to our \"interior\" compress depth then compress the next node we find.\n * If compress depth is larger than the entire list, we return immediately. */\nREDIS_STATIC void __quicklistCompress(const quicklist *quicklist,\n                                      quicklistNode *node) {\n    /* If length is less than our compress depth (from both sides),\n     * we can't compress anything. */\n    if (!quicklistAllowsCompression(quicklist) ||\n        quicklist->len < (unsigned int)(quicklist->compress * 2))\n        return;\n\n#if 0\n    /* Optimized cases for small depth counts */\n    if (quicklist->compress == 1) {\n        quicklistNode *h = quicklist->head, *t = quicklist->tail;\n        quicklistDecompressNode(h);\n        quicklistDecompressNode(t);\n        if (h != node && t != node)\n            quicklistCompressNode(node);\n        return;\n    } else if (quicklist->compress == 2) {\n        quicklistNode *h = quicklist->head, *hn = h->next, *hnn = hn->next;\n        quicklistNode *t = quicklist->tail, *tp = t->prev, *tpp = tp->prev;\n        quicklistDecompressNode(h);\n        quicklistDecompressNode(hn);\n        quicklistDecompressNode(t);\n        quicklistDecompressNode(tp);\n        if (h != node && hn != node && t != node && tp != node) {\n            quicklistCompressNode(node);\n        }\n        if (hnn != t) {\n            quicklistCompressNode(hnn);\n        }\n        if (tpp != h) {\n            quicklistCompressNode(tpp);\n        }\n        return;\n    }\n#endif\n\n    /* Iterate until we reach compress depth for both sides of the list.a\n     * Note: because we do length checks at the *top* of this function,\n     *       we can skip explicit null checks below. Everything exists. */\n    quicklistNode *forward = quicklist->head;\n    quicklistNode *reverse = quicklist->tail;\n    int depth = 0;\n    int in_depth = 0;\n    while (depth++ < quicklist->compress) {\n        quicklistDecompressNode(forward);\n        quicklistDecompressNode(reverse);\n\n        if (forward == node || reverse == node)\n            in_depth = 1;\n\n        if (forward == reverse)\n            return;\n\n        forward = forward->next;\n        reverse = reverse->prev;\n    }\n\n    if (!in_depth)\n        quicklistCompressNode(node);\n\n    if (depth > 2) {\n        /* At this point, forward and reverse are one node beyond depth */\n        quicklistCompressNode(forward);\n        quicklistCompressNode(reverse);\n    }\n}\n\n#define quicklistCompress(_ql, _node)                                          \\\n    do {                                                                       \\\n        if ((_node)->recompress)                                               \\\n            quicklistCompressNode((_node));                                    \\\n        else                                                                   \\\n            __quicklistCompress((_ql), (_node));                               \\\n    } while (0)\n\n/* If we previously used quicklistDecompressNodeForUse(), just recompress. */\n#define quicklistRecompressOnly(_ql, _node)                                    \\\n    do {                                                                       \\\n        if ((_node)->recompress)                                               \\\n            quicklistCompressNode((_node));                                    \\\n    } while (0)\n\n/* Insert 'new_node' after 'old_node' if 'after' is 1.\n * Insert 'new_node' before 'old_node' if 'after' is 0.\n * Note: 'new_node' is *always* uncompressed, so if we assign it to\n *       head or tail, we do not need to uncompress it. */\nREDIS_STATIC void __quicklistInsertNode(quicklist *quicklist,\n                                        quicklistNode *old_node,\n                                        quicklistNode *new_node, int after) {\n    if (after) {\n        new_node->prev = old_node;\n        if (old_node) {\n            new_node->next = old_node->next;\n            if (old_node->next)\n                old_node->next->prev = new_node;\n            old_node->next = new_node;\n        }\n        if (quicklist->tail == old_node)\n            quicklist->tail = new_node;\n    } else {\n        new_node->next = old_node;\n        if (old_node) {\n            new_node->prev = old_node->prev;\n            if (old_node->prev)\n                old_node->prev->next = new_node;\n            old_node->prev = new_node;\n        }\n        if (quicklist->head == old_node)\n            quicklist->head = new_node;\n    }\n    /* If this insert creates the only element so far, initialize head/tail. */\n    if (quicklist->len == 0) {\n        quicklist->head = quicklist->tail = new_node;\n    }\n\n    if (old_node)\n        quicklistCompress(quicklist, old_node);\n\n    quicklist->len++;\n}\n\n/* Wrappers for node inserting around existing node. */\nREDIS_STATIC void _quicklistInsertNodeBefore(quicklist *quicklist,\n                                             quicklistNode *old_node,\n                                             quicklistNode *new_node) {\n    __quicklistInsertNode(quicklist, old_node, new_node, 0);\n}\n\nREDIS_STATIC void _quicklistInsertNodeAfter(quicklist *quicklist,\n                                            quicklistNode *old_node,\n                                            quicklistNode *new_node) {\n    __quicklistInsertNode(quicklist, old_node, new_node, 1);\n}\n\nREDIS_STATIC int\n_quicklistNodeSizeMeetsOptimizationRequirement(const size_t sz,\n                                               const int fill) {\n    if (fill >= 0)\n        return 0;\n\n    size_t offset = (-fill) - 1;\n    if (offset < (sizeof(optimization_level) / sizeof(*optimization_level))) {\n        if (sz <= optimization_level[offset]) {\n            return 1;\n        } else {\n            return 0;\n        }\n    } else {\n        return 0;\n    }\n}\n\n#define sizeMeetsSafetyLimit(sz) ((sz) <= SIZE_SAFETY_LIMIT)\n\nREDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node,\n                                           const int fill, const size_t sz) {\n    if (unlikely(!node))\n        return 0;\n\n    int ziplist_overhead;\n    /* size of previous offset */\n    if (sz < 254)\n        ziplist_overhead = 1;\n    else\n        ziplist_overhead = 5;\n\n    /* size of forward offset */\n    if (sz < 64)\n        ziplist_overhead += 1;\n    else if (likely(sz < 16384))\n        ziplist_overhead += 2;\n    else\n        ziplist_overhead += 5;\n\n    /* new_sz overestimates if 'sz' encodes to an integer type */\n    unsigned int new_sz = node->sz + sz + ziplist_overhead;\n    if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill)))\n        return 1;\n    else if (!sizeMeetsSafetyLimit(new_sz))\n        return 0;\n    else if ((int)node->count < fill)\n        return 1;\n    else\n        return 0;\n}\n\nREDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a,\n                                          const quicklistNode *b,\n                                          const int fill) {\n    if (!a || !b)\n        return 0;\n\n    /* approximate merged ziplist size (- 11 to remove one ziplist\n     * header/trailer) */\n    unsigned int merge_sz = a->sz + b->sz - 11;\n    if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill)))\n        return 1;\n    else if (!sizeMeetsSafetyLimit(merge_sz))\n        return 0;\n    else if ((int)(a->count + b->count) <= fill)\n        return 1;\n    else\n        return 0;\n}\n\n#define quicklistNodeUpdateSz(node)                                            \\\n    do {                                                                       \\\n        (node)->sz = ziplistBlobLen((node)->zl);                               \\\n    } while (0)\n\n/* Add new entry to head node of quicklist.\n *\n * Returns 0 if used existing head.\n * Returns 1 if new head created. */\nint quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {\n    quicklistNode *orig_head = quicklist->head;\n    if (likely(\n            _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {\n        quicklist->head->zl =\n            ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);\n        quicklistNodeUpdateSz(quicklist->head);\n    } else {\n        quicklistNode *node = quicklistCreateNode();\n        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n\n        quicklistNodeUpdateSz(node);\n        _quicklistInsertNodeBefore(quicklist, quicklist->head, node);\n    }\n    quicklist->count++;\n    quicklist->head->count++;\n    return (orig_head != quicklist->head);\n}\n\n/* Add new entry to tail node of quicklist.\n *\n * Returns 0 if used existing tail.\n * Returns 1 if new tail created. */\nint quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {\n    quicklistNode *orig_tail = quicklist->tail;\n    if (likely(\n            _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {\n        quicklist->tail->zl =\n            ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);\n        quicklistNodeUpdateSz(quicklist->tail);\n    } else {\n        quicklistNode *node = quicklistCreateNode();\n        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);\n\n        quicklistNodeUpdateSz(node);\n        _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);\n    }\n    quicklist->count++;\n    quicklist->tail->count++;\n    return (orig_tail != quicklist->tail);\n}\n\n/* Create new node consisting of a pre-formed ziplist.\n * Used for loading RDBs where entire ziplists have been stored\n * to be retrieved later. */\nvoid quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl) {\n    quicklistNode *node = quicklistCreateNode();\n\n    node->zl = zl;\n    node->count = ziplistLen(node->zl);\n    node->sz = ziplistBlobLen(zl);\n\n    _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);\n    quicklist->count += node->count;\n}\n\n/* Append all values of ziplist 'zl' individually into 'quicklist'.\n *\n * This allows us to restore old RDB ziplists into new quicklists\n * with smaller ziplist sizes than the saved RDB ziplist.\n *\n * Returns 'quicklist' argument. Frees passed-in ziplist 'zl' */\nquicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,\n                                            unsigned char *zl) {\n    unsigned char *value;\n    unsigned int sz;\n    long long longval;\n    char longstr[32] = {0};\n\n    unsigned char *p = ziplistIndex(zl, 0);\n    while (ziplistGet(p, &value, &sz, &longval)) {\n        if (!value) {\n            /* Write the longval as a string so we can re-add it */\n            sz = ll2string(longstr, sizeof(longstr), longval);\n            value = (unsigned char *)longstr;\n        }\n        quicklistPushTail(quicklist, value, sz);\n        p = ziplistNext(zl, p);\n    }\n    zfree(zl);\n    return quicklist;\n}\n\n/* Create new (potentially multi-node) quicklist from a single existing ziplist.\n *\n * Returns new quicklist.  Frees passed-in ziplist 'zl'. */\nquicklist *quicklistCreateFromZiplist(int fill, int compress,\n                                      unsigned char *zl) {\n    return quicklistAppendValuesFromZiplist(quicklistNew(fill, compress), zl);\n}\n\n#define quicklistDeleteIfEmpty(ql, n)                                          \\\n    do {                                                                       \\\n        if ((n)->count == 0) {                                                 \\\n            __quicklistDelNode((ql), (n));                                     \\\n            (n) = NULL;                                                        \\\n        }                                                                      \\\n    } while (0)\n\nREDIS_STATIC void __quicklistDelNode(quicklist *quicklist,\n                                     quicklistNode *node) {\n    /* Update the bookmark if any */\n    quicklistBookmark *bm = _quicklistBookmarkFindByNode(quicklist, node);\n    if (bm) {\n        bm->node = node->next;\n        /* if the bookmark was to the last node, delete it. */\n        if (!bm->node)\n            _quicklistBookmarkDelete(quicklist, bm);\n    }\n\n    if (node->next)\n        node->next->prev = node->prev;\n    if (node->prev)\n        node->prev->next = node->next;\n\n    if (node == quicklist->tail) {\n        quicklist->tail = node->prev;\n    }\n\n    if (node == quicklist->head) {\n        quicklist->head = node->next;\n    }\n\n    /* If we deleted a node within our compress depth, we\n     * now have compressed nodes needing to be decompressed. */\n    __quicklistCompress(quicklist, NULL);\n\n    quicklist->count -= node->count;\n\n    zfree(node->zl);\n    zfree(node);\n    quicklist->len--;\n}\n\n/* Delete one entry from list given the node for the entry and a pointer\n * to the entry in the node.\n *\n * Note: quicklistDelIndex() *requires* uncompressed nodes because you\n *       already had to get *p from an uncompressed node somewhere.\n *\n * Returns 1 if the entire node was deleted, 0 if node still exists.\n * Also updates in/out param 'p' with the next offset in the ziplist. */\nREDIS_STATIC int quicklistDelIndex(quicklist *quicklist, quicklistNode *node,\n                                   unsigned char **p) {\n    int gone = 0;\n\n    node->zl = ziplistDelete(node->zl, p);\n    node->count--;\n    if (node->count == 0) {\n        gone = 1;\n        __quicklistDelNode(quicklist, node);\n    } else {\n        quicklistNodeUpdateSz(node);\n    }\n    quicklist->count--;\n    /* If we deleted the node, the original node is no longer valid */\n    return gone ? 1 : 0;\n}\n\n/* Delete one element represented by 'entry'\n *\n * 'entry' stores enough metadata to delete the proper position in\n * the correct ziplist in the correct quicklist node. */\nvoid quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry) {\n    quicklistNode *prev = entry->node->prev;\n    quicklistNode *next = entry->node->next;\n    int deleted_node = quicklistDelIndex((quicklist *)entry->quicklist,\n                                         entry->node, &entry->zi);\n\n    /* after delete, the zi is now invalid for any future usage. */\n    iter->zi = NULL;\n\n    /* If current node is deleted, we must update iterator node and offset. */\n    if (deleted_node) {\n        if (iter->direction == AL_START_HEAD) {\n            iter->current = next;\n            iter->offset = 0;\n        } else if (iter->direction == AL_START_TAIL) {\n            iter->current = prev;\n            iter->offset = -1;\n        }\n    }\n    /* else if (!deleted_node), no changes needed.\n     * we already reset iter->zi above, and the existing iter->offset\n     * doesn't move again because:\n     *   - [1, 2, 3] => delete offset 1 => [1, 3]: next element still offset 1\n     *   - [1, 2, 3] => delete offset 0 => [2, 3]: next element still offset 0\n     *  if we deleted the last element at offet N and now\n     *  length of this ziplist is N-1, the next call into\n     *  quicklistNext() will jump to the next node. */\n}\n\n/* Replace quicklist entry at offset 'index' by 'data' with length 'sz'.\n *\n * Returns 1 if replace happened.\n * Returns 0 if replace failed and no changes happened. */\nint quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,\n                            int sz) {\n    quicklistEntry entry;\n    if (likely(quicklistIndex(quicklist, index, &entry))) {\n        /* quicklistIndex provides an uncompressed node */\n        entry.node->zl = ziplistDelete(entry.node->zl, &entry.zi);\n        entry.node->zl = ziplistInsert(entry.node->zl, entry.zi, data, sz);\n        quicklistNodeUpdateSz(entry.node);\n        quicklistCompress(quicklist, entry.node);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Given two nodes, try to merge their ziplists.\n *\n * This helps us not have a quicklist with 3 element ziplists if\n * our fill factor can handle much higher levels.\n *\n * Note: 'a' must be to the LEFT of 'b'.\n *\n * After calling this function, both 'a' and 'b' should be considered\n * unusable.  The return value from this function must be used\n * instead of re-using any of the quicklistNode input arguments.\n *\n * Returns the input node picked to merge against or NULL if\n * merging was not possible. */\nREDIS_STATIC quicklistNode *_quicklistZiplistMerge(quicklist *quicklist,\n                                                   quicklistNode *a,\n                                                   quicklistNode *b) {\n    D(\"Requested merge (a,b) (%u, %u)\", a->count, b->count);\n\n    quicklistDecompressNode(a);\n    quicklistDecompressNode(b);\n    if ((ziplistMerge(&a->zl, &b->zl))) {\n        /* We merged ziplists! Now remove the unused quicklistNode. */\n        quicklistNode *keep = NULL, *nokeep = NULL;\n        if (!a->zl) {\n            nokeep = a;\n            keep = b;\n        } else if (!b->zl) {\n            nokeep = b;\n            keep = a;\n        }\n        keep->count = ziplistLen(keep->zl);\n        quicklistNodeUpdateSz(keep);\n\n        nokeep->count = 0;\n        __quicklistDelNode(quicklist, nokeep);\n        quicklistCompress(quicklist, keep);\n        return keep;\n    } else {\n        /* else, the merge returned NULL and nothing changed. */\n        return NULL;\n    }\n}\n\n/* Attempt to merge ziplists within two nodes on either side of 'center'.\n *\n * We attempt to merge:\n *   - (center->prev->prev, center->prev)\n *   - (center->next, center->next->next)\n *   - (center->prev, center)\n *   - (center, center->next)\n */\nREDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist,\n                                       quicklistNode *center) {\n    int fill = quicklist->fill;\n    quicklistNode *prev, *prev_prev, *next, *next_next, *target;\n    prev = prev_prev = next = next_next = target = NULL;\n\n    if (center->prev) {\n        prev = center->prev;\n        if (center->prev->prev)\n            prev_prev = center->prev->prev;\n    }\n\n    if (center->next) {\n        next = center->next;\n        if (center->next->next)\n            next_next = center->next->next;\n    }\n\n    /* Try to merge prev_prev and prev */\n    if (_quicklistNodeAllowMerge(prev, prev_prev, fill)) {\n        _quicklistZiplistMerge(quicklist, prev_prev, prev);\n        prev_prev = prev = NULL; /* they could have moved, invalidate them. */\n    }\n\n    /* Try to merge next and next_next */\n    if (_quicklistNodeAllowMerge(next, next_next, fill)) {\n        _quicklistZiplistMerge(quicklist, next, next_next);\n        next = next_next = NULL; /* they could have moved, invalidate them. */\n    }\n\n    /* Try to merge center node and previous node */\n    if (_quicklistNodeAllowMerge(center, center->prev, fill)) {\n        target = _quicklistZiplistMerge(quicklist, center->prev, center);\n        center = NULL; /* center could have been deleted, invalidate it. */\n    } else {\n        /* else, we didn't merge here, but target needs to be valid below. */\n        target = center;\n    }\n\n    /* Use result of center merge (or original) to merge with next node. */\n    if (_quicklistNodeAllowMerge(target, target->next, fill)) {\n        _quicklistZiplistMerge(quicklist, target, target->next);\n    }\n}\n\n/* Split 'node' into two parts, parameterized by 'offset' and 'after'.\n *\n * The 'after' argument controls which quicklistNode gets returned.\n * If 'after'==1, returned node has elements after 'offset'.\n *                input node keeps elements up to 'offset', including 'offset'.\n * If 'after'==0, returned node has elements up to 'offset', including 'offset'.\n *                input node keeps elements after 'offset'.\n *\n * If 'after'==1, returned node will have elements _after_ 'offset'.\n *                The returned node will have elements [OFFSET+1, END].\n *                The input node keeps elements [0, OFFSET].\n *\n * If 'after'==0, returned node will keep elements up to and including 'offset'.\n *                The returned node will have elements [0, OFFSET].\n *                The input node keeps elements [OFFSET+1, END].\n *\n * The input node keeps all elements not taken by the returned node.\n *\n * Returns newly created node or NULL if split not possible. */\nREDIS_STATIC quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset,\n                                                int after) {\n    size_t zl_sz = node->sz;\n\n    quicklistNode *new_node = quicklistCreateNode();\n    new_node->zl = zmalloc(zl_sz);\n\n    /* Copy original ziplist so we can split it */\n    memcpy(new_node->zl, node->zl, zl_sz);\n\n    /* -1 here means \"continue deleting until the list ends\" */\n    int orig_start = after ? offset + 1 : 0;\n    int orig_extent = after ? -1 : offset;\n    int new_start = after ? 0 : offset;\n    int new_extent = after ? offset + 1 : -1;\n\n    D(\"After %d (%d); ranges: [%d, %d], [%d, %d]\", after, offset, orig_start,\n      orig_extent, new_start, new_extent);\n\n    node->zl = ziplistDeleteRange(node->zl, orig_start, orig_extent);\n    node->count = ziplistLen(node->zl);\n    quicklistNodeUpdateSz(node);\n\n    new_node->zl = ziplistDeleteRange(new_node->zl, new_start, new_extent);\n    new_node->count = ziplistLen(new_node->zl);\n    quicklistNodeUpdateSz(new_node);\n\n    D(\"After split lengths: orig (%d), new (%d)\", node->count, new_node->count);\n    return new_node;\n}\n\n/* Insert a new entry before or after existing entry 'entry'.\n *\n * If after==1, the new value is inserted after 'entry', otherwise\n * the new value is inserted before 'entry'. */\nREDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry,\n                                   void *value, const size_t sz, int after) {\n    int full = 0, at_tail = 0, at_head = 0, full_next = 0, full_prev = 0;\n    int fill = quicklist->fill;\n    quicklistNode *node = entry->node;\n    quicklistNode *new_node = NULL;\n\n    if (!node) {\n        /* we have no reference node, so let's create only node in the list */\n        D(\"No node given!\");\n        new_node = quicklistCreateNode();\n        new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n        __quicklistInsertNode(quicklist, NULL, new_node, after);\n        new_node->count++;\n        quicklist->count++;\n        return;\n    }\n\n    /* Populate accounting flags for easier boolean checks later */\n    if (!_quicklistNodeAllowInsert(node, fill, sz)) {\n        D(\"Current node is full with count %d with requested fill %lu\",\n          node->count, fill);\n        full = 1;\n    }\n\n    if (after && (entry->offset == node->count)) {\n        D(\"At Tail of current ziplist\");\n        at_tail = 1;\n        if (!_quicklistNodeAllowInsert(node->next, fill, sz)) {\n            D(\"Next node is full too.\");\n            full_next = 1;\n        }\n    }\n\n    if (!after && (entry->offset == 0)) {\n        D(\"At Head\");\n        at_head = 1;\n        if (!_quicklistNodeAllowInsert(node->prev, fill, sz)) {\n            D(\"Prev node is full too.\");\n            full_prev = 1;\n        }\n    }\n\n    /* Now determine where and how to insert the new element */\n    if (!full && after) {\n        D(\"Not full, inserting after current position.\");\n        quicklistDecompressNodeForUse(node);\n        unsigned char *next = ziplistNext(node->zl, entry->zi);\n        if (next == NULL) {\n            node->zl = ziplistPush(node->zl, value, sz, ZIPLIST_TAIL);\n        } else {\n            node->zl = ziplistInsert(node->zl, next, value, sz);\n        }\n        node->count++;\n        quicklistNodeUpdateSz(node);\n        quicklistRecompressOnly(quicklist, node);\n    } else if (!full && !after) {\n        D(\"Not full, inserting before current position.\");\n        quicklistDecompressNodeForUse(node);\n        node->zl = ziplistInsert(node->zl, entry->zi, value, sz);\n        node->count++;\n        quicklistNodeUpdateSz(node);\n        quicklistRecompressOnly(quicklist, node);\n    } else if (full && at_tail && node->next && !full_next && after) {\n        /* If we are: at tail, next has free space, and inserting after:\n         *   - insert entry at head of next node. */\n        D(\"Full and tail, but next isn't full; inserting next node head\");\n        new_node = node->next;\n        quicklistDecompressNodeForUse(new_node);\n        new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_HEAD);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        quicklistRecompressOnly(quicklist, new_node);\n    } else if (full && at_head && node->prev && !full_prev && !after) {\n        /* If we are: at head, previous has free space, and inserting before:\n         *   - insert entry at tail of previous node. */\n        D(\"Full and head, but prev isn't full, inserting prev node tail\");\n        new_node = node->prev;\n        quicklistDecompressNodeForUse(new_node);\n        new_node->zl = ziplistPush(new_node->zl, value, sz, ZIPLIST_TAIL);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        quicklistRecompressOnly(quicklist, new_node);\n    } else if (full && ((at_tail && node->next && full_next && after) ||\n                        (at_head && node->prev && full_prev && !after))) {\n        /* If we are: full, and our prev/next is full, then:\n         *   - create new node and attach to quicklist */\n        D(\"\\tprovisioning new node...\");\n        new_node = quicklistCreateNode();\n        new_node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        __quicklistInsertNode(quicklist, node, new_node, after);\n    } else if (full) {\n        /* else, node is full we need to split it. */\n        /* covers both after and !after cases */\n        D(\"\\tsplitting node...\");\n        quicklistDecompressNodeForUse(node);\n        new_node = _quicklistSplitNode(node, entry->offset, after);\n        new_node->zl = ziplistPush(new_node->zl, value, sz,\n                                   after ? ZIPLIST_HEAD : ZIPLIST_TAIL);\n        new_node->count++;\n        quicklistNodeUpdateSz(new_node);\n        __quicklistInsertNode(quicklist, node, new_node, after);\n        _quicklistMergeNodes(quicklist, node);\n    }\n\n    quicklist->count++;\n}\n\nvoid quicklistInsertBefore(quicklist *quicklist, quicklistEntry *entry,\n                           void *value, const size_t sz) {\n    _quicklistInsert(quicklist, entry, value, sz, 0);\n}\n\nvoid quicklistInsertAfter(quicklist *quicklist, quicklistEntry *entry,\n                          void *value, const size_t sz) {\n    _quicklistInsert(quicklist, entry, value, sz, 1);\n}\n\n/* Delete a range of elements from the quicklist.\n *\n * elements may span across multiple quicklistNodes, so we\n * have to be careful about tracking where we start and end.\n *\n * Returns 1 if entries were deleted, 0 if nothing was deleted. */\nint quicklistDelRange(quicklist *quicklist, const long start,\n                      const long count) {\n    if (count <= 0)\n        return 0;\n\n    unsigned long extent = count; /* range is inclusive of start position */\n\n    if (start >= 0 && extent > (quicklist->count - start)) {\n        /* if requesting delete more elements than exist, limit to list size. */\n        extent = quicklist->count - start;\n    } else if (start < 0 && extent > (unsigned long)(-start)) {\n        /* else, if at negative offset, limit max size to rest of list. */\n        extent = -start; /* c.f. LREM -29 29; just delete until end. */\n    }\n\n    quicklistEntry entry;\n    if (!quicklistIndex(quicklist, start, &entry))\n        return 0;\n\n    D(\"Quicklist delete request for start %ld, count %ld, extent: %ld\", start,\n      count, extent);\n    quicklistNode *node = entry.node;\n\n    /* iterate over next nodes until everything is deleted. */\n    while (extent) {\n        quicklistNode *next = node->next;\n\n        unsigned long del;\n        int delete_entire_node = 0;\n        if (entry.offset == 0 && extent >= node->count) {\n            /* If we are deleting more than the count of this node, we\n             * can just delete the entire node without ziplist math. */\n            delete_entire_node = 1;\n            del = node->count;\n        } else if (entry.offset >= 0 && extent >= node->count) {\n            /* If deleting more nodes after this one, calculate delete based\n             * on size of current node. */\n            del = node->count - entry.offset;\n        } else if (entry.offset < 0) {\n            /* If offset is negative, we are in the first run of this loop\n             * and we are deleting the entire range\n             * from this start offset to end of list.  Since the Negative\n             * offset is the number of elements until the tail of the list,\n             * just use it directly as the deletion count. */\n            del = -entry.offset;\n\n            /* If the positive offset is greater than the remaining extent,\n             * we only delete the remaining extent, not the entire offset.\n             */\n            if (del > extent)\n                del = extent;\n        } else {\n            /* else, we are deleting less than the extent of this node, so\n             * use extent directly. */\n            del = extent;\n        }\n\n        D(\"[%ld]: asking to del: %ld because offset: %d; (ENTIRE NODE: %d), \"\n          \"node count: %u\",\n          extent, del, entry.offset, delete_entire_node, node->count);\n\n        if (delete_entire_node) {\n            __quicklistDelNode(quicklist, node);\n        } else {\n            quicklistDecompressNodeForUse(node);\n            node->zl = ziplistDeleteRange(node->zl, entry.offset, del);\n            quicklistNodeUpdateSz(node);\n            node->count -= del;\n            quicklist->count -= del;\n            quicklistDeleteIfEmpty(quicklist, node);\n            if (node)\n                quicklistRecompressOnly(quicklist, node);\n        }\n\n        extent -= del;\n\n        node = next;\n\n        entry.offset = 0;\n    }\n    return 1;\n}\n\n/* Passthrough to ziplistCompare() */\nint quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len) {\n    return ziplistCompare(p1, p2, p2_len);\n}\n\n/* Returns a quicklist iterator 'iter'. After the initialization every\n * call to quicklistNext() will return the next element of the quicklist. */\nquicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction) {\n    quicklistIter *iter;\n\n    iter = zmalloc(sizeof(*iter));\n\n    if (direction == AL_START_HEAD) {\n        iter->current = quicklist->head;\n        iter->offset = 0;\n    } else if (direction == AL_START_TAIL) {\n        iter->current = quicklist->tail;\n        iter->offset = -1;\n    }\n\n    iter->direction = direction;\n    iter->quicklist = quicklist;\n\n    iter->zi = NULL;\n\n    return iter;\n}\n\n/* Initialize an iterator at a specific offset 'idx' and make the iterator\n * return nodes in 'direction' direction. */\nquicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,\n                                         const int direction,\n                                         const long long idx) {\n    quicklistEntry entry;\n\n    if (quicklistIndex(quicklist, idx, &entry)) {\n        quicklistIter *base = quicklistGetIterator(quicklist, direction);\n        base->zi = NULL;\n        base->current = entry.node;\n        base->offset = entry.offset;\n        return base;\n    } else {\n        return NULL;\n    }\n}\n\n/* Release iterator.\n * If we still have a valid current node, then re-encode current node. */\nvoid quicklistReleaseIterator(quicklistIter *iter) {\n    if (iter->current)\n        quicklistCompress(iter->quicklist, iter->current);\n\n    zfree(iter);\n}\n\n/* Get next element in iterator.\n *\n * Note: You must NOT insert into the list while iterating over it.\n * You *may* delete from the list while iterating using the\n * quicklistDelEntry() function.\n * If you insert into the quicklist while iterating, you should\n * re-create the iterator after your addition.\n *\n * iter = quicklistGetIterator(quicklist,<direction>);\n * quicklistEntry entry;\n * while (quicklistNext(iter, &entry)) {\n *     if (entry.value)\n *          [[ use entry.value with entry.sz ]]\n *     else\n *          [[ use entry.longval ]]\n * }\n *\n * Populates 'entry' with values for this iteration.\n * Returns 0 when iteration is complete or if iteration not possible.\n * If return value is 0, the contents of 'entry' are not valid.\n */\nint quicklistNext(quicklistIter *iter, quicklistEntry *entry) {\n    initEntry(entry);\n\n    if (!iter) {\n        D(\"Returning because no iter!\");\n        return 0;\n    }\n\n    entry->quicklist = iter->quicklist;\n    entry->node = iter->current;\n\n    if (!iter->current) {\n        D(\"Returning because current node is NULL\")\n        return 0;\n    }\n\n    unsigned char *(*nextFn)(unsigned char *, unsigned char *) = NULL;\n    int offset_update = 0;\n\n    if (!iter->zi) {\n        /* If !zi, use current index. */\n        quicklistDecompressNodeForUse(iter->current);\n        iter->zi = ziplistIndex(iter->current->zl, iter->offset);\n    } else {\n        /* else, use existing iterator offset and get prev/next as necessary. */\n        if (iter->direction == AL_START_HEAD) {\n            nextFn = ziplistNext;\n            offset_update = 1;\n        } else if (iter->direction == AL_START_TAIL) {\n            nextFn = ziplistPrev;\n            offset_update = -1;\n        }\n        iter->zi = nextFn(iter->current->zl, iter->zi);\n        iter->offset += offset_update;\n    }\n\n    entry->zi = iter->zi;\n    entry->offset = iter->offset;\n\n    if (iter->zi) {\n        /* Populate value from existing ziplist position */\n        ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);\n        return 1;\n    } else {\n        /* We ran out of ziplist entries.\n         * Pick next node, update offset, then re-run retrieval. */\n        quicklistCompress(iter->quicklist, iter->current);\n        if (iter->direction == AL_START_HEAD) {\n            /* Forward traversal */\n            D(\"Jumping to start of next node\");\n            iter->current = iter->current->next;\n            iter->offset = 0;\n        } else if (iter->direction == AL_START_TAIL) {\n            /* Reverse traversal */\n            D(\"Jumping to end of previous node\");\n            iter->current = iter->current->prev;\n            iter->offset = -1;\n        }\n        iter->zi = NULL;\n        return quicklistNext(iter, entry);\n    }\n}\n\n/* Duplicate the quicklist.\n * On success a copy of the original quicklist is returned.\n *\n * The original quicklist both on success or error is never modified.\n *\n * Returns newly allocated quicklist. */\nquicklist *quicklistDup(quicklist *orig) {\n    quicklist *copy;\n\n    copy = quicklistNew(orig->fill, orig->compress);\n\n    for (quicklistNode *current = orig->head; current;\n         current = current->next) {\n        quicklistNode *node = quicklistCreateNode();\n\n        if (current->encoding == QUICKLIST_NODE_ENCODING_LZF) {\n            quicklistLZF *lzf = (quicklistLZF *)current->zl;\n            size_t lzf_sz = sizeof(*lzf) + lzf->sz;\n            node->zl = zmalloc(lzf_sz);\n            memcpy(node->zl, current->zl, lzf_sz);\n        } else if (current->encoding == QUICKLIST_NODE_ENCODING_RAW) {\n            node->zl = zmalloc(current->sz);\n            memcpy(node->zl, current->zl, current->sz);\n        }\n\n        node->count = current->count;\n        copy->count += node->count;\n        node->sz = current->sz;\n        node->encoding = current->encoding;\n\n        _quicklistInsertNodeAfter(copy, copy->tail, node);\n    }\n\n    /* copy->count must equal orig->count here */\n    return copy;\n}\n\n/* Populate 'entry' with the element at the specified zero-based index\n * where 0 is the head, 1 is the element next to head\n * and so on. Negative integers are used in order to count\n * from the tail, -1 is the last element, -2 the penultimate\n * and so on. If the index is out of range 0 is returned.\n *\n * Returns 1 if element found\n * Returns 0 if element not found */\nint quicklistIndex(const quicklist *quicklist, const long long idx,\n                   quicklistEntry *entry) {\n    quicklistNode *n;\n    unsigned long long accum = 0;\n    unsigned long long index;\n    int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */\n\n    initEntry(entry);\n    entry->quicklist = quicklist;\n\n    if (!forward) {\n        index = (-idx) - 1;\n        n = quicklist->tail;\n    } else {\n        index = idx;\n        n = quicklist->head;\n    }\n\n    if (index >= quicklist->count)\n        return 0;\n\n    while (likely(n)) {\n        if ((accum + n->count) > index) {\n            break;\n        } else {\n            D(\"Skipping over (%p) %u at accum %lld\", (void *)n, n->count,\n              accum);\n            accum += n->count;\n            n = forward ? n->next : n->prev;\n        }\n    }\n\n    if (!n)\n        return 0;\n\n    D(\"Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu\", (void *)n,\n      accum, index, index - accum, (-index) - 1 + accum);\n\n    entry->node = n;\n    if (forward) {\n        /* forward = normal head-to-tail offset. */\n        entry->offset = index - accum;\n    } else {\n        /* reverse = need negative offset for tail-to-head, so undo\n         * the result of the original if (index < 0) above. */\n        entry->offset = (-index) - 1 + accum;\n    }\n\n    quicklistDecompressNodeForUse(entry->node);\n    entry->zi = ziplistIndex(entry->node->zl, entry->offset);\n    ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);\n    /* The caller will use our result, so we don't re-compress here.\n     * The caller can recompress or delete the node as needed. */\n    return 1;\n}\n\n/* Rotate quicklist by moving the tail element to the head. */\nvoid quicklistRotate(quicklist *quicklist) {\n    if (quicklist->count <= 1)\n        return;\n\n    /* First, get the tail entry */\n    unsigned char *p = ziplistIndex(quicklist->tail->zl, -1);\n    unsigned char *value;\n    long long longval;\n    unsigned int sz;\n    char longstr[32] = {0};\n    ziplistGet(p, &value, &sz, &longval);\n\n    /* If value found is NULL, then ziplistGet populated longval instead */\n    if (!value) {\n        /* Write the longval as a string so we can re-add it */\n        sz = ll2string(longstr, sizeof(longstr), longval);\n        value = (unsigned char *)longstr;\n    }\n\n    /* Add tail entry to head (must happen before tail is deleted). */\n    quicklistPushHead(quicklist, value, sz);\n\n    /* If quicklist has only one node, the head ziplist is also the\n     * tail ziplist and PushHead() could have reallocated our single ziplist,\n     * which would make our pre-existing 'p' unusable. */\n    if (quicklist->len == 1) {\n        p = ziplistIndex(quicklist->tail->zl, -1);\n    }\n\n    /* Remove tail entry. */\n    quicklistDelIndex(quicklist, quicklist->tail, &p);\n}\n\n/* pop from quicklist and return result in 'data' ptr.  Value of 'data'\n * is the return value of 'saver' function pointer if the data is NOT a number.\n *\n * If the quicklist element is a long long, then the return value is returned in\n * 'sval'.\n *\n * Return value of 0 means no elements available.\n * Return value of 1 means check 'data' and 'sval' for values.\n * If 'data' is set, use 'data' and 'sz'.  Otherwise, use 'sval'. */\nint quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,\n                       unsigned int *sz, long long *sval,\n                       void *(*saver)(unsigned char *data, unsigned int sz)) {\n    unsigned char *p;\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    int pos = (where == QUICKLIST_HEAD) ? 0 : -1;\n\n    if (quicklist->count == 0)\n        return 0;\n\n    if (data)\n        *data = NULL;\n    if (sz)\n        *sz = 0;\n    if (sval)\n        *sval = -123456789;\n\n    quicklistNode *node;\n    if (where == QUICKLIST_HEAD && quicklist->head) {\n        node = quicklist->head;\n    } else if (where == QUICKLIST_TAIL && quicklist->tail) {\n        node = quicklist->tail;\n    } else {\n        return 0;\n    }\n\n    p = ziplistIndex(node->zl, pos);\n    if (ziplistGet(p, &vstr, &vlen, &vlong)) {\n        if (vstr) {\n            if (data)\n                *data = saver(vstr, vlen);\n            if (sz)\n                *sz = vlen;\n        } else {\n            if (data)\n                *data = NULL;\n            if (sval)\n                *sval = vlong;\n        }\n        quicklistDelIndex(quicklist, node, &p);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return a malloc'd copy of data passed in */\nREDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) {\n    unsigned char *vstr;\n    if (data) {\n        vstr = zmalloc(sz);\n        memcpy(vstr, data, sz);\n        return vstr;\n    }\n    return NULL;\n}\n\n/* Default pop function\n *\n * Returns malloc'd value from quicklist */\nint quicklistPop(quicklist *quicklist, int where, unsigned char **data,\n                 unsigned int *sz, long long *slong) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    if (quicklist->count == 0)\n        return 0;\n    int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong,\n                                 _quicklistSaver);\n    if (data)\n        *data = vstr;\n    if (slong)\n        *slong = vlong;\n    if (sz)\n        *sz = vlen;\n    return ret;\n}\n\n/* Wrapper to allow argument-based switching between HEAD/TAIL pop */\nvoid quicklistPush(quicklist *quicklist, void *value, const size_t sz,\n                   int where) {\n    if (where == QUICKLIST_HEAD) {\n        quicklistPushHead(quicklist, value, sz);\n    } else if (where == QUICKLIST_TAIL) {\n        quicklistPushTail(quicklist, value, sz);\n    }\n}\n\n/* Create or update a bookmark in the list which will be updated to the next node\n * automatically when the one referenced gets deleted.\n * Returns 1 on success (creation of new bookmark or override of an existing one).\n * Returns 0 on failure (reached the maximum supported number of bookmarks).\n * NOTE: use short simple names, so that string compare on find is quick.\n * NOTE: bookmakrk creation may re-allocate the quicklist, so the input pointer\n         may change and it's the caller responsibilty to update the reference.\n */\nint quicklistBookmarkCreate(quicklist **ql_ref, const char *name, quicklistNode *node) {\n    quicklist *ql = *ql_ref;\n    if (ql->bookmark_count >= QL_MAX_BM)\n        return 0;\n    quicklistBookmark *bm = _quicklistBookmarkFindByName(ql, name);\n    if (bm) {\n        bm->node = node;\n        return 1;\n    }\n    ql = zrealloc(ql, sizeof(quicklist) + (ql->bookmark_count+1) * sizeof(quicklistBookmark));\n    *ql_ref = ql;\n    ql->bookmarks[ql->bookmark_count].node = node;\n    ql->bookmarks[ql->bookmark_count].name = zstrdup(name);\n    ql->bookmark_count++;\n    return 1;\n}\n\n/* Find the quicklist node referenced by a named bookmark.\n * When the bookmarked node is deleted the bookmark is updated to the next node,\n * and if that's the last node, the bookmark is deleted (so find returns NULL). */\nquicklistNode *quicklistBookmarkFind(quicklist *ql, const char *name) {\n    quicklistBookmark *bm = _quicklistBookmarkFindByName(ql, name);\n    if (!bm) return NULL;\n    return bm->node;\n}\n\n/* Delete a named bookmark.\n * returns 0 if bookmark was not found, and 1 if deleted.\n * Note that the bookmark memory is not freed yet, and is kept for future use. */\nint quicklistBookmarkDelete(quicklist *ql, const char *name) {\n    quicklistBookmark *bm = _quicklistBookmarkFindByName(ql, name);\n    if (!bm)\n        return 0;\n    _quicklistBookmarkDelete(ql, bm);\n    return 1;\n}\n\nquicklistBookmark *_quicklistBookmarkFindByName(quicklist *ql, const char *name) {\n    unsigned i;\n    for (i=0; i<ql->bookmark_count; i++) {\n        if (!strcmp(ql->bookmarks[i].name, name)) {\n            return &ql->bookmarks[i];\n        }\n    }\n    return NULL;\n}\n\nquicklistBookmark *_quicklistBookmarkFindByNode(quicklist *ql, quicklistNode *node) {\n    unsigned i;\n    for (i=0; i<ql->bookmark_count; i++) {\n        if (ql->bookmarks[i].node == node) {\n            return &ql->bookmarks[i];\n        }\n    }\n    return NULL;\n}\n\nvoid _quicklistBookmarkDelete(quicklist *ql, quicklistBookmark *bm) {\n    int index = bm - ql->bookmarks;\n    zfree(bm->name);\n    ql->bookmark_count--;\n    memmove(bm, bm+1, (ql->bookmark_count - index)* sizeof(*bm));\n    /* NOTE: We do not shrink (realloc) the quicklist yet (to avoid resonance,\n     * it may be re-used later (a call to realloc may NOP). */\n}\n\nvoid quicklistBookmarksClear(quicklist *ql) {\n    while (ql->bookmark_count)\n        zfree(ql->bookmarks[--ql->bookmark_count].name);\n    /* NOTE: We do not shrink (realloc) the quick list. main use case for this\n     * function is just before releasing the allocation. */\n}\n\n/* The rest of this file is test cases and test helpers. */\n#ifdef REDIS_TEST\n#include <stdint.h>\n#include <sys/time.h>\n\n#define assert(_e)                                                             \\\n    do {                                                                       \\\n        if (!(_e)) {                                                           \\\n            printf(\"\\n\\n=== ASSERTION FAILED ===\\n\");                          \\\n            printf(\"==> %s:%d '%s' is not true\\n\", __FILE__, __LINE__, #_e);   \\\n            err++;                                                             \\\n        }                                                                      \\\n    } while (0)\n\n#define yell(str, ...) printf(\"ERROR! \" str \"\\n\\n\", __VA_ARGS__)\n\n#define OK printf(\"\\tOK\\n\")\n\n#define ERROR                                                                  \\\n    do {                                                                       \\\n        printf(\"\\tERROR!\\n\");                                                  \\\n        err++;                                                                 \\\n    } while (0)\n\n#define ERR(x, ...)                                                            \\\n    do {                                                                       \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(\"ERROR! \" x \"\\n\", __VA_ARGS__);                                 \\\n        err++;                                                                 \\\n    } while (0)\n\n#define TEST(name) printf(\"test — %s\\n\", name);\n#define TEST_DESC(name, ...) printf(\"test — \" name \"\\n\", __VA_ARGS__);\n\n#define QL_TEST_VERBOSE 0\n\n#define UNUSED(x) (void)(x)\nstatic void ql_info(quicklist *ql) {\n#if QL_TEST_VERBOSE\n    printf(\"Container length: %lu\\n\", ql->len);\n    printf(\"Container size: %lu\\n\", ql->count);\n    if (ql->head)\n        printf(\"\\t(zsize head: %d)\\n\", ziplistLen(ql->head->zl));\n    if (ql->tail)\n        printf(\"\\t(zsize tail: %d)\\n\", ziplistLen(ql->tail->zl));\n    printf(\"\\n\");\n#else\n    UNUSED(ql);\n#endif\n}\n\n/* Return the UNIX time in microseconds */\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\n/* Return the UNIX time in milliseconds */\nstatic long long mstime(void) { return ustime() / 1000; }\n\n/* Iterate over an entire quicklist.\n * Print the list if 'print' == 1.\n *\n * Returns physical count of elements found by iterating over the list. */\nstatic int _itrprintr(quicklist *ql, int print, int forward) {\n    quicklistIter *iter =\n        quicklistGetIterator(ql, forward ? AL_START_HEAD : AL_START_TAIL);\n    quicklistEntry entry;\n    int i = 0;\n    int p = 0;\n    quicklistNode *prev = NULL;\n    while (quicklistNext(iter, &entry)) {\n        if (entry.node != prev) {\n            /* Count the number of list nodes too */\n            p++;\n            prev = entry.node;\n        }\n        if (print) {\n            printf(\"[%3d (%2d)]: [%.*s] (%lld)\\n\", i, p, entry.sz,\n                   (char *)entry.value, entry.longval);\n        }\n        i++;\n    }\n    quicklistReleaseIterator(iter);\n    return i;\n}\nstatic int itrprintr(quicklist *ql, int print) {\n    return _itrprintr(ql, print, 1);\n}\n\nstatic int itrprintr_rev(quicklist *ql, int print) {\n    return _itrprintr(ql, print, 0);\n}\n\n#define ql_verify(a, b, c, d, e)                                               \\\n    do {                                                                       \\\n        err += _ql_verify((a), (b), (c), (d), (e));                            \\\n    } while (0)\n\n/* Verify list metadata matches physical list contents. */\nstatic int _ql_verify(quicklist *ql, uint32_t len, uint32_t count,\n                      uint32_t head_count, uint32_t tail_count) {\n    int errors = 0;\n\n    ql_info(ql);\n    if (len != ql->len) {\n        yell(\"quicklist length wrong: expected %d, got %u\", len, ql->len);\n        errors++;\n    }\n\n    if (count != ql->count) {\n        yell(\"quicklist count wrong: expected %d, got %lu\", count, ql->count);\n        errors++;\n    }\n\n    int loopr = itrprintr(ql, 0);\n    if (loopr != (int)ql->count) {\n        yell(\"quicklist cached count not match actual count: expected %lu, got \"\n             \"%d\",\n             ql->count, loopr);\n        errors++;\n    }\n\n    int rloopr = itrprintr_rev(ql, 0);\n    if (loopr != rloopr) {\n        yell(\"quicklist has different forward count than reverse count!  \"\n             \"Forward count is %d, reverse count is %d.\",\n             loopr, rloopr);\n        errors++;\n    }\n\n    if (ql->len == 0 && !errors) {\n        OK;\n        return errors;\n    }\n\n    if (ql->head && head_count != ql->head->count &&\n        head_count != ziplistLen(ql->head->zl)) {\n        yell(\"quicklist head count wrong: expected %d, \"\n             \"got cached %d vs. actual %d\",\n             head_count, ql->head->count, ziplistLen(ql->head->zl));\n        errors++;\n    }\n\n    if (ql->tail && tail_count != ql->tail->count &&\n        tail_count != ziplistLen(ql->tail->zl)) {\n        yell(\"quicklist tail count wrong: expected %d, \"\n             \"got cached %u vs. actual %d\",\n             tail_count, ql->tail->count, ziplistLen(ql->tail->zl));\n        errors++;\n    }\n\n    if (quicklistAllowsCompression(ql)) {\n        quicklistNode *node = ql->head;\n        unsigned int low_raw = ql->compress;\n        unsigned int high_raw = ql->len - ql->compress;\n\n        for (unsigned int at = 0; at < ql->len; at++, node = node->next) {\n            if (node && (at < low_raw || at >= high_raw)) {\n                if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {\n                    yell(\"Incorrect compression: node %d is \"\n                         \"compressed at depth %d ((%u, %u); total \"\n                         \"nodes: %u; size: %u; recompress: %d)\",\n                         at, ql->compress, low_raw, high_raw, ql->len, node->sz,\n                         node->recompress);\n                    errors++;\n                }\n            } else {\n                if (node->encoding != QUICKLIST_NODE_ENCODING_LZF &&\n                    !node->attempted_compress) {\n                    yell(\"Incorrect non-compression: node %d is NOT \"\n                         \"compressed at depth %d ((%u, %u); total \"\n                         \"nodes: %u; size: %u; recompress: %d; attempted: %d)\",\n                         at, ql->compress, low_raw, high_raw, ql->len, node->sz,\n                         node->recompress, node->attempted_compress);\n                    errors++;\n                }\n            }\n        }\n    }\n\n    if (!errors)\n        OK;\n    return errors;\n}\n\n/* Generate new string concatenating integer i against string 'prefix' */\nstatic char *genstr(char *prefix, int i) {\n    static char result[64] = {0};\n    snprintf(result, sizeof(result), \"%s%d\", prefix, i);\n    return result;\n}\n\n/* main test, but callable from other files */\nint quicklistTest(int argc, char *argv[]) {\n    UNUSED(argc);\n    UNUSED(argv);\n\n    unsigned int err = 0;\n    int optimize_start =\n        -(int)(sizeof(optimization_level) / sizeof(*optimization_level));\n\n    printf(\"Starting optimization offset at: %d\\n\", optimize_start);\n\n    int options[] = {0, 1, 2, 3, 4, 5, 6, 10};\n    size_t option_count = sizeof(options) / sizeof(*options);\n    long long runtime[option_count];\n\n    for (int _i = 0; _i < (int)option_count; _i++) {\n        printf(\"Testing Option %d\\n\", options[_i]);\n        long long start = mstime();\n\n        TEST(\"create list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"add to tail of empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushTail(ql, \"hello\", 6);\n            /* 1 for head and 1 for tail because 1 node = head = tail */\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"add to head of empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"hello\", 6);\n            /* 1 for head and 1 for tail because 1 node = head = tail */\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        for (int f = optimize_start; f < 32; f++) {\n            TEST_DESC(\"add to tail 5x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 5; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i), 32);\n                if (ql->count != 5)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 1, 5, 5, 5);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 32; f++) {\n            TEST_DESC(\"add to head 5x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 5; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 32);\n                if (ql->count != 5)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 1, 5, 5, 5);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 512; f++) {\n            TEST_DESC(\"add to tail 500x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i), 64);\n                if (ql->count != 500)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 16, 500, 32, 20);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 512; f++) {\n            TEST_DESC(\"add to head 500x at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 32);\n                if (ql->count != 500)\n                    ERROR;\n                if (f == 32)\n                    ql_verify(ql, 16, 500, 20, 32);\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"rotate empty\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistRotate(ql);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        for (int f = optimize_start; f < 32; f++) {\n            TEST(\"rotate one val once\") {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushHead(ql, \"hello\", 6);\n                quicklistRotate(ql);\n                /* Ignore compression verify because ziplist is\n                 * too small to compress. */\n                ql_verify(ql, 1, 1, 1, 1);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 3; f++) {\n            TEST_DESC(\"rotate 500 val 5000 times at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushHead(ql, \"900\", 3);\n                quicklistPushHead(ql, \"7000\", 4);\n                quicklistPushHead(ql, \"-1200\", 5);\n                quicklistPushHead(ql, \"42\", 2);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 64);\n                ql_info(ql);\n                for (int i = 0; i < 5000; i++) {\n                    ql_info(ql);\n                    quicklistRotate(ql);\n                }\n                if (f == 1)\n                    ql_verify(ql, 504, 504, 1, 1);\n                else if (f == 2)\n                    ql_verify(ql, 252, 504, 2, 2);\n                else if (f == 32)\n                    ql_verify(ql, 16, 504, 32, 24);\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"pop empty\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPop(ql, QUICKLIST_HEAD, NULL, NULL, NULL);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop 1 string from 1\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            char *populate = genstr(\"hello\", 331);\n            quicklistPushHead(ql, populate, 32);\n            unsigned char *data;\n            unsigned int sz;\n            long long lv;\n            ql_info(ql);\n            quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n            assert(data != NULL);\n            assert(sz == 32);\n            if (strcmp(populate, (char *)data))\n                ERR(\"Pop'd value (%.*s) didn't equal original value (%s)\", sz,\n                    data, populate);\n            zfree(data);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop head 1 number from 1\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"55513\", 5);\n            unsigned char *data;\n            unsigned int sz;\n            long long lv;\n            ql_info(ql);\n            quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n            assert(data == NULL);\n            assert(lv == 55513);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop head 500 from 500\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_info(ql);\n            for (int i = 0; i < 500; i++) {\n                unsigned char *data;\n                unsigned int sz;\n                long long lv;\n                int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n                assert(ret == 1);\n                assert(data != NULL);\n                assert(sz == 32);\n                if (strcmp(genstr(\"hello\", 499 - i), (char *)data))\n                    ERR(\"Pop'd value (%.*s) didn't equal original value (%s)\",\n                        sz, data, genstr(\"hello\", 499 - i));\n                zfree(data);\n            }\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"pop head 5000 from 500\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            for (int i = 0; i < 5000; i++) {\n                unsigned char *data;\n                unsigned int sz;\n                long long lv;\n                int ret = quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);\n                if (i < 500) {\n                    assert(ret == 1);\n                    assert(data != NULL);\n                    assert(sz == 32);\n                    if (strcmp(genstr(\"hello\", 499 - i), (char *)data))\n                        ERR(\"Pop'd value (%.*s) didn't equal original value \"\n                            \"(%s)\",\n                            sz, data, genstr(\"hello\", 499 - i));\n                    zfree(data);\n                } else {\n                    assert(ret == 0);\n                }\n            }\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"iterate forward over 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);\n            quicklistEntry entry;\n            int i = 499, count = 0;\n            while (quicklistNext(iter, &entry)) {\n                char *h = genstr(\"hello\", i);\n                if (strcmp((char *)entry.value, h))\n                    ERR(\"value [%s] didn't match [%s] at position %d\",\n                        entry.value, h, i);\n                i--;\n                count++;\n            }\n            if (count != 500)\n                ERR(\"Didn't iterate over exactly 500 elements (%d)\", i);\n            ql_verify(ql, 16, 500, 20, 32);\n            quicklistReleaseIterator(iter);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"iterate reverse over 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL);\n            quicklistEntry entry;\n            int i = 0;\n            while (quicklistNext(iter, &entry)) {\n                char *h = genstr(\"hello\", i);\n                if (strcmp((char *)entry.value, h))\n                    ERR(\"value [%s] didn't match [%s] at position %d\",\n                        entry.value, h, i);\n                i++;\n            }\n            if (i != 500)\n                ERR(\"Didn't iterate over exactly 500 elements (%d)\", i);\n            ql_verify(ql, 16, 500, 20, 32);\n            quicklistReleaseIterator(iter);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert before with 0 elements\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertBefore(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert after with 0 elements\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertAfter(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert after 1 element\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"hello\", 6);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertAfter(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 2, 2, 2);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"insert before 1 element\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, \"hello\", 6);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            quicklistInsertAfter(ql, &entry, \"abc\", 4);\n            ql_verify(ql, 1, 2, 2, 2);\n            quicklistRelease(ql);\n        }\n\n        for (int f = optimize_start; f < 12; f++) {\n            TEST_DESC(\"insert once in elements while iterating at fill %d at \"\n                      \"compress %d\\n\",\n                      f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushTail(ql, \"abc\", 3);\n                quicklistSetFill(ql, 1);\n                quicklistPushTail(ql, \"def\", 3); /* force to unique node */\n                quicklistSetFill(ql, f);\n                quicklistPushTail(ql, \"bob\", 3); /* force to reset for +3 */\n                quicklistPushTail(ql, \"foo\", 3);\n                quicklistPushTail(ql, \"zoo\", 3);\n\n                itrprintr(ql, 0);\n                /* insert \"bar\" before \"bob\" while iterating over list. */\n                quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);\n                quicklistEntry entry;\n                while (quicklistNext(iter, &entry)) {\n                    if (!strncmp((char *)entry.value, \"bob\", 3)) {\n                        /* Insert as fill = 1 so it spills into new node. */\n                        quicklistInsertBefore(ql, &entry, \"bar\", 3);\n                        break; /* didn't we fix insert-while-iterating? */\n                    }\n                }\n                itrprintr(ql, 0);\n\n                /* verify results */\n                quicklistIndex(ql, 0, &entry);\n                if (strncmp((char *)entry.value, \"abc\", 3))\n                    ERR(\"Value 0 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 1, &entry);\n                if (strncmp((char *)entry.value, \"def\", 3))\n                    ERR(\"Value 1 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 2, &entry);\n                if (strncmp((char *)entry.value, \"bar\", 3))\n                    ERR(\"Value 2 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 3, &entry);\n                if (strncmp((char *)entry.value, \"bob\", 3))\n                    ERR(\"Value 3 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 4, &entry);\n                if (strncmp((char *)entry.value, \"foo\", 3))\n                    ERR(\"Value 4 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistIndex(ql, 5, &entry);\n                if (strncmp((char *)entry.value, \"zoo\", 3))\n                    ERR(\"Value 5 didn't match, instead got: %.*s\", entry.sz,\n                        entry.value);\n                quicklistReleaseIterator(iter);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 1024; f++) {\n            TEST_DESC(\n                \"insert [before] 250 new in middle of 500 elements at fill\"\n                \" %d at compress %d\",\n                f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i), 32);\n                for (int i = 0; i < 250; i++) {\n                    quicklistEntry entry;\n                    quicklistIndex(ql, 250, &entry);\n                    quicklistInsertBefore(ql, &entry, genstr(\"abc\", i), 32);\n                }\n                if (f == 32)\n                    ql_verify(ql, 25, 750, 32, 20);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 1024; f++) {\n            TEST_DESC(\"insert [after] 250 new in middle of 500 elements at \"\n                      \"fill %d at compress %d\",\n                      f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushHead(ql, genstr(\"hello\", i), 32);\n                for (int i = 0; i < 250; i++) {\n                    quicklistEntry entry;\n                    quicklistIndex(ql, 250, &entry);\n                    quicklistInsertAfter(ql, &entry, genstr(\"abc\", i), 32);\n                }\n\n                if (ql->count != 750)\n                    ERR(\"List size not 750, but rather %ld\", ql->count);\n\n                if (f == 32)\n                    ql_verify(ql, 26, 750, 20, 32);\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"duplicate empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklist *copy = quicklistDup(ql);\n            ql_verify(copy, 0, 0, 0, 0);\n            quicklistRelease(ql);\n            quicklistRelease(copy);\n        }\n\n        TEST(\"duplicate list of 1 element\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushHead(ql, genstr(\"hello\", 3), 32);\n            ql_verify(ql, 1, 1, 1, 1);\n            quicklist *copy = quicklistDup(ql);\n            ql_verify(copy, 1, 1, 1, 1);\n            quicklistRelease(ql);\n            quicklistRelease(copy);\n        }\n\n        TEST(\"duplicate list of 500\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_verify(ql, 16, 500, 20, 32);\n\n            quicklist *copy = quicklistDup(ql);\n            ql_verify(copy, 16, 500, 20, 32);\n            quicklistRelease(ql);\n            quicklistRelease(copy);\n        }\n\n        for (int f = optimize_start; f < 512; f++) {\n            TEST_DESC(\"index 1,200 from 500 list at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                quicklistIndex(ql, 1, &entry);\n                if (!strcmp((char *)entry.value, \"hello2\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistIndex(ql, 200, &entry);\n                if (!strcmp((char *)entry.value, \"hello201\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistRelease(ql);\n            }\n\n            TEST_DESC(\"index -1,-2 from 500 list at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                quicklistIndex(ql, -1, &entry);\n                if (!strcmp((char *)entry.value, \"hello500\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistIndex(ql, -2, &entry);\n                if (!strcmp((char *)entry.value, \"hello499\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistRelease(ql);\n            }\n\n            TEST_DESC(\"index -100 from 500 list at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 500; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                quicklistIndex(ql, -100, &entry);\n                if (!strcmp((char *)entry.value, \"hello401\"))\n                    OK;\n                else\n                    ERR(\"Value: %s\", entry.value);\n                quicklistRelease(ql);\n            }\n\n            TEST_DESC(\"index too big +1 from 50 list at fill %d at compress %d\",\n                      f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                for (int i = 0; i < 50; i++)\n                    quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n                quicklistEntry entry;\n                if (quicklistIndex(ql, 50, &entry))\n                    ERR(\"Index found at 50 with 50 list: %.*s\", entry.sz,\n                        entry.value);\n                else\n                    OK;\n                quicklistRelease(ql);\n            }\n        }\n\n        TEST(\"delete range empty list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistDelRange(ql, 5, 20);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete range of entire node in list of one node\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 32; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_verify(ql, 1, 32, 32, 32);\n            quicklistDelRange(ql, 0, 32);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete range of entire node with overflow counts\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            for (int i = 0; i < 32; i++)\n                quicklistPushHead(ql, genstr(\"hello\", i), 32);\n            ql_verify(ql, 1, 32, 32, 32);\n            quicklistDelRange(ql, 0, 128);\n            ql_verify(ql, 0, 0, 0, 0);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete middle 100 of 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 16, 500, 32, 20);\n            quicklistDelRange(ql, 200, 100);\n            ql_verify(ql, 14, 400, 32, 20);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete negative 1 from 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 16, 500, 32, 20);\n            quicklistDelRange(ql, -1, 1);\n            ql_verify(ql, 16, 499, 32, 19);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete negative 1 from 500 list with overflow counts\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 16, 500, 32, 20);\n            quicklistDelRange(ql, -1, 128);\n            ql_verify(ql, 16, 499, 32, 19);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete negative 100 from 500 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 500; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            quicklistDelRange(ql, -100, 100);\n            ql_verify(ql, 13, 400, 32, 16);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"delete -10 count 5 from 50 list\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            for (int i = 0; i < 50; i++)\n                quicklistPushTail(ql, genstr(\"hello\", i + 1), 32);\n            ql_verify(ql, 2, 50, 32, 18);\n            quicklistDelRange(ql, -10, 5);\n            ql_verify(ql, 2, 45, 32, 13);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"numbers only list read\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushTail(ql, \"1111\", 4);\n            quicklistPushTail(ql, \"2222\", 4);\n            quicklistPushTail(ql, \"3333\", 4);\n            quicklistPushTail(ql, \"4444\", 4);\n            ql_verify(ql, 1, 4, 4, 4);\n            quicklistEntry entry;\n            quicklistIndex(ql, 0, &entry);\n            if (entry.longval != 1111)\n                ERR(\"Not 1111, %lld\", entry.longval);\n            quicklistIndex(ql, 1, &entry);\n            if (entry.longval != 2222)\n                ERR(\"Not 2222, %lld\", entry.longval);\n            quicklistIndex(ql, 2, &entry);\n            if (entry.longval != 3333)\n                ERR(\"Not 3333, %lld\", entry.longval);\n            quicklistIndex(ql, 3, &entry);\n            if (entry.longval != 4444)\n                ERR(\"Not 4444, %lld\", entry.longval);\n            if (quicklistIndex(ql, 4, &entry))\n                ERR(\"Index past elements: %lld\", entry.longval);\n            quicklistIndex(ql, -1, &entry);\n            if (entry.longval != 4444)\n                ERR(\"Not 4444 (reverse), %lld\", entry.longval);\n            quicklistIndex(ql, -2, &entry);\n            if (entry.longval != 3333)\n                ERR(\"Not 3333 (reverse), %lld\", entry.longval);\n            quicklistIndex(ql, -3, &entry);\n            if (entry.longval != 2222)\n                ERR(\"Not 2222 (reverse), %lld\", entry.longval);\n            quicklistIndex(ql, -4, &entry);\n            if (entry.longval != 1111)\n                ERR(\"Not 1111 (reverse), %lld\", entry.longval);\n            if (quicklistIndex(ql, -5, &entry))\n                ERR(\"Index past elements (reverse), %lld\", entry.longval);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"numbers larger list read\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistSetFill(ql, 32);\n            char num[32];\n            long long nums[5000];\n            for (int i = 0; i < 5000; i++) {\n                nums[i] = -5157318210846258176 + i;\n                int sz = ll2string(num, sizeof(num), nums[i]);\n                quicklistPushTail(ql, num, sz);\n            }\n            quicklistPushTail(ql, \"xxxxxxxxxxxxxxxxxxxx\", 20);\n            quicklistEntry entry;\n            for (int i = 0; i < 5000; i++) {\n                quicklistIndex(ql, i, &entry);\n                if (entry.longval != nums[i])\n                    ERR(\"[%d] Not longval %lld but rather %lld\", i, nums[i],\n                        entry.longval);\n                entry.longval = 0xdeadbeef;\n            }\n            quicklistIndex(ql, 5000, &entry);\n            if (strncmp((char *)entry.value, \"xxxxxxxxxxxxxxxxxxxx\", 20))\n                ERR(\"String val not match: %s\", entry.value);\n            ql_verify(ql, 157, 5001, 32, 9);\n            quicklistRelease(ql);\n        }\n\n        TEST(\"numbers larger list read B\") {\n            quicklist *ql = quicklistNew(-2, options[_i]);\n            quicklistPushTail(ql, \"99\", 2);\n            quicklistPushTail(ql, \"98\", 2);\n            quicklistPushTail(ql, \"xxxxxxxxxxxxxxxxxxxx\", 20);\n            quicklistPushTail(ql, \"96\", 2);\n            quicklistPushTail(ql, \"95\", 2);\n            quicklistReplaceAtIndex(ql, 1, \"foo\", 3);\n            quicklistReplaceAtIndex(ql, -1, \"bar\", 3);\n            quicklistRelease(ql);\n            OK;\n        }\n\n        for (int f = optimize_start; f < 16; f++) {\n            TEST_DESC(\"lrem test at fill %d at compress %d\", f, options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char *words[] = {\"abc\", \"foo\", \"bar\",  \"foobar\", \"foobared\",\n                                 \"zap\", \"bar\", \"test\", \"foo\"};\n                char *result[] = {\"abc\", \"foo\",  \"foobar\", \"foobared\",\n                                  \"zap\", \"test\", \"foo\"};\n                char *resultB[] = {\"abc\",      \"foo\", \"foobar\",\n                                   \"foobared\", \"zap\", \"test\"};\n                for (int i = 0; i < 9; i++)\n                    quicklistPushTail(ql, words[i], strlen(words[i]));\n\n                /* lrem 0 bar */\n                quicklistIter *iter = quicklistGetIterator(ql, AL_START_HEAD);\n                quicklistEntry entry;\n                int i = 0;\n                while (quicklistNext(iter, &entry)) {\n                    if (quicklistCompare(entry.zi, (unsigned char *)\"bar\", 3)) {\n                        quicklistDelEntry(iter, &entry);\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                /* check result of lrem 0 bar */\n                iter = quicklistGetIterator(ql, AL_START_HEAD);\n                i = 0;\n                int ok = 1;\n                while (quicklistNext(iter, &entry)) {\n                    /* Result must be: abc, foo, foobar, foobared, zap, test,\n                     * foo */\n                    if (strncmp((char *)entry.value, result[i], entry.sz)) {\n                        ERR(\"No match at position %d, got %.*s instead of %s\",\n                            i, entry.sz, entry.value, result[i]);\n                        ok = 0;\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                quicklistPushTail(ql, \"foo\", 3);\n\n                /* lrem -2 foo */\n                iter = quicklistGetIterator(ql, AL_START_TAIL);\n                i = 0;\n                int del = 2;\n                while (quicklistNext(iter, &entry)) {\n                    if (quicklistCompare(entry.zi, (unsigned char *)\"foo\", 3)) {\n                        quicklistDelEntry(iter, &entry);\n                        del--;\n                    }\n                    if (!del)\n                        break;\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                /* check result of lrem -2 foo */\n                /* (we're ignoring the '2' part and still deleting all foo\n                 * because\n                 * we only have two foo) */\n                iter = quicklistGetIterator(ql, AL_START_TAIL);\n                i = 0;\n                size_t resB = sizeof(resultB) / sizeof(*resultB);\n                while (quicklistNext(iter, &entry)) {\n                    /* Result must be: abc, foo, foobar, foobared, zap, test,\n                     * foo */\n                    if (strncmp((char *)entry.value, resultB[resB - 1 - i],\n                                entry.sz)) {\n                        ERR(\"No match at position %d, got %.*s instead of %s\",\n                            i, entry.sz, entry.value, resultB[resB - 1 - i]);\n                        ok = 0;\n                    }\n                    i++;\n                }\n\n                quicklistReleaseIterator(iter);\n                /* final result of all tests */\n                if (ok)\n                    OK;\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 16; f++) {\n            TEST_DESC(\"iterate reverse + delete at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                quicklistPushTail(ql, \"abc\", 3);\n                quicklistPushTail(ql, \"def\", 3);\n                quicklistPushTail(ql, \"hij\", 3);\n                quicklistPushTail(ql, \"jkl\", 3);\n                quicklistPushTail(ql, \"oop\", 3);\n\n                quicklistEntry entry;\n                quicklistIter *iter = quicklistGetIterator(ql, AL_START_TAIL);\n                int i = 0;\n                while (quicklistNext(iter, &entry)) {\n                    if (quicklistCompare(entry.zi, (unsigned char *)\"hij\", 3)) {\n                        quicklistDelEntry(iter, &entry);\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n\n                if (i != 5)\n                    ERR(\"Didn't iterate 5 times, iterated %d times.\", i);\n\n                /* Check results after deletion of \"hij\" */\n                iter = quicklistGetIterator(ql, AL_START_HEAD);\n                i = 0;\n                char *vals[] = {\"abc\", \"def\", \"jkl\", \"oop\"};\n                while (quicklistNext(iter, &entry)) {\n                    if (!quicklistCompare(entry.zi, (unsigned char *)vals[i],\n                                          3)) {\n                        ERR(\"Value at %d didn't match %s\\n\", i, vals[i]);\n                    }\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 800; f++) {\n            TEST_DESC(\"iterator at index test at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 760; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n\n                quicklistEntry entry;\n                quicklistIter *iter =\n                    quicklistGetIteratorAtIdx(ql, AL_START_HEAD, 437);\n                int i = 437;\n                while (quicklistNext(iter, &entry)) {\n                    if (entry.longval != nums[i])\n                        ERR(\"Expected %lld, but got %lld\", entry.longval,\n                            nums[i]);\n                    i++;\n                }\n                quicklistReleaseIterator(iter);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test A at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 32; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 1, 32, 32, 32);\n                /* ltrim 25 53 (keep [25,32] inclusive = 7 remaining) */\n                quicklistDelRange(ql, 0, 25);\n                quicklistDelRange(ql, 0, 0);\n                quicklistEntry entry;\n                for (int i = 0; i < 7; i++) {\n                    quicklistIndex(ql, i, &entry);\n                    if (entry.longval != nums[25 + i])\n                        ERR(\"Deleted invalid range!  Expected %lld but got \"\n                            \"%lld\",\n                            entry.longval, nums[25 + i]);\n                }\n                if (f == 32)\n                    ql_verify(ql, 1, 7, 7, 7);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test B at fill %d at compress %d\", f,\n                      options[_i]) {\n                /* Force-disable compression because our 33 sequential\n                 * integers don't compress and the check always fails. */\n                quicklist *ql = quicklistNew(f, QUICKLIST_NOCOMPRESS);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 2, 33, 32, 1);\n                /* ltrim 5 16 (keep [5,16] inclusive = 12 remaining) */\n                quicklistDelRange(ql, 0, 5);\n                quicklistDelRange(ql, -16, 16);\n                if (f == 32)\n                    ql_verify(ql, 1, 12, 12, 12);\n                quicklistEntry entry;\n                quicklistIndex(ql, 0, &entry);\n                if (entry.longval != 5)\n                    ERR(\"A: longval not 5, but %lld\", entry.longval);\n                else\n                    OK;\n                quicklistIndex(ql, -1, &entry);\n                if (entry.longval != 16)\n                    ERR(\"B! got instead: %lld\", entry.longval);\n                else\n                    OK;\n                quicklistPushTail(ql, \"bobobob\", 7);\n                quicklistIndex(ql, -1, &entry);\n                if (strncmp((char *)entry.value, \"bobobob\", 7))\n                    ERR(\"Tail doesn't match bobobob, it's %.*s instead\",\n                        entry.sz, entry.value);\n                for (int i = 0; i < 12; i++) {\n                    quicklistIndex(ql, i, &entry);\n                    if (entry.longval != nums[5 + i])\n                        ERR(\"Deleted invalid range!  Expected %lld but got \"\n                            \"%lld\",\n                            entry.longval, nums[5 + i]);\n                }\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test C at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 2, 33, 32, 1);\n                /* ltrim 3 3 (keep [3,3] inclusive = 1 remaining) */\n                quicklistDelRange(ql, 0, 3);\n                quicklistDelRange(ql, -29,\n                                  4000); /* make sure not loop forever */\n                if (f == 32)\n                    ql_verify(ql, 1, 1, 1, 1);\n                quicklistEntry entry;\n                quicklistIndex(ql, 0, &entry);\n                if (entry.longval != -5157318210846258173)\n                    ERROR;\n                else\n                    OK;\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 40; f++) {\n            TEST_DESC(\"ltrim test D at fill %d at compress %d\", f,\n                      options[_i]) {\n                quicklist *ql = quicklistNew(f, options[_i]);\n                char num[32];\n                long long nums[5000];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    quicklistPushTail(ql, num, sz);\n                }\n                if (f == 32)\n                    ql_verify(ql, 2, 33, 32, 1);\n                quicklistDelRange(ql, -12, 3);\n                if (ql->count != 30)\n                    ERR(\"Didn't delete exactly three elements!  Count is: %lu\",\n                        ql->count);\n                quicklistRelease(ql);\n            }\n        }\n\n        for (int f = optimize_start; f < 72; f++) {\n            TEST_DESC(\"create quicklist from ziplist at fill %d at compress %d\",\n                      f, options[_i]) {\n                unsigned char *zl = ziplistNew();\n                long long nums[64];\n                char num[64];\n                for (int i = 0; i < 33; i++) {\n                    nums[i] = -5157318210846258176 + i;\n                    int sz = ll2string(num, sizeof(num), nums[i]);\n                    zl =\n                        ziplistPush(zl, (unsigned char *)num, sz, ZIPLIST_TAIL);\n                }\n                for (int i = 0; i < 33; i++) {\n                    zl = ziplistPush(zl, (unsigned char *)genstr(\"hello\", i),\n                                     32, ZIPLIST_TAIL);\n                }\n                quicklist *ql = quicklistCreateFromZiplist(f, options[_i], zl);\n                if (f == 1)\n                    ql_verify(ql, 66, 66, 1, 1);\n                else if (f == 32)\n                    ql_verify(ql, 3, 66, 32, 2);\n                else if (f == 66)\n                    ql_verify(ql, 1, 66, 66, 66);\n                quicklistRelease(ql);\n            }\n        }\n\n        long long stop = mstime();\n        runtime[_i] = stop - start;\n    }\n\n    /* Run a longer test of compression depth outside of primary test loop. */\n    int list_sizes[] = {250, 251, 500, 999, 1000};\n    long long start = mstime();\n    for (int list = 0; list < (int)(sizeof(list_sizes) / sizeof(*list_sizes));\n         list++) {\n        for (int f = optimize_start; f < 128; f++) {\n            for (int depth = 1; depth < 40; depth++) {\n                /* skip over many redundant test cases */\n                TEST_DESC(\"verify specific compression of interior nodes with \"\n                          \"%d list \"\n                          \"at fill %d at compress %d\",\n                          list_sizes[list], f, depth) {\n                    quicklist *ql = quicklistNew(f, depth);\n                    for (int i = 0; i < list_sizes[list]; i++) {\n                        quicklistPushTail(ql, genstr(\"hello TAIL\", i + 1), 64);\n                        quicklistPushHead(ql, genstr(\"hello HEAD\", i + 1), 64);\n                    }\n\n                    quicklistNode *node = ql->head;\n                    unsigned int low_raw = ql->compress;\n                    unsigned int high_raw = ql->len - ql->compress;\n\n                    for (unsigned int at = 0; at < ql->len;\n                         at++, node = node->next) {\n                        if (at < low_raw || at >= high_raw) {\n                            if (node->encoding != QUICKLIST_NODE_ENCODING_RAW) {\n                                ERR(\"Incorrect compression: node %d is \"\n                                    \"compressed at depth %d ((%u, %u); total \"\n                                    \"nodes: %u; size: %u)\",\n                                    at, depth, low_raw, high_raw, ql->len,\n                                    node->sz);\n                            }\n                        } else {\n                            if (node->encoding != QUICKLIST_NODE_ENCODING_LZF) {\n                                ERR(\"Incorrect non-compression: node %d is NOT \"\n                                    \"compressed at depth %d ((%u, %u); total \"\n                                    \"nodes: %u; size: %u; attempted: %d)\",\n                                    at, depth, low_raw, high_raw, ql->len,\n                                    node->sz, node->attempted_compress);\n                            }\n                        }\n                    }\n                    quicklistRelease(ql);\n                }\n            }\n        }\n    }\n    long long stop = mstime();\n\n    printf(\"\\n\");\n    for (size_t i = 0; i < option_count; i++)\n        printf(\"Test Loop %02d: %0.2f seconds.\\n\", options[i],\n               (float)runtime[i] / 1000);\n    printf(\"Compressions: %0.2f seconds.\\n\", (float)(stop - start) / 1000);\n    printf(\"\\n\");\n\n    TEST(\"bookmark get updated to next item\") {\n        quicklist *ql = quicklistNew(1, 0);\n        quicklistPushTail(ql, \"1\", 1);\n        quicklistPushTail(ql, \"2\", 1);\n        quicklistPushTail(ql, \"3\", 1);\n        quicklistPushTail(ql, \"4\", 1);\n        quicklistPushTail(ql, \"5\", 1);\n        assert(ql->len==5);\n        /* add two bookmarks, one pointing to the node before the last. */\n        assert(quicklistBookmarkCreate(&ql, \"_dummy\", ql->head->next));\n        assert(quicklistBookmarkCreate(&ql, \"_test\", ql->tail->prev));\n        /* test that the bookmark returns the right node, delete it and see that the bookmark points to the last node */\n        assert(quicklistBookmarkFind(ql, \"_test\") == ql->tail->prev);\n        assert(quicklistDelRange(ql, -2, 1));\n        assert(quicklistBookmarkFind(ql, \"_test\") == ql->tail);\n        /* delete the last node, and see that the bookmark was deleted. */\n        assert(quicklistDelRange(ql, -1, 1));\n        assert(quicklistBookmarkFind(ql, \"_test\") == NULL);\n        /* test that other bookmarks aren't affected */\n        assert(quicklistBookmarkFind(ql, \"_dummy\") == ql->head->next);\n        assert(quicklistBookmarkFind(ql, \"_missing\") == NULL);\n        assert(ql->len==3);\n        quicklistBookmarksClear(ql); /* for coverage */\n        assert(quicklistBookmarkFind(ql, \"_dummy\") == NULL);\n        quicklistRelease(ql);\n    }\n\n    TEST(\"bookmark limit\") {\n        int i;\n        quicklist *ql = quicklistNew(1, 0);\n        quicklistPushHead(ql, \"1\", 1);\n        for (i=0; i<QL_MAX_BM; i++)\n            assert(quicklistBookmarkCreate(&ql, genstr(\"\",i), ql->head));\n        /* when all bookmarks are used, creation fails */\n        assert(!quicklistBookmarkCreate(&ql, \"_test\", ql->head));\n        /* delete one and see that we can now create another */\n        assert(quicklistBookmarkDelete(ql, \"0\"));\n        assert(quicklistBookmarkCreate(&ql, \"_test\", ql->head));\n        /* delete one and see that the rest survive */\n        assert(quicklistBookmarkDelete(ql, \"_test\"));\n        for (i=1; i<QL_MAX_BM; i++)\n            assert(quicklistBookmarkFind(ql, genstr(\"\",i)) == ql->head);\n        /* make sure the deleted ones are indeed gone */\n        assert(!quicklistBookmarkFind(ql, \"0\"));\n        assert(!quicklistBookmarkFind(ql, \"_test\"));\n        quicklistRelease(ql);\n    }\n\n    if (!err)\n        printf(\"ALL TESTS PASSED!\\n\");\n    else\n        ERR(\"Sorry, not all tests passed!  In fact, %d tests failed.\", err);\n\n    return err;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/quicklist.h",
    "content": "/* quicklist.h - A generic doubly linked quicklist implementation\n *\n * Copyright (c) 2014, Matt Stancliff <matt@genges.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 quicklist of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this quicklist 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> // for UINTPTR_MAX\n\n#ifndef __QUICKLIST_H__\n#define __QUICKLIST_H__\n\n/* Node, quicklist, and Iterator are the only data structures used currently. */\n\n/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.\n * We use bit fields keep the quicklistNode at 32 bytes.\n * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).\n * encoding: 2 bits, RAW=1, LZF=2.\n * container: 2 bits, NONE=1, ZIPLIST=2.\n * recompress: 1 bit, bool, true if node is temporarry decompressed for usage.\n * attempted_compress: 1 bit, boolean, used for verifying during testing.\n * extra: 10 bits, free for future use; pads out the remainder of 32 bits */\ntypedef struct quicklistNode {\n    struct quicklistNode *prev;\n    struct quicklistNode *next;\n    unsigned char *zl;\n    unsigned int sz;             /* ziplist size in bytes */\n    unsigned int count : 16;     /* count of items in ziplist */\n    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */\n    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */\n    unsigned int recompress : 1; /* was this node previous compressed? */\n    unsigned int attempted_compress : 1; /* node can't compress; too small */\n    unsigned int extra : 10; /* more bits to steal for future usage */\n} quicklistNode;\n\n/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.\n * 'sz' is byte length of 'compressed' field.\n * 'compressed' is LZF data with total (compressed) length 'sz'\n * NOTE: uncompressed length is stored in quicklistNode->sz.\n * When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */\ntypedef struct quicklistLZF {\n    unsigned int sz; /* LZF size in bytes*/\n    char compressed[];\n} quicklistLZF;\n\n/* Bookmarks are padded with realloc at the end of of the quicklist struct.\n * They should only be used for very big lists if thousands of nodes were the\n * excess memory usage is negligible, and there's a real need to iterate on them\n * in portions.\n * When not used, they don't add any memory overhead, but when used and then\n * deleted, some overhead remains (to avoid resonance).\n * The number of bookmarks used should be kept to minimum since it also adds\n * overhead on node deletion (searching for a bookmark to update). */\ntypedef struct quicklistBookmark {\n    quicklistNode *node;\n    char *name;\n} quicklistBookmark;\n\n#if UINTPTR_MAX == 0xffffffff\n/* 32-bit */\n#   define QL_FILL_BITS 14\n#   define QL_COMP_BITS 14\n#   define QL_BM_BITS 4\n#elif UINTPTR_MAX == 0xffffffffffffffff\n/* 64-bit */\n#   define QL_FILL_BITS 16\n#   define QL_COMP_BITS 16\n#   define QL_BM_BITS 4 /* we can encode more, but we rather limit the user\n                           since they cause performance degradation. */\n#else\n#   error unknown arch bits count\n#endif\n\n/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.\n * 'count' is the number of total entries.\n * 'len' is the number of quicklist nodes.\n * 'compress' is: -1 if compression disabled, otherwise it's the number\n *                of quicklistNodes to leave uncompressed at ends of quicklist.\n * 'fill' is the user-requested (or default) fill factor.\n * 'bookmakrs are an optional feature that is used by realloc this struct,\n *      so that they don't consume memory when not used. */\ntypedef struct quicklist {\n    quicklistNode *head;\n    quicklistNode *tail;\n    unsigned long count;        /* total count of all entries in all ziplists */\n    unsigned long len;          /* number of quicklistNodes */\n    int fill : QL_FILL_BITS;              /* fill factor for individual nodes */\n    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */\n    unsigned int bookmark_count: QL_BM_BITS;\n    quicklistBookmark bookmarks[];\n} quicklist;\n\ntypedef struct quicklistIter {\n    const quicklist *quicklist;\n    quicklistNode *current;\n    unsigned char *zi;\n    long offset; /* offset in current ziplist */\n    int direction;\n} quicklistIter;\n\ntypedef struct quicklistEntry {\n    const quicklist *quicklist;\n    quicklistNode *node;\n    unsigned char *zi;\n    unsigned char *value;\n    long long longval;\n    unsigned int sz;\n    int offset;\n} quicklistEntry;\n\n#define QUICKLIST_HEAD 0\n#define QUICKLIST_TAIL -1\n\n/* quicklist node encodings */\n#define QUICKLIST_NODE_ENCODING_RAW 1\n#define QUICKLIST_NODE_ENCODING_LZF 2\n\n/* quicklist compression disable */\n#define QUICKLIST_NOCOMPRESS 0\n\n/* quicklist container formats */\n#define QUICKLIST_NODE_CONTAINER_NONE 1\n#define QUICKLIST_NODE_CONTAINER_ZIPLIST 2\n\n#define quicklistNodeIsCompressed(node)                                        \\\n    ((node)->encoding == QUICKLIST_NODE_ENCODING_LZF)\n\n/* Prototypes */\nquicklist *quicklistCreate(void);\nquicklist *quicklistNew(int fill, int compress);\nvoid quicklistSetCompressDepth(quicklist *quicklist, int depth);\nvoid quicklistSetFill(quicklist *quicklist, int fill);\nvoid quicklistSetOptions(quicklist *quicklist, int fill, int depth);\nvoid quicklistRelease(quicklist *quicklist);\nint quicklistPushHead(quicklist *quicklist, void *value, const size_t sz);\nint quicklistPushTail(quicklist *quicklist, void *value, const size_t sz);\nvoid quicklistPush(quicklist *quicklist, void *value, const size_t sz,\n                   int where);\nvoid quicklistAppendZiplist(quicklist *quicklist, unsigned char *zl);\nquicklist *quicklistAppendValuesFromZiplist(quicklist *quicklist,\n                                            unsigned char *zl);\nquicklist *quicklistCreateFromZiplist(int fill, int compress,\n                                      unsigned char *zl);\nvoid quicklistInsertAfter(quicklist *quicklist, quicklistEntry *node,\n                          void *value, const size_t sz);\nvoid quicklistInsertBefore(quicklist *quicklist, quicklistEntry *node,\n                           void *value, const size_t sz);\nvoid quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry);\nint quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,\n                            int sz);\nint quicklistDelRange(quicklist *quicklist, const long start, const long stop);\nquicklistIter *quicklistGetIterator(const quicklist *quicklist, int direction);\nquicklistIter *quicklistGetIteratorAtIdx(const quicklist *quicklist,\n                                         int direction, const long long idx);\nint quicklistNext(quicklistIter *iter, quicklistEntry *node);\nvoid quicklistReleaseIterator(quicklistIter *iter);\nquicklist *quicklistDup(quicklist *orig);\nint quicklistIndex(const quicklist *quicklist, const long long index,\n                   quicklistEntry *entry);\nvoid quicklistRewind(quicklist *quicklist, quicklistIter *li);\nvoid quicklistRewindTail(quicklist *quicklist, quicklistIter *li);\nvoid quicklistRotate(quicklist *quicklist);\nint quicklistPopCustom(quicklist *quicklist, int where, unsigned char **data,\n                       unsigned int *sz, long long *sval,\n                       void *(*saver)(unsigned char *data, unsigned int sz));\nint quicklistPop(quicklist *quicklist, int where, unsigned char **data,\n                 unsigned int *sz, long long *slong);\nunsigned long quicklistCount(const quicklist *ql);\nint quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len);\nsize_t quicklistGetLzf(const quicklistNode *node, void **data);\n\n/* bookmarks */\nint quicklistBookmarkCreate(quicklist **ql_ref, const char *name, quicklistNode *node);\nint quicklistBookmarkDelete(quicklist *ql, const char *name);\nquicklistNode *quicklistBookmarkFind(quicklist *ql, const char *name);\nvoid quicklistBookmarksClear(quicklist *ql);\n\n#ifdef REDIS_TEST\nint quicklistTest(int argc, char *argv[]);\n#endif\n\n/* Directions for iterators */\n#define AL_START_HEAD 0\n#define AL_START_TAIL 1\n\n#endif /* __QUICKLIST_H__ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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(void);\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(void) {\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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/src/rax.c",
    "content": "/* Rax -- A radix tree implementation.\n *\n * Version 1.2 -- 7 February 2019\n *\n * Copyright (c) 2017-2019, 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 <string.h>\n#include <assert.h>\n#include <stdio.h>\n#include <errno.h>\n#include <math.h>\n#include \"rax.h\"\n\n#ifndef RAX_MALLOC_INCLUDE\n#define RAX_MALLOC_INCLUDE \"rax_malloc.h\"\n#endif\n\n#include RAX_MALLOC_INCLUDE\n\n/* This is a special pointer that is guaranteed to never have the same value\n * of a radix tree node. It's used in order to report \"not found\" error without\n * requiring the function to have multiple return values. */\nvoid *raxNotFound = (void*)\"rax-not-found-pointer\";\n\n/* -------------------------------- Debugging ------------------------------ */\n\nvoid raxDebugShowNode(const char *msg, raxNode *n);\n\n/* Turn debugging messages on/off by compiling with RAX_DEBUG_MSG macro on.\n * When RAX_DEBUG_MSG is defined by default Rax operations will emit a lot\n * of debugging info to the standard output, however you can still turn\n * debugging on/off in order to enable it only when you suspect there is an\n * operation causing a bug using the function raxSetDebugMsg(). */\n#ifdef RAX_DEBUG_MSG\n#define debugf(...)                                                            \\\n    if (raxDebugMsg) {                                                         \\\n        printf(\"%s:%s:%d:\\t\", __FILE__, __FUNCTION__, __LINE__);               \\\n        printf(__VA_ARGS__);                                                   \\\n        fflush(stdout);                                                        \\\n    }\n\n#define debugnode(msg,n) raxDebugShowNode(msg,n)\n#else\n#define debugf(...)\n#define debugnode(msg,n)\n#endif\n\n/* By default log debug info if RAX_DEBUG_MSG is defined. */\nstatic int raxDebugMsg = 1;\n\n/* When debug messages are enabled, turn them on/off dynamically. By\n * default they are enabled. Set the state to 0 to disable, and 1 to\n * re-enable. */\nvoid raxSetDebugMsg(int onoff) {\n    raxDebugMsg = onoff;\n}\n\n/* ------------------------- raxStack functions --------------------------\n * The raxStack is a simple stack of pointers that is capable of switching\n * from using a stack-allocated array to dynamic heap once a given number of\n * items are reached. It is used in order to retain the list of parent nodes\n * while walking the radix tree in order to implement certain operations that\n * need to navigate the tree upward.\n * ------------------------------------------------------------------------- */\n\n/* Initialize the stack. */\nstatic inline void raxStackInit(raxStack *ts) {\n    ts->stack = ts->static_items;\n    ts->items = 0;\n    ts->maxitems = RAX_STACK_STATIC_ITEMS;\n    ts->oom = 0;\n}\n\n/* Push an item into the stack, returns 1 on success, 0 on out of memory. */\nstatic inline int raxStackPush(raxStack *ts, void *ptr) {\n    if (ts->items == ts->maxitems) {\n        if (ts->stack == ts->static_items) {\n            ts->stack = rax_malloc(sizeof(void*)*ts->maxitems*2);\n            if (ts->stack == NULL) {\n                ts->stack = ts->static_items;\n                ts->oom = 1;\n                errno = ENOMEM;\n                return 0;\n            }\n            memcpy(ts->stack,ts->static_items,sizeof(void*)*ts->maxitems);\n        } else {\n            void **newalloc = rax_realloc(ts->stack,sizeof(void*)*ts->maxitems*2);\n            if (newalloc == NULL) {\n                ts->oom = 1;\n                errno = ENOMEM;\n                return 0;\n            }\n            ts->stack = newalloc;\n        }\n        ts->maxitems *= 2;\n    }\n    ts->stack[ts->items] = ptr;\n    ts->items++;\n    return 1;\n}\n\n/* Pop an item from the stack, the function returns NULL if there are no\n * items to pop. */\nstatic inline void *raxStackPop(raxStack *ts) {\n    if (ts->items == 0) return NULL;\n    ts->items--;\n    return ts->stack[ts->items];\n}\n\n/* Return the stack item at the top of the stack without actually consuming\n * it. */\nstatic inline void *raxStackPeek(raxStack *ts) {\n    if (ts->items == 0) return NULL;\n    return ts->stack[ts->items-1];\n}\n\n/* Free the stack in case we used heap allocation. */\nstatic inline void raxStackFree(raxStack *ts) {\n    if (ts->stack != ts->static_items) rax_free(ts->stack);\n}\n\n/* ----------------------------------------------------------------------------\n * Radix tree implementation\n * --------------------------------------------------------------------------*/\n\n/* Return the padding needed in the characters section of a node having size\n * 'nodesize'. The padding is needed to store the child pointers to aligned\n * addresses. Note that we add 4 to the node size because the node has a four\n * bytes header. */\n#define raxPadding(nodesize) ((sizeof(void*)-((nodesize+4) % sizeof(void*))) & (sizeof(void*)-1))\n\n/* Return the pointer to the last child pointer in a node. For the compressed\n * nodes this is the only child pointer. */\n#define raxNodeLastChildPtr(n) ((raxNode**) ( \\\n    ((char*)(n)) + \\\n    raxNodeCurrentLength(n) - \\\n    sizeof(raxNode*) - \\\n    (((n)->iskey && !(n)->isnull) ? sizeof(void*) : 0) \\\n))\n\n/* Return the pointer to the first child pointer. */\n#define raxNodeFirstChildPtr(n) ((raxNode**) ( \\\n    (n)->data + \\\n    (n)->size + \\\n    raxPadding((n)->size)))\n\n/* Return the current total size of the node. Note that the second line\n * computes the padding after the string of characters, needed in order to\n * save pointers to aligned addresses. */\n#define raxNodeCurrentLength(n) ( \\\n    sizeof(raxNode)+(n)->size+ \\\n    raxPadding((n)->size)+ \\\n    ((n)->iscompr ? sizeof(raxNode*) : sizeof(raxNode*)*(n)->size)+ \\\n    (((n)->iskey && !(n)->isnull)*sizeof(void*)) \\\n)\n\n/* Allocate a new non compressed node with the specified number of children.\n * If datafiled is true, the allocation is made large enough to hold the\n * associated data pointer.\n * Returns the new node pointer. On out of memory NULL is returned. */\nraxNode *raxNewNode(size_t children, int datafield) {\n    size_t nodesize = sizeof(raxNode)+children+raxPadding(children)+\n                      sizeof(raxNode*)*children;\n    if (datafield) nodesize += sizeof(void*);\n    raxNode *node = rax_malloc(nodesize);\n    if (node == NULL) return NULL;\n    node->iskey = 0;\n    node->isnull = 0;\n    node->iscompr = 0;\n    node->size = children;\n    return node;\n}\n\n/* Allocate a new rax and return its pointer. On out of memory the function\n * returns NULL. */\nrax *raxNew(void) {\n    rax *rax = rax_malloc(sizeof(*rax));\n    if (rax == NULL) return NULL;\n    rax->numele = 0;\n    rax->numnodes = 1;\n    rax->head = raxNewNode(0,0);\n    if (rax->head == NULL) {\n        rax_free(rax);\n        return NULL;\n    } else {\n        return rax;\n    }\n}\n\n/* realloc the node to make room for auxiliary data in order\n * to store an item in that node. On out of memory NULL is returned. */\nraxNode *raxReallocForData(raxNode *n, void *data) {\n    if (data == NULL) return n; /* No reallocation needed, setting isnull=1 */\n    size_t curlen = raxNodeCurrentLength(n);\n    return rax_realloc(n,curlen+sizeof(void*));\n}\n\n/* Set the node auxiliary data to the specified pointer. */\nvoid raxSetData(raxNode *n, void *data) {\n    n->iskey = 1;\n    if (data != NULL) {\n        n->isnull = 0;\n        void **ndata = (void**)\n            ((char*)n+raxNodeCurrentLength(n)-sizeof(void*));\n        memcpy(ndata,&data,sizeof(data));\n    } else {\n        n->isnull = 1;\n    }\n}\n\n/* Get the node auxiliary data. */\nvoid *raxGetData(raxNode *n) {\n    if (n->isnull) return NULL;\n    void **ndata =(void**)((char*)n+raxNodeCurrentLength(n)-sizeof(void*));\n    void *data;\n    memcpy(&data,ndata,sizeof(data));\n    return data;\n}\n\n/* Add a new child to the node 'n' representing the character 'c' and return\n * its new pointer, as well as the child pointer by reference. Additionally\n * '***parentlink' is populated with the raxNode pointer-to-pointer of where\n * the new child was stored, which is useful for the caller to replace the\n * child pointer if it gets reallocated.\n *\n * On success the new parent node pointer is returned (it may change because\n * of the realloc, so the caller should discard 'n' and use the new value).\n * On out of memory NULL is returned, and the old node is still valid. */\nraxNode *raxAddChild(raxNode *n, unsigned char c, raxNode **childptr, raxNode ***parentlink) {\n    assert(n->iscompr == 0);\n\n    size_t curlen = raxNodeCurrentLength(n);\n    n->size++;\n    size_t newlen = raxNodeCurrentLength(n);\n    n->size--; /* For now restore the orignal size. We'll update it only on\n                  success at the end. */\n\n    /* Alloc the new child we will link to 'n'. */\n    raxNode *child = raxNewNode(0,0);\n    if (child == NULL) return NULL;\n\n    /* Make space in the original node. */\n    raxNode *newn = rax_realloc(n,newlen);\n    if (newn == NULL) {\n        rax_free(child);\n        return NULL;\n    }\n    n = newn;\n\n    /* After the reallocation, we have up to 8/16 (depending on the system\n     * pointer size, and the required node padding) bytes at the end, that is,\n     * the additional char in the 'data' section, plus one pointer to the new\n     * child, plus the padding needed in order to store addresses into aligned\n     * locations.\n     *\n     * So if we start with the following node, having \"abde\" edges.\n     *\n     * Note:\n     * - We assume 4 bytes pointer for simplicity.\n     * - Each space below corresponds to one byte\n     *\n     * [HDR*][abde][Aptr][Bptr][Dptr][Eptr]|AUXP|\n     *\n     * After the reallocation we need: 1 byte for the new edge character\n     * plus 4 bytes for a new child pointer (assuming 32 bit machine).\n     * However after adding 1 byte to the edge char, the header + the edge\n     * characters are no longer aligned, so we also need 3 bytes of padding.\n     * In total the reallocation will add 1+4+3 bytes = 8 bytes:\n     *\n     * (Blank bytes are represented by \".\")\n     *\n     * [HDR*][abde][Aptr][Bptr][Dptr][Eptr]|AUXP|[....][....]\n     *\n     * Let's find where to insert the new child in order to make sure\n     * it is inserted in-place lexicographically. Assuming we are adding\n     * a child \"c\" in our case pos will be = 2 after the end of the following\n     * loop. */\n    int pos;\n    for (pos = 0; pos < n->size; pos++) {\n        if (n->data[pos] > c) break;\n    }\n\n    /* Now, if present, move auxiliary data pointer at the end\n     * so that we can mess with the other data without overwriting it.\n     * We will obtain something like that:\n     *\n     * [HDR*][abde][Aptr][Bptr][Dptr][Eptr][....][....]|AUXP|\n     */\n    unsigned char *src, *dst;\n    if (n->iskey && !n->isnull) {\n        src = ((unsigned char*)n+curlen-sizeof(void*));\n        dst = ((unsigned char*)n+newlen-sizeof(void*));\n        memmove(dst,src,sizeof(void*));\n    }\n\n    /* Compute the \"shift\", that is, how many bytes we need to move the\n     * pointers section forward because of the addition of the new child\n     * byte in the string section. Note that if we had no padding, that\n     * would be always \"1\", since we are adding a single byte in the string\n     * section of the node (where now there is \"abde\" basically).\n     *\n     * However we have padding, so it could be zero, or up to 8.\n     *\n     * Another way to think at the shift is, how many bytes we need to\n     * move child pointers forward *other than* the obvious sizeof(void*)\n     * needed for the additional pointer itself. */\n    size_t shift = newlen - curlen - sizeof(void*);\n\n    /* We said we are adding a node with edge 'c'. The insertion\n     * point is between 'b' and 'd', so the 'pos' variable value is\n     * the index of the first child pointer that we need to move forward\n     * to make space for our new pointer.\n     *\n     * To start, move all the child pointers after the insertion point\n     * of shift+sizeof(pointer) bytes on the right, to obtain:\n     *\n     * [HDR*][abde][Aptr][Bptr][....][....][Dptr][Eptr]|AUXP|\n     */\n    src = n->data+n->size+\n          raxPadding(n->size)+\n          sizeof(raxNode*)*pos;\n    memmove(src+shift+sizeof(raxNode*),src,sizeof(raxNode*)*(n->size-pos));\n\n    /* Move the pointers to the left of the insertion position as well. Often\n     * we don't need to do anything if there was already some padding to use. In\n     * that case the final destination of the pointers will be the same, however\n     * in our example there was no pre-existing padding, so we added one byte\n     * plus thre bytes of padding. After the next memmove() things will look\n     * like thata:\n     *\n     * [HDR*][abde][....][Aptr][Bptr][....][Dptr][Eptr]|AUXP|\n     */\n    if (shift) {\n        src = (unsigned char*) raxNodeFirstChildPtr(n);\n        memmove(src+shift,src,sizeof(raxNode*)*pos);\n    }\n\n    /* Now make the space for the additional char in the data section,\n     * but also move the pointers before the insertion point to the right\n     * by shift bytes, in order to obtain the following:\n     *\n     * [HDR*][ab.d][e...][Aptr][Bptr][....][Dptr][Eptr]|AUXP|\n     */\n    src = n->data+pos;\n    memmove(src+1,src,n->size-pos);\n\n    /* We can now set the character and its child node pointer to get:\n     *\n     * [HDR*][abcd][e...][Aptr][Bptr][....][Dptr][Eptr]|AUXP|\n     * [HDR*][abcd][e...][Aptr][Bptr][Cptr][Dptr][Eptr]|AUXP|\n     */\n    n->data[pos] = c;\n    n->size++;\n    src = (unsigned char*) raxNodeFirstChildPtr(n);\n    raxNode **childfield = (raxNode**)(src+sizeof(raxNode*)*pos);\n    memcpy(childfield,&child,sizeof(child));\n    *childptr = child;\n    *parentlink = childfield;\n    return n;\n}\n\n/* Turn the node 'n', that must be a node without any children, into a\n * compressed node representing a set of nodes linked one after the other\n * and having exactly one child each. The node can be a key or not: this\n * property and the associated value if any will be preserved.\n *\n * The function also returns a child node, since the last node of the\n * compressed chain cannot be part of the chain: it has zero children while\n * we can only compress inner nodes with exactly one child each. */\nraxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **child) {\n    assert(n->size == 0 && n->iscompr == 0);\n    void *data = NULL; /* Initialized only to avoid warnings. */\n    size_t newsize;\n\n    debugf(\"Compress node: %.*s\\n\", (int)len,s);\n\n    /* Allocate the child to link to this node. */\n    *child = raxNewNode(0,0);\n    if (*child == NULL) return NULL;\n\n    /* Make space in the parent node. */\n    newsize = sizeof(raxNode)+len+raxPadding(len)+sizeof(raxNode*);\n    if (n->iskey) {\n        data = raxGetData(n); /* To restore it later. */\n        if (!n->isnull) newsize += sizeof(void*);\n    }\n    raxNode *newn = rax_realloc(n,newsize);\n    if (newn == NULL) {\n        rax_free(*child);\n        return NULL;\n    }\n    n = newn;\n\n    n->iscompr = 1;\n    n->size = len;\n    memcpy(n->data,s,len);\n    if (n->iskey) raxSetData(n,data);\n    raxNode **childfield = raxNodeLastChildPtr(n);\n    memcpy(childfield,child,sizeof(*child));\n    return n;\n}\n\n/* Low level function that walks the tree looking for the string\n * 's' of 'len' bytes. The function returns the number of characters\n * of the key that was possible to process: if the returned integer\n * is the same as 'len', then it means that the node corresponding to the\n * string was found (however it may not be a key in case the node->iskey is\n * zero or if simply we stopped in the middle of a compressed node, so that\n * 'splitpos' is non zero).\n *\n * Otherwise if the returned integer is not the same as 'len', there was an\n * early stop during the tree walk because of a character mismatch.\n *\n * The node where the search ended (because the full string was processed\n * or because there was an early stop) is returned by reference as\n * '*stopnode' if the passed pointer is not NULL. This node link in the\n * parent's node is returned as '*plink' if not NULL. Finally, if the\n * search stopped in a compressed node, '*splitpos' returns the index\n * inside the compressed node where the search ended. This is useful to\n * know where to split the node for insertion.\n *\n * Note that when we stop in the middle of a compressed node with\n * a perfect match, this function will return a length equal to the\n * 'len' argument (all the key matched), and will return a *splitpos which is\n * always positive (that will represent the index of the character immediately\n * *after* the last match in the current compressed node).\n *\n * When instead we stop at a compressed node and *splitpos is zero, it\n * means that the current node represents the key (that is, none of the\n * compressed node characters are needed to represent the key, just all\n * its parents nodes). */\nstatic inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) {\n    raxNode *h = rax->head;\n    raxNode **parentlink = &rax->head;\n\n    size_t i = 0; /* Position in the string. */\n    size_t j = 0; /* Position in the node children (or bytes if compressed).*/\n    while(h->size && i < len) {\n        debugnode(\"Lookup current node\",h);\n        unsigned char *v = h->data;\n\n        if (h->iscompr) {\n            for (j = 0; j < h->size && i < len; j++, i++) {\n                if (v[j] != s[i]) break;\n            }\n            if (j != h->size) break;\n        } else {\n            /* Even when h->size is large, linear scan provides good\n             * performances compared to other approaches that are in theory\n             * more sounding, like performing a binary search. */\n            for (j = 0; j < h->size; j++) {\n                if (v[j] == s[i]) break;\n            }\n            if (j == h->size) break;\n            i++;\n        }\n\n        if (ts) raxStackPush(ts,h); /* Save stack of parent nodes. */\n        raxNode **children = raxNodeFirstChildPtr(h);\n        if (h->iscompr) j = 0; /* Compressed node only child is at index 0. */\n        memcpy(&h,children+j,sizeof(h));\n        parentlink = children+j;\n        j = 0; /* If the new node is compressed and we do not\n                  iterate again (since i == l) set the split\n                  position to 0 to signal this node represents\n                  the searched key. */\n    }\n    debugnode(\"Lookup stop node is\",h);\n    if (stopnode) *stopnode = h;\n    if (plink) *plink = parentlink;\n    if (splitpos && h->iscompr) *splitpos = j;\n    return i;\n}\n\n/* Insert the element 's' of size 'len', setting as auxiliary data\n * the pointer 'data'. If the element is already present, the associated\n * data is updated (only if 'overwrite' is set to 1), and 0 is returned,\n * otherwise the element is inserted and 1 is returned. On out of memory the\n * function returns 0 as well but sets errno to ENOMEM, otherwise errno will\n * be set to 0.\n */\nint raxGenericInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old, int overwrite) {\n    size_t i;\n    int j = 0; /* Split position. If raxLowWalk() stops in a compressed\n                  node, the index 'j' represents the char we stopped within the\n                  compressed node, that is, the position where to split the\n                  node for insertion. */\n    raxNode *h, **parentlink;\n\n    debugf(\"### Insert %.*s with value %p\\n\", (int)len, s, data);\n    i = raxLowWalk(rax,s,len,&h,&parentlink,&j,NULL);\n\n    /* If i == len we walked following the whole string. If we are not\n     * in the middle of a compressed node, the string is either already\n     * inserted or this middle node is currently not a key, but can represent\n     * our key. We have just to reallocate the node and make space for the\n     * data pointer. */\n    if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) {\n        debugf(\"### Insert: node representing key exists\\n\");\n        /* Make space for the value pointer if needed. */\n        if (!h->iskey || (h->isnull && overwrite)) {\n            h = raxReallocForData(h,data);\n            if (h) memcpy(parentlink,&h,sizeof(h));\n        }\n        if (h == NULL) {\n            errno = ENOMEM;\n            return 0;\n        }\n\n        /* Update the existing key if there is already one. */\n        if (h->iskey) {\n            if (old) *old = raxGetData(h);\n            if (overwrite) raxSetData(h,data);\n            errno = 0;\n            return 0; /* Element already exists. */\n        }\n\n        /* Otherwise set the node as a key. Note that raxSetData()\n         * will set h->iskey. */\n        raxSetData(h,data);\n        rax->numele++;\n        return 1; /* Element inserted. */\n    }\n\n    /* If the node we stopped at is a compressed node, we need to\n     * split it before to continue.\n     *\n     * Splitting a compressed node have a few possible cases.\n     * Imagine that the node 'h' we are currently at is a compressed\n     * node contaning the string \"ANNIBALE\" (it means that it represents\n     * nodes A -> N -> N -> I -> B -> A -> L -> E with the only child\n     * pointer of this node pointing at the 'E' node, because remember that\n     * we have characters at the edges of the graph, not inside the nodes\n     * themselves.\n     *\n     * In order to show a real case imagine our node to also point to\n     * another compressed node, that finally points at the node without\n     * children, representing 'O':\n     *\n     *     \"ANNIBALE\" -> \"SCO\" -> []\n     *\n     * When inserting we may face the following cases. Note that all the cases\n     * require the insertion of a non compressed node with exactly two\n     * children, except for the last case which just requires splitting a\n     * compressed node.\n     *\n     * 1) Inserting \"ANNIENTARE\"\n     *\n     *               |B| -> \"ALE\" -> \"SCO\" -> []\n     *     \"ANNI\" -> |-|\n     *               |E| -> (... continue algo ...) \"NTARE\" -> []\n     *\n     * 2) Inserting \"ANNIBALI\"\n     *\n     *                  |E| -> \"SCO\" -> []\n     *     \"ANNIBAL\" -> |-|\n     *                  |I| -> (... continue algo ...) []\n     *\n     * 3) Inserting \"AGO\" (Like case 1, but set iscompr = 0 into original node)\n     *\n     *            |N| -> \"NIBALE\" -> \"SCO\" -> []\n     *     |A| -> |-|\n     *            |G| -> (... continue algo ...) |O| -> []\n     *\n     * 4) Inserting \"CIAO\"\n     *\n     *     |A| -> \"NNIBALE\" -> \"SCO\" -> []\n     *     |-|\n     *     |C| -> (... continue algo ...) \"IAO\" -> []\n     *\n     * 5) Inserting \"ANNI\"\n     *\n     *     \"ANNI\" -> \"BALE\" -> \"SCO\" -> []\n     *\n     * The final algorithm for insertion covering all the above cases is as\n     * follows.\n     *\n     * ============================= ALGO 1 =============================\n     *\n     * For the above cases 1 to 4, that is, all cases where we stopped in\n     * the middle of a compressed node for a character mismatch, do:\n     *\n     * Let $SPLITPOS be the zero-based index at which, in the\n     * compressed node array of characters, we found the mismatching\n     * character. For example if the node contains \"ANNIBALE\" and we add\n     * \"ANNIENTARE\" the $SPLITPOS is 4, that is, the index at which the\n     * mismatching character is found.\n     *\n     * 1. Save the current compressed node $NEXT pointer (the pointer to the\n     *    child element, that is always present in compressed nodes).\n     *\n     * 2. Create \"split node\" having as child the non common letter\n     *    at the compressed node. The other non common letter (at the key)\n     *    will be added later as we continue the normal insertion algorithm\n     *    at step \"6\".\n     *\n     * 3a. IF $SPLITPOS == 0:\n     *     Replace the old node with the split node, by copying the auxiliary\n     *     data if any. Fix parent's reference. Free old node eventually\n     *     (we still need its data for the next steps of the algorithm).\n     *\n     * 3b. IF $SPLITPOS != 0:\n     *     Trim the compressed node (reallocating it as well) in order to\n     *     contain $splitpos characters. Change chilid pointer in order to link\n     *     to the split node. If new compressed node len is just 1, set\n     *     iscompr to 0 (layout is the same). Fix parent's reference.\n     *\n     * 4a. IF the postfix len (the length of the remaining string of the\n     *     original compressed node after the split character) is non zero,\n     *     create a \"postfix node\". If the postfix node has just one character\n     *     set iscompr to 0, otherwise iscompr to 1. Set the postfix node\n     *     child pointer to $NEXT.\n     *\n     * 4b. IF the postfix len is zero, just use $NEXT as postfix pointer.\n     *\n     * 5. Set child[0] of split node to postfix node.\n     *\n     * 6. Set the split node as the current node, set current index at child[1]\n     *    and continue insertion algorithm as usually.\n     *\n     * ============================= ALGO 2 =============================\n     *\n     * For case 5, that is, if we stopped in the middle of a compressed\n     * node but no mismatch was found, do:\n     *\n     * Let $SPLITPOS be the zero-based index at which, in the\n     * compressed node array of characters, we stopped iterating because\n     * there were no more keys character to match. So in the example of\n     * the node \"ANNIBALE\", addig the string \"ANNI\", the $SPLITPOS is 4.\n     *\n     * 1. Save the current compressed node $NEXT pointer (the pointer to the\n     *    child element, that is always present in compressed nodes).\n     *\n     * 2. Create a \"postfix node\" containing all the characters from $SPLITPOS\n     *    to the end. Use $NEXT as the postfix node child pointer.\n     *    If the postfix node length is 1, set iscompr to 0.\n     *    Set the node as a key with the associated value of the new\n     *    inserted key.\n     *\n     * 3. Trim the current node to contain the first $SPLITPOS characters.\n     *    As usually if the new node length is just 1, set iscompr to 0.\n     *    Take the iskey / associated value as it was in the orignal node.\n     *    Fix the parent's reference.\n     *\n     * 4. Set the postfix node as the only child pointer of the trimmed\n     *    node created at step 1.\n     */\n\n    /* ------------------------- ALGORITHM 1 --------------------------- */\n    if (h->iscompr && i != len) {\n        debugf(\"ALGO 1: Stopped at compressed node %.*s (%p)\\n\",\n            h->size, h->data, (void*)h);\n        debugf(\"Still to insert: %.*s\\n\", (int)(len-i), s+i);\n        debugf(\"Splitting at %d: '%c'\\n\", j, ((char*)h->data)[j]);\n        debugf(\"Other (key) letter is '%c'\\n\", s[i]);\n\n        /* 1: Save next pointer. */\n        raxNode **childfield = raxNodeLastChildPtr(h);\n        raxNode *next;\n        memcpy(&next,childfield,sizeof(next));\n        debugf(\"Next is %p\\n\", (void*)next);\n        debugf(\"iskey %d\\n\", h->iskey);\n        if (h->iskey) {\n            debugf(\"key value is %p\\n\", raxGetData(h));\n        }\n\n        /* Set the length of the additional nodes we will need. */\n        size_t trimmedlen = j;\n        size_t postfixlen = h->size - j - 1;\n        int split_node_is_key = !trimmedlen && h->iskey && !h->isnull;\n        size_t nodesize;\n\n        /* 2: Create the split node. Also allocate the other nodes we'll need\n         *    ASAP, so that it will be simpler to handle OOM. */\n        raxNode *splitnode = raxNewNode(1, split_node_is_key);\n        raxNode *trimmed = NULL;\n        raxNode *postfix = NULL;\n\n        if (trimmedlen) {\n            nodesize = sizeof(raxNode)+trimmedlen+raxPadding(trimmedlen)+\n                       sizeof(raxNode*);\n            if (h->iskey && !h->isnull) nodesize += sizeof(void*);\n            trimmed = rax_malloc(nodesize);\n        }\n\n        if (postfixlen) {\n            nodesize = sizeof(raxNode)+postfixlen+raxPadding(postfixlen)+\n                       sizeof(raxNode*);\n            postfix = rax_malloc(nodesize);\n        }\n\n        /* OOM? Abort now that the tree is untouched. */\n        if (splitnode == NULL ||\n            (trimmedlen && trimmed == NULL) ||\n            (postfixlen && postfix == NULL))\n        {\n            rax_free(splitnode);\n            rax_free(trimmed);\n            rax_free(postfix);\n            errno = ENOMEM;\n            return 0;\n        }\n        splitnode->data[0] = h->data[j];\n\n        if (j == 0) {\n            /* 3a: Replace the old node with the split node. */\n            if (h->iskey) {\n                void *ndata = raxGetData(h);\n                raxSetData(splitnode,ndata);\n            }\n            memcpy(parentlink,&splitnode,sizeof(splitnode));\n        } else {\n            /* 3b: Trim the compressed node. */\n            trimmed->size = j;\n            memcpy(trimmed->data,h->data,j);\n            trimmed->iscompr = j > 1 ? 1 : 0;\n            trimmed->iskey = h->iskey;\n            trimmed->isnull = h->isnull;\n            if (h->iskey && !h->isnull) {\n                void *ndata = raxGetData(h);\n                raxSetData(trimmed,ndata);\n            }\n            raxNode **cp = raxNodeLastChildPtr(trimmed);\n            memcpy(cp,&splitnode,sizeof(splitnode));\n            memcpy(parentlink,&trimmed,sizeof(trimmed));\n            parentlink = cp; /* Set parentlink to splitnode parent. */\n            rax->numnodes++;\n        }\n\n        /* 4: Create the postfix node: what remains of the original\n         * compressed node after the split. */\n        if (postfixlen) {\n            /* 4a: create a postfix node. */\n            postfix->iskey = 0;\n            postfix->isnull = 0;\n            postfix->size = postfixlen;\n            postfix->iscompr = postfixlen > 1;\n            memcpy(postfix->data,h->data+j+1,postfixlen);\n            raxNode **cp = raxNodeLastChildPtr(postfix);\n            memcpy(cp,&next,sizeof(next));\n            rax->numnodes++;\n        } else {\n            /* 4b: just use next as postfix node. */\n            postfix = next;\n        }\n\n        /* 5: Set splitnode first child as the postfix node. */\n        raxNode **splitchild = raxNodeLastChildPtr(splitnode);\n        memcpy(splitchild,&postfix,sizeof(postfix));\n\n        /* 6. Continue insertion: this will cause the splitnode to\n         * get a new child (the non common character at the currently\n         * inserted key). */\n        rax_free(h);\n        h = splitnode;\n    } else if (h->iscompr && i == len) {\n    /* ------------------------- ALGORITHM 2 --------------------------- */\n        debugf(\"ALGO 2: Stopped at compressed node %.*s (%p) j = %d\\n\",\n            h->size, h->data, (void*)h, j);\n\n        /* Allocate postfix & trimmed nodes ASAP to fail for OOM gracefully. */\n        size_t postfixlen = h->size - j;\n        size_t nodesize = sizeof(raxNode)+postfixlen+raxPadding(postfixlen)+\n                          sizeof(raxNode*);\n        if (data != NULL) nodesize += sizeof(void*);\n        raxNode *postfix = rax_malloc(nodesize);\n\n        nodesize = sizeof(raxNode)+j+raxPadding(j)+sizeof(raxNode*);\n        if (h->iskey && !h->isnull) nodesize += sizeof(void*);\n        raxNode *trimmed = rax_malloc(nodesize);\n\n        if (postfix == NULL || trimmed == NULL) {\n            rax_free(postfix);\n            rax_free(trimmed);\n            errno = ENOMEM;\n            return 0;\n        }\n\n        /* 1: Save next pointer. */\n        raxNode **childfield = raxNodeLastChildPtr(h);\n        raxNode *next;\n        memcpy(&next,childfield,sizeof(next));\n\n        /* 2: Create the postfix node. */\n        postfix->size = postfixlen;\n        postfix->iscompr = postfixlen > 1;\n        postfix->iskey = 1;\n        postfix->isnull = 0;\n        memcpy(postfix->data,h->data+j,postfixlen);\n        raxSetData(postfix,data);\n        raxNode **cp = raxNodeLastChildPtr(postfix);\n        memcpy(cp,&next,sizeof(next));\n        rax->numnodes++;\n\n        /* 3: Trim the compressed node. */\n        trimmed->size = j;\n        trimmed->iscompr = j > 1;\n        trimmed->iskey = 0;\n        trimmed->isnull = 0;\n        memcpy(trimmed->data,h->data,j);\n        memcpy(parentlink,&trimmed,sizeof(trimmed));\n        if (h->iskey) {\n            void *aux = raxGetData(h);\n            raxSetData(trimmed,aux);\n        }\n\n        /* Fix the trimmed node child pointer to point to\n         * the postfix node. */\n        cp = raxNodeLastChildPtr(trimmed);\n        memcpy(cp,&postfix,sizeof(postfix));\n\n        /* Finish! We don't need to continue with the insertion\n         * algorithm for ALGO 2. The key is already inserted. */\n        rax->numele++;\n        rax_free(h);\n        return 1; /* Key inserted. */\n    }\n\n    /* We walked the radix tree as far as we could, but still there are left\n     * chars in our string. We need to insert the missing nodes. */\n    while(i < len) {\n        raxNode *child;\n\n        /* If this node is going to have a single child, and there\n         * are other characters, so that that would result in a chain\n         * of single-childed nodes, turn it into a compressed node. */\n        if (h->size == 0 && len-i > 1) {\n            debugf(\"Inserting compressed node\\n\");\n            size_t comprsize = len-i;\n            if (comprsize > RAX_NODE_MAX_SIZE)\n                comprsize = RAX_NODE_MAX_SIZE;\n            raxNode *newh = raxCompressNode(h,s+i,comprsize,&child);\n            if (newh == NULL) goto oom;\n            h = newh;\n            memcpy(parentlink,&h,sizeof(h));\n            parentlink = raxNodeLastChildPtr(h);\n            i += comprsize;\n        } else {\n            debugf(\"Inserting normal node\\n\");\n            raxNode **new_parentlink;\n            raxNode *newh = raxAddChild(h,s[i],&child,&new_parentlink);\n            if (newh == NULL) goto oom;\n            h = newh;\n            memcpy(parentlink,&h,sizeof(h));\n            parentlink = new_parentlink;\n            i++;\n        }\n        rax->numnodes++;\n        h = child;\n    }\n    raxNode *newh = raxReallocForData(h,data);\n    if (newh == NULL) goto oom;\n    h = newh;\n    if (!h->iskey) rax->numele++;\n    raxSetData(h,data);\n    memcpy(parentlink,&h,sizeof(h));\n    return 1; /* Element inserted. */\n\noom:\n    /* This code path handles out of memory after part of the sub-tree was\n     * already modified. Set the node as a key, and then remove it. However we\n     * do that only if the node is a terminal node, otherwise if the OOM\n     * happened reallocating a node in the middle, we don't need to free\n     * anything. */\n    if (h->size == 0) {\n        h->isnull = 1;\n        h->iskey = 1;\n        rax->numele++; /* Compensate the next remove. */\n        assert(raxRemove(rax,s,i,NULL) != 0);\n    }\n    errno = ENOMEM;\n    return 0;\n}\n\n/* Overwriting insert. Just a wrapper for raxGenericInsert() that will\n * update the element if there is already one for the same key. */\nint raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {\n    return raxGenericInsert(rax,s,len,data,old,1);\n}\n\n/* Non overwriting insert function: this if an element with the same key\n * exists, the value is not updated and the function returns 0.\n * This is a just a wrapper for raxGenericInsert(). */\nint raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {\n    return raxGenericInsert(rax,s,len,data,old,0);\n}\n\n/* Find a key in the rax, returns raxNotFound special void pointer value\n * if the item was not found, otherwise the value associated with the\n * item is returned. */\nvoid *raxFind(rax *rax, unsigned char *s, size_t len) {\n    raxNode *h;\n\n    debugf(\"### Lookup: %.*s\\n\", (int)len, s);\n    int splitpos = 0;\n    size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,NULL);\n    if (i != len || (h->iscompr && splitpos != 0) || !h->iskey)\n        return raxNotFound;\n    return raxGetData(h);\n}\n\n/* Return the memory address where the 'parent' node stores the specified\n * 'child' pointer, so that the caller can update the pointer with another\n * one if needed. The function assumes it will find a match, otherwise the\n * operation is an undefined behavior (it will continue scanning the\n * memory without any bound checking). */\nraxNode **raxFindParentLink(raxNode *parent, raxNode *child) {\n    raxNode **cp = raxNodeFirstChildPtr(parent);\n    raxNode *c;\n    while(1) {\n        memcpy(&c,cp,sizeof(c));\n        if (c == child) break;\n        cp++;\n    }\n    return cp;\n}\n\n/* Low level child removal from node. The new node pointer (after the child\n * removal) is returned. Note that this function does not fix the pointer\n * of the parent node in its parent, so this task is up to the caller.\n * The function never fails for out of memory. */\nraxNode *raxRemoveChild(raxNode *parent, raxNode *child) {\n    debugnode(\"raxRemoveChild before\", parent);\n    /* If parent is a compressed node (having a single child, as for definition\n     * of the data structure), the removal of the child consists into turning\n     * it into a normal node without children. */\n    if (parent->iscompr) {\n        void *data = NULL;\n        if (parent->iskey) data = raxGetData(parent);\n        parent->isnull = 0;\n        parent->iscompr = 0;\n        parent->size = 0;\n        if (parent->iskey) raxSetData(parent,data);\n        debugnode(\"raxRemoveChild after\", parent);\n        return parent;\n    }\n\n    /* Otherwise we need to scan for the child pointer and memmove()\n     * accordingly.\n     *\n     * 1. To start we seek the first element in both the children\n     *    pointers and edge bytes in the node. */\n    raxNode **cp = raxNodeFirstChildPtr(parent);\n    raxNode **c = cp;\n    unsigned char *e = parent->data;\n\n    /* 2. Search the child pointer to remove inside the array of children\n     *    pointers. */\n    while(1) {\n        raxNode *aux;\n        memcpy(&aux,c,sizeof(aux));\n        if (aux == child) break;\n        c++;\n        e++;\n    }\n\n    /* 3. Remove the edge and the pointer by memmoving the remaining children\n     *    pointer and edge bytes one position before. */\n    int taillen = parent->size - (e - parent->data) - 1;\n    debugf(\"raxRemoveChild tail len: %d\\n\", taillen);\n    memmove(e,e+1,taillen);\n\n    /* Compute the shift, that is the amount of bytes we should move our\n     * child pointers to the left, since the removal of one edge character\n     * and the corresponding padding change, may change the layout.\n     * We just check if in the old version of the node there was at the\n     * end just a single byte and all padding: in that case removing one char\n     * will remove a whole sizeof(void*) word. */\n    size_t shift = ((parent->size+4) % sizeof(void*)) == 1 ? sizeof(void*) : 0;\n\n    /* Move the children pointers before the deletion point. */\n    if (shift)\n        memmove(((char*)cp)-shift,cp,(parent->size-taillen-1)*sizeof(raxNode**));\n\n    /* Move the remaining \"tail\" pointers at the right position as well. */\n    size_t valuelen = (parent->iskey && !parent->isnull) ? sizeof(void*) : 0;\n    memmove(((char*)c)-shift,c+1,taillen*sizeof(raxNode**)+valuelen);\n\n    /* 4. Update size. */\n    parent->size--;\n\n    /* realloc the node according to the theoretical memory usage, to free\n     * data if we are over-allocating right now. */\n    raxNode *newnode = rax_realloc(parent,raxNodeCurrentLength(parent));\n    if (newnode) {\n        debugnode(\"raxRemoveChild after\", newnode);\n    }\n    /* Note: if rax_realloc() fails we just return the old address, which\n     * is valid. */\n    return newnode ? newnode : parent;\n}\n\n/* Remove the specified item. Returns 1 if the item was found and\n * deleted, 0 otherwise. */\nint raxRemove(rax *rax, unsigned char *s, size_t len, void **old) {\n    raxNode *h;\n    raxStack ts;\n\n    debugf(\"### Delete: %.*s\\n\", (int)len, s);\n    raxStackInit(&ts);\n    int splitpos = 0;\n    size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,&ts);\n    if (i != len || (h->iscompr && splitpos != 0) || !h->iskey) {\n        raxStackFree(&ts);\n        return 0;\n    }\n    if (old) *old = raxGetData(h);\n    h->iskey = 0;\n    rax->numele--;\n\n    /* If this node has no children, the deletion needs to reclaim the\n     * no longer used nodes. This is an iterative process that needs to\n     * walk the three upward, deleting all the nodes with just one child\n     * that are not keys, until the head of the rax is reached or the first\n     * node with more than one child is found. */\n\n    int trycompress = 0; /* Will be set to 1 if we should try to optimize the\n                            tree resulting from the deletion. */\n\n    if (h->size == 0) {\n        debugf(\"Key deleted in node without children. Cleanup needed.\\n\");\n        raxNode *child = NULL;\n        while(h != rax->head) {\n            child = h;\n            debugf(\"Freeing child %p [%.*s] key:%d\\n\", (void*)child,\n                (int)child->size, (char*)child->data, child->iskey);\n            rax_free(child);\n            rax->numnodes--;\n            h = raxStackPop(&ts);\n             /* If this node has more then one child, or actually holds\n              * a key, stop here. */\n            if (h->iskey || (!h->iscompr && h->size != 1)) break;\n        }\n        if (child) {\n            debugf(\"Unlinking child %p from parent %p\\n\",\n                (void*)child, (void*)h);\n            raxNode *new = raxRemoveChild(h,child);\n            if (new != h) {\n                raxNode *parent = raxStackPeek(&ts);\n                raxNode **parentlink;\n                if (parent == NULL) {\n                    parentlink = &rax->head;\n                } else {\n                    parentlink = raxFindParentLink(parent,h);\n                }\n                memcpy(parentlink,&new,sizeof(new));\n            }\n\n            /* If after the removal the node has just a single child\n             * and is not a key, we need to try to compress it. */\n            if (new->size == 1 && new->iskey == 0) {\n                trycompress = 1;\n                h = new;\n            }\n        }\n    } else if (h->size == 1) {\n        /* If the node had just one child, after the removal of the key\n         * further compression with adjacent nodes is pontentially possible. */\n        trycompress = 1;\n    }\n\n    /* Don't try node compression if our nodes pointers stack is not\n     * complete because of OOM while executing raxLowWalk() */\n    if (trycompress && ts.oom) trycompress = 0;\n\n    /* Recompression: if trycompress is true, 'h' points to a radix tree node\n     * that changed in a way that could allow to compress nodes in this\n     * sub-branch. Compressed nodes represent chains of nodes that are not\n     * keys and have a single child, so there are two deletion events that\n     * may alter the tree so that further compression is needed:\n     *\n     * 1) A node with a single child was a key and now no longer is a key.\n     * 2) A node with two children now has just one child.\n     *\n     * We try to navigate upward till there are other nodes that can be\n     * compressed, when we reach the upper node which is not a key and has\n     * a single child, we scan the chain of children to collect the\n     * compressable part of the tree, and replace the current node with the\n     * new one, fixing the child pointer to reference the first non\n     * compressable node.\n     *\n     * Example of case \"1\". A tree stores the keys \"FOO\" = 1 and\n     * \"FOOBAR\" = 2:\n     *\n     *\n     * \"FOO\" -> \"BAR\" -> [] (2)\n     *           (1)\n     *\n     * After the removal of \"FOO\" the tree can be compressed as:\n     *\n     * \"FOOBAR\" -> [] (2)\n     *\n     *\n     * Example of case \"2\". A tree stores the keys \"FOOBAR\" = 1 and\n     * \"FOOTER\" = 2:\n     *\n     *          |B| -> \"AR\" -> [] (1)\n     * \"FOO\" -> |-|\n     *          |T| -> \"ER\" -> [] (2)\n     *\n     * After the removal of \"FOOTER\" the resulting tree is:\n     *\n     * \"FOO\" -> |B| -> \"AR\" -> [] (1)\n     *\n     * That can be compressed into:\n     *\n     * \"FOOBAR\" -> [] (1)\n     */\n    if (trycompress) {\n        debugf(\"After removing %.*s:\\n\", (int)len, s);\n        debugnode(\"Compression may be needed\",h);\n        debugf(\"Seek start node\\n\");\n\n        /* Try to reach the upper node that is compressible.\n         * At the end of the loop 'h' will point to the first node we\n         * can try to compress and 'parent' to its parent. */\n        raxNode *parent;\n        while(1) {\n            parent = raxStackPop(&ts);\n            if (!parent || parent->iskey ||\n                (!parent->iscompr && parent->size != 1)) break;\n            h = parent;\n            debugnode(\"Going up to\",h);\n        }\n        raxNode *start = h; /* Compression starting node. */\n\n        /* Scan chain of nodes we can compress. */\n        size_t comprsize = h->size;\n        int nodes = 1;\n        while(h->size != 0) {\n            raxNode **cp = raxNodeLastChildPtr(h);\n            memcpy(&h,cp,sizeof(h));\n            if (h->iskey || (!h->iscompr && h->size != 1)) break;\n            /* Stop here if going to the next node would result into\n             * a compressed node larger than h->size can hold. */\n            if (comprsize + h->size > RAX_NODE_MAX_SIZE) break;\n            nodes++;\n            comprsize += h->size;\n        }\n        if (nodes > 1) {\n            /* If we can compress, create the new node and populate it. */\n            size_t nodesize =\n                sizeof(raxNode)+comprsize+raxPadding(comprsize)+sizeof(raxNode*);\n            raxNode *new = rax_malloc(nodesize);\n            /* An out of memory here just means we cannot optimize this\n             * node, but the tree is left in a consistent state. */\n            if (new == NULL) {\n                raxStackFree(&ts);\n                return 1;\n            }\n            new->iskey = 0;\n            new->isnull = 0;\n            new->iscompr = 1;\n            new->size = comprsize;\n            rax->numnodes++;\n\n            /* Scan again, this time to populate the new node content and\n             * to fix the new node child pointer. At the same time we free\n             * all the nodes that we'll no longer use. */\n            comprsize = 0;\n            h = start;\n            while(h->size != 0) {\n                memcpy(new->data+comprsize,h->data,h->size);\n                comprsize += h->size;\n                raxNode **cp = raxNodeLastChildPtr(h);\n                raxNode *tofree = h;\n                memcpy(&h,cp,sizeof(h));\n                rax_free(tofree); rax->numnodes--;\n                if (h->iskey || (!h->iscompr && h->size != 1)) break;\n            }\n            debugnode(\"New node\",new);\n\n            /* Now 'h' points to the first node that we still need to use,\n             * so our new node child pointer will point to it. */\n            raxNode **cp = raxNodeLastChildPtr(new);\n            memcpy(cp,&h,sizeof(h));\n\n            /* Fix parent link. */\n            if (parent) {\n                raxNode **parentlink = raxFindParentLink(parent,start);\n                memcpy(parentlink,&new,sizeof(new));\n            } else {\n                rax->head = new;\n            }\n\n            debugf(\"Compressed %d nodes, %d total bytes\\n\",\n                nodes, (int)comprsize);\n        }\n    }\n    raxStackFree(&ts);\n    return 1;\n}\n\n/* This is the core of raxFree(): performs a depth-first scan of the\n * tree and releases all the nodes found. */\nvoid raxRecursiveFree(rax *rax, raxNode *n, void (*free_callback)(void*)) {\n    debugnode(\"free traversing\",n);\n    int numchildren = n->iscompr ? 1 : n->size;\n    raxNode **cp = raxNodeLastChildPtr(n);\n    while(numchildren--) {\n        raxNode *child;\n        memcpy(&child,cp,sizeof(child));\n        raxRecursiveFree(rax,child,free_callback);\n        cp--;\n    }\n    debugnode(\"free depth-first\",n);\n    if (free_callback && n->iskey && !n->isnull)\n        free_callback(raxGetData(n));\n    rax_free(n);\n    rax->numnodes--;\n}\n\n/* Free a whole radix tree, calling the specified callback in order to\n * free the auxiliary data. */\nvoid raxFreeWithCallback(rax *rax, void (*free_callback)(void*)) {\n    raxRecursiveFree(rax,rax->head,free_callback);\n    assert(rax->numnodes == 0);\n    rax_free(rax);\n}\n\n/* Free a whole radix tree. */\nvoid raxFree(rax *rax) {\n    raxFreeWithCallback(rax,NULL);\n}\n\n/* ------------------------------- Iterator --------------------------------- */\n\n/* Initialize a Rax iterator. This call should be performed a single time\n * to initialize the iterator, and must be followed by a raxSeek() call,\n * otherwise the raxPrev()/raxNext() functions will just return EOF. */\nvoid raxStart(raxIterator *it, rax *rt) {\n    it->flags = RAX_ITER_EOF; /* No crash if the iterator is not seeked. */\n    it->rt = rt;\n    it->key_len = 0;\n    it->key = it->key_static_string;\n    it->key_max = RAX_ITER_STATIC_LEN;\n    it->data = NULL;\n    it->node_cb = NULL;\n    raxStackInit(&it->stack);\n}\n\n/* Append characters at the current key string of the iterator 'it'. This\n * is a low level function used to implement the iterator, not callable by\n * the user. Returns 0 on out of memory, otherwise 1 is returned. */\nint raxIteratorAddChars(raxIterator *it, unsigned char *s, size_t len) {\n    if (it->key_max < it->key_len+len) {\n        unsigned char *old = (it->key == it->key_static_string) ? NULL :\n                                                                  it->key;\n        size_t new_max = (it->key_len+len)*2;\n        it->key = rax_realloc(old,new_max);\n        if (it->key == NULL) {\n            it->key = (!old) ? it->key_static_string : old;\n            errno = ENOMEM;\n            return 0;\n        }\n        if (old == NULL) memcpy(it->key,it->key_static_string,it->key_len);\n        it->key_max = new_max;\n    }\n    /* Use memmove since there could be an overlap between 's' and\n     * it->key when we use the current key in order to re-seek. */\n    memmove(it->key+it->key_len,s,len);\n    it->key_len += len;\n    return 1;\n}\n\n/* Remove the specified number of chars from the right of the current\n * iterator key. */\nvoid raxIteratorDelChars(raxIterator *it, size_t count) {\n    it->key_len -= count;\n}\n\n/* Do an iteration step towards the next element. At the end of the step the\n * iterator key will represent the (new) current key. If it is not possible\n * to step in the specified direction since there are no longer elements, the\n * iterator is flagged with RAX_ITER_EOF.\n *\n * If 'noup' is true the function starts directly scanning for the next\n * lexicographically smaller children, and the current node is already assumed\n * to be the parent of the last key node, so the first operation to go back to\n * the parent will be skipped. This option is used by raxSeek() when\n * implementing seeking a non existing element with the \">\" or \"<\" options:\n * the starting node is not a key in that particular case, so we start the scan\n * from a node that does not represent the key set.\n *\n * The function returns 1 on success or 0 on out of memory. */\nint raxIteratorNextStep(raxIterator *it, int noup) {\n    if (it->flags & RAX_ITER_EOF) {\n        return 1;\n    } else if (it->flags & RAX_ITER_JUST_SEEKED) {\n        it->flags &= ~RAX_ITER_JUST_SEEKED;\n        return 1;\n    }\n\n    /* Save key len, stack items and the node where we are currently\n     * so that on iterator EOF we can restore the current key and state. */\n    size_t orig_key_len = it->key_len;\n    size_t orig_stack_items = it->stack.items;\n    raxNode *orig_node = it->node;\n\n    while(1) {\n        int children = it->node->iscompr ? 1 : it->node->size;\n        if (!noup && children) {\n            debugf(\"GO DEEPER\\n\");\n            /* Seek the lexicographically smaller key in this subtree, which\n             * is the first one found always going torwards the first child\n             * of every successive node. */\n            if (!raxStackPush(&it->stack,it->node)) return 0;\n            raxNode **cp = raxNodeFirstChildPtr(it->node);\n            if (!raxIteratorAddChars(it,it->node->data,\n                it->node->iscompr ? it->node->size : 1)) return 0;\n            memcpy(&it->node,cp,sizeof(it->node));\n            /* Call the node callback if any, and replace the node pointer\n             * if the callback returns true. */\n            if (it->node_cb && it->node_cb(&it->node))\n                memcpy(cp,&it->node,sizeof(it->node));\n            /* For \"next\" step, stop every time we find a key along the\n             * way, since the key is lexicograhically smaller compared to\n             * what follows in the sub-children. */\n            if (it->node->iskey) {\n                it->data = raxGetData(it->node);\n                return 1;\n            }\n        } else {\n            /* If we finished exporing the previous sub-tree, switch to the\n             * new one: go upper until a node is found where there are\n             * children representing keys lexicographically greater than the\n             * current key. */\n            while(1) {\n                int old_noup = noup;\n\n                /* Already on head? Can't go up, iteration finished. */\n                if (!noup && it->node == it->rt->head) {\n                    it->flags |= RAX_ITER_EOF;\n                    it->stack.items = orig_stack_items;\n                    it->key_len = orig_key_len;\n                    it->node = orig_node;\n                    return 1;\n                }\n                /* If there are no children at the current node, try parent's\n                 * next child. */\n                unsigned char prevchild = it->key[it->key_len-1];\n                if (!noup) {\n                    it->node = raxStackPop(&it->stack);\n                } else {\n                    noup = 0;\n                }\n                /* Adjust the current key to represent the node we are\n                 * at. */\n                int todel = it->node->iscompr ? it->node->size : 1;\n                raxIteratorDelChars(it,todel);\n\n                /* Try visiting the next child if there was at least one\n                 * additional child. */\n                if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) {\n                    raxNode **cp = raxNodeFirstChildPtr(it->node);\n                    int i = 0;\n                    while (i < it->node->size) {\n                        debugf(\"SCAN NEXT %c\\n\", it->node->data[i]);\n                        if (it->node->data[i] > prevchild) break;\n                        i++;\n                        cp++;\n                    }\n                    if (i != it->node->size) {\n                        debugf(\"SCAN found a new node\\n\");\n                        raxIteratorAddChars(it,it->node->data+i,1);\n                        if (!raxStackPush(&it->stack,it->node)) return 0;\n                        memcpy(&it->node,cp,sizeof(it->node));\n                        /* Call the node callback if any, and replace the node\n                         * pointer if the callback returns true. */\n                        if (it->node_cb && it->node_cb(&it->node))\n                            memcpy(cp,&it->node,sizeof(it->node));\n                        if (it->node->iskey) {\n                            it->data = raxGetData(it->node);\n                            return 1;\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n    }\n}\n\n/* Seek the greatest key in the subtree at the current node. Return 0 on\n * out of memory, otherwise 1. This is an helper function for different\n * iteration functions below. */\nint raxSeekGreatest(raxIterator *it) {\n    while(it->node->size) {\n        if (it->node->iscompr) {\n            if (!raxIteratorAddChars(it,it->node->data,\n                it->node->size)) return 0;\n        } else {\n            if (!raxIteratorAddChars(it,it->node->data+it->node->size-1,1))\n                return 0;\n        }\n        raxNode **cp = raxNodeLastChildPtr(it->node);\n        if (!raxStackPush(&it->stack,it->node)) return 0;\n        memcpy(&it->node,cp,sizeof(it->node));\n    }\n    return 1;\n}\n\n/* Like raxIteratorNextStep() but implements an iteration step moving\n * to the lexicographically previous element. The 'noup' option has a similar\n * effect to the one of raxIteratorNextStep(). */\nint raxIteratorPrevStep(raxIterator *it, int noup) {\n    if (it->flags & RAX_ITER_EOF) {\n        return 1;\n    } else if (it->flags & RAX_ITER_JUST_SEEKED) {\n        it->flags &= ~RAX_ITER_JUST_SEEKED;\n        return 1;\n    }\n\n    /* Save key len, stack items and the node where we are currently\n     * so that on iterator EOF we can restore the current key and state. */\n    size_t orig_key_len = it->key_len;\n    size_t orig_stack_items = it->stack.items;\n    raxNode *orig_node = it->node;\n\n    while(1) {\n        int old_noup = noup;\n\n        /* Already on head? Can't go up, iteration finished. */\n        if (!noup && it->node == it->rt->head) {\n            it->flags |= RAX_ITER_EOF;\n            it->stack.items = orig_stack_items;\n            it->key_len = orig_key_len;\n            it->node = orig_node;\n            return 1;\n        }\n\n        unsigned char prevchild = it->key[it->key_len-1];\n        if (!noup) {\n            it->node = raxStackPop(&it->stack);\n        } else {\n            noup = 0;\n        }\n\n        /* Adjust the current key to represent the node we are\n         * at. */\n        int todel = it->node->iscompr ? it->node->size : 1;\n        raxIteratorDelChars(it,todel);\n\n        /* Try visiting the prev child if there is at least one\n         * child. */\n        if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) {\n            raxNode **cp = raxNodeLastChildPtr(it->node);\n            int i = it->node->size-1;\n            while (i >= 0) {\n                debugf(\"SCAN PREV %c\\n\", it->node->data[i]);\n                if (it->node->data[i] < prevchild) break;\n                i--;\n                cp--;\n            }\n            /* If we found a new subtree to explore in this node,\n             * go deeper following all the last children in order to\n             * find the key lexicographically greater. */\n            if (i != -1) {\n                debugf(\"SCAN found a new node\\n\");\n                /* Enter the node we just found. */\n                if (!raxIteratorAddChars(it,it->node->data+i,1)) return 0;\n                if (!raxStackPush(&it->stack,it->node)) return 0;\n                memcpy(&it->node,cp,sizeof(it->node));\n                /* Seek sub-tree max. */\n                if (!raxSeekGreatest(it)) return 0;\n            }\n        }\n\n        /* Return the key: this could be the key we found scanning a new\n         * subtree, or if we did not find a new subtree to explore here,\n         * before giving up with this node, check if it's a key itself. */\n        if (it->node->iskey) {\n            it->data = raxGetData(it->node);\n            return 1;\n        }\n    }\n}\n\n/* Seek an iterator at the specified element.\n * Return 0 if the seek failed for syntax error or out of memory. Otherwise\n * 1 is returned. When 0 is returned for out of memory, errno is set to\n * the ENOMEM value. */\nint raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {\n    int eq = 0, lt = 0, gt = 0, first = 0, last = 0;\n\n    it->stack.items = 0; /* Just resetting. Intialized by raxStart(). */\n    it->flags |= RAX_ITER_JUST_SEEKED;\n    it->flags &= ~RAX_ITER_EOF;\n    it->key_len = 0;\n    it->node = NULL;\n\n    /* Set flags according to the operator used to perform the seek. */\n    if (op[0] == '>') {\n        gt = 1;\n        if (op[1] == '=') eq = 1;\n    } else if (op[0] == '<') {\n        lt = 1;\n        if (op[1] == '=') eq = 1;\n    } else if (op[0] == '=') {\n        eq = 1;\n    } else if (op[0] == '^') {\n        first = 1;\n    } else if (op[0] == '$') {\n        last = 1;\n    } else {\n        errno = 0;\n        return 0; /* Error. */\n    }\n\n    /* If there are no elements, set the EOF condition immediately and\n     * return. */\n    if (it->rt->numele == 0) {\n        it->flags |= RAX_ITER_EOF;\n        return 1;\n    }\n\n    if (first) {\n        /* Seeking the first key greater or equal to the empty string\n         * is equivalent to seeking the smaller key available. */\n        return raxSeek(it,\">=\",NULL,0);\n    }\n\n    if (last) {\n        /* Find the greatest key taking always the last child till a\n         * final node is found. */\n        it->node = it->rt->head;\n        if (!raxSeekGreatest(it)) return 0;\n        assert(it->node->iskey);\n        it->data = raxGetData(it->node);\n        return 1;\n    }\n\n    /* We need to seek the specified key. What we do here is to actually\n     * perform a lookup, and later invoke the prev/next key code that\n     * we already use for iteration. */\n    int splitpos = 0;\n    size_t i = raxLowWalk(it->rt,ele,len,&it->node,NULL,&splitpos,&it->stack);\n\n    /* Return OOM on incomplete stack info. */\n    if (it->stack.oom) return 0;\n\n    if (eq && i == len && (!it->node->iscompr || splitpos == 0) &&\n        it->node->iskey)\n    {\n        /* We found our node, since the key matches and we have an\n         * \"equal\" condition. */\n        if (!raxIteratorAddChars(it,ele,len)) return 0; /* OOM. */\n        it->data = raxGetData(it->node);\n    } else if (lt || gt) {\n        /* Exact key not found or eq flag not set. We have to set as current\n         * key the one represented by the node we stopped at, and perform\n         * a next/prev operation to seek. To reconstruct the key at this node\n         * we start from the parent and go to the current node, accumulating\n         * the characters found along the way. */\n        if (!raxStackPush(&it->stack,it->node)) return 0;\n        for (size_t j = 1; j < it->stack.items; j++) {\n            raxNode *parent = it->stack.stack[j-1];\n            raxNode *child = it->stack.stack[j];\n            if (parent->iscompr) {\n                if (!raxIteratorAddChars(it,parent->data,parent->size))\n                    return 0;\n            } else {\n                raxNode **cp = raxNodeFirstChildPtr(parent);\n                unsigned char *p = parent->data;\n                while(1) {\n                    raxNode *aux;\n                    memcpy(&aux,cp,sizeof(aux));\n                    if (aux == child) break;\n                    cp++;\n                    p++;\n                }\n                if (!raxIteratorAddChars(it,p,1)) return 0;\n            }\n        }\n        raxStackPop(&it->stack);\n\n        /* We need to set the iterator in the correct state to call next/prev\n         * step in order to seek the desired element. */\n        debugf(\"After initial seek: i=%d len=%d key=%.*s\\n\",\n            (int)i, (int)len, (int)it->key_len, it->key);\n        if (i != len && !it->node->iscompr) {\n            /* If we stopped in the middle of a normal node because of a\n             * mismatch, add the mismatching character to the current key\n             * and call the iterator with the 'noup' flag so that it will try\n             * to seek the next/prev child in the current node directly based\n             * on the mismatching character. */\n            if (!raxIteratorAddChars(it,ele+i,1)) return 0;\n            debugf(\"Seek normal node on mismatch: %.*s\\n\",\n                (int)it->key_len, (char*)it->key);\n\n            it->flags &= ~RAX_ITER_JUST_SEEKED;\n            if (lt && !raxIteratorPrevStep(it,1)) return 0;\n            if (gt && !raxIteratorNextStep(it,1)) return 0;\n            it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */\n        } else if (i != len && it->node->iscompr) {\n            debugf(\"Compressed mismatch: %.*s\\n\",\n                (int)it->key_len, (char*)it->key);\n            /* In case of a mismatch within a compressed node. */\n            int nodechar = it->node->data[splitpos];\n            int keychar = ele[i];\n            it->flags &= ~RAX_ITER_JUST_SEEKED;\n            if (gt) {\n                /* If the key the compressed node represents is greater\n                 * than our seek element, continue forward, otherwise set the\n                 * state in order to go back to the next sub-tree. */\n                if (nodechar > keychar) {\n                    if (!raxIteratorNextStep(it,0)) return 0;\n                } else {\n                    if (!raxIteratorAddChars(it,it->node->data,it->node->size))\n                        return 0;\n                    if (!raxIteratorNextStep(it,1)) return 0;\n                }\n            }\n            if (lt) {\n                /* If the key the compressed node represents is smaller\n                 * than our seek element, seek the greater key in this\n                 * subtree, otherwise set the state in order to go back to\n                 * the previous sub-tree. */\n                if (nodechar < keychar) {\n                    if (!raxSeekGreatest(it)) return 0;\n                    it->data = raxGetData(it->node);\n                } else {\n                    if (!raxIteratorAddChars(it,it->node->data,it->node->size))\n                        return 0;\n                    if (!raxIteratorPrevStep(it,1)) return 0;\n                }\n            }\n            it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */\n        } else {\n            debugf(\"No mismatch: %.*s\\n\",\n                (int)it->key_len, (char*)it->key);\n            /* If there was no mismatch we are into a node representing the\n             * key, (but which is not a key or the seek operator does not\n             * include 'eq'), or we stopped in the middle of a compressed node\n             * after processing all the key. Continue iterating as this was\n             * a legitimate key we stopped at. */\n            it->flags &= ~RAX_ITER_JUST_SEEKED;\n            if (it->node->iscompr && it->node->iskey && splitpos && lt) {\n                /* If we stopped in the middle of a compressed node with\n                 * perfect match, and the condition is to seek a key \"<\" than\n                 * the specified one, then if this node is a key it already\n                 * represents our match. For instance we may have nodes:\n                 *\n                 * \"f\" -> \"oobar\" = 1 -> \"\" = 2\n                 *\n                 * Representing keys \"f\" = 1, \"foobar\" = 2. A seek for\n                 * the key < \"foo\" will stop in the middle of the \"oobar\"\n                 * node, but will be our match, representing the key \"f\".\n                 *\n                 * So in that case, we don't seek backward. */\n                it->data = raxGetData(it->node);\n            } else {\n                if (gt && !raxIteratorNextStep(it,0)) return 0;\n                if (lt && !raxIteratorPrevStep(it,0)) return 0;\n            }\n            it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */\n        }\n    } else {\n        /* If we are here just eq was set but no match was found. */\n        it->flags |= RAX_ITER_EOF;\n        return 1;\n    }\n    return 1;\n}\n\n/* Go to the next element in the scope of the iterator 'it'.\n * If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is\n * returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */\nint raxNext(raxIterator *it) {\n    if (!raxIteratorNextStep(it,0)) {\n        errno = ENOMEM;\n        return 0;\n    }\n    if (it->flags & RAX_ITER_EOF) {\n        errno = 0;\n        return 0;\n    }\n    return 1;\n}\n\n/* Go to the previous element in the scope of the iterator 'it'.\n * If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is\n * returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */\nint raxPrev(raxIterator *it) {\n    if (!raxIteratorPrevStep(it,0)) {\n        errno = ENOMEM;\n        return 0;\n    }\n    if (it->flags & RAX_ITER_EOF) {\n        errno = 0;\n        return 0;\n    }\n    return 1;\n}\n\n/* Perform a random walk starting in the current position of the iterator.\n * Return 0 if the tree is empty or on out of memory. Otherwise 1 is returned\n * and the iterator is set to the node reached after doing a random walk\n * of 'steps' steps. If the 'steps' argument is 0, the random walk is performed\n * using a random number of steps between 1 and two times the logarithm of\n * the number of elements.\n *\n * NOTE: if you use this function to generate random elements from the radix\n * tree, expect a disappointing distribution. A random walk produces good\n * random elements if the tree is not sparse, however in the case of a radix\n * tree certain keys will be reported much more often than others. At least\n * this function should be able to expore every possible element eventually. */\nint raxRandomWalk(raxIterator *it, size_t steps) {\n    if (it->rt->numele == 0) {\n        it->flags |= RAX_ITER_EOF;\n        return 0;\n    }\n\n    if (steps == 0) {\n        size_t fle = 1+floor(log(it->rt->numele));\n        fle *= 2;\n        steps = 1 + rand() % fle;\n    }\n\n    raxNode *n = it->node;\n    while(steps > 0 || !n->iskey) {\n        int numchildren = n->iscompr ? 1 : n->size;\n        int r = rand() % (numchildren+(n != it->rt->head));\n\n        if (r == numchildren) {\n            /* Go up to parent. */\n            n = raxStackPop(&it->stack);\n            int todel = n->iscompr ? n->size : 1;\n            raxIteratorDelChars(it,todel);\n        } else {\n            /* Select a random child. */\n            if (n->iscompr) {\n                if (!raxIteratorAddChars(it,n->data,n->size)) return 0;\n            } else {\n                if (!raxIteratorAddChars(it,n->data+r,1)) return 0;\n            }\n            raxNode **cp = raxNodeFirstChildPtr(n)+r;\n            if (!raxStackPush(&it->stack,n)) return 0;\n            memcpy(&n,cp,sizeof(n));\n        }\n        if (n->iskey) steps--;\n    }\n    it->node = n;\n    it->data = raxGetData(it->node);\n    return 1;\n}\n\n/* Compare the key currently pointed by the iterator to the specified\n * key according to the specified operator. Returns 1 if the comparison is\n * true, otherwise 0 is returned. */\nint raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len) {\n    int eq = 0, lt = 0, gt = 0;\n\n    if (op[0] == '=' || op[1] == '=') eq = 1;\n    if (op[0] == '>') gt = 1;\n    else if (op[0] == '<') lt = 1;\n    else if (op[1] != '=') return 0; /* Syntax error. */\n\n    size_t minlen = key_len < iter->key_len ? key_len : iter->key_len;\n    int cmp = memcmp(iter->key,key,minlen);\n\n    /* Handle == */\n    if (lt == 0 && gt == 0) return cmp == 0 && key_len == iter->key_len;\n\n    /* Handle >, >=, <, <= */\n    if (cmp == 0) {\n        /* Same prefix: longer wins. */\n        if (eq && key_len == iter->key_len) return 1;\n        else if (lt) return iter->key_len < key_len;\n        else if (gt) return iter->key_len > key_len;\n        else return 0; /* Avoid warning, just 'eq' is handled before. */\n    } else if (cmp > 0) {\n        return gt ? 1 : 0;\n    } else /* (cmp < 0) */ {\n        return lt ? 1 : 0;\n    }\n}\n\n/* Free the iterator. */\nvoid raxStop(raxIterator *it) {\n    if (it->key != it->key_static_string) rax_free(it->key);\n    raxStackFree(&it->stack);\n}\n\n/* Return if the iterator is in an EOF state. This happens when raxSeek()\n * failed to seek an appropriate element, so that raxNext() or raxPrev()\n * will return zero, or when an EOF condition was reached while iterating\n * with raxNext() and raxPrev(). */\nint raxEOF(raxIterator *it) {\n    return it->flags & RAX_ITER_EOF;\n}\n\n/* Return the number of elements inside the radix tree. */\nuint64_t raxSize(rax *rax) {\n    return rax->numele;\n}\n\n/* ----------------------------- Introspection ------------------------------ */\n\n/* This function is mostly used for debugging and learning purposes.\n * It shows an ASCII representation of a tree on standard output, outling\n * all the nodes and the contained keys.\n *\n * The representation is as follow:\n *\n *  \"foobar\" (compressed node)\n *  [abc] (normal node with three children)\n *  [abc]=0x12345678 (node is a key, pointing to value 0x12345678)\n *  [] (a normal empty node)\n *\n *  Children are represented in new idented lines, each children prefixed by\n *  the \"`-(x)\" string, where \"x\" is the edge byte.\n *\n *  [abc]\n *   `-(a) \"ladin\"\n *   `-(b) [kj]\n *   `-(c) []\n *\n *  However when a node has a single child the following representation\n *  is used instead:\n *\n *  [abc] -> \"ladin\" -> []\n */\n\n/* The actual implementation of raxShow(). */\nvoid raxRecursiveShow(int level, int lpad, raxNode *n) {\n    char s = n->iscompr ? '\"' : '[';\n    char e = n->iscompr ? '\"' : ']';\n\n    int numchars = printf(\"%c%.*s%c\", s, n->size, n->data, e);\n    if (n->iskey) {\n        numchars += printf(\"=%p\",raxGetData(n));\n    }\n\n    int numchildren = n->iscompr ? 1 : n->size;\n    /* Note that 7 and 4 magic constants are the string length\n     * of \" `-(x) \" and \" -> \" respectively. */\n    if (level) {\n        lpad += (numchildren > 1) ? 7 : 4;\n        if (numchildren == 1) lpad += numchars;\n    }\n    raxNode **cp = raxNodeFirstChildPtr(n);\n    for (int i = 0; i < numchildren; i++) {\n        char *branch = \" `-(%c) \";\n        if (numchildren > 1) {\n            printf(\"\\n\");\n            for (int j = 0; j < lpad; j++) putchar(' ');\n            printf(branch,n->data[i]);\n        } else {\n            printf(\" -> \");\n        }\n        raxNode *child;\n        memcpy(&child,cp,sizeof(child));\n        raxRecursiveShow(level+1,lpad,child);\n        cp++;\n    }\n}\n\n/* Show a tree, as outlined in the comment above. */\nvoid raxShow(rax *rax) {\n    raxRecursiveShow(0,0,rax->head);\n    putchar('\\n');\n}\n\n/* Used by debugnode() macro to show info about a given node. */\nvoid raxDebugShowNode(const char *msg, raxNode *n) {\n    if (raxDebugMsg == 0) return;\n    printf(\"%s: %p [%.*s] key:%d size:%d children:\",\n        msg, (void*)n, (int)n->size, (char*)n->data, n->iskey, n->size);\n    int numcld = n->iscompr ? 1 : n->size;\n    raxNode **cldptr = raxNodeLastChildPtr(n) - (numcld-1);\n    while(numcld--) {\n        raxNode *child;\n        memcpy(&child,cldptr,sizeof(child));\n        cldptr++;\n        printf(\"%p \", (void*)child);\n    }\n    printf(\"\\n\");\n    fflush(stdout);\n}\n\n/* Touch all the nodes of a tree returning a check sum. This is useful\n * in order to make Valgrind detect if there is something wrong while\n * reading the data structure.\n *\n * This function was used in order to identify Rax bugs after a big refactoring\n * using this technique:\n *\n * 1. The rax-test is executed using Valgrind, adding a printf() so that for\n *    the fuzz tester we see what iteration in the loop we are in.\n * 2. After every modification of the radix tree made by the fuzz tester\n *    in rax-test.c, we add a call to raxTouch().\n * 3. Now as soon as an operation will corrupt the tree, raxTouch() will\n *    detect it (via Valgrind) immediately. We can add more calls to narrow\n *    the state.\n * 4. At this point a good idea is to enable Rax debugging messages immediately\n *    before the moment the tree is corrupted, to see what happens.\n */\nunsigned long raxTouch(raxNode *n) {\n    debugf(\"Touching %p\\n\", (void*)n);\n    unsigned long sum = 0;\n    if (n->iskey) {\n        sum += (unsigned long)raxGetData(n);\n    }\n\n    int numchildren = n->iscompr ? 1 : n->size;\n    raxNode **cp = raxNodeFirstChildPtr(n);\n    int count = 0;\n    for (int i = 0; i < numchildren; i++) {\n        if (numchildren > 1) {\n            sum += (long)n->data[i];\n        }\n        raxNode *child;\n        memcpy(&child,cp,sizeof(child));\n        if (child == (void*)0x65d1760) count++;\n        if (count > 1) exit(1);\n        sum += raxTouch(child);\n        cp++;\n    }\n    return sum;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/rax.h",
    "content": "/* Rax -- A radix tree implementation.\n *\n * Copyright (c) 2017-2018, 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 RAX_H\n#define RAX_H\n\n#include <stdint.h>\n\n/* Representation of a radix tree as implemented in this file, that contains\n * the strings \"foo\", \"foobar\" and \"footer\" after the insertion of each\n * word. When the node represents a key inside the radix tree, we write it\n * between [], otherwise it is written between ().\n *\n * This is the vanilla representation:\n *\n *              (f) \"\"\n *                \\\n *                (o) \"f\"\n *                  \\\n *                  (o) \"fo\"\n *                    \\\n *                  [t   b] \"foo\"\n *                  /     \\\n *         \"foot\" (e)     (a) \"foob\"\n *                /         \\\n *      \"foote\" (r)         (r) \"fooba\"\n *              /             \\\n *    \"footer\" []             [] \"foobar\"\n *\n * However, this implementation implements a very common optimization where\n * successive nodes having a single child are \"compressed\" into the node\n * itself as a string of characters, each representing a next-level child,\n * and only the link to the node representing the last character node is\n * provided inside the representation. So the above representation is turend\n * into:\n *\n *                  [\"foo\"] \"\"\n *                     |\n *                  [t   b] \"foo\"\n *                  /     \\\n *        \"foot\" (\"er\")    (\"ar\") \"foob\"\n *                 /          \\\n *       \"footer\" []          [] \"foobar\"\n *\n * However this optimization makes the implementation a bit more complex.\n * For instance if a key \"first\" is added in the above radix tree, a\n * \"node splitting\" operation is needed, since the \"foo\" prefix is no longer\n * composed of nodes having a single child one after the other. This is the\n * above tree and the resulting node splitting after this event happens:\n *\n *\n *                    (f) \"\"\n *                    /\n *                 (i o) \"f\"\n *                 /   \\\n *    \"firs\"  (\"rst\")  (o) \"fo\"\n *              /        \\\n *    \"first\" []       [t   b] \"foo\"\n *                     /     \\\n *           \"foot\" (\"er\")    (\"ar\") \"foob\"\n *                    /          \\\n *          \"footer\" []          [] \"foobar\"\n *\n * Similarly after deletion, if a new chain of nodes having a single child\n * is created (the chain must also not include nodes that represent keys),\n * it must be compressed back into a single node.\n *\n */\n\n#define RAX_NODE_MAX_SIZE ((1<<29)-1)\ntypedef struct raxNode {\n    uint32_t iskey:1;     /* Does this node contain a key? */\n    uint32_t isnull:1;    /* Associated value is NULL (don't store it). */\n    uint32_t iscompr:1;   /* Node is compressed. */\n    uint32_t size:29;     /* Number of children, or compressed string len. */\n    /* Data layout is as follows:\n     *\n     * If node is not compressed we have 'size' bytes, one for each children\n     * character, and 'size' raxNode pointers, point to each child node.\n     * Note how the character is not stored in the children but in the\n     * edge of the parents:\n     *\n     * [header iscompr=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?)\n     *\n     * if node is compressed (iscompr bit is 1) the node has 1 children.\n     * In that case the 'size' bytes of the string stored immediately at\n     * the start of the data section, represent a sequence of successive\n     * nodes linked one after the other, for which only the last one in\n     * the sequence is actually represented as a node, and pointed to by\n     * the current compressed node.\n     *\n     * [header iscompr=1][xyz][z-ptr](value-ptr?)\n     *\n     * Both compressed and not compressed nodes can represent a key\n     * with associated data in the radix tree at any level (not just terminal\n     * nodes).\n     *\n     * If the node has an associated key (iskey=1) and is not NULL\n     * (isnull=0), then after the raxNode pointers poiting to the\n     * children, an additional value pointer is present (as you can see\n     * in the representation above as \"value-ptr\" field).\n     */\n    unsigned char data[];\n} raxNode;\n\ntypedef struct rax {\n    raxNode *head;\n    uint64_t numele;\n    uint64_t numnodes;\n} rax;\n\n/* Stack data structure used by raxLowWalk() in order to, optionally, return\n * a list of parent nodes to the caller. The nodes do not have a \"parent\"\n * field for space concerns, so we use the auxiliary stack when needed. */\n#define RAX_STACK_STATIC_ITEMS 32\ntypedef struct raxStack {\n    void **stack; /* Points to static_items or an heap allocated array. */\n    size_t items, maxitems; /* Number of items contained and total space. */\n    /* Up to RAXSTACK_STACK_ITEMS items we avoid to allocate on the heap\n     * and use this static array of pointers instead. */\n    void *static_items[RAX_STACK_STATIC_ITEMS];\n    int oom; /* True if pushing into this stack failed for OOM at some point. */\n} raxStack;\n\n/* Optional callback used for iterators and be notified on each rax node,\n * including nodes not representing keys. If the callback returns true\n * the callback changed the node pointer in the iterator structure, and the\n * iterator implementation will have to replace the pointer in the radix tree\n * internals. This allows the callback to reallocate the node to perform\n * very special operations, normally not needed by normal applications.\n *\n * This callback is used to perform very low level analysis of the radix tree\n * structure, scanning each possible node (but the root node), or in order to\n * reallocate the nodes to reduce the allocation fragmentation (this is the\n * Redis application for this callback).\n *\n * This is currently only supported in forward iterations (raxNext) */\ntypedef int (*raxNodeCallback)(raxNode **noderef);\n\n/* Radix tree iterator state is encapsulated into this data structure. */\n#define RAX_ITER_STATIC_LEN 128\n#define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current\n                                       element for the first iteration and\n                                       clear the flag. */\n#define RAX_ITER_EOF (1<<1)    /* End of iteration reached. */\n#define RAX_ITER_SAFE (1<<2)   /* Safe iterator, allows operations while\n                                  iterating. But it is slower. */\ntypedef struct raxIterator {\n    int flags;\n    rax *rt;                /* Radix tree we are iterating. */\n    unsigned char *key;     /* The current string. */\n    void *data;             /* Data associated to this key. */\n    size_t key_len;         /* Current key length. */\n    size_t key_max;         /* Max key len the current key buffer can hold. */\n    unsigned char key_static_string[RAX_ITER_STATIC_LEN];\n    raxNode *node;          /* Current node. Only for unsafe iteration. */\n    raxStack stack;         /* Stack used for unsafe iteration. */\n    raxNodeCallback node_cb; /* Optional node callback. Normally set to NULL. */\n} raxIterator;\n\n/* A special pointer returned for not found items. */\nextern void *raxNotFound;\n\n/* Exported API. */\nrax *raxNew(void);\nint raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);\nint raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);\nint raxRemove(rax *rax, unsigned char *s, size_t len, void **old);\nvoid *raxFind(rax *rax, unsigned char *s, size_t len);\nvoid raxFree(rax *rax);\nvoid raxFreeWithCallback(rax *rax, void (*free_callback)(void*));\nvoid raxStart(raxIterator *it, rax *rt);\nint raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len);\nint raxNext(raxIterator *it);\nint raxPrev(raxIterator *it);\nint raxRandomWalk(raxIterator *it, size_t steps);\nint raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len);\nvoid raxStop(raxIterator *it);\nint raxEOF(raxIterator *it);\nvoid raxShow(rax *rax);\nuint64_t raxSize(rax *rax);\nunsigned long raxTouch(raxNode *n);\nvoid raxSetDebugMsg(int onoff);\n\n/* Internal API. May be used by the node callback in order to access rax nodes\n * in a low level way, so this function is exported as well. */\nvoid raxSetData(raxNode *n, void *data);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/rax_malloc.h",
    "content": "/* Rax -- A radix tree implementation.\n *\n * Copyright (c) 2017, 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/* Allocator selection.\n *\n * This file is used in order to change the Rax allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#ifndef RAX_ALLOC_H\n#define RAX_ALLOC_H\n#include \"zmalloc.h\"\n#define rax_malloc zmalloc\n#define rax_realloc zrealloc\n#define rax_free zfree\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"lzf.h\"    /* LZF compression library */\n#include \"zipmap.h\"\n#include \"endianconv.h\"\n#include \"stream.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#include <sys/param.h>\n\n/* This macro is called when the internal RDB stracture is corrupt */\n#define rdbExitReportCorruptRDB(...) rdbReportError(1, __LINE__,__VA_ARGS__)\n/* This macro is called when RDB read failed (possibly a short read) */\n#define rdbReportReadError(...) rdbReportError(0, __LINE__,__VA_ARGS__)\n\nchar* rdbFileBeingLoaded = NULL; /* used for rdb checking on read error */\nextern int rdbCheckMode;\nvoid rdbCheckError(const char *fmt, ...);\nvoid rdbCheckSetError(const char *fmt, ...);\n\nvoid rdbReportError(int corruption_error, int linenum, char *reason, ...) {\n    va_list ap;\n    char msg[1024];\n    int len;\n\n    len = snprintf(msg,sizeof(msg),\n        \"Internal error in RDB reading offset %llu, function at rdb.c:%d -> \",\n        (unsigned long long)server.loading_loaded_bytes, linenum);\n    va_start(ap,reason);\n    vsnprintf(msg+len,sizeof(msg)-len,reason,ap);\n    va_end(ap);\n\n    if (!rdbCheckMode) {\n        if (rdbFileBeingLoaded || corruption_error) {\n            serverLog(LL_WARNING, \"%s\", msg);\n            char *argv[2] = {\"\",rdbFileBeingLoaded};\n            redis_check_rdb_main(2,argv,NULL);\n        } else {\n            serverLog(LL_WARNING, \"%s. Failure loading rdb format from socket, assuming connection error, resuming operation.\", msg);\n            return;\n        }\n    } else {\n        rdbCheckError(\"%s\",msg);\n    }\n    serverLog(LL_WARNING, \"Terminating server after rdb file reading failure.\");\n    exit(1);\n}\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\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 * 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. */\nint rdbLoadType(rio *rdb) {\n    unsigned char type;\n    if (rioRead(rdb,&type,1) == 0) return -1;\n    return type;\n}\n\n/* This is only used to load old databases stored with the RDB_OPCODE_EXPIRETIME\n * opcode. New versions of Redis store using the RDB_OPCODE_EXPIRETIME_MS\n * opcode. On error -1 is returned, however this could be a valid time, so\n * to check for loading errors the caller should call rioGetReadError() after\n * calling this function. */\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\nint rdbSaveMillisecondTime(rio *rdb, long long t) {\n    int64_t t64 = (int64_t) t;\n    memrev64ifbe(&t64); /* Store in little endian. */\n    return rdbWriteRaw(rdb,&t64,8);\n}\n\n/* This function loads a time from the RDB file. It gets the version of the\n * RDB because, unfortunately, before Redis 5 (RDB version 9), the function\n * failed to convert data to/from little endian, so RDB files with keys having\n * expires could not be shared between big endian and little endian systems\n * (because the expire time will be totally wrong). The fix for this is just\n * to call memrev64ifbe(), however if we fix this for all the RDB versions,\n * this call will introduce an incompatibility for big endian systems:\n * after upgrading to Redis version 5 they will no longer be able to load their\n * own old RDB files. Because of that, we instead fix the function only for new\n * RDB versions, and load older RDB versions as we used to do in the past,\n * allowing big endian systems to load their own old RDB files.\n *\n * On I/O error the function returns LLONG_MAX, however if this is also a\n * valid stored value, the caller should use rioGetReadError() to check for\n * errors after calling this function. */\nlong long rdbLoadMillisecondTime(rio *rdb, int rdbver) {\n    int64_t t64;\n    if (rioRead(rdb,&t64,8) == 0) return LLONG_MAX;\n    if (rdbver >= 9) /* Check the top comment of this function. */\n        memrev64ifbe(&t64); /* Convert in big endian if the system is BE. */\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 RDB_* definitions for more information\n * on the types of encoding. */\nint rdbSaveLen(rio *rdb, uint64_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)|(RDB_6BITLEN<<6);\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        nwritten = 1;\n    } else if (len < (1<<14)) {\n        /* Save a 14 bit len */\n        buf[0] = ((len>>8)&0xFF)|(RDB_14BITLEN<<6);\n        buf[1] = len&0xFF;\n        if (rdbWriteRaw(rdb,buf,2) == -1) return -1;\n        nwritten = 2;\n    } else if (len <= UINT32_MAX) {\n        /* Save a 32 bit len */\n        buf[0] = RDB_32BITLEN;\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        uint32_t len32 = htonl(len);\n        if (rdbWriteRaw(rdb,&len32,4) == -1) return -1;\n        nwritten = 1+4;\n    } else {\n        /* Save a 64 bit len */\n        buf[0] = RDB_64BITLEN;\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n        len = htonu64(len);\n        if (rdbWriteRaw(rdb,&len,8) == -1) return -1;\n        nwritten = 1+8;\n    }\n    return nwritten;\n}\n\n\n/* Load an encoded length. If the loaded length is a normal length as stored\n * with rdbSaveLen(), the read length is set to '*lenptr'. If instead the\n * loaded length describes a special encoding that follows, then '*isencoded'\n * is set to 1 and the encoding format is stored at '*lenptr'.\n *\n * See the RDB_ENC_* definitions in rdb.h for more information on special\n * encodings.\n *\n * The function returns -1 on error, 0 on success. */\nint rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr) {\n    unsigned char buf[2];\n    int type;\n\n    if (isencoded) *isencoded = 0;\n    if (rioRead(rdb,buf,1) == 0) return -1;\n    type = (buf[0]&0xC0)>>6;\n    if (type == RDB_ENCVAL) {\n        /* Read a 6 bit encoding type. */\n        if (isencoded) *isencoded = 1;\n        *lenptr = buf[0]&0x3F;\n    } else if (type == RDB_6BITLEN) {\n        /* Read a 6 bit len. */\n        *lenptr = buf[0]&0x3F;\n    } else if (type == RDB_14BITLEN) {\n        /* Read a 14 bit len. */\n        if (rioRead(rdb,buf+1,1) == 0) return -1;\n        *lenptr = ((buf[0]&0x3F)<<8)|buf[1];\n    } else if (buf[0] == RDB_32BITLEN) {\n        /* Read a 32 bit len. */\n        uint32_t len;\n        if (rioRead(rdb,&len,4) == 0) return -1;\n        *lenptr = ntohl(len);\n    } else if (buf[0] == RDB_64BITLEN) {\n        /* Read a 64 bit len. */\n        uint64_t len;\n        if (rioRead(rdb,&len,8) == 0) return -1;\n        *lenptr = ntohu64(len);\n    } else {\n        rdbExitReportCorruptRDB(\n            \"Unknown length encoding %d in rdbLoadLen()\",type);\n        return -1; /* Never reached. */\n    }\n    return 0;\n}\n\n/* This is like rdbLoadLenByRef() but directly returns the value read\n * from the RDB stream, signaling an error by returning RDB_LENERR\n * (since it is a too large count to be applicable in any Redis data\n * structure). */\nuint64_t rdbLoadLen(rio *rdb, int *isencoded) {\n    uint64_t len;\n\n    if (rdbLoadLenByRef(rdb,isencoded,&len) == -1) return RDB_LENERR;\n    return len;\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. */\nint rdbEncodeInteger(long long value, unsigned char *enc) {\n    if (value >= -(1<<7) && value <= (1<<7)-1) {\n        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT8;\n        enc[1] = value&0xFF;\n        return 2;\n    } else if (value >= -(1<<15) && value <= (1<<15)-1) {\n        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT16;\n        enc[1] = value&0xFF;\n        enc[2] = (value>>8)&0xFF;\n        return 3;\n    } else if (value >= -((long long)1<<31) && value <= ((long long)1<<31)-1) {\n        enc[0] = (RDB_ENCVAL<<6)|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    } else {\n        return 0;\n    }\n}\n\n/* Loads an integer-encoded object with the specified encoding type \"enctype\".\n * The returned value changes according to the flags, see\n * rdbGenericLoadStringObject() for more info. */\nvoid *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr) {\n    int plain = flags & RDB_LOAD_PLAIN;\n    int sds = flags & RDB_LOAD_SDS;\n    int encode = flags & RDB_LOAD_ENC;\n    unsigned char enc[4];\n    long long val;\n\n    if (enctype == RDB_ENC_INT8) {\n        if (rioRead(rdb,enc,1) == 0) return NULL;\n        val = (signed char)enc[0];\n    } else if (enctype == 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 == 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        rdbExitReportCorruptRDB(\"Unknown RDB integer encoding type %d\",enctype);\n        return NULL; /* Never reached. */\n    }\n    if (plain || sds) {\n        char buf[LONG_STR_SIZE], *p;\n        int len = ll2string(buf,sizeof(buf),val);\n        if (lenptr) *lenptr = len;\n        p = plain ? zmalloc(len) : sdsnewlen(SDS_NOINIT,len);\n        memcpy(p,buf,len);\n        return p;\n    } else if (encode) {\n        return createStringObjectFromLongLongForValue(val);\n    } else {\n        return createObject(OBJ_STRING,sdsfromlonglong(val));\n    }\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 */\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    value = strtoll(s, &endptr, 10);\n    if (endptr[0] != '\\0') return 0;\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    if (strlen(buf) != len || memcmp(buf,s,len)) return 0;\n\n    return rdbEncodeInteger(value,enc);\n}\n\nssize_t rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,\n                       size_t original_len) {\n    unsigned char byte;\n    ssize_t n, nwritten = 0;\n\n    /* Data compressed! Let's save it on disk */\n    byte = (RDB_ENCVAL<<6)|RDB_ENC_LZF;\n    if ((n = rdbWriteRaw(rdb,&byte,1)) == -1) goto writeerr;\n    nwritten += n;\n\n    if ((n = rdbSaveLen(rdb,compress_len)) == -1) goto writeerr;\n    nwritten += n;\n\n    if ((n = rdbSaveLen(rdb,original_len)) == -1) goto writeerr;\n    nwritten += n;\n\n    if ((n = rdbWriteRaw(rdb,data,compress_len)) == -1) goto writeerr;\n    nwritten += n;\n\n    return nwritten;\n\nwriteerr:\n    return -1;\n}\n\nssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {\n    size_t comprlen, outlen;\n    void *out;\n\n    /* We require at least four bytes compression for this to be worth it */\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    ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);\n    zfree(out);\n    return nwritten;\n}\n\n/* Load an LZF compressed string in RDB format. The returned value\n * changes according to 'flags'. For more info check the\n * rdbGenericLoadStringObject() function. */\nvoid *rdbLoadLzfStringObject(rio *rdb, int flags, size_t *lenptr) {\n    int plain = flags & RDB_LOAD_PLAIN;\n    int sds = flags & RDB_LOAD_SDS;\n    uint64_t len, clen;\n    unsigned char *c = NULL;\n    char *val = NULL;\n\n    if ((clen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n    if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n    if ((c = zmalloc(clen)) == NULL) goto err;\n\n    /* Allocate our target according to the uncompressed size. */\n    if (plain) {\n        val = zmalloc(len);\n    } else {\n        val = sdsnewlen(SDS_NOINIT,len);\n    }\n    if (lenptr) *lenptr = len;\n\n    /* Load the compressed representation and uncompress it to target. */\n    if (rioRead(rdb,c,clen) == 0) goto err;\n    if (lzf_decompress(c,clen,val,len) == 0) {\n        rdbExitReportCorruptRDB(\"Invalid LZF compressed string\");\n    }\n    zfree(c);\n\n    if (plain || sds) {\n        return val;\n    } else {\n        return createObject(OBJ_STRING,val);\n    }\nerr:\n    zfree(c);\n    if (plain)\n        zfree(val);\n    else\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 */\nssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {\n    int enclen;\n    ssize_t n, nwritten = 0;\n\n    /* Try integer encoding */\n    if (len <= 11) {\n        unsigned char buf[5];\n        if ((enclen = rdbTryIntegerEncoding((char*)s,len,buf)) > 0) {\n            if (rdbWriteRaw(rdb,buf,enclen) == -1) return -1;\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    if (server.rdb_compression && len > 20) {\n        n = rdbSaveLzfStringObject(rdb,s,len);\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    /* Store verbatim */\n    if ((n = rdbSaveLen(rdb,len)) == -1) return -1;\n    nwritten += n;\n    if (len > 0) {\n        if (rdbWriteRaw(rdb,s,len) == -1) return -1;\n        nwritten += len;\n    }\n    return nwritten;\n}\n\n/* Save a long long value as either an encoded string or a string. */\nssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) {\n    unsigned char buf[32];\n    ssize_t n, nwritten = 0;\n    int enclen = rdbEncodeInteger(value,buf);\n    if (enclen > 0) {\n        return rdbWriteRaw(rdb,buf,enclen);\n    } else {\n        /* Encode as string */\n        enclen = ll2string((char*)buf,32,value);\n        serverAssert(enclen < 32);\n        if ((n = rdbSaveLen(rdb,enclen)) == -1) return -1;\n        nwritten += n;\n        if ((n = rdbWriteRaw(rdb,buf,enclen)) == -1) return -1;\n        nwritten += n;\n    }\n    return nwritten;\n}\n\n/* Like rdbSaveRawString() gets a Redis object instead. */\nssize_t rdbSaveStringObject(rio *rdb, robj *obj) {\n    /* Avoid to decode the object, then encode it again, if the\n     * object is already integer encoded. */\n    if (obj->encoding == OBJ_ENCODING_INT) {\n        return rdbSaveLongLongAsStringObject(rdb,(long)obj->ptr);\n    } else {\n        serverAssertWithInfo(NULL,obj,sdsEncodedObject(obj));\n        return rdbSaveRawString(rdb,obj->ptr,sdslen(obj->ptr));\n    }\n}\n\n/* Load a string object from an RDB file according to flags:\n *\n * RDB_LOAD_NONE (no flags): load an RDB object, unencoded.\n * RDB_LOAD_ENC: If the returned type is a Redis object, try to\n *               encode it in a special way to be more memory\n *               efficient. When this flag is passed the function\n *               no longer guarantees that obj->ptr is an SDS string.\n * RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc()\n *                 instead of a Redis object with an sds in it.\n * RDB_LOAD_SDS: Return an SDS string instead of a Redis object.\n *\n * On I/O error NULL is returned.\n */\nvoid *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr) {\n    int encode = flags & RDB_LOAD_ENC;\n    int plain = flags & RDB_LOAD_PLAIN;\n    int sds = flags & RDB_LOAD_SDS;\n    int isencoded;\n    uint64_t len;\n\n    len = rdbLoadLen(rdb,&isencoded);\n    if (isencoded) {\n        switch(len) {\n        case RDB_ENC_INT8:\n        case RDB_ENC_INT16:\n        case RDB_ENC_INT32:\n            return rdbLoadIntegerObject(rdb,len,flags,lenptr);\n        case RDB_ENC_LZF:\n            return rdbLoadLzfStringObject(rdb,flags,lenptr);\n        default:\n            rdbExitReportCorruptRDB(\"Unknown RDB string encoding type %d\",len);\n            return NULL; /* Never reached. */\n        }\n    }\n\n    if (len == RDB_LENERR) return NULL;\n    if (plain || sds) {\n        void *buf = plain ? zmalloc(len) : sdsnewlen(SDS_NOINIT,len);\n        if (lenptr) *lenptr = len;\n        if (len && rioRead(rdb,buf,len) == 0) {\n            if (plain)\n                zfree(buf);\n            else\n                sdsfree(buf);\n            return NULL;\n        }\n        return buf;\n    } else {\n        robj *o = encode ? createStringObject(SDS_NOINIT,len) :\n                           createRawStringObject(SDS_NOINIT,len);\n        if (len && rioRead(rdb,o->ptr,len) == 0) {\n            decrRefCount(o);\n            return NULL;\n        }\n        return o;\n    }\n}\n\nrobj *rdbLoadStringObject(rio *rdb) {\n    return rdbGenericLoadStringObject(rdb,RDB_LOAD_NONE,NULL);\n}\n\nrobj *rdbLoadEncodedStringObject(rio *rdb) {\n    return rdbGenericLoadStringObject(rdb,RDB_LOAD_ENC,NULL);\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 * This 8 bit integer has special values in order to specify the following\n * conditions:\n * 253: not a number\n * 254: + inf\n * 255: - inf\n */\nint rdbSaveDoubleValue(rio *rdb, double val) {\n    unsigned char buf[128];\n    int len;\n\n    if (isnan(val)) {\n        buf[0] = 253;\n        len = 1;\n    } else if (!isfinite(val)) {\n        len = 1;\n        buf[0] = (val < 0) ? 255 : 254;\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    return rdbWriteRaw(rdb,buf,len);\n}\n\n/* For information about double serialization check rdbSaveDoubleValue() */\nint rdbLoadDoubleValue(rio *rdb, double *val) {\n    char buf[256];\n    unsigned char len;\n\n    if (rioRead(rdb,&len,1) == 0) return -1;\n    switch(len) {\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    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/* Saves a double for RDB 8 or greater, where IE754 binary64 format is assumed.\n * We just make sure the integer is always stored in little endian, otherwise\n * the value is copied verbatim from memory to disk.\n *\n * Return -1 on error, the size of the serialized value on success. */\nint rdbSaveBinaryDoubleValue(rio *rdb, double val) {\n    memrev64ifbe(&val);\n    return rdbWriteRaw(rdb,&val,sizeof(val));\n}\n\n/* Loads a double from RDB 8 or greater. See rdbSaveBinaryDoubleValue() for\n * more info. On error -1 is returned, otherwise 0. */\nint rdbLoadBinaryDoubleValue(rio *rdb, double *val) {\n    if (rioRead(rdb,val,sizeof(*val)) == 0) return -1;\n    memrev64ifbe(val);\n    return 0;\n}\n\n/* Like rdbSaveBinaryDoubleValue() but single precision. */\nint rdbSaveBinaryFloatValue(rio *rdb, float val) {\n    memrev32ifbe(&val);\n    return rdbWriteRaw(rdb,&val,sizeof(val));\n}\n\n/* Like rdbLoadBinaryDoubleValue() but single precision. */\nint rdbLoadBinaryFloatValue(rio *rdb, float *val) {\n    if (rioRead(rdb,val,sizeof(*val)) == 0) return -1;\n    memrev32ifbe(val);\n    return 0;\n}\n\n/* Save the object type of object \"o\". */\nint rdbSaveObjectType(rio *rdb, robj *o) {\n    switch (o->type) {\n    case OBJ_STRING:\n        return rdbSaveType(rdb,RDB_TYPE_STRING);\n    case OBJ_LIST:\n        if (o->encoding == OBJ_ENCODING_QUICKLIST)\n            return rdbSaveType(rdb,RDB_TYPE_LIST_QUICKLIST);\n        else\n            serverPanic(\"Unknown list encoding\");\n    case OBJ_SET:\n        if (o->encoding == OBJ_ENCODING_INTSET)\n            return rdbSaveType(rdb,RDB_TYPE_SET_INTSET);\n        else if (o->encoding == OBJ_ENCODING_HT)\n            return rdbSaveType(rdb,RDB_TYPE_SET);\n        else\n            serverPanic(\"Unknown set encoding\");\n    case OBJ_ZSET:\n        if (o->encoding == OBJ_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,RDB_TYPE_ZSET_ZIPLIST);\n        else if (o->encoding == OBJ_ENCODING_SKIPLIST)\n            return rdbSaveType(rdb,RDB_TYPE_ZSET_2);\n        else\n            serverPanic(\"Unknown sorted set encoding\");\n    case OBJ_HASH:\n        if (o->encoding == OBJ_ENCODING_ZIPLIST)\n            return rdbSaveType(rdb,RDB_TYPE_HASH_ZIPLIST);\n        else if (o->encoding == OBJ_ENCODING_HT)\n            return rdbSaveType(rdb,RDB_TYPE_HASH);\n        else\n            serverPanic(\"Unknown hash encoding\");\n    case OBJ_STREAM:\n        return rdbSaveType(rdb,RDB_TYPE_STREAM_LISTPACKS);\n    case OBJ_MODULE:\n        return rdbSaveType(rdb,RDB_TYPE_MODULE_2);\n    default:\n        serverPanic(\"Unknown object type\");\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/* This helper function serializes a consumer group Pending Entries List (PEL)\n * into the RDB file. The 'nacks' argument tells the function if also persist\n * the informations about the not acknowledged message, or if to persist\n * just the IDs: this is useful because for the global consumer group PEL\n * we serialized the NACKs as well, but when serializing the local consumer\n * PELs we just add the ID, that will be resolved inside the global PEL to\n * put a reference to the same structure. */\nssize_t rdbSaveStreamPEL(rio *rdb, rax *pel, int nacks) {\n    ssize_t n, nwritten = 0;\n\n    /* Number of entries in the PEL. */\n    if ((n = rdbSaveLen(rdb,raxSize(pel))) == -1) return -1;\n    nwritten += n;\n\n    /* Save each entry. */\n    raxIterator ri;\n    raxStart(&ri,pel);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        /* We store IDs in raw form as 128 big big endian numbers, like\n         * they are inside the radix tree key. */\n        if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) return -1;\n        nwritten += n;\n\n        if (nacks) {\n            streamNACK *nack = ri.data;\n            if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1)\n                return -1;\n            nwritten += n;\n            if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) return -1;\n            nwritten += n;\n            /* We don't save the consumer name: we'll save the pending IDs\n             * for each consumer in the consumer PEL, and resolve the consumer\n             * at loading time. */\n        }\n    }\n    raxStop(&ri);\n    return nwritten;\n}\n\n/* Serialize the consumers of a stream consumer group into the RDB. Helper\n * function for the stream data type serialization. What we do here is to\n * persist the consumer metadata, and it's PEL, for each consumer. */\nsize_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {\n    ssize_t n, nwritten = 0;\n\n    /* Number of consumers in this consumer group. */\n    if ((n = rdbSaveLen(rdb,raxSize(cg->consumers))) == -1) return -1;\n    nwritten += n;\n\n    /* Save each consumer. */\n    raxIterator ri;\n    raxStart(&ri,cg->consumers);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        streamConsumer *consumer = ri.data;\n\n        /* Consumer name. */\n        if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;\n        nwritten += n;\n\n        /* Last seen time. */\n        if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1)\n            return -1;\n        nwritten += n;\n\n        /* Consumer PEL, without the ACKs (see last parameter of the function\n         * passed with value of 0), at loading time we'll lookup the ID\n         * in the consumer group global PEL and will put a reference in the\n         * consumer local PEL. */\n        if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1)\n            return -1;\n        nwritten += n;\n    }\n    raxStop(&ri);\n    return nwritten;\n}\n\n/* Save a Redis object.\n * Returns -1 on error, number of bytes written on success. */\nssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) {\n    ssize_t n = 0, nwritten = 0;\n\n    if (o->type == OBJ_STRING) {\n        /* Save a string value */\n        if ((n = rdbSaveStringObject(rdb,o)) == -1) return -1;\n        nwritten += n;\n    } else if (o->type == OBJ_LIST) {\n        /* Save a list value */\n        if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n            quicklist *ql = o->ptr;\n            quicklistNode *node = ql->head;\n\n            if ((n = rdbSaveLen(rdb,ql->len)) == -1) return -1;\n            nwritten += n;\n\n            while(node) {\n                if (quicklistNodeIsCompressed(node)) {\n                    void *data;\n                    size_t compress_len = quicklistGetLzf(node, &data);\n                    if ((n = rdbSaveLzfBlob(rdb,data,compress_len,node->sz)) == -1) return -1;\n                    nwritten += n;\n                } else {\n                    if ((n = rdbSaveRawString(rdb,node->zl,node->sz)) == -1) return -1;\n                    nwritten += n;\n                }\n                node = node->next;\n            }\n        } else {\n            serverPanic(\"Unknown list encoding\");\n        }\n    } else if (o->type == OBJ_SET) {\n        /* Save a set value */\n        if (o->encoding == OBJ_ENCODING_HT) {\n            dict *set = o->ptr;\n            dictIterator *di = dictGetIterator(set);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize(set))) == -1) {\n                dictReleaseIterator(di);\n                return -1;\n            }\n            nwritten += n;\n\n            while((de = dictNext(di)) != NULL) {\n                sds ele = dictGetKey(de);\n                if ((n = rdbSaveRawString(rdb,(unsigned char*)ele,sdslen(ele)))\n                    == -1)\n                {\n                    dictReleaseIterator(di);\n                    return -1;\n                }\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n        } else if (o->encoding == OBJ_ENCODING_INTSET) {\n            size_t l = intsetBlobLen((intset*)o->ptr);\n\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (o->type == OBJ_ZSET) {\n        /* Save a sorted set value */\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n        } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = o->ptr;\n            zskiplist *zsl = zs->zsl;\n\n            if ((n = rdbSaveLen(rdb,zsl->length)) == -1) return -1;\n            nwritten += n;\n\n            /* We save the skiplist elements from the greatest to the smallest\n             * (that's trivial since the elements are already ordered in the\n             * skiplist): this improves the load process, since the next loaded\n             * element will always be the smaller, so adding to the skiplist\n             * will always immediately stop at the head, making the insertion\n             * O(1) instead of O(log(N)). */\n            zskiplistNode *zn = zsl->tail;\n            while (zn != NULL) {\n                if ((n = rdbSaveRawString(rdb,\n                    (unsigned char*)zn->ele,sdslen(zn->ele))) == -1)\n                {\n                    return -1;\n                }\n                nwritten += n;\n                if ((n = rdbSaveBinaryDoubleValue(rdb,zn->score)) == -1)\n                    return -1;\n                nwritten += n;\n                zn = zn->backward;\n            }\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else if (o->type == OBJ_HASH) {\n        /* Save a hash value */\n        if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n            size_t l = ziplistBlobLen((unsigned char*)o->ptr);\n\n            if ((n = rdbSaveRawString(rdb,o->ptr,l)) == -1) return -1;\n            nwritten += n;\n\n        } else if (o->encoding == OBJ_ENCODING_HT) {\n            dictIterator *di = dictGetIterator(o->ptr);\n            dictEntry *de;\n\n            if ((n = rdbSaveLen(rdb,dictSize((dict*)o->ptr))) == -1) {\n                dictReleaseIterator(di);\n                return -1;\n            }\n            nwritten += n;\n\n            while((de = dictNext(di)) != NULL) {\n                sds field = dictGetKey(de);\n                sds value = dictGetVal(de);\n\n                if ((n = rdbSaveRawString(rdb,(unsigned char*)field,\n                        sdslen(field))) == -1)\n                {\n                    dictReleaseIterator(di);\n                    return -1;\n                }\n                nwritten += n;\n                if ((n = rdbSaveRawString(rdb,(unsigned char*)value,\n                        sdslen(value))) == -1)\n                {\n                    dictReleaseIterator(di);\n                    return -1;\n                }\n                nwritten += n;\n            }\n            dictReleaseIterator(di);\n        } else {\n            serverPanic(\"Unknown hash encoding\");\n        }\n    } else if (o->type == OBJ_STREAM) {\n        /* Store how many listpacks we have inside the radix tree. */\n        stream *s = o->ptr;\n        rax *rax = s->rax;\n        if ((n = rdbSaveLen(rdb,raxSize(rax))) == -1) return -1;\n        nwritten += n;\n\n        /* Serialize all the listpacks inside the radix tree as they are,\n         * when loading back, we'll use the first entry of each listpack\n         * to insert it back into the radix tree. */\n        raxIterator ri;\n        raxStart(&ri,rax);\n        raxSeek(&ri,\"^\",NULL,0);\n        while (raxNext(&ri)) {\n            unsigned char *lp = ri.data;\n            size_t lp_bytes = lpBytes(lp);\n            if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;\n            nwritten += n;\n            if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) return -1;\n            nwritten += n;\n        }\n        raxStop(&ri);\n\n        /* Save the number of elements inside the stream. We cannot obtain\n         * this easily later, since our macro nodes should be checked for\n         * number of items: not a great CPU / space tradeoff. */\n        if ((n = rdbSaveLen(rdb,s->length)) == -1) return -1;\n        nwritten += n;\n        /* Save the last entry ID. */\n        if ((n = rdbSaveLen(rdb,s->last_id.ms)) == -1) return -1;\n        nwritten += n;\n        if ((n = rdbSaveLen(rdb,s->last_id.seq)) == -1) return -1;\n        nwritten += n;\n\n        /* The consumer groups and their clients are part of the stream\n         * type, so serialize every consumer group. */\n\n        /* Save the number of groups. */\n        size_t num_cgroups = s->cgroups ? raxSize(s->cgroups) : 0;\n        if ((n = rdbSaveLen(rdb,num_cgroups)) == -1) return -1;\n        nwritten += n;\n\n        if (num_cgroups) {\n            /* Serialize each consumer group. */\n            raxStart(&ri,s->cgroups);\n            raxSeek(&ri,\"^\",NULL,0);\n            while(raxNext(&ri)) {\n                streamCG *cg = ri.data;\n\n                /* Save the group name. */\n                if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1)\n                    return -1;\n                nwritten += n;\n\n                /* Last ID. */\n                if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) return -1;\n                nwritten += n;\n                if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) return -1;\n                nwritten += n;\n\n                /* Save the global PEL. */\n                if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) return -1;\n                nwritten += n;\n\n                /* Save the consumers of this group. */\n                if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) return -1;\n                nwritten += n;\n            }\n            raxStop(&ri);\n        }\n    } else if (o->type == OBJ_MODULE) {\n        /* Save a module-specific value. */\n        RedisModuleIO io;\n        moduleValue *mv = o->ptr;\n        moduleType *mt = mv->type;\n\n        /* Write the \"module\" identifier as prefix, so that we'll be able\n         * to call the right module during loading. */\n        int retval = rdbSaveLen(rdb,mt->id);\n        if (retval == -1) return -1;\n        io.bytes += retval;\n\n        /* Then write the module-specific representation + EOF marker. */\n        moduleInitIOContext(io,mt,rdb,key);\n        mt->rdb_save(&io,mv->value);\n        retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);\n        if (retval == -1)\n            io.error = 1;\n        else\n            io.bytes += retval;\n\n        if (io.ctx) {\n            moduleFreeContext(io.ctx);\n            zfree(io.ctx);\n        }\n        return io.error ? -1 : (ssize_t)io.bytes;\n    } else {\n        serverPanic(\"Unknown object type\");\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. */\nsize_t rdbSavedObjectLen(robj *o, robj *key) {\n    ssize_t len = rdbSaveObject(NULL,o,key);\n    serverAssertWithInfo(NULL,o,len != -1);\n    return len;\n}\n\n/* Save a key-value pair, with expire time, type, key, value.\n * On error -1 is returned.\n * On success if the key was actually saved 1 is returned, otherwise 0\n * is returned (the key was already expired). */\nint rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) {\n    int savelru = server.maxmemory_policy & MAXMEMORY_FLAG_LRU;\n    int savelfu = server.maxmemory_policy & MAXMEMORY_FLAG_LFU;\n\n    /* Save the expire time */\n    if (expiretime != -1) {\n        if (rdbSaveType(rdb,RDB_OPCODE_EXPIRETIME_MS) == -1) return -1;\n        if (rdbSaveMillisecondTime(rdb,expiretime) == -1) return -1;\n    }\n\n    /* Save the LRU info. */\n    if (savelru) {\n        uint64_t idletime = estimateObjectIdleTime(val);\n        idletime /= 1000; /* Using seconds is enough and requires less space.*/\n        if (rdbSaveType(rdb,RDB_OPCODE_IDLE) == -1) return -1;\n        if (rdbSaveLen(rdb,idletime) == -1) return -1;\n    }\n\n    /* Save the LFU info. */\n    if (savelfu) {\n        uint8_t buf[1];\n        buf[0] = LFUDecrAndReturn(val);\n        /* We can encode this in exactly two bytes: the opcode and an 8\n         * bit counter, since the frequency is logarithmic with a 0-255 range.\n         * Note that we do not store the halving time because to reset it\n         * a single time when loading does not affect the frequency much. */\n        if (rdbSaveType(rdb,RDB_OPCODE_FREQ) == -1) return -1;\n        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;\n    }\n\n    /* Save type, key, value */\n    if (rdbSaveObjectType(rdb,val) == -1) return -1;\n    if (rdbSaveStringObject(rdb,key) == -1) return -1;\n    if (rdbSaveObject(rdb,val,key) == -1) return -1;\n\n    /* Delay return if required (for testing) */\n    if (server.rdb_key_save_delay)\n        usleep(server.rdb_key_save_delay);\n\n    return 1;\n}\n\n/* Save an AUX field. */\nssize_t rdbSaveAuxField(rio *rdb, void *key, size_t keylen, void *val, size_t vallen) {\n    ssize_t ret, len = 0;\n    if ((ret = rdbSaveType(rdb,RDB_OPCODE_AUX)) == -1) return -1;\n    len += ret;\n    if ((ret = rdbSaveRawString(rdb,key,keylen)) == -1) return -1;\n    len += ret;\n    if ((ret = rdbSaveRawString(rdb,val,vallen)) == -1) return -1;\n    len += ret;\n    return len;\n}\n\n/* Wrapper for rdbSaveAuxField() used when key/val length can be obtained\n * with strlen(). */\nssize_t rdbSaveAuxFieldStrStr(rio *rdb, char *key, char *val) {\n    return rdbSaveAuxField(rdb,key,strlen(key),val,strlen(val));\n}\n\n/* Wrapper for strlen(key) + integer type (up to long long range). */\nssize_t rdbSaveAuxFieldStrInt(rio *rdb, char *key, long long val) {\n    char buf[LONG_STR_SIZE];\n    int vlen = ll2string(buf,sizeof(buf),val);\n    return rdbSaveAuxField(rdb,key,strlen(key),buf,vlen);\n}\n\n/* Save a few default AUX fields with information about the RDB generated. */\nint rdbSaveInfoAuxFields(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {\n    int redis_bits = (sizeof(void*) == 8) ? 64 : 32;\n    int aof_preamble = (rdbflags & RDBFLAGS_AOF_PREAMBLE) != 0;\n\n    /* Add a few fields about the state when the RDB was created. */\n    if (rdbSaveAuxFieldStrStr(rdb,\"redis-ver\",REDIS_VERSION) == -1) return -1;\n    if (rdbSaveAuxFieldStrInt(rdb,\"redis-bits\",redis_bits) == -1) return -1;\n    if (rdbSaveAuxFieldStrInt(rdb,\"ctime\",time(NULL)) == -1) return -1;\n    if (rdbSaveAuxFieldStrInt(rdb,\"used-mem\",zmalloc_used_memory()) == -1) return -1;\n\n    /* Handle saving options that generate aux fields. */\n    if (rsi) {\n        if (rdbSaveAuxFieldStrInt(rdb,\"repl-stream-db\",rsi->repl_stream_db)\n            == -1) return -1;\n        if (rdbSaveAuxFieldStrStr(rdb,\"repl-id\",server.replid)\n            == -1) return -1;\n        if (rdbSaveAuxFieldStrInt(rdb,\"repl-offset\",server.master_repl_offset)\n            == -1) return -1;\n    }\n    if (rdbSaveAuxFieldStrInt(rdb,\"aof-preamble\",aof_preamble) == -1) return -1;\n    return 1;\n}\n\nssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt) {\n    /* Save a module-specific aux value. */\n    RedisModuleIO io;\n    int retval = rdbSaveType(rdb, RDB_OPCODE_MODULE_AUX);\n\n    /* Write the \"module\" identifier as prefix, so that we'll be able\n     * to call the right module during loading. */\n    retval = rdbSaveLen(rdb,mt->id);\n    if (retval == -1) return -1;\n    io.bytes += retval;\n\n    /* write the 'when' so that we can provide it on loading. add a UINT opcode\n     * for backwards compatibility, everything after the MT needs to be prefixed\n     * by an opcode. */\n    retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_UINT);\n    if (retval == -1) return -1;\n    io.bytes += retval;\n    retval = rdbSaveLen(rdb,when);\n    if (retval == -1) return -1;\n    io.bytes += retval;\n\n    /* Then write the module-specific representation + EOF marker. */\n    moduleInitIOContext(io,mt,rdb,NULL);\n    mt->aux_save(&io,when);\n    retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);\n    if (retval == -1)\n        io.error = 1;\n    else\n        io.bytes += retval;\n\n    if (io.ctx) {\n        moduleFreeContext(io.ctx);\n        zfree(io.ctx);\n    }\n    if (io.error)\n        return -1;\n    return io.bytes;\n}\n\n/* Produces a dump of the database in RDB format sending it to the specified\n * Redis I/O channel. On success C_OK is returned, otherwise C_ERR\n * is returned and part of the output, or all the output, can be\n * missing because of I/O errors.\n *\n * When the function returns C_ERR and if 'error' is not NULL, the\n * integer pointed by 'error' is set to the value of errno just after the I/O\n * error. */\nint rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) {\n    dictIterator *di = NULL;\n    dictEntry *de;\n    char magic[10];\n    int j;\n    uint64_t cksum;\n    size_t processed = 0;\n\n    if (server.rdb_checksum)\n        rdb->update_cksum = rioGenericUpdateChecksum;\n    snprintf(magic,sizeof(magic),\"REDIS%04d\",RDB_VERSION);\n    if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;\n    if (rdbSaveInfoAuxFields(rdb,rdbflags,rsi) == -1) goto werr;\n    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_BEFORE_RDB) == -1) goto werr;\n\n    for (j = 0; j < server.dbnum; j++) {\n        redisDb *db = server.db+j;\n        dict *d = db->dict;\n        if (dictSize(d) == 0) continue;\n        di = dictGetSafeIterator(d);\n\n        /* Write the SELECT DB opcode */\n        if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;\n        if (rdbSaveLen(rdb,j) == -1) goto werr;\n\n        /* Write the RESIZE DB opcode. */\n        uint64_t db_size, expires_size;\n        db_size = dictSize(db->dict);\n        expires_size = dictSize(db->expires);\n        if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;\n        if (rdbSaveLen(rdb,db_size) == -1) goto werr;\n        if (rdbSaveLen(rdb,expires_size) == -1) goto werr;\n\n        /* Iterate this DB writing every entry */\n        while((de = dictNext(di)) != NULL) {\n            sds keystr = dictGetKey(de);\n            robj key, *o = dictGetVal(de);\n            long long expire;\n\n            initStaticStringObject(key,keystr);\n            expire = getExpire(db,&key);\n            if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;\n\n            /* When this RDB is produced as part of an AOF rewrite, move\n             * accumulated diff from parent to child while rewriting in\n             * order to have a smaller final write. */\n            if (rdbflags & RDBFLAGS_AOF_PREAMBLE &&\n                rdb->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES)\n            {\n                processed = rdb->processed_bytes;\n                aofReadDiffFromParent();\n            }\n        }\n        dictReleaseIterator(di);\n        di = NULL; /* So that we don't release it again on error. */\n    }\n\n    /* If we are storing the replication information on disk, persist\n     * the script cache as well: on successful PSYNC after a restart, we need\n     * to be able to process any EVALSHA inside the replication backlog the\n     * master will send us. */\n    if (rsi && dictSize(server.lua_scripts)) {\n        di = dictGetIterator(server.lua_scripts);\n        while((de = dictNext(di)) != NULL) {\n            robj *body = dictGetVal(de);\n            if (rdbSaveAuxField(rdb,\"lua\",3,body->ptr,sdslen(body->ptr)) == -1)\n                goto werr;\n        }\n        dictReleaseIterator(di);\n        di = NULL; /* So that we don't release it again on error. */\n    }\n\n    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_AFTER_RDB) == -1) goto werr;\n\n    /* EOF opcode */\n    if (rdbSaveType(rdb,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    cksum = rdb->cksum;\n    memrev64ifbe(&cksum);\n    if (rioWrite(rdb,&cksum,8) == 0) goto werr;\n    return C_OK;\n\nwerr:\n    if (error) *error = errno;\n    if (di) dictReleaseIterator(di);\n    return C_ERR;\n}\n\n/* This is just a wrapper to rdbSaveRio() that additionally adds a prefix\n * and a suffix to the generated RDB dump. The prefix is:\n *\n * $EOF:<40 bytes unguessable hex string>\\r\\n\n *\n * While the suffix is the 40 bytes hex string we announced in the prefix.\n * This way processes receiving the payload can understand when it ends\n * without doing any processing of the content. */\nint rdbSaveRioWithEOFMark(rio *rdb, int *error, rdbSaveInfo *rsi) {\n    char eofmark[RDB_EOF_MARK_SIZE];\n\n    startSaving(RDBFLAGS_REPLICATION);\n    getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);\n    if (error) *error = 0;\n    if (rioWrite(rdb,\"$EOF:\",5) == 0) goto werr;\n    if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;\n    if (rioWrite(rdb,\"\\r\\n\",2) == 0) goto werr;\n    if (rdbSaveRio(rdb,error,RDBFLAGS_NONE,rsi) == C_ERR) goto werr;\n    if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;\n    stopSaving(1);\n    return C_OK;\n\nwerr: /* Write error. */\n    /* Set 'error' only if not already set by rdbSaveRio() call. */\n    if (error && *error == 0) *error = errno;\n    stopSaving(0);\n    return C_ERR;\n}\n\n/* Save the DB on disk. Return C_ERR on error, C_OK on success. */\nint rdbSave(char *filename, rdbSaveInfo *rsi) {\n    char tmpfile[256];\n    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */\n    FILE *fp;\n    rio rdb;\n    int error = 0;\n\n    snprintf(tmpfile,256,\"temp-%d.rdb\", (int) getpid());\n    fp = fopen(tmpfile,\"w\");\n    if (!fp) {\n        char *cwdp = getcwd(cwd,MAXPATHLEN);\n        serverLog(LL_WARNING,\n            \"Failed opening the RDB file %s (in server root dir %s) \"\n            \"for saving: %s\",\n            filename,\n            cwdp ? cwdp : \"unknown\",\n            strerror(errno));\n        return C_ERR;\n    }\n\n    rioInitWithFile(&rdb,fp);\n    startSaving(RDBFLAGS_NONE);\n\n    if (server.rdb_save_incremental_fsync)\n        rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);\n\n    if (rdbSaveRio(&rdb,&error,RDBFLAGS_NONE,rsi) == C_ERR) {\n        errno = error;\n        goto werr;\n    }\n\n    /* Make sure data will not remain on the OS's output buffers */\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    if (rename(tmpfile,filename) == -1) {\n        char *cwdp = getcwd(cwd,MAXPATHLEN);\n        serverLog(LL_WARNING,\n            \"Error moving temp DB file %s on the final \"\n            \"destination %s (in server root dir %s): %s\",\n            tmpfile,\n            filename,\n            cwdp ? cwdp : \"unknown\",\n            strerror(errno));\n        unlink(tmpfile);\n        stopSaving(0);\n        return C_ERR;\n    }\n\n    serverLog(LL_NOTICE,\"DB saved on disk\");\n    server.dirty = 0;\n    server.lastsave = time(NULL);\n    server.lastbgsave_status = C_OK;\n    stopSaving(1);\n    return C_OK;\n\nwerr:\n    serverLog(LL_WARNING,\"Write error saving DB on disk: %s\", strerror(errno));\n    fclose(fp);\n    unlink(tmpfile);\n    stopSaving(0);\n    return C_ERR;\n}\n\nint rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {\n    pid_t childpid;\n\n    if (hasActiveChildProcess()) return C_ERR;\n\n    server.dirty_before_bgsave = server.dirty;\n    server.lastbgsave_try = time(NULL);\n    openChildInfoPipe();\n\n    if ((childpid = redisFork()) == 0) {\n        int retval;\n\n        /* Child */\n        redisSetProcTitle(\"redis-rdb-bgsave\");\n        redisSetCpuAffinity(server.bgsave_cpulist);\n        retval = rdbSave(filename,rsi);\n        if (retval == C_OK) {\n            sendChildCOWInfo(CHILD_INFO_TYPE_RDB, \"RDB\");\n        }\n        exitFromChild((retval == C_OK) ? 0 : 1);\n    } else {\n        /* Parent */\n        if (childpid == -1) {\n            closeChildInfoPipe();\n            server.lastbgsave_status = C_ERR;\n            serverLog(LL_WARNING,\"Can't save in background: fork: %s\",\n                strerror(errno));\n            return C_ERR;\n        }\n        serverLog(LL_NOTICE,\"Background saving started by pid %d\",childpid);\n        server.rdb_save_time_start = time(NULL);\n        server.rdb_child_pid = childpid;\n        server.rdb_child_type = RDB_CHILD_TYPE_DISK;\n        return C_OK;\n    }\n    return C_OK; /* unreached */\n}\n\nvoid rdbRemoveTempFile(pid_t childpid) {\n    char tmpfile[256];\n\n    snprintf(tmpfile,sizeof(tmpfile),\"temp-%d.rdb\", (int) childpid);\n    unlink(tmpfile);\n}\n\n/* This function is called by rdbLoadObject() when the code is in RDB-check\n * mode and we find a module value of type 2 that can be parsed without\n * the need of the actual module. The value is parsed for errors, finally\n * a dummy redis object is returned just to conform to the API. */\nrobj *rdbLoadCheckModuleValue(rio *rdb, char *modulename) {\n    uint64_t opcode;\n    while((opcode = rdbLoadLen(rdb,NULL)) != RDB_MODULE_OPCODE_EOF) {\n        if (opcode == RDB_MODULE_OPCODE_SINT ||\n            opcode == RDB_MODULE_OPCODE_UINT)\n        {\n            uint64_t len;\n            if (rdbLoadLenByRef(rdb,NULL,&len) == -1) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading integer from module %s value\", modulename);\n            }\n        } else if (opcode == RDB_MODULE_OPCODE_STRING) {\n            robj *o = rdbGenericLoadStringObject(rdb,RDB_LOAD_NONE,NULL);\n            if (o == NULL) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading string from module %s value\", modulename);\n            }\n            decrRefCount(o);\n        } else if (opcode == RDB_MODULE_OPCODE_FLOAT) {\n            float val;\n            if (rdbLoadBinaryFloatValue(rdb,&val) == -1) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading float from module %s value\", modulename);\n            }\n        } else if (opcode == RDB_MODULE_OPCODE_DOUBLE) {\n            double val;\n            if (rdbLoadBinaryDoubleValue(rdb,&val) == -1) {\n                rdbExitReportCorruptRDB(\n                    \"Error reading double from module %s value\", modulename);\n            }\n        }\n    }\n    return createStringObject(\"module-dummy-value\",18);\n}\n\n/* Load a Redis object of the specified type from the specified file.\n * On success a newly allocated object is returned, otherwise NULL. */\nrobj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {\n    robj *o = NULL, *ele, *dec;\n    uint64_t len;\n    unsigned int i;\n\n    if (rdbtype == RDB_TYPE_STRING) {\n        /* Read string value */\n        if ((o = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;\n        o = tryObjectEncoding(o);\n    } else if (rdbtype == RDB_TYPE_LIST) {\n        /* Read list value */\n        if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n\n        o = createQuicklistObject();\n        quicklistSetOptions(o->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n\n        /* Load every single element of the list */\n        while(len--) {\n            if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            dec = getDecodedObject(ele);\n            size_t len = sdslen(dec->ptr);\n            quicklistPushTail(o->ptr, dec->ptr, len);\n            decrRefCount(dec);\n            decrRefCount(ele);\n        }\n    } else if (rdbtype == RDB_TYPE_SET) {\n        /* Read Set value */\n        if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n\n        /* Use a regular set when there are too many entries. */\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 set */\n        for (i = 0; i < len; i++) {\n            long long llval;\n            sds sdsele;\n\n            if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n\n            if (o->encoding == OBJ_ENCODING_INTSET) {\n                /* Fetch integer value from element. */\n                if (isSdsRepresentableAsLongLong(sdsele,&llval) == C_OK) {\n                    o->ptr = intsetAdd(o->ptr,llval,NULL);\n                } else {\n                    setTypeConvert(o,OBJ_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            if (o->encoding == OBJ_ENCODING_HT) {\n                dictAdd((dict*)o->ptr,sdsele,NULL);\n            } else {\n                sdsfree(sdsele);\n            }\n        }\n    } else if (rdbtype == RDB_TYPE_ZSET_2 || rdbtype == RDB_TYPE_ZSET) {\n        /* Read list/set value. */\n        uint64_t zsetlen;\n        size_t maxelelen = 0;\n        zset *zs;\n\n        if ((zsetlen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n        o = createZsetObject();\n        zs = o->ptr;\n\n        if (zsetlen > DICT_HT_INITIAL_SIZE)\n            dictExpand(zs->dict,zsetlen);\n\n        /* Load every single element of the sorted set. */\n        while(zsetlen--) {\n            sds sdsele;\n            double score;\n            zskiplistNode *znode;\n\n            if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n\n            if (rdbtype == RDB_TYPE_ZSET_2) {\n                if (rdbLoadBinaryDoubleValue(rdb,&score) == -1) {\n                    decrRefCount(o);\n                    sdsfree(sdsele);\n                    return NULL;\n                }\n            } else {\n                if (rdbLoadDoubleValue(rdb,&score) == -1) {\n                    decrRefCount(o);\n                    sdsfree(sdsele);\n                    return NULL;\n                }\n            }\n\n            /* Don't care about integer-encoded strings. */\n            if (sdslen(sdsele) > maxelelen) maxelelen = sdslen(sdsele);\n\n            znode = zslInsert(zs->zsl,score,sdsele);\n            dictAdd(zs->dict,sdsele,&znode->score);\n        }\n\n        /* Convert *after* loading, since sorted sets are not stored ordered. */\n        if (zsetLength(o) <= server.zset_max_ziplist_entries &&\n            maxelelen <= server.zset_max_ziplist_value)\n                zsetConvert(o,OBJ_ENCODING_ZIPLIST);\n    } else if (rdbtype == RDB_TYPE_HASH) {\n        uint64_t len;\n        int ret;\n        sds field, value;\n\n        len = rdbLoadLen(rdb, NULL);\n        if (len == RDB_LENERR) return NULL;\n\n        o = createHashObject();\n\n        /* Too many entries? Use a hash table. */\n        if (len > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n\n        /* Load every field and value into the ziplist */\n        while (o->encoding == OBJ_ENCODING_ZIPLIST && len > 0) {\n            len--;\n            /* Load raw strings */\n            if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                sdsfree(field);\n                decrRefCount(o);\n                return NULL;\n            }\n\n            /* Add pair to ziplist */\n            o->ptr = ziplistPush(o->ptr, (unsigned char*)field,\n                    sdslen(field), ZIPLIST_TAIL);\n            o->ptr = ziplistPush(o->ptr, (unsigned char*)value,\n                    sdslen(value), ZIPLIST_TAIL);\n\n            /* Convert to hash table if size threshold is exceeded */\n            if (sdslen(field) > server.hash_max_ziplist_value ||\n                sdslen(value) > server.hash_max_ziplist_value)\n            {\n                sdsfree(field);\n                sdsfree(value);\n                hashTypeConvert(o, OBJ_ENCODING_HT);\n                break;\n            }\n            sdsfree(field);\n            sdsfree(value);\n        }\n\n        if (o->encoding == OBJ_ENCODING_HT && len > DICT_HT_INITIAL_SIZE)\n            dictExpand(o->ptr,len);\n\n        /* Load remaining fields and values into the hash table */\n        while (o->encoding == OBJ_ENCODING_HT && len > 0) {\n            len--;\n            /* Load encoded strings */\n            if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {\n                sdsfree(field);\n                decrRefCount(o);\n                return NULL;\n            }\n\n            /* Add pair to hash table */\n            ret = dictAdd((dict*)o->ptr, field, value);\n            if (ret == DICT_ERR) {\n                rdbExitReportCorruptRDB(\"Duplicate keys detected\");\n            }\n        }\n\n        /* All pairs should be read by now */\n        serverAssert(len == 0);\n    } else if (rdbtype == RDB_TYPE_LIST_QUICKLIST) {\n        if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;\n        o = createQuicklistObject();\n        quicklistSetOptions(o->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n\n        while (len--) {\n            unsigned char *zl =\n                rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);\n            if (zl == NULL) {\n                decrRefCount(o);\n                return NULL;\n            }\n            quicklistAppendZiplist(o->ptr, zl);\n        }\n    } else if (rdbtype == RDB_TYPE_HASH_ZIPMAP  ||\n               rdbtype == RDB_TYPE_LIST_ZIPLIST ||\n               rdbtype == RDB_TYPE_SET_INTSET   ||\n               rdbtype == RDB_TYPE_ZSET_ZIPLIST ||\n               rdbtype == RDB_TYPE_HASH_ZIPLIST)\n    {\n        unsigned char *encoded =\n            rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);\n        if (encoded == NULL) return NULL;\n        o = createObject(OBJ_STRING,encoded); /* Obj type fixed below. */\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        switch(rdbtype) {\n            case 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                    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                    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                    o->ptr = zl;\n                    o->type = OBJ_HASH;\n                    o->encoding = OBJ_ENCODING_ZIPLIST;\n\n                    if (hashTypeLength(o) > server.hash_max_ziplist_entries ||\n                        maxlen > server.hash_max_ziplist_value)\n                    {\n                        hashTypeConvert(o, OBJ_ENCODING_HT);\n                    }\n                }\n                break;\n            case RDB_TYPE_LIST_ZIPLIST:\n                o->type = OBJ_LIST;\n                o->encoding = OBJ_ENCODING_ZIPLIST;\n                listTypeConvert(o,OBJ_ENCODING_QUICKLIST);\n                break;\n            case RDB_TYPE_SET_INTSET:\n                o->type = OBJ_SET;\n                o->encoding = OBJ_ENCODING_INTSET;\n                if (intsetLen(o->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(o,OBJ_ENCODING_HT);\n                break;\n            case RDB_TYPE_ZSET_ZIPLIST:\n                o->type = OBJ_ZSET;\n                o->encoding = OBJ_ENCODING_ZIPLIST;\n                if (zsetLength(o) > server.zset_max_ziplist_entries)\n                    zsetConvert(o,OBJ_ENCODING_SKIPLIST);\n                break;\n            case RDB_TYPE_HASH_ZIPLIST:\n                o->type = OBJ_HASH;\n                o->encoding = OBJ_ENCODING_ZIPLIST;\n                if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n                    hashTypeConvert(o, OBJ_ENCODING_HT);\n                break;\n            default:\n                /* totally unreachable */\n                rdbExitReportCorruptRDB(\"Unknown RDB encoding type %d\",rdbtype);\n                break;\n        }\n    } else if (rdbtype == RDB_TYPE_STREAM_LISTPACKS) {\n        o = createStreamObject();\n        stream *s = o->ptr;\n        uint64_t listpacks = rdbLoadLen(rdb,NULL);\n        if (listpacks == RDB_LENERR) {\n            rdbReportReadError(\"Stream listpacks len loading failed.\");\n            decrRefCount(o);\n            return NULL;\n        }\n\n        while(listpacks--) {\n            /* Get the master ID, the one we'll use as key of the radix tree\n             * node: the entries inside the listpack itself are delta-encoded\n             * relatively to this ID. */\n            sds nodekey = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL);\n            if (nodekey == NULL) {\n                rdbReportReadError(\"Stream master ID loading failed: invalid encoding or I/O error.\");\n                decrRefCount(o);\n                return NULL;\n            }\n            if (sdslen(nodekey) != sizeof(streamID)) {\n                rdbExitReportCorruptRDB(\"Stream node key entry is not the \"\n                                        \"size of a stream ID\");\n            }\n\n            /* Load the listpack. */\n            unsigned char *lp =\n                rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);\n            if (lp == NULL) {\n                rdbReportReadError(\"Stream listpacks loading failed.\");\n                sdsfree(nodekey);\n                decrRefCount(o);\n                return NULL;\n            }\n            unsigned char *first = lpFirst(lp);\n            if (first == NULL) {\n                /* Serialized listpacks should never be empty, since on\n                 * deletion we should remove the radix tree key if the\n                 * resulting listpack is empty. */\n                rdbExitReportCorruptRDB(\"Empty listpack inside stream\");\n            }\n\n            /* Insert the key in the radix tree. */\n            int retval = raxInsert(s->rax,\n                (unsigned char*)nodekey,sizeof(streamID),lp,NULL);\n            sdsfree(nodekey);\n            if (!retval)\n                rdbExitReportCorruptRDB(\"Listpack re-added with existing key\");\n        }\n        /* Load total number of items inside the stream. */\n        s->length = rdbLoadLen(rdb,NULL);\n\n        /* Load the last entry ID. */\n        s->last_id.ms = rdbLoadLen(rdb,NULL);\n        s->last_id.seq = rdbLoadLen(rdb,NULL);\n\n        if (rioGetReadError(rdb)) {\n            rdbReportReadError(\"Stream object metadata loading failed.\");\n            decrRefCount(o);\n            return NULL;\n        }\n\n        /* Consumer groups loading */\n        uint64_t cgroups_count = rdbLoadLen(rdb,NULL);\n        if (cgroups_count == RDB_LENERR) {\n            rdbReportReadError(\"Stream cgroup count loading failed.\");\n            decrRefCount(o);\n            return NULL;\n        }\n        while(cgroups_count--) {\n            /* Get the consumer group name and ID. We can then create the\n             * consumer group ASAP and populate its structure as\n             * we read more data. */\n            streamID cg_id;\n            sds cgname = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL);\n            if (cgname == NULL) {\n                rdbReportReadError(\n                    \"Error reading the consumer group name from Stream\");\n                decrRefCount(o);\n                return NULL;\n            }\n\n            cg_id.ms = rdbLoadLen(rdb,NULL);\n            cg_id.seq = rdbLoadLen(rdb,NULL);\n            if (rioGetReadError(rdb)) {\n                rdbReportReadError(\"Stream cgroup ID loading failed.\");\n                sdsfree(cgname);\n                decrRefCount(o);\n                return NULL;\n            }\n\n            streamCG *cgroup = streamCreateCG(s,cgname,sdslen(cgname),&cg_id);\n            if (cgroup == NULL)\n                rdbExitReportCorruptRDB(\"Duplicated consumer group name %s\",\n                                         cgname);\n            sdsfree(cgname);\n\n            /* Load the global PEL for this consumer group, however we'll\n             * not yet populate the NACK structures with the message\n             * owner, since consumers for this group and their messages will\n             * be read as a next step. So for now leave them not resolved\n             * and later populate it. */\n            uint64_t pel_size = rdbLoadLen(rdb,NULL);\n            if (pel_size == RDB_LENERR) {\n                rdbReportReadError(\"Stream PEL size loading failed.\");\n                decrRefCount(o);\n                return NULL;\n            }\n            while(pel_size--) {\n                unsigned char rawid[sizeof(streamID)];\n                if (rioRead(rdb,rawid,sizeof(rawid)) == 0) {\n                    rdbReportReadError(\"Stream PEL ID loading failed.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n                streamNACK *nack = streamCreateNACK(NULL);\n                nack->delivery_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);\n                nack->delivery_count = rdbLoadLen(rdb,NULL);\n                if (rioGetReadError(rdb)) {\n                    rdbReportReadError(\"Stream PEL NACK loading failed.\");\n                    decrRefCount(o);\n                    streamFreeNACK(nack);\n                    return NULL;\n                }\n                if (!raxInsert(cgroup->pel,rawid,sizeof(rawid),nack,NULL))\n                    rdbExitReportCorruptRDB(\"Duplicated gobal PEL entry \"\n                                            \"loading stream consumer group\");\n            }\n\n            /* Now that we loaded our global PEL, we need to load the\n             * consumers and their local PELs. */\n            uint64_t consumers_num = rdbLoadLen(rdb,NULL);\n            if (consumers_num == RDB_LENERR) {\n                rdbReportReadError(\"Stream consumers num loading failed.\");\n                decrRefCount(o);\n                return NULL;\n            }\n            while(consumers_num--) {\n                sds cname = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL);\n                if (cname == NULL) {\n                    rdbReportReadError(\n                        \"Error reading the consumer name from Stream group.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n                streamConsumer *consumer =\n                    streamLookupConsumer(cgroup,cname,SLC_NONE);\n                sdsfree(cname);\n                consumer->seen_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);\n                if (rioGetReadError(rdb)) {\n                    rdbReportReadError(\"Stream short read reading seen time.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n\n                /* Load the PEL about entries owned by this specific\n                 * consumer. */\n                pel_size = rdbLoadLen(rdb,NULL);\n                if (pel_size == RDB_LENERR) {\n                    rdbReportReadError(\n                        \"Stream consumer PEL num loading failed.\");\n                    decrRefCount(o);\n                    return NULL;\n                }\n                while(pel_size--) {\n                    unsigned char rawid[sizeof(streamID)];\n                    if (rioRead(rdb,rawid,sizeof(rawid)) == 0) {\n                        rdbReportReadError(\n                            \"Stream short read reading PEL streamID.\");\n                        decrRefCount(o);\n                        return NULL;\n                    }\n                    streamNACK *nack = raxFind(cgroup->pel,rawid,sizeof(rawid));\n                    if (nack == raxNotFound)\n                        rdbExitReportCorruptRDB(\"Consumer entry not found in \"\n                                                \"group global PEL\");\n\n                    /* Set the NACK consumer, that was left to NULL when\n                     * loading the global PEL. Then set the same shared\n                     * NACK structure also in the consumer-specific PEL. */\n                    nack->consumer = consumer;\n                    if (!raxInsert(consumer->pel,rawid,sizeof(rawid),nack,NULL))\n                        rdbExitReportCorruptRDB(\"Duplicated consumer PEL entry \"\n                                                \" loading a stream consumer \"\n                                                \"group\");\n                }\n            }\n        }\n    } else if (rdbtype == RDB_TYPE_MODULE || rdbtype == RDB_TYPE_MODULE_2) {\n        uint64_t moduleid = rdbLoadLen(rdb,NULL);\n        if (rioGetReadError(rdb)) {\n            rdbReportReadError(\"Short read module id\");\n            return NULL;\n        }\n        moduleType *mt = moduleTypeLookupModuleByID(moduleid);\n        char name[10];\n\n        if (rdbCheckMode && rdbtype == RDB_TYPE_MODULE_2) {\n            moduleTypeNameByID(name,moduleid);\n            return rdbLoadCheckModuleValue(rdb,name);\n        }\n\n        if (mt == NULL) {\n            moduleTypeNameByID(name,moduleid);\n            serverLog(LL_WARNING,\"The RDB file contains module data I can't load: no matching module '%s'\", name);\n            exit(1);\n        }\n        RedisModuleIO io;\n        robj keyobj;\n        initStaticStringObject(keyobj,key);\n        moduleInitIOContext(io,mt,rdb,&keyobj);\n        io.ver = (rdbtype == RDB_TYPE_MODULE) ? 1 : 2;\n        /* Call the rdb_load method of the module providing the 10 bit\n         * encoding version in the lower 10 bits of the module ID. */\n        void *ptr = mt->rdb_load(&io,moduleid&1023);\n        if (io.ctx) {\n            moduleFreeContext(io.ctx);\n            zfree(io.ctx);\n        }\n\n        /* Module v2 serialization has an EOF mark at the end. */\n        if (io.ver == 2) {\n            uint64_t eof = rdbLoadLen(rdb,NULL);\n            if (eof == RDB_LENERR) {\n                o = createModuleObject(mt,ptr); /* creating just in order to easily destroy */\n                decrRefCount(o);\n                return NULL;\n            }\n            if (eof != RDB_MODULE_OPCODE_EOF) {\n                serverLog(LL_WARNING,\"The RDB file contains module data for the module '%s' that is not terminated by the proper module value EOF marker\", name);\n                exit(1);\n            }\n        }\n\n        if (ptr == NULL) {\n            moduleTypeNameByID(name,moduleid);\n            serverLog(LL_WARNING,\"The RDB file contains module data for the module type '%s', that the responsible module is not able to load. Check for modules log above for additional clues.\", name);\n            exit(1);\n        }\n        o = createModuleObject(mt,ptr);\n    } else {\n        rdbReportReadError(\"Unknown RDB encoding type %d\",rdbtype);\n        return NULL;\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. */\nvoid startLoading(size_t size, int rdbflags) {\n    /* Load the DB */\n    server.loading = 1;\n    server.loading_start_time = time(NULL);\n    server.loading_loaded_bytes = 0;\n    server.loading_total_bytes = size;\n\n    /* Fire the loading modules start event. */\n    int subevent;\n    if (rdbflags & RDBFLAGS_AOF_PREAMBLE)\n        subevent = REDISMODULE_SUBEVENT_LOADING_AOF_START;\n    else if(rdbflags & RDBFLAGS_REPLICATION)\n        subevent = REDISMODULE_SUBEVENT_LOADING_REPL_START;\n    else\n        subevent = REDISMODULE_SUBEVENT_LOADING_RDB_START;\n    moduleFireServerEvent(REDISMODULE_EVENT_LOADING,subevent,NULL);\n}\n\n/* Mark that we are loading in the global state and setup the fields\n * needed to provide loading stats.\n * 'filename' is optional and used for rdb-check on error */\nvoid startLoadingFile(FILE *fp, char* filename, int rdbflags) {\n    struct stat sb;\n    if (fstat(fileno(fp), &sb) == -1)\n        sb.st_size = 0;\n    rdbFileBeingLoaded = filename;\n    startLoading(sb.st_size, rdbflags);\n}\n\n/* Refresh the loading progress info */\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 */\nvoid stopLoading(int success) {\n    server.loading = 0;\n    rdbFileBeingLoaded = NULL;\n\n    /* Fire the loading modules end event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_LOADING,\n                          success?\n                            REDISMODULE_SUBEVENT_LOADING_ENDED:\n                            REDISMODULE_SUBEVENT_LOADING_FAILED,\n                          NULL);\n}\n\nvoid startSaving(int rdbflags) {\n    /* Fire the persistence modules end event. */\n    int subevent;\n    if (rdbflags & RDBFLAGS_AOF_PREAMBLE)\n        subevent = REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START;\n    else if (getpid()!=server.pid)\n        subevent = REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START;\n    else\n        subevent = REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START;\n    moduleFireServerEvent(REDISMODULE_EVENT_PERSISTENCE,subevent,NULL);\n}\n\nvoid stopSaving(int success) {\n    /* Fire the persistence modules end event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_PERSISTENCE,\n                          success?\n                            REDISMODULE_SUBEVENT_PERSISTENCE_ENDED:\n                            REDISMODULE_SUBEVENT_PERSISTENCE_FAILED,\n                          NULL);\n}\n\n/* Track loading progress in order to serve client's from time to time\n   and if needed calculate rdb checksum  */\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(0);\n        if (server.masterhost && server.repl_state == REPL_STATE_TRANSFER)\n            replicationSendNewlineToMaster();\n        loadingProgress(r->processed_bytes);\n        processEventsWhileBlocked();\n        processModuleLoadingProgressEvent(0);\n    }\n}\n\n/* Load an RDB file from the rio stream 'rdb'. On success C_OK is returned,\n * otherwise C_ERR is returned and 'errno' is set accordingly. */\nint rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {\n    uint64_t dbid;\n    int type, rdbver;\n    redisDb *db = server.db+0;\n    char buf[1024];\n\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    if (memcmp(buf,\"REDIS\",5) != 0) {\n        serverLog(LL_WARNING,\"Wrong signature trying to load DB from file\");\n        errno = EINVAL;\n        return C_ERR;\n    }\n    rdbver = atoi(buf+5);\n    if (rdbver < 1 || rdbver > RDB_VERSION) {\n        serverLog(LL_WARNING,\"Can't handle RDB format version %d\",rdbver);\n        errno = EINVAL;\n        return C_ERR;\n    }\n\n    /* Key-specific attributes, set by opcodes before the key type. */\n    long long lru_idle = -1, lfu_freq = -1, expiretime = -1, now = mstime();\n    long long lru_clock = LRU_CLOCK();\n\n    while(1) {\n        sds key;\n        robj *val;\n\n        /* Read type. */\n        if ((type = rdbLoadType(rdb)) == -1) goto eoferr;\n\n        /* Handle special types. */\n        if (type == RDB_OPCODE_EXPIRETIME) {\n            /* EXPIRETIME: load an expire associated with the next key\n             * to load. Note that after loading an expire we need to\n             * load the actual type, and continue. */\n            expiretime = rdbLoadTime(rdb);\n            expiretime *= 1000;\n            if (rioGetReadError(rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EXPIRETIME_MS) {\n            /* EXPIRETIME_MS: milliseconds precision expire times introduced\n             * with RDB v3. Like EXPIRETIME but no with more precision. */\n            expiretime = rdbLoadMillisecondTime(rdb,rdbver);\n            if (rioGetReadError(rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_FREQ) {\n            /* FREQ: LFU frequency. */\n            uint8_t byte;\n            if (rioRead(rdb,&byte,1) == 0) goto eoferr;\n            lfu_freq = byte;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_IDLE) {\n            /* IDLE: LRU idle time. */\n            uint64_t qword;\n            if ((qword = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr;\n            lru_idle = qword;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EOF) {\n            /* EOF: End of file, exit the main loop. */\n            break;\n        } else if (type == RDB_OPCODE_SELECTDB) {\n            /* SELECTDB: Select the specified database. */\n            if ((dbid = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr;\n            if (dbid >= (unsigned)server.dbnum) {\n                serverLog(LL_WARNING,\n                    \"FATAL: Data file was created with a Redis \"\n                    \"server configured to handle more than %d \"\n                    \"databases. Exiting\\n\", server.dbnum);\n                exit(1);\n            }\n            db = server.db+dbid;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_RESIZEDB) {\n            /* RESIZEDB: Hint about the size of the keys in the currently\n             * selected data base, in order to avoid useless rehashing. */\n            uint64_t db_size, expires_size;\n            if ((db_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            if ((expires_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            dictExpand(db->dict,db_size);\n            dictExpand(db->expires,expires_size);\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_AUX) {\n            /* AUX: generic string-string fields. Use to add state to RDB\n             * which is backward compatible. Implementations of RDB loading\n             * are requierd to skip AUX fields they don't understand.\n             *\n             * An AUX field is composed of two strings: key and value. */\n            robj *auxkey, *auxval;\n            if ((auxkey = rdbLoadStringObject(rdb)) == NULL) goto eoferr;\n            if ((auxval = rdbLoadStringObject(rdb)) == NULL) goto eoferr;\n\n            if (((char*)auxkey->ptr)[0] == '%') {\n                /* All the fields with a name staring with '%' are considered\n                 * information fields and are logged at startup with a log\n                 * level of NOTICE. */\n                serverLog(LL_NOTICE,\"RDB '%s': %s\",\n                    (char*)auxkey->ptr,\n                    (char*)auxval->ptr);\n            } else if (!strcasecmp(auxkey->ptr,\"repl-stream-db\")) {\n                if (rsi) rsi->repl_stream_db = atoi(auxval->ptr);\n            } else if (!strcasecmp(auxkey->ptr,\"repl-id\")) {\n                if (rsi && sdslen(auxval->ptr) == CONFIG_RUN_ID_SIZE) {\n                    memcpy(rsi->repl_id,auxval->ptr,CONFIG_RUN_ID_SIZE+1);\n                    rsi->repl_id_is_set = 1;\n                }\n            } else if (!strcasecmp(auxkey->ptr,\"repl-offset\")) {\n                if (rsi) rsi->repl_offset = strtoll(auxval->ptr,NULL,10);\n            } else if (!strcasecmp(auxkey->ptr,\"lua\")) {\n                /* Load the script back in memory. */\n                if (luaCreateFunction(NULL,server.lua,auxval) == NULL) {\n                    rdbExitReportCorruptRDB(\n                        \"Can't load Lua script from RDB file! \"\n                        \"BODY: %s\", auxval->ptr);\n                }\n            } else if (!strcasecmp(auxkey->ptr,\"redis-ver\")) {\n                serverLog(LL_NOTICE,\"Loading RDB produced by version %s\",\n                    (char*)auxval->ptr);\n            } else if (!strcasecmp(auxkey->ptr,\"ctime\")) {\n                time_t age = time(NULL)-strtol(auxval->ptr,NULL,10);\n                if (age < 0) age = 0;\n                serverLog(LL_NOTICE,\"RDB age %ld seconds\",\n                    (unsigned long) age);\n            } else if (!strcasecmp(auxkey->ptr,\"used-mem\")) {\n                long long usedmem = strtoll(auxval->ptr,NULL,10);\n                serverLog(LL_NOTICE,\"RDB memory usage when created %.2f Mb\",\n                    (double) usedmem / (1024*1024));\n            } else if (!strcasecmp(auxkey->ptr,\"aof-preamble\")) {\n                long long haspreamble = strtoll(auxval->ptr,NULL,10);\n                if (haspreamble) serverLog(LL_NOTICE,\"RDB has an AOF tail\");\n            } else if (!strcasecmp(auxkey->ptr,\"redis-bits\")) {\n                /* Just ignored. */\n            } else {\n                /* We ignore fields we don't understand, as by AUX field\n                 * contract. */\n                serverLog(LL_DEBUG,\"Unrecognized RDB AUX field: '%s'\",\n                    (char*)auxkey->ptr);\n            }\n\n            decrRefCount(auxkey);\n            decrRefCount(auxval);\n            continue; /* Read type again. */\n        } else if (type == RDB_OPCODE_MODULE_AUX) {\n            /* Load module data that is not related to the Redis key space.\n             * Such data can be potentially be stored both before and after the\n             * RDB keys-values section. */\n            uint64_t moduleid = rdbLoadLen(rdb,NULL);\n            int when_opcode = rdbLoadLen(rdb,NULL);\n            int when = rdbLoadLen(rdb,NULL);\n            if (rioGetReadError(rdb)) goto eoferr;\n            if (when_opcode != RDB_MODULE_OPCODE_UINT)\n                rdbReportReadError(\"bad when_opcode\");\n            moduleType *mt = moduleTypeLookupModuleByID(moduleid);\n            char name[10];\n            moduleTypeNameByID(name,moduleid);\n\n            if (!rdbCheckMode && mt == NULL) {\n                /* Unknown module. */\n                serverLog(LL_WARNING,\"The RDB file contains AUX module data I can't load: no matching module '%s'\", name);\n                exit(1);\n            } else if (!rdbCheckMode && mt != NULL) {\n                if (!mt->aux_load) {\n                    /* Module doesn't support AUX. */\n                    serverLog(LL_WARNING,\"The RDB file contains module AUX data, but the module '%s' doesn't seem to support it.\", name);\n                    exit(1);\n                }\n\n                RedisModuleIO io;\n                moduleInitIOContext(io,mt,rdb,NULL);\n                io.ver = 2;\n                /* Call the rdb_load method of the module providing the 10 bit\n                 * encoding version in the lower 10 bits of the module ID. */\n                if (mt->aux_load(&io,moduleid&1023, when) != REDISMODULE_OK || io.error) {\n                    moduleTypeNameByID(name,moduleid);\n                    serverLog(LL_WARNING,\"The RDB file contains module AUX data for the module type '%s', that the responsible module is not able to load. Check for modules log above for additional clues.\", name);\n                    exit(1);\n                }\n                if (io.ctx) {\n                    moduleFreeContext(io.ctx);\n                    zfree(io.ctx);\n                }\n                uint64_t eof = rdbLoadLen(rdb,NULL);\n                if (eof != RDB_MODULE_OPCODE_EOF) {\n                    serverLog(LL_WARNING,\"The RDB file contains module AUX data for the module '%s' that is not terminated by the proper module value EOF marker\", name);\n                    exit(1);\n                }\n                continue;\n            } else {\n                /* RDB check mode. */\n                robj *aux = rdbLoadCheckModuleValue(rdb,name);\n                decrRefCount(aux);\n                continue; /* Read next opcode. */\n            }\n        }\n\n        /* Read key */\n        if ((key = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL)\n            goto eoferr;\n        /* Read value */\n        if ((val = rdbLoadObject(type,rdb,key)) == NULL) {\n            sdsfree(key);\n            goto eoferr;\n        }\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         * Similarly if the RDB is the preamble of an AOF file, we want to\n         * load all the keys as they are, since the log of operations later\n         * assume to work in an exact keyspace state. */\n        if (iAmMaster() &&\n            !(rdbflags&RDBFLAGS_AOF_PREAMBLE) &&\n            expiretime != -1 && expiretime < now)\n        {\n            sdsfree(key);\n            decrRefCount(val);\n        } else {\n            robj keyobj;\n\n            /* Add the new object in the hash table */\n            int added = dbAddRDBLoad(db,key,val);\n            if (!added) {\n                if (rdbflags & RDBFLAGS_ALLOW_DUP) {\n                    /* This flag is useful for DEBUG RELOAD special modes.\n                     * When it's set we allow new keys to replace the current\n                     * keys with the same name. */\n                    initStaticStringObject(keyobj,key);\n                    dbSyncDelete(db,&keyobj);\n                    dbAddRDBLoad(db,key,val);\n                } else {\n                    serverLog(LL_WARNING,\n                        \"RDB has duplicated key '%s' in DB %d\",key,db->id);\n                    serverPanic(\"Duplicated key found in RDB file\");\n                }\n            }\n\n            /* Set the expire time if needed */\n            if (expiretime != -1) {\n                initStaticStringObject(keyobj,key);\n                setExpire(NULL,db,&keyobj,expiretime);\n            }\n\n            /* Set usage information (for eviction). */\n            objectSetLRUOrLFU(val,lfu_freq,lru_idle,lru_clock,1000);\n        }\n\n        /* Loading the database more slowly is useful in order to test\n         * certain edge cases. */\n        if (server.key_load_delay) usleep(server.key_load_delay);\n\n        /* Reset the state that is key-specified and is populated by\n         * opcodes before the key, so that we start from scratch again. */\n        expiretime = -1;\n        lfu_freq = -1;\n        lru_idle = -1;\n    }\n    /* Verify the checksum if RDB version is >= 5 */\n    if (rdbver >= 5) {\n        uint64_t cksum, expected = rdb->cksum;\n\n        if (rioRead(rdb,&cksum,8) == 0) goto eoferr;\n        if (server.rdb_checksum) {\n            memrev64ifbe(&cksum);\n            if (cksum == 0) {\n                serverLog(LL_WARNING,\"RDB file was saved with checksum disabled: no check performed.\");\n            } else if (cksum != expected) {\n                serverLog(LL_WARNING,\"Wrong RDB checksum expected: (%llx) but \"\n                    \"got (%llx). Aborting now.\",\n                        (unsigned long long)expected,\n                        (unsigned long long)cksum);\n                rdbExitReportCorruptRDB(\"RDB CRC error\");\n            }\n        }\n    }\n    return C_OK;\n\n    /* Unexpected end of file is handled here calling rdbReportReadError():\n     * this will in turn either abort Redis in most cases, or if we are loading\n     * the RDB file from a socket during initial SYNC (diskless replica mode),\n     * we'll report the error to the caller, so that we can retry. */\neoferr:\n    serverLog(LL_WARNING,\n        \"Short read or OOM loading DB. Unrecoverable error, aborting now.\");\n    rdbReportReadError(\"Unexpected EOF reading RDB file\");\n    return C_ERR;\n}\n\n/* Like rdbLoadRio() but takes a filename instead of a rio stream. The\n * filename is open for reading and a rio stream object created in order\n * to do the actual loading. Moreover the ETA displayed in the INFO\n * output is initialized and finalized.\n *\n * If you pass an 'rsi' structure initialied with RDB_SAVE_OPTION_INIT, the\n * loading code will fiil the information fields in the structure. */\nint rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags) {\n    FILE *fp;\n    rio rdb;\n    int retval;\n\n    if ((fp = fopen(filename,\"r\")) == NULL) return C_ERR;\n    startLoadingFile(fp, filename,rdbflags);\n    rioInitWithFile(&rdb,fp);\n    retval = rdbLoadRio(&rdb,rdbflags,rsi);\n    fclose(fp);\n    stopLoading(retval==C_OK);\n    return retval;\n}\n\n/* A background saving child (BGSAVE) terminated its work. Handle this.\n * This function covers the case of actual BGSAVEs. */\nvoid backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {\n    if (!bysignal && exitcode == 0) {\n        serverLog(LL_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 = C_OK;\n    } else if (!bysignal && exitcode != 0) {\n        serverLog(LL_WARNING, \"Background saving error\");\n        server.lastbgsave_status = C_ERR;\n    } else {\n        mstime_t latency;\n\n        serverLog(LL_WARNING,\n            \"Background saving terminated by signal %d\", bysignal);\n        latencyStartMonitor(latency);\n        rdbRemoveTempFile(server.rdb_child_pid);\n        latencyEndMonitor(latency);\n        latencyAddSampleIfNeeded(\"rdb-unlink-temp-file\",latency);\n        /* SIGUSR1 is whitelisted, so we have a way to kill a child without\n         * tirggering an error condition. */\n        if (bysignal != SIGUSR1)\n            server.lastbgsave_status = C_ERR;\n    }\n    server.rdb_child_pid = -1;\n    server.rdb_child_type = RDB_CHILD_TYPE_NONE;\n    server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;\n    server.rdb_save_time_start = -1;\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    updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? C_OK : C_ERR, RDB_CHILD_TYPE_DISK);\n}\n\n/* A background saving child (BGSAVE) terminated its work. Handle this.\n * This function covers the case of RDB -> Slaves socket transfers for\n * diskless replication. */\nvoid backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {\n    if (!bysignal && exitcode == 0) {\n        serverLog(LL_NOTICE,\n            \"Background RDB transfer terminated with success\");\n    } else if (!bysignal && exitcode != 0) {\n        serverLog(LL_WARNING, \"Background transfer error\");\n    } else {\n        serverLog(LL_WARNING,\n            \"Background transfer terminated by signal %d\", bysignal);\n    }\n    server.rdb_child_pid = -1;\n    server.rdb_child_type = RDB_CHILD_TYPE_NONE;\n    server.rdb_save_time_start = -1;\n\n    updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? C_OK : C_ERR, RDB_CHILD_TYPE_SOCKET);\n}\n\n/* When a background RDB saving/transfer terminates, call the right handler. */\nvoid backgroundSaveDoneHandler(int exitcode, int bysignal) {\n    switch(server.rdb_child_type) {\n    case RDB_CHILD_TYPE_DISK:\n        backgroundSaveDoneHandlerDisk(exitcode,bysignal);\n        break;\n    case RDB_CHILD_TYPE_SOCKET:\n        backgroundSaveDoneHandlerSocket(exitcode,bysignal);\n        break;\n    default:\n        serverPanic(\"Unknown RDB child type.\");\n        break;\n    }\n}\n\n/* Kill the RDB saving child using SIGUSR1 (so that the parent will know\n * the child did not exit for an error, but because we wanted), and performs\n * the cleanup needed. */\nvoid killRDBChild(void) {\n    kill(server.rdb_child_pid,SIGUSR1);\n    rdbRemoveTempFile(server.rdb_child_pid);\n    closeChildInfoPipe();\n    updateDictResizePolicy();\n}\n\n/* Spawn an RDB child that writes the RDB to the sockets of the slaves\n * that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */\nint rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {\n    listNode *ln;\n    listIter li;\n    pid_t childpid;\n    int pipefds[2];\n\n    if (hasActiveChildProcess()) return C_ERR;\n\n    /* Even if the previous fork child exited, don't start a new one until we\n     * drained the pipe. */\n    if (server.rdb_pipe_conns) return C_ERR;\n\n    /* Before to fork, create a pipe that is used to transfer the rdb bytes to\n     * the parent, we can't let it write directly to the sockets, since in case\n     * of TLS we must let the parent handle a continuous TLS state when the\n     * child terminates and parent takes over. */\n    if (pipe(pipefds) == -1) return C_ERR;\n    server.rdb_pipe_read = pipefds[0];\n    server.rdb_pipe_write = pipefds[1];\n    anetNonBlock(NULL, server.rdb_pipe_read);\n\n    /* Collect the connections of the replicas we want to transfer\n     * the RDB to, which are i WAIT_BGSAVE_START state. */\n    server.rdb_pipe_conns = zmalloc(sizeof(connection *)*listLength(server.slaves));\n    server.rdb_pipe_numconns = 0;\n    server.rdb_pipe_numconns_writing = 0;\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n            server.rdb_pipe_conns[server.rdb_pipe_numconns++] = slave->conn;\n            replicationSetupSlaveForFullResync(slave,getPsyncInitialOffset());\n        }\n    }\n\n    /* Create the child process. */\n    openChildInfoPipe();\n    if ((childpid = redisFork()) == 0) {\n        /* Child */\n        int retval;\n        rio rdb;\n\n        rioInitWithFd(&rdb,server.rdb_pipe_write);\n\n        redisSetProcTitle(\"redis-rdb-to-slaves\");\n        redisSetCpuAffinity(server.bgsave_cpulist);\n\n        retval = rdbSaveRioWithEOFMark(&rdb,NULL,rsi);\n        if (retval == C_OK && rioFlush(&rdb) == 0)\n            retval = C_ERR;\n\n        if (retval == C_OK) {\n            sendChildCOWInfo(CHILD_INFO_TYPE_RDB, \"RDB\");\n        }\n\n        rioFreeFd(&rdb);\n        close(server.rdb_pipe_write); /* wake up the reader, tell it we're done. */\n        exitFromChild((retval == C_OK) ? 0 : 1);\n    } else {\n        /* Parent */\n        if (childpid == -1) {\n            serverLog(LL_WARNING,\"Can't save in background: fork: %s\",\n                strerror(errno));\n\n            /* Undo the state change. The caller will perform cleanup on\n             * all the slaves in BGSAVE_START state, but an early call to\n             * replicationSetupSlaveForFullResync() turned it into BGSAVE_END */\n            listRewind(server.slaves,&li);\n            while((ln = listNext(&li))) {\n                client *slave = ln->value;\n                if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {\n                    slave->replstate = SLAVE_STATE_WAIT_BGSAVE_START;\n                }\n            }\n            close(server.rdb_pipe_write);\n            close(server.rdb_pipe_read);\n            zfree(server.rdb_pipe_conns);\n            server.rdb_pipe_conns = NULL;\n            server.rdb_pipe_numconns = 0;\n            server.rdb_pipe_numconns_writing = 0;\n            closeChildInfoPipe();\n        } else {\n            serverLog(LL_NOTICE,\"Background RDB transfer started by pid %d\",\n                childpid);\n            server.rdb_save_time_start = time(NULL);\n            server.rdb_child_pid = childpid;\n            server.rdb_child_type = RDB_CHILD_TYPE_SOCKET;\n            close(server.rdb_pipe_write); /* close write in parent so that it can detect the close on the child. */\n            if (aeCreateFileEvent(server.el, server.rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {\n                serverPanic(\"Unrecoverable error creating server.rdb_pipe_read file event.\");\n            }\n        }\n        return (childpid == -1) ? C_ERR : C_OK;\n    }\n    return C_OK; /* Unreached. */\n}\n\nvoid saveCommand(client *c) {\n    if (server.rdb_child_pid != -1) {\n        addReplyError(c,\"Background save already in progress\");\n        return;\n    }\n    rdbSaveInfo rsi, *rsiptr;\n    rsiptr = rdbPopulateSaveInfo(&rsi);\n    if (rdbSave(server.rdb_filename,rsiptr) == C_OK) {\n        addReply(c,shared.ok);\n    } else {\n        addReply(c,shared.err);\n    }\n}\n\n/* BGSAVE [SCHEDULE] */\nvoid bgsaveCommand(client *c) {\n    int schedule = 0;\n\n    /* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite\n     * is in progress. Instead of returning an error a BGSAVE gets scheduled. */\n    if (c->argc > 1) {\n        if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"schedule\")) {\n            schedule = 1;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    rdbSaveInfo rsi, *rsiptr;\n    rsiptr = rdbPopulateSaveInfo(&rsi);\n\n    if (server.rdb_child_pid != -1) {\n        addReplyError(c,\"Background save already in progress\");\n    } else if (hasActiveChildProcess()) {\n        if (schedule) {\n            server.rdb_bgsave_scheduled = 1;\n            addReplyStatus(c,\"Background saving scheduled\");\n        } else {\n            addReplyError(c,\n            \"Another child process is active (AOF?): can't BGSAVE right now. \"\n            \"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever \"\n            \"possible.\");\n        }\n    } else if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK) {\n        addReplyStatus(c,\"Background saving started\");\n    } else {\n        addReply(c,shared.err);\n    }\n}\n\n/* Populate the rdbSaveInfo structure used to persist the replication\n * information inside the RDB file. Currently the structure explicitly\n * contains just the currently selected DB from the master stream, however\n * if the rdbSave*() family functions receive a NULL rsi structure also\n * the Replication ID/offset is not saved. The function popultes 'rsi'\n * that is normally stack-allocated in the caller, returns the populated\n * pointer if the instance has a valid master client, otherwise NULL\n * is returned, and the RDB saving will not persist any replication related\n * information. */\nrdbSaveInfo *rdbPopulateSaveInfo(rdbSaveInfo *rsi) {\n    rdbSaveInfo rsi_init = RDB_SAVE_INFO_INIT;\n    *rsi = rsi_init;\n\n    /* If the instance is a master, we can populate the replication info\n     * only when repl_backlog is not NULL. If the repl_backlog is NULL,\n     * it means that the instance isn't in any replication chains. In this\n     * scenario the replication info is useless, because when a slave\n     * connects to us, the NULL repl_backlog will trigger a full\n     * synchronization, at the same time we will use a new replid and clear\n     * replid2. */\n    if (!server.masterhost && server.repl_backlog) {\n        /* Note that when server.slaveseldb is -1, it means that this master\n         * didn't apply any write commands after a full synchronization.\n         * So we can let repl_stream_db be 0, this allows a restarted slave\n         * to reload replication ID/offset, it's safe because the next write\n         * command must generate a SELECT statement. */\n        rsi->repl_stream_db = server.slaveseldb == -1 ? 0 : server.slaveseldb;\n        return rsi;\n    }\n\n    /* If the instance is a slave we need a connected master\n     * in order to fetch the currently selected DB. */\n    if (server.master) {\n        rsi->repl_stream_db = server.master->db->id;\n        return rsi;\n    }\n\n    /* If we have a cached master we can use it in order to populate the\n     * replication selected DB info inside the RDB file: the slave can\n     * increment the master_repl_offset only from data arriving from the\n     * master, so if we are disconnected the offset in the cached master\n     * is valid. */\n    if (server.cached_master) {\n        rsi->repl_stream_db = server.cached_master->db->id;\n        return rsi;\n    }\n    return NULL;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 __RDB_H\n#define __RDB_H\n\n#include <stdio.h>\n#include \"rio.h\"\n\n/* TBD: include only necessary headers. */\n#include \"server.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#define RDB_VERSION 9\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|XXXXXX => if the two MSB are 00 the len is the 6 bits of this byte\n * 01|XXXXXX XXXXXXXX =>  01, the len is 14 byes, 6 bits + 8 bits of next byte\n * 10|000000 [32 bit integer] => A full 32 bit len in net byte order will follow\n * 10|000001 [64 bit integer] => A full 64 bit len in net byte order will follow\n * 11|OBKIND this means: specially encoded object will follow. The six bits\n *           number specify the kind of object that follows.\n *           See the RDB_ENC_* defines.\n *\n * Lengths up to 63 are stored using a single byte, most DB keys, and may\n * values, will fit inside. */\n#define RDB_6BITLEN 0\n#define RDB_14BITLEN 1\n#define RDB_32BITLEN 0x80\n#define RDB_64BITLEN 0x81\n#define RDB_ENCVAL 3\n#define RDB_LENERR UINT64_MAX\n\n/* When a length of a string object stored on disk has the first two bits\n * set, the remaining six bits specify a special encoding for the object\n * accordingly to the following defines: */\n#define RDB_ENC_INT8 0        /* 8 bit signed integer */\n#define RDB_ENC_INT16 1       /* 16 bit signed integer */\n#define RDB_ENC_INT32 2       /* 32 bit signed integer */\n#define RDB_ENC_LZF 3         /* string compressed with FASTLZ */\n\n/* Map object types to RDB object types. Macros starting with OBJ_ are for\n * memory storage and may change. Instead RDB types must be fixed because\n * we store them on disk. */\n#define RDB_TYPE_STRING 0\n#define RDB_TYPE_LIST   1\n#define RDB_TYPE_SET    2\n#define RDB_TYPE_ZSET   3\n#define RDB_TYPE_HASH   4\n#define RDB_TYPE_ZSET_2 5 /* ZSET version 2 with doubles stored in binary. */\n#define RDB_TYPE_MODULE 6\n#define RDB_TYPE_MODULE_2 7 /* Module value with annotations for parsing without\n                               the generating module being loaded. */\n/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */\n\n/* Object types for encoded objects. */\n#define RDB_TYPE_HASH_ZIPMAP    9\n#define RDB_TYPE_LIST_ZIPLIST  10\n#define RDB_TYPE_SET_INTSET    11\n#define RDB_TYPE_ZSET_ZIPLIST  12\n#define RDB_TYPE_HASH_ZIPLIST  13\n#define RDB_TYPE_LIST_QUICKLIST 14\n#define RDB_TYPE_STREAM_LISTPACKS 15\n/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */\n\n/* Test if a type is an object type. */\n#define rdbIsObjectType(t) ((t >= 0 && t <= 7) || (t >= 9 && t <= 15))\n\n/* Special RDB opcodes (saved/loaded with rdbSaveType/rdbLoadType). */\n#define RDB_OPCODE_MODULE_AUX 247   /* Module auxiliary data. */\n#define RDB_OPCODE_IDLE       248   /* LRU idle time. */\n#define RDB_OPCODE_FREQ       249   /* LFU frequency. */\n#define RDB_OPCODE_AUX        250   /* RDB aux field. */\n#define RDB_OPCODE_RESIZEDB   251   /* Hash table resize hint. */\n#define RDB_OPCODE_EXPIRETIME_MS 252    /* Expire time in milliseconds. */\n#define RDB_OPCODE_EXPIRETIME 253       /* Old expire time in seconds. */\n#define RDB_OPCODE_SELECTDB   254   /* DB number of the following keys. */\n#define RDB_OPCODE_EOF        255   /* End of the RDB file. */\n\n/* Module serialized values sub opcodes */\n#define RDB_MODULE_OPCODE_EOF   0   /* End of module value. */\n#define RDB_MODULE_OPCODE_SINT  1   /* Signed integer. */\n#define RDB_MODULE_OPCODE_UINT  2   /* Unsigned integer. */\n#define RDB_MODULE_OPCODE_FLOAT 3   /* Float. */\n#define RDB_MODULE_OPCODE_DOUBLE 4  /* Double. */\n#define RDB_MODULE_OPCODE_STRING 5  /* String. */\n\n/* rdbLoad...() functions flags. */\n#define RDB_LOAD_NONE   0\n#define RDB_LOAD_ENC    (1<<0)\n#define RDB_LOAD_PLAIN  (1<<1)\n#define RDB_LOAD_SDS    (1<<2)\n\n/* flags on the purpose of rdb save or load */\n#define RDBFLAGS_NONE 0                 /* No special RDB loading. */\n#define RDBFLAGS_AOF_PREAMBLE (1<<0)    /* Load/save the RDB as AOF preamble. */\n#define RDBFLAGS_REPLICATION (1<<1)     /* Load/save for SYNC. */\n#define RDBFLAGS_ALLOW_DUP (1<<2)       /* Allow duplicated keys when loading.*/\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, uint64_t len);\nint rdbSaveMillisecondTime(rio *rdb, long long t);\nlong long rdbLoadMillisecondTime(rio *rdb, int rdbver);\nuint64_t rdbLoadLen(rio *rdb, int *isencoded);\nint rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr);\nint rdbSaveObjectType(rio *rdb, robj *o);\nint rdbLoadObjectType(rio *rdb);\nint rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags);\nint rdbSaveBackground(char *filename, rdbSaveInfo *rsi);\nint rdbSaveToSlavesSockets(rdbSaveInfo *rsi);\nvoid rdbRemoveTempFile(pid_t childpid);\nint rdbSave(char *filename, rdbSaveInfo *rsi);\nssize_t rdbSaveObject(rio *rdb, robj *o, robj *key);\nsize_t rdbSavedObjectLen(robj *o, robj *key);\nrobj *rdbLoadObject(int type, rio *rdb, sds key);\nvoid backgroundSaveDoneHandler(int exitcode, int bysignal);\nint rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime);\nssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt);\nrobj *rdbLoadStringObject(rio *rdb);\nssize_t rdbSaveStringObject(rio *rdb, robj *obj);\nssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len);\nvoid *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr);\nint rdbSaveBinaryDoubleValue(rio *rdb, double val);\nint rdbLoadBinaryDoubleValue(rio *rdb, double *val);\nint rdbSaveBinaryFloatValue(rio *rdb, float val);\nint rdbLoadBinaryFloatValue(rio *rdb, float *val);\nint rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi);\nint rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi);\nrdbSaveInfo *rdbPopulateSaveInfo(rdbSaveInfo *rsi);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <math.h>\n#include <pthread.h>\n\n#include <sds.h> /* Use hiredis sds. */\n#include \"ae.h\"\n#include \"hiredis.h\"\n#include \"adlist.h\"\n#include \"dict.h\"\n#include \"zmalloc.h\"\n#include \"atomicvar.h\"\n#include \"crc16_slottable.h\"\n\n#define UNUSED(V) ((void) V)\n#define RANDPTR_INITIAL_SIZE 8\n#define MAX_LATENCY_PRECISION 3\n#define MAX_THREADS 500\n#define CLUSTER_SLOTS 16384\n\n#define CLIENT_GET_EVENTLOOP(c) \\\n    (c->thread_id >= 0 ? config.threads[c->thread_id]->el : config.el)\n\nstruct benchmarkThread;\nstruct clusterNode;\nstruct redisConfig;\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    int showerrors;\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    char *auth;\n    const char *user;\n    int precision;\n    int num_threads;\n    struct benchmarkThread **threads;\n    int cluster_mode;\n    int cluster_node_count;\n    struct clusterNode **cluster_nodes;\n    struct redisConfig *redis_config;\n    int is_fetching_slots;\n    int is_updating_slots;\n    int slots_last_update;\n    int enable_tracking;\n    /* Thread mutexes to be used as fallbacks by atomicvar.h */\n    pthread_mutex_t requests_issued_mutex;\n    pthread_mutex_t requests_finished_mutex;\n    pthread_mutex_t liveclients_mutex;\n    pthread_mutex_t is_fetching_slots_mutex;\n    pthread_mutex_t is_updating_slots_mutex;\n    pthread_mutex_t updating_slots_mutex;\n    pthread_mutex_t slots_last_update_mutex;\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    char **stagptr;         /* Pointers to slot hashtags (cluster mode only) */\n    size_t staglen;         /* Number of pointers in client->stagptr */\n    size_t stagfree;        /* Number of unused pointers in client->stagptr */\n    size_t written;         /* Bytes of 'obuf' already written */\n    long long start;        /* Start time of a request */\n    long long latency;      /* Request latency */\n    int pending;            /* Number of pending requests (replies to consume) */\n    int prefix_pending;     /* If non-zero, number of pending prefix commands. Commands\n                               such as auth and select are prefixed to the pipeline of\n                               benchmark commands and discarded after the first send. */\n    int prefixlen;          /* Size in bytes of the pending prefix commands */\n    int thread_id;\n    struct clusterNode *cluster_node;\n    int slots_last_update;\n} *client;\n\n/* Threads. */\n\ntypedef struct benchmarkThread {\n    int index;\n    pthread_t thread;\n    aeEventLoop *el;\n} benchmarkThread;\n\n/* Cluster. */\ntypedef struct clusterNode {\n    char *ip;\n    int port;\n    sds name;\n    int flags;\n    sds replicate;  /* Master ID if node is a slave */\n    int *slots;\n    int slots_count;\n    int current_slot_index;\n    int *updated_slots;         /* Used by updateClusterSlotsConfiguration */\n    int updated_slots_count;    /* Used by updateClusterSlotsConfiguration */\n    int replicas_count;\n    sds *migrating; /* An array of sds where even strings are slots and odd\n                     * strings are the destination node IDs. */\n    sds *importing; /* An array of sds where even strings are slots and odd\n                     * strings are the source node IDs. */\n    int migrating_count; /* Length of the migrating array (migrating slots*2) */\n    int importing_count; /* Length of the importing array (importing slots*2) */\n    struct redisConfig *redis_config;\n} clusterNode;\n\ntypedef struct redisConfig {\n    sds save;\n    sds appendonly;\n} redisConfig;\n\n/* Prototypes */\nstatic void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nstatic void createMissingClients(client c);\nstatic benchmarkThread *createBenchmarkThread(int index);\nstatic void freeBenchmarkThread(benchmarkThread *thread);\nstatic void freeBenchmarkThreads();\nstatic void *execBenchmarkThread(void *ptr);\nstatic clusterNode *createClusterNode(char *ip, int port);\nstatic redisConfig *getRedisConfig(const char *ip, int port,\n                                   const char *hostsocket);\nstatic void freeRedisConfig(redisConfig *cfg);\nstatic int fetchClusterSlotsConfiguration(client c);\nstatic void updateClusterSlotsConfiguration();\nint showThroughput(struct aeEventLoop *eventLoop, long long id,\n                   void *clientData);\n\n/* Dict callbacks */\nstatic uint64_t dictSdsHash(const void *key);\nstatic int dictSdsKeyCompare(void *privdata, const void *key1,\n    const void *key2);\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 uint64_t dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nstatic int 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/* _serverAssert is needed by dict */\nvoid _serverAssert(const char *estr, const char *file, int line) {\n    fprintf(stderr, \"=== ASSERTION FAILED ===\");\n    fprintf(stderr, \"==> %s:%d '%s' is not true\",file,line,estr);\n    *((char*)-1) = 'x';\n}\n\nstatic redisConfig *getRedisConfig(const char *ip, int port,\n                                   const char *hostsocket)\n{\n    redisConfig *cfg = zcalloc(sizeof(*cfg));\n    if (!cfg) return NULL;\n    redisContext *c = NULL;\n    redisReply *reply = NULL, *sub_reply = NULL;\n    if (hostsocket == NULL)\n        c = redisConnect(ip, port);\n    else\n        c = redisConnectUnix(hostsocket);\n    if (c == NULL || c->err) {\n        fprintf(stderr,\"Could not connect to Redis at \");\n        char *err = (c != NULL ? c->errstr : \"\");\n        if (hostsocket == NULL) fprintf(stderr,\"%s:%d: %s\\n\",ip,port,err);\n        else fprintf(stderr,\"%s: %s\\n\",hostsocket,err);\n        goto fail;\n    }\n\n    if(config.auth) {\n        void *authReply = NULL;\n        if (config.user == NULL)\n            redisAppendCommand(c, \"AUTH %s\", config.auth);\n        else\n            redisAppendCommand(c, \"AUTH %s %s\", config.user, config.auth);\n        if (REDIS_OK != redisGetReply(c, &authReply)) goto fail;\n        if (reply) freeReplyObject(reply);\n        reply = ((redisReply *) authReply);\n        if (reply->type == REDIS_REPLY_ERROR) {\n            fprintf(stderr, \"ERROR: %s\\n\", reply->str);\n            goto fail;\n        }\n    }\n\n    redisAppendCommand(c, \"CONFIG GET %s\", \"save\");\n    redisAppendCommand(c, \"CONFIG GET %s\", \"appendonly\");\n    int i = 0;\n    void *r = NULL;\n    for (; i < 2; i++) {\n        int res = redisGetReply(c, &r);\n        if (reply) freeReplyObject(reply);\n        reply = res == REDIS_OK ? ((redisReply *) r) : NULL;\n        if (res != REDIS_OK || !r) goto fail;\n        if (reply->type == REDIS_REPLY_ERROR) {\n            fprintf(stderr, \"ERROR: %s\\n\", reply->str);\n            goto fail;\n        }\n        if (reply->type != REDIS_REPLY_ARRAY || reply->elements < 2) goto fail;\n        sub_reply = reply->element[1];\n        char *value = sub_reply->str;\n        if (!value) value = \"\";\n        switch (i) {\n        case 0: cfg->save = sdsnew(value); break;\n        case 1: cfg->appendonly = sdsnew(value); break;\n        }\n    }\n    freeReplyObject(reply);\n    redisFree(c);\n    return cfg;\nfail:\n    fprintf(stderr, \"ERROR: failed to fetch CONFIG from \");\n    if (hostsocket == NULL) fprintf(stderr, \"%s:%d\\n\", ip, port);\n    else fprintf(stderr, \"%s\\n\", hostsocket);\n    freeReplyObject(reply);\n    redisFree(c);\n    zfree(cfg);\n    return NULL;\n}\nstatic void freeRedisConfig(redisConfig *cfg) {\n    if (cfg->save) sdsfree(cfg->save);\n    if (cfg->appendonly) sdsfree(cfg->appendonly);\n    zfree(cfg);\n}\n\nstatic void freeClient(client c) {\n    aeEventLoop *el = CLIENT_GET_EVENTLOOP(c);\n    listNode *ln;\n    aeDeleteFileEvent(el,c->context->fd,AE_WRITABLE);\n    aeDeleteFileEvent(el,c->context->fd,AE_READABLE);\n    if (c->thread_id >= 0) {\n        int requests_finished = 0;\n        atomicGet(config.requests_finished, requests_finished);\n        if (requests_finished >= config.requests) {\n            aeStop(el);\n        }\n    }\n    redisFree(c->context);\n    sdsfree(c->obuf);\n    zfree(c->randptr);\n    zfree(c->stagptr);\n    zfree(c);\n    if (config.num_threads) pthread_mutex_lock(&(config.liveclients_mutex));\n    config.liveclients--;\n    ln = listSearchKey(config.clients,c);\n    assert(ln != NULL);\n    listDelNode(config.clients,ln);\n    if (config.num_threads) pthread_mutex_unlock(&(config.liveclients_mutex));\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    aeEventLoop *el = CLIENT_GET_EVENTLOOP(c);\n    aeDeleteFileEvent(el,c->context->fd,AE_WRITABLE);\n    aeDeleteFileEvent(el,c->context->fd,AE_READABLE);\n    aeCreateFileEvent(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 = 0;\n        if (config.randomkeys_keyspacelen != 0)\n            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 setClusterKeyHashTag(client c) {\n    assert(c->thread_id >= 0);\n    clusterNode *node = c->cluster_node;\n    assert(node);\n    assert(node->current_slot_index < node->slots_count);\n    int is_updating_slots = 0;\n    atomicGet(config.is_updating_slots, is_updating_slots);\n    /* If updateClusterSlotsConfiguration is updating the slots array,\n     * call updateClusterSlotsConfiguration is order to block the thread\n     * since the mutex is locked. When the slots will be updated by the\n     * thread that's actually performing the update, the execution of\n     * updateClusterSlotsConfiguration won't actually do anything, since\n     * the updated_slots_count array will be already NULL. */\n    if (is_updating_slots) updateClusterSlotsConfiguration();\n    int slot = node->slots[node->current_slot_index];\n    const char *tag = crc16_slot_table[slot];\n    int taglen = strlen(tag);\n    size_t i;\n    for (i = 0; i < c->staglen; i++) {\n        char *p = c->stagptr[i] + 1;\n        p[0] = tag[0];\n        p[1] = (taglen >= 2 ? tag[1] : '}');\n        p[2] = (taglen == 3 ? tag[2] : '}');\n    }\n}\n\nstatic void clientDone(client c) {\n    int requests_finished = 0;\n    atomicGet(config.requests_finished, requests_finished);\n    if (requests_finished >= config.requests) {\n        freeClient(c);\n        if (!config.num_threads && config.el) aeStop(config.el);\n        return;\n    }\n    if (config.keepalive) {\n        resetClient(c);\n    } else {\n        if (config.num_threads) pthread_mutex_lock(&(config.liveclients_mutex));\n        config.liveclients--;\n        createMissingClients(c);\n        config.liveclients++;\n        if (config.num_threads)\n            pthread_mutex_unlock(&(config.liveclients_mutex));\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    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Calculate latency only for the first read event. This means that the\n     * server already sent the reply and we need to parse it. Parsing overhead\n     * is not part of the latency, so calculate it only once, here. */\n    if (c->latency < 0) c->latency = 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                redisReply *r = reply;\n                int is_err = (r->type == REDIS_REPLY_ERROR);\n\n                if (is_err && config.showerrors) {\n                    /* TODO: static lasterr_time not thread-safe */\n                    static time_t lasterr_time = 0;\n                    time_t now = time(NULL);\n                    if (lasterr_time != now) {\n                        lasterr_time = now;\n                        if (c->cluster_node) {\n                            printf(\"Error from server %s:%d: %s\\n\",\n                                   c->cluster_node->ip,\n                                   c->cluster_node->port,\n                                   r->str);\n                        } else printf(\"Error from server: %s\\n\", r->str);\n                    }\n                }\n\n                /* Try to update slots configuration if reply error is\n                 * MOVED/ASK/CLUSTERDOWN and the key(s) used by the command\n                 * contain(s) the slot hash tag. */\n                if (is_err && c->cluster_node && c->staglen) {\n                    int fetch_slots = 0, do_wait = 0;\n                    if (!strncmp(r->str,\"MOVED\",5) || !strncmp(r->str,\"ASK\",3))\n                        fetch_slots = 1;\n                    else if (!strncmp(r->str,\"CLUSTERDOWN\",11)) {\n                        /* Usually the cluster is able to recover itself after\n                         * a CLUSTERDOWN error, so try to sleep one second\n                         * before requesting the new configuration. */\n                        fetch_slots = 1;\n                        do_wait = 1;\n                        printf(\"Error from server %s:%d: %s\\n\",\n                               c->cluster_node->ip,\n                               c->cluster_node->port,\n                               r->str);\n                    }\n                    if (do_wait) sleep(1);\n                    if (fetch_slots && !fetchClusterSlotsConfiguration(c))\n                        exit(1);\n                }\n\n                freeReplyObject(reply);\n                /* This is an OK for prefix commands such as auth and select.*/\n                if (c->prefix_pending > 0) {\n                    c->prefix_pending--;\n                    c->pending--;\n                    /* Discard prefix commands on first response.*/\n                    if (c->prefixlen > 0) {\n                        size_t j;\n                        sdsrange(c->obuf, c->prefixlen, -1);\n                        /* We also need to fix the pointers to the strings\n                        * we need to randomize. */\n                        for (j = 0; j < c->randlen; j++)\n                            c->randptr[j] -= c->prefixlen;\n                        c->prefixlen = 0;\n                    }\n                    continue;\n                }\n                int requests_finished = 0;\n                atomicGetIncr(config.requests_finished, requests_finished, 1);\n                if (requests_finished < config.requests)\n                    config.latency[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    UNUSED(el);\n    UNUSED(fd);\n    UNUSED(mask);\n\n    /* Initialize request when nothing was written. */\n    if (c->written == 0) {\n        /* Enforce upper bound to number of requests. */\n        int requests_issued = 0;\n        atomicGetIncr(config.requests_issued, requests_issued, 1);\n        if (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        if (config.cluster_mode && c->staglen > 0) setClusterKeyHashTag(c);\n        atomicGet(config.slots_last_update, c->slots_last_update);\n        c->start = ustime();\n        c->latency = -1;\n    }\n    if (sdslen(c->obuf) > c->written) {\n        void *ptr = c->obuf+c->written;\n        ssize_t 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(el,c->context->fd,AE_WRITABLE);\n            aeCreateFileEvent(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, prefix commands are applied if needed.*/\nstatic client createClient(char *cmd, size_t len, client from, int thread_id) {\n    int j;\n    int is_cluster_client = (config.cluster_mode && thread_id >= 0);\n    client c = zmalloc(sizeof(struct _client));\n\n    const char *ip = NULL;\n    int port = 0;\n    c->cluster_node = NULL;\n    if (config.hostsocket == NULL || is_cluster_client) {\n        if (!is_cluster_client) {\n            ip = config.hostip;\n            port = config.hostport;\n        } else {\n            int node_idx = 0;\n            if (config.num_threads < config.cluster_node_count)\n                node_idx = config.liveclients % config.cluster_node_count;\n            else\n                node_idx = thread_id % config.cluster_node_count;\n            clusterNode *node = config.cluster_nodes[node_idx];\n            assert(node != NULL);\n            ip = (const char *) node->ip;\n            port = node->port;\n            c->cluster_node = node;\n        }\n        c->context = redisConnectNonBlock(ip,port);\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 || is_cluster_client)\n            fprintf(stderr,\"%s:%d: %s\\n\",ip,port,c->context->errstr);\n        else\n            fprintf(stderr,\"%s: %s\\n\",config.hostsocket,c->context->errstr);\n        exit(1);\n    }\n    c->thread_id = thread_id;\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    /* Prefix the request buffer with AUTH and/or SELECT commands, if applicable.\n     * These commands are discarded after the first response, so if the client is\n     * reused the commands will not be used again. */\n    c->prefix_pending = 0;\n    if (config.auth) {\n        char *buf = NULL;\n        int len;\n        if (config.user == NULL)\n            len = redisFormatCommand(&buf, \"AUTH %s\", config.auth);\n        else\n            len = redisFormatCommand(&buf, \"AUTH %s %s\",\n                                     config.user, config.auth);\n        c->obuf = sdscatlen(c->obuf, buf, len);\n        free(buf);\n        c->prefix_pending++;\n    }\n\n    if (config.enable_tracking) {\n        char *buf = NULL;\n        int len = redisFormatCommand(&buf, \"CLIENT TRACKING on\");\n        c->obuf = sdscatlen(c->obuf, buf, len);\n        free(buf);\n        c->prefix_pending++;\n    }\n\n    /* If a DB number different than zero is selected, prefix our request\n     * buffer with the SELECT command, that will be discarded the first\n     * time the replies are received, so if the client is reused the\n     * SELECT command will not be used again. */\n    if (config.dbnum != 0 && !is_cluster_client) {\n        c->obuf = sdscatprintf(c->obuf,\"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n            (int)sdslen(config.dbnumstr),config.dbnumstr);\n        c->prefix_pending++;\n    }\n    c->prefixlen = sdslen(c->obuf);\n    /* Append the request itself. */\n    if (from) {\n        c->obuf = sdscatlen(c->obuf,\n            from->obuf+from->prefixlen,\n            sdslen(from->obuf)-from->prefixlen);\n    } else {\n        for (j = 0; j < config.pipeline; j++)\n            c->obuf = sdscatlen(c->obuf,cmd,len);\n    }\n\n    c->written = 0;\n    c->pending = config.pipeline+c->prefix_pending;\n    c->randptr = NULL;\n    c->randlen = 0;\n    c->stagptr = NULL;\n    c->staglen = 0;\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 < (int)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->prefixlen - from->prefixlen;\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    /* If cluster mode is enabled, set slot hashtags pointers. */\n    if (config.cluster_mode) {\n        if (from) {\n            c->staglen = from->staglen;\n            c->stagfree = 0;\n            c->stagptr = zmalloc(sizeof(char*)*c->staglen);\n            /* copy the offsets. */\n            for (j = 0; j < (int)c->staglen; j++) {\n                c->stagptr[j] = c->obuf + (from->stagptr[j]-from->obuf);\n                /* Adjust for the different select prefix length. */\n                c->stagptr[j] += c->prefixlen - from->prefixlen;\n            }\n        } else {\n            char *p = c->obuf;\n\n            c->staglen = 0;\n            c->stagfree = RANDPTR_INITIAL_SIZE;\n            c->stagptr = zmalloc(sizeof(char*)*c->stagfree);\n            while ((p = strstr(p,\"{tag}\")) != NULL) {\n                if (c->stagfree == 0) {\n                    c->stagptr = zrealloc(c->stagptr,\n                                          sizeof(char*) * c->staglen*2);\n                    c->stagfree += c->staglen;\n                }\n                c->stagptr[c->staglen++] = p;\n                c->stagfree--;\n                p += 5; /* 12 is strlen(\"{tag}\"). */\n            }\n        }\n    }\n    aeEventLoop *el = NULL;\n    if (thread_id < 0) el = config.el;\n    else {\n        benchmarkThread *thread = config.threads[thread_id];\n        el = thread->el;\n    }\n    if (config.idlemode == 0)\n        aeCreateFileEvent(el,c->context->fd,AE_WRITABLE,writeHandler,c);\n    listAddNodeTail(config.clients,c);\n    atomicIncr(config.liveclients, 1);\n    atomicGet(config.slots_last_update, c->slots_last_update);\n    return c;\n}\n\nstatic void createMissingClients(client c) {\n    int n = 0;\n    while(config.liveclients < config.numclients) {\n        int thread_id = -1;\n        if (config.num_threads)\n            thread_id = config.liveclients % config.num_threads;\n        createClient(NULL,0,c,thread_id);\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 int ipow(int base, int exp) {\n    int result = 1;\n    while (exp) {\n        if (exp & 1) result *= base;\n        exp /= 2;\n        base *= base;\n    }\n    return result;\n}\n\nstatic void showLatencyReport(void) {\n    int i, curlat = 0;\n    int usbetweenlat = ipow(10, MAX_LATENCY_PRECISION-config.precision);\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        if (config.cluster_mode) {\n            printf(\"  cluster mode: yes (%d masters)\\n\",\n                   config.cluster_node_count);\n            int m ;\n            for (m = 0; m < config.cluster_node_count; m++) {\n                clusterNode *node =  config.cluster_nodes[m];\n                redisConfig *cfg = node->redis_config;\n                if (cfg == NULL) continue;\n                printf(\"  node [%d] configuration:\\n\",m );\n                printf(\"    save: %s\\n\",\n                    sdslen(cfg->save) ? cfg->save : \"NONE\");\n                printf(\"    appendonly: %s\\n\", cfg->appendonly);\n            }\n        } else {\n            if (config.redis_config) {\n                printf(\"  host configuration \\\"save\\\": %s\\n\",\n                       config.redis_config->save);\n                printf(\"  host configuration \\\"appendonly\\\": %s\\n\",\n                       config.redis_config->appendonly);\n            }\n        }\n        printf(\"  multi-thread: %s\\n\", (config.num_threads ? \"yes\" : \"no\"));\n        if (config.num_threads)\n            printf(\"  threads: %d\\n\", config.num_threads);\n\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]/usbetweenlat != curlat ||\n                i == (config.requests-1))\n            {\n                /* After the 2 milliseconds latency to have percentages split\n                 * by decimals will just add a lot of noise to the output. */\n                if (config.latency[i] >= 2000) {\n                    config.precision = 0;\n                    usbetweenlat = ipow(10,\n                        MAX_LATENCY_PRECISION-config.precision);\n                }\n\n                curlat = config.latency[i]/usbetweenlat;\n                perc = ((float)(i+1)*100)/config.requests;\n                printf(\"%.2f%% <= %.*f milliseconds\\n\", perc, config.precision,\n                    curlat/pow(10.0, config.precision));\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 initBenchmarkThreads() {\n    int i;\n    if (config.threads) freeBenchmarkThreads();\n    config.threads = zmalloc(config.num_threads * sizeof(benchmarkThread*));\n    for (i = 0; i < config.num_threads; i++) {\n        benchmarkThread *thread = createBenchmarkThread(i);\n        config.threads[i] = thread;\n    }\n}\n\nstatic void startBenchmarkThreads() {\n    int i;\n    for (i = 0; i < config.num_threads; i++) {\n        benchmarkThread *t = config.threads[i];\n        if (pthread_create(&(t->thread), NULL, execBenchmarkThread, t)){\n            fprintf(stderr, \"FATAL: Failed to start thread %d.\\n\", i);\n            exit(1);\n        }\n    }\n    for (i = 0; i < config.num_threads; i++)\n        pthread_join(config.threads[i]->thread, NULL);\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    if (config.num_threads) initBenchmarkThreads();\n\n    int thread_id = config.num_threads > 0 ? 0 : -1;\n    c = createClient(cmd,len,NULL,thread_id);\n    createMissingClients(c);\n\n    config.start = mstime();\n    if (!config.num_threads) aeMain(config.el);\n    else startBenchmarkThreads();\n    config.totlatency = mstime()-config.start;\n\n    showLatencyReport();\n    freeAllClients();\n    if (config.threads) freeBenchmarkThreads();\n}\n\n/* Thread functions. */\n\nstatic benchmarkThread *createBenchmarkThread(int index) {\n    benchmarkThread *thread = zmalloc(sizeof(*thread));\n    if (thread == NULL) return NULL;\n    thread->index = index;\n    thread->el = aeCreateEventLoop(1024*10);\n    aeCreateTimeEvent(thread->el,1,showThroughput,NULL,NULL);\n    return thread;\n}\n\nstatic void freeBenchmarkThread(benchmarkThread *thread) {\n    if (thread->el) aeDeleteEventLoop(thread->el);\n    zfree(thread);\n}\n\nstatic void freeBenchmarkThreads() {\n    int i = 0;\n    for (; i < config.num_threads; i++) {\n        benchmarkThread *thread = config.threads[i];\n        if (thread) freeBenchmarkThread(thread);\n    }\n    zfree(config.threads);\n    config.threads = NULL;\n}\n\nstatic void *execBenchmarkThread(void *ptr) {\n    benchmarkThread *thread = (benchmarkThread *) ptr;\n    aeMain(thread->el);\n    return NULL;\n}\n\n/* Cluster helper functions. */\n\nstatic clusterNode *createClusterNode(char *ip, int port) {\n    clusterNode *node = zmalloc(sizeof(*node));\n    if (!node) return NULL;\n    node->ip = ip;\n    node->port = port;\n    node->name = NULL;\n    node->flags = 0;\n    node->replicate = NULL;\n    node->replicas_count = 0;\n    node->slots = zmalloc(CLUSTER_SLOTS * sizeof(int));\n    node->slots_count = 0;\n    node->current_slot_index = 0;\n    node->updated_slots = NULL;\n    node->updated_slots_count = 0;\n    node->migrating = NULL;\n    node->importing = NULL;\n    node->migrating_count = 0;\n    node->importing_count = 0;\n    node->redis_config = NULL;\n    return node;\n}\n\nstatic void freeClusterNode(clusterNode *node) {\n    int i;\n    if (node->name) sdsfree(node->name);\n    if (node->replicate) sdsfree(node->replicate);\n    if (node->migrating != NULL) {\n        for (i = 0; i < node->migrating_count; i++) sdsfree(node->migrating[i]);\n        zfree(node->migrating);\n    }\n    if (node->importing != NULL) {\n        for (i = 0; i < node->importing_count; i++) sdsfree(node->importing[i]);\n        zfree(node->importing);\n    }\n    /* If the node is not the reference node, that uses the address from\n     * config.hostip and config.hostport, then the node ip has been\n     * allocated by fetchClusterConfiguration, so it must be freed. */\n    if (node->ip && strcmp(node->ip, config.hostip) != 0) sdsfree(node->ip);\n    if (node->redis_config != NULL) freeRedisConfig(node->redis_config);\n    zfree(node->slots);\n    zfree(node);\n}\n\nstatic void freeClusterNodes() {\n    int i = 0;\n    for (; i < config.cluster_node_count; i++) {\n        clusterNode *n = config.cluster_nodes[i];\n        if (n) freeClusterNode(n);\n    }\n    zfree(config.cluster_nodes);\n    config.cluster_nodes = NULL;\n}\n\nstatic clusterNode **addClusterNode(clusterNode *node) {\n    int count = config.cluster_node_count + 1;\n    config.cluster_nodes = zrealloc(config.cluster_nodes,\n                                    count * sizeof(*node));\n    if (!config.cluster_nodes) return NULL;\n    config.cluster_nodes[config.cluster_node_count++] = node;\n    return config.cluster_nodes;\n}\n\nstatic int fetchClusterConfiguration() {\n    int success = 1;\n    redisContext *ctx = NULL;\n    redisReply *reply =  NULL;\n    if (config.hostsocket == NULL)\n        ctx = redisConnect(config.hostip,config.hostport);\n    else\n        ctx = redisConnectUnix(config.hostsocket);\n    if (ctx->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,\n                    ctx->errstr);\n        } else fprintf(stderr,\"%s: %s\\n\",config.hostsocket,ctx->errstr);\n        exit(1);\n    }\n    clusterNode *firstNode = createClusterNode((char *) config.hostip,\n                                               config.hostport);\n    if (!firstNode) {success = 0; goto cleanup;}\n    reply = redisCommand(ctx, \"CLUSTER NODES\");\n    success = (reply != NULL);\n    if (!success) goto cleanup;\n    success = (reply->type != REDIS_REPLY_ERROR);\n    if (!success) {\n        if (config.hostsocket == NULL) {\n            fprintf(stderr, \"Cluster node %s:%d replied with error:\\n%s\\n\",\n                    config.hostip, config.hostport, reply->str);\n        } else {\n            fprintf(stderr, \"Cluster node %s replied with error:\\n%s\\n\",\n                    config.hostsocket, reply->str);\n        }\n        goto cleanup;\n    }\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *name = NULL, *addr = NULL, *flags = NULL, *master_id = NULL;\n        int i = 0;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            switch(i++){\n            case 0: name = token; break;\n            case 1: addr = token; break;\n            case 2: flags = token; break;\n            case 3: master_id = token; break;\n            }\n            if (i == 8) break; // Slots\n        }\n        if (!flags) {\n            fprintf(stderr, \"Invalid CLUSTER NODES reply: missing flags.\\n\");\n            success = 0;\n            goto cleanup;\n        }\n        int myself = (strstr(flags, \"myself\") != NULL);\n        int is_replica = (strstr(flags, \"slave\") != NULL ||\n                         (master_id != NULL && master_id[0] != '-'));\n        if (is_replica) continue;\n        if (addr == NULL) {\n            fprintf(stderr, \"Invalid CLUSTER NODES reply: missing addr.\\n\");\n            success = 0;\n            goto cleanup;\n        }\n        clusterNode *node = NULL;\n        char *ip = NULL;\n        int port = 0;\n        char *paddr = strchr(addr, ':');\n        if (paddr != NULL) {\n            *paddr = '\\0';\n            ip = addr;\n            addr = paddr + 1;\n            /* If internal bus is specified, then just drop it. */\n            if ((paddr = strchr(addr, '@')) != NULL) *paddr = '\\0';\n            port = atoi(addr);\n        }\n        if (myself) {\n            node = firstNode;\n            if (node->ip == NULL && ip != NULL) {\n                node->ip = ip;\n                node->port = port;\n            }\n        } else {\n            node = createClusterNode(sdsnew(ip), port);\n        }\n        if (node == NULL) {\n            success = 0;\n            goto cleanup;\n        }\n        if (name != NULL) node->name = sdsnew(name);\n        if (i == 8) {\n            int remaining = strlen(line);\n            while (remaining > 0) {\n                p = strchr(line, ' ');\n                if (p == NULL) p = line + remaining;\n                remaining -= (p - line);\n\n                char *slotsdef = line;\n                *p = '\\0';\n                if (remaining) {\n                    line = p + 1;\n                    remaining--;\n                } else line = p;\n                char *dash = NULL;\n                if (slotsdef[0] == '[') {\n                    slotsdef++;\n                    if ((p = strstr(slotsdef, \"->-\"))) { // Migrating\n                        *p = '\\0';\n                        p += 3;\n                        char *closing_bracket = strchr(p, ']');\n                        if (closing_bracket) *closing_bracket = '\\0';\n                        sds slot = sdsnew(slotsdef);\n                        sds dst = sdsnew(p);\n                        node->migrating_count += 2;\n                        node->migrating =\n                            zrealloc(node->migrating,\n                                (node->migrating_count * sizeof(sds)));\n                        node->migrating[node->migrating_count - 2] =\n                            slot;\n                        node->migrating[node->migrating_count - 1] =\n                            dst;\n                    }  else if ((p = strstr(slotsdef, \"-<-\"))) {//Importing\n                        *p = '\\0';\n                        p += 3;\n                        char *closing_bracket = strchr(p, ']');\n                        if (closing_bracket) *closing_bracket = '\\0';\n                        sds slot = sdsnew(slotsdef);\n                        sds src = sdsnew(p);\n                        node->importing_count += 2;\n                        node->importing = zrealloc(node->importing,\n                            (node->importing_count * sizeof(sds)));\n                        node->importing[node->importing_count - 2] =\n                            slot;\n                        node->importing[node->importing_count - 1] =\n                            src;\n                    }\n                } else if ((dash = strchr(slotsdef, '-')) != NULL) {\n                    p = dash;\n                    int start, stop;\n                    *p = '\\0';\n                    start = atoi(slotsdef);\n                    stop = atoi(p + 1);\n                    while (start <= stop) {\n                        int slot = start++;\n                        node->slots[node->slots_count++] = slot;\n                    }\n                } else if (p > slotsdef) {\n                    int slot = atoi(slotsdef);\n                    node->slots[node->slots_count++] = slot;\n                }\n            }\n        }\n        if (node->slots_count == 0) {\n            printf(\"WARNING: master node %s:%d has no slots, skipping...\\n\",\n                   node->ip, node->port);\n            continue;\n        }\n        if (!addClusterNode(node)) {\n            success = 0;\n            goto cleanup;\n        }\n    }\ncleanup:\n    if (ctx) redisFree(ctx);\n    if (!success) {\n        if (config.cluster_nodes) freeClusterNodes();\n    }\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Request the current cluster slots configuration by calling CLUSTER SLOTS\n * and atomically update the slots after a successful reply. */\nstatic int fetchClusterSlotsConfiguration(client c) {\n    UNUSED(c);\n    int success = 1, is_fetching_slots = 0, last_update = 0;\n    size_t i;\n    atomicGet(config.slots_last_update, last_update);\n    if (c->slots_last_update < last_update) {\n        c->slots_last_update = last_update;\n        return -1;\n    }\n    redisReply *reply = NULL;\n    atomicGetIncr(config.is_fetching_slots, is_fetching_slots, 1);\n    if (is_fetching_slots) return -1; //TODO: use other codes || errno ?\n    atomicSet(config.is_fetching_slots, 1);\n    if (config.showerrors)\n        printf(\"Cluster slots configuration changed, fetching new one...\\n\");\n    const char *errmsg = \"Failed to update cluster slots configuration\";\n    static dictType dtype = {\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    /* printf(\"[%d] fetchClusterSlotsConfiguration\\n\", c->thread_id); */\n    dict *masters = dictCreate(&dtype, NULL);\n    redisContext *ctx = NULL;\n    for (i = 0; i < (size_t) config.cluster_node_count; i++) {\n        clusterNode *node = config.cluster_nodes[i];\n        assert(node->ip != NULL);\n        assert(node->name != NULL);\n        assert(node->port);\n        /* Use first node as entry point to connect to. */\n        if (ctx == NULL) {\n            ctx = redisConnect(node->ip, node->port);\n            if (!ctx || ctx->err) {\n                success = 0;\n                if (ctx && ctx->err)\n                    fprintf(stderr, \"REDIS CONNECTION ERROR: %s\\n\", ctx->errstr);\n                goto cleanup;\n            }\n        }\n        if (node->updated_slots != NULL)\n            zfree(node->updated_slots);\n        node->updated_slots = NULL;\n        node->updated_slots_count = 0;\n        dictReplace(masters, node->name, node) ;\n    }\n    reply = redisCommand(ctx, \"CLUSTER SLOTS\");\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {\n        success = 0;\n        if (reply)\n            fprintf(stderr,\"%s\\nCLUSTER SLOTS ERROR: %s\\n\",errmsg,reply->str);\n        goto cleanup;\n    }\n    assert(reply->type == REDIS_REPLY_ARRAY);\n    for (i = 0; i < reply->elements; i++) {\n        redisReply *r = reply->element[i];\n        assert(r->type == REDIS_REPLY_ARRAY);\n        assert(r->elements >= 3);\n        int from, to, slot;\n        from = r->element[0]->integer;\n        to = r->element[1]->integer;\n        redisReply *nr =  r->element[2];\n        assert(nr->type == REDIS_REPLY_ARRAY && nr->elements >= 3);\n        assert(nr->element[2]->str != NULL);\n        sds name =  sdsnew(nr->element[2]->str);\n        dictEntry *entry = dictFind(masters, name);\n        if (entry == NULL) {\n            success = 0;\n            fprintf(stderr, \"%s: could not find node with ID %s in current \"\n                            \"configuration.\\n\", errmsg, name);\n            if (name) sdsfree(name);\n            goto cleanup;\n        }\n        sdsfree(name);\n        clusterNode *node = dictGetVal(entry);\n        if (node->updated_slots == NULL)\n            node->updated_slots = zcalloc(CLUSTER_SLOTS * sizeof(int));\n        for (slot = from; slot <= to; slot++)\n            node->updated_slots[node->updated_slots_count++] = slot;\n    }\n    updateClusterSlotsConfiguration();\ncleanup:\n    freeReplyObject(reply);\n    redisFree(ctx);\n    dictRelease(masters);\n    atomicSet(config.is_fetching_slots, 0);\n    return success;\n}\n\n/* Atomically update the new slots configuration. */\nstatic void updateClusterSlotsConfiguration() {\n    pthread_mutex_lock(&config.is_updating_slots_mutex);\n    atomicSet(config.is_updating_slots, 1);\n    int i;\n    for (i = 0; i < config.cluster_node_count; i++) {\n        clusterNode *node = config.cluster_nodes[i];\n        if (node->updated_slots != NULL) {\n            int *oldslots = node->slots;\n            node->slots = node->updated_slots;\n            node->slots_count = node->updated_slots_count;\n            node->current_slot_index = 0;\n            node->updated_slots = NULL;\n            node->updated_slots_count = 0;\n            zfree(oldslots);\n        }\n    }\n    atomicSet(config.is_updating_slots, 0);\n    atomicIncr(config.slots_last_update, 1);\n    pthread_mutex_unlock(&config.is_updating_slots_mutex);\n}\n\n/* Returns number of consumed options. */\nint parseOptions(int argc, const char **argv) {\n    int i;\n    int lastarg;\n    int exit_status = 1;\n\n    for (i = 1; i < argc; i++) {\n        lastarg = (i == (argc-1));\n\n        if (!strcmp(argv[i],\"-c\")) {\n            if (lastarg) goto invalid;\n            config.numclients = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-n\")) {\n            if (lastarg) goto invalid;\n            config.requests = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-k\")) {\n            if (lastarg) goto invalid;\n            config.keepalive = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-h\")) {\n            if (lastarg) goto invalid;\n            config.hostip = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-p\")) {\n            if (lastarg) goto invalid;\n            config.hostport = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"-s\")) {\n            if (lastarg) goto invalid;\n            config.hostsocket = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"-a\") ) {\n            if (lastarg) goto invalid;\n            config.auth = strdup(argv[++i]);\n        } else if (!strcmp(argv[i],\"--user\")) {\n            if (lastarg) goto invalid;\n            config.user = 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            const char *next = argv[++i], *p = next;\n            if (*p == '-') {\n                p++;\n                if (*p < '0' || *p > '9') goto invalid;\n            }\n            config.randomkeys = 1;\n            config.randomkeys_keyspacelen = atoi(next);\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],\"-e\")) {\n            config.showerrors = 1;\n        } else if (!strcmp(argv[i],\"-t\")) {\n            if (lastarg) goto invalid;\n            /* We get the list of tests to run as a string in the form\n             * get,set,lrange,...,test_N. Then we add a comma before and\n             * after the string in order to make sure that searching\n             * for \",testname,\" will always get a match if the test is\n             * enabled. */\n            config.tests = sdsnew(\",\");\n            config.tests = sdscat(config.tests,(char*)argv[++i]);\n            config.tests = sdscat(config.tests,\",\");\n            sdstolower(config.tests);\n        } else if (!strcmp(argv[i],\"--dbnum\")) {\n            if (lastarg) goto invalid;\n            config.dbnum = atoi(argv[++i]);\n            config.dbnumstr = sdsfromlonglong(config.dbnum);\n        } else if (!strcmp(argv[i],\"--precision\")) {\n            if (lastarg) goto invalid;\n            config.precision = atoi(argv[++i]);\n            if (config.precision < 0) config.precision = 0;\n            if (config.precision > MAX_LATENCY_PRECISION) config.precision = MAX_LATENCY_PRECISION;\n        } else if (!strcmp(argv[i],\"--threads\")) {\n             if (lastarg) goto invalid;\n             config.num_threads = atoi(argv[++i]);\n             if (config.num_threads > MAX_THREADS) {\n                printf(\"WARNING: too many threads, limiting threads to %d.\\n\",\n                       MAX_THREADS);\n                config.num_threads = MAX_THREADS;\n             } else if (config.num_threads < 0) config.num_threads = 0;\n        } else if (!strcmp(argv[i],\"--cluster\")) {\n            config.cluster_mode = 1;\n        } else if (!strcmp(argv[i],\"--enable-tracking\")) {\n            config.enable_tracking = 1;\n        } else if (!strcmp(argv[i],\"--help\")) {\n            exit_status = 0;\n            goto usage;\n        } else {\n            /* Assume the user meant to provide an option when the arg starts\n             * with a dash. We're done otherwise and should use the remainder\n             * as the command and arguments for running the benchmark. */\n            if (argv[i][0] == '-') goto invalid;\n            return i;\n        }\n    }\n\n    return i;\n\ninvalid:\n    printf(\"Invalid option \\\"%s\\\" or option argument missing\\n\\n\",argv[i]);\n\nusage:\n    printf(\n\"Usage: 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\" -a <password>      Password for Redis Auth\\n\"\n\" --user <username>  Used to send ACL style 'AUTH username pass'. Needs -a.\\n\"\n\" -c <clients>       Number of parallel connections (default 50)\\n\"\n\" -n <requests>      Total number of requests (default 100000)\\n\"\n\" -d <size>          Data size of SET/GET value in bytes (default 3)\\n\"\n\" --dbnum <db>       SELECT the specified db number (default 0)\\n\"\n\" --threads <num>    Enable multi-thread mode.\\n\"\n\" --cluster          Enable cluster mode.\\n\"\n\" --enable-tracking  Send CLIENT TRACKING on before starting benchmark.\\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\" -e                 If server replies with errors, show them on stdout.\\n\"\n\"                    (no more than 1 error per second is displayed)\\n\"\n\" -q                 Quiet. Just show query/sec values\\n\"\n\" --precision        Number of decimal places to display in latency output (default 0)\\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    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(clientData);\n    int liveclients = 0;\n    int requests_finished = 0;\n    atomicGet(config.liveclients, liveclients);\n    atomicGet(config.requests_finished, requests_finished);\n\n    if (liveclients == 0 && requests_finished != config.requests) {\n        fprintf(stderr,\"All clients disconnected... aborting.\\n\");\n        exit(1);\n    }\n    if (config.num_threads && requests_finished >= config.requests) {\n        aeStop(eventLoop);\n        return AE_NOMORE;\n    }\n    if (config.csv) return 250;\n    if (config.idlemode == 1) {\n        printf(\"clients: %d\\r\", config.liveclients);\n        fflush(stdout);\n\treturn 250;\n    }\n    float dt = (float)(mstime()-config.start)/1000.0;\n    float rps = (float)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 = 100000;\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.showerrors = 0;\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    config.auth = NULL;\n    config.precision = 1;\n    config.num_threads = 0;\n    config.threads = NULL;\n    config.cluster_mode = 0;\n    config.cluster_node_count = 0;\n    config.cluster_nodes = NULL;\n    config.redis_config = NULL;\n    config.is_fetching_slots = 0;\n    config.is_updating_slots = 0;\n    config.slots_last_update = 0;\n    config.enable_tracking = 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.cluster_mode) {\n        /* Fetch cluster configuration. */\n        if (!fetchClusterConfiguration() || !config.cluster_nodes) {\n            if (!config.hostsocket) {\n                fprintf(stderr, \"Failed to fetch cluster configuration from \"\n                                \"%s:%d\\n\", config.hostip, config.hostport);\n            } else {\n                fprintf(stderr, \"Failed to fetch cluster configuration from \"\n                                \"%s\\n\", config.hostsocket);\n            }\n            exit(1);\n        }\n        if (config.cluster_node_count <= 1) {\n            fprintf(stderr, \"Invalid cluster: %d node(s).\\n\",\n                    config.cluster_node_count);\n            exit(1);\n        }\n        printf(\"Cluster has %d master nodes:\\n\\n\", config.cluster_node_count);\n        int i = 0;\n        for (; i < config.cluster_node_count; i++) {\n            clusterNode *node = config.cluster_nodes[i];\n            if (!node) {\n                fprintf(stderr, \"Invalid cluster node #%d\\n\", i);\n                exit(1);\n            }\n            printf(\"Master %d: \", i);\n            if (node->name) printf(\"%s \", node->name);\n            printf(\"%s:%d\\n\", node->ip, node->port);\n            node->redis_config = getRedisConfig(node->ip, node->port, NULL);\n            if (node->redis_config == NULL) {\n                fprintf(stderr, \"WARN: could not fetch node CONFIG %s:%d\\n\",\n                        node->ip, node->port);\n            }\n        }\n        printf(\"\\n\");\n        /* Automatically set thread number to node count if not specified\n         * by the user. */\n        if (config.num_threads == 0)\n            config.num_threads = config.cluster_node_count;\n    } else {\n        config.redis_config =\n            getRedisConfig(config.hostip, config.hostport, config.hostsocket);\n        if (config.redis_config == NULL)\n            fprintf(stderr, \"WARN: could not fetch server CONFIG\\n\");\n    }\n\n    if (config.num_threads > 0) {\n        pthread_mutex_init(&(config.requests_issued_mutex), NULL);\n        pthread_mutex_init(&(config.requests_finished_mutex), NULL);\n        pthread_mutex_init(&(config.liveclients_mutex), NULL);\n        pthread_mutex_init(&(config.is_fetching_slots_mutex), NULL);\n        pthread_mutex_init(&(config.is_updating_slots_mutex), NULL);\n        pthread_mutex_init(&(config.updating_slots_mutex), NULL);\n        pthread_mutex_init(&(config.slots_last_update_mutex), NULL);\n    }\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        int thread_id = -1, use_threads = (config.num_threads > 0);\n        if (use_threads) {\n            thread_id = 0;\n            initBenchmarkThreads();\n        }\n        c = createClient(\"\",0,NULL,thread_id); /* will never receive a reply */\n        createMissingClients(c);\n        if (use_threads) startBenchmarkThreads();\n        else 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        if (config.redis_config != NULL) freeRedisConfig(config.redis_config);\n        return 0;\n    }\n\n    /* Run default benchmark suite. */\n    data = zmalloc(config.datasize+1);\n    do {\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:{tag}:__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:{tag}:__rand_int__\");\n            benchmark(\"GET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"incr\")) {\n            len = redisFormatCommand(&cmd,\"INCR counter:{tag}:__rand_int__\");\n            benchmark(\"INCR\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpush\")) {\n            len = redisFormatCommand(&cmd,\"LPUSH mylist:{tag} %s\",data);\n            benchmark(\"LPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"rpush\")) {\n            len = redisFormatCommand(&cmd,\"RPUSH mylist:{tag} %s\",data);\n            benchmark(\"RPUSH\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"lpop\")) {\n            len = redisFormatCommand(&cmd,\"LPOP mylist:{tag}\");\n            benchmark(\"LPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"rpop\")) {\n            len = redisFormatCommand(&cmd,\"RPOP mylist:{tag}\");\n            benchmark(\"RPOP\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"sadd\")) {\n            len = redisFormatCommand(&cmd,\n                \"SADD myset:{tag} element:__rand_int__\");\n            benchmark(\"SADD\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"hset\")) {\n            len = redisFormatCommand(&cmd,\n                \"HSET myhash:{tag}:__rand_int__ element:__rand_int__ %s\",data);\n            benchmark(\"HSET\",cmd,len);\n            free(cmd);\n        }\n\n        if (test_is_selected(\"spop\")) {\n            len = redisFormatCommand(&cmd,\"SPOP myset:{tag}\");\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:{tag} %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:{tag} 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:{tag} 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:{tag} 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:{tag} 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:{tag}:__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    if (config.redis_config != NULL) freeRedisConfig(config.redis_config);\n\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include <sys/stat.h>\n\n#define ERROR(...) { \\\n    char __buf[1024]; \\\n    snprintf(__buf, sizeof(__buf), __VA_ARGS__); \\\n    snprintf(error, sizeof(error), \"0x%16llx: %s\", (long long)epos, __buf); \\\n}\n\nstatic char error[1044];\nstatic off_t epos;\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\nint readLong(FILE *fp, char prefix, long *target) {\n    char buf[128], *eptr;\n    epos = ftello(fp);\n    if (fgets(buf,sizeof(buf),fp) == NULL) {\n        return 0;\n    }\n    if (buf[0] != prefix) {\n        ERROR(\"Expected prefix '%c', got: '%c'\",prefix,buf[0]);\n        return 0;\n    }\n    *target = strtol(buf+1,&eptr,10);\n    return consumeNewline(eptr);\n}\n\nint readBytes(FILE *fp, char *target, long length) {\n    long real;\n    epos = ftello(fp);\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    return 1;\n}\n\nint readString(FILE *fp, char** target) {\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    *target = (char*)zmalloc(len);\n    if (!readBytes(fp,*target,len)) {\n        return 0;\n    }\n    if (!consumeNewline(*target+len-2)) {\n        return 0;\n    }\n    (*target)[len-2] = '\\0';\n    return 1;\n}\n\nint readArgc(FILE *fp, long *target) {\n    return readLong(fp,'*',target);\n}\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        if (!multi) pos = ftello(fp);\n        if (!readArgc(fp, &argc)) break;\n\n        for (i = 0; i < argc; i++) {\n            if (!readString(fp,&str)) break;\n            if (i == 0) {\n                if (strcasecmp(str, \"multi\") == 0) {\n                    if (multi++) {\n                        ERROR(\"Unexpected MULTI\");\n                        break;\n                    }\n                } else if (strcasecmp(str, \"exec\") == 0) {\n                    if (--multi) {\n                        ERROR(\"Unexpected EXEC\");\n                        break;\n                    }\n                }\n            }\n            zfree(str);\n        }\n\n        /* Stop if the loop did not finish */\n        if (i < argc) {\n            if (str) zfree(str);\n            break;\n        }\n    }\n\n    if (feof(fp) && multi && strlen(error) == 0) {\n        ERROR(\"Reached EOF before reading EXEC for MULTI\");\n    }\n    if (strlen(error) > 0) {\n        printf(\"%s\\n\", error);\n    }\n    return pos;\n}\n\nint redis_check_aof_main(int argc, char **argv) {\n    char *filename;\n    int fix = 0;\n\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    FILE *fp = fopen(filename,\"r+\");\n    if (fp == NULL) {\n        printf(\"Cannot open file: %s\\n\", filename);\n        exit(1);\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    off_t size = sb.st_size;\n    if (size == 0) {\n        printf(\"Empty file: %s\\n\", filename);\n        exit(1);\n    }\n\n    /* This AOF file may have an RDB preamble. Check this to start, and if this\n     * is the case, start processing the RDB part. */\n    if (size >= 8) {    /* There must be at least room for the RDB header. */\n        char sig[5];\n        int has_preamble = fread(sig,sizeof(sig),1,fp) == 1 &&\n                            memcmp(sig,\"REDIS\",sizeof(sig)) == 0;\n        rewind(fp);\n        if (has_preamble) {\n            printf(\"The AOF appears to start with an RDB preamble.\\n\"\n                   \"Checking the RDB preamble to start:\\n\");\n            if (redis_check_rdb_main(argc,argv,fp) == C_ERR) {\n                printf(\"RDB preamble of AOF file is not sane, aborting.\\n\");\n                exit(1);\n            } else {\n                printf(\"RDB preamble is OK, proceeding with AOF tail...\\n\");\n            }\n        }\n    }\n\n    off_t pos = process(fp);\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    if (diff > 0) {\n        if (fix) {\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            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        } else {\n            printf(\"AOF is not valid. \"\n                   \"Use the --fix option to try fixing it.\\n\");\n            exit(1);\n        }\n    } else {\n        printf(\"AOF is valid\\n\");\n    }\n\n    fclose(fp);\n    exit(0);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/redis-check-rdb.c",
    "content": "/*\n * Copyright (c) 2016, 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 \"server.h\"\n#include \"rdb.h\"\n\n#include <stdarg.h>\n\nvoid createSharedObjects(void);\nvoid rdbLoadProgressCallback(rio *r, const void *buf, size_t len);\nint rdbCheckMode = 0;\n\nstruct {\n    rio *rio;\n    robj *key;                      /* Current key we are reading. */\n    int key_type;                   /* Current key type if != -1. */\n    unsigned long keys;             /* Number of keys processed. */\n    unsigned long expires;          /* Number of keys with an expire. */\n    unsigned long already_expired;  /* Number of keys already expired. */\n    int doing;                      /* The state while reading the RDB. */\n    int error_set;                  /* True if error is populated. */\n    char error[1024];\n} rdbstate;\n\n/* At every loading step try to remember what we were about to do, so that\n * we can log this information when an error is encountered. */\n#define RDB_CHECK_DOING_START 0\n#define RDB_CHECK_DOING_READ_TYPE 1\n#define RDB_CHECK_DOING_READ_EXPIRE 2\n#define RDB_CHECK_DOING_READ_KEY 3\n#define RDB_CHECK_DOING_READ_OBJECT_VALUE 4\n#define RDB_CHECK_DOING_CHECK_SUM 5\n#define RDB_CHECK_DOING_READ_LEN 6\n#define RDB_CHECK_DOING_READ_AUX 7\n\nchar *rdb_check_doing_string[] = {\n    \"start\",\n    \"read-type\",\n    \"read-expire\",\n    \"read-key\",\n    \"read-object-value\",\n    \"check-sum\",\n    \"read-len\",\n    \"read-aux\"\n};\n\nchar *rdb_type_string[] = {\n    \"string\",\n    \"list-linked\",\n    \"set-hashtable\",\n    \"zset-v1\",\n    \"hash-hashtable\",\n    \"zset-v2\",\n    \"module-value\",\n    \"\",\"\",\n    \"hash-zipmap\",\n    \"list-ziplist\",\n    \"set-intset\",\n    \"zset-ziplist\",\n    \"hash-ziplist\",\n    \"quicklist\",\n    \"stream\"\n};\n\n/* Show a few stats collected into 'rdbstate' */\nvoid rdbShowGenericInfo(void) {\n    printf(\"[info] %lu keys read\\n\", rdbstate.keys);\n    printf(\"[info] %lu expires\\n\", rdbstate.expires);\n    printf(\"[info] %lu already expired\\n\", rdbstate.already_expired);\n}\n\n/* Called on RDB errors. Provides details about the RDB and the offset\n * we were when the error was detected. */\nvoid rdbCheckError(const char *fmt, ...) {\n    char msg[1024];\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(msg, sizeof(msg), fmt, ap);\n    va_end(ap);\n\n    printf(\"--- RDB ERROR DETECTED ---\\n\");\n    printf(\"[offset %llu] %s\\n\",\n        (unsigned long long) (rdbstate.rio ?\n            rdbstate.rio->processed_bytes : 0), msg);\n    printf(\"[additional info] While doing: %s\\n\",\n        rdb_check_doing_string[rdbstate.doing]);\n    if (rdbstate.key)\n        printf(\"[additional info] Reading key '%s'\\n\",\n            (char*)rdbstate.key->ptr);\n    if (rdbstate.key_type != -1)\n        printf(\"[additional info] Reading type %d (%s)\\n\",\n            rdbstate.key_type,\n            ((unsigned)rdbstate.key_type <\n             sizeof(rdb_type_string)/sizeof(char*)) ?\n                rdb_type_string[rdbstate.key_type] : \"unknown\");\n    rdbShowGenericInfo();\n}\n\n/* Print informations during RDB checking. */\nvoid rdbCheckInfo(const char *fmt, ...) {\n    char msg[1024];\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(msg, sizeof(msg), fmt, ap);\n    va_end(ap);\n\n    printf(\"[offset %llu] %s\\n\",\n        (unsigned long long) (rdbstate.rio ?\n            rdbstate.rio->processed_bytes : 0), msg);\n}\n\n/* Used inside rdb.c in order to log specific errors happening inside\n * the RDB loading internals. */\nvoid rdbCheckSetError(const char *fmt, ...) {\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(rdbstate.error, sizeof(rdbstate.error), fmt, ap);\n    va_end(ap);\n    rdbstate.error_set = 1;\n}\n\n/* During RDB check we setup a special signal handler for memory violations\n * and similar conditions, so that we can log the offending part of the RDB\n * if the crash is due to broken content. */\nvoid rdbCheckHandleCrash(int sig, siginfo_t *info, void *secret) {\n    UNUSED(sig);\n    UNUSED(info);\n    UNUSED(secret);\n\n    rdbCheckError(\"Server crash checking the specified RDB file!\");\n    exit(1);\n}\n\nvoid rdbCheckSetupSignals(void) {\n    struct sigaction act;\n\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;\n    act.sa_sigaction = rdbCheckHandleCrash;\n    sigaction(SIGSEGV, &act, NULL);\n    sigaction(SIGBUS, &act, NULL);\n    sigaction(SIGFPE, &act, NULL);\n    sigaction(SIGILL, &act, NULL);\n}\n\n/* Check the specified RDB file. Return 0 if the RDB looks sane, otherwise\n * 1 is returned.\n * The file is specified as a filename in 'rdbfilename' if 'fp' is not NULL,\n * otherwise the already open file 'fp' is checked. */\nint redis_check_rdb(char *rdbfilename, FILE *fp) {\n    uint64_t dbid;\n    int type, rdbver;\n    char buf[1024];\n    long long expiretime, now = mstime();\n    static rio rdb; /* Pointed by global struct riostate. */\n\n    int closefile = (fp == NULL);\n    if (fp == NULL && (fp = fopen(rdbfilename,\"r\")) == NULL) return 1;\n\n    rioInitWithFile(&rdb,fp);\n    rdbstate.rio = &rdb;\n    rdb.update_cksum = rdbLoadProgressCallback;\n    if (rioRead(&rdb,buf,9) == 0) goto eoferr;\n    buf[9] = '\\0';\n    if (memcmp(buf,\"REDIS\",5) != 0) {\n        rdbCheckError(\"Wrong signature trying to load DB from file\");\n        goto err;\n    }\n    rdbver = atoi(buf+5);\n    if (rdbver < 1 || rdbver > RDB_VERSION) {\n        rdbCheckError(\"Can't handle RDB format version %d\",rdbver);\n        goto err;\n    }\n\n    expiretime = -1;\n    startLoadingFile(fp, rdbfilename, RDBFLAGS_NONE);\n    while(1) {\n        robj *key, *val;\n\n        /* Read type. */\n        rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;\n        if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;\n\n        /* Handle special types. */\n        if (type == RDB_OPCODE_EXPIRETIME) {\n            rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;\n            /* EXPIRETIME: load an expire associated with the next key\n             * to load. Note that after loading an expire we need to\n             * load the actual type, and continue. */\n            expiretime = rdbLoadTime(&rdb);\n            expiretime *= 1000;\n            if (rioGetReadError(&rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EXPIRETIME_MS) {\n            /* EXPIRETIME_MS: milliseconds precision expire times introduced\n             * with RDB v3. Like EXPIRETIME but no with more precision. */\n            rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;\n            expiretime = rdbLoadMillisecondTime(&rdb, rdbver);\n            if (rioGetReadError(&rdb)) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_FREQ) {\n            /* FREQ: LFU frequency. */\n            uint8_t byte;\n            if (rioRead(&rdb,&byte,1) == 0) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_IDLE) {\n            /* IDLE: LRU idle time. */\n            if (rdbLoadLen(&rdb,NULL) == RDB_LENERR) goto eoferr;\n            continue; /* Read next opcode. */\n        } else if (type == RDB_OPCODE_EOF) {\n            /* EOF: End of file, exit the main loop. */\n            break;\n        } else if (type == RDB_OPCODE_SELECTDB) {\n            /* SELECTDB: Select the specified database. */\n            rdbstate.doing = RDB_CHECK_DOING_READ_LEN;\n            if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            rdbCheckInfo(\"Selecting DB ID %d\", dbid);\n            continue; /* Read type again. */\n        } else if (type == RDB_OPCODE_RESIZEDB) {\n            /* RESIZEDB: Hint about the size of the keys in the currently\n             * selected data base, in order to avoid useless rehashing. */\n            uint64_t db_size, expires_size;\n            rdbstate.doing = RDB_CHECK_DOING_READ_LEN;\n            if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)\n                goto eoferr;\n            continue; /* Read type again. */\n        } else if (type == RDB_OPCODE_AUX) {\n            /* AUX: generic string-string fields. Use to add state to RDB\n             * which is backward compatible. Implementations of RDB loading\n             * are requierd to skip AUX fields they don't understand.\n             *\n             * An AUX field is composed of two strings: key and value. */\n            robj *auxkey, *auxval;\n            rdbstate.doing = RDB_CHECK_DOING_READ_AUX;\n            if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n            if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n\n            rdbCheckInfo(\"AUX FIELD %s = '%s'\",\n                (char*)auxkey->ptr, (char*)auxval->ptr);\n            decrRefCount(auxkey);\n            decrRefCount(auxval);\n            continue; /* Read type again. */\n        } else {\n            if (!rdbIsObjectType(type)) {\n                rdbCheckError(\"Invalid object type: %d\", type);\n                goto err;\n            }\n            rdbstate.key_type = type;\n        }\n\n        /* Read key */\n        rdbstate.doing = RDB_CHECK_DOING_READ_KEY;\n        if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;\n        rdbstate.key = key;\n        rdbstate.keys++;\n        /* Read value */\n        rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;\n        if ((val = rdbLoadObject(type,&rdb,key->ptr)) == NULL) goto eoferr;\n        /* Check if the key already expired. */\n        if (expiretime != -1 && expiretime < now)\n            rdbstate.already_expired++;\n        if (expiretime != -1) rdbstate.expires++;\n        rdbstate.key = NULL;\n        decrRefCount(key);\n        decrRefCount(val);\n        rdbstate.key_type = -1;\n        expiretime = -1;\n    }\n    /* Verify the checksum if RDB version is >= 5 */\n    if (rdbver >= 5 && server.rdb_checksum) {\n        uint64_t cksum, expected = rdb.cksum;\n\n        rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM;\n        if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;\n        memrev64ifbe(&cksum);\n        if (cksum == 0) {\n            rdbCheckInfo(\"RDB file was saved with checksum disabled: no check performed.\");\n        } else if (cksum != expected) {\n            rdbCheckError(\"RDB CRC error\");\n            goto err;\n        } else {\n            rdbCheckInfo(\"Checksum OK\");\n        }\n    }\n\n    if (closefile) fclose(fp);\n    stopLoading(1);\n    return 0;\n\neoferr: /* unexpected end of file is handled here with a fatal exit */\n    if (rdbstate.error_set) {\n        rdbCheckError(rdbstate.error);\n    } else {\n        rdbCheckError(\"Unexpected EOF reading RDB file\");\n    }\nerr:\n    if (closefile) fclose(fp);\n    stopLoading(0);\n    return 1;\n}\n\n/* RDB check main: called form redis.c when Redis is executed with the\n * redis-check-rdb alias, on during RDB loading errors.\n *\n * The function works in two ways: can be called with argc/argv as a\n * standalone executable, or called with a non NULL 'fp' argument if we\n * already have an open file to check. This happens when the function\n * is used to check an RDB preamble inside an AOF file.\n *\n * When called with fp = NULL, the function never returns, but exits with the\n * status code according to success (RDB is sane) or error (RDB is corrupted).\n * Otherwise if called with a non NULL fp, the function returns C_OK or\n * C_ERR depending on the success or failure. */\nint redis_check_rdb_main(int argc, char **argv, FILE *fp) {\n    if (argc != 2 && fp == NULL) {\n        fprintf(stderr, \"Usage: %s <rdb-file-name>\\n\", argv[0]);\n        exit(1);\n    }\n    /* In order to call the loading functions we need to create the shared\n     * integer objects, however since this function may be called from\n     * an already initialized Redis instance, check if we really need to. */\n    if (shared.integers[0] == NULL)\n        createSharedObjects();\n    server.loading_process_events_interval_bytes = 0;\n    rdbCheckMode = 1;\n    rdbCheckInfo(\"Checking RDB file %s\", argv[1]);\n    rdbCheckSetupSignals();\n    int retval = redis_check_rdb(argv[1],fp);\n    if (retval == 0) {\n        rdbCheckInfo(\"\\\\o/ RDB looks OK! \\\\o/\");\n        rdbShowGenericInfo();\n    }\n    if (fp) return (retval == 0) ? C_OK : C_ERR;\n    exit(retval);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 <signal.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#include <math.h>\n\n#include <hiredis.h>\n#ifdef USE_OPENSSL\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <hiredis_ssl.h>\n#endif\n#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */\n#include \"dict.h\"\n#include \"adlist.h\"\n#include \"zmalloc.h\"\n#include \"linenoise.h\"\n#include \"help.h\"\n#include \"anet.h\"\n#include \"ae.h\"\n\n#define UNUSED(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#define REDIS_CLI_HISTFILE_ENV \"REDISCLI_HISTFILE\"\n#define REDIS_CLI_HISTFILE_DEFAULT \".rediscli_history\"\n#define REDIS_CLI_RCFILE_ENV \"REDISCLI_RCFILE\"\n#define REDIS_CLI_RCFILE_DEFAULT \".redisclirc\"\n#define REDIS_CLI_AUTH_ENV \"REDISCLI_AUTH\"\n\n#define CLUSTER_MANAGER_SLOTS               16384\n#define CLUSTER_MANAGER_MIGRATE_TIMEOUT     60000\n#define CLUSTER_MANAGER_MIGRATE_PIPELINE    10\n#define CLUSTER_MANAGER_REBALANCE_THRESHOLD 2\n\n#define CLUSTER_MANAGER_INVALID_HOST_ARG \\\n    \"[ERR] Invalid arguments: you need to pass either a valid \" \\\n    \"address (ie. 120.0.0.1:7000) or space separated IP \" \\\n    \"and port (ie. 120.0.0.1 7000)\\n\"\n#define CLUSTER_MANAGER_MODE() (config.cluster_manager_command.name != NULL)\n#define CLUSTER_MANAGER_MASTERS_COUNT(nodes, replicas) (nodes/(replicas + 1))\n#define CLUSTER_MANAGER_COMMAND(n,...) \\\n        (redisCommand(n->context, __VA_ARGS__))\n\n#define CLUSTER_MANAGER_NODE_ARRAY_FREE(array) zfree(array->alloc)\n\n#define CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err) \\\n    clusterManagerLogErr(\"Node %s:%d replied with error:\\n%s\\n\", \\\n                         n->ip, n->port, err);\n\n#define clusterManagerLogInfo(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_INFO,__VA_ARGS__)\n\n#define clusterManagerLogErr(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_ERR,__VA_ARGS__)\n\n#define clusterManagerLogWarn(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_WARN,__VA_ARGS__)\n\n#define clusterManagerLogOk(...) \\\n    clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_SUCCESS,__VA_ARGS__)\n\n#define CLUSTER_MANAGER_FLAG_MYSELF     1 << 0\n#define CLUSTER_MANAGER_FLAG_SLAVE      1 << 1\n#define CLUSTER_MANAGER_FLAG_FRIEND     1 << 2\n#define CLUSTER_MANAGER_FLAG_NOADDR     1 << 3\n#define CLUSTER_MANAGER_FLAG_DISCONNECT 1 << 4\n#define CLUSTER_MANAGER_FLAG_FAIL       1 << 5\n\n#define CLUSTER_MANAGER_CMD_FLAG_FIX            1 << 0\n#define CLUSTER_MANAGER_CMD_FLAG_SLAVE          1 << 1\n#define CLUSTER_MANAGER_CMD_FLAG_YES            1 << 2\n#define CLUSTER_MANAGER_CMD_FLAG_AUTOWEIGHTS    1 << 3\n#define CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER    1 << 4\n#define CLUSTER_MANAGER_CMD_FLAG_SIMULATE       1 << 5\n#define CLUSTER_MANAGER_CMD_FLAG_REPLACE        1 << 6\n#define CLUSTER_MANAGER_CMD_FLAG_COPY           1 << 7\n#define CLUSTER_MANAGER_CMD_FLAG_COLOR          1 << 8\n#define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS   1 << 9\n#define CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS 1 << 10\n\n#define CLUSTER_MANAGER_OPT_GETFRIENDS  1 << 0\n#define CLUSTER_MANAGER_OPT_COLD        1 << 1\n#define CLUSTER_MANAGER_OPT_UPDATE      1 << 2\n#define CLUSTER_MANAGER_OPT_QUIET       1 << 6\n#define CLUSTER_MANAGER_OPT_VERBOSE     1 << 7\n\n#define CLUSTER_MANAGER_LOG_LVL_INFO    1\n#define CLUSTER_MANAGER_LOG_LVL_WARN    2\n#define CLUSTER_MANAGER_LOG_LVL_ERR     3\n#define CLUSTER_MANAGER_LOG_LVL_SUCCESS 4\n\n#define CLUSTER_JOIN_CHECK_AFTER        20\n\n#define LOG_COLOR_BOLD      \"29;1m\"\n#define LOG_COLOR_RED       \"31;1m\"\n#define LOG_COLOR_GREEN     \"32;1m\"\n#define LOG_COLOR_YELLOW    \"33;1m\"\n#define LOG_COLOR_RESET     \"0m\"\n\n/* cliConnect() flags. */\n#define CC_FORCE (1<<0)         /* Re-connect if already connected. */\n#define CC_QUIET (1<<1)         /* Don't log connecting errors. */\n\n/* --latency-dist palettes. */\nint spectrum_palette_color_size = 19;\nint spectrum_palette_color[] = {0,233,234,235,237,239,241,243,245,247,144,143,142,184,226,214,208,202,196};\n\nint spectrum_palette_mono_size = 13;\nint spectrum_palette_mono[] = {0,233,234,235,237,239,241,243,245,247,249,251,253};\n\n/* The actual palette in use. */\nint *spectrum_palette;\nint spectrum_palette_size;\n\n/* Dict Helpers */\n\nstatic uint64_t dictSdsHash(const void *key);\nstatic int dictSdsKeyCompare(void *privdata, const void *key1,\n    const void *key2);\nstatic void dictSdsDestructor(void *privdata, void *val);\nstatic void dictListDestructor(void *privdata, void *val);\n\n/* Cluster Manager Command Info */\ntypedef struct clusterManagerCommand {\n    char *name;\n    int argc;\n    char **argv;\n    int flags;\n    int replicas;\n    char *from;\n    char *to;\n    char **weight;\n    int weight_argc;\n    char *master_id;\n    int slots;\n    int timeout;\n    int pipeline;\n    float threshold;\n    char *backup_dir;\n} clusterManagerCommand;\n\nstatic void createClusterManagerCommand(char *cmdname, int argc, char **argv);\n\n\nstatic redisContext *context;\nstatic struct config {\n    char *hostip;\n    int hostport;\n    char *hostsocket;\n    int tls;\n    char *sni;\n    char *cacert;\n    char *cacertdir;\n    char *cert;\n    char *key;\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_dist_mode;\n    int latency_history;\n    int lru_test_mode;\n    long long lru_test_sample_size;\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 memkeys;\n    unsigned memkeys_samples;\n    int hotkeys;\n    int stdinarg; /* get last arg from stdin. (-x option) */\n    char *auth;\n    int askpass;\n    char *user;\n    int output; /* output mode, see OUTPUT_* defines */\n    sds mb_delim;\n    char prompt[128];\n    char *eval;\n    int eval_ldb;\n    int eval_ldb_sync;  /* Ask for synchronous mode of the Lua debugger. */\n    int eval_ldb_end;   /* Lua debugging session ended. */\n    int enable_ldb_on_eval; /* Handle manual SCRIPT DEBUG + EVAL commands. */\n    int last_cmd_type;\n    int verbose;\n    clusterManagerCommand cluster_manager_command;\n    int no_auth_warning;\n    int resp3;\n} config;\n\n/* User preferences. */\nstatic struct pref {\n    int hints;\n} pref;\n\nstatic volatile sig_atomic_t force_cancel_loop = 0;\nstatic void usage(void);\nstatic void slaveMode(void);\nchar *redisGitSHA1(void);\nchar *redisGitDirty(void);\nstatic int cliConnect(int force);\n\nstatic char *getInfoField(char *info, char *field);\nstatic long getLongInfoField(char *info, char *field);\n\n/*------------------------------------------------------------------------------\n * Utility functions\n *--------------------------------------------------------------------------- */\n\nuint16_t crc16(const char *buf, int len);\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    if (config.eval_ldb) return;\n\n    sds prompt = sdsempty();\n    if (config.hostsocket != NULL) {\n        prompt = sdscatfmt(prompt,\"redis %s\",config.hostsocket);\n    } else {\n        char addr[256];\n        anetFormatAddr(addr, sizeof(addr), config.hostip, config.hostport);\n        prompt = sdscatlen(prompt,addr,strlen(addr));\n    }\n\n    /* Add [dbnum] if needed */\n    if (config.dbnum != 0)\n        prompt = sdscatfmt(prompt,\"[%i]\",config.dbnum);\n\n    /* Copy the prompt in the static buffer. */\n    prompt = sdscatlen(prompt,\"> \",2);\n    snprintf(config.prompt,sizeof(config.prompt),\"%s\",prompt);\n    sdsfree(prompt);\n}\n\n/* Return the name of the dotfile for the specified 'dotfilename'.\n * Normally it just concatenates user $HOME to the file specified\n * in 'dotfilename'. However if the environment varialbe 'envoverride'\n * is set, its value is taken as the path.\n *\n * The function returns NULL (if the file is /dev/null or cannot be\n * obtained for some error), or an SDS string that must be freed by\n * the user. */\nstatic sds getDotfilePath(char *envoverride, char *dotfilename) {\n    char *path = NULL;\n    sds dotPath = NULL;\n\n    /* Check the env for a dotfile override. */\n    path = getenv(envoverride);\n    if (path != NULL && *path != '\\0') {\n        if (!strcmp(\"/dev/null\", path)) {\n            return NULL;\n        }\n\n        /* If the env is set, return it. */\n        dotPath = sdsnew(path);\n    } else {\n        char *home = getenv(\"HOME\");\n        if (home != NULL && *home != '\\0') {\n            /* If no override is set use $HOME/<dotfilename>. */\n            dotPath = sdscatprintf(sdsempty(), \"%s/%s\", home, dotfilename);\n        }\n    }\n    return dotPath;\n}\n\n/* URL-style percent decoding. */\n#define isHexChar(c) (isdigit(c) || (c >= 'a' && c <= 'f'))\n#define decodeHexChar(c) (isdigit(c) ? c - '0' : c - 'a' + 10)\n#define decodeHex(h, l) ((decodeHexChar(h) << 4) + decodeHexChar(l))\n\nstatic sds percentDecode(const char *pe, size_t len) {\n    const char *end = pe + len;\n    sds ret = sdsempty();\n    const char *curr = pe;\n\n    while (curr < end) {\n        if (*curr == '%') {\n            if ((end - curr) < 2) {\n                fprintf(stderr, \"Incomplete URI encoding\\n\");\n                exit(1);\n            }\n\n            char h = tolower(*(++curr));\n            char l = tolower(*(++curr));\n            if (!isHexChar(h) || !isHexChar(l)) {\n                fprintf(stderr, \"Illegal character in URI encoding\\n\");\n                exit(1);\n            }\n            char c = decodeHex(h, l);\n            ret = sdscatlen(ret, &c, 1);\n            curr++;\n        } else {\n            ret = sdscatlen(ret, curr++, 1);\n        }\n    }\n\n    return ret;\n}\n\n/* Parse a URI and extract the server connection information.\n * URI scheme is based on the the provisional specification[1] excluding support\n * for query parameters. Valid URIs are:\n *   scheme:    \"redis://\"\n *   authority: [[<username> \":\"] <password> \"@\"] [<hostname> [\":\" <port>]]\n *   path:      [\"/\" [<db>]]\n *\n *  [1]: https://www.iana.org/assignments/uri-schemes/prov/redis */\nstatic void parseRedisUri(const char *uri) {\n\n    const char *scheme = \"redis://\";\n    const char *curr = uri;\n    const char *end = uri + strlen(uri);\n    const char *userinfo, *username, *port, *host, *path;\n\n    /* URI must start with a valid scheme. */\n    if (strncasecmp(scheme, curr, strlen(scheme))) {\n        fprintf(stderr,\"Invalid URI scheme\\n\");\n        exit(1);\n    }\n    curr += strlen(scheme);\n    if (curr == end) return;\n\n    /* Extract user info. */\n    if ((userinfo = strchr(curr,'@'))) {\n        if ((username = strchr(curr, ':')) && username < userinfo) {\n            /* If provided, username is ignored. */\n            curr = username + 1;\n        }\n\n        config.auth = percentDecode(curr, userinfo - curr);\n        curr = userinfo + 1;\n    }\n    if (curr == end) return;\n\n    /* Extract host and port. */\n    path = strchr(curr, '/');\n    if (*curr != '/') {\n        host = path ? path - 1 : end;\n        if ((port = strchr(curr, ':'))) {\n            config.hostport = atoi(port + 1);\n            host = port - 1;\n        }\n        config.hostip = sdsnewlen(curr, host - curr + 1);\n    }\n    curr = path ? path + 1 : end;\n    if (curr == end) return;\n\n    /* Extract database number. */\n    config.dbnum = atoi(curr);\n}\n\nstatic uint64_t dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nstatic int 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\nstatic void dictSdsDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    sdsfree(val);\n}\n\nvoid dictListDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n    listRelease((list*)val);\n}\n\n/* _serverAssert is needed by dict */\nvoid _serverAssert(const char *estr, const char *file, int line) {\n    fprintf(stderr, \"=== ASSERTION FAILED ===\");\n    fprintf(stderr, \"==> %s:%d '%s' is not true\",file,line,estr);\n    *((char*)-1) = 'x';\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(void) {\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(void) {\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 = zmalloc(sizeof(helpEntry)*len);\n\n    for (i = 0; i < groupslen; i++) {\n        tmp.argc = 1;\n        tmp.argv = zmalloc(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/* cliInitHelp() setups the helpEntries array with the command and group\n * names from the help.h file. However the Redis instance we are connecting\n * to may support more commands, so this function integrates the previous\n * entries with additional entries obtained using the COMMAND command\n * available in recent versions of Redis. */\nstatic void cliIntegrateHelp(void) {\n    if (cliConnect(CC_QUIET) == REDIS_ERR) return;\n\n    redisReply *reply = redisCommand(context, \"COMMAND\");\n    if(reply == NULL || reply->type != REDIS_REPLY_ARRAY) return;\n\n    /* Scan the array reported by COMMAND and fill only the entries that\n     * don't already match what we have. */\n    for (size_t j = 0; j < reply->elements; j++) {\n        redisReply *entry = reply->element[j];\n        if (entry->type != REDIS_REPLY_ARRAY || entry->elements < 4 ||\n            entry->element[0]->type != REDIS_REPLY_STRING ||\n            entry->element[1]->type != REDIS_REPLY_INTEGER ||\n            entry->element[3]->type != REDIS_REPLY_INTEGER) return;\n        char *cmdname = entry->element[0]->str;\n        int i;\n\n        for (i = 0; i < helpEntriesLen; i++) {\n            helpEntry *he = helpEntries+i;\n            if (!strcasecmp(he->argv[0],cmdname))\n                break;\n        }\n        if (i != helpEntriesLen) continue;\n\n        helpEntriesLen++;\n        helpEntries = zrealloc(helpEntries,sizeof(helpEntry)*helpEntriesLen);\n        helpEntry *new = helpEntries+(helpEntriesLen-1);\n\n        new->argc = 1;\n        new->argv = zmalloc(sizeof(sds));\n        new->argv[0] = sdsnew(cmdname);\n        new->full = new->argv[0];\n        new->type = CLI_HELP_COMMAND;\n        sdstoupper(new->argv[0]);\n\n        struct commandHelp *ch = zmalloc(sizeof(*ch));\n        ch->name = new->argv[0];\n        ch->params = sdsempty();\n        int args = llabs(entry->element[1]->integer);\n        args--; /* Remove the command name itself. */\n        if (entry->element[3]->integer == 1) {\n            ch->params = sdscat(ch->params,\"key \");\n            args--;\n        }\n        while(args-- > 0) ch->params = sdscat(ch->params,\"arg \");\n        if (entry->element[1]->integer < 0)\n            ch->params = sdscat(ch->params,\"...options...\");\n        ch->summary = \"Help not available\";\n        ch->group = 0;\n        ch->since = \"not known\";\n        new->org = ch;\n    }\n    freeReplyObject(reply);\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(void) {\n    sds version = cliVersion();\n    printf(\n        \"redis-cli %s\\n\"\n        \"To get help about Redis commands type:\\n\"\n        \"      \\\"help @<group>\\\" to get a list of commands in <group>\\n\"\n        \"      \\\"help <command>\\\" for help on <command>\\n\"\n        \"      \\\"help <tab>\\\" to get a list of possible help topics\\n\"\n        \"      \\\"quit\\\" to exit\\n\"\n        \"\\n\"\n        \"To set redis-cli preferences:\\n\"\n        \"      \\\":set hints\\\" enable online hints\\n\"\n        \"      \\\":set nohints\\\" disable online hints\\n\"\n        \"Set your preferences in ~/.redisclirc\\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\n/* Linenoise completion callback. */\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/* Linenoise hints callback. */\nstatic char *hintsCallback(const char *buf, int *color, int *bold) {\n    if (!pref.hints) return NULL;\n\n    int i, argc, buflen = strlen(buf);\n    sds *argv = sdssplitargs(buf,&argc);\n    int endspace = buflen && isspace(buf[buflen-1]);\n\n    /* Check if the argument list is empty and return ASAP. */\n    if (argc == 0) {\n        sdsfreesplitres(argv,argc);\n        return NULL;\n    }\n\n    for (i = 0; i < helpEntriesLen; i++) {\n        if (!(helpEntries[i].type & CLI_HELP_COMMAND)) continue;\n\n        if (strcasecmp(argv[0],helpEntries[i].full) == 0 ||\n            strcasecmp(buf,helpEntries[i].full) == 0)\n        {\n            *color = 90;\n            *bold = 0;\n            sds hint = sdsnew(helpEntries[i].org->params);\n\n            /* Remove arguments from the returned hint to show only the\n             * ones the user did not yet typed. */\n            int toremove = argc-1;\n            while(toremove > 0 && sdslen(hint)) {\n                if (hint[0] == '[') break;\n                if (hint[0] == ' ') toremove--;\n                sdsrange(hint,1,-1);\n            }\n\n            /* Add an initial space if needed. */\n            if (!endspace) {\n                sds newhint = sdsnewlen(\" \",1);\n                newhint = sdscatsds(newhint,hint);\n                sdsfree(hint);\n                hint = newhint;\n            }\n\n            sdsfreesplitres(argv,argc);\n            return hint;\n        }\n    }\n    sdsfreesplitres(argv,argc);\n    return NULL;\n}\n\nstatic void freeHintsCallback(void *ptr) {\n    sdsfree(ptr);\n}\n\n/*------------------------------------------------------------------------------\n * Networking / parsing\n *--------------------------------------------------------------------------- */\n\n/* Send AUTH command to the server */\nstatic int cliAuth(void) {\n    redisReply *reply;\n    if (config.auth == NULL) return REDIS_OK;\n\n    if (config.user == NULL)\n        reply = redisCommand(context,\"AUTH %s\",config.auth);\n    else\n        reply = redisCommand(context,\"AUTH %s %s\",config.user,config.auth);\n    if (reply != NULL) {\n        if (reply->type == REDIS_REPLY_ERROR)\n            fprintf(stderr,\"Warning: AUTH failed\\n\");\n        freeReplyObject(reply);\n        return REDIS_OK;\n    }\n    return REDIS_ERR;\n}\n\n/* Send SELECT dbnum to the server */\nstatic int cliSelect(void) {\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        int result = REDIS_OK;\n        if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;\n        freeReplyObject(reply);\n        return result;\n    }\n    return REDIS_ERR;\n}\n\n/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if\n * not building with TLS support.\n */\nstatic int cliSecureConnection(redisContext *c, const char **err) {\n#ifdef USE_OPENSSL\n    static SSL_CTX *ssl_ctx = NULL;\n\n    if (!ssl_ctx) {\n        ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n        if (!ssl_ctx) {\n            *err = \"Failed to create SSL_CTX\";\n            goto error;\n        }\n\n        SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);\n        SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);\n\n        if (config.cacert || config.cacertdir) {\n            if (!SSL_CTX_load_verify_locations(ssl_ctx, config.cacert, config.cacertdir)) {\n                *err = \"Invalid CA Certificate File/Directory\";\n                goto error;\n            }\n        } else {\n            if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {\n                *err = \"Failed to use default CA paths\";\n                goto error;\n            }\n        }\n\n        if (config.cert && !SSL_CTX_use_certificate_chain_file(ssl_ctx, config.cert)) {\n            *err = \"Invalid client certificate\";\n            goto error;\n        }\n\n        if (config.key && !SSL_CTX_use_PrivateKey_file(ssl_ctx, config.key, SSL_FILETYPE_PEM)) {\n            *err = \"Invalid private key\";\n            goto error;\n        }\n    }\n\n    SSL *ssl = SSL_new(ssl_ctx);\n    if (!ssl) {\n        *err = \"Failed to create SSL object\";\n        return REDIS_ERR;\n    }\n\n    if (config.sni && !SSL_set_tlsext_host_name(ssl, config.sni)) {\n        *err = \"Failed to configure SNI\";\n        SSL_free(ssl);\n        return REDIS_ERR;\n    }\n\n    return redisInitiateSSL(c, ssl);\n\nerror:\n    SSL_CTX_free(ssl_ctx);\n    ssl_ctx = NULL;\n    return REDIS_ERR;\n#else\n    (void) c;\n    (void) err;\n    return REDIS_OK;\n#endif\n}\n\n/* Select RESP3 mode if redis-cli was started with the -3 option.  */\nstatic int cliSwitchProto(void) {\n    redisReply *reply;\n    if (config.resp3 == 0) return REDIS_OK;\n\n    reply = redisCommand(context,\"HELLO 3\");\n    if (reply != NULL) {\n        int result = REDIS_OK;\n        if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;\n        freeReplyObject(reply);\n        return result;\n    }\n    return REDIS_ERR;\n}\n\n/* Connect to the server. It is possible to pass certain flags to the function:\n *      CC_FORCE: The connection is performed even if there is already\n *                a connected socket.\n *      CC_QUIET: Don't print errors if connection fails. */\nstatic int cliConnect(int flags) {\n    if (context == NULL || flags & CC_FORCE) {\n        if (context != NULL) {\n            redisFree(context);\n        }\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 && config.tls) {\n            const char *err = NULL;\n            if (cliSecureConnection(context, &err) == REDIS_ERR && err) {\n                fprintf(stderr, \"Could not negotiate a TLS connection: %s\\n\", err);\n                context = NULL;\n                redisFree(context);\n                return REDIS_ERR;\n            }\n        }\n\n        if (context->err) {\n            if (!(flags & CC_QUIET)) {\n                fprintf(stderr,\"Could not connect to Redis at \");\n                if (config.hostsocket == NULL)\n                    fprintf(stderr,\"%s:%d: %s\\n\",\n                        config.hostip,config.hostport,context->errstr);\n                else\n                    fprintf(stderr,\"%s: %s\\n\",\n                        config.hostsocket,context->errstr);\n            }\n            redisFree(context);\n            context = NULL;\n            return REDIS_ERR;\n        }\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, select the right DB, switch to RESP3 if needed. */\n        if (cliAuth() != REDIS_OK)\n            return REDIS_ERR;\n        if (cliSelect() != REDIS_OK)\n            return REDIS_ERR;\n        if (cliSwitchProto() != REDIS_OK)\n            return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic void cliPrintContextError(void) {\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_DOUBLE:\n        out = sdscatprintf(out,\"(double) %s\\n\",r->str);\n    break;\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        /* If you are producing output for the standard output we want\n        * a more interesting output with quoted characters and so forth,\n        * unless it's a verbatim string type. */\n        if (r->type == REDIS_REPLY_STRING) {\n            out = sdscatrepr(out,r->str,r->len);\n            out = sdscat(out,\"\\n\");\n        } else {\n            out = sdscatlen(out,r->str,r->len);\n            out = sdscat(out,\"\\n\");\n        }\n    break;\n    case REDIS_REPLY_NIL:\n        out = sdscat(out,\"(nil)\\n\");\n    break;\n    case REDIS_REPLY_BOOL:\n        out = sdscat(out,r->integer ? \"(true)\\n\" : \"(false)\\n\");\n    break;\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n        if (r->elements == 0) {\n            if (r->type == REDIS_REPLY_ARRAY)\n                out = sdscat(out,\"(empty array)\\n\");\n            else if (r->type == REDIS_REPLY_MAP)\n                out = sdscat(out,\"(empty hash)\\n\");\n            else if (r->type == REDIS_REPLY_SET)\n                out = sdscat(out,\"(empty set)\\n\");\n            else\n                out = sdscat(out,\"(empty aggregate type)\\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            if (r->type == REDIS_REPLY_MAP) i /= 2;\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            char numsep;\n            if (r->type == REDIS_REPLY_SET) numsep = '~';\n            else if (r->type == REDIS_REPLY_MAP) numsep = '#';\n            else numsep = ')';\n            snprintf(_prefixfmt,sizeof(_prefixfmt),\"%%s%%%ud%c \",idxlen,numsep);\n\n            for (i = 0; i < r->elements; i++) {\n                unsigned int human_idx = (r->type == REDIS_REPLY_MAP) ?\n                                         i/2 : i;\n                human_idx++; /* Make it 1-based. */\n\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,human_idx);\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                /* For maps, format the value as well. */\n                if (r->type == REDIS_REPLY_MAP) {\n                    i++;\n                    sdsrange(out,0,-2);\n                    out = sdscat(out,\" => \");\n                    tmp = cliFormatReplyTTY(r->element[i],_prefix);\n                    out = sdscatlen(out,tmp,sdslen(tmp));\n                    sdsfree(tmp);\n                }\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\nint isColorTerm(void) {\n    char *t = getenv(\"TERM\");\n    return t != NULL && strstr(t,\"xterm\") != NULL;\n}\n\n/* Helper  function for sdsCatColorizedLdbReply() appending colorize strings\n * to an SDS string. */\nsds sdscatcolor(sds o, char *s, size_t len, char *color) {\n    if (!isColorTerm()) return sdscatlen(o,s,len);\n\n    int bold = strstr(color,\"bold\") != NULL;\n    int ccode = 37; /* Defaults to white. */\n    if (strstr(color,\"red\")) ccode = 31;\n    else if (strstr(color,\"green\")) ccode = 32;\n    else if (strstr(color,\"yellow\")) ccode = 33;\n    else if (strstr(color,\"blue\")) ccode = 34;\n    else if (strstr(color,\"magenta\")) ccode = 35;\n    else if (strstr(color,\"cyan\")) ccode = 36;\n    else if (strstr(color,\"white\")) ccode = 37;\n\n    o = sdscatfmt(o,\"\\033[%i;%i;49m\",bold,ccode);\n    o = sdscatlen(o,s,len);\n    o = sdscat(o,\"\\033[0m\");\n    return o;\n}\n\n/* Colorize Lua debugger status replies according to the prefix they\n * have. */\nsds sdsCatColorizedLdbReply(sds o, char *s, size_t len) {\n    char *color = \"white\";\n\n    if (strstr(s,\"<debug>\")) color = \"bold\";\n    if (strstr(s,\"<redis>\")) color = \"green\";\n    if (strstr(s,\"<reply>\")) color = \"cyan\";\n    if (strstr(s,\"<error>\")) color = \"red\";\n    if (strstr(s,\"<hint>\")) color = \"bold\";\n    if (strstr(s,\"<value>\") || strstr(s,\"<retval>\")) color = \"magenta\";\n    if (len > 4 && isdigit(s[3])) {\n        if (s[1] == '>') color = \"yellow\"; /* Current line. */\n        else if (s[2] == '#') color = \"bold\"; /* Break point. */\n    }\n    return sdscatcolor(o,s,len,color);\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    case REDIS_REPLY_VERB:\n        if (r->type == REDIS_REPLY_STATUS && config.eval_ldb) {\n            /* The Lua debugger replies with arrays of simple (status)\n             * strings. We colorize the output for more fun if this\n             * is a debugging session. */\n\n            /* Detect the end of a debugging session. */\n            if (strstr(r->str,\"<endsession>\") == r->str) {\n                config.enable_ldb_on_eval = 0;\n                config.eval_ldb = 0;\n                config.eval_ldb_end = 1; /* Signal the caller session ended. */\n                config.output = OUTPUT_STANDARD;\n                cliRefreshPrompt();\n            } else {\n                out = sdsCatColorizedLdbReply(out,r->str,r->len);\n            }\n        } else {\n            out = sdscatlen(out,r->str,r->len);\n        }\n        break;\n    case REDIS_REPLY_BOOL:\n        out = sdscat(out,r->integer ? \"(true)\" : \"(false)\");\n    break;\n    case REDIS_REPLY_INTEGER:\n        out = sdscatprintf(out,\"%lld\",r->integer);\n        break;\n    case REDIS_REPLY_DOUBLE:\n        out = sdscatprintf(out,\"%s\",r->str);\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    case REDIS_REPLY_MAP:\n        for (i = 0; i < r->elements; i += 2) {\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            out = sdscatlen(out,\" \",1);\n            tmp = cliFormatReplyRaw(r->element[i+1]);\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_DOUBLE:\n        out = sdscatprintf(out,\"%s\",r->str);\n        break;\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        out = sdscatrepr(out,r->str,r->len);\n    break;\n    case REDIS_REPLY_NIL:\n        out = sdscat(out,\"NULL\");\n    break;\n    case REDIS_REPLY_BOOL:\n        out = sdscat(out,r->integer ? \"true\" : \"false\");\n    break;\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP: /* CSV has no map type, just output flat list. */\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            redisFree(context);\n            context = NULL;\n            return REDIS_OK;\n        }\n        if (config.interactive) {\n            /* Filter cases where we should reconnect */\n            if (context->err == REDIS_ERR_IO &&\n                (errno == ECONNRESET || errno == EPIPE))\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    config.last_cmd_type = reply->type;\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 = strrchr(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, long repeat) {\n    char *command = argv[0];\n    size_t *argvlen;\n    int j, output_raw;\n\n    if (!config.eval_ldb && /* In debugging mode, let's pass \"help\" to Redis. */\n        (!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        !strcasecmp(command,\"lolwut\") ||\n        (argc >= 2 && !strcasecmp(command,\"debug\") &&\n                       !strcasecmp(argv[1],\"htstats\")) ||\n        (argc >= 2 && !strcasecmp(command,\"debug\") &&\n                       !strcasecmp(argv[1],\"htstats-key\")) ||\n        (argc >= 2 && !strcasecmp(command,\"memory\") &&\n                      (!strcasecmp(argv[1],\"malloc-stats\") ||\n                       !strcasecmp(argv[1],\"doctor\"))) ||\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        (argc == 3 && !strcasecmp(command,\"latency\") &&\n                       !strcasecmp(argv[1],\"graph\")) ||\n        (argc == 2 && !strcasecmp(command,\"latency\") &&\n                       !strcasecmp(argv[1],\"doctor\")) ||\n        /* Format PROXY INFO command for Redis Cluster Proxy:\n         * https://github.com/artix75/redis-cluster-proxy */\n        (argc >= 2 && !strcasecmp(command,\"proxy\") &&\n                       !strcasecmp(argv[1],\"info\")))\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    /* When the user manually calls SCRIPT DEBUG, setup the activation of\n     * debugging mode on the next eval if needed. */\n    if (argc == 3 && !strcasecmp(argv[0],\"script\") &&\n                     !strcasecmp(argv[1],\"debug\"))\n    {\n        if (!strcasecmp(argv[2],\"yes\") || !strcasecmp(argv[2],\"sync\")) {\n            config.enable_ldb_on_eval = 1;\n        } else {\n            config.enable_ldb_on_eval = 0;\n        }\n    }\n\n    /* Actually activate LDB on EVAL if needed. */\n    if (!strcasecmp(command,\"eval\") && config.enable_ldb_on_eval) {\n        config.eval_ldb = 1;\n        config.output = OUTPUT_RAW;\n    }\n\n    /* Setup argument length */\n    argvlen = zmalloc(argc*sizeof(size_t));\n    for (j = 0; j < argc; j++)\n        argvlen[j] = sdslen(argv[j]);\n\n    /* Negative repeat is allowed and causes infinite loop,\n       works well with the interval option. */\n    while(repeat < 0 || repeat-- > 0) {\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 replica output mode...  (press Ctrl-C to quit)\\n\");\n            slaveMode();\n            config.slave_mode = 0;\n            zfree(argvlen);\n            return REDIS_ERR;  /* Error = slaveMode lost connection to master */\n        }\n\n        if (cliReadReply(output_raw) != REDIS_OK) {\n            zfree(argvlen);\n            return REDIS_ERR;\n        } else {\n            /* Store database number when SELECT was successfully executed. */\n            if (!strcasecmp(command,\"select\") && argc == 2 && config.last_cmd_type != REDIS_REPLY_ERROR) {\n                config.dbnum = atoi(argv[1]);\n                cliRefreshPrompt();\n            } else if (!strcasecmp(command,\"auth\") && (argc == 2 || argc == 3))\n            {\n                cliSelect();\n            }\n        }\n        if (config.cluster_reissue_command){\n            /* If we need to reissue the command, break to prevent a\n               further 'repeat' number of dud interations */\n            break;\n        }\n        if (config.interval) usleep(config.interval);\n        fflush(stdout); /* Make it grep friendly */\n    }\n\n    zfree(argvlen);\n    return REDIS_OK;\n}\n\n/* Send a command reconnecting the link if needed. */\nstatic redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {\n    redisReply *reply = NULL;\n    int tries = 0;\n    va_list ap;\n\n    assert(!c->err);\n    while(reply == NULL) {\n        while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {\n            printf(\"\\r\\x1b[0K\"); /* Cursor to left edge + clear line. */\n            printf(\"Reconnecting... %d\\r\", ++tries);\n            fflush(stdout);\n\n            redisFree(c);\n            c = redisConnect(config.hostip,config.hostport);\n            if (!c->err && config.tls) {\n                const char *err = NULL;\n                if (cliSecureConnection(c, &err) == REDIS_ERR && err) {\n                    fprintf(stderr, \"TLS Error: %s\\n\", err);\n                    exit(1);\n                }\n            }\n            usleep(1000000);\n        }\n\n        va_start(ap,fmt);\n        reply = redisvCommand(c,fmt,ap);\n        va_end(ap);\n\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(\"\\r\\x1b[0K\"); /* Cursor to left edge + clear line. */\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], \"--no-auth-warning\")) {\n            config.no_auth_warning = 1;\n        } else if (!strcmp(argv[i], \"--askpass\")) {\n            config.askpass = 1;\n        } else if ((!strcmp(argv[i],\"-a\") || !strcmp(argv[i],\"--pass\"))\n                   && !lastarg)\n        {\n            config.auth = argv[++i];\n        } else if (!strcmp(argv[i],\"--user\") && !lastarg) {\n            config.user = argv[++i];\n        } else if (!strcmp(argv[i],\"-u\") && !lastarg) {\n            parseRedisUri(argv[++i]);\n        } else if (!strcmp(argv[i],\"--raw\")) {\n            config.output = OUTPUT_RAW;\n        } else if (!strcmp(argv[i],\"--no-raw\")) {\n            config.output = OUTPUT_STANDARD;\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-dist\")) {\n            config.latency_dist_mode = 1;\n        } else if (!strcmp(argv[i],\"--mono\")) {\n            spectrum_palette = spectrum_palette_mono;\n            spectrum_palette_size = spectrum_palette_mono_size;\n        } else if (!strcmp(argv[i],\"--latency-history\")) {\n            config.latency_mode = 1;\n            config.latency_history = 1;\n        } else if (!strcmp(argv[i],\"--lru-test\") && !lastarg) {\n            config.lru_test_mode = 1;\n            config.lru_test_sample_size = strtoll(argv[++i],NULL,10);\n        } else if (!strcmp(argv[i],\"--slave\")) {\n            config.slave_mode = 1;\n        } else if (!strcmp(argv[i],\"--replica\")) {\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],\"--memkeys\")) {\n            config.memkeys = 1;\n            config.memkeys_samples = 0; /* use redis default */\n        } else if (!strcmp(argv[i],\"--memkeys-samples\")) {\n            config.memkeys = 1;\n            config.memkeys_samples = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--hotkeys\")) {\n            config.hotkeys = 1;\n        } else if (!strcmp(argv[i],\"--eval\") && !lastarg) {\n            config.eval = argv[++i];\n        } else if (!strcmp(argv[i],\"--ldb\")) {\n            config.eval_ldb = 1;\n            config.output = OUTPUT_RAW;\n        } else if (!strcmp(argv[i],\"--ldb-sync-mode\")) {\n            config.eval_ldb = 1;\n            config.eval_ldb_sync = 1;\n            config.output = OUTPUT_RAW;\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],\"--verbose\")) {\n            config.verbose = 1;\n        } else if (!strcmp(argv[i],\"--cluster\") && !lastarg) {\n            if (CLUSTER_MANAGER_MODE()) usage();\n            char *cmd = argv[++i];\n            int j = i;\n            while (j < argc && argv[j][0] != '-') j++;\n            if (j > i) j--;\n            createClusterManagerCommand(cmd, j - i, argv + i + 1);\n            i = j;\n        } else if (!strcmp(argv[i],\"--cluster\") && lastarg) {\n            usage();\n        } else if (!strcmp(argv[i],\"--cluster-replicas\") && !lastarg) {\n            config.cluster_manager_command.replicas = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-master-id\") && !lastarg) {\n            config.cluster_manager_command.master_id = argv[++i];\n        } else if (!strcmp(argv[i],\"--cluster-from\") && !lastarg) {\n            config.cluster_manager_command.from = argv[++i];\n        } else if (!strcmp(argv[i],\"--cluster-to\") && !lastarg) {\n            config.cluster_manager_command.to = argv[++i];\n        } else if (!strcmp(argv[i],\"--cluster-weight\") && !lastarg) {\n            if (config.cluster_manager_command.weight != NULL) {\n                fprintf(stderr, \"WARNING: you cannot use --cluster-weight \"\n                                \"more than once.\\n\"\n                                \"You can set more weights by adding them \"\n                                \"as a space-separated list, ie:\\n\"\n                                \"--cluster-weight n1=w n2=w\\n\");\n                exit(1);\n            }\n            int widx = i + 1;\n            char **weight = argv + widx;\n            int wargc = 0;\n            for (; widx < argc; widx++) {\n                if (strstr(argv[widx], \"--\") == argv[widx]) break;\n                if (strchr(argv[widx], '=') == NULL) break;\n                wargc++;\n            }\n            if (wargc > 0) {\n                config.cluster_manager_command.weight = weight;\n                config.cluster_manager_command.weight_argc = wargc;\n                i += wargc;\n            }\n        } else if (!strcmp(argv[i],\"--cluster-slots\") && !lastarg) {\n            config.cluster_manager_command.slots = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-timeout\") && !lastarg) {\n            config.cluster_manager_command.timeout = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-pipeline\") && !lastarg) {\n            config.cluster_manager_command.pipeline = atoi(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-threshold\") && !lastarg) {\n            config.cluster_manager_command.threshold = atof(argv[++i]);\n        } else if (!strcmp(argv[i],\"--cluster-yes\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_YES;\n        } else if (!strcmp(argv[i],\"--cluster-simulate\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_SIMULATE;\n        } else if (!strcmp(argv[i],\"--cluster-replace\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_REPLACE;\n        } else if (!strcmp(argv[i],\"--cluster-copy\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_COPY;\n        } else if (!strcmp(argv[i],\"--cluster-slave\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_SLAVE;\n        } else if (!strcmp(argv[i],\"--cluster-use-empty-masters\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;\n        } else if (!strcmp(argv[i],\"--cluster-search-multiple-owners\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;\n        } else if (!strcmp(argv[i],\"--cluster-fix-with-unreachable-masters\")) {\n            config.cluster_manager_command.flags |=\n                CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;\n#ifdef USE_OPENSSL\n        } else if (!strcmp(argv[i],\"--tls\")) {\n            config.tls = 1;\n        } else if (!strcmp(argv[i],\"--sni\") && !lastarg) {\n            config.sni = argv[++i];\n        } else if (!strcmp(argv[i],\"--cacertdir\") && !lastarg) {\n            config.cacertdir = argv[++i];\n        } else if (!strcmp(argv[i],\"--cacert\") && !lastarg) {\n            config.cacert = argv[++i];\n        } else if (!strcmp(argv[i],\"--cert\") && !lastarg) {\n            config.cert = argv[++i];\n        } else if (!strcmp(argv[i],\"--key\") && !lastarg) {\n            config.key = argv[++i];\n#endif\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 if (!strcmp(argv[i],\"-3\")) {\n            config.resp3 = 1;\n        } else if (CLUSTER_MANAGER_MODE() && argv[i][0] != '-') {\n            if (config.cluster_manager_command.argc == 0) {\n                int j = i + 1;\n                while (j < argc && argv[j][0] != '-') j++;\n                int cmd_argc = j - i;\n                config.cluster_manager_command.argc = cmd_argc;\n                config.cluster_manager_command.argv = argv + i;\n                if (cmd_argc > 1) i = j - 1;\n            }\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\n    /* --ldb requires --eval. */\n    if (config.eval_ldb && config.eval == NULL) {\n        fprintf(stderr,\"Options --ldb and --ldb-sync-mode require --eval.\\n\");\n        fprintf(stderr,\"Try %s --help for more information.\\n\", argv[0]);\n        exit(1);\n    }\n\n    if (!config.no_auth_warning && config.auth != NULL) {\n        fputs(\"Warning: Using a password with '-a' or '-u' option on the command\"\n              \" line interface may not be safe.\\n\", stderr);\n    }\n\n    return i;\n}\n\nstatic void parseEnv() {\n    /* Set auth from env, but do not overwrite CLI arguments if passed */\n    char *auth = getenv(REDIS_CLI_AUTH_ENV);\n    if (auth != NULL && config.auth == NULL) {\n        config.auth = auth;\n    }\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(void) {\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\"                     You can also use the \" REDIS_CLI_AUTH_ENV \" environment\\n\"\n\"                     variable to pass this password more safely\\n\"\n\"                     (if both are used, this argument takes predecence).\\n\"\n\"  --user <username>  Used to send ACL style 'AUTH username pass'. Needs -a.\\n\"\n\"  --pass <password>  Alias of -a for consistency with the new --user option.\\n\"\n\"  --askpass          Force user to input password with mask from STDIN.\\n\"\n\"                     If this argument is used, '-a' and \" REDIS_CLI_AUTH_ENV \"\\n\"\n\"                     environment variable will be ignored.\\n\"\n\"  -u <uri>           Server URI.\\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\"  -3                 Start session in RESP3 protocol mode.\\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#ifdef USE_OPENSSL\n\"  --tls              Establish a secure TLS connection.\\n\"\n\"  --sni <host>       Server name indication for TLS.\\n\"\n\"  --cacert <file>    CA Certificate file to verify with.\\n\"\n\"  --cacertdir <dir>  Directory where trusted CA certificates are stored.\\n\"\n\"                     If neither cacert nor cacertdir are specified, the default\\n\"\n\"                     system-wide trusted root certs configuration will apply.\\n\"\n\"  --cert <file>      Client certificate to authenticate with.\\n\"\n\"  --key <file>       Private key file to authenticate with.\\n\"\n#endif\n\"  --raw              Use raw formatting for replies (default when STDOUT is\\n\"\n\"                     not a tty).\\n\"\n\"  --no-raw           Force formatted output even when STDOUT is not a tty.\\n\"\n\"  --csv              Output in CSV format.\\n\"\n\"  --stat             Print rolling stats about server: mem, clients, ...\\n\"\n\"  --latency          Enter a special mode continuously sampling latency.\\n\"\n\"                     If you use this mode in an interactive session it runs\\n\"\n\"                     forever displaying real-time stats. Otherwise if --raw or\\n\"\n\"                     --csv is specified, or if you redirect the output to a non\\n\"\n\"                     TTY, it samples the latency for 1 second (you can use\\n\"\n\"                     -i to change the interval), then produces a single output\\n\"\n\"                     and exits.\\n\",version);\n\n    fprintf(stderr,\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\"  --latency-dist     Shows latency as a spectrum, requires xterm 256 colors.\\n\"\n\"                     Default time interval is 1 sec. Change it using -i.\\n\"\n\"  --lru-test <keys>  Simulate a cache workload with an 80-20 distribution.\\n\"\n\"  --replica          Simulate a replica 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    REDIS_CLI_DEFAULT_PIPE_TIMEOUT);\n    fprintf(stderr,\n\"  --bigkeys          Sample Redis keys looking for keys with many elements (complexity).\\n\"\n\"  --memkeys          Sample Redis keys looking for keys consuming a lot of memory.\\n\"\n\"  --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.\\n\"\n\"                     And define number of key elements to sample\\n\"\n\"  --hotkeys          Sample Redis keys looking for hot keys.\\n\"\n\"                     only works when maxmemory-policy is *lfu.\\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\"  --ldb              Used with --eval enable the Redis Lua debugger.\\n\"\n\"  --ldb-sync-mode    Like --ldb but uses the synchronous Lua debugger, in\\n\"\n\"                     this mode the server is blocked and script changes are\\n\"\n\"                     not rolled back from the server memory.\\n\"\n\"  --cluster <command> [args...] [opts...]\\n\"\n\"                     Cluster Manager command and arguments (see below).\\n\"\n\"  --verbose          Verbose mode.\\n\"\n\"  --no-auth-warning  Don't show warning message when using password on command\\n\"\n\"                     line interface.\\n\"\n\"  --help             Output this help and exit.\\n\"\n\"  --version          Output version and exit.\\n\"\n\"\\n\");\n    /* Using another fprintf call to avoid -Woverlength-strings compile warning */\n    fprintf(stderr,\n\"Cluster Manager Commands:\\n\"\n\"  Use --cluster help to list all available cluster manager commands.\\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\"and settings.\\n\"\n\"\\n\");\n    sdsfree(version);\n    exit(1);\n}\n\nstatic int confirmWithYes(char *msg) {\n    printf(\"%s (type 'yes' to accept): \", msg);\n    fflush(stdout);\n    char buf[4];\n    int nread = read(fileno(stdin),buf,4);\n    buf[3] = '\\0';\n    return (nread != 0 && !strcmp(\"yes\", buf));\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\nstatic int issueCommandRepeat(int argc, char **argv, long repeat) {\n    while (1) {\n        config.cluster_reissue_command = 0;\n        if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {\n            cliConnect(CC_FORCE);\n\n            /* If we still cannot send the command print error.\n             * We'll try to reconnect the next time. */\n            if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {\n                cliPrintContextError();\n                return REDIS_ERR;\n            }\n        }\n        /* Issue the command again if we got redirected in cluster mode */\n        if (config.cluster_mode && config.cluster_reissue_command) {\n            cliConnect(CC_FORCE);\n        } else {\n            break;\n        }\n    }\n    return REDIS_OK;\n}\n\nstatic int issueCommand(int argc, char **argv) {\n    return issueCommandRepeat(argc, argv, config.repeat);\n}\n\n/* Split the user provided command into multiple SDS arguments.\n * This function normally uses sdssplitargs() from sds.c which is able\n * to understand \"quoted strings\", escapes and so forth. However when\n * we are in Lua debugging mode and the \"eval\" command is used, we want\n * the remaining Lua script (after \"e \" or \"eval \") to be passed verbatim\n * as a single big argument. */\nstatic sds *cliSplitArgs(char *line, int *argc) {\n    if (config.eval_ldb && (strstr(line,\"eval \") == line ||\n                            strstr(line,\"e \") == line))\n    {\n        sds *argv = sds_malloc(sizeof(sds)*2);\n        *argc = 2;\n        int len = strlen(line);\n        int elen = line[1] == ' ' ? 2 : 5; /* \"e \" or \"eval \"? */\n        argv[0] = sdsnewlen(line,elen-1);\n        argv[1] = sdsnewlen(line+elen,len-elen);\n        return argv;\n    } else {\n        return sdssplitargs(line,argc);\n    }\n}\n\n/* Set the CLI preferences. This function is invoked when an interactive\n * \":command\" is called, or when reading ~/.redisclirc file, in order to\n * set user preferences. */\nvoid cliSetPreferences(char **argv, int argc, int interactive) {\n    if (!strcasecmp(argv[0],\":set\") && argc >= 2) {\n        if (!strcasecmp(argv[1],\"hints\")) pref.hints = 1;\n        else if (!strcasecmp(argv[1],\"nohints\")) pref.hints = 0;\n        else {\n            printf(\"%sunknown redis-cli preference '%s'\\n\",\n                interactive ? \"\" : \".redisclirc: \",\n                argv[1]);\n        }\n    } else {\n        printf(\"%sunknown redis-cli internal command '%s'\\n\",\n            interactive ? \"\" : \".redisclirc: \",\n            argv[0]);\n    }\n}\n\n/* Load the ~/.redisclirc file if any. */\nvoid cliLoadPreferences(void) {\n    sds rcfile = getDotfilePath(REDIS_CLI_RCFILE_ENV,REDIS_CLI_RCFILE_DEFAULT);\n    if (rcfile == NULL) return;\n    FILE *fp = fopen(rcfile,\"r\");\n    char buf[1024];\n\n    if (fp) {\n        while(fgets(buf,sizeof(buf),fp) != NULL) {\n            sds *argv;\n            int argc;\n\n            argv = sdssplitargs(buf,&argc);\n            if (argc > 0) cliSetPreferences(argv,argc,0);\n            sdsfreesplitres(argv,argc);\n        }\n        fclose(fp);\n    }\n    sdsfree(rcfile);\n}\n\nstatic void repl(void) {\n    sds historyfile = NULL;\n    int history = 0;\n    char *line;\n    int argc;\n    sds *argv;\n\n    /* Initialize the help and, if possible, use the COMMAND command in order\n     * to retrieve missing entries. */\n    cliInitHelp();\n    cliIntegrateHelp();\n\n    config.interactive = 1;\n    linenoiseSetMultiLine(1);\n    linenoiseSetCompletionCallback(completionCallback);\n    linenoiseSetHintsCallback(hintsCallback);\n    linenoiseSetFreeHintsCallback(freeHintsCallback);\n\n    /* Only use history and load the rc file when stdin is a tty. */\n    if (isatty(fileno(stdin))) {\n        historyfile = getDotfilePath(REDIS_CLI_HISTFILE_ENV,REDIS_CLI_HISTFILE_DEFAULT);\n        //keep in-memory history always regardless if history file can be determined\n        history = 1;\n        if (historyfile != NULL) {\n            linenoiseHistoryLoad(historyfile);\n        }\n        cliLoadPreferences();\n    }\n\n    cliRefreshPrompt();\n    while((line = linenoise(context ? config.prompt : \"not connected> \")) != NULL) {\n        if (line[0] != '\\0') {\n            long repeat = 1;\n            int skipargs = 0;\n            char *endptr = NULL;\n\n            argv = cliSplitArgs(line,&argc);\n\n            /* check if we have a repeat command option and\n             * need to skip the first arg */\n            if (argv && argc > 0) {\n                errno = 0;\n                repeat = strtol(argv[0], &endptr, 10);\n                if (argc > 1 && *endptr == '\\0') {\n                    if (errno == ERANGE || errno == EINVAL || repeat <= 0) {\n                        fputs(\"Invalid redis-cli repeat command option value.\\n\", stdout);\n                        sdsfreesplitres(argv, argc);\n                        linenoiseFree(line);\n                        continue;\n                    }\n                    skipargs = 1;\n                } else {\n                    repeat = 1;\n                }\n            }\n\n            /* Won't save auth or acl setuser commands in history file */\n            int dangerous = 0;\n            if (argv && argc > 0) {\n                if (!strcasecmp(argv[skipargs], \"auth\")) {\n                    dangerous = 1;\n                } else if (skipargs+1 < argc &&\n                           !strcasecmp(argv[skipargs], \"acl\") &&\n                           !strcasecmp(argv[skipargs+1], \"setuser\"))\n                {\n                    dangerous = 1;\n                }\n            }\n\n            if (!dangerous) {\n                if (history) linenoiseHistoryAdd(line);\n                if (historyfile) linenoiseHistorySave(historyfile);\n            }\n\n            if (argv == NULL) {\n                printf(\"Invalid argument(s)\\n\");\n                linenoiseFree(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 (argv[0][0] == ':') {\n                    cliSetPreferences(argv,argc,1);\n                    sdsfreesplitres(argv,argc);\n                    linenoiseFree(line);\n                    continue;\n                } else if (strcasecmp(argv[0],\"restart\") == 0) {\n                    if (config.eval) {\n                        config.eval_ldb = 1;\n                        config.output = OUTPUT_RAW;\n                        sdsfreesplitres(argv,argc);\n                        linenoiseFree(line);\n                        return; /* Return to evalMode to restart the session. */\n                    } else {\n                        printf(\"Use 'restart' only in Lua debugging mode.\");\n                    }\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(CC_FORCE);\n                } else if (argc == 1 && !strcasecmp(argv[0],\"clear\")) {\n                    linenoiseClearScreen();\n                } else {\n                    long long start_time = mstime(), elapsed;\n\n                    issueCommandRepeat(argc-skipargs, argv+skipargs, repeat);\n\n                    /* If our debugging session ended, show the EVAL final\n                     * reply. */\n                    if (config.eval_ldb_end) {\n                        config.eval_ldb_end = 0;\n                        cliReadReply(0);\n                        printf(\"\\n(Lua debugging session ended%s)\\n\\n\",\n                            config.eval_ldb_sync ? \"\" :\n                            \" -- dataset changes rolled back\");\n                    }\n\n                    elapsed = mstime()-start_time;\n                    if (elapsed >= 500 &&\n                        config.output == OUTPUT_STANDARD)\n                    {\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        linenoiseFree(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 = issueCommand(argc+1, argv);\n    } else {\n        retval = issueCommand(argc, argv);\n    }\n    return retval;\n}\n\n/*------------------------------------------------------------------------------\n * Eval mode\n *--------------------------------------------------------------------------- */\n\nstatic int evalMode(int argc, char **argv) {\n    sds script = NULL;\n    FILE *fp;\n    char buf[1024];\n    size_t nread;\n    char **argv2;\n    int j, got_comma, keys;\n    int retval = REDIS_OK;\n\n    while(1) {\n        if (config.eval_ldb) {\n            printf(\n            \"Lua debugging session started, please use:\\n\"\n            \"quit    -- End the session.\\n\"\n            \"restart -- Restart the script in debug mode again.\\n\"\n            \"help    -- Show Lua script debugging commands.\\n\\n\"\n            );\n        }\n\n        sdsfree(script);\n        script = sdsempty();\n        got_comma = 0;\n        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        /* If we are debugging a script, enable the Lua debugger. */\n        if (config.eval_ldb) {\n            redisReply *reply = redisCommand(context,\n                    config.eval_ldb_sync ?\n                    \"SCRIPT DEBUG sync\": \"SCRIPT DEBUG yes\");\n            if (reply) freeReplyObject(reply);\n        }\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        int eval_ldb = config.eval_ldb; /* Save it, may be reverteed. */\n        retval = issueCommand(argc+3-got_comma, argv2);\n        if (eval_ldb) {\n            if (!config.eval_ldb) {\n                /* If the debugging session ended immediately, there was an\n                 * error compiling the script. Show it and they don't enter\n                 * the REPL at all. */\n                printf(\"Eval debugging session can't start:\\n\");\n                cliReadReply(0);\n                break; /* Return to the caller. */\n            } else {\n                strncpy(config.prompt,\"lua debugger> \",sizeof(config.prompt));\n                repl();\n                /* Restart the session if repl() returned. */\n                cliConnect(CC_FORCE);\n                printf(\"\\n\");\n            }\n        } else {\n            break; /* Return to the caller. */\n        }\n    }\n    return retval;\n}\n\n/*------------------------------------------------------------------------------\n * Cluster Manager\n *--------------------------------------------------------------------------- */\n\n/* The Cluster Manager global structure */\nstatic struct clusterManager {\n    list *nodes;    /* List of nodes in the configuration. */\n    list *errors;\n    int unreachable_masters;    /* Masters we are not able to reach. */\n} cluster_manager;\n\n/* Used by clusterManagerFixSlotsCoverage */\ndict *clusterManagerUncoveredSlots = NULL;\n\ntypedef struct clusterManagerNode {\n    redisContext *context;\n    sds name;\n    char *ip;\n    int port;\n    uint64_t current_epoch;\n    time_t ping_sent;\n    time_t ping_recv;\n    int flags;\n    list *flags_str; /* Flags string representations */\n    sds replicate;  /* Master ID if node is a slave */\n    int dirty;      /* Node has changes that can be flushed */\n    uint8_t slots[CLUSTER_MANAGER_SLOTS];\n    int slots_count;\n    int replicas_count;\n    list *friends;\n    sds *migrating; /* An array of sds where even strings are slots and odd\n                     * strings are the destination node IDs. */\n    sds *importing; /* An array of sds where even strings are slots and odd\n                     * strings are the source node IDs. */\n    int migrating_count; /* Length of the migrating array (migrating slots*2) */\n    int importing_count; /* Length of the importing array (importing slots*2) */\n    float weight;   /* Weight used by rebalance */\n    int balance;    /* Used by rebalance */\n} clusterManagerNode;\n\n/* Data structure used to represent a sequence of cluster nodes. */\ntypedef struct clusterManagerNodeArray {\n    clusterManagerNode **nodes; /* Actual nodes array */\n    clusterManagerNode **alloc; /* Pointer to the allocated memory */\n    int len;                    /* Actual length of the array */\n    int count;                  /* Non-NULL nodes count */\n} clusterManagerNodeArray;\n\n/* Used for the reshard table. */\ntypedef struct clusterManagerReshardTableItem {\n    clusterManagerNode *source;\n    int slot;\n} clusterManagerReshardTableItem;\n\n/* Info about a cluster internal link. */\n\ntypedef struct clusterManagerLink {\n    sds node_name;\n    sds node_addr;\n    int connected;\n    int handshaking;\n} clusterManagerLink;\n\nstatic dictType clusterManagerDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* key destructor */\n    dictSdsDestructor          /* val destructor */\n};\n\nstatic dictType clusterManagerLinkDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    dictSdsDestructor,         /* key destructor */\n    dictListDestructor         /* val destructor */\n};\n\ntypedef int clusterManagerCommandProc(int argc, char **argv);\ntypedef int (*clusterManagerOnReplyError)(redisReply *reply,\n    clusterManagerNode *n, int bulk_idx);\n\n/* Cluster Manager helper functions */\n\nstatic clusterManagerNode *clusterManagerNewNode(char *ip, int port);\nstatic clusterManagerNode *clusterManagerNodeByName(const char *name);\nstatic clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char *n);\nstatic void clusterManagerNodeResetSlots(clusterManagerNode *node);\nstatic int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err);\nstatic void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,\n                                                   char *err);\nstatic int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,\n                                      char **err);\nstatic int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts);\nstatic int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err);\nstatic int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,\n    int ip_count, clusterManagerNode ***offending, int *offending_len);\nstatic void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,\n    int ip_count);\nstatic sds clusterManagerNodeInfo(clusterManagerNode *node, int indent);\nstatic void clusterManagerShowNodes(void);\nstatic void clusterManagerShowClusterInfo(void);\nstatic int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err);\nstatic void clusterManagerWaitForClusterJoin(void);\nstatic int clusterManagerCheckCluster(int quiet);\nstatic void clusterManagerLog(int level, const char* fmt, ...);\nstatic int clusterManagerIsConfigConsistent(void);\nstatic dict *clusterManagerGetLinkStatus(void);\nstatic void clusterManagerOnError(sds err);\nstatic void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,\n                                        int len);\nstatic void clusterManagerNodeArrayReset(clusterManagerNodeArray *array);\nstatic void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,\n                                         clusterManagerNode **nodeptr);\nstatic void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,\n                                       clusterManagerNode *node);\n\n/* Cluster Manager commands. */\n\nstatic int clusterManagerCommandCreate(int argc, char **argv);\nstatic int clusterManagerCommandAddNode(int argc, char **argv);\nstatic int clusterManagerCommandDeleteNode(int argc, char **argv);\nstatic int clusterManagerCommandInfo(int argc, char **argv);\nstatic int clusterManagerCommandCheck(int argc, char **argv);\nstatic int clusterManagerCommandFix(int argc, char **argv);\nstatic int clusterManagerCommandReshard(int argc, char **argv);\nstatic int clusterManagerCommandRebalance(int argc, char **argv);\nstatic int clusterManagerCommandSetTimeout(int argc, char **argv);\nstatic int clusterManagerCommandImport(int argc, char **argv);\nstatic int clusterManagerCommandCall(int argc, char **argv);\nstatic int clusterManagerCommandHelp(int argc, char **argv);\nstatic int clusterManagerCommandBackup(int argc, char **argv);\n\ntypedef struct clusterManagerCommandDef {\n    char *name;\n    clusterManagerCommandProc *proc;\n    int arity;\n    char *args;\n    char *options;\n} clusterManagerCommandDef;\n\nclusterManagerCommandDef clusterManagerCommands[] = {\n    {\"create\", clusterManagerCommandCreate, -2, \"host1:port1 ... hostN:portN\",\n     \"replicas <arg>\"},\n    {\"check\", clusterManagerCommandCheck, -1, \"host:port\",\n     \"search-multiple-owners\"},\n    {\"info\", clusterManagerCommandInfo, -1, \"host:port\", NULL},\n    {\"fix\", clusterManagerCommandFix, -1, \"host:port\",\n     \"search-multiple-owners,fix-with-unreachable-masters\"},\n    {\"reshard\", clusterManagerCommandReshard, -1, \"host:port\",\n     \"from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,\"\n     \"replace\"},\n    {\"rebalance\", clusterManagerCommandRebalance, -1, \"host:port\",\n     \"weight <node1=w1...nodeN=wN>,use-empty-masters,\"\n     \"timeout <arg>,simulate,pipeline <arg>,threshold <arg>,replace\"},\n    {\"add-node\", clusterManagerCommandAddNode, 2,\n     \"new_host:new_port existing_host:existing_port\", \"slave,master-id <arg>\"},\n    {\"del-node\", clusterManagerCommandDeleteNode, 2, \"host:port node_id\",NULL},\n    {\"call\", clusterManagerCommandCall, -2,\n        \"host:port command arg arg .. arg\", NULL},\n    {\"set-timeout\", clusterManagerCommandSetTimeout, 2,\n     \"host:port milliseconds\", NULL},\n    {\"import\", clusterManagerCommandImport, 1, \"host:port\",\n     \"from <arg>,copy,replace\"},\n    {\"backup\", clusterManagerCommandBackup, 2,  \"host:port backup_directory\",\n     NULL},\n    {\"help\", clusterManagerCommandHelp, 0, NULL, NULL}\n};\n\nstatic void getRDB(clusterManagerNode *node);\n\nstatic void createClusterManagerCommand(char *cmdname, int argc, char **argv) {\n    clusterManagerCommand *cmd = &config.cluster_manager_command;\n    cmd->name = cmdname;\n    cmd->argc = argc;\n    cmd->argv = argc ? argv : NULL;\n    if (isColorTerm()) cmd->flags |= CLUSTER_MANAGER_CMD_FLAG_COLOR;\n}\n\n\nstatic clusterManagerCommandProc *validateClusterManagerCommand(void) {\n    int i, commands_count = sizeof(clusterManagerCommands) /\n                            sizeof(clusterManagerCommandDef);\n    clusterManagerCommandProc *proc = NULL;\n    char *cmdname = config.cluster_manager_command.name;\n    int argc = config.cluster_manager_command.argc;\n    for (i = 0; i < commands_count; i++) {\n        clusterManagerCommandDef cmddef = clusterManagerCommands[i];\n        if (!strcmp(cmddef.name, cmdname)) {\n            if ((cmddef.arity > 0 && argc != cmddef.arity) ||\n                (cmddef.arity < 0 && argc < (cmddef.arity * -1))) {\n                fprintf(stderr, \"[ERR] Wrong number of arguments for \"\n                                \"specified --cluster sub command\\n\");\n                return NULL;\n            }\n            proc = cmddef.proc;\n        }\n    }\n    if (!proc) fprintf(stderr, \"Unknown --cluster subcommand\\n\");\n    return proc;\n}\n\nstatic int parseClusterNodeAddress(char *addr, char **ip_ptr, int *port_ptr,\n                                   int *bus_port_ptr)\n{\n    char *c = strrchr(addr, '@');\n    if (c != NULL) {\n        *c = '\\0';\n        if (bus_port_ptr != NULL)\n            *bus_port_ptr = atoi(c + 1);\n    }\n    c = strrchr(addr, ':');\n    if (c != NULL) {\n        *c = '\\0';\n        *ip_ptr = addr;\n        *port_ptr = atoi(++c);\n    } else return 0;\n    return 1;\n}\n\n/* Get host ip and port from command arguments. If only one argument has\n * been provided it must be in the form of 'ip:port', elsewhere\n * the first argument must be the ip and the second one the port.\n * If host and port can be detected, it returns 1 and it stores host and\n * port into variables referenced by'ip_ptr' and 'port_ptr' pointers,\n * elsewhere it returns 0. */\nstatic int getClusterHostFromCmdArgs(int argc, char **argv,\n                                     char **ip_ptr, int *port_ptr) {\n    int port = 0;\n    char *ip = NULL;\n    if (argc == 1) {\n        char *addr = argv[0];\n        if (!parseClusterNodeAddress(addr, &ip, &port, NULL)) return 0;\n    } else {\n        ip = argv[0];\n        port = atoi(argv[1]);\n    }\n    if (!ip || !port) return 0;\n    else {\n        *ip_ptr = ip;\n        *port_ptr = port;\n    }\n    return 1;\n}\n\nstatic void freeClusterManagerNodeFlags(list *flags) {\n    listIter li;\n    listNode *ln;\n    listRewind(flags, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        sds flag = ln->value;\n        sdsfree(flag);\n    }\n    listRelease(flags);\n}\n\nstatic void freeClusterManagerNode(clusterManagerNode *node) {\n    if (node->context != NULL) redisFree(node->context);\n    if (node->friends != NULL) {\n        listIter li;\n        listNode *ln;\n        listRewind(node->friends,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *fn = ln->value;\n            freeClusterManagerNode(fn);\n        }\n        listRelease(node->friends);\n        node->friends = NULL;\n    }\n    if (node->name != NULL) sdsfree(node->name);\n    if (node->replicate != NULL) sdsfree(node->replicate);\n    if ((node->flags & CLUSTER_MANAGER_FLAG_FRIEND) && node->ip)\n        sdsfree(node->ip);\n    int i;\n    if (node->migrating != NULL) {\n        for (i = 0; i < node->migrating_count; i++) sdsfree(node->migrating[i]);\n        zfree(node->migrating);\n    }\n    if (node->importing != NULL) {\n        for (i = 0; i < node->importing_count; i++) sdsfree(node->importing[i]);\n        zfree(node->importing);\n    }\n    if (node->flags_str != NULL) {\n        freeClusterManagerNodeFlags(node->flags_str);\n        node->flags_str = NULL;\n    }\n    zfree(node);\n}\n\nstatic void freeClusterManager(void) {\n    listIter li;\n    listNode *ln;\n    if (cluster_manager.nodes != NULL) {\n        listRewind(cluster_manager.nodes,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            freeClusterManagerNode(n);\n        }\n        listRelease(cluster_manager.nodes);\n        cluster_manager.nodes = NULL;\n    }\n    if (cluster_manager.errors != NULL) {\n        listRewind(cluster_manager.errors,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            sds err = ln->value;\n            sdsfree(err);\n        }\n        listRelease(cluster_manager.errors);\n        cluster_manager.errors = NULL;\n    }\n    if (clusterManagerUncoveredSlots != NULL)\n        dictRelease(clusterManagerUncoveredSlots);\n}\n\nstatic clusterManagerNode *clusterManagerNewNode(char *ip, int port) {\n    clusterManagerNode *node = zmalloc(sizeof(*node));\n    node->context = NULL;\n    node->name = NULL;\n    node->ip = ip;\n    node->port = port;\n    node->current_epoch = 0;\n    node->ping_sent = 0;\n    node->ping_recv = 0;\n    node->flags = 0;\n    node->flags_str = NULL;\n    node->replicate = NULL;\n    node->dirty = 0;\n    node->friends = NULL;\n    node->migrating = NULL;\n    node->importing = NULL;\n    node->migrating_count = 0;\n    node->importing_count = 0;\n    node->replicas_count = 0;\n    node->weight = 1.0f;\n    node->balance = 0;\n    clusterManagerNodeResetSlots(node);\n    return node;\n}\n\nstatic sds clusterManagerGetNodeRDBFilename(clusterManagerNode *node) {\n    assert(config.cluster_manager_command.backup_dir);\n    sds filename = sdsnew(config.cluster_manager_command.backup_dir);\n    if (filename[sdslen(filename) - 1] != '/')\n        filename = sdscat(filename, \"/\");\n    filename = sdscatprintf(filename, \"redis-node-%s-%d-%s.rdb\", node->ip,\n                            node->port, node->name);\n    return filename;\n}\n\n/* Check whether reply is NULL or its type is REDIS_REPLY_ERROR. In the\n * latest case, if the 'err' arg is not NULL, it gets allocated with a copy\n * of reply error (it's up to the caller function to free it), elsewhere\n * the error is directly printed. */\nstatic int clusterManagerCheckRedisReply(clusterManagerNode *n,\n                                         redisReply *r, char **err)\n{\n    int is_err = 0;\n    if (!r || (is_err = (r->type == REDIS_REPLY_ERROR))) {\n        if (is_err) {\n            if (err != NULL) {\n                *err = zmalloc((r->len + 1) * sizeof(char));\n                strcpy(*err, r->str);\n            } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, r->str);\n        }\n        return 0;\n    }\n    return 1;\n}\n\n/* Call MULTI command on a cluster node. */\nstatic int clusterManagerStartTransaction(clusterManagerNode *node) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"MULTI\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Call EXEC command on a cluster node. */\nstatic int clusterManagerExecTransaction(clusterManagerNode *node,\n                                         clusterManagerOnReplyError onerror)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"EXEC\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (success) {\n        if (reply->type != REDIS_REPLY_ARRAY) {\n            success = 0;\n            goto cleanup;\n        }\n        size_t i;\n        for (i = 0; i < reply->elements; i++) {\n            redisReply *r = reply->element[i];\n            char *err = NULL;\n            success = clusterManagerCheckRedisReply(node, r, &err);\n            if (!success && onerror) success = onerror(r, node, i);\n            if (err) {\n                if (!success)\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                zfree(err);\n            }\n            if (!success) break;\n        }\n    }\ncleanup:\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerNodeConnect(clusterManagerNode *node) {\n    if (node->context) redisFree(node->context);\n    node->context = redisConnect(node->ip, node->port);\n    if (!node->context->err && config.tls) {\n        const char *err = NULL;\n        if (cliSecureConnection(node->context, &err) == REDIS_ERR && err) {\n            fprintf(stderr,\"TLS Error: %s\\n\", err);\n            redisFree(node->context);\n            node->context = NULL;\n            return 0;\n        }\n    }\n    if (node->context->err) {\n        fprintf(stderr,\"Could not connect to Redis at \");\n        fprintf(stderr,\"%s:%d: %s\\n\", node->ip, node->port,\n                node->context->errstr);\n        redisFree(node->context);\n        node->context = NULL;\n        return 0;\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, node->context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);\n    if (config.auth) {\n        redisReply *reply;\n        if (config.user == NULL)\n            reply = redisCommand(node->context,\"AUTH %s\", config.auth);\n        else\n            reply = redisCommand(node->context,\"AUTH %s %s\",\n                                 config.user,config.auth);\n        int ok = clusterManagerCheckRedisReply(node, reply, NULL);\n        if (reply != NULL) freeReplyObject(reply);\n        if (!ok) return 0;\n    }\n    return 1;\n}\n\nstatic void clusterManagerRemoveNodeFromList(list *nodelist,\n                                             clusterManagerNode *node) {\n    listIter li;\n    listNode *ln;\n    listRewind(nodelist, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        if (node == ln->value) {\n            listDelNode(nodelist, ln);\n            break;\n        }\n    }\n}\n\n/* Return the node with the specified name (ID) or NULL. */\nstatic clusterManagerNode *clusterManagerNodeByName(const char *name) {\n    if (cluster_manager.nodes == NULL) return NULL;\n    clusterManagerNode *found = NULL;\n    sds lcname = sdsempty();\n    lcname = sdscpy(lcname, name);\n    sdstolower(lcname);\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->name && !sdscmp(n->name, lcname)) {\n            found = n;\n            break;\n        }\n    }\n    sdsfree(lcname);\n    return found;\n}\n\n/* Like clusterManagerNodeByName but the specified name can be just the first\n * part of the node ID as long as the prefix in unique across the\n * cluster.\n */\nstatic clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char*name)\n{\n    if (cluster_manager.nodes == NULL) return NULL;\n    clusterManagerNode *found = NULL;\n    sds lcname = sdsempty();\n    lcname = sdscpy(lcname, name);\n    sdstolower(lcname);\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->name &&\n            strstr(n->name, lcname) == n->name) {\n            found = n;\n            break;\n        }\n    }\n    sdsfree(lcname);\n    return found;\n}\n\nstatic void clusterManagerNodeResetSlots(clusterManagerNode *node) {\n    memset(node->slots, 0, sizeof(node->slots));\n    node->slots_count = 0;\n}\n\n/* Call \"INFO\" redis command on the specified node and return the reply. */\nstatic redisReply *clusterManagerGetNodeRedisInfo(clusterManagerNode *node,\n                                                  char **err)\n{\n    redisReply *info = CLUSTER_MANAGER_COMMAND(node, \"INFO\");\n    if (err != NULL) *err = NULL;\n    if (info == NULL) return NULL;\n    if (info->type == REDIS_REPLY_ERROR) {\n        if (err != NULL) {\n            *err = zmalloc((info->len + 1) * sizeof(char));\n            strcpy(*err, info->str);\n        }\n        freeReplyObject(info);\n        return  NULL;\n    }\n    return info;\n}\n\nstatic int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err) {\n    redisReply *info = clusterManagerGetNodeRedisInfo(node, err);\n    if (info == NULL) return 0;\n    int is_cluster = (int) getLongInfoField(info->str, \"cluster_enabled\");\n    freeReplyObject(info);\n    return is_cluster;\n}\n\n/* Checks whether the node is empty. Node is considered not-empty if it has\n * some key or if it already knows other nodes */\nstatic int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err) {\n    redisReply *info = clusterManagerGetNodeRedisInfo(node, err);\n    int is_empty = 1;\n    if (info == NULL) return 0;\n    if (strstr(info->str, \"db0:\") != NULL) {\n        is_empty = 0;\n        goto result;\n    }\n    freeReplyObject(info);\n    info = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER INFO\");\n    if (err != NULL) *err = NULL;\n    if (!clusterManagerCheckRedisReply(node, info, err)) {\n        is_empty = 0;\n        goto result;\n    }\n    long known_nodes = getLongInfoField(info->str, \"cluster_known_nodes\");\n    is_empty = (known_nodes == 1);\nresult:\n    freeReplyObject(info);\n    return is_empty;\n}\n\n/* Return the anti-affinity score, which is a measure of the amount of\n * violations of anti-affinity in the current cluster layout, that is, how\n * badly the masters and slaves are distributed in the different IP\n * addresses so that slaves of the same master are not in the master\n * host and are also in different hosts.\n *\n * The score is calculated as follows:\n *\n * SAME_AS_MASTER = 10000 * each slave in the same IP of its master.\n * SAME_AS_SLAVE  = 1 * each slave having the same IP as another slave\n                      of the same master.\n * FINAL_SCORE = SAME_AS_MASTER + SAME_AS_SLAVE\n *\n * So a greater score means a worse anti-affinity level, while zero\n * means perfect anti-affinity.\n *\n * The anti affinity optimizator will try to get a score as low as\n * possible. Since we do not want to sacrifice the fact that slaves should\n * not be in the same host as the master, we assign 10000 times the score\n * to this violation, so that we'll optimize for the second factor only\n * if it does not impact the first one.\n *\n * The ipnodes argument is an array of clusterManagerNodeArray, one for\n * each IP, while ip_count is the total number of IPs in the configuration.\n *\n * The function returns the above score, and the list of\n * offending slaves can be stored into the 'offending' argument,\n * so that the optimizer can try changing the configuration of the\n * slaves violating the anti-affinity goals. */\nstatic int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,\n    int ip_count, clusterManagerNode ***offending, int *offending_len)\n{\n    int score = 0, i, j;\n    int node_len = cluster_manager.nodes->len;\n    clusterManagerNode **offending_p = NULL;\n    if (offending != NULL) {\n        *offending = zcalloc(node_len * sizeof(clusterManagerNode*));\n        offending_p = *offending;\n    }\n    /* For each set of nodes in the same host, split by\n     * related nodes (masters and slaves which are involved in\n     * replication of each other) */\n    for (i = 0; i < ip_count; i++) {\n        clusterManagerNodeArray *node_array = &(ipnodes[i]);\n        dict *related = dictCreate(&clusterManagerDictType, NULL);\n        char *ip = NULL;\n        for (j = 0; j < node_array->len; j++) {\n            clusterManagerNode *node = node_array->nodes[j];\n            if (node == NULL) continue;\n            if (!ip) ip = node->ip;\n            sds types;\n            /* We always use the Master ID as key. */\n            sds key = (!node->replicate ? node->name : node->replicate);\n            assert(key != NULL);\n            dictEntry *entry = dictFind(related, key);\n            if (entry) types = sdsdup((sds) dictGetVal(entry));\n            else types = sdsempty();\n            /* Master type 'm' is always set as the first character of the\n             * types string. */\n            if (!node->replicate) types = sdscatprintf(types, \"m%s\", types);\n            else types = sdscat(types, \"s\");\n            dictReplace(related, key, types);\n        }\n        /* Now it's trivial to check, for each related group having the\n         * same host, what is their local score. */\n        dictIterator *iter = dictGetIterator(related);\n        dictEntry *entry;\n        while ((entry = dictNext(iter)) != NULL) {\n            sds types = (sds) dictGetVal(entry);\n            sds name = (sds) dictGetKey(entry);\n            int typeslen = sdslen(types);\n            if (typeslen < 2) continue;\n            if (types[0] == 'm') score += (10000 * (typeslen - 1));\n            else score += (1 * typeslen);\n            if (offending == NULL) continue;\n            /* Populate the list of offending nodes. */\n            listIter li;\n            listNode *ln;\n            listRewind(cluster_manager.nodes, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (n->replicate == NULL) continue;\n                if (!strcmp(n->replicate, name) && !strcmp(n->ip, ip)) {\n                    *(offending_p++) = n;\n                    if (offending_len != NULL) (*offending_len)++;\n                    break;\n                }\n            }\n        }\n        //if (offending_len != NULL) *offending_len = offending_p - *offending;\n        dictReleaseIterator(iter);\n        dictRelease(related);\n    }\n    return score;\n}\n\nstatic void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,\n    int ip_count)\n{\n    clusterManagerNode **offenders = NULL;\n    int score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count,\n                                                   NULL, NULL);\n    if (score == 0) goto cleanup;\n    clusterManagerLogInfo(\">>> Trying to optimize slaves allocation \"\n                          \"for anti-affinity\\n\");\n    int node_len = cluster_manager.nodes->len;\n    int maxiter = 500 * node_len; // Effort is proportional to cluster size...\n    srand(time(NULL));\n    while (maxiter > 0) {\n        int offending_len = 0;\n        if (offenders != NULL) {\n            zfree(offenders);\n            offenders = NULL;\n        }\n        score = clusterManagerGetAntiAffinityScore(ipnodes,\n                                                   ip_count,\n                                                   &offenders,\n                                                   &offending_len);\n        if (score == 0) break; // Optimal anti affinity reached\n        /* We'll try to randomly swap a slave's assigned master causing\n         * an affinity problem with another random slave, to see if we\n         * can improve the affinity. */\n        int rand_idx = rand() % offending_len;\n        clusterManagerNode *first = offenders[rand_idx],\n                           *second = NULL;\n        clusterManagerNode **other_replicas = zcalloc((node_len - 1) *\n                                                      sizeof(*other_replicas));\n        int other_replicas_count = 0;\n        listIter li;\n        listNode *ln;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n != first && n->replicate != NULL)\n                other_replicas[other_replicas_count++] = n;\n        }\n        if (other_replicas_count == 0) {\n            zfree(other_replicas);\n            break;\n        }\n        rand_idx = rand() % other_replicas_count;\n        second = other_replicas[rand_idx];\n        char *first_master = first->replicate,\n             *second_master = second->replicate;\n        first->replicate = second_master, first->dirty = 1;\n        second->replicate = first_master, second->dirty = 1;\n        int new_score = clusterManagerGetAntiAffinityScore(ipnodes,\n                                                           ip_count,\n                                                           NULL, NULL);\n        /* If the change actually makes thing worse, revert. Otherwise\n         * leave as it is because the best solution may need a few\n         * combined swaps. */\n        if (new_score > score) {\n            first->replicate = first_master;\n            second->replicate = second_master;\n        }\n        zfree(other_replicas);\n        maxiter--;\n    }\n    score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL, NULL);\n    char *msg;\n    int perfect = (score == 0);\n    int log_level = (perfect ? CLUSTER_MANAGER_LOG_LVL_SUCCESS :\n                               CLUSTER_MANAGER_LOG_LVL_WARN);\n    if (perfect) msg = \"[OK] Perfect anti-affinity obtained!\";\n    else if (score >= 10000)\n        msg = (\"[WARNING] Some slaves are in the same host as their master\");\n    else\n        msg=(\"[WARNING] Some slaves of the same master are in the same host\");\n    clusterManagerLog(log_level, \"%s\\n\", msg);\ncleanup:\n    zfree(offenders);\n}\n\n/* Return a representable string of the node's flags */\nstatic sds clusterManagerNodeFlagString(clusterManagerNode *node) {\n    sds flags = sdsempty();\n    if (!node->flags_str) return flags;\n    int empty = 1;\n    listIter li;\n    listNode *ln;\n    listRewind(node->flags_str, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        sds flag = ln->value;\n        if (strcmp(flag, \"myself\") == 0) continue;\n        if (!empty) flags = sdscat(flags, \",\");\n        flags = sdscatfmt(flags, \"%S\", flag);\n        empty = 0;\n    }\n    return flags;\n}\n\n/* Return a representable string of the node's slots */\nstatic sds clusterManagerNodeSlotsString(clusterManagerNode *node) {\n    sds slots = sdsempty();\n    int first_range_idx = -1, last_slot_idx = -1, i;\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        int has_slot = node->slots[i];\n        if (has_slot) {\n            if (first_range_idx == -1) {\n                if (sdslen(slots)) slots = sdscat(slots, \",\");\n                first_range_idx = i;\n                slots = sdscatfmt(slots, \"[%u\", i);\n            }\n            last_slot_idx = i;\n        } else {\n            if (last_slot_idx >= 0) {\n                if (first_range_idx == last_slot_idx)\n                    slots = sdscat(slots, \"]\");\n                else slots = sdscatfmt(slots, \"-%u]\", last_slot_idx);\n            }\n            last_slot_idx = -1;\n            first_range_idx = -1;\n        }\n    }\n    if (last_slot_idx >= 0) {\n        if (first_range_idx == last_slot_idx) slots = sdscat(slots, \"]\");\n        else slots = sdscatfmt(slots, \"-%u]\", last_slot_idx);\n    }\n    return slots;\n}\n\nstatic sds clusterManagerNodeGetJSON(clusterManagerNode *node,\n                                     unsigned long error_count)\n{\n    sds json = sdsempty();\n    sds replicate = sdsempty();\n    if (node->replicate)\n        replicate = sdscatprintf(replicate, \"\\\"%s\\\"\", node->replicate);\n    else\n        replicate = sdscat(replicate, \"null\");\n    sds slots = clusterManagerNodeSlotsString(node);\n    sds flags = clusterManagerNodeFlagString(node);\n    char *p = slots;\n    while ((p = strchr(p, '-')) != NULL)\n        *(p++) = ',';\n    json = sdscatprintf(json,\n        \"  {\\n\"\n        \"    \\\"name\\\": \\\"%s\\\",\\n\"\n        \"    \\\"host\\\": \\\"%s\\\",\\n\"\n        \"    \\\"port\\\": %d,\\n\"\n        \"    \\\"replicate\\\": %s,\\n\"\n        \"    \\\"slots\\\": [%s],\\n\"\n        \"    \\\"slots_count\\\": %d,\\n\"\n        \"    \\\"flags\\\": \\\"%s\\\",\\n\"\n        \"    \\\"current_epoch\\\": %llu\",\n        node->name,\n        node->ip,\n        node->port,\n        replicate,\n        slots,\n        node->slots_count,\n        flags,\n        (unsigned long long)node->current_epoch\n    );\n    if (error_count > 0) {\n        json = sdscatprintf(json, \",\\n    \\\"cluster_errors\\\": %lu\",\n                            error_count);\n    }\n    if (node->migrating_count > 0 && node->migrating != NULL) {\n        int i = 0;\n        sds migrating = sdsempty();\n        for (; i < node->migrating_count; i += 2) {\n            sds slot = node->migrating[i];\n            sds dest = node->migrating[i + 1];\n            if (slot && dest) {\n                if (sdslen(migrating) > 0) migrating = sdscat(migrating, \",\");\n                migrating = sdscatfmt(migrating, \"\\\"%S\\\": \\\"%S\\\"\", slot, dest);\n            }\n        }\n        if (sdslen(migrating) > 0)\n            json = sdscatfmt(json, \",\\n    \\\"migrating\\\": {%S}\", migrating);\n        sdsfree(migrating);\n    }\n    if (node->importing_count > 0 && node->importing != NULL) {\n        int i = 0;\n        sds importing = sdsempty();\n        for (; i < node->importing_count; i += 2) {\n            sds slot = node->importing[i];\n            sds from = node->importing[i + 1];\n            if (slot && from) {\n                if (sdslen(importing) > 0) importing = sdscat(importing, \",\");\n                importing = sdscatfmt(importing, \"\\\"%S\\\": \\\"%S\\\"\", slot, from);\n            }\n        }\n        if (sdslen(importing) > 0)\n            json = sdscatfmt(json, \",\\n    \\\"importing\\\": {%S}\", importing);\n        sdsfree(importing);\n    }\n    json = sdscat(json, \"\\n  }\");\n    sdsfree(replicate);\n    sdsfree(slots);\n    sdsfree(flags);\n    return json;\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). */\nstatic unsigned int clusterManagerKeyHashSlot(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 between {} ? 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/* Return a string representation of the cluster node. */\nstatic sds clusterManagerNodeInfo(clusterManagerNode *node, int indent) {\n    sds info = sdsempty();\n    sds spaces = sdsempty();\n    int i;\n    for (i = 0; i < indent; i++) spaces = sdscat(spaces, \" \");\n    if (indent) info = sdscat(info, spaces);\n    int is_master = !(node->flags & CLUSTER_MANAGER_FLAG_SLAVE);\n    char *role = (is_master ? \"M\" : \"S\");\n    sds slots = NULL;\n    if (node->dirty && node->replicate != NULL)\n        info = sdscatfmt(info, \"S: %S %s:%u\", node->name, node->ip, node->port);\n    else {\n        slots = clusterManagerNodeSlotsString(node);\n        sds flags = clusterManagerNodeFlagString(node);\n        info = sdscatfmt(info, \"%s: %S %s:%u\\n\"\n                               \"%s   slots:%S (%u slots) \"\n                               \"%S\",\n                               role, node->name, node->ip, node->port, spaces,\n                               slots, node->slots_count, flags);\n        sdsfree(slots);\n        sdsfree(flags);\n    }\n    if (node->replicate != NULL)\n        info = sdscatfmt(info, \"\\n%s   replicates %S\", spaces, node->replicate);\n    else if (node->replicas_count)\n        info = sdscatfmt(info, \"\\n%s   %U additional replica(s)\",\n                         spaces, node->replicas_count);\n    sdsfree(spaces);\n    return info;\n}\n\nstatic void clusterManagerShowNodes(void) {\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        sds info = clusterManagerNodeInfo(node, 0);\n        printf(\"%s\\n\", (char *) info);\n        sdsfree(info);\n    }\n}\n\nstatic void clusterManagerShowClusterInfo(void) {\n    int masters = 0;\n    int keys = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        if (!(node->flags & CLUSTER_MANAGER_FLAG_SLAVE)) {\n            if (!node->name) continue;\n            int replicas = 0;\n            int dbsize = -1;\n            char name[9];\n            memcpy(name, node->name, 8);\n            name[8] = '\\0';\n            listIter ri;\n            listNode *rn;\n            listRewind(cluster_manager.nodes, &ri);\n            while ((rn = listNext(&ri)) != NULL) {\n                clusterManagerNode *n = rn->value;\n                if (n == node || !(n->flags & CLUSTER_MANAGER_FLAG_SLAVE))\n                    continue;\n                if (n->replicate && !strcmp(n->replicate, node->name))\n                    replicas++;\n            }\n            redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"DBSIZE\");\n            if (reply != NULL && reply->type == REDIS_REPLY_INTEGER)\n                dbsize = reply->integer;\n            if (dbsize < 0) {\n                char *err = \"\";\n                if (reply != NULL && reply->type == REDIS_REPLY_ERROR)\n                    err = reply->str;\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                if (reply != NULL) freeReplyObject(reply);\n                return;\n            };\n            if (reply != NULL) freeReplyObject(reply);\n            printf(\"%s:%d (%s...) -> %d keys | %d slots | %d slaves.\\n\",\n                   node->ip, node->port, name, dbsize,\n                   node->slots_count, replicas);\n            masters++;\n            keys += dbsize;\n        }\n    }\n    clusterManagerLogOk(\"[OK] %d keys in %d masters.\\n\", keys, masters);\n    float keys_per_slot = keys / (float) CLUSTER_MANAGER_SLOTS;\n    printf(\"%.2f keys per slot on average.\\n\", keys_per_slot);\n}\n\n/* Flush dirty slots configuration of the node by calling CLUSTER ADDSLOTS */\nstatic int clusterManagerAddSlots(clusterManagerNode *node, char**err)\n{\n    redisReply *reply = NULL;\n    void *_reply = NULL;\n    int success = 1;\n    /* First two args are used for the command itself. */\n    int argc = node->slots_count + 2;\n    sds *argv = zmalloc(argc * sizeof(*argv));\n    size_t *argvlen = zmalloc(argc * sizeof(*argvlen));\n    argv[0] = \"CLUSTER\";\n    argv[1] = \"ADDSLOTS\";\n    argvlen[0] = 7;\n    argvlen[1] = 8;\n    *err = NULL;\n    int i, argv_idx = 2;\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        if (argv_idx >= argc) break;\n        if (node->slots[i]) {\n            argv[argv_idx] = sdsfromlonglong((long long) i);\n            argvlen[argv_idx] = sdslen(argv[argv_idx]);\n            argv_idx++;\n        }\n    }\n    if (!argv_idx) {\n        success = 0;\n        goto cleanup;\n    }\n    redisAppendCommandArgv(node->context,argc,(const char**)argv,argvlen);\n    if (redisGetReply(node->context, &_reply) != REDIS_OK) {\n        success = 0;\n        goto cleanup;\n    }\n    reply = (redisReply*) _reply;\n    success = clusterManagerCheckRedisReply(node, reply, err);\ncleanup:\n    zfree(argvlen);\n    if (argv != NULL) {\n        for (i = 2; i < argc; i++) sdsfree(argv[i]);\n        zfree(argv);\n    }\n    if (reply != NULL) freeReplyObject(reply);\n    return success;\n}\n\n/* Get the node the slot is assigned to from the point of view of node *n.\n * If the slot is unassigned or if the reply is an error, return NULL.\n * Use the **err argument in order to check wether the slot is unassigned\n * or the reply resulted in an error. */\nstatic clusterManagerNode *clusterManagerGetSlotOwner(clusterManagerNode *n,\n                                                      int slot, char **err)\n{\n    assert(slot >= 0 && slot < CLUSTER_MANAGER_SLOTS);\n    clusterManagerNode *owner = NULL;\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER SLOTS\");\n    if (clusterManagerCheckRedisReply(n, reply, err)) {\n        assert(reply->type == REDIS_REPLY_ARRAY);\n        size_t i;\n        for (i = 0; i < reply->elements; i++) {\n            redisReply *r = reply->element[i];\n            assert(r->type == REDIS_REPLY_ARRAY && r->elements >= 3);\n            int from, to;\n            from = r->element[0]->integer;\n            to = r->element[1]->integer;\n            if (slot < from || slot > to) continue;\n            redisReply *nr =  r->element[2];\n            assert(nr->type == REDIS_REPLY_ARRAY && nr->elements >= 2);\n            char *name = NULL;\n            if (nr->elements >= 3)\n                name =  nr->element[2]->str;\n            if (name != NULL)\n                owner = clusterManagerNodeByName(name);\n            else {\n                char *ip = nr->element[0]->str;\n                assert(ip != NULL);\n                int port = (int) nr->element[1]->integer;\n                listIter li;\n                listNode *ln;\n                listRewind(cluster_manager.nodes, &li);\n                while ((ln = listNext(&li)) != NULL) {\n                    clusterManagerNode *nd = ln->value;\n                    if (strcmp(nd->ip, ip) == 0 && port == nd->port) {\n                        owner = nd;\n                        break;\n                    }\n                }\n            }\n            if (owner) break;\n        }\n    }\n    if (reply) freeReplyObject(reply);\n    return owner;\n}\n\n/* Set slot status to \"importing\" or \"migrating\" */\nstatic int clusterManagerSetSlot(clusterManagerNode *node1,\n                                 clusterManagerNode *node2,\n                                 int slot, const char *status, char **err) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node1, \"CLUSTER \"\n                                                \"SETSLOT %d %s %s\",\n                                                slot, status,\n                                                (char *) node2->name);\n    if (err != NULL) *err = NULL;\n    if (!reply) return 0;\n    int success = 1;\n    if (reply->type == REDIS_REPLY_ERROR) {\n        success = 0;\n        if (err != NULL) {\n            *err = zmalloc((reply->len + 1) * sizeof(char));\n            strcpy(*err, reply->str);\n        } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(node1, reply->str);\n        goto cleanup;\n    }\ncleanup:\n    freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerClearSlotStatus(clusterManagerNode *node, int slot) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER SETSLOT %d %s\", slot, \"STABLE\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerDelSlot(clusterManagerNode *node, int slot,\n                                 int ignore_unassigned_err)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER DELSLOTS %d\", slot);\n    char *err = NULL;\n    int success = clusterManagerCheckRedisReply(node, reply, &err);\n    if (!success && reply && reply->type == REDIS_REPLY_ERROR &&\n        ignore_unassigned_err)\n    {\n        char *get_owner_err = NULL;\n        clusterManagerNode *assigned_to =\n            clusterManagerGetSlotOwner(node, slot, &get_owner_err);\n        if (!assigned_to) {\n            if (get_owner_err == NULL) success = 1;\n            else {\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, get_owner_err);\n                zfree(get_owner_err);\n            }\n        }\n    }\n    if (!success && err != NULL) {\n        CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n        zfree(err);\n    }\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic int clusterManagerAddSlot(clusterManagerNode *node, int slot) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER ADDSLOTS %d\", slot);\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\nstatic signed int clusterManagerCountKeysInSlot(clusterManagerNode *node,\n                                                int slot)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node,\n        \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n    int count = -1;\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (success && reply->type == REDIS_REPLY_INTEGER) count = reply->integer;\n    if (reply) freeReplyObject(reply);\n    return count;\n}\n\nstatic int clusterManagerBumpEpoch(clusterManagerNode *node) {\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER BUMPEPOCH\");\n    int success = clusterManagerCheckRedisReply(node, reply, NULL);\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Callback used by clusterManagerSetSlotOwner transaction. It should ignore\n * errors except for ADDSLOTS errors.\n * Return 1 if the error should be ignored. */\nstatic int clusterManagerOnSetOwnerErr(redisReply *reply,\n    clusterManagerNode *n, int bulk_idx)\n{\n    UNUSED(reply);\n    UNUSED(n);\n    /* Only raise error when ADDSLOTS fail (bulk_idx == 1). */\n    return (bulk_idx != 1);\n}\n\nstatic int clusterManagerSetSlotOwner(clusterManagerNode *owner,\n                                      int slot,\n                                      int do_clear)\n{\n    int success = clusterManagerStartTransaction(owner);\n    if (!success) return 0;\n    /* Ensure the slot is not already assigned. */\n    clusterManagerDelSlot(owner, slot, 1);\n    /* Add the slot and bump epoch. */\n    clusterManagerAddSlot(owner, slot);\n    if (do_clear) clusterManagerClearSlotStatus(owner, slot);\n    clusterManagerBumpEpoch(owner);\n    success = clusterManagerExecTransaction(owner, clusterManagerOnSetOwnerErr);\n    return success;\n}\n\n/* Get the hash for the values of the specified keys in *keys_reply for the\n * specified nodes *n1 and *n2, by calling DEBUG DIGEST-VALUE redis command\n * on both nodes. Every key with same name on both nodes but having different\n * values will be added to the *diffs list. Return 0 in case of reply\n * error. */\nstatic int clusterManagerCompareKeysValues(clusterManagerNode *n1,\n                                          clusterManagerNode *n2,\n                                          redisReply *keys_reply,\n                                          list *diffs)\n{\n    size_t i, argc = keys_reply->elements + 2;\n    static const char *hash_zero = \"0000000000000000000000000000000000000000\";\n    char **argv = zcalloc(argc * sizeof(char *));\n    size_t  *argv_len = zcalloc(argc * sizeof(size_t));\n    argv[0] = \"DEBUG\";\n    argv_len[0] = 5;\n    argv[1] = \"DIGEST-VALUE\";\n    argv_len[1] = 12;\n    for (i = 0; i < keys_reply->elements; i++) {\n        redisReply *entry = keys_reply->element[i];\n        int idx = i + 2;\n        argv[idx] = entry->str;\n        argv_len[idx] = entry->len;\n    }\n    int success = 0;\n    void *_reply1 = NULL, *_reply2 = NULL;\n    redisReply *r1 = NULL, *r2 = NULL;\n    redisAppendCommandArgv(n1->context,argc, (const char**)argv,argv_len);\n    success = (redisGetReply(n1->context, &_reply1) == REDIS_OK);\n    if (!success) goto cleanup;\n    r1 = (redisReply *) _reply1;\n    redisAppendCommandArgv(n2->context,argc, (const char**)argv,argv_len);\n    success = (redisGetReply(n2->context, &_reply2) == REDIS_OK);\n    if (!success) goto cleanup;\n    r2 = (redisReply *) _reply2;\n    success = (r1->type != REDIS_REPLY_ERROR && r2->type != REDIS_REPLY_ERROR);\n    if (r1->type == REDIS_REPLY_ERROR) {\n        CLUSTER_MANAGER_PRINT_REPLY_ERROR(n1, r1->str);\n        success = 0;\n    }\n    if (r2->type == REDIS_REPLY_ERROR) {\n        CLUSTER_MANAGER_PRINT_REPLY_ERROR(n2, r2->str);\n        success = 0;\n    }\n    if (!success) goto cleanup;\n    assert(keys_reply->elements == r1->elements &&\n           keys_reply->elements == r2->elements);\n    for (i = 0; i < keys_reply->elements; i++) {\n        char *key = keys_reply->element[i]->str;\n        char *hash1 = r1->element[i]->str;\n        char *hash2 = r2->element[i]->str;\n        /* Ignore keys that don't exist in both nodes. */\n        if (strcmp(hash1, hash_zero) == 0 || strcmp(hash2, hash_zero) == 0)\n            continue;\n        if (strcmp(hash1, hash2) != 0) listAddNodeTail(diffs, key);\n    }\ncleanup:\n    if (r1) freeReplyObject(r1);\n    if (r2) freeReplyObject(r2);\n    zfree(argv);\n    zfree(argv_len);\n    return success;\n}\n\n/* Migrate keys taken from reply->elements. It returns the reply from the\n * MIGRATE command, or NULL if something goes wrong. If the argument 'dots'\n * is not NULL, a dot will be printed for every migrated key. */\nstatic redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,\n                                                    clusterManagerNode *target,\n                                                    redisReply *reply,\n                                                    int replace, int timeout,\n                                                    char *dots)\n{\n    redisReply *migrate_reply = NULL;\n    char **argv = NULL;\n    size_t *argv_len = NULL;\n    int c = (replace ? 8 : 7);\n    if (config.auth) c += 2;\n    size_t argc = c + reply->elements;\n    size_t i, offset = 6; // Keys Offset\n    argv = zcalloc(argc * sizeof(char *));\n    argv_len = zcalloc(argc * sizeof(size_t));\n    char portstr[255];\n    char timeoutstr[255];\n    snprintf(portstr, 10, \"%d\", target->port);\n    snprintf(timeoutstr, 10, \"%d\", timeout);\n    argv[0] = \"MIGRATE\";\n    argv_len[0] = 7;\n    argv[1] = target->ip;\n    argv_len[1] = strlen(target->ip);\n    argv[2] = portstr;\n    argv_len[2] = strlen(portstr);\n    argv[3] = \"\";\n    argv_len[3] = 0;\n    argv[4] = \"0\";\n    argv_len[4] = 1;\n    argv[5] = timeoutstr;\n    argv_len[5] = strlen(timeoutstr);\n    if (replace) {\n        argv[offset] = \"REPLACE\";\n        argv_len[offset] = 7;\n        offset++;\n    }\n    if (config.auth) {\n        argv[offset] = \"AUTH\";\n        argv_len[offset] = 4;\n        offset++;\n        argv[offset] = config.auth;\n        argv_len[offset] = strlen(config.auth);\n        offset++;\n    }\n    argv[offset] = \"KEYS\";\n    argv_len[offset] = 4;\n    offset++;\n    for (i = 0; i < reply->elements; i++) {\n        redisReply *entry = reply->element[i];\n        size_t idx = i + offset;\n        assert(entry->type == REDIS_REPLY_STRING);\n        argv[idx] = (char *) sdsnewlen(entry->str, entry->len);\n        argv_len[idx] = entry->len;\n        if (dots) dots[i] = '.';\n    }\n    if (dots) dots[reply->elements] = '\\0';\n    void *_reply = NULL;\n    redisAppendCommandArgv(source->context,argc,\n                           (const char**)argv,argv_len);\n    int success = (redisGetReply(source->context, &_reply) == REDIS_OK);\n    for (i = 0; i < reply->elements; i++) sdsfree(argv[i + offset]);\n    if (!success) goto cleanup;\n    migrate_reply = (redisReply *) _reply;\ncleanup:\n    zfree(argv);\n    zfree(argv_len);\n    return migrate_reply;\n}\n\n/* Migrate all keys in the given slot from source to target.*/\nstatic int clusterManagerMigrateKeysInSlot(clusterManagerNode *source,\n                                           clusterManagerNode *target,\n                                           int slot, int timeout,\n                                           int pipeline, int verbose,\n                                           char **err)\n{\n    int success = 1;\n    int do_fix = config.cluster_manager_command.flags &\n                 CLUSTER_MANAGER_CMD_FLAG_FIX;\n    int do_replace = config.cluster_manager_command.flags &\n                     CLUSTER_MANAGER_CMD_FLAG_REPLACE;\n    while (1) {\n        char *dots = NULL;\n        redisReply *reply = NULL, *migrate_reply = NULL;\n        reply = CLUSTER_MANAGER_COMMAND(source, \"CLUSTER \"\n                                        \"GETKEYSINSLOT %d %d\", slot,\n                                        pipeline);\n        success = (reply != NULL);\n        if (!success) return 0;\n        if (reply->type == REDIS_REPLY_ERROR) {\n            success = 0;\n            if (err != NULL) {\n                *err = zmalloc((reply->len + 1) * sizeof(char));\n                strcpy(*err, reply->str);\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(source, *err);\n            }\n            goto next;\n        }\n        assert(reply->type == REDIS_REPLY_ARRAY);\n        size_t count = reply->elements;\n        if (count == 0) {\n            freeReplyObject(reply);\n            break;\n        }\n        if (verbose) dots = zmalloc((count+1) * sizeof(char));\n        /* Calling MIGRATE command. */\n        migrate_reply = clusterManagerMigrateKeysInReply(source, target,\n                                                         reply, 0, timeout,\n                                                         dots);\n        if (migrate_reply == NULL) goto next;\n        if (migrate_reply->type == REDIS_REPLY_ERROR) {\n            int is_busy = strstr(migrate_reply->str, \"BUSYKEY\") != NULL;\n            int not_served = 0;\n            if (!is_busy) {\n                /* Check if the slot is unassigned (not served) in the\n                 * source node's configuration. */\n                char *get_owner_err = NULL;\n                clusterManagerNode *served_by =\n                    clusterManagerGetSlotOwner(source, slot, &get_owner_err);\n                if (!served_by) {\n                    if (get_owner_err == NULL) not_served = 1;\n                    else {\n                        CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,\n                                                          get_owner_err);\n                        zfree(get_owner_err);\n                    }\n                }\n            }\n            /* Try to handle errors. */\n            if (is_busy || not_served) {\n                /* If the key's slot is not served, try to assign slot\n                 * to the target node. */\n                if (do_fix && not_served) {\n                    clusterManagerLogWarn(\"*** Slot was not served, setting \"\n                                          \"owner to node %s:%d.\\n\",\n                                          target->ip, target->port);\n                    clusterManagerSetSlot(source, target, slot, \"node\", NULL);\n                }\n                /* If the key already exists in the target node (BUSYKEY),\n                 * check whether its value is the same in both nodes.\n                 * In case of equal values, retry migration with the\n                 * REPLACE option.\n                 * In case of different values:\n                 *  - If the migration is requested by the fix command, stop\n                 *    and warn the user.\n                 *  - In other cases (ie. reshard), proceed only if the user\n                 *    launched the command with the --cluster-replace option.*/\n                if (is_busy) {\n                    clusterManagerLogWarn(\"\\n*** Target key exists\\n\");\n                    if (!do_replace) {\n                        clusterManagerLogWarn(\"*** Checking key values on \"\n                                              \"both nodes...\\n\");\n                        list *diffs = listCreate();\n                        success = clusterManagerCompareKeysValues(source,\n                            target, reply, diffs);\n                        if (!success) {\n                            clusterManagerLogErr(\"*** Value check failed!\\n\");\n                            listRelease(diffs);\n                            goto next;\n                        }\n                        if (listLength(diffs) > 0) {\n                            success = 0;\n                            clusterManagerLogErr(\n                                \"*** Found %d key(s) in both source node and \"\n                                \"target node having different values.\\n\"\n                                \"    Source node: %s:%d\\n\"\n                                \"    Target node: %s:%d\\n\"\n                                \"    Keys(s):\\n\",\n                                listLength(diffs),\n                                source->ip, source->port,\n                                target->ip, target->port);\n                            listIter dli;\n                            listNode *dln;\n                            listRewind(diffs, &dli);\n                            while((dln = listNext(&dli)) != NULL) {\n                                char *k = dln->value;\n                                clusterManagerLogErr(\"    - %s\\n\", k);\n                            }\n                            clusterManagerLogErr(\"Please fix the above key(s) \"\n                                                 \"manually and try again \"\n                                                 \"or relaunch the command \\n\"\n                                                 \"with --cluster-replace \"\n                                                 \"option to force key \"\n                                                 \"overriding.\\n\");\n                            listRelease(diffs);\n                            goto next;\n                        }\n                        listRelease(diffs);\n                    }\n                    clusterManagerLogWarn(\"*** Replacing target keys...\\n\");\n                }\n                freeReplyObject(migrate_reply);\n                migrate_reply = clusterManagerMigrateKeysInReply(source,\n                                                                 target,\n                                                                 reply,\n                                                                 is_busy,\n                                                                 timeout,\n                                                                 NULL);\n                success = (migrate_reply != NULL &&\n                           migrate_reply->type != REDIS_REPLY_ERROR);\n            } else success = 0;\n            if (!success) {\n                if (migrate_reply != NULL) {\n                    if (err) {\n                        *err = zmalloc((migrate_reply->len + 1) * sizeof(char));\n                        strcpy(*err, migrate_reply->str);\n                    }\n                    printf(\"\\n\");\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,\n                                                      migrate_reply->str);\n                }\n                goto next;\n            }\n        }\n        if (verbose) {\n            printf(\"%s\", dots);\n            fflush(stdout);\n        }\nnext:\n        if (reply != NULL) freeReplyObject(reply);\n        if (migrate_reply != NULL) freeReplyObject(migrate_reply);\n        if (dots) zfree(dots);\n        if (!success) break;\n    }\n    return success;\n}\n\n/* Move slots between source and target nodes using MIGRATE.\n *\n * Options:\n * CLUSTER_MANAGER_OPT_VERBOSE -- Print a dot for every moved key.\n * CLUSTER_MANAGER_OPT_COLD    -- Move keys without opening slots /\n *                                reconfiguring the nodes.\n * CLUSTER_MANAGER_OPT_UPDATE  -- Update node->slots for source/target nodes.\n * CLUSTER_MANAGER_OPT_QUIET   -- Don't print info messages.\n*/\nstatic int clusterManagerMoveSlot(clusterManagerNode *source,\n                                  clusterManagerNode *target,\n                                  int slot, int opts,  char**err)\n{\n    if (!(opts & CLUSTER_MANAGER_OPT_QUIET)) {\n        printf(\"Moving slot %d from %s:%d to %s:%d: \", slot, source->ip,\n               source->port, target->ip, target->port);\n        fflush(stdout);\n    }\n    if (err != NULL) *err = NULL;\n    int pipeline = config.cluster_manager_command.pipeline,\n        timeout = config.cluster_manager_command.timeout,\n        print_dots = (opts & CLUSTER_MANAGER_OPT_VERBOSE),\n        option_cold = (opts & CLUSTER_MANAGER_OPT_COLD),\n        success = 1;\n    if (!option_cold) {\n        success = clusterManagerSetSlot(target, source, slot,\n                                        \"importing\", err);\n        if (!success) return 0;\n        success = clusterManagerSetSlot(source, target, slot,\n                                        \"migrating\", err);\n        if (!success) return 0;\n    }\n    success = clusterManagerMigrateKeysInSlot(source, target, slot, timeout,\n                                              pipeline, print_dots, err);\n    if (!(opts & CLUSTER_MANAGER_OPT_QUIET)) printf(\"\\n\");\n    if (!success) return 0;\n    /* Set the new node as the owner of the slot in all the known nodes. */\n    if (!option_cold) {\n        listIter li;\n        listNode *ln;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER \"\n                                                    \"SETSLOT %d %s %s\",\n                                                    slot, \"node\",\n                                                    target->name);\n            success = (r != NULL);\n            if (!success) return 0;\n            if (r->type == REDIS_REPLY_ERROR) {\n                success = 0;\n                if (err != NULL) {\n                    *err = zmalloc((r->len + 1) * sizeof(char));\n                    strcpy(*err, r->str);\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, *err);\n                }\n            }\n            freeReplyObject(r);\n            if (!success) return 0;\n        }\n    }\n    /* Update the node logical config */\n    if (opts & CLUSTER_MANAGER_OPT_UPDATE) {\n        source->slots[slot] = 0;\n        target->slots[slot] = 1;\n    }\n    return 1;\n}\n\n/* Flush the dirty node configuration by calling replicate for slaves or\n * adding the slots defined in the masters. */\nstatic int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err) {\n    if (!node->dirty) return 0;\n    redisReply *reply = NULL;\n    int is_err = 0, success = 1;\n    if (err != NULL) *err = NULL;\n    if (node->replicate != NULL) {\n        reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER REPLICATE %s\",\n                                        node->replicate);\n        if (reply == NULL || (is_err = (reply->type == REDIS_REPLY_ERROR))) {\n            if (is_err && err != NULL) {\n                *err = zmalloc((reply->len + 1) * sizeof(char));\n                strcpy(*err, reply->str);\n            }\n            success = 0;\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            goto cleanup;\n        }\n    } else {\n        int added = clusterManagerAddSlots(node, err);\n        if (!added || *err != NULL) success = 0;\n    }\n    node->dirty = 0;\ncleanup:\n    if (reply != NULL) freeReplyObject(reply);\n    return success;\n}\n\n/* Wait until the cluster configuration is consistent. */\nstatic void clusterManagerWaitForClusterJoin(void) {\n    printf(\"Waiting for the cluster to join\\n\");\n    int counter = 0,\n        check_after = CLUSTER_JOIN_CHECK_AFTER +\n                      (int)(listLength(cluster_manager.nodes) * 0.15f);\n    while(!clusterManagerIsConfigConsistent()) {\n        printf(\".\");\n        fflush(stdout);\n        sleep(1);\n        if (++counter > check_after) {\n            dict *status = clusterManagerGetLinkStatus();\n            dictIterator *iter = NULL;\n            if (status != NULL && dictSize(status) > 0) {\n                printf(\"\\n\");\n                clusterManagerLogErr(\"Warning: %d node(s) may \"\n                                     \"be unreachable\\n\", dictSize(status));\n                iter = dictGetIterator(status);\n                dictEntry *entry;\n                while ((entry = dictNext(iter)) != NULL) {\n                    sds nodeaddr = (sds) dictGetKey(entry);\n                    char *node_ip = NULL;\n                    int node_port = 0, node_bus_port = 0;\n                    list *from = (list *) dictGetVal(entry);\n                    if (parseClusterNodeAddress(nodeaddr, &node_ip,\n                        &node_port, &node_bus_port) && node_bus_port) {\n                        clusterManagerLogErr(\" - The port %d of node %s may \"\n                                             \"be unreachable from:\\n\",\n                                             node_bus_port, node_ip);\n                    } else {\n                        clusterManagerLogErr(\" - Node %s may be unreachable \"\n                                             \"from:\\n\", nodeaddr);\n                    }\n                    listIter li;\n                    listNode *ln;\n                    listRewind(from, &li);\n                    while ((ln = listNext(&li)) != NULL) {\n                        sds from_addr = ln->value;\n                        clusterManagerLogErr(\"   %s\\n\", from_addr);\n                        sdsfree(from_addr);\n                    }\n                    clusterManagerLogErr(\"Cluster bus ports must be reachable \"\n                                         \"by every node.\\nRemember that \"\n                                         \"cluster bus ports are different \"\n                                         \"from standard instance ports.\\n\");\n                    listEmpty(from);\n                }\n            }\n            if (iter != NULL) dictReleaseIterator(iter);\n            if (status != NULL) dictRelease(status);\n            counter = 0;\n        }\n    }\n    printf(\"\\n\");\n}\n\n/* Load node's cluster configuration by calling \"CLUSTER NODES\" command.\n * Node's configuration (name, replicate, slots, ...) is then updated.\n * If CLUSTER_MANAGER_OPT_GETFRIENDS flag is set into 'opts' argument,\n * and node already knows other nodes, the node's friends list is populated\n * with the other nodes info. */\nstatic int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,\n                                      char **err)\n{\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER NODES\");\n    int success = 1;\n    *err = NULL;\n    if (!clusterManagerCheckRedisReply(node, reply, err)) {\n        success = 0;\n        goto cleanup;\n    }\n    int getfriends = (opts & CLUSTER_MANAGER_OPT_GETFRIENDS);\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *name = NULL, *addr = NULL, *flags = NULL, *master_id = NULL,\n             *ping_sent = NULL, *ping_recv = NULL, *config_epoch = NULL,\n             *link_status = NULL;\n        UNUSED(link_status);\n        int i = 0;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            switch(i++){\n            case 0: name = token; break;\n            case 1: addr = token; break;\n            case 2: flags = token; break;\n            case 3: master_id = token; break;\n            case 4: ping_sent = token; break;\n            case 5: ping_recv = token; break;\n            case 6: config_epoch = token; break;\n            case 7: link_status = token; break;\n            }\n            if (i == 8) break; // Slots\n        }\n        if (!flags) {\n            success = 0;\n            goto cleanup;\n        }\n        int myself = (strstr(flags, \"myself\") != NULL);\n        clusterManagerNode *currentNode = NULL;\n        if (myself) {\n            node->flags |= CLUSTER_MANAGER_FLAG_MYSELF;\n            currentNode = node;\n            clusterManagerNodeResetSlots(node);\n            if (i == 8) {\n                int remaining = strlen(line);\n                while (remaining > 0) {\n                    p = strchr(line, ' ');\n                    if (p == NULL) p = line + remaining;\n                    remaining -= (p - line);\n\n                    char *slotsdef = line;\n                    *p = '\\0';\n                    if (remaining) {\n                        line = p + 1;\n                        remaining--;\n                    } else line = p;\n                    char *dash = NULL;\n                    if (slotsdef[0] == '[') {\n                        slotsdef++;\n                        if ((p = strstr(slotsdef, \"->-\"))) { // Migrating\n                            *p = '\\0';\n                            p += 3;\n                            char *closing_bracket = strchr(p, ']');\n                            if (closing_bracket) *closing_bracket = '\\0';\n                            sds slot = sdsnew(slotsdef);\n                            sds dst = sdsnew(p);\n                            node->migrating_count += 2;\n                            node->migrating = zrealloc(node->migrating,\n                                (node->migrating_count * sizeof(sds)));\n                            node->migrating[node->migrating_count - 2] =\n                                slot;\n                            node->migrating[node->migrating_count - 1] =\n                                dst;\n                        }  else if ((p = strstr(slotsdef, \"-<-\"))) {//Importing\n                            *p = '\\0';\n                            p += 3;\n                            char *closing_bracket = strchr(p, ']');\n                            if (closing_bracket) *closing_bracket = '\\0';\n                            sds slot = sdsnew(slotsdef);\n                            sds src = sdsnew(p);\n                            node->importing_count += 2;\n                            node->importing = zrealloc(node->importing,\n                                (node->importing_count * sizeof(sds)));\n                            node->importing[node->importing_count - 2] =\n                                slot;\n                            node->importing[node->importing_count - 1] =\n                                src;\n                        }\n                    } else if ((dash = strchr(slotsdef, '-')) != NULL) {\n                        p = dash;\n                        int start, stop;\n                        *p = '\\0';\n                        start = atoi(slotsdef);\n                        stop = atoi(p + 1);\n                        node->slots_count += (stop - (start - 1));\n                        while (start <= stop) node->slots[start++] = 1;\n                    } else if (p > slotsdef) {\n                        node->slots[atoi(slotsdef)] = 1;\n                        node->slots_count++;\n                    }\n                }\n            }\n            node->dirty = 0;\n        } else if (!getfriends) {\n            if (!(node->flags & CLUSTER_MANAGER_FLAG_MYSELF)) continue;\n            else break;\n        } else {\n            if (addr == NULL) {\n                fprintf(stderr, \"Error: invalid CLUSTER NODES reply\\n\");\n                success = 0;\n                goto cleanup;\n            }\n            char *c = strrchr(addr, '@');\n            if (c != NULL) *c = '\\0';\n            c = strrchr(addr, ':');\n            if (c == NULL) {\n                fprintf(stderr, \"Error: invalid CLUSTER NODES reply\\n\");\n                success = 0;\n                goto cleanup;\n            }\n            *c = '\\0';\n            int port = atoi(++c);\n            currentNode = clusterManagerNewNode(sdsnew(addr), port);\n            currentNode->flags |= CLUSTER_MANAGER_FLAG_FRIEND;\n            if (node->friends == NULL) node->friends = listCreate();\n            listAddNodeTail(node->friends, currentNode);\n        }\n        if (name != NULL) {\n            if (currentNode->name) sdsfree(currentNode->name);\n            currentNode->name = sdsnew(name);\n        }\n        if (currentNode->flags_str != NULL)\n            freeClusterManagerNodeFlags(currentNode->flags_str);\n        currentNode->flags_str = listCreate();\n        int flag_len;\n        while ((flag_len = strlen(flags)) > 0) {\n            sds flag = NULL;\n            char *fp = strchr(flags, ',');\n            if (fp) {\n                *fp = '\\0';\n                flag = sdsnew(flags);\n                flags = fp + 1;\n            } else {\n                flag = sdsnew(flags);\n                flags += flag_len;\n            }\n            if (strcmp(flag, \"noaddr\") == 0)\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_NOADDR;\n            else if (strcmp(flag, \"disconnected\") == 0)\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_DISCONNECT;\n            else if (strcmp(flag, \"fail\") == 0)\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_FAIL;\n            else if (strcmp(flag, \"slave\") == 0) {\n                currentNode->flags |= CLUSTER_MANAGER_FLAG_SLAVE;\n                if (master_id != NULL) {\n                    if (currentNode->replicate) sdsfree(currentNode->replicate);\n                    currentNode->replicate = sdsnew(master_id);\n                }\n            }\n            listAddNodeTail(currentNode->flags_str, flag);\n        }\n        if (config_epoch != NULL)\n            currentNode->current_epoch = atoll(config_epoch);\n        if (ping_sent != NULL) currentNode->ping_sent = atoll(ping_sent);\n        if (ping_recv != NULL) currentNode->ping_recv = atoll(ping_recv);\n        if (!getfriends && myself) break;\n    }\ncleanup:\n    if (reply) freeReplyObject(reply);\n    return success;\n}\n\n/* Retrieves info about the cluster using argument 'node' as the starting\n * point. All nodes will be loaded inside the cluster_manager.nodes list.\n * Warning: if something goes wrong, it will free the starting node before\n * returning 0. */\nstatic int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts) {\n    if (node->context == NULL && !clusterManagerNodeConnect(node)) {\n        freeClusterManagerNode(node);\n        return 0;\n    }\n    opts |= CLUSTER_MANAGER_OPT_GETFRIENDS;\n    char *e = NULL;\n    if (!clusterManagerNodeIsCluster(node, &e)) {\n        clusterManagerPrintNotClusterNodeError(node, e);\n        if (e) zfree(e);\n        freeClusterManagerNode(node);\n        return 0;\n    }\n    e = NULL;\n    if (!clusterManagerNodeLoadInfo(node, opts, &e)) {\n        if (e) {\n            CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, e);\n            zfree(e);\n        }\n        freeClusterManagerNode(node);\n        return 0;\n    }\n    listIter li;\n    listNode *ln;\n    if (cluster_manager.nodes != NULL) {\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL)\n            freeClusterManagerNode((clusterManagerNode *) ln->value);\n        listRelease(cluster_manager.nodes);\n    }\n    cluster_manager.nodes = listCreate();\n    listAddNodeTail(cluster_manager.nodes, node);\n    if (node->friends != NULL) {\n        listRewind(node->friends, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *friend = ln->value;\n            if (!friend->ip || !friend->port) goto invalid_friend;\n            if (!friend->context && !clusterManagerNodeConnect(friend))\n                goto invalid_friend;\n            e = NULL;\n            if (clusterManagerNodeLoadInfo(friend, 0, &e)) {\n                if (friend->flags & (CLUSTER_MANAGER_FLAG_NOADDR |\n                                     CLUSTER_MANAGER_FLAG_DISCONNECT |\n                                     CLUSTER_MANAGER_FLAG_FAIL))\n                {\n                    goto invalid_friend;\n                }\n                listAddNodeTail(cluster_manager.nodes, friend);\n            } else {\n                clusterManagerLogErr(\"[ERR] Unable to load info for \"\n                                     \"node %s:%d\\n\",\n                                     friend->ip, friend->port);\n                goto invalid_friend;\n            }\n            continue;\ninvalid_friend:\n            if (!(friend->flags & CLUSTER_MANAGER_FLAG_SLAVE))\n                cluster_manager.unreachable_masters++;\n            freeClusterManagerNode(friend);\n        }\n        listRelease(node->friends);\n        node->friends = NULL;\n    }\n    // Count replicas for each node\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->replicate != NULL) {\n            clusterManagerNode *master = clusterManagerNodeByName(n->replicate);\n            if (master == NULL) {\n                clusterManagerLogWarn(\"*** WARNING: %s:%d claims to be \"\n                                      \"slave of unknown node ID %s.\\n\",\n                                      n->ip, n->port, n->replicate);\n            } else master->replicas_count++;\n        }\n    }\n    return 1;\n}\n\n/* Compare functions used by various sorting operations. */\nint clusterManagerSlotCompare(const void *slot1, const void *slot2) {\n    const char **i1 = (const char **)slot1;\n    const char **i2 = (const char **)slot2;\n    return strcmp(*i1, *i2);\n}\n\nint clusterManagerSlotCountCompareDesc(const void *n1, const void *n2) {\n    clusterManagerNode *node1 = *((clusterManagerNode **) n1);\n    clusterManagerNode *node2 = *((clusterManagerNode **) n2);\n    return node2->slots_count - node1->slots_count;\n}\n\nint clusterManagerCompareNodeBalance(const void *n1, const void *n2) {\n    clusterManagerNode *node1 = *((clusterManagerNode **) n1);\n    clusterManagerNode *node2 = *((clusterManagerNode **) n2);\n    return node1->balance - node2->balance;\n}\n\nstatic sds clusterManagerGetConfigSignature(clusterManagerNode *node) {\n    sds signature = NULL;\n    int node_count = 0, i = 0, name_len = 0;\n    char **node_configs = NULL;\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER NODES\");\n    if (reply == NULL || reply->type == REDIS_REPLY_ERROR)\n        goto cleanup;\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        i = 0;\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *nodename = NULL;\n        int tot_size = 0;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            if (i == 0) {\n                nodename = token;\n                tot_size = (p - token);\n                name_len = tot_size++; // Make room for ':' in tot_size\n            }\n            if (++i == 8) break;\n        }\n        if (i != 8) continue;\n        if (nodename == NULL) continue;\n        int remaining = strlen(line);\n        if (remaining == 0) continue;\n        char **slots = NULL;\n        int c = 0;\n        while (remaining > 0) {\n            p = strchr(line, ' ');\n            if (p == NULL) p = line + remaining;\n            int size = (p - line);\n            remaining -= size;\n            tot_size += size;\n            char *slotsdef = line;\n            *p = '\\0';\n            if (remaining) {\n                line = p + 1;\n                remaining--;\n            } else line = p;\n            if (slotsdef[0] != '[') {\n                c++;\n                slots = zrealloc(slots, (c * sizeof(char *)));\n                slots[c - 1] = slotsdef;\n            }\n        }\n        if (c > 0) {\n            if (c > 1)\n                qsort(slots, c, sizeof(char *), clusterManagerSlotCompare);\n            node_count++;\n            node_configs =\n                zrealloc(node_configs, (node_count * sizeof(char *)));\n            /* Make room for '|' separators. */\n            tot_size += (sizeof(char) * (c - 1));\n            char *cfg = zmalloc((sizeof(char) * tot_size) + 1);\n            memcpy(cfg, nodename, name_len);\n            char *sp = cfg + name_len;\n            *(sp++) = ':';\n            for (i = 0; i < c; i++) {\n                if (i > 0) *(sp++) = ',';\n                int slen = strlen(slots[i]);\n                memcpy(sp, slots[i], slen);\n                sp += slen;\n            }\n            *(sp++) = '\\0';\n            node_configs[node_count - 1] = cfg;\n        }\n        zfree(slots);\n    }\n    if (node_count > 0) {\n        if (node_count > 1) {\n            qsort(node_configs, node_count, sizeof(char *),\n                  clusterManagerSlotCompare);\n        }\n        signature = sdsempty();\n        for (i = 0; i < node_count; i++) {\n            if (i > 0) signature = sdscatprintf(signature, \"%c\", '|');\n            signature = sdscatfmt(signature, \"%s\", node_configs[i]);\n        }\n    }\ncleanup:\n    if (reply != NULL) freeReplyObject(reply);\n    if (node_configs != NULL) {\n        for (i = 0; i < node_count; i++) zfree(node_configs[i]);\n        zfree(node_configs);\n    }\n    return signature;\n}\n\nstatic int clusterManagerIsConfigConsistent(void) {\n    if (cluster_manager.nodes == NULL) return 0;\n    int consistent = (listLength(cluster_manager.nodes) <= 1);\n    // If the Cluster has only one node, it's always consistent\n    if (consistent) return 1;\n    sds first_cfg = NULL;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        sds cfg = clusterManagerGetConfigSignature(node);\n        if (cfg == NULL) {\n            consistent = 0;\n            break;\n        }\n        if (first_cfg == NULL) first_cfg = cfg;\n        else {\n            consistent = !sdscmp(first_cfg, cfg);\n            sdsfree(cfg);\n            if (!consistent) break;\n        }\n    }\n    if (first_cfg != NULL) sdsfree(first_cfg);\n    return consistent;\n}\n\nstatic list *clusterManagerGetDisconnectedLinks(clusterManagerNode *node) {\n    list *links = NULL;\n    redisReply *reply = CLUSTER_MANAGER_COMMAND(node, \"CLUSTER NODES\");\n    if (!clusterManagerCheckRedisReply(node, reply, NULL)) goto cleanup;\n    links = listCreate();\n    char *lines = reply->str, *p, *line;\n    while ((p = strstr(lines, \"\\n\")) != NULL) {\n        int i = 0;\n        *p = '\\0';\n        line = lines;\n        lines = p + 1;\n        char *nodename = NULL, *addr = NULL, *flags = NULL, *link_status = NULL;\n        while ((p = strchr(line, ' ')) != NULL) {\n            *p = '\\0';\n            char *token = line;\n            line = p + 1;\n            if (i == 0) nodename = token;\n            else if (i == 1) addr = token;\n            else if (i == 2) flags = token;\n            else if (i == 7) link_status = token;\n            else if (i == 8) break;\n            i++;\n        }\n        if (i == 7) link_status = line;\n        if (nodename == NULL || addr == NULL || flags == NULL ||\n            link_status == NULL) continue;\n        if (strstr(flags, \"myself\") != NULL) continue;\n        int disconnected = ((strstr(flags, \"disconnected\") != NULL) ||\n                            (strstr(link_status, \"disconnected\")));\n        int handshaking = (strstr(flags, \"handshake\") != NULL);\n        if (disconnected || handshaking) {\n            clusterManagerLink *link = zmalloc(sizeof(*link));\n            link->node_name = sdsnew(nodename);\n            link->node_addr = sdsnew(addr);\n            link->connected = 0;\n            link->handshaking = handshaking;\n            listAddNodeTail(links, link);\n        }\n    }\ncleanup:\n    if (reply != NULL) freeReplyObject(reply);\n    return links;\n}\n\n/* Check for disconnected cluster links. It returns a dict whose keys\n * are the unreachable node addresses and the values are lists of\n * node addresses that cannot reach the unreachable node. */\nstatic dict *clusterManagerGetLinkStatus(void) {\n    if (cluster_manager.nodes == NULL) return NULL;\n    dict *status = dictCreate(&clusterManagerLinkDictType, NULL);\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        list *links = clusterManagerGetDisconnectedLinks(node);\n        if (links) {\n            listIter lli;\n            listNode *lln;\n            listRewind(links, &lli);\n            while ((lln = listNext(&lli)) != NULL) {\n                clusterManagerLink *link = lln->value;\n                list *from = NULL;\n                dictEntry *entry = dictFind(status, link->node_addr);\n                if (entry) from = dictGetVal(entry);\n                else {\n                    from = listCreate();\n                    dictAdd(status, sdsdup(link->node_addr), from);\n                }\n                sds myaddr = sdsempty();\n                myaddr = sdscatfmt(myaddr, \"%s:%u\", node->ip, node->port);\n                listAddNodeTail(from, myaddr);\n                sdsfree(link->node_name);\n                sdsfree(link->node_addr);\n                zfree(link);\n            }\n            listRelease(links);\n        }\n    }\n    return status;\n}\n\n/* Add the error string to cluster_manager.errors and print it. */\nstatic void clusterManagerOnError(sds err) {\n    if (cluster_manager.errors == NULL)\n        cluster_manager.errors = listCreate();\n    listAddNodeTail(cluster_manager.errors, err);\n    clusterManagerLogErr(\"%s\\n\", (char *) err);\n}\n\n/* Check the slots coverage of the cluster. The 'all_slots' argument must be\n * and array of 16384 bytes. Every covered slot will be set to 1 in the\n * 'all_slots' array. The function returns the total number if covered slots.*/\nstatic int clusterManagerGetCoveredSlots(char *all_slots) {\n    if (cluster_manager.nodes == NULL) return 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    int totslots = 0, i;\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n            if (node->slots[i] && !all_slots[i]) {\n                all_slots[i] = 1;\n                totslots++;\n            }\n        }\n    }\n    return totslots;\n}\n\nstatic void clusterManagerPrintSlotsList(list *slots) {\n    clusterManagerNode n = {0};\n    listIter li;\n    listNode *ln;\n    listRewind(slots, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        int slot = atoi(ln->value);\n        if (slot >= 0 && slot < CLUSTER_MANAGER_SLOTS)\n            n.slots[slot] = 1;\n    }\n    sds nodeslist = clusterManagerNodeSlotsString(&n);\n    printf(\"%s\\n\", nodeslist);\n    sdsfree(nodeslist);\n}\n\n/* Return the node, among 'nodes' with the greatest number of keys\n * in the specified slot. */\nstatic clusterManagerNode * clusterManagerGetNodeWithMostKeysInSlot(list *nodes,\n                                                                    int slot,\n                                                                    char **err)\n{\n    clusterManagerNode *node = NULL;\n    int numkeys = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(nodes, &li);\n    if (err) *err = NULL;\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n            continue;\n        redisReply *r =\n            CLUSTER_MANAGER_COMMAND(n, \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n        int success = clusterManagerCheckRedisReply(n, r, err);\n        if (success) {\n            if (r->integer > numkeys || node == NULL) {\n                numkeys = r->integer;\n                node = n;\n            }\n        }\n        if (r != NULL) freeReplyObject(r);\n        /* If the reply contains errors */\n        if (!success) {\n            if (err != NULL && *err != NULL)\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err);\n            node = NULL;\n            break;\n        }\n    }\n    return node;\n}\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\nstatic clusterManagerNode *clusterManagerNodeWithLeastReplicas() {\n    clusterManagerNode *node = NULL;\n    int lowest_count = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        if (node == NULL || n->replicas_count < lowest_count) {\n            node = n;\n            lowest_count = n->replicas_count;\n        }\n    }\n    return node;\n}\n\n/* This function returns a random master node, return NULL if none */\n\nstatic clusterManagerNode *clusterManagerNodeMasterRandom() {\n    int master_count = 0;\n    int idx;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        master_count++;\n    }\n\n    srand(time(NULL));\n    idx = rand() % master_count;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        if (!idx--) {\n            return n;\n        }\n    }\n    /* Can not be reached */\n    return NULL;\n}\n\nstatic int clusterManagerFixSlotsCoverage(char *all_slots) {\n    int force_fix = config.cluster_manager_command.flags &\n                    CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;\n\n    if (cluster_manager.unreachable_masters > 0 && !force_fix) {\n        clusterManagerLogWarn(\"*** Fixing slots coverage with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\\n\", cluster_manager.unreachable_masters);\n        exit(1);\n    }\n\n    int i, fixed = 0;\n    list *none = NULL, *single = NULL, *multi = NULL;\n    clusterManagerLogInfo(\">>> Fixing slots coverage...\\n\");\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        int covered = all_slots[i];\n        if (!covered) {\n            sds slot = sdsfromlonglong((long long) i);\n            list *slot_nodes = listCreate();\n            sds slot_nodes_str = sdsempty();\n            listIter li;\n            listNode *ln;\n            listRewind(cluster_manager.nodes, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n                    continue;\n                redisReply *reply = CLUSTER_MANAGER_COMMAND(n,\n                    \"CLUSTER GETKEYSINSLOT %d %d\", i, 1);\n                if (!clusterManagerCheckRedisReply(n, reply, NULL)) {\n                    fixed = -1;\n                    if (reply) freeReplyObject(reply);\n                    goto cleanup;\n                }\n                assert(reply->type == REDIS_REPLY_ARRAY);\n                if (reply->elements > 0) {\n                    listAddNodeTail(slot_nodes, n);\n                    if (listLength(slot_nodes) > 1)\n                        slot_nodes_str = sdscat(slot_nodes_str, \", \");\n                    slot_nodes_str = sdscatfmt(slot_nodes_str,\n                                               \"%s:%u\", n->ip, n->port);\n                }\n                freeReplyObject(reply);\n            }\n            sdsfree(slot_nodes_str);\n            dictAdd(clusterManagerUncoveredSlots, slot, slot_nodes);\n        }\n    }\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    none = listCreate();\n    single = listCreate();\n    multi = listCreate();\n    dictIterator *iter = dictGetIterator(clusterManagerUncoveredSlots);\n    dictEntry *entry;\n    while ((entry = dictNext(iter)) != NULL) {\n        sds slot = (sds) dictGetKey(entry);\n        list *nodes = (list *) dictGetVal(entry);\n        switch (listLength(nodes)){\n        case 0: listAddNodeTail(none, slot); break;\n        case 1: listAddNodeTail(single, slot); break;\n        default: listAddNodeTail(multi, slot); break;\n        }\n    }\n    dictReleaseIterator(iter);\n\n    /*  Handle case \"1\": keys in no node. */\n    if (listLength(none) > 0) {\n        printf(\"The following uncovered slots have no keys \"\n               \"across the cluster:\\n\");\n        clusterManagerPrintSlotsList(none);\n        if (confirmWithYes(\"Fix these slots by covering with a random node?\")){\n            listIter li;\n            listNode *ln;\n            listRewind(none, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                sds slot = ln->value;\n                int s = atoi(slot);\n                clusterManagerNode *n = clusterManagerNodeMasterRandom();\n                clusterManagerLogInfo(\">>> Covering slot %s with %s:%d\\n\",\n                                      slot, n->ip, n->port);\n                if (!clusterManagerSetSlotOwner(n, s, 0)) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n                 * info into the node struct, in order to keep it synced */\n                n->slots[s] = 1;\n                fixed++;\n            }\n        }\n    }\n\n    /*  Handle case \"2\": keys only in one node. */\n    if (listLength(single) > 0) {\n        printf(\"The following uncovered slots have keys in just one node:\\n\");\n        clusterManagerPrintSlotsList(single);\n        if (confirmWithYes(\"Fix these slots by covering with those nodes?\")){\n            listIter li;\n            listNode *ln;\n            listRewind(single, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                sds slot = ln->value;\n                int s = atoi(slot);\n                dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);\n                assert(entry != NULL);\n                list *nodes = (list *) dictGetVal(entry);\n                listNode *fn = listFirst(nodes);\n                assert(fn != NULL);\n                clusterManagerNode *n = fn->value;\n                clusterManagerLogInfo(\">>> Covering slot %s with %s:%d\\n\",\n                                      slot, n->ip, n->port);\n                if (!clusterManagerSetSlotOwner(n, s, 0)) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n                 * info into the node struct, in order to keep it synced */\n                n->slots[atoi(slot)] = 1;\n                fixed++;\n            }\n        }\n    }\n\n    /* Handle case \"3\": keys in multiple nodes. */\n    if (listLength(multi) > 0) {\n        printf(\"The following uncovered slots have keys in multiple nodes:\\n\");\n        clusterManagerPrintSlotsList(multi);\n        if (confirmWithYes(\"Fix these slots by moving keys \"\n                           \"into a single node?\")) {\n            listIter li;\n            listNode *ln;\n            listRewind(multi, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                sds slot = ln->value;\n                dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);\n                assert(entry != NULL);\n                list *nodes = (list *) dictGetVal(entry);\n                int s = atoi(slot);\n                clusterManagerNode *target =\n                    clusterManagerGetNodeWithMostKeysInSlot(nodes, s, NULL);\n                if (target == NULL) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                clusterManagerLogInfo(\">>> Covering slot %s moving keys \"\n                                      \"to %s:%d\\n\", slot,\n                                      target->ip, target->port);\n                if (!clusterManagerSetSlotOwner(target, s, 1)) {\n                    fixed = -1;\n                    goto cleanup;\n                }\n                /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n                 * info into the node struct, in order to keep it synced */\n                target->slots[atoi(slot)] = 1;\n                listIter nli;\n                listNode *nln;\n                listRewind(nodes, &nli);\n                while ((nln = listNext(&nli)) != NULL) {\n                    clusterManagerNode *src = nln->value;\n                    if (src == target) continue;\n                    /* Assign the slot to target node in the source node. */\n                    if (!clusterManagerSetSlot(src, target, s, \"NODE\", NULL))\n                        fixed = -1;\n                    if (fixed < 0) goto cleanup;\n                    /* Set the source node in 'importing' state\n                     * (even if we will actually migrate keys away)\n                     * in order to avoid receiving redirections\n                     * for MIGRATE. */\n                    if (!clusterManagerSetSlot(src, target, s,\n                                               \"IMPORTING\", NULL)) fixed = -1;\n                    if (fixed < 0) goto cleanup;\n                    int opts = CLUSTER_MANAGER_OPT_VERBOSE |\n                               CLUSTER_MANAGER_OPT_COLD;\n                    if (!clusterManagerMoveSlot(src, target, s, opts, NULL)) {\n                        fixed = -1;\n                        goto cleanup;\n                    }\n                    if (!clusterManagerClearSlotStatus(src, s))\n                        fixed = -1;\n                    if (fixed < 0) goto cleanup;\n                }\n                fixed++;\n            }\n        }\n    }\ncleanup:\n    if (none) listRelease(none);\n    if (single) listRelease(single);\n    if (multi) listRelease(multi);\n    return fixed;\n}\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. */\nstatic int clusterManagerFixOpenSlot(int slot) {\n    int force_fix = config.cluster_manager_command.flags &\n                    CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;\n\n    if (cluster_manager.unreachable_masters > 0 && !force_fix) {\n        clusterManagerLogWarn(\"*** Fixing open slots with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\\n\", cluster_manager.unreachable_masters);\n        exit(1);\n    }\n\n    clusterManagerLogInfo(\">>> Fixing open slot %d\\n\", slot);\n    /* Try to obtain the current slot owner, according to the current\n     * nodes configuration. */\n    int success = 1;\n    list *owners = listCreate();    /* List of nodes claiming some ownership.\n                                       it could be stating in the configuration\n                                       to have the node ownership, or just\n                                       holding keys for such slot. */\n    list *migrating = listCreate();\n    list *importing = listCreate();\n    sds migrating_str = sdsempty();\n    sds importing_str = sdsempty();\n    clusterManagerNode *owner = NULL; /* The obvious slot owner if any. */\n\n    /* Iterate all the nodes, looking for potential owners of this slot. */\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        if (n->slots[slot]) {\n            listAddNodeTail(owners, n);\n        } else {\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n,\n                \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (success && r->integer > 0) {\n                clusterManagerLogWarn(\"*** Found keys about slot %d \"\n                                      \"in non-owner node %s:%d!\\n\", slot,\n                                      n->ip, n->port);\n                listAddNodeTail(owners, n);\n            }\n            if (r) freeReplyObject(r);\n            if (!success) goto cleanup;\n        }\n    }\n\n    /* If we have only a single potential owner for this slot,\n     * set it as \"owner\". */\n    if (listLength(owners) == 1) owner = listFirst(owners)->value;\n\n    /* Scan the list of nodes again, in order to populate the\n     * list of nodes in importing or migrating state for\n     * this slot. */\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        int is_migrating = 0, is_importing = 0;\n        if (n->migrating) {\n            for (int i = 0; i < n->migrating_count; i += 2) {\n                sds migrating_slot = n->migrating[i];\n                if (atoi(migrating_slot) == slot) {\n                    char *sep = (listLength(migrating) == 0 ? \"\" : \",\");\n                    migrating_str = sdscatfmt(migrating_str, \"%s%s:%u\",\n                                              sep, n->ip, n->port);\n                    listAddNodeTail(migrating, n);\n                    is_migrating = 1;\n                    break;\n                }\n            }\n        }\n        if (!is_migrating && n->importing) {\n            for (int i = 0; i < n->importing_count; i += 2) {\n                sds importing_slot = n->importing[i];\n                if (atoi(importing_slot) == slot) {\n                    char *sep = (listLength(importing) == 0 ? \"\" : \",\");\n                    importing_str = sdscatfmt(importing_str, \"%s%s:%u\",\n                                              sep, n->ip, n->port);\n                    listAddNodeTail(importing, n);\n                    is_importing = 1;\n                    break;\n                }\n            }\n        }\n\n        /* If the node is neither migrating nor importing and it's not\n         * the owner, then is added to the importing list in case\n         * it has keys in the slot. */\n        if (!is_migrating && !is_importing && n != owner) {\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n,\n                \"CLUSTER COUNTKEYSINSLOT %d\", slot);\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (success && r->integer > 0) {\n                clusterManagerLogWarn(\"*** Found keys about slot %d \"\n                                      \"in node %s:%d!\\n\", slot, n->ip,\n                                      n->port);\n                char *sep = (listLength(importing) == 0 ? \"\" : \",\");\n                importing_str = sdscatfmt(importing_str, \"%s%S:%u\",\n                                          sep, n->ip, n->port);\n                listAddNodeTail(importing, n);\n            }\n            if (r) freeReplyObject(r);\n            if (!success) goto cleanup;\n        }\n    }\n    if (sdslen(migrating_str) > 0)\n        printf(\"Set as migrating in: %s\\n\", migrating_str);\n    if (sdslen(importing_str) > 0)\n        printf(\"Set as importing in: %s\\n\", importing_str);\n\n    /* If there is no slot owner, set as owner the node with the biggest\n     * number of keys, among the set of migrating / importing nodes. */\n    if (owner == NULL) {\n        clusterManagerLogInfo(\">>> No single clear owner for the slot, \"\n                              \"selecting an owner by # of keys...\\n\");\n        owner = clusterManagerGetNodeWithMostKeysInSlot(cluster_manager.nodes,\n                                                        slot, NULL);\n        // If we still don't have an owner, we can't fix it.\n        if (owner == NULL) {\n            clusterManagerLogErr(\"[ERR] Can't select a slot owner. \"\n                                 \"Impossible to fix.\\n\");\n            success = 0;\n            goto cleanup;\n        }\n\n        // Use ADDSLOTS to assign the slot.\n        clusterManagerLogWarn(\"*** Configuring %s:%d as the slot owner\\n\",\n                              owner->ip, owner->port);\n        success = clusterManagerClearSlotStatus(owner, slot);\n        if (!success) goto cleanup;\n        success = clusterManagerSetSlotOwner(owner, slot, 0);\n        if (!success) goto cleanup;\n        /* Since CLUSTER ADDSLOTS succeeded, we also update the slot\n         * info into the node struct, in order to keep it synced */\n        owner->slots[slot] = 1;\n        /* Make sure this information will propagate. Not strictly needed\n         * since there is no past owner, so all the other nodes will accept\n         * whatever epoch this node will claim the slot with. */\n        success = clusterManagerBumpEpoch(owner);\n        if (!success) goto cleanup;\n        /* Remove the owner from the list of migrating/importing\n         * nodes. */\n        clusterManagerRemoveNodeFromList(migrating, owner);\n        clusterManagerRemoveNodeFromList(importing, owner);\n    }\n\n    /* If there are multiple owners of the slot, we need to fix it\n     * so that a single node is the owner and all the other nodes\n     * are in importing state. Later the fix can be handled by one\n     * of the base cases above.\n     *\n     * Note that this case also covers multiple nodes having the slot\n     * in migrating state, since migrating is a valid state only for\n     * slot owners. */\n    if (listLength(owners) > 1) {\n        /* Owner cannot be NULL at this point, since if there are more owners,\n         * the owner has been set in the previous condition (owner == NULL). */\n        assert(owner != NULL);\n        listRewind(owners, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n == owner) continue;\n            success = clusterManagerDelSlot(n, slot, 1);\n            if (!success) goto cleanup;\n            n->slots[slot] = 0;\n            /* Assign the slot to the owner in the node 'n' configuration.' */\n            success = clusterManagerSetSlot(n, owner, slot, \"node\", NULL);\n            if (!success) goto cleanup;\n            success = clusterManagerSetSlot(n, owner, slot, \"importing\", NULL);\n            if (!success) goto cleanup;\n            /* Avoid duplicates. */\n            clusterManagerRemoveNodeFromList(importing, n);\n            listAddNodeTail(importing, n);\n            /* Ensure that the node is not in the migrating list. */\n            clusterManagerRemoveNodeFromList(migrating, n);\n        }\n    }\n    int move_opts = CLUSTER_MANAGER_OPT_VERBOSE;\n\n    /* Case 1: The slot is in migrating state in one node, and in\n     *         importing state in 1 node. That's trivial to address. */\n    if (listLength(migrating) == 1 && listLength(importing) == 1) {\n        clusterManagerNode *src = listFirst(migrating)->value;\n        clusterManagerNode *dst = listFirst(importing)->value;\n        clusterManagerLogInfo(\">>> Case 1: Moving slot %d from \"\n                              \"%s:%d to %s:%d\\n\", slot,\n                              src->ip, src->port, dst->ip, dst->port);\n        move_opts |= CLUSTER_MANAGER_OPT_UPDATE;\n        success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);\n    }\n\n    /* Case 2: There are multiple nodes that claim the slot as importing,\n     * they probably got keys about the slot after a restart so opened\n     * the slot. In this case we just move all the keys to the owner\n     * according to the configuration. */\n    else if (listLength(migrating) == 0 && listLength(importing) > 0) {\n        clusterManagerLogInfo(\">>> Case 2: Moving all the %d slot keys to its \"\n                              \"owner %s:%d\\n\", slot, owner->ip, owner->port);\n        move_opts |= CLUSTER_MANAGER_OPT_COLD;\n        listRewind(importing, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n == owner) continue;\n            success = clusterManagerMoveSlot(n, owner, slot, move_opts, NULL);\n            if (!success) goto cleanup;\n            clusterManagerLogInfo(\">>> Setting %d as STABLE in \"\n                                  \"%s:%d\\n\", slot, n->ip, n->port);\n            success = clusterManagerClearSlotStatus(n, slot);\n            if (!success) goto cleanup;\n        }\n        /* Since the slot has been moved in \"cold\" mode, ensure that all the\n         * other nodes update their own configuration about the slot itself. */\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n == owner) continue;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n            success = clusterManagerSetSlot(n, owner, slot, \"NODE\", NULL);\n            if (!success) goto cleanup;\n        }\n    }\n\n    /* Case 3: The slot is in migrating state in one node but multiple\n     * other nodes claim to be in importing state and don't have any key in\n     * the slot. We search for the importing node having the same ID as\n     * the destination node of the migrating node.\n     * In that case we move the slot from the migrating node to this node and\n     * we close the importing states on all the other importing nodes.\n     * If no importing node has the same ID as the destination node of the\n     * migrating node, the slot's state is closed on both the migrating node\n     * and the importing nodes. */\n    else if (listLength(migrating) == 1 && listLength(importing) > 1) {\n        int try_to_fix = 1;\n        clusterManagerNode *src = listFirst(migrating)->value;\n        clusterManagerNode *dst = NULL;\n        sds target_id = NULL;\n        for (int i = 0; i < src->migrating_count; i += 2) {\n            sds migrating_slot = src->migrating[i];\n            if (atoi(migrating_slot) == slot) {\n                target_id = src->migrating[i + 1];\n                break;\n            }\n        }\n        assert(target_id != NULL);\n        listIter li;\n        listNode *ln;\n        listRewind(importing, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            int count = clusterManagerCountKeysInSlot(n, slot);\n            if (count > 0) {\n                try_to_fix = 0;\n                break;\n            }\n            if (strcmp(n->name, target_id) == 0) dst = n;\n        }\n        if (!try_to_fix) goto unhandled_case;\n        if (dst != NULL) {\n            clusterManagerLogInfo(\">>> Case 3: Moving slot %d from %s:%d to \"\n                                  \"%s:%d and closing it on all the other \"\n                                  \"importing nodes.\\n\",\n                                  slot, src->ip, src->port,\n                                  dst->ip, dst->port);\n            /* Move the slot to the destination node. */\n            success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);\n            if (!success) goto cleanup;\n            /* Close slot on all the other importing nodes. */\n            listRewind(importing, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (dst == n) continue;\n                success = clusterManagerClearSlotStatus(n, slot);\n                if (!success) goto cleanup;\n            }\n        } else {\n            clusterManagerLogInfo(\">>> Case 3: Closing slot %d on both \"\n                                  \"migrating and importing nodes.\\n\", slot);\n            /* Close the slot on both the migrating node and the importing\n             * nodes. */\n            success = clusterManagerClearSlotStatus(src, slot);\n            if (!success) goto cleanup;\n            listRewind(importing, &li);\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                success = clusterManagerClearSlotStatus(n, slot);\n                if (!success) goto cleanup;\n            }\n        }\n    } else {\n        int try_to_close_slot = (listLength(importing) == 0 &&\n                                 listLength(migrating) == 1);\n        if (try_to_close_slot) {\n            clusterManagerNode *n = listFirst(migrating)->value;\n            if (!owner || owner != n) {\n                redisReply *r = CLUSTER_MANAGER_COMMAND(n,\n                    \"CLUSTER GETKEYSINSLOT %d %d\", slot, 10);\n                success = clusterManagerCheckRedisReply(n, r, NULL);\n                if (r) {\n                    if (success) try_to_close_slot = (r->elements == 0);\n                    freeReplyObject(r);\n                }\n                if (!success) goto cleanup;\n            }\n        }\n        /* Case 4: There are no slots claiming to be in importing state, but\n         * there is a migrating node that actually don't have any key or is the\n         * slot owner. We can just close the slot, probably a reshard\n         * interrupted in the middle. */\n        if (try_to_close_slot) {\n            clusterManagerNode *n = listFirst(migrating)->value;\n            clusterManagerLogInfo(\">>> Case 4: Closing slot %d on %s:%d\\n\",\n                                  slot, n->ip, n->port);\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER SETSLOT %d %s\",\n                                                    slot, \"STABLE\");\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (r) freeReplyObject(r);\n            if (!success) goto cleanup;\n        } else {\nunhandled_case:\n            success = 0;\n            clusterManagerLogErr(\"[ERR] Sorry, redis-cli can't fix this slot \"\n                                 \"yet (work in progress). Slot is set as \"\n                                 \"migrating in %s, as importing in %s, \"\n                                 \"owner is %s:%d\\n\", migrating_str,\n                                 importing_str, owner->ip, owner->port);\n        }\n    }\ncleanup:\n    listRelease(owners);\n    listRelease(migrating);\n    listRelease(importing);\n    sdsfree(migrating_str);\n    sdsfree(importing_str);\n    return success;\n}\n\nstatic int clusterManagerFixMultipleSlotOwners(int slot, list *owners) {\n    clusterManagerLogInfo(\">>> Fixing multiple owners for slot %d...\\n\", slot);\n    int success = 0;\n    assert(listLength(owners) > 1);\n    clusterManagerNode *owner = clusterManagerGetNodeWithMostKeysInSlot(owners,\n                                                                        slot,\n                                                                        NULL);\n    if (!owner) owner = listFirst(owners)->value;\n    clusterManagerLogInfo(\">>> Setting slot %d owner: %s:%d\\n\",\n                          slot, owner->ip, owner->port);\n    /* Set the slot owner. */\n    if (!clusterManagerSetSlotOwner(owner, slot, 0)) return 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    /* Update configuration in all the other master nodes by assigning the slot\n     * itself to the new owner, and by eventually migrating keys if the node\n     * has keys for the slot. */\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n == owner) continue;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n        int count = clusterManagerCountKeysInSlot(n, slot);\n        success = (count >= 0);\n        if (!success) break;\n        clusterManagerDelSlot(n, slot, 1);\n        if (!clusterManagerSetSlot(n, owner, slot, \"node\", NULL)) return 0;\n        if (count > 0) {\n            int opts = CLUSTER_MANAGER_OPT_VERBOSE |\n                       CLUSTER_MANAGER_OPT_COLD;\n            success = clusterManagerMoveSlot(n, owner, slot, opts, NULL);\n            if (!success) break;\n        }\n    }\n    return success;\n}\n\nstatic int clusterManagerCheckCluster(int quiet) {\n    listNode *ln = listFirst(cluster_manager.nodes);\n    if (!ln) return 0;\n    clusterManagerNode *node = ln->value;\n    clusterManagerLogInfo(\">>> Performing Cluster Check (using node %s:%d)\\n\",\n                          node->ip, node->port);\n    int result = 1, consistent = 0;\n    int do_fix = config.cluster_manager_command.flags &\n                 CLUSTER_MANAGER_CMD_FLAG_FIX;\n    if (!quiet) clusterManagerShowNodes();\n    consistent = clusterManagerIsConfigConsistent();\n    if (!consistent) {\n        sds err = sdsnew(\"[ERR] Nodes don't agree about configuration!\");\n        clusterManagerOnError(err);\n        result = 0;\n    } else {\n        clusterManagerLogOk(\"[OK] All nodes agree about slots \"\n                            \"configuration.\\n\");\n    }\n    /* Check open slots */\n    clusterManagerLogInfo(\">>> Check for open slots...\\n\");\n    listIter li;\n    listRewind(cluster_manager.nodes, &li);\n    int i;\n    dict *open_slots = NULL;\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->migrating != NULL) {\n            if (open_slots == NULL)\n                open_slots = dictCreate(&clusterManagerDictType, NULL);\n            sds errstr = sdsempty();\n            errstr = sdscatprintf(errstr,\n                                \"[WARNING] Node %s:%d has slots in \"\n                                \"migrating state \",\n                                n->ip,\n                                n->port);\n            for (i = 0; i < n->migrating_count; i += 2) {\n                sds slot = n->migrating[i];\n                dictReplace(open_slots, slot, sdsdup(n->migrating[i + 1]));\n                char *fmt = (i > 0 ? \",%S\" : \"%S\");\n                errstr = sdscatfmt(errstr, fmt, slot);\n            }\n            errstr = sdscat(errstr, \".\");\n            clusterManagerOnError(errstr);\n        }\n        if (n->importing != NULL) {\n            if (open_slots == NULL)\n                open_slots = dictCreate(&clusterManagerDictType, NULL);\n            sds errstr = sdsempty();\n            errstr = sdscatprintf(errstr,\n                                \"[WARNING] Node %s:%d has slots in \"\n                                \"importing state \",\n                                n->ip,\n                                n->port);\n            for (i = 0; i < n->importing_count; i += 2) {\n                sds slot = n->importing[i];\n                dictReplace(open_slots, slot, sdsdup(n->importing[i + 1]));\n                char *fmt = (i > 0 ? \",%S\" : \"%S\");\n                errstr = sdscatfmt(errstr, fmt, slot);\n            }\n            errstr = sdscat(errstr, \".\");\n            clusterManagerOnError(errstr);\n        }\n    }\n    if (open_slots != NULL) {\n        result = 0;\n        dictIterator *iter = dictGetIterator(open_slots);\n        dictEntry *entry;\n        sds errstr = sdsnew(\"[WARNING] The following slots are open: \");\n        i = 0;\n        while ((entry = dictNext(iter)) != NULL) {\n            sds slot = (sds) dictGetKey(entry);\n            char *fmt = (i++ > 0 ? \",%S\" : \"%S\");\n            errstr = sdscatfmt(errstr, fmt, slot);\n        }\n        clusterManagerLogErr(\"%s.\\n\", (char *) errstr);\n        sdsfree(errstr);\n        if (do_fix) {\n            /* Fix open slots. */\n            dictReleaseIterator(iter);\n            iter = dictGetIterator(open_slots);\n            while ((entry = dictNext(iter)) != NULL) {\n                sds slot = (sds) dictGetKey(entry);\n                result = clusterManagerFixOpenSlot(atoi(slot));\n                if (!result) break;\n            }\n        }\n        dictReleaseIterator(iter);\n        dictRelease(open_slots);\n    }\n    clusterManagerLogInfo(\">>> Check slots coverage...\\n\");\n    char slots[CLUSTER_MANAGER_SLOTS];\n    memset(slots, 0, CLUSTER_MANAGER_SLOTS);\n    int coverage = clusterManagerGetCoveredSlots(slots);\n    if (coverage == CLUSTER_MANAGER_SLOTS) {\n        clusterManagerLogOk(\"[OK] All %d slots covered.\\n\",\n                            CLUSTER_MANAGER_SLOTS);\n    } else {\n        sds err = sdsempty();\n        err = sdscatprintf(err, \"[ERR] Not all %d slots are \"\n                                \"covered by nodes.\\n\",\n                                CLUSTER_MANAGER_SLOTS);\n        clusterManagerOnError(err);\n        result = 0;\n        if (do_fix/* && result*/) {\n            dictType dtype = clusterManagerDictType;\n            dtype.keyDestructor = dictSdsDestructor;\n            dtype.valDestructor = dictListDestructor;\n            clusterManagerUncoveredSlots = dictCreate(&dtype, NULL);\n            int fixed = clusterManagerFixSlotsCoverage(slots);\n            if (fixed > 0) result = 1;\n        }\n    }\n    int search_multiple_owners = config.cluster_manager_command.flags &\n                                 CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;\n    if (search_multiple_owners) {\n        /* Check whether there are multiple owners, even when slots are\n         * fully covered and there are no open slots. */\n        clusterManagerLogInfo(\">>> Check for multiple slot owners...\\n\");\n        int slot = 0, slots_with_multiple_owners = 0;\n        for (; slot < CLUSTER_MANAGER_SLOTS; slot++) {\n            listIter li;\n            listNode *ln;\n            listRewind(cluster_manager.nodes, &li);\n            list *owners = listCreate();\n            while ((ln = listNext(&li)) != NULL) {\n                clusterManagerNode *n = ln->value;\n                if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n                if (n->slots[slot]) listAddNodeTail(owners, n);\n                else {\n                    /* Nodes having keys for the slot will be considered\n                     * owners too. */\n                    int count = clusterManagerCountKeysInSlot(n, slot);\n                    if (count > 0) listAddNodeTail(owners, n);\n                }\n            }\n            if (listLength(owners) > 1) {\n                result = 0;\n                clusterManagerLogErr(\"[WARNING] Slot %d has %d owners:\\n\",\n                                     slot, listLength(owners));\n                listRewind(owners, &li);\n                while ((ln = listNext(&li)) != NULL) {\n                    clusterManagerNode *n = ln->value;\n                    clusterManagerLogErr(\"    %s:%d\\n\", n->ip, n->port);\n                }\n                slots_with_multiple_owners++;\n                if (do_fix) {\n                    result = clusterManagerFixMultipleSlotOwners(slot, owners);\n                    if (!result) {\n                        clusterManagerLogErr(\"Failed to fix multiple owners \"\n                                             \"for slot %d\\n\", slot);\n                        listRelease(owners);\n                        break;\n                    } else slots_with_multiple_owners--;\n                }\n            }\n            listRelease(owners);\n        }\n        if (slots_with_multiple_owners == 0)\n            clusterManagerLogOk(\"[OK] No multiple owners found.\\n\");\n    }\n    return result;\n}\n\nstatic clusterManagerNode *clusterNodeForResharding(char *id,\n                                                    clusterManagerNode *target,\n                                                    int *raise_err)\n{\n    clusterManagerNode *node = NULL;\n    const char *invalid_node_msg = \"*** The specified node (%s) is not known \"\n                                   \"or not a master, please retry.\\n\";\n    node = clusterManagerNodeByName(id);\n    *raise_err = 0;\n    if (!node || node->flags & CLUSTER_MANAGER_FLAG_SLAVE) {\n        clusterManagerLogErr(invalid_node_msg, id);\n        *raise_err = 1;\n        return NULL;\n    } else if (node != NULL && target != NULL) {\n        if (!strcmp(node->name, target->name)) {\n            clusterManagerLogErr( \"*** It is not possible to use \"\n                                  \"the target node as \"\n                                  \"source node.\\n\");\n            return NULL;\n        }\n    }\n    return node;\n}\n\nstatic list *clusterManagerComputeReshardTable(list *sources, int numslots) {\n    list *moved = listCreate();\n    int src_count = listLength(sources), i = 0, tot_slots = 0, j;\n    clusterManagerNode **sorted = zmalloc(src_count * sizeof(*sorted));\n    listIter li;\n    listNode *ln;\n    listRewind(sources, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *node = ln->value;\n        tot_slots += node->slots_count;\n        sorted[i++] = node;\n    }\n    qsort(sorted, src_count, sizeof(clusterManagerNode *),\n          clusterManagerSlotCountCompareDesc);\n    for (i = 0; i < src_count; i++) {\n        clusterManagerNode *node = sorted[i];\n        float n = ((float) numslots / tot_slots * node->slots_count);\n        if (i == 0) n = ceil(n);\n        else n = floor(n);\n        int max = (int) n, count = 0;\n        for (j = 0; j < CLUSTER_MANAGER_SLOTS; j++) {\n            int slot = node->slots[j];\n            if (!slot) continue;\n            if (count >= max || (int)listLength(moved) >= numslots) break;\n            clusterManagerReshardTableItem *item = zmalloc(sizeof(*item));\n            item->source = node;\n            item->slot = j;\n            listAddNodeTail(moved, item);\n            count++;\n        }\n    }\n    zfree(sorted);\n    return moved;\n}\n\nstatic void clusterManagerShowReshardTable(list *table) {\n    listIter li;\n    listNode *ln;\n    listRewind(table, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerReshardTableItem *item = ln->value;\n        clusterManagerNode *n = item->source;\n        printf(\"    Moving slot %d from %s\\n\", item->slot, (char *) n->name);\n    }\n}\n\nstatic void clusterManagerReleaseReshardTable(list *table) {\n    if (table != NULL) {\n        listIter li;\n        listNode *ln;\n        listRewind(table, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerReshardTableItem *item = ln->value;\n            zfree(item);\n        }\n        listRelease(table);\n    }\n}\n\nstatic void clusterManagerLog(int level, const char* fmt, ...) {\n    int use_colors =\n        (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COLOR);\n    if (use_colors) {\n        printf(\"\\033[\");\n        switch (level) {\n        case CLUSTER_MANAGER_LOG_LVL_INFO: printf(LOG_COLOR_BOLD); break;\n        case CLUSTER_MANAGER_LOG_LVL_WARN: printf(LOG_COLOR_YELLOW); break;\n        case CLUSTER_MANAGER_LOG_LVL_ERR: printf(LOG_COLOR_RED); break;\n        case CLUSTER_MANAGER_LOG_LVL_SUCCESS: printf(LOG_COLOR_GREEN); break;\n        default: printf(LOG_COLOR_RESET); break;\n        }\n    }\n    va_list ap;\n    va_start(ap, fmt);\n    vprintf(fmt, ap);\n    va_end(ap);\n    if (use_colors) printf(\"\\033[\" LOG_COLOR_RESET);\n}\n\nstatic void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,\n                                        int alloc_len)\n{\n    array->nodes = zcalloc(alloc_len * sizeof(clusterManagerNode*));\n    array->alloc = array->nodes;\n    array->len = alloc_len;\n    array->count = 0;\n}\n\n/* Reset array->nodes to the original array allocation and re-count non-NULL\n * nodes. */\nstatic void clusterManagerNodeArrayReset(clusterManagerNodeArray *array) {\n    if (array->nodes > array->alloc) {\n        array->len = array->nodes - array->alloc;\n        array->nodes = array->alloc;\n        array->count = 0;\n        int i = 0;\n        for(; i < array->len; i++) {\n            if (array->nodes[i] != NULL) array->count++;\n        }\n    }\n}\n\n/* Shift array->nodes and store the shifted node into 'nodeptr'. */\nstatic void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,\n                                         clusterManagerNode **nodeptr)\n{\n    assert(array->nodes < (array->nodes + array->len));\n    /* If the first node to be shifted is not NULL, decrement count. */\n    if (*array->nodes != NULL) array->count--;\n    /* Store the first node to be shifted into 'nodeptr'. */\n    *nodeptr = *array->nodes;\n    /* Shift the nodes array and decrement length. */\n    array->nodes++;\n    array->len--;\n}\n\nstatic void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,\n                                       clusterManagerNode *node)\n{\n    assert(array->nodes < (array->nodes + array->len));\n    assert(node != NULL);\n    assert(array->count < array->len);\n    array->nodes[array->count++] = node;\n}\n\nstatic void clusterManagerPrintNotEmptyNodeError(clusterManagerNode *node,\n                                                 char *err)\n{\n    char *msg;\n    if (err) msg = err;\n    else {\n        msg = \"is not empty. Either the node already knows other \"\n              \"nodes (check with CLUSTER NODES) or contains some \"\n              \"key in database 0.\";\n    }\n    clusterManagerLogErr(\"[ERR] Node %s:%d %s\\n\", node->ip, node->port, msg);\n}\n\nstatic void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,\n                                                   char *err)\n{\n    char *msg = (err ? err : \"is not configured as a cluster node.\");\n    clusterManagerLogErr(\"[ERR] Node %s:%d %s\\n\", node->ip, node->port, msg);\n}\n\n/* Execute redis-cli in Cluster Manager mode */\nstatic void clusterManagerMode(clusterManagerCommandProc *proc) {\n    int argc = config.cluster_manager_command.argc;\n    char **argv = config.cluster_manager_command.argv;\n    cluster_manager.nodes = NULL;\n    if (!proc(argc, argv)) goto cluster_manager_err;\n    freeClusterManager();\n    exit(0);\ncluster_manager_err:\n    freeClusterManager();\n    sdsfree(config.hostip);\n    sdsfree(config.mb_delim);\n    exit(1);\n}\n\n/* Cluster Manager Commands */\n\nstatic int clusterManagerCommandCreate(int argc, char **argv) {\n    int i, j, success = 1;\n    cluster_manager.nodes = listCreate();\n    for (i = 0; i < argc; i++) {\n        char *addr = argv[i];\n        char *c = strrchr(addr, '@');\n        if (c != NULL) *c = '\\0';\n        c = strrchr(addr, ':');\n        if (c == NULL) {\n            fprintf(stderr, \"Invalid address format: %s\\n\", addr);\n            return 0;\n        }\n        *c = '\\0';\n        char *ip = addr;\n        int port = atoi(++c);\n        clusterManagerNode *node = clusterManagerNewNode(ip, port);\n        if (!clusterManagerNodeConnect(node)) {\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        char *err = NULL;\n        if (!clusterManagerNodeIsCluster(node, &err)) {\n            clusterManagerPrintNotClusterNodeError(node, err);\n            if (err) zfree(err);\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        err = NULL;\n        if (!clusterManagerNodeLoadInfo(node, 0, &err)) {\n            if (err) {\n                CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                zfree(err);\n            }\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        err = NULL;\n        if (!clusterManagerNodeIsEmpty(node, &err)) {\n            clusterManagerPrintNotEmptyNodeError(node, err);\n            if (err) zfree(err);\n            freeClusterManagerNode(node);\n            return 0;\n        }\n        listAddNodeTail(cluster_manager.nodes, node);\n    }\n    int node_len = cluster_manager.nodes->len;\n    int replicas = config.cluster_manager_command.replicas;\n    int masters_count = CLUSTER_MANAGER_MASTERS_COUNT(node_len, replicas);\n    if (masters_count < 3) {\n        clusterManagerLogErr(\n            \"*** ERROR: Invalid configuration for cluster creation.\\n\"\n            \"*** Redis Cluster requires at least 3 master nodes.\\n\"\n            \"*** This is not possible with %d nodes and %d replicas per node.\",\n            node_len, replicas);\n        clusterManagerLogErr(\"\\n*** At least %d nodes are required.\\n\",\n                             3 * (replicas + 1));\n        return 0;\n    }\n    clusterManagerLogInfo(\">>> Performing hash slots allocation \"\n                          \"on %d nodes...\\n\", node_len);\n    int interleaved_len = 0, ip_count = 0;\n    clusterManagerNode **interleaved = zcalloc(node_len*sizeof(**interleaved));\n    char **ips = zcalloc(node_len * sizeof(char*));\n    clusterManagerNodeArray *ip_nodes = zcalloc(node_len * sizeof(*ip_nodes));\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        int found = 0;\n        for (i = 0; i < ip_count; i++) {\n            char *ip = ips[i];\n            if (!strcmp(ip, n->ip)) {\n                found = 1;\n                break;\n            }\n        }\n        if (!found) {\n            ips[ip_count++] = n->ip;\n        }\n        clusterManagerNodeArray *node_array = &(ip_nodes[i]);\n        if (node_array->nodes == NULL)\n            clusterManagerNodeArrayInit(node_array, node_len);\n        clusterManagerNodeArrayAdd(node_array, n);\n    }\n    while (interleaved_len < node_len) {\n        for (i = 0; i < ip_count; i++) {\n            clusterManagerNodeArray *node_array = &(ip_nodes[i]);\n            if (node_array->count > 0) {\n                clusterManagerNode *n = NULL;\n                clusterManagerNodeArrayShift(node_array, &n);\n                interleaved[interleaved_len++] = n;\n            }\n        }\n    }\n    clusterManagerNode **masters = interleaved;\n    interleaved += masters_count;\n    interleaved_len -= masters_count;\n    float slots_per_node = CLUSTER_MANAGER_SLOTS / (float) masters_count;\n    long first = 0;\n    float cursor = 0.0f;\n    for (i = 0; i < masters_count; i++) {\n        clusterManagerNode *master = masters[i];\n        long last = lround(cursor + slots_per_node - 1);\n        if (last > CLUSTER_MANAGER_SLOTS || i == (masters_count - 1))\n            last = CLUSTER_MANAGER_SLOTS - 1;\n        if (last < first) last = first;\n        printf(\"Master[%d] -> Slots %lu - %lu\\n\", i, first, last);\n        master->slots_count = 0;\n        for (j = first; j <= last; j++) {\n            master->slots[j] = 1;\n            master->slots_count++;\n        }\n        master->dirty = 1;\n        first = last + 1;\n        cursor += slots_per_node;\n    }\n\n    /* Rotating the list sometimes helps to get better initial\n     * anti-affinity before the optimizer runs. */\n    clusterManagerNode *first_node = interleaved[0];\n    for (i = 0; i < (interleaved_len - 1); i++)\n        interleaved[i] = interleaved[i + 1];\n    interleaved[interleaved_len - 1] = first_node;\n    int assign_unused = 0, available_count = interleaved_len;\nassign_replicas:\n    for (i = 0; i < masters_count; i++) {\n        clusterManagerNode *master = masters[i];\n        int assigned_replicas = 0;\n        while (assigned_replicas < replicas) {\n            if (available_count == 0) break;\n            clusterManagerNode *found = NULL, *slave = NULL;\n            int firstNodeIdx = -1;\n            for (j = 0; j < interleaved_len; j++) {\n                clusterManagerNode *n = interleaved[j];\n                if (n == NULL) continue;\n                if (strcmp(n->ip, master->ip)) {\n                    found = n;\n                    interleaved[j] = NULL;\n                    break;\n                }\n                if (firstNodeIdx < 0) firstNodeIdx = j;\n            }\n            if (found) slave = found;\n            else if (firstNodeIdx >= 0) {\n                slave = interleaved[firstNodeIdx];\n                interleaved_len -= (interleaved - (interleaved + firstNodeIdx));\n                interleaved += (firstNodeIdx + 1);\n            }\n            if (slave != NULL) {\n                assigned_replicas++;\n                available_count--;\n                if (slave->replicate) sdsfree(slave->replicate);\n                slave->replicate = sdsnew(master->name);\n                slave->dirty = 1;\n            } else break;\n            printf(\"Adding replica %s:%d to %s:%d\\n\", slave->ip, slave->port,\n                   master->ip, master->port);\n            if (assign_unused) break;\n        }\n    }\n    if (!assign_unused && available_count > 0) {\n        assign_unused = 1;\n        printf(\"Adding extra replicas...\\n\");\n        goto assign_replicas;\n    }\n    for (i = 0; i < ip_count; i++) {\n        clusterManagerNodeArray *node_array = ip_nodes + i;\n        clusterManagerNodeArrayReset(node_array);\n    }\n    clusterManagerOptimizeAntiAffinity(ip_nodes, ip_count);\n    clusterManagerShowNodes();\n    if (confirmWithYes(\"Can I set the above configuration?\")) {\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            char *err = NULL;\n            int flushed = clusterManagerFlushNodeConfig(node, &err);\n            if (!flushed && node->dirty && !node->replicate) {\n                if (err != NULL) {\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                    zfree(err);\n                }\n                success = 0;\n                goto cleanup;\n            } else if (err != NULL) zfree(err);\n        }\n        clusterManagerLogInfo(\">>> Nodes configuration updated\\n\");\n        clusterManagerLogInfo(\">>> Assign a different config epoch to \"\n                              \"each node\\n\");\n        int config_epoch = 1;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            redisReply *reply = NULL;\n            reply = CLUSTER_MANAGER_COMMAND(node,\n                                            \"cluster set-config-epoch %d\",\n                                            config_epoch++);\n            if (reply != NULL) freeReplyObject(reply);\n        }\n        clusterManagerLogInfo(\">>> Sending CLUSTER MEET messages to join \"\n                              \"the cluster\\n\");\n        clusterManagerNode *first = NULL;\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            if (first == NULL) {\n                first = node;\n                continue;\n            }\n            redisReply *reply = NULL;\n            reply = CLUSTER_MANAGER_COMMAND(node, \"cluster meet %s %d\",\n                                            first->ip, first->port);\n            int is_err = 0;\n            if (reply != NULL) {\n                if ((is_err = reply->type == REDIS_REPLY_ERROR))\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, reply->str);\n                freeReplyObject(reply);\n            } else {\n                is_err = 1;\n                fprintf(stderr, \"Failed to send CLUSTER MEET command.\\n\");\n            }\n            if (is_err) {\n                success = 0;\n                goto cleanup;\n            }\n        }\n        /* Give one second for the join to start, in order to avoid that\n         * waiting for cluster join will find all the nodes agree about\n         * the config as they are still empty with unassigned slots. */\n        sleep(1);\n        clusterManagerWaitForClusterJoin();\n        /* Useful for the replicas */\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            if (!node->dirty) continue;\n            char *err = NULL;\n            int flushed = clusterManagerFlushNodeConfig(node, &err);\n            if (!flushed && !node->replicate) {\n                if (err != NULL) {\n                    CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);\n                    zfree(err);\n                }\n                success = 0;\n                goto cleanup;\n            }\n        }\n        // Reset Nodes\n        listRewind(cluster_manager.nodes, &li);\n        clusterManagerNode *first_node = NULL;\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *node = ln->value;\n            if (!first_node) first_node = node;\n            else freeClusterManagerNode(node);\n        }\n        listEmpty(cluster_manager.nodes);\n        if (!clusterManagerLoadInfoFromNode(first_node, 0)) {\n            success = 0;\n            goto cleanup;\n        }\n        clusterManagerCheckCluster(0);\n    }\ncleanup:\n    /* Free everything */\n    zfree(masters);\n    zfree(ips);\n    for (i = 0; i < node_len; i++) {\n        clusterManagerNodeArray *node_array = ip_nodes + i;\n        CLUSTER_MANAGER_NODE_ARRAY_FREE(node_array);\n    }\n    zfree(ip_nodes);\n    return success;\n}\n\nstatic int clusterManagerCommandAddNode(int argc, char **argv) {\n    int success = 1;\n    redisReply *reply = NULL;\n    char *ref_ip = NULL, *ip = NULL;\n    int ref_port = 0, port = 0;\n    if (!getClusterHostFromCmdArgs(argc - 1, argv + 1, &ref_ip, &ref_port))\n        goto invalid_args;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port))\n        goto invalid_args;\n    clusterManagerLogInfo(\">>> Adding node %s:%d to cluster %s:%d\\n\", ip, port,\n                          ref_ip, ref_port);\n    // Check the existing cluster\n    clusterManagerNode *refnode = clusterManagerNewNode(ref_ip, ref_port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    if (!clusterManagerCheckCluster(0)) return 0;\n\n    /* If --cluster-master-id was specified, try to resolve it now so that we\n     * abort before starting with the node configuration. */\n    clusterManagerNode *master_node = NULL;\n    if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVE) {\n        char *master_id = config.cluster_manager_command.master_id;\n        if (master_id != NULL) {\n            master_node = clusterManagerNodeByName(master_id);\n            if (master_node == NULL) {\n                clusterManagerLogErr(\"[ERR] No such master ID %s\\n\", master_id);\n                return 0;\n            }\n        } else {\n            master_node = clusterManagerNodeWithLeastReplicas();\n            assert(master_node != NULL);\n            printf(\"Automatically selected master %s:%d\\n\", master_node->ip,\n                   master_node->port);\n        }\n    }\n\n    // Add the new node\n    clusterManagerNode *new_node = clusterManagerNewNode(ip, port);\n    int added = 0;\n    if (!clusterManagerNodeConnect(new_node)) {\n        clusterManagerLogErr(\"[ERR] Sorry, can't connect to node %s:%d\\n\",\n                             ip, port);\n        success = 0;\n        goto cleanup;\n    }\n    char *err = NULL;\n    if (!(success = clusterManagerNodeIsCluster(new_node, &err))) {\n        clusterManagerPrintNotClusterNodeError(new_node, err);\n        if (err) zfree(err);\n        goto cleanup;\n    }\n    if (!clusterManagerNodeLoadInfo(new_node, 0, &err)) {\n        if (err) {\n            CLUSTER_MANAGER_PRINT_REPLY_ERROR(new_node, err);\n            zfree(err);\n        }\n        success = 0;\n        goto cleanup;\n    }\n    if (!(success = clusterManagerNodeIsEmpty(new_node, &err))) {\n        clusterManagerPrintNotEmptyNodeError(new_node, err);\n        if (err) zfree(err);\n        goto cleanup;\n    }\n    clusterManagerNode *first = listFirst(cluster_manager.nodes)->value;\n    listAddNodeTail(cluster_manager.nodes, new_node);\n    added = 1;\n\n    // Send CLUSTER MEET command to the new node\n    clusterManagerLogInfo(\">>> Send CLUSTER MEET to node %s:%d to make it \"\n                          \"join the cluster.\\n\", ip, port);\n    reply = CLUSTER_MANAGER_COMMAND(new_node, \"CLUSTER MEET %s %d\",\n                                    first->ip, first->port);\n    if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))\n        goto cleanup;\n\n    /* Additional configuration is needed if the node is added as a slave. */\n    if (master_node) {\n        sleep(1);\n        clusterManagerWaitForClusterJoin();\n        clusterManagerLogInfo(\">>> Configure node as replica of %s:%d.\\n\",\n                              master_node->ip, master_node->port);\n        freeReplyObject(reply);\n        reply = CLUSTER_MANAGER_COMMAND(new_node, \"CLUSTER REPLICATE %s\",\n                                        master_node->name);\n        if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))\n            goto cleanup;\n    }\n    clusterManagerLogOk(\"[OK] New node added correctly.\\n\");\ncleanup:\n    if (!added && new_node) freeClusterManagerNode(new_node);\n    if (reply) freeReplyObject(reply);\n    return success;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandDeleteNode(int argc, char **argv) {\n    UNUSED(argc);\n    int success = 1;\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    char *node_id = argv[1];\n    clusterManagerLogInfo(\">>> Removing node %s from cluster %s:%d\\n\",\n                          node_id, ip, port);\n    clusterManagerNode *ref_node = clusterManagerNewNode(ip, port);\n    clusterManagerNode *node = NULL;\n\n    // Load cluster information\n    if (!clusterManagerLoadInfoFromNode(ref_node, 0)) return 0;\n\n    // Check if the node exists and is not empty\n    node = clusterManagerNodeByName(node_id);\n    if (node == NULL) {\n        clusterManagerLogErr(\"[ERR] No such node ID %s\\n\", node_id);\n        return 0;\n    }\n    if (node->slots_count != 0) {\n        clusterManagerLogErr(\"[ERR] Node %s:%d is not empty! Reshard data \"\n                             \"away and try again.\\n\", node->ip, node->port);\n        return 0;\n    }\n\n    // Send CLUSTER FORGET to all the nodes but the node to remove\n    clusterManagerLogInfo(\">>> Sending CLUSTER FORGET messages to the \"\n                          \"cluster...\\n\");\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n == node) continue;\n        if (n->replicate && !strcasecmp(n->replicate, node_id)) {\n            // Reconfigure the slave to replicate with some other node\n            clusterManagerNode *master = clusterManagerNodeWithLeastReplicas();\n            assert(master != NULL);\n            clusterManagerLogInfo(\">>> %s:%d as replica of %s:%d\\n\",\n                                  n->ip, n->port, master->ip, master->port);\n            redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER REPLICATE %s\",\n                                                    master->name);\n            success = clusterManagerCheckRedisReply(n, r, NULL);\n            if (r) freeReplyObject(r);\n            if (!success) return 0;\n        }\n        redisReply *r = CLUSTER_MANAGER_COMMAND(n, \"CLUSTER FORGET %s\",\n                                                node_id);\n        success = clusterManagerCheckRedisReply(n, r, NULL);\n        if (r) freeReplyObject(r);\n        if (!success) return 0;\n    }\n\n    /* Finally send CLUSTER RESET to the node. */\n    clusterManagerLogInfo(\">>> Sending CLUSTER RESET SOFT to the \"\n                          \"deleted node.\\n\");\n    redisReply *r = redisCommand(node->context, \"CLUSTER RESET %s\", \"SOFT\");\n    success = clusterManagerCheckRedisReply(node, r, NULL);\n    if (r) freeReplyObject(r);\n    return success;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandInfo(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    clusterManagerShowClusterInfo();\n    return 1;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandCheck(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    clusterManagerShowClusterInfo();\n    return clusterManagerCheckCluster(0);\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandFix(int argc, char **argv) {\n    config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_FIX;\n    return clusterManagerCommandCheck(argc, argv);\n}\n\nstatic int clusterManagerCommandReshard(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    clusterManagerCheckCluster(0);\n    if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {\n        fflush(stdout);\n        fprintf(stderr,\n                \"*** Please fix your cluster problems before resharding\\n\");\n        return 0;\n    }\n    int slots = config.cluster_manager_command.slots;\n    if (!slots) {\n        while (slots <= 0 || slots > CLUSTER_MANAGER_SLOTS) {\n            printf(\"How many slots do you want to move (from 1 to %d)? \",\n                   CLUSTER_MANAGER_SLOTS);\n            fflush(stdout);\n            char buf[6];\n            int nread = read(fileno(stdin),buf,6);\n            if (nread <= 0) continue;\n            int last_idx = nread - 1;\n            if (buf[last_idx] != '\\n') {\n                int ch;\n                while ((ch = getchar()) != '\\n' && ch != EOF) {}\n            }\n            buf[last_idx] = '\\0';\n            slots = atoi(buf);\n        }\n    }\n    char buf[255];\n    char *to = config.cluster_manager_command.to,\n         *from = config.cluster_manager_command.from;\n    while (to == NULL) {\n        printf(\"What is the receiving node ID? \");\n        fflush(stdout);\n        int nread = read(fileno(stdin),buf,255);\n        if (nread <= 0) continue;\n        int last_idx = nread - 1;\n        if (buf[last_idx] != '\\n') {\n            int ch;\n            while ((ch = getchar()) != '\\n' && ch != EOF) {}\n        }\n        buf[last_idx] = '\\0';\n        if (strlen(buf) > 0) to = buf;\n    }\n    int raise_err = 0;\n    clusterManagerNode *target = clusterNodeForResharding(to, NULL, &raise_err);\n    if (target == NULL) return 0;\n    list *sources = listCreate();\n    list *table = NULL;\n    int all = 0, result = 1;\n    if (from == NULL) {\n        printf(\"Please enter all the source node IDs.\\n\");\n        printf(\"  Type 'all' to use all the nodes as source nodes for \"\n               \"the hash slots.\\n\");\n        printf(\"  Type 'done' once you entered all the source nodes IDs.\\n\");\n        while (1) {\n            printf(\"Source node #%lu: \", listLength(sources) + 1);\n            fflush(stdout);\n            int nread = read(fileno(stdin),buf,255);\n            if (nread <= 0) continue;\n            int last_idx = nread - 1;\n            if (buf[last_idx] != '\\n') {\n                int ch;\n                while ((ch = getchar()) != '\\n' && ch != EOF) {}\n            }\n            buf[last_idx] = '\\0';\n            if (!strcmp(buf, \"done\")) break;\n            else if (!strcmp(buf, \"all\")) {\n                all = 1;\n                break;\n            } else {\n                clusterManagerNode *src =\n                    clusterNodeForResharding(buf, target, &raise_err);\n                if (src != NULL) listAddNodeTail(sources, src);\n                else if (raise_err) {\n                    result = 0;\n                    goto cleanup;\n                }\n            }\n        }\n    } else {\n        char *p;\n        while((p = strchr(from, ',')) != NULL) {\n            *p = '\\0';\n            if (!strcmp(from, \"all\")) {\n                all = 1;\n                break;\n            } else {\n                clusterManagerNode *src =\n                    clusterNodeForResharding(from, target, &raise_err);\n                if (src != NULL) listAddNodeTail(sources, src);\n                else if (raise_err) {\n                    result = 0;\n                    goto cleanup;\n                }\n            }\n            from = p + 1;\n        }\n        /* Check if there's still another source to process. */\n        if (!all && strlen(from) > 0) {\n            if (!strcmp(from, \"all\")) all = 1;\n            if (!all) {\n                clusterManagerNode *src =\n                    clusterNodeForResharding(from, target, &raise_err);\n                if (src != NULL) listAddNodeTail(sources, src);\n                else if (raise_err) {\n                    result = 0;\n                    goto cleanup;\n                }\n            }\n        }\n    }\n    listIter li;\n    listNode *ln;\n    if (all) {\n        listEmpty(sources);\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n                continue;\n            if (!sdscmp(n->name, target->name)) continue;\n            listAddNodeTail(sources, n);\n        }\n    }\n    if (listLength(sources) == 0) {\n        fprintf(stderr, \"*** No source nodes given, operation aborted.\\n\");\n        result = 0;\n        goto cleanup;\n    }\n    printf(\"\\nReady to move %d slots.\\n\", slots);\n    printf(\"  Source nodes:\\n\");\n    listRewind(sources, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *src = ln->value;\n        sds info = clusterManagerNodeInfo(src, 4);\n        printf(\"%s\\n\", info);\n        sdsfree(info);\n    }\n    printf(\"  Destination node:\\n\");\n    sds info = clusterManagerNodeInfo(target, 4);\n    printf(\"%s\\n\", info);\n    sdsfree(info);\n    table = clusterManagerComputeReshardTable(sources, slots);\n    printf(\"  Resharding plan:\\n\");\n    clusterManagerShowReshardTable(table);\n    if (!(config.cluster_manager_command.flags &\n          CLUSTER_MANAGER_CMD_FLAG_YES))\n    {\n        printf(\"Do you want to proceed with the proposed \"\n               \"reshard plan (yes/no)? \");\n        fflush(stdout);\n        char buf[4];\n        int nread = read(fileno(stdin),buf,4);\n        buf[3] = '\\0';\n        if (nread <= 0 || strcmp(\"yes\", buf) != 0) {\n            result = 0;\n            goto cleanup;\n        }\n    }\n    int opts = CLUSTER_MANAGER_OPT_VERBOSE;\n    listRewind(table, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerReshardTableItem *item = ln->value;\n        char *err = NULL;\n        result = clusterManagerMoveSlot(item->source, target, item->slot,\n                                        opts, &err);\n        if (!result) {\n            if (err != NULL) {\n                //clusterManagerLogErr(\"\\n%s\\n\", err);\n                zfree(err);\n            }\n            goto cleanup;\n        }\n    }\ncleanup:\n    listRelease(sources);\n    clusterManagerReleaseReshardTable(table);\n    return result;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandRebalance(int argc, char **argv) {\n    int port = 0;\n    char *ip = NULL;\n    clusterManagerNode **weightedNodes = NULL;\n    list *involved = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    int result = 1, i;\n    if (config.cluster_manager_command.weight != NULL) {\n        for (i = 0; i < config.cluster_manager_command.weight_argc; i++) {\n            char *name = config.cluster_manager_command.weight[i];\n            char *p = strchr(name, '=');\n            if (p == NULL) {\n                result = 0;\n                goto cleanup;\n            }\n            *p = '\\0';\n            float w = atof(++p);\n            clusterManagerNode *n = clusterManagerNodeByAbbreviatedName(name);\n            if (n == NULL) {\n                clusterManagerLogErr(\"*** No such master node %s\\n\", name);\n                result = 0;\n                goto cleanup;\n            }\n            n->weight = w;\n        }\n    }\n    float total_weight = 0;\n    int nodes_involved = 0;\n    int use_empty = config.cluster_manager_command.flags &\n                    CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;\n    involved = listCreate();\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    /* Compute the total cluster weight. */\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)\n            continue;\n        if (!use_empty && n->slots_count == 0) {\n            n->weight = 0;\n            continue;\n        }\n        total_weight += n->weight;\n        nodes_involved++;\n        listAddNodeTail(involved, n);\n    }\n    weightedNodes = zmalloc(nodes_involved * sizeof(clusterManagerNode *));\n    if (weightedNodes == NULL) goto cleanup;\n    /* Check cluster, only proceed if it looks sane. */\n    clusterManagerCheckCluster(1);\n    if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {\n        clusterManagerLogErr(\"*** Please fix your cluster problems \"\n                             \"before rebalancing\\n\");\n        result = 0;\n        goto cleanup;\n    }\n    /* Calculate the slots balance for each node. It's the number of\n     * slots the node should lose (if positive) or gain (if negative)\n     * in order to be balanced. */\n    int threshold_reached = 0, total_balance = 0;\n    float threshold = config.cluster_manager_command.threshold;\n    i = 0;\n    listRewind(involved, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        weightedNodes[i++] = n;\n        int expected = (int) (((float)CLUSTER_MANAGER_SLOTS / total_weight) *\n                        n->weight);\n        n->balance = n->slots_count - expected;\n        total_balance += n->balance;\n        /* Compute the percentage of difference between the\n         * expected number of slots and the real one, to see\n         * if it's over the threshold specified by the user. */\n        int over_threshold = 0;\n        if (threshold > 0) {\n            if (n->slots_count > 0) {\n                float err_perc = fabs((100-(100.0*expected/n->slots_count)));\n                if (err_perc > threshold) over_threshold = 1;\n            } else if (expected > 1) {\n                over_threshold = 1;\n            }\n        }\n        if (over_threshold) threshold_reached = 1;\n    }\n    if (!threshold_reached) {\n        clusterManagerLogWarn(\"*** No rebalancing needed! \"\n                             \"All nodes are within the %.2f%% threshold.\\n\",\n                             config.cluster_manager_command.threshold);\n        goto cleanup;\n    }\n    /* Because of rounding, it is possible that the balance of all nodes\n     * summed does not give 0. Make sure that nodes that have to provide\n     * slots are always matched by nodes receiving slots. */\n    while (total_balance > 0) {\n        listRewind(involved, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->balance <= 0 && total_balance > 0) {\n                n->balance--;\n                total_balance--;\n            }\n        }\n    }\n    /* Sort nodes by their slots balance. */\n    qsort(weightedNodes, nodes_involved, sizeof(clusterManagerNode *),\n          clusterManagerCompareNodeBalance);\n    clusterManagerLogInfo(\">>> Rebalancing across %d nodes. \"\n                          \"Total weight = %.2f\\n\",\n                          nodes_involved, total_weight);\n    if (config.verbose) {\n        for (i = 0; i < nodes_involved; i++) {\n            clusterManagerNode *n = weightedNodes[i];\n            printf(\"%s:%d balance is %d slots\\n\", n->ip, n->port, n->balance);\n        }\n    }\n    /* Now we have at the start of the 'sn' array nodes that should get\n     * slots, at the end nodes that must give slots.\n     * We take two indexes, one at the start, and one at the end,\n     * incrementing or decrementing the indexes accordingly til we\n     * find nodes that need to get/provide slots. */\n    int dst_idx = 0;\n    int src_idx = nodes_involved - 1;\n    int simulate = config.cluster_manager_command.flags &\n                   CLUSTER_MANAGER_CMD_FLAG_SIMULATE;\n    while (dst_idx < src_idx) {\n        clusterManagerNode *dst = weightedNodes[dst_idx];\n        clusterManagerNode *src = weightedNodes[src_idx];\n        int db = abs(dst->balance);\n        int sb = abs(src->balance);\n        int numslots = (db < sb ? db : sb);\n        if (numslots > 0) {\n            printf(\"Moving %d slots from %s:%d to %s:%d\\n\", numslots,\n                                                            src->ip,\n                                                            src->port,\n                                                            dst->ip,\n                                                            dst->port);\n            /* Actually move the slots. */\n            list *lsrc = listCreate(), *table = NULL;\n            listAddNodeTail(lsrc, src);\n            table = clusterManagerComputeReshardTable(lsrc, numslots);\n            listRelease(lsrc);\n            int table_len = (int) listLength(table);\n            if (!table || table_len != numslots) {\n                clusterManagerLogErr(\"*** Assertion failed: Reshard table \"\n                                     \"!= number of slots\");\n                result = 0;\n                goto end_move;\n            }\n            if (simulate) {\n                for (i = 0; i < table_len; i++) printf(\"#\");\n            } else {\n                int opts = CLUSTER_MANAGER_OPT_QUIET |\n                           CLUSTER_MANAGER_OPT_UPDATE;\n                listRewind(table, &li);\n                while ((ln = listNext(&li)) != NULL) {\n                    clusterManagerReshardTableItem *item = ln->value;\n                    result = clusterManagerMoveSlot(item->source,\n                                                    dst,\n                                                    item->slot,\n                                                    opts, NULL);\n                    if (!result) goto end_move;\n                    printf(\"#\");\n                    fflush(stdout);\n                }\n\n            }\n            printf(\"\\n\");\nend_move:\n            clusterManagerReleaseReshardTable(table);\n            if (!result) goto cleanup;\n        }\n        /* Update nodes balance. */\n        dst->balance += numslots;\n        src->balance -= numslots;\n        if (dst->balance == 0) dst_idx++;\n        if (src->balance == 0) src_idx --;\n    }\ncleanup:\n    if (involved != NULL) listRelease(involved);\n    if (weightedNodes != NULL) zfree(weightedNodes);\n    return result;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandSetTimeout(int argc, char **argv) {\n    UNUSED(argc);\n    int port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    int timeout = atoi(argv[1]);\n    if (timeout < 100) {\n        fprintf(stderr, \"Setting a node timeout of less than 100 \"\n                \"milliseconds is a bad idea.\\n\");\n        return 0;\n    }\n    // Load cluster information\n    clusterManagerNode *node = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;\n    int ok_count = 0, err_count = 0;\n\n    clusterManagerLogInfo(\">>> Reconfiguring node timeout in every \"\n                          \"cluster node...\\n\");\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        char *err = NULL;\n        redisReply *reply = CLUSTER_MANAGER_COMMAND(n, \"CONFIG %s %s %d\",\n                                                    \"SET\",\n                                                    \"cluster-node-timeout\",\n                                                    timeout);\n        if (reply == NULL) goto reply_err;\n        int ok = clusterManagerCheckRedisReply(n, reply, &err);\n        freeReplyObject(reply);\n        if (!ok) goto reply_err;\n        reply = CLUSTER_MANAGER_COMMAND(n, \"CONFIG %s\", \"REWRITE\");\n        if (reply == NULL) goto reply_err;\n        ok = clusterManagerCheckRedisReply(n, reply, &err);\n        freeReplyObject(reply);\n        if (!ok) goto reply_err;\n        clusterManagerLogWarn(\"*** New timeout set for %s:%d\\n\", n->ip,\n                              n->port);\n        ok_count++;\n        continue;\nreply_err:;\n        int need_free = 0;\n        if (err == NULL) err = \"\";\n        else need_free = 1;\n        clusterManagerLogErr(\"ERR setting node-timeot for %s:%d: %s\\n\", n->ip,\n                             n->port, err);\n        if (need_free) zfree(err);\n        err_count++;\n    }\n    clusterManagerLogInfo(\">>> New node timeout set. %d OK, %d ERR.\\n\",\n                          ok_count, err_count);\n    return 1;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandImport(int argc, char **argv) {\n    int success = 1;\n    int port = 0, src_port = 0;\n    char *ip = NULL, *src_ip = NULL;\n    char *invalid_args_msg = NULL;\n    if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) {\n        invalid_args_msg = CLUSTER_MANAGER_INVALID_HOST_ARG;\n        goto invalid_args;\n    }\n    if (config.cluster_manager_command.from == NULL) {\n        invalid_args_msg = \"[ERR] Option '--cluster-from' is required for \"\n                           \"subcommand 'import'.\\n\";\n        goto invalid_args;\n    }\n    char *src_host[] = {config.cluster_manager_command.from};\n    if (!getClusterHostFromCmdArgs(1, src_host, &src_ip, &src_port)) {\n        invalid_args_msg = \"[ERR] Invalid --cluster-from host. You need to \"\n                           \"pass a valid address (ie. 120.0.0.1:7000).\\n\";\n        goto invalid_args;\n    }\n    clusterManagerLogInfo(\">>> Importing data from %s:%d to cluster %s:%d\\n\",\n                          src_ip, src_port, ip, port);\n\n    clusterManagerNode *refnode = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    if (!clusterManagerCheckCluster(0)) return 0;\n    char *reply_err = NULL;\n    redisReply *src_reply = NULL;\n    // Connect to the source node.\n    redisContext *src_ctx = redisConnect(src_ip, src_port);\n    if (src_ctx->err) {\n        success = 0;\n        fprintf(stderr,\"Could not connect to Redis at %s:%d: %s.\\n\", src_ip,\n                src_port, src_ctx->errstr);\n        goto cleanup;\n    }\n    src_reply = reconnectingRedisCommand(src_ctx, \"INFO\");\n    if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {\n        if (src_reply && src_reply->str) reply_err = src_reply->str;\n        success = 0;\n        goto cleanup;\n    }\n    if (getLongInfoField(src_reply->str, \"cluster_enabled\")) {\n        clusterManagerLogErr(\"[ERR] The source node should not be a \"\n                             \"cluster node.\\n\");\n        success = 0;\n        goto cleanup;\n    }\n    freeReplyObject(src_reply);\n    src_reply = reconnectingRedisCommand(src_ctx, \"DBSIZE\");\n    if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {\n        if (src_reply && src_reply->str) reply_err = src_reply->str;\n        success = 0;\n        goto cleanup;\n    }\n    int size = src_reply->integer, i;\n    clusterManagerLogWarn(\"*** Importing %d keys from DB 0\\n\", size);\n\n    // Build a slot -> node map\n    clusterManagerNode  *slots_map[CLUSTER_MANAGER_SLOTS];\n    memset(slots_map, 0, sizeof(slots_map));\n    listIter li;\n    listNode *ln;\n    for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {\n        listRewind(cluster_manager.nodes, &li);\n        while ((ln = listNext(&li)) != NULL) {\n            clusterManagerNode *n = ln->value;\n            if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;\n            if (n->slots_count == 0) continue;\n            if (n->slots[i]) {\n                slots_map[i] = n;\n                break;\n            }\n        }\n    }\n\n    char cmdfmt[50] = \"MIGRATE %s %d %s %d %d\";\n    if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COPY)\n        strcat(cmdfmt, \" %s\");\n    if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_REPLACE)\n        strcat(cmdfmt, \" %s\");\n\n    /* Use SCAN to iterate over the keys, migrating to the\n     * right node as needed. */\n    int cursor = -999, timeout = config.cluster_manager_command.timeout;\n    while (cursor != 0) {\n        if (cursor < 0) cursor = 0;\n        freeReplyObject(src_reply);\n        src_reply = reconnectingRedisCommand(src_ctx, \"SCAN %d COUNT %d\",\n                                             cursor, 1000);\n        if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {\n            if (src_reply && src_reply->str) reply_err = src_reply->str;\n            success = 0;\n            goto cleanup;\n        }\n        assert(src_reply->type == REDIS_REPLY_ARRAY);\n        assert(src_reply->elements >= 2);\n        assert(src_reply->element[1]->type == REDIS_REPLY_ARRAY);\n        if (src_reply->element[0]->type == REDIS_REPLY_STRING)\n            cursor = atoi(src_reply->element[0]->str);\n        else if (src_reply->element[0]->type == REDIS_REPLY_INTEGER)\n            cursor = src_reply->element[0]->integer;\n        int keycount = src_reply->element[1]->elements;\n        for (i = 0; i < keycount; i++) {\n            redisReply *kr = src_reply->element[1]->element[i];\n            assert(kr->type == REDIS_REPLY_STRING);\n            char *key = kr->str;\n            uint16_t slot = clusterManagerKeyHashSlot(key, kr->len);\n            clusterManagerNode *target = slots_map[slot];\n            printf(\"Migrating %s to %s:%d: \", key, target->ip, target->port);\n            redisReply *r = reconnectingRedisCommand(src_ctx, cmdfmt,\n                                                     target->ip, target->port,\n                                                     key, 0, timeout,\n                                                     \"COPY\", \"REPLACE\");\n            if (!r || r->type == REDIS_REPLY_ERROR) {\n                if (r && r->str) {\n                    clusterManagerLogErr(\"Source %s:%d replied with \"\n                                         \"error:\\n%s\\n\", src_ip, src_port,\n                                         r->str);\n                }\n                success = 0;\n            }\n            freeReplyObject(r);\n            if (!success) goto cleanup;\n            clusterManagerLogOk(\"OK\\n\");\n        }\n    }\ncleanup:\n    if (reply_err)\n        clusterManagerLogErr(\"Source %s:%d replied with error:\\n%s\\n\",\n                             src_ip, src_port, reply_err);\n    if (src_ctx) redisFree(src_ctx);\n    if (src_reply) freeReplyObject(src_reply);\n    return success;\ninvalid_args:\n    fprintf(stderr, \"%s\", invalid_args_msg);\n    return 0;\n}\n\nstatic int clusterManagerCommandCall(int argc, char **argv) {\n    int port = 0, i;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *refnode = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    argc--;\n    argv++;\n    size_t *argvlen = zmalloc(argc*sizeof(size_t));\n    clusterManagerLogInfo(\">>> Calling\");\n    for (i = 0; i < argc; i++) {\n        argvlen[i] = strlen(argv[i]);\n        printf(\" %s\", argv[i]);\n    }\n    printf(\"\\n\");\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        clusterManagerNode *n = ln->value;\n        if (!n->context && !clusterManagerNodeConnect(n)) continue;\n        redisReply *reply = NULL;\n        redisAppendCommandArgv(n->context, argc, (const char **) argv, argvlen);\n        int status = redisGetReply(n->context, (void **)(&reply));\n        if (status != REDIS_OK || reply == NULL )\n            printf(\"%s:%d: Failed!\\n\", n->ip, n->port);\n        else {\n            sds formatted_reply = cliFormatReplyRaw(reply);\n            printf(\"%s:%d: %s\\n\", n->ip, n->port, (char *) formatted_reply);\n            sdsfree(formatted_reply);\n        }\n        if (reply != NULL) freeReplyObject(reply);\n    }\n    zfree(argvlen);\n    return 1;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandBackup(int argc, char **argv) {\n    UNUSED(argc);\n    int success = 1, port = 0;\n    char *ip = NULL;\n    if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;\n    clusterManagerNode *refnode = clusterManagerNewNode(ip, port);\n    if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;\n    int no_issues = clusterManagerCheckCluster(0);\n    int cluster_errors_count = (no_issues ? 0 :\n                                listLength(cluster_manager.errors));\n    config.cluster_manager_command.backup_dir = argv[1];\n    /* TODO: check if backup_dir is a valid directory. */\n    sds json = sdsnew(\"[\\n\");\n    int first_node = 0;\n    listIter li;\n    listNode *ln;\n    listRewind(cluster_manager.nodes, &li);\n    while ((ln = listNext(&li)) != NULL) {\n        if (!first_node) first_node = 1;\n        else json = sdscat(json, \",\\n\");\n        clusterManagerNode *node = ln->value;\n        sds node_json = clusterManagerNodeGetJSON(node, cluster_errors_count);\n        json = sdscat(json, node_json);\n        sdsfree(node_json);\n        if (node->replicate)\n            continue;\n        clusterManagerLogInfo(\">>> Node %s:%d -> Saving RDB...\\n\",\n                              node->ip, node->port);\n        fflush(stdout);\n        getRDB(node);\n    }\n    json = sdscat(json, \"\\n]\");\n    sds jsonpath = sdsnew(config.cluster_manager_command.backup_dir);\n    if (jsonpath[sdslen(jsonpath) - 1] != '/')\n        jsonpath = sdscat(jsonpath, \"/\");\n    jsonpath = sdscat(jsonpath, \"nodes.json\");\n    fflush(stdout);\n    clusterManagerLogInfo(\"Saving cluster configuration to: %s\\n\", jsonpath);\n    FILE *out = fopen(jsonpath, \"w+\");\n    if (!out) {\n        clusterManagerLogErr(\"Could not save nodes to: %s\\n\", jsonpath);\n        success = 0;\n        goto cleanup;\n    }\n    fputs(json, out);\n    fclose(out);\ncleanup:\n    sdsfree(json);\n    sdsfree(jsonpath);\n    if (success) {\n        if (!no_issues) {\n            clusterManagerLogWarn(\"*** Cluster seems to have some problems, \"\n                                  \"please be aware of it if you're going \"\n                                  \"to restore this backup.\\n\");\n        }\n        clusterManagerLogOk(\"[OK] Backup created into: %s\\n\",\n                            config.cluster_manager_command.backup_dir);\n    } else clusterManagerLogOk(\"[ERR] Failed to back cluster!\\n\");\n    return success;\ninvalid_args:\n    fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);\n    return 0;\n}\n\nstatic int clusterManagerCommandHelp(int argc, char **argv) {\n    UNUSED(argc);\n    UNUSED(argv);\n    int commands_count = sizeof(clusterManagerCommands) /\n                         sizeof(clusterManagerCommandDef);\n    int i = 0, j;\n    fprintf(stderr, \"Cluster Manager Commands:\\n\");\n    int padding = 15;\n    for (; i < commands_count; i++) {\n        clusterManagerCommandDef *def = &(clusterManagerCommands[i]);\n        int namelen = strlen(def->name), padlen = padding - namelen;\n        fprintf(stderr, \"  %s\", def->name);\n        for (j = 0; j < padlen; j++) fprintf(stderr, \" \");\n        fprintf(stderr, \"%s\\n\", (def->args ? def->args : \"\"));\n        if (def->options != NULL) {\n            int optslen = strlen(def->options);\n            char *p = def->options, *eos = p + optslen;\n            char *comma = NULL;\n            while ((comma = strchr(p, ',')) != NULL) {\n                int deflen = (int)(comma - p);\n                char buf[255];\n                memcpy(buf, p, deflen);\n                buf[deflen] = '\\0';\n                for (j = 0; j < padding; j++) fprintf(stderr, \" \");\n                fprintf(stderr, \"  --cluster-%s\\n\", buf);\n                p = comma + 1;\n                if (p >= eos) break;\n            }\n            if (p < eos) {\n                for (j = 0; j < padding; j++) fprintf(stderr, \" \");\n                fprintf(stderr, \"  --cluster-%s\\n\", p);\n            }\n        }\n    }\n    fprintf(stderr, \"\\nFor check, fix, reshard, del-node, set-timeout you \"\n                    \"can specify the host and port of any working node in \"\n                    \"the cluster.\\n\\n\");\n    return 0;\n}\n\n/*------------------------------------------------------------------------------\n * Latency and latency history modes\n *--------------------------------------------------------------------------- */\n\nstatic void latencyModePrint(long long min, long long max, double avg, long long count) {\n    if (config.output == OUTPUT_STANDARD) {\n        printf(\"min: %lld, max: %lld, avg: %.2f (%lld samples)\",\n                min, max, avg, count);\n        fflush(stdout);\n    } else if (config.output == OUTPUT_CSV) {\n        printf(\"%lld,%lld,%.2f,%lld\\n\", min, max, avg, count);\n    } else if (config.output == OUTPUT_RAW) {\n        printf(\"%lld %lld %.2f %lld\\n\", min, max, avg, count);\n    }\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    /* Set a default for the interval in case of --latency option\n     * with --raw, --csv or when it is redirected to non tty. */\n    if (config.interval == 0) {\n        config.interval = 1000;\n    } else {\n        config.interval /= 1000; /* We need to convert to milliseconds. */\n    }\n\n    if (!context) exit(1);\n    while(1) {\n        start = mstime();\n        reply = reconnectingRedisCommand(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\n        if (config.output == OUTPUT_STANDARD) {\n            printf(\"\\x1b[0G\\x1b[2K\"); /* Clear the line. */\n            latencyModePrint(min,max,avg,count);\n        } else {\n            if (config.latency_history) {\n                latencyModePrint(min,max,avg,count);\n            } else if (mstime()-history_start > config.interval) {\n                latencyModePrint(min,max,avg,count);\n                exit(0);\n            }\n        }\n\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 * Latency distribution mode -- requires 256 colors xterm\n *--------------------------------------------------------------------------- */\n\n#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */\n\n/* Structure to store samples distribution. */\nstruct distsamples {\n    long long max;   /* Max latency to fit into this interval (usec). */\n    long long count; /* Number of samples in this interval. */\n    int character;   /* Associated character in visualization. */\n};\n\n/* Helper function for latencyDistMode(). Performs the spectrum visualization\n * of the collected samples targeting an xterm 256 terminal.\n *\n * Takes an array of distsamples structures, ordered from smaller to bigger\n * 'max' value. Last sample max must be 0, to mean that it olds all the\n * samples greater than the previous one, and is also the stop sentinel.\n *\n * \"tot' is the total number of samples in the different buckets, so it\n * is the SUM(samples[i].conut) for i to 0 up to the max sample.\n *\n * As a side effect the function sets all the buckets count to 0. */\nvoid showLatencyDistSamples(struct distsamples *samples, long long tot) {\n    int j;\n\n     /* We convert samples into a index inside the palette\n     * proportional to the percentage a given bucket represents.\n     * This way intensity of the different parts of the spectrum\n     * don't change relative to the number of requests, which avoids to\n     * pollute the visualization with non-latency related info. */\n    printf(\"\\033[38;5;0m\"); /* Set foreground color to black. */\n    for (j = 0; ; j++) {\n        int coloridx =\n            ceil((float) samples[j].count / tot * (spectrum_palette_size-1));\n        int color = spectrum_palette[coloridx];\n        printf(\"\\033[48;5;%dm%c\", (int)color, samples[j].character);\n        samples[j].count = 0;\n        if (samples[j].max == 0) break; /* Last sample. */\n    }\n    printf(\"\\033[0m\\n\");\n    fflush(stdout);\n}\n\n/* Show the legend: different buckets values and colors meaning, so\n * that the spectrum is more easily readable. */\nvoid showLatencyDistLegend(void) {\n    int j;\n\n    printf(\"---------------------------------------------\\n\");\n    printf(\". - * #          .01 .125 .25 .5 milliseconds\\n\");\n    printf(\"1,2,3,...,9      from 1 to 9     milliseconds\\n\");\n    printf(\"A,B,C,D,E        10,20,30,40,50  milliseconds\\n\");\n    printf(\"F,G,H,I,J        .1,.2,.3,.4,.5       seconds\\n\");\n    printf(\"K,L,M,N,O,P,Q,?  1,2,4,8,16,30,60,>60 seconds\\n\");\n    printf(\"From 0 to 100%%: \");\n    for (j = 0; j < spectrum_palette_size; j++) {\n        printf(\"\\033[48;5;%dm \", spectrum_palette[j]);\n    }\n    printf(\"\\033[0m\\n\");\n    printf(\"---------------------------------------------\\n\");\n}\n\nstatic void latencyDistMode(void) {\n    redisReply *reply;\n    long long start, latency, count = 0;\n    long long history_interval =\n        config.interval ? config.interval/1000 :\n                          LATENCY_DIST_DEFAULT_INTERVAL;\n    long long history_start = ustime();\n    int j, outputs = 0;\n\n    struct distsamples samples[] = {\n        /* We use a mostly logarithmic scale, with certain linear intervals\n         * which are more interesting than others, like 1-10 milliseconds\n         * range. */\n        {10,0,'.'},         /* 0.01 ms */\n        {125,0,'-'},        /* 0.125 ms */\n        {250,0,'*'},        /* 0.25 ms */\n        {500,0,'#'},        /* 0.5 ms */\n        {1000,0,'1'},       /* 1 ms */\n        {2000,0,'2'},       /* 2 ms */\n        {3000,0,'3'},       /* 3 ms */\n        {4000,0,'4'},       /* 4 ms */\n        {5000,0,'5'},       /* 5 ms */\n        {6000,0,'6'},       /* 6 ms */\n        {7000,0,'7'},       /* 7 ms */\n        {8000,0,'8'},       /* 8 ms */\n        {9000,0,'9'},       /* 9 ms */\n        {10000,0,'A'},      /* 10 ms */\n        {20000,0,'B'},      /* 20 ms */\n        {30000,0,'C'},      /* 30 ms */\n        {40000,0,'D'},      /* 40 ms */\n        {50000,0,'E'},      /* 50 ms */\n        {100000,0,'F'},     /* 0.1 s */\n        {200000,0,'G'},     /* 0.2 s */\n        {300000,0,'H'},     /* 0.3 s */\n        {400000,0,'I'},     /* 0.4 s */\n        {500000,0,'J'},     /* 0.5 s */\n        {1000000,0,'K'},    /* 1 s */\n        {2000000,0,'L'},    /* 2 s */\n        {4000000,0,'M'},    /* 4 s */\n        {8000000,0,'N'},    /* 8 s */\n        {16000000,0,'O'},   /* 16 s */\n        {30000000,0,'P'},   /* 30 s */\n        {60000000,0,'Q'},   /* 1 minute */\n        {0,0,'?'},          /* > 1 minute */\n    };\n\n    if (!context) exit(1);\n    while(1) {\n        start = ustime();\n        reply = reconnectingRedisCommand(context,\"PING\");\n        if (reply == NULL) {\n            fprintf(stderr,\"\\nI/O error\\n\");\n            exit(1);\n        }\n        latency = ustime()-start;\n        freeReplyObject(reply);\n        count++;\n\n        /* Populate the relevant bucket. */\n        for (j = 0; ; j++) {\n            if (samples[j].max == 0 || latency <= samples[j].max) {\n                samples[j].count++;\n                break;\n            }\n        }\n\n        /* From time to time show the spectrum. */\n        if (count && (ustime()-history_start)/1000 > history_interval) {\n            if ((outputs++ % 20) == 0)\n                showLatencyDistLegend();\n            showLatencyDistSamples(samples,count);\n            history_start = ustime();\n            count = 0;\n        }\n        usleep(LATENCY_SAMPLE_RATE * 1000);\n    }\n}\n\n/*------------------------------------------------------------------------------\n * Slave mode\n *--------------------------------------------------------------------------- */\n\n#define RDB_EOF_MARK_SIZE 40\n\nvoid sendReplconf(const char* arg1, const char* arg2) {\n    printf(\"sending REPLCONF %s %s\\n\", arg1, arg2);\n    redisReply *reply = redisCommand(context, \"REPLCONF %s %s\", arg1, arg2);\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, \"REPLCONF %s error: %s\\n\", arg1, reply->str);\n        /* non fatal, old versions may not support it */\n    }\n    freeReplyObject(reply);\n}\n\nvoid sendCapa() {\n    sendReplconf(\"capa\", \"eof\");\n}\n\n/* Sends SYNC and reads the number of bytes in the payload. Used both by\n * slaveMode() and getRDB().\n * returns 0 in case an EOF marker is used. */\nunsigned long long sendSync(int fd, char *out_eof) {\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    if (strncmp(buf+1,\"EOF:\",4) == 0 && strlen(buf+5) >= RDB_EOF_MARK_SIZE) {\n        memcpy(out_eof, buf+5, RDB_EOF_MARK_SIZE);\n        return 0;\n    }\n    return strtoull(buf+1,NULL,10);\n}\n\nstatic void slaveMode(void) {\n    int fd = context->fd;\n    static char eofmark[RDB_EOF_MARK_SIZE];\n    static char lastbytes[RDB_EOF_MARK_SIZE];\n    static int usemark = 0;\n    unsigned long long payload = sendSync(fd, eofmark);\n    char buf[1024];\n    int original_output = config.output;\n\n    if (payload == 0) {\n        payload = ULLONG_MAX;\n        memset(lastbytes,0,RDB_EOF_MARK_SIZE);\n        usemark = 1;\n        fprintf(stderr,\"SYNC with master, discarding \"\n                       \"bytes of bulk transfer until EOF marker...\\n\");\n    } else {\n        fprintf(stderr,\"SYNC with master, discarding %llu \"\n                       \"bytes of bulk transfer...\\n\", payload);\n    }\n\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        if (usemark) {\n            /* Update the last bytes array, and check if it matches our delimiter.*/\n            if (nread >= RDB_EOF_MARK_SIZE) {\n                memcpy(lastbytes,buf+nread-RDB_EOF_MARK_SIZE,RDB_EOF_MARK_SIZE);\n            } else {\n                int rem = RDB_EOF_MARK_SIZE-nread;\n                memmove(lastbytes,lastbytes+nread,rem);\n                memcpy(lastbytes+rem,buf,nread);\n            }\n            if (memcmp(lastbytes,eofmark,RDB_EOF_MARK_SIZE) == 0)\n                break;\n        }\n    }\n\n    if (usemark) {\n        unsigned long long offset = ULLONG_MAX - payload;\n        fprintf(stderr,\"SYNC done after %llu bytes. Logging commands from master.\\n\", offset);\n        /* put the slave online */\n        sleep(1);\n        sendReplconf(\"ACK\", \"0\");\n    } else\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(clusterManagerNode *node) {\n    int s, fd;\n    char *filename;\n    if (node != NULL) {\n        assert(node->context);\n        s = node->context->fd;\n        filename = clusterManagerGetNodeRDBFilename(node);\n    } else {\n        s = context->fd;\n        filename = config.rdb_filename;\n    }\n    static char eofmark[RDB_EOF_MARK_SIZE];\n    static char lastbytes[RDB_EOF_MARK_SIZE];\n    static int usemark = 0;\n    unsigned long long payload = sendSync(s, eofmark);\n    char buf[4096];\n\n    if (payload == 0) {\n        payload = ULLONG_MAX;\n        memset(lastbytes,0,RDB_EOF_MARK_SIZE);\n        usemark = 1;\n        fprintf(stderr,\"SYNC sent to master, writing bytes of bulk transfer \"\n                \"until EOF marker to '%s'\\n\", filename);\n    } else {\n        fprintf(stderr,\"SYNC sent to master, writing %llu bytes to '%s'\\n\",\n            payload, filename);\n    }\n\n    /* Write to file. */\n    if (!strcmp(filename,\"-\")) {\n        fd = STDOUT_FILENO;\n    } else {\n        fd = open(filename, O_CREAT|O_WRONLY, 0644);\n        if (fd == -1) {\n            fprintf(stderr, \"Error opening '%s': %s\\n\", 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                (nwritten == -1) ? strerror(errno) : \"short write\");\n            exit(1);\n        }\n        payload -= nread;\n\n        if (usemark) {\n            /* Update the last bytes array, and check if it matches our delimiter.*/\n            if (nread >= RDB_EOF_MARK_SIZE) {\n                memcpy(lastbytes,buf+nread-RDB_EOF_MARK_SIZE,RDB_EOF_MARK_SIZE);\n            } else {\n                int rem = RDB_EOF_MARK_SIZE-nread;\n                memmove(lastbytes,lastbytes+nread,rem);\n                memcpy(lastbytes+rem,buf,nread);\n            }\n            if (memcmp(lastbytes,eofmark,RDB_EOF_MARK_SIZE) == 0)\n                break;\n        }\n    }\n    if (usemark) {\n        payload = ULLONG_MAX - payload - RDB_EOF_MARK_SIZE;\n        if (ftruncate(fd, payload) == -1)\n            fprintf(stderr,\"ftruncate failed: %s.\\n\", strerror(errno));\n        fprintf(stderr,\"Transfer finished with success after %llu bytes\\n\", payload);\n    } else {\n        fprintf(stderr,\"Transfer finished with success.\\n\");\n    }\n    close(s); /* Close the file descriptor ASAP as fsync() may take time. */\n    fsync(fd);\n    close(fd);\n    fprintf(stderr,\"Transfer finished with success.\\n\");\n    if (node) {\n        sdsfree(filename);\n        return;\n    }\n    exit(0);\n}\n\n/*------------------------------------------------------------------------------\n * Bulk import (pipe) mode\n *--------------------------------------------------------------------------- */\n\n#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)\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            int read_error = 0;\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                    read_error = 1;\n                    break;\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            /* Abort on read errors. We abort here because it is important\n             * to consume replies even after a read error: this way we can\n             * show a potential problem to the user. */\n            if (read_error) exit(1);\n        }\n\n        /* Handle the writable state: we can send protocol to the server. */\n        if (mask & AE_WRITABLE) {\n            ssize_t loop_nwritten = 0;\n\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                    loop_nwritten += 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) ||\n                    loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) 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\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 = strtoull(reply->element[0]->str, NULL, 10);\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\ntypedef struct {\n    char *name;\n    char *sizecmd;\n    char *sizeunit;\n    unsigned long long biggest;\n    unsigned long long count;\n    unsigned long long totalsize;\n    sds biggest_key;\n} typeinfo;\n\ntypeinfo type_string = { \"string\", \"STRLEN\", \"bytes\" };\ntypeinfo type_list = { \"list\", \"LLEN\", \"items\" };\ntypeinfo type_set = { \"set\", \"SCARD\", \"members\" };\ntypeinfo type_hash = { \"hash\", \"HLEN\", \"fields\" };\ntypeinfo type_zset = { \"zset\", \"ZCARD\", \"members\" };\ntypeinfo type_stream = { \"stream\", \"XLEN\", \"entries\" };\ntypeinfo type_other = { \"other\", NULL, \"?\" };\n\nstatic typeinfo* typeinfo_add(dict *types, char* name, typeinfo* type_template) {\n    typeinfo *info = zmalloc(sizeof(typeinfo));\n    *info = *type_template;\n    info->name = sdsnew(name);\n    dictAdd(types, info->name, info);\n    return info;\n}\n\nvoid type_free(void* priv_data, void* val) {\n    typeinfo *info = val;\n    UNUSED(priv_data);\n    if (info->biggest_key)\n        sdsfree(info->biggest_key);\n    sdsfree(info->name);\n    zfree(info);\n}\n\nstatic dictType typeinfoDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* key destructor (owned by the value)*/\n    type_free                  /* val destructor */\n};\n\nstatic void getKeyTypes(dict *types_dict, redisReply *keys, typeinfo **types) {\n    redisReply *reply;\n    unsigned 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            if(reply->type == REDIS_REPLY_ERROR) {\n                fprintf(stderr, \"TYPE returned an error: %s\\n\", reply->str);\n            } else {\n                fprintf(stderr,\n                    \"Invalid reply type (%d) for TYPE on key '%s'!\\n\",\n                    reply->type, keys->element[i]->str);\n            }\n            exit(1);\n        }\n\n        sds typereply = sdsnew(reply->str);\n        dictEntry *de = dictFind(types_dict, typereply);\n        sdsfree(typereply);\n        typeinfo *type = NULL;\n        if (de)\n            type = dictGetVal(de);\n        else if (strcmp(reply->str, \"none\")) /* create new types for modules, (but not for deleted keys) */\n            type = typeinfo_add(types_dict, reply->str, &type_other);\n        types[i] = type;\n        freeReplyObject(reply);\n    }\n}\n\nstatic void getKeySizes(redisReply *keys, typeinfo **types,\n                        unsigned long long *sizes, int memkeys,\n                        unsigned memkeys_samples)\n{\n    redisReply *reply;\n    unsigned int i;\n\n    /* Pipeline size commands */\n    for(i=0;i<keys->elements;i++) {\n        /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */\n        if(!types[i] || (!types[i]->sizecmd && !memkeys))\n            continue;\n\n        if (!memkeys)\n            redisAppendCommand(context, \"%s %s\",\n                types[i]->sizecmd, keys->element[i]->str);\n        else if (memkeys_samples==0)\n            redisAppendCommand(context, \"%s %s %s\",\n                \"MEMORY\", \"USAGE\", keys->element[i]->str);\n        else\n            redisAppendCommand(context, \"%s %s %s SAMPLES %u\",\n                \"MEMORY\", \"USAGE\", keys->element[i]->str, memkeys_samples);\n    }\n\n    /* Retrieve sizes */\n    for(i=0;i<keys->elements;i++) {\n        /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */\n        if(!types[i] || (!types[i]->sizecmd && !memkeys)) {\n            sizes[i] = 0;\n            continue;\n        }\n\n        /* Retrieve 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                !memkeys? types[i]->sizecmd: \"MEMORY USAGE\",\n                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(int memkeys, unsigned memkeys_samples) {\n    unsigned long long sampled = 0, total_keys, totlen=0, *sizes=NULL, it=0;\n    redisReply *reply, *keys;\n    unsigned int arrsize=0, i;\n    dictIterator *di;\n    dictEntry *de;\n    typeinfo **types = NULL;\n    double pct;\n\n    dict *types_dict = dictCreate(&typeinfoDictType, NULL);\n    typeinfo_add(types_dict, \"string\", &type_string);\n    typeinfo_add(types_dict, \"list\", &type_list);\n    typeinfo_add(types_dict, \"set\", &type_set);\n    typeinfo_add(types_dict, \"hash\", &type_hash);\n    typeinfo_add(types_dict, \"zset\", &type_zset);\n    typeinfo_add(types_dict, \"stream\", &type_stream);\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    /* 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(typeinfo*)*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        /* Retrieve types and then sizes */\n        getKeyTypes(types_dict, keys, types);\n        getKeySizes(keys, types, sizes, memkeys, memkeys_samples);\n\n        /* Now update our stats */\n        for(i=0;i<keys->elements;i++) {\n            typeinfo *type = types[i];\n            /* Skip keys that disappeared between SCAN and TYPE */\n            if(!type)\n                continue;\n\n            type->totalsize += sizes[i];\n            type->count++;\n            totlen += keys->element[i]->len;\n            sampled++;\n\n            if(type->biggest<sizes[i]) {\n                printf(\n                   \"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\\n\",\n                   pct, type->name, keys->element[i]->str, sizes[i],\n                   !memkeys? type->sizeunit: \"bytes\");\n\n                /* Keep track of biggest key name for this type */\n                if (type->biggest_key)\n                    sdsfree(type->biggest_key);\n                type->biggest_key = sdsnew(keys->element[i]->str);\n                if(!type->biggest_key) {\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                type->biggest = 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    di = dictGetIterator(types_dict);\n    while ((de = dictNext(di))) {\n        typeinfo *type = dictGetVal(de);\n        if(type->biggest_key) {\n            printf(\"Biggest %6s found '%s' has %llu %s\\n\", type->name, type->biggest_key,\n               type->biggest, !memkeys? type->sizeunit: \"bytes\");\n        }\n    }\n    dictReleaseIterator(di);\n\n    printf(\"\\n\");\n\n    di = dictGetIterator(types_dict);\n    while ((de = dictNext(di))) {\n        typeinfo *type = dictGetVal(de);\n        printf(\"%llu %ss with %llu %s (%05.2f%% of keys, avg size %.2f)\\n\",\n           type->count, type->name, type->totalsize, !memkeys? type->sizeunit: \"bytes\",\n           sampled ? 100 * (double)type->count/sampled : 0,\n           type->count ? (double)type->totalsize/type->count : 0);\n    }\n    dictReleaseIterator(di);\n\n    dictRelease(types_dict);\n\n    /* Success! */\n    exit(0);\n}\n\nstatic void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {\n    redisReply *reply;\n    unsigned int i;\n\n    /* Pipeline OBJECT freq commands */\n    for(i=0;i<keys->elements;i++) {\n        redisAppendCommand(context, \"OBJECT freq %s\", keys->element[i]->str);\n    }\n\n    /* Retrieve freqs */\n    for(i=0;i<keys->elements;i++) {\n        if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {\n            fprintf(stderr, \"Error getting freq 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            if(reply->type == REDIS_REPLY_ERROR) {\n                fprintf(stderr, \"Error: %s\\n\", reply->str);\n                exit(1);\n            } else {\n                fprintf(stderr, \"Warning: OBJECT freq on '%s' failed (may have been deleted)\\n\", keys->element[i]->str);\n                freqs[i] = 0;\n            }\n        } else {\n            freqs[i] = reply->integer;\n        }\n        freeReplyObject(reply);\n    }\n}\n\n#define HOTKEYS_SAMPLE 16\nstatic void findHotKeys(void) {\n    redisReply *keys, *reply;\n    unsigned long long counters[HOTKEYS_SAMPLE] = {0};\n    sds hotkeys[HOTKEYS_SAMPLE] = {NULL};\n    unsigned long long sampled = 0, total_keys, *freqs = NULL, it = 0;\n    unsigned int arrsize = 0, i, k;\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 hot 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    /* 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 freqs array if we need to */\n        if(keys->elements > arrsize) {\n            freqs = zrealloc(freqs, sizeof(unsigned long long)*keys->elements);\n\n            if(!freqs) {\n                fprintf(stderr, \"Failed to allocate storage for keys!\\n\");\n                exit(1);\n            }\n\n            arrsize = keys->elements;\n        }\n\n        getKeyFreqs(keys, freqs);\n\n        /* Now update our stats */\n        for(i=0;i<keys->elements;i++) {\n            sampled++;\n            /* Update overall progress */\n            if(sampled % 1000000 == 0) {\n                printf(\"[%05.2f%%] Sampled %llu keys so far\\n\", pct, sampled);\n            }\n\n            /* Use eviction pool here */\n            k = 0;\n            while (k < HOTKEYS_SAMPLE && freqs[i] > counters[k]) k++;\n            if (k == 0) continue;\n            k--;\n            if (k == 0 || counters[k] == 0) {\n                sdsfree(hotkeys[k]);\n            } else {\n                sdsfree(hotkeys[0]);\n                memmove(counters,counters+1,sizeof(counters[0])*k);\n                memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);\n            }\n            counters[k] = freqs[i];\n            hotkeys[k] = sdsnew(keys->element[i]->str);\n            printf(\n               \"[%05.2f%%] Hot key '%s' found so far with counter %llu\\n\",\n               pct, keys->element[i]->str, freqs[i]);\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 (freqs) zfree(freqs);\n\n    /* We're done */\n    printf(\"\\n-------- summary -------\\n\\n\");\n\n    printf(\"Sampled %llu keys in the keyspace!\\n\", sampled);\n\n    for (i=1; i<= HOTKEYS_SAMPLE; i++) {\n        k = HOTKEYS_SAMPLE - i;\n        if(counters[k]>0) {\n            printf(\"hot key found with counter: %llu\\tkeyname: %s\\n\", counters[k], hotkeys[k]);\n            sdsfree(hotkeys[k]);\n        }\n    }\n\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 = zmalloc(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    zfree(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,\"%lldB\",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(void) {\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 = reconnectingRedisCommand(context,\"INFO\");\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        /* Requests */\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        aux |= getLongInfoField(reply->str,\"loading\") << 2;\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        case 4:\n            printf(\"LOAD\");\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(void) {\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            unsigned 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 * LRU test mode\n *--------------------------------------------------------------------------- */\n\n/* Return an integer from min to max (both inclusive) using a power-law\n * distribution, depending on the value of alpha: the greater the alpha\n * the more bias towards lower values.\n *\n * With alpha = 6.2 the output follows the 80-20 rule where 20% of\n * the returned numbers will account for 80% of the frequency. */\nlong long powerLawRand(long long min, long long max, double alpha) {\n    double pl, r;\n\n    max += 1;\n    r = ((double)rand()) / RAND_MAX;\n    pl = pow(\n        ((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),\n        (1.0/(alpha+1)));\n    return (max-1-(long long)pl)+min;\n}\n\n/* Generates a key name among a set of lru_test_sample_size keys, using\n * an 80-20 distribution. */\nvoid LRUTestGenKey(char *buf, size_t buflen) {\n    snprintf(buf, buflen, \"lru:%lld\",\n        powerLawRand(1, config.lru_test_sample_size, 6.2));\n}\n\n#define LRU_CYCLE_PERIOD 1000 /* 1000 milliseconds. */\n#define LRU_CYCLE_PIPELINE_SIZE 250\nstatic void LRUTestMode(void) {\n    redisReply *reply;\n    char key[128];\n    long long start_cycle;\n    int j;\n\n    srand(time(NULL)^getpid());\n    while(1) {\n        /* Perform cycles of 1 second with 50% writes and 50% reads.\n         * We use pipelining batching writes / reads N times per cycle in order\n         * to fill the target instance easily. */\n        start_cycle = mstime();\n        long long hits = 0, misses = 0;\n        while(mstime() - start_cycle < LRU_CYCLE_PERIOD) {\n            /* Write cycle. */\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {\n                char val[6];\n                val[5] = '\\0';\n                for (int i = 0; i < 5; i++) val[i] = 'A'+rand()%('z'-'A');\n                LRUTestGenKey(key,sizeof(key));\n                redisAppendCommand(context, \"SET %s %s\",key,val);\n            }\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)\n                redisGetReply(context, (void**)&reply);\n\n            /* Read cycle. */\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {\n                LRUTestGenKey(key,sizeof(key));\n                redisAppendCommand(context, \"GET %s\",key);\n            }\n            for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {\n                if (redisGetReply(context, (void**)&reply) == REDIS_OK) {\n                    switch(reply->type) {\n                        case REDIS_REPLY_ERROR:\n                            printf(\"%s\\n\", reply->str);\n                            break;\n                        case REDIS_REPLY_NIL:\n                            misses++;\n                            break;\n                        default:\n                            hits++;\n                            break;\n                    }\n                }\n            }\n\n            if (context->err) {\n                fprintf(stderr,\"I/O error during LRU test\\n\");\n                exit(1);\n            }\n        }\n        /* Print stats. */\n        printf(\n            \"%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\\n\",\n            hits+misses,\n            hits, (double)hits/(hits+misses)*100,\n            misses, (double)misses/(hits+misses)*100);\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 intrinsicLatencyModeStop(int s) {\n    UNUSED(s);\n    force_cancel_loop = 1;\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    signal(SIGINT, intrinsicLatencyModeStop);\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        double avg_us = (double)run_time/runs;\n        double avg_ns = avg_us * 1e3;\n        if (force_cancel_loop || end > test_end) {\n            printf(\"\\n%lld total runs \"\n                \"(avg latency: \"\n                \"%.4f microseconds / %.2f nanoseconds per run).\\n\",\n                runs, avg_us, avg_ns);\n            printf(\"Worst run took %.0fx longer than the average latency.\\n\",\n                max_latency / avg_us);\n            exit(0);\n        }\n    }\n}\n\nstatic sds askPassword() {\n    linenoiseMaskModeEnable();\n    sds auth = linenoise(\"Please input password: \");\n    linenoiseMaskModeDisable();\n    return auth;\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_dist_mode = 0;\n    config.latency_history = 0;\n    config.lru_test_mode = 0;\n    config.lru_test_sample_size = 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.hotkeys = 0;\n    config.stdinarg = 0;\n    config.auth = NULL;\n    config.askpass = 0;\n    config.user = NULL;\n    config.eval = NULL;\n    config.eval_ldb = 0;\n    config.eval_ldb_end = 0;\n    config.eval_ldb_sync = 0;\n    config.enable_ldb_on_eval = 0;\n    config.last_cmd_type = -1;\n    config.verbose = 0;\n    config.no_auth_warning = 0;\n    config.cluster_manager_command.name = NULL;\n    config.cluster_manager_command.argc = 0;\n    config.cluster_manager_command.argv = NULL;\n    config.cluster_manager_command.flags = 0;\n    config.cluster_manager_command.replicas = 0;\n    config.cluster_manager_command.from = NULL;\n    config.cluster_manager_command.to = NULL;\n    config.cluster_manager_command.weight = NULL;\n    config.cluster_manager_command.weight_argc = 0;\n    config.cluster_manager_command.slots = 0;\n    config.cluster_manager_command.timeout = CLUSTER_MANAGER_MIGRATE_TIMEOUT;\n    config.cluster_manager_command.pipeline = CLUSTER_MANAGER_MIGRATE_PIPELINE;\n    config.cluster_manager_command.threshold =\n        CLUSTER_MANAGER_REBALANCE_THRESHOLD;\n    config.cluster_manager_command.backup_dir = NULL;\n    pref.hints = 1;\n\n    spectrum_palette = spectrum_palette_color;\n    spectrum_palette_size = spectrum_palette_color_size;\n\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\n    firstarg = parseOptions(argc,argv);\n    argc -= firstarg;\n    argv += firstarg;\n\n    parseEnv();\n\n    if (config.askpass) {\n        config.auth = askPassword();\n    }\n\n#ifdef USE_OPENSSL\n    if (config.tls) {\n        ERR_load_crypto_strings();\n        SSL_load_error_strings();\n        SSL_library_init();\n    }\n#endif\n\n    /* Cluster Manager mode */\n    if (CLUSTER_MANAGER_MODE()) {\n        clusterManagerCommandProc *proc = validateClusterManagerCommand();\n        if (!proc) {\n            sdsfree(config.hostip);\n            sdsfree(config.mb_delim);\n            exit(1);\n        }\n        clusterManagerMode(proc);\n    }\n\n    /* Latency mode */\n    if (config.latency_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        latencyMode();\n    }\n\n    /* Latency distribution mode */\n    if (config.latency_dist_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        latencyDistMode();\n    }\n\n    /* Slave mode */\n    if (config.slave_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        sendCapa();\n        slaveMode();\n    }\n\n    /* Get RDB mode. */\n    if (config.getrdb_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        sendCapa();\n        getRDB(NULL);\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(0, 0);\n    }\n\n    /* Find large keys */\n    if (config.memkeys) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        findBigKeys(1, config.memkeys_samples);\n    }\n\n    /* Find hot keys */\n    if (config.hotkeys) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        findHotKeys();\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    /* LRU test mode */\n    if (config.lru_test_mode) {\n        if (cliConnect(0) == REDIS_ERR) exit(1);\n        LRUTestMode();\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        /* Ignore SIGPIPE in interactive mode to force a reconnect */\n        signal(SIGPIPE, SIG_IGN);\n\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\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/redis-trib.rb",
    "content": "#!/usr/bin/env ruby\n\ndef colorized(str, color)\n    return str if !(ENV['TERM'] || '')[\"xterm\"]\n    color_code = {\n        white: 29,\n        bold: '29;1',\n        black: 30,\n        red: 31,\n        green: 32,\n        yellow: 33,\n        blue: 34,\n        magenta: 35,\n        cyan: 36,\n        gray: 37\n    }[color]\n    return str if !color_code\n    \"\\033[#{color_code}m#{str}\\033[0m\"\nend\n\nclass String\n\n    %w(white bold black red green yellow blue magenta cyan gray).each{|color|\n        color = :\"#{color}\"\n        define_method(color){\n            colorized(self, color)\n        }\n    }\n\nend\n\nCOMMANDS = %w(create check info fix reshard rebalance add-node \n              del-node set-timeout call import help)\n\nALLOWED_OPTIONS={\n    \"create\" => {\"replicas\" => true},\n    \"add-node\" => {\"slave\" => false, \"master-id\" => true},\n    \"import\" => {\"from\" => :required, \"copy\" => false, \"replace\" => false},\n    \"reshard\" => {\"from\" => true, \"to\" => true, \"slots\" => true, \"yes\" => false, \"timeout\" => true, \"pipeline\" => true},\n    \"rebalance\" => {\"weight\" => [], \"auto-weights\" => false, \"use-empty-masters\" => false, \"timeout\" => true, \"simulate\" => false, \"pipeline\" => true, \"threshold\" => true},\n    \"fix\" => {\"timeout\" => 0},\n}\n\ndef parse_options(cmd)\n    cmd = cmd.downcase\n    idx = 0\n    options = {}\n    args = []\n    while (arg = ARGV.shift)\n        if arg[0..1] == \"--\"\n            option = arg[2..-1]\n\n            # --verbose is a global option\n            if option == \"--verbose\"\n                options['verbose'] = true\n                next\n            end\n            if ALLOWED_OPTIONS[cmd] == nil || \n               ALLOWED_OPTIONS[cmd][option] == nil\n                next\n            end\n            if ALLOWED_OPTIONS[cmd][option] != false\n                value = ARGV.shift\n                next if !value\n            else\n                value = true\n            end\n\n            # If the option is set to [], it's a multiple arguments\n            # option. We just queue every new value into an array.\n            if ALLOWED_OPTIONS[cmd][option] == []\n                options[option] = [] if !options[option]\n                options[option] << value\n            else\n                options[option] = value\n            end\n        else\n            next if arg[0,1] == '-'\n            args << arg\n        end\n    end\n\n    return options,args\nend\n\ndef command_example(cmd, args, opts)\n    cmd = \"redis-cli --cluster #{cmd}\"\n    args.each{|a| \n        a = a.to_s\n        a = a.inspect if a[' ']\n        cmd << \" #{a}\"\n    }\n    opts.each{|opt, val|\n        opt = \" --cluster-#{opt.downcase}\"\n        if val != true\n            val = val.join(' ') if val.is_a? Array\n            opt << \" #{val}\"\n        end\n        cmd << opt\n    }\n    cmd\nend\n\n$command = ARGV.shift\n$opts, $args = parse_options($command) if $command\n\nputs \"WARNING: redis-trib.rb is not longer available!\".yellow\nputs \"You should use #{'redis-cli'.bold} instead.\"\nputs ''\nputs \"All commands and features belonging to redis-trib.rb \"+\n     \"have been moved\\nto redis-cli.\"\nputs \"In order to use them you should call redis-cli with the #{'--cluster'.bold}\"\nputs \"option followed by the subcommand name, arguments and options.\"\nputs ''\nputs \"Use the following syntax:\"\nputs \"redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS]\".bold\nputs ''\nputs \"Example:\"\nif $command\n    example = command_example $command, $args, $opts\nelse\n    example = \"redis-cli --cluster info 127.0.0.1:7000\"\nend\nputs example.bold\nputs ''\nputs \"To get help about all subcommands, type:\"\nputs \"redis-cli --cluster help\".bold\nputs ''\nexit 1\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/redisassert.h",
    "content": "/* redisassert.h -- Drop in replacements 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 : (_serverAssert(#_e,__FILE__,__LINE__),_exit(1)))\n#define panic(...) _serverPanic(__FILE__,__LINE__,__VA_ARGS__),_exit(1)\n\nvoid _serverAssert(char *estr, char *file, int line);\nvoid _serverPanic(const char *file, int line, const char *msg, ...);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/redismodule.h",
    "content": "#ifndef REDISMODULE_H\n#define REDISMODULE_H\n\n#include <sys/types.h>\n#include <stdint.h>\n#include <stdio.h>\n\n/* ---------------- Defines common between core and modules --------------- */\n\n/* Error status return values. */\n#define REDISMODULE_OK 0\n#define REDISMODULE_ERR 1\n\n/* API versions. */\n#define REDISMODULE_APIVER_1 1\n\n/* API flags and constants */\n#define REDISMODULE_READ (1<<0)\n#define REDISMODULE_WRITE (1<<1)\n\n/* RedisModule_OpenKey extra flags for the 'mode' argument.\n * Avoid touching the LRU/LFU of the key when opened. */\n#define REDISMODULE_OPEN_KEY_NOTOUCH (1<<16)\n\n#define REDISMODULE_LIST_HEAD 0\n#define REDISMODULE_LIST_TAIL 1\n\n/* Key types. */\n#define REDISMODULE_KEYTYPE_EMPTY 0\n#define REDISMODULE_KEYTYPE_STRING 1\n#define REDISMODULE_KEYTYPE_LIST 2\n#define REDISMODULE_KEYTYPE_HASH 3\n#define REDISMODULE_KEYTYPE_SET 4\n#define REDISMODULE_KEYTYPE_ZSET 5\n#define REDISMODULE_KEYTYPE_MODULE 6\n#define REDISMODULE_KEYTYPE_STREAM 7\n\n/* Reply types. */\n#define REDISMODULE_REPLY_UNKNOWN -1\n#define REDISMODULE_REPLY_STRING 0\n#define REDISMODULE_REPLY_ERROR 1\n#define REDISMODULE_REPLY_INTEGER 2\n#define REDISMODULE_REPLY_ARRAY 3\n#define REDISMODULE_REPLY_NULL 4\n\n/* Postponed array length. */\n#define REDISMODULE_POSTPONED_ARRAY_LEN -1\n\n/* Expire */\n#define REDISMODULE_NO_EXPIRE -1\n\n/* Sorted set API flags. */\n#define REDISMODULE_ZADD_XX      (1<<0)\n#define REDISMODULE_ZADD_NX      (1<<1)\n#define REDISMODULE_ZADD_ADDED   (1<<2)\n#define REDISMODULE_ZADD_UPDATED (1<<3)\n#define REDISMODULE_ZADD_NOP     (1<<4)\n\n/* Hash API flags. */\n#define REDISMODULE_HASH_NONE       0\n#define REDISMODULE_HASH_NX         (1<<0)\n#define REDISMODULE_HASH_XX         (1<<1)\n#define REDISMODULE_HASH_CFIELDS    (1<<2)\n#define REDISMODULE_HASH_EXISTS     (1<<3)\n\n/* Context Flags: Info about the current context returned by\n * RM_GetContextFlags(). */\n\n/* The command is running in the context of a Lua script */\n#define REDISMODULE_CTX_FLAGS_LUA (1<<0)\n/* The command is running inside a Redis transaction */\n#define REDISMODULE_CTX_FLAGS_MULTI (1<<1)\n/* The instance is a master */\n#define REDISMODULE_CTX_FLAGS_MASTER (1<<2)\n/* The instance is a slave */\n#define REDISMODULE_CTX_FLAGS_SLAVE (1<<3)\n/* The instance is read-only (usually meaning it's a slave as well) */\n#define REDISMODULE_CTX_FLAGS_READONLY (1<<4)\n/* The instance is running in cluster mode */\n#define REDISMODULE_CTX_FLAGS_CLUSTER (1<<5)\n/* The instance has AOF enabled */\n#define REDISMODULE_CTX_FLAGS_AOF (1<<6)\n/* The instance has RDB enabled */\n#define REDISMODULE_CTX_FLAGS_RDB (1<<7)\n/* The instance has Maxmemory set */\n#define REDISMODULE_CTX_FLAGS_MAXMEMORY (1<<8)\n/* Maxmemory is set and has an eviction policy that may delete keys */\n#define REDISMODULE_CTX_FLAGS_EVICT (1<<9)\n/* Redis is out of memory according to the maxmemory flag. */\n#define REDISMODULE_CTX_FLAGS_OOM (1<<10)\n/* Less than 25% of memory available according to maxmemory. */\n#define REDISMODULE_CTX_FLAGS_OOM_WARNING (1<<11)\n/* The command was sent over the replication link. */\n#define REDISMODULE_CTX_FLAGS_REPLICATED (1<<12)\n/* Redis is currently loading either from AOF or RDB. */\n#define REDISMODULE_CTX_FLAGS_LOADING (1<<13)\n/* The replica has no link with its master, note that\n * there is the inverse flag as well:\n *\n *  REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE\n *\n * The two flags are exclusive, one or the other can be set. */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE (1<<14)\n/* The replica is trying to connect with the master.\n * (REPL_STATE_CONNECT and REPL_STATE_CONNECTING states) */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING (1<<15)\n/* THe replica is receiving an RDB file from its master. */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING (1<<16)\n/* The replica is online, receiving updates from its master. */\n#define REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE (1<<17)\n/* There is currently some background process active. */\n#define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18)\n/* The next EXEC will fail due to dirty CAS (touched keys). */\n#define REDISMODULE_CTX_FLAGS_MULTI_DIRTY (1<<19)\n\n/* Keyspace changes notification classes. Every class is associated with a\n * character for configuration purposes.\n * NOTE: These have to be in sync with NOTIFY_* in server.h */\n#define REDISMODULE_NOTIFY_KEYSPACE (1<<0)    /* K */\n#define REDISMODULE_NOTIFY_KEYEVENT (1<<1)    /* E */\n#define REDISMODULE_NOTIFY_GENERIC (1<<2)     /* g */\n#define REDISMODULE_NOTIFY_STRING (1<<3)      /* $ */\n#define REDISMODULE_NOTIFY_LIST (1<<4)        /* l */\n#define REDISMODULE_NOTIFY_SET (1<<5)         /* s */\n#define REDISMODULE_NOTIFY_HASH (1<<6)        /* h */\n#define REDISMODULE_NOTIFY_ZSET (1<<7)        /* z */\n#define REDISMODULE_NOTIFY_EXPIRED (1<<8)     /* x */\n#define REDISMODULE_NOTIFY_EVICTED (1<<9)     /* e */\n#define REDISMODULE_NOTIFY_STREAM (1<<10)     /* t */\n#define REDISMODULE_NOTIFY_KEY_MISS (1<<11)   /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */\n#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM)      /* A */\n\n\n/* A special pointer that we can use between the core and the module to signal\n * field deletion, and that is impossible to be a valid pointer. */\n#define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1)\n\n/* Error messages. */\n#define REDISMODULE_ERRORMSG_WRONGTYPE \"WRONGTYPE Operation against a key holding the wrong kind of value\"\n\n#define REDISMODULE_POSITIVE_INFINITE (1.0/0.0)\n#define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0)\n\n/* Cluster API defines. */\n#define REDISMODULE_NODE_ID_LEN 40\n#define REDISMODULE_NODE_MYSELF     (1<<0)\n#define REDISMODULE_NODE_MASTER     (1<<1)\n#define REDISMODULE_NODE_SLAVE      (1<<2)\n#define REDISMODULE_NODE_PFAIL      (1<<3)\n#define REDISMODULE_NODE_FAIL       (1<<4)\n#define REDISMODULE_NODE_NOFAILOVER (1<<5)\n\n#define REDISMODULE_CLUSTER_FLAG_NONE 0\n#define REDISMODULE_CLUSTER_FLAG_NO_FAILOVER (1<<1)\n#define REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION (1<<2)\n\n#define REDISMODULE_NOT_USED(V) ((void) V)\n\n/* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */\n#define REDISMODULE_AUX_BEFORE_RDB (1<<0)\n#define REDISMODULE_AUX_AFTER_RDB (1<<1)\n\n/* This type represents a timer handle, and is returned when a timer is\n * registered and used in order to invalidate a timer. It's just a 64 bit\n * number, because this is how each timer is represented inside the radix tree\n * of timers that are going to expire, sorted by expire time. */\ntypedef uint64_t RedisModuleTimerID;\n\n/* CommandFilter Flags */\n\n/* Do filter RedisModule_Call() commands initiated by module itself. */\n#define REDISMODULE_CMDFILTER_NOSELF    (1<<0)\n\n/* Declare that the module can handle errors with RedisModule_SetModuleOptions. */\n#define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS    (1<<0)\n/* When set, Redis will not call RedisModule_SignalModifiedKey(), implicitly in\n * RedisModule_CloseKey, and the module needs to do that when manually when keys\n * are modified from the user's sperspective, to invalidate WATCH. */\n#define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1)\n\n/* Server events definitions. */\n#define REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED 0\n#define REDISMODULE_EVENT_PERSISTENCE 1\n#define REDISMODULE_EVENT_FLUSHDB 2\n#define REDISMODULE_EVENT_LOADING 3\n#define REDISMODULE_EVENT_CLIENT_CHANGE 4\n#define REDISMODULE_EVENT_SHUTDOWN 5\n#define REDISMODULE_EVENT_REPLICA_CHANGE 6\n#define REDISMODULE_EVENT_MASTER_LINK_CHANGE 7\n#define REDISMODULE_EVENT_CRON_LOOP 8\n#define REDISMODULE_EVENT_MODULE_CHANGE 9\n#define REDISMODULE_EVENT_LOADING_PROGRESS 10\n\ntypedef struct RedisModuleEvent {\n    uint64_t id;        /* REDISMODULE_EVENT_... defines. */\n    uint64_t dataver;   /* Version of the structure we pass as 'data'. */\n} RedisModuleEvent;\n\nstruct RedisModuleCtx;\ntypedef void (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data);\n\nstatic const RedisModuleEvent\n    RedisModuleEvent_ReplicationRoleChanged = {\n        REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,\n        1\n    },\n    RedisModuleEvent_Persistence = {\n        REDISMODULE_EVENT_PERSISTENCE,\n        1\n    },\n    RedisModuleEvent_FlushDB = {\n        REDISMODULE_EVENT_FLUSHDB,\n        1\n    },\n    RedisModuleEvent_Loading = {\n        REDISMODULE_EVENT_LOADING,\n        1\n    },\n    RedisModuleEvent_ClientChange = {\n        REDISMODULE_EVENT_CLIENT_CHANGE,\n        1\n    },\n    RedisModuleEvent_Shutdown = {\n        REDISMODULE_EVENT_SHUTDOWN,\n        1\n    },\n    RedisModuleEvent_ReplicaChange = {\n        REDISMODULE_EVENT_REPLICA_CHANGE,\n        1\n    },\n    RedisModuleEvent_CronLoop = {\n        REDISMODULE_EVENT_CRON_LOOP,\n        1\n    },\n    RedisModuleEvent_MasterLinkChange = {\n        REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n        1\n    },\n    RedisModuleEvent_ModuleChange = {\n        REDISMODULE_EVENT_MODULE_CHANGE,\n        1\n    },\n    RedisModuleEvent_LoadingProgress = {\n        REDISMODULE_EVENT_LOADING_PROGRESS,\n        1\n    };\n\n/* Those are values that are used for the 'subevent' callback argument. */\n#define REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START 0\n#define REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START 1\n#define REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START 2\n#define REDISMODULE_SUBEVENT_PERSISTENCE_ENDED 3\n#define REDISMODULE_SUBEVENT_PERSISTENCE_FAILED 4\n\n#define REDISMODULE_SUBEVENT_LOADING_RDB_START 0\n#define REDISMODULE_SUBEVENT_LOADING_AOF_START 1\n#define REDISMODULE_SUBEVENT_LOADING_REPL_START 2\n#define REDISMODULE_SUBEVENT_LOADING_ENDED 3\n#define REDISMODULE_SUBEVENT_LOADING_FAILED 4\n\n#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0\n#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1\n\n#define REDISMODULE_SUBEVENT_MASTER_LINK_UP 0\n#define REDISMODULE_SUBEVENT_MASTER_LINK_DOWN 1\n\n#define REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE 0\n#define REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE 1\n\n#define REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER 0\n#define REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA 1\n\n#define REDISMODULE_SUBEVENT_FLUSHDB_START 0\n#define REDISMODULE_SUBEVENT_FLUSHDB_END 1\n\n#define REDISMODULE_SUBEVENT_MODULE_LOADED 0\n#define REDISMODULE_SUBEVENT_MODULE_UNLOADED 1\n\n#define REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB 0\n#define REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF 1\n\n/* RedisModuleClientInfo flags. */\n#define REDISMODULE_CLIENTINFO_FLAG_SSL (1<<0)\n#define REDISMODULE_CLIENTINFO_FLAG_PUBSUB (1<<1)\n#define REDISMODULE_CLIENTINFO_FLAG_BLOCKED (1<<2)\n#define REDISMODULE_CLIENTINFO_FLAG_TRACKING (1<<3)\n#define REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET (1<<4)\n#define REDISMODULE_CLIENTINFO_FLAG_MULTI (1<<5)\n\n/* Here we take all the structures that the module pass to the core\n * and the other way around. Notably the list here contains the structures\n * used by the hooks API RedisModule_RegisterToServerEvent().\n *\n * The structures always start with a 'version' field. This is useful\n * when we want to pass a reference to the structure to the core APIs,\n * for the APIs to fill the structure. In that case, the structure 'version'\n * field is initialized before passing it to the core, so that the core is\n * able to cast the pointer to the appropriate structure version. In this\n * way we obtain ABI compatibility.\n *\n * Here we'll list all the structure versions in case they evolve over time,\n * however using a define, we'll make sure to use the last version as the\n * public name for the module to use. */\n\n#define REDISMODULE_CLIENTINFO_VERSION 1\ntypedef struct RedisModuleClientInfo {\n    uint64_t version;       /* Version of this structure for ABI compat. */\n    uint64_t flags;         /* REDISMODULE_CLIENTINFO_FLAG_* */\n    uint64_t id;            /* Client ID. */\n    char addr[46];          /* IPv4 or IPv6 address. */\n    uint16_t port;          /* TCP port. */\n    uint16_t db;            /* Selected DB. */\n} RedisModuleClientInfoV1;\n\n#define RedisModuleClientInfo RedisModuleClientInfoV1\n\n#define REDISMODULE_REPLICATIONINFO_VERSION 1\ntypedef struct RedisModuleReplicationInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int master;             /* true if master, false if replica */\n    char *masterhost;       /* master instance hostname for NOW_REPLICA */\n    int masterport;         /* master instance port for NOW_REPLICA */\n    char *replid1;          /* Main replication ID */\n    char *replid2;          /* Secondary replication ID */\n    uint64_t repl1_offset;  /* Main replication offset */\n    uint64_t repl2_offset;  /* Offset of replid2 validity */\n} RedisModuleReplicationInfoV1;\n\n#define RedisModuleReplicationInfo RedisModuleReplicationInfoV1\n\n#define REDISMODULE_FLUSHINFO_VERSION 1\ntypedef struct RedisModuleFlushInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int32_t sync;           /* Synchronous or threaded flush?. */\n    int32_t dbnum;          /* Flushed database number, -1 for ALL. */\n} RedisModuleFlushInfoV1;\n\n#define RedisModuleFlushInfo RedisModuleFlushInfoV1\n\n#define REDISMODULE_MODULE_CHANGE_VERSION 1\ntypedef struct RedisModuleModuleChange {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    const char* module_name;/* Name of module loaded or unloaded. */\n    int32_t module_version; /* Module version. */\n} RedisModuleModuleChangeV1;\n\n#define RedisModuleModuleChange RedisModuleModuleChangeV1\n\n#define REDISMODULE_CRON_LOOP_VERSION 1\ntypedef struct RedisModuleCronLoopInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int32_t hz;             /* Approximate number of events per second. */\n} RedisModuleCronLoopV1;\n\n#define RedisModuleCronLoop RedisModuleCronLoopV1\n\n#define REDISMODULE_LOADING_PROGRESS_VERSION 1\ntypedef struct RedisModuleLoadingProgressInfo {\n    uint64_t version;       /* Not used since this structure is never passed\n                               from the module to the core right now. Here\n                               for future compatibility. */\n    int32_t hz;             /* Approximate number of events per second. */\n    int32_t progress;       /* Approximate progress between 0 and 1024, or -1\n                             * if unknown. */\n} RedisModuleLoadingProgressV1;\n\n#define RedisModuleLoadingProgress RedisModuleLoadingProgressV1\n\n/* ------------------------- End of common defines ------------------------ */\n\n#ifndef REDISMODULE_CORE\n\ntypedef long long mstime_t;\n\n/* Incomplete structures for compiler checks but opaque access. */\ntypedef struct RedisModuleCtx RedisModuleCtx;\ntypedef struct RedisModuleKey RedisModuleKey;\ntypedef struct RedisModuleString RedisModuleString;\ntypedef struct RedisModuleCallReply RedisModuleCallReply;\ntypedef struct RedisModuleIO RedisModuleIO;\ntypedef struct RedisModuleType RedisModuleType;\ntypedef struct RedisModuleDigest RedisModuleDigest;\ntypedef struct RedisModuleBlockedClient RedisModuleBlockedClient;\ntypedef struct RedisModuleClusterInfo RedisModuleClusterInfo;\ntypedef struct RedisModuleDict RedisModuleDict;\ntypedef struct RedisModuleDictIter RedisModuleDictIter;\ntypedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;\ntypedef struct RedisModuleCommandFilter RedisModuleCommandFilter;\ntypedef struct RedisModuleInfoCtx RedisModuleInfoCtx;\ntypedef struct RedisModuleServerInfoData RedisModuleServerInfoData;\ntypedef struct RedisModuleScanCursor RedisModuleScanCursor;\ntypedef struct RedisModuleUser RedisModuleUser;\n\ntypedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);\ntypedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc);\ntypedef int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);\ntypedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);\ntypedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);\ntypedef int (*RedisModuleTypeAuxLoadFunc)(RedisModuleIO *rdb, int encver, int when);\ntypedef void (*RedisModuleTypeAuxSaveFunc)(RedisModuleIO *rdb, int when);\ntypedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);\ntypedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value);\ntypedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);\ntypedef void (*RedisModuleTypeFreeFunc)(void *value);\ntypedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len);\ntypedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);\ntypedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);\ntypedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);\ntypedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);\ntypedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata);\ntypedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata);\ntypedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);\n\n#define REDISMODULE_TYPE_METHOD_VERSION 2\ntypedef struct RedisModuleTypeMethods {\n    uint64_t version;\n    RedisModuleTypeLoadFunc rdb_load;\n    RedisModuleTypeSaveFunc rdb_save;\n    RedisModuleTypeRewriteFunc aof_rewrite;\n    RedisModuleTypeMemUsageFunc mem_usage;\n    RedisModuleTypeDigestFunc digest;\n    RedisModuleTypeFreeFunc free;\n    RedisModuleTypeAuxLoadFunc aux_load;\n    RedisModuleTypeAuxSaveFunc aux_save;\n    int aux_save_triggers;\n} RedisModuleTypeMethods;\n\n#define REDISMODULE_GET_API(name) \\\n    RedisModule_GetApi(\"RedisModule_\" #name, ((void **)&RedisModule_ ## name))\n\n#define REDISMODULE_API_FUNC(x) (*x)\n\n\nvoid *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes);\nvoid *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes);\nvoid REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr);\nvoid *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size);\nchar *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str);\nint REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *);\nint REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep);\nvoid REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver);\nint REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name);\nint REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll);\nint REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid);\nvoid *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode);\nvoid REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp);\nint REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp);\nsize_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp);\nint REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where);\nRedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);\nconst char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply);\nint REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply);\nlong long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply);\nsize_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply);\nRedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);\n#ifdef __GNUC__\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));\n#else\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);\n#endif\nvoid REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str);\nconst char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d);\nint REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply);\nint REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll);\nint REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d);\nint REDISMODULE_API_FUNC(RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d);\nvoid REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);\nint REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx);\nconst char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply);\nint REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str);\nchar *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode);\nint REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen);\nmstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire);\nvoid REDISMODULE_API_FUNC(RedisModule_ResetDataset)(int restart_aof, int async);\nunsigned long long REDISMODULE_API_FUNC(RedisModule_DbSize)(RedisModuleCtx *ctx);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_RandomKey)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr);\nint REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore);\nint REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted);\nvoid REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);\nint REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);\nint REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);\nint REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...);\nint REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...);\nint REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos);\nunsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_GetClientInfoById)(void *ci, uint64_t id);\nint REDISMODULE_API_FUNC(RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message);\nint REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_AvoidReplicaTraffic)();\nvoid *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes);\nRedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods);\nint REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);\nint REDISMODULE_API_FUNC(RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value);\nRedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);\nvoid *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);\nint REDISMODULE_API_FUNC(RedisModule_IsIOError)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options);\nint REDISMODULE_API_FUNC(RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);\nuint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);\nint64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io);\nchar *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value);\ndouble REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);\nfloat REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);\nvoid REDISMODULE_API_FUNC(RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value);\nlong double REDISMODULE_API_FUNC(RedisModule_LoadLongDouble)(RedisModuleIO *io);\nvoid *REDISMODULE_API_FUNC(RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt);\n#ifdef __GNUC__\nvoid REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));\nvoid REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));\n#else\nvoid REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);\nvoid REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);\n#endif\nvoid REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);\nvoid REDISMODULE_API_FUNC(RedisModule_LatencyAddSample)(const char *event, mstime_t latency);\nint REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str);\nint REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b);\nRedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io);\nconst RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromIO)(RedisModuleIO *io);\nconst RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key);\nlong long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void);\nvoid REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele);\nvoid REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md);\nRedisModuleDict *REDISMODULE_API_FUNC(RedisModule_CreateDict)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d);\nuint64_t REDISMODULE_API_FUNC(RedisModule_DictSize)(RedisModuleDict *d);\nint REDISMODULE_API_FUNC(RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);\nint REDISMODULE_API_FUNC(RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);\nint REDISMODULE_API_FUNC(RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr);\nint REDISMODULE_API_FUNC(RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey);\nint REDISMODULE_API_FUNC(RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval);\nint REDISMODULE_API_FUNC(RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval);\nRedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen);\nRedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key);\nvoid REDISMODULE_API_FUNC(RedisModule_DictIteratorStop)(RedisModuleDictIter *di);\nint REDISMODULE_API_FUNC(RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);\nint REDISMODULE_API_FUNC(RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);\nvoid *REDISMODULE_API_FUNC(RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);\nint REDISMODULE_API_FUNC(RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);\nint REDISMODULE_API_FUNC(RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);\nint REDISMODULE_API_FUNC(RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name);\nint REDISMODULE_API_FUNC(RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name);\nint REDISMODULE_API_FUNC(RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value);\nint REDISMODULE_API_FUNC(RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value);\nRedisModuleServerInfoData *REDISMODULE_API_FUNC(RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field);\nconst char *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field);\nlong long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);\nunsigned long long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);\ndouble REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err);\nint REDISMODULE_API_FUNC(RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback);\nint REDISMODULE_API_FUNC(RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle);\nint REDISMODULE_API_FUNC(RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle);\nint REDISMODULE_API_FUNC(RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq);\nint REDISMODULE_API_FUNC(RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq);\nRedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata);\nvoid REDISMODULE_API_FUNC(RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key);\nRedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx);\nRedisModuleScanCursor *REDISMODULE_API_FUNC(RedisModule_ScanCursorCreate)();\nvoid REDISMODULE_API_FUNC(RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor);\nvoid REDISMODULE_API_FUNC(RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor);\nint REDISMODULE_API_FUNC(RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata);\nint REDISMODULE_API_FUNC(RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata);\n/* Experimental APIs */\n#ifdef REDISMODULE_EXPERIMENTAL_API\n#define REDISMODULE_EXPERIMENTAL_API_VERSION 3\nRedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms);\nint REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata);\nint REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx);\nvoid *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx);\nRedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc);\nRedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx);\nint REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb);\nint REDISMODULE_API_FUNC(RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);\nint REDISMODULE_API_FUNC(RedisModule_GetNotifyKeyspaceEvents)();\nint REDISMODULE_API_FUNC(RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx);\nvoid REDISMODULE_API_FUNC(RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback);\nint REDISMODULE_API_FUNC(RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len);\nint REDISMODULE_API_FUNC(RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags);\nchar **REDISMODULE_API_FUNC(RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeClusterNodesList)(char **ids);\nRedisModuleTimerID REDISMODULE_API_FUNC(RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data);\nint REDISMODULE_API_FUNC(RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data);\nint REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data);\nconst char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void);\nsize_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(void);\nvoid REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);\nvoid REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback);\nvoid REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);\nint REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func);\nvoid *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname);\nRedisModuleCommandFilter *REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags);\nint REDISMODULE_API_FUNC(RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx);\nconst RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);\nint REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos);\nint REDISMODULE_API_FUNC(RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data);\nint REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode);\nint REDISMODULE_API_FUNC(RedisModule_KillForkChild)(int child_pid);\nfloat REDISMODULE_API_FUNC(RedisModule_GetUsedMemoryRatio)();\nsize_t REDISMODULE_API_FUNC(RedisModule_MallocSize)(void* ptr);\nRedisModuleUser *REDISMODULE_API_FUNC(RedisModule_CreateModuleUser)(const char *name);\nvoid REDISMODULE_API_FUNC(RedisModule_FreeModuleUser)(RedisModuleUser *user);\nint REDISMODULE_API_FUNC(RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl);\nint REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);\nint REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);\nvoid REDISMODULE_API_FUNC(RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id);\n#endif\n\n#define RedisModule_IsAOFClient(id) ((id) == CLIENT_ID_AOF)\n\n/* This is included inline inside each Redis module. */\nstatic int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));\nstatic int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {\n    void *getapifuncptr = ((void**)ctx)[0];\n    RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr;\n    REDISMODULE_GET_API(Alloc);\n    REDISMODULE_GET_API(Calloc);\n    REDISMODULE_GET_API(Free);\n    REDISMODULE_GET_API(Realloc);\n    REDISMODULE_GET_API(Strdup);\n    REDISMODULE_GET_API(CreateCommand);\n    REDISMODULE_GET_API(SetModuleAttribs);\n    REDISMODULE_GET_API(IsModuleNameBusy);\n    REDISMODULE_GET_API(WrongArity);\n    REDISMODULE_GET_API(ReplyWithLongLong);\n    REDISMODULE_GET_API(ReplyWithError);\n    REDISMODULE_GET_API(ReplyWithSimpleString);\n    REDISMODULE_GET_API(ReplyWithArray);\n    REDISMODULE_GET_API(ReplyWithNullArray);\n    REDISMODULE_GET_API(ReplyWithEmptyArray);\n    REDISMODULE_GET_API(ReplySetArrayLength);\n    REDISMODULE_GET_API(ReplyWithStringBuffer);\n    REDISMODULE_GET_API(ReplyWithCString);\n    REDISMODULE_GET_API(ReplyWithString);\n    REDISMODULE_GET_API(ReplyWithEmptyString);\n    REDISMODULE_GET_API(ReplyWithVerbatimString);\n    REDISMODULE_GET_API(ReplyWithNull);\n    REDISMODULE_GET_API(ReplyWithCallReply);\n    REDISMODULE_GET_API(ReplyWithDouble);\n    REDISMODULE_GET_API(ReplyWithLongDouble);\n    REDISMODULE_GET_API(GetSelectedDb);\n    REDISMODULE_GET_API(SelectDb);\n    REDISMODULE_GET_API(OpenKey);\n    REDISMODULE_GET_API(CloseKey);\n    REDISMODULE_GET_API(KeyType);\n    REDISMODULE_GET_API(ValueLength);\n    REDISMODULE_GET_API(ListPush);\n    REDISMODULE_GET_API(ListPop);\n    REDISMODULE_GET_API(StringToLongLong);\n    REDISMODULE_GET_API(StringToDouble);\n    REDISMODULE_GET_API(StringToLongDouble);\n    REDISMODULE_GET_API(Call);\n    REDISMODULE_GET_API(CallReplyProto);\n    REDISMODULE_GET_API(FreeCallReply);\n    REDISMODULE_GET_API(CallReplyInteger);\n    REDISMODULE_GET_API(CallReplyType);\n    REDISMODULE_GET_API(CallReplyLength);\n    REDISMODULE_GET_API(CallReplyArrayElement);\n    REDISMODULE_GET_API(CallReplyStringPtr);\n    REDISMODULE_GET_API(CreateStringFromCallReply);\n    REDISMODULE_GET_API(CreateString);\n    REDISMODULE_GET_API(CreateStringFromLongLong);\n    REDISMODULE_GET_API(CreateStringFromDouble);\n    REDISMODULE_GET_API(CreateStringFromLongDouble);\n    REDISMODULE_GET_API(CreateStringFromString);\n    REDISMODULE_GET_API(CreateStringPrintf);\n    REDISMODULE_GET_API(FreeString);\n    REDISMODULE_GET_API(StringPtrLen);\n    REDISMODULE_GET_API(AutoMemory);\n    REDISMODULE_GET_API(Replicate);\n    REDISMODULE_GET_API(ReplicateVerbatim);\n    REDISMODULE_GET_API(DeleteKey);\n    REDISMODULE_GET_API(UnlinkKey);\n    REDISMODULE_GET_API(StringSet);\n    REDISMODULE_GET_API(StringDMA);\n    REDISMODULE_GET_API(StringTruncate);\n    REDISMODULE_GET_API(GetExpire);\n    REDISMODULE_GET_API(SetExpire);\n    REDISMODULE_GET_API(ResetDataset);\n    REDISMODULE_GET_API(DbSize);\n    REDISMODULE_GET_API(RandomKey);\n    REDISMODULE_GET_API(ZsetAdd);\n    REDISMODULE_GET_API(ZsetIncrby);\n    REDISMODULE_GET_API(ZsetScore);\n    REDISMODULE_GET_API(ZsetRem);\n    REDISMODULE_GET_API(ZsetRangeStop);\n    REDISMODULE_GET_API(ZsetFirstInScoreRange);\n    REDISMODULE_GET_API(ZsetLastInScoreRange);\n    REDISMODULE_GET_API(ZsetFirstInLexRange);\n    REDISMODULE_GET_API(ZsetLastInLexRange);\n    REDISMODULE_GET_API(ZsetRangeCurrentElement);\n    REDISMODULE_GET_API(ZsetRangeNext);\n    REDISMODULE_GET_API(ZsetRangePrev);\n    REDISMODULE_GET_API(ZsetRangeEndReached);\n    REDISMODULE_GET_API(HashSet);\n    REDISMODULE_GET_API(HashGet);\n    REDISMODULE_GET_API(IsKeysPositionRequest);\n    REDISMODULE_GET_API(KeyAtPos);\n    REDISMODULE_GET_API(GetClientId);\n    REDISMODULE_GET_API(GetContextFlags);\n    REDISMODULE_GET_API(AvoidReplicaTraffic);\n    REDISMODULE_GET_API(PoolAlloc);\n    REDISMODULE_GET_API(CreateDataType);\n    REDISMODULE_GET_API(ModuleTypeSetValue);\n    REDISMODULE_GET_API(ModuleTypeReplaceValue);\n    REDISMODULE_GET_API(ModuleTypeGetType);\n    REDISMODULE_GET_API(ModuleTypeGetValue);\n    REDISMODULE_GET_API(IsIOError);\n    REDISMODULE_GET_API(SetModuleOptions);\n    REDISMODULE_GET_API(SignalModifiedKey);\n    REDISMODULE_GET_API(SaveUnsigned);\n    REDISMODULE_GET_API(LoadUnsigned);\n    REDISMODULE_GET_API(SaveSigned);\n    REDISMODULE_GET_API(LoadSigned);\n    REDISMODULE_GET_API(SaveString);\n    REDISMODULE_GET_API(SaveStringBuffer);\n    REDISMODULE_GET_API(LoadString);\n    REDISMODULE_GET_API(LoadStringBuffer);\n    REDISMODULE_GET_API(SaveDouble);\n    REDISMODULE_GET_API(LoadDouble);\n    REDISMODULE_GET_API(SaveFloat);\n    REDISMODULE_GET_API(LoadFloat);\n    REDISMODULE_GET_API(SaveLongDouble);\n    REDISMODULE_GET_API(LoadLongDouble);\n    REDISMODULE_GET_API(SaveDataTypeToString);\n    REDISMODULE_GET_API(LoadDataTypeFromString);\n    REDISMODULE_GET_API(EmitAOF);\n    REDISMODULE_GET_API(Log);\n    REDISMODULE_GET_API(LogIOError);\n    REDISMODULE_GET_API(_Assert);\n    REDISMODULE_GET_API(LatencyAddSample);\n    REDISMODULE_GET_API(StringAppendBuffer);\n    REDISMODULE_GET_API(RetainString);\n    REDISMODULE_GET_API(StringCompare);\n    REDISMODULE_GET_API(GetContextFromIO);\n    REDISMODULE_GET_API(GetKeyNameFromIO);\n    REDISMODULE_GET_API(GetKeyNameFromModuleKey);\n    REDISMODULE_GET_API(Milliseconds);\n    REDISMODULE_GET_API(DigestAddStringBuffer);\n    REDISMODULE_GET_API(DigestAddLongLong);\n    REDISMODULE_GET_API(DigestEndSequence);\n    REDISMODULE_GET_API(CreateDict);\n    REDISMODULE_GET_API(FreeDict);\n    REDISMODULE_GET_API(DictSize);\n    REDISMODULE_GET_API(DictSetC);\n    REDISMODULE_GET_API(DictReplaceC);\n    REDISMODULE_GET_API(DictSet);\n    REDISMODULE_GET_API(DictReplace);\n    REDISMODULE_GET_API(DictGetC);\n    REDISMODULE_GET_API(DictGet);\n    REDISMODULE_GET_API(DictDelC);\n    REDISMODULE_GET_API(DictDel);\n    REDISMODULE_GET_API(DictIteratorStartC);\n    REDISMODULE_GET_API(DictIteratorStart);\n    REDISMODULE_GET_API(DictIteratorStop);\n    REDISMODULE_GET_API(DictIteratorReseekC);\n    REDISMODULE_GET_API(DictIteratorReseek);\n    REDISMODULE_GET_API(DictNextC);\n    REDISMODULE_GET_API(DictPrevC);\n    REDISMODULE_GET_API(DictNext);\n    REDISMODULE_GET_API(DictPrev);\n    REDISMODULE_GET_API(DictCompare);\n    REDISMODULE_GET_API(DictCompareC);\n    REDISMODULE_GET_API(RegisterInfoFunc);\n    REDISMODULE_GET_API(InfoAddSection);\n    REDISMODULE_GET_API(InfoBeginDictField);\n    REDISMODULE_GET_API(InfoEndDictField);\n    REDISMODULE_GET_API(InfoAddFieldString);\n    REDISMODULE_GET_API(InfoAddFieldCString);\n    REDISMODULE_GET_API(InfoAddFieldDouble);\n    REDISMODULE_GET_API(InfoAddFieldLongLong);\n    REDISMODULE_GET_API(InfoAddFieldULongLong);\n    REDISMODULE_GET_API(GetServerInfo);\n    REDISMODULE_GET_API(FreeServerInfo);\n    REDISMODULE_GET_API(ServerInfoGetField);\n    REDISMODULE_GET_API(ServerInfoGetFieldC);\n    REDISMODULE_GET_API(ServerInfoGetFieldSigned);\n    REDISMODULE_GET_API(ServerInfoGetFieldUnsigned);\n    REDISMODULE_GET_API(ServerInfoGetFieldDouble);\n    REDISMODULE_GET_API(GetClientInfoById);\n    REDISMODULE_GET_API(PublishMessage);\n    REDISMODULE_GET_API(SubscribeToServerEvent);\n    REDISMODULE_GET_API(SetLRU);\n    REDISMODULE_GET_API(GetLRU);\n    REDISMODULE_GET_API(SetLFU);\n    REDISMODULE_GET_API(GetLFU);\n    REDISMODULE_GET_API(BlockClientOnKeys);\n    REDISMODULE_GET_API(SignalKeyAsReady);\n    REDISMODULE_GET_API(GetBlockedClientReadyKey);\n    REDISMODULE_GET_API(ScanCursorCreate);\n    REDISMODULE_GET_API(ScanCursorRestart);\n    REDISMODULE_GET_API(ScanCursorDestroy);\n    REDISMODULE_GET_API(Scan);\n    REDISMODULE_GET_API(ScanKey);\n\n#ifdef REDISMODULE_EXPERIMENTAL_API\n    REDISMODULE_GET_API(GetThreadSafeContext);\n    REDISMODULE_GET_API(FreeThreadSafeContext);\n    REDISMODULE_GET_API(ThreadSafeContextLock);\n    REDISMODULE_GET_API(ThreadSafeContextUnlock);\n    REDISMODULE_GET_API(BlockClient);\n    REDISMODULE_GET_API(UnblockClient);\n    REDISMODULE_GET_API(IsBlockedReplyRequest);\n    REDISMODULE_GET_API(IsBlockedTimeoutRequest);\n    REDISMODULE_GET_API(GetBlockedClientPrivateData);\n    REDISMODULE_GET_API(GetBlockedClientHandle);\n    REDISMODULE_GET_API(AbortBlock);\n    REDISMODULE_GET_API(SetDisconnectCallback);\n    REDISMODULE_GET_API(SubscribeToKeyspaceEvents);\n    REDISMODULE_GET_API(NotifyKeyspaceEvent);\n    REDISMODULE_GET_API(GetNotifyKeyspaceEvents);\n    REDISMODULE_GET_API(BlockedClientDisconnected);\n    REDISMODULE_GET_API(RegisterClusterMessageReceiver);\n    REDISMODULE_GET_API(SendClusterMessage);\n    REDISMODULE_GET_API(GetClusterNodeInfo);\n    REDISMODULE_GET_API(GetClusterNodesList);\n    REDISMODULE_GET_API(FreeClusterNodesList);\n    REDISMODULE_GET_API(CreateTimer);\n    REDISMODULE_GET_API(StopTimer);\n    REDISMODULE_GET_API(GetTimerInfo);\n    REDISMODULE_GET_API(GetMyClusterID);\n    REDISMODULE_GET_API(GetClusterSize);\n    REDISMODULE_GET_API(GetRandomBytes);\n    REDISMODULE_GET_API(GetRandomHexChars);\n    REDISMODULE_GET_API(SetClusterFlags);\n    REDISMODULE_GET_API(ExportSharedAPI);\n    REDISMODULE_GET_API(GetSharedAPI);\n    REDISMODULE_GET_API(RegisterCommandFilter);\n    REDISMODULE_GET_API(UnregisterCommandFilter);\n    REDISMODULE_GET_API(CommandFilterArgsCount);\n    REDISMODULE_GET_API(CommandFilterArgGet);\n    REDISMODULE_GET_API(CommandFilterArgInsert);\n    REDISMODULE_GET_API(CommandFilterArgReplace);\n    REDISMODULE_GET_API(CommandFilterArgDelete);\n    REDISMODULE_GET_API(Fork);\n    REDISMODULE_GET_API(ExitFromChild);\n    REDISMODULE_GET_API(KillForkChild);\n    REDISMODULE_GET_API(GetUsedMemoryRatio);\n    REDISMODULE_GET_API(MallocSize);\n    REDISMODULE_GET_API(CreateModuleUser);\n    REDISMODULE_GET_API(FreeModuleUser);\n    REDISMODULE_GET_API(SetModuleUserACL);\n    REDISMODULE_GET_API(DeauthenticateAndCloseClient);\n    REDISMODULE_GET_API(AuthenticateClientWithACLUser);\n    REDISMODULE_GET_API(AuthenticateClientWithUser);\n#endif\n\n    if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;\n    RedisModule_SetModuleAttribs(ctx,name,ver,apiver);\n    return REDISMODULE_OK;\n}\n\n#define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1)))\n\n#else\n\n/* Things only defined for the modules core, not exported to modules\n * including this file. */\n#define RedisModuleString robj\n\n#endif /* REDISMODULE_CORE */\n#endif /* REDISMOUDLE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <stdio.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\n/* Return a cached value of the build string in order to avoid recomputing\n * and converting it in hex every time: this string is shown in the INFO\n * output that should be fast. */\nchar *redisBuildIdString(void) {\n    static char buf[32];\n    static int cached = 0;\n    if (!cached) {\n        snprintf(buf,sizeof(buf),\"%llx\",(unsigned long long) redisBuildId());\n        cached = 1;\n    }\n    return buf;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/replication.c",
    "content": "/* Asynchronous replication 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\n#include \"server.h\"\n#include \"cluster.h\"\n#include \"bio.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\nlong long adjustMeaningfulReplOffset();\nvoid replicationDiscardCachedMaster(void);\nvoid replicationResurrectCachedMaster(connection *conn);\nvoid replicationSendAck(void);\nvoid putSlaveOnline(client *slave);\nint cancelReplicationHandshake(void);\n\n/* We take a global flag to remember if this instance generated an RDB\n * because of replication, so that we can remove the RDB file in case\n * the instance is configured to have no persistence. */\nint RDBGeneratedByReplication = 0;\n\n/* --------------------------- Utility functions ---------------------------- */\n\n/* Return the pointer to a string representing the slave ip:listening_port\n * pair. Mostly useful for logging, since we want to log a slave using its\n * IP address and its listening port which is more clear for the user, for\n * example: \"Closing connection with replica 10.1.2.3:6380\". */\nchar *replicationGetSlaveName(client *c) {\n    static char buf[NET_PEER_ID_LEN];\n    char ip[NET_IP_STR_LEN];\n\n    ip[0] = '\\0';\n    buf[0] = '\\0';\n    if (c->slave_ip[0] != '\\0' ||\n        connPeerToString(c->conn,ip,sizeof(ip),NULL) != -1)\n    {\n        /* Note that the 'ip' buffer is always larger than 'c->slave_ip' */\n        if (c->slave_ip[0] != '\\0') memcpy(ip,c->slave_ip,sizeof(c->slave_ip));\n\n        if (c->slave_listening_port)\n            anetFormatAddr(buf,sizeof(buf),ip,c->slave_listening_port);\n        else\n            snprintf(buf,sizeof(buf),\"%s:<unknown-replica-port>\",ip);\n    } else {\n        snprintf(buf,sizeof(buf),\"client id #%llu\",\n            (unsigned long long) c->id);\n    }\n    return buf;\n}\n\n/* Plain unlink() can block for quite some time in order to actually apply\n * the file deletion to the filesystem. This call removes the file in a\n * background thread instead. We actually just do close() in the thread,\n * by using the fact that if there is another instance of the same file open,\n * the foreground unlink() will not really do anything, and deleting the\n * file will only happen once the last reference is lost. */\nint bg_unlink(const char *filename) {\n    int fd = open(filename,O_RDONLY|O_NONBLOCK);\n    if (fd == -1) {\n        /* Can't open the file? Fall back to unlinking in the main thread. */\n        return unlink(filename);\n    } else {\n        /* The following unlink() will not do anything since file\n         * is still open. */\n        int retval = unlink(filename);\n        if (retval == -1) {\n            /* If we got an unlink error, we just return it, closing the\n             * new reference we have to the file. */\n            int old_errno = errno;\n            close(fd);  /* This would overwrite our errno. So we saved it. */\n            errno = old_errno;\n            return -1;\n        }\n        bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)fd,NULL,NULL);\n        return 0; /* Success. */\n    }\n}\n\n/* ---------------------------------- MASTER -------------------------------- */\n\nvoid createReplicationBacklog(void) {\n    serverAssert(server.repl_backlog == NULL);\n    server.repl_backlog = zmalloc(server.repl_backlog_size);\n    server.repl_backlog_histlen = 0;\n    server.repl_backlog_idx = 0;\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    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). */\nvoid resizeReplicationBacklog(long long newsize) {\n    if (newsize < CONFIG_REPL_BACKLOG_MIN_SIZE)\n        newsize = CONFIG_REPL_BACKLOG_MIN_SIZE;\n    if (server.repl_backlog_size == newsize) return;\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        zfree(server.repl_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 empty. */\n        server.repl_backlog_off = server.master_repl_offset+1;\n    }\n}\n\nvoid freeReplicationBacklog(void) {\n    serverAssert(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 offset. */\nvoid feedReplicationBacklog(void *ptr, size_t len) {\n    unsigned char *p = ptr;\n\n    server.master_repl_offset += len;\n    server.master_repl_meaningful_offset = server.master_repl_offset;\n\n    /* This is a circular buffer, so write as much data we can at every\n     * iteration and rewind the \"idx\" index if we reach the limit. */\n    while(len) {\n        size_t thislen = server.repl_backlog_size - server.repl_backlog_idx;\n        if (thislen > len) thislen = len;\n        memcpy(server.repl_backlog+server.repl_backlog_idx,p,thislen);\n        server.repl_backlog_idx += thislen;\n        if (server.repl_backlog_idx == server.repl_backlog_size)\n            server.repl_backlog_idx = 0;\n        len -= thislen;\n        p += thislen;\n        server.repl_backlog_histlen += thislen;\n    }\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    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. */\nvoid feedReplicationBacklogWithObject(robj *o) {\n    char llstr[LONG_STR_SIZE];\n    void *p;\n    size_t len;\n\n    if (o->encoding == OBJ_ENCODING_INT) {\n        len = ll2string(llstr,sizeof(llstr),(long)o->ptr);\n        p = llstr;\n    } else {\n        len = sdslen(o->ptr);\n        p = o->ptr;\n    }\n    feedReplicationBacklog(p,len);\n}\n\n/* Propagate write commands to slaves, and populate the replication backlog\n * as well. This function is used if the instance is a master: we use\n * the commands received by our clients in order to create the replication\n * stream. Instead if the instance is a slave and has sub-slaves attached,\n * we use replicationFeedSlavesFromMaster() */\nvoid replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {\n    listNode *ln;\n    listIter li;\n    int j, len;\n    char llstr[LONG_STR_SIZE];\n\n    /* If the instance is not a top level master, return ASAP: we'll just proxy\n     * the stream of data we receive from our master instead, in order to\n     * propagate *identical* replication stream. In this way this slave can\n     * advertise the same replication ID as the master (since it shares the\n     * master replication history and has the same backlog and offsets). */\n    if (server.masterhost != NULL) return;\n\n    /* If there aren't slaves, and there is no backlog buffer to populate,\n     * we can return ASAP. */\n    if (server.repl_backlog == NULL && listLength(slaves) == 0) return;\n\n    /* We can't have slaves attached and no backlog. */\n    serverAssert(!(listLength(slaves) != 0 && server.repl_backlog == NULL));\n\n    /* Send SELECT command to every slave if needed. */\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 < PROTO_SHARED_SELECT_CMDS) {\n            selectcmd = shared.select[dictid];\n        } else {\n            int dictid_len;\n\n            dictid_len = ll2string(llstr,sizeof(llstr),dictid);\n            selectcmd = createObject(OBJ_STRING,\n                sdscatprintf(sdsempty(),\n                \"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n                dictid_len, llstr));\n        }\n\n        /* Add the SELECT command into the backlog. */\n        if (server.repl_backlog) feedReplicationBacklogWithObject(selectcmd);\n\n        /* Send it to slaves. */\n        listRewind(slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n            addReply(slave,selectcmd);\n        }\n\n        if (dictid < 0 || dictid >= PROTO_SHARED_SELECT_CMDS)\n            decrRefCount(selectcmd);\n    }\n    server.slaveseldb = dictid;\n\n    /* Write the command to the replication backlog if any. */\n    if (server.repl_backlog) {\n        char aux[LONG_STR_SIZE+3];\n\n        /* Add the multi bulk reply length. */\n        aux[0] = '*';\n        len = ll2string(aux+1,sizeof(aux)-1,argc);\n        aux[len+1] = '\\r';\n        aux[len+2] = '\\n';\n        feedReplicationBacklog(aux,len+3);\n\n        for (j = 0; j < argc; j++) {\n            long objlen = stringObjectLen(argv[j]);\n\n            /* We need to feed the buffer with the object as a bulk reply\n             * not just as a plain string, so create the $..CRLF payload len\n             * and add the final CRLF */\n            aux[0] = '$';\n            len = ll2string(aux+1,sizeof(aux)-1,objlen);\n            aux[len+1] = '\\r';\n            aux[len+2] = '\\n';\n            feedReplicationBacklog(aux,len+3);\n            feedReplicationBacklogWithObject(argv[j]);\n            feedReplicationBacklog(aux+len+1,2);\n        }\n    }\n\n    /* Write the command to every slave. */\n    listRewind(slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        /* Don't feed slaves that are still waiting for BGSAVE to start. */\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n\n        /* Feed slaves that are waiting for the initial SYNC (so these commands\n         * are queued in the output buffer until the initial SYNC completes),\n         * or are already in sync with the master. */\n\n        /* Add the multi bulk length. */\n        addReplyArrayLen(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/* This function is used in order to proxy what we receive from our master\n * to our sub-slaves. */\n#include <ctype.h>\nvoid replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen) {\n    listNode *ln;\n    listIter li;\n\n    /* Debugging: this is handy to see the stream sent from master\n     * to slaves. Disabled with if(0). */\n    if (0) {\n        printf(\"%zu:\",buflen);\n        for (size_t j = 0; j < buflen; j++) {\n            printf(\"%c\", isprint(buf[j]) ? buf[j] : '.');\n        }\n        printf(\"\\n\");\n    }\n\n    if (server.repl_backlog) feedReplicationBacklog(buf,buflen);\n    listRewind(slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        /* Don't feed slaves that are still waiting for BGSAVE to start. */\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;\n        addReplyProto(slave,buf,buflen);\n    }\n}\n\nvoid replicationFeedMonitors(client *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    gettimeofday(&tv,NULL);\n    cmdrepr = sdscatprintf(cmdrepr,\"%ld.%06ld \",(long)tv.tv_sec,(long)tv.tv_usec);\n    if (c->flags & CLIENT_LUA) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d lua] \",dictid);\n    } else if (c->flags & CLIENT_UNIX_SOCKET) {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d unix:%s] \",dictid,server.unixsocket);\n    } else {\n        cmdrepr = sdscatprintf(cmdrepr,\"[%d %s] \",dictid,getClientPeerId(c));\n    }\n\n    for (j = 0; j < argc; j++) {\n        if (argv[j]->encoding == OBJ_ENCODING_INT) {\n            cmdrepr = sdscatprintf(cmdrepr, \"\\\"%ld\\\"\", (long)argv[j]->ptr);\n        } else {\n            cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr,\n                        sdslen(argv[j]->ptr));\n        }\n        if (j != argc-1)\n            cmdrepr = sdscatlen(cmdrepr,\" \",1);\n    }\n    cmdrepr = sdscatlen(cmdrepr,\"\\r\\n\",2);\n    cmdobj = createObject(OBJ_STRING,cmdrepr);\n\n    listRewind(monitors,&li);\n    while((ln = listNext(&li))) {\n        client *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. */\nlong long addReplyReplicationBacklog(client *c, long long offset) {\n    long long j, skip, len;\n\n    serverLog(LL_DEBUG, \"[PSYNC] Replica request offset: %lld\", offset);\n\n    if (server.repl_backlog_histlen == 0) {\n        serverLog(LL_DEBUG, \"[PSYNC] Backlog history len is zero\");\n        return 0;\n    }\n\n    serverLog(LL_DEBUG, \"[PSYNC] Backlog size: %lld\",\n             server.repl_backlog_size);\n    serverLog(LL_DEBUG, \"[PSYNC] First byte: %lld\",\n             server.repl_backlog_off);\n    serverLog(LL_DEBUG, \"[PSYNC] History len: %lld\",\n             server.repl_backlog_histlen);\n    serverLog(LL_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    serverLog(LL_DEBUG, \"[PSYNC] Skipping: %lld\", skip);\n\n    /* Point j to the oldest byte, that is actually 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    serverLog(LL_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    serverLog(LL_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        serverLog(LL_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/* Return the offset to provide as reply to the PSYNC command received\n * from the slave. The returned value is only valid immediately after\n * the BGSAVE process started and before executing any other command\n * from clients. */\nlong long getPsyncInitialOffset(void) {\n    return server.master_repl_offset;\n}\n\n/* Send a FULLRESYNC reply in the specific case of a full resynchronization,\n * as a side effect setup the slave for a full sync in different ways:\n *\n * 1) Remember, into the slave client structure, the replication offset\n *    we sent here, so that if new slaves will later attach to the same\n *    background RDB saving process (by duplicating this client output\n *    buffer), we can get the right offset from this slave.\n * 2) Set the replication state of the slave to WAIT_BGSAVE_END so that\n *    we start accumulating differences from this point.\n * 3) Force the replication stream to re-emit a SELECT statement so\n *    the new slave incremental differences will start selecting the\n *    right database number.\n *\n * Normally this function should be called immediately after a successful\n * BGSAVE for replication was started, or when there is one already in\n * progress that we attached our slave to. */\nint replicationSetupSlaveForFullResync(client *slave, long long offset) {\n    char buf[128];\n    int buflen;\n\n    slave->psync_initial_offset = offset;\n    slave->replstate = SLAVE_STATE_WAIT_BGSAVE_END;\n    /* We are going to accumulate the incremental changes for this\n     * slave as well. Set slaveseldb to -1 in order to force to re-emit\n     * a SELECT statement in the replication stream. */\n    server.slaveseldb = -1;\n\n    /* Don't send this reply to slaves that approached us with\n     * the old SYNC command. */\n    if (!(slave->flags & CLIENT_PRE_PSYNC)) {\n        buflen = snprintf(buf,sizeof(buf),\"+FULLRESYNC %s %lld\\r\\n\",\n                          server.replid,offset);\n        if (connWrite(slave->conn,buf,buflen) != buflen) {\n            freeClientAsync(slave);\n            return C_ERR;\n        }\n    }\n    return C_OK;\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 C_OK, otherwise C_ERR is returned and we proceed\n * with the usual full resync. */\nint masterTryPartialResynchronization(client *c) {\n    long long psync_offset, psync_len;\n    char *master_replid = c->argv[1]->ptr;\n    char buf[128];\n    int buflen;\n\n    /* Parse the replication offset asked by the slave. Go to full sync\n     * on parse error: this should never happen but we try to handle\n     * it in a robust way compared to aborting. */\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&psync_offset,NULL) !=\n       C_OK) goto need_full_resync;\n\n    /* Is the replication ID of this master the same advertised by the wannabe\n     * slave via PSYNC? If the replication ID changed this master has a\n     * different replication history, and there is no way to continue.\n     *\n     * Note that there are two potentially valid replication IDs: the ID1\n     * and the ID2. The ID2 however is only valid up to a specific offset. */\n    if (strcasecmp(master_replid, server.replid) &&\n        (strcasecmp(master_replid, server.replid2) ||\n         psync_offset > server.second_replid_offset))\n    {\n        /* Run id \"?\" is used by slaves that want to force a full resync. */\n        if (master_replid[0] != '?') {\n            if (strcasecmp(master_replid, server.replid) &&\n                strcasecmp(master_replid, server.replid2))\n            {\n                serverLog(LL_NOTICE,\"Partial resynchronization not accepted: \"\n                    \"Replication ID mismatch (Replica asked for '%s', my \"\n                    \"replication IDs are '%s' and '%s')\",\n                    master_replid, server.replid, server.replid2);\n            } else {\n                serverLog(LL_NOTICE,\"Partial resynchronization not accepted: \"\n                    \"Requested offset for second ID was %lld, but I can reply \"\n                    \"up to %lld\", psync_offset, server.second_replid_offset);\n            }\n        } else {\n            serverLog(LL_NOTICE,\"Full resync requested by replica %s\",\n                replicationGetSlaveName(c));\n        }\n        goto need_full_resync;\n    }\n\n    /* We still have the data our slave is asking for? */\n    if (!server.repl_backlog ||\n        psync_offset < server.repl_backlog_off ||\n        psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen))\n    {\n        serverLog(LL_NOTICE,\n            \"Unable to partial resync with replica %s for lack of backlog (Replica request was: %lld).\", replicationGetSlaveName(c), psync_offset);\n        if (psync_offset > server.master_repl_offset) {\n            serverLog(LL_WARNING,\n                \"Warning: replica %s tried to PSYNC with an offset that is greater than the master replication offset.\", replicationGetSlaveName(c));\n        }\n        goto need_full_resync;\n    }\n\n    /* If we reached this point, we are able to perform a partial resync:\n     * 1) Set client state to make it a slave.\n     * 2) Inform the client we can continue with +CONTINUE\n     * 3) Send the backlog data (from the offset to the end) to the slave. */\n    c->flags |= CLIENT_SLAVE;\n    c->replstate = SLAVE_STATE_ONLINE;\n    c->repl_ack_time = server.unixtime;\n    c->repl_put_online_on_ack = 0;\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     * empty so this write will never fail actually. */\n    if (c->slave_capa & SLAVE_CAPA_PSYNC2) {\n        buflen = snprintf(buf,sizeof(buf),\"+CONTINUE %s\\r\\n\", server.replid);\n    } else {\n        buflen = snprintf(buf,sizeof(buf),\"+CONTINUE\\r\\n\");\n    }\n    if (connWrite(c->conn,buf,buflen) != buflen) {\n        freeClientAsync(c);\n        return C_OK;\n    }\n    psync_len = addReplyReplicationBacklog(c,psync_offset);\n    serverLog(LL_NOTICE,\n        \"Partial resynchronization request from %s accepted. Sending %lld bytes of backlog starting from offset %lld.\",\n            replicationGetSlaveName(c),\n            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    refreshGoodSlavesCount();\n\n    /* Fire the replica change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,\n                          REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE,\n                          NULL);\n\n    return C_OK; /* The caller can return, no full resync needed. */\n\nneed_full_resync:\n    /* We need a full resync for some reason... Note that we can't\n     * reply to PSYNC right now if a full SYNC is needed. The reply\n     * must include the master offset at the time the RDB file we transfer\n     * is generated, so we need to delay the reply to that moment. */\n    return C_ERR;\n}\n\n/* Start a BGSAVE for replication goals, which is, selecting the disk or\n * socket target depending on the configuration, and making sure that\n * the script cache is flushed before to start.\n *\n * The mincapa argument is the bitwise AND among all the slaves capabilities\n * of the slaves waiting for this BGSAVE, so represents the slave capabilities\n * all the slaves support. Can be tested via SLAVE_CAPA_* macros.\n *\n * Side effects, other than starting a BGSAVE:\n *\n * 1) Handle the slaves in WAIT_START state, by preparing them for a full\n *    sync if the BGSAVE was successfully started, or sending them an error\n *    and dropping them from the list of slaves.\n *\n * 2) Flush the Lua scripting script cache if the BGSAVE was actually\n *    started.\n *\n * Returns C_OK on success or C_ERR otherwise. */\nint startBgsaveForReplication(int mincapa) {\n    int retval;\n    int socket_target = server.repl_diskless_sync && (mincapa & SLAVE_CAPA_EOF);\n    listIter li;\n    listNode *ln;\n\n    serverLog(LL_NOTICE,\"Starting BGSAVE for SYNC with target: %s\",\n        socket_target ? \"replicas sockets\" : \"disk\");\n\n    rdbSaveInfo rsi, *rsiptr;\n    rsiptr = rdbPopulateSaveInfo(&rsi);\n    /* Only do rdbSave* when rsiptr is not NULL,\n     * otherwise slave will miss repl-stream-db. */\n    if (rsiptr) {\n        if (socket_target)\n            retval = rdbSaveToSlavesSockets(rsiptr);\n        else\n            retval = rdbSaveBackground(server.rdb_filename,rsiptr);\n    } else {\n        serverLog(LL_WARNING,\"BGSAVE for replication: replication information not available, can't generate the RDB file right now. Try later.\");\n        retval = C_ERR;\n    }\n\n    /* If we succeeded to start a BGSAVE with disk target, let's remember\n     * this fact, so that we can later delete the file if needed. Note\n     * that we don't set the flag to 1 if the feature is disabled, otherwise\n     * it would never be cleared: the file is not deleted. This way if\n     * the user enables it later with CONFIG SET, we are fine. */\n    if (retval == C_OK && !socket_target && server.rdb_del_sync_files)\n        RDBGeneratedByReplication = 1;\n\n    /* If we failed to BGSAVE, remove the slaves waiting for a full\n     * resynchronization from the list of slaves, inform them with\n     * an error about what happened, close the connection ASAP. */\n    if (retval == C_ERR) {\n        serverLog(LL_WARNING,\"BGSAVE for replication failed\");\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n                slave->replstate = REPL_STATE_NONE;\n                slave->flags &= ~CLIENT_SLAVE;\n                listDelNode(server.slaves,ln);\n                addReplyError(slave,\n                    \"BGSAVE failed, replication can't continue\");\n                slave->flags |= CLIENT_CLOSE_AFTER_REPLY;\n            }\n        }\n        return retval;\n    }\n\n    /* If the target is socket, rdbSaveToSlavesSockets() already setup\n     * the slaves for a full resync. Otherwise for disk target do it now.*/\n    if (!socket_target) {\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n                    replicationSetupSlaveForFullResync(slave,\n                            getPsyncInitialOffset());\n            }\n        }\n    }\n\n    /* Flush the script cache, since we need that slave differences are\n     * accumulated without requiring slaves to match our cached scripts. */\n    if (retval == C_OK) replicationScriptCacheFlush();\n    return retval;\n}\n\n/* SYNC and PSYNC command implemenation. */\nvoid syncCommand(client *c) {\n    /* ignore SYNC if already slave or in monitor mode */\n    if (c->flags & CLIENT_SLAVE) return;\n\n    /* Refuse SYNC requests if we are a slave but the link with our master\n     * is not ok... */\n    if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED) {\n        addReplySds(c,sdsnew(\"-NOMASTERLINK Can't SYNC while not connected with my master\\r\\n\"));\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    if (clientHasPendingReplies(c)) {\n        addReplyError(c,\"SYNC and PSYNC are invalid with pending output\");\n        return;\n    }\n\n    serverLog(LL_NOTICE,\"Replica %s asks for synchronization\",\n        replicationGetSlaveName(c));\n\n    /* Try a partial resynchronization if this is a PSYNC command.\n     * If it fails, we continue with usual full resynchronization, however\n     * when this happens masterTryPartialResynchronization() already\n     * replied with:\n     *\n     * +FULLRESYNC <replid> <offset>\n     *\n     * So the slave knows the new replid and offset to try a PSYNC later\n     * if the connection with the master is lost. */\n    if (!strcasecmp(c->argv[0]->ptr,\"psync\")) {\n        if (masterTryPartialResynchronization(c) == C_OK) {\n            server.stat_sync_partial_ok++;\n            return; /* No full resync needed, return. */\n        } else {\n            char *master_replid = c->argv[1]->ptr;\n\n            /* Increment stats for failed PSYNCs, but only if the\n             * replid 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_replid[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        c->flags |= CLIENT_PRE_PSYNC;\n    }\n\n    /* Full resynchronization. */\n    server.stat_sync_full++;\n\n    /* Setup the slave as one waiting for BGSAVE to start. The following code\n     * paths will change the state if we handle the slave differently. */\n    c->replstate = SLAVE_STATE_WAIT_BGSAVE_START;\n    if (server.repl_disable_tcp_nodelay)\n        connDisableTcpNoDelay(c->conn); /* Non critical if it fails. */\n    c->repldbfd = -1;\n    c->flags |= CLIENT_SLAVE;\n    listAddNodeTail(server.slaves,c);\n\n    /* Create the replication backlog if needed. */\n    if (listLength(server.slaves) == 1 && server.repl_backlog == NULL) {\n        /* When we create the backlog from scratch, we always use a new\n         * replication ID and clear the ID2, since there is no valid\n         * past history. */\n        changeReplicationId();\n        clearReplicationId2();\n        createReplicationBacklog();\n    }\n\n    /* CASE 1: BGSAVE is in progress, with disk target. */\n    if (server.rdb_child_pid != -1 &&\n        server.rdb_child_type == RDB_CHILD_TYPE_DISK)\n    {\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        client *slave;\n        listNode *ln;\n        listIter li;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) break;\n        }\n        /* To attach this slave, we check that it has at least all the\n         * capabilities of the slave that triggered the current BGSAVE. */\n        if (ln && ((c->slave_capa & slave->slave_capa) == slave->slave_capa)) {\n            /* Perfect, the server is already registering differences for\n             * another slave. Set the right state, and copy the buffer. */\n            copyClientOutputBuffer(c,slave);\n            replicationSetupSlaveForFullResync(c,slave->psync_initial_offset);\n            serverLog(LL_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            serverLog(LL_NOTICE,\"Can't attach the replica to the current BGSAVE. Waiting for next BGSAVE for SYNC\");\n        }\n\n    /* CASE 2: BGSAVE is in progress, with socket target. */\n    } else if (server.rdb_child_pid != -1 &&\n               server.rdb_child_type == RDB_CHILD_TYPE_SOCKET)\n    {\n        /* There is an RDB child process but it is writing directly to\n         * children sockets. We need to wait for the next BGSAVE\n         * in order to synchronize. */\n        serverLog(LL_NOTICE,\"Current BGSAVE has socket target. Waiting for next BGSAVE for SYNC\");\n\n    /* CASE 3: There is no BGSAVE is progress. */\n    } else {\n        if (server.repl_diskless_sync && (c->slave_capa & SLAVE_CAPA_EOF)) {\n            /* Diskless replication RDB child is created inside\n             * replicationCron() since we want to delay its start a\n             * few seconds to wait for more slaves to arrive. */\n            if (server.repl_diskless_sync_delay)\n                serverLog(LL_NOTICE,\"Delay next BGSAVE for diskless SYNC\");\n        } else {\n            /* Target is disk (or the slave is not capable of supporting\n             * diskless replication) and we don't have a BGSAVE in progress,\n             * let's start one. */\n            if (!hasActiveChildProcess()) {\n                startBgsaveForReplication(c->slave_capa);\n            } else {\n                serverLog(LL_NOTICE,\n                    \"No BGSAVE in progress, but another BG operation is active. \"\n                    \"BGSAVE for replication delayed\");\n            }\n        }\n    }\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 * Currently the only use of this command is to communicate to the master\n * what is the listening port of the Slave redis instance, so that the\n * master can accurately list slaves and their listening ports in\n * the INFO output.\n *\n * In the future the same command can be used in order to configure\n * the replication to initiate an incremental replication instead of a\n * full resync. */\nvoid replconfCommand(client *c) {\n    int j;\n\n    if ((c->argc % 2) == 0) {\n        /* Number of arguments must be odd to make sure that every\n         * option has a corresponding value. */\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Process every option-value pair. */\n    for (j = 1; j < c->argc; j+=2) {\n        if (!strcasecmp(c->argv[j]->ptr,\"listening-port\")) {\n            long port;\n\n            if ((getLongFromObjectOrReply(c,c->argv[j+1],\n                    &port,NULL) != C_OK))\n                return;\n            c->slave_listening_port = port;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"ip-address\")) {\n            sds ip = c->argv[j+1]->ptr;\n            if (sdslen(ip) < sizeof(c->slave_ip)) {\n                memcpy(c->slave_ip,ip,sdslen(ip)+1);\n            } else {\n                addReplyErrorFormat(c,\"REPLCONF ip-address provided by \"\n                    \"replica instance is too long: %zd bytes\", sdslen(ip));\n                return;\n            }\n        } else if (!strcasecmp(c->argv[j]->ptr,\"capa\")) {\n            /* Ignore capabilities not understood by this master. */\n            if (!strcasecmp(c->argv[j+1]->ptr,\"eof\"))\n                c->slave_capa |= SLAVE_CAPA_EOF;\n            else if (!strcasecmp(c->argv[j+1]->ptr,\"psync2\"))\n                c->slave_capa |= SLAVE_CAPA_PSYNC2;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"ack\")) {\n            /* REPLCONF ACK is used by slave to inform the master the amount\n             * of replication stream that it processed so far. It is an\n             * internal only command that normal clients should never use. */\n            long long offset;\n\n            if (!(c->flags & CLIENT_SLAVE)) return;\n            if ((getLongLongFromObject(c->argv[j+1], &offset) != C_OK))\n                return;\n            if (offset > c->repl_ack_off)\n                c->repl_ack_off = offset;\n            c->repl_ack_time = server.unixtime;\n            /* If this was a diskless replication, we need to really put\n             * the slave online when the first ACK is received (which\n             * confirms slave is online and ready to get more data). This\n             * allows for simpler and less CPU intensive EOF detection\n             * when streaming RDB files. */\n            if (c->repl_put_online_on_ack && c->replstate == SLAVE_STATE_ONLINE)\n                putSlaveOnline(c);\n            /* Note: this command does not reply anything! */\n            return;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"getack\")) {\n            /* REPLCONF GETACK is used in order to request an ACK ASAP\n             * to the slave. */\n            if (server.masterhost && server.master) replicationSendAck();\n            return;\n        } else {\n            addReplyErrorFormat(c,\"Unrecognized REPLCONF option: %s\",\n                (char*)c->argv[j]->ptr);\n            return;\n        }\n    }\n    addReply(c,shared.ok);\n}\n\n/* This function puts a replica in the online state, and should be called just\n * after a replica received the RDB file for the initial synchronization, and\n * we are finally ready to send the incremental stream of commands.\n *\n * It does a few things:\n *\n * 1) Put the slave in ONLINE state. Note that the function may also be called\n *    for a replicas that are already in ONLINE state, but having the flag\n *    repl_put_online_on_ack set to true: we still have to install the write\n *    handler in that case. This function will take care of that.\n * 2) Make sure the writable event is re-installed, since calling the SYNC\n *    command disables it, so that we can accumulate output buffer without\n *    sending it to the replica.\n * 3) Update the count of \"good replicas\". */\nvoid putSlaveOnline(client *slave) {\n    slave->replstate = SLAVE_STATE_ONLINE;\n    slave->repl_put_online_on_ack = 0;\n    slave->repl_ack_time = server.unixtime; /* Prevent false timeout. */\n    if (connSetWriteHandler(slave->conn, sendReplyToClient) == C_ERR) {\n        serverLog(LL_WARNING,\"Unable to register writable event for replica bulk transfer: %s\", strerror(errno));\n        freeClient(slave);\n        return;\n    }\n    refreshGoodSlavesCount();\n    /* Fire the replica change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,\n                          REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE,\n                          NULL);\n    serverLog(LL_NOTICE,\"Synchronization with replica %s succeeded\",\n        replicationGetSlaveName(slave));\n}\n\n/* We call this function periodically to remove an RDB file that was\n * generated because of replication, in an instance that is otherwise\n * without any persistence. We don't want instances without persistence\n * to take RDB files around, this violates certain policies in certain\n * environments. */\nvoid removeRDBUsedToSyncReplicas(void) {\n    /* If the feature is disabled, return ASAP but also clear the\n     * RDBGeneratedByReplication flag in case it was set. Otherwise if the\n     * feature was enabled, but gets disabled later with CONFIG SET, the\n     * flag may remain set to one: then next time the feature is re-enabled\n     * via CONFIG SET we have have it set even if no RDB was generated\n     * because of replication recently. */\n    if (!server.rdb_del_sync_files) {\n        RDBGeneratedByReplication = 0;\n        return;\n    }\n\n    if (allPersistenceDisabled() && RDBGeneratedByReplication) {\n        client *slave;\n        listNode *ln;\n        listIter li;\n\n        int delrdb = 1;\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||\n                slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END ||\n                slave->replstate == SLAVE_STATE_SEND_BULK)\n            {\n                delrdb = 0;\n                break; /* No need to check the other replicas. */\n            }\n        }\n        if (delrdb) {\n            struct stat sb;\n            if (lstat(server.rdb_filename,&sb) != -1) {\n                RDBGeneratedByReplication = 0;\n                serverLog(LL_NOTICE,\n                    \"Removing the RDB file used to feed replicas \"\n                    \"in a persistence-less instance\");\n                bg_unlink(server.rdb_filename);\n            }\n        }\n    }\n}\n\nvoid sendBulkToSlave(connection *conn) {\n    client *slave = connGetPrivateData(conn);\n    char buf[PROTO_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 = connWrite(conn,slave->replpreamble,sdslen(slave->replpreamble));\n        if (nwritten == -1) {\n            serverLog(LL_VERBOSE,\n                \"Write error sending RDB preamble to replica: %s\",\n                connGetLastError(conn));\n            freeClient(slave);\n            return;\n        }\n        server.stat_net_output_bytes += nwritten;\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 transferred, send the RDB bulk data. */\n    lseek(slave->repldbfd,slave->repldboff,SEEK_SET);\n    buflen = read(slave->repldbfd,buf,PROTO_IOBUF_LEN);\n    if (buflen <= 0) {\n        serverLog(LL_WARNING,\"Read error sending DB to replica: %s\",\n            (buflen == 0) ? \"premature EOF\" : strerror(errno));\n        freeClient(slave);\n        return;\n    }\n    if ((nwritten = connWrite(conn,buf,buflen)) == -1) {\n        if (connGetState(conn) != CONN_STATE_CONNECTED) {\n            serverLog(LL_WARNING,\"Write error sending DB to replica: %s\",\n                connGetLastError(conn));\n            freeClient(slave);\n        }\n        return;\n    }\n    slave->repldboff += nwritten;\n    server.stat_net_output_bytes += nwritten;\n    if (slave->repldboff == slave->repldbsize) {\n        close(slave->repldbfd);\n        slave->repldbfd = -1;\n        connSetWriteHandler(slave->conn,NULL);\n        putSlaveOnline(slave);\n    }\n}\n\n/* Remove one write handler from the list of connections waiting to be writable\n * during rdb pipe transfer. */\nvoid rdbPipeWriteHandlerConnRemoved(struct connection *conn) {\n    if (!connHasWriteHandler(conn))\n        return;\n    connSetWriteHandler(conn, NULL);\n    server.rdb_pipe_numconns_writing--;\n    /* if there are no more writes for now for this conn, or write error: */\n    if (server.rdb_pipe_numconns_writing == 0) {\n        if (aeCreateFileEvent(server.el, server.rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {\n            serverPanic(\"Unrecoverable error creating server.rdb_pipe_read file event.\");\n        }\n    }\n}\n\n/* Called in diskless master during transfer of data from the rdb pipe, when\n * the replica becomes writable again. */\nvoid rdbPipeWriteHandler(struct connection *conn) {\n    serverAssert(server.rdb_pipe_bufflen>0);\n    client *slave = connGetPrivateData(conn);\n    int nwritten;\n    if ((nwritten = connWrite(conn, server.rdb_pipe_buff + slave->repldboff,\n                              server.rdb_pipe_bufflen - slave->repldboff)) == -1)\n    {\n        if (connGetState(conn) == CONN_STATE_CONNECTED)\n            return; /* equivalent to EAGAIN */\n        serverLog(LL_WARNING,\"Write error sending DB to replica: %s\",\n            connGetLastError(conn));\n        freeClient(slave);\n        return;\n    } else {\n        slave->repldboff += nwritten;\n        server.stat_net_output_bytes += nwritten;\n        if (slave->repldboff < server.rdb_pipe_bufflen)\n            return; /* more data to write.. */\n    }\n    rdbPipeWriteHandlerConnRemoved(conn);\n}\n\n/* When the the pipe serving diskless rdb transfer is drained (write end was\n * closed), we can clean up all the temporary variables, and cleanup after the\n * fork child. */\nvoid RdbPipeCleanup() {\n    close(server.rdb_pipe_read);\n    zfree(server.rdb_pipe_conns);\n    server.rdb_pipe_conns = NULL;\n    server.rdb_pipe_numconns = 0;\n    server.rdb_pipe_numconns_writing = 0;\n    zfree(server.rdb_pipe_buff);\n    server.rdb_pipe_buff = NULL;\n    server.rdb_pipe_bufflen = 0;\n\n    /* Since we're avoiding to detect the child exited as long as the pipe is\n     * not drained, so now is the time to check. */\n    checkChildrenDone();\n}\n\n/* Called in diskless master, when there's data to read from the child's rdb pipe */\nvoid rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask) {\n    UNUSED(mask);\n    UNUSED(clientData);\n    UNUSED(eventLoop);\n    int i;\n    if (!server.rdb_pipe_buff)\n        server.rdb_pipe_buff = zmalloc(PROTO_IOBUF_LEN);\n    serverAssert(server.rdb_pipe_numconns_writing==0);\n\n    while (1) {\n        server.rdb_pipe_bufflen = read(fd, server.rdb_pipe_buff, PROTO_IOBUF_LEN);\n        if (server.rdb_pipe_bufflen < 0) {\n            if (errno == EAGAIN || errno == EWOULDBLOCK)\n                return;\n            serverLog(LL_WARNING,\"Diskless rdb transfer, read error sending DB to replicas: %s\", strerror(errno));\n            for (i=0; i < server.rdb_pipe_numconns; i++) {\n                connection *conn = server.rdb_pipe_conns[i];\n                if (!conn)\n                    continue;\n                client *slave = connGetPrivateData(conn);\n                freeClient(slave);\n                server.rdb_pipe_conns[i] = NULL;\n            }\n            killRDBChild();\n            return;\n        }\n\n        if (server.rdb_pipe_bufflen == 0) {\n            /* EOF - write end was closed. */\n            int stillUp = 0;\n            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE);\n            for (i=0; i < server.rdb_pipe_numconns; i++)\n            {\n                connection *conn = server.rdb_pipe_conns[i];\n                if (!conn)\n                    continue;\n                stillUp++;\n            }\n            serverLog(LL_WARNING,\"Diskless rdb transfer, done reading from pipe, %d replicas still up.\", stillUp);\n            RdbPipeCleanup();\n            return;\n        }\n\n        int stillAlive = 0;\n        for (i=0; i < server.rdb_pipe_numconns; i++)\n        {\n            int nwritten;\n            connection *conn = server.rdb_pipe_conns[i];\n            if (!conn)\n                continue;\n\n            client *slave = connGetPrivateData(conn);\n            if ((nwritten = connWrite(conn, server.rdb_pipe_buff, server.rdb_pipe_bufflen)) == -1) {\n                if (connGetState(conn) != CONN_STATE_CONNECTED) {\n                    serverLog(LL_WARNING,\"Diskless rdb transfer, write error sending DB to replica: %s\",\n                        connGetLastError(conn));\n                    freeClient(slave);\n                    server.rdb_pipe_conns[i] = NULL;\n                    continue;\n                }\n                /* An error and still in connected state, is equivalent to EAGAIN */\n                slave->repldboff = 0;\n            } else {\n                slave->repldboff = nwritten;\n                server.stat_net_output_bytes += nwritten;\n            }\n            /* If we were unable to write all the data to one of the replicas,\n             * setup write handler (and disable pipe read handler, below) */\n            if (nwritten != server.rdb_pipe_bufflen) {\n                server.rdb_pipe_numconns_writing++;\n                connSetWriteHandler(conn, rdbPipeWriteHandler);\n            }\n            stillAlive++;\n        }\n\n        if (stillAlive == 0) {\n            serverLog(LL_WARNING,\"Diskless rdb transfer, last replica dropped, killing fork child.\");\n            killRDBChild();\n            RdbPipeCleanup();\n        }\n        /*  Remove the pipe read handler if at least one write handler was set. */\n        if (server.rdb_pipe_numconns_writing || stillAlive == 0) {\n            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE);\n            break;\n        }\n    }\n}\n\n/* This function is called at the end of every background saving,\n * or when the replication RDB transfer strategy is modified from\n * disk to socket or the other way around.\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, and\n * to schedule a new BGSAVE if there are slaves that attached while a\n * BGSAVE was in progress, but it was not a good one for replication (no\n * other slave was accumulating differences).\n *\n * The argument bgsaveerr is C_OK if the background saving succeeded\n * otherwise C_ERR is passed to the function.\n * The 'type' argument is the type of the child that terminated\n * (if it had a disk or socket target). */\nvoid updateSlavesWaitingBgsave(int bgsaveerr, int type) {\n    listNode *ln;\n    int startbgsave = 0;\n    int mincapa = -1;\n    listIter li;\n\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n            startbgsave = 1;\n            mincapa = (mincapa == -1) ? slave->slave_capa :\n                                        (mincapa & slave->slave_capa);\n        } else if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {\n            struct redis_stat buf;\n\n            /* If this was an RDB on disk save, we have to prepare to send\n             * the RDB from disk to the slave socket. Otherwise if this was\n             * already an RDB -> Slaves socket transfer, used in the case of\n             * diskless replication, our work is trivial, we can just put\n             * the slave online. */\n            if (type == RDB_CHILD_TYPE_SOCKET) {\n                serverLog(LL_NOTICE,\n                    \"Streamed RDB transfer with replica %s succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming\",\n                        replicationGetSlaveName(slave));\n                /* Note: we wait for a REPLCONF ACK message from the replica in\n                 * order to really put it online (install the write handler\n                 * so that the accumulated data can be transferred). However\n                 * we change the replication state ASAP, since our slave\n                 * is technically online now.\n                 *\n                 * So things work like that:\n                 *\n                 * 1. We end trasnferring the RDB file via socket.\n                 * 2. The replica is put ONLINE but the write handler\n                 *    is not installed.\n                 * 3. The replica however goes really online, and pings us\n                 *    back via REPLCONF ACK commands.\n                 * 4. Now we finally install the write handler, and send\n                 *    the buffers accumulated so far to the replica.\n                 *\n                 * But why we do that? Because the replica, when we stream\n                 * the RDB directly via the socket, must detect the RDB\n                 * EOF (end of file), that is a special random string at the\n                 * end of the RDB (for streamed RDBs we don't know the length\n                 * in advance). Detecting such final EOF string is much\n                 * simpler and less CPU intensive if no more data is sent\n                 * after such final EOF. So we don't want to glue the end of\n                 * the RDB trasfer with the start of the other replication\n                 * data. */\n                slave->replstate = SLAVE_STATE_ONLINE;\n                slave->repl_put_online_on_ack = 1;\n                slave->repl_ack_time = server.unixtime; /* Timeout otherwise. */\n            } else {\n                if (bgsaveerr != C_OK) {\n                    freeClient(slave);\n                    serverLog(LL_WARNING,\"SYNC failed. BGSAVE child returned an error\");\n                    continue;\n                }\n                if ((slave->repldbfd = open(server.rdb_filename,O_RDONLY)) == -1 ||\n                    redis_fstat(slave->repldbfd,&buf) == -1) {\n                    freeClient(slave);\n                    serverLog(LL_WARNING,\"SYNC failed. Can't open/stat DB after BGSAVE: %s\", strerror(errno));\n                    continue;\n                }\n                slave->repldboff = 0;\n                slave->repldbsize = buf.st_size;\n                slave->replstate = SLAVE_STATE_SEND_BULK;\n                slave->replpreamble = sdscatprintf(sdsempty(),\"$%lld\\r\\n\",\n                    (unsigned long long) slave->repldbsize);\n\n                connSetWriteHandler(slave->conn,NULL);\n                if (connSetWriteHandler(slave->conn,sendBulkToSlave) == C_ERR) {\n                    freeClient(slave);\n                    continue;\n                }\n            }\n        }\n    }\n    if (startbgsave) startBgsaveForReplication(mincapa);\n}\n\n/* Change the current instance replication ID with a new, random one.\n * This will prevent successful PSYNCs between this master and other\n * slaves, so the command should be called when something happens that\n * alters the current story of the dataset. */\nvoid changeReplicationId(void) {\n    getRandomHexChars(server.replid,CONFIG_RUN_ID_SIZE);\n    server.replid[CONFIG_RUN_ID_SIZE] = '\\0';\n}\n\n/* Clear (invalidate) the secondary replication ID. This happens, for\n * example, after a full resynchronization, when we start a new replication\n * history. */\nvoid clearReplicationId2(void) {\n    memset(server.replid2,'0',sizeof(server.replid));\n    server.replid2[CONFIG_RUN_ID_SIZE] = '\\0';\n    server.second_replid_offset = -1;\n}\n\n/* Use the current replication ID / offset as secondary replication\n * ID, and change the current one in order to start a new history.\n * This should be used when an instance is switched from slave to master\n * so that it can serve PSYNC requests performed using the master\n * replication ID. */\nvoid shiftReplicationId(void) {\n    memcpy(server.replid2,server.replid,sizeof(server.replid));\n    /* We set the second replid offset to the master offset + 1, since\n     * the slave will ask for the first byte it has not yet received, so\n     * we need to add one to the offset: for example if, as a slave, we are\n     * sure we have the same history as the master for 50 bytes, after we\n     * are turned into a master, we can accept a PSYNC request with offset\n     * 51, since the slave asking has the same history up to the 50th\n     * byte, and is asking for the new bytes starting at offset 51. */\n    server.second_replid_offset = server.master_repl_offset+1;\n    changeReplicationId();\n    serverLog(LL_WARNING,\"Setting secondary replication ID to %s, valid up to offset: %lld. New replication ID is %s\", server.replid2, server.second_replid_offset, server.replid);\n}\n\n/* ----------------------------------- SLAVE -------------------------------- */\n\n/* Returns 1 if the given replication state is a handshake state,\n * 0 otherwise. */\nint slaveIsInHandshakeState(void) {\n    return server.repl_state >= REPL_STATE_RECEIVE_PONG &&\n           server.repl_state <= REPL_STATE_RECEIVE_PSYNC;\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 entirely 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        /* Pinging back in this stage is best-effort. */\n        if (server.repl_transfer_s) connWrite(server.repl_transfer_s, \"\\n\", 1);\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    UNUSED(privdata);\n    replicationSendNewlineToMaster();\n}\n\n/* Once we have a link with the master and the synchroniziation was\n * performed, this function materializes the master client we store\n * at server.master, starting from the specified file descriptor. */\nvoid replicationCreateMasterClient(connection *conn, int dbid) {\n    server.master = createClient(conn);\n    if (conn)\n        connSetReadHandler(server.master->conn, readQueryFromClient);\n    server.master->flags |= CLIENT_MASTER;\n    server.master->authenticated = 1;\n    server.master->reploff = server.master_initial_offset;\n    server.master->read_reploff = server.master->reploff;\n    server.master->user = NULL; /* This client can do everything. */\n    memcpy(server.master->replid, server.master_replid,\n        sizeof(server.master_replid));\n    /* If master offset is set to -1, this master is old and is not\n     * PSYNC capable, so we flag it accordingly. */\n    if (server.master->reploff == -1)\n        server.master->flags |= CLIENT_PRE_PSYNC;\n    if (dbid != -1) selectDb(server.master,dbid);\n}\n\n/* This function will try to re-enable the AOF file after the\n * master-replica synchronization: if it fails after multiple attempts\n * the replica cannot be considered reliable and exists with an\n * error. */\nvoid restartAOFAfterSYNC() {\n    unsigned int tries, max_tries = 10;\n    for (tries = 0; tries < max_tries; ++tries) {\n        if (startAppendOnly() == C_OK) break;\n        serverLog(LL_WARNING,\n            \"Failed enabling the AOF after successful master synchronization! \"\n            \"Trying it again in one second.\");\n        sleep(1);\n    }\n    if (tries == max_tries) {\n        serverLog(LL_WARNING,\n            \"FATAL: this replica instance finished the synchronization with \"\n            \"its master, but the AOF can't be turned on. Exiting now.\");\n        exit(1);\n    }\n}\n\nstatic int useDisklessLoad() {\n    /* compute boolean decision to use diskless load */\n    int enabled = server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB ||\n           (server.repl_diskless_load == REPL_DISKLESS_LOAD_WHEN_DB_EMPTY && dbTotalServerKeyCount()==0);\n    /* Check all modules handle read errors, otherwise it's not safe to use diskless load. */\n    if (enabled && !moduleAllDatatypesHandleErrors()) {\n        serverLog(LL_WARNING,\n            \"Skipping diskless-load because there are modules that don't handle read errors.\");\n        enabled = 0;\n    }\n    return enabled;\n}\n\n/* Helper function for readSyncBulkPayload() to make backups of the current\n * DBs before socket-loading the new ones. The backups may be restored later\n * or freed by disklessLoadRestoreBackups(). */\nredisDb *disklessLoadMakeBackups(void) {\n    redisDb *backups = zmalloc(sizeof(redisDb)*server.dbnum);\n    for (int i=0; i<server.dbnum; i++) {\n        backups[i] = server.db[i];\n        server.db[i].dict = dictCreate(&dbDictType,NULL);\n        server.db[i].expires = dictCreate(&keyptrDictType,NULL);\n    }\n    return backups;\n}\n\n/* Helper function for readSyncBulkPayload(): when replica-side diskless\n * database loading is used, Redis makes a backup of the existing databases\n * before loading the new ones from the socket.\n *\n * If the socket loading went wrong, we want to restore the old backups\n * into the server databases. This function does just that in the case\n * the 'restore' argument (the number of DBs to replace) is non-zero.\n *\n * When instead the loading succeeded we want just to free our old backups,\n * in that case the funciton will do just that when 'restore' is 0. */\nvoid disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags)\n{\n    if (restore) {\n        /* Restore. */\n        emptyDbGeneric(server.db,-1,empty_db_flags,replicationEmptyDbCallback);\n        for (int i=0; i<server.dbnum; i++) {\n            dictRelease(server.db[i].dict);\n            dictRelease(server.db[i].expires);\n            server.db[i] = backup[i];\n        }\n    } else {\n        /* Delete (Pass EMPTYDB_BACKUP in order to avoid firing module events) . */\n        emptyDbGeneric(backup,-1,empty_db_flags|EMPTYDB_BACKUP,replicationEmptyDbCallback);\n        for (int i=0; i<server.dbnum; i++) {\n            dictRelease(backup[i].dict);\n            dictRelease(backup[i].expires);\n        }\n    }\n    zfree(backup);\n}\n\n/* Asynchronously read the SYNC payload we receive from a master */\n#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */\nvoid readSyncBulkPayload(connection *conn) {\n    char buf[PROTO_IOBUF_LEN];\n    ssize_t nread, readlen, nwritten;\n    int use_diskless_load = useDisklessLoad();\n    redisDb *diskless_load_backup = NULL;\n    int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :\n                                                        EMPTYDB_NO_FLAGS;\n    off_t left;\n\n    /* Static vars used to hold the EOF mark, and the last bytes received\n     * form the server: when they match, we reached the end of the transfer. */\n    static char eofmark[CONFIG_RUN_ID_SIZE];\n    static char lastbytes[CONFIG_RUN_ID_SIZE];\n    static int usemark = 0;\n\n    /* If repl_transfer_size == -1 we still have to read the bulk length\n     * from the master reply. */\n    if (server.repl_transfer_size == -1) {\n        if (connSyncReadLine(conn,buf,1024,server.repl_syncio_timeout*1000) == -1) {\n            serverLog(LL_WARNING,\n                \"I/O error reading bulk count from MASTER: %s\",\n                strerror(errno));\n            goto error;\n        }\n\n        if (buf[0] == '-') {\n            serverLog(LL_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            server.repl_transfer_lastio = server.unixtime;\n            return;\n        } else if (buf[0] != '$') {\n            serverLog(LL_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        /* There are two possible forms for the bulk payload. One is the\n         * usual $<count> bulk format. The other is used for diskless transfers\n         * when the master does not know beforehand the size of the file to\n         * transfer. In the latter case, the following format is used:\n         *\n         * $EOF:<40 bytes delimiter>\n         *\n         * At the end of the file the announced delimiter is transmitted. The\n         * delimiter is long and random enough that the probability of a\n         * collision with the actual file content can be ignored. */\n        if (strncmp(buf+1,\"EOF:\",4) == 0 && strlen(buf+5) >= CONFIG_RUN_ID_SIZE) {\n            usemark = 1;\n            memcpy(eofmark,buf+5,CONFIG_RUN_ID_SIZE);\n            memset(lastbytes,0,CONFIG_RUN_ID_SIZE);\n            /* Set any repl_transfer_size to avoid entering this code path\n             * at the next call. */\n            server.repl_transfer_size = 0;\n            serverLog(LL_NOTICE,\n                \"MASTER <-> REPLICA sync: receiving streamed RDB from master with EOF %s\",\n                use_diskless_load? \"to parser\":\"to disk\");\n        } else {\n            usemark = 0;\n            server.repl_transfer_size = strtol(buf+1,NULL,10);\n            serverLog(LL_NOTICE,\n                \"MASTER <-> REPLICA sync: receiving %lld bytes from master %s\",\n                (long long) server.repl_transfer_size,\n                use_diskless_load? \"to parser\":\"to disk\");\n        }\n        return;\n    }\n\n    if (!use_diskless_load) {\n        /* Read the data from the socket, store it to a file and search\n         * for the EOF. */\n        if (usemark) {\n            readlen = sizeof(buf);\n        } else {\n            left = server.repl_transfer_size - server.repl_transfer_read;\n            readlen = (left < (signed)sizeof(buf)) ? left : (signed)sizeof(buf);\n        }\n\n        nread = connRead(conn,buf,readlen);\n        if (nread <= 0) {\n            serverLog(LL_WARNING,\"I/O error trying to sync with MASTER: %s\",\n                (nread == -1) ? strerror(errno) : \"connection lost\");\n            cancelReplicationHandshake();\n            return;\n        }\n        server.stat_net_input_bytes += nread;\n\n        /* When a mark is used, we want to detect EOF asap in order to avoid\n         * writing the EOF mark into the file... */\n        int eof_reached = 0;\n\n        if (usemark) {\n            /* Update the last bytes array, and check if it matches our\n             * delimiter. */\n            if (nread >= CONFIG_RUN_ID_SIZE) {\n                memcpy(lastbytes,buf+nread-CONFIG_RUN_ID_SIZE,\n                       CONFIG_RUN_ID_SIZE);\n            } else {\n                int rem = CONFIG_RUN_ID_SIZE-nread;\n                memmove(lastbytes,lastbytes+nread,rem);\n                memcpy(lastbytes+rem,buf,nread);\n            }\n            if (memcmp(lastbytes,eofmark,CONFIG_RUN_ID_SIZE) == 0)\n                eof_reached = 1;\n        }\n\n        /* Update the last I/O time for the replication transfer (used in\n         * order to detect timeouts during replication), and write what we\n         * got from the socket to the dump file on disk. */\n        server.repl_transfer_lastio = server.unixtime;\n        if ((nwritten = write(server.repl_transfer_fd,buf,nread)) != nread) {\n            serverLog(LL_WARNING,\n                \"Write error or short write writing to the DB dump file \"\n                \"needed for MASTER <-> REPLICA synchronization: %s\",\n                (nwritten == -1) ? strerror(errno) : \"short write\");\n            goto error;\n        }\n        server.repl_transfer_read += nread;\n\n        /* Delete the last 40 bytes from the file if we reached EOF. */\n        if (usemark && eof_reached) {\n            if (ftruncate(server.repl_transfer_fd,\n                server.repl_transfer_read - CONFIG_RUN_ID_SIZE) == -1)\n            {\n                serverLog(LL_WARNING,\n                    \"Error truncating the RDB file received from the master \"\n                    \"for SYNC: %s\", strerror(errno));\n                goto error;\n            }\n        }\n\n        /* Sync data on disk from time to time, otherwise at the end of the\n         * transfer we may suffer a big delay as the memory buffers are copied\n         * into the actual disk. */\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        if (!usemark) {\n            if (server.repl_transfer_read == server.repl_transfer_size)\n                eof_reached = 1;\n        }\n\n        /* If the transfer is yet not complete, we need to read more, so\n         * return ASAP and wait for the handler to be called again. */\n        if (!eof_reached) return;\n    }\n\n    /* We reach this point in one of the following cases:\n     *\n     * 1. The replica is using diskless replication, that is, it reads data\n     *    directly from the socket to the Redis memory, without using\n     *    a temporary RDB file on disk. In that case we just block and\n     *    read everything from the socket.\n     *\n     * 2. Or when we are done reading from the socket to the RDB file, in\n     *    such case we want just to read the RDB file in memory. */\n    serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Flushing old data\");\n\n    /* We need to stop any AOF rewriting child before flusing and parsing\n     * the RDB, otherwise we'll create a copy-on-write disaster. */\n    if (server.aof_state != AOF_OFF) stopAppendOnly();\n\n    /* When diskless RDB loading is used by replicas, it may be configured\n     * in order to save the current DB instead of throwing it away,\n     * so that we can restore it in case of failed transfer. */\n    if (use_diskless_load &&\n        server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB)\n    {\n        /* Create a backup of server.db[] and initialize to empty\n         * dictionaries */\n        diskless_load_backup = disklessLoadMakeBackups();\n    }\n    /* We call to emptyDb even in case of REPL_DISKLESS_LOAD_SWAPDB\n     * (Where disklessLoadMakeBackups left server.db empty) because we\n     * want to execute all the auxiliary logic of emptyDb (Namely,\n     * fire module events) */\n    emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);\n\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    connSetReadHandler(conn, NULL);\n    serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Loading DB in memory\");\n    rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;\n    if (use_diskless_load) {\n        rio rdb;\n        rioInitWithConn(&rdb,conn,server.repl_transfer_size);\n\n        /* Put the socket in blocking mode to simplify RDB transfer.\n         * We'll restore it when the RDB is received. */\n        connBlock(conn);\n        connRecvTimeout(conn, server.repl_timeout*1000);\n        startLoading(server.repl_transfer_size, RDBFLAGS_REPLICATION);\n\n        if (rdbLoadRio(&rdb,RDBFLAGS_REPLICATION,&rsi) != C_OK) {\n            /* RDB loading failed. */\n            stopLoading(0);\n            serverLog(LL_WARNING,\n                \"Failed trying to load the MASTER synchronization DB \"\n                \"from socket\");\n            cancelReplicationHandshake();\n            rioFreeConn(&rdb, NULL);\n            if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {\n                /* Restore the backed up databases. */\n                disklessLoadRestoreBackups(diskless_load_backup,1,\n                                           empty_db_flags);\n            } else {\n                /* Remove the half-loaded data in case we started with\n                 * an empty replica. */\n                emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);\n            }\n\n            /* Note that there's no point in restarting the AOF on SYNC\n             * failure, it'll be restarted when sync succeeds or the replica\n             * gets promoted. */\n            return;\n        }\n        stopLoading(1);\n\n        /* RDB loading succeeded if we reach this point. */\n        if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {\n            /* Delete the backup databases we created before starting to load\n             * the new RDB. Now the RDB was loaded with success so the old\n             * data is useless. */\n            disklessLoadRestoreBackups(diskless_load_backup,0,empty_db_flags);\n        }\n\n        /* Verify the end mark is correct. */\n        if (usemark) {\n            if (!rioRead(&rdb,buf,CONFIG_RUN_ID_SIZE) ||\n                memcmp(buf,eofmark,CONFIG_RUN_ID_SIZE) != 0)\n            {\n                serverLog(LL_WARNING,\"Replication stream EOF marker is broken\");\n                cancelReplicationHandshake();\n                rioFreeConn(&rdb, NULL);\n                return;\n            }\n        }\n\n        /* Cleanup and restore the socket to the original state to continue\n         * with the normal replication. */\n        rioFreeConn(&rdb, NULL);\n        connNonBlock(conn);\n        connRecvTimeout(conn,0);\n    } else {\n        /* Ensure background save doesn't overwrite synced data */\n        if (server.rdb_child_pid != -1) {\n            serverLog(LL_NOTICE,\n                \"Replica is about to load the RDB file received from the \"\n                \"master, but there is a pending RDB child running. \"\n                \"Killing process %ld and removing its temp file to avoid \"\n                \"any race\",\n                    (long) server.rdb_child_pid);\n            killRDBChild();\n        }\n\n        /* Rename rdb like renaming rewrite aof asynchronously. */\n        int old_rdb_fd = open(server.rdb_filename,O_RDONLY|O_NONBLOCK);\n        if (rename(server.repl_transfer_tmpfile,server.rdb_filename) == -1) {\n            serverLog(LL_WARNING,\n                \"Failed trying to rename the temp DB into %s in \"\n                \"MASTER <-> REPLICA synchronization: %s\",\n                server.rdb_filename, strerror(errno));\n            cancelReplicationHandshake();\n            if (old_rdb_fd != -1) close(old_rdb_fd);\n            return;\n        }\n        /* Close old rdb asynchronously. */\n        if (old_rdb_fd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)old_rdb_fd,NULL,NULL);\n\n        if (rdbLoad(server.rdb_filename,&rsi,RDBFLAGS_REPLICATION) != C_OK) {\n            serverLog(LL_WARNING,\n                \"Failed trying to load the MASTER synchronization \"\n                \"DB from disk\");\n            cancelReplicationHandshake();\n            if (server.rdb_del_sync_files && allPersistenceDisabled()) {\n                serverLog(LL_NOTICE,\"Removing the RDB file obtained from \"\n                                    \"the master. This replica has persistence \"\n                                    \"disabled\");\n                bg_unlink(server.rdb_filename);\n            }\n            /* Note that there's no point in restarting the AOF on sync failure,\n               it'll be restarted when sync succeeds or replica promoted. */\n            return;\n        }\n\n        /* Cleanup. */\n        if (server.rdb_del_sync_files && allPersistenceDisabled()) {\n            serverLog(LL_NOTICE,\"Removing the RDB file obtained from \"\n                                \"the master. This replica has persistence \"\n                                \"disabled\");\n            bg_unlink(server.rdb_filename);\n        }\n\n        zfree(server.repl_transfer_tmpfile);\n        close(server.repl_transfer_fd);\n        server.repl_transfer_fd = -1;\n        server.repl_transfer_tmpfile = NULL;\n    }\n\n    /* Final setup of the connected slave <- master link */\n    replicationCreateMasterClient(server.repl_transfer_s,rsi.repl_stream_db);\n    server.repl_state = REPL_STATE_CONNECTED;\n    server.repl_down_since = 0;\n\n    /* Fire the master link modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                          REDISMODULE_SUBEVENT_MASTER_LINK_UP,\n                          NULL);\n\n    /* After a full resynchroniziation we use the replication ID and\n     * offset of the master. The secondary ID / offset are cleared since\n     * we are starting a new history. */\n    memcpy(server.replid,server.master->replid,sizeof(server.replid));\n    server.master_repl_offset = server.master->reploff;\n    server.master_repl_meaningful_offset = server.master->reploff;\n    clearReplicationId2();\n\n    /* Let's create the replication backlog if needed. Slaves need to\n     * accumulate the backlog regardless of the fact they have sub-slaves\n     * or not, in order to behave correctly if they are promoted to\n     * masters after a failover. */\n    if (server.repl_backlog == NULL) createReplicationBacklog();\n    serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Finished with success\");\n\n    if (server.supervised_mode == SUPERVISED_SYSTEMD) {\n        redisCommunicateSystemd(\"STATUS=MASTER <-> REPLICA sync: Finished with success. Ready to accept connections.\\n\");\n        redisCommunicateSystemd(\"READY=1\\n\");\n    }\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    if (server.aof_enabled) restartAOFAfterSYNC();\n    return;\n\nerror:\n    cancelReplicationHandshake();\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#define SYNC_CMD_READ (1<<0)\n#define SYNC_CMD_WRITE (1<<1)\n#define SYNC_CMD_FULL (SYNC_CMD_READ|SYNC_CMD_WRITE)\nchar *sendSynchronousCommand(int flags, connection *conn, ...) {\n\n    /* Create the command to send to the master, we use redis binary\n     * protocol to make sure correct arguments are sent. This function\n     * is not safe for all binary data. */\n    if (flags & SYNC_CMD_WRITE) {\n        char *arg;\n        va_list ap;\n        sds cmd = sdsempty();\n        sds cmdargs = sdsempty();\n        size_t argslen = 0;\n        va_start(ap,conn);\n\n        while(1) {\n            arg = va_arg(ap, char*);\n            if (arg == NULL) break;\n\n            cmdargs = sdscatprintf(cmdargs,\"$%zu\\r\\n%s\\r\\n\",strlen(arg),arg);\n            argslen++;\n        }\n\n        va_end(ap);\n\n        cmd = sdscatprintf(cmd,\"*%zu\\r\\n\",argslen);\n        cmd = sdscatsds(cmd,cmdargs);\n        sdsfree(cmdargs);\n\n        /* Transfer command to the server. */\n        if (connSyncWrite(conn,cmd,sdslen(cmd),server.repl_syncio_timeout*1000)\n            == -1)\n        {\n            sdsfree(cmd);\n            return sdscatprintf(sdsempty(),\"-Writing to master: %s\",\n                    connGetLastError(conn));\n        }\n        sdsfree(cmd);\n    }\n\n    /* Read the reply from the server. */\n    if (flags & SYNC_CMD_READ) {\n        char buf[256];\n\n        if (connSyncReadLine(conn,buf,sizeof(buf),server.repl_syncio_timeout*1000)\n            == -1)\n        {\n            return sdscatprintf(sdsempty(),\"-Reading from master: %s\",\n                    strerror(errno));\n        }\n        server.repl_transfer_lastio = server.unixtime;\n        return sdsnew(buf);\n    }\n    return NULL;\n}\n\n/* Try a partial resynchronization with the master if we are about to reconnect.\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 * This function is designed to be called from syncWithMaster(), so the\n * following assumptions are made:\n *\n * 1) We pass the function an already connected socket \"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 *\n * The function is split in two halves: if read_reply is 0, the function\n * writes the PSYNC command on the socket, and a new function call is\n * needed, with read_reply set to 1, in order to read the reply of the\n * command. This is useful in order to support non blocking operations, so\n * that we write, return into the event loop, and read when there are data.\n *\n * When read_reply is 0 the function returns PSYNC_WRITE_ERR if there\n * was a write error, or PSYNC_WAIT_REPLY to signal we need another call\n * with read_reply set to 1. However even when read_reply is set to 1\n * the function may return PSYNC_WAIT_REPLY again to signal there were\n * insufficient data to read to complete its work. We should re-enter\n * into the event loop and wait in such a case.\n *\n * The function returns:\n *\n * PSYNC_CONTINUE: If the PSYNC command succeeded and we can continue.\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_NOT_SUPPORTED: If the server does not understand PSYNC at all and\n *                      the caller should fall back to SYNC.\n * PSYNC_WRITE_ERROR: There was an error writing the command to the socket.\n * PSYNC_WAIT_REPLY: Call again the function with read_reply set to 1.\n * PSYNC_TRY_LATER: Master is currently in a transient error condition.\n *\n * Notable side effects:\n *\n * 1) As a side effect of the function call the function removes the readable\n *    event handler from \"fd\", unless the return value is PSYNC_WAIT_REPLY.\n * 2) server.master_initial_offset is set to the right value according\n *    to the master reply. This will be used to populate the 'server.master'\n *    structure replication offset.\n */\n\n#define PSYNC_WRITE_ERROR 0\n#define PSYNC_WAIT_REPLY 1\n#define PSYNC_CONTINUE 2\n#define PSYNC_FULLRESYNC 3\n#define PSYNC_NOT_SUPPORTED 4\n#define PSYNC_TRY_LATER 5\nint slaveTryPartialResynchronization(connection *conn, int read_reply) {\n    char *psync_replid;\n    char psync_offset[32];\n    sds reply;\n\n    /* Writing half */\n    if (!read_reply) {\n        /* Initially set 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.master_initial_offset = -1;\n\n        if (server.cached_master) {\n            psync_replid = server.cached_master->replid;\n            snprintf(psync_offset,sizeof(psync_offset),\"%lld\", server.cached_master->reploff+1);\n            serverLog(LL_NOTICE,\"Trying a partial resynchronization (request %s:%s).\", psync_replid, psync_offset);\n        } else {\n            serverLog(LL_NOTICE,\"Partial resynchronization not possible (no cached master)\");\n            psync_replid = \"?\";\n            memcpy(psync_offset,\"-1\",3);\n        }\n\n        /* Issue the PSYNC command */\n        reply = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"PSYNC\",psync_replid,psync_offset,NULL);\n        if (reply != NULL) {\n            serverLog(LL_WARNING,\"Unable to send PSYNC to master: %s\",reply);\n            sdsfree(reply);\n            connSetReadHandler(conn, NULL);\n            return PSYNC_WRITE_ERROR;\n        }\n        return PSYNC_WAIT_REPLY;\n    }\n\n    /* Reading half */\n    reply = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n    if (sdslen(reply) == 0) {\n        /* The master may send empty newlines after it receives PSYNC\n         * and before to reply, just to keep the connection alive. */\n        sdsfree(reply);\n        return PSYNC_WAIT_REPLY;\n    }\n\n    connSetReadHandler(conn, NULL);\n\n    if (!strncmp(reply,\"+FULLRESYNC\",11)) {\n        char *replid = NULL, *offset = NULL;\n\n        /* FULL RESYNC, parse the reply in order to extract the run id\n         * and the replication offset. */\n        replid = strchr(reply,' ');\n        if (replid) {\n            replid++;\n            offset = strchr(replid,' ');\n            if (offset) offset++;\n        }\n        if (!replid || !offset || (offset-replid-1) != CONFIG_RUN_ID_SIZE) {\n            serverLog(LL_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             * replid to make sure next PSYNCs will fail. */\n            memset(server.master_replid,0,CONFIG_RUN_ID_SIZE+1);\n        } else {\n            memcpy(server.master_replid, replid, offset-replid-1);\n            server.master_replid[CONFIG_RUN_ID_SIZE] = '\\0';\n            server.master_initial_offset = strtoll(offset,NULL,10);\n            serverLog(LL_NOTICE,\"Full resync from master: %s:%lld\",\n                server.master_replid,\n                server.master_initial_offset);\n        }\n        /* We are going to full resync, discard the cached master structure. */\n        replicationDiscardCachedMaster();\n        sdsfree(reply);\n        return PSYNC_FULLRESYNC;\n    }\n\n    if (!strncmp(reply,\"+CONTINUE\",9)) {\n        /* Partial resync was accepted. */\n        serverLog(LL_NOTICE,\n            \"Successful partial resynchronization with master.\");\n\n        /* Check the new replication ID advertised by the master. If it\n         * changed, we need to set the new ID as primary ID, and set or\n         * secondary ID as the old master ID up to the current offset, so\n         * that our sub-slaves will be able to PSYNC with us after a\n         * disconnection. */\n        char *start = reply+10;\n        char *end = reply+9;\n        while(end[0] != '\\r' && end[0] != '\\n' && end[0] != '\\0') end++;\n        if (end-start == CONFIG_RUN_ID_SIZE) {\n            char new[CONFIG_RUN_ID_SIZE+1];\n            memcpy(new,start,CONFIG_RUN_ID_SIZE);\n            new[CONFIG_RUN_ID_SIZE] = '\\0';\n\n            if (strcmp(new,server.cached_master->replid)) {\n                /* Master ID changed. */\n                serverLog(LL_WARNING,\"Master replication ID changed to %s\",new);\n\n                /* Set the old ID as our ID2, up to the current offset+1. */\n                memcpy(server.replid2,server.cached_master->replid,\n                    sizeof(server.replid2));\n                server.second_replid_offset = server.master_repl_offset+1;\n\n                /* Update the cached master ID and our own primary ID to the\n                 * new one. */\n                memcpy(server.replid,new,sizeof(server.replid));\n                memcpy(server.cached_master->replid,new,sizeof(server.replid));\n\n                /* Disconnect all the sub-slaves: they need to be notified. */\n                disconnectSlaves();\n            }\n        }\n\n        /* Setup the replication to continue. */\n        sdsfree(reply);\n        replicationResurrectCachedMaster(conn);\n\n        /* If this instance was restarted and we read the metadata to\n         * PSYNC from the persistence file, our replication backlog could\n         * be still not initialized. Create it. */\n        if (server.repl_backlog == NULL) createReplicationBacklog();\n        return PSYNC_CONTINUE;\n    }\n\n    /* If we reach this point we received either an error (since the master does\n     * not understand PSYNC or because it is in a special state and cannot\n     * serve our request), or an unexpected reply from the master.\n     *\n     * Return PSYNC_NOT_SUPPORTED on errors we don't understand, otherwise\n     * return PSYNC_TRY_LATER if we believe this is a transient error. */\n\n    if (!strncmp(reply,\"-NOMASTERLINK\",13) ||\n        !strncmp(reply,\"-LOADING\",8))\n    {\n        serverLog(LL_NOTICE,\n            \"Master is currently unable to PSYNC \"\n            \"but should be in the future: %s\", reply);\n        sdsfree(reply);\n        return PSYNC_TRY_LATER;\n    }\n\n    if (strncmp(reply,\"-ERR\",4)) {\n        /* If it's not an error, log the unexpected event. */\n        serverLog(LL_WARNING,\n            \"Unexpected reply to PSYNC from master: %s\", reply);\n    } else {\n        serverLog(LL_NOTICE,\n            \"Master does not support PSYNC or is in \"\n            \"error state (reply: %s)\", reply);\n    }\n    sdsfree(reply);\n    replicationDiscardCachedMaster();\n    return PSYNC_NOT_SUPPORTED;\n}\n\n/* This handler fires when the non blocking connect was able to\n * establish a connection with the master. */\nvoid syncWithMaster(connection *conn) {\n    char tmpfile[256], *err = NULL;\n    int dfd = -1, maxtries = 5;\n    int psync_result;\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    if (server.repl_state == REPL_STATE_NONE) {\n        connClose(conn);\n        return;\n    }\n\n    /* Check for errors in the socket: after a non blocking connect() we\n     * may find that the socket is in error state. */\n    if (connGetState(conn) != CONN_STATE_CONNECTED) {\n        serverLog(LL_WARNING,\"Error condition on socket for SYNC: %s\",\n                connGetLastError(conn));\n        goto error;\n    }\n\n    /* Send a PING to check the master is able to reply without errors. */\n    if (server.repl_state == REPL_STATE_CONNECTING) {\n        serverLog(LL_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        connSetReadHandler(conn, syncWithMaster);\n        connSetWriteHandler(conn, NULL);\n        server.repl_state = REPL_STATE_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        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"PING\",NULL);\n        if (err) goto write_error;\n        return;\n    }\n\n    /* Receive the PONG command. */\n    if (server.repl_state == REPL_STATE_RECEIVE_PONG) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\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        if (err[0] != '+' &&\n            strncmp(err,\"-NOAUTH\",7) != 0 &&\n            strncmp(err,\"-ERR operation not permitted\",28) != 0)\n        {\n            serverLog(LL_WARNING,\"Error reply to PING from master: '%s'\",err);\n            sdsfree(err);\n            goto error;\n        } else {\n            serverLog(LL_NOTICE,\n                \"Master replied to PING, replication can continue...\");\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_AUTH;\n    }\n\n    /* AUTH with the master if required. */\n    if (server.repl_state == REPL_STATE_SEND_AUTH) {\n        if (server.masteruser && server.masterauth) {\n            err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"AUTH\",\n                                         server.masteruser,server.masterauth,NULL);\n            if (err) goto write_error;\n            server.repl_state = REPL_STATE_RECEIVE_AUTH;\n            return;\n        } else if (server.masterauth) {\n            err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"AUTH\",server.masterauth,NULL);\n            if (err) goto write_error;\n            server.repl_state = REPL_STATE_RECEIVE_AUTH;\n            return;\n        } else {\n            server.repl_state = REPL_STATE_SEND_PORT;\n        }\n    }\n\n    /* Receive AUTH reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_AUTH) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        if (err[0] == '-') {\n            serverLog(LL_WARNING,\"Unable to AUTH to MASTER: %s\",err);\n            sdsfree(err);\n            goto error;\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_PORT;\n    }\n\n    /* Set the slave port, so that Master's INFO command can list the\n     * slave listening port correctly. */\n    if (server.repl_state == REPL_STATE_SEND_PORT) {\n        int port;\n        if (server.slave_announce_port) port = server.slave_announce_port;\n        else if (server.tls_replication && server.tls_port) port = server.tls_port;\n        else port = server.port;\n        sds portstr = sdsfromlonglong(port);\n        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"REPLCONF\",\n                \"listening-port\",portstr, NULL);\n        sdsfree(portstr);\n        if (err) goto write_error;\n        sdsfree(err);\n        server.repl_state = REPL_STATE_RECEIVE_PORT;\n        return;\n    }\n\n    /* Receive REPLCONF listening-port reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_PORT) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF listening-port. */\n        if (err[0] == '-') {\n            serverLog(LL_NOTICE,\"(Non critical) Master does not understand \"\n                                \"REPLCONF listening-port: %s\", err);\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_IP;\n    }\n\n    /* Skip REPLCONF ip-address if there is no slave-announce-ip option set. */\n    if (server.repl_state == REPL_STATE_SEND_IP &&\n        server.slave_announce_ip == NULL)\n    {\n            server.repl_state = REPL_STATE_SEND_CAPA;\n    }\n\n    /* Set the slave ip, so that Master's INFO command can list the\n     * slave IP address port correctly in case of port forwarding or NAT. */\n    if (server.repl_state == REPL_STATE_SEND_IP) {\n        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"REPLCONF\",\n                \"ip-address\",server.slave_announce_ip, NULL);\n        if (err) goto write_error;\n        sdsfree(err);\n        server.repl_state = REPL_STATE_RECEIVE_IP;\n        return;\n    }\n\n    /* Receive REPLCONF ip-address reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_IP) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF listening-port. */\n        if (err[0] == '-') {\n            serverLog(LL_NOTICE,\"(Non critical) Master does not understand \"\n                                \"REPLCONF ip-address: %s\", err);\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_CAPA;\n    }\n\n    /* Inform the master of our (slave) capabilities.\n     *\n     * EOF: supports EOF-style RDB transfer for diskless replication.\n     * PSYNC2: supports PSYNC v2, so understands +CONTINUE <new repl ID>.\n     *\n     * The master will ignore capabilities it does not understand. */\n    if (server.repl_state == REPL_STATE_SEND_CAPA) {\n        err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,\"REPLCONF\",\n                \"capa\",\"eof\",\"capa\",\"psync2\",NULL);\n        if (err) goto write_error;\n        sdsfree(err);\n        server.repl_state = REPL_STATE_RECEIVE_CAPA;\n        return;\n    }\n\n    /* Receive CAPA reply. */\n    if (server.repl_state == REPL_STATE_RECEIVE_CAPA) {\n        err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);\n        /* Ignore the error if any, not all the Redis versions support\n         * REPLCONF capa. */\n        if (err[0] == '-') {\n            serverLog(LL_NOTICE,\"(Non critical) Master does not understand \"\n                                  \"REPLCONF capa: %s\", err);\n        }\n        sdsfree(err);\n        server.repl_state = REPL_STATE_SEND_PSYNC;\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    if (server.repl_state == REPL_STATE_SEND_PSYNC) {\n        if (slaveTryPartialResynchronization(conn,0) == PSYNC_WRITE_ERROR) {\n            err = sdsnew(\"Write error sending the PSYNC command.\");\n            goto write_error;\n        }\n        server.repl_state = REPL_STATE_RECEIVE_PSYNC;\n        return;\n    }\n\n    /* If reached this point, we should be in REPL_STATE_RECEIVE_PSYNC. */\n    if (server.repl_state != REPL_STATE_RECEIVE_PSYNC) {\n        serverLog(LL_WARNING,\"syncWithMaster(): state machine error, \"\n                             \"state should be RECEIVE_PSYNC but is %d\",\n                             server.repl_state);\n        goto error;\n    }\n\n    psync_result = slaveTryPartialResynchronization(conn,1);\n    if (psync_result == PSYNC_WAIT_REPLY) return; /* Try again later... */\n\n    /* If the master is in an transient error, we should try to PSYNC\n     * from scratch later, so go to the error path. This happens when\n     * the server is loading the dataset or is not connected with its\n     * master and so forth. */\n    if (psync_result == PSYNC_TRY_LATER) goto error;\n\n    /* Note: if PSYNC does not return WAIT_REPLY, it will take care of\n     * uninstalling the read handler from the file descriptor. */\n\n    if (psync_result == PSYNC_CONTINUE) {\n        serverLog(LL_NOTICE, \"MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.\");\n        if (server.supervised_mode == SUPERVISED_SYSTEMD) {\n            redisCommunicateSystemd(\"STATUS=MASTER <-> REPLICA sync: Partial Resynchronization accepted. Ready to accept connections.\\n\");\n            redisCommunicateSystemd(\"READY=1\\n\");\n        }\n        return;\n    }\n\n    /* PSYNC failed or is not supported: we want our slaves to resync with us\n     * as well, if we have any sub-slaves. The master may transfer us an\n     * entirely different data set and we have no way to incrementally feed\n     * our slaves after that. */\n    disconnectSlaves(); /* Force our slaves to resync with us as well. */\n    freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */\n\n    /* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC\n     * and the server.master_replid and master_initial_offset are\n     * already populated. */\n    if (psync_result == PSYNC_NOT_SUPPORTED) {\n        serverLog(LL_NOTICE,\"Retrying with SYNC...\");\n        if (connSyncWrite(conn,\"SYNC\\r\\n\",6,server.repl_syncio_timeout*1000) == -1) {\n            serverLog(LL_WARNING,\"I/O error writing to MASTER: %s\",\n                strerror(errno));\n            goto error;\n        }\n    }\n\n    /* Prepare a suitable temp file for bulk transfer */\n    if (!useDisklessLoad()) {\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            serverLog(LL_WARNING,\"Opening the temp file needed for MASTER <-> REPLICA synchronization: %s\",strerror(errno));\n            goto error;\n        }\n        server.repl_transfer_tmpfile = zstrdup(tmpfile);\n        server.repl_transfer_fd = dfd;\n    }\n\n    /* Setup the non blocking download of the bulk file. */\n    if (connSetReadHandler(conn, readSyncBulkPayload)\n            == C_ERR)\n    {\n        char conninfo[CONN_INFO_LEN];\n        serverLog(LL_WARNING,\n            \"Can't create readable event for SYNC: %s (%s)\",\n            strerror(errno), connGetInfo(conn, conninfo, sizeof(conninfo)));\n        goto error;\n    }\n\n    server.repl_state = REPL_STATE_TRANSFER;\n    server.repl_transfer_size = -1;\n    server.repl_transfer_read = 0;\n    server.repl_transfer_last_fsync_off = 0;\n    server.repl_transfer_lastio = server.unixtime;\n    return;\n\nerror:\n    if (dfd != -1) close(dfd);\n    connClose(conn);\n    server.repl_transfer_s = NULL;\n    if (server.repl_transfer_fd != -1)\n        close(server.repl_transfer_fd);\n    if (server.repl_transfer_tmpfile)\n        zfree(server.repl_transfer_tmpfile);\n    server.repl_transfer_tmpfile = NULL;\n    server.repl_transfer_fd = -1;\n    server.repl_state = REPL_STATE_CONNECT;\n    return;\n\nwrite_error: /* Handle sendSynchronousCommand(SYNC_CMD_WRITE) errors. */\n    serverLog(LL_WARNING,\"Sending command to master in replication handshake: %s\", err);\n    sdsfree(err);\n    goto error;\n}\n\nint connectWithMaster(void) {\n    server.repl_transfer_s = server.tls_replication ? connCreateTLS() : connCreateSocket();\n    if (connConnect(server.repl_transfer_s, server.masterhost, server.masterport,\n                NET_FIRST_BIND_ADDR, syncWithMaster) == C_ERR) {\n        serverLog(LL_WARNING,\"Unable to connect to MASTER: %s\",\n                connGetLastError(server.repl_transfer_s));\n        connClose(server.repl_transfer_s);\n        server.repl_transfer_s = NULL;\n        return C_ERR;\n    }\n\n\n    server.repl_transfer_lastio = server.unixtime;\n    server.repl_state = REPL_STATE_CONNECTING;\n    return C_OK;\n}\n\n/* This function can be called when a non blocking connection is currently\n * in progress to undo it.\n * Never call this function directly, use cancelReplicationHandshake() instead.\n */\nvoid undoConnectWithMaster(void) {\n    connClose(server.repl_transfer_s);\n    server.repl_transfer_s = NULL;\n}\n\n/* Abort the async download of the bulk dataset while SYNC-ing with master.\n * Never call this function directly, use cancelReplicationHandshake() instead.\n */\nvoid replicationAbortSyncTransfer(void) {\n    serverAssert(server.repl_state == REPL_STATE_TRANSFER);\n    undoConnectWithMaster();\n    if (server.repl_transfer_fd!=-1) {\n        close(server.repl_transfer_fd);\n        unlink(server.repl_transfer_tmpfile);\n        zfree(server.repl_transfer_tmpfile);\n        server.repl_transfer_tmpfile = NULL;\n        server.repl_transfer_fd = -1;\n    }\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 * If there was a replication handshake in progress 1 is returned and\n * the replication state (server.repl_state) set to REPL_STATE_CONNECT.\n *\n * Otherwise zero is returned and no operation is perforemd at all. */\nint cancelReplicationHandshake(void) {\n    if (server.repl_state == REPL_STATE_TRANSFER) {\n        replicationAbortSyncTransfer();\n        server.repl_state = REPL_STATE_CONNECT;\n    } else if (server.repl_state == REPL_STATE_CONNECTING ||\n               slaveIsInHandshakeState())\n    {\n        undoConnectWithMaster();\n        server.repl_state = REPL_STATE_CONNECT;\n    } else {\n        return 0;\n    }\n    return 1;\n}\n\n/* Set replication to the specified master address and port. */\nvoid replicationSetMaster(char *ip, int port) {\n    int was_master = server.masterhost == NULL;\n\n    sdsfree(server.masterhost);\n    server.masterhost = sdsnew(ip);\n    server.masterport = port;\n    if (server.master) {\n        freeClient(server.master);\n    }\n    disconnectAllBlockedClients(); /* Clients blocked in master, now slave. */\n\n    /* Force our slaves to resync with us as well. They may hopefully be able\n     * to partially resync with us, but we can notify the replid change. */\n    disconnectSlaves();\n    cancelReplicationHandshake();\n    /* Before destroying our master state, create a cached master using\n     * our own parameters, to later PSYNC with the new master. */\n    if (was_master) {\n        replicationDiscardCachedMaster();\n        replicationCacheMasterUsingMyself();\n    }\n\n    /* Fire the role change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,\n                          REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA,\n                          NULL);\n\n    /* Fire the master link modules event. */\n    if (server.repl_state == REPL_STATE_CONNECTED)\n        moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN,\n                              NULL);\n\n    server.repl_state = REPL_STATE_CONNECT;\n}\n\n/* Cancel replication, setting the instance as a master itself. */\nvoid replicationUnsetMaster(void) {\n    if (server.masterhost == NULL) return; /* Nothing to do. */\n\n    /* Fire the master link modules event. */\n    if (server.repl_state == REPL_STATE_CONNECTED)\n        moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN,\n                              NULL);\n\n    sdsfree(server.masterhost);\n    server.masterhost = NULL;\n    /* When a slave is turned into a master, the current replication ID\n     * (that was inherited from the master at synchronization time) is\n     * used as secondary ID up to the current offset, and a new replication\n     * ID is created to continue with a new replication history. */\n    shiftReplicationId();\n    if (server.master) freeClient(server.master);\n    replicationDiscardCachedMaster();\n    cancelReplicationHandshake();\n    /* Disconnecting all the slaves is required: we need to inform slaves\n     * of the replication ID change (see shiftReplicationId() call). However\n     * the slaves will be able to partially resync with us, so it will be\n     * a very fast reconnection. */\n    disconnectSlaves();\n    server.repl_state = REPL_STATE_NONE;\n\n    /* We need to make sure the new master will start the replication stream\n     * with a SELECT statement. This is forced after a full resync, but\n     * with PSYNC version 2, there is no need for full resync after a\n     * master switch. */\n    server.slaveseldb = -1;\n\n    /* Once we turn from slave to master, we consider the starting time without\n     * slaves (that is used to count the replication backlog time to live) as\n     * starting from now. Otherwise the backlog will be freed after a\n     * failover if slaves do not connect immediately. */\n    server.repl_no_slaves_since = server.unixtime;\n\n    /* Fire the role change modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,\n                          REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER,\n                          NULL);\n\n    /* Restart the AOF subsystem in case we shut it down during a sync when\n     * we were still a slave. */\n    if (server.aof_enabled && server.aof_state == AOF_OFF) restartAOFAfterSYNC();\n}\n\n/* This function is called when the slave lose the connection with the\n * master into an unexpected way. */\nvoid replicationHandleMasterDisconnection(void) {\n    /* Fire the master link modules event. */\n    if (server.repl_state == REPL_STATE_CONNECTED)\n        moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,\n                              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN,\n                              NULL);\n\n    server.master = NULL;\n    server.repl_state = REPL_STATE_CONNECT;\n    server.repl_down_since = server.unixtime;\n    /* We lost connection with our master, don't disconnect slaves yet,\n     * maybe we'll be able to PSYNC with our master later. We'll disconnect\n     * the slaves only if we'll have to do a full resync with our master. */\n}\n\nvoid replicaofCommand(client *c) {\n    /* SLAVEOF is not allowed in cluster mode as replication is automatically\n     * configured using the current address of the master node. */\n    if (server.cluster_enabled) {\n        addReplyError(c,\"REPLICAOF 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    if (!strcasecmp(c->argv[1]->ptr,\"no\") &&\n        !strcasecmp(c->argv[2]->ptr,\"one\")) {\n        if (server.masterhost) {\n            replicationUnsetMaster();\n            sds client = catClientInfoString(sdsempty(),c);\n            serverLog(LL_NOTICE,\"MASTER MODE enabled (user request from '%s')\",\n                client);\n            sdsfree(client);\n        }\n    } else {\n        long port;\n\n        if (c->flags & CLIENT_SLAVE)\n        {\n            /* If a client is already a replica they cannot run this command,\n             * because it involves flushing all replicas (including this\n             * client) */\n            addReplyError(c, \"Command is not valid when client is a replica.\");\n            return;\n        }\n\n        if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != C_OK))\n            return;\n\n        /* Check if we are already attached to the specified slave */\n        if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr)\n            && server.masterport == port) {\n            serverLog(LL_NOTICE,\"REPLICAOF would result into synchronization \"\n                                \"with the master we are already connected \"\n                                \"with. No operation performed.\");\n            addReplySds(c,sdsnew(\"+OK Already connected to specified \"\n                                 \"master\\r\\n\"));\n            return;\n        }\n        /* There was no previous master or the user specified a different one,\n         * we can continue. */\n        replicationSetMaster(c->argv[1]->ptr, port);\n        sds client = catClientInfoString(sdsempty(),c);\n        serverLog(LL_NOTICE,\"REPLICAOF %s:%d enabled (user request from '%s')\",\n            server.masterhost, server.masterport, client);\n        sdsfree(client);\n    }\n    addReply(c,shared.ok);\n}\n\n/* ROLE command: provide information about the role of the instance\n * (master or slave) and additional information related to replication\n * in an easy to process format. */\nvoid roleCommand(client *c) {\n    if (server.masterhost == NULL) {\n        listIter li;\n        listNode *ln;\n        void *mbcount;\n        int slaves = 0;\n\n        addReplyArrayLen(c,3);\n        addReplyBulkCBuffer(c,\"master\",6);\n        addReplyLongLong(c,server.master_repl_offset);\n        mbcount = addReplyDeferredLen(c);\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n            char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;\n\n            if (slaveip[0] == '\\0') {\n                if (connPeerToString(slave->conn,ip,sizeof(ip),NULL) == -1)\n                    continue;\n                slaveip = ip;\n            }\n            if (slave->replstate != SLAVE_STATE_ONLINE) continue;\n            addReplyArrayLen(c,3);\n            addReplyBulkCString(c,slaveip);\n            addReplyBulkLongLong(c,slave->slave_listening_port);\n            addReplyBulkLongLong(c,slave->repl_ack_off);\n            slaves++;\n        }\n        setDeferredArrayLen(c,mbcount,slaves);\n    } else {\n        char *slavestate = NULL;\n\n        addReplyArrayLen(c,5);\n        addReplyBulkCBuffer(c,\"slave\",5);\n        addReplyBulkCString(c,server.masterhost);\n        addReplyLongLong(c,server.masterport);\n        if (slaveIsInHandshakeState()) {\n            slavestate = \"handshake\";\n        } else {\n            switch(server.repl_state) {\n            case REPL_STATE_NONE: slavestate = \"none\"; break;\n            case REPL_STATE_CONNECT: slavestate = \"connect\"; break;\n            case REPL_STATE_CONNECTING: slavestate = \"connecting\"; break;\n            case REPL_STATE_TRANSFER: slavestate = \"sync\"; break;\n            case REPL_STATE_CONNECTED: slavestate = \"connected\"; break;\n            default: slavestate = \"unknown\"; break;\n            }\n        }\n        addReplyBulkCString(c,slavestate);\n        addReplyLongLong(c,server.master ? server.master->reploff : -1);\n    }\n}\n\n/* Send a REPLCONF ACK command to the master to inform it about the current\n * processed offset. If we are not connected with a master, the command has\n * no effects. */\nvoid replicationSendAck(void) {\n    client *c = server.master;\n\n    if (c != NULL) {\n        c->flags |= CLIENT_MASTER_FORCE_REPLY;\n        addReplyArrayLen(c,3);\n        addReplyBulkCString(c,\"REPLCONF\");\n        addReplyBulkCString(c,\"ACK\");\n        addReplyBulkLongLong(c,c->reploff);\n        c->flags &= ~CLIENT_MASTER_FORCE_REPLY;\n    }\n}\n\n/* ---------------------- MASTER CACHING FOR PSYNC -------------------------- */\n\n/* In order to implement partial synchronization we need to be able to cache\n * our master's client structure after a transient disconnection.\n * It is cached into server.cached_master and flushed away using the following\n * functions. */\n\n/* This function is called by freeClient() in order to cache the master\n * client structure instead of destroying it. freeClient() will return\n * ASAP after this function returns, so every action needed to avoid problems\n * with a client that is really \"suspended\" has to be done by this function.\n *\n * The other functions that will deal with the cached master are:\n *\n * replicationDiscardCachedMaster() that will make sure to kill the client\n * as for some reason we don't want to use it in the future.\n *\n * replicationResurrectCachedMaster() that is used after a successful PSYNC\n * handshake in order to reactivate the cached master.\n */\nvoid replicationCacheMaster(client *c) {\n    serverAssert(server.master != NULL && server.cached_master == NULL);\n    serverLog(LL_NOTICE,\"Caching the disconnected master state.\");\n\n    /* Unlink the client from the server structures. */\n    unlinkClient(c);\n\n    /* Reset the master client so that's ready to accept new commands:\n     * we want to discard te non processed query buffers and non processed\n     * offsets, including pending transactions, already populated arguments,\n     * pending outputs to the master. */\n    sdsclear(server.master->querybuf);\n    sdsclear(server.master->pending_querybuf);\n\n    /* Adjust reploff and read_reploff to the last meaningful offset we\n     * executed. This is the offset the replica will use for future PSYNC. */\n    server.master->reploff = adjustMeaningfulReplOffset();\n    server.master->read_reploff = server.master->reploff;\n    if (c->flags & CLIENT_MULTI) discardTransaction(c);\n    listEmpty(c->reply);\n    c->sentlen = 0;\n    c->reply_bytes = 0;\n    c->bufpos = 0;\n    resetClient(c);\n\n    /* Save the master. Server.master will be set to null later by\n     * replicationHandleMasterDisconnection(). */\n    server.cached_master = server.master;\n\n    /* Invalidate the Peer ID cache. */\n    if (c->peerid) {\n        sdsfree(c->peerid);\n        c->peerid = NULL;\n    }\n\n    /* Caching the master happens instead of the actual freeClient() call,\n     * so make sure to adjust the replication state. This function will\n     * also set server.master to NULL. */\n    replicationHandleMasterDisconnection();\n}\n\n/* If the \"meaningful\" offset, that is the offset without the final PINGs\n * in the stream, is different than the last offset, use it instead:\n * often when the master is no longer reachable, replicas will never\n * receive the PINGs, however the master will end with an incremented\n * offset because of the PINGs and will not be able to incrementally\n * PSYNC with the new master.\n * This function trims the replication backlog when needed, and returns\n * the offset to be used for future partial sync. */\nlong long adjustMeaningfulReplOffset() {\n    if (server.master_repl_offset > server.master_repl_meaningful_offset) {\n        long long delta = server.master_repl_offset -\n                          server.master_repl_meaningful_offset;\n        serverLog(LL_NOTICE,\n            \"Using the meaningful offset %lld instead of %lld to exclude \"\n            \"the final PINGs (%lld bytes difference)\",\n                server.master_repl_meaningful_offset,\n                server.master_repl_offset,\n                delta);\n        server.master_repl_offset = server.master_repl_meaningful_offset;\n        if (server.repl_backlog_histlen <= delta) {\n            server.repl_backlog_histlen = 0;\n            server.repl_backlog_idx = 0;\n        } else {\n            server.repl_backlog_histlen -= delta;\n            server.repl_backlog_idx =\n                (server.repl_backlog_idx + (server.repl_backlog_size - delta)) %\n                server.repl_backlog_size;\n        }\n    }\n    return server.master_repl_offset;\n}\n\n/* This function is called when a master is turend into a slave, in order to\n * create from scratch a cached master for the new client, that will allow\n * to PSYNC with the slave that was promoted as the new master after a\n * failover.\n *\n * Assuming this instance was previously the master instance of the new master,\n * the new master will accept its replication ID, and potentiall also the\n * current offset if no data was lost during the failover. So we use our\n * current replication ID and offset in order to synthesize a cached master. */\nvoid replicationCacheMasterUsingMyself(void) {\n    serverLog(LL_NOTICE,\n        \"Before turning into a replica, using my own master parameters \"\n        \"to synthesize a cached master: I may be able to synchronize with \"\n        \"the new master with just a partial transfer.\");\n\n    /* This will be used to populate the field server.master->reploff\n     * by replicationCreateMasterClient(). We'll later set the created\n     * master as server.cached_master, so the replica will use such\n     * offset for PSYNC. */\n    server.master_initial_offset = adjustMeaningfulReplOffset();\n\n    /* The master client we create can be set to any DBID, because\n     * the new master will start its replication stream with SELECT. */\n    replicationCreateMasterClient(NULL,-1);\n\n    /* Use our own ID / offset. */\n    memcpy(server.master->replid, server.replid, sizeof(server.replid));\n\n    /* Set as cached master. */\n    unlinkClient(server.master);\n    server.cached_master = server.master;\n    server.master = NULL;\n}\n\n/* Free a cached master, called when there are no longer the conditions for\n * a partial resync on reconnection. */\nvoid replicationDiscardCachedMaster(void) {\n    if (server.cached_master == NULL) return;\n\n    serverLog(LL_NOTICE,\"Discarding previously cached master state.\");\n    server.cached_master->flags &= ~CLIENT_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 * This function 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. */\nvoid replicationResurrectCachedMaster(connection *conn) {\n    server.master = server.cached_master;\n    server.cached_master = NULL;\n    server.master->conn = conn;\n    connSetPrivateData(server.master->conn, server.master);\n    server.master->flags &= ~(CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP);\n    server.master->authenticated = 1;\n    server.master->lastinteraction = server.unixtime;\n    server.repl_state = REPL_STATE_CONNECTED;\n    server.repl_down_since = 0;\n\n    /* Re-add to the list of clients. */\n    linkClient(server.master);\n    if (connSetReadHandler(server.master->conn, readQueryFromClient)) {\n        serverLog(LL_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 (clientHasPendingReplies(server.master)) {\n        if (connSetWriteHandler(server.master->conn, sendReplyToClient)) {\n            serverLog(LL_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 * If the option is active, the server will prevent writes if there are not\n * enough connected slaves with the specified lag (or less). */\nvoid refreshGoodSlavesCount(void) {\n    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        client *slave = ln->value;\n        time_t lag = server.unixtime - slave->repl_ack_time;\n\n        if (slave->replstate == SLAVE_STATE_ONLINE &&\n            lag <= server.repl_min_slaves_max_lag) good++;\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 * 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 * 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 * This is how the system works:\n *\n * 1) Every time a new slave connects, we flush the whole script cache.\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 * 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 * 4) On SCRIPT FLUSH command, we replicate the command to all the slaves\n *    and at the same time flush the script cache.\n * 5) When the last slave disconnects, flush the cache.\n * 6) We handle SCRIPT LOAD as well since that's how scripts are loaded\n *    in the master sometimes.\n */\n\n/* Initialize the script cache, only called at startup. */\nvoid replicationScriptCacheInit(void) {\n    server.repl_scriptcache_size = 10000;\n    server.repl_scriptcache_dict = dictCreate(&replScriptCacheDictType,NULL);\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 * 1) Every time a new slave reconnects to this master and performs a\n *    full SYNC (PSYNC does not require flushing).\n * 2) Every time an AOF rewrite is performed.\n * 3) Every time we are left without slaves at all, and AOF is off, in order\n *    to reclaim otherwise unused memory.\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. */\nvoid replicationScriptCacheAdd(sds sha1) {\n    int retval;\n    sds key = sdsdup(sha1);\n\n    /* Evict oldest. */\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        serverAssert(retval == DICT_OK);\n        listDelNode(server.repl_scriptcache_fifo,ln);\n    }\n\n    /* Add current. */\n    retval = dictAdd(server.repl_scriptcache_dict,key,NULL);\n    listAddNodeHead(server.repl_scriptcache_fifo,key);\n    serverAssert(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. */\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        client *slave = ln->value;\n\n        if (slave->replstate != SLAVE_STATE_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(client *c) {\n    mstime_t timeout;\n    long numreplicas, ackreplicas;\n    long long offset = c->woff;\n\n    if (server.masterhost) {\n        addReplyError(c,\"WAIT cannot be used with replica instances. Please also note that since Redis 4.0 if a replica is configured to be writable (which is not the default) writes to replicas are just local and are not propagated.\");\n        return;\n    }\n\n    /* Argument parsing. */\n    if (getLongFromObjectOrReply(c,c->argv[1],&numreplicas,NULL) != C_OK)\n        return;\n    if (getTimeoutFromObjectOrReply(c,c->argv[2],&timeout,UNIT_MILLISECONDS)\n        != C_OK) return;\n\n    /* First try without blocking at all. */\n    ackreplicas = replicationCountAcksByOffset(c->woff);\n    if (ackreplicas >= numreplicas || c->flags & CLIENT_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,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(client *c) {\n    listNode *ln = listSearchKey(server.clients_waiting_acks,c);\n    serverAssert(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        client *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 function, called 1 time per second. */\nvoid replicationCron(void) {\n    static long long replication_cron_loops = 0;\n\n    /* Non blocking connection timeout? */\n    if (server.masterhost &&\n        (server.repl_state == REPL_STATE_CONNECTING ||\n         slaveIsInHandshakeState()) &&\n         (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)\n    {\n        serverLog(LL_WARNING,\"Timeout connecting to the MASTER...\");\n        cancelReplicationHandshake();\n    }\n\n    /* Bulk transfer I/O timeout? */\n    if (server.masterhost && server.repl_state == REPL_STATE_TRANSFER &&\n        (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)\n    {\n        serverLog(LL_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        cancelReplicationHandshake();\n    }\n\n    /* Timed out master when we are an already connected slave? */\n    if (server.masterhost && server.repl_state == REPL_STATE_CONNECTED &&\n        (time(NULL)-server.master->lastinteraction) > server.repl_timeout)\n    {\n        serverLog(LL_WARNING,\"MASTER timeout: no data nor PING received...\");\n        freeClient(server.master);\n    }\n\n    /* Check if we should connect to a MASTER */\n    if (server.repl_state == REPL_STATE_CONNECT) {\n        serverLog(LL_NOTICE,\"Connecting to MASTER %s:%d\",\n            server.masterhost, server.masterport);\n        if (connectWithMaster() == C_OK) {\n            serverLog(LL_NOTICE,\"MASTER <-> REPLICA 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    if (server.masterhost && server.master &&\n        !(server.master->flags & CLIENT_PRE_PSYNC))\n        replicationSendAck();\n\n    /* If we have attached slaves, PING them from time to time.\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    listIter li;\n    listNode *ln;\n    robj *ping_argv[1];\n\n    /* First, send PING according to ping_slave_period. */\n    if ((replication_cron_loops % server.repl_ping_slave_period) == 0 &&\n        listLength(server.slaves))\n    {\n        /* Note that we don't send the PING if the clients are paused during\n         * a Redis Cluster manual failover: the PING we send will otherwise\n         * alter the replication offsets of master and slave, and will no longer\n         * match the one stored into 'mf_master_offset' state. */\n        int manual_failover_in_progress =\n            server.cluster_enabled &&\n            server.cluster->mf_end &&\n            clientsArePaused();\n\n        if (!manual_failover_in_progress) {\n            long long before_ping = server.master_repl_meaningful_offset;\n            ping_argv[0] = createStringObject(\"PING\",4);\n            replicationFeedSlaves(server.slaves, server.slaveseldb,\n                ping_argv, 1);\n            decrRefCount(ping_argv[0]);\n            /* The server.master_repl_meaningful_offset variable represents\n             * the offset of the replication stream without the pending PINGs.\n             * This is useful to set the right replication offset for PSYNC\n             * when the master is turned into a replica. Otherwise pending\n             * PINGs may not allow it to perform an incremental sync with the\n             * new master. */\n            server.master_repl_meaningful_offset = before_ping;\n        }\n    }\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     * Also send the a newline to all the chained slaves we have, if we lost\n     * connection from our master, to keep the slaves aware that their\n     * master is online. This is needed since sub-slaves only receive proxied\n     * data from top-level masters, so there is no explicit pinging in order\n     * to avoid altering the replication offsets. This special out of band\n     * pings (newlines) can be sent, they will have no effect in the offset.\n     *\n     * The newline will be ignored by the slave but will refresh the\n     * last interaction timer preventing a timeout. In this case we ignore the\n     * ping period and refresh the connection once per second since certain\n     * timeouts are set at a few seconds (example: PSYNC response). */\n    listRewind(server.slaves,&li);\n    while((ln = listNext(&li))) {\n        client *slave = ln->value;\n\n        int is_presync =\n            (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||\n            (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&\n             server.rdb_child_type != RDB_CHILD_TYPE_SOCKET));\n\n        if (is_presync) {\n            connWrite(slave->conn, \"\\n\", 1);\n        }\n    }\n\n    /* Disconnect timedout slaves. */\n    if (listLength(server.slaves)) {\n        listIter li;\n        listNode *ln;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n\n            if (slave->replstate != SLAVE_STATE_ONLINE) continue;\n            if (slave->flags & CLIENT_PRE_PSYNC) continue;\n            if ((server.unixtime - slave->repl_ack_time) > server.repl_timeout)\n            {\n                serverLog(LL_WARNING, \"Disconnecting timedout replica: %s\",\n                    replicationGetSlaveName(slave));\n                freeClient(slave);\n            }\n        }\n    }\n\n    /* If this is a master without attached slaves and there is a replication\n     * backlog active, in order to reclaim memory we can free it after some\n     * (configured) time. Note that this cannot be done for slaves: slaves\n     * without sub-slaves attached should still accumulate data into the\n     * backlog, in order to reply to PSYNC queries if they are turned into\n     * masters after a failover. */\n    if (listLength(server.slaves) == 0 && server.repl_backlog_time_limit &&\n        server.repl_backlog && server.masterhost == NULL)\n    {\n        time_t idle = server.unixtime - server.repl_no_slaves_since;\n\n        if (idle > server.repl_backlog_time_limit) {\n            /* When we free the backlog, we always use a new\n             * replication ID and clear the ID2. This is needed\n             * because when there is no backlog, the master_repl_offset\n             * is not updated, but we would still retain our replication\n             * ID, leading to the following problem:\n             *\n             * 1. We are a master instance.\n             * 2. Our slave is promoted to master. It's repl-id-2 will\n             *    be the same as our repl-id.\n             * 3. We, yet as master, receive some updates, that will not\n             *    increment the master_repl_offset.\n             * 4. Later we are turned into a slave, connect to the new\n             *    master that will accept our PSYNC request by second\n             *    replication ID, but there will be data inconsistency\n             *    because we received writes. */\n            changeReplicationId();\n            clearReplicationId2();\n            freeReplicationBacklog();\n            serverLog(LL_NOTICE,\n                \"Replication backlog freed after %d seconds \"\n                \"without connected replicas.\",\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    if (listLength(server.slaves) == 0 &&\n        server.aof_state == AOF_OFF &&\n        listLength(server.repl_scriptcache_fifo) != 0)\n    {\n        replicationScriptCacheFlush();\n    }\n\n    /* Start a BGSAVE good for replication if we have slaves in\n     * WAIT_BGSAVE_START state.\n     *\n     * In case of diskless replication, we make sure to wait the specified\n     * number of seconds (according to configuration) so that other slaves\n     * have the time to arrive before we start streaming. */\n    if (!hasActiveChildProcess()) {\n        time_t idle, max_idle = 0;\n        int slaves_waiting = 0;\n        int mincapa = -1;\n        listNode *ln;\n        listIter li;\n\n        listRewind(server.slaves,&li);\n        while((ln = listNext(&li))) {\n            client *slave = ln->value;\n            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {\n                idle = server.unixtime - slave->lastinteraction;\n                if (idle > max_idle) max_idle = idle;\n                slaves_waiting++;\n                mincapa = (mincapa == -1) ? slave->slave_capa :\n                                            (mincapa & slave->slave_capa);\n            }\n        }\n\n        if (slaves_waiting &&\n            (!server.repl_diskless_sync ||\n             max_idle > server.repl_diskless_sync_delay))\n        {\n            /* Start the BGSAVE. The called function may start a\n             * BGSAVE with socket target or disk target depending on the\n             * configuration and slaves capabilities. */\n            startBgsaveForReplication(mincapa);\n        }\n    }\n\n    /* Remove the RDB file used for replication if Redis is not running\n     * with any persistence. */\n    removeRDBUsedToSyncReplicas();\n\n    /* Refresh the number of slaves with lag <= min-slaves-max-lag. */\n    refreshGoodSlavesCount();\n    replication_cron_loops++; /* Incremented with frequency 1 HZ. */\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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. 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 * A rio object provides the following methods:\n *  read: read from stream.\n *  write: write to stream.\n *  tell: get the current offset.\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 * ----------------------------------------------------------------------------\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 \"server.h\"\n\n/* ------------------------- Buffer I/O implementation ----------------------- */\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioBufferWrite(rio *r, const void *buf, size_t len) {\n    r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);\n    r->io.buffer.pos += len;\n    return 1;\n}\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioBufferRead(rio *r, void *buf, size_t len) {\n    if (sdslen(r->io.buffer.ptr)-r->io.buffer.pos < len)\n        return 0; /* not enough buffer to return len bytes. */\n    memcpy(buf,r->io.buffer.ptr+r->io.buffer.pos,len);\n    r->io.buffer.pos += len;\n    return 1;\n}\n\n/* Returns read/write position in buffer. */\nstatic off_t rioBufferTell(rio *r) {\n    return r->io.buffer.pos;\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioBufferFlush(rio *r) {\n    UNUSED(r);\n    return 1; /* Nothing to do, our write just appends to the buffer. */\n}\n\nstatic const rio rioBufferIO = {\n    rioBufferRead,\n    rioBufferWrite,\n    rioBufferTell,\n    rioBufferFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\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/* --------------------- Stdio file pointer implementation ------------------- */\n\n/* Returns 1 or 0 for success/failure. */\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    if (r->io.file.autosync &&\n        r->io.file.buffered >= r->io.file.autosync)\n    {\n        fflush(r->io.file.fp);\n        redis_fsync(fileno(r->io.file.fp));\n        r->io.file.buffered = 0;\n    }\n    return retval;\n}\n\n/* Returns 1 or 0 for success/failure. */\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. */\nstatic off_t rioFileTell(rio *r) {\n    return ftello(r->io.file.fp);\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioFileFlush(rio *r) {\n    return (fflush(r->io.file.fp) == 0) ? 1 : 0;\n}\n\nstatic const rio rioFileIO = {\n    rioFileRead,\n    rioFileWrite,\n    rioFileTell,\n    rioFileFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\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/* ------------------- Connection implementation -------------------\n * We use this RIO implemetnation when reading an RDB file directly from\n * the connection to the memory via rdbLoadRio(), thus this implementation\n * only implements reading from a connection that is, normally,\n * just a socket. */\n\nstatic size_t rioConnWrite(rio *r, const void *buf, size_t len) {\n    UNUSED(r);\n    UNUSED(buf);\n    UNUSED(len);\n    return 0; /* Error, this target does not yet support writing. */\n}\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioConnRead(rio *r, void *buf, size_t len) {\n    size_t avail = sdslen(r->io.conn.buf)-r->io.conn.pos;\n\n    /* If the buffer is too small for the entire request: realloc. */\n    if (sdslen(r->io.conn.buf) + sdsavail(r->io.conn.buf) < len)\n        r->io.conn.buf = sdsMakeRoomFor(r->io.conn.buf, len - sdslen(r->io.conn.buf));\n\n    /* If the remaining unused buffer is not large enough: memmove so that we\n     * can read the rest. */\n    if (len > avail && sdsavail(r->io.conn.buf) < len - avail) {\n        sdsrange(r->io.conn.buf, r->io.conn.pos, -1);\n        r->io.conn.pos = 0;\n    }\n\n    /* If we don't already have all the data in the sds, read more */\n    while (len > sdslen(r->io.conn.buf) - r->io.conn.pos) {\n        size_t buffered = sdslen(r->io.conn.buf) - r->io.conn.pos;\n        size_t toread = len - buffered;\n        /* Read either what's missing, or PROTO_IOBUF_LEN, the bigger of\n         * the two. */\n        if (toread < PROTO_IOBUF_LEN) toread = PROTO_IOBUF_LEN;\n        if (toread > sdsavail(r->io.conn.buf)) toread = sdsavail(r->io.conn.buf);\n        if (r->io.conn.read_limit != 0 &&\n            r->io.conn.read_so_far + buffered + toread > r->io.conn.read_limit)\n        {\n            if (r->io.conn.read_limit >= r->io.conn.read_so_far - buffered)\n                toread = r->io.conn.read_limit - r->io.conn.read_so_far - buffered;\n            else {\n                errno = EOVERFLOW;\n                return 0;\n            }\n        }\n        int retval = connRead(r->io.conn.conn,\n                          (char*)r->io.conn.buf + sdslen(r->io.conn.buf),\n                          toread);\n        if (retval <= 0) {\n            if (errno == EWOULDBLOCK) errno = ETIMEDOUT;\n            return 0;\n        }\n        sdsIncrLen(r->io.conn.buf, retval);\n    }\n\n    memcpy(buf, (char*)r->io.conn.buf + r->io.conn.pos, len);\n    r->io.conn.read_so_far += len;\n    r->io.conn.pos += len;\n    return len;\n}\n\n/* Returns read/write position in file. */\nstatic off_t rioConnTell(rio *r) {\n    return r->io.conn.read_so_far;\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioConnFlush(rio *r) {\n    /* Our flush is implemented by the write method, that recognizes a\n     * buffer set to NULL with a count of zero as a flush request. */\n    return rioConnWrite(r,NULL,0);\n}\n\nstatic const rio rioConnIO = {\n    rioConnRead,\n    rioConnWrite,\n    rioConnTell,\n    rioConnFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\n};\n\n/* Create an RIO that implements a buffered read from an fd\n * read_limit argument stops buffering when the reaching the limit. */\nvoid rioInitWithConn(rio *r, connection *conn, size_t read_limit) {\n    *r = rioConnIO;\n    r->io.conn.conn = conn;\n    r->io.conn.pos = 0;\n    r->io.conn.read_limit = read_limit;\n    r->io.conn.read_so_far = 0;\n    r->io.conn.buf = sdsnewlen(NULL, PROTO_IOBUF_LEN);\n    sdsclear(r->io.conn.buf);\n}\n\n/* Release the RIO tream. Optionally returns the unread buffered data\n * when the SDS pointer 'remaining' is passed. */\nvoid rioFreeConn(rio *r, sds *remaining) {\n    if (remaining && (size_t)r->io.conn.pos < sdslen(r->io.conn.buf)) {\n        if (r->io.conn.pos > 0) sdsrange(r->io.conn.buf, r->io.conn.pos, -1);\n        *remaining = r->io.conn.buf;\n    } else {\n        sdsfree(r->io.conn.buf);\n        if (remaining) *remaining = NULL;\n    }\n    r->io.conn.buf = NULL;\n}\n\n/* ------------------- File descriptor implementation ------------------\n * This target is used to write the RDB file to pipe, when the master just\n * streams the data to the replicas without creating an RDB on-disk image\n * (diskless replication option).\n * It only implements writes. */\n\n/* Returns 1 or 0 for success/failure.\n *\n * When buf is NULL and len is 0, the function performs a flush operation\n * if there is some pending buffer, so this function is also used in order\n * to implement rioFdFlush(). */\nstatic size_t rioFdWrite(rio *r, const void *buf, size_t len) {\n    ssize_t retval;\n    unsigned char *p = (unsigned char*) buf;\n    int doflush = (buf == NULL && len == 0);\n\n    /* For small writes, we rather keep the data in user-space buffer, and flush\n     * it only when it grows. however for larger writes, we prefer to flush\n     * any pre-existing buffer, and write the new one directly without reallocs\n     * and memory copying. */\n    if (len > PROTO_IOBUF_LEN) {\n        /* First, flush any pre-existing buffered data. */\n        if (sdslen(r->io.fd.buf)) {\n            if (rioFdWrite(r, NULL, 0) == 0)\n                return 0;\n        }\n        /* Write the new data, keeping 'p' and 'len' from the input. */\n    } else {\n        if (len) {\n            r->io.fd.buf = sdscatlen(r->io.fd.buf,buf,len);\n            if (sdslen(r->io.fd.buf) > PROTO_IOBUF_LEN)\n                doflush = 1;\n            if (!doflush)\n                return 1;\n        }\n        /* Flusing the buffered data. set 'p' and 'len' accordintly. */\n        p = (unsigned char*) r->io.fd.buf;\n        len = sdslen(r->io.fd.buf);\n    }\n\n    size_t nwritten = 0;\n    while(nwritten != len) {\n        retval = write(r->io.fd.fd,p+nwritten,len-nwritten);\n        if (retval <= 0) {\n            /* With blocking io, which is the sole user of this\n             * rio target, EWOULDBLOCK is returned only because of\n             * the SO_SNDTIMEO socket option, so we translate the error\n             * into one more recognizable by the user. */\n            if (retval == -1 && errno == EWOULDBLOCK) errno = ETIMEDOUT;\n            return 0; /* error. */\n        }\n        nwritten += retval;\n    }\n\n    r->io.fd.pos += len;\n    sdsclear(r->io.fd.buf);\n    return 1;\n}\n\n/* Returns 1 or 0 for success/failure. */\nstatic size_t rioFdRead(rio *r, void *buf, size_t len) {\n    UNUSED(r);\n    UNUSED(buf);\n    UNUSED(len);\n    return 0; /* Error, this target does not support reading. */\n}\n\n/* Returns read/write position in file. */\nstatic off_t rioFdTell(rio *r) {\n    return r->io.fd.pos;\n}\n\n/* Flushes any buffer to target device if applicable. Returns 1 on success\n * and 0 on failures. */\nstatic int rioFdFlush(rio *r) {\n    /* Our flush is implemented by the write method, that recognizes a\n     * buffer set to NULL with a count of zero as a flush request. */\n    return rioFdWrite(r,NULL,0);\n}\n\nstatic const rio rioFdIO = {\n    rioFdRead,\n    rioFdWrite,\n    rioFdTell,\n    rioFdFlush,\n    NULL,           /* update_checksum */\n    0,              /* current checksum */\n    0,              /* flags */\n    0,              /* bytes read or written */\n    0,              /* read/write chunk size */\n    { { NULL, 0 } } /* union for io-specific vars */\n};\n\nvoid rioInitWithFd(rio *r, int fd) {\n    *r = rioFdIO;\n    r->io.fd.fd = fd;\n    r->io.fd.pos = 0;\n    r->io.fd.buf = sdsempty();\n}\n\n/* release the rio stream. */\nvoid rioFreeFd(rio *r) {\n    sdsfree(r->io.fd.buf);\n}\n\n/* ---------------------------- Generic functions ---------------------------- */\n\n/* This function can be installed both in memory and file streams when checksum\n * computation is needed. */\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 * By default this is set to zero that means no automatic file sync is\n * performed.\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. */\nvoid rioSetAutoSync(rio *r, off_t bytes) {\n    if(r->write != rioFileIO.write) return;\n    r->io.file.autosync = bytes;\n}\n\n/* --------------------------- Higher level interface --------------------------\n *\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/* Write multi bulk count in the format: \"*<count>\\r\\n\". */\nsize_t rioWriteBulkCount(rio *r, char prefix, long count) {\n    char cbuf[128];\n    int clen;\n\n    cbuf[0] = prefix;\n    clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,count);\n    cbuf[clen++] = '\\r';\n    cbuf[clen++] = '\\n';\n    if (rioWrite(r,cbuf,clen) == 0) return 0;\n    return clen;\n}\n\n/* Write binary-safe string in the format: \"$<count>\\r\\n<payload>\\r\\n\". */\nsize_t rioWriteBulkString(rio *r, const char *buf, size_t len) {\n    size_t nwritten;\n\n    if ((nwritten = rioWriteBulkCount(r,'$',len)) == 0) return 0;\n    if (len > 0 && rioWrite(r,buf,len) == 0) return 0;\n    if (rioWrite(r,\"\\r\\n\",2) == 0) return 0;\n    return nwritten+len+2;\n}\n\n/* Write a long long value in format: \"$<count>\\r\\n<payload>\\r\\n\". */\nsize_t rioWriteBulkLongLong(rio *r, long long l) {\n    char lbuf[32];\n    unsigned int llen;\n\n    llen = ll2string(lbuf,sizeof(lbuf),l);\n    return rioWriteBulkString(r,lbuf,llen);\n}\n\n/* Write a double value in the format: \"$<count>\\r\\n<payload>\\r\\n\" */\nsize_t rioWriteBulkDouble(rio *r, double d) {\n    char dbuf[128];\n    unsigned int dlen;\n\n    dlen = snprintf(dbuf,sizeof(dbuf),\"%.17g\",d);\n    return rioWriteBulkString(r,dbuf,dlen);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/rio.h",
    "content": "/*\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2019, 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#include \"connection.h\"\n\n#define RIO_FLAG_READ_ERROR (1<<0)\n#define RIO_FLAG_WRITE_ERROR (1<<1)\n\nstruct _rio {\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    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    int (*flush)(struct _rio *);\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    void (*update_cksum)(struct _rio *, const void *buf, size_t len);\n\n    /* The current checksum and flags (see RIO_FLAG_*) */\n    uint64_t cksum, flags;\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        /* In-memory buffer target. */\n        struct {\n            sds ptr;\n            off_t pos;\n        } buffer;\n        /* Stdio file pointer target. */\n        struct {\n            FILE *fp;\n            off_t buffered; /* Bytes written since last fsync. */\n            off_t autosync; /* fsync after 'autosync' bytes written. */\n        } file;\n        /* Connection object (used to read from socket) */\n        struct {\n            connection *conn;   /* Connection */\n            off_t pos;    /* pos in buf that was returned */\n            sds buf;      /* buffered data */\n            size_t read_limit;  /* don't allow to buffer/read more than that */\n            size_t read_so_far; /* amount of data read from the rio (not buffered) */\n        } conn;\n        /* FD target (used to write to pipe). */\n        struct {\n            int fd;       /* File descriptor. */\n            off_t pos;\n            sds buf;\n        } fd;\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\nstatic inline size_t rioWrite(rio *r, const void *buf, size_t len) {\n    if (r->flags & RIO_FLAG_WRITE_ERROR) return 0;\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            r->flags |= RIO_FLAG_WRITE_ERROR;\n            return 0;\n        }\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\nstatic inline size_t rioRead(rio *r, void *buf, size_t len) {\n    if (r->flags & RIO_FLAG_READ_ERROR) return 0;\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            r->flags |= RIO_FLAG_READ_ERROR;\n            return 0;\n        }\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\nstatic inline off_t rioTell(rio *r) {\n    return r->tell(r);\n}\n\nstatic inline int rioFlush(rio *r) {\n    return r->flush(r);\n}\n\n/* This function allows to know if there was a read error in any past\n * operation, since the rio stream was created or since the last call\n * to rioClearError(). */\nstatic inline int rioGetReadError(rio *r) {\n    return (r->flags & RIO_FLAG_READ_ERROR) != 0;\n}\n\n/* Like rioGetReadError() but for write errors. */\nstatic inline int rioGetWriteError(rio *r) {\n    return (r->flags & RIO_FLAG_WRITE_ERROR) != 0;\n}\n\nstatic inline void rioClearErrors(rio *r) {\n    r->flags &= ~(RIO_FLAG_READ_ERROR|RIO_FLAG_WRITE_ERROR);\n}\n\nvoid rioInitWithFile(rio *r, FILE *fp);\nvoid rioInitWithBuffer(rio *r, sds s);\nvoid rioInitWithConn(rio *r, connection *conn, size_t read_limit);\nvoid rioInitWithFd(rio *r, int fd);\n\nvoid rioFreeFd(rio *r);\nvoid rioFreeConn(rio *r, sds* out_remainingBufferedData);\n\nsize_t rioWriteBulkCount(rio *r, char prefix, long 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\nstruct redisObject;\nint rioWriteBulkObject(rio *r, struct redisObject *obj);\n\nvoid rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);\nvoid rioSetAutoSync(rio *r, off_t bytes);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"sha1.h\"\n#include \"rand.h\"\n#include \"cluster.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_Aggregate(lua_State *lua, char *reply, int atype);\nchar *redisProtocolToLuaType_Null(lua_State *lua, char *reply);\nchar *redisProtocolToLuaType_Bool(lua_State *lua, char *reply, int tf);\nchar *redisProtocolToLuaType_Double(lua_State *lua, char *reply);\nint redis_math_random (lua_State *L);\nint redis_math_randomseed (lua_State *L);\nvoid ldbInit(void);\nvoid ldbDisable(client *c);\nvoid ldbEnable(client *c);\nvoid evalGenericCommandWithDebugging(client *c, int evalsha);\nvoid luaLdbLineHook(lua_State *lua, lua_Debug *ar);\nvoid ldbLog(sds entry);\nvoid ldbLogRedisReply(char *reply);\nsds ldbCatStackValue(sds s, lua_State *lua, int idx);\n\n/* Debugger shared state is stored inside this global structure. */\n#define LDB_BREAKPOINTS_MAX 64  /* Max number of breakpoints. */\n#define LDB_MAX_LEN_DEFAULT 256 /* Default len limit for replies / var dumps. */\nstruct ldbState {\n    connection *conn; /* Connection of the debugging client. */\n    int active; /* Are we debugging EVAL right now? */\n    int forked; /* Is this a fork()ed debugging session? */\n    list *logs; /* List of messages to send to the client. */\n    list *traces; /* Messages about Redis commands executed since last stop.*/\n    list *children; /* All forked debugging sessions pids. */\n    int bp[LDB_BREAKPOINTS_MAX]; /* An array of breakpoints line numbers. */\n    int bpcount; /* Number of valid entries inside bp. */\n    int step;   /* Stop at next line ragardless of breakpoints. */\n    int luabp;  /* Stop at next line because redis.breakpoint() was called. */\n    sds *src;   /* Lua script source code split by line. */\n    int lines;  /* Number of lines in 'src'. */\n    int currentline;    /* Current line number. */\n    sds cbuf;   /* Debugger client command buffer. */\n    size_t maxlen;  /* Max var dump / reply length. */\n    int maxlen_hint_sent; /* Did we already hint about \"set maxlen\"? */\n} ldb;\n\n/* ---------------------------------------------------------------------------\n * Utility functions.\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 * 'digest' should point to a 41 bytes buffer: 40 for SHA1 converted into an\n * hexadecimal number, plus 1 byte for null term. */\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 * Redis reply to Lua type conversion functions.\n * ------------------------------------------------------------------------- */\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 * 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. The script\n * is like a normal client that bypasses all the slow I/O paths.\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 * Errors are returned as a table with a single 'err' field set to the\n * error string.\n */\n\nchar *redisProtocolToLuaType(lua_State *lua, char* reply) {\n    char *p = reply;\n\n    switch(*p) {\n    case ':': p = redisProtocolToLuaType_Int(lua,reply); break;\n    case '$': p = redisProtocolToLuaType_Bulk(lua,reply); break;\n    case '+': p = redisProtocolToLuaType_Status(lua,reply); break;\n    case '-': p = redisProtocolToLuaType_Error(lua,reply); break;\n    case '*': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;\n    case '%': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;\n    case '~': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;\n    case '_': p = redisProtocolToLuaType_Null(lua,reply); break;\n    case '#': p = redisProtocolToLuaType_Bool(lua,reply,p[1]); break;\n    case ',': p = redisProtocolToLuaType_Double(lua,reply); break;\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_Aggregate(lua_State *lua, char *reply, int atype) {\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    if (server.lua_client->resp == 2 || atype == '*') {\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    } else if (server.lua_client->resp == 3) {\n        /* Here we handle only Set and Map replies in RESP3 mode, since arrays\n         * follow the above RESP2 code path. Note that those are represented\n         * as a table with the \"map\" or \"set\" field populated with the actual\n         * table representing the set or the map type. */\n        p += 2;\n        lua_newtable(lua);\n        lua_pushstring(lua,atype == '%' ? \"map\" : \"set\");\n        lua_newtable(lua);\n        for (j = 0; j < mbulklen; j++) {\n            p = redisProtocolToLuaType(lua,p);\n            if (atype == '%') {\n                p = redisProtocolToLuaType(lua,p);\n            } else {\n                lua_pushboolean(lua,1);\n            }\n            lua_settable(lua,-3);\n        }\n        lua_settable(lua,-3);\n    }\n    return p;\n}\n\nchar *redisProtocolToLuaType_Null(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    lua_pushnil(lua);\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_Bool(lua_State *lua, char *reply, int tf) {\n    char *p = strchr(reply+1,'\\r');\n    lua_pushboolean(lua,tf == 't');\n    return p+2;\n}\n\nchar *redisProtocolToLuaType_Double(lua_State *lua, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    char buf[MAX_LONG_DOUBLE_CHARS+1];\n    size_t len = p-reply-1;\n    double d;\n\n    if (len <= MAX_LONG_DOUBLE_CHARS) {\n        memcpy(buf,reply+1,len);\n        buf[len] = '\\0';\n        d = strtod(buf,NULL); /* We expect a valid representation. */\n    } else {\n        d = 0;\n    }\n\n    lua_newtable(lua);\n    lua_pushstring(lua,\"double\");\n    lua_pushnumber(lua,d);\n    lua_settable(lua,-3);\n    return p+2;\n}\n\n/* This function is used in order to push an error on the Lua stack in the\n * format used by redis.pcall to return errors, which is a lua table\n * with a single \"err\" field set to the error string. Note that this\n * table is never a valid reply by proper commands, since the returned\n * tables are otherwise always indexed by integers, never by strings. */\nvoid luaPushError(lua_State *lua, char *error) {\n    lua_Debug dbg;\n\n    /* If debugging is active and in step mode, log errors resulting from\n     * Redis commands. */\n    if (ldb.active && ldb.step) {\n        ldbLog(sdscatprintf(sdsempty(),\"<error> %s\",error));\n    }\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/* In case the error set into the Lua stack by luaPushError() was generated\n * by the non-error-trapping version of redis.pcall(), which is redis.call(),\n * this function will raise the Lua error so that the execution of the\n * script will be halted. */\nint luaRaiseError(lua_State *lua) {\n    lua_pushstring(lua,\"err\");\n    lua_gettable(lua,-2);\n    return lua_error(lua);\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 * The array is sorted using table.sort itself, and assuming all the\n * list elements are strings. */\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 reply to Redis reply conversion functions.\n * ------------------------------------------------------------------------- */\n\n/* Reply to client 'c' converting the top element in the Lua stack to a\n * Redis reply. As a side effect the element is consumed from the stack.  */\nvoid luaReplyToRedisReply(client *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        if (server.lua_client->resp == 2)\n            addReply(c,lua_toboolean(lua,-1) ? shared.cone :\n                                               shared.null[c->resp]);\n        else\n            addReplyBool(c,lua_toboolean(lua,-1));\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'\n         * field. */\n\n        /* Handle error reply. */\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        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle status reply. */\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,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle double reply. */\n        lua_pushstring(lua,\"double\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TNUMBER) {\n            addReplyDouble(c,lua_tonumber(lua,-1));\n            lua_pop(lua,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle map reply. */\n        lua_pushstring(lua,\"map\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TTABLE) {\n            int maplen = 0;\n            void *replylen = addReplyDeferredLen(c);\n            lua_pushnil(lua); /* Use nil to start iteration. */\n            while (lua_next(lua,-2)) {\n                /* Stack now: table, key, value */\n                luaReplyToRedisReply(c, lua); /* Return value. */\n                lua_pushvalue(lua,-1);        /* Dup key before consuming. */\n                luaReplyToRedisReply(c, lua); /* Return key. */\n                /* Stack now: table, key. */\n                maplen++;\n            }\n            setDeferredMapLen(c,replylen,maplen);\n            lua_pop(lua,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle set reply. */\n        lua_pushstring(lua,\"set\");\n        lua_gettable(lua,-2);\n        t = lua_type(lua,-1);\n        if (t == LUA_TTABLE) {\n            int setlen = 0;\n            void *replylen = addReplyDeferredLen(c);\n            lua_pushnil(lua); /* Use nil to start iteration. */\n            while (lua_next(lua,-2)) {\n                /* Stack now: table, key, true */\n                lua_pop(lua,1);               /* Discard the boolean value. */\n                lua_pushvalue(lua,-1);        /* Dup key before consuming. */\n                luaReplyToRedisReply(c, lua); /* Return key. */\n                /* Stack now: table, key. */\n                setlen++;\n            }\n            setDeferredSetLen(c,replylen,setlen);\n            lua_pop(lua,2);\n            return;\n        }\n        lua_pop(lua,1); /* Discard field name pushed before. */\n\n        /* Handle the array reply. */\n        void *replylen = addReplyDeferredLen(c);\n        int j = 1, mbulklen = 0;\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        setDeferredArrayLen(c,replylen,mbulklen);\n        break;\n    default:\n        addReplyNull(c);\n    }\n    lua_pop(lua,1);\n}\n\n/* ---------------------------------------------------------------------------\n * Lua redis.* functions implementations.\n * ------------------------------------------------------------------------- */\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    client *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 size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE];\n    static int inuse = 0;   /* Recursive calls detection. */\n\n    /* By using Lua debug hooks it is possible to trigger a recursive call\n     * to luaRedisGenericCommand(), which normally should never happen.\n     * To make this function reentrant is futile and makes it slower, but\n     * we should at least detect such a misuse, and abort. */\n    if (inuse) {\n        char *recursion_warning =\n            \"luaRedisGenericCommand() recursive call detected. \"\n            \"Are you doing funny stuff with Lua debug hooks?\";\n        serverLog(LL_WARNING,\"%s\",recursion_warning);\n        luaPushError(lua,recursion_warning);\n        return 1;\n    }\n    inuse++;\n\n    /* Require at least one argument */\n    if (argc == 0) {\n        luaPushError(lua,\n            \"Please specify at least one argument for redis.call()\");\n        inuse--;\n        return raise_error ? luaRaiseError(lua) : 1;\n    }\n\n    /* Build the arguments vector */\n    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        char dbuf[64];\n\n        if (lua_type(lua,j+1) == LUA_TNUMBER) {\n            /* We can't use lua_tolstring() for number -> string conversion\n             * since Lua uses a format specifier that loses precision. */\n            lua_Number num = lua_tonumber(lua,j+1);\n\n            obj_len = snprintf(dbuf,sizeof(dbuf),\"%.17g\",(double)num);\n            obj_s = dbuf;\n        } else {\n            obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);\n            if (obj_s == NULL) break; /* Not a string. */\n        }\n\n        /* Try to use a cached object. */\n        if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] &&\n            cached_objects_len[j] >= obj_len)\n        {\n            sds s = cached_objects[j]->ptr;\n            argv[j] = cached_objects[j];\n            cached_objects[j] = NULL;\n            memcpy(s,obj_s,obj_len+1);\n            sdssetlen(s, 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    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        inuse--;\n        return raise_error ? luaRaiseError(lua) : 1;\n    }\n\n    /* Setup our fake client for command execution */\n    c->argv = argv;\n    c->argc = argc;\n    c->user = server.lua_caller->user;\n\n    /* Process module hooks */\n    moduleCallCommandFilters(c);\n    argv = c->argv;\n    argc = c->argc;\n\n    /* Log the command if debugging is active. */\n    if (ldb.active && ldb.step) {\n        sds cmdlog = sdsnew(\"<redis>\");\n        for (j = 0; j < c->argc; j++) {\n            if (j == 10) {\n                cmdlog = sdscatprintf(cmdlog,\" ... (%d more)\",\n                    c->argc-j-1);\n                break;\n            } else {\n                cmdlog = sdscatlen(cmdlog,\" \",1);\n                cmdlog = sdscatsds(cmdlog,c->argv[j]->ptr);\n            }\n        }\n        ldbLog(cmdlog);\n    }\n\n    /* Command lookup */\n    cmd = lookupCommand(argv[0]->ptr);\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    c->cmd = c->lastcmd = cmd;\n\n    /* There are commands that are not allowed inside scripts. */\n    if (cmd->flags & CMD_NOSCRIPT) {\n        luaPushError(lua, \"This Redis command is not allowed from scripts\");\n        goto cleanup;\n    }\n\n    /* Check the ACLs. */\n    int acl_keypos;\n    int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);\n    if (acl_retval != ACL_OK) {\n        addACLLogEntry(c,acl_retval,acl_keypos,NULL);\n        if (acl_retval == ACL_DENIED_CMD)\n            luaPushError(lua, \"The user executing the script can't run this \"\n                              \"command or subcommand\");\n        else\n            luaPushError(lua, \"The user executing the script can't access \"\n                              \"at least one of the keys mentioned in the \"\n                              \"command arguments\");\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    if (cmd->flags & CMD_WRITE) {\n        int deny_write_type = writeCommandsDeniedByDiskError();\n        if (server.lua_random_dirty && !server.lua_replicate_commands) {\n            luaPushError(lua,\n                \"Write commands not allowed after non deterministic commands. Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode.\");\n            goto cleanup;\n        } else if (server.masterhost && server.repl_slave_ro &&\n                   !server.loading &&\n                   !(server.lua_caller->flags & CLIENT_MASTER))\n        {\n            luaPushError(lua, shared.roslaveerr->ptr);\n            goto cleanup;\n        } else if (deny_write_type != DISK_ERROR_TYPE_NONE) {\n            if (deny_write_type == DISK_ERROR_TYPE_RDB) {\n                luaPushError(lua, shared.bgsaveerr->ptr);\n            } else {\n                sds aof_write_err = sdscatfmt(sdsempty(),\n                    \"-MISCONF Errors writing to the AOF file: %s\\r\\n\",\n                    strerror(server.aof_last_write_errno));\n                luaPushError(lua, aof_write_err);\n                sdsfree(aof_write_err);\n            }\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    if (server.maxmemory &&             /* Maxmemory is actually enabled. */\n        !server.loading &&              /* Don't care about mem if loading. */\n        !server.masterhost &&           /* Slave must execute the script. */\n        server.lua_write_dirty == 0 &&  /* Script had no side effects so far. */\n        server.lua_oom &&               /* Detected OOM when script start. */\n        (cmd->flags & CMD_DENYOOM))\n    {\n        luaPushError(lua, shared.oomerr->ptr);\n        goto cleanup;\n    }\n\n    if (cmd->flags & CMD_RANDOM) server.lua_random_dirty = 1;\n    if (cmd->flags & CMD_WRITE) server.lua_write_dirty = 1;\n\n    /* If this is a Redis Cluster node, we need to make sure Lua is not\n     * trying to access non-local keys, with the exception of commands\n     * received from our master or when loading the AOF back in memory. */\n    if (server.cluster_enabled && !server.loading &&\n        !(server.lua_caller->flags & CLIENT_MASTER))\n    {\n        int error_code;\n        /* Duplicate relevant flags in the lua client. */\n        c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);\n        c->flags |= server.lua_caller->flags & (CLIENT_READONLY|CLIENT_ASKING);\n        if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,&error_code) !=\n                           server.cluster->myself)\n        {\n            if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) { \n                luaPushError(lua,\n                    \"Lua script attempted to execute a write command while the \"\n                    \"cluster is down and readonly\");\n            } else if (error_code == CLUSTER_REDIR_DOWN_STATE) { \n                luaPushError(lua,\n                    \"Lua script attempted to execute a command while the \"\n                    \"cluster is down\");\n            } else {\n                luaPushError(lua,\n                    \"Lua script attempted to access a non local key in a \"\n                    \"cluster node\");\n            }\n\n            goto cleanup;\n        }\n    }\n\n    /* If we are using single commands replication, we need to wrap what\n     * we propagate into a MULTI/EXEC block, so that it will be atomic like\n     * a Lua script in the context of AOF and slaves. */\n    if (server.lua_replicate_commands &&\n        !server.lua_multi_emitted &&\n        !(server.lua_caller->flags & CLIENT_MULTI) &&\n        server.lua_write_dirty &&\n        server.lua_repl != PROPAGATE_NONE)\n    {\n        execCommandPropagateMulti(server.lua_caller);\n        server.lua_multi_emitted = 1;\n        /* Now we are in the MULTI context, the lua_client should be\n         * flag as CLIENT_MULTI. */\n        c->flags |= CLIENT_MULTI;\n    }\n\n    /* Run the command */\n    int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS;\n    if (server.lua_replicate_commands) {\n        /* Set flags according to redis.set_repl() settings. */\n        if (server.lua_repl & PROPAGATE_AOF)\n            call_flags |= CMD_CALL_PROPAGATE_AOF;\n        if (server.lua_repl & PROPAGATE_REPL)\n            call_flags |= CMD_CALL_PROPAGATE_REPL;\n    }\n    call(c,call_flags);\n\n    /* Convert the result of the Redis command into a suitable Lua type.\n     * The first thing we need is to create a single string from the client\n     * output buffers. */\n    if (listLength(c->reply) == 0 && c->bufpos < PROTO_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            clientReplyBlock *o = listNodeValue(listFirst(c->reply));\n\n            reply = sdscatlen(reply,o->buf,o->used);\n            listDelNode(c->reply,listFirst(c->reply));\n        }\n    }\n    if (raise_error && reply[0] != '-') raise_error = 0;\n    redisProtocolToLuaType(lua,reply);\n\n    /* If the debugger is active, log the reply from Redis. */\n    if (ldb.active && ldb.step)\n        ldbLogRedisReply(reply);\n\n    /* Sort the output array if needed, assuming it is a non-null multi bulk\n     * reply as expected. */\n    if ((cmd->flags & CMD_SORT_FOR_SCRIPT) &&\n        (server.lua_replicate_commands == 0) &&\n        (reply[0] == '*' && reply[1] != '-')) {\n            luaSortArray(lua);\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 == OBJ_ENCODING_RAW ||\n             o->encoding == OBJ_ENCODING_EMBSTR) &&\n            sdslen(o->ptr) <= LUA_CMD_OBJCACHE_MAX_LEN)\n        {\n            sds s = o->ptr;\n            if (cached_objects[j]) decrRefCount(cached_objects[j]);\n            cached_objects[j] = o;\n            cached_objects_len[j] = sdsalloc(s);\n        } else {\n            decrRefCount(o);\n        }\n    }\n\n    if (c->argv != argv) {\n        zfree(c->argv);\n        argv = NULL;\n        argv_size = 0;\n    }\n\n    c->user = NULL;\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        inuse--;\n        return luaRaiseError(lua);\n    }\n    inuse--;\n    return 1;\n}\n\n/* redis.call() */\nint luaRedisCallCommand(lua_State *lua) {\n    return luaRedisGenericCommand(lua,1);\n}\n\n/* redis.pcall() */\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        lua_pushstring(lua, \"wrong number of arguments\");\n        return lua_error(lua);\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\n/* redis.error_reply() */\nint luaRedisErrorReplyCommand(lua_State *lua) {\n    return luaRedisReturnSingleFieldTable(lua,\"err\");\n}\n\n/* redis.status_reply() */\nint luaRedisStatusReplyCommand(lua_State *lua) {\n    return luaRedisReturnSingleFieldTable(lua,\"ok\");\n}\n\n/* redis.replicate_commands()\n *\n * Turn on single commands replication if the script never called\n * a write command so far, and returns true. Otherwise if the script\n * already started to write, returns false and stick to whole scripts\n * replication, which is our default. */\nint luaRedisReplicateCommandsCommand(lua_State *lua) {\n    if (server.lua_write_dirty) {\n        lua_pushboolean(lua,0);\n    } else {\n        server.lua_replicate_commands = 1;\n        /* When we switch to single commands replication, we can provide\n         * different math.random() sequences at every call, which is what\n         * the user normally expects. */\n        redisSrand48(rand());\n        lua_pushboolean(lua,1);\n    }\n    return 1;\n}\n\n/* redis.breakpoint()\n *\n * Allows to stop execution during a debuggign session from within\n * the Lua code implementation, like if a breakpoint was set in the code\n * immediately after the function. */\nint luaRedisBreakpointCommand(lua_State *lua) {\n    if (ldb.active) {\n        ldb.luabp = 1;\n        lua_pushboolean(lua,1);\n    } else {\n        lua_pushboolean(lua,0);\n    }\n    return 1;\n}\n\n/* redis.debug()\n *\n * Log a string message into the output console.\n * Can take multiple arguments that will be separated by commas.\n * Nothing is returned to the caller. */\nint luaRedisDebugCommand(lua_State *lua) {\n    if (!ldb.active) return 0;\n    int argc = lua_gettop(lua);\n    sds log = sdscatprintf(sdsempty(),\"<debug> line %d: \", ldb.currentline);\n    while(argc--) {\n        log = ldbCatStackValue(log,lua,-1 - argc);\n        if (argc != 0) log = sdscatlen(log,\", \",2);\n    }\n    ldbLog(log);\n    return 0;\n}\n\n/* redis.set_repl()\n *\n * Set the propagation of write commands executed in the context of the\n * script to on/off for AOF and slaves. */\nint luaRedisSetReplCommand(lua_State *lua) {\n    int argc = lua_gettop(lua);\n    int flags;\n\n    if (server.lua_replicate_commands == 0) {\n        lua_pushstring(lua, \"You can set the replication behavior only after turning on single commands replication with redis.replicate_commands().\");\n        return lua_error(lua);\n    } else if (argc != 1) {\n        lua_pushstring(lua, \"redis.set_repl() requires two arguments.\");\n        return lua_error(lua);\n    }\n\n    flags = lua_tonumber(lua,-1);\n    if ((flags & ~(PROPAGATE_AOF|PROPAGATE_REPL)) != 0) {\n        lua_pushstring(lua, \"Invalid replication flags. Use REPL_AOF, REPL_REPLICA, REPL_ALL or REPL_NONE.\");\n        return lua_error(lua);\n    }\n    server.lua_repl = flags;\n    return 0;\n}\n\n/* redis.log() */\nint luaLogCommand(lua_State *lua) {\n    int j, argc = lua_gettop(lua);\n    int level;\n    sds log;\n\n    if (argc < 2) {\n        lua_pushstring(lua, \"redis.log() requires two arguments or more.\");\n        return lua_error(lua);\n    } else if (!lua_isnumber(lua,-argc)) {\n        lua_pushstring(lua, \"First argument must be a number (log level).\");\n        return lua_error(lua);\n    }\n    level = lua_tonumber(lua,-argc);\n    if (level < LL_DEBUG || level > LL_WARNING) {\n        lua_pushstring(lua, \"Invalid debug level.\");\n        return lua_error(lua);\n    }\n    if (level < server.verbosity) return 0;\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    serverLogRaw(level,log);\n    sdsfree(log);\n    return 0;\n}\n\n/* redis.setresp() */\nint luaSetResp(lua_State *lua) {\n    int argc = lua_gettop(lua);\n\n    if (argc != 1) {\n        lua_pushstring(lua, \"redis.setresp() requires one argument.\");\n        return lua_error(lua);\n    }\n\n    int resp = lua_tonumber(lua,-argc);\n    if (resp != 2 && resp != 3) {\n        lua_pushstring(lua, \"RESP version must be 2 or 3.\");\n        return lua_error(lua);\n    }\n\n    server.lua_client->resp = resp;\n    return 0;\n}\n\n/* ---------------------------------------------------------------------------\n * Lua engine initialization and reset.\n * ------------------------------------------------------------------------- */\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);\nLUALIB_API int (luaopen_bit) (lua_State *L);\n\nvoid luaLoadLibraries(lua_State *lua) {\n    luaLoadLib(lua, \"\", luaopen_base);\n    luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);\n    luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);\n    luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);\n    luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug);\n    luaLoadLib(lua, \"cjson\", luaopen_cjson);\n    luaLoadLib(lua, \"struct\", luaopen_struct);\n    luaLoadLib(lua, \"cmsgpack\", luaopen_cmsgpack);\n    luaLoadLib(lua, \"bit\", luaopen_bit);\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. */\nvoid luaRemoveUnsupportedFunctions(lua_State *lua) {\n    lua_pushnil(lua);\n    lua_setglobal(lua,\"loadfile\");\n    lua_pushnil(lua);\n    lua_setglobal(lua,\"dofile\");\n}\n\n/* This function installs metamethods in the global table _G that prevent\n * the creation of globals accidentally.\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. */\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 dbg=debug\\n\";\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 dbg.getinfo(2) then\\n\";\n    s[j++]=\"    local w = dbg.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 dbg.getinfo(2) and dbg.getinfo(2, \\\"S\\\").what ~= \\\"C\\\" then\\n\";\n    s[j++]=\"    error(\\\"Script attempted to access nonexistent 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++]=\"debug = nil\\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 * This function is called the first time at server startup with\n * the 'setup' argument set to 1.\n *\n * It can be called again multiple times during the lifetime of the Redis\n * process, with 'setup' set to 0, and following a scriptingRelease() call,\n * in order to reset the Lua scripting environment.\n *\n * However it is simpler to just call scriptingReset() that does just that. */\nvoid scriptingInit(int setup) {\n    lua_State *lua = lua_open();\n\n    if (setup) {\n        server.lua_client = NULL;\n        server.lua_caller = NULL;\n        server.lua_cur_script = NULL;\n        server.lua_timedout = 0;\n        ldbInit();\n    }\n\n    luaLoadLibraries(lua);\n    luaRemoveUnsupportedFunctions(lua);\n\n    /* Initialize a dictionary we use to map SHAs to scripts.\n     * This is useful for replication, as we need to replicate EVALSHA\n     * as EVAL, so we need to remember the associated script. */\n    server.lua_scripts = dictCreate(&shaScriptObjectDictType,NULL);\n    server.lua_scripts_mem = 0;\n\n    /* Register the redis commands table and fields */\n    lua_newtable(lua);\n\n    /* redis.call */\n    lua_pushstring(lua,\"call\");\n    lua_pushcfunction(lua,luaRedisCallCommand);\n    lua_settable(lua,-3);\n\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    lua_pushstring(lua,\"log\");\n    lua_pushcfunction(lua,luaLogCommand);\n    lua_settable(lua,-3);\n\n    /* redis.setresp */\n    lua_pushstring(lua,\"setresp\");\n    lua_pushcfunction(lua,luaSetResp);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_DEBUG\");\n    lua_pushnumber(lua,LL_DEBUG);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_VERBOSE\");\n    lua_pushnumber(lua,LL_VERBOSE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_NOTICE\");\n    lua_pushnumber(lua,LL_NOTICE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"LOG_WARNING\");\n    lua_pushnumber(lua,LL_WARNING);\n    lua_settable(lua,-3);\n\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    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    /* redis.replicate_commands */\n    lua_pushstring(lua, \"replicate_commands\");\n    lua_pushcfunction(lua, luaRedisReplicateCommandsCommand);\n    lua_settable(lua, -3);\n\n    /* redis.set_repl and associated flags. */\n    lua_pushstring(lua,\"set_repl\");\n    lua_pushcfunction(lua,luaRedisSetReplCommand);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_NONE\");\n    lua_pushnumber(lua,PROPAGATE_NONE);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_AOF\");\n    lua_pushnumber(lua,PROPAGATE_AOF);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_SLAVE\");\n    lua_pushnumber(lua,PROPAGATE_REPL);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_REPLICA\");\n    lua_pushnumber(lua,PROPAGATE_REPL);\n    lua_settable(lua,-3);\n\n    lua_pushstring(lua,\"REPL_ALL\");\n    lua_pushnumber(lua,PROPAGATE_AOF|PROPAGATE_REPL);\n    lua_settable(lua,-3);\n\n    /* redis.breakpoint */\n    lua_pushstring(lua,\"breakpoint\");\n    lua_pushcfunction(lua,luaRedisBreakpointCommand);\n    lua_settable(lua,-3);\n\n    /* redis.debug */\n    lua_pushstring(lua,\"debug\");\n    lua_pushcfunction(lua,luaRedisDebugCommand);\n    lua_settable(lua,-3);\n\n    /* Finally set the table as 'redis' global var. */\n    lua_setglobal(lua,\"redis\");\n\n    /* Replace math.random and math.randomseed with our implementations. */\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        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        char *errh_func =       \"local dbg = debug\\n\"\n                                \"function __redis__err__handler(err)\\n\"\n                                \"  local i = dbg.getinfo(2,'nSl')\\n\"\n                                \"  if i and i.what == 'C' then\\n\"\n                                \"    i = dbg.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     * Note: there is no need to create it again when this function is called\n     * by scriptingReset(). */\n    if (server.lua_client == NULL) {\n        server.lua_client = createClient(NULL);\n        server.lua_client->flags |= CLIENT_LUA;\n    }\n\n    /* Lua beginners often 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    scriptingEnableGlobalsProtection(lua);\n\n    server.lua = lua;\n}\n\n/* Release resources related to Lua scripting.\n * This function is used in order to reset the scripting environment. */\nvoid scriptingRelease(void) {\n    dictRelease(server.lua_scripts);\n    server.lua_scripts_mem = 0;\n    lua_close(server.lua);\n}\n\nvoid scriptingReset(void) {\n    scriptingRelease();\n    scriptingInit(0);\n}\n\n/* Set an array of Redis String Objects as a Lua array (table) stored into a\n * global variable. */\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/* ---------------------------------------------------------------------------\n * Redis provided math.random\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/* 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 * EVAL and SCRIPT commands implementation\n * ------------------------------------------------------------------------- */\n\n/* Define a Lua function with the specified body.\n * The function name will be generated in the following form:\n *\n *   f_<hex sha1 sum>\n *\n * The function increments the reference count of the 'body' object as a\n * side effect of a successful call.\n *\n * On success a pointer to an SDS string representing the function SHA1 of the\n * just added function is returned (and will be valid until the next call\n * to scriptingReset() function), otherwise NULL is returned.\n *\n * The function handles the fact of being called with a script that already\n * exists, and in such a case, it behaves like in the success case.\n *\n * If 'c' is not NULL, on error the client is informed with an appropriate\n * error describing the nature of the problem and the Lua interpreter error. */\nsds luaCreateFunction(client *c, lua_State *lua, robj *body) {\n    char funcname[43];\n    dictEntry *de;\n\n    funcname[0] = 'f';\n    funcname[1] = '_';\n    sha1hex(funcname+2,body->ptr,sdslen(body->ptr));\n\n    sds sha = sdsnewlen(funcname+2,40);\n    if ((de = dictFind(server.lua_scripts,sha)) != NULL) {\n        sdsfree(sha);\n        return dictGetKey(de);\n    }\n\n    sds funcdef = sdsempty();\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,\"\\nend\",4);\n\n    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),\"@user_script\")) {\n        if (c != NULL) {\n            addReplyErrorFormat(c,\n                \"Error compiling script (new function): %s\\n\",\n                lua_tostring(lua,-1));\n        }\n        lua_pop(lua,1);\n        sdsfree(sha);\n        sdsfree(funcdef);\n        return NULL;\n    }\n    sdsfree(funcdef);\n\n    if (lua_pcall(lua,0,0,0)) {\n        if (c != NULL) {\n            addReplyErrorFormat(c,\"Error running script (new function): %s\\n\",\n                lua_tostring(lua,-1));\n        }\n        lua_pop(lua,1);\n        sdsfree(sha);\n        return NULL;\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    int retval = dictAdd(server.lua_scripts,sha,body);\n    serverAssertWithInfo(c ? c : server.lua_client,NULL,retval == DICT_OK);\n    server.lua_scripts_mem += sdsZmallocSize(sha) + getStringObjectSdsUsedMemory(body);\n    incrRefCount(body);\n    return sha;\n}\n\n/* This is the Lua script \"count\" hook that we use to detect scripts timeout. */\nvoid luaMaskCountHook(lua_State *lua, lua_Debug *ar) {\n    long long elapsed = mstime() - server.lua_time_start;\n    UNUSED(ar);\n    UNUSED(lua);\n\n    /* Set the timeout condition if not already set and the maximum\n     * execution time was reached. */\n    if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {\n        serverLog(LL_WARNING,\n            \"Lua slow script detected: still in execution after %lld milliseconds. \"\n            \"You can try killing the script using the SCRIPT KILL command. \"\n            \"Script SHA1 is: %s\",\n            elapsed, server.lua_cur_script);\n        server.lua_timedout = 1;\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        protectClient(server.lua_caller);\n    }\n    if (server.lua_timedout) processEventsWhileBlocked();\n    if (server.lua_kill) {\n        serverLog(LL_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\nvoid prepareLuaClient(void) {\n    /* Select the right DB in the context of the Lua client */\n    selectDb(server.lua_client,server.lua_caller->db->id);\n    server.lua_client->resp = 2; /* Default is RESP2, scripts can change it. */\n\n    /* If we are in MULTI context, flag Lua client as CLIENT_MULTI. */\n    if (server.lua_caller->flags & CLIENT_MULTI) {\n        server.lua_client->flags |= CLIENT_MULTI;\n    }\n}\n\nvoid resetLuaClient(void) {\n    /* After the script done, remove the MULTI state. */\n    server.lua_client->flags &= ~CLIENT_MULTI;\n}\n\nvoid evalGenericCommand(client *c, int evalsha) {\n    lua_State *lua = server.lua;\n    char funcname[43];\n    long long numkeys;\n    long long initial_server_dirty = server.dirty;\n    int delhook = 0, err;\n\n    /* When we replicate whole scripts, we want the same PRNG sequence at\n     * every call so that our PRNG is not affected by external state. */\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     * Thanks to this flag we'll raise an error every time a write command\n     * is called after a random command was used. */\n    server.lua_random_dirty = 0;\n    server.lua_write_dirty = 0;\n    server.lua_replicate_commands = server.lua_always_replicate_commands;\n    server.lua_multi_emitted = 0;\n    server.lua_repl = PROPAGATE_AOF|PROPAGATE_REPL;\n\n    /* Get the number of arguments that are keys */\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != C_OK)\n        return;\n    if (numkeys > (c->argc - 3)) {\n        addReplyError(c,\"Number of keys can't be greater than number of args\");\n        return;\n    } else if (numkeys < 0) {\n        addReplyError(c,\"Number of keys can't be negative\");\n        return;\n    }\n\n    /* We obtain the script SHA1, then check if this function is already\n     * defined into the Lua state */\n    funcname[0] = 'f';\n    funcname[1] = '_';\n    if (!evalsha) {\n        /* Hash the code if this is an EVAL call */\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        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_getglobal(lua, funcname);\n    if (lua_isnil(lua,-1)) {\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        if (evalsha) {\n            lua_pop(lua,1); /* remove the error handler from the stack. */\n            addReply(c, shared.noscripterr);\n            return;\n        }\n        if (luaCreateFunction(c,lua,c->argv[1]) == NULL) {\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 NULL. */\n            return;\n        }\n        /* Now the following is guaranteed to return non nil */\n        lua_getglobal(lua, funcname);\n        serverAssert(!lua_isnil(lua,-1));\n    }\n\n    /* Populate the argv and keys table accordingly to the arguments that\n     * EVAL received. */\n    luaSetGlobalArray(lua,\"KEYS\",c->argv+3,numkeys);\n    luaSetGlobalArray(lua,\"ARGV\",c->argv+3+numkeys,c->argc-3-numkeys);\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     * We set the hook only if the time limit is enabled as the hook will\n     * make the Lua script execution slower.\n     *\n     * If we are debugging, we set instead a \"line\" hook so that the\n     * debugger is call-back at every line executed by the script. */\n    server.lua_caller = c;\n    server.lua_cur_script = funcname + 2;\n    server.lua_time_start = mstime();\n    server.lua_kill = 0;\n    if (server.lua_time_limit > 0 && ldb.active == 0) {\n        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);\n        delhook = 1;\n    } else if (ldb.active) {\n        lua_sethook(server.lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000);\n        delhook = 1;\n    }\n\n    prepareLuaClient();\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    err = lua_pcall(lua,0,1,-2);\n\n    resetLuaClient();\n\n    /* Perform some cleanup that we need to do both on error and success. */\n    if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */\n    if (server.lua_timedout) {\n        server.lua_timedout = 0;\n        /* Restore the client that was protected when the script timeout\n         * was detected. */\n        unprotectClient(c);\n        if (server.masterhost && server.master)\n            queueClientForReprocessing(server.master);\n    }\n    server.lua_caller = NULL;\n    server.lua_cur_script = NULL;\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    if (err) {\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        luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */\n        lua_pop(lua,1); /* Remove the error handler. */\n    }\n\n    /* If we are using single commands replication, emit EXEC if there\n     * was at least a write. */\n    if (server.lua_replicate_commands) {\n        preventCommandPropagation(c);\n        if (server.lua_multi_emitted) {\n            execCommandPropagateExec(c);\n        }\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     * 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     * 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    if (evalsha && !server.lua_replicate_commands) {\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            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);\n\n            replicationScriptCacheAdd(c->argv[1]->ptr);\n            serverAssertWithInfo(c,NULL,script != NULL);\n\n            /* If the script did not produce any changes in the dataset we want\n             * just to replicate it as SCRIPT LOAD, otherwise we risk running\n             * an aborted script on slaves (that may then produce results there)\n             * or just running a CPU costly read-only script on the slaves. */\n            if (server.dirty == initial_server_dirty) {\n                rewriteClientCommandVector(c,3,\n                    resetRefCount(createStringObject(\"SCRIPT\",6)),\n                    resetRefCount(createStringObject(\"LOAD\",4)),\n                    script);\n            } else {\n                rewriteClientCommandArgument(c,0,\n                    resetRefCount(createStringObject(\"EVAL\",4)));\n                rewriteClientCommandArgument(c,1,script);\n            }\n            forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF);\n        }\n    }\n}\n\nvoid evalCommand(client *c) {\n    if (!(c->flags & CLIENT_LUA_DEBUG))\n        evalGenericCommand(c,0);\n    else\n        evalGenericCommandWithDebugging(c,0);\n}\n\nvoid evalShaCommand(client *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    if (!(c->flags & CLIENT_LUA_DEBUG))\n        evalGenericCommand(c,1);\n    else {\n        addReplyError(c,\"Please use EVAL instead of EVALSHA for debugging\");\n        return;\n    }\n}\n\nvoid scriptCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"DEBUG (yes|sync|no) -- Set the debug mode for subsequent scripts executed.\",\n\"EXISTS <sha1> [<sha1> ...] -- Return information about the existence of the scripts in the script cache.\",\n\"FLUSH -- Flush the Lua scripts cache. Very dangerous on replicas.\",\n\"KILL -- Kill the currently executing Lua script.\",\n\"LOAD <script> -- Load a script into the scripts cache, without executing it.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else 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    } else if (c->argc >= 2 && !strcasecmp(c->argv[1]->ptr,\"exists\")) {\n        int j;\n\n        addReplyArrayLen(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    } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,\"load\")) {\n        sds sha = luaCreateFunction(c,server.lua,c->argv[2]);\n        if (sha == NULL) return; /* The error was sent by luaCreateFunction(). */\n        addReplyBulkCBuffer(c,sha,40);\n        forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF);\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_caller->flags & CLIENT_MASTER) {\n            addReplySds(c,sdsnew(\"-UNKILLABLE The busy script was sent by a master instance in the context of replication and cannot be killed.\\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    } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,\"debug\")) {\n        if (clientHasPendingReplies(c)) {\n            addReplyError(c,\"SCRIPT DEBUG must be called outside a pipeline\");\n            return;\n        }\n        if (!strcasecmp(c->argv[2]->ptr,\"no\")) {\n            ldbDisable(c);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"yes\")) {\n            ldbEnable(c);\n            addReply(c,shared.ok);\n        } else if (!strcasecmp(c->argv[2]->ptr,\"sync\")) {\n            ldbEnable(c);\n            addReply(c,shared.ok);\n            c->flags |= CLIENT_LUA_DEBUG_SYNC;\n        } else {\n            addReplyError(c,\"Use SCRIPT DEBUG yes/sync/no\");\n            return;\n        }\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\n/* ---------------------------------------------------------------------------\n * LDB: Redis Lua debugging facilities\n * ------------------------------------------------------------------------- */\n\n/* Initialize Lua debugger data structures. */\nvoid ldbInit(void) {\n    ldb.conn = NULL;\n    ldb.active = 0;\n    ldb.logs = listCreate();\n    listSetFreeMethod(ldb.logs,(void (*)(void*))sdsfree);\n    ldb.children = listCreate();\n    ldb.src = NULL;\n    ldb.lines = 0;\n    ldb.cbuf = sdsempty();\n}\n\n/* Remove all the pending messages in the specified list. */\nvoid ldbFlushLog(list *log) {\n    listNode *ln;\n\n    while((ln = listFirst(log)) != NULL)\n        listDelNode(log,ln);\n}\n\n/* Enable debug mode of Lua scripts for this client. */\nvoid ldbEnable(client *c) {\n    c->flags |= CLIENT_LUA_DEBUG;\n    ldbFlushLog(ldb.logs);\n    ldb.conn = c->conn;\n    ldb.step = 1;\n    ldb.bpcount = 0;\n    ldb.luabp = 0;\n    sdsfree(ldb.cbuf);\n    ldb.cbuf = sdsempty();\n    ldb.maxlen = LDB_MAX_LEN_DEFAULT;\n    ldb.maxlen_hint_sent = 0;\n}\n\n/* Exit debugging mode from the POV of client. This function is not enough\n * to properly shut down a client debugging session, see ldbEndSession()\n * for more information. */\nvoid ldbDisable(client *c) {\n    c->flags &= ~(CLIENT_LUA_DEBUG|CLIENT_LUA_DEBUG_SYNC);\n}\n\n/* Append a log entry to the specified LDB log. */\nvoid ldbLog(sds entry) {\n    listAddNodeTail(ldb.logs,entry);\n}\n\n/* A version of ldbLog() which prevents producing logs greater than\n * ldb.maxlen. The first time the limit is reached an hint is generated\n * to inform the user that reply trimming can be disabled using the\n * debugger \"maxlen\" command. */\nvoid ldbLogWithMaxLen(sds entry) {\n    int trimmed = 0;\n    if (ldb.maxlen && sdslen(entry) > ldb.maxlen) {\n        sdsrange(entry,0,ldb.maxlen-1);\n        entry = sdscatlen(entry,\" ...\",4);\n        trimmed = 1;\n    }\n    ldbLog(entry);\n    if (trimmed && ldb.maxlen_hint_sent == 0) {\n        ldb.maxlen_hint_sent = 1;\n        ldbLog(sdsnew(\n        \"<hint> The above reply was trimmed. Use 'maxlen 0' to disable trimming.\"));\n    }\n}\n\n/* Send ldb.logs to the debugging client as a multi-bulk reply\n * consisting of simple strings. Log entries which include newlines have them\n * replaced with spaces. The entries sent are also consumed. */\nvoid ldbSendLogs(void) {\n    sds proto = sdsempty();\n    proto = sdscatfmt(proto,\"*%i\\r\\n\", (int)listLength(ldb.logs));\n    while(listLength(ldb.logs)) {\n        listNode *ln = listFirst(ldb.logs);\n        proto = sdscatlen(proto,\"+\",1);\n        sdsmapchars(ln->value,\"\\r\\n\",\"  \",2);\n        proto = sdscatsds(proto,ln->value);\n        proto = sdscatlen(proto,\"\\r\\n\",2);\n        listDelNode(ldb.logs,ln);\n    }\n    if (connWrite(ldb.conn,proto,sdslen(proto)) == -1) {\n        /* Avoid warning. We don't check the return value of write()\n         * since the next read() will catch the I/O error and will\n         * close the debugging session. */\n    }\n    sdsfree(proto);\n}\n\n/* Start a debugging session before calling EVAL implementation.\n * The techique we use is to capture the client socket file descriptor,\n * in order to perform direct I/O with it from within Lua hooks. This\n * way we don't have to re-enter Redis in order to handle I/O.\n *\n * The function returns 1 if the caller should proceed to call EVAL,\n * and 0 if instead the caller should abort the operation (this happens\n * for the parent in a forked session, since it's up to the children\n * to continue, or when fork returned an error).\n *\n * The caller should call ldbEndSession() only if ldbStartSession()\n * returned 1. */\nint ldbStartSession(client *c) {\n    ldb.forked = (c->flags & CLIENT_LUA_DEBUG_SYNC) == 0;\n    if (ldb.forked) {\n        pid_t cp = redisFork();\n        if (cp == -1) {\n            addReplyError(c,\"Fork() failed: can't run EVAL in debugging mode.\");\n            return 0;\n        } else if (cp == 0) {\n            /* Child. Let's ignore important signals handled by the parent. */\n            struct sigaction act;\n            sigemptyset(&act.sa_mask);\n            act.sa_flags = 0;\n            act.sa_handler = SIG_IGN;\n            sigaction(SIGTERM, &act, NULL);\n            sigaction(SIGINT, &act, NULL);\n\n            /* Log the creation of the child and close the listening\n             * socket to make sure if the parent crashes a reset is sent\n             * to the clients. */\n            serverLog(LL_WARNING,\"Redis forked for debugging eval\");\n        } else {\n            /* Parent */\n            listAddNodeTail(ldb.children,(void*)(unsigned long)cp);\n            freeClientAsync(c); /* Close the client in the parent side. */\n            return 0;\n        }\n    } else {\n        serverLog(LL_WARNING,\n            \"Redis synchronous debugging eval session started\");\n    }\n\n    /* Setup our debugging session. */\n    connBlock(ldb.conn);\n    connSendTimeout(ldb.conn,5000);\n    ldb.active = 1;\n\n    /* First argument of EVAL is the script itself. We split it into different\n     * lines since this is the way the debugger accesses the source code. */\n    sds srcstring = sdsdup(c->argv[1]->ptr);\n    size_t srclen = sdslen(srcstring);\n    while(srclen && (srcstring[srclen-1] == '\\n' ||\n                     srcstring[srclen-1] == '\\r'))\n    {\n        srcstring[--srclen] = '\\0';\n    }\n    sdssetlen(srcstring,srclen);\n    ldb.src = sdssplitlen(srcstring,sdslen(srcstring),\"\\n\",1,&ldb.lines);\n    sdsfree(srcstring);\n    return 1;\n}\n\n/* End a debugging session after the EVAL call with debugging enabled\n * returned. */\nvoid ldbEndSession(client *c) {\n    /* Emit the remaining logs and an <endsession> mark. */\n    ldbLog(sdsnew(\"<endsession>\"));\n    ldbSendLogs();\n\n    /* If it's a fork()ed session, we just exit. */\n    if (ldb.forked) {\n        writeToClient(c,0);\n        serverLog(LL_WARNING,\"Lua debugging session child exiting\");\n        exitFromChild(0);\n    } else {\n        serverLog(LL_WARNING,\n            \"Redis synchronous debugging eval session ended\");\n    }\n\n    /* Otherwise let's restore client's state. */\n    connNonBlock(ldb.conn);\n    connSendTimeout(ldb.conn,0);\n\n    /* Close the client connectin after sending the final EVAL reply\n     * in order to signal the end of the debugging session. */\n    c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n\n    /* Cleanup. */\n    sdsfreesplitres(ldb.src,ldb.lines);\n    ldb.lines = 0;\n    ldb.active = 0;\n}\n\n/* If the specified pid is among the list of children spawned for\n * forked debugging sessions, it is removed from the children list.\n * If the pid was found non-zero is returned. */\nint ldbRemoveChild(pid_t pid) {\n    listNode *ln = listSearchKey(ldb.children,(void*)(unsigned long)pid);\n    if (ln) {\n        listDelNode(ldb.children,ln);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return the number of children we still did not receive termination\n * acknowledge via wait() in the parent process. */\nint ldbPendingChildren(void) {\n    return listLength(ldb.children);\n}\n\n/* Kill all the forked sessions. */\nvoid ldbKillForkedSessions(void) {\n    listIter li;\n    listNode *ln;\n\n    listRewind(ldb.children,&li);\n    while((ln = listNext(&li))) {\n        pid_t pid = (unsigned long) ln->value;\n        serverLog(LL_WARNING,\"Killing debugging session %ld\",(long)pid);\n        kill(pid,SIGKILL);\n    }\n    listRelease(ldb.children);\n    ldb.children = listCreate();\n}\n\n/* Wrapper for EVAL / EVALSHA that enables debugging, and makes sure\n * that when EVAL returns, whatever happened, the session is ended. */\nvoid evalGenericCommandWithDebugging(client *c, int evalsha) {\n    if (ldbStartSession(c)) {\n        evalGenericCommand(c,evalsha);\n        ldbEndSession(c);\n    } else {\n        ldbDisable(c);\n    }\n}\n\n/* Return a pointer to ldb.src source code line, considering line to be\n * one-based, and returning a special string for out of range lines. */\nchar *ldbGetSourceLine(int line) {\n    int idx = line-1;\n    if (idx < 0 || idx >= ldb.lines) return \"<out of range source code line>\";\n    return ldb.src[idx];\n}\n\n/* Return true if there is a breakpoint in the specified line. */\nint ldbIsBreakpoint(int line) {\n    int j;\n\n    for (j = 0; j < ldb.bpcount; j++)\n        if (ldb.bp[j] == line) return 1;\n    return 0;\n}\n\n/* Add the specified breakpoint. Ignore it if we already reached the max.\n * Returns 1 if the breakpoint was added (or was already set). 0 if there is\n * no space for the breakpoint or if the line is invalid. */\nint ldbAddBreakpoint(int line) {\n    if (line <= 0 || line > ldb.lines) return 0;\n    if (!ldbIsBreakpoint(line) && ldb.bpcount != LDB_BREAKPOINTS_MAX) {\n        ldb.bp[ldb.bpcount++] = line;\n        return 1;\n    }\n    return 0;\n}\n\n/* Remove the specified breakpoint, returning 1 if the operation was\n * performed or 0 if there was no such breakpoint. */\nint ldbDelBreakpoint(int line) {\n    int j;\n\n    for (j = 0; j < ldb.bpcount; j++) {\n        if (ldb.bp[j] == line) {\n            ldb.bpcount--;\n            memmove(ldb.bp+j,ldb.bp+j+1,ldb.bpcount-j);\n            return 1;\n        }\n    }\n    return 0;\n}\n\n/* Expect a valid multi-bulk command in the debugging client query buffer.\n * On success the command is parsed and returned as an array of SDS strings,\n * otherwise NULL is returned and there is to read more buffer. */\nsds *ldbReplParseCommand(int *argcp) {\n    sds *argv = NULL;\n    int argc = 0;\n    if (sdslen(ldb.cbuf) == 0) return NULL;\n\n    /* Working on a copy is simpler in this case. We can modify it freely\n     * for the sake of simpler parsing. */\n    sds copy = sdsdup(ldb.cbuf);\n    char *p = copy;\n\n    /* This Redis protocol parser is a joke... just the simplest thing that\n     * works in this context. It is also very forgiving regarding broken\n     * protocol. */\n\n    /* Seek and parse *<count>\\r\\n. */\n    p = strchr(p,'*'); if (!p) goto protoerr;\n    char *plen = p+1; /* Multi bulk len pointer. */\n    p = strstr(p,\"\\r\\n\"); if (!p) goto protoerr;\n    *p = '\\0'; p += 2;\n    *argcp = atoi(plen);\n    if (*argcp <= 0 || *argcp > 1024) goto protoerr;\n\n    /* Parse each argument. */\n    argv = zmalloc(sizeof(sds)*(*argcp));\n    argc = 0;\n    while(argc < *argcp) {\n        if (*p != '$') goto protoerr;\n        plen = p+1; /* Bulk string len pointer. */\n        p = strstr(p,\"\\r\\n\"); if (!p) goto protoerr;\n        *p = '\\0'; p += 2;\n        int slen = atoi(plen); /* Length of this arg. */\n        if (slen <= 0 || slen > 1024) goto protoerr;\n        argv[argc++] = sdsnewlen(p,slen);\n        p += slen; /* Skip the already parsed argument. */\n        if (p[0] != '\\r' || p[1] != '\\n') goto protoerr;\n        p += 2; /* Skip \\r\\n. */\n    }\n    sdsfree(copy);\n    return argv;\n\nprotoerr:\n    sdsfreesplitres(argv,argc);\n    sdsfree(copy);\n    return NULL;\n}\n\n/* Log the specified line in the Lua debugger output. */\nvoid ldbLogSourceLine(int lnum) {\n    char *line = ldbGetSourceLine(lnum);\n    char *prefix;\n    int bp = ldbIsBreakpoint(lnum);\n    int current = ldb.currentline == lnum;\n\n    if (current && bp)\n        prefix = \"->#\";\n    else if (current)\n        prefix = \"-> \";\n    else if (bp)\n        prefix = \"  #\";\n    else\n        prefix = \"   \";\n    sds thisline = sdscatprintf(sdsempty(),\"%s%-3d %s\", prefix, lnum, line);\n    ldbLog(thisline);\n}\n\n/* Implement the \"list\" command of the Lua debugger. If around is 0\n * the whole file is listed, otherwise only a small portion of the file\n * around the specified line is shown. When a line number is specified\n * the amonut of context (lines before/after) is specified via the\n * 'context' argument. */\nvoid ldbList(int around, int context) {\n    int j;\n\n    for (j = 1; j <= ldb.lines; j++) {\n        if (around != 0 && abs(around-j) > context) continue;\n        ldbLogSourceLine(j);\n    }\n}\n\n/* Append an human readable representation of the Lua value at position 'idx'\n * on the stack of the 'lua' state, to the SDS string passed as argument.\n * The new SDS string with the represented value attached is returned.\n * Used in order to implement ldbLogStackValue().\n *\n * The element is not automatically removed from the stack, nor it is\n * converted to a different type. */\n#define LDB_MAX_VALUES_DEPTH (LUA_MINSTACK/2)\nsds ldbCatStackValueRec(sds s, lua_State *lua, int idx, int level) {\n    int t = lua_type(lua,idx);\n\n    if (level++ == LDB_MAX_VALUES_DEPTH)\n        return sdscat(s,\"<max recursion level reached! Nested table?>\");\n\n    switch(t) {\n    case LUA_TSTRING:\n        {\n        size_t strl;\n        char *strp = (char*)lua_tolstring(lua,idx,&strl);\n        s = sdscatrepr(s,strp,strl);\n        }\n        break;\n    case LUA_TBOOLEAN:\n        s = sdscat(s,lua_toboolean(lua,idx) ? \"true\" : \"false\");\n        break;\n    case LUA_TNUMBER:\n        s = sdscatprintf(s,\"%g\",(double)lua_tonumber(lua,idx));\n        break;\n    case LUA_TNIL:\n        s = sdscatlen(s,\"nil\",3);\n        break;\n    case LUA_TTABLE:\n        {\n        int expected_index = 1; /* First index we expect in an array. */\n        int is_array = 1; /* Will be set to null if check fails. */\n        /* Note: we create two representations at the same time, one\n         * assuming the table is an array, one assuming it is not. At the\n         * end we know what is true and select the right one. */\n        sds repr1 = sdsempty();\n        sds repr2 = sdsempty();\n        lua_pushnil(lua); /* The first key to start the iteration is nil. */\n        while (lua_next(lua,idx-1)) {\n            /* Test if so far the table looks like an array. */\n            if (is_array &&\n                (lua_type(lua,-2) != LUA_TNUMBER ||\n                 lua_tonumber(lua,-2) != expected_index)) is_array = 0;\n            /* Stack now: table, key, value */\n            /* Array repr. */\n            repr1 = ldbCatStackValueRec(repr1,lua,-1,level);\n            repr1 = sdscatlen(repr1,\"; \",2);\n            /* Full repr. */\n            repr2 = sdscatlen(repr2,\"[\",1);\n            repr2 = ldbCatStackValueRec(repr2,lua,-2,level);\n            repr2 = sdscatlen(repr2,\"]=\",2);\n            repr2 = ldbCatStackValueRec(repr2,lua,-1,level);\n            repr2 = sdscatlen(repr2,\"; \",2);\n            lua_pop(lua,1); /* Stack: table, key. Ready for next iteration. */\n            expected_index++;\n        }\n        /* Strip the last \" ;\" from both the representations. */\n        if (sdslen(repr1)) sdsrange(repr1,0,-3);\n        if (sdslen(repr2)) sdsrange(repr2,0,-3);\n        /* Select the right one and discard the other. */\n        s = sdscatlen(s,\"{\",1);\n        s = sdscatsds(s,is_array ? repr1 : repr2);\n        s = sdscatlen(s,\"}\",1);\n        sdsfree(repr1);\n        sdsfree(repr2);\n        }\n        break;\n    case LUA_TFUNCTION:\n    case LUA_TUSERDATA:\n    case LUA_TTHREAD:\n    case LUA_TLIGHTUSERDATA:\n        {\n        const void *p = lua_topointer(lua,idx);\n        char *typename = \"unknown\";\n        if (t == LUA_TFUNCTION) typename = \"function\";\n        else if (t == LUA_TUSERDATA) typename = \"userdata\";\n        else if (t == LUA_TTHREAD) typename = \"thread\";\n        else if (t == LUA_TLIGHTUSERDATA) typename = \"light-userdata\";\n        s = sdscatprintf(s,\"\\\"%s@%p\\\"\",typename,p);\n        }\n        break;\n    default:\n        s = sdscat(s,\"\\\"<unknown-lua-type>\\\"\");\n        break;\n    }\n    return s;\n}\n\n/* Higher level wrapper for ldbCatStackValueRec() that just uses an initial\n * recursion level of '0'. */\nsds ldbCatStackValue(sds s, lua_State *lua, int idx) {\n    return ldbCatStackValueRec(s,lua,idx,0);\n}\n\n/* Produce a debugger log entry representing the value of the Lua object\n * currently on the top of the stack. The element is ot popped nor modified.\n * Check ldbCatStackValue() for the actual implementation. */\nvoid ldbLogStackValue(lua_State *lua, char *prefix) {\n    sds s = sdsnew(prefix);\n    s = ldbCatStackValue(s,lua,-1);\n    ldbLogWithMaxLen(s);\n}\n\nchar *ldbRedisProtocolToHuman_Int(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Bulk(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Status(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_MultiBulk(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Set(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Map(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Null(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Bool(sds *o, char *reply);\nchar *ldbRedisProtocolToHuman_Double(sds *o, char *reply);\n\n/* Get Redis protocol from 'reply' and appends it in human readable form to\n * the passed SDS string 'o'.\n *\n * Note that the SDS string is passed by reference (pointer of pointer to\n * char*) so that we can return a modified pointer, as for SDS semantics. */\nchar *ldbRedisProtocolToHuman(sds *o, char *reply) {\n    char *p = reply;\n    switch(*p) {\n    case ':': p = ldbRedisProtocolToHuman_Int(o,reply); break;\n    case '$': p = ldbRedisProtocolToHuman_Bulk(o,reply); break;\n    case '+': p = ldbRedisProtocolToHuman_Status(o,reply); break;\n    case '-': p = ldbRedisProtocolToHuman_Status(o,reply); break;\n    case '*': p = ldbRedisProtocolToHuman_MultiBulk(o,reply); break;\n    case '~': p = ldbRedisProtocolToHuman_Set(o,reply); break;\n    case '%': p = ldbRedisProtocolToHuman_Map(o,reply); break;\n    case '_': p = ldbRedisProtocolToHuman_Null(o,reply); break;\n    case '#': p = ldbRedisProtocolToHuman_Bool(o,reply); break;\n    case ',': p = ldbRedisProtocolToHuman_Double(o,reply); break;\n    }\n    return p;\n}\n\n/* The following functions are helpers for ldbRedisProtocolToHuman(), each\n * take care of a given Redis return type. */\n\nchar *ldbRedisProtocolToHuman_Int(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    *o = sdscatlen(*o,reply+1,p-reply-1);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_Bulk(sds *o, 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        *o = sdscatlen(*o,\"NULL\",4);\n        return p+2;\n    } else {\n        *o = sdscatrepr(*o,p+2,bulklen);\n        return p+2+bulklen+2;\n    }\n}\n\nchar *ldbRedisProtocolToHuman_Status(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n\n    *o = sdscatrepr(*o,reply,p-reply);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_MultiBulk(sds *o, 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        *o = sdscatlen(*o,\"NULL\",4);\n        return p;\n    }\n    *o = sdscatlen(*o,\"[\",1);\n    for (j = 0; j < mbulklen; j++) {\n        p = ldbRedisProtocolToHuman(o,p);\n        if (j != mbulklen-1) *o = sdscatlen(*o,\",\",1);\n    }\n    *o = sdscatlen(*o,\"]\",1);\n    return p;\n}\n\nchar *ldbRedisProtocolToHuman_Set(sds *o, 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    *o = sdscatlen(*o,\"~(\",2);\n    for (j = 0; j < mbulklen; j++) {\n        p = ldbRedisProtocolToHuman(o,p);\n        if (j != mbulklen-1) *o = sdscatlen(*o,\",\",1);\n    }\n    *o = sdscatlen(*o,\")\",1);\n    return p;\n}\n\nchar *ldbRedisProtocolToHuman_Map(sds *o, 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    *o = sdscatlen(*o,\"{\",1);\n    for (j = 0; j < mbulklen; j++) {\n        p = ldbRedisProtocolToHuman(o,p);\n        *o = sdscatlen(*o,\" => \",4);\n        p = ldbRedisProtocolToHuman(o,p);\n        if (j != mbulklen-1) *o = sdscatlen(*o,\",\",1);\n    }\n    *o = sdscatlen(*o,\"}\",1);\n    return p;\n}\n\nchar *ldbRedisProtocolToHuman_Null(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    *o = sdscatlen(*o,\"(null)\",6);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_Bool(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    if (reply[1] == 't')\n        *o = sdscatlen(*o,\"#true\",5);\n    else\n        *o = sdscatlen(*o,\"#false\",6);\n    return p+2;\n}\n\nchar *ldbRedisProtocolToHuman_Double(sds *o, char *reply) {\n    char *p = strchr(reply+1,'\\r');\n    *o = sdscatlen(*o,\"(double) \",9);\n    *o = sdscatlen(*o,reply+1,p-reply-1);\n    return p+2;\n}\n\n/* Log a Redis reply as debugger output, in an human readable format.\n * If the resulting string is longer than 'len' plus a few more chars\n * used as prefix, it gets truncated. */\nvoid ldbLogRedisReply(char *reply) {\n    sds log = sdsnew(\"<reply> \");\n    ldbRedisProtocolToHuman(&log,reply);\n    ldbLogWithMaxLen(log);\n}\n\n/* Implements the \"print <var>\" command of the Lua debugger. It scans for Lua\n * var \"varname\" starting from the current stack frame up to the top stack\n * frame. The first matching variable is printed. */\nvoid ldbPrint(lua_State *lua, char *varname) {\n    lua_Debug ar;\n\n    int l = 0; /* Stack level. */\n    while (lua_getstack(lua,l,&ar) != 0) {\n        l++;\n        const char *name;\n        int i = 1; /* Variable index. */\n        while((name = lua_getlocal(lua,&ar,i)) != NULL) {\n            i++;\n            if (strcmp(varname,name) == 0) {\n                ldbLogStackValue(lua,\"<value> \");\n                lua_pop(lua,1);\n                return;\n            } else {\n                lua_pop(lua,1); /* Discard the var name on the stack. */\n            }\n        }\n    }\n\n    /* Let's try with global vars in two selected cases */\n    if (!strcmp(varname,\"ARGV\") || !strcmp(varname,\"KEYS\")) {\n        lua_getglobal(lua, varname);\n        ldbLogStackValue(lua,\"<value> \");\n        lua_pop(lua,1);\n    } else {\n        ldbLog(sdsnew(\"No such variable.\"));\n    }\n}\n\n/* Implements the \"print\" command (without arguments) of the Lua debugger.\n * Prints all the variables in the current stack frame. */\nvoid ldbPrintAll(lua_State *lua) {\n    lua_Debug ar;\n    int vars = 0;\n\n    if (lua_getstack(lua,0,&ar) != 0) {\n        const char *name;\n        int i = 1; /* Variable index. */\n        while((name = lua_getlocal(lua,&ar,i)) != NULL) {\n            i++;\n            if (!strstr(name,\"(*temporary)\")) {\n                sds prefix = sdscatprintf(sdsempty(),\"<value> %s = \",name);\n                ldbLogStackValue(lua,prefix);\n                sdsfree(prefix);\n                vars++;\n            }\n            lua_pop(lua,1);\n        }\n    }\n\n    if (vars == 0) {\n        ldbLog(sdsnew(\"No local variables in the current context.\"));\n    }\n}\n\n/* Implements the break command to list, add and remove breakpoints. */\nvoid ldbBreak(sds *argv, int argc) {\n    if (argc == 1) {\n        if (ldb.bpcount == 0) {\n            ldbLog(sdsnew(\"No breakpoints set. Use 'b <line>' to add one.\"));\n            return;\n        } else {\n            ldbLog(sdscatfmt(sdsempty(),\"%i breakpoints set:\",ldb.bpcount));\n            int j;\n            for (j = 0; j < ldb.bpcount; j++)\n                ldbLogSourceLine(ldb.bp[j]);\n        }\n    } else {\n        int j;\n        for (j = 1; j < argc; j++) {\n            char *arg = argv[j];\n            long line;\n            if (!string2l(arg,sdslen(arg),&line)) {\n                ldbLog(sdscatfmt(sdsempty(),\"Invalid argument:'%s'\",arg));\n            } else {\n                if (line == 0) {\n                    ldb.bpcount = 0;\n                    ldbLog(sdsnew(\"All breakpoints removed.\"));\n                } else if (line > 0) {\n                    if (ldb.bpcount == LDB_BREAKPOINTS_MAX) {\n                        ldbLog(sdsnew(\"Too many breakpoints set.\"));\n                    } else if (ldbAddBreakpoint(line)) {\n                        ldbList(line,1);\n                    } else {\n                        ldbLog(sdsnew(\"Wrong line number.\"));\n                    }\n                } else if (line < 0) {\n                    if (ldbDelBreakpoint(-line))\n                        ldbLog(sdsnew(\"Breakpoint removed.\"));\n                    else\n                        ldbLog(sdsnew(\"No breakpoint in the specified line.\"));\n                }\n            }\n        }\n    }\n}\n\n/* Implements the Lua debugger \"eval\" command. It just compiles the user\n * passed fragment of code and executes it, showing the result left on\n * the stack. */\nvoid ldbEval(lua_State *lua, sds *argv, int argc) {\n    /* Glue the script together if it is composed of multiple arguments. */\n    sds code = sdsjoinsds(argv+1,argc-1,\" \",1);\n    sds expr = sdscatsds(sdsnew(\"return \"),code);\n\n    /* Try to compile it as an expression, prepending \"return \". */\n    if (luaL_loadbuffer(lua,expr,sdslen(expr),\"@ldb_eval\")) {\n        lua_pop(lua,1);\n        /* Failed? Try as a statement. */\n        if (luaL_loadbuffer(lua,code,sdslen(code),\"@ldb_eval\")) {\n            ldbLog(sdscatfmt(sdsempty(),\"<error> %s\",lua_tostring(lua,-1)));\n            lua_pop(lua,1);\n            sdsfree(code);\n            sdsfree(expr);\n            return;\n        }\n    }\n\n    /* Call it. */\n    sdsfree(code);\n    sdsfree(expr);\n    if (lua_pcall(lua,0,1,0)) {\n        ldbLog(sdscatfmt(sdsempty(),\"<error> %s\",lua_tostring(lua,-1)));\n        lua_pop(lua,1);\n        return;\n    }\n    ldbLogStackValue(lua,\"<retval> \");\n    lua_pop(lua,1);\n}\n\n/* Implement the debugger \"redis\" command. We use a trick in order to make\n * the implementation very simple: we just call the Lua redis.call() command\n * implementation, with ldb.step enabled, so as a side effect the Redis command\n * and its reply are logged. */\nvoid ldbRedis(lua_State *lua, sds *argv, int argc) {\n    int j, saved_rc = server.lua_replicate_commands;\n\n    lua_getglobal(lua,\"redis\");\n    lua_pushstring(lua,\"call\");\n    lua_gettable(lua,-2);       /* Stack: redis, redis.call */\n    for (j = 1; j < argc; j++)\n        lua_pushlstring(lua,argv[j],sdslen(argv[j]));\n    ldb.step = 1;               /* Force redis.call() to log. */\n    server.lua_replicate_commands = 1;\n    lua_pcall(lua,argc-1,1,0);  /* Stack: redis, result */\n    ldb.step = 0;               /* Disable logging. */\n    server.lua_replicate_commands = saved_rc;\n    lua_pop(lua,2);             /* Discard the result and clean the stack. */\n}\n\n/* Implements \"trace\" command of the Lua debugger. It just prints a backtrace\n * querying Lua starting from the current callframe back to the outer one. */\nvoid ldbTrace(lua_State *lua) {\n    lua_Debug ar;\n    int level = 0;\n\n    while(lua_getstack(lua,level,&ar)) {\n        lua_getinfo(lua,\"Snl\",&ar);\n        if(strstr(ar.short_src,\"user_script\") != NULL) {\n            ldbLog(sdscatprintf(sdsempty(),\"%s %s:\",\n                (level == 0) ? \"In\" : \"From\",\n                ar.name ? ar.name : \"top level\"));\n            ldbLogSourceLine(ar.currentline);\n        }\n        level++;\n    }\n    if (level == 0) {\n        ldbLog(sdsnew(\"<error> Can't retrieve Lua stack.\"));\n    }\n}\n\n/* Impleemnts the debugger \"maxlen\" command. It just queries or sets the\n * ldb.maxlen variable. */\nvoid ldbMaxlen(sds *argv, int argc) {\n    if (argc == 2) {\n        int newval = atoi(argv[1]);\n        ldb.maxlen_hint_sent = 1; /* User knows about this command. */\n        if (newval != 0 && newval <= 60) newval = 60;\n        ldb.maxlen = newval;\n    }\n    if (ldb.maxlen) {\n        ldbLog(sdscatprintf(sdsempty(),\"<value> replies are truncated at %d bytes.\",(int)ldb.maxlen));\n    } else {\n        ldbLog(sdscatprintf(sdsempty(),\"<value> replies are unlimited.\"));\n    }\n}\n\n/* Read debugging commands from client.\n * Return C_OK if the debugging session is continuing, otherwise\n * C_ERR if the client closed the connection or is timing out. */\nint ldbRepl(lua_State *lua) {\n    sds *argv;\n    int argc;\n\n    /* We continue processing commands until a command that should return\n     * to the Lua interpreter is found. */\n    while(1) {\n        while((argv = ldbReplParseCommand(&argc)) == NULL) {\n            char buf[1024];\n            int nread = connRead(ldb.conn,buf,sizeof(buf));\n            if (nread <= 0) {\n                /* Make sure the script runs without user input since the\n                 * client is no longer connected. */\n                ldb.step = 0;\n                ldb.bpcount = 0;\n                return C_ERR;\n            }\n            ldb.cbuf = sdscatlen(ldb.cbuf,buf,nread);\n        }\n\n        /* Flush the old buffer. */\n        sdsfree(ldb.cbuf);\n        ldb.cbuf = sdsempty();\n\n        /* Execute the command. */\n        if (!strcasecmp(argv[0],\"h\") || !strcasecmp(argv[0],\"help\")) {\nldbLog(sdsnew(\"Redis Lua debugger help:\"));\nldbLog(sdsnew(\"[h]elp               Show this help.\"));\nldbLog(sdsnew(\"[s]tep               Run current line and stop again.\"));\nldbLog(sdsnew(\"[n]ext               Alias for step.\"));\nldbLog(sdsnew(\"[c]continue          Run till next breakpoint.\"));\nldbLog(sdsnew(\"[l]list              List source code around current line.\"));\nldbLog(sdsnew(\"[l]list [line]       List source code around [line].\"));\nldbLog(sdsnew(\"                     line = 0 means: current position.\"));\nldbLog(sdsnew(\"[l]list [line] [ctx] In this form [ctx] specifies how many lines\"));\nldbLog(sdsnew(\"                     to show before/after [line].\"));\nldbLog(sdsnew(\"[w]hole              List all source code. Alias for 'list 1 1000000'.\"));\nldbLog(sdsnew(\"[p]rint              Show all the local variables.\"));\nldbLog(sdsnew(\"[p]rint <var>        Show the value of the specified variable.\"));\nldbLog(sdsnew(\"                     Can also show global vars KEYS and ARGV.\"));\nldbLog(sdsnew(\"[b]reak              Show all breakpoints.\"));\nldbLog(sdsnew(\"[b]reak <line>       Add a breakpoint to the specified line.\"));\nldbLog(sdsnew(\"[b]reak -<line>      Remove breakpoint from the specified line.\"));\nldbLog(sdsnew(\"[b]reak 0            Remove all breakpoints.\"));\nldbLog(sdsnew(\"[t]race              Show a backtrace.\"));\nldbLog(sdsnew(\"[e]eval <code>       Execute some Lua code (in a different callframe).\"));\nldbLog(sdsnew(\"[r]edis <cmd>        Execute a Redis command.\"));\nldbLog(sdsnew(\"[m]axlen [len]       Trim logged Redis replies and Lua var dumps to len.\"));\nldbLog(sdsnew(\"                     Specifying zero as <len> means unlimited.\"));\nldbLog(sdsnew(\"[a]bort              Stop the execution of the script. In sync\"));\nldbLog(sdsnew(\"                     mode dataset changes will be retained.\"));\nldbLog(sdsnew(\"\"));\nldbLog(sdsnew(\"Debugger functions you can call from Lua scripts:\"));\nldbLog(sdsnew(\"redis.debug()        Produce logs in the debugger console.\"));\nldbLog(sdsnew(\"redis.breakpoint()   Stop execution like if there was a breakpoing.\"));\nldbLog(sdsnew(\"                     in the next line of code.\"));\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"s\") || !strcasecmp(argv[0],\"step\") ||\n                   !strcasecmp(argv[0],\"n\") || !strcasecmp(argv[0],\"next\")) {\n            ldb.step = 1;\n            break;\n        } else if (!strcasecmp(argv[0],\"c\") || !strcasecmp(argv[0],\"continue\")){\n            break;\n        } else if (!strcasecmp(argv[0],\"t\") || !strcasecmp(argv[0],\"trace\")) {\n            ldbTrace(lua);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"m\") || !strcasecmp(argv[0],\"maxlen\")) {\n            ldbMaxlen(argv,argc);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"b\") || !strcasecmp(argv[0],\"break\")) {\n            ldbBreak(argv,argc);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"e\") || !strcasecmp(argv[0],\"eval\")) {\n            ldbEval(lua,argv,argc);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"a\") || !strcasecmp(argv[0],\"abort\")) {\n            lua_pushstring(lua, \"script aborted for user request\");\n            lua_error(lua);\n        } else if (argc > 1 &&\n                   (!strcasecmp(argv[0],\"r\") || !strcasecmp(argv[0],\"redis\"))) {\n            ldbRedis(lua,argv,argc);\n            ldbSendLogs();\n        } else if ((!strcasecmp(argv[0],\"p\") || !strcasecmp(argv[0],\"print\"))) {\n            if (argc == 2)\n                ldbPrint(lua,argv[1]);\n            else\n                ldbPrintAll(lua);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"l\") || !strcasecmp(argv[0],\"list\")){\n            int around = ldb.currentline, ctx = 5;\n            if (argc > 1) {\n                int num = atoi(argv[1]);\n                if (num > 0) around = num;\n            }\n            if (argc > 2) ctx = atoi(argv[2]);\n            ldbList(around,ctx);\n            ldbSendLogs();\n        } else if (!strcasecmp(argv[0],\"w\") || !strcasecmp(argv[0],\"whole\")){\n            ldbList(1,1000000);\n            ldbSendLogs();\n        } else {\n            ldbLog(sdsnew(\"<error> Unknown Redis Lua debugger command or \"\n                          \"wrong number of arguments.\"));\n            ldbSendLogs();\n        }\n\n        /* Free the command vector. */\n        sdsfreesplitres(argv,argc);\n    }\n\n    /* Free the current command argv if we break inside the while loop. */\n    sdsfreesplitres(argv,argc);\n    return C_OK;\n}\n\n/* This is the core of our Lua debugger, called each time Lua is about\n * to start executing a new line. */\nvoid luaLdbLineHook(lua_State *lua, lua_Debug *ar) {\n    lua_getstack(lua,0,ar);\n    lua_getinfo(lua,\"Sl\",ar);\n    ldb.currentline = ar->currentline;\n\n    int bp = ldbIsBreakpoint(ldb.currentline) || ldb.luabp;\n    int timeout = 0;\n\n    /* Events outside our script are not interesting. */\n    if(strstr(ar->short_src,\"user_script\") == NULL) return;\n\n    /* Check if a timeout occurred. */\n    if (ar->event == LUA_HOOKCOUNT && ldb.step == 0 && bp == 0) {\n        mstime_t elapsed = mstime() - server.lua_time_start;\n        mstime_t timelimit = server.lua_time_limit ?\n                             server.lua_time_limit : 5000;\n        if (elapsed >= timelimit) {\n            timeout = 1;\n            ldb.step = 1;\n        } else {\n            return; /* No timeout, ignore the COUNT event. */\n        }\n    }\n\n    if (ldb.step || bp) {\n        char *reason = \"step over\";\n        if (bp) reason = ldb.luabp ? \"redis.breakpoint() called\" :\n                                     \"break point\";\n        else if (timeout) reason = \"timeout reached, infinite loop?\";\n        ldb.step = 0;\n        ldb.luabp = 0;\n        ldbLog(sdscatprintf(sdsempty(),\n            \"* Stopped at %d, stop reason = %s\",\n            ldb.currentline, reason));\n        ldbLogSourceLine(ldb.currentline);\n        ldbSendLogs();\n        if (ldbRepl(lua) == C_ERR && timeout) {\n            /* If the client closed the connection and we have a timeout\n             * connection, let's kill the script otherwise the process\n             * will remain blocked indefinitely. */\n            lua_pushstring(lua, \"timeout during Lua debugging with client closing connection\");\n            lua_error(lua);\n        }\n        server.lua_time_start = mstime();\n    }\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sds.c",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include <limits.h>\n#include \"sds.h\"\n#include \"sdsalloc.h\"\n\nconst char *SDS_NOINIT = \"SDS_NOINIT\";\n\nstatic inline int sdsHdrSize(char type) {\n    switch(type&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return sizeof(struct sdshdr5);\n        case SDS_TYPE_8:\n            return sizeof(struct sdshdr8);\n        case SDS_TYPE_16:\n            return sizeof(struct sdshdr16);\n        case SDS_TYPE_32:\n            return sizeof(struct sdshdr32);\n        case SDS_TYPE_64:\n            return sizeof(struct sdshdr64);\n    }\n    return 0;\n}\n\nstatic inline char sdsReqType(size_t string_size) {\n    if (string_size < 1<<5)\n        return SDS_TYPE_5;\n    if (string_size < 1<<8)\n        return SDS_TYPE_8;\n    if (string_size < 1<<16)\n        return SDS_TYPE_16;\n#if (LONG_MAX == LLONG_MAX)\n    if (string_size < 1ll<<32)\n        return SDS_TYPE_32;\n    return SDS_TYPE_64;\n#else\n    return SDS_TYPE_32;\n#endif\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 * If SDS_NOINIT is used, the buffer is left uninitialized;\n *\n * The string is always null-termined (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3);\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    void *sh;\n    sds s;\n    char type = sdsReqType(initlen);\n    /* Empty strings are usually created in order to append. Use type 8\n     * since type 5 is not good at this. */\n    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;\n    int hdrlen = sdsHdrSize(type);\n    unsigned char *fp; /* flags pointer. */\n\n    sh = s_malloc(hdrlen+initlen+1);\n    if (sh == NULL) return NULL;\n    if (init==SDS_NOINIT)\n        init = NULL;\n    else if (!init)\n        memset(sh, 0, hdrlen+initlen+1);\n    s = (char*)sh+hdrlen;\n    fp = ((unsigned char*)s)-1;\n    switch(type) {\n        case SDS_TYPE_5: {\n            *fp = type | (initlen << SDS_TYPE_BITS);\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n    }\n    if (initlen && init)\n        memcpy(s, init, initlen);\n    s[initlen] = '\\0';\n    return s;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null terminated C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    s_free((char*)s-sdsHdrSize(s[-1]));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    size_t reallen = strlen(s);\n    sdssetlen(s, reallen);\n}\n\n/* Modify an sds string in-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    sdssetlen(s, 0);\n    s[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\nsds sdsMakeRoomFor(sds s, size_t addlen) {\n    void *sh, *newsh;\n    size_t avail = sdsavail(s);\n    size_t len, newlen;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n\n    /* Return ASAP if there is enough space left. */\n    if (avail >= addlen) return s;\n\n    len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n    newlen = (len+addlen);\n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n\n    type = sdsReqType(newlen);\n\n    /* Don't use type 5: the user is appending to the string and type 5 is\n     * not able to remember empty space, so sdsMakeRoomFor() must be called\n     * at every appending operation. */\n    if (type == SDS_TYPE_5) type = SDS_TYPE_8;\n\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        /* Since the header size changes, need to move the string forward,\n         * and can't use realloc */\n        newsh = s_malloc(hdrlen+newlen+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, newlen);\n    return s;\n}\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    void *sh, *newsh;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);\n    size_t len = sdslen(s);\n    size_t avail = sdsavail(s);\n    sh = (char*)s-oldhdrlen;\n\n    /* Return ASAP if there is no space left. */\n    if (avail == 0) return s;\n\n    /* Check what would be the minimum SDS header that is just good enough to\n     * fit this string. */\n    type = sdsReqType(len);\n    hdrlen = sdsHdrSize(type);\n\n    /* If the type is the same, or at least a large enough type is still\n     * required, we just realloc(), letting the allocator to do the copy\n     * only if really needed. Otherwise if the change is huge, we manually\n     * reallocate the string to use the different header type. */\n    if (oldtype==type || type > SDS_TYPE_8) {\n        newsh = s_realloc(sh, oldhdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+oldhdrlen;\n    } else {\n        newsh = s_malloc(hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, len);\n    return s;\n}\n\n/* Return the total size of the allocation of the specified sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    size_t alloc = sdsalloc(s);\n    return sdsHdrSize(s[-1])+alloc+1;\n}\n\n/* Return the pointer of the actual SDS allocation (normally SDS strings\n * are referenced by the start of the string buffer). */\nvoid *sdsAllocPtr(sds s) {\n    return (void*) (s-sdsHdrSize(s[-1]));\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, ssize_t incr) {\n    unsigned char flags = s[-1];\n    size_t len;\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            unsigned char *fp = ((unsigned char*)s)-1;\n            unsigned char oldlen = SDS_TYPE_5_LEN(flags);\n            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));\n            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);\n            len = oldlen+incr;\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        default: len = 0; /* Just to avoid compilation warnings. */\n    }\n    s[len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    size_t curlen = sdslen(s);\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    memcpy(s+curlen, t, len);\n    sdssetlen(s, curlen+len);\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    if (sdsalloc(s) < len) {\n        s = sdsMakeRoomFor(s,len-sdslen(s));\n        if (s == NULL) return NULL;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-termined string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the length of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[SDS_LLSTR_SIZE];\n    int len = sdsll2str(buf,value);\n\n    return sdsnewlen(buf,len);\n}\n\n/* Like sdscatprintf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char staticbuf[1024], *buf = staticbuf, *t;\n    size_t buflen = strlen(fmt)*2;\n\n    /* We try to start using a static buffer for speed.\n     * If not possible we revert to heap allocation. */\n    if (buflen > sizeof(staticbuf)) {\n        buf = s_malloc(buflen);\n        if (buf == NULL) return NULL;\n    } else {\n        buflen = sizeof(staticbuf);\n    }\n\n    /* Try with buffers two times bigger every time we fail to\n     * fit the string in the current buffer size. */\n    while(1) {\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        va_end(cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) s_free(buf);\n            buflen *= 2;\n            buf = s_malloc(buflen);\n            if (buf == NULL) return NULL;\n            continue;\n        }\n        break;\n    }\n\n    /* Finally concat the obtained string to the SDS string and return it. */\n    t = sdscat(s, buf);\n    if (buf != staticbuf) s_free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b).\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    size_t initlen = sdslen(s);\n    const char *f = fmt;\n    long i;\n    va_list ap;\n\n    /* To avoid continuous reallocations, let's start with a buffer that\n     * can hold at least two times the format string itself. It's not the\n     * best heuristic but seems to work in practice. */\n    s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);\n    va_start(ap,fmt);\n    f = fmt;    /* Next format specifier byte to process. */\n    i = initlen; /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        size_t l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sdsavail(s)==0) {\n            s = sdsMakeRoomFor(s,1);\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sdsavail(s) < l) {\n                    s = sdsMakeRoomFor(s,l);\n                }\n                memcpy(s+i,str,l);\n                sdsinclen(s,l);\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else\n                    unum = va_arg(ap,unsigned long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sdsinclen(s,1);\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sdsinclen(s,1);\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n}\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"Aa. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"HelloWorld\".\n */\nsds sdstrim(sds s, const char *cset) {\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > sp && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (s != sp) memmove(s, sp, len);\n    s[len] = '\\0';\n    sdssetlen(s,len);\n    return s;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nvoid sdsrange(sds s, ssize_t start, ssize_t end) {\n    size_t newlen, len = sdslen(s);\n\n    if (len == 0) return;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (ssize_t)len) {\n            newlen = 0;\n        } else if (end >= (ssize_t)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(s, s+start, newlen);\n    s[newlen] = 0;\n    sdssetlen(s,newlen);\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    size_t 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    size_t len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     positive if s1 > s2.\n *     negative if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0);\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, ssize_t len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5;\n    long start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = s_malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = s_realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        s_free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    s_free(tokens);\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that returns non zero if 'c'\n * is a valid hex digit. */\nint is_hex_digit(char c) {\n    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||\n           (c >= 'A' && c <= 'F');\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             is_hex_digit(*(p+2)) &&\n                                             is_hex_digit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            vector = s_realloc(vector,((*argc)+1)*sizeof(char*));\n            vector[*argc] = current;\n            (*argc)++;\n            current = NULL;\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = s_malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    s_free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscat(join,sep);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n/* Wrappers to the allocators used by SDS. Note that SDS will actually\n * just use the macros defined into sdsalloc.h in order to avoid to pay\n * the overhead of function calls. Here we define these wrappers only for\n * the programs SDS is linked to, if they want to touch the SDS internals\n * even if they use a different allocator. */\nvoid *sds_malloc(size_t size) { return s_malloc(size); }\nvoid *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }\nvoid sds_free(void *ptr) { s_free(ptr); }\n\n#if defined(SDS_TEST_MAIN)\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.h\"\n\n#define UNUSED(x) (void)(x)\nint sdsTest(void) {\n    {\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"Hello %s World %I,%I--\", \"Hi!\", LLONG_MIN,LLONG_MAX);\n        test_cond(\"sdscatfmt() seems working in the base case\",\n            sdslen(x) == 60 &&\n            memcmp(x,\"--Hello Hi! World -9223372036854775808,\"\n                     \"9223372036854775807--\",60) == 0)\n        printf(\"[%s]\\n\",x);\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"%u,%U--\", UINT_MAX, ULLONG_MAX);\n        test_cond(\"sdscatfmt() seems working with unsigned numbers\",\n            sdslen(x) == 35 &&\n            memcmp(x,\"--4294967295,18446744073709551615--\",35) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" x\");\n        test_cond(\"sdstrim() works when all chars match\",\n            sdslen(x) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" \");\n        test_cond(\"sdstrim() works when a single char remains\",\n            sdslen(x) == 1 && x[0] == 'x')\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            unsigned int oldfree;\n            char *p;\n            int step = 10, j, i;\n\n            sdsfree(x);\n            sdsfree(y);\n            x = sdsnew(\"0\");\n            test_cond(\"sdsnew() free/len buffers\", sdslen(x) == 1 && sdsavail(x) == 0);\n\n            /* Run the test a few times in order to hit the first two\n             * SDS header types. */\n            for (i = 0; i < 10; i++) {\n                int oldlen = sdslen(x);\n                x = sdsMakeRoomFor(x,step);\n                int type = x[-1]&SDS_TYPE_MASK;\n\n                test_cond(\"sdsMakeRoomFor() len\", sdslen(x) == oldlen);\n                if (type != SDS_TYPE_5) {\n                    test_cond(\"sdsMakeRoomFor() free\", sdsavail(x) >= step);\n                    oldfree = sdsavail(x);\n                }\n                p = x+oldlen;\n                for (j = 0; j < step; j++) {\n                    p[j] = 'A'+j;\n                }\n                sdsIncrLen(x,step);\n            }\n            test_cond(\"sdsMakeRoomFor() content\",\n                memcmp(\"0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ\",x,101) == 0);\n            test_cond(\"sdsMakeRoomFor() final length\",sdslen(x)==101);\n\n            sdsfree(x);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n\n#ifdef SDS_TEST_MAIN\nint main(void) {\n    return sdsTest();\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sds.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\nextern const char *SDS_NOINIT;\n\n#include <sys/types.h>\n#include <stdarg.h>\n#include <stdint.h>\n\ntypedef char *sds;\n\n/* Note: sdshdr5 is never used, we just access the flags byte directly.\n * However is here to document the layout of type 5 SDS strings. */\nstruct __attribute__ ((__packed__)) sdshdr5 {\n    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr8 {\n    uint8_t len; /* used */\n    uint8_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr16 {\n    uint16_t len; /* used */\n    uint16_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr32 {\n    uint32_t len; /* used */\n    uint32_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr64 {\n    uint64_t len; /* used */\n    uint64_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\n\n#define SDS_TYPE_5  0\n#define SDS_TYPE_8  1\n#define SDS_TYPE_16 2\n#define SDS_TYPE_32 3\n#define SDS_TYPE_64 4\n#define SDS_TYPE_MASK 7\n#define SDS_TYPE_BITS 3\n#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));\n#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))\n#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)\n\nstatic inline size_t sdslen(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->len;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->len;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->len;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->len;\n    }\n    return 0;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            return 0;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            return sh->alloc - sh->len;\n        }\n    }\n    return 0;\n}\n\nstatic inline void sdssetlen(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len = newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len = newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len = newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len = newlen;\n            break;\n    }\n}\n\nstatic inline void sdsinclen(sds s, size_t inc) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len += inc;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len += inc;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len += inc;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len += inc;\n            break;\n    }\n}\n\n/* sdsalloc() = sdsavail() + sdslen() */\nstatic inline size_t sdsalloc(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->alloc;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->alloc;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->alloc;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->alloc;\n    }\n    return 0;\n}\n\nstatic inline void sdssetalloc(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            /* Nothing to do, this type has no total allocation info. */\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->alloc = newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->alloc = newlen;\n            break;\n    }\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nsds sdstrim(sds s, const char *cset);\nvoid sdsrange(sds s, ssize_t start, ssize_t end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, ssize_t incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\nvoid *sdsAllocPtr(sds s);\n\n/* Export the allocator used by SDS to the program using SDS.\n * Sometimes the program SDS is linked to, may use a different set of\n * allocators, but may want to allocate or free things that SDS will\n * respectively free or allocate. */\nvoid *sds_malloc(size_t size);\nvoid *sds_realloc(void *ptr, size_t size);\nvoid sds_free(void *ptr);\n\n#ifdef REDIS_TEST\nint sdsTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sdsalloc.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Redis Labs, Inc\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* SDS allocator selection.\n *\n * This file is used in order to change the SDS allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#ifndef __SDS_ALLOC_H__\n#define __SDS_ALLOC_H__\n\n#include \"zmalloc.h\"\n#define s_malloc zmalloc\n#define s_realloc zrealloc\n#define s_free zfree\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"hiredis.h\"\n#ifdef USE_OPENSSL\n#include \"openssl/ssl.h\"\n#include \"hiredis_ssl.h\"\n#endif\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#ifdef USE_OPENSSL\nextern SSL_CTX *redis_tls_ctx;\n#endif\n\n#define REDIS_SENTINEL_PORT 26379\n\n/* ======================== Sentinel global state =========================== */\n\n/* Address object, used to describe an ip:port pair. */\ntypedef struct sentinelAddr {\n    char *ip;\n    int port;\n} sentinelAddr;\n\n/* A Sentinel Redis Instance object is monitoring. */\n#define SRI_MASTER  (1<<0)\n#define SRI_SLAVE   (1<<1)\n#define SRI_SENTINEL (1<<2)\n#define SRI_S_DOWN (1<<3)   /* Subjectively down (no quorum). */\n#define SRI_O_DOWN (1<<4)   /* Objectively down (confirmed by others). */\n#define SRI_MASTER_DOWN (1<<5) /* A Sentinel with this flag set thinks that\n                                   its master is down. */\n#define SRI_FAILOVER_IN_PROGRESS (1<<6) /* Failover is in progress for\n                                           this master. */\n#define SRI_PROMOTED (1<<7)            /* Slave selected for promotion. */\n#define SRI_RECONF_SENT (1<<8)     /* SLAVEOF <newmaster> sent. */\n#define SRI_RECONF_INPROG (1<<9)   /* Slave synchronization in progress. */\n#define SRI_RECONF_DONE (1<<10)     /* Slave synchronized with new master. */\n#define SRI_FORCE_FAILOVER (1<<11)  /* Force failover with master up. */\n#define SRI_SCRIPT_KILL_SENT (1<<12) /* SCRIPT KILL already sent on -BUSY */\n\n/* Note: times are in milliseconds. */\n#define SENTINEL_INFO_PERIOD 10000\n#define SENTINEL_PING_PERIOD 1000\n#define SENTINEL_ASK_PERIOD 1000\n#define SENTINEL_PUBLISH_PERIOD 2000\n#define SENTINEL_DEFAULT_DOWN_AFTER 30000\n#define SENTINEL_HELLO_CHANNEL \"__sentinel__:hello\"\n#define SENTINEL_TILT_TRIGGER 2000\n#define SENTINEL_TILT_PERIOD (SENTINEL_PING_PERIOD*30)\n#define SENTINEL_DEFAULT_SLAVE_PRIORITY 100\n#define SENTINEL_SLAVE_RECONF_TIMEOUT 10000\n#define SENTINEL_DEFAULT_PARALLEL_SYNCS 1\n#define SENTINEL_MIN_LINK_RECONNECT_PERIOD 15000\n#define SENTINEL_DEFAULT_FAILOVER_TIMEOUT (60*3*1000)\n#define SENTINEL_MAX_PENDING_COMMANDS 100\n#define SENTINEL_ELECTION_TIMEOUT 10000\n#define SENTINEL_MAX_DESYNC 1000\n#define SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG 1\n\n/* Failover machine different states. */\n#define SENTINEL_FAILOVER_STATE_NONE 0  /* No failover in progress. */\n#define SENTINEL_FAILOVER_STATE_WAIT_START 1  /* Wait for failover_start_time*/\n#define SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* Select slave to promote */\n#define SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* Slave -> Master */\n#define SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* Wait slave to change role */\n#define SENTINEL_FAILOVER_STATE_RECONF_SLAVES 5 /* SLAVEOF newmaster */\n#define SENTINEL_FAILOVER_STATE_UPDATE_CONFIG 6 /* Monitor promoted slave. */\n\n#define SENTINEL_MASTER_LINK_STATUS_UP 0\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#define SENTINEL_NO_FLAGS 0\n#define SENTINEL_GENERATE_EVENT (1<<16)\n#define SENTINEL_LEADER (1<<17)\n#define SENTINEL_OBSERVER (1<<18)\n\n/* Script execution flags and limits. */\n#define SENTINEL_SCRIPT_NONE 0\n#define SENTINEL_SCRIPT_RUNNING 1\n#define SENTINEL_SCRIPT_MAX_QUEUE 256\n#define SENTINEL_SCRIPT_MAX_RUNNING 16\n#define SENTINEL_SCRIPT_MAX_RUNTIME 60000 /* 60 seconds max exec time. */\n#define SENTINEL_SCRIPT_MAX_RETRY 10\n#define SENTINEL_SCRIPT_RETRY_DELAY 30000 /* 30 seconds between retries. */\n\n/* SENTINEL SIMULATE-FAILURE command flags. */\n#define SENTINEL_SIMFAILURE_NONE 0\n#define SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION (1<<0)\n#define SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION (1<<1)\n\n/* The link to a sentinelRedisInstance. When we have the same set of Sentinels\n * monitoring many masters, we have different instances representing the\n * same Sentinels, one per master, and we need to share the hiredis connections\n * among them. Oherwise if 5 Sentinels are monitoring 100 masters we create\n * 500 outgoing connections instead of 5.\n *\n * So this structure represents a reference counted link in terms of the two\n * hiredis connections for commands and Pub/Sub, and the fields needed for\n * failure detection, since the ping/pong time are now local to the link: if\n * the link is available, the instance is avaialbe. This way we don't just\n * have 5 connections instead of 500, we also send 5 pings instead of 500.\n *\n * Links are shared only for Sentinels: master and slave instances have\n * a link with refcount = 1, always. */\ntypedef struct instanceLink {\n    int refcount;          /* Number of sentinelRedisInstance owners. */\n    int disconnected;      /* Non-zero if we need to reconnect cc or pc. */\n    int pending_commands;  /* Number of commands sent waiting for a reply. */\n    redisAsyncContext *cc; /* Hiredis context for commands. */\n    redisAsyncContext *pc; /* Hiredis context for Pub / Sub. */\n    mstime_t cc_conn_time; /* cc connection time. */\n    mstime_t pc_conn_time; /* pc connection time. */\n    mstime_t pc_last_activity; /* Last time we received any message. */\n    mstime_t last_avail_time; /* Last time the instance replied to ping with\n                                 a reply we consider valid. */\n    mstime_t act_ping_time;   /* Time at which the last pending ping (no pong\n                                 received after it) was sent. This field is\n                                 set to 0 when a pong is received, and set again\n                                 to the current time if the value is 0 and a new\n                                 ping is sent. */\n    mstime_t last_ping_time;  /* Time at which we sent the last ping. This is\n                                 only used to avoid sending too many pings\n                                 during failure. Idle time is computed using\n                                 the act_ping_time field. */\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    mstime_t last_reconn_time;  /* Last reconnection attempt performed when\n                                   the link was down. */\n} instanceLink;\n\ntypedef struct sentinelRedisInstance {\n    int flags;      /* See SRI_... defines */\n    char *name;     /* Master name from the point of view of this sentinel. */\n    char *runid;    /* Run ID of this instance, or unique ID if is a Sentinel.*/\n    uint64_t config_epoch;  /* Configuration epoch. */\n    sentinelAddr *addr; /* Master host. */\n    instanceLink *link; /* Link to the instance, may be shared for Sentinels. */\n    mstime_t last_pub_time;   /* Last time we sent hello via Pub/Sub. */\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    mstime_t last_master_down_reply_time; /* Time of last reply to\n                                             SENTINEL is-master-down command. */\n    mstime_t s_down_since_time; /* Subjectively down since time. */\n    mstime_t o_down_since_time; /* Objectively down since time. */\n    mstime_t down_after_period; /* Consider it down after that period. */\n    mstime_t info_refresh;  /* Time at which we received INFO output from it. */\n    dict *renamed_commands;     /* Commands renamed in this instance:\n                                   Sentinel will use the alternative commands\n                                   mapped on this table to send things like\n                                   SLAVEOF, CONFING, INFO, ... */\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    int role_reported;\n    mstime_t role_reported_time;\n    mstime_t slave_conf_change_time; /* Last time slave master addr changed. */\n\n    /* Master specific. */\n    dict *sentinels;    /* Other sentinels monitoring the same master. */\n    dict *slaves;       /* Slaves for this master instance. */\n    unsigned int quorum;/* Number of sentinels that need to agree on failure. */\n    int parallel_syncs; /* How many slaves to reconfigure at same time. */\n    char *auth_pass;    /* Password to use for AUTH against master & replica. */\n    char *auth_user;    /* Username for ACLs AUTH against master & replica. */\n\n    /* Slave specific. */\n    mstime_t master_link_down_time; /* Slave replication link down time. */\n    int slave_priority; /* Slave priority according to its INFO output. */\n    mstime_t slave_reconf_sent_time; /* Time at which we sent SLAVE OF <new> */\n    struct sentinelRedisInstance *master; /* Master instance if it's slave. */\n    char *slave_master_host;    /* Master host as reported by INFO */\n    int slave_master_port;      /* Master port as reported by INFO */\n    int slave_master_link_status; /* Master link status as reported by INFO */\n    unsigned long long slave_repl_offset; /* Slave replication offset. */\n    /* Failover */\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    uint64_t leader_epoch; /* Epoch of the 'leader' field. */\n    uint64_t failover_epoch; /* Epoch of the currently started failover. */\n    int failover_state; /* See SENTINEL_FAILOVER_STATE_* defines. */\n    mstime_t failover_state_change_time;\n    mstime_t failover_start_time;   /* Last failover attempt start time. */\n    mstime_t failover_timeout;      /* Max time to refresh failover state. */\n    mstime_t failover_delay_logged; /* For what failover_start_time value we\n                                       logged the failover delay. */\n    struct sentinelRedisInstance *promoted_slave; /* Promoted slave instance. */\n    /* Scripts executed to notify admin or reconfigure clients: when they\n     * are set to NULL no script is executed. */\n    char *notification_script;\n    char *client_reconfig_script;\n    sds info; /* cached INFO output */\n} sentinelRedisInstance;\n\n/* Main state. */\nstruct sentinelState {\n    char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */\n    uint64_t current_epoch;         /* Current epoch. */\n    dict *masters;      /* Dictionary of master sentinelRedisInstances.\n                           Key is the instance name, value is the\n                           sentinelRedisInstance structure pointer. */\n    int tilt;           /* Are we in TILT mode? */\n    int running_scripts;    /* Number of scripts in execution right now. */\n    mstime_t tilt_start_time;       /* When TITL started. */\n    mstime_t previous_time;         /* Last time we ran the time handler. */\n    list *scripts_queue;            /* Queue of user scripts to execute. */\n    char *announce_ip;  /* IP addr that is gossiped to other sentinels if\n                           not NULL. */\n    int announce_port;  /* Port that is gossiped to other sentinels if\n                           non zero. */\n    unsigned long simfailure_flags; /* Failures simulation. */\n    int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script\n                                  paths at runtime? */\n} sentinel;\n\n/* A script execution job. */\ntypedef struct sentinelScriptJob {\n    int flags;              /* Script job flags: SENTINEL_SCRIPT_* */\n    int retry_num;          /* Number of times we tried to execute it. */\n    char **argv;            /* Arguments to call the script. */\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    pid_t pid;              /* Script execution pid. */\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\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    zfree(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 C_ERR;\n\n    /* Create container for context and r/w events */\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    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 C_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 instanceLinkConnectionError(const 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);\nint sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master);\nsentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, char *ip, int port, char *runid);\nvoid sentinelSimFailureCrash(void);\n\n/* ========================= Dictionary types =============================== */\n\nuint64_t dictSdsHash(const void *key);\nuint64_t dictSdsCaseHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\nint dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);\nvoid releaseSentinelRedisInstance(sentinelRedisInstance *ri);\n\nvoid dictInstancesValDestructor (void *privdata, void *obj) {\n    UNUSED(privdata);\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. */\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. */\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/* Instance renamed commands table. */\ndictType renamedCommandsDictType = {\n    dictSdsCaseHash,           /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCaseCompare,     /* key compare */\n    dictSdsDestructor,         /* key destructor */\n    dictSdsDestructor          /* val destructor */\n};\n\n/* =========================== Initialization =============================== */\n\nvoid sentinelCommand(client *c);\nvoid sentinelInfoCommand(client *c);\nvoid sentinelSetCommand(client *c);\nvoid sentinelPublishCommand(client *c);\nvoid sentinelRoleCommand(client *c);\n\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    {\"role\",sentinelRoleCommand,1,\"ok-loading\",0,NULL,0,0,0,0,0},\n    {\"client\",clientCommand,-2,\"read-only no-script\",0,NULL,0,0,0,0,0},\n    {\"shutdown\",shutdownCommand,-1,\"\",0,NULL,0,0,0,0,0},\n    {\"auth\",authCommand,2,\"no-auth no-script ok-loading ok-stale fast\",0,NULL,0,0,0,0,0},\n    {\"hello\",helloCommand,-2,\"no-auth no-script fast\",0,NULL,0,0,0,0,0}\n};\n\n/* This function overwrites a few normal Redis config default with Sentinel\n * specific defaults. */\nvoid initSentinelConfig(void) {\n    server.port = REDIS_SENTINEL_PORT;\n    server.protected_mode = 0; /* Sentinel must be exposed. */\n}\n\n/* Perform the Sentinel mode initialization. */\nvoid initSentinel(void) {\n    unsigned int j;\n\n    /* Remove usual Redis commands from the command table, then just add\n     * the SENTINEL command. */\n    dictEmpty(server.commands,NULL);\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        serverAssert(retval == DICT_OK);\n\n        /* Translate the command string flags description into an actual\n         * set of flags. */\n        if (populateCommandTableParseFlags(cmd,cmd->sflags) == C_ERR)\n            serverPanic(\"Unsupported command flag\");\n    }\n\n    /* Initialize various data structures. */\n    sentinel.current_epoch = 0;\n    sentinel.masters = dictCreate(&instancesDictType,NULL);\n    sentinel.tilt = 0;\n    sentinel.tilt_start_time = 0;\n    sentinel.previous_time = mstime();\n    sentinel.running_scripts = 0;\n    sentinel.scripts_queue = listCreate();\n    sentinel.announce_ip = NULL;\n    sentinel.announce_port = 0;\n    sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;\n    sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;\n    memset(sentinel.myid,0,sizeof(sentinel.myid));\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. */\nvoid sentinelIsRunning(void) {\n    int j;\n\n    if (server.configfile == NULL) {\n        serverLog(LL_WARNING,\n            \"Sentinel started without a config file. Exiting...\");\n        exit(1);\n    } else if (access(server.configfile,W_OK) == -1) {\n        serverLog(LL_WARNING,\n            \"Sentinel config file %s is not writable: %s. Exiting...\",\n            server.configfile,strerror(errno));\n        exit(1);\n    }\n\n    /* If this Sentinel has yet no ID set in the configuration file, we\n     * pick a random one and persist the config on disk. From now on this\n     * will be this Sentinel ID across restarts. */\n    for (j = 0; j < CONFIG_RUN_ID_SIZE; j++)\n        if (sentinel.myid[j] != 0) break;\n\n    if (j == CONFIG_RUN_ID_SIZE) {\n        /* Pick ID and persist the config. */\n        getRandomHexChars(sentinel.myid,CONFIG_RUN_ID_SIZE);\n        sentinelFlushConfig();\n    }\n\n    /* Log its ID to make debugging of issues simpler. */\n    serverLog(LL_WARNING,\"Sentinel ID is %s\", sentinel.myid);\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 * On error NULL is returned and errno is set to:\n *  ENOENT: Can't resolve the hostname.\n *  EINVAL: Invalid port number.\n */\nsentinelAddr *createSentinelAddr(char *hostname, int port) {\n    char ip[NET_IP_STR_LEN];\n    sentinelAddr *sa;\n\n    if (port < 0 || port > 65535) {\n        errno = EINVAL;\n        return NULL;\n    }\n    if (anetResolve(NULL,hostname,ip,sizeof(ip)) == ANET_ERR) {\n        errno = ENOENT;\n        return NULL;\n    }\n    sa = zmalloc(sizeof(*sa));\n    sa->ip = sdsnew(ip);\n    sa->port = port;\n    return sa;\n}\n\n/* Return a duplicate of the source address. */\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. */\nvoid releaseSentinelAddr(sentinelAddr *sa) {\n    sdsfree(sa->ip);\n    zfree(sa);\n}\n\n/* Return non-zero if two addresses are equal. */\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 * 'level' is the log level for logging. Only LL_WARNING events will trigger\n * the execution of the user notification script.\n *\n * 'type' is the message type, also used as a pub/sub channel name.\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 * The remaining arguments are printf-alike.\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 *  <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 *  @ <master name> <master ip> <master port>\n *\n *  Any other specifier after \"%@\" is processed by printf itself.\n */\nvoid sentinelEvent(int level, char *type, sentinelRedisInstance *ri,\n                   const char *fmt, ...) {\n    va_list ap;\n    char msg[LOG_MAX_LEN];\n    robj *channel, *payload;\n\n    /* Handle %@ */\n    if (fmt[0] == '%' && fmt[1] == '@') {\n        sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ?\n                                         NULL : ri->master;\n\n        if (master) {\n            snprintf(msg, sizeof(msg), \"%s %s %s %d @ %s %s %d\",\n                sentinelRedisInstanceTypeStr(ri),\n                ri->name, ri->addr->ip, ri->addr->port,\n                master->name, master->addr->ip, master->addr->port);\n        } else {\n            snprintf(msg, sizeof(msg), \"%s %s %s %d\",\n                sentinelRedisInstanceTypeStr(ri),\n                ri->name, ri->addr->ip, ri->addr->port);\n        }\n        fmt += 2;\n    } else {\n        msg[0] = '\\0';\n    }\n\n    /* Use vsprintf for the rest of the formatting if any. */\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    if (level >= server.verbosity)\n        serverLog(level,\"%s %s\",type,msg);\n\n    /* Publish the message via Pub/Sub if it's not a debugging one. */\n    if (level != LL_DEBUG) {\n        channel = createStringObject(type,strlen(type));\n        payload = createStringObject(msg,strlen(msg));\n        pubsubPublishMessage(channel,payload);\n        decrRefCount(channel);\n        decrRefCount(payload);\n    }\n\n    /* Call the notification script if applicable. */\n    if (level == LL_WARNING && ri != NULL) {\n        sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ?\n                                         ri : ri->master;\n        if (master && 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. */\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(LL_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. */\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#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    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    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    listAddNodeTail(sentinel.scripts_queue,sj);\n\n    /* Remove the oldest non running script if we already hit the limit. */\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            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        serverAssert(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). */\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. */\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    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        if (sj->flags & SENTINEL_SCRIPT_RUNNING) continue;\n\n        /* Skip if it's a retry, but not enough time has elapsed. */\n        if (sj->start_time && sj->start_time > now) continue;\n\n        sj->flags |= SENTINEL_SCRIPT_RUNNING;\n        sj->start_time = mstime();\n        sj->retry_num++;\n        pid = fork();\n\n        if (pid == -1) {\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(LL_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            /* 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            sentinel.running_scripts++;\n            sj->pid = pid;\n            sentinelEvent(LL_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. */\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. */\nvoid sentinelCollectTerminatedScripts(void) {\n    int statloc;\n    pid_t pid;\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        if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);\n        sentinelEvent(LL_DEBUG,\"-script-child\",NULL,\"%ld %d %d\",\n            (long)pid, exitcode, bysignal);\n\n        ln = sentinelGetScriptListNodeByPid(pid);\n        if (ln == NULL) {\n            serverLog(LL_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            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            if (bysignal || exitcode != 0) {\n                sentinelEvent(LL_WARNING,\"-script-error\",NULL,\n                              \"%s %d %d\", sj->argv[0], bysignal, exitcode);\n            }\n            listDelNode(sentinel.scripts_queue,ln);\n            sentinelReleaseScriptJob(sj);\n        }\n        sentinel.running_scripts--;\n    }\n}\n\n/* Kill scripts in timeout, they'll be collected by the\n * sentinelCollectTerminatedScripts() function. */\nvoid sentinelKillTimedoutScripts(void) {\n    listNode *ln;\n    listIter li;\n    mstime_t now = mstime();\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 &&\n            (now - sj->start_time) > SENTINEL_SCRIPT_MAX_RUNTIME)\n        {\n            sentinelEvent(LL_WARNING,\"-script-timeout\",NULL,\"%s %ld\",\n                sj->argv[0], (long)sj->pid);\n            kill(sj->pid,SIGKILL);\n        }\n    }\n}\n\n/* Implements SENTINEL PENDING-SCRIPTS command. */\nvoid sentinelPendingScriptsCommand(client *c) {\n    listNode *ln;\n    listIter li;\n\n    addReplyArrayLen(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        addReplyMapLen(c,5);\n\n        addReplyBulkCString(c,\"argv\");\n        while (sj->argv[j]) j++;\n        addReplyArrayLen(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 * <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 * <state> is currently always \"failover\".\n * <role> is either \"leader\" or \"observer\".\n *\n * from/to fields are respectively master -> promoted slave addresses for\n * \"start\" and \"end\". */\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    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/* =============================== instanceLink ============================= */\n\n/* Create a not yet connected link object. */\ninstanceLink *createInstanceLink(void) {\n    instanceLink *link = zmalloc(sizeof(*link));\n\n    link->refcount = 1;\n    link->disconnected = 1;\n    link->pending_commands = 0;\n    link->cc = NULL;\n    link->pc = NULL;\n    link->cc_conn_time = 0;\n    link->pc_conn_time = 0;\n    link->last_reconn_time = 0;\n    link->pc_last_activity = 0;\n    /* We set the act_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    link->act_ping_time = mstime();\n    link->last_ping_time = 0;\n    link->last_avail_time = mstime();\n    link->last_pong_time = mstime();\n    return link;\n}\n\n/* Disconnect an hiredis connection in the context of an instance link. */\nvoid instanceLinkCloseConnection(instanceLink *link, redisAsyncContext *c) {\n    if (c == NULL) return;\n\n    if (link->cc == c) {\n        link->cc = NULL;\n        link->pending_commands = 0;\n    }\n    if (link->pc == c) link->pc = NULL;\n    c->data = NULL;\n    link->disconnected = 1;\n    redisAsyncFree(c);\n}\n\n/* Decrement the refcount of a link object, if it drops to zero, actually\n * free it and return NULL. Otherwise don't do anything and return the pointer\n * to the object.\n *\n * If we are not going to free the link and ri is not NULL, we rebind all the\n * pending requests in link->cc (hiredis connection for commands) to a\n * callback that will just ignore them. This is useful to avoid processing\n * replies for an instance that no longer exists. */\ninstanceLink *releaseInstanceLink(instanceLink *link, sentinelRedisInstance *ri)\n{\n    serverAssert(link->refcount > 0);\n    link->refcount--;\n    if (link->refcount != 0) {\n        if (ri && ri->link->cc) {\n            /* This instance may have pending callbacks in the hiredis async\n             * context, having as 'privdata' the instance that we are going to\n             * free. Let's rewrite the callback list, directly exploiting\n             * hiredis internal data structures, in order to bind them with\n             * a callback that will ignore the reply at all. */\n            redisCallback *cb;\n            redisCallbackList *callbacks = &link->cc->replies;\n\n            cb = callbacks->head;\n            while(cb) {\n                if (cb->privdata == ri) {\n                    cb->fn = sentinelDiscardReplyCallback;\n                    cb->privdata = NULL; /* Not strictly needed. */\n                }\n                cb = cb->next;\n            }\n        }\n        return link; /* Other active users. */\n    }\n\n    instanceLinkCloseConnection(link,link->cc);\n    instanceLinkCloseConnection(link,link->pc);\n    zfree(link);\n    return NULL;\n}\n\n/* This function will attempt to share the instance link we already have\n * for the same Sentinel in the context of a different master, with the\n * instance we are passing as argument.\n *\n * This way multiple Sentinel objects that refer all to the same physical\n * Sentinel instance but in the context of different masters will use\n * a single connection, will send a single PING per second for failure\n * detection and so forth.\n *\n * Return C_OK if a matching Sentinel was found in the context of a\n * different master and sharing was performed. Otherwise C_ERR\n * is returned. */\nint sentinelTryConnectionSharing(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_SENTINEL);\n    dictIterator *di;\n    dictEntry *de;\n\n    if (ri->runid == NULL) return C_ERR; /* No way to identify it. */\n    if (ri->link->refcount > 1) return C_ERR; /* Already shared. */\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *master = dictGetVal(de), *match;\n        /* We want to share with the same physical Sentinel referenced\n         * in other masters, so skip our master. */\n        if (master == ri->master) continue;\n        match = getSentinelRedisInstanceByAddrAndRunID(master->sentinels,\n                                                       NULL,0,ri->runid);\n        if (match == NULL) continue; /* No match. */\n        if (match == ri) continue; /* Should never happen but... safer. */\n\n        /* We identified a matching Sentinel, great! Let's free our link\n         * and use the one of the matching Sentinel. */\n        releaseInstanceLink(ri->link,NULL);\n        ri->link = match->link;\n        match->link->refcount++;\n        return C_OK;\n    }\n    dictReleaseIterator(di);\n    return C_ERR;\n}\n\n/* When we detect a Sentinel to switch address (reporting a different IP/port\n * pair in Hello messages), let's update all the matching Sentinels in the\n * context of other masters as well and disconnect the links, so that everybody\n * will be updated.\n *\n * Return the number of updated Sentinel addresses. */\nint sentinelUpdateSentinelAddressInAllMasters(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_SENTINEL);\n    dictIterator *di;\n    dictEntry *de;\n    int reconfigured = 0;\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *master = dictGetVal(de), *match;\n        match = getSentinelRedisInstanceByAddrAndRunID(master->sentinels,\n                                                       NULL,0,ri->runid);\n        /* If there is no match, this master does not know about this\n         * Sentinel, try with the next one. */\n        if (match == NULL) continue;\n\n        /* Disconnect the old links if connected. */\n        if (match->link->cc != NULL)\n            instanceLinkCloseConnection(match->link,match->link->cc);\n        if (match->link->pc != NULL)\n            instanceLinkCloseConnection(match->link,match->link->pc);\n\n        if (match == ri) continue; /* Address already updated for it. */\n\n        /* Update the address of the matching Sentinel by copying the address\n         * of the Sentinel object that received the address update. */\n        releaseSentinelAddr(match->addr);\n        match->addr = dupSentinelAddr(ri->addr);\n        reconfigured++;\n    }\n    dictReleaseIterator(di);\n    if (reconfigured)\n        sentinelEvent(LL_NOTICE,\"+sentinel-address-update\", ri,\n                    \"%@ %d additional matching instances\", reconfigured);\n    return reconfigured;\n}\n\n/* This function is called when an hiredis connection reported an error.\n * We set it to NULL and mark the link as disconnected so that it will be\n * reconnected again.\n *\n * Note: we don't free the hiredis context as hiredis will do it for us\n * for async connections. */\nvoid instanceLinkConnectionError(const redisAsyncContext *c) {\n    instanceLink *link = c->data;\n    int pubsub;\n\n    if (!link) return;\n\n    pubsub = (link->pc == c);\n    if (pubsub)\n        link->pc = NULL;\n    else\n        link->cc = NULL;\n    link->disconnected = 1;\n}\n\n/* Hiredis connection established / disconnected callbacks. We need them\n * just to cleanup our link state. */\nvoid sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) {\n    if (status != C_OK) instanceLinkConnectionError(c);\n}\n\nvoid sentinelDisconnectCallback(const redisAsyncContext *c, int status) {\n    UNUSED(status);\n    instanceLinkConnectionError(c);\n}\n\n/* ========================== sentinelRedisInstance ========================= */\n\n/* Create a redis instance, the following fields must be populated by the\n * caller if needed:\n * runid: set to NULL but will be populated once INFO output is received.\n * info_refresh: is set to 0 to mean that we never received INFO so far.\n *\n * If SRI_MASTER is set into initial flags the instance is added to\n * sentinel.masters table.\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 * If the instance is a slave or sentinel, the name parameter is ignored and\n * is created automatically as 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 * The function may also fail and return NULL with errno set to EBUSY if\n * a master with the same name, a slave with the same address, or a sentinel\n * with the same ID already exists. */\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[NET_PEER_ID_LEN], *sdsname;\n\n    serverAssert(flags & (SRI_MASTER|SRI_SLAVE|SRI_SENTINEL));\n    serverAssert((flags & SRI_MASTER) || master != NULL);\n\n    /* Check address validity. */\n    addr = createSentinelAddr(hostname,port);\n    if (addr == NULL) return NULL;\n\n    /* For slaves use ip:port as name. */\n    if (flags & SRI_SLAVE) {\n        anetFormatAddr(slavename, sizeof(slavename), 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    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        releaseSentinelAddr(addr);\n        sdsfree(sdsname);\n        errno = EBUSY;\n        return NULL;\n    }\n\n    /* Create the instance object. */\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    ri->flags = flags;\n    ri->name = sdsname;\n    ri->runid = NULL;\n    ri->config_epoch = 0;\n    ri->addr = addr;\n    ri->link = createInstanceLink();\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->auth_user = 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    ri->renamed_commands = dictCreate(&renamedCommandsDictType,NULL);\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    ri->info = 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    dictAdd(table, ri->name, ri);\n    return ri;\n}\n\n/* Release this instance and all its slaves, sentinels, hiredis connections.\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. */\nvoid releaseSentinelRedisInstance(sentinelRedisInstance *ri) {\n    /* Release all its slaves or sentinels if any. */\n    dictRelease(ri->sentinels);\n    dictRelease(ri->slaves);\n\n    /* Disconnect the instance. */\n    releaseInstanceLink(ri->link,ri);\n\n    /* Free other resources. */\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    sdsfree(ri->auth_user);\n    sdsfree(ri->info);\n    releaseSentinelAddr(ri->addr);\n    dictRelease(ri->renamed_commands);\n\n    /* Clear state into the master if needed. */\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. */\nsentinelRedisInstance *sentinelRedisInstanceLookupSlave(\n                sentinelRedisInstance *ri, char *ip, int port)\n{\n    sds key;\n    sentinelRedisInstance *slave;\n    char buf[NET_PEER_ID_LEN];\n\n    serverAssert(ri->flags & SRI_MASTER);\n    anetFormatAddr(buf,sizeof(buf),ip,port);\n    key = sdsnew(buf);\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. */\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 remove the Sentinel with the specified ID from the\n * specified master.\n *\n * If \"runid\" is NULL the function returns ASAP.\n *\n * This function is useful because on Sentinels address switch, we want to\n * remove our old entry and add a new one for the same ID but with the new\n * address.\n *\n * The function returns 1 if the matching Sentinel was removed, otherwise\n * 0 if there was no Sentinel with this ID. */\nint removeMatchingSentinelFromMaster(sentinelRedisInstance *master, char *runid) {\n    dictIterator *di;\n    dictEntry *de;\n    int removed = 0;\n\n    if (runid == NULL) return 0;\n\n    di = dictGetSafeIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (ri->runid && strcmp(ri->runid,runid) == 0) {\n            dictDelete(master->sentinels,ri->name);\n            removed++;\n        }\n    }\n    dictReleaseIterator(di);\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 or ip can be NULL. In such a case the search is performed only\n * by the non-NULL field. */\nsentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, char *ip, int port, char *runid) {\n    dictIterator *di;\n    dictEntry *de;\n    sentinelRedisInstance *instance = NULL;\n\n    serverAssert(ip || runid);   /* User must pass at least one search param. */\n    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (runid && !ri->runid) continue;\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    return instance;\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. */\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. */\nvoid sentinelDelFlagsToDictOfRedisInstances(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/* Reset the state of a monitored master:\n * 1) Remove all slaves.\n * 2) Remove all sentinels.\n * 3) Remove most of the flags resulting from runtime operations.\n * 4) Reset timers to their default value. For example after a reset it will be\n *    possible to failover again the same master ASAP, without waiting the\n *    failover timeout delay.\n * 5) In the process of doing this undo the failover if in progress.\n * 6) Disconnect the connections with the master (will reconnect automatically).\n */\n\n#define SENTINEL_RESET_NO_SENTINELS (1<<0)\nvoid sentinelResetMaster(sentinelRedisInstance *ri, int flags) {\n    serverAssert(ri->flags & SRI_MASTER);\n    dictRelease(ri->slaves);\n    ri->slaves = dictCreate(&instancesDictType,NULL);\n    if (!(flags & SENTINEL_RESET_NO_SENTINELS)) {\n        dictRelease(ri->sentinels);\n        ri->sentinels = dictCreate(&instancesDictType,NULL);\n    }\n    instanceLinkCloseConnection(ri->link,ri->link->cc);\n    instanceLinkCloseConnection(ri->link,ri->link->pc);\n    ri->flags &= SRI_MASTER;\n    if (ri->leader) {\n        sdsfree(ri->leader);\n        ri->leader = NULL;\n    }\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = 0;\n    ri->failover_start_time = 0; /* We can failover again ASAP. */\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->link->act_ping_time = mstime();\n    ri->link->last_ping_time = 0;\n    ri->link->last_avail_time = mstime();\n    ri->link->last_pong_time = mstime();\n    ri->role_reported_time = mstime();\n    ri->role_reported = SRI_MASTER;\n    if (flags & SENTINEL_GENERATE_EVENT)\n        sentinelEvent(LL_WARNING,\"+reset-master\",ri,\"%@\");\n}\n\n/* Call sentinelResetMaster() on every master with a name matching the specified\n * pattern. */\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 * This is used to handle the +switch-master event.\n *\n * The function returns C_ERR if the address can't be resolved for some\n * reason. Otherwise C_OK is returned.  */\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    newaddr = createSentinelAddr(ip,port);\n    if (newaddr == NULL) return C_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    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        if (sentinelAddrIsEqual(slave->addr,newaddr)) continue;\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    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    sentinelResetMaster(master,SENTINEL_RESET_NO_SENTINELS);\n    oldaddr = master->addr;\n    master->addr = newaddr;\n    master->o_down_since_time = 0;\n    master->s_down_since_time = 0;\n\n    /* Add slaves back. */\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        releaseSentinelAddr(slaves[j]);\n        if (slave) sentinelEvent(LL_NOTICE,\"+slave\",slave,\"%@\");\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    releaseSentinelAddr(oldaddr);\n    sentinelFlushConfig();\n    return C_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. */\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. */\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\nchar *sentinelGetInstanceTypeString(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 is used in order to send commands to Redis instances: the\n * commands we send from Sentinel may be renamed, a common case is a master\n * with CONFIG and SLAVEOF commands renamed for security concerns. In that\n * case we check the ri->renamed_command table (or if the instance is a slave,\n * we check the one of the master), and map the command that we should send\n * to the set of renamed commads. However, if the command was not renamed,\n * we just return \"command\" itself. */\nchar *sentinelInstanceMapCommand(sentinelRedisInstance *ri, char *command) {\n    sds sc = sdsnew(command);\n    if (ri->master) ri = ri->master;\n    char *retval = dictFetchValue(ri->renamed_commands, sc);\n    sdsfree(sc);\n    return retval ? retval : command;\n}\n\n/* ============================ Config handling ============================= */\nchar *sentinelHandleConfiguration(char **argv, int argc) {\n    sentinelRedisInstance *ri;\n\n    if (!strcasecmp(argv[0],\"monitor\") && argc == 5) {\n        /* monitor <name> <host> <port> <quorum> */\n        int quorum = atoi(argv[4]);\n\n        if (quorum <= 0) return \"Quorum must be 1 or greater.\";\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    } else if (!strcasecmp(argv[0],\"down-after-milliseconds\") && argc == 3) {\n        /* down-after-milliseconds <name> <milliseconds> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->down_after_period = atoi(argv[2]);\n        if (ri->down_after_period <= 0)\n            return \"negative or zero time parameter.\";\n        sentinelPropagateDownAfterPeriod(ri);\n    } else if (!strcasecmp(argv[0],\"failover-timeout\") && argc == 3) {\n        /* failover-timeout <name> <milliseconds> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->failover_timeout = atoi(argv[2]);\n        if (ri->failover_timeout <= 0)\n            return \"negative or zero time parameter.\";\n    } else if (!strcasecmp(argv[0],\"parallel-syncs\") && argc == 3) {\n        /* parallel-syncs <name> <milliseconds> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->parallel_syncs = atoi(argv[2]);\n    } else if (!strcasecmp(argv[0],\"notification-script\") && argc == 3) {\n        /* notification-script <name> <path> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        if (access(argv[2],X_OK) == -1)\n            return \"Notification script seems non existing or non executable.\";\n        ri->notification_script = sdsnew(argv[2]);\n    } else if (!strcasecmp(argv[0],\"client-reconfig-script\") && argc == 3) {\n        /* client-reconfig-script <name> <path> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        if (access(argv[2],X_OK) == -1)\n            return \"Client reconfiguration script seems non existing or \"\n                   \"non executable.\";\n        ri->client_reconfig_script = sdsnew(argv[2]);\n    } else if (!strcasecmp(argv[0],\"auth-pass\") && argc == 3) {\n        /* auth-pass <name> <password> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->auth_pass = sdsnew(argv[2]);\n    } else if (!strcasecmp(argv[0],\"auth-user\") && argc == 3) {\n        /* auth-user <name> <username> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        ri->auth_user = sdsnew(argv[2]);\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    } else if (!strcasecmp(argv[0],\"myid\") && argc == 2) {\n        if (strlen(argv[1]) != CONFIG_RUN_ID_SIZE)\n            return \"Malformed Sentinel id in myid option.\";\n        memcpy(sentinel.myid,argv[1],CONFIG_RUN_ID_SIZE);\n    } else if (!strcasecmp(argv[0],\"config-epoch\") && argc == 3) {\n        /* config-epoch <name> <epoch> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\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    } 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    } else if ((!strcasecmp(argv[0],\"known-slave\") ||\n                !strcasecmp(argv[0],\"known-replica\")) && argc == 4)\n    {\n        sentinelRedisInstance *slave;\n\n        /* known-replica <name> <ip> <port> */\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 replica.\";\n        }\n    } else if (!strcasecmp(argv[0],\"known-sentinel\") &&\n               (argc == 4 || argc == 5)) {\n        sentinelRedisInstance *si;\n\n        if (argc == 5) { /* Ignore the old form without runid. */\n            /* known-sentinel <name> <ip> <port> [runid] */\n            ri = sentinelGetMasterByName(argv[1]);\n            if (!ri) return \"No such master with specified name.\";\n            if ((si = createSentinelRedisInstance(argv[4],SRI_SENTINEL,argv[2],\n                        atoi(argv[3]), ri->quorum, ri)) == NULL)\n            {\n                return \"Wrong hostname or port for sentinel.\";\n            }\n            si->runid = sdsnew(argv[4]);\n            sentinelTryConnectionSharing(si);\n        }\n    } else if (!strcasecmp(argv[0],\"rename-command\") && argc == 4) {\n        /* rename-command <name> <command> <renamed-command> */\n        ri = sentinelGetMasterByName(argv[1]);\n        if (!ri) return \"No such master with specified name.\";\n        sds oldcmd = sdsnew(argv[2]);\n        sds newcmd = sdsnew(argv[3]);\n        if (dictAdd(ri->renamed_commands,oldcmd,newcmd) != DICT_OK) {\n            sdsfree(oldcmd);\n            sdsfree(newcmd);\n            return \"Same command renamed multiple times with rename-command.\";\n        }\n    } else if (!strcasecmp(argv[0],\"announce-ip\") && argc == 2) {\n        /* announce-ip <ip-address> */\n        if (strlen(argv[1]))\n            sentinel.announce_ip = sdsnew(argv[1]);\n    } else if (!strcasecmp(argv[0],\"announce-port\") && argc == 2) {\n        /* announce-port <port> */\n        sentinel.announce_port = atoi(argv[1]);\n    } else if (!strcasecmp(argv[0],\"deny-scripts-reconfig\") && argc == 2) {\n        /* deny-scripts-reconfig <yes|no> */\n        if ((sentinel.deny_scripts_reconfig = yesnotoi(argv[1])) == -1) {\n            return \"Please specify yes or no for the \"\n                   \"deny-scripts-reconfig options.\";\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. */\nvoid rewriteConfigSentinelOption(struct rewriteConfigState *state) {\n    dictIterator *di, *di2;\n    dictEntry *de;\n    sds line;\n\n    /* sentinel unique ID. */\n    line = sdscatprintf(sdsempty(), \"sentinel myid %s\", sentinel.myid);\n    rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n\n    /* sentinel deny-scripts-reconfig. */\n    line = sdscatprintf(sdsempty(), \"sentinel deny-scripts-reconfig %s\",\n        sentinel.deny_scripts_reconfig ? \"yes\" : \"no\");\n    rewriteConfigRewriteLine(state,\"sentinel\",line,\n        sentinel.deny_scripts_reconfig != SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG);\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 & auth-user */\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        if (master->auth_user) {\n            line = sdscatprintf(sdsempty(),\n                \"sentinel auth-user %s %s\",\n                master->name, master->auth_user);\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-replica %s %s %d\",\n                master->name, slave_addr->ip, slave_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            if (ri->runid == NULL) continue;\n            line = sdscatprintf(sdsempty(),\n                \"sentinel known-sentinel %s %s %d %s\",\n                master->name, ri->addr->ip, ri->addr->port, ri->runid);\n            rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n        }\n        dictReleaseIterator(di2);\n\n        /* sentinel rename-command */\n        di2 = dictGetIterator(master->renamed_commands);\n        while((de = dictNext(di2)) != NULL) {\n            sds oldname = dictGetKey(de);\n            sds newname = dictGetVal(de);\n            line = sdscatprintf(sdsempty(),\n                \"sentinel rename-command %s %s %s\",\n                master->name, oldname, newname);\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    /* sentinel announce-ip. */\n    if (sentinel.announce_ip) {\n        line = sdsnew(\"sentinel announce-ip \");\n        line = sdscatrepr(line, sentinel.announce_ip, sdslen(sentinel.announce_ip));\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n    }\n\n    /* sentinel announce-port. */\n    if (sentinel.announce_port) {\n        line = sdscatprintf(sdsempty(),\"sentinel announce-port %d\",\n                            sentinel.announce_port);\n        rewriteConfigRewriteLine(state,\"sentinel\",line,1);\n    }\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 * Before returning the function calls fsync() against the generated\n * configuration file to make sure changes are committed to disk.\n *\n * On failure the function logs a warning on the Redis log. */\nvoid sentinelFlushConfig(void) {\n    int fd = -1;\n    int saved_hz = server.hz;\n    int rewrite_status;\n\n    server.hz = CONFIG_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    serverLog(LL_WARNING,\"WARNING: Sentinel was not able to save the new configuration on disk!!!: %s\", strerror(errno));\n}\n\n/* ====================== hiredis connection handling ======================= */\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 * In case this Sentinel requires a password as well, via the \"requirepass\"\n * configuration directive, we assume we should use the local password in\n * order to authenticate when connecting with the other Sentinels as well.\n * So basically all the Sentinels share the same password and use it to\n * authenticate reciprocally.\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. */\nvoid sentinelSendAuthIfNeeded(sentinelRedisInstance *ri, redisAsyncContext *c) {\n    char *auth_pass = NULL;\n    char *auth_user = NULL;\n\n    if (ri->flags & SRI_MASTER) {\n        auth_pass = ri->auth_pass;\n        auth_user = ri->auth_user;\n    } else if (ri->flags & SRI_SLAVE) {\n        auth_pass = ri->master->auth_pass;\n        auth_user = ri->master->auth_user;\n    } else if (ri->flags & SRI_SENTINEL) {\n        auth_pass = server.requirepass;\n        auth_user = NULL;\n    }\n\n    if (auth_pass && auth_user == NULL) {\n        if (redisAsyncCommand(c, sentinelDiscardReplyCallback, ri, \"%s %s\",\n            sentinelInstanceMapCommand(ri,\"AUTH\"),\n            auth_pass) == C_OK) ri->link->pending_commands++;\n    } else if (auth_pass && auth_user) {\n        /* If we also have an username, use the ACL-style AUTH command\n         * with two arguments, username and password. */\n        if (redisAsyncCommand(c, sentinelDiscardReplyCallback, ri, \"%s %s %s\",\n            sentinelInstanceMapCommand(ri,\"AUTH\"),\n            auth_user, auth_pass) == C_OK) ri->link->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. */\nvoid sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char *type) {\n    char name[64];\n\n    snprintf(name,sizeof(name),\"sentinel-%.8s-%s\",sentinel.myid,type);\n    if (redisAsyncCommand(c, sentinelDiscardReplyCallback, ri,\n        \"%s SETNAME %s\",\n        sentinelInstanceMapCommand(ri,\"CLIENT\"),\n        name) == C_OK)\n    {\n        ri->link->pending_commands++;\n    }\n}\n\nstatic int instanceLinkNegotiateTLS(redisAsyncContext *context) {\n#ifndef USE_OPENSSL\n    (void) context;\n#else\n    if (!redis_tls_ctx) return C_ERR;\n    SSL *ssl = SSL_new(redis_tls_ctx);\n    if (!ssl) return C_ERR;\n\n    if (redisInitiateSSL(&context->c, ssl) == REDIS_ERR) return C_ERR;\n#endif\n    return C_OK;\n}\n\n/* Create the async connections for the instance link if the link\n * is disconnected. Note that link->disconnected is true even if just\n * one of the two links (commands and pub/sub) is missing. */\nvoid sentinelReconnectInstance(sentinelRedisInstance *ri) {\n    if (ri->link->disconnected == 0) return;\n    if (ri->addr->port == 0) return; /* port == 0 means invalid address. */\n    instanceLink *link = ri->link;\n    mstime_t now = mstime();\n\n    if (now - ri->link->last_reconn_time < SENTINEL_PING_PERIOD) return;\n    ri->link->last_reconn_time = now;\n\n    /* Commands connection. */\n    if (link->cc == NULL) {\n        link->cc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,NET_FIRST_BIND_ADDR);\n        if (!link->cc->err && server.tls_replication &&\n                (instanceLinkNegotiateTLS(link->cc) == C_ERR)) {\n            sentinelEvent(LL_DEBUG,\"-cmd-link-reconnection\",ri,\"%@ #Failed to initialize TLS\");\n            instanceLinkCloseConnection(link,link->cc);\n        } else if (link->cc->err) {\n            sentinelEvent(LL_DEBUG,\"-cmd-link-reconnection\",ri,\"%@ #%s\",\n                link->cc->errstr);\n            instanceLinkCloseConnection(link,link->cc);\n        } else {\n            link->pending_commands = 0;\n            link->cc_conn_time = mstime();\n            link->cc->data = link;\n            redisAeAttach(server.el,link->cc);\n            redisAsyncSetConnectCallback(link->cc,\n                    sentinelLinkEstablishedCallback);\n            redisAsyncSetDisconnectCallback(link->cc,\n                    sentinelDisconnectCallback);\n            sentinelSendAuthIfNeeded(ri,link->cc);\n            sentinelSetClientName(ri,link->cc,\"cmd\");\n\n            /* Send a PING ASAP when reconnecting. */\n            sentinelSendPing(ri);\n        }\n    }\n    /* Pub / Sub */\n    if ((ri->flags & (SRI_MASTER|SRI_SLAVE)) && link->pc == NULL) {\n        link->pc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,NET_FIRST_BIND_ADDR);\n        if (!link->pc->err && server.tls_replication &&\n                (instanceLinkNegotiateTLS(link->pc) == C_ERR)) {\n            sentinelEvent(LL_DEBUG,\"-pubsub-link-reconnection\",ri,\"%@ #Failed to initialize TLS\");\n        } else if (link->pc->err) {\n            sentinelEvent(LL_DEBUG,\"-pubsub-link-reconnection\",ri,\"%@ #%s\",\n                link->pc->errstr);\n            instanceLinkCloseConnection(link,link->pc);\n        } else {\n            int retval;\n\n            link->pc_conn_time = mstime();\n            link->pc->data = link;\n            redisAeAttach(server.el,link->pc);\n            redisAsyncSetConnectCallback(link->pc,\n                    sentinelLinkEstablishedCallback);\n            redisAsyncSetDisconnectCallback(link->pc,\n                    sentinelDisconnectCallback);\n            sentinelSendAuthIfNeeded(ri,link->pc);\n            sentinelSetClientName(ri,link->pc,\"pubsub\");\n            /* Now we subscribe to the Sentinels \"Hello\" channel. */\n            retval = redisAsyncCommand(link->pc,\n                sentinelReceiveHelloMessages, ri, \"%s %s\",\n                sentinelInstanceMapCommand(ri,\"SUBSCRIBE\"),\n                SENTINEL_HELLO_CHANNEL);\n            if (retval != C_OK) {\n                /* If we can't subscribe, the Pub/Sub connection is useless\n                 * and we can simply disconnect it and try again. */\n                instanceLinkCloseConnection(link,link->pc);\n                return;\n            }\n        }\n    }\n    /* Clear the disconnected status only if we have both the connections\n     * (or just the commands connection if this is a sentinel instance). */\n    if (link->cc && (ri->flags & SRI_SENTINEL || link->pc))\n        link->disconnected = 0;\n}\n\n/* ======================== Redis instances pinging  ======================== */\n\n/* Return true if master looks \"sane\", that is:\n * 1) It is actually a master in the current configuration.\n * 2) It reports itself as a master.\n * 3) It is not SDOWN or ODOWN.\n * 4) We obtained last INFO no more than two times the INFO period time ago. */\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. */\nvoid sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {\n    sds *lines;\n    int numlines, j;\n    int role = 0;\n\n    /* cache full INFO output for instance */\n    sdsfree(ri->info);\n    ri->info = sdsnew(info);\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    ri->master_link_down_time = 0;\n\n    /* Process line by line. */\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        if (sdslen(l) >= 47 && !memcmp(l,\"run_id:\",7)) {\n            if (ri->runid == NULL) {\n                ri->runid = sdsnewlen(l+7,40);\n            } else {\n                if (strncmp(ri->runid,l+7,40) != 0) {\n                    sentinelEvent(LL_NOTICE,\"+reboot\",ri,\"%@\");\n                    sdsfree(ri->runid);\n                    ri->runid = sdsnewlen(l+7,40);\n                }\n            }\n        }\n\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            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(LL_NOTICE,\"+slave\",slave,\"%@\");\n                    sentinelFlushConfig();\n                }\n            }\n        }\n\n        /* master_link_down_since_seconds:<seconds> */\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        if (!memcmp(l,\"role:master\",11)) role = SRI_MASTER;\n        else if (!memcmp(l,\"role:slave\",10)) role = SRI_SLAVE;\n\n        if (role == SRI_SLAVE) {\n            /* master_host:<host> */\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            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            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            if (sdslen(l) >= 15 && !memcmp(l,\"slave_priority:\",15))\n                ri->slave_priority = atoi(l+15);\n\n            /* slave_repl_offset:<offset> */\n            if (sdslen(l) >= 18 && !memcmp(l,\"slave_repl_offset:\",18))\n                ri->slave_repl_offset = strtoull(l+18,NULL,10);\n        }\n    }\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    /* 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(LL_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    if (sentinel.tilt) return;\n\n    /* Handle master -> slave role switch. */\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    }\n\n    /* Handle slave -> master role switch. */\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        if ((ri->flags & SRI_PROMOTED) &&\n            (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            ri->master->config_epoch = ri->master->failover_epoch;\n            ri->master->failover_state = SENTINEL_FAILOVER_STATE_RECONF_SLAVES;\n            ri->master->failover_state_change_time = mstime();\n            sentinelFlushConfig();\n            sentinelEvent(LL_WARNING,\"+promoted-slave\",ri,\"%@\");\n            if (sentinel.simfailure_flags &\n                SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION)\n                sentinelSimFailureCrash();\n            sentinelEvent(LL_WARNING,\"+failover-state-reconf-slaves\",\n                ri->master,\"%@\");\n            sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER,\n                \"start\",ri->master->addr,ri->addr);\n            sentinelForceHelloUpdateForMaster(ri->master);\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            mstime_t wait_time = SENTINEL_PUBLISH_PERIOD*4;\n\n            if (!(ri->flags & SRI_PROMOTED) &&\n                 sentinelMasterLooksSane(ri->master) &&\n                 sentinelRedisInstanceNoDownFor(ri,wait_time) &&\n                 mstime() - ri->role_reported_time > wait_time)\n            {\n                int retval = sentinelSendSlaveOf(ri,\n                        ri->master->addr->ip,\n                        ri->master->addr->port);\n                if (retval == C_OK)\n                    sentinelEvent(LL_NOTICE,\"+convert-to-slave\",ri,\"%@\");\n            }\n        }\n    }\n\n    /* Handle slaves replicating to a different master address. */\n    if ((ri->flags & SRI_SLAVE) &&\n        role == SRI_SLAVE &&\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        if (sentinelMasterLooksSane(ri->master) &&\n            sentinelRedisInstanceNoDownFor(ri,wait_time) &&\n            mstime() - ri->slave_conf_change_time > wait_time)\n        {\n            int retval = sentinelSendSlaveOf(ri,\n                    ri->master->addr->ip,\n                    ri->master->addr->port);\n            if (retval == C_OK)\n                sentinelEvent(LL_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    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        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(LL_NOTICE,\"+slave-reconf-inprog\",ri,\"%@\");\n        }\n\n        /* SRI_RECONF_INPROG -> SRI_RECONF_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(LL_NOTICE,\"+slave-reconf-done\",ri,\"%@\");\n        }\n    }\n}\n\nvoid sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    if (r->type == REDIS_REPLY_STRING)\n        sentinelRefreshInstanceInfo(ri,r->str);\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. */\nvoid sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    instanceLink *link = c->data;\n    UNUSED(reply);\n    UNUSED(privdata);\n\n    if (link) link->pending_commands--;\n}\n\nvoid sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    if (r->type == REDIS_REPLY_STATUS ||\n        r->type == REDIS_REPLY_ERROR) {\n        /* Update the \"instance available\" field only if this is an\n         * acceptable reply. */\n        if (strncmp(r->str,\"PONG\",4) == 0 ||\n            strncmp(r->str,\"LOADING\",7) == 0 ||\n            strncmp(r->str,\"MASTERDOWN\",10) == 0)\n        {\n            link->last_avail_time = mstime();\n            link->act_ping_time = 0; /* Flag the pong as received. */\n        } else {\n            /* Send a SCRIPT KILL command if the instance appears to be\n             * down because of a busy script. */\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->link->cc,\n                        sentinelDiscardReplyCallback, ri,\n                        \"%s KILL\",\n                        sentinelInstanceMapCommand(ri,\"SCRIPT\")) == C_OK)\n                {\n                    ri->link->pending_commands++;\n                }\n                ri->flags |= SRI_SCRIPT_KILL_SENT;\n            }\n        }\n    }\n    link->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. */\nvoid sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    /* Only update pub_time if we actually published our message. Otherwise\n     * we'll retry again in 100 milliseconds. */\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 * If the master name specified in the message is not known, the message is\n * discarded. */\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        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        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            /* If not, remove all the sentinels that have the same runid\n             * because there was an address change, and add the same Sentinel\n             * with the new address back. */\n            removed = removeMatchingSentinelFromMaster(master,token[2]);\n            if (removed) {\n                sentinelEvent(LL_NOTICE,\"+sentinel-address-switch\",master,\n                    \"%@ ip %s port %d for %s\", token[0],port,token[2]);\n            } else {\n                /* Check if there is another Sentinel with the same address this\n                 * new one is reporting. What we do if this happens is to set its\n                 * port to 0, to signal the address is invalid. We'll update it\n                 * later if we get an HELLO message. */\n                sentinelRedisInstance *other =\n                    getSentinelRedisInstanceByAddrAndRunID(\n                        master->sentinels, token[0],port,NULL);\n                if (other) {\n                    sentinelEvent(LL_NOTICE,\"+sentinel-invalid-addr\",other,\"%@\");\n                    other->addr->port = 0; /* It means: invalid address. */\n                    sentinelUpdateSentinelAddressInAllMasters(other);\n                }\n            }\n\n            /* Add the new sentinel. */\n            si = createSentinelRedisInstance(token[2],SRI_SENTINEL,\n                            token[0],port,master->quorum,master);\n\n            if (si) {\n                if (!removed) sentinelEvent(LL_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                sentinelTryConnectionSharing(si);\n                if (removed) sentinelUpdateSentinelAddressInAllMasters(si);\n                sentinelFlushConfig();\n            }\n        }\n\n        /* Update local current_epoch if received current_epoch is greater.*/\n        if (current_epoch > sentinel.current_epoch) {\n            sentinel.current_epoch = current_epoch;\n            sentinelFlushConfig();\n            sentinelEvent(LL_WARNING,\"+new-epoch\",master,\"%llu\",\n                (unsigned long long) sentinel.current_epoch);\n        }\n\n        /* Update master info if received configuration is newer. */\n        if (si && 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(LL_WARNING,\"+config-update-from\",si,\"%@\");\n                sentinelEvent(LL_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        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. */\nvoid sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    redisReply *r;\n    UNUSED(c);\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    ri->link->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    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    if (strstr(r->element[2]->str,sentinel.myid) != 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 configuration for this\n * master, and to advertise the existence of this Sentinel at the same time.\n *\n * The message has the following format:\n *\n * sentinel_ip,sentinel_port,sentinel_runid,current_epoch,\n * master_name,master_ip,master_port,master_config_epoch.\n *\n * Returns C_OK if the PUBLISH was queued correctly, otherwise\n * C_ERR is returned. */\nint sentinelSendHello(sentinelRedisInstance *ri) {\n    char ip[NET_IP_STR_LEN];\n    char payload[NET_IP_STR_LEN+1024];\n    int retval;\n    char *announce_ip;\n    int announce_port;\n    sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master;\n    sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master);\n\n    if (ri->link->disconnected) return C_ERR;\n\n    /* Use the specified announce address if specified, otherwise try to\n     * obtain our own IP address. */\n    if (sentinel.announce_ip) {\n        announce_ip = sentinel.announce_ip;\n    } else {\n        if (anetSockName(ri->link->cc->c.fd,ip,sizeof(ip),NULL) == -1)\n            return C_ERR;\n        announce_ip = ip;\n    }\n    if (sentinel.announce_port) announce_port = sentinel.announce_port;\n    else if (server.tls_replication && server.tls_port) announce_port = server.tls_port;\n    else announce_port = server.port;\n\n    /* Format and send the Hello message. */\n    snprintf(payload,sizeof(payload),\n        \"%s,%d,%s,%llu,\" /* Info about this sentinel. */\n        \"%s,%s,%d,%llu\", /* Info about current master. */\n        announce_ip, announce_port, sentinel.myid,\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    retval = redisAsyncCommand(ri->link->cc,\n        sentinelPublishReplyCallback, ri, \"%s %s %s\",\n        sentinelInstanceMapCommand(ri,\"PUBLISH\"),\n        SENTINEL_HELLO_CHANNEL,payload);\n    if (retval != C_OK) return C_ERR;\n    ri->link->pending_commands++;\n    return C_OK;\n}\n\n/* Reset last_pub_time in all the instances in the specified dictionary\n * in order to force the delivery of an Hello update ASAP. */\nvoid sentinelForceHelloUpdateDictOfRedisInstances(dict *instances) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetSafeIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        if (ri->last_pub_time >= (SENTINEL_PUBLISH_PERIOD+1))\n            ri->last_pub_time -= (SENTINEL_PUBLISH_PERIOD+1);\n    }\n    dictReleaseIterator(di);\n}\n\n/* This function forces the delivery of an \"Hello\" message (see\n * sentinelSendHello() top comment for further information) to all the Redis\n * and Sentinel instances related to the specified 'master'.\n *\n * It is technically not needed since we send an update to every instance\n * with a period of SENTINEL_PUBLISH_PERIOD milliseconds, however when a\n * Sentinel upgrades a configuration it is a good idea to deliever an update\n * to the other Sentinels ASAP. */\nint sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master) {\n    if (!(master->flags & SRI_MASTER)) return C_ERR;\n    if (master->last_pub_time >= (SENTINEL_PUBLISH_PERIOD+1))\n        master->last_pub_time -= (SENTINEL_PUBLISH_PERIOD+1);\n    sentinelForceHelloUpdateDictOfRedisInstances(master->sentinels);\n    sentinelForceHelloUpdateDictOfRedisInstances(master->slaves);\n    return C_OK;\n}\n\n/* Send a PING to the specified instance and refresh the act_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. */\nint sentinelSendPing(sentinelRedisInstance *ri) {\n    int retval = redisAsyncCommand(ri->link->cc,\n        sentinelPingReplyCallback, ri, \"%s\",\n        sentinelInstanceMapCommand(ri,\"PING\"));\n    if (retval == C_OK) {\n        ri->link->pending_commands++;\n        ri->link->last_ping_time = mstime();\n        /* We update the active ping time only if we received the pong for\n         * the previous ping, otherwise we are technically waiting since the\n         * first ping that did not receive a reply. */\n        if (ri->link->act_ping_time == 0)\n            ri->link->act_ping_time = ri->link->last_ping_time;\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\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    if (ri->link->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    if (ri->link->pending_commands >=\n        SENTINEL_MAX_PENDING_COMMANDS * ri->link->refcount) 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     *\n     * Similarly we monitor the INFO output more often if the slave reports\n     * to be disconnected from the master, so that we can have a fresh\n     * disconnection time figure. */\n    if ((ri->flags & SRI_SLAVE) &&\n        ((ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS)) ||\n         (ri->master_link_down_time != 0)))\n    {\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    /* Send INFO to masters and slaves, not sentinels. */\n    if ((ri->flags & SRI_SENTINEL) == 0 &&\n        (ri->info_refresh == 0 ||\n        (now - ri->info_refresh) > info_period))\n    {\n        retval = redisAsyncCommand(ri->link->cc,\n            sentinelInfoReplyCallback, ri, \"%s\",\n            sentinelInstanceMapCommand(ri,\"INFO\"));\n        if (retval == C_OK) ri->link->pending_commands++;\n    }\n\n    /* Send PING to all the three kinds of instances. */\n    if ((now - ri->link->last_pong_time) > ping_period &&\n               (now - ri->link->last_ping_time) > ping_period/2) {\n        sentinelSendPing(ri);\n    }\n\n    /* PUBLISH hello messages to all the three kinds of instances. */\n    if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) {\n        sentinelSendHello(ri);\n    }\n}\n\n/* =========================== SENTINEL command ============================= */\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. */\nvoid addReplySentinelRedisInstance(client *c, sentinelRedisInstance *ri) {\n    char *flags = sdsempty();\n    void *mbl;\n    int fields = 0;\n\n    mbl = addReplyDeferredLen(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->link->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,\"link-pending-commands\");\n    addReplyBulkLongLong(c,ri->link->pending_commands);\n    fields++;\n\n    addReplyBulkCString(c,\"link-refcount\");\n    addReplyBulkLongLong(c,ri->link->refcount);\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->link->act_ping_time ? (mstime() - ri->link->act_ping_time) : 0);\n    fields++;\n\n    addReplyBulkCString(c,\"last-ok-ping-reply\");\n    addReplyBulkLongLong(c,mstime() - ri->link->last_avail_time);\n    fields++;\n\n    addReplyBulkCString(c,\"last-ping-reply\");\n    addReplyBulkLongLong(c,mstime() - ri->link->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    setDeferredMapLen(c,mbl,fields);\n}\n\n/* Output a number of instances contained inside a dictionary as\n * Redis protocol. */\nvoid addReplyDictOfRedisInstances(client *c, dict *instances) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(instances);\n    addReplyArrayLen(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. */\nsentinelRedisInstance *sentinelGetMasterByNameOrReplyError(client *c,\n                        robj *name)\n{\n    sentinelRedisInstance *ri;\n\n    ri = dictFetchValue(sentinel.masters,name->ptr);\n    if (!ri) {\n        addReplyError(c,\"No such master with that name\");\n        return NULL;\n    }\n    return ri;\n}\n\n#define SENTINEL_ISQR_OK 0\n#define SENTINEL_ISQR_NOQUORUM (1<<0)\n#define SENTINEL_ISQR_NOAUTH (1<<1)\nint sentinelIsQuorumReachable(sentinelRedisInstance *master, int *usableptr) {\n    dictIterator *di;\n    dictEntry *de;\n    int usable = 1; /* Number of usable Sentinels. Init to 1 to count myself. */\n    int result = SENTINEL_ISQR_OK;\n    int voters = dictSize(master->sentinels)+1; /* Known Sentinels + myself. */\n\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        if (ri->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;\n        usable++;\n    }\n    dictReleaseIterator(di);\n\n    if (usable < (int)master->quorum) result |= SENTINEL_ISQR_NOQUORUM;\n    if (usable < voters/2+1) result |= SENTINEL_ISQR_NOAUTH;\n    if (usableptr) *usableptr = usable;\n    return result;\n}\n\nvoid sentinelCommand(client *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               !strcasecmp(c->argv[1]->ptr,\"replicas\"))\n    {\n        /* SENTINEL REPLICAS <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         *\n         * Arguments:\n         *\n         * ip and port are the ip and port of the master we want to be\n         * checked by Sentinel. Note that the command will not check by\n         * name but just by master, in theory different Sentinels may monitor\n         * differnet masters with the same name.\n         *\n         * current-epoch is needed in order to understand if we are allowed\n         * to vote for a failover leader or not. Each Sentinel can vote just\n         * one time per epoch.\n         *\n         * runid is \"*\" if we are not seeking for a vote from the Sentinel\n         * in order to elect the failover leader. Otherwise it is set to the\n         * runid we want the Sentinel to vote if it did not already voted.\n         */\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) != C_OK ||\n            getLongLongFromObjectOrReply(c,c->argv[4],&req_epoch,NULL)\n                                                              != C_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        addReplyArrayLen(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            addReplyNullArray(c);\n        } else {\n            sentinelAddr *addr = sentinelGetCurrentMasterAddress(ri);\n\n            addReplyArrayLen(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 replica to promote\\r\\n\"));\n            return;\n        }\n        serverLog(LL_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 ip[NET_IP_STR_LEN];\n\n        if (c->argc != 6) goto numargserr;\n        if (getLongFromObjectOrReply(c,c->argv[5],&quorum,\"Invalid quorum\")\n            != C_OK) return;\n        if (getLongFromObjectOrReply(c,c->argv[4],&port,\"Invalid port\")\n            != C_OK) return;\n\n        if (quorum <= 0) {\n            addReplyError(c, \"Quorum must be 1 or greater.\");\n            return;\n        }\n\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,ip,sizeof(ip)) == 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(LL_WARNING,\"+monitor\",ri,\"%@ quorum %d\",ri->quorum);\n            addReply(c,shared.ok);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"flushconfig\")) {\n        if (c->argc != 2) goto numargserr;\n        sentinelFlushConfig();\n        addReply(c,shared.ok);\n        return;\n    } else if (!strcasecmp(c->argv[1]->ptr,\"remove\")) {\n        /* SENTINEL REMOVE <name> */\n        sentinelRedisInstance *ri;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n            == NULL) return;\n        sentinelEvent(LL_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,\"ckquorum\")) {\n        /* SENTINEL CKQUORUM <name> */\n        sentinelRedisInstance *ri;\n        int usable;\n\n        if (c->argc != 3) goto numargserr;\n        if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))\n            == NULL) return;\n        int result = sentinelIsQuorumReachable(ri,&usable);\n        if (result == SENTINEL_ISQR_OK) {\n            addReplySds(c, sdscatfmt(sdsempty(),\n                \"+OK %i usable Sentinels. Quorum and failover authorization \"\n                \"can be reached\\r\\n\",usable));\n        } else {\n            sds e = sdscatfmt(sdsempty(),\n                \"-NOQUORUM %i usable Sentinels. \",usable);\n            if (result & SENTINEL_ISQR_NOQUORUM)\n                e = sdscat(e,\"Not enough available Sentinels to reach the\"\n                             \" specified quorum for this master\");\n            if (result & SENTINEL_ISQR_NOAUTH) {\n                if (result & SENTINEL_ISQR_NOQUORUM) e = sdscat(e,\". \");\n                e = sdscat(e, \"Not enough available Sentinels to reach the\"\n                              \" majority and authorize a failover\");\n            }\n            e = sdscat(e,\"\\r\\n\");\n            addReplySds(c,e);\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr,\"set\")) {\n        if (c->argc < 3) goto numargserr;\n        sentinelSetCommand(c);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"info-cache\")) {\n        /* SENTINEL INFO-CACHE <name> */\n        if (c->argc < 2) goto numargserr;\n        mstime_t now = mstime();\n\n        /* Create an ad-hoc dictionary type so that we can iterate\n         * a dictionary composed of just the master groups the user\n         * requested. */\n        dictType copy_keeper = instancesDictType;\n        copy_keeper.valDestructor = NULL;\n        dict *masters_local = sentinel.masters;\n        if (c->argc > 2) {\n            masters_local = dictCreate(&copy_keeper, NULL);\n\n            for (int i = 2; i < c->argc; i++) {\n                sentinelRedisInstance *ri;\n                ri = sentinelGetMasterByName(c->argv[i]->ptr);\n                if (!ri) continue; /* ignore non-existing names */\n                dictAdd(masters_local, ri->name, ri);\n            }\n        }\n\n        /* Reply format:\n         *   1.) master name\n         *   2.) 1.) info from master\n         *       2.) info from replica\n         *       ...\n         *   3.) other master name\n         *   ...\n         */\n        addReplyArrayLen(c,dictSize(masters_local) * 2);\n\n        dictIterator  *di;\n        dictEntry *de;\n        di = dictGetIterator(masters_local);\n        while ((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n            addReplyBulkCBuffer(c,ri->name,strlen(ri->name));\n            addReplyArrayLen(c,dictSize(ri->slaves) + 1); /* +1 for self */\n            addReplyArrayLen(c,2);\n            addReplyLongLong(c, now - ri->info_refresh);\n            if (ri->info)\n                addReplyBulkCBuffer(c,ri->info,sdslen(ri->info));\n            else\n                addReplyNull(c);\n\n            dictIterator *sdi;\n            dictEntry *sde;\n            sdi = dictGetIterator(ri->slaves);\n            while ((sde = dictNext(sdi)) != NULL) {\n                sentinelRedisInstance *sri = dictGetVal(sde);\n                addReplyArrayLen(c,2);\n                addReplyLongLong(c, now - sri->info_refresh);\n                if (sri->info)\n                    addReplyBulkCBuffer(c,sri->info,sdslen(sri->info));\n                else\n                    addReplyNull(c);\n            }\n            dictReleaseIterator(sdi);\n        }\n        dictReleaseIterator(di);\n        if (masters_local != sentinel.masters) dictRelease(masters_local);\n    } else if (!strcasecmp(c->argv[1]->ptr,\"simulate-failure\")) {\n        /* SENTINEL SIMULATE-FAILURE <flag> <flag> ... <flag> */\n        int j;\n\n        sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;\n        for (j = 2; j < c->argc; j++) {\n            if (!strcasecmp(c->argv[j]->ptr,\"crash-after-election\")) {\n                sentinel.simfailure_flags |=\n                    SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION;\n                serverLog(LL_WARNING,\"Failure simulation: this Sentinel \"\n                    \"will crash after being successfully elected as failover \"\n                    \"leader\");\n            } else if (!strcasecmp(c->argv[j]->ptr,\"crash-after-promotion\")) {\n                sentinel.simfailure_flags |=\n                    SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION;\n                serverLog(LL_WARNING,\"Failure simulation: this Sentinel \"\n                    \"will crash after promoting the selected replica to master\");\n            } else if (!strcasecmp(c->argv[j]->ptr,\"help\")) {\n                addReplyArrayLen(c,2);\n                addReplyBulkCString(c,\"crash-after-election\");\n                addReplyBulkCString(c,\"crash-after-promotion\");\n            } else {\n                addReplyError(c,\"Unknown failure simulation specified\");\n                return;\n            }\n        }\n        addReply(c,shared.ok);\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#define info_section_from_redis(section_name) do { \\\n    if (defsections || allsections || !strcasecmp(section,section_name)) { \\\n        sds redissection; \\\n        if (sections++) info = sdscat(info,\"\\r\\n\"); \\\n        redissection = genRedisInfoString(section_name); \\\n        info = sdscatlen(info,redissection,sdslen(redissection)); \\\n        sdsfree(redissection); \\\n    } \\\n} while(0)\n\n/* SENTINEL INFO [section] */\nvoid sentinelInfoCommand(client *c) {\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    int defsections = 0, allsections = 0;\n    char *section = c->argc == 2 ? c->argv[1]->ptr : NULL;\n    if (section) {\n        allsections = !strcasecmp(section,\"all\");\n        defsections = !strcasecmp(section,\"default\");\n    } else {\n        defsections = 1;\n    }\n\n    int sections = 0;\n    sds info = sdsempty();\n\n    info_section_from_redis(\"server\");\n    info_section_from_redis(\"clients\");\n    info_section_from_redis(\"cpu\");\n    info_section_from_redis(\"stats\");\n\n    if (defsections || allsections || !strcasecmp(section,\"sentinel\")) {\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            \"sentinel_simulate_failure_flags:%lu\\r\\n\",\n            dictSize(sentinel.masters),\n            sentinel.tilt,\n            sentinel.running_scripts,\n            listLength(sentinel.scripts_queue),\n            sentinel.simfailure_flags);\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    addReplyBulkSds(c, info);\n}\n\n/* Implements Sentinel version of the ROLE command. The output is\n * \"sentinel\" and the list of currently monitored master names. */\nvoid sentinelRoleCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    addReplyArrayLen(c,2);\n    addReplyBulkCBuffer(c,\"sentinel\",8);\n    addReplyArrayLen(c,dictSize(sentinel.masters));\n\n    di = dictGetIterator(sentinel.masters);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        addReplyBulkCString(c,ri->name);\n    }\n    dictReleaseIterator(di);\n}\n\n/* SENTINEL SET <mastername> [<option> <value> ...] */\nvoid sentinelSetCommand(client *c) {\n    sentinelRedisInstance *ri;\n    int j, changes = 0;\n    int badarg = 0; /* Bad argument position for error reporting. */\n    char *option;\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++) {\n        int moreargs = (c->argc-1) - j;\n        option = c->argv[j]->ptr;\n        long long ll;\n        int old_j = j; /* Used to know what to log as an event. */\n\n        if (!strcasecmp(option,\"down-after-milliseconds\") && moreargs > 0) {\n            /* down-after-millisecodns <milliseconds> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->down_after_period = ll;\n            sentinelPropagateDownAfterPeriod(ri);\n            changes++;\n        } else if (!strcasecmp(option,\"failover-timeout\") && moreargs > 0) {\n            /* failover-timeout <milliseconds> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->failover_timeout = ll;\n            changes++;\n        } else if (!strcasecmp(option,\"parallel-syncs\") && moreargs > 0) {\n            /* parallel-syncs <milliseconds> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->parallel_syncs = ll;\n            changes++;\n        } else if (!strcasecmp(option,\"notification-script\") && moreargs > 0) {\n            /* notification-script <path> */\n            char *value = c->argv[++j]->ptr;\n            if (sentinel.deny_scripts_reconfig) {\n                addReplyError(c,\n                    \"Reconfiguration of scripts path is denied for \"\n                    \"security reasons. Check the deny-scripts-reconfig \"\n                    \"configuration directive in your Sentinel configuration\");\n                return;\n            }\n\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\") && moreargs > 0) {\n            /* client-reconfig-script <path> */\n            char *value = c->argv[++j]->ptr;\n            if (sentinel.deny_scripts_reconfig) {\n                addReplyError(c,\n                    \"Reconfiguration of scripts path is denied for \"\n                    \"security reasons. Check the deny-scripts-reconfig \"\n                    \"configuration directive in your Sentinel configuration\");\n                return;\n            }\n\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\") && moreargs > 0) {\n            /* auth-pass <password> */\n            char *value = c->argv[++j]->ptr;\n            sdsfree(ri->auth_pass);\n            ri->auth_pass = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n        } else if (!strcasecmp(option,\"auth-user\") && moreargs > 0) {\n            /* auth-user <username> */\n            char *value = c->argv[++j]->ptr;\n            sdsfree(ri->auth_user);\n            ri->auth_user = strlen(value) ? sdsnew(value) : NULL;\n            changes++;\n        } else if (!strcasecmp(option,\"quorum\") && moreargs > 0) {\n            /* quorum <count> */\n            robj *o = c->argv[++j];\n            if (getLongLongFromObject(o,&ll) == C_ERR || ll <= 0) {\n                badarg = j;\n                goto badfmt;\n            }\n            ri->quorum = ll;\n            changes++;\n        } else if (!strcasecmp(option,\"rename-command\") && moreargs > 1) {\n            /* rename-command <oldname> <newname> */\n            sds oldname = c->argv[++j]->ptr;\n            sds newname = c->argv[++j]->ptr;\n\n            if ((sdslen(oldname) == 0) || (sdslen(newname) == 0)) {\n                badarg = sdslen(newname) ? j-1 : j;\n                goto badfmt;\n            }\n\n            /* Remove any older renaming for this command. */\n            dictDelete(ri->renamed_commands,oldname);\n\n            /* If the target name is the same as the source name there\n             * is no need to add an entry mapping to itself. */\n            if (!dictSdsKeyCaseCompare(NULL,oldname,newname)) {\n                oldname = sdsdup(oldname);\n                newname = sdsdup(newname);\n                dictAdd(ri->renamed_commands,oldname,newname);\n            }\n            changes++;\n        } else {\n            addReplyErrorFormat(c,\"Unknown option or number of arguments for \"\n                                  \"SENTINEL SET '%s'\", option);\n            if (changes) sentinelFlushConfig();\n            return;\n        }\n\n        /* Log the event. */\n        int numargs = j-old_j+1;\n        switch(numargs) {\n        case 2:\n            sentinelEvent(LL_WARNING,\"+set\",ri,\"%@ %s %s\",c->argv[old_j]->ptr,\n                                                          c->argv[old_j+1]->ptr);\n            break;\n        case 3:\n            sentinelEvent(LL_WARNING,\"+set\",ri,\"%@ %s %s %s\",c->argv[old_j]->ptr,\n                                                             c->argv[old_j+1]->ptr,\n                                                             c->argv[old_j+2]->ptr);\n            break;\n        default:\n            sentinelEvent(LL_WARNING,\"+set\",ri,\"%@ %s\",c->argv[old_j]->ptr);\n            break;\n        }\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        (char*)c->argv[badarg]->ptr,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(client *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? */\nvoid sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {\n    mstime_t elapsed = 0;\n\n    if (ri->link->act_ping_time)\n        elapsed = mstime() - ri->link->act_ping_time;\n    else if (ri->link->disconnected)\n        elapsed = mstime() - ri->link->last_avail_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     * 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    if (ri->link->cc &&\n        (mstime() - ri->link->cc_conn_time) >\n        SENTINEL_MIN_LINK_RECONNECT_PERIOD &&\n        ri->link->act_ping_time != 0 && /* There is a pending ping... */\n        /* The pending ping is delayed, and we did not receive\n         * error replies as well. */\n        (mstime() - ri->link->act_ping_time) > (ri->down_after_period/2) &&\n        (mstime() - ri->link->last_pong_time) > (ri->down_after_period/2))\n    {\n        instanceLinkCloseConnection(ri->link,ri->link->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    if (ri->link->pc &&\n        (mstime() - ri->link->pc_conn_time) >\n         SENTINEL_MIN_LINK_RECONNECT_PERIOD &&\n        (mstime() - ri->link->pc_last_activity) > (SENTINEL_PUBLISH_PERIOD*3))\n    {\n        instanceLinkCloseConnection(ri->link,ri->link->pc);\n    }\n\n    /* Update the SDOWN flag. We believe the instance is SDOWN if:\n     *\n     * 1) It is not replying.\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    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            sentinelEvent(LL_WARNING,\"+sdown\",ri,\"%@\");\n            ri->s_down_since_time = mstime();\n            ri->flags |= SRI_S_DOWN;\n        }\n    } else {\n        /* Is subjectively up */\n        if (ri->flags & SRI_S_DOWN) {\n            sentinelEvent(LL_WARNING,\"-sdown\",ri,\"%@\");\n            ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT);\n        }\n    }\n}\n\n/* Is this instance down according to the configured quorum?\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 * However messages can be delayed so there are no strong guarantees about\n * N instances agreeing at the same time about the down state. */\nvoid sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    unsigned int quorum = 0, odown = 0;\n\n    if (master->flags & SRI_S_DOWN) {\n        /* Is down for enough sentinels? */\n        quorum = 1; /* the current sentinel. */\n        /* Count all the other sentinels. */\n        di = dictGetIterator(master->sentinels);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *ri = dictGetVal(de);\n\n            if (ri->flags & SRI_MASTER_DOWN) quorum++;\n        }\n        dictReleaseIterator(di);\n        if (quorum >= master->quorum) odown = 1;\n    }\n\n    /* Set the flag accordingly to the outcome. */\n    if (odown) {\n        if ((master->flags & SRI_O_DOWN) == 0) {\n            sentinelEvent(LL_WARNING,\"+odown\",master,\"%@ #quorum %d/%d\",\n                quorum, master->quorum);\n            master->flags |= SRI_O_DOWN;\n            master->o_down_since_time = mstime();\n        }\n    } else {\n        if (master->flags & SRI_O_DOWN) {\n            sentinelEvent(LL_WARNING,\"-odown\",master,\"%@\");\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. */\nvoid sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) {\n    sentinelRedisInstance *ri = privdata;\n    instanceLink *link = c->data;\n    redisReply *r;\n\n    if (!reply || !link) return;\n    link->pending_commands--;\n    r = reply;\n\n    /* Ignore every error or unexpected reply.\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        ri->last_master_down_reply_time = mstime();\n        if (r->element[0]->integer == 1) {\n            ri->flags |= SRI_MASTER_DOWN;\n        } else {\n            ri->flags &= ~SRI_MASTER_DOWN;\n        }\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            if ((long long)ri->leader_epoch != r->element[2]->integer)\n                serverLog(LL_WARNING,\n                    \"%s voted for %s %llu\", ri->name,\n                    r->element[1]->str,\n                    (unsigned long long) r->element[2]->integer);\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#define SENTINEL_ASK_FORCED (1<<0)\nvoid sentinelAskMasterStateToOtherSentinels(sentinelRedisInstance *master, int flags) {\n    dictIterator *di;\n    dictEntry *de;\n\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        mstime_t elapsed = mstime() - ri->last_master_down_reply_time;\n        char port[32];\n        int retval;\n\n        /* If the master state from other sentinel is too old, we clear it. */\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         * 1) We believe it is down, or there is a failover in progress.\n         * 2) Sentinel is connected.\n         * 3) We did not receive the info within SENTINEL_ASK_PERIOD ms. */\n        if ((master->flags & SRI_S_DOWN) == 0) continue;\n        if (ri->link->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        ll2string(port,sizeof(port),master->addr->port);\n        retval = redisAsyncCommand(ri->link->cc,\n                    sentinelReceiveIsMasterDownReply, ri,\n                    \"%s is-master-down-by-addr %s %s %llu %s\",\n                    sentinelInstanceMapCommand(ri,\"SENTINEL\"),\n                    master->addr->ip, port,\n                    sentinel.current_epoch,\n                    (master->failover_state > SENTINEL_FAILOVER_STATE_NONE) ?\n                    sentinel.myid : \"*\");\n        if (retval == C_OK) ri->link->pending_commands++;\n    }\n    dictReleaseIterator(di);\n}\n\n/* =============================== FAILOVER ================================= */\n\n/* Crash because of user request via SENTINEL simulate-failure command. */\nvoid sentinelSimFailureCrash(void) {\n    serverLog(LL_WARNING,\n        \"Sentinel CRASH because of SENTINEL simulate-failure\");\n    exit(99);\n}\n\n/* Vote for the sentinel with 'req_runid' or return the old vote if already\n * voted for the specified 'req_epoch' or one greater.\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. */\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(LL_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(LL_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,sentinel.myid))\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\nstruct sentinelLeader {\n    char *runid;\n    unsigned long votes;\n};\n\n/* Helper function for sentinelGetLeader, increment the counter\n * relative to the specified runid. */\nint sentinelLeaderIncr(dict *counters, char *runid) {\n    dictEntry *existing, *de;\n    uint64_t oldval;\n\n    de = dictAddRaw(counters,runid,&existing);\n    if (existing) {\n        oldval = dictGetUnsignedIntegerVal(existing);\n        dictSetUnsignedIntegerVal(existing,oldval+1);\n        return oldval+1;\n    } else {\n        serverAssert(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 * To be a leader for a given epoch, we should have the majority of\n * the Sentinels we know (ever seen since the last SENTINEL RESET) that\n * reported the same instance as leader for the same 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    serverAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS));\n    counters = dictCreate(&leaderVotesDictType,NULL);\n\n    voters = dictSize(master->sentinels)+1; /* All the other sentinels and me.*/\n\n    /* Count other sentinels votes */\n    di = dictGetIterator(master->sentinels);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n        if (ri->leader != NULL && ri->leader_epoch == sentinel.current_epoch)\n            sentinelLeaderIncr(counters,ri->leader);\n    }\n    dictReleaseIterator(di);\n\n    /* Check what's the winner. For the winner to win, it needs two conditions:\n     * 1) Absolute majority between voters (50% + 1).\n     * 2) And anyway at least master->quorum votes. */\n    di = dictGetIterator(counters);\n    while((de = dictNext(di)) != NULL) {\n        uint64_t votes = dictGetUnsignedIntegerVal(de);\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    if (winner)\n        myvote = sentinelVoteLeader(master,epoch,winner,&leader_epoch);\n    else\n        myvote = sentinelVoteLeader(master,epoch,sentinel.myid,&leader_epoch);\n\n    if (myvote && leader_epoch == epoch) {\n        uint64_t votes = sentinelLeaderIncr(counters,myvote);\n\n        if (votes > max_votes) {\n            max_votes = votes;\n            winner = myvote;\n        }\n    }\n\n    voters_quorum = voters/2+1;\n    if (winner && (max_votes < voters_quorum || max_votes < master->quorum))\n        winner = NULL;\n\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 * If Host is NULL the function sends \"SLAVEOF NO ONE\".\n *\n * The command returns C_OK if the SLAVEOF command was accepted for\n * (later) delivery otherwise C_ERR. The command replies are just\n * discarded. */\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 is NULL we send SLAVEOF NO ONE that will turn the instance\n     * into a master. */\n    if (host == NULL) {\n        host = \"NO\";\n        memcpy(portstr,\"ONE\",4);\n    }\n\n    /* In order to send SLAVEOF in a safe way, we send a transaction performing\n     * the following tasks:\n     * 1) Reconfigure the instance according to the specified host/port params.\n     * 2) Rewrite the configuration.\n     * 3) Disconnect all clients (but this one sending the commnad) in order\n     *    to trigger the ask-master-on-reconnection protocol for connected\n     *    clients.\n     *\n     * Note that we don't check the replies returned by commands, since we\n     * will observe instead the effects in the next INFO output. */\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s\",\n        sentinelInstanceMapCommand(ri,\"MULTI\"));\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s %s %s\",\n        sentinelInstanceMapCommand(ri,\"SLAVEOF\"),\n        host, portstr);\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s REWRITE\",\n        sentinelInstanceMapCommand(ri,\"CONFIG\"));\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    /* CLIENT KILL TYPE <type> is only supported starting from Redis 2.8.12,\n     * however sending it to an instance not understanding this command is not\n     * an issue because CLIENT is variadic command, so Redis will not\n     * recognized as a syntax error, and the transaction will not fail (but\n     * only the unsupported command will fail). */\n    for (int type = 0; type < 2; type++) {\n        retval = redisAsyncCommand(ri->link->cc,\n            sentinelDiscardReplyCallback, ri, \"%s KILL TYPE %s\",\n            sentinelInstanceMapCommand(ri,\"CLIENT\"),\n            type == 0 ? \"normal\" : \"pubsub\");\n        if (retval == C_ERR) return retval;\n        ri->link->pending_commands++;\n    }\n\n    retval = redisAsyncCommand(ri->link->cc,\n        sentinelDiscardReplyCallback, ri, \"%s\",\n        sentinelInstanceMapCommand(ri,\"EXEC\"));\n    if (retval == C_ERR) return retval;\n    ri->link->pending_commands++;\n\n    return C_OK;\n}\n\n/* Setup the master state to start a failover. */\nvoid sentinelStartFailover(sentinelRedisInstance *master) {\n    serverAssert(master->flags & SRI_MASTER);\n\n    master->failover_state = SENTINEL_FAILOVER_STATE_WAIT_START;\n    master->flags |= SRI_FAILOVER_IN_PROGRESS;\n    master->failover_epoch = ++sentinel.current_epoch;\n    sentinelEvent(LL_WARNING,\"+new-epoch\",master,\"%llu\",\n        (unsigned long long) sentinel.current_epoch);\n    sentinelEvent(LL_WARNING,\"+try-failover\",master,\"%@\");\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 * 1) Master must be in ODOWN condition.\n * 2) No failover already in progress.\n * 3) No failover already attempted recently.\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 * Return non-zero if a failover was started. */\nint sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) {\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            serverLog(LL_WARNING,\n                \"Next failover delay: I will not start a failover before %s\",\n                ctimebuf);\n        }\n        return 0;\n    }\n\n    sentinelStartFailover(master);\n    return 1;\n}\n\n/* Select a suitable slave to promote. The current algorithm only uses\n * the following parameters:\n *\n * 1) None of the following conditions: S_DOWN, O_DOWN, DISCONNECTED.\n * 2) Last time the slave replied to ping no more than 5 times the PING period.\n * 3) info_refresh not older than 3 times the INFO refresh period.\n * 4) master_link_down_time no more than:\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 *    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 *    Anyway we'll select the best slave according to replication offset.\n * 5) Slave priority can't be zero, otherwise the slave is discarded.\n *\n * Among all the slaves matching the above conditions we select the slave\n * with, in order of sorting key:\n *\n * - lower slave_priority.\n * - bigger processed replication offset.\n * - lexicographically smaller runid.\n *\n * Basically if runid is the same, the slave that processed more commands\n * from the master is selected.\n *\n * The function returns the pointer to the selected slave, otherwise\n * NULL if no suitable slave was found.\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. */\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    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 from the master). */\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; /* a > b */\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    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\nsentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) {\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    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    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n        mstime_t info_validity_time;\n\n        if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;\n        if (slave->link->disconnected) continue;\n        if (mstime() - slave->link->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        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        if (mstime() - slave->info_refresh > info_validity_time) continue;\n        if (slave->master_link_down_time > max_master_down_time) continue;\n        instance[instances++] = slave;\n    }\n    dictReleaseIterator(di);\n    if (instances) {\n        qsort(instance,instances,sizeof(sentinelRedisInstance*),\n            compareSlavesForPromotion);\n        selected = instance[0];\n    }\n    zfree(instance);\n    return selected;\n}\n\n/* ---------------- Failover state machine implementation ------------------- */\nvoid sentinelFailoverWaitStart(sentinelRedisInstance *ri) {\n    char *leader;\n    int isleader;\n\n    /* Check if we are the leader for the failover epoch. */\n    leader = sentinelGetLeader(ri, ri->failover_epoch);\n    isleader = leader && strcasecmp(leader,sentinel.myid) == 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    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        if (election_timeout > ri->failover_timeout)\n            election_timeout = ri->failover_timeout;\n        /* Abort the failover if I'm not the leader after some time. */\n        if (mstime() - ri->failover_start_time > election_timeout) {\n            sentinelEvent(LL_WARNING,\"-failover-abort-not-elected\",ri,\"%@\");\n            sentinelAbortFailover(ri);\n        }\n        return;\n    }\n    sentinelEvent(LL_WARNING,\"+elected-leader\",ri,\"%@\");\n    if (sentinel.simfailure_flags & SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION)\n        sentinelSimFailureCrash();\n    ri->failover_state = SENTINEL_FAILOVER_STATE_SELECT_SLAVE;\n    ri->failover_state_change_time = mstime();\n    sentinelEvent(LL_WARNING,\"+failover-state-select-slave\",ri,\"%@\");\n}\n\nvoid sentinelFailoverSelectSlave(sentinelRedisInstance *ri) {\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    if (slave == NULL) {\n        sentinelEvent(LL_WARNING,\"-failover-abort-no-good-slave\",ri,\"%@\");\n        sentinelAbortFailover(ri);\n    } else {\n        sentinelEvent(LL_WARNING,\"+selected-slave\",slave,\"%@\");\n        slave->flags |= SRI_PROMOTED;\n        ri->promoted_slave = slave;\n        ri->failover_state = SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE;\n        ri->failover_state_change_time = mstime();\n        sentinelEvent(LL_NOTICE,\"+failover-state-send-slaveof-noone\",\n            slave, \"%@\");\n    }\n}\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    if (ri->promoted_slave->link->disconnected) {\n        if (mstime() - ri->failover_state_change_time > ri->failover_timeout) {\n            sentinelEvent(LL_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     * 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    retval = sentinelSendSlaveOf(ri->promoted_slave,NULL,0);\n    if (retval != C_OK) return;\n    sentinelEvent(LL_NOTICE, \"+failover-state-wait-promotion\",\n        ri->promoted_slave,\"%@\");\n    ri->failover_state = SENTINEL_FAILOVER_STATE_WAIT_PROMOTION;\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. */\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(LL_WARNING,\"-failover-abort-slave-timeout\",ri,\"%@\");\n        sentinelAbortFailover(ri);\n    }\n}\n\nvoid sentinelFailoverDetectEnd(sentinelRedisInstance *master) {\n    int not_reconfigured = 0, timeout = 0;\n    dictIterator *di;\n    dictEntry *de;\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    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    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue;\n        if (slave->flags & SRI_S_DOWN) continue;\n        not_reconfigured++;\n    }\n    dictReleaseIterator(di);\n\n    /* Force end of failover on timeout. */\n    if (elapsed > master->failover_timeout) {\n        not_reconfigured = 0;\n        timeout = 1;\n        sentinelEvent(LL_WARNING,\"+failover-end-for-timeout\",master,\"%@\");\n    }\n\n    if (not_reconfigured == 0) {\n        sentinelEvent(LL_WARNING,\"+failover-end\",master,\"%@\");\n        master->failover_state = SENTINEL_FAILOVER_STATE_UPDATE_CONFIG;\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        di = dictGetIterator(master->slaves);\n        while((de = dictNext(di)) != NULL) {\n            sentinelRedisInstance *slave = dictGetVal(de);\n            int retval;\n\n            if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE|SRI_RECONF_SENT)) continue;\n            if (slave->link->disconnected) continue;\n\n            retval = sentinelSendSlaveOf(slave,\n                    master->promoted_slave->addr->ip,\n                    master->promoted_slave->addr->port);\n            if (retval == C_OK) {\n                sentinelEvent(LL_NOTICE,\"+slave-reconf-sent-be\",slave,\"%@\");\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. */\nvoid sentinelFailoverReconfNextSlave(sentinelRedisInstance *master) {\n    dictIterator *di;\n    dictEntry *de;\n    int in_progress = 0;\n\n    di = dictGetIterator(master->slaves);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *slave = dictGetVal(de);\n\n        if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG))\n            in_progress++;\n    }\n    dictReleaseIterator(di);\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        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            sentinelEvent(LL_NOTICE,\"-slave-reconf-sent-timeout\",slave,\"%@\");\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        if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG)) continue;\n        if (slave->link->disconnected) continue;\n\n        /* Send SLAVEOF <new master>. */\n        retval = sentinelSendSlaveOf(slave,\n                master->promoted_slave->addr->ip,\n                master->promoted_slave->addr->port);\n        if (retval == C_OK) {\n            slave->flags |= SRI_RECONF_SENT;\n            slave->slave_reconf_sent_time = mstime();\n            sentinelEvent(LL_NOTICE,\"+slave-reconf-sent\",slave,\"%@\");\n            in_progress++;\n        }\n    }\n    dictReleaseIterator(di);\n\n    /* Check if all the slaves are reconfigured and handle timeout. */\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. */\nvoid sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) {\n    sentinelRedisInstance *ref = master->promoted_slave ?\n                                 master->promoted_slave : master;\n\n    sentinelEvent(LL_WARNING,\"+switch-master\",master,\"%s %s %d %s %d\",\n        master->name, master->addr->ip, master->addr->port,\n        ref->addr->ip, ref->addr->port);\n\n    sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port);\n}\n\nvoid sentinelFailoverStateMachine(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_MASTER);\n\n    if (!(ri->flags & SRI_FAILOVER_IN_PROGRESS)) return;\n\n    switch(ri->failover_state) {\n        case SENTINEL_FAILOVER_STATE_WAIT_START:\n            sentinelFailoverWaitStart(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_SELECT_SLAVE:\n            sentinelFailoverSelectSlave(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE:\n            sentinelFailoverSendSlaveOfNoOne(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION:\n            sentinelFailoverWaitPromotion(ri);\n            break;\n        case SENTINEL_FAILOVER_STATE_RECONF_SLAVES:\n            sentinelFailoverReconfNextSlave(ri);\n            break;\n    }\n}\n\n/* Abort a failover in progress:\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). */\nvoid sentinelAbortFailover(sentinelRedisInstance *ri) {\n    serverAssert(ri->flags & SRI_FAILOVER_IN_PROGRESS);\n    serverAssert(ri->failover_state <= SENTINEL_FAILOVER_STATE_WAIT_PROMOTION);\n\n    ri->flags &= ~(SRI_FAILOVER_IN_PROGRESS|SRI_FORCE_FAILOVER);\n    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;\n    ri->failover_state_change_time = mstime();\n    if (ri->promoted_slave) {\n        ri->promoted_slave->flags &= ~SRI_PROMOTED;\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. */\nvoid sentinelHandleRedisInstance(sentinelRedisInstance *ri) {\n    /* ========== MONITORING HALF ============ */\n    /* Every kind of instance */\n    sentinelReconnectInstance(ri);\n    sentinelSendPeriodicCommands(ri);\n\n    /* ============== ACTING HALF ============= */\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    if (sentinel.tilt) {\n        if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;\n        sentinel.tilt = 0;\n        sentinelEvent(LL_WARNING,\"-tilt\",NULL,\"#tilt mode exited\");\n    }\n\n    /* Every kind of instance */\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    if (ri->flags & SRI_MASTER) {\n        sentinelCheckObjectivelyDown(ri);\n        if (sentinelStartFailoverIfNeeded(ri))\n            sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);\n        sentinelFailoverStateMachine(ri);\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. */\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    di = dictGetIterator(instances);\n    while((de = dictNext(di)) != NULL) {\n        sentinelRedisInstance *ri = dictGetVal(de);\n\n        sentinelHandleRedisInstance(ri);\n        if (ri->flags & SRI_MASTER) {\n            sentinelHandleDictOfRedisInstances(ri->slaves);\n            sentinelHandleDictOfRedisInstances(ri->sentinels);\n            if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) {\n                switch_to_promoted = ri;\n            }\n        }\n    }\n    if (switch_to_promoted)\n        sentinelFailoverSwitchToPromotedSlave(switch_to_promoted);\n    dictReleaseIterator(di);\n}\n\n/* This function checks if we need to enter the TITL mode.\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 * 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 * 1) The Sentiel 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 * 2) The system clock was altered significantly.\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 * During TILT time we still collect information, we just do not act. */\nvoid sentinelCheckTiltCondition(void) {\n    mstime_t now = mstime();\n    mstime_t delta = now - sentinel.previous_time;\n\n    if (delta < 0 || delta > SENTINEL_TILT_TRIGGER) {\n        sentinel.tilt = 1;\n        sentinel.tilt_start_time = mstime();\n        sentinelEvent(LL_WARNING,\"+tilt\",NULL,\"#tilt mode entered\");\n    }\n    sentinel.previous_time = mstime();\n}\n\nvoid sentinelTimer(void) {\n    sentinelCheckTiltCondition();\n    sentinelHandleDictOfRedisInstances(sentinel.masters);\n    sentinelRunPendingScripts();\n    sentinelCollectTerminatedScripts();\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 = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/server.c",
    "content": "/*\n * Copyright (c) 2009-2016, 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 \"server.h\"\n#include \"cluster.h\"\n#include \"slowlog.h\"\n#include \"bio.h\"\n#include \"latency.h\"\n#include \"atomicvar.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 <sys/un.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#include <sys/socket.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 */\nvolatile unsigned long lru_clock; /* Server global current LRU time. */\n\n/* Our command table.\n *\n * Every entry is composed of the following fields:\n *\n * name:        A string representing the command name.\n *\n * function:    Pointer to the C function implementing the command.\n *\n * arity:       Number of arguments, it is possible to use -N to say >= N\n *\n * sflags:      Command flags as string. See below for a table of flags.\n *\n * flags:       Flags as bitmask. Computed by Redis using the 'sflags' field.\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 *\n * first_key_index: First argument that is a key\n *\n * last_key_index: Last argument that is a key\n *\n * key_step:    Step to get all the keys from first to last argument.\n *              For instance in MSET the step is two since arguments\n *              are key,val,key,val,...\n *\n * microseconds: Microseconds of total execution time for this command.\n *\n * calls:       Total number of calls of this command.\n *\n * id:          Command bit identifier for ACLs or other goals.\n *\n * The flags, microseconds and calls fields are computed by Redis and should\n * always be set to zero.\n *\n * Command flags are expressed using space separated strings, that are turned\n * into actual flags by the populateCommandTable() function.\n *\n * This is the meaning of the flags:\n *\n * write:       Write command (may modify the key space).\n *\n * read-only:   All the non special commands just reading from keys without\n *              changing the content, or returning other informations like\n *              the TIME command. Special commands such administrative commands\n *              or transaction related commands (multi, exec, discard, ...)\n *              are not flagged as read-only commands, since they affect the\n *              server or the connection in other ways.\n *\n * use-memory:  May increase memory usage once called. Don't allow if out\n *              of memory.\n *\n * admin:       Administrative command, like SAVE or SHUTDOWN.\n *\n * pub-sub:     Pub/Sub related command.\n *\n * no-script:   Command not allowed in scripts.\n *\n * random:      Random command. Command is not deterministic, that is, the same\n *              command with the same arguments, with the same key space, may\n *              have different results. For instance SPOP and RANDOMKEY are\n *              two random commands.\n *\n * to-sort:     Sort command output array if called from script, so that the\n *              output is deterministic. When this flag is used (not always\n *              possible), then the \"random\" flag is not needed.\n *\n * ok-loading:  Allow the command while loading the database.\n *\n * ok-stale:    Allow the command while a slave has stale data but is not\n *              allowed to serve this data. Normally no command is accepted\n *              in this condition but just a few.\n *\n * no-monitor:  Do not automatically propagate the command on MONITOR.\n *\n * no-slowlog:  Do not automatically propagate the command to the slowlog.\n *\n * cluster-asking: Perform an implicit ASKING for this command, so the\n *              command will be accepted in cluster mode if the slot is marked\n *              as 'importing'.\n *\n * fast:        Fast command: O(1) or O(log(N)) command that should never\n *              delay its execution as long as the kernel scheduler is giving\n *              us time. Note that commands that may trigger a DEL as a side\n *              effect (like SET) are not fast commands.\n *\n * The following additional flags are only used in order to put commands\n * in a specific ACL category. Commands can have multiple ACL categories.\n *\n * @keyspace, @read, @write, @set, @sortedset, @list, @hash, @string, @bitmap,\n * @hyperloglog, @stream, @admin, @fast, @slow, @pubsub, @blocking, @dangerous,\n * @connection, @transaction, @scripting, @geo.\n *\n * Note that:\n *\n * 1) The read-only flag implies the @read ACL category.\n * 2) The write flag implies the @write ACL category.\n * 3) The fast flag implies the @fast ACL category.\n * 4) The admin flag implies the @admin and @dangerous ACL category.\n * 5) The pub-sub flag implies the @pubsub ACL category.\n * 6) The lack of fast flag implies the @slow ACL category.\n * 7) The non obvious \"keyspace\" category includes the commands\n *    that interact with keys without having anything to do with\n *    specific data structures, such as: DEL, RENAME, MOVE, SELECT,\n *    TYPE, EXPIRE*, PEXPIRE*, TTL, PTTL, ...\n */\n\nstruct redisCommand redisCommandTable[] = {\n    {\"module\",moduleCommand,-2,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"get\",getCommand,2,\n     \"read-only fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* Note that we can't flag set as fast, since it may perform an\n     * implicit DEL of a large key. */\n    {\"set\",setCommand,-3,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"setnx\",setnxCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"setex\",setexCommand,4,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"psetex\",psetexCommand,4,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"append\",appendCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"strlen\",strlenCommand,2,\n     \"read-only fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"del\",delCommand,-2,\n     \"write @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"unlink\",unlinkCommand,-2,\n     \"write fast @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"exists\",existsCommand,-2,\n     \"read-only fast @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"setbit\",setbitCommand,4,\n     \"write use-memory @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"getbit\",getbitCommand,3,\n     \"read-only fast @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bitfield\",bitfieldCommand,-2,\n     \"write use-memory @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bitfield_ro\",bitfieldroCommand,-2,\n     \"read-only fast @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"setrange\",setrangeCommand,4,\n     \"write use-memory @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"getrange\",getrangeCommand,4,\n     \"read-only @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"substr\",getrangeCommand,4,\n     \"read-only @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"incr\",incrCommand,2,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"decr\",decrCommand,2,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"mget\",mgetCommand,-2,\n     \"read-only fast @string\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"rpush\",rpushCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lpush\",lpushCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"rpushx\",rpushxCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lpushx\",lpushxCommand,-3,\n     \"write use-memory fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"linsert\",linsertCommand,5,\n     \"write use-memory @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"rpop\",rpopCommand,2,\n     \"write fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lpop\",lpopCommand,2,\n     \"write fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"brpop\",brpopCommand,-3,\n     \"write no-script @list @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"brpoplpush\",brpoplpushCommand,4,\n     \"write use-memory no-script @list @blocking\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"blpop\",blpopCommand,-3,\n     \"write no-script @list @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"llen\",llenCommand,2,\n     \"read-only fast @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lindex\",lindexCommand,3,\n     \"read-only @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lset\",lsetCommand,4,\n     \"write use-memory @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lrange\",lrangeCommand,4,\n     \"read-only @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"ltrim\",ltrimCommand,4,\n     \"write @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"lrem\",lremCommand,4,\n     \"write @list\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"rpoplpush\",rpoplpushCommand,3,\n     \"write use-memory @list\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"sadd\",saddCommand,-3,\n     \"write use-memory fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"srem\",sremCommand,-3,\n     \"write fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"smove\",smoveCommand,4,\n     \"write fast @set\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"sismember\",sismemberCommand,3,\n     \"read-only fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"scard\",scardCommand,2,\n     \"read-only fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"spop\",spopCommand,-2,\n     \"write random fast @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"srandmember\",srandmemberCommand,-2,\n     \"read-only random @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"sinter\",sinterCommand,-2,\n     \"read-only to-sort @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sinterstore\",sinterstoreCommand,-3,\n     \"write use-memory @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sunion\",sunionCommand,-2,\n     \"read-only to-sort @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sunionstore\",sunionstoreCommand,-3,\n     \"write use-memory @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sdiff\",sdiffCommand,-2,\n     \"read-only to-sort @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"sdiffstore\",sdiffstoreCommand,-3,\n     \"write use-memory @set\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"smembers\",sinterCommand,2,\n     \"read-only to-sort @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"sscan\",sscanCommand,-3,\n     \"read-only random @set\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zadd\",zaddCommand,-4,\n     \"write use-memory fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zincrby\",zincrbyCommand,4,\n     \"write use-memory fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrem\",zremCommand,-3,\n     \"write fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zremrangebyscore\",zremrangebyscoreCommand,4,\n     \"write @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zremrangebyrank\",zremrangebyrankCommand,4,\n     \"write @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zremrangebylex\",zremrangebylexCommand,4,\n     \"write @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zunionstore\",zunionstoreCommand,-4,\n     \"write use-memory @sortedset\",\n     0,zunionInterGetKeys,0,0,0,0,0,0},\n\n    {\"zinterstore\",zinterstoreCommand,-4,\n     \"write use-memory @sortedset\",\n     0,zunionInterGetKeys,0,0,0,0,0,0},\n\n    {\"zrange\",zrangeCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrangebyscore\",zrangebyscoreCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrangebyscore\",zrevrangebyscoreCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrangebylex\",zrangebylexCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrangebylex\",zrevrangebylexCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zcount\",zcountCommand,4,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zlexcount\",zlexcountCommand,4,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrange\",zrevrangeCommand,-4,\n     \"read-only @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zcard\",zcardCommand,2,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zscore\",zscoreCommand,3,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrank\",zrankCommand,3,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zrevrank\",zrevrankCommand,3,\n     \"read-only fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zscan\",zscanCommand,-3,\n     \"read-only random @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zpopmin\",zpopminCommand,-2,\n     \"write fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"zpopmax\",zpopmaxCommand,-2,\n     \"write fast @sortedset\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bzpopmin\",bzpopminCommand,-3,\n     \"write no-script fast @sortedset @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"bzpopmax\",bzpopmaxCommand,-3,\n     \"write no-script fast @sortedset @blocking\",\n     0,NULL,1,-2,1,0,0,0},\n\n    {\"hset\",hsetCommand,-4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hsetnx\",hsetnxCommand,4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hget\",hgetCommand,3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hmset\",hsetCommand,-4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hmget\",hmgetCommand,-3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hincrby\",hincrbyCommand,4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hincrbyfloat\",hincrbyfloatCommand,4,\n     \"write use-memory fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hdel\",hdelCommand,-3,\n     \"write fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hlen\",hlenCommand,2,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hstrlen\",hstrlenCommand,3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hkeys\",hkeysCommand,2,\n     \"read-only to-sort @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hvals\",hvalsCommand,2,\n     \"read-only to-sort @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hgetall\",hgetallCommand,2,\n     \"read-only random @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hexists\",hexistsCommand,3,\n     \"read-only fast @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"hscan\",hscanCommand,-3,\n     \"read-only random @hash\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"incrby\",incrbyCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"decrby\",decrbyCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"incrbyfloat\",incrbyfloatCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"getset\",getsetCommand,3,\n     \"write use-memory fast @string\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"mset\",msetCommand,-3,\n     \"write use-memory @string\",\n     0,NULL,1,-1,2,0,0,0},\n\n    {\"msetnx\",msetnxCommand,-3,\n     \"write use-memory @string\",\n     0,NULL,1,-1,2,0,0,0},\n\n    {\"randomkey\",randomkeyCommand,1,\n     \"read-only random @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"select\",selectCommand,2,\n     \"ok-loading fast ok-stale @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"swapdb\",swapdbCommand,3,\n     \"write fast @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"move\",moveCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* Like for SET, we can't mark rename as a fast command because\n     * overwriting the target key may result in an implicit slow DEL. */\n    {\"rename\",renameCommand,3,\n     \"write @keyspace\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"renamenx\",renamenxCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,2,1,0,0,0},\n\n    {\"expire\",expireCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"expireat\",expireatCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"pexpire\",pexpireCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"pexpireat\",pexpireatCommand,3,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"keys\",keysCommand,2,\n     \"read-only to-sort @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"scan\",scanCommand,-2,\n     \"read-only random @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"dbsize\",dbsizeCommand,1,\n     \"read-only fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"auth\",authCommand,-2,\n     \"no-auth no-script ok-loading ok-stale fast no-monitor no-slowlog @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    /* We don't allow PING during loading since in Redis PING is used as\n     * failure detection, and a loading server is considered to be\n     * not available. */\n    {\"ping\",pingCommand,-1,\n     \"ok-stale fast @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"echo\",echoCommand,2,\n     \"read-only fast @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"save\",saveCommand,1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"bgsave\",bgsaveCommand,-1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"bgrewriteaof\",bgrewriteaofCommand,1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"shutdown\",shutdownCommand,-1,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"lastsave\",lastsaveCommand,1,\n     \"read-only random fast ok-loading ok-stale @admin @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"type\",typeCommand,2,\n     \"read-only fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"multi\",multiCommand,1,\n     \"no-script fast ok-loading ok-stale @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"exec\",execCommand,1,\n     \"no-script no-monitor no-slowlog ok-loading ok-stale @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"discard\",discardCommand,1,\n     \"no-script fast ok-loading ok-stale @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"sync\",syncCommand,1,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"psync\",syncCommand,3,\n     \"admin no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"replconf\",replconfCommand,-1,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"flushdb\",flushdbCommand,-1,\n     \"write @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"flushall\",flushallCommand,-1,\n     \"write @keyspace @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"sort\",sortCommand,-2,\n     \"write use-memory @list @set @sortedset @dangerous\",\n     0,sortGetKeys,1,1,1,0,0,0},\n\n    {\"info\",infoCommand,-1,\n     \"ok-loading ok-stale random @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"monitor\",monitorCommand,1,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"ttl\",ttlCommand,2,\n     \"read-only fast random @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"touch\",touchCommand,-2,\n     \"read-only fast @keyspace\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"pttl\",pttlCommand,2,\n     \"read-only fast random @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"persist\",persistCommand,2,\n     \"write fast @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"slaveof\",replicaofCommand,3,\n     \"admin no-script ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"replicaof\",replicaofCommand,3,\n     \"admin no-script ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"role\",roleCommand,1,\n     \"ok-loading ok-stale no-script fast read-only @dangerous\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"debug\",debugCommand,-2,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"config\",configCommand,-2,\n     \"admin ok-loading ok-stale no-script\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"subscribe\",subscribeCommand,-2,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"unsubscribe\",unsubscribeCommand,-1,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"psubscribe\",psubscribeCommand,-2,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"punsubscribe\",punsubscribeCommand,-1,\n     \"pub-sub no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"publish\",publishCommand,3,\n     \"pub-sub ok-loading ok-stale fast\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"pubsub\",pubsubCommand,-2,\n     \"pub-sub ok-loading ok-stale random\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"watch\",watchCommand,-2,\n     \"no-script fast @transaction\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"unwatch\",unwatchCommand,1,\n     \"no-script fast @transaction\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"cluster\",clusterCommand,-2,\n     \"admin ok-stale random\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"restore\",restoreCommand,-4,\n     \"write use-memory @keyspace @dangerous\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"restore-asking\",restoreCommand,-4,\n    \"write use-memory cluster-asking @keyspace @dangerous\",\n    0,NULL,1,1,1,0,0,0},\n\n    {\"migrate\",migrateCommand,-6,\n     \"write random @keyspace @dangerous\",\n     0,migrateGetKeys,0,0,0,0,0,0},\n\n    {\"asking\",askingCommand,1,\n     \"fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"readonly\",readonlyCommand,1,\n     \"fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"readwrite\",readwriteCommand,1,\n     \"fast @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"dump\",dumpCommand,2,\n     \"read-only random @keyspace\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"object\",objectCommand,-2,\n     \"read-only random @keyspace\",\n     0,NULL,2,2,1,0,0,0},\n\n    {\"memory\",memoryCommand,-2,\n     \"random read-only\",\n     0,memoryGetKeys,0,0,0,0,0,0},\n\n    {\"client\",clientCommand,-2,\n     \"admin no-script random ok-loading ok-stale @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"hello\",helloCommand,-2,\n     \"no-auth no-script fast no-monitor ok-loading ok-stale no-slowlog @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    /* EVAL can modify the dataset, however it is not flagged as a write\n     * command since we do the check while running commands from Lua. */\n    {\"eval\",evalCommand,-3,\n     \"no-script @scripting\",\n     0,evalGetKeys,0,0,0,0,0,0},\n\n    {\"evalsha\",evalShaCommand,-3,\n     \"no-script @scripting\",\n     0,evalGetKeys,0,0,0,0,0,0},\n\n    {\"slowlog\",slowlogCommand,-2,\n     \"admin random ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"script\",scriptCommand,-2,\n     \"no-script @scripting\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"time\",timeCommand,1,\n     \"read-only random fast ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"bitop\",bitopCommand,-4,\n     \"write use-memory @bitmap\",\n     0,NULL,2,-1,1,0,0,0},\n\n    {\"bitcount\",bitcountCommand,-2,\n     \"read-only @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"bitpos\",bitposCommand,-3,\n     \"read-only @bitmap\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"wait\",waitCommand,3,\n     \"no-script @keyspace\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"command\",commandCommand,-1,\n     \"ok-loading ok-stale random @connection\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"geoadd\",geoaddCommand,-5,\n     \"write use-memory @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* GEORADIUS has store options that may write. */\n    {\"georadius\",georadiusCommand,-6,\n     \"write @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"georadius_ro\",georadiusroCommand,-6,\n     \"read-only @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"georadiusbymember\",georadiusbymemberCommand,-5,\n     \"write @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"georadiusbymember_ro\",georadiusbymemberroCommand,-5,\n     \"read-only @geo\",\n     0,georadiusGetKeys,1,1,1,0,0,0},\n\n    {\"geohash\",geohashCommand,-2,\n     \"read-only @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"geopos\",geoposCommand,-2,\n     \"read-only @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"geodist\",geodistCommand,-4,\n     \"read-only @geo\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"pfselftest\",pfselftestCommand,1,\n     \"admin @hyperloglog\",\n      0,NULL,0,0,0,0,0,0},\n\n    {\"pfadd\",pfaddCommand,-2,\n     \"write use-memory fast @hyperloglog\",\n     0,NULL,1,1,1,0,0,0},\n\n    /* Technically speaking PFCOUNT may change the key since it changes the\n     * final bytes in the HyperLogLog representation. However in this case\n     * we claim that the representation, even if accessible, is an internal\n     * affair, and the command is semantically read only. */\n    {\"pfcount\",pfcountCommand,-2,\n     \"read-only @hyperloglog\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"pfmerge\",pfmergeCommand,-2,\n     \"write use-memory @hyperloglog\",\n     0,NULL,1,-1,1,0,0,0},\n\n    {\"pfdebug\",pfdebugCommand,-3,\n     \"admin write\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"xadd\",xaddCommand,-5,\n     \"write use-memory fast random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xrange\",xrangeCommand,-4,\n     \"read-only @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xrevrange\",xrevrangeCommand,-4,\n     \"read-only @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xlen\",xlenCommand,2,\n     \"read-only fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xread\",xreadCommand,-4,\n     \"read-only @stream @blocking\",\n     0,xreadGetKeys,1,1,1,0,0,0},\n\n    {\"xreadgroup\",xreadCommand,-7,\n     \"write @stream @blocking\",\n     0,xreadGetKeys,1,1,1,0,0,0},\n\n    {\"xgroup\",xgroupCommand,-2,\n     \"write use-memory @stream\",\n     0,NULL,2,2,1,0,0,0},\n\n    {\"xsetid\",xsetidCommand,3,\n     \"write use-memory fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xack\",xackCommand,-4,\n     \"write fast random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xpending\",xpendingCommand,-3,\n     \"read-only random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xclaim\",xclaimCommand,-6,\n     \"write random fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xinfo\",xinfoCommand,-2,\n     \"read-only random @stream\",\n     0,NULL,2,2,1,0,0,0},\n\n    {\"xdel\",xdelCommand,-3,\n     \"write fast @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"xtrim\",xtrimCommand,-2,\n     \"write random @stream\",\n     0,NULL,1,1,1,0,0,0},\n\n    {\"post\",securityWarningCommand,-1,\n     \"ok-loading ok-stale read-only\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"host:\",securityWarningCommand,-1,\n     \"ok-loading ok-stale read-only\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"latency\",latencyCommand,-2,\n     \"admin no-script ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"lolwut\",lolwutCommand,-1,\n     \"read-only fast\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"acl\",aclCommand,-2,\n     \"admin no-script no-slowlog ok-loading ok-stale\",\n     0,NULL,0,0,0,0,0,0},\n\n    {\"stralgo\",stralgoCommand,-2,\n     \"read-only @string\",\n     0,lcsGetKeys,0,0,0,0,0,0}\n};\n\n/*============================ Utility functions ============================ */\n\n/* We use a private localtime implementation which is fork-safe. The logging\n * function of Redis may be called from other threads. */\nvoid nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst);\n\n/* Low level logging. To use only for very big messages, otherwise\n * serverLog() is to prefer. */\nvoid serverLogRaw(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 & LL_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        int role_char;\n        pid_t pid = getpid();\n\n        gettimeofday(&tv,NULL);\n        struct tm tm;\n        nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active);\n        off = strftime(buf,sizeof(buf),\"%d %b %Y %H:%M:%S.\",&tm);\n        snprintf(buf+off,sizeof(buf)-off,\"%03d\",(int)tv.tv_usec/1000);\n        if (server.sentinel_mode) {\n            role_char = 'X'; /* Sentinel. */\n        } else if (pid != server.pid) {\n            role_char = 'C'; /* RDB / AOF writing child. */\n        } else {\n            role_char = (server.masterhost ? 'S':'M'); /* Slave or Master. */\n        }\n        fprintf(fp,\"%d:%c %s %c %s\\n\",\n            (int)getpid(),role_char, 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 serverLogRaw() 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 serverLog(int level, const char *fmt, ...) {\n    va_list ap;\n    char msg[LOG_MAX_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    serverLogRaw(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 serverLog(). */\nvoid serverLogFromHandler(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,buf,strlen(buf)) == -1) goto err;\n    if (write(fd,\":signal-handler (\",17) == -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 */\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 */\nmstime_t 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 redis 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 dictObjectDestructor(void *privdata, void *val)\n{\n    DICT_NOTUSED(privdata);\n\n    if (val == NULL) return; /* Lazy freeing will set value 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\nuint64_t dictObjHash(const void *key) {\n    const robj *o = key;\n    return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));\n}\n\nuint64_t dictSdsHash(const void *key) {\n    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));\n}\n\nuint64_t 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 == OBJ_ENCODING_INT &&\n        o2->encoding == OBJ_ENCODING_INT)\n            return o1->ptr == o2->ptr;\n\n    /* Due to OBJ_STATIC_REFCOUNT, we avoid calling getDecodedObject() without\n     * good reasons, because it would incrRefCount() the object, which\n     * is invalid. So we check to make sure dictFind() works with static\n     * objects as well. */\n    if (o1->refcount != OBJ_STATIC_REFCOUNT) o1 = getDecodedObject(o1);\n    if (o2->refcount != OBJ_STATIC_REFCOUNT) o2 = getDecodedObject(o2);\n    cmp = dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);\n    if (o1->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o1);\n    if (o2->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o2);\n    return cmp;\n}\n\nuint64_t 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 == OBJ_ENCODING_INT) {\n            char buf[32];\n            int len;\n\n            len = ll2string(buf,32,(long)o->ptr);\n            return dictGenHashFunction((unsigned char*)buf, len);\n        } else {\n            uint64_t 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/* Generic hash table type where keys are Redis Objects, Values\n * dummy pointers. */\ndictType objectKeyPointerValueDictType = {\n    dictEncObjHash,            /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictEncObjKeyCompare,      /* key compare */\n    dictObjectDestructor,      /* key destructor */\n    NULL                       /* val destructor */\n};\n\n/* Like objectKeyPointerValueDictType(), but values can be destroyed, if\n * not NULL, calling zfree(). */\ndictType objectKeyHeapPointerValueDictType = {\n    dictEncObjHash,            /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictEncObjKeyCompare,      /* key compare */\n    dictObjectDestructor,      /* key destructor */\n    dictVanillaFree            /* val destructor */\n};\n\n/* Set dictionary type. Keys are SDS strings, values are ot used. */\ndictType setDictType = {\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/* Sorted sets hash (note: a skiplist is used in addition to the hash table) */\ndictType zsetDictType = {\n    dictSdsHash,               /* hash function */\n    NULL,                      /* key dup */\n    NULL,                      /* val dup */\n    dictSdsKeyCompare,         /* key compare */\n    NULL,                      /* Note: SDS string shared & freed by skiplist */\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    dictObjectDestructor   /* 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    dictObjectDestructor        /* val destructor */\n};\n\n/* Db->expires */\ndictType keyptrDictType = {\n    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    NULL,                       /* key destructor */\n    NULL                        /* val destructor */\n};\n\n/* 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    dictSdsHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictSdsKeyCompare,          /* key compare */\n    dictSdsDestructor,          /* key destructor */\n    dictSdsDestructor           /* val destructor */\n};\n\n/* Keylist hash table type has unencoded redis objects as keys and\n * lists as values. It's used for blocking operations (BLPOP) and to\n * map swapped keys to a list of clients waiting for this keys to be loaded. */\ndictType keylistDictType = {\n    dictObjHash,                /* hash function */\n    NULL,                       /* key dup */\n    NULL,                       /* val dup */\n    dictObjKeyCompare,          /* key compare */\n    dictObjectDestructor,       /* key destructor */\n    dictListDestructor          /* val destructor */\n};\n\n/* 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/* 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 modulesDictType = {\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 > DICT_HT_INITIAL_SIZE &&\n            (used*100/size < HASHTABLE_MIN_FILL));\n}\n\n/* If the percentage of used slots in the HT reaches HASHTABLE_MIN_FILL\n * we resize the hash table to save memory */\nvoid 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 * The function returns 1 if some rehashing was performed, otherwise 0\n * is returned. */\nint incrementallyRehash(int dbid) {\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    /* 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    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 (!hasActiveChildProcess())\n        dictEnableResize();\n    else\n        dictDisableResize();\n}\n\n/* Return true if there are no active children processes doing RDB saving,\n * AOF rewriting, or some side process spawned by a loaded module. */\nint hasActiveChildProcess() {\n    return server.rdb_child_pid != -1 ||\n           server.aof_child_pid != -1 ||\n           server.module_child_pid != -1;\n}\n\n/* Return true if this instance has persistence completely turned off:\n * both RDB and AOF are disabled. */\nint allPersistenceDisabled(void) {\n    return server.saveparamslen == 0 && server.aof_state == AOF_OFF;\n}\n\n/* ======================= Cron: called every 100 ms ======================== */\n\n/* Add a sample to the operations per second array of samples. */\nvoid trackInstantaneousMetric(int metric, long long current_reading) {\n    long long t = mstime() - server.inst_metric[metric].last_sample_time;\n    long long ops = current_reading -\n                    server.inst_metric[metric].last_sample_count;\n    long long ops_sec;\n\n    ops_sec = t > 0 ? (ops*1000/t) : 0;\n\n    server.inst_metric[metric].samples[server.inst_metric[metric].idx] =\n        ops_sec;\n    server.inst_metric[metric].idx++;\n    server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;\n    server.inst_metric[metric].last_sample_time = mstime();\n    server.inst_metric[metric].last_sample_count = current_reading;\n}\n\n/* Return the mean of all the samples. */\nlong long getInstantaneousMetric(int metric) {\n    int j;\n    long long sum = 0;\n\n    for (j = 0; j < STATS_METRIC_SAMPLES; j++)\n        sum += server.inst_metric[metric].samples[j];\n    return sum / STATS_METRIC_SAMPLES;\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 * The function always returns 0 as it never terminates the client. */\nint clientsCronResizeQueryBuffer(client *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     * 1) Query buffer is > BIG_ARG and too big for latest peak.\n     * 2) Query buffer is > BIG_ARG and client is idle. */\n    if (querybuf_size > PROTO_MBULK_BIG_ARG &&\n         ((querybuf_size/(c->querybuf_peak+1)) > 2 ||\n          idletime > 2))\n    {\n        /* Only resize the query buffer if it is actually wasting\n         * at least a few kbytes. */\n        if (sdsavail(c->querybuf) > 1024*4) {\n            c->querybuf = sdsRemoveFreeSpace(c->querybuf);\n        }\n    }\n    /* Reset the peak again to capture the peak memory usage in the next\n     * cycle. */\n    c->querybuf_peak = 0;\n\n    /* Clients representing masters also use a \"pending query buffer\" that\n     * is the yet not applied part of the stream we are reading. Such buffer\n     * also needs resizing from time to time, otherwise after a very large\n     * transfer (a huge value or a big MIGRATE operation) it will keep using\n     * a lot of memory. */\n    if (c->flags & CLIENT_MASTER) {\n        /* There are two conditions to resize the pending query buffer:\n         * 1) Pending Query buffer is > LIMIT_PENDING_QUERYBUF.\n         * 2) Used length is smaller than pending_querybuf_size/2 */\n        size_t pending_querybuf_size = sdsAllocSize(c->pending_querybuf);\n        if(pending_querybuf_size > LIMIT_PENDING_QUERYBUF &&\n           sdslen(c->pending_querybuf) < (pending_querybuf_size/2))\n        {\n            c->pending_querybuf = sdsRemoveFreeSpace(c->pending_querybuf);\n        }\n    }\n    return 0;\n}\n\n/* This function is used in order to track clients using the biggest amount\n * of memory in the latest few seconds. This way we can provide such information\n * in the INFO output (clients section), without having to do an O(N) scan for\n * all the clients.\n *\n * This is how it works. We have an array of CLIENTS_PEAK_MEM_USAGE_SLOTS slots\n * where we track, for each, the biggest client output and input buffers we\n * saw in that slot. Every slot correspond to one of the latest seconds, since\n * the array is indexed by doing UNIXTIME % CLIENTS_PEAK_MEM_USAGE_SLOTS.\n *\n * When we want to know what was recently the peak memory usage, we just scan\n * such few slots searching for the maximum value. */\n#define CLIENTS_PEAK_MEM_USAGE_SLOTS 8\nsize_t ClientsPeakMemInput[CLIENTS_PEAK_MEM_USAGE_SLOTS];\nsize_t ClientsPeakMemOutput[CLIENTS_PEAK_MEM_USAGE_SLOTS];\n\nint clientsCronTrackExpansiveClients(client *c) {\n    size_t in_usage = sdsAllocSize(c->querybuf);\n    size_t out_usage = getClientOutputBufferMemoryUsage(c);\n    int i = server.unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS;\n    int zeroidx = (i+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS;\n\n    /* Always zero the next sample, so that when we switch to that second, we'll\n     * only register samples that are greater in that second without considering\n     * the history of such slot.\n     *\n     * Note: our index may jump to any random position if serverCron() is not\n     * called for some reason with the normal frequency, for instance because\n     * some slow command is called taking multiple seconds to execute. In that\n     * case our array may end containing data which is potentially older\n     * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem\n     * since here we want just to track if \"recently\" there were very expansive\n     * clients from the POV of memory usage. */\n    ClientsPeakMemInput[zeroidx] = 0;\n    ClientsPeakMemOutput[zeroidx] = 0;\n\n    /* Track the biggest values observed so far in this slot. */\n    if (in_usage > ClientsPeakMemInput[i]) ClientsPeakMemInput[i] = in_usage;\n    if (out_usage > ClientsPeakMemOutput[i]) ClientsPeakMemOutput[i] = out_usage;\n\n    return 0; /* This function never terminates the client. */\n}\n\n/* Iterating all the clients in getMemoryOverheadData() is too slow and\n * in turn would make the INFO command too slow. So we perform this\n * computation incrementally and track the (not instantaneous but updated\n * to the second) total memory used by clients using clinetsCron() in\n * a more incremental way (depending on server.hz). */\nint clientsCronTrackClientsMemUsage(client *c) {\n    size_t mem = 0;\n    int type = getClientType(c);\n    mem += getClientOutputBufferMemoryUsage(c);\n    mem += sdsAllocSize(c->querybuf);\n    mem += sizeof(client);\n    /* Now that we have the memory used by the client, remove the old\n     * value from the old categoty, and add it back. */\n    server.stat_clients_type_memory[c->client_cron_last_memory_type] -=\n        c->client_cron_last_memory_usage;\n    server.stat_clients_type_memory[type] += mem;\n    /* Remember what we added and where, to remove it next time. */\n    c->client_cron_last_memory_usage = mem;\n    c->client_cron_last_memory_type = type;\n    return 0;\n}\n\n/* Return the max samples in the memory usage of clients tracked by\n * the function clientsCronTrackExpansiveClients(). */\nvoid getExpansiveClientsInfo(size_t *in_usage, size_t *out_usage) {\n    size_t i = 0, o = 0;\n    for (int j = 0; j < CLIENTS_PEAK_MEM_USAGE_SLOTS; j++) {\n        if (ClientsPeakMemInput[j] > i) i = ClientsPeakMemInput[j];\n        if (ClientsPeakMemOutput[j] > o) o = ClientsPeakMemOutput[j];\n    }\n    *in_usage = i;\n    *out_usage = o;\n}\n\n/* This function is called by serverCron() and is used in order to perform\n * operations on clients that are important to perform constantly. For instance\n * we use this function in order to disconnect clients after a timeout, including\n * clients blocked in some blocking command with a non-zero timeout.\n *\n * The function makes some effort to process all the clients every second, even\n * if this cannot be strictly guaranteed, since serverCron() may be called with\n * an actual frequency lower than server.hz in case of latency events like slow\n * commands.\n *\n * It is very important for this function, and the functions it calls, to be\n * very fast: sometimes Redis has tens of hundreds of connected clients, and the\n * default server.hz value is 10, so sometimes here we need to process thousands\n * of clients per second, turning this function into a source of latency.\n */\n#define CLIENTS_CRON_MIN_ITERATIONS 5\nvoid clientsCron(void) {\n    /* Try to process at least numclients/server.hz of clients\n     * per call. Since normally (if there are no big latency events) this\n     * function is called server.hz times per second, in the average case we\n     * process all the clients in 1 second. */\n    int numclients = listLength(server.clients);\n    int iterations = numclients/server.hz;\n    mstime_t now = mstime();\n\n    /* Process at least a few clients while we are at it, even if we need\n     * to process less than CLIENTS_CRON_MIN_ITERATIONS to meet our contract\n     * of processing each client once per second. */\n    if (iterations < CLIENTS_CRON_MIN_ITERATIONS)\n        iterations = (numclients < CLIENTS_CRON_MIN_ITERATIONS) ?\n                     numclients : CLIENTS_CRON_MIN_ITERATIONS;\n\n    while(listLength(server.clients) && iterations--) {\n        client *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        listRotateTailToHead(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        if (clientsCronHandleTimeout(c,now)) continue;\n        if (clientsCronResizeQueryBuffer(c)) continue;\n        if (clientsCronTrackExpansiveClients(c)) continue;\n        if (clientsCronTrackClientsMemUsage(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. */\nvoid databasesCron(void) {\n    /* Expire keys by random sampling. Not required for slaves\n     * as master will synthesize DELs for us. */\n    if (server.active_expire_enabled) {\n        if (iAmMaster()) {\n            activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);\n        } else {\n            expireSlaveKeys();\n        }\n    }\n\n    /* Defrag keys gradually. */\n    activeDefragCycle();\n\n    /* Perform hash tables rehashing if needed, but only if there are no\n     * other processes saving the DB on disk. Otherwise rehashing is bad\n     * as will cause a lot of copy-on-write of memory pages. */\n    if (!hasActiveChildProcess()) {\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        int dbs_per_call = CRON_DBS_PER_CALL;\n        int j;\n\n        /* Don't test more DBs than we have. */\n        if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;\n\n        /* Resize */\n        for (j = 0; j < dbs_per_call; j++) {\n            tryResizeHashTables(resize_db % server.dbnum);\n            resize_db++;\n        }\n\n        /* Rehash */\n        if (server.activerehashing) {\n            for (j = 0; j < dbs_per_call; j++) {\n                int work_done = incrementallyRehash(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                } else {\n                    /* If this db didn't need rehash, we'll try the next one. */\n                    rehash_db++;\n                    rehash_db %= server.dbnum;\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).\n *\n * This function should be fast because it is called at every command execution\n * in call(), so it is possible to decide if to update the daylight saving\n * info or not using the 'update_daylight_info' argument. Normally we update\n * such info only when calling this function from serverCron() but not when\n * calling it from call(). */\nvoid updateCachedTime(int update_daylight_info) {\n    server.ustime = ustime();\n    server.mstime = server.ustime / 1000;\n    server.unixtime = server.mstime / 1000;\n\n    /* To get information about daylight saving time, we need to call\n     * localtime_r and cache the result. However calling localtime_r in this\n     * context is safe since we will never fork() while here, in the main\n     * thread. The logging function will call a thread safe version of\n     * localtime that has no locks. */\n    if (update_daylight_info) {\n        struct tm tm;\n        time_t ut = server.unixtime;\n        localtime_r(&ut,&tm);\n        server.daylight_active = tm.tm_isdst;\n    }\n}\n\nvoid checkChildrenDone(void) {\n    int statloc;\n    pid_t pid;\n\n    /* If we have a diskless rdb child (note that we support only one concurrent\n     * child), we want to avoid collecting it's exit status and acting on it\n     * as long as we didn't finish to drain the pipe, since then we're at risk\n     * of starting a new fork and a new pipe before we're done with the previous\n     * one. */\n    if (server.rdb_child_pid != -1 && server.rdb_pipe_conns)\n        return;\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        /* sigKillChildHandler catches the signal and calls exit(), but we\n         * must make sure not to flag lastbgsave_status, etc incorrectly.\n         * We could directly terminate the child process via SIGUSR1\n         * without handling it, but in this case Valgrind will log an\n         * annoying error. */\n        if (exitcode == SERVER_CHILD_NOERROR_RETVAL) {\n            bysignal = SIGUSR1;\n            exitcode = 1;\n        }\n\n        if (pid == -1) {\n            serverLog(LL_WARNING,\"wait3() returned an error: %s. \"\n                \"rdb_child_pid = %d, aof_child_pid = %d, module_child_pid = %d\",\n                strerror(errno),\n                (int) server.rdb_child_pid,\n                (int) server.aof_child_pid,\n                (int) server.module_child_pid);\n        } else if (pid == server.rdb_child_pid) {\n            backgroundSaveDoneHandler(exitcode,bysignal);\n            if (!bysignal && exitcode == 0) receiveChildInfo();\n        } else if (pid == server.aof_child_pid) {\n            backgroundRewriteDoneHandler(exitcode,bysignal);\n            if (!bysignal && exitcode == 0) receiveChildInfo();\n        } else if (pid == server.module_child_pid) {\n            ModuleForkDoneHandler(exitcode,bysignal);\n            if (!bysignal && exitcode == 0) receiveChildInfo();\n        } else {\n            if (!ldbRemoveChild(pid)) {\n                serverLog(LL_WARNING,\n                    \"Warning, detected child with unmatched pid: %ld\",\n                    (long)pid);\n            }\n        }\n        updateDictResizePolicy();\n        closeChildInfoPipe();\n    }\n}\n\n/* This is our timer interrupt, called server.hz times per second.\n * Here is where we do a number of things that need to be done asynchronously.\n * For instance:\n *\n * - Active expired keys collection (it is also performed in a lazy way on\n *   lookup).\n * - Software watchdog.\n * - Update some statistic.\n * - Incremental rehashing of the DBs hash tables.\n * - Triggering BGSAVE / AOF rewrite, and handling of terminated children.\n * - Clients timeout of different kinds.\n * - Replication reconnection.\n * - Many more...\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\nint serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {\n    int j;\n    UNUSED(eventLoop);\n    UNUSED(id);\n    UNUSED(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(1);\n\n    server.hz = server.config_hz;\n    /* Adapt the server.hz value to the number of configured clients. If we have\n     * many clients, we want to call serverCron() with an higher frequency. */\n    if (server.dynamic_hz) {\n        while (listLength(server.clients) / server.hz >\n               MAX_CLIENTS_PER_CLOCK_TICK)\n        {\n            server.hz *= 2;\n            if (server.hz > CONFIG_MAX_HZ) {\n                server.hz = CONFIG_MAX_HZ;\n                break;\n            }\n        }\n    }\n\n    run_with_period(100) {\n        trackInstantaneousMetric(STATS_METRIC_COMMAND,server.stat_numcommands);\n        trackInstantaneousMetric(STATS_METRIC_NET_INPUT,\n                server.stat_net_input_bytes);\n        trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT,\n                server.stat_net_output_bytes);\n    }\n\n    /* We have just 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     * Note that you can change the resolution altering the\n     * LRU_CLOCK_RESOLUTION define. */\n    server.lruclock = getLRUClock();\n\n    /* Record the max memory used since the server was started. */\n    if (zmalloc_used_memory() > server.stat_peak_memory)\n        server.stat_peak_memory = zmalloc_used_memory();\n\n    run_with_period(100) {\n        /* Sample the RSS and other metrics here since this is a relatively slow call.\n         * We must sample the zmalloc_used at the same time we take the rss, otherwise\n         * the frag ratio calculate may be off (ratio of two samples at different times) */\n        server.cron_malloc_stats.process_rss = zmalloc_get_rss();\n        server.cron_malloc_stats.zmalloc_used = zmalloc_used_memory();\n        /* Sampling the allcator info can be slow too.\n         * The fragmentation ratio it'll show is potentically more accurate\n         * it excludes other RSS pages such as: shared libraries, LUA and other non-zmalloc\n         * allocations, and allocator reserved pages that can be pursed (all not actual frag) */\n        zmalloc_get_allocator_info(&server.cron_malloc_stats.allocator_allocated,\n                                   &server.cron_malloc_stats.allocator_active,\n                                   &server.cron_malloc_stats.allocator_resident);\n        /* in case the allocator isn't providing these stats, fake them so that\n         * fragmention info still shows some (inaccurate metrics) */\n        if (!server.cron_malloc_stats.allocator_resident) {\n            /* LUA memory isn't part of zmalloc_used, but it is part of the process RSS,\n             * so we must desuct it in order to be able to calculate correct\n             * \"allocator fragmentation\" ratio */\n            size_t lua_memory = lua_gc(server.lua,LUA_GCCOUNT,0)*1024LL;\n            server.cron_malloc_stats.allocator_resident = server.cron_malloc_stats.process_rss - lua_memory;\n        }\n        if (!server.cron_malloc_stats.allocator_active)\n            server.cron_malloc_stats.allocator_active = server.cron_malloc_stats.allocator_resident;\n        if (!server.cron_malloc_stats.allocator_allocated)\n            server.cron_malloc_stats.allocator_allocated = server.cron_malloc_stats.zmalloc_used;\n    }\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    if (server.shutdown_asap) {\n        if (prepareForShutdown(SHUTDOWN_NOFLAGS) == C_OK) exit(0);\n        serverLog(LL_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    run_with_period(5000) {\n        for (j = 0; j < server.dbnum; j++) {\n            long long size, used, vkeys;\n\n            size = dictSlots(server.db[j].dict);\n            used = dictSize(server.db[j].dict);\n            vkeys = dictSize(server.db[j].expires);\n            if (used || vkeys) {\n                serverLog(LL_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    if (!server.sentinel_mode) {\n        run_with_period(5000) {\n            serverLog(LL_DEBUG,\n                \"%lu clients connected (%lu replicas), %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    clientsCron();\n\n    /* Handle background operations on Redis databases. */\n    databasesCron();\n\n    /* Start a scheduled AOF rewrite if this was requested by the user while\n     * a BGSAVE was in progress. */\n    if (!hasActiveChildProcess() &&\n        server.aof_rewrite_scheduled)\n    {\n        rewriteAppendOnlyFileBackground();\n    }\n\n    /* Check if a background saving or AOF rewrite in progress terminated. */\n    if (hasActiveChildProcess() || ldbPendingChildren())\n    {\n        checkChildrenDone();\n    } else {\n        /* If there is not a background saving/rewrite in progress check if\n         * we have to save/rewrite now. */\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             * CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed. */\n            if (server.dirty >= sp->changes &&\n                server.unixtime-server.lastsave > sp->seconds &&\n                (server.unixtime-server.lastbgsave_try >\n                 CONFIG_BGSAVE_RETRY_DELAY ||\n                 server.lastbgsave_status == C_OK))\n            {\n                serverLog(LL_NOTICE,\"%d changes in %d seconds. Saving...\",\n                    sp->changes, (int)sp->seconds);\n                rdbSaveInfo rsi, *rsiptr;\n                rsiptr = rdbPopulateSaveInfo(&rsi);\n                rdbSaveBackground(server.rdb_filename,rsiptr);\n                break;\n            }\n        }\n\n        /* Trigger an AOF rewrite if needed. */\n        if (server.aof_state == AOF_ON &&\n            !hasActiveChildProcess() &&\n            server.aof_rewrite_perc &&\n            server.aof_current_size > server.aof_rewrite_min_size)\n        {\n            long long base = server.aof_rewrite_base_size ?\n                server.aof_rewrite_base_size : 1;\n            long long growth = (server.aof_current_size*100/base) - 100;\n            if (growth >= server.aof_rewrite_perc) {\n                serverLog(LL_NOTICE,\"Starting automatic rewriting of AOF on %lld%% growth\",growth);\n                rewriteAppendOnlyFileBackground();\n            }\n        }\n    }\n\n\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 == C_ERR)\n            flushAppendOnlyFile(0);\n    }\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,\n     * detect transfer failures, start background RDB transfers and so forth. */\n    run_with_period(1000) replicationCron();\n\n    /* Run the Redis Cluster cron. */\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    if (server.sentinel_mode) sentinelTimer();\n\n    /* Cleanup expired MIGRATE cached sockets. */\n    run_with_period(1000) {\n        migrateCloseTimedoutSockets();\n    }\n\n    /* Stop the I/O threads if we don't have enough pending work. */\n    stopThreadedIOIfNeeded();\n\n    /* Resize tracking keys table if needed. This is also done at every\n     * command execution, but we want to be sure that if the last command\n     * executed changes the value via CONFIG SET, the server will perform\n     * the operation even if completely idle. */\n    if (server.tracking_clients) trackingLimitUsedSlots();\n\n    /* Start a scheduled BGSAVE if the corresponding flag is set. This is\n     * useful when we are forced to postpone a BGSAVE because an AOF\n     * rewrite is in progress.\n     *\n     * Note: this code must be after the replicationCron() call above so\n     * make sure when refactoring this file to keep this order. This is useful\n     * because we want to give priority to RDB savings for replication. */\n    if (!hasActiveChildProcess() &&\n        server.rdb_bgsave_scheduled &&\n        (server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||\n         server.lastbgsave_status == C_OK))\n    {\n        rdbSaveInfo rsi, *rsiptr;\n        rsiptr = rdbPopulateSaveInfo(&rsi);\n        if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK)\n            server.rdb_bgsave_scheduled = 0;\n    }\n\n    /* Fire the cron loop modules event. */\n    RedisModuleCronLoopV1 ei = {REDISMODULE_CRON_LOOP_VERSION,server.hz};\n    moduleFireServerEvent(REDISMODULE_EVENT_CRON_LOOP,\n                          0,\n                          &ei);\n\n    server.cronloops++;\n    return 1000/server.hz;\n}\n\nextern int ProcessingEventsWhileBlocked;\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 *\n * Note: This function is (currently) called from two functions:\n * 1. aeMain - The main server loop\n * 2. processEventsWhileBlocked - Process clients during RDB/AOF load\n *\n * If it was called from processEventsWhileBlocked we don't want\n * to perform all actions (For example, we don't want to expire\n * keys), but we do need to perform some actions.\n *\n * The most important is freeClientsInAsyncFreeQueue but we also\n * call some other low-risk functions. */\nvoid beforeSleep(struct aeEventLoop *eventLoop) {\n    UNUSED(eventLoop);\n\n    /* Just call a subset of vital functions in case we are re-entering\n     * the event loop from processEventsWhileBlocked(). Note that in this\n     * case we keep track of the number of events we are processing, since\n     * processEventsWhileBlocked() wants to stop ASAP if there are no longer\n     * events to handle. */\n    if (ProcessingEventsWhileBlocked) {\n        uint64_t processed = 0;\n        processed += handleClientsWithPendingReadsUsingThreads();\n        processed += tlsProcessPendingData();\n        processed += handleClientsWithPendingWrites();\n        processed += freeClientsInAsyncFreeQueue();\n        server.events_processed_while_blocked += processed;\n        return;\n    }\n\n    /* Handle precise timeouts of blocked clients. */\n    handleBlockedClientsTimeout();\n\n    /* We should handle pending reads clients ASAP after event loop. */\n    handleClientsWithPendingReadsUsingThreads();\n\n    /* Handle TLS pending data. (must be done before flushAppendOnlyFile) */\n    tlsProcessPendingData();\n\n    /* If tls still has pending unread data don't sleep at all. */\n    aeSetDontWait(server.el, tlsHasPendingData());\n\n    /* Call the Redis Cluster before sleep function. Note that this function\n     * may change the state of Redis Cluster (from ok to fail or vice versa),\n     * so it's a good idea to call it before serving the unblocked clients\n     * later in this function. */\n    if (server.cluster_enabled) clusterBeforeSleep();\n\n    /* Run a fast expire cycle (the called function will return\n     * ASAP if a fast cycle is not needed). */\n    if (server.active_expire_enabled && server.masterhost == NULL)\n        activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);\n\n    /* Unblock all the clients blocked for synchronous replication\n     * in WAIT. */\n    if (listLength(server.clients_waiting_acks))\n        processClientsWaitingReplicas();\n\n    /* Check if there are clients unblocked by modules that implement\n     * blocking commands. */\n    if (moduleCount()) moduleHandleBlockedClients();\n\n    /* Try to process pending commands for clients that were just unblocked. */\n    if (listLength(server.unblocked_clients))\n        processUnblockedClients();\n\n    /* Send all the slaves an ACK request if at least one client blocked\n     * during the previous event loop iteration. Note that we do this after\n     * processUnblockedClients(), so if there are multiple pipelined WAITs\n     * and the just unblocked WAIT gets blocked again, we don't have to wait\n     * a server cron cycle in absence of other event loop events. See #6623. */\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    /* Send the invalidation messages to clients participating to the\n     * client side caching protocol in broadcasting (BCAST) mode. */\n    trackingBroadcastInvalidationMessages();\n\n    /* Write the AOF buffer on disk */\n    flushAppendOnlyFile(0);\n\n    /* Handle writes with pending output buffers. */\n    handleClientsWithPendingWritesUsingThreads();\n\n    /* Close clients that need to be closed asynchronous */\n    freeClientsInAsyncFreeQueue();\n\n    /* Before we are going to sleep, let the threads access the dataset by\n     * releasing the GIL. Redis main thread will not touch anything at this\n     * time. */\n    if (moduleCount()) moduleReleaseGIL();\n}\n\n/* This function is called immadiately after the event loop multiplexing\n * API returned, and the control is going to soon return to Redis by invoking\n * the different events callbacks. */\nvoid afterSleep(struct aeEventLoop *eventLoop) {\n    UNUSED(eventLoop);\n\n    if (!ProcessingEventsWhileBlocked) {\n        if (moduleCount()) moduleAcquireGIL();\n    }\n}\n\n/* =========================== Server initialization ======================== */\n\nvoid createSharedObjects(void) {\n    int j;\n\n    shared.crlf = createObject(OBJ_STRING,sdsnew(\"\\r\\n\"));\n    shared.ok = createObject(OBJ_STRING,sdsnew(\"+OK\\r\\n\"));\n    shared.err = createObject(OBJ_STRING,sdsnew(\"-ERR\\r\\n\"));\n    shared.emptybulk = createObject(OBJ_STRING,sdsnew(\"$0\\r\\n\\r\\n\"));\n    shared.czero = createObject(OBJ_STRING,sdsnew(\":0\\r\\n\"));\n    shared.cone = createObject(OBJ_STRING,sdsnew(\":1\\r\\n\"));\n    shared.emptyarray = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.pong = createObject(OBJ_STRING,sdsnew(\"+PONG\\r\\n\"));\n    shared.queued = createObject(OBJ_STRING,sdsnew(\"+QUEUED\\r\\n\"));\n    shared.emptyscan = createObject(OBJ_STRING,sdsnew(\"*2\\r\\n$1\\r\\n0\\r\\n*0\\r\\n\"));\n    shared.wrongtypeerr = createObject(OBJ_STRING,sdsnew(\n        \"-WRONGTYPE Operation against a key holding the wrong kind of value\\r\\n\"));\n    shared.nokeyerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR no such key\\r\\n\"));\n    shared.syntaxerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR syntax error\\r\\n\"));\n    shared.sameobjecterr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR source and destination objects are the same\\r\\n\"));\n    shared.outofrangeerr = createObject(OBJ_STRING,sdsnew(\n        \"-ERR index out of range\\r\\n\"));\n    shared.noscripterr = createObject(OBJ_STRING,sdsnew(\n        \"-NOSCRIPT No matching script. Please use EVAL.\\r\\n\"));\n    shared.loadingerr = createObject(OBJ_STRING,sdsnew(\n        \"-LOADING Redis is loading the dataset in memory\\r\\n\"));\n    shared.slowscripterr = createObject(OBJ_STRING,sdsnew(\n        \"-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\\r\\n\"));\n    shared.masterdownerr = createObject(OBJ_STRING,sdsnew(\n        \"-MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'.\\r\\n\"));\n    shared.bgsaveerr = createObject(OBJ_STRING,sdsnew(\n        \"-MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.\\r\\n\"));\n    shared.roslaveerr = createObject(OBJ_STRING,sdsnew(\n        \"-READONLY You can't write against a read only replica.\\r\\n\"));\n    shared.noautherr = createObject(OBJ_STRING,sdsnew(\n        \"-NOAUTH Authentication required.\\r\\n\"));\n    shared.oomerr = createObject(OBJ_STRING,sdsnew(\n        \"-OOM command not allowed when used memory > 'maxmemory'.\\r\\n\"));\n    shared.execaborterr = createObject(OBJ_STRING,sdsnew(\n        \"-EXECABORT Transaction discarded because of previous errors.\\r\\n\"));\n    shared.noreplicaserr = createObject(OBJ_STRING,sdsnew(\n        \"-NOREPLICAS Not enough good replicas to write.\\r\\n\"));\n    shared.busykeyerr = createObject(OBJ_STRING,sdsnew(\n        \"-BUSYKEY Target key name already exists.\\r\\n\"));\n    shared.space = createObject(OBJ_STRING,sdsnew(\" \"));\n    shared.colon = createObject(OBJ_STRING,sdsnew(\":\"));\n    shared.plus = createObject(OBJ_STRING,sdsnew(\"+\"));\n\n    /* The shared NULL depends on the protocol version. */\n    shared.null[0] = NULL;\n    shared.null[1] = NULL;\n    shared.null[2] = createObject(OBJ_STRING,sdsnew(\"$-1\\r\\n\"));\n    shared.null[3] = createObject(OBJ_STRING,sdsnew(\"_\\r\\n\"));\n\n    shared.nullarray[0] = NULL;\n    shared.nullarray[1] = NULL;\n    shared.nullarray[2] = createObject(OBJ_STRING,sdsnew(\"*-1\\r\\n\"));\n    shared.nullarray[3] = createObject(OBJ_STRING,sdsnew(\"_\\r\\n\"));\n\n    shared.emptymap[0] = NULL;\n    shared.emptymap[1] = NULL;\n    shared.emptymap[2] = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.emptymap[3] = createObject(OBJ_STRING,sdsnew(\"%0\\r\\n\"));\n\n    shared.emptyset[0] = NULL;\n    shared.emptyset[1] = NULL;\n    shared.emptyset[2] = createObject(OBJ_STRING,sdsnew(\"*0\\r\\n\"));\n    shared.emptyset[3] = createObject(OBJ_STRING,sdsnew(\"~0\\r\\n\"));\n\n    for (j = 0; j < PROTO_SHARED_SELECT_CMDS; j++) {\n        char dictid_str[64];\n        int dictid_len;\n\n        dictid_len = ll2string(dictid_str,sizeof(dictid_str),j);\n        shared.select[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\n                \"*2\\r\\n$6\\r\\nSELECT\\r\\n$%d\\r\\n%s\\r\\n\",\n                dictid_len, dictid_str));\n    }\n    shared.messagebulk = createStringObject(\"$7\\r\\nmessage\\r\\n\",13);\n    shared.pmessagebulk = createStringObject(\"$8\\r\\npmessage\\r\\n\",14);\n    shared.subscribebulk = createStringObject(\"$9\\r\\nsubscribe\\r\\n\",15);\n    shared.unsubscribebulk = createStringObject(\"$11\\r\\nunsubscribe\\r\\n\",18);\n    shared.psubscribebulk = createStringObject(\"$10\\r\\npsubscribe\\r\\n\",17);\n    shared.punsubscribebulk = createStringObject(\"$12\\r\\npunsubscribe\\r\\n\",19);\n    shared.del = createStringObject(\"DEL\",3);\n    shared.unlink = createStringObject(\"UNLINK\",6);\n    shared.rpop = createStringObject(\"RPOP\",4);\n    shared.lpop = createStringObject(\"LPOP\",4);\n    shared.lpush = createStringObject(\"LPUSH\",5);\n    shared.rpoplpush = createStringObject(\"RPOPLPUSH\",9);\n    shared.zpopmin = createStringObject(\"ZPOPMIN\",7);\n    shared.zpopmax = createStringObject(\"ZPOPMAX\",7);\n    shared.multi = createStringObject(\"MULTI\",5);\n    shared.exec = createStringObject(\"EXEC\",4);\n    for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {\n        shared.integers[j] =\n            makeObjectShared(createObject(OBJ_STRING,(void*)(long)j));\n        shared.integers[j]->encoding = OBJ_ENCODING_INT;\n    }\n    for (j = 0; j < OBJ_SHARED_BULKHDR_LEN; j++) {\n        shared.mbulkhdr[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\"*%d\\r\\n\",j));\n        shared.bulkhdr[j] = createObject(OBJ_STRING,\n            sdscatprintf(sdsempty(),\"$%d\\r\\n\",j));\n    }\n    /* The following two shared objects, minstring and maxstrings, are not\n     * actually used for their value but as a special object meaning\n     * respectively the minimum possible string and the maximum possible\n     * string in string comparisons for the ZRANGEBYLEX command. */\n    shared.minstring = sdsnew(\"minstring\");\n    shared.maxstring = sdsnew(\"maxstring\");\n}\n\nvoid initServerConfig(void) {\n    int j;\n\n    updateCachedTime(1);\n    getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE);\n    server.runid[CONFIG_RUN_ID_SIZE] = '\\0';\n    changeReplicationId();\n    clearReplicationId2();\n    server.hz = CONFIG_DEFAULT_HZ; /* Initialize it ASAP, even if it may get\n                                      updated later after loading the config.\n                                      This value may be used before the server\n                                      is initialized. */\n    server.timezone = getTimeZone(); /* Initialized by tzset(). */\n    server.configfile = NULL;\n    server.executable = NULL;\n    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;\n    server.bindaddr_count = 0;\n    server.unixsocketperm = CONFIG_DEFAULT_UNIX_SOCKET_PERM;\n    server.ipfd_count = 0;\n    server.tlsfd_count = 0;\n    server.sofd = -1;\n    server.active_expire_enabled = 1;\n    server.client_max_querybuf_len = PROTO_MAX_QUERYBUF_LEN;\n    server.saveparams = NULL;\n    server.loading = 0;\n    server.logfile = zstrdup(CONFIG_DEFAULT_LOGFILE);\n    server.aof_state = AOF_OFF;\n    server.aof_rewrite_base_size = 0;\n    server.aof_rewrite_scheduled = 0;\n    server.aof_flush_sleep = 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 = C_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.pidfile = NULL;\n    server.active_defrag_running = 0;\n    server.notify_keyspace_events = 0;\n    server.blocked_clients = 0;\n    memset(server.blocked_clients_by_type,0,\n           sizeof(server.blocked_clients_by_type));\n    server.shutdown_asap = 0;\n    server.cluster_configfile = zstrdup(CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);\n    server.cluster_module_flags = CLUSTER_MODULE_FLAG_NONE;\n    server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL);\n    server.next_client_id = 1; /* Client IDs, start from 1 .*/\n    server.loading_process_events_interval_bytes = (1024*1024*2);\n\n    server.lruclock = getLRUClock();\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    server.masterauth = NULL;\n    server.masterhost = NULL;\n    server.masterport = 6379;\n    server.master = NULL;\n    server.cached_master = NULL;\n    server.master_initial_offset = -1;\n    server.repl_state = REPL_STATE_NONE;\n    server.repl_transfer_tmpfile = NULL;\n    server.repl_transfer_fd = -1;\n    server.repl_transfer_s = NULL;\n    server.repl_syncio_timeout = CONFIG_REPL_SYNCIO_TIMEOUT;\n    server.repl_down_since = 0; /* Never connected, repl is down since EVER. */\n    server.master_repl_offset = 0;\n    server.master_repl_meaningful_offset = 0;\n\n    /* Replication partial resync backlog */\n    server.repl_backlog = NULL;\n    server.repl_backlog_histlen = 0;\n    server.repl_backlog_idx = 0;\n    server.repl_backlog_off = 0;\n    server.repl_no_slaves_since = time(NULL);\n\n    /* Client output buffer limits */\n    for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++)\n        server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];\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    /* 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    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    server.zpopminCommand = lookupCommandByCString(\"zpopmin\");\n    server.zpopmaxCommand = lookupCommandByCString(\"zpopmax\");\n    server.sremCommand = lookupCommandByCString(\"srem\");\n    server.execCommand = lookupCommandByCString(\"exec\");\n    server.expireCommand = lookupCommandByCString(\"expire\");\n    server.pexpireCommand = lookupCommandByCString(\"pexpire\");\n    server.xclaimCommand = lookupCommandByCString(\"xclaim\");\n    server.xgroupCommand = lookupCommandByCString(\"xgroup\");\n    server.rpoplpushCommand = lookupCommandByCString(\"rpoplpush\");\n\n    /* Debugging */\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    /* By default we want scripts to be always replicated by effects\n     * (single commands executed by the script), and not by sending the\n     * script to the slave / AOF. This is the new way starting from\n     * Redis 5. However it is possible to revert it via redis.conf. */\n    server.lua_always_replicate_commands = 1;\n\n    initConfigValues();\n}\n\nextern char **environ;\n\n/* Restart the server, executing the same executable that started this\n * instance, with the same arguments and configuration file.\n *\n * The function is designed to directly call execve() so that the new\n * server instance will retain the PID of the previous one.\n *\n * The list of flags, that may be bitwise ORed together, alter the\n * behavior of this function:\n *\n * RESTART_SERVER_NONE              No flags.\n * RESTART_SERVER_GRACEFULLY        Do a proper shutdown before restarting.\n * RESTART_SERVER_CONFIG_REWRITE    Rewrite the config file before restarting.\n *\n * On success the function does not return, because the process turns into\n * a different process. On error C_ERR is returned. */\nint restartServer(int flags, mstime_t delay) {\n    int j;\n\n    /* Check if we still have accesses to the executable that started this\n     * server instance. */\n    if (access(server.executable,X_OK) == -1) {\n        serverLog(LL_WARNING,\"Can't restart: this process has no \"\n                             \"permissions to execute %s\", server.executable);\n        return C_ERR;\n    }\n\n    /* Config rewriting. */\n    if (flags & RESTART_SERVER_CONFIG_REWRITE &&\n        server.configfile &&\n        rewriteConfig(server.configfile) == -1)\n    {\n        serverLog(LL_WARNING,\"Can't restart: configuration rewrite process \"\n                             \"failed\");\n        return C_ERR;\n    }\n\n    /* Perform a proper shutdown. */\n    if (flags & RESTART_SERVER_GRACEFULLY &&\n        prepareForShutdown(SHUTDOWN_NOFLAGS) != C_OK)\n    {\n        serverLog(LL_WARNING,\"Can't restart: error preparing for shutdown\");\n        return C_ERR;\n    }\n\n    /* Close all file descriptors, with the exception of stdin, stdout, strerr\n     * which are useful if we restart a Redis server which is not daemonized. */\n    for (j = 3; j < (int)server.maxclients + 1024; j++) {\n        /* Test the descriptor validity before closing it, otherwise\n         * Valgrind issues a warning on close(). */\n        if (fcntl(j,F_GETFD) != -1) close(j);\n    }\n\n    /* Execute the server with the original command line. */\n    if (delay) usleep(delay*1000);\n    zfree(server.exec_argv[0]);\n    server.exec_argv[0] = zstrdup(server.executable);\n    execve(server.executable,server.exec_argv,environ);\n\n    /* If an error occurred here, there is nothing we can do, but exit. */\n    _exit(1);\n\n    return C_ERR; /* Never reached. */\n}\n\n/* This function will try to raise the max number of open files accordingly to\n * the configured max number of clients. It also reserves a number of file\n * descriptors (CONFIG_MIN_RESERVED_FDS) for extra operations of\n * persistence, listening sockets, log files and so forth.\n *\n * If it will not be possible to set the limit accordingly to the configured\n * max number of clients, the function will do the reverse setting\n * server.maxclients to the value that we can actually handle. */\nvoid adjustOpenFilesLimit(void) {\n    rlim_t maxfiles = server.maxclients+CONFIG_MIN_RESERVED_FDS;\n    struct rlimit limit;\n\n    if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {\n        serverLog(LL_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-CONFIG_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 bestlimit;\n            int setrlimit_error = 0;\n\n            /* Try to set the file limit to match 'maxfiles' or at least\n             * to the higher value supported less than maxfiles. */\n            bestlimit = maxfiles;\n            while(bestlimit > oldlimit) {\n                rlim_t decr_step = 16;\n\n                limit.rlim_cur = bestlimit;\n                limit.rlim_max = bestlimit;\n                if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;\n                setrlimit_error = errno;\n\n                /* We failed to set file limit to 'bestlimit'. Try with a\n                 * smaller limit decrementing by a few FDs per iteration. */\n                if (bestlimit < decr_step) break;\n                bestlimit -= decr_step;\n            }\n\n            /* Assume that the limit we get initially is still valid if\n             * our last try was even lower. */\n            if (bestlimit < oldlimit) bestlimit = oldlimit;\n\n            if (bestlimit < maxfiles) {\n                unsigned int old_maxclients = server.maxclients;\n                server.maxclients = bestlimit-CONFIG_MIN_RESERVED_FDS;\n                /* maxclients is unsigned so may overflow: in order\n                 * to check if maxclients is now logically less than 1\n                 * we test indirectly via bestlimit. */\n                if (bestlimit <= CONFIG_MIN_RESERVED_FDS) {\n                    serverLog(LL_WARNING,\"Your current 'ulimit -n' \"\n                        \"of %llu is not enough for the server to start. \"\n                        \"Please increase your open file limit to at least \"\n                        \"%llu. Exiting.\",\n                        (unsigned long long) oldlimit,\n                        (unsigned long long) maxfiles);\n                    exit(1);\n                }\n                serverLog(LL_WARNING,\"You requested maxclients of %d \"\n                    \"requiring at least %llu max file descriptors.\",\n                    old_maxclients,\n                    (unsigned long long) maxfiles);\n                serverLog(LL_WARNING,\"Server can't set maximum open files \"\n                    \"to %llu because of OS error: %s.\",\n                    (unsigned long long) maxfiles, strerror(setrlimit_error));\n                serverLog(LL_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) bestlimit, server.maxclients);\n            } else {\n                serverLog(LL_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/* Check that server.tcp_backlog can be actually enforced in Linux according\n * to the value of /proc/sys/net/core/somaxconn, or warn about it. */\nvoid checkTcpBacklogSettings(void) {\n#ifdef HAVE_PROC_SOMAXCONN\n    FILE *fp = fopen(\"/proc/sys/net/core/somaxconn\",\"r\");\n    char buf[1024];\n    if (!fp) return;\n    if (fgets(buf,sizeof(buf),fp) != NULL) {\n        int somaxconn = atoi(buf);\n        if (somaxconn > 0 && somaxconn < server.tcp_backlog) {\n            serverLog(LL_WARNING,\"WARNING: The TCP backlog setting of %d cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of %d.\", server.tcp_backlog, somaxconn);\n        }\n    }\n    fclose(fp);\n#endif\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 C_OK.\n *\n * On error the function returns C_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            int unsupported = 0;\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            } else if (errno == EAFNOSUPPORT) {\n                unsupported++;\n                serverLog(LL_WARNING,\"Not listening to IPv6: unsupported\");\n            }\n\n            if (*count == 1 || unsupported) {\n                /* Bind the IPv4 address as well. */\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                } else if (errno == EAFNOSUPPORT) {\n                    unsupported++;\n                    serverLog(LL_WARNING,\"Not listening to IPv4: unsupported\");\n                }\n            }\n            /* Exit the loop if we were able to bind * on IPv4 and 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 + unsupported == 2) 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            serverLog(LL_WARNING,\n                \"Could not create server TCP listening socket %s:%d: %s\",\n                server.bindaddr[j] ? server.bindaddr[j] : \"*\",\n                port, server.neterr);\n                if (errno == ENOPROTOOPT     || errno == EPROTONOSUPPORT ||\n                    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||\n                    errno == EAFNOSUPPORT    || errno == EADDRNOTAVAIL)\n                    continue;\n            return C_ERR;\n        }\n        anetNonBlock(NULL,fds[*count]);\n        (*count)++;\n    }\n    return C_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    int j;\n\n    server.stat_numcommands = 0;\n    server.stat_numconnections = 0;\n    server.stat_expiredkeys = 0;\n    server.stat_expired_stale_perc = 0;\n    server.stat_expired_time_cap_reached_count = 0;\n    server.stat_expire_cycle_time_used = 0;\n    server.stat_evictedkeys = 0;\n    server.stat_keyspace_misses = 0;\n    server.stat_keyspace_hits = 0;\n    server.stat_active_defrag_hits = 0;\n    server.stat_active_defrag_misses = 0;\n    server.stat_active_defrag_key_hits = 0;\n    server.stat_active_defrag_key_misses = 0;\n    server.stat_active_defrag_scanned = 0;\n    server.stat_fork_time = 0;\n    server.stat_fork_rate = 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    for (j = 0; j < STATS_METRIC_COUNT; j++) {\n        server.inst_metric[j].idx = 0;\n        server.inst_metric[j].last_sample_time = mstime();\n        server.inst_metric[j].last_sample_count = 0;\n        memset(server.inst_metric[j].samples,0,\n            sizeof(server.inst_metric[j].samples));\n    }\n    server.stat_net_input_bytes = 0;\n    server.stat_net_output_bytes = 0;\n    server.stat_unexpected_error_replies = 0;\n    server.aof_delayed_fsync = 0;\n}\n\nvoid initServer(void) {\n    int j;\n\n    signal(SIGHUP, SIG_IGN);\n    signal(SIGPIPE, SIG_IGN);\n    setupSignalHandlers();\n\n    if (server.syslog_enabled) {\n        openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,\n            server.syslog_facility);\n    }\n\n    /* Initialization after setting defaults from the config system. */\n    server.aof_state = server.aof_enabled ? AOF_ON : AOF_OFF;\n    server.hz = server.config_hz;\n    server.pid = getpid();\n    server.current_client = NULL;\n    server.fixed_time_expire = 0;\n    server.clients = listCreate();\n    server.clients_index = raxNew();\n    server.clients_to_close = listCreate();\n    server.slaves = listCreate();\n    server.monitors = listCreate();\n    server.clients_pending_write = listCreate();\n    server.clients_pending_read = listCreate();\n    server.clients_timeout_table = raxNew();\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    server.events_processed_while_blocked = 0;\n    server.system_memory_size = zmalloc_get_memory_size();\n\n    if (server.tls_port && tlsConfigure(&server.tls_ctx_config) == C_ERR) {\n        serverLog(LL_WARNING, \"Failed to configure TLS. Check logs for more info.\");\n        exit(1);\n    }\n\n    createSharedObjects();\n    adjustOpenFilesLimit();\n    server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);\n    if (server.el == NULL) {\n        serverLog(LL_WARNING,\n            \"Failed creating the event loop. Error message: '%s'\",\n            strerror(errno));\n        exit(1);\n    }\n    server.db = zmalloc(sizeof(redisDb)*server.dbnum);\n\n    /* Open the TCP listening socket for the user commands. */\n    if (server.port != 0 &&\n        listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)\n        exit(1);\n    if (server.tls_port != 0 &&\n        listenToPort(server.tls_port,server.tlsfd,&server.tlsfd_count) == C_ERR)\n        exit(1);\n\n    /* Open the listening Unix domain socket. */\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            serverLog(LL_WARNING, \"Opening Unix 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.tlsfd_count == 0 && server.sofd < 0) {\n        serverLog(LL_WARNING, \"Configured to not listen anywhere, exiting.\");\n        exit(1);\n    }\n\n    /* Create the Redis databases, and initialize other internal state. */\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].expires_cursor = 0;\n        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);\n        server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);\n        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);\n        server.db[j].id = j;\n        server.db[j].avg_ttl = 0;\n        server.db[j].defrag_later = listCreate();\n        listSetFreeMethod(server.db[j].defrag_later,(void (*)(void*))sdsfree);\n    }\n    evictionPoolAlloc(); /* Initialize the LRU keys pool. */\n    server.pubsub_channels = dictCreate(&keylistDictType,NULL);\n    server.pubsub_patterns = listCreate();\n    server.pubsub_patterns_dict = dictCreate(&keylistDictType,NULL);\n    listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);\n    listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);\n    server.cronloops = 0;\n    server.rdb_child_pid = -1;\n    server.aof_child_pid = -1;\n    server.module_child_pid = -1;\n    server.rdb_child_type = RDB_CHILD_TYPE_NONE;\n    server.rdb_pipe_conns = NULL;\n    server.rdb_pipe_numconns = 0;\n    server.rdb_pipe_numconns_writing = 0;\n    server.rdb_pipe_buff = NULL;\n    server.rdb_pipe_bufflen = 0;\n    server.rdb_bgsave_scheduled = 0;\n    server.child_info_pipe[0] = -1;\n    server.child_info_pipe[1] = -1;\n    server.child_info_data.magic = 0;\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.stat_rdb_cow_bytes = 0;\n    server.stat_aof_cow_bytes = 0;\n    server.stat_module_cow_bytes = 0;\n    for (int j = 0; j < CLIENT_TYPE_COUNT; j++)\n        server.stat_clients_type_memory[j] = 0;\n    server.cron_malloc_stats.zmalloc_used = 0;\n    server.cron_malloc_stats.process_rss = 0;\n    server.cron_malloc_stats.allocator_allocated = 0;\n    server.cron_malloc_stats.allocator_active = 0;\n    server.cron_malloc_stats.allocator_resident = 0;\n    server.lastbgsave_status = C_OK;\n    server.aof_last_write_status = C_OK;\n    server.aof_last_write_errno = 0;\n    server.repl_good_slaves_count = 0;\n\n    /* Create the timer callback, this is our way to process many background\n     * operations incrementally, like clients timeout, eviction of unaccessed\n     * expired keys and so forth. */\n    if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {\n        serverPanic(\"Can't create event loop timers.\");\n        exit(1);\n    }\n\n    /* Create an event handler for accepting new connections in TCP and Unix\n     * domain sockets. */\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                serverPanic(\n                    \"Unrecoverable error creating server.ipfd file event.\");\n            }\n    }\n    for (j = 0; j < server.tlsfd_count; j++) {\n        if (aeCreateFileEvent(server.el, server.tlsfd[j], AE_READABLE,\n            acceptTLSHandler,NULL) == AE_ERR)\n            {\n                serverPanic(\n                    \"Unrecoverable error creating server.tlsfd file event.\");\n            }\n    }\n    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,\n        acceptUnixHandler,NULL) == AE_ERR) serverPanic(\"Unrecoverable error creating server.sofd file event.\");\n\n\n    /* Register a readable event for the pipe used to awake the event loop\n     * when a blocked client in a module needs attention. */\n    if (aeCreateFileEvent(server.el, server.module_blocked_pipe[0], AE_READABLE,\n        moduleBlockedClientPipeReadable,NULL) == AE_ERR) {\n            serverPanic(\n                \"Error registering the readable event for the module \"\n                \"blocked clients subsystem.\");\n    }\n\n    /* Register before and after sleep handlers (note this needs to be done\n     * before loading persistence since it is used by processEventsWhileBlocked. */\n    aeSetBeforeSleepProc(server.el,beforeSleep);\n    aeSetAfterSleepProc(server.el,afterSleep);\n\n    /* Open the AOF file if needed. */\n    if (server.aof_state == 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            serverLog(LL_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    if (server.arch_bits == 32 && server.maxmemory == 0) {\n        serverLog(LL_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 = MAXMEMORY_NO_EVICTION;\n    }\n\n    if (server.cluster_enabled) clusterInit();\n    replicationScriptCacheInit();\n    scriptingInit(1);\n    slowlogInit();\n    latencyMonitorInit();\n}\n\n/* Some steps in server initialization need to be done last (after modules\n * are loaded).\n * Specifically, creation of threads due to a race bug in ld.so, in which\n * Thread Local Storage initialization collides with dlopen call.\n * see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */\nvoid InitServerLast() {\n    bioInit();\n    initThreadedIO();\n    set_jemalloc_bg_thread(server.jemalloc_bg_thread);\n    server.initial_memory_usage = zmalloc_used_memory();\n}\n\n/* Parse the flags string description 'strflags' and set them to the\n * command 'c'. If the flags are all valid C_OK is returned, otherwise\n * C_ERR is returned (yet the recognized flags are set in the command). */\nint populateCommandTableParseFlags(struct redisCommand *c, char *strflags) {\n    int argc;\n    sds *argv;\n\n    /* Split the line into arguments for processing. */\n    argv = sdssplitargs(strflags,&argc);\n    if (argv == NULL) return C_ERR;\n\n    for (int j = 0; j < argc; j++) {\n        char *flag = argv[j];\n        if (!strcasecmp(flag,\"write\")) {\n            c->flags |= CMD_WRITE|CMD_CATEGORY_WRITE;\n        } else if (!strcasecmp(flag,\"read-only\")) {\n            c->flags |= CMD_READONLY|CMD_CATEGORY_READ;\n        } else if (!strcasecmp(flag,\"use-memory\")) {\n            c->flags |= CMD_DENYOOM;\n        } else if (!strcasecmp(flag,\"admin\")) {\n            c->flags |= CMD_ADMIN|CMD_CATEGORY_ADMIN|CMD_CATEGORY_DANGEROUS;\n        } else if (!strcasecmp(flag,\"pub-sub\")) {\n            c->flags |= CMD_PUBSUB|CMD_CATEGORY_PUBSUB;\n        } else if (!strcasecmp(flag,\"no-script\")) {\n            c->flags |= CMD_NOSCRIPT;\n        } else if (!strcasecmp(flag,\"random\")) {\n            c->flags |= CMD_RANDOM;\n        } else if (!strcasecmp(flag,\"to-sort\")) {\n            c->flags |= CMD_SORT_FOR_SCRIPT;\n        } else if (!strcasecmp(flag,\"ok-loading\")) {\n            c->flags |= CMD_LOADING;\n        } else if (!strcasecmp(flag,\"ok-stale\")) {\n            c->flags |= CMD_STALE;\n        } else if (!strcasecmp(flag,\"no-monitor\")) {\n            c->flags |= CMD_SKIP_MONITOR;\n        } else if (!strcasecmp(flag,\"no-slowlog\")) {\n            c->flags |= CMD_SKIP_SLOWLOG;\n        } else if (!strcasecmp(flag,\"cluster-asking\")) {\n            c->flags |= CMD_ASKING;\n        } else if (!strcasecmp(flag,\"fast\")) {\n            c->flags |= CMD_FAST | CMD_CATEGORY_FAST;\n        } else if (!strcasecmp(flag,\"no-auth\")) {\n            c->flags |= CMD_NO_AUTH;\n        } else {\n            /* Parse ACL categories here if the flag name starts with @. */\n            uint64_t catflag;\n            if (flag[0] == '@' &&\n                (catflag = ACLGetCommandCategoryFlagByName(flag+1)) != 0)\n            {\n                c->flags |= catflag;\n            } else {\n                sdsfreesplitres(argv,argc);\n                return C_ERR;\n            }\n        }\n    }\n    /* If it's not @fast is @slow in this binary world. */\n    if (!(c->flags & CMD_CATEGORY_FAST)) c->flags |= CMD_CATEGORY_SLOW;\n\n    sdsfreesplitres(argv,argc);\n    return C_OK;\n}\n\n/* Populates the Redis Command Table starting from the hard coded list\n * we have on top of redis.c file. */\nvoid populateCommandTable(void) {\n    int j;\n    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);\n\n    for (j = 0; j < numcommands; j++) {\n        struct redisCommand *c = redisCommandTable+j;\n        int retval1, retval2;\n\n        /* Translate the command string flags description into an actual\n         * set of flags. */\n        if (populateCommandTableParseFlags(c,c->sflags) == C_ERR)\n            serverPanic(\"Unsupported command flag\");\n\n        c->id = ACLGetCommandID(c->name); /* Assign the ID used for ACL. */\n        retval1 = dictAdd(server.commands, sdsnew(c->name), c);\n        /* Populate an additional dictionary that will be unaffected\n         * by rename-command statements in redis.conf. */\n        retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);\n        serverAssert(retval1 == DICT_OK && retval2 == DICT_OK);\n    }\n}\n\nvoid resetCommandTableStats(void) {\n    struct redisCommand *c;\n    dictEntry *de;\n    dictIterator *di;\n\n    di = dictGetSafeIterator(server.commands);\n    while((de = dictNext(di)) != NULL) {\n        c = (struct redisCommand *) dictGetVal(de);\n        c->microseconds = 0;\n        c->calls = 0;\n    }\n    dictReleaseIterator(di);\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\nstruct redisCommand *lookupCommand(sds name) {\n    return dictFetchValue(server.commands, name);\n}\n\nstruct redisCommand *lookupCommandByCString(char *s) {\n    struct redisCommand *cmd;\n    sds name = sdsnew(s);\n\n    cmd = dictFetchValue(server.commands, name);\n    sdsfree(name);\n    return cmd;\n}\n\n/* Lookup the command in the current table, if not found also check in\n * the original table containing the original command names unaffected by\n * redis.conf rename-command statement.\n *\n * This is used by functions rewriting the argument vector such as\n * rewriteClientCommandVector() in order to set client->cmd pointer\n * correctly even if the command was renamed. */\nstruct redisCommand *lookupCommandOrOriginal(sds name) {\n    struct redisCommand *cmd = dictFetchValue(server.commands, name);\n\n    if (!cmd) cmd = dictFetchValue(server.orig_commands,name);\n    return cmd;\n}\n\n/* Propagate the specified command (in the context of the specified database id)\n * to AOF and Slaves.\n *\n * flags are an xor between:\n * + PROPAGATE_NONE (no propagation of command at all)\n * + PROPAGATE_AOF (propagate into the AOF file if is enabled)\n * + PROPAGATE_REPL (propagate into the replication link)\n *\n * This should not be used inside commands implementation since it will not\n * wrap the resulting commands in MULTI/EXEC. Use instead alsoPropagate(),\n * preventCommandPropagation(), forceCommandPropagation().\n *\n * However for functions that need to (also) propagate out of the context of a\n * command execution, for example when serving a blocked client, you\n * want to use propagate().\n */\nvoid propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n               int flags)\n{\n    if (server.aof_state != AOF_OFF && flags & PROPAGATE_AOF)\n        feedAppendOnlyFile(cmd,dbid,argv,argc);\n    if (flags & PROPAGATE_REPL)\n        replicationFeedSlaves(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.\n *\n * 'cmd' must be a pointer to the Redis command to replicate, dbid is the\n * database ID the command should be propagated into.\n * Arguments of the command to propagte are passed as an array of redis\n * objects pointers of len 'argc', using the 'argv' vector.\n *\n * The function does not take a reference to the passed 'argv' vector,\n * so it is up to the caller to release the passed argv (but it is usually\n * stack allocated).  The function autoamtically increments ref count of\n * passed objects, so the caller does not need to. */\nvoid alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,\n                   int target)\n{\n    robj **argvcopy;\n    int j;\n\n    if (server.loading) return; /* No propagation during loading. */\n\n    argvcopy = zmalloc(sizeof(robj*)*argc);\n    for (j = 0; j < argc; j++) {\n        argvcopy[j] = argv[j];\n        incrRefCount(argv[j]);\n    }\n    redisOpArrayAppend(&server.also_propagate,cmd,dbid,argvcopy,argc,target);\n}\n\n/* It is possible to call the function forceCommandPropagation() inside a\n * Redis command implementation in order to to force the propagation of a\n * specific command execution into AOF / Replication. */\nvoid forceCommandPropagation(client *c, int flags) {\n    if (flags & PROPAGATE_REPL) c->flags |= CLIENT_FORCE_REPL;\n    if (flags & PROPAGATE_AOF) c->flags |= CLIENT_FORCE_AOF;\n}\n\n/* Avoid that the executed command is propagated at all. This way we\n * are free to just propagate what we want using the alsoPropagate()\n * API. */\nvoid preventCommandPropagation(client *c) {\n    c->flags |= CLIENT_PREVENT_PROP;\n}\n\n/* AOF specific version of preventCommandPropagation(). */\nvoid preventCommandAOF(client *c) {\n    c->flags |= CLIENT_PREVENT_AOF_PROP;\n}\n\n/* Replication specific version of preventCommandPropagation(). */\nvoid preventCommandReplication(client *c) {\n    c->flags |= CLIENT_PREVENT_REPL_PROP;\n}\n\n/* Call() is the core of Redis execution of a command.\n *\n * The following flags can be passed:\n * CMD_CALL_NONE        No flags.\n * CMD_CALL_SLOWLOG     Check command speed and log in the slow log if needed.\n * CMD_CALL_STATS       Populate command stats.\n * CMD_CALL_PROPAGATE_AOF   Append command to AOF if it modified the dataset\n *                          or if the client flags are forcing propagation.\n * CMD_CALL_PROPAGATE_REPL  Send command to slaves if it modified the dataset\n *                          or if the client flags are forcing propagation.\n * CMD_CALL_PROPAGATE   Alias for PROPAGATE_AOF|PROPAGATE_REPL.\n * CMD_CALL_FULL        Alias for SLOWLOG|STATS|PROPAGATE.\n *\n * The exact propagation behavior depends on the client flags.\n * Specifically:\n *\n * 1. If the client flags CLIENT_FORCE_AOF or CLIENT_FORCE_REPL are set\n *    and assuming the corresponding CMD_CALL_PROPAGATE_AOF/REPL is set\n *    in the call flags, then the command is propagated even if the\n *    dataset was not affected by the command.\n * 2. If the client flags CLIENT_PREVENT_REPL_PROP or CLIENT_PREVENT_AOF_PROP\n *    are set, the propagation into AOF or to slaves is not performed even\n *    if the command modified the dataset.\n *\n * Note that regardless of the client flags, if CMD_CALL_PROPAGATE_AOF\n * or CMD_CALL_PROPAGATE_REPL are not set, then respectively AOF or\n * slaves propagation will never occur.\n *\n * Client flags are modified by the implementation of a given command\n * using the following API:\n *\n * forceCommandPropagation(client *c, int flags);\n * preventCommandPropagation(client *c);\n * preventCommandAOF(client *c);\n * preventCommandReplication(client *c);\n *\n */\nvoid call(client *c, int flags) {\n    long long dirty;\n    ustime_t start, duration;\n    int client_old_flags = c->flags;\n    struct redisCommand *real_cmd = c->cmd;\n\n    server.fixed_time_expire++;\n\n    /* Send the command to clients in MONITOR mode if applicable.\n     * Administrative commands are considered too dangerous to be shown. */\n    if (listLength(server.monitors) &&\n        !server.loading &&\n        !(c->cmd->flags & (CMD_SKIP_MONITOR|CMD_ADMIN)))\n    {\n        replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);\n    }\n\n    /* Initialization: clear the flags that must be set by the command on\n     * demand, and initialize the array for additional commands propagation. */\n    c->flags &= ~(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n    redisOpArray prev_also_propagate = server.also_propagate;\n    redisOpArrayInit(&server.also_propagate);\n\n    /* Call the command. */\n    dirty = server.dirty;\n    updateCachedTime(0);\n    start = server.ustime;\n    c->cmd->proc(c);\n    duration = ustime()-start;\n    dirty = server.dirty-dirty;\n    if (dirty < 0) dirty = 0;\n\n    /* When EVAL is called loading the AOF we don't want commands called\n     * from Lua to go into the slowlog or to populate statistics. */\n    if (server.loading && c->flags & CLIENT_LUA)\n        flags &= ~(CMD_CALL_SLOWLOG | CMD_CALL_STATS);\n\n    /* If the caller is Lua, we want to force the EVAL caller to propagate\n     * the script if the command flag or client flag are forcing the\n     * propagation. */\n    if (c->flags & CLIENT_LUA && server.lua_caller) {\n        if (c->flags & CLIENT_FORCE_REPL)\n            server.lua_caller->flags |= CLIENT_FORCE_REPL;\n        if (c->flags & CLIENT_FORCE_AOF)\n            server.lua_caller->flags |= CLIENT_FORCE_AOF;\n    }\n\n    /* Log the command into the Slow log if needed, and populate the\n     * per-command statistics that we show in INFO commandstats. */\n    if (flags & CMD_CALL_SLOWLOG && !(c->cmd->flags & CMD_SKIP_SLOWLOG)) {\n        char *latency_event = (c->cmd->flags & CMD_FAST) ?\n                              \"fast-command\" : \"command\";\n        latencyAddSampleIfNeeded(latency_event,duration/1000);\n        slowlogPushEntryIfNeeded(c,c->argv,c->argc,duration);\n    }\n\n    if (flags & CMD_CALL_STATS) {\n        /* use the real command that was executed (cmd and lastamc) may be\n         * different, in case of MULTI-EXEC or re-written commands such as\n         * EXPIRE, GEOADD, etc. */\n        real_cmd->microseconds += duration;\n        real_cmd->calls++;\n    }\n\n    /* Propagate the command into the AOF and replication link */\n    if (flags & CMD_CALL_PROPAGATE &&\n        (c->flags & CLIENT_PREVENT_PROP) != CLIENT_PREVENT_PROP)\n    {\n        int propagate_flags = PROPAGATE_NONE;\n\n        /* Check if the command operated changes in the data set. If so\n         * set for replication / AOF propagation. */\n        if (dirty) propagate_flags |= (PROPAGATE_AOF|PROPAGATE_REPL);\n\n        /* If the client forced AOF / replication of the command, set\n         * the flags regardless of the command effects on the data set. */\n        if (c->flags & CLIENT_FORCE_REPL) propagate_flags |= PROPAGATE_REPL;\n        if (c->flags & CLIENT_FORCE_AOF) propagate_flags |= PROPAGATE_AOF;\n\n        /* However prevent AOF / replication propagation if the command\n         * implementations called preventCommandPropagation() or similar,\n         * or if we don't have the call() flags to do so. */\n        if (c->flags & CLIENT_PREVENT_REPL_PROP ||\n            !(flags & CMD_CALL_PROPAGATE_REPL))\n                propagate_flags &= ~PROPAGATE_REPL;\n        if (c->flags & CLIENT_PREVENT_AOF_PROP ||\n            !(flags & CMD_CALL_PROPAGATE_AOF))\n                propagate_flags &= ~PROPAGATE_AOF;\n\n        /* Call propagate() only if at least one of AOF / replication\n         * propagation is needed. Note that modules commands handle replication\n         * in an explicit way, so we never replicate them automatically. */\n        if (propagate_flags != PROPAGATE_NONE && !(c->cmd->flags & CMD_MODULE))\n            propagate(c->cmd,c->db->id,c->argv,c->argc,propagate_flags);\n    }\n\n    /* Restore the old replication flags, since call() can be executed\n     * recursively. */\n    c->flags &= ~(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n    c->flags |= client_old_flags &\n        (CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);\n\n    /* Handle the alsoPropagate() API to handle commands that want to propagate\n     * multiple separated commands. Note that alsoPropagate() is not affected\n     * by CLIENT_PREVENT_PROP flag. */\n    if (server.also_propagate.numops) {\n        int j;\n        redisOp *rop;\n\n        if (flags & CMD_CALL_PROPAGATE) {\n            int multi_emitted = 0;\n            /* Wrap the commands in server.also_propagate array,\n             * but don't wrap it if we are already in MULTI context,\n             * in case the nested MULTI/EXEC.\n             *\n             * And if the array contains only one command, no need to\n             * wrap it, since the single command is atomic. */\n            if (server.also_propagate.numops > 1 &&\n                !(c->cmd->flags & CMD_MODULE) &&\n                !(c->flags & CLIENT_MULTI) &&\n                !(flags & CMD_CALL_NOWRAP))\n            {\n                execCommandPropagateMulti(c);\n                multi_emitted = 1;\n            }\n\n            for (j = 0; j < server.also_propagate.numops; j++) {\n                rop = &server.also_propagate.ops[j];\n                int target = rop->target;\n                /* Whatever the command wish is, we honor the call() flags. */\n                if (!(flags&CMD_CALL_PROPAGATE_AOF)) target &= ~PROPAGATE_AOF;\n                if (!(flags&CMD_CALL_PROPAGATE_REPL)) target &= ~PROPAGATE_REPL;\n                if (target)\n                    propagate(rop->cmd,rop->dbid,rop->argv,rop->argc,target);\n            }\n\n            if (multi_emitted) {\n                execCommandPropagateExec(c);\n            }\n        }\n        redisOpArrayFree(&server.also_propagate);\n    }\n    server.also_propagate = prev_also_propagate;\n\n    /* If the client has keys tracking enabled for client side caching,\n     * make sure to remember the keys it fetched via this command. */\n    if (c->cmd->flags & CMD_READONLY) {\n        client *caller = (c->flags & CLIENT_LUA && server.lua_caller) ?\n                            server.lua_caller : c;\n        if (caller->flags & CLIENT_TRACKING &&\n            !(caller->flags & CLIENT_TRACKING_BCAST))\n        {\n            trackingRememberKeys(caller);\n        }\n    }\n\n    server.fixed_time_expire--;\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 * If C_OK is returned the client is still alive and valid and\n * other operations can be performed by the caller. Otherwise\n * if C_ERR is returned the client was destroyed (i.e. after QUIT). */\nint processCommand(client *c) {\n    moduleCallCommandFilters(c);\n\n    /* The QUIT command is handled separately. Normal command procs will\n     * go through checking for replication and QUIT will cause trouble\n     * when FORCE_REPLICATION is enabled and would be implemented in\n     * a regular command proc. */\n    if (!strcasecmp(c->argv[0]->ptr,\"quit\")) {\n        addReply(c,shared.ok);\n        c->flags |= CLIENT_CLOSE_AFTER_REPLY;\n        return C_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    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);\n    if (!c->cmd) {\n        flagTransaction(c);\n        sds args = sdsempty();\n        int i;\n        for (i=1; i < c->argc && sdslen(args) < 128; i++)\n            args = sdscatprintf(args, \"`%.*s`, \", 128-(int)sdslen(args), (char*)c->argv[i]->ptr);\n        addReplyErrorFormat(c,\"unknown command `%s`, with args beginning with: %s\",\n            (char*)c->argv[0]->ptr, args);\n        sdsfree(args);\n        return C_OK;\n    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||\n               (c->argc < -c->cmd->arity)) {\n        flagTransaction(c);\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return C_OK;\n    }\n\n    /* Check if the user is authenticated. This check is skipped in case\n     * the default user is flagged as \"nopass\" and is active. */\n    int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) ||\n                          (DefaultUser->flags & USER_FLAG_DISABLED)) &&\n                        !c->authenticated;\n    if (auth_required) {\n        /* AUTH and HELLO and no auth modules are valid even in\n         * non-authenticated state. */\n        if (!(c->cmd->flags & CMD_NO_AUTH)) {\n            flagTransaction(c);\n            addReply(c,shared.noautherr);\n            return C_OK;\n        }\n    }\n\n    /* Check if the user can run this command according to the current\n     * ACLs. */\n    int acl_keypos;\n    int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);\n    if (acl_retval != ACL_OK) {\n        addACLLogEntry(c,acl_retval,acl_keypos,NULL);\n        flagTransaction(c);\n        if (acl_retval == ACL_DENIED_CMD)\n            addReplyErrorFormat(c,\n                \"-NOPERM this user has no permissions to run \"\n                \"the '%s' command or its subcommand\", c->cmd->name);\n        else\n            addReplyErrorFormat(c,\n                \"-NOPERM this user has no permissions to access \"\n                \"one of the keys used as arguments\");\n        return C_OK;\n    }\n\n    /* If cluster is enabled perform the cluster redirection here.\n     * However we don't perform the redirection if:\n     * 1) The sender of this command is our master.\n     * 2) The command has no key arguments. */\n    if (server.cluster_enabled &&\n        !(c->flags & CLIENT_MASTER) &&\n        !(c->flags & CLIENT_LUA &&\n          server.lua_caller->flags & CLIENT_MASTER) &&\n        !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0 &&\n          c->cmd->proc != execCommand))\n    {\n        int hashslot;\n        int error_code;\n        clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,\n                                        &hashslot,&error_code);\n        if (n == NULL || n != server.cluster->myself) {\n            if (c->cmd->proc == execCommand) {\n                discardTransaction(c);\n            } else {\n                flagTransaction(c);\n            }\n            clusterRedirectClient(c,n,hashslot,error_code);\n            return C_OK;\n        }\n    }\n\n    /* Handle the maxmemory directive.\n     *\n     * Note that we do not want to reclaim memory if we are here re-entering\n     * the event loop since there is a busy Lua script running in timeout\n     * condition, to avoid mixing the propagation of scripts with the\n     * propagation of DELs due to eviction. */\n    if (server.maxmemory && !server.lua_timedout) {\n        int out_of_memory = freeMemoryIfNeededAndSafe() == C_ERR;\n        /* freeMemoryIfNeeded may flush slave output buffers. This may result\n         * into a slave, that may be the active client, to be freed. */\n        if (server.current_client == NULL) return C_ERR;\n\n        /* It was impossible to free enough memory, and the command the client\n         * is trying to execute is denied during OOM conditions or the client\n         * is in MULTI/EXEC context? Error. */\n        if (out_of_memory &&\n            (c->cmd->flags & CMD_DENYOOM ||\n             (c->flags & CLIENT_MULTI &&\n              c->cmd->proc != execCommand &&\n              c->cmd->proc != discardCommand)))\n        {\n            flagTransaction(c);\n            addReply(c, shared.oomerr);\n            return C_OK;\n        }\n\n        /* Save out_of_memory result at script start, otherwise if we check OOM\n         * untill first write within script, memory used by lua stack and\n         * arguments might interfere. */\n        if (c->cmd->proc == evalCommand || c->cmd->proc == evalShaCommand) {\n            server.lua_oom = out_of_memory;\n        }\n    }\n\n    /* Make sure to use a reasonable amount of memory for client side\n     * caching metadata. */\n    if (server.tracking_clients) trackingLimitUsedSlots();\n\n    /* Don't accept write commands if there are problems persisting on disk\n     * and if this is a master instance. */\n    int deny_write_type = writeCommandsDeniedByDiskError();\n    if (deny_write_type != DISK_ERROR_TYPE_NONE &&\n        server.masterhost == NULL &&\n        (c->cmd->flags & CMD_WRITE ||\n         c->cmd->proc == pingCommand))\n    {\n        flagTransaction(c);\n        if (deny_write_type == DISK_ERROR_TYPE_RDB)\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 C_OK;\n    }\n\n    /* Don't accept write commands if there are not enough good slaves and\n     * user configured the min-slaves-to-write option. */\n    if (server.masterhost == NULL &&\n        server.repl_min_slaves_to_write &&\n        server.repl_min_slaves_max_lag &&\n        c->cmd->flags & 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 C_OK;\n    }\n\n    /* Don't accept write commands if this is a read only slave. But\n     * accept write commands if this is our master. */\n    if (server.masterhost && server.repl_slave_ro &&\n        !(c->flags & CLIENT_MASTER) &&\n        c->cmd->flags & CMD_WRITE)\n    {\n        flagTransaction(c);\n        addReply(c, shared.roslaveerr);\n        return C_OK;\n    }\n\n    /* Only allow a subset of commands in the context of Pub/Sub if the\n     * connection is in RESP2 mode. With RESP3 there are no limits. */\n    if ((c->flags & CLIENT_PUBSUB && c->resp == 2) &&\n        c->cmd->proc != pingCommand &&\n        c->cmd->proc != subscribeCommand &&\n        c->cmd->proc != unsubscribeCommand &&\n        c->cmd->proc != psubscribeCommand &&\n        c->cmd->proc != punsubscribeCommand) {\n        addReplyErrorFormat(c,\n            \"Can't execute '%s': only (P)SUBSCRIBE / \"\n            \"(P)UNSUBSCRIBE / PING / QUIT are allowed in this context\",\n            c->cmd->name);\n        return C_OK;\n    }\n\n    /* Only allow commands with flag \"t\", such as INFO, SLAVEOF and so on,\n     * when slave-serve-stale-data is no and we are a slave with a broken\n     * link with master. */\n    if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED &&\n        server.repl_serve_stale_data == 0 &&\n        !(c->cmd->flags & CMD_STALE))\n    {\n        flagTransaction(c);\n        addReply(c, shared.masterdownerr);\n        return C_OK;\n    }\n\n    /* Loading DB? Return an error if the command has not the\n     * CMD_LOADING flag. */\n    if (server.loading && !(c->cmd->flags & CMD_LOADING)) {\n        addReply(c, shared.loadingerr);\n        return C_OK;\n    }\n\n    /* Lua script too slow? Only allow a limited number of commands.\n     * Note that we need to allow the transactions commands, otherwise clients\n     * sending a transaction with pipelining without error checking, may have\n     * the MULTI plus a few initial commands refused, then the timeout\n     * condition resolves, and the bottom-half of the transaction gets\n     * executed, see Github PR #7022. */\n    if (server.lua_timedout &&\n          c->cmd->proc != authCommand &&\n          c->cmd->proc != helloCommand &&\n          c->cmd->proc != replconfCommand &&\n          c->cmd->proc != multiCommand &&\n          c->cmd->proc != execCommand &&\n          c->cmd->proc != discardCommand &&\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 C_OK;\n    }\n\n    /* Exec the command */\n    if (c->flags & CLIENT_MULTI &&\n        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&\n        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)\n    {\n        queueMultiCommand(c);\n        addReply(c,shared.queued);\n    } else {\n        call(c,CMD_CALL_FULL);\n        c->woff = server.master_repl_offset;\n        if (listLength(server.ready_keys))\n            handleClientsBlockedOnKeys();\n    }\n    return C_OK;\n}\n\n/*================================== Shutdown =============================== */\n\n/* Close listening sockets. Also unlink the unix domain socket if\n * unlink_unix_socket is non-zero. */\nvoid closeListeningSockets(int unlink_unix_socket) {\n    int j;\n\n    for (j = 0; j < server.ipfd_count; j++) close(server.ipfd[j]);\n    for (j = 0; j < server.tlsfd_count; j++) close(server.tlsfd[j]);\n    if (server.sofd != -1) close(server.sofd);\n    if (server.cluster_enabled)\n        for (j = 0; j < server.cfd_count; j++) close(server.cfd[j]);\n    if (unlink_unix_socket && server.unixsocket) {\n        serverLog(LL_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 & SHUTDOWN_SAVE;\n    int nosave = flags & SHUTDOWN_NOSAVE;\n\n    serverLog(LL_WARNING,\"User requested shutdown...\");\n    if (server.supervised_mode == SUPERVISED_SYSTEMD)\n        redisCommunicateSystemd(\"STOPPING=1\\n\");\n\n    /* Kill all the Lua debugger forked sessions. */\n    ldbKillForkedSessions();\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    if (server.rdb_child_pid != -1) {\n        serverLog(LL_WARNING,\"There is a child saving an .rdb. Killing it!\");\n        killRDBChild();\n    }\n\n    /* Kill module child if there is one. */\n    if (server.module_child_pid != -1) {\n        serverLog(LL_WARNING,\"There is a module fork child. Killing it!\");\n        TerminateModuleForkChild(server.module_child_pid,0);\n    }\n\n    if (server.aof_state != 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            /* If we have AOF enabled but haven't written the AOF yet, don't\n             * shutdown or else the dataset will be lost. */\n            if (server.aof_state == AOF_WAIT_REWRITE) {\n                serverLog(LL_WARNING, \"Writing initial AOF, can't exit.\");\n                return C_ERR;\n            }\n            serverLog(LL_WARNING,\n                \"There is a child rewriting the AOF. Killing it!\");\n            killAppendOnlyChild();\n        }\n        /* Append only file: flush buffers and fsync() the AOF at exit */\n        serverLog(LL_NOTICE,\"Calling fsync() on the AOF file.\");\n        flushAppendOnlyFile(1);\n        redis_fsync(server.aof_fd);\n    }\n\n    /* Create a new RDB file before exiting. */\n    if ((server.saveparamslen > 0 && !nosave) || save) {\n        serverLog(LL_NOTICE,\"Saving the final RDB snapshot before exiting.\");\n        if (server.supervised_mode == SUPERVISED_SYSTEMD)\n            redisCommunicateSystemd(\"STATUS=Saving the final RDB snapshot\\n\");\n        /* Snapshotting. Perform a SYNC SAVE and exit */\n        rdbSaveInfo rsi, *rsiptr;\n        rsiptr = rdbPopulateSaveInfo(&rsi);\n        if (rdbSave(server.rdb_filename,rsiptr) != C_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            serverLog(LL_WARNING,\"Error trying to save the DB, can't exit.\");\n            if (server.supervised_mode == SUPERVISED_SYSTEMD)\n                redisCommunicateSystemd(\"STATUS=Error trying to save the DB, can't exit.\\n\");\n            return C_ERR;\n        }\n    }\n\n    /* Fire the shutdown modules event. */\n    moduleFireServerEvent(REDISMODULE_EVENT_SHUTDOWN,0,NULL);\n\n    /* Remove the pid file if possible and needed. */\n    if (server.daemonize || server.pidfile) {\n        serverLog(LL_NOTICE,\"Removing the pid file.\");\n        unlink(server.pidfile);\n    }\n\n    /* Best effort flush of slave output buffers, so that we hopefully\n     * send them pending writes. */\n    flushSlavesOutputBuffers();\n\n    /* Close the listening sockets. Apparently this allows faster restarts. */\n    closeListeningSockets(1);\n    serverLog(LL_WARNING,\"%s is now ready to exit, bye bye...\",\n        server.sentinel_mode ? \"Sentinel\" : \"Redis\");\n    return C_OK;\n}\n\n/*================================== Commands =============================== */\n\n/* Sometimes Redis cannot accept write commands because there is a perstence\n * error with the RDB or AOF file, and Redis is configured in order to stop\n * accepting writes in such situation. This function returns if such a\n * condition is active, and the type of the condition.\n *\n * Function return values:\n *\n * DISK_ERROR_TYPE_NONE:    No problems, we can accept writes.\n * DISK_ERROR_TYPE_AOF:     Don't accept writes: AOF errors.\n * DISK_ERROR_TYPE_RDB:     Don't accept writes: RDB errors.\n */\nint writeCommandsDeniedByDiskError(void) {\n    if (server.stop_writes_on_bgsave_err &&\n        server.saveparamslen > 0 &&\n        server.lastbgsave_status == C_ERR)\n    {\n        return DISK_ERROR_TYPE_RDB;\n    } else if (server.aof_state != AOF_OFF &&\n               server.aof_last_write_status == C_ERR)\n    {\n        return DISK_ERROR_TYPE_AOF;\n    } else {\n        return DISK_ERROR_TYPE_NONE;\n    }\n}\n\n/* The PING command. It works in a different way if the client is in\n * in Pub/Sub mode. */\nvoid pingCommand(client *c) {\n    /* The command takes zero or one arguments. */\n    if (c->argc > 2) {\n        addReplyErrorFormat(c,\"wrong number of arguments for '%s' command\",\n            c->cmd->name);\n        return;\n    }\n\n    if (c->flags & CLIENT_PUBSUB && c->resp == 2) {\n        addReply(c,shared.mbulkhdr[2]);\n        addReplyBulkCBuffer(c,\"pong\",4);\n        if (c->argc == 1)\n            addReplyBulkCBuffer(c,\"\",0);\n        else\n            addReplyBulk(c,c->argv[1]);\n    } else {\n        if (c->argc == 1)\n            addReply(c,shared.pong);\n        else\n            addReplyBulk(c,c->argv[1]);\n    }\n}\n\nvoid echoCommand(client *c) {\n    addReplyBulk(c,c->argv[1]);\n}\n\nvoid timeCommand(client *c) {\n    struct timeval tv;\n\n    /* gettimeofday() can only fail if &tv is a bad address so we\n     * don't check for errors. */\n    gettimeofday(&tv,NULL);\n    addReplyArrayLen(c,2);\n    addReplyBulkLongLong(c,tv.tv_sec);\n    addReplyBulkLongLong(c,tv.tv_usec);\n}\n\n/* Helper function for addReplyCommand() to output flags. */\nint addReplyCommandFlag(client *c, struct redisCommand *cmd, int f, char *reply) {\n    if (cmd->flags & f) {\n        addReplyStatus(c, reply);\n        return 1;\n    }\n    return 0;\n}\n\n/* Output the representation of a Redis command. Used by the COMMAND command. */\nvoid addReplyCommand(client *c, struct redisCommand *cmd) {\n    if (!cmd) {\n        addReplyNull(c);\n    } else {\n        /* We are adding: command name, arg count, flags, first, last, offset, categories */\n        addReplyArrayLen(c, 7);\n        addReplyBulkCString(c, cmd->name);\n        addReplyLongLong(c, cmd->arity);\n\n        int flagcount = 0;\n        void *flaglen = addReplyDeferredLen(c);\n        flagcount += addReplyCommandFlag(c,cmd,CMD_WRITE, \"write\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_READONLY, \"readonly\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_DENYOOM, \"denyoom\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_ADMIN, \"admin\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_PUBSUB, \"pubsub\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_NOSCRIPT, \"noscript\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_RANDOM, \"random\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SORT_FOR_SCRIPT,\"sort_for_script\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_LOADING, \"loading\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_STALE, \"stale\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_MONITOR, \"skip_monitor\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_SLOWLOG, \"skip_slowlog\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_ASKING, \"asking\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_FAST, \"fast\");\n        flagcount += addReplyCommandFlag(c,cmd,CMD_NO_AUTH, \"no_auth\");\n        if ((cmd->getkeys_proc && !(cmd->flags & CMD_MODULE)) ||\n            cmd->flags & CMD_MODULE_GETKEYS)\n        {\n            addReplyStatus(c, \"movablekeys\");\n            flagcount += 1;\n        }\n        setDeferredSetLen(c, flaglen, flagcount);\n\n        addReplyLongLong(c, cmd->firstkey);\n        addReplyLongLong(c, cmd->lastkey);\n        addReplyLongLong(c, cmd->keystep);\n\n        addReplyCommandCategories(c,cmd);\n    }\n}\n\n/* COMMAND <subcommand> <args> */\nvoid commandCommand(client *c) {\n    dictIterator *di;\n    dictEntry *de;\n\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"(no subcommand) -- Return details about all Redis commands.\",\n\"COUNT -- Return the total number of commands in this Redis server.\",\n\"GETKEYS <full-command> -- Return the keys from a full Redis command.\",\n\"INFO [command-name ...] -- Return details about multiple Redis commands.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (c->argc == 1) {\n        addReplyArrayLen(c, dictSize(server.commands));\n        di = dictGetIterator(server.commands);\n        while ((de = dictNext(di)) != NULL) {\n            addReplyCommand(c, dictGetVal(de));\n        }\n        dictReleaseIterator(di);\n    } else if (!strcasecmp(c->argv[1]->ptr, \"info\")) {\n        int i;\n        addReplyArrayLen(c, c->argc-2);\n        for (i = 2; i < c->argc; i++) {\n            addReplyCommand(c, dictFetchValue(server.commands, c->argv[i]->ptr));\n        }\n    } else if (!strcasecmp(c->argv[1]->ptr, \"count\") && c->argc == 2) {\n        addReplyLongLong(c, dictSize(server.commands));\n    } else if (!strcasecmp(c->argv[1]->ptr,\"getkeys\") && c->argc >= 3) {\n        struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);\n        int *keys, numkeys, j;\n\n        if (!cmd) {\n            addReplyError(c,\"Invalid command specified\");\n            return;\n        } else if (cmd->getkeys_proc == NULL && cmd->firstkey == 0) {\n            addReplyError(c,\"The command has no key arguments\");\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        if (!keys) {\n            addReplyError(c,\"Invalid arguments specified for command\");\n        } else {\n            addReplyArrayLen(c,numkeys);\n            for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);\n            getKeysFreeResult(keys);\n        }\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\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    } else if (n < (1024*1024)) {\n        d = (double)n/(1024);\n        sprintf(s,\"%.2fK\",d);\n    } else if (n < (1024LL*1024*1024)) {\n        d = (double)n/(1024*1024);\n        sprintf(s,\"%.2fM\",d);\n    } else if (n < (1024LL*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024);\n        sprintf(s,\"%.2fG\",d);\n    } else if (n < (1024LL*1024*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024*1024);\n        sprintf(s,\"%.2fT\",d);\n    } else if (n < (1024LL*1024*1024*1024*1024*1024)) {\n        d = (double)n/(1024LL*1024*1024*1024*1024);\n        sprintf(s,\"%.2fP\",d);\n    } else {\n        /* Let's hope we never need this */\n        sprintf(s,\"%lluB\",n);\n    }\n}\n\n/* 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(const char *section) {\n    sds info = sdsempty();\n    time_t uptime = server.unixtime-server.stat_starttime;\n    int j;\n    struct rusage self_ru, c_ru;\n    int allsections = 0, defsections = 0, everything = 0, modules = 0;\n    int sections = 0;\n\n    if (section == NULL) section = \"default\";\n    allsections = strcasecmp(section,\"all\") == 0;\n    defsections = strcasecmp(section,\"default\") == 0;\n    everything = strcasecmp(section,\"everything\") == 0;\n    modules = strcasecmp(section,\"modules\") == 0;\n    if (everything) allsections = 1;\n\n    getrusage(RUSAGE_SELF, &self_ru);\n    getrusage(RUSAGE_CHILDREN, &c_ru);\n\n    /* Server */\n    if (allsections || defsections || !strcasecmp(section,\"server\")) {\n        static int call_uname = 1;\n        static struct utsname name;\n        char *mode;\n\n        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 = sdscatfmt(info,\n            \"# Server\\r\\n\"\n            \"redis_version:%s\\r\\n\"\n            \"redis_git_sha1:%s\\r\\n\"\n            \"redis_git_dirty:%i\\r\\n\"\n            \"redis_build_id:%s\\r\\n\"\n            \"redis_mode:%s\\r\\n\"\n            \"os:%s %s %s\\r\\n\"\n            \"arch_bits:%i\\r\\n\"\n            \"multiplexing_api:%s\\r\\n\"\n            \"atomicvar_api:%s\\r\\n\"\n            \"gcc_version:%i.%i.%i\\r\\n\"\n            \"process_id:%I\\r\\n\"\n            \"run_id:%s\\r\\n\"\n            \"tcp_port:%i\\r\\n\"\n            \"uptime_in_seconds:%I\\r\\n\"\n            \"uptime_in_days:%I\\r\\n\"\n            \"hz:%i\\r\\n\"\n            \"configured_hz:%i\\r\\n\"\n            \"lru_clock:%u\\r\\n\"\n            \"executable:%s\\r\\n\"\n            \"config_file:%s\\r\\n\",\n            REDIS_VERSION,\n            redisGitSHA1(),\n            strtol(redisGitDirty(),NULL,10) > 0,\n            redisBuildIdString(),\n            mode,\n            name.sysname, name.release, name.machine,\n            server.arch_bits,\n            aeGetApiName(),\n            REDIS_ATOMIC_API,\n#ifdef __GNUC__\n            __GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__,\n#else\n            0,0,0,\n#endif\n            (int64_t) getpid(),\n            server.runid,\n            server.port ? server.port : server.tls_port,\n            (int64_t)uptime,\n            (int64_t)(uptime/(3600*24)),\n            server.hz,\n            server.config_hz,\n            server.lruclock,\n            server.executable ? server.executable : \"\",\n            server.configfile ? server.configfile : \"\");\n    }\n\n    /* Clients */\n    if (allsections || defsections || !strcasecmp(section,\"clients\")) {\n        size_t maxin, maxout;\n        getExpansiveClientsInfo(&maxin,&maxout);\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Clients\\r\\n\"\n            \"connected_clients:%lu\\r\\n\"\n            \"client_recent_max_input_buffer:%zu\\r\\n\"\n            \"client_recent_max_output_buffer:%zu\\r\\n\"\n            \"blocked_clients:%d\\r\\n\"\n            \"tracking_clients:%d\\r\\n\"\n            \"clients_in_timeout_table:%llu\\r\\n\",\n            listLength(server.clients)-listLength(server.slaves),\n            maxin, maxout,\n            server.blocked_clients,\n            server.tracking_clients,\n            (unsigned long long) raxSize(server.clients_timeout_table));\n    }\n\n    /* Memory */\n    if (allsections || defsections || !strcasecmp(section,\"memory\")) {\n        char hmem[64];\n        char peak_hmem[64];\n        char total_system_hmem[64];\n        char used_memory_lua_hmem[64];\n        char used_memory_scripts_hmem[64];\n        char used_memory_rss_hmem[64];\n        char maxmemory_hmem[64];\n        size_t zmalloc_used = zmalloc_used_memory();\n        size_t total_system_mem = server.system_memory_size;\n        const char *evict_policy = evictPolicyToString();\n        long long memory_lua = server.lua ? (long long)lua_gc(server.lua,LUA_GCCOUNT,0)*1024 : 0;\n        struct redisMemOverhead *mh = getMemoryOverheadData();\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        bytesToHuman(total_system_hmem,total_system_mem);\n        bytesToHuman(used_memory_lua_hmem,memory_lua);\n        bytesToHuman(used_memory_scripts_hmem,mh->lua_caches);\n        bytesToHuman(used_memory_rss_hmem,server.cron_malloc_stats.process_rss);\n        bytesToHuman(maxmemory_hmem,server.maxmemory);\n\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\n            \"# Memory\\r\\n\"\n            \"used_memory:%zu\\r\\n\"\n            \"used_memory_human:%s\\r\\n\"\n            \"used_memory_rss:%zu\\r\\n\"\n            \"used_memory_rss_human:%s\\r\\n\"\n            \"used_memory_peak:%zu\\r\\n\"\n            \"used_memory_peak_human:%s\\r\\n\"\n            \"used_memory_peak_perc:%.2f%%\\r\\n\"\n            \"used_memory_overhead:%zu\\r\\n\"\n            \"used_memory_startup:%zu\\r\\n\"\n            \"used_memory_dataset:%zu\\r\\n\"\n            \"used_memory_dataset_perc:%.2f%%\\r\\n\"\n            \"allocator_allocated:%zu\\r\\n\"\n            \"allocator_active:%zu\\r\\n\"\n            \"allocator_resident:%zu\\r\\n\"\n            \"total_system_memory:%lu\\r\\n\"\n            \"total_system_memory_human:%s\\r\\n\"\n            \"used_memory_lua:%lld\\r\\n\"\n            \"used_memory_lua_human:%s\\r\\n\"\n            \"used_memory_scripts:%lld\\r\\n\"\n            \"used_memory_scripts_human:%s\\r\\n\"\n            \"number_of_cached_scripts:%lu\\r\\n\"\n            \"maxmemory:%lld\\r\\n\"\n            \"maxmemory_human:%s\\r\\n\"\n            \"maxmemory_policy:%s\\r\\n\"\n            \"allocator_frag_ratio:%.2f\\r\\n\"\n            \"allocator_frag_bytes:%zu\\r\\n\"\n            \"allocator_rss_ratio:%.2f\\r\\n\"\n            \"allocator_rss_bytes:%zd\\r\\n\"\n            \"rss_overhead_ratio:%.2f\\r\\n\"\n            \"rss_overhead_bytes:%zd\\r\\n\"\n            \"mem_fragmentation_ratio:%.2f\\r\\n\"\n            \"mem_fragmentation_bytes:%zd\\r\\n\"\n            \"mem_not_counted_for_evict:%zu\\r\\n\"\n            \"mem_replication_backlog:%zu\\r\\n\"\n            \"mem_clients_slaves:%zu\\r\\n\"\n            \"mem_clients_normal:%zu\\r\\n\"\n            \"mem_aof_buffer:%zu\\r\\n\"\n            \"mem_allocator:%s\\r\\n\"\n            \"active_defrag_running:%d\\r\\n\"\n            \"lazyfree_pending_objects:%zu\\r\\n\",\n            zmalloc_used,\n            hmem,\n            server.cron_malloc_stats.process_rss,\n            used_memory_rss_hmem,\n            server.stat_peak_memory,\n            peak_hmem,\n            mh->peak_perc,\n            mh->overhead_total,\n            mh->startup_allocated,\n            mh->dataset,\n            mh->dataset_perc,\n            server.cron_malloc_stats.allocator_allocated,\n            server.cron_malloc_stats.allocator_active,\n            server.cron_malloc_stats.allocator_resident,\n            (unsigned long)total_system_mem,\n            total_system_hmem,\n            memory_lua,\n            used_memory_lua_hmem,\n            (long long) mh->lua_caches,\n            used_memory_scripts_hmem,\n            dictSize(server.lua_scripts),\n            server.maxmemory,\n            maxmemory_hmem,\n            evict_policy,\n            mh->allocator_frag,\n            mh->allocator_frag_bytes,\n            mh->allocator_rss,\n            mh->allocator_rss_bytes,\n            mh->rss_extra,\n            mh->rss_extra_bytes,\n            mh->total_frag,       /* This is the total RSS overhead, including\n                                     fragmentation, but not just it. This field\n                                     (and the next one) is named like that just\n                                     for backward compatibility. */\n            mh->total_frag_bytes,\n            freeMemoryGetNotCountedMemory(),\n            mh->repl_backlog,\n            mh->clients_slaves,\n            mh->clients_normal,\n            mh->aof_buffer,\n            ZMALLOC_LIB,\n            server.active_defrag_running,\n            lazyfreeGetPendingObjectsCount()\n        );\n        freeMemoryOverheadData(mh);\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            \"rdb_last_cow_size:%zu\\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            \"aof_last_cow_size:%zu\\r\\n\"\n            \"module_fork_in_progress:%d\\r\\n\"\n            \"module_fork_last_cow_size:%zu\\r\\n\",\n            server.loading,\n            server.dirty,\n            server.rdb_child_pid != -1,\n            (intmax_t)server.lastsave,\n            (server.lastbgsave_status == C_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.stat_rdb_cow_bytes,\n            server.aof_state != 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 == C_OK) ? \"ok\" : \"err\",\n            (server.aof_last_write_status == C_OK) ? \"ok\" : \"err\",\n            server.stat_aof_cow_bytes,\n            server.module_child_pid != -1,\n            server.stat_module_cow_bytes);\n\n        if (server.aof_enabled) {\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(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+1)) * 100;\n\n            elapsed = time(NULL)-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+1);\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            \"total_net_input_bytes:%lld\\r\\n\"\n            \"total_net_output_bytes:%lld\\r\\n\"\n            \"instantaneous_input_kbps:%.2f\\r\\n\"\n            \"instantaneous_output_kbps:%.2f\\r\\n\"\n            \"rejected_connections:%lld\\r\\n\"\n            \"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            \"expired_stale_perc:%.2f\\r\\n\"\n            \"expired_time_cap_reached_count:%lld\\r\\n\"\n            \"expire_cycle_cpu_milliseconds:%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            \"slave_expires_tracked_keys:%zu\\r\\n\"\n            \"active_defrag_hits:%lld\\r\\n\"\n            \"active_defrag_misses:%lld\\r\\n\"\n            \"active_defrag_key_hits:%lld\\r\\n\"\n            \"active_defrag_key_misses:%lld\\r\\n\"\n            \"tracking_total_keys:%lld\\r\\n\"\n            \"tracking_total_items:%lld\\r\\n\"\n            \"tracking_total_prefixes:%lld\\r\\n\"\n            \"unexpected_error_replies:%lld\\r\\n\",\n            server.stat_numconnections,\n            server.stat_numcommands,\n            getInstantaneousMetric(STATS_METRIC_COMMAND),\n            server.stat_net_input_bytes,\n            server.stat_net_output_bytes,\n            (float)getInstantaneousMetric(STATS_METRIC_NET_INPUT)/1024,\n            (float)getInstantaneousMetric(STATS_METRIC_NET_OUTPUT)/1024,\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_expired_stale_perc*100,\n            server.stat_expired_time_cap_reached_count,\n            server.stat_expire_cycle_time_used/1000,\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            getSlaveKeyWithExpireCount(),\n            server.stat_active_defrag_hits,\n            server.stat_active_defrag_misses,\n            server.stat_active_defrag_key_hits,\n            server.stat_active_defrag_key_misses,\n            (unsigned long long) trackingGetTotalKeys(),\n            (unsigned long long) trackingGetTotalItems(),\n            (unsigned long long) trackingGetTotalPrefixes(),\n            server.stat_unexpected_error_replies);\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 == REPL_STATE_CONNECTED) ?\n                    \"up\" : \"down\",\n                server.master ?\n                ((int)(server.unixtime-server.master->lastinteraction)) : -1,\n                server.repl_state == REPL_STATE_TRANSFER,\n                slave_repl_offset\n            );\n\n            if (server.repl_state == REPL_STATE_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 != REPL_STATE_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                client *slave = listNodeValue(ln);\n                char *state = NULL;\n                char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;\n                int port;\n                long lag = 0;\n\n                if (slaveip[0] == '\\0') {\n                    if (connPeerToString(slave->conn,ip,sizeof(ip),&port) == -1)\n                        continue;\n                    slaveip = ip;\n                }\n                switch(slave->replstate) {\n                case SLAVE_STATE_WAIT_BGSAVE_START:\n                case SLAVE_STATE_WAIT_BGSAVE_END:\n                    state = \"wait_bgsave\";\n                    break;\n                case SLAVE_STATE_SEND_BULK:\n                    state = \"send_bulk\";\n                    break;\n                case SLAVE_STATE_ONLINE:\n                    state = \"online\";\n                    break;\n                }\n                if (state == NULL) continue;\n                if (slave->replstate == SLAVE_STATE_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,slaveip,slave->slave_listening_port,state,\n                    slave->repl_ack_off, lag);\n                slaveid++;\n            }\n        }\n        info = sdscatprintf(info,\n            \"master_replid:%s\\r\\n\"\n            \"master_replid2:%s\\r\\n\"\n            \"master_repl_offset:%lld\\r\\n\"\n            \"master_repl_meaningful_offset:%lld\\r\\n\"\n            \"second_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.replid,\n            server.replid2,\n            server.master_repl_offset,\n            server.master_repl_meaningful_offset,\n            server.second_replid_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:%ld.%06ld\\r\\n\"\n        \"used_cpu_user:%ld.%06ld\\r\\n\"\n        \"used_cpu_sys_children:%ld.%06ld\\r\\n\"\n        \"used_cpu_user_children:%ld.%06ld\\r\\n\",\n        (long)self_ru.ru_stime.tv_sec, (long)self_ru.ru_stime.tv_usec,\n        (long)self_ru.ru_utime.tv_sec, (long)self_ru.ru_utime.tv_usec,\n        (long)c_ru.ru_stime.tv_sec, (long)c_ru.ru_stime.tv_usec,\n        (long)c_ru.ru_utime.tv_sec, (long)c_ru.ru_utime.tv_usec);\n    }\n\n    /* Modules */\n    if (allsections || defsections || !strcasecmp(section,\"modules\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info,\"# Modules\\r\\n\");\n        info = genModulesInfoString(info);\n    }\n\n    /* Command statistics */\n    if (allsections || !strcasecmp(section,\"commandstats\")) {\n        if (sections++) info = sdscat(info,\"\\r\\n\");\n        info = sdscatprintf(info, \"# Commandstats\\r\\n\");\n\n        struct redisCommand *c;\n        dictEntry *de;\n        dictIterator *di;\n        di = dictGetSafeIterator(server.commands);\n        while((de = dictNext(di)) != NULL) {\n            c = (struct redisCommand *) dictGetVal(de);\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        dictReleaseIterator(di);\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\n    /* Get info from modules.\n     * if user asked for \"everything\" or \"modules\", or a specific section\n     * that's not found yet. */\n    if (everything || modules ||\n        (!allsections && !defsections && sections==0)) {\n        info = modulesCollectInfo(info,\n                                  everything || modules ? NULL: section,\n                                  0, /* not a crash report */\n                                  sections);\n    }\n    return info;\n}\n\nvoid infoCommand(client *c) {\n    char *section = c->argc == 2 ? c->argv[1]->ptr : \"default\";\n\n    if (c->argc > 2) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    sds info = genRedisInfoString(section);\n    addReplyVerbatim(c,info,sdslen(info),\"txt\");\n    sdsfree(info);\n}\n\nvoid monitorCommand(client *c) {\n    /* ignore MONITOR if already slave or in monitor mode */\n    if (c->flags & CLIENT_SLAVE) return;\n\n    c->flags |= (CLIENT_SLAVE|CLIENT_MONITOR);\n    listAddNodeTail(server.monitors,c);\n    addReply(c,shared.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 linuxMemoryWarnings(void) {\n    if (linuxOvercommitMemoryValue() == 0) {\n        serverLog(LL_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    if (THPIsEnabled()) {\n        serverLog(LL_WARNING,\"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.\");\n    }\n}\n#endif /* __linux__ */\n\nvoid createPidFile(void) {\n    /* If pidfile requested, but no pidfile defined, use\n     * default pidfile path */\n    if (!server.pidfile) server.pidfile = zstrdup(CONFIG_DEFAULT_PID_FILE);\n\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(void) {\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(void) {\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 --replicaof 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;\n\n    if (server.cluster_enabled) mode = \"cluster\";\n    else if (server.sentinel_mode) mode = \"sentinel\";\n    else mode = \"standalone\";\n\n    /* Show the ASCII logo if: log file is stdout AND stdout is a\n     * tty AND syslog logging is disabled. Also show logo if the user\n     * forced us to do so via redis.conf. */\n    int show_logo = ((!server.syslog_enabled &&\n                      server.logfile[0] == '\\0' &&\n                      isatty(fileno(stdout))) ||\n                     server.always_show_logo);\n\n    if (!show_logo) {\n        serverLog(LL_NOTICE,\n            \"Running mode=%s, port=%d.\",\n            mode, server.port ? server.port : server.tls_port\n        );\n    } else {\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 ? server.port : server.tls_port,\n            (long) getpid()\n        );\n        serverLogRaw(LL_NOTICE|LL_RAW,buf);\n    }\n    zfree(buf);\n}\n\nstatic void sigShutdownHandler(int sig) {\n    char *msg;\n\n    switch (sig) {\n    case SIGINT:\n        msg = \"Received SIGINT scheduling shutdown...\";\n        break;\n    case SIGTERM:\n        msg = \"Received SIGTERM scheduling shutdown...\";\n        break;\n    default:\n        msg = \"Received shutdown signal, scheduling shutdown...\";\n    };\n\n    /* SIGINT is often delivered via Ctrl+C in an interactive session.\n     * If we receive the signal the second time, we interpret this as\n     * the user really wanting to quit ASAP without waiting to persist\n     * on disk. */\n    if (server.shutdown_asap && sig == SIGINT) {\n        serverLogFromHandler(LL_WARNING, \"You insist... exiting now.\");\n        rdbRemoveTempFile(getpid());\n        exit(1); /* Exit with an error since this was not a clean shutdown. */\n    } else if (server.loading) {\n        serverLogFromHandler(LL_WARNING, \"Received shutdown signal during loading, exiting now.\");\n        exit(0);\n    }\n\n    serverLogFromHandler(LL_WARNING, msg);\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 = sigShutdownHandler;\n    sigaction(SIGTERM, &act, NULL);\n    sigaction(SIGINT, &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\n/* This is the signal handler for children process. It is currently useful\n * in order to track the SIGUSR1, that we send to a child in order to terminate\n * it in a clean way, without the parent detecting an error and stop\n * accepting writes because of a write error condition. */\nstatic void sigKillChildHandler(int sig) {\n    UNUSED(sig);\n    serverLogFromHandler(LL_WARNING, \"Received SIGUSR1 in child, exiting now.\");\n    exitFromChild(SERVER_CHILD_NOERROR_RETVAL);\n}\n\nvoid setupChildSignalHandlers(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 = sigKillChildHandler;\n    sigaction(SIGUSR1, &act, NULL);\n    return;\n}\n\nint redisFork() {\n    int childpid;\n    long long start = ustime();\n    if ((childpid = fork()) == 0) {\n        /* Child */\n        closeListeningSockets(0);\n        setupChildSignalHandlers();\n    } else {\n        /* Parent */\n        server.stat_fork_time = ustime()-start;\n        server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */\n        latencyAddSampleIfNeeded(\"fork\",server.stat_fork_time/1000);\n        if (childpid == -1) {\n            return -1;\n        }\n        updateDictResizePolicy();\n    }\n    return childpid;\n}\n\nvoid sendChildCOWInfo(int ptype, char *pname) {\n    size_t private_dirty = zmalloc_get_private_dirty(-1);\n\n    if (private_dirty) {\n        serverLog(LL_NOTICE,\n            \"%s: %zu MB of memory used by copy-on-write\",\n            pname, private_dirty/(1024*1024));\n    }\n\n    server.child_info_data.cow_size = private_dirty;\n    sendChildInfo(ptype);\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] contains \"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    long long start = ustime();\n    if (server.aof_state == AOF_ON) {\n        if (loadAppendOnlyFile(server.aof_filename) == C_OK)\n            serverLog(LL_NOTICE,\"DB loaded from append only file: %.3f seconds\",(float)(ustime()-start)/1000000);\n    } else {\n        rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;\n        if (rdbLoad(server.rdb_filename,&rsi,RDBFLAGS_NONE) == C_OK) {\n            serverLog(LL_NOTICE,\"DB loaded from disk: %.3f seconds\",\n                (float)(ustime()-start)/1000000);\n\n            /* Restore the replication ID / offset from the RDB file. */\n            if ((server.masterhost ||\n                (server.cluster_enabled &&\n                nodeIsSlave(server.cluster->myself))) &&\n                rsi.repl_id_is_set &&\n                rsi.repl_offset != -1 &&\n                /* Note that older implementations may save a repl_stream_db\n                 * of -1 inside the RDB file in a wrong way, see more\n                 * information in function rdbPopulateSaveInfo. */\n                rsi.repl_stream_db != -1)\n            {\n                memcpy(server.replid,rsi.repl_id,sizeof(server.replid));\n                server.master_repl_offset = rsi.repl_offset;\n                server.master_repl_meaningful_offset = rsi.repl_offset;\n                /* If we are a slave, create a cached master from this\n                 * information, in order to allow partial resynchronizations\n                 * with masters. */\n                replicationCacheMasterUsingMyself();\n                selectDb(server.cached_master,rsi.repl_stream_db);\n            }\n        } else if (errno != ENOENT) {\n            serverLog(LL_WARNING,\"Fatal error loading the DB: %s. Exiting.\",strerror(errno));\n            exit(1);\n        }\n    }\n}\n\nvoid redisOutOfMemoryHandler(size_t allocation_size) {\n    serverLog(LL_WARNING,\"Out Of Memory allocating %zu bytes!\",\n        allocation_size);\n    serverPanic(\"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 ? server.port : server.tls_port,\n        server_mode);\n#else\n    UNUSED(title);\n#endif\n}\n\nvoid redisSetCpuAffinity(const char *cpulist) {\n#ifdef USE_SETCPUAFFINITY\n    setcpuaffinity(cpulist);\n#else\n    UNUSED(cpulist);\n#endif\n}\n\n/*\n * Check whether systemd or upstart have been used to start redis.\n */\n\nint redisSupervisedUpstart(void) {\n    const char *upstart_job = getenv(\"UPSTART_JOB\");\n\n    if (!upstart_job) {\n        serverLog(LL_WARNING,\n                \"upstart supervision requested, but UPSTART_JOB not found\");\n        return 0;\n    }\n\n    serverLog(LL_NOTICE, \"supervised by upstart, will stop to signal readiness\");\n    raise(SIGSTOP);\n    unsetenv(\"UPSTART_JOB\");\n    return 1;\n}\n\nint redisCommunicateSystemd(const char *sd_notify_msg) {\n    const char *notify_socket = getenv(\"NOTIFY_SOCKET\");\n    if (!notify_socket) {\n        serverLog(LL_WARNING,\n                \"systemd supervision requested, but NOTIFY_SOCKET not found\");\n    }\n\n    #ifdef HAVE_LIBSYSTEMD\n    (void) sd_notify(0, sd_notify_msg);\n    #else\n    UNUSED(sd_notify_msg);\n    #endif\n    return 0;\n}\n\nint redisIsSupervised(int mode) {\n    if (mode == SUPERVISED_AUTODETECT) {\n        const char *upstart_job = getenv(\"UPSTART_JOB\");\n        const char *notify_socket = getenv(\"NOTIFY_SOCKET\");\n\n        if (upstart_job) {\n            redisSupervisedUpstart();\n        } else if (notify_socket) {\n            server.supervised_mode = SUPERVISED_SYSTEMD;\n            serverLog(LL_WARNING,\n                \"WARNING auto-supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit.\");\n            return redisCommunicateSystemd(\"STATUS=Redis is loading...\\n\");\n        }\n    } else if (mode == SUPERVISED_UPSTART) {\n        return redisSupervisedUpstart();\n    } else if (mode == SUPERVISED_SYSTEMD) {\n        serverLog(LL_WARNING,\n            \"WARNING supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit.\");\n        return redisCommunicateSystemd(\"STATUS=Redis is loading...\\n\");\n    }\n\n    return 0;\n}\n\nint iAmMaster(void) {\n    return ((!server.cluster_enabled && server.masterhost == NULL) ||\n            (server.cluster_enabled && nodeIsMaster(server.cluster->myself)));\n}\n\n\nint main(int argc, char **argv) {\n    struct timeval tv;\n    int j;\n\n#ifdef REDIS_TEST\n    if (argc == 3 && !strcasecmp(argv[1], \"test\")) {\n        if (!strcasecmp(argv[2], \"ziplist\")) {\n            return ziplistTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"quicklist\")) {\n            quicklistTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"intset\")) {\n            return intsetTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"zipmap\")) {\n            return zipmapTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"sha1test\")) {\n            return sha1Test(argc, argv);\n        } else if (!strcasecmp(argv[2], \"util\")) {\n            return utilTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"endianconv\")) {\n            return endianconvTest(argc, argv);\n        } else if (!strcasecmp(argv[2], \"crc64\")) {\n            return crc64Test(argc, argv);\n        } else if (!strcasecmp(argv[2], \"zmalloc\")) {\n            return zmalloc_test(argc, argv);\n        }\n\n        return -1; /* test not found */\n    }\n#endif\n\n    /* We need to initialize our libraries, and the server configuration. */\n#ifdef INIT_SETPROCTITLE_REPLACEMENT\n    spt_init(argc, argv);\n#endif\n    setlocale(LC_COLLATE,\"\");\n    tzset(); /* Populates 'timezone' global. */\n    zmalloc_set_oom_handler(redisOutOfMemoryHandler);\n    srand(time(NULL)^getpid());\n    gettimeofday(&tv,NULL);\n    crc64_init();\n\n    uint8_t hashseed[16];\n    getRandomBytes(hashseed,sizeof(hashseed));\n    dictSetHashFunctionSeed(hashseed);\n    server.sentinel_mode = checkForSentinelMode(argc,argv);\n    initServerConfig();\n    ACLInit(); /* The ACL subsystem must be initialized ASAP because the\n                  basic networking code and client creation depends on it. */\n    moduleInitModulesSystem();\n    tlsInit();\n\n    /* Store the executable path and arguments in a safe place in order\n     * to be able to restart the server later. */\n    server.executable = getAbsolutePath(argv[0]);\n    server.exec_argv = zmalloc(sizeof(char*)*(argc+1));\n    server.exec_argv[argc] = NULL;\n    for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]);\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    if (server.sentinel_mode) {\n        initSentinelConfig();\n        initSentinel();\n    }\n\n    /* Check if we need to start in redis-check-rdb/aof mode. We just execute\n     * the program main. However the program is part of the Redis executable\n     * so that we can easily execute an RDB check on loading errors. */\n    if (strstr(argv[0],\"redis-check-rdb\") != NULL)\n        redis_check_rdb_main(argc,argv,NULL);\n    else if (strstr(argv[0],\"redis-check-aof\") != NULL)\n        redis_check_aof_main(argc,argv);\n\n    if (argc >= 2) {\n        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        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        if (argv[j][0] != '-' || argv[j][1] != '-') {\n            configfile = argv[j];\n            server.configfile = getAbsolutePath(configfile);\n            /* Replace the config file in server.exec_argv with\n             * its absolute path. */\n            zfree(server.exec_argv[j]);\n            server.exec_argv[j] = zstrdup(server.configfile);\n            j++;\n        }\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        while(j != argc) {\n            if (argv[j][0] == '-' && argv[j][1] == '-') {\n                /* Option name */\n                if (!strcmp(argv[j], \"--check-rdb\")) {\n                    /* Argument has no options, need to skip for parsing. */\n                    j++;\n                    continue;\n                }\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 (server.sentinel_mode && configfile && *configfile == '-') {\n            serverLog(LL_WARNING,\n                \"Sentinel config from STDIN not allowed.\");\n            serverLog(LL_WARNING,\n                \"Sentinel needs config file on disk to save state.  Exiting...\");\n            exit(1);\n        }\n        resetServerSaveParams();\n        loadServerConfig(configfile,options);\n        sdsfree(options);\n    }\n\n    serverLog(LL_WARNING, \"oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo\");\n    serverLog(LL_WARNING,\n        \"Redis version=%s, bits=%d, commit=%s, modified=%d, pid=%d, just started\",\n            REDIS_VERSION,\n            (sizeof(long) == 8) ? 64 : 32,\n            redisGitSHA1(),\n            strtol(redisGitDirty(),NULL,10) > 0,\n            (int)getpid());\n\n    if (argc == 1) {\n        serverLog(LL_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    } else {\n        serverLog(LL_WARNING, \"Configuration loaded\");\n    }\n\n    server.supervised = redisIsSupervised(server.supervised_mode);\n    int background = server.daemonize && !server.supervised;\n    if (background) daemonize();\n\n    initServer();\n    if (background || server.pidfile) createPidFile();\n    redisSetProcTitle(argv[0]);\n    redisAsciiArt();\n    checkTcpBacklogSettings();\n\n    if (!server.sentinel_mode) {\n        /* Things not needed when running in Sentinel mode. */\n        serverLog(LL_WARNING,\"Server initialized\");\n    #ifdef __linux__\n        linuxMemoryWarnings();\n    #endif\n        moduleLoadFromQueue();\n        ACLLoadUsersAtStartup();\n        InitServerLast();\n        loadDataFromDisk();\n        if (server.cluster_enabled) {\n            if (verifyClusterConfigWithData() == C_ERR) {\n                serverLog(LL_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        if (server.ipfd_count > 0 || server.tlsfd_count > 0)\n            serverLog(LL_NOTICE,\"Ready to accept connections\");\n        if (server.sofd > 0)\n            serverLog(LL_NOTICE,\"The server is now ready to accept connections at %s\", server.unixsocket);\n        if (server.supervised_mode == SUPERVISED_SYSTEMD) {\n            if (!server.masterhost) {\n                redisCommunicateSystemd(\"STATUS=Ready to accept connections\\n\");\n                redisCommunicateSystemd(\"READY=1\\n\");\n            } else {\n                redisCommunicateSystemd(\"STATUS=Waiting for MASTER <-> REPLICA sync\\n\");\n            }\n        }\n    } else {\n        InitServerLast();\n        sentinelIsRunning();\n    }\n\n    /* Warning the user about suspicious maxmemory setting. */\n    if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {\n        serverLog(LL_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    redisSetCpuAffinity(server.server_cpulist);\n    aeMain(server.el);\n    aeDeleteEventLoop(server.el);\n    return 0;\n}\n\n/* The End */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/server.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#include \"solarisfixes.h\"\n#include \"rio.h\"\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 <sys/socket.h>\n#include <lua.h>\n#include <signal.h>\n\n#ifdef HAVE_LIBSYSTEMD\n#include <systemd/sd-daemon.h>\n#endif\n\ntypedef long long mstime_t; /* millisecond time type. */\ntypedef long long ustime_t; /* microsecond time type. */\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#include \"latency.h\" /* Latency monitor API */\n#include \"sparkline.h\" /* ASCII graphs API */\n#include \"quicklist.h\"  /* Lists are encoded as linked lists of\n                           N-elements flat arrays */\n#include \"rax.h\"     /* Radix tree */\n#include \"connection.h\" /* Connection abstraction */\n\n#define REDISMODULE_CORE 1\n#include \"redismodule.h\"    /* Redis modules API defines. */\n\n/* Following includes allow test functions to be called from Redis main() */\n#include \"zipmap.h\"\n#include \"sha1.h\"\n#include \"endianconv.h\"\n#include \"crc64.h\"\n\n/* Error codes */\n#define C_OK                    0\n#define C_ERR                   -1\n\n/* Static server configuration */\n#define CONFIG_DEFAULT_HZ        10             /* Time interrupt calls/sec. */\n#define CONFIG_MIN_HZ            1\n#define CONFIG_MAX_HZ            500\n#define MAX_CLIENTS_PER_CLOCK_TICK 200          /* HZ is adapted based on that. */\n#define CONFIG_MAX_LINE    1024\n#define CRON_DBS_PER_CALL 16\n#define NET_MAX_WRITES_PER_EVENT (1024*64)\n#define PROTO_SHARED_SELECT_CMDS 10\n#define OBJ_SHARED_INTEGERS 10000\n#define OBJ_SHARED_BULKHDR_LEN 32\n#define LOG_MAX_LEN    1024 /* Default maximum length of syslog messages.*/\n#define AOF_REWRITE_ITEMS_PER_CMD 64\n#define AOF_READ_DIFF_INTERVAL_BYTES (1024*10)\n#define CONFIG_AUTHPASS_MAX_LEN 512\n#define CONFIG_RUN_ID_SIZE 40\n#define RDB_EOF_MARK_SIZE 40\n#define CONFIG_REPL_BACKLOG_MIN_SIZE (1024*16)          /* 16k */\n#define CONFIG_BGSAVE_RETRY_DELAY 5 /* Wait a few secs before trying again. */\n#define CONFIG_DEFAULT_PID_FILE \"/var/run/redis.pid\"\n#define CONFIG_DEFAULT_CLUSTER_CONFIG_FILE \"nodes.conf\"\n#define CONFIG_DEFAULT_UNIX_SOCKET_PERM 0\n#define CONFIG_DEFAULT_LOGFILE \"\"\n#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */\n#define NET_PEER_ID_LEN (NET_IP_STR_LEN+32) /* Must be enough for ip:port */\n#define CONFIG_BINDADDR_MAX 16\n#define CONFIG_MIN_RESERVED_FDS 32\n\n#define ACTIVE_EXPIRE_CYCLE_SLOW 0\n#define ACTIVE_EXPIRE_CYCLE_FAST 1\n\n/* Children process will exit with this status code to signal that the\n * process terminated without an error: this is useful in order to kill\n * a saving child (RDB or AOF one), without triggering in the parent the\n * write protection that is normally turned on on write errors.\n * Usually children that are terminated with SIGUSR1 will exit with this\n * special code. */\n#define SERVER_CHILD_NOERROR_RETVAL    255\n\n/* Instantaneous metrics tracking. */\n#define STATS_METRIC_SAMPLES 16     /* Number of samples per metric. */\n#define STATS_METRIC_COMMAND 0      /* Number of commands executed. */\n#define STATS_METRIC_NET_INPUT 1    /* Bytes read to network .*/\n#define STATS_METRIC_NET_OUTPUT 2   /* Bytes written to network. */\n#define STATS_METRIC_COUNT 3\n\n/* Protocol and I/O related defines */\n#define PROTO_MAX_QUERYBUF_LEN  (1024*1024*1024) /* 1GB max query buffer. */\n#define PROTO_IOBUF_LEN         (1024*16)  /* Generic I/O buffer size */\n#define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */\n#define PROTO_INLINE_MAX_SIZE   (1024*64) /* Max size of inline reads */\n#define PROTO_MBULK_BIG_ARG     (1024*32)\n#define LONG_STR_SIZE      21          /* Bytes needed for long -> str + '\\0' */\n#define REDIS_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */\n\n#define LIMIT_PENDING_QUERYBUF (4*1024*1024) /* 4mb */\n\n/* When configuring the server eventloop, we setup it so that the total number\n * of file descriptors we can handle are server.maxclients + RESERVED_FDS +\n * a few more to stay safe. Since RESERVED_FDS defaults to 32, we add 96\n * in order to make sure of not over provisioning more than 128 fds. */\n#define CONFIG_FDSET_INCR (CONFIG_MIN_RESERVED_FDS+96)\n\n/* Hash table parameters */\n#define HASHTABLE_MIN_FILL        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#define CMD_WRITE (1ULL<<0)            /* \"write\" flag */\n#define CMD_READONLY (1ULL<<1)         /* \"read-only\" flag */\n#define CMD_DENYOOM (1ULL<<2)          /* \"use-memory\" flag */\n#define CMD_MODULE (1ULL<<3)           /* Command exported by module. */\n#define CMD_ADMIN (1ULL<<4)            /* \"admin\" flag */\n#define CMD_PUBSUB (1ULL<<5)           /* \"pub-sub\" flag */\n#define CMD_NOSCRIPT (1ULL<<6)         /* \"no-script\" flag */\n#define CMD_RANDOM (1ULL<<7)           /* \"random\" flag */\n#define CMD_SORT_FOR_SCRIPT (1ULL<<8)  /* \"to-sort\" flag */\n#define CMD_LOADING (1ULL<<9)          /* \"ok-loading\" flag */\n#define CMD_STALE (1ULL<<10)           /* \"ok-stale\" flag */\n#define CMD_SKIP_MONITOR (1ULL<<11)    /* \"no-monitor\" flag */\n#define CMD_SKIP_SLOWLOG (1ULL<<12)    /* \"no-slowlog\" flag */\n#define CMD_ASKING (1ULL<<13)          /* \"cluster-asking\" flag */\n#define CMD_FAST (1ULL<<14)            /* \"fast\" flag */\n#define CMD_NO_AUTH (1ULL<<15)         /* \"no-auth\" flag */\n\n/* Command flags used by the module system. */\n#define CMD_MODULE_GETKEYS (1ULL<<16)  /* Use the modules getkeys interface. */\n#define CMD_MODULE_NO_CLUSTER (1ULL<<17) /* Deny on Redis Cluster. */\n\n/* Command flags that describe ACLs categories. */\n#define CMD_CATEGORY_KEYSPACE (1ULL<<18)\n#define CMD_CATEGORY_READ (1ULL<<19)\n#define CMD_CATEGORY_WRITE (1ULL<<20)\n#define CMD_CATEGORY_SET (1ULL<<21)\n#define CMD_CATEGORY_SORTEDSET (1ULL<<22)\n#define CMD_CATEGORY_LIST (1ULL<<23)\n#define CMD_CATEGORY_HASH (1ULL<<24)\n#define CMD_CATEGORY_STRING (1ULL<<25)\n#define CMD_CATEGORY_BITMAP (1ULL<<26)\n#define CMD_CATEGORY_HYPERLOGLOG (1ULL<<27)\n#define CMD_CATEGORY_GEO (1ULL<<28)\n#define CMD_CATEGORY_STREAM (1ULL<<29)\n#define CMD_CATEGORY_PUBSUB (1ULL<<30)\n#define CMD_CATEGORY_ADMIN (1ULL<<31)\n#define CMD_CATEGORY_FAST (1ULL<<32)\n#define CMD_CATEGORY_SLOW (1ULL<<33)\n#define CMD_CATEGORY_BLOCKING (1ULL<<34)\n#define CMD_CATEGORY_DANGEROUS (1ULL<<35)\n#define CMD_CATEGORY_CONNECTION (1ULL<<36)\n#define CMD_CATEGORY_TRANSACTION (1ULL<<37)\n#define CMD_CATEGORY_SCRIPTING (1ULL<<38)\n\n/* AOF states */\n#define AOF_OFF 0             /* AOF is off */\n#define AOF_ON 1              /* AOF is on */\n#define AOF_WAIT_REWRITE 2    /* AOF waits rewrite to start appending */\n\n/* Client flags */\n#define CLIENT_SLAVE (1<<0)   /* This client is a repliaca */\n#define CLIENT_MASTER (1<<1)  /* This client is a master */\n#define CLIENT_MONITOR (1<<2) /* This client is a slave monitor, see MONITOR */\n#define CLIENT_MULTI (1<<3)   /* This client is in a MULTI context */\n#define CLIENT_BLOCKED (1<<4) /* The client is waiting in a blocking operation */\n#define CLIENT_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */\n#define CLIENT_CLOSE_AFTER_REPLY (1<<6) /* Close after writing entire reply. */\n#define CLIENT_UNBLOCKED (1<<7) /* This client was unblocked and is stored in\n                                  server.unblocked_clients */\n#define CLIENT_LUA (1<<8) /* This is a non connected client used by Lua */\n#define CLIENT_ASKING (1<<9)     /* Client issued the ASKING command */\n#define CLIENT_CLOSE_ASAP (1<<10)/* Close this client ASAP */\n#define CLIENT_UNIX_SOCKET (1<<11) /* Client connected via Unix domain socket */\n#define CLIENT_DIRTY_EXEC (1<<12)  /* EXEC will fail for errors while queueing */\n#define CLIENT_MASTER_FORCE_REPLY (1<<13)  /* Queue replies even if is master */\n#define CLIENT_FORCE_AOF (1<<14)   /* Force AOF propagation of current cmd. */\n#define CLIENT_FORCE_REPL (1<<15)  /* Force replication of current cmd. */\n#define CLIENT_PRE_PSYNC (1<<16)   /* Instance don't understand PSYNC. */\n#define CLIENT_READONLY (1<<17)    /* Cluster client is in read-only state. */\n#define CLIENT_PUBSUB (1<<18)      /* Client is in Pub/Sub mode. */\n#define CLIENT_PREVENT_AOF_PROP (1<<19)  /* Don't propagate to AOF. */\n#define CLIENT_PREVENT_REPL_PROP (1<<20)  /* Don't propagate to slaves. */\n#define CLIENT_PREVENT_PROP (CLIENT_PREVENT_AOF_PROP|CLIENT_PREVENT_REPL_PROP)\n#define CLIENT_PENDING_WRITE (1<<21) /* Client has output to send but a write\n                                        handler is yet not installed. */\n#define CLIENT_REPLY_OFF (1<<22)   /* Don't send replies to client. */\n#define CLIENT_REPLY_SKIP_NEXT (1<<23)  /* Set CLIENT_REPLY_SKIP for next cmd */\n#define CLIENT_REPLY_SKIP (1<<24)  /* Don't send just this reply. */\n#define CLIENT_LUA_DEBUG (1<<25)  /* Run EVAL in debug mode. */\n#define CLIENT_LUA_DEBUG_SYNC (1<<26)  /* EVAL debugging without fork() */\n#define CLIENT_MODULE (1<<27) /* Non connected client used by some module. */\n#define CLIENT_PROTECTED (1<<28) /* Client should not be freed for now. */\n#define CLIENT_PENDING_READ (1<<29) /* The client has pending reads and was put\n                                       in the list of clients we can read\n                                       from. */\n#define CLIENT_PENDING_COMMAND (1<<30) /* Used in threaded I/O to signal after\n                                          we return single threaded that the\n                                          client has already pending commands\n                                          to be executed. */\n#define CLIENT_TRACKING (1ULL<<31) /* Client enabled keys tracking in order to\n                                   perform client side caching. */\n#define CLIENT_TRACKING_BROKEN_REDIR (1ULL<<32) /* Target client is invalid. */\n#define CLIENT_TRACKING_BCAST (1ULL<<33) /* Tracking in BCAST mode. */\n#define CLIENT_TRACKING_OPTIN (1ULL<<34)  /* Tracking in opt-in mode. */\n#define CLIENT_TRACKING_OPTOUT (1ULL<<35) /* Tracking in opt-out mode. */\n#define CLIENT_TRACKING_CACHING (1ULL<<36) /* CACHING yes/no was given,\n                                              depending on optin/optout mode. */\n#define CLIENT_TRACKING_NOLOOP (1ULL<<37) /* Don't send invalidation messages\n                                             about writes performed by myself.*/\n#define CLIENT_IN_TO_TABLE (1ULL<<38) /* This client is in the timeout table. */\n#define CLIENT_PROTOCOL_ERROR (1ULL<<39) /* Protocol error chatting with it. */\n\n/* Client block type (btype field in client structure)\n * if CLIENT_BLOCKED flag is set. */\n#define BLOCKED_NONE 0    /* Not blocked, no CLIENT_BLOCKED flag set. */\n#define BLOCKED_LIST 1    /* BLPOP & co. */\n#define BLOCKED_WAIT 2    /* WAIT for synchronous replication. */\n#define BLOCKED_MODULE 3  /* Blocked by a loadable module. */\n#define BLOCKED_STREAM 4  /* XREAD. */\n#define BLOCKED_ZSET 5    /* BZPOP et al. */\n#define BLOCKED_NUM 6     /* Number of blocked states. */\n\n/* Client request types */\n#define PROTO_REQ_INLINE 1\n#define PROTO_REQ_MULTIBULK 2\n\n/* Client classes for client limits, currently used only for\n * the max-client-output-buffer limit implementation. */\n#define CLIENT_TYPE_NORMAL 0 /* Normal req-reply clients + MONITORs */\n#define CLIENT_TYPE_SLAVE 1  /* Slaves. */\n#define CLIENT_TYPE_PUBSUB 2 /* Clients subscribed to PubSub channels. */\n#define CLIENT_TYPE_MASTER 3 /* Master. */\n#define CLIENT_TYPE_COUNT 4  /* Total number of client types. */\n#define CLIENT_TYPE_OBUF_COUNT 3 /* Number of clients to expose to output\n                                    buffer configuration. Just the first\n                                    three: normal, slave, pubsub. */\n\n/* Slave replication state. Used in server.repl_state for slaves to remember\n * what to do next. */\n#define REPL_STATE_NONE 0 /* No active replication */\n#define REPL_STATE_CONNECT 1 /* Must connect to master */\n#define REPL_STATE_CONNECTING 2 /* Connecting to master */\n/* --- Handshake states, must be ordered --- */\n#define REPL_STATE_RECEIVE_PONG 3 /* Wait for PING reply */\n#define REPL_STATE_SEND_AUTH 4 /* Send AUTH to master */\n#define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */\n#define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */\n#define REPL_STATE_RECEIVE_PORT 7 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_IP 8 /* Send REPLCONF ip-address */\n#define REPL_STATE_RECEIVE_IP 9 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_CAPA 10 /* Send REPLCONF capa */\n#define REPL_STATE_RECEIVE_CAPA 11 /* Wait for REPLCONF reply */\n#define REPL_STATE_SEND_PSYNC 12 /* Send PSYNC */\n#define REPL_STATE_RECEIVE_PSYNC 13 /* Wait for PSYNC reply */\n/* --- End of handshake states --- */\n#define REPL_STATE_TRANSFER 14 /* Receiving .rdb from master */\n#define REPL_STATE_CONNECTED 15 /* Connected to master */\n\n/* State of slaves from the POV of the master. Used in client->replstate.\n * In SEND_BULK and ONLINE state the slave receives new updates\n * in its output queue. In the WAIT_BGSAVE states instead the server is waiting\n * to start the next background saving in order to send updates to it. */\n#define SLAVE_STATE_WAIT_BGSAVE_START 6 /* We need to produce a new RDB file. */\n#define SLAVE_STATE_WAIT_BGSAVE_END 7 /* Waiting RDB file creation to finish. */\n#define SLAVE_STATE_SEND_BULK 8 /* Sending RDB file to slave. */\n#define SLAVE_STATE_ONLINE 9 /* RDB file transmitted, sending just updates. */\n\n/* Slave capabilities. */\n#define SLAVE_CAPA_NONE 0\n#define SLAVE_CAPA_EOF (1<<0)    /* Can parse the RDB EOF streaming format. */\n#define SLAVE_CAPA_PSYNC2 (1<<1) /* Supports PSYNC2 protocol. */\n\n/* Synchronous read timeout - slave side */\n#define CONFIG_REPL_SYNCIO_TIMEOUT 5\n\n/* List related stuff */\n#define LIST_HEAD 0\n#define LIST_TAIL 1\n#define ZSET_MIN 0\n#define ZSET_MAX 1\n\n/* Sort operations */\n#define SORT_OP_GET 0\n\n/* Log levels */\n#define LL_DEBUG 0\n#define LL_VERBOSE 1\n#define LL_NOTICE 2\n#define LL_WARNING 3\n#define LL_RAW (1<<10) /* Modifier to log without timestamp */\n\n/* Supervision options */\n#define SUPERVISED_NONE 0\n#define SUPERVISED_AUTODETECT 1\n#define SUPERVISED_SYSTEMD 2\n#define SUPERVISED_UPSTART 3\n\n/* Anti-warning macro... */\n#define UNUSED(V) ((void) V)\n\n#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 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\n/* Replication diskless load defines */\n#define REPL_DISKLESS_LOAD_DISABLED 0\n#define REPL_DISKLESS_LOAD_WHEN_DB_EMPTY 1\n#define REPL_DISKLESS_LOAD_SWAPDB 2\n\n/* Sets operations codes */\n#define SET_OP_UNION 0\n#define SET_OP_DIFF 1\n#define SET_OP_INTER 2\n\n/* Redis maxmemory strategies. Instead of using just incremental number\n * for this defines, we use a set of flags so that testing for certain\n * properties common to multiple policies is faster. */\n#define MAXMEMORY_FLAG_LRU (1<<0)\n#define MAXMEMORY_FLAG_LFU (1<<1)\n#define MAXMEMORY_FLAG_ALLKEYS (1<<2)\n#define MAXMEMORY_FLAG_NO_SHARED_INTEGERS \\\n    (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)\n\n#define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU)\n#define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU)\n#define MAXMEMORY_VOLATILE_TTL (2<<8)\n#define MAXMEMORY_VOLATILE_RANDOM (3<<8)\n#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_ALLKEYS)\n#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU|MAXMEMORY_FLAG_ALLKEYS)\n#define MAXMEMORY_ALLKEYS_RANDOM ((6<<8)|MAXMEMORY_FLAG_ALLKEYS)\n#define MAXMEMORY_NO_EVICTION (7<<8)\n\n/* Units */\n#define UNIT_SECONDS 0\n#define UNIT_MILLISECONDS 1\n\n/* SHUTDOWN flags */\n#define SHUTDOWN_NOFLAGS 0      /* No flags. */\n#define SHUTDOWN_SAVE 1         /* Force SAVE on SHUTDOWN even if no save\n                                   points are configured. */\n#define SHUTDOWN_NOSAVE 2       /* Don't SAVE on SHUTDOWN. */\n\n/* Command call flags, see call() function */\n#define CMD_CALL_NONE 0\n#define CMD_CALL_SLOWLOG (1<<0)\n#define CMD_CALL_STATS (1<<1)\n#define CMD_CALL_PROPAGATE_AOF (1<<2)\n#define CMD_CALL_PROPAGATE_REPL (1<<3)\n#define CMD_CALL_PROPAGATE (CMD_CALL_PROPAGATE_AOF|CMD_CALL_PROPAGATE_REPL)\n#define CMD_CALL_FULL (CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_PROPAGATE)\n#define CMD_CALL_NOWRAP (1<<4)  /* Don't wrap also propagate array into\n                                   MULTI/EXEC: the caller will handle it.  */\n\n/* Command propagation flags, see propagate() function */\n#define PROPAGATE_NONE 0\n#define PROPAGATE_AOF 1\n#define PROPAGATE_REPL 2\n\n/* RDB active child save type. */\n#define RDB_CHILD_TYPE_NONE 0\n#define RDB_CHILD_TYPE_DISK 1     /* RDB is written to disk. */\n#define RDB_CHILD_TYPE_SOCKET 2   /* RDB is written to slave socket. */\n\n/* Keyspace changes notification classes. Every class is associated with a\n * character for configuration purposes. */\n#define NOTIFY_KEYSPACE (1<<0)    /* K */\n#define NOTIFY_KEYEVENT (1<<1)    /* E */\n#define NOTIFY_GENERIC (1<<2)     /* g */\n#define NOTIFY_STRING (1<<3)      /* $ */\n#define NOTIFY_LIST (1<<4)        /* l */\n#define NOTIFY_SET (1<<5)         /* s */\n#define NOTIFY_HASH (1<<6)        /* h */\n#define NOTIFY_ZSET (1<<7)        /* z */\n#define NOTIFY_EXPIRED (1<<8)     /* x */\n#define NOTIFY_EVICTED (1<<9)     /* e */\n#define NOTIFY_STREAM (1<<10)     /* t */\n#define NOTIFY_KEY_MISS (1<<11)   /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */\n#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM) /* A flag */\n\n/* Get the first bind addr or NULL */\n#define NET_FIRST_BIND_ADDR (server.bindaddr_count ? server.bindaddr[0] : NULL)\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 serverAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_serverAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))\n#define serverAssert(_e) ((_e)?(void)0 : (_serverAssert(#_e,__FILE__,__LINE__),_exit(1)))\n#define serverPanic(...) _serverPanic(__FILE__,__LINE__,__VA_ARGS__),_exit(1)\n\n/*-----------------------------------------------------------------------------\n * Data types\n *----------------------------------------------------------------------------*/\n\n/* A redis object, that is a type able to hold a string / list / set */\n\n/* The actual Redis Object */\n#define OBJ_STRING 0    /* String object. */\n#define OBJ_LIST 1      /* List object. */\n#define OBJ_SET 2       /* Set object. */\n#define OBJ_ZSET 3      /* Sorted set object. */\n#define OBJ_HASH 4      /* Hash object. */\n\n/* The \"module\" object type is a special one that signals that the object\n * is one directly managed by a Redis module. In this case the value points\n * to a moduleValue struct, which contains the object value (which is only\n * handled by the module itself) and the RedisModuleType struct which lists\n * function pointers in order to serialize, deserialize, AOF-rewrite and\n * free the object.\n *\n * Inside the RDB file, module types are encoded as OBJ_MODULE followed\n * by a 64 bit module type ID, which has a 54 bits module-specific signature\n * in order to dispatch the loading to the right module, plus a 10 bits\n * encoding version. */\n#define OBJ_MODULE 5    /* Module object. */\n#define OBJ_STREAM 6    /* Stream object. */\n\n/* Extract encver / signature from a module type ID. */\n#define REDISMODULE_TYPE_ENCVER_BITS 10\n#define REDISMODULE_TYPE_ENCVER_MASK ((1<<REDISMODULE_TYPE_ENCVER_BITS)-1)\n#define REDISMODULE_TYPE_ENCVER(id) (id & REDISMODULE_TYPE_ENCVER_MASK)\n#define REDISMODULE_TYPE_SIGN(id) ((id & ~((uint64_t)REDISMODULE_TYPE_ENCVER_MASK)) >>REDISMODULE_TYPE_ENCVER_BITS)\n\n/* Bit flags for moduleTypeAuxSaveFunc */\n#define REDISMODULE_AUX_BEFORE_RDB (1<<0)\n#define REDISMODULE_AUX_AFTER_RDB (1<<1)\n\nstruct RedisModule;\nstruct RedisModuleIO;\nstruct RedisModuleDigest;\nstruct RedisModuleCtx;\nstruct redisObject;\n\n/* Each module type implementation should export a set of methods in order\n * to serialize and deserialize the value in the RDB file, rewrite the AOF\n * log, create the digest for \"DEBUG DIGEST\", and free the value when a key\n * is deleted. */\ntypedef void *(*moduleTypeLoadFunc)(struct RedisModuleIO *io, int encver);\ntypedef void (*moduleTypeSaveFunc)(struct RedisModuleIO *io, void *value);\ntypedef int (*moduleTypeAuxLoadFunc)(struct RedisModuleIO *rdb, int encver, int when);\ntypedef void (*moduleTypeAuxSaveFunc)(struct RedisModuleIO *rdb, int when);\ntypedef void (*moduleTypeRewriteFunc)(struct RedisModuleIO *io, struct redisObject *key, void *value);\ntypedef void (*moduleTypeDigestFunc)(struct RedisModuleDigest *digest, void *value);\ntypedef size_t (*moduleTypeMemUsageFunc)(const void *value);\ntypedef void (*moduleTypeFreeFunc)(void *value);\n\n/* A callback that is called when the client authentication changes. This\n * needs to be exposed since you can't cast a function pointer to (void *) */\ntypedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);\n\n\n/* The module type, which is referenced in each value of a given type, defines\n * the methods and links to the module exporting the type. */\ntypedef struct RedisModuleType {\n    uint64_t id; /* Higher 54 bits of type ID + 10 lower bits of encoding ver. */\n    struct RedisModule *module;\n    moduleTypeLoadFunc rdb_load;\n    moduleTypeSaveFunc rdb_save;\n    moduleTypeRewriteFunc aof_rewrite;\n    moduleTypeMemUsageFunc mem_usage;\n    moduleTypeDigestFunc digest;\n    moduleTypeFreeFunc free;\n    moduleTypeAuxLoadFunc aux_load;\n    moduleTypeAuxSaveFunc aux_save;\n    int aux_save_triggers;\n    char name[10]; /* 9 bytes name + null term. Charset: A-Z a-z 0-9 _- */\n} moduleType;\n\n/* In Redis objects 'robj' structures of type OBJ_MODULE, the value pointer\n * is set to the following structure, referencing the moduleType structure\n * in order to work with the value, and at the same time providing a raw\n * pointer to the value, as created by the module commands operating with\n * the module type.\n *\n * So for example in order to free such a value, it is possible to use\n * the following code:\n *\n *  if (robj->type == OBJ_MODULE) {\n *      moduleValue *mt = robj->ptr;\n *      mt->type->free(mt->value);\n *      zfree(mt); // We need to release this in-the-middle struct as well.\n *  }\n */\ntypedef struct moduleValue {\n    moduleType *type;\n    void *value;\n} moduleValue;\n\n/* This is a wrapper for the 'rio' streams used inside rdb.c in Redis, so that\n * the user does not have to take the total count of the written bytes nor\n * to care about error conditions. */\ntypedef struct RedisModuleIO {\n    size_t bytes;       /* Bytes read / written so far. */\n    rio *rio;           /* Rio stream. */\n    moduleType *type;   /* Module type doing the operation. */\n    int error;          /* True if error condition happened. */\n    int ver;            /* Module serialization version: 1 (old),\n                         * 2 (current version with opcodes annotation). */\n    struct RedisModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/\n    struct redisObject *key;    /* Optional name of key processed */\n} RedisModuleIO;\n\n/* Macro to initialize an IO context. Note that the 'ver' field is populated\n * inside rdb.c according to the version of the value to load. */\n#define moduleInitIOContext(iovar,mtype,rioptr,keyptr) do { \\\n    iovar.rio = rioptr; \\\n    iovar.type = mtype; \\\n    iovar.bytes = 0; \\\n    iovar.error = 0; \\\n    iovar.ver = 0; \\\n    iovar.key = keyptr; \\\n    iovar.ctx = NULL; \\\n} while(0);\n\n/* This is a structure used to export DEBUG DIGEST capabilities to Redis\n * modules. We want to capture both the ordered and unordered elements of\n * a data structure, so that a digest can be created in a way that correctly\n * reflects the values. See the DEBUG DIGEST command implementation for more\n * background. */\ntypedef struct RedisModuleDigest {\n    unsigned char o[20];    /* Ordered elements. */\n    unsigned char x[20];    /* Xored elements. */\n} RedisModuleDigest;\n\n/* Just start with a digest composed of all zero bytes. */\n#define moduleInitDigestContext(mdvar) do { \\\n    memset(mdvar.o,0,sizeof(mdvar.o)); \\\n    memset(mdvar.x,0,sizeof(mdvar.x)); \\\n} while(0);\n\n/* Objects encoding. Some kind of objects like Strings and Hashes can be\n * internally represented in multiple ways. The 'encoding' field of the object\n * is set to one of this fields for this object. */\n#define OBJ_ENCODING_RAW 0     /* Raw representation */\n#define OBJ_ENCODING_INT 1     /* Encoded as integer */\n#define OBJ_ENCODING_HT 2      /* Encoded as hash table */\n#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */\n#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */\n#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */\n#define OBJ_ENCODING_INTSET 6  /* Encoded as intset */\n#define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */\n#define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */\n#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */\n#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */\n\n#define LRU_BITS 24\n#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */\n#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */\n\n#define OBJ_SHARED_REFCOUNT INT_MAX     /* Global object never destroyed. */\n#define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */\n#define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT\ntypedef struct redisObject {\n    unsigned type:4;\n    unsigned encoding:4;\n    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or\n                            * LFU data (least significant 8 bits frequency\n                            * and most significant 16 bits access time). */\n    int refcount;\n    void *ptr;\n} robj;\n\n/* The a string name for an object's type as listed above\n * Native types are checked against the OBJ_STRING, OBJ_LIST, OBJ_* defines,\n * and Module types have their registered name returned. */\nchar *getObjectTypeName(robj*);\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 = OBJ_STATIC_REFCOUNT; \\\n    _var.type = OBJ_STRING; \\\n    _var.encoding = OBJ_ENCODING_RAW; \\\n    _var.ptr = _ptr; \\\n} while(0)\n\nstruct evictionPoolEntry; /* Defined in evict.c */\n\n/* This structure is used in order to represent the output buffer of a client,\n * which is actually a linked list of blocks like that, that is: client->reply. */\ntypedef struct clientReplyBlock {\n    size_t size, used;\n    char buf[];\n} clientReplyBlock;\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    dict *dict;                 /* The keyspace for this DB */\n    dict *expires;              /* Timeout of keys with a timeout set */\n    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/\n    dict *ready_keys;           /* Blocked keys that received a PUSH */\n    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */\n    int id;                     /* Database ID */\n    long long avg_ttl;          /* Average TTL, just for stats */\n    unsigned long expires_cursor; /* Cursor of the active expire cycle. */\n    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */\n} redisDb;\n\n/* Client MULTI/EXEC state */\ntypedef struct multiCmd {\n    robj **argv;\n    int argc;\n    struct redisCommand *cmd;\n} multiCmd;\n\ntypedef struct multiState {\n    multiCmd *commands;     /* Array of MULTI commands */\n    int count;              /* Total number of MULTI commands */\n    int cmd_flags;          /* The accumulated command flags OR-ed together.\n                               So if at least a command has a given flag, it\n                               will be set in this field. */\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. */\ntypedef struct blockingState {\n    /* Generic fields. */\n    mstime_t timeout;       /* Blocking operation timeout. If UNIX current time\n                             * is > timeout then the operation timed out. */\n\n    /* BLOCKED_LIST, BLOCKED_ZSET and BLOCKED_STREAM */\n    dict *keys;             /* The keys we are waiting to terminate a blocking\n                             * operation such as BLPOP or XREAD. Or NULL. */\n    robj *target;           /* The key that should receive the element,\n                             * for BRPOPLPUSH. */\n\n    /* BLOCK_STREAM */\n    size_t xread_count;     /* XREAD COUNT option. */\n    robj *xread_group;      /* XREADGROUP group name. */\n    robj *xread_consumer;   /* XREADGROUP consumer name. */\n    mstime_t xread_retry_time, xread_retry_ttl;\n    int xread_group_noack;\n\n    /* BLOCKED_WAIT */\n    int numreplicas;        /* Number of replicas we are waiting for ACK. */\n    long long reploffset;   /* Replication offset to reach. */\n\n    /* BLOCKED_MODULE */\n    void *module_blocked_handle; /* RedisModuleBlockedClient structure.\n                                    which is opaque for the Redis core, only\n                                    handled in module.c. */\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. */\ntypedef struct readyList {\n    redisDb *db;\n    robj *key;\n} readyList;\n\n/* This structure represents a Redis user. This is useful for ACLs, the\n * user is associated to the connection after the connection is authenticated.\n * If there is no associated user, the connection uses the default user. */\n#define USER_COMMAND_BITS_COUNT 1024    /* The total number of command bits\n                                           in the user structure. The last valid\n                                           command ID we can set in the user\n                                           is USER_COMMAND_BITS_COUNT-1. */\n#define USER_FLAG_ENABLED (1<<0)        /* The user is active. */\n#define USER_FLAG_DISABLED (1<<1)       /* The user is disabled. */\n#define USER_FLAG_ALLKEYS (1<<2)        /* The user can mention any key. */\n#define USER_FLAG_ALLCOMMANDS (1<<3)    /* The user can run all commands. */\n#define USER_FLAG_NOPASS      (1<<4)    /* The user requires no password, any\n                                           provided password will work. For the\n                                           default user, this also means that\n                                           no AUTH is needed, and every\n                                           connection is immediately\n                                           authenticated. */\ntypedef struct {\n    sds name;       /* The username as an SDS string. */\n    uint64_t flags; /* See USER_FLAG_* */\n\n    /* The bit in allowed_commands is set if this user has the right to\n     * execute this command. In commands having subcommands, if this bit is\n     * set, then all the subcommands are also available.\n     *\n     * If the bit for a given command is NOT set and the command has\n     * subcommands, Redis will also check allowed_subcommands in order to\n     * understand if the command can be executed. */\n    uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64];\n\n    /* This array points, for each command ID (corresponding to the command\n     * bit set in allowed_commands), to an array of SDS strings, terminated by\n     * a NULL pointer, with all the sub commands that can be executed for\n     * this command. When no subcommands matching is used, the field is just\n     * set to NULL to avoid allocating USER_COMMAND_BITS_COUNT pointers. */\n    sds **allowed_subcommands;\n    list *passwords; /* A list of SDS valid passwords for this user. */\n    list *patterns;  /* A list of allowed key patterns. If this field is NULL\n                        the user cannot mention any key in a command, unless\n                        the flag ALLKEYS is set in the user. */\n} user;\n\n/* With multiplexing we need to take per-client state.\n * Clients are taken in a linked list. */\n\n#define CLIENT_ID_AOF (UINT64_MAX) /* Reserved ID for the AOF client. If you\n                                      need more reserved IDs use UINT64_MAX-1,\n                                      -2, ... and so forth. */\n\ntypedef struct client {\n    uint64_t id;            /* Client incremental unique ID. */\n    connection *conn;\n    int resp;               /* RESP protocol version. Can be 2 or 3. */\n    redisDb *db;            /* Pointer to currently SELECTed DB. */\n    robj *name;             /* As set by CLIENT SETNAME. */\n    sds querybuf;           /* Buffer we use to accumulate client queries. */\n    size_t qb_pos;          /* The position we have read in querybuf. */\n    sds pending_querybuf;   /* If this client is flagged as master, this buffer\n                               represents the yet not applied portion of the\n                               replication stream that we are receiving from\n                               the master. */\n    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size. */\n    int argc;               /* Num of arguments of current command. */\n    robj **argv;            /* Arguments of current command. */\n    struct redisCommand *cmd, *lastcmd;  /* Last command executed. */\n    user *user;             /* User associated with this connection. If the\n                               user is set to NULL the connection can do\n                               anything (admin). */\n    int reqtype;            /* Request protocol type: PROTO_REQ_* */\n    int multibulklen;       /* Number of multi bulk arguments left to read. */\n    long bulklen;           /* Length of bulk argument in multi bulk request. */\n    list *reply;            /* List of reply objects to send to the client. */\n    unsigned long long reply_bytes; /* Tot bytes of objects in reply list. */\n    size_t sentlen;         /* Amount of bytes already sent in the current\n                               buffer or object being sent. */\n    time_t ctime;           /* Client creation time. */\n    time_t lastinteraction; /* Time of the last interaction, used for timeout */\n    time_t obuf_soft_limit_reached_time;\n    uint64_t flags;         /* Client flags: CLIENT_* macros. */\n    int authenticated;      /* Needed when the default user requires auth. */\n    int replstate;          /* Replication state if this is a slave. */\n    int repl_put_online_on_ack; /* Install slave write handler on first ACK. */\n    int repldbfd;           /* Replication DB file descriptor. */\n    off_t repldboff;        /* Replication DB file offset. */\n    off_t repldbsize;       /* Replication DB file size. */\n    sds replpreamble;       /* Replication DB preamble. */\n    long long read_reploff; /* Read replication offset if this is a master. */\n    long long reploff;      /* Applied replication offset if this is a master. */\n    long long repl_ack_off; /* Replication ack offset, if this is a slave. */\n    long long repl_ack_time;/* Replication ack time, if this is a slave. */\n    long long psync_initial_offset; /* FULLRESYNC reply offset other slaves\n                                       copying this slave output buffer\n                                       should use. */\n    char replid[CONFIG_RUN_ID_SIZE+1]; /* Master replication ID (if master). */\n    int slave_listening_port; /* As configured with: SLAVECONF listening-port */\n    char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */\n    int slave_capa;         /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */\n    multiState mstate;      /* MULTI/EXEC state */\n    int btype;              /* Type of blocking op if CLIENT_BLOCKED. */\n    blockingState bpop;     /* blocking state */\n    long long woff;         /* Last write global replication offset. */\n    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS */\n    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) */\n    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) */\n    sds peerid;             /* Cached peer ID. */\n    listNode *client_list_node; /* list node in client list */\n    RedisModuleUserChangedFunc auth_callback; /* Module callback to execute\n                                               * when the authenticated user\n                                               * changes. */\n    void *auth_callback_privdata; /* Private data that is passed when the auth\n                                   * changed callback is executed. Opaque for\n                                   * Redis Core. */\n    void *auth_module;      /* The module that owns the callback, which is used\n                             * to disconnect the client if the module is\n                             * unloaded for cleanup. Opaque for Redis Core.*/\n\n    /* If this client is in tracking mode and this field is non zero,\n     * invalidation messages for keys fetched by this client will be send to\n     * the specified client ID. */\n    uint64_t client_tracking_redirection;\n    rax *client_tracking_prefixes; /* A dictionary of prefixes we are already\n                                      subscribed to in BCAST mode, in the\n                                      context of client side caching. */\n    /* In clientsCronTrackClientsMemUsage() we track the memory usage of\n     * each client and add it to the sum of all the clients of a given type,\n     * however we need to remember what was the old contribution of each\n     * client, and in which categoty the client was, in order to remove it\n     * before adding it the new value. */\n    uint64_t client_cron_last_memory_usage;\n    int      client_cron_last_memory_type;\n    /* Response buffer */\n    int bufpos;\n    char buf[PROTO_REPLY_CHUNK_BYTES];\n} client;\n\nstruct saveparam {\n    time_t seconds;\n    int changes;\n};\n\nstruct moduleLoadQueueEntry {\n    sds path;\n    int argc;\n    robj **argv;\n};\n\nstruct sharedObjectsStruct {\n    robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,\n    *colon, *queued, *null[4], *nullarray[4], *emptymap[4], *emptyset[4],\n    *emptyarray, *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, *unlink,\n    *rpop, *lpop, *lpush, *rpoplpush, *zpopmin, *zpopmax, *emptyscan,\n    *multi, *exec,\n    *select[PROTO_SHARED_SELECT_CMDS],\n    *integers[OBJ_SHARED_INTEGERS],\n    *mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* \"*<value>\\r\\n\" */\n    *bulkhdr[OBJ_SHARED_BULKHDR_LEN];  /* \"$<value>\\r\\n\" */\n    sds minstring, maxstring;\n};\n\n/* ZSETs use a specialized version of Skiplists */\ntypedef struct zskiplistNode {\n    sds ele;\n    double score;\n    struct zskiplistNode *backward;\n    struct zskiplistLevel {\n        struct zskiplistNode *forward;\n        unsigned long span;\n    } level[];\n} zskiplistNode;\n\ntypedef struct zskiplist {\n    struct zskiplistNode *header, *tail;\n    unsigned long length;\n    int level;\n} zskiplist;\n\ntypedef struct zset {\n    dict *dict;\n    zskiplist *zsl;\n} zset;\n\ntypedef struct clientBufferLimitsConfig {\n    unsigned long long hard_limit_bytes;\n    unsigned long long soft_limit_bytes;\n    time_t soft_limit_seconds;\n} clientBufferLimitsConfig;\n\nextern clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT];\n\n/* The redisOp structure defines a Redis Operation, that is an instance of\n * a command with an argument vector, database ID, propagation target\n * (PROPAGATE_*), and command pointer.\n *\n * Currently only used to additionally propagate more commands to AOF/Replication\n * after the propagation of the executed command. */\ntypedef struct redisOp {\n    robj **argv;\n    int argc, dbid, target;\n    struct redisCommand *cmd;\n} redisOp;\n\n/* Defines an array of Redis operations. There is an API to add to this\n * structure in a easy way.\n *\n * redisOpArrayInit();\n * redisOpArrayAppend();\n * redisOpArrayFree();\n */\ntypedef struct redisOpArray {\n    redisOp *ops;\n    int numops;\n} redisOpArray;\n\n/* This structure is returned by the getMemoryOverheadData() function in\n * order to return memory overhead information. */\nstruct redisMemOverhead {\n    size_t peak_allocated;\n    size_t total_allocated;\n    size_t startup_allocated;\n    size_t repl_backlog;\n    size_t clients_slaves;\n    size_t clients_normal;\n    size_t aof_buffer;\n    size_t lua_caches;\n    size_t overhead_total;\n    size_t dataset;\n    size_t total_keys;\n    size_t bytes_per_key;\n    float dataset_perc;\n    float peak_perc;\n    float total_frag;\n    ssize_t total_frag_bytes;\n    float allocator_frag;\n    ssize_t allocator_frag_bytes;\n    float allocator_rss;\n    ssize_t allocator_rss_bytes;\n    float rss_extra;\n    size_t rss_extra_bytes;\n    size_t num_dbs;\n    struct {\n        size_t dbid;\n        size_t overhead_ht_main;\n        size_t overhead_ht_expires;\n    } *db;\n};\n\n/* This structure can be optionally passed to RDB save/load functions in\n * order to implement additional functionalities, by storing and loading\n * metadata to the RDB file.\n *\n * Currently the only use is to select a DB at load time, useful in\n * replication in order to make sure that chained slaves (slaves of slaves)\n * select the correct DB and are able to accept the stream coming from the\n * top-level master. */\ntypedef struct rdbSaveInfo {\n    /* Used saving and loading. */\n    int repl_stream_db;  /* DB to select in server.master client. */\n\n    /* Used only loading. */\n    int repl_id_is_set;  /* True if repl_id field is set. */\n    char repl_id[CONFIG_RUN_ID_SIZE+1];     /* Replication ID. */\n    long long repl_offset;                  /* Replication offset. */\n} rdbSaveInfo;\n\n#define RDB_SAVE_INFO_INIT {-1,0,\"000000000000000000000000000000\",-1}\n\nstruct malloc_stats {\n    size_t zmalloc_used;\n    size_t process_rss;\n    size_t allocator_allocated;\n    size_t allocator_active;\n    size_t allocator_resident;\n};\n\n/*-----------------------------------------------------------------------------\n * TLS Context Configuration\n *----------------------------------------------------------------------------*/\n\ntypedef struct redisTLSContextConfig {\n    char *cert_file;\n    char *key_file;\n    char *dh_params_file;\n    char *ca_cert_file;\n    char *ca_cert_dir;\n    char *protocols;\n    char *ciphers;\n    char *ciphersuites;\n    int prefer_server_ciphers;\n} redisTLSContextConfig;\n\n/*-----------------------------------------------------------------------------\n * Global server state\n *----------------------------------------------------------------------------*/\n\nstruct clusterState;\n\n/* AIX defines hz to __hz, we don't use this define and in order to allow\n * Redis build on AIX we need to undef it. */\n#ifdef _AIX\n#undef hz\n#endif\n\n#define CHILD_INFO_MAGIC 0xC17DDA7A12345678LL\n#define CHILD_INFO_TYPE_RDB 0\n#define CHILD_INFO_TYPE_AOF 1\n#define CHILD_INFO_TYPE_MODULE 3\n\nstruct redisServer {\n    /* General */\n    pid_t pid;                  /* Main process pid. */\n    char *configfile;           /* Absolute config file path, or NULL */\n    char *executable;           /* Absolute executable file path. */\n    char **exec_argv;           /* Executable argv vector (copy). */\n    int dynamic_hz;             /* Change hz value depending on # of clients. */\n    int config_hz;              /* Configured HZ value. May be different than\n                                   the actual 'hz' field value if dynamic-hz\n                                   is enabled. */\n    int hz;                     /* serverCron() calls frequency in hertz */\n    redisDb *db;\n    dict *commands;             /* Command table */\n    dict *orig_commands;        /* Command table before command renaming. */\n    aeEventLoop *el;\n    _Atomic unsigned int lruclock; /* Clock for LRU eviction */\n    int shutdown_asap;          /* SHUTDOWN needed ASAP */\n    int activerehashing;        /* Incremental rehash in serverCron() */\n    int active_defrag_running;  /* Active defragmentation running (holds current scan aggressiveness) */\n    char *pidfile;              /* PID file path */\n    int arch_bits;              /* 32 or 64 depending on sizeof(long) */\n    int cronloops;              /* Number of times the cron function run */\n    char runid[CONFIG_RUN_ID_SIZE+1];  /* ID always different at every exec. */\n    int sentinel_mode;          /* True if this instance is a Sentinel. */\n    size_t initial_memory_usage; /* Bytes used after initialization. */\n    int always_show_logo;       /* Show logo even for non-stdout logging. */\n    /* Modules */\n    dict *moduleapi;            /* Exported core APIs dictionary for modules. */\n    dict *sharedapi;            /* Like moduleapi but containing the APIs that\n                                   modules share with each other. */\n    list *loadmodule_queue;     /* List of modules to load at startup. */\n    int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a\n                                   client blocked on a module command needs\n                                   to be processed. */\n    pid_t module_child_pid;     /* PID of module child */\n    /* Networking */\n    int port;                   /* TCP listening port */\n    int tls_port;               /* TLS listening port */\n    int tcp_backlog;            /* TCP listen() backlog */\n    char *bindaddr[CONFIG_BINDADDR_MAX]; /* Addresses we should bind to */\n    int bindaddr_count;         /* Number of addresses in server.bindaddr[] */\n    char *unixsocket;           /* UNIX socket path */\n    mode_t unixsocketperm;      /* UNIX socket permission */\n    int ipfd[CONFIG_BINDADDR_MAX]; /* TCP socket file descriptors */\n    int ipfd_count;             /* Used slots in ipfd[] */\n    int tlsfd[CONFIG_BINDADDR_MAX]; /* TLS socket file descriptors */\n    int tlsfd_count;            /* Used slots in tlsfd[] */\n    int sofd;                   /* Unix socket file descriptor */\n    int cfd[CONFIG_BINDADDR_MAX];/* Cluster bus listening socket */\n    int cfd_count;              /* Used slots in cfd[] */\n    list *clients;              /* List of active clients */\n    list *clients_to_close;     /* Clients to close asynchronously */\n    list *clients_pending_write; /* There is to write or install handler. */\n    list *clients_pending_read;  /* Client has pending read socket buffers. */\n    list *slaves, *monitors;    /* List of slaves and MONITORs */\n    client *current_client;     /* Current client executing the command. */\n    rax *clients_timeout_table; /* Radix tree for blocked clients timeouts. */\n    long fixed_time_expire;     /* If > 0, expire keys against server.mstime. */\n    rax *clients_index;         /* Active clients dictionary by client ID. */\n    int clients_paused;         /* True if clients are currently paused */\n    mstime_t clients_pause_end_time; /* Time when we undo clients_paused */\n    char neterr[ANET_ERR_LEN];   /* Error buffer for anet.c */\n    dict *migrate_cached_sockets;/* MIGRATE cached sockets */\n    _Atomic uint64_t next_client_id; /* Next client unique ID. Incremental. */\n    int protected_mode;         /* Don't accept external connections. */\n    int gopher_enabled;         /* If true the server will reply to gopher\n                                   queries. Will still serve RESP2 queries. */\n    int io_threads_num;         /* Number of IO threads to use. */\n    int io_threads_do_reads;    /* Read and parse from IO threads? */\n    long long events_processed_while_blocked; /* processEventsWhileBlocked() */\n\n    /* RDB / AOF loading information */\n    int loading;                /* We are loading data from disk if true */\n    off_t loading_total_bytes;\n    off_t loading_loaded_bytes;\n    time_t loading_start_time;\n    off_t loading_process_events_interval_bytes;\n    /* Fast pointers to often looked up command */\n    struct redisCommand *delCommand, *multiCommand, *lpushCommand,\n                        *lpopCommand, *rpopCommand, *zpopminCommand,\n                        *zpopmaxCommand, *sremCommand, *execCommand,\n                        *expireCommand, *pexpireCommand, *xclaimCommand,\n                        *xgroupCommand, *rpoplpushCommand;\n    /* Fields used only for stats */\n    time_t stat_starttime;          /* Server start time */\n    long long stat_numcommands;     /* Number of processed commands */\n    long long stat_numconnections;  /* Number of connections received */\n    long long stat_expiredkeys;     /* Number of expired keys */\n    double stat_expired_stale_perc; /* Percentage of keys probably expired */\n    long long stat_expired_time_cap_reached_count; /* Early expire cylce stops.*/\n    long long stat_expire_cycle_time_used; /* Cumulative microseconds used. */\n    long long stat_evictedkeys;     /* Number of evicted keys (maxmemory) */\n    long long stat_keyspace_hits;   /* Number of successful lookups of keys */\n    long long stat_keyspace_misses; /* Number of failed lookups of keys */\n    long long stat_active_defrag_hits;      /* number of allocations moved */\n    long long stat_active_defrag_misses;    /* number of allocations scanned but not moved */\n    long long stat_active_defrag_key_hits;  /* number of keys with moved allocations */\n    long long stat_active_defrag_key_misses;/* number of keys scanned and not moved */\n    long long stat_active_defrag_scanned;   /* number of dictEntries scanned */\n    size_t stat_peak_memory;        /* Max used memory record */\n    long long stat_fork_time;       /* Time needed to perform latest fork() */\n    double stat_fork_rate;          /* Fork rate in GB/sec. */\n    long long stat_rejected_conn;   /* Clients rejected because of maxclients */\n    long long stat_sync_full;       /* Number of full resyncs with slaves. */\n    long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */\n    long long stat_sync_partial_err;/* Number of unaccepted PSYNC requests. */\n    list *slowlog;                  /* SLOWLOG list of commands */\n    long long slowlog_entry_id;     /* SLOWLOG current entry ID */\n    long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */\n    unsigned long slowlog_max_len;     /* SLOWLOG max number of items logged */\n    struct malloc_stats cron_malloc_stats; /* sampled in serverCron(). */\n    _Atomic long long stat_net_input_bytes; /* Bytes read from network. */\n    _Atomic long long stat_net_output_bytes; /* Bytes written to network. */\n    size_t stat_rdb_cow_bytes;      /* Copy on write bytes during RDB saving. */\n    size_t stat_aof_cow_bytes;      /* Copy on write bytes during AOF rewrite. */\n    size_t stat_module_cow_bytes;   /* Copy on write bytes during module fork. */\n    uint64_t stat_clients_type_memory[CLIENT_TYPE_COUNT];/* Mem usage by type */\n    long long stat_unexpected_error_replies; /* Number of unexpected (aof-loading, replica to master, etc.) error replies */\n    /* The following two are used to track instantaneous metrics, like\n     * number of operations per second, network traffic. */\n    struct {\n        long long last_sample_time; /* Timestamp of last sample in ms */\n        long long last_sample_count;/* Count in last sample */\n        long long samples[STATS_METRIC_SAMPLES];\n        int idx;\n    } inst_metric[STATS_METRIC_COUNT];\n    /* Configuration */\n    int verbosity;                  /* Loglevel in redis.conf */\n    int maxidletime;                /* Client timeout in seconds */\n    int tcpkeepalive;               /* Set SO_KEEPALIVE if non-zero. */\n    int active_expire_enabled;      /* Can be disabled for testing purposes. */\n    int active_expire_effort;       /* From 1 (default) to 10, active effort. */\n    int active_defrag_enabled;\n    int jemalloc_bg_thread;         /* Enable jemalloc background thread */\n    size_t active_defrag_ignore_bytes; /* minimum amount of fragmentation waste to start active defrag */\n    int active_defrag_threshold_lower; /* minimum percentage of fragmentation to start active defrag */\n    int active_defrag_threshold_upper; /* maximum percentage of fragmentation at which we use maximum effort */\n    int active_defrag_cycle_min;       /* minimal effort for defrag in CPU percentage */\n    int active_defrag_cycle_max;       /* maximal effort for defrag in CPU percentage */\n    unsigned long active_defrag_max_scan_fields; /* maximum number of fields of set/hash/zset/list to process from within the main dict scan */\n    _Atomic size_t client_max_querybuf_len; /* Limit for client query buffer length */\n    int dbnum;                      /* Total number of configured DBs */\n    int supervised;                 /* 1 if supervised, 0 otherwise. */\n    int supervised_mode;            /* See SUPERVISED_* */\n    int daemonize;                  /* True if running as a daemon */\n    clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT];\n    /* AOF persistence */\n    int aof_enabled;                /* AOF configuration */\n    int aof_state;                  /* AOF_(ON|OFF|WAIT_REWRITE) */\n    int aof_fsync;                  /* Kind of fsync() policy */\n    char *aof_filename;             /* Name of the AOF file */\n    int aof_no_fsync_on_rewrite;    /* Don't fsync if a rewrite is in prog. */\n    int aof_rewrite_perc;           /* Rewrite AOF if % growth is > M and... */\n    off_t aof_rewrite_min_size;     /* the AOF file is at least N bytes. */\n    off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */\n    off_t aof_current_size;         /* AOF current size. */\n    off_t aof_fsync_offset;         /* AOF offset which is already synced to disk. */\n    int aof_flush_sleep;            /* Micros to sleep before flush. (used by tests) */\n    int aof_rewrite_scheduled;      /* Rewrite once BGSAVE terminates. */\n    pid_t aof_child_pid;            /* PID if rewriting process */\n    list *aof_rewrite_buf_blocks;   /* Hold changes during an AOF rewrite. */\n    sds aof_buf;      /* AOF buffer, written before entering the event loop */\n    int aof_fd;       /* File descriptor of currently selected AOF file */\n    int aof_selected_db; /* Currently selected DB in AOF */\n    time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */\n    time_t aof_last_fsync;            /* UNIX time of last fsync() */\n    time_t aof_rewrite_time_last;   /* Time used by last AOF rewrite run. */\n    time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */\n    int aof_lastbgrewrite_status;   /* C_OK or C_ERR */\n    unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */\n    int aof_rewrite_incremental_fsync;/* fsync incrementally while aof rewriting? */\n    int rdb_save_incremental_fsync;   /* fsync incrementally while rdb saving? */\n    int aof_last_write_status;      /* C_OK or C_ERR */\n    int aof_last_write_errno;       /* Valid if aof_last_write_status is ERR */\n    int aof_load_truncated;         /* Don't stop on unexpected AOF EOF. */\n    int aof_use_rdb_preamble;       /* Use RDB preamble on AOF rewrites. */\n    /* AOF pipes used to communicate between parent and child during rewrite. */\n    int aof_pipe_write_data_to_child;\n    int aof_pipe_read_data_from_parent;\n    int aof_pipe_write_ack_to_parent;\n    int aof_pipe_read_ack_from_child;\n    int aof_pipe_write_ack_to_child;\n    int aof_pipe_read_ack_from_parent;\n    int aof_stop_sending_diff;     /* If true stop sending accumulated diffs\n                                      to child process. */\n    sds aof_child_diff;             /* AOF diff accumulator child side. */\n    /* RDB persistence */\n    long long dirty;                /* Changes to DB from the last save */\n    long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */\n    pid_t rdb_child_pid;            /* PID of RDB saving child */\n    struct saveparam *saveparams;   /* Save points array for RDB */\n    int saveparamslen;              /* Number of saving points */\n    char *rdb_filename;             /* Name of RDB file */\n    int rdb_compression;            /* Use compression in RDB? */\n    int rdb_checksum;               /* Use RDB checksum? */\n    int rdb_del_sync_files;         /* Remove RDB files used only for SYNC if\n                                       the instance does not use persistence. */\n    time_t lastsave;                /* Unix time of last successful save */\n    time_t lastbgsave_try;          /* Unix time of last attempted bgsave */\n    time_t rdb_save_time_last;      /* Time used by last RDB save run. */\n    time_t rdb_save_time_start;     /* Current RDB save start time. */\n    int rdb_bgsave_scheduled;       /* BGSAVE when possible if true. */\n    int rdb_child_type;             /* Type of save by active child. */\n    int lastbgsave_status;          /* C_OK or C_ERR */\n    int stop_writes_on_bgsave_err;  /* Don't allow writes if can't BGSAVE */\n    int rdb_pipe_write;             /* RDB pipes used to transfer the rdb */\n    int rdb_pipe_read;              /* data to the parent process in diskless repl. */\n    connection **rdb_pipe_conns;    /* Connections which are currently the */\n    int rdb_pipe_numconns;          /* target of diskless rdb fork child. */\n    int rdb_pipe_numconns_writing;  /* Number of rdb conns with pending writes. */\n    char *rdb_pipe_buff;            /* In diskless replication, this buffer holds data */\n    int rdb_pipe_bufflen;           /* that was read from the the rdb pipe. */\n    int rdb_key_save_delay;         /* Delay in microseconds between keys while\n                                     * writing the RDB. (for testings) */\n    int key_load_delay;             /* Delay in microseconds between keys while\n                                     * loading aof or rdb. (for testings) */\n    /* Pipe and data structures for child -> parent info sharing. */\n    int child_info_pipe[2];         /* Pipe used to write the child_info_data. */\n    struct {\n        int process_type;           /* AOF or RDB child? */\n        size_t cow_size;            /* Copy on write size. */\n        unsigned long long magic;   /* Magic value to make sure data is valid. */\n    } child_info_data;\n    /* Propagation of commands in AOF / replication */\n    redisOpArray also_propagate;    /* Additional command to propagate. */\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    /* Replication (master) */\n    char replid[CONFIG_RUN_ID_SIZE+1];  /* My current replication ID. */\n    char replid2[CONFIG_RUN_ID_SIZE+1]; /* replid inherited from master*/\n    long long master_repl_offset;   /* My current replication offset */\n    long long master_repl_meaningful_offset; /* Offset minus latest PINGs. */\n    long long second_replid_offset; /* Accept offsets up to this for replid2. */\n    int slaveseldb;                 /* Last SELECTed DB in replication output */\n    int repl_ping_slave_period;     /* Master pings the slave every N seconds */\n    char *repl_backlog;             /* Replication backlog for partial syncs */\n    long long repl_backlog_size;    /* Backlog circular buffer size */\n    long long repl_backlog_histlen; /* Backlog actual data length */\n    long long repl_backlog_idx;     /* Backlog circular buffer current offset,\n                                       that is the next byte will'll write to.*/\n    long long repl_backlog_off;     /* Replication \"master offset\" of first\n                                       byte in the replication backlog buffer.*/\n    time_t repl_backlog_time_limit; /* Time without slaves after the backlog\n                                       gets released. */\n    time_t repl_no_slaves_since;    /* We have no slaves since that time.\n                                       Only valid if server.slaves len is 0. */\n    int repl_min_slaves_to_write;   /* Min number of slaves to write. */\n    int repl_min_slaves_max_lag;    /* Max lag of <count> slaves to write. */\n    int repl_good_slaves_count;     /* Number of slaves with lag <= max_lag. */\n    int repl_diskless_sync;         /* Master send RDB to slaves sockets directly. */\n    int repl_diskless_load;         /* Slave parse RDB directly from the socket.\n                                     * see REPL_DISKLESS_LOAD_* enum */\n    int repl_diskless_sync_delay;   /* Delay to start a diskless repl BGSAVE. */\n    /* Replication (slave) */\n    char *masteruser;               /* AUTH with this user and masterauth with master */\n    char *masterauth;               /* AUTH with this password with master */\n    char *masterhost;               /* Hostname of master */\n    int masterport;                 /* Port of master */\n    int repl_timeout;               /* Timeout after N seconds of master idle */\n    client *master;     /* Client that is master for this slave */\n    client *cached_master; /* Cached master to be reused for PSYNC. */\n    int repl_syncio_timeout; /* Timeout for synchronous I/O calls */\n    int repl_state;          /* Replication status if the instance is a slave */\n    off_t repl_transfer_size; /* Size of RDB to read from master during sync. */\n    off_t repl_transfer_read; /* Amount of RDB read from master during sync. */\n    off_t repl_transfer_last_fsync_off; /* Offset when we fsync-ed last time. */\n    connection *repl_transfer_s;     /* Slave -> Master SYNC connection */\n    int repl_transfer_fd;    /* Slave -> Master SYNC temp file descriptor */\n    char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */\n    time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */\n    int repl_serve_stale_data; /* Serve stale data when link is down? */\n    int repl_slave_ro;          /* Slave is read only? */\n    int repl_slave_ignore_maxmemory;    /* If true slaves do not evict. */\n    time_t repl_down_since; /* Unix time at which link with master went down */\n    int repl_disable_tcp_nodelay;   /* Disable TCP_NODELAY after SYNC? */\n    int slave_priority;             /* Reported in INFO and used by Sentinel. */\n    int slave_announce_port;        /* Give the master this listening port. */\n    char *slave_announce_ip;        /* Give the master this ip address. */\n    /* The following two fields is where we store master PSYNC replid/offset\n     * while the PSYNC is in progress. At the end we'll copy the fields into\n     * the server->master client structure. */\n    char master_replid[CONFIG_RUN_ID_SIZE+1];  /* Master PSYNC runid. */\n    long long master_initial_offset;           /* Master PSYNC offset. */\n    int repl_slave_lazy_flush;          /* Lazy FLUSHALL before loading DB? */\n    /* Replication script cache. */\n    dict *repl_scriptcache_dict;        /* SHA1 all slaves are aware of. */\n    list *repl_scriptcache_fifo;        /* First in, first out LRU eviction. */\n    unsigned int repl_scriptcache_size; /* Max number of elements. */\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    unsigned 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    int lfu_log_factor;             /* LFU logarithmic counter factor. */\n    int lfu_decay_time;             /* LFU counter decay factor. */\n    long long proto_max_bulk_len;   /* Protocol bulk length maximum size. */\n    /* Blocked clients */\n    unsigned int blocked_clients;   /* # of clients executing a blocking cmd.*/\n    unsigned int blocked_clients_by_type[BLOCKED_NUM];\n    list *unblocked_clients; /* list of clients to unblock before next loop */\n    list *ready_keys;        /* List of readyList structures for BLPOP & co */\n    /* Client side caching. */\n    unsigned int tracking_clients;  /* # of clients with tracking enabled.*/\n    size_t tracking_table_max_keys; /* Max number of keys in tracking table. */\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    /* Zip structure config, see redis.conf for more information  */\n    size_t hash_max_ziplist_entries;\n    size_t hash_max_ziplist_value;\n    size_t set_max_intset_entries;\n    size_t zset_max_ziplist_entries;\n    size_t zset_max_ziplist_value;\n    size_t hll_sparse_max_bytes;\n    size_t stream_node_max_bytes;\n    long long stream_node_max_entries;\n    /* List parameters */\n    int list_max_ziplist_size;\n    int list_compress_depth;\n    /* time cache */\n    _Atomic time_t unixtime;    /* Unix time sampled every cron cycle. */\n    time_t timezone;            /* Cached timezone. As set by tzset(). */\n    int daylight_active;        /* Currently in daylight saving time. */\n    mstime_t mstime;            /* 'unixtime' in milliseconds. */\n    ustime_t ustime;            /* 'unixtime' in microseconds. */\n    /* Pubsub */\n    dict *pubsub_channels;  /* Map channels to list of subscribed clients */\n    list *pubsub_patterns;  /* A list of pubsub_patterns */\n    dict *pubsub_patterns_dict;  /* A dict of pubsub_patterns */\n    int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an\n                                   xor of NOTIFY_... flags. */\n    /* Cluster */\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    int cluster_migration_barrier; /* Cluster replicas migration barrier. */\n    int cluster_slave_validity_factor; /* Slave max data age for failover. */\n    int cluster_require_full_coverage; /* If true, put the cluster down if\n                                          there is at least an uncovered slot.*/\n    int cluster_slave_no_failover;  /* Prevent slave from starting a failover\n                                       if the master is in failure state. */\n    char *cluster_announce_ip;  /* IP address to announce on cluster bus. */\n    int cluster_announce_port;     /* base port to announce on cluster bus. */\n    int cluster_announce_bus_port; /* bus port to announce on cluster bus. */\n    int cluster_module_flags;      /* Set of flags that Redis modules are able\n                                      to set in order to suppress certain\n                                      native Redis Cluster features. Check the\n                                      REDISMODULE_CLUSTER_FLAG_*. */\n    int cluster_allow_reads_when_down; /* Are reads allowed when the cluster\n                                        is down? */\n    /* Scripting */\n    lua_State *lua; /* The Lua interpreter. We use just one for all clients */\n    client *lua_client;   /* The \"fake client\" to query Redis from Lua */\n    client *lua_caller;   /* The client running EVAL right now, or NULL */\n    char* lua_cur_script; /* SHA1 of the script currently running, or NULL */\n    dict *lua_scripts;         /* A dictionary of SHA1 -> Lua scripts */\n    unsigned long long lua_scripts_mem;  /* Cached scripts' memory + oh */\n    mstime_t lua_time_limit;  /* Script timeout in milliseconds */\n    mstime_t lua_time_start;  /* Start time of script, milliseconds time */\n    int lua_write_dirty;  /* True if a write command was called during the\n                             execution of the current script. */\n    int lua_random_dirty; /* True if a random command was called during the\n                             execution of the current script. */\n    int lua_replicate_commands; /* True if we are doing single commands repl. */\n    int lua_multi_emitted;/* True if we already proagated MULTI. */\n    int lua_repl;         /* Script replication flags for redis.set_repl(). */\n    int lua_timedout;     /* True if we reached the time limit for script\n                             execution. */\n    int lua_kill;         /* Kill the script if true. */\n    int lua_always_replicate_commands; /* Default replication type. */\n    int lua_oom;          /* OOM detected when script start? */\n    /* Lazy free */\n    int lazyfree_lazy_eviction;\n    int lazyfree_lazy_expire;\n    int lazyfree_lazy_server_del;\n    int lazyfree_lazy_user_del;\n    /* Latency monitor */\n    long long latency_monitor_threshold;\n    dict *latency_events;\n    /* ACLs */\n    char *acl_filename;     /* ACL Users file. NULL if not configured. */\n    unsigned long acllog_max_len; /* Maximum length of the ACL LOG list. */\n    sds requirepass;        /* Remember the cleartext password set with the\n                               old \"requirepass\" directive for backward\n                               compatibility with Redis <= 5. */\n    /* Assert & bug reporting */\n    const char *assert_failed;\n    const 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    /* System hardware info */\n    size_t system_memory_size;  /* Total memory in system as reported by OS */\n    /* TLS Configuration */\n    int tls_cluster;\n    int tls_replication;\n    int tls_auth_clients;\n    redisTLSContextConfig tls_ctx_config;\n    /* cpu affinity */\n    char *server_cpulist; /* cpu affinity list of redis server main/io thread. */\n    char *bio_cpulist; /* cpu affinity list of bio thread. */\n    char *aof_rewrite_cpulist; /* cpu affinity list of aof rewrite process. */\n    char *bgsave_cpulist; /* cpu affinity list of bgsave process. */\n};\n\ntypedef struct pubsubPattern {\n    client *client;\n    robj *pattern;\n} pubsubPattern;\n\ntypedef void redisCommandProc(client *c);\ntypedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nstruct redisCommand {\n    char *name;\n    redisCommandProc *proc;\n    int arity;\n    char *sflags;   /* Flags as string representation, one char per flag. */\n    uint64_t flags; /* The actual flags, obtained from the 'sflags' field. */\n    /* Use a function to determine keys arguments in a command line.\n     * Used for Redis Cluster redirect. */\n    redisGetKeysProc *getkeys_proc;\n    /* What keys should be loaded in background when calling this command? */\n    int firstkey; /* The first argument that's a key (0 = no keys) */\n    int lastkey;  /* The last argument that's a key */\n    int keystep;  /* The step between first and last key */\n    long long microseconds, calls;\n    int id;     /* Command ID. This is a progressive ID starting from 0 that\n                   is assigned at runtime, and is used in order to check\n                   ACLs. A connection is able to execute a given command if\n                   the user associated to the connection has this command\n                   bit set in the bitmap of allowed commands. */\n};\n\nstruct redisFunctionSym {\n    char *name;\n    unsigned long pointer;\n};\n\ntypedef struct _redisSortObject {\n    robj *obj;\n    union {\n        double score;\n        robj *cmpobj;\n    } u;\n} redisSortObject;\n\ntypedef struct _redisSortOperation {\n    int type;\n    robj *pattern;\n} redisSortOperation;\n\n/* Structure to hold list iteration abstraction. */\ntypedef struct {\n    robj *subject;\n    unsigned char encoding;\n    unsigned char direction; /* Iteration direction */\n    quicklistIter *iter;\n} listTypeIterator;\n\n/* Structure for an entry while iterating over a list. */\ntypedef struct {\n    listTypeIterator *li;\n    quicklistEntry entry; /* Entry in quicklist */\n} listTypeEntry;\n\n/* Structure to hold set iteration abstraction. */\ntypedef struct {\n    robj *subject;\n    int encoding;\n    int ii; /* intset iterator */\n    dictIterator *di;\n} setTypeIterator;\n\n/* Structure to hold hash iteration abstraction. Note that iteration over\n * hashes involves both fields and values. Because it is possible that\n * not both are required, store pointers in the iterator to avoid\n * unnecessary memory allocation for fields/values. */\ntypedef struct {\n    robj *subject;\n    int encoding;\n\n    unsigned char *fptr, *vptr;\n\n    dictIterator *di;\n    dictEntry *de;\n} hashTypeIterator;\n\n#include \"stream.h\"  /* Stream data type header file. */\n\n#define OBJ_HASH_KEY 1\n#define OBJ_HASH_VALUE 2\n\n/*-----------------------------------------------------------------------------\n * Extern declarations\n *----------------------------------------------------------------------------*/\n\nextern struct redisServer server;\nextern struct sharedObjectsStruct shared;\nextern dictType objectKeyPointerValueDictType;\nextern dictType objectKeyHeapPointerValueDictType;\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;\nextern dictType keyptrDictType;\nextern dictType modulesDictType;\n\n/*-----------------------------------------------------------------------------\n * Functions prototypes\n *----------------------------------------------------------------------------*/\n\n/* Modules */\nvoid moduleInitModulesSystem(void);\nint moduleLoad(const char *path, void **argv, int argc);\nvoid moduleLoadFromQueue(void);\nint *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nmoduleType *moduleTypeLookupModuleByID(uint64_t id);\nvoid moduleTypeNameByID(char *name, uint64_t moduleid);\nvoid moduleFreeContext(struct RedisModuleCtx *ctx);\nvoid unblockClientFromModule(client *c);\nvoid moduleHandleBlockedClients(void);\nvoid moduleBlockedClientTimedOut(client *c);\nvoid moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask);\nsize_t moduleCount(void);\nvoid moduleAcquireGIL(void);\nvoid moduleReleaseGIL(void);\nvoid moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);\nvoid moduleCallCommandFilters(client *c);\nvoid ModuleForkDoneHandler(int exitcode, int bysignal);\nint TerminateModuleForkChild(int child_pid, int wait);\nssize_t rdbSaveModulesAux(rio *rdb, int when);\nint moduleAllDatatypesHandleErrors();\nsds modulesCollectInfo(sds info, const char *section, int for_crash_report, int sections);\nvoid moduleFireServerEvent(uint64_t eid, int subid, void *data);\nvoid processModuleLoadingProgressEvent(int is_aof);\nint moduleTryServeClientBlockedOnKey(client *c, robj *key);\nvoid moduleUnblockClient(client *c);\nint moduleClientIsBlockedOnKeys(client *c);\nvoid moduleNotifyUserChanged(client *c);\n\n/* Utils */\nlong long ustime(void);\nlong long mstime(void);\nvoid getRandomHexChars(char *p, size_t len);\nvoid getRandomBytes(unsigned char *p, size_t 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);\nint redisCommunicateSystemd(const char *sd_notify_msg);\nvoid redisSetCpuAffinity(const char *cpulist);\n\n/* networking.c -- Networking and Client related operations */\nclient *createClient(connection *conn);\nvoid closeTimedoutClients(void);\nvoid freeClient(client *c);\nvoid freeClientAsync(client *c);\nvoid resetClient(client *c);\nvoid sendReplyToClient(connection *conn);\nvoid *addReplyDeferredLen(client *c);\nvoid setDeferredArrayLen(client *c, void *node, long length);\nvoid setDeferredMapLen(client *c, void *node, long length);\nvoid setDeferredSetLen(client *c, void *node, long length);\nvoid setDeferredAttributeLen(client *c, void *node, long length);\nvoid setDeferredPushLen(client *c, void *node, long length);\nvoid processInputBuffer(client *c);\nvoid processGopherRequest(client *c);\nvoid acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);\nvoid readQueryFromClient(connection *conn);\nvoid addReplyNull(client *c);\nvoid addReplyNullArray(client *c);\nvoid addReplyBool(client *c, int b);\nvoid addReplyVerbatim(client *c, const char *s, size_t len, const char *ext);\nvoid addReplyProto(client *c, const char *s, size_t len);\nvoid AddReplyFromClient(client *c, client *src);\nvoid addReplyBulk(client *c, robj *obj);\nvoid addReplyBulkCString(client *c, const char *s);\nvoid addReplyBulkCBuffer(client *c, const void *p, size_t len);\nvoid addReplyBulkLongLong(client *c, long long ll);\nvoid addReply(client *c, robj *obj);\nvoid addReplySds(client *c, sds s);\nvoid addReplyBulkSds(client *c, sds s);\nvoid addReplyError(client *c, const char *err);\nvoid addReplyStatus(client *c, const char *status);\nvoid addReplyDouble(client *c, double d);\nvoid addReplyHumanLongDouble(client *c, long double d);\nvoid addReplyLongLong(client *c, long long ll);\nvoid addReplyArrayLen(client *c, long length);\nvoid addReplyMapLen(client *c, long length);\nvoid addReplySetLen(client *c, long length);\nvoid addReplyAttributeLen(client *c, long length);\nvoid addReplyPushLen(client *c, long length);\nvoid addReplyHelp(client *c, const char **help);\nvoid addReplySubcommandSyntaxError(client *c);\nvoid addReplyLoadedModules(client *c);\nvoid copyClientOutputBuffer(client *dst, client *src);\nsize_t sdsZmallocSize(sds s);\nsize_t getStringObjectSdsUsedMemory(robj *o);\nvoid freeClientReplyValue(void *o);\nvoid *dupClientReplyValue(void *o);\nvoid getClientsMaxBuffers(unsigned long *longest_output_list,\n                          unsigned long *biggest_input_buffer);\nchar *getClientPeerId(client *client);\nsds catClientInfoString(sds s, client *client);\nsds getAllClientsInfoString(int type);\nvoid rewriteClientCommandVector(client *c, int argc, ...);\nvoid rewriteClientCommandArgument(client *c, int i, robj *newval);\nvoid replaceClientCommandVector(client *c, int argc, robj **argv);\nunsigned long getClientOutputBufferMemoryUsage(client *c);\nint freeClientsInAsyncFreeQueue(void);\nvoid asyncCloseClientOnOutputBufferLimitReached(client *c);\nint getClientType(client *c);\nint getClientTypeByName(char *name);\nchar *getClientTypeName(int class);\nvoid flushSlavesOutputBuffers(void);\nvoid disconnectSlaves(void);\nint listenToPort(int port, int *fds, int *count);\nvoid pauseClients(mstime_t duration);\nint clientsArePaused(void);\nvoid processEventsWhileBlocked(void);\nint handleClientsWithPendingWrites(void);\nint handleClientsWithPendingWritesUsingThreads(void);\nint handleClientsWithPendingReadsUsingThreads(void);\nint stopThreadedIOIfNeeded(void);\nint clientHasPendingReplies(client *c);\nvoid unlinkClient(client *c);\nint writeToClient(client *c, int handler_installed);\nvoid linkClient(client *c);\nvoid protectClient(client *c);\nvoid unprotectClient(client *c);\nvoid initThreadedIO(void);\nclient *lookupClientByID(uint64_t id);\n\n#ifdef __GNUC__\nvoid addReplyErrorFormat(client *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\nvoid addReplyStatusFormat(client *c, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid addReplyErrorFormat(client *c, const char *fmt, ...);\nvoid addReplyStatusFormat(client *c, const char *fmt, ...);\n#endif\n\n/* Client side caching (tracking mode) */\nvoid enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **prefix, size_t numprefix);\nvoid disableTracking(client *c);\nvoid trackingRememberKeys(client *c);\nvoid trackingInvalidateKey(client *c, robj *keyobj);\nvoid trackingInvalidateKeysOnFlush(int dbid);\nvoid trackingLimitUsedSlots(void);\nuint64_t trackingGetTotalItems(void);\nuint64_t trackingGetTotalKeys(void);\nuint64_t trackingGetTotalPrefixes(void);\nvoid trackingBroadcastInvalidationMessages(void);\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(const robj *subject);\nlistTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction);\nvoid listTypeReleaseIterator(listTypeIterator *li);\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry);\nrobj *listTypeGet(listTypeEntry *entry);\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where);\nint listTypeEqual(listTypeEntry *entry, robj *o);\nvoid listTypeDelete(listTypeIterator *iter, listTypeEntry *entry);\nvoid listTypeConvert(robj *subject, int enc);\nvoid unblockClientWaitingData(client *c);\nvoid popGenericCommand(client *c, int where);\n\n/* MULTI/EXEC/WATCH... */\nvoid unwatchAllKeys(client *c);\nvoid initClientMultiState(client *c);\nvoid freeClientMultiState(client *c);\nvoid queueMultiCommand(client *c);\nvoid touchWatchedKey(redisDb *db, robj *key);\nvoid touchWatchedKeysOnFlush(int dbid);\nvoid discardTransaction(client *c);\nvoid flagTransaction(client *c);\nvoid execCommandPropagateMulti(client *c);\nvoid execCommandPropagateExec(client *c);\n\n/* Redis object implementation */\nvoid decrRefCount(robj *o);\nvoid decrRefCountVoid(void *o);\nvoid incrRefCount(robj *o);\nrobj *makeObjectShared(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(const char *ptr, size_t len);\nrobj *createRawStringObject(const char *ptr, size_t len);\nrobj *createEmbeddedStringObject(const char *ptr, size_t len);\nrobj *dupStringObject(const robj *o);\nint isSdsRepresentableAsLongLong(sds s, long long *llval);\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 *createStringObjectFromLongLongForValue(long long value);\nrobj *createStringObjectFromLongDouble(long double value, int humanfriendly);\nrobj *createQuicklistObject(void);\nrobj *createZiplistObject(void);\nrobj *createSetObject(void);\nrobj *createIntsetObject(void);\nrobj *createHashObject(void);\nrobj *createZsetObject(void);\nrobj *createZsetZiplistObject(void);\nrobj *createStreamObject(void);\nrobj *createModuleObject(moduleType *mt, void *value);\nint getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg);\nint checkType(client *c, robj *o, int type);\nint getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg);\nint getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg);\nint getDoubleFromObject(const robj *o, double *target);\nint getLongLongFromObject(robj *o, long long *target);\nint getLongDoubleFromObject(robj *o, long double *target);\nint getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg);\nchar *strEncoding(int encoding);\nint compareStringObjects(robj *a, robj *b);\nint collateStringObjects(robj *a, robj *b);\nint equalStringObjects(robj *a, robj *b);\nunsigned long long estimateObjectIdleTime(robj *o);\nvoid trimStringObjectIfNeeded(robj *o);\n#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_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 replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen);\nvoid replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc);\nvoid updateSlavesWaitingBgsave(int bgsaveerr, int type);\nvoid replicationCron(void);\nvoid replicationHandleMasterDisconnection(void);\nvoid replicationCacheMaster(client *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(client *c);\nint replicationCountAcksByOffset(long long offset);\nvoid replicationSendNewlineToMaster(void);\nlong long replicationGetSlaveOffset(void);\nchar *replicationGetSlaveName(client *c);\nlong long getPsyncInitialOffset(void);\nint replicationSetupSlaveForFullResync(client *slave, long long offset);\nvoid changeReplicationId(void);\nvoid clearReplicationId2(void);\nvoid chopReplicationBacklog(void);\nvoid replicationCacheMasterUsingMyself(void);\nvoid feedReplicationBacklog(void *ptr, size_t len);\nvoid rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);\nvoid rdbPipeWriteHandlerConnRemoved(struct connection *conn);\n\n/* Generic persistence functions */\nvoid startLoadingFile(FILE* fp, char* filename, int rdbflags);\nvoid startLoading(size_t size, int rdbflags);\nvoid loadingProgress(off_t pos);\nvoid stopLoading(int success);\nvoid startSaving(int rdbflags);\nvoid stopSaving(int success);\nint allPersistenceDisabled(void);\n\n#define DISK_ERROR_TYPE_AOF 1       /* Don't accept writes: AOF errors. */\n#define DISK_ERROR_TYPE_RDB 2       /* Don't accept writes: RDB errors. */\n#define DISK_ERROR_TYPE_NONE 0      /* No problems, we can accept writes. */\nint writeCommandsDeniedByDiskError(void);\n\n/* RDB persistence */\n#include \"rdb.h\"\nvoid killRDBChild(void);\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);\nssize_t aofReadDiffFromParent(void);\nvoid killAppendOnlyChild(void);\nvoid restartAOFAfterSYNC();\n\n/* Child info */\nvoid openChildInfoPipe(void);\nvoid closeChildInfoPipe(void);\nvoid sendChildInfo(int process_type);\nvoid receiveChildInfo(void);\n\n/* Fork helpers */\nint redisFork();\nint hasActiveChildProcess();\nvoid sendChildCOWInfo(int ptype, char *pname);\n\n/* acl.c -- Authentication related prototypes. */\nextern rax *Users;\nextern user *DefaultUser;\nvoid ACLInit(void);\n/* Return values for ACLCheckUserCredentials(). */\n#define ACL_OK 0\n#define ACL_DENIED_CMD 1\n#define ACL_DENIED_KEY 2\n#define ACL_DENIED_AUTH 3 /* Only used for ACL LOG entries. */\nint ACLCheckUserCredentials(robj *username, robj *password);\nint ACLAuthenticateUser(client *c, robj *username, robj *password);\nunsigned long ACLGetCommandID(const char *cmdname);\nuser *ACLGetUserByName(const char *name, size_t namelen);\nint ACLCheckCommandPerm(client *c, int *keyidxptr);\nint ACLSetUser(user *u, const char *op, ssize_t oplen);\nsds ACLDefaultUserFirstPassword(void);\nuint64_t ACLGetCommandCategoryFlagByName(const char *name);\nint ACLAppendUserForLoading(sds *argv, int argc, int *argc_err);\nchar *ACLSetUserStringError(void);\nint ACLLoadConfiguredUsers(void);\nsds ACLDescribeUser(user *u);\nvoid ACLLoadUsersAtStartup(void);\nvoid addReplyCommandCategories(client *c, struct redisCommand *cmd);\nuser *ACLCreateUnlinkedUser();\nvoid ACLFreeUserAndKillClients(user *u);\nvoid addACLLogEntry(client *c, int reason, int keypos, sds username);\n\n/* Sorted sets data type */\n\n/* Input flags. */\n#define ZADD_NONE 0\n#define ZADD_INCR (1<<0)    /* Increment the score instead of setting it. */\n#define ZADD_NX (1<<1)      /* Don't touch elements not already existing. */\n#define ZADD_XX (1<<2)      /* Only touch elements already existing. */\n\n/* Output flags. */\n#define ZADD_NOP (1<<3)     /* Operation not performed because of conditionals.*/\n#define ZADD_NAN (1<<4)     /* Only touch elements already existing. */\n#define ZADD_ADDED (1<<5)   /* The element was new and was added. */\n#define ZADD_UPDATED (1<<6) /* The element already existed, score updated. */\n\n/* Flags only used by the ZADD command but not by zsetAdd() API: */\n#define ZADD_CH (1<<16)      /* Return num of elements added or updated. */\n\n/* Struct to hold a inclusive/exclusive range spec by score comparison. */\ntypedef struct {\n    double min, max;\n    int minex, maxex; /* are min or max exclusive? */\n} zrangespec;\n\n/* Struct to hold an inclusive/exclusive range spec by lexicographic comparison. */\ntypedef struct {\n    sds 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, sds ele);\nunsigned char *zzlInsert(unsigned char *zl, sds ele, double score);\nint zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node);\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 char *zzlFirstInRange(unsigned char *zl, zrangespec *range);\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range);\nunsigned long zsetLength(const robj *zobj);\nvoid zsetConvert(robj *zobj, int encoding);\nvoid zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen);\nint zsetScore(robj *zobj, sds member, double *score);\nunsigned long zslGetRank(zskiplist *zsl, double score, sds o);\nint zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore);\nlong zsetRank(robj *zobj, sds ele, int reverse);\nint zsetDel(robj *zobj, sds ele);\nvoid genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey, robj *countarg);\nsds ziplistGetObject(unsigned char *sptr);\nint zslValueGteMin(double value, zrangespec *spec);\nint zslValueLteMax(double value, zrangespec *spec);\nvoid zslFreeLexRange(zlexrangespec *spec);\nint zslParseLexRange(robj *min, robj *max, zlexrangespec *spec);\nunsigned char *zzlFirstInLexRange(unsigned char *zl, zlexrangespec *range);\nunsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range);\nzskiplistNode *zslFirstInLexRange(zskiplist *zsl, zlexrangespec *range);\nzskiplistNode *zslLastInLexRange(zskiplist *zsl, zlexrangespec *range);\nint zzlLexValueGteMin(unsigned char *p, zlexrangespec *spec);\nint zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec);\nint zslLexValueGteMin(sds value, zlexrangespec *spec);\nint zslLexValueLteMax(sds value, zlexrangespec *spec);\n\n/* Core functions */\nint getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level);\nsize_t freeMemoryGetNotCountedMemory();\nint freeMemoryIfNeeded(void);\nint freeMemoryIfNeededAndSafe(void);\nint processCommand(client *c);\nvoid setupSignalHandlers(void);\nstruct redisCommand *lookupCommand(sds name);\nstruct redisCommand *lookupCommandByCString(char *s);\nstruct redisCommand *lookupCommandOrOriginal(sds name);\nvoid call(client *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 redisOpArrayInit(redisOpArray *oa);\nvoid redisOpArrayFree(redisOpArray *oa);\nvoid forceCommandPropagation(client *c, int flags);\nvoid preventCommandPropagation(client *c);\nvoid preventCommandAOF(client *c);\nvoid preventCommandReplication(client *c);\nint prepareForShutdown();\n#ifdef __GNUC__\nvoid serverLog(int level, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nvoid serverLog(int level, const char *fmt, ...);\n#endif\nvoid serverLogRaw(int level, const char *msg);\nvoid serverLogFromHandler(int level, const char *msg);\nvoid usage(void);\nvoid updateDictResizePolicy(void);\nint htNeedsResize(dict *dict);\nvoid populateCommandTable(void);\nvoid resetCommandTableStats(void);\nvoid adjustOpenFilesLimit(void);\nvoid closeListeningSockets(int unlink_unix_socket);\nvoid updateCachedTime(int update_daylight_info);\nvoid resetServerStats(void);\nvoid activeDefragCycle(void);\nunsigned int getLRUClock(void);\nunsigned int LRU_CLOCK(void);\nconst char *evictPolicyToString(void);\nstruct redisMemOverhead *getMemoryOverheadData(void);\nvoid freeMemoryOverheadData(struct redisMemOverhead *mh);\nvoid checkChildrenDone(void);\n\n#define RESTART_SERVER_NONE 0\n#define RESTART_SERVER_GRACEFULLY (1<<0)     /* Do proper shutdown. */\n#define RESTART_SERVER_CONFIG_REWRITE (1<<1) /* CONFIG REWRITE before restart.*/\nint restartServer(int flags, mstime_t delay);\n\n/* Set data type */\nrobj *setTypeCreate(sds value);\nint setTypeAdd(robj *subject, sds value);\nint setTypeRemove(robj *subject, sds value);\nint setTypeIsMember(robj *subject, sds value);\nsetTypeIterator *setTypeInitIterator(robj *subject);\nvoid setTypeReleaseIterator(setTypeIterator *si);\nint setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele);\nsds setTypeNextObject(setTypeIterator *si);\nint setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele);\nunsigned long setTypeRandomElements(robj *set, unsigned long count, robj *aux_set);\nunsigned long setTypeSize(const robj *subject);\nvoid setTypeConvert(robj *subject, int enc);\n\n/* Hash data type */\n#define HASH_SET_TAKE_FIELD (1<<0)\n#define HASH_SET_TAKE_VALUE (1<<1)\n#define HASH_SET_COPY 0\n\nvoid hashTypeConvert(robj *o, int enc);\nvoid hashTypeTryConversion(robj *subject, robj **argv, int start, int end);\nint hashTypeExists(robj *o, sds key);\nint hashTypeDelete(robj *o, sds key);\nunsigned long hashTypeLength(const 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);\nsds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what);\nvoid hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll);\nsds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what);\nrobj *hashTypeLookupWriteOrCreate(client *c, robj *key);\nrobj *hashTypeGetValueObject(robj *o, sds field);\nint hashTypeSet(robj *o, sds field, sds value, int flags);\n\n/* Pub / Sub */\nint pubsubUnsubscribeAllChannels(client *c, int notify);\nint pubsubUnsubscribeAllPatterns(client *c, int notify);\nvoid freePubsubPattern(void *p);\nint listMatchPubsubPattern(void *a, void *b);\nint pubsubPublishMessage(robj *channel, robj *message);\nvoid addReplyPubsubMessage(client *c, robj *channel, robj *msg);\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(void);\nstruct rewriteConfigState; /* Forward declaration to export API. */\nvoid rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force);\nint rewriteConfig(char *path);\nvoid initConfigValues();\n\n/* db.c -- Keyspace access API */\nint removeExpire(redisDb *db, robj *key);\nvoid propagateExpire(redisDb *db, robj *key, int lazy);\nint expireIfNeeded(redisDb *db, robj *key);\nlong long getExpire(redisDb *db, robj *key);\nvoid setExpire(client *c, redisDb *db, robj *key, long long when);\nrobj *lookupKey(redisDb *db, robj *key, int flags);\nrobj *lookupKeyRead(redisDb *db, robj *key);\nrobj *lookupKeyWrite(redisDb *db, robj *key);\nrobj *lookupKeyReadOrReply(client *c, robj *key, robj *reply);\nrobj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);\nrobj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);\nrobj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags);\nrobj *objectCommandLookup(client *c, robj *key);\nrobj *objectCommandLookupOrReply(client *c, robj *key, robj *reply);\nint objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,\n                       long long lru_clock, int lru_multiplier);\n#define LOOKUP_NONE 0\n#define LOOKUP_NOTOUCH (1<<0)\nvoid dbAdd(redisDb *db, robj *key, robj *val);\nint dbAddRDBLoad(redisDb *db, sds key, robj *val);\nvoid dbOverwrite(redisDb *db, robj *key, robj *val);\nvoid genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal);\nvoid setKey(client *c, redisDb *db, robj *key, robj *val);\nint dbExists(redisDb *db, robj *key);\nrobj *dbRandomKey(redisDb *db);\nint dbSyncDelete(redisDb *db, robj *key);\nint dbDelete(redisDb *db, robj *key);\nrobj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);\n\n#define EMPTYDB_NO_FLAGS 0      /* No flags. */\n#define EMPTYDB_ASYNC (1<<0)    /* Reclaim memory in another thread. */\n#define EMPTYDB_BACKUP (1<<2)   /* DB array is a backup for REPL_DISKLESS_LOAD_SWAPDB. */\nlong long emptyDb(int dbnum, int flags, void(callback)(void*));\nlong long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*));\nvoid flushAllDataAndResetRDB(int flags);\nlong long dbTotalServerKeyCount();\n\nint selectDb(client *c, int id);\nvoid signalModifiedKey(client *c, 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(client *c, robj *o, unsigned long cursor);\nint parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor);\nvoid slotToKeyAdd(sds key);\nvoid slotToKeyDel(sds key);\nvoid slotToKeyFlush(void);\nint dbAsyncDelete(redisDb *db, robj *key);\nvoid emptyDbAsync(redisDb *db);\nvoid slotToKeyFlushAsync(void);\nsize_t lazyfreeGetPendingObjectsCount(void);\nvoid freeObjAsync(robj *o);\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);\nint *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);\nint *lcsGetKeys(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);\nint clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, unsigned char *payload, uint32_t len);\n\n/* Sentinel */\nvoid initSentinelConfig(void);\nvoid initSentinel(void);\nvoid sentinelTimer(void);\nchar *sentinelHandleConfiguration(char **argv, int argc);\nvoid sentinelIsRunning(void);\n\n/* redis-check-rdb & aof */\nint redis_check_rdb(char *rdbfilename, FILE *fp);\nint redis_check_rdb_main(int argc, char **argv, FILE *fp);\nint redis_check_aof_main(int argc, char **argv);\n\n/* Scripting */\nvoid scriptingInit(int setup);\nint ldbRemoveChild(pid_t pid);\nvoid ldbKillForkedSessions(void);\nint ldbPendingChildren(void);\nsds luaCreateFunction(client *c, lua_State *lua, robj *body);\n\n/* Blocked clients */\nvoid processUnblockedClients(void);\nvoid blockClient(client *c, int btype);\nvoid unblockClient(client *c);\nvoid queueClientForReprocessing(client *c);\nvoid replyToBlockedClientTimedOut(client *c);\nint getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit);\nvoid disconnectAllBlockedClients(void);\nvoid handleClientsBlockedOnKeys(void);\nvoid signalKeyAsReady(redisDb *db, robj *key);\nvoid blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids);\n\n/* timeout.c -- Blocked clients timeout and connections timeout. */\nvoid addClientToTimeoutTable(client *c);\nvoid removeClientFromTimeoutTable(client *c);\nvoid handleBlockedClientsTimeout(void);\nint clientsCronHandleTimeout(client *c, mstime_t now_ms);\n\n/* expire.c -- Handling of expired keys */\nvoid activeExpireCycle(int type);\nvoid expireSlaveKeys(void);\nvoid rememberSlaveKeyWithExpire(redisDb *db, robj *key);\nvoid flushSlaveKeysWithExpireList(void);\nsize_t getSlaveKeyWithExpireCount(void);\n\n/* evict.c -- maxmemory handling and LRU eviction. */\nvoid evictionPoolAlloc(void);\n#define LFU_INIT_VAL 5\nunsigned long LFUGetTimeInMinutes(void);\nuint8_t LFULogIncr(uint8_t value);\nunsigned long LFUDecrAndReturn(robj *o);\n\n/* Keys hashing / comparison functions for dict.c hash tables. */\nuint64_t dictSdsHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\nvoid dictSdsDestructor(void *privdata, void *val);\n\n/* Git SHA1 */\nchar *redisGitSHA1(void);\nchar *redisGitDirty(void);\nuint64_t redisBuildId(void);\nchar *redisBuildIdString(void);\n\n/* Commands prototypes */\nvoid authCommand(client *c);\nvoid pingCommand(client *c);\nvoid echoCommand(client *c);\nvoid commandCommand(client *c);\nvoid setCommand(client *c);\nvoid setnxCommand(client *c);\nvoid setexCommand(client *c);\nvoid psetexCommand(client *c);\nvoid getCommand(client *c);\nvoid delCommand(client *c);\nvoid unlinkCommand(client *c);\nvoid existsCommand(client *c);\nvoid setbitCommand(client *c);\nvoid getbitCommand(client *c);\nvoid bitfieldCommand(client *c);\nvoid bitfieldroCommand(client *c);\nvoid setrangeCommand(client *c);\nvoid getrangeCommand(client *c);\nvoid incrCommand(client *c);\nvoid decrCommand(client *c);\nvoid incrbyCommand(client *c);\nvoid decrbyCommand(client *c);\nvoid incrbyfloatCommand(client *c);\nvoid selectCommand(client *c);\nvoid swapdbCommand(client *c);\nvoid randomkeyCommand(client *c);\nvoid keysCommand(client *c);\nvoid scanCommand(client *c);\nvoid dbsizeCommand(client *c);\nvoid lastsaveCommand(client *c);\nvoid saveCommand(client *c);\nvoid bgsaveCommand(client *c);\nvoid bgrewriteaofCommand(client *c);\nvoid shutdownCommand(client *c);\nvoid moveCommand(client *c);\nvoid renameCommand(client *c);\nvoid renamenxCommand(client *c);\nvoid lpushCommand(client *c);\nvoid rpushCommand(client *c);\nvoid lpushxCommand(client *c);\nvoid rpushxCommand(client *c);\nvoid linsertCommand(client *c);\nvoid lpopCommand(client *c);\nvoid rpopCommand(client *c);\nvoid llenCommand(client *c);\nvoid lindexCommand(client *c);\nvoid lrangeCommand(client *c);\nvoid ltrimCommand(client *c);\nvoid typeCommand(client *c);\nvoid lsetCommand(client *c);\nvoid saddCommand(client *c);\nvoid sremCommand(client *c);\nvoid smoveCommand(client *c);\nvoid sismemberCommand(client *c);\nvoid scardCommand(client *c);\nvoid spopCommand(client *c);\nvoid srandmemberCommand(client *c);\nvoid sinterCommand(client *c);\nvoid sinterstoreCommand(client *c);\nvoid sunionCommand(client *c);\nvoid sunionstoreCommand(client *c);\nvoid sdiffCommand(client *c);\nvoid sdiffstoreCommand(client *c);\nvoid sscanCommand(client *c);\nvoid syncCommand(client *c);\nvoid flushdbCommand(client *c);\nvoid flushallCommand(client *c);\nvoid sortCommand(client *c);\nvoid lremCommand(client *c);\nvoid rpoplpushCommand(client *c);\nvoid infoCommand(client *c);\nvoid mgetCommand(client *c);\nvoid monitorCommand(client *c);\nvoid expireCommand(client *c);\nvoid expireatCommand(client *c);\nvoid pexpireCommand(client *c);\nvoid pexpireatCommand(client *c);\nvoid getsetCommand(client *c);\nvoid ttlCommand(client *c);\nvoid touchCommand(client *c);\nvoid pttlCommand(client *c);\nvoid persistCommand(client *c);\nvoid replicaofCommand(client *c);\nvoid roleCommand(client *c);\nvoid debugCommand(client *c);\nvoid msetCommand(client *c);\nvoid msetnxCommand(client *c);\nvoid zaddCommand(client *c);\nvoid zincrbyCommand(client *c);\nvoid zrangeCommand(client *c);\nvoid zrangebyscoreCommand(client *c);\nvoid zrevrangebyscoreCommand(client *c);\nvoid zrangebylexCommand(client *c);\nvoid zrevrangebylexCommand(client *c);\nvoid zcountCommand(client *c);\nvoid zlexcountCommand(client *c);\nvoid zrevrangeCommand(client *c);\nvoid zcardCommand(client *c);\nvoid zremCommand(client *c);\nvoid zscoreCommand(client *c);\nvoid zremrangebyscoreCommand(client *c);\nvoid zremrangebylexCommand(client *c);\nvoid zpopminCommand(client *c);\nvoid zpopmaxCommand(client *c);\nvoid bzpopminCommand(client *c);\nvoid bzpopmaxCommand(client *c);\nvoid multiCommand(client *c);\nvoid execCommand(client *c);\nvoid discardCommand(client *c);\nvoid blpopCommand(client *c);\nvoid brpopCommand(client *c);\nvoid brpoplpushCommand(client *c);\nvoid appendCommand(client *c);\nvoid strlenCommand(client *c);\nvoid zrankCommand(client *c);\nvoid zrevrankCommand(client *c);\nvoid hsetCommand(client *c);\nvoid hsetnxCommand(client *c);\nvoid hgetCommand(client *c);\nvoid hmsetCommand(client *c);\nvoid hmgetCommand(client *c);\nvoid hdelCommand(client *c);\nvoid hlenCommand(client *c);\nvoid hstrlenCommand(client *c);\nvoid zremrangebyrankCommand(client *c);\nvoid zunionstoreCommand(client *c);\nvoid zinterstoreCommand(client *c);\nvoid zscanCommand(client *c);\nvoid hkeysCommand(client *c);\nvoid hvalsCommand(client *c);\nvoid hgetallCommand(client *c);\nvoid hexistsCommand(client *c);\nvoid hscanCommand(client *c);\nvoid configCommand(client *c);\nvoid hincrbyCommand(client *c);\nvoid hincrbyfloatCommand(client *c);\nvoid subscribeCommand(client *c);\nvoid unsubscribeCommand(client *c);\nvoid psubscribeCommand(client *c);\nvoid punsubscribeCommand(client *c);\nvoid publishCommand(client *c);\nvoid pubsubCommand(client *c);\nvoid watchCommand(client *c);\nvoid unwatchCommand(client *c);\nvoid clusterCommand(client *c);\nvoid restoreCommand(client *c);\nvoid migrateCommand(client *c);\nvoid askingCommand(client *c);\nvoid readonlyCommand(client *c);\nvoid readwriteCommand(client *c);\nvoid dumpCommand(client *c);\nvoid objectCommand(client *c);\nvoid memoryCommand(client *c);\nvoid clientCommand(client *c);\nvoid helloCommand(client *c);\nvoid evalCommand(client *c);\nvoid evalShaCommand(client *c);\nvoid scriptCommand(client *c);\nvoid timeCommand(client *c);\nvoid bitopCommand(client *c);\nvoid bitcountCommand(client *c);\nvoid bitposCommand(client *c);\nvoid replconfCommand(client *c);\nvoid waitCommand(client *c);\nvoid geoencodeCommand(client *c);\nvoid geodecodeCommand(client *c);\nvoid georadiusbymemberCommand(client *c);\nvoid georadiusbymemberroCommand(client *c);\nvoid georadiusCommand(client *c);\nvoid georadiusroCommand(client *c);\nvoid geoaddCommand(client *c);\nvoid geohashCommand(client *c);\nvoid geoposCommand(client *c);\nvoid geodistCommand(client *c);\nvoid pfselftestCommand(client *c);\nvoid pfaddCommand(client *c);\nvoid pfcountCommand(client *c);\nvoid pfmergeCommand(client *c);\nvoid pfdebugCommand(client *c);\nvoid latencyCommand(client *c);\nvoid moduleCommand(client *c);\nvoid securityWarningCommand(client *c);\nvoid xaddCommand(client *c);\nvoid xrangeCommand(client *c);\nvoid xrevrangeCommand(client *c);\nvoid xlenCommand(client *c);\nvoid xreadCommand(client *c);\nvoid xgroupCommand(client *c);\nvoid xsetidCommand(client *c);\nvoid xackCommand(client *c);\nvoid xpendingCommand(client *c);\nvoid xclaimCommand(client *c);\nvoid xinfoCommand(client *c);\nvoid xdelCommand(client *c);\nvoid xtrimCommand(client *c);\nvoid lolwutCommand(client *c);\nvoid aclCommand(client *c);\nvoid stralgoCommand(client *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 _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line);\nvoid _serverAssert(const char *estr, const char *file, int line);\nvoid _serverPanic(const char *file, int line, const char *msg, ...);\nvoid bugReportStart(void);\nvoid serverLogObjectDebugInfo(const robj *o);\nvoid sigsegvHandler(int sig, siginfo_t *info, void *secret);\nsds genRedisInfoString(const char *section);\nsds genModulesInfoString(sds info);\nvoid enableWatchdog(int period);\nvoid disableWatchdog(void);\nvoid watchdogScheduleSignal(int period);\nvoid serverLogHexDump(int level, char *descr, void *value, size_t len);\nint memtest_preserving_test(unsigned long *m, size_t bytes, int passes);\nvoid mixDigest(unsigned char *digest, void *ptr, size_t len);\nvoid xorDigest(unsigned char *digest, void *ptr, size_t len);\nint populateCommandTableParseFlags(struct redisCommand *c, char *strflags);\n\n/* TLS stuff */\nvoid tlsInit(void);\nint tlsConfigure(redisTLSContextConfig *ctx_config);\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\nint iAmMaster(void);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/setcpuaffinity.c",
    "content": "/* ==========================================================================\n * setcpuaffinity.c - Linux/BSD setcpuaffinity.\n * --------------------------------------------------------------------------\n * Copyright (C) 2020  zhenwei pi\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#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#ifdef __linux__\n#include <sched.h>\n#endif\n#ifdef __FreeBSD__\n#include <sys/param.h>\n#include <sys/cpuset.h>\n#endif\n#ifdef __NetBSD__\n#include <pthread.h>\n#include <sched.h>\n#endif\n#include \"config.h\"\n\n#ifdef USE_SETCPUAFFINITY\nstatic const char *next_token(const char *q,  int sep) {\n    if (q)\n        q = strchr(q, sep);\n    if (q)\n        q++;\n\n    return q;\n}\n\nstatic int next_num(const char *str, char **end, int *result) {\n    if (!str || *str == '\\0' || !isdigit(*str))\n        return -1;\n\n    *result = strtoul(str, end, 10);\n    if (str == *end)\n        return -1;\n\n    return 0;\n}\n\n/* set current thread cpu affinity to cpu list, this function works like\n * taskset command (actually cpulist parsing logic reference to util-linux).\n * example of this function: \"0,2,3\", \"0,2-3\", \"0-20:2\". */\nvoid setcpuaffinity(const char *cpulist) {\n    const char *p, *q;\n    char *end = NULL;\n#ifdef __linux__\n    cpu_set_t cpuset;\n#endif\n#ifdef __FreeBSD__\n    cpuset_t cpuset;\n#endif\n#ifdef __NetBSD__\n    cpuset_t *cpuset;\n#endif\n\n    if (!cpulist)\n        return;\n\n#ifndef __NetBSD__\n    CPU_ZERO(&cpuset);\n#else\n    cpuset = cpuset_create();\n#endif\n\n    q = cpulist;\n    while (p = q, q = next_token(q, ','), p) {\n        int a, b, s;\n        const char *c1, *c2;\n\n        if (next_num(p, &end, &a) != 0)\n            return;\n\n        b = a;\n        s = 1;\n        p = end;\n\n        c1 = next_token(p, '-');\n        c2 = next_token(p, ',');\n\n        if (c1 != NULL && (c2 == NULL || c1 < c2)) {\n            if (next_num(c1, &end, &b) != 0)\n                return;\n\n            c1 = end && *end ? next_token(end, ':') : NULL;\n            if (c1 != NULL && (c2 == NULL || c1 < c2)) {\n                if (next_num(c1, &end, &s) != 0)\n                    return;\n\n                if (s == 0)\n                    return;\n            }\n        }\n\n        if ((a > b))\n            return;\n\n        while (a <= b) {\n#ifndef __NetBSD__\n            CPU_SET(a, &cpuset);\n#else\n            cpuset_set(a, cpuset);\n#endif\n            a += s;\n        }\n    }\n\n    if (end && *end)\n        return;\n\n#ifdef __linux__\n    sched_setaffinity(0, sizeof(cpuset), &cpuset);\n#endif\n#ifdef __FreeBSD__\n    cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset);\n#endif\n#ifdef __NetBSD__\n    pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset);\n    cpuset_destroy(cpuset);\n#endif\n}\n\n#endif /* USE_SETCPUAFFINITY */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__)\n#define HAVE_SETPROCTITLE 1\n#else\n#define HAVE_SETPROCTITLE 0\n#endif\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": "Chapter07/redis-6.0.3/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 <stdint.h>\n#include \"solarisfixes.h\"\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(uint32_t state[5], const unsigned char buffer[64])\n{\n    uint32_t a, b, c, d, e;\n    typedef union {\n        unsigned char c[64];\n        uint32_t l[16];\n    } CHAR64LONG16;\n#ifdef SHA1HANDSOFF\n    CHAR64LONG16 block[1];  /* use array to appear as a pointer */\n    memcpy(block, buffer, 64);\n#else\n    /* The following had better never be used because it causes the\n     * pointer-to-const buffer to be cast into a pointer to non-const.\n     * And the result is written through.  I threw a \"const\" in, hoping\n     * this will cause a diagnostic.\n     */\n    CHAR64LONG16* block = (const CHAR64LONG16*)buffer;\n#endif\n    /* Copy context->state[] to working vars */\n    a = state[0];\n    b = state[1];\n    c = state[2];\n    d = state[3];\n    e = state[4];\n    /* 4 rounds of 20 operations each. Loop unrolled. */\n    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);\n    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);\n    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);\n    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);\n    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);\n    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);\n    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);\n    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);\n    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);\n    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);\n    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);\n    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);\n    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);\n    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);\n    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);\n    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);\n    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);\n    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);\n    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);\n    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);\n    /* Add the working vars back into context.state[] */\n    state[0] += a;\n    state[1] += b;\n    state[2] += c;\n    state[3] += d;\n    state[4] += e;\n    /* Wipe variables */\n    a = b = c = d = e = 0;\n#ifdef SHA1HANDSOFF\n    memset(block, '\\0', sizeof(block));\n#endif\n}\n\n\n/* SHA1Init - Initialize new context */\n\nvoid SHA1Init(SHA1_CTX* context)\n{\n    /* SHA1 initialization constants */\n    context->state[0] = 0x67452301;\n    context->state[1] = 0xEFCDAB89;\n    context->state[2] = 0x98BADCFE;\n    context->state[3] = 0x10325476;\n    context->state[4] = 0xC3D2E1F0;\n    context->count[0] = context->count[1] = 0;\n}\n\n\n/* Run your data through this. */\n\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)\n{\n    uint32_t i, j;\n\n    j = context->count[0];\n    if ((context->count[0] += len << 3) < j)\n        context->count[1]++;\n    context->count[1] += (len>>29);\n    j = (j >> 3) & 63;\n    if ((j + len) > 63) {\n        memcpy(&context->buffer[j], data, (i = 64-j));\n        SHA1Transform(context->state, context->buffer);\n        for ( ; i + 63 < len; i += 64) {\n            SHA1Transform(context->state, &data[i]);\n        }\n        j = 0;\n    }\n    else i = 0;\n    memcpy(&context->buffer[j], &data[i], len - i);\n}\n\n\n/* Add padding and return the message digest. */\n\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context)\n{\n    unsigned i;\n    unsigned char finalcount[8];\n    unsigned char c;\n\n#if 0\t/* untested \"improvement\" by DHR */\n    /* Convert context->count to a sequence of bytes\n     * in finalcount.  Second element first, but\n     * big-endian order within element.\n     * But we do it all backwards.\n     */\n    unsigned char *fcp = &finalcount[8];\n\n    for (i = 0; i < 2; i++)\n       {\n        uint32_t t = context->count[i];\n        int j;\n\n        for (j = 0; j < 4; t >>= 8, j++)\n\t          *--fcp = (unsigned char) t;\n    }\n#else\n    for (i = 0; i < 8; i++) {\n        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]\n         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */\n    }\n#endif\n    c = 0200;\n    SHA1Update(context, &c, 1);\n    while ((context->count[0] & 504) != 448) {\n\tc = 0000;\n        SHA1Update(context, &c, 1);\n    }\n    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */\n    for (i = 0; i < 20; i++) {\n        digest[i] = (unsigned char)\n         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);\n    }\n    /* Wipe variables */\n    memset(context, '\\0', sizeof(*context));\n    memset(&finalcount, '\\0', sizeof(finalcount));\n}\n/* ================ end of sha1.c ================ */\n\n#ifdef REDIS_TEST\n#define BUFSIZE 4096\n\n#define UNUSED(x) (void)(x)\nint sha1Test(int argc, char **argv)\n{\n    SHA1_CTX ctx;\n    unsigned char hash[20], buf[BUFSIZE];\n    int i;\n\n    UNUSED(argc);\n    UNUSED(argv);\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#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sha1.h",
    "content": "#ifndef SHA1_H\n#define SHA1_H\n/* ================ sha1.h ================ */\n/*\nSHA-1 in C\nBy Steve Reid <steve@edmweb.com>\n100% Public Domain\n*/\n\ntypedef struct {\n    uint32_t state[5];\n    uint32_t count[2];\n    unsigned char buffer[64];\n} SHA1_CTX;\n\nvoid SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);\nvoid SHA1Init(SHA1_CTX* context);\nvoid SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);\nvoid SHA1Final(unsigned char digest[20], SHA1_CTX* context);\n\n#ifdef REDIS_TEST\nint sha1Test(int argc, char **argv);\n#endif\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sha256.c",
    "content": "/*********************************************************************\n* Filename:   sha256.c\n* Author:     Brad Conte (brad AT bradconte.com)\n* Copyright:\n* Disclaimer: This code is presented \"as is\" without any guarantees.\n* Details:    Implementation of the SHA-256 hashing algorithm.\n              SHA-256 is one of the three algorithms in the SHA2\n              specification. The others, SHA-384 and SHA-512, are not\n              offered in this implementation.\n              Algorithm specification can be found here:\n               * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf\n              This implementation uses little endian byte order.\n*********************************************************************/\n\n/*************************** HEADER FILES ***************************/\n#include <stdlib.h>\n#include <string.h>\n#include \"sha256.h\"\n\n/****************************** MACROS ******************************/\n#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))\n#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))\n\n#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n\n/**************************** VARIABLES *****************************/\nstatic const WORD k[64] = {\n\t0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n\t0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n\t0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n\t0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n\t0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n\t0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n\t0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n\t0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n};\n\n/*********************** FUNCTION DEFINITIONS ***********************/\nvoid sha256_transform(SHA256_CTX *ctx, const BYTE data[])\n{\n\tWORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n\n\tfor (i = 0, j = 0; i < 16; ++i, j += 4)\n\t\tm[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n\tfor ( ; i < 64; ++i)\n\t\tm[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n\n\ta = ctx->state[0];\n\tb = ctx->state[1];\n\tc = ctx->state[2];\n\td = ctx->state[3];\n\te = ctx->state[4];\n\tf = ctx->state[5];\n\tg = ctx->state[6];\n\th = ctx->state[7];\n\n\tfor (i = 0; i < 64; ++i) {\n\t\tt1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];\n\t\tt2 = EP0(a) + MAJ(a,b,c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + t1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = t1 + t2;\n\t}\n\n\tctx->state[0] += a;\n\tctx->state[1] += b;\n\tctx->state[2] += c;\n\tctx->state[3] += d;\n\tctx->state[4] += e;\n\tctx->state[5] += f;\n\tctx->state[6] += g;\n\tctx->state[7] += h;\n}\n\nvoid sha256_init(SHA256_CTX *ctx)\n{\n\tctx->datalen = 0;\n\tctx->bitlen = 0;\n\tctx->state[0] = 0x6a09e667;\n\tctx->state[1] = 0xbb67ae85;\n\tctx->state[2] = 0x3c6ef372;\n\tctx->state[3] = 0xa54ff53a;\n\tctx->state[4] = 0x510e527f;\n\tctx->state[5] = 0x9b05688c;\n\tctx->state[6] = 0x1f83d9ab;\n\tctx->state[7] = 0x5be0cd19;\n}\n\nvoid sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)\n{\n\tWORD i;\n\n\tfor (i = 0; i < len; ++i) {\n\t\tctx->data[ctx->datalen] = data[i];\n\t\tctx->datalen++;\n\t\tif (ctx->datalen == 64) {\n\t\t\tsha256_transform(ctx, ctx->data);\n\t\t\tctx->bitlen += 512;\n\t\t\tctx->datalen = 0;\n\t\t}\n\t}\n}\n\nvoid sha256_final(SHA256_CTX *ctx, BYTE hash[])\n{\n\tWORD i;\n\n\ti = ctx->datalen;\n\n\t// Pad whatever data is left in the buffer.\n\tif (ctx->datalen < 56) {\n\t\tctx->data[i++] = 0x80;\n\t\twhile (i < 56)\n\t\t\tctx->data[i++] = 0x00;\n\t}\n\telse {\n\t\tctx->data[i++] = 0x80;\n\t\twhile (i < 64)\n\t\t\tctx->data[i++] = 0x00;\n\t\tsha256_transform(ctx, ctx->data);\n\t\tmemset(ctx->data, 0, 56);\n\t}\n\n\t// Append to the padding the total message's length in bits and transform.\n\tctx->bitlen += ctx->datalen * 8;\n\tctx->data[63] = ctx->bitlen;\n\tctx->data[62] = ctx->bitlen >> 8;\n\tctx->data[61] = ctx->bitlen >> 16;\n\tctx->data[60] = ctx->bitlen >> 24;\n\tctx->data[59] = ctx->bitlen >> 32;\n\tctx->data[58] = ctx->bitlen >> 40;\n\tctx->data[57] = ctx->bitlen >> 48;\n\tctx->data[56] = ctx->bitlen >> 56;\n\tsha256_transform(ctx, ctx->data);\n\n\t// Since this implementation uses little endian byte ordering and SHA uses big endian,\n\t// reverse all the bytes when copying the final state to the output hash.\n\tfor (i = 0; i < 4; ++i) {\n\t\thash[i]      = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 4]  = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 8]  = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n\t}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sha256.h",
    "content": "/*********************************************************************\n* Filename:   sha256.h\n* Author:     Brad Conte (brad AT bradconte.com)\n* Copyright:\n* Disclaimer: This code is presented \"as is\" without any guarantees.\n* Details:    Defines the API for the corresponding SHA1 implementation.\n*********************************************************************/\n\n#ifndef SHA256_H\n#define SHA256_H\n\n/*************************** HEADER FILES ***************************/\n#include <stddef.h>\n#include <stdint.h>\n\n/****************************** MACROS ******************************/\n#define SHA256_BLOCK_SIZE 32            // SHA256 outputs a 32 byte digest\n\n/**************************** DATA TYPES ****************************/\ntypedef uint8_t BYTE;   // 8-bit byte\ntypedef uint32_t WORD;  // 32-bit word\n\ntypedef struct {\n\tBYTE data[64];\n\tWORD datalen;\n\tunsigned long long bitlen;\n\tWORD state[8];\n} SHA256_CTX;\n\n/*********************** FUNCTION DECLARATIONS **********************/\nvoid sha256_init(SHA256_CTX *ctx);\nvoid sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);\nvoid sha256_final(SHA256_CTX *ctx, BYTE hash[]);\n\n#endif   // SHA256_H\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/siphash.c",
    "content": "/*\n   SipHash reference C implementation\n\n   Copyright (c) 2012-2016 Jean-Philippe Aumasson\n   <jeanphilippe.aumasson@gmail.com>\n   Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>\n   Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>\n\n   To the extent possible under law, the author(s) have dedicated all copyright\n   and related and neighboring rights to this software to the public domain\n   worldwide. This software is distributed without any warranty.\n\n   You should have received a copy of the CC0 Public Domain Dedication along\n   with this software. If not, see\n   <http://creativecommons.org/publicdomain/zero/1.0/>.\n\n   ----------------------------------------------------------------------------\n\n   This version was modified by Salvatore Sanfilippo <antirez@gmail.com>\n   in the following ways:\n\n   1. We use SipHash 1-2. This is not believed to be as strong as the\n      suggested 2-4 variant, but AFAIK there are not trivial attacks\n      against this reduced-rounds version, and it runs at the same speed\n      as Murmurhash2 that we used previously, why the 2-4 variant slowed\n      down Redis by a 4% figure more or less.\n   2. Hard-code rounds in the hope the compiler can optimize it more\n      in this raw from. Anyway we always want the standard 2-4 variant.\n   3. Modify the prototype and implementation so that the function directly\n      returns an uint64_t value, the hash itself, instead of receiving an\n      output buffer. This also means that the output size is set to 8 bytes\n      and the 16 bytes output code handling was removed.\n   4. Provide a case insensitive variant to be used when hashing strings that\n      must be considered identical by the hash table regardless of the case.\n      If we don't have directly a case insensitive hash function, we need to\n      perform a text transformation in some temporary buffer, which is costly.\n   5. Remove debugging code.\n   6. Modified the original test.c file to be a stand-alone function testing\n      the function in the new form (returing an uint64_t) using just the\n      relevant test vector.\n */\n#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n#include <ctype.h>\n\n/* Fast tolower() alike function that does not care about locale\n * but just returns a-z insetad of A-Z. */\nint siptlw(int c) {\n    if (c >= 'A' && c <= 'Z') {\n        return c+('a'-'A');\n    } else {\n        return c;\n    }\n}\n\n/* Test of the CPU is Little Endian and supports not aligned accesses.\n * Two interesting conditions to speedup the function that happen to be\n * in most of x86 servers. */\n#if defined(__X86_64__) || defined(__x86_64__) || defined (__i386__) \\\n\t|| defined (__aarch64__) || defined (__arm64__)\n#define UNALIGNED_LE_CPU\n#endif\n\n#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))\n\n#define U32TO8_LE(p, v)                                                        \\\n    (p)[0] = (uint8_t)((v));                                                   \\\n    (p)[1] = (uint8_t)((v) >> 8);                                              \\\n    (p)[2] = (uint8_t)((v) >> 16);                                             \\\n    (p)[3] = (uint8_t)((v) >> 24);\n\n#define U64TO8_LE(p, v)                                                        \\\n    U32TO8_LE((p), (uint32_t)((v)));                                           \\\n    U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));\n\n#ifdef UNALIGNED_LE_CPU\n#define U8TO64_LE(p) (*((uint64_t*)(p)))\n#else\n#define U8TO64_LE(p)                                                           \\\n    (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |                        \\\n     ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) |                 \\\n     ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) |                 \\\n     ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))\n#endif\n\n#define U8TO64_LE_NOCASE(p)                                                    \\\n    (((uint64_t)(siptlw((p)[0]))) |                                           \\\n     ((uint64_t)(siptlw((p)[1])) << 8) |                                      \\\n     ((uint64_t)(siptlw((p)[2])) << 16) |                                     \\\n     ((uint64_t)(siptlw((p)[3])) << 24) |                                     \\\n     ((uint64_t)(siptlw((p)[4])) << 32) |                                              \\\n     ((uint64_t)(siptlw((p)[5])) << 40) |                                              \\\n     ((uint64_t)(siptlw((p)[6])) << 48) |                                              \\\n     ((uint64_t)(siptlw((p)[7])) << 56))\n\n#define SIPROUND                                                               \\\n    do {                                                                       \\\n        v0 += v1;                                                              \\\n        v1 = ROTL(v1, 13);                                                     \\\n        v1 ^= v0;                                                              \\\n        v0 = ROTL(v0, 32);                                                     \\\n        v2 += v3;                                                              \\\n        v3 = ROTL(v3, 16);                                                     \\\n        v3 ^= v2;                                                              \\\n        v0 += v3;                                                              \\\n        v3 = ROTL(v3, 21);                                                     \\\n        v3 ^= v0;                                                              \\\n        v2 += v1;                                                              \\\n        v1 = ROTL(v1, 17);                                                     \\\n        v1 ^= v2;                                                              \\\n        v2 = ROTL(v2, 32);                                                     \\\n    } while (0)\n\nuint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) {\n#ifndef UNALIGNED_LE_CPU\n    uint64_t hash;\n    uint8_t *out = (uint8_t*) &hash;\n#endif\n    uint64_t v0 = 0x736f6d6570736575ULL;\n    uint64_t v1 = 0x646f72616e646f6dULL;\n    uint64_t v2 = 0x6c7967656e657261ULL;\n    uint64_t v3 = 0x7465646279746573ULL;\n    uint64_t k0 = U8TO64_LE(k);\n    uint64_t k1 = U8TO64_LE(k + 8);\n    uint64_t m;\n    const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));\n    const int left = inlen & 7;\n    uint64_t b = ((uint64_t)inlen) << 56;\n    v3 ^= k1;\n    v2 ^= k0;\n    v1 ^= k1;\n    v0 ^= k0;\n\n    for (; in != end; in += 8) {\n        m = U8TO64_LE(in);\n        v3 ^= m;\n\n        SIPROUND;\n\n        v0 ^= m;\n    }\n\n    switch (left) {\n    case 7: b |= ((uint64_t)in[6]) << 48; /* fall-thru */\n    case 6: b |= ((uint64_t)in[5]) << 40; /* fall-thru */\n    case 5: b |= ((uint64_t)in[4]) << 32; /* fall-thru */\n    case 4: b |= ((uint64_t)in[3]) << 24; /* fall-thru */\n    case 3: b |= ((uint64_t)in[2]) << 16; /* fall-thru */\n    case 2: b |= ((uint64_t)in[1]) << 8; /* fall-thru */\n    case 1: b |= ((uint64_t)in[0]); break;\n    case 0: break;\n    }\n\n    v3 ^= b;\n\n    SIPROUND;\n\n    v0 ^= b;\n    v2 ^= 0xff;\n\n    SIPROUND;\n    SIPROUND;\n\n    b = v0 ^ v1 ^ v2 ^ v3;\n#ifndef UNALIGNED_LE_CPU\n    U64TO8_LE(out, b);\n    return hash;\n#else\n    return b;\n#endif\n}\n\nuint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)\n{\n#ifndef UNALIGNED_LE_CPU\n    uint64_t hash;\n    uint8_t *out = (uint8_t*) &hash;\n#endif\n    uint64_t v0 = 0x736f6d6570736575ULL;\n    uint64_t v1 = 0x646f72616e646f6dULL;\n    uint64_t v2 = 0x6c7967656e657261ULL;\n    uint64_t v3 = 0x7465646279746573ULL;\n    uint64_t k0 = U8TO64_LE(k);\n    uint64_t k1 = U8TO64_LE(k + 8);\n    uint64_t m;\n    const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));\n    const int left = inlen & 7;\n    uint64_t b = ((uint64_t)inlen) << 56;\n    v3 ^= k1;\n    v2 ^= k0;\n    v1 ^= k1;\n    v0 ^= k0;\n\n    for (; in != end; in += 8) {\n        m = U8TO64_LE_NOCASE(in);\n        v3 ^= m;\n\n        SIPROUND;\n\n        v0 ^= m;\n    }\n\n    switch (left) {\n    case 7: b |= ((uint64_t)siptlw(in[6])) << 48; /* fall-thru */\n    case 6: b |= ((uint64_t)siptlw(in[5])) << 40; /* fall-thru */\n    case 5: b |= ((uint64_t)siptlw(in[4])) << 32; /* fall-thru */\n    case 4: b |= ((uint64_t)siptlw(in[3])) << 24; /* fall-thru */\n    case 3: b |= ((uint64_t)siptlw(in[2])) << 16; /* fall-thru */\n    case 2: b |= ((uint64_t)siptlw(in[1])) << 8; /* fall-thru */\n    case 1: b |= ((uint64_t)siptlw(in[0])); break;\n    case 0: break;\n    }\n\n    v3 ^= b;\n\n    SIPROUND;\n\n    v0 ^= b;\n    v2 ^= 0xff;\n\n    SIPROUND;\n    SIPROUND;\n\n    b = v0 ^ v1 ^ v2 ^ v3;\n#ifndef UNALIGNED_LE_CPU\n    U64TO8_LE(out, b);\n    return hash;\n#else\n    return b;\n#endif\n}\n\n\n/* --------------------------------- TEST ------------------------------------ */\n\n#ifdef SIPHASH_TEST\n\nconst uint8_t vectors_sip64[64][8] = {\n    { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },\n    { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },\n    { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },\n    { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },\n    { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },\n    { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },\n    { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },\n    { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },\n    { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },\n    { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },\n    { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },\n    { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },\n    { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },\n    { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },\n    { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },\n    { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },\n    { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },\n    { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },\n    { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },\n    { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },\n    { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },\n    { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },\n    { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },\n    { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },\n    { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },\n    { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },\n    { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },\n    { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },\n    { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },\n    { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },\n    { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },\n    { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },\n    { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },\n    { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },\n    { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },\n    { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },\n    { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },\n    { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },\n    { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },\n    { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },\n    { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },\n    { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },\n    { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },\n    { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },\n    { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },\n    { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },\n    { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },\n    { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },\n    { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },\n    { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },\n    { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },\n    { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },\n    { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },\n    { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },\n    { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },\n    { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },\n    { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },\n    { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },\n    { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },\n    { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },\n    { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },\n    { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },\n    { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },\n    { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, },\n};\n\n\n/* Test siphash using a test vector. Returns 0 if the function passed\n * all the tests, otherwise 1 is returned.\n *\n * IMPORTANT: The test vector is for SipHash 2-4. Before running\n * the test revert back the siphash() function to 2-4 rounds since\n * now it uses 1-2 rounds. */\nint siphash_test(void) {\n    uint8_t in[64], k[16];\n    int i;\n    int fails = 0;\n\n    for (i = 0; i < 16; ++i)\n        k[i] = i;\n\n    for (i = 0; i < 64; ++i) {\n        in[i] = i;\n        uint64_t hash = siphash(in, i, k);\n        const uint8_t *v = NULL;\n        v = (uint8_t *)vectors_sip64;\n        if (memcmp(&hash, v + (i * 8), 8)) {\n            /* printf(\"fail for %d bytes\\n\", i); */\n            fails++;\n        }\n    }\n\n    /* Run a few basic tests with the case insensitive version. */\n    uint64_t h1, h2;\n    h1 = siphash((uint8_t*)\"hello world\",11,(uint8_t*)\"1234567812345678\");\n    h2 = siphash_nocase((uint8_t*)\"hello world\",11,(uint8_t*)\"1234567812345678\");\n    if (h1 != h2) fails++;\n\n    h1 = siphash((uint8_t*)\"hello world\",11,(uint8_t*)\"1234567812345678\");\n    h2 = siphash_nocase((uint8_t*)\"HELLO world\",11,(uint8_t*)\"1234567812345678\");\n    if (h1 != h2) fails++;\n\n    h1 = siphash((uint8_t*)\"HELLO world\",11,(uint8_t*)\"1234567812345678\");\n    h2 = siphash_nocase((uint8_t*)\"HELLO world\",11,(uint8_t*)\"1234567812345678\");\n    if (h1 == h2) fails++;\n\n    if (!fails) return 0;\n    return 1;\n}\n\nint main(void) {\n    if (siphash_test() == 0) {\n        printf(\"SipHash test: OK\\n\");\n        return 0;\n    } else {\n        printf(\"SipHash test: FAILED\\n\");\n        return 1;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 * 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 * 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 * 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 \"server.h\"\n#include \"slowlog.h\"\n\n/* Create a new slowlog entry.\n * Incrementing the ref count of all the objects retained is up to\n * this function. */\nslowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long duration) {\n    slowlogEntry *se = zmalloc(sizeof(*se));\n    int j, slargc = argc;\n\n    if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;\n    se->argc = slargc;\n    se->argv = 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        if (slargc != argc && j == slargc-1) {\n            se->argv[j] = createObject(OBJ_STRING,\n                sdscatprintf(sdsempty(),\"... (%d more arguments)\",\n                argc-slargc+1));\n        } else {\n            /* Trim too long strings as well... */\n            if (argv[j]->type == OBJ_STRING &&\n                sdsEncodedObject(argv[j]) &&\n                sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)\n            {\n                sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);\n\n                s = sdscatprintf(s,\"... (%lu more bytes)\",\n                    (unsigned long)\n                    sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);\n                se->argv[j] = createObject(OBJ_STRING,s);\n            } else if (argv[j]->refcount == OBJ_SHARED_REFCOUNT) {\n                se->argv[j] = argv[j];\n            } else {\n                /* Here we need to dupliacate the string objects composing the\n                 * argument vector of the command, because those may otherwise\n                 * end shared with string objects stored into keys. Having\n                 * shared objects between any part of Redis, and the data\n                 * structure holding the data, is a problem: FLUSHALL ASYNC\n                 * may release the shared string object and create a race. */\n                se->argv[j] = dupStringObject(argv[j]);\n            }\n        }\n    }\n    se->time = time(NULL);\n    se->duration = duration;\n    se->id = server.slowlog_entry_id++;\n    se->peerid = sdsnew(getClientPeerId(c));\n    se->cname = c->name ? sdsnew(c->name->ptr) : sdsempty();\n    return se;\n}\n\n/* Free a slow log entry. The argument is void so that the prototype of this\n * function matches the one of the 'free' method of adlist.c.\n *\n * This function will take care to release all the retained object. */\nvoid slowlogFreeEntry(void *septr) {\n    slowlogEntry *se = septr;\n    int j;\n\n    for (j = 0; j < se->argc; j++)\n        decrRefCount(se->argv[j]);\n    zfree(se->argv);\n    sdsfree(se->peerid);\n    sdsfree(se->cname);\n    zfree(se);\n}\n\n/* Initialize the slow log. This function should be called a single time\n * at server startup. */\nvoid slowlogInit(void) {\n    server.slowlog = listCreate();\n    server.slowlog_entry_id = 0;\n    listSetFreeMethod(server.slowlog,slowlogFreeEntry);\n}\n\n/* Push a new entry into the slow log.\n * This function will make sure to trim the slow log accordingly to the\n * configured max length. */\nvoid slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration) {\n    if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */\n    if (duration >= server.slowlog_log_slower_than)\n        listAddNodeHead(server.slowlog,\n                        slowlogCreateEntry(c,argv,argc,duration));\n\n    /* Remove old entries if needed. */\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. */\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. */\nvoid slowlogCommand(client *c) {\n    if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"help\")) {\n        const char *help[] = {\n\"GET [count] -- Return top entries from the slowlog (default: 10).\"\n\"    Entries are made of:\",\n\"    id, timestamp, time in microseconds, arguments array, client IP and port, client name\",\n\"LEN -- Return the length of the slowlog.\",\n\"RESET -- Reset the slowlog.\",\nNULL\n        };\n        addReplyHelp(c, help);\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"reset\")) {\n        slowlogReset();\n        addReply(c,shared.ok);\n    } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,\"len\")) {\n        addReplyLongLong(c,listLength(server.slowlog));\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) != C_OK)\n            return;\n\n        listRewind(server.slowlog,&li);\n        totentries = addReplyDeferredLen(c);\n        while(count-- && (ln = listNext(&li))) {\n            int j;\n\n            se = ln->value;\n            addReplyArrayLen(c,6);\n            addReplyLongLong(c,se->id);\n            addReplyLongLong(c,se->time);\n            addReplyLongLong(c,se->duration);\n            addReplyArrayLen(c,se->argc);\n            for (j = 0; j < se->argc; j++)\n                addReplyBulk(c,se->argv[j]);\n            addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid));\n            addReplyBulkCBuffer(c,se->cname,sdslen(se->cname));\n            sent++;\n        }\n        setDeferredArrayLen(c,totentries,sent);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#ifndef __SLOWLOG_H__\n#define __SLOWLOG_H__\n\n#define SLOWLOG_ENTRY_MAX_ARGC 32\n#define SLOWLOG_ENTRY_MAX_STRING 128\n\n/* This structure defines an entry inside the slow log list */\ntypedef struct slowlogEntry {\n    robj **argv;\n    int argc;\n    long long id;       /* Unique entry identifier. */\n    long long duration; /* Time spent by the query, in microseconds. */\n    time_t time;        /* Unix time at which the query was executed. */\n    sds cname;          /* Client name. */\n    sds peerid;         /* Client network address. */\n} slowlogEntry;\n\n/* Exported API */\nvoid slowlogInit(void);\nvoid slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration);\n\n/* Exported commands */\nvoid slowlogCommand(client *c);\n\n#endif /* __SLOWLOG_H__ */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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(__sun)\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\n#endif /* __sun */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include \"pqsort.h\" /* Partial qsort for SORT+LIMIT */\n#include <math.h> /* isnan() */\n\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);\n\nredisSortOperation *createSortOperation(int type, robj *pattern) {\n    redisSortOperation *so = zmalloc(sizeof(*so));\n    so->type = type;\n    so->pattern = pattern;\n    return so;\n}\n\n/* Return the value associated to the key with a name obtained using\n * the following rules:\n *\n * 1) The first occurrence of '*' in 'pattern' is substituted with '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 *\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 *\n * The returned object will always have its refcount increased by 1\n * when it is non-NULL. */\nrobj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag) {\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    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 = getDecodedObject(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    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    if ((f = strstr(p+1, \"->\")) != NULL && *(f+2) != '\\0') {\n        fieldlen = sdslen(spat)-(f-spat)-2;\n        fieldobj = createStringObject(f+2,fieldlen);\n    } else {\n        fieldlen = 0;\n    }\n\n    /* Perform the '*' substitution. */\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    if (!writeflag)\n        o = lookupKeyRead(db,keyobj);\n    else\n        o = lookupKeyWrite(db,keyobj);\n    if (o == NULL) goto noobj;\n\n    if (fieldobj) {\n        if (o->type != OBJ_HASH) goto noobj;\n\n        /* Retrieve value from hash by the field name. The returend object\n         * is a new object with refcount already incremented. */\n        o = hashTypeGetValueObject(o, fieldobj->ptr);\n    } else {\n        if (o->type != OBJ_STRING) goto noobj;\n\n        /* Every object that this function returns needs to have its refcount\n         * increased. sortCommand decreases it again. */\n        incrRefCount(o);\n    }\n    decrRefCount(keyobj);\n    if (fieldobj) decrRefCount(fieldobj);\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 */\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        /* Numeric sorting. Here it's trivial as we precomputed scores */\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            cmp = compareStringObjects(so1->obj,so2->obj);\n        }\n    } else {\n        /* Alphanumeric sorting */\n        if (server.sort_bypattern) {\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                if (server.sort_store) {\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                    cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);\n                }\n            }\n        } else {\n            /* Compare elements directly. */\n            if (server.sort_store) {\n                cmp = compareStringObjects(so1->obj,so2->obj);\n            } else {\n                cmp = collateStringObjects(so1->obj,so2->obj);\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(client *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_conversion_error = 0;\n    int syntax_error = 0;\n    robj *sortval, *sortby = NULL, *storekey = NULL;\n    redisSortObject *vector; /* Resulting vector to sort */\n\n    /* Create a list of operations to perform for every sorted element.\n     * Operations can be GET */\n    operations = listCreate();\n    listSetFreeMethod(operations,zfree);\n    j = 2; /* options start at argv[2] */\n\n    /* The SORT command has an SQL-alike syntax, parse it */\n    while(j < c->argc) {\n        int leftargs = c->argc-j-1;\n        if (!strcasecmp(c->argv[j]->ptr,\"asc\")) {\n            desc = 0;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"desc\")) {\n            desc = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"alpha\")) {\n            alpha = 1;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"limit\") && leftargs >= 2) {\n            if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL)\n                 != C_OK) ||\n                (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL)\n                 != C_OK))\n            {\n                syntax_error++;\n                break;\n            }\n            j+=2;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"store\") && leftargs >= 1) {\n            storekey = c->argv[j+1];\n            j++;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"by\") && leftargs >= 1) {\n            sortby = c->argv[j+1];\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            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        } else if (!strcasecmp(c->argv[j]->ptr,\"get\") && leftargs >= 1) {\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                SORT_OP_GET,c->argv[j+1]));\n            getop++;\n            j++;\n        } else {\n            addReply(c,shared.syntaxerr);\n            syntax_error++;\n            break;\n        }\n        j++;\n    }\n\n    /* Handle syntax errors set during options parsing. */\n    if (syntax_error) {\n        listRelease(operations);\n        return;\n    }\n\n    /* Lookup the key to sort. It must be of the right types */\n    if (storekey)\n        sortval = lookupKeyRead(c->db,c->argv[1]);\n    else\n        sortval = lookupKeyWrite(c->db,c->argv[1]);\n    if (sortval && sortval->type != OBJ_SET &&\n                   sortval->type != OBJ_LIST &&\n                   sortval->type != OBJ_ZSET)\n    {\n        listRelease(operations);\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\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    if (sortval)\n        incrRefCount(sortval);\n    else\n        sortval = createQuicklistObject();\n\n\n    /* When sorting a set with no sort specified, we must sort the output\n     * so the result is consistent across scripting and replication.\n     *\n     * The other types (list, sorted set) will retain their native order\n     * even if no sort order is requested, so they remain stable across\n     * scripting and replication. */\n    if (dontsort &&\n        sortval->type == OBJ_SET &&\n        (storekey || c->flags & CLIENT_LUA))\n    {\n        /* Force ALPHA sorting */\n        dontsort = 0;\n        alpha = 1;\n        sortby = NULL;\n    }\n\n    /* Destructively convert encoded sorted sets for SORT. */\n    if (sortval->type == OBJ_ZSET)\n        zsetConvert(sortval, OBJ_ENCODING_SKIPLIST);\n\n    /* Objtain the length of the object to sort. */\n    switch(sortval->type) {\n    case OBJ_LIST: vectorlen = listTypeLength(sortval); break;\n    case OBJ_SET: vectorlen =  setTypeSize(sortval); break;\n    case OBJ_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;\n    default: vectorlen = 0; serverPanic(\"Bad SORT type\"); /* Avoid GCC warning */\n    }\n\n    /* Perform LIMIT start,count sanity checking. */\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    /* Whenever possible, we load elements into the output array in a more\n     * direct way. This is possible if:\n     *\n     * 1) The object to sort is a sorted set or a list (internally sorted).\n     * 2) There is nothing to sort as dontsort is true (BY <constant string>).\n     *\n     * In this special case, if we have a LIMIT option that actually reduces\n     * the number of elements to fetch, we also optimize to just load the\n     * range we are interested in and allocating a vector that is big enough\n     * for the selected range length. */\n    if ((sortval->type == OBJ_ZSET || sortval->type == OBJ_LIST) &&\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    vector = zmalloc(sizeof(redisSortObject)*vectorlen);\n    j = 0;\n\n    if (sortval->type == OBJ_LIST && dontsort) {\n        /* Special handling for a list, if 'dontsort' is true.\n         * This makes sure we return elements in the list 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        if (end >= start) {\n            listTypeIterator *li;\n            listTypeEntry entry;\n            li = listTypeInitIterator(sortval,\n                    desc ? (long)(listTypeLength(sortval) - start - 1) : start,\n                    desc ? LIST_HEAD : LIST_TAIL);\n\n            while(j < vectorlen && 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            /* Fix start/end: output code is not aware of this optimization. */\n            end -= start;\n            start = 0;\n        }\n    } else if (sortval->type == OBJ_LIST) {\n        listTypeIterator *li = listTypeInitIterator(sortval,0,LIST_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    } else if (sortval->type == OBJ_SET) {\n        setTypeIterator *si = setTypeInitIterator(sortval);\n        sds sdsele;\n        while((sdsele = setTypeNextObject(si)) != NULL) {\n            vector[j].obj = createObject(OBJ_STRING,sdsele);\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        setTypeReleaseIterator(si);\n    } else if (sortval->type == OBJ_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        zset *zs = sortval->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n        sds sdsele;\n        int rangelen = vectorlen;\n\n        /* Check if starting point is trivial, before doing log(N) lookup. */\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        while(rangelen--) {\n            serverAssertWithInfo(c,sortval,ln != NULL);\n            sdsele = ln->ele;\n            vector[j].obj = createStringObject(sdsele,sdslen(sdsele));\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        /* Fix start/end: output code is not aware of this optimization. */\n        end -= start;\n        start = 0;\n    } else if (sortval->type == OBJ_ZSET) {\n        dict *set = ((zset*)sortval->ptr)->dict;\n        dictIterator *di;\n        dictEntry *setele;\n        sds sdsele;\n        di = dictGetIterator(set);\n        while((setele = dictNext(di)) != NULL) {\n            sdsele =  dictGetKey(setele);\n            vector[j].obj = createStringObject(sdsele,sdslen(sdsele));\n            vector[j].u.score = 0;\n            vector[j].u.cmpobj = NULL;\n            j++;\n        }\n        dictReleaseIterator(di);\n    } else {\n        serverPanic(\"Unknown type\");\n    }\n    serverAssertWithInfo(c,sortval,j == vectorlen);\n\n    /* Now it's time to load the right scores in the sorting vector */\n    if (!dontsort) {\n        for (j = 0; j < vectorlen; j++) {\n            robj *byval;\n            if (sortby) {\n                /* lookup value to sort by */\n                byval = lookupKeyByPattern(c->db,sortby,vector[j].obj,storekey!=NULL);\n                if (!byval) continue;\n            } else {\n                /* use object itself to sort by */\n                byval = vector[j].obj;\n            }\n\n            if (alpha) {\n                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);\n            } else {\n                if (sdsEncodedObject(byval)) {\n                    char *eptr;\n\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_conversion_error = 1;\n                    }\n                } else if (byval->encoding == OBJ_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                    vector[j].u.score = (long)byval->ptr;\n                } else {\n                    serverAssertWithInfo(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        server.sort_desc = desc;\n        server.sort_alpha = alpha;\n        server.sort_bypattern = sortby ? 1 : 0;\n        server.sort_store = storekey ? 1 : 0;\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    outputlen = getop ? getop*(end-start+1) : end-start+1;\n    if (int_conversion_error) {\n        addReplyError(c,\"One or more scores can't be converted into double\");\n    } else if (storekey == NULL) {\n        /* STORE option not specified, sent the sorting result to client */\n        addReplyArrayLen(c,outputlen);\n        for (j = start; j <= end; j++) {\n            listNode *ln;\n            listIter li;\n\n            if (!getop) addReplyBulk(c,vector[j].obj);\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,storekey!=NULL);\n\n                if (sop->type == SORT_OP_GET) {\n                    if (!val) {\n                        addReplyNull(c);\n                    } else {\n                        addReplyBulk(c,val);\n                        decrRefCount(val);\n                    }\n                } else {\n                    /* Always fails */\n                    serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET);\n                }\n            }\n        }\n    } else {\n        robj *sobj = createQuicklistObject();\n\n        /* STORE option specified, set the sorting result as a List object */\n        for (j = start; j <= end; j++) {\n            listNode *ln;\n            listIter li;\n\n            if (!getop) {\n                listTypePush(sobj,vector[j].obj,LIST_TAIL);\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,storekey!=NULL);\n\n                    if (sop->type == SORT_OP_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,LIST_TAIL);\n                        decrRefCount(val);\n                    } else {\n                        /* Always fails */\n                        serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET);\n                    }\n                }\n            }\n        }\n        if (outputlen) {\n            setKey(c,c->db,storekey,sobj);\n            notifyKeyspaceEvent(NOTIFY_LIST,\"sortstore\",storekey,\n                                c->db->id);\n            server.dirty += outputlen;\n        } else if (dbDelete(c->db,storekey)) {\n            signalModifiedKey(c,c->db,storekey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",storekey,c->db->id);\n            server.dirty++;\n        }\n        decrRefCount(sobj);\n        addReplyLongLong(c,outputlen);\n    }\n\n    /* Cleanup */\n    for (j = 0; j < vectorlen; j++)\n        decrRefCount(vector[j].obj);\n\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": "Chapter07/redis-6.0.3/src/sparkline.c",
    "content": "/* sparkline.c -- ASCII Sparklines\n * This code is modified from http://github.com/antirez/aspark and adapted\n * in order to return SDS strings instead of outputting directly to\n * the terminal.\n *\n * ---------------------------------------------------------------------------\n *\n * Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@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 *\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 \"server.h\"\n\n#include <math.h>\n\n/* This is the charset used to display the graphs, but multiple rows are used\n * to increase the resolution. */\nstatic char charset[] = \"_-`\";\nstatic char charset_fill[] = \"_o#\";\nstatic int charset_len = sizeof(charset)-1;\nstatic int label_margin_top = 1;\n\n/* ----------------------------------------------------------------------------\n * Sequences are arrays of samples we use to represent data to turn\n * into sparklines. This is the API in order to generate a sparkline:\n *\n * struct sequence *seq = createSparklineSequence();\n * sparklineSequenceAddSample(seq, 10, NULL);\n * sparklineSequenceAddSample(seq, 20, NULL);\n * sparklineSequenceAddSample(seq, 30, \"last sample label\");\n * sds output = sparklineRender(sdsempty(), seq, 80, 4, SPARKLINE_FILL);\n * freeSparklineSequence(seq);\n * ------------------------------------------------------------------------- */\n\n/* Create a new sequence. */\nstruct sequence *createSparklineSequence(void) {\n    struct sequence *seq = zmalloc(sizeof(*seq));\n    seq->length = 0;\n    seq->samples = NULL;\n    return seq;\n}\n\n/* Add a new sample into a sequence. */\nvoid sparklineSequenceAddSample(struct sequence *seq, double value, char *label) {\n    label = (label == NULL || label[0] == '\\0') ? NULL : zstrdup(label);\n    if (seq->length == 0) {\n        seq->min = seq->max = value;\n    } else {\n        if (value < seq->min) seq->min = value;\n        else if (value > seq->max) seq->max = value;\n    }\n    seq->samples = zrealloc(seq->samples,sizeof(struct sample)*(seq->length+1));\n    seq->samples[seq->length].value = value;\n    seq->samples[seq->length].label = label;\n    seq->length++;\n    if (label) seq->labels++;\n}\n\n/* Free a sequence. */\nvoid freeSparklineSequence(struct sequence *seq) {\n    int j;\n\n    for (j = 0; j < seq->length; j++)\n        zfree(seq->samples[j].label);\n    zfree(seq->samples);\n    zfree(seq);\n}\n\n/* ----------------------------------------------------------------------------\n * ASCII rendering of sequence\n * ------------------------------------------------------------------------- */\n\n/* Render part of a sequence, so that render_sequence() call call this function\n * with differnent parts in order to create the full output without overflowing\n * the current terminal columns. */\nsds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) {\n    int j;\n    double relmax = seq->max - seq->min;\n    int steps = charset_len*rows;\n    int row = 0;\n    char *chars = zmalloc(len);\n    int loop = 1;\n    int opt_fill = flags & SPARKLINE_FILL;\n    int opt_log = flags & SPARKLINE_LOG_SCALE;\n\n    if (opt_log) {\n        relmax = log(relmax+1);\n    } else if (relmax == 0) {\n        relmax = 1;\n    }\n\n    while(loop) {\n        loop = 0;\n        memset(chars,' ',len);\n        for (j = 0; j < len; j++) {\n            struct sample *s = &seq->samples[j+offset];\n            double relval = s->value - seq->min;\n            int step;\n\n            if (opt_log) relval = log(relval+1);\n            step = (int) (relval*steps)/relmax;\n            if (step < 0) step = 0;\n            if (step >= steps) step = steps-1;\n\n            if (row < rows) {\n                /* Print the character needed to create the sparkline */\n                int charidx = step-((rows-row-1)*charset_len);\n                loop = 1;\n                if (charidx >= 0 && charidx < charset_len) {\n                    chars[j] = opt_fill ? charset_fill[charidx] :\n                                          charset[charidx];\n                } else if(opt_fill && charidx >= charset_len) {\n                    chars[j] = '|';\n                }\n            } else {\n                /* Labels spacing */\n                if (seq->labels && row-rows < label_margin_top) {\n                    loop = 1;\n                    break;\n                }\n                /* Print the label if needed. */\n                if (s->label) {\n                    int label_len = strlen(s->label);\n                    int label_char = row - rows - label_margin_top;\n\n                    if (label_len > label_char) {\n                        loop = 1;\n                        chars[j] = s->label[label_char];\n                    }\n                }\n            }\n        }\n        if (loop) {\n            row++;\n            output = sdscatlen(output,chars,len);\n            output = sdscatlen(output,\"\\n\",1);\n        }\n    }\n    zfree(chars);\n    return output;\n}\n\n/* Turn a sequence into its ASCII representation */\nsds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags) {\n    int j;\n\n    for (j = 0; j < seq->length; j += columns) {\n        int sublen = (seq->length-j) < columns ? (seq->length-j) : columns;\n\n        if (j != 0) output = sdscatlen(output,\"\\n\",1);\n        output = sparklineRenderRange(output, seq, rows, j, sublen, flags);\n    }\n    return output;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/sparkline.h",
    "content": "/* sparkline.h -- ASCII Sparklines header file\n *\n * ---------------------------------------------------------------------------\n *\n * Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@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 *\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 __SPARKLINE_H\n#define __SPARKLINE_H\n\n/* A sequence is represented of many \"samples\" */\nstruct sample {\n    double value;\n    char *label;\n};\n\nstruct sequence {\n    int length;\n    int labels;\n    struct sample *samples;\n    double min, max;\n};\n\n#define SPARKLINE_NO_FLAGS 0\n#define SPARKLINE_FILL 1      /* Fill the area under the curve. */\n#define SPARKLINE_LOG_SCALE 2 /* Use logarithmic scale. */\n\nstruct sequence *createSparklineSequence(void);\nvoid sparklineSequenceAddSample(struct sequence *seq, double value, char *label);\nvoid freeSparklineSequence(struct sequence *seq);\nsds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags);\nsds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags);\n\n#endif /* __SPARKLINE_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/stream.h",
    "content": "#ifndef STREAM_H\n#define STREAM_H\n\n#include \"rax.h\"\n#include \"listpack.h\"\n\n/* Stream item ID: a 128 bit number composed of a milliseconds time and\n * a sequence counter. IDs generated in the same millisecond (or in a past\n * millisecond if the clock jumped backward) will use the millisecond time\n * of the latest generated ID and an incremented sequence. */\ntypedef struct streamID {\n    uint64_t ms;        /* Unix time in milliseconds. */\n    uint64_t seq;       /* Sequence number. */\n} streamID;\n\ntypedef struct stream {\n    rax *rax;               /* The radix tree holding the stream. */\n    uint64_t length;        /* Number of elements inside this stream. */\n    streamID last_id;       /* Zero if there are yet no items. */\n    rax *cgroups;           /* Consumer groups dictionary: name -> streamCG */\n} stream;\n\n/* We define an iterator to iterate stream items in an abstract way, without\n * caring about the radix tree + listpack representation. Technically speaking\n * the iterator is only used inside streamReplyWithRange(), so could just\n * be implemented inside the function, but practically there is the AOF\n * rewriting code that also needs to iterate the stream to emit the XADD\n * commands. */\ntypedef struct streamIterator {\n    stream *stream;         /* The stream we are iterating. */\n    streamID master_id;     /* ID of the master entry at listpack head. */\n    uint64_t master_fields_count;       /* Master entries # of fields. */\n    unsigned char *master_fields_start; /* Master entries start in listpack. */\n    unsigned char *master_fields_ptr;   /* Master field to emit next. */\n    int entry_flags;                    /* Flags of entry we are emitting. */\n    int rev;                /* True if iterating end to start (reverse). */\n    uint64_t start_key[2];  /* Start key as 128 bit big endian. */\n    uint64_t end_key[2];    /* End key as 128 bit big endian. */\n    raxIterator ri;         /* Rax iterator. */\n    unsigned char *lp;      /* Current listpack. */\n    unsigned char *lp_ele;  /* Current listpack cursor. */\n    unsigned char *lp_flags; /* Current entry flags pointer. */\n    /* Buffers used to hold the string of lpGet() when the element is\n     * integer encoded, so that there is no string representation of the\n     * element inside the listpack itself. */\n    unsigned char field_buf[LP_INTBUF_SIZE];\n    unsigned char value_buf[LP_INTBUF_SIZE];\n} streamIterator;\n\n/* Consumer group. */\ntypedef struct streamCG {\n    streamID last_id;       /* Last delivered (not acknowledged) ID for this\n                               group. Consumers that will just ask for more\n                               messages will served with IDs > than this. */\n    rax *pel;               /* Pending entries list. This is a radix tree that\n                               has every message delivered to consumers (without\n                               the NOACK option) that was yet not acknowledged\n                               as processed. The key of the radix tree is the\n                               ID as a 64 bit big endian number, while the\n                               associated value is a streamNACK structure.*/\n    rax *consumers;         /* A radix tree representing the consumers by name\n                               and their associated representation in the form\n                               of streamConsumer structures. */\n} streamCG;\n\n/* A specific consumer in a consumer group.  */\ntypedef struct streamConsumer {\n    mstime_t seen_time;         /* Last time this consumer was active. */\n    sds name;                   /* Consumer name. This is how the consumer\n                                   will be identified in the consumer group\n                                   protocol. Case sensitive. */\n    rax *pel;                   /* Consumer specific pending entries list: all\n                                   the pending messages delivered to this\n                                   consumer not yet acknowledged. Keys are\n                                   big endian message IDs, while values are\n                                   the same streamNACK structure referenced\n                                   in the \"pel\" of the conumser group structure\n                                   itself, so the value is shared. */\n} streamConsumer;\n\n/* Pending (yet not acknowledged) message in a consumer group. */\ntypedef struct streamNACK {\n    mstime_t delivery_time;     /* Last time this message was delivered. */\n    uint64_t delivery_count;    /* Number of times this message was delivered.*/\n    streamConsumer *consumer;   /* The consumer this message was delivered to\n                                   in the last delivery. */\n} streamNACK;\n\n/* Stream propagation informations, passed to functions in order to propagate\n * XCLAIM commands to AOF and slaves. */\ntypedef struct streamPropInfo {\n    robj *keyname;\n    robj *groupname;\n} streamPropInfo;\n\n/* Prototypes of exported APIs. */\nstruct client;\n\n/* Flags for streamLookupConsumer */\n#define SLC_NONE      0\n#define SLC_NOCREAT   (1<<0) /* Do not create the consumer if it doesn't exist */\n#define SLC_NOREFRESH (1<<1) /* Do not update consumer's seen-time */\n\nstream *streamNew(void);\nvoid freeStream(stream *s);\nunsigned long streamLength(const robj *subject);\nsize_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end, size_t count, int rev, streamCG *group, streamConsumer *consumer, int flags, streamPropInfo *spi);\nvoid streamIteratorStart(streamIterator *si, stream *s, streamID *start, streamID *end, int rev);\nint streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields);\nvoid streamIteratorGetField(streamIterator *si, unsigned char **fieldptr, unsigned char **valueptr, int64_t *fieldlen, int64_t *valuelen);\nvoid streamIteratorStop(streamIterator *si);\nstreamCG *streamLookupCG(stream *s, sds groupname);\nstreamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags);\nstreamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id);\nstreamNACK *streamCreateNACK(streamConsumer *consumer);\nvoid streamDecodeID(void *buf, streamID *id);\nint streamCompareID(streamID *a, streamID *b);\nvoid streamFreeNACK(streamNACK *na);\nvoid streamIncrID(streamID *id);\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.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 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 > SYNCIO__RESOLUTION) ?\n                          remaining : 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 > SYNCIO__RESOLUTION) ?\n                          remaining : 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        size--;\n    }\n    return nread;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.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. Note that we only check string encoded objects\n * as their string length can be queried in constant time. */\nvoid hashTypeTryConversion(robj *o, robj **argv, int start, int end) {\n    int i;\n\n    if (o->encoding != OBJ_ENCODING_ZIPLIST) return;\n\n    for (i = start; i <= end; i++) {\n        if (sdsEncodedObject(argv[i]) &&\n            sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)\n        {\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n            break;\n        }\n    }\n}\n\n/* Get the value from a ziplist encoded hash, identified by field.\n * Returns -1 when the field cannot be found. */\nint hashTypeGetFromZiplist(robj *o, sds 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    serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);\n\n    zl = o->ptr;\n    fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n    if (fptr != NULL) {\n        fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);\n        if (fptr != NULL) {\n            /* Grab pointer to the value (fptr points to the field) */\n            vptr = ziplistNext(zl, fptr);\n            serverAssert(vptr != NULL);\n        }\n    }\n\n    if (vptr != NULL) {\n        ret = ziplistGet(vptr, vstr, vlen, vll);\n        serverAssert(ret);\n        return 0;\n    }\n\n    return -1;\n}\n\n/* Get the value from a hash table encoded hash, identified by field.\n * Returns NULL when the field cannot be found, otherwise the SDS value\n * is returned. */\nsds hashTypeGetFromHashTable(robj *o, sds field) {\n    dictEntry *de;\n\n    serverAssert(o->encoding == OBJ_ENCODING_HT);\n\n    de = dictFind(o->ptr, field);\n    if (de == NULL) return NULL;\n    return dictGetVal(de);\n}\n\n/* Higher level function of hashTypeGet*() that returns the hash value\n * associated with the specified field. If the field is found C_OK\n * is returned, otherwise C_ERR. The returned object is returned by\n * reference in either *vstr and *vlen if it's returned in string form,\n * or stored in *vll if it's returned as a number.\n *\n * If *vll is populated *vstr is set to NULL, so the caller\n * can always check the function return by checking the return value\n * for C_OK and checking if vll (or vstr) is NULL. */\nint hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        *vstr = NULL;\n        if (hashTypeGetFromZiplist(o, field, vstr, vlen, vll) == 0)\n            return C_OK;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        sds value;\n        if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {\n            *vstr = (unsigned char*) value;\n            *vlen = sdslen(value);\n            return C_OK;\n        }\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return C_ERR;\n}\n\n/* Like hashTypeGetValue() but returns a Redis object, which is useful for\n * interaction with the hash type outside t_hash.c.\n * The function returns NULL if the field is not found in the hash. Otherwise\n * a newly allocated string object with the value is returned. */\nrobj *hashTypeGetValueObject(robj *o, sds field) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vll;\n\n    if (hashTypeGetValue(o,field,&vstr,&vlen,&vll) == C_ERR) return NULL;\n    if (vstr) return createStringObject((char*)vstr,vlen);\n    else return createStringObjectFromLongLong(vll);\n}\n\n/* Higher level function using hashTypeGet*() to return the length of the\n * object associated with the requested field, or 0 if the field does not\n * exist. */\nsize_t hashTypeGetValueLength(robj *o, sds field) {\n    size_t len = 0;\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)\n            len = vstr ? vlen : sdigits10(vll);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        sds aux;\n\n        if ((aux = hashTypeGetFromHashTable(o, field)) != NULL)\n            len = sdslen(aux);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return len;\n}\n\n/* Test if the specified field exists in the given hash. Returns 1 if the field\n * exists, and 0 when it doesn't. */\nint hashTypeExists(robj *o, sds field) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        if (hashTypeGetFromHashTable(o, field) != NULL) return 1;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return 0;\n}\n\n/* Add a new field, overwrite the old with the new value if it already exists.\n * Return 0 on insert and 1 on update.\n *\n * By default, the key and value SDS strings are copied if needed, so the\n * caller retains ownership of the strings passed. However this behavior\n * can be effected by passing appropriate flags (possibly bitwise OR-ed):\n *\n * HASH_SET_TAKE_FIELD -- The SDS field ownership passes to the function.\n * HASH_SET_TAKE_VALUE -- The SDS value ownership passes to the function.\n *\n * When the flags are used the caller does not need to release the passed\n * SDS string(s). It's up to the function to use the string to create a new\n * entry or to free the SDS string before returning to the caller.\n *\n * HASH_SET_COPY corresponds to no flags passed, and means the default\n * semantics of copying the values if needed.\n *\n */\n#define HASH_SET_TAKE_FIELD (1<<0)\n#define HASH_SET_TAKE_VALUE (1<<1)\n#define HASH_SET_COPY 0\nint hashTypeSet(robj *o, sds field, sds value, int flags) {\n    int update = 0;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr, *vptr;\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);\n            if (fptr != NULL) {\n                /* Grab pointer to the value (fptr points to the field) */\n                vptr = ziplistNext(zl, fptr);\n                serverAssert(vptr != NULL);\n                update = 1;\n\n                /* Delete value */\n                zl = ziplistDelete(zl, &vptr);\n\n                /* Insert new value */\n                zl = ziplistInsert(zl, vptr, (unsigned char*)value,\n                        sdslen(value));\n            }\n        }\n\n        if (!update) {\n            /* Push new field/value pair onto the tail of the ziplist */\n            zl = ziplistPush(zl, (unsigned char*)field, sdslen(field),\n                    ZIPLIST_TAIL);\n            zl = ziplistPush(zl, (unsigned char*)value, sdslen(value),\n                    ZIPLIST_TAIL);\n        }\n        o->ptr = zl;\n\n        /* Check if the ziplist needs to be converted to a hash table */\n        if (hashTypeLength(o) > server.hash_max_ziplist_entries)\n            hashTypeConvert(o, OBJ_ENCODING_HT);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictFind(o->ptr,field);\n        if (de) {\n            sdsfree(dictGetVal(de));\n            if (flags & HASH_SET_TAKE_VALUE) {\n                dictGetVal(de) = value;\n                value = NULL;\n            } else {\n                dictGetVal(de) = sdsdup(value);\n            }\n            update = 1;\n        } else {\n            sds f,v;\n            if (flags & HASH_SET_TAKE_FIELD) {\n                f = field;\n                field = NULL;\n            } else {\n                f = sdsdup(field);\n            }\n            if (flags & HASH_SET_TAKE_VALUE) {\n                v = value;\n                value = NULL;\n            } else {\n                v = sdsdup(value);\n            }\n            dictAdd(o->ptr,f,v);\n        }\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n\n    /* Free SDS strings we did not referenced elsewhere if the flags\n     * want this function to be responsible. */\n    if (flags & HASH_SET_TAKE_FIELD && field) sdsfree(field);\n    if (flags & HASH_SET_TAKE_VALUE && value) sdsfree(value);\n    return update;\n}\n\n/* Delete an element from a hash.\n * Return 1 on deleted and 0 on not found. */\nint hashTypeDelete(robj *o, sds field) {\n    int deleted = 0;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl, *fptr;\n\n        zl = o->ptr;\n        fptr = ziplistIndex(zl, ZIPLIST_HEAD);\n        if (fptr != NULL) {\n            fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);\n            if (fptr != NULL) {\n                zl = ziplistDelete(zl,&fptr); /* Delete the key. */\n                zl = ziplistDelete(zl,&fptr); /* Delete the value. */\n                o->ptr = zl;\n                deleted = 1;\n            }\n        }\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        if (dictDelete((dict*)o->ptr, field) == C_OK) {\n            deleted = 1;\n\n            /* Always check if the dictionary needs a resize after a delete. */\n            if (htNeedsResize(o->ptr)) dictResize(o->ptr);\n        }\n\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return deleted;\n}\n\n/* Return the number of elements in a hash. */\nunsigned long hashTypeLength(const robj *o) {\n    unsigned long length = ULONG_MAX;\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        length = ziplistLen(o->ptr) / 2;\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        length = dictSize((const dict*)o->ptr);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return length;\n}\n\nhashTypeIterator *hashTypeInitIterator(robj *subject) {\n    hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));\n    hi->subject = subject;\n    hi->encoding = subject->encoding;\n\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        hi->fptr = NULL;\n        hi->vptr = NULL;\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        hi->di = dictGetIterator(subject->ptr);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return hi;\n}\n\nvoid hashTypeReleaseIterator(hashTypeIterator *hi) {\n    if (hi->encoding == OBJ_ENCODING_HT)\n        dictReleaseIterator(hi->di);\n    zfree(hi);\n}\n\n/* Move to the next entry in the hash. Return C_OK when the next entry\n * could be found and C_ERR when the iterator reaches the end. */\nint hashTypeNext(hashTypeIterator *hi) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl;\n        unsigned char *fptr, *vptr;\n\n        zl = hi->subject->ptr;\n        fptr = hi->fptr;\n        vptr = hi->vptr;\n\n        if (fptr == NULL) {\n            /* Initialize cursor */\n            serverAssert(vptr == NULL);\n            fptr = ziplistIndex(zl, 0);\n        } else {\n            /* Advance cursor */\n            serverAssert(vptr != NULL);\n            fptr = ziplistNext(zl, vptr);\n        }\n        if (fptr == NULL) return C_ERR;\n\n        /* Grab pointer to the value (fptr points to the field) */\n        vptr = ziplistNext(zl, fptr);\n        serverAssert(vptr != NULL);\n\n        /* fptr, vptr now point to the first or next pair */\n        hi->fptr = fptr;\n        hi->vptr = vptr;\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        if ((hi->de = dictNext(hi->di)) == NULL) return C_ERR;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n    return C_OK;\n}\n\n/* Get the field or value at iterator cursor, for an iterator on a hash value\n * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */\nvoid hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,\n                                unsigned char **vstr,\n                                unsigned int *vlen,\n                                long long *vll)\n{\n    int ret;\n\n    serverAssert(hi->encoding == OBJ_ENCODING_ZIPLIST);\n\n    if (what & OBJ_HASH_KEY) {\n        ret = ziplistGet(hi->fptr, vstr, vlen, vll);\n        serverAssert(ret);\n    } else {\n        ret = ziplistGet(hi->vptr, vstr, vlen, vll);\n        serverAssert(ret);\n    }\n}\n\n/* Get the field or value at iterator cursor, for an iterator on a hash value\n * encoded as a hash table. Prototype is similar to\n * `hashTypeGetFromHashTable`. */\nsds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what) {\n    serverAssert(hi->encoding == OBJ_ENCODING_HT);\n\n    if (what & OBJ_HASH_KEY) {\n        return dictGetKey(hi->de);\n    } else {\n        return dictGetVal(hi->de);\n    }\n}\n\n/* Higher level function of hashTypeCurrent*() that returns the hash value\n * at current iterator position.\n *\n * The returned element is returned by reference in either *vstr and *vlen if\n * it's returned in string form, or stored in *vll if it's returned as\n * a number.\n *\n * If *vll is populated *vstr is set to NULL, so the caller\n * can always check the function return by checking the return value\n * type checking if vstr == NULL. */\nvoid hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        *vstr = NULL;\n        hashTypeCurrentFromZiplist(hi, what, vstr, vlen, vll);\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        sds ele = hashTypeCurrentFromHashTable(hi, what);\n        *vstr = (unsigned char*) ele;\n        *vlen = sdslen(ele);\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\n/* Return the key or value at the current iterator position as a new\n * SDS string. */\nsds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vll;\n\n    hashTypeCurrentObject(hi,what,&vstr,&vlen,&vll);\n    if (vstr) return sdsnewlen(vstr,vlen);\n    return sdsfromlonglong(vll);\n}\n\nrobj *hashTypeLookupWriteOrCreate(client *c, robj *key) {\n    robj *o = lookupKeyWrite(c->db,key);\n    if (o == NULL) {\n        o = createHashObject();\n        dbAdd(c->db,key,o);\n    } else {\n        if (o->type != OBJ_HASH) {\n            addReply(c,shared.wrongtypeerr);\n            return NULL;\n        }\n    }\n    return o;\n}\n\nvoid hashTypeConvertZiplist(robj *o, int enc) {\n    serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);\n\n    if (enc == OBJ_ENCODING_ZIPLIST) {\n        /* Nothing to do... */\n\n    } else if (enc == OBJ_ENCODING_HT) {\n        hashTypeIterator *hi;\n        dict *dict;\n        int ret;\n\n        hi = hashTypeInitIterator(o);\n        dict = dictCreate(&hashDictType, NULL);\n\n        while (hashTypeNext(hi) != C_ERR) {\n            sds key, value;\n\n            key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);\n            value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);\n            ret = dictAdd(dict, key, value);\n            if (ret != DICT_OK) {\n                serverLogHexDump(LL_WARNING,\"ziplist with dup elements dump\",\n                    o->ptr,ziplistBlobLen(o->ptr));\n                serverPanic(\"Ziplist corruption detected\");\n            }\n        }\n        hashTypeReleaseIterator(hi);\n        zfree(o->ptr);\n        o->encoding = OBJ_ENCODING_HT;\n        o->ptr = dict;\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hashTypeConvert(robj *o, int enc) {\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        hashTypeConvertZiplist(o, enc);\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        serverPanic(\"Not implemented\");\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Hash type commands\n *----------------------------------------------------------------------------*/\n\nvoid hsetnxCommand(client *c) {\n    robj *o;\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    hashTypeTryConversion(o,c->argv,2,3);\n\n    if (hashTypeExists(o, c->argv[2]->ptr)) {\n        addReply(c, shared.czero);\n    } else {\n        hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);\n        addReply(c, shared.cone);\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n        server.dirty++;\n    }\n}\n\nvoid hsetCommand(client *c) {\n    int i, created = 0;\n    robj *o;\n\n    if ((c->argc % 2) == 1) {\n        addReplyError(c,\"wrong number of arguments for HMSET\");\n        return;\n    }\n\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    hashTypeTryConversion(o,c->argv,2,c->argc-1);\n\n    for (i = 2; i < c->argc; i += 2)\n        created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);\n\n    /* HMSET (deprecated) and HSET return value is different. */\n    char *cmdname = c->argv[0]->ptr;\n    if (cmdname[1] == 's' || cmdname[1] == 'S') {\n        /* HSET */\n        addReplyLongLong(c, created);\n    } else {\n        /* HMSET */\n        addReply(c, shared.ok);\n    }\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hset\",c->argv[1],c->db->id);\n    server.dirty++;\n}\n\nvoid hincrbyCommand(client *c) {\n    long long value, incr, oldvalue;\n    robj *o;\n    sds new;\n    unsigned char *vstr;\n    unsigned int vlen;\n\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {\n        if (vstr) {\n            if (string2ll((char*)vstr,vlen,&value) == 0) {\n                addReplyError(c,\"hash value is not an integer\");\n                return;\n            }\n        } /* Else hashTypeGetValue() already stored it into &value */\n    } else {\n        value = 0;\n    }\n\n    oldvalue = value;\n    if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||\n        (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {\n        addReplyError(c,\"increment or decrement would overflow\");\n        return;\n    }\n    value += incr;\n    new = sdsfromlonglong(value);\n    hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);\n    addReplyLongLong(c,value);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hincrby\",c->argv[1],c->db->id);\n    server.dirty++;\n}\n\nvoid hincrbyfloatCommand(client *c) {\n    long double value, incr;\n    long long ll;\n    robj *o;\n    sds new;\n    unsigned char *vstr;\n    unsigned int vlen;\n\n    if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;\n    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {\n        if (vstr) {\n            if (string2ld((char*)vstr,vlen,&value) == 0) {\n                addReplyError(c,\"hash value is not a float\");\n                return;\n            }\n        } else {\n            value = (long double)ll;\n        }\n    } else {\n        value = 0;\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    char buf[MAX_LONG_DOUBLE_CHARS];\n    int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);\n    new = sdsnewlen(buf,len);\n    hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);\n    addReplyBulkCBuffer(c,buf,len);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_HASH,\"hincrbyfloat\",c->argv[1],c->db->id);\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    robj *aux, *newobj;\n    aux = createStringObject(\"HSET\",4);\n    newobj = createRawStringObject(buf,len);\n    rewriteClientCommandArgument(c,0,aux);\n    decrRefCount(aux);\n    rewriteClientCommandArgument(c,3,newobj);\n    decrRefCount(newobj);\n}\n\nstatic void addHashFieldToReply(client *c, robj *o, sds field) {\n    int ret;\n\n    if (o == NULL) {\n        addReplyNull(c);\n        return;\n    }\n\n    if (o->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);\n        if (ret < 0) {\n            addReplyNull(c);\n        } else {\n            if (vstr) {\n                addReplyBulkCBuffer(c, vstr, vlen);\n            } else {\n                addReplyBulkLongLong(c, vll);\n            }\n        }\n\n    } else if (o->encoding == OBJ_ENCODING_HT) {\n        sds value = hashTypeGetFromHashTable(o, field);\n        if (value == NULL)\n            addReplyNull(c);\n        else\n            addReplyBulkCBuffer(c, value, sdslen(value));\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid hgetCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    addHashFieldToReply(c, o, c->argv[2]->ptr);\n}\n\nvoid hmgetCommand(client *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    o = lookupKeyRead(c->db, c->argv[1]);\n    if (o != NULL && o->type != OBJ_HASH) {\n        addReply(c, shared.wrongtypeerr);\n        return;\n    }\n\n    addReplyArrayLen(c, c->argc-2);\n    for (i = 2; i < c->argc; i++) {\n        addHashFieldToReply(c, o, c->argv[i]->ptr);\n    }\n}\n\nvoid hdelCommand(client *c) {\n    robj *o;\n    int j, deleted = 0, keyremoved = 0;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        if (hashTypeDelete(o,c->argv[j]->ptr)) {\n            deleted++;\n            if (hashTypeLength(o) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_HASH,\"hdel\",c->argv[1],c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\nvoid hlenCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    addReplyLongLong(c,hashTypeLength(o));\n}\n\nvoid hstrlenCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n    addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]->ptr));\n}\n\nstatic void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {\n    if (hi->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *vstr = NULL;\n        unsigned int vlen = UINT_MAX;\n        long long vll = LLONG_MAX;\n\n        hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);\n        if (vstr)\n            addReplyBulkCBuffer(c, vstr, vlen);\n        else\n            addReplyBulkLongLong(c, vll);\n    } else if (hi->encoding == OBJ_ENCODING_HT) {\n        sds value = hashTypeCurrentFromHashTable(hi, what);\n        addReplyBulkCBuffer(c, value, sdslen(value));\n    } else {\n        serverPanic(\"Unknown hash encoding\");\n    }\n}\n\nvoid genericHgetallCommand(client *c, int flags) {\n    robj *o;\n    hashTypeIterator *hi;\n    int length, count = 0;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymap[c->resp]))\n        == NULL || checkType(c,o,OBJ_HASH)) return;\n\n    /* We return a map if the user requested keys and values, like in the\n     * HGETALL case. Otherwise to use a flat array makes more sense. */\n    length = hashTypeLength(o);\n    if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {\n        addReplyMapLen(c, length);\n    } else {\n        addReplyArrayLen(c, length);\n    }\n\n    hi = hashTypeInitIterator(o);\n    while (hashTypeNext(hi) != C_ERR) {\n        if (flags & OBJ_HASH_KEY) {\n            addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);\n            count++;\n        }\n        if (flags & OBJ_HASH_VALUE) {\n            addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);\n            count++;\n        }\n    }\n\n    hashTypeReleaseIterator(hi);\n\n    /* Make sure we returned the right number of elements. */\n    if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= 2;\n    serverAssert(count == length);\n}\n\nvoid hkeysCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_KEY);\n}\n\nvoid hvalsCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_VALUE);\n}\n\nvoid hgetallCommand(client *c) {\n    genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);\n}\n\nvoid hexistsCommand(client *c) {\n    robj *o;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n\n    addReply(c, hashTypeExists(o,c->argv[2]->ptr) ? shared.cone : shared.czero);\n}\n\nvoid hscanCommand(client *c) {\n    robj *o;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,o,OBJ_HASH)) return;\n    scanGenericCommand(c,o,cursor);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n\n/*-----------------------------------------------------------------------------\n * List API\n *----------------------------------------------------------------------------*/\n\n/* The function pushes an element to the specified list object 'subject',\n * at head or tail position as specified by 'where'.\n *\n * There is no need for the caller to increment the refcount of 'value' as\n * the function takes care of it if needed. */\nvoid listTypePush(robj *subject, robj *value, int where) {\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        int pos = (where == LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;\n        value = getDecodedObject(value);\n        size_t len = sdslen(value->ptr);\n        quicklistPush(subject->ptr, value->ptr, len, pos);\n        decrRefCount(value);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid *listPopSaver(unsigned char *data, unsigned int sz) {\n    return createStringObject((char*)data,sz);\n}\n\nrobj *listTypePop(robj *subject, int where) {\n    long long vlong;\n    robj *value = NULL;\n\n    int ql_where = where == LIST_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL;\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value,\n                               NULL, &vlong, listPopSaver)) {\n            if (!value)\n                value = createStringObjectFromLongLong(vlong);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return value;\n}\n\nunsigned long listTypeLength(const robj *subject) {\n    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {\n        return quicklistCount(subject->ptr);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Initialize an iterator at the specified index. */\nlistTypeIterator *listTypeInitIterator(robj *subject, long index,\n                                       unsigned char direction) {\n    listTypeIterator *li = zmalloc(sizeof(listTypeIterator));\n    li->subject = subject;\n    li->encoding = subject->encoding;\n    li->direction = direction;\n    li->iter = NULL;\n    /* LIST_HEAD means start at TAIL and move *towards* head.\n     * LIST_TAIL means start at HEAD and move *towards tail. */\n    int iter_direction =\n        direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD;\n    if (li->encoding == OBJ_ENCODING_QUICKLIST) {\n        li->iter = quicklistGetIteratorAtIdx(li->subject->ptr,\n                                             iter_direction, index);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return li;\n}\n\n/* Clean up the iterator. */\nvoid listTypeReleaseIterator(listTypeIterator *li) {\n    zfree(li->iter);\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. */\nint listTypeNext(listTypeIterator *li, listTypeEntry *entry) {\n    /* Protect from converting when iterating */\n    serverAssert(li->subject->encoding == li->encoding);\n\n    entry->li = li;\n    if (li->encoding == OBJ_ENCODING_QUICKLIST) {\n        return quicklistNext(li->iter, &entry->entry);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return 0;\n}\n\n/* Return entry or NULL at the current position of the iterator. */\nrobj *listTypeGet(listTypeEntry *entry) {\n    robj *value = NULL;\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        if (entry->entry.value) {\n            value = createStringObject((char *)entry->entry.value,\n                                       entry->entry.sz);\n        } else {\n            value = createStringObjectFromLongLong(entry->entry.longval);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n    return value;\n}\n\nvoid listTypeInsert(listTypeEntry *entry, robj *value, int where) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        value = getDecodedObject(value);\n        sds str = value->ptr;\n        size_t len = sdslen(str);\n        if (where == LIST_TAIL) {\n            quicklistInsertAfter((quicklist *)entry->entry.quicklist,\n                                 &entry->entry, str, len);\n        } else if (where == LIST_HEAD) {\n            quicklistInsertBefore((quicklist *)entry->entry.quicklist,\n                                  &entry->entry, str, len);\n        }\n        decrRefCount(value);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Compare the given object with the entry at the current position. */\nint listTypeEqual(listTypeEntry *entry, robj *o) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        serverAssertWithInfo(NULL,o,sdsEncodedObject(o));\n        return quicklistCompare(entry->entry.zi,o->ptr,sdslen(o->ptr));\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Delete the element pointed to. */\nvoid listTypeDelete(listTypeIterator *iter, listTypeEntry *entry) {\n    if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistDelEntry(iter->iter, &entry->entry);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\n/* Create a quicklist from a single ziplist */\nvoid listTypeConvert(robj *subject, int enc) {\n    serverAssertWithInfo(NULL,subject,subject->type==OBJ_LIST);\n    serverAssertWithInfo(NULL,subject,subject->encoding==OBJ_ENCODING_ZIPLIST);\n\n    if (enc == OBJ_ENCODING_QUICKLIST) {\n        size_t zlen = server.list_max_ziplist_size;\n        int depth = server.list_compress_depth;\n        subject->ptr = quicklistCreateFromZiplist(zlen, depth, subject->ptr);\n        subject->encoding = OBJ_ENCODING_QUICKLIST;\n    } else {\n        serverPanic(\"Unsupported list conversion\");\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * List Commands\n *----------------------------------------------------------------------------*/\n\nvoid pushGenericCommand(client *c, int where) {\n    int j, pushed = 0;\n    robj *lobj = lookupKeyWrite(c->db,c->argv[1]);\n\n    if (lobj && lobj->type != OBJ_LIST) {\n        addReply(c,shared.wrongtypeerr);\n        return;\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        if (!lobj) {\n            lobj = createQuicklistObject();\n            quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,\n                                server.list_compress_depth);\n            dbAdd(c->db,c->argv[1],lobj);\n        }\n        listTypePush(lobj,c->argv[j],where);\n        pushed++;\n    }\n    addReplyLongLong(c, (lobj ? listTypeLength(lobj) : 0));\n    if (pushed) {\n        char *event = (where == LIST_HEAD) ? \"lpush\" : \"rpush\";\n\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n    }\n    server.dirty += pushed;\n}\n\nvoid lpushCommand(client *c) {\n    pushGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpushCommand(client *c) {\n    pushGenericCommand(c,LIST_TAIL);\n}\n\nvoid pushxGenericCommand(client *c, int where) {\n    int j, pushed = 0;\n    robj *subject;\n\n    if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,subject,OBJ_LIST)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        listTypePush(subject,c->argv[j],where);\n        pushed++;\n    }\n\n    addReplyLongLong(c,listTypeLength(subject));\n\n    if (pushed) {\n        char *event = (where == LIST_HEAD) ? \"lpush\" : \"rpush\";\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n    }\n    server.dirty += pushed;\n}\n\nvoid lpushxCommand(client *c) {\n    pushxGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpushxCommand(client *c) {\n    pushxGenericCommand(c,LIST_TAIL);\n}\n\nvoid linsertCommand(client *c) {\n    int where;\n    robj *subject;\n    listTypeIterator *iter;\n    listTypeEntry entry;\n    int inserted = 0;\n\n    if (strcasecmp(c->argv[2]->ptr,\"after\") == 0) {\n        where = LIST_TAIL;\n    } else if (strcasecmp(c->argv[2]->ptr,\"before\") == 0) {\n        where = LIST_HEAD;\n    } else {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,subject,OBJ_LIST)) return;\n\n    /* Seek pivot from head to tail */\n    iter = listTypeInitIterator(subject,0,LIST_TAIL);\n    while (listTypeNext(iter,&entry)) {\n        if (listTypeEqual(&entry,c->argv[3])) {\n            listTypeInsert(&entry,c->argv[4],where);\n            inserted = 1;\n            break;\n        }\n    }\n    listTypeReleaseIterator(iter);\n\n    if (inserted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,\"linsert\",\n                            c->argv[1],c->db->id);\n        server.dirty++;\n    } else {\n        /* Notify client of a failed insert */\n        addReplyLongLong(c,-1);\n        return;\n    }\n\n    addReplyLongLong(c,listTypeLength(subject));\n}\n\nvoid llenCommand(client *c) {\n    robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n    addReplyLongLong(c,listTypeLength(o));\n}\n\nvoid lindexCommand(client *c) {\n    robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n    long index;\n    robj *value = NULL;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))\n        return;\n\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistEntry entry;\n        if (quicklistIndex(o->ptr, index, &entry)) {\n            if (entry.value) {\n                value = createStringObject((char*)entry.value,entry.sz);\n            } else {\n                value = createStringObjectFromLongLong(entry.longval);\n            }\n            addReplyBulk(c,value);\n            decrRefCount(value);\n        } else {\n            addReplyNull(c);\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid lsetCommand(client *c) {\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n    long index;\n    robj *value = c->argv[3];\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))\n        return;\n\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklist *ql = o->ptr;\n        int replaced = quicklistReplaceAtIndex(ql, index,\n                                               value->ptr, sdslen(value->ptr));\n        if (!replaced) {\n            addReply(c,shared.outofrangeerr);\n        } else {\n            addReply(c,shared.ok);\n            signalModifiedKey(c,c->db,c->argv[1]);\n            notifyKeyspaceEvent(NOTIFY_LIST,\"lset\",c->argv[1],c->db->id);\n            server.dirty++;\n        }\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n}\n\nvoid popGenericCommand(client *c, int where) {\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp]);\n    if (o == NULL || checkType(c,o,OBJ_LIST)) return;\n\n    robj *value = listTypePop(o,where);\n    if (value == NULL) {\n        addReplyNull(c);\n    } else {\n        char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n\n        addReplyBulk(c,value);\n        decrRefCount(value);\n        notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);\n        if (listTypeLength(o) == 0) {\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                c->argv[1],c->db->id);\n            dbDelete(c->db,c->argv[1]);\n        }\n        signalModifiedKey(c,c->db,c->argv[1]);\n        server.dirty++;\n    }\n}\n\nvoid lpopCommand(client *c) {\n    popGenericCommand(c,LIST_HEAD);\n}\n\nvoid rpopCommand(client *c) {\n    popGenericCommand(c,LIST_TAIL);\n}\n\nvoid lrangeCommand(client *c) {\n    robj *o;\n    long start, end, llen, rangelen;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == NULL\n         || checkType(c,o,OBJ_LIST)) return;\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        addReply(c,shared.emptyarray);\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    addReplyArrayLen(c,rangelen);\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        listTypeIterator *iter = listTypeInitIterator(o, start, LIST_TAIL);\n\n        while(rangelen--) {\n            listTypeEntry entry;\n            listTypeNext(iter, &entry);\n            quicklistEntry *qe = &entry.entry;\n            if (qe->value) {\n                addReplyBulkCBuffer(c,qe->value,qe->sz);\n            } else {\n                addReplyBulkLongLong(c,qe->longval);\n            }\n        }\n        listTypeReleaseIterator(iter);\n    } else {\n        serverPanic(\"List encoding is not QUICKLIST!\");\n    }\n}\n\nvoid ltrimCommand(client *c) {\n    robj *o;\n    long start, end, llen, ltrim, rtrim;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.ok)) == NULL ||\n        checkType(c,o,OBJ_LIST)) return;\n    llen = listTypeLength(o);\n\n    /* convert negative indexes */\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        /* Out of range start or start > end result in empty list */\n        ltrim = llen;\n        rtrim = 0;\n    } else {\n        if (end >= llen) end = llen-1;\n        ltrim = start;\n        rtrim = llen-end-1;\n    }\n\n    /* Remove list elements to perform the trim */\n    if (o->encoding == OBJ_ENCODING_QUICKLIST) {\n        quicklistDelRange(o->ptr,0,ltrim);\n        quicklistDelRange(o->ptr,-rtrim,rtrim);\n    } else {\n        serverPanic(\"Unknown list encoding\");\n    }\n\n    notifyKeyspaceEvent(NOTIFY_LIST,\"ltrim\",c->argv[1],c->db->id);\n    if (listTypeLength(o) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n    signalModifiedKey(c,c->db,c->argv[1]);\n    server.dirty++;\n    addReply(c,shared.ok);\n}\n\nvoid lremCommand(client *c) {\n    robj *subject, *obj;\n    obj = c->argv[3];\n    long toremove;\n    long removed = 0;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != C_OK))\n        return;\n\n    subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero);\n    if (subject == NULL || checkType(c,subject,OBJ_LIST)) return;\n\n    listTypeIterator *li;\n    if (toremove < 0) {\n        toremove = -toremove;\n        li = listTypeInitIterator(subject,-1,LIST_HEAD);\n    } else {\n        li = listTypeInitIterator(subject,0,LIST_TAIL);\n    }\n\n    listTypeEntry entry;\n    while (listTypeNext(li,&entry)) {\n        if (listTypeEqual(&entry,obj)) {\n            listTypeDelete(li, &entry);\n            server.dirty++;\n            removed++;\n            if (toremove && removed == toremove) break;\n        }\n    }\n    listTypeReleaseIterator(li);\n\n    if (removed) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_LIST,\"lrem\",c->argv[1],c->db->id);\n    }\n\n    if (listTypeLength(subject) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    addReplyLongLong(c,removed);\n}\n\n/* This is the semantic of this command:\n *  RPOPLPUSH srclist dstlist:\n *    IF LLEN(srclist) > 0\n *      element = RPOP srclist\n *      LPUSH dstlist element\n *      RETURN element\n *    ELSE\n *      RETURN nil\n *    END\n *  END\n *\n * The idea is to be able to get an element from a list in a reliable way\n * since the element is not just returned but pushed against another list\n * as well. This command was originally proposed by Ezra Zygmuntowicz.\n */\n\nvoid rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value) {\n    /* Create the list if the key does not exist */\n    if (!dstobj) {\n        dstobj = createQuicklistObject();\n        quicklistSetOptions(dstobj->ptr, server.list_max_ziplist_size,\n                            server.list_compress_depth);\n        dbAdd(c->db,dstkey,dstobj);\n    }\n    signalModifiedKey(c,c->db,dstkey);\n    listTypePush(dstobj,value,LIST_HEAD);\n    notifyKeyspaceEvent(NOTIFY_LIST,\"lpush\",dstkey,c->db->id);\n    /* Always send the pushed value to the client. */\n    addReplyBulk(c,value);\n}\n\nvoid rpoplpushCommand(client *c) {\n    robj *sobj, *value;\n    if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp]))\n        == NULL || checkType(c,sobj,OBJ_LIST)) return;\n\n    if (listTypeLength(sobj) == 0) {\n        /* This may only happen after loading very old RDB files. Recent\n         * versions of Redis delete keys of empty lists. */\n        addReplyNull(c);\n    } else {\n        robj *dobj = lookupKeyWrite(c->db,c->argv[2]);\n        robj *touchedkey = c->argv[1];\n\n        if (dobj && checkType(c,dobj,OBJ_LIST)) return;\n        value = listTypePop(sobj,LIST_TAIL);\n        /* We saved touched key, and protect it, since rpoplpushHandlePush\n         * may change the client command argument vector (it does not\n         * currently). */\n        incrRefCount(touchedkey);\n        rpoplpushHandlePush(c,c->argv[2],dobj,value);\n\n        /* listTypePop returns an object with its refcount incremented */\n        decrRefCount(value);\n\n        /* Delete the source list when it is empty */\n        notifyKeyspaceEvent(NOTIFY_LIST,\"rpop\",touchedkey,c->db->id);\n        if (listTypeLength(sobj) == 0) {\n            dbDelete(c->db,touchedkey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                touchedkey,c->db->id);\n        }\n        signalModifiedKey(c,c->db,touchedkey);\n        decrRefCount(touchedkey);\n        server.dirty++;\n        if (c->cmd->proc == brpoplpushCommand) {\n            rewriteClientCommandVector(c,3,shared.rpoplpush,c->argv[1],c->argv[2]);\n        }\n    }\n}\n\n/*-----------------------------------------------------------------------------\n * Blocking POP operations\n *----------------------------------------------------------------------------*/\n\n/* This is a helper function for handleClientsBlockedOnKeys(). It's work\n * is to serve a specific client (receiver) that is blocked on 'key'\n * in the context of the specified 'db', doing the following:\n *\n * 1) Provide the client with the 'value' element.\n * 2) If the dstkey is not NULL (we are serving a BRPOPLPUSH) also push the\n *    'value' element on the destination list (the LPUSH side of the command).\n * 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into\n *    the AOF and replication channel.\n *\n * The argument 'where' is LIST_TAIL or LIST_HEAD, and indicates if the\n * 'value' element was popped from the head (BLPOP) or tail (BRPOP) so that\n * we can propagate the command properly.\n *\n * The function returns C_OK if we are able to serve the client, otherwise\n * C_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. */\nint serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where)\n{\n    robj *argv[3];\n\n    if (dstkey == NULL) {\n        /* Propagate the [LR]POP operation. */\n        argv[0] = (where == LIST_HEAD) ? shared.lpop :\n                                          shared.rpop;\n        argv[1] = key;\n        propagate((where == LIST_HEAD) ?\n            server.lpopCommand : server.rpopCommand,\n            db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL);\n\n        /* BRPOP/BLPOP */\n        addReplyArrayLen(receiver,2);\n        addReplyBulk(receiver,key);\n        addReplyBulk(receiver,value);\n\n        /* Notify event. */\n        char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n        notifyKeyspaceEvent(NOTIFY_LIST,event,key,receiver->db->id);\n    } else {\n        /* BRPOPLPUSH */\n        robj *dstobj =\n            lookupKeyWrite(receiver->db,dstkey);\n        if (!(dstobj &&\n             checkType(receiver,dstobj,OBJ_LIST)))\n        {\n            rpoplpushHandlePush(receiver,dstkey,dstobj,\n                value);\n            /* Propagate the RPOPLPUSH operation. */\n            argv[0] = shared.rpoplpush;\n            argv[1] = key;\n            argv[2] = dstkey;\n            propagate(server.rpoplpushCommand,\n                db->id,argv,3,\n                PROPAGATE_AOF|\n                PROPAGATE_REPL);\n\n            /* Notify event (\"lpush\" was notified by rpoplpushHandlePush). */\n            notifyKeyspaceEvent(NOTIFY_LIST,\"rpop\",key,receiver->db->id);\n        } else {\n            /* BRPOPLPUSH failed because of wrong\n             * destination type. */\n            return C_ERR;\n        }\n    }\n    return C_OK;\n}\n\n/* Blocking RPOP/LPOP */\nvoid blockingPopGenericCommand(client *c, int where) {\n    robj *o;\n    mstime_t timeout;\n    int j;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)\n        != C_OK) return;\n\n    for (j = 1; j < c->argc-1; j++) {\n        o = lookupKeyWrite(c->db,c->argv[j]);\n        if (o != NULL) {\n            if (o->type != OBJ_LIST) {\n                addReply(c,shared.wrongtypeerr);\n                return;\n            } else {\n                if (listTypeLength(o) != 0) {\n                    /* Non empty list, this is like a non normal [LR]POP. */\n                    char *event = (where == LIST_HEAD) ? \"lpop\" : \"rpop\";\n                    robj *value = listTypePop(o,where);\n                    serverAssert(value != NULL);\n\n                    addReplyArrayLen(c,2);\n                    addReplyBulk(c,c->argv[j]);\n                    addReplyBulk(c,value);\n                    decrRefCount(value);\n                    notifyKeyspaceEvent(NOTIFY_LIST,event,\n                                        c->argv[j],c->db->id);\n                    if (listTypeLength(o) == 0) {\n                        dbDelete(c->db,c->argv[j]);\n                        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                                            c->argv[j],c->db->id);\n                    }\n                    signalModifiedKey(c,c->db,c->argv[j]);\n                    server.dirty++;\n\n                    /* Replicate it as an [LR]POP instead of B[LR]POP. */\n                    rewriteClientCommandVector(c,2,\n                        (where == LIST_HEAD) ? shared.lpop : shared.rpop,\n                        c->argv[j]);\n                    return;\n                }\n            }\n        }\n    }\n\n    /* If we are inside a MULTI/EXEC and the list is empty the only thing\n     * we can do is treating it as a timeout (even with timeout 0). */\n    if (c->flags & CLIENT_MULTI) {\n        addReplyNullArray(c);\n        return;\n    }\n\n    /* If the list is empty or the key does not exists we must block */\n    blockForKeys(c,BLOCKED_LIST,c->argv + 1,c->argc - 2,timeout,NULL,NULL);\n}\n\nvoid blpopCommand(client *c) {\n    blockingPopGenericCommand(c,LIST_HEAD);\n}\n\nvoid brpopCommand(client *c) {\n    blockingPopGenericCommand(c,LIST_TAIL);\n}\n\nvoid brpoplpushCommand(client *c) {\n    mstime_t timeout;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout,UNIT_SECONDS)\n        != C_OK) return;\n\n    robj *key = lookupKeyWrite(c->db, c->argv[1]);\n\n    if (key == NULL) {\n        if (c->flags & CLIENT_MULTI) {\n            /* Blocking against an empty list in a multi state\n             * returns immediately. */\n            addReplyNull(c);\n        } else {\n            /* The list is empty and the client blocks. */\n            blockForKeys(c,BLOCKED_LIST,c->argv + 1,1,timeout,c->argv[2],NULL);\n        }\n    } else {\n        if (key->type != OBJ_LIST) {\n            addReply(c, shared.wrongtypeerr);\n        } else {\n            /* The list exists and has elements, so\n             * the regular rpoplpushCommand is executed. */\n            serverAssertWithInfo(c,key,listTypeLength(key) > 0);\n            rpoplpushCommand(c);\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n\n/*-----------------------------------------------------------------------------\n * Set Commands\n *----------------------------------------------------------------------------*/\n\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,\n                              robj *dstkey, int op);\n\n/* Factory method to return a set that *can* hold \"value\". When the object has\n * an integer-encodable value, an intset will be returned. Otherwise a regular\n * hash table. */\nrobj *setTypeCreate(sds value) {\n    if (isSdsRepresentableAsLongLong(value,NULL) == C_OK)\n        return createIntsetObject();\n    return createSetObject();\n}\n\n/* Add the specified value into a set.\n *\n * If the value was already member of the set, nothing is done and 0 is\n * returned, otherwise the new element is added and 1 is returned. */\nint setTypeAdd(robj *subject, sds value) {\n    long long llval;\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        dict *ht = subject->ptr;\n        dictEntry *de = dictAddRaw(ht,value,NULL);\n        if (de) {\n            dictSetKey(ht,de,sdsdup(value));\n            dictSetVal(ht,de,NULL);\n            return 1;\n        }\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {\n            uint8_t success = 0;\n            subject->ptr = intsetAdd(subject->ptr,llval,&success);\n            if (success) {\n                /* Convert to regular set when the intset contains\n                 * too many entries. */\n                if (intsetLen(subject->ptr) > server.set_max_intset_entries)\n                    setTypeConvert(subject,OBJ_ENCODING_HT);\n                return 1;\n            }\n        } else {\n            /* Failed to get integer from object, convert to regular set. */\n            setTypeConvert(subject,OBJ_ENCODING_HT);\n\n            /* The set *was* an intset and this value is not integer\n             * encodable, so dictAdd should always work. */\n            serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nint setTypeRemove(robj *setobj, sds value) {\n    long long llval;\n    if (setobj->encoding == OBJ_ENCODING_HT) {\n        if (dictDelete(setobj->ptr,value) == DICT_OK) {\n            if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);\n            return 1;\n        }\n    } else if (setobj->encoding == OBJ_ENCODING_INTSET) {\n        if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {\n            int success;\n            setobj->ptr = intsetRemove(setobj->ptr,llval,&success);\n            if (success) return 1;\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nint setTypeIsMember(robj *subject, sds value) {\n    long long llval;\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        return dictFind((dict*)subject->ptr,value) != NULL;\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {\n            return intsetFind((intset*)subject->ptr,llval);\n        }\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return 0;\n}\n\nsetTypeIterator *setTypeInitIterator(robj *subject) {\n    setTypeIterator *si = zmalloc(sizeof(setTypeIterator));\n    si->subject = subject;\n    si->encoding = subject->encoding;\n    if (si->encoding == OBJ_ENCODING_HT) {\n        si->di = dictGetIterator(subject->ptr);\n    } else if (si->encoding == OBJ_ENCODING_INTSET) {\n        si->ii = 0;\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return si;\n}\n\nvoid setTypeReleaseIterator(setTypeIterator *si) {\n    if (si->encoding == OBJ_ENCODING_HT)\n        dictReleaseIterator(si->di);\n    zfree(si);\n}\n\n/* Move to the next entry in the set. Returns the object at the current\n * position.\n *\n * Since set elements can be internally be stored as SDS strings 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 * (sdsele) or (llele) accordingly.\n *\n * Note that both the sdsele and llele pointers should be passed and cannot\n * be NULL since the function will try to defensively populate the non\n * used field with values which are easy to trap if misused.\n *\n * When there are no longer elements -1 is returned. */\nint setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele) {\n    if (si->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictNext(si->di);\n        if (de == NULL) return -1;\n        *sdsele = dictGetKey(de);\n        *llele = -123456789; /* Not needed. Defensive. */\n    } else if (si->encoding == OBJ_ENCODING_INTSET) {\n        if (!intsetGet(si->subject->ptr,si->ii++,llele))\n            return -1;\n        *sdsele = NULL; /* Not needed. Defensive. */\n    } else {\n        serverPanic(\"Wrong set encoding in setTypeNext\");\n    }\n    return si->encoding;\n}\n\n/* The not copy on write friendly version but easy to use version\n * of setTypeNext() is setTypeNextObject(), returning new SDS\n * strings. So if you don't retain a pointer to this object you should call\n * sdsfree() against it.\n *\n * This function is the way to go for write operations where COW is not\n * an issue. */\nsds setTypeNextObject(setTypeIterator *si) {\n    int64_t intele;\n    sds sdsele;\n    int encoding;\n\n    encoding = setTypeNext(si,&sdsele,&intele);\n    switch(encoding) {\n        case -1:    return NULL;\n        case OBJ_ENCODING_INTSET:\n            return sdsfromlonglong(intele);\n        case OBJ_ENCODING_HT:\n            return sdsdup(sdsele);\n        default:\n            serverPanic(\"Unsupported encoding\");\n    }\n    return NULL; /* just to suppress warnings */\n}\n\n/* Return random element from a non empty set.\n * The returned element can be a int64_t value if the set is encoded\n * as an \"intset\" blob of integers, or an SDS string if the set\n * is a regular set.\n *\n * The caller provides both pointers to be populated with the right\n * object. The return value of the function is the object->encoding\n * field of the object and is used by the caller to check if the\n * int64_t pointer or the redis object pointer was populated.\n *\n * Note that both the sdsele and llele pointers should be passed and cannot\n * be NULL since the function will try to defensively populate the non\n * used field with values which are easy to trap if misused. */\nint setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele) {\n    if (setobj->encoding == OBJ_ENCODING_HT) {\n        dictEntry *de = dictGetFairRandomKey(setobj->ptr);\n        *sdsele = dictGetKey(de);\n        *llele = -123456789; /* Not needed. Defensive. */\n    } else if (setobj->encoding == OBJ_ENCODING_INTSET) {\n        *llele = intsetRandom(setobj->ptr);\n        *sdsele = NULL; /* Not needed. Defensive. */\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n    return setobj->encoding;\n}\n\nunsigned long setTypeSize(const robj *subject) {\n    if (subject->encoding == OBJ_ENCODING_HT) {\n        return dictSize((const dict*)subject->ptr);\n    } else if (subject->encoding == OBJ_ENCODING_INTSET) {\n        return intsetLen((const intset*)subject->ptr);\n    } else {\n        serverPanic(\"Unknown set encoding\");\n    }\n}\n\n/* Convert the set to specified encoding. The resulting dict (when converting\n * to a hash table) is presized to hold the number of elements in the original\n * set. */\nvoid setTypeConvert(robj *setobj, int enc) {\n    setTypeIterator *si;\n    serverAssertWithInfo(NULL,setobj,setobj->type == OBJ_SET &&\n                             setobj->encoding == OBJ_ENCODING_INTSET);\n\n    if (enc == OBJ_ENCODING_HT) {\n        int64_t intele;\n        dict *d = dictCreate(&setDictType,NULL);\n        sds element;\n\n        /* Presize the dict to avoid rehashing */\n        dictExpand(d,intsetLen(setobj->ptr));\n\n        /* To add the elements we extract integers and create redis objects */\n        si = setTypeInitIterator(setobj);\n        while (setTypeNext(si,&element,&intele) != -1) {\n            element = sdsfromlonglong(intele);\n            serverAssert(dictAdd(d,element,NULL) == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n\n        setobj->encoding = OBJ_ENCODING_HT;\n        zfree(setobj->ptr);\n        setobj->ptr = d;\n    } else {\n        serverPanic(\"Unsupported set conversion\");\n    }\n}\n\nvoid saddCommand(client *c) {\n    robj *set;\n    int j, added = 0;\n\n    set = lookupKeyWrite(c->db,c->argv[1]);\n    if (set == NULL) {\n        set = setTypeCreate(c->argv[2]->ptr);\n        dbAdd(c->db,c->argv[1],set);\n    } else {\n        if (set->type != OBJ_SET) {\n            addReply(c,shared.wrongtypeerr);\n            return;\n        }\n    }\n\n    for (j = 2; j < c->argc; j++) {\n        if (setTypeAdd(set,c->argv[j]->ptr)) added++;\n    }\n    if (added) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_SET,\"sadd\",c->argv[1],c->db->id);\n    }\n    server.dirty += added;\n    addReplyLongLong(c,added);\n}\n\nvoid sremCommand(client *c) {\n    robj *set;\n    int j, deleted = 0, keyremoved = 0;\n\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        if (setTypeRemove(set,c->argv[j]->ptr)) {\n            deleted++;\n            if (setTypeSize(set) == 0) {\n                dbDelete(c->db,c->argv[1]);\n                keyremoved = 1;\n                break;\n            }\n        }\n    }\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],\n                                c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\nvoid smoveCommand(client *c) {\n    robj *srcset, *dstset, *ele;\n    srcset = lookupKeyWrite(c->db,c->argv[1]);\n    dstset = lookupKeyWrite(c->db,c->argv[2]);\n    ele = c->argv[3];\n\n    /* If the source key does not exist return 0 */\n    if (srcset == NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* If the source key has the wrong type, or the destination key\n     * is set and has the wrong type, return with an error. */\n    if (checkType(c,srcset,OBJ_SET) ||\n        (dstset && checkType(c,dstset,OBJ_SET))) return;\n\n    /* If srcset and dstset are equal, SMOVE is a no-op */\n    if (srcset == dstset) {\n        addReply(c,setTypeIsMember(srcset,ele->ptr) ?\n            shared.cone : shared.czero);\n        return;\n    }\n\n    /* If the element cannot be removed from the src set, return 0. */\n    if (!setTypeRemove(srcset,ele->ptr)) {\n        addReply(c,shared.czero);\n        return;\n    }\n    notifyKeyspaceEvent(NOTIFY_SET,\"srem\",c->argv[1],c->db->id);\n\n    /* Remove the src set from the database when empty */\n    if (setTypeSize(srcset) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    /* Create the destination set when it doesn't exist */\n    if (!dstset) {\n        dstset = setTypeCreate(ele->ptr);\n        dbAdd(c->db,c->argv[2],dstset);\n    }\n\n    signalModifiedKey(c,c->db,c->argv[1]);\n    signalModifiedKey(c,c->db,c->argv[2]);\n    server.dirty++;\n\n    /* An extra key has changed when ele was successfully added to dstset */\n    if (setTypeAdd(dstset,ele->ptr)) {\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_SET,\"sadd\",c->argv[2],c->db->id);\n    }\n    addReply(c,shared.cone);\n}\n\nvoid sismemberCommand(client *c) {\n    robj *set;\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n\n    if (setTypeIsMember(set,c->argv[2]->ptr))\n        addReply(c,shared.cone);\n    else\n        addReply(c,shared.czero);\n}\n\nvoid scardCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_SET)) return;\n\n    addReplyLongLong(c,setTypeSize(o));\n}\n\n/* Handle the \"SPOP key <count>\" variant. The normal version of the\n * command is handled by the spopCommand() function itself. */\n\n/* How many times bigger should be the set compared to the remaining size\n * for us to use the \"create new set\" strategy? Read later in the\n * implementation for more info. */\n#define SPOP_MOVE_STRATEGY_MUL 5\n\nvoid spopWithCountCommand(client *c) {\n    long l;\n    unsigned long count, size;\n    robj *set;\n\n    /* Get the count argument */\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;\n    if (l >= 0) {\n        count = (unsigned long) l;\n    } else {\n        addReply(c,shared.outofrangeerr);\n        return;\n    }\n\n    /* Make sure a key with the name inputted exists, and that it's type is\n     * indeed a set. Otherwise, return nil */\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.emptyset[c->resp]))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n\n    /* If count is zero, serve an empty set ASAP to avoid special\n     * cases later. */\n    if (count == 0) {\n        addReply(c,shared.emptyset[c->resp]);\n        return;\n    }\n\n    size = setTypeSize(set);\n\n    /* Generate an SPOP keyspace notification */\n    notifyKeyspaceEvent(NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n    server.dirty += count;\n\n    /* CASE 1:\n     * The number of requested elements is greater than or equal to\n     * the number of elements inside the set: simply return the whole set. */\n    if (count >= size) {\n        /* We just return the entire set */\n        sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION);\n\n        /* Delete the set as it is now empty */\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n\n        /* Propagate this command as an DEL operation */\n        rewriteClientCommandVector(c,2,shared.del,c->argv[1]);\n        signalModifiedKey(c,c->db,c->argv[1]);\n        server.dirty++;\n        return;\n    }\n\n    /* Case 2 and 3 require to replicate SPOP as a set of SREM commands.\n     * Prepare our replication argument vector. Also send the array length\n     * which is common to both the code paths. */\n    robj *propargv[3];\n    propargv[0] = createStringObject(\"SREM\",4);\n    propargv[1] = c->argv[1];\n    addReplySetLen(c,count);\n\n    /* Common iteration vars. */\n    sds sdsele;\n    robj *objele;\n    int encoding;\n    int64_t llele;\n    unsigned long remaining = size-count; /* Elements left after SPOP. */\n\n    /* If we are here, the number of requested elements is less than the\n     * number of elements inside the set. Also we are sure that count < size.\n     * Use two different strategies.\n     *\n     * CASE 2: The number of elements to return is small compared to the\n     * set size. We can just extract random elements and return them to\n     * the set. */\n    if (remaining*SPOP_MOVE_STRATEGY_MUL > count) {\n        while(count--) {\n            /* Emit and remove. */\n            encoding = setTypeRandomElement(set,&sdsele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n                objele = createStringObjectFromLongLong(llele);\n                set->ptr = intsetRemove(set->ptr,llele,NULL);\n            } else {\n                addReplyBulkCBuffer(c,sdsele,sdslen(sdsele));\n                objele = createStringObject(sdsele,sdslen(sdsele));\n                setTypeRemove(set,sdsele);\n            }\n\n            /* Replicate/AOF this command as an SREM operation */\n            propargv[2] = objele;\n            alsoPropagate(server.sremCommand,c->db->id,propargv,3,\n                PROPAGATE_AOF|PROPAGATE_REPL);\n            decrRefCount(objele);\n        }\n    } else {\n    /* CASE 3: The number of elements to return is very big, approaching\n     * the size of the set itself. After some time extracting random elements\n     * from such a set becomes computationally expensive, so we use\n     * a different strategy, we extract random elements that we don't\n     * want to return (the elements that will remain part of the set),\n     * creating a new set as we do this (that will be stored as the original\n     * set). Then we return the elements left in the original set and\n     * release it. */\n        robj *newset = NULL;\n\n        /* Create a new set with just the remaining elements. */\n        while(remaining--) {\n            encoding = setTypeRandomElement(set,&sdsele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                sdsele = sdsfromlonglong(llele);\n            } else {\n                sdsele = sdsdup(sdsele);\n            }\n            if (!newset) newset = setTypeCreate(sdsele);\n            setTypeAdd(newset,sdsele);\n            setTypeRemove(set,sdsele);\n            sdsfree(sdsele);\n        }\n\n        /* Transfer the old set to the client. */\n        setTypeIterator *si;\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&sdsele,&llele)) != -1) {\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n                objele = createStringObjectFromLongLong(llele);\n            } else {\n                addReplyBulkCBuffer(c,sdsele,sdslen(sdsele));\n                objele = createStringObject(sdsele,sdslen(sdsele));\n            }\n\n            /* Replicate/AOF this command as an SREM operation */\n            propargv[2] = objele;\n            alsoPropagate(server.sremCommand,c->db->id,propargv,3,\n                PROPAGATE_AOF|PROPAGATE_REPL);\n            decrRefCount(objele);\n        }\n        setTypeReleaseIterator(si);\n\n        /* Assign the new set as the key value. */\n        dbOverwrite(c->db,c->argv[1],newset);\n    }\n\n    /* Don't propagate the command itself even if we incremented the\n     * dirty counter. We don't want to propagate an SPOP command since\n     * we propagated the command as a set of SREMs operations using\n     * the alsoPropagate() API. */\n    decrRefCount(propargv[0]);\n    preventCommandPropagation(c);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    server.dirty++;\n}\n\nvoid spopCommand(client *c) {\n    robj *set, *ele, *aux;\n    sds sdsele;\n    int64_t llele;\n    int encoding;\n\n    if (c->argc == 3) {\n        spopWithCountCommand(c);\n        return;\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Make sure a key with the name inputted exists, and that it's type is\n     * indeed a set */\n    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp]))\n         == NULL || checkType(c,set,OBJ_SET)) return;\n\n    /* Get a random element from the set */\n    encoding = setTypeRandomElement(set,&sdsele,&llele);\n\n    /* Remove the element from the set */\n    if (encoding == OBJ_ENCODING_INTSET) {\n        ele = createStringObjectFromLongLong(llele);\n        set->ptr = intsetRemove(set->ptr,llele,NULL);\n    } else {\n        ele = createStringObject(sdsele,sdslen(sdsele));\n        setTypeRemove(set,ele->ptr);\n    }\n\n    notifyKeyspaceEvent(NOTIFY_SET,\"spop\",c->argv[1],c->db->id);\n\n    /* Replicate/AOF this command as an SREM operation */\n    aux = createStringObject(\"SREM\",4);\n    rewriteClientCommandVector(c,3,aux,c->argv[1],ele);\n    decrRefCount(aux);\n\n    /* Add the element to the reply */\n    addReplyBulk(c,ele);\n    decrRefCount(ele);\n\n    /* Delete the set if it's empty */\n    if (setTypeSize(set) == 0) {\n        dbDelete(c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",c->argv[1],c->db->id);\n    }\n\n    /* Set has been modified */\n    signalModifiedKey(c,c->db,c->argv[1]);\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/* How many times bigger should be the set compared to the requested size\n * for us to don't use the \"remove elements\" strategy? Read later in the\n * implementation for more info. */\n#define SRANDMEMBER_SUB_STRATEGY_MUL 3\n\nvoid srandmemberWithCountCommand(client *c) {\n    long l;\n    unsigned long count, size;\n    int uniq = 1;\n    robj *set;\n    sds ele;\n    int64_t llele;\n    int encoding;\n\n    dict *d;\n\n    if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;\n    if (l >= 0) {\n        count = (unsigned long) l;\n    } else {\n        /* A negative count means: return the same elements multiple times\n         * (i.e. don't remove the extracted element after every extraction). */\n        count = -l;\n        uniq = 0;\n    }\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyset[c->resp]))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n    size = setTypeSize(set);\n\n    /* If count is zero, serve it ASAP to avoid special cases later. */\n    if (count == 0) {\n        addReply(c,shared.emptyset[c->resp]);\n        return;\n    }\n\n    /* CASE 1: The count was negative, so the extraction method is just:\n     * \"return N random elements\" sampling the whole set every time.\n     * This case is trivial and can be served without auxiliary data\n     * structures. */\n    if (!uniq) {\n        addReplySetLen(c,count);\n        while(count--) {\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                addReplyBulkLongLong(c,llele);\n            } else {\n                addReplyBulkCBuffer(c,ele,sdslen(ele));\n            }\n        }\n        return;\n    }\n\n    /* CASE 2:\n     * The number of requested elements is greater than the number of\n     * elements inside the set: simply return the whole set. */\n    if (count >= size) {\n        sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION);\n        return;\n    }\n\n    /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */\n    d = dictCreate(&objectKeyPointerValueDictType,NULL);\n\n    /* CASE 3:\n     * The number of elements inside the set is not greater than\n     * SRANDMEMBER_SUB_STRATEGY_MUL times the number of requested elements.\n     * In this case we create a set from scratch with all the elements, and\n     * subtract random elements to reach the requested number of elements.\n     *\n     * This is done because if the number of requsted elements is just\n     * a bit less than the number of elements in the set, the natural approach\n     * used into CASE 3 is highly inefficient. */\n    if (count*SRANDMEMBER_SUB_STRATEGY_MUL > size) {\n        setTypeIterator *si;\n\n        /* Add all the elements into the temporary dictionary. */\n        si = setTypeInitIterator(set);\n        while((encoding = setTypeNext(si,&ele,&llele)) != -1) {\n            int retval = DICT_ERR;\n\n            if (encoding == OBJ_ENCODING_INTSET) {\n                retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);\n            } else {\n                retval = dictAdd(d,createStringObject(ele,sdslen(ele)),NULL);\n            }\n            serverAssert(retval == DICT_OK);\n        }\n        setTypeReleaseIterator(si);\n        serverAssert(dictSize(d) == size);\n\n        /* Remove random elements to reach the right count. */\n        while(size > count) {\n            dictEntry *de;\n\n            de = dictGetRandomKey(d);\n            dictDelete(d,dictGetKey(de));\n            size--;\n        }\n    }\n\n    /* CASE 4: We have a big set compared to the requested number of elements.\n     * In this case we can simply get random elements from the set and add\n     * to the temporary set, trying to eventually get enough unique elements\n     * to reach the specified count. */\n    else {\n        unsigned long added = 0;\n        robj *objele;\n\n        while(added < count) {\n            encoding = setTypeRandomElement(set,&ele,&llele);\n            if (encoding == OBJ_ENCODING_INTSET) {\n                objele = createStringObjectFromLongLong(llele);\n            } else {\n                objele = createStringObject(ele,sdslen(ele));\n            }\n            /* Try to add the object to the dictionary. If it already exists\n             * free it, otherwise increment the number of objects we have\n             * in the result dictionary. */\n            if (dictAdd(d,objele,NULL) == DICT_OK)\n                added++;\n            else\n                decrRefCount(objele);\n        }\n    }\n\n    /* CASE 3 & 4: send the result to the user. */\n    {\n        dictIterator *di;\n        dictEntry *de;\n\n        addReplySetLen(c,count);\n        di = dictGetIterator(d);\n        while((de = dictNext(di)) != NULL)\n            addReplyBulk(c,dictGetKey(de));\n        dictReleaseIterator(di);\n        dictRelease(d);\n    }\n}\n\nvoid srandmemberCommand(client *c) {\n    robj *set;\n    sds ele;\n    int64_t llele;\n    int encoding;\n\n    if (c->argc == 3) {\n        srandmemberWithCountCommand(c);\n        return;\n    } else if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]))\n        == NULL || checkType(c,set,OBJ_SET)) return;\n\n    encoding = setTypeRandomElement(set,&ele,&llele);\n    if (encoding == OBJ_ENCODING_INTSET) {\n        addReplyBulkLongLong(c,llele);\n    } else {\n        addReplyBulkCBuffer(c,ele,sdslen(ele));\n    }\n}\n\nint qsortCompareSetsByCardinality(const void *s1, const void *s2) {\n    if (setTypeSize(*(robj**)s1) > setTypeSize(*(robj**)s2)) return 1;\n    if (setTypeSize(*(robj**)s1) < setTypeSize(*(robj**)s2)) return -1;\n    return 0;\n}\n\n/* This is used by SDIFF and in this case we can receive NULL that should\n * be handled as empty sets. */\nint qsortCompareSetsByRevCardinality(const void *s1, const void *s2) {\n    robj *o1 = *(robj**)s1, *o2 = *(robj**)s2;\n    unsigned long first = o1 ? setTypeSize(o1) : 0;\n    unsigned long second = o2 ? setTypeSize(o2) : 0;\n\n    if (first < second) return 1;\n    if (first > second) return -1;\n    return 0;\n}\n\nvoid sinterGenericCommand(client *c, robj **setkeys,\n                          unsigned long setnum, robj *dstkey) {\n    robj **sets = zmalloc(sizeof(robj*)*setnum);\n    setTypeIterator *si;\n    robj *dstset = NULL;\n    sds elesds;\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        robj *setobj = dstkey ?\n            lookupKeyWrite(c->db,setkeys[j]) :\n            lookupKeyRead(c->db,setkeys[j]);\n        if (!setobj) {\n            zfree(sets);\n            if (dstkey) {\n                if (dbDelete(c->db,dstkey)) {\n                    signalModifiedKey(c,c->db,dstkey);\n                    server.dirty++;\n                }\n                addReply(c,shared.czero);\n            } else {\n                addReply(c,shared.emptyset[c->resp]);\n            }\n            return;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            zfree(sets);\n            return;\n        }\n        sets[j] = setobj;\n    }\n    /* Sort sets from the smallest to largest, this will improve our\n     * algorithm's performance */\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    if (!dstkey) {\n        replylen = addReplyDeferredLen(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    si = setTypeInitIterator(sets[0]);\n    while((encoding = setTypeNext(si,&elesds,&intobj)) != -1) {\n        for (j = 1; j < setnum; j++) {\n            if (sets[j] == sets[0]) continue;\n            if (encoding == OBJ_ENCODING_INTSET) {\n                /* intset with intset is simple... and fast */\n                if (sets[j]->encoding == OBJ_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 == OBJ_ENCODING_HT) {\n                    elesds = sdsfromlonglong(intobj);\n                    if (!setTypeIsMember(sets[j],elesds)) {\n                        sdsfree(elesds);\n                        break;\n                    }\n                    sdsfree(elesds);\n                }\n            } else if (encoding == OBJ_ENCODING_HT) {\n                if (!setTypeIsMember(sets[j],elesds)) {\n                    break;\n                }\n            }\n        }\n\n        /* Only take action when all sets contain the member */\n        if (j == setnum) {\n            if (!dstkey) {\n                if (encoding == OBJ_ENCODING_HT)\n                    addReplyBulkCBuffer(c,elesds,sdslen(elesds));\n                else\n                    addReplyBulkLongLong(c,intobj);\n                cardinality++;\n            } else {\n                if (encoding == OBJ_ENCODING_INTSET) {\n                    elesds = sdsfromlonglong(intobj);\n                    setTypeAdd(dstset,elesds);\n                    sdsfree(elesds);\n                } else {\n                    setTypeAdd(dstset,elesds);\n                }\n            }\n        }\n    }\n    setTypeReleaseIterator(si);\n\n    if (dstkey) {\n        /* Store the resulting set into the target, if the intersection\n         * is not an empty set. */\n        int deleted = dbDelete(c->db,dstkey);\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(NOTIFY_SET,\"sinterstore\",\n                dstkey,c->db->id);\n        } else {\n            decrRefCount(dstset);\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n        signalModifiedKey(c,c->db,dstkey);\n        server.dirty++;\n    } else {\n        setDeferredSetLen(c,replylen,cardinality);\n    }\n    zfree(sets);\n}\n\nvoid sinterCommand(client *c) {\n    sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);\n}\n\nvoid sinterstoreCommand(client *c) {\n    sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);\n}\n\n#define SET_OP_UNION 0\n#define SET_OP_DIFF 1\n#define SET_OP_INTER 2\n\nvoid sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,\n                              robj *dstkey, int op) {\n    robj **sets = zmalloc(sizeof(robj*)*setnum);\n    setTypeIterator *si;\n    robj *dstset = NULL;\n    sds ele;\n    int j, cardinality = 0;\n    int diff_algo = 1;\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        if (!setobj) {\n            sets[j] = NULL;\n            continue;\n        }\n        if (checkType(c,setobj,OBJ_SET)) {\n            zfree(sets);\n            return;\n        }\n        sets[j] = setobj;\n    }\n\n    /* Select what DIFF algorithm to use.\n     *\n     * Algorithm 1 is O(N*M) where N is the size of the element first set\n     * and M the total number of sets.\n     *\n     * Algorithm 2 is O(N) where N is the total number of elements in all\n     * the sets.\n     *\n     * We compute what is the best bet with the current input here. */\n    if (op == SET_OP_DIFF && sets[0]) {\n        long long algo_one_work = 0, algo_two_work = 0;\n\n        for (j = 0; j < setnum; j++) {\n            if (sets[j] == NULL) continue;\n\n            algo_one_work += setTypeSize(sets[0]);\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        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            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    dstset = createIntsetObject();\n\n    if (op == SET_OP_UNION) {\n        /* Union is trivial, just add every element of every set to the\n         * temporary set. */\n        for (j = 0; j < setnum; j++) {\n            if (!sets[j]) continue; /* non existing keys are like empty sets */\n\n            si = setTypeInitIterator(sets[j]);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                if (setTypeAdd(dstset,ele)) cardinality++;\n                sdsfree(ele);\n            }\n            setTypeReleaseIterator(si);\n        }\n    } else if (op == SET_OP_DIFF && sets[0] && diff_algo == 1) {\n        /* DIFF Algorithm 1:\n         *\n         * We perform the diff by iterating all the elements of the first set,\n         * and only adding it to the target set if the element does not exist\n         * into all the other sets.\n         *\n         * This way we perform at max N*M operations, where N is the size of\n         * the first set, and M the number of sets. */\n        si = setTypeInitIterator(sets[0]);\n        while((ele = setTypeNextObject(si)) != NULL) {\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            if (j == setnum) {\n                /* There is no other set with this element. Add it. */\n                setTypeAdd(dstset,ele);\n                cardinality++;\n            }\n            sdsfree(ele);\n        }\n        setTypeReleaseIterator(si);\n    } else if (op == SET_OP_DIFF && sets[0] && diff_algo == 2) {\n        /* DIFF Algorithm 2:\n         *\n         * Add all the elements of the first set to the auxiliary set.\n         * Then remove all the elements of all the next sets from it.\n         *\n         * This is O(N) where N is the sum of all the elements in every\n         * set. */\n        for (j = 0; j < setnum; j++) {\n            if (!sets[j]) continue; /* non existing keys are like empty sets */\n\n            si = setTypeInitIterator(sets[j]);\n            while((ele = setTypeNextObject(si)) != NULL) {\n                if (j == 0) {\n                    if (setTypeAdd(dstset,ele)) cardinality++;\n                } else {\n                    if (setTypeRemove(dstset,ele)) cardinality--;\n                }\n                sdsfree(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    if (!dstkey) {\n        addReplySetLen(c,cardinality);\n        si = setTypeInitIterator(dstset);\n        while((ele = setTypeNextObject(si)) != NULL) {\n            addReplyBulkCBuffer(c,ele,sdslen(ele));\n            sdsfree(ele);\n        }\n        setTypeReleaseIterator(si);\n        server.lazyfree_lazy_server_del ? freeObjAsync(dstset) :\n                                          decrRefCount(dstset);\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        int deleted = dbDelete(c->db,dstkey);\n        if (setTypeSize(dstset) > 0) {\n            dbAdd(c->db,dstkey,dstset);\n            addReplyLongLong(c,setTypeSize(dstset));\n            notifyKeyspaceEvent(NOTIFY_SET,\n                op == SET_OP_UNION ? \"sunionstore\" : \"sdiffstore\",\n                dstkey,c->db->id);\n        } else {\n            decrRefCount(dstset);\n            addReply(c,shared.czero);\n            if (deleted)\n                notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",\n                    dstkey,c->db->id);\n        }\n        signalModifiedKey(c,c->db,dstkey);\n        server.dirty++;\n    }\n    zfree(sets);\n}\n\nvoid sunionCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_UNION);\n}\n\nvoid sunionstoreCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_UNION);\n}\n\nvoid sdiffCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,SET_OP_DIFF);\n}\n\nvoid sdiffstoreCommand(client *c) {\n    sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],SET_OP_DIFF);\n}\n\nvoid sscanCommand(client *c) {\n    robj *set;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;\n    if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,set,OBJ_SET)) return;\n    scanGenericCommand(c,set,cursor);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/t_stream.c",
    "content": "/*\n * Copyright (c) 2017, 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 \"server.h\"\n#include \"endianconv.h\"\n#include \"stream.h\"\n\n#define STREAM_BYTES_PER_LISTPACK 2048\n\n/* Every stream item inside the listpack, has a flags field that is used to\n * mark the entry as deleted, or having the same field as the \"master\"\n * entry at the start of the listpack> */\n#define STREAM_ITEM_FLAG_NONE 0             /* No special flags. */\n#define STREAM_ITEM_FLAG_DELETED (1<<0)     /* Entry is deleted. Skip it. */\n#define STREAM_ITEM_FLAG_SAMEFIELDS (1<<1)  /* Same fields as master entry. */\n\nvoid streamFreeCG(streamCG *cg);\nvoid streamFreeNACK(streamNACK *na);\nsize_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer);\n\n/* -----------------------------------------------------------------------\n * Low level stream encoding: a radix tree of listpacks.\n * ----------------------------------------------------------------------- */\n\n/* Create a new stream data structure. */\nstream *streamNew(void) {\n    stream *s = zmalloc(sizeof(*s));\n    s->rax = raxNew();\n    s->length = 0;\n    s->last_id.ms = 0;\n    s->last_id.seq = 0;\n    s->cgroups = NULL; /* Created on demand to save memory when not used. */\n    return s;\n}\n\n/* Free a stream, including the listpacks stored inside the radix tree. */\nvoid freeStream(stream *s) {\n    raxFreeWithCallback(s->rax,(void(*)(void*))lpFree);\n    if (s->cgroups)\n        raxFreeWithCallback(s->cgroups,(void(*)(void*))streamFreeCG);\n    zfree(s);\n}\n\n/* Return the length of a stream. */\nunsigned long streamLength(const robj *subject) {\n    stream *s = subject->ptr;\n    return s->length;\n}\n\n/* Set 'id' to be its successor streamID */\nvoid streamIncrID(streamID *id) {\n    if (id->seq == UINT64_MAX) {\n        if (id->ms == UINT64_MAX) {\n            /* Special case where 'id' is the last possible streamID... */\n            id->ms = id->seq = 0;\n        } else {\n            id->ms++;\n            id->seq = 0;\n        }\n    } else {\n        id->seq++;\n    }\n}\n\n/* Generate the next stream item ID given the previous one. If the current\n * milliseconds Unix time is greater than the previous one, just use this\n * as time part and start with sequence part of zero. Otherwise we use the\n * previous time (and never go backward) and increment the sequence. */\nvoid streamNextID(streamID *last_id, streamID *new_id) {\n    uint64_t ms = mstime();\n    if (ms > last_id->ms) {\n        new_id->ms = ms;\n        new_id->seq = 0;\n    } else {\n        *new_id = *last_id;\n        streamIncrID(new_id);\n    }\n}\n\n/* This is just a wrapper for lpAppend() to directly use a 64 bit integer\n * instead of a string. */\nunsigned char *lpAppendInteger(unsigned char *lp, int64_t value) {\n    char buf[LONG_STR_SIZE];\n    int slen = ll2string(buf,sizeof(buf),value);\n    return lpAppend(lp,(unsigned char*)buf,slen);\n}\n\n/* This is just a wrapper for lpReplace() to directly use a 64 bit integer\n * instead of a string to replace the current element. The function returns\n * the new listpack as return value, and also updates the current cursor\n * by updating '*pos'. */\nunsigned char *lpReplaceInteger(unsigned char *lp, unsigned char **pos, int64_t value) {\n    char buf[LONG_STR_SIZE];\n    int slen = ll2string(buf,sizeof(buf),value);\n    return lpInsert(lp, (unsigned char*)buf, slen, *pos, LP_REPLACE, pos);\n}\n\n/* This is a wrapper function for lpGet() to directly get an integer value\n * from the listpack (that may store numbers as a string), converting\n * the string if needed. */\nint64_t lpGetInteger(unsigned char *ele) {\n    int64_t v;\n    unsigned char *e = lpGet(ele,&v,NULL);\n    if (e == NULL) return v;\n    /* The following code path should never be used for how listpacks work:\n     * they should always be able to store an int64_t value in integer\n     * encoded form. However the implementation may change. */\n    long long ll;\n    int retval = string2ll((char*)e,v,&ll);\n    serverAssert(retval != 0);\n    v = ll;\n    return v;\n}\n\n/* Debugging function to log the full content of a listpack. Useful\n * for development and debugging. */\nvoid streamLogListpackContent(unsigned char *lp) {\n    unsigned char *p = lpFirst(lp);\n    while(p) {\n        unsigned char buf[LP_INTBUF_SIZE];\n        int64_t v;\n        unsigned char *ele = lpGet(p,&v,buf);\n        serverLog(LL_WARNING,\"- [%d] '%.*s'\", (int)v, (int)v, ele);\n        p = lpNext(lp,p);\n    }\n}\n\n/* Convert the specified stream entry ID as a 128 bit big endian number, so\n * that the IDs can be sorted lexicographically. */\nvoid streamEncodeID(void *buf, streamID *id) {\n    uint64_t e[2];\n    e[0] = htonu64(id->ms);\n    e[1] = htonu64(id->seq);\n    memcpy(buf,e,sizeof(e));\n}\n\n/* This is the reverse of streamEncodeID(): the decoded ID will be stored\n * in the 'id' structure passed by reference. The buffer 'buf' must point\n * to a 128 bit big-endian encoded ID. */\nvoid streamDecodeID(void *buf, streamID *id) {\n    uint64_t e[2];\n    memcpy(e,buf,sizeof(e));\n    id->ms = ntohu64(e[0]);\n    id->seq = ntohu64(e[1]);\n}\n\n/* Compare two stream IDs. Return -1 if a < b, 0 if a == b, 1 if a > b. */\nint streamCompareID(streamID *a, streamID *b) {\n    if (a->ms > b->ms) return 1;\n    else if (a->ms < b->ms) return -1;\n    /* The ms part is the same. Check the sequence part. */\n    else if (a->seq > b->seq) return 1;\n    else if (a->seq < b->seq) return -1;\n    /* Everything is the same: IDs are equal. */\n    return 0;\n}\n\n/* Adds a new item into the stream 's' having the specified number of\n * field-value pairs as specified in 'numfields' and stored into 'argv'.\n * Returns the new entry ID populating the 'added_id' structure.\n *\n * If 'use_id' is not NULL, the ID is not auto-generated by the function,\n * but instead the passed ID is used to add the new entry. In this case\n * adding the entry may fail as specified later in this comment.\n *\n * The function returns C_OK if the item was added, this is always true\n * if the ID was generated by the function. However the function may return\n * C_ERR if an ID was given via 'use_id', but adding it failed since the\n * current top ID is greater or equal. */\nint streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_id, streamID *use_id) {\n    \n    /* Generate the new entry ID. */\n    streamID id;\n    if (use_id)\n        id = *use_id;\n    else\n        streamNextID(&s->last_id,&id);\n\n    /* Check that the new ID is greater than the last entry ID\n     * or return an error. Automatically generated IDs might\n     * overflow (and wrap-around) when incrementing the sequence \n       part. */\n    if (streamCompareID(&id,&s->last_id) <= 0) return C_ERR;\n\n    /* Add the new entry. */\n    raxIterator ri;\n    raxStart(&ri,s->rax);\n    raxSeek(&ri,\"$\",NULL,0);\n\n    size_t lp_bytes = 0;        /* Total bytes in the tail listpack. */\n    unsigned char *lp = NULL;   /* Tail listpack pointer. */\n\n    /* Get a reference to the tail node listpack. */\n    if (raxNext(&ri)) {\n        lp = ri.data;\n        lp_bytes = lpBytes(lp);\n    }\n    raxStop(&ri);\n\n    /* We have to add the key into the radix tree in lexicographic order,\n     * to do so we consider the ID as a single 128 bit number written in\n     * big endian, so that the most significant bytes are the first ones. */\n    uint64_t rax_key[2];    /* Key in the radix tree containing the listpack.*/\n    streamID master_id;     /* ID of the master entry in the listpack. */\n\n    /* Create a new listpack and radix tree node if needed. Note that when\n     * a new listpack is created, we populate it with a \"master entry\". This\n     * is just a set of fields that is taken as references in order to compress\n     * the stream entries that we'll add inside the listpack.\n     *\n     * Note that while we use the first added entry fields to create\n     * the master entry, the first added entry is NOT represented in the master\n     * entry, which is a stand alone object. But of course, the first entry\n     * will compress well because it's used as reference.\n     *\n     * The master entry is composed like in the following example:\n     *\n     * +-------+---------+------------+---------+--/--+---------+---------+-+\n     * | count | deleted | num-fields | field_1 | field_2 | ... | field_N |0|\n     * +-------+---------+------------+---------+--/--+---------+---------+-+\n     *\n     * count and deleted just represent respectively the total number of\n     * entries inside the listpack that are valid, and marked as deleted\n     * (deleted flag in the entry flags set). So the total number of items\n     * actually inside the listpack (both deleted and not) is count+deleted.\n     *\n     * The real entries will be encoded with an ID that is just the\n     * millisecond and sequence difference compared to the key stored at\n     * the radix tree node containing the listpack (delta encoding), and\n     * if the fields of the entry are the same as the master entry fields, the\n     * entry flags will specify this fact and the entry fields and number\n     * of fields will be omitted (see later in the code of this function).\n     *\n     * The \"0\" entry at the end is the same as the 'lp-count' entry in the\n     * regular stream entries (see below), and marks the fact that there are\n     * no more entries, when we scan the stream from right to left. */\n\n    /* First of all, check if we can append to the current macro node or\n     * if we need to switch to the next one. 'lp' will be set to NULL if\n     * the current node is full. */\n    if (lp != NULL) {\n        if (server.stream_node_max_bytes &&\n            lp_bytes >= server.stream_node_max_bytes)\n        {\n            lp = NULL;\n        } else if (server.stream_node_max_entries) {\n            int64_t count = lpGetInteger(lpFirst(lp));\n            if (count >= server.stream_node_max_entries) lp = NULL;\n        }\n    }\n\n    int flags = STREAM_ITEM_FLAG_NONE;\n    if (lp == NULL || lp_bytes >= server.stream_node_max_bytes) {\n        master_id = id;\n        streamEncodeID(rax_key,&id);\n        /* Create the listpack having the master entry ID and fields. */\n        lp = lpNew();\n        lp = lpAppendInteger(lp,1); /* One item, the one we are adding. */\n        lp = lpAppendInteger(lp,0); /* Zero deleted so far. */\n        lp = lpAppendInteger(lp,numfields);\n        for (int64_t i = 0; i < numfields; i++) {\n            sds field = argv[i*2]->ptr;\n            lp = lpAppend(lp,(unsigned char*)field,sdslen(field));\n        }\n        lp = lpAppendInteger(lp,0); /* Master entry zero terminator. */\n        raxInsert(s->rax,(unsigned char*)&rax_key,sizeof(rax_key),lp,NULL);\n        /* The first entry we insert, has obviously the same fields of the\n         * master entry. */\n        flags |= STREAM_ITEM_FLAG_SAMEFIELDS;\n    } else {\n        serverAssert(ri.key_len == sizeof(rax_key));\n        memcpy(rax_key,ri.key,sizeof(rax_key));\n\n        /* Read the master ID from the radix tree key. */\n        streamDecodeID(rax_key,&master_id);\n        unsigned char *lp_ele = lpFirst(lp);\n\n        /* Update count and skip the deleted fields. */\n        int64_t count = lpGetInteger(lp_ele);\n        lp = lpReplaceInteger(lp,&lp_ele,count+1);\n        lp_ele = lpNext(lp,lp_ele); /* seek deleted. */\n        lp_ele = lpNext(lp,lp_ele); /* seek master entry num fields. */\n\n        /* Check if the entry we are adding, have the same fields\n         * as the master entry. */\n        int64_t master_fields_count = lpGetInteger(lp_ele);\n        lp_ele = lpNext(lp,lp_ele);\n        if (numfields == master_fields_count) {\n            int64_t i;\n            for (i = 0; i < master_fields_count; i++) {\n                sds field = argv[i*2]->ptr;\n                int64_t e_len;\n                unsigned char buf[LP_INTBUF_SIZE];\n                unsigned char *e = lpGet(lp_ele,&e_len,buf);\n                /* Stop if there is a mismatch. */\n                if (sdslen(field) != (size_t)e_len ||\n                    memcmp(e,field,e_len) != 0) break;\n                lp_ele = lpNext(lp,lp_ele);\n            }\n            /* All fields are the same! We can compress the field names\n             * setting a single bit in the flags. */\n            if (i == master_fields_count) flags |= STREAM_ITEM_FLAG_SAMEFIELDS;\n        }\n    }\n\n    /* Populate the listpack with the new entry. We use the following\n     * encoding:\n     *\n     * +-----+--------+----------+-------+-------+-/-+-------+-------+--------+\n     * |flags|entry-id|num-fields|field-1|value-1|...|field-N|value-N|lp-count|\n     * +-----+--------+----------+-------+-------+-/-+-------+-------+--------+\n     *\n     * However if the SAMEFIELD flag is set, we have just to populate\n     * the entry with the values, so it becomes:\n     *\n     * +-----+--------+-------+-/-+-------+--------+\n     * |flags|entry-id|value-1|...|value-N|lp-count|\n     * +-----+--------+-------+-/-+-------+--------+\n     *\n     * The entry-id field is actually two separated fields: the ms\n     * and seq difference compared to the master entry.\n     *\n     * The lp-count field is a number that states the number of listpack pieces\n     * that compose the entry, so that it's possible to travel the entry\n     * in reverse order: we can just start from the end of the listpack, read\n     * the entry, and jump back N times to seek the \"flags\" field to read\n     * the stream full entry. */\n    lp = lpAppendInteger(lp,flags);\n    lp = lpAppendInteger(lp,id.ms - master_id.ms);\n    lp = lpAppendInteger(lp,id.seq - master_id.seq);\n    if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS))\n        lp = lpAppendInteger(lp,numfields);\n    for (int64_t i = 0; i < numfields; i++) {\n        sds field = argv[i*2]->ptr, value = argv[i*2+1]->ptr;\n        if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS))\n            lp = lpAppend(lp,(unsigned char*)field,sdslen(field));\n        lp = lpAppend(lp,(unsigned char*)value,sdslen(value));\n    }\n    /* Compute and store the lp-count field. */\n    int64_t lp_count = numfields;\n    lp_count += 3; /* Add the 3 fixed fields flags + ms-diff + seq-diff. */\n    if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS)) {\n        /* If the item is not compressed, it also has the fields other than\n         * the values, and an additional num-fileds field. */\n        lp_count += numfields+1;\n    }\n    lp = lpAppendInteger(lp,lp_count);\n\n    /* Insert back into the tree in order to update the listpack pointer. */\n    if (ri.data != lp)\n        raxInsert(s->rax,(unsigned char*)&rax_key,sizeof(rax_key),lp,NULL);\n    s->length++;\n    s->last_id = id;\n    if (added_id) *added_id = id;\n    return C_OK;\n}\n\n/* Trim the stream 's' to have no more than maxlen elements, and return the\n * number of elements removed from the stream. The 'approx' option, if non-zero,\n * specifies that the trimming must be performed in a approximated way in\n * order to maximize performances. This means that the stream may contain\n * more elements than 'maxlen', and elements are only removed if we can remove\n * a *whole* node of the radix tree. The elements are removed from the head\n * of the stream (older elements).\n *\n * The function may return zero if:\n *\n * 1) The stream is already shorter or equal to the specified max length.\n * 2) The 'approx' option is true and the head node had not enough elements\n *    to be deleted, leaving the stream with a number of elements >= maxlen.\n */\nint64_t streamTrimByLength(stream *s, size_t maxlen, int approx) {\n    if (s->length <= maxlen) return 0;\n\n    raxIterator ri;\n    raxStart(&ri,s->rax);\n    raxSeek(&ri,\"^\",NULL,0);\n\n    int64_t deleted = 0;\n    while(s->length > maxlen && raxNext(&ri)) {\n        unsigned char *lp = ri.data, *p = lpFirst(lp);\n        int64_t entries = lpGetInteger(p);\n\n        /* Check if we can remove the whole node, and still have at\n         * least maxlen elements. */\n        if (s->length - entries >= maxlen) {\n            lpFree(lp);\n            raxRemove(s->rax,ri.key,ri.key_len,NULL);\n            raxSeek(&ri,\">=\",ri.key,ri.key_len);\n            s->length -= entries;\n            deleted += entries;\n            continue;\n        }\n\n        /* If we cannot remove a whole element, and approx is true,\n         * stop here. */\n        if (approx) break;\n\n        /* Otherwise, we have to mark single entries inside the listpack\n         * as deleted. We start by updating the entries/deleted counters. */\n        int64_t to_delete = s->length - maxlen;\n        serverAssert(to_delete < entries);\n        lp = lpReplaceInteger(lp,&p,entries-to_delete);\n        p = lpNext(lp,p); /* Seek deleted field. */\n        int64_t marked_deleted = lpGetInteger(p);\n        lp = lpReplaceInteger(lp,&p,marked_deleted+to_delete);\n        p = lpNext(lp,p); /* Seek num-of-fields in the master entry. */\n\n        /* Skip all the master fields. */\n        int64_t master_fields_count = lpGetInteger(p);\n        p = lpNext(lp,p); /* Seek the first field. */\n        for (int64_t j = 0; j < master_fields_count; j++)\n            p = lpNext(lp,p); /* Skip all master fields. */\n        p = lpNext(lp,p); /* Skip the zero master entry terminator. */\n\n        /* 'p' is now pointing to the first entry inside the listpack.\n         * We have to run entry after entry, marking entries as deleted\n         * if they are already not deleted. */\n        while(p) {\n            int flags = lpGetInteger(p);\n            int to_skip;\n\n            /* Mark the entry as deleted. */\n            if (!(flags & STREAM_ITEM_FLAG_DELETED)) {\n                flags |= STREAM_ITEM_FLAG_DELETED;\n                lp = lpReplaceInteger(lp,&p,flags);\n                deleted++;\n                s->length--;\n                if (s->length <= maxlen) break; /* Enough entries deleted. */\n            }\n\n            p = lpNext(lp,p); /* Skip ID ms delta. */\n            p = lpNext(lp,p); /* Skip ID seq delta. */\n            p = lpNext(lp,p); /* Seek num-fields or values (if compressed). */\n            if (flags & STREAM_ITEM_FLAG_SAMEFIELDS) {\n                to_skip = master_fields_count;\n            } else {\n                to_skip = lpGetInteger(p);\n                to_skip = 1+(to_skip*2);\n            }\n\n            while(to_skip--) p = lpNext(lp,p); /* Skip the whole entry. */\n            p = lpNext(lp,p); /* Skip the final lp-count field. */\n        }\n\n        /* Here we should perform garbage collection in case at this point\n         * there are too many entries deleted inside the listpack. */\n        entries -= to_delete;\n        marked_deleted += to_delete;\n        if (entries + marked_deleted > 10 && marked_deleted > entries/2) {\n            /* TODO: perform a garbage collection. */\n        }\n\n        /* Update the listpack with the new pointer. */\n        raxInsert(s->rax,ri.key,ri.key_len,lp,NULL);\n\n        break; /* If we are here, there was enough to delete in the current\n                  node, so no need to go to the next node. */\n    }\n\n    raxStop(&ri);\n    return deleted;\n}\n\n/* Initialize the stream iterator, so that we can call iterating functions\n * to get the next items. This requires a corresponding streamIteratorStop()\n * at the end. The 'rev' parameter controls the direction. If it's zero the\n * iteration is from the start to the end element (inclusive), otherwise\n * if rev is non-zero, the iteration is reversed.\n *\n * Once the iterator is initialized, we iterate like this:\n *\n *  streamIterator myiterator;\n *  streamIteratorStart(&myiterator,...);\n *  int64_t numfields;\n *  while(streamIteratorGetID(&myiterator,&ID,&numfields)) {\n *      while(numfields--) {\n *          unsigned char *key, *value;\n *          size_t key_len, value_len;\n *          streamIteratorGetField(&myiterator,&key,&value,&key_len,&value_len);\n *\n *          ... do what you want with key and value ...\n *      }\n *  }\n *  streamIteratorStop(&myiterator); */\nvoid streamIteratorStart(streamIterator *si, stream *s, streamID *start, streamID *end, int rev) {\n    /* Initialize the iterator and translates the iteration start/stop\n     * elements into a 128 big big-endian number. */\n    if (start) {\n        streamEncodeID(si->start_key,start);\n    } else {\n        si->start_key[0] = 0;\n        si->start_key[1] = 0;\n    }\n\n    if (end) {\n        streamEncodeID(si->end_key,end);\n    } else {\n        si->end_key[0] = UINT64_MAX;\n        si->end_key[1] = UINT64_MAX;\n    }\n\n    /* Seek the correct node in the radix tree. */\n    raxStart(&si->ri,s->rax);\n    if (!rev) {\n        if (start && (start->ms || start->seq)) {\n            raxSeek(&si->ri,\"<=\",(unsigned char*)si->start_key,\n                    sizeof(si->start_key));\n            if (raxEOF(&si->ri)) raxSeek(&si->ri,\"^\",NULL,0);\n        } else {\n            raxSeek(&si->ri,\"^\",NULL,0);\n        }\n    } else {\n        if (end && (end->ms || end->seq)) {\n            raxSeek(&si->ri,\"<=\",(unsigned char*)si->end_key,\n                    sizeof(si->end_key));\n            if (raxEOF(&si->ri)) raxSeek(&si->ri,\"$\",NULL,0);\n        } else {\n            raxSeek(&si->ri,\"$\",NULL,0);\n        }\n    }\n    si->stream = s;\n    si->lp = NULL; /* There is no current listpack right now. */\n    si->lp_ele = NULL; /* Current listpack cursor. */\n    si->rev = rev;  /* Direction, if non-zero reversed, from end to start. */\n}\n\n/* Return 1 and store the current item ID at 'id' if there are still\n * elements within the iteration range, otherwise return 0 in order to\n * signal the iteration terminated. */\nint streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields) {\n    while(1) { /* Will stop when element > stop_key or end of radix tree. */\n        /* If the current listpack is set to NULL, this is the start of the\n         * iteration or the previous listpack was completely iterated.\n         * Go to the next node. */\n        if (si->lp == NULL || si->lp_ele == NULL) {\n            if (!si->rev && !raxNext(&si->ri)) return 0;\n            else if (si->rev && !raxPrev(&si->ri)) return 0;\n            serverAssert(si->ri.key_len == sizeof(streamID));\n            /* Get the master ID. */\n            streamDecodeID(si->ri.key,&si->master_id);\n            /* Get the master fields count. */\n            si->lp = si->ri.data;\n            si->lp_ele = lpFirst(si->lp);           /* Seek items count */\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek deleted count. */\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek num fields. */\n            si->master_fields_count = lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek first field. */\n            si->master_fields_start = si->lp_ele;\n            /* We are now pointing to the first field of the master entry.\n             * We need to seek either the first or the last entry depending\n             * on the direction of the iteration. */\n            if (!si->rev) {\n                /* If we are iterating in normal order, skip the master fields\n                 * to seek the first actual entry. */\n                for (uint64_t i = 0; i < si->master_fields_count; i++)\n                    si->lp_ele = lpNext(si->lp,si->lp_ele);\n            } else {\n                /* If we are iterating in reverse direction, just seek the\n                 * last part of the last entry in the listpack (that is, the\n                 * fields count). */\n                si->lp_ele = lpLast(si->lp);\n            }\n        } else if (si->rev) {\n            /* If we are iterating in the reverse order, and this is not\n             * the first entry emitted for this listpack, then we already\n             * emitted the current entry, and have to go back to the previous\n             * one. */\n            int lp_count = lpGetInteger(si->lp_ele);\n            while(lp_count--) si->lp_ele = lpPrev(si->lp,si->lp_ele);\n            /* Seek lp-count of prev entry. */\n            si->lp_ele = lpPrev(si->lp,si->lp_ele);\n        }\n\n        /* For every radix tree node, iterate the corresponding listpack,\n         * returning elements when they are within range. */\n        while(1) {\n            if (!si->rev) {\n                /* If we are going forward, skip the previous entry\n                 * lp-count field (or in case of the master entry, the zero\n                 * term field) */\n                si->lp_ele = lpNext(si->lp,si->lp_ele);\n                if (si->lp_ele == NULL) break;\n            } else {\n                /* If we are going backward, read the number of elements this\n                 * entry is composed of, and jump backward N times to seek\n                 * its start. */\n                int64_t lp_count = lpGetInteger(si->lp_ele);\n                if (lp_count == 0) { /* We reached the master entry. */\n                    si->lp = NULL;\n                    si->lp_ele = NULL;\n                    break;\n                }\n                while(lp_count--) si->lp_ele = lpPrev(si->lp,si->lp_ele);\n            }\n\n            /* Get the flags entry. */\n            si->lp_flags = si->lp_ele;\n            int flags = lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek ID. */\n\n            /* Get the ID: it is encoded as difference between the master\n             * ID and this entry ID. */\n            *id = si->master_id;\n            id->ms += lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele);\n            id->seq += lpGetInteger(si->lp_ele);\n            si->lp_ele = lpNext(si->lp,si->lp_ele);\n            unsigned char buf[sizeof(streamID)];\n            streamEncodeID(buf,id);\n\n            /* The number of entries is here or not depending on the\n             * flags. */\n            if (flags & STREAM_ITEM_FLAG_SAMEFIELDS) {\n                *numfields = si->master_fields_count;\n            } else {\n                *numfields = lpGetInteger(si->lp_ele);\n                si->lp_ele = lpNext(si->lp,si->lp_ele);\n            }\n\n            /* If current >= start, and the entry is not marked as\n             * deleted, emit it. */\n            if (!si->rev) {\n                if (memcmp(buf,si->start_key,sizeof(streamID)) >= 0 &&\n                    !(flags & STREAM_ITEM_FLAG_DELETED))\n                {\n                    if (memcmp(buf,si->end_key,sizeof(streamID)) > 0)\n                        return 0; /* We are already out of range. */\n                    si->entry_flags = flags;\n                    if (flags & STREAM_ITEM_FLAG_SAMEFIELDS)\n                        si->master_fields_ptr = si->master_fields_start;\n                    return 1; /* Valid item returned. */\n                }\n            } else {\n                if (memcmp(buf,si->end_key,sizeof(streamID)) <= 0 &&\n                    !(flags & STREAM_ITEM_FLAG_DELETED))\n                {\n                    if (memcmp(buf,si->start_key,sizeof(streamID)) < 0)\n                        return 0; /* We are already out of range. */\n                    si->entry_flags = flags;\n                    if (flags & STREAM_ITEM_FLAG_SAMEFIELDS)\n                        si->master_fields_ptr = si->master_fields_start;\n                    return 1; /* Valid item returned. */\n                }\n            }\n\n            /* If we do not emit, we have to discard if we are going\n             * forward, or seek the previous entry if we are going\n             * backward. */\n            if (!si->rev) {\n                int64_t to_discard = (flags & STREAM_ITEM_FLAG_SAMEFIELDS) ?\n                                      *numfields : *numfields*2;\n                for (int64_t i = 0; i < to_discard; i++)\n                    si->lp_ele = lpNext(si->lp,si->lp_ele);\n            } else {\n                int64_t prev_times = 4; /* flag + id ms + id seq + one more to\n                                           go back to the previous entry \"count\"\n                                           field. */\n                /* If the entry was not flagged SAMEFIELD we also read the\n                 * number of fields, so go back one more. */\n                if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS)) prev_times++;\n                while(prev_times--) si->lp_ele = lpPrev(si->lp,si->lp_ele);\n            }\n        }\n\n        /* End of listpack reached. Try the next/prev radix tree node. */\n    }\n}\n\n/* Get the field and value of the current item we are iterating. This should\n * be called immediately after streamIteratorGetID(), and for each field\n * according to the number of fields returned by streamIteratorGetID().\n * The function populates the field and value pointers and the corresponding\n * lengths by reference, that are valid until the next iterator call, assuming\n * no one touches the stream meanwhile. */\nvoid streamIteratorGetField(streamIterator *si, unsigned char **fieldptr, unsigned char **valueptr, int64_t *fieldlen, int64_t *valuelen) {\n    if (si->entry_flags & STREAM_ITEM_FLAG_SAMEFIELDS) {\n        *fieldptr = lpGet(si->master_fields_ptr,fieldlen,si->field_buf);\n        si->master_fields_ptr = lpNext(si->lp,si->master_fields_ptr);\n    } else {\n        *fieldptr = lpGet(si->lp_ele,fieldlen,si->field_buf);\n        si->lp_ele = lpNext(si->lp,si->lp_ele);\n    }\n    *valueptr = lpGet(si->lp_ele,valuelen,si->value_buf);\n    si->lp_ele = lpNext(si->lp,si->lp_ele);\n}\n\n/* Remove the current entry from the stream: can be called after the\n * GetID() API or after any GetField() call, however we need to iterate\n * a valid entry while calling this function. Moreover the function\n * requires the entry ID we are currently iterating, that was previously\n * returned by GetID().\n *\n * Note that after calling this function, next calls to GetField() can't\n * be performed: the entry is now deleted. Instead the iterator will\n * automatically re-seek to the next entry, so the caller should continue\n * with GetID(). */\nvoid streamIteratorRemoveEntry(streamIterator *si, streamID *current) {\n    unsigned char *lp = si->lp;\n    int64_t aux;\n\n    /* We do not really delete the entry here. Instead we mark it as\n     * deleted flagging it, and also incrementing the count of the\n     * deleted entries in the listpack header.\n     *\n     * We start flagging: */\n    int flags = lpGetInteger(si->lp_flags);\n    flags |= STREAM_ITEM_FLAG_DELETED;\n    lp = lpReplaceInteger(lp,&si->lp_flags,flags);\n\n    /* Change the valid/deleted entries count in the master entry. */\n    unsigned char *p = lpFirst(lp);\n    aux = lpGetInteger(p);\n\n    if (aux == 1) {\n        /* If this is the last element in the listpack, we can remove the whole\n         * node. */\n        lpFree(lp);\n        raxRemove(si->stream->rax,si->ri.key,si->ri.key_len,NULL);\n    } else {\n        /* In the base case we alter the counters of valid/deleted entries. */\n        lp = lpReplaceInteger(lp,&p,aux-1);\n        p = lpNext(lp,p); /* Seek deleted field. */\n        aux = lpGetInteger(p);\n        lp = lpReplaceInteger(lp,&p,aux+1);\n\n        /* Update the listpack with the new pointer. */\n        if (si->lp != lp)\n            raxInsert(si->stream->rax,si->ri.key,si->ri.key_len,lp,NULL);\n    }\n\n    /* Update the number of entries counter. */\n    si->stream->length--;\n\n    /* Re-seek the iterator to fix the now messed up state. */\n    streamID start, end;\n    if (si->rev) {\n        streamDecodeID(si->start_key,&start);\n        end = *current;\n    } else {\n        start = *current;\n        streamDecodeID(si->end_key,&end);\n    }\n    streamIteratorStop(si);\n    streamIteratorStart(si,si->stream,&start,&end,si->rev);\n\n    /* TODO: perform a garbage collection here if the ration between\n     * deleted and valid goes over a certain limit. */\n}\n\n/* Stop the stream iterator. The only cleanup we need is to free the rax\n * iterator, since the stream iterator itself is supposed to be stack\n * allocated. */\nvoid streamIteratorStop(streamIterator *si) {\n    raxStop(&si->ri);\n}\n\n/* Delete the specified item ID from the stream, returning 1 if the item\n * was deleted 0 otherwise (if it does not exist). */\nint streamDeleteItem(stream *s, streamID *id) {\n    int deleted = 0;\n    streamIterator si;\n    streamIteratorStart(&si,s,id,id,0);\n    streamID myid;\n    int64_t numfields;\n    if (streamIteratorGetID(&si,&myid,&numfields)) {\n        streamIteratorRemoveEntry(&si,&myid);\n        deleted = 1;\n    }\n    streamIteratorStop(&si);\n    return deleted;\n}\n\n/* Get the last valid (non-tombstone) streamID of 's'. */\nvoid streamLastValidID(stream *s, streamID *maxid)\n{\n    streamIterator si;\n    streamIteratorStart(&si,s,NULL,NULL,1);\n    int64_t numfields;\n    streamIteratorGetID(&si,maxid,&numfields);\n    streamIteratorStop(&si);\n}\n\n/* Emit a reply in the client output buffer by formatting a Stream ID\n * in the standard <ms>-<seq> format, using the simple string protocol\n * of REPL. */\nvoid addReplyStreamID(client *c, streamID *id) {\n    sds replyid = sdscatfmt(sdsempty(),\"%U-%U\",id->ms,id->seq);\n    addReplyBulkSds(c,replyid);\n}\n\n/* Similar to the above function, but just creates an object, usually useful\n * for replication purposes to create arguments. */\nrobj *createObjectFromStreamID(streamID *id) {\n    return createObject(OBJ_STRING, sdscatfmt(sdsempty(),\"%U-%U\",\n                        id->ms,id->seq));\n}\n\n/* As a result of an explicit XCLAIM or XREADGROUP command, new entries\n * are created in the pending list of the stream and consumers. We need\n * to propagate this changes in the form of XCLAIM commands. */\nvoid streamPropagateXCLAIM(client *c, robj *key, streamCG *group, robj *groupname, robj *id, streamNACK *nack) {\n    /* We need to generate an XCLAIM that will work in a idempotent fashion:\n     *\n     * XCLAIM <key> <group> <consumer> 0 <id> TIME <milliseconds-unix-time>\n     *        RETRYCOUNT <count> FORCE JUSTID LASTID <id>.\n     *\n     * Note that JUSTID is useful in order to avoid that XCLAIM will do\n     * useless work in the slave side, trying to fetch the stream item. */\n    robj *argv[14];\n    argv[0] = createStringObject(\"XCLAIM\",6);\n    argv[1] = key;\n    argv[2] = groupname;\n    argv[3] = createStringObject(nack->consumer->name,sdslen(nack->consumer->name));\n    argv[4] = createStringObjectFromLongLong(0);\n    argv[5] = id;\n    argv[6] = createStringObject(\"TIME\",4);\n    argv[7] = createStringObjectFromLongLong(nack->delivery_time);\n    argv[8] = createStringObject(\"RETRYCOUNT\",10);\n    argv[9] = createStringObjectFromLongLong(nack->delivery_count);\n    argv[10] = createStringObject(\"FORCE\",5);\n    argv[11] = createStringObject(\"JUSTID\",6);\n    argv[12] = createStringObject(\"LASTID\",6);\n    argv[13] = createObjectFromStreamID(&group->last_id);\n\n    /* We use progagate() because this code path is not always called from\n     * the command execution context. Moreover this will just alter the\n     * consumer group state, and we don't need MULTI/EXEC wrapping because\n     * there is no message state cross-message atomicity required. */\n    propagate(server.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[3]);\n    decrRefCount(argv[4]);\n    decrRefCount(argv[6]);\n    decrRefCount(argv[7]);\n    decrRefCount(argv[8]);\n    decrRefCount(argv[9]);\n    decrRefCount(argv[10]);\n    decrRefCount(argv[11]);\n    decrRefCount(argv[12]);\n    decrRefCount(argv[13]);\n}\n\n/* We need this when we want to propoagate the new last-id of a consumer group\n * that was consumed by XREADGROUP with the NOACK option: in that case we can't\n * propagate the last ID just using the XCLAIM LASTID option, so we emit\n *\n *  XGROUP SETID <key> <groupname> <id>\n */\nvoid streamPropagateGroupID(client *c, robj *key, streamCG *group, robj *groupname) {\n    robj *argv[5];\n    argv[0] = createStringObject(\"XGROUP\",6);\n    argv[1] = createStringObject(\"SETID\",5);\n    argv[2] = key;\n    argv[3] = groupname;\n    argv[4] = createObjectFromStreamID(&group->last_id);\n\n    /* We use progagate() because this code path is not always called from\n     * the command execution context. Moreover this will just alter the\n     * consumer group state, and we don't need MULTI/EXEC wrapping because\n     * there is no message state cross-message atomicity required. */\n    propagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);\n    decrRefCount(argv[0]);\n    decrRefCount(argv[1]);\n    decrRefCount(argv[4]);\n}\n\n/* Send the stream items in the specified range to the client 'c'. The range\n * the client will receive is between start and end inclusive, if 'count' is\n * non zero, no more than 'count' elements are sent.\n *\n * The 'end' pointer can be NULL to mean that we want all the elements from\n * 'start' till the end of the stream. If 'rev' is non zero, elements are\n * produced in reversed order from end to start.\n *\n * The function returns the number of entries emitted.\n *\n * If group and consumer are not NULL, the function performs additional work:\n * 1. It updates the last delivered ID in the group in case we are\n *    sending IDs greater than the current last ID.\n * 2. If the requested IDs are already assigned to some other consumer, the\n *    function will not return it to the client.\n * 3. An entry in the pending list will be created for every entry delivered\n *    for the first time to this consumer.\n *\n * The behavior may be modified passing non-zero flags:\n *\n * STREAM_RWR_NOACK: Do not create PEL entries, that is, the point \"3\" above\n *                   is not performed.\n * STREAM_RWR_RAWENTRIES: Do not emit array boundaries, but just the entries,\n *                        and return the number of entries emitted as usually.\n *                        This is used when the function is just used in order\n *                        to emit data and there is some higher level logic.\n *\n * The final argument 'spi' (stream propagation info pointer) is a structure\n * filled with information needed to propagate the command execution to AOF\n * and slaves, in the case a consumer group was passed: we need to generate\n * XCLAIM commands to create the pending list into AOF/slaves in that case.\n *\n * If 'spi' is set to NULL no propagation will happen even if the group was\n * given, but currently such a feature is never used by the code base that\n * will always pass 'spi' and propagate when a group is passed.\n *\n * Note that this function is recursive in certain cases. When it's called\n * with a non NULL group and consumer argument, it may call\n * streamReplyWithRangeFromConsumerPEL() in order to get entries from the\n * consumer pending entries list. However such a function will then call\n * streamReplyWithRange() in order to emit single entries (found in the\n * PEL by ID) to the client. This is the use case for the STREAM_RWR_RAWENTRIES\n * flag.\n */\n#define STREAM_RWR_NOACK (1<<0)         /* Do not create entries in the PEL. */\n#define STREAM_RWR_RAWENTRIES (1<<1)    /* Do not emit protocol for array\n                                           boundaries, just the entries. */\n#define STREAM_RWR_HISTORY (1<<2)       /* Only serve consumer local PEL. */\nsize_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end, size_t count, int rev, streamCG *group, streamConsumer *consumer, int flags, streamPropInfo *spi) {\n    void *arraylen_ptr = NULL;\n    size_t arraylen = 0;\n    streamIterator si;\n    int64_t numfields;\n    streamID id;\n    int propagate_last_id = 0;\n    int noack = flags & STREAM_RWR_NOACK;\n\n    /* If the client is asking for some history, we serve it using a\n     * different function, so that we return entries *solely* from its\n     * own PEL. This ensures each consumer will always and only see\n     * the history of messages delivered to it and not yet confirmed\n     * as delivered. */\n    if (group && (flags & STREAM_RWR_HISTORY)) {\n        return streamReplyWithRangeFromConsumerPEL(c,s,start,end,count,\n                                                   consumer);\n    }\n\n    if (!(flags & STREAM_RWR_RAWENTRIES))\n        arraylen_ptr = addReplyDeferredLen(c);\n    streamIteratorStart(&si,s,start,end,rev);\n    while(streamIteratorGetID(&si,&id,&numfields)) {\n        /* Update the group last_id if needed. */\n        if (group && streamCompareID(&id,&group->last_id) > 0) {\n            group->last_id = id;\n            /* Group last ID should be propagated only if NOACK was\n             * specified, otherwise the last id will be included\n             * in the propagation of XCLAIM itself. */\n            if (noack) propagate_last_id = 1;\n        }\n\n        /* Emit a two elements array for each item. The first is\n         * the ID, the second is an array of field-value pairs. */\n        addReplyArrayLen(c,2);\n        addReplyStreamID(c,&id);\n\n        addReplyArrayLen(c,numfields*2);\n\n        /* Emit the field-value pairs. */\n        while(numfields--) {\n            unsigned char *key, *value;\n            int64_t key_len, value_len;\n            streamIteratorGetField(&si,&key,&value,&key_len,&value_len);\n            addReplyBulkCBuffer(c,key,key_len);\n            addReplyBulkCBuffer(c,value,value_len);\n        }\n\n        /* If a group is passed, we need to create an entry in the\n         * PEL (pending entries list) of this group *and* this consumer.\n         *\n         * Note that we cannot be sure about the fact the message is not\n         * already owned by another consumer, because the admin is able\n         * to change the consumer group last delivered ID using the\n         * XGROUP SETID command. So if we find that there is already\n         * a NACK for the entry, we need to associate it to the new\n         * consumer. */\n        if (group && !noack) {\n            unsigned char buf[sizeof(streamID)];\n            streamEncodeID(buf,&id);\n\n            /* Try to add a new NACK. Most of the time this will work and\n             * will not require extra lookups. We'll fix the problem later\n             * if we find that there is already a entry for this ID. */\n            streamNACK *nack = streamCreateNACK(consumer);\n            int group_inserted =\n                raxTryInsert(group->pel,buf,sizeof(buf),nack,NULL);\n            int consumer_inserted =\n                raxTryInsert(consumer->pel,buf,sizeof(buf),nack,NULL);\n\n            /* Now we can check if the entry was already busy, and\n             * in that case reassign the entry to the new consumer,\n             * or update it if the consumer is the same as before. */\n            if (group_inserted == 0) {\n                streamFreeNACK(nack);\n                nack = raxFind(group->pel,buf,sizeof(buf));\n                serverAssert(nack != raxNotFound);\n                raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);\n                /* Update the consumer and NACK metadata. */\n                nack->consumer = consumer;\n                nack->delivery_time = mstime();\n                nack->delivery_count = 1;\n                /* Add the entry in the new consumer local PEL. */\n                raxInsert(consumer->pel,buf,sizeof(buf),nack,NULL);\n            } else if (group_inserted == 1 && consumer_inserted == 0) {\n                serverPanic(\"NACK half-created. Should not be possible.\");\n            }\n\n            /* Propagate as XCLAIM. */\n            if (spi) {\n                robj *idarg = createObjectFromStreamID(&id);\n                streamPropagateXCLAIM(c,spi->keyname,group,spi->groupname,idarg,nack);\n                decrRefCount(idarg);\n            }\n        }\n\n        arraylen++;\n        if (count && count == arraylen) break;\n    }\n\n    if (spi && propagate_last_id)\n        streamPropagateGroupID(c,spi->keyname,group,spi->groupname);\n\n    streamIteratorStop(&si);\n    if (arraylen_ptr) setDeferredArrayLen(c,arraylen_ptr,arraylen);\n    return arraylen;\n}\n\n/* This is an helper function for streamReplyWithRange() when called with\n * group and consumer arguments, but with a range that is referring to already\n * delivered messages. In this case we just emit messages that are already\n * in the history of the consumer, fetching the IDs from its PEL.\n *\n * Note that this function does not have a 'rev' argument because it's not\n * possible to iterate in reverse using a group. Basically this function\n * is only called as a result of the XREADGROUP command.\n *\n * This function is more expensive because it needs to inspect the PEL and then\n * seek into the radix tree of the messages in order to emit the full message\n * to the client. However clients only reach this code path when they are\n * fetching the history of already retrieved messages, which is rare. */\nsize_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer) {\n    raxIterator ri;\n    unsigned char startkey[sizeof(streamID)];\n    unsigned char endkey[sizeof(streamID)];\n    streamEncodeID(startkey,start);\n    if (end) streamEncodeID(endkey,end);\n\n    size_t arraylen = 0;\n    void *arraylen_ptr = addReplyDeferredLen(c);\n    raxStart(&ri,consumer->pel);\n    raxSeek(&ri,\">=\",startkey,sizeof(startkey));\n    while(raxNext(&ri) && (!count || arraylen < count)) {\n        if (end && memcmp(ri.key,end,ri.key_len) > 0) break;\n        streamID thisid;\n        streamDecodeID(ri.key,&thisid);\n        if (streamReplyWithRange(c,s,&thisid,&thisid,1,0,NULL,NULL,\n                                 STREAM_RWR_RAWENTRIES,NULL) == 0)\n        {\n            /* Note that we may have a not acknowledged entry in the PEL\n             * about a message that's no longer here because was removed\n             * by the user by other means. In that case we signal it emitting\n             * the ID but then a NULL entry for the fields. */\n            addReplyArrayLen(c,2);\n            addReplyStreamID(c,&thisid);\n            addReplyNullArray(c);\n        } else {\n            streamNACK *nack = ri.data;\n            nack->delivery_time = mstime();\n            nack->delivery_count++;\n        }\n        arraylen++;\n    }\n    raxStop(&ri);\n    setDeferredArrayLen(c,arraylen_ptr,arraylen);\n    return arraylen;\n}\n\n/* -----------------------------------------------------------------------\n * Stream commands implementation\n * ----------------------------------------------------------------------- */\n\n/* Look the stream at 'key' and return the corresponding stream object.\n * The function creates a key setting it to an empty stream if needed. */\nrobj *streamTypeLookupWriteOrCreate(client *c, robj *key) {\n    robj *o = lookupKeyWrite(c->db,key);\n    if (o == NULL) {\n        o = createStreamObject();\n        dbAdd(c->db,key,o);\n    } else {\n        if (o->type != OBJ_STREAM) {\n            addReply(c,shared.wrongtypeerr);\n            return NULL;\n        }\n    }\n    return o;\n}\n\n/* Parse a stream ID in the format given by clients to Redis, that is\n * <ms>-<seq>, and converts it into a streamID structure. If\n * the specified ID is invalid C_ERR is returned and an error is reported\n * to the client, otherwise C_OK is returned. The ID may be in incomplete\n * form, just stating the milliseconds time part of the stream. In such a case\n * the missing part is set according to the value of 'missing_seq' parameter.\n *\n * The IDs \"-\" and \"+\" specify respectively the minimum and maximum IDs\n * that can be represented. If 'strict' is set to 1, \"-\" and \"+\" will be\n * treated as an invalid ID.\n *\n * If 'c' is set to NULL, no reply is sent to the client. */\nint streamGenericParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq, int strict) {\n    char buf[128];\n    if (sdslen(o->ptr) > sizeof(buf)-1) goto invalid;\n    memcpy(buf,o->ptr,sdslen(o->ptr)+1);\n\n    if (strict && (buf[0] == '-' || buf[0] == '+') && buf[1] == '\\0')\n        goto invalid;\n\n    /* Handle the \"-\" and \"+\" special cases. */\n    if (buf[0] == '-' && buf[1] == '\\0') {\n        id->ms = 0;\n        id->seq = 0;\n        return C_OK;\n    } else if (buf[0] == '+' && buf[1] == '\\0') {\n        id->ms = UINT64_MAX;\n        id->seq = UINT64_MAX;\n        return C_OK;\n    }\n\n    /* Parse <ms>-<seq> form. */\n    char *dot = strchr(buf,'-');\n    if (dot) *dot = '\\0';\n    unsigned long long ms, seq;\n    if (string2ull(buf,&ms) == 0) goto invalid;\n    if (dot && string2ull(dot+1,&seq) == 0) goto invalid;\n    if (!dot) seq = missing_seq;\n    id->ms = ms;\n    id->seq = seq;\n    return C_OK;\n\ninvalid:\n    if (c) addReplyError(c,\"Invalid stream ID specified as stream \"\n                           \"command argument\");\n    return C_ERR;\n}\n\n/* Wrapper for streamGenericParseIDOrReply() with 'strict' argument set to\n * 0, to be used when - and + are acceptable IDs. */\nint streamParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {\n    return streamGenericParseIDOrReply(c,o,id,missing_seq,0);\n}\n\n/* Wrapper for streamGenericParseIDOrReply() with 'strict' argument set to\n * 1, to be used when we want to return an error if the special IDs + or -\n * are provided. */\nint streamParseStrictIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {\n    return streamGenericParseIDOrReply(c,o,id,missing_seq,1);\n}\n\n/* We propagate MAXLEN ~ <count> as MAXLEN = <resulting-len-of-stream>\n * otherwise trimming is no longer determinsitic on replicas / AOF. */\nvoid streamRewriteApproxMaxlen(client *c, stream *s, int maxlen_arg_idx) {\n    robj *maxlen_obj = createStringObjectFromLongLong(s->length);\n    robj *equal_obj = createStringObject(\"=\",1);\n\n    rewriteClientCommandArgument(c,maxlen_arg_idx,maxlen_obj);\n    rewriteClientCommandArgument(c,maxlen_arg_idx-1,equal_obj);\n\n    decrRefCount(equal_obj);\n    decrRefCount(maxlen_obj);\n}\n\n/* XADD key [MAXLEN [~|=] <count>] <ID or *> [field value] [field value] ... */\nvoid xaddCommand(client *c) {\n    streamID id;\n    int id_given = 0; /* Was an ID different than \"*\" specified? */\n    long long maxlen = -1;  /* If left to -1 no trimming is performed. */\n    int approx_maxlen = 0;  /* If 1 only delete whole radix tree nodes, so\n                               the maxium length is not applied verbatim. */\n    int maxlen_arg_idx = 0; /* Index of the count in MAXLEN, for rewriting. */\n\n    /* Parse options. */\n    int i = 2; /* This is the first argument position where we could\n                  find an option, or the ID. */\n    for (; i < c->argc; i++) {\n        int moreargs = (c->argc-1) - i; /* Number of additional arguments. */\n        char *opt = c->argv[i]->ptr;\n        if (opt[0] == '*' && opt[1] == '\\0') {\n            /* This is just a fast path for the common case of auto-ID\n             * creation. */\n            break;\n        } else if (!strcasecmp(opt,\"maxlen\") && moreargs) {\n            approx_maxlen = 0;\n            char *next = c->argv[i+1]->ptr;\n            /* Check for the form MAXLEN ~ <count>. */\n            if (moreargs >= 2 && next[0] == '~' && next[1] == '\\0') {\n                approx_maxlen = 1;\n                i++;\n            } else if (moreargs >= 2 && next[0] == '=' && next[1] == '\\0') {\n                i++;\n            }\n            if (getLongLongFromObjectOrReply(c,c->argv[i+1],&maxlen,NULL)\n                != C_OK) return;\n\n            if (maxlen < 0) {\n                addReplyError(c,\"The MAXLEN argument must be >= 0.\");\n                return;\n            }\n            i++;\n            maxlen_arg_idx = i;\n        } else {\n            /* If we are here is a syntax error or a valid ID. */\n            if (streamParseStrictIDOrReply(c,c->argv[i],&id,0) != C_OK) return;\n            id_given = 1;\n            break;\n        }\n    }\n    int field_pos = i+1;\n\n    /* Check arity. */\n    if ((c->argc - field_pos) < 2 || ((c->argc-field_pos) % 2) == 1) {\n        addReplyError(c,\"wrong number of arguments for XADD\");\n        return;\n    }\n\n    /* Return ASAP if minimal ID (0-0) was given so we avoid possibly creating\n     * a new stream and have streamAppendItem fail, leaving an empty key in the\n     * database. */\n    if (id_given && id.ms == 0 && id.seq == 0) {\n        addReplyError(c,\"The ID specified in XADD must be greater than 0-0\");\n        return;\n    }\n\n    /* Lookup the stream at key. */\n    robj *o;\n    stream *s;\n    if ((o = streamTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;\n    s = o->ptr;\n\n    /* Return ASAP if the stream has reached the last possible ID */\n    if (s->last_id.ms == UINT64_MAX && s->last_id.seq == UINT64_MAX) {\n        addReplyError(c,\"The stream has exhausted the last possible ID, \"\n                        \"unable to add more items\");\n        return;\n    }\n\n    /* Append using the low level function and return the ID. */\n    if (streamAppendItem(s,c->argv+field_pos,(c->argc-field_pos)/2,\n        &id, id_given ? &id : NULL)\n        == C_ERR)\n    {\n        addReplyError(c,\"The ID specified in XADD is equal or smaller than the \"\n                        \"target stream top item\");\n        return;\n    }\n    addReplyStreamID(c,&id);\n\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STREAM,\"xadd\",c->argv[1],c->db->id);\n    server.dirty++;\n\n    if (maxlen >= 0) {\n        /* Notify xtrim event if needed. */\n        if (streamTrimByLength(s,maxlen,approx_maxlen)) {\n            notifyKeyspaceEvent(NOTIFY_STREAM,\"xtrim\",c->argv[1],c->db->id);\n        }\n        if (approx_maxlen) streamRewriteApproxMaxlen(c,s,maxlen_arg_idx);\n    }\n\n    /* Let's rewrite the ID argument with the one actually generated for\n     * AOF/replication propagation. */\n    robj *idarg = createObjectFromStreamID(&id);\n    rewriteClientCommandArgument(c,i,idarg);\n    decrRefCount(idarg);\n\n    /* We need to signal to blocked clients that there is new data on this\n     * stream. */\n    if (server.blocked_clients_by_type[BLOCKED_STREAM])\n        signalKeyAsReady(c->db, c->argv[1]);\n}\n\n/* XRANGE/XREVRANGE actual implementation. */\nvoid xrangeGenericCommand(client *c, int rev) {\n    robj *o;\n    stream *s;\n    streamID startid, endid;\n    long long count = -1;\n    robj *startarg = rev ? c->argv[3] : c->argv[2];\n    robj *endarg = rev ? c->argv[2] : c->argv[3];\n\n    if (streamParseIDOrReply(c,startarg,&startid,0) == C_ERR) return;\n    if (streamParseIDOrReply(c,endarg,&endid,UINT64_MAX) == C_ERR) return;\n\n    /* Parse the COUNT option if any. */\n    if (c->argc > 4) {\n        for (int j = 4; j < c->argc; j++) {\n            int additional = c->argc-j-1;\n            if (strcasecmp(c->argv[j]->ptr,\"COUNT\") == 0 && additional >= 1) {\n                if (getLongLongFromObjectOrReply(c,c->argv[j+1],&count,NULL)\n                    != C_OK) return;\n                if (count < 0) count = 0;\n                j++; /* Consume additional arg. */\n            } else {\n                addReply(c,shared.syntaxerr);\n                return;\n            }\n        }\n    }\n\n    /* Return the specified range to the user. */\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == NULL ||\n         checkType(c,o,OBJ_STREAM)) return;\n\n    s = o->ptr;\n\n    if (count == 0) {\n        addReplyNullArray(c);\n    } else {\n        if (count == -1) count = 0;\n        streamReplyWithRange(c,s,&startid,&endid,count,rev,NULL,NULL,0,NULL);\n    }\n}\n\n/* XRANGE key start end [COUNT <n>] */\nvoid xrangeCommand(client *c) {\n    xrangeGenericCommand(c,0);\n}\n\n/* XREVRANGE key end start [COUNT <n>] */\nvoid xrevrangeCommand(client *c) {\n    xrangeGenericCommand(c,1);\n}\n\n/* XLEN */\nvoid xlenCommand(client *c) {\n    robj *o;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL\n        || checkType(c,o,OBJ_STREAM)) return;\n    stream *s = o->ptr;\n    addReplyLongLong(c,s->length);\n}\n\n/* XREAD [BLOCK <milliseconds>] [COUNT <count>] STREAMS key_1 key_2 ... key_N\n *       ID_1 ID_2 ... ID_N\n *\n * This function also implements the XREAD-GROUP command, which is like XREAD\n * but accepting the [GROUP group-name consumer-name] additional option.\n * This is useful because while XREAD is a read command and can be called\n * on slaves, XREAD-GROUP is not. */\n#define XREAD_BLOCKED_DEFAULT_COUNT 1000\nvoid xreadCommand(client *c) {\n    long long timeout = -1; /* -1 means, no BLOCK argument given. */\n    long long count = 0;\n    int streams_count = 0;\n    int streams_arg = 0;\n    int noack = 0;          /* True if NOACK option was specified. */\n    #define STREAMID_STATIC_VECTOR_LEN 8\n    streamID static_ids[STREAMID_STATIC_VECTOR_LEN];\n    streamID *ids = static_ids;\n    streamCG **groups = NULL;\n    int xreadgroup = sdslen(c->argv[0]->ptr) == 10; /* XREAD or XREADGROUP? */\n    robj *groupname = NULL;\n    robj *consumername = NULL;\n\n    /* Parse arguments. */\n    for (int i = 1; i < c->argc; i++) {\n        int moreargs = c->argc-i-1;\n        char *o = c->argv[i]->ptr;\n        if (!strcasecmp(o,\"BLOCK\") && moreargs) {\n            if (c->flags & CLIENT_LUA) {\n                /* There is no sense to use BLOCK option within LUA */\n                addReplyErrorFormat(c, \"%s command is not allowed with BLOCK option from scripts\", (char *)c->argv[0]->ptr);\n                return;\n            }\n            i++;\n            if (getTimeoutFromObjectOrReply(c,c->argv[i],&timeout,\n                UNIT_MILLISECONDS) != C_OK) return;\n        } else if (!strcasecmp(o,\"COUNT\") && moreargs) {\n            i++;\n            if (getLongLongFromObjectOrReply(c,c->argv[i],&count,NULL) != C_OK)\n                return;\n            if (count < 0) count = 0;\n        } else if (!strcasecmp(o,\"STREAMS\") && moreargs) {\n            streams_arg = i+1;\n            streams_count = (c->argc-streams_arg);\n            if ((streams_count % 2) != 0) {\n                addReplyError(c,\"Unbalanced XREAD list of streams: \"\n                                \"for each stream key an ID or '$' must be \"\n                                \"specified.\");\n                return;\n            }\n            streams_count /= 2; /* We have two arguments for each stream. */\n            break;\n        } else if (!strcasecmp(o,\"GROUP\") && moreargs >= 2) {\n            if (!xreadgroup) {\n                addReplyError(c,\"The GROUP option is only supported by \"\n                                \"XREADGROUP. You called XREAD instead.\");\n                return;\n            }\n            groupname = c->argv[i+1];\n            consumername = c->argv[i+2];\n            i += 2;\n        } else if (!strcasecmp(o,\"NOACK\")) {\n            if (!xreadgroup) {\n                addReplyError(c,\"The NOACK option is only supported by \"\n                                \"XREADGROUP. You called XREAD instead.\");\n                return;\n            }\n            noack = 1;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* STREAMS option is mandatory. */\n    if (streams_arg == 0) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* If the user specified XREADGROUP then it must also\n     * provide the GROUP option. */\n    if (xreadgroup && groupname == NULL) {\n        addReplyError(c,\"Missing GROUP option for XREADGROUP\");\n        return;\n    }\n\n    /* Parse the IDs and resolve the group name. */\n    if (streams_count > STREAMID_STATIC_VECTOR_LEN)\n        ids = zmalloc(sizeof(streamID)*streams_count);\n    if (groupname) groups = zmalloc(sizeof(streamCG*)*streams_count);\n\n    for (int i = streams_arg + streams_count; i < c->argc; i++) {\n        /* Specifying \"$\" as last-known-id means that the client wants to be\n         * served with just the messages that will arrive into the stream\n         * starting from now. */\n        int id_idx = i - streams_arg - streams_count;\n        robj *key = c->argv[i-streams_count];\n        robj *o = lookupKeyRead(c->db,key);\n        if (o && checkType(c,o,OBJ_STREAM)) goto cleanup;\n        streamCG *group = NULL;\n\n        /* If a group was specified, than we need to be sure that the\n         * key and group actually exist. */\n        if (groupname) {\n            if (o == NULL ||\n                (group = streamLookupCG(o->ptr,groupname->ptr)) == NULL)\n            {\n                addReplyErrorFormat(c, \"-NOGROUP No such key '%s' or consumer \"\n                                       \"group '%s' in XREADGROUP with GROUP \"\n                                       \"option\",\n                                    (char*)key->ptr,(char*)groupname->ptr);\n                goto cleanup;\n            }\n            groups[id_idx] = group;\n        }\n\n        if (strcmp(c->argv[i]->ptr,\"$\") == 0) {\n            if (xreadgroup) {\n                addReplyError(c,\"The $ ID is meaningless in the context of \"\n                                \"XREADGROUP: you want to read the history of \"\n                                \"this consumer by specifying a proper ID, or \"\n                                \"use the > ID to get new messages. The $ ID would \"\n                                \"just return an empty result set.\");\n                goto cleanup;\n            }\n            if (o) {\n                stream *s = o->ptr;\n                ids[id_idx] = s->last_id;\n            } else {\n                ids[id_idx].ms = 0;\n                ids[id_idx].seq = 0;\n            }\n            continue;\n        } else if (strcmp(c->argv[i]->ptr,\">\") == 0) {\n            if (!xreadgroup) {\n                addReplyError(c,\"The > ID can be specified only when calling \"\n                                \"XREADGROUP using the GROUP <group> \"\n                                \"<consumer> option.\");\n                goto cleanup;\n            }\n            /* We use just the maximum ID to signal this is a \">\" ID, anyway\n             * the code handling the blocking clients will have to update the\n             * ID later in order to match the changing consumer group last ID. */\n            ids[id_idx].ms = UINT64_MAX;\n            ids[id_idx].seq = UINT64_MAX;\n            continue;\n        }\n        if (streamParseStrictIDOrReply(c,c->argv[i],ids+id_idx,0) != C_OK)\n            goto cleanup;\n    }\n\n    /* Try to serve the client synchronously. */\n    size_t arraylen = 0;\n    void *arraylen_ptr = NULL;\n    for (int i = 0; i < streams_count; i++) {\n        robj *o = lookupKeyRead(c->db,c->argv[streams_arg+i]);\n        if (o == NULL) continue;\n        stream *s = o->ptr;\n        streamID *gt = ids+i; /* ID must be greater than this. */\n        int serve_synchronously = 0;\n        int serve_history = 0; /* True for XREADGROUP with ID != \">\". */\n\n        /* Check if there are the conditions to serve the client\n         * synchronously. */\n        if (groups) {\n            /* If the consumer is blocked on a group, we always serve it\n             * synchronously (serving its local history) if the ID specified\n             * was not the special \">\" ID. */\n            if (gt->ms != UINT64_MAX ||\n                gt->seq != UINT64_MAX)\n            {\n                serve_synchronously = 1;\n                serve_history = 1;\n            } else if (s->length) {\n                /* We also want to serve a consumer in a consumer group\n                 * synchronously in case the group top item delivered is smaller\n                 * than what the stream has inside. */\n                streamID maxid, *last = &groups[i]->last_id;\n                streamLastValidID(s, &maxid);\n                if (streamCompareID(&maxid, last) > 0) {\n                    serve_synchronously = 1;\n                    *gt = *last;\n                }\n            }\n        } else if (s->length) {\n            /* For consumers without a group, we serve synchronously if we can\n             * actually provide at least one item from the stream. */\n            streamID maxid;\n            streamLastValidID(s, &maxid);\n            if (streamCompareID(&maxid, gt) > 0) {\n                serve_synchronously = 1;\n            }\n        }\n\n        if (serve_synchronously) {\n            arraylen++;\n            if (arraylen == 1) arraylen_ptr = addReplyDeferredLen(c);\n            /* streamReplyWithRange() handles the 'start' ID as inclusive,\n             * so start from the next ID, since we want only messages with\n             * IDs greater than start. */\n            streamID start = *gt;\n            streamIncrID(&start);\n\n            /* Emit the two elements sub-array consisting of the name\n             * of the stream and the data we extracted from it. */\n            if (c->resp == 2) addReplyArrayLen(c,2);\n            addReplyBulk(c,c->argv[streams_arg+i]);\n            streamConsumer *consumer = NULL;\n            if (groups) consumer = streamLookupConsumer(groups[i],\n                                                        consumername->ptr,\n                                                        SLC_NONE);\n            streamPropInfo spi = {c->argv[i+streams_arg],groupname};\n            int flags = 0;\n            if (noack) flags |= STREAM_RWR_NOACK;\n            if (serve_history) flags |= STREAM_RWR_HISTORY;\n            streamReplyWithRange(c,s,&start,NULL,count,0,\n                                 groups ? groups[i] : NULL,\n                                 consumer, flags, &spi);\n            if (groups) server.dirty++;\n        }\n    }\n\n     /* We replied synchronously! Set the top array len and return to caller. */\n    if (arraylen) {\n        if (c->resp == 2)\n            setDeferredArrayLen(c,arraylen_ptr,arraylen);\n        else\n            setDeferredMapLen(c,arraylen_ptr,arraylen);\n        goto cleanup;\n    }\n\n    /* Block if needed. */\n    if (timeout != -1) {\n        /* If we are inside a MULTI/EXEC and the list is empty the only thing\n         * we can do is treating it as a timeout (even with timeout 0). */\n        if (c->flags & CLIENT_MULTI) {\n            addReplyNullArray(c);\n            goto cleanup;\n        }\n        blockForKeys(c, BLOCKED_STREAM, c->argv+streams_arg, streams_count,\n                     timeout, NULL, ids);\n        /* If no COUNT is given and we block, set a relatively small count:\n         * in case the ID provided is too low, we do not want the server to\n         * block just to serve this client a huge stream of messages. */\n        c->bpop.xread_count = count ? count : XREAD_BLOCKED_DEFAULT_COUNT;\n\n        /* If this is a XREADGROUP + GROUP we need to remember for which\n         * group and consumer name we are blocking, so later when one of the\n         * keys receive more data, we can call streamReplyWithRange() passing\n         * the right arguments. */\n        if (groupname) {\n            incrRefCount(groupname);\n            incrRefCount(consumername);\n            c->bpop.xread_group = groupname;\n            c->bpop.xread_consumer = consumername;\n            c->bpop.xread_group_noack = noack;\n        } else {\n            c->bpop.xread_group = NULL;\n            c->bpop.xread_consumer = NULL;\n        }\n        goto cleanup;\n    }\n\n    /* No BLOCK option, nor any stream we can serve. Reply as with a\n     * timeout happened. */\n    addReplyNullArray(c);\n    /* Continue to cleanup... */\n\ncleanup: /* Cleanup. */\n\n    /* The command is propagated (in the READGROUP form) as a side effect\n     * of calling lower level APIs. So stop any implicit propagation. */\n    preventCommandPropagation(c);\n    if (ids != static_ids) zfree(ids);\n    zfree(groups);\n}\n\n/* -----------------------------------------------------------------------\n * Low level implementation of consumer groups\n * ----------------------------------------------------------------------- */\n\n/* Create a NACK entry setting the delivery count to 1 and the delivery\n * time to the current time. The NACK consumer will be set to the one\n * specified as argument of the function. */\nstreamNACK *streamCreateNACK(streamConsumer *consumer) {\n    streamNACK *nack = zmalloc(sizeof(*nack));\n    nack->delivery_time = mstime();\n    nack->delivery_count = 1;\n    nack->consumer = consumer;\n    return nack;\n}\n\n/* Free a NACK entry. */\nvoid streamFreeNACK(streamNACK *na) {\n    zfree(na);\n}\n\n/* Free a consumer and associated data structures. Note that this function\n * will not reassign the pending messages associated with this consumer\n * nor will delete them from the stream, so when this function is called\n * to delete a consumer, and not when the whole stream is destroyed, the caller\n * should do some work before. */\nvoid streamFreeConsumer(streamConsumer *sc) {\n    raxFree(sc->pel); /* No value free callback: the PEL entries are shared\n                         between the consumer and the main stream PEL. */\n    sdsfree(sc->name);\n    zfree(sc);\n}\n\n/* Create a new consumer group in the context of the stream 's', having the\n * specified name and last server ID. If a consumer group with the same name\n * already existed NULL is returned, otherwise the pointer to the consumer\n * group is returned. */\nstreamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id) {\n    if (s->cgroups == NULL) s->cgroups = raxNew();\n    if (raxFind(s->cgroups,(unsigned char*)name,namelen) != raxNotFound)\n        return NULL;\n\n    streamCG *cg = zmalloc(sizeof(*cg));\n    cg->pel = raxNew();\n    cg->consumers = raxNew();\n    cg->last_id = *id;\n    raxInsert(s->cgroups,(unsigned char*)name,namelen,cg,NULL);\n    return cg;\n}\n\n/* Free a consumer group and all its associated data. */\nvoid streamFreeCG(streamCG *cg) {\n    raxFreeWithCallback(cg->pel,(void(*)(void*))streamFreeNACK);\n    raxFreeWithCallback(cg->consumers,(void(*)(void*))streamFreeConsumer);\n    zfree(cg);\n}\n\n/* Lookup the consumer group in the specified stream and returns its\n * pointer, otherwise if there is no such group, NULL is returned. */\nstreamCG *streamLookupCG(stream *s, sds groupname) {\n    if (s->cgroups == NULL) return NULL;\n    streamCG *cg = raxFind(s->cgroups,(unsigned char*)groupname,\n                           sdslen(groupname));\n    return (cg == raxNotFound) ? NULL : cg;\n}\n\n/* Lookup the consumer with the specified name in the group 'cg': if the\n * consumer does not exist it is automatically created as a side effect\n * of calling this function, otherwise its last seen time is updated and\n * the existing consumer reference returned. */\nstreamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags) {\n    int create = !(flags & SLC_NOCREAT);\n    int refresh = !(flags & SLC_NOREFRESH);\n    streamConsumer *consumer = raxFind(cg->consumers,(unsigned char*)name,\n                               sdslen(name));\n    if (consumer == raxNotFound) {\n        if (!create) return NULL;\n        consumer = zmalloc(sizeof(*consumer));\n        consumer->name = sdsdup(name);\n        consumer->pel = raxNew();\n        raxInsert(cg->consumers,(unsigned char*)name,sdslen(name),\n                  consumer,NULL);\n    }\n    if (refresh) consumer->seen_time = mstime();\n    return consumer;\n}\n\n/* Delete the consumer specified in the consumer group 'cg'. The consumer\n * may have pending messages: they are removed from the PEL, and the number\n * of pending messages \"lost\" is returned. */\nuint64_t streamDelConsumer(streamCG *cg, sds name) {\n    streamConsumer *consumer =\n        streamLookupConsumer(cg,name,SLC_NOCREAT|SLC_NOREFRESH);\n    if (consumer == NULL) return 0;\n\n    uint64_t retval = raxSize(consumer->pel);\n\n    /* Iterate all the consumer pending messages, deleting every corresponding\n     * entry from the global entry. */\n    raxIterator ri;\n    raxStart(&ri,consumer->pel);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        streamNACK *nack = ri.data;\n        raxRemove(cg->pel,ri.key,ri.key_len,NULL);\n        streamFreeNACK(nack);\n    }\n    raxStop(&ri);\n\n    /* Deallocate the consumer. */\n    raxRemove(cg->consumers,(unsigned char*)name,sdslen(name),NULL);\n    streamFreeConsumer(consumer);\n    return retval;\n}\n\n/* -----------------------------------------------------------------------\n * Consumer groups commands\n * ----------------------------------------------------------------------- */\n\n/* XGROUP CREATE <key> <groupname> <id or $> [MKSTREAM]\n * XGROUP SETID <key> <groupname> <id or $>\n * XGROUP DESTROY <key> <groupname>\n * XGROUP DELCONSUMER <key> <groupname> <consumername> */\nvoid xgroupCommand(client *c) {\n    const char *help[] = {\n\"CREATE      <key> <groupname> <id or $> [opt] -- Create a new consumer group.\",\n\"            option MKSTREAM: create the empty stream if it does not exist.\",\n\"SETID       <key> <groupname> <id or $>  -- Set the current group ID.\",\n\"DESTROY     <key> <groupname>            -- Remove the specified group.\",\n\"DELCONSUMER <key> <groupname> <consumer> -- Remove the specified consumer.\",\n\"HELP                                     -- Prints this help.\",\nNULL\n    };\n    stream *s = NULL;\n    sds grpname = NULL;\n    streamCG *cg = NULL;\n    char *opt = c->argv[1]->ptr; /* Subcommand name. */\n    int mkstream = 0;\n    robj *o;\n\n    /* CREATE has an MKSTREAM option that creates the stream if it\n     * does not exist. */\n    if (c->argc == 6 && !strcasecmp(opt,\"CREATE\")) {\n        if (strcasecmp(c->argv[5]->ptr,\"MKSTREAM\")) {\n            addReplySubcommandSyntaxError(c);\n            return;\n        }\n        mkstream = 1;\n        grpname = c->argv[3]->ptr;\n    }\n\n    /* Everything but the \"HELP\" option requires a key and group name. */\n    if (c->argc >= 4) {\n        o = lookupKeyWrite(c->db,c->argv[2]);\n        if (o) {\n            if (checkType(c,o,OBJ_STREAM)) return;\n            s = o->ptr;\n        }\n        grpname = c->argv[3]->ptr;\n    }\n\n    /* Check for missing key/group. */\n    if (c->argc >= 4 && !mkstream) {\n        /* At this point key must exist, or there is an error. */\n        if (s == NULL) {\n            addReplyError(c,\n                \"The XGROUP subcommand requires the key to exist. \"\n                \"Note that for CREATE you may want to use the MKSTREAM \"\n                \"option to create an empty stream automatically.\");\n            return;\n        }\n\n        /* Certain subcommands require the group to exist. */\n        if ((cg = streamLookupCG(s,grpname)) == NULL &&\n            (!strcasecmp(opt,\"SETID\") ||\n             !strcasecmp(opt,\"DELCONSUMER\")))\n        {\n            addReplyErrorFormat(c, \"-NOGROUP No such consumer group '%s' \"\n                                   \"for key name '%s'\",\n                                   (char*)grpname, (char*)c->argv[2]->ptr);\n            return;\n        }\n    }\n\n    /* Dispatch the different subcommands. */\n    if (!strcasecmp(opt,\"CREATE\") && (c->argc == 5 || c->argc == 6)) {\n        streamID id;\n        if (!strcmp(c->argv[4]->ptr,\"$\")) {\n            if (s) {\n                id = s->last_id;\n            } else {\n                id.ms = 0;\n                id.seq = 0;\n            }\n        } else if (streamParseStrictIDOrReply(c,c->argv[4],&id,0) != C_OK) {\n            return;\n        }\n\n        /* Handle the MKSTREAM option now that the command can no longer fail. */\n        if (s == NULL) {\n            serverAssert(mkstream);\n            o = createStreamObject();\n            dbAdd(c->db,c->argv[2],o);\n            s = o->ptr;\n        }\n\n        streamCG *cg = streamCreateCG(s,grpname,sdslen(grpname),&id);\n        if (cg) {\n            addReply(c,shared.ok);\n            server.dirty++;\n            notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-create\",\n                                c->argv[2],c->db->id);\n        } else {\n            addReplySds(c,\n                sdsnew(\"-BUSYGROUP Consumer Group name already exists\\r\\n\"));\n        }\n    } else if (!strcasecmp(opt,\"SETID\") && c->argc == 5) {\n        streamID id;\n        if (!strcmp(c->argv[4]->ptr,\"$\")) {\n            id = s->last_id;\n        } else if (streamParseIDOrReply(c,c->argv[4],&id,0) != C_OK) {\n            return;\n        }\n        cg->last_id = id;\n        addReply(c,shared.ok);\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-setid\",c->argv[2],c->db->id);\n    } else if (!strcasecmp(opt,\"DESTROY\") && c->argc == 4) {\n        if (cg) {\n            raxRemove(s->cgroups,(unsigned char*)grpname,sdslen(grpname),NULL);\n            streamFreeCG(cg);\n            addReply(c,shared.cone);\n            server.dirty++;\n            notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-destroy\",\n                                c->argv[2],c->db->id);\n            /* We want to unblock any XREADGROUP consumers with -NOGROUP. */\n            signalKeyAsReady(c->db,c->argv[2]);\n        } else {\n            addReply(c,shared.czero);\n        }\n    } else if (!strcasecmp(opt,\"DELCONSUMER\") && c->argc == 5) {\n        /* Delete the consumer and returns the number of pending messages\n         * that were yet associated with such a consumer. */\n        long long pending = streamDelConsumer(cg,c->argv[4]->ptr);\n        addReplyLongLong(c,pending);\n        server.dirty++;\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xgroup-delconsumer\",\n                            c->argv[2],c->db->id);\n    } else if (!strcasecmp(opt,\"HELP\")) {\n        addReplyHelp(c, help);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n\n/* XSETID <stream> <groupname> <id>\n *\n * Set the internal \"last ID\" of a stream. */\nvoid xsetidCommand(client *c) {\n    robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr);\n    if (o == NULL || checkType(c,o,OBJ_STREAM)) return;\n\n    stream *s = o->ptr;\n    streamID id;\n    if (streamParseStrictIDOrReply(c,c->argv[2],&id,0) != C_OK) return;\n\n    /* If the stream has at least one item, we want to check that the user\n     * is setting a last ID that is equal or greater than the current top\n     * item, otherwise the fundamental ID monotonicity assumption is violated. */\n    if (s->length > 0) {\n        streamID maxid;\n        streamLastValidID(s,&maxid);\n\n        if (streamCompareID(&id,&maxid) < 0) {\n            addReplyError(c,\"The ID specified in XSETID is smaller than the \"\n                            \"target stream top item\");\n            return;\n        }\n    }\n    s->last_id = id;\n    addReply(c,shared.ok);\n    server.dirty++;\n    notifyKeyspaceEvent(NOTIFY_STREAM,\"xsetid\",c->argv[1],c->db->id);\n}\n\n/* XACK <key> <group> <id> <id> ... <id>\n *\n * Acknowledge a message as processed. In practical terms we just check the\n * pendine entries list (PEL) of the group, and delete the PEL entry both from\n * the group and the consumer (pending messages are referenced in both places).\n *\n * Return value of the command is the number of messages successfully\n * acknowledged, that is, the IDs we were actually able to resolve in the PEL.\n */\nvoid xackCommand(client *c) {\n    streamCG *group = NULL;\n    robj *o = lookupKeyRead(c->db,c->argv[1]);\n    if (o) {\n        if (checkType(c,o,OBJ_STREAM)) return; /* Type error. */\n        group = streamLookupCG(o->ptr,c->argv[2]->ptr);\n    }\n\n    /* No key or group? Nothing to ack. */\n    if (o == NULL || group == NULL) {\n        addReply(c,shared.czero);\n        return;\n    }\n\n    /* Start parsing the IDs, so that we abort ASAP if there is a syntax\n     * error: the return value of this command cannot be an error in case\n     * the client successfully acknowledged some messages, so it should be\n     * executed in a \"all or nothing\" fashion. */\n    for (int j = 3; j < c->argc; j++) {\n        streamID id;\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;\n    }\n\n    int acknowledged = 0;\n    for (int j = 3; j < c->argc; j++) {\n        streamID id;\n        unsigned char buf[sizeof(streamID)];\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK)\n            serverPanic(\"StreamID invalid after check. Should not be possible.\");\n        streamEncodeID(buf,&id);\n\n        /* Lookup the ID in the group PEL: it will have a reference to the\n         * NACK structure that will have a reference to the consumer, so that\n         * we are able to remove the entry from both PELs. */\n        streamNACK *nack = raxFind(group->pel,buf,sizeof(buf));\n        if (nack != raxNotFound) {\n            raxRemove(group->pel,buf,sizeof(buf),NULL);\n            raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);\n            streamFreeNACK(nack);\n            acknowledged++;\n            server.dirty++;\n        }\n    }\n    addReplyLongLong(c,acknowledged);\n}\n\n/* XPENDING <key> <group> [<start> <stop> <count> [<consumer>]]\n *\n * If start and stop are omitted, the command just outputs information about\n * the amount of pending messages for the key/group pair, together with\n * the minimum and maxium ID of pending messages.\n *\n * If start and stop are provided instead, the pending messages are returned\n * with informations about the current owner, number of deliveries and last\n * delivery time and so forth. */\nvoid xpendingCommand(client *c) {\n    int justinfo = c->argc == 3; /* Without the range just outputs general\n                                    informations about the PEL. */\n    robj *key = c->argv[1];\n    robj *groupname = c->argv[2];\n    robj *consumername = (c->argc == 7) ? c->argv[6] : NULL;\n    streamID startid, endid;\n    long long count;\n\n    /* Start and stop, and the consumer, can be omitted. */\n    if (c->argc != 3 && c->argc != 6 && c->argc != 7) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* Parse start/end/count arguments ASAP if needed, in order to report\n     * syntax errors before any other error. */\n    if (c->argc >= 6) {\n        if (getLongLongFromObjectOrReply(c,c->argv[5],&count,NULL) == C_ERR)\n            return;\n        if (count < 0) count = 0;\n        if (streamParseIDOrReply(c,c->argv[3],&startid,0) == C_ERR)\n            return;\n        if (streamParseIDOrReply(c,c->argv[4],&endid,UINT64_MAX) == C_ERR)\n            return;\n    }\n\n    /* Lookup the key and the group inside the stream. */\n    robj *o = lookupKeyRead(c->db,c->argv[1]);\n    streamCG *group;\n\n    if (o && checkType(c,o,OBJ_STREAM)) return;\n    if (o == NULL ||\n        (group = streamLookupCG(o->ptr,groupname->ptr)) == NULL)\n    {\n        addReplyErrorFormat(c, \"-NOGROUP No such key '%s' or consumer \"\n                               \"group '%s'\",\n                               (char*)key->ptr,(char*)groupname->ptr);\n        return;\n    }\n\n    /* XPENDING <key> <group> variant. */\n    if (justinfo) {\n        addReplyArrayLen(c,4);\n        /* Total number of messages in the PEL. */\n        addReplyLongLong(c,raxSize(group->pel));\n        /* First and last IDs. */\n        if (raxSize(group->pel) == 0) {\n            addReplyNull(c); /* Start. */\n            addReplyNull(c); /* End. */\n            addReplyNullArray(c); /* Clients. */\n        } else {\n            /* Start. */\n            raxIterator ri;\n            raxStart(&ri,group->pel);\n            raxSeek(&ri,\"^\",NULL,0);\n            raxNext(&ri);\n            streamDecodeID(ri.key,&startid);\n            addReplyStreamID(c,&startid);\n\n            /* End. */\n            raxSeek(&ri,\"$\",NULL,0);\n            raxNext(&ri);\n            streamDecodeID(ri.key,&endid);\n            addReplyStreamID(c,&endid);\n            raxStop(&ri);\n\n            /* Consumers with pending messages. */\n            raxStart(&ri,group->consumers);\n            raxSeek(&ri,\"^\",NULL,0);\n            void *arraylen_ptr = addReplyDeferredLen(c);\n            size_t arraylen = 0;\n            while(raxNext(&ri)) {\n                streamConsumer *consumer = ri.data;\n                if (raxSize(consumer->pel) == 0) continue;\n                addReplyArrayLen(c,2);\n                addReplyBulkCBuffer(c,ri.key,ri.key_len);\n                addReplyBulkLongLong(c,raxSize(consumer->pel));\n                arraylen++;\n            }\n            setDeferredArrayLen(c,arraylen_ptr,arraylen);\n            raxStop(&ri);\n        }\n    }\n    /* XPENDING <key> <group> <start> <stop> <count> [<consumer>] variant. */\n    else {\n        streamConsumer *consumer = NULL;\n        if (consumername) {\n            consumer = streamLookupConsumer(group,\n                                            consumername->ptr,\n                                            SLC_NOCREAT|SLC_NOREFRESH);\n\n            /* If a consumer name was mentioned but it does not exist, we can\n             * just return an empty array. */\n            if (consumer == NULL) {\n                addReplyArrayLen(c,0);\n                return;\n            }\n        }\n\n        rax *pel = consumer ? consumer->pel : group->pel;\n        unsigned char startkey[sizeof(streamID)];\n        unsigned char endkey[sizeof(streamID)];\n        raxIterator ri;\n        mstime_t now = mstime();\n\n        streamEncodeID(startkey,&startid);\n        streamEncodeID(endkey,&endid);\n        raxStart(&ri,pel);\n        raxSeek(&ri,\">=\",startkey,sizeof(startkey));\n        void *arraylen_ptr = addReplyDeferredLen(c);\n        size_t arraylen = 0;\n\n        while(count && raxNext(&ri) && memcmp(ri.key,endkey,ri.key_len) <= 0) {\n            streamNACK *nack = ri.data;\n\n            arraylen++;\n            count--;\n            addReplyArrayLen(c,4);\n\n            /* Entry ID. */\n            streamID id;\n            streamDecodeID(ri.key,&id);\n            addReplyStreamID(c,&id);\n\n            /* Consumer name. */\n            addReplyBulkCBuffer(c,nack->consumer->name,\n                                sdslen(nack->consumer->name));\n\n            /* Milliseconds elapsed since last delivery. */\n            mstime_t elapsed = now - nack->delivery_time;\n            if (elapsed < 0) elapsed = 0;\n            addReplyLongLong(c,elapsed);\n\n            /* Number of deliveries. */\n            addReplyLongLong(c,nack->delivery_count);\n        }\n        raxStop(&ri);\n        setDeferredArrayLen(c,arraylen_ptr,arraylen);\n    }\n}\n\n/* XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2>\n *        [IDLE <milliseconds>] [TIME <mstime>] [RETRYCOUNT <count>]\n *        [FORCE] [JUSTID]\n *\n * Gets ownership of one or multiple messages in the Pending Entries List\n * of a given stream consumer group.\n *\n * If the message ID (among the specified ones) exists, and its idle\n * time greater or equal to <min-idle-time>, then the message new owner\n * becomes the specified <consumer>. If the minimum idle time specified\n * is zero, messages are claimed regardless of their idle time.\n *\n * All the messages that cannot be found inside the pending entries list\n * are ignored, but in case the FORCE option is used. In that case we\n * create the NACK (representing a not yet acknowledged message) entry in\n * the consumer group PEL.\n *\n * This command creates the consumer as side effect if it does not yet\n * exists. Moreover the command reset the idle time of the message to 0,\n * even if by using the IDLE or TIME options, the user can control the\n * new idle time.\n *\n * The options at the end can be used in order to specify more attributes\n * to set in the representation of the pending message:\n *\n * 1. IDLE <ms>:\n *      Set the idle time (last time it was delivered) of the message.\n *      If IDLE is not specified, an IDLE of 0 is assumed, that is,\n *      the time count is reset because the message has now a new\n *      owner trying to process it.\n *\n * 2. TIME <ms-unix-time>:\n *      This is the same as IDLE but instead of a relative amount of\n *      milliseconds, it sets the idle time to a specific unix time\n *      (in milliseconds). This is useful in order to rewrite the AOF\n *      file generating XCLAIM commands.\n *\n * 3. RETRYCOUNT <count>:\n *      Set the retry counter to the specified value. This counter is\n *      incremented every time a message is delivered again. Normally\n *      XCLAIM does not alter this counter, which is just served to clients\n *      when the XPENDING command is called: this way clients can detect\n *      anomalies, like messages that are never processed for some reason\n *      after a big number of delivery attempts.\n *\n * 4. FORCE:\n *      Creates the pending message entry in the PEL even if certain\n *      specified IDs are not already in the PEL assigned to a different\n *      client. However the message must be exist in the stream, otherwise\n *      the IDs of non existing messages are ignored.\n *\n * 5. JUSTID:\n *      Return just an array of IDs of messages successfully claimed,\n *      without returning the actual message.\n *\n * 6. LASTID <id>:\n *      Update the consumer group last ID with the specified ID if the\n *      current last ID is smaller than the provided one.\n *      This is used for replication / AOF, so that when we read from a\n *      consumer group, the XCLAIM that gets propagated to give ownership\n *      to the consumer, is also used in order to update the group current\n *      ID.\n *\n * The command returns an array of messages that the user\n * successfully claimed, so that the caller is able to understand\n * what messages it is now in charge of. */\nvoid xclaimCommand(client *c) {\n    streamCG *group = NULL;\n    robj *o = lookupKeyRead(c->db,c->argv[1]);\n    long long minidle; /* Minimum idle time argument. */\n    long long retrycount = -1;   /* -1 means RETRYCOUNT option not given. */\n    mstime_t deliverytime = -1;  /* -1 means IDLE/TIME options not given. */\n    int force = 0;\n    int justid = 0;\n\n    if (o) {\n        if (checkType(c,o,OBJ_STREAM)) return; /* Type error. */\n        group = streamLookupCG(o->ptr,c->argv[2]->ptr);\n    }\n\n    /* No key or group? Send an error given that the group creation\n     * is mandatory. */\n    if (o == NULL || group == NULL) {\n        addReplyErrorFormat(c,\"-NOGROUP No such key '%s' or \"\n                              \"consumer group '%s'\", (char*)c->argv[1]->ptr,\n                              (char*)c->argv[2]->ptr);\n        return;\n    }\n\n    if (getLongLongFromObjectOrReply(c,c->argv[4],&minidle,\n        \"Invalid min-idle-time argument for XCLAIM\")\n        != C_OK) return;\n    if (minidle < 0) minidle = 0;\n\n    /* Start parsing the IDs, so that we abort ASAP if there is a syntax\n     * error: the return value of this command cannot be an error in case\n     * the client successfully claimed some message, so it should be\n     * executed in a \"all or nothing\" fashion. */\n    int j;\n    for (j = 5; j < c->argc; j++) {\n        streamID id;\n        if (streamParseStrictIDOrReply(NULL,c->argv[j],&id,0) != C_OK) break;\n    }\n    int last_id_arg = j-1; /* Next time we iterate the IDs we now the range. */\n\n    /* If we stopped because some IDs cannot be parsed, perhaps they\n     * are trailing options. */\n    mstime_t now = mstime();\n    streamID last_id = {0,0};\n    int propagate_last_id = 0;\n    for (; j < c->argc; j++) {\n        int moreargs = (c->argc-1) - j; /* Number of additional arguments. */\n        char *opt = c->argv[j]->ptr;\n        if (!strcasecmp(opt,\"FORCE\")) {\n            force = 1;\n        } else if (!strcasecmp(opt,\"JUSTID\")) {\n            justid = 1;\n        } else if (!strcasecmp(opt,\"IDLE\") && moreargs) {\n            j++;\n            if (getLongLongFromObjectOrReply(c,c->argv[j],&deliverytime,\n                \"Invalid IDLE option argument for XCLAIM\")\n                != C_OK) return;\n            deliverytime = now - deliverytime;\n        } else if (!strcasecmp(opt,\"TIME\") && moreargs) {\n            j++;\n            if (getLongLongFromObjectOrReply(c,c->argv[j],&deliverytime,\n                \"Invalid TIME option argument for XCLAIM\")\n                != C_OK) return;\n        } else if (!strcasecmp(opt,\"RETRYCOUNT\") && moreargs) {\n            j++;\n            if (getLongLongFromObjectOrReply(c,c->argv[j],&retrycount,\n                \"Invalid RETRYCOUNT option argument for XCLAIM\")\n                != C_OK) return;\n        } else if (!strcasecmp(opt,\"LASTID\") && moreargs) {\n            j++;\n            if (streamParseStrictIDOrReply(c,c->argv[j],&last_id,0) != C_OK) return;\n        } else {\n            addReplyErrorFormat(c,\"Unrecognized XCLAIM option '%s'\",opt);\n            return;\n        }\n    }\n\n    if (streamCompareID(&last_id,&group->last_id) > 0) {\n        group->last_id = last_id;\n        propagate_last_id = 1;\n    }\n\n    if (deliverytime != -1) {\n        /* If a delivery time was passed, either with IDLE or TIME, we\n         * do some sanity check on it, and set the deliverytime to now\n         * (which is a sane choice usually) if the value is bogus.\n         * To raise an error here is not wise because clients may compute\n         * the idle time doing some math starting from their local time,\n         * and this is not a good excuse to fail in case, for instance,\n         * the computer time is a bit in the future from our POV. */\n        if (deliverytime < 0 || deliverytime > now) deliverytime = now;\n    } else {\n        /* If no IDLE/TIME option was passed, we want the last delivery\n         * time to be now, so that the idle time of the message will be\n         * zero. */\n        deliverytime = now;\n    }\n\n    /* Do the actual claiming. */\n    streamConsumer *consumer = NULL;\n    void *arraylenptr = addReplyDeferredLen(c);\n    size_t arraylen = 0;\n    for (int j = 5; j <= last_id_arg; j++) {\n        streamID id;\n        unsigned char buf[sizeof(streamID)];\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK)\n            serverPanic(\"StreamID invalid after check. Should not be possible.\");\n        streamEncodeID(buf,&id);\n\n        /* Lookup the ID in the group PEL. */\n        streamNACK *nack = raxFind(group->pel,buf,sizeof(buf));\n\n        /* If FORCE is passed, let's check if at least the entry\n         * exists in the Stream. In such case, we'll crate a new\n         * entry in the PEL from scratch, so that XCLAIM can also\n         * be used to create entries in the PEL. Useful for AOF\n         * and replication of consumer groups. */\n        if (force && nack == raxNotFound) {\n            streamIterator myiterator;\n            streamIteratorStart(&myiterator,o->ptr,&id,&id,0);\n            int64_t numfields;\n            int found = 0;\n            streamID item_id;\n            if (streamIteratorGetID(&myiterator,&item_id,&numfields)) found = 1;\n            streamIteratorStop(&myiterator);\n\n            /* Item must exist for us to create a NACK for it. */\n            if (!found) continue;\n\n            /* Create the NACK. */\n            nack = streamCreateNACK(NULL);\n            raxInsert(group->pel,buf,sizeof(buf),nack,NULL);\n        }\n\n        if (nack != raxNotFound) {\n            /* We need to check if the minimum idle time requested\n             * by the caller is satisfied by this entry.\n             *\n             * Note that the nack could be created by FORCE, in this\n             * case there was no pre-existing entry and minidle should\n             * be ignored, but in that case nick->consumer is NULL. */\n            if (nack->consumer && minidle) {\n                mstime_t this_idle = now - nack->delivery_time;\n                if (this_idle < minidle) continue;\n            }\n            /* Remove the entry from the old consumer.\n             * Note that nack->consumer is NULL if we created the\n             * NACK above because of the FORCE option. */\n            if (nack->consumer)\n                raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);\n            /* Update the consumer and idle time. */\n            if (consumer == NULL)\n                consumer = streamLookupConsumer(group,c->argv[3]->ptr,SLC_NONE);\n            nack->consumer = consumer;\n            nack->delivery_time = deliverytime;\n            /* Set the delivery attempts counter if given, otherwise\n             * autoincrement unless JUSTID option provided */\n            if (retrycount >= 0) {\n                nack->delivery_count = retrycount;\n            } else if (!justid) {\n                nack->delivery_count++;\n            }\n            /* Add the entry in the new consumer local PEL. */\n            raxInsert(consumer->pel,buf,sizeof(buf),nack,NULL);\n            /* Send the reply for this entry. */\n            if (justid) {\n                addReplyStreamID(c,&id);\n            } else {\n                size_t emitted = streamReplyWithRange(c,o->ptr,&id,&id,1,0,\n                                    NULL,NULL,STREAM_RWR_RAWENTRIES,NULL);\n                if (!emitted) addReplyNull(c);\n            }\n            arraylen++;\n\n            /* Propagate this change. */\n            streamPropagateXCLAIM(c,c->argv[1],group,c->argv[2],c->argv[j],nack);\n            propagate_last_id = 0; /* Will be propagated by XCLAIM itself. */\n            server.dirty++;\n        }\n    }\n    if (propagate_last_id) {\n        streamPropagateGroupID(c,c->argv[1],group,c->argv[2]);\n        server.dirty++;\n    }\n    setDeferredArrayLen(c,arraylenptr,arraylen);\n    preventCommandPropagation(c);\n}\n\n\n/* XDEL <key> [<ID1> <ID2> ... <IDN>]\n *\n * Removes the specified entries from the stream. Returns the number\n * of items actually deleted, that may be different from the number\n * of IDs passed in case certain IDs do not exist. */\nvoid xdelCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL\n        || checkType(c,o,OBJ_STREAM)) return;\n    stream *s = o->ptr;\n\n    /* We need to sanity check the IDs passed to start. Even if not\n     * a big issue, it is not great that the command is only partially\n     * executed because at some point an invalid ID is parsed. */\n    streamID id;\n    for (int j = 2; j < c->argc; j++) {\n        if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;\n    }\n\n    /* Actually apply the command. */\n    int deleted = 0;\n    for (int j = 2; j < c->argc; j++) {\n        streamParseStrictIDOrReply(c,c->argv[j],&id,0); /* Retval already checked. */\n        deleted += streamDeleteItem(s,&id);\n    }\n\n    /* Propagate the write if needed. */\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xdel\",c->argv[1],c->db->id);\n        server.dirty += deleted;\n    }\n    addReplyLongLong(c,deleted);\n}\n\n/* General form: XTRIM <key> [... options ...]\n *\n * List of options:\n *\n * MAXLEN [~|=] <count>     -- Trim so that the stream will be capped at\n *                             the specified length. Use ~ before the\n *                             count in order to demand approximated trimming\n *                             (like XADD MAXLEN option).\n */\n\n#define TRIM_STRATEGY_NONE 0\n#define TRIM_STRATEGY_MAXLEN 1\nvoid xtrimCommand(client *c) {\n    robj *o;\n\n    /* If the key does not exist, we are ok returning zero, that is, the\n     * number of elements removed from the stream. */\n    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL\n        || checkType(c,o,OBJ_STREAM)) return;\n    stream *s = o->ptr;\n\n    /* Argument parsing. */\n    int trim_strategy = TRIM_STRATEGY_NONE;\n    long long maxlen = -1;  /* If left to -1 no trimming is performed. */\n    int approx_maxlen = 0;  /* If 1 only delete whole radix tree nodes, so\n                               the maxium length is not applied verbatim. */\n    int maxlen_arg_idx = 0; /* Index of the count in MAXLEN, for rewriting. */\n\n    /* Parse options. */\n    int i = 2; /* Start of options. */\n    for (; i < c->argc; i++) {\n        int moreargs = (c->argc-1) - i; /* Number of additional arguments. */\n        char *opt = c->argv[i]->ptr;\n        if (!strcasecmp(opt,\"maxlen\") && moreargs) {\n            approx_maxlen = 0;\n            trim_strategy = TRIM_STRATEGY_MAXLEN;\n            char *next = c->argv[i+1]->ptr;\n            /* Check for the form MAXLEN ~ <count>. */\n            if (moreargs >= 2 && next[0] == '~' && next[1] == '\\0') {\n                approx_maxlen = 1;\n                i++;\n            } else if (moreargs >= 2 && next[0] == '=' && next[1] == '\\0') {\n                i++;\n            }\n            if (getLongLongFromObjectOrReply(c,c->argv[i+1],&maxlen,NULL)\n                != C_OK) return;\n\n            if (maxlen < 0) {\n                addReplyError(c,\"The MAXLEN argument must be >= 0.\");\n                return;\n            }\n            i++;\n            maxlen_arg_idx = i;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Perform the trimming. */\n    int64_t deleted = 0;\n    if (trim_strategy == TRIM_STRATEGY_MAXLEN) {\n        deleted = streamTrimByLength(s,maxlen,approx_maxlen);\n    } else {\n        addReplyError(c,\"XTRIM called without an option to trim the stream\");\n        return;\n    }\n\n    /* Propagate the write if needed. */\n    if (deleted) {\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STREAM,\"xtrim\",c->argv[1],c->db->id);\n        server.dirty += deleted;\n        if (approx_maxlen) streamRewriteApproxMaxlen(c,s,maxlen_arg_idx);\n    }\n    addReplyLongLong(c,deleted);\n}\n\n/* Helper function for xinfoCommand.\n * Handles the variants of XINFO STREAM */\nvoid xinfoReplyWithStreamInfo(client *c, stream *s) {\n    int full = 1;\n    long long count = 10; /* Default COUNT is 10 so we don't block the server */\n    robj **optv = c->argv + 3; /* Options start after XINFO STREAM <key> */\n    int optc = c->argc - 3;\n\n    /* Parse options. */\n    if (optc == 0) {\n        full = 0;\n    } else {\n        /* Valid options are [FULL] or [FULL COUNT <count>] */\n        if (optc != 1 && optc != 3) {\n            addReplySubcommandSyntaxError(c);\n            return;\n        }\n\n        /* First option must be \"FULL\" */\n        if (strcasecmp(optv[0]->ptr,\"full\")) {\n            addReplySubcommandSyntaxError(c);\n            return;\n        }\n\n        if (optc == 3) {\n            /* First option must be \"FULL\" */\n            if (strcasecmp(optv[1]->ptr,\"count\")) {\n                addReplySubcommandSyntaxError(c);\n                return;\n            }\n            if (getLongLongFromObjectOrReply(c,optv[2],&count,NULL) == C_ERR)\n                return;\n            if (count < 0) count = 10;\n        }\n    }\n\n    addReplyMapLen(c,full ? 6 : 7);\n    addReplyBulkCString(c,\"length\");\n    addReplyLongLong(c,s->length);\n    addReplyBulkCString(c,\"radix-tree-keys\");\n    addReplyLongLong(c,raxSize(s->rax));\n    addReplyBulkCString(c,\"radix-tree-nodes\");\n    addReplyLongLong(c,s->rax->numnodes);\n    addReplyBulkCString(c,\"last-generated-id\");\n    addReplyStreamID(c,&s->last_id);\n\n    if (!full) {\n        /* XINFO STREAM <key> */\n\n        addReplyBulkCString(c,\"groups\");\n        addReplyLongLong(c,s->cgroups ? raxSize(s->cgroups) : 0);\n\n        /* To emit the first/last entry we use streamReplyWithRange(). */\n        int emitted;\n        streamID start, end;\n        start.ms = start.seq = 0;\n        end.ms = end.seq = UINT64_MAX;\n        addReplyBulkCString(c,\"first-entry\");\n        emitted = streamReplyWithRange(c,s,&start,&end,1,0,NULL,NULL,\n                                       STREAM_RWR_RAWENTRIES,NULL);\n        if (!emitted) addReplyNull(c);\n        addReplyBulkCString(c,\"last-entry\");\n        emitted = streamReplyWithRange(c,s,&start,&end,1,1,NULL,NULL,\n                                       STREAM_RWR_RAWENTRIES,NULL);\n        if (!emitted) addReplyNull(c);\n    } else {\n        /* XINFO STREAM <key> FULL [COUNT <count>] */\n\n        /* Stream entries */\n        addReplyBulkCString(c,\"entries\");\n        streamReplyWithRange(c,s,NULL,NULL,count,0,NULL,NULL,0,NULL);\n\n        /* Consumer groups */\n        addReplyBulkCString(c,\"groups\");\n        if (s->cgroups == NULL) {\n            addReplyArrayLen(c,0);\n        } else {\n            addReplyArrayLen(c,raxSize(s->cgroups));\n            raxIterator ri_cgroups;\n            raxStart(&ri_cgroups,s->cgroups);\n            raxSeek(&ri_cgroups,\"^\",NULL,0);\n            while(raxNext(&ri_cgroups)) {\n                streamCG *cg = ri_cgroups.data;\n                addReplyMapLen(c,5);\n\n                /* Name */\n                addReplyBulkCString(c,\"name\");\n                addReplyBulkCBuffer(c,ri_cgroups.key,ri_cgroups.key_len);\n\n                /* Last delivered ID */\n                addReplyBulkCString(c,\"last-delivered-id\");\n                addReplyStreamID(c,&cg->last_id);\n\n                /* Group PEL count */\n                addReplyBulkCString(c,\"pel-count\");\n                addReplyLongLong(c,raxSize(cg->pel));\n\n                /* Group PEL */\n                addReplyBulkCString(c,\"pending\");\n                long long arraylen_cg_pel = 0;\n                void *arrayptr_cg_pel = addReplyDeferredLen(c);\n                raxIterator ri_cg_pel;\n                raxStart(&ri_cg_pel,cg->pel);\n                raxSeek(&ri_cg_pel,\"^\",NULL,0);\n                while(raxNext(&ri_cg_pel) && (!count || arraylen_cg_pel < count)) {\n                    streamNACK *nack = ri_cg_pel.data;\n                    addReplyArrayLen(c,4);\n\n                    /* Entry ID. */\n                    streamID id;\n                    streamDecodeID(ri_cg_pel.key,&id);\n                    addReplyStreamID(c,&id);\n\n                    /* Consumer name. */\n                    addReplyBulkCBuffer(c,nack->consumer->name,\n                                        sdslen(nack->consumer->name));\n\n                    /* Last delivery. */\n                    addReplyLongLong(c,nack->delivery_time);\n\n                    /* Number of deliveries. */\n                    addReplyLongLong(c,nack->delivery_count);\n\n                    arraylen_cg_pel++;\n                }\n                setDeferredArrayLen(c,arrayptr_cg_pel,arraylen_cg_pel);\n                raxStop(&ri_cg_pel);\n\n                /* Consumers */\n                addReplyBulkCString(c,\"consumers\");\n                addReplyArrayLen(c,raxSize(cg->consumers));\n                raxIterator ri_consumers;\n                raxStart(&ri_consumers,cg->consumers);\n                raxSeek(&ri_consumers,\"^\",NULL,0);\n                while(raxNext(&ri_consumers)) {\n                    streamConsumer *consumer = ri_consumers.data;\n                    addReplyMapLen(c,4);\n\n                    /* Consumer name */\n                    addReplyBulkCString(c,\"name\");\n                    addReplyBulkCBuffer(c,consumer->name,sdslen(consumer->name));\n\n                    /* Seen-time */\n                    addReplyBulkCString(c,\"seen-time\");\n                    addReplyLongLong(c,consumer->seen_time);\n\n                    /* Consumer PEL count */\n                    addReplyBulkCString(c,\"pel-count\");\n                    addReplyLongLong(c,raxSize(consumer->pel));\n\n                    /* Consumer PEL */\n                    addReplyBulkCString(c,\"pending\");\n                    long long arraylen_cpel = 0;\n                    void *arrayptr_cpel = addReplyDeferredLen(c);\n                    raxIterator ri_cpel;\n                    raxStart(&ri_cpel,consumer->pel);\n                    raxSeek(&ri_cpel,\"^\",NULL,0);\n                    while(raxNext(&ri_cpel) && (!count || arraylen_cpel < count)) {\n                        streamNACK *nack = ri_cpel.data;\n                        addReplyArrayLen(c,3);\n\n                        /* Entry ID. */\n                        streamID id;\n                        streamDecodeID(ri_cpel.key,&id);\n                        addReplyStreamID(c,&id);\n\n                        /* Last delivery. */\n                        addReplyLongLong(c,nack->delivery_time);\n\n                        /* Number of deliveries. */\n                        addReplyLongLong(c,nack->delivery_count);\n\n                        arraylen_cpel++;\n                    }\n                    setDeferredArrayLen(c,arrayptr_cpel,arraylen_cpel);\n                    raxStop(&ri_cpel);\n                }\n                raxStop(&ri_consumers);\n            }\n            raxStop(&ri_cgroups);\n        }\n    }\n}\n\n/* XINFO CONSUMERS <key> <group>\n * XINFO GROUPS <key>\n * XINFO STREAM <key> [FULL [COUNT <count>]]\n * XINFO HELP. */\nvoid xinfoCommand(client *c) {\n    const char *help[] = {\n\"CONSUMERS <key> <groupname>         -- Show consumer groups of group <groupname>.\",\n\"GROUPS <key>                        -- Show the stream consumer groups.\",\n\"STREAM <key> [FULL [COUNT <count>]] -- Show information about the stream.\",\n\"                                       FULL will return the full state of the stream,\",\n\"                                            including all entries, groups, consumers and PELs.\",\n\"                                            It's possible to show only the first stream/PEL entries\",\n\"                                            by using the COUNT modifier (Default is 10)\",\n\"HELP                                -- Print this help.\",\nNULL\n    };\n    stream *s = NULL;\n    char *opt;\n    robj *key;\n\n    /* HELP is special. Handle it ASAP. */\n    if (!strcasecmp(c->argv[1]->ptr,\"HELP\")) {\n        addReplyHelp(c, help);\n        return;\n    } else if (c->argc < 3) {\n        addReplyError(c,\"syntax error, try 'XINFO HELP'\");\n        return;\n    }\n\n    /* With the exception of HELP handled before any other sub commands, all\n     * the ones are in the form of \"<subcommand> <key>\". */\n    opt = c->argv[1]->ptr;\n    key = c->argv[2];\n\n    /* Lookup the key now, this is common for all the subcommands but HELP. */\n    robj *o = lookupKeyWriteOrReply(c,key,shared.nokeyerr);\n    if (o == NULL || checkType(c,o,OBJ_STREAM)) return;\n    s = o->ptr;\n\n    /* Dispatch the different subcommands. */\n    if (!strcasecmp(opt,\"CONSUMERS\") && c->argc == 4) {\n        /* XINFO CONSUMERS <key> <group>. */\n        streamCG *cg = streamLookupCG(s,c->argv[3]->ptr);\n        if (cg == NULL) {\n            addReplyErrorFormat(c, \"-NOGROUP No such consumer group '%s' \"\n                                   \"for key name '%s'\",\n                                   (char*)c->argv[3]->ptr, (char*)key->ptr);\n            return;\n        }\n\n        addReplyArrayLen(c,raxSize(cg->consumers));\n        raxIterator ri;\n        raxStart(&ri,cg->consumers);\n        raxSeek(&ri,\"^\",NULL,0);\n        mstime_t now = mstime();\n        while(raxNext(&ri)) {\n            streamConsumer *consumer = ri.data;\n            mstime_t idle = now - consumer->seen_time;\n            if (idle < 0) idle = 0;\n\n            addReplyMapLen(c,3);\n            addReplyBulkCString(c,\"name\");\n            addReplyBulkCBuffer(c,consumer->name,sdslen(consumer->name));\n            addReplyBulkCString(c,\"pending\");\n            addReplyLongLong(c,raxSize(consumer->pel));\n            addReplyBulkCString(c,\"idle\");\n            addReplyLongLong(c,idle);\n        }\n        raxStop(&ri);\n    } else if (!strcasecmp(opt,\"GROUPS\") && c->argc == 3) {\n        /* XINFO GROUPS <key>. */\n        if (s->cgroups == NULL) {\n            addReplyArrayLen(c,0);\n            return;\n        }\n\n        addReplyArrayLen(c,raxSize(s->cgroups));\n        raxIterator ri;\n        raxStart(&ri,s->cgroups);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            streamCG *cg = ri.data;\n            addReplyMapLen(c,4);\n            addReplyBulkCString(c,\"name\");\n            addReplyBulkCBuffer(c,ri.key,ri.key_len);\n            addReplyBulkCString(c,\"consumers\");\n            addReplyLongLong(c,raxSize(cg->consumers));\n            addReplyBulkCString(c,\"pending\");\n            addReplyLongLong(c,raxSize(cg->pel));\n            addReplyBulkCString(c,\"last-delivered-id\");\n            addReplyStreamID(c,&cg->last_id);\n        }\n        raxStop(&ri);\n    } else if (!strcasecmp(opt,\"STREAM\")) {\n        /* XINFO STREAM <key> [FULL [COUNT <count>]]. */\n        xinfoReplyWithStreamInfo(c,s);\n    } else {\n        addReplySubcommandSyntaxError(c);\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"server.h\"\n#include <math.h> /* isnan(), isinf() */\n\n/*-----------------------------------------------------------------------------\n * String Commands\n *----------------------------------------------------------------------------*/\n\nstatic int checkStringLength(client *c, long long size) {\n    if (size > 512*1024*1024) {\n        addReplyError(c,\"string exceeds maximum allowed size (512MB)\");\n        return C_ERR;\n    }\n    return C_OK;\n}\n\n/* The setGenericCommand() function implements the SET operation with different\n * options and variants. This function is called in order to implement the\n * following commands: SET, SETEX, PSETEX, SETNX.\n *\n * 'flags' changes the behavior of the command (NX or XX, see below).\n *\n * 'expire' represents an expire to set in form of a Redis object as passed\n * by the user. It is interpreted according to the specified 'unit'.\n *\n * 'ok_reply' and 'abort_reply' is what the function will reply to the client\n * if the operation is performed, or when it is not because of NX or\n * XX flags.\n *\n * If ok_reply is NULL \"+OK\" is used.\n * If abort_reply is NULL, \"$-1\" is used. */\n\n#define OBJ_SET_NO_FLAGS 0\n#define OBJ_SET_NX (1<<0)          /* Set if key not exists. */\n#define OBJ_SET_XX (1<<1)          /* Set if key exists. */\n#define OBJ_SET_EX (1<<2)          /* Set if time in seconds is given */\n#define OBJ_SET_PX (1<<3)          /* Set if time in ms in given */\n#define OBJ_SET_KEEPTTL (1<<4)     /* Set and keep the ttl */\n\nvoid setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {\n    long long milliseconds = 0; /* initialized to avoid any harmness warning */\n\n    if (expire) {\n        if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK)\n            return;\n        if (milliseconds <= 0) {\n            addReplyErrorFormat(c,\"invalid expire time in %s\",c->cmd->name);\n            return;\n        }\n        if (unit == UNIT_SECONDS) milliseconds *= 1000;\n    }\n\n    if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||\n        (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))\n    {\n        addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);\n        return;\n    }\n    genericSetKey(c,c->db,key,val,flags & OBJ_SET_KEEPTTL,1);\n    server.dirty++;\n    if (expire) setExpire(c,c->db,key,mstime()+milliseconds);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"set\",key,c->db->id);\n    if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,\n        \"expire\",key,c->db->id);\n    addReply(c, ok_reply ? ok_reply : shared.ok);\n}\n\n/* SET key value [NX] [XX] [KEEPTTL] [EX <seconds>] [PX <milliseconds>] */\nvoid setCommand(client *c) {\n    int j;\n    robj *expire = NULL;\n    int unit = UNIT_SECONDS;\n    int flags = OBJ_SET_NO_FLAGS;\n\n    for (j = 3; j < c->argc; j++) {\n        char *a = c->argv[j]->ptr;\n        robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];\n\n        if ((a[0] == 'n' || a[0] == 'N') &&\n            (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n            !(flags & OBJ_SET_XX))\n        {\n            flags |= OBJ_SET_NX;\n        } else if ((a[0] == 'x' || a[0] == 'X') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_NX))\n        {\n            flags |= OBJ_SET_XX;\n        } else if (!strcasecmp(c->argv[j]->ptr,\"KEEPTTL\") &&\n                   !(flags & OBJ_SET_EX) && !(flags & OBJ_SET_PX))\n        {\n            flags |= OBJ_SET_KEEPTTL;\n        } else if ((a[0] == 'e' || a[0] == 'E') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_KEEPTTL) &&\n                   !(flags & OBJ_SET_PX) && next)\n        {\n            flags |= OBJ_SET_EX;\n            unit = UNIT_SECONDS;\n            expire = next;\n            j++;\n        } else if ((a[0] == 'p' || a[0] == 'P') &&\n                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\\0' &&\n                   !(flags & OBJ_SET_KEEPTTL) &&\n                   !(flags & OBJ_SET_EX) && next)\n        {\n            flags |= OBJ_SET_PX;\n            unit = UNIT_MILLISECONDS;\n            expire = next;\n            j++;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);\n}\n\nvoid setnxCommand(client *c) {\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setGenericCommand(c,OBJ_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);\n}\n\nvoid setexCommand(client *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);\n}\n\nvoid psetexCommand(client *c) {\n    c->argv[3] = tryObjectEncoding(c->argv[3]);\n    setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);\n}\n\nint getGenericCommand(client *c) {\n    robj *o;\n\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL)\n        return C_OK;\n\n    if (o->type != OBJ_STRING) {\n        addReply(c,shared.wrongtypeerr);\n        return C_ERR;\n    } else {\n        addReplyBulk(c,o);\n        return C_OK;\n    }\n}\n\nvoid getCommand(client *c) {\n    getGenericCommand(c);\n}\n\nvoid getsetCommand(client *c) {\n    if (getGenericCommand(c) == C_ERR) return;\n    c->argv[2] = tryObjectEncoding(c->argv[2]);\n    setKey(c,c->db,c->argv[1],c->argv[2]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[1],c->db->id);\n    server.dirty++;\n}\n\nvoid setrangeCommand(client *c) {\n    robj *o;\n    long offset;\n    sds value = c->argv[3]->ptr;\n\n    if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != C_OK)\n        return;\n\n    if (offset < 0) {\n        addReplyError(c,\"offset is out of range\");\n        return;\n    }\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n        /* Return 0 when setting nothing on a non-existing string */\n        if (sdslen(value) == 0) {\n            addReply(c,shared.czero);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        if (checkStringLength(c,offset+sdslen(value)) != C_OK)\n            return;\n\n        o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));\n        dbAdd(c->db,c->argv[1],o);\n    } else {\n        size_t olen;\n\n        /* Key exists, check type */\n        if (checkType(c,o,OBJ_STRING))\n            return;\n\n        /* Return existing string length when setting nothing */\n        olen = stringObjectLen(o);\n        if (sdslen(value) == 0) {\n            addReplyLongLong(c,olen);\n            return;\n        }\n\n        /* Return when the resulting string exceeds allowed size */\n        if (checkStringLength(c,offset+sdslen(value)) != C_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    if (sdslen(value) > 0) {\n        o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));\n        memcpy((char*)o->ptr+offset,value,sdslen(value));\n        signalModifiedKey(c,c->db,c->argv[1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\n            \"setrange\",c->argv[1],c->db->id);\n        server.dirty++;\n    }\n    addReplyLongLong(c,sdslen(o->ptr));\n}\n\nvoid getrangeCommand(client *c) {\n    robj *o;\n    long long start, end;\n    char *str, llbuf[32];\n    size_t strlen;\n\n    if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)\n        return;\n    if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)\n        return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n\n    if (o->encoding == OBJ_ENCODING_INT) {\n        str = llbuf;\n        strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);\n    } else {\n        str = o->ptr;\n        strlen = sdslen(str);\n    }\n\n    /* Convert negative indexes */\n    if (start < 0 && end < 0 && start > end) {\n        addReply(c,shared.emptybulk);\n        return;\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 long long)end >= strlen) end = strlen-1;\n\n    /* Precondition: end >= 0 && end < strlen, so the only condition where\n     * nothing can be returned is: start > end. */\n    if (start > end || strlen == 0) {\n        addReply(c,shared.emptybulk);\n    } else {\n        addReplyBulkCBuffer(c,(char*)str+start,end-start+1);\n    }\n}\n\nvoid mgetCommand(client *c) {\n    int j;\n\n    addReplyArrayLen(c,c->argc-1);\n    for (j = 1; j < c->argc; j++) {\n        robj *o = lookupKeyRead(c->db,c->argv[j]);\n        if (o == NULL) {\n            addReplyNull(c);\n        } else {\n            if (o->type != OBJ_STRING) {\n                addReplyNull(c);\n            } else {\n                addReplyBulk(c,o);\n            }\n        }\n    }\n}\n\nvoid msetGenericCommand(client *c, int nx) {\n    int j;\n\n    if ((c->argc % 2) == 0) {\n        addReplyError(c,\"wrong number of arguments for MSET\");\n        return;\n    }\n\n    /* Handle the NX flag. The MSETNX semantic is to return zero and don't\n     * set anything if at least one key alerady exists. */\n    if (nx) {\n        for (j = 1; j < c->argc; j += 2) {\n            if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {\n                addReply(c, shared.czero);\n                return;\n            }\n        }\n    }\n\n    for (j = 1; j < c->argc; j += 2) {\n        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);\n        setKey(c,c->db,c->argv[j],c->argv[j+1]);\n        notifyKeyspaceEvent(NOTIFY_STRING,\"set\",c->argv[j],c->db->id);\n    }\n    server.dirty += (c->argc-1)/2;\n    addReply(c, nx ? shared.cone : shared.ok);\n}\n\nvoid msetCommand(client *c) {\n    msetGenericCommand(c,0);\n}\n\nvoid msetnxCommand(client *c) {\n    msetGenericCommand(c,1);\n}\n\nvoid incrDecrCommand(client *c, long long incr) {\n    long long value, oldvalue;\n    robj *o, *new;\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o != NULL && checkType(c,o,OBJ_STRING)) return;\n    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;\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    value += incr;\n\n    if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&\n        (value < 0 || value >= OBJ_SHARED_INTEGERS) &&\n        value >= LONG_MIN && value <= LONG_MAX)\n    {\n        new = o;\n        o->ptr = (void*)((long)value);\n    } else {\n        new = createStringObjectFromLongLongForValue(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,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"incrby\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReply(c,shared.colon);\n    addReply(c,new);\n    addReply(c,shared.crlf);\n}\n\nvoid incrCommand(client *c) {\n    incrDecrCommand(c,1);\n}\n\nvoid decrCommand(client *c) {\n    incrDecrCommand(c,-1);\n}\n\nvoid incrbyCommand(client *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;\n    incrDecrCommand(c,incr);\n}\n\nvoid decrbyCommand(client *c) {\n    long long incr;\n\n    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;\n    incrDecrCommand(c,-incr);\n}\n\nvoid incrbyfloatCommand(client *c) {\n    long double incr, value;\n    robj *o, *new, *aux1, *aux2;\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o != NULL && checkType(c,o,OBJ_STRING)) return;\n    if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != C_OK ||\n        getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != C_OK)\n        return;\n\n    value += incr;\n    if (isnan(value) || isinf(value)) {\n        addReplyError(c,\"increment would produce NaN or Infinity\");\n        return;\n    }\n    new = createStringObjectFromLongDouble(value,1);\n    if (o)\n        dbOverwrite(c->db,c->argv[1],new);\n    else\n        dbAdd(c->db,c->argv[1],new);\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"incrbyfloat\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReplyBulk(c,new);\n\n    /* Always replicate INCRBYFLOAT as a SET command with the final value\n     * in order to make sure that differences in float precision or formatting\n     * will not create differences in replicas or after an AOF restart. */\n    aux1 = createStringObject(\"SET\",3);\n    rewriteClientCommandArgument(c,0,aux1);\n    decrRefCount(aux1);\n    rewriteClientCommandArgument(c,2,new);\n    aux2 = createStringObject(\"KEEPTTL\",7);\n    rewriteClientCommandArgument(c,3,aux2);\n    decrRefCount(aux2);\n}\n\nvoid appendCommand(client *c) {\n    size_t totlen;\n    robj *o, *append;\n\n    o = lookupKeyWrite(c->db,c->argv[1]);\n    if (o == NULL) {\n        /* Create the key */\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        /* Key exists, check type */\n        if (checkType(c,o,OBJ_STRING))\n            return;\n\n        /* \"append\" is an argument, so always an sds */\n        append = c->argv[2];\n        totlen = stringObjectLen(o)+sdslen(append->ptr);\n        if (checkStringLength(c,totlen) != C_OK)\n            return;\n\n        /* Append the value */\n        o = dbUnshareStringValue(c->db,c->argv[1],o);\n        o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));\n        totlen = sdslen(o->ptr);\n    }\n    signalModifiedKey(c,c->db,c->argv[1]);\n    notifyKeyspaceEvent(NOTIFY_STRING,\"append\",c->argv[1],c->db->id);\n    server.dirty++;\n    addReplyLongLong(c,totlen);\n}\n\nvoid strlenCommand(client *c) {\n    robj *o;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||\n        checkType(c,o,OBJ_STRING)) return;\n    addReplyLongLong(c,stringObjectLen(o));\n}\n\n\n/* STRALGO -- Implement complex algorithms on strings.\n *\n * STRALGO <algorithm> ... arguments ... */\nvoid stralgoLCS(client *c);     /* This implements the LCS algorithm. */\nvoid stralgoCommand(client *c) {\n    /* Select the algorithm. */\n    if (!strcasecmp(c->argv[1]->ptr,\"lcs\")) {\n        stralgoLCS(c);\n    } else {\n        addReply(c,shared.syntaxerr);\n    }\n}\n\n/* STRALGO <algo> [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN]\n *     STRINGS <string> <string> | KEYS <keya> <keyb>\n */\nvoid stralgoLCS(client *c) {\n    uint32_t i, j;\n    long long minmatchlen = 0;\n    sds a = NULL, b = NULL;\n    int getlen = 0, getidx = 0, withmatchlen = 0;\n    robj *obja = NULL, *objb = NULL;\n\n    for (j = 2; j < (uint32_t)c->argc; j++) {\n        char *opt = c->argv[j]->ptr;\n        int moreargs = (c->argc-1) - j;\n\n        if (!strcasecmp(opt,\"IDX\")) {\n            getidx = 1;\n        } else if (!strcasecmp(opt,\"LEN\")) {\n            getlen = 1;\n        } else if (!strcasecmp(opt,\"WITHMATCHLEN\")) {\n            withmatchlen = 1;\n        } else if (!strcasecmp(opt,\"MINMATCHLEN\") && moreargs) {\n            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&minmatchlen,NULL)\n                != C_OK) return;\n            if (minmatchlen < 0) minmatchlen = 0;\n            j++;\n        } else if (!strcasecmp(opt,\"STRINGS\")) {\n            if (a != NULL) {\n                addReplyError(c,\"Either use STRINGS or KEYS\");\n                return;\n            }\n            a = c->argv[j+1]->ptr;\n            b = c->argv[j+2]->ptr;\n            j += 2;\n        } else if (!strcasecmp(opt,\"KEYS\")) {\n            if (a != NULL) {\n                addReplyError(c,\"Either use STRINGS or KEYS\");\n                return;\n            }\n            obja = lookupKeyRead(c->db,c->argv[j+1]);\n            objb = lookupKeyRead(c->db,c->argv[j+2]);\n            obja = obja ? getDecodedObject(obja) : createStringObject(\"\",0);\n            objb = objb ? getDecodedObject(objb) : createStringObject(\"\",0);\n            a = obja->ptr;\n            b = objb->ptr;\n            j += 2;\n        } else {\n            addReply(c,shared.syntaxerr);\n            return;\n        }\n    }\n\n    /* Complain if the user passed ambiguous parameters. */\n    if (a == NULL) {\n        addReplyError(c,\"Please specify two strings: \"\n                        \"STRINGS or KEYS options are mandatory\");\n        return;\n    } else if (getlen && getidx) {\n        addReplyError(c,\n            \"If you want both the length and indexes, please \"\n            \"just use IDX.\");\n        return;\n    }\n\n    /* Compute the LCS using the vanilla dynamic programming technique of\n     * building a table of LCS(x,y) substrings. */\n    uint32_t alen = sdslen(a);\n    uint32_t blen = sdslen(b);\n\n    /* Setup an uint32_t array to store at LCS[i,j] the length of the\n     * LCS A0..i-1, B0..j-1. Note that we have a linear array here, so\n     * we index it as LCS[j+(blen+1)*j] */\n    uint32_t *lcs = zmalloc((alen+1)*(blen+1)*sizeof(uint32_t));\n    #define LCS(A,B) lcs[(B)+((A)*(blen+1))]\n\n    /* Start building the LCS table. */\n    for (uint32_t i = 0; i <= alen; i++) {\n        for (uint32_t j = 0; j <= blen; j++) {\n            if (i == 0 || j == 0) {\n                /* If one substring has length of zero, the\n                 * LCS length is zero. */\n                LCS(i,j) = 0;\n            } else if (a[i-1] == b[j-1]) {\n                /* The len LCS (and the LCS itself) of two\n                 * sequences with the same final character, is the\n                 * LCS of the two sequences without the last char\n                 * plus that last char. */\n                LCS(i,j) = LCS(i-1,j-1)+1;\n            } else {\n                /* If the last character is different, take the longest\n                 * between the LCS of the first string and the second\n                 * minus the last char, and the reverse. */\n                uint32_t lcs1 = LCS(i-1,j);\n                uint32_t lcs2 = LCS(i,j-1);\n                LCS(i,j) = lcs1 > lcs2 ? lcs1 : lcs2;\n            }\n        }\n    }\n\n    /* Store the actual LCS string in \"result\" if needed. We create\n     * it backward, but the length is already known, we store it into idx. */\n    uint32_t idx = LCS(alen,blen);\n    sds result = NULL;        /* Resulting LCS string. */\n    void *arraylenptr = NULL; /* Deffered length of the array for IDX. */\n    uint32_t arange_start = alen, /* alen signals that values are not set. */\n             arange_end = 0,\n             brange_start = 0,\n             brange_end = 0;\n\n    /* Do we need to compute the actual LCS string? Allocate it in that case. */\n    int computelcs = getidx || !getlen;\n    if (computelcs) result = sdsnewlen(SDS_NOINIT,idx);\n\n    /* Start with a deferred array if we have to emit the ranges. */\n    uint32_t arraylen = 0;  /* Number of ranges emitted in the array. */\n    if (getidx) {\n        addReplyMapLen(c,2);\n        addReplyBulkCString(c,\"matches\");\n        arraylenptr = addReplyDeferredLen(c);\n    }\n\n    i = alen, j = blen;\n    while (computelcs && i > 0 && j > 0) {\n        int emit_range = 0;\n        if (a[i-1] == b[j-1]) {\n            /* If there is a match, store the character and reduce\n             * the indexes to look for a new match. */\n            result[idx-1] = a[i-1];\n\n            /* Track the current range. */\n            if (arange_start == alen) {\n                arange_start = i-1;\n                arange_end = i-1;\n                brange_start = j-1;\n                brange_end = j-1;\n            } else {\n                /* Let's see if we can extend the range backward since\n                 * it is contiguous. */\n                if (arange_start == i && brange_start == j) {\n                    arange_start--;\n                    brange_start--;\n                } else {\n                    emit_range = 1;\n                }\n            }\n            /* Emit the range if we matched with the first byte of\n             * one of the two strings. We'll exit the loop ASAP. */\n            if (arange_start == 0 || brange_start == 0) emit_range = 1;\n            idx--; i--; j--;\n        } else {\n            /* Otherwise reduce i and j depending on the largest\n             * LCS between, to understand what direction we need to go. */\n            uint32_t lcs1 = LCS(i-1,j);\n            uint32_t lcs2 = LCS(i,j-1);\n            if (lcs1 > lcs2)\n                i--;\n            else\n                j--;\n            if (arange_start != alen) emit_range = 1;\n        }\n\n        /* Emit the current range if needed. */\n        uint32_t match_len = arange_end - arange_start + 1;\n        if (emit_range) {\n            if (minmatchlen == 0 || match_len >= minmatchlen) {\n                if (arraylenptr) {\n                    addReplyArrayLen(c,2+withmatchlen);\n                    addReplyArrayLen(c,2);\n                    addReplyLongLong(c,arange_start);\n                    addReplyLongLong(c,arange_end);\n                    addReplyArrayLen(c,2);\n                    addReplyLongLong(c,brange_start);\n                    addReplyLongLong(c,brange_end);\n                    if (withmatchlen) addReplyLongLong(c,match_len);\n                    arraylen++;\n                }\n            }\n            arange_start = alen; /* Restart at the next match. */\n        }\n    }\n\n    /* Signal modified key, increment dirty, ... */\n\n    /* Reply depending on the given options. */\n    if (arraylenptr) {\n        addReplyBulkCString(c,\"len\");\n        addReplyLongLong(c,LCS(alen,blen));\n        setDeferredArrayLen(c,arraylenptr,arraylen);\n    } else if (getlen) {\n        addReplyLongLong(c,LCS(alen,blen));\n    } else {\n        addReplyBulkSds(c,result);\n        result = NULL;\n    }\n\n    /* Cleanup. */\n    if (obja) decrRefCount(obja);\n    if (objb) decrRefCount(objb);\n    sdsfree(result);\n    zfree(lcs);\n    return;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 * 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 * Note that the SDS string representing the element is the same in both\n * the hash table and skiplist in order to save memory. What we do in order\n * to manage the shared SDS string more easily is to free the SDS string\n * only in zslFreeNode(). The dictionary has no value free method set.\n * So we should always remove an element from the dictionary, and later from\n * the skiplist.\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 * a) this implementation allows for repeated scores.\n * b) the comparison is not just by key (our 'score') but by satellite data.\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#include \"server.h\"\n#include <math.h>\n\n/*-----------------------------------------------------------------------------\n * Skiplist implementation of the low level API\n *----------------------------------------------------------------------------*/\n\nint zslLexValueGteMin(sds value, zlexrangespec *spec);\nint zslLexValueLteMax(sds value, zlexrangespec *spec);\n\n/* Create a skiplist node with the specified number of levels.\n * The SDS string 'ele' is referenced by the node after the call. */\nzskiplistNode *zslCreateNode(int level, double score, sds ele) {\n    zskiplistNode *zn =\n        zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));\n    zn->score = score;\n    zn->ele = ele;\n    return zn;\n}\n\n/* Create a new skiplist. */\nzskiplist *zslCreate(void) {\n    int j;\n    zskiplist *zsl;\n\n    zsl = zmalloc(sizeof(*zsl));\n    zsl->level = 1;\n    zsl->length = 0;\n    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);\n    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {\n        zsl->header->level[j].forward = NULL;\n        zsl->header->level[j].span = 0;\n    }\n    zsl->header->backward = NULL;\n    zsl->tail = NULL;\n    return zsl;\n}\n\n/* Free the specified skiplist node. The referenced SDS string representation\n * of the element is freed too, unless node->ele is set to NULL before calling\n * this function. */\nvoid zslFreeNode(zskiplistNode *node) {\n    sdsfree(node->ele);\n    zfree(node);\n}\n\n/* Free a whole skiplist. */\nvoid zslFree(zskiplist *zsl) {\n    zskiplistNode *node = zsl->header->level[0].forward, *next;\n\n    zfree(zsl->header);\n    while(node) {\n        next = node->level[0].forward;\n        zslFreeNode(node);\n        node = next;\n    }\n    zfree(zsl);\n}\n\n/* Returns a random level for the new skiplist node we are going to create.\n * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL\n * (both inclusive), with a powerlaw-alike distribution where higher\n * levels are less likely to be returned. */\nint zslRandomLevel(void) {\n    int level = 1;\n    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))\n        level += 1;\n    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;\n}\n\n/* Insert a new node in the skiplist. Assumes the element does not already\n * exist (up to the caller to enforce that). The skiplist takes ownership\n * of the passed SDS string 'ele'. */\nzskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned int rank[ZSKIPLIST_MAXLEVEL];\n    int i, level;\n\n    serverAssert(!isnan(score));\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* store rank that is crossed to reach the insert position */\n        rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];\n        while (x->level[i].forward &&\n                (x->level[i].forward->score < score ||\n                    (x->level[i].forward->score == score &&\n                    sdscmp(x->level[i].forward->ele,ele) < 0)))\n        {\n            rank[i] += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n    /* we assume the element is not already inside, since we allow duplicated\n     * scores, reinserting the same element should never happen since the\n     * caller of zslInsert() should test in the hash table if the element is\n     * already inside or not. */\n    level = zslRandomLevel();\n    if (level > zsl->level) {\n        for (i = zsl->level; i < level; i++) {\n            rank[i] = 0;\n            update[i] = zsl->header;\n            update[i]->level[i].span = zsl->length;\n        }\n        zsl->level = level;\n    }\n    x = zslCreateNode(level,score,ele);\n    for (i = 0; i < level; i++) {\n        x->level[i].forward = update[i]->level[i].forward;\n        update[i]->level[i].forward = x;\n\n        /* update span covered by update[i] as x is inserted here */\n        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);\n        update[i]->level[i].span = (rank[0] - rank[i]) + 1;\n    }\n\n    /* increment span for untouched levels */\n    for (i = level; i < zsl->level; i++) {\n        update[i]->level[i].span++;\n    }\n\n    x->backward = (update[0] == zsl->header) ? NULL : update[0];\n    if (x->level[0].forward)\n        x->level[0].forward->backward = x;\n    else\n        zsl->tail = x;\n    zsl->length++;\n    return x;\n}\n\n/* Internal function used by zslDelete, zslDeleteRangeByScore and\n * zslDeleteRangeByRank. */\nvoid zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {\n    int i;\n    for (i = 0; i < zsl->level; i++) {\n        if (update[i]->level[i].forward == x) {\n            update[i]->level[i].span += x->level[i].span - 1;\n            update[i]->level[i].forward = x->level[i].forward;\n        } else {\n            update[i]->level[i].span -= 1;\n        }\n    }\n    if (x->level[0].forward) {\n        x->level[0].forward->backward = x->backward;\n    } else {\n        zsl->tail = x->backward;\n    }\n    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)\n        zsl->level--;\n    zsl->length--;\n}\n\n/* Delete an element with matching score/element from the skiplist.\n * The function returns 1 if the node was found and deleted, otherwise\n * 0 is returned.\n *\n * If 'node' is NULL the deleted node is freed by zslFreeNode(), otherwise\n * it is not freed (but just unlinked) and *node is set to the node pointer,\n * so that it is possible for the caller to reuse the node (including the\n * referenced SDS string at node->ele). */\nint zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n                (x->level[i].forward->score < score ||\n                    (x->level[i].forward->score == score &&\n                     sdscmp(x->level[i].forward->ele,ele) < 0)))\n        {\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n    /* We may have multiple elements with the same score, what we need\n     * is to find the element with both the right score and object. */\n    x = x->level[0].forward;\n    if (x && score == x->score && sdscmp(x->ele,ele) == 0) {\n        zslDeleteNode(zsl, x, update);\n        if (!node)\n            zslFreeNode(x);\n        else\n            *node = x;\n        return 1;\n    }\n    return 0; /* not found */\n}\n\n/* Update the score of an elmenent inside the sorted set skiplist.\n * Note that the element must exist and must match 'score'.\n * This function does not update the score in the hash table side, the\n * caller should take care of it.\n *\n * Note that this function attempts to just update the node, in case after\n * the score update, the node would be exactly at the same position.\n * Otherwise the skiplist is modified by removing and re-adding a new\n * element, which is more costly.\n *\n * The function returns the updated element skiplist node pointer. */\nzskiplistNode *zslUpdateScore(zskiplist *zsl, double curscore, sds ele, double newscore) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    int i;\n\n    /* We need to seek to element to update to start: this is useful anyway,\n     * we'll have to update or remove it. */\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n                (x->level[i].forward->score < curscore ||\n                    (x->level[i].forward->score == curscore &&\n                     sdscmp(x->level[i].forward->ele,ele) < 0)))\n        {\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n\n    /* Jump to our element: note that this function assumes that the\n     * element with the matching score exists. */\n    x = x->level[0].forward;\n    serverAssert(x && curscore == x->score && sdscmp(x->ele,ele) == 0);\n\n    /* If the node, after the score update, would be still exactly\n     * at the same position, we can just update the score without\n     * actually removing and re-inserting the element in the skiplist. */\n    if ((x->backward == NULL || x->backward->score < newscore) &&\n        (x->level[0].forward == NULL || x->level[0].forward->score > newscore))\n    {\n        x->score = newscore;\n        return x;\n    }\n\n    /* No way to reuse the old node: we need to remove and insert a new\n     * one at a different place. */\n    zslDeleteNode(zsl, x, update);\n    zskiplistNode *newnode = zslInsert(zsl,newscore,x->ele);\n    /* We reused the old node x->ele SDS string, free the node now\n     * since zslInsert created a new one. */\n    x->ele = NULL;\n    zslFreeNode(x);\n    return newnode;\n}\n\nint zslValueGteMin(double value, zrangespec *spec) {\n    return spec->minex ? (value > spec->min) : (value >= spec->min);\n}\n\nint zslValueLteMax(double value, zrangespec *spec) {\n    return spec->maxex ? (value < spec->max) : (value <= spec->max);\n}\n\n/* Returns if there is a part of the zset is in range. */\nint zslIsInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n\n    /* Test for ranges that will always be empty. */\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n    x = zsl->tail;\n    if (x == NULL || !zslValueGteMin(x->score,range))\n        return 0;\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslValueLteMax(x->score,range))\n        return 0;\n    return 1;\n}\n\n/* Find the first node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *OUT* of range. */\n        while (x->level[i].forward &&\n            !zslValueGteMin(x->level[i].forward->score,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so the next node cannot be NULL. */\n    x = x->level[0].forward;\n    serverAssert(x != NULL);\n\n    /* Check if score <= max. */\n    if (!zslValueLteMax(x->score,range)) return NULL;\n    return x;\n}\n\n/* Find the last node that is contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nzskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec *range) {\n    zskiplistNode *x;\n    int i;\n\n    /* If everything is out of range, return early. */\n    if (!zslIsInRange(zsl,range)) return NULL;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        /* Go forward while *IN* range. */\n        while (x->level[i].forward &&\n            zslValueLteMax(x->level[i].forward->score,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so this node cannot be NULL. */\n    serverAssert(x != NULL);\n\n    /* Check if score >= min. */\n    if (!zslValueGteMin(x->score,range)) return NULL;\n    return x;\n}\n\n/* Delete all the elements with score between min and max from the skiplist.\n * Min and max are inclusive, so a score >= min || score <= max is deleted.\n * Note that this function takes the reference to the hash table view of the\n * sorted set, in order to remove the elements from the hash table too. */\nunsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long removed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (range->minex ?\n            x->level[i].forward->score <= range->min :\n            x->level[i].forward->score < range->min))\n                x = x->level[i].forward;\n        update[i] = x;\n    }\n\n    /* Current node is the last with score < or <= min. */\n    x = x->level[0].forward;\n\n    /* Delete nodes while in range. */\n    while (x &&\n           (range->maxex ? x->score < range->max : x->score <= range->max))\n    {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->ele);\n        zslFreeNode(x); /* Here is where x->ele is actually released. */\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->ele,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->ele,range)) {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->ele);\n        zslFreeNode(x); /* Here is where x->ele is actually released. */\n        removed++;\n        x = next;\n    }\n    return removed;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n * Start and end are inclusive. Note that start and end need to be 1-based */\nunsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict) {\n    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;\n    unsigned long traversed = 0, removed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (traversed + x->level[i].span) < start) {\n            traversed += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        update[i] = x;\n    }\n\n    traversed++;\n    x = x->level[0].forward;\n    while (x && traversed <= end) {\n        zskiplistNode *next = x->level[0].forward;\n        zslDeleteNode(zsl,x,update);\n        dictDelete(dict,x->ele);\n        zslFreeNode(x);\n        removed++;\n        traversed++;\n        x = next;\n    }\n    return removed;\n}\n\n/* Find the rank for an element by both score and key.\n * Returns 0 when the element cannot be found, rank otherwise.\n * Note that the rank is 1-based due to the span of zsl->header to the\n * first element. */\nunsigned long zslGetRank(zskiplist *zsl, double score, sds ele) {\n    zskiplistNode *x;\n    unsigned long rank = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward &&\n            (x->level[i].forward->score < score ||\n                (x->level[i].forward->score == score &&\n                sdscmp(x->level[i].forward->ele,ele) <= 0))) {\n            rank += x->level[i].span;\n            x = x->level[i].forward;\n        }\n\n        /* x might be equal to zsl->header, so test if obj is non-NULL */\n        if (x->ele && sdscmp(x->ele,ele) == 0) {\n            return rank;\n        }\n    }\n    return 0;\n}\n\n/* Finds an element by its rank. The rank argument needs to be 1-based. */\nzskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {\n    zskiplistNode *x;\n    unsigned long traversed = 0;\n    int i;\n\n    x = zsl->header;\n    for (i = zsl->level-1; i >= 0; i--) {\n        while (x->level[i].forward && (traversed + x->level[i].span) <= rank)\n        {\n            traversed += x->level[i].span;\n            x = x->level[i].forward;\n        }\n        if (traversed == rank) {\n            return x;\n        }\n    }\n    return NULL;\n}\n\n/* Populate the rangespec according to the objects min and max. */\nstatic int zslParseRange(robj *min, robj *max, zrangespec *spec) {\n    char *eptr;\n    spec->minex = spec->maxex = 0;\n\n    /* Parse the min-max interval. If one of the values is prefixed\n     * by the \"(\" character, it's considered \"open\". For instance\n     * ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max\n     * ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */\n    if (min->encoding == OBJ_ENCODING_INT) {\n        spec->min = (long)min->ptr;\n    } else {\n        if (((char*)min->ptr)[0] == '(') {\n            spec->min = strtod((char*)min->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return C_ERR;\n            spec->minex = 1;\n        } else {\n            spec->min = strtod((char*)min->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->min)) return C_ERR;\n        }\n    }\n    if (max->encoding == OBJ_ENCODING_INT) {\n        spec->max = (long)max->ptr;\n    } else {\n        if (((char*)max->ptr)[0] == '(') {\n            spec->max = strtod((char*)max->ptr+1,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return C_ERR;\n            spec->maxex = 1;\n        } else {\n            spec->max = strtod((char*)max->ptr,&eptr);\n            if (eptr[0] != '\\0' || isnan(spec->max)) return C_ERR;\n        }\n    }\n\n    return C_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 comparison, and ex will be set to 0 or 1\n  * respectively if the item is exclusive or inclusive. C_OK will be\n  * returned.\n  *\n  * If the string is not a valid range C_ERR is returned, and the value\n  * of *dest and *ex is undefined. */\nint zslParseLexRangeItem(robj *item, sds *dest, int *ex) {\n    char *c = item->ptr;\n\n    switch(c[0]) {\n    case '+':\n        if (c[1] != '\\0') return C_ERR;\n        *ex = 1;\n        *dest = shared.maxstring;\n        return C_OK;\n    case '-':\n        if (c[1] != '\\0') return C_ERR;\n        *ex = 1;\n        *dest = shared.minstring;\n        return C_OK;\n    case '(':\n        *ex = 1;\n        *dest = sdsnewlen(c+1,sdslen(c)-1);\n        return C_OK;\n    case '[':\n        *ex = 0;\n        *dest = sdsnewlen(c+1,sdslen(c)-1);\n        return C_OK;\n    default:\n        return C_ERR;\n    }\n}\n\n/* Free a lex range structure, must be called only after zelParseLexRange()\n * populated the structure with success (C_OK returned). */\nvoid zslFreeLexRange(zlexrangespec *spec) {\n    if (spec->min != shared.minstring &&\n        spec->min != shared.maxstring) sdsfree(spec->min);\n    if (spec->max != shared.minstring &&\n        spec->max != shared.maxstring) sdsfree(spec->max);\n}\n\n/* Populate the lex rangespec according to the objects min and max.\n *\n * Return C_OK on success. On error C_ERR is returned.\n * When OK is returned the structure must be freed with zslFreeLexRange(),\n * otherwise no release is needed. */\nint zslParseLexRange(robj *min, robj *max, zlexrangespec *spec) {\n    /* The range can't be valid if objects are integer encoded.\n     * Every item must start with ( or [. */\n    if (min->encoding == OBJ_ENCODING_INT ||\n        max->encoding == OBJ_ENCODING_INT) return C_ERR;\n\n    spec->min = spec->max = NULL;\n    if (zslParseLexRangeItem(min, &spec->min, &spec->minex) == C_ERR ||\n        zslParseLexRangeItem(max, &spec->max, &spec->maxex) == C_ERR) {\n        zslFreeLexRange(spec);\n        return C_ERR;\n    } else {\n        return C_OK;\n    }\n}\n\n/* This is just a wrapper to sdscmp() that is able to\n * handle shared.minstring and shared.maxstring as the equivalent of\n * -inf and +inf for strings */\nint sdscmplex(sds a, sds b) {\n    if (a == b) return 0;\n    if (a == shared.minstring || b == shared.maxstring) return -1;\n    if (a == shared.maxstring || b == shared.minstring) return 1;\n    return sdscmp(a,b);\n}\n\nint zslLexValueGteMin(sds value, zlexrangespec *spec) {\n    return spec->minex ?\n        (sdscmplex(value,spec->min) > 0) :\n        (sdscmplex(value,spec->min) >= 0);\n}\n\nint zslLexValueLteMax(sds value, zlexrangespec *spec) {\n    return spec->maxex ?\n        (sdscmplex(value,spec->max) < 0) :\n        (sdscmplex(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    int cmp = sdscmplex(range->min,range->max);\n    if (cmp > 0 || (cmp == 0 && (range->minex || range->maxex)))\n        return 0;\n    x = zsl->tail;\n    if (x == NULL || !zslLexValueGteMin(x->ele,range))\n        return 0;\n    x = zsl->header->level[0].forward;\n    if (x == NULL || !zslLexValueLteMax(x->ele,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->ele,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    serverAssert(x != NULL);\n\n    /* Check if score <= max. */\n    if (!zslLexValueLteMax(x->ele,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->ele,range))\n                x = x->level[i].forward;\n    }\n\n    /* This is an inner range, so this node cannot be NULL. */\n    serverAssert(x != NULL);\n\n    /* Check if score >= min. */\n    if (!zslLexValueGteMin(x->ele,range)) return NULL;\n    return x;\n}\n\n/*-----------------------------------------------------------------------------\n * Ziplist-backed sorted set API\n *----------------------------------------------------------------------------*/\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    serverAssert(sptr != NULL);\n    serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));\n\n    if (vstr) {\n        memcpy(buf,vstr,vlen);\n        buf[vlen] = '\\0';\n        score = strtod(buf,NULL);\n    } else {\n        score = vlong;\n    }\n\n    return score;\n}\n\n/* Return a ziplist element as an SDS string. */\nsds ziplistGetObject(unsigned char *sptr) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    serverAssert(sptr != NULL);\n    serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));\n\n    if (vstr) {\n        return sdsnewlen((char*)vstr,vlen);\n    } else {\n        return sdsfromlonglong(vlong);\n    }\n}\n\n/* Compare element in sorted set with given element. */\nint zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen) {\n    unsigned char *vstr;\n    unsigned int vlen;\n    long long vlong;\n    unsigned char vbuf[32];\n    int minlen, cmp;\n\n    serverAssert(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    minlen = (vlen < clen) ? vlen : clen;\n    cmp = memcmp(vstr,cstr,minlen);\n    if (cmp == 0) return vlen-clen;\n    return cmp;\n}\n\nunsigned int zzlLength(unsigned char *zl) {\n    return ziplistLen(zl)/2;\n}\n\n/* Move to next entry based on the values in eptr and sptr. Both are set to\n * NULL when there is no next entry. */\nvoid zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    serverAssert(*eptr != NULL && *sptr != NULL);\n\n    _eptr = ziplistNext(zl,*sptr);\n    if (_eptr != NULL) {\n        _sptr = ziplistNext(zl,_eptr);\n        serverAssert(_sptr != NULL);\n    } else {\n        /* No next entry. */\n        _sptr = NULL;\n    }\n\n    *eptr = _eptr;\n    *sptr = _sptr;\n}\n\n/* Move to the previous entry based on the values in eptr and sptr. Both are\n * set to NULL when there is no next entry. */\nvoid zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {\n    unsigned char *_eptr, *_sptr;\n    serverAssert(*eptr != NULL && *sptr != NULL);\n\n    _sptr = ziplistPrev(zl,*eptr);\n    if (_sptr != NULL) {\n        _eptr = ziplistPrev(zl,_sptr);\n        serverAssert(_eptr != NULL);\n    } else {\n        /* No previous entry. */\n        _eptr = NULL;\n    }\n\n    *eptr = _eptr;\n    *sptr = _sptr;\n}\n\n/* Returns if there is a part of the zset is in range. Should only be used\n * internally by zzlFirstInRange and zzlLastInRange. */\nint zzlIsInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *p;\n    double score;\n\n    /* Test for ranges that will always be empty. */\n    if (range->min > range->max ||\n            (range->min == range->max && (range->minex || range->maxex)))\n        return 0;\n\n    p = ziplistIndex(zl,-1); /* Last score. */\n    if (p == NULL) return 0; /* Empty sorted set */\n    score = zzlGetScore(p);\n    if (!zslValueGteMin(score,range))\n        return 0;\n\n    p = ziplistIndex(zl,1); /* First score. */\n    serverAssert(p != NULL);\n    score = zzlGetScore(p);\n    if (!zslValueLteMax(score,range))\n        return 0;\n\n    return 1;\n}\n\n/* Find pointer to the first element contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double score;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueGteMin(score,range)) {\n            /* Check if score <= max. */\n            if (zslValueLteMax(score,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    return NULL;\n}\n\n/* Find pointer to the last element contained in the specified range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;\n    double score;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        score = zzlGetScore(sptr);\n        if (zslValueLteMax(score,range)) {\n            /* Check if score >= min. */\n            if (zslValueGteMin(score,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to previous element by moving to the score of previous element.\n         * When this returns NULL, we know there also is no element. */\n        sptr = ziplistPrev(zl,eptr);\n        if (sptr != NULL)\n            serverAssert((eptr = ziplistPrev(zl,sptr)) != NULL);\n        else\n            eptr = NULL;\n    }\n\n    return NULL;\n}\n\nint zzlLexValueGteMin(unsigned char *p, zlexrangespec *spec) {\n    sds value = ziplistGetObject(p);\n    int res = zslLexValueGteMin(value,spec);\n    sdsfree(value);\n    return res;\n}\n\nint zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec) {\n    sds value = ziplistGetObject(p);\n    int res = zslLexValueLteMax(value,spec);\n    sdsfree(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    int cmp = sdscmplex(range->min,range->max);\n    if (cmp > 0 || (cmp == 0 && (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    serverAssert(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        serverAssert(sptr != NULL);\n        eptr = ziplistNext(zl,sptr); /* Next element. */\n    }\n\n    return NULL;\n}\n\n/* Find pointer to the last element contained in the specified lex range.\n * Returns NULL when no element is contained in the range. */\nunsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range) {\n    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;\n\n    /* If everything is out of range, return early. */\n    if (!zzlIsInLexRange(zl,range)) return NULL;\n\n    while (eptr != NULL) {\n        if (zzlLexValueLteMax(eptr,range)) {\n            /* Check if score >= min. */\n            if (zzlLexValueGteMin(eptr,range))\n                return eptr;\n            return NULL;\n        }\n\n        /* Move to previous element by moving to the score of previous element.\n         * When this returns NULL, we know there also is no element. */\n        sptr = ziplistPrev(zl,eptr);\n        if (sptr != NULL)\n            serverAssert((eptr = ziplistPrev(zl,sptr)) != NULL);\n        else\n            eptr = NULL;\n    }\n\n    return NULL;\n}\n\nunsigned char *zzlFind(unsigned char *zl, sds ele, double *score) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        if (ziplistCompare(eptr,(unsigned char*)ele,sdslen(ele))) {\n            /* Matching element, pull out score. */\n            if (score != NULL) *score = zzlGetScore(sptr);\n            return eptr;\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\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. */\nunsigned char *zzlDelete(unsigned char *zl, unsigned char *eptr) {\n    unsigned char *p = eptr;\n\n    /* TODO: add function to ziplist API to delete N elements from offset. */\n    zl = ziplistDelete(zl,&p);\n    zl = ziplistDelete(zl,&p);\n    return zl;\n}\n\nunsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, sds ele, double score) {\n    unsigned char *sptr;\n    char scorebuf[128];\n    int scorelen;\n    size_t offset;\n\n    scorelen = d2string(scorebuf,sizeof(scorebuf),score);\n    if (eptr == NULL) {\n        zl = ziplistPush(zl,(unsigned char*)ele,sdslen(ele),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)scorebuf,scorelen,ZIPLIST_TAIL);\n    } else {\n        /* Keep offset relative to zl, as it might be re-allocated. */\n        offset = eptr-zl;\n        zl = ziplistInsert(zl,eptr,(unsigned char*)ele,sdslen(ele));\n        eptr = zl+offset;\n\n        /* Insert score after the element. */\n        serverAssert((sptr = ziplistNext(zl,eptr)) != NULL);\n        zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);\n    }\n    return zl;\n}\n\n/* Insert (element,score) pair in ziplist. This function assumes the element is\n * not yet present in the list. */\nunsigned char *zzlInsert(unsigned char *zl, sds ele, double score) {\n    unsigned char *eptr = ziplistIndex(zl,0), *sptr;\n    double s;\n\n    while (eptr != NULL) {\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n        s = zzlGetScore(sptr);\n\n        if (s > score) {\n            /* First element with score larger than score for element to be\n             * inserted. This means we should take its spot in the list to\n             * maintain ordering. */\n            zl = zzlInsertAt(zl,eptr,ele,score);\n            break;\n        } else if (s == score) {\n            /* Ensure lexicographical ordering for elements. */\n            if (zzlCompareElements(eptr,(unsigned char*)ele,sdslen(ele)) > 0) {\n                zl = zzlInsertAt(zl,eptr,ele,score);\n                break;\n            }\n        }\n\n        /* Move to next element. */\n        eptr = ziplistNext(zl,sptr);\n    }\n\n    /* Push on tail of list when it was not yet inserted. */\n    if (eptr == NULL)\n        zl = zzlInsertAt(zl,NULL,ele,score);\n    return zl;\n}\n\nunsigned char *zzlDeleteRangeByScore(unsigned char *zl, zrangespec *range, unsigned long *deleted) {\n    unsigned char *eptr, *sptr;\n    double score;\n    unsigned long num = 0;\n\n    if (deleted != NULL) *deleted = 0;\n\n    eptr = zzlFirstInRange(zl,range);\n    if (eptr == NULL) return zl;\n\n    /* When the tail of the ziplist is deleted, eptr will point to the sentinel\n     * byte and ziplistNext will return NULL. */\n    while ((sptr = ziplistNext(zl,eptr)) != NULL) {\n        score = zzlGetScore(sptr);\n        if (zslValueLteMax(score,range)) {\n            /* Delete both the element and the score. */\n            zl = ziplistDelete(zl,&eptr);\n            zl = ziplistDelete(zl,&eptr);\n            num++;\n        } else {\n            /* No longer in range. */\n            break;\n        }\n    }\n\n    if (deleted != NULL) *deleted = num;\n    return zl;\n}\n\nunsigned char *zzlDeleteRangeByLex(unsigned char *zl, zlexrangespec *range, unsigned long *deleted) {\n    unsigned char *eptr, *sptr;\n    unsigned long num = 0;\n\n    if (deleted != NULL) *deleted = 0;\n\n    eptr = zzlFirstInLexRange(zl,range);\n    if (eptr == NULL) return zl;\n\n    /* When the tail of the ziplist is deleted, eptr will point to the sentinel\n     * byte and ziplistNext will return NULL. */\n    while ((sptr = ziplistNext(zl,eptr)) != NULL) {\n        if (zzlLexValueLteMax(eptr,range)) {\n            /* Delete both the element and the score. */\n            zl = ziplistDelete(zl,&eptr);\n            zl = ziplistDelete(zl,&eptr);\n            num++;\n        } else {\n            /* No longer in range. */\n            break;\n        }\n    }\n\n    if (deleted != NULL) *deleted = num;\n    return zl;\n}\n\n/* Delete all the elements with rank between start and end from the skiplist.\n * Start and end are inclusive. Note that start and end need to be 1-based */\nunsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsigned int end, unsigned long *deleted) {\n    unsigned int num = (end-start)+1;\n    if (deleted) *deleted = num;\n    zl = ziplistDeleteRange(zl,2*(start-1),2*num);\n    return zl;\n}\n\n/*-----------------------------------------------------------------------------\n * Common sorted set API\n *----------------------------------------------------------------------------*/\n\nunsigned long zsetLength(const robj *zobj) {\n    unsigned long length = 0;\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        length = zzlLength(zobj->ptr);\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        length = ((const zset*)zobj->ptr)->zsl->length;\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return length;\n}\n\nvoid zsetConvert(robj *zobj, int encoding) {\n    zset *zs;\n    zskiplistNode *node, *next;\n    sds ele;\n    double score;\n\n    if (zobj->encoding == encoding) return;\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        if (encoding != OBJ_ENCODING_SKIPLIST)\n            serverPanic(\"Unknown target encoding\");\n\n        zs = zmalloc(sizeof(*zs));\n        zs->dict = dictCreate(&zsetDictType,NULL);\n        zs->zsl = zslCreate();\n\n        eptr = ziplistIndex(zl,0);\n        serverAssertWithInfo(NULL,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(NULL,zobj,sptr != NULL);\n\n        while (eptr != NULL) {\n            score = zzlGetScore(sptr);\n            serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n            if (vstr == NULL)\n                ele = sdsfromlonglong(vlong);\n            else\n                ele = sdsnewlen((char*)vstr,vlen);\n\n            node = zslInsert(zs->zsl,score,ele);\n            serverAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        zfree(zobj->ptr);\n        zobj->ptr = zs;\n        zobj->encoding = OBJ_ENCODING_SKIPLIST;\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        unsigned char *zl = ziplistNew();\n\n        if (encoding != OBJ_ENCODING_ZIPLIST)\n            serverPanic(\"Unknown target encoding\");\n\n        /* Approach similar to zslFree(), since we want to free the skiplist at\n         * the same time as creating the ziplist. */\n        zs = zobj->ptr;\n        dictRelease(zs->dict);\n        node = zs->zsl->header->level[0].forward;\n        zfree(zs->zsl->header);\n        zfree(zs->zsl);\n\n        while (node) {\n            zl = zzlInsertAt(zl,NULL,node->ele,node->score);\n            next = node->level[0].forward;\n            zslFreeNode(node);\n            node = next;\n        }\n\n        zfree(zs);\n        zobj->ptr = zl;\n        zobj->encoding = OBJ_ENCODING_ZIPLIST;\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\n/* Convert the sorted set object into a ziplist if it is not already a ziplist\n * and if the number of elements and the maximum element size is within the\n * expected ranges. */\nvoid zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return;\n    zset *zset = zobj->ptr;\n\n    if (zset->zsl->length <= server.zset_max_ziplist_entries &&\n        maxelelen <= server.zset_max_ziplist_value)\n            zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);\n}\n\n/* Return (by reference) the score of the specified member of the sorted set\n * storing it into *score. If the element does not exist C_ERR is returned\n * otherwise C_OK is returned and *score is correctly populated.\n * If 'zobj' or 'member' is NULL, C_ERR is returned. */\nint zsetScore(robj *zobj, sds member, double *score) {\n    if (!zobj || !member) return C_ERR;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        if (zzlFind(zobj->ptr, member, score) == NULL) return C_ERR;\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de = dictFind(zs->dict, member);\n        if (de == NULL) return C_ERR;\n        *score = *(double*)dictGetVal(de);\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return C_OK;\n}\n\n/* Add a new element or update the score of an existing element in a sorted\n * set, regardless of its encoding.\n *\n * The set of flags change the command behavior. They are passed with an integer\n * pointer since the function will clear the flags and populate them with\n * other flags to indicate different conditions.\n *\n * The input flags are the following:\n *\n * ZADD_INCR: Increment the current element score by 'score' instead of updating\n *            the current element score. If the element does not exist, we\n *            assume 0 as previous score.\n * ZADD_NX:   Perform the operation only if the element does not exist.\n * ZADD_XX:   Perform the operation only if the element already exist.\n *\n * When ZADD_INCR is used, the new score of the element is stored in\n * '*newscore' if 'newscore' is not NULL.\n *\n * The returned flags are the following:\n *\n * ZADD_NAN:     The resulting score is not a number.\n * ZADD_ADDED:   The element was added (not present before the call).\n * ZADD_UPDATED: The element score was updated.\n * ZADD_NOP:     No operation was performed because of NX or XX.\n *\n * Return value:\n *\n * The function returns 1 on success, and sets the appropriate flags\n * ADDED or UPDATED to signal what happened during the operation (note that\n * none could be set if we re-added an element using the same score it used\n * to have, or in the case a zero increment is used).\n *\n * The function returns 0 on error, currently only when the increment\n * produces a NAN condition, or when the 'score' value is NAN since the\n * start.\n *\n * The command as a side effect of adding a new element may convert the sorted\n * set internal encoding from ziplist to hashtable+skiplist.\n *\n * Memory management of 'ele':\n *\n * The function does not take ownership of the 'ele' SDS string, but copies\n * it if needed. */\nint zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {\n    /* Turn options into simple to check vars. */\n    int incr = (*flags & ZADD_INCR) != 0;\n    int nx = (*flags & ZADD_NX) != 0;\n    int xx = (*flags & ZADD_XX) != 0;\n    *flags = 0; /* We'll return our response flags. */\n    double curscore;\n\n    /* NaN as input is an error regardless of all the other parameters. */\n    if (isnan(score)) {\n        *flags = ZADD_NAN;\n        return 0;\n    }\n\n    /* Update the sorted set according to its encoding. */\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr;\n\n        if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {\n            /* NX? Return, same element already exists. */\n            if (nx) {\n                *flags |= ZADD_NOP;\n                return 1;\n            }\n\n            /* Prepare the score for the increment if needed. */\n            if (incr) {\n                score += curscore;\n                if (isnan(score)) {\n                    *flags |= ZADD_NAN;\n                    return 0;\n                }\n                if (newscore) *newscore = score;\n            }\n\n            /* Remove and re-insert when score changed. */\n            if (score != curscore) {\n                zobj->ptr = zzlDelete(zobj->ptr,eptr);\n                zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n                *flags |= ZADD_UPDATED;\n            }\n            return 1;\n        } else if (!xx) {\n            /* Optimize: check if the element is too large or the list\n             * becomes too long *before* executing zzlInsert. */\n            zobj->ptr = zzlInsert(zobj->ptr,ele,score);\n            if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries ||\n                sdslen(ele) > server.zset_max_ziplist_value)\n                zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);\n            if (newscore) *newscore = score;\n            *flags |= ZADD_ADDED;\n            return 1;\n        } else {\n            *flags |= ZADD_NOP;\n            return 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplistNode *znode;\n        dictEntry *de;\n\n        de = dictFind(zs->dict,ele);\n        if (de != NULL) {\n            /* NX? Return, same element already exists. */\n            if (nx) {\n                *flags |= ZADD_NOP;\n                return 1;\n            }\n            curscore = *(double*)dictGetVal(de);\n\n            /* Prepare the score for the increment if needed. */\n            if (incr) {\n                score += curscore;\n                if (isnan(score)) {\n                    *flags |= ZADD_NAN;\n                    return 0;\n                }\n                if (newscore) *newscore = score;\n            }\n\n            /* Remove and re-insert when score changes. */\n            if (score != curscore) {\n                znode = zslUpdateScore(zs->zsl,curscore,ele,score);\n                /* Note that we did not removed the original element from\n                 * the hash table representing the sorted set, so we just\n                 * update the score. */\n                dictGetVal(de) = &znode->score; /* Update score ptr. */\n                *flags |= ZADD_UPDATED;\n            }\n            return 1;\n        } else if (!xx) {\n            ele = sdsdup(ele);\n            znode = zslInsert(zs->zsl,score,ele);\n            serverAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);\n            *flags |= ZADD_ADDED;\n            if (newscore) *newscore = score;\n            return 1;\n        } else {\n            *flags |= ZADD_NOP;\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return 0; /* Never reached. */\n}\n\n/* Delete the element 'ele' from the sorted set, returning 1 if the element\n * existed and was deleted, 0 otherwise (the element was not there). */\nint zsetDel(robj *zobj, sds ele) {\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *eptr;\n\n        if ((eptr = zzlFind(zobj->ptr,ele,NULL)) != NULL) {\n            zobj->ptr = zzlDelete(zobj->ptr,eptr);\n            return 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        dictEntry *de;\n        double score;\n\n        de = dictUnlink(zs->dict,ele);\n        if (de != NULL) {\n            /* Get the score in order to delete from the skiplist later. */\n            score = *(double*)dictGetVal(de);\n\n            /* Delete from the hash table and later from the skiplist.\n             * Note that the order is important: deleting from the skiplist\n             * actually releases the SDS string representing the element,\n             * which is shared between the skiplist and the hash table, so\n             * we need to delete from the skiplist as the final step. */\n            dictFreeUnlinkedEntry(zs->dict,de);\n\n            /* Delete from skiplist. */\n            int retval = zslDelete(zs->zsl,score,ele,NULL);\n            serverAssert(retval);\n\n            if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n            return 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n    return 0; /* No such element found. */\n}\n\n/* Given a sorted set object returns the 0-based rank of the object or\n * -1 if the object does not exist.\n *\n * For rank we mean the position of the element in the sorted collection\n * of elements. So the first element has rank 0, the second rank 1, and so\n * forth up to length-1 elements.\n *\n * If 'reverse' is false, the rank is returned considering as first element\n * the one with the lowest score. Otherwise if 'reverse' is non-zero\n * the rank is computed considering as element with rank 0 the one with\n * the highest score. */\nlong zsetRank(robj *zobj, sds ele, int reverse) {\n    unsigned long llen;\n    unsigned long rank;\n\n    llen = zsetLength(zobj);\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        eptr = ziplistIndex(zl,0);\n        serverAssert(eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n        serverAssert(sptr != NULL);\n\n        rank = 1;\n        while(eptr != NULL) {\n            if (ziplistCompare(eptr,(unsigned char*)ele,sdslen(ele)))\n                break;\n            rank++;\n            zzlNext(zl,&eptr,&sptr);\n        }\n\n        if (eptr != NULL) {\n            if (reverse)\n                return llen-rank;\n            else\n                return rank-1;\n        } else {\n            return -1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        dictEntry *de;\n        double score;\n\n        de = dictFind(zs->dict,ele);\n        if (de != NULL) {\n            score = *(double*)dictGetVal(de);\n            rank = zslGetRank(zsl,score,ele);\n            /* Existing elements always have a rank. */\n            serverAssert(rank != 0);\n            if (reverse)\n                return llen-rank;\n            else\n                return rank-1;\n        } else {\n            return -1;\n        }\n    } else {\n        serverPanic(\"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(client *c, int flags) {\n    static char *nanerr = \"resulting score is not a number (NaN)\";\n    robj *key = c->argv[1];\n    robj *zobj;\n    sds ele;\n    double score = 0, *scores = NULL;\n    int j, elements;\n    int scoreidx = 0;\n    /* The following vars are used in order to track what the command actually\n     * did during the execution, to reply to the client and to trigger the\n     * notification of keyspace change. */\n    int added = 0;      /* Number of new elements added. */\n    int updated = 0;    /* Number of elements with updated score. */\n    int processed = 0;  /* Number of elements processed, may remain zero with\n                           options like XX. */\n\n    /* Parse options. At the end 'scoreidx' is set to the argument position\n     * of the score of the first score-element pair. */\n    scoreidx = 2;\n    while(scoreidx < c->argc) {\n        char *opt = c->argv[scoreidx]->ptr;\n        if (!strcasecmp(opt,\"nx\")) flags |= ZADD_NX;\n        else if (!strcasecmp(opt,\"xx\")) flags |= ZADD_XX;\n        else if (!strcasecmp(opt,\"ch\")) flags |= ZADD_CH;\n        else if (!strcasecmp(opt,\"incr\")) flags |= ZADD_INCR;\n        else break;\n        scoreidx++;\n    }\n\n    /* Turn options into simple to check vars. */\n    int incr = (flags & ZADD_INCR) != 0;\n    int nx = (flags & ZADD_NX) != 0;\n    int xx = (flags & ZADD_XX) != 0;\n    int ch = (flags & ZADD_CH) != 0;\n\n    /* After the options, we expect to have an even number of args, since\n     * we expect any number of score-element pairs. */\n    elements = c->argc-scoreidx;\n    if (elements % 2 || !elements) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    elements /= 2; /* Now this holds the number of score-element pairs. */\n\n    /* Check for incompatible options. */\n    if (nx && xx) {\n        addReplyError(c,\n            \"XX and NX options at the same time are not compatible\");\n        return;\n    }\n\n    if (incr && elements > 1) {\n        addReplyError(c,\n            \"INCR option supports a single increment-element pair\");\n        return;\n    }\n\n    /* Start parsing all the scores, we need to emit any syntax error\n     * before executing additions to the sorted set, as the command should\n     * either execute fully or nothing at all. */\n    scores = zmalloc(sizeof(double)*elements);\n    for (j = 0; j < elements; j++) {\n        if (getDoubleFromObjectOrReply(c,c->argv[scoreidx+j*2],&scores[j],NULL)\n            != C_OK) goto cleanup;\n    }\n\n    /* Lookup the key and create the sorted set if does not exist. */\n    zobj = lookupKeyWrite(c->db,key);\n    if (zobj == NULL) {\n        if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */\n        if (server.zset_max_ziplist_entries == 0 ||\n            server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr))\n        {\n            zobj = createZsetObject();\n        } else {\n            zobj = createZsetZiplistObject();\n        }\n        dbAdd(c->db,key,zobj);\n    } else {\n        if (zobj->type != OBJ_ZSET) {\n            addReply(c,shared.wrongtypeerr);\n            goto cleanup;\n        }\n    }\n\n    for (j = 0; j < elements; j++) {\n        double newscore;\n        score = scores[j];\n        int retflags = flags;\n\n        ele = c->argv[scoreidx+1+j*2]->ptr;\n        int retval = zsetAdd(zobj, score, ele, &retflags, &newscore);\n        if (retval == 0) {\n            addReplyError(c,nanerr);\n            goto cleanup;\n        }\n        if (retflags & ZADD_ADDED) added++;\n        if (retflags & ZADD_UPDATED) updated++;\n        if (!(retflags & ZADD_NOP)) processed++;\n        score = newscore;\n    }\n    server.dirty += (added+updated);\n\nreply_to_client:\n    if (incr) { /* ZINCRBY or INCR option. */\n        if (processed)\n            addReplyDouble(c,score);\n        else\n            addReplyNull(c);\n    } else { /* ZADD. */\n        addReplyLongLong(c,ch ? added+updated : added);\n    }\n\ncleanup:\n    zfree(scores);\n    if (added || updated) {\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_ZSET,\n            incr ? \"zincr\" : \"zadd\", key, c->db->id);\n    }\n}\n\nvoid zaddCommand(client *c) {\n    zaddGenericCommand(c,ZADD_NONE);\n}\n\nvoid zincrbyCommand(client *c) {\n    zaddGenericCommand(c,ZADD_INCR);\n}\n\nvoid zremCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int deleted = 0, keyremoved = 0, j;\n\n    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    for (j = 2; j < c->argc; j++) {\n        if (zsetDel(zobj,c->argv[j]->ptr)) deleted++;\n        if (zsetLength(zobj) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n            break;\n        }\n    }\n\n    if (deleted) {\n        notifyKeyspaceEvent(NOTIFY_ZSET,\"zrem\",key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n        signalModifiedKey(c,c->db,key);\n        server.dirty += deleted;\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(client *c, int rangetype) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int keyremoved = 0;\n    unsigned long deleted = 0;\n    zrangespec range;\n    zlexrangespec lexrange;\n    long start, end, llen;\n\n    /* Step 1: Parse the range. */\n    if (rangetype == ZRANGE_RANK) {\n        if ((getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK) ||\n            (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK))\n            return;\n    } else if (rangetype == ZRANGE_SCORE) {\n        if (zslParseRange(c->argv[2],c->argv[3],&range) != C_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) != C_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,OBJ_ZSET)) goto cleanup;\n\n    if (rangetype == ZRANGE_RANK) {\n        /* Sanitize indexes. */\n        llen = zsetLength(zobj);\n        if (start < 0) start = llen+start;\n        if (end < 0) end = llen+end;\n        if (start < 0) start = 0;\n\n        /* Invariant: start >= 0, so this test will be true when end < 0.\n         * The range is empty when start > end or start >= length. */\n        if (start > end || start >= llen) {\n            addReply(c,shared.czero);\n            goto cleanup;\n        }\n        if (end >= llen) end = llen-1;\n    }\n\n    /* Step 3: Perform the range deletion operation. */\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        switch(rangetype) {\n        case ZRANGE_RANK:\n            zobj->ptr = zzlDeleteRangeByRank(zobj->ptr,start+1,end+1,&deleted);\n            break;\n        case ZRANGE_SCORE:\n            zobj->ptr = zzlDeleteRangeByScore(zobj->ptr,&range,&deleted);\n            break;\n        case ZRANGE_LEX:\n            zobj->ptr = zzlDeleteRangeByLex(zobj->ptr,&lexrange,&deleted);\n            break;\n        }\n        if (zzlLength(zobj->ptr) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        switch(rangetype) {\n        case ZRANGE_RANK:\n            deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);\n            break;\n        case ZRANGE_SCORE:\n            deleted = zslDeleteRangeByScore(zs->zsl,&range,zs->dict);\n            break;\n        case ZRANGE_LEX:\n            deleted = zslDeleteRangeByLex(zs->zsl,&lexrange,zs->dict);\n            break;\n        }\n        if (htNeedsResize(zs->dict)) dictResize(zs->dict);\n        if (dictSize(zs->dict) == 0) {\n            dbDelete(c->db,key);\n            keyremoved = 1;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    /* Step 4: Notifications and reply. */\n    if (deleted) {\n        char *event[3] = {\"zremrangebyrank\",\"zremrangebyscore\",\"zremrangebylex\"};\n        signalModifiedKey(c,c->db,key);\n        notifyKeyspaceEvent(NOTIFY_ZSET,event[rangetype],key,c->db->id);\n        if (keyremoved)\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n    }\n    server.dirty += deleted;\n    addReplyLongLong(c,deleted);\n\ncleanup:\n    if (rangetype == ZRANGE_LEX) zslFreeLexRange(&lexrange);\n}\n\nvoid zremrangebyrankCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_RANK);\n}\n\nvoid zremrangebyscoreCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_SCORE);\n}\n\nvoid zremrangebylexCommand(client *c) {\n    zremrangeGenericCommand(c,ZRANGE_LEX);\n}\n\ntypedef struct {\n    robj *subject;\n    int type; /* Set, sorted set */\n    int encoding;\n    double weight;\n\n    union {\n        /* Set iterators. */\n        union _iterset {\n            struct {\n                intset *is;\n                int ii;\n            } is;\n            struct {\n                dict *dict;\n                dictIterator *di;\n                dictEntry *de;\n            } ht;\n        } set;\n\n        /* Sorted set iterators. */\n        union _iterzset {\n            struct {\n                unsigned char *zl;\n                unsigned char *eptr, *sptr;\n            } zl;\n            struct {\n                zset *zs;\n                zskiplistNode *node;\n            } sl;\n        } zset;\n    } iter;\n} zsetopsrc;\n\n\n/* Use dirty flags for pointers that need to be cleaned up in the next\n * iteration over the zsetopval. The dirty flag for the long long value is\n * special, since long long values don't need cleanup. Instead, it means that\n * we already checked that \"ell\" holds a long long, or tried to convert another\n * representation into a long long value. When this was successful,\n * OPVAL_VALID_LL is set as well. */\n#define OPVAL_DIRTY_SDS 1\n#define OPVAL_DIRTY_LL 2\n#define OPVAL_VALID_LL 4\n\n/* Store value retrieved from the iterator. */\ntypedef struct {\n    int flags;\n    unsigned char _buf[32]; /* Private buffer. */\n    sds ele;\n    unsigned char *estr;\n    unsigned int elen;\n    long long ell;\n    double score;\n} zsetopval;\n\ntypedef union _iterset iterset;\ntypedef union _iterzset iterzset;\n\nvoid zuiInitIterator(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            it->is.is = op->subject->ptr;\n            it->is.ii = 0;\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            it->ht.dict = op->subject->ptr;\n            it->ht.di = dictGetIterator(op->subject->ptr);\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            it->zl.zl = op->subject->ptr;\n            it->zl.eptr = ziplistIndex(it->zl.zl,0);\n            if (it->zl.eptr != NULL) {\n                it->zl.sptr = ziplistNext(it->zl.zl,it->zl.eptr);\n                serverAssert(it->zl.sptr != NULL);\n            }\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            it->sl.zs = op->subject->ptr;\n            it->sl.node = it->sl.zs->zsl->header->level[0].forward;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nvoid zuiClearIterator(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return;\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            UNUSED(it); /* skip */\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dictReleaseIterator(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            UNUSED(it); /* skip */\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            UNUSED(it); /* skip */\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nunsigned long zuiLength(zsetopsrc *op) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == OBJ_SET) {\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            return intsetLen(op->subject->ptr);\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            return dictSize(ht);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            return zzlLength(op->subject->ptr);\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            return zs->zsl->length;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\n/* Check if the current value is valid. If so, store it in the passed structure\n * and move to the next element. If not valid, this means we have reached the\n * end of the structure and can abort. */\nint zuiNext(zsetopsrc *op, zsetopval *val) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (val->flags & OPVAL_DIRTY_SDS)\n        sdsfree(val->ele);\n\n    memset(val,0,sizeof(zsetopval));\n\n    if (op->type == OBJ_SET) {\n        iterset *it = &op->iter.set;\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            int64_t ell;\n\n            if (!intsetGet(it->is.is,it->is.ii,&ell))\n                return 0;\n            val->ell = ell;\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->is.ii++;\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            if (it->ht.de == NULL)\n                return 0;\n            val->ele = dictGetKey(it->ht.de);\n            val->score = 1.0;\n\n            /* Move to next element. */\n            it->ht.de = dictNext(it->ht.di);\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        iterzset *it = &op->iter.zset;\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            /* No need to check both, but better be explicit. */\n            if (it->zl.eptr == NULL || it->zl.sptr == NULL)\n                return 0;\n            serverAssert(ziplistGet(it->zl.eptr,&val->estr,&val->elen,&val->ell));\n            val->score = zzlGetScore(it->zl.sptr);\n\n            /* Move to next element. */\n            zzlNext(it->zl.zl,&it->zl.eptr,&it->zl.sptr);\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            if (it->sl.node == NULL)\n                return 0;\n            val->ele = it->sl.node->ele;\n            val->score = it->sl.node->score;\n\n            /* Move to next element. */\n            it->sl.node = it->sl.node->level[0].forward;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n    return 1;\n}\n\nint zuiLongLongFromValue(zsetopval *val) {\n    if (!(val->flags & OPVAL_DIRTY_LL)) {\n        val->flags |= OPVAL_DIRTY_LL;\n\n        if (val->ele != NULL) {\n            if (string2ll(val->ele,sdslen(val->ele),&val->ell))\n                val->flags |= OPVAL_VALID_LL;\n        } else if (val->estr != NULL) {\n            if (string2ll((char*)val->estr,val->elen,&val->ell))\n                val->flags |= OPVAL_VALID_LL;\n        } else {\n            /* The long long was already set, flag as valid. */\n            val->flags |= OPVAL_VALID_LL;\n        }\n    }\n    return val->flags & OPVAL_VALID_LL;\n}\n\nsds zuiSdsFromValue(zsetopval *val) {\n    if (val->ele == NULL) {\n        if (val->estr != NULL) {\n            val->ele = sdsnewlen((char*)val->estr,val->elen);\n        } else {\n            val->ele = sdsfromlonglong(val->ell);\n        }\n        val->flags |= OPVAL_DIRTY_SDS;\n    }\n    return val->ele;\n}\n\n/* This is different from zuiSdsFromValue since returns a new SDS string\n * which is up to the caller to free. */\nsds zuiNewSdsFromValue(zsetopval *val) {\n    if (val->flags & OPVAL_DIRTY_SDS) {\n        /* We have already one to return! */\n        sds ele = val->ele;\n        val->flags &= ~OPVAL_DIRTY_SDS;\n        val->ele = NULL;\n        return ele;\n    } else if (val->ele) {\n        return sdsdup(val->ele);\n    } else if (val->estr) {\n        return sdsnewlen((char*)val->estr,val->elen);\n    } else {\n        return sdsfromlonglong(val->ell);\n    }\n}\n\nint zuiBufferFromValue(zsetopval *val) {\n    if (val->estr == NULL) {\n        if (val->ele != NULL) {\n            val->elen = sdslen(val->ele);\n            val->estr = (unsigned char*)val->ele;\n        } else {\n            val->elen = ll2string((char*)val->_buf,sizeof(val->_buf),val->ell);\n            val->estr = val->_buf;\n        }\n    }\n    return 1;\n}\n\n/* Find value pointed to by val in the source pointer to by op. When found,\n * return 1 and store its score in target. Return 0 otherwise. */\nint zuiFind(zsetopsrc *op, zsetopval *val, double *score) {\n    if (op->subject == NULL)\n        return 0;\n\n    if (op->type == OBJ_SET) {\n        if (op->encoding == OBJ_ENCODING_INTSET) {\n            if (zuiLongLongFromValue(val) &&\n                intsetFind(op->subject->ptr,val->ell))\n            {\n                *score = 1.0;\n                return 1;\n            } else {\n                return 0;\n            }\n        } else if (op->encoding == OBJ_ENCODING_HT) {\n            dict *ht = op->subject->ptr;\n            zuiSdsFromValue(val);\n            if (dictFind(ht,val->ele) != NULL) {\n                *score = 1.0;\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            serverPanic(\"Unknown set encoding\");\n        }\n    } else if (op->type == OBJ_ZSET) {\n        zuiSdsFromValue(val);\n\n        if (op->encoding == OBJ_ENCODING_ZIPLIST) {\n            if (zzlFind(op->subject->ptr,val->ele,score) != NULL) {\n                /* Score is already set by zzlFind. */\n                return 1;\n            } else {\n                return 0;\n            }\n        } else if (op->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = op->subject->ptr;\n            dictEntry *de;\n            if ((de = dictFind(zs->dict,val->ele)) != NULL) {\n                *score = *(double*)dictGetVal(de);\n                return 1;\n            } else {\n                return 0;\n            }\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n    } else {\n        serverPanic(\"Unsupported type\");\n    }\n}\n\nint zuiCompareByCardinality(const void *s1, const void *s2) {\n    unsigned long first = zuiLength((zsetopsrc*)s1);\n    unsigned long second = zuiLength((zsetopsrc*)s2);\n    if (first > second) return 1;\n    if (first < second) return -1;\n    return 0;\n}\n\n#define REDIS_AGGR_SUM 1\n#define REDIS_AGGR_MIN 2\n#define REDIS_AGGR_MAX 3\n#define zunionInterDictValue(_e) (dictGetVal(_e) == NULL ? 1.0 : *(double*)dictGetVal(_e))\n\ninline static void zunionInterAggregate(double *target, double val, int aggregate) {\n    if (aggregate == REDIS_AGGR_SUM) {\n        *target = *target + val;\n        /* The result of adding two doubles is NaN when one variable\n         * is +inf and the other is -inf. When these numbers are added,\n         * we maintain the convention of the result being 0.0. */\n        if (isnan(*target)) *target = 0.0;\n    } else if (aggregate == REDIS_AGGR_MIN) {\n        *target = val < *target ? val : *target;\n    } else if (aggregate == REDIS_AGGR_MAX) {\n        *target = val > *target ? val : *target;\n    } else {\n        /* safety net */\n        serverPanic(\"Unknown ZUNION/INTER aggregate type\");\n    }\n}\n\nuint64_t dictSdsHash(const void *key);\nint dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);\n\ndictType setAccumulatorDictType = {\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\nvoid zunionInterGenericCommand(client *c, robj *dstkey, int op) {\n    int i, j;\n    long setnum;\n    int aggregate = REDIS_AGGR_SUM;\n    zsetopsrc *src;\n    zsetopval zval;\n    sds tmp;\n    size_t maxelelen = 0;\n    robj *dstobj;\n    zset *dstzset;\n    zskiplistNode *znode;\n    int touched = 0;\n\n    /* expect setnum input keys to be given */\n    if ((getLongFromObjectOrReply(c, c->argv[2], &setnum, NULL) != C_OK))\n        return;\n\n    if (setnum < 1) {\n        addReplyError(c,\n            \"at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE\");\n        return;\n    }\n\n    /* test if the expected number of keys would overflow */\n    if (setnum > c->argc-3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    /* read keys to be used for input */\n    src = zcalloc(sizeof(zsetopsrc) * setnum);\n    for (i = 0, j = 3; i < setnum; i++, j++) {\n        robj *obj = lookupKeyWrite(c->db,c->argv[j]);\n        if (obj != NULL) {\n            if (obj->type != OBJ_ZSET && obj->type != OBJ_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        } else {\n            src[i].subject = NULL;\n        }\n\n        /* Default all weights to 1. */\n        src[i].weight = 1.0;\n    }\n\n    /* parse optional extra arguments */\n    if (j < c->argc) {\n        int remaining = c->argc - j;\n\n        while (remaining) {\n            if (remaining >= (setnum + 1) &&\n                !strcasecmp(c->argv[j]->ptr,\"weights\"))\n            {\n                j++; remaining--;\n                for (i = 0; i < setnum; i++, j++, remaining--) {\n                    if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,\n                            \"weight value is not a float\") != C_OK)\n                    {\n                        zfree(src);\n                        return;\n                    }\n                }\n            } else if (remaining >= 2 &&\n                       !strcasecmp(c->argv[j]->ptr,\"aggregate\"))\n            {\n                j++; remaining--;\n                if (!strcasecmp(c->argv[j]->ptr,\"sum\")) {\n                    aggregate = REDIS_AGGR_SUM;\n                } else if (!strcasecmp(c->argv[j]->ptr,\"min\")) {\n                    aggregate = REDIS_AGGR_MIN;\n                } else if (!strcasecmp(c->argv[j]->ptr,\"max\")) {\n                    aggregate = REDIS_AGGR_MAX;\n                } else {\n                    zfree(src);\n                    addReply(c,shared.syntaxerr);\n                    return;\n                }\n                j++; remaining--;\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    qsort(src,setnum,sizeof(zsetopsrc),zuiCompareByCardinality);\n\n    dstobj = createZsetObject();\n    dstzset = dstobj->ptr;\n    memset(&zval, 0, sizeof(zval));\n\n    if (op == SET_OP_INTER) {\n        /* Skip everything if the smallest input is empty. */\n        if (zuiLength(&src[0]) > 0) {\n            /* Precondition: as src[0] is non-empty and the inputs are ordered\n             * by size, all src[i > 0] are non-empty too. */\n            zuiInitIterator(&src[0]);\n            while (zuiNext(&src[0],&zval)) {\n                double score, value;\n\n                score = src[0].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                for (j = 1; j < setnum; j++) {\n                    /* It is not safe to access the zset we are\n                     * iterating, so explicitly check for equal object. */\n                    if (src[j].subject == src[0].subject) {\n                        value = zval.score*src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    } else if (zuiFind(&src[j],&zval,&value)) {\n                        value *= src[j].weight;\n                        zunionInterAggregate(&score,value,aggregate);\n                    } else {\n                        break;\n                    }\n                }\n\n                /* Only continue when present in every input. */\n                if (j == setnum) {\n                    tmp = zuiNewSdsFromValue(&zval);\n                    znode = zslInsert(dstzset->zsl,score,tmp);\n                    dictAdd(dstzset->dict,tmp,&znode->score);\n                    if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp);\n                }\n            }\n            zuiClearIterator(&src[0]);\n        }\n    } else if (op == SET_OP_UNION) {\n        dict *accumulator = dictCreate(&setAccumulatorDictType,NULL);\n        dictIterator *di;\n        dictEntry *de, *existing;\n        double score;\n\n        if (setnum) {\n            /* Our union is at least as large as the largest set.\n             * Resize the dictionary ASAP to avoid useless rehashing. */\n            dictExpand(accumulator,zuiLength(&src[setnum-1]));\n        }\n\n        /* Step 1: Create a dictionary of elements -> aggregated-scores\n         * by iterating one sorted set after the other. */\n        for (i = 0; i < setnum; i++) {\n            if (zuiLength(&src[i]) == 0) continue;\n\n            zuiInitIterator(&src[i]);\n            while (zuiNext(&src[i],&zval)) {\n                /* Initialize value */\n                score = src[i].weight * zval.score;\n                if (isnan(score)) score = 0;\n\n                /* Search for this element in the accumulating dictionary. */\n                de = dictAddRaw(accumulator,zuiSdsFromValue(&zval),&existing);\n                /* If we don't have it, we need to create a new entry. */\n                if (!existing) {\n                    tmp = zuiNewSdsFromValue(&zval);\n                    /* Remember the longest single element encountered,\n                     * to understand if it's possible to convert to ziplist\n                     * at the end. */\n                     if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp);\n                    /* Update the element with its initial score. */\n                    dictSetKey(accumulator, de, tmp);\n                    dictSetDoubleVal(de,score);\n                } else {\n                    /* Update the score with the score of the new instance\n                     * of the element found in the current sorted set.\n                     *\n                     * Here we access directly the dictEntry double\n                     * value inside the union as it is a big speedup\n                     * compared to using the getDouble/setDouble API. */\n                    zunionInterAggregate(&existing->v.d,score,aggregate);\n                }\n            }\n            zuiClearIterator(&src[i]);\n        }\n\n        /* Step 2: convert the dictionary into the final sorted set. */\n        di = dictGetIterator(accumulator);\n\n        /* We now are aware of the final size of the resulting sorted set,\n         * let's resize the dictionary embedded inside the sorted set to the\n         * right size, in order to save rehashing time. */\n        dictExpand(dstzset->dict,dictSize(accumulator));\n\n        while((de = dictNext(di)) != NULL) {\n            sds ele = dictGetKey(de);\n            score = dictGetDoubleVal(de);\n            znode = zslInsert(dstzset->zsl,score,ele);\n            dictAdd(dstzset->dict,ele,&znode->score);\n        }\n        dictReleaseIterator(di);\n        dictRelease(accumulator);\n    } else {\n        serverPanic(\"Unknown operator\");\n    }\n\n    if (dbDelete(c->db,dstkey))\n        touched = 1;\n    if (dstzset->zsl->length) {\n        zsetConvertToZiplistIfNeeded(dstobj,maxelelen);\n        dbAdd(c->db,dstkey,dstobj);\n        addReplyLongLong(c,zsetLength(dstobj));\n        signalModifiedKey(c,c->db,dstkey);\n        notifyKeyspaceEvent(NOTIFY_ZSET,\n            (op == SET_OP_UNION) ? \"zunionstore\" : \"zinterstore\",\n            dstkey,c->db->id);\n        server.dirty++;\n    } else {\n        decrRefCount(dstobj);\n        addReply(c,shared.czero);\n        if (touched) {\n            signalModifiedKey(c,c->db,dstkey);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",dstkey,c->db->id);\n            server.dirty++;\n        }\n    }\n    zfree(src);\n}\n\nvoid zunionstoreCommand(client *c) {\n    zunionInterGenericCommand(c,c->argv[1], SET_OP_UNION);\n}\n\nvoid zinterstoreCommand(client *c) {\n    zunionInterGenericCommand(c,c->argv[1], SET_OP_INTER);\n}\n\nvoid zrangeGenericCommand(client *c, int reverse) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    int withscores = 0;\n    long start;\n    long end;\n    long llen;\n    long rangelen;\n\n    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||\n        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;\n\n    if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,\"withscores\")) {\n        withscores = 1;\n    } else if (c->argc >= 5) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptyarray)) == NULL\n         || checkType(c,zobj,OBJ_ZSET)) return;\n\n    /* Sanitize indexes. */\n    llen = zsetLength(zobj);\n    if (start < 0) start = llen+start;\n    if (end < 0) end = llen+end;\n    if (start < 0) start = 0;\n\n    /* Invariant: start >= 0, so this test will be true when end < 0.\n     * The range is empty when start > end or start >= length. */\n    if (start > end || start >= llen) {\n        addReply(c,shared.emptyarray);\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. RESP3 clients\n     * will receive sub arrays with score->element, while RESP2 returned\n     * a flat array. */\n    if (withscores && c->resp == 2)\n        addReplyArrayLen(c, rangelen*2);\n    else\n        addReplyArrayLen(c, rangelen);\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        if (reverse)\n            eptr = ziplistIndex(zl,-2-(2*start));\n        else\n            eptr = ziplistIndex(zl,2*start);\n\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        while (rangelen--) {\n            serverAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            if (vstr == NULL)\n                addReplyBulkLongLong(c,vlong);\n            else\n                addReplyBulkCBuffer(c,vstr,vlen);\n            if (withscores) addReplyDouble(c,zzlGetScore(sptr));\n\n            if (reverse)\n                zzlPrev(zl,&eptr,&sptr);\n            else\n                zzlNext(zl,&eptr,&sptr);\n        }\n\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n        sds ele;\n\n        /* Check if starting point is trivial, before doing log(N) lookup. */\n        if (reverse) {\n            ln = zsl->tail;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,llen-start);\n        } else {\n            ln = zsl->header->level[0].forward;\n            if (start > 0)\n                ln = zslGetElementByRank(zsl,start+1);\n        }\n\n        while(rangelen--) {\n            serverAssertWithInfo(c,zobj,ln != NULL);\n            ele = ln->ele;\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            addReplyBulkCBuffer(c,ele,sdslen(ele));\n            if (withscores) addReplyDouble(c,ln->score);\n            ln = reverse ? ln->backward : ln->level[0].forward;\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n}\n\nvoid zrangeCommand(client *c) {\n    zrangeGenericCommand(c,0);\n}\n\nvoid zrevrangeCommand(client *c) {\n    zrangeGenericCommand(c,1);\n}\n\n/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE. */\nvoid genericZrangebyscoreCommand(client *c, int reverse) {\n    zrangespec range;\n    robj *key = c->argv[1];\n    robj *zobj;\n    long offset = 0, limit = -1;\n    int withscores = 0;\n    unsigned long rangelen = 0;\n    void *replylen = NULL;\n    int minidx, maxidx;\n\n    /* Parse the range arguments. */\n    if (reverse) {\n        /* Range is given as [max,min] */\n        maxidx = 2; minidx = 3;\n    } else {\n        /* Range is given as [min,max] */\n        minidx = 2; maxidx = 3;\n    }\n\n    if (zslParseRange(c->argv[minidx],c->argv[maxidx],&range) != C_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    /* Parse optional extra arguments. Note that ZCOUNT will exactly have\n     * 4 arguments, so we'll never enter the following code path. */\n    if (c->argc > 4) {\n        int remaining = c->argc - 4;\n        int pos = 4;\n\n        while (remaining) {\n            if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,\"withscores\")) {\n                pos++; remaining--;\n                withscores = 1;\n            } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,\"limit\")) {\n                if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL)\n                        != C_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL)\n                        != C_OK))\n                {\n                    return;\n                }\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    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptyarray)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n        double score;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            eptr = zzlLastInRange(zl,&range);\n        } else {\n            eptr = zzlFirstInRange(zl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (eptr == NULL) {\n            addReply(c,shared.emptyarray);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addReplyDeferredLen(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        while (eptr && limit--) {\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslValueGteMin(score,&range)) break;\n            } else {\n                if (!zslValueLteMax(score,&range)) break;\n            }\n\n            /* We know the element exists, so ziplistGet should always\n             * succeed */\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            rangelen++;\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            if (vstr == NULL) {\n                addReplyBulkLongLong(c,vlong);\n            } else {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            }\n            if (withscores) addReplyDouble(c,score);\n\n            /* Move to next node */\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            ln = zslLastInRange(zsl,&range);\n        } else {\n            ln = zslFirstInRange(zsl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (ln == NULL) {\n            addReply(c,shared.emptyarray);\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 = addReplyDeferredLen(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (ln && offset--) {\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n\n        while (ln && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zslValueGteMin(ln->score,&range)) break;\n            } else {\n                if (!zslValueLteMax(ln->score,&range)) break;\n            }\n\n            rangelen++;\n            if (withscores && c->resp > 2) addReplyArrayLen(c,2);\n            addReplyBulkCBuffer(c,ln->ele,sdslen(ln->ele));\n            if (withscores) addReplyDouble(c,ln->score);\n\n            /* Move to next node */\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    if (withscores && c->resp == 2) rangelen *= 2;\n    setDeferredArrayLen(c, replylen, rangelen);\n}\n\nvoid zrangebyscoreCommand(client *c) {\n    genericZrangebyscoreCommand(c,0);\n}\n\nvoid zrevrangebyscoreCommand(client *c) {\n    genericZrangebyscoreCommand(c,1);\n}\n\nvoid zcountCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zrangespec range;\n    unsigned long count = 0;\n\n    /* Parse the range arguments */\n    if (zslParseRange(c->argv[2],c->argv[3],&range) != C_OK) {\n        addReplyError(c,\"min or max is not a float\");\n        return;\n    }\n\n    /* Lookup the sorted set */\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET)) return;\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        double score;\n\n        /* Use the first element in range as the starting point */\n        eptr = zzlFirstInRange(zl,&range);\n\n        /* No \"first\" element */\n        if (eptr == NULL) {\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        sptr = ziplistNext(zl,eptr);\n        score = zzlGetScore(sptr);\n        serverAssertWithInfo(c,zobj,zslValueLteMax(score,&range));\n\n        /* Iterate over elements in range */\n        while (eptr) {\n            score = zzlGetScore(sptr);\n\n            /* Abort when the node is no longer in range. */\n            if (!zslValueLteMax(score,&range)) {\n                break;\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *zn;\n        unsigned long rank;\n\n        /* Find first element in range */\n        zn = zslFirstInRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        if (zn != NULL) {\n            rank = zslGetRank(zsl, zn->score, zn->ele);\n            count = (zsl->length - (rank - 1));\n\n            /* Find last element in range */\n            zn = zslLastInRange(zsl, &range);\n\n            /* Use rank of last element, if any, to determine the actual count */\n            if (zn != NULL) {\n                rank = zslGetRank(zsl, zn->score, zn->ele);\n                count -= (zsl->length - rank);\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    addReplyLongLong(c, count);\n}\n\nvoid zlexcountCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    zlexrangespec range;\n    unsigned long count = 0;\n\n    /* Parse the range arguments */\n    if (zslParseLexRange(c->argv[2],c->argv[3],&range) != C_OK) {\n        addReplyError(c,\"min or max not valid string range item\");\n        return;\n    }\n\n    /* Lookup the sorted set */\n    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||\n        checkType(c, zobj, OBJ_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n\n        /* Use the first element in range as the starting point */\n        eptr = zzlFirstInLexRange(zl,&range);\n\n        /* No \"first\" element */\n        if (eptr == NULL) {\n            zslFreeLexRange(&range);\n            addReply(c, shared.czero);\n            return;\n        }\n\n        /* First element is in range */\n        sptr = ziplistNext(zl,eptr);\n        serverAssertWithInfo(c,zobj,zzlLexValueLteMax(eptr,&range));\n\n        /* Iterate over elements in range */\n        while (eptr) {\n            /* Abort when the node is no longer in range. */\n            if (!zzlLexValueLteMax(eptr,&range)) {\n                break;\n            } else {\n                count++;\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *zn;\n        unsigned long rank;\n\n        /* Find first element in range */\n        zn = zslFirstInLexRange(zsl, &range);\n\n        /* Use rank of first element, if any, to determine preliminary count */\n        if (zn != NULL) {\n            rank = zslGetRank(zsl, zn->score, zn->ele);\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->ele);\n                count -= (zsl->length - rank);\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    addReplyLongLong(c, count);\n}\n\n/* This command implements ZRANGEBYLEX, ZREVRANGEBYLEX. */\nvoid genericZrangebylexCommand(client *c, int reverse) {\n    zlexrangespec range;\n    robj *key = c->argv[1];\n    robj *zobj;\n    long offset = 0, limit = -1;\n    unsigned long rangelen = 0;\n    void *replylen = NULL;\n    int minidx, maxidx;\n\n    /* Parse the range arguments. */\n    if (reverse) {\n        /* Range is given as [max,min] */\n        maxidx = 2; minidx = 3;\n    } else {\n        /* Range is given as [min,max] */\n        minidx = 2; maxidx = 3;\n    }\n\n    if (zslParseLexRange(c->argv[minidx],c->argv[maxidx],&range) != C_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) != C_OK) ||\n                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != C_OK)) {\n                    zslFreeLexRange(&range);\n                    return;\n                }\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.emptyarray)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET))\n    {\n        zslFreeLexRange(&range);\n        return;\n    }\n\n    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n        unsigned char *zl = zobj->ptr;\n        unsigned char *eptr, *sptr;\n        unsigned char *vstr;\n        unsigned int vlen;\n        long long vlong;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            eptr = zzlLastInLexRange(zl,&range);\n        } else {\n            eptr = zzlFirstInLexRange(zl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (eptr == NULL) {\n            addReply(c,shared.emptyarray);\n            zslFreeLexRange(&range);\n            return;\n        }\n\n        /* Get score pointer for the first element. */\n        serverAssertWithInfo(c,zobj,eptr != NULL);\n        sptr = ziplistNext(zl,eptr);\n\n        /* We don't know in advance how many matching elements there are in the\n         * list, so we push this object that will represent the multi-bulk\n         * length in the output buffer, and will \"fix\" it later */\n        replylen = addReplyDeferredLen(c);\n\n        /* If there is an offset, just traverse the number of elements without\n         * checking the score because that is done in the next loop. */\n        while (eptr && offset--) {\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n\n        while (eptr && limit--) {\n            /* Abort when the node is no longer in range. */\n            if (reverse) {\n                if (!zzlLexValueGteMin(eptr,&range)) break;\n            } else {\n                if (!zzlLexValueLteMax(eptr,&range)) break;\n            }\n\n            /* We know the element exists, so ziplistGet should always\n             * succeed. */\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n\n            rangelen++;\n            if (vstr == NULL) {\n                addReplyBulkLongLong(c,vlong);\n            } else {\n                addReplyBulkCBuffer(c,vstr,vlen);\n            }\n\n            /* Move to next node */\n            if (reverse) {\n                zzlPrev(zl,&eptr,&sptr);\n            } else {\n                zzlNext(zl,&eptr,&sptr);\n            }\n        }\n    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n        zset *zs = zobj->ptr;\n        zskiplist *zsl = zs->zsl;\n        zskiplistNode *ln;\n\n        /* If reversed, get the last node in range as starting point. */\n        if (reverse) {\n            ln = zslLastInLexRange(zsl,&range);\n        } else {\n            ln = zslFirstInLexRange(zsl,&range);\n        }\n\n        /* No \"first\" element in the specified interval. */\n        if (ln == NULL) {\n            addReply(c,shared.emptyarray);\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 = addReplyDeferredLen(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->ele,&range)) break;\n            } else {\n                if (!zslLexValueLteMax(ln->ele,&range)) break;\n            }\n\n            rangelen++;\n            addReplyBulkCBuffer(c,ln->ele,sdslen(ln->ele));\n\n            /* Move to next node */\n            if (reverse) {\n                ln = ln->backward;\n            } else {\n                ln = ln->level[0].forward;\n            }\n        }\n    } else {\n        serverPanic(\"Unknown sorted set encoding\");\n    }\n\n    zslFreeLexRange(&range);\n    setDeferredArrayLen(c, replylen, rangelen);\n}\n\nvoid zrangebylexCommand(client *c) {\n    genericZrangebylexCommand(c,0);\n}\n\nvoid zrevrangebylexCommand(client *c) {\n    genericZrangebylexCommand(c,1);\n}\n\nvoid zcardCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.czero)) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    addReplyLongLong(c,zsetLength(zobj));\n}\n\nvoid zscoreCommand(client *c) {\n    robj *key = c->argv[1];\n    robj *zobj;\n    double score;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    if (zsetScore(zobj,c->argv[2]->ptr,&score) == C_ERR) {\n        addReplyNull(c);\n    } else {\n        addReplyDouble(c,score);\n    }\n}\n\nvoid zrankGenericCommand(client *c, int reverse) {\n    robj *key = c->argv[1];\n    robj *ele = c->argv[2];\n    robj *zobj;\n    long rank;\n\n    if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||\n        checkType(c,zobj,OBJ_ZSET)) return;\n\n    serverAssertWithInfo(c,ele,sdsEncodedObject(ele));\n    rank = zsetRank(zobj,ele->ptr,reverse);\n    if (rank >= 0) {\n        addReplyLongLong(c,rank);\n    } else {\n        addReplyNull(c);\n    }\n}\n\nvoid zrankCommand(client *c) {\n    zrankGenericCommand(c, 0);\n}\n\nvoid zrevrankCommand(client *c) {\n    zrankGenericCommand(c, 1);\n}\n\nvoid zscanCommand(client *c) {\n    robj *o;\n    unsigned long cursor;\n\n    if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;\n    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||\n        checkType(c,o,OBJ_ZSET)) return;\n    scanGenericCommand(c,o,cursor);\n}\n\n/* This command implements the generic zpop operation, used by:\n * ZPOPMIN, ZPOPMAX, BZPOPMIN and BZPOPMAX. This function is also used\n * inside blocked.c in the unblocking stage of BZPOPMIN and BZPOPMAX.\n *\n * If 'emitkey' is true also the key name is emitted, useful for the blocking\n * behavior of BZPOP[MIN|MAX], since we can block into multiple keys.\n *\n * The synchronous version instead does not need to emit the key, but may\n * use the 'count' argument to return multiple items if available. */\nvoid genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey, robj *countarg) {\n    int idx;\n    robj *key = NULL;\n    robj *zobj = NULL;\n    sds ele;\n    double score;\n    long count = 1;\n\n    /* If a count argument as passed, parse it or return an error. */\n    if (countarg) {\n        if (getLongFromObjectOrReply(c,countarg,&count,NULL) != C_OK)\n            return;\n        if (count <= 0) {\n            addReply(c,shared.emptyarray);\n            return;\n        }\n    }\n\n    /* Check type and break on the first error, otherwise identify candidate. */\n    idx = 0;\n    while (idx < keyc) {\n        key = keyv[idx++];\n        zobj = lookupKeyWrite(c->db,key);\n        if (!zobj) continue;\n        if (checkType(c,zobj,OBJ_ZSET)) return;\n        break;\n    }\n\n    /* No candidate for zpopping, return empty. */\n    if (!zobj) {\n        addReply(c,shared.emptyarray);\n        return;\n    }\n\n    void *arraylen_ptr = addReplyDeferredLen(c);\n    long arraylen = 0;\n\n    /* We emit the key only for the blocking variant. */\n    if (emitkey) addReplyBulk(c,key);\n\n    /* Remove the element. */\n    do {\n        if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {\n            unsigned char *zl = zobj->ptr;\n            unsigned char *eptr, *sptr;\n            unsigned char *vstr;\n            unsigned int vlen;\n            long long vlong;\n\n            /* Get the first or last element in the sorted set. */\n            eptr = ziplistIndex(zl,where == ZSET_MAX ? -2 : 0);\n            serverAssertWithInfo(c,zobj,eptr != NULL);\n            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));\n            if (vstr == NULL)\n                ele = sdsfromlonglong(vlong);\n            else\n                ele = sdsnewlen(vstr,vlen);\n\n            /* Get the score. */\n            sptr = ziplistNext(zl,eptr);\n            serverAssertWithInfo(c,zobj,sptr != NULL);\n            score = zzlGetScore(sptr);\n        } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {\n            zset *zs = zobj->ptr;\n            zskiplist *zsl = zs->zsl;\n            zskiplistNode *zln;\n\n            /* Get the first or last element in the sorted set. */\n            zln = (where == ZSET_MAX ? zsl->tail :\n                                       zsl->header->level[0].forward);\n\n            /* There must be an element in the sorted set. */\n            serverAssertWithInfo(c,zobj,zln != NULL);\n            ele = sdsdup(zln->ele);\n            score = zln->score;\n        } else {\n            serverPanic(\"Unknown sorted set encoding\");\n        }\n\n        serverAssertWithInfo(c,zobj,zsetDel(zobj,ele));\n        server.dirty++;\n\n        if (arraylen == 0) { /* Do this only for the first iteration. */\n            char *events[2] = {\"zpopmin\",\"zpopmax\"};\n            notifyKeyspaceEvent(NOTIFY_ZSET,events[where],key,c->db->id);\n            signalModifiedKey(c,c->db,key);\n        }\n\n        addReplyBulkCBuffer(c,ele,sdslen(ele));\n        addReplyDouble(c,score);\n        sdsfree(ele);\n        arraylen += 2;\n\n        /* Remove the key, if indeed needed. */\n        if (zsetLength(zobj) == 0) {\n            dbDelete(c->db,key);\n            notifyKeyspaceEvent(NOTIFY_GENERIC,\"del\",key,c->db->id);\n            break;\n        }\n    } while(--count);\n\n    setDeferredArrayLen(c,arraylen_ptr,arraylen + (emitkey != 0));\n}\n\n/* ZPOPMIN key [<count>] */\nvoid zpopminCommand(client *c) {\n    if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    genericZpopCommand(c,&c->argv[1],1,ZSET_MIN,0,\n        c->argc == 3 ? c->argv[2] : NULL);\n}\n\n/* ZMAXPOP key [<count>] */\nvoid zpopmaxCommand(client *c) {\n    if (c->argc > 3) {\n        addReply(c,shared.syntaxerr);\n        return;\n    }\n    genericZpopCommand(c,&c->argv[1],1,ZSET_MAX,0,\n        c->argc == 3 ? c->argv[2] : NULL);\n}\n\n/* BZPOPMIN / BZPOPMAX actual implementation. */\nvoid blockingGenericZpopCommand(client *c, int where) {\n    robj *o;\n    mstime_t timeout;\n    int j;\n\n    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)\n        != C_OK) return;\n\n    for (j = 1; j < c->argc-1; j++) {\n        o = lookupKeyWrite(c->db,c->argv[j]);\n        if (o != NULL) {\n            if (o->type != OBJ_ZSET) {\n                addReply(c,shared.wrongtypeerr);\n                return;\n            } else {\n                if (zsetLength(o) != 0) {\n                    /* Non empty zset, this is like a normal ZPOP[MIN|MAX]. */\n                    genericZpopCommand(c,&c->argv[j],1,where,1,NULL);\n                    /* Replicate it as an ZPOP[MIN|MAX] instead of BZPOP[MIN|MAX]. */\n                    rewriteClientCommandVector(c,2,\n                        where == ZSET_MAX ? shared.zpopmax : shared.zpopmin,\n                        c->argv[j]);\n                    return;\n                }\n            }\n        }\n    }\n\n    /* If we are inside a MULTI/EXEC and the zset is empty the only thing\n     * we can do is treating it as a timeout (even with timeout 0). */\n    if (c->flags & CLIENT_MULTI) {\n        addReplyNullArray(c);\n        return;\n    }\n\n    /* If the keys do not exist we must block */\n    blockForKeys(c,BLOCKED_ZSET,c->argv + 1,c->argc - 2,timeout,NULL,NULL);\n}\n\n// BZPOPMIN key [key ...] timeout\nvoid bzpopminCommand(client *c) {\n    blockingGenericZpopCommand(c,ZSET_MIN);\n}\n\n// BZPOPMAX key [key ...] timeout\nvoid bzpopmaxCommand(client *c) {\n    blockingGenericZpopCommand(c,ZSET_MAX);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/src/timeout.c",
    "content": "/* Copyright (c) 2009-2020, 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 \"server.h\"\n#include \"cluster.h\"\n\n/* ========================== Clients timeouts ============================= */\n\n/* Check if this blocked client timedout (does nothing if the client is\n * not blocked right now). If so send a reply, unblock it, and return 1.\n * Otherwise 0 is returned and no operation is performed. */\nint checkBlockedClientTimeout(client *c, mstime_t now) {\n    if (c->flags & CLIENT_BLOCKED &&\n        c->bpop.timeout != 0\n        && c->bpop.timeout < now)\n    {\n        /* Handle blocking operation specific timeout. */\n        replyToBlockedClientTimedOut(c);\n        unblockClient(c);\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\n/* Check for timeouts. Returns non-zero if the client was terminated.\n * The function gets the current time in milliseconds as argument since\n * it gets called multiple times in a loop, so calling gettimeofday() for\n * each iteration would be costly without any actual gain. */\nint clientsCronHandleTimeout(client *c, mstime_t now_ms) {\n    time_t now = now_ms/1000;\n\n    if (server.maxidletime &&\n        /* This handles the idle clients connection timeout if set. */\n        !(c->flags & CLIENT_SLAVE) &&   /* No timeout for slaves and monitors */\n        !(c->flags & CLIENT_MASTER) &&  /* No timeout for masters */\n        !(c->flags & CLIENT_BLOCKED) && /* No timeout for BLPOP */\n        !(c->flags & CLIENT_PUBSUB) &&  /* No timeout for Pub/Sub clients */\n        (now - c->lastinteraction > server.maxidletime))\n    {\n        serverLog(LL_VERBOSE,\"Closing idle client\");\n        freeClient(c);\n        return 1;\n    } else if (c->flags & CLIENT_BLOCKED) {\n        /* Cluster: handle unblock & redirect of clients blocked\n         * into keys no longer served by this server. */\n        if (server.cluster_enabled) {\n            if (clusterRedirectBlockedClientIfNeeded(c))\n                unblockClient(c);\n        }\n    }\n    return 0;\n}\n\n/* For blocked clients timeouts we populate a radix tree of 128 bit keys\n * composed as such:\n *\n *  [8 byte big endian expire time]+[8 byte client ID]\n *\n * We don't do any cleanup in the Radix tree: when we run the clients that\n * reached the timeout already, if they are no longer existing or no longer\n * blocked with such timeout, we just go forward.\n *\n * Every time a client blocks with a timeout, we add the client in\n * the tree. In beforeSleep() we call handleBlockedClientsTimeout() to run\n * the tree and unblock the clients. */\n\n#define CLIENT_ST_KEYLEN 16    /* 8 bytes mstime + 8 bytes client ID. */\n\n/* Given client ID and timeout, write the resulting radix tree key in buf. */\nvoid encodeTimeoutKey(unsigned char *buf, uint64_t timeout, client *c) {\n    timeout = htonu64(timeout);\n    memcpy(buf,&timeout,sizeof(timeout));\n    memcpy(buf+8,&c,sizeof(c));\n    if (sizeof(c) == 4) memset(buf+12,0,4); /* Zero padding for 32bit target. */\n}\n\n/* Given a key encoded with encodeTimeoutKey(), resolve the fields and write\n * the timeout into *toptr and the client pointer into *cptr. */\nvoid decodeTimeoutKey(unsigned char *buf, uint64_t *toptr, client **cptr) {\n    memcpy(toptr,buf,sizeof(*toptr));\n    *toptr = ntohu64(*toptr);\n    memcpy(cptr,buf+8,sizeof(*cptr));\n}\n\n/* Add the specified client id / timeout as a key in the radix tree we use\n * to handle blocked clients timeouts. The client is not added to the list\n * if its timeout is zero (block forever). */\nvoid addClientToTimeoutTable(client *c) {\n    if (c->bpop.timeout == 0) return;\n    uint64_t timeout = c->bpop.timeout;\n    unsigned char buf[CLIENT_ST_KEYLEN];\n    encodeTimeoutKey(buf,timeout,c);\n    if (raxTryInsert(server.clients_timeout_table,buf,sizeof(buf),NULL,NULL))\n        c->flags |= CLIENT_IN_TO_TABLE;\n}\n\n/* Remove the client from the table when it is unblocked for reasons\n * different than timing out. */\nvoid removeClientFromTimeoutTable(client *c) {\n    if (!(c->flags & CLIENT_IN_TO_TABLE)) return;\n    c->flags &= ~CLIENT_IN_TO_TABLE;\n    uint64_t timeout = c->bpop.timeout;\n    unsigned char buf[CLIENT_ST_KEYLEN];\n    encodeTimeoutKey(buf,timeout,c);\n    raxRemove(server.clients_timeout_table,buf,sizeof(buf),NULL);\n}\n\n/* This function is called in beforeSleep() in order to unblock clients\n * that are waiting in blocking operations with a timeout set. */\nvoid handleBlockedClientsTimeout(void) {\n    if (raxSize(server.clients_timeout_table) == 0) return;\n    uint64_t now = mstime();\n    raxIterator ri;\n    raxStart(&ri,server.clients_timeout_table);\n    raxSeek(&ri,\"^\",NULL,0);\n\n    while(raxNext(&ri)) {\n        uint64_t timeout;\n        client *c;\n        decodeTimeoutKey(ri.key,&timeout,&c);\n        if (timeout >= now) break; /* All the timeouts are in the future. */\n        c->flags &= ~CLIENT_IN_TO_TABLE;\n        checkBlockedClientTimeout(c,now);\n        raxRemove(server.clients_timeout_table,ri.key,ri.key_len,NULL);\n        raxSeek(&ri,\"^\",NULL,0);\n    }\n}\n\n/* Get a timeout value from an object and store it into 'timeout'.\n * The final timeout is always stored as milliseconds as a time where the\n * timeout will expire, however the parsing is performed according to\n * the 'unit' that can be seconds or milliseconds.\n *\n * Note that if the timeout is zero (usually from the point of view of\n * commands API this means no timeout) the value stored into 'timeout'\n * is zero. */\nint getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit) {\n    long long tval;\n    long double ftval;\n\n    if (unit == UNIT_SECONDS) {\n        if (getLongDoubleFromObjectOrReply(c,object,&ftval,\n            \"timeout is not a float or out of range\") != C_OK)\n            return C_ERR;\n        tval = (long long) (ftval * 1000.0);\n    } else {\n        if (getLongLongFromObjectOrReply(c,object,&tval,\n            \"timeout is not an integer or out of range\") != C_OK)\n            return C_ERR;\n    }\n\n    if (tval < 0) {\n        addReplyError(c,\"timeout is negative\");\n        return C_ERR;\n    }\n\n    if (tval > 0) {\n        tval += mstime();\n    }\n    *timeout = tval;\n\n    return C_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/tls.c",
    "content": "/*\n * Copyright (c) 2019, Redis Labs\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 \"server.h\"\n#include \"connhelpers.h\"\n#include \"adlist.h\"\n\n#ifdef USE_OPENSSL\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <openssl/rand.h>\n\n#define REDIS_TLS_PROTO_TLSv1       (1<<0)\n#define REDIS_TLS_PROTO_TLSv1_1     (1<<1)\n#define REDIS_TLS_PROTO_TLSv1_2     (1<<2)\n#define REDIS_TLS_PROTO_TLSv1_3     (1<<3)\n\n/* Use safe defaults */\n#ifdef TLS1_3_VERSION\n#define REDIS_TLS_PROTO_DEFAULT     (REDIS_TLS_PROTO_TLSv1_2|REDIS_TLS_PROTO_TLSv1_3)\n#else\n#define REDIS_TLS_PROTO_DEFAULT     (REDIS_TLS_PROTO_TLSv1_2)\n#endif\n\nextern ConnectionType CT_Socket;\n\nSSL_CTX *redis_tls_ctx;\n\nstatic int parseProtocolsConfig(const char *str) {\n    int i, count = 0;\n    int protocols = 0;\n\n    if (!str) return REDIS_TLS_PROTO_DEFAULT;\n    sds *tokens = sdssplitlen(str, strlen(str), \" \", 1, &count);\n\n    if (!tokens) { \n        serverLog(LL_WARNING, \"Invalid tls-protocols configuration string\");\n        return -1;\n    }\n    for (i = 0; i < count; i++) {\n        if (!strcasecmp(tokens[i], \"tlsv1\")) protocols |= REDIS_TLS_PROTO_TLSv1;\n        else if (!strcasecmp(tokens[i], \"tlsv1.1\")) protocols |= REDIS_TLS_PROTO_TLSv1_1;\n        else if (!strcasecmp(tokens[i], \"tlsv1.2\")) protocols |= REDIS_TLS_PROTO_TLSv1_2;\n        else if (!strcasecmp(tokens[i], \"tlsv1.3\")) {\n#ifdef TLS1_3_VERSION\n            protocols |= REDIS_TLS_PROTO_TLSv1_3;\n#else\n            serverLog(LL_WARNING, \"TLSv1.3 is specified in tls-protocols but not supported by OpenSSL.\");\n            protocols = -1;\n            break;\n#endif\n        } else {\n            serverLog(LL_WARNING, \"Invalid tls-protocols specified. \"\n                    \"Use a combination of 'TLSv1', 'TLSv1.1', 'TLSv1.2' and 'TLSv1.3'.\");\n            protocols = -1;\n            break;\n        }\n    }\n    sdsfreesplitres(tokens, count);\n\n    return protocols;\n}\n\n/* list of connections with pending data already read from the socket, but not\n * served to the reader yet. */\nstatic list *pending_list = NULL;\n\n/**\n * OpenSSL global initialization and locking handling callbacks.\n * Note that this is only required for OpenSSL < 1.1.0.\n */\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#define USE_CRYPTO_LOCKS\n#endif\n\n#ifdef USE_CRYPTO_LOCKS\n\nstatic pthread_mutex_t *openssl_locks;\n\nstatic void sslLockingCallback(int mode, int lock_id, const char *f, int line) {\n    pthread_mutex_t *mt = openssl_locks + lock_id;\n\n    if (mode & CRYPTO_LOCK) {\n        pthread_mutex_lock(mt);\n    } else {\n        pthread_mutex_unlock(mt);\n    }\n\n    (void)f;\n    (void)line;\n}\n\nstatic void initCryptoLocks(void) {\n    unsigned i, nlocks;\n    if (CRYPTO_get_locking_callback() != NULL) {\n        /* Someone already set the callback before us. Don't destroy it! */\n        return;\n    }\n    nlocks = CRYPTO_num_locks();\n    openssl_locks = zmalloc(sizeof(*openssl_locks) * nlocks);\n    for (i = 0; i < nlocks; i++) {\n        pthread_mutex_init(openssl_locks + i, NULL);\n    }\n    CRYPTO_set_locking_callback(sslLockingCallback);\n}\n#endif /* USE_CRYPTO_LOCKS */\n\nvoid tlsInit(void) {\n    ERR_load_crypto_strings();\n    SSL_load_error_strings();\n    SSL_library_init();\n\n#ifdef USE_CRYPTO_LOCKS\n    initCryptoLocks();\n#endif\n\n    if (!RAND_poll()) {\n        serverLog(LL_WARNING, \"OpenSSL: Failed to seed random number generator.\");\n    }\n\n    pending_list = listCreate();\n\n    /* Server configuration */\n    server.tls_auth_clients = 1;    /* Secure by default */\n}\n\n/* Attempt to configure/reconfigure TLS. This operation is atomic and will\n * leave the SSL_CTX unchanged if fails.\n */\nint tlsConfigure(redisTLSContextConfig *ctx_config) {\n    char errbuf[256];\n    SSL_CTX *ctx = NULL;\n\n    if (!ctx_config->cert_file) {\n        serverLog(LL_WARNING, \"No tls-cert-file configured!\");\n        goto error;\n    }\n\n    if (!ctx_config->key_file) {\n        serverLog(LL_WARNING, \"No tls-key-file configured!\");\n        goto error;\n    }\n\n    if (!ctx_config->ca_cert_file && !ctx_config->ca_cert_dir) {\n        serverLog(LL_WARNING, \"Either tls-ca-cert-file or tls-ca-cert-dir must be configured!\");\n        goto error;\n    }\n\n    ctx = SSL_CTX_new(SSLv23_method());\n\n    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);\n    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);\n\n#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS\n    SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);\n#endif\n\n    int protocols = parseProtocolsConfig(ctx_config->protocols);\n    if (protocols == -1) goto error;\n\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1_1))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);\n#ifdef SSL_OP_NO_TLSv1_2\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1_2))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);\n#endif\n#ifdef SSL_OP_NO_TLSv1_3\n    if (!(protocols & REDIS_TLS_PROTO_TLSv1_3))\n        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3);\n#endif\n\n#ifdef SSL_OP_NO_COMPRESSION\n    SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);\n#endif\n\n#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION\n    SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);\n#endif\n\n    if (ctx_config->prefer_server_ciphers)\n        SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);\n\n    SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);\n#if defined(SSL_CTX_set_ecdh_auto)\n    SSL_CTX_set_ecdh_auto(ctx, 1);\n#endif\n\n    if (SSL_CTX_use_certificate_file(ctx, ctx_config->cert_file, SSL_FILETYPE_PEM) <= 0) {\n        ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n        serverLog(LL_WARNING, \"Failed to load certificate: %s: %s\", ctx_config->cert_file, errbuf);\n        goto error;\n    }\n        \n    if (SSL_CTX_use_PrivateKey_file(ctx, ctx_config->key_file, SSL_FILETYPE_PEM) <= 0) {\n        ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n        serverLog(LL_WARNING, \"Failed to load private key: %s: %s\", ctx_config->key_file, errbuf);\n        goto error;\n    }\n    \n    if (SSL_CTX_load_verify_locations(ctx, ctx_config->ca_cert_file, ctx_config->ca_cert_dir) <= 0) {\n        ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n        serverLog(LL_WARNING, \"Failed to configure CA certificate(s) file/directory: %s\", errbuf);\n        goto error;\n    }\n\n    if (ctx_config->dh_params_file) {\n        FILE *dhfile = fopen(ctx_config->dh_params_file, \"r\");\n        DH *dh = NULL;\n        if (!dhfile) {\n            serverLog(LL_WARNING, \"Failed to load %s: %s\", ctx_config->dh_params_file, strerror(errno));\n            goto error;\n        }\n\n        dh = PEM_read_DHparams(dhfile, NULL, NULL, NULL);\n        fclose(dhfile);\n        if (!dh) {\n            serverLog(LL_WARNING, \"%s: failed to read DH params.\", ctx_config->dh_params_file);\n            goto error;\n        }\n\n        if (SSL_CTX_set_tmp_dh(ctx, dh) <= 0) {\n            ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));\n            serverLog(LL_WARNING, \"Failed to load DH params file: %s: %s\", ctx_config->dh_params_file, errbuf);\n            DH_free(dh);\n            goto error;\n        }\n\n        DH_free(dh);\n    }\n\n    if (ctx_config->ciphers && !SSL_CTX_set_cipher_list(ctx, ctx_config->ciphers)) {\n        serverLog(LL_WARNING, \"Failed to configure ciphers: %s\", ctx_config->ciphers);\n        goto error;\n    }\n\n#ifdef TLS1_3_VERSION\n    if (ctx_config->ciphersuites && !SSL_CTX_set_ciphersuites(ctx, ctx_config->ciphersuites)) {\n        serverLog(LL_WARNING, \"Failed to configure ciphersuites: %s\", ctx_config->ciphersuites);\n        goto error;\n    }\n#endif\n\n    SSL_CTX_free(redis_tls_ctx);\n    redis_tls_ctx = ctx;\n\n    return C_OK;\n\nerror:\n    if (ctx) SSL_CTX_free(ctx);\n    return C_ERR;\n}\n\n#ifdef TLS_DEBUGGING\n#define TLSCONN_DEBUG(fmt, ...) \\\n    serverLog(LL_DEBUG, \"TLSCONN: \" fmt, __VA_ARGS__)\n#else\n#define TLSCONN_DEBUG(fmt, ...)\n#endif\n\nConnectionType CT_TLS;\n\n/* Normal socket connections have a simple events/handler correlation.\n *\n * With TLS connections we need to handle cases where during a logical read\n * or write operation, the SSL library asks to block for the opposite\n * socket operation.\n *\n * When this happens, we need to do two things:\n * 1. Make sure we register for the even.\n * 2. Make sure we know which handler needs to execute when the\n *    event fires.  That is, if we notify the caller of a write operation\n *    that it blocks, and SSL asks for a read, we need to trigger the\n *    write handler again on the next read event.\n *\n */\n\ntypedef enum {\n    WANT_READ = 1,\n    WANT_WRITE\n} WantIOType;\n\n#define TLS_CONN_FLAG_READ_WANT_WRITE   (1<<0)\n#define TLS_CONN_FLAG_WRITE_WANT_READ   (1<<1)\n#define TLS_CONN_FLAG_FD_SET            (1<<2)\n\ntypedef struct tls_connection {\n    connection c;\n    int flags;\n    SSL *ssl;\n    char *ssl_error;\n    listNode *pending_list_node;\n} tls_connection;\n\nconnection *connCreateTLS(void) {\n    tls_connection *conn = zcalloc(sizeof(tls_connection));\n    conn->c.type = &CT_TLS;\n    conn->c.fd = -1;\n    conn->ssl = SSL_new(redis_tls_ctx);\n    return (connection *) conn;\n}\n\nconnection *connCreateAcceptedTLS(int fd, int require_auth) {\n    tls_connection *conn = (tls_connection *) connCreateTLS();\n    conn->c.fd = fd;\n    conn->c.state = CONN_STATE_ACCEPTING;\n\n    if (!require_auth) {\n        /* We still verify certificates if provided, but don't require them.\n         */\n        SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, NULL);\n    }\n\n    SSL_set_fd(conn->ssl, conn->c.fd);\n    SSL_set_accept_state(conn->ssl);\n\n    return (connection *) conn;\n}\n\nstatic void tlsEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask);\n\n/* Process the return code received from OpenSSL>\n * Update the want parameter with expected I/O.\n * Update the connection's error state if a real error has occured.\n * Returns an SSL error code, or 0 if no further handling is required.\n */\nstatic int handleSSLReturnCode(tls_connection *conn, int ret_value, WantIOType *want) {\n    if (ret_value <= 0) {\n        int ssl_err = SSL_get_error(conn->ssl, ret_value);\n        switch (ssl_err) {\n            case SSL_ERROR_WANT_WRITE:\n                *want = WANT_WRITE;\n                return 0;\n            case SSL_ERROR_WANT_READ:\n                *want = WANT_READ;\n                return 0;\n            case SSL_ERROR_SYSCALL:\n                conn->c.last_errno = errno;\n                if (conn->ssl_error) zfree(conn->ssl_error);\n                conn->ssl_error = errno ? zstrdup(strerror(errno)) : NULL;\n                break;\n            default:\n                /* Error! */\n                conn->c.last_errno = 0;\n                if (conn->ssl_error) zfree(conn->ssl_error);\n                conn->ssl_error = zmalloc(512);\n                ERR_error_string_n(ERR_get_error(), conn->ssl_error, 512);\n                break;\n        }\n\n        return ssl_err;\n    }\n\n    return 0;\n}\n\nvoid registerSSLEvent(tls_connection *conn, WantIOType want) {\n    int mask = aeGetFileEvents(server.el, conn->c.fd);\n\n    switch (want) {\n        case WANT_READ:\n            if (mask & AE_WRITABLE) aeDeleteFileEvent(server.el, conn->c.fd, AE_WRITABLE);\n            if (!(mask & AE_READABLE)) aeCreateFileEvent(server.el, conn->c.fd, AE_READABLE,\n                        tlsEventHandler, conn);\n            break;\n        case WANT_WRITE:\n            if (mask & AE_READABLE) aeDeleteFileEvent(server.el, conn->c.fd, AE_READABLE);\n            if (!(mask & AE_WRITABLE)) aeCreateFileEvent(server.el, conn->c.fd, AE_WRITABLE,\n                        tlsEventHandler, conn);\n            break;\n        default:\n            serverAssert(0);\n            break;\n    }\n}\n\nvoid updateSSLEvent(tls_connection *conn) {\n    int mask = aeGetFileEvents(server.el, conn->c.fd);\n    int need_read = conn->c.read_handler || (conn->flags & TLS_CONN_FLAG_WRITE_WANT_READ);\n    int need_write = conn->c.write_handler || (conn->flags & TLS_CONN_FLAG_READ_WANT_WRITE);\n\n    if (need_read && !(mask & AE_READABLE))\n        aeCreateFileEvent(server.el, conn->c.fd, AE_READABLE, tlsEventHandler, conn);\n    if (!need_read && (mask & AE_READABLE))\n        aeDeleteFileEvent(server.el, conn->c.fd, AE_READABLE);\n\n    if (need_write && !(mask & AE_WRITABLE))\n        aeCreateFileEvent(server.el, conn->c.fd, AE_WRITABLE, tlsEventHandler, conn);\n    if (!need_write && (mask & AE_WRITABLE))\n        aeDeleteFileEvent(server.el, conn->c.fd, AE_WRITABLE);\n}\n\nstatic void tlsHandleEvent(tls_connection *conn, int mask) {\n    int ret;\n\n    TLSCONN_DEBUG(\"tlsEventHandler(): fd=%d, state=%d, mask=%d, r=%d, w=%d, flags=%d\",\n            fd, conn->c.state, mask, conn->c.read_handler != NULL, conn->c.write_handler != NULL,\n            conn->flags);\n\n    ERR_clear_error();\n\n    switch (conn->c.state) {\n        case CONN_STATE_CONNECTING:\n            if (connGetSocketError((connection *) conn)) {\n                conn->c.last_errno = errno;\n                conn->c.state = CONN_STATE_ERROR;\n            } else {\n                if (!(conn->flags & TLS_CONN_FLAG_FD_SET)) {\n                    SSL_set_fd(conn->ssl, conn->c.fd);\n                    conn->flags |= TLS_CONN_FLAG_FD_SET;\n                }\n                ret = SSL_connect(conn->ssl);\n                if (ret <= 0) {\n                    WantIOType want = 0;\n                    if (!handleSSLReturnCode(conn, ret, &want)) {\n                        registerSSLEvent(conn, want);\n\n                        /* Avoid hitting UpdateSSLEvent, which knows nothing\n                         * of what SSL_connect() wants and instead looks at our\n                         * R/W handlers.\n                         */\n                        return;\n                    }\n\n                    /* If not handled, it's an error */\n                    conn->c.state = CONN_STATE_ERROR;\n                } else {\n                    conn->c.state = CONN_STATE_CONNECTED;\n                }\n            }\n\n            if (!callHandler((connection *) conn, conn->c.conn_handler)) return;\n            conn->c.conn_handler = NULL;\n            break;\n        case CONN_STATE_ACCEPTING:\n            ret = SSL_accept(conn->ssl);\n            if (ret <= 0) {\n                WantIOType want = 0;\n                if (!handleSSLReturnCode(conn, ret, &want)) {\n                    /* Avoid hitting UpdateSSLEvent, which knows nothing\n                     * of what SSL_connect() wants and instead looks at our\n                     * R/W handlers.\n                     */\n                    registerSSLEvent(conn, want);\n                    return;\n                }\n\n                /* If not handled, it's an error */\n                conn->c.state = CONN_STATE_ERROR;\n            } else {\n                conn->c.state = CONN_STATE_CONNECTED;\n            }\n\n            if (!callHandler((connection *) conn, conn->c.conn_handler)) return;\n            conn->c.conn_handler = NULL;\n            break;\n        case CONN_STATE_CONNECTED:\n        {\n            int call_read = ((mask & AE_READABLE) && conn->c.read_handler) ||\n                ((mask & AE_WRITABLE) && (conn->flags & TLS_CONN_FLAG_READ_WANT_WRITE));\n            int call_write = ((mask & AE_WRITABLE) && conn->c.write_handler) ||\n                ((mask & AE_READABLE) && (conn->flags & TLS_CONN_FLAG_WRITE_WANT_READ));\n\n            /* Normally we execute the readable event first, and the writable\n             * event laster. This is useful as sometimes we may be able\n             * to serve the reply of a query immediately after processing the\n             * query.\n             *\n             * However if WRITE_BARRIER is set in the mask, our application is\n             * asking us to do the reverse: never fire the writable event\n             * after the readable. In such a case, we invert the calls.\n             * This is useful when, for instance, we want to do things\n             * in the beforeSleep() hook, like fsynching a file to disk,\n             * before replying to a client. */\n            int invert = conn->c.flags & CONN_FLAG_WRITE_BARRIER;\n\n            if (!invert && call_read) {\n                conn->flags &= ~TLS_CONN_FLAG_READ_WANT_WRITE;\n                if (!callHandler((connection *) conn, conn->c.read_handler)) return;\n            }\n\n            /* Fire the writable event. */\n            if (call_write) {\n                conn->flags &= ~TLS_CONN_FLAG_WRITE_WANT_READ;\n                if (!callHandler((connection *) conn, conn->c.write_handler)) return;\n            }\n\n            /* If we have to invert the call, fire the readable event now\n             * after the writable one. */\n            if (invert && call_read) {\n                conn->flags &= ~TLS_CONN_FLAG_READ_WANT_WRITE;\n                if (!callHandler((connection *) conn, conn->c.read_handler)) return;\n            }\n\n            /* If SSL has pending that, already read from the socket, we're at\n             * risk of not calling the read handler again, make sure to add it\n             * to a list of pending connection that should be handled anyway. */\n            if ((mask & AE_READABLE)) {\n                if (SSL_pending(conn->ssl) > 0) {\n                    if (!conn->pending_list_node) {\n                        listAddNodeTail(pending_list, conn);\n                        conn->pending_list_node = listLast(pending_list);\n                    }\n                } else if (conn->pending_list_node) {\n                    listDelNode(pending_list, conn->pending_list_node);\n                    conn->pending_list_node = NULL;\n                }\n            }\n\n            break;\n        }\n        default:\n            break;\n    }\n\n    updateSSLEvent(conn);\n}\n\nstatic void tlsEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask) {\n    UNUSED(el);\n    UNUSED(fd);\n    tls_connection *conn = clientData;\n    tlsHandleEvent(conn, mask);\n}\n\nstatic void connTLSClose(connection *conn_) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    if (conn->ssl) {\n        SSL_free(conn->ssl);\n        conn->ssl = NULL;\n    }\n\n    if (conn->ssl_error) {\n        zfree(conn->ssl_error);\n        conn->ssl_error = NULL;\n    }\n\n    if (conn->pending_list_node) {\n        listDelNode(pending_list, conn->pending_list_node);\n        conn->pending_list_node = NULL;\n    }\n\n    CT_Socket.close(conn_);\n}\n\nstatic int connTLSAccept(connection *_conn, ConnectionCallbackFunc accept_handler) {\n    tls_connection *conn = (tls_connection *) _conn;\n    int ret;\n\n    if (conn->c.state != CONN_STATE_ACCEPTING) return C_ERR;\n    ERR_clear_error();\n\n    /* Try to accept */\n    conn->c.conn_handler = accept_handler;\n    ret = SSL_accept(conn->ssl);\n\n    if (ret <= 0) {\n        WantIOType want = 0;\n        if (!handleSSLReturnCode(conn, ret, &want)) {\n            registerSSLEvent(conn, want);   /* We'll fire back */\n            return C_OK;\n        } else {\n            conn->c.state = CONN_STATE_ERROR;\n            return C_ERR;\n        }\n    }\n\n    conn->c.state = CONN_STATE_CONNECTED;\n    if (!callHandler((connection *) conn, conn->c.conn_handler)) return C_OK;\n    conn->c.conn_handler = NULL;\n\n    return C_OK;\n}\n\nstatic int connTLSConnect(connection *conn_, const char *addr, int port, const char *src_addr, ConnectionCallbackFunc connect_handler) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    if (conn->c.state != CONN_STATE_NONE) return C_ERR;\n    ERR_clear_error();\n\n    /* Initiate Socket connection first */\n    if (CT_Socket.connect(conn_, addr, port, src_addr, connect_handler) == C_ERR) return C_ERR;\n\n    /* Return now, once the socket is connected we'll initiate\n     * TLS connection from the event handler.\n     */\n    return C_OK;\n}\n\nstatic int connTLSWrite(connection *conn_, const void *data, size_t data_len) {\n    tls_connection *conn = (tls_connection *) conn_;\n    int ret, ssl_err;\n\n    if (conn->c.state != CONN_STATE_CONNECTED) return -1;\n    ERR_clear_error();\n    ret = SSL_write(conn->ssl, data, data_len);\n\n    if (ret <= 0) {\n        WantIOType want = 0;\n        if (!(ssl_err = handleSSLReturnCode(conn, ret, &want))) {\n            if (want == WANT_READ) conn->flags |= TLS_CONN_FLAG_WRITE_WANT_READ;\n            updateSSLEvent(conn);\n            errno = EAGAIN;\n            return -1;\n        } else {\n            if (ssl_err == SSL_ERROR_ZERO_RETURN ||\n                    ((ssl_err == SSL_ERROR_SYSCALL && !errno))) {\n                conn->c.state = CONN_STATE_CLOSED;\n                return 0;\n            } else {\n                conn->c.state = CONN_STATE_ERROR;\n                return -1;\n            }\n        }\n    }\n\n    return ret;\n}\n\nstatic int connTLSRead(connection *conn_, void *buf, size_t buf_len) {\n    tls_connection *conn = (tls_connection *) conn_;\n    int ret;\n    int ssl_err;\n\n    if (conn->c.state != CONN_STATE_CONNECTED) return -1;\n    ERR_clear_error();\n    ret = SSL_read(conn->ssl, buf, buf_len);\n    if (ret <= 0) {\n        WantIOType want = 0;\n        if (!(ssl_err = handleSSLReturnCode(conn, ret, &want))) {\n            if (want == WANT_WRITE) conn->flags |= TLS_CONN_FLAG_READ_WANT_WRITE;\n            updateSSLEvent(conn);\n\n            errno = EAGAIN;\n            return -1;\n        } else {\n            if (ssl_err == SSL_ERROR_ZERO_RETURN ||\n                    ((ssl_err == SSL_ERROR_SYSCALL) && !errno)) {\n                conn->c.state = CONN_STATE_CLOSED;\n                return 0;\n            } else {\n                conn->c.state = CONN_STATE_ERROR;\n                return -1;\n            }\n        }\n    }\n\n    return ret;\n}\n\nstatic const char *connTLSGetLastError(connection *conn_) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    if (conn->ssl_error) return conn->ssl_error;\n    return NULL;\n}\n\nint connTLSSetWriteHandler(connection *conn, ConnectionCallbackFunc func, int barrier) {\n    conn->write_handler = func;\n    if (barrier)\n        conn->flags |= CONN_FLAG_WRITE_BARRIER;\n    else\n        conn->flags &= ~CONN_FLAG_WRITE_BARRIER;\n    updateSSLEvent((tls_connection *) conn);\n    return C_OK;\n}\n\nint connTLSSetReadHandler(connection *conn, ConnectionCallbackFunc func) {\n    conn->read_handler = func;\n    updateSSLEvent((tls_connection *) conn);\n    return C_OK;\n}\n\nstatic void setBlockingTimeout(tls_connection *conn, long long timeout) {\n    anetBlock(NULL, conn->c.fd);\n    anetSendTimeout(NULL, conn->c.fd, timeout);\n    anetRecvTimeout(NULL, conn->c.fd, timeout);\n}\n\nstatic void unsetBlockingTimeout(tls_connection *conn) {\n    anetNonBlock(NULL, conn->c.fd);\n    anetSendTimeout(NULL, conn->c.fd, 0);\n    anetRecvTimeout(NULL, conn->c.fd, 0);\n}\n\nstatic int connTLSBlockingConnect(connection *conn_, const char *addr, int port, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n    int ret;\n\n    if (conn->c.state != CONN_STATE_NONE) return C_ERR;\n\n    /* Initiate socket blocking connect first */\n    if (CT_Socket.blocking_connect(conn_, addr, port, timeout) == C_ERR) return C_ERR;\n\n    /* Initiate TLS connection now.  We set up a send/recv timeout on the socket,\n     * which means the specified timeout will not be enforced accurately. */\n    SSL_set_fd(conn->ssl, conn->c.fd);\n    setBlockingTimeout(conn, timeout);\n\n    if ((ret = SSL_connect(conn->ssl)) <= 0) {\n        conn->c.state = CONN_STATE_ERROR;\n        return C_ERR;\n    }\n    unsetBlockingTimeout(conn);\n\n    conn->c.state = CONN_STATE_CONNECTED;\n    return C_OK;\n}\n\nstatic ssize_t connTLSSyncWrite(connection *conn_, char *ptr, ssize_t size, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    setBlockingTimeout(conn, timeout);\n    SSL_clear_mode(conn->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);\n    int ret = SSL_write(conn->ssl, ptr, size);\n    SSL_set_mode(conn->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);\n    unsetBlockingTimeout(conn);\n\n    return ret;\n}\n\nstatic ssize_t connTLSSyncRead(connection *conn_, char *ptr, ssize_t size, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n\n    setBlockingTimeout(conn, timeout);\n    int ret = SSL_read(conn->ssl, ptr, size);\n    unsetBlockingTimeout(conn);\n\n    return ret;\n}\n\nstatic ssize_t connTLSSyncReadLine(connection *conn_, char *ptr, ssize_t size, long long timeout) {\n    tls_connection *conn = (tls_connection *) conn_;\n    ssize_t nread = 0;\n\n    setBlockingTimeout(conn, timeout);\n\n    size--;\n    while(size) {\n        char c;\n\n        if (SSL_read(conn->ssl,&c,1) <= 0) {\n            nread = -1;\n            goto exit;\n        }\n        if (c == '\\n') {\n            *ptr = '\\0';\n            if (nread && *(ptr-1) == '\\r') *(ptr-1) = '\\0';\n            goto exit;\n        } else {\n            *ptr++ = c;\n            *ptr = '\\0';\n            nread++;\n        }\n        size--;\n    }\nexit:\n    unsetBlockingTimeout(conn);\n    return nread;\n}\n\nConnectionType CT_TLS = {\n    .ae_handler = tlsEventHandler,\n    .accept = connTLSAccept,\n    .connect = connTLSConnect,\n    .blocking_connect = connTLSBlockingConnect,\n    .read = connTLSRead,\n    .write = connTLSWrite,\n    .close = connTLSClose,\n    .set_write_handler = connTLSSetWriteHandler,\n    .set_read_handler = connTLSSetReadHandler,\n    .get_last_error = connTLSGetLastError,\n    .sync_write = connTLSSyncWrite,\n    .sync_read = connTLSSyncRead,\n    .sync_readline = connTLSSyncReadLine,\n};\n\nint tlsHasPendingData() {\n    if (!pending_list)\n        return 0;\n    return listLength(pending_list) > 0;\n}\n\nint tlsProcessPendingData() {\n    listIter li;\n    listNode *ln;\n\n    int processed = listLength(pending_list);\n    listRewind(pending_list,&li);\n    while((ln = listNext(&li))) {\n        tls_connection *conn = listNodeValue(ln);\n        tlsHandleEvent(conn, AE_READABLE);\n    }\n    return processed;\n}\n\n#else   /* USE_OPENSSL */\n\nvoid tlsInit(void) {\n}\n\nint tlsConfigure(redisTLSContextConfig *ctx_config) {\n    UNUSED(ctx_config);\n    return C_OK;\n}\n\nconnection *connCreateTLS(void) { \n    return NULL;\n}\n\nconnection *connCreateAcceptedTLS(int fd, int require_auth) {\n    UNUSED(fd);\n    UNUSED(require_auth);\n\n    return NULL;\n}\n\nint tlsHasPendingData() {\n    return 0;\n}\n\nint tlsProcessPendingData() {\n    return 0;\n}\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/tracking.c",
    "content": "/* tracking.c - Client side caching: keys tracking and invalidation\n *\n * Copyright (c) 2019, 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 \"server.h\"\n\n/* The tracking table is constituted by a radix tree of keys, each pointing\n * to a radix tree of client IDs, used to track the clients that may have\n * certain keys in their local, client side, cache.\n *\n * When a client enables tracking with \"CLIENT TRACKING on\", each key served to\n * the client is remembered in the table mapping the keys to the client IDs.\n * Later, when a key is modified, all the clients that may have local copy\n * of such key will receive an invalidation message.\n *\n * Clients will normally take frequently requested objects in memory, removing\n * them when invalidation messages are received. */\nrax *TrackingTable = NULL;\nrax *PrefixTable = NULL;\nuint64_t TrackingTableTotalItems = 0; /* Total number of IDs stored across\n                                         the whole tracking table. This gives\n                                         an hint about the total memory we\n                                         are using server side for CSC. */\nrobj *TrackingChannelName;\n\n/* This is the structure that we have as value of the PrefixTable, and\n * represents the list of keys modified, and the list of clients that need\n * to be notified, for a given prefix. */\ntypedef struct bcastState {\n    rax *keys;      /* Keys modified in the current event loop cycle. */\n    rax *clients;   /* Clients subscribed to the notification events for this\n                       prefix. */\n} bcastState;\n\n/* Remove the tracking state from the client 'c'. Note that there is not much\n * to do for us here, if not to decrement the counter of the clients in\n * tracking mode, because we just store the ID of the client in the tracking\n * table, so we'll remove the ID reference in a lazy way. Otherwise when a\n * client with many entries in the table is removed, it would cost a lot of\n * time to do the cleanup. */\nvoid disableTracking(client *c) {\n    /* If this client is in broadcasting mode, we need to unsubscribe it\n     * from all the prefixes it is registered to. */\n    if (c->flags & CLIENT_TRACKING_BCAST) {\n        raxIterator ri;\n        raxStart(&ri,c->client_tracking_prefixes);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            bcastState *bs = raxFind(PrefixTable,ri.key,ri.key_len);\n            serverAssert(bs != raxNotFound);\n            raxRemove(bs->clients,(unsigned char*)&c,sizeof(c),NULL);\n            /* Was it the last client? Remove the prefix from the\n             * table. */\n            if (raxSize(bs->clients) == 0) {\n                raxFree(bs->clients);\n                raxFree(bs->keys);\n                zfree(bs);\n                raxRemove(PrefixTable,ri.key,ri.key_len,NULL);\n            }\n        }\n        raxStop(&ri);\n        raxFree(c->client_tracking_prefixes);\n        c->client_tracking_prefixes = NULL;\n    }\n\n    /* Clear flags and adjust the count. */\n    if (c->flags & CLIENT_TRACKING) {\n        server.tracking_clients--;\n        c->flags &= ~(CLIENT_TRACKING|CLIENT_TRACKING_BROKEN_REDIR|\n                      CLIENT_TRACKING_BCAST|CLIENT_TRACKING_OPTIN|\n                      CLIENT_TRACKING_OPTOUT|CLIENT_TRACKING_CACHING|\n                      CLIENT_TRACKING_NOLOOP);\n    }\n}\n\n/* Set the client 'c' to track the prefix 'prefix'. If the client 'c' is\n * already registered for the specified prefix, no operation is performed. */\nvoid enableBcastTrackingForPrefix(client *c, char *prefix, size_t plen) {\n    bcastState *bs = raxFind(PrefixTable,(unsigned char*)prefix,sdslen(prefix));\n    /* If this is the first client subscribing to such prefix, create\n     * the prefix in the table. */\n    if (bs == raxNotFound) {\n        bs = zmalloc(sizeof(*bs));\n        bs->keys = raxNew();\n        bs->clients = raxNew();\n        raxInsert(PrefixTable,(unsigned char*)prefix,plen,bs,NULL);\n    }\n    if (raxTryInsert(bs->clients,(unsigned char*)&c,sizeof(c),NULL,NULL)) {\n        if (c->client_tracking_prefixes == NULL)\n            c->client_tracking_prefixes = raxNew();\n        raxInsert(c->client_tracking_prefixes,\n                  (unsigned char*)prefix,plen,NULL,NULL);\n    }\n}\n\n/* Enable the tracking state for the client 'c', and as a side effect allocates\n * the tracking table if needed. If the 'redirect_to' argument is non zero, the\n * invalidation messages for this client will be sent to the client ID\n * specified by the 'redirect_to' argument. Note that if such client will\n * eventually get freed, we'll send a message to the original client to\n * inform it of the condition. Multiple clients can redirect the invalidation\n * messages to the same client ID. */\nvoid enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **prefix, size_t numprefix) {\n    if (!(c->flags & CLIENT_TRACKING)) server.tracking_clients++;\n    c->flags |= CLIENT_TRACKING;\n    c->flags &= ~(CLIENT_TRACKING_BROKEN_REDIR|CLIENT_TRACKING_BCAST|\n                  CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT|\n                  CLIENT_TRACKING_NOLOOP);\n    c->client_tracking_redirection = redirect_to;\n\n    /* This may be the first client we ever enable. Crete the tracking\n     * table if it does not exist. */\n    if (TrackingTable == NULL) {\n        TrackingTable = raxNew();\n        PrefixTable = raxNew();\n        TrackingChannelName = createStringObject(\"__redis__:invalidate\",20);\n    }\n\n    /* For broadcasting, set the list of prefixes in the client. */\n    if (options & CLIENT_TRACKING_BCAST) {\n        c->flags |= CLIENT_TRACKING_BCAST;\n        if (numprefix == 0) enableBcastTrackingForPrefix(c,\"\",0);\n        for (size_t j = 0; j < numprefix; j++) {\n            sds sdsprefix = prefix[j]->ptr;\n            enableBcastTrackingForPrefix(c,sdsprefix,sdslen(sdsprefix));\n        }\n    }\n\n    /* Set the remaining flags that don't need any special handling. */\n    c->flags |= options & (CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT|\n                           CLIENT_TRACKING_NOLOOP);\n}\n\n/* This function is called after the execution of a readonly command in the\n * case the client 'c' has keys tracking enabled and the tracking is not\n * in BCAST mode. It will populate the tracking invalidation table according\n * to the keys the user fetched, so that Redis will know what are the clients\n * that should receive an invalidation message with certain groups of keys\n * are modified. */\nvoid trackingRememberKeys(client *c) {\n    /* Return if we are in optin/out mode and the right CACHING command\n     * was/wasn't given in order to modify the default behavior. */\n    uint64_t optin = c->flags & CLIENT_TRACKING_OPTIN;\n    uint64_t optout = c->flags & CLIENT_TRACKING_OPTOUT;\n    uint64_t caching_given = c->flags & CLIENT_TRACKING_CACHING;\n    if ((optin && !caching_given) || (optout && caching_given)) return;\n\n    int numkeys;\n    int *keys = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);\n    if (keys == NULL) return;\n\n    for(int j = 0; j < numkeys; j++) {\n        int idx = keys[j];\n        sds sdskey = c->argv[idx]->ptr;\n        rax *ids = raxFind(TrackingTable,(unsigned char*)sdskey,sdslen(sdskey));\n        if (ids == raxNotFound) {\n            ids = raxNew();\n            int inserted = raxTryInsert(TrackingTable,(unsigned char*)sdskey,\n                                        sdslen(sdskey),ids, NULL);\n            serverAssert(inserted == 1);\n        }\n        if (raxTryInsert(ids,(unsigned char*)&c->id,sizeof(c->id),NULL,NULL))\n            TrackingTableTotalItems++;\n    }\n    getKeysFreeResult(keys);\n}\n\n/* Given a key name, this function sends an invalidation message in the\n * proper channel (depending on RESP version: PubSub or Push message) and\n * to the proper client (in case fo redirection), in the context of the\n * client 'c' with tracking enabled.\n *\n * In case the 'proto' argument is non zero, the function will assume that\n * 'keyname' points to a buffer of 'keylen' bytes already expressed in the\n * form of Redis RESP protocol, representing an array of keys to send\n * to the client as value of the invalidation. This is used in BCAST mode\n * in order to optimized the implementation to use less CPU time. */\nvoid sendTrackingMessage(client *c, char *keyname, size_t keylen, int proto) {\n    int using_redirection = 0;\n    if (c->client_tracking_redirection) {\n        client *redir = lookupClientByID(c->client_tracking_redirection);\n        if (!redir) {\n            /* We need to signal to the original connection that we\n             * are unable to send invalidation messages to the redirected\n             * connection, because the client no longer exist. */\n            if (c->resp > 2) {\n                addReplyPushLen(c,3);\n                addReplyBulkCBuffer(c,\"tracking-redir-broken\",21);\n                addReplyLongLong(c,c->client_tracking_redirection);\n            }\n            return;\n        }\n        c = redir;\n        using_redirection = 1;\n    }\n\n    /* Only send such info for clients in RESP version 3 or more. However\n     * if redirection is active, and the connection we redirect to is\n     * in Pub/Sub mode, we can support the feature with RESP 2 as well,\n     * by sending Pub/Sub messages in the __redis__:invalidate channel. */\n    if (c->resp > 2) {\n        addReplyPushLen(c,2);\n        addReplyBulkCBuffer(c,\"invalidate\",10);\n    } else if (using_redirection && c->flags & CLIENT_PUBSUB) {\n        /* We use a static object to speedup things, however we assume\n         * that addReplyPubsubMessage() will not take a reference. */\n        addReplyPubsubMessage(c,TrackingChannelName,NULL);\n    } else {\n        /* If are here, the client is not using RESP3, nor is\n         * redirecting to another client. We can't send anything to\n         * it since RESP2 does not support push messages in the same\n         * connection. */\n        return;\n    }\n\n    /* Send the \"value\" part, which is the array of keys. */\n    if (proto) {\n        addReplyProto(c,keyname,keylen);\n    } else {\n        addReplyArrayLen(c,1);\n        addReplyBulkCBuffer(c,keyname,keylen);\n    }\n}\n\n/* This function is called when a key is modified in Redis and in the case\n * we have at least one client with the BCAST mode enabled.\n * Its goal is to set the key in the right broadcast state if the key\n * matches one or more prefixes in the prefix table. Later when we\n * return to the event loop, we'll send invalidation messages to the\n * clients subscribed to each prefix. */\nvoid trackingRememberKeyToBroadcast(client *c, char *keyname, size_t keylen) {\n    raxIterator ri;\n    raxStart(&ri,PrefixTable);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        if (ri.key_len > keylen) continue;\n        if (ri.key_len != 0 && memcmp(ri.key,keyname,ri.key_len) != 0)\n            continue;\n        bcastState *bs = ri.data;\n        /* We insert the client pointer as associated value in the radix\n         * tree. This way we know who was the client that did the last\n         * change to the key, and can avoid sending the notification in the\n         * case the client is in NOLOOP mode. */\n        raxTryInsert(bs->keys,(unsigned char*)keyname,keylen,c,NULL);\n    }\n    raxStop(&ri);\n}\n\n/* This function is called from signalModifiedKey() or other places in Redis\n * when a key changes value. In the context of keys tracking, our task here is\n * to send a notification to every client that may have keys about such caching\n * slot.\n *\n * Note that 'c' may be NULL in case the operation was performed outside the\n * context of a client modifying the database (for instance when we delete a\n * key because of expire).\n *\n * The last argument 'bcast' tells the function if it should also schedule\n * the key for broadcasting to clients in BCAST mode. This is the case when\n * the function is called from the Redis core once a key is modified, however\n * we also call the function in order to evict keys in the key table in case\n * of memory pressure: in that case the key didn't really change, so we want\n * just to notify the clients that are in the table for this key, that would\n * otherwise miss the fact we are no longer tracking the key for them. */\nvoid trackingInvalidateKeyRaw(client *c, char *key, size_t keylen, int bcast) {\n    if (TrackingTable == NULL) return;\n\n    if (bcast && raxSize(PrefixTable) > 0)\n        trackingRememberKeyToBroadcast(c,key,keylen);\n\n    rax *ids = raxFind(TrackingTable,(unsigned char*)key,keylen);\n    if (ids == raxNotFound) return;\n\n    raxIterator ri;\n    raxStart(&ri,ids);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        uint64_t id;\n        memcpy(&id,ri.key,sizeof(id));\n        client *target = lookupClientByID(id);\n        /* Note that if the client is in BCAST mode, we don't want to\n         * send invalidation messages that were pending in the case\n         * previously the client was not in BCAST mode. This can happen if\n         * TRACKING is enabled normally, and then the client switches to\n         * BCAST mode. */\n        if (target == NULL ||\n            !(target->flags & CLIENT_TRACKING)||\n            target->flags & CLIENT_TRACKING_BCAST)\n        {\n            continue;\n        }\n\n        /* If the client enabled the NOLOOP mode, don't send notifications\n         * about keys changed by the client itself. */\n        if (target->flags & CLIENT_TRACKING_NOLOOP &&\n            target == c)\n        {\n            continue;\n        }\n\n        sendTrackingMessage(target,key,keylen,0);\n    }\n    raxStop(&ri);\n\n    /* Free the tracking table: we'll create the radix tree and populate it\n     * again if more keys will be modified in this caching slot. */\n    TrackingTableTotalItems -= raxSize(ids);\n    raxFree(ids);\n    raxRemove(TrackingTable,(unsigned char*)key,keylen,NULL);\n}\n\n/* Wrapper (the one actually called across the core) to pass the key\n * as object. */\nvoid trackingInvalidateKey(client *c, robj *keyobj) {\n    trackingInvalidateKeyRaw(c,keyobj->ptr,sdslen(keyobj->ptr),1);\n}\n\n/* This function is called when one or all the Redis databases are flushed\n * (dbid == -1 in case of FLUSHALL). Caching keys are not specific for\n * each DB but are global: currently what we do is send a special\n * notification to clients with tracking enabled, invalidating the caching\n * key \"\", which means, \"all the keys\", in order to avoid flooding clients\n * with many invalidation messages for all the keys they may hold.\n */\nvoid freeTrackingRadixTree(void *rt) {\n    raxFree(rt);\n}\n\nvoid trackingInvalidateKeysOnFlush(int dbid) {\n    if (server.tracking_clients) {\n        listNode *ln;\n        listIter li;\n        listRewind(server.clients,&li);\n        while ((ln = listNext(&li)) != NULL) {\n            client *c = listNodeValue(ln);\n            if (c->flags & CLIENT_TRACKING) {\n                sendTrackingMessage(c,\"\",1,0);\n            }\n        }\n    }\n\n    /* In case of FLUSHALL, reclaim all the memory used by tracking. */\n    if (dbid == -1 && TrackingTable) {\n        raxFreeWithCallback(TrackingTable,freeTrackingRadixTree);\n        TrackingTable = raxNew();\n        TrackingTableTotalItems = 0;\n    }\n}\n\n/* Tracking forces Redis to remember information about which client may have\n * certain keys. In workloads where there are a lot of reads, but keys are\n * hardly modified, the amount of information we have to remember server side\n * could be a lot, with the number of keys being totally not bound.\n *\n * So Redis allows the user to configure a maximum number of keys for the\n * invalidation table. This function makes sure that we don't go over the\n * specified fill rate: if we are over, we can just evict informations about\n * a random key, and send invalidation messages to clients like if the key was\n * modified. */\nvoid trackingLimitUsedSlots(void) {\n    static unsigned int timeout_counter = 0;\n    if (TrackingTable == NULL) return;\n    if (server.tracking_table_max_keys == 0) return; /* No limits set. */\n    size_t max_keys = server.tracking_table_max_keys;\n    if (raxSize(TrackingTable) <= max_keys) {\n        timeout_counter = 0;\n        return; /* Limit not reached. */\n    }\n\n    /* We have to invalidate a few keys to reach the limit again. The effort\n     * we do here is proportional to the number of times we entered this\n     * function and found that we are still over the limit. */\n    int effort = 100 * (timeout_counter+1);\n\n    /* We just remove one key after another by using a random walk. */\n    raxIterator ri;\n    raxStart(&ri,TrackingTable);\n    while(effort > 0) {\n        effort--;\n        raxSeek(&ri,\"^\",NULL,0);\n        raxRandomWalk(&ri,0);\n        if (raxEOF(&ri)) break;\n        trackingInvalidateKeyRaw(NULL,(char*)ri.key,ri.key_len,0);\n        if (raxSize(TrackingTable) <= max_keys) {\n            timeout_counter = 0;\n            raxStop(&ri);\n            return; /* Return ASAP: we are again under the limit. */\n        }\n    }\n\n    /* If we reach this point, we were not able to go under the configured\n     * limit using the maximum effort we had for this run. */\n    raxStop(&ri);\n    timeout_counter++;\n}\n\n/* Generate Redis protocol for an array containing all the key names\n * in the 'keys' radix tree. If the client is not NULL, the list will not\n * include keys that were modified the last time by this client, in order\n * to implement the NOLOOP option.\n *\n * If the resultin array would be empty, NULL is returned instead. */\nsds trackingBuildBroadcastReply(client *c, rax *keys) {\n    raxIterator ri;\n    uint64_t count;\n\n    if (c == NULL) {\n        count = raxSize(keys);\n    } else {\n        count = 0;\n        raxStart(&ri,keys);\n        raxSeek(&ri,\"^\",NULL,0);\n        while(raxNext(&ri)) {\n            if (ri.data != c) count++;\n        }\n        raxStop(&ri);\n\n        if (count == 0) return NULL;\n    }\n\n    /* Create the array reply with the list of keys once, then send\n    * it to all the clients subscribed to this prefix. */\n    char buf[32];\n    size_t len = ll2string(buf,sizeof(buf),count);\n    sds proto = sdsempty();\n    proto = sdsMakeRoomFor(proto,count*15);\n    proto = sdscatlen(proto,\"*\",1);\n    proto = sdscatlen(proto,buf,len);\n    proto = sdscatlen(proto,\"\\r\\n\",2);\n    raxStart(&ri,keys);\n    raxSeek(&ri,\"^\",NULL,0);\n    while(raxNext(&ri)) {\n        if (c && ri.data == c) continue;\n        len = ll2string(buf,sizeof(buf),ri.key_len);\n        proto = sdscatlen(proto,\"$\",1);\n        proto = sdscatlen(proto,buf,len);\n        proto = sdscatlen(proto,\"\\r\\n\",2);\n        proto = sdscatlen(proto,ri.key,ri.key_len);\n        proto = sdscatlen(proto,\"\\r\\n\",2);\n    }\n    raxStop(&ri);\n    return proto;\n}\n\n/* This function will run the prefixes of clients in BCAST mode and\n * keys that were modified about each prefix, and will send the\n * notifications to each client in each prefix. */\nvoid trackingBroadcastInvalidationMessages(void) {\n    raxIterator ri, ri2;\n\n    /* Return ASAP if there is nothing to do here. */\n    if (TrackingTable == NULL || !server.tracking_clients) return;\n\n    raxStart(&ri,PrefixTable);\n    raxSeek(&ri,\"^\",NULL,0);\n\n    /* For each prefix... */\n    while(raxNext(&ri)) {\n        bcastState *bs = ri.data;\n\n        if (raxSize(bs->keys)) {\n            /* Generate the common protocol for all the clients that are\n             * not using the NOLOOP option. */\n            sds proto = trackingBuildBroadcastReply(NULL,bs->keys);\n\n            /* Send this array of keys to every client in the list. */\n            raxStart(&ri2,bs->clients);\n            raxSeek(&ri2,\"^\",NULL,0);\n            while(raxNext(&ri2)) {\n                client *c;\n                memcpy(&c,ri2.key,sizeof(c));\n                if (c->flags & CLIENT_TRACKING_NOLOOP) {\n                    /* This client may have certain keys excluded. */\n                    sds adhoc = trackingBuildBroadcastReply(c,bs->keys);\n                    if (adhoc) {\n                        sendTrackingMessage(c,adhoc,sdslen(adhoc),1);\n                        sdsfree(adhoc);\n                    }\n                } else {\n                    sendTrackingMessage(c,proto,sdslen(proto),1);\n                }\n            }\n            raxStop(&ri2);\n\n            /* Clean up: we can remove everything from this state, because we\n             * want to only track the new keys that will be accumulated starting\n             * from now. */\n            sdsfree(proto);\n        }\n        raxFree(bs->keys);\n        bs->keys = raxNew();\n    }\n    raxStop(&ri);\n}\n\n/* This is just used in order to access the amount of used slots in the\n * tracking table. */\nuint64_t trackingGetTotalItems(void) {\n    return TrackingTableTotalItems;\n}\n\nuint64_t trackingGetTotalKeys(void) {\n    if (TrackingTable == NULL) return 0;\n    return raxSize(TrackingTable);\n}\n\nuint64_t trackingGetTotalPrefixes(void) {\n    if (PrefixTable == NULL) return 0;\n    return raxSize(PrefixTable);\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <stdint.h>\n#include <errno.h>\n#include <time.h>\n\n#include \"util.h\"\n#include \"sha256.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 && stringLen) {\n        switch(pattern[0]) {\n        case '*':\n            while (patternLen && 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            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] == '\\\\' && patternLen >= 2) {\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 (patternLen >= 3 && pattern[1] == '-') {\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/* Fuzz stringmatchlen() trying to crash it with bad input. */\nint stringmatchlen_fuzz_test(void) {\n    char str[32];\n    char pat[32];\n    int cycles = 10000000;\n    int total_matches = 0;\n    while(cycles--) {\n        int strlen = rand() % sizeof(str);\n        int patlen = rand() % sizeof(pat);\n        for (int j = 0; j < strlen; j++) str[j] = rand() % 128;\n        for (int j = 0; j < patlen; j++) pat[j] = rand() % 128;\n        total_matches += stringmatchlen(pat, patlen, str, strlen, 0);\n    }\n    return total_matches;\n}\n\n/* Convert a string representing an amount of memory into the number of\n * bytes, so for instance memtoll(\"1Gb\") will return 1073741824 that is\n * (1024*1024*1024).\n *\n * On parsing error, if *err is not NULL, it's set to 1, otherwise it's\n * set to 0. On error the function return value is 0, regardless of the\n * fact 'err' is NULL or not. */\nlong long memtoll(const char *p, int *err) {\n    const char *u;\n    char buf[128];\n    long mul; /* unit multiplier */\n    long long val;\n    unsigned int digits;\n\n    if (err) *err = 0;\n\n    /* Search the first non digit character. */\n    u = p;\n    if (*u == '-') u++;\n    while(*u && isdigit(*u)) u++;\n    if (*u == '\\0' || !strcasecmp(u,\"b\")) {\n        mul = 1;\n    } else if (!strcasecmp(u,\"k\")) {\n        mul = 1000;\n    } else if (!strcasecmp(u,\"kb\")) {\n        mul = 1024;\n    } else if (!strcasecmp(u,\"m\")) {\n        mul = 1000*1000;\n    } else if (!strcasecmp(u,\"mb\")) {\n        mul = 1024*1024;\n    } else if (!strcasecmp(u,\"g\")) {\n        mul = 1000L*1000*1000;\n    } else if (!strcasecmp(u,\"gb\")) {\n        mul = 1024L*1024*1024;\n    } else {\n        if (err) *err = 1;\n        return 0;\n    }\n\n    /* Copy the digits into a buffer, we'll use strtoll() to convert\n     * the digit (without the unit) into a number. */\n    digits = u-p;\n    if (digits >= sizeof(buf)) {\n        if (err) *err = 1;\n        return 0;\n    }\n    memcpy(buf,p,digits);\n    buf[digits] = '\\0';\n\n    char *endptr;\n    errno = 0;\n    val = strtoll(buf,&endptr,10);\n    if ((val == 0 && errno == EINVAL) || *endptr != '\\0') {\n        if (err) *err = 1;\n        return 0;\n    }\n    return val*mul;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * See ll2string() for more information. */\nuint32_t digits10(uint64_t v) {\n    if (v < 10) return 1;\n    if (v < 100) return 2;\n    if (v < 1000) return 3;\n    if (v < 1000000000000UL) {\n        if (v < 100000000UL) {\n            if (v < 1000000) {\n                if (v < 10000) return 4;\n                return 5 + (v >= 100000);\n            }\n            return 7 + (v >= 10000000UL);\n        }\n        if (v < 10000000000UL) {\n            return 9 + (v >= 1000000000UL);\n        }\n        return 11 + (v >= 100000000000UL);\n    }\n    return 12 + digits10(v / 1000000000000UL);\n}\n\n/* Like digits10() but for signed values. */\nuint32_t sdigits10(int64_t v) {\n    if (v < 0) {\n        /* Abs value of LLONG_MIN requires special handling. */\n        uint64_t uv = (v != LLONG_MIN) ?\n                      (uint64_t)-v : ((uint64_t) LLONG_MAX)+1;\n        return digits10(uv)+1; /* +1 for the minus. */\n    } else {\n        return digits10(v);\n    }\n}\n\n/* Convert a long long into a string. Returns the number of\n * characters needed to represent the number.\n * If the buffer is not big enough to store the string, 0 is returned.\n *\n * Based on the following article (that apparently does not provide a\n * novel approach but only publicizes an already used technique):\n *\n * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920\n *\n * Modified in order to handle signed integers since the original code was\n * designed for unsigned integers. */\nint ll2string(char *dst, size_t dstlen, long long svalue) {\n    static const char digits[201] =\n        \"0001020304050607080910111213141516171819\"\n        \"2021222324252627282930313233343536373839\"\n        \"4041424344454647484950515253545556575859\"\n        \"6061626364656667686970717273747576777879\"\n        \"8081828384858687888990919293949596979899\";\n    int negative;\n    unsigned long long value;\n\n    /* The main loop works with 64bit unsigned integers for simplicity, so\n     * we convert the number here and remember if it is negative. */\n    if (svalue < 0) {\n        if (svalue != LLONG_MIN) {\n            value = -svalue;\n        } else {\n            value = ((unsigned long long) LLONG_MAX)+1;\n        }\n        negative = 1;\n    } else {\n        value = svalue;\n        negative = 0;\n    }\n\n    /* Check length. */\n    uint32_t const length = digits10(value)+negative;\n    if (length >= dstlen) return 0;\n\n    /* Null term. */\n    uint32_t next = length;\n    dst[next] = '\\0';\n    next--;\n    while (value >= 100) {\n        int const i = (value % 100) * 2;\n        value /= 100;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n        next -= 2;\n    }\n\n    /* Handle last 1-2 digits. */\n    if (value < 10) {\n        dst[next] = '0' + (uint32_t) value;\n    } else {\n        int i = (uint32_t) value * 2;\n        dst[next] = digits[i + 1];\n        dst[next - 1] = digits[i];\n    }\n\n    /* Add sign. */\n    if (negative) dst[0] = '-';\n    return length;\n}\n\n/* Convert a string into a long long. Returns 1 if the string could be parsed\n * into a (non-overflowing) long long, 0 otherwise. The value will be set to\n * the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a long long: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. */\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    /* A zero length string is not a valid number. */\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    /* Handle negative numbers: just set a flag and continue like if it\n     * was a positive number. Later convert into negative. */\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 {\n        return 0;\n    }\n\n    /* Parse all the other digits, checking for overflow at every step. */\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    /* Convert to negative if needed, and do the final overflow check when\n     * converting from unsigned long long to long long. */\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/* Helper function to convert a string to an unsigned long long value.\n * The function attempts to use the faster string2ll() function inside\n * Redis: if it fails, strtoull() is used instead. The function returns\n * 1 if the conversion happened successfully or 0 if the number is\n * invalid or out of range. */\nint string2ull(const char *s, unsigned long long *value) {\n    long long ll;\n    if (string2ll(s,strlen(s),&ll)) {\n        if (ll < 0) return 0; /* Negative values are out of range. */\n        *value = ll;\n        return 1;\n    }\n    errno = 0;\n    char *endptr = NULL;\n    *value = strtoull(s,&endptr,10);\n    if (errno == EINVAL || errno == ERANGE || !(*s != '\\0' && *endptr == '\\0'))\n        return 0; /* strtoull() failed. */\n    return 1; /* Conversion done! */\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 string into a double. Returns 1 if the string could be parsed\n * into a (non-overflowing) double, 0 otherwise. The value will be set to\n * the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a double: no spaces or other characters before or after the string\n * representing the number are accepted. */\nint string2ld(const char *s, size_t slen, long double *dp) {\n    char buf[MAX_LONG_DOUBLE_CHARS];\n    long double value;\n    char *eptr;\n\n    if (slen == 0 || slen >= sizeof(buf)) return 0;\n    memcpy(buf,s,slen);\n    buf[slen] = '\\0';\n\n    errno = 0;\n    value = strtold(buf, &eptr);\n    if (isspace(buf[0]) || eptr[0] != '\\0' ||\n        (size_t)(eptr-buf) != slen ||\n        (errno == ERANGE &&\n            (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||\n        errno == EINVAL ||\n        isnan(value))\n        return 0;\n\n    if (dp) *dp = value;\n    return 1;\n}\n\n/* Convert a string into a double. Returns 1 if the string could be parsed\n * into a (non-overflowing) double, 0 otherwise. The value will be set to\n * the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a double: no spaces or other characters before or after the string\n * representing the number are accepted. */\nint string2d(const char *s, size_t slen, double *dp) {\n    errno = 0;\n    char *eptr;\n    *dp = strtod(s, &eptr);\n    if (slen == 0 ||\n        isspace(((const char*)s)[0]) ||\n        (size_t)(eptr-(char*)s) != slen ||\n        (errno == ERANGE &&\n            (*dp == HUGE_VAL || *dp == -HUGE_VAL || *dp == 0)) ||\n        isnan(*dp))\n        return 0;\n    return 1;\n}\n\n/* Convert a double to a string representation. Returns the number of bytes\n * required. The representation should always be parsable by strtod(3).\n * This function does not support human-friendly formatting like ld2string\n * does. It is intended mainly to be used inside t_zset.c when writing scores\n * into a ziplist representing a sorted set. */\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/* Create a string object from a long double.\n * If mode is humanfriendly it does not use exponential format and trims trailing\n * zeroes at the end (may result in loss of precision).\n * If mode is default exp format is used and the output of snprintf()\n * is not modified (may result in loss of precision).\n * If mode is hex hexadecimal format is used (no loss of precision)\n *\n * The function returns the length of the string or zero if there was not\n * enough buffer room to store it. */\nint ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {\n    size_t l = 0;\n\n    if (isinf(value)) {\n        /* Libc in odd systems (Hi Solaris!) will format infinite in a\n         * different way, so better to handle it in an explicit way. */\n        if (len < 5) return 0; /* No room. 5 is \"-inf\\0\" */\n        if (value > 0) {\n            memcpy(buf,\"inf\",3);\n            l = 3;\n        } else {\n            memcpy(buf,\"-inf\",4);\n            l = 4;\n        }\n    } else {\n        switch (mode) {\n        case LD_STR_AUTO:\n            l = snprintf(buf,len,\"%.17Lg\",value);\n            if (l+1 > len) return 0; /* No room. */\n            break;\n        case LD_STR_HEX:\n            l = snprintf(buf,len,\"%La\",value);\n            if (l+1 > len) return 0; /* No room. */\n            break;\n        case LD_STR_HUMAN:\n            /* We use 17 digits precision since with 128 bit floats that precision\n             * after rounding is able to represent most small decimal numbers in a\n             * way that is \"non surprising\" for the user (that is, most small\n             * decimal numbers will be represented in a way that when converted\n             * back into a string are exactly the same as what the user typed.) */\n            l = snprintf(buf,len,\"%.17Lf\",value);\n            if (l+1 > len) return 0; /* No room. */\n            /* Now remove trailing zeroes after the '.' */\n            if (strchr(buf,'.') != NULL) {\n                char *p = buf+l-1;\n                while(*p == '0') {\n                    p--;\n                    l--;\n                }\n                if (*p == '.') l--;\n            }\n            if (l == 2 && buf[0] == '-' && buf[1] == '0') {\n                buf[0] = '0';\n                l = 1;\n            }\n            break;\n        default: return 0; /* Invalid mode. */\n        }\n    }\n    buf[l] = '\\0';\n    return l;\n}\n\n/* Get random bytes, attempts to get an initial seed from /dev/urandom and\n * the uses a one way hash function in counter mode to generate a random\n * stream. However if /dev/urandom is not available, a weaker seed is used.\n *\n * This function is not thread safe, since the state is global. */\nvoid getRandomBytes(unsigned char *p, size_t len) {\n    /* Global state. */\n    static int seed_initialized = 0;\n    static unsigned char seed[64]; /* 512 bit internal block size. */\n    static uint64_t counter = 0; /* The counter we hash with the seed. */\n\n    if (!seed_initialized) {\n        /* Initialize a seed and use SHA1 in counter mode, where we hash\n         * the same seed with a progressive counter. For the goals of this\n         * function we just need non-colliding strings, there are no\n         * cryptographic security needs. */\n        FILE *fp = fopen(\"/dev/urandom\",\"r\");\n        if (fp == NULL || fread(seed,sizeof(seed),1,fp) != 1) {\n            /* Revert to a weaker seed, and in this case reseed again\n             * at every call.*/\n            for (unsigned int j = 0; j < sizeof(seed); j++) {\n                struct timeval tv;\n                gettimeofday(&tv,NULL);\n                pid_t pid = getpid();\n                seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp;\n            }\n        } else {\n            seed_initialized = 1;\n        }\n        if (fp) fclose(fp);\n    }\n\n    while(len) {\n        /* This implements SHA256-HMAC. */\n        unsigned char digest[SHA256_BLOCK_SIZE];\n        unsigned char kxor[64];\n        unsigned int copylen =\n            len > SHA256_BLOCK_SIZE ? SHA256_BLOCK_SIZE : len;\n\n        /* IKEY: key xored with 0x36. */\n        memcpy(kxor,seed,sizeof(kxor));\n        for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x36;\n\n        /* Obtain HASH(IKEY||MESSAGE). */\n        SHA256_CTX ctx;\n        sha256_init(&ctx);\n        sha256_update(&ctx,kxor,sizeof(kxor));\n        sha256_update(&ctx,(unsigned char*)&counter,sizeof(counter));\n        sha256_final(&ctx,digest);\n\n        /* OKEY: key xored with 0x5c. */\n        memcpy(kxor,seed,sizeof(kxor));\n        for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x5C;\n\n        /* Obtain HASH(OKEY || HASH(IKEY||MESSAGE)). */\n        sha256_init(&ctx);\n        sha256_update(&ctx,kxor,sizeof(kxor));\n        sha256_update(&ctx,digest,SHA256_BLOCK_SIZE);\n        sha256_final(&ctx,digest);\n\n        /* Increment the counter for the next iteration. */\n        counter++;\n\n        memcpy(p,digest,copylen);\n        len -= copylen;\n        p += copylen;\n    }\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, size_t len) {\n    char *charset = \"0123456789abcdef\";\n    size_t j;\n\n    getRandomBytes((unsigned char*)p,len);\n    for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F];\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 \"../\" appearing 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/*\n * Gets the proper timezone in a more portable fashion\n * i.e timezone variables are linux specific.\n */\n\nunsigned long getTimeZone(void) {\n#ifdef __linux__\n    return timezone;\n#else\n    struct timeval tv;\n    struct timezone tz;\n\n    gettimeofday(&tv, &tz);\n\n    return tz.tz_minuteswest * 60UL;\n#endif\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 REDIS_TEST\n#include <assert.h>\n\nstatic void 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\nstatic void 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\nstatic void test_ll2string(void) {\n    char buf[32];\n    long long v;\n    int sz;\n\n    v = 0;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 1);\n    assert(!strcmp(buf, \"0\"));\n\n    v = -1;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 2);\n    assert(!strcmp(buf, \"-1\"));\n\n    v = 99;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 2);\n    assert(!strcmp(buf, \"99\"));\n\n    v = -99;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 3);\n    assert(!strcmp(buf, \"-99\"));\n\n    v = -2147483648;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 11);\n    assert(!strcmp(buf, \"-2147483648\"));\n\n    v = LLONG_MIN;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 20);\n    assert(!strcmp(buf, \"-9223372036854775808\"));\n\n    v = LLONG_MAX;\n    sz = ll2string(buf, sizeof buf, v);\n    assert(sz == 19);\n    assert(!strcmp(buf, \"9223372036854775807\"));\n}\n\n#define UNUSED(x) (void)(x)\nint utilTest(int argc, char **argv) {\n    UNUSED(argc);\n    UNUSED(argv);\n\n    test_string2ll();\n    test_string2l();\n    test_ll2string();\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 <stdint.h>\n#include \"sds.h\"\n\n/* The maximum number of characters needed to represent a long double\n * as a string (long double has a huge range).\n * This should be the size of the buffer given to ld2string */\n#define MAX_LONG_DOUBLE_CHARS 5*1024\n\n/* long double to string convertion options */\ntypedef enum {\n    LD_STR_AUTO,     /* %.17Lg */\n    LD_STR_HUMAN,    /* %.17Lf + Trimming of trailing zeros */\n    LD_STR_HEX       /* %La */\n} ld2string_mode;\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);\nint stringmatchlen_fuzz_test(void);\nlong long memtoll(const char *p, int *err);\nuint32_t digits10(uint64_t v);\nuint32_t sdigits10(int64_t v);\nint ll2string(char *s, size_t len, long long value);\nint string2ll(const char *s, size_t slen, long long *value);\nint string2ull(const char *s, unsigned long long *value);\nint string2l(const char *s, size_t slen, long *value);\nint string2ld(const char *s, size_t slen, long double *dp);\nint string2d(const char *s, size_t slen, double *dp);\nint d2string(char *buf, size_t len, double value);\nint ld2string(char *buf, size_t len, long double value, ld2string_mode mode);\nsds getAbsolutePath(char *filename);\nunsigned long getTimeZone(void);\nint pathIsBaseName(char *path);\n\n#ifdef REDIS_TEST\nint utilTest(int argc, char **argv);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/src/version.h",
    "content": "#define REDIS_VERSION \"6.0.3\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/ziplist.c",
    "content": "/* The ziplist is a specially encoded dually linked list that is designed\n * to be very memory efficient. It stores both strings and integer values,\n * where integers are encoded as actual integers instead of a series of\n * characters. It allows push and pop operations on either side of the list\n * in O(1) time. However, because every operation requires a reallocation of\n * the memory used by the ziplist, the actual complexity is related to the\n * amount of memory used by the ziplist.\n *\n * ----------------------------------------------------------------------------\n *\n * ZIPLIST OVERALL LAYOUT\n * ======================\n *\n * The general layout of the ziplist is as follows:\n *\n * <zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>\n *\n * NOTE: all fields are stored in little endian, if not specified otherwise.\n *\n * <uint32_t zlbytes> is an unsigned integer to hold the number of bytes that\n * the ziplist occupies, including the four bytes of the zlbytes field itself.\n * This value needs to be stored to be able to resize the entire structure\n * without the need to traverse it first.\n *\n * <uint32_t zltail> is the offset to the last entry in the list. This allows\n * a pop operation on the far side of the list without the need for full\n * traversal.\n *\n * <uint16_t zllen> is the number of entries. When there are more than\n * 2^16-2 entries, this value is set to 2^16-1 and we need to traverse the\n * entire list to know how many items it holds.\n *\n * <uint8_t zlend> is a special entry representing the end of the ziplist.\n * Is encoded as a single byte equal to 255. No other normal entry starts\n * with a byte set to the value of 255.\n *\n * ZIPLIST ENTRIES\n * ===============\n *\n * Every entry in the ziplist is prefixed by metadata 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 entry encoding is\n * provided. It represents the entry type, integer or string, and in the case\n * of strings it also represents the length of the string payload.\n * So a complete entry is stored like this:\n *\n * <prevlen> <encoding> <entry-data>\n *\n * Sometimes the encoding represents the entry itself, like for small integers\n * as we'll see later. In such a case the <entry-data> part is missing, and we\n * could have just:\n *\n * <prevlen> <encoding>\n *\n * The length of the previous entry, <prevlen>, is encoded in the following way:\n * If this length is smaller than 254 bytes, it will only consume a single\n * byte representing the length as an unsinged 8 bit integer. When the length\n * is greater than or equal to 254, it will consume 5 bytes. The first byte is\n * set to 254 (FE) to indicate a larger value is following. The remaining 4\n * bytes take the length of the previous entry as value.\n *\n * So practically an entry is encoded in the following way:\n *\n * <prevlen from 0 to 253> <encoding> <entry>\n *\n * Or alternatively if the previous entry length is greater than 253 bytes\n * the following encoding is used:\n *\n * 0xFE <4 bytes unsigned little endian prevlen> <encoding> <entry>\n *\n * The encoding field of the entry depends on the content of the\n * entry. When the entry is a string, the first 2 bits of the encoding first\n * byte will hold the type of encoding used to store the length of the string,\n * followed by the actual length of the string. When the entry is an integer\n * the first 2 bits are both set to 1. The following 2 bits are used to specify\n * what kind of integer will be stored after this header. An overview of the\n * different types and encodings is as follows. The first byte is always enough\n * to determine the kind of entry.\n *\n * |00pppppp| - 1 byte\n *      String value with length less than or equal to 63 bytes (6 bits).\n *      \"pppppp\" represents the unsigned 6 bit length.\n * |01pppppp|qqqqqqqq| - 2 bytes\n *      String value with length less than or equal to 16383 bytes (14 bits).\n *      IMPORTANT: The 14 bit number is stored in big endian.\n * |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes\n *      String value with length greater than or equal to 16384 bytes.\n *      Only the 4 bytes following the first byte represents the length\n *      up to 32^2-1. The 6 lower bits of the first byte are not used and\n *      are set to zero.\n *      IMPORTANT: The 32 bit number is stored in big endian.\n * |11000000| - 3 bytes\n *      Integer encoded as int16_t (2 bytes).\n * |11010000| - 5 bytes\n *      Integer encoded as int32_t (4 bytes).\n * |11100000| - 9 bytes\n *      Integer encoded as int64_t (8 bytes).\n * |11110000| - 4 bytes\n *      Integer encoded as 24 bit signed (3 bytes).\n * |11111110| - 2 bytes\n *      Integer encoded as 8 bit signed (1 byte).\n * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer.\n *      Unsigned integer from 0 to 12. The encoded value is actually from\n *      1 to 13 because 0000 and 1111 can not be used, so 1 should be\n *      subtracted from the encoded 4 bit value to obtain the right value.\n * |11111111| - End of ziplist special entry.\n *\n * Like for the ziplist header, all the integers are represented in little\n * endian byte order, even when this code is compiled in big endian systems.\n *\n * EXAMPLES OF ACTUAL ZIPLISTS\n * ===========================\n *\n * The following is a ziplist containing the two elements representing\n * the strings \"2\" and \"5\". It is composed of 15 bytes, that we visually\n * split into sections:\n *\n *  [0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]\n *        |             |          |       |       |     |\n *     zlbytes        zltail    entries   \"2\"     \"5\"   end\n *\n * The first 4 bytes represent the number 15, that is the number of bytes\n * the whole ziplist is composed of. The second 4 bytes are the offset\n * at which the last ziplist entry is found, that is 12, in fact the\n * last entry, that is \"5\", is at offset 12 inside the ziplist.\n * The next 16 bit integer represents the number of elements inside the\n * ziplist, its value is 2 since there are just two elements inside.\n * Finally \"00 f3\" is the first entry representing the number 2. It is\n * composed of the previous entry length, which is zero because this is\n * our first entry, and the byte F3 which corresponds to the encoding\n * |1111xxxx| with xxxx between 0001 and 1101. We need to remove the \"F\"\n * higher order bits 1111, and subtract 1 from the \"3\", so the entry value\n * is \"2\". The next entry has a prevlen of 02, since the first entry is\n * composed of exactly two bytes. The entry itself, F6, is encoded exactly\n * like the first entry, and 6-1 = 5, so the value of the entry is 5.\n * Finally the special entry FF signals the end of the ziplist.\n *\n * Adding another element to the above string with the value \"Hello World\"\n * allows us to show how the ziplist encodes small strings. We'll just show\n * the hex dump of the entry itself. Imagine the bytes as following the\n * entry that stores \"5\" in the ziplist above:\n *\n * [02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]\n *\n * The first byte, 02, is the length of the previous entry. The next\n * byte represents the encoding in the pattern |00pppppp| that means\n * that the entry is a string of length <pppppp>, so 0B means that\n * an 11 bytes string follows. From the third byte (48) to the last (64)\n * there are just the ASCII characters for \"Hello World\".\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2017, 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#define ZIP_END 255         /* Special \"end of ziplist\" entry. */\n#define ZIP_BIG_PREVLEN 254 /* Max number of bytes of the previous entry, for\n                               the \"prevlen\" field prefixing each entry, to be\n                               represented with just a single byte. Otherwise\n                               it is represented as FF AA BB CC DD, where\n                               AA BB CC DD are a 4 bytes unsigned integer\n                               representing the previous entry len. */\n\n/* Different encoding/length possibilities */\n#define ZIP_STR_MASK 0xc0\n#define ZIP_INT_MASK 0x30\n#define ZIP_STR_06B (0 << 6)\n#define ZIP_STR_14B (1 << 6)\n#define ZIP_STR_32B (2 << 6)\n#define ZIP_INT_16B (0xc0 | 0<<4)\n#define ZIP_INT_32B (0xc0 | 1<<4)\n#define ZIP_INT_64B (0xc0 | 2<<4)\n#define ZIP_INT_24B (0xc0 | 3<<4)\n#define ZIP_INT_8B 0xfe\n\n/* 4 bit integer immediate encoding |1111xxxx| with xxxx between\n * 0001 and 1101. */\n#define ZIP_INT_IMM_MASK 0x0f   /* Mask to extract the 4 bits value. To add\n                                   one is needed to reconstruct the value. */\n#define ZIP_INT_IMM_MIN 0xf1    /* 11110001 */\n#define ZIP_INT_IMM_MAX 0xfd    /* 11111101 */\n\n#define INT24_MAX 0x7fffff\n#define INT24_MIN (-INT24_MAX - 1)\n\n/* Macro to determine if the entry is a string. String entries never start\n * with \"11\" as most significant bits of the first byte. */\n#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)\n\n/* Utility macros.*/\n\n/* Return total bytes a ziplist is composed of. */\n#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))\n\n/* Return the offset of the last item inside the ziplist. */\n#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))\n\n/* Return the length of a ziplist, or UINT16_MAX if the length cannot be\n * determined without scanning the whole ziplist. */\n#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))\n\n/* The size of a ziplist header: two 32 bit integers for the total\n * bytes count and last item offset. One 16 bit integer for the number\n * of items field. */\n#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))\n\n/* Size of the \"end of ziplist\" entry. Just one byte. */\n#define ZIPLIST_END_SIZE        (sizeof(uint8_t))\n\n/* Return the pointer to the first entry of a ziplist. */\n#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)\n\n/* Return the pointer to the last entry of a ziplist, using the\n * last entry offset inside the ziplist header. */\n#define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))\n\n/* Return the pointer to the last byte of a ziplist, which is, the\n * end of ziplist FF entry. */\n#define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)\n\n/* Increment the number of items field in the ziplist header. Note that this\n * macro should never overflow the unsigned 16 bit integer, since entries are\n * always pushed one at a time. When UINT16_MAX is reached we want the count\n * to stay there to signal that a full scan is needed to get the number of\n * items inside the ziplist. */\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/* We use this function to receive information about a ziplist entry.\n * Note that this is not how the data is actually encoded, is just what we\n * get filled by a function in order to operate more easily. */\ntypedef struct zlentry {\n    unsigned int prevrawlensize; /* Bytes used to encode the previous entry len*/\n    unsigned int prevrawlen;     /* Previous entry len. */\n    unsigned int lensize;        /* Bytes used to encode this entry type/len.\n                                    For example strings have a 1, 2 or 5 bytes\n                                    header. Integers always use a single byte.*/\n    unsigned int len;            /* Bytes used to represent the actual entry.\n                                    For strings this is just the string length\n                                    while for integers it is 1, 2, 3, 4, 8 or\n                                    0 (for 4 bit immediate) depending on the\n                                    number range. */\n    unsigned int headersize;     /* prevrawlensize + lensize. */\n    unsigned char encoding;      /* Set to ZIP_STR_* or ZIP_INT_* depending on\n                                    the entry encoding. However for 4 bits\n                                    immediate integers this can assume a range\n                                    of values and must be range-checked. */\n    unsigned char *p;            /* Pointer to the very start of the entry, that\n                                    is, this points to prev-entry-len field. */\n} zlentry;\n\n#define ZIPLIST_ENTRY_ZERO(zle) { \\\n    (zle)->prevrawlensize = (zle)->prevrawlen = 0; \\\n    (zle)->lensize = (zle)->len = (zle)->headersize = 0; \\\n    (zle)->encoding = 0; \\\n    (zle)->p = NULL; \\\n}\n\n/* Extract the encoding from the byte pointed by 'ptr' and set it into\n * 'encoding' field of the zlentry structure. */\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'. */\nunsigned int zipIntSize(unsigned char encoding) {\n    switch(encoding) {\n    case ZIP_INT_8B:  return 1;\n    case ZIP_INT_16B: return 2;\n    case ZIP_INT_24B: return 3;\n    case ZIP_INT_32B: return 4;\n    case ZIP_INT_64B: return 8;\n    }\n    if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)\n        return 0; /* 4 bit immediate */\n    panic(\"Invalid integer encoding 0x%02X\", encoding);\n    return 0;\n}\n\n/* Write the encoidng header of the entry in 'p'. If p is NULL it just returns\n * the amount of bytes required to encode such a length. Arguments:\n *\n * 'encoding' is the encoding we are using for the entry. It could be\n * ZIP_INT_* or ZIP_STR_* or between ZIP_INT_IMM_MIN and ZIP_INT_IMM_MAX\n * for single-byte small immediate integers.\n *\n * 'rawlen' is only used for ZIP_STR_* encodings and is the length of the\n * srting that this entry represents.\n *\n * The function returns the number of bytes used by the encoding/length\n * header stored in 'p'. */\nunsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, unsigned int rawlen) {\n    unsigned char len = 1, buf[5];\n\n    if (ZIP_IS_STR(encoding)) {\n        /* Although encoding is given it may not be set for strings,\n         * so we determine it here using the raw length. */\n        if (rawlen <= 0x3f) {\n            if (!p) return len;\n            buf[0] = ZIP_STR_06B | rawlen;\n        } else if (rawlen <= 0x3fff) {\n            len += 1;\n            if (!p) return len;\n            buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);\n            buf[1] = rawlen & 0xff;\n        } else {\n            len += 4;\n            if (!p) return len;\n            buf[0] = ZIP_STR_32B;\n            buf[1] = (rawlen >> 24) & 0xff;\n            buf[2] = (rawlen >> 16) & 0xff;\n            buf[3] = (rawlen >> 8) & 0xff;\n            buf[4] = rawlen & 0xff;\n        }\n    } else {\n        /* Implies integer encoding, so length is always 1. */\n        if (!p) return len;\n        buf[0] = encoding;\n    }\n\n    /* Store this length at p. */\n    memcpy(p,buf,len);\n    return len;\n}\n\n/* Decode the entry encoding type and data length (string length for strings,\n * number of bytes used for the integer for integer entries) encoded in 'ptr'.\n * The 'encoding' variable will hold the entry encoding, the 'lensize'\n * variable will hold the number of bytes required to encode the entry\n * length, and the 'len' variable will hold the entry length. */\n#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do {                    \\\n    ZIP_ENTRY_ENCODING((ptr), (encoding));                                     \\\n    if ((encoding) < ZIP_STR_MASK) {                                           \\\n        if ((encoding) == ZIP_STR_06B) {                                       \\\n            (lensize) = 1;                                                     \\\n            (len) = (ptr)[0] & 0x3f;                                           \\\n        } else if ((encoding) == ZIP_STR_14B) {                                \\\n            (lensize) = 2;                                                     \\\n            (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1];                       \\\n        } else if ((encoding) == ZIP_STR_32B) {                                \\\n            (lensize) = 5;                                                     \\\n            (len) = ((ptr)[1] << 24) |                                         \\\n                    ((ptr)[2] << 16) |                                         \\\n                    ((ptr)[3] <<  8) |                                         \\\n                    ((ptr)[4]);                                                \\\n        } else {                                                               \\\n            panic(\"Invalid string encoding 0x%02X\", (encoding));               \\\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\". This only\n * uses the larger encoding (required in __ziplistCascadeUpdate). */\nint zipStorePrevEntryLengthLarge(unsigned char *p, unsigned int len) {\n    if (p != NULL) {\n        p[0] = ZIP_BIG_PREVLEN;\n        memcpy(p+1,&len,sizeof(len));\n        memrev32ifbe(p+1);\n    }\n    return 1+sizeof(len);\n}\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. */\nunsigned int zipStorePrevEntryLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return (len < ZIP_BIG_PREVLEN) ? 1 : sizeof(len)+1;\n    } else {\n        if (len < ZIP_BIG_PREVLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            return zipStorePrevEntryLengthLarge(p,len);\n        }\n    }\n}\n\n/* Return the number of bytes used to encode the length of the previous\n * entry. The length is returned by setting the var 'prevlensize'. */\n#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do {                          \\\n    if ((ptr)[0] < ZIP_BIG_PREVLEN) {                                          \\\n        (prevlensize) = 1;                                                     \\\n    } else {                                                                   \\\n        (prevlensize) = 5;                                                     \\\n    }                                                                          \\\n} while(0);\n\n/* Return the length of the previous element, and the number of bytes that\n * are used in order to encode the previous element length.\n * 'ptr' must point to the prevlen prefix of an entry (that encodes the\n * length of the previous entry in order to navigate the elements backward).\n * The length of the previous entry is stored in 'prevlen', the number of\n * bytes needed to encode the previous entry length are stored in\n * 'prevlensize'. */\n#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do {                     \\\n    ZIP_DECODE_PREVLENSIZE(ptr, prevlensize);                                  \\\n    if ((prevlensize) == 1) {                                                  \\\n        (prevlen) = (ptr)[0];                                                  \\\n    } else if ((prevlensize) == 5) {                                           \\\n        assert(sizeof((prevlen)) == 4);                                        \\\n        memcpy(&(prevlen), ((char*)(ptr)) + 1, 4);                             \\\n        memrev32ifbe(&prevlen);                                                \\\n    }                                                                          \\\n} while(0);\n\n/* Given a pointer 'p' to the prevlen info that prefixes an entry, this\n * function returns the difference in number of bytes needed to encode\n * the prevlen if the previous entry changes of size.\n *\n * So if A is the number of bytes used right now to encode the 'prevlen'\n * field.\n *\n * And B is the number of bytes that are needed in order to encode the\n * 'prevlen' if the previous element will be updated to one of size 'len'.\n *\n * Then the function returns B - A\n *\n * So the function returns a positive number if more space is needed,\n * a negative number if less space is needed, or zero if the same space\n * is needed. */\nint zipPrevLenByteDiff(unsigned char *p, unsigned int len) {\n    unsigned int prevlensize;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    return zipStorePrevEntryLength(NULL, len) - prevlensize;\n}\n\n/* Return the total number of bytes used by the entry pointed to by 'p'. */\nunsigned int zipRawEntryLength(unsigned char *p) {\n    unsigned int prevlensize, encoding, lensize, len;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n    return prevlensize + lensize + len;\n}\n\n/* Check if string pointed to by 'entry' can be encoded as an integer.\n * Stores the integer value in 'v' and its encoding in 'encoding'. */\nint zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {\n    long long value;\n\n    if (entrylen >= 32 || entrylen == 0) return 0;\n    if (string2ll((char*)entry,entrylen,&value)) {\n        /* Great, the string can be encoded. Check what's the smallest\n         * of our encoding types that can hold this value. */\n        if (value >= 0 && value <= 12) {\n            *encoding = ZIP_INT_IMM_MIN+value;\n        } else if (value >= INT8_MIN && value <= INT8_MAX) {\n            *encoding = ZIP_INT_8B;\n        } else if (value >= INT16_MIN && value <= INT16_MAX) {\n            *encoding = ZIP_INT_16B;\n        } else if (value >= INT24_MIN && value <= INT24_MAX) {\n            *encoding = ZIP_INT_24B;\n        } else if (value >= INT32_MIN && value <= INT32_MAX) {\n            *encoding = ZIP_INT_32B;\n        } else {\n            *encoding = ZIP_INT_64B;\n        }\n        *v = value;\n        return 1;\n    }\n    return 0;\n}\n\n/* Store integer 'value' at 'p', encoded as 'encoding' */\nvoid zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64;\n    if (encoding == ZIP_INT_8B) {\n        ((int8_t*)p)[0] = (int8_t)value;\n    } else if (encoding == ZIP_INT_16B) {\n        i16 = value;\n        memcpy(p,&i16,sizeof(i16));\n        memrev16ifbe(p);\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = value<<8;\n        memrev32ifbe(&i32);\n        memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));\n    } else if (encoding == ZIP_INT_32B) {\n        i32 = value;\n        memcpy(p,&i32,sizeof(i32));\n        memrev32ifbe(p);\n    } else if (encoding == ZIP_INT_64B) {\n        i64 = value;\n        memcpy(p,&i64,sizeof(i64));\n        memrev64ifbe(p);\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        /* Nothing to do, the value is stored in the encoding itself. */\n    } else {\n        assert(NULL);\n    }\n}\n\n/* Read integer encoded as 'encoding' from 'p' */\nint64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64, ret = 0;\n    if (encoding == ZIP_INT_8B) {\n        ret = ((int8_t*)p)[0];\n    } else if (encoding == ZIP_INT_16B) {\n        memcpy(&i16,p,sizeof(i16));\n        memrev16ifbe(&i16);\n        ret = i16;\n    } else if (encoding == ZIP_INT_32B) {\n        memcpy(&i32,p,sizeof(i32));\n        memrev32ifbe(&i32);\n        ret = i32;\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = 0;\n        memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t));\n        memrev32ifbe(&i32);\n        ret = i32>>8;\n    } else if (encoding == ZIP_INT_64B) {\n        memcpy(&i64,p,sizeof(i64));\n        memrev64ifbe(&i64);\n        ret = i64;\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        ret = (encoding & ZIP_INT_IMM_MASK)-1;\n    } else {\n        assert(NULL);\n    }\n    return ret;\n}\n\n/* Return a struct with all information about an entry. */\nvoid zipEntry(unsigned char *p, zlentry *e) {\n\n    ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);\n    ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);\n    e->headersize = e->prevrawlensize + e->lensize;\n    e->p = p;\n}\n\n/* Create a new empty ziplist. */\nunsigned char *ziplistNew(void) {\n    unsigned int bytes = ZIPLIST_HEADER_SIZE+ZIPLIST_END_SIZE;\n    unsigned char *zl = zmalloc(bytes);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);\n    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);\n    ZIPLIST_LENGTH(zl) = 0;\n    zl[bytes-1] = ZIP_END;\n    return zl;\n}\n\n/* Resize the ziplist. */\nunsigned char *ziplistResize(unsigned char *zl, unsigned int len) {\n    zl = zrealloc(zl,len);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(len);\n    zl[len-1] = ZIP_END;\n    return zl;\n}\n\n/* When an entry is inserted, we need to set the prevlen field of the next\n * entry to equal the length of the inserted entry. It can occur that this\n * length cannot be encoded in 1 byte and the next entry needs to be grow\n * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,\n * because this only happens when an entry is already being inserted (which\n * causes a realloc and memmove). However, encoding the prevlen may require\n * that this entry is grown as well. This effect may cascade throughout\n * the ziplist when there are consecutive entries with a size close to\n * ZIP_BIG_PREVLEN, so we need to check that the prevlen can be encoded in\n * every consecutive entry.\n *\n * Note that this effect can also happen in reverse, where the bytes required\n * to encode the prevlen field can shrink. This effect is deliberately ignored,\n * because it can cause a \"flapping\" effect where a chain prevlen fields is\n * first grown and then shrunk again after consecutive inserts. Rather, the\n * field is allowed to stay larger than necessary, because a large prevlen\n * field implies the ziplist is holding large entries anyway.\n *\n * The pointer \"p\" points to the first entry that does NOT need to be\n * updated, i.e. consecutive fields MAY need an update. */\nunsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;\n    size_t offset, noffset, extra;\n    unsigned char *np;\n    zlentry cur, next;\n\n    while (p[0] != ZIP_END) {\n        zipEntry(p, &cur);\n        rawlen = cur.headersize + cur.len;\n        rawlensize = zipStorePrevEntryLength(NULL,rawlen);\n\n        /* Abort if there is no next entry. */\n        if (p[rawlen] == ZIP_END) break;\n        zipEntry(p+rawlen, &next);\n\n        /* Abort when \"prevlen\" has not changed. */\n        if (next.prevrawlen == rawlen) break;\n\n        if (next.prevrawlensize < rawlensize) {\n            /* The \"prevlen\" field of \"next\" needs more bytes to hold\n             * the raw length of \"cur\". */\n            offset = p-zl;\n            extra = rawlensize-next.prevrawlensize;\n            zl = ziplistResize(zl,curlen+extra);\n            p = zl+offset;\n\n            /* Current pointer and offset for next element. */\n            np = p+rawlen;\n            noffset = np-zl;\n\n            /* Update tail offset when next element is not the tail element. */\n            if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);\n            }\n\n            /* Move the tail to the back. */\n            memmove(np+rawlensize,\n                np+next.prevrawlensize,\n                curlen-noffset-next.prevrawlensize-1);\n            zipStorePrevEntryLength(np,rawlen);\n\n            /* Advance the cursor */\n            p += rawlen;\n            curlen += extra;\n        } else {\n            if (next.prevrawlensize > rawlensize) {\n                /* This would result in shrinking, which we want to avoid.\n                 * So, set \"rawlen\" in the available bytes. */\n                zipStorePrevEntryLengthLarge(p+rawlen,rawlen);\n            } else {\n                zipStorePrevEntryLength(p+rawlen,rawlen);\n            }\n\n            /* Stop here, as the raw length of \"next\" has not changed. */\n            break;\n        }\n    }\n    return zl;\n}\n\n/* Delete \"num\" entries, starting at \"p\". Returns pointer to the ziplist. */\nunsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {\n    unsigned int i, totlen, deleted = 0;\n    size_t offset;\n    int nextdiff = 0;\n    zlentry first, tail;\n\n    zipEntry(p, &first);\n    for (i = 0; p[0] != ZIP_END && i < num; i++) {\n        p += zipRawEntryLength(p);\n        deleted++;\n    }\n\n    totlen = p-first.p; /* Bytes taken by the element(s) to delete. */\n    if (totlen > 0) {\n        if (p[0] != ZIP_END) {\n            /* Storing `prevrawlen` in this entry may increase or decrease the\n             * number of bytes required compare to the current `prevrawlen`.\n             * There always is room to store this, because it was previously\n             * stored by an entry that is now being deleted. */\n            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);\n\n            /* Note that there is always space when p jumps backward: if\n             * the new previous entry is large, one of the deleted elements\n             * had a 5 bytes prevlen header, so there is for sure at least\n             * 5 bytes free and we need just 4. */\n            p -= nextdiff;\n            zipStorePrevEntryLength(p,first.prevrawlen);\n\n            /* Update offset for tail */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);\n\n            /* When the tail contains more than one entry, we need to take\n             * \"nextdiff\" in account as well. Otherwise, a change in the\n             * size of prevlen doesn't have an effect on the *tail* offset. */\n            zipEntry(p, &tail);\n            if (p[tail.headersize+tail.len] != ZIP_END) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                   intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n            }\n\n            /* Move tail to the front of the ziplist */\n            memmove(first.p,p,\n                intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);\n        } else {\n            /* The entire tail was deleted. No need to move memory. */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe((first.p-zl)-first.prevrawlen);\n        }\n\n        /* Resize and update length */\n        offset = first.p-zl;\n        zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);\n        ZIPLIST_INCR_LENGTH(zl,-deleted);\n        p = zl+offset;\n\n        /* When nextdiff != 0, the raw length of the next entry has changed, so\n         * we need to cascade the update throughout the ziplist */\n        if (nextdiff != 0)\n            zl = __ziplistCascadeUpdate(zl,p);\n    }\n    return zl;\n}\n\n/* Insert item at \"p\". */\nunsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;\n    unsigned int prevlensize, prevlen = 0;\n    size_t offset;\n    int nextdiff = 0;\n    unsigned char encoding = 0;\n    long long value = 123456789; /* initialized to avoid warning. Using a value\n                                    that is easy to see if for some reason\n                                    we use it uninitialized. */\n    zlentry tail;\n\n    /* Find out prevlen for the entry that is inserted. */\n    if (p[0] != ZIP_END) {\n        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n    } else {\n        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);\n        if (ptail[0] != ZIP_END) {\n            prevlen = zipRawEntryLength(ptail);\n        }\n    }\n\n    /* See if the entry can be encoded */\n    if (zipTryEncoding(s,slen,&value,&encoding)) {\n        /* 'encoding' is set to the appropriate integer encoding */\n        reqlen = zipIntSize(encoding);\n    } else {\n        /* 'encoding' is untouched, however zipStoreEntryEncoding will use the\n         * string length to figure out how to encode it. */\n        reqlen = slen;\n    }\n    /* We need space for both the length of the previous entry and\n     * the length of the payload. */\n    reqlen += zipStorePrevEntryLength(NULL,prevlen);\n    reqlen += zipStoreEntryEncoding(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    int forcelarge = 0;\n    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;\n    if (nextdiff == -4 && reqlen < 4) {\n        nextdiff = 0;\n        forcelarge = 1;\n    }\n\n    /* Store offset because a realloc may change the address of zl. */\n    offset = p-zl;\n    zl = ziplistResize(zl,curlen+reqlen+nextdiff);\n    p = zl+offset;\n\n    /* Apply memory move when necessary and update tail offset. */\n    if (p[0] != ZIP_END) {\n        /* Subtract one because of the ZIP_END bytes */\n        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);\n\n        /* Encode this entry's raw length in the next entry. */\n        if (forcelarge)\n            zipStorePrevEntryLengthLarge(p+reqlen,reqlen);\n        else\n            zipStorePrevEntryLength(p+reqlen,reqlen);\n\n        /* Update offset for tail */\n        ZIPLIST_TAIL_OFFSET(zl) =\n            intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);\n\n        /* When the tail contains more than one entry, we need to take\n         * \"nextdiff\" in account as well. Otherwise, a change in the\n         * size of prevlen doesn't have an effect on the *tail* offset. */\n        zipEntry(p+reqlen, &tail);\n        if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n        }\n    } else {\n        /* This element will be the new tail. */\n        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);\n    }\n\n    /* When nextdiff != 0, the raw length of the next entry has changed, so\n     * we need to cascade the update throughout the ziplist */\n    if (nextdiff != 0) {\n        offset = p-zl;\n        zl = __ziplistCascadeUpdate(zl,p+reqlen);\n        p = zl+offset;\n    }\n\n    /* Write the entry */\n    p += zipStorePrevEntryLength(p,prevlen);\n    p += zipStoreEntryEncoding(p,encoding,slen);\n    if (ZIP_IS_STR(encoding)) {\n        memcpy(p,s,slen);\n    } else {\n        zipSaveInteger(p,value,encoding);\n    }\n    ZIPLIST_INCR_LENGTH(zl,1);\n    return zl;\n}\n\n/* Merge ziplists 'first' and 'second' by appending 'second' to 'first'.\n *\n * NOTE: The larger ziplist is reallocated to contain the new merged ziplist.\n * Either 'first' or 'second' can be used for the result.  The parameter not\n * used will be free'd and set to NULL.\n *\n * After calling this function, the input parameters are no longer valid since\n * they are changed and free'd in-place.\n *\n * The result ziplist is the contents of 'first' followed by 'second'.\n *\n * On failure: returns NULL if the merge is impossible.\n * On success: returns the merged ziplist (which is expanded version of either\n * 'first' or 'second', also frees the other unused input ziplist, and sets the\n * input ziplist argument equal to newly reallocated ziplist return value. */\nunsigned char *ziplistMerge(unsigned char **first, unsigned char **second) {\n    /* If any params are null, we can't merge, so NULL. */\n    if (first == NULL || *first == NULL || second == NULL || *second == NULL)\n        return NULL;\n\n    /* Can't merge same list into itself. */\n    if (*first == *second)\n        return NULL;\n\n    size_t first_bytes = intrev32ifbe(ZIPLIST_BYTES(*first));\n    size_t first_len = intrev16ifbe(ZIPLIST_LENGTH(*first));\n\n    size_t second_bytes = intrev32ifbe(ZIPLIST_BYTES(*second));\n    size_t second_len = intrev16ifbe(ZIPLIST_LENGTH(*second));\n\n    int append;\n    unsigned char *source, *target;\n    size_t target_bytes, source_bytes;\n    /* Pick the largest ziplist so we can resize easily in-place.\n     * We must also track if we are now appending or prepending to\n     * the target ziplist. */\n    if (first_len >= second_len) {\n        /* retain first, append second to first. */\n        target = *first;\n        target_bytes = first_bytes;\n        source = *second;\n        source_bytes = second_bytes;\n        append = 1;\n    } else {\n        /* else, retain second, prepend first to second. */\n        target = *second;\n        target_bytes = second_bytes;\n        source = *first;\n        source_bytes = first_bytes;\n        append = 0;\n    }\n\n    /* Calculate final bytes (subtract one pair of metadata) */\n    size_t zlbytes = first_bytes + second_bytes -\n                     ZIPLIST_HEADER_SIZE - ZIPLIST_END_SIZE;\n    size_t zllength = first_len + second_len;\n\n    /* Combined zl length should be limited within UINT16_MAX */\n    zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX;\n\n    /* Save offset positions before we start ripping memory apart. */\n    size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first));\n    size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second));\n\n    /* Extend target to new zlbytes then append or prepend source. */\n    target = zrealloc(target, zlbytes);\n    if (append) {\n        /* append == appending to target */\n        /* Copy source after target (copying over original [END]):\n         *   [TARGET - END, SOURCE - HEADER] */\n        memcpy(target + target_bytes - ZIPLIST_END_SIZE,\n               source + ZIPLIST_HEADER_SIZE,\n               source_bytes - ZIPLIST_HEADER_SIZE);\n    } else {\n        /* !append == prepending to target */\n        /* Move target *contents* exactly size of (source - [END]),\n         * then copy source into vacataed space (source - [END]):\n         *   [SOURCE - END, TARGET - HEADER] */\n        memmove(target + source_bytes - ZIPLIST_END_SIZE,\n                target + ZIPLIST_HEADER_SIZE,\n                target_bytes - ZIPLIST_HEADER_SIZE);\n        memcpy(target, source, source_bytes - ZIPLIST_END_SIZE);\n    }\n\n    /* Update header metadata. */\n    ZIPLIST_BYTES(target) = intrev32ifbe(zlbytes);\n    ZIPLIST_LENGTH(target) = intrev16ifbe(zllength);\n    /* New tail offset is:\n     *   + N bytes of first ziplist\n     *   - 1 byte for [END] of first ziplist\n     *   + M bytes for the offset of the original tail of the second ziplist\n     *   - J bytes for HEADER because second_offset keeps no header. */\n    ZIPLIST_TAIL_OFFSET(target) = intrev32ifbe(\n                                   (first_bytes - ZIPLIST_END_SIZE) +\n                                   (second_offset - ZIPLIST_HEADER_SIZE));\n\n    /* __ziplistCascadeUpdate just fixes the prev length values until it finds a\n     * correct prev length value (then it assumes the rest of the list is okay).\n     * We tell CascadeUpdate to start at the first ziplist's tail element to fix\n     * the merge seam. */\n    target = __ziplistCascadeUpdate(target, target+first_offset);\n\n    /* Now free and NULL out what we didn't realloc */\n    if (append) {\n        zfree(*second);\n        *second = NULL;\n        *first = target;\n    } else {\n        zfree(*first);\n        *first = NULL;\n        *second = target;\n    }\n    return target;\n}\n\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {\n    unsigned char *p;\n    p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);\n    return __ziplistInsert(zl,p,s,slen);\n}\n\n/* Returns an offset to use for iterating with ziplistNext. When the given\n * index is negative, the list is traversed back to front. When the list\n * doesn't contain an element at the provided index, NULL is returned. */\nunsigned char *ziplistIndex(unsigned char *zl, int index) {\n    unsigned char *p;\n    unsigned int prevlensize, prevlen = 0;\n    if (index < 0) {\n        index = (-index)-1;\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        if (p[0] != ZIP_END) {\n            ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n            while (prevlen > 0 && index--) {\n                p -= prevlen;\n                ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n            }\n        }\n    } else {\n        p = ZIPLIST_ENTRY_HEAD(zl);\n        while (p[0] != ZIP_END && index--) {\n            p += zipRawEntryLength(p);\n        }\n    }\n    return (p[0] == ZIP_END || index > 0) ? NULL : p;\n}\n\n/* Return pointer to next entry in ziplist.\n *\n * zl is the pointer to the ziplist\n * p is the pointer to the current element\n *\n * The element after 'p' is returned, otherwise NULL if we are at the end. */\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {\n    ((void) zl);\n\n    /* \"p\" could be equal to ZIP_END, caused by ziplistDelete,\n     * and we should return NULL. Otherwise, we should return NULL\n     * when the *next* element is ZIP_END (there is no next entry). */\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    p += zipRawEntryLength(p);\n    if (p[0] == ZIP_END) {\n        return NULL;\n    }\n\n    return p;\n}\n\n/* Return pointer to previous entry in ziplist. */\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {\n    unsigned int prevlensize, prevlen = 0;\n\n    /* Iterating backwards from ZIP_END should return the tail. When \"p\" is\n     * equal to the first element of the list, we're already at the head,\n     * and should return NULL. */\n    if (p[0] == ZIP_END) {\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        return (p[0] == ZIP_END) ? NULL : p;\n    } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {\n        return NULL;\n    } else {\n        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);\n        assert(prevlen > 0);\n        return p-prevlen;\n    }\n}\n\n/* Get entry pointed to by 'p' and store in either '*sstr' or 'sval' depending\n * on the encoding of the entry. '*sstr' is always set to NULL to be able\n * to find out whether the string pointer or the integer value was set.\n * Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */\nunsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {\n    zlentry entry;\n    if (p == NULL || p[0] == ZIP_END) return 0;\n    if (sstr) *sstr = NULL;\n\n    zipEntry(p, &entry);\n    if (ZIP_IS_STR(entry.encoding)) {\n        if (sstr) {\n            *slen = entry.len;\n            *sstr = p+entry.headersize;\n        }\n    } else {\n        if (sval) {\n            *sval = zipLoadInteger(p+entry.headersize,entry.encoding);\n        }\n    }\n    return 1;\n}\n\n/* Insert an entry at \"p\". */\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    return __ziplistInsert(zl,p,s,slen);\n}\n\n/* Delete a single entry from the ziplist, pointed to by *p.\n * Also update *p in place, to be able to iterate over the\n * ziplist, while deleting entries. */\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {\n    size_t offset = *p-zl;\n    zl = __ziplistDelete(zl,*p,1);\n\n    /* Store pointer to current element in p, because ziplistDelete will\n     * do a realloc which might result in a different \"zl\"-pointer.\n     * When the delete direction is back to front, we might delete the last\n     * entry and end up with \"p\" pointing to ZIP_END, so check this. */\n    *p = zl+offset;\n    return zl;\n}\n\n/* Delete a range of entries from the ziplist. */\nunsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num) {\n    unsigned char *p = ziplistIndex(zl,index);\n    return (p == NULL) ? zl : __ziplistDelete(zl,p,num);\n}\n\n/* Compare entry pointer to by 'p' with 'sstr' of length 'slen'. */\n/* Return 1 if equal. */\nunsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {\n    zlentry entry;\n    unsigned char sencoding;\n    long long zval, sval;\n    if (p[0] == ZIP_END) return 0;\n\n    zipEntry(p, &entry);\n    if (ZIP_IS_STR(entry.encoding)) {\n        /* Raw compare */\n        if (entry.len == slen) {\n            return memcmp(p+entry.headersize,sstr,slen) == 0;\n        } else {\n            return 0;\n        }\n    } else {\n        /* Try to compare encoded values. Don't compare encoding because\n         * different implementations may encoded integers differently. */\n        if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {\n          zval = zipLoadInteger(p+entry.headersize,entry.encoding);\n          return zval == sval;\n        }\n    }\n    return 0;\n}\n\n/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries\n * between every comparison. Returns NULL when the field could not be found. */\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {\n    int skipcnt = 0;\n    unsigned char vencoding = 0;\n    long long vll = 0;\n\n    while (p[0] != ZIP_END) {\n        unsigned int prevlensize, encoding, lensize, len;\n        unsigned char *q;\n\n        ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n        ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n        q = p + prevlensize + lensize;\n\n        if (skipcnt == 0) {\n            /* Compare current entry with specified entry */\n            if (ZIP_IS_STR(encoding)) {\n                if (len == vlen && memcmp(q, vstr, vlen) == 0) {\n                    return p;\n                }\n            } else {\n                /* Find out if the searched field can be encoded. Note that\n                 * we do it only the first time, once done vencoding is set\n                 * to non-zero and vll is set to the integer value. */\n                if (vencoding == 0) {\n                    if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {\n                        /* If the entry can't be encoded we set it to\n                         * UCHAR_MAX so that we don't retry again the next\n                         * time. */\n                        vencoding = UCHAR_MAX;\n                    }\n                    /* Must be non-zero by now */\n                    assert(vencoding);\n                }\n\n                /* Compare current entry with specified entry, do it only\n                 * if vencoding != UCHAR_MAX because if there is no encoding\n                 * possible for the field it can't be a valid integer. */\n                if (vencoding != UCHAR_MAX) {\n                    long long ll = zipLoadInteger(q, encoding);\n                    if (ll == vll) {\n                        return p;\n                    }\n                }\n            }\n\n            /* Reset skip count */\n            skipcnt = skip;\n        } else {\n            /* Skip entry */\n            skipcnt--;\n        }\n\n        /* Move to next entry */\n        p = q + len;\n    }\n\n    return NULL;\n}\n\n/* Return length of ziplist. */\nunsigned int ziplistLen(unsigned char *zl) {\n    unsigned int len = 0;\n    if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {\n        len = intrev16ifbe(ZIPLIST_LENGTH(zl));\n    } else {\n        unsigned char *p = zl+ZIPLIST_HEADER_SIZE;\n        while (*p != ZIP_END) {\n            p += zipRawEntryLength(p);\n            len++;\n        }\n\n        /* Re-store length if small enough */\n        if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);\n    }\n    return len;\n}\n\n/* Return ziplist blob size in bytes. */\nsize_t ziplistBlobLen(unsigned char *zl) {\n    return intrev32ifbe(ZIPLIST_BYTES(zl));\n}\n\nvoid ziplistRepr(unsigned char *zl) {\n    unsigned char *p;\n    int index = 0;\n    zlentry entry;\n\n    printf(\n        \"{total bytes %d} \"\n        \"{num entries %u}\\n\"\n        \"{tail offset %u}\\n\",\n        intrev32ifbe(ZIPLIST_BYTES(zl)),\n        intrev16ifbe(ZIPLIST_LENGTH(zl)),\n        intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));\n    p = ZIPLIST_ENTRY_HEAD(zl);\n    while(*p != ZIP_END) {\n        zipEntry(p, &entry);\n        printf(\n            \"{\\n\"\n                \"\\taddr 0x%08lx,\\n\"\n                \"\\tindex %2d,\\n\"\n                \"\\toffset %5ld,\\n\"\n                \"\\thdr+entry len: %5u,\\n\"\n                \"\\thdr len%2u,\\n\"\n                \"\\tprevrawlen: %5u,\\n\"\n                \"\\tprevrawlensize: %2u,\\n\"\n                \"\\tpayload %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        printf(\"\\tbytes: \");\n        for (unsigned int i = 0; i < entry.headersize+entry.len; i++) {\n            printf(\"%02x|\",p[i]);\n        }\n        printf(\"\\n\");\n        p += entry.headersize;\n        if (ZIP_IS_STR(entry.encoding)) {\n            printf(\"\\t[str]\");\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(\"\\t[int]%lld\", (long long) zipLoadInteger(p,entry.encoding));\n        }\n        printf(\"\\n}\\n\");\n        p += entry.len;\n        index++;\n    }\n    printf(\"{end}\\n\\n\");\n}\n\n#ifdef REDIS_TEST\n#include <sys/time.h>\n#include \"adlist.h\"\n#include \"sds.h\"\n\n#define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }\n\nstatic unsigned char *createList() {\n    unsigned char *zl = ziplistNew();\n    zl = ziplistPush(zl, (unsigned char*)\"foo\", 3, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"quux\", 4, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"hello\", 5, ZIPLIST_HEAD);\n    zl = ziplistPush(zl, (unsigned char*)\"1024\", 4, ZIPLIST_TAIL);\n    return zl;\n}\n\nstatic unsigned char *createIntList() {\n    unsigned char *zl = ziplistNew();\n    char buf[32];\n\n    sprintf(buf, \"100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"128000\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"-100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"4294967296\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"much much longer non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    return zl;\n}\n\nstatic long long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\nstatic void stress(int pos, int num, int maxsize, int dnum) {\n    int i,j,k;\n    unsigned char *zl;\n    char posstr[2][5] = { \"HEAD\", \"TAIL\" };\n    long long start;\n    for (i = 0; i < maxsize; i+=dnum) {\n        zl = ziplistNew();\n        for (j = 0; j < i; j++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,ZIPLIST_TAIL);\n        }\n\n        /* Do num times a push+pop from pos */\n        start = usec();\n        for (k = 0; k < num; k++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,pos);\n            zl = ziplistDeleteRange(zl,0,1);\n        }\n        printf(\"List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\\n\",\n            i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start);\n        zfree(zl);\n    }\n}\n\nstatic unsigned char *pop(unsigned char *zl, int where) {\n    unsigned char *p, *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);\n    if (ziplistGet(p,&vstr,&vlen,&vlong)) {\n        if (where == ZIPLIST_HEAD)\n            printf(\"Pop head: \");\n        else\n            printf(\"Pop tail: \");\n\n        if (vstr) {\n            if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror(\"fwrite\");\n        }\n        else {\n            printf(\"%lld\", vlong);\n        }\n\n        printf(\"\\n\");\n        return ziplistDelete(zl,&p);\n    } else {\n        printf(\"ERROR: Could not pop\\n\");\n        exit(1);\n    }\n}\n\nstatic int randstring(char *target, unsigned int min, unsigned int max) {\n    int p = 0;\n    int len = min+rand()%(max-min+1);\n    int minval, maxval;\n    switch(rand() % 3) {\n    case 0:\n        minval = 0;\n        maxval = 255;\n    break;\n    case 1:\n        minval = 48;\n        maxval = 122;\n    break;\n    case 2:\n        minval = 48;\n        maxval = 52;\n    break;\n    default:\n        assert(NULL);\n    }\n\n    while(p < len)\n        target[p++] = minval+rand()%(maxval-minval+1);\n    return len;\n}\n\nstatic void verify(unsigned char *zl, zlentry *e) {\n    int len = ziplistLen(zl);\n    zlentry _e;\n\n    ZIPLIST_ENTRY_ZERO(&_e);\n\n    for (int i = 0; i < len; i++) {\n        memset(&e[i], 0, sizeof(zlentry));\n        zipEntry(ziplistIndex(zl, i), &e[i]);\n\n        memset(&_e, 0, sizeof(zlentry));\n        zipEntry(ziplistIndex(zl, -len+i), &_e);\n\n        assert(memcmp(&e[i], &_e, sizeof(zlentry)) == 0);\n    }\n}\n\nint ziplistTest(int argc, char **argv) {\n    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    zfree(zl);\n\n    zl = createList();\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_HEAD);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zl = pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    zfree(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        zfree(zl);\n    }\n\n    printf(\"Get element at index 4 (out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Get element at index -1 (last element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -1\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Get element at index -4 (first element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -4\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Get element at index -5 (reverse out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -5);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate list from 0 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 0);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate list from 1 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate list from 2 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 2);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate starting out of range:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate from back to front:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Iterate from back to front, deleting all items:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            zl = ziplistDelete(zl,&p);\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Delete inclusive range 0,0:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 1);\n        ziplistRepr(zl);\n        zfree(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        zfree(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        zfree(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        zfree(zl);\n    }\n\n    printf(\"Delete with num overflow:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 5);\n        ziplistRepr(zl);\n        zfree(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        zfree(zl);\n    }\n\n    printf(\"Regression test for >255 byte strings:\\n\");\n    {\n        char v1[257] = {0}, v2[257] = {0};\n        memset(v1,'x',256);\n        memset(v2,'y',256);\n        zl = ziplistNew();\n        zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL);\n\n        /* Pop values again and compare their value. */\n        p = ziplistIndex(zl,0);\n        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        zfree(zl);\n    }\n\n    printf(\"Regression test deleting next to last entries:\\n\");\n    {\n        char v[3][257] = {{0}};\n        zlentry e[3] = {{.prevrawlensize = 0, .prevrawlen = 0, .lensize = 0,\n                         .len = 0, .headersize = 0, .encoding = 0, .p = NULL}};\n        size_t i;\n\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            memset(v[i], 'a' + i, sizeof(v[0]));\n        }\n\n        v[0][256] = '\\0';\n        v[1][  1] = '\\0';\n        v[2][256] = '\\0';\n\n        zl = ziplistNew();\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL);\n        }\n\n        verify(zl, e);\n\n        assert(e[0].prevrawlensize == 1);\n        assert(e[1].prevrawlensize == 5);\n        assert(e[2].prevrawlensize == 1);\n\n        /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */\n        unsigned char *p = e[1].p;\n        zl = ziplistDelete(zl, &p);\n\n        verify(zl, e);\n\n        assert(e[0].prevrawlensize == 1);\n        assert(e[1].prevrawlensize == 5);\n\n        printf(\"SUCCESS\\n\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Create long list and check indices:\\n\");\n    {\n        zl = ziplistNew();\n        char buf[32];\n        int i,len;\n        for (i = 0; i < 1000; i++) {\n            len = sprintf(buf,\"%d\",i);\n            zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);\n        }\n        for (i = 0; i < 1000; i++) {\n            p = ziplistIndex(zl,i);\n            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        zfree(zl);\n    }\n\n    printf(\"Compare strings with ziplist entries:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Merge test:\\n\");\n    {\n        /* create list gives us: [hello, foo, quux, 1024] */\n        zl = createList();\n        unsigned char *zl2 = createList();\n\n        unsigned char *zl3 = ziplistNew();\n        unsigned char *zl4 = ziplistNew();\n\n        if (ziplistMerge(&zl4, &zl4)) {\n            printf(\"ERROR: Allowed merging of one ziplist into itself.\\n\");\n            return 1;\n        }\n\n        /* Merge two empty ziplists, get empty result back. */\n        zl4 = ziplistMerge(&zl3, &zl4);\n        ziplistRepr(zl4);\n        if (ziplistLen(zl4)) {\n            printf(\"ERROR: Merging two empty ziplists created entries.\\n\");\n            return 1;\n        }\n        zfree(zl4);\n\n        zl2 = ziplistMerge(&zl, &zl2);\n        /* merge gives us: [hello, foo, quux, 1024, hello, foo, quux, 1024] */\n        ziplistRepr(zl2);\n\n        if (ziplistLen(zl2) != 8) {\n            printf(\"ERROR: Merged length not 8, but: %u\\n\", ziplistLen(zl2));\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,4);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl2,7);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n        zfree(zl);\n    }\n\n    printf(\"Stress with random payloads of different encoding:\\n\");\n    {\n        int i,j,len,where;\n        unsigned char *p;\n        char buf[1024];\n        int buflen;\n        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,(void (*)(void*))sdsfree);\n            len = rand() % 256;\n\n            /* Create lists */\n            for (j = 0; j < len; j++) {\n                where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;\n                if (rand() % 2) {\n                    buflen = randstring(buf,1,sizeof(buf)-1);\n                } else {\n                    switch(rand() % 3) {\n                    case 0:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) >> 20);\n                        break;\n                    case 1:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()));\n                        break;\n                    case 2:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) << 20);\n                        break;\n                    default:\n                        assert(NULL);\n                    }\n                }\n\n                /* Add to ziplist */\n                zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);\n\n                /* Add to reference list */\n                if (where == ZIPLIST_HEAD) {\n                    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#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#ifndef _ZIPLIST_H\n#define _ZIPLIST_H\n\n#define ZIPLIST_HEAD 0\n#define ZIPLIST_TAIL 1\n\nunsigned char *ziplistNew(void);\nunsigned char *ziplistMerge(unsigned char **first, unsigned char **second);\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);\nunsigned char *ziplistIndex(unsigned char *zl, int index);\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p);\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p);\nunsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval);\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);\nunsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num);\nunsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);\nunsigned int ziplistLen(unsigned char *zl);\nsize_t ziplistBlobLen(unsigned char *zl);\nvoid ziplistRepr(unsigned char *zl);\n\n#ifdef REDIS_TEST\nint ziplistTest(int argc, char *argv[]);\n#endif\n\n#endif /* _ZIPLIST_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/src/zipmap.c",
    "content": "/* String -> String Map data structure optimized for size.\n * This file implements a data structure mapping strings to other strings\n * implementing an O(n) lookup data structure designed to be very memory\n * efficient.\n *\n * The Redis Hash type uses this data structure for hashes composed of a small\n * number of elements, to switch to a hash table once a given number of\n * elements is reached.\n *\n * Given that many times Redis Hashes are used to represent objects composed\n * of few fields, this is a very big win in terms of used memory.\n *\n * --------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Memory layout of a zipmap, for the map \"foo\" => \"bar\", \"hello\" => \"world\":\n *\n * <zmlen><len>\"foo\"<len><free>\"bar\"<len>\"hello\"<len><free>\"world\"\n *\n * <zmlen> is 1 byte length that holds the current size of the zipmap.\n * When the zipmap length is greater than or equal to 254, this value\n * is not used and the zipmap needs to be traversed to find out the length.\n *\n * <len> is the length of the following string (key or value).\n * <len> lengths are encoded in a single value or in a 5 bytes value.\n * If the first byte value (as an unsigned 8 bit value) is between 0 and\n * 253, it's a single-byte length. If it is 254 then a four bytes unsigned\n * integer follows (in the host byte ordering). A value of 255 is used to\n * signal the end of the hash.\n *\n * <free> is the number of free unused bytes after the string, resulting\n * from modification of values associated to a key. For instance if \"foo\"\n * is set to \"bar\", and later \"foo\" will be set to \"hi\", it will have a\n * free byte to use if the value will enlarge again later, or even in\n * order to add a key/value pair if it fits.\n *\n * <free> is always an unsigned 8 bit number, because if after an\n * update operation there are more than a few free bytes, the zipmap will be\n * reallocated to make sure it is as small as possible.\n *\n * The most compact representation of the above two elements hash is actually:\n *\n * \"\\x02\\x03foo\\x03\\x00bar\\x05hello\\x05\\x00world\\xff\"\n *\n * Note that because keys and values are prefixed length \"objects\",\n * the lookup will take O(N) where N is the number of elements\n * in the zipmap and *not* the number of bytes needed to represent the zipmap.\n * This lowers the constant times considerably.\n */\n\n#include <stdio.h>\n#include <string.h>\n#include \"zmalloc.h\"\n#include \"endianconv.h\"\n\n#define ZIPMAP_BIGLEN 254\n#define ZIPMAP_END 255\n\n/* The following defines the max value for the <free> field described in the\n * comments above, that is, the max number of trailing bytes in a value. */\n#define ZIPMAP_VALUE_MAX_FREE 4\n\n/* The following macro returns the number of bytes needed to encode the length\n * for the integer value _l, that is, 1 byte for lengths < ZIPMAP_BIGLEN and\n * 5 bytes for all the other lengths. */\n#define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)\n\n/* Create a new empty zipmap. */\nunsigned char *zipmapNew(void) {\n    unsigned char *zm = zmalloc(2);\n\n    zm[0] = 0; /* Length */\n    zm[1] = ZIPMAP_END;\n    return zm;\n}\n\n/* Decode the encoded length pointed by 'p' */\nstatic unsigned int zipmapDecodeLength(unsigned char *p) {\n    unsigned int len = *p;\n\n    if (len < ZIPMAP_BIGLEN) return len;\n    memcpy(&len,p+1,sizeof(unsigned int));\n    memrev32ifbe(&len);\n    return len;\n}\n\n/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns\n * the amount of bytes required to encode such a length. */\nstatic unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return ZIPMAP_LEN_BYTES(len);\n    } else {\n        if (len < ZIPMAP_BIGLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            p[0] = ZIPMAP_BIGLEN;\n            memcpy(p+1,&len,sizeof(len));\n            memrev32ifbe(p+1);\n            return 1+sizeof(len);\n        }\n    }\n}\n\n/* Search for a matching key, returning a pointer to the entry inside the\n * zipmap. Returns NULL if the key is not found.\n *\n * If NULL is returned, and totlen is not NULL, it is set to the entire\n * size of the zimap, so that the calling function will be able to\n * reallocate the original zipmap to make room for more entries. */\nstatic unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {\n    unsigned char *p = zm+1, *k = NULL;\n    unsigned int l,llen;\n\n    while(*p != ZIPMAP_END) {\n        unsigned char free;\n\n        /* Match or skip the key */\n        l = zipmapDecodeLength(p);\n        llen = zipmapEncodeLength(NULL,l);\n        if (key != NULL && k == NULL && l == klen && !memcmp(p+llen,key,l)) {\n            /* Only return when the user doesn't care\n             * for the total length of the zipmap. */\n            if (totlen != NULL) {\n                k = p;\n            } else {\n                return p;\n            }\n        }\n        p += llen+l;\n        /* Skip the value as well */\n        l = zipmapDecodeLength(p);\n        p += zipmapEncodeLength(NULL,l);\n        free = p[0];\n        p += l+1+free; /* +1 to skip the free byte */\n    }\n    if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;\n    return k;\n}\n\nstatic unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) {\n    unsigned int l;\n\n    l = klen+vlen+3;\n    if (klen >= ZIPMAP_BIGLEN) l += 4;\n    if (vlen >= ZIPMAP_BIGLEN) l += 4;\n    return l;\n}\n\n/* Return the total amount used by a key (encoded length + payload) */\nstatic unsigned int zipmapRawKeyLength(unsigned char *p) {\n    unsigned int l = zipmapDecodeLength(p);\n    return zipmapEncodeLength(NULL,l) + l;\n}\n\n/* Return the total amount used by a value\n * (encoded length + single byte free count + payload) */\nstatic unsigned int zipmapRawValueLength(unsigned char *p) {\n    unsigned int l = zipmapDecodeLength(p);\n    unsigned int used;\n\n    used = zipmapEncodeLength(NULL,l);\n    used += p[used] + 1 + l;\n    return used;\n}\n\n/* If 'p' points to a key, this function returns the total amount of\n * bytes used to store this entry (entry = key + associated value + trailing\n * free space if any). */\nstatic unsigned int zipmapRawEntryLength(unsigned char *p) {\n    unsigned int l = zipmapRawKeyLength(p);\n    return l + zipmapRawValueLength(p+l);\n}\n\nstatic inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) {\n    zm = zrealloc(zm, len);\n    zm[len-1] = ZIPMAP_END;\n    return zm;\n}\n\n/* Set key to value, creating the key if it does not already exist.\n * If 'update' is not NULL, *update is set to 1 if the key was\n * already preset, otherwise to 0. */\nunsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) {\n    unsigned int zmlen, offset;\n    unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);\n    unsigned int empty, vempty;\n    unsigned char *p;\n\n    freelen = reqlen;\n    if (update) *update = 0;\n    p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p == NULL) {\n        /* Key not found: enlarge */\n        zm = zipmapResize(zm, zmlen+reqlen);\n        p = zm+zmlen-1;\n        zmlen = zmlen+reqlen;\n\n        /* Increase zipmap length (this is an insert) */\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;\n    } else {\n        /* Key found. Is there enough space for the new value? */\n        /* Compute the total length: */\n        if (update) *update = 1;\n        freelen = zipmapRawEntryLength(p);\n        if (freelen < reqlen) {\n            /* Store the offset of this key within the current zipmap, so\n             * it can be resized. Then, move the tail backwards so this\n             * pair fits at the current position. */\n            offset = p-zm;\n            zm = zipmapResize(zm, zmlen-freelen+reqlen);\n            p = zm+offset;\n\n            /* The +1 in the number of bytes to be moved is caused by the\n             * end-of-zipmap byte. Note: the *original* zmlen is used. */\n            memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n            zmlen = zmlen-freelen+reqlen;\n            freelen = reqlen;\n        }\n    }\n\n    /* We now have a suitable block where the key/value entry can\n     * be written. If there is too much free space, move the tail\n     * of the zipmap a few bytes to the front and shrink the zipmap,\n     * as we want zipmaps to be very space efficient. */\n    empty = freelen-reqlen;\n    if (empty >= ZIPMAP_VALUE_MAX_FREE) {\n        /* First, move the tail <empty> bytes to the front, then resize\n         * the zipmap to be <empty> bytes smaller. */\n        offset = p-zm;\n        memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));\n        zmlen -= empty;\n        zm = zipmapResize(zm, zmlen);\n        p = zm+offset;\n        vempty = 0;\n    } else {\n        vempty = empty;\n    }\n\n    /* Just write the key + value and we are done. */\n    /* Key: */\n    p += zipmapEncodeLength(p,klen);\n    memcpy(p,key,klen);\n    p += klen;\n    /* Value: */\n    p += zipmapEncodeLength(p,vlen);\n    *p++ = vempty;\n    memcpy(p,val,vlen);\n    return zm;\n}\n\n/* Remove the specified key. If 'deleted' is not NULL the pointed integer is\n * set to 0 if the key was not found, to 1 if it was found and deleted. */\nunsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) {\n    unsigned int zmlen, freelen;\n    unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen);\n    if (p) {\n        freelen = zipmapRawEntryLength(p);\n        memmove(p, p+freelen, zmlen-((p-zm)+freelen+1));\n        zm = zipmapResize(zm, zmlen-freelen);\n\n        /* Decrease zipmap length */\n        if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;\n\n        if (deleted) *deleted = 1;\n    } else {\n        if (deleted) *deleted = 0;\n    }\n    return zm;\n}\n\n/* Call before iterating through elements via zipmapNext() */\nunsigned char *zipmapRewind(unsigned char *zm) {\n    return zm+1;\n}\n\n/* This function is used to iterate through all the zipmap elements.\n * In the first call the first argument is the pointer to the zipmap + 1.\n * In the next calls what zipmapNext returns is used as first argument.\n * Example:\n *\n * unsigned char *i = zipmapRewind(my_zipmap);\n * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {\n *     printf(\"%d bytes key at $p\\n\", klen, key);\n *     printf(\"%d bytes value at $p\\n\", vlen, value);\n * }\n */\nunsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {\n    if (zm[0] == ZIPMAP_END) return NULL;\n    if (key) {\n        *key = zm;\n        *klen = zipmapDecodeLength(zm);\n        *key += ZIPMAP_LEN_BYTES(*klen);\n    }\n    zm += zipmapRawKeyLength(zm);\n    if (value) {\n        *value = zm+1;\n        *vlen = zipmapDecodeLength(zm);\n        *value += ZIPMAP_LEN_BYTES(*vlen);\n    }\n    zm += zipmapRawValueLength(zm);\n    return zm;\n}\n\n/* Search a key and retrieve the pointer and len of the associated value.\n * If the key is found the function returns 1, otherwise 0. */\nint zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) {\n    unsigned char *p;\n\n    if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0;\n    p += zipmapRawKeyLength(p);\n    *vlen = zipmapDecodeLength(p);\n    *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;\n    return 1;\n}\n\n/* Return 1 if the key exists, otherwise 0 is returned. */\nint zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen) {\n    return zipmapLookupRaw(zm,key,klen,NULL) != NULL;\n}\n\n/* Return the number of entries inside a zipmap */\nunsigned int zipmapLen(unsigned char *zm) {\n    unsigned int len = 0;\n    if (zm[0] < ZIPMAP_BIGLEN) {\n        len = zm[0];\n    } else {\n        unsigned char *p = zipmapRewind(zm);\n        while((p = zipmapNext(p,NULL,NULL,NULL,NULL)) != NULL) len++;\n\n        /* Re-store length if small enough */\n        if (len < ZIPMAP_BIGLEN) zm[0] = len;\n    }\n    return len;\n}\n\n/* Return the raw size in bytes of a zipmap, so that we can serialize\n * the zipmap on disk (or everywhere is needed) just writing the returned\n * amount of bytes of the C array starting at the zipmap pointer. */\nsize_t zipmapBlobLen(unsigned char *zm) {\n    unsigned int totlen;\n    zipmapLookupRaw(zm,NULL,0,&totlen);\n    return totlen;\n}\n\n#ifdef REDIS_TEST\nstatic void 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\n#define UNUSED(x) (void)(x)\nint zipmapTest(int argc, char *argv[]) {\n    unsigned char *zm;\n\n    UNUSED(argc);\n    UNUSED(argv);\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": "Chapter07/redis-6.0.3/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#ifdef REDIS_TEST\nint zipmapTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#include <stdint.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#include \"atomicvar.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#define mallocx(size,flags) je_mallocx(size,flags)\n#define dallocx(ptr,flags) je_dallocx(ptr,flags)\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    atomicIncr(used_memory,__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    atomicDecr(used_memory,__n); \\\n} while(0)\n\nstatic size_t used_memory = 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\n/* Allocation and free functions that bypass the thread cache\n * and go straight to the allocator arena bins.\n * Currently implemented only for jemalloc. Used for online defragmentation. */\n#ifdef HAVE_DEFRAG\nvoid *zmalloc_no_tcache(size_t size) {\n    void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);\n    if (!ptr) zmalloc_oom_handler(size);\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n}\n\nvoid zfree_no_tcache(void *ptr) {\n    if (ptr == NULL) return;\n    update_zmalloc_stat_free(zmalloc_size(ptr));\n    dallocx(ptr, MALLOCX_TCACHE_NONE);\n}\n#endif\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 (size == 0 && ptr != NULL) {\n        zfree(ptr);\n        return NULL;\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+PREFIX_SIZE);\n    update_zmalloc_stat_alloc(size+PREFIX_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}\nsize_t zmalloc_usable(void *ptr) {\n    return zmalloc_size(ptr)-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    atomicGet(used_memory,um);\n    return um;\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#elif defined(__FreeBSD__)\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <sys/user.h>\n#include <unistd.h>\n\nsize_t zmalloc_get_rss(void) {\n    struct kinfo_proc info;\n    size_t infolen = sizeof(info);\n    int mib[4];\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PID;\n    mib[3] = getpid();\n\n    if (sysctl(mib, 4, &info, &infolen, NULL, 0) == 0)\n        return (size_t)info.ki_rssize;\n\n    return 0L;\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#if defined(USE_JEMALLOC)\n\nint zmalloc_get_allocator_info(size_t *allocated,\n                               size_t *active,\n                               size_t *resident) {\n    uint64_t epoch = 1;\n    size_t sz;\n    *allocated = *resident = *active = 0;\n    /* Update the statistics cached by mallctl. */\n    sz = sizeof(epoch);\n    je_mallctl(\"epoch\", &epoch, &sz, &epoch, sz);\n    sz = sizeof(size_t);\n    /* Unlike RSS, this does not include RSS from shared libraries and other non\n     * heap mappings. */\n    je_mallctl(\"stats.resident\", resident, &sz, NULL, 0);\n    /* Unlike resident, this doesn't not include the pages jemalloc reserves\n     * for re-use (purge will clean that). */\n    je_mallctl(\"stats.active\", active, &sz, NULL, 0);\n    /* Unlike zmalloc_used_memory, this matches the stats.resident by taking\n     * into account all allocations done by this process (not only zmalloc). */\n    je_mallctl(\"stats.allocated\", allocated, &sz, NULL, 0);\n    return 1;\n}\n\nvoid set_jemalloc_bg_thread(int enable) {\n    /* let jemalloc do purging asynchronously, required when there's no traffic \n     * after flushdb */\n    char val = !!enable;\n    je_mallctl(\"background_thread\", NULL, 0, &val, 1);\n}\n\nint jemalloc_purge() {\n    /* return all unused (reserved) pages to the OS */\n    char tmp[32];\n    unsigned narenas = 0;\n    size_t sz = sizeof(unsigned);\n    if (!je_mallctl(\"arenas.narenas\", &narenas, &sz, NULL, 0)) {\n        sprintf(tmp, \"arena.%d.purge\", narenas);\n        if (!je_mallctl(tmp, NULL, 0, NULL, 0))\n            return 0;\n    }\n    return -1;\n}\n\n#else\n\nint zmalloc_get_allocator_info(size_t *allocated,\n                               size_t *active,\n                               size_t *resident) {\n    *allocated = *resident = *active = 0;\n    return 1;\n}\n\nvoid set_jemalloc_bg_thread(int enable) {\n    ((void)(enable));\n}\n\nint jemalloc_purge() {\n    return 0;\n}\n\n#endif\n\n#if defined(__APPLE__)\n/* For proc_pidinfo() used later in zmalloc_get_smap_bytes_by_field().\n * Note that this file cannot be included in zmalloc.h because it includes\n * a Darwin queue.h file where there is a \"LIST_HEAD\" macro (!) defined\n * conficting with Redis user code. */\n#include <libproc.h>\n#endif\n\n/* Get the sum of the specified field (converted form kb to bytes) in\n * /proc/self/smaps. The field must be specified with trailing \":\" as it\n * apperas in the smaps output.\n *\n * If a pid is specified, the information is extracted for such a pid,\n * otherwise if pid is -1 the information is reported is about the\n * current process.\n *\n * Example: zmalloc_get_smap_bytes_by_field(\"Rss:\",-1);\n */\n#if defined(HAVE_PROC_SMAPS)\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {\n    char line[1024];\n    size_t bytes = 0;\n    int flen = strlen(field);\n    FILE *fp;\n\n    if (pid == -1) {\n        fp = fopen(\"/proc/self/smaps\",\"r\");\n    } else {\n        char filename[128];\n        snprintf(filename,sizeof(filename),\"/proc/%ld/smaps\",pid);\n        fp = fopen(filename,\"r\");\n    }\n\n    if (!fp) return 0;\n    while(fgets(line,sizeof(line),fp) != NULL) {\n        if (strncmp(line,field,flen) == 0) {\n            char *p = strchr(line,'k');\n            if (p) {\n                *p = '\\0';\n                bytes += strtol(line+flen,NULL,10) * 1024;\n            }\n        }\n    }\n    fclose(fp);\n    return bytes;\n}\n#else\n/* Get sum of the specified field from libproc api call.\n * As there are per page value basis we need to convert\n * them accordingly.\n *\n * Note that AnonHugePages is a no-op as THP feature\n * is not supported in this platform\n */\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {\n#if defined(__APPLE__)\n    struct proc_regioninfo pri;\n    if (proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, PROC_PIDREGIONINFO_SIZE) ==\n\tPROC_PIDREGIONINFO_SIZE) {\n\tif (!strcmp(field, \"Private_Dirty:\")) {\n            return (size_t)pri.pri_pages_dirtied * 4096;\n\t} else if (!strcmp(field, \"Rss:\")) {\n            return (size_t)pri.pri_pages_resident * 4096;\n\t} else if (!strcmp(field, \"AnonHugePages:\")) {\n            return 0;\n\t}\n    }\n    return 0;\n#endif\n    ((void) field);\n    ((void) pid);\n    return 0;\n}\n#endif\n\nsize_t zmalloc_get_private_dirty(long pid) {\n    return zmalloc_get_smap_bytes_by_field(\"Private_Dirty:\",pid);\n}\n\n/* Returns the size of physical memory (RAM) in bytes.\n * It looks ugly, but this is the cleanest way to achieve cross platform results.\n * Cleaned up from:\n *\n * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system\n *\n * Note that this function:\n * 1) Was released under the following CC attribution license:\n *    http://creativecommons.org/licenses/by/3.0/deed.en_US.\n * 2) Was originally implemented by David Robert Nadeau.\n * 3) Was modified for Redis by Matt Stancliff.\n * 4) This note exists in order to comply with the original license.\n */\nsize_t zmalloc_get_memory_size(void) {\n#if defined(__unix__) || defined(__unix) || defined(unix) || \\\n    (defined(__APPLE__) && defined(__MACH__))\n#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_MEMSIZE)\n    mib[1] = HW_MEMSIZE;            /* OSX. --------------------- */\n#elif defined(HW_PHYSMEM64)\n    mib[1] = HW_PHYSMEM64;          /* NetBSD, OpenBSD. --------- */\n#endif\n    int64_t size = 0;               /* 64-bit */\n    size_t len = sizeof(size);\n    if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n\n#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)\n    /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */\n    return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);\n\n#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))\n    /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_REALMEM)\n    mib[1] = HW_REALMEM;        /* FreeBSD. ----------------- */\n#elif defined(HW_PHYSMEM)\n    mib[1] = HW_PHYSMEM;        /* Others. ------------------ */\n#endif\n    unsigned int size = 0;      /* 32-bit */\n    size_t len = sizeof(size);\n    if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n#else\n    return 0L;          /* Unknown method to get the data. */\n#endif\n#else\n    return 0L;          /* Unknown OS. */\n#endif\n}\n\n#ifdef REDIS_TEST\n#define UNUSED(x) ((void)(x))\nint zmalloc_test(int argc, char **argv) {\n    void *ptr;\n\n    UNUSED(argc);\n    UNUSED(argv);\n    printf(\"Initial used memory: %zu\\n\", zmalloc_used_memory());\n    ptr = zmalloc(123);\n    printf(\"Allocated 123 bytes; used: %zu\\n\", zmalloc_used_memory());\n    ptr = zrealloc(ptr, 456);\n    printf(\"Reallocated to 456 bytes; used: %zu\\n\", zmalloc_used_memory());\n    zfree(ptr);\n    printf(\"Freed pointer; used: %zu\\n\", zmalloc_used_memory());\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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#ifdef __GLIBC__\n#include <malloc.h>\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) malloc_usable_size(p)\n#endif\n#endif\n\n/* We can enable the Redis defrag capabilities only if we are using Jemalloc\n * and the version used is our special version modified for Redis having\n * the ability to return per-allocation fragmentation hints. */\n#if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)\n#define HAVE_DEFRAG\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_set_oom_handler(void (*oom_handler)(size_t));\nsize_t zmalloc_get_rss(void);\nint zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);\nvoid set_jemalloc_bg_thread(int enable);\nint jemalloc_purge();\nsize_t zmalloc_get_private_dirty(long pid);\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid);\nsize_t zmalloc_get_memory_size(void);\nvoid zlibc_free(void *ptr);\n\n#ifdef HAVE_DEFRAG\nvoid zfree_no_tcache(void *ptr);\nvoid *zmalloc_no_tcache(size_t size);\n#endif\n\n#ifndef HAVE_MALLOC_SIZE\nsize_t zmalloc_size(void *ptr);\nsize_t zmalloc_usable(void *ptr);\n#else\n#define zmalloc_usable(p) zmalloc_size(p)\n#endif\n\n#ifdef REDIS_TEST\nint zmalloc_test(int argc, char **argv);\n#endif\n\n#endif /* __ZMALLOC_H */\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/assets/default.conf",
    "content": "# Redis configuration for testing.\n\nalways-show-logo yes\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\nlatency-monitor-threshold 1\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": "Chapter07/redis-6.0.3/tests/cluster/cluster.tcl",
    "content": "# Cluster-specific test functions.\n#\n# Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This software 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# Get a specific node by ID by parsing the CLUSTER NODES output\n# of the instance Number 'instance_id'\nproc get_node_by_id {instance_id node_id} {\n    set nodes [get_cluster_nodes $instance_id]\n    foreach n $nodes {\n        if {[dict get $n id] eq $node_id} {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\n# Search the first node starting from ID $first that is not\n# already configured as a slave.\nproc cluster_find_available_slave {first} {\n    foreach_redis_id id {\n        if {$id < $first} continue\n        if {[instance_is_killed redis $id]} continue\n        set me [get_myself $id]\n        if {[dict get $me slaveof] eq {-}} {return $id}\n    }\n    fail \"No available slaves\"\n}\n\n# Add 'slaves' slaves to a cluster composed of 'masters' masters.\n# It assumes that masters are allocated sequentially from instance ID 0\n# to N-1.\nproc cluster_allocate_slaves {masters slaves} {\n    for {set j 0} {$j < $slaves} {incr j} {\n        set master_id [expr {$j % $masters}]\n        set slave_id [cluster_find_available_slave $masters]\n        set master_myself [get_myself $master_id]\n        R $slave_id cluster replicate [dict get $master_myself id]\n    }\n}\n\n# Create a cluster composed of the specified number of masters and slaves.\nproc create_cluster {masters slaves} {\n    cluster_allocate_slots $masters\n    if {$slaves} {\n        cluster_allocate_slaves $masters $slaves\n    }\n    assert_cluster_state ok\n}\n\n# Set the cluster node-timeout to all the reachalbe nodes.\nproc set_cluster_node_timeout {to} {\n    foreach_redis_id id {\n        catch {R $id CONFIG SET cluster-node-timeout $to}\n    }\n}\n\n# Check if the cluster is writable and readable. Use node \"id\"\n# as a starting point to talk with the cluster.\nproc cluster_write_test {id} {\n    set prefix [randstring 20 20 alpha]\n    set port [get_instance_attrib redis $id 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 $prefix.$j\n    }\n    for {set j 0} {$j < 100} {incr j} {\n        assert {[$cluster get key.$j] eq \"$prefix.$j\"}\n    }\n    $cluster close\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/run.tcl",
    "content": "# Cluster test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This software 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.\nset ::tlsdir \"../../tls\"\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    end_tests\n}\n\nif {[catch main e]} {\n    puts $::errorInfo\n    if {$::pause_on_error} pause_on_error\n    cleanup\n    exit 1\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"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 \"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    cluster_write_test 0\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/01-faildet.tcl",
    "content": "# Check the basic monitoring and failover capabilities.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster should start ok\" {\n    assert_cluster_state ok\n}\n\ntest \"Killing two slave nodes\" {\n    kill_instance redis 5\n    kill_instance redis 6\n}\n\ntest \"Cluster should be still up\" {\n    assert_cluster_state ok\n}\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\n# Note: the only slave of instance 0 is already down so no\n# failover is possible, that would change the state back to ok.\ntest \"Cluster should be down now\" {\n    assert_cluster_state fail\n}\n\ntest \"Restarting master node\" {\n    restart_instance redis 0\n}\n\ntest \"Cluster should be up again\" {\n    assert_cluster_state ok\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/02-failover.tcl",
    "content": "# Check the basic monitoring and failover capabilities.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance #5 is now a master\" {\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Restarting the previously killed master node\" {\n    restart_instance redis 0\n}\n\ntest \"Instance #0 gets converted into a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave}\n    } else {\n        fail \"Old master was not converted into slave\"\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/03-failover-loop.tcl",
    "content": "# Failover stress test.\n# In this test a different node is killed in a loop for N\n# iterations. The test checks that certain properties\n# are preseved across iterations.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\nset iterations 20\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\n\nwhile {[incr iterations -1]} {\n    set tokill [randomInt 10]\n    set other [expr {($tokill+1)%10}] ; # Some other instance.\n    set key [randstring 20 20 alpha]\n    set val [randstring 20 20 alpha]\n    set role [RI $tokill role]\n    if {$role eq {master}} {\n        set slave {}\n        set myid [dict get [get_myself $tokill] id]\n        foreach_redis_id id {\n            if {$id == $tokill} continue\n            if {[dict get [get_myself $id] slaveof] eq $myid} {\n                set slave $id\n            }\n        }\n        if {$slave eq {}} {\n            fail \"Unable to retrieve slave's ID for master #$tokill\"\n        }\n    }\n\n    puts \"--- Iteration $iterations ---\"\n\n    if {$role eq {master}} {\n        test \"Wait for slave of #$tokill to sync\" {\n            wait_for_condition 1000 50 {\n                [string match {*state=online*} [RI $tokill slave0]]\n            } else {\n                fail \"Slave of node #$tokill is not ok\"\n            }\n        }\n        set slave_config_epoch [CI $slave cluster_my_epoch]\n    }\n\n    test \"Cluster is writable before failover\" {\n        for {set i 0} {$i < 100} {incr i} {\n            catch {$cluster set $key:$i $val:$i} err\n            assert {$err eq {OK}}\n        }\n        # Wait for the write to propagate to the slave if we\n        # are going to kill a master.\n        if {$role eq {master}} {\n            R $tokill wait 1 20000\n        }\n    }\n\n    test \"Killing node #$tokill\" {\n        kill_instance redis $tokill\n    }\n\n    if {$role eq {master}} {\n        test \"Wait failover by #$slave with old epoch $slave_config_epoch\" {\n            wait_for_condition 1000 50 {\n                [CI $slave cluster_my_epoch] > $slave_config_epoch\n            } else {\n                fail \"No failover detected, epoch is still [CI $slave cluster_my_epoch]\"\n            }\n        }\n    }\n\n    test \"Cluster should eventually be up again\" {\n        assert_cluster_state ok\n    }\n\n    test \"Cluster is writable again\" {\n        for {set i 0} {$i < 100} {incr i} {\n            catch {$cluster set $key:$i:2 $val:$i:2} err\n            assert {$err eq {OK}}\n        }\n    }\n\n    test \"Restarting node #$tokill\" {\n        restart_instance redis $tokill\n    }\n\n    test \"Instance #$tokill is now a slave\" {\n        wait_for_condition 1000 50 {\n            [RI $tokill role] eq {slave}\n        } else {\n            fail \"Restarted instance is not a slave\"\n        }\n    }\n\n    test \"We can read back the value we set before\" {\n        for {set i 0} {$i < 100} {incr i} {\n            catch {$cluster get $key:$i} err\n            assert {$err eq \"$val:$i\"}\n            catch {$cluster get $key:$i:2} err\n            assert {$err eq \"$val:$i:2\"}\n        }\n    }\n}\n\ntest \"Post condition: current_epoch >= my_epoch everywhere\" {\n    foreach_redis_id id {\n        assert {[CI $id cluster_current_epoch] >= [CI $id cluster_my_epoch]}\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/04-resharding.tcl",
    "content": "# Failover stress test.\n# In this test a different node is killed in a loop for N\n# iterations. The test checks that certain properties\n# are preseved across iterations.\n\nsource \"../tests/includes/init-tests.tcl\"\nsource \"../../../tests/support/cli.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Enable AOF in all the instances\" {\n    foreach_redis_id id {\n        R $id config set appendonly yes\n        # We use \"appendfsync no\" because it's fast but also guarantees that\n        # write(2) is performed before replying to client.\n        R $id config set appendfsync no\n    }\n\n    foreach_redis_id id {\n        wait_for_condition 1000 500 {\n            [RI $id aof_rewrite_in_progress] == 0 &&\n            [RI $id aof_enabled] == 1\n        } else {\n            fail \"Failed to enable AOF on instance #$id\"\n        }\n    }\n}\n\n# Return nno-zero if the specified PID is about a process still in execution,\n# otherwise 0 is returned.\nproc process_is_running {pid} {\n    # PS should return with an error if PID is non existing,\n    # and catch will return non-zero. We want to return non-zero if\n    # the PID exists, so we invert the return value with expr not operator.\n    expr {![catch {exec ps -p $pid}]}\n}\n\n# Our resharding test performs the following actions:\n#\n# - N commands are sent to the cluster in the course of the test.\n# - Every command selects a random key from key:0 to key:MAX-1.\n# - The operation RPUSH key <randomvalue> is perforemd.\n# - Tcl remembers into an array all the values pushed to each list.\n# - After N/2 commands, the resharding process is started in background.\n# - The test continues while the resharding is in progress.\n# - At the end of the test, we wait for the resharding process to stop.\n# - Finally the keys are checked to see if they contain the value they should.\n\nset numkeys 50000\nset numops 200000\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\ncatch {unset content}\narray set content {}\nset tribpid {}\n\ntest \"Cluster consistency during live resharding\" {\n    set ele 0\n    for {set j 0} {$j < $numops} {incr j} {\n        # Trigger the resharding once we execute half the ops.\n        if {$tribpid ne {} &&\n            ($j % 10000) == 0 &&\n            ![process_is_running $tribpid]} {\n            set tribpid {}\n        }\n\n        if {$j >= $numops/2 && $tribpid eq {}} {\n            puts -nonewline \"...Starting resharding...\"\n            flush stdout\n            set target [dict get [get_myself [randomInt 5]] id]\n            set tribpid [lindex [exec \\\n                ../../../src/redis-cli --cluster reshard \\\n                127.0.0.1:[get_instance_attrib redis 0 port] \\\n                --cluster-from all \\\n                --cluster-to $target \\\n                --cluster-slots 100 \\\n                --cluster-yes \\\n                {*}[rediscli_tls_config \"../../../tests\"] \\\n                | [info nameofexecutable] \\\n                ../tests/helpers/onlydots.tcl \\\n                &] 0]\n        }\n\n        # Write random data to random list.\n        set listid [randomInt $numkeys]\n        set key \"key:$listid\"\n        incr ele\n        # We write both with Lua scripts and with plain commands.\n        # This way we are able to stress Lua -> Redis command invocation\n        # as well, that has tests to prevent Lua to write into wrong\n        # hash slots.\n        if {$listid % 2} {\n            $cluster rpush $key $ele\n        } else {\n            $cluster eval {redis.call(\"rpush\",KEYS[1],ARGV[1])} 1 $key $ele\n        }\n        lappend content($key) $ele\n\n        if {($j % 1000) == 0} {\n            puts -nonewline W; flush stdout\n        }\n    }\n\n    # Wait for the resharding process to end\n    wait_for_condition 1000 500 {\n        [process_is_running $tribpid] == 0\n    } else {\n        fail \"Resharding is not terminating after some time.\"\n    }\n\n}\n\ntest \"Verify $numkeys keys for consistency with logical content\" {\n    # Check that the Redis Cluster content matches our logical content.\n    foreach {key value} [array get content] {\n        if {[$cluster lrange $key 0 -1] ne $value} {\n            fail \"Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]\"\n        }\n    }\n}\n\ntest \"Crash and restart all the instances\" {\n    foreach_redis_id id {\n        kill_instance redis $id\n        restart_instance redis $id\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Verify $numkeys keys after the crash & restart\" {\n    # Check that the Redis Cluster content matches our logical content.\n    foreach {key value} [array get content] {\n        if {[$cluster lrange $key 0 -1] ne $value} {\n            fail \"Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]\"\n        }\n    }\n}\n\ntest \"Disable AOF in all the instances\" {\n    foreach_redis_id id {\n        R $id config set appendonly no\n    }\n}\n\ntest \"Verify slaves consistency\" {\n    set verified_masters 0\n    foreach_redis_id id {\n        set role [R $id role]\n        lassign $role myrole myoffset slaves\n        if {$myrole eq {slave}} continue\n        set masterport [get_instance_attrib redis $id port]\n        set masterdigest [R $id debug digest]\n        foreach_redis_id sid {\n            set srole [R $sid role]\n            if {[lindex $srole 0] eq {master}} continue\n            if {[lindex $srole 2] != $masterport} continue\n            wait_for_condition 1000 500 {\n                [R $sid debug digest] eq $masterdigest\n            } else {\n                fail \"Master and slave data digest are different\"\n            }\n            incr verified_masters\n        }\n    }\n    assert {$verified_masters >= 5}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/05-slave-selection.tcl",
    "content": "# Slave selection test\n# Check the algorithm trying to pick the slave with the most complete history.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 10 slaves, so that we have 2\n# slaves for each master.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 10\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"The first master has actually two slaves\" {\n    assert {[llength [lindex [R 0 role] 2]] == 2}\n}\n\ntest {Slaves of #0 are instance #5 and #10 as expected} {\n    set port0 [get_instance_attrib redis 0 port]\n    assert {[lindex [R 5 role] 2] == $port0}\n    assert {[lindex [R 10 role] 2] == $port0}\n}\n\ntest \"Instance #5 and #10 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up} &&\n        [RI 10 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 or #10 master link status is not up\"\n    }\n}\n\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\n\ntest \"Slaves are both able to receive and acknowledge writes\" {\n    for {set j 0} {$j < 100} {incr j} {\n        $cluster set $j $j\n    }\n    assert {[R 0 wait 2 60000] == 2}\n}\n\ntest \"Write data while slave #10 is paused and can't receive it\" {\n    # Stop the slave with a multi/exec transaction so that the master will\n    # be killed as soon as it can accept writes again.\n    R 10 multi\n    R 10 debug sleep 10\n    R 10 client kill 127.0.0.1:$port0\n    R 10 deferred 1\n    R 10 exec\n\n    # Write some data the slave can't receive.\n    for {set j 0} {$j < 100} {incr j} {\n        $cluster set $j $j\n    }\n\n    # Prevent the master from accepting new slaves.\n    # Use a large pause value since we'll kill it anyway.\n    R 0 CLIENT PAUSE 60000\n\n    # Wait for the slave to return available again\n    R 10 deferred 0\n    assert {[R 10 read] eq {OK OK}}\n\n    # Kill the master so that a reconnection will not be possible.\n    kill_instance redis 0\n}\n\ntest \"Wait for instance #5 (and not #10) to turn into a master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 role] eq {master}\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Wait for the node #10 to return alive before ending the test\" {\n    R 10 ping\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Node #10 should eventually replicate node #5\" {\n    set port5 [get_instance_attrib redis 5 port]\n    wait_for_condition 1000 50 {\n        ([lindex [R 10 role] 2] == $port5) &&\n        ([lindex [R 10 role] 3] eq {connected})\n    } else {\n        fail \"#10 didn't became slave of #5\"\n    }\n}\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 3 master and 15 slaves, so that we have 5\n# slaves for eatch master.\ntest \"Create a 3 nodes cluster\" {\n    create_cluster 3 15\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"The first master has actually 5 slaves\" {\n    assert {[llength [lindex [R 0 role] 2]] == 5}\n}\n\ntest {Slaves of #0 are instance #3, #6, #9, #12 and #15 as expected} {\n    set port0 [get_instance_attrib redis 0 port]\n    assert {[lindex [R 3 role] 2] == $port0}\n    assert {[lindex [R 6 role] 2] == $port0}\n    assert {[lindex [R 9 role] 2] == $port0}\n    assert {[lindex [R 12 role] 2] == $port0}\n    assert {[lindex [R 15 role] 2] == $port0}\n}\n\ntest {Instance #3, #6, #9, #12 and #15 synced with the master} {\n    wait_for_condition 1000 50 {\n        [RI 3 master_link_status] eq {up} &&\n        [RI 6 master_link_status] eq {up} &&\n        [RI 9 master_link_status] eq {up} &&\n        [RI 12 master_link_status] eq {up} &&\n        [RI 15 master_link_status] eq {up}\n    } else {\n        fail \"Instance #3 or #6 or #9 or #12 or #15 master link status is not up\"\n    }\n}\n\nproc master_detected {instances} {\n    foreach instance [dict keys $instances] {\n        if {[RI $instance role] eq {master}} {\n            return true\n        }\n    }\n\n    return false\n}\n\ntest \"New Master down consecutively\" {\n    set instances [dict create 0 1 3 1 6 1 9 1 12 1 15 1]\n\n    set loops [expr {[dict size $instances]-1}]\n    for {set i 0} {$i < $loops} {incr i} {\n        set master_id -1\n        foreach instance [dict keys $instances] {\n            if {[RI $instance role] eq {master}} {\n                set master_id $instance\n                break;\n            }\n        }\n\n        if {$master_id eq -1} {\n            fail \"no master detected, #loop $i\"\n        }\n\n        set instances [dict remove $instances $master_id]\n\n        kill_instance redis $master_id\n        wait_for_condition 1000 50 {\n            [master_detected $instances]\n        } else {\n            failover \"No failover detected when master $master_id fails\"\n        }\n\n        assert_cluster_state ok\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/06-slave-stop-cond.tcl",
    "content": "# Slave stop condition test\n# Check that if there is a disconnection time limit, the slave will not try\n# to failover its master.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 5 slaves.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"The first master has actually one slave\" {\n    assert {[llength [lindex [R 0 role] 2]] == 1}\n}\n\ntest {Slaves of #0 is instance #5 as expected} {\n    set port0 [get_instance_attrib redis 0 port]\n    assert {[lindex [R 5 role] 2] == $port0}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"Lower the slave validity factor of #5 to the value of 2\" {\n    assert {[R 5 config set cluster-slave-validity-factor 2] eq {OK}}\n}\n\ntest \"Break master-slave link and prevent further reconnections\" {\n    # Stop the slave with a multi/exec transaction so that the master will\n    # be killed as soon as it can accept writes again.\n    R 5 multi\n    R 5 client kill 127.0.0.1:$port0\n    # here we should sleep 6 or more seconds (node_timeout * slave_validity)\n    # but the actual validity time is actually incremented by the\n    # repl-ping-slave-period value which is 10 seconds by default. So we\n    # need to wait more than 16 seconds.\n    R 5 debug sleep 20\n    R 5 deferred 1\n    R 5 exec\n\n    # Prevent the master from accepting new slaves.\n    # Use a large pause value since we'll kill it anyway.\n    R 0 CLIENT PAUSE 60000\n\n    # Wait for the slave to return available again\n    R 5 deferred 0\n    assert {[R 5 read] eq {OK OK}}\n\n    # Kill the master so that a reconnection will not be possible.\n    kill_instance redis 0\n}\n\ntest \"Slave #5 is reachable and alive\" {\n    assert {[R 5 ping] eq {PONG}}\n}\n\ntest \"Slave #5 should not be able to failover\" {\n    after 10000\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Cluster should be down\" {\n    assert_cluster_state fail\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/07-replica-migration.tcl",
    "content": "# Replica migration test.\n# Check that orphaned masters are joined by replicas of masters having\n# multiple replicas attached, according to the migration barrier settings.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 10 slaves, so that we have 2\n# slaves for each master.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 10\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Each master should have two replicas attached\" {\n    foreach_redis_id id {\n        if {$id < 5} {\n            wait_for_condition 1000 50 {\n                [llength [lindex [R 0 role] 2]] == 2\n            } else {\n                fail \"Master #$id does not have 2 slaves as expected\"\n            }\n        }\n    }\n}\n\ntest \"Killing all the slaves of master #0 and #1\" {\n    kill_instance redis 5\n    kill_instance redis 10\n    kill_instance redis 6\n    kill_instance redis 11\n    after 4000\n}\n\nforeach_redis_id id {\n    if {$id < 5} {\n        test \"Master #$id should have at least one replica\" {\n            wait_for_condition 1000 50 {\n                [llength [lindex [R $id role] 2]] >= 1\n            } else {\n                fail \"Master #$id has no replicas\"\n            }\n        }\n    }\n}\n\n# Now test the migration to a master which used to be a slave, after\n# a failver.\n\nsource \"../tests/includes/init-tests.tcl\"\n\n# Create a cluster with 5 master and 10 slaves, so that we have 2\n# slaves for each master.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 10\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Kill slave #7 of master #2. Only slave left is #12 now\" {\n    kill_instance redis 7\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing master node #2, #12 should failover\" {\n    kill_instance redis 2\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance 12 is now a master without slaves\" {\n    assert {[RI 12 role] eq {master}}\n}\n\n# The remaining instance is now without slaves. Some other slave\n# should migrate to it.\n\ntest \"Master #12 should get at least one migrated replica\" {\n    wait_for_condition 1000 50 {\n        [llength [lindex [R 12 role] 2]] >= 1\n    } else {\n        fail \"Master #12 has no replicas\"\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/08-update-msg.tcl",
    "content": "# Test UPDATE messages sent by other nodes when the currently authorirative\n# master is unavaialble. The test is performed in the following steps:\n#\n# 1) Master goes down.\n# 2) Slave failover and becomes new master.\n# 3) New master is partitoned away.\n# 4) Old master returns.\n# 5) At this point we expect the old master to turn into a slave ASAP because\n#    of the UPDATE messages it will receive from the other nodes when its\n#    configuration will be found to be outdated.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance #5 is now a master\" {\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Killing the new master #5\" {\n    kill_instance redis 5\n}\n\ntest \"Cluster should be down now\" {\n    assert_cluster_state fail\n}\n\ntest \"Restarting the old master node\" {\n    restart_instance redis 0\n}\n\ntest \"Instance #0 gets converted into a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave}\n    } else {\n        fail \"Old master was not converted into slave\"\n    }\n}\n\ntest \"Restarting the new master node\" {\n    restart_instance redis 5\n}\n\ntest \"Cluster is up again\" {\n    assert_cluster_state ok\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/09-pubsub.tcl",
    "content": "# Test PUBLISH propagation across the cluster.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\nproc test_cluster_publish {instance instances} {\n    # Subscribe all the instances but the one we use to send.\n    for {set j 0} {$j < $instances} {incr j} {\n        if {$j != $instance} {\n            R $j deferred 1\n            R $j subscribe testchannel\n            R $j read; # Read the subscribe reply\n        }\n    }\n\n    set data [randomValue]\n    R $instance PUBLISH testchannel $data\n\n    # Read the message back from all the nodes.\n    for {set j 0} {$j < $instances} {incr j} {\n        if {$j != $instance} {\n            set msg [R $j read]\n            assert {$data eq [lindex $msg 2]}\n            R $j unsubscribe testchannel\n            R $j read; # Read the unsubscribe reply\n            R $j deferred 0\n        }\n    }\n}\n\ntest \"Test publishing to master\" {\n    test_cluster_publish 0 10\n}\n\ntest \"Test publishing to slave\" {\n    test_cluster_publish 5 10\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/10-manual-failover.tcl",
    "content": "# Check the manual failover\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\nset numkeys 50000\nset numops 10000\nset cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]\ncatch {unset content}\narray set content {}\n\ntest \"Send CLUSTER FAILOVER to #5, during load\" {\n    for {set j 0} {$j < $numops} {incr j} {\n        # Write random data to random list.\n        set listid [randomInt $numkeys]\n        set key \"key:$listid\"\n        set ele [randomValue]\n        # We write both with Lua scripts and with plain commands.\n        # This way we are able to stress Lua -> Redis command invocation\n        # as well, that has tests to prevent Lua to write into wrong\n        # hash slots.\n        if {$listid % 2} {\n            $cluster rpush $key $ele\n        } else {\n           $cluster eval {redis.call(\"rpush\",KEYS[1],ARGV[1])} 1 $key $ele\n        }\n        lappend content($key) $ele\n\n        if {($j % 1000) == 0} {\n            puts -nonewline W; flush stdout\n        }\n\n        if {$j == $numops/2} {R 5 cluster failover}\n    }\n}\n\ntest \"Wait for failover\" {\n    wait_for_condition 1000 50 {\n        [CI 1 cluster_current_epoch] > $current_epoch\n    } else {\n        fail \"No failover detected\"\n    }\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 1\n}\n\ntest \"Instance #5 is now a master\" {\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Verify $numkeys keys for consistency with logical content\" {\n    # Check that the Redis Cluster content matches our logical content.\n    foreach {key value} [array get content] {\n        assert {[$cluster lrange $key 0 -1] eq $value}\n    }\n}\n\ntest \"Instance #0 gets converted into a slave\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave}\n    } else {\n        fail \"Old master was not converted into slave\"\n    }\n}\n\n## Check that manual failover does not happen if we can't talk with the master.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"Make instance #0 unreachable without killing it\" {\n    R 0 deferred 1\n    R 0 DEBUG SLEEP 10\n}\n\ntest \"Send CLUSTER FAILOVER to instance #5\" {\n    R 5 cluster failover\n}\n\ntest \"Instance #5 is still a slave after some time (no failover)\" {\n    after 5000\n    assert {[RI 5 role] eq {master}}\n}\n\ntest \"Wait for instance #0 to return back alive\" {\n    R 0 deferred 0\n    assert {[R 0 read] eq {OK}}\n}\n\n## Check with \"force\" failover happens anyway.\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"Make instance #0 unreachable without killing it\" {\n    R 0 deferred 1\n    R 0 DEBUG SLEEP 10\n}\n\ntest \"Send CLUSTER FAILOVER to instance #5\" {\n    R 5 cluster failover force\n}\n\ntest \"Instance #5 is a master after some time\" {\n    wait_for_condition 1000 50 {\n        [RI 5 role] eq {master}\n    } else {\n        fail \"Instance #5 is not a master after some time regardless of FORCE\"\n    }\n}\n\ntest \"Wait for instance #0 to return back alive\" {\n    R 0 deferred 0\n    assert {[R 0 read] eq {OK}}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/11-manual-takeover.tcl",
    "content": "# Manual takeover test\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Killing majority of master nodes\" {\n    kill_instance redis 0\n    kill_instance redis 1\n    kill_instance redis 2\n}\n\ntest \"Cluster should eventually be down\" {\n    assert_cluster_state fail\n}\n\ntest \"Use takeover to bring slaves back\" {\n    R 5 cluster failover takeover\n    R 6 cluster failover takeover\n    R 7 cluster failover takeover\n}\n\ntest \"Cluster should eventually be up again\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 4\n}\n\ntest \"Instance #5, #6, #7 are now masters\" {\n    assert {[RI 5 role] eq {master}}\n    assert {[RI 6 role] eq {master}}\n    assert {[RI 7 role] eq {master}}\n}\n\ntest \"Restarting the previously killed master nodes\" {\n    restart_instance redis 0\n    restart_instance redis 1\n    restart_instance redis 2\n}\n\ntest \"Instance #0, #1, #2 gets converted into a slaves\" {\n    wait_for_condition 1000 50 {\n        [RI 0 role] eq {slave} && [RI 1 role] eq {slave} && [RI 2 role] eq {slave}\n    } else {\n        fail \"Old masters not converted into slaves\"\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/12-replica-migration-2.tcl",
    "content": "# Replica migration test #2.\n#\n# Check that the status of master that can be targeted by replica migration\n# is acquired again, after being getting slots again, in a cluster where the\n# other masters have slaves.\n\nsource \"../tests/includes/init-tests.tcl\"\nsource \"../../../tests/support/cli.tcl\"\n\n# Create a cluster with 5 master and 15 slaves, to make sure there are no\n# empty masters and make rebalancing simpler to handle during the test.\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 15\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Each master should have at least two replicas attached\" {\n    foreach_redis_id id {\n        if {$id < 5} {\n            wait_for_condition 1000 50 {\n                [llength [lindex [R 0 role] 2]] >= 2\n            } else {\n                fail \"Master #$id does not have 2 slaves as expected\"\n            }\n        }\n    }\n}\n\nset master0_id [dict get [get_myself 0] id]\ntest \"Resharding all the master #0 slots away from it\" {\n    set output [exec \\\n        ../../../src/redis-cli --cluster rebalance \\\n        127.0.0.1:[get_instance_attrib redis 0 port] \\\n        {*}[rediscli_tls_config \"../../../tests\"] \\\n        --cluster-weight ${master0_id}=0 >@ stdout ]\n\n}\n\ntest \"Master #0 should lose its replicas\" {\n    wait_for_condition 1000 50 {\n        [llength [lindex [R 0 role] 2]] == 0\n    } else {\n        fail \"Master #0 still has replicas\"\n    }\n}\n\ntest \"Resharding back some slot to master #0\" {\n    # Wait for the cluster config to propagate before attempting a\n    # new resharding.\n    after 10000\n    set output [exec \\\n        ../../../src/redis-cli --cluster rebalance \\\n        127.0.0.1:[get_instance_attrib redis 0 port] \\\n        {*}[rediscli_tls_config \"../../../tests\"] \\\n        --cluster-weight ${master0_id}=.01 \\\n        --cluster-use-empty-masters  >@ stdout]\n}\n\ntest \"Master #0 should re-acquire one or more replicas\" {\n    wait_for_condition 1000 50 {\n        [llength [lindex [R 0 role] 2]] >= 1\n    } else {\n        fail \"Master #0 has no has replicas\"\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/13-no-failover-option.tcl",
    "content": "# Check that the no-failover option works\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n\n    # Configure it to never failover the master\n    R 5 CONFIG SET cluster-slave-no-failover yes\n}\n\ntest \"Instance #5 synced with the master\" {\n    wait_for_condition 1000 50 {\n        [RI 5 master_link_status] eq {up}\n    } else {\n        fail \"Instance #5 master link status is not up\"\n    }\n}\n\ntest \"The nofailover flag is propagated\" {\n    set slave5_id [dict get [get_myself 5] id]\n\n    foreach_redis_id id {\n        wait_for_condition 1000 50 {\n            [has_flag [get_node_by_id $id $slave5_id] nofailover]\n        } else {\n            fail \"Instance $id can't see the nofailover flag of slave\"\n        }\n    }\n}\n\nset current_epoch [CI 1 cluster_current_epoch]\n\ntest \"Killing one master node\" {\n    kill_instance redis 0\n}\n\ntest \"Cluster should be still down after some time\" {\n    after 10000\n    assert_cluster_state fail\n}\n\ntest \"Instance #5 is still a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"Restarting the previously killed master node\" {\n    restart_instance redis 0\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/14-consistency-check.tcl",
    "content": "source \"../tests/includes/init-tests.tcl\"\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster 5 5\n}\n\ntest \"Cluster should start ok\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\nproc find_non_empty_master {} {\n    set master_id_no {}\n    foreach_redis_id id {\n        if {[RI $id role] eq {master} && [R $id dbsize] > 0} {\n            set master_id_no $id\n        }\n    }\n    return $master_id_no\n}\n\nproc get_one_of_my_replica {id} {\n    set replica_port [lindex [lindex [lindex [R $id role] 2] 0] 1]\n    set replica_id_num [get_instance_id_by_port redis $replica_port]\n    return $replica_id_num\n}\n\nproc cluster_write_keys_with_expire {id ttl} {\n    set prefix [randstring 20 20 alpha]\n    set port [get_instance_attrib redis $id port]\n    set cluster [redis_cluster 127.0.0.1:$port]\n    for {set j 100} {$j < 200} {incr j} {\n        $cluster setex key_expire.$j $ttl $prefix.$j\n    }\n    $cluster close\n}\n\nproc test_slave_load_expired_keys {aof} {\n    test \"Slave expired keys is loaded when restarted: appendonly=$aof\" {\n        set master_id [find_non_empty_master]\n        set replica_id [get_one_of_my_replica $master_id]\n\n        set master_dbsize [R $master_id dbsize]\n        set slave_dbsize [R $replica_id dbsize]\n        assert_equal $master_dbsize $slave_dbsize\n\n        set data_ttl 5\n        cluster_write_keys_with_expire $master_id $data_ttl\n        after 100\n        set replica_dbsize_1 [R $replica_id dbsize]\n        assert {$replica_dbsize_1  > $slave_dbsize}\n\n        R $replica_id config set appendonly $aof\n        R $replica_id config rewrite\n\n        set start_time [clock seconds]\n        set end_time [expr $start_time+$data_ttl+2]\n        R $replica_id save\n        set replica_dbsize_2 [R $replica_id dbsize]\n        assert {$replica_dbsize_2  > $slave_dbsize}\n        kill_instance redis $replica_id\n\n        set master_port [get_instance_attrib redis $master_id port]\n        exec ../../../src/redis-cli -h 127.0.0.1 -p $master_port debug sleep [expr $data_ttl+3] > /dev/null &\n\n        while {[clock seconds] <= $end_time} {\n            #wait for $data_ttl seconds\n        }\n        restart_instance redis $replica_id\n\n        wait_for_condition 200 50 {\n            [R $replica_id ping] eq {PONG}\n        } else {\n            fail \"replica #$replica_id not started\"\n        }\n\n        set replica_dbsize_3 [R $replica_id dbsize]\n        assert {$replica_dbsize_3  > $slave_dbsize}\n    }\n}\n\ntest_slave_load_expired_keys no\nafter 5000\ntest_slave_load_expired_keys yes\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/15-cluster-slots.tcl",
    "content": "source \"../tests/includes/init-tests.tcl\"\n\nproc cluster_allocate_mixedSlots {n} {\n    set slot 16383\n    while {$slot >= 0} {\n        set node [expr {$slot % $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\nproc create_cluster_with_mixedSlot {masters slaves} {\n    cluster_allocate_mixedSlots $masters\n    if {$slaves} {\n        cluster_allocate_slaves $masters $slaves\n    }\n    assert_cluster_state ok\n}\n\ntest \"Create a 5 nodes cluster\" {\n    create_cluster_with_mixedSlot 5 15\n}\n\ntest \"Cluster is up\" {\n    assert_cluster_state ok\n}\n\ntest \"Cluster is writable\" {\n    cluster_write_test 0\n}\n\ntest \"Instance #5 is a slave\" {\n    assert {[RI 5 role] eq {slave}}\n}\n\ntest \"client do not break when cluster slot\" {\n    R 0 config set client-output-buffer-limit \"normal 33554432 16777216 60\"\n    if { [catch {R 0 cluster slots}] } {\n        fail \"output overflow when cluster slots\"\n    }\n}"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tests/helpers/onlydots.tcl",
    "content": "# Read the standard input and only shows dots in the output, filtering out\n# all the other characters. Designed to avoid bufferization so that when\n# we get the output of redis-trib and want to show just the dots, we'll see\n# the dots as soon as redis-trib will output them.\n\nfconfigure stdin -buffering none\n\nwhile 1 {\n    set c [read stdin 1]\n    if {$c eq {}} {\n        exit 0; # EOF\n    } elseif {$c eq {.}} {\n        puts -nonewline .\n        flush stdout\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\ntest \"Cluster nodes are reachable\" {\n    foreach_redis_id id {\n        # Every node should be reachable.\n        wait_for_condition 1000 50 {\n            ([catch {R $id ping} ping_reply] == 0) &&\n            ($ping_reply eq {PONG})\n        } else {\n            catch {R $id ping} err\n            fail \"Node #$id keeps replying '$err' to PING.\"\n        }\n    }\n}\n\ntest \"Cluster nodes hard reset\" {\n    foreach_redis_id id {\n        if {$::valgrind} {\n            set node_timeout 10000\n        } else {\n            set node_timeout 3000\n        }\n        catch {R $id flushall} ; # May fail for readonly slaves.\n        R $id MULTI\n        R $id cluster reset hard\n        R $id cluster set-config-epoch [expr {$id+1}]\n        R $id EXEC\n        R $id config set cluster-node-timeout $node_timeout\n        R $id config set cluster-slave-validity-factor 10\n        R $id config rewrite\n    }\n}\n\ntest \"Cluster Join and auto-discovery test\" {\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"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/cluster/tmp/.gitignore",
    "content": "redis_*\nsentinel_*\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/helpers/bg_block_op.tcl",
    "content": "source tests/support/redis.tcl\nsource tests/support/util.tcl\n\nset ::tlsdir \"tests/tls\"\n\n# This function sometimes writes sometimes blocking-reads from lists/sorted\n# sets. There are multiple processes like this executing at the same time\n# so that we have some chance to trap some corner condition if there is\n# a regression. For this to happen it is important that we narrow the key\n# space to just a few elements, and balance the operations so that it is\n# unlikely that lists and zsets just get more data without ever causing\n# blocking.\nproc bg_block_op {host port db ops tls} {\n    set r [redis $host $port 0 $tls]\n    $r select $db\n\n    for {set j 0} {$j < $ops} {incr j} {\n\n        # List side\n        set k list_[randomInt 10]\n        set k2 list_[randomInt 10]\n        set v [randomValue]\n\n        randpath {\n            randpath {\n                $r rpush $k $v\n            } {\n                $r lpush $k $v\n            }\n        } {\n            $r blpop $k 2\n        } {\n            $r blpop $k $k2 2\n        }\n\n        # Zset side\n        set k zset_[randomInt 10]\n        set k2 zset_[randomInt 10]\n        set v1 [randomValue]\n        set v2 [randomValue]\n\n        randpath {\n            $r zadd $k [randomInt 10000] $v\n        } {\n            $r zadd $k [randomInt 10000] $v [randomInt 10000] $v2\n        } {\n            $r bzpopmin $k 2\n        } {\n            $r bzpopmax $k 2\n        }\n    }\n}\n\nbg_block_op [lindex $argv 0] [lindex $argv 1] [lindex $argv 2] [lindex $argv 3] [lindex $argv 4]\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/helpers/bg_complex_data.tcl",
    "content": "source tests/support/redis.tcl\nsource tests/support/util.tcl\n\nset ::tlsdir \"tests/tls\"\n\nproc bg_complex_data {host port db ops tls} {\n    set r [redis $host $port 0 $tls]\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] [lindex $argv 4]\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/helpers/gen_write_load.tcl",
    "content": "source tests/support/redis.tcl\n\nset ::tlsdir \"tests/tls\"\n\nproc gen_write_load {host port seconds tls} {\n    set start_time [clock seconds]\n    set r [redis $host $port 1 $tls]\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] [lindex $argv 3]\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 software 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 ::valgrind 0\nset ::tls 0\nset ::pause_on_error 0\nset ::simulate_error 0\nset ::failed 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# Execute the specified instance of the server specified by 'type', using\n# the provided configuration file. Returns the PID of the process.\nproc exec_instance {type cfgfile} {\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\n    if {$::valgrind} {\n        set pid [exec valgrind --track-origins=yes --suppressions=../../../src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full ../../../src/${prgname} $cfgfile &]\n    } else {\n        set pid [exec ../../../src/${prgname} $cfgfile &]\n    }\n    return $pid\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        if {$::tls} {\n            puts $cfg \"tls-port $port\"\n            puts $cfg \"tls-replication yes\"\n            puts $cfg \"tls-cluster yes\"\n            puts $cfg \"port 0\"\n            puts $cfg [format \"tls-cert-file %s/../../tls/redis.crt\" [pwd]]\n            puts $cfg [format \"tls-key-file %s/../../tls/redis.key\" [pwd]]\n            puts $cfg [format \"tls-dh-params-file %s/../../tls/redis.dh\" [pwd]]\n            puts $cfg [format \"tls-ca-cert-file %s/../../tls/ca.crt\" [pwd]]\n            puts $cfg \"loglevel debug\"\n        } else {\n            puts $cfg \"port $port\"\n        }\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        set pid [exec_instance $type $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        set link [redis 127.0.0.1 $port 0 $::tls]\n        $link reconnect 1\n        lappend ::${type}_instances [list \\\n            pid $pid \\\n            host 127.0.0.1 \\\n            port $port \\\n            link $link \\\n        ]\n    }\n}\n\nproc log_crashes {} {\n    set start_pattern {*REDIS BUG REPORT START*}\n    set logs [glob */log.txt]\n    foreach log $logs {\n        set fd [open $log]\n        set found 0\n        while {[gets $fd line] >= 0} {\n            if {[string match $start_pattern $line]} {\n                puts \"\\n*** Crash report found in $log ***\"\n                set found 1\n            }\n            if {$found} {puts $line}\n        }\n    }\n}\n\nproc cleanup {} {\n    puts \"Cleaning up...\"\n    log_crashes\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    incr ::failed\n    puts \"WARNING: Aborting the test.\"\n    puts \">>>>>>>> $msg\"\n    if {$::pause_on_error} pause_on_error\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 {--valgrind}} {\n            set ::valgrind 1\n        } elseif {$opt eq {--tls}} {\n            package require tls 1.6\n            ::tls::init \\\n                -cafile \"$::tlsdir/ca.crt\" \\\n                -certfile \"$::tlsdir/redis.crt\" \\\n                -keyfile \"$::tlsdir/redis.key\"\n            set ::tls 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 \"--valgrind              Run with valgrind.\"\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-redis-logs}} {\n            set count 10\n            if {[lindex $argv 1] ne {}} {set count [lindex $argv 1]}\n            foreach_redis_id id {\n                puts \"=== REDIS $id ====\"\n                puts [exec tail -$count redis_$id/log.txt]\n                puts \"---------------------\\n\"\n            }\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 \"show-redis-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        incr ::failed\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\n# Check memory leaks when running on OSX using the \"leaks\" utility.\nproc check_leaks instance_types {\n    if {[string match {*Darwin*} [exec uname -a]]} {\n        puts -nonewline \"Testing for memory leaks...\"; flush stdout\n        foreach type $instance_types {\n            foreach_instance_id [set ::${type}_instances] id {\n                if {[instance_is_killed $type $id]} continue\n                set pid [get_instance_attrib $type $id 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                } else {\n                    puts -nonewline \"$type/$pid \"\n                    flush stdout\n                }\n                if {![string match {*0 leaks*} $output]} {\n                    puts [colorstr red \"=== MEMORY LEAK DETECTED ===\"]\n                    puts \"Instance type $type, ID $id:\"\n                    puts $output\n                    puts \"===\"\n                    incr ::failed\n                }\n            }\n        }\n        puts \"\"\n    }\n}\n\n# Execute all the units inside the 'tests' directory.\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        check_leaks {redis sentinel}\n    }\n}\n\n# Print a message and exists with 0 / 1 according to zero or more failures.\nproc end_tests {} {\n    if {$::failed == 0} {\n        puts \"GOOD! No errors.\"\n        exit 0\n    } else {\n        puts \"WARNING $::failed test(s) failed.\"\n        exit 1\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    set port [get_instance_attrib $type $id port]\n\n    if {$pid == -1} {\n        error \"You tried to kill $type $id twice.\"\n    }\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    # Wait for the port it was using to be available again, so that's not\n    # an issue to start a new server ASAP with the same port.\n    set retry 10\n    while {[incr retry -1]} {\n        set port_is_free [catch {set s [socket 127.0.01 $port]}]\n        if {$port_is_free} break\n        catch {close $s}\n        after 1000\n    }\n    if {$retry == 0} {\n        error \"Port $port does not return available after killing instance.\"\n    }\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    expr {$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    set pid [exec_instance $type $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 #$id: ping timeout\"\n    }\n\n    # Connect with it with a fresh link\n    set link [redis 127.0.0.1 $port 0 $::tls]\n    $link reconnect 1\n    set_instance_attrib $type $id link $link\n\n    # Make sure the instance is not loading the dataset when this\n    # function returns.\n    while 1 {\n        catch {[$link ping]} retval\n        if {[string match {*LOADING*} $retval]} {\n            after 100\n            continue\n        } else {\n            break\n        }\n    }\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/aof-race.tcl",
    "content": "set defaults { appendonly {yes} appendfilename {appendonly.aof} aof-use-rdb-preamble {no} }\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] 0 $::tls]\n        set bench [open \"|src/redis-benchmark -q -s [srv unixsocket] -c 20 -n 20000 incr foo\" \"r+\"]\n\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] 0 $::tls]\n        assert_equal 20000 [$client get foo]\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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    ## Server can start when aof-load-truncated is set to yes and AOF\n    ## is truncated, with an incomplete MULTI block.\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 aof-load-truncated yes] {\n        test \"Unfinished MULTI: Server should start if load-truncated is yes\" {\n            assert_equal 1 [is_alive $srv]\n        }\n    }\n\n    ## Should also start with truncated AOF without incomplete MULTI block.\n    create_aof {\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [formatCommand incr foo]\n        append_to_aof [string range [formatCommand incr foo] 0 end-1]\n    }\n\n    start_server_aof [list dir $server_path aof-load-truncated yes] {\n        test \"Short read: Server should start if load-truncated is yes\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]\n\n        wait_for_condition 50 100 {\n            [catch {$client ping} e] == 0\n        } else {\n            fail \"Loading DB is taking too much time.\"\n        }\n\n        test \"Truncated AOF loaded: we expect foo to be equal to 5\" {\n            assert {[$client get foo] eq \"5\"}\n        }\n\n        test \"Append a new command after loading an incomplete AOF\" {\n            $client incr foo\n        }\n    }\n\n    # Now the AOF file is expected to be correct\n    start_server_aof [list dir $server_path aof-load-truncated yes] {\n        test \"Short read + command: Server should start\" {\n            assert_equal 1 [is_alive $srv]\n        }\n\n        set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]\n\n        wait_for_condition 50 100 {\n            [catch {$client ping} e] == 0\n        } else {\n            fail \"Loading DB is taking too much time.\"\n        }\n\n        test \"Truncated AOF loaded: we expect foo to be equal to 6 now\" {\n            assert {[$client get foo] eq \"6\"}\n        }\n    }\n\n    ## Test that the server exits when the AOF contains a format error\n    create_aof {\n        append_to_aof [formatCommand set foo hello]\n        append_to_aof \"!!!\"\n        append_to_aof [formatCommand set foo hello]\n    }\n\n    start_server_aof [list dir $server_path aof-load-truncated yes] {\n        test \"Bad format: 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 -1 < [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 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 aof-load-truncated no] {\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 -1 < [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 aof-load-truncated no] {\n        test \"Short read: 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 -1 < [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 aof-load-truncated no] {\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 parseable\" {\n            set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\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 aof-load-truncated no] {\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] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\n            assert_equal 1 [$client scard set]\n        }\n    }\n\n    ## Uses the alsoPropagate() API.\n    create_aof {\n        append_to_aof [formatCommand sadd set foo]\n        append_to_aof [formatCommand sadd set bar]\n        append_to_aof [formatCommand sadd set gah]\n        append_to_aof [formatCommand spop set 2]\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] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\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 aof-load-truncated no] {\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] 0 $::tls]\n            wait_for_condition 50 100 {\n                [catch {$client ping} e] == 0\n            } else {\n                fail \"Loading DB is taking too much time.\"\n            }\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    start_server {overrides {appendonly {yes} appendfilename {appendonly.aof} appendfsync always}} {\n        test {AOF fsync always barrier issue} {\n            set rd [redis_deferring_client]\n            # Set a sleep when aof is flushed, so that we have a chance to look\n            # at the aof size and detect if the response of an incr command\n            # arrives before the data was written (and hopefully fsynced)\n            # We create a big reply, which will hopefully not have room in the\n            # socket buffers, and will install a write handler, then we sleep\n            # a big and issue the incr command, hoping that the last portion of\n            # the output buffer write, and the processing of the incr will happen\n            # in the same event loop cycle.\n            # Since the socket buffers and timing are unpredictable, we fuzz this\n            # test with slightly different sizes and sleeps a few times.\n            for {set i 0} {$i < 10} {incr i} {\n                r debug aof-flush-sleep 0\n                r del x\n                r setrange x [expr {int(rand()*5000000)+10000000}] x\n                r debug aof-flush-sleep 500000\n                set aof [file join [lindex [r config get dir] 1] appendonly.aof]\n                set size1 [file size $aof]\n                $rd get x\n                after [expr {int(rand()*30)}]\n                $rd incr new_value\n                $rd read\n                $rd read\n                set size2 [file size $aof]\n                assert {$size1 != $size2}\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/block-repl.tcl",
    "content": "# Test replication of blocking lists and zset operations.\n# Unlike stream operations such operations are \"pop\" style, so they consume\n# the list or sorted set, and must be replicated correctly.\n\nproc start_bg_block_op {host port db ops tls} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/bg_block_op.tcl $host $port $db $ops $tls &\n}\n\nproc stop_bg_block_op {handle} {\n    catch {exec /bin/kill -9 $handle}\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        set load_handle0 [start_bg_block_op $master_host $master_port 9 100000 $::tls]\n        set load_handle1 [start_bg_block_op $master_host $master_port 9 100000 $::tls]\n        set load_handle2 [start_bg_block_op $master_host $master_port 9 100000 $::tls]\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 blocking lists and sorted sets operations} {\n            after 25000\n            stop_bg_block_op $load_handle0\n            stop_bg_block_op $load_handle1\n            stop_bg_block_op $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\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 - Replica 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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/tests/integration/logging.tcl",
    "content": "set server_path [tmpdir server.log]\nset system_name [string tolower [exec uname -s]]\n\nif {$system_name eq {linux} || $system_name eq {darwin}} {\n    start_server [list overrides [list dir $server_path]] {\n        test \"Server is able to generate a stack trace on selected systems\" {\n            r config set watchdog-period 200\n            r debug sleep 1\n            set pattern \"*debugCommand*\"\n            set retry 10\n            while {$retry} {\n                set result [exec tail -100 < [srv 0 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 stack trace not found into log file\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/psync2-pingoff.tcl",
    "content": "# Test the meaningful offset implementation to make sure masters\n# are able to PSYNC with replicas even if the replication stream\n# has pending PINGs at the end.\n\nstart_server {tags {\"psync2\"}} {\nstart_server {} {\n    # Config\n    set debug_msg 0                 ; # Enable additional debug messages\n\n    for {set j 0} {$j < 2} {incr j} {\n        set R($j) [srv [expr 0-$j] client]\n        set R_host($j) [srv [expr 0-$j] host]\n        set R_port($j) [srv [expr 0-$j] port]\n        $R($j) CONFIG SET repl-ping-replica-period 1\n        if {$debug_msg} {puts \"Log file: [srv [expr 0-$j] stdout]\"}\n    }\n\n    # Setup replication\n    test \"PSYNC2 meaningful offset: setup\" {\n        $R(1) replicaof $R_host(0) $R_port(0)\n        $R(0) set foo bar\n        wait_for_condition 50 1000 {\n            [status $R(1) master_link_status] == \"up\" &&\n            [$R(0) dbsize] == 1 && [$R(1) dbsize] == 1\n        } else {\n            fail \"Replicas not replicating from master\"\n        }\n    }\n\n    test \"PSYNC2 meaningful offset: write and wait replication\" {\n        $R(0) INCR counter\n        $R(0) INCR counter\n        $R(0) INCR counter\n        wait_for_condition 50 1000 {\n            [$R(0) GET counter] eq [$R(1) GET counter]\n        } else {\n            fail \"Master and replica don't agree about counter\"\n        }\n    }\n\n    # In this test we'll make sure the replica will get stuck, but with\n    # an active connection: this way the master will continue to send PINGs\n    # every second (we modified the PING period earlier)\n    test \"PSYNC2 meaningful offset: pause replica and promote it\" {\n        $R(1) MULTI\n        $R(1) DEBUG SLEEP 5\n        $R(1) SLAVEOF NO ONE\n        $R(1) EXEC\n        $R(1) ping ; # Wait for it to return back available\n    }\n\n    test \"Make the old master a replica of the new one and check conditions\" {\n        set sync_partial [status $R(1) sync_partial_ok]\n        assert {$sync_partial == 0}\n        $R(0) REPLICAOF $R_host(1) $R_port(1)\n        wait_for_condition 50 1000 {\n            [status $R(1) sync_partial_ok] == 1\n        } else {\n            fail \"The new master was not able to partial sync\"\n        }\n    }\n}}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/psync2-reg.tcl",
    "content": "# Issue 3899 regression test.\n# We create a chain of three instances: master -> slave -> slave2\n# and continuously break the link while traffic is generated by\n# redis-benchmark. At the end we check that the data is the same\n# everywhere.\n\nstart_server {tags {\"psync2\"}} {\nstart_server {} {\nstart_server {} {\n    # Config\n    set debug_msg 0                 ; # Enable additional debug messages\n\n    set no_exit 0                   ; # Do not exit at end of the test\n\n    set duration 20                 ; # Total test seconds\n\n    for {set j 0} {$j < 3} {incr j} {\n        set R($j) [srv [expr 0-$j] client]\n        set R_host($j) [srv [expr 0-$j] host]\n        set R_port($j) [srv [expr 0-$j] port]\n        set R_unixsocket($j) [srv [expr 0-$j] unixsocket]\n        if {$debug_msg} {puts \"Log file: [srv [expr 0-$j] stdout]\"}\n    }\n\n    # Setup the replication and backlog parameters\n    test \"PSYNC2 #3899 regression: setup\" {\n        $R(1) slaveof $R_host(0) $R_port(0)\n        $R(2) slaveof $R_host(0) $R_port(0)\n        $R(0) set foo bar\n        wait_for_condition 50 1000 {\n            [status $R(1) master_link_status] == \"up\" &&\n            [status $R(2) master_link_status] == \"up\" &&\n            [$R(1) dbsize] == 1 &&\n            [$R(2) dbsize] == 1\n        } else {\n            fail \"Replicas not replicating from master\"\n        }\n        $R(0) config set repl-backlog-size 10mb\n        $R(1) config set repl-backlog-size 10mb\n    }\n\n    set cycle_start_time [clock milliseconds]\n    set bench_pid [exec src/redis-benchmark -s $R_unixsocket(0) -n 10000000 -r 1000 incr __rand_int__ > /dev/null &]\n    while 1 {\n        set elapsed [expr {[clock milliseconds]-$cycle_start_time}]\n        if {$elapsed > $duration*1000} break\n        if {rand() < .05} {\n            test \"PSYNC2 #3899 regression: kill first replica\" {\n                $R(1) client kill type master\n            }\n        }\n        if {rand() < .05} {\n            test \"PSYNC2 #3899 regression: kill chained replica\" {\n                $R(2) client kill type master\n            }\n        }\n        after 100\n    }\n    exec kill -9 $bench_pid\n\n    if {$debug_msg} {\n        for {set j 0} {$j < 100} {incr j} {\n            if {\n                [$R(0) debug digest] == [$R(1) debug digest] &&\n                [$R(1) debug digest] == [$R(2) debug digest]\n            } break\n            puts [$R(0) debug digest]\n            puts [$R(1) debug digest]\n            puts [$R(2) debug digest]\n            after 1000\n        }\n    }\n\n    test \"PSYNC2 #3899 regression: verify consistency\" {\n        wait_for_condition 50 1000 {\n            ([$R(0) debug digest] eq [$R(1) debug digest]) &&\n            ([$R(1) debug digest] eq [$R(2) debug digest])\n        } else {\n            fail \"The three instances have different data sets\"\n        }\n    }\n}}}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/psync2.tcl",
    "content": "start_server {tags {\"psync2\"}} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\n    set master_id 0                 ; # Current master\n    set start_time [clock seconds]  ; # Test start time\n    set counter_value 0             ; # Current value of the Redis counter \"x\"\n\n    # Config\n    set debug_msg 0                 ; # Enable additional debug messages\n\n    set no_exit 0                   ; # Do not exit at end of the test\n\n    set duration 20                 ; # Total test seconds\n\n    set genload 1                   ; # Load master with writes at every cycle\n\n    set genload_time 5000           ; # Writes duration time in ms\n\n    set disconnect 1                ; # Break replication link between random\n                                      # master and slave instances while the\n                                      # master is loaded with writes.\n\n    set disconnect_period 1000      ; # Disconnect repl link every N ms.\n\n    for {set j 0} {$j < 5} {incr j} {\n        set R($j) [srv [expr 0-$j] client]\n        set R_host($j) [srv [expr 0-$j] host]\n        set R_port($j) [srv [expr 0-$j] port]\n        if {$debug_msg} {puts \"Log file: [srv [expr 0-$j] stdout]\"}\n    }\n\n    set cycle 1\n    while {([clock seconds]-$start_time) < $duration} {\n        test \"PSYNC2: --- CYCLE $cycle ---\" {}\n        incr cycle\n\n        # Create a random replication layout.\n        # Start with switching master (this simulates a failover).\n\n        # 1) Select the new master.\n        set master_id [randomInt 5]\n        set used [list $master_id]\n        test \"PSYNC2: \\[NEW LAYOUT\\] Set #$master_id as master\" {\n            $R($master_id) slaveof no one\n            $R($master_id) config set repl-ping-replica-period 1 ;# increse the chance that random ping will cause issues\n            if {$counter_value == 0} {\n                $R($master_id) set x $counter_value\n            }\n        }\n\n        # 2) Attach all the slaves to a random instance\n        while {[llength $used] != 5} {\n            while 1 {\n                set slave_id [randomInt 5]\n                if {[lsearch -exact $used $slave_id] == -1} break\n            }\n            set rand [randomInt [llength $used]]\n            set mid [lindex $used $rand]\n            set master_host $R_host($mid)\n            set master_port $R_port($mid)\n\n            test \"PSYNC2: Set #$slave_id to replicate from #$mid\" {\n                $R($slave_id) slaveof $master_host $master_port\n            }\n            lappend used $slave_id\n        }\n\n        # Wait for replicas to sync. so next loop won't get -LOADING error\n        wait_for_condition 50 1000 {\n            [status $R([expr {($master_id+1)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+2)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+3)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+4)%5}]) master_link_status] == \"up\"\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n\n        # 3) Increment the counter and wait for all the instances\n        # to converge.\n        test \"PSYNC2: cluster is consistent after failover\" {\n            $R($master_id) incr x; incr counter_value\n            for {set j 0} {$j < 5} {incr j} {\n                wait_for_condition 50 1000 {\n                    [$R($j) get x] == $counter_value\n                } else {\n                    fail \"Instance #$j x variable is inconsistent\"\n                }\n            }\n        }\n\n        # 4) Generate load while breaking the connection of random\n        # slave-master pairs.\n        test \"PSYNC2: generate load while killing replication links\" {\n            set t [clock milliseconds]\n            set next_break [expr {$t+$disconnect_period}]\n            while {[clock milliseconds]-$t < $genload_time} {\n                if {$genload} {\n                    $R($master_id) incr x; incr counter_value\n                }\n                if {[clock milliseconds] == $next_break} {\n                    set next_break \\\n                        [expr {[clock milliseconds]+$disconnect_period}]\n                    set slave_id [randomInt 5]\n                    if {$disconnect} {\n                        $R($slave_id) client kill type master\n                        if {$debug_msg} {\n                            puts \"+++ Breaking link for replica #$slave_id\"\n                        }\n                    }\n                }\n            }\n        }\n\n        # 5) Increment the counter and wait for all the instances\n        set x [$R($master_id) get x]\n        test \"PSYNC2: cluster is consistent after load (x = $x)\" {\n            for {set j 0} {$j < 5} {incr j} {\n                wait_for_condition 50 1000 {\n                    [$R($j) get x] == $counter_value\n                } else {\n                    fail \"Instance #$j x variable is inconsistent\"\n                }\n            }\n        }\n\n        # wait for all the slaves to be in sync with the master, due to pings, we have to re-sample the master constantly too\n        wait_for_condition 500 100 {\n            [status $R($master_id) master_repl_offset] == [status $R(0) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(1) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(2) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(3) master_repl_offset] &&\n            [status $R($master_id) master_repl_offset] == [status $R(4) master_repl_offset]\n        } else {\n            for {set j 0} {$j < 5} {incr j} {\n                puts \"$j: sync_full: [status $R($j) sync_full]\"\n                puts \"$j: id1      : [status $R($j) master_replid]:[status $R($j) master_repl_offset]\"\n                puts \"$j: id2      : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]\"\n                puts \"$j: backlog  : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]\"\n                puts \"---\"\n            }\n            fail \"Slaves are not in sync with the master after too long time.\"\n        }\n\n        # Put down the old master so that it cannot generate more\n        # replication stream, this way in the next master switch, the time at\n        # which we move slaves away is not important, each will have full\n        # history (otherwise PINGs will make certain slaves have more history),\n        # and sometimes a full resync will be needed.\n        $R($master_id) slaveof 127.0.0.1 0 ;# We use port zero to make it fail.\n\n        if {$debug_msg} {\n            for {set j 0} {$j < 5} {incr j} {\n                puts \"$j: sync_full: [status $R($j) sync_full]\"\n                puts \"$j: id1      : [status $R($j) master_replid]:[status $R($j) master_repl_offset]\"\n                puts \"$j: id2      : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]\"\n                puts \"$j: backlog  : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]\"\n                puts \"---\"\n            }\n        }\n\n        test \"PSYNC2: total sum of full synchronizations is exactly 4\" {\n            set sum 0\n            for {set j 0} {$j < 5} {incr j} {\n                incr sum [status $R($j) sync_full]\n            }\n            assert {$sum == 4}\n        }\n\n        # Limit anyway the maximum number of cycles. This is useful when the\n        # test is skipped via --only option of the test suite. In that case\n        # we don't want to see many seconds of this test being just skipped.\n        if {$cycle > 50} break\n    }\n\n    test \"PSYNC2: Bring the master back again for next test\" {\n        $R($master_id) slaveof no one\n        set master_host $R_host($master_id)\n        set master_port $R_port($master_id)\n        for {set j 0} {$j < 5} {incr j} {\n            if {$j == $master_id} continue\n            $R($j) slaveof $master_host $master_port\n        }\n\n        # Wait for replicas to sync. it is not enough to just wait for connected_slaves==4\n        # since we might do the check before the master realized that they're disconnected\n        wait_for_condition 50 1000 {\n            [status $R($master_id) connected_slaves] == 4 &&\n            [status $R([expr {($master_id+1)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+2)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+3)%5}]) master_link_status] == \"up\" &&\n            [status $R([expr {($master_id+4)%5}]) master_link_status] == \"up\"\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n    }\n\n    test \"PSYNC2: Partial resync after restart using RDB aux fields\" {\n        # Pick a random slave\n        set slave_id [expr {($master_id+1)%5}]\n        set sync_count [status $R($master_id) sync_full]\n        set sync_partial [status $R($master_id) sync_partial_ok]\n        set sync_partial_err [status $R($master_id) sync_partial_err]\n        catch {\n            $R($slave_id) config rewrite\n            $R($slave_id) debug restart\n        }\n        # note: just waiting for connected_slaves==4 has a race condition since\n        # we might do the check before the master realized that the slave disconnected\n        wait_for_condition 50 1000 {\n            [status $R($master_id) sync_partial_ok] == $sync_partial + 1\n        } else {\n            puts \"prev sync_full: $sync_count\"\n            puts \"prev sync_partial_ok: $sync_partial\"\n            puts \"prev sync_partial_err: $sync_partial_err\"\n            puts [$R($master_id) info stats]\n            fail \"Replica didn't partial sync\"\n        }\n        set new_sync_count [status $R($master_id) sync_full]\n        assert {$sync_count == $new_sync_count}\n    }\n\n    test \"PSYNC2: Replica RDB restart with EVALSHA in backlog issue #4483\" {\n        # Pick a random slave\n        set slave_id [expr {($master_id+1)%5}]\n        set sync_count [status $R($master_id) sync_full]\n\n        # Make sure to replicate the first EVAL while the salve is online\n        # so that it's part of the scripts the master believes it's safe\n        # to propagate as EVALSHA.\n        $R($master_id) EVAL {return redis.call(\"incr\",\"__mycounter\")} 0\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n\n        # Wait for the two to sync\n        wait_for_condition 50 1000 {\n            [$R($master_id) debug digest] == [$R($slave_id) debug digest]\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n\n        # Prevent the slave from receiving master updates, and at\n        # the same time send a new script several times to the\n        # master, so that we'll end with EVALSHA into the backlog.\n        $R($slave_id) slaveof 127.0.0.1 0\n\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n        $R($master_id) EVALSHA e6e0b547500efcec21eddb619ac3724081afee89 0\n\n        catch {\n            $R($slave_id) config rewrite\n            $R($slave_id) debug restart\n        }\n\n        # Reconfigure the slave correctly again, when it's back online.\n        set retry 50\n        while {$retry} {\n            if {[catch {\n                $R($slave_id) slaveof $master_host $master_port\n            }]} {\n                after 1000\n            } else {\n                break\n            }\n            incr retry -1\n        }\n\n        # The master should be back at 4 slaves eventually\n        wait_for_condition 50 1000 {\n            [status $R($master_id) connected_slaves] == 4\n        } else {\n            fail \"Replica not reconnecting\"\n        }\n        set new_sync_count [status $R($master_id) sync_full]\n        assert {$sync_count == $new_sync_count}\n\n        # However if the slave started with the full state of the\n        # scripting engine, we should now have the same digest.\n        wait_for_condition 50 1000 {\n            [$R($master_id) debug digest] == [$R($slave_id) debug digest]\n        } else {\n            fail \"Debug digest mismatch between master and replica in post-restart handshake\"\n        }\n    }\n\n    if {$no_exit} {\n        while 1 { puts -nonewline .; flush stdout; after 1000}\n    }\n\n}}}}}\n\nstart_server {tags {\"psync2\"}} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\nstart_server {} {\n    test {pings at the end of replication stream are ignored for psync} {\n        set master [srv -4 client]\n        set master_host [srv -4 host]\n        set master_port [srv -4 port]\n        set replica1 [srv -3 client]\n        set replica2 [srv -2 client]\n        set replica3 [srv -1 client]\n        set replica4 [srv -0 client]\n\n        $replica1 replicaof $master_host $master_port\n        $replica2 replicaof $master_host $master_port\n        $replica3 replicaof $master_host $master_port\n        $replica4 replicaof $master_host $master_port\n        wait_for_condition 50 1000 {\n            [status $master connected_slaves] == 4\n        } else {\n            fail \"replicas didn't connect\"\n        }\n\n        $master incr x\n        wait_for_condition 50 1000 {\n            [$replica1 get x] == 1 && [$replica2 get x] == 1 &&\n            [$replica3 get x] == 1 && [$replica4 get x] == 1\n        } else {\n            fail \"replicas didn't get incr\"\n        }\n\n        # disconnect replica1 and replica2\n        # and wait for the master to send a ping to replica3 and replica4\n        $replica1 replicaof no one\n        $replica2 replicaof 127.0.0.1 1 ;# we can't promote it to master since that will cycle the replication id\n        $master config set repl-ping-replica-period 1\n        after 1500\n\n        # make everyone sync from the replica1 that didn't get the last ping from the old master\n        # replica4 will keep syncing from the old master which now syncs from replica1\n        # and replica2 will re-connect to the old master (which went back in time)\n        set new_master_host [srv -3 host]\n        set new_master_port [srv -3 port]\n        $replica3 replicaof $new_master_host $new_master_port\n        $master replicaof $new_master_host $new_master_port\n        $replica2 replicaof $master_host $master_port\n        wait_for_condition 50 1000 {\n            [status $replica2 master_link_status] == \"up\" &&\n            [status $replica3 master_link_status] == \"up\" &&\n            [status $replica4 master_link_status] == \"up\" &&\n            [status $master master_link_status] == \"up\"\n        } else {\n            fail \"replicas didn't connect\"\n        }\n\n        # make sure replication is still alive and kicking\n        $replica1 incr x\n        wait_for_condition 50 1000 {\n            [$replica2 get x] == 2 &&\n            [$replica3 get x] == 2 &&\n            [$replica4 get x] == 2 &&\n            [$master get x] == 2\n        } else {\n            fail \"replicas didn't get incr\"\n        }\n\n        # make sure there are full syncs other than the initial ones\n        assert_equal [status $master sync_full] 4\n        assert_equal [status $replica1 sync_full] 0\n        assert_equal [status $replica2 sync_full] 0\n        assert_equal [status $replica3 sync_full] 0\n        assert_equal [status $replica4 sync_full] 0\n\n        # force psync\n        $master client kill type master\n        $replica2 client kill type master\n        $replica3 client kill type master\n        $replica4 client kill type master\n\n        # make sure replication is still alive and kicking\n        $replica1 incr x\n        wait_for_condition 50 1000 {\n            [$replica2 get x] == 3 &&\n            [$replica3 get x] == 3 &&\n            [$replica4 get x] == 3 &&\n            [$master get x] == 3\n        } else {\n            fail \"replicas didn't get incr\"\n        }\n\n        # make sure there are full syncs other than the initial ones\n        assert_equal [status $master sync_full] 4\n        assert_equal [status $replica1 sync_full] 0\n        assert_equal [status $replica2 sync_full] 0\n        assert_equal [status $replica3 sync_full] 0\n        assert_equal [status $replica4 sync_full] 0\n}\n}}}}}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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  } {\"0\",\"compressible\",\"string\",\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\"0\",\"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\"0\",\"hash_zipped\",\"hash\",\"a\",\"1\",\"b\",\"2\",\"c\",\"3\",\n\"0\",\"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\"0\",\"list_zipped\",\"list\",\"1\",\"2\",\"3\",\"a\",\"b\",\"c\",\"100000\",\"6000000000\",\n\"0\",\"number\",\"string\",\"10\"\n\"0\",\"set\",\"set\",\"1\",\"100000\",\"2\",\"3\",\"6000000000\",\"a\",\"b\",\"c\",\n\"0\",\"set_zipped_1\",\"set\",\"1\",\"2\",\"3\",\"4\",\n\"0\",\"set_zipped_2\",\"set\",\"100000\",\"200000\",\"300000\",\"400000\",\n\"0\",\"set_zipped_3\",\"set\",\"1000000000\",\"2000000000\",\"3000000000\",\"4000000000\",\"5000000000\",\"6000000000\",\n\"0\",\"string\",\"string\",\"Hello World\"\n\"0\",\"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\"0\",\"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\nstart_server [list overrides [list \"dir\" $server_path]] {\n    test {Test RDB stream encoding} {\n        for {set j 0} {$j < 1000} {incr j} {\n            if {rand() < 0.9} {\n                r xadd stream * foo $j\n            } else {\n                r xadd stream * bar $j\n            }\n        }\n        r xgroup create stream mygroup 0\n        r xreadgroup GROUP mygroup Alice COUNT 1 STREAMS stream >\n        set digest [r debug digest]\n        r debug reload\n        set newdigest [r debug digest]\n        assert {$digest eq $newdigest}\n        r del stream\n    }\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 -1 < [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 {*CRC error*} \\\n                [exec tail -10 < [dict get $srv stdout]]]\n        } else {\n            fail \"Server started even if RDB was corrupted!\"\n        }\n    }\n}\n\nstart_server {} {\n    test {Test FLUSHALL aborts bgsave} {\n        r config set rdb-key-save-delay 1000\n        r debug populate 1000\n        r bgsave\n        assert_equal [s rdb_bgsave_in_progress] 1\n        r flushall\n        after 200\n        assert_equal [s rdb_bgsave_in_progress] 0\n        # make sure the server is still writable\n        r set x xx\n    }\n}\n\ntest {client freed during loading} {\n    start_server [list overrides [list key-load-delay 10 rdbcompression no]] {\n        # create a big rdb that will take long to load. it is important\n        # for keys to be big since the server processes events only once in 2mb.\n        # 100mb of rdb, 100k keys will load in more than 1 second\n        r debug populate 100000 key 1000\n\n        catch {\n            r debug restart\n        }\n\n        set stdout [srv 0 stdout]\n        while 1 {\n            # check that the new server actually started and is ready for connections\n            if {[exec grep -i \"Server initialized\" | wc -l < $stdout] > 1} {\n                break\n            }\n            after 10\n        }\n        # make sure it's still loading\n        assert_equal [s loading] 1\n\n        # connect and disconnect 10 clients\n        set clients {}\n        for {set j 0} {$j < 10} {incr j} {\n            lappend clients [redis_deferring_client]\n        }\n        foreach rd $clients {\n            $rd debug log bla\n        }\n        foreach rd $clients {\n            $rd read\n        }\n        foreach rd $clients {\n            $rd close\n        }\n\n        # make sure the server freed the clients\n        wait_for_condition 100 100 {\n            [s connected_clients] < 3\n        } else {\n            fail \"clients didn't disconnect\"\n        }\n\n        # make sure it's still loading\n        assert_equal [s loading] 1\n\n        # no need to keep waiting for loading to complete\n        exec kill [srv 0 pid]\n    }\n}"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/redis-cli.tcl",
    "content": "source tests/support/cli.tcl\n\nstart_server {tags {\"cli\"}} {\n    proc open_cli {} {\n        set ::env(TERM) dumb\n        set cmdline [rediscli [srv port] \"-n 9\"]\n        set fd [open \"|$cmdline\" \"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 [rediscli [srv port] [list -n 9 {*}$args]]\n        foreach {key value} $args {\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": "Chapter07/redis-6.0.3/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            wait_for_condition 50 100 {\n                [s -1 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n        }\n\n        test {If min-slaves-to-write is honored, write is accepted} {\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 10\n            r set foo 12345\n            wait_for_condition 50 100 {\n                [r -1 get foo] eq {12345}\n            } else {\n                fail \"Write did not reached replica\"\n            }\n        }\n\n        test {No write if min-slaves-to-write is < attached slaves} {\n            r config set min-slaves-to-write 2\n            r config set min-slaves-max-lag 10\n            catch {r set foo 12345} err\n            set err\n        } {NOREPLICAS*}\n\n        test {If min-slaves-to-write is honored, write is accepted (again)} {\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 10\n            r set foo 12345\n            wait_for_condition 50 100 {\n                [r -1 get foo] eq {12345}\n            } else {\n                fail \"Write did not reached replica\"\n            }\n        }\n\n        test {No write if min-slaves-max-lag is > of the slave lag} {\n            r -1 deferred 1\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 2\n            r -1 debug sleep 6\n            assert {[r set foo 12345] eq {OK}}\n            after 4000\n            catch {r set foo 12345} err\n            assert {[r -1 read] eq {OK}}\n            r -1 deferred 0\n            set err\n        } {NOREPLICAS*}\n\n        test {min-slaves-to-write is ignored by slaves} {\n            r config set min-slaves-to-write 1\n            r config set min-slaves-max-lag 10\n            r -1 config set min-slaves-to-write 1\n            r -1 config set min-slaves-max-lag 10\n            r set foo aaabbb\n            wait_for_condition 50 100 {\n                [r -1 get foo] eq {aaabbb}\n            } else {\n                fail \"Write did not reached replica\"\n            }\n        }\n\n        # Fix parameters for the next test to work\n        r config set min-slaves-to-write 0\n        r -1 config set min-slaves-to-write 0\n        r flushall\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 - Replica 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": "Chapter07/redis-6.0.3/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 - Replica 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        test {Slave is able to evict keys created in writable slaves} {\n            r -1 select 5\n            assert {[r -1 dbsize] == 0}\n            r -1 config set slave-read-only no\n            r -1 set key1 1 ex 5\n            r -1 set key2 2 ex 5\n            r -1 set key3 3 ex 5\n            assert {[r -1 dbsize] == 3}\n            after 6000\n            r -1 dbsize\n        } {0}\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 - Replica inconsistency\"\n                puts \"Run diff -u against /tmp/repldump*.txt for more info\"\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        test {SLAVE can reload \"lua\" AUX RDB fields of duplicated scripts} {\n            # Force a Slave full resynchronization\n            r debug change-repl-id\n            r -1 client kill type master\n\n            # Check that after a full resync the slave can still load\n            # correctly the RDB file: such file will contain \"lua\" AUX\n            # sections with scripts already in the memory of the master.\n\n            wait_for_condition 500 100 {\n                [s -1 master_link_status] eq {up}\n            } else {\n                fail \"Replication not started.\"\n            }\n\n            wait_for_condition 50 100 {\n                [r debug digest] eq [r -1 debug digest]\n            } else {\n                fail \"DEBUG DIGEST mismatch after full SYNC with many scripts\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/replication-4.tcl",
    "content": "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        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 - Replica 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        test {Replication of SPOP command -- alsoPropagate() API} {\n            $master del myset\n            set size [expr 1+[randomInt 100]]\n            set content {}\n            for {set j 0} {$j < $size} {incr j} {\n                lappend content [randomValue]\n            }\n            $master sadd myset {*}$content\n\n            set count [randomInt 100]\n            set result [$master spop myset $count]\n\n            wait_for_condition 50 100 {\n                [$master debug digest] eq [$slave debug digest]\n            } else {\n                fail \"SPOP replication inconsistency\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/replication-psync.tcl",
    "content": "# 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 specify backlog size, ttl, delay before reconnection, test duration\n# in seconds, and an additional condition to verify at the end.\n#\n# If reconnect is > 0, the test actually try to break the connection and\n# reconnect with the master, otherwise just the initial synchronization is\n# checked for consistency.\nproc test_psync {descr duration backlog_size backlog_ttl delay cond mdl sdl reconnect} {\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            $master config set repl-diskless-sync $mdl\n            $master config set repl-diskless-sync-delay 1\n            $slave config set repl-diskless-load $sdl\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 {Slave should be able to synchronize with the master} {\n                $slave slaveof $master_host $master_port\n                wait_for_condition 50 100 {\n                    [lindex [r role] 0] eq {slave} &&\n                    [lindex [r role] 3] eq {connected}\n                } else {\n                    fail \"Replication not started.\"\n                }\n            }\n\n            # Check that the background clients are actually writing.\n            test {Detect write load to master} {\n                wait_for_condition 50 1000 {\n                    [$master dbsize] > 100\n                } else {\n                    fail \"Can't detect write load from background clients.\"\n                }\n            }\n\n            test \"Test replication partial resync: $descr (diskless: $mdl, $sdl, reconnect: $reconnect)\" {\n                # Now while the clients are writing data, break the maste-slave\n                # link multiple times.\n                if ($reconnect) {\n                    for {set j 0} {$j < $duration*10} {incr j} {\n                        after 100\n                        # catch {puts \"MASTER [$master dbsize] keys, REPLICA [$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                }\n                stop_bg_complex_data $load_handle0\n                stop_bg_complex_data $load_handle1\n                stop_bg_complex_data $load_handle2\n\n                # Wait for the slave to reach the \"online\"\n                # state from the POV of the master.\n                set retry 5000\n                while {$retry} {\n                    set info [$master info]\n                    if {[string match {*slave0:*state=online*} $info]} {\n                        break\n                    } else {\n                        incr retry -1\n                        after 100\n                    }\n                }\n                if {$retry == 0} {\n                    error \"assertion:Slave not correctly synchronized\"\n                }\n\n                # Wait that slave acknowledge it is online so\n                # we are sure that DBSIZE and DEBUG DIGEST will not\n                # fail because of timing issues. (-LOADING error)\n                wait_for_condition 5000 100 {\n                    [lindex [$slave role] 3] eq {connected}\n                } else {\n                    fail \"Slave still not connected after some time\"\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                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 - Replica 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\nforeach mdl {no yes} {\n    foreach sdl {disabled swapdb} {\n        test_psync {no reconnection, just sync} 6 1000000 3600 0 {\n        } $mdl $sdl 0\n\n        test_psync {ok psync} 6 100000000 3600 0 {\n        assert {[s -1 sync_partial_ok] > 0}\n        } $mdl $sdl 1\n\n        test_psync {no backlog} 6 100 3600 0.5 {\n        assert {[s -1 sync_partial_err] > 0}\n        } $mdl $sdl 1\n\n        test_psync {ok after delay} 3 100000000 3600 3 {\n        assert {[s -1 sync_partial_ok] > 0}\n        } $mdl $sdl 1\n\n        test_psync {backlog expired} 3 100000000 1 3 {\n        assert {[s -1 sync_partial_err] > 0}\n        } $mdl $sdl 1\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/integration/replication.tcl",
    "content": "proc log_file_matches {log pattern} {\n    set fp [open $log r]\n    set content [read $fp]\n    close $fp\n    string match $pattern $content\n}\n\nstart_server {tags {\"repl\"}} {\n    set slave [srv 0 client]\n    set slave_host [srv 0 host]\n    set slave_port [srv 0 port]\n    set slave_log [srv 0 stdout]\n    start_server {} {\n        set master [srv 0 client]\n        set master_host [srv 0 host]\n        set master_port [srv 0 port]\n\n        # Configure the master in order to hang waiting for the BGSAVE\n        # operation, so that the slave remains in the handshake state.\n        $master config set repl-diskless-sync yes\n        $master config set repl-diskless-sync-delay 1000\n\n        # Use a short replication timeout on the slave, so that if there\n        # are no bugs the timeout is triggered in a reasonable amount\n        # of time.\n        $slave config set repl-timeout 5\n\n        # Start the replication process...\n        $slave slaveof $master_host $master_port\n\n        test {Slave enters handshake} {\n            wait_for_condition 50 1000 {\n                [string match *handshake* [$slave role]]\n            } else {\n                fail \"Replica does not enter handshake state\"\n            }\n        }\n\n        # But make the master unable to send\n        # the periodic newlines to refresh the connection. The slave\n        # should detect the timeout.\n        $master debug sleep 10\n\n        test {Slave is able to detect timeout during handshake} {\n            wait_for_condition 50 1000 {\n                [log_file_matches $slave_log \"*Timeout connecting to the MASTER*\"]\n            } else {\n                fail \"Replica is not able to detect timeout\"\n            }\n        }\n    }\n}\n\nstart_server {tags {\"repl\"}} {\n    set A [srv 0 client]\n    set A_host [srv 0 host]\n    set A_port [srv 0 port]\n    start_server {} {\n        set B [srv 0 client]\n        set B_host [srv 0 host]\n        set B_port [srv 0 port]\n\n        test {Set instance A as slave of B} {\n            $A slaveof $B_host $B_port\n            wait_for_condition 50 100 {\n                [lindex [$A role] 0] eq {slave} &&\n                [string match {*master_link_status:up*} [$A info replication]]\n            } else {\n                fail \"Can't turn the instance into a replica\"\n            }\n        }\n\n        test {INCRBYFLOAT replication, should not remove expire} {\n            r set test 1 EX 100\n            r incrbyfloat test 0.1\n            after 1000\n            assert_equal [$A debug digest] [$B debug digest]\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                [$A debug digest] eq [$B debug digest]\n            } else {\n                fail \"Master and replica have different digest: [$A debug digest] VS [$B 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 [$A debug digest] [$B debug digest]\n        }\n\n        test {BLPOP followed by role change, issue #2473} {\n            set rd [redis_deferring_client]\n            $rd blpop foo 0 ; # Block while B is a master\n\n            # Turn B into master of A\n            $A slaveof no one\n            $B slaveof $A_host $A_port\n            wait_for_condition 50 100 {\n                [lindex [$B role] 0] eq {slave} &&\n                [string match {*master_link_status:up*} [$B info replication]]\n            } else {\n                fail \"Can't turn the instance into a replica\"\n            }\n\n            # Push elements into the \"foo\" list of the new replica.\n            # If the client is still attached to the instance, we'll get\n            # a desync between the two instances.\n            $A rpush foo a b c\n            after 100\n\n            wait_for_condition 50 100 {\n                [$A debug digest] eq [$B debug digest] &&\n                [$A lrange foo 0 -1] eq {a b c} &&\n                [$B lrange foo 0 -1] eq {a b c}\n            } else {\n                fail \"Master and replica have different digest: [$A debug digest] VS [$B debug digest]\"\n            }\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 \"replica\"} {\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 replica\"\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        test {ROLE in master reports master with a slave} {\n            set res [r -1 role]\n            lassign $res role offset slaves\n            assert {$role eq {master}}\n            assert {$offset > 0}\n            assert {[llength $slaves] == 1}\n            lassign [lindex $slaves 0] master_host master_port slave_offset\n            assert {$slave_offset <= $offset}\n        }\n\n        test {ROLE in slave reports slave in connected state} {\n            set res [r role]\n            lassign $res role master_host master_port slave_state slave_offset\n            assert {$role eq {slave}}\n            assert {$slave_state eq {connected}}\n        }\n    }\n}\n\nforeach mdl {no yes} {\n    foreach sdl {disabled swapdb} {\n        start_server {tags {\"repl\"}} {\n            set master [srv 0 client]\n            $master config set repl-diskless-sync $mdl\n            $master config set repl-diskless-sync-delay 1\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n            set slaves {}\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 replicas at the same time (issue #141), master diskless=$mdl, replica diskless=$sdl\" {\n                            # start load handles only inside the test, so that the test can be skipped\n                            set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000000]\n                            set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000000]\n                            set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000000]\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                            after 5000 ;# wait for some data to accumulate so that we have RDB part for the fork\n\n                            # Send SLAVEOF commands to slaves\n                            [lindex $slaves 0] config set repl-diskless-load $sdl\n                            [lindex $slaves 1] config set repl-diskless-load $sdl\n                            [lindex $slaves 2] config set repl-diskless-load $sdl\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\"\n                            # state from the POV of the master.\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                            # Wait that slaves acknowledge they are online so\n                            # we are sure that DBSIZE and DEBUG DIGEST will not\n                            # fail because of timing issues.\n                            wait_for_condition 500 100 {\n                                [lindex [[lindex $slaves 0] role] 3] eq {connected} &&\n                                [lindex [[lindex $slaves 1] role] 3] eq {connected} &&\n                                [lindex [[lindex $slaves 2] role] 3] eq {connected}\n                            } else {\n                                fail \"Slaves still not connected after some time\"\n                            }\n\n                            # Stop the write load\n                            stop_bg_complex_data $load_handle0\n                            stop_bg_complex_data $load_handle1\n                            stop_bg_complex_data $load_handle2\n                            stop_write_load $load_handle3\n                            stop_write_load $load_handle4\n\n                            # Make sure that slaves and master have same\n                            # 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 master and replica 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    }\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    start_server {} {\n        test \"Master stream is correctly processed while the replica has a script in -BUSY state\" {\n            set load_handle0 [start_write_load $master_host $master_port 3]\n            set slave [srv 0 client]\n            $slave config set lua-time-limit 500\n            $slave slaveof $master_host $master_port\n\n            # Wait for the slave to be online\n            wait_for_condition 500 100 {\n                [lindex [$slave role] 3] eq {connected}\n            } else {\n                fail \"Replica still not connected after some time\"\n            }\n\n            # Wait some time to make sure the master is sending data\n            # to the slave.\n            after 5000\n\n            # Stop the ability of the slave to process data by sendig\n            # a script that will put it in BUSY state.\n            $slave eval {for i=1,3000000000 do end} 0\n\n            # Wait some time again so that more master stream will\n            # be processed.\n            after 2000\n\n            # Stop the write load\n            stop_write_load $load_handle0\n\n            # number of keys\n            wait_for_condition 500 100 {\n                [$master debug digest] eq [$slave debug digest]\n            } else {\n                fail \"Different datasets between replica and master\"\n            }\n        }\n    }\n}\n\ntest {slave fails full sync and diskless load swapdb recovers it} {\n    start_server {tags {\"repl\"}} {\n        set slave [srv 0 client]\n        set slave_host [srv 0 host]\n        set slave_port [srv 0 port]\n        set slave_log [srv 0 stdout]\n        start_server {} {\n            set master [srv 0 client]\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n\n            # Put different data sets on the master and slave\n            # we need to put large keys on the master since the slave replies to info only once in 2mb\n            $slave debug populate 2000 slave 10\n            $master debug populate 200 master 100000\n            $master config set rdbcompression no\n\n            # Set master and slave to use diskless replication\n            $master config set repl-diskless-sync yes\n            $master config set repl-diskless-sync-delay 0\n            $slave config set repl-diskless-load swapdb\n\n            # Set master with a slow rdb generation, so that we can easily disconnect it mid sync\n            # 10ms per key, with 200 keys is 2 seconds\n            $master config set rdb-key-save-delay 10000\n\n            # Start the replication process...\n            $slave slaveof $master_host $master_port\n\n            # wait for the slave to start reading the rdb\n            wait_for_condition 50 100 {\n                [s -1 loading] eq 1\n            } else {\n                fail \"Replica didn't get into loading mode\"\n            }\n\n            # make sure that next sync will not start immediately so that we can catch the slave in betweeen syncs\n            $master config set repl-diskless-sync-delay 5\n            # for faster server shutdown, make rdb saving fast again (the fork is already uses the slow one)\n            $master config set rdb-key-save-delay 0\n\n            # waiting slave to do flushdb (key count drop)\n            wait_for_condition 50 100 {\n                2000 != [scan [regexp -inline {keys\\=([\\d]*)} [$slave info keyspace]] keys=%d]\n            } else {\n                fail \"Replica didn't flush\"\n            }\n\n            # make sure we're still loading\n            assert_equal [s -1 loading] 1\n\n            # kill the slave connection on the master\n            set killed [$master client kill type slave]\n\n            # wait for loading to stop (fail)\n            wait_for_condition 50 100 {\n                [s -1 loading] eq 0\n            } else {\n                fail \"Replica didn't disconnect\"\n            }\n\n            # make sure the original keys were restored\n            assert_equal [$slave dbsize] 2000\n        }\n    }\n}\n\ntest {diskless loading short read} {\n    start_server {tags {\"repl\"}} {\n        set replica [srv 0 client]\n        set replica_host [srv 0 host]\n        set replica_port [srv 0 port]\n        start_server {} {\n            set master [srv 0 client]\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n\n            # Set master and replica to use diskless replication\n            $master config set repl-diskless-sync yes\n            $master config set rdbcompression no\n            $replica config set repl-diskless-load swapdb\n            # Try to fill the master with all types of data types / encodings\n            for {set k 0} {$k < 3} {incr k} {\n                for {set i 0} {$i < 10} {incr i} {\n                    r set \"$k int_$i\" [expr {int(rand()*10000)}]\n                    r expire \"$k int_$i\" [expr {int(rand()*10000)}]\n                    r set \"$k string_$i\" [string repeat A [expr {int(rand()*1000000)}]]\n                    r hset \"$k hash_small\" [string repeat A [expr {int(rand()*10)}]]  0[string repeat A [expr {int(rand()*10)}]]\n                    r hset \"$k hash_large\" [string repeat A [expr {int(rand()*10000)}]] [string repeat A [expr {int(rand()*1000000)}]]\n                    r sadd \"$k set_small\" [string repeat A [expr {int(rand()*10)}]]\n                    r sadd \"$k set_large\" [string repeat A [expr {int(rand()*1000000)}]]\n                    r zadd \"$k zset_small\" [expr {rand()}] [string repeat A [expr {int(rand()*10)}]]\n                    r zadd \"$k zset_large\" [expr {rand()}] [string repeat A [expr {int(rand()*1000000)}]]\n                    r lpush \"$k list_small\" [string repeat A [expr {int(rand()*10)}]]\n                    r lpush \"$k list_large\" [string repeat A [expr {int(rand()*1000000)}]]\n                    for {set j 0} {$j < 10} {incr j} {\n                        r xadd \"$k stream\" * foo \"asdf\" bar \"1234\"\n                    }\n                    r xgroup create \"$k stream\" \"mygroup_$i\" 0\n                    r xreadgroup GROUP \"mygroup_$i\" Alice COUNT 1 STREAMS \"$k stream\" >\n                }\n            }\n\n            # Start the replication process...\n            $master config set repl-diskless-sync-delay 0\n            $replica replicaof $master_host $master_port\n\n            # kill the replication at various points\n            set attempts 3\n            if {$::accurate} { set attempts 10 }\n            for {set i 0} {$i < $attempts} {incr i} {\n                # wait for the replica to start reading the rdb\n                # using the log file since the replica only responds to INFO once in 2mb\n                wait_for_log_message -1 \"*Loading DB in memory*\" 5 2000 1\n\n                # add some additional random sleep so that we kill the master on a different place each time\n                after [expr {int(rand()*100)}]\n\n                # kill the replica connection on the master\n                set killed [$master client kill type replica]\n\n                if {[catch {\n                    set res [wait_for_log_message -1 \"*Internal error in RDB*\" 5 100 10]\n                    if {$::verbose} {\n                        puts $res\n                    }\n                }]} {\n                    puts \"failed triggering short read\"\n                    # force the replica to try another full sync\n                    $master client kill type replica\n                    $master set asdf asdf\n                    # the side effect of resizing the backlog is that it is flushed (16k is the min size)\n                    $master config set repl-backlog-size [expr {16384 + $i}]\n                }\n                # wait for loading to stop (fail)\n                wait_for_condition 100 10 {\n                    [s -1 loading] eq 0\n                } else {\n                    fail \"Replica didn't disconnect\"\n                }\n            }\n            # enable fast shutdown\n            $master config set rdb-key-save-delay 0\n        }\n    }\n}\n\n# get current stime and utime metrics for a thread (since it's creation)\nproc get_cpu_metrics { statfile } {\n    if { [ catch {\n        set fid   [ open $statfile r ]\n        set data  [ read $fid 1024 ]\n        ::close $fid\n        set data  [ split $data ]\n\n        ;## number of jiffies it has been scheduled...\n        set utime [ lindex $data 13 ]\n        set stime [ lindex $data 14 ]\n    } err ] } {\n        error \"assertion:can't parse /proc: $err\"\n    }\n    set mstime [clock milliseconds]\n    return [ list $mstime $utime $stime ]\n}\n\n# compute %utime and %stime of a thread between two measurements\nproc compute_cpu_usage {start end} {\n    set clock_ticks [exec getconf CLK_TCK]\n    # convert ms time to jiffies and calc delta\n    set dtime [ expr { ([lindex $end 0] - [lindex $start 0]) * double($clock_ticks) / 1000 } ]\n    set utime [ expr { [lindex $end 1] - [lindex $start 1] } ]\n    set stime [ expr { [lindex $end 2] - [lindex $start 2] } ]\n    set pucpu  [ expr { ($utime / $dtime) * 100 } ]\n    set pscpu  [ expr { ($stime / $dtime) * 100 } ]\n    return [ list $pucpu $pscpu ]\n}\n\n\n# test diskless rdb pipe with multiple replicas, which may drop half way\nstart_server {tags {\"repl\"}} {\n    set master [srv 0 client]\n    $master config set repl-diskless-sync yes\n    $master config set repl-diskless-sync-delay 1\n    set master_host [srv 0 host]\n    set master_port [srv 0 port]\n    set master_pid [srv 0 pid]\n    # put enough data in the db that the rdb file will be bigger than the socket buffers\n    # and since we'll have key-load-delay of 100, 20000 keys will take at least 2 seconds\n    # we also need the replica to process requests during transfer (which it does only once in 2mb)\n    $master debug populate 20000 test 10000\n    $master config set rdbcompression no\n    # If running on Linux, we also measure utime/stime to detect possible I/O handling issues\n    set os [catch {exec unamee}]\n    set measure_time [expr {$os == \"Linux\"} ? 1 : 0]\n    foreach all_drop {no slow fast all} {\n        test \"diskless $all_drop replicas drop during rdb pipe\" {\n            set replicas {}\n            set replicas_alive {}\n            # start one replica that will read the rdb fast, and one that will be slow\n            start_server {} {\n                lappend replicas [srv 0 client]\n                lappend replicas_alive [srv 0 client]\n                start_server {} {\n                    lappend replicas [srv 0 client]\n                    lappend replicas_alive [srv 0 client]\n\n                    # start replication\n                    # it's enough for just one replica to be slow, and have it's write handler enabled\n                    # so that the whole rdb generation process is bound to that\n                    [lindex $replicas 0] config set repl-diskless-load swapdb\n                    [lindex $replicas 0] config set key-load-delay 100\n                    [lindex $replicas 0] replicaof $master_host $master_port\n                    [lindex $replicas 1] replicaof $master_host $master_port\n\n                    # wait for the replicas to start reading the rdb\n                    # using the log file since the replica only responds to INFO once in 2mb\n                    wait_for_log_message -1 \"*Loading DB in memory*\" 8 800 10\n\n                    if {$measure_time} {\n                        set master_statfile \"/proc/$master_pid/stat\"\n                        set master_start_metrics [get_cpu_metrics $master_statfile]\n                        set start_time [clock seconds]\n                    }\n\n                    # wait a while so that the pipe socket writer will be\n                    # blocked on write (since replica 0 is slow to read from the socket)\n                    after 500\n\n                    # add some command to be present in the command stream after the rdb.\n                    $master incr $all_drop\n\n                    # disconnect replicas depending on the current test\n                    if {$all_drop == \"all\" || $all_drop == \"fast\"} {\n                        exec kill [srv 0 pid]\n                        set replicas_alive [lreplace $replicas_alive 1 1]\n                    }\n                    if {$all_drop == \"all\" || $all_drop == \"slow\"} {\n                        exec kill [srv -1 pid]\n                        set replicas_alive [lreplace $replicas_alive 0 0]\n                    }\n\n                    # wait for rdb child to exit\n                    wait_for_condition 500 100 {\n                        [s -2 rdb_bgsave_in_progress] == 0\n                    } else {\n                        fail \"rdb child didn't terminate\"\n                    }\n\n                    # make sure we got what we were aiming for, by looking for the message in the log file\n                    if {$all_drop == \"all\"} {\n                        wait_for_log_message -2 \"*Diskless rdb transfer, last replica dropped, killing fork child*\" 12 1 1\n                    }\n                    if {$all_drop == \"no\"} {\n                        wait_for_log_message -2 \"*Diskless rdb transfer, done reading from pipe, 2 replicas still up*\" 12 1 1\n                    }\n                    if {$all_drop == \"slow\" || $all_drop == \"fast\"} {\n                        wait_for_log_message -2 \"*Diskless rdb transfer, done reading from pipe, 1 replicas still up*\" 12 1 1\n                    }\n\n                    # make sure we don't have a busy loop going thought epoll_wait\n                    if {$measure_time} {\n                        set master_end_metrics [get_cpu_metrics $master_statfile]\n                        set time_elapsed [expr {[clock seconds]-$start_time}]\n                        set master_cpu [compute_cpu_usage $master_start_metrics $master_end_metrics]\n                        set master_utime [lindex $master_cpu 0]\n                        set master_stime [lindex $master_cpu 1]\n                        if {$::verbose} {\n                            puts \"elapsed: $time_elapsed\"\n                            puts \"master utime: $master_utime\"\n                            puts \"master stime: $master_stime\"\n                        }\n                        if {$all_drop == \"all\" || $all_drop == \"slow\"} {\n                            assert {$master_utime < 70}\n                            assert {$master_stime < 70}\n                        }\n                        if {$all_drop == \"none\" || $all_drop == \"fast\"} {\n                            assert {$master_utime < 15}\n                            assert {$master_stime < 15}\n                        }\n                    }\n\n                    # verify the data integrity\n                    foreach replica $replicas_alive {\n                        # Wait that replicas acknowledge they are online so\n                        # we are sure that DBSIZE and DEBUG DIGEST will not\n                        # fail because of timing issues.\n                        wait_for_condition 50 100 {\n                            [lindex [$replica role] 3] eq {connected}\n                        } else {\n                            fail \"replicas still not connected after some time\"\n                        }\n\n                        # Make sure that replicas and master have same\n                        # number of keys\n                        wait_for_condition 50 100 {\n                            [$master dbsize] == [$replica dbsize]\n                        } else {\n                            fail \"Different number of keys between master and replicas after too long time.\"\n                        }\n\n                        # Check digests\n                        set digest [$master debug digest]\n                        set digest0 [$replica debug digest]\n                        assert {$digest ne 0000000000000000000000000000000000000000}\n                        assert {$digest eq $digest0}\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/Makefile",
    "content": "\n# find the OS\nuname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\n# Compile flags for linux / osx\nifeq ($(uname_S),Linux)\n\tSHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -shared\nelse\n\tSHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2\n\tSHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup\nendif\n\nTEST_MODULES = \\\n    commandfilter.so \\\n    testrdb.so \\\n    fork.so \\\n    infotest.so \\\n    propagate.so \\\n    misc.so \\\n    hooks.so \\\n    blockonkeys.so \\\n    scan.so \\\n    datatype.so \\\n    auth.so\n\n.PHONY: all\n\nall: $(TEST_MODULES)\n\n%.xo: %.c ../../src/redismodule.h\n\t$(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@\n\n%.so: %.xo\n\t$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc\n\n.PHONY: clean\n\nclean:\n\trm -f $(TEST_MODULES) $(TEST_MODULES:.so=.xo)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/auth.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n// A simple global user\nstatic RedisModuleUser *global = NULL;\nstatic long long client_change_delta = 0;\n\nvoid UserChangedCallback(uint64_t client_id, void *privdata) {\n    REDISMODULE_NOT_USED(privdata);\n    REDISMODULE_NOT_USED(client_id);\n    client_change_delta++;\n}\n\nint Auth_CreateModuleUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (global) {\n        RedisModule_FreeModuleUser(global);\n    }\n\n    global = RedisModule_CreateModuleUser(\"global\");\n    RedisModule_SetModuleUserACL(global, \"allcommands\");\n    RedisModule_SetModuleUserACL(global, \"allkeys\");\n    RedisModule_SetModuleUserACL(global, \"on\");\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\nint Auth_AuthModuleUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    uint64_t client_id;\n    RedisModule_AuthenticateClientWithUser(ctx, global, UserChangedCallback, NULL, &client_id);\n\n    return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id);\n}\n\nint Auth_AuthRealUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\n    size_t length;\n    uint64_t client_id;\n\n    RedisModuleString *user_string = argv[1];\n    const char *name = RedisModule_StringPtrLen(user_string, &length);\n\n    if (RedisModule_AuthenticateClientWithACLUser(ctx, name, length, \n            UserChangedCallback, NULL, &client_id) == REDISMODULE_ERR) {\n        return RedisModule_ReplyWithError(ctx, \"Invalid user\");   \n    }\n\n    return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id);\n}\n\nint Auth_ChangeCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    long long result = client_change_delta;\n    client_change_delta = 0;\n    return RedisModule_ReplyWithLongLong(ctx, result);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"testacl\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.authrealuser\",\n        Auth_AuthRealUser,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.createmoduleuser\",\n        Auth_CreateModuleUser,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.authmoduleuser\",\n        Auth_AuthModuleUser,\"no-auth\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"auth.changecount\",\n        Auth_ChangeCount,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/blockonkeys.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\n#define LIST_SIZE 1024\n\ntypedef struct {\n    long long list[LIST_SIZE];\n    long long length;\n} fsl_t; /* Fixed-size list */\n\nstatic RedisModuleType *fsltype = NULL;\n\nfsl_t *fsl_type_create() {\n    fsl_t *o;\n    o = RedisModule_Alloc(sizeof(*o));\n    o->length = 0;\n    return o;\n}\n\nvoid fsl_type_free(fsl_t *o) {\n    RedisModule_Free(o);\n}\n\n/* ========================== \"fsltype\" type methods ======================= */\n\nvoid *fsl_rdb_load(RedisModuleIO *rdb, int encver) {\n    if (encver != 0) {\n        return NULL;\n    }\n    fsl_t *fsl = fsl_type_create();\n    fsl->length = RedisModule_LoadUnsigned(rdb);\n    for (long long i = 0; i < fsl->length; i++)\n        fsl->list[i] = RedisModule_LoadSigned(rdb);\n    return fsl;\n}\n\nvoid fsl_rdb_save(RedisModuleIO *rdb, void *value) {\n    fsl_t *fsl = value;\n    RedisModule_SaveUnsigned(rdb,fsl->length);\n    for (long long i = 0; i < fsl->length; i++)\n        RedisModule_SaveSigned(rdb, fsl->list[i]);\n}\n\nvoid fsl_aofrw(RedisModuleIO *aof, RedisModuleString *key, void *value) {\n    fsl_t *fsl = value;\n    for (long long i = 0; i < fsl->length; i++)\n        RedisModule_EmitAOF(aof, \"FSL.PUSH\",\"sl\", key, fsl->list[i]);\n}\n\nvoid fsl_free(void *value) {\n    fsl_type_free(value);\n}\n\n/* ========================== helper methods ======================= */\n\nint get_fsl(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode, int create, fsl_t **fsl, int reply_on_failure) {\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);\n\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != fsltype) {\n        RedisModule_CloseKey(key);\n        if (reply_on_failure)\n            RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);\n        return 0;\n    }\n\n    /* Create an empty value object if the key is currently empty. */\n    if (type == REDISMODULE_KEYTYPE_EMPTY) {\n        if (!create) {\n            /* Key is empty but we cannot create */\n            RedisModule_CloseKey(key);\n            *fsl = NULL;\n            return 1;\n        }\n        *fsl = fsl_type_create();\n        RedisModule_ModuleTypeSetValue(key, fsltype, *fsl);\n    } else {\n        *fsl = RedisModule_ModuleTypeGetValue(key);\n    }\n\n    RedisModule_CloseKey(key);\n    return 1;\n}\n\n/* ========================== commands ======================= */\n\n/* FSL.PUSH <key> <int> - Push an integer to the fixed-size list (to the right).\n * It must be greater than the element in the head of the list. */\nint fsl_push(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3)\n        return RedisModule_WrongArity(ctx);\n\n    long long ele;\n    if (RedisModule_StringToLongLong(argv[2],&ele) != REDISMODULE_OK)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid integer\");\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 1, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (fsl->length == LIST_SIZE)\n        return RedisModule_ReplyWithError(ctx,\"ERR list is full\");\n\n    if (fsl->length != 0 && fsl->list[fsl->length-1] >= ele)\n        return RedisModule_ReplyWithError(ctx,\"ERR new element has to be greater than the head element\");\n\n    fsl->list[fsl->length++] = ele;\n    RedisModule_SignalKeyAsReady(ctx, argv[1]);\n\n    return RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n}\n\nint bpop_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0) || !fsl)\n        return REDISMODULE_ERR;\n\n    RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    return REDISMODULE_OK;\n}\n\nint bpop_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\n/* FSL.BPOP <key> <timeout> - Block clients until list has two or more elements.\n * When that happens, unblock client and pop the last two elements (from the right). */\nint fsl_bpop(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3)\n        return RedisModule_WrongArity(ctx);\n\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK || timeout < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid timeout\");\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (!fsl) {\n        RedisModule_BlockClientOnKeys(ctx, bpop_reply_callback, bpop_timeout_callback,\n                                      NULL, timeout, &argv[1], 1, NULL);\n    } else {\n        RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    }\n\n    return REDISMODULE_OK;\n}\n\nint bpopgt_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n    long long *pgt = RedisModule_GetBlockedClientPrivateData(ctx);\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0) || !fsl)\n        return REDISMODULE_ERR;\n\n    if (fsl->list[fsl->length-1] <= *pgt)\n        return REDISMODULE_ERR;\n\n    RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    return REDISMODULE_OK;\n}\n\nint bpopgt_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\nvoid bpopgt_free_privdata(RedisModuleCtx *ctx, void *privdata) {\n    REDISMODULE_NOT_USED(ctx);\n    RedisModule_Free(privdata);\n}\n\n/* FSL.BPOPGT <key> <gt> <timeout> - Block clients until list has an element greater than <gt>.\n * When that happens, unblock client and pop the last element (from the right). */\nint fsl_bpopgt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4)\n        return RedisModule_WrongArity(ctx);\n\n    long long gt;\n    if (RedisModule_StringToLongLong(argv[2],&gt) != REDISMODULE_OK)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid integer\");\n\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid timeout\");\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (!fsl || fsl->list[fsl->length-1] <= gt) {\n        /* We use malloc so the tests in blockedonkeys.tcl can check for memory leaks */\n        long long *pgt = RedisModule_Alloc(sizeof(long long));\n        *pgt = gt;\n        RedisModule_BlockClientOnKeys(ctx, bpopgt_reply_callback, bpopgt_timeout_callback,\n                                      bpopgt_free_privdata, timeout, &argv[1], 1, pgt);\n    } else {\n        RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);\n    }\n\n    return REDISMODULE_OK;\n}\n\nint bpoppush_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *src_keyname = RedisModule_GetBlockedClientReadyKey(ctx);\n    RedisModuleString *dst_keyname = RedisModule_GetBlockedClientPrivateData(ctx);\n\n    fsl_t *src;\n    if (!get_fsl(ctx, src_keyname, REDISMODULE_READ, 0, &src, 0) || !src)\n        return REDISMODULE_ERR;\n\n    fsl_t *dst;\n    if (!get_fsl(ctx, dst_keyname, REDISMODULE_WRITE, 1, &dst, 0) || !dst)\n        return REDISMODULE_ERR;\n\n    long long ele = src->list[--src->length];\n    dst->list[dst->length++] = ele;\n    RedisModule_SignalKeyAsReady(ctx, dst_keyname);\n    return RedisModule_ReplyWithLongLong(ctx, ele);\n}\n\nint bpoppush_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    return RedisModule_ReplyWithSimpleString(ctx, \"Request timedout\");\n}\n\nvoid bpoppush_free_privdata(RedisModuleCtx *ctx, void *privdata) {\n    RedisModule_FreeString(ctx, privdata);\n}\n\n/* FSL.BPOPPUSH <src> <dst> <timeout> - Block clients until <src> has an element.\n * When that happens, unblock client, pop the last element from <src> and push it to <dst>\n * (from the right). */\nint fsl_bpoppush(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4)\n        return RedisModule_WrongArity(ctx);\n\n    long long timeout;\n    if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0)\n        return RedisModule_ReplyWithError(ctx,\"ERR invalid timeout\");\n\n    fsl_t *src;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &src, 1))\n        return REDISMODULE_OK;\n\n    if (!src) {\n        /* Retain string for reply callback */\n        RedisModule_RetainString(ctx, argv[2]);\n        /* Key is empty, we must block */\n        RedisModule_BlockClientOnKeys(ctx, bpoppush_reply_callback, bpoppush_timeout_callback,\n                                      bpoppush_free_privdata, timeout, &argv[1], 1, argv[2]);\n    } else {\n        fsl_t *dst;\n        if (!get_fsl(ctx, argv[2], REDISMODULE_WRITE, 1, &dst, 1))\n            return REDISMODULE_OK;\n        long long ele = src->list[--src->length];\n        dst->list[dst->length++] = ele;\n        RedisModule_SignalKeyAsReady(ctx, argv[2]);\n        RedisModule_ReplyWithLongLong(ctx, ele);\n    }\n\n    return REDISMODULE_OK;\n}\n\n/* FSL.GETALL <key> - Reply with an array containing all elements. */\nint fsl_getall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2)\n        return RedisModule_WrongArity(ctx);\n\n    fsl_t *fsl;\n    if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))\n        return REDISMODULE_OK;\n\n    if (!fsl)\n        return RedisModule_ReplyWithArray(ctx, 0);\n\n    RedisModule_ReplyWithArray(ctx, fsl->length);\n    for (int i = 0; i < fsl->length; i++)\n        RedisModule_ReplyWithLongLong(ctx, fsl->list[i]);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx, \"blockonkeys\", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModuleTypeMethods tm = {\n        .version = REDISMODULE_TYPE_METHOD_VERSION,\n        .rdb_load = fsl_rdb_load,\n        .rdb_save = fsl_rdb_save,\n        .aof_rewrite = fsl_aofrw,\n        .mem_usage = NULL,\n        .free = fsl_free,\n        .digest = NULL\n    };\n\n    fsltype = RedisModule_CreateDataType(ctx, \"fsltype_t\", 0, &tm);\n    if (fsltype == NULL)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.push\",fsl_push,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.bpop\",fsl_bpop,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.bpopgt\",fsl_bpopgt,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.bpoppush\",fsl_bpoppush,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fsl.getall\",fsl_getall,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/commandfilter.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n#include <string.h>\n\nstatic RedisModuleString *log_key_name;\n\nstatic const char log_command_name[] = \"commandfilter.log\";\nstatic const char ping_command_name[] = \"commandfilter.ping\";\nstatic const char unregister_command_name[] = \"commandfilter.unregister\";\nstatic int in_log_command = 0;\n\nstatic RedisModuleCommandFilter *filter = NULL;\n\nint CommandFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    (void) argc;\n    (void) argv;\n\n    RedisModule_ReplyWithLongLong(ctx,\n            RedisModule_UnregisterCommandFilter(ctx, filter));\n\n    return REDISMODULE_OK;\n}\n\nint CommandFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    (void) argc;\n    (void) argv;\n\n    RedisModuleCallReply *reply = RedisModule_Call(ctx, \"ping\", \"c\", \"@log\");\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithSimpleString(ctx, \"Unknown command or invalid arguments\");\n    }\n\n    return REDISMODULE_OK;\n}\n\nint CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    RedisModuleString *s = RedisModule_CreateString(ctx, \"\", 0);\n\n    int i;\n    for (i = 1; i < argc; i++) {\n        size_t arglen;\n        const char *arg = RedisModule_StringPtrLen(argv[i], &arglen);\n\n        if (i > 1) RedisModule_StringAppendBuffer(ctx, s, \" \", 1);\n        RedisModule_StringAppendBuffer(ctx, s, arg, arglen);\n    }\n\n    RedisModuleKey *log = RedisModule_OpenKey(ctx, log_key_name, REDISMODULE_WRITE|REDISMODULE_READ);\n    RedisModule_ListPush(log, REDISMODULE_LIST_HEAD, s);\n    RedisModule_CloseKey(log);\n    RedisModule_FreeString(ctx, s);\n\n    in_log_command = 1;\n\n    size_t cmdlen;\n    const char *cmdname = RedisModule_StringPtrLen(argv[1], &cmdlen);\n    RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, \"v\", &argv[2], argc - 2);\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithSimpleString(ctx, \"Unknown command or invalid arguments\");\n    }\n\n    in_log_command = 0;\n\n    return REDISMODULE_OK;\n}\n\nvoid CommandFilter_CommandFilter(RedisModuleCommandFilterCtx *filter)\n{\n    if (in_log_command) return;  /* don't process our own RM_Call() from CommandFilter_LogCommand() */\n\n    /* Fun manipulations:\n     * - Remove @delme\n     * - Replace @replaceme\n     * - Append @insertbefore or @insertafter\n     * - Prefix with Log command if @log encounterd\n     */\n    int log = 0;\n    int pos = 0;\n    while (pos < RedisModule_CommandFilterArgsCount(filter)) {\n        const RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, pos);\n        size_t arg_len;\n        const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len);\n\n        if (arg_len == 6 && !memcmp(arg_str, \"@delme\", 6)) {\n            RedisModule_CommandFilterArgDelete(filter, pos);\n            continue;\n        } \n        if (arg_len == 10 && !memcmp(arg_str, \"@replaceme\", 10)) {\n            RedisModule_CommandFilterArgReplace(filter, pos,\n                    RedisModule_CreateString(NULL, \"--replaced--\", 12));\n        } else if (arg_len == 13 && !memcmp(arg_str, \"@insertbefore\", 13)) {\n            RedisModule_CommandFilterArgInsert(filter, pos,\n                    RedisModule_CreateString(NULL, \"--inserted-before--\", 19));\n            pos++;\n        } else if (arg_len == 12 && !memcmp(arg_str, \"@insertafter\", 12)) {\n            RedisModule_CommandFilterArgInsert(filter, pos + 1,\n                    RedisModule_CreateString(NULL, \"--inserted-after--\", 18));\n            pos++;\n        } else if (arg_len == 4 && !memcmp(arg_str, \"@log\", 4)) {\n            log = 1;\n        }\n        pos++;\n    }\n\n    if (log) RedisModule_CommandFilterArgInsert(filter, 0,\n            RedisModule_CreateString(NULL, log_command_name, sizeof(log_command_name)-1));\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (RedisModule_Init(ctx,\"commandfilter\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (argc != 2) {\n        RedisModule_Log(ctx, \"warning\", \"Log key name not specified\");\n        return REDISMODULE_ERR;\n    }\n\n    long long noself = 0;\n    log_key_name = RedisModule_CreateStringFromString(ctx, argv[0]);\n    RedisModule_StringToLongLong(argv[1], &noself);\n\n    if (RedisModule_CreateCommand(ctx,log_command_name,\n                CommandFilter_LogCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,ping_command_name,\n                CommandFilter_PingCommand,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,unregister_command_name,\n                CommandFilter_UnregisterCommand,\"write deny-oom\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter, \n                    noself ? REDISMODULE_CMDFILTER_NOSELF : 0))\n            == NULL) return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnUnload(RedisModuleCtx *ctx) {\n    RedisModule_FreeString(ctx, log_key_name);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/datatype.c",
    "content": "/* This module current tests a small subset but should be extended in the future\n * for general ModuleDataType coverage.\n */\n\n#include \"redismodule.h\"\n\nstatic RedisModuleType *datatype = NULL;\n\ntypedef struct {\n    long long intval;\n    RedisModuleString *strval;\n} DataType;\n\nstatic void *datatype_load(RedisModuleIO *io, int encver) {\n    (void) encver;\n\n    int intval = RedisModule_LoadSigned(io);\n    if (RedisModule_IsIOError(io)) return NULL;\n\n    RedisModuleString *strval = RedisModule_LoadString(io);\n    if (RedisModule_IsIOError(io)) return NULL;\n\n    DataType *dt = (DataType *) RedisModule_Alloc(sizeof(DataType));\n    dt->intval = intval;\n    dt->strval = strval;\n    return dt;\n}\n\nstatic void datatype_save(RedisModuleIO *io, void *value) {\n    DataType *dt = (DataType *) value;\n    RedisModule_SaveSigned(io, dt->intval);\n    RedisModule_SaveString(io, dt->strval);\n}\n\nstatic void datatype_free(void *value) {\n    if (value) {\n        DataType *dt = (DataType *) value;\n\n        if (dt->strval) RedisModule_FreeString(NULL, dt->strval);\n        RedisModule_Free(dt);\n    }\n}\n\nstatic int datatype_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 4) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    long long intval;\n\n    if (RedisModule_StringToLongLong(argv[2], &intval) != REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"Invalid integr value\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    DataType *dt = RedisModule_Calloc(sizeof(DataType), 1);\n    dt->intval = intval;\n    dt->strval = argv[3];\n    RedisModule_RetainString(ctx, dt->strval);\n\n    RedisModule_ModuleTypeSetValue(key, datatype, dt);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_restore(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    DataType *dt = RedisModule_LoadDataTypeFromString(argv[2], datatype);\n    if (!dt) {\n        RedisModule_ReplyWithError(ctx, \"Invalid data\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModule_ModuleTypeSetValue(key, datatype, dt);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);\n    DataType *dt = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_CloseKey(key);\n\n    RedisModule_ReplyWithArray(ctx, 2);\n    RedisModule_ReplyWithLongLong(ctx, dt->intval);\n    RedisModule_ReplyWithString(ctx, dt->strval);\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_dump(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);\n    DataType *dt = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_CloseKey(key);\n\n    RedisModuleString *reply = RedisModule_SaveDataTypeToString(ctx, dt, datatype);\n    if (!reply) {\n        RedisModule_ReplyWithError(ctx, \"Failed to save\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModule_ReplyWithString(ctx, reply);\n    RedisModule_FreeString(ctx, reply);\n    return REDISMODULE_OK;\n}\n\nstatic int datatype_swap(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    if (argc != 3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *a = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModuleKey *b = RedisModule_OpenKey(ctx, argv[2], REDISMODULE_WRITE);\n    void *val = RedisModule_ModuleTypeGetValue(a);\n\n    int error = (RedisModule_ModuleTypeReplaceValue(b, datatype, val, &val) == REDISMODULE_ERR ||\n                 RedisModule_ModuleTypeReplaceValue(a, datatype, val, NULL) == REDISMODULE_ERR);\n    if (!error)\n        RedisModule_ReplyWithSimpleString(ctx, \"OK\");\n    else\n        RedisModule_ReplyWithError(ctx, \"ERR failed\");\n\n    RedisModule_CloseKey(a);\n    RedisModule_CloseKey(b);\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"datatype\",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS);\n\n    RedisModuleTypeMethods datatype_methods = {\n        .version = REDISMODULE_TYPE_METHOD_VERSION,\n        .rdb_load = datatype_load,\n        .rdb_save = datatype_save,\n        .free = datatype_free,\n    };\n\n    datatype = RedisModule_CreateDataType(ctx, \"test___dt\", 1, &datatype_methods);\n    if (datatype == NULL)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.set\", datatype_set,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.get\", datatype_get,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.restore\", datatype_restore,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.dump\", datatype_dump,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"datatype.swap\", datatype_swap,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/fork.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n\n/* define macros for having usleep */\n#define _BSD_SOURCE\n#define _DEFAULT_SOURCE\n\n#include \"redismodule.h\"\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\n#define UNUSED(V) ((void) V)\n\nint child_pid = -1;\nint exitted_with_code = -1;\n\nvoid done_handler(int exitcode, int bysignal, void *user_data) {\n    child_pid = -1;\n    exitted_with_code = exitcode;\n    assert(user_data==(void*)0xdeadbeef);\n    UNUSED(bysignal);\n}\n\nint fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    long long code_to_exit_with;\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModule_StringToLongLong(argv[1], &code_to_exit_with);\n    exitted_with_code = -1;\n    child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef);\n    if (child_pid < 0) {\n        RedisModule_ReplyWithError(ctx, \"Fork failed\");\n        return REDISMODULE_OK;\n    } else if (child_pid > 0) {\n        /* parent */\n        RedisModule_ReplyWithLongLong(ctx, child_pid);\n        return REDISMODULE_OK;\n    }\n\n    /* child */\n    RedisModule_Log(ctx, \"notice\", \"fork child started\");\n    usleep(500000);\n    RedisModule_Log(ctx, \"notice\", \"fork child exiting\");\n    RedisModule_ExitFromChild(code_to_exit_with);\n    /* unreachable */\n    return 0;\n}\n\nint fork_exitcode(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    UNUSED(argv);\n    UNUSED(argc);\n    RedisModule_ReplyWithLongLong(ctx, exitted_with_code);\n    return REDISMODULE_OK;\n}\n\nint fork_kill(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    UNUSED(argv);\n    UNUSED(argc);\n    if (RedisModule_KillForkChild(child_pid) != REDISMODULE_OK)\n        RedisModule_ReplyWithError(ctx, \"KillForkChild failed\");\n    else\n        RedisModule_ReplyWithLongLong(ctx, 1);\n    child_pid = -1;\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    UNUSED(argv);\n    UNUSED(argc);\n    if (RedisModule_Init(ctx,\"fork\",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fork.create\", fork_create,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fork.exitcode\", fork_exitcode,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"fork.kill\", fork_kill,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/hooks.c",
    "content": "/* This module is used to test the server events hooks API.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2019, 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 \"redismodule.h\"\n#include <stdio.h>\n#include <string.h>\n\n/* We need to store events to be able to test and see what we got, and we can't\n * store them in the key-space since that would mess up rdb loading (duplicates)\n * and be lost of flushdb. */\nRedisModuleDict *event_log = NULL;\n\ntypedef struct EventElement {\n    long count;\n    RedisModuleString *last_val_string;\n    long last_val_int;\n} EventElement;\n\nvoid LogStringEvent(RedisModuleCtx *ctx, const char* keyname, const char* data) {\n    EventElement *event = RedisModule_DictGetC(event_log, (void*)keyname, strlen(keyname), NULL);\n    if (!event) {\n        event = RedisModule_Alloc(sizeof(EventElement));\n        memset(event, 0, sizeof(EventElement));\n        RedisModule_DictSetC(event_log, (void*)keyname, strlen(keyname), event);\n    }\n    if (event->last_val_string) RedisModule_FreeString(ctx, event->last_val_string);\n    event->last_val_string = RedisModule_CreateString(ctx, data, strlen(data));\n    event->count++;\n}\n\nvoid LogNumericEvent(RedisModuleCtx *ctx, const char* keyname, long data) {\n    REDISMODULE_NOT_USED(ctx);\n    EventElement *event = RedisModule_DictGetC(event_log, (void*)keyname, strlen(keyname), NULL);\n    if (!event) {\n        event = RedisModule_Alloc(sizeof(EventElement));\n        memset(event, 0, sizeof(EventElement));\n        RedisModule_DictSetC(event_log, (void*)keyname, strlen(keyname), event);\n    }\n    event->last_val_int = data;\n    event->count++;\n}\n\nvoid FreeEvent(RedisModuleCtx *ctx, EventElement *event) {\n    if (event->last_val_string)\n        RedisModule_FreeString(ctx, event->last_val_string);\n    RedisModule_Free(event);\n}\n\nint cmdEventCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    EventElement *event = RedisModule_DictGet(event_log, argv[1], NULL);\n    RedisModule_ReplyWithLongLong(ctx, event? event->count: 0);\n    return REDISMODULE_OK;\n}\n\nint cmdEventLast(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    EventElement *event = RedisModule_DictGet(event_log, argv[1], NULL);\n    if (event && event->last_val_string)\n        RedisModule_ReplyWithString(ctx, event->last_val_string);\n    else if (event)\n        RedisModule_ReplyWithLongLong(ctx, event->last_val_int);\n    else\n        RedisModule_ReplyWithNull(ctx);\n    return REDISMODULE_OK;\n}\n\nvoid clearEvents(RedisModuleCtx *ctx)\n{\n    RedisModuleString *key;\n    EventElement *event;\n    RedisModuleDictIter *iter = RedisModule_DictIteratorStart(event_log, \"^\", NULL);\n    while((key = RedisModule_DictNext(ctx, iter, (void**)&event)) != NULL) {\n        event->count = 0;\n        event->last_val_int = 0;\n        if (event->last_val_string) RedisModule_FreeString(ctx, event->last_val_string);\n        event->last_val_string = NULL;\n        RedisModule_DictDel(event_log, key, NULL);\n        RedisModule_Free(event);\n    }\n    RedisModule_DictIteratorStop(iter);\n}\n\nint cmdEventsClear(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argc);\n    REDISMODULE_NOT_USED(argv);\n    clearEvents(ctx);\n    return REDISMODULE_OK;\n}\n\n/* Client state change callback. */\nvoid clientChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleClientInfo *ci = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED) ?\n        \"client-connected\" : \"client-disconnected\";\n    LogNumericEvent(ctx, keyname, ci->id);\n}\n\nvoid flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleFlushInfo *fi = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) ?\n        \"flush-start\" : \"flush-end\";\n    LogNumericEvent(ctx, keyname, fi->dbnum);\n}\n\nvoid roleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    RedisModuleReplicationInfo *ri = data;\n    char *keyname = (sub == REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER) ?\n        \"role-master\" : \"role-replica\";\n    LogStringEvent(ctx, keyname, ri->masterhost);\n}\n\nvoid replicationChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = (sub == REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE) ?\n        \"replica-online\" : \"replica-offline\";\n    LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid rasterLinkChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = (sub == REDISMODULE_SUBEVENT_MASTER_LINK_UP) ?\n        \"masterlink-up\" : \"masterlink-down\";\n    LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid persistenceCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = NULL;\n    switch (sub) {\n        case REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START: keyname = \"persistence-rdb-start\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START: keyname = \"persistence-aof-start\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START: keyname = \"persistence-syncrdb-start\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_ENDED: keyname = \"persistence-end\"; break;\n        case REDISMODULE_SUBEVENT_PERSISTENCE_FAILED: keyname = \"persistence-failed\"; break;\n    }\n    /* modifying the keyspace from the fork child is not an option, using log instead */\n    RedisModule_Log(ctx, \"warning\", \"module-event-%s\", keyname);\n    if (sub == REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START)\n        LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid loadingCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n\n    char *keyname = NULL;\n    switch (sub) {\n        case REDISMODULE_SUBEVENT_LOADING_RDB_START: keyname = \"loading-rdb-start\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_AOF_START: keyname = \"loading-aof-start\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_REPL_START: keyname = \"loading-repl-start\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_ENDED: keyname = \"loading-end\"; break;\n        case REDISMODULE_SUBEVENT_LOADING_FAILED: keyname = \"loading-failed\"; break;\n    }\n    LogNumericEvent(ctx, keyname, 0);\n}\n\nvoid loadingProgressCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleLoadingProgress *ei = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB) ?\n        \"loading-progress-rdb\" : \"loading-progress-aof\";\n    LogNumericEvent(ctx, keyname, ei->progress);\n}\n\nvoid shutdownCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(data);\n    REDISMODULE_NOT_USED(sub);\n\n    RedisModule_Log(ctx, \"warning\", \"module-event-%s\", \"shutdown\");\n}\n\nvoid cronLoopCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n    REDISMODULE_NOT_USED(sub);\n\n    RedisModuleCronLoop *ei = data;\n    LogNumericEvent(ctx, \"cron-loop\", ei->hz);\n}\n\nvoid moduleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)\n{\n    REDISMODULE_NOT_USED(e);\n\n    RedisModuleModuleChange *ei = data;\n    char *keyname = (sub == REDISMODULE_SUBEVENT_MODULE_LOADED) ?\n        \"module-loaded\" : \"module-unloaded\";\n    LogStringEvent(ctx, keyname, ei->module_name);\n}\n\n/* This function must be present on each Redis module. It is used in order to\n * register the commands into the Redis server. */\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"testhook\",1,REDISMODULE_APIVER_1)\n        == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    /* replication related hooks */\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ReplicationRoleChanged, roleChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ReplicaChange, replicationChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_MasterLinkChange, rasterLinkChangeCallback);\n\n    /* persistence related hooks */\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_Persistence, persistenceCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_Loading, loadingCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_LoadingProgress, loadingProgressCallback);\n\n    /* other hooks */\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ClientChange, clientChangeCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_FlushDB, flushdbCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_Shutdown, shutdownCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_CronLoop, cronLoopCallback);\n    RedisModule_SubscribeToServerEvent(ctx,\n        RedisModuleEvent_ModuleChange, moduleChangeCallback);\n\n    event_log = RedisModule_CreateDict(ctx);\n\n    if (RedisModule_CreateCommand(ctx,\"hooks.event_count\", cmdEventCount,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"hooks.event_last\", cmdEventLast,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"hooks.clear\", cmdEventsClear,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnUnload(RedisModuleCtx *ctx) {\n    clearEvents(ctx);\n    RedisModule_FreeDict(ctx, event_log);\n    event_log = NULL;\n    return REDISMODULE_OK;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/infotest.c",
    "content": "#include \"redismodule.h\"\n\n#include <string.h>\n\nvoid InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) {\n    RedisModule_InfoAddSection(ctx, \"\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"global\", -2);\n    RedisModule_InfoAddFieldULongLong(ctx, \"uglobal\", (unsigned long long)-2);\n\n    RedisModule_InfoAddSection(ctx, \"Spanish\");\n    RedisModule_InfoAddFieldCString(ctx, \"uno\", \"one\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"dos\", 2);\n\n    RedisModule_InfoAddSection(ctx, \"Italian\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"due\", 2);\n    RedisModule_InfoAddFieldDouble(ctx, \"tre\", 3.3);\n\n    RedisModule_InfoAddSection(ctx, \"keyspace\");\n    RedisModule_InfoBeginDictField(ctx, \"db0\");\n    RedisModule_InfoAddFieldLongLong(ctx, \"keys\", 3);\n    RedisModule_InfoAddFieldLongLong(ctx, \"expires\", 1);\n    RedisModule_InfoEndDictField(ctx);\n\n    if (for_crash_report) {\n        RedisModule_InfoAddSection(ctx, \"Klingon\");\n        RedisModule_InfoAddFieldCString(ctx, \"one\", \"wa’\");\n        RedisModule_InfoAddFieldCString(ctx, \"two\", \"cha’\");\n        RedisModule_InfoAddFieldCString(ctx, \"three\", \"wej\");\n    }\n\n}\n\nint info_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, char field_type)\n{\n    if (argc != 3 && argc != 4) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    int err = REDISMODULE_OK;\n    const char *section, *field;\n    section = RedisModule_StringPtrLen(argv[1], NULL);\n    field = RedisModule_StringPtrLen(argv[2], NULL);\n    RedisModuleServerInfoData *info = RedisModule_GetServerInfo(ctx, section);\n    if (field_type=='i') {\n        long long ll = RedisModule_ServerInfoGetFieldSigned(info, field, &err);\n        if (err==REDISMODULE_OK)\n            RedisModule_ReplyWithLongLong(ctx, ll);\n    } else if (field_type=='u') {\n        unsigned long long ll = (unsigned long long)RedisModule_ServerInfoGetFieldUnsigned(info, field, &err);\n        if (err==REDISMODULE_OK)\n            RedisModule_ReplyWithLongLong(ctx, ll);\n    } else if (field_type=='d') {\n        double d = RedisModule_ServerInfoGetFieldDouble(info, field, &err);\n        if (err==REDISMODULE_OK)\n            RedisModule_ReplyWithDouble(ctx, d);\n    } else if (field_type=='c') {\n        const char *str = RedisModule_ServerInfoGetFieldC(info, field);\n        if (str)\n            RedisModule_ReplyWithCString(ctx, str);\n    } else {\n        RedisModuleString *str = RedisModule_ServerInfoGetField(ctx, info, field);\n        if (str) {\n            RedisModule_ReplyWithString(ctx, str);\n            RedisModule_FreeString(ctx, str);\n        } else\n            err=REDISMODULE_ERR;\n    }\n    if (err!=REDISMODULE_OK)\n        RedisModule_ReplyWithError(ctx, \"not found\");\n    RedisModule_FreeServerInfo(ctx, info);\n    return REDISMODULE_OK;\n}\n\nint info_gets(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 's');\n}\n\nint info_getc(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'c');\n}\n\nint info_geti(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'i');\n}\n\nint info_getu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'u');\n}\n\nint info_getd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    return info_get(ctx, argv, argc, 'd');\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    if (RedisModule_Init(ctx,\"infotest\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_RegisterInfoFunc(ctx, InfoFunc) == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"info.gets\", info_gets,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.getc\", info_getc,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.geti\", info_geti,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.getu\", info_getu,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"info.getd\", info_getd,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/misc.c",
    "content": "#define REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n#include <errno.h>\n\n#define UNUSED(x) (void)(x)\n\nint test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    const char* cmdname = RedisModule_StringPtrLen(argv[1], NULL);\n    RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, \"v\", argv+2, argc-2);\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithError(ctx, strerror(errno));\n    }\n    return REDISMODULE_OK;\n}\n\nint test_call_info(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    RedisModuleCallReply *reply;\n    if (argc>1)\n        reply = RedisModule_Call(ctx, \"info\", \"s\", argv[1]);\n    else\n        reply = RedisModule_Call(ctx, \"info\", \"\");\n    if (reply) {\n        RedisModule_ReplyWithCallReply(ctx, reply);\n        RedisModule_FreeCallReply(reply);\n    } else {\n        RedisModule_ReplyWithError(ctx, strerror(errno));\n    }\n    return REDISMODULE_OK;\n}\n\nint test_ld_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    UNUSED(argv);\n    UNUSED(argc);\n    long double ld = 0.00000000000000001L;\n    const char *ldstr = \"0.00000000000000001\";\n    RedisModuleString *s1 = RedisModule_CreateStringFromLongDouble(ctx, ld, 1);\n    RedisModuleString *s2 =\n        RedisModule_CreateString(ctx, ldstr, strlen(ldstr));\n    if (RedisModule_StringCompare(s1, s2) != 0) {\n        char err[4096];\n        snprintf(err, 4096,\n            \"Failed to convert long double to string ('%s' != '%s')\",\n            RedisModule_StringPtrLen(s1, NULL),\n            RedisModule_StringPtrLen(s2, NULL));\n        RedisModule_ReplyWithError(ctx, err);\n        goto final;\n    }\n    long double ld2 = 0;\n    if (RedisModule_StringToLongDouble(s2, &ld2) == REDISMODULE_ERR) {\n        RedisModule_ReplyWithError(ctx,\n            \"Failed to convert string to long double\");\n        goto final;\n    }\n    if (ld2 != ld) {\n        char err[4096];\n        snprintf(err, 4096,\n            \"Failed to convert string to long double (%.40Lf != %.40Lf)\",\n            ld2,\n            ld);\n        RedisModule_ReplyWithError(ctx, err);\n        goto final;\n    }\n\n    /* Make sure we can't convert a string that has \\0 in it */\n    char buf[4] = \"123\";\n    buf[1] = '\\0';\n    RedisModuleString *s3 = RedisModule_CreateString(ctx, buf, 3);\n    long double ld3;\n    if (RedisModule_StringToLongDouble(s3, &ld3) == REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"Invalid string successfully converted to long double\");\n        RedisModule_FreeString(ctx, s3);\n        goto final;\n    }\n    RedisModule_FreeString(ctx, s3);\n\n    RedisModule_ReplyWithLongDouble(ctx, ld2);\nfinal:\n    RedisModule_FreeString(ctx, s1);\n    RedisModule_FreeString(ctx, s2);\n    return REDISMODULE_OK;\n}\n\nint test_flushall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModule_ResetDataset(1, 0);\n    RedisModule_ReplyWithCString(ctx, \"Ok\");\n    return REDISMODULE_OK;\n}\n\nint test_dbsize(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    long long ll = RedisModule_DbSize(ctx);\n    RedisModule_ReplyWithLongLong(ctx, ll);\n    return REDISMODULE_OK;\n}\n\nint test_randomkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleString *str = RedisModule_RandomKey(ctx);\n    RedisModule_ReplyWithString(ctx, str);\n    RedisModule_FreeString(ctx, str);\n    return REDISMODULE_OK;\n}\n\nRedisModuleKey *open_key_or_reply(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) {\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);\n    if (!key) {\n        RedisModule_ReplyWithError(ctx, \"key not found\");\n        return NULL;\n    }\n    return key;\n}\n\nint test_getlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lru;\n    RedisModule_GetLRU(key, &lru);\n    RedisModule_ReplyWithLongLong(ctx, lru);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint test_setlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lru;\n    if (RedisModule_StringToLongLong(argv[2], &lru) != REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"invalid idle time\");\n        return REDISMODULE_OK;\n    }\n    int was_set = RedisModule_SetLRU(key, lru)==REDISMODULE_OK;\n    RedisModule_ReplyWithLongLong(ctx, was_set);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint test_getlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lfu;\n    RedisModule_GetLFU(key, &lfu);\n    RedisModule_ReplyWithLongLong(ctx, lfu);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc<3) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);\n    mstime_t lfu;\n    if (RedisModule_StringToLongLong(argv[2], &lfu) != REDISMODULE_OK) {\n        RedisModule_ReplyWithError(ctx, \"invalid freq\");\n        return REDISMODULE_OK;\n    }\n    int was_set = RedisModule_SetLFU(key, lfu)==REDISMODULE_OK;\n    RedisModule_ReplyWithLongLong(ctx, was_set);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    if (RedisModule_Init(ctx,\"misc\",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"test.call_generic\", test_call_generic,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.call_info\", test_call_info,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.ld_conversion\", test_ld_conv, \"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.flushall\", test_flushall,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.dbsize\", test_dbsize,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.randomkey\", test_randomkey,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.setlru\", test_setlru,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.getlru\", test_getlru,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.setlfu\", test_setlfu,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n    if (RedisModule_CreateCommand(ctx,\"test.getlfu\", test_getlfu,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/propagate.c",
    "content": "/* This module is used to test the propagation (replication + AOF) of\n * commands, via the RedisModule_Replicate() interface, in asynchronous\n * contexts, such as callbacks not implementing commands, and thread safe\n * contexts.\n *\n * We create a timer callback and a threads using a thread safe context.\n * Using both we try to propagate counters increments, and later we check\n * if the replica contains the changes as expected.\n *\n * -----------------------------------------------------------------------------\n *\n * Copyright (c) 2019, 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 REDISMODULE_EXPERIMENTAL_API\n#include \"redismodule.h\"\n#include <pthread.h>\n\n/* Timer callback. */\nvoid timerHandler(RedisModuleCtx *ctx, void *data) {\n    REDISMODULE_NOT_USED(ctx);\n    REDISMODULE_NOT_USED(data);\n\n    static int times = 0;\n\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"timer\");\n    times++;\n\n    if (times < 10)\n        RedisModule_CreateTimer(ctx,100,timerHandler,NULL);\n    else\n        times = 0;\n}\n\n/* The thread entry point. */\nvoid *threadMain(void *arg) {\n    REDISMODULE_NOT_USED(arg);\n    RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(NULL);\n    RedisModule_SelectDb(ctx,9); /* Tests ran in database number 9. */\n    for (int i = 0; i < 10; i++) {\n        RedisModule_ThreadSafeContextLock(ctx);\n        RedisModule_Replicate(ctx,\"INCR\",\"c\",\"a-from-thread\");\n        RedisModule_Replicate(ctx,\"INCR\",\"c\",\"b-from-thread\");\n        RedisModule_ThreadSafeContextUnlock(ctx);\n    }\n    RedisModule_FreeThreadSafeContext(ctx);\n    return NULL;\n}\n\nint propagateTestCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    RedisModuleTimerID timer_id =\n        RedisModule_CreateTimer(ctx,100,timerHandler,NULL);\n    REDISMODULE_NOT_USED(timer_id);\n\n    pthread_t tid;\n    if (pthread_create(&tid,NULL,threadMain,NULL) != 0)\n        return RedisModule_ReplyWithError(ctx,\"-ERR Can't start thread\");\n    REDISMODULE_NOT_USED(tid);\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n}\n\nint propagateTest2Command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    /* Replicate two commands to test MULTI/EXEC wrapping. */\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-1\");\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-2\");\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n}\n\nint propagateTest3Command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    RedisModuleCallReply *reply;\n\n    /* This test mixes multiple propagation systems. */\n    reply = RedisModule_Call(ctx, \"INCR\", \"c!\", \"using-call\");\n    RedisModule_FreeCallReply(reply);\n\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-1\");\n    RedisModule_Replicate(ctx,\"INCR\",\"c\",\"counter-2\");\n\n    reply = RedisModule_Call(ctx, \"INCR\", \"c!\", \"after-call\");\n    RedisModule_FreeCallReply(reply);\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"propagate-test\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"propagate-test\",\n                propagateTestCommand,\n                \"\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"propagate-test-2\",\n                propagateTest2Command,\n                \"\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"propagate-test-3\",\n                propagateTest3Command,\n                \"\",1,1,1) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/scan.c",
    "content": "#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\ntypedef struct {\n    size_t nkeys;\n} scan_strings_pd;\n\nvoid scan_strings_callback(RedisModuleCtx *ctx, RedisModuleString* keyname, RedisModuleKey* key, void *privdata) {\n    scan_strings_pd* pd = privdata;\n    int was_opened = 0;\n    if (!key) {\n        key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ);\n        was_opened = 1;\n    }\n\n    if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING) {\n        size_t len;\n        char * data = RedisModule_StringDMA(key, &len, REDISMODULE_READ);\n        RedisModule_ReplyWithArray(ctx, 2);\n        RedisModule_ReplyWithString(ctx, keyname);\n        RedisModule_ReplyWithStringBuffer(ctx, data, len);\n        pd->nkeys++;\n    }\n    if (was_opened)\n        RedisModule_CloseKey(key);\n}\n\nint scan_strings(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    scan_strings_pd pd = {\n        .nkeys = 0,\n    };\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);\n\n    RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate();\n    while(RedisModule_Scan(ctx, cursor, scan_strings_callback, &pd));\n    RedisModule_ScanCursorDestroy(cursor);\n\n    RedisModule_ReplySetArrayLength(ctx, pd.nkeys);\n    return REDISMODULE_OK;\n}\n\ntypedef struct {\n    RedisModuleCtx *ctx;\n    size_t nreplies;\n} scan_key_pd;\n\nvoid scan_key_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata) {\n    REDISMODULE_NOT_USED(key);\n    scan_key_pd* pd = privdata;\n    RedisModule_ReplyWithArray(pd->ctx, 2);\n    RedisModule_ReplyWithString(pd->ctx, field);\n    if (value)\n        RedisModule_ReplyWithString(pd->ctx, value);\n    else\n        RedisModule_ReplyWithNull(pd->ctx);\n    pd->nreplies++;\n}\n\nint scan_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    scan_key_pd pd = {\n        .ctx = ctx,\n        .nreplies = 0,\n    };\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);\n    if (!key) {\n        RedisModule_ReplyWithError(ctx, \"not found\");\n        return REDISMODULE_OK;\n    }\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);\n\n    RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate();\n    while(RedisModule_ScanKey(key, cursor, scan_key_callback, &pd));\n    RedisModule_ScanCursorDestroy(cursor);\n\n    RedisModule_ReplySetArrayLength(ctx, pd.nreplies);\n    RedisModule_CloseKey(key);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n    if (RedisModule_Init(ctx, \"scan\", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx, \"scan.scan_strings\", scan_strings, \"\", 0, 0, 0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx, \"scan.scan_key\", scan_key, \"\", 0, 0, 0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/modules/testrdb.c",
    "content": "#include \"redismodule.h\"\n\n#include <string.h>\n#include <assert.h>\n\n/* Module configuration, save aux or not? */\nlong long conf_aux_count = 0;\n\n/* Registered type */\nRedisModuleType *testrdb_type = NULL;\n\n/* Global values to store and persist to aux */\nRedisModuleString *before_str = NULL;\nRedisModuleString *after_str = NULL;\n\nvoid *testrdb_type_load(RedisModuleIO *rdb, int encver) {\n    int count = RedisModule_LoadSigned(rdb);\n    RedisModuleString *str = RedisModule_LoadString(rdb);\n    float f = RedisModule_LoadFloat(rdb);\n    long double ld = RedisModule_LoadLongDouble(rdb);\n    if (RedisModule_IsIOError(rdb)) {\n        RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb);\n        if (str)\n            RedisModule_FreeString(ctx, str);\n        return NULL;\n    }\n    /* Using the values only after checking for io errors. */\n    assert(count==1);\n    assert(encver==1);\n    assert(f==1.5f);\n    assert(ld==0.333333333333333333L);\n    return str;\n}\n\nvoid testrdb_type_save(RedisModuleIO *rdb, void *value) {\n    RedisModuleString *str = (RedisModuleString*)value;\n    RedisModule_SaveSigned(rdb, 1);\n    RedisModule_SaveString(rdb, str);\n    RedisModule_SaveFloat(rdb, 1.5);\n    RedisModule_SaveLongDouble(rdb, 0.333333333333333333L);\n}\n\nvoid testrdb_aux_save(RedisModuleIO *rdb, int when) {\n    if (conf_aux_count==1) assert(when == REDISMODULE_AUX_AFTER_RDB);\n    if (conf_aux_count==0) assert(0);\n    if (when == REDISMODULE_AUX_BEFORE_RDB) {\n        if (before_str) {\n            RedisModule_SaveSigned(rdb, 1);\n            RedisModule_SaveString(rdb, before_str);\n        } else {\n            RedisModule_SaveSigned(rdb, 0);\n        }\n    } else {\n        if (after_str) {\n            RedisModule_SaveSigned(rdb, 1);\n            RedisModule_SaveString(rdb, after_str);\n        } else {\n            RedisModule_SaveSigned(rdb, 0);\n        }\n    }\n}\n\nint testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) {\n    assert(encver == 1);\n    if (conf_aux_count==1) assert(when == REDISMODULE_AUX_AFTER_RDB);\n    if (conf_aux_count==0) assert(0);\n    RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb);\n    if (when == REDISMODULE_AUX_BEFORE_RDB) {\n        if (before_str)\n            RedisModule_FreeString(ctx, before_str);\n        before_str = NULL;\n        int count = RedisModule_LoadSigned(rdb);\n        if (RedisModule_IsIOError(rdb))\n            return REDISMODULE_ERR;\n        if (count)\n            before_str = RedisModule_LoadString(rdb);\n    } else {\n        if (after_str)\n            RedisModule_FreeString(ctx, after_str);\n        after_str = NULL;\n        int count = RedisModule_LoadSigned(rdb);\n        if (RedisModule_IsIOError(rdb))\n            return REDISMODULE_ERR;\n        if (count)\n            after_str = RedisModule_LoadString(rdb);\n    }\n    if (RedisModule_IsIOError(rdb))\n        return REDISMODULE_ERR;\n    return REDISMODULE_OK;\n}\n\nvoid testrdb_type_free(void *value) {\n    if (value)\n        RedisModule_FreeString(NULL, (RedisModuleString*)value);\n}\n\nint testrdb_set_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2) {\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    if (before_str)\n        RedisModule_FreeString(ctx, before_str);\n    before_str = argv[1];\n    RedisModule_RetainString(ctx, argv[1]);\n    RedisModule_ReplyWithLongLong(ctx, 1);\n    return REDISMODULE_OK;\n}\n\nint testrdb_get_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    if (argc != 1){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    if (before_str)\n        RedisModule_ReplyWithString(ctx, before_str);\n    else\n        RedisModule_ReplyWithStringBuffer(ctx, \"\", 0);\n    return REDISMODULE_OK;\n}\n\nint testrdb_set_after(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    if (after_str)\n        RedisModule_FreeString(ctx, after_str);\n    after_str = argv[1];\n    RedisModule_RetainString(ctx, argv[1]);\n    RedisModule_ReplyWithLongLong(ctx, 1);\n    return REDISMODULE_OK;\n}\n\nint testrdb_get_after(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    REDISMODULE_NOT_USED(argv);\n    if (argc != 1){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n    if (after_str)\n        RedisModule_ReplyWithString(ctx, after_str);\n    else\n        RedisModule_ReplyWithStringBuffer(ctx, \"\", 0);\n    return REDISMODULE_OK;\n}\n\nint testrdb_set_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 3){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModuleString *str = RedisModule_ModuleTypeGetValue(key);\n    if (str)\n        RedisModule_FreeString(ctx, str);\n    RedisModule_ModuleTypeSetValue(key, testrdb_type, argv[2]);\n    RedisModule_RetainString(ctx, argv[2]);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithLongLong(ctx, 1);\n    return REDISMODULE_OK;\n}\n\nint testrdb_get_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)\n{\n    if (argc != 2){\n        RedisModule_WrongArity(ctx);\n        return REDISMODULE_OK;\n    }\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);\n    RedisModuleString *str = RedisModule_ModuleTypeGetValue(key);\n    RedisModule_CloseKey(key);\n    RedisModule_ReplyWithString(ctx, str);\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n    REDISMODULE_NOT_USED(argv);\n    REDISMODULE_NOT_USED(argc);\n\n    if (RedisModule_Init(ctx,\"testrdb\",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS);\n\n    if (argc > 0)\n        RedisModule_StringToLongLong(argv[0], &conf_aux_count);\n\n    if (conf_aux_count==0) {\n        RedisModuleTypeMethods datatype_methods = {\n            .version = 1,\n            .rdb_load = testrdb_type_load,\n            .rdb_save = testrdb_type_save,\n            .aof_rewrite = NULL,\n            .digest = NULL,\n            .free = testrdb_type_free,\n        };\n\n        testrdb_type = RedisModule_CreateDataType(ctx, \"test__rdb\", 1, &datatype_methods);\n        if (testrdb_type == NULL)\n            return REDISMODULE_ERR;\n    } else {\n        RedisModuleTypeMethods datatype_methods = {\n            .version = REDISMODULE_TYPE_METHOD_VERSION,\n            .rdb_load = testrdb_type_load,\n            .rdb_save = testrdb_type_save,\n            .aof_rewrite = NULL,\n            .digest = NULL,\n            .free = testrdb_type_free,\n            .aux_load = testrdb_aux_load,\n            .aux_save = testrdb_aux_save,\n            .aux_save_triggers = (conf_aux_count == 1 ?\n                                  REDISMODULE_AUX_AFTER_RDB :\n                                  REDISMODULE_AUX_BEFORE_RDB | REDISMODULE_AUX_AFTER_RDB)\n        };\n\n        testrdb_type = RedisModule_CreateDataType(ctx, \"test__rdb\", 1, &datatype_methods);\n        if (testrdb_type == NULL)\n            return REDISMODULE_ERR;\n    }\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.set.before\", testrdb_set_before,\"deny-oom\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.get.before\", testrdb_get_before,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.set.after\", testrdb_set_after,\"deny-oom\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.get.after\", testrdb_get_after,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.set.key\", testrdb_set_key,\"deny-oom\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    if (RedisModule_CreateCommand(ctx,\"testrdb.get.key\", testrdb_get_key,\"\",1,1,1) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n    return REDISMODULE_OK;\n}\n\nint RedisModule_OnUnload(RedisModuleCtx *ctx) {\n    if (before_str)\n        RedisModule_FreeString(ctx, before_str);\n    if (after_str)\n        RedisModule_FreeString(ctx, after_str);\n    return REDISMODULE_OK;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/sentinel/run.tcl",
    "content": "# Sentinel test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com\n# This software 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.\nset ::tlsdir \"../../tls\"\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    end_tests\n}\n\nif {[catch main e]} {\n    puts $::errorInfo\n    cleanup\n    exit 1\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 receive 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 receive 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": "Chapter07/redis-6.0.3/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 receive 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 receive failover info\"\n    }\n}\n\ntest \"New master [join $addr {:}] role matches\" {\n    assert {[RI $master_id role] eq {master}}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 02_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                    ([RI $id master_link_status] eq {up})\n                } else {\n                    fail \"Redis slave $id is replicating from wrong master\"\n                }\n            }\n        }\n    }\n}\n\nproc 02_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 receive 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\n02_test_slaves_replication\n02_crash_and_failover\n02_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\n02_crash_and_failover\n02_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.\n02_test_slaves_replication\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/sentinel/tests/03-runtime-reconf.tcl",
    "content": "# Test runtime reconfiguration command SENTINEL SET.\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/tests/sentinel/tests/05-manual.tcl",
    "content": "# Test manual failover\n\nsource \"../tests/includes/init-tests.tcl\"\n\ntest \"Manual failover works\" {\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    catch {S 0 SENTINEL FAILOVER mymaster} reply\n    assert {$reply eq \"OK\"}\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 receive failover info\"\n        }\n    }\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\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/sentinel/tests/06-ckquorum.tcl",
    "content": "# Test for the SENTINEL CKQUORUM command\n\nsource \"../tests/includes/init-tests.tcl\"\nset num_sentinels [llength $::sentinel_instances]\n\ntest \"CKQUORUM reports OK and the right amount of Sentinels\" {\n    foreach_sentinel_id id {\n        assert_match \"*OK $num_sentinels usable*\" [S $id SENTINEL CKQUORUM mymaster]\n    }\n}\n\ntest \"CKQUORUM detects quorum cannot be reached\" {\n    set orig_quorum [expr {$num_sentinels/2+1}]\n    S 0 SENTINEL SET mymaster quorum [expr {$num_sentinels+1}]\n    catch {[S 0 SENTINEL CKQUORUM mymaster]} err\n    assert_match \"*NOQUORUM*\" $err\n    S 0 SENTINEL SET mymaster quorum $orig_quorum\n}\n\ntest \"CKQUORUM detects failover authorization cannot be reached\" {\n    set orig_quorum [expr {$num_sentinels/2+1}]\n    S 0 SENTINEL SET mymaster quorum 1\n    kill_instance sentinel 1\n    kill_instance sentinel 2\n    kill_instance sentinel 3\n    after 5000\n    catch {[S 0 SENTINEL CKQUORUM mymaster]} err\n    assert_match \"*NOQUORUM*\" $err\n    S 0 SENTINEL SET mymaster quorum $orig_quorum\n    restart_instance sentinel 1\n    restart_instance sentinel 2\n    restart_instance sentinel 3\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/sentinel/tests/07-down-conditions.tcl",
    "content": "# Test conditions where an instance is considered to be down\n\nsource \"../tests/includes/init-tests.tcl\"\nsource \"../../../tests/support/cli.tcl\"\n\nproc ensure_master_up {} {\n    wait_for_condition 1000 50 {\n        [dict get [S 4 sentinel master mymaster] flags] eq \"master\"\n    } else {\n        fail \"Master flags are not just 'master'\"\n    }\n}\n\nproc ensure_master_down {} {\n    wait_for_condition 1000 50 {\n        [string match *down* \\\n            [dict get [S 4 sentinel master mymaster] flags]]\n    } else {\n        fail \"Master is not flagged SDOWN\"\n    }\n}\n\ntest \"Crash the majority of Sentinels to prevent failovers for this unit\" {\n    for {set id 0} {$id < $quorum} {incr id} {\n        kill_instance sentinel $id\n    }\n}\n\ntest \"SDOWN is triggered by non-responding but not crashed instance\" {\n    lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port\n    ensure_master_up\n    exec ../../../src/redis-cli -h $host -p $port {*}[rediscli_tls_config \"../../../tests\"] debug sleep 10 > /dev/null &\n    ensure_master_down\n    ensure_master_up\n}\n\ntest \"SDOWN is triggered by crashed instance\" {\n    lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port\n    ensure_master_up\n    kill_instance redis 0\n    ensure_master_down\n    restart_instance redis 0\n    ensure_master_up\n}\n\ntest \"SDOWN is triggered by masters advertising as slaves\" {\n    ensure_master_up\n    R 0 slaveof 127.0.0.1 34567\n    ensure_master_down\n    R 0 slaveof no one\n    ensure_master_up\n}\n\ntest \"SDOWN is triggered by misconfigured instance repling with errors\" {\n    ensure_master_up\n    set orig_dir [lindex [R 0 config get dir] 1]\n    set orig_save [lindex [R 0 config get save] 1]\n    # Set dir to / and filename to \"tmp\" to make sure it will fail.\n    R 0 config set dir /\n    R 0 config set dbfilename tmp\n    R 0 config set save \"1000000 1000000\"\n    R 0 bgsave\n    ensure_master_down\n    R 0 config set save $orig_save\n    R 0 config set dir $orig_dir\n    R 0 config set dbfilename dump.rdb\n    R 0 bgsave\n    ensure_master_up\n}\n\n# We use this test setup to also test command renaming, as a side\n# effect of the master going down if we send PONG instead of PING\ntest \"SDOWN is triggered if we rename PING to PONG\" {\n    ensure_master_up\n    S 4 SENTINEL SET mymaster rename-command PING PONG\n    ensure_master_down\n    S 4 SENTINEL SET mymaster rename-command PING PING\n    ensure_master_up\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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\ntest \"(init) Remove old master entry from sentinels\" {\n    foreach_sentinel_id id {\n        catch {S $id SENTINEL REMOVE mymaster}\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        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 1000 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 1000 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 1000 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": "Chapter07/redis-6.0.3/tests/sentinel/tmp/.gitignore",
    "content": "redis_*\nsentinel_*\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/support/cli.tcl",
    "content": "proc rediscli_tls_config {testsdir} {\n    set tlsdir [file join $testsdir tls]\n    set cert [file join $tlsdir redis.crt]\n    set key [file join $tlsdir redis.key]\n    set cacert [file join $tlsdir ca.crt]\n\n    if {$::tls} {\n        return [list --tls --cert $cert --key $key --cacert $cacert]\n    } else {\n        return {}\n    }\n}\n\nproc rediscli {port {opts {}}} {\n    set cmd [list src/redis-cli -p $port]\n    lappend cmd {*}[rediscli_tls_config \"tests\"]\n    lappend cmd {*}$opts\n    return $cmd\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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        set ip_port [lindex [split $start_node @] 0]\n        lassign [split $ip_port :] start_host start_port\n        if {[catch {\n            set r {}\n            set r [redis $start_host $start_port 0 $::tls]\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 \" $ip_port: $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        set addr [lindex [split $addr @] 0]\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 0 $::tls]}\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        eval {return [lrange $argv 2 1+[lindex $argv 1]]}\n        evalsha {return [lrange $argv 2 1+[lindex $argv 1]]}\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": "Chapter07/redis-6.0.3/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::addr {}\narray set ::redis::blocking {}\narray set ::redis::deferred {}\narray set ::redis::reconnect {}\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} {tls 0} {tlsoptions {}}} {\n    if {$tls} {\n        package require tls\n        ::tls::init \\\n            -cafile \"$::tlsdir/ca.crt\" \\\n            -certfile \"$::tlsdir/redis.crt\" \\\n            -keyfile \"$::tlsdir/redis.key\" \\\n            {*}$tlsoptions\n        set fd [::tls::socket $server $port]\n    } else {\n        set fd [socket $server $port]\n    }\n    fconfigure $fd -translation binary\n    set id [incr ::redis::id]\n    set ::redis::fd($id) $fd\n    set ::redis::addr($id) [list $server $port]\n    set ::redis::blocking($id) 1\n    set ::redis::deferred($id) $defer\n    set ::redis::reconnect($id) 0\n    set ::redis::tls $tls\n    ::redis::redis_reset_state $id\n    interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id\n}\n\n# This is a wrapper to the actual dispatching procedure that handles\n# reconnection if needed.\nproc ::redis::__dispatch__ {id method args} {\n    set errorcode [catch {::redis::__dispatch__raw__ $id $method $args} retval]\n    if {$errorcode && $::redis::reconnect($id) && $::redis::fd($id) eq {}} {\n        # Try again if the connection was lost.\n        # FIXME: we don't re-select the previously selected DB, nor we check\n        # if we are inside a transaction that needs to be re-issued from\n        # scratch.\n        set errorcode [catch {::redis::__dispatch__raw__ $id $method $args} retval]\n    }\n    return -code $errorcode $retval\n}\n\nproc ::redis::__dispatch__raw__ {id method argv} {\n    set fd $::redis::fd($id)\n\n    # Reconnect the link if needed.\n    if {$fd eq {}} {\n        lassign $::redis::addr($id) host port\n        if {$::redis::tls} {\n            set ::redis::fd($id) [::tls::socket $host $port]\n        } else {\n            set ::redis::fd($id) [socket $host $port]\n        }\n        fconfigure $::redis::fd($id) -translation binary\n        set fd $::redis::fd($id)\n    }\n\n    set blocking $::redis::blocking($id)\n    set deferred $::redis::deferred($id)\n    if {$blocking == 0} {\n        if {[llength $argv] == 0} {\n            error \"Please provide a callback in non-blocking mode\"\n        }\n        set callback [lindex $argv end]\n        set argv [lrange $argv 0 end-1]\n    }\n    if {[info command ::redis::__method__$method] eq {}} {\n        set cmd \"*[expr {[llength $argv]+1}]\\r\\n\"\n        append cmd \"$[string length $method]\\r\\n$method\\r\\n\"\n        foreach a $argv {\n            append cmd \"$[string length $a]\\r\\n$a\\r\\n\"\n        }\n        ::redis::redis_write $fd $cmd\n        if {[catch {flush $fd}]} {\n            set ::redis::fd($id) {}\n            return -code error \"I/O error reading reply\"\n        }\n\n        if {!$deferred} {\n            if {$blocking} {\n                ::redis::redis_read_reply $id $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] $argv\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__reconnect {id fd val} {\n    set ::redis::reconnect($id) $val\n}\n\nproc ::redis::__method__read {id fd} {\n    ::redis::redis_read_reply $id $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::addr($id)}\n    catch {unset ::redis::blocking($id)}\n    catch {unset ::redis::deferred($id)}\n    catch {unset ::redis::reconnect($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 {id 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 $id $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 {id 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 $id $fd}\n        default {\n            if {$type eq {}} {\n                set ::redis::fd($id) {}\n                return -code error \"I/O error reading reply\"\n            }\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": "Chapter07/redis-6.0.3/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 \"Can't 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    send_data_packet $::test_server_fd server-killing $pid\n    catch {exec kill $pid}\n    if {$::valgrind} {\n        set max_wait 60000\n    } else {\n        set max_wait 10000\n    }\n    while {[is_alive $config]} {\n        incr wait 10\n\n        if {$wait >= $max_wait} {\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    # Remove this pid from the set of active pids in the test server.\n    send_data_packet $::test_server_fd server-killed $pid\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        if {$::tls} {\n            set fd [::tls::socket $host $port] \n        } else {\n            set fd [socket $host $port]\n        }\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\n# Write the configuration in the dictionary 'config' in the specified\n# file name.\nproc create_server_config_file {filename config} {\n    set fp [open $filename 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\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 0 $::tls]\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    if {$::tls} {\n        dict set config \"tls-cert-file\" [format \"%s/tests/tls/redis.crt\" [pwd]]\n        dict set config \"tls-key-file\" [format \"%s/tests/tls/redis.key\" [pwd]]\n        dict set config \"tls-dh-params-file\" [format \"%s/tests/tls/redis.dh\" [pwd]]\n        dict set config \"tls-ca-cert-file\" [format \"%s/tests/tls/ca.crt\" [pwd]]\n        dict set config \"loglevel\" \"debug\"\n    }\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    if {$::tls} {\n        dict set config \"port\" 0\n        dict set config \"tls-port\" $::port\n        dict set config \"tls-cluster\" \"yes\"\n        dict set config \"tls-replication\" \"yes\"\n    } else {\n        dict set config port $::port\n    }\n\n    set unixsocket [file normalize [format \"%s/%s\" [dict get $config \"dir\"] \"socket\"]]\n    dict set config \"unixsocket\" $unixsocket\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    create_server_config_file $config_file $config\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    # We need a loop here to retry with different ports.\n    set server_started 0\n    while {$server_started == 0} {\n        if {$::verbose} {\n            puts -nonewline \"=== ($tags) Starting server ${::host}:${::port} \"\n        }\n\n        send_data_packet $::test_server_fd \"server-spawning\" \"port $::port\"\n\n        if {$::valgrind} {\n            set pid [exec valgrind --track-origins=yes --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &]\n        } elseif ($::stack_logging) {\n            set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt src/redis-server $config_file > $stdout 2> $stderr &]\n        } else {\n            set pid [exec src/redis-server $config_file > $stdout 2> $stderr &]\n        }\n\n        # Tell the test server about this new instance.\n        send_data_packet $::test_server_fd server-spawned $pid\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        # Wait for actual startup\n        set checkperiod 100; # Milliseconds\n        set maxiter [expr {120*1000/100}] ; # Wait up to 2 minutes.\n        set port_busy 0\n        while {![info exists _pid]} {\n            regexp {PID:\\s(\\d+)} [exec cat $stdout] _ _pid\n            after $checkperiod\n            incr maxiter -1\n            if {$maxiter == 0} {\n                start_server_error $config_file \"No PID detected in log $stdout\"\n                puts \"--- LOG CONTENT ---\"\n                puts [exec cat $stdout]\n                puts \"-------------------\"\n                break\n            }\n\n            # Check if the port is actually busy and the server failed\n            # for this reason.\n            if {[regexp {Could not create server TCP} [exec cat $stdout]]} {\n                set port_busy 1\n                break\n            }\n        }\n\n        # Sometimes we have to try a different port, even if we checked\n        # for availability. Other test clients may grab the port before we\n        # are able to do it for example.\n        if {$port_busy} {\n            puts \"Port $::port was already busy, trying another port...\"\n            set ::port [find_available_port [expr {$::port+1}]]\n            if {$::tls} {\n                dict set config \"tls-port\" $::port\n            } else {\n                dict set config port $::port\n            }\n            create_server_config_file $config_file $config\n            continue; # Try again\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        set server_started 1\n    }\n\n    # setup properties to be able to initialize a client object\n    set port_param [expr $::tls ? {\"tls-port\"} : {\"port\"}]\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_param]} { set port [dict get $config $port_param] }\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    dict set srv \"unixsocket\" $unixsocket\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        if {$::wait_server} {\n            set msg \"server started PID: [dict get $srv \"pid\"]. press any key to continue...\"\n            puts $msg\n            read stdin 1\n        }\n\n        while 1 {\n            # check that the server actually started and is ready for connections\n            if {[exec grep -i \"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": "Chapter07/redis-6.0.3/tests/support/test.tcl",
    "content": "set ::num_tests 0\nset ::num_passed 0\nset ::num_failed 0\nset ::num_skipped 0\nset ::num_aborted 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        set context \"(context: [info frame -1])\"\n        error \"assertion:Expected [uplevel 1 [list subst -nocommands $condition]] $context\"\n    }\n}\n\nproc assert_no_match {pattern value} {\n    if {[string match $pattern $value]} {\n        set context \"(context: [info frame -1])\"\n        error \"assertion:Expected '$value' to not match '$pattern' $context\"\n    }\n}\n\nproc assert_match {pattern value} {\n    if {![string match $pattern $value]} {\n        set context \"(context: [info frame -1])\"\n        error \"assertion:Expected '$value' to match '$pattern' $context\"\n    }\n}\n\nproc assert_equal {value expected {detail \"\"}} {\n    if {$expected ne $value} {\n        if {$detail ne \"\"} {\n            set detail \"(detail: $detail)\"\n        } else {\n            set detail \"(context: [info frame -1])\"\n        }\n        error \"assertion:Expected '$value' to be equal to '$expected' $detail\"\n    }\n}\n\nproc assert_lessthan {value expected {detail \"\"}} {\n    if {!($value < $expected)} {\n        if {$detail ne \"\"} {\n            set detail \"(detail: $detail)\"\n        } else {\n            set detail \"(context: [info frame -1])\"\n        }\n        error \"assertion:Expected '$value' to be lessthan to '$expected' $detail\"\n    }\n}\n\nproc assert_range {value min max {detail \"\"}} {\n    if {!($value <= $max && $value >= $min)} {\n        if {$detail ne \"\"} {\n            set detail \"(detail: $detail)\"\n        } else {\n            set detail \"(context: [info frame -1])\"\n        }\n        error \"assertion:Expected '$value' to be between to '$min' and '$max' $detail\"\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 caught\"\n    }\n}\n\nproc assert_encoding {enc key} {\n    set dbg [r debug object $key]\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            incr ::num_aborted\n            send_data_packet $::test_server_fd ignore $name\n            return\n        }\n    }\n\n    # abort if test name in skiptests\n    if {[lsearch $::skiptests $name] >= 0} {\n        incr ::num_skipped\n        send_data_packet $::test_server_fd skip $name\n        return\n    }\n\n    # abort if test name in skiptests\n    if {[llength $::only_tests] > 0 && [lsearch $::only_tests $name] < 0} {\n        incr ::num_skipped\n        send_data_packet $::test_server_fd skip $name\n        return\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            incr ::num_aborted\n            send_data_packet $::test_server_fd ignore $name\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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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\nproc wait_for_ofs_sync {r1 r2} {\n    wait_for_condition 50 100 {\n        [status $r1 master_repl_offset] eq [status $r2 master_repl_offset]\n    } else {\n        fail \"replica didn't sync in time\"\n    }\n}\n\nproc wait_for_log_message {srv_idx pattern last_lines maxtries delay} {\n    set retry $maxtries\n    set stdout [srv $srv_idx stdout]\n    while {$retry} {\n        set result [exec tail -$last_lines < $stdout]\n        set result [split $result \"\\n\"]\n        foreach line $result {\n            if {[string match $pattern $line]} {\n                return $line\n            }\n        }\n        incr retry -1\n        after $delay\n    }\n    if {$retry == 0} {\n        fail \"log message of '$pattern' not found\"\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    for {set db 0} {$db < 16} {incr db} {\n        {*}$r select $db\n        foreach k [lsort [{*}$r keys *]] {\n            set type [{*}$r type $k]\n            append o [csvstring $db] , [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    }\n    {*}$r select 9\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 {set fd1 [socket 127.0.0.1 $j]}] &&\n            [catch {set fd2 [socket 127.0.0.1 [expr $j+10000]]}]} {\n            return $j\n        } else {\n            catch {\n                close $fd1\n                close $fd2\n            }\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};49m$str\\033\\[0m\"\n        }\n    } else {\n        return $str\n    }\n}\n\n# Execute a background process writing random data for the specified number\n# of seconds to the specified Redis instance.\nproc start_write_load {host port seconds} {\n    set tclsh [info nameofexecutable]\n    exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds $::tls &\n}\n\n# Stop a process generating write load executed with start_write_load.\nproc stop_write_load {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n\nproc K { x y } { set x } \n\n# Shuffle a list. From Tcl wiki. Originally from Steve Cohen that improved\n# other versions. Code should be under public domain.\nproc lshuffle {list} {\n    set n [llength $list]\n    while {$n>0} {\n        set j [expr {int(rand()*$n)}]\n        lappend slist [lindex $list $j]\n        incr n -1\n        set temp [lindex $list $n]\n        set list [lreplace [K $list [set list {}]] $j $j $temp]\n    }\n    return $slist\n}\n\n# Execute a background process writing complex data for the specified number\n# of ops to the specified Redis instance.\nproc 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 $::tls &\n}\n\n# Stop a process generating write load executed with start_bg_complex_data.\nproc stop_bg_complex_data {handle} {\n    catch {exec /bin/kill -9 $handle}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/test_helper.tcl",
    "content": "# Redis test suite. Copyright (C) 2009 Salvatore Sanfilippo antirez@gmail.com\n# This software 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/keyspace\n    unit/scan\n    unit/type/string\n    unit/type/incr\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/type/stream\n    unit/type/stream-cgroups\n    unit/sort\n    unit/expire\n    unit/other\n    unit/multi\n    unit/quit\n    unit/aofrw\n    unit/acl\n    integration/block-repl\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    integration/logging\n    integration/psync2\n    integration/psync2-reg\n    integration/psync2-pingoff\n    unit/pubsub\n    unit/slowlog\n    unit/scripting\n    unit/maxmemory\n    unit/introspection\n    unit/introspection-2\n    unit/limits\n    unit/obuf-limits\n    unit/bitops\n    unit/bitfield\n    unit/geo\n    unit/memefficiency\n    unit/hyperloglog\n    unit/lazyfree\n    unit/wait\n    unit/pendingquerybuf\n    unit/tls\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 ::tls 0\nset ::stack_logging 0\nset ::verbose 0\nset ::quiet 0\nset ::denytags {}\nset ::skiptests {}\nset ::allowtags {}\nset ::only_tests {}\nset ::single_tests {}\nset ::run_solo_tests {}\nset ::skip_till \"\"\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\nset ::timeout 1200; # 20 minutes without progresses will quit the test.\nset ::last_progress [clock seconds]\nset ::active_servers {} ; # Pids of active Redis instances.\nset ::dont_clean 0\nset ::wait_server 0\nset ::stop_on_failure 0\nset ::loop 0\nset ::tlsdir \"tests/tls\"\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\n# This function is called by one of the test clients when it receives\n# a \"run\" command from the server, with a filename as data.\n# It will run the specified test source file and signal it to the\n# test server when finished.\nproc execute_test_file 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# This function is called by one of the test clients when it receives\n# a \"run_code\" command from the server, with a verbatim test source code\n# as argument, and an associated name.\n# It will run the specified code and signal it to the test server when\n# finished.\nproc execute_test_code {name code} {\n    eval $code\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 0 $::tls]\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 $::tls]\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\n# Test wrapped into run_solo are sent back from the client to the\n# test server, so that the test server will send them again to\n# clients once the clients are idle.\nproc run_solo {name code} {\n    if {$::numclients == 1 || $::loop || $::external} {\n        # run_solo is not supported in these scenarios, just run the code.\n        eval $code\n        return\n    }\n    send_data_packet $::test_server_fd run_solo [list $name $code]\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    if {$::external} {\n        set p [exec $tclsh [info script] {*}$::argv \\\n            --client $port --port $::port &]\n        lappend ::clients_pids $p\n    } else {\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\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.\nproc test_server_cron {} {\n    set elapsed [expr {[clock seconds]-$::last_progress}]\n\n    if {$elapsed > $::timeout} {\n        set err \"\\[[colorstr red TIMEOUT]\\]: clients state report follows.\"\n        puts $err\n        lappend ::failed_tests $err\n        show_clients_state\n        kill_clients\n        force_kill_all_servers\n        the_end\n    }\n\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# skip: a test was skipped by skipfile or individual test options.\n# ignore: a test was skipped by a group tag.\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    set ::last_progress [clock seconds]\n\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) $data\"\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 {skip}} {\n        if {!$::quiet} {\n            puts \"\\[[colorstr yellow $status]\\]: $data\"\n        }\n    } elseif {$status eq {ignore}} {\n        if {!$::quiet} {\n            puts \"\\[[colorstr cyan $status]\\]: $data\"\n        }\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            if {$::stop_on_failure} {\n            puts -nonewline \"(Test stopped, press enter to continue)\"\n            flush stdout\n            gets stdin\n        }\n    } elseif {$status eq {exception}} {\n        puts \"\\[[colorstr red $status]\\]: $data\"\n        kill_clients\n        force_kill_all_servers\n        exit 1\n    } elseif {$status eq {testing}} {\n        set ::active_clients_task($fd) \"(IN PROGRESS) $data\"\n    } elseif {$status eq {server-spawning}} {\n        set ::active_clients_task($fd) \"(SPAWNING SERVER) $data\"\n    } elseif {$status eq {server-spawned}} {\n        lappend ::active_servers $data\n        set ::active_clients_task($fd) \"(SPAWNED SERVER) pid:$data\"\n    } elseif {$status eq {server-killing}} {\n        set ::active_clients_task($fd) \"(KILLING SERVER) pid:$data\"\n    } elseif {$status eq {server-killed}} {\n        set ::active_servers [lsearch -all -inline -not -exact $::active_servers $data]\n        set ::active_clients_task($fd) \"(KILLED SERVER) pid:$data\"\n    } elseif {$status eq {run_solo}} {\n        lappend ::run_solo_tests $data\n    } else {\n        if {!$::quiet} {\n            puts \"\\[$status\\]: $data\"\n        }\n    }\n}\n\nproc show_clients_state {} {\n    # The following loop is only useful for debugging tests that may\n    # enter an infinite loop.\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\nproc kill_clients {} {\n    foreach p $::clients_pids {\n        catch {exec kill $p}\n    }\n}\n\nproc force_kill_all_servers {} {\n    foreach p $::active_servers {\n        puts \"Killing still running Redis server $p\"\n        catch {exec kill -9 $p}\n    }\n}\n\nproc lpop {listVar {count 1}} {\n    upvar 1 $listVar l\n    set ele [lindex $l 0]\n    set l [lrange $l 1 end]\n    set ele\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    # 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        if {$::loop && $::next_test == [llength $::all_tests]} {\n            set ::next_test 0\n        }\n    } elseif {[llength $::run_solo_tests] != 0 && [llength $::active_clients] == 0} {\n        if {!$::quiet} {\n            puts [colorstr bold-white \"Testing solo test\"]\n            set ::active_clients_task($fd) \"ASSIGNED: $fd solo test\"\n        }\n        set ::clients_start_time($fd) [clock seconds]\n        send_data_packet $fd run_code [lpop ::run_solo_tests]\n        lappend ::active_clients $fd\n    } else {\n        lappend ::idle_clients $fd\n        set ::active_clients_task($fd) \"SLEEPING, no more units to assign\"\n        if {[llength $::active_clients] == 0} {\n            the_end\n        }\n    }\n}\n\n# The the_end function 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        if {!$::dont_clean} cleanup\n        exit 1\n    } else {\n        puts \"\\n[colorstr bold-white {\\o/}] [colorstr bold-green {All tests passed without errors!}]\\n\"\n        if {!$::dont_clean} 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_test_file $data\n        } elseif {$cmd eq {run_code}} {\n            foreach {name code} $data break\n            execute_test_code $name $code\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        \"--stack-logging    Enable OSX leaks/malloc stack logging.\"\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). this option can be repeated.\"\n        \"--list-tests       List all the available test units.\"\n        \"--only <test>      Just execute the specified test by test name. this option can be repeated.\"\n        \"--skip-till <unit> Skip all units until (and including) the specified one.\"\n        \"--clients <num>    Number of test clients (default 16).\"\n        \"--timeout <sec>    Test timeout in seconds (default 10 min).\"\n        \"--force-failure    Force the execution of a test that always fails.\"\n        \"--config <k> <v>   Extra config file argument.\"\n        \"--skipfile <file>  Name of a file containing test names that should be skipped (one per line).\"\n        \"--dont-clean       Don't delete redis log files after the run.\"\n        \"--stop             Blocks once the first test fails.\"\n        \"--loop             Execute the specified set of tests forever.\"\n        \"--wait-server      Wait after server is started (so that you can attach a debugger).\"\n        \"--tls              Run tests in TLS mode.\"\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 {--config}} {\n        set arg2 [lindex $argv [expr $j+2]]\n        lappend ::global_overrides $arg\n        lappend ::global_overrides $arg2\n        incr j 2\n    } elseif {$opt eq {--skipfile}} {\n        incr j\n        set fp [open $arg r]\n        set file_data [read $fp]\n        close $fp\n        set ::skiptests [split $file_data \"\\n\"]\n    } elseif {$opt eq {--valgrind}} {\n        set ::valgrind 1\n    } elseif {$opt eq {--stack-logging}} {\n        if {[string match {*Darwin*} [exec uname -a]]} {\n            set ::stack_logging 1\n        }\n    } elseif {$opt eq {--quiet}} {\n        set ::quiet 1\n    } elseif {$opt eq {--tls}} {\n        package require tls 1.6\n        set ::tls 1\n        ::tls::init \\\n            -cafile \"$::tlsdir/ca.crt\" \\\n            -certfile \"$::tlsdir/redis.crt\" \\\n            -keyfile \"$::tlsdir/redis.key\"\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        lappend ::single_tests $arg\n        incr j\n    } elseif {$opt eq {--only}} {\n        lappend ::only_tests $arg\n        incr j\n    } elseif {$opt eq {--skip-till}} {\n        set ::skip_till $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 {--verbose}} {\n        set ::verbose 1\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 {--dont-clean}} {\n        set ::dont_clean 1\n    } elseif {$opt eq {--wait-server}} {\n        set ::wait_server 1\n    } elseif {$opt eq {--stop}} {\n        set ::stop_on_failure 1\n    } elseif {$opt eq {--loop}} {\n        set ::loop 1\n    } elseif {$opt eq {--timeout}} {\n        set ::timeout $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\n# If --skil-till option was given, we populate the list of single tests\n# to run with everything *after* the specified unit.\nif {$::skip_till != \"\"} {\n    set skipping 1\n    foreach t $::all_tests {\n        if {$skipping == 0} {\n            lappend ::single_tests $t\n        }\n        if {$t == $::skip_till} {\n            set skipping 0\n        }\n    }\n    if {$skipping} {\n        puts \"test $::skip_till not found\"\n        exit 0\n    }\n}\n\n# Override the list of tests with the specific tests we want to run\n# in case there was some filter, that is --single or --skip-till options.\nif {[llength $::single_tests] > 0} {\n    set ::all_tests $::single_tests\n}\n\nproc attach_to_replication_stream {} {\n    if {$::tls} {\n        set s [::tls::socket [srv 0 \"host\"] [srv 0 \"port\"]]\n    } else {\n        set s [socket [srv 0 \"host\"] [srv 0 \"port\"]]\n    }\n    fconfigure $s -translation binary\n    puts -nonewline $s \"SYNC\\r\\n\"\n    flush $s\n\n    # Get the count\n    while 1 {\n        set count [gets $s]\n        set prefix [string range $count 0 0]\n        if {$prefix ne {}} break; # Newlines are allowed as PINGs.\n    }\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": "Chapter07/redis-6.0.3/tests/unit/acl.tcl",
    "content": "start_server {tags {\"acl\"}} {\n    test {Connections start with the default user} {\n        r ACL WHOAMI\n    } {default}\n\n    test {It is possible to create new users} {\n        r ACL setuser newuser\n    }\n\n    test {New users start disabled} {\n        r ACL setuser newuser >passwd1\n        catch {r AUTH newuser passwd1} err\n        set err\n    } {*WRONGPASS*}\n\n    test {Enabling the user allows the login} {\n        r ACL setuser newuser on +acl\n        r AUTH newuser passwd1\n        r ACL WHOAMI\n    } {newuser}\n\n    test {Only the set of correct passwords work} {\n        r ACL setuser newuser >passwd2\n        catch {r AUTH newuser passwd1} e\n        assert {$e eq \"OK\"}\n        catch {r AUTH newuser passwd2} e\n        assert {$e eq \"OK\"}\n        catch {r AUTH newuser passwd3} e\n        set e\n    } {*WRONGPASS*}\n\n    test {It is possible to remove passwords from the set of valid ones} {\n        r ACL setuser newuser <passwd1\n        catch {r AUTH newuser passwd1} e\n        set e\n    } {*WRONGPASS*}\n\n    test {Test password hashes can be added} {\n        r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6\n        catch {r AUTH newuser passwd4} e\n        assert {$e eq \"OK\"}\n    }\n\n    test {Test password hashes validate input} {\n        # Validate Length\n        catch {r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e} e\n        # Validate character outside set\n        catch {r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4eq} e\n        set e\n    } {*Error in ACL SETUSER modifier*}\n\n    test {ACL GETUSER returns the password hash instead of the actual password} {\n        set passstr [dict get [r ACL getuser newuser] passwords]\n        assert_match {*34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6*} $passstr\n        assert_no_match {*passwd4*} $passstr\n    }\n\n    test {Test hashed passwords removal} {\n        r ACL setuser newuser !34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6\n        set passstr [dict get [r ACL getuser newuser] passwords]\n        assert_no_match {*34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6*} $passstr\n    }\n\n    test {By default users are not able to access any command} {\n        catch {r SET foo bar} e\n        set e\n    } {*NOPERM*}\n\n    test {By default users are not able to access any key} {\n        r ACL setuser newuser +set\n        catch {r SET foo bar} e\n        set e\n    } {*NOPERM*key*}\n\n    test {It's possible to allow the access of a subset of keys} {\n        r ACL setuser newuser allcommands ~foo:* ~bar:*\n        r SET foo:1 a\n        r SET bar:2 b\n        catch {r SET zap:3 c} e\n        r ACL setuser newuser allkeys; # Undo keys ACL\n        set e\n    } {*NOPERM*key*}\n\n    test {Users can be configured to authenticate with any password} {\n        r ACL setuser newuser nopass\n        r AUTH newuser zipzapblabla\n    } {OK}\n\n    test {ACLs can exclude single commands} {\n        r ACL setuser newuser -ping\n        r INCR mycounter ; # Should not raise an error\n        catch {r PING} e\n        set e\n    } {*NOPERM*}\n\n    test {ACLs can include or exclude whole classes of commands} {\n        r ACL setuser newuser -@all +@set +acl\n        r SADD myset a b c; # Should not raise an error\n        r ACL setuser newuser +@all -@string\n        r SADD myset a b c; # Again should not raise an error\n        # String commands instead should raise an error\n        catch {r SET foo bar} e\n        r ACL setuser newuser allcommands; # Undo commands ACL\n        set e\n    } {*NOPERM*}\n\n    test {ACLs can include single subcommands} {\n        r ACL setuser newuser +@all -client\n        r ACL setuser newuser +client|id +client|setname\n        r CLIENT ID; # Should not fail\n        r CLIENT SETNAME foo ; # Should not fail\n        catch {r CLIENT KILL type master} e\n        set e\n    } {*NOPERM*}\n\n    # Note that the order of the generated ACL rules is not stable in Redis\n    # so we need to match the different parts and not as a whole string.\n    test {ACL GETUSER is able to translate back command permissions} {\n        # Subtractive\n        r ACL setuser newuser reset +@all ~* -@string +incr -debug +debug|digest\n        set cmdstr [dict get [r ACL getuser newuser] commands]\n        assert_match {*+@all*} $cmdstr\n        assert_match {*-@string*} $cmdstr\n        assert_match {*+incr*} $cmdstr\n        assert_match {*-debug +debug|digest**} $cmdstr\n\n        # Additive\n        r ACL setuser newuser reset +@string -incr +acl +debug|digest +debug|segfault\n        set cmdstr [dict get [r ACL getuser newuser] commands]\n        assert_match {*-@all*} $cmdstr\n        assert_match {*+@string*} $cmdstr\n        assert_match {*-incr*} $cmdstr\n        assert_match {*+debug|digest*} $cmdstr\n        assert_match {*+debug|segfault*} $cmdstr\n        assert_match {*+acl*} $cmdstr\n    }\n\n    test {ACL #5998 regression: memory leaks adding / removing subcommands} {\n        r AUTH default \"\"\n        r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c\n        r ACL setuser newuser -debug\n        # The test framework will detect a leak if any.\n    }\n\n    test {ACL LOG shows failed command executions at toplevel} {\n        r ACL LOG RESET\n        r ACL setuser antirez >foo on +set ~object:1234\n        r ACL setuser antirez +eval +multi +exec\n        r AUTH antirez foo\n        catch {r GET foo}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry username] eq {antirez}}\n        assert {[dict get $entry context] eq {toplevel}}\n        assert {[dict get $entry reason] eq {command}}\n        assert {[dict get $entry object] eq {get}}\n    }\n\n    test {ACL LOG is able to test similar events} {\n        r AUTH antirez foo\n        catch {r GET foo}\n        catch {r GET foo}\n        catch {r GET foo}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry count] == 4}\n    }\n\n    test {ACL LOG is able to log keys access violations and key name} {\n        r AUTH antirez foo\n        catch {r SET somekeynotallowed 1234}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry reason] eq {key}}\n        assert {[dict get $entry object] eq {somekeynotallowed}}\n    }\n\n    test {ACL LOG RESET is able to flush the entries in the log} {\n        r ACL LOG RESET\n        assert {[llength [r ACL LOG]] == 0}\n    }\n\n    test {ACL LOG can distinguish the transaction context (1)} {\n        r AUTH antirez foo\n        r MULTI\n        catch {r INCR foo}\n        catch {r EXEC}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {multi}}\n        assert {[dict get $entry object] eq {incr}}\n    }\n\n    test {ACL LOG can distinguish the transaction context (2)} {\n        set rd1 [redis_deferring_client]\n        r ACL SETUSER antirez +incr\n\n        r AUTH antirez foo\n        r MULTI\n        r INCR object:1234\n        $rd1 ACL SETUSER antirez -incr\n        $rd1 read\n        catch {r EXEC}\n        $rd1 close\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {multi}}\n        assert {[dict get $entry object] eq {incr}}\n        r ACL SETUSER antirez -incr\n    }\n\n    test {ACL can log errors in the context of Lua scripting} {\n        r AUTH antirez foo\n        catch {r EVAL {redis.call('incr','foo')} 0}\n        r AUTH default \"\"\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {lua}}\n        assert {[dict get $entry object] eq {incr}}\n    }\n\n    test {ACL LOG can accept a numerical argument to show less entries} {\n        r AUTH antirez foo\n        catch {r INCR foo}\n        catch {r INCR foo}\n        catch {r INCR foo}\n        catch {r INCR foo}\n        r AUTH default \"\"\n        assert {[llength [r ACL LOG]] > 1}\n        assert {[llength [r ACL LOG 2]] == 2}\n    }\n\n    test {ACL LOG can log failed auth attempts} {\n        catch {r AUTH antirez wrong-password}\n        set entry [lindex [r ACL LOG] 0]\n        assert {[dict get $entry context] eq {toplevel}}\n        assert {[dict get $entry reason] eq {auth}}\n        assert {[dict get $entry object] eq {AUTH}}\n        assert {[dict get $entry username] eq {antirez}}\n    }\n\n    test {ACL LOG entries are limited to a maximum amount} {\n        r ACL LOG RESET\n        r CONFIG SET acllog-max-len 5\n        r AUTH antirez foo\n        for {set j 0} {$j < 10} {incr j} {\n            catch {r SET obj:$j 123}\n        }\n        r AUTH default \"\"\n        assert {[llength [r ACL LOG]] == 5}\n    }\n\n    test {When default user is off, new connections are not authenticated} {\n        r ACL setuser default off\n        catch {set rd1 [redis_deferring_client]} e\n        r ACL setuser default on\n        set e\n    } {*NOAUTH*}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/aofrw.tcl",
    "content": "start_server {tags {\"aofrw\"}} {\n    # Enable the AOF\n    r config set appendonly yes\n    r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.\n    waitForBgrewriteaof r\n\n    foreach rdbpre {yes no} {\n        r config set aof-use-rdb-preamble $rdbpre\n        test \"AOF rewrite during write load: RDB preamble=$rdbpre\" {\n            # Start a write load for 10 seconds\n            set master [srv 0 client]\n            set master_host [srv 0 host]\n            set master_port [srv 0 port]\n            set load_handle0 [start_write_load $master_host $master_port 10]\n            set load_handle1 [start_write_load $master_host $master_port 10]\n            set load_handle2 [start_write_load $master_host $master_port 10]\n            set load_handle3 [start_write_load $master_host $master_port 10]\n            set load_handle4 [start_write_load $master_host $master_port 10]\n\n            # Make sure the instance is really receiving data\n            wait_for_condition 50 100 {\n                [r dbsize] > 0\n            } else {\n                fail \"No write load detected.\"\n            }\n\n            # After 3 seconds, start a rewrite, while the write load is still\n            # active.\n            after 3000\n            r bgrewriteaof\n            waitForBgrewriteaof r\n\n            # Let it run a bit more so that we'll append some data to the new\n            # AOF.\n            after 1000\n\n            # Stop the processes generating the load if they are still active\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            # Make sure that we remain the only connected client.\n            # This step is needed to make sure there are no pending writes\n            # that will be processed between the two \"debug digest\" calls.\n            wait_for_condition 50 100 {\n                [llength [split [string trim [r client list]] \"\\n\"]] == 1\n            } else {\n                puts [r client list]\n                fail \"Clients generating loads are not disconnecting\"\n            }\n\n            # Get the data set digest\n            set d1 [r debug digest]\n\n            # Load the AOF\n            r debug loadaof\n            set d2 [r debug digest]\n\n            # Make sure they are the same\n            assert {$d1 eq $d2}\n        }\n    }\n}\n\nstart_server {tags {\"aofrw\"} overrides {aof-use-rdb-preamble no}} {\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 -5 < [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 {quicklist} {\n            test \"AOF rewrite of list with $e encoding, $d data\" {\n                r flushall\n                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": "Chapter07/redis-6.0.3/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*any 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    } {WRONGPASS*}\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": "Chapter07/redis-6.0.3/tests/unit/bitfield.tcl",
    "content": "start_server {tags {\"bitops\"}} {\n    test {BITFIELD signed SET and GET basics} {\n        r del bits\n        set results {}\n        lappend results [r bitfield bits set i8 0 -100]\n        lappend results [r bitfield bits set i8 0 101]\n        lappend results [r bitfield bits get i8 0]\n        set results\n    } {0 -100 101}\n\n    test {BITFIELD unsigned SET and GET basics} {\n        r del bits\n        set results {}\n        lappend results [r bitfield bits set u8 0 255]\n        lappend results [r bitfield bits set u8 0 100]\n        lappend results [r bitfield bits get u8 0]\n        set results\n    } {0 255 100}\n\n    test {BITFIELD #<idx> form} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 65\n        r bitfield bits set u8 #1 66\n        r bitfield bits set u8 #2 67\n        r get bits\n    } {ABC}\n\n    test {BITFIELD basic INCRBY form} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 10\n        lappend results [r bitfield bits incrby u8 #0 100]\n        lappend results [r bitfield bits incrby u8 #0 100]\n        set results\n    } {110 210}\n\n    test {BITFIELD chaining of multiple commands} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 10\n        lappend results [r bitfield bits incrby u8 #0 100 incrby u8 #0 100]\n        set results\n    } {{110 210}}\n\n    test {BITFIELD unsigned overflow wrap} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 100\n        lappend results [r bitfield bits overflow wrap incrby u8 #0 257]\n        lappend results [r bitfield bits get u8 #0]\n        lappend results [r bitfield bits overflow wrap incrby u8 #0 255]\n        lappend results [r bitfield bits get u8 #0]\n    } {101 101 100 100}\n\n    test {BITFIELD unsigned overflow sat} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 100\n        lappend results [r bitfield bits overflow sat incrby u8 #0 257]\n        lappend results [r bitfield bits get u8 #0]\n        lappend results [r bitfield bits overflow sat incrby u8 #0 -255]\n        lappend results [r bitfield bits get u8 #0]\n    } {255 255 0 0}\n\n    test {BITFIELD signed overflow wrap} {\n        r del bits\n        set results {}\n        r bitfield bits set i8 #0 100\n        lappend results [r bitfield bits overflow wrap incrby i8 #0 257]\n        lappend results [r bitfield bits get i8 #0]\n        lappend results [r bitfield bits overflow wrap incrby i8 #0 255]\n        lappend results [r bitfield bits get i8 #0]\n    } {101 101 100 100}\n\n    test {BITFIELD signed overflow sat} {\n        r del bits\n        set results {}\n        r bitfield bits set u8 #0 100\n        lappend results [r bitfield bits overflow sat incrby i8 #0 257]\n        lappend results [r bitfield bits get i8 #0]\n        lappend results [r bitfield bits overflow sat incrby i8 #0 -255]\n        lappend results [r bitfield bits get i8 #0]\n    } {127 127 -128 -128}\n\n    test {BITFIELD overflow detection fuzzing} {\n        for {set j 0} {$j < 1000} {incr j} {\n            set bits [expr {[randomInt 64]+1}]\n            set sign [randomInt 2]\n            set range [expr {2**$bits}]\n            if {$bits == 64} {set sign 1} ; # u64 is not supported by BITFIELD.\n            if {$sign} {\n                set min [expr {-($range/2)}]\n                set type \"i$bits\"\n            } else {\n                set min 0\n                set type \"u$bits\"\n            }\n            set max [expr {$min+$range-1}]\n\n            # Compare Tcl vs Redis\n            set range2 [expr {$range*2}]\n            set value [expr {($min*2)+[randomInt $range2]}]\n            set increment [expr {($min*2)+[randomInt $range2]}]\n            if {$value > 9223372036854775807} {\n                set value 9223372036854775807\n            }\n            if {$value < -9223372036854775808} {\n                set value -9223372036854775808\n            }\n            if {$increment > 9223372036854775807} {\n                set increment 9223372036854775807\n            }\n            if {$increment < -9223372036854775808} {\n                set increment -9223372036854775808\n            }\n\n            set overflow 0\n            if {$value > $max || $value < $min} {set overflow 1}\n            if {($value + $increment) > $max} {set overflow 1}\n            if {($value + $increment) < $min} {set overflow 1}\n\n            r del bits\n            set res1 [r bitfield bits overflow fail set $type 0 $value]\n            set res2 [r bitfield bits overflow fail incrby $type 0 $increment]\n\n            if {$overflow && [lindex $res1 0] ne {} &&\n                             [lindex $res2 0] ne {}} {\n                fail \"OW not detected where needed: $type $value+$increment\"\n            }\n            if {!$overflow && ([lindex $res1 0] eq {} ||\n                               [lindex $res2 0] eq {})} {\n                fail \"OW detected where NOT needed: $type $value+$increment\"\n            }\n        }\n    }\n\n    test {BITFIELD overflow wrap fuzzing} {\n        for {set j 0} {$j < 1000} {incr j} {\n            set bits [expr {[randomInt 64]+1}]\n            set sign [randomInt 2]\n            set range [expr {2**$bits}]\n            if {$bits == 64} {set sign 1} ; # u64 is not supported by BITFIELD.\n            if {$sign} {\n                set min [expr {-($range/2)}]\n                set type \"i$bits\"\n            } else {\n                set min 0\n                set type \"u$bits\"\n            }\n            set max [expr {$min+$range-1}]\n\n            # Compare Tcl vs Redis\n            set range2 [expr {$range*2}]\n            set value [expr {($min*2)+[randomInt $range2]}]\n            set increment [expr {($min*2)+[randomInt $range2]}]\n            if {$value > 9223372036854775807} {\n                set value 9223372036854775807\n            }\n            if {$value < -9223372036854775808} {\n                set value -9223372036854775808\n            }\n            if {$increment > 9223372036854775807} {\n                set increment 9223372036854775807\n            }\n            if {$increment < -9223372036854775808} {\n                set increment -9223372036854775808\n            }\n\n            r del bits\n            r bitfield bits overflow wrap set $type 0 $value\n            r bitfield bits overflow wrap incrby $type 0 $increment\n            set res [lindex [r bitfield bits get $type 0] 0]\n\n            set expected 0\n            if {$sign} {incr expected [expr {$max+1}]}\n            incr expected $value\n            incr expected $increment\n            set expected [expr {$expected % $range}]\n            if {$sign} {incr expected $min}\n\n            if {$res != $expected} {\n                fail \"WRAP error: $type $value+$increment = $res, should be $expected\"\n            }\n        }\n    }\n\n    test {BITFIELD regression for #3221} {\n        r set bits 1\n        r bitfield bits get u1 0\n    } {0}\n\n    test {BITFIELD regression for #3564} {\n        for {set j 0} {$j < 10} {incr j} {\n            r del mystring\n            set res [r BITFIELD mystring SET i8 0 10 SET i8 64 10 INCRBY i8 10 99900]\n            assert {$res eq {0 0 60}}\n        }\n        r del mystring\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 {BITFIELD: setup slave} {\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 {BITFIELD: write on master, read on slave} {\n            $master del bits\n            assert_equal 0 [$master bitfield bits set u8 0 255]\n            assert_equal 255 [$master bitfield bits set u8 0 100]\n            wait_for_ofs_sync $master $slave\n            assert_equal 100 [$slave bitfield_ro bits get u8 0]\n        }\n\n        test {BITFIELD_RO fails when write option is used} {\n            catch {$slave bitfield_ro bits set u8 0 100 get u8 0} err\n            assert_match {*ERR BITFIELD_RO only supports the GET subcommand*} $err\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/bitops.tcl",
    "content": "# Compare Redis commands 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    test {BITCOUNT returns 0 with out of range indexes} {\n        r set str \"xxxx\"\n        r bitcount str 4 10\n    } 0\n\n    test {BITCOUNT returns 0 with negative indexes where start > end} {\n        r set str \"xxxx\"\n        r bitcount str -6 -7\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 foo\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": "Chapter07/redis-6.0.3/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 can set an absolute expire} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        set now [clock milliseconds]\n        r restore foo [expr $now+3000] $encoded absttl\n        set ttl [r pttl foo]\n        assert {$ttl >= 2900 && $ttl <= 3100}\n        r get foo\n    } {bar}\n    \n    test {RESTORE can set LRU} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        r config set maxmemory-policy allkeys-lru\n        r restore foo 0 $encoded idletime 1000\n        set idle [r object idletime foo]\n        assert {$idle >= 1000 && $idle <= 1010}\n        r get foo\n    } {bar}\n    \n    test {RESTORE can set LFU} {\n        r set foo bar\n        set encoded [r dump foo]\n        r del foo\n        r config set maxmemory-policy allkeys-lfu\n        r restore foo 0 $encoded freq 100\n        set freq [r object freq foo]\n        assert {$freq == 100}\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 < 40000} {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] == 40000*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    test {MIGRATE can migrate multiple keys at once} {\n        set first [srv 0 client]\n        r set key1 \"v1\"\n        r set key2 \"v2\"\n        r set key3 \"v3\"\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 key1] == 1}\n            assert {[$second exists key1] == 0}\n            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys key1 key2 key3]\n            assert {$ret eq {OK}}\n            assert {[$first exists key1] == 0}\n            assert {[$first exists key2] == 0}\n            assert {[$first exists key3] == 0}\n            assert {[$second get key1] eq {v1}}\n            assert {[$second get key2] eq {v2}}\n            assert {[$second get key3] eq {v3}}\n        }\n    }\n\n    test {MIGRATE with multiple keys must have empty key arg} {\n        catch {r MIGRATE 127.0.0.1 6379 NotEmpty 9 5000 keys a b c} e\n        set e\n    } {*empty string*}\n\n    test {MIGRATE with multiple keys migrate just existing ones} {\n        set first [srv 0 client]\n        r set key1 \"v1\"\n        r set key2 \"v2\"\n        r set key3 \"v3\"\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            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys nokey-1 nokey-2 nokey-2]\n            assert {$ret eq {NOKEY}}\n\n            assert {[$first exists key1] == 1}\n            assert {[$second exists key1] == 0}\n            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys nokey-1 key1 nokey-2 key2 nokey-3 key3]\n            assert {$ret eq {OK}}\n            assert {[$first exists key1] == 0}\n            assert {[$first exists key2] == 0}\n            assert {[$first exists key3] == 0}\n            assert {[$second get key1] eq {v1}}\n            assert {[$second get key2] eq {v2}}\n            assert {[$second get key3] eq {v3}}\n        }\n    }\n\n    test {MIGRATE with multiple keys: stress command rewriting} {\n        set first [srv 0 client]\n        r flushdb\n        r mset a 1 b 2 c 3 d 4 c 5 e 6 f 7 g 8 h 9 i 10 l 11 m 12 n 13 o 14 p 15 q 16\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            set ret [r -1 migrate $second_host $second_port \"\" 9 5000 keys a b c d e f g h i l m n o p q]\n\n            assert {[$first dbsize] == 0}\n            assert {[$second dbsize] == 15}\n        }\n    }\n\n    test {MIGRATE with multiple keys: delete just ack keys} {\n        set first [srv 0 client]\n        r flushdb\n        r mset a 1 b 2 c 3 d 4 c 5 e 6 f 7 g 8 h 9 i 10 l 11 m 12 n 13 o 14 p 15 q 16\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            $second mset c _ d _; # Two busy keys and no REPLACE used\n\n            catch {r -1 migrate $second_host $second_port \"\" 9 5000 keys a b c d e f g h i l m n o p q} e\n\n            assert {[$first dbsize] == 2}\n            assert {[$second dbsize] == 15}\n            assert {[$first exists c] == 1}\n            assert {[$first exists d] == 1}\n        }\n    }\n\n    test {MIGRATE AUTH: correct and wrong password cases} {\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            $second config set requirepass foobar\n            $second auth foobar\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 AUTH foobar]\n            assert {$ret eq {OK}}\n            assert {[$second exists list] == 1}\n            assert {[$second lrange list 0 -1] eq {d c b a}}\n\n            r -1 lpush list a b c d\n            $second config set requirepass foobar2\n            catch {r -1 migrate $second_host $second_port list 9 5000 AUTH foobar} err\n            assert_match {*WRONGPASS*} $err\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 time 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    test {EXPIRE with empty string as TTL should report an error} {\n        r set foo bar\n        catch {r expire foo \"\"} e\n        set e\n    } {*not an integer*}\n\n    test {SET - use EX/PX option, TTL should not be reseted after loadaof} {\n        r config set appendonly yes\n        r set foo bar EX 100\n        after 2000\n        r debug loadaof\n        set ttl [r ttl foo]\n        assert {$ttl <= 98 && $ttl > 90}\n\n        r set foo bar PX 100000\n        after 2000\n        r debug loadaof\n        set ttl [r ttl foo]\n        assert {$ttl <= 98 && $ttl > 90}\n    }\n\n    test {SET command will remove expire} {\n        r set foo bar EX 100\n        r set foo bar\n        r ttl foo\n    } {-1}\n\n    test {SET - use KEEPTTL option, TTL should not be removed} {\n        r set foo bar EX 100\n        r set foo bar KEEPTTL\n        set ttl [r ttl foo]\n        assert {$ttl <= 100 && $ttl > 90}\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/geo.tcl",
    "content": "# Helper functions to simulate search-in-radius in the Tcl side in order to\n# verify the Redis implementation with a fuzzy test.\nproc geo_degrad deg {expr {$deg*atan(1)*8/360}}\n\nproc geo_distance {lon1d lat1d lon2d lat2d} {\n    set lon1r [geo_degrad $lon1d]\n    set lat1r [geo_degrad $lat1d]\n    set lon2r [geo_degrad $lon2d]\n    set lat2r [geo_degrad $lat2d]\n    set v [expr {sin(($lon2r - $lon1r) / 2)}]\n    set u [expr {sin(($lat2r - $lat1r) / 2)}]\n    expr {2.0 * 6372797.560856 * \\\n            asin(sqrt($u * $u + cos($lat1r) * cos($lat2r) * $v * $v))}\n}\n\nproc geo_random_point {lonvar latvar} {\n    upvar 1 $lonvar lon\n    upvar 1 $latvar lat\n    # Note that the actual latitude limit should be -85 to +85, we restrict\n    # the test to -70 to +70 since in this range the algorithm is more precise\n    # while outside this range occasionally some element may be missing.\n    set lon [expr {-180 + rand()*360}]\n    set lat [expr {-70 + rand()*140}]\n}\n\n# Return elements non common to both the lists.\n# This code is from http://wiki.tcl.tk/15489\nproc compare_lists {List1 List2} {\n   set DiffList {}\n   foreach Item $List1 {\n      if {[lsearch -exact $List2 $Item] == -1} {\n         lappend DiffList $Item\n      }\n   }\n   foreach Item $List2 {\n      if {[lsearch -exact $List1 $Item] == -1} {\n         if {[lsearch -exact $DiffList $Item] == -1} {\n            lappend DiffList $Item\n         }\n      }\n   }\n   return $DiffList\n}\n\n# The following list represents sets of random seed, search position\n# and radius that caused bugs in the past. It is used by the randomized\n# test later as a starting point. When the regression vectors are scanned\n# the code reverts to using random data.\n#\n# The format is: seed km lon lat\nset regression_vectors {\n    {1482225976969 7083 81.634948934258375 30.561509253718668}\n    {1482340074151 5416 -70.863281847379767 -46.347003465679947}\n    {1499014685896 6064 -89.818768962202014 -40.463868561416803}\n    {1412 156 149.29737817929004 15.95807862745508}\n    {441574 143 59.235461856813856 66.269555127373678}\n    {160645 187 -101.88575239939883 49.061997951502917}\n    {750269 154 -90.187939661642517 66.615930412251487}\n    {342880 145 163.03472387745728 64.012747720821181}\n    {729955 143 137.86663517256579 63.986745399416776}\n    {939895 151 59.149620271823181 65.204186651485145}\n    {1412 156 149.29737817929004 15.95807862745508}\n    {564862 149 84.062063109158544 -65.685403922426232}\n    {1546032440391 16751 -1.8175081637769495 20.665668878082954}\n}\nset rv_idx 0\n\nstart_server {tags {\"geo\"}} {\n    test {GEOADD create} {\n        r geoadd nyc -73.9454966 40.747533 \"lic market\"\n    } {1}\n\n    test {GEOADD update} {\n        r geoadd nyc -73.9454966 40.747533 \"lic market\"\n    } {0}\n\n    test {GEOADD invalid coordinates} {\n        catch {\n            r geoadd nyc -73.9454966 40.747533 \"lic market\" \\\n                foo bar \"luck market\"\n        } err\n        set err\n    } {*valid*}\n\n    test {GEOADD multi add} {\n        r geoadd nyc -73.9733487 40.7648057 \"central park n/q/r\" -73.9903085 40.7362513 \"union square\" -74.0131604 40.7126674 \"wtc one\" -73.7858139 40.6428986 \"jfk\" -73.9375699 40.7498929 \"q4\" -73.9564142 40.7480973 4545\n    } {6}\n\n    test {Check geoset values} {\n        r zrange nyc 0 -1 withscores\n    } {{wtc one} 1791873972053020 {union square} 1791875485187452 {central park n/q/r} 1791875761332224 4545 1791875796750882 {lic market} 1791875804419201 q4 1791875830079666 jfk 1791895905559723}\n\n    test {GEORADIUS simple (sorted)} {\n        r georadius nyc -73.9798091 40.7598464 3 km asc\n    } {{central park n/q/r} 4545 {union square}}\n\n    test {GEORADIUS withdist (sorted)} {\n        r georadius nyc -73.9798091 40.7598464 3 km withdist asc\n    } {{{central park n/q/r} 0.7750} {4545 2.3651} {{union square} 2.7697}}\n\n    test {GEORADIUS with COUNT} {\n        r georadius nyc -73.9798091 40.7598464 10 km COUNT 3\n    } {{central park n/q/r} 4545 {union square}}\n\n    test {GEORADIUS with COUNT but missing integer argument} {\n        catch {r georadius nyc -73.9798091 40.7598464 10 km COUNT} e\n        set e\n    } {ERR*syntax*}\n\n    test {GEORADIUS with COUNT DESC} {\n        r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC\n    } {{wtc one} q4}\n\n    test {GEORADIUS HUGE, issue #2767} {\n        r geoadd users -47.271613776683807 -54.534504198047678 user_000000\n        llength [r GEORADIUS users 0 0 50000 km WITHCOORD]\n    } {1}\n\n    test {GEORADIUSBYMEMBER simple (sorted)} {\n        r georadiusbymember nyc \"wtc one\" 7 km\n    } {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}\n\n    test {GEORADIUSBYMEMBER withdist (sorted)} {\n        r georadiusbymember nyc \"wtc one\" 7 km withdist\n    } {{{wtc one} 0.0000} {{union square} 3.2544} {{central park n/q/r} 6.7000} {4545 6.1975} {{lic market} 6.8969}}\n\n    test {GEOHASH is able to return geohash strings} {\n        # Example from Wikipedia.\n        r del points\n        r geoadd points -5.6 42.6 test\n        lindex [r geohash points test] 0\n    } {ezs42e44yx0}\n\n    test {GEOPOS simple} {\n        r del points\n        r geoadd points 10 20 a 30 40 b\n        lassign [lindex [r geopos points a b] 0] x1 y1\n        lassign [lindex [r geopos points a b] 1] x2 y2\n        assert {abs($x1 - 10) < 0.001}\n        assert {abs($y1 - 20) < 0.001}\n        assert {abs($x2 - 30) < 0.001}\n        assert {abs($y2 - 40) < 0.001}\n    }\n\n    test {GEOPOS missing element} {\n        r del points\n        r geoadd points 10 20 a 30 40 b\n        lindex [r geopos points a x b] 1\n    } {}\n\n    test {GEODIST simple & unit} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        set m [r geodist points Palermo Catania]\n        assert {$m > 166274 && $m < 166275}\n        set km [r geodist points Palermo Catania km]\n        assert {$km > 166.2 && $km < 166.3}\n    }\n\n    test {GEODIST missing elements} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        set m [r geodist points Palermo Agrigento]\n        assert {$m eq {}}\n        set m [r geodist points Ragusa Agrigento]\n        assert {$m eq {}}\n        set m [r geodist empty_key Palermo Catania]\n        assert {$m eq {}}\n    }\n\n    test {GEORADIUS STORE option: syntax error} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        catch {r georadius points 13.361389 38.115556 50 km store} e\n        set e\n    } {*ERR*syntax*}\n\n    test {GEORANGE STORE option: incompatible options} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        catch {r georadius points 13.361389 38.115556 50 km store points2 withdist} e\n        assert_match {*ERR*} $e\n        catch {r georadius points 13.361389 38.115556 50 km store points2 withhash} e\n        assert_match {*ERR*} $e\n        catch {r georadius points 13.361389 38.115556 50 km store points2 withcoords} e\n        assert_match {*ERR*} $e\n    }\n\n    test {GEORANGE STORE option: plain usage} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        r georadius points 13.361389 38.115556 500 km store points2\n        assert_equal [r zrange points 0 -1] [r zrange points2 0 -1]\n    }\n\n    test {GEORANGE STOREDIST option: plain usage} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        r georadius points 13.361389 38.115556 500 km storedist points2\n        set res [r zrange points2 0 -1 withscores]\n        assert {[lindex $res 1] < 1}\n        assert {[lindex $res 3] > 166}\n        assert {[lindex $res 3] < 167}\n    }\n\n    test {GEORANGE STOREDIST option: COUNT ASC and DESC} {\n        r del points\n        r geoadd points 13.361389 38.115556 \"Palermo\" \\\n                        15.087269 37.502669 \"Catania\"\n        r georadius points 13.361389 38.115556 500 km storedist points2 asc count 1\n        assert {[r zcard points2] == 1}\n        set res [r zrange points2 0 -1 withscores]\n        assert {[lindex $res 0] eq \"Palermo\"}\n\n        r georadius points 13.361389 38.115556 500 km storedist points2 desc count 1\n        assert {[r zcard points2] == 1}\n        set res [r zrange points2 0 -1 withscores]\n        assert {[lindex $res 0] eq \"Catania\"}\n    }\n\n    test {GEOADD + GEORANGE randomized test} {\n        set attempt 30\n        while {[incr attempt -1]} {\n            set rv [lindex $regression_vectors $rv_idx]\n            incr rv_idx\n\n            unset -nocomplain debuginfo\n            set srand_seed [clock milliseconds]\n            if {$rv ne {}} {set srand_seed [lindex $rv 0]}\n            lappend debuginfo \"srand_seed is $srand_seed\"\n            expr {srand($srand_seed)} ; # If you need a reproducible run\n            r del mypoints\n\n            if {[randomInt 10] == 0} {\n                # From time to time use very big radiuses\n                set radius_km [expr {[randomInt 50000]+10}]\n            } else {\n                # Normally use a few - ~200km radiuses to stress\n                # test the code the most in edge cases.\n                set radius_km [expr {[randomInt 200]+10}]\n            }\n            if {$rv ne {}} {set radius_km [lindex $rv 1]}\n            set radius_m [expr {$radius_km*1000}]\n            geo_random_point search_lon search_lat\n            if {$rv ne {}} {\n                set search_lon [lindex $rv 2]\n                set search_lat [lindex $rv 3]\n            }\n            lappend debuginfo \"Search area: $search_lon,$search_lat $radius_km km\"\n            set tcl_result {}\n            set argv {}\n            for {set j 0} {$j < 20000} {incr j} {\n                geo_random_point lon lat\n                lappend argv $lon $lat \"place:$j\"\n                set distance [geo_distance $lon $lat $search_lon $search_lat]\n                if {$distance < $radius_m} {\n                    lappend tcl_result \"place:$j\"\n                }\n                lappend debuginfo \"place:$j $lon $lat [expr {$distance/1000}] km\"\n            }\n            r geoadd mypoints {*}$argv\n            set res [lsort [r georadius mypoints $search_lon $search_lat $radius_km km]]\n            set res2 [lsort $tcl_result]\n            set test_result OK\n\n            if {$res != $res2} {\n                set rounding_errors 0\n                set diff [compare_lists $res $res2]\n                foreach place $diff {\n                    set mydist [geo_distance $lon $lat $search_lon $search_lat]\n                    set mydist [expr $mydist/1000]\n                    if {($mydist / $radius_km) > 0.999} {\n                        incr rounding_errors\n                        continue\n                    }\n                    if {$mydist < $radius_m} {\n                        # This is a false positive for redis since given the \n                        # same points the higher precision calculation provided \n                        # by TCL shows the point within range\n                        incr rounding_errors\n                        continue\n                    }\n                }\n\n                # Make sure this is a real error and not a rounidng issue.\n                if {[llength $diff] == $rounding_errors} {\n                    set res $res2; # Error silenced\n                }\n            }\n\n            if {$res != $res2} {\n                set diff [compare_lists $res $res2]\n                puts \"*** Possible problem in GEO radius query ***\"\n                puts \"Redis: $res\"\n                puts \"Tcl  : $res2\"\n                puts \"Diff : $diff\"\n                puts [join $debuginfo \"\\n\"]\n                foreach place $diff {\n                    if {[lsearch -exact $res2 $place] != -1} {\n                        set where \"(only in Tcl)\"\n                    } else {\n                        set where \"(only in Redis)\"\n                    }\n                    lassign [lindex [r geopos mypoints $place] 0] lon lat\n                    set mydist [geo_distance $lon $lat $search_lon $search_lat]\n                    set mydist [expr $mydist/1000]\n                    puts \"$place -> [r geopos mypoints $place] $mydist $where\"\n                    if {($mydist / $radius_km) > 0.999} {incr rounding_errors}\n                }\n                set test_result FAIL\n            }\n            unset -nocomplain debuginfo\n            if {$test_result ne {OK}} break\n        }\n        set test_result\n    } {OK}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 {Fuzzing dense/sparse encoding: Redis should always detect errors} {\n        for {set j 0} {$j < 1000} {incr j} {\n            r del hll\n            set items {}\n            set numitems [randomInt 3000]\n            for {set i 0} {$i < $numitems} {incr i} {\n                lappend items [expr {rand()}]\n            }\n            r pfadd hll {*}$items\n\n            # Corrupt it in some random way.\n            for {set i 0} {$i < 5} {incr i} {\n                set len [r strlen hll]\n                set pos [randomInt $len]\n                set byte [randstring 1 1 binary]\n                r setrange hll $pos $byte\n                # Don't modify more bytes 50% of times\n                if {rand() < 0.5} break\n            }\n\n            # Use the hyperloglog to check if it crashes\n            # Redis in some way.\n            catch {\n                r pfcount hll\n            }\n        }\n    }\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 #1} {\n        r del hll1 hll2 hll3\n        for {set x 1} {$x < 10000} {incr x} {\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 {PFCOUNT multiple-keys merge returns cardinality of union #2} {\n        r del hll1 hll2 hll3\n        set elements {}\n        for {set x 1} {$x < 10000} {incr x} {\n            for {set j 1} {$j <= 3} {incr j} {\n                set rint [randomInt 20000]\n                r pfadd hll$j $rint\n                lappend elements $rint\n            }\n        }\n        set realcard [llength [lsort -unique $elements]]\n        set card [r pfcount hll1 hll2 hll3]\n        set err [expr {abs($card-$realcard)}]\n        assert {$err < (double($card)/100)*5}\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    test {PFADD / PFCOUNT cache invalidation works} {\n        r del hll\n        r pfadd hll a b c\n        r pfcount hll\n        assert {[r getrange hll 15 15] eq \"\\x00\"}\n        r pfadd hll a b c\n        assert {[r getrange hll 15 15] eq \"\\x00\"}\n        r pfadd hll 1 2 3\n        assert {[r getrange hll 15 15] eq \"\\x80\"}\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/introspection-2.tcl",
    "content": "proc cmdstat {cmd} {\n    if {[regexp \"\\r\\ncmdstat_$cmd:(.*?)\\r\\n\" [r info commandstats] _ value]} {\n        set _ $value\n    }\n}\n\nstart_server {tags {\"introspection\"}} {\n    test {TTL and TYPYE do not alter the last access time of a key} {\n        r set foo bar\n        after 3000\n        r ttl foo\n        r type foo\n        assert {[r object idletime foo] >= 2}\n    }\n\n    test {TOUCH alters the last access time of a key} {\n        r set foo bar\n        after 3000\n        r touch foo\n        assert {[r object idletime foo] < 2}\n    }\n\n    test {TOUCH returns the number of existing keys specified} {\n        r flushdb\n        r set key1 1\n        r set key2 2\n        r touch key0 key1 key2 key3\n    } 2\n\n    test {command stats for GEOADD} {\n        r config resetstat\n        r GEOADD foo 0 0 bar\n        assert_match {*calls=1,*} [cmdstat geoadd]\n        assert_match {} [cmdstat zadd]\n    }\n\n    test {command stats for EXPIRE} {\n        r config resetstat\n        r SET foo bar\n        r EXPIRE foo 0\n        assert_match {*calls=1,*} [cmdstat expire]\n        assert_match {} [cmdstat del]\n    }\n\n    test {command stats for BRPOP} {\n        r config resetstat\n        r LPUSH list foo\n        r BRPOP list 0\n        assert_match {*calls=1,*} [cmdstat brpop]\n        assert_match {} [cmdstat rpop]\n    }\n\n    test {command stats for MULTI} {\n        r config resetstat\n        r MULTI\n        r set foo bar\n        r GEOADD foo2 0 0 bar\n        r EXPIRE foo2 0\n        r EXEC\n        assert_match {*calls=1,*} [cmdstat multi]\n        assert_match {*calls=1,*} [cmdstat exec]\n        assert_match {*calls=1,*} [cmdstat set]\n        assert_match {*calls=1,*} [cmdstat expire]\n        assert_match {*calls=1,*} [cmdstat geoadd]\n    }\n\n    test {command stats for scripts} {\n        r config resetstat\n        r set mykey myval\n        r eval {\n            redis.call('set', KEYS[1], 0)\n            redis.call('expire', KEYS[1], 0)\n            redis.call('geoadd', KEYS[1], 0, 0, \"bar\")\n        } 1 mykey\n        assert_match {*calls=1,*} [cmdstat eval]\n        assert_match {*calls=2,*} [cmdstat set]\n        assert_match {*calls=1,*} [cmdstat expire]\n        assert_match {*calls=1,*} [cmdstat geoadd]\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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=26 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        assert_match {*OK*} [$rd read]\n        r set foo bar\n        r get foo\n        list [$rd read] [$rd read]\n    } {*\"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        $rd read ;# Discard the OK\n        r eval {redis.call('set',KEYS[1],ARGV[1])} 1 foo bar\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    test {CONFIG sanity} {\n        # Do CONFIG GET, CONFIG SET and then CONFIG GET again\n        # Skip immutable configs, one with no get, and other complicated configs\n        set skip_configs {\n            rdbchecksum\n            daemonize\n            io-threads-do-reads\n            tcp-backlog\n            always-show-logo\n            syslog-enabled\n            cluster-enabled\n            aclfile\n            unixsocket\n            pidfile\n            syslog-ident\n            appendfilename\n            supervised\n            syslog-facility\n            databases\n            port\n            io-threads\n            tls-port\n            tls-prefer-server-ciphers\n            tls-cert-file\n            tls-key-file\n            tls-dh-params-file\n            tls-ca-cert-file\n            tls-ca-cert-dir\n            tls-protocols\n            tls-ciphers\n            tls-ciphersuites\n            logfile\n            unixsocketperm\n            slaveof\n            bind\n            requirepass\n            server_cpulist\n            bio_cpulist\n            aof_rewrite_cpulist\n            bgsave_cpulist\n        }\n\n        set configs {}\n        foreach {k v} [r config get *] {\n            if {[lsearch $skip_configs $k] != -1} {\n                continue\n            }\n            dict set configs $k $v\n            # try to set the config to the same value it already has\n            r config set $k $v\n        }\n\n        set newconfigs {}\n        foreach {k v} [r config get *] {\n            if {[lsearch $skip_configs $k] != -1} {\n                continue\n            }\n            dict set newconfigs $k $v\n        }\n\n        dict for {k v} $configs {\n            set vv [dict get $newconfigs $k]\n            if {$v != $vv} {\n                fail \"config $k mismatch, expecting $v but got $vv\"\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/keyspace.tcl",
    "content": "start_server {tags {\"keyspace\"}} {\n    test {DEL against a single item} {\n        r set x foo\n        assert {[r get x] eq \"foo\"}\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 \"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* [r read]]\n        append res [r read]\n        append res [string match PONG* [r read]]\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 are the same (existing)} {\n        r set mykey foo\n        r rename mykey mykey\n    } {OK}\n\n    test {RENAMENX where source and dest key are the same (existing)} {\n        r set mykey foo\n        r renamenx mykey mykey\n    } {0}\n\n    test {RENAME where source and dest key are the same (non existing)} {\n        r del mykey\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 {MOVE against non-integer DB (#1428)} {\n        r set mykey hello\n        catch {r move mykey notanumber} e\n        set e\n    } {*ERR*index out of range}\n\n    test {MOVE can move key expire metadata as well} {\n        r select 10\n        r flushdb\n        r select 9\n        r set mykey foo ex 100\n        r move mykey 10\n        assert {[r ttl mykey] == -2}\n        r select 10\n        assert {[r ttl mykey] > 0 && [r ttl mykey] <= 100}\n        assert {[r get mykey] eq \"foo\"}\n        r select 9\n    }\n\n    test {MOVE does not create an expire if it does not exist} {\n        r select 10\n        r flushdb\n        r select 9\n        r set mykey foo\n        r move mykey 10\n        assert {[r ttl mykey] == -2}\n        r select 10\n        assert {[r ttl mykey] == -1}\n        assert {[r get mykey] eq \"foo\"}\n        r select 9\n    }\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 {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 {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": "Chapter07/redis-6.0.3/tests/unit/latency-monitor.tcl",
    "content": "start_server {tags {\"latency-monitor\"}} {\n    # Set a threshold high enough to avoid spurious latency events.\n    r config set latency-monitor-threshold 200\n    r latency reset\n\n    test {Test latency events logging} {\n        r debug sleep 0.3\n        after 1100\n        r debug sleep 0.4\n        after 1100\n        r debug sleep 0.5\n        assert {[r latency history command] >= 3}\n    }\n\n    test {LATENCY HISTORY output is ok} {\n        set min 250\n        set max 450\n        foreach event [r latency history command] {\n            lassign $event time latency\n            assert {$latency >= $min && $latency <= $max}\n            incr min 100\n            incr max 100\n            set last_time $time ; # Used in the next test\n        }\n    }\n\n    test {LATENCY LATEST output is ok} {\n        foreach event [r latency latest] {\n            lassign $event eventname time latency max\n            assert {$eventname eq \"command\"}\n            assert {$max >= 450 & $max <= 650}\n            assert {$time == $last_time}\n            break\n        }\n    }\n\n    test {LATENCY HISTORY / RESET with wrong event name is fine} {\n        assert {[llength [r latency history blabla]] == 0}\n        assert {[r latency reset blabla] == 0}\n    }\n\n    test {LATENCY DOCTOR produces some output} {\n        assert {[string length [r latency doctor]] > 0}\n    }\n\n    test {LATENCY RESET is able to reset events} {\n        assert {[r latency reset] > 0}\n        assert {[r latency latest] eq {}}\n    }\n\n    test {LATENCY of expire events are correctly collected} {\n        r config set latency-monitor-threshold 20\n        r eval {\n            local i = 0\n            while (i < 1000000) do\n                redis.call('sadd','mybigkey',i)\n                i = i+1\n             end\n        } 0\n        r pexpire mybigkey 1\n        after 500\n        assert_match {*expire-cycle*} [r latency latest]\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/lazyfree.tcl",
    "content": "start_server {tags {\"lazyfree\"}} {\n    test \"UNLINK can reclaim memory in background\" {\n        set orig_mem [s used_memory]\n        set args {}\n        for {set i 0} {$i < 100000} {incr i} {\n            lappend args $i\n        }\n        r sadd myset {*}$args\n        assert {[r scard myset] == 100000}\n        set peak_mem [s used_memory]\n        assert {[r unlink myset] == 1}\n        assert {$peak_mem > $orig_mem+1000000}\n        wait_for_condition 50 100 {\n            [s used_memory] < $peak_mem &&\n            [s used_memory] < $orig_mem*2\n        } else {\n            fail \"Memory is not reclaimed by UNLINK\"\n        }\n    }\n\n    test \"FLUSHDB ASYNC can reclaim memory in background\" {\n        set orig_mem [s used_memory]\n        set args {}\n        for {set i 0} {$i < 100000} {incr i} {\n            lappend args $i\n        }\n        r sadd myset {*}$args\n        assert {[r scard myset] == 100000}\n        set peak_mem [s used_memory]\n        r flushdb async\n        assert {$peak_mem > $orig_mem+1000000}\n        wait_for_condition 50 100 {\n            [s used_memory] < $peak_mem &&\n            [s used_memory] < $orig_mem*2\n        } else {\n            fail \"Memory is not reclaimed by FLUSHDB ASYNC\"\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/limits.tcl",
    "content": "start_server {tags {\"limits\"} overrides {maxclients 10}} {\n    if {$::tls} {\n        set expected_code \"*I/O error*\"\n    } else {\n        set expected_code \"*ERR max*reached*\"\n    }\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    } $expected_code\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/maxmemory.tcl",
    "content": "start_server {tags {\"maxmemory\"}} {\n    test \"Without maxmemory small integers are shared\" {\n        r config set maxmemory 0\n        r set a 1\n        assert {[r object refcount a] > 1}\n    }\n\n    test \"With maxmemory and non-LRU policy integers are still shared\" {\n        r config set maxmemory 1073741824\n        r config set maxmemory-policy allkeys-random\n        r set a 1\n        assert {[r object refcount a] > 1}\n    }\n\n    test \"With maxmemory and LRU policy integers are not shared\" {\n        r config set maxmemory 1073741824\n        r config set maxmemory-policy allkeys-lru\n        r set a 1\n        r config set maxmemory-policy volatile-lru\n        r set b 1\n        assert {[r object refcount a] == 1}\n        assert {[r object refcount b] == 1}\n        r config set maxmemory 0\n    }\n\n    foreach policy {\n        allkeys-random allkeys-lru allkeys-lfu volatile-lru volatile-lfu 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-lfu 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\nproc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline} {\n    start_server {tags {\"maxmemory\"}} {\n        start_server {} {\n        set slave_pid [s process_id]\n        test \"$test_name\" {\n            set slave [srv 0 client]\n            set slave_host [srv 0 host]\n            set slave_port [srv 0 port]\n            set master [srv -1 client]\n            set master_host [srv -1 host]\n            set master_port [srv -1 port]\n\n            # add 100 keys of 100k (10MB total)\n            for {set j 0} {$j < 100} {incr j} {\n                $master setrange \"key:$j\" 100000 asdf\n            }\n\n            # make sure master doesn't disconnect slave because of timeout\n            $master config set repl-timeout 1200 ;# 20 minutes (for valgrind and slow machines)\n            $master config set maxmemory-policy allkeys-random\n            $master config set client-output-buffer-limit \"replica 100000000 100000000 300\"\n            $master config set repl-backlog-size [expr {10*1024}]\n\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            # measure used memory after the slave connected and set maxmemory\n            set orig_used [s -1 used_memory]\n            set orig_client_buf [s -1 mem_clients_normal]\n            set orig_mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]\n            set orig_used_no_repl [expr {$orig_used - $orig_mem_not_counted_for_evict}]\n            set limit [expr {$orig_used - $orig_mem_not_counted_for_evict + 20*1024}]\n\n            if {$limit_memory==1} {\n                $master config set maxmemory $limit\n            }\n\n            # put the slave to sleep\n            set rd_slave [redis_deferring_client]\n            exec kill -SIGSTOP $slave_pid\n\n            # send some 10mb worth of commands that don't increase the memory usage\n            if {$pipeline == 1} {\n                set rd_master [redis_deferring_client -1]\n                for {set k 0} {$k < $cmd_count} {incr k} {\n                    $rd_master setrange key:0 0 [string repeat A $payload_len]\n                }\n                for {set k 0} {$k < $cmd_count} {incr k} {\n                    #$rd_master read\n                }\n            } else {\n                for {set k 0} {$k < $cmd_count} {incr k} {\n                    $master setrange key:0 0 [string repeat A $payload_len]\n                }\n            }\n\n            set new_used [s -1 used_memory]\n            set slave_buf [s -1 mem_clients_slaves]\n            set client_buf [s -1 mem_clients_normal]\n            set mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]\n            set used_no_repl [expr {$new_used - $mem_not_counted_for_evict}]\n            set delta [expr {($used_no_repl - $client_buf) - ($orig_used_no_repl - $orig_client_buf)}]\n\n            assert {[$master dbsize] == 100}\n            assert {$slave_buf > 2*1024*1024} ;# some of the data may have been pushed to the OS buffers\n            set delta_max [expr {$cmd_count / 2}] ;# 1 byte unaccounted for, with 1M commands will consume some 1MB\n            assert {$delta < $delta_max && $delta > -$delta_max}\n\n            $master client kill type slave\n            set killed_used [s -1 used_memory]\n            set killed_slave_buf [s -1 mem_clients_slaves]\n            set killed_mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]\n            set killed_used_no_repl [expr {$killed_used - $killed_mem_not_counted_for_evict}]\n            set delta_no_repl [expr {$killed_used_no_repl - $used_no_repl}]\n            assert {$killed_slave_buf == 0}\n            assert {$delta_no_repl > -$delta_max && $delta_no_repl < $delta_max}\n\n        }\n        # unfreeze slave process (after the 'test' succeeded or failed, but before we attempt to terminate the server\n        exec kill -SIGCONT $slave_pid\n        }\n    }\n}\n\n# test that slave buffer are counted correctly\n# we wanna use many small commands, and we don't wanna wait long\n# so we need to use a pipeline (redis_deferring_client)\n# that may cause query buffer to fill and induce eviction, so we disable it\ntest_slave_buffers {slave buffer are counted correctly} 1000000 10 0 1\n\n# test that slave buffer don't induce eviction\n# test again with fewer (and bigger) commands without pipeline, but with eviction\ntest_slave_buffers \"replica buffer don't induce eviction\" 100000 100 1 0\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/memefficiency.tcl",
    "content": "proc test_memory_efficiency {range} {\n    r flushall\n    set rd [redis_deferring_client]\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        $rd 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    for {set j 0} {$j < 10000} {incr j} {\n        $rd read ; # Discard replies\n    }\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\nrun_solo {defrag} {\nstart_server {tags {\"defrag\"}} {\n    if {[string match {*jemalloc*} [s mem_allocator]]} {\n        test \"Active defrag\" {\n            r config set save \"\" ;# prevent bgsave from interfereing with save below\n            r config set hz 100\n            r config set activedefrag no\n            r config set active-defrag-threshold-lower 5\n            r config set active-defrag-cycle-min 65\n            r config set active-defrag-cycle-max 75\n            r config set active-defrag-ignore-bytes 2mb\n            r config set maxmemory 100mb\n            r config set maxmemory-policy allkeys-lru\n            r debug populate 700000 asdf1 150\n            r debug populate 170000 asdf2 300\n            r ping ;# trigger eviction following the previous population\n            after 120 ;# serverCron only updates the info once in 100ms\n            set frag [s allocator_frag_ratio]\n            if {$::verbose} {\n                puts \"frag $frag\"\n            }\n            assert {$frag >= 1.4}\n\n            r config set latency-monitor-threshold 5\n            r latency reset\n            r config set maxmemory 110mb ;# prevent further eviction (not to fail the digest test)\n            set digest [r debug digest]\n            catch {r config set activedefrag yes} e\n            if {![string match {DISABLED*} $e]} {\n                # Wait for the active defrag to start working (decision once a\n                # second).\n                wait_for_condition 50 100 {\n                    [s active_defrag_running] ne 0\n                } else {\n                    fail \"defrag not started.\"\n                }\n\n                # Wait for the active defrag to stop working.\n                wait_for_condition 150 100 {\n                    [s active_defrag_running] eq 0\n                } else {\n                    after 120 ;# serverCron only updates the info once in 100ms\n                    puts [r info memory]\n                    puts [r memory malloc-stats]\n                    fail \"defrag didn't stop.\"\n                }\n\n                # Test the the fragmentation is lower.\n                after 120 ;# serverCron only updates the info once in 100ms\n                set frag [s allocator_frag_ratio]\n                set max_latency 0\n                foreach event [r latency latest] {\n                    lassign $event eventname time latency max\n                    if {$eventname == \"active-defrag-cycle\"} {\n                        set max_latency $max\n                    }\n                }\n                if {$::verbose} {\n                    puts \"frag $frag\"\n                    puts \"max latency $max_latency\"\n                    puts [r latency latest]\n                    puts [r latency history active-defrag-cycle]\n                }\n                assert {$frag < 1.1}\n                # due to high fragmentation, 100hz, and active-defrag-cycle-max set to 75,\n                # we expect max latency to be not much higher than 7.5ms but due to rare slowness threshold is set higher\n                assert {$max_latency <= 30}\n            } else {\n                set _ \"\"\n            }\n            # verify the data isn't corrupted or changed\n            set newdigest [r debug digest]\n            assert {$digest eq $newdigest}\n            r save ;# saving an rdb iterates over all the data / pointers\n        } {OK}\n\n        test \"Active defrag big keys\" {\n            r flushdb\n            r config resetstat\n            r config set save \"\" ;# prevent bgsave from interfereing with save below\n            r config set hz 100\n            r config set activedefrag no\n            r config set active-defrag-max-scan-fields 1000\n            r config set active-defrag-threshold-lower 5\n            r config set active-defrag-cycle-min 65\n            r config set active-defrag-cycle-max 75\n            r config set active-defrag-ignore-bytes 2mb\n            r config set maxmemory 0\n            r config set list-max-ziplist-size 5 ;# list of 10k items will have 2000 quicklist nodes\n            r config set stream-node-max-entries 5\n            r hmset hash h1 v1 h2 v2 h3 v3\n            r lpush list a b c d\n            r zadd zset 0 a 1 b 2 c 3 d\n            r sadd set a b c d\n            r xadd stream * item 1 value a\n            r xadd stream * item 2 value b\n            r xgroup create stream mygroup 0\n            r xreadgroup GROUP mygroup Alice COUNT 1 STREAMS stream >\n\n            # create big keys with 10k items\n            set rd [redis_deferring_client]\n            for {set j 0} {$j < 10000} {incr j} {\n                $rd hset bighash $j [concat \"asdfasdfasdf\" $j]\n                $rd lpush biglist [concat \"asdfasdfasdf\" $j]\n                $rd zadd bigzset $j [concat \"asdfasdfasdf\" $j]\n                $rd sadd bigset [concat \"asdfasdfasdf\" $j]\n                $rd xadd bigstream * item 1 value a\n            }\n            for {set j 0} {$j < 50000} {incr j} {\n                $rd read ; # Discard replies\n            }\n\n            set expected_frag 1.7\n            if {$::accurate} {\n                # scale the hash to 1m fields in order to have a measurable the latency\n                for {set j 10000} {$j < 1000000} {incr j} {\n                    $rd hset bighash $j [concat \"asdfasdfasdf\" $j]\n                }\n                for {set j 10000} {$j < 1000000} {incr j} {\n                    $rd read ; # Discard replies\n                }\n                # creating that big hash, increased used_memory, so the relative frag goes down\n                set expected_frag 1.3\n            }\n\n            # add a mass of string keys\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd setrange $j 150 a\n            }\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd read ; # Discard replies\n            }\n            assert_equal [r dbsize] 500010\n\n            # create some fragmentation\n            for {set j 0} {$j < 500000} {incr j 2} {\n                $rd del $j\n            }\n            for {set j 0} {$j < 500000} {incr j 2} {\n                $rd read ; # Discard replies\n            }\n            assert_equal [r dbsize] 250010\n\n            # start defrag\n            after 120 ;# serverCron only updates the info once in 100ms\n            set frag [s allocator_frag_ratio]\n            if {$::verbose} {\n                puts \"frag $frag\"\n            }\n            assert {$frag >= $expected_frag}\n            r config set latency-monitor-threshold 5\n            r latency reset\n\n            set digest [r debug digest]\n            catch {r config set activedefrag yes} e\n            if {![string match {DISABLED*} $e]} {\n                # wait for the active defrag to start working (decision once a second)\n                wait_for_condition 50 100 {\n                    [s active_defrag_running] ne 0\n                } else {\n                    fail \"defrag not started.\"\n                }\n\n                # wait for the active defrag to stop working\n                wait_for_condition 500 100 {\n                    [s active_defrag_running] eq 0\n                } else {\n                    after 120 ;# serverCron only updates the info once in 100ms\n                    puts [r info memory]\n                    puts [r memory malloc-stats]\n                    fail \"defrag didn't stop.\"\n                }\n\n                # test the the fragmentation is lower\n                after 120 ;# serverCron only updates the info once in 100ms\n                set frag [s allocator_frag_ratio]\n                set max_latency 0\n                foreach event [r latency latest] {\n                    lassign $event eventname time latency max\n                    if {$eventname == \"active-defrag-cycle\"} {\n                        set max_latency $max\n                    }\n                }\n                if {$::verbose} {\n                    puts \"frag $frag\"\n                    puts \"max latency $max_latency\"\n                    puts [r latency latest]\n                    puts [r latency history active-defrag-cycle]\n                }\n                assert {$frag < 1.1}\n                # due to high fragmentation, 100hz, and active-defrag-cycle-max set to 75,\n                # we expect max latency to be not much higher than 7.5ms but due to rare slowness threshold is set higher\n                assert {$max_latency <= 30}\n            }\n            # verify the data isn't corrupted or changed\n            set newdigest [r debug digest]\n            assert {$digest eq $newdigest}\n            r save ;# saving an rdb iterates over all the data / pointers\n        } {OK}\n\n        test \"Active defrag big list\" {\n            r flushdb\n            r config resetstat\n            r config set save \"\" ;# prevent bgsave from interfereing with save below\n            r config set hz 100\n            r config set activedefrag no\n            r config set active-defrag-max-scan-fields 1000\n            r config set active-defrag-threshold-lower 5\n            r config set active-defrag-cycle-min 65\n            r config set active-defrag-cycle-max 75\n            r config set active-defrag-ignore-bytes 2mb\n            r config set maxmemory 0\n            r config set list-max-ziplist-size 5 ;# list of 500k items will have 100k quicklist nodes\n\n            # create big keys with 10k items\n            set rd [redis_deferring_client]\n\n            set expected_frag 1.7\n            # add a mass of list nodes to two lists (allocations are interlaced)\n            set val [string repeat A 100] ;# 5 items of 100 bytes puts us in the 640 bytes bin, which has 32 regs, so high potential for fragmentation\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd lpush biglist1 $val\n                $rd lpush biglist2 $val\n            }\n            for {set j 0} {$j < 500000} {incr j} {\n                $rd read ; # Discard replies\n                $rd read ; # Discard replies\n            }\n\n            # create some fragmentation\n            r del biglist2\n\n            # start defrag\n            after 120 ;# serverCron only updates the info once in 100ms\n            set frag [s allocator_frag_ratio]\n            if {$::verbose} {\n                puts \"frag $frag\"\n            }\n\n            assert {$frag >= $expected_frag}\n            r config set latency-monitor-threshold 5\n            r latency reset\n\n            set digest [r debug digest]\n            catch {r config set activedefrag yes} e\n            if {![string match {DISABLED*} $e]} {\n                # wait for the active defrag to start working (decision once a second)\n                wait_for_condition 50 100 {\n                    [s active_defrag_running] ne 0\n                } else {\n                    fail \"defrag not started.\"\n                }\n\n                # wait for the active defrag to stop working\n                wait_for_condition 500 100 {\n                    [s active_defrag_running] eq 0\n                } else {\n                    after 120 ;# serverCron only updates the info once in 100ms\n                    puts [r info memory]\n                    puts [r info stats]\n                    puts [r memory malloc-stats]\n                    fail \"defrag didn't stop.\"\n                }\n\n                # test the the fragmentation is lower\n                after 120 ;# serverCron only updates the info once in 100ms\n                set frag [s allocator_frag_ratio]\n                set max_latency 0\n                foreach event [r latency latest] {\n                    lassign $event eventname time latency max\n                    if {$eventname == \"active-defrag-cycle\"} {\n                        set max_latency $max\n                    }\n                }\n                if {$::verbose} {\n                    puts \"frag $frag\"\n                    puts \"max latency $max_latency\"\n                    puts [r latency latest]\n                    puts [r latency history active-defrag-cycle]\n                }\n                assert {$frag < 1.1}\n                # due to high fragmentation, 100hz, and active-defrag-cycle-max set to 75,\n                # we expect max latency to be not much higher than 7.5ms but due to rare slowness threshold is set higher\n                assert {$max_latency <= 30}\n            }\n            # verify the data isn't corrupted or changed\n            set newdigest [r debug digest]\n            assert {$digest eq $newdigest}\n            r save ;# saving an rdb iterates over all the data / pointers\n            r del biglist1 ;# coverage for quicklistBookmarksClear\n        } {1}\n    }\n}\n} ;# run_solo\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/auth.tcl",
    "content": "set testmodule [file normalize tests/modules/auth.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {Modules can create a user that can be authenticated} {\n        # Make sure we start authenticated with default user\n        r auth default \"\"\n        assert_equal [r acl whoami] \"default\"\n        r auth.createmoduleuser\n\n        set id [r auth.authmoduleuser]\n        assert_equal [r client id] $id\n\n        # Verify returned id is the same as our current id and\n        # we are authenticated with the specified user\n        assert_equal [r acl whoami] \"global\"\n    }\n\n    test {De-authenticating clients is tracked and kills clients} {\n        assert_equal [r auth.changecount] 0\n        r auth.createmoduleuser\n\n        # Catch the I/O exception that was thrown when Redis\n        # disconnected with us. \n        catch { [r ping] } e\n        assert_match {*I/O*} $e\n\n        # Check that a user change was registered\n        assert_equal [r auth.changecount] 1\n    }\n\n    test {Modules cant authenticate with ACLs users that dont exist} {\n        catch { [r auth.authrealuser auth-module-test-fake] } e\n        assert_match {*Invalid user*} $e\n    }\n\n    test {Modules can authenticate with ACL users} {\n        assert_equal [r acl whoami] \"default\"\n\n        # Create user to auth into\n        r acl setuser auth-module-test on allkeys allcommands\n\n        set id [r auth.authrealuser auth-module-test]\n\n        # Verify returned id is the same as our current id and\n        # we are authenticated with the specified user\n        assert_equal [r client id] $id\n        assert_equal [r acl whoami] \"auth-module-test\"\n    }\n\n    test {Client callback is called on user switch} {\n        assert_equal [r auth.changecount] 0\n\n        # Auth again and validate change count\n        r auth.authrealuser auth-module-test\n        assert_equal [r auth.changecount] 1\n\n        # Re-auth with the default user\n        r auth default \"\"\n        assert_equal [r auth.changecount] 1\n        assert_equal [r acl whoami] \"default\"\n\n        # Re-auth with the default user again, to\n        # verify the callback isn't fired again\n        r auth default \"\"\n        assert_equal [r auth.changecount] 0\n        assert_equal [r acl whoami] \"default\"\n    }\n\n}"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/blockonkeys.tcl",
    "content": "set testmodule [file normalize tests/modules/blockonkeys.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test \"Module client blocked on keys: Circular BPOPPUSH\" {\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n\n        r del src dst\n\n        $rd1 fsl.bpoppush src dst 0\n        $rd2 fsl.bpoppush dst src 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {2}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n\n        r fsl.push src 42\n\n        assert_equal {42} [r fsl.getall src]\n        assert_equal {} [r fsl.getall dst]\n    }\n\n    test \"Module client blocked on keys: Self-referential BPOPPUSH\" {\n        set rd1 [redis_deferring_client]\n\n        r del src\n\n        $rd1 fsl.bpoppush src src 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push src 42\n\n        assert_equal {42} [r fsl.getall src]\n    }\n\n    test {Module client blocked on keys (no metadata): No block} {\n        r del k\n        r fsl.push k 33\n        r fsl.push k 34\n        r fsl.bpop k 0\n    } {34}\n\n    test {Module client blocked on keys (no metadata): Timeout} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpop k 1\n        assert_equal {Request timedout} [$rd read]\n    }\n\n    test {Module client blocked on keys (no metadata): Blocked} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpop k 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push k 34\n        assert_equal {34} [$rd read]\n    }\n\n    test {Module client blocked on keys (with metadata): No block} {\n        r del k\n        r fsl.push k 34\n        r fsl.bpopgt k 30 0\n    } {34}\n\n    test {Module client blocked on keys (with metadata): Timeout} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        r fsl.push k 33\n        $rd fsl.bpopgt k 35 1\n        assert_equal {Request timedout} [$rd read]\n        r client kill id $cid ;# try to smoke-out client-related memory leak\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, case 1} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        r fsl.push k 33\n        $rd fsl.bpopgt k 33 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push k 34\n        assert_equal {34} [$rd read]\n        r client kill id $cid ;# try to smoke-out client-related memory leak\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, case 2} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r fsl.push k 33\n        r fsl.push k 34\n        r fsl.push k 35\n        r fsl.push k 36\n        assert_equal {36} [$rd read]\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, CLIENT KILL} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r client kill id $cid ;# try to smoke-out client-related memory leak\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, CLIENT UNBLOCK TIMEOUT} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r client unblock $cid timeout ;# try to smoke-out client-related memory leak\n        assert_equal {Request timedout} [$rd read]\n    }\n\n    test {Module client blocked on keys (with metadata): Blocked, CLIENT UNBLOCK ERROR} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd client id\n        set cid [$rd read]\n        $rd fsl.bpopgt k 35 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r client unblock $cid error ;# try to smoke-out client-related memory leak\n        assert_error \"*unblocked*\" {$rd read}\n    }\n\n    test {Module client blocked on keys does not wake up on wrong type} {\n        r del k\n        set rd [redis_deferring_client]\n        $rd fsl.bpop k 0\n        ;# wait until clients are actually blocked\n        wait_for_condition 50 100 {\n            [s 0 blocked_clients] eq {1}\n        } else {\n            fail \"Clients are not blocked\"\n        }\n        r lpush k 12\n        r lpush k 13\n        r lpush k 14\n        r del k\n        r fsl.push k 34\n        assert_equal {34} [$rd read]\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/commandfilter.tcl",
    "content": "set testmodule [file normalize tests/modules/commandfilter.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule log-key 0\n\n    test {Command Filter handles redirected commands} {\n        r set mykey @log\n        r lrange log-key 0 -1\n    } \"{set mykey @log}\"\n\n    test {Command Filter can call RedisModule_CommandFilterArgDelete} {\n        r rpush mylist elem1 @delme elem2\n        r lrange mylist 0 -1\n    } {elem1 elem2}\n\n    test {Command Filter can call RedisModule_CommandFilterArgInsert} {\n        r del mylist\n        r rpush mylist elem1 @insertbefore elem2 @insertafter elem3\n        r lrange mylist 0 -1\n    } {elem1 --inserted-before-- @insertbefore elem2 @insertafter --inserted-after-- elem3}\n\n    test {Command Filter can call RedisModule_CommandFilterArgReplace} {\n        r del mylist\n        r rpush mylist elem1 @replaceme elem2\n        r lrange mylist 0 -1\n    } {elem1 --replaced-- elem2}\n\n    test {Command Filter applies on RM_Call() commands} {\n        r del log-key\n        r commandfilter.ping\n        r lrange log-key 0 -1\n    } \"{ping @log}\"\n\n    test {Command Filter applies on Lua redis.call()} {\n        r del log-key\n        r eval \"redis.call('ping', '@log')\" 0\n        r lrange log-key 0 -1\n    } \"{ping @log}\"\n\n    test {Command Filter applies on Lua redis.call() that calls a module} {\n        r del log-key\n        r eval \"redis.call('commandfilter.ping')\" 0\n        r lrange log-key 0 -1\n    } \"{ping @log}\"\n\n    test {Command Filter is unregistered implicitly on module unload} {\n        r del log-key\n        r module unload commandfilter\n        r set mykey @log\n        r lrange log-key 0 -1\n    } {}\n\n    r module load $testmodule log-key 0\n\n    test {Command Filter unregister works as expected} {\n        # Validate reloading succeeded\n        r del log-key\n        r set mykey @log\n        assert_equal \"{set mykey @log}\" [r lrange log-key 0 -1]\n\n        # Unregister\n        r commandfilter.unregister\n        r del log-key\n\n        r set mykey @log\n        r lrange log-key 0 -1\n    } {}\n\n    r module unload commandfilter\n    r module load $testmodule log-key 1\n\n    test {Command Filter REDISMODULE_CMDFILTER_NOSELF works as expected} {\n        r set mykey @log\n        assert_equal \"{set mykey @log}\" [r lrange log-key 0 -1]\n\n        r del log-key\n        r commandfilter.ping\n        assert_equal {} [r lrange log-key 0 -1]\n\n        r eval \"redis.call('commandfilter.ping')\" 0\n        assert_equal {} [r lrange log-key 0 -1]\n    }\n\n} \n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/datatype.tcl",
    "content": "set testmodule [file normalize tests/modules/datatype.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {DataType: Test module is sane, GET/SET work.} {\n        r datatype.set dtkey 100 stringval\n        assert {[r datatype.get dtkey] eq {100 stringval}}\n    }\n\n    test {DataType: RM_SaveDataTypeToString(), RM_LoadDataTypeFromString() work} {\n        r datatype.set dtkey -1111 MyString\n        set encoded [r datatype.dump dtkey]\n\n        r datatype.restore dtkeycopy $encoded\n        assert {[r datatype.get dtkeycopy] eq {-1111 MyString}}\n    }\n\n    test {DataType: Handle truncated RM_LoadDataTypeFromString()} {\n        r datatype.set dtkey -1111 MyString\n        set encoded [r datatype.dump dtkey]\n        set truncated [string range $encoded 0 end-1]\n\n        catch {r datatype.restore dtkeycopy $truncated} e\n        set e\n    } {*Invalid*}\n\n    test {DataType: ModuleTypeReplaceValue() happy path works} {\n        r datatype.set key-a 1 AAA\n        r datatype.set key-b 2 BBB\n\n        assert {[r datatype.swap key-a key-b] eq {OK}}\n        assert {[r datatype.get key-a] eq {2 BBB}}\n        assert {[r datatype.get key-b] eq {1 AAA}}\n    }\n\n    test {DataType: ModuleTypeReplaceValue() fails on non-module keys} {\n        r datatype.set key-a 1 AAA\n        r set key-b RedisString\n\n        catch {r datatype.swap key-a key-b} e\n        set e\n    } {*ERR*}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/fork.tcl",
    "content": "set testmodule [file normalize tests/modules/fork.so]\n\nproc count_log_message {pattern} {\n    set result [exec grep -c $pattern < [srv 0 stdout]]\n}\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {Module fork} {\n        # the argument to fork.create is the exitcode on termination\n        r fork.create 3\n        wait_for_condition 20 100 {\n            [r fork.exitcode] != -1\n        } else {\n            fail \"fork didn't terminate\"\n        }\n        r fork.exitcode\n    } {3}\n\n    test {Module fork kill} {\n        r fork.create 3\n        after 250\n        r fork.kill\n\n        assert {[count_log_message \"fork child started\"] eq \"2\"}\n        assert {[count_log_message \"Received SIGUSR1 in child\"] eq \"1\"}\n        assert {[count_log_message \"fork child exiting\"] eq \"1\"}\n    }\n\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/hooks.tcl",
    "content": "set testmodule [file normalize tests/modules/hooks.so]\n\ntags \"modules\" {\n    start_server [list overrides [list loadmodule \"$testmodule\" appendonly yes]] {\n\n        test {Test clients connection / disconnection hooks} {\n            for {set j 0} {$j < 2} {incr j} {\n                set rd1 [redis_deferring_client]\n                $rd1 close\n            }\n            assert {[r hooks.event_count client-connected] > 1}\n            assert {[r hooks.event_count client-disconnected] > 1}\n        }\n\n        test {Test module cron hook} {\n            after 100\n            assert {[r hooks.event_count cron-loop] > 0}\n            set hz [r hooks.event_last cron-loop]\n            assert_equal $hz 10\n        }\n\n        test {Test module loaded / unloaded hooks} {\n            set othermodule [file normalize tests/modules/infotest.so]\n            r module load $othermodule\n            r module unload infotest\n            assert_equal [r hooks.event_last module-loaded] \"infotest\"\n            assert_equal [r hooks.event_last module-unloaded] \"infotest\"\n        }\n\n        test {Test module aofrw hook} {\n            r debug populate 1000 foo 10000 ;# 10mb worth of data\n            r config set rdbcompression no ;# rdb progress is only checked once in 2mb\n            r BGREWRITEAOF\n            waitForBgrewriteaof r\n            assert_equal [string match {*module-event-persistence-aof-start*} [exec tail -20 < [srv 0 stdout]]] 1\n            assert_equal [string match {*module-event-persistence-end*} [exec tail -20 < [srv 0 stdout]]] 1\n        }\n\n        test {Test module aof load and rdb/aof progress hooks} {\n            # create some aof tail (progress is checked only once in 1000 commands)\n            for {set j 0} {$j < 4000} {incr j} {\n                r set \"bar$j\" x\n            }\n            # set some configs that will cause many loading progress events during aof loading\n            r config set key-load-delay 500\n            r config set dynamic-hz no\n            r config set hz 500\n            r DEBUG LOADAOF\n            assert_equal [r hooks.event_last loading-aof-start] 0\n            assert_equal [r hooks.event_last loading-end] 0\n            assert {[r hooks.event_count loading-rdb-start] == 0}\n            assert_lessthan 2 [r hooks.event_count loading-progress-rdb] ;# comes from the preamble section\n            assert_lessthan 2 [r hooks.event_count loading-progress-aof]\n            if {$::verbose} {\n                puts \"rdb progress events [r hooks.event_count loading-progress-rdb]\"\n                puts \"aof progress events [r hooks.event_count loading-progress-aof]\"\n            }\n        }\n        # undo configs before next test\n        r config set dynamic-hz yes\n        r config set key-load-delay 0\n\n        test {Test module rdb save hook} {\n            # debug reload does: save, flush, load:\n            assert {[r hooks.event_count persistence-syncrdb-start] == 0}\n            assert {[r hooks.event_count loading-rdb-start] == 0}\n            r debug reload\n            assert {[r hooks.event_count persistence-syncrdb-start] == 1}\n            assert {[r hooks.event_count loading-rdb-start] == 1}\n        }\n\n        test {Test flushdb hooks} {\n            r flushdb\n            assert_equal [r hooks.event_last flush-start] 9\n            assert_equal [r hooks.event_last flush-end] 9\n            r flushall\n            assert_equal [r hooks.event_last flush-start] -1\n            assert_equal [r hooks.event_last flush-end] -1\n        }\n\n        # replication related tests\n        set master [srv 0 client]\n        set master_host [srv 0 host]\n        set master_port [srv 0 port]\n        start_server {} {\n            r module load $testmodule\n            set replica [srv 0 client]\n            set replica_host [srv 0 host]\n            set replica_port [srv 0 port]\n            $replica replicaof $master_host $master_port\n\n            wait_for_condition 50 100 {\n                [string match {*master_link_status:up*} [r info replication]]\n            } else {\n                fail \"Can't turn the instance into a replica\"\n            }\n\n            test {Test master link up hook} {\n                assert_equal [r hooks.event_count masterlink-up] 1\n                assert_equal [r hooks.event_count masterlink-down] 0\n            }\n\n            test {Test role-replica hook} {\n                assert_equal [r hooks.event_count role-replica] 1\n                assert_equal [r hooks.event_count role-master] 0\n                assert_equal [r hooks.event_last role-replica] [s 0 master_host]\n            }\n\n            test {Test replica-online hook} {\n                assert_equal [r -1 hooks.event_count replica-online] 1\n                assert_equal [r -1 hooks.event_count replica-offline] 0\n            }\n\n            test {Test master link down hook} {\n                r client kill type master\n                assert_equal [r hooks.event_count masterlink-down] 1\n            }\n\n            $replica replicaof no one\n\n            test {Test role-master hook} {\n                assert_equal [r hooks.event_count role-replica] 1\n                assert_equal [r hooks.event_count role-master] 1\n                assert_equal [r hooks.event_last role-master] {}\n            }\n\n            test {Test replica-offline hook} {\n                assert_equal [r -1 hooks.event_count replica-online] 1\n                assert_equal [r -1 hooks.event_count replica-offline] 1\n            }\n            # get the replica stdout, to be used by the next test\n            set replica_stdout [srv 0 stdout]\n        }\n\n\n        # look into the log file of the server that just exited\n        test {Test shutdown hook} {\n            assert_equal [string match {*module-event-shutdown*} [exec tail -5 < $replica_stdout]] 1\n        }\n\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/infotest.tcl",
    "content": "set testmodule [file normalize tests/modules/infotest.so]\n\n# Return value for INFO property\nproc field {info property} {\n    if {[regexp \"\\r\\n$property:(.*?)\\r\\n\" $info _ value]} {\n        set _ $value\n    }\n}\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule log-key 0\n\n    test {module reading info} {\n        # check string, integer and float fields\n        assert_equal [r info.gets replication role] \"master\"\n        assert_equal [r info.getc replication role] \"master\"\n        assert_equal [r info.geti stats expired_keys] 0\n        assert_equal [r info.getd stats expired_stale_perc] 0\n\n        # check signed and unsigned\n        assert_equal [r info.geti infotest infotest_global] -2\n        assert_equal [r info.getu infotest infotest_uglobal] -2\n\n        # the above are always 0, try module info that is non-zero\n        assert_equal [r info.geti infotest_italian infotest_due] 2\n        set tre [r info.getd infotest_italian infotest_tre]\n        assert {$tre > 3.2 && $tre < 3.4 }\n\n        # search using the wrong section\n        catch { [r info.gets badname redis_version] } e\n        assert_match {*not found*} $e\n\n        # check that section filter works\n        assert { [string match \"*usec_per_call*\" [r info.gets all cmdstat_info.gets] ] }\n        catch { [r info.gets default cmdstat_info.gets] ] } e\n        assert_match {*not found*} $e\n    }\n\n    test {module info all} {\n        set info [r info all]\n        # info all does not contain modules\n        assert { ![string match \"*Spanish*\" $info] }\n        assert { ![string match \"*infotest_*\" $info] }\n        assert { [string match \"*used_memory*\" $info] }\n    }\n\n    test {module info everything} {\n        set info [r info everything]\n        # info everything contains all default sections, but not ones for crash report\n        assert { [string match \"*infotest_global*\" $info] }\n        assert { [string match \"*Spanish*\" $info] }\n        assert { [string match \"*Italian*\" $info] }\n        assert { [string match \"*used_memory*\" $info] }\n        assert { ![string match \"*Klingon*\" $info] }\n        field $info infotest_dos\n    } {2}\n\n    test {module info modules} {\n        set info [r info modules]\n        # info all does not contain modules\n        assert { [string match \"*Spanish*\" $info] }\n        assert { [string match \"*infotest_global*\" $info] }\n        assert { ![string match \"*used_memory*\" $info] }\n    }\n\n    test {module info one module} {\n        set info [r info INFOTEST]\n        # info all does not contain modules\n        assert { [string match \"*Spanish*\" $info] }\n        assert { ![string match \"*used_memory*\" $info] }\n        field $info infotest_global\n    } {-2}\n\n    test {module info one section} {\n        set info [r info INFOTEST_SPANISH]\n        assert { ![string match \"*used_memory*\" $info] }\n        assert { ![string match \"*Italian*\" $info] }\n        assert { ![string match \"*infotest_global*\" $info] }\n        field $info infotest_uno\n    } {one}\n\n    test {module info dict} {\n        set info [r info infotest_keyspace]\n        set keyspace [field $info infotest_db0]\n        set keys [scan [regexp -inline {keys\\=([\\d]*)} $keyspace] keys=%d]\n    } {3}\n\n    # TODO: test crash report.\n} \n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/misc.tcl",
    "content": "set testmodule [file normalize tests/modules/misc.so]\n\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {test RM_Call} {\n        set info [r test.call_info commandstats]\n        # cmdstat is not in a default section, so we also test an argument was passed\n        assert { [string match \"*cmdstat_module*\" $info] }\n    }\n\n    test {test RM_Call args array} {\n        set info [r test.call_generic info commandstats]\n        # cmdstat is not in a default section, so we also test an argument was passed\n        assert { [string match \"*cmdstat_module*\" $info] }\n    }\n\n    test {test long double conversions} {\n        set ld [r test.ld_conversion]\n        assert {[string match $ld \"0.00000000000000001\"]}\n    }\n\n    test {test module db commands} {\n        r set x foo\n        set key [r test.randomkey]\n        assert_equal $key \"x\"\n        assert_equal [r test.dbsize] 1\n        r test.flushall\n        assert_equal [r test.dbsize] 0\n    }\n\n    test {test modle lru api} {\n        r config set maxmemory-policy allkeys-lru\n        r set x foo\n        set lru [r test.getlru x]\n        assert { $lru <= 1000 }\n        set was_set [r test.setlru x 100000]\n        assert { $was_set == 1 }\n        set idle [r object idletime x]\n        assert { $idle >= 100 }\n        set lru [r test.getlru x]\n        assert { $lru >= 100000 }\n        r config set maxmemory-policy allkeys-lfu\n        set lru [r test.getlru x]\n        assert { $lru == -1 }\n        set was_set [r test.setlru x 100000]\n        assert { $was_set == 0 }\n    }\n    r config set maxmemory-policy allkeys-lru\n\n    test {test modle lfu api} {\n        r config set maxmemory-policy allkeys-lfu\n        r set x foo\n        set lfu [r test.getlfu x]\n        assert { $lfu >= 1 }\n        set was_set [r test.setlfu x 100]\n        assert { $was_set == 1 }\n        set freq [r object freq x]\n        assert { $freq <= 100 }\n        set lfu [r test.getlfu x]\n        assert { $lfu <= 100 }\n        r config set maxmemory-policy allkeys-lru\n        set lfu [r test.getlfu x]\n        assert { $lfu == -1 }\n        set was_set [r test.setlfu x 100]\n        assert { $was_set == 0 }\n    }\n\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/propagate.tcl",
    "content": "set testmodule [file normalize tests/modules/propagate.so]\n\ntags \"modules\" {\n    test {Modules can propagate in async and threaded contexts} {\n        start_server {} {\n            set replica [srv 0 client]\n            set replica_host [srv 0 host]\n            set replica_port [srv 0 port]\n            start_server [list overrides [list loadmodule \"$testmodule\"]] {\n                set master [srv 0 client]\n                set master_host [srv 0 host]\n                set master_port [srv 0 port]\n\n                # Start the replication process...\n                $replica replicaof $master_host $master_port\n                wait_for_sync $replica\n\n                after 1000\n                $master propagate-test\n\n                wait_for_condition 5000 10 {\n                    ([$replica get timer] eq \"10\") && \\\n                    ([$replica get a-from-thread] eq \"10\")\n                } else {\n                    fail \"The two counters don't match the expected value.\"\n                }\n\n                $master propagate-test-2\n                $master propagate-test-3\n                $master multi\n                $master propagate-test-2\n                $master propagate-test-3\n                $master exec\n                wait_for_ofs_sync $master $replica\n\n                assert_equal [s -1 unexpected_error_replies] 0\n            }\n        }\n    }\n}\n\ntags \"modules aof\" {\n    test {Modules RM_Replicate replicates MULTI/EXEC correctly} {\n        start_server [list overrides [list loadmodule \"$testmodule\"]] {\n            # Enable the AOF\n            r config set appendonly yes\n            r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.\n            waitForBgrewriteaof r\n\n            r propagate-test-2\n            r propagate-test-3\n            r multi\n            r propagate-test-2\n            r propagate-test-3\n            r exec\n\n            # Load the AOF\n            r debug loadaof\n\n            assert_equal [s 0 unexpected_error_replies] 0\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/scan.tcl",
    "content": "set testmodule [file normalize tests/modules/scan.so]\n\nstart_server {tags {\"modules\"}} {\n    r module load $testmodule\n\n    test {Module scan keyspace} {\n        # the module create a scan command with filtering which also return values\n        r set x 1\n        r set y 2\n        r set z 3\n        r hset h f v\n        lsort [r scan.scan_strings]\n    } {{x 1} {y 2} {z 3}}\n\n    test {Module scan hash ziplist} {\n        r hmset hh f1 v1 f2 v2\n        lsort [r scan.scan_key hh]\n    } {{f1 v1} {f2 v2}}\n\n    test {Module scan hash dict} {\n        r config set hash-max-ziplist-entries 2\n        r hmset hh f3 v3\n        lsort [r scan.scan_key hh]\n    } {{f1 v1} {f2 v2} {f3 v3}}\n\n    test {Module scan zset ziplist} {\n        r zadd zz 1 f1 2 f2\n        lsort [r scan.scan_key zz]\n    } {{f1 1} {f2 2}}\n\n    test {Module scan zset dict} {\n        r config set zset-max-ziplist-entries 2\n        r zadd zz 3 f3\n        lsort [r scan.scan_key zz]\n    } {{f1 1} {f2 2} {f3 3}}\n\n    test {Module scan set intset} {\n        r sadd ss 1 2\n        lsort [r scan.scan_key ss]\n    } {{1 {}} {2 {}}}\n\n    test {Module scan set dict} {\n        r config set set-max-intset-entries 2\n        r sadd ss 3\n        lsort [r scan.scan_key ss]\n    } {{1 {}} {2 {}} {3 {}}}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/moduleapi/testrdb.tcl",
    "content": "set testmodule [file normalize tests/modules/testrdb.so]\n\ntags \"modules\" {\n    test {modules are able to persist types} {\n        start_server [list overrides [list loadmodule \"$testmodule\"]] {\n            r testrdb.set.key key1 value1\n            assert_equal \"value1\" [r testrdb.get.key key1]\n            r debug reload\n            assert_equal \"value1\" [r testrdb.get.key key1]\n        }\n    }\n\n    test {modules global are lost without aux} {\n        set server_path [tmpdir \"server.module-testrdb\"]\n        start_server [list overrides [list loadmodule \"$testmodule\" \"dir\" $server_path]] {\n            r testrdb.set.before global1\n            assert_equal \"global1\" [r testrdb.get.before]\n        }\n        start_server [list overrides [list loadmodule \"$testmodule\" \"dir\" $server_path]] {\n            assert_equal \"\" [r testrdb.get.before]\n        }\n    }\n\n    test {modules are able to persist globals before and after} {\n        set server_path [tmpdir \"server.module-testrdb\"]\n        start_server [list overrides [list loadmodule \"$testmodule 2\" \"dir\" $server_path]] {\n            r testrdb.set.before global1\n            r testrdb.set.after global2\n            assert_equal \"global1\" [r testrdb.get.before]\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n        start_server [list overrides [list loadmodule \"$testmodule 2\" \"dir\" $server_path]] {\n            assert_equal \"global1\" [r testrdb.get.before]\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n\n    }\n\n    test {modules are able to persist globals just after} {\n        set server_path [tmpdir \"server.module-testrdb\"]\n        start_server [list overrides [list loadmodule \"$testmodule 1\" \"dir\" $server_path]] {\n            r testrdb.set.after global2\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n        start_server [list overrides [list loadmodule \"$testmodule 1\" \"dir\" $server_path]] {\n            assert_equal \"global2\" [r testrdb.get.after]\n        }\n    }\n\n    tags {repl} {\n        test {diskless loading short read with module} {\n            start_server [list overrides [list loadmodule \"$testmodule\"]] {\n                set replica [srv 0 client]\n                set replica_host [srv 0 host]\n                set replica_port [srv 0 port]\n                start_server [list overrides [list loadmodule \"$testmodule\"]] {\n                    set master [srv 0 client]\n                    set master_host [srv 0 host]\n                    set master_port [srv 0 port]\n\n                    # Set master and replica to use diskless replication\n                    $master config set repl-diskless-sync yes\n                    $master config set rdbcompression no\n                    $replica config set repl-diskless-load swapdb\n                    for {set k 0} {$k < 30} {incr k} {\n                        r testrdb.set.key key$k [string repeat A [expr {int(rand()*1000000)}]]\n                    }\n\n                    # Start the replication process...\n                    $master config set repl-diskless-sync-delay 0\n                    $replica replicaof $master_host $master_port\n\n                    # kill the replication at various points\n                    set attempts 3\n                    if {$::accurate} { set attempts 10 }\n                    for {set i 0} {$i < $attempts} {incr i} {\n                        # wait for the replica to start reading the rdb\n                        # using the log file since the replica only responds to INFO once in 2mb\n                        wait_for_log_message -1 \"*Loading DB in memory*\" 5 2000 1\n\n                        # add some additional random sleep so that we kill the master on a different place each time\n                        after [expr {int(rand()*100)}]\n\n                        # kill the replica connection on the master\n                        set killed [$master client kill type replica]\n\n                        if {[catch {\n                            set res [wait_for_log_message -1 \"*Internal error in RDB*\" 5 100 10]\n                            if {$::verbose} {\n                                puts $res\n                            }\n                        }]} {\n                            puts \"failed triggering short read\"\n                            # force the replica to try another full sync\n                            $master client kill type replica\n                            $master set asdf asdf\n                            # the side effect of resizing the backlog is that it is flushed (16k is the min size)\n                            $master config set repl-backlog-size [expr {16384 + $i}]\n                        }\n                        # wait for loading to stop (fail)\n                        wait_for_condition 100 10 {\n                            [s -1 loading] eq 0\n                        } else {\n                            fail \"Replica didn't disconnect\"\n                        }\n                    }\n                    # enable fast shutdown\n                    $master config set rdb-key-save-delay 0\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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    test {DISCARD should not fail during OOM} {\n        set rd [redis_deferring_client]\n        $rd config set maxmemory 1\n        assert  {[$rd read] eq {OK}}\n        r multi\n        catch {r set x 1} e\n        assert_match {OOM*} $e\n        r discard\n        $rd config set maxmemory 0\n        assert  {[$rd read] eq {OK}}\n        $rd close\n        r ping\n    } {PONG}\n\n    test {MULTI and script timeout} {\n        # check that if MULTI arrives during timeout, it is either refused, or\n        # allowed to pass, and we don't end up executing half of the transaction\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r config set lua-time-limit 10\n        r set xx 1\n        $rd1 eval {while true do end} 0\n        after 200\n        catch { $rd2 multi; $rd2 read } e\n        catch { $rd2 incr xx; $rd2 read } e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        catch { $rd2 incr xx; $rd2 read } e\n        catch { $rd2 exec; $rd2 read } e\n        set xx [r get xx]\n        # make sure that either the whole transcation passed or none of it (we actually expect none)\n        assert { $xx == 1 || $xx == 3}\n        # check that the connection is no longer in multi state\n        $rd2 ping asdf\n        set pong [$rd2 read]\n        assert_equal $pong \"asdf\"\n    }\n\n    test {EXEC and script timeout} {\n        # check that if EXEC arrives during timeout, we don't end up executing\n        # half of the transaction, and also that we exit the multi state\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r config set lua-time-limit 10\n        r set xx 1\n        catch { $rd2 multi; $rd2 read } e\n        catch { $rd2 incr xx; $rd2 read } e\n        $rd1 eval {while true do end} 0\n        after 200\n        catch { $rd2 incr xx; $rd2 read } e\n        catch { $rd2 exec; $rd2 read } e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        set xx [r get xx]\n        # make sure that either the whole transcation passed or none of it (we actually expect none)\n        assert { $xx == 1 || $xx == 3}\n        # check that the connection is no longer in multi state\n        $rd2 ping asdf\n        set pong [$rd2 read]\n        assert_equal $pong \"asdf\"\n    }\n\n    test {MULTI-EXEC body and script timeout} {\n        # check that we don't run an imcomplete transaction due to some commands\n        # arriving during busy script\n        set rd1 [redis_deferring_client]\n        set rd2 [redis_deferring_client]\n        r config set lua-time-limit 10\n        r set xx 1\n        catch { $rd2 multi; $rd2 read } e\n        catch { $rd2 incr xx; $rd2 read } e\n        $rd1 eval {while true do end} 0\n        after 200\n        catch { $rd2 incr xx; $rd2 read } e\n        r script kill\n        after 200 ; # Give some time to Lua to call the hook again...\n        catch { $rd2 exec; $rd2 read } e\n        set xx [r get xx]\n        # make sure that either the whole transcation passed or none of it (we actually expect none)\n        assert { $xx == 1 || $xx == 3}\n        # check that the connection is no longer in multi state\n        $rd2 ping asdf\n        set pong [$rd2 read]\n        assert_equal $pong \"asdf\"\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 >= 70000 && $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": "Chapter07/redis-6.0.3/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    } {*index is out of range*}\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 config set aof-use-rdb-preamble no\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 config set aof-use-rdb-preamble no\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            if {$::tls} {\n                set fd2 [::tls::socket $::host $::port]\n            } else {\n                set fd2 [socket $::host $::port]\n            }\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        r del foo\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": "Chapter07/redis-6.0.3/tests/unit/pendingquerybuf.tcl",
    "content": "proc info_memory {r property} {\n    if {[regexp \"\\r\\n$property:(.*?)\\r\\n\" [{*}$r info memory] _ value]} {\n        set _ $value\n    }\n}\n\nproc prepare_value {size} {\n    set _v \"c\" \n    for {set i 1} {$i < $size} {incr i} {\n        append _v 0   \n    }\n    return $_v\n}\n\nstart_server {tags {\"wait\"}} {\nstart_server {} {\n    set slave [srv 0 client]\n    set slave_host [srv 0 host]\n    set slave_port [srv 0 port]\n    set master [srv -1 client]\n    set master_host [srv -1 host]\n    set master_port [srv -1 port]\n\n    test \"pending querybuf: check size of pending_querybuf after set a big value\" {\n        $slave slaveof $master_host $master_port\n        set _v [prepare_value [expr 32*1024*1024]]\n        $master set key $_v \n        after 2000\n        set m_usedmemory [info_memory $master used_memory]\n        set s_usedmemory [info_memory $slave used_memory]\n        if { $s_usedmemory > $m_usedmemory + 10*1024*1024 } {\n            fail \"the used_memory of replica is much larger than master. Master:$m_usedmemory Replica:$s_usedmemory\"\n        }\n    }  \n}}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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            if {$::tls} {\n                set s [::tls::socket [srv 0 host] [srv 0 port]]\n            } else {\n                set s [socket [srv 0 host] [srv 0 port]]\n            }\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": "Chapter07/redis-6.0.3/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 \"Pub/Sub PING\" {\n        set rd1 [redis_deferring_client]\n        subscribe $rd1 somechannel\n        # While subscribed to non-zero channels PING works in Pub/Sub mode.\n        $rd1 ping\n        $rd1 ping \"foo\"\n        set reply1 [$rd1 read]\n        set reply2 [$rd1 read]\n        unsubscribe $rd1 somechannel\n        # Now we are unsubscribed, PING should just return PONG.\n        $rd1 ping\n        set reply3 [$rd1 read]\n        $rd1 close\n        list $reply1 $reply2 $reply3\n    } {{pong {}} {pong foo} PONG}\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 \"NUMSUB returns numbers, not strings (#1561)\" {\n        r pubsub numsub abc def\n    } {abc 0 def 0}\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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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    test \"SCAN TYPE\" {\n        r flushdb\n        # populate only creates strings\n        r debug populate 1000\n\n        # Check non-strings are excluded\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur type \"list\"]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        assert_equal 0 [llength $keys]\n\n        # Check strings are included\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur type \"string\"]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        assert_equal 1000 [llength $keys]\n\n        # Check all three args work together\n        set cur 0\n        set keys {}\n        while 1 {\n            set res [r scan $cur type \"string\" match \"key:*\" count 10]\n            set cur [lindex $res 0]\n            set k [lindex $res 1]\n            lappend keys {*}$k\n            if {$cur == 0} break\n        }\n\n        assert_equal 1000 [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    test \"ZSCAN scores: regression test for issue #2175\" {\n        r del mykey\n        for {set j 0} {$j < 500} {incr j} {\n            r zadd mykey 9.8813129168249309e-323 $j\n        }\n        set res [lindex [r zscan mykey 0] 1]\n        set first_score [lindex $res 1]\n        assert {$first_score != 0}\n    }\n\n    test \"SCAN regression test for issue #4906\" {\n        for {set k 0} {$k < 100} {incr k} {\n            r del set\n            r sadd set x; # Make sure it's not intset encoded\n            set toremove {}\n            unset -nocomplain found\n            array set found {}\n\n            # Populate the set\n            set numele [expr {101+[randomInt 1000]}]\n            for {set j 0} {$j < $numele} {incr j} {\n                r sadd set $j\n                if {$j >= 100} {\n                    lappend toremove $j\n                }\n            }\n\n            # Start scanning\n            set cursor 0\n            set iteration 0\n            set del_iteration [randomInt 10]\n            while {!($cursor == 0 && $iteration != 0)} {\n                lassign [r sscan set $cursor] cursor items\n\n                # Mark found items. We expect to find from 0 to 99 at the end\n                # since those elements will never be removed during the scanning.\n                foreach i $items {\n                    set found($i) 1\n                }\n                incr iteration\n                # At some point remove most of the items to trigger the\n                # rehashing to a smaller hash table.\n                if {$iteration == $del_iteration} {\n                    r srem set {*}$toremove\n                }\n            }\n\n            # Verify that SSCAN reported everything from 0 to 99\n            for {set j 0} {$j < 100} {incr j} {\n                if {![info exists found($j)]} {\n                    fail \"SSCAN element missing $j\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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',KEYS[1])} 1 mykey\n    } {myval}\n\n    test {EVALSHA - Can we call a SHA1 if already defined?} {\n        r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey\n    } {myval}\n\n    test {EVALSHA - Can we call a SHA1 in uppercase?} {\n        r evalsha FD758D1589D044DD850A6F05D52F2EEFD27F033F 1 mykey\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 set x 0\n        r eval {\n            local foo = redis.pcall('incr',KEYS[1])\n            return {type(foo),foo}\n        } 1 x\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',KEYS[1])\n            return {type(foo),foo}\n        } 1 mykey\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',KEYS[1],0,-1)\n            return {type(foo),foo[1],foo[2],foo[3],# foo}\n        } 1 mylist\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',KEYS[1],'myval')\n            return {type(foo),foo['ok']}\n        } 1 mykey\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',KEYS[1])\n            return {type(foo),foo['err']}\n        } 1 mykey\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',KEYS[1])\n            return {type(foo),foo == false}\n        } 1 mykey\n    } {boolean 1}\n\n    test {EVAL - Is the Lua client using 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',KEYS[1])} 1 mykey\n    } {this is DB 10}\n\n    test {EVAL - SELECT inside Lua should not affect the caller} {\n        # here we DB 10 is selected\n        r set mykey \"original value\"\n        r eval {return redis.pcall('select','9')} 0\n        set res [r get mykey]\n        r select 9\n        set res\n    } {original value}\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('blpop','x',0)} 0} e\n        set e\n    } {*not allowed*}\n\n    test {EVAL - Scripts can't run XREAD and XREADGROUP with BLOCK option} {\n        r del s\n        r xgroup create s g $ MKSTREAM\n        set res [r eval {return redis.pcall('xread','STREAMS','s','$')} 1 s]\n        assert {$res eq {}}\n        assert_error \"*xread command is not allowed with BLOCK option from scripts\" {r eval {return redis.pcall('xread','BLOCK',0,'STREAMS','s','$')} 1 s}\n        set res [r eval {return redis.pcall('xreadgroup','group','g','c','STREAMS','s','>')} 1 s]\n        assert {$res eq {}}\n        assert_error \"*xreadgroup command is not allowed with BLOCK option from scripts\" {r eval {return redis.pcall('xreadgroup','group','g','c','BLOCK',0,'STREAMS','s','>')} 1 s}\n    }\n\n    test {EVAL - Scripts can't run certain commands} {\n        set e {}\n        r debug lua-always-replicate-commands 0\n        catch {\n            r eval \"redis.pcall('randomkey'); return redis.pcall('set','x','ciao')\" 0\n        } e\n        r debug lua-always-replicate-commands 1\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',KEYS[1],'val')} 1 foo\n        } e\n        set e\n    } {*against a key*}\n\n    test {EVAL - JSON numeric decoding} {\n        # We must return the table as a string because otherwise\n        # Redis converts floats to ints and we get 0 and 1023 instead\n        # of 0.0003 and 1023.2 as the parsed output.\n        r eval {return\n                 table.concat(\n                   cjson.decode(\n                    \"[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]\"), \" \")\n        } 0\n    } {0 -5000 -1 0.0003 1023.2 0}\n\n    test {EVAL - JSON string decoding} {\n        r eval {local decoded = cjson.decode('{\"keya\": \"a\", \"keyb\": \"b\"}')\n                return {decoded.keya, decoded.keyb}\n        } 0\n    } {a b}\n\n    test {EVAL - cmsgpack can pack double?} {\n        r eval {local encoded = cmsgpack.pack(0.1)\n                local h = \"\"\n                for i = 1, #encoded do\n                    h = h .. string.format(\"%02x\",string.byte(encoded,i))\n                end\n                return h\n        } 0\n    } {cb3fb999999999999a}\n\n    test {EVAL - cmsgpack can pack negative int64?} {\n        r eval {local encoded = cmsgpack.pack(-1099511627776)\n                local h = \"\"\n                for i = 1, #encoded do\n                    h = h .. string.format(\"%02x\",string.byte(encoded,i))\n                end\n                return h\n        } 0\n    } {d3ffffff0000000000}\n\n    test {EVAL - cmsgpack can pack and unpack circular references?} {\n        r eval {local a = {x=nil,y=5}\n                local b = {x=a}\n                a['x'] = b\n                local encoded = cmsgpack.pack(a)\n                local h = \"\"\n                -- cmsgpack encodes to a depth of 16, but can't encode\n                -- references, so the encoded object has a deep copy recusive\n                -- depth of 16.\n                for i = 1, #encoded do\n                    h = h .. string.format(\"%02x\",string.byte(encoded,i))\n                end\n                -- when unpacked, re.x.x != re because the unpack creates\n                -- individual tables down to a depth of 16.\n                -- (that's why the encoded output is so large)\n                local re = cmsgpack.unpack(encoded)\n                assert(re)\n                assert(re.x)\n                assert(re.x.x.y == re.y)\n                assert(re.x.x.x.x.y == re.y)\n                assert(re.x.x.x.x.x.x.y == re.y)\n                assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y)\n                -- maximum working depth:\n                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y)\n                -- now the last x would be b above and has no y\n                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x)\n                -- so, the final x.x is at the depth limit and was assigned nil\n                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil)\n                return {h, re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5}\n        } 0\n    } {82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0 1 1}\n\n    test {EVAL - Numerical sanity check from bitop} {\n        r eval {assert(0x7fffffff == 2147483647, \"broken hex literals\");\n                assert(0xffffffff == -1 or 0xffffffff == 2^32-1,\n                    \"broken hex literals\");\n                assert(tostring(-1) == \"-1\", \"broken tostring()\");\n                assert(tostring(0xffffffff) == \"-1\" or\n                    tostring(0xffffffff) == \"4294967295\",\n                    \"broken tostring()\")\n        } 0\n    } {}\n\n    test {EVAL - Verify minimal bitop functionality} {\n        r eval {assert(bit.tobit(1) == 1);\n                assert(bit.band(1) == 1);\n                assert(bit.bxor(1,2) == 3);\n                assert(bit.bor(1,2,4,8,16,32,64,128) == 255)\n        } 0\n    } {}\n\n    test {EVAL - Able to parse trailing comments} {\n        r eval {return 'hello' --trailing comment} 0\n    } {hello}\n\n    test {SCRIPTING FLUSH - is able to clear the scripts cache?} {\n        r set mykey myval\n        set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]\n        assert_equal $v myval\n        set e \"\"\n        r script flush\n        catch {r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey} 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 debug lua-always-replicate-commands 0\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        set res [r eval {return redis.call('smembers',KEYS[1])} 1 myset]\n        r debug lua-always-replicate-commands 1\n        set res\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',KEYS[1],'desc')} 1 myset\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',KEYS[1],'by','_')} 1 myset\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',KEYS[1],'by','_','get','#','get','_:*')} 1 myset\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 * 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 config set aof-use-rdb-preamble no\n        r eval {redis.call(\"set\",KEYS[1],\"100\")} 1 foo\n        r eval {redis.call(\"incr\",KEYS[1])} 1 foo\n        r eval {redis.call(\"incr\",KEYS[1])} 1 foo\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 1] 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    test {Call Redis command with many args from Lua (issue #1764)} {\n        r eval {\n            local i\n            local x={}\n            redis.call('del','mylist')\n            for i=1,100 do\n                table.insert(x,i)\n            end\n            redis.call('rpush','mylist',unpack(x))\n            return redis.call('lrange','mylist',0,-1)\n        } 0\n    } {1 2 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100}\n\n    test {Number conversion precision test (issue #1118)} {\n        r eval {\n              local value = 9007199254740991\n              redis.call(\"set\",\"foo\",value)\n              return redis.call(\"get\",\"foo\")\n        } 0\n    } {9007199254740991}\n\n    test {String containing number precision test (regression of issue #1118)} {\n        r eval {\n            redis.call(\"set\", \"key\", \"12039611435714932082\")\n            return redis.call(\"get\", \"key\")\n        } 0\n    } {12039611435714932082}\n\n    test {Verify negative arg count is error instead of crash (issue #1842)} {\n        catch { r eval { return \"hello\" } -12 } e\n        set e\n    } {ERR Number of keys can't be negative}\n\n    test {Correct handling of reused argv (issue #1939)} {\n        r eval {\n              for i = 0, 10 do\n                  redis.call('SET', 'a', '1')\n                  redis.call('MGET', 'a', 'b', 'c')\n                  redis.call('EXPIRE', 'a', 0)\n                  redis.call('GET', 'a')\n                  redis.call('MGET', 'a', 'b', 'c')\n              end\n        } 0\n    }\n\n    test {Functions in the Redis namespace are able to report errors} {\n        catch {\n            r eval {\n                  redis.sha1hex()\n            } 0\n        } e\n        set e\n    } {*wrong number*}\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',KEYS[1],'y'); while true do end} 1 x\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 could 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\nforeach cmdrepl {0 1} {\n    start_server {tags {\"scripting repl\"}} {\n        start_server {} {\n            if {$cmdrepl == 1} {\n                set rt \"(commands replication)\"\n            } else {\n                set rt \"(scripts replication)\"\n                r debug lua-always-replicate-commands 1\n            }\n\n            test \"Before the replica connects we issue two EVAL commands $rt\" {\n                # One with an error, but still executing a command.\n                # SHA is: 67164fc43fa971f76fd1aaeeaf60c1c178d25876\n                catch {\n                    r eval {redis.call('incr',KEYS[1]); redis.call('nonexisting')} 1 x\n                }\n                # One command is correct:\n                # SHA is: 6f5ade10a69975e903c6d07b10ea44c6382381a5\n                r eval {return redis.call('incr',KEYS[1])} 1 x\n            } {2}\n\n            test \"Connect a replica to the master instance $rt\" {\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 replica\"\n                }\n            }\n\n            test \"Now use EVALSHA against the master, with both SHAs $rt\" {\n                # The server should replicate successful and unsuccessful\n                # commands as EVAL instead of EVALSHA.\n                catch {\n                    r evalsha 67164fc43fa971f76fd1aaeeaf60c1c178d25876 1 x\n                }\n                r evalsha 6f5ade10a69975e903c6d07b10ea44c6382381a5 1 x\n            } {4}\n\n            test \"If EVALSHA was replicated as EVAL, 'x' should be '4' $rt\" {\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 $rt\" {\n                set rd [redis_deferring_client]\n                $rd brpop a 0\n                r eval {\n                    redis.call(\"lpush\",KEYS[1],\"1\");\n                    redis.call(\"lpush\",KEYS[1],\"2\");\n                } 1 a\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 replica 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 $rt\" {\n                r del x\n                r eval {if tonumber(ARGV[1]) > 0 then redis.call('incr', KEYS[1]) end} 1 x 0\n                r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 0\n                r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 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            test \"Lua scripts using SELECT are replicated correctly $rt\" {\n                r eval {\n                    redis.call(\"set\",\"foo1\",\"bar1\")\n                    redis.call(\"select\",\"10\")\n                    redis.call(\"incr\",\"x\")\n                    redis.call(\"select\",\"11\")\n                    redis.call(\"incr\",\"z\")\n                } 0\n                r eval {\n                    redis.call(\"set\",\"foo1\",\"bar1\")\n                    redis.call(\"select\",\"10\")\n                    redis.call(\"incr\",\"x\")\n                    redis.call(\"select\",\"11\")\n                    redis.call(\"incr\",\"z\")\n                } 0\n                wait_for_condition 50 100 {\n                    [r -1 debug digest] eq [r debug digest]\n                } else {\n                    fail \"Master-Replica desync after Lua script using SELECT.\"\n                }\n            }\n        }\n    }\n}\n\nstart_server {tags {\"scripting repl\"}} {\n    start_server {overrides {appendonly yes aof-use-rdb-preamble no}} {\n        test \"Connect a replica to the master 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 replica\"\n            }\n        }\n\n        test \"Redis.replicate_commands() must be issued before any write\" {\n            r eval {\n                redis.call('set','foo','bar');\n                return redis.replicate_commands();\n            } 0\n        } {}\n\n        test \"Redis.replicate_commands() must be issued before any write (2)\" {\n            r eval {\n                return redis.replicate_commands();\n            } 0\n        } {1}\n\n        test \"Redis.set_repl() must be issued after replicate_commands()\" {\n            r debug lua-always-replicate-commands 0\n            catch {\n                r eval {\n                    redis.set_repl(redis.REPL_ALL);\n                } 0\n            } e\n            r debug lua-always-replicate-commands 1\n            set e\n        } {*only after turning on*}\n\n        test \"Redis.set_repl() don't accept invalid values\" {\n            catch {\n                r eval {\n                    redis.replicate_commands();\n                    redis.set_repl(12345);\n                } 0\n            } e\n            set e\n        } {*Invalid*flags*}\n\n        test \"Test selective replication of certain Redis commands from Lua\" {\n            r del a b c d\n            r eval {\n                redis.replicate_commands();\n                redis.call('set','a','1');\n                redis.set_repl(redis.REPL_NONE);\n                redis.call('set','b','2');\n                redis.set_repl(redis.REPL_AOF);\n                redis.call('set','c','3');\n                redis.set_repl(redis.REPL_ALL);\n                redis.call('set','d','4');\n            } 0\n\n            wait_for_condition 50 100 {\n                [r -1 mget a b c d] eq {1 {} {} 4}\n            } else {\n                fail \"Only a and c should be replicated to replica\"\n            }\n\n            # Master should have everything right now\n            assert {[r mget a b c d] eq {1 2 3 4}}\n\n            # After an AOF reload only a, c and d should exist\n            r debug loadaof\n\n            assert {[r mget a b c d] eq {1 {} 3 4}}\n        }\n\n        test \"PRNG is seeded randomly for command replication\" {\n            set a [\n                r eval {\n                    redis.replicate_commands();\n                    return math.random()*100000;\n                } 0\n            ]\n            set b [\n                r eval {\n                    redis.replicate_commands();\n                    return math.random()*100000;\n                } 0\n            ]\n            assert {$a ne $b}\n        }\n\n        test \"Using side effects is not a problem with command replication\" {\n            r eval {\n                redis.replicate_commands();\n                redis.call('set','time',redis.call('time')[1])\n            } 0\n\n            assert {[r get time] ne {}}\n\n            wait_for_condition 50 100 {\n                [r get time] eq [r -1 get time]\n            } else {\n                fail \"Time key does not match between master and replica\"\n            }\n        }\n    }\n}\n\nstart_server {tags {\"scripting\"}} {\n    r script debug sync\n    r eval {return 'hello'} 0\n    r eval {return 'hello'} 0\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 client setname foobar\n        r debug sleep 0.2\n        set e [lindex [r slowlog get] 0]\n        assert_equal [llength $e] 6\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        assert_equal {foobar} [lindex $e 5]\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    test {SLOWLOG - can clean older entires} {\n        r client setname lastentry_client\n        r config set slowlog-max-len 1\n        r debug sleep 0.2\n        assert {[llength [r slowlog get]] == 1}\n        set e [lindex [r slowlog get] 0]\n        assert_equal {lastentry_client} [lindex $e 5]\n    }\n\n    test {SLOWLOG - can be disabled} {\n        r config set slowlog-max-len 1\n        r config set slowlog-log-slower-than 1\n        r slowlog reset\n        r debug sleep 0.2\n        assert_equal [r slowlog len] 1\n        r config set slowlog-log-slower-than -1\n        r slowlog reset\n        r debug sleep 0.2\n        assert_equal [r slowlog len] 0\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/sort.tcl",
    "content": "start_server {\n    tags {\"sort\"}\n    overrides {\n        \"list-max-ziplist-size\" 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 quicklist \"Old Ziplist\"\n        1000 lpush quicklist \"Old Linked list\"\n        10000 lpush quicklist \"Old 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 quicklist 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 quicklist sort-res\n    }\n\n    test \"SORT extracts STORE correctly\" {\n        r command getkeys sort abc store def\n    } {abc def}\n\n    test \"SORT extracts multiple STORE correctly\" {\n        r command getkeys sort abc store invalid store stillbad store def\n    } {abc def}\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',KEYS[1],'by','nosort','asc'),\n                    redis.call('sort',KEYS[1],'by','nosort','desc')}\n        } 1 zset\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 issue 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    test \"SORT by nosort retains native order for lists\" {\n        r del testa\n        r lpush testa 2 1 4 3 5\n        r sort testa by nosort\n    } {5 3 4 1 2}\n\n    test \"SORT by nosort plus store retains native order for lists\" {\n        r del testa\n        r lpush testa 2 1 4 3 5\n        r sort testa by nosort store testb\n        r lrange testb 0 -1\n    } {5 3 4 1 2}\n\n    test \"SORT by nosort with limit returns based on original list order\" {\n        r sort testa by nosort limit 0 3 store testb\n        r lrange testb 0 -1\n    } {5 3 4}\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": "Chapter07/redis-6.0.3/tests/unit/tls.tcl",
    "content": "start_server {tags {\"tls\"}} {\n    if {$::tls} {\n        package require tls\n\n        test {TLS: Not accepting non-TLS connections on a TLS port} {\n            set s [redis [srv 0 host] [srv 0 port]]\n            catch {$s PING} e\n            set e\n        } {*I/O error*}\n\n        test {TLS: Verify tls-auth-clients behaves as expected} {\n            set s [redis [srv 0 host] [srv 0 port]]\n            ::tls::import [$s channel]\n            catch {$s PING} e\n            assert_match {*error*} $e\n\n            r CONFIG SET tls-auth-clients no\n\n            set s [redis [srv 0 host] [srv 0 port]]\n            ::tls::import [$s channel]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-auth-clients yes\n        }\n\n        test {TLS: Verify tls-protocols behaves as expected} {\n            r CONFIG SET tls-protocols TLSv1.2\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.2 0}]\n            catch {$s PING} e\n            assert_match {*I/O error*} $e\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.2 1}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-protocols \"\"\n        }\n\n        test {TLS: Verify tls-ciphers behaves as expected} {\n            r CONFIG SET tls-protocols TLSv1.2\n            r CONFIG SET tls-ciphers \"DEFAULT:-AES128-SHA256\"\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"-ALL:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {*I/O error*} $e\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"-ALL:AES256-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-ciphers \"DEFAULT\"\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"-ALL:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            r CONFIG SET tls-protocols \"\"\n            r CONFIG SET tls-ciphers \"DEFAULT\"\n        }\n\n        test {TLS: Verify tls-prefer-server-ciphers behaves as expected} {\n            r CONFIG SET tls-protocols TLSv1.2\n            r CONFIG SET tls-ciphers \"AES128-SHA256:AES256-SHA256\"\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"AES256-SHA256:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            assert_equal \"AES256-SHA256\" [dict get [::tls::status [$s channel]] cipher]\n\n            r CONFIG SET tls-prefer-server-ciphers yes\n\n            set s [redis [srv 0 host] [srv 0 port] 0 1 {-cipher \"AES256-SHA256:AES128-SHA256\"}]\n            catch {$s PING} e\n            assert_match {PONG} $e\n\n            assert_equal \"AES128-SHA256\" [dict get [::tls::status [$s channel]] cipher]\n\n            r CONFIG SET tls-protocols \"\"\n            r CONFIG SET tls-ciphers \"DEFAULT\"\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/tracking.tcl",
    "content": "start_server {tags {\"tracking\"}} {\n    # Create a deferred client we'll use to redirect invalidation\n    # messages to.\n    set rd1 [redis_deferring_client]\n    $rd1 client id\n    set redir [$rd1 read]\n    $rd1 subscribe __redis__:invalidate\n    $rd1 read ; # Consume the SUBSCRIBE reply.\n\n    # Create another client as well in order to test NOLOOP\n    set rd2 [redis_deferring_client]\n\n    test {Clients are able to enable tracking and redirect it} {\n        r CLIENT TRACKING on REDIRECT $redir\n    } {*OK}\n\n    test {The other connection is able to get invalidations} {\n        r SET a 1\n        r GET a\n        r INCR a\n        r INCR b ; # This key should not be notified, since it wasn't fetched.\n        set keys [lindex [$rd1 read] 2]\n        assert {[llength $keys] == 1}\n        assert {[lindex $keys 0] eq {a}}\n    }\n\n    test {The client is now able to disable tracking} {\n        # Make sure to add a few more keys in the tracking list\n        # so that we can check for leaks, as a side effect.\n        r MGET a b c d e f g\n        r CLIENT TRACKING off\n    }\n\n    test {Clients can enable the BCAST mode with the empty prefix} {\n        r CLIENT TRACKING on BCAST REDIRECT $redir\n    } {*OK*}\n\n    test {The connection gets invalidation messages about all the keys} {\n        r MSET a 1 b 2 c 3\n        set keys [lsort [lindex [$rd1 read] 2]]\n        assert {$keys eq {a b c}}\n    }\n\n    test {Clients can enable the BCAST mode with prefixes} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on BCAST REDIRECT $redir PREFIX a: PREFIX b:\n        r MULTI\n        r INCR a:1\n        r INCR a:2\n        r INCR b:1\n        r INCR b:2\n        r EXEC\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys2 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1 {*}$keys2]]\n        assert {$keys eq {a:1 a:2 b:1 b:2}}\n    }\n    \n    test {Adding prefixes to BCAST mode works} {\n        r CLIENT TRACKING on BCAST REDIRECT $redir PREFIX c:\n        r INCR c:1234\n        set keys [lsort [lindex [$rd1 read] 2]]\n        assert {$keys eq {c:1234}}\n    }\n\n    test {Tracking NOLOOP mode in standard mode works} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on REDIRECT $redir NOLOOP\n        r MGET otherkey1 loopkey otherkey2\n        $rd2 SET otherkey1 1; # We should get this\n        r SET loopkey 1 ; # We should not get this\n        $rd2 SET otherkey2 1; # We should get this\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys2 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1 {*}$keys2]]\n        assert {$keys eq {otherkey1 otherkey2}}\n    }\n\n    test {Tracking NOLOOP mode in BCAST mode works} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on BCAST REDIRECT $redir NOLOOP\n        $rd2 SET otherkey1 1; # We should get this\n        r SET loopkey 1 ; # We should not get this\n        $rd2 SET otherkey2 1; # We should get this\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys2 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1 {*}$keys2]]\n        assert {$keys eq {otherkey1 otherkey2}}\n    }\n\n    test {Tracking gets notification of expired keys} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on BCAST REDIRECT $redir NOLOOP\n        r SET mykey myval px 1\n        r SET mykeyotherkey myval ; # We should not get it\n        after 1000\n        # Because of the internals, we know we are going to receive\n        # two separated notifications for the two different prefixes.\n        set keys1 [lsort [lindex [$rd1 read] 2]]\n        set keys [lsort [list {*}$keys1]]\n        assert {$keys eq {mykey}}\n    }\n\n    test {Tracking gets notification on tracking table key eviction} {\n        r CLIENT TRACKING off\n        r CLIENT TRACKING on REDIRECT $redir NOLOOP\n        r MSET key1 1 key2 2\n        # Let the server track the two keys for us\n        r MGET key1 key2\n        # Force the eviction of all the keys but one:\n        r config set tracking-table-max-keys 1\n        # Note that we may have other keys in the table for this client,\n        # since we disabled/enabled tracking multiple time with the same\n        # ID, and tracking does not do ID cleanups for performance reasons.\n        # So we check that eventually we'll receive one or the other key,\n        # otherwise the test will die for timeout.\n        while 1 {\n            set keys [lindex [$rd1 read] 2]\n            if {$keys eq {key1} || $keys eq {key2}} break\n        }\n        # We should receive an expire notification for one of\n        # the two keys (only one must remain)\n        assert {$keys eq {key1} || $keys eq {key2}}\n    }\n\n    $rd1 close\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 __avoid_collisions__[randstring 0 8 alpha]\n            set val __avoid_collisions__[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 __avoid_collisions__[randstring 0 8 alpha]\n            set val __avoid_collisions__[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 an hash table?} {\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 {HINCRBYFLOAT fails against hash value that contains a null-terminator in the middle} {\n        r hset h f \"1\\x002\"\n        catch {r hincrbyfloat h f 1} err\n        set rv {}\n        lappend rv [string match \"ERR*not*float*\" $err]\n    } {1}\n\n    test {HSTRLEN against the small hash} {\n        set err {}\n        foreach k [array names smallhash *] {\n            if {[string length $smallhash($k)] ne [r hstrlen smallhash $k]} {\n                set err \"[string length $smallhash($k)] != [r hstrlen smallhash $k]\"\n                break\n            }\n        }\n        set _ $err\n    } {}\n\n    test {HSTRLEN against the big hash} {\n        set err {}\n        foreach k [array names bighash *] {\n            if {[string length $bighash($k)] ne [r hstrlen bighash $k]} {\n                set err \"[string length $bighash($k)] != [r hstrlen bighash $k]\"\n                puts \"HSTRLEN and logical length mismatch:\"\n                puts \"key: $k\"\n                puts \"Logical content: $bighash($k)\"\n                puts \"Server  content: [r hget bighash $k]\"\n            }\n        }\n        set _ $err\n    } {}\n\n    test {HSTRLEN against non existing field} {\n        set rv {}\n        lappend rv [r hstrlen smallhash __123123123__]\n        lappend rv [r hstrlen bighash __123123123__]\n        set _ $rv\n    } {0 0}\n\n    test {HSTRLEN corner cases} {\n        set vals {\n            -9223372036854775808 9223372036854775807 9223372036854775808\n            {} 0 -1 x\n        }\n        foreach v $vals {\n            r hmset smallhash field $v\n            r hmset bighash field $v\n            set len1 [string length $v]\n            set len2 [r hstrlen smallhash field]\n            set len3 [r hstrlen bighash field]\n            assert {$len1 == $len2}\n            assert {$len2 == $len3}\n        }\n    }\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    # The following test can only be executed if we don't use Valgrind, and if\n    # we are using x86_64 architecture, because:\n    #\n    # 1) Valgrind has floating point limitations, no support for 80 bits math.\n    # 2) Other archs may have the same limits.\n    #\n    # 1.23 cannot be represented correctly with 64 bit doubles, so we skip\n    # the test, since we are only testing pretty printing here and is not\n    # a bug if the program outputs things like 1.299999...\n    if {!$::valgrind && [string match *x86_64* [exec uname -a]]} {\n        test {Test HINCRBYFLOAT for correct float representation (issue #2846)} {\n            r del myhash\n            assert {[r hincrbyfloat myhash float 1.23] eq {1.23}}\n            assert {[r hincrbyfloat myhash float 0.77] eq {2}}\n            assert {[r hincrbyfloat myhash float -0.1] eq {1.9}}\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/type/incr.tcl",
    "content": "start_server {tags {\"incr\"}} {\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 {INCR uses shared objects in the 0-9999 range} {\n        r set foo -1\n        r incr foo\n        assert {[r object refcount foo] > 1}\n        r set foo 9998\n        r incr foo\n        assert {[r object refcount foo] > 1}\n        r incr foo\n        assert {[r object refcount foo] == 1}\n    }\n\n    test {INCR can modify objects in-place} {\n        r set foo 20000\n        r incr foo\n        assert {[r object refcount foo] == 1}\n        set old [lindex [split [r debug object foo]] 1]\n        r incr foo\n        set new [lindex [split [r debug object foo]] 1]\n        assert {[string range $old 0 2] eq \"at:\"}\n        assert {[string range $new 0 2] eq \"at:\"}\n        assert {$old eq $new}\n    }\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 {string to double with null terminator} {\n        r set foo 1\n        r setrange foo 2 2\n        catch {r incrbyfloat foo 1} err\n        format $err\n    } {ERR*valid*}\n\n    test {No negative zero} {\n        r del foo\n        r incrbyfloat foo [expr double(1)/41]\n        r incrbyfloat foo [expr double(-1)/41]\n        r get foo\n    } {0}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/type/list-2.tcl",
    "content": "start_server {\n    tags {\"list\"}\n    overrides {\n        \"list-max-ziplist-size\" 4\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 before_len [llength $mylist]\n                    set before_len_r [r llen mylist]\n                    set mylist [lrange $mylist $min $max]\n                    r ltrim mylist $min $max\n                    assert_equal $mylist [r lrange mylist 0 -1] \"failed trim\"\n\n                    set starting [r llen mylist]\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                        assert_equal $mylist [r lrange mylist 0 -1] \"failed append match\"\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/type/list-3.tcl",
    "content": "start_server {\n    tags {list ziplist}\n    overrides {\n        \"list-max-ziplist-size\" 16\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    test {Regression for quicklist #3343 bug} {\n        r del mylist\n        r lpush mylist 401\n        r lpush mylist 392\n        r rpush mylist [string repeat x 5105]\"799\"\n        r lset mylist -1 [string repeat x 1014]\"702\"\n        r lpop mylist\n        r lset mylist -1 [string repeat x 4149]\"852\"\n        r linsert mylist before 401 [string repeat x 9927]\"12\"\n        r lrange mylist 0 -1\n        r ping ; # It's enough if the server is still alive\n    } {PONG}\n\n    test {Stress tester for #3343-alike bugs} {\n        r del key\n        for {set j 0} {$j < 10000} {incr j} {\n            set op [randomInt 6]\n            set small_signed_count [expr 5-[randomInt 10]]\n            if {[randomInt 2] == 0} {\n                set ele [randomInt 1000]\n            } else {\n                set ele [string repeat x [randomInt 10000]][randomInt 1000]\n            }\n            switch $op {\n                0 {r lpush key $ele}\n                1 {r rpush key $ele}\n                2 {r lpop key}\n                3 {r rpop key}\n                4 {\n                    catch {r lset key $small_signed_count $ele}\n                }\n                5 {\n                    set otherele [randomInt 1000]\n                    if {[randomInt 2] == 0} {\n                        set where before\n                    } else {\n                        set where after\n                    }\n                    r linsert key $where $otherele $ele\n                }\n            }\n        }\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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/tests/unit/type/list.tcl",
    "content": "start_server {\n    tags {\"list\"}\n    overrides {\n        \"list-max-ziplist-size\" 5\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 aa]\n        assert_equal 2 [r rpush myziplist1 bb]\n        assert_equal 3 [r rpush myziplist1 cc]\n        assert_equal 3 [r llen myziplist1]\n        assert_equal aa [r lindex myziplist1 0]\n        assert_equal bb [r lindex myziplist1 1]\n        assert_equal cc [r lindex myziplist1 2]\n        assert_equal {} [r lindex myziplist2 3]\n        assert_equal cc [r rpop myziplist1]\n        assert_equal aa [r lpop myziplist1]\n        assert_encoding quicklist 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 quicklist 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 quicklist 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 quicklist 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} {\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_list {key entries} {\n        r del $key\n        foreach entry $entries { r rpush $key $entry }\n        assert_encoding quicklist $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_list 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_list blist1 \"a $large c\"\n            create_list 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_list 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_list 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    test \"BLPOP when new key is moved into place\" {\n        set rd [redis_deferring_client]\n\n        $rd blpop foo 5\n        r lpush bob abc def hij\n        r rename bob foo\n        $rd read\n    } {foo hij}\n\n    test \"BLPOP when result key is created by SORT..STORE\" {\n        set rd [redis_deferring_client]\n\n        # zero out list from previous test without explicit delete\n        r lpop foo\n        r lpop foo\n        r lpop foo\n\n        $rd blpop foo 5\n        r lpush notfoo hello hola aguacate konichiwa zanzibar\n        r sort notfoo ALPHA store foo\n        $rd read\n    } {foo aguacate}\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            r del blist1\n            $rd $pop blist1 0.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 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_list xlist \"$large c\"\n            assert_equal 3 [r rpushx xlist d]\n            assert_equal 4 [r lpushx xlist a]\n            assert_equal 6 [r rpushx xlist 42 x]\n            assert_equal 9 [r lpushx xlist y3 y2 y1]\n            assert_equal \"y1 y2 y3 a $large c d 42 x\" [r lrange xlist 0 -1]\n        }\n\n        test \"LINSERT - $type\" {\n            create_list xlist \"a $large c d\"\n            assert_equal 5 [r linsert xlist before c zz] \"before c\"\n            assert_equal \"a $large zz c d\" [r lrange xlist 0 10] \"lrangeA\"\n            assert_equal 6 [r linsert xlist after c yy] \"after c\"\n            assert_equal \"a $large zz c yy d\" [r lrange xlist 0 10] \"lrangeB\"\n            assert_equal 7 [r linsert xlist after d dd] \"after d\"\n            assert_equal -1 [r linsert xlist after bad ddd] \"after bad\"\n            assert_equal \"a $large zz c yy d dd\" [r lrange xlist 0 10] \"lrangeC\"\n            assert_equal 8 [r linsert xlist before a aa] \"before a\"\n            assert_equal -1 [r linsert xlist before bad aaa] \"before bad\"\n            assert_equal \"aa a $large zz c yy d dd\" [r lrange xlist 0 10] \"lrangeD\"\n\n            # check inserting integer encoded value\n            assert_equal 9 [r linsert xlist before aa 42] \"before aa\"\n            assert_equal 42 [r lrange xlist 0 0] \"lrangeE\"\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    foreach {type num} {quicklist 250 quicklist 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_list 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 quicklist mylist2\n        }\n\n        test \"RPOPLPUSH with the same list as src and dst - $type\" {\n            create_list 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_list srclist \"a b c $large\"\n                create_list 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 quicklist 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_list 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_list 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} {quicklist 250 quicklist 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_list 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_list 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_list 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_list 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_list 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_list 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_list 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_list 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_list 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": "Chapter07/redis-6.0.3/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 \"SPOP with <count>=1 - $type\" {\n            create_set myset $contents\n            assert_encoding $type myset\n            assert_equal $contents [lsort [list [r spop myset 1] [r spop myset 1] [r spop myset 1]]]\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    foreach {type contents} {\n        hashtable {a b c d e f g h i j k l m n o p q r s t u v w x y z} \n        intset {1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 26 3 4 5 6 7 8 9}\n    } {\n        test \"SPOP with <count>\" {\n            create_set myset $contents\n            assert_encoding $type myset\n            assert_equal $contents [lsort [concat [r spop myset 11] [r spop myset 9] [r spop myset 0] [r spop myset 4] [r spop myset 1] [r spop myset 0] [r spop myset 1] [r spop myset 0]]]\n            assert_equal 0 [r scard myset]\n        }\n    }\n\n    # As seen in intsetRandomMembers\n    test \"SPOP using integers, testing Knuth's and Floyd's algorithm\" {\n        create_set myset {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        assert_encoding intset myset\n        assert_equal 20 [r scard myset]\n        r spop myset 1\n        assert_equal 19 [r scard myset]\n        r spop myset 2\n        assert_equal 17 [r scard myset]\n        r spop myset 3\n        assert_equal 14 [r scard myset]\n        r spop myset 10\n        assert_equal 4 [r scard myset]\n        r spop myset 10\n        assert_equal 0 [r scard myset]\n        r spop myset 1\n        assert_equal 0 [r scard myset]\n    } {}\n\n    test \"SPOP using integers with Knuth's algorithm\" {\n        r spop nonexisting_key 100\n    } {}\n\n    test \"SPOP new implementation: code path #1\" {\n        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        create_set myset $content\n        set res [r spop myset 30]\n        assert {[lsort $content] eq [lsort $res]}\n    }\n\n    test \"SPOP new implementation: code path #2\" {\n        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        create_set myset $content\n        set res [r spop myset 2]\n        assert {[llength $res] == 2}\n        assert {[r scard myset] == 18}\n        set union [concat [r smembers myset] $res]\n        assert {[lsort $union] eq [lsort $content]}\n    }\n\n    test \"SPOP new implementation: code path #3\" {\n        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}\n        create_set myset $content\n        set res [r spop myset 18]\n        assert {[llength $res] == 18}\n        assert {[r scard myset] == 2}\n        set union [concat [r smembers myset] $res]\n        assert {[lsort $union] eq [lsort $content]}\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 0 [r smove myset1 myset1 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": "Chapter07/redis-6.0.3/tests/unit/type/stream-cgroups.tcl",
    "content": "start_server {\n    tags {\"stream\"}\n} {\n    test {XGROUP CREATE: creation and duplicate group name detection} {\n        r DEL mystream\n        r XADD mystream * foo bar\n        r XGROUP CREATE mystream mygroup $\n        catch {r XGROUP CREATE mystream mygroup $} err\n        set err\n    } {BUSYGROUP*}\n\n    test {XGROUP CREATE: automatic stream creation fails without MKSTREAM} {\n        r DEL mystream\n        catch {r XGROUP CREATE mystream mygroup $} err\n        set err\n    } {ERR*}\n\n    test {XGROUP CREATE: automatic stream creation works with MKSTREAM} {\n        r DEL mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n    } {OK}\n\n    test {XREADGROUP will return only new elements} {\n        r XADD mystream * a 1\n        r XADD mystream * b 2\n        # XREADGROUP should return only the new elements \"a 1\" \"b 1\"\n        # and not the element \"foo bar\" which was pre existing in the\n        # stream (see previous test)\n        set reply [\n            r XREADGROUP GROUP mygroup client-1 STREAMS mystream \">\"\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        lindex $reply 0 1 0 1\n    } {a 1}\n\n    test {XREADGROUP can read the history of the elements we own} {\n        # Add a few more elements\n        r XADD mystream * c 3\n        r XADD mystream * d 4\n        # Read a few elements using a different consumer name\n        set reply [\n            r XREADGROUP GROUP mygroup client-2 STREAMS mystream \">\"\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        assert {[lindex $reply 0 1 0 1] eq {c 3}}\n\n        set r1 [r XREADGROUP GROUP mygroup client-1 COUNT 10 STREAMS mystream 0]\n        set r2 [r XREADGROUP GROUP mygroup client-2 COUNT 10 STREAMS mystream 0]\n        assert {[lindex $r1 0 1 0 1] eq {a 1}}\n        assert {[lindex $r2 0 1 0 1] eq {c 3}}\n    }\n\n    test {XPENDING is able to return pending items} {\n        set pending [r XPENDING mystream mygroup - + 10]\n        assert {[llength $pending] == 4}\n        for {set j 0} {$j < 4} {incr j} {\n            set item [lindex $pending $j]\n            if {$j < 2} {\n                set owner client-1\n            } else {\n                set owner client-2\n            }\n            assert {[lindex $item 1] eq $owner}\n            assert {[lindex $item 1] eq $owner}\n        }\n    }\n\n    test {XPENDING can return single consumer items} {\n        set pending [r XPENDING mystream mygroup - + 10 client-1]\n        assert {[llength $pending] == 2}\n    }\n\n    test {XACK is able to remove items from the client/group PEL} {\n        set pending [r XPENDING mystream mygroup - + 10 client-1]\n        set id1 [lindex $pending 0 0]\n        set id2 [lindex $pending 1 0]\n        assert {[r XACK mystream mygroup $id1] eq 1}\n        set pending [r XPENDING mystream mygroup - + 10 client-1]\n        assert {[llength $pending] == 1}\n        set id [lindex $pending 0 0]\n        assert {$id eq $id2}\n        set global_pel [r XPENDING mystream mygroup - + 10]\n        assert {[llength $global_pel] == 3}\n    }\n\n    test {XACK can't remove the same item multiple times} {\n        assert {[r XACK mystream mygroup $id1] eq 0}\n    }\n\n    test {XACK is able to accept multiple arguments} {\n        # One of the IDs was already removed, so it should ack\n        # just ID2.\n        assert {[r XACK mystream mygroup $id1 $id2] eq 1}\n    }\n\n    test {XACK should fail if got at least one invalid ID} {\n        r del mystream\n        r xgroup create s g $ MKSTREAM\n        r xadd s * f1 v1\n        set c [llength [lindex [r xreadgroup group g c streams s >] 0 1]]\n        assert {$c == 1}\n        set pending [r xpending s g - + 10 c]\n        set id1 [lindex $pending 0 0]\n        assert_error \"*Invalid stream ID specified*\" {r xack s g $id1 invalid-id}\n        assert {[r xack s g $id1] eq 1}\n    }\n\n    test {PEL NACK reassignment after XGROUP SETID event} {\n        r del events\n        r xadd events * f1 v1\n        r xadd events * f1 v1\n        r xadd events * f1 v1\n        r xadd events * f1 v1\n        r xgroup create events g1 $\n        r xadd events * f1 v1\n        set c [llength [lindex [r xreadgroup group g1 c1 streams events >] 0 1]]\n        assert {$c == 1}\n        r xgroup setid events g1 -\n        set c [llength [lindex [r xreadgroup group g1 c2 streams events >] 0 1]]\n        assert {$c == 5}\n    }\n\n    test {XREADGROUP will not report data on empty history. Bug #5577} {\n        r del events\n        r xadd events * a 1\n        r xadd events * b 2\n        r xadd events * c 3\n        r xgroup create events mygroup 0\n\n        # Current local PEL should be empty\n        set res [r xpending events mygroup - + 10]\n        assert {[llength $res] == 0}\n\n        # So XREADGROUP should read an empty history as well\n        set res [r xreadgroup group mygroup myconsumer count 3 streams events 0]\n        assert {[llength [lindex $res 0 1]] == 0}\n\n        # We should fetch all the elements in the stream asking for >\n        set res [r xreadgroup group mygroup myconsumer count 3 streams events >]\n        assert {[llength [lindex $res 0 1]] == 3}\n\n        # Now the history is populated with three not acked entries\n        set res [r xreadgroup group mygroup myconsumer count 3 streams events 0]\n        assert {[llength [lindex $res 0 1]] == 3}\n    }\n\n    test {XREADGROUP history reporting of deleted entries. Bug #5570} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        r XADD mystream 1 field1 A\n        r XREADGROUP GROUP mygroup myconsumer STREAMS mystream >\n        r XADD mystream MAXLEN 1 2 field1 B\n        r XREADGROUP GROUP mygroup myconsumer STREAMS mystream >\n\n        # Now we have two pending entries, however one should be deleted\n        # and one should be ok (we should only see \"B\")\n        set res [r XREADGROUP GROUP mygroup myconsumer STREAMS mystream 0-1]\n        assert {[lindex $res 0 1 0] == {1-0 {}}}\n        assert {[lindex $res 0 1 1] == {2-0 {field1 B}}}\n    }\n\n    test {Blocking XREADGROUP will not reply with an empty array} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        r XADD mystream 666 f v\n        set res [r XREADGROUP GROUP mygroup Alice BLOCK 10 STREAMS mystream \">\"]\n        assert {[lindex $res 0 1 0] == {666-0 {f v}}}\n        r XADD mystream 667 f2 v2\n        r XDEL mystream 667\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 10 STREAMS mystream \">\"\n        after 20\n        assert {[$rd read] == {}} ;# before the fix, client didn't even block, but was served synchronously with {mystream {}}\n    }\n\n    test {XGROUP DESTROY should unblock XREADGROUP with -NOGROUP} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 100 STREAMS mystream \">\"\n        r XGROUP DESTROY mystream mygroup\n        assert_error \"*NOGROUP*\" {$rd read}\n    }\n\n    test {RENAME can unblock XREADGROUP with data} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS mystream \">\"\n        r XGROUP CREATE mystream2 mygroup $ MKSTREAM\n        r XADD mystream2 100 f1 v1\n        r RENAME mystream2 mystream\n        assert_equal \"{mystream {{100-0 {f1 v1}}}}\" [$rd read] ;# mystream2 had mygroup before RENAME\n    }\n\n    test {RENAME can unblock XREADGROUP with -NOGROUP} {\n        r del mystream\n        r XGROUP CREATE mystream mygroup $ MKSTREAM\n        set rd [redis_deferring_client]\n        $rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS mystream \">\"\n        r XADD mystream2 100 f1 v1\n        r RENAME mystream2 mystream\n        assert_error \"*NOGROUP*\" {$rd read} ;# mystream2 didn't have mygroup before RENAME\n    }\n\n    test {XCLAIM can claim PEL items from another consumer} {\n        # Add 3 items into the stream, and create a consumer group\n        r del mystream\n        set id1 [r XADD mystream * a 1]\n        set id2 [r XADD mystream * b 2]\n        set id3 [r XADD mystream * c 3]\n        r XGROUP CREATE mystream mygroup 0\n\n        # Client 1 reads item 1 from the stream without acknowledgements.\n        # Client 2 then claims pending item 1 from the PEL of client 1\n        set reply [\n            r XREADGROUP GROUP mygroup client1 count 1 STREAMS mystream >\n        ]\n        assert {[llength [lindex $reply 0 1 0 1]] == 2}\n        assert {[lindex $reply 0 1 0 1] eq {a 1}}\n        r debug sleep 0.2\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id1\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        assert {[lindex $reply 0 1] eq {a 1}}\n\n        # Client 1 reads another 2 items from stream\n        r XREADGROUP GROUP mygroup client1 count 2 STREAMS mystream >\n        r debug sleep 0.2\n\n        # Delete item 2 from the stream. Now client 1 has PEL that contains\n        # only item 3. Try to use client 2 to claim the deleted item 2\n        # from the PEL of client 1, this should return nil\n        r XDEL mystream $id2\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id2\n        ]\n        assert {[llength $reply] == 1}\n        assert_equal \"\" [lindex $reply 0]\n\n        # Delete item 3 from the stream. Now client 1 has PEL that is empty.\n        # Try to use client 2 to claim the deleted item 3 from the PEL\n        # of client 1, this should return nil\n        r debug sleep 0.2\n        r XDEL mystream $id3\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id3\n        ]\n        assert {[llength $reply] == 1}\n        assert_equal \"\" [lindex $reply 0]\n    }\n\n    test {XCLAIM without JUSTID increments delivery count} {\n        # Add 3 items into the stream, and create a consumer group\n        r del mystream\n        set id1 [r XADD mystream * a 1]\n        set id2 [r XADD mystream * b 2]\n        set id3 [r XADD mystream * c 3]\n        r XGROUP CREATE mystream mygroup 0\n\n        # Client 1 reads item 1 from the stream without acknowledgements.\n        # Client 2 then claims pending item 1 from the PEL of client 1\n        set reply [\n            r XREADGROUP GROUP mygroup client1 count 1 STREAMS mystream >\n        ]\n        assert {[llength [lindex $reply 0 1 0 1]] == 2}\n        assert {[lindex $reply 0 1 0 1] eq {a 1}}\n        r debug sleep 0.2\n        set reply [\n            r XCLAIM mystream mygroup client2 10 $id1\n        ]\n        assert {[llength [lindex $reply 0 1]] == 2}\n        assert {[lindex $reply 0 1] eq {a 1}}\n\n        set reply [\n            r XPENDING mystream mygroup - + 10\n        ]\n        assert {[llength [lindex $reply 0]] == 4}\n        assert {[lindex $reply 0 3] == 2}\n\n        # Client 3 then claims pending item 1 from the PEL of client 2 using JUSTID\n        r debug sleep 0.2\n        set reply [\n            r XCLAIM mystream mygroup client3 10 $id1 JUSTID\n        ]\n        assert {[llength $reply] == 1}\n        assert {[lindex $reply 0] eq $id1}\n\n        set reply [\n            r XPENDING mystream mygroup - + 10\n        ]\n        assert {[llength [lindex $reply 0]] == 4}\n        assert {[lindex $reply 0 3] == 2}\n    }\n\n    test {XINFO FULL output} {\n        r del x\n        r XADD x 100 a 1\n        r XADD x 101 b 1\n        r XADD x 102 c 1\n        r XADD x 103 e 1\n        r XADD x 104 f 1\n        r XGROUP CREATE x g1 0\n        r XGROUP CREATE x g2 0\n        r XREADGROUP GROUP g1 Alice COUNT 1 STREAMS x >\n        r XREADGROUP GROUP g1 Bob COUNT 1 STREAMS x >\n        r XREADGROUP GROUP g1 Bob NOACK COUNT 1 STREAMS x >\n        r XREADGROUP GROUP g2 Charlie COUNT 4 STREAMS x >\n        r XDEL x 103\n\n        set reply [r XINFO STREAM x FULL]\n        assert_equal [llength $reply] 12\n        assert_equal [lindex $reply 1] 4 ;# stream length\n        assert_equal [lindex $reply 9] \"{100-0 {a 1}} {101-0 {b 1}} {102-0 {c 1}} {104-0 {f 1}}\" ;# entries\n        assert_equal [lindex $reply 11 0 1] \"g1\" ;# first group name\n        assert_equal [lindex $reply 11 0 7 0 0] \"100-0\" ;# first entry in group's PEL\n        assert_equal [lindex $reply 11 0 9 0 1] \"Alice\" ;# first consumer\n        assert_equal [lindex $reply 11 0 9 0 7 0 0] \"100-0\" ;# first entry in first consumer's PEL\n        assert_equal [lindex $reply 11 1 1] \"g2\" ;# second group name\n        assert_equal [lindex $reply 11 1 9 0 1] \"Charlie\" ;# first consumer\n        assert_equal [lindex $reply 11 1 9 0 7 0 0] \"100-0\" ;# first entry in first consumer's PEL\n        assert_equal [lindex $reply 11 1 9 0 7 1 0] \"101-0\" ;# second entry in first consumer's PEL\n\n        set reply [r XINFO STREAM x FULL COUNT 1]\n        assert_equal [llength $reply] 12\n        assert_equal [lindex $reply 1] 4\n        assert_equal [lindex $reply 9] \"{100-0 {a 1}}\"\n    }\n\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        foreach noack {0 1} {\n            test \"Consumer group last ID propagation to slave (NOACK=$noack)\" {\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                $master del stream\n                $master xadd stream * a 1\n                $master xadd stream * a 2\n                $master xadd stream * a 3\n                $master xgroup create stream mygroup 0\n\n                # Consume the first two items on the master\n                for {set j 0} {$j < 2} {incr j} {\n                    if {$noack} {\n                        set item [$master xreadgroup group mygroup \\\n                                  myconsumer COUNT 1 NOACK STREAMS stream >]\n                    } else {\n                        set item [$master xreadgroup group mygroup \\\n                                  myconsumer COUNT 1 STREAMS stream >]\n                    }\n                    set id [lindex $item 0 1 0 0]\n                    if {$noack == 0} {\n                        assert {[$master xack stream mygroup $id] eq \"1\"}\n                    }\n                }\n\n                wait_for_ofs_sync $master $slave\n\n                # Turn slave into master\n                $slave slaveof no one\n\n                set item [$slave xreadgroup group mygroup myconsumer \\\n                          COUNT 1 STREAMS stream >]\n\n                # The consumed enty should be the third\n                set myentry [lindex $item 0 1 0 1]\n                assert {$myentry eq {a 3}}\n            }\n        }\n    }\n\n    start_server {tags {\"stream\"} overrides {appendonly yes aof-use-rdb-preamble no}} {\n        test {Empty stream with no lastid can be rewrite into AOF correctly} {\n            r XGROUP CREATE mystream group-name $ MKSTREAM\n            assert {[dict get [r xinfo stream mystream] length] == 0}\n            set grpinfo [r xinfo groups mystream]\n            r bgrewriteaof\n            waitForBgrewriteaof r\n            r debug loadaof\n            assert {[dict get [r xinfo stream mystream] length] == 0}\n            assert {[r xinfo groups mystream] == $grpinfo}\n        }\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/type/stream.tcl",
    "content": "# return value is like strcmp() and similar.\nproc streamCompareID {a b} {\n    if {$a eq $b} {return 0}\n    lassign [split $a -] a_ms a_seq\n    lassign [split $b -] b_ms b_seq\n    if {$a_ms > $b_ms} {return 1}\n    if {$a_ms < $b_ms} {return -1}\n    # Same ms case, compare seq.\n    if {$a_seq > $b_seq} {return 1}\n    if {$a_seq < $b_seq} {return -1}\n}\n\n# return the ID immediately greater than the specified one.\n# Note that this function does not care to handle 'seq' overflow\n# since it's a 64 bit value.\nproc streamNextID {id} {\n    lassign [split $id -] ms seq\n    incr seq\n    join [list $ms $seq] -\n}\n\n# Generate a random stream entry ID with the ms part between min and max\n# and a low sequence number (0 - 999 range), in order to stress test\n# XRANGE against a Tcl implementation implementing the same concept\n# with Tcl-only code in a linear array.\nproc streamRandomID {min_id max_id} {\n    lassign [split $min_id -] min_ms min_seq\n    lassign [split $max_id -] max_ms max_seq\n    set delta [expr {$max_ms-$min_ms+1}]\n    set ms [expr {$min_ms+[randomInt $delta]}]\n    set seq [randomInt 1000]\n    return $ms-$seq\n}\n\n# Tcl-side implementation of XRANGE to perform fuzz testing in the Redis\n# XRANGE implementation.\nproc streamSimulateXRANGE {items start end} {\n    set res {}\n    foreach i $items  {\n        set this_id [lindex $i 0]\n        if {[streamCompareID $this_id $start] >= 0} {\n            if {[streamCompareID $this_id $end] <= 0} {\n                lappend res $i\n            }\n        }\n    }\n    return $res\n}\n\nset content {} ;# Will be populated with Tcl side copy of the stream content.\n\nstart_server {\n    tags {\"stream\"}\n} {\n    test {XADD can add entries into a stream that XRANGE can fetch} {\n        r XADD mystream * item 1 value a\n        r XADD mystream * item 2 value b\n        assert_equal 2 [r XLEN mystream]\n        set items [r XRANGE mystream - +]\n        assert_equal [lindex $items 0 1] {item 1 value a}\n        assert_equal [lindex $items 1 1] {item 2 value b}\n    }\n\n    test {XADD IDs are incremental} {\n        set id1 [r XADD mystream * item 1 value a]\n        set id2 [r XADD mystream * item 2 value b]\n        set id3 [r XADD mystream * item 3 value c]\n        assert {[streamCompareID $id1 $id2] == -1}\n        assert {[streamCompareID $id2 $id3] == -1}\n    }\n\n    test {XADD IDs are incremental when ms is the same as well} {\n        r multi\n        r XADD mystream * item 1 value a\n        r XADD mystream * item 2 value b\n        r XADD mystream * item 3 value c\n        lassign [r exec] id1 id2 id3\n        assert {[streamCompareID $id1 $id2] == -1}\n        assert {[streamCompareID $id2 $id3] == -1}\n    }\n\n    test {XADD IDs correctly report an error when overflowing} {\n        r DEL mystream\n        r xadd mystream 18446744073709551615-18446744073709551615 a b\n        assert_error ERR* {r xadd mystream * c d}\n    }\n\n    test {XADD with MAXLEN option} {\n        r DEL mystream\n        for {set j 0} {$j < 1000} {incr j} {\n            if {rand() < 0.9} {\n                r XADD mystream MAXLEN 5 * xitem $j\n            } else {\n                r XADD mystream MAXLEN 5 * yitem $j\n            }\n        }\n        set res [r xrange mystream - +]\n        set expected 995\n        foreach r $res {\n            assert {[lindex $r 1 1] == $expected}\n            incr expected\n        }\n    }\n\n    test {XADD mass insertion and XLEN} {\n        r DEL mystream\n        r multi\n        for {set j 0} {$j < 10000} {incr j} {\n            # From time to time insert a field with a different set\n            # of fields in order to stress the stream compression code.\n            if {rand() < 0.9} {\n                r XADD mystream * item $j\n            } else {\n                r XADD mystream * item $j otherfield foo\n            }\n        }\n        r exec\n\n        set items [r XRANGE mystream - +]\n        for {set j 0} {$j < 10000} {incr j} {\n            assert {[lrange [lindex $items $j 1] 0 1] eq [list item $j]}\n        }\n        assert {[r xlen mystream] == $j}\n    }\n\n    test {XADD with ID 0-0} {\n        r DEL otherstream\n        catch {r XADD otherstream 0-0 k v} err\n        assert {[r EXISTS otherstream] == 0}\n    }\n\n    test {XRANGE COUNT works as expected} {\n        assert {[llength [r xrange mystream - + COUNT 10]] == 10}\n    }\n\n    test {XREVRANGE COUNT works as expected} {\n        assert {[llength [r xrevrange mystream + - COUNT 10]] == 10}\n    }\n\n    test {XRANGE can be used to iterate the whole stream} {\n        set last_id \"-\"\n        set j 0\n        while 1 {\n            set elements [r xrange mystream $last_id + COUNT 100]\n            if {[llength $elements] == 0} break\n            foreach e $elements {\n                assert {[lrange [lindex $e 1] 0 1] eq [list item $j]}\n                incr j;\n            }\n            set last_id [streamNextID [lindex $elements end 0]]\n        }\n        assert {$j == 10000}\n    }\n\n    test {XREVRANGE returns the reverse of XRANGE} {\n        assert {[r xrange mystream - +] == [lreverse [r xrevrange mystream + -]]}\n    }\n\n    test {XREAD with non empty stream} {\n        set res [r XREAD COUNT 1 STREAMS mystream 0-0]\n        assert {[lrange [lindex $res 0 1 0 1] 0 1] eq {item 0}}\n    }\n\n    test {Non blocking XREAD with empty streams} {\n        set res [r XREAD STREAMS s1 s2 0-0 0-0]\n        assert {$res eq {}}\n    }\n\n    test {XREAD with non empty second stream} {\n        set res [r XREAD COUNT 1 STREAMS nostream mystream 0-0 0-0]\n        assert {[lindex $res 0 0] eq {mystream}}\n        assert {[lrange [lindex $res 0 1 0 1] 0 1] eq {item 0}}\n    }\n\n    test {Blocking XREAD waiting new data} {\n        r XADD s2 * old abcd1234\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s1 s2 s3 $ $ $\n        r XADD s2 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test {Blocking XREAD waiting old data} {\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s1 s2 s3 $ 0-0 $\n        r XADD s2 * foo abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {old abcd1234}}\n    }\n\n    test {Blocking XREAD will not reply with an empty array} {\n        r del s1\n        r XADD s1 666 f v\n        r XADD s1 667 f2 v2\n        r XDEL s1 667\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 10 STREAMS s1 666\n        after 20\n        assert {[$rd read] == {}} ;# before the fix, client didn't even block, but was served synchronously with {s1 {}}\n    }\n\n    test \"XREAD: XADD + DEL should not awake client\" {\n        set rd [redis_deferring_client]\n        r del s1\n        $rd XREAD BLOCK 20000 STREAMS s1 $\n        r multi\n        r XADD s1 * old abcd1234\n        r DEL s1\n        r exec\n        r XADD s1 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s1}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test \"XREAD: XADD + DEL + LPUSH should not awake client\" {\n        set rd [redis_deferring_client]\n        r del s1\n        $rd XREAD BLOCK 20000 STREAMS s1 $\n        r multi\n        r XADD s1 * old abcd1234\n        r DEL s1\n        r LPUSH s1 foo bar\n        r exec\n        r DEL s1\n        r XADD s1 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s1}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test {XREAD with same stream name multiple times should work} {\n        r XADD s2 * old abcd1234\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s2 s2 s2 $ $ $\n        r XADD s2 * new abcd1234\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {new abcd1234}}\n    }\n\n    test {XREAD + multiple XADD inside transaction} {\n        r XADD s2 * old abcd1234\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 20000 STREAMS s2 s2 s2 $ $ $\n        r MULTI\n        r XADD s2 * field one\n        r XADD s2 * field two\n        r XADD s2 * field three\n        r EXEC\n        set res [$rd read]\n        assert {[lindex $res 0 0] eq {s2}}\n        assert {[lindex $res 0 1 0 1] eq {field one}}\n        assert {[lindex $res 0 1 1 1] eq {field two}}\n    }\n\n    test {XDEL basic test} {\n        r del somestream\n        r xadd somestream * foo value0\n        set id [r xadd somestream * foo value1]\n        r xadd somestream * foo value2\n        r xdel somestream $id\n        assert {[r xlen somestream] == 2}\n        set result [r xrange somestream - +]\n        assert {[lindex $result 0 1 1] eq {value0}}\n        assert {[lindex $result 1 1 1] eq {value2}}\n    }\n\n    # Here the idea is to check the consistency of the stream data structure\n    # as we remove all the elements down to zero elements.\n    test {XDEL fuzz test} {\n        r del somestream\n        set ids {}\n        set x 0; # Length of the stream\n        while 1 {\n            lappend ids [r xadd somestream * item $x]\n            incr x\n            # Add enough elements to have a few radix tree nodes inside the stream.\n            if {[dict get [r xinfo stream somestream] radix-tree-keys] > 20} break\n        }\n\n        # Now remove all the elements till we reach an empty stream\n        # and after every deletion, check that the stream is sane enough\n        # to report the right number of elements with XRANGE: this will also\n        # force accessing the whole data structure to check sanity.\n        assert {[r xlen somestream] == $x}\n\n        # We want to remove elements in random order to really test the\n        # implementation in a better way.\n        set ids [lshuffle $ids]\n        foreach id $ids {\n            assert {[r xdel somestream $id] == 1}\n            incr x -1\n            assert {[r xlen somestream] == $x}\n            # The test would be too slow calling XRANGE for every iteration.\n            # Do it every 100 removal.\n            if {$x % 100 == 0} {\n                set res [r xrange somestream - +]\n                assert {[llength $res] == $x}\n            }\n        }\n    }\n\n    test {XRANGE fuzzing} {\n        set low_id [lindex $items 0 0]\n        set high_id [lindex $items end 0]\n        for {set j 0} {$j < 100} {incr j} {\n            set start [streamRandomID $low_id $high_id]\n            set end [streamRandomID $low_id $high_id]\n            set range [r xrange mystream $start $end]\n            set tcl_range [streamSimulateXRANGE $items $start $end]\n            if {$range ne $tcl_range} {\n                puts \"*** WARNING *** - XRANGE fuzzing mismatch: $start - $end\"\n                puts \"---\"\n                puts \"XRANGE: '$range'\"\n                puts \"---\"\n                puts \"TCL: '$tcl_range'\"\n                puts \"---\"\n                fail \"XRANGE fuzzing failed, check logs for details\"\n            }\n        }\n    }\n\n    test {XREVRANGE regression test for issue #5006} {\n        # Add non compressed entries\n        r xadd teststream 1234567891230 key1 value1\n        r xadd teststream 1234567891240 key2 value2\n        r xadd teststream 1234567891250 key3 value3\n\n        # Add SAMEFIELD compressed entries\n        r xadd teststream2 1234567891230 key1 value1\n        r xadd teststream2 1234567891240 key1 value2\n        r xadd teststream2 1234567891250 key1 value3\n\n        assert_equal [r xrevrange teststream 1234567891245 -] {{1234567891240-0 {key2 value2}} {1234567891230-0 {key1 value1}}}\n\n        assert_equal [r xrevrange teststream2 1234567891245 -] {{1234567891240-0 {key1 value2}} {1234567891230-0 {key1 value1}}}\n    }\n\n    test {XREAD streamID edge (no-blocking)} {\n        r del x\n        r XADD x 1-1 f v\n        r XADD x 1-18446744073709551615 f v\n        r XADD x 2-1 f v\n        set res [r XREAD BLOCK 0 STREAMS x 1-18446744073709551615]\n        assert {[lindex $res 0 1 0] == {2-1 {f v}}}\n    }\n\n    test {XREAD streamID edge (blocking)} {\n        r del x\n        set rd [redis_deferring_client]\n        $rd XREAD BLOCK 0 STREAMS x 1-18446744073709551615\n        r XADD x 1-1 f v\n        r XADD x 1-18446744073709551615 f v\n        r XADD x 2-1 f v\n        set res [$rd read]\n        assert {[lindex $res 0 1 0] == {2-1 {f v}}}\n    }\n\n    test {XADD streamID edge} {\n        r del x\n        r XADD x 2577343934890-18446744073709551615 f v ;# we need the timestamp to be in the future\n        r XADD x * f2 v2\n        assert_equal [r XRANGE x - +] {{2577343934890-18446744073709551615 {f v}} {2577343934891-0 {f2 v2}}}\n    }\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes}} {\n    test {XADD with MAXLEN > xlen can propagate correctly} {\n        for {set j 0} {$j < 100} {incr j} {\n            r XADD mystream * xitem v\n        }\n        r XADD mystream MAXLEN 200 * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n        r debug loadaof\n        r XADD mystream * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n    }\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes}} {\n    test {XADD with ~ MAXLEN can propagate correctly} {\n        for {set j 0} {$j < 100} {incr j} {\n            r XADD mystream * xitem v\n        }\n        r XADD mystream MAXLEN ~ $j * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n        r config set stream-node-max-entries 1\n        r debug loadaof\n        r XADD mystream * xitem v\n        incr j\n        assert {[r xlen mystream] == $j}\n    }\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes stream-node-max-entries 10}} {\n    test {XTRIM with ~ MAXLEN can propagate correctly} {\n        for {set j 0} {$j < 100} {incr j} {\n            r XADD mystream * xitem v\n        }\n        r XTRIM mystream MAXLEN ~ 85\n        assert {[r xlen mystream] == 90}\n        r config set stream-node-max-entries 1\n        r debug loadaof\n        r XADD mystream * xitem v\n        incr j\n        assert {[r xlen mystream] == 91}\n    }\n}\n\nstart_server {tags {\"stream xsetid\"}} {\n    test {XADD can CREATE an empty stream} {\n        r XADD mystream MAXLEN 0 * a b\n        assert {[dict get [r xinfo stream mystream] length] == 0}\n    }\n\n    test {XSETID can set a specific ID} {\n        r XSETID mystream \"200-0\"\n        assert {[dict get [r xinfo stream mystream] last-generated-id] == \"200-0\"}\n    }\n\n    test {XSETID cannot SETID with smaller ID} {\n        r XADD mystream * a b\n        catch {r XSETID mystream \"1-1\"} err\n        r XADD mystream MAXLEN 0 * a b\n        set err\n    } {ERR*smaller*}\n\n    test {XSETID cannot SETID on non-existent key} {\n        catch {r XSETID stream 1-1} err\n        set _ $err\n    } {ERR no such key}\n}\n\nstart_server {tags {\"stream\"} overrides {appendonly yes aof-use-rdb-preamble no}} {\n    test {Empty stream can be rewrite into AOF correctly} {\n        r XADD mystream MAXLEN 0 * a b\n        assert {[dict get [r xinfo stream mystream] length] == 0}\n        r bgrewriteaof\n        waitForBgrewriteaof r\n        r debug loadaof\n        assert {[dict get [r xinfo stream mystream] length] == 0}\n    }\n\n    test {Stream can be rewrite into AOF correctly after XDEL lastid} {\n        r XSETID mystream 0-0\n        r XADD mystream 1-1 a b\n        r XADD mystream 2-2 a b\n        assert {[dict get [r xinfo stream mystream] length] == 2}\n        r XDEL mystream 2-2\n        r bgrewriteaof\n        waitForBgrewriteaof r\n        r debug loadaof\n        assert {[dict get [r xinfo stream mystream] length] == 1}\n        assert {[dict get [r xinfo stream mystream] last-generated-id] == \"2-2\"}\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/type/string.tcl",
    "content": "start_server {tags {\"string\"}} {\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 {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            r flushdb\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 \"Element at position $x is $val instead of $x\"\n                    break\n                }\n            }\n            set _ $err\n        } {}\n\n        test {DBSIZE should be 10000 now} {\n            r dbsize\n        } {10000}\n    }\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 {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 {GETSET (set new value)} {\n        r del foo\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 {GETRANGE with huge ranges, Github issue #1844} {\n        r set foo bar\n        r getrange foo 0 4294967297\n    } {bar}\n\n    set rna1 {CACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTG}\n    set rna2 {ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}\n    set rnalcs {ACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}\n\n    test {STRALGO LCS string output with STRINGS option} {\n        r STRALGO LCS STRINGS $rna1 $rna2\n    } $rnalcs\n\n    test {STRALGO LCS len} {\n        r STRALGO LCS LEN STRINGS $rna1 $rna2\n    } [string length $rnalcs]\n\n    test {LCS with KEYS option} {\n        r set virus1 $rna1\n        r set virus2 $rna2\n        r STRALGO LCS KEYS virus1 virus2\n    } $rnalcs\n\n    test {LCS indexes} {\n        dict get [r STRALGO LCS IDX KEYS virus1 virus2] matches\n    } {{{238 238} {239 239}} {{236 236} {238 238}} {{229 230} {236 237}} {{224 224} {235 235}} {{1 222} {13 234}}}\n\n    test {LCS indexes with match len} {\n        dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN] matches\n    } {{{238 238} {239 239} 1} {{236 236} {238 238} 1} {{229 230} {236 237} 2} {{224 224} {235 235} 1} {{1 222} {13 234} 222}}\n\n    test {LCS indexes with match len and minimum match len} {\n        dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches\n    } {{{1 222} {13 234} 222}}\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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 \"ZADD with options syntax error with incomplete pair\" {\n            r del ztmp\n            catch {r zadd ztmp xx 10 x 20} err\n            set err\n        } {ERR*}\n\n        test \"ZADD XX option without key - $encoding\" {\n            r del ztmp\n            assert {[r zadd ztmp xx 10 x] == 0}\n            assert {[r type ztmp] eq {none}}\n        }\n\n        test \"ZADD XX existing key - $encoding\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            assert {[r zadd ztmp xx 20 y] == 0}\n            assert {[r zcard ztmp] == 1}\n        }\n\n        test \"ZADD XX returns the number of elements actually added\" {\n            r del ztmp\n            r zadd ztmp 10 x\n            set retval [r zadd ztmp 10 x 20 y 30 z]\n            assert {$retval == 2}\n        }\n\n        test \"ZADD XX updates existing elements score\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            r zadd ztmp xx 5 foo 11 x 21 y 40 zap\n            assert {[r zcard ztmp] == 3}\n            assert {[r zscore ztmp x] == 11}\n            assert {[r zscore ztmp y] == 21}\n        }\n\n        test \"ZADD XX and NX are not compatible\" {\n            r del ztmp\n            catch {r zadd ztmp xx nx 10 x} err\n            set err\n        } {ERR*}\n\n        test \"ZADD NX with non existing key\" {\n            r del ztmp\n            r zadd ztmp nx 10 x 20 y 30 z\n            assert {[r zcard ztmp] == 3}\n        }\n\n        test \"ZADD NX only add new elements without updating old ones\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            assert {[r zadd ztmp nx 11 x 21 y 100 a 200 b] == 2}\n            assert {[r zscore ztmp x] == 10}\n            assert {[r zscore ztmp y] == 20}\n            assert {[r zscore ztmp a] == 100}\n            assert {[r zscore ztmp b] == 200}\n        }\n\n        test \"ZADD INCR works like ZINCRBY\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            r zadd ztmp INCR 15 x\n            assert {[r zscore ztmp x] == 25}\n        }\n\n        test \"ZADD INCR works with a single score-elemenet pair\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            catch {r zadd ztmp INCR 15 x 10 y} err\n            set err\n        } {ERR*}\n\n        test \"ZADD CH option changes return value to all changed elements\" {\n            r del ztmp\n            r zadd ztmp 10 x 20 y 30 z\n            assert {[r zadd ztmp 11 x 21 y 30 z] == 0}\n            assert {[r zadd ztmp ch 12 x 22 y 30 z] == 2}\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            r del ztmp\n            r zadd ztmp 10 a 20 b 30 c\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        test \"ZINCRBY return value\" {\n            r del ztmp\n            set retval [r zincrby ztmp 1.0 x]\n            assert {$retval == 1.0}\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/ZLEXCOUNT 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 \"ZLEXCOUNT advanced\" {\n            create_default_lex_zset\n    \n            assert_equal 9 [r zlexcount zset - +]\n            assert_equal 0 [r zlexcount zset + -]\n            assert_equal 0 [r zlexcount zset + \\[c]\n            assert_equal 0 [r zlexcount zset \\[c -]\n            assert_equal 8 [r zlexcount zset \\[bar +]\n            assert_equal 5 [r zlexcount zset \\[bar \\[foo]\n            assert_equal 4 [r zlexcount zset \\[bar (foo]\n            assert_equal 4 [r zlexcount zset (bar \\[foo]\n            assert_equal 3 [r zlexcount zset (bar (foo]\n            assert_equal 5 [r zlexcount zset - (foo]\n            assert_equal 1 [r zlexcount zset (maxstring +]\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        test \"Basic ZPOP with a single key - $encoding\" {\n            r del zset\n            assert_equal {} [r zpopmin zset]\n            create_zset zset {-1 a 1 b 2 c 3 d 4 e}\n            assert_equal {a -1} [r zpopmin zset]\n            assert_equal {b 1} [r zpopmin zset]\n            assert_equal {e 4} [r zpopmax zset]\n            assert_equal {d 3} [r zpopmax zset]\n            assert_equal {c 2} [r zpopmin zset]\n            assert_equal 0 [r exists zset]\n            r set foo bar\n            assert_error \"*WRONGTYPE*\" {r zpopmin foo}\n        }\n\n        test \"ZPOP with count - $encoding\" {\n            r del z1 z2 z3 foo\n            r set foo bar\n            assert_equal {} [r zpopmin z1 2]\n            assert_error \"*WRONGTYPE*\" {r zpopmin foo 2}\n            create_zset z1 {0 a 1 b 2 c 3 d}\n            assert_equal {a 0 b 1} [r zpopmin z1 2]\n            assert_equal {d 3 c 2} [r zpopmax z1 2]\n        }\n\n        test \"BZPOP with a single existing sorted set - $encoding\" {\n            set rd [redis_deferring_client]\n            create_zset zset {0 a 1 b 2 c}\n\n            $rd bzpopmin zset 5\n            assert_equal {zset a 0} [$rd read]\n            $rd bzpopmin zset 5\n            assert_equal {zset b 1} [$rd read]\n            $rd bzpopmax zset 5\n            assert_equal {zset c 2} [$rd read]\n            assert_equal 0 [r exists zset]\n        }\n\n        test \"BZPOP with multiple existing sorted sets - $encoding\" {\n            set rd [redis_deferring_client]\n            create_zset z1 {0 a 1 b 2 c}\n            create_zset z2 {3 d 4 e 5 f}\n\n            $rd bzpopmin z1 z2 5\n            assert_equal {z1 a 0} [$rd read]\n            $rd bzpopmax z1 z2 5\n            assert_equal {z1 c 2} [$rd read]\n            assert_equal 1 [r zcard z1]\n            assert_equal 3 [r zcard z2]\n\n            $rd bzpopmax z2 z1 5\n            assert_equal {z2 f 5} [$rd read]\n            $rd bzpopmin z2 z1 5\n            assert_equal {z2 d 3} [$rd read]\n            assert_equal 1 [r zcard z1]\n            assert_equal 1 [r zcard z2]\n        }\n\n        test \"BZPOP second sorted set has members - $encoding\" {\n            set rd [redis_deferring_client]\n            r del z1\n            create_zset z2 {3 d 4 e 5 f}\n            $rd bzpopmax z1 z2 5\n            assert_equal {z2 f 5} [$rd read]\n            $rd bzpopmin z2 z1 5\n            assert_equal {z2 d 3} [$rd read]\n            assert_equal 0 [r zcard z1]\n            assert_equal 1 [r zcard z2]\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    test {ZUNIONSTORE result is sorted} {\n        # Create two sets with common and not common elements, perform\n        # the UNION, check that elements are still sorted.\n        r del one two dest\n        set cmd1 [list r zadd one]\n        set cmd2 [list r zadd two]\n        for {set j 0} {$j < 1000} {incr j} {\n            lappend cmd1 [expr rand()] [randomInt 1000]\n            lappend cmd2 [expr rand()] [randomInt 1000]\n        }\n        {*}$cmd1\n        {*}$cmd2\n        assert {[r zcard one] > 100}\n        assert {[r zcard two] > 100}\n        r zunionstore dest 2 one two\n        set oldscore 0\n        foreach {ele score} [r zrange dest 0 -1 withscores] {\n            assert {$score >= $oldscore}\n            set oldscore $score\n        }\n    }\n\n    test \"ZSET commands don't accept the empty strings as valid score\" {\n        assert_error \"*not*float*\" {r zadd myzset \"\" abc}\n    }\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        test \"BZPOPMIN, ZADD + DEL should not awake blocked client\" {\n            set rd [redis_deferring_client]\n            r del zset\n\n            $rd bzpopmin zset 0\n            r multi\n            r zadd zset 0 foo\n            r del zset\n            r exec\n            r del zset\n            r zadd zset 1 bar\n            $rd read\n        } {zset bar 1}\n\n        test \"BZPOPMIN, ZADD + DEL + SET should not awake blocked client\" {\n            set rd [redis_deferring_client]\n            r del list\n\n            r del zset\n\n            $rd bzpopmin zset 0\n            r multi\n            r zadd zset 0 foo\n            r del zset\n            r set zset foo\n            r exec\n            r del zset\n            r zadd zset 1 bar\n            $rd read\n        } {zset bar 1}\n\n        test \"BZPOPMIN with same key multiple times should work\" {\n            set rd [redis_deferring_client]\n            r del z1 z2\n\n            # Data arriving after the BZPOPMIN.\n            $rd bzpopmin z1 z2 z2 z1 0\n            r zadd z1 0 a\n            assert_equal [$rd read] {z1 a 0}\n            $rd bzpopmin z1 z2 z2 z1 0\n            r zadd z2 1 b\n            assert_equal [$rd read] {z2 b 1}\n\n            # Data already there.\n            r zadd z1 0 a\n            r zadd z2 1 b\n            $rd bzpopmin z1 z2 z2 z1 0\n            assert_equal [$rd read] {z1 a 0}\n            $rd bzpopmin z1 z2 z2 z1 0\n            assert_equal [$rd read] {z2 b 1}\n        }\n\n        test \"MULTI/EXEC is isolated from the point of view of BZPOPMIN\" {\n            set rd [redis_deferring_client]\n            r del zset\n            $rd bzpopmin zset 0\n            r multi\n            r zadd zset 0 a\n            r zadd zset 1 b\n            r zadd zset 2 c\n            r exec\n            $rd read\n        } {zset a 0}\n\n        test \"BZPOPMIN with variadic ZADD\" {\n            set rd [redis_deferring_client]\n            r del zset\n            if {$::valgrind} {after 100}\n            $rd bzpopmin zset 0\n            if {$::valgrind} {after 100}\n            assert_equal 2 [r zadd zset -1 foo 1 bar]\n            if {$::valgrind} {after 100}\n            assert_equal {zset foo -1} [$rd read]\n            assert_equal {bar} [r zrange zset 0 -1]\n        }\n\n        test \"BZPOPMIN with zero timeout should block indefinitely\" {\n            set rd [redis_deferring_client]\n            r del zset\n            $rd bzpopmin zset 0\n            after 1000\n            r zadd zset 0 foo\n            assert_equal {zset foo 0} [$rd read]\n        }\n    }\n\n    tags {\"slow\"} {\n        stressers ziplist\n        stressers skiplist\n    }\n\n    test {ZSET skiplist order consistency when elements are moved} {\n        set original_max [lindex [r config get zset-max-ziplist-entries] 1]\n        r config set zset-max-ziplist-entries 0\n        for {set times 0} {$times < 10} {incr times} {\n            r del zset\n            for {set j 0} {$j < 1000} {incr j} {\n                r zadd zset [randomInt 50] ele-[randomInt 10]\n            }\n\n            # Make sure that element ordering is correct\n            set prev_element {}\n            set prev_score -1\n            foreach {element score} [r zrange zset 0 -1 WITHSCORES] {\n                # Assert that elements are in increasing ordering\n                assert {\n                    $prev_score < $score ||\n                    ($prev_score == $score &&\n                     [string compare $prev_element $element] == -1)\n                }\n                set prev_element $element\n                set prev_score $score\n            }\n        }\n        r config set zset-max-ziplist-entries $original_max\n    }\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/tests/unit/wait.tcl",
    "content": "source tests/support/cli.tcl\n\nstart_server {tags {\"wait\"}} {\nstart_server {} {\n    set slave [srv 0 client]\n    set slave_host [srv 0 host]\n    set slave_port [srv 0 port]\n    set master [srv -1 client]\n    set master_host [srv -1 host]\n    set master_port [srv -1 port]\n\n    test {Setup slave} {\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 {WAIT should acknowledge 1 additional copy of the data} {\n        $master set foo 0\n        $master incr foo\n        $master incr foo\n        $master incr foo\n        assert {[$master wait 1 5000] == 1}\n        assert {[$slave get foo] == 3}\n    }\n\n    test {WAIT should not acknowledge 2 additional copies of the data} {\n        $master incr foo\n        assert {[$master wait 2 1000] <= 1}\n    }\n\n    test {WAIT should not acknowledge 1 additional copy if slave is blocked} {\n        set cmd [rediscli $slave_port \"-h $slave_host debug sleep 5\"]\n        exec {*}$cmd > /dev/null 2> /dev/null &\n        after 1000 ;# Give redis-cli the time to execute the command.\n        $master set foo 0\n        $master incr foo\n        $master incr foo\n        $master incr foo\n        assert {[$master wait 1 3000] == 0}\n    }\n}}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/utils/cluster_fail_time.tcl",
    "content": "# This simple script is used in order to estimate the average PFAIL->FAIL\n# state switch after a failure.\n\nset ::sleep_time 10     ; # How much to sleep to trigger PFAIL.\nset ::fail_port 30016   ; # Node to put in sleep.\nset ::other_port 30001  ; # Node to use to monitor the flag switch.\n\nproc avg vector {\n    set sum 0.0\n    foreach x $vector {\n        set sum [expr {$sum+$x}]\n    }\n    expr {$sum/[llength $vector]}\n}\n\nset samples {}\nwhile 1 {\n    exec redis-cli -p $::fail_port debug sleep $::sleep_time > /dev/null &\n\n    # Wait for fail? to appear.\n    while 1 {\n        set output [exec redis-cli -p $::other_port cluster nodes]\n        if {[string match {*fail\\?*} $output]} break\n        after 100\n    }\n\n    puts \"FAIL?\"\n    set start [clock milliseconds]\n\n    # Wait for fail? to disappear.\n    while 1 {\n        set output [exec redis-cli -p $::other_port cluster nodes]\n        if {![string match {*fail\\?*} $output]} break\n        after 100\n    }\n\n    puts \"FAIL\"\n    set now [clock milliseconds]\n    set elapsed [expr {$now-$start}]\n    puts $elapsed\n    lappend samples $elapsed\n\n    puts \"AVG([llength $samples]): [avg $samples]\"\n\n    # Wait for the instance to be available again.\n    exec redis-cli -p $::fail_port ping\n\n    # Wait for the fail flag to be cleared.\n    after 2000\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/corrupt_rdb.c",
    "content": "/* Trivia program to corrupt an RDB file in order to check the RDB check\n * program behavior and effectiveness.\n *\n * Copyright (C) 2016 Salvatore Sanfilippo.\n * This software is released in the 3-clause BSD license. */\n\n#include <stdio.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <time.h>\n\nint main(int argc, char **argv) {\n    struct stat stat;\n    int fd, cycles;\n\n    if (argc != 3) {\n        fprintf(stderr,\"Usage: <filename> <cycles>\\n\");\n        exit(1);\n    }\n\n    srand(time(NULL));\n    char *filename = argv[1];\n    cycles = atoi(argv[2]);\n    fd = open(filename,O_RDWR);\n    if (fd == -1) {\n        perror(\"open\");\n        exit(1);\n    }\n    fstat(fd,&stat);\n\n    while(cycles--) {\n        unsigned char buf[32];\n        unsigned long offset = rand()%stat.st_size;\n        int writelen = 1+rand()%31;\n        int j;\n\n        for (j = 0; j < writelen; j++) buf[j] = (char)rand();\n        lseek(fd,offset,SEEK_SET);\n        printf(\"Writing %d bytes at offset %lu\\n\", writelen, offset);\n        write(fd,buf,writelen);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/create-cluster/.gitignore",
    "content": "config.sh\n*.rdb\n*.aof\n*.conf\n*.log\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/create-cluster/README",
    "content": "Create-custer is a small script used to easily start a big number of Redis\ninstances configured to run in cluster mode. Its main goal is to allow manual\ntesting in a condition which is not easy to replicate with the Redis cluster\nunit tests, for example when a lot of instances are needed in order to trigger\na given bug.\n\nThe tool can also be used just to easily create a number of instances in a\nRedis Cluster in order to experiment a bit with the system.\n\nUSAGE\n---\n\nTo create a cluster, follow these steps:\n\n1. Edit create-cluster and change the start / end port, depending on the\nnumber of instances you want to create.\n2. Use \"./create-cluster start\" in order to run the instances.\n3. Use \"./create-cluster create\" in order to execute redis-cli --cluster create, so that\nan actual Redis cluster will be created. (If you're accessing your setup via a local container, ensure that the CLUSTER_HOST value is changed to your local IP)\n4. Now you are ready to play with the cluster. AOF files and logs for each instances are created in the current directory.\n\nIn order to stop a cluster:\n\n1. Use \"./create-cluster stop\" to stop all the instances. After you stopped the instances you can use \"./create-cluster start\" to restart them if you change your mind.\n2. Use \"./create-cluster clean\" to remove all the AOF / log files to restart with a clean environment.\n\nUse the command \"./create-cluster help\" to get the full list of features.\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/create-cluster/create-cluster",
    "content": "#!/bin/bash\n\n# Settings\nBIN_PATH=\"../../src/\"\nCLUSTER_HOST=127.0.0.1\nPORT=30000\nTIMEOUT=2000\nNODES=6\nREPLICAS=1\nPROTECTED_MODE=yes\nADDITIONAL_OPTIONS=\"\"\n\n# You may want to put the above config parameters into config.sh in order to\n# override the defaults without modifying this script.\n\nif [ -a config.sh ]\nthen\n    source \"config.sh\"\nfi\n\n# Computed vars\nENDPORT=$((PORT+NODES))\n\nif [ \"$1\" == \"start\" ]\nthen\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        echo \"Starting $PORT\"\n        $BIN_PATH/redis-server --port $PORT  --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"create\" ]\nthen\n    HOSTS=\"\"\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        HOSTS=\"$HOSTS $CLUSTER_HOST:$PORT\"\n    done\n    $BIN_PATH/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS\n    exit 0\nfi\n\nif [ \"$1\" == \"stop\" ]\nthen\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        echo \"Stopping $PORT\"\n        $BIN_PATH/redis-cli -p $PORT shutdown nosave\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"watch\" ]\nthen\n    PORT=$((PORT+1))\n    while [ 1 ]; do\n        clear\n        date\n        $BIN_PATH/redis-cli -p $PORT cluster nodes | head -30\n        sleep 1\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"tail\" ]\nthen\n    INSTANCE=$2\n    PORT=$((PORT+INSTANCE))\n    tail -f ${PORT}.log\n    exit 0\nfi\n\nif [ \"$1\" == \"tailall\" ]\nthen\n    tail -f *.log\n    exit 0\nfi\n\nif [ \"$1\" == \"call\" ]\nthen\n    while [ $((PORT < ENDPORT)) != \"0\" ]; do\n        PORT=$((PORT+1))\n        $BIN_PATH/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9\n    done\n    exit 0\nfi\n\nif [ \"$1\" == \"clean\" ]\nthen\n    rm -rf *.log\n    rm -rf appendonly*.aof\n    rm -rf dump*.rdb\n    rm -rf nodes*.conf\n    exit 0\nfi\n\nif [ \"$1\" == \"clean-logs\" ]\nthen\n    rm -rf *.log\n    exit 0\nfi\n\necho \"Usage: $0 [start|create|stop|watch|tail|clean|call]\"\necho \"start       -- Launch Redis Cluster instances.\"\necho \"create      -- Create a cluster using redis-cli --cluster create.\"\necho \"stop        -- Stop Redis Cluster instances.\"\necho \"watch       -- Show CLUSTER NODES output (first 30 lines) of first node.\"\necho \"tail <id>   -- Run tail -f of instance at base port + ID.\"\necho \"tailall     -- Run tail -f for all the log files at once.\"\necho \"clean       -- Remove all instances data, logs, configs.\"\necho \"clean-logs  -- Remove just instances logs.\"\necho \"call <cmd>  -- Call a command (up to 7 arguments) on all nodes.\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/gen-test-certs.sh",
    "content": "#!/bin/bash\nmkdir -p tests/tls\nopenssl genrsa -out tests/tls/ca.key 4096\nopenssl req \\\n    -x509 -new -nodes -sha256 \\\n    -key tests/tls/ca.key \\\n    -days 3650 \\\n    -subj '/O=Redis Test/CN=Certificate Authority' \\\n    -out tests/tls/ca.crt\nopenssl genrsa -out tests/tls/redis.key 2048\nopenssl req \\\n    -new -sha256 \\\n    -key tests/tls/redis.key \\\n    -subj '/O=Redis Test/CN=Server' | \\\n    openssl x509 \\\n        -req -sha256 \\\n        -CA tests/tls/ca.crt \\\n        -CAkey tests/tls/ca.key \\\n        -CAserial tests/tls/ca.txt \\\n        -CAcreateserial \\\n        -days 365 \\\n        -out tests/tls/redis.crt\nopenssl dhparam -out tests/tls/redis.dh 2048\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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  \"cluster\",\n  \"geo\",\n  \"stream\"\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.githubusercontent.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": "Chapter07/redis-6.0.3/utils/graphs/commits-over-time/README.md",
    "content": "This Tcl script is what I used in order to generate the graph you\ncan find at http://antirez.com/news/98. It's really quick & dirty, more\na trow away program than anything else, but probably could be reused or\nmodified in the future in order to visualize other similar data or an\nupdated version of the same data.\n\nThe usage is trivial:\n\n    ./genhtml.tcl > output.html\n\nThe generated HTML is quite broken but good enough to grab a screenshot\nfrom the browser. Feel free to improve it if you got time / interest.\n\nNote that the code filtering the tags, and the hardcoded branch name, does\nnot make the script, as it is, able to analyze a different repository.\nHowever the changes needed are trivial.\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/graphs/commits-over-time/genhtml.tcl",
    "content": "#!/usr/bin/env tclsh\n\n# Load commits history as \"sha1 unixtime\".\nset commits [exec git log unstable {--pretty=\"%H %at\"}]\nset raw_tags [exec git tag]\n\n# Load all the tags that are about stable releases.\nforeach tag $raw_tags {\n    if {[string match v*-stable $tag]} {\n        set tag [string range $tag 1 end-7]\n        puts $tag\n    }\n    if {[regexp {^[0-9]+.[0-9]+.[0-9]+$} $tag]} {\n        lappend tags $tag\n    }\n}\n\n# For each tag, create a list of \"name unixtime\"\nforeach tag $tags {\n    set taginfo [exec git log $tag -n 1 \"--pretty=\\\"$tag %at\\\"\"]\n    set taginfo [string trim $taginfo {\"}]\n    lappend labels $taginfo\n}\n\n# For each commit, check the amount of code changed and create an array\n# mapping the commit to the number of lines affected.\nforeach c $commits {\n    set stat [exec git show --oneline --numstat [lindex $c 0]]\n    set linenum 0\n    set affected 0\n    foreach line [split $stat \"\\n\"] {\n        incr linenum\n        if {$linenum == 1 || [string match *deps/* $line]} continue\n        if {[catch {llength $line} numfields]} continue\n        if {$numfields == 0} continue\n        catch {\n            incr affected [lindex $line 0]\n            incr affected [lindex $line 1]\n        }\n    }\n    set commit_to_affected([lindex $c 0]) $affected\n}\n\nset base_time [lindex [lindex $commits end] 1]\nputs [clock format $base_time]\n\n# Generate a graph made of HTML DIVs.\nputs {<html>\n<style>\n.box {\n    position:absolute;\n    width:10px;\n    height:5px;\n    border:1px black solid;\n    background-color:#44aa33;\n    opacity: 0.04;\n}\n.label {\n    position:absolute;\n    background-color:#dddddd;\n    font-family:helvetica;\n    font-size:12px;\n    padding:2px;\n    color:#666;\n    border:1px #aaa solid;\n    border-radius: 5px;\n}\n#outer {\n    position:relative;\n    width:1500;\n    height:500;\n    border:1px #aaa solid;\n}\n</style>\n<div id=\"outer\">\n}\nforeach c $commits {\n    set sha [lindex $c 0]\n    set t [expr {([lindex $c 1]-$base_time)/(3600*24*2)}]\n    set affected [expr $commit_to_affected($sha)]\n    set left $t\n    set height [expr {log($affected)*20}]\n    puts \"<div class=\\\"box\\\" style=\\\"left:$left; bottom:0; height:$height\\\"></div>\"\n}\n\nset bottom -30\nforeach l $labels {\n    set name [lindex $l 0]\n    set t [expr {([lindex $l 1]-$base_time)/(3600*24*2)}]\n    set left $t\n    if {$left < 0} continue\n    incr bottom -20\n    if  {$bottom == -210} {set bottom -30}\n    puts \"<div class=\\\"label\\\" style=\\\"left:$left; bottom:$bottom\\\">$name</div>\"\n}\nputs {</div></html>}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/hashtable/README",
    "content": "Hash table implementation related utilities.\n\nrehashing.c\n---\n\nVisually show buckets in the two hash tables between rehashings. Also stress\ntest getRandomKeys() implementation, that may actually disappear from\nRedis soon, however visualization some code is reusable in new bugs\ninvestigation.\n\nCompile with:\n\n    cc -I ../../src/ rehashing.c ../../src/zmalloc.c ../../src/dict.c -o rehashing_test\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/hashtable/rehashing.c",
    "content": "#include \"redis.h\"\n#include \"dict.h\"\n\nvoid _redisAssert(char *x, char *y, int l) {\n    printf(\"ASSERT: %s %s %d\\n\",x,y,l);\n    exit(1);\n}\n\nunsigned int dictKeyHash(const void *keyp) {\n    unsigned long key = (unsigned long)keyp;\n    key = dictGenHashFunction(&key,sizeof(key));\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\nint dictKeyCompare(void *privdata, const void *key1, const void *key2) {\n    unsigned long k1 = (unsigned long)key1;\n    unsigned long k2 = (unsigned long)key2;\n    return k1 == k2;\n}\n\ndictType dictTypeTest = {\n    dictKeyHash,                   /* hash function */\n    NULL,                          /* key dup */\n    NULL,                          /* val dup */\n    dictKeyCompare,                /* key compare */\n    NULL,                          /* key destructor */\n    NULL                           /* val destructor */\n};\n\nvoid showBuckets(dictht ht) {\n    if (ht.table == NULL) {\n        printf(\"NULL\\n\");\n    } else {\n        int j;\n        for (j = 0; j < ht.size; j++) {\n            printf(\"%c\", ht.table[j] ? '1' : '0');\n        }\n        printf(\"\\n\");\n    }\n}\n\nvoid show(dict *d) {\n    int j;\n    if (d->rehashidx != -1) {\n        printf(\"rhidx: \");\n        for (j = 0; j < d->rehashidx; j++)\n            printf(\".\");\n        printf(\"|\\n\");\n    }\n    printf(\"ht[0]: \");\n    showBuckets(d->ht[0]);\n    printf(\"ht[1]: \");\n    showBuckets(d->ht[1]);\n    printf(\"\\n\");\n}\n\nint sortPointers(const void *a, const void *b) {\n    unsigned long la, lb;\n\n    la = (long) (*((dictEntry**)a));\n    lb = (long) (*((dictEntry**)b));\n    return la-lb;\n}\n\nvoid stressGetKeys(dict *d, int times, int *perfect_run, int *approx_run) {\n    int j;\n\n    dictEntry **des = zmalloc(sizeof(dictEntry*)*dictSize(d));\n    for (j = 0; j < times; j++) {\n        int requested = rand() % (dictSize(d)+1);\n        int returned = dictGetSomeKeys(d, des, requested);\n        int dup = 0;\n\n        qsort(des,returned,sizeof(dictEntry*),sortPointers);\n        if (returned > 1) {\n            int i;\n            for (i = 0; i < returned-1; i++) {\n                if (des[i] == des[i+1]) dup++;\n            }\n        }\n\n        if (requested == returned && dup == 0) {\n            (*perfect_run)++;\n        } else {\n            (*approx_run)++;\n            printf(\"Requested, returned, duplicated: %d %d %d\\n\",\n                requested, returned, dup);\n        }\n    }\n    zfree(des);\n}\n\n#define MAX1 120\n#define MAX2 1000\nint main(void) {\n    dict *d = dictCreate(&dictTypeTest,NULL);\n    unsigned long i;\n    srand(time(NULL));\n\n    for (i = 0; i < MAX1; i++) {\n        dictAdd(d,(void*)i,NULL);\n        show(d);\n    }\n    printf(\"Size: %d\\n\", (int)dictSize(d));\n\n    for (i = 0; i < MAX1; i++) {\n        dictDelete(d,(void*)i);\n        dictResize(d);\n        show(d);\n    }\n    dictRelease(d);\n\n    d = dictCreate(&dictTypeTest,NULL);\n\n    printf(\"Stress testing dictGetSomeKeys\\n\");\n    int perfect_run = 0, approx_run = 0;\n\n    for (i = 0; i < MAX2; i++) {\n        dictAdd(d,(void*)i,NULL);\n        stressGetKeys(d,100,&perfect_run,&approx_run);\n    }\n\n    for (i = 0; i < MAX2; i++) {\n        dictDelete(d,(void*)i);\n        dictResize(d);\n        stressGetKeys(d,100,&perfect_run,&approx_run);\n    }\n\n    printf(\"dictGetSomeKey, %d perfect runs, %d approximated runs\\n\",\n        perfect_run, approx_run);\n\n    dictRelease(d);\n\n    printf(\"TEST PASSED!\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/hyperloglog/.gitignore",
    "content": "*.txt\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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# Service installer for redis server, runs interactively by default.\n#\n# To run this script non-interactively (for automation/provisioning purposes),\n# feed the variables into the script. Any missing variables will be prompted!\n# Tip: Environment variables also support command substitution (see REDIS_EXECUTABLE)\n#\n# Example:\n#\n# sudo REDIS_PORT=1234 \\\n# \t\t REDIS_CONFIG_FILE=/etc/redis/1234.conf \\\n# \t\t REDIS_LOG_FILE=/var/log/redis_1234.log \\\n# \t\t REDIS_DATA_DIR=/var/lib/redis/1234 \\\n# \t\t REDIS_EXECUTABLE=`command -v redis-server` ./utils/install_server.sh\n#\n# This generates a redis config file and an /etc/init.d script, and installs them.\n#\n# /!\\ This script should be run as root\n#\n# NOTE: This script will not work on Mac OSX.\n#       It supports Debian and Ubuntu Linux.\n#\n################################################################################\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_MANUAL_EXECUTION=false\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#bail if this system is managed by systemd\n_pid_1_exe=\"$(readlink -f /proc/1/exe)\"\nif [ \"${_pid_1_exe##*/}\" = systemd ]\nthen\n\techo \"This systems seems to use systemd.\"\n\techo \"Please take a look at the provided example service unit files in this directory, and adapt and install them. Sorry!\"\n\texit 1\nfi\nunset _pid_1_exe\n\nif ! echo $REDIS_PORT | egrep -q '^[0-9]+$' ; then\n\t_MANUAL_EXECUTION=true\n\t#Read the redis port\n\tread  -p \"Please select the redis port for this instance: [$_REDIS_PORT] \" REDIS_PORT\n\tif ! echo $REDIS_PORT | egrep -q '^[0-9]+$' ; then\n\t\techo \"Selecting default: $_REDIS_PORT\"\n\t\tREDIS_PORT=$_REDIS_PORT\n\tfi\nfi\n\nif [ -z \"$REDIS_CONFIG_FILE\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#read the redis config file\n\t_REDIS_CONFIG_FILE=\"/etc/redis/$REDIS_PORT.conf\"\n\tread -p \"Please select the redis config file name [$_REDIS_CONFIG_FILE] \" REDIS_CONFIG_FILE\n\tif [ -z \"$REDIS_CONFIG_FILE\" ] ; then\n\t\tREDIS_CONFIG_FILE=$_REDIS_CONFIG_FILE\n\t\techo \"Selected default - $REDIS_CONFIG_FILE\"\n\tfi\nfi\n\nif [ -z \"$REDIS_LOG_FILE\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#read the redis log file path\n\t_REDIS_LOG_FILE=\"/var/log/redis_$REDIS_PORT.log\"\n\tread -p \"Please select the redis log file name [$_REDIS_LOG_FILE] \" REDIS_LOG_FILE\n\tif [ -z \"$REDIS_LOG_FILE\" ] ; then\n\t\tREDIS_LOG_FILE=$_REDIS_LOG_FILE\n\t\techo \"Selected default - $REDIS_LOG_FILE\"\n\tfi\nfi\n\nif [ -z \"$REDIS_DATA_DIR\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#get the redis data directory\n\t_REDIS_DATA_DIR=\"/var/lib/redis/$REDIS_PORT\"\n\tread -p \"Please select the data directory for this instance [$_REDIS_DATA_DIR] \" REDIS_DATA_DIR\n\tif [ -z \"$REDIS_DATA_DIR\" ] ; then\n\t\tREDIS_DATA_DIR=$_REDIS_DATA_DIR\n\t\techo \"Selected default - $REDIS_DATA_DIR\"\n\tfi\nfi\n\nif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t_MANUAL_EXECUTION=true\n\t#get the redis executable path\n\t_REDIS_EXECUTABLE=`command -v redis-server`\n\tread -p \"Please select the redis executable path [$_REDIS_EXECUTABLE] \" REDIS_EXECUTABLE\n\tif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t\tREDIS_EXECUTABLE=$_REDIS_EXECUTABLE\n\n\t\tif [ ! -x \"$REDIS_EXECUTABLE\" ] ; then\n\t\t\techo \"Mmmmm...  it seems like you don't have a redis executable. Did you run make install yet?\"\n\t\t\texit 1\n\t\tfi\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\nif $_MANUAL_EXECUTION == true ; then\n\tread -p \"Is this ok? Then press ENTER to go on or Ctrl-C to abort.\" _UNUSED_\nfi\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 .\\+#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 \"$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": "Chapter07/redis-6.0.3/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 the file server.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\nYou can optionally specify a number of times to run, so that the program\nwill output averages of different runs, by adding an additional argument.\nFor instance in order to run the test 10 times use:\n\n    ruby test-lru.rb /tmp/lru.html 10\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/lru/lfu-simulation.c",
    "content": "#include <stdio.h>\n#include <time.h>\n#include <stdint.h>\n#include <stdlib.h>\n\nint decr_every = 1;\nint keyspace_size = 1000000;\ntime_t switch_after = 30; /* Switch access pattern after N seconds. */\n\nstruct entry {\n    /* Field that the LFU Redis implementation will have (we have\n     * 24 bits of total space in the object->lru field). */\n    uint8_t counter;    /* Logarithmic counter. */\n    uint16_t decrtime;  /* (Reduced precision) time of last decrement. */\n\n    /* Fields only useful for visualization. */\n    uint64_t hits;      /* Number of real accesses. */\n    time_t ctime;       /* Key creation time. */\n};\n\n#define to_16bit_minutes(x) ((x/60) & 65535)\n#define COUNTER_INIT_VAL 5\n\n/* Compute the difference in minutes between two 16 bit minutes times\n * obtained with to_16bit_minutes(). Since they can wrap around if\n * we detect the overflow we account for it as if the counter wrapped\n * a single time. */\nuint16_t minutes_diff(uint16_t now, uint16_t prev) {\n    if (now >= prev) return now-prev;\n    return 65535-prev+now;\n}\n\n/* Increment a couter logaritmically: the greatest is its value, the\n * less likely is that the counter is really incremented.\n * The maximum value of the counter is saturated at 255. */\nuint8_t log_incr(uint8_t counter) {\n    if (counter == 255) return counter;\n    double r = (double)rand()/RAND_MAX;\n    double baseval = counter-COUNTER_INIT_VAL;\n    if (baseval < 0) baseval = 0;\n    double limit = 1.0/(baseval*10+1);\n    if (r < limit) counter++;\n    return counter;\n}\n\n/* Simulate an access to an entry. */\nvoid access_entry(struct entry *e) {\n    e->counter = log_incr(e->counter);\n    e->hits++;\n}\n\n/* Return the entry LFU value and as a side effect decrement the\n * entry value if the decrement time was reached. */\nuint8_t scan_entry(struct entry *e) {\n    if (minutes_diff(to_16bit_minutes(time(NULL)),e->decrtime)\n        >= decr_every)\n    {\n        if (e->counter) {\n            if (e->counter > COUNTER_INIT_VAL*2) {\n                e->counter /= 2;\n            } else {\n                e->counter--;\n            }\n        }\n        e->decrtime = to_16bit_minutes(time(NULL));\n    }\n    return e->counter;\n}\n\n/* Print the entry info. */\nvoid show_entry(long pos, struct entry *e) {\n    char *tag = \"normal       \";\n\n    if (pos >= 10 && pos <= 14) tag = \"new no access\";\n    if (pos >= 15 && pos <= 19) tag = \"new accessed \";\n    if (pos >= keyspace_size -5) tag= \"old no access\";\n\n    printf(\"%ld] <%s> frequency:%d decrtime:%d [%lu hits | age:%ld sec]\\n\",\n        pos, tag, e->counter, e->decrtime, (unsigned long)e->hits,\n            time(NULL) - e->ctime);\n}\n\nint main(void) {\n    time_t start = time(NULL);\n    time_t new_entry_time = start;\n    time_t display_time = start;\n    struct entry *entries = malloc(sizeof(*entries)*keyspace_size);\n    long j;\n\n    /* Initialize. */\n    for (j = 0; j < keyspace_size; j++) {\n        entries[j].counter = COUNTER_INIT_VAL;\n        entries[j].decrtime = to_16bit_minutes(start);\n        entries[j].hits = 0;\n        entries[j].ctime = time(NULL);\n    }\n\n    while(1) {\n        time_t now = time(NULL);\n        long idx;\n\n        /* Scan N random entries (simulates the eviction under maxmemory). */\n        for (j = 0; j < 3; j++) {\n            scan_entry(entries+(rand()%keyspace_size));\n        }\n\n        /* Access a random entry: use a power-law access pattern up to\n         * 'switch_after' seconds. Then revert to flat access pattern. */\n        if (now-start < switch_after) {\n            /* Power law. */\n            idx = 1;\n            while((rand() % 21) != 0 && idx < keyspace_size) idx *= 2;\n            if (idx > keyspace_size) idx = keyspace_size;\n            idx = rand() % idx;\n        } else {\n            /* Flat. */\n            idx = rand() % keyspace_size;\n        }\n\n        /* Never access entries between position 10 and 14, so that\n         * we simulate what happens to new entries that are never\n         * accessed VS new entries which are accessed in positions\n         * 15-19.\n         *\n         * Also never access last 5 entry, so that we have keys which\n         * are never recreated (old), and never accessed. */\n        if ((idx < 10 || idx > 14) && (idx < keyspace_size-5))\n            access_entry(entries+idx);\n\n        /* Simulate the addition of new entries at positions between\n         * 10 and 19, a random one every 10 seconds. */\n        if (new_entry_time <= now) {\n            idx = 10+(rand()%10);\n            entries[idx].counter = COUNTER_INIT_VAL;\n            entries[idx].decrtime = to_16bit_minutes(time(NULL));\n            entries[idx].hits = 0;\n            entries[idx].ctime = time(NULL);\n            new_entry_time = now+10;\n        }\n\n        /* Show the first 20 entries and the last 20 entries. */\n        if (display_time != now) {\n            printf(\"=============================\\n\");\n            printf(\"Current minutes time: %d\\n\", (int)to_16bit_minutes(now));\n            printf(\"Access method: %s\\n\",\n                (now-start < switch_after) ? \"power-law\" : \"flat\");\n\n            for (j = 0; j < 20; j++)\n                show_entry(j,entries+j);\n\n            for (j = keyspace_size-20; j < keyspace_size; j++)\n                show_entry(j,entries+j);\n            display_time = now;\n        }\n    }\n    return 0;\n}\n\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/lru/test-lru.rb",
    "content": "require 'rubygems'\nrequire 'redis'\n\n$runs = []; # Remember the error rate of each run for average purposes.\n$o = {};    # Options set parsing arguments\n\ndef testit(filename)\n    r = Redis.new\n    r.config(\"SET\",\"maxmemory\",\"2000000\")\n    if $o[:ttl]\n        r.config(\"SET\",\"maxmemory-policy\",\"volatile-ttl\")\n    else\n        r.config(\"SET\",\"maxmemory-policy\",\"allkeys-lru\")\n    end\n    r.config(\"SET\",\"maxmemory-samples\",5)\n    r.config(\"RESETSTAT\")\n    r.flushall\n\n    html = \"\"\n    html << <<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    .otherdb {\n        border: 1px red solid;\n    }\n\n    .ex {\n        background-color: #666;\n    }\n    </style>\n    <pre>\nEOF\n\n    # Fill the DB up to the first eviction.\n    oldsize = r.dbsize\n    id = 0\n    while true\n        id += 1\n        begin\n            r.set(id,\"foo\")\n        rescue\n            break\n        end\n        newsize = r.dbsize\n        break if newsize == oldsize # A key was evicted? Stop.\n        oldsize = newsize\n    end\n\n    inserted = r.dbsize\n    first_set_max_id = id\n    html << \"#{r.dbsize} keys inserted.\\n\"\n\n    # Access keys sequentially, so that in theory the first part will be expired\n    # and the latter part will not, according to perfect LRU.\n\n    if $o[:ttl]\n        STDERR.puts \"Set increasing expire value\"\n        (1..first_set_max_id).each{|id|\n            r.expire(id,1000+id)\n            STDERR.print(\".\") if (id % 150) == 0\n        }\n    else\n        STDERR.puts \"Access keys sequentially\"\n        (1..first_set_max_id).each{|id|\n            r.get(id)\n            sleep 0.001\n            STDERR.print(\".\") if (id % 150) == 0\n        }\n    end\n    STDERR.puts\n\n    # Insert more 50% keys. We expect that the new keys will rarely be expired\n    # since their last access time is recent compared to the others.\n    #\n    # Note that we insert the first 100 keys of the new set into DB1 instead\n    # of DB0, so that we can try how cross-DB eviction works.\n    half = inserted/2\n    html << \"Insert enough keys to evict half the keys we inserted.\\n\"\n    add = 0\n\n    otherdb_start_idx = id+1\n    otherdb_end_idx = id+100\n    while true\n        add += 1\n        id += 1\n        if id >= otherdb_start_idx && id <= otherdb_end_idx\n            r.select(1)\n            r.set(id,\"foo\")\n            r.select(0)\n        else\n            r.set(id,\"foo\")\n        end\n        break if r.info['evicted_keys'].to_i >= half\n    end\n\n    html << \"#{add} additional keys added.\\n\"\n    html << \"#{r.dbsize} keys in DB.\\n\"\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\n    errors = 0\n    e = 1\n    error_per_key = 100000.0/first_set_max_id\n    half_set_size = first_set_max_id/2\n    maxerr = 0\n    (1..(first_set_max_id/2)).each{|id|\n        if id >= otherdb_start_idx && id <= otherdb_end_idx\n            r.select(1)\n            exists = r.exists(id)\n            r.select(0)\n        else\n            exists = r.exists(id)\n        end\n        if id < first_set_max_id/2\n            thiserr = error_per_key * ((half_set_size-id).to_f/half_set_size)\n            maxerr += thiserr\n            errors += thiserr if exists\n        elsif id >= first_set_max_id/2\n            thiserr = error_per_key * ((id-half_set_size).to_f/half_set_size)\n            maxerr += thiserr\n            errors += thiserr if !exists\n        end\n    }\n    errors = errors*100/maxerr\n\n    STDERR.puts \"Test finished with #{errors}% error! Generating HTML on stdout.\"\n\n    html << \"#{errors}% error!\\n\"\n    html << \"</pre>\"\n    $runs << errors\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 >= otherdb_start_idx && id <= otherdb_end_idx\n            c << \" otherdb\"\n        elsif id <= first_set_max_id\n            c << \" old\"\n        else\n            c << \" new\"\n        end\n\n        # Add class if exists\n        if id >= otherdb_start_idx && id <= otherdb_end_idx\n            r.select(1)\n            exists = r.exists(id)\n            r.select(0)\n        else\n            exists = r.exists(id)\n        end\n\n        c << \" ex\" if exists\n        html << \"<div title=\\\"#{id}\\\" class=\\\"#{c}\\\"></div>\"\n    }\n\n    # Close HTML page\n\n    html << <<EOF\n    </body>\n    </html>\nEOF\n\n    f = File.open(filename,\"w\")\n    f.write(html)\n    f.close\nend\n\ndef print_avg\n    avg = ($runs.reduce {|a,b| a+b}) / $runs.length\n    puts \"#{$runs.length} runs, AVG is #{avg}\"\nend\n\nif ARGV.length < 1\n    STDERR.puts \"Usage: ruby test-lru.rb <html-output-filename> [--runs <count>] [--ttl]\"\n    STDERR.puts \"Options:\"\n    STDERR.puts \"  --runs <count>    Execute the test <count> times.\"\n    STDERR.puts \"  --ttl             Set keys with increasing TTL values\"\n    STDERR.puts \"                    (starting from 1000 seconds) in order to\"\n    STDERR.puts \"                    test the volatile-lru policy.\"\n    exit 1\nend\n\nfilename = ARGV[0]\n$o[:numruns] = 1\n\n# Options parsing\ni = 1\nwhile i < ARGV.length\n    if ARGV[i] == '--runs'\n        $o[:numruns] = ARGV[i+1].to_i\n        i+= 1\n    elsif ARGV[i] == '--ttl'\n        $o[:ttl] = true\n    else\n        STDERR.puts \"Unknown option #{ARGV[i]}\"\n        exit 1\n    end\n    i+= 1\nend\n\n$o[:numruns].times {\n    testit(filename)\n    print_avg if $o[:numruns] != 1\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/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\n### BEGIN INIT INFO\n# Provides:     redis_6379\n# Default-Start:        2 3 4 5\n# Default-Stop:         0 1 6\n# Short-Description:    Redis data structure server\n# Description:          Redis data structure server. See https://redis.io\n### END INIT INFO\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": "Chapter07/redis-6.0.3/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        PID=$(cat $PIDFILE)\n        if [ ! -x /proc/${PID} ]\n        then\n            echo 'Redis is not running'\n        else\n            echo \"Redis is running ($PID)\"\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": "Chapter07/redis-6.0.3/utils/releasetools/01_create_tarball.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}\"\ncd ~/hack/redis\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": "Chapter07/redis-6.0.3/utils/releasetools/02_upload_tarball.sh",
    "content": "#!/bin/bash\necho \"Uploading...\"\nscp /tmp/redis-${1}.tar.gz antirez@antirez.com:/var/virtual/download.redis.io/httpdocs/releases/\necho \"Updating web site... (press any key if it is a stable release, or Ctrl+C)\"\nread x\nssh antirez@antirez.com \"cd /var/virtual/download.redis.io/httpdocs; ./update.sh ${1}\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/releasetools/03_test_release.sh",
    "content": "#!/bin/sh\nif [ $# != \"1\" ]\nthen\n    echo \"Usage: ${0} <git-ref>\"\n    exit 1\nfi\n\nTAG=$1\nTARNAME=\"redis-${TAG}.tar.gz\"\nDOWNLOADURL=\"http://download.redis.io/releases/${TARNAME}\"\n\nssh antirez@metal \"export TERM=xterm;\n                   cd /tmp;\n                   rm -rf test_release_tmp_dir;\n                   cd test_release_tmp_dir;\n                   rm -f $TARNAME;\n                   rm -rf redis-${TAG};\n                   wget $DOWNLOADURL;\n                   tar xvzf $TARNAME;\n                   cd redis-${TAG};\n                   make;\n                   ./runtest;\n                   ./runtest-sentinel;\n                   if [ -x runtest-cluster ]; then\n                       ./runtest-cluster;\n                   fi\"\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/releasetools/04_release_hash.sh",
    "content": "#!/bin/bash\nSHA=$(curl -s http://download.redis.io/releases/redis-${1}.tar.gz | shasum -a 256 | cut -f 1 -d' ')\nENTRY=\"hash redis-${1}.tar.gz sha256 $SHA http://download.redis.io/releases/redis-${1}.tar.gz\"\necho $ENTRY >> ~/hack/redis-hashes/README\nvi ~/hack/redis-hashes/README\necho \"Press any key to commit, Ctrl-C to abort).\"\nread yes\n(cd ~/hack/redis-hashes; git commit -a -m \"${1} hash.\"; git push)\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/releasetools/changelog.tcl",
    "content": "#!/usr/bin/env tclsh\n\nif {[llength $::argv] != 2 && [llength $::argv] != 3} {\n    puts \"Usage: $::argv0 <branch> <version> \\[<num-commits>\\]\"\n    exit 1\n}\n\nset branch [lindex $::argv 0]\nset ver [lindex $::argv 1]\nif {[llength $::argv] == 3} {\n    set count [lindex ::$argv 2]\n} else {\n    set count 100\n}\n\nset template {\n================================================================================\nRedis %ver%     Released %date%\n================================================================================\n\nUpgrade urgency <URGENCY>: <DESCRIPTION>\n}\n\nset template [string trim $template]\nappend template \"\\n\\n\"\nset date [clock format [clock seconds]]\nset template [string map [list %ver% $ver %date% $date] $template]\n\nappend template [exec git log $branch~$count..$branch \"--format=format:%an in commit %h:%n %s\" --shortstat]\n\n#Older, more verbose version.\n#\n#append template [exec git log $branch~30..$branch \"--format=format:+-------------------------------------------------------------------------------%n| %s%n| By %an, %ai%n+--------------------------------------------------------------------------------%nhttps://github.com/antirez/redis/commit/%H%n%n%b\" --stat]\n\nputs $template\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/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": "Chapter07/redis-6.0.3/utils/srandmember/README.md",
    "content": "The utilities in this directory plot the distribution of SRANDMEMBER to\nevaluate how fair it is.\n\nSee http://theshfl.com/redis_sets for more information on the topic that lead\nto such investigation fix.\n\nshowdist.rb -- shows the distribution of the frequency elements are returned.\n               The x axis is the number of times elements were returned, and\n               the y axis is how many elements were returned with such\n               frequency.\n\nshowfreq.rb -- shows the frequency each element was returned.\n               The x axis is the element number.\n               The y axis is the times it was returned.\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/srandmember/showdist.rb",
    "content": "require 'redis'\n\nr = Redis.new\nr.select(9)\nr.del(\"myset\");\nr.sadd(\"myset\",(0..999).to_a)\nfreq = {}\n100.times {\n    res = r.pipelined {\n        1000.times {\n            r.srandmember(\"myset\")\n        }\n    }\n    res.each{|ele|\n        freq[ele] = 0 if freq[ele] == nil\n        freq[ele] += 1\n    }\n}\n\n# Convert into frequency distribution\ndist = {}\nfreq.each{|item,count|\n    dist[count] = 0 if dist[count] == nil\n    dist[count] += 1\n}\n\nmin = dist.keys.min\nmax = dist.keys.max\n(min..max).each{|x|\n    count = dist[x]\n    count = 0 if count == nil\n    puts \"#{x} -> #{\"*\"*count}\"\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/srandmember/showfreq.rb",
    "content": "require 'redis'\n\nr = Redis.new\nr.select(9)\nr.del(\"myset\");\nr.sadd(\"myset\",(0..999).to_a)\nfreq = {}\n500.times {\n    res = r.pipelined {\n        1000.times {\n            r.srandmember(\"myset\")\n        }\n    }\n    res.each{|ele|\n        freq[ele] = 0 if freq[ele] == nil\n        freq[ele] += 1\n    }\n}\n\n# Print the frequency each element was yeld to process it with gnuplot\nfreq.each{|item,count|\n    puts \"#{item} #{count}\"\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/systemd-redis_multiple_servers@.service",
    "content": "# example systemd template service unit file for multiple redis-servers\n#\n# You can use this file as a blueprint for your actual template service unit\n# file, if you intend to run multiple independent redis-server instances in\n# parallel using systemd's \"template unit files\" feature. If you do, you will\n# want to choose a better basename for your service unit by renaming this file\n# when copying it.\n#\n# Please take a look at the provided \"systemd-redis_server.service\" example\n# service unit file, too, if you choose to use this approach at managing\n# multiple redis-server instances via systemd.\n\n[Unit]\nDescription=Redis data structure server - instance %i\nDocumentation=https://redis.io/documentation\n# This template unit assumes your redis-server configuration file(s)\n# to live at /etc/redis/redis_server_<INSTANCE_NAME>.conf\nAssertPathExists=/etc/redis/redis_server_%i.conf\n#Before=your_application.service another_example_application.service\n#AssertPathExists=/var/lib/redis\n\n[Service]\nExecStart=/usr/local/bin/redis-server /etc/redis/redis_server_%i.conf\nLimitNOFILE=10032\nNoNewPrivileges=yes\n#OOMScoreAdjust=-900\n#PrivateTmp=yes\nType=notify\nTimeoutStartSec=infinity\nTimeoutStopSec=infinity\nUMask=0077\n#User=redis\n#Group=redis\n#WorkingDirectory=/var/lib/redis\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/systemd-redis_server.service",
    "content": "# example systemd service unit file for redis-server\n#\n# In order to use this as a template for providing a redis service in your\n# environment, _at the very least_ make sure to adapt the redis configuration\n# file you intend to use as needed (make sure to set \"supervised systemd\"), and\n# to set sane TimeoutStartSec and TimeoutStopSec property values in the unit's\n# \"[Service]\" section to fit your needs.\n#\n# Some properties, such as User= and Group=, are highly desirable for virtually\n# all deployments of redis, but cannot be provided in a manner that fits all\n# expectable environments. Some of these properties have been commented out in\n# this example service unit file, but you are highly encouraged to set them to\n# fit your needs.\n#\n# Please refer to systemd.unit(5), systemd.service(5), and systemd.exec(5) for\n# more information.\n\n[Unit]\nDescription=Redis data structure server\nDocumentation=https://redis.io/documentation\n#Before=your_application.service another_example_application.service\n#AssertPathExists=/var/lib/redis\n\n[Service]\nExecStart=/usr/local/bin/redis-server --supervised systemd --daemonize no\n## Alternatively, have redis-server load a configuration file:\n#ExecStart=/usr/local/bin/redis-server /path/to/your/redis.conf\nLimitNOFILE=10032\nNoNewPrivileges=yes\n#OOMScoreAdjust=-900\n#PrivateTmp=yes\nType=notify\nTimeoutStartSec=infinity\nTimeoutStopSec=infinity\nUMask=0077\n#User=redis\n#Group=redis\n#WorkingDirectory=/var/lib/redis\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/tracking_collisions.c",
    "content": "/* This is a small program used in order to understand the collison rate\n * of CRC64 (ISO version) VS other stronger hashing functions in the context\n * of hashing keys for the Redis \"tracking\" feature (client side caching\n * assisted by the server).\n *\n * The program attempts to hash keys with common names in the form of\n *\n *  prefix:<counter>\n *\n * And counts the resulting collisons generated in the 24 bits of output\n * needed for the tracking feature invalidation table (16 millions + entries)\n *\n * Compile with:\n *\n *  cc -O2 ./tracking_collisions.c ../src/crc64.c ../src/sha1.c\n *  ./a.out\n *\n * --------------------------------------------------------------------------\n *\n * Copyright (C) 2019 Salvatore Sanfilippo\n * This code is released under the BSD 2 clause license.\n */\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdio.h>\n#include \"../src/crc64.h\"\n#include \"../src/sha1.h\"\n\n#define TABLE_SIZE (1<<24)\nint Table[TABLE_SIZE];\n\nuint64_t crc64Hash(char *key, size_t len) {\n    return crc64(0,(unsigned char*)key,len);\n}\n\nuint64_t sha1Hash(char *key, size_t len) {\n    SHA1_CTX ctx;\n    unsigned char hash[20];\n\n    SHA1Init(&ctx);\n    SHA1Update(&ctx,(unsigned char*)key,len);\n    SHA1Final(hash,&ctx);\n    uint64_t hash64;\n    memcpy(&hash64,hash,sizeof(hash64));\n    return hash64;\n}\n\n/* Test the hashing function provided as callback and return the\n * number of collisions found. */\nunsigned long testHashingFunction(uint64_t (*hash)(char *, size_t)) {\n    unsigned long collisions = 0;\n    memset(Table,0,sizeof(Table));\n    char *prefixes[] = {\"object\", \"message\", \"user\", NULL};\n    for (int i = 0; prefixes[i] != NULL; i++) {\n        for (int j = 0; j < TABLE_SIZE/2; j++) {\n            char keyname[128];\n            size_t keylen = snprintf(keyname,sizeof(keyname),\"%s:%d\",\n                                     prefixes[i],j);\n            uint64_t bucket = hash(keyname,keylen) % TABLE_SIZE;\n            if (Table[bucket]) {\n                collisions++;\n            } else {\n                Table[bucket] = 1;\n            }\n        }\n    }\n    return collisions;\n}\n\nint main(void) {\n    printf(\"SHA1 : %lu\\n\", testHashingFunction(sha1Hash));\n    printf(\"CRC64: %lu\\n\", testHashingFunction(crc64Hash));\n    return 0;\n}\n"
  },
  {
    "path": "Chapter07/redis-6.0.3/utils/whatisdoing.sh",
    "content": "# This script is from http://poormansprofiler.org/\n#\n# NOTE: Instead of using this script, you should use the Redis\n# Software Watchdog, which provides a similar functionality but in\n# a more reliable / easy to use way.\n#\n# Check http://redis.io/topics/latency for more information.\n\n#!/bin/bash\nnsamples=1\nsleeptime=0\npid=$(ps auxww | grep '[r]edis-server' | awk '{print $2}')\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"
  },
  {
    "path": "Chapter09/code/AsyncLogger/AsyncLogger.cpp",
    "content": "/**\r\n *@desc: AsyncLogger.cpp\r\n *@author: zhangyl\r\n *@date: 2018.11.10\r\n */\r\n#include \"stdafx.h\"\r\n#include <thread>\r\n#include <mutex>\r\n#include <condition_variable>\r\n#include <list>\r\n#include <string>\r\n#include <sstream>\r\n#include <iostream>\r\n\r\nstd::mutex log_mutex;\r\nstd::condition_variable log_cv;\r\nstd::list<std::string> cached_logs;\r\nFILE* log_file = NULL;\r\n\r\nbool init_log_file()\r\n{\r\n    //׷ݵʽдļݣļڣ򴴽\r\n    log_file = fopen(\"my.log\", \"a+\");\r\n    return log_file != NULL;\r\n}\r\n\r\nvoid uninit_log_file()\r\n{\r\n    if (log_file != NULL)\r\n        fclose(log_file);\r\n}\r\n\r\nbool write_log_tofile(const std::string& line)\r\n{\r\n    if (log_file == NULL)\r\n        return false;\r\n\r\n    if (fwrite((void*)line.c_str(), 1, line.length(), log_file) != line.length())\r\n        return false;\r\n\r\n    //־ˢļȥ\r\n    fflush(log_file);\r\n\r\n    return true;\r\n}\r\n\r\nvoid log_producer()\r\n{\r\n    int index = 0;\r\n    while (true)\r\n    {\r\n        ++ index;\r\n        std::ostringstream os;\r\n        os << \"This is log, index: \" << index << \", producer threadID: \" << std::this_thread::get_id() << \"\\n\";\r\n        //ʹûΪǼС\r\n        {\r\n            std::lock_guard<std::mutex> lock(log_mutex);\r\n            cached_logs.emplace_back(os.str());\r\n            log_cv.notify_one();\r\n        }\r\n           \r\n        std::chrono::milliseconds duration(100);\r\n        std::this_thread::sleep_for(duration);\r\n    }\r\n}\r\n\r\nvoid log_consumer()\r\n{\r\n    std::string line;\r\n    while (true)\r\n    {\r\n        //ʹûΪǼС\r\n        {\r\n            std::unique_lock<std::mutex> lock(log_mutex);\r\n            if (cached_logs.empty())\r\n            {\r\n                //޵ȴ\r\n                log_cv.wait(lock);\r\n            }\r\n\r\n            line = cached_logs.front();\r\n            cached_logs.pop_front();\r\n        }\r\n\r\n        if (line.empty())\r\n        {        \r\n            std::chrono::milliseconds duration(1000);\r\n            std::this_thread::sleep_for(duration);\r\n\r\n            continue;\r\n        }\r\n\r\n        write_log_tofile(line);\r\n\r\n        line.clear();\r\n    }\r\n}\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    if (!init_log_file())\r\n    {\r\n        std::cout << \"init log file error.\" << std::endl;\r\n        return -1;\r\n    }\r\n    \r\n    std::thread log_producer1(log_producer);\r\n    std::thread log_producer2(log_producer);\r\n    std::thread log_producer3(log_producer);\r\n\r\n    std::thread log_consumer1(log_consumer);\r\n    std::thread log_consumer2(log_consumer);\r\n    std::thread log_consumer3(log_consumer);\r\n\r\n    log_producer1.join();\r\n    log_producer2.join();\r\n    log_producer3.join();\r\n\r\n    log_consumer1.join();\r\n    log_consumer2.join();\r\n    log_consumer3.join();\r\n\r\n    uninit_log_file();\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger/AsyncLogger.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.30501.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"AsyncLogger\", \"AsyncLogger.vcxproj\", \"{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger/AsyncLogger.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>AsyncLogger</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"AsyncLogger.cpp\" />\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter09/code/AsyncLogger/AsyncLogger.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"AsyncLogger.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter09/code/AsyncLogger/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// AsyncLogger.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#include <stdio.h>\r\n#include <tchar.h>\r\n\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/AsyncLogger.cpp",
    "content": "/**\r\n *@desc: AsyncLogger.cpp\r\n *@author: zhangyl\r\n *@date: 2018.11.10\r\n */\r\n#include \"stdafx.h\"\r\n#include <thread>\r\n#include <mutex>\r\n#include <list>\r\n#include <string>\r\n#include <sstream>\r\n#include <iostream>\r\n\r\nstd::mutex log_mutex;\r\nstd::list<std::string> cached_logs;\r\nFILE* log_file = NULL;\r\n\r\nbool init_log_file()\r\n{\r\n    //׷ݵʽдļݣļڣ򴴽\r\n    log_file = fopen(\"my.log\", \"a+\");\r\n    return log_file != NULL;\r\n}\r\n\r\nvoid uninit_log_file()\r\n{\r\n    if (log_file != NULL)\r\n        fclose(log_file);\r\n}\r\n\r\nbool write_log_tofile(const std::string& line)\r\n{\r\n    if (log_file == NULL)\r\n        return false;\r\n\r\n    if (fwrite((void*)line.c_str(), 1, line.length(), log_file) != line.length())\r\n        return false;\r\n\r\n    //־ˢļȥ\r\n    fflush(log_file);\r\n\r\n    return true;\r\n}\r\n\r\nvoid log_producer()\r\n{\r\n    int index = 0;\r\n    while (true)\r\n    {\r\n        ++ index;\r\n        std::ostringstream os;\r\n        os << \"This is log, index: \" << index << \", producer threadID: \" << std::this_thread::get_id() << \"\\n\";\r\n        //ʹûΪǼС\r\n        {\r\n            std::lock_guard<std::mutex> lock(log_mutex);\r\n            cached_logs.emplace_back(os.str());\r\n        }\r\n           \r\n        std::chrono::milliseconds duration(100);\r\n        std::this_thread::sleep_for(duration);\r\n    }\r\n}\r\n\r\nvoid log_consumer()\r\n{\r\n    std::string line;\r\n    while (true)\r\n    {\r\n        //ʹûΪǼС\r\n        {\r\n            std::lock_guard<std::mutex> lock(log_mutex);\r\n            if (!cached_logs.empty())\r\n            {\r\n                line = cached_logs.front();\r\n                cached_logs.pop_front();\r\n            }\r\n        }\r\n\r\n        if (line.empty())\r\n        {        \r\n            std::chrono::milliseconds duration(1000);\r\n            std::this_thread::sleep_for(duration);\r\n\r\n            continue;\r\n        }\r\n\r\n        write_log_tofile(line);\r\n\r\n        line.clear();\r\n    }\r\n}\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    if (!init_log_file())\r\n    {\r\n        std::cout << \"init log file error.\" << std::endl;\r\n        return -1;\r\n    }\r\n    \r\n    std::thread log_producer1(log_producer);\r\n    std::thread log_producer2(log_producer);\r\n    std::thread log_producer3(log_producer);\r\n\r\n    std::thread log_consumer1(log_consumer);\r\n    std::thread log_consumer2(log_consumer);\r\n    std::thread log_consumer3(log_consumer);\r\n\r\n    log_producer1.join();\r\n    log_producer2.join();\r\n    log_producer3.join();\r\n\r\n    log_consumer1.join();\r\n    log_consumer2.join();\r\n    log_consumer3.join();\r\n\r\n    uninit_log_file();\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/AsyncLogger.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.30501.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"AsyncLogger\", \"AsyncLogger.vcxproj\", \"{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/AsyncLogger.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>AsyncLogger</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"AsyncLogger.cpp\" />\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/AsyncLogger.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"AsyncLogger.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// AsyncLogger.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#include <stdio.h>\r\n#include <tchar.h>\r\n\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLogger1/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLoggerLinux.cpp",
    "content": "/**\r\n *@desc: AsyncLogger.cpplinux汾\r\n *@author: zhangyl\r\n *@date: 2018.11.10\r\n */\r\n#include <unistd.h>\r\n#include <list>\r\n#include <stdio.h>\r\n#include <pthread.h>\r\n#include <semaphore.h>\r\n#include <string>\r\n#include <iostream>\r\n#include <sstream>\r\n\r\n\r\nstd::list<std::string> cached_logs;\r\npthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;\r\nsem_t           log_semphore;\r\nFILE* plogfile = NULL;\r\n\r\nbool init()\r\n{\r\n    pthread_mutex_init(&log_mutex, NULL);\r\n    //ʼźԴĿ0\r\n    sem_init(&log_semphore, 0, 0);\r\n \r\n    //ļڣ򴴽\r\n    plogfile = fopen(\"my.log\", \"a++\");\r\n\r\n    return plogfile != NULL;\r\n}\r\n\r\nvoid uninit()\r\n{\r\n    pthread_mutex_destroy(&log_mutex);\r\n    sem_destroy(&log_semphore);\r\n       \r\n    if (plogfile != NULL)\r\n        fclose(plogfile);\r\n}\r\n\r\nbool write_log_to_file(const std::string& line)\r\n{\r\n    if (plogfile == NULL)\r\n        return false;\r\n\r\n    //ڱȽϳ־Ӧ÷ֶд룬Ϊдֻд벿֣Ϊʾ㣬߼Ӽ\r\n    if (fwrite((void*)line.c_str(), 1, line.length(), plogfile) != line.length())\r\n        return false;\r\n    \r\n    //־ˢļȥ\r\n    fflush(plogfile);\r\n\r\n    return true;\r\n}\r\n\r\nvoid* producer_thread_proc(void* arg)\r\n{\r\n    int index = 0;\r\n    while (true)\r\n    {\r\n        ++ index;\r\n        std::ostringstream os;\r\n        os << \"This is log, index: \" << index << \", producer threadID: \" << pthread_self() << \"\\n\";\r\n      \r\n        pthread_mutex_lock(&log_mutex);\r\n        cached_logs.push_back(os.str());\r\n        pthread_mutex_unlock(&log_mutex);\r\n\r\n        sem_post(&log_semphore);\r\n                   \r\n        usleep(100000);\r\n    }\r\n}\r\n\r\nvoid* consumer_thread_proc(void* arg)\r\n{\r\n    std::string line;\r\n    while (true)\r\n    {     \r\n        //޵ȴ\r\n        sem_wait(&log_semphore);\r\n\r\n        pthread_mutex_lock(&log_mutex);\r\n        if (!cached_logs.empty())\r\n        {\r\n            line = cached_logs.front();\r\n            cached_logs.pop_front();\r\n        }     \r\n        pthread_mutex_unlock(&log_mutex);\r\n\r\n        if (line.empty())\r\n        {        \r\n            sleep(1);\r\n\r\n            continue;\r\n        }\r\n\r\n        write_log_to_file(line);\r\n\r\n        line.clear();\r\n    }\r\n}\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    if (!init())\r\n    {\r\n        std::cout << \"init log file error.\" << std::endl;\r\n        return -1;\r\n    }\r\n    \r\n    pthread_t producer_thread_id[3];\r\n    for (int i = 0; i < sizeof(producer_thread_id) / sizeof(producer_thread_id[0]); ++i)\r\n    {\r\n        pthread_create(&producer_thread_id[i], NULL, producer_thread_proc, NULL);\r\n    }\r\n\r\n    pthread_t consumer_thread_id[3];\r\n    for (int i = 0; i < sizeof(consumer_thread_id) / sizeof(consumer_thread_id[0]); ++i)\r\n    {\r\n        pthread_create(&consumer_thread_id[i], NULL, consumer_thread_proc, NULL);\r\n    }\r\n\r\n    //ȴ߳˳\r\n    for (int i = 0; i < sizeof(producer_thread_id) / sizeof(producer_thread_id[0]); ++i)\r\n    {\r\n        pthread_join(producer_thread_id[i], NULL);\r\n    }\r\n\r\n    //ȴ߳˳\r\n    for (int i = 0; i < sizeof(consumer_thread_id) / sizeof(consumer_thread_id[0]); ++i)\r\n    {\r\n        pthread_join(consumer_thread_id[i], NULL);\r\n    }\r\n\r\n    uninit();\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/AsyncLogger.cpp",
    "content": "/**\r\n *@desc: AsyncLogger.cppWindows汾\r\n *@author: zhangyl\r\n *@date: 2018.11.10\r\n */\r\n#include \"stdafx.h\"\r\n#include <windows.h>\r\n#include <list>\r\n#include <string>\r\n#include <iostream>\r\n#include <sstream>\r\n\r\nstd::list<std::string> cached_logs;\r\nCRITICAL_SECTION g_cs;\r\nHANDLE g_hSemaphore = NULL;\r\nHANDLE g_hLogFile = INVALID_HANDLE_VALUE;\r\n\r\nbool Init()\r\n{\r\n    InitializeCriticalSection(&g_cs);\r\n    \r\n    //Դ0xFFFFFFFF\r\n    g_hSemaphore = CreateSemaphore(NULL, 0, 0xFFFFFFFF, NULL);\r\n    \r\n    //ļڣ򴴽\r\n    g_hLogFile = CreateFile(_T(\"my.log\"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r\n    if (g_hLogFile == INVALID_HANDLE_VALUE)\r\n        return false;\r\n\r\n    return true;\r\n}\r\n\r\nvoid Uninit()\r\n{\r\n    DeleteCriticalSection(&g_cs);\r\n    \r\n    if (g_hSemaphore != NULL)\r\n        CloseHandle(g_hSemaphore);\r\n    \r\n    if (g_hLogFile != INVALID_HANDLE_VALUE)\r\n        CloseHandle(g_hLogFile);\r\n}\r\n\r\nbool WriteLogToFile(const std::string& line)\r\n{\r\n    if (g_hLogFile == INVALID_HANDLE_VALUE)\r\n        return false;\r\n\r\n    DWORD dwBytesWritten;\r\n    //ڱȽϳ־Ӧ÷ֶд룬Ϊдֻд벿֣Ϊʾ㣬߼Ӽ\r\n    if (!WriteFile(g_hLogFile, line.c_str(), line.length(), &dwBytesWritten, NULL) || dwBytesWritten != line.length())\r\n        return false;\r\n    \r\n    //־ˢļȥ\r\n    FlushFileBuffers(g_hLogFile);\r\n\r\n    return true;\r\n}\r\n\r\nDWORD CALLBACK LogProduceThreadProc(LPVOID lpThreadParameter)\r\n{\r\n    int index = 0;\r\n    while (true)\r\n    {\r\n        ++ index;\r\n        std::ostringstream os;\r\n        os << \"This is log, index: \" << index << \", producer threadID: \" << GetCurrentThreadId() << \"\\n\";\r\n      \r\n        EnterCriticalSection(&g_cs);       \r\n        cached_logs.emplace_back(os.str());\r\n        LeaveCriticalSection(&g_cs);\r\n\r\n        ReleaseSemaphore(g_hSemaphore, 1, NULL);\r\n                   \r\n        Sleep(100);\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\nDWORD CALLBACK LogConsumeThreadProc(LPVOID lpThreadParameter)\r\n{\r\n    std::string line;\r\n    while (true)\r\n    {     \r\n        //޵ȴ\r\n        WaitForSingleObject(g_hSemaphore, INFINITE);\r\n\r\n        EnterCriticalSection(&g_cs);\r\n        if (!cached_logs.empty())\r\n        {\r\n            line = cached_logs.front();\r\n            cached_logs.pop_front();\r\n        }     \r\n        LeaveCriticalSection(&g_cs);          \r\n\r\n\r\n        if (line.empty())\r\n        {        \r\n            Sleep(1000);\r\n\r\n            continue;\r\n        }\r\n\r\n        WriteLogToFile(line);\r\n\r\n        line.clear();\r\n    }\r\n}\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n    if (!Init())\r\n    {\r\n        std::cout << \"init log file error.\" << std::endl;\r\n        return -1;\r\n    }\r\n    \r\n    HANDLE hProducers[3];\r\n    for (int i = 0; i < sizeof(hProducers) / sizeof(hProducers[0]); ++i)\r\n    {\r\n        hProducers[i] = CreateThread(NULL, 0, LogProduceThreadProc, NULL, 0, NULL);\r\n    }\r\n\r\n    HANDLE hConsumers[3];\r\n    for (int i = 0; i < sizeof(hConsumers) / sizeof(hConsumers[0]); ++i)\r\n    {\r\n        hConsumers[i] = CreateThread(NULL, 0, LogConsumeThreadProc, NULL, 0, NULL);\r\n    }\r\n\r\n    //ȴ߳˳\r\n    for (int i = 0; i < sizeof(hProducers) / sizeof(hProducers[0]); ++i)\r\n    {\r\n        WaitForSingleObject(hProducers[i], INFINITE);\r\n    }\r\n\r\n    //ȴ߳˳\r\n    for (int i = 0; i < sizeof(hConsumers) / sizeof(hConsumers[0]); ++i)\r\n    {\r\n        WaitForSingleObject(hConsumers[i], INFINITE);\r\n    }\r\n\r\n    Uninit();\r\n    \r\n    return 0;\r\n}\r\n\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/AsyncLogger.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2013\r\nVisualStudioVersion = 12.0.30501.0\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"AsyncLogger\", \"AsyncLogger.vcxproj\", \"{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tRelease|Win32 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}.Release|Win32.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/AsyncLogger.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{F77EAAE7-C424-4EB5-A82B-B7AFDDA3C892}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>AsyncLogger</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"AsyncLogger.cpp\" />\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\r\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/AsyncLogger.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Text Include=\"ReadMe.txt\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"stdafx.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"targetver.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"stdafx.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"AsyncLogger.cpp\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n// AsyncLogger.pch will be the pre-compiled header\r\n// stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n// TODO: reference any additional headers you need in STDAFX.H\r\n// and not in this file\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n// or project specific include files that are used frequently, but\r\n// are changed infrequently\r\n//\r\n\r\n#pragma once\r\n\r\n#include \"targetver.h\"\r\n\r\n#include <stdio.h>\r\n#include <tchar.h>\r\n\r\n\r\n\r\n// TODO: reference additional headers your program requires here\r\n"
  },
  {
    "path": "Chapter09/code/AsyncLoggerWindows/targetver.h",
    "content": "#pragma once\r\n\r\n// Including SDKDDKVer.h defines the highest available Windows platform.\r\n\r\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\r\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\r\n\r\n#include <SDKDDKVer.h>\r\n"
  },
  {
    "path": "README.md",
    "content": "# 《C++ 服务器开发精髓》随书配套源码\n\n\n\n这是《C++ 服务器开发精髓》的随书配套源码。\n\n![](https://github.com/balloonwj/mybooksources/blob/master/bookCover.jpeg?raw=true)\n\n\n\n### 购买链接\n\nhttps://item.jd.com/13312460.html\n\n\n\n### 相关链接：\n\n[在 2021 年写一本 C++ 图书是一种什么体验？](https://mp.weixin.qq.com/s/gEtCIc5AtSXhUp0ClyEvwg)\n\n\n\n### 联系方式\n\n你可以加入本书读者交流群一起交流学习，加微信 **easy_coder** 拉你进群。\n\n\n\n### 微信公众号\n\n微信搜索【**高性能服务器开发**】或者微信扫码关注。\n\n![](https://github.com/balloonwj/mybooksources/blob/master/easyserverdev.png?raw=true)\n"
  }
]